diff --git a/2dlib/CMakeLists.txt b/2dlib/CMakeLists.txt new file mode 100644 index 000000000..771da00b5 --- /dev/null +++ b/2dlib/CMakeLists.txt @@ -0,0 +1,13 @@ +SET (HEADERS lib2d.h) +SET (CPPS + font.cpp + hardsurf.cpp + memsurf.cpp + pen.cpp + pentext.cpp + screen.cpp + surface.cpp + viewport.cpp) + +ADD_LIBRARY(2dlib STATIC ${HEADERS} ${CPPS}) + \ No newline at end of file diff --git a/2dlib/font.cpp b/2dlib/font.cpp new file mode 100644 index 000000000..a879ad11b --- /dev/null +++ b/2dlib/font.cpp @@ -0,0 +1,795 @@ +/* + * $Logfile: /DescentIII/Main/2dlib/font.cpp $ + * $Revision: 15 $ + * $Date: 4/17/99 6:15p $ + * $Author: Samir $ + * + * will load and manage a font's capabilities. + * + * $Log: /DescentIII/Main/2dlib/font.cpp $ + * + * 15 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 14 10/13/98 10:31a Sean + * Fixed a mem_free (free()) conflict + * + * 13 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 12 3/31/98 3:48p Jason + * added memory lib + * + * 11 12/19/97 12:31p Samir + * load font registration + * + * 10 12/12/97 6:40p Samir + * Some viewport font functions. + * + * 9 11/16/97 6:53p Samir + * Reinstated instance count for fonts. + * + * 8 11/16/97 4:41p Matt + * Commented-out an assert. + * + * 7 11/14/97 12:06p Samir + * When you use a font for the first time, load it. Never free font until + * game closes. + * + * 6 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 5 9/26/97 2:53p Samir + * Delete old m_CharSurf if creating a new one. + * + * 4 9/19/97 5:37p Samir + * Software font coloring works. + * + * 3 8/07/97 3:15p Samir + * May have fixed horizontal left clipping problem. + * + * 22 6/25/97 10:10a Samir + * Removed unused code causing warning. + * + * 21 6/24/97 7:42p Matt + * Included bitmap.h instead of font.h + * + * 20 6/16/97 4:45p Samir + * Fixed transparency probs + * + * 19 6/16/97 3:01p Samir + * Changed font system to work better with renderer. Fonts are rendered + * on bitmaps of 128x128 at load time. + * + * 18 6/12/97 6:27p Samir + * DDGR v2.0 (GR v1.1) Changes to ddgr system reflected in 2d system + * + * 17 5/14/97 8:07p Samir + * some pathname problem which screwed up Mac. + * + * 16 3/07/97 4:01p Samir + * Took out malloc.h for ANSI compliance. + * + * 15 3/03/97 6:20p Matt + * Changed cfile functions to use D3 naming convention + * + * 14 2/28/97 3:17 PM Jeremy + * changed a call of strcmpi to stricmp + * + * 13 2/28/97 2:57p Samir + * Took out __cdecl from atexit + * + * 12 2/10/97 12:14p Samir + * Allow multiple font loading per viewport. + * + * 11 2/06/97 6:33p Samir + * Added clipping character functions for font. + * + * 10 2/06/97 12:43p Samir + * Mono fonts work. + * + * 9 2/05/97 12:19p Samir + * Stopped redundant increases in font reference count for same font + * object. + * + * 8 2/05/97 10:35a Samir + * Fixed text drawing at wrong location problem + * + * $NoKeywords: $ + */ + +#include "pserror.h" +#include "renderer.h" +#include "gr.h" +#include "mono.h" +#include "CFILE.H" +#include "bitmap.h" +#include "mem.h" + +#include +#include + +#define FT_COLOR 1 +#define FT_PROPORTIONAL 2 +#define FT_KERNED 4 +#define FT_GRADIENT 8 + + +#define GRFONT_SURFACE_WIDTH 128 +#define GRFONT_SURFACE_HEIGHT 128 + +// ---------------------------------------------------------------------------- +// Macros for file io. +// ---------------------------------------------------------------------------- + +typedef CFILE* FONTFILE; + +#define BITS_TO_BYTES(_c) (((_c)+7)>>3) +#define BITS_TO_SHORTS(_c) (((_c)+15)>>4) + +inline int READ_FONT_INT(FONTFILE ffile) { + return cf_ReadInt(ffile); +} + +inline short READ_FONT_SHORT(FONTFILE ffile) { + return cf_ReadShort(ffile); +} + +inline ubyte READ_FONT_BYTE(FONTFILE ffile) { + return (ubyte)cf_ReadByte(ffile); +} + +inline int READ_FONT_DATA(FONTFILE ffile, void *buf, int size, int nelem) +{ + int i; + + i = cf_ReadBytes((ubyte *)buf, size*nelem, ffile); + + ASSERT(i == (size*nelem)); + + return i; +} + +inline FONTFILE OPEN_FONT(char *filename, bool &success) +{ + FONTFILE fp; + int file_id; + + success = false; + fp = (FONTFILE)cfopen(filename, "rb"); + if (!fp) return NULL; + + file_id = READ_FONT_INT(fp); + if (file_id != 0xfeedbaba) return (FONTFILE)(-1); + + success = true; + return fp; +} + +inline void CLOSE_FONT(FONTFILE ffile) { + cfclose(ffile); +} + + + +// ---------------------------------------------------------------------------- +// static variables and functions +// ---------------------------------------------------------------------------- + +gr_font_record grFont::m_FontList[MAX_FONTS]; +grMemorySurface *grFont::m_CharSurf = NULL; + + +void grFont::init_system() +{ + for (int i = 0; i < MAX_FONTS; i++) + { + grFont::m_FontList[i].references = 0; + grFont::m_FontList[i].filename[0] = 0; + } + + if (grFont::m_CharSurf) { + delete grFont::m_CharSurf; + grFont::m_CharSurf = NULL; + } + + if (!grFont::m_CharSurf) { + grFont::m_CharSurf = new grMemorySurface; + grFont::m_CharSurf->init(0,0,BPP_16, NULL, 0,0, "char_surf"); + } + + atexit(grFont::close_system); +} + + +void grFont::close_system() +{ + if (grFont::m_CharSurf) { + delete grFont::m_CharSurf; + grFont::m_CharSurf = NULL; + } + +// we should free any fonts currently allocated here. + mprintf((0, "Freeing cached fonts...")); + for (int i = 0; i < MAX_FONTS; i++) + { + gr_font_record *ft; + if (grFont::m_FontList[i].references) { + ft = &grFont::m_FontList[i]; + grFont::free_font(ft); + grFont::m_FontList[i].references = 0; + } + } +} + + +int grFont::register_font(char *fontname, char *filename) +{ +/* look for a slot with no fontname and place fontname in it */ + ASSERT(fontname != NULL); + + int i; + + for (i = 0; i < MAX_FONTS; i++) + { + if (grFont::m_FontList[i].filename[0] == 0) { + strcpy(grFont::m_FontList[i].filename, filename); + strcpy(grFont::m_FontList[i].name, fontname); + grFont::load(m_FontList[i].filename, i); + grFont::m_FontList[i].references=1; + break; + } + } + + if (i == MAX_FONTS) i = -1; + + return i; +} + + +// ---------------------------------------------------------------------------- +// constructor and destructor of font object +// ---------------------------------------------------------------------------- + +grFont::grFont() +{ + m_FontHandle = -1; +} + + +grFont::~grFont() +{ + if (m_FontHandle > -1) + grFont::free(); +} + + +// ---------------------------------------------------------------------------- +// high level font initialization and cleanup routines +// ---------------------------------------------------------------------------- + +void grFont::init(const char *fontname) +{ + int i, slot = -1; + +// Look for this font in the list, check if it has any references, and load it if +// there are none. + if (fontname == NULL) { + slot = 0; + } + else { + for (i = 0; i < MAX_FONTS; i++) + { + if (stricmp(grFont::m_FontList[i].name, fontname) == 0) { + slot = i; + + if (grFont::m_FontList[i].references == 0) { + break; + } + else { + m_FontHandle = slot; + return; + } + } + } + + ASSERT(slot != -1); + + // clear up surface and bitmap lists + grFont::m_FontList[slot].ch_w = NULL; + grFont::m_FontList[slot].ch_h = NULL; + grFont::m_FontList[slot].ch_u = NULL; + grFont::m_FontList[slot].ch_v = NULL; + + for (i = 0; i < MAX_FONT_BITMAPS; i++) + { + grFont::m_FontList[slot].bmps[i] = -1; + grFont::m_FontList[slot].surfs[i] = NULL; + } + + // We must load this font from the 'hogfile' + grFont::load(m_FontList[slot].filename, slot); + + grFont::m_FontList[slot].references++; + } + + m_FontHandle = slot; +} + + +void grFont::free() +{ + if (m_FontHandle == -1) return; + + return; + +//!! Removed the following assert because there are bugs in the reference count system. Matt, 11-15-97. +//!! ASSERT(grFont::m_FontList[m_FontHandle].references > 0); +//@@ +// free font if there was only one reference left. +//@@ if (grFont::m_FontList[m_FontHandle].references == 1) { +//@@ gr_font_record *ft; +//@@ +//@@ // free up all memory allocated to this font. +//@@ ft = &grFont::m_FontList[m_FontHandle]; +//@@ +//@@ grFont::free_font(ft); +//@@ } +//@@ +//@@ grFont::m_FontList[m_FontHandle].references--; + m_FontHandle = -1; +} + + +void grFont::free_font(gr_font_record *ft) +{ + int i; + +// delete font surface info. + if (ft->ch_wf) delete[] ft->ch_wf; + if (ft->ch_hf) delete[] ft->ch_hf; + if (ft->ch_uf) delete[] ft->ch_uf; + if (ft->ch_vf) delete[] ft->ch_vf; + if (ft->ch_w) delete[] ft->ch_w; + if (ft->ch_h) delete[] ft->ch_h; + if (ft->ch_u) delete[] ft->ch_u; + if (ft->ch_v) delete[] ft->ch_v; + if (ft->ch_surf) delete[] ft->ch_surf; + + for (i = 0; i < MAX_FONT_BITMAPS; i++) + { + if (ft->bmps[i] != -1) bm_FreeBitmap(ft->bmps[i]); + if (ft->surfs[i]) delete ft->surfs[i]; + } + +// delete font file info. + if (ft->font.flags & FT_PROPORTIONAL) { + delete[] ft->font.char_widths; + } + + ::mem_free(ft->font.raw_data); + ::mem_free(ft->font.char_data); +} + + +void grFont::load(char *filename, int slot) +{ + FONTFILE ff; + gr_font_file_record fnt, *ft; + char fontname[32]; + int num_char; + int i; + + ft = &fnt; + + bool success; + ff = OPEN_FONT(filename, success); + if (!ff) + { + Error("Unable to open font in file %s.\n", filename); + } + else if ( !success ) + { + Error("Illegal font file: %s.\n", filename); + } + + mprintf((0, "%s font.\n", grFont::m_FontList[slot].name)); + + ft->width = READ_FONT_SHORT(ff); + ft->height = READ_FONT_SHORT(ff); + ft->flags = READ_FONT_SHORT(ff); + ft->baseline = READ_FONT_SHORT(ff); + ft->min_ascii = READ_FONT_BYTE(ff); + ft->max_ascii = READ_FONT_BYTE(ff); + READ_FONT_DATA(ff, fontname, 32, 1); + + mprintf((0, " ::::::", ft->height, ft->min_ascii, ft->max_ascii, ft->baseline)); + + num_char = ft->max_ascii-ft->min_ascii+1; + +// Read in all widths + if (ft->flags & FT_PROPORTIONAL) { + ft->char_widths = new short[num_char]; + for (i = 0; i < num_char; i++) + ft->char_widths[i] = READ_FONT_SHORT(ff); + mprintf((0, "::proportional")); + } + else { + ft->char_widths = NULL; + } + +// Read in pixel data. +// for color fonts, read in byte count and then the data, +// generate character data pointer table +// for mono fonts, read in byte count, then the data, convert to bits and store +// generate character data pointer table + int bytesize = READ_FONT_INT(ff); + + ft->raw_data = (ubyte *)mem_malloc(bytesize); + ft->char_data = (ubyte **)mem_malloc(num_char * sizeof(ubyte *)); + + READ_FONT_DATA(ff, ft->raw_data, bytesize, 1); + + if (ft->flags & FT_COLOR) { + int off = 0; + mprintf((0, "::color")); + for (i = 0; i < num_char; i++) + { + ft->char_data[i] = ft->raw_data + off; + if (ft->flags & FT_PROPORTIONAL) + off += (ft->char_widths[i]*ft->height*BITS_TO_BYTES(BPP_16)); + else + off += (ft->width*ft->height*BITS_TO_BYTES(BPP_16)); + } + } + else { // Monochrome + ubyte *ptr = ft->raw_data; + mprintf((0, "::mono")); + for (i = 0; i < num_char; i++) + { + ft->char_data[i] = ptr; + if (ft->flags & FT_PROPORTIONAL) + ptr += BITS_TO_BYTES(ft->char_widths[i]) * ft->height; + else + ptr += BITS_TO_BYTES(ft->width) * ft->height; + } + } + +// Read in kerning data + ft->kern_data= NULL; + +// Then read in + CLOSE_FONT(ff); + + mprintf((0, "\n")); + + grFont::m_FontList[slot].font = fnt; + +// draw font to bitmaps, load into surfaces too. + grFont::translate_to_surfaces(slot); +} + + +// translates a font to surfaces +void grFont::translate_to_surfaces(int slot) +{ + gr_font_file_record *fntfile; + gr_font_record *fnt; + + fnt = &grFont::m_FontList[slot]; + fntfile = &grFont::m_FontList[slot].font; + +// start creating font surfaces, map these surfaces onto bitmaps created via bitmap library +// this is needed for the renderer library. +// create a 128x128 surface first. +// draw each character into surface until we need to create another +// surface. + ubyte u=0, v=0, w; + int ch, num_ch; + ubyte surf_index = 0; + + num_ch = fntfile->max_ascii-fntfile->min_ascii+1; + +// initialize memory + fnt->ch_w = new ubyte[num_ch]; + fnt->ch_h = new ubyte[num_ch]; + fnt->ch_u = new ubyte[num_ch]; + fnt->ch_v = new ubyte[num_ch]; + fnt->ch_surf = new ubyte[num_ch]; + fnt->ch_uf = new float[num_ch]; + fnt->ch_vf = new float[num_ch]; + fnt->ch_wf = new float[num_ch]; + fnt->ch_hf = new float[num_ch]; + + fnt->bmps[surf_index] = bm_AllocBitmap(GRFONT_SURFACE_WIDTH, GRFONT_SURFACE_HEIGHT, 0); + if (fnt->bmps[surf_index] == -1) Error("grFont::translate_to_surfaces "); + fnt->surfs[surf_index] = new grMemorySurface; + fnt->surfs[surf_index]->init(GRFONT_SURFACE_WIDTH, GRFONT_SURFACE_HEIGHT, BPP_16, + (char *)bm_data(fnt->bmps[surf_index],0), + GRFONT_SURFACE_WIDTH * BITS_TO_BYTES(BPP_16), + SURFFLAG_COLORKEY); + fnt->surfs[surf_index]->clear(TRANSPARENT_COLOR32); + surf_index++; + + for (ch = 0; ch < num_ch; ch++) + { + if (fntfile->flags & FT_PROPORTIONAL) w = (int)fntfile->char_widths[ch]; + else w = (int)fntfile->width; + + // blt each character + if (fntfile->flags & FT_COLOR) { + m_CharSurf->init(w, fntfile->height, BPP_16, (char *)fntfile->char_data[ch], + w*BITS_TO_BYTES(BPP_16), SURFFLAG_COLORKEY); + + fnt->surfs[surf_index-1]->blt(u,v,m_CharSurf); + } + else { // font monochrome, convert bits to pixels + grFont::translate_mono_char(fnt->surfs[surf_index-1], u,v,ch,fntfile,w); + } + + fnt->ch_h[ch] = fntfile->height; + fnt->ch_w[ch] = w; + fnt->ch_u[ch] = u; + fnt->ch_v[ch] = v; + fnt->ch_surf[ch] = surf_index-1; + fnt->ch_hf[ch] = ((float)fntfile->height)/((float)GRFONT_SURFACE_HEIGHT); + fnt->ch_wf[ch] = ((float)w)/((float)GRFONT_SURFACE_WIDTH); + fnt->ch_uf[ch] = ((float)u)/((float)GRFONT_SURFACE_WIDTH); + fnt->ch_vf[ch] = ((float)v)/((float)GRFONT_SURFACE_HEIGHT); + + // check to adjust uv's if we are outside surface. + u+= w; + if ((u+w) > GRFONT_SURFACE_WIDTH) { + u = 0; + v += fntfile->height; + if ((v+fntfile->height) > GRFONT_SURFACE_HEIGHT) { + if (surf_index == MAX_FONT_BITMAPS) Int3(); + fnt->bmps[surf_index] = bm_AllocBitmap(GRFONT_SURFACE_WIDTH, GRFONT_SURFACE_HEIGHT, 0); + if (fnt->bmps[surf_index] == -1) Error("grFont::translate_to_surfaces "); + fnt->surfs[surf_index] = new grMemorySurface; + fnt->surfs[surf_index]->init(GRFONT_SURFACE_WIDTH, GRFONT_SURFACE_HEIGHT, BPP_16, + (char*)bm_data(fnt->bmps[surf_index],0), + GRFONT_SURFACE_WIDTH * BITS_TO_BYTES(BPP_16), + SURFFLAG_COLORKEY); + fnt->surfs[surf_index]->clear(TRANSPARENT_COLOR32); + surf_index++; + v = 0; + } + } + } +} + + +// translate mono font data to the surface +void grFont::translate_mono_char(grSurface *sf, int x, int y, int index, gr_font_file_record *ft, + int width) +{ + int row,col; // byte width of char + int rowsize; + ubyte bit_mask=0, byte; + ubyte *fp; + + fp = ft->char_data[index]; + + switch (sf->bpp()) + { + case BPP_16: + { + /* draw one-bit one color. */ + ushort *dest_ptr; + ushort col_w = GR_COLOR_TO_16(GR_RGB(255,255,255)); + int rowsize_w; + + dest_ptr = (ushort *)sf->lock(&rowsize); + rowsize_w = sf->rowsize()/2; + dest_ptr += (y*rowsize_w)+x; + + for (row = 0; row < ft->height; row++) + { + bit_mask = 0; + for (col = 0; col < width; col++) + { + if (bit_mask == 0) { + byte = *fp++; + bit_mask = 0x80; + } + + if (byte & bit_mask) + dest_ptr[col] = col_w; + bit_mask >>=1; + } + dest_ptr += rowsize_w; + } + + sf->unlock(); + break; + } + + case BPP_32: + default: + Int3(); // NOT SUPPORTED YET + } +} + + +// ---------------------------------------------------------------------------- +// draw_char function +// give a viewport x,y and char +// ---------------------------------------------------------------------------- + +int grFont::draw_char(grSurface *sf, int x, int y, int ch, tCharProperties *chprop) +{ + gr_font_record *ft; + int next_x, width, index; + + ASSERT(m_FontHandle > -1); + if ((ch < min_ascii()) || (ch > max_ascii())) + return (next_x+1); + +// save current x and get this font + ft = &grFont::m_FontList[m_FontHandle]; + next_x = x; + index = ch - ft->font.min_ascii; + +// draw either a color bitmap or mono font. + if (ft->font.flags & FT_PROPORTIONAL) { + width = (int)ft->font.char_widths[index]; + } + else { + width = (int)ft->font.width; + } + +// perform blt. + if (sf->get_flags() & SURFFLAG_RENDERER) { + rend_SetCharacterParameters(chprop->col[0],chprop->col[1],chprop->col[2],chprop->col[3]); + rend_DrawFontCharacter (ft->bmps[ft->ch_surf[index]], x,y,x+ft->ch_w[index]-1,y+ft->ch_h[index]-1, + ft->ch_uf[index], ft->ch_vf[index], ft->ch_wf[index], ft->ch_hf[index]); + } + else if (ft->font.flags & FT_COLOR) { + sf->blt(x,y,ft->surfs[ft->ch_surf[index]], + ft->ch_u[index], ft->ch_v[index], + ft->ch_w[index], ft->ch_h[index]); + } + else { + charblt16(sf, chprop->col[0], x,y, ft->surfs[ft->ch_surf[index]], + ft->ch_u[index], ft->ch_v[index], + ft->ch_w[index], ft->ch_h[index]); + } + +// adjust next x value with kerning and return it. + next_x += width; + + return next_x; +} + + +int grFont::draw_char(grSurface *sf, int x, int y, int ch, int sx, int sy, int sw, int sh, tCharProperties *chprop) +{ + gr_font_record *ft; + int next_x, width, index; + + ASSERT(m_FontHandle > -1); + if ((ch < min_ascii()) || (ch > max_ascii())) + return (next_x+1); + +// save current x and get this font + ft = &grFont::m_FontList[m_FontHandle]; + next_x = x; + index = ch - ft->font.min_ascii; + +// draw either a color bitmap or mono font. + if (ft->font.flags & FT_PROPORTIONAL) { + width = (int)ft->font.char_widths[index]; + } + else { + width = (int)ft->font.width; + } + + ASSERT(sx+sw <= width); + ASSERT(sy+sh <= ft->font.height); + +// peform blt instead. +// perform blt. + if (sf->get_flags() & SURFFLAG_RENDERER) { + rend_SetCharacterParameters(chprop->col[0],chprop->col[1],chprop->col[2],chprop->col[3]); + + rend_DrawFontCharacter (ft->bmps[ft->ch_surf[index]], x,y,x+sw-1,y+sh-1, + ft->ch_uf[index]+(((float)sx)/((float)GRFONT_SURFACE_WIDTH)), + ft->ch_vf[index]+(((float)sy)/((float)GRFONT_SURFACE_HEIGHT)), + ((float)sw)/((float)GRFONT_SURFACE_WIDTH), + ((float)sh)/((float)GRFONT_SURFACE_HEIGHT)); + } + else if (ft->font.flags & FT_COLOR) { + sf->blt(x,y,ft->surfs[ft->ch_surf[index]], + ft->ch_u[index]+sx, ft->ch_v[index]+sy, + sw, sh); + } + else { + charblt16(sf, chprop->col[0], x,y,ft->surfs[ft->ch_surf[index]], + ft->ch_u[index]+sx, ft->ch_v[index]+sy, + sw, sh); + } + +// adjust next x value with kerning and return it. + next_x += sw; + + return next_x; +} + + +// ---------------------------------------------------------------------------- +// accessor functions +// ---------------------------------------------------------------------------- + +int grFont::get_char_info(int ch, int *width) +{ + gr_font_file_record *ft; + + ASSERT(m_FontHandle > -1); + + ft = &grFont::m_FontList[m_FontHandle].font; + + if (ch < ft->min_ascii || ch > ft->max_ascii) + *width = 0; + else if (ft->flags & FT_PROPORTIONAL) + *width = ft->char_widths[ch-ft->min_ascii]; + else + *width = ft->width; + + return 0; +} + + +ubyte *grFont::get_kern_info(ubyte c1, ubyte c2) +{ +// gr_font_file_record *ft; +// ubyte *p; + +// p = ft->kern_data; +// ft = &grFont::m_FontList[m_FontHandle].font; +// c2 = c2 - ft->min_ascii; +// c1 = c1 - ft->min_ascii; + +// while (*p!=255) +// { +// if (p[0]==c1 && p[1]==c2) return p; +// else p+=3; +// } + + return NULL; +} + + +void grFont::charblt16(grSurface *dsf, ddgr_color col, int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh) +{ + ushort *dbits; + ushort *sbits; + int srowsize_w, drowsize_w, row, coln; + ushort scol = GR_COLOR_TO_16(col); + + dbits = (ushort *)dsf->lock(&drowsize_w); + if (dbits) { + sbits = (ushort *)ssf->lock(&srowsize_w); + if (sbits) { + srowsize_w >>= 1; // rowsize in shorts + drowsize_w >>= 1; + dbits = dbits + (dy * drowsize_w) + dx; + sbits = sbits + (sy * srowsize_w) + sx; + + for (row = 0; row < sh; row++) + { + for (coln = 0; coln < sw; coln++) + { + if (sbits[coln] & OPAQUE_FLAG16) + dbits[coln] = scol; + } + sbits += srowsize_w; + dbits += drowsize_w; + } + + ssf->unlock(); + } + dsf->unlock(); + } +} + diff --git a/2dlib/hardsurf.cpp b/2dlib/hardsurf.cpp new file mode 100644 index 000000000..ddb892370 --- /dev/null +++ b/2dlib/hardsurf.cpp @@ -0,0 +1,57 @@ +/* + * $Logfile: /descent3/main/2dlib/hardsurf.cpp $ + * $Revision: 8 $ + * $Date: 6/12/97 6:28p $ + * $Author: Samir $ + * + * hardware surface class + * + * $Log: /descent3/main/2dlib/hardsurf.cpp $ + * + * 8 6/12/97 6:28p Samir + * DDGR v2.0 Changes in 2d system implemeted. + * + * 7 1/30/97 6:01p Samir + * The partition of memory and os surfaces as well as the change in + * ddgr_surface structure and all related changes. + * + * $NoKeywords: $ + */ + +#include "gr.h" + + +// ---------------------------------------------------------------------------- +// constructor and destructor +// ---------------------------------------------------------------------------- + +grHardwareSurface::grHardwareSurface() + :grSurface() +{ + +} + + +grHardwareSurface::grHardwareSurface(int w, int h, int bpp, unsigned flags, const char *name) + :grSurface(w, h, bpp, SURFTYPE_GENERIC, flags, name) +{ + +} + + +grHardwareSurface::~grHardwareSurface() +{ + +} + + +// ---------------------------------------------------------------------------- +// initialize a hardware surface with these values +// ---------------------------------------------------------------------------- + +bool grHardwareSurface::create(int w, int h, int bpp, unsigned flags, const char *name) +{ + grSurface::create(w,h,bpp, SURFTYPE_GENERIC, flags, name); + return 1; +} + diff --git a/2dlib/lib2d.h b/2dlib/lib2d.h new file mode 100644 index 000000000..06adc5bf9 --- /dev/null +++ b/2dlib/lib2d.h @@ -0,0 +1,54 @@ +/* internal header to 2dlib + +*/ + +#ifndef LIB2D_H +#define LIB2D_H + +#include "gr.h" +#include "renderer.h" + +// structures + +typedef struct mem_bitmap { + char *data; + short bpp; + int rowsize; + ushort alloced:2; + ushort flag:14; +} mem_bitmap; + +#define MEMFLAG_TRANSBLT 1 + + +// --------------------------------------------------------------------------- +// Line Drawing Functions + +void gr_Line(gr_pen *pen, int x1, int y1, int x2, int y2); +void gr_HLine(gr_pen *pen, int x1, int x2, int y); +void gr_VLine(gr_pen *pen, int y1, int y2, int x); +void gr_Rect(gr_pen *pen, int l, int t, int r, int b); +void gr_FillRect(gr_pen *pen, int l, int t, int r, int b); + + +// --------------------------------------------------------------------------- +// Memory surface functions + +bool gr_mem_surf_Create(ddgr_surface *sf); +bool gr_mem_surf_Destroy(ddgr_surface *sf); + +// generic clearing functions +void gr_mem_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h); + +// non-scaling bitmap blt functions +bool gr_mem_surf_Blt(ddgr_surface *dsf, int dx, int dy, + ddgr_surface *ssf, int sx, int sy, int sw, int sh); + +// retrieves a pointer to surface memory. allowed to lock one surface multiple times. +bool gr_mem_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize); +bool gr_mem_surf_Unlock(ddgr_surface *sf, void *ptr); + +// initializes a surface based of preinitialized objects by the user. +bool gr_mem_surf_Init(ddgr_surface *sf, char *data, int rowsize); + +#endif diff --git a/2dlib/memsurf.cpp b/2dlib/memsurf.cpp new file mode 100644 index 000000000..ca1a4d67c --- /dev/null +++ b/2dlib/memsurf.cpp @@ -0,0 +1,400 @@ +/* + * $Logfile: /DescentIII/Main/2dlib/memsurf.cpp $ + * $Revision: 6 $ + * $Date: 4/23/98 6:38p $ + * $Author: Jason $ + * + * memory surface class + * + * $Log: /DescentIII/Main/2dlib/memsurf.cpp $ + * + * 6 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 5 3/31/98 3:48p Jason + * added memory lib + * + * 4 11/16/97 2:19p Samir + * Added constructor takes a bitmap handle. + * + * 3 11/14/97 12:56p Samir + * Took out annoying mprintf. + * + * 2 10/01/97 4:22p Samir + * Fixed memory leak in memsurf library destruction + * + * 12 6/12/97 6:29p Samir + * DDGR v2.0 Changes in 2d system implemented. + * + * 11 5/15/97 4:24p Samir + * Fixed one-pixel overwrite of clear + * + * 10 4/10/97 6:45p Samir + * Fixed confusion of variables in memory clear function. + * + * 9 3/28/97 3:07p Samir + * Clear function now takes a color + * + * 8 3/27/97 11:11a Samir + * Moved code from ddgr library to 2dlib because it was ANSI compliant and + * should work on all systems + * + * 7 2/03/97 3:00p Samir + * Allow for first time initialization code to properly register surface + * in surface list. + * + * 6 1/30/97 6:01p Samir + * The partition of memory and os surfaces as well as the change in + * ddgr_surface structure and all related changes. + * + * $NoKeywords: $ + */ + + +#include "lib2d.h" + +#include "bitmap.h" + +#include +#include +#include "mem.h" + + +// ---------------------------------------------------------------------------- +// constructor and destructor +// ---------------------------------------------------------------------------- + +grMemorySurface::grMemorySurface(): + grSurface() +{ + m_FirstTimeInit = 1; + m_AllowInit = 1; +} + + +grMemorySurface::grMemorySurface(int w, int h, int bpp, unsigned flags, const char *name) + :grSurface(w, h, bpp, SURFTYPE_MEMORY, flags, name) +{ + m_FirstTimeInit = 0; + m_AllowInit = 0; +} + + +grMemorySurface::grMemorySurface(int bm) +{ + m_FirstTimeInit = 1; + m_AllowInit = 1; + + grMemorySurface::init(bm_w(bm,0), bm_h(bm,0), bm_bpp(bm), (char *)bm_data(bm, 0), bm_rowsize(bm,0), SURFFLAG_COLORKEY); +} + + +grMemorySurface::~grMemorySurface() +{ + // this should invoke the grSurface destructor + // even if we just initialized the surface, the memory routines should know this and + // do appropriate deinitialization. +} + + +// ---------------------------------------------------------------------------- +// initialize a memory surface with these values +// ---------------------------------------------------------------------------- + +bool grMemorySurface::init(int w, int h, int bpp, char *data, int rowsize, unsigned flags, const char *name) +{ + ASSERT(m_AllowInit); + + if (m_FirstTimeInit) { + // add to list ONLY ONCE! + add_to_list(&ddsfObj); + m_FirstTimeInit = 0; + } + + ddsfObj.w = w; + ddsfObj.h = h; + ddsfObj.bpp = bpp; + ddsfObj.type = SURFTYPE_MEMORY; + ddsfObj.flags = (ushort)flags; + if (name) + strncpy(ddsfObj.name, name,15); + + + if (!gr_mem_surf_Init(&ddsfObj, data, rowsize)) return 0; + + surf_init(1); + + return true; +} + + + +// --------------------------------------------------------------------------- +// gr_mem_xxx functions for memory surfaces +// +// internal functions +// --------------------------------------------------------------------------- + +static mem_bitmap grMemSurf_object; + +bool gr_bm_mem_Blt16(ddgr_surface *dsf, + int dx, int dy, + ddgr_surface *ssf, + int sx, int sy, int sw, int sh); +bool gr_bm_mem_Blt16ck(ddgr_surface *dsf, + int dx, int dy, + ddgr_surface *ssf, + int sx, int sy, int sw, int sh); + + +// --------------------------------------------------------------------------- +// initializes a memory surface by allocating memory based on bit depth + +bool gr_mem_surf_Create(ddgr_surface *sf) +{ + mem_bitmap *bm; + + bm = new mem_bitmap; + + switch (sf->bpp) + { + case BPP_16: + bm->bpp = BPP_16; + bm->rowsize = sf->w*2; + bm->alloced = 1; + break; + + case BPP_32: + bm->bpp = BPP_32; + bm->rowsize = sf->w*4; + bm->alloced = 1; + break; + + default: + Int3(); + } + + bm->data = (char *)mem_malloc(bm->rowsize * sf->h); + if (!bm->data) { + mprintf((0, "mem_Create malloc fail <%s>\n", sf->name)); + delete bm; + return false; + } + + sf->obj = (void *)bm; + + return true; +} + + +// --------------------------------------------------------------------------- +// deallocates memory created in a mem_Create + +bool gr_mem_surf_Destroy(ddgr_surface *sf) +{ + ASSERT(sf); + + mem_bitmap *bm = (mem_bitmap *)sf->obj; + +// here, we check if we initialized, or allocated this memory surface. + if (bm->alloced==2) { + delete bm; + return false; + } + else if (bm->alloced == 0) { + return false; + } + +// otherwise free all objects + mem_free(bm->data); + delete bm; + + return true; +} + + +// --------------------------------------------------------------------------- +// initializes a memory surface + +bool gr_mem_surf_Init(ddgr_surface *sf, char *data, int rowsize) +{ +// initialize a memory surface, with preallocated memory + mem_bitmap *mbm; + +// create new memory bitmap object for initialized surface + if (!sf->obj) { + mbm = new mem_bitmap; + mbm->alloced = 2; + } + else { + mbm = (mem_bitmap *)sf->obj; + mbm->alloced = 0; + } + + mbm->data = data; + mbm->rowsize = rowsize; + mbm->bpp = (short)sf->bpp; + mbm->flag = 0; + + sf->obj = (void *)mbm; + + return true; +} + + +// --------------------------------------------------------------------------- +// clears a memory surface + +void gr_mem_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h) +{ + mem_bitmap *dbm = (mem_bitmap *)dsf->obj; + + switch (dbm->bpp) + { + case BPP_16: + { + ushort *sptr; + ushort scol; + + scol = (ushort)GR_COLOR_TO_16(col); + sptr = (ushort *)dbm->data + (t * ((dbm->rowsize)>>1)); + + for (int y = t; y < (t+h); y++) + { + for (int x = l; x < (l+w);x++) + sptr[x] = scol; + + sptr += ((dbm->rowsize)>>1); + } + break; + } + + case BPP_32: + default: + Int3(); + } +} + + +// --------------------------------------------------------------------------- +// blts a memory surface to another such surface + +bool gr_mem_surf_Blt(ddgr_surface *dsf, int dx, int dy, ddgr_surface *ssf, int sx, int sy, int sw, int sh) +{ +// Maybe we should allow slow 16bpp to 24bpp and vice-versa blts. +// for now, we wont. + mem_bitmap *sbm = (mem_bitmap *)ssf->obj, *dbm = (mem_bitmap *)dsf->obj; + + ASSERT (dbm->bpp == sbm->bpp); + + switch (dbm->bpp) + { + case BPP_16: + if (ssf->flags & SURFFLAG_COLORKEY) + gr_bm_mem_Blt16ck(dsf, dx, dy, ssf, sx, sy, sw, sh); + else + gr_bm_mem_Blt16(dsf, dx, dy, ssf, sx, sy, sw, sh); + break; + + case BPP_32: + default: + Int3(); + } + + return true; +} + + +// --------------------------------------------------------------------------- +// just extracts info from mem_bitmap abstract object + +bool gr_mem_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize) +{ + mem_bitmap *bm = (mem_bitmap *)sf->obj; + + *ptr = (char *)bm->data; + *rowsize = bm->rowsize; + + return true; +} + + +// --------------------------------------------------------------------------- +// invalidates sf members + +bool gr_mem_surf_Unlock(ddgr_surface *sf, void *ptr) +{ + return true; +} + + +// --------------------------------------------------------------------------- +// does shit. + +bool gr_mem_surf_AttachHandle(ddgr_surface *sf, unsigned handle) +{ + return true; +} + + +// --------------------------------------------------------------------------- +// performs 16-bit blts. + +bool gr_bm_mem_Blt16(ddgr_surface *dsf, + int dx, int dy, + ddgr_surface *ssf, + int sx, int sy, int sw, int sh) +{ + mem_bitmap *dbm = (mem_bitmap *)dsf->obj, *sbm = (mem_bitmap *)ssf->obj; + ushort *dbits; + ushort *sbits; + int srowsize_w, drowsize_w, row, col; + +// set up blt. + srowsize_w = sbm->rowsize >> 1; // rowsize in shorts + drowsize_w = dbm->rowsize >> 1; + dbits = (ushort *)dbm->data + (dy * drowsize_w) + dx; + sbits = (ushort *)sbm->data + (sy * srowsize_w) + sx; + + for (row = 0; row < sh; row++) + { + for (col = 0; col < sw; col++) + dbits[col] = sbits[col]; + sbits += srowsize_w; + dbits += drowsize_w; + } + + return true; +} + + +bool gr_bm_mem_Blt16ck(ddgr_surface *dsf, + int dx, int dy, + ddgr_surface *ssf, + int sx, int sy, int sw, int sh) +{ + mem_bitmap *dbm = (mem_bitmap *)dsf->obj, *sbm = (mem_bitmap *)ssf->obj; + ushort *dbits; + ushort *sbits; + int srowsize_w, drowsize_w, row, col; + +// set up blt. + srowsize_w = sbm->rowsize >> 1; // rowsize in shorts + drowsize_w = dbm->rowsize >> 1; + dbits = (ushort *)dbm->data + (dy * drowsize_w) + dx; + sbits = (ushort *)sbm->data + (sy * srowsize_w) + sx; + + for (row = 0; row < sh; row++) + { + for (col = 0; col < sw; col++) + { + if (sbits[col] & OPAQUE_FLAG16) + dbits[col] = sbits[col]; + } + sbits += srowsize_w; + dbits += drowsize_w; + } + + return true; +} + diff --git a/2dlib/pen.cpp b/2dlib/pen.cpp new file mode 100644 index 000000000..9f108bd8b --- /dev/null +++ b/2dlib/pen.cpp @@ -0,0 +1,183 @@ +/* + * $Logfile: /DescentIII/Main/2dlib/pen.cpp $ + * $Revision: 5 $ + * $Date: 12/19/97 5:28p $ + * $Author: Samir $ + * + * line and rectangle drawing + * + * $Log: /DescentIII/Main/2dlib/pen.cpp $ + * + * 5 12/19/97 5:28p Samir + * Took out old rasterization code. Moved to renderer library. + * + * 4 12/19/97 5:23p Samir + * Cleaned up a lot of code to call renderer code. + * + * 3 12/19/97 12:31p Samir + * Better renderer support. + * + * 21 6/16/97 4:45p Samir + * Added rendering line hooks. + * + * 20 6/12/97 6:29p Samir + * + * 19 6/06/97 12:18p Samir + * OpenGL HACKS to call renderer for some drawing functions. Must fix. + * + * 18 5/29/97 5:59p Samir + * Convert clipping from fix math to float math. + * + * 17 4/11/97 3:56 PM Jeremy + * replaced calls of ddgr_H/Vline to gr_h/vline + * + * 16 4/08/97 12:53p Samir + * Fixed hline clipping fuckup. + * + * 15 4/08/97 11:30a Jason + * made circles not draw if they are touching the edge ofthe viewport + * + * 14 4/08/97 10:42a Jason + * fixed bug with hline + * + * 13 4/01/97 4:37p Samir + * Simply better circle functions using viewport line and pixel functions. + * made an hline function that clips and a setpixel function that clips. + * + * 12 4/01/97 3:56p Samir + * Cheap clipping for scanlines. Should fix this so it's cleaner. + * + * 11 4/01/97 1:51p Samir + * Fixed clipping of circles. + * + * 10 4/01/97 1:40p Samir + * Circle functions + * + * 9 3/27/97 11:11a Samir + * Moved code from ddgr library to 2dlib because it was ANSI compliant and + * should work on all systems + * + * 8 2/28/97 2:52 PM Jeremy + * removed inline declaration of grViewport::clip_line + * + * 7 2/27/97 6:14p Samir + * moved higher level ddgr pen code to this library. + * + * 6 2/07/97 6:04p Matt + * Renamed vm_FixMulDiv() to FixMulDiv() + * + * 5 2/04/97 11:14a Samir + * Improved clipping system by adding a separate clipport based off of the + * viewport's rectangle in it's parent surface + * + * $NoKeywords: $ + */ + +#include "lib2d.h" +#include "pserror.h" + + +#define CLIP_LEFT (vp_InitLeft+vp_Left) +#define CLIP_TOP (vp_InitTop+vp_Top) +#define CLIP_RIGHT (vp_InitLeft+vp_Right) +#define CLIP_BOTTOM (vp_InitTop+vp_Bottom) + + +void grViewport::hline(ddgr_color col, int x1, int x2, int y1) +{ + ASSERT(vp_Locked); + + x1 = global_x(x1); + y1 = global_y(y1); + x2 = global_x(x2); + + if (x1>x2) + { + int t=x2; + x2=x1; + x1=t; + } + + if (y1 < CLIP_TOP) return; + if (y1 > CLIP_BOTTOM) return; + if (x1 > CLIP_RIGHT) return; + if (x2 < CLIP_LEFT) return; + if (x1 < CLIP_LEFT) x1 = CLIP_LEFT; + if (x2 > CLIP_RIGHT) x2 = CLIP_RIGHT; + + rend_SetFlatColor(col); + rend_DrawLine(x1,y1,x2,y1); +} + + +// grViewport::line +// draws a clipped line + +void grViewport::line(ddgr_color color, int x1, int y1, int x2, int y2) +{ + int xa, ya, xb, yb; + ASSERT(vp_Locked); + + xa = global_x(x1); + ya = global_y(y1); + xb = global_x(x2); + yb = global_y(y2); + + rend_SetFlatColor(color); + rend_DrawLine(x1,y1,x2,y2); +} + +void grViewport::rect(ddgr_color color, int l, int t, int r, int b) +{ + ASSERT(vp_Locked); + + l = global_x(l); + t = global_y(t); + r = global_x(r); + b = global_y(b); + + rend_SetFlatColor(color); + rend_DrawLine(l,t,r,t); + rend_DrawLine(r,t,r,b); + rend_DrawLine(l,b,r,b); + rend_DrawLine(l,t,l,b); +} + + +void grViewport::fillrect(ddgr_color color, int l, int t, int r, int b) +{ + ASSERT(vp_Locked); + + l = global_x(l); + t = global_y(t); + r = global_x(r); + b = global_y(b); + + rend_FillRect(color, l, t, r, b); +} + + +void grViewport::circle(ddgr_color col, int xc, int yc, int r) +{ + ASSERT(vp_Locked); + + xc = global_x(xc); + yc = global_y(yc); + + rend_SetFlatColor(col); + rend_DrawCircle(xc,yc,r); +} + + +void grViewport::fillcircle(ddgr_color col, int xc, int yc, int r) +{ + ASSERT(vp_Locked); + + xc = global_x(xc); + yc = global_y(yc); + + rend_FillCircle(col, xc, yc, r); +} + + + diff --git a/2dlib/pentext.cpp b/2dlib/pentext.cpp new file mode 100644 index 000000000..03510fef1 --- /dev/null +++ b/2dlib/pentext.cpp @@ -0,0 +1,375 @@ +/* + * $Logfile: /DescentIII/Main/2dlib/pentext.cpp $ + * $Revision: 8 $ + * $Date: 11/01/98 1:57a $ + * $Author: Jeff $ + * + * handles text drawing caps for viewports + * + * $Log: /DescentIII/Main/2dlib/pentext.cpp $ + * + * 8 11/01/98 1:57a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 7 12/19/97 12:31p Samir + * Better renderer support. + * + * 6 12/12/97 6:40p Samir + * Some viewport font functions. + * + * 5 11/17/97 3:46p Matt + * Made the tab character move the current text location to the next even + * multiple of the width of a space + * + * 4 11/16/97 5:06p Matt + * Fixed puts() to deal correctly with multiple blank likes in a string. + * + * 3 11/14/97 5:37p Matt + * Fixed small bug w/ printing multi-line strings + * + * 2 8/15/97 6:34p Samir + * Fixed bug when passing an empty string to puts. + * + * 13 3/04/97 12:06p Samir + * fixed bug in draw_clip_text_line. + * + * 12 2/13/97 3:44p Samir + * get_text_line_width works correctly with special color text formatting. + * + * 11 2/13/97 2:24p Samir + * Added mid-string color-coding text. Note that when + * done with text operation, the original color is restored. + * + * 10 2/06/97 6:33p Samir + * Untested clipping of text. + * + * 9 2/06/97 12:44p Samir + * Made change to draw_char call based off change of prototype. + * + * 8 2/05/97 10:35a Samir + * Fixed text drawing at wrong location problem + * + * 7 2/04/97 11:14a Samir + * Improved clipping system by adding a separate clipport based off of the + * viewport's rectangle in it's parent surface + * + * 6 2/03/97 3:00p Samir + * Fixed x updating problem in draw_text_line + * + * 5 1/30/97 7:01p Samir + * updated font stuff + * + * $NoKeywords: $ + */ + + +#include +#include +#include +#include +#include "pstring.h" +#include "gr.h" +#include "mono.h" +#include "renderer.h" + +#define CLIP_LEFT (vp_InitLeft+vp_Left) +#define CLIP_TOP (vp_InitTop+vp_Top) +#define CLIP_RIGHT (vp_InitLeft+vp_Right) +#define CLIP_BOTTOM (vp_InitTop+vp_Bottom) + + +#define STR_BUF_SIZE 512 +static char Str_buf[STR_BUF_SIZE]; // String buffer for variable arguments +static char Draw_str_buf[STR_BUF_SIZE]; // used to buffer words in a string + + + +// --------------------------------------------------------------------------- +// Viewport font and text funcitons +// --------------------------------------------------------------------------- + +void grViewport::set_font(const char *fontname) +{ + text_Font.init(fontname); +} + +void grViewport::get_font(char *fontname, int size) +{ + ASSERT(size > (int)strlen(text_Font.get_name())); + strcpy(fontname, text_Font.get_name()); +} + + +// --------------------------------------------------------------------------- +// Standard text functions +// --------------------------------------------------------------------------- + +int grViewport::printf(grTextAlign align, int x, int y, char *fmt, ...) +{ + va_list arglist; + int len; + + va_start(arglist,fmt); + len = Pvsprintf(Str_buf,STR_BUF_SIZE,fmt,arglist); + va_end(arglist); + if (len < 0) return 0; + + return grViewport::puts(align, x, y, Str_buf); +} + + +int grViewport::printf(int x, int y, char *fmt, ...) +{ + va_list arglist; + int len; + + va_start(arglist,fmt); + len = Pvsprintf(Str_buf,STR_BUF_SIZE,fmt,arglist); + va_end(arglist); + if (len < 0) return 0; + + return grViewport::puts(grTextUnjustified, x, y, Str_buf); +} + + +int grViewport::puts(int x, int y, char *str) +{ + return grViewport::puts(grTextUnjustified, x, y, str); +} + + +// Ultimately all text output string functions call this monolith which +// calls the ddgr interface. + +int grViewport::puts(grTextAlign align, int x, int y, char *str) +{ + int cur_x, cur_y, gx, gy; + char *line,*save_pos; + tCharProperties old_props; + +// setup rendering of text. + rend_SetTextureType (TT_LINEAR); + rend_SetOverlayType (OT_NONE); + rend_SetFiltering (0); + rend_SetLighting (LS_GOURAUD); + rend_SetAlphaType (ATF_TEXTURE+ATF_CONSTANT); + rend_SetColorModel (CM_RGB); + rend_SetZBufferState (0); + rend_SetAlphaValue(char_Props.alpha); + + get_char_properties(&old_props); + + cur_x = x; + cur_y = y; + + line = str; + + do + { + //Look for newline, and if found, put in a 0 to terminate the line + save_pos = strchr(line,'\n'); + if (save_pos) //found an newline + *save_pos = 0; //terminate the line + + int line_width; + int clipped; + + line_width = get_text_line_width(line); + + if (align == grTextCentered) + cur_x = (((vp_Right-vp_Left)+1)/2) - (line_width/2); + else if (align == grTextLeft) + cur_x = 0; + + gy = global_y(cur_y); + gx = global_x(cur_x);// Clip function needs global coords. + + clipped = 0; + if ((CLIP_TOP > (gy+text_Font.height())) || (CLIP_BOTTOM < gy)) clipped = 2; + else if ((CLIP_LEFT > (gx+line_width)) || (CLIP_RIGHT < gx)) clipped = 2; + + if (clipped != 2) { + if (CLIP_LEFT > gx || CLIP_RIGHT < (gx+line_width)) clipped = 1; + if (CLIP_TOP > gy || CLIP_BOTTOM < (gy+text_Font.height())) clipped = 1; + + if (clipped == 0) + draw_text_line(gx, gy, line); + else if (clipped == 1) + draw_text_line_clip(gx, gy, line); + } + cur_y += (get_text_line_spacing() + text_Font.height()); + cur_x = x; + + //replace the newline, if there was one + if (save_pos) + *save_pos++ = '\n'; + + line = save_pos; + + } while (line); + + set_char_properties(GR_VPCP_ALL, &old_props); + + rend_SetFiltering (1); + rend_SetZBufferState (1); + + return strlen(str); +} + + + +// --------------------------------------------------------------------------- +// Private functions +// --------------------------------------------------------------------------- + +// get_text_line_width() +// returns length in pixels of text line using current text spacing settings + +int grViewport::get_text_line_width(char *str) +{ + int line_width = 0; + int rgb_define_mode = 0; + + for (int i = 0; i < (int)strlen(str); i++) + { + int width; + + // note that if we hit the GR_COLOR_CHAR then the next three values should + // not count when defining the width of the line. + if (rgb_define_mode == 3) rgb_define_mode = 0; + else if (str[i] == GR_COLOR_CHAR) rgb_define_mode = 1; + if (!rgb_define_mode) { + if (str[i] == '\t') { //tab char + int space_width; + text_Font.get_char_info(' ',&space_width); + space_width += text_Spacing; + line_width = (line_width + space_width - 1) / space_width * space_width; + } + else { + text_Font.get_char_info((int)str[i], &width); + line_width += (width+get_text_spacing()); + } + } + else rgb_define_mode++; + } + + return line_width - get_text_spacing(); +} + + +/* These internal routines just draw a line of text. depending on the function + we clip or don't clip +*/ + +void grViewport::draw_text_line_clip(int x, int y, char *str) +{ + int ch_y, ch_x, ch_w, ch_h; // what part of the character to draw + int i, cur_x, draw_x, draw_y; // where to draw character section + int h; + +/* by clipping, we should first determine what our vertical clipping is. then + go through each character in the line and determine what is totally clipped, + partially clipped and by how much, and not clipped at all and draw accordingly +*/ + h = text_Font.height(); + ch_y = 0; + ch_h = h; + draw_y = y; +// determine each character bitmap y and height to blt. + if (CLIP_TOP >= y) { + ch_y = CLIP_TOP - y; + draw_y = CLIP_TOP; + } + if (CLIP_BOTTOM < (y+text_Font.height())) { + ch_h = CLIP_BOTTOM - y; + } + ch_h = ch_h - ch_y; // do this to clip both top and bottom + + cur_x = x; + for (i = 0; i < (int)strlen(str); i++) + { + ubyte ch; + int w; + ch = (ubyte)str[i]; + text_Font.get_char_info((int)ch, &w); + + if (ch == GR_COLOR_CHAR) { + if ((i+3) >= (int)strlen(str)) + Int3(); // This shouldn't happen! bad string! + set_text_color(GR_RGB(str[i+1], str[i+2], str[i+3])); + i += 4; + if (i >= (int)strlen(str)) Int3(); // This shouldn't happen too. + ch = (ubyte)str[i]; + } + else if (ch == '\t') { //tab char + int space_width; + text_Font.get_char_info(' ',&space_width); + space_width += text_Spacing; + cur_x = (cur_x + space_width - 1) / space_width * space_width; + } + + // do horizontal clipping + if (((cur_x+w) < CLIP_LEFT) || (cur_x > CLIP_RIGHT)) { + cur_x += (text_Spacing+w); + } + else { + ch_x = 0; + ch_w = w; + draw_x = cur_x; + if (CLIP_LEFT > cur_x) { + ch_x = CLIP_LEFT - cur_x; + draw_x = CLIP_LEFT; + } + if (CLIP_RIGHT < (cur_x+w)) { + ch_w = CLIP_RIGHT - cur_x; + } + ch_w = ch_w - ch_x; + + if (ch_x == 0 && ch_w == w && ch_y == 0 && ch_h == h) + cur_x = text_Font.draw_char(sf_Parent, draw_x, draw_y, ch, &char_Props); + else + cur_x = text_Font.draw_char(sf_Parent, draw_x, draw_y, ch, ch_x, ch_y, ch_w, ch_h, &char_Props); + + cur_x+= text_Spacing; + } + } + +} + + +void grViewport::draw_text_line(int x, int y, char *str) +{ + unsigned i, cur_x; + +/* perform string drawing without viewport clipping. + supports bitmap fonts or color(alpha) mapped fonts. +*/ + cur_x = x; + for (i = 0; i < (int)strlen(str); i++) + { + ubyte ch; + + ch = (ubyte)str[i]; + + if (ch == GR_COLOR_CHAR) { + if ((i+3) >= (int)strlen(str)) Int3(); // This shouldn't happen! bad string! + set_text_color(GR_RGB(str[i+1], str[i+2], str[i+3])); + i += 4; + if (i >= (int)strlen(str)) Int3(); // This shouldn't happen too. + ch = (ubyte)str[i]; + } + else if (ch == '\t') { //tab char + int space_width; + text_Font.get_char_info(' ',&space_width); + space_width += text_Spacing; + cur_x = (cur_x + space_width - 1) / space_width * space_width; + } + else { + cur_x = text_Font.draw_char(sf_Parent, cur_x, y, ch, &char_Props); + cur_x+= text_Spacing; + } + } +} + + diff --git a/2dlib/screen.cpp b/2dlib/screen.cpp new file mode 100644 index 000000000..1788a5e6f --- /dev/null +++ b/2dlib/screen.cpp @@ -0,0 +1,65 @@ +/* + * $Logfile: /descent3/main/2dlib/screen.cpp $ + * $Revision: 12 $ + * $Date: 6/16/97 5:16p $ + * $Author: Jason $ + * + * Screen creation class + * + * $Log: /descent3/main/2dlib/screen.cpp $ + * + * 12 6/16/97 5:16p Jason + * added renderer.h header + * + * 11 6/16/97 4:52p Samir + * If a renderer, then call rendflip instead. + * + * 10 6/13/97 3:00p Samir + * Only flip if screen has a backbuffer. + * + * 9 6/12/97 6:29p Samir + * DDGR v2.0 Changes in 2d system implemented. + * + * 8 1/30/97 6:01p Samir + * The partition of memory and os surfaces as well as the change in + * ddgr_surface structure and all related changes. + * + * $NoKeywords: $ + */ + + +#include + +#include "gr.h" +#include "pserror.h" +#include "renderer.h" + + +// --------------------------------------------------------------------------- +// grSurface constructor and destructor +// --------------------------------------------------------------------------- + +grScreen::grScreen(int w, int h, int bpp, const char *name) + :grSurface(w,h,bpp,SURFTYPE_VIDEOSCREEN, SURFFLAG_BACKBUFFER, name) +{ + +} + + +grScreen::~grScreen() +{ + +} + + +// --------------------------------------------------------------------------- +// screen refresh routines +// --------------------------------------------------------------------------- + + +void grScreen::flip() +{ + ASSERT(surf_init()); + if (ddsfObj.flags & SURFFLAG_BACKBUFFER) ddgr_surf_FlipVideo(&ddsfObj); + else if (ddsfObj.flags & SURFFLAG_RENDERER) rend_Flip(); +} diff --git a/2dlib/surface.cpp b/2dlib/surface.cpp new file mode 100644 index 000000000..bedca1c26 --- /dev/null +++ b/2dlib/surface.cpp @@ -0,0 +1,832 @@ +/* + * $Logfile: /DescentIII/Main/2dlib/surface.cpp $ + * $Revision: 7 $ + * $Date: 11/30/98 5:50p $ + * $Author: Jason $ + * + * Master Surface class + * + * $Log: /DescentIII/Main/2dlib/surface.cpp $ + * + * 7 11/30/98 5:50p Jason + * added 4444 bitmap support + * + * 6 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 5 12/19/97 2:26p Jason + * more fixes for 2d/3d intergration + * + * 4 12/19/97 12:32p Samir + * lock now calls renderer functions. + * + * 3 11/11/97 1:24p Samir + * Fixed problem with clearing using the renderer. + * + * 2 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 24 6/12/97 6:29p Samir + * DDGR v2.0 Changes in 2d system implemented. + * + * 23 6/06/97 12:18p Samir + * OpenGL HACKS to call renderer for some drawing functions. Must fix. + * + * 22 5/19/97 5:10p Jason + * changes for our new abstracted renderer code + * + * 21 4/30/97 3:15p Jason + * changes to support both 8bit and 16bit rendering in software + * + * 20 3/28/97 3:07p Samir + * Clear function now takes a color + * + * 19 3/27/97 11:11a Samir + * Moved code from ddgr library to 2dlib because it was ANSI compliant and + * should work on all systems + * + * 18 2/28/97 2:51p Samir + * took out __cdecl in atexit function. + * + * 17 2/17/97 2:19p Jason + * made blit function scale instead of clip + * + * 16 2/11/97 3:19p Jason + * added displaying of 8bit palettized graphics on 16bit surfaces + * + * 15 2/04/97 12:49p Samir + * Added lock(x,y) for those special cases. + * + * 14 2/03/97 3:01p Samir + * Used one memory surface global to grSurface for scratchpad blitting. + * + * 13 1/30/97 6:01p Samir + * The partition of memory and os surfaces as well as the change in + * ddgr_surface structure and all related changes. + * + * $NoKeywords: $ + */ + + +#include +#include + +#include "lib2d.h" +#include "pserror.h" +#include "bitmap.h" +#include "texture.h" +#include "renderer.h" + + +inline unsigned XLAT_RGB_TO_16(ddgr_color c) +{ + unsigned char r,g,b; + r = (unsigned char)((c & 0x00ff0000) >> 16); + g = (unsigned char)((c & 0x0000ff00) >> 8); + b = (unsigned char)(c & 0x000000ff); + + return (((r>>3)<<10) + ((g>>3)<<5) + (b>>3)); +} + +ddgr_surface_node *grSurface::surf_list = NULL; // the surface list +ddgr_surface_node *grSurface::surf_list_cur = NULL; // current node on list. +ddgr_surface *grSurface::scratch_mem_surf = NULL; + + + +void grSurface::init_system() +{ +// allocate our scratch memory surface used in uniblt + if (!grSurface::scratch_mem_surf) { + grSurface::scratch_mem_surf = new ddgr_surface; + memset(grSurface::scratch_mem_surf, 0, sizeof(ddgr_surface)); + } + + atexit(grSurface::close_system); +} + + +void grSurface::close_system() +{ +// cleanup our scratch memory surface for blt + if (grSurface::scratch_mem_surf) { + if (grSurface::scratch_mem_surf->obj != NULL) + gr_mem_surf_Destroy(grSurface::scratch_mem_surf); + + delete grSurface::scratch_mem_surf; + grSurface::scratch_mem_surf = NULL; + } +} + + +// --------------------------------------------------------------------------- +// grSurface constructor and destructor +// --------------------------------------------------------------------------- + +grSurface::grSurface() +{ + surf_Locked = 0; + m_SurfInit = 0; + memset(&ddsfObj, 0, sizeof(ddgr_surface)); +} + + +grSurface::grSurface(int w, int h, int bpp, unsigned type, unsigned flags, const char *name) +{ + surf_Locked = 0; + m_SurfInit = 0; + + grSurface::create(w, h, bpp, type, flags, name); +} + + +grSurface::~grSurface() +{ + grSurface::free(); +} + +void grSurface::create(int w, int h, int bpp, unsigned type, unsigned flags, const char *name) +{ + bool grerr; + + if (m_SurfInit) grSurface::free(); + +// create new surface: set up surface structure + memset(&ddsfObj, 0, sizeof(ddgr_surface)); + ddsfObj.w = w; + ddsfObj.h = h; + ddsfObj.bpp = bpp; + ddsfObj.type = (ushort)type; + ddsfObj.flags = (ushort)flags; + + if (name) + strncpy(ddsfObj.name, name, 15); + + switch (type) + { + case SURFTYPE_VIDEOSCREEN: + grerr = ddgr_surf_InitVideo(&ddsfObj); break; + case SURFTYPE_GENERIC: + grerr = ddgr_surf_Create(&ddsfObj); break; + case SURFTYPE_MEMORY: + grerr = (bool)gr_mem_surf_Create(&ddsfObj); break; + default: + Int3(); // Invalid!! + } + if (!grerr) Int3(); + + add_to_list(&ddsfObj); + + m_SurfInit = 1; +} + + +void grSurface::free() +{ +/* Freeing surface is same regardless of whether it was loaded from a bitmap handle or + created ourselves +*/ + if (!m_SurfInit) return; + ASSERT(!surf_Locked); + + switch (ddsfObj.type) + { + case SURFTYPE_VIDEOSCREEN: + ddgr_surf_CloseVideo(&ddsfObj); break; + case SURFTYPE_GENERIC: + ddgr_surf_Destroy(&ddsfObj); break; + case SURFTYPE_MEMORY: + gr_mem_surf_Destroy(&ddsfObj); break; + default: + Int3(); // This really shouldn't happen. bad type! + } + + remove_from_list(&ddsfObj); + + m_SurfInit = 0; +} + + +void grSurface::load(int handle) +{ +/* just load the bitmap from the handle info */ + ASSERT(m_SurfInit); + + grSurface::load((char *)bm_data(handle, 0), bm_w(handle, 0), bm_h(handle,0), bm_bpp(handle),bm_format(handle)); +} + + +void grSurface::load(char *data, int w, int h, int bpp,int format) +{ +// copy defined bitmap given as arguments to this surface, with correct bpp translation. + int rowsize; + ASSERT(m_SurfInit); + +/* perform lock, then copy data to this surface, then unlock */ + grSurface::lock(&rowsize); + + switch(ddsfObj.bpp) + { + case BPP_16: + if (bpp == BPP_16) xlat16_16(data, w, h,format); + if (bpp == BPP_24) xlat24_16(data, w, h); + if (bpp == BPP_32) xlat32_16(data, w, h); + break; + + case BPP_24: + if (bpp == BPP_16) xlat16_24(data, w, h); + break; + + default: + Int3(); // NOT SUPPORTED YET + } + + grSurface::unlock(); +} + +// loads 8bit palettized data onto this surface. +void grSurface::load(int handle, char *pal) +{ + ASSERT(m_SurfInit); + + grSurface::load((char *)bm_data(handle, 0), bm_w(handle, 0), bm_h(handle,0),pal); + +} + +// loads 8bit palettized data onto this surface. +void grSurface::load(char *data, int w, int h, char *pal) +{ + int rowsize; + ASSERT(m_SurfInit); + +/* perform lock, then copy data to this surface, then unlock */ + grSurface::lock(&rowsize); + + switch(ddsfObj.bpp) + { + case BPP_16: + if (Renderer_type==RENDERER_SOFTWARE_8BIT) + xlat8_16(data, w, h,pal); + else + xlat16_16(data, w, h); + break; + default: + Int3(); // NOT SUPPORTED YET + } + + grSurface::unlock(); +} + + + +// --------------------------------------------------------------------------- +// blt functions:: clear +// --------------------------------------------------------------------------- + +void grSurface::clear(ddgr_color col) +{ + grSurface::clear(0,0,ddsfObj.w, ddsfObj.h, col); +} + + +void grSurface::clear(int l, int t, int w, int h, ddgr_color col) +{ + ASSERT(m_SurfInit); + ASSERT(!surf_Locked); + + switch (ddsfObj.type) + { + case SURFTYPE_VIDEOSCREEN: + case SURFTYPE_GENERIC: + // HACK!!!!!!! I MUST CHANGE THIS + // --------------------------------------------------------------------------- + if (ddsfObj.flags & SURFFLAG_RENDERER) { + rend_FillRect(col, l,t,l+w,t+h); + return; + } + // --------------------------------------------------------------------------- + ddgr_surf_Clear(&ddsfObj, col,l, t, w, h); + break; + case SURFTYPE_MEMORY: + gr_mem_surf_Clear(&ddsfObj, col,l, t, w, h); break; + default: + Int3(); + } +} + + +// --------------------------------------------------------------------------- +// blt functions: +// these functions must support memory->os, os->memory, os->os, mem->mem blts. +// note that (os) blts and (video) blts use the os library. +// --------------------------------------------------------------------------- + +void grSurface::uniblt(int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh) +{ + int rowsize; + + ASSERT(m_SurfInit); + +// mode 11b = dd_to_dd, 01b = mem_to_dd, 10b = dd_to_mem, 00b = mem_to_mem + int mode = 0; + ddgr_surface *dsfs = &ddsfObj, *ssfs = &ssf->ddsfObj; + bool err; + +// setup blitting mode + if (ssfs->type == SURFTYPE_GENERIC || ssfs->type == SURFTYPE_VIDEOSCREEN) + mode = mode | 0x1; + if (dsfs->type == SURFTYPE_GENERIC || dsfs->type == SURFTYPE_VIDEOSCREEN) + mode = mode | 0x2; + +/* case 1: os->os + just invoke os blitter +*/ + if (mode == 0x3) { + ASSERT(!surf_Locked); + err = ddgr_surf_Blt(dsfs, dx, dy, ssfs, sx, sy, sw, sh); + return; + } + +/* case 2: mem->mem + just lock both surfaces and do a memory blt. +*/ + if (mode == 0) { + err = gr_mem_surf_Blt(dsfs, dx, dy, ssfs, sx, sy, sw, sh); + return; + } + +/* case 3: os->mem + This requires locking both surfaces and performing a memory blt. + This also means tricking the memory blitter into thinking that the OS surface + is a memory surface +*/ + if (mode == 0x1) { + char *data = ssf->lock(&rowsize); + + grSurface::scratch_mem_surf->w = ssfs->w; + grSurface::scratch_mem_surf->h = ssfs->h; + grSurface::scratch_mem_surf->bpp = ssfs->bpp; + grSurface::scratch_mem_surf->type = SURFTYPE_MEMORY; + grSurface::scratch_mem_surf->flags = ssfs->flags; + + gr_mem_surf_Init(grSurface::scratch_mem_surf, data, rowsize); + err = gr_mem_surf_Blt(dsfs, dx, dy, grSurface::scratch_mem_surf, sx, sy, sw, sh); + + ssf->unlock(); + return; + } + +/* case 4: mem->os + This requires locking both surfaces and performing a memory blt. + This also means tricking the memory blitter into thinking that the OS surface + is a memory surface +*/ + if (mode == 0x2) { + grSurface::lock(&rowsize); + + grSurface::scratch_mem_surf->w = dsfs->w; + grSurface::scratch_mem_surf->h = dsfs->h; + grSurface::scratch_mem_surf->bpp = dsfs->bpp; + grSurface::scratch_mem_surf->type = SURFTYPE_MEMORY; + grSurface::scratch_mem_surf->flags = dsfs->flags; + + gr_mem_surf_Init(grSurface::scratch_mem_surf, m_DataPtr, m_DataRowsize); + err = gr_mem_surf_Blt(grSurface::scratch_mem_surf, dx, dy, ssfs, sx, sy, sw, sh); + + grSurface::unlock(); + return; + } +} + + +void grSurface::blt(int dx, int dy, grSurface *ssf) +{ + ASSERT(m_SurfInit); + grSurface::uniblt(dx, dy, ssf, 0,0,ssf->width(), ssf->height()); +} + + +void grSurface::blt(int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh) +{ + ASSERT(m_SurfInit); + grSurface::uniblt(dx, dy, ssf, sx, sy, sw, sh); +} + + +/* Helper functions + replace_color, simply replaces one color in the surface with another +*/ +void grSurface::replace_color(ddgr_color sc, ddgr_color dc) +{ + int rowsize; + ASSERT(m_SurfInit); + + grSurface::lock(&rowsize); + + switch (ddsfObj.bpp) + { + case BPP_16: + { + ushort *data = (ushort *)m_DataPtr; + int rowsize_w = m_DataRowsize/2; + ushort scc = XLAT_RGB_TO_16(sc), dcc = XLAT_RGB_TO_16(dc); + + for (int y = 0; y < ddsfObj.h; y++) + { + for (int x = 0; x < ddsfObj.w; x++) + if (data[x] == scc) data[x] = dcc; + + data += rowsize_w; + } + break; + } + + case BPP_32: + default: + Int3(); + } + + grSurface::unlock(); +} + + +// --------------------------------------------------------------------------- +// lock and unlock functions +// these handle memory and os locks +// --------------------------------------------------------------------------- + +char *grSurface::lock(int *rowsize) +{ + bool grerr; + + ASSERT(m_SurfInit); + + switch(ddsfObj.type) + { + case SURFTYPE_VIDEOSCREEN: + case SURFTYPE_GENERIC: + grerr = ddgr_surf_Lock(&ddsfObj, (void **)&m_OrigDataPtr, &m_DataRowsize); break; + case SURFTYPE_MEMORY: + grerr = gr_mem_surf_Lock(&ddsfObj, (void **)&m_OrigDataPtr, &m_DataRowsize); break; + default: + Int3(); // invalid type!! + } + + m_DataPtr = m_OrigDataPtr; + *rowsize = m_DataRowsize; + + if (!grerr) return NULL; + + surf_Locked++; // increment lock counter for this surface + + rend_SetSoftwareParameters(ddgr_GetAspectRatio(), ddsfObj.w, ddsfObj.h, m_DataRowsize, (ubyte *)m_OrigDataPtr); + + return m_DataPtr; +} + + +/* do lock based off of x,y offsets */ +char *grSurface::lock(int x, int y, int *rowsize) +{ + bool grerr; + + ASSERT(m_SurfInit); + + switch(ddsfObj.type) + { + case SURFTYPE_VIDEOSCREEN: + case SURFTYPE_GENERIC: + grerr = ddgr_surf_Lock(&ddsfObj, (void **)&m_OrigDataPtr, &m_DataRowsize); break; + case SURFTYPE_MEMORY: + grerr = gr_mem_surf_Lock(&ddsfObj, (void **)&m_OrigDataPtr, &m_DataRowsize); break; + default: + Int3(); // invalid type!! + } + + *rowsize = m_DataRowsize; + if (!grerr) return NULL; + +/* calculate the adjusted data pointer based off of x,y,bpp */ + char *data = m_OrigDataPtr; + + data += (y*m_DataRowsize); + + switch(ddsfObj.bpp) + { + case BPP_16: data += (x*2); break; + case BPP_32: data += (x*4); break; + default: Int3(); + } + + m_DataPtr = data; + + surf_Locked++; // increment lock counter for this surface + + rend_SetSoftwareParameters(ddgr_GetAspectRatio(), ddsfObj.w, ddsfObj.h, m_DataRowsize, (ubyte *)m_OrigDataPtr); + + return m_DataPtr; +} + + +void grSurface::unlock() +{ + bool grerr; + + ASSERT(m_SurfInit); + ASSERT(surf_Locked); + + switch(ddsfObj.type) + { + case SURFTYPE_VIDEOSCREEN: + case SURFTYPE_GENERIC: + grerr = ddgr_surf_Unlock(&ddsfObj, m_OrigDataPtr); break; + case SURFTYPE_MEMORY: + grerr = gr_mem_surf_Unlock(&ddsfObj, m_OrigDataPtr); break; + default: + Int3(); // invalid type!! + } + + surf_Locked--; +} + + +// --------------------------------------------------------------------------- +// miscellaneous +// --------------------------------------------------------------------------- + +void grSurface::attach_to_window(unsigned handle) +{ + ASSERT(m_SurfInit); + ddgr_surf_AttachHandle(&ddsfObj, handle); +} + + +// Private functions +// conversion_xlat +// --------------------------------------------------------------------------- + +void grSurface::xlat8_16(char *data, int w, int h,char *pal) +{ +/* copy from 8bit source bitmap to destination surface just created and + locked +*/ + ushort *dptr; + char *sptr; + int row, col; + int rowsize_w; + int height, width; + ubyte *upal=(ubyte *)pal; + + dptr = (ushort *)m_DataPtr; + sptr = (char *)data; + rowsize_w = m_DataRowsize/2; + + height = SET_MIN(h, ddsfObj.h); + width = SET_MIN(w, ddsfObj.w); + + for (row = 0; row < height; row++) + { + for (col = 0; col < width; col++) + { + ubyte spix=sptr[col]; + if (spix!=0) + { + int r=upal[spix*3]>>3; + int g=upal[spix*3+1]>>2; + int b=upal[spix*3+2]>>3; + ushort destpix=(r<<11) | (g<<5) | b; + + dptr[col] = destpix; + } + else + dptr[col]=0; + } + sptr += 256; + dptr += rowsize_w; + } +} + +void grSurface::xlat16_16(char *data, int w, int h,int format) +{ +/* 16-bit copy from source bitmap to destination surface just created and + locked + This function performs scaling if the source width and height don't match + that of the destinations - JL +*/ + ushort *sptr, *dptr; + int row, col; + int rowsize_w; + int height, width; + fix xstep=IntToFix(w)/ddsfObj.w; + fix ystep=IntToFix(h)/ddsfObj.h; + fix fu=0,fv=0; + + dptr = (ushort *)m_DataPtr; + sptr = (ushort *)data; + rowsize_w = m_DataRowsize/2; + + height=ddsfObj.h; + width=ddsfObj.w; + + //height = SET_MIN(h, ddsfObj.h); + //width = SET_MIN(w, ddsfObj.w); + + for (row = 0; row < height; row++,fv+=ystep,fu=0) + { + for (col = 0; col < width; col++,fu+=xstep) + { + int newpix; + + if (format==BITMAP_FORMAT_1555) + newpix=sptr[(FixToInt(fv)*w)+FixToInt(fu)]; + else + { + int temp_pix=sptr[(FixToInt(fv)*w)+FixToInt(fu)]; + int r=(temp_pix>>8) & 0xf; + int g=(temp_pix>>4) & 0xf; + int b=(temp_pix) & 0xf; + + r<<=4; + g<<=4; + b<<=4; + + newpix=OPAQUE_FLAG|GR_RGB16(r,g,b); + } + + dptr[col] = newpix; + } + + dptr += rowsize_w; + } +} + + +void grSurface::xlat16_24(char *data, int w, int h) +{ + ushort *sptr; + char *dptr; + int scol, dcol, row; + int height, width; + + dptr = (char *)m_DataPtr; + sptr = (ushort *)data; + height = SET_MIN(h, ddsfObj.h); + width = SET_MIN(w, ddsfObj.w); + + for (row = 0; row < height; row++) + { + dcol = 0; + for (scol = 0; scol < width; scol++) + { + ushort pix; + ddgr_color new_color; + char r,g,b; + pix = sptr[scol]; + + new_color=GR_16_TO_COLOR (pix); + + r=GR_COLOR_RED (new_color); + g=GR_COLOR_GREEN (new_color); + b=GR_COLOR_BLUE (new_color); + + dptr[dcol++] = r; + dptr[dcol++] = g; + dptr[dcol++] = b; + } + sptr += w; + dptr += m_DataRowsize; + } +} + + +void grSurface::xlat24_16(char *data, int w, int h) +{ + char *sptr; + ushort *dptr; + int scol, dcol, row; + int rowsize_w, height, width; + + dptr = (ushort *)m_DataPtr; + sptr = (char *)data; + rowsize_w = m_DataRowsize/2; + height = SET_MIN(h, ddsfObj.h); + width = SET_MIN(w, ddsfObj.w); + + for (row = 0; row < height; row++) + { + scol = 0; + for (dcol = 0; dcol < width;dcol++) + { + ushort pix; + char r,g,b; + r = sptr[scol++]; + g = sptr[scol++]; + b = sptr[scol++]; + pix = ((ushort)(r>>3) << 11) + ((ushort)(g>>2) << 5) + ((ushort)(b>>3)); + dptr[dcol] = pix; + } + sptr += (w*3); + dptr += m_DataRowsize; + } +} + + +void grSurface::xlat32_16(char *data, int w, int h) +{ + unsigned *sptr; + ushort *dptr; + int col, row; + int rowsize_w, height, width; + + ASSERT((w%4) == 0); + + dptr = (ushort *)m_DataPtr; + sptr = (unsigned *)data; + rowsize_w = m_DataRowsize/2; + height = SET_MIN(h, ddsfObj.h); + width = SET_MIN(w, ddsfObj.w); + + for (row = 0; row < height; row++) + { + for (col = 0; col < width; col++) + { + unsigned pix; + ushort spix; + + pix = sptr[col]; + spix = (ushort)(pix & 0x000000f8) >> 3; + spix |=(ushort)(pix & 0x0000fc00) >> 5; + spix |=(ushort)(pix & 0x00f80000) >> 8; + dptr[col] = spix; + } + sptr += w; + dptr += rowsize_w; + } +} + + +//----------------------------------------------------------------------------- +// surface list functions + +void grSurface::add_to_list(ddgr_surface *sf) +{ + ddgr_surface_node *node; + + ASSERT(sf != NULL); + + if (!grSurface::surf_list_cur) { + // first surface on list + node = new ddgr_surface_node; + node->sf = sf; + node->prev = NULL; + grSurface::surf_list = grSurface::surf_list_cur = node; + } + else { + // next surface on list. + node = new ddgr_surface_node; + node->sf = sf; + node->prev = grSurface::surf_list_cur; + grSurface::surf_list_cur = node; + } + +// mprintf((0, "Allocated surface %s [%d].\n", sf->name, DDData.surfs_alloc)); +} + + +void grSurface::remove_from_list(ddgr_surface *sf) +{ + ddgr_surface_node *node, *next_node; + + ASSERT(grSurface::surf_list_cur); + ASSERT(sf != NULL); + +// node is current node. next node is node after it on the list. since we are +// going from end to beginning, this may confuse you. + node = grSurface::surf_list_cur; + next_node = NULL; // grSurface::surf_list_cur should be at end of list, so no next node + + while (node) + { + if (node->sf == sf) { + if (next_node) { // delete from within list. + next_node->prev = node->prev; + } + else { // delete from end of list. set list tail to deleted node prev. + grSurface::surf_list_cur = node->prev; + } + delete node; + + node = NULL; // break out of loop + } + else { + // go down one node. + next_node = node; + node = node->prev; + } + } + +// mprintf((0, "Freed surface %s [%d].\n", sf->name, DDData.surfs_alloc)); +} + + + diff --git a/2dlib/viewport.cpp b/2dlib/viewport.cpp new file mode 100644 index 000000000..22ab622bf --- /dev/null +++ b/2dlib/viewport.cpp @@ -0,0 +1,403 @@ +/* + * $Logfile: /DescentIII/Main/2dlib/viewport.cpp $ + * $Revision: 7 $ + * $Date: 12/19/97 5:23p $ + * $Author: Samir $ + * + * this class manages a specific viewing region of either a screen or bitmap. + * functions such as font setting and bitmap bltting are supported + * under this class. + * + * this class also spawns a pen object which effectively locks the screen or + * bitmap associated with it. + * + * $Log: /DescentIII/Main/2dlib/viewport.cpp $ + * + * 7 12/19/97 5:23p Samir + * Cleaned up a lot of code to call renderer code. + * + * 6 12/19/97 12:32p Samir + * Support alpha for text. Stubbed out clipping support. + * + * 5 11/18/97 12:32p Samir + * Took out useless lines. + * + * 4 11/14/97 12:05p Samir + * don't free font, just init it. + * + * 20 6/13/97 1:17p Samir + * Fucked up last checkin + * + * 19 6/13/97 1:00p Samir + * lock when called will always lock, so if the lock fails, then it's not + * viewport's job to fix it. + * + * 18 6/12/97 6:29p Samir + * DDGR v2.0 Changes in 2d system implemented. + * + * 17 6/06/97 12:18p Samir + * OpenGL HACKS to call renderer for some drawing functions. Must fix. + * + * 16 5/15/97 3:47p Samir + * Fixed lock_clear. + * + * 15 5/13/97 6:36p Samir + * added lock_clear functions. + * + * 14 4/01/97 4:37p Samir + * Simply better circle functions using viewport line and pixel functions. + * made an hline function that clips and a setpixel function that clips. + * + * 13 3/28/97 3:07p Samir + * Clear function now takes a color + * + * 12 3/28/97 2:50p Samir + * Fixed clip_rect fuckup. + * + * 11 2/18/97 1:04p Samir + * Changed prototype for setpixel + * + * 10 2/18/97 11:38a Samir + * Added getpixel and setpixel to grViewport. NOTE: you must lock the + * viewport when doing these ops. + * + * 9 2/04/97 4:56p Samir + * added 'hack' in viewport lock. see the function to see what I mean. + * + * 8 2/04/97 12:50p Samir + * Viewports now lock on CLIP_LEFT, CLIP_TOP + * + * 7 2/04/97 11:14a Samir + * Improved clipping system by adding a separate clipport based off of the + * viewport's rectangle in it's parent surface + * + * 6 2/03/97 7:56p Samir + * + * 5 1/31/97 11:02a Samir + * Adjusted clear function to use x,y,width,height parms. + * + * 4 1/30/97 6:28p Samir + * Stupid me, left some dummy text causing errors. + * + * 3 1/30/97 6:01p Samir + * The partition of memory and os surfaces as well as the change in + * ddgr_surface structure and all related changes. + * + * $NoKeywords: $ + */ + +#include "gr.h" +#include "lib2d.h" +#include "renderer.h" + +#include "pserror.h" + +#define CLIP_LEFT (vp_InitLeft+vp_Left) +#define CLIP_TOP (vp_InitTop+vp_Top) +#define CLIP_RIGHT (vp_InitLeft+vp_Right) +#define CLIP_BOTTOM (vp_InitTop+vp_Bottom) + + +int grViewport::vp_Locked = 0; +grViewport *grViewport::vp_Current_VP = NULL; + + + +// --------------------------------------------------------------------------- +// grViewport constructor and destructor +// --------------------------------------------------------------------------- + +grViewport::grViewport(grScreen *scr_parent) +{ + ASSERT(scr_parent != NULL); + + sf_Parent = scr_parent; // screen is a child of grSurface, so we + // can do this. + vp_InitLeft = vp_Left = 0; // define bounds = entire screen + vp_InitTop = vp_Top = 0; + vp_InitRight = vp_Right = sf_Parent->width()-1; + vp_InitBottom = vp_Bottom = sf_Parent->height()-1; + +// initialize font object +// setup text settings. + char_Props.col[0] = GR_WHITE; + char_Props.col[1] = GR_WHITE; + char_Props.col[2] = GR_WHITE; + char_Props.col[3] = GR_WHITE; + char_Props.alpha = 255; + + text_Spacing = 1; + text_Line_spacing = 1; + text_Font.init(DEFAULT_FONT); +} + + +grViewport::grViewport(grSurface *sf_parent) +{ + ASSERT(sf_parent != NULL); + + sf_Parent = sf_parent; + + vp_InitLeft = vp_Left = 0; // define bounds = entire bitmap + vp_InitTop = vp_Top = 0; + vp_InitRight = vp_Right = sf_Parent->width()-1; + vp_InitBottom = vp_Bottom = sf_Parent->height()-1; + +// initialize font object +// setup text settings. + char_Props.col[0] = GR_WHITE; + char_Props.col[1] = GR_WHITE; + char_Props.col[2] = GR_WHITE; + char_Props.col[3] = GR_WHITE; + char_Props.alpha = 255; + + text_Spacing = 1; + text_Line_spacing = 1; + text_Font.init(DEFAULT_FONT); +} + + + +grViewport::~grViewport() +{ + ASSERT(!vp_Locked); // Viewport shouldn't be locked here! + +// text_Font.free(); +} + + +void grViewport::set_clipping_rect(int left, int top, int right, int bottom) +{ + ASSERT(!vp_Locked); // YOU CANT DO THIS WHILE LOCKED! + +// these are local to the viewport. +// vp_Left = left; +// vp_Top = top; +// vp_Right = right; +// vp_Bottom = bottom; +} + + +// these functions set and restore the state of the viewport (font, colors, etc.) +void grViewport::get_state(tVPState *state) +{ + state->font = text_Font; + state->text_spacing = text_Spacing; + state->text_line_spacing = text_Line_spacing; + get_char_properties(&state->chprops); +} + + +void grViewport::set_state(const tVPState *state) +{ + set_char_properties(GR_VPCP_ALL, &state->chprops); + text_Font = state->font; + text_Spacing = state->text_spacing; + text_Line_spacing = state->text_line_spacing; +} + + +// --------------------------------------------------------------------------- +// font functions +// --------------------------------------------------------------------------- + + +// --------------------------------------------------------------------------- +// bltting functions +// --------------------------------------------------------------------------- + +void grViewport::clear(ddgr_color col) // clear with background color +{ + sf_Parent->clear(CLIP_LEFT, CLIP_TOP, (vp_Right-vp_Left)+1, (vp_Bottom-vp_Top)+1,col); +} + + +void grViewport::blt(int dx, int dy, grSurface *ssf) +{ + int x1,y1,x2,y2, sx, sy; + +// perform rectangular clipping + x1 = global_x(dx); y1 = global_y(dy); + x2 = x1+ssf->width()-1; y2 = y1+ssf->height()-1; + + if (x1 < CLIP_LEFT) sx = CLIP_LEFT - x1; + else sx = 0; + if (y1 < CLIP_TOP) sy = CLIP_TOP - y1; + else sy = 0; + + if (clip_rect(x1, y1, x2, y2)==2) return; + + sf_Parent->blt(x1, y1, ssf, sx, sy, x2-x1+1, y2-y1+1); +} + + +void grViewport::blt(int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh) +{ + int x1, y1, x2, y2; + + x1 = global_x(dx); y1 = global_y(dy); x2 = x1+sw-1; y2 = y1+sh-1; + if (clip_rect(x1, y1, x2, y2)==2) return; + + sf_Parent->blt(x1, y1, ssf, sx, sy, x2-x1+1, y2-y1+1); +} + + +// --------------------------------------------------------------------------- +// general lock creation functions +// --------------------------------------------------------------------------- + +grSurface* grViewport::lock() +{ + int rowsize; + + ASSERT(!vp_Locked); + + sf_Parent->lock(CLIP_LEFT, CLIP_TOP, &rowsize); + vp_Locked = 1; + + if (vp_Locked) { + pen_Obj.sf = &sf_Parent->ddsfObj; + pen_Obj.rowsize = sf_Parent->rowsize(); + pen_Obj.rgbcolor = GR_RGB(255,255,255); + + // SUPERHACK. Pen functions don't use the adjusted pointer of grSurface, but use the + // base data pointer provided by locking the entire surface at (0,0). Pen functions + // use global coordinates, so they can calculate the adjusted pointer themselves. This + // makes global_x and global_y consisitent with the surface base pointer. This problem + // was caused by the clipping rect being a virtual viewport within a viewport to make + // the 3d system work better. + pen_Obj.data = sf_Parent->m_OrigDataPtr; + } + + grViewport::vp_Current_VP = this; + + return sf_Parent; +} + + +void grViewport::unlock() +{ + ASSERT(vp_Locked); + + sf_Parent->unlock(); + vp_Locked = 0; + + grViewport::vp_Current_VP = NULL; + + pen_Obj.data = NULL; + pen_Obj.rowsize = 0; + pen_Obj.sf = NULL; +} + + +// --------------------------------------------------------------------------- +// miscellaneous viewport functions +// --------------------------------------------------------------------------- + +void grViewport::setpixel(ddgr_color col, int x, int y) +{ + ASSERT(vp_Locked); + + rend_SetPixel(col, x,y); +} + + +void grViewport::setpixelc(ddgr_color col, int x, int y) +{ + ASSERT(vp_Locked); + + if (x < CLIP_LEFT || x > CLIP_RIGHT || y < CLIP_TOP || y > CLIP_BOTTOM) return; + + rend_SetPixel(col, x,y); +} + + + +ddgr_color grViewport::getpixel(int x, int y) +{ + ASSERT(vp_Locked); + + return rend_GetPixel(x,y); +} + + +ddgr_color grViewport::getpixelc(int x, int y) +{ + ASSERT(vp_Locked); + + if (x < CLIP_LEFT || x > CLIP_RIGHT || y < CLIP_TOP || y > CLIP_BOTTOM) return 0; + + return rend_GetPixel(x,y); +} + + +void grViewport::lock_clear(ddgr_color col) +{ + int l=CLIP_LEFT,t = CLIP_TOP,r=CLIP_RIGHT, b=CLIP_BOTTOM; + ddgr_surface *memsurf = sf_Parent->scratch_mem_surf; + + ASSERT(vp_Locked); + + if (sf_Parent->ddsfObj.flags & SURFFLAG_RENDERER) { + rend_FillRect(col, l,t,r,b); + } + else { + memsurf->bpp = pen_Obj.sf->bpp; + + gr_mem_surf_Init(memsurf, pen_Obj.data, pen_Obj.rowsize); + gr_mem_surf_Clear(memsurf, col, l,t,r-l+1,b-t+1); + } +} + + +void grViewport::lock_clear(ddgr_color col, int l, int t, int r, int b) +{ + ddgr_surface *memsurf = sf_Parent->scratch_mem_surf; + + ASSERT(vp_Locked); + + l = global_x(l); + t = global_y(t); + r = global_x(r); + b = global_y(b); + + clip_rect(l,t,r,b); + + if (sf_Parent->ddsfObj.flags & SURFFLAG_RENDERER) { + rend_FillRect(col, l,t,r,b); + } + else { + memsurf->bpp = pen_Obj.sf->bpp; + gr_mem_surf_Init(memsurf, pen_Obj.data, pen_Obj.rowsize); + gr_mem_surf_Clear(memsurf, col, l,t,r-l+1,b-t+1); + } +} + + + +// --------------------------------------------------------------------------- +// private functions +// --------------------------------------------------------------------------- + +// grPen::clip_rect +// returns 2 if totally clipped +// returns 1 if partially clipped +// returns 0 if not clipped. + +int grViewport::clip_rect(int &l, int &t, int &r, int &b) +{ + int clipped = 0; + + if (l > r) SWAP(l,r); + if (t > b) SWAP(t,b); + + if (l > CLIP_RIGHT || t > CLIP_BOTTOM || r < CLIP_LEFT || b < CLIP_TOP) return 2; + + if (l < CLIP_LEFT) { l = CLIP_LEFT; clipped = 1; } + if (t < CLIP_TOP) { t = CLIP_TOP; clipped = 1; } + if (r > CLIP_RIGHT) { r = CLIP_RIGHT; clipped = 1; } + if (b > CLIP_BOTTOM) { b = CLIP_BOTTOM; clipped = 1; } + + return clipped; +} + diff --git a/AngelScript/CMakeLists.txt b/AngelScript/CMakeLists.txt new file mode 100644 index 000000000..6eadadd06 --- /dev/null +++ b/AngelScript/CMakeLists.txt @@ -0,0 +1,13 @@ +include_directories("include" "source" "scriptany" "scriptarray" "scriptdictionary" "scriptfile" "scriptstring") + +file(GLOB_RECURSE HEADERS "*.h") +file(GLOB_RECURSE CPPS "*.cpp") + +SOURCE_GROUP(Main REGULAR_EXPRESSION .*) +SOURCE_GROUP(ScriptAny REGULAR_EXPRESSION scriptany?.*) +SOURCE_GROUP(ScriptArray REGULAR_EXPRESSION scriptarray?.*) +SOURCE_GROUP(ScriptDictionary REGULAR_EXPRESSION scriptdictionary?.*) +SOURCE_GROUP(ScriptFile REGULAR_EXPRESSION scriptfile?.*) +SOURCE_GROUP(ScriptString REGULAR_EXPRESSION scriptstring?.*) + +ADD_LIBRARY(AngelScript STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/AngelScript/include/angelscript.h b/AngelScript/include/angelscript.h new file mode 100644 index 000000000..29b70de76 --- /dev/null +++ b/AngelScript/include/angelscript.h @@ -0,0 +1,1614 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// angelscript.h +// +// The script engine interface +// + + +#ifndef ANGELSCRIPT_H +#define ANGELSCRIPT_H + +#include + +#ifdef AS_USE_NAMESPACE + #define BEGIN_AS_NAMESPACE namespace AngelScript { + #define END_AS_NAMESPACE } +#else + #define BEGIN_AS_NAMESPACE + #define END_AS_NAMESPACE +#endif + +BEGIN_AS_NAMESPACE + +// AngelScript version + +#define ANGELSCRIPT_VERSION 21801 +#define ANGELSCRIPT_VERSION_STRING "2.18.1 WIP" + +// Data types + +class asIScriptEngine; +class asIScriptModule; +class asIScriptContext; +class asIScriptGeneric; +class asIScriptObject; +class asIScriptArray; +class asIObjectType; +class asIScriptFunction; +class asIBinaryStream; +class asIJITCompiler; + +// Enumerations and constants + +// Engine properties +enum asEEngineProp +{ + asEP_ALLOW_UNSAFE_REFERENCES = 1, + asEP_OPTIMIZE_BYTECODE = 2, + asEP_COPY_SCRIPT_SECTIONS = 3, + asEP_MAX_STACK_SIZE = 4, + asEP_USE_CHARACTER_LITERALS = 5, + asEP_ALLOW_MULTILINE_STRINGS = 6, + asEP_ALLOW_IMPLICIT_HANDLE_TYPES = 7, + asEP_BUILD_WITHOUT_LINE_CUES = 8, + asEP_INIT_GLOBAL_VARS_AFTER_BUILD = 9, + asEP_REQUIRE_ENUM_SCOPE = 10, + asEP_SCRIPT_SCANNER = 11, + asEP_INCLUDE_JIT_INSTRUCTIONS = 12, + asEP_STRING_ENCODING = 13 +}; + +// Calling conventions +enum asECallConvTypes +{ + asCALL_CDECL = 0, + asCALL_STDCALL = 1, + asCALL_THISCALL = 2, + asCALL_CDECL_OBJLAST = 3, + asCALL_CDECL_OBJFIRST = 4, + asCALL_GENERIC = 5 +}; + +// Object type flags +enum asEObjTypeFlags +{ + asOBJ_REF = 0x01, + asOBJ_VALUE = 0x02, + asOBJ_GC = 0x04, + asOBJ_POD = 0x08, + asOBJ_NOHANDLE = 0x10, + asOBJ_SCOPED = 0x20, + asOBJ_TEMPLATE = 0x40, + asOBJ_APP_CLASS = 0x100, + asOBJ_APP_CLASS_CONSTRUCTOR = 0x200, + asOBJ_APP_CLASS_DESTRUCTOR = 0x400, + asOBJ_APP_CLASS_ASSIGNMENT = 0x800, + asOBJ_APP_CLASS_C = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR), + asOBJ_APP_CLASS_CD = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR), + asOBJ_APP_CLASS_CA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_CDA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_CONSTRUCTOR + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_D = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR), + asOBJ_APP_CLASS_A = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_CLASS_DA = (asOBJ_APP_CLASS + asOBJ_APP_CLASS_DESTRUCTOR + asOBJ_APP_CLASS_ASSIGNMENT), + asOBJ_APP_PRIMITIVE = 0x1000, + asOBJ_APP_FLOAT = 0x2000, + asOBJ_MASK_VALID_FLAGS = 0x3F7F, + asOBJ_SCRIPT_OBJECT = 0x10000 +}; + +// Behaviours +enum asEBehaviours +{ + // Value object memory management + asBEHAVE_CONSTRUCT, + asBEHAVE_DESTRUCT, + + // Reference object memory management + asBEHAVE_FACTORY, + asBEHAVE_ADDREF, + asBEHAVE_RELEASE, + + // Object operators + asBEHAVE_VALUE_CAST, + asBEHAVE_IMPLICIT_VALUE_CAST, + asBEHAVE_REF_CAST, + asBEHAVE_IMPLICIT_REF_CAST, + asBEHAVE_INDEX, + asBEHAVE_TEMPLATE_CALLBACK, + + // Garbage collection behaviours + asBEHAVE_FIRST_GC, + asBEHAVE_GETREFCOUNT = asBEHAVE_FIRST_GC, + asBEHAVE_SETGCFLAG, + asBEHAVE_GETGCFLAG, + asBEHAVE_ENUMREFS, + asBEHAVE_RELEASEREFS, + asBEHAVE_LAST_GC = asBEHAVE_RELEASEREFS, + + asBEHAVE_MAX +}; + +// Return codes +enum asERetCodes +{ + asSUCCESS = 0, + asERROR = -1, + asCONTEXT_ACTIVE = -2, + asCONTEXT_NOT_FINISHED = -3, + asCONTEXT_NOT_PREPARED = -4, + asINVALID_ARG = -5, + asNO_FUNCTION = -6, + asNOT_SUPPORTED = -7, + asINVALID_NAME = -8, + asNAME_TAKEN = -9, + asINVALID_DECLARATION = -10, + asINVALID_OBJECT = -11, + asINVALID_TYPE = -12, + asALREADY_REGISTERED = -13, + asMULTIPLE_FUNCTIONS = -14, + asNO_MODULE = -15, + asNO_GLOBAL_VAR = -16, + asINVALID_CONFIGURATION = -17, + asINVALID_INTERFACE = -18, + asCANT_BIND_ALL_FUNCTIONS = -19, + asLOWER_ARRAY_DIMENSION_NOT_REGISTERED = -20, + asWRONG_CONFIG_GROUP = -21, + asCONFIG_GROUP_IS_IN_USE = -22, + asILLEGAL_BEHAVIOUR_FOR_TYPE = -23, + asWRONG_CALLING_CONV = -24, + asBUILD_IN_PROGRESS = -25, + asINIT_GLOBAL_VARS_FAILED = -26 +}; + +// Context states +enum asEContextState +{ + asEXECUTION_FINISHED = 0, + asEXECUTION_SUSPENDED = 1, + asEXECUTION_ABORTED = 2, + asEXECUTION_EXCEPTION = 3, + asEXECUTION_PREPARED = 4, + asEXECUTION_UNINITIALIZED = 5, + asEXECUTION_ACTIVE = 6, + asEXECUTION_ERROR = 7 +}; + +#ifdef AS_DEPRECATED +// Deprecated since 2.18.0, 2009-12-08 +// ExecuteString flags +enum asEExecStrFlags +{ + asEXECSTRING_ONLY_PREPARE = 1, + asEXECSTRING_USE_MY_CONTEXT = 2 +}; +#endif + +// Message types +enum asEMsgType +{ + asMSGTYPE_ERROR = 0, + asMSGTYPE_WARNING = 1, + asMSGTYPE_INFORMATION = 2 +}; + +// Garbage collector flags +enum asEGCFlags +{ + asGC_FULL_CYCLE = 1, + asGC_ONE_STEP = 2, + asGC_DESTROY_GARBAGE = 4, + asGC_DETECT_GARBAGE = 8 +}; + +// Token classes +enum asETokenClass +{ + asTC_UNKNOWN = 0, + asTC_KEYWORD = 1, + asTC_VALUE = 2, + asTC_IDENTIFIER = 3, + asTC_COMMENT = 4, + asTC_WHITESPACE = 5 +}; + +// Prepare flags +const int asPREPARE_PREVIOUS = -1; + +// Config groups +const char * const asALL_MODULES = (const char * const)-1; + +// Type id flags +enum asETypeIdFlags +{ + asTYPEID_VOID = 0, + asTYPEID_BOOL = 1, + asTYPEID_INT8 = 2, + asTYPEID_INT16 = 3, + asTYPEID_INT32 = 4, + asTYPEID_INT64 = 5, + asTYPEID_UINT8 = 6, + asTYPEID_UINT16 = 7, + asTYPEID_UINT32 = 8, + asTYPEID_UINT64 = 9, + asTYPEID_FLOAT = 10, + asTYPEID_DOUBLE = 11, + asTYPEID_OBJHANDLE = 0x40000000, + asTYPEID_HANDLETOCONST = 0x20000000, + asTYPEID_MASK_OBJECT = 0x1C000000, + asTYPEID_APPOBJECT = 0x04000000, + asTYPEID_SCRIPTOBJECT = 0x08000000, + asTYPEID_SCRIPTARRAY = 0x10000000, + asTYPEID_MASK_SEQNBR = 0x03FFFFFF +}; + +// Type modifiers +enum asETypeModifiers +{ + asTM_NONE = 0, + asTM_INREF = 1, + asTM_OUTREF = 2, + asTM_INOUTREF = 3 +}; + +// GetModule flags +enum asEGMFlags +{ + asGM_ONLY_IF_EXISTS = 0, + asGM_CREATE_IF_NOT_EXISTS = 1, + asGM_ALWAYS_CREATE = 2 +}; + +// Compile flags +enum asECompileFlags +{ + asCOMP_ADD_TO_MODULE = 1 +}; + +// +// asBYTE = 8 bits +// asWORD = 16 bits +// asDWORD = 32 bits +// asQWORD = 64 bits +// asPWORD = size of pointer +// +typedef unsigned char asBYTE; +typedef unsigned short asWORD; +typedef unsigned int asUINT; +typedef size_t asPWORD; +#ifdef __LP64__ + typedef unsigned int asDWORD; + typedef unsigned long asQWORD; + typedef long asINT64; +#else + typedef unsigned long asDWORD; + #if defined(__GNUC__) || defined(__MWERKS__) + typedef unsigned long long asQWORD; + typedef long long asINT64; + #else + typedef unsigned __int64 asQWORD; + typedef __int64 asINT64; + #endif +#endif + +typedef void (*asFUNCTION_t)(); +typedef void (*asGENFUNC_t)(asIScriptGeneric *); +typedef void *(*asALLOCFUNC_t)(size_t); +typedef void (*asFREEFUNC_t)(void *); + +#define asFUNCTION(f) asFunctionPtr(f) +#if defined(_MSC_VER) && _MSC_VER <= 1200 +// MSVC 6 has a bug that prevents it from properly compiling using the correct asFUNCTIONPR with operator > +// so we need to use ordinary C style cast instead of static_cast. The drawback is that the compiler can't +// check that the cast is really valid. +#define asFUNCTIONPR(f,p,r) asFunctionPtr((void (*)())((r (*)p)(f))) +#else +#define asFUNCTIONPR(f,p,r) asFunctionPtr((void (*)())(static_cast(f))) +#endif + +#ifndef AS_NO_CLASS_METHODS + +class asCUnknownClass; +typedef void (asCUnknownClass::*asMETHOD_t)(); + +struct asSFuncPtr +{ + union + { + // The largest known method point is 20 bytes (MSVC 64bit), + // but with 8byte alignment this becomes 24 bytes. So we need + // to be able to store at least that much. + char dummy[25]; + struct {asMETHOD_t mthd; char dummy[25-sizeof(asMETHOD_t)];} m; + struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; + } ptr; + asBYTE flag; // 1 = generic, 2 = global func, 3 = method +}; + +#define asMETHOD(c,m) asSMethodPtr::Convert((void (c::*)())(&c::m)) +#define asMETHODPR(c,m,p,r) asSMethodPtr::Convert(static_cast(&c::m)) + +#else // Class methods are disabled + +struct asSFuncPtr +{ + union + { + char dummy[25]; // largest known class method pointer + struct {asFUNCTION_t func; char dummy[25-sizeof(asFUNCTION_t)];} f; + } ptr; + asBYTE flag; // 1 = generic, 2 = global func +}; + +#endif + +struct asSMessageInfo +{ + const char *section; + int row; + int col; + asEMsgType type; + const char *message; +}; + + +// API functions + +// ANGELSCRIPT_EXPORT is defined when compiling the dll or lib +// ANGELSCRIPT_DLL_LIBRARY_IMPORT is defined when dynamically linking to the +// dll through the link lib automatically generated by MSVC++ +// ANGELSCRIPT_DLL_MANUAL_IMPORT is defined when manually loading the dll +// Don't define anything when linking statically to the lib + +#ifdef WIN32 + #ifdef ANGELSCRIPT_EXPORT + #define AS_API __declspec(dllexport) + #elif defined ANGELSCRIPT_DLL_LIBRARY_IMPORT + #define AS_API __declspec(dllimport) + #else // statically linked library + #define AS_API + #endif +#else + #define AS_API +#endif + +#ifndef ANGELSCRIPT_DLL_MANUAL_IMPORT +extern "C" +{ + // Engine + AS_API asIScriptEngine * asCreateScriptEngine(asDWORD version); + AS_API const char * asGetLibraryVersion(); + AS_API const char * asGetLibraryOptions(); + + // Context + AS_API asIScriptContext * asGetActiveContext(); + + // Thread support + AS_API int asThreadCleanup(); + + // Memory management + AS_API int asSetGlobalMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc); + AS_API int asResetGlobalMemoryFunctions(); +} +#endif // ANGELSCRIPT_DLL_MANUAL_IMPORT + +// Interface declarations + +class asIScriptEngine +{ +public: + // Memory management + virtual int AddRef() = 0; + virtual int Release() = 0; + + // Engine properties + virtual int SetEngineProperty(asEEngineProp property, asPWORD value) = 0; + virtual asPWORD GetEngineProperty(asEEngineProp property) = 0; + + // Compiler messages + virtual int SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv) = 0; + virtual int ClearMessageCallback() = 0; + virtual int WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message) = 0; + + // JIT Compiler + virtual int SetJITCompiler(asIJITCompiler *compiler) = 0; + virtual asIJITCompiler *GetJITCompiler() = 0; + + // Global functions + virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) = 0; + virtual int GetGlobalFunctionCount() = 0; + virtual int GetGlobalFunctionIdByIndex(asUINT index) = 0; + + // Global properties + virtual int RegisterGlobalProperty(const char *declaration, void *pointer) = 0; + virtual int GetGlobalPropertyCount() = 0; + virtual int GetGlobalPropertyByIndex(asUINT index, const char **name, int *typeId = 0, bool *isConst = 0, const char **configGroup = 0, void **pointer = 0) = 0; + + // Object types + virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags) = 0; + virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset) = 0; + virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) = 0; + virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) = 0; + virtual int RegisterInterface(const char *name) = 0; + virtual int RegisterInterfaceMethod(const char *intf, const char *declaration) = 0; + virtual int GetObjectTypeCount() = 0; + virtual asIObjectType *GetObjectTypeByIndex(asUINT index) = 0; + + // String factory + virtual int RegisterStringFactory(const char *datatype, const asSFuncPtr &factoryFunc, asDWORD callConv) = 0; + virtual int GetStringFactoryReturnTypeId() = 0; + + // Enums + virtual int RegisterEnum(const char *type) = 0; + virtual int RegisterEnumValue(const char *type, const char *name, int value) = 0; + virtual int GetEnumCount() = 0; + virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId, const char **configGroup = 0) = 0; + virtual int GetEnumValueCount(int enumTypeId) = 0; + virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) = 0; + + // Typedefs + virtual int RegisterTypedef(const char *type, const char *decl) = 0; + virtual int GetTypedefCount() = 0; + virtual const char *GetTypedefByIndex(asUINT index, int *typeId, const char **configGroup = 0) = 0; + + // Configuration groups + virtual int BeginConfigGroup(const char *groupName) = 0; + virtual int EndConfigGroup() = 0; + virtual int RemoveConfigGroup(const char *groupName) = 0; + virtual int SetConfigGroupModuleAccess(const char *groupName, const char *module, bool hasAccess) = 0; + + // Script modules + virtual asIScriptModule *GetModule(const char *module, asEGMFlags flag = asGM_ONLY_IF_EXISTS) = 0; + virtual int DiscardModule(const char *module) = 0; + + // Script functions + virtual asIScriptFunction *GetFunctionDescriptorById(int funcId) = 0; + + // Type identification + virtual asIObjectType *GetObjectTypeById(int typeId) = 0; + virtual int GetTypeIdByDecl(const char *decl) = 0; + virtual const char *GetTypeDeclaration(int typeId) = 0; + virtual int GetSizeOfPrimitiveType(int typeId) = 0; + + // Script execution + virtual asIScriptContext *CreateContext() = 0; + virtual void *CreateScriptObject(int typeId) = 0; + virtual void *CreateScriptObjectCopy(void *obj, int typeId) = 0; + virtual void CopyScriptObject(void *dstObj, void *srcObj, int typeId) = 0; + virtual void ReleaseScriptObject(void *obj, int typeId) = 0; + virtual void AddRefScriptObject(void *obj, int typeId) = 0; + virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) = 0; + + // String interpretation + virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, int *tokenLength = 0) = 0; + + // Garbage collection + virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE) = 0; + virtual void GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed = 0, asUINT *totalDetected = 0) = 0; + virtual void NotifyGarbageCollectorOfNewObject(void *obj, int typeId) = 0; + virtual void GCEnumCallback(void *reference) = 0; + + // User data + virtual void *SetUserData(void *data) = 0; + virtual void *GetUserData() = 0; + +#ifdef AS_DEPRECATED + // deprecated since 2009-12-08, 2.18.0 + virtual int ExecuteString(const char *module, const char *script, asIScriptContext **ctx = 0, asDWORD flags = 0) = 0; +#endif + +protected: + virtual ~asIScriptEngine() {} +}; + +class asIScriptModule +{ +public: + virtual asIScriptEngine *GetEngine() = 0; + virtual void SetName(const char *name) = 0; + virtual const char *GetName() = 0; + + // Compilation + virtual int AddScriptSection(const char *name, const char *code, size_t codeLength = 0, int lineOffset = 0) = 0; + virtual int Build() = 0; + virtual int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asIScriptFunction **outFunc) = 0; + virtual int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) = 0; + + // Functions + virtual int GetFunctionCount() = 0; + virtual int GetFunctionIdByIndex(int index) = 0; + virtual int GetFunctionIdByName(const char *name) = 0; + virtual int GetFunctionIdByDecl(const char *decl) = 0; + virtual asIScriptFunction *GetFunctionDescriptorByIndex(int index) = 0; + virtual asIScriptFunction *GetFunctionDescriptorById(int funcId) = 0; + virtual int RemoveFunction(int funcId) = 0; + + // Global variables + virtual int ResetGlobalVars() = 0; + virtual int GetGlobalVarCount() = 0; + virtual int GetGlobalVarIndexByName(const char *name) = 0; + virtual int GetGlobalVarIndexByDecl(const char *decl) = 0; + virtual const char *GetGlobalVarDeclaration(int index) = 0; + virtual const char *GetGlobalVarName(int index) = 0; + virtual int GetGlobalVarTypeId(int index, bool *isConst = 0) = 0; + virtual void *GetAddressOfGlobalVar(int index) = 0; + virtual int RemoveGlobalVar(int index) = 0; + + // Type identification + virtual int GetObjectTypeCount() = 0; + virtual asIObjectType *GetObjectTypeByIndex(asUINT index) = 0; + virtual int GetTypeIdByDecl(const char *decl) = 0; + + // Enums + virtual int GetEnumCount() = 0; + virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId) = 0; + virtual int GetEnumValueCount(int enumTypeId) = 0; + virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) = 0; + + // Typedefs + virtual int GetTypedefCount() = 0; + virtual const char *GetTypedefByIndex(asUINT index, int *typeId) = 0; + + // Dynamic binding between modules + virtual int GetImportedFunctionCount() = 0; + virtual int GetImportedFunctionIndexByDecl(const char *decl) = 0; + virtual const char *GetImportedFunctionDeclaration(int importIndex) = 0; + virtual const char *GetImportedFunctionSourceModule(int importIndex) = 0; + virtual int BindImportedFunction(int importIndex, int funcId) = 0; + virtual int UnbindImportedFunction(int importIndex) = 0; + virtual int BindAllImportedFunctions() = 0; + virtual int UnbindAllImportedFunctions() = 0; + + // Bytecode saving and loading + virtual int SaveByteCode(asIBinaryStream *out) = 0; + virtual int LoadByteCode(asIBinaryStream *in) = 0; + +protected: + virtual ~asIScriptModule() {} +}; + +class asIScriptContext +{ +public: + // Memory management + virtual int AddRef() = 0; + virtual int Release() = 0; + + // Miscellaneous + virtual asIScriptEngine *GetEngine() = 0; + + // Execution + virtual int Prepare(int funcId) = 0; + virtual int Unprepare() = 0; + virtual int SetObject(void *obj) = 0; + virtual int Execute() = 0; + virtual int Abort() = 0; + virtual int Suspend() = 0; + virtual asEContextState GetState() = 0; + + // Arguments + virtual int SetArgByte(asUINT arg, asBYTE value) = 0; + virtual int SetArgWord(asUINT arg, asWORD value) = 0; + virtual int SetArgDWord(asUINT arg, asDWORD value) = 0; + virtual int SetArgQWord(asUINT arg, asQWORD value) = 0; + virtual int SetArgFloat(asUINT arg, float value) = 0; + virtual int SetArgDouble(asUINT arg, double value) = 0; + virtual int SetArgAddress(asUINT arg, void *addr) = 0; + virtual int SetArgObject(asUINT arg, void *obj) = 0; + virtual void *GetAddressOfArg(asUINT arg) = 0; + + // Return value + virtual asBYTE GetReturnByte() = 0; + virtual asWORD GetReturnWord() = 0; + virtual asDWORD GetReturnDWord() = 0; + virtual asQWORD GetReturnQWord() = 0; + virtual float GetReturnFloat() = 0; + virtual double GetReturnDouble() = 0; + virtual void *GetReturnAddress() = 0; + virtual void *GetReturnObject() = 0; + virtual void *GetAddressOfReturnValue() = 0; + + // Exception handling + virtual int SetException(const char *string) = 0; + virtual int GetExceptionLineNumber(int *column = 0) = 0; + virtual int GetExceptionFunction() = 0; + virtual const char *GetExceptionString() = 0; + virtual int SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv) = 0; + virtual void ClearExceptionCallback() = 0; + + // Debugging + virtual int SetLineCallback(asSFuncPtr callback, void *obj, int callConv) = 0; + virtual void ClearLineCallback() = 0; + virtual int GetCurrentLineNumber(int *column = 0) = 0; + virtual int GetCurrentFunction() = 0; + virtual int GetCallstackSize() = 0; + virtual int GetCallstackFunction(int index) = 0; + virtual int GetCallstackLineNumber(int index, int *column = 0) = 0; + virtual int GetVarCount(int stackLevel = -1) = 0; + virtual const char *GetVarName(int varIndex, int stackLevel = -1) = 0; + virtual const char *GetVarDeclaration(int varIndex, int stackLevel = -1) = 0; + virtual int GetVarTypeId(int varIndex, int stackLevel = -1) = 0; + virtual void *GetAddressOfVar(int varIndex, int stackLevel = -1) = 0; + virtual int GetThisTypeId(int stackLevel = -1) = 0; + virtual void *GetThisPointer(int stackLevel = -1) = 0; + + // User data + virtual void *SetUserData(void *data) = 0; + virtual void *GetUserData() = 0; + +protected: + virtual ~asIScriptContext() {} +}; + +class asIScriptGeneric +{ +public: + // Miscellaneous + virtual asIScriptEngine *GetEngine() = 0; + virtual int GetFunctionId() = 0; + + // Object + virtual void *GetObject() = 0; + virtual int GetObjectTypeId() = 0; + + // Arguments + virtual int GetArgCount() = 0; + virtual int GetArgTypeId(asUINT arg) = 0; + virtual asBYTE GetArgByte(asUINT arg) = 0; + virtual asWORD GetArgWord(asUINT arg) = 0; + virtual asDWORD GetArgDWord(asUINT arg) = 0; + virtual asQWORD GetArgQWord(asUINT arg) = 0; + virtual float GetArgFloat(asUINT arg) = 0; + virtual double GetArgDouble(asUINT arg) = 0; + virtual void *GetArgAddress(asUINT arg) = 0; + virtual void *GetArgObject(asUINT arg) = 0; + virtual void *GetAddressOfArg(asUINT arg) = 0; + + // Return value + virtual int GetReturnTypeId() = 0; + virtual int SetReturnByte(asBYTE val) = 0; + virtual int SetReturnWord(asWORD val) = 0; + virtual int SetReturnDWord(asDWORD val) = 0; + virtual int SetReturnQWord(asQWORD val) = 0; + virtual int SetReturnFloat(float val) = 0; + virtual int SetReturnDouble(double val) = 0; + virtual int SetReturnAddress(void *addr) = 0; + virtual int SetReturnObject(void *obj) = 0; + virtual void *GetAddressOfReturnLocation() = 0; + +protected: + virtual ~asIScriptGeneric() {} +}; + +class asIScriptObject +{ +public: + // Memory management + virtual int AddRef() = 0; + virtual int Release() = 0; + + // Type info + virtual int GetTypeId() const = 0; + virtual asIObjectType *GetObjectType() const = 0; + + // Class properties + virtual int GetPropertyCount() const = 0; + virtual int GetPropertyTypeId(asUINT prop) const = 0; + virtual const char *GetPropertyName(asUINT prop) const = 0; + virtual void *GetAddressOfProperty(asUINT prop) = 0; + + virtual asIScriptEngine *GetEngine() const = 0; + virtual int CopyFrom(asIScriptObject *other) = 0; + +protected: + virtual ~asIScriptObject() {} +}; + +class asIScriptArray +{ +public: + virtual asIScriptEngine *GetEngine() const = 0; + + // Memory management + virtual int AddRef() = 0; + virtual int Release() = 0; + + // Array type + virtual int GetArrayTypeId() = 0; + + // Elements + virtual int GetElementTypeId() = 0; + virtual asUINT GetElementCount() = 0; + virtual void * GetElementPointer(asUINT index) = 0; + virtual void Resize(asUINT size) = 0; + virtual int CopyFrom(asIScriptArray *other) = 0; + +protected: + virtual ~asIScriptArray() {} +}; + +class asIObjectType +{ +public: + virtual asIScriptEngine *GetEngine() const = 0; + virtual const char *GetConfigGroup() const = 0; + + // Memory management + virtual int AddRef() = 0; + virtual int Release() = 0; + + // Type info + virtual const char *GetName() const = 0; + virtual asIObjectType *GetBaseType() const = 0; + virtual asDWORD GetFlags() const = 0; + virtual asUINT GetSize() const = 0; + virtual int GetTypeId() const = 0; + virtual int GetSubTypeId() const = 0; + + // Interfaces + virtual int GetInterfaceCount() const = 0; + virtual asIObjectType *GetInterface(asUINT index) const = 0; + + // Factories + virtual int GetFactoryCount() const = 0; + virtual int GetFactoryIdByIndex(int index) const = 0; + virtual int GetFactoryIdByDecl(const char *decl) const = 0; + + // Methods + virtual int GetMethodCount() const = 0; + virtual int GetMethodIdByIndex(int index) const = 0; + virtual int GetMethodIdByName(const char *name) const = 0; + virtual int GetMethodIdByDecl(const char *decl) const = 0; + virtual asIScriptFunction *GetMethodDescriptorByIndex(int index) const = 0; + + // Properties + virtual int GetPropertyCount() const = 0; + virtual int GetPropertyTypeId(asUINT prop) const = 0; + virtual const char *GetPropertyName(asUINT prop) const = 0; + virtual int GetPropertyOffset(asUINT prop) const = 0; + + // Behaviours + virtual int GetBehaviourCount() const = 0; + virtual int GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const = 0; + +protected: + virtual ~asIObjectType() {} +}; + +class asIScriptFunction +{ +public: + virtual asIScriptEngine *GetEngine() const = 0; + + // Memory management + virtual int AddRef() = 0; + virtual int Release() = 0; + + virtual int GetId() const = 0; + virtual const char *GetModuleName() const = 0; + virtual const char *GetScriptSectionName() const = 0; + virtual const char *GetConfigGroup() const = 0; + virtual asIObjectType *GetObjectType() const = 0; + virtual const char *GetObjectName() const = 0; + virtual const char *GetName() const = 0; + virtual const char *GetDeclaration(bool includeObjectName = true) const = 0; + virtual bool IsClassMethod() const = 0; + virtual bool IsInterfaceMethod() const = 0; + virtual bool IsReadOnly() const = 0; + + virtual int GetParamCount() const = 0; + virtual int GetParamTypeId(int index, asDWORD *flags = 0) const = 0; + virtual int GetReturnTypeId() const = 0; + + // For JIT compilation + virtual asDWORD *GetByteCode(asUINT *length = 0) = 0; + +protected: + virtual ~asIScriptFunction() {}; +}; + +class asIBinaryStream +{ +public: + virtual void Read(void *ptr, asUINT size) = 0; + virtual void Write(const void *ptr, asUINT size) = 0; + +public: + virtual ~asIBinaryStream() {} +}; + +//----------------------------------------------------------------- +// Function pointers + +// Use our own memset() and memcpy() implementations for better portability +inline void asMemClear(void *_p, int size) +{ + char *p = (char *)_p; + const char *e = p + size; + for( ; p < e; p++ ) + *p = 0; +} + +inline void asMemCopy(void *_d, const void *_s, int size) +{ + char *d = (char *)_d; + const char *s = (const char *)_s; + const char *e = s + size; + for( ; s < e; d++, s++ ) + *d = *s; +} + +// Template function to capture all global functions, +// except the ones using the generic calling convention +template +inline asSFuncPtr asFunctionPtr(T func) +{ + asSFuncPtr p; + asMemClear(&p, sizeof(p)); + p.ptr.f.func = (asFUNCTION_t)(size_t)func; + + // Mark this as a global function + p.flag = 2; + + return p; +} + +// Specialization for functions using the generic calling convention +template<> +inline asSFuncPtr asFunctionPtr(asGENFUNC_t func) +{ + asSFuncPtr p; + asMemClear(&p, sizeof(p)); + p.ptr.f.func = (asFUNCTION_t)func; + + // Mark this as a generic function + p.flag = 1; + + return p; +} + +#ifndef AS_NO_CLASS_METHODS + +// Method pointers + +// Declare a dummy class so that we can determine the size of a simple method pointer +class asCSimpleDummy {}; +typedef void (asCSimpleDummy::*asSIMPLEMETHOD_t)(); +const int SINGLE_PTR_SIZE = sizeof(asSIMPLEMETHOD_t); + +// Define template +template +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // This version of the function should never be executed, nor compiled, + // as it would mean that the size of the method pointer cannot be determined. + + int ERROR_UnsupportedMethodPtr[N-100]; + return 0; + } +}; + +// Template specialization +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + asSFuncPtr p; + asMemClear(&p, sizeof(p)); + + asMemCopy(&p, &Mthd, SINGLE_PTR_SIZE); + + // Mark this as a class method + p.flag = 3; + + return p; + } +}; + +#if defined(_MSC_VER) && !defined(__MWERKS__) + +// MSVC and Intel uses different sizes for different class method pointers +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + asSFuncPtr p; + asMemClear(&p, sizeof(p)); + + asMemCopy(&p, &Mthd, SINGLE_PTR_SIZE+sizeof(int)); + + // Mark this as a class method + p.flag = 3; + + return p; + } +}; + +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // This is where a class with virtual inheritance falls, or + // on 64bit platforms with 8byte data alignments. + + // Method pointers for virtual inheritance is not supported, + // as it requires the location of the vbase table, which is + // only available to the C++ compiler, but not in the method + // pointer. + + // You can get around this by forward declaring the class and + // storing the sizeof its method pointer in a constant. Example: + + // class ClassWithVirtualInheritance; + // const int ClassWithVirtualInheritance_workaround = sizeof(void ClassWithVirtualInheritance::*()); + + // This will force the compiler to use the unknown type + // for the class, which falls under the next case + + // TODO: We need to try to identify if this is really a method pointer + // with virtual inheritance, or just a method pointer for multiple + // inheritance with pad bytes to produce a 16byte structure. + + asSFuncPtr p; + asMemClear(&p, sizeof(p)); + + asMemCopy(&p, &Mthd, SINGLE_PTR_SIZE+2*sizeof(int)); + + // Mark this as a class method + p.flag = 3; + + return p; + } +}; + +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + asSFuncPtr p; + asMemClear(&p, sizeof(p)); + + asMemCopy(&p, &Mthd, SINGLE_PTR_SIZE+3*sizeof(int)); + + // Mark this as a class method + p.flag = 3; + + return p; + } +}; + +template <> +struct asSMethodPtr +{ + template + static asSFuncPtr Convert(M Mthd) + { + // On 64bit platforms with 8byte data alignment + // the unknown class method pointers will come here. + + asSFuncPtr p; + asMemClear(&p, sizeof(p)); + + asMemCopy(&p, &Mthd, SINGLE_PTR_SIZE+4*sizeof(int)); + + // Mark this as a class method + p.flag = 3; + + return p; + } +}; + +#endif + +#endif // AS_NO_CLASS_METHODS + +//---------------------------------------------------------------- +// JIT compiler + +struct asSVMRegisters +{ + asDWORD *programPointer; // points to current bytecode instruction + asDWORD *stackFramePointer; // function stack frame + asDWORD *stackPointer; // top of stack (grows downward) + asQWORD valueRegister; // temp register for primitives + void *objectRegister; // temp register for objects and handles + asIObjectType *objectType; // type of object held in object register + bool doProcessSuspend; // whether or not the JIT should break out when it encounters a suspend instruction +}; + +typedef void (*asJITFunction)(asSVMRegisters *registers, asDWORD entryId); + +class asIJITCompiler +{ +public: + virtual int CompileFunction(asIScriptFunction *function, asJITFunction *output) = 0; + virtual void ReleaseJITFunction(asJITFunction func) = 0; +public: + virtual ~asIJITCompiler() {} +}; + +// Byte code instructions +enum asEBCInstr +{ + asBC_POP = 0, + asBC_PUSH = 1, + asBC_PshC4 = 2, + asBC_PshV4 = 3, + asBC_PSF = 4, + asBC_SWAP4 = 5, + asBC_NOT = 6, + asBC_PshG4 = 7, + asBC_LdGRdR4 = 8, + asBC_CALL = 9, + asBC_RET = 10, + asBC_JMP = 11, + asBC_JZ = 12, + asBC_JNZ = 13, + asBC_JS = 14, + asBC_JNS = 15, + asBC_JP = 16, + asBC_JNP = 17, + asBC_TZ = 18, + asBC_TNZ = 19, + asBC_TS = 20, + asBC_TNS = 21, + asBC_TP = 22, + asBC_TNP = 23, + asBC_NEGi = 24, + asBC_NEGf = 25, + asBC_NEGd = 26, + asBC_INCi16 = 27, + asBC_INCi8 = 28, + asBC_DECi16 = 29, + asBC_DECi8 = 30, + asBC_INCi = 31, + asBC_DECi = 32, + asBC_INCf = 33, + asBC_DECf = 34, + asBC_INCd = 35, + asBC_DECd = 36, + asBC_IncVi = 37, + asBC_DecVi = 38, + asBC_BNOT = 39, + asBC_BAND = 40, + asBC_BOR = 41, + asBC_BXOR = 42, + asBC_BSLL = 43, + asBC_BSRL = 44, + asBC_BSRA = 45, + asBC_COPY = 46, + asBC_PshC8 = 47, + asBC_RDS8 = 48, + asBC_SWAP8 = 49, + asBC_CMPd = 50, + asBC_CMPu = 51, + asBC_CMPf = 52, + asBC_CMPi = 53, + asBC_CMPIi = 54, + asBC_CMPIf = 55, + asBC_CMPIu = 56, + asBC_JMPP = 57, + asBC_PopRPtr = 58, + asBC_PshRPtr = 59, + asBC_STR = 60, + asBC_CALLSYS = 61, + asBC_CALLBND = 62, + asBC_SUSPEND = 63, + asBC_ALLOC = 64, + asBC_FREE = 65, + asBC_LOADOBJ = 66, + asBC_STOREOBJ = 67, + asBC_GETOBJ = 68, + asBC_REFCPY = 69, + asBC_CHKREF = 70, + asBC_GETOBJREF = 71, + asBC_GETREF = 72, + asBC_SWAP48 = 73, + asBC_SWAP84 = 74, + asBC_OBJTYPE = 75, + asBC_TYPEID = 76, + asBC_SetV4 = 77, + asBC_SetV8 = 78, + asBC_ADDSi = 79, + asBC_CpyVtoV4 = 80, + asBC_CpyVtoV8 = 81, + asBC_CpyVtoR4 = 82, + asBC_CpyVtoR8 = 83, + asBC_CpyVtoG4 = 84, + asBC_CpyRtoV4 = 85, + asBC_CpyRtoV8 = 86, + asBC_CpyGtoV4 = 87, + asBC_WRTV1 = 88, + asBC_WRTV2 = 89, + asBC_WRTV4 = 90, + asBC_WRTV8 = 91, + asBC_RDR1 = 92, + asBC_RDR2 = 93, + asBC_RDR4 = 94, + asBC_RDR8 = 95, + asBC_LDG = 96, + asBC_LDV = 97, + asBC_PGA = 98, + asBC_RDS4 = 99, + asBC_VAR = 100, + asBC_iTOf = 101, + asBC_fTOi = 102, + asBC_uTOf = 103, + asBC_fTOu = 104, + asBC_sbTOi = 105, + asBC_swTOi = 106, + asBC_ubTOi = 107, + asBC_uwTOi = 108, + asBC_dTOi = 109, + asBC_dTOu = 110, + asBC_dTOf = 111, + asBC_iTOd = 112, + asBC_uTOd = 113, + asBC_fTOd = 114, + asBC_ADDi = 115, + asBC_SUBi = 116, + asBC_MULi = 117, + asBC_DIVi = 118, + asBC_MODi = 119, + asBC_ADDf = 120, + asBC_SUBf = 121, + asBC_MULf = 122, + asBC_DIVf = 123, + asBC_MODf = 124, + asBC_ADDd = 125, + asBC_SUBd = 126, + asBC_MULd = 127, + asBC_DIVd = 128, + asBC_MODd = 129, + asBC_ADDIi = 130, + asBC_SUBIi = 131, + asBC_MULIi = 132, + asBC_ADDIf = 133, + asBC_SUBIf = 134, + asBC_MULIf = 135, + asBC_SetG4 = 136, + asBC_ChkRefS = 137, + asBC_ChkNullV = 138, + asBC_CALLINTF = 139, + asBC_iTOb = 140, + asBC_iTOw = 141, + asBC_SetV1 = 142, + asBC_SetV2 = 143, + asBC_Cast = 144, + asBC_i64TOi = 145, + asBC_uTOi64 = 146, + asBC_iTOi64 = 147, + asBC_fTOi64 = 148, + asBC_dTOi64 = 149, + asBC_fTOu64 = 150, + asBC_dTOu64 = 151, + asBC_i64TOf = 152, + asBC_u64TOf = 153, + asBC_i64TOd = 154, + asBC_u64TOd = 155, + asBC_NEGi64 = 156, + asBC_INCi64 = 157, + asBC_DECi64 = 158, + asBC_BNOT64 = 159, + asBC_ADDi64 = 160, + asBC_SUBi64 = 161, + asBC_MULi64 = 162, + asBC_DIVi64 = 163, + asBC_MODi64 = 164, + asBC_BAND64 = 165, + asBC_BOR64 = 166, + asBC_BXOR64 = 167, + asBC_BSLL64 = 168, + asBC_BSRL64 = 169, + asBC_BSRA64 = 170, + asBC_CMPi64 = 171, + asBC_CMPu64 = 172, + asBC_ChkNullS = 173, + asBC_ClrHi = 174, + asBC_JitEntry = 175, + + asBC_MAXBYTECODE = 176, + + // Temporary tokens. Can't be output to the final program + asBC_PSP = 253, + asBC_LINE = 254, + asBC_LABEL = 255 +}; + +// Instruction types +enum asEBCType +{ + asBCTYPE_INFO = 0, + asBCTYPE_NO_ARG = 1, + asBCTYPE_W_ARG = 2, + asBCTYPE_wW_ARG = 3, + asBCTYPE_DW_ARG = 4, + asBCTYPE_rW_DW_ARG = 5, + asBCTYPE_QW_ARG = 6, + asBCTYPE_DW_DW_ARG = 7, + asBCTYPE_wW_rW_rW_ARG = 8, + asBCTYPE_wW_QW_ARG = 9, + asBCTYPE_wW_rW_ARG = 10, + asBCTYPE_rW_ARG = 11, + asBCTYPE_wW_DW_ARG = 12, + asBCTYPE_wW_rW_DW_ARG = 13, + asBCTYPE_rW_rW_ARG = 14, + asBCTYPE_W_rW_ARG = 15, + asBCTYPE_wW_W_ARG = 16, + asBCTYPE_QW_DW_ARG = 17, + asBCTYPE_rW_QW_ARG = 18 +}; + +// Instruction type sizes +const int asBCTypeSize[19] = +{ + 0, // asBCTYPE_INFO + 1, // asBCTYPE_NO_ARG + 1, // asBCTYPE_W_ARG + 1, // asBCTYPE_wW_ARG + 2, // asBCTYPE_DW_ARG + 2, // asBCTYPE_rW_DW_ARG + 3, // asBCTYPE_QW_ARG + 3, // asBCTYPE_DW_DW_ARG + 2, // asBCTYPE_wW_rW_rW_ARG + 3, // asBCTYPE_wW_QW_ARG + 2, // asBCTYPE_wW_rW_ARG + 1, // asBCTYPE_rW_ARG + 2, // asBCTYPE_wW_DW_ARG + 3, // asBCTYPE_wW_rW_DW_ARG + 2, // asBCTYPE_rW_rW_ARG + 2, // asBCTYPE_W_rW_ARG + 2, // asBCTYPE_wW_W_ARG + 4, // asBCTYPE_QW_DW_ARG + 3 // asBCTYPE_rW_QW_ARG +}; + +// Instruction info +struct asSBCInfo +{ + asEBCInstr bc; + asEBCType type; + int stackInc; + const char *name; +}; + +#ifndef AS_64BIT_PTR + #define asBCTYPE_PTR_ARG asBCTYPE_DW_ARG + #define asBCTYPE_PTR_DW_ARG asBCTYPE_DW_DW_ARG + #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_DW_ARG + #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_DW_ARG + #ifndef AS_PTR_SIZE + #define AS_PTR_SIZE 1 + #endif +#else + #define asBCTYPE_PTR_ARG asBCTYPE_QW_ARG + #define asBCTYPE_PTR_DW_ARG asBCTYPE_QW_DW_ARG + #define asBCTYPE_wW_PTR_ARG asBCTYPE_wW_QW_ARG + #define asBCTYPE_rW_PTR_ARG asBCTYPE_rW_QW_ARG + #ifndef AS_PTR_SIZE + #define AS_PTR_SIZE 2 + #endif +#endif + +#define asBCINFO(b,t,s) {asBC_##b, asBCTYPE_##t, s, #b} +#define asBCINFO_DUMMY(b) {asEBCInstr(b), asBCTYPE_INFO, 0, "BC_" #b} + +const asSBCInfo asBCInfo[256] = +{ + asBCINFO(POP, W_ARG, 0xFFFF), + asBCINFO(PUSH, W_ARG, 0xFFFF), + asBCINFO(PshC4, DW_ARG, 1), + asBCINFO(PshV4, rW_ARG, 1), + asBCINFO(PSF, rW_ARG, AS_PTR_SIZE), + asBCINFO(SWAP4, NO_ARG, 0), + asBCINFO(NOT, rW_ARG, 0), + asBCINFO(PshG4, PTR_ARG, 1), + asBCINFO(LdGRdR4, wW_PTR_ARG, 0), + asBCINFO(CALL, DW_ARG, 0xFFFF), + asBCINFO(RET, W_ARG, 0xFFFF), + asBCINFO(JMP, DW_ARG, 0), + asBCINFO(JZ, DW_ARG, 0), + asBCINFO(JNZ, DW_ARG, 0), + asBCINFO(JS, DW_ARG, 0), + asBCINFO(JNS, DW_ARG, 0), + asBCINFO(JP, DW_ARG, 0), + asBCINFO(JNP, DW_ARG, 0), + asBCINFO(TZ, NO_ARG, 0), + asBCINFO(TNZ, NO_ARG, 0), + asBCINFO(TS, NO_ARG, 0), + asBCINFO(TNS, NO_ARG, 0), + asBCINFO(TP, NO_ARG, 0), + asBCINFO(TNP, NO_ARG, 0), + asBCINFO(NEGi, rW_ARG, 0), + asBCINFO(NEGf, rW_ARG, 0), + asBCINFO(NEGd, rW_ARG, 0), + asBCINFO(INCi16, NO_ARG, 0), + asBCINFO(INCi8, NO_ARG, 0), + asBCINFO(DECi16, NO_ARG, 0), + asBCINFO(DECi8, NO_ARG, 0), + asBCINFO(INCi, NO_ARG, 0), + asBCINFO(DECi, NO_ARG, 0), + asBCINFO(INCf, NO_ARG, 0), + asBCINFO(DECf, NO_ARG, 0), + asBCINFO(INCd, NO_ARG, 0), + asBCINFO(DECd, NO_ARG, 0), + asBCINFO(IncVi, rW_ARG, 0), + asBCINFO(DecVi, rW_ARG, 0), + asBCINFO(BNOT, rW_ARG, 0), + asBCINFO(BAND, wW_rW_rW_ARG, 0), + asBCINFO(BOR, wW_rW_rW_ARG, 0), + asBCINFO(BXOR, wW_rW_rW_ARG, 0), + asBCINFO(BSLL, wW_rW_rW_ARG, 0), + asBCINFO(BSRL, wW_rW_rW_ARG, 0), + asBCINFO(BSRA, wW_rW_rW_ARG, 0), + asBCINFO(COPY, W_ARG, -AS_PTR_SIZE), + asBCINFO(PshC8, QW_ARG, 2), + asBCINFO(RDS8, NO_ARG, 2-AS_PTR_SIZE), + asBCINFO(SWAP8, NO_ARG, 0), + asBCINFO(CMPd, rW_rW_ARG, 0), + asBCINFO(CMPu, rW_rW_ARG, 0), + asBCINFO(CMPf, rW_rW_ARG, 0), + asBCINFO(CMPi, rW_rW_ARG, 0), + asBCINFO(CMPIi, rW_DW_ARG, 0), + asBCINFO(CMPIf, rW_DW_ARG, 0), + asBCINFO(CMPIu, rW_DW_ARG, 0), + asBCINFO(JMPP, rW_ARG, 0), + asBCINFO(PopRPtr, NO_ARG, -AS_PTR_SIZE), + asBCINFO(PshRPtr, NO_ARG, AS_PTR_SIZE), + asBCINFO(STR, W_ARG, 1+AS_PTR_SIZE), + asBCINFO(CALLSYS, DW_ARG, 0xFFFF), + asBCINFO(CALLBND, DW_ARG, 0xFFFF), + asBCINFO(SUSPEND, NO_ARG, 0), + asBCINFO(ALLOC, PTR_DW_ARG, 0xFFFF), + asBCINFO(FREE, PTR_ARG, -AS_PTR_SIZE), + asBCINFO(LOADOBJ, rW_ARG, 0), + asBCINFO(STOREOBJ, wW_ARG, 0), + asBCINFO(GETOBJ, W_ARG, 0), + asBCINFO(REFCPY, PTR_ARG, -AS_PTR_SIZE), + asBCINFO(CHKREF, NO_ARG, 0), + asBCINFO(GETOBJREF, W_ARG, 0), + asBCINFO(GETREF, W_ARG, 0), + asBCINFO(SWAP48, NO_ARG, 0), + asBCINFO(SWAP84, NO_ARG, 0), + asBCINFO(OBJTYPE, PTR_ARG, AS_PTR_SIZE), + asBCINFO(TYPEID, DW_ARG, 1), + asBCINFO(SetV4, wW_DW_ARG, 0), + asBCINFO(SetV8, wW_QW_ARG, 0), + asBCINFO(ADDSi, DW_ARG, 0), + asBCINFO(CpyVtoV4, wW_rW_ARG, 0), + asBCINFO(CpyVtoV8, wW_rW_ARG, 0), + asBCINFO(CpyVtoR4, rW_ARG, 0), + asBCINFO(CpyVtoR8, rW_ARG, 0), + asBCINFO(CpyVtoG4, rW_PTR_ARG, 0), + asBCINFO(CpyRtoV4, wW_ARG, 0), + asBCINFO(CpyRtoV8, wW_ARG, 0), + asBCINFO(CpyGtoV4, wW_PTR_ARG, 0), + asBCINFO(WRTV1, rW_ARG, 0), + asBCINFO(WRTV2, rW_ARG, 0), + asBCINFO(WRTV4, rW_ARG, 0), + asBCINFO(WRTV8, rW_ARG, 0), + asBCINFO(RDR1, wW_ARG, 0), + asBCINFO(RDR2, wW_ARG, 0), + asBCINFO(RDR4, wW_ARG, 0), + asBCINFO(RDR8, wW_ARG, 0), + asBCINFO(LDG, PTR_ARG, 0), + asBCINFO(LDV, rW_ARG, 0), + asBCINFO(PGA, PTR_ARG, AS_PTR_SIZE), + asBCINFO(RDS4, NO_ARG, 1-AS_PTR_SIZE), + asBCINFO(VAR, rW_ARG, AS_PTR_SIZE), + asBCINFO(iTOf, rW_ARG, 0), + asBCINFO(fTOi, rW_ARG, 0), + asBCINFO(uTOf, rW_ARG, 0), + asBCINFO(fTOu, rW_ARG, 0), + asBCINFO(sbTOi, rW_ARG, 0), + asBCINFO(swTOi, rW_ARG, 0), + asBCINFO(ubTOi, rW_ARG, 0), + asBCINFO(uwTOi, rW_ARG, 0), + asBCINFO(dTOi, wW_rW_ARG, 0), + asBCINFO(dTOu, wW_rW_ARG, 0), + asBCINFO(dTOf, wW_rW_ARG, 0), + asBCINFO(iTOd, wW_rW_ARG, 0), + asBCINFO(uTOd, wW_rW_ARG, 0), + asBCINFO(fTOd, wW_rW_ARG, 0), + asBCINFO(ADDi, wW_rW_rW_ARG, 0), + asBCINFO(SUBi, wW_rW_rW_ARG, 0), + asBCINFO(MULi, wW_rW_rW_ARG, 0), + asBCINFO(DIVi, wW_rW_rW_ARG, 0), + asBCINFO(MODi, wW_rW_rW_ARG, 0), + asBCINFO(ADDf, wW_rW_rW_ARG, 0), + asBCINFO(SUBf, wW_rW_rW_ARG, 0), + asBCINFO(MULf, wW_rW_rW_ARG, 0), + asBCINFO(DIVf, wW_rW_rW_ARG, 0), + asBCINFO(MODf, wW_rW_rW_ARG, 0), + asBCINFO(ADDd, wW_rW_rW_ARG, 0), + asBCINFO(SUBd, wW_rW_rW_ARG, 0), + asBCINFO(MULd, wW_rW_rW_ARG, 0), + asBCINFO(DIVd, wW_rW_rW_ARG, 0), + asBCINFO(MODd, wW_rW_rW_ARG, 0), + asBCINFO(ADDIi, wW_rW_DW_ARG, 0), + asBCINFO(SUBIi, wW_rW_DW_ARG, 0), + asBCINFO(MULIi, wW_rW_DW_ARG, 0), + asBCINFO(ADDIf, wW_rW_DW_ARG, 0), + asBCINFO(SUBIf, wW_rW_DW_ARG, 0), + asBCINFO(MULIf, wW_rW_DW_ARG, 0), + asBCINFO(SetG4, PTR_DW_ARG, 0), + asBCINFO(ChkRefS, NO_ARG, 0), + asBCINFO(ChkNullV, rW_ARG, 0), + asBCINFO(CALLINTF, DW_ARG, 0xFFFF), + asBCINFO(iTOb, rW_ARG, 0), + asBCINFO(iTOw, rW_ARG, 0), + asBCINFO(SetV1, wW_DW_ARG, 0), + asBCINFO(SetV2, wW_DW_ARG, 0), + asBCINFO(Cast, DW_ARG, -AS_PTR_SIZE), + asBCINFO(i64TOi, wW_rW_ARG, 0), + asBCINFO(uTOi64, wW_rW_ARG, 0), + asBCINFO(iTOi64, wW_rW_ARG, 0), + asBCINFO(fTOi64, wW_rW_ARG, 0), + asBCINFO(dTOi64, rW_ARG, 0), + asBCINFO(fTOu64, wW_rW_ARG, 0), + asBCINFO(dTOu64, rW_ARG, 0), + asBCINFO(i64TOf, wW_rW_ARG, 0), + asBCINFO(u64TOf, wW_rW_ARG, 0), + asBCINFO(i64TOd, rW_ARG, 0), + asBCINFO(u64TOd, rW_ARG, 0), + asBCINFO(NEGi64, rW_ARG, 0), + asBCINFO(INCi64, NO_ARG, 0), + asBCINFO(DECi64, NO_ARG, 0), + asBCINFO(BNOT64, rW_ARG, 0), + asBCINFO(ADDi64, wW_rW_rW_ARG, 0), + asBCINFO(SUBi64, wW_rW_rW_ARG, 0), + asBCINFO(MULi64, wW_rW_rW_ARG, 0), + asBCINFO(DIVi64, wW_rW_rW_ARG, 0), + asBCINFO(MODi64, wW_rW_rW_ARG, 0), + asBCINFO(BAND64, wW_rW_rW_ARG, 0), + asBCINFO(BOR64, wW_rW_rW_ARG, 0), + asBCINFO(BXOR64, wW_rW_rW_ARG, 0), + asBCINFO(BSLL64, wW_rW_rW_ARG, 0), + asBCINFO(BSRL64, wW_rW_rW_ARG, 0), + asBCINFO(BSRA64, wW_rW_rW_ARG, 0), + asBCINFO(CMPi64, rW_rW_ARG, 0), + asBCINFO(CMPu64, rW_rW_ARG, 0), + asBCINFO(ChkNullS, W_ARG, 0), + asBCINFO(ClrHi, NO_ARG, 0), + asBCINFO(JitEntry, W_ARG, 0), + + asBCINFO_DUMMY(176), + asBCINFO_DUMMY(177), + asBCINFO_DUMMY(178), + asBCINFO_DUMMY(179), + asBCINFO_DUMMY(180), + asBCINFO_DUMMY(181), + asBCINFO_DUMMY(182), + asBCINFO_DUMMY(183), + asBCINFO_DUMMY(184), + asBCINFO_DUMMY(185), + asBCINFO_DUMMY(186), + asBCINFO_DUMMY(187), + asBCINFO_DUMMY(188), + asBCINFO_DUMMY(189), + asBCINFO_DUMMY(190), + asBCINFO_DUMMY(191), + asBCINFO_DUMMY(192), + asBCINFO_DUMMY(193), + asBCINFO_DUMMY(194), + asBCINFO_DUMMY(195), + asBCINFO_DUMMY(196), + asBCINFO_DUMMY(197), + asBCINFO_DUMMY(198), + asBCINFO_DUMMY(199), + asBCINFO_DUMMY(200), + asBCINFO_DUMMY(201), + asBCINFO_DUMMY(202), + asBCINFO_DUMMY(203), + asBCINFO_DUMMY(204), + asBCINFO_DUMMY(205), + asBCINFO_DUMMY(206), + asBCINFO_DUMMY(207), + asBCINFO_DUMMY(208), + asBCINFO_DUMMY(209), + asBCINFO_DUMMY(210), + asBCINFO_DUMMY(211), + asBCINFO_DUMMY(212), + asBCINFO_DUMMY(213), + asBCINFO_DUMMY(214), + asBCINFO_DUMMY(215), + asBCINFO_DUMMY(216), + asBCINFO_DUMMY(217), + asBCINFO_DUMMY(218), + asBCINFO_DUMMY(219), + asBCINFO_DUMMY(220), + asBCINFO_DUMMY(221), + asBCINFO_DUMMY(222), + asBCINFO_DUMMY(223), + asBCINFO_DUMMY(224), + asBCINFO_DUMMY(225), + asBCINFO_DUMMY(226), + asBCINFO_DUMMY(227), + asBCINFO_DUMMY(228), + asBCINFO_DUMMY(229), + asBCINFO_DUMMY(230), + asBCINFO_DUMMY(231), + asBCINFO_DUMMY(232), + asBCINFO_DUMMY(233), + asBCINFO_DUMMY(234), + asBCINFO_DUMMY(235), + asBCINFO_DUMMY(236), + asBCINFO_DUMMY(237), + asBCINFO_DUMMY(238), + asBCINFO_DUMMY(239), + asBCINFO_DUMMY(240), + asBCINFO_DUMMY(241), + asBCINFO_DUMMY(242), + asBCINFO_DUMMY(243), + asBCINFO_DUMMY(244), + asBCINFO_DUMMY(245), + asBCINFO_DUMMY(246), + asBCINFO_DUMMY(247), + asBCINFO_DUMMY(248), + asBCINFO_DUMMY(249), + asBCINFO_DUMMY(250), + asBCINFO_DUMMY(251), + asBCINFO_DUMMY(252), + + asBCINFO(PSP, W_ARG, AS_PTR_SIZE), + asBCINFO(LINE, INFO, 0xFFFF), + asBCINFO(LABEL, INFO, 0xFFFF) +}; + +// Macros to access bytecode instruction arguments +#define asBC_DWORDARG(x) (asDWORD(*(x+1))) +#define asBC_INTARG(x) (int(*(x+1))) +#define asBC_QWORDARG(x) (*(asQWORD*)(x+1)) +#define asBC_FLOATARG(x) (*(float*)(x+1)) +#define asBC_PTRARG(x) (asPTRWORD(*(x+1))) +#define asBC_WORDARG0(x) (*(((asWORD*)x)+1)) +#define asBC_WORDARG1(x) (*(((asWORD*)x)+2)) +#define asBC_SWORDARG0(x) (*(((short*)x)+1)) +#define asBC_SWORDARG1(x) (*(((short*)x)+2)) +#define asBC_SWORDARG2(x) (*(((short*)x)+3)) + + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/scriptany/scriptany.cpp b/AngelScript/scriptany/scriptany.cpp new file mode 100644 index 000000000..0046c758b --- /dev/null +++ b/AngelScript/scriptany/scriptany.cpp @@ -0,0 +1,464 @@ +#include "scriptany.h" +#include +#include +#include + +BEGIN_AS_NAMESPACE + +// We'll use the generic interface for the factories as we need the engine pointer +static void ScriptAnyFactory_Generic(asIScriptGeneric *gen) +{ + asIScriptEngine *engine = gen->GetEngine(); + + *(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(engine); +} + +static void ScriptAnyFactory2_Generic(asIScriptGeneric *gen) +{ + asIScriptEngine *engine = gen->GetEngine(); + void *ref = (void*)gen->GetArgAddress(0); + int refType = gen->GetArgTypeId(0); + + *(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(ref,refType,engine); +} + +static CScriptAny &ScriptAnyAssignment(CScriptAny *other, CScriptAny *self) +{ + return *self = *other; +} + +static void ScriptAnyAssignment_Generic(asIScriptGeneric *gen) +{ + CScriptAny *other = (CScriptAny*)gen->GetArgObject(0); + CScriptAny *self = (CScriptAny*)gen->GetObject(); + + *self = *other; + + gen->SetReturnObject(self); +} + +static void ScriptAny_Store_Generic(asIScriptGeneric *gen) +{ + void *ref = (void*)gen->GetArgAddress(0); + int refTypeId = gen->GetArgTypeId(0); + CScriptAny *self = (CScriptAny*)gen->GetObject(); + + self->Store(ref, refTypeId); +} + +static void ScriptAny_StoreInt_Generic(asIScriptGeneric *gen) +{ + asINT64 *ref = (asINT64*)gen->GetArgAddress(0); + CScriptAny *self = (CScriptAny*)gen->GetObject(); + + self->Store(*ref); +} + +static void ScriptAny_StoreFlt_Generic(asIScriptGeneric *gen) +{ + double *ref = (double*)gen->GetArgAddress(0); + CScriptAny *self = (CScriptAny*)gen->GetObject(); + + self->Store(*ref); +} + +static void ScriptAny_Retrieve_Generic(asIScriptGeneric *gen) +{ + void *ref = (void*)gen->GetArgAddress(0); + int refTypeId = gen->GetArgTypeId(0); + CScriptAny *self = (CScriptAny*)gen->GetObject(); + + *(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(ref, refTypeId); +} + +static void ScriptAny_RetrieveInt_Generic(asIScriptGeneric *gen) +{ + asINT64 *ref = (asINT64*)gen->GetArgAddress(0); + CScriptAny *self = (CScriptAny*)gen->GetObject(); + + *(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref); +} + +static void ScriptAny_RetrieveFlt_Generic(asIScriptGeneric *gen) +{ + double *ref = (double*)gen->GetArgAddress(0); + CScriptAny *self = (CScriptAny*)gen->GetObject(); + + *(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref); +} + +static void ScriptAny_AddRef_Generic(asIScriptGeneric *gen) +{ + CScriptAny *self = (CScriptAny*)gen->GetObject(); + self->AddRef(); +} + +static void ScriptAny_Release_Generic(asIScriptGeneric *gen) +{ + CScriptAny *self = (CScriptAny*)gen->GetObject(); + self->Release(); +} + +static void ScriptAny_GetRefCount_Generic(asIScriptGeneric *gen) +{ + CScriptAny *self = (CScriptAny*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ScriptAny_SetFlag_Generic(asIScriptGeneric *gen) +{ + CScriptAny *self = (CScriptAny*)gen->GetObject(); + self->SetFlag(); +} + +static void ScriptAny_GetFlag_Generic(asIScriptGeneric *gen) +{ + CScriptAny *self = (CScriptAny*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); +} + +static void ScriptAny_EnumReferences_Generic(asIScriptGeneric *gen) +{ + CScriptAny *self = (CScriptAny*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptAny_ReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + CScriptAny *self = (CScriptAny*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +void RegisterScriptAny(asIScriptEngine *engine) +{ + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) + RegisterScriptAny_Generic(engine); + else + RegisterScriptAny_Native(engine); +} + +void RegisterScriptAny_Native(asIScriptEngine *engine) +{ + int r; + r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 ); + + // We'll use the generic interface for the constructor as we need the engine pointer + r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptAny,AddRef), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptAny,Release), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "void store(?&in)", asMETHODPR(CScriptAny,Store,(void*,int),void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "void store(int64&in)", asMETHODPR(CScriptAny,Store,(asINT64&),void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "void store(double&in)", asMETHODPR(CScriptAny,Store,(double&),void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "bool retrieve(?&out)", asMETHODPR(CScriptAny,Retrieve,(void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out)", asMETHODPR(CScriptAny,Retrieve,(asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "bool retrieve(double&out)", asMETHODPR(CScriptAny,Retrieve,(double&) const,bool), asCALL_THISCALL); assert( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptAny,GetRefCount), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptAny,SetFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptAny,GetFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptAny,EnumReferences), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptAny,ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 ); +} + +void RegisterScriptAny_Generic(asIScriptEngine *engine) +{ + int r; + r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 ); + + // We'll use the generic interface for the constructor as we need the engine pointer + r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in)", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptAny_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptAny_Release_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "void store(?&in)", asFUNCTION(ScriptAny_Store_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "void store(int64&in)", asFUNCTION(ScriptAny_StoreInt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "void store(double&in)", asFUNCTION(ScriptAny_StoreFlt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "bool retrieve(?&out) const", asFUNCTION(ScriptAny_Retrieve_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out) const", asFUNCTION(ScriptAny_RetrieveInt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("any", "bool retrieve(double&out) const", asFUNCTION(ScriptAny_RetrieveFlt_Generic), asCALL_GENERIC); assert( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptAny_GetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptAny_SetFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptAny_GetFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptAny_EnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptAny_ReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 ); +} + + +CScriptAny &CScriptAny::operator=(const CScriptAny &other) +{ + // Hold on to the object type reference so it isn't destroyed too early + if( other.value.valueObj && (other.value.typeId & asTYPEID_MASK_OBJECT) ) + { + asIObjectType *ot = engine->GetObjectTypeById(other.value.typeId); + if( ot ) + ot->AddRef(); + } + + FreeObject(); + + value.typeId = other.value.typeId; + if( value.typeId & asTYPEID_OBJHANDLE ) + { + // For handles, copy the pointer and increment the reference count + value.valueObj = other.value.valueObj; + engine->AddRefScriptObject(value.valueObj, value.typeId); + } + else if( value.typeId & asTYPEID_MASK_OBJECT ) + { + // Create a copy of the object + value.valueObj = engine->CreateScriptObjectCopy(other.value.valueObj, value.typeId); + } + else + { + // Primitives can be copied directly + value.valueInt = other.value.valueInt; + } + + return *this; +} + +int CScriptAny::CopyFrom(const CScriptAny *other) +{ + if( other == 0 ) return asINVALID_ARG; + + *this = *(CScriptAny*)other; + + return 0; +} + +CScriptAny::CScriptAny(asIScriptEngine *engine) +{ + this->engine = engine; + refCount = 1; + + value.typeId = 0; + value.valueInt = 0; + + // Notify the garbage collector of this object + engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeIdByDecl("any")); +} + +CScriptAny::CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine) +{ + this->engine = engine; + refCount = 1; + + value.typeId = 0; + value.valueInt = 0; + + // Notify the garbage collector of this object + engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeIdByDecl("any")); + + Store(ref, refTypeId); +} + +CScriptAny::~CScriptAny() +{ + FreeObject(); +} + +void CScriptAny::Store(void *ref, int refTypeId) +{ + // Hold on to the object type reference so it isn't destroyed too early + if( *(void**)ref && (refTypeId & asTYPEID_MASK_OBJECT) ) + { + asIObjectType *ot = engine->GetObjectTypeById(refTypeId); + if( ot ) + ot->AddRef(); + } + + FreeObject(); + + value.typeId = refTypeId; + if( value.typeId & asTYPEID_OBJHANDLE ) + { + // We're receiving a reference to the handle, so we need to dereference it + value.valueObj = *(void**)ref; + engine->AddRefScriptObject(value.valueObj, value.typeId); + } + else if( value.typeId & asTYPEID_MASK_OBJECT ) + { + // Create a copy of the object + value.valueObj = engine->CreateScriptObjectCopy(ref, value.typeId); + } + else + { + // Primitives can be copied directly + value.valueInt = 0; + + // Copy the primitive value + // We receive a pointer to the value. + int size = engine->GetSizeOfPrimitiveType(value.typeId); + memcpy(&value.valueInt, ref, size); + } +} + +void CScriptAny::Store(double &ref) +{ + Store(&ref, asTYPEID_DOUBLE); +} + +void CScriptAny::Store(asINT64 &ref) +{ + Store(&ref, asTYPEID_INT64); +} + + +bool CScriptAny::Retrieve(void *ref, int refTypeId) const +{ + if( refTypeId & asTYPEID_OBJHANDLE ) + { + // Is the handle type compatible with the stored value? + + // A handle can be retrieved if the stored type is a handle of same or compatible type + // or if the stored type is an object that implements the interface that the handle refer to. + if( (value.typeId & asTYPEID_MASK_OBJECT) && + engine->IsHandleCompatibleWithObject(value.valueObj, value.typeId, refTypeId) ) + { + engine->AddRefScriptObject(value.valueObj, value.typeId); + *(void**)ref = value.valueObj; + + return true; + } + } + else if( refTypeId & asTYPEID_MASK_OBJECT ) + { + // Is the object type compatible with the stored value? + + // Copy the object into the given reference + if( value.typeId == refTypeId ) + { + engine->CopyScriptObject(ref, value.valueObj, value.typeId); + + return true; + } + } + else + { + // Is the primitive type compatible with the stored value? + + if( value.typeId == refTypeId ) + { + int size = engine->GetSizeOfPrimitiveType(refTypeId); + memcpy(ref, &value.valueInt, size); + return true; + } + + // We know all numbers are stored as either int64 or double, since we register overloaded functions for those + if( value.typeId == asTYPEID_INT64 && refTypeId == asTYPEID_DOUBLE ) + { + *(double*)ref = double(value.valueInt); + return true; + } + else if( value.typeId == asTYPEID_DOUBLE && refTypeId == asTYPEID_INT64 ) + { + *(asINT64*)ref = asINT64(value.valueFlt); + return true; + } + } + + return false; +} + +bool CScriptAny::Retrieve(asINT64 &value) const +{ + return Retrieve(&value, asTYPEID_INT64); +} + +bool CScriptAny::Retrieve(double &value) const +{ + return Retrieve(&value, asTYPEID_DOUBLE); +} + +int CScriptAny::GetTypeId() const +{ + return value.typeId; +} + +void CScriptAny::FreeObject() +{ + // If it is a handle or a ref counted object, call release + if( value.typeId & asTYPEID_MASK_OBJECT ) + { + // Let the engine release the object + engine->ReleaseScriptObject(value.valueObj, value.typeId); + + // Release the object type info + asIObjectType *ot = engine->GetObjectTypeById(value.typeId); + if( ot ) + ot->Release(); + + value.valueObj = 0; + value.typeId = 0; + } + + // For primitives, there's nothing to do +} + + +void CScriptAny::EnumReferences(asIScriptEngine *engine) +{ + // If we're holding a reference, we'll notify the garbage collector of it + if( value.valueObj && (value.typeId & asTYPEID_MASK_OBJECT) ) + { + engine->GCEnumCallback(value.valueObj); + + // The object type itself is also garbage collected + asIObjectType *ot = engine->GetObjectTypeById(value.typeId); + if( ot ) + engine->GCEnumCallback(ot); + } +} + +void CScriptAny::ReleaseAllHandles(asIScriptEngine * /*engine*/) +{ + FreeObject(); +} + +int CScriptAny::AddRef() +{ + // Increase counter and clear flag set by GC + refCount = (refCount & 0x7FFFFFFF) + 1; + return refCount; +} + +int CScriptAny::Release() +{ + // Now do the actual releasing (clearing the flag set by GC) + refCount = (refCount & 0x7FFFFFFF) - 1; + if( refCount == 0 ) + { + delete this; + return 0; + } + + return refCount; +} + +int CScriptAny::GetRefCount() +{ + return refCount & 0x7FFFFFFF; +} + +void CScriptAny::SetFlag() +{ + refCount |= 0x80000000; +} + +bool CScriptAny::GetFlag() +{ + return (refCount & 0x80000000) ? true : false; +} + + +END_AS_NAMESPACE diff --git a/AngelScript/scriptany/scriptany.h b/AngelScript/scriptany/scriptany.h new file mode 100644 index 000000000..807efc986 --- /dev/null +++ b/AngelScript/scriptany/scriptany.h @@ -0,0 +1,71 @@ +#ifndef SCRIPTANY_H +#define SCRIPTANY_H + +#include + +BEGIN_AS_NAMESPACE + +class CScriptAny +{ +public: + // Constructors + CScriptAny(asIScriptEngine *engine); + CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine); + + // Memory management + int AddRef(); + int Release(); + + // Copy the stored value from another any object + CScriptAny &operator=(const CScriptAny&); + int CopyFrom(const CScriptAny *other); + + // Store the value, either as variable type, integer number, or real number + void Store(void *ref, int refTypeId); + void Store(asINT64 &value); + void Store(double &value); + + // Retrieve the stored value, either as variable type, integer number, or real number + bool Retrieve(void *ref, int refTypeId) const; + bool Retrieve(asINT64 &value) const; + bool Retrieve(double &value) const; + + // Get the type id of the stored value + int GetTypeId() const; + + // GC methods + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + +protected: + virtual ~CScriptAny(); + void FreeObject(); + + int refCount; + asIScriptEngine *engine; + + // The structure for holding the values + struct valueStruct + { + union + { + asINT64 valueInt; + double valueFlt; + void *valueObj; + }; + int typeId; + }; + + valueStruct value; +}; + +void RegisterScriptAny(asIScriptEngine *engine); +void RegisterScriptAny_Native(asIScriptEngine *engine); +void RegisterScriptAny_Generic(asIScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/scriptarray/scriptarray.cpp b/AngelScript/scriptarray/scriptarray.cpp new file mode 100644 index 000000000..31cd52f86 --- /dev/null +++ b/AngelScript/scriptarray/scriptarray.cpp @@ -0,0 +1,642 @@ +#include +#include +#include +#include + +#include "scriptarray.h" + +BEGIN_AS_NAMESPACE + +struct SArrayBuffer +{ + asDWORD numElements; + asBYTE data[1]; +}; + +static CScriptArray* ScriptArrayFactory2(asIObjectType *ot, asUINT length) +{ + CScriptArray *a = new CScriptArray(length, ot); + + // It's possible the constructor raised a script exception, in which case we + // need to free the memory and return null instead, else we get a memory leak. + asIScriptContext *ctx = asGetActiveContext(); + if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION ) + { + delete a; + return 0; + } + + return a; +} + +static CScriptArray* ScriptArrayFactory(asIObjectType *ot) +{ + return ScriptArrayFactory2(ot, 0); +} + +// This optional callback is called when the template type is first used by the compiler. +// It allows the application to validate if the template can be instanciated for the requested +// subtype at compile time, instead of at runtime. +static bool ScriptArrayTemplateCallback(asIObjectType *ot) +{ + // Make sure the subtype can be instanciated with a default factory/constructor, + // otherwise we won't be able to instanciate the elements + int typeId = ot->GetSubTypeId(); + if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) ) + { + asIObjectType *subtype = ot->GetEngine()->GetObjectTypeById(typeId); + asDWORD flags = subtype->GetFlags(); + if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) ) + { + // Verify that there is a default constructor + for( int n = 0; n < subtype->GetBehaviourCount(); n++ ) + { + asEBehaviours beh; + int funcId = subtype->GetBehaviourByIndex(n, &beh); + if( beh != asBEHAVE_CONSTRUCT ) continue; + + asIScriptFunction *func = ot->GetEngine()->GetFunctionDescriptorById(funcId); + if( func->GetParamCount() == 0 ) + { + // Found the default constructor + return true; + } + } + + // There is no default constructor + return false; + } + else if( (flags & asOBJ_REF) ) + { + // Verify that there is a default factory + for( int n = 0; n < subtype->GetFactoryCount(); n++ ) + { + int funcId = subtype->GetFactoryIdByIndex(n); + asIScriptFunction *func = ot->GetEngine()->GetFunctionDescriptorById(funcId); + if( func->GetParamCount() == 0 ) + { + // Found the default factory + return true; + } + } + + // No default factory + return false; + } + } + + // The type is ok + return true; +} + +// Registers the template array type +void RegisterScriptArray(asIScriptEngine *engine) +{ + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") == 0 ) + RegisterScriptArray_Native(engine); + else + RegisterScriptArray_Generic(engine); +} + +void RegisterScriptArray_Native(asIScriptEngine *engine) +{ + int r; + + // Register the array type as a template + r = engine->RegisterObjectType("array", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 ); + + // Register a callback for validating the subtype before it is used + r = engine->RegisterObjectBehaviour("array", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in)", asFUNCTION(ScriptArrayTemplateCallback), asCALL_CDECL); assert( r >= 0 ); + + // Templates receive the object type as the first parameter. To the script writer this is hidden + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in)", asFUNCTIONPR(ScriptArrayFactory, (asIObjectType*), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint)", asFUNCTIONPR(ScriptArrayFactory2, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL); assert( r >= 0 ); + + // The memory management methods + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptArray,AddRef), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptArray,Release), asCALL_THISCALL); assert( r >= 0 ); + + // The index operator returns the template subtype + r = engine->RegisterObjectBehaviour("array", asBEHAVE_INDEX, "T &f(uint)", asMETHOD(CScriptArray, At), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_INDEX, "const T &f(uint) const", asMETHOD(CScriptArray, At), asCALL_THISCALL); assert( r >= 0 ); + + // The assignment operator + r = engine->RegisterObjectMethod("array", "array &opAssign(const array&in)", asMETHOD(CScriptArray, operator=), asCALL_THISCALL); assert( r >= 0 ); + + // Other methods + r = engine->RegisterObjectMethod("array", "uint length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void resize(uint)", asMETHOD(CScriptArray, Resize), asCALL_THISCALL); assert( r >= 0 ); + + // Register GC behaviours in case the array needs to be garbage collected + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptArray, GetRefCount), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptArray, SetFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptArray, GetFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptArray, EnumReferences), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptArray, ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 ); +} + +CScriptArray &CScriptArray::operator=(const CScriptArray &other) +{ + // Only perform the copy if the array types are the same + if( &other != this && + other.GetArrayObjectType() == GetArrayObjectType() ) + { + if( buffer ) + { + DeleteBuffer(buffer); + buffer = 0; + } + + // Copy all elements from the other array + CreateBuffer(&buffer, other.buffer->numElements); + CopyBuffer(buffer, other.buffer); + } + + return *this; +} + +CScriptArray::CScriptArray(asUINT length, asIObjectType *ot) +{ + refCount = 1; + gcFlag = false; + objType = ot; + objType->AddRef(); + buffer = 0; + + // Determine element size + // TODO: Should probably store the template sub type id as well + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + elementSize = sizeof(asPWORD); + } + else + { + elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(typeId); + } + + isArrayOfHandles = typeId & asTYPEID_OBJHANDLE ? true : false; + + // Make sure the array size isn't too large for us to handle + if( !CheckMaxSize(length) ) + { + // Don't continue with the initialization + return; + } + + CreateBuffer(&buffer, length); + + // Notify the GC of the successful creation + if( objType->GetFlags() & asOBJ_GC ) + objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType->GetTypeId()); +} + +CScriptArray::~CScriptArray() +{ + if( buffer ) + { + DeleteBuffer(buffer); + buffer = 0; + } + if( objType ) objType->Release(); +} + +asUINT CScriptArray::GetSize() +{ + return buffer->numElements; +} + +void CScriptArray::Resize(asUINT numElements) +{ + // Don't do anything if the size is already correct + if( numElements == buffer->numElements ) + return; + + // Make sure the array size isn't too large for us to handle + if( !CheckMaxSize(numElements) ) + { + // Don't resize the array + return; + } + + SArrayBuffer *newBuffer; + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + // Allocate memory for the buffer + newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements]; + newBuffer->numElements = numElements; + + // Copy the elements from the old buffer + int c = numElements > buffer->numElements ? buffer->numElements : numElements; + asDWORD **d = (asDWORD**)newBuffer->data; + asDWORD **s = (asDWORD**)buffer->data; + for( int n = 0; n < c; n++ ) + d[n] = s[n]; + + if( numElements > buffer->numElements ) + { + Construct(newBuffer, buffer->numElements, numElements); + } + else if( numElements < buffer->numElements ) + { + Destruct(buffer, numElements, buffer->numElements); + } + } + else + { + // Allocate memory for the buffer + newBuffer = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements]; + newBuffer->numElements = numElements; + + int c = numElements > buffer->numElements ? buffer->numElements : numElements; + memcpy(newBuffer->data, buffer->data, c*elementSize); + } + + // Release the old buffer + delete[] (asBYTE*)buffer; + + buffer = newBuffer; +} + +// internal +bool CScriptArray::CheckMaxSize(asUINT numElements) +{ + // This code makes sure the size of the buffer that is allocated + // for the array doesn't overflow and becomes smaller than requested + + asUINT maxSize = 0xFFFFFFFFul - sizeof(SArrayBuffer) + 1; + if( objType->GetSubTypeId() & asTYPEID_MASK_OBJECT ) + { + maxSize /= sizeof(void*); + } + else + { + maxSize /= elementSize; + } + + if( numElements > maxSize ) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + { + // Set a script exception + ctx->SetException("Too large array size"); + } + + return false; + } + + // OK + return true; +} + +asIObjectType *CScriptArray::GetArrayObjectType() const +{ + return objType; +} + +int CScriptArray::GetArrayTypeId() const +{ + return objType->GetTypeId(); +} + +int CScriptArray::GetElementTypeId() const +{ + return objType->GetSubTypeId(); +} + +// Return a pointer to the array element. Returns 0 if the index is out of bounds +void *CScriptArray::At(asUINT index) +{ + if( index >= buffer->numElements ) + { + // If this is called from a script we raise a script exception + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException("Index out of bounds"); + return 0; + } + else + { + int typeId = objType->GetSubTypeId(); + if( (typeId & asTYPEID_MASK_OBJECT) && !isArrayOfHandles ) + return (void*)((size_t*)buffer->data)[index]; + else + return buffer->data + elementSize*index; + } +} + +// internal +void CScriptArray::CreateBuffer(SArrayBuffer **buf, asUINT numElements) +{ + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+sizeof(void*)*numElements]; + (*buf)->numElements = numElements; + } + else + { + *buf = (SArrayBuffer*)new asBYTE[sizeof(SArrayBuffer)-1+elementSize*numElements]; + (*buf)->numElements = numElements; + } + + Construct(*buf, 0, numElements); +} + +// internal +void CScriptArray::DeleteBuffer(SArrayBuffer *buf) +{ + Destruct(buf, 0, buf->numElements); + + // Free the buffer + delete[] (asBYTE*)buf; +} + +// internal +void CScriptArray::Construct(SArrayBuffer *buf, asUINT start, asUINT end) +{ + int typeId = objType->GetSubTypeId(); + if( isArrayOfHandles ) + { + // Set all object handles to null + asDWORD *d = (asDWORD*)(buf->data + start * sizeof(void*)); + memset(d, 0, (end-start)*sizeof(void*)); + } + else if( typeId & asTYPEID_MASK_OBJECT ) + { + asDWORD **max = (asDWORD**)(buf->data + end * sizeof(void*)); + asDWORD **d = (asDWORD**)(buf->data + start * sizeof(void*)); + + asIScriptEngine *engine = objType->GetEngine(); + + for( ; d < max; d++ ) + *d = (asDWORD*)engine->CreateScriptObject(typeId); + } +} + +// internal +void CScriptArray::Destruct(SArrayBuffer *buf, asUINT start, asUINT end) +{ + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + asIScriptEngine *engine = objType->GetEngine(); + + asDWORD **max = (asDWORD**)(buf->data + end * sizeof(void*)); + asDWORD **d = (asDWORD**)(buf->data + start * sizeof(void*)); + + for( ; d < max; d++ ) + { + if( *d ) + engine->ReleaseScriptObject(*d, typeId); + } + } +} + +// internal +void CScriptArray::CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src) +{ + asIScriptEngine *engine = objType->GetEngine(); + if( isArrayOfHandles ) + { + // Copy the references and increase the reference counters + if( dst->numElements > 0 && src->numElements > 0 ) + { + int typeId = objType->GetSubTypeId(); + int count = dst->numElements > src->numElements ? src->numElements : dst->numElements; + + asDWORD **max = (asDWORD**)(dst->data + count * sizeof(void*)); + asDWORD **d = (asDWORD**)dst->data; + asDWORD **s = (asDWORD**)src->data; + + for( ; d < max; d++, s++ ) + { + *d = *s; + if( *d ) + engine->AddRefScriptObject(*d, typeId); + } + } + } + else + { + int typeId = objType->GetSubTypeId(); + + if( dst->numElements > 0 && src->numElements > 0 ) + { + int count = dst->numElements > src->numElements ? src->numElements : dst->numElements; + if( typeId & asTYPEID_MASK_OBJECT ) + { + // Call the assignment operator on all of the objects + asDWORD **max = (asDWORD**)(dst->data + count * sizeof(void*)); + asDWORD **d = (asDWORD**)dst->data; + asDWORD **s = (asDWORD**)src->data; + + for( ; d < max; d++, s++ ) + engine->CopyScriptObject(*d, *s, typeId); + } + else + { + // Primitives are copied byte for byte + memcpy(dst->data, src->data, count*elementSize); + } + } + } +} + +// GC behaviour +void CScriptArray::EnumReferences(asIScriptEngine *engine) +{ + // If the array is holding handles, then we need to notify the GC of them + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + void **d = (void**)buffer->data; + for( asUINT n = 0; n < buffer->numElements; n++ ) + { + if( d[n] ) + engine->GCEnumCallback(d[n]); + } + } +} + +// GC behaviour +void CScriptArray::ReleaseAllHandles(asIScriptEngine *engine) +{ + int subTypeId = objType->GetSubTypeId(); + asIObjectType *subType = engine->GetObjectTypeById(subTypeId); + if( subType && subType->GetFlags() & asOBJ_GC ) + { + void **d = (void**)buffer->data; + for( asUINT n = 0; n < buffer->numElements; n++ ) + { + if( d[n] ) + { + engine->ReleaseScriptObject(d[n], subTypeId); + d[n] = 0; + } + } + } +} + +void CScriptArray::AddRef() +{ + // Clear the GC flag then increase the counter + gcFlag = false; + refCount++; +} + +void CScriptArray::Release() +{ + // Now do the actual releasing (clearing the flag set by GC) + gcFlag = false; + if( --refCount == 0 ) + { + delete this; + } +} + +// GC behaviour +int CScriptArray::GetRefCount() +{ + return refCount; +} + +// GC behaviour +void CScriptArray::SetFlag() +{ + gcFlag = true; +} + +// GC behaviour +bool CScriptArray::GetFlag() +{ + return gcFlag; +} + +//-------------------------------------------- +// Generic calling conventions + +static void ScriptArrayFactory_Generic(asIScriptGeneric *gen) +{ + asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); + + *(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayFactory(ot); +} + +static void ScriptArrayFactory2_Generic(asIScriptGeneric *gen) +{ + asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); + asUINT length = gen->GetArgDWord(1); + + *(CScriptArray**)gen->GetAddressOfReturnLocation() = ScriptArrayFactory2(ot, length); +} + +static void ScriptArrayTemplateCallback_Generic(asIScriptGeneric *gen) +{ + asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); + *(bool*)gen->GetAddressOfReturnLocation() = ScriptArrayTemplateCallback(ot); +} + +static void ScriptArrayAssignment_Generic(asIScriptGeneric *gen) +{ + CScriptArray *other = (CScriptArray*)gen->GetArgObject(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + + *self = *other; + + gen->SetReturnObject(self); +} + +static void ScriptArrayAt_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + + gen->SetReturnAddress(self->At(index)); +} + +static void ScriptArrayLength_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + + gen->SetReturnDWord(self->GetSize()); +} + +static void ScriptArrayResize_Generic(asIScriptGeneric *gen) +{ + asUINT size = gen->GetArgDWord(0); + CScriptArray *self = (CScriptArray*)gen->GetObject(); + + self->Resize(size); +} + +static void ScriptArrayAddRef_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->AddRef(); +} + +static void ScriptArrayRelease_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->Release(); +} + +static void ScriptArrayGetRefCount_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ScriptArraySetFlag_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + self->SetFlag(); +} + +static void ScriptArrayGetFlag_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); +} + +static void ScriptArrayEnumReferences_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptArrayReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + CScriptArray *self = (CScriptArray*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +void RegisterScriptArray_Generic(asIScriptEngine *engine) +{ + int r; + + r = engine->RegisterObjectType("array", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 ); + + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in)", asFUNCTION(ScriptArrayFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in)", asFUNCTION(ScriptArrayTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_FACTORY, "array@ f(int&in, uint)", asFUNCTION(ScriptArrayFactory2_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptArrayAddRef_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptArrayRelease_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_INDEX, "T &f(uint)", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_INDEX, "const T &f(uint) const", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "array &opAssign(const array&in)", asFUNCTION(ScriptArrayAssignment_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "uint length() const", asFUNCTION(ScriptArrayLength_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("array", "void resize(uint)", asFUNCTION(ScriptArrayResize_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptArrayGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptArraySetFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptArrayGetFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptArrayEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptArrayReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 ); +} + +END_AS_NAMESPACE diff --git a/AngelScript/scriptarray/scriptarray.h b/AngelScript/scriptarray/scriptarray.h new file mode 100644 index 000000000..eaa23eaaa --- /dev/null +++ b/AngelScript/scriptarray/scriptarray.h @@ -0,0 +1,65 @@ +#ifndef SCRIPTARRAY_H +#define SCRIPTARRAY_H + +#include + +BEGIN_AS_NAMESPACE + +struct SArrayBuffer; + +class CScriptArray +{ +public: + CScriptArray(asUINT length, asIObjectType *ot); + virtual ~CScriptArray(); + + void AddRef(); + void Release(); + + // Type information + asIObjectType *GetArrayObjectType() const; + int GetArrayTypeId() const; + int GetElementTypeId() const; + + void Resize(asUINT numElements); + asUINT GetSize(); + + // Get a pointer to an element. Returns 0 if out of bounds + void *At(asUINT index); + + CScriptArray &operator=(const CScriptArray&); + + // TODO: Add methods Sort, Reverse, Find, PopLast, PushLast, InsertAt, RemoveAt, etc + + // GC methods + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + +protected: + int refCount; + bool gcFlag; + asIObjectType *objType; + SArrayBuffer *buffer; + bool isArrayOfHandles; + int elementSize; + + bool CheckMaxSize(asUINT numElements); + + void CreateBuffer(SArrayBuffer **buf, asUINT numElements); + void DeleteBuffer(SArrayBuffer *buf); + void CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src); + + void Construct(SArrayBuffer *buf, asUINT start, asUINT end); + void Destruct(SArrayBuffer *buf, asUINT start, asUINT end); +}; + +void RegisterScriptArray(asIScriptEngine *engine); +void RegisterScriptArray_Native(asIScriptEngine *engine); +void RegisterScriptArray_Generic(asIScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/scriptdictionary/scriptdictionary.cpp b/AngelScript/scriptdictionary/scriptdictionary.cpp new file mode 100644 index 000000000..1a4820379 --- /dev/null +++ b/AngelScript/scriptdictionary/scriptdictionary.cpp @@ -0,0 +1,467 @@ +#include +#include +#include "scriptdictionary.h" + +BEGIN_AS_NAMESPACE + +using namespace std; + +//-------------------------------------------------------------------------- +// CScriptDictionary implementation + +CScriptDictionary::CScriptDictionary(asIScriptEngine *engine) +{ + // We start with one reference + refCount = 1; + + // Keep a reference to the engine for as long as we live + // We don't increment the reference counter, because the + // engine will hold a pointer to the object. + this->engine = engine; + + // Notify the garbage collector of this object + // TODO: The type id should be cached + engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeIdByDecl("dictionary")); +} + +CScriptDictionary::~CScriptDictionary() +{ + // Delete all keys and values + DeleteAll(); +} + +void CScriptDictionary::AddRef() +{ + // We need to clear the GC flag + refCount = (refCount & 0x7FFFFFFF) + 1; +} + +void CScriptDictionary::Release() +{ + // We need to clear the GC flag + refCount = (refCount & 0x7FFFFFFF) - 1; + if( refCount == 0 ) + delete this; +} + +int CScriptDictionary::GetRefCount() +{ + return refCount & 0x7FFFFFFF; +} + +void CScriptDictionary::SetGCFlag() +{ + refCount |= 0x80000000; +} + +bool CScriptDictionary::GetGCFlag() +{ + return (refCount & 0x80000000) ? true : false; +} + +void CScriptDictionary::EnumReferences(asIScriptEngine *engine) +{ + // Call the gc enum callback for each of the objects + map::iterator it; + for( it = dict.begin(); it != dict.end(); it++ ) + { + if( it->second.typeId & asTYPEID_MASK_OBJECT ) + engine->GCEnumCallback(it->second.valueObj); + } +} + +void CScriptDictionary::ReleaseAllReferences(asIScriptEngine * /*engine*/) +{ + // We're being told to release all references in + // order to break circular references for dead objects + DeleteAll(); +} + +CScriptDictionary &CScriptDictionary::operator =(const CScriptDictionary & /*other*/) +{ + // Do nothing + // TODO: Should do a shallow copy of the dictionary + + return *this; +} + +void CScriptDictionary::Set(const string &key, void *value, int typeId) +{ + valueStruct valStruct = {{0},0}; + valStruct.typeId = typeId; + if( typeId & asTYPEID_OBJHANDLE ) + { + // We're receiving a reference to the handle, so we need to dereference it + valStruct.valueObj = *(void**)value; + engine->AddRefScriptObject(valStruct.valueObj, typeId); + } + else if( typeId & asTYPEID_MASK_OBJECT ) + { + // Create a copy of the object + valStruct.valueObj = engine->CreateScriptObjectCopy(value, typeId); + } + else + { + // Copy the primitive value + // We receive a pointer to the value. + int size = engine->GetSizeOfPrimitiveType(typeId); + memcpy(&valStruct.valueInt, value, size); + } + + map::iterator it; + it = dict.find(key); + if( it != dict.end() ) + { + FreeValue(it->second); + + // Insert the new value + it->second = valStruct; + } + else + { + dict.insert(map::value_type(key, valStruct)); + } +} + +// This overloaded method is implemented so that all integer and +// unsigned integers types will be stored in the dictionary as int64 +// through implicit conversions. This simplifies the management of the +// numeric types when the script retrieves the stored value using a +// different type. +void CScriptDictionary::Set(const string &key, asINT64 &value) +{ + Set(key, &value, asTYPEID_INT64); +} + +// This overloaded method is implemented so that all floating point types +// will be stored in the dictionary as double through implicit conversions. +// This simplifies the management of the numeric types when the script +// retrieves the stored value using a different type. +void CScriptDictionary::Set(const string &key, double &value) +{ + Set(key, &value, asTYPEID_DOUBLE); +} + +// Returns true if the value was successfully retrieved +bool CScriptDictionary::Get(const string &key, void *value, int typeId) const +{ + map::const_iterator it; + it = dict.find(key); + if( it != dict.end() ) + { + // Return the value + if( typeId & asTYPEID_OBJHANDLE ) + { + // A handle can be retrieved if the stored type is a handle of same or compatible type + // or if the stored type is an object that implements the interface that the handle refer to. + if( (it->second.typeId & asTYPEID_MASK_OBJECT) && + engine->IsHandleCompatibleWithObject(it->second.valueObj, it->second.typeId, typeId) ) + { + engine->AddRefScriptObject(it->second.valueObj, it->second.typeId); + *(void**)value = it->second.valueObj; + + return true; + } + } + else if( typeId & asTYPEID_MASK_OBJECT ) + { + // Verify that the copy can be made + bool isCompatible = false; + if( it->second.typeId == typeId ) + isCompatible = true; + + // Copy the object into the given reference + if( isCompatible ) + { + engine->CopyScriptObject(value, it->second.valueObj, typeId); + + return true; + } + } + else + { + if( it->second.typeId == typeId ) + { + int size = engine->GetSizeOfPrimitiveType(typeId); + memcpy(value, &it->second.valueInt, size); + return true; + } + + // We know all numbers are stored as either int64 or double, since we register overloaded functions for those + if( it->second.typeId == asTYPEID_INT64 && typeId == asTYPEID_DOUBLE ) + { + *(double*)value = double(it->second.valueInt); + return true; + } + else if( it->second.typeId == asTYPEID_DOUBLE && typeId == asTYPEID_INT64 ) + { + *(asINT64*)value = asINT64(it->second.valueFlt); + return true; + } + } + } + + // AngelScript has already initialized the value with a default value, + // so we don't have to do anything if we don't find the element, or if + // the element is incompatible with the requested type. + + return false; +} + +bool CScriptDictionary::Get(const string &key, asINT64 &value) const +{ + return Get(key, &value, asTYPEID_INT64); +} + +bool CScriptDictionary::Get(const string &key, double &value) const +{ + return Get(key, &value, asTYPEID_DOUBLE); +} + +bool CScriptDictionary::Exists(const string &key) const +{ + map::const_iterator it; + it = dict.find(key); + if( it != dict.end() ) + { + return true; + } + + return false; +} + +void CScriptDictionary::Delete(const string &key) +{ + map::iterator it; + it = dict.find(key); + if( it != dict.end() ) + { + FreeValue(it->second); + + dict.erase(it); + } +} + +void CScriptDictionary::DeleteAll() +{ + map::iterator it; + for( it = dict.begin(); it != dict.end(); it++ ) + { + FreeValue(it->second); + } + + dict.clear(); +} + +void CScriptDictionary::FreeValue(valueStruct &value) +{ + // If it is a handle or a ref counted object, call release + if( value.typeId & asTYPEID_MASK_OBJECT ) + { + // Let the engine release the object + engine->ReleaseScriptObject(value.valueObj, value.typeId); + value.valueObj = 0; + value.typeId = 0; + } + + // For primitives, there's nothing to do +} + +//-------------------------------------------------------------------------- +// Generic wrappers + +void ScriptDictionaryFactory_Generic(asIScriptGeneric *gen) +{ + *(CScriptDictionary**)gen->GetAddressOfReturnLocation() = new CScriptDictionary(gen->GetEngine()); +} + +void ScriptDictionaryAddRef_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + dict->AddRef(); +} + +void ScriptDictionaryRelease_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + dict->Release(); +} + +void ScriptDictionarySet_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + void *ref = *(void**)gen->GetAddressOfArg(1); + int typeId = gen->GetArgTypeId(1); + dict->Set(*key, ref, typeId); +} + +void ScriptDictionarySetInt_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + void *ref = *(void**)gen->GetAddressOfArg(1); + dict->Set(*key, *(asINT64*)ref); +} + +void ScriptDictionarySetFlt_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + void *ref = *(void**)gen->GetAddressOfArg(1); + dict->Set(*key, *(double*)ref); +} + +void ScriptDictionaryGet_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + void *ref = *(void**)gen->GetAddressOfArg(1); + int typeId = gen->GetArgTypeId(1); + *(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, ref, typeId); +} + +void ScriptDictionaryGetInt_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + void *ref = *(void**)gen->GetAddressOfArg(1); + *(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(asINT64*)ref); +} + +void ScriptDictionaryGetFlt_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + void *ref = *(void**)gen->GetAddressOfArg(1); + *(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(double*)ref); +} + +void ScriptDictionaryExists_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + bool ret = dict->Exists(*key); + *(bool*)gen->GetAddressOfReturnLocation() = ret; +} + +void ScriptDictionaryDelete_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + string *key = *(string**)gen->GetAddressOfArg(0); + dict->Delete(*key); +} + +void ScriptDictionaryDeleteAll_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject(); + dict->DeleteAll(); +} + +static void ScriptDictionaryGetRefCount_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *self = (CScriptDictionary*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ScriptDictionarySetGCFlag_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *self = (CScriptDictionary*)gen->GetObject(); + self->SetGCFlag(); +} + +static void ScriptDictionaryGetGCFlag_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *self = (CScriptDictionary*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag(); +} + +static void ScriptDictionaryEnumReferences_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *self = (CScriptDictionary*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptDictionaryReleaseAllReferences_Generic(asIScriptGeneric *gen) +{ + CScriptDictionary *self = (CScriptDictionary*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllReferences(engine); +} + +//-------------------------------------------------------------------------- +// Register the type + +void RegisterScriptDictionary(asIScriptEngine *engine) +{ + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) + RegisterScriptDictionary_Generic(engine); + else + RegisterScriptDictionary_Native(engine); +} + +void RegisterScriptDictionary_Native(asIScriptEngine *engine) +{ + int r; + + r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 ); + // Use the generic interface to construct the object since we need the engine pointer, we could also have retrieved the engine pointer from the active context + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r>= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptDictionary,AddRef), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptDictionary,Release), asCALL_THISCALL); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, ?&in)", asMETHODPR(CScriptDictionary,Set,(const string&,void*,int),void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asMETHODPR(CScriptDictionary,Get,(const string&,void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, int64&in)", asMETHODPR(CScriptDictionary,Set,(const string&,asINT64&),void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asMETHODPR(CScriptDictionary,Get,(const string&,asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, double&in)", asMETHODPR(CScriptDictionary,Set,(const string&,double&),void), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asMETHODPR(CScriptDictionary,Get,(const string&,double&) const,bool), asCALL_THISCALL); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asMETHOD(CScriptDictionary,Exists), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "void delete(const string &in)", asMETHOD(CScriptDictionary,Delete), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asMETHOD(CScriptDictionary,DeleteAll), asCALL_THISCALL); assert( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptDictionary,GetRefCount), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptDictionary,SetGCFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptDictionary,GetGCFlag), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictionary,EnumReferences), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictionary,ReleaseAllReferences), asCALL_THISCALL); assert( r >= 0 ); +} + +void RegisterScriptDictionary_Generic(asIScriptEngine *engine) +{ + int r; + + r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r>= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptDictionaryAddRef_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptDictionaryRelease_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, ?&in)", asFUNCTION(ScriptDictionarySet_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asFUNCTION(ScriptDictionaryGet_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, int64&in)", asFUNCTION(ScriptDictionarySetInt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asFUNCTION(ScriptDictionaryGetInt_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, double&in)", asFUNCTION(ScriptDictionarySetFlt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asFUNCTION(ScriptDictionaryGetFlt_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asFUNCTION(ScriptDictionaryExists_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "void delete(const string &in)", asFUNCTION(ScriptDictionaryDelete_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asFUNCTION(ScriptDictionaryDeleteAll_Generic), asCALL_GENERIC); assert( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptDictionaryGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptDictionarySetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptDictionaryGetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryReleaseAllReferences_Generic), asCALL_GENERIC); assert( r >= 0 ); +} + +END_AS_NAMESPACE + + diff --git a/AngelScript/scriptdictionary/scriptdictionary.h b/AngelScript/scriptdictionary/scriptdictionary.h new file mode 100644 index 000000000..66ab45633 --- /dev/null +++ b/AngelScript/scriptdictionary/scriptdictionary.h @@ -0,0 +1,98 @@ +#ifndef SCRIPTDICTIONARY_H +#define SCRIPTDICTIONARY_H + +// The dictionary class relies on the script string object, thus the script +// string type must be registered with the engine before registering the +// dictionary type + +#include +#include + +#ifdef _MSC_VER +// Turn off annoying warnings about truncated symbol names +#pragma warning (disable:4786) +#endif + +#include + +BEGIN_AS_NAMESPACE + +class CScriptDictionary +{ +public: + // Memory management + CScriptDictionary(asIScriptEngine *engine); + void AddRef(); + void Release(); + + // Sets/Gets a variable type value for a key + void Set(const std::string &key, void *value, int typeId); + bool Get(const std::string &key, void *value, int typeId) const; + + // Sets/Gets an integer number value for a key + void Set(const std::string &key, asINT64 &value); + bool Get(const std::string &key, asINT64 &value) const; + + // Sets/Gets a real number value for a key + void Set(const std::string &key, double &value); + bool Get(const std::string &key, double &value) const; + + // Returns true if the key is set + bool Exists(const std::string &key) const; + + // Deletes the key + void Delete(const std::string &key); + + // Deletes all keys + void DeleteAll(); + + // Garbage collections behaviours + int GetRefCount(); + void SetGCFlag(); + bool GetGCFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllReferences(asIScriptEngine *engine); + +protected: + // The structure for holding the values + struct valueStruct + { + union + { + asINT64 valueInt; + double valueFlt; + void *valueObj; + }; + int typeId; + }; + + // We don't want anyone to call the destructor directly, it should be called through the Release method + virtual ~CScriptDictionary(); + + // Don't allow assignment + CScriptDictionary &operator =(const CScriptDictionary &other); + + // Helper methods + void FreeValue(valueStruct &value); + + // Our properties + asIScriptEngine *engine; + int refCount; + std::map dict; +}; + +// This function will determine the configuration of the engine +// and use one of the two functions below to register the dictionary object +void RegisterScriptDictionary(asIScriptEngine *engine); + +// Call this function to register the math functions +// using native calling conventions +void RegisterScriptDictionary_Native(asIScriptEngine *engine); + +// Use this one instead if native calling conventions +// are not supported on the target platform +void RegisterScriptDictionary_Generic(asIScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/scriptfile/scriptfile.cpp b/AngelScript/scriptfile/scriptfile.cpp new file mode 100644 index 000000000..6e4c88df7 --- /dev/null +++ b/AngelScript/scriptfile/scriptfile.cpp @@ -0,0 +1,369 @@ +#include "scriptfile.h" +#include +#include +#include +#include +#include + +#ifdef _WIN32_WCE +#include // For GetModuleFileName +#ifdef GetObject +#undef GetObject +#endif +#endif + +using namespace std; + +BEGIN_AS_NAMESPACE + +CScriptFile *ScriptFile_Factory() +{ + return new CScriptFile(); +} + +void ScriptFile_Factory_Generic(asIScriptGeneric *gen) +{ + *(CScriptFile**)gen->GetAddressOfReturnLocation() = ScriptFile_Factory(); +} + +void ScriptFile_AddRef_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + file->AddRef(); +} + +void ScriptFile_Release_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + file->Release(); +} + +void ScriptFile_Open_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + std::string *f = (std::string*)gen->GetArgAddress(0); + std::string *m = (std::string*)gen->GetArgAddress(1); + int r = file->Open(*f, *m); + gen->SetReturnDWord(r); +} + +void ScriptFile_Close_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + int r = file->Close(); + gen->SetReturnDWord(r); +} + +void ScriptFile_GetSize_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + int r = file->GetSize(); + gen->SetReturnDWord(r); +} + +void ScriptFile_ReadString_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + int len = gen->GetArgDWord(0); + std::string *str = (std::string*)gen->GetArgAddress(1); + len = file->ReadString(len, *str); + gen->SetReturnDWord(len); +} + +void ScriptFile_ReadLine_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + std::string *str = (std::string*)gen->GetArgAddress(0); + int len = file->ReadLine(*str); + gen->SetReturnDWord(len); +} + +void ScriptFile_WriteString_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + std::string *str = (std::string*)gen->GetArgAddress(0); + gen->SetReturnDWord(file->WriteString(*str)); +} + +void ScriptFile_IsEOF_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + bool r = file->IsEOF(); + gen->SetReturnByte(r); +} + +void ScriptFile_GetPos_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + gen->SetReturnDWord(file->GetPos()); +} + +void ScriptFile_SetPos_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + int pos = (int)gen->GetArgDWord(0); + gen->SetReturnDWord(file->SetPos(pos)); +} + +void ScriptFile_MovePos_Generic(asIScriptGeneric *gen) +{ + CScriptFile *file = (CScriptFile*)gen->GetObject(); + int delta = (int)gen->GetArgDWord(0); + gen->SetReturnDWord(file->MovePos(delta)); +} + +void RegisterScriptFile_Native(asIScriptEngine *engine) +{ + int r; + + r = engine->RegisterObjectType("file", 0, asOBJ_REF); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("file", asBEHAVE_FACTORY, "file @f()", asFUNCTION(ScriptFile_Factory), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("file", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptFile,AddRef), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("file", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptFile,Release), asCALL_THISCALL); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("file", "int open(const string &in, const string &in)", asMETHOD(CScriptFile,Open), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int close()", asMETHOD(CScriptFile,Close), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int getSize() const", asMETHOD(CScriptFile,GetSize), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "bool isEndOfFile() const", asMETHOD(CScriptFile,IsEOF), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int readString(uint, string &out)", asMETHOD(CScriptFile,ReadString), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int readLine(string &out)", asMETHOD(CScriptFile,ReadLine), asCALL_THISCALL); assert( r >= 0 ); +#if AS_WRITE_OPS == 1 + r = engine->RegisterObjectMethod("file", "int writeString(const string &in)", asMETHOD(CScriptFile,WriteString), asCALL_THISCALL); assert( r >= 0 ); +#endif + r = engine->RegisterObjectMethod("file", "int getPos() const", asMETHOD(CScriptFile,GetPos), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int setPos(int)", asMETHOD(CScriptFile,SetPos), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int movePos(int)", asMETHOD(CScriptFile,MovePos), asCALL_THISCALL); assert( r >= 0 ); +} + +void RegisterScriptFile_Generic(asIScriptEngine *engine) +{ + int r; + + r = engine->RegisterObjectType("file", 0, asOBJ_REF); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("file", asBEHAVE_FACTORY, "file @f()", asFUNCTION(ScriptFile_Factory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("file", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFile_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("file", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptFile_Release_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("file", "int open(const string &in, const string &in)", asFUNCTION(ScriptFile_Open_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int close()", asFUNCTION(ScriptFile_Close_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int getSize() const", asFUNCTION(ScriptFile_GetSize_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "bool isEndOfFile() const", asFUNCTION(ScriptFile_IsEOF_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int readString(uint, string &out)", asFUNCTION(ScriptFile_ReadString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int readLine(string &out)", asFUNCTION(ScriptFile_ReadLine_Generic), asCALL_GENERIC); assert( r >= 0 ); +#if AS_WRITE_OPS == 1 + r = engine->RegisterObjectMethod("file", "int writeString(const string &in)", asFUNCTION(ScriptFile_WriteString_Generic), asCALL_GENERIC); assert( r >= 0 ); +#endif + r = engine->RegisterObjectMethod("file", "int getPos() const", asFUNCTION(ScriptFile_GetPos_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int setPos(int)", asFUNCTION(ScriptFile_SetPos_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("file", "int movePos(int)", asFUNCTION(ScriptFile_MovePos_Generic), asCALL_GENERIC); assert( r >= 0 ); +} + +void RegisterScriptFile(asIScriptEngine *engine) +{ + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) + RegisterScriptFile_Generic(engine); + else + RegisterScriptFile_Native(engine); +} + +CScriptFile::CScriptFile() +{ + refCount = 1; + file = 0; +} + +CScriptFile::~CScriptFile() +{ + Close(); +} + +void CScriptFile::AddRef() +{ + ++refCount; +} + +void CScriptFile::Release() +{ + if( --refCount == 0 ) + delete this; +} + +int CScriptFile::Open(const std::string &filename, const std::string &mode) +{ + // Close the previously opened file handle + if( file ) + Close(); + + std::string myFilename = filename; + + // Validate the mode + string m; +#if AS_WRITE_OPS == 1 + if( mode != "r" && mode != "w" && mode != "a" ) +#else + if( mode != "r" ) +#endif + return -1; + else + m = mode; + +#ifdef _WIN32_WCE + // no relative pathing on CE + char buf[MAX_PATH]; + static TCHAR apppath[MAX_PATH] = TEXT(""); + if (!apppath[0]) + { + GetModuleFileName(NULL, apppath, MAX_PATH); + + int appLen = _tcslen(apppath); + while (appLen > 1) + { + if (apppath[appLen-1] == TEXT('\\')) + break; + appLen--; + } + + // Terminate the string after the trailing backslash + apppath[appLen] = TEXT('\0'); + } +#ifdef _UNICODE + wcstombs(buf, apppath, wcslen(apppath)+1); +#else + memcpy(buf, apppath, strlen(apppath)); +#endif + myFilename = buf + myFilename; +#endif + + + // By default windows translates "\r\n" to "\n", but we want to read the file as-is. + m += "b"; + + // Open the file + file = fopen(myFilename.c_str(), m.c_str()); + if( file == 0 ) + return -1; + + return 0; +} + +int CScriptFile::Close() +{ + if( file == 0 ) + return -1; + + fclose(file); + file = 0; + + return 0; +} + +int CScriptFile::GetSize() const +{ + if( file == 0 ) + return -1; + + int pos = ftell(file); + fseek(file, 0, SEEK_END); + int size = ftell(file); + fseek(file, pos, SEEK_SET); + + return size; +} + +int CScriptFile::GetPos() const +{ + if( file == 0 ) + return -1; + + return ftell(file); +} + +int CScriptFile::SetPos(int pos) +{ + if( file == 0 ) + return -1; + + int r = fseek(file, pos, SEEK_SET); + + // Return -1 on error + return r ? -1 : 0; +} + +int CScriptFile::MovePos(int delta) +{ + if( file == 0 ) + return -1; + + int r = fseek(file, delta, SEEK_CUR); + + // Return -1 on error + return r ? -1 : 0; +} + +int CScriptFile::ReadString(unsigned int length, std::string &str) +{ + if( file == 0 ) + return 0; + + // Read the string + str.resize(length); + int size = (int)fread(&str[0], 1, length, file); + str.resize(size); + + return size; +} + +int CScriptFile::ReadLine(std::string &str) +{ + if( file == 0 ) + return 0; + + // Read until the first new-line character + str = ""; + char buf[256]; + + do + { + // Get the current position so we can determine how many characters were read + int start = ftell(file); + + // Set the last byte to something different that 0, so that we can check if the buffer was filled up + buf[255] = 1; + + // Read the line (or first 255 characters, which ever comes first) + fgets(buf, 256, file); + + // Get the position after the read + int end = ftell(file); + + // Add the read characters to the output buffer + str.append(buf, end-start); + } + while( !feof(file) && buf[255] == 0 && buf[254] != '\n' ); + + return int(str.size()); +} + +#if AS_WRITE_OPS == 1 +int CScriptFile::WriteString(const std::string &str) +{ + if( file == 0 ) + return -1; + + // Write the entire string + size_t r = fwrite(&str[0], 1, str.length(), file); + + return int(r); +} +#endif + +bool CScriptFile::IsEOF() const +{ + if( file == 0 ) + return true; + + return feof(file) ? true : false; +} + + +END_AS_NAMESPACE diff --git a/AngelScript/scriptfile/scriptfile.h b/AngelScript/scriptfile/scriptfile.h new file mode 100644 index 000000000..f70b0e1ce --- /dev/null +++ b/AngelScript/scriptfile/scriptfile.h @@ -0,0 +1,89 @@ +// +// CScriptFile +// +// This class encapsulates a FILE pointer in a reference counted class for +// use within AngelScript. +// +// It requires the CScriptString add-on to work. +// + +#ifndef SCRIPTFILE_H +#define SCRIPTFILE_H + +//--------------------------- +// Compilation settings +// + +// Set this flag to turn on/off write support +// 0 = off +// 1 = on + +#ifndef AS_WRITE_OPS +#define AS_WRITE_OPS 1 +#endif + + + + +//--------------------------- +// Declaration +// + +#include +#include + +BEGIN_AS_NAMESPACE + +class CScriptString; + +class CScriptFile +{ +public: + CScriptFile(); + + void AddRef(); + void Release(); + + // TODO: Implement the "r+", "w+" and "a+" modes + // mode = "r" -> open the file for reading + // "w" -> open the file for writing (overwrites existing file) + // "a" -> open the file for appending + int Open(const std::string &filename, const std::string &mode); + int Close(); + int GetSize() const; + bool IsEOF() const; + + // Reading + int ReadString(unsigned int length, std::string &str); + int ReadLine(std::string &str); + + // Writing + int WriteString(const std::string &str); + + // Cursor + int GetPos() const; + int SetPos(int pos); + int MovePos(int delta); + +protected: + ~CScriptFile(); + + int refCount; + FILE *file; +}; + +// This function will determine the configuration of the engine +// and use one of the two functions below to register the file type +void RegisterScriptFile(asIScriptEngine *engine); + +// Call this function to register the file type +// using native calling conventions +void RegisterScriptFile_Native(asIScriptEngine *engine); + +// Use this one instead if native calling conventions +// are not supported on the target platform +void RegisterScriptFile_Generic(asIScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/scriptstring/scriptstring.cpp b/AngelScript/scriptstring/scriptstring.cpp new file mode 100644 index 000000000..138a8b74c --- /dev/null +++ b/AngelScript/scriptstring/scriptstring.cpp @@ -0,0 +1,705 @@ +#include +#include // strstr +#include // sprintf +#include "scriptstring.h" +using namespace std; + +BEGIN_AS_NAMESPACE + +//-------------- +// constructors +//-------------- + +CScriptString::CScriptString() +{ + // Count the first reference + refCount = 1; +} + +CScriptString::CScriptString(const char *s, unsigned int len) +{ + refCount = 1; + buffer.assign(s, len); +} + +CScriptString::CScriptString(const string &s) +{ + refCount = 1; + buffer = s; +} + +CScriptString::CScriptString(const CScriptString &s) +{ + refCount = 1; + buffer = s.buffer; +} + +CScriptString::~CScriptString() +{ + assert( refCount == 0 ); +} + +//-------------------- +// reference counting +//-------------------- + +void CScriptString::AddRef() +{ + refCount++; +} + +static void StringAddRef_Generic(asIScriptGeneric *gen) +{ + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + thisPointer->AddRef(); +} + +void CScriptString::Release() +{ + if( --refCount == 0 ) + delete this; +} + +static void StringRelease_Generic(asIScriptGeneric *gen) +{ + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + thisPointer->Release(); +} + +//----------------- +// string = string +//----------------- + +CScriptString &CScriptString::operator=(const CScriptString &other) +{ + // Copy only the buffer, not the reference counter + buffer = other.buffer; + + // Return a reference to this object + return *this; +} + +static void AssignString_Generic(asIScriptGeneric *gen) +{ + CScriptString *a = (CScriptString*)gen->GetArgAddress(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + *thisPointer = *a; + gen->SetReturnAddress(thisPointer); +} + +//------------------ +// string += string +//------------------ + +CScriptString &CScriptString::operator+=(const CScriptString &other) +{ + buffer += other.buffer; + return *this; +} + +static void AddAssignString_Generic(asIScriptGeneric *gen) +{ + CScriptString *a = (CScriptString*)gen->GetArgAddress(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + *thisPointer += *a; + gen->SetReturnAddress(thisPointer); +} + +//----------------- +// string opCmp string +//----------------- + +static int StringCmp(const string &a, const string &b) +{ + int cmp = 0; + if( a < b ) cmp = -1; + else if( a > b ) cmp = 1; + return cmp; +} + +static void StringCmp_Generic(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + string * b = static_cast(gen->GetArgAddress(0)); + + int cmp = 0; + if( *a < *b ) cmp = -1; + else if( *a > *b ) cmp = 1; + + *(int*)gen->GetAddressOfReturnLocation() = cmp; +} + +//----------------- +// string + string +//----------------- + +CScriptString *operator+(const CScriptString &a, const CScriptString &b) +{ + // Return a new object as a script handle + CScriptString *str = new CScriptString(); + + // Avoid unnecessary memory copying by first reserving the full memory buffer, then concatenating + str->buffer.reserve(a.buffer.length() + b.buffer.length()); + str->buffer += a.buffer; + str->buffer += b.buffer; + + return str; +} + +static void ConcatenateStrings_Generic(asIScriptGeneric *gen) +{ + CScriptString *a = (CScriptString*)gen->GetObject(); + CScriptString *b = (CScriptString*)gen->GetArgAddress(0); + CScriptString *out = *a + *b; + gen->SetReturnAddress(out); +} + +//---------------- +// string = value +//---------------- + +static CScriptString &AssignUIntToString(unsigned int i, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%u", i); + dest.buffer = buf; + return dest; +} + +static void AssignUIntToString_Generic(asIScriptGeneric *gen) +{ + unsigned int i = gen->GetArgDWord(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AssignUIntToString(i, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +static CScriptString &AssignIntToString(int i, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%d", i); + dest.buffer = buf; + return dest; +} + +static void AssignIntToString_Generic(asIScriptGeneric *gen) +{ + int i = gen->GetArgDWord(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AssignIntToString(i, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +static CScriptString &AssignFloatToString(float f, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%g", f); + dest.buffer = buf; + return dest; +} + +static void AssignFloatToString_Generic(asIScriptGeneric *gen) +{ + float f = gen->GetArgFloat(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AssignFloatToString(f, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +static CScriptString &AssignDoubleToString(double f, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%g", f); + dest.buffer = buf; + return dest; +} + +static void AssignDoubleToString_Generic(asIScriptGeneric *gen) +{ + double f = gen->GetArgDouble(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AssignDoubleToString(f, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +//----------------- +// string += value +//----------------- + +static CScriptString &AddAssignUIntToString(unsigned int i, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%u", i); + dest.buffer += buf; + return dest; +} + +static void AddAssignUIntToString_Generic(asIScriptGeneric *gen) +{ + unsigned int i = gen->GetArgDWord(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AddAssignUIntToString(i, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +static CScriptString &AddAssignIntToString(int i, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%d", i); + dest.buffer += buf; + return dest; +} + +static void AddAssignIntToString_Generic(asIScriptGeneric *gen) +{ + int i = gen->GetArgDWord(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AddAssignIntToString(i, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +static CScriptString &AddAssignFloatToString(float f, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%g", f); + dest.buffer += buf; + return dest; +} + +static void AddAssignFloatToString_Generic(asIScriptGeneric *gen) +{ + float f = gen->GetArgFloat(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AddAssignFloatToString(f, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +static CScriptString &AddAssignDoubleToString(double f, CScriptString &dest) +{ + char buf[100]; + sprintf(buf, "%g", f); + dest.buffer += buf; + return dest; +} + +static void AddAssignDoubleToString_Generic(asIScriptGeneric *gen) +{ + double f = gen->GetArgDouble(0); + CScriptString *thisPointer = (CScriptString*)gen->GetObject(); + AddAssignDoubleToString(f, *thisPointer); + gen->SetReturnAddress(thisPointer); +} + +//---------------- +// string + value +//---------------- + +static CScriptString *AddStringUInt(const CScriptString &str, unsigned int i) +{ + char buf[100]; + sprintf(buf, "%u", i); + return new CScriptString(str.buffer + buf); +} + +static void AddStringUInt_Generic(asIScriptGeneric *gen) +{ + CScriptString *str = (CScriptString*)gen->GetObject(); + unsigned int i = gen->GetArgDWord(0); + CScriptString *out = AddStringUInt(*str, i); + gen->SetReturnAddress(out); +} + +static CScriptString *AddStringInt(const CScriptString &str, int i) +{ + char buf[100]; + sprintf(buf, "%d", i); + return new CScriptString(str.buffer + buf); +} + +static void AddStringInt_Generic(asIScriptGeneric *gen) +{ + CScriptString *str = (CScriptString*)gen->GetObject(); + int i = gen->GetArgDWord(0); + CScriptString *out = AddStringInt(*str, i); + gen->SetReturnAddress(out); +} + +static CScriptString *AddStringFloat(const CScriptString &str, float f) +{ + char buf[100]; + sprintf(buf, "%g", f); + return new CScriptString(str.buffer + buf); +} + +static void AddStringFloat_Generic(asIScriptGeneric *gen) +{ + CScriptString *str = (CScriptString*)gen->GetObject(); + float f = gen->GetArgFloat(0); + CScriptString *out = AddStringFloat(*str, f); + gen->SetReturnAddress(out); +} + +static CScriptString *AddStringDouble(const CScriptString &str, double f) +{ + char buf[100]; + sprintf(buf, "%g", f); + return new CScriptString(str.buffer + buf); +} + +static void AddStringDouble_Generic(asIScriptGeneric *gen) +{ + CScriptString *str = (CScriptString*)gen->GetObject(); + double f = gen->GetArgDouble(0); + CScriptString *out = AddStringDouble(*str, f); + gen->SetReturnAddress(out); +} + +//---------------- +// value + string +//---------------- + +static CScriptString *AddIntString(int i, const CScriptString &str) +{ + char buf[100]; + sprintf(buf, "%d", i); + return new CScriptString(buf + str.buffer); +} + +static void AddIntString_Generic(asIScriptGeneric *gen) +{ + int i = gen->GetArgDWord(0); + CScriptString *str = (CScriptString*)gen->GetObject(); + CScriptString *out = AddIntString(i, *str); + gen->SetReturnAddress(out); +} + +static CScriptString *AddUIntString(unsigned int i, const CScriptString &str) +{ + char buf[100]; + sprintf(buf, "%u", i); + return new CScriptString(buf + str.buffer); +} + +static void AddUIntString_Generic(asIScriptGeneric *gen) +{ + unsigned int i = gen->GetArgDWord(0); + CScriptString *str = (CScriptString*)gen->GetObject(); + CScriptString *out = AddUIntString(i, *str); + gen->SetReturnAddress(out); +} + +static CScriptString *AddFloatString(float f, const CScriptString &str) +{ + char buf[100]; + sprintf(buf, "%g", f); + return new CScriptString(buf + str.buffer); +} + +static void AddFloatString_Generic(asIScriptGeneric *gen) +{ + float f = gen->GetArgFloat(0); + CScriptString *str = (CScriptString*)gen->GetObject(); + CScriptString *out = AddFloatString(f, *str); + gen->SetReturnAddress(out); +} + +static CScriptString *AddDoubleString(double f, const CScriptString &str) +{ + char buf[100]; + sprintf(buf, "%g", f); + return new CScriptString(buf + str.buffer); +} + +static void AddDoubleString_Generic(asIScriptGeneric *gen) +{ + double f = gen->GetArgDouble(0); + CScriptString *str = (CScriptString*)gen->GetObject(); + CScriptString *out = AddDoubleString(f, *str); + gen->SetReturnAddress(out); +} + +//---------- +// string[] +//---------- + +static char *StringCharAt(unsigned int i, CScriptString &str) +{ + if( i >= str.buffer.size() ) + { + // Set a script exception + asIScriptContext *ctx = asGetActiveContext(); + ctx->SetException("Out of range"); + + // Return a null pointer + return 0; + } + + return &str.buffer[i]; +} + +static void StringCharAt_Generic(asIScriptGeneric *gen) +{ + unsigned int i = gen->GetArgDWord(0); + CScriptString *str = (CScriptString*)gen->GetObject(); + char *ch = StringCharAt(i, *str); + gen->SetReturnAddress(ch); +} + +//----------------------- +// AngelScript functions +//----------------------- + +// This is the string factory that creates new strings for the script based on string literals +static CScriptString *StringFactory(asUINT length, const char *s) +{ + return new CScriptString(s, length); +} + +static void StringFactory_Generic(asIScriptGeneric *gen) +{ + asUINT length = gen->GetArgDWord(0); + const char *s = (const char*)gen->GetArgAddress(1); + CScriptString *str = StringFactory(length, s); + gen->SetReturnAddress(str); +} + +// This is the default string factory, that is responsible for creating empty string objects, e.g. when a variable is declared +static CScriptString *StringDefaultFactory() +{ + // Allocate and initialize with the default constructor + return new CScriptString(); +} + +static CScriptString *StringCopyFactory(const CScriptString &other) +{ + // Allocate and initialize with the copy constructor + return new CScriptString(other); +} + +static void StringDefaultFactory_Generic(asIScriptGeneric *gen) +{ + *(CScriptString**)gen->GetAddressOfReturnLocation() = StringDefaultFactory(); +} + +static void StringCopyFactory_Generic(asIScriptGeneric *gen) +{ + CScriptString *other = (CScriptString *)gen->GetArgObject(0); + *(CScriptString**)gen->GetAddressOfReturnLocation() = StringCopyFactory(*other); +} + +static void StringEqual_Generic(asIScriptGeneric *gen) +{ + string *a = (string*)gen->GetArgAddress(0); + string *b = (string*)gen->GetArgAddress(1); + bool r = *a == *b; + *(bool*)gen->GetAddressOfReturnLocation() = r; +} + +static void StringEquals_Generic(asIScriptGeneric * gen) +{ + string * a = static_cast(gen->GetObject()); + string * b = static_cast(gen->GetArgAddress(0)); + *(bool*)gen->GetAddressOfReturnLocation() = (*a == *b); +} + +static void StringNotEqual_Generic(asIScriptGeneric *gen) +{ + string *a = (string*)gen->GetArgAddress(0); + string *b = (string*)gen->GetArgAddress(1); + bool r = *a != *b; + *(bool*)gen->GetAddressOfReturnLocation() = r; +} + +static void StringLesserOrEqual_Generic(asIScriptGeneric *gen) +{ + string *a = (string*)gen->GetArgAddress(0); + string *b = (string*)gen->GetArgAddress(1); + bool r = *a <= *b; + *(bool*)gen->GetAddressOfReturnLocation() = r; +} + +static void StringGreaterOrEqual_Generic(asIScriptGeneric *gen) +{ + string *a = (string*)gen->GetArgAddress(0); + string *b = (string*)gen->GetArgAddress(1); + bool r = *a >= *b; + *(bool*)gen->GetAddressOfReturnLocation() = r; +} + +static void StringLesser_Generic(asIScriptGeneric *gen) +{ + string *a = (string*)gen->GetArgAddress(0); + string *b = (string*)gen->GetArgAddress(1); + bool r = *a < *b; + *(bool*)gen->GetAddressOfReturnLocation() = r; +} + +static void StringGreater_Generic(asIScriptGeneric *gen) +{ + string *a = (string*)gen->GetArgAddress(0); + string *b = (string*)gen->GetArgAddress(1); + bool r = *a > *b; + *(bool*)gen->GetAddressOfReturnLocation() = r; +} + +static void StringLength_Generic(asIScriptGeneric *gen) +{ + string *s = (string*)gen->GetObject(); + size_t l = s->size(); + if( sizeof(size_t) == 4 ) + gen->SetReturnDWord((asUINT)l); + else + gen->SetReturnQWord((asQWORD)l); +} + +static void StringResize_Generic(asIScriptGeneric *gen) +{ + string *s = (string*)gen->GetObject(); + size_t v = *(size_t*)gen->GetAddressOfArg(0); + s->resize(v); +} + +// This is where we register the string type +void RegisterScriptString_Native(asIScriptEngine *engine) +{ + int r; + + // Register the type + r = engine->RegisterObjectType("string", 0, asOBJ_REF); assert( r >= 0 ); + + // Register the object operator overloads + // Note: We don't have to register the destructor, since the object uses reference counting + r = engine->RegisterObjectBehaviour("string", asBEHAVE_FACTORY, "string @f()", asFUNCTION(StringDefaultFactory), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_FACTORY, "string @f(const string &in)", asFUNCTION(StringCopyFactory), asCALL_CDECL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptString,AddRef), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptString,Release), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asMETHODPR(CScriptString, operator =, (const CScriptString&), CScriptString&), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asMETHODPR(CScriptString, operator+=, (const CScriptString&), CScriptString&), asCALL_THISCALL); assert( r >= 0 ); + + // Register the factory to return a handle to a new string + // Note: We must register the string factory after the basic behaviours, + // otherwise the library will not allow the use of object handles for this type + r = engine->RegisterStringFactory("string@", asFUNCTION(StringFactory), asCALL_CDECL); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTIONPR(operator ==, (const string &, const string &), bool), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(const string &in) const", asFUNCTIONPR(operator +, (const CScriptString &, const CScriptString &), CScriptString*), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + + // Register the index operator, both as a mutator and as an inspector + r = engine->RegisterObjectBehaviour("string", asBEHAVE_INDEX, "uint8 &f(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_INDEX, "const uint8 &f(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + // Register the object methods + if( sizeof(size_t) == 4 ) + { + r = engine->RegisterObjectMethod("string", "uint length() const", asMETHOD(string,size), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void resize(uint)", asMETHODPR(string,resize,(size_t),void), asCALL_THISCALL); assert( r >= 0 ); + } + else + { + r = engine->RegisterObjectMethod("string", "uint64 length() const", asMETHOD(string,size), asCALL_THISCALL); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void resize(uint64)", asMETHODPR(string,resize,(size_t),void), asCALL_THISCALL); assert( r >= 0 ); + } + + // TODO: Add factory string(const string &in str, int repeatCount) + + // TODO: Add explicit type conversion via constructor and value cast + + // TODO: Add parseInt and parseDouble. Two versions, one without parameter, one with an outparm that returns the number of characters parsed. + + // Automatic conversion from values + r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(double) const", asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(double) const", asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(float) const", asFUNCTION(AddStringFloat), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(float) const", asFUNCTION(AddFloatString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(int)", asFUNCTION(AssignIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(int)", asFUNCTION(AddAssignIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(int) const", asFUNCTION(AddStringInt), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(int) const", asFUNCTION(AddIntString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(uint)", asFUNCTION(AssignUIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint)", asFUNCTION(AddAssignUIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(uint) const", asFUNCTION(AddStringUInt), asCALL_CDECL_OBJFIRST); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(uint) const", asFUNCTION(AddUIntString), asCALL_CDECL_OBJLAST); assert( r >= 0 ); +} + +void RegisterScriptString_Generic(asIScriptEngine *engine) +{ + int r; + + // Register the type + r = engine->RegisterObjectType("string", 0, asOBJ_REF); assert( r >= 0 ); + + // Register the object operator overloads + // Note: We don't have to register the destructor, since the object uses reference counting + r = engine->RegisterObjectBehaviour("string", asBEHAVE_FACTORY, "string @f()", asFUNCTION(StringDefaultFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_FACTORY, "string @f(const string &in)", asFUNCTION(StringCopyFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_ADDREF, "void f()", asFUNCTION(StringAddRef_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_RELEASE, "void f()", asFUNCTION(StringRelease_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asFUNCTION(AssignString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asFUNCTION(AddAssignString_Generic), asCALL_GENERIC); assert( r >= 0 ); + + // Register the factory to return a handle to a new string + // Note: We must register the string factory after the basic behaviours, + // otherwise the library will not allow the use of object handles for this type + r = engine->RegisterStringFactory("string@", asFUNCTION(StringFactory_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTION(StringEquals_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmp_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(const string &in) const", asFUNCTION(ConcatenateStrings_Generic), asCALL_GENERIC); assert( r >= 0 ); + + // Register the index operator, both as a mutator and as an inspector + r = engine->RegisterObjectBehaviour("string", asBEHAVE_INDEX, "uint8 &f(uint)", asFUNCTION(StringCharAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectBehaviour("string", asBEHAVE_INDEX, "const uint8 &f(uint) const", asFUNCTION(StringCharAt_Generic), asCALL_GENERIC); assert( r >= 0 ); + + // Register the object methods + if( sizeof(size_t) == 4 ) + { + r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringLength_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void resize(uint)", asFUNCTION(StringResize_Generic), asCALL_GENERIC); assert( r >= 0 ); + } + else + { + r = engine->RegisterObjectMethod("string", "uint64 length() const", asFUNCTION(StringLength_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "void resize(uint64)", asFUNCTION(StringResize_Generic), asCALL_GENERIC); assert( r >= 0 ); + } + + // Automatic conversion from values + r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDoubleToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDoubleToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(double) const", asFUNCTION(AddStringDouble_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(double) const", asFUNCTION(AddDoubleString_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloatToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloatToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(float) const", asFUNCTION(AddStringFloat_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(float) const", asFUNCTION(AddFloatString_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(int)", asFUNCTION(AssignIntToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(int)", asFUNCTION(AddAssignIntToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(int) const", asFUNCTION(AddStringInt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(int) const", asFUNCTION(AddIntString_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("string", "string &opAssign(uint)", asFUNCTION(AssignUIntToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint)", asFUNCTION(AddAssignUIntToString_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd(uint) const", asFUNCTION(AddStringUInt_Generic), asCALL_GENERIC); assert( r >= 0 ); + r = engine->RegisterObjectMethod("string", "string@ opAdd_r(uint) const", asFUNCTION(AddUIntString_Generic), asCALL_GENERIC); assert( r >= 0 ); +} + +void RegisterScriptString(asIScriptEngine *engine) +{ + if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") ) + RegisterScriptString_Generic(engine); + else + RegisterScriptString_Native(engine); +} + +END_AS_NAMESPACE + + diff --git a/AngelScript/scriptstring/scriptstring.h b/AngelScript/scriptstring/scriptstring.h new file mode 100644 index 000000000..d2bcbde29 --- /dev/null +++ b/AngelScript/scriptstring/scriptstring.h @@ -0,0 +1,64 @@ +// +// CScriptString +// +// This class is used to pass strings between the application and the script engine. +// It is basically a container for the normal std::string, with the addition of a +// reference counter so that the script can use object handles to hold the type. +// +// Because the class is reference counted it cannot be stored locally in the +// application functions, nor be received or returned by value. Instead it should +// be manipulated through pointers or references. +// +// Note, because the internal buffer is placed at the beginning of the class +// structure it is infact possible to receive this type as a reference or pointer +// to a normal std::string where the reference counter doesn't have to be manipulated. +// + +#ifndef SCRIPTSTRING_H +#define SCRIPTSTRING_H + +#include +#include + +BEGIN_AS_NAMESPACE + +class CScriptString +{ +public: + CScriptString(); + CScriptString(const CScriptString &other); + CScriptString(const char *s, unsigned int len); + CScriptString(const std::string &s); + + void AddRef(); + void Release(); + + CScriptString &operator=(const CScriptString &other); + CScriptString &operator+=(const CScriptString &other); + friend CScriptString *operator+(const CScriptString &a, const CScriptString &b); + + std::string buffer; + +protected: + ~CScriptString(); + int refCount; +}; + +// This function will determine the configuration of the engine +// and use one of the two functions below to register the string type +void RegisterScriptString(asIScriptEngine *engine); + +// Call this function to register the string type +// using native calling conventions +void RegisterScriptString_Native(asIScriptEngine *engine); + +// Use this one instead if native calling conventions +// are not supported on the target platform +void RegisterScriptString_Generic(asIScriptEngine *engine); + +// This function will register utility functions for the script string +void RegisterScriptStringUtils(asIScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/scriptstring/scriptstring_utils.cpp b/AngelScript/scriptstring/scriptstring_utils.cpp new file mode 100644 index 000000000..13eefecaa --- /dev/null +++ b/AngelScript/scriptstring/scriptstring_utils.cpp @@ -0,0 +1,369 @@ +#include +#include "scriptstring.h" +#include // strstr + + + +// This function returns a string containing the substring of the input string +// determined by the starting index and count of characters. +// +// AngelScript signature: +// string@ substring(const string &in str, int start, int count) +void StringSubString_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + int start = *(int*)gen->GetAddressOfArg(1); + int count = *(int*)gen->GetAddressOfArg(2); + + // Create the substring + CScriptString *sub = new CScriptString(); + sub->buffer = str->buffer.substr(start,count); + + // Return the substring + *(CScriptString**)gen->GetAddressOfReturnLocation() = sub; +} + + + +// This function returns the index of the first position where the substring +// exists in the input string. If the substring doesn't exist in the input +// string -1 is returned. +// +// AngelScript signature: +// int findFirst(const string &in str, const string &in sub, int start) +void StringFindFirst_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *sub = *(CScriptString**)gen->GetAddressOfArg(1); + int start = *(int*)gen->GetAddressOfArg(2); + + // Find the substring + int loc = (int)str->buffer.find(sub->buffer, start); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} +// TODO: Angelscript should permit default parameters +void StringFindFirst0_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *sub = *(CScriptString**)gen->GetAddressOfArg(1); + + // Find the substring + int loc = (int)str->buffer.find(sub->buffer); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} + + + +// This function returns the index of the last position where the substring +// exists in the input string. If the substring doesn't exist in the input +// string -1 is returned. +// +// AngelScript signature: +// int findLast(const string &in str, const string &in sub, int start) +void StringFindLast_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *sub = *(CScriptString**)gen->GetAddressOfArg(1); + int start = *(int*)gen->GetAddressOfArg(2); + + // Find the substring + int loc = (int)str->buffer.rfind(sub->buffer, start); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} +void StringFindLast0_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *sub = *(CScriptString**)gen->GetAddressOfArg(1); + + // Find the substring + int loc = (int)str->buffer.rfind(sub->buffer); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} + + + +// This function returns the index of the first character that is in +// the specified set of characters. If no such character is found -1 is +// returned. +// +// AngelScript signature: +// int findFirstOf(const string &in str, const string &in chars, int start) +void StringFindFirstOf_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + int start = *(int*)gen->GetAddressOfArg(2); + + // Find the substring + int loc = (int)str->buffer.find_first_of(chars->buffer, start); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} +void StringFindFirstOf0_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + + // Find the substring + int loc = (int)str->buffer.find_first_of(chars->buffer); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} + + +// This function returns the index of the first character that is not in +// the specified set of characters. If no such character is found -1 is +// returned. +// +// AngelScript signature: +// int findFirstNotOf(const string &in str, const string &in chars, int start) +void StringFindFirstNotOf_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + int start = *(int*)gen->GetAddressOfArg(2); + + // Find the substring + int loc = (int)str->buffer.find_first_not_of(chars->buffer, start); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} +void StringFindFirstNotOf0_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + + // Find the substring + int loc = (int)str->buffer.find_first_not_of(chars->buffer); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} + + + +// This function returns the index of the last character that is in +// the specified set of characters. If no such character is found -1 is +// returned. +// +// AngelScript signature: +// int findLastOf(const string &in str, const string &in chars, int start) +void StringFindLastOf_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + int start = *(int*)gen->GetAddressOfArg(2); + + // Find the substring + int loc = (int)str->buffer.find_last_of(chars->buffer, start); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} +void StringFindLastOf0_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + + // Find the substring + int loc = (int)str->buffer.find_last_of(chars->buffer); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} + + + +// This function returns the index of the last character that is not in +// the specified set of characters. If no such character is found -1 is +// returned. +// +// AngelScript signature: +// int findLastNotOf(const string &in str, const string &in chars, int start) +void StringFindLastNotOf_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + int start = *(int*)gen->GetAddressOfArg(2); + + // Find the substring + int loc = (int)str->buffer.find_last_not_of(chars->buffer, start); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} +void StringFindLastNotOf0_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *chars = *(CScriptString**)gen->GetAddressOfArg(1); + + // Find the substring + int loc = (int)str->buffer.find_last_not_of(chars->buffer); + + // Return the result + *(int*)gen->GetAddressOfReturnLocation() = loc; +} + + + +// This function takes an input string and splits it into parts by looking +// for a specified delimiter. Example: +// +// string str = "A|B||D"; +// string@[]@ array = split(str, "|"); +// +// The resulting array has the following elements: +// +// {"A", "B", "", "D"} +// +// AngelScript signature: +// string@[]@ split(const string &in str, const string &in delim) +void StringSplit_Generic(asIScriptGeneric *gen) +{ + // Obtain a pointer to the engine + asIScriptContext *ctx = asGetActiveContext(); + asIScriptEngine *engine = ctx->GetEngine(); + + // TODO: This should only be done once + int stringArrayType = engine->GetTypeIdByDecl("string@[]"); + + // Create the array object + asIScriptArray *array = (asIScriptArray*)engine->CreateScriptObject(stringArrayType); + + // Get the arguments + CScriptString *str = *(CScriptString**)gen->GetAddressOfArg(0); + CScriptString *delim = *(CScriptString**)gen->GetAddressOfArg(1); + + // Find the existence of the delimiter in the input string + int pos = 0, prev = 0, count = 0; + while( (pos = (int)str->buffer.find(delim->buffer, prev)) != (int)std::string::npos ) + { + // Add the part to the array + CScriptString *part = new CScriptString(); + part->buffer.assign(&str->buffer[prev], pos-prev); + array->Resize(array->GetElementCount()+1); + *(CScriptString**)array->GetElementPointer(count) = part; + + // Find the next part + count++; + prev = pos + (int)delim->buffer.length(); + } + + // Add the remaining part + CScriptString *part = new CScriptString(); + part->buffer.assign(&str->buffer[prev]); + array->Resize(array->GetElementCount()+1); + *(CScriptString**)array->GetElementPointer(count) = part; + + // Return the array by handle + *(asIScriptArray**)gen->GetAddressOfReturnLocation() = array; +} + + + +// This function takes as input an array of string handles as well as a +// delimiter and concatenates the array elements into one delimited string. +// Example: +// +// string@[] array = {"A", "B", "", "D"}; +// string str = join(array, "|"); +// +// The resulting string is: +// +// "A|B||D" +// +// AngelScript signature: +// string@ join(const string@[] &in array, const string &in delim) +void StringJoin_Generic(asIScriptGeneric *gen) +{ + // Get the arguments + asIScriptArray *array = *(asIScriptArray**)gen->GetAddressOfArg(0); + CScriptString *delim = *(CScriptString**)gen->GetAddressOfArg(1); + + // Create the new string + CScriptString *str = new CScriptString(); + int n; + for( n = 0; n < (int)array->GetElementCount() - 1; n++ ) + { + CScriptString *part = *(CScriptString**)array->GetElementPointer(n); + str->buffer += part->buffer; + str->buffer += delim->buffer; + } + + // Add the last part + CScriptString *part = *(CScriptString**)array->GetElementPointer(n); + str->buffer += part->buffer; + + // Return the string + *(CScriptString**)gen->GetAddressOfReturnLocation() = str; +} + + + +// TODO: Implement the following functions +// +// int64 parseInt(const string &in str, int &out bytesParsed); +// double parseDouble(const string &in str, int &out bytesParsed); +// string @ formatString(int64, const string &in format); // should use sprintf to format the string +// string @ formatDouble(double, const string &in format); +// +// int16 byteStringToInt16(const string &in str, int start); +// int32 byteStringToInt32(const string &in str, int start); +// int64 byteStringtoInt64(const string &in str, int start); +// float byteStringToFloat(const string &in str, int start); +// double byteStringToDouble(const string &in str, int start); +// string @ int16ToByteString(int16); +// string @ int32ToByteString(int32); +// string @ int64ToByteString(int64); +// string @ floatToByteString(float); +// string @ doubleToByteString(double); + + + + +// This is where the utility functions are registered. +// The string type must have been registered first. +void RegisterScriptStringUtils(asIScriptEngine *engine) +{ + int r; + + r = engine->RegisterGlobalFunction("string@ substring(const string &in, int, int)", asFUNCTION(StringSubString_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findFirst(const string &in, const string &in)", asFUNCTION(StringFindFirst0_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findFirst(const string &in, const string &in, int)", asFUNCTION(StringFindFirst_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findLast(const string &in, const string &in)", asFUNCTION(StringFindLast0_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findLast(const string &in, const string &in, int)", asFUNCTION(StringFindLast_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findFirstOf(const string &in, const string &in)", asFUNCTION(StringFindFirstOf0_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findFirstOf(const string &in, const string &in, int)", asFUNCTION(StringFindFirstOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findFirstNotOf(const string &in, const string &in)", asFUNCTION(StringFindFirstNotOf0_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findFirstNotOf(const string &in, const string &in, int)", asFUNCTION(StringFindFirstNotOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findLastOf(const string &in, const string &in)", asFUNCTION(StringFindLastOf0_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findLastOf(const string &in, const string &in, int)", asFUNCTION(StringFindLastOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findLastNotOf(const string &in, const string &in)", asFUNCTION(StringFindLastNotOf0_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("int findLastNotOf(const string &in, const string &in, int)", asFUNCTION(StringFindLastNotOf_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string@[]@ split(const string &in, const string &in)", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string@ join(const string@[] &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0); +} diff --git a/AngelScript/source/as_array.h b/AngelScript/source/as_array.h new file mode 100644 index 000000000..463295463 --- /dev/null +++ b/AngelScript/source/as_array.h @@ -0,0 +1,365 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +#ifndef AS_ARRAY_H +#define AS_ARRAY_H + +#if !defined(AS_NO_MEMORY_H) +#include +#endif +#include // some compilers declare memcpy() here + +#ifdef _MSC_VER +#pragma warning(disable:4345) // warning about a change in how the code is handled in this version +#endif + +BEGIN_AS_NAMESPACE + +template class asCArray +{ +public: + asCArray(); + asCArray(const asCArray &); + asCArray(int reserve); + ~asCArray(); + + void Allocate(size_t numElements, bool keepData); + size_t GetCapacity() const; + + void PushLast(const T &element); + T PopLast(); + + void SetLength(size_t numElements); + size_t GetLength() const; + + void Copy(const T*, size_t count); + asCArray &operator =(const asCArray &); + + const T &operator [](size_t index) const; + T &operator [](size_t index); + T *AddressOf(); + + void Concatenate(const asCArray &); + void Concatenate(T*, unsigned int count); + + bool Exists(const T &element); + int IndexOf(const T &element); + void RemoveIndex(size_t index); // Removes the entry without reordering the array + void RemoveValue(const T &element); + + bool operator==(const asCArray &) const; + bool operator!=(const asCArray &) const; + +protected: + T *array; + size_t length; + size_t maxLength; + char buf[8]; +}; + +// Implementation + +template +T *asCArray::AddressOf() +{ + return array; +} + +template +asCArray::asCArray(void) +{ + array = 0; + length = 0; + maxLength = 0; +} + +template +asCArray::asCArray(const asCArray ©) +{ + array = 0; + length = 0; + maxLength = 0; + + *this = copy; +} + +template +asCArray::asCArray(int reserve) +{ + array = 0; + length = 0; + maxLength = 0; + + Allocate(reserve, false); +} + +template +asCArray::~asCArray(void) +{ + // Allocating a zero length array will free all memory + Allocate(0,0); +} + +template +size_t asCArray::GetLength() const +{ + return length; +} + +template +const T &asCArray::operator [](size_t index) const +{ + asASSERT(index < length); + + return array[index]; +} + +template +T &asCArray::operator [](size_t index) +{ + asASSERT(index < length); + + return array[index]; +} + +template +void asCArray::PushLast(const T &element) +{ + if( length == maxLength ) + { + if( maxLength == 0 ) + Allocate(1, false); + else + Allocate(2*maxLength, true); + } + + array[length++] = element; +} + +template +T asCArray::PopLast() +{ + asASSERT(length > 0); + + return array[--length]; +} + +template +void asCArray::Allocate(size_t numElements, bool keepData) +{ + // We have 4 situations + // 1. The previous array is 8 bytes or smaller and the new array is also 8 bytes or smaller + // 2. The previous array is 8 bytes or smaller and the new array is larger than 8 bytes + // 3. The previous array is larger than 8 bytes and the new array is 8 bytes or smaller + // 4. The previous array is larger than 8 bytes and the new array is also larger than 8 bytes + + T *tmp = 0; + if( numElements ) + { + if( sizeof(T)*numElements <= 8 ) + // Use the internal buffer + tmp = (T*)buf; + else + // Allocate the array and construct each of the elements + tmp = asNEWARRAY(T,numElements); + + if( array == tmp ) + { + // Construct only the newly allocated elements + for( size_t n = length; n < numElements; n++ ) + new (&tmp[n]) T(); + } + else + { + // Construct all elements + for( size_t n = 0; n < numElements; n++ ) + new (&tmp[n]) T(); + } + } + + if( array ) + { + size_t oldLength = length; + + if( array == tmp ) + { + if( keepData ) + { + if( length > numElements ) + length = numElements; + } + else + length = 0; + + // Call the destructor for elements that are no longer used + for( size_t n = length; n < oldLength; n++ ) + array[n].~T(); + } + else + { + if( keepData ) + { + if( length > numElements ) + length = numElements; + + for( size_t n = 0; n < length; n++ ) + tmp[n] = array[n]; + } + else + length = 0; + + // Call the destructor for all elements + for( size_t n = 0; n < oldLength; n++ ) + array[n].~T(); + + if( array != (T*)buf ) + asDELETEARRAY(array); + } + } + + array = tmp; + maxLength = numElements; +} + +template +size_t asCArray::GetCapacity() const +{ + return maxLength; +} + +template +void asCArray::SetLength(size_t numElements) +{ + if( numElements > maxLength ) + Allocate(numElements, true); + + length = numElements; +} + +template +void asCArray::Copy(const T *data, size_t count) +{ + if( maxLength < count ) + Allocate(count, false); + + for( size_t n = 0; n < count; n++ ) + array[n] = data[n]; + + length = count; +} + +template +asCArray &asCArray::operator =(const asCArray ©) +{ + Copy(copy.array, copy.length); + + return *this; +} + +template +bool asCArray::operator ==(const asCArray &other) const +{ + if( length != other.length ) return false; + + for( asUINT n = 0; n < length; n++ ) + if( array[n] != other.array[n] ) + return false; + + return true; +} + +template +bool asCArray::operator !=(const asCArray &other) const +{ + return !(*this == other); +} + +template +void asCArray::Concatenate(const asCArray &other) +{ + if( maxLength < length + other.length ) + Allocate(length + other.length, true); + + for( size_t n = 0; n < other.length; n++ ) + array[length+n] = other.array[n]; + + length += other.length; +} + +template +void asCArray::Concatenate(T* array, unsigned int count) +{ + for( unsigned int c = 0; c < count; c++ ) + PushLast(array[c]); +} + +template +bool asCArray::Exists(const T &e) +{ + return IndexOf(e) == -1 ? false : true; +} + +template +int asCArray::IndexOf(const T &e) +{ + for( size_t n = 0; n < length; n++ ) + if( array[n] == e ) return (int)n; + + return -1; +} + +template +void asCArray::RemoveIndex(size_t index) +{ + if( index < length ) + { + for( size_t n = index; n < length-1; n++ ) + array[n] = array[n+1]; + + PopLast(); + } +} + +template +void asCArray::RemoveValue(const T &e) +{ + for( size_t n = 0; n < length; n++ ) + { + if( array[n] == e ) + { + RemoveIndex(n); + break; + } + } +} + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_arrayobject.cpp b/AngelScript/source/as_arrayobject.cpp new file mode 100644 index 000000000..d3a4d84f0 --- /dev/null +++ b/AngelScript/source/as_arrayobject.cpp @@ -0,0 +1,630 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +#include +#include + +#include "as_config.h" +#include "as_arrayobject.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +struct sArrayBuffer +{ + asDWORD numElements; + asBYTE data[1]; +}; + +static asCArrayObject* ArrayObjectFactory2(asIObjectType *ot, asUINT length) +{ + asCArrayObject *a = asNEW(asCArrayObject)(length, ot); + + // It's possible the constructor raised a script exception, in which case we + // need to free the memory and return null instead, else we get a memory leak. + asIScriptContext *ctx = asGetActiveContext(); + if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION ) + { + asDELETE(a, asCArrayObject); + return 0; + } + + return a; +} + +asCArrayObject* ArrayObjectFactory(asIObjectType *ot) +{ + return ArrayObjectFactory2(ot, 0); +} + +#ifndef AS_MAX_PORTABILITY + +static asCArrayObject &ArrayObjectAssignment(asCArrayObject *other, asCArrayObject *self) +{ + return *self = *other; +} + +static void *ArrayObjectAt(asUINT index, asCArrayObject *self) +{ + return self->at(index); +} + +static asUINT ArrayObjectLength(asCArrayObject *self) +{ + return self->GetElementCount(); +} +static void ArrayObjectResize(asUINT size, asCArrayObject *self) +{ + self->Resize(size); +} + +#else + +static void ArrayObjectFactory_Generic(asIScriptGeneric *gen) +{ + asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); + + *(asCArrayObject**)gen->GetAddressOfReturnLocation() = ArrayObjectFactory(ot); +} + +static void ArrayObjectFactory2_Generic(asIScriptGeneric *gen) +{ + asIObjectType *ot = *(asIObjectType**)gen->GetAddressOfArg(0); + asUINT length = gen->GetArgDWord(1); + + *(asCArrayObject**)gen->GetAddressOfReturnLocation() = ArrayObjectFactory2(ot, length); +} + +static void ArrayObjectAssignment_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *other = (asCArrayObject*)gen->GetArgObject(0); + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + + *self = *other; + + gen->SetReturnObject(self); +} + +static void ArrayObjectAt_Generic(asIScriptGeneric *gen) +{ + asUINT index = gen->GetArgDWord(0); + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + + gen->SetReturnAddress(self->at(index)); +} + +static void ArrayObjectLength_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + + gen->SetReturnDWord(self->GetElementCount()); +} + +static void ArrayObjectResize_Generic(asIScriptGeneric *gen) +{ + asUINT size = gen->GetArgDWord(0); + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + + self->Resize(size); +} + +static void ArrayObject_AddRef_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + self->AddRef(); +} + +static void ArrayObject_Release_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + self->Release(); +} + +static void ArrayObject_GetRefCount_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ArrayObject_SetFlag_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + self->SetFlag(); +} + +static void ArrayObject_GetFlag_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); +} + +static void ArrayObject_EnumReferences_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ArrayObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + asCArrayObject *self = (asCArrayObject*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +#endif + +void RegisterArrayObject(asIScriptEngine *engine) +{ + int r; + + r = engine->RegisterObjectType("_builtin_array_", sizeof(asCArrayObject), asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); asASSERT( r >= 0 ); +#ifndef AS_MAX_PORTABILITY + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_FACTORY, "_builtin_array_@ f(int&in)", asFUNCTIONPR(ArrayObjectFactory, (asIObjectType*), asCArrayObject*), asCALL_CDECL); asASSERT( r >= 0 ); + // TODO: initlist: Need a special behaviour for this + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_FACTORY, "_builtin_array_@ f(int&in, uint)", asFUNCTIONPR(ArrayObjectFactory2, (asIObjectType*, asUINT), asCArrayObject*), asCALL_CDECL); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_ADDREF, "void f()", asMETHOD(asCArrayObject,AddRef), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_RELEASE, "void f()", asMETHOD(asCArrayObject,Release), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterObjectMethod("_builtin_array_", "_builtin_array_ &opAssign(const _builtin_array_&in)", asFUNCTION(ArrayObjectAssignment), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_INDEX, "T &f(uint)", asFUNCTION(ArrayObjectAt), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_INDEX, "const T &f(uint) const", asFUNCTION(ArrayObjectAt), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + r = engine->RegisterObjectMethod("_builtin_array_", "uint length() const", asFUNCTION(ArrayObjectLength), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + r = engine->RegisterObjectMethod("_builtin_array_", "void resize(uint)", asFUNCTION(ArrayObjectResize), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCArrayObject,GetRefCount), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCArrayObject,SetFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCArrayObject,GetFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCArrayObject,EnumReferences), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCArrayObject,ReleaseAllHandles), asCALL_THISCALL); asASSERT( r >= 0 ); +#else + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_FACTORY, "_builtin_array_@ f(int&in)", asFUNCTION(ArrayObjectFactory_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + // TODO: initlist: Need a special behaviour for this + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_FACTORY, "_builtin_array_@ f(int&in, uint)", asFUNCTION(ArrayObjectFactory2_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_ADDREF, "void f()", asFUNCTION(ArrayObject_AddRef_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_RELEASE, "void f()", asFUNCTION(ArrayObject_Release_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectMethod("_builtin_array_", "_builtin_array_ &opAssign(const _builtin_array_&in)", asFUNCTION(ArrayObjectAssignment_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_INDEX, "T &f(uint)", asFUNCTION(ArrayObjectAt_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_INDEX, "const T &f(uint) const", asFUNCTION(ArrayObjectAt_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectMethod("_builtin_array_", "uint length() const", asFUNCTION(ArrayObjectLength_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectMethod("_builtin_array_", "void resize(uint)", asFUNCTION(ArrayObjectResize_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ArrayObject_GetRefCount_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ArrayObject_SetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ArrayObject_GetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ArrayObject_EnumReferences_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterObjectBehaviour("_builtin_array_", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ArrayObject_ReleaseAllHandles_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); +#endif +} + +asCArrayObject &asCArrayObject::operator=(asCArrayObject &other) +{ + if( &other != this ) + { + if( buffer ) + { + DeleteBuffer(buffer); + buffer = 0; + } + + // Copy all elements from the other array + CreateBuffer(&buffer, other.buffer->numElements); + CopyBuffer(buffer, other.buffer); + } + + return *this; +} + +int asCArrayObject::CopyFrom(asIScriptArray *other) +{ + if( other == 0 ) return asINVALID_ARG; + + // Verify that the types are equal + if( GetArrayTypeId() != other->GetArrayTypeId() ) + return asINVALID_TYPE; + + *this = *(asCArrayObject*)other; + + return 0; +} + +asCArrayObject::asCArrayObject(asUINT length, asIObjectType *ot) +{ + refCount.set(1); + gcFlag = false; + objType = ot; + objType->AddRef(); + buffer = 0; + + // Determine element size + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + elementSize = sizeof(asPWORD); + } + else + { + elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(typeId); + } + + isArrayOfHandles = typeId & asTYPEID_OBJHANDLE ? true : false; + + // Make sure the array size isn't too large for us to handle + if( !CheckMaxSize(length) ) + { + // Don't continue with the initialization + return; + } + + CreateBuffer(&buffer, length); + + // Notify the GC of the successful creation + if( objType->GetFlags() & asOBJ_GC ) + objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType->GetTypeId()); +} + +asCArrayObject::~asCArrayObject() +{ + if( buffer ) + { + DeleteBuffer(buffer); + buffer = 0; + } + if( objType ) objType->Release(); +} + +asIScriptEngine *asCArrayObject::GetEngine() const +{ + return objType->GetEngine(); +} + +asUINT asCArrayObject::GetElementCount() +{ + return buffer->numElements; +} + +void asCArrayObject::Resize(asUINT numElements) +{ + // Don't do anything if the size is already correct + if( numElements == buffer->numElements ) + return; + + // Make sure the array size isn't too large for us to handle + if( !CheckMaxSize(numElements) ) + { + // Don't resize the array + return; + } + + sArrayBuffer *newBuffer; + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + // Allocate memory for the buffer + newBuffer = (sArrayBuffer*)asNEWARRAY(asBYTE, sizeof(sArrayBuffer)-1+sizeof(void*)*numElements); + newBuffer->numElements = numElements; + + // Copy the elements from the old buffer + int c = numElements > buffer->numElements ? buffer->numElements : numElements; + asDWORD **d = (asDWORD**)newBuffer->data; + asDWORD **s = (asDWORD**)buffer->data; + for( int n = 0; n < c; n++ ) + d[n] = s[n]; + + if( numElements > buffer->numElements ) + { + Construct(newBuffer, buffer->numElements, numElements); + } + else if( numElements < buffer->numElements ) + { + Destruct(buffer, numElements, buffer->numElements); + } + } + else + { + // Allocate memory for the buffer + newBuffer = (sArrayBuffer*)asNEWARRAY(asBYTE, sizeof(sArrayBuffer)-1+elementSize*numElements); + newBuffer->numElements = numElements; + + int c = numElements > buffer->numElements ? buffer->numElements : numElements; + memcpy(newBuffer->data, buffer->data, c*elementSize); + } + + // Release the old buffer + userFree(buffer); + + buffer = newBuffer; +} + +// internal +bool asCArrayObject::CheckMaxSize(asUINT numElements) +{ + // This code makes sure the size of the buffer that is allocated + // for the array doesn't overflow and becomes smaller than requested + + asUINT maxSize = 0xFFFFFFFFul - sizeof(sArrayBuffer) + 1; + if( objType->GetSubTypeId() & asTYPEID_MASK_OBJECT ) + { + maxSize /= sizeof(void*); + } + else + { + maxSize /= elementSize; + } + + if( numElements > maxSize ) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + { + // Set a script exception + ctx->SetException("Too large array size"); + } + + return false; + } + + // OK + return true; +} + +int asCArrayObject::GetArrayTypeId() +{ + return objType->GetTypeId(); +} + +int asCArrayObject::GetElementTypeId() +{ + return objType->GetSubTypeId(); +} + +void *asCArrayObject::GetElementPointer(asUINT index) +{ + if( index >= buffer->numElements ) return 0; + + int typeId = objType->GetSubTypeId(); + if( (typeId & asTYPEID_MASK_OBJECT) && !isArrayOfHandles ) + return (void*)((size_t*)buffer->data)[index]; + else + return buffer->data + elementSize*index; +} + +void *asCArrayObject::at(asUINT index) +{ + if( index >= buffer->numElements ) + { + asIScriptContext *ctx = asGetActiveContext(); + if( ctx ) + ctx->SetException(TXT_OUT_OF_BOUNDS); + return 0; + } + else + { + int typeId = objType->GetSubTypeId(); + if( (typeId & asTYPEID_MASK_OBJECT) && !isArrayOfHandles ) + return (void*)((size_t*)buffer->data)[index]; + else + return buffer->data + elementSize*index; + } +} + +void asCArrayObject::CreateBuffer(sArrayBuffer **buf, asUINT numElements) +{ + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + *buf = (sArrayBuffer*)asNEWARRAY(asBYTE, sizeof(sArrayBuffer)-1+sizeof(void*)*numElements); + (*buf)->numElements = numElements; + } + else + { + *buf = (sArrayBuffer*)asNEWARRAY(asBYTE, sizeof(sArrayBuffer)-1+elementSize*numElements); + (*buf)->numElements = numElements; + } + + Construct(*buf, 0, numElements); +} + +void asCArrayObject::DeleteBuffer(sArrayBuffer *buf) +{ + Destruct(buf, 0, buf->numElements); + + // Free the buffer + asDELETEARRAY(buf); +} + +void asCArrayObject::Construct(sArrayBuffer *buf, asUINT start, asUINT end) +{ + int typeId = objType->GetSubTypeId(); + if( isArrayOfHandles ) + { + // Set all object handles to null + asDWORD *d = (asDWORD*)(buf->data + start * sizeof(void*)); + memset(d, 0, (end-start)*sizeof(void*)); + } + else if( typeId & asTYPEID_MASK_OBJECT ) + { + asDWORD **max = (asDWORD**)(buf->data + end * sizeof(void*)); + asDWORD **d = (asDWORD**)(buf->data + start * sizeof(void*)); + + asIScriptEngine *engine = objType->GetEngine(); + + for( ; d < max; d++ ) + *d = (asDWORD*)engine->CreateScriptObject(typeId); + } +} + +void asCArrayObject::Destruct(sArrayBuffer *buf, asUINT start, asUINT end) +{ + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + asIScriptEngine *engine = objType->GetEngine(); + + asDWORD **max = (asDWORD**)(buf->data + end * sizeof(void*)); + asDWORD **d = (asDWORD**)(buf->data + start * sizeof(void*)); + + for( ; d < max; d++ ) + { + if( *d ) + engine->ReleaseScriptObject(*d, typeId); + } + } +} + + +void asCArrayObject::CopyBuffer(sArrayBuffer *dst, sArrayBuffer *src) +{ + asIScriptEngine *engine = objType->GetEngine(); + if( isArrayOfHandles ) + { + // Copy the references and increase the reference counters + if( dst->numElements > 0 && src->numElements > 0 ) + { + int typeId = objType->GetSubTypeId(); + int count = dst->numElements > src->numElements ? src->numElements : dst->numElements; + + asDWORD **max = (asDWORD**)(dst->data + count * sizeof(void*)); + asDWORD **d = (asDWORD**)dst->data; + asDWORD **s = (asDWORD**)src->data; + + for( ; d < max; d++, s++ ) + { + *d = *s; + if( *d ) + engine->AddRefScriptObject(*d, typeId); + } + } + } + else + { + int typeId = objType->GetSubTypeId(); + + if( dst->numElements > 0 && src->numElements > 0 ) + { + int count = dst->numElements > src->numElements ? src->numElements : dst->numElements; + if( typeId & asTYPEID_MASK_OBJECT ) + { + // Call the assignment operator on all of the objects + asDWORD **max = (asDWORD**)(dst->data + count * sizeof(void*)); + asDWORD **d = (asDWORD**)dst->data; + asDWORD **s = (asDWORD**)src->data; + + for( ; d < max; d++, s++ ) + engine->CopyScriptObject(*d, *s, typeId); + } + else + { + // Primitives are copied byte for byte + memcpy(dst->data, src->data, count*elementSize); + } + } + } +} + +void asCArrayObject::Destruct() +{ + // Call destructor and free the memory + asDELETE(this, asCArrayObject); +} + +void asCArrayObject::EnumReferences(asIScriptEngine *engine) +{ + // If the array is holding handles, then we need to notify the GC of them + int typeId = objType->GetSubTypeId(); + if( typeId & asTYPEID_MASK_OBJECT ) + { + void **d = (void**)buffer->data; + for( asUINT n = 0; n < buffer->numElements; n++ ) + { + if( d[n] ) + engine->GCEnumCallback(d[n]); + } + } +} + +void asCArrayObject::ReleaseAllHandles(asIScriptEngine *engine) +{ + int subTypeId = objType->GetSubTypeId(); + asIObjectType *subType = engine->GetObjectTypeById(subTypeId); + if( subType && subType->GetFlags() & asOBJ_GC ) + { + void **d = (void**)buffer->data; + for( asUINT n = 0; n < buffer->numElements; n++ ) + { + if( d[n] ) + { + engine->ReleaseScriptObject(d[n], subTypeId); + d[n] = 0; + } + } + } +} + + +int asCArrayObject::AddRef() +{ + // Clear the GC flag then increase the counter + gcFlag = false; + return refCount.atomicInc(); +} + +int asCArrayObject::Release() +{ + // Now do the actual releasing (clearing the flag set by GC) + gcFlag = false; + int r = refCount.atomicDec(); + if( r == 0 ) + { + Destruct(); + return 0; + } + + return r; +} + +int asCArrayObject::GetRefCount() +{ + return refCount.get(); +} + +void asCArrayObject::SetFlag() +{ + gcFlag = true; +} + +bool asCArrayObject::GetFlag() +{ + return gcFlag; +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_arrayobject.h b/AngelScript/source/as_arrayobject.h new file mode 100644 index 000000000..53f4e0933 --- /dev/null +++ b/AngelScript/source/as_arrayobject.h @@ -0,0 +1,104 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_arrayobject.h +// +// A class for storing arrays of any object type in the scripts +// + + + +#ifndef AS_ARRAYOBJECT_H +#define AS_ARRAYOBJECT_H + +#include "as_config.h" +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +struct sArrayBuffer; + +class asCArrayObject : public asIScriptArray +{ +public: + asCArrayObject(asUINT length, asIObjectType *ot); + virtual ~asCArrayObject(); + + asIScriptEngine *GetEngine() const; + + int AddRef(); + int Release(); + + int GetArrayTypeId(); + int GetElementTypeId(); + + void Resize(asUINT numElements); + asUINT GetElementCount(); + void *GetElementPointer(asUINT index); + void *at(asUINT index); + asCArrayObject &operator=(asCArrayObject&); + + int CopyFrom(asIScriptArray *other); + + // GC methods + void Destruct(); + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + +protected: + asCAtomic refCount; + bool gcFlag; + asIObjectType *objType; + sArrayBuffer *buffer; + bool isArrayOfHandles; + int elementSize; + + bool CheckMaxSize(asUINT numElements); + + void CreateBuffer(sArrayBuffer **buf, asUINT numElements); + void DeleteBuffer(sArrayBuffer *buf); + void CopyBuffer(sArrayBuffer *dst, sArrayBuffer *src); + + void Construct(sArrayBuffer *buf, asUINT start, asUINT end); + void Destruct(sArrayBuffer *buf, asUINT start, asUINT end); +}; + +void RegisterArrayObject(asIScriptEngine *engine); +asCArrayObject *ArrayObjectFactory(asIObjectType *ot); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_atomic.cpp b/AngelScript/source/as_atomic.cpp new file mode 100644 index 000000000..1fb61e92d --- /dev/null +++ b/AngelScript/source/as_atomic.cpp @@ -0,0 +1,157 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +// +// as_gc.cpp +// +// The implementation of the garbage collector +// + +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +asCAtomic::asCAtomic() +{ + value = 0; +} + +asDWORD asCAtomic::get() const +{ + return value; +} + +void asCAtomic::set(asDWORD val) +{ + value = val; +} + +// +// The following code implements the atomicInc and atomicDec on different platforms +// +#ifdef AS_NO_THREADS + +asDWORD asCAtomic::atomicInc() +{ + return ++value; +} + +asDWORD asCAtomic::atomicDec() +{ + return --value; +} + +#elif defined(AS_NO_ATOMIC) + +asDWORD asCAtomic::atomicInc() +{ + asDWORD v; + ENTERCRITICALSECTION(cs); + v = ++value; + LEAVECRITICALSECTION(cs); + return v; +} + +asDWORD asCAtomic::atomicDec() +{ + asDWORD v; + ENTERCRITICALSECTION(cs); + v = --value; + LEAVECRITICALSECTION(cs); + return v; +} + +#elif defined(AS_WIN) + +END_AS_NAMESPACE +#define WIN32_MEAN_AND_LEAN +#include +BEGIN_AS_NAMESPACE + +asDWORD asCAtomic::atomicInc() +{ + return InterlockedIncrement((LONG*)&value); +} + +asDWORD asCAtomic::atomicDec() +{ + asASSERT(value > 0); + return InterlockedDecrement((LONG*)&value); +} + +#elif defined(AS_LINUX) || defined(AS_BSD) + +// +// atomic_inc_and_test() and atomic_dec_and_test() from asm/atomic.h is not meant +// to be used outside the Linux kernel. Instead we should use the GNUC provided +// __sync_add_and_fetch() and __sync_sub_and_fetch() functions. +// +// Reference: http://golubenco.org/blog/atomic-operations/ +// +// These are only available in GCC 4.1 and above, so for older versions we +// use the critical sections, though it is a lot slower. +// + +asDWORD asCAtomic::atomicInc() +{ + return __sync_add_and_fetch(&value, 1); +} + +asDWORD asCAtomic::atomicDec() +{ + return __sync_sub_and_fetch(&value, 1); +} + +#elif defined(AS_MAC) + +END_AS_NAMESPACE +#include +BEGIN_AS_NAMESPACE + +asDWORD asCAtomic::atomicInc() +{ + return OSAtomicIncrement32((int32_t*)&value); +} + +asDWORD asCAtomic::atomicDec() +{ + return OSAtomicDecrement32((int32_t*)&value); +} + +#else + +// If we get here, then the configuration in as_config.h +// is wrong for the compiler/platform combination. +int ERROR_PleaseFixTheConfig[-1]; + +#endif + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_atomic.h b/AngelScript/source/as_atomic.h new file mode 100644 index 000000000..7242844b7 --- /dev/null +++ b/AngelScript/source/as_atomic.h @@ -0,0 +1,74 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_atomic.h +// +// The asCAtomic class provides methods for performing threadsafe +// operations on a single dword, e.g. reference counting and +// bitfields. +// + + + +#ifndef AS_ATOMIC_H +#define AS_ATOMIC_H + +#include "as_config.h" +#include "as_criticalsection.h" + +BEGIN_AS_NAMESPACE + +class asCAtomic +{ +public: + asCAtomic(); + + asDWORD get() const; + void set(asDWORD val); + + // Increase and return new value + asDWORD atomicInc(); + + // Decrease and return new value + asDWORD atomicDec(); + +protected: + asDWORD value; + +#if !defined(AS_NO_THREADS) && defined(AS_NO_ATOMIC) + DECLARECRITICALSECTION(cs); +#endif +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_builder.cpp b/AngelScript/source/as_builder.cpp new file mode 100644 index 000000000..4a503ac95 --- /dev/null +++ b/AngelScript/source/as_builder.cpp @@ -0,0 +1,2815 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_builder.cpp +// +// This is the class that manages the compilation of the scripts +// + + +#include "as_config.h" +#include "as_builder.h" +#include "as_parser.h" +#include "as_compiler.h" +#include "as_tokendef.h" +#include "as_string_util.h" +#include "as_outputbuffer.h" +#include "as_texts.h" +#include "as_scriptobject.h" + +BEGIN_AS_NAMESPACE + +asCBuilder::asCBuilder(asCScriptEngine *engine, asCModule *module) +{ + this->engine = engine; + this->module = module; +} + +asCBuilder::~asCBuilder() +{ + asUINT n; + + // Free all functions + for( n = 0; n < functions.GetLength(); n++ ) + { + if( functions[n] ) + { + if( functions[n]->node ) + { + functions[n]->node->Destroy(engine); + } + + asDELETE(functions[n],sFunctionDescription); + } + + functions[n] = 0; + } + + // Free all global variables + for( n = 0; n < globVariables.GetLength(); n++ ) + { + if( globVariables[n] ) + { + if( globVariables[n]->nextNode ) + { + globVariables[n]->nextNode->Destroy(engine); + } + + asDELETE(globVariables[n],sGlobalVariableDescription); + globVariables[n] = 0; + } + } + + // Free all the loaded files + for( n = 0; n < scripts.GetLength(); n++ ) + { + if( scripts[n] ) + { + asDELETE(scripts[n],asCScriptCode); + } + + scripts[n] = 0; + } + + // Free all class declarations + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + if( classDeclarations[n] ) + { + if( classDeclarations[n]->node ) + { + classDeclarations[n]->node->Destroy(engine); + } + + asDELETE(classDeclarations[n],sClassDeclaration); + classDeclarations[n] = 0; + } + } + + for( n = 0; n < interfaceDeclarations.GetLength(); n++ ) + { + if( interfaceDeclarations[n] ) + { + if( interfaceDeclarations[n]->node ) + { + interfaceDeclarations[n]->node->Destroy(engine); + } + + asDELETE(interfaceDeclarations[n],sClassDeclaration); + interfaceDeclarations[n] = 0; + } + } + + for( n = 0; n < namedTypeDeclarations.GetLength(); n++ ) + { + if( namedTypeDeclarations[n] ) + { + if( namedTypeDeclarations[n]->node ) + { + namedTypeDeclarations[n]->node->Destroy(engine); + } + + asDELETE(namedTypeDeclarations[n],sClassDeclaration); + namedTypeDeclarations[n] = 0; + } + } +} + +int asCBuilder::AddCode(const char *name, const char *code, int codeLength, int lineOffset, int sectionIdx, bool makeCopy) +{ + asCScriptCode *script = asNEW(asCScriptCode); + script->SetCode(name, code, codeLength, makeCopy); + script->lineOffset = lineOffset; + script->idx = sectionIdx; + scripts.PushLast(script); + + return 0; +} + +int asCBuilder::Build() +{ + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + ParseScripts(); + CompileClasses(); + CompileGlobalVariables(); + CompileFunctions(); + + if( numErrors > 0 ) + return asERROR; + + return asSUCCESS; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 +int asCBuilder::BuildString(const char *string, asCContext *ctx) +{ + asCScriptFunction *execFunc = 0; + int r = CompileFunction(TXT_EXECUTESTRING, string, -1, 0, &execFunc); + if( r >= 0 ) + { + ctx->SetExecuteStringFunction(execFunc); + } + + return r; +} +#endif + +int asCBuilder::CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) +{ + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + // Add the string to the script code + asCScriptCode *script = asNEW(asCScriptCode); + script->SetCode(sectionName, code, true); + script->lineOffset = lineOffset; + scripts.PushLast(script); + + // Parse the string + asCParser parser(this); + if( parser.ParseScript(scripts[0]) < 0 ) + return asERROR; + + asCScriptNode *node = parser.GetScriptNode(); + + // Make sure there is nothing else than the global variable in the script code + if( node == 0 || + node->firstChild == 0 || + node->firstChild != node->lastChild || + node->firstChild->nodeType != snGlobalVar ) + { + WriteError(script->name.AddressOf(), TXT_ONLY_ONE_VARIABLE_ALLOWED, 0, 0); + return asERROR; + } + + node = node->firstChild; + node->DisconnectParent(); + RegisterGlobalVar(node, script); + + CompileGlobalVariables(); + + if( numErrors > 0 ) + { + // Remove the variable from the module, if it was registered + if( globVariables.GetLength() > 0 ) + { + module->RemoveGlobalVar(module->GetGlobalVarCount()-1); + } + + return asERROR; + } + + return 0; +} + +int asCBuilder::CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asCScriptFunction **outFunc) +{ + asASSERT(outFunc != 0); + + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + // Add the string to the script code + asCScriptCode *script = asNEW(asCScriptCode); + script->SetCode(sectionName, code, true); + script->lineOffset = lineOffset; + scripts.PushLast(script); + + // Parse the string + asCParser parser(this); + if( parser.ParseScript(scripts[0]) < 0 ) + return asERROR; + + asCScriptNode *node = parser.GetScriptNode(); + + // Make sure there is nothing else than the function in the script code + if( node == 0 || + node->firstChild == 0 || + node->firstChild != node->lastChild || + node->firstChild->nodeType != snFunction ) + { + WriteError(script->name.AddressOf(), TXT_ONLY_ONE_FUNCTION_ALLOWED, 0, 0); + return asERROR; + } + + // Find the function node + node = node->firstChild; + + // Create the function + bool isConstructor, isDestructor; + asCScriptFunction *func = asNEW(asCScriptFunction)(engine,module,asFUNC_SCRIPT); + GetParsedFunctionDetails(node, scripts[0], 0, func->name, func->returnType, func->parameterTypes, func->inOutFlags, func->isReadOnly, isConstructor, isDestructor); + func->id = engine->GetNextScriptFunctionId(); + func->scriptSectionIdx = engine->GetScriptSectionNameIndex(sectionName ? sectionName : ""); + + // Tell the engine that the function exists already so the compiler can access it + if( compileFlags & asCOMP_ADD_TO_MODULE ) + { + int r = CheckNameConflict(func->name.AddressOf(), node, scripts[0]); + if( r < 0 ) + { + func->Release(); + return asERROR; + } + + module->globalFunctions.PushLast(func); + func->AddRef(); + module->AddScriptFunction(func); + } + else + engine->SetScriptFunction(func); + + // Fill in the function info for the builder too + node->DisconnectParent(); + sFunctionDescription *funcDesc = asNEW(sFunctionDescription); + functions.PushLast(funcDesc); + funcDesc->script = scripts[0]; + funcDesc->node = node; + funcDesc->name = func->name; + funcDesc->funcId = func->id; + + asCCompiler compiler(engine); + if( compiler.CompileFunction(this, functions[0]->script, functions[0]->node, func) >= 0 ) + { + // Return the function + *outFunc = func; + } + else + { + // If the function was added to the module then remove it again + if( compileFlags & asCOMP_ADD_TO_MODULE ) + { + module->globalFunctions.RemoveValue(func); + module->scriptFunctions.RemoveValue(func); + func->Release(); + func->Release(); + } + + func->Release(); + + return asERROR; + } + + return asSUCCESS; +} + +void asCBuilder::ParseScripts() +{ + asCArray parsers((int)scripts.GetLength()); + + // Parse all the files as if they were one + asUINT n = 0; + for( n = 0; n < scripts.GetLength(); n++ ) + { + asCParser *parser = asNEW(asCParser)(this); + parsers.PushLast(parser); + + // Parse the script file + parser->ParseScript(scripts[n]); + } + + if( numErrors == 0 ) + { + // Find all type declarations + for( n = 0; n < scripts.GetLength(); n++ ) + { + asCScriptNode *node = parsers[n]->GetScriptNode(); + + // Find structure definitions first + node = node->firstChild; + while( node ) + { + asCScriptNode *next = node->next; + if( node->nodeType == snClass ) + { + node->DisconnectParent(); + RegisterClass(node, scripts[n]); + } + else if( node->nodeType == snInterface ) + { + node->DisconnectParent(); + RegisterInterface(node, scripts[n]); + } + // Handle enumeration + else if( node->nodeType == snEnum ) + { + node->DisconnectParent(); + RegisterEnum(node, scripts[n]); + } + // Handle typedef + else if( node->nodeType == snTypedef ) + { + node->DisconnectParent(); + RegisterTypedef(node, scripts[n]); + } + + node = next; + } + } + + // Register script methods found in the interfaces + for( n = 0; n < interfaceDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = interfaceDeclarations[n]; + + asCScriptNode *node = decl->node->firstChild->next; + while( node ) + { + asCScriptNode *next = node->next; + if( node->nodeType == snFunction ) + { + node->DisconnectParent(); + RegisterScriptFunction(engine->GetNextScriptFunctionId(), node, decl->script, decl->objType, true); + } + + node = next; + } + } + + // Now the interfaces have been completely established, now we need to determine if + // the same interface has already been registered before, and if so reuse the interface id. + module->ResolveInterfaceIds(); + + // Register script methods found in the structures + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + + asCScriptNode *node = decl->node->firstChild->next; + + // Skip list of classes and interfaces + while( node && node->nodeType == snIdentifier ) + node = node->next; + + while( node ) + { + asCScriptNode *next = node->next; + if( node->nodeType == snFunction ) + { + node->DisconnectParent(); + RegisterScriptFunction(engine->GetNextScriptFunctionId(), node, decl->script, decl->objType); + } + + node = next; + } + + // Make sure the default factory & constructor exists for classes + if( decl->objType->beh.construct == engine->scriptTypeBehaviours.beh.construct ) + { + AddDefaultConstructor(decl->objType, decl->script); + } + } + + // Find other global nodes + for( n = 0; n < scripts.GetLength(); n++ ) + { + // Find other global nodes + asCScriptNode *node = parsers[n]->GetScriptNode(); + node = node->firstChild; + while( node ) + { + asCScriptNode *next = node->next; + node->DisconnectParent(); + + if( node->nodeType == snFunction ) + { + RegisterScriptFunction(engine->GetNextScriptFunctionId(), node, scripts[n], 0, false, true); + } + else if( node->nodeType == snGlobalVar ) + { + RegisterGlobalVar(node, scripts[n]); + } + else if( node->nodeType == snImport ) + { + RegisterImportedFunction(module->GetNextImportedFunctionId(), node, scripts[n]); + } + else + { + // Unused script node + int r, c; + scripts[n]->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteWarning(scripts[n]->name.AddressOf(), TXT_UNUSED_SCRIPT_NODE, r, c); + + node->Destroy(engine); + } + + node = next; + } + } + } + + for( n = 0; n < parsers.GetLength(); n++ ) + { + asDELETE(parsers[n],asCParser); + } +} + +void asCBuilder::CompileFunctions() +{ + // Compile each function + for( asUINT n = 0; n < functions.GetLength(); n++ ) + { + if( functions[n] == 0 ) continue; + + asCCompiler compiler(engine); + asCScriptFunction *func = engine->scriptFunctions[functions[n]->funcId]; + + if( functions[n]->node ) + { + int r, c; + functions[n]->script->ConvertPosToRowCol(functions[n]->node->tokenPos, &r, &c); + + asCString str = func->GetDeclarationStr(); + str.Format(TXT_COMPILING_s, str.AddressOf()); + WriteInfo(functions[n]->script->name.AddressOf(), str.AddressOf(), r, c, true); + + compiler.CompileFunction(this, functions[n]->script, functions[n]->node, func); + + preMessage.isSet = false; + } + else + { + // This is the default constructor, that is generated + // automatically if not implemented by the user. + asASSERT( functions[n]->name == functions[n]->objType->name ); + compiler.CompileDefaultConstructor(this, functions[n]->script, func); + } + } +} + +int asCBuilder::ParseDataType(const char *datatype, asCDataType *result) +{ + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + asCScriptCode source; + source.SetCode("", datatype, true); + + asCParser parser(this); + int r = parser.ParseDataType(&source); + if( r < 0 ) + return asINVALID_TYPE; + + // Get data type and property name + asCScriptNode *dataType = parser.GetScriptNode()->firstChild; + + *result = CreateDataTypeFromNode(dataType, &source, true); + + if( numErrors > 0 ) + return asINVALID_TYPE; + + return asSUCCESS; +} + +int asCBuilder::ParseTemplateDecl(const char *decl, asCString *name, asCString *subtypeName) +{ + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + asCScriptCode source; + source.SetCode("", decl, true); + + asCParser parser(this); + int r = parser.ParseTemplateDecl(&source); + if( r < 0 ) + return asINVALID_TYPE; + + // Get the template name and subtype name + asCScriptNode *node = parser.GetScriptNode()->firstChild; + + name->Assign(&decl[node->tokenPos], node->tokenLength); + node = node->next; + subtypeName->Assign(&decl[node->tokenPos], node->tokenLength); + + // TODO: template: check for name conflicts + + if( numErrors > 0 ) + return asINVALID_DECLARATION; + + return asSUCCESS; +} + +int asCBuilder::VerifyProperty(asCDataType *dt, const char *decl, asCString &name, asCDataType &type) +{ + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + if( dt ) + { + // Verify that the object type exist + if( dt->GetObjectType() == 0 ) + return asINVALID_OBJECT; + } + + // Check property declaration and type + asCScriptCode source; + source.SetCode(TXT_PROPERTY, decl, true); + + asCParser parser(this); + int r = parser.ParsePropertyDeclaration(&source); + if( r < 0 ) + return asINVALID_DECLARATION; + + // Get data type and property name + asCScriptNode *dataType = parser.GetScriptNode()->firstChild; + + asCScriptNode *nameNode = dataType->next; + + type = CreateDataTypeFromNode(dataType, &source); + name.Assign(&decl[nameNode->tokenPos], nameNode->tokenLength); + + // Verify property name + if( dt ) + { + if( CheckNameConflictMember(dt->GetObjectType(), name.AddressOf(), nameNode, &source) < 0 ) + return asNAME_TAKEN; + } + else + { + if( CheckNameConflict(name.AddressOf(), nameNode, &source) < 0 ) + return asNAME_TAKEN; + } + + if( numErrors > 0 ) + return asINVALID_DECLARATION; + + return asSUCCESS; +} + +asCObjectProperty *asCBuilder::GetObjectProperty(asCDataType &obj, const char *prop) +{ + asASSERT(obj.GetObjectType() != 0); + + // TODO: Only search in config groups to which the module has access + // TODO: optimize: Improve linear search + asCArray &props = obj.GetObjectType()->properties; + for( asUINT n = 0; n < props.GetLength(); n++ ) + if( props[n]->name == prop ) + return props[n]; + + return 0; +} + +asCGlobalProperty *asCBuilder::GetGlobalProperty(const char *prop, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue) +{ + asUINT n; + + if( isCompiled ) *isCompiled = true; + if( isPureConstant ) *isPureConstant = false; + + // TODO: optimize: Improve linear search + // Check application registered properties + asCArray *props = &(engine->registeredGlobalProps); + for( n = 0; n < props->GetLength(); ++n ) + if( (*props)[n] && (*props)[n]->name == prop ) + { + if( module ) + { + // Find the config group for the global property + asCConfigGroup *group = engine->FindConfigGroupForGlobalVar((*props)[n]->id); + if( !group || group->HasModuleAccess(module->name.AddressOf()) ) + return (*props)[n]; + } + else + { + // We're not compiling a module right now, so it must be a registered global property + return (*props)[n]; + } + } + + // TODO: optimize: Improve linear search + // Check properties being compiled now + asCArray *gvars = &globVariables; + for( n = 0; n < gvars->GetLength(); ++n ) + { + if( (*gvars)[n] && (*gvars)[n]->name == prop ) + { + if( isCompiled ) *isCompiled = (*gvars)[n]->isCompiled; + + if( isPureConstant ) *isPureConstant = (*gvars)[n]->isPureConstant; + if( constantValue ) *constantValue = (*gvars)[n]->constantValue; + + return (*gvars)[n]->property; + } + } + + // TODO: optimize: Improve linear search + // Check previously compiled global variables + if( module ) + { + props = &module->scriptGlobals; + for( n = 0; n < props->GetLength(); ++n ) + if( (*props)[n]->name == prop ) + return (*props)[n]; + } + + return 0; +} + +int asCBuilder::ParseFunctionDeclaration(asCObjectType *objType, const char *decl, asCScriptFunction *func, bool isSystemFunction, asCArray *paramAutoHandles, bool *returnAutoHandle) +{ + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + asCScriptCode source; + source.SetCode(TXT_SYSTEM_FUNCTION, decl, true); + + asCParser parser(this); + int r = parser.ParseFunctionDefinition(&source); + if( r < 0 ) + return asINVALID_DECLARATION; + + asCScriptNode *node = parser.GetScriptNode(); + + // Find name + asCScriptNode *n = node->firstChild->next->next; + func->name.Assign(&source.code[n->tokenPos], n->tokenLength); + + // Initialize a script function object for registration + bool autoHandle; + + // Scoped reference types are allowed to use handle when returned from application functions + func->returnType = CreateDataTypeFromNode(node->firstChild, &source, true, objType); + func->returnType = ModifyDataTypeFromNode(func->returnType, node->firstChild->next, &source, 0, &autoHandle); + if( autoHandle && (!func->returnType.IsObjectHandle() || func->returnType.IsReference()) ) + return asINVALID_DECLARATION; + if( returnAutoHandle ) *returnAutoHandle = autoHandle; + + // Reference types cannot be returned by value from system functions + if( isSystemFunction && + (func->returnType.GetObjectType() && + (func->returnType.GetObjectType()->flags & asOBJ_REF)) && + !(func->returnType.IsReference() || + func->returnType.IsObjectHandle()) ) + return asINVALID_DECLARATION; + + // Count number of parameters + int paramCount = 0; + n = n->next->firstChild; + while( n ) + { + paramCount++; + n = n->next->next; + if( n && n->nodeType == snIdentifier ) + n = n->next; + } + + // Preallocate memory + func->parameterTypes.Allocate(paramCount, false); + func->inOutFlags.Allocate(paramCount, false); + if( paramAutoHandles ) paramAutoHandles->Allocate(paramCount, false); + + n = node->firstChild->next->next->next->firstChild; + while( n ) + { + asETypeModifiers inOutFlags; + asCDataType type = CreateDataTypeFromNode(n, &source, false, objType); + type = ModifyDataTypeFromNode(type, n->next, &source, &inOutFlags, &autoHandle); + + // Reference types cannot be passed by value to system functions + if( isSystemFunction && + (type.GetObjectType() && + (type.GetObjectType()->flags & asOBJ_REF)) && + !(type.IsReference() || + type.IsObjectHandle()) ) + return asINVALID_DECLARATION; + + // Store the parameter type + func->parameterTypes.PushLast(type); + func->inOutFlags.PushLast(inOutFlags); + + // Don't permit void parameters + if( type.GetTokenType() == ttVoid ) + return asINVALID_DECLARATION; + + if( autoHandle && (!type.IsObjectHandle() || type.IsReference()) ) + return asINVALID_DECLARATION; + + if( paramAutoHandles ) paramAutoHandles->PushLast(autoHandle); + + // Make sure that var type parameters are references + if( type.GetTokenType() == ttQuestion && + !type.IsReference() ) + return asINVALID_DECLARATION; + + // Move to next parameter + n = n->next->next; + if( n && n->nodeType == snIdentifier ) + n = n->next; + } + + // Set the read-only flag if const is declared after parameter list + if( node->lastChild->nodeType == snUndefined && node->lastChild->tokenType == ttConst ) + func->isReadOnly = true; + else + func->isReadOnly = false; + + if( numErrors > 0 || numWarnings > 0 ) + return asINVALID_DECLARATION; + + return 0; +} + +int asCBuilder::ParseVariableDeclaration(const char *decl, asCObjectProperty *var) +{ + numErrors = 0; + numWarnings = 0; + preMessage.isSet = false; + + asCScriptCode source; + source.SetCode(TXT_VARIABLE_DECL, decl, true); + + asCParser parser(this); + + int r = parser.ParsePropertyDeclaration(&source); + if( r < 0 ) + return asINVALID_DECLARATION; + + asCScriptNode *node = parser.GetScriptNode(); + + // Find name + asCScriptNode *n = node->firstChild->next; + var->name.Assign(&source.code[n->tokenPos], n->tokenLength); + + // Initialize a script variable object for registration + var->type = CreateDataTypeFromNode(node->firstChild, &source); + + if( numErrors > 0 || numWarnings > 0 ) + return asINVALID_DECLARATION; + + return 0; +} + +int asCBuilder::CheckNameConflictMember(asCObjectType *t, const char *name, asCScriptNode *node, asCScriptCode *code) +{ + // It's not necessary to check against object types + + // TODO: optimize: Improve linear search + asCArray &props = t->properties; + for( asUINT n = 0; n < props.GetLength(); n++ ) + { + if( props[n]->name == name ) + { + if( code ) + { + int r, c; + code->ConvertPosToRowCol(node->tokenPos, &r, &c); + + asCString str; + str.Format(TXT_NAME_CONFLICT_s_OBJ_PROPERTY, name); + WriteError(code->name.AddressOf(), str.AddressOf(), r, c); + } + + return -1; + } + } + + // TODO: Property names must be checked against method names + + return 0; +} + +int asCBuilder::CheckNameConflict(const char *name, asCScriptNode *node, asCScriptCode *code) +{ + // TODO: Must verify object types in all config groups, whether the module has access or not + // Check against object types + if( engine->GetObjectType(name) != 0 ) + { + if( code ) + { + int r, c; + code->ConvertPosToRowCol(node->tokenPos, &r, &c); + + asCString str; + str.Format(TXT_NAME_CONFLICT_s_EXTENDED_TYPE, name); + WriteError(code->name.AddressOf(), str.AddressOf(), r, c); + } + + return -1; + } + + // TODO: Must verify global properties in all config groups, whether the module has access or not + // Check against global properties + asCGlobalProperty *prop = GetGlobalProperty(name, 0, 0, 0); + if( prop ) + { + if( code ) + { + int r, c; + code->ConvertPosToRowCol(node->tokenPos, &r, &c); + + asCString str; + str.Format(TXT_NAME_CONFLICT_s_GLOBAL_PROPERTY, name); + + WriteError(code->name.AddressOf(), str.AddressOf(), r, c); + } + + return -1; + } + + // TODO: Property names must be checked against function names + + // Check against class types + asUINT n; + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + if( classDeclarations[n]->name == name ) + { + if( code ) + { + int r, c; + code->ConvertPosToRowCol(node->tokenPos, &r, &c); + + asCString str; + str.Format(TXT_NAME_CONFLICT_s_STRUCT, name); + + WriteError(code->name.AddressOf(), str.AddressOf(), r, c); + } + + return -1; + } + } + + // Check against named types + for( n = 0; n < namedTypeDeclarations.GetLength(); n++ ) + { + if( namedTypeDeclarations[n]->name == name ) + { + if( code ) + { + int r, c; + code->ConvertPosToRowCol(node->tokenPos, &r, &c); + + asCString str; + + str.Format(TXT_NAME_CONFLICT_s_IS_NAMED_TYPE, name); + + WriteError(code->name.AddressOf(), str.AddressOf(), r, c); + } + + return -1; + } + } + + return 0; +} + + +int asCBuilder::RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file) +{ + // What data type is it? + asCDataType type = CreateDataTypeFromNode(node->firstChild, file); + + if( !type.CanBeInstanciated() ) + { + asCString str; + // TODO: Change to "'type' cannot be declared as variable" + str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf()); + + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteError(file->name.AddressOf(), str.AddressOf(), r, c); + } + + asCScriptNode *n = node->firstChild->next; + + while( n ) + { + // Verify that the name isn't taken + asCString name(&file->code[n->tokenPos], n->tokenLength); + CheckNameConflict(name.AddressOf(), n, file); + + // Register the global variable + sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription); + globVariables.PushLast(gvar); + + gvar->script = file; + gvar->name = name; + gvar->isCompiled = false; + gvar->datatype = type; + gvar->isEnumValue = false; + + // TODO: Give error message if wrong + asASSERT(!gvar->datatype.IsReference()); + + gvar->idNode = n; + gvar->nextNode = 0; + if( n->next && + (n->next->nodeType == snAssignment || + n->next->nodeType == snArgList || + n->next->nodeType == snInitList ) ) + { + gvar->nextNode = n->next; + n->next->DisconnectParent(); + } + + gvar->property = module->AllocateGlobalProperty(name.AddressOf(), gvar->datatype); + gvar->index = gvar->property->id; + + n = n->next; + } + + node->Destroy(engine); + + return 0; +} + +int asCBuilder::RegisterClass(asCScriptNode *node, asCScriptCode *file) +{ + asCScriptNode *n = node->firstChild; + asCString name(&file->code[n->tokenPos], n->tokenLength); + + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + CheckNameConflict(name.AddressOf(), n, file); + + sClassDeclaration *decl = asNEW(sClassDeclaration); + classDeclarations.PushLast(decl); + decl->name = name; + decl->script = file; + decl->validState = 0; + decl->node = node; + + asCObjectType *st = asNEW(asCObjectType)(engine); + st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT; + + if( node->tokenType == ttHandle ) + st->flags |= asOBJ_IMPLICIT_HANDLE; + + st->size = sizeof(asCScriptObject); + st->name = name; + module->classTypes.PushLast(st); + engine->classTypes.PushLast(st); + st->AddRef(); + decl->objType = st; + + // Add script classes to the GC + engine->gc.AddScriptObjectToGC(st, &engine->objectTypeBehaviours); + + // Use the default script class behaviours + st->beh = engine->scriptTypeBehaviours.beh; + + // TODO: Move this to asCObjectType so that the asCRestore can reuse it + engine->scriptFunctions[st->beh.addref]->AddRef(); + engine->scriptFunctions[st->beh.release]->AddRef(); + engine->scriptFunctions[st->beh.gcEnumReferences]->AddRef(); + engine->scriptFunctions[st->beh.gcGetFlag]->AddRef(); + engine->scriptFunctions[st->beh.gcGetRefCount]->AddRef(); + engine->scriptFunctions[st->beh.gcReleaseAllReferences]->AddRef(); + engine->scriptFunctions[st->beh.gcSetFlag]->AddRef(); + engine->scriptFunctions[st->beh.copy]->AddRef(); + engine->scriptFunctions[st->beh.factory]->AddRef(); + engine->scriptFunctions[st->beh.construct]->AddRef(); + for( asUINT i = 1; i < st->beh.operators.GetLength(); i += 2 ) + engine->scriptFunctions[st->beh.operators[i]]->AddRef(); + + + return 0; +} + +int asCBuilder::RegisterInterface(asCScriptNode *node, asCScriptCode *file) +{ + asCScriptNode *n = node->firstChild; + asCString name(&file->code[n->tokenPos], n->tokenLength); + + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + CheckNameConflict(name.AddressOf(), n, file); + + sClassDeclaration *decl = asNEW(sClassDeclaration); + interfaceDeclarations.PushLast(decl); + decl->name = name; + decl->script = file; + decl->validState = 0; + decl->node = node; + + // Register the object type for the interface + asCObjectType *st = asNEW(asCObjectType)(engine); + st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT; + st->size = 0; // Cannot be instanciated + st->name = name; + module->classTypes.PushLast(st); + engine->classTypes.PushLast(st); + st->AddRef(); + decl->objType = st; + + // Use the default script class behaviours + st->beh.construct = 0; + st->beh.addref = engine->scriptTypeBehaviours.beh.addref; + engine->scriptFunctions[st->beh.addref]->AddRef(); + st->beh.release = engine->scriptTypeBehaviours.beh.release; + engine->scriptFunctions[st->beh.release]->AddRef(); + st->beh.copy = 0; + + return 0; +} + + +void asCBuilder::CompileGlobalVariables() +{ + asUINT n; + + bool compileSucceeded = true; + + // Store state of compilation (errors, warning, output) + int currNumErrors = numErrors; + int currNumWarnings = numWarnings; + + // Backup the original message stream + bool msgCallback = engine->msgCallback; + asSSystemFunctionInterface msgCallbackFunc = engine->msgCallbackFunc; + void *msgCallbackObj = engine->msgCallbackObj; + + // Set the new temporary message stream + asCOutputBuffer outBuffer; + engine->SetMessageCallback(asMETHOD(asCOutputBuffer, Callback), &outBuffer, asCALL_THISCALL); + + asCOutputBuffer finalOutput; + asCScriptFunction *initFunc = 0; + + asCArray initOrder; + + // We first try to compile all the primitive global variables, and only after that + // compile the non-primitive global variables. This permits the constructors + // for the complex types to use the already initialized variables of primitive + // type. Note, we currently don't know which global variables are used in the + // constructors, so we cannot guarantee that variables of complex types are + // initialized in the correct order, so we won't reorder those. + bool compilingPrimitives = true; + + // Compile each global variable + while( compileSucceeded ) + { + compileSucceeded = false; + + int accumErrors = 0; + int accumWarnings = 0; + + // Restore state of compilation + finalOutput.Clear(); + for( asUINT n = 0; n < globVariables.GetLength(); n++ ) + { + asCByteCode init(engine); + numWarnings = 0; + numErrors = 0; + outBuffer.Clear(); + + sGlobalVariableDescription *gvar = globVariables[n]; + if( gvar->isCompiled ) + continue; + + // Skip this for now if we're not compiling complex types yet + if( compilingPrimitives && !gvar->datatype.IsPrimitive() ) + continue; + + if( gvar->nextNode ) + { + int r, c; + gvar->script->ConvertPosToRowCol(gvar->nextNode->tokenPos, &r, &c); + asCString str = gvar->datatype.Format(); + str += " " + gvar->name; + str.Format(TXT_COMPILING_s, str.AddressOf()); + WriteInfo(gvar->script->name.AddressOf(), str.AddressOf(), r, c, true); + } + + if( gvar->isEnumValue ) + { + int r; + if( gvar->nextNode ) + { + asCCompiler comp(engine); + asCScriptFunction func(engine, module, -1); + + // Temporarily switch the type of the variable to int so it can be compiled properly + asCDataType saveType; + saveType = gvar->datatype; + gvar->datatype = asCDataType::CreatePrimitive(ttInt, true); + r = comp.CompileGlobalVariable(this, gvar->script, gvar->nextNode, gvar, &func); + gvar->datatype = saveType; + } + else + { + r = 0; + + // When there is no assignment the value is the last + 1 + int enumVal = 0; + if( n > 0 ) + { + sGlobalVariableDescription *gvar2 = globVariables[n-1]; + if( gvar2->datatype == gvar->datatype ) + { + // The integer value is stored in the lower bytes + enumVal = (*(int*)&gvar2->constantValue) + 1; + + if( !gvar2->isCompiled ) + { + // TODO: Need to get the correct script position + int row, col; + gvar->script->ConvertPosToRowCol(0, &row, &col); + + asCString str = gvar->datatype.Format(); + str += " " + gvar->name; + str.Format(TXT_COMPILING_s, str.AddressOf()); + WriteInfo(gvar->script->name.AddressOf(), str.AddressOf(), row, col, true); + + str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, gvar2->name.AddressOf()); + WriteError(gvar->script->name.AddressOf(), str.AddressOf(), row, col); + r = -1; + } + } + } + + // The integer value is stored in the lower bytes + *(int*)&gvar->constantValue = enumVal; + } + + if( r >= 0 ) + { + // Set the value as compiled + gvar->isCompiled = true; + compileSucceeded = true; + } + } + else + { + // Compile the global variable + initFunc = asNEW(asCScriptFunction)(engine, module, -1); + asCCompiler comp(engine); + int r = comp.CompileGlobalVariable(this, gvar->script, gvar->nextNode, gvar, initFunc); + if( r >= 0 ) + { + // Compilation succeeded + gvar->isCompiled = true; + compileSucceeded = true; + } + else + { + // Compilation failed + asDELETE(initFunc, asCScriptFunction); + initFunc = 0; + } + } + + if( gvar->isCompiled ) + { + // Add warnings for this constant to the total build + if( numWarnings ) + { + currNumWarnings += numWarnings; + if( msgCallback ) + outBuffer.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj); + } + + // Determine order of variable initializations + if( gvar->property && !gvar->isEnumValue ) + initOrder.PushLast(gvar->property); + + // Does the function contain more than just a RET instruction? + if( initFunc && initFunc->byteCode.GetLength() > 1 ) + { + // Create the init function for this variable + initFunc->id = engine->GetNextScriptFunctionId(); + engine->SetScriptFunction(initFunc); + + // Finalize the init function for this variable + initFunc->funcType = asFUNC_SCRIPT; + initFunc->returnType = asCDataType::CreatePrimitive(ttVoid, false); + + // Notify the GC of the new script function + engine->gc.AddScriptObjectToGC(initFunc, &engine->functionBehaviours); + + // The function's refCount was already initialized to 1 + gvar->property->initFunc = initFunc; + initFunc = 0; + } + else if( initFunc ) + { + // Destroy the function as it won't be used + asDELETE(initFunc, asCScriptFunction); + initFunc = 0; + } + } + else + { + // Add output to final output + finalOutput.Append(outBuffer); + accumErrors += numErrors; + accumWarnings += numWarnings; + } + + preMessage.isSet = false; + } + + if( !compileSucceeded ) + { + if( compilingPrimitives ) + { + // No more primitives could be compiled, so + // switch to compiling the complex variables + compilingPrimitives = false; + compileSucceeded = true; + } + else + { + // No more variables can be compiled + // Add errors and warnings to total build + currNumWarnings += accumWarnings; + currNumErrors += accumErrors; + if( msgCallback ) + finalOutput.SendToCallback(engine, &msgCallbackFunc, msgCallbackObj); + } + } + } + + // Restore states + engine->msgCallback = msgCallback; + engine->msgCallbackFunc = msgCallbackFunc; + engine->msgCallbackObj = msgCallbackObj; + + numWarnings = currNumWarnings; + numErrors = currNumErrors; + + // Set the correct order of initialization + if( numErrors == 0 ) + { + // If the length of the arrays are not the same, then this is the compilation + // of a single variable, in which case the initialization order of the previous + // variables must be preserved. + if( module->scriptGlobals.GetLength() == initOrder.GetLength() ) + module->scriptGlobals = initOrder; + } + + // Convert all variables compiled for the enums to true enum values + for( n = 0; n < globVariables.GetLength(); n++ ) + { + asCObjectType *objectType; + sGlobalVariableDescription *gvar = globVariables[n]; + if( !gvar->isEnumValue ) + continue; + + objectType = gvar->datatype.GetObjectType(); + asASSERT(NULL != objectType); + + asSEnumValue *e = asNEW(asSEnumValue); + e->name = gvar->name; + e->value = *(int*)&gvar->constantValue; + + objectType->enumValues.PushLast(e); + + // Destroy the gvar property + if( gvar->nextNode ) + gvar->nextNode->Destroy(engine); + if( gvar->property ) + asDELETE(gvar->property, asCGlobalProperty); + + asDELETE(gvar, sGlobalVariableDescription); + globVariables[n] = 0; + } +} + +void asCBuilder::CompileClasses() +{ + asUINT n; + asCArray toValidate((int)classDeclarations.GetLength()); + + // Determine class inheritances and interfaces + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + asCScriptCode *file = decl->script; + + // Find the base class that this class inherits from + bool multipleInheritance = false; + asCScriptNode *node = decl->node->firstChild->next; + while( node && node->nodeType == snIdentifier ) + { + // Get the interface name from the node + asCString name(&file->code[node->tokenPos], node->tokenLength); + + // Find the object type for the interface + asCObjectType *objType = GetObjectType(name.AddressOf()); + + if( objType == 0 ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + asCString str; + str.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, name.AddressOf()); + WriteError(file->name.AddressOf(), str.AddressOf(), r, c); + } + else if( !(objType->flags & asOBJ_SCRIPT_OBJECT) ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + asCString str; + str.Format(TXT_CANNOT_INHERIT_FROM_s, objType->name.AddressOf()); + WriteError(file->name.AddressOf(), str.AddressOf(), r, c); + } + else if( objType->size != 0 ) + { + // The class inherits from another script class + if( decl->objType->derivedFrom != 0 ) + { + if( !multipleInheritance ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), TXT_CANNOT_INHERIT_FROM_MULTIPLE_CLASSES, r, c); + multipleInheritance = true; + } + } + else + { + // Make sure none of the base classes inherit from this one + asCObjectType *base = objType; + bool error = false; + while( base != 0 ) + { + if( base == decl->objType ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), TXT_CANNOT_INHERIT_FROM_SELF, r, c); + error = true; + break; + } + + base = base->derivedFrom; + } + + if( !error ) + { + decl->objType->derivedFrom = objType; + objType->AddRef(); + } + } + } + else + { + // The class implements an interface + if( decl->objType->Implements(objType) ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + asCString msg; + msg.Format(TXT_INTERFACE_s_ALREADY_IMPLEMENTED, objType->GetName()); + WriteWarning(file->name.AddressOf(), msg.AddressOf(), r, c); + } + else + { + decl->objType->interfaces.PushLast(objType); + + // Make sure all the methods of the interface are implemented + for( asUINT i = 0; i < objType->methods.GetLength(); i++ ) + { + if( !DoesMethodExist(decl->objType, objType->methods[i]) ) + { + int r, c; + file->ConvertPosToRowCol(decl->node->tokenPos, &r, &c); + asCString str; + str.Format(TXT_MISSING_IMPLEMENTATION_OF_s, + engine->GetFunctionDeclaration(objType->methods[i]).AddressOf()); + WriteError(file->name.AddressOf(), str.AddressOf(), r, c); + } + } + } + } + + node = node->next; + } + } + + // Order class declarations so that base classes are compiled before derived classes. + // This will allow the derived classes to copy properties and methods in the next step. + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + asCObjectType *derived = decl->objType; + asCObjectType *base = derived->derivedFrom; + + if( base == 0 ) continue; + + // If the base class is found after the derived class, then move the derived class to the end of the list + for( asUINT m = n+1; m < classDeclarations.GetLength(); m++ ) + { + sClassDeclaration *declBase = classDeclarations[m]; + if( base == declBase->objType ) + { + classDeclarations.RemoveIndex(n); + classDeclarations.PushLast(decl); + + // Decrease index so that we don't skip an entry + n--; + break; + } + } + } + + // Go through each of the classes and register the object type descriptions + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + + // Add all properties and methods from the base class + if( decl->objType->derivedFrom ) + { + asCObjectType *baseType = decl->objType->derivedFrom; + + // The derived class inherits all interfaces from the base class + for( unsigned int n = 0; n < baseType->interfaces.GetLength(); n++ ) + { + if( !decl->objType->Implements(baseType->interfaces[n]) ) + { + decl->objType->interfaces.PushLast(baseType->interfaces[n]); + } + else + { + // Warn if derived class already implements the interface + int r, c; + decl->script->ConvertPosToRowCol(decl->node->tokenPos, &r, &c); + asCString msg; + msg.Format(TXT_INTERFACE_s_ALREADY_IMPLEMENTED, baseType->interfaces[n]->GetName()); + WriteWarning(decl->script->name.AddressOf(), msg.AddressOf(), r, c); + } + } + + // TODO: Need to check for name conflict with new class methods + + // Copy properties from base class to derived class + for( asUINT p = 0; p < baseType->properties.GetLength(); p++ ) + { + asCObjectProperty *prop = AddPropertyToClass(decl, baseType->properties[p]->name, baseType->properties[p]->type); + + // The properties must maintain the same offset + asASSERT(prop->byteOffset == baseType->properties[p]->byteOffset); UNUSED_VAR(prop); + } + + // Copy methods from base class to derived class + for( asUINT m = 0; m < baseType->methods.GetLength(); m++ ) + { + // If the derived class implements the same method, then don't add the base class' method + asCScriptFunction *baseFunc = GetFunctionDescription(baseType->methods[m]); + asCScriptFunction *derivedFunc = 0; + bool found = false; + for( asUINT d = 0; d < decl->objType->methods.GetLength(); d++ ) + { + derivedFunc = GetFunctionDescription(decl->objType->methods[d]); + if( derivedFunc->IsSignatureEqual(baseFunc) ) + { + // Move the function from the methods array to the virtualFunctionTable + decl->objType->methods.RemoveIndex(d); + decl->objType->virtualFunctionTable.PushLast(derivedFunc); + found = true; + break; + } + } + + if( !found ) + { + // Push the base class function on the virtual function table + decl->objType->virtualFunctionTable.PushLast(baseType->virtualFunctionTable[m]); + baseType->virtualFunctionTable[m]->AddRef(); + } + + decl->objType->methods.PushLast(baseType->methods[m]); + engine->scriptFunctions[baseType->methods[m]]->AddRef(); + } + } + + // Move this class' methods into the virtual function table + for( asUINT m = 0; m < decl->objType->methods.GetLength(); m++ ) + { + asCScriptFunction *func = GetFunctionDescription(decl->objType->methods[m]); + if( func->funcType != asFUNC_VIRTUAL ) + { + // Move the reference from the method list to the virtual function list + decl->objType->methods.RemoveIndex(m); + decl->objType->virtualFunctionTable.PushLast(func); + + // Substitute the function description in the method list for a virtual method + // Make sure the methods are in the same order as the virtual function table + decl->objType->methods.PushLast(CreateVirtualFunction(func, (int)decl->objType->virtualFunctionTable.GetLength() - 1)); + m--; + } + } + + // Enumerate each of the declared properties + asCScriptNode *node = decl->node->firstChild->next; + + // Skip list of classes and interfaces + while( node && node->nodeType == snIdentifier ) + node = node->next; + + while( node ) + { + if( node->nodeType == snDeclaration ) + { + asCScriptCode *file = decl->script; + asCDataType dt = CreateDataTypeFromNode(node->firstChild, file); + asCString name(&file->code[node->lastChild->tokenPos], node->lastChild->tokenLength); + + if( dt.IsReadOnly() ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteError(file->name.AddressOf(), TXT_PROPERTY_CANT_BE_CONST, r, c); + } + + CheckNameConflictMember(decl->objType, name.AddressOf(), node->lastChild, file); + + AddPropertyToClass(decl, name, dt, file, node); + } + else + asASSERT(false); + + node = node->next; + } + + toValidate.PushLast(decl); + } + + // Verify that the declared structures are valid, e.g. that the structure + // doesn't contain a member of its own type directly or indirectly + while( toValidate.GetLength() > 0 ) + { + asUINT numClasses = (asUINT)toValidate.GetLength(); + + asCArray toValidateNext((int)toValidate.GetLength()); + while( toValidate.GetLength() > 0 ) + { + sClassDeclaration *decl = toValidate[toValidate.GetLength()-1]; + int validState = 1; + for( asUINT n = 0; n < decl->objType->properties.GetLength(); n++ ) + { + // A valid structure is one that uses only primitives or other valid objects + asCObjectProperty *prop = decl->objType->properties[n]; + asCDataType dt = prop->type; + + if( dt.IsTemplate() ) + { + asCDataType sub = dt; + while( sub.IsTemplate() && !sub.IsObjectHandle() ) + sub = sub.GetSubType(); + + dt = sub; + } + + if( dt.IsObject() && !dt.IsObjectHandle() ) + { + // Find the class declaration + sClassDeclaration *pdecl = 0; + for( asUINT p = 0; p < classDeclarations.GetLength(); p++ ) + { + if( classDeclarations[p]->objType == dt.GetObjectType() ) + { + pdecl = classDeclarations[p]; + break; + } + } + + if( pdecl ) + { + if( pdecl->objType == decl->objType ) + { + int r, c; + decl->script->ConvertPosToRowCol(decl->node->tokenPos, &r, &c); + WriteError(decl->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c); + validState = 2; + break; + } + else if( pdecl->validState != 1 ) + { + validState = pdecl->validState; + break; + } + } + } + } + + if( validState == 1 ) + { + decl->validState = 1; + toValidate.PopLast(); + } + else if( validState == 2 ) + { + decl->validState = 2; + toValidate.PopLast(); + } + else + { + toValidateNext.PushLast(toValidate.PopLast()); + } + } + + toValidate = toValidateNext; + toValidateNext.SetLength(0); + + if( numClasses == toValidate.GetLength() ) + { + int r, c; + toValidate[0]->script->ConvertPosToRowCol(toValidate[0]->node->tokenPos, &r, &c); + WriteError(toValidate[0]->script->name.AddressOf(), TXT_ILLEGAL_MEMBER_TYPE, r, c); + break; + } + } + + if( numErrors > 0 ) return; + + // TODO: The declarations form a graph, all circles in + // the graph must be flagged as potential circles + + // Verify potential circular references + for( n = 0; n < classDeclarations.GetLength(); n++ ) + { + sClassDeclaration *decl = classDeclarations[n]; + asCObjectType *ot = decl->objType; + + // Is there some path in which this structure is involved in circular references? + for( asUINT p = 0; p < ot->properties.GetLength(); p++ ) + { + asCDataType dt = ot->properties[p]->type; + if( dt.IsObject() ) + { + if( dt.IsObjectHandle() ) + { + // TODO: Can this handle really generate a circular reference? + // Only if the handle is of a type that can reference this type, either directly or indirectly + + ot->flags |= asOBJ_GC; + } + else if( dt.GetObjectType()->flags & asOBJ_GC ) + { + // TODO: Just because the member type is a potential circle doesn't mean that this one is + // Only if the object is of a type that can reference this type, either directly or indirectly + + ot->flags |= asOBJ_GC; + } + + // TODO: array: The template type should define if the instanciation can form circles or not + if( dt.IsArrayType() ) + { + asCDataType sub = dt.GetSubType(); + while( sub.IsObject() ) + { + if( sub.IsObjectHandle() || (sub.GetObjectType()->flags & asOBJ_GC) ) + { + decl->objType->flags |= asOBJ_GC; + + // Make sure the array object is also marked as potential circle + sub = dt; + while( sub.IsTemplate() ) + { + sub.GetObjectType()->flags |= asOBJ_GC; + sub = sub.GetSubType(); + } + + break; + } + + if( sub.IsTemplate() ) + sub = sub.GetSubType(); + else + break; + } + } + } + } + } +} + +int asCBuilder::CreateVirtualFunction(asCScriptFunction *func, int idx) +{ + asCScriptFunction *vf = asNEW(asCScriptFunction)(engine, module, asFUNC_VIRTUAL); + + vf->funcType = asFUNC_VIRTUAL; + vf->name = func->name; + vf->returnType = func->returnType; + vf->parameterTypes = func->parameterTypes; + vf->inOutFlags = func->inOutFlags; + vf->id = engine->GetNextScriptFunctionId(); + vf->scriptSectionIdx = func->scriptSectionIdx; + vf->isReadOnly = func->isReadOnly; + vf->objectType = func->objectType; + vf->signatureId = func->signatureId; + vf->vfTableIdx = idx; + + module->AddScriptFunction(vf); + + // Add a dummy to the builder so that it doesn't mix up function ids + functions.PushLast(0); + + return vf->id; +} + +asCObjectProperty *asCBuilder::AddPropertyToClass(sClassDeclaration *decl, const asCString &name, const asCDataType &dt, asCScriptCode *file, asCScriptNode *node) +{ + // Store the properties in the object type descriptor + asCObjectProperty *prop = asNEW(asCObjectProperty); + prop->name = name; + prop->type = dt; + + int propSize; + if( dt.IsObject() ) + { + propSize = dt.GetSizeOnStackDWords()*4; + if( !dt.IsObjectHandle() ) + { + if( !dt.CanBeInstanciated() ) + { + asASSERT( file && node ); + + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + asCString str; + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf()); + WriteError(file->name.AddressOf(), str.AddressOf(), r, c); + } + prop->type.MakeReference(true); + } + } + else + { + propSize = dt.GetSizeInMemoryBytes(); + if( propSize == 0 && file && node ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + asCString str; + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf()); + WriteError(file->name.AddressOf(), str.AddressOf(), r, c); + } + } + + // Add extra bytes so that the property will be properly aligned + if( propSize == 2 && (decl->objType->size & 1) ) decl->objType->size += 1; + if( propSize > 2 && (decl->objType->size & 3) ) decl->objType->size += 4 - (decl->objType->size & 3); + + prop->byteOffset = decl->objType->size; + decl->objType->size += propSize; + + decl->objType->properties.PushLast(prop); + + // Make sure the struct holds a reference to the config group where the object is registered + asCConfigGroup *group = engine->FindConfigGroupForObjectType(prop->type.GetObjectType()); + if( group != 0 ) group->AddRef(); + + return prop; +} + +bool asCBuilder::DoesMethodExist(asCObjectType *objType, int methodId) +{ + asCScriptFunction *method = GetFunctionDescription(methodId); + + for( asUINT n = 0; n < objType->methods.GetLength(); n++ ) + { + asCScriptFunction *m = GetFunctionDescription(objType->methods[n]); + + if( m->name != method->name ) continue; + if( m->returnType != method->returnType ) continue; + if( m->isReadOnly != method->isReadOnly ) continue; + if( m->parameterTypes != method->parameterTypes ) continue; + if( m->inOutFlags != method->inOutFlags ) continue; + + return true; + } + + return false; +} + +void asCBuilder::AddDefaultConstructor(asCObjectType *objType, asCScriptCode *file) +{ + int funcId = engine->GetNextScriptFunctionId(); + + asCDataType returnType = asCDataType::CreatePrimitive(ttVoid, false); + asCArray parameterTypes; + asCArray inOutFlags; + + // Add the script function + module->AddScriptFunction(file->idx, funcId, objType->name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false, objType); + + // Set it as default constructor + if( objType->beh.construct ) + engine->scriptFunctions[objType->beh.construct]->Release(); + objType->beh.construct = funcId; + objType->beh.constructors[0] = funcId; + engine->scriptFunctions[funcId]->AddRef(); + + // The bytecode for the default constructor will be generated + // only after the potential inheritance has been established + sFunctionDescription *func = asNEW(sFunctionDescription); + functions.PushLast(func); + + func->script = file; + func->node = 0; + func->name = objType->name; + func->objType = objType; + func->funcId = funcId; + + // Add a default factory as well + funcId = engine->GetNextScriptFunctionId(); + if( objType->beh.factory ) + engine->scriptFunctions[objType->beh.factory]->Release(); + objType->beh.factory = funcId; + objType->beh.factories[0] = funcId; + returnType = asCDataType::CreateObjectHandle(objType, false); + module->AddScriptFunction(file->idx, funcId, objType->name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false); + functions.PushLast(0); + asCCompiler compiler(engine); + compiler.CompileFactory(this, file, engine->scriptFunctions[funcId]); + engine->scriptFunctions[funcId]->AddRef(); +} + +int asCBuilder::RegisterEnum(asCScriptNode *node, asCScriptCode *file) +{ + // Grab the name of the enumeration + asCScriptNode *tmp = node->firstChild; + asASSERT(snDataType == tmp->nodeType); + + asCString name; + asASSERT(snIdentifier == tmp->firstChild->nodeType); + name.Assign(&file->code[tmp->firstChild->tokenPos], tmp->firstChild->tokenLength); + + // Check the name and add the enum + int r = CheckNameConflict(name.AddressOf(), tmp->firstChild, file); + if( asSUCCESS == r ) + { + asCObjectType *st; + asCDataType dataType; + + st = asNEW(asCObjectType)(engine); + dataType.CreatePrimitive(ttInt, false); + + st->flags = asOBJ_ENUM; + st->size = dataType.GetSizeInMemoryBytes(); + st->name = name; + + module->enumTypes.PushLast(st); + st->AddRef(); + engine->classTypes.PushLast(st); + + // Store the location of this declaration for reference in name collisions + sClassDeclaration *decl = asNEW(sClassDeclaration); + decl->name = name; + decl->script = file; + decl->validState = 0; + decl->node = NULL; + decl->objType = st; + namedTypeDeclarations.PushLast(decl); + + asCDataType type = CreateDataTypeFromNode(tmp, file); + asASSERT(!type.IsReference()); + + tmp = tmp->next; + + while( tmp ) + { + asASSERT(snIdentifier == tmp->nodeType); + + asCString name(&file->code[tmp->tokenPos], tmp->tokenLength); + + // TODO: Should only have to check for conflicts within the enum type + // Check for name conflict errors + r = CheckNameConflict(name.AddressOf(), tmp, file); + if(asSUCCESS != r) + { + continue; + } + + // check for assignment + asCScriptNode *asnNode = tmp->next; + if( asnNode && snAssignment == asnNode->nodeType ) + asnNode->DisconnectParent(); + else + asnNode = 0; + + // Create the global variable description so the enum value can be evaluated + sGlobalVariableDescription *gvar = asNEW(sGlobalVariableDescription); + globVariables.PushLast(gvar); + + gvar->script = file; + gvar->idNode = 0; + gvar->nextNode = asnNode; + gvar->name = name; + gvar->datatype = type; + // No need to allocate space on the global memory stack since the values are stored in the asCObjectType + gvar->index = 0; + gvar->isCompiled = false; + gvar->isPureConstant = true; + gvar->isEnumValue = true; + gvar->constantValue = 0xdeadbeef; + + // Allocate dummy property so we can compile the value. + // This will be removed later on so we don't add it to the engine. + gvar->property = asNEW(asCGlobalProperty); + gvar->property->name = name; + gvar->property->type = gvar->datatype; + gvar->property->id = 0; + + tmp = tmp->next; + } + } + + node->Destroy(engine); + + return r; +} + +int asCBuilder::RegisterTypedef(asCScriptNode *node, asCScriptCode *file) +{ + // Get the native data type + asCScriptNode *tmp = node->firstChild; + asASSERT(NULL != tmp && snDataType == tmp->nodeType); + asCDataType dataType; + dataType.CreatePrimitive(tmp->tokenType, false); + dataType.SetTokenType(tmp->tokenType); + tmp = tmp->next; + + // Grab the name of the typedef + asASSERT(NULL != tmp && NULL == tmp->next); + asCString name; + name.Assign(&file->code[tmp->tokenPos], tmp->tokenLength); + + // If the name is not already in use add it + int r = CheckNameConflict(name.AddressOf(), tmp, file); + if( asSUCCESS == r ) + { + // Create the new type + asCObjectType *st = asNEW(asCObjectType)(engine); + + st->flags = asOBJ_TYPEDEF; + st->size = dataType.GetSizeInMemoryBytes(); + st->name = name; + st->templateSubType = dataType; + + st->AddRef(); + + module->typeDefs.PushLast(st); + engine->classTypes.PushLast(st); + + // Store the location of this declaration for reference in name collisions + sClassDeclaration *decl = asNEW(sClassDeclaration); + decl->name = name; + decl->script = file; + decl->validState = 0; + decl->node = NULL; + decl->objType = st; + namedTypeDeclarations.PushLast(decl); + } + + node->Destroy(engine); + + if( r < 0 ) + { + engine->ConfigError(r); + } + + return 0; +} + +void asCBuilder::GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterTypes, asCArray &inOutFlags, bool &isConstMethod, bool &isConstructor, bool &isDestructor) +{ + // Find the name + isConstructor = false; + isDestructor = false; + asCScriptNode *n = 0; + if( node->firstChild->nodeType == snDataType ) + n = node->firstChild->next->next; + else + { + // If the first node is a ~ token, then we know it is a destructor + if( node->firstChild->tokenType == ttBitNot ) + { + n = node->firstChild->next; + isDestructor = true; + } + else + { + n = node->firstChild; + isConstructor = true; + } + } + name.Assign(&file->code[n->tokenPos], n->tokenLength); + + // Initialize a script function object for registration + if( !isConstructor && !isDestructor ) + { + returnType = CreateDataTypeFromNode(node->firstChild, file); + returnType = ModifyDataTypeFromNode(returnType, node->firstChild->next, file, 0, 0); + } + else + returnType = asCDataType::CreatePrimitive(ttVoid, false); + + // Is this a const method? + if( objType && n->next->next && n->next->next->tokenType == ttConst ) + isConstMethod = true; + else + isConstMethod = false; + + // Count the number of parameters + int count = 0; + asCScriptNode *c = n->next->firstChild; + while( c ) + { + count++; + c = c->next->next; + if( c && c->nodeType == snIdentifier ) + c = c->next; + } + + // Get the parameter types + parameterTypes.Allocate(count, false); + inOutFlags.Allocate(count, false); + n = n->next->firstChild; + while( n ) + { + asETypeModifiers inOutFlag; + asCDataType type = CreateDataTypeFromNode(n, file); + type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0); + + // Store the parameter type + parameterTypes.PushLast(type); + inOutFlags.PushLast(inOutFlag); + + // Move to next parameter + n = n->next->next; + if( n && n->nodeType == snIdentifier ) + n = n->next; + } +} + + +int asCBuilder::RegisterScriptFunction(int funcID, asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, bool isInterface, bool isGlobalFunction) +{ + asCString name; + asCDataType returnType; + asCArray parameterTypes; + asCArray inOutFlags; + bool isConstMethod; + bool isConstructor; + bool isDestructor; + + GetParsedFunctionDetails(node, file, objType, name, returnType, parameterTypes, inOutFlags, isConstMethod, isConstructor, isDestructor); + + // Check for name conflicts + if( !isConstructor && !isDestructor ) + { + if( objType ) + CheckNameConflictMember(objType, name.AddressOf(), node, file); + else + CheckNameConflict(name.AddressOf(), node, file); + } + else + { + // Verify that the name of the constructor/destructor is the same as the class + if( name != objType->name ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), TXT_CONSTRUCTOR_NAME_ERROR, r, c); + } + + if( isDestructor ) + name = "~" + name; + } + + if( !isInterface ) + { + sFunctionDescription *func = asNEW(sFunctionDescription); + functions.PushLast(func); + + func->script = file; + func->node = node; + func->name = name; + func->objType = objType; + func->funcId = funcID; + } + + // Destructors may not have any parameters + if( isDestructor && parameterTypes.GetLength() > 0 ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteError(file->name.AddressOf(), TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM, r, c); + } + + // TODO: Much of this can probably be reduced by using the IsSignatureEqual method + // Check that the same function hasn't been registered already + asCArray funcs; + GetFunctionDescriptions(name.AddressOf(), funcs); + if( funcs.GetLength() ) + { + for( asUINT n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *func = GetFunctionDescription(funcs[n]); + + if( parameterTypes.GetLength() == func->parameterTypes.GetLength() ) + { + bool match = true; + if( func->objectType != objType ) + { + match = false; + break; + } + + for( asUINT p = 0; p < parameterTypes.GetLength(); ++p ) + { + if( parameterTypes[p] != func->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c); + break; + } + } + } + } + + // Register the function + module->AddScriptFunction(file->idx, funcID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), isInterface, objType, isConstMethod, isGlobalFunction); + + if( objType ) + { + engine->scriptFunctions[funcID]->AddRef(); + if( isConstructor ) + { + int factoryId = engine->GetNextScriptFunctionId(); + if( parameterTypes.GetLength() == 0 ) + { + // Overload the default constructor + engine->scriptFunctions[objType->beh.construct]->Release(); + objType->beh.construct = funcID; + objType->beh.constructors[0] = funcID; + + // Register the default factory as well + engine->scriptFunctions[objType->beh.factory]->Release(); + objType->beh.factory = factoryId; + objType->beh.factories[0] = factoryId; + } + else + { + objType->beh.constructors.PushLast(funcID); + + // Register the factory as well + objType->beh.factories.PushLast(factoryId); + } + + asCDataType dt = asCDataType::CreateObjectHandle(objType, false); + module->AddScriptFunction(file->idx, factoryId, name.AddressOf(), dt, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), false); + + // Add a dummy function to the module so that it doesn't mix up the fund Ids + functions.PushLast(0); + + // Compile the factory immediately + asCCompiler compiler(engine); + compiler.CompileFactory(this, file, engine->scriptFunctions[factoryId]); + engine->scriptFunctions[factoryId]->AddRef(); + } + else if( isDestructor ) + objType->beh.destruct = funcID; + else + objType->methods.PushLast(funcID); + } + + // We need to delete the node already if this is an interface method + if( isInterface && node ) + { + node->Destroy(engine); + } + + return 0; +} + +int asCBuilder::RegisterImportedFunction(int importID, asCScriptNode *node, asCScriptCode *file) +{ + // Find name + asCScriptNode *f = node->firstChild; + asCScriptNode *n = f->firstChild->next->next; + + // Check for name conflicts + asCString name(&file->code[n->tokenPos], n->tokenLength); + CheckNameConflict(name.AddressOf(), n, file); + + // Initialize a script function object for registration + asCDataType returnType; + returnType = CreateDataTypeFromNode(f->firstChild, file); + returnType = ModifyDataTypeFromNode(returnType, f->firstChild->next, file, 0, 0); + + // Count the parameters + int count = 0; + asCScriptNode *c = n->next->firstChild; + while( c ) + { + count++; + c = c->next->next; + if( c && c->nodeType == snIdentifier ) + c = c->next; + } + + asCArray parameterTypes(count); + asCArray inOutFlags(count); + n = n->next->firstChild; + while( n ) + { + asETypeModifiers inOutFlag; + asCDataType type = CreateDataTypeFromNode(n, file); + type = ModifyDataTypeFromNode(type, n->next, file, &inOutFlag, 0); + + // Store the parameter type + n = n->next->next; + parameterTypes.PushLast(type); + inOutFlags.PushLast(inOutFlag); + + // Move to next parameter + if( n && n->nodeType == snIdentifier ) + n = n->next; + } + + // Check that the same function hasn't been registered already + asCArray funcs; + GetFunctionDescriptions(name.AddressOf(), funcs); + if( funcs.GetLength() ) + { + for( asUINT n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *func = GetFunctionDescription(funcs[n]); + + // TODO: Isn't the name guaranteed to be equal, because of GetFunctionDescriptions()? + if( name == func->name && + parameterTypes.GetLength() == func->parameterTypes.GetLength() ) + { + bool match = true; + for( asUINT p = 0; p < parameterTypes.GetLength(); ++p ) + { + if( parameterTypes[p] != func->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + int r, c; + file->ConvertPosToRowCol(node->tokenPos, &r, &c); + + WriteError(file->name.AddressOf(), TXT_FUNCTION_ALREADY_EXIST, r, c); + break; + } + } + } + } + + // Read the module name as well + n = node->firstChild->next; + asCString moduleName; + moduleName.Assign(&file->code[n->tokenPos+1], n->tokenLength-2); + + node->Destroy(engine); + + // Register the function + module->AddImportedFunction(importID, name.AddressOf(), returnType, parameterTypes.AddressOf(), inOutFlags.AddressOf(), (asUINT)parameterTypes.GetLength(), moduleName); + + return 0; +} + + +asCScriptFunction *asCBuilder::GetFunctionDescription(int id) +{ + // TODO: This should be improved + // Get the description from the engine + if( (id & 0xFFFF0000) == 0 ) + return engine->scriptFunctions[id]; + else + return engine->importedFunctions[id & 0xFFFF]->importedFunctionSignature; +} + +void asCBuilder::GetFunctionDescriptions(const char *name, asCArray &funcs) +{ + asUINT n; + // TODO: optimize: Improve linear search + for( n = 0; n < module->scriptFunctions.GetLength(); n++ ) + { + if( module->scriptFunctions[n]->name == name && + module->scriptFunctions[n]->objectType == 0 ) + funcs.PushLast(module->scriptFunctions[n]->id); + } + + // TODO: optimize: Improve linear search + for( n = 0; n < module->bindInformations.GetLength(); n++ ) + { + if( module->bindInformations[n]->importedFunctionSignature->name == name ) + funcs.PushLast(module->bindInformations[n]->importedFunctionSignature->id); + } + + // TODO: optimize: Improve linear search + // TODO: optimize: Use the registeredGlobalFunctions array instead + for( n = 0; n < engine->scriptFunctions.GetLength(); n++ ) + { + if( engine->scriptFunctions[n] && + engine->scriptFunctions[n]->funcType == asFUNC_SYSTEM && + engine->scriptFunctions[n]->objectType == 0 && + engine->scriptFunctions[n]->name == name ) + { + // Find the config group for the global function + asCConfigGroup *group = engine->FindConfigGroupForFunction(engine->scriptFunctions[n]->id); + if( !group || group->HasModuleAccess(module->name.AddressOf()) ) + funcs.PushLast(engine->scriptFunctions[n]->id); + } + } +} + +void asCBuilder::GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope) +{ + if( scope != "" ) + { + // Find the base class with the specified scope + while( objectType && objectType->name != scope ) + objectType = objectType->derivedFrom; + + // If the scope is not any of the base classes, then return no methods + if( objectType == 0 ) + return; + } + + // TODO: optimize: Improve linear search + if( objIsConst ) + { + // Only add const methods to the list + for( asUINT n = 0; n < objectType->methods.GetLength(); n++ ) + { + if( engine->scriptFunctions[objectType->methods[n]]->name == name && + engine->scriptFunctions[objectType->methods[n]]->isReadOnly ) + { + // When the scope is defined the returned methods should be the true methods, not the virtual method stubs + if( scope == "" ) + methods.PushLast(engine->scriptFunctions[objectType->methods[n]]->id); + else + { + asCScriptFunction *virtFunc = engine->scriptFunctions[objectType->methods[n]]; + asCScriptFunction *realFunc = objectType->virtualFunctionTable[virtFunc->vfTableIdx]; + methods.PushLast(realFunc->id); + } + } + } + } + else + { + // TODO: Prefer non-const over const + for( asUINT n = 0; n < objectType->methods.GetLength(); n++ ) + { + if( engine->scriptFunctions[objectType->methods[n]]->name == name ) + { + // When the scope is defined the returned methods should be the true methods, not the virtual method stubs + if( scope == "" ) + methods.PushLast(engine->scriptFunctions[objectType->methods[n]]->id); + else + { + asCScriptFunction *virtFunc = engine->scriptFunctions[objectType->methods[n]]; + asCScriptFunction *realFunc = objectType->virtualFunctionTable[virtFunc->vfTableIdx]; + methods.PushLast(realFunc->id); + } + } + } + } +} + +void asCBuilder::WriteInfo(const char *scriptname, const char *message, int r, int c, bool pre) +{ + // Need to store the pre message in a structure + if( pre ) + { + preMessage.isSet = true; + preMessage.c = c; + preMessage.r = r; + preMessage.message = message; + } + else + { + preMessage.isSet = false; + engine->WriteMessage(scriptname, r, c, asMSGTYPE_INFORMATION, message); + } +} + +void asCBuilder::WriteError(const char *scriptname, const char *message, int r, int c) +{ + numErrors++; + + // Need to pass the preMessage first + if( preMessage.isSet ) + WriteInfo(scriptname, preMessage.message.AddressOf(), preMessage.r, preMessage.c, false); + + engine->WriteMessage(scriptname, r, c, asMSGTYPE_ERROR, message); +} + +void asCBuilder::WriteWarning(const char *scriptname, const char *message, int r, int c) +{ + numWarnings++; + + // Need to pass the preMessage first + if( preMessage.isSet ) + WriteInfo(scriptname, preMessage.message.AddressOf(), preMessage.r, preMessage.c, false); + + engine->WriteMessage(scriptname, r, c, asMSGTYPE_WARNING, message); +} + + +asCDataType asCBuilder::CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, bool acceptHandleForScope, asCObjectType *templateType) +{ + asASSERT(node->nodeType == snDataType); + + asCDataType dt; + + asCScriptNode *n = node->firstChild; + + bool isConst = false; + bool isImplicitHandle = false; + if( n->tokenType == ttConst ) + { + isConst = true; + n = n->next; + } + + if( n->tokenType == ttIdentifier ) + { + asCString str; + str.Assign(&file->code[n->tokenPos], n->tokenLength); + + asCObjectType *ot = 0; + + // If this is for a template type, then we must first determine if the + // identifier matches any of the template subtypes + // TODO: template: it should be possible to have more than one subtypes + if( templateType && (templateType->flags & asOBJ_TEMPLATE) && str == templateType->templateSubType.GetObjectType()->name ) + ot = templateType->templateSubType.GetObjectType(); + + if( ot == 0 ) + ot = GetObjectType(str.AddressOf()); + + if( ot == 0 ) + { + asCString msg; + msg.Format(TXT_IDENTIFIER_s_NOT_DATA_TYPE, (const char *)str.AddressOf()); + + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + WriteError(file->name.AddressOf(), msg.AddressOf(), r, c); + + dt.SetTokenType(ttInt); + } + else + { + if( ot->flags & asOBJ_IMPLICIT_HANDLE ) + isImplicitHandle = true; + + // Find the config group for the object type + asCConfigGroup *group = engine->FindConfigGroupForObjectType(ot); + if( !module || !group || group->HasModuleAccess(module->name.AddressOf()) ) + { + if(asOBJ_TYPEDEF == (ot->flags & asOBJ_TYPEDEF)) + { + // TODO: typedef: A typedef should be considered different from the original type (though with implicit conversions between the two) + // Create primitive data type based on object flags + dt = ot->templateSubType; + dt.MakeReadOnly(isConst); + } + else + { + if( ot->flags & asOBJ_TEMPLATE ) + { + n = n->next; + + // Check if the subtype is a type or the template's subtype + // if it is the template's subtype then this is the actual template type, + // orderwise it is a template instance. + asCDataType subType = CreateDataTypeFromNode(n, file, false, ot); + if( subType.GetObjectType() != ot->templateSubType.GetObjectType() ) + { + // This is a template instance + // Need to find the correct object type + asCObjectType *otInstance = engine->GetTemplateInstanceType(ot, subType); + + if( !otInstance ) + { + asCString msg; + msg.Format(TXT_CANNOT_INSTANCIATE_TEMPLATE_s_WITH_s, ot->name.AddressOf(), subType.Format().AddressOf()); + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), msg.AddressOf(), r, c); + } + + ot = otInstance; + } + } + + // Create object data type + if( ot ) + dt = asCDataType::CreateObject(ot, isConst); + else + dt = asCDataType::CreatePrimitive(ttInt, isConst); + } + } + else + { + asCString msg; + msg.Format(TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE, (const char *)str.AddressOf()); + + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + WriteError(file->name.AddressOf(), msg.AddressOf(), r, c); + + dt.SetTokenType(ttInt); + } + } + } + else + { + // Create primitive data type + dt = asCDataType::CreatePrimitive(n->tokenType, isConst); + } + + // Determine array dimensions and object handles + n = n->next; + while( n && (n->tokenType == ttOpenBracket || n->tokenType == ttHandle) ) + { + if( n->tokenType == ttOpenBracket ) + { + // Make sure the sub type can be instanciated + if( !dt.CanBeInstanciated() ) + { + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + + asCString str; + // TODO: Change to "Array sub type cannot be 'type'" + str.Format(TXT_DATA_TYPE_CANT_BE_s, dt.Format().AddressOf()); + + WriteError(file->name.AddressOf(), str.AddressOf(), r, c); + } + + // Make the type an array (or multidimensional array) + if( dt.MakeArray(engine) < 0 ) + { + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), TXT_TOO_MANY_ARRAY_DIMENSIONS, r, c); + break; + } + } + else + { + // Make the type a handle + if( dt.MakeHandle(true, acceptHandleForScope) < 0 ) + { + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), TXT_OBJECT_HANDLE_NOT_SUPPORTED, r, c); + break; + } + } + n = n->next; + } + + if( isImplicitHandle ) + { + // Make the type a handle + if( dt.MakeHandle(true, acceptHandleForScope) < 0 ) + { + int r, c; + file->ConvertPosToRowCol(n->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), TXT_OBJECT_HANDLE_NOT_SUPPORTED, r, c); + } + } + + return dt; +} + +asCDataType asCBuilder::ModifyDataTypeFromNode(const asCDataType &type, asCScriptNode *node, asCScriptCode *file, asETypeModifiers *inOutFlags, bool *autoHandle) +{ + asCDataType dt = type; + + if( inOutFlags ) *inOutFlags = asTM_NONE; + + // Is the argument sent by reference? + asCScriptNode *n = node->firstChild; + if( n && n->tokenType == ttAmp ) + { + dt.MakeReference(true); + n = n->next; + + if( n ) + { + if( inOutFlags ) + { + if( n->tokenType == ttIn ) + *inOutFlags = asTM_INREF; + else if( n->tokenType == ttOut ) + *inOutFlags = asTM_OUTREF; + else if( n->tokenType == ttInOut ) + *inOutFlags = asTM_INOUTREF; + else + asASSERT(false); + } + + n = n->next; + } + else + { + if( inOutFlags ) + *inOutFlags = asTM_INOUTREF; // ttInOut + } + + if( !engine->ep.allowUnsafeReferences && + inOutFlags && *inOutFlags == asTM_INOUTREF ) + { + // Verify that the base type support &inout parameter types + if( !dt.IsObject() || dt.IsObjectHandle() || !dt.GetObjectType()->beh.addref || !dt.GetObjectType()->beh.release ) + { + int r, c; + file->ConvertPosToRowCol(node->firstChild->tokenPos, &r, &c); + WriteError(file->name.AddressOf(), TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT, r, c); + } + } + } + + if( autoHandle ) *autoHandle = false; + + if( n && n->tokenType == ttPlus ) + { + if( autoHandle ) *autoHandle = true; + } + + return dt; +} + +asCObjectType *asCBuilder::GetObjectType(const char *type) +{ + // TODO: Only search in config groups to which the module has access + asCObjectType *ot = engine->GetObjectType(type); + if( !ot && module ) + ot = module->GetObjectType(type); + + return ot; +} + +int asCBuilder::GetEnumValueFromObjectType(asCObjectType *objType, const char *name, asCDataType &outDt, asDWORD &outValue) +{ + if( !objType || !(objType->flags & asOBJ_ENUM) ) + return 0; + + for( asUINT n = 0; n < objType->enumValues.GetLength(); ++n ) + { + if( objType->enumValues[n]->name == name ) + { + outDt = asCDataType::CreateObject(objType, true); + outValue = objType->enumValues[n]->value; + return 1; + } + } + + return 0; +} + +int asCBuilder::GetEnumValue(const char *name, asCDataType &outDt, asDWORD &outValue) +{ + bool found = false; + + // Search all available enum types + asUINT t; + for( t = 0; t < engine->objectTypes.GetLength(); t++ ) + { + asCObjectType *ot = engine->objectTypes[t]; + if( GetEnumValueFromObjectType( ot, name, outDt, outValue ) ) + { + if( !found ) + { + found = true; + } + else + { + // Found more than one value in different enum types + return 2; + } + } + } + + for( t = 0; t < module->enumTypes.GetLength(); t++ ) + { + asCObjectType *ot = module->enumTypes[t]; + if( GetEnumValueFromObjectType( ot, name, outDt, outValue ) ) + { + if( !found ) + { + found = true; + } + else + { + // Found more than one value in different enum types + return 2; + } + } + } + + if( found ) + return 1; + + // Didn't find any value + return 0; +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_builder.h b/AngelScript/source/as_builder.h new file mode 100644 index 000000000..cf45c13cb --- /dev/null +++ b/AngelScript/source/as_builder.h @@ -0,0 +1,185 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_builder.h +// +// This is the class that manages the compilation of the scripts +// + + +#ifndef AS_BUILDER_H +#define AS_BUILDER_H + +#include "as_config.h" +#include "as_scriptengine.h" +#include "as_module.h" +#include "as_array.h" +#include "as_scriptcode.h" +#include "as_scriptnode.h" +#include "as_datatype.h" +#include "as_property.h" + +BEGIN_AS_NAMESPACE + +struct sFunctionDescription +{ + asCScriptCode *script; + asCScriptNode *node; + asCString name; + asCObjectType *objType; + int funcId; +}; + +struct sGlobalVariableDescription +{ + asCScriptCode *script; + asCScriptNode *idNode; + asCScriptNode *nextNode; + asCString name; + asCGlobalProperty *property; + asCDataType datatype; + int index; + bool isCompiled; + bool isPureConstant; + bool isEnumValue; + asQWORD constantValue; +}; + +struct sClassDeclaration +{ + asCScriptCode *script; + asCScriptNode *node; + asCString name; + int validState; + asCObjectType *objType; +}; + +class asCCompiler; + +class asCBuilder +{ +public: + asCBuilder(asCScriptEngine *engine, asCModule *module); + ~asCBuilder(); + + int VerifyProperty(asCDataType *dt, const char *decl, asCString &outName, asCDataType &outType); + + int ParseDataType(const char *datatype, asCDataType *result); + int ParseTemplateDecl(const char *decl, asCString *name, asCString *subtypeName); + + int ParseFunctionDeclaration(asCObjectType *type, const char *decl, asCScriptFunction *func, bool isSystemFunction, asCArray *paramAutoHandles = 0, bool *returnAutoHandle = 0); + int ParseVariableDeclaration(const char *decl, asCObjectProperty *var); + + int AddCode(const char *name, const char *code, int codeLength, int lineOffset, int sectionIdx, bool makeCopy); + int Build(); + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 + int BuildString(const char *string, asCContext *ctx); +#endif + int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asCScriptFunction **outFunc); + int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset); + + void WriteInfo(const char *scriptname, const char *msg, int r, int c, bool preMessage); + void WriteError(const char *scriptname, const char *msg, int r, int c); + void WriteWarning(const char *scriptname, const char *msg, int r, int c); + + int CheckNameConflict(const char *name, asCScriptNode *node, asCScriptCode *code); + int CheckNameConflictMember(asCObjectType *type, const char *name, asCScriptNode *node, asCScriptCode *code); + +protected: + friend class asCCompiler; + friend class asCModule; + friend class asCParser; + + asCObjectProperty *GetObjectProperty(asCDataType &obj, const char *prop); + asCGlobalProperty *GetGlobalProperty(const char *prop, bool *isCompiled, bool *isPureConstant, asQWORD *constantValue); + + asCScriptFunction *GetFunctionDescription(int funcID); + void GetFunctionDescriptions(const char *name, asCArray &funcs); + void GetObjectMethodDescriptions(const char *name, asCObjectType *objectType, asCArray &methods, bool objIsConst, const asCString &scope = ""); + + int RegisterScriptFunction(int funcID, asCScriptNode *node, asCScriptCode *file, asCObjectType *object = 0, bool isInterface = false, bool isGlobalFunction = false); + int RegisterImportedFunction(int funcID, asCScriptNode *node, asCScriptCode *file); + int RegisterGlobalVar(asCScriptNode *node, asCScriptCode *file); + int RegisterClass(asCScriptNode *node, asCScriptCode *file); + int RegisterInterface(asCScriptNode *node, asCScriptCode *file); + int RegisterEnum(asCScriptNode *node, asCScriptCode *file); + int RegisterTypedef(asCScriptNode *node, asCScriptCode *file); + void CompileClasses(); + + void GetParsedFunctionDetails(asCScriptNode *node, asCScriptCode *file, asCObjectType *objType, asCString &name, asCDataType &returnType, asCArray ¶meterTypes, asCArray &inOutFlags, bool &isConstMethod, bool &isConstructor, bool &isDestructor); + + bool DoesMethodExist(asCObjectType *objType, int methodId); + + void AddDefaultConstructor(asCObjectType *objType, asCScriptCode *file); + asCObjectProperty *AddPropertyToClass(sClassDeclaration *c, const asCString &name, const asCDataType &type, asCScriptCode *file = 0, asCScriptNode *node = 0); + + int CreateVirtualFunction(asCScriptFunction *func, int idx); + + asCObjectType *GetObjectType(const char *type); + + int GetEnumValueFromObjectType(asCObjectType *objType, const char *name, asCDataType &outDt, asDWORD &outValue); + int GetEnumValue(const char *name, asCDataType &outDt, asDWORD &outValue); + + void ParseScripts(); + void CompileFunctions(); + void CompileGlobalVariables(); + + struct preMessage_t + { + bool isSet; + asCString message; + int r; + int c; + } preMessage; + + int numErrors; + int numWarnings; + + asCArray scripts; + asCArray functions; + asCArray globVariables; + asCArray classDeclarations; + asCArray interfaceDeclarations; + asCArray namedTypeDeclarations; + + asCScriptEngine *engine; + asCModule *module; + + asCDataType CreateDataTypeFromNode(asCScriptNode *node, asCScriptCode *file, bool acceptHandleForScope = false, asCObjectType *templateType = 0); + asCDataType ModifyDataTypeFromNode(const asCDataType &type, asCScriptNode *node, asCScriptCode *file, asETypeModifiers *inOutFlag, bool *autoHandle); +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_bytecode.cpp b/AngelScript/source/as_bytecode.cpp new file mode 100644 index 000000000..58c26e389 --- /dev/null +++ b/AngelScript/source/as_bytecode.cpp @@ -0,0 +1,2215 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_bytecode.cpp +// +// A class for constructing the final byte code +// + +#include // fopen(), fprintf(), fclose() + +#include "as_config.h" +#include "as_bytecode.h" +#include "as_debug.h" // mkdir() +#include "as_array.h" +#include "as_string.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCByteCode::asCByteCode(asCScriptEngine *engine) +{ + first = 0; + last = 0; + largestStackUsed = -1; + + this->engine = engine; +} + +asCByteCode::~asCByteCode() +{ + ClearAll(); +} + +void asCByteCode::Finalize() +{ + // verify the bytecode + PostProcess(); + + // Optimize the code (optionally) + if( engine->ep.optimizeByteCode ) + Optimize(); + + // Resolve jumps + ResolveJumpAddresses(); + + // Build line numbers buffer + ExtractLineNumbers(); +} + +void asCByteCode::ClearAll() +{ + cByteInstruction *del = first; + + while( del ) + { + first = del->next; + engine->memoryMgr.FreeByteInstruction(del); + del = first; + } + + first = 0; + last = 0; + + lineNumbers.SetLength(0); + + largestStackUsed = -1; + + temporaryVariables.SetLength(0); +} + +void asCByteCode::InsertIfNotExists(asCArray &vars, int var) +{ + if( !vars.Exists(var) ) + vars.PushLast(var); +} + +void asCByteCode::GetVarsUsed(asCArray &vars) +{ + cByteInstruction *curr = first; + while( curr ) + { + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG ) + { + InsertIfNotExists(vars, curr->wArg[0]); + InsertIfNotExists(vars, curr->wArg[1]); + InsertIfNotExists(vars, curr->wArg[2]); + } + else if( asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG ) + { + InsertIfNotExists(vars, curr->wArg[0]); + } + else if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG ) + { + InsertIfNotExists(vars, curr->wArg[0]); + InsertIfNotExists(vars, curr->wArg[1]); + } + else if( asBCInfo[curr->op].type == asBCTYPE_W_rW_ARG ) + { + InsertIfNotExists(vars, curr->wArg[1]); + } + + curr = curr->next; + } +} + +bool asCByteCode::IsVarUsed(int offset) +{ + cByteInstruction *curr = first; + while( curr ) + { + // Verify all ops that use variables + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG ) + { + if( curr->wArg[0] == offset || curr->wArg[1] == offset || curr->wArg[2] == offset ) + return true; + } + else if( asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG ) + { + if( curr->wArg[0] == offset ) + return true; + } + else if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG ) + { + if( curr->wArg[0] == offset || curr->wArg[1] == offset ) + return true; + } + else if( asBCInfo[curr->op].type == asBCTYPE_W_rW_ARG ) + { + if( curr->wArg[1] == offset ) + return true; + } + + curr = curr->next; + } + + return false; +} + +void asCByteCode::ExchangeVar(int oldOffset, int newOffset) +{ + cByteInstruction *curr = first; + while( curr ) + { + // Verify all ops that use variables + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG ) + { + if( curr->wArg[0] == oldOffset ) + curr->wArg[0] = (short)newOffset; + if( curr->wArg[1] == oldOffset ) + curr->wArg[1] = (short)newOffset; + if( curr->wArg[2] == oldOffset ) + curr->wArg[2] = (short)newOffset; + } + else if( asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG ) + { + if( curr->wArg[0] == oldOffset ) + curr->wArg[0] = (short)newOffset; + } + else if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG ) + { + if( curr->wArg[0] == oldOffset ) + curr->wArg[0] = (short)newOffset; + if( curr->wArg[1] == oldOffset ) + curr->wArg[1] = (short)newOffset; + } + else if( asBCInfo[curr->op].type == asBCTYPE_W_rW_ARG ) + { + if( curr->wArg[1] == oldOffset ) + curr->wArg[1] = (short)newOffset; + } + + curr = curr->next; + } +} + +void asCByteCode::AddPath(asCArray &paths, cByteInstruction *instr, int stackSize) +{ + if( instr->marked ) + { + // Verify the size of the stack + asASSERT(instr->stackSize == stackSize); + } + else + { + // Add the destination to the code paths + instr->marked = true; + instr->stackSize = stackSize; + paths.PushLast(instr); + } +} + +bool asCByteCode::IsCombination(cByteInstruction *curr, asEBCInstr bc1, asEBCInstr bc2) +{ + if( curr->op == bc1 && curr->next && curr->next->op == bc2 ) + return true; + + return false; +} + +bool asCByteCode::IsCombination(cByteInstruction *curr, asEBCInstr bc1, asEBCInstr bc2, asEBCInstr bc3) +{ + if( curr->op == bc1 && + curr->next && curr->next->op == bc2 && + curr->next->next && curr->next->next->op == bc3 ) + return true; + + return false; +} + +cByteInstruction *asCByteCode::ChangeFirstDeleteNext(cByteInstruction *curr, asEBCInstr bc) +{ + curr->op = bc; + + if( curr->next ) DeleteInstruction(curr->next); + + // Continue optimization with the instruction before the altered one + if( curr->prev ) + return curr->prev; + else + return curr; +} + +cByteInstruction *asCByteCode::DeleteFirstChangeNext(cByteInstruction *curr, asEBCInstr bc) +{ + asASSERT( curr->next ); + + cByteInstruction *instr = curr->next; + instr->op = bc; + + DeleteInstruction(curr); + + // Continue optimization with the instruction before the altered one + if( instr->prev ) + return instr->prev; + else + return instr; +} + +void asCByteCode::InsertBefore(cByteInstruction *before, cByteInstruction *instr) +{ + asASSERT(instr->next == 0); + asASSERT(instr->prev == 0); + + if( before->prev ) before->prev->next = instr; + instr->prev = before->prev; + before->prev = instr; + instr->next = before; + + if( first == before ) first = instr; +} + +void asCByteCode::RemoveInstruction(cByteInstruction *instr) +{ + if( instr == first ) first = first->next; + if( instr == last ) last = last->prev; + + if( instr->prev ) instr->prev->next = instr->next; + if( instr->next ) instr->next->prev = instr->prev; + + instr->next = 0; + instr->prev = 0; +} + +bool asCByteCode::CanBeSwapped(cByteInstruction *curr) +{ + if( !curr || !curr->next || !curr->next->next ) return false; + if( curr->next->next->op != asBC_SWAP4 ) return false; + + cByteInstruction *next = curr->next; + + if( curr->op != asBC_PshC4 && + curr->op != asBC_PshV4 && + curr->op != asBC_PSF ) + return false; + + if( next->op != asBC_PshC4 && + next->op != asBC_PshV4 && + next->op != asBC_PSF ) + return false; + + return true; +} + +cByteInstruction *asCByteCode::GoBack(cByteInstruction *curr) +{ + // Go back 2 instructions + if( !curr ) return 0; + if( curr->prev ) curr = curr->prev; + if( curr->prev ) curr = curr->prev; + return curr; +} + +bool asCByteCode::PostponeInitOfTemp(cByteInstruction *curr, cByteInstruction **next) +{ + if( curr->op != asBC_SetV4 || !IsTemporary(curr->wArg[0]) ) return false; + + // Move the initialization to just before it's use. + // Don't move it beyond any labels or jumps. + cByteInstruction *use = curr->next; + while( use ) + { + if( IsTempVarReadByInstr(use, curr->wArg[0]) ) + break; + + if( IsTempVarOverwrittenByInstr(use, curr->wArg[0]) ) + return false; + + if( IsInstrJmpOrLabel(use) ) + return false; + + use = use->next; + } + + if( use && use->prev != curr ) + { + *next = curr->next; + + // Move the instruction + RemoveInstruction(curr); + InsertBefore(use, curr); + + // Try a RemoveUnusedValue to see if it can be combined with the other + cByteInstruction *temp; + if( RemoveUnusedValue(curr, &temp) ) + { + *next = GoBack(*next); + return true; + } + + // Return the instructions to its original position as it wasn't useful + RemoveInstruction(curr); + InsertBefore(*next, curr); + } + + return false; +} + +bool asCByteCode::RemoveUnusedValue(cByteInstruction *curr, cByteInstruction **next) +{ + // The value isn't used for anything + if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG) && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr, curr->wArg[0]) ) + { + if( curr->op == asBC_LdGRdR4 && IsTempRegUsed(curr) ) + { + curr->op = asBC_LDG; + *next = GoBack(curr); + return true; + } + + *next = GoBack(DeleteInstruction(curr)); + return true; + } + + // TODO: There should be one for doubles as well + // The value is immediately used and then never again + if( curr->op == asBC_SetV4 && + curr->next && + (curr->next->op == asBC_CMPi || + curr->next->op == asBC_CMPf || + curr->next->op == asBC_CMPu) && + curr->wArg[0] == curr->next->wArg[1] && + (IsTemporary(curr->wArg[0]) && // The variable is temporary and never used again + !IsTempVarRead(curr->next, curr->wArg[0])) ) + { + if( curr->next->op == asBC_CMPi ) curr->next->op = asBC_CMPIi; + else if( curr->next->op == asBC_CMPf ) curr->next->op = asBC_CMPIf; + else if( curr->next->op == asBC_CMPu ) curr->next->op = asBC_CMPIu; + curr->next->size = asBCTypeSize[asBCInfo[asBC_CMPIi].type]; + curr->next->arg = curr->arg; + *next = GoBack(DeleteInstruction(curr)); + return true; + } + + // The value is immediately used and then never again + if( curr->op == asBC_SetV4 && + curr->next && + (curr->next->op == asBC_ADDi || + curr->next->op == asBC_SUBi || + curr->next->op == asBC_MULi || + curr->next->op == asBC_ADDf || + curr->next->op == asBC_SUBf || + curr->next->op == asBC_MULf) && + curr->wArg[0] == curr->next->wArg[2] && + (curr->next->wArg[0] == curr->wArg[0] || // The variable is overwritten + (IsTemporary(curr->wArg[0]) && // The variable is temporary and never used again + !IsTempVarRead(curr->next, curr->wArg[0]))) ) + { + if( curr->next->op == asBC_ADDi ) curr->next->op = asBC_ADDIi; + else if( curr->next->op == asBC_SUBi ) curr->next->op = asBC_SUBIi; + else if( curr->next->op == asBC_MULi ) curr->next->op = asBC_MULIi; + else if( curr->next->op == asBC_ADDf ) curr->next->op = asBC_ADDIf; + else if( curr->next->op == asBC_SUBf ) curr->next->op = asBC_SUBIf; + else if( curr->next->op == asBC_MULf ) curr->next->op = asBC_MULIf; + curr->next->size = asBCTypeSize[asBCInfo[asBC_ADDIi].type]; + curr->next->arg = curr->arg; + *next = GoBack(DeleteInstruction(curr)); + return true; + } + + if( curr->op == asBC_SetV4 && + curr->next && + (curr->next->op == asBC_ADDi || + curr->next->op == asBC_MULi || + curr->next->op == asBC_ADDf || + curr->next->op == asBC_MULf) && + curr->wArg[0] == curr->next->wArg[1] && + (curr->next->wArg[0] == curr->wArg[0] || // The variable is overwritten + (IsTemporary(curr->wArg[0]) && // The variable is temporary and never used again + !IsTempVarRead(curr->next, curr->wArg[0]))) ) + { + if( curr->next->op == asBC_ADDi ) curr->next->op = asBC_ADDIi; + else if( curr->next->op == asBC_MULi ) curr->next->op = asBC_MULIi; + else if( curr->next->op == asBC_ADDf ) curr->next->op = asBC_ADDIf; + else if( curr->next->op == asBC_MULf ) curr->next->op = asBC_MULIf; + curr->next->size = asBCTypeSize[asBCInfo[asBC_ADDIi].type]; + curr->next->arg = curr->arg; + + // The order of the operands are changed + curr->next->wArg[1] = curr->next->wArg[2]; + + *next = GoBack(DeleteInstruction(curr)); + return true; + } + + // The values is immediately moved to another variable and then not used again + if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG) && + curr->next && curr->next->op == asBC_CpyVtoV4 && + curr->wArg[0] == curr->next->wArg[1] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->wArg[0] = curr->next->wArg[0]; + DeleteInstruction(curr->next); + *next = GoBack(curr); + return true; + } + + // The constant value is immediately moved to another variable and then not used again + if( curr->op == asBC_SetV4 && curr->next && curr->next->op == asBC_CpyVtoV4 && + curr->wArg[0] == curr->next->wArg[1] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->wArg[0] = curr->next->wArg[0]; + DeleteInstruction(curr->next); + *next = GoBack(curr); + return true; + } + + // The register is copied to a temp variable and then back to the register again without being used afterwards + if( curr->op == asBC_CpyRtoV4 && curr->next && curr->next->op == asBC_CpyVtoR4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + // Delete both instructions + DeleteInstruction(curr->next); + *next = GoBack(DeleteInstruction(curr)); + return true; + } + + // The global value is copied to a temp and then immediately pushed on the stack + if( curr->op == asBC_CpyGtoV4 && curr->next && curr->next->op == asBC_PshV4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->op = asBC_PshG4; + curr->size = asBCTypeSize[asBCInfo[asBC_PshG4].type]; + curr->stackInc = asBCInfo[asBC_PshG4].stackInc; + DeleteInstruction(curr->next); + *next = GoBack(curr); + return true; + } + + // The constant is copied to a temp and then immediately pushed on the stack + if( curr->op == asBC_SetV4 && curr->next && curr->next->op == asBC_PshV4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->op = asBC_PshC4; + curr->stackInc = asBCInfo[asBC_PshC4].stackInc; + DeleteInstruction(curr->next); + *next = GoBack(curr); + return true; + } + + // The constant is copied to a global variable and then never used again + if( curr->op == asBC_SetV4 && curr->next && curr->next->op == asBC_CpyVtoG4 && + curr->wArg[0] == curr->next->wArg[0] && + IsTemporary(curr->wArg[0]) && + !IsTempVarRead(curr->next, curr->wArg[0]) ) + { + curr->op = asBC_SetG4; + curr->size = asBCTypeSize[asBCInfo[asBC_SetG4].type]; + *(((asDWORD*)&curr->arg)+AS_PTR_SIZE) = (asDWORD)curr->arg; + *(asPTRWORD*)&curr->arg = (asDWORD)curr->next->arg; + DeleteInstruction(curr->next); + *next = GoBack(curr); + return true; + } + + return false; +} + +bool asCByteCode::IsTemporary(short offset) +{ + for( asUINT n = 0; n < temporaryVariables.GetLength(); n++ ) + if( temporaryVariables[n] == offset ) + return true; + + return false; +} + +int asCByteCode::Optimize() +{ + // TODO: optimize: The optimizer should be able to inline function calls. + // If the called function has only a few instructions, the function call should be inlined. + // This is especially useful with the factory stubs used for template types and script classes. + + // TODO: optimize: Optimize the release of script objects. Most of the time the instructions PSV and FREE are used for this. + // Need a bytecode BC_FreeV that can free the object stored in a variable directly + + // TODO: optimize: Need a bytecode BC_AddRef so that BC_CALLSYS doesn't have to be used for this trivial call + + cByteInstruction *instr = first; + while( instr ) + { + cByteInstruction *curr = instr; + instr = instr->next; + + // Remove or combine instructions + if( RemoveUnusedValue(curr, &instr) ) continue; + + // Postpone initializations so that they may be combined in the second pass + if( PostponeInitOfTemp(curr, &instr) ) continue; + + // XXX x, YYY y, SWAP4 -> YYY y, XXX x + if( CanBeSwapped(curr) ) + { + // Delete SWAP4 + DeleteInstruction(instr->next); + + // Swap instructions + RemoveInstruction(instr); + InsertBefore(curr, instr); + + instr = GoBack(instr); + } + // SWAP4, OP -> OP + else if( IsCombination(curr, asBC_SWAP4, asBC_ADDi) || + IsCombination(curr, asBC_SWAP4, asBC_MULi) || + IsCombination(curr, asBC_SWAP4, asBC_ADDf) || + IsCombination(curr, asBC_SWAP4, asBC_MULf) ) + instr = GoBack(DeleteInstruction(curr)); + // PSF x, RDS4 -> PshV4 x + else if( IsCombination(curr, asBC_PSF, asBC_RDS4) ) + instr = GoBack(ChangeFirstDeleteNext(curr, asBC_PshV4)); + // RDS4, POP x -> POP x + else if( IsCombination(curr, asBC_RDS4, asBC_POP) && instr->wArg[0] > 0 ) + instr = GoBack(DeleteInstruction(curr)); + // LDG x, WRTV4 y -> CpyVtoG4 y, x + else if( IsCombination(curr, asBC_LDG, asBC_WRTV4) && !IsTempRegUsed(instr) ) + { + curr->op = asBC_CpyVtoG4; + curr->size = asBCTypeSize[asBCInfo[asBC_CpyVtoG4].type]; + curr->wArg[0] = instr->wArg[0]; + + DeleteInstruction(instr); + instr = GoBack(curr); + } + // LDG x, RDR4 y -> CpyGtoV4 y, x + else if( IsCombination(curr, asBC_LDG, asBC_RDR4) ) + { + if( !IsTempRegUsed(instr) ) + curr->op = asBC_CpyGtoV4; + else + curr->op = asBC_LdGRdR4; + curr->size = asBCTypeSize[asBCInfo[asBC_CpyGtoV4].type]; + curr->wArg[0] = instr->wArg[0]; + + DeleteInstruction(instr); + instr = GoBack(curr); + } + // LDV x, INCi -> IncVi x + else if( IsCombination(curr, asBC_LDV, asBC_INCi) && !IsTempRegUsed(instr) ) + { + curr->op = asBC_IncVi; + + DeleteInstruction(instr); + instr = GoBack(curr); + } + // LDV x, DECi -> DecVi x + else if( IsCombination(curr, asBC_LDV, asBC_DECi) && !IsTempRegUsed(instr) ) + { + curr->op = asBC_DecVi; + + DeleteInstruction(instr); + instr = GoBack(curr); + } + // POP a, RET b -> RET b + else if( IsCombination(curr, asBC_POP, asBC_RET) ) + { + // We don't combine the POP+RET because RET first restores + // the previous stack pointer and then pops the arguments + + // Delete POP + instr = GoBack(DeleteInstruction(curr)); + } + // Delete JitEntry if the JIT instructions are not supposed to be included + else if( curr->op == asBC_JitEntry && !engine->ep.includeJitInstructions ) + { + instr = GoBack(DeleteInstruction(curr)); + } + // SUSPEND, JitEntry, SUSPEND -> SUSPEND + // LINE, JitEntry, LINE -> LINE + else if( (IsCombination(curr, asBC_SUSPEND, asBC_JitEntry) && IsCombination(instr, asBC_JitEntry, asBC_SUSPEND)) || + (IsCombination(curr, asBC_LINE, asBC_JitEntry) && IsCombination(instr, asBC_JitEntry, asBC_LINE)) ) + { + // Delete the two first instructions + DeleteInstruction(instr); + instr = GoBack(DeleteInstruction(curr)); + } + // SUSPEND, SUSPEND -> SUSPEND + // LINE, LINE -> LINE + else if( IsCombination(curr, asBC_SUSPEND, asBC_SUSPEND) || + IsCombination(curr, asBC_LINE, asBC_LINE) ) + { + // Delete the first instruction + instr = GoBack(DeleteInstruction(curr)); + } + // PUSH a, PUSH b -> PUSH a+b + else if( IsCombination(curr, asBC_PUSH, asBC_PUSH) ) + { + // Combine the two PUSH + instr->wArg[0] = curr->wArg[0] + instr->wArg[0]; + // Delete current + DeleteInstruction(curr); + // Continue with the instruction before the one removed + instr = GoBack(instr); + } + // PshC4 a, GETREF 0 -> PSF a + else if( IsCombination(curr, asBC_PshC4, asBC_GETREF) && instr->wArg[0] == 0 ) + { + // Convert PshC4 a, to PSF a + curr->wArg[0] = (short)*ARG_DW(curr->arg); + curr->size = asBCTypeSize[asBCInfo[asBC_PSF].type]; + curr->op = asBC_PSF; + DeleteInstruction(instr); + instr = GoBack(curr); + } + // YYY y, POP x -> POP x-1 + else if( (IsCombination(curr, asBC_PshV4, asBC_POP) || + IsCombination(curr, asBC_PshC4, asBC_POP)) && instr->wArg[0] > 0 ) + { + DeleteInstruction(curr); + instr->wArg[0]--; + instr = GoBack(instr); + } + // PshRPtr, POP x -> POP x - 1 + else if( (IsCombination(curr, asBC_PshRPtr, asBC_POP) || + IsCombination(curr, asBC_PSF , asBC_POP) || + IsCombination(curr, asBC_VAR , asBC_POP)) + && instr->wArg[0] > (AS_PTR_SIZE-1) ) + { + DeleteInstruction(curr); + instr->wArg[0] -= AS_PTR_SIZE; + instr = GoBack(instr); + } + // RDS8, POP 2 -> POP x-1 + else if( IsCombination(curr, asBC_RDS8, asBC_POP) && instr->wArg[0] > 1 ) + { + DeleteInstruction(curr); + instr->wArg[0] -= 2-AS_PTR_SIZE; // Transform the pop to remove the address instead of the 8 byte word + instr = GoBack(instr); + } + // PshC8 y, POP x -> POP x-2 + else if( IsCombination(curr, asBC_PshC8, asBC_POP) && instr->wArg[0] > 1 ) + { + DeleteInstruction(curr); + instr->wArg[0] -= 2; + instr = GoBack(instr); + } + // POP 0 -> remove + // PUSH 0 -> remove + else if( (curr->op == asBC_POP || curr->op == asBC_PUSH ) && curr->wArg[0] == 0 ) + instr = GoBack(DeleteInstruction(curr)); +// Begin PATTERN + // T**; J** +x -> J** +x + else if( IsCombination(curr, asBC_TZ , asBC_JZ ) || + IsCombination(curr, asBC_TNZ, asBC_JNZ) ) + instr = GoBack(DeleteFirstChangeNext(curr, asBC_JNZ)); + else if( IsCombination(curr, asBC_TNZ, asBC_JZ ) || + IsCombination(curr, asBC_TZ , asBC_JNZ) ) + instr = GoBack(DeleteFirstChangeNext(curr, asBC_JZ)); + else if( IsCombination(curr, asBC_TS , asBC_JZ ) || + IsCombination(curr, asBC_TNS, asBC_JNZ) ) + instr = GoBack(DeleteFirstChangeNext(curr, asBC_JNS)); + else if( IsCombination(curr, asBC_TNS, asBC_JZ ) || + IsCombination(curr, asBC_TS , asBC_JNZ) ) + instr = GoBack(DeleteFirstChangeNext(curr, asBC_JS)); + else if( IsCombination(curr, asBC_TP , asBC_JZ ) || + IsCombination(curr, asBC_TNP, asBC_JNZ) ) + instr = GoBack(DeleteFirstChangeNext(curr, asBC_JNP)); + else if( IsCombination(curr, asBC_TNP, asBC_JZ ) || + IsCombination(curr, asBC_TP , asBC_JNZ) ) + instr = GoBack(DeleteFirstChangeNext(curr, asBC_JP)); +// End PATTERN + // JMP +0 -> remove + else if( IsCombination(curr, asBC_JMP, asBC_LABEL) && *(int*)&curr->arg == instr->wArg[0] ) + instr = GoBack(DeleteInstruction(curr)); + // PSF, ChkRefS, RDS4 -> PshV4, CHKREF + else if( IsCombination(curr, asBC_PSF, asBC_ChkRefS) && + IsCombination(instr, asBC_ChkRefS, asBC_RDS4) ) + { + asASSERT( AS_PTR_SIZE == 1 ); + + // TODO: Pointer size + curr->op = asBC_PshV4; + instr->op = asBC_CHKREF; + DeleteInstruction(instr->next); + instr = GoBack(curr); + } + // PSF, ChkRefS, POP -> ChkNullV + else if( (IsCombination(curr, asBC_PSF, asBC_ChkRefS) && + IsCombination(instr, asBC_ChkRefS, asBC_POP) && + instr->next->wArg[0] >= AS_PTR_SIZE) ) + { + curr->op = asBC_ChkNullV; + curr->stackInc = 0; + // Decrease the number of DWORDs popped + instr->next->wArg[0] -= AS_PTR_SIZE; + // Delete the ChkRefS instruction + DeleteInstruction(instr); + instr = GoBack(curr); + } + // PshV4, CHKREF, POP -> ChkNullV + else if( (IsCombination(curr, asBC_PshV4, asBC_ChkRefS) && + IsCombination(instr, asBC_CHKREF, asBC_POP) && + instr->next->wArg[0] > 0) ) + { + asASSERT( AS_PTR_SIZE == 1 ); + + // TODO: Pointer size + curr->op = asBC_ChkNullV; + curr->stackInc = 0; + DeleteInstruction(instr->next); + DeleteInstruction(instr); + instr = GoBack(curr); + } + } + + return 0; +} + +bool asCByteCode::IsTempVarReadByInstr(cByteInstruction *curr, int offset) +{ + // Which instructions read from variables? + if( asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG && + (curr->wArg[1] == offset || curr->wArg[2] == offset) ) + return true; + else if( (asBCInfo[curr->op].type == asBCTYPE_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_rW_QW_ARG) && + curr->wArg[0] == offset ) + return true; + else if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_W_rW_ARG) && + curr->wArg[1] == offset ) + return true; + else if( asBCInfo[curr->op].type == asBCTYPE_rW_rW_ARG && + ((signed)curr->wArg[0] == offset || (signed)curr->wArg[1] == offset) ) + return true; + + return false; +} + +bool asCByteCode::IsInstrJmpOrLabel(cByteInstruction *curr) +{ + if( curr->op == asBC_JS || + curr->op == asBC_JNS || + curr->op == asBC_JP || + curr->op == asBC_JNP || + curr->op == asBC_JMPP || + curr->op == asBC_JMP || + curr->op == asBC_JZ || + curr->op == asBC_JNZ || + curr->op == asBC_LABEL ) + return true; + + return false; +} + +bool asCByteCode::IsTempVarOverwrittenByInstr(cByteInstruction *curr, int offset) +{ + // Which instructions overwrite the variable or discard it? + if( curr->op == asBC_RET || + curr->op == asBC_SUSPEND ) + return true; + else if( (asBCInfo[curr->op].type == asBCTYPE_wW_rW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_rW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_W_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_DW_ARG || + asBCInfo[curr->op].type == asBCTYPE_wW_QW_ARG) && + curr->wArg[0] == offset ) + return true; + + return false; +} + +bool asCByteCode::IsTempVarRead(cByteInstruction *curr, int offset) +{ + asCArray openPaths; + asCArray closedPaths; + + // We're not interested in the first instruction, since it is the one that sets the variable + openPaths.PushLast(curr->next); + + while( openPaths.GetLength() ) + { + curr = openPaths.PopLast(); + + // Add the instruction to the closed paths so that we don't verify it again + closedPaths.PushLast(curr); + + while( curr ) + { + if( IsTempVarReadByInstr(curr, offset) ) return true; + + if( IsTempVarOverwrittenByInstr(curr, offset) ) break; + + // In case of jumps, we must follow the each of the paths + if( curr->op == asBC_JMP ) + { + int label = *((int*)ARG_DW(curr->arg)); + int r = FindLabel(label, curr, &curr, 0); asASSERT( r == 0 ); UNUSED_VAR(r); + + if( !closedPaths.Exists(curr) && + !openPaths.Exists(curr) ) + openPaths.PushLast(curr); + + break; + } + else if( curr->op == asBC_JZ || curr->op == asBC_JNZ || + curr->op == asBC_JS || curr->op == asBC_JNS || + curr->op == asBC_JP || curr->op == asBC_JNP ) + { + cByteInstruction *dest = 0; + int label = *((int*)ARG_DW(curr->arg)); + int r = FindLabel(label, curr, &dest, 0); asASSERT( r == 0 ); UNUSED_VAR(r); + + if( !closedPaths.Exists(dest) && + !openPaths.Exists(dest) ) + openPaths.PushLast(dest); + } + // We cannot optimize over BC_JMPP + else if( curr->op == asBC_JMPP ) return true; + + curr = curr->next; + } + } + + return false; +} + +bool asCByteCode::IsTempRegUsed(cByteInstruction *curr) +{ + // We're not interested in the first instruction, since it is the one that sets the register + while( curr->next ) + { + curr = curr->next; + + // Which instructions read from the register? + if( curr->op == asBC_INCi || + curr->op == asBC_INCi16 || + curr->op == asBC_INCi8 || + curr->op == asBC_INCf || + curr->op == asBC_INCd || + curr->op == asBC_DECi || + curr->op == asBC_DECi16 || + curr->op == asBC_DECi8 || + curr->op == asBC_DECf || + curr->op == asBC_DECd || + curr->op == asBC_WRTV1 || + curr->op == asBC_WRTV2 || + curr->op == asBC_WRTV4 || + curr->op == asBC_WRTV8 || + curr->op == asBC_RDR1 || + curr->op == asBC_RDR2 || + curr->op == asBC_RDR4 || + curr->op == asBC_RDR8 || + curr->op == asBC_PshRPtr || + curr->op == asBC_CpyRtoV4 || + curr->op == asBC_CpyRtoV8 || + curr->op == asBC_TZ || + curr->op == asBC_TNZ || + curr->op == asBC_TS || + curr->op == asBC_TNS || + curr->op == asBC_TP || + curr->op == asBC_TNP || + curr->op == asBC_JZ || + curr->op == asBC_JNZ || + curr->op == asBC_JS || + curr->op == asBC_JNS || + curr->op == asBC_JP || + curr->op == asBC_JNP ) + return true; + + // Which instructions overwrite the register or discard the value? + if( curr->op == asBC_CALL || + curr->op == asBC_PopRPtr || + curr->op == asBC_CALLSYS || + curr->op == asBC_CALLBND || + curr->op == asBC_SUSPEND || + curr->op == asBC_ALLOC || + curr->op == asBC_FREE || + curr->op == asBC_CpyVtoR4 || + curr->op == asBC_LdGRdR4 || + curr->op == asBC_LDG || + curr->op == asBC_LDV || + curr->op == asBC_TZ || + curr->op == asBC_TNZ || + curr->op == asBC_TS || + curr->op == asBC_TNS || + curr->op == asBC_TP || + curr->op == asBC_TNP || + curr->op == asBC_JS || + curr->op == asBC_JNS || + curr->op == asBC_JP || + curr->op == asBC_JNP || + curr->op == asBC_JMPP || + curr->op == asBC_JMP || + curr->op == asBC_JZ || + curr->op == asBC_JNZ || + curr->op == asBC_CMPi || + curr->op == asBC_CMPu || + curr->op == asBC_CMPf || + curr->op == asBC_CMPd || + curr->op == asBC_CMPIi || + curr->op == asBC_CMPIu || + curr->op == asBC_CMPIf || + curr->op == asBC_LABEL ) + return false; + } + + return false; +} + +void asCByteCode::ExtractLineNumbers() +{ + int lastLinePos = -1; + int pos = 0; + cByteInstruction *instr = first; + while( instr ) + { + cByteInstruction *curr = instr; + instr = instr->next; + + if( curr->op == asBC_LINE ) + { + if( lastLinePos == pos ) + { + lineNumbers.PopLast(); + lineNumbers.PopLast(); + } + + lastLinePos = pos; + lineNumbers.PushLast(pos); + lineNumbers.PushLast(*(int*)ARG_DW(curr->arg)); + + if( !engine->ep.buildWithoutLineCues ) + { + // Transform BC_LINE into BC_SUSPEND + curr->op = asBC_SUSPEND; + curr->size = asBCTypeSize[asBCInfo[asBC_SUSPEND].type]; + pos += curr->size; + } + else + { + // Delete the instruction + DeleteInstruction(curr); + } + } + else + pos += curr->size; + } +} + +int asCByteCode::GetSize() +{ + int size = 0; + cByteInstruction *instr = first; + while( instr ) + { + size += instr->GetSize(); + + instr = instr->next; + } + + return size; +} + +void asCByteCode::AddCode(asCByteCode *bc) +{ + if( bc->first ) + { + if( first == 0 ) + { + first = bc->first; + last = bc->last; + bc->first = 0; + bc->last = 0; + } + else + { + last->next = bc->first; + bc->first->prev = last; + last = bc->last; + bc->first = 0; + bc->last = 0; + } + } +} + +int asCByteCode::AddInstruction() +{ + cByteInstruction *instr = new(engine->memoryMgr.AllocByteInstruction()) cByteInstruction(); + if( first == 0 ) + { + first = last = instr; + } + else + { + last->AddAfter(instr); + last = instr; + } + + return 0; +} + +int asCByteCode::AddInstructionFirst() +{ + cByteInstruction *instr = new(engine->memoryMgr.AllocByteInstruction()) cByteInstruction(); + if( first == 0 ) + { + first = last = instr; + } + else + { + first->AddBefore(instr); + first = instr; + } + + return 0; +} + +void asCByteCode::Call(asEBCInstr instr, int funcID, int pop) +{ + if( AddInstruction() < 0 ) + return; + + asASSERT(asBCInfo[instr].type == asBCTYPE_DW_ARG); + + last->op = instr; + last->size = asBCTypeSize[asBCInfo[instr].type]; + last->stackInc = -pop; // BC_CALL and BC_CALLBND doesn't pop the argument but when the callee returns the arguments are already popped + *((int*)ARG_DW(last->arg)) = funcID; + + // Add in a JitEntry instruction after function calls so that JIT's can resume execution + // TODO: Should this be done by the compiler? + InstrWORD(asBC_JitEntry, 0); +} + +void asCByteCode::Alloc(asEBCInstr instr, void *objID, int funcID, int pop) +{ + if( AddInstruction() < 0 ) + return; + + last->op = instr; + last->size = asBCTypeSize[asBCInfo[instr].type]; + last->stackInc = -pop; // BC_ALLOC + + asASSERT(asBCInfo[instr].type == asBCTYPE_PTR_DW_ARG); + *ARG_PTR(last->arg) = (asPTRWORD)(size_t)objID; + *((int*)(ARG_DW(last->arg)+AS_PTR_SIZE)) = funcID; + + // Add in a JitEntry instruction after function calls so that JIT's can resume execution + // TODO: Should this be done by the compiler? + InstrWORD(asBC_JitEntry, 0); +} + +void asCByteCode::Ret(int pop) +{ + if( AddInstruction() < 0 ) + return; + + asASSERT(asBCInfo[asBC_RET].type == asBCTYPE_W_ARG); + + last->op = asBC_RET; + last->size = asBCTypeSize[asBCInfo[asBC_RET].type]; + last->stackInc = 0; // The instruction pops the argument, but it doesn't affect current function + last->wArg[0] = (short)pop; +} + +void asCByteCode::JmpP(int var, asDWORD max) +{ + if( AddInstruction() < 0 ) + return; + + asASSERT(asBCInfo[asBC_JMPP].type == asBCTYPE_rW_ARG); + + last->op = asBC_JMPP; + last->size = asBCTypeSize[asBCInfo[asBC_JMPP].type]; + last->stackInc = asBCInfo[asBC_JMPP].stackInc; + last->wArg[0] = (short)var; + + // Store the largest jump that is made for PostProcess() + *ARG_DW(last->arg) = max; +} + +void asCByteCode::Label(short label) +{ + if( AddInstruction() < 0 ) + return; + + last->op = asBC_LABEL; + last->size = 0; + last->stackInc = 0; + last->wArg[0] = label; +} + +void asCByteCode::Line(int line, int column) +{ + if( AddInstruction() < 0 ) + return; + + last->op = asBC_LINE; + // If the build is without line cues these instructions will be removed + // otherwise they will be transformed into SUSPEND instructions. + if( engine->ep.buildWithoutLineCues ) + last->size = 0; + else + last->size = asBCTypeSize[asBCInfo[asBC_SUSPEND].type]; + last->stackInc = 0; + *((int*)ARG_DW(last->arg)) = (line & 0xFFFFF)|((column & 0xFFF)<<20); + + // Add a JitEntry after the line instruction to allow the JIT function to resume after a suspend + // TODO: Should this be added by the compiler? + InstrWORD(asBC_JitEntry, 0); +} + +int asCByteCode::FindLabel(int label, cByteInstruction *from, cByteInstruction **dest, int *positionDelta) +{ + // Search forward + int labelPos = -from->GetSize(); + + cByteInstruction *labelInstr = from; + while( labelInstr ) + { + labelPos += labelInstr->GetSize(); + labelInstr = labelInstr->next; + + if( labelInstr && labelInstr->op == asBC_LABEL ) + { + if( labelInstr->wArg[0] == label ) + break; + } + } + + if( labelInstr == 0 ) + { + // Search backwards + labelPos = -from->GetSize(); + + labelInstr = from; + while( labelInstr ) + { + labelInstr = labelInstr->prev; + if( labelInstr ) + { + labelPos -= labelInstr->GetSize(); + + if( labelInstr->op == asBC_LABEL ) + { + if( labelInstr->wArg[0] == label ) + break; + } + } + } + } + + if( labelInstr != 0 ) + { + if( dest ) *dest = labelInstr; + if( positionDelta ) *positionDelta = labelPos; + return 0; + } + + return -1; +} + +int asCByteCode::ResolveJumpAddresses() +{ + int pos = 0; + cByteInstruction *instr = first; + while( instr ) + { + // The program pointer is updated as the instruction is read + pos += instr->GetSize(); + + if( instr->op == asBC_JMP || + instr->op == asBC_JZ || instr->op == asBC_JNZ || + instr->op == asBC_JS || instr->op == asBC_JNS || + instr->op == asBC_JP || instr->op == asBC_JNP ) + { + int label = *((int*) ARG_DW(instr->arg)); + int labelPosOffset; + int r = FindLabel(label, instr, 0, &labelPosOffset); + if( r == 0 ) + *((int*) ARG_DW(instr->arg)) = labelPosOffset; + else + return -1; + } + + instr = instr->next; + } + + return 0; +} + + +cByteInstruction *asCByteCode::DeleteInstruction(cByteInstruction *instr) +{ + if( instr == 0 ) return 0; + + cByteInstruction *ret = instr->prev ? instr->prev : instr->next; + + RemoveInstruction(instr); + + engine->memoryMgr.FreeByteInstruction(instr); + + return ret; +} + +void asCByteCode::Output(asDWORD *array) +{ + // TODO: Receive a script function pointer + + // TODO: When arguments in a byte instruction are too large put them in the constant memory instead + // 4 byte arguments may remain in the instruction code for now. But move + // the 8 byte arguments to the constant memory + // Pointers will also be moved to the pointer array + + asDWORD *ap = array; + + cByteInstruction *instr = first; + while( instr ) + { + if( instr->GetSize() > 0 ) + { + *(asBYTE*)ap = asBYTE(instr->op); + *(((asBYTE*)ap)+1) = 0; // Second byte is always zero + switch( asBCInfo[instr->op].type ) + { + case asBCTYPE_NO_ARG: + *(((asWORD*)ap)+1) = 0; // Clear upper bytes + break; + case asBCTYPE_wW_rW_rW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(((asWORD*)ap)+2) = instr->wArg[1]; + *(((asWORD*)ap)+3) = instr->wArg[2]; + break; + case asBCTYPE_wW_DW_ARG: + case asBCTYPE_rW_DW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(ap+1) = *(asDWORD*)&instr->arg; + break; + case asBCTYPE_wW_rW_DW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(((asWORD*)ap)+2) = instr->wArg[1]; + *(ap+2) = *(asDWORD*)&instr->arg; + break; + case asBCTYPE_wW_QW_ARG: + case asBCTYPE_rW_QW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + *(asQWORD*)(ap+1) = asQWORD(instr->arg); + break; + case asBCTYPE_W_ARG: + case asBCTYPE_rW_ARG: + case asBCTYPE_wW_ARG: + *(((asWORD*)ap)+1) = instr->wArg[0]; + break; + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_rW_rW_ARG: + case asBCTYPE_W_rW_ARG: + case asBCTYPE_wW_W_ARG: + *(((asWORD *)ap)+1) = instr->wArg[0]; + *(((asWORD *)ap)+2) = instr->wArg[1]; + break; + case asBCTYPE_QW_DW_ARG: + case asBCTYPE_DW_DW_ARG: + case asBCTYPE_QW_ARG: + case asBCTYPE_DW_ARG: + *(((asWORD*)ap)+1) = 0; // Clear upper bytes + memcpy(ap+1, &instr->arg, instr->GetSize()*4-4); + break; + default: + // How did we get here? + asASSERT(false); + break; + } + } + + ap += instr->GetSize(); + instr = instr->next; + } +} + +void asCByteCode::PostProcess() +{ + if( first == 0 ) return; + + // This function will do the following + // - Verify if there is any code that never gets executed and remove it + // - Calculate the stack size at the position of each byte code + // - Calculate the largest stack needed + + largestStackUsed = 0; + + cByteInstruction *instr = first; + while( instr ) + { + instr->marked = false; + instr->stackSize = -1; + instr = instr->next; + } + + // Add the first instruction to the list of unchecked code paths + asCArray paths; + AddPath(paths, first, 0); + + // Go through each of the code paths + for( asUINT p = 0; p < paths.GetLength(); ++p ) + { + instr = paths[p]; + int stackSize = instr->stackSize; + + while( instr ) + { + instr->marked = true; + instr->stackSize = stackSize; + stackSize += instr->stackInc; + if( stackSize > largestStackUsed ) + largestStackUsed = stackSize; + + // PSP -> PSF + if( instr->op == asBC_PSP ) + { + instr->op = asBC_PSF; + instr->wArg[0] = instr->wArg[0] + (short)instr->stackSize; + } + + if( instr->op == asBC_JMP ) + { + // Find the label that we should jump to + int label = *((int*) ARG_DW(instr->arg)); + cByteInstruction *dest = 0; + int r = FindLabel(label, instr, &dest, 0); asASSERT( r == 0 ); UNUSED_VAR(r); + + AddPath(paths, dest, stackSize); + break; + } + else if( instr->op == asBC_JZ || instr->op == asBC_JNZ || + instr->op == asBC_JS || instr->op == asBC_JNS || + instr->op == asBC_JP || instr->op == asBC_JNP ) + { + // Find the label that is being jumped to + int label = *((int*) ARG_DW(instr->arg)); + cByteInstruction *dest = 0; + int r = FindLabel(label, instr, &dest, 0); asASSERT( r == 0 ); UNUSED_VAR(r); + + AddPath(paths, dest, stackSize); + + // Add both paths to the code paths + AddPath(paths, instr->next, stackSize); + + break; + } + else if( instr->op == asBC_JMPP ) + { + // I need to know the largest value possible + asDWORD max = *ARG_DW(instr->arg); + + // Add all destinations to the code paths + cByteInstruction *dest = instr->next; + for( asDWORD n = 0; n <= max && dest != 0; ++n ) + { + AddPath(paths, dest, stackSize); + dest = dest->next; + } + + break; + } + else + { + instr = instr->next; + if( instr == 0 || instr->marked ) + break; + } + } + } + + // Are there any instructions that didn't get visited? + instr = first; + while( instr ) + { + if( instr->marked == false ) + { + // TODO: Give warning of unvisited code + + // Remove it + cByteInstruction *curr = instr; + instr = instr->next; + DeleteInstruction(curr); + } + else + instr = instr->next; + } +} + +#ifdef AS_DEBUG +void asCByteCode::DebugOutput(const char *name, asCScriptEngine *engine) +{ + _mkdir("AS_DEBUG"); + + asCString str = "AS_DEBUG/"; + str += name; + +#if _MSC_VER >= 1500 + FILE *file; + fopen_s(&file, str.AddressOf(), "w"); +#else + FILE *file = fopen(str.AddressOf(), "w"); +#endif + + fprintf(file, "Temps: "); + for( asUINT n = 0; n < temporaryVariables.GetLength(); n++ ) + { + fprintf(file, "%d", temporaryVariables[n]); + if( n < temporaryVariables.GetLength()-1 ) + fprintf(file, ", "); + } + fprintf(file, "\n\n"); + + int pos = 0; + asUINT lineIndex = 0; + cByteInstruction *instr = first; + while( instr ) + { + if( lineIndex < lineNumbers.GetLength() && lineNumbers[lineIndex] == pos ) + { + asDWORD line = lineNumbers[lineIndex+1]; + fprintf(file, "- %d,%d -\n", (int)(line&0xFFFFF), (int)(line>>20)); + lineIndex += 2; + } + + fprintf(file, "%5d ", pos); + pos += instr->GetSize(); + + fprintf(file, "%3d %c ", instr->stackSize, instr->marked ? '*' : ' '); + + switch( asBCInfo[instr->op].type ) + { + case asBCTYPE_W_ARG: + if( instr->op == asBC_STR ) + { + int id = instr->wArg[0]; + const asCString &str = engine->GetConstantString(id); + fprintf(file, " %-8s %d (l:%ld s:\"%.10s\")\n", asBCInfo[instr->op].name, instr->wArg[0], (long int)str.GetLength(), str.AddressOf()); + } + else + fprintf(file, " %-8s %d\n", asBCInfo[instr->op].name, instr->wArg[0]); + break; + + case asBCTYPE_wW_ARG: + case asBCTYPE_rW_ARG: + fprintf(file, " %-8s v%d\n", asBCInfo[instr->op].name, instr->wArg[0]); + break; + + case asBCTYPE_wW_rW_ARG: + case asBCTYPE_rW_rW_ARG: + fprintf(file, " %-8s v%d, v%d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1]); + break; + + case asBCTYPE_W_rW_ARG: + fprintf(file, " %-8s %d, v%d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1]); + break; + + case asBCTYPE_wW_W_ARG: + fprintf(file, " %-8s v%d, %d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1]); + break; + + case asBCTYPE_wW_rW_DW_ARG: + switch( instr->op ) + { + case asBC_ADDIf: + case asBC_SUBIf: + case asBC_MULIf: + fprintf(file, " %-8s v%d, v%d, %f\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1], *((float*) ARG_DW(instr->arg))); + break; + default: + fprintf(file, " %-8s v%d, v%d, %d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1], *((int*) ARG_DW(instr->arg))); + break; + } + break; + + case asBCTYPE_DW_ARG: + switch( instr->op ) + { + case asBC_OBJTYPE: + fprintf(file, " %-8s 0x%x\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg)); + break; + + case asBC_PshC4: + case asBC_Cast: + fprintf(file, " %-8s 0x%x (i:%d, f:%g)\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), *((int*) ARG_DW(instr->arg)), *((float*) ARG_DW(instr->arg))); + break; + + case asBC_TYPEID: + fprintf(file, " %-8s 0x%x '%s'\n", asBCInfo[instr->op].name, (asUINT)*ARG_DW(instr->arg), engine->GetTypeDeclaration((int)*ARG_DW(instr->arg))); + break; + + case asBC_CALL: + case asBC_CALLSYS: + case asBC_CALLBND: + case asBC_CALLINTF: + { + int funcID = *(int*)ARG_DW(instr->arg); + asCString decl = engine->GetFunctionDeclaration(funcID); + + fprintf(file, " %-8s %d (%s)\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg)), decl.AddressOf()); + } + break; + + case asBC_FREE: + case asBC_REFCPY: + fprintf(file, " %-8s 0x%x\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg))); + break; + + case asBC_JMP: + case asBC_JZ: + case asBC_JS: + case asBC_JP: + case asBC_JNZ: + case asBC_JNS: + case asBC_JNP: + fprintf(file, " %-8s %+d (d:%d)\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg)), pos+*((int*) ARG_DW(instr->arg))); + break; + + default: + fprintf(file, " %-8s %d\n", asBCInfo[instr->op].name, *((int*) ARG_DW(instr->arg))); + break; + } + break; + + case asBCTYPE_QW_ARG: +#ifdef __GNUC__ +#ifdef _LP64 + fprintf(file, " %-8s 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#else + fprintf(file, " %-8s 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif +#else + fprintf(file, " %-8s 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif + break; + + case asBCTYPE_wW_QW_ARG: + case asBCTYPE_rW_QW_ARG: +#ifdef __GNUC__ +#ifdef _LP64 + fprintf(file, " %-8s v%d, 0x%lx (i:%ld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#else + fprintf(file, " %-8s v%d, 0x%llx (i:%lld, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif +#else + fprintf(file, " %-8s v%d, 0x%I64x (i:%I64d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], *ARG_QW(instr->arg), *((asINT64*) ARG_QW(instr->arg)), *((double*) ARG_QW(instr->arg))); +#endif + break; + + case asBCTYPE_DW_DW_ARG: + if( instr->op == asBC_ALLOC ) + { + asCObjectType *ot = *(asCObjectType**)ARG_DW(instr->arg); + fprintf(file, " %-8s 0x%x, %d (type:%s)\n", asBCInfo[instr->op].name, *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1), ot->GetName()); + } + else + fprintf(file, " %-8s %u, %d\n", asBCInfo[instr->op].name, *(int*)ARG_DW(instr->arg), *(int*)(ARG_DW(instr->arg)+1)); + break; + + case asBCTYPE_QW_DW_ARG: + if( instr->op == asBC_ALLOC ) + { + asCObjectType *ot = *(asCObjectType**)ARG_QW(instr->arg); +#ifdef __GNUC__ +#ifdef AS_64BIT_PTR + fprintf(file, " %-8s 0x%lx, %d (type:%s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName()); +#else + fprintf(file, " %-8s 0x%llx, %d (type:%s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName()); +#endif +#else + fprintf(file, " %-8s 0x%I64x, %d (type:%s)\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2), ot->GetName()); +#endif + } + else +#ifdef __GNUC__ +#ifdef AS_64BIT_PTR + fprintf(file, " %-8s %lu, %d\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2)); +#else + fprintf(file, " %-8s %llu, %d\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2)); +#endif +#else + fprintf(file, " %-8s %I64u, %d\n", asBCInfo[instr->op].name, *(asINT64*)ARG_QW(instr->arg), *(int*)(ARG_DW(instr->arg)+2)); +#endif + break; + + case asBCTYPE_INFO: + if( instr->op == asBC_LABEL ) + fprintf(file, "%d:\n", instr->wArg[0]); + else + fprintf(file, " %s\n", asBCInfo[instr->op].name); + break; + + case asBCTYPE_rW_DW_ARG: + case asBCTYPE_wW_DW_ARG: + if( instr->op == asBC_SetV1 ) + fprintf(file, " %-8s v%d, 0x%x\n", asBCInfo[instr->op].name, instr->wArg[0], *(asBYTE*)ARG_DW(instr->arg)); + else if( instr->op == asBC_SetV2 ) + fprintf(file, " %-8s v%d, 0x%x\n", asBCInfo[instr->op].name, instr->wArg[0], *(asWORD*)ARG_DW(instr->arg)); + else if( instr->op == asBC_SetV4 ) + fprintf(file, " %-8s v%d, 0x%x (i:%d, f:%g)\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_DW(instr->arg), *((int*) ARG_DW(instr->arg)), *((float*) ARG_DW(instr->arg))); + else if( instr->op == asBC_CMPIf ) + fprintf(file, " %-8s v%d, %f\n", asBCInfo[instr->op].name, instr->wArg[0], *(float*)ARG_DW(instr->arg)); + else + fprintf(file, " %-8s v%d, %d\n", asBCInfo[instr->op].name, instr->wArg[0], (asUINT)*ARG_DW(instr->arg)); + break; + + case asBCTYPE_wW_rW_rW_ARG: + fprintf(file, " %-8s v%d, v%d, v%d\n", asBCInfo[instr->op].name, instr->wArg[0], instr->wArg[1], instr->wArg[2]); + break; + + case asBCTYPE_NO_ARG: + fprintf(file, " %s\n", asBCInfo[instr->op].name); + break; + + default: + asASSERT(false); + } + + instr = instr->next; + } + + fclose(file); +} +#endif + +//============================================================================= + +// Decrease stack with "numDwords" +int asCByteCode::Pop(int numDwords) +{ + asASSERT(asBCInfo[asBC_POP].type == asBCTYPE_W_ARG); + + if( AddInstruction() < 0 ) + return 0; + + last->op = asBC_POP; + last->wArg[0] = (short)numDwords; + last->size = asBCTypeSize[asBCInfo[asBC_POP].type]; + last->stackInc = -numDwords; + + return last->stackInc; +} + +// Increase stack with "numDwords" +int asCByteCode::Push(int numDwords) +{ + asASSERT(asBCInfo[asBC_PUSH].type == asBCTYPE_W_ARG); + + if( AddInstruction() < 0 ) + return 0; + + last->op = asBC_PUSH; + last->wArg[0] = (short)numDwords; + last->size = asBCTypeSize[asBCInfo[asBC_PUSH].type]; + last->stackInc = numDwords; + + return last->stackInc; +} + + +int asCByteCode::InsertFirstInstrDWORD(asEBCInstr bc, asDWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstructionFirst() < 0 ) + return 0; + + first->op = bc; + *ARG_DW(first->arg) = param; + first->size = asBCTypeSize[asBCInfo[bc].type]; + first->stackInc = asBCInfo[bc].stackInc; + + return first->stackInc; +} + +int asCByteCode::InsertFirstInstrQWORD(asEBCInstr bc, asQWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_QW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstructionFirst() < 0 ) + return 0; + + first->op = bc; + *ARG_QW(first->arg) = param; + first->size = asBCTypeSize[asBCInfo[bc].type]; + first->stackInc = asBCInfo[bc].stackInc; + + return first->stackInc; +} + +int asCByteCode::Instr(asEBCInstr bc) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_NO_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_W_W(asEBCInstr bc, int a, int b, int c) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_rW_rW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = (short)a; + last->wArg[1] = (short)b; + last->wArg[2] = (short)c; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_W(asEBCInstr bc, int a, int b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_rW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_rW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = (short)a; + last->wArg[1] = (short)b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_DW(asEBCInstr bc, asWORD a, asDWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *((int*) ARG_DW(last->arg)) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_B(asEBCInstr bc, short a, asBYTE b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + + // We'll have to be careful to store the byte correctly, independent of endianess. + // Some optimizing compilers may change the order of operations, so we make sure + // the value is not overwritten even if that happens. + asBYTE *argPtr = (asBYTE*)ARG_DW(last->arg); + argPtr[0] = b; // The value is always stored in the lower byte + argPtr[1] = 0; // and clear the rest of the DWORD + argPtr[2] = 0; + argPtr[3] = 0; + + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_W(asEBCInstr bc, short a, asWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + + // We'll have to be careful to store the word correctly, independent of endianess. + // Some optimizing compilers may change the order of operations, so we make sure + // the value is not overwritten even if that happens. + asWORD *argPtr = (asWORD*)ARG_DW(last->arg); + argPtr[0] = b; // The value is always stored in the lower word + argPtr[1] = 0; // and clear the rest of the DWORD + + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_DW(asEBCInstr bc, short a, asDWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG || + asBCInfo[bc].type == asBCTYPE_rW_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *((int*) ARG_DW(last->arg)) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_QW(asEBCInstr bc, asWORD a, asQWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_QW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *ARG_QW(last->arg) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT_QW(asEBCInstr bc, short a, asQWORD b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_QW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *ARG_QW(last->arg) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrW_FLOAT(asEBCInstr bc, asWORD a, float b) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_wW_DW_ARG); + asASSERT(asBCInfo[bc].stackInc == 0); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = a; + *((float*) ARG_DW(last->arg)) = b; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrSHORT(asEBCInstr bc, short param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_rW_ARG || + asBCInfo[bc].type == asBCTYPE_wW_ARG || + asBCInfo[bc].type == asBCTYPE_W_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrINT(asEBCInstr bc, int param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *((int*) ARG_DW(last->arg)) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrDWORD(asEBCInstr bc, asDWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *ARG_DW(last->arg) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrPTR(asEBCInstr bc, void *param) +{ + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + asASSERT(asBCInfo[bc].type == asBCTYPE_PTR_ARG); + *ARG_PTR(last->arg) = (asPTRWORD)(size_t)param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrQWORD(asEBCInstr bc, asQWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_QW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *ARG_QW(last->arg) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrWORD(asEBCInstr bc, asWORD param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_W_ARG || + asBCInfo[bc].type == asBCTYPE_rW_ARG || + asBCInfo[bc].type == asBCTYPE_wW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + last->wArg[0] = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrFLOAT(asEBCInstr bc, float param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_DW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *((float*) ARG_DW(last->arg)) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::InstrDOUBLE(asEBCInstr bc, double param) +{ + asASSERT(asBCInfo[bc].type == asBCTYPE_QW_ARG); + asASSERT(asBCInfo[bc].stackInc != 0xFFFF); + + if( AddInstruction() < 0 ) + return 0; + + last->op = bc; + *((double*) ARG_QW(last->arg)) = param; + last->size = asBCTypeSize[asBCInfo[bc].type]; + last->stackInc = asBCInfo[bc].stackInc; + + return last->stackInc; +} + +int asCByteCode::GetLastInstr() +{ + if( last == 0 ) return -1; + + return last->op; +} + +int asCByteCode::RemoveLastInstr() +{ + if( last == 0 ) return -1; + + if( first == last ) + { + engine->memoryMgr.FreeByteInstruction(last); + first = 0; + last = 0; + } + else + { + cByteInstruction *bc = last; + last = bc->prev; + + bc->Remove(); + engine->memoryMgr.FreeByteInstruction(bc); + } + + return 0; +} + +asDWORD asCByteCode::GetLastInstrValueDW() +{ + if( last == 0 ) return 0; + + return *ARG_DW(last->arg); +} + +void asCByteCode::DefineTemporaryVariable(int varOffset) +{ + temporaryVariables.PushLast(varOffset); +} + +//=================================================================== + +cByteInstruction::cByteInstruction() +{ + next = 0; + prev = 0; + + op = asBC_LABEL; + + arg = 0; + wArg[0] = 0; + wArg[1] = 0; + wArg[2] = 0; + size = 0; + stackInc = 0; + marked = false; + stackSize = 0; +} + +void cByteInstruction::AddAfter(cByteInstruction *nextCode) +{ + if( next ) + next->prev = nextCode; + + nextCode->next = next; + nextCode->prev = this; + next = nextCode; +} + +void cByteInstruction::AddBefore(cByteInstruction *prevCode) +{ + if( prev ) + prev->next = prevCode; + + prevCode->prev = prev; + prevCode->next = this; + prev = prevCode; +} + +int cByteInstruction::GetSize() +{ + return size; +} + +int cByteInstruction::GetStackIncrease() +{ + return stackInc; +} + +void cByteInstruction::Remove() +{ + if( prev ) prev->next = next; + if( next ) next->prev = prev; + prev = 0; + next = 0; +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_bytecode.h b/AngelScript/source/as_bytecode.h new file mode 100644 index 000000000..29c965b45 --- /dev/null +++ b/AngelScript/source/as_bytecode.h @@ -0,0 +1,187 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_bytecode.h +// +// A class for constructing the final byte code +// + + + +#ifndef AS_BYTECODE_H +#define AS_BYTECODE_H + +#include "as_config.h" +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +#define BYTECODE_SIZE 4 +#define MAX_DATA_SIZE 8 +#define MAX_INSTR_SIZE (BYTECODE_SIZE+MAX_DATA_SIZE) + +class asCScriptEngine; +class cByteInstruction; + +class asCByteCode +{ +public: + asCByteCode(asCScriptEngine *engine); + ~asCByteCode(); + + void ClearAll(); + + int GetSize(); + + void Finalize(); + + int Optimize(); + void ExtractLineNumbers(); + int ResolveJumpAddresses(); + int FindLabel(int label, cByteInstruction *from, cByteInstruction **dest, int *positionDelta); + + void AddPath(asCArray &paths, cByteInstruction *instr, int stackSize); + + void Output(asDWORD *array); + void AddCode(asCByteCode *bc); + + void PostProcess(); + +#ifdef AS_DEBUG + void DebugOutput(const char *name, asCScriptEngine *engine); +#endif + + int GetLastInstr(); + int RemoveLastInstr(); + asDWORD GetLastInstrValueDW(); + + void InsertIfNotExists(asCArray &vars, int var); + void GetVarsUsed(asCArray &vars); + bool IsVarUsed(int offset); + void ExchangeVar(int oldOffset, int newOffset); + + void Label(short label); + void Line(int line, int column); + void Call(asEBCInstr bc, int funcID, int pop); + void Alloc(asEBCInstr bc, void *objID, int funcID, int pop); + void Ret(int pop); + void JmpP(int var, asDWORD max); + + int InsertFirstInstrDWORD(asEBCInstr bc, asDWORD param); + int InsertFirstInstrQWORD(asEBCInstr bc, asQWORD param); + int Instr(asEBCInstr bc); + int InstrQWORD(asEBCInstr bc, asQWORD param); + int InstrDOUBLE(asEBCInstr bc, double param); + int InstrPTR(asEBCInstr bc, void *param); + int InstrDWORD(asEBCInstr bc, asDWORD param); + int InstrWORD(asEBCInstr bc, asWORD param); + int InstrSHORT(asEBCInstr bc, short param); + int InstrFLOAT(asEBCInstr bc, float param); + int InstrINT(asEBCInstr bc, int param); + int InstrW_W_W(asEBCInstr bc, int a, int b, int c); + int InstrSHORT_B(asEBCInstr bc, short a, asBYTE b); + int InstrSHORT_W(asEBCInstr bc, short a, asWORD b); + int InstrSHORT_DW(asEBCInstr bc, short a, asDWORD b); + int InstrSHORT_QW(asEBCInstr bc, short a, asQWORD b); + int InstrW_DW(asEBCInstr bc, asWORD a, asDWORD b); + int InstrW_QW(asEBCInstr bc, asWORD a, asQWORD b); + int InstrW_FLOAT(asEBCInstr bc, asWORD a, float b); + int InstrW_W(asEBCInstr bc, int w, int b); + + int Pop (int numDwords); + int Push(int numDwords); + + asCArray lineNumbers; + int largestStackUsed; + + void DefineTemporaryVariable(int varOffset); + +protected: + // Helpers for Optimize + bool CanBeSwapped(cByteInstruction *curr); + bool IsCombination(cByteInstruction *curr, asEBCInstr bc1, asEBCInstr bc2); + bool IsCombination(cByteInstruction *curr, asEBCInstr bc1, asEBCInstr bc2, asEBCInstr bc3); + cByteInstruction *ChangeFirstDeleteNext(cByteInstruction *curr, asEBCInstr bc); + cByteInstruction *DeleteFirstChangeNext(cByteInstruction *curr, asEBCInstr bc); + cByteInstruction *DeleteInstruction(cByteInstruction *instr); + void RemoveInstruction(cByteInstruction *instr); + cByteInstruction *GoBack(cByteInstruction *curr); + void InsertBefore(cByteInstruction *before, cByteInstruction *instr); + bool RemoveUnusedValue(cByteInstruction *curr, cByteInstruction **next); + bool IsTemporary(short offset); + bool IsTempRegUsed(cByteInstruction *curr); + bool IsTempVarRead(cByteInstruction *curr, int offset); + bool PostponeInitOfTemp(cByteInstruction *curr, cByteInstruction **next); + bool IsTempVarReadByInstr(cByteInstruction *curr, int var); + bool IsTempVarOverwrittenByInstr(cByteInstruction *curr, int var); + bool IsInstrJmpOrLabel(cByteInstruction *curr); + + int AddInstruction(); + int AddInstructionFirst(); + + cByteInstruction *first; + cByteInstruction *last; + + asCArray temporaryVariables; + + asCScriptEngine *engine; +}; + +class cByteInstruction +{ +public: + cByteInstruction(); + + void AddAfter(cByteInstruction *nextCode); + void AddBefore(cByteInstruction *nextCode); + void Remove(); + + int GetSize(); + int GetStackIncrease(); + + cByteInstruction *next; + cByteInstruction *prev; + + asEBCInstr op; + asQWORD arg; + short wArg[3]; + int size; + int stackInc; + + // Testing + bool marked; + int stackSize; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_callfunc.cpp b/AngelScript/source/as_callfunc.cpp new file mode 100644 index 000000000..01aee5b49 --- /dev/null +++ b/AngelScript/source/as_callfunc.cpp @@ -0,0 +1,342 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc.cpp +// +// These functions handle the actual calling of system functions +// + + + +#include "as_config.h" +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, asSSystemFunctionInterface *internal) +{ + memset(internal, 0, sizeof(asSSystemFunctionInterface)); + + internal->func = (size_t)ptr.ptr.f.func; + + // Was a compatible calling convention specified? + if( internal->func ) + { + if( ptr.flag == 1 && callConv != asCALL_GENERIC ) + return asWRONG_CALLING_CONV; + else if( ptr.flag == 2 && (callConv == asCALL_GENERIC || callConv == asCALL_THISCALL) ) + return asWRONG_CALLING_CONV; + else if( ptr.flag == 3 && callConv != asCALL_THISCALL ) + return asWRONG_CALLING_CONV; + } + + asDWORD base = callConv; + if( !isMethod ) + { + if( base == asCALL_CDECL ) + internal->callConv = ICC_CDECL; + else if( base == asCALL_STDCALL ) + internal->callConv = ICC_STDCALL; + else if( base == asCALL_GENERIC ) + internal->callConv = ICC_GENERIC_FUNC; + else + return asNOT_SUPPORTED; + } + else + { +#ifndef AS_NO_CLASS_METHODS + if( base == asCALL_THISCALL ) + { + internal->callConv = ICC_THISCALL; +#ifdef GNU_STYLE_VIRTUAL_METHOD + if( (size_t(ptr.ptr.f.func) & 1) ) + internal->callConv = ICC_VIRTUAL_THISCALL; +#endif + internal->baseOffset = ( int )MULTI_BASE_OFFSET(ptr); +#if defined(AS_ARM) && defined(__GNUC__) + // As the least significant bit in func is used to switch to THUMB mode + // on ARM processors, the LSB in the __delta variable is used instead of + // the one in __pfn on ARM processors. + if( (size_t(internal->baseOffset) & 1) ) + internal->callConv = ICC_VIRTUAL_THISCALL; +#endif + +#ifdef HAVE_VIRTUAL_BASE_OFFSET + // We don't support virtual inheritance + if( VIRTUAL_BASE_OFFSET(ptr) != 0 ) + return asNOT_SUPPORTED; +#endif + } + else +#endif + if( base == asCALL_CDECL_OBJLAST ) + internal->callConv = ICC_CDECL_OBJLAST; + else if( base == asCALL_CDECL_OBJFIRST ) + internal->callConv = ICC_CDECL_OBJFIRST; + else if( base == asCALL_GENERIC ) + internal->callConv = ICC_GENERIC_METHOD; + else + return asNOT_SUPPORTED; + } + + return 0; +} + +// This function should prepare system functions so that it will be faster to call them +int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine * /*engine*/) +{ + asASSERT(internal->callConv == ICC_GENERIC_METHOD || internal->callConv == ICC_GENERIC_FUNC); + + // Calculate the size needed for the parameters + internal->paramSize = func->GetSpaceNeededForArguments(); + + return 0; +} + +// This function should prepare system functions so that it will be faster to call them +int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine) +{ +#ifdef AS_MAX_PORTABILITY + // This should never happen, as when AS_MAX_PORTABILITY is on, all functions + // are asCALL_GENERIC, which are prepared by PrepareSystemFunctionGeneric + asASSERT(false); +#endif + + // References are always returned as primitive data + if( func->returnType.IsReference() || func->returnType.IsObjectHandle() ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = sizeof(void*)/4; + internal->hostReturnFloat = false; + } + // Registered types have special flags that determine how they are returned + else if( func->returnType.IsObject() ) + { + asDWORD objType = func->returnType.GetObjectType()->flags; + + // Only value types can be returned by value + asASSERT( objType & asOBJ_VALUE ); + + if( !(objType & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT)) ) + { + // If the return is by value then we need to know the true type + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); + + asCString str; + str.Format(TXT_CANNOT_RET_TYPE_s_BY_VAL, func->returnType.GetObjectType()->name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + engine->ConfigError(asINVALID_CONFIGURATION); + } + else if( objType & asOBJ_APP_CLASS ) + { + internal->hostReturnFloat = false; + if( objType & COMPLEX_MASK ) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } + else + { +#ifdef HAS_128_BIT_PRIMITIVES + if( func->returnType.GetSizeInMemoryDWords() > 4 ) +#else + if( func->returnType.GetSizeInMemoryDWords() > 2 ) +#endif + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } + else + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords(); + } + +#ifdef THISCALL_RETURN_SIMPLE_IN_MEMORY + if((internal->callConv == ICC_THISCALL || + internal->callConv == ICC_VIRTUAL_THISCALL) && + func->returnType.GetSizeInMemoryDWords() >= THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } +#endif +#ifdef CDECL_RETURN_SIMPLE_IN_MEMORY + if((internal->callConv == ICC_CDECL || + internal->callConv == ICC_CDECL_OBJLAST || + internal->callConv == ICC_CDECL_OBJFIRST) && + func->returnType.GetSizeInMemoryDWords() >= CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } +#endif +#ifdef STDCALL_RETURN_SIMPLE_IN_MEMORY + if( internal->callConv == ICC_STDCALL && + func->returnType.GetSizeInMemoryDWords() >= STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE) + { + internal->hostReturnInMemory = true; + internal->hostReturnSize = sizeof(void*)/4; + } +#endif + } + } + else if( objType & asOBJ_APP_PRIMITIVE ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords(); + internal->hostReturnFloat = false; + } + else if( objType & asOBJ_APP_FLOAT ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = func->returnType.GetSizeInMemoryDWords(); + internal->hostReturnFloat = true; + } + } + // Primitive types can easily be determined +#ifdef HAS_128_BIT_PRIMITIVES + else if( func->returnType.GetSizeInMemoryDWords() > 4 ) + { + // Shouldn't be possible to get here + asASSERT(false); + } + else if( func->returnType.GetSizeInMemoryDWords() == 4 ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 4; + internal->hostReturnFloat = false; + } +#else + else if( func->returnType.GetSizeInMemoryDWords() > 2 ) + { + // Shouldn't be possible to get here + asASSERT(false); + } +#endif + else if( func->returnType.GetSizeInMemoryDWords() == 2 ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 2; + internal->hostReturnFloat = func->returnType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttDouble, true)); + } + else if( func->returnType.GetSizeInMemoryDWords() == 1 ) + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 1; + internal->hostReturnFloat = func->returnType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttFloat, true)); + } + else + { + internal->hostReturnInMemory = false; + internal->hostReturnSize = 0; + internal->hostReturnFloat = false; + } + + // Calculate the size needed for the parameters + internal->paramSize = func->GetSpaceNeededForArguments(); + + // Verify if the function takes any objects by value + asUINT n; + internal->takesObjByVal = false; + for( n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( func->parameterTypes[n].IsObject() && !func->parameterTypes[n].IsObjectHandle() && !func->parameterTypes[n].IsReference() ) + { + internal->takesObjByVal = true; + + // Can't pass objects by value unless the application type is informed + if( !(func->parameterTypes[n].GetObjectType()->flags & (asOBJ_APP_CLASS | asOBJ_APP_PRIMITIVE | asOBJ_APP_FLOAT)) ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); + + asCString str; + str.Format(TXT_CANNOT_PASS_TYPE_s_BY_VAL, func->parameterTypes[n].GetObjectType()->name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + engine->ConfigError(asINVALID_CONFIGURATION); + } + + +#ifdef SPLIT_OBJS_BY_MEMBER_TYPES + // It's not safe to pass objects by value because different registers + // will be used depending on the memory layout of the object +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( !(func->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) ) +#endif + { + engine->WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, func->GetDeclarationStr().AddressOf()); + + asCString str; + str.Format(TXT_DONT_SUPPORT_TYPE_s_BY_VAL, func->parameterTypes[n].GetObjectType()->name.AddressOf()); + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + engine->ConfigError(asINVALID_CONFIGURATION); + } +#endif + break; + } + } + + // Verify if the function has any registered autohandles + internal->hasAutoHandles = false; + for( n = 0; n < internal->paramAutoHandles.GetLength(); n++ ) + { + if( internal->paramAutoHandles[n] ) + { + internal->hasAutoHandles = true; + break; + } + } + + return 0; +} + +#ifdef AS_MAX_PORTABILITY + +int CallSystemFunction(int id, asCContext *context, void *objectPointer) +{ + asCScriptEngine *engine = context->engine; + asSSystemFunctionInterface *sysFunc = engine->scriptFunctions[id]->sysFuncIntf; + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + return context->CallGeneric(id, objectPointer); + + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + + return 0; +} + +#endif // AS_MAX_PORTABILITY + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_callfunc.h b/AngelScript/source/as_callfunc.h new file mode 100644 index 000000000..16eab6672 --- /dev/null +++ b/AngelScript/source/as_callfunc.h @@ -0,0 +1,122 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc.h +// +// These functions handle the actual calling of system functions +// + + +#ifndef AS_CALLFUNC_H +#define AS_CALLFUNC_H + +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +class asCContext; +class asCScriptEngine; +class asCScriptFunction; +struct asSSystemFunctionInterface; + +int DetectCallingConvention(bool isMethod, const asSFuncPtr &ptr, int callConv, asSSystemFunctionInterface *internal); + +int PrepareSystemFunctionGeneric(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); + +int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); + +int CallSystemFunction(int id, asCContext *context, void *objectPointer); + +enum internalCallConv +{ + ICC_GENERIC_FUNC, + ICC_GENERIC_FUNC_RETURNINMEM, // never used + ICC_CDECL, + ICC_CDECL_RETURNINMEM, + ICC_STDCALL, + ICC_STDCALL_RETURNINMEM, + ICC_THISCALL, + ICC_THISCALL_RETURNINMEM, + ICC_VIRTUAL_THISCALL, + ICC_VIRTUAL_THISCALL_RETURNINMEM, + ICC_CDECL_OBJLAST, + ICC_CDECL_OBJLAST_RETURNINMEM, + ICC_CDECL_OBJFIRST, + ICC_CDECL_OBJFIRST_RETURNINMEM, + ICC_GENERIC_METHOD, + ICC_GENERIC_METHOD_RETURNINMEM // never used +}; + +struct asSSystemFunctionInterface +{ + size_t func; + int baseOffset; + internalCallConv callConv; + int scriptReturnSize; + bool hostReturnInMemory; + bool hostReturnFloat; + int hostReturnSize; + int paramSize; + bool takesObjByVal; + asCArray paramAutoHandles; + bool returnAutoHandle; + bool hasAutoHandles; + + asSSystemFunctionInterface() {} + + asSSystemFunctionInterface(const asSSystemFunctionInterface &in) + { + *this = in; + } + + asSSystemFunctionInterface &operator=(const asSSystemFunctionInterface &in) + { + func = in.func; + baseOffset = in.baseOffset; + callConv = in.callConv; + scriptReturnSize = in.scriptReturnSize; + hostReturnInMemory = in.hostReturnInMemory; + hostReturnFloat = in.hostReturnFloat; + hostReturnSize = in.hostReturnSize; + paramSize = in.paramSize; + takesObjByVal = in.takesObjByVal; + paramAutoHandles = in.paramAutoHandles; + returnAutoHandle = in.returnAutoHandle; + hasAutoHandles = in.hasAutoHandles; + return *this; + } +}; + +END_AS_NAMESPACE + +#endif + diff --git a/AngelScript/source/as_callfunc_arm.cpp b/AngelScript/source/as_callfunc_arm.cpp new file mode 100644 index 000000000..1698fba19 --- /dev/null +++ b/AngelScript/source/as_callfunc_arm.cpp @@ -0,0 +1,344 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_arm.cpp +// +// These functions handle the actual calling of system functions on the arm platform +// +// Written by Fredrik Ehnbom in June 2009, based on as_callfunc_x86.cpp + + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_ARM + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" + +BEGIN_AS_NAMESPACE + +extern "C" asQWORD armFunc(const asDWORD *, int, size_t); +extern "C" asQWORD armFuncR0(const asDWORD *, int, size_t, asDWORD r0); +extern "C" asQWORD armFuncR0R1(const asDWORD *, int, size_t, asDWORD r0, asDWORD r1); +extern "C" asQWORD armFuncObjLast(const asDWORD *, int, size_t, asDWORD obj); +extern "C" asQWORD armFuncR0ObjLast(const asDWORD *, int, size_t, asDWORD r0, asDWORD obj); + +// TODO: CallSystemFunction should be split in two layers. The top layer +// implements the parts that are common to all system functions, +// e.g. the check for generic calling convention, the extraction of the +// object pointer, the processing of auto handles, and the pop size. +// Remember that the proper handling of auto handles is implemented in +// as_callfunc_x64_gcc.cpp as that code can handle both 32bit and 64bit pointers. +// +// The lower layer implemented in CallNativeSystemFunction will then +// be responsible for just transforming the parameters to the native +// calling convention. +// +// This should be done for all supported platforms. +int CallSystemFunction(int id, asCContext *context, void *objectPointer) +{ + asCScriptEngine *engine = context->engine; + asCScriptFunction *descr = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + return context->CallGeneric(id, objectPointer); + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *args = context->regs.stackPointer; + void *retPointer = 0; + void *obj = 0; + asDWORD *vftable; + int popSize = paramSize; + + context->regs.objectType = descr->returnType.GetObjectType(); + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + // Allocate the memory for the object + retPointer = engine->CallAlloc(descr->returnType.GetObjectType()); + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + } + } + + if( callConv >= ICC_THISCALL ) + { + if( objectPointer ) + { + obj = objectPointer; + } + else + { + // The object pointer should be popped from the context stack + popSize++; + + // Check for null pointer + obj = (void*)*(size_t*)(args); + if( obj == 0 ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + if( retPointer ) + engine->CallFree(retPointer); + return 0; + } + + // Add the base offset for multiple inheritance +#ifdef __GNUC__ + // On GNUC + ARM the lsb of the offset is used to indicate a virtual function + // and the whole offset is thus shifted one bit left to keep the original + // offset resolution + obj = (void*)(size_t(obj) + (sysFunc->baseOffset>>1)); +#else + obj = (void*)(size_t(obj) + sysFunc->baseOffset); +#endif + + // Skip the object pointer + args++; + } + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + context->isCallingSystemFunction = true; + + switch( callConv ) + { + case ICC_CDECL_RETURNINMEM: // fall through + case ICC_STDCALL_RETURNINMEM: + retQW = armFuncR0(args, paramSize<<2, (asDWORD)func, (asDWORD) retPointer); + break; + case ICC_CDECL: // fall through + case ICC_STDCALL: + retQW = armFunc(args, paramSize<<2, (asDWORD)func); + break; + case ICC_THISCALL: // fall through + case ICC_CDECL_OBJFIRST: + retQW = armFuncR0(args, paramSize<<2, (asDWORD)func, (asDWORD) obj); + break; + case ICC_THISCALL_RETURNINMEM: +#ifndef __GNUC__ + retQW = armFuncR0R1(args, paramSize<<2, (asDWORD)func, (asDWORD) obj, (asDWORD) retPointer); + break; +#endif + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = armFuncR0R1(args, paramSize<<2, (asDWORD)func, (asDWORD) retPointer, (asDWORD) obj); + break; + case ICC_VIRTUAL_THISCALL: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = armFuncR0(args, paramSize<<2, vftable[asDWORD(func)>>2], (asDWORD) obj); + break; + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; +#ifndef __GNUC__ + retQW = armFuncR0R1(args, (paramSize+1)<<2, vftable[asDWORD(func)>>2], (asDWORD) retPointer, (asDWORD) obj); +#else + retQW = armFuncR0R1(args, (paramSize+1)<<2, vftable[asDWORD(func)>>2], (asDWORD) obj, (asDWORD) retPointer); +#endif + break; + case ICC_CDECL_OBJLAST: + retQW = armFuncObjLast(args, paramSize<<2, (asDWORD)func, (asDWORD) obj); + break; + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = armFuncR0ObjLast(args, paramSize<<2, (asDWORD)func, (asDWORD) retPointer, (asDWORD) obj); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + context->isCallingSystemFunction = false; +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) + { + // Need to free the complex objects passed by value + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && + !descr->parameterTypes[n].IsReference() && + !descr->parameterTypes[n].IsObjectHandle() + ) + { + if (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) + { + void *obj = (void*)args[spos++]; + + // Running the destructor here results in the destructor being + // run twice as it has already been called in the native function. + // TODO: Should this be an ifdef or removed completely? +// asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh; +// if( beh->destruct ) +// engine->CallObjectMethod(obj, beh->destruct); + + engine->CallFree(obj); + } + else + { + // The original data was already freed earlier + spos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + } +#endif + + // Store the returned value in our stack + if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) + { + if( descr->returnType.IsObjectHandle() ) + { + context->regs.objectRegister = (void*)(size_t)retQW; + + if( sysFunc->returnAutoHandle && context->regs.objectRegister ) + engine->CallObjectMethod(context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref); + } + else + { + if( !sysFunc->hostReturnInMemory ) + { + // Copy the returned value to the pointer sent by the script engine + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)retPointer = (asDWORD)retQW; + else + *(asQWORD*)retPointer = retQW; + } + + // Store the object in the register + context->regs.objectRegister = retPointer; + } + } + else + { + // Store value in valueRegister + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = (asDWORD) retQW; + else + context->regs.valueRegister = retQW; + } + else if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = (asDWORD)retQW; + else + context->regs.valueRegister = retQW; + } + + if( sysFunc->hasAutoHandles ) + { + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( sysFunc->paramAutoHandles[n] && args[spos] ) + { + // Call the release method on the type + engine->CallObjectMethod((void*)*(size_t*)&args[spos], descr->parameterTypes[n].GetObjectType()->beh.release); + args[spos] = 0; + } + + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + spos++; + else + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + return popSize; +} + +END_AS_NAMESPACE + +#endif // AS_ARM +#endif // AS_MAX_PORTABILITY + + + + diff --git a/AngelScript/source/as_callfunc_arm_gcc.S b/AngelScript/source/as_callfunc_arm_gcc.S new file mode 100644 index 000000000..66d786847 --- /dev/null +++ b/AngelScript/source/as_callfunc_arm_gcc.S @@ -0,0 +1,235 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// Assembly routines for the ARM call convention +// Written by Fredrik Ehnbom in June 2009 + +// Adapted to GNUC by darktemplar216 in September 2009 + +#if defined(__arm__) || defined(__ARM__) + +.global armFunc +.global armFuncR0 +.global armFuncR0R1 +.global armFuncObjLast +.global armFuncR0ObjLast + +armFunc: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + beq nomoreargs + + // Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + cmp r7, #3*4 + ldrge r2, [r6],#4 + cmp r7, #4*4 + ldrge r3, [r6],#4 + ble nomoreargs + + // Load the rest of the arguments onto the stack + sub r7, r7, #4*4 // skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +stackargsloop: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargsloop +nomoreargs: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +armFuncObjLast: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // objlast. might get overwritten + str r3, [sp, #-4]! // objlast again. + + beq nomoreargsarmFuncObjLast + + // Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + ldrlt r1, [sp] + cmp r7, #3*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #4*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble nomoreargsarmFuncObjLast + + // Load the rest of the arguments onto the stack + sub r7, r7, #4*4 // skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncObjLast +nomoreargsarmFuncObjLast: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + +armFuncR0ObjLast: + stmdb sp!, {r4-r8, lr} + ldr r7, [sp,#6*4] + str r7, [sp,#-4]! + + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + ldr r1, [sp] // objlast. might get overwritten + + beq nomoreargsarmFuncR0ObjLast + + // Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #3*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble nomoreargsarmFuncR0ObjLast + + // Load the rest of the arguments onto the stack + sub r7, r7, #3*4 // skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0ObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0ObjLast +nomoreargsarmFuncR0ObjLast: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + + +armFuncR0: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + + beq nomoreargsarmFuncR0 + + // Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + cmp r7, #3*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0 + + // Load the rest of the arguments onto the stack + sub r7, r7, #3*4 // skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0 +nomoreargsarmFuncR0: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + + +armFuncR0R1: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + ldr r1, [sp, #6*4] // r1 explicitly set too + + beq nomoreargsarmFuncR0R1 + + // Load the first 2 arguments into r2-r3 + cmp r7, #1*4 + ldrge r2, [r6],#4 + cmp r7, #2*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0R1 + + // Load the rest of the arguments onto the stack + sub r7, r7, #2*4 // skip the 2 registers already loaded into r2-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0R1: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0R1 +nomoreargsarmFuncR0R1: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +#endif diff --git a/AngelScript/source/as_callfunc_arm_msvc.asm b/AngelScript/source/as_callfunc_arm_msvc.asm new file mode 100644 index 000000000..12c3cc134 --- /dev/null +++ b/AngelScript/source/as_callfunc_arm_msvc.asm @@ -0,0 +1,242 @@ +; +; AngelCode Scripting Library +; Copyright (c) 2003-2009 Andreas Jonsson +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any +; damages arising from the use of this software. +; +; Permission is granted to anyone to use this software for any +; purpose, including commercial applications, and to alter it and +; redistribute it freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you +; must not claim that you wrote the original software. If you use +; this software in a product, an acknowledgment in the product +; documentation would be appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and +; must not be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any source +; distribution. +; +; The original version of this library can be located at: +; http://www.angelcode.com/angelscript/ +; +; Andreas Jonsson +; andreas@angelcode.com +; + + +; Assembly routines for the ARM call convention +; Written by Fredrik Ehnbom in June 2009 + +; MSVC currently doesn't support inline assembly for the ARM platform +; so this separate file is needed. + + + AREA |.rdata|, DATA, READONLY + EXPORT |armFunc| + EXPORT armFuncR0 + EXPORT armFuncR0R1 + EXPORT armFuncObjLast + EXPORT armFuncR0ObjLast + + + AREA |.text|, CODE, ARM + +|armFunc| PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + beq |nomoreargs| + + ; Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + cmp r7, #3*4 + ldrge r2, [r6],#4 + cmp r7, #4*4 + ldrge r3, [r6],#4 + ble |nomoreargs| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #4*4 ; skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop| +|nomoreargs| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + ENDP + +armFuncObjLast PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; objlast. might get overwritten + str r3, [sp, #-4]! ; objlast again. + + beq |nomoreargs@armFuncObjLast| + + ; Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + ldrlt r1, [sp] + cmp r7, #3*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #4*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble |nomoreargs@armFuncObjLast| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #4*4 ; skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncObjLast| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncObjLast| +|nomoreargs@armFuncObjLast| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + ENDP + +armFuncR0ObjLast PROC + stmdb sp!, {r4-r8, lr} + ldr r7, [sp,#6*4] + str r7, [sp,#-4]! + + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; r0 explicitly set + ldr r1, [sp] ; objlast. might get overwritten + + beq |nomoreargs@armFuncR0ObjLast| + + ; Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #3*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble |nomoreargs@armFuncR0ObjLast| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #3*4 ; skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncR0ObjLast| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncR0ObjLast| +|nomoreargs@armFuncR0ObjLast| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + ENDP + +armFuncR0 PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; r0 explicitly set + + beq |nomoreargs@armFuncR0| + + ; Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + cmp r7, #3*4 + ldrge r3, [r6],#4 + ble |nomoreargs@armFuncR0| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #3*4 ; skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncR0| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncR0| +|nomoreargs@armFuncR0| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + ENDP + +armFuncR0R1 PROC + stmdb sp!, {r4-r8, lr} + mov r6, r0 ; arg table + movs r7, r1 ; arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 ; function address + mov r8, #0 + + mov r0, r3 ; r0 explicitly set + ldr r1, [sp, #6*4] ; r1 explicitly set too + + beq |nomoreargs@armFuncR0R1| + + ; Load the first 2 arguments into r2-r3 + cmp r7, #1*4 + ldrge r2, [r6],#4 + cmp r7, #2*4 + ldrge r3, [r6],#4 + ble |nomoreargs@armFuncR0R1| + + ; Load the rest of the arguments onto the stack + sub r7, r7, #2*4 ; skip the 2 registers already loaded into r2-r3 + sub sp, sp, r7 + mov r8, r7 +|stackargsloop@armFuncR0R1| + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne |stackargsloop@armFuncR0R1| +|nomoreargs@armFuncR0R1| + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + ENDP + + END diff --git a/AngelScript/source/as_callfunc_arm_xcode.s b/AngelScript/source/as_callfunc_arm_xcode.s new file mode 100644 index 000000000..c3de382fa --- /dev/null +++ b/AngelScript/source/as_callfunc_arm_xcode.s @@ -0,0 +1,237 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// Assembly routines for the ARM call convention +// Written by Fredrik Ehnbom in June 2009 + +// Adapted to GNUC by darktemplar216 in September 2009 +// Small fixed to work under XCode GCC by Gilad Novik in October 2009 + +#if defined(__arm__) || defined(__ARM__) + +.align 2 +.globl _armFunc +.globl _armFuncR0 +.globl _armFuncR0R1 +.globl _armFuncObjLast +.globl _armFuncR0ObjLast + +_armFunc: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + beq nomoreargs + + // Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + cmp r7, #3*4 + ldrge r2, [r6],#4 + cmp r7, #4*4 + ldrge r3, [r6],#4 + ble nomoreargs + + // Load the rest of the arguments onto the stack + sub r7, r7, #4*4 // skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +stackargsloop: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargsloop +nomoreargs: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +_armFuncObjLast: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // objlast. might get overwritten + str r3, [sp, #-4]! // objlast again. + + beq nomoreargsarmFuncObjLast + + // Load the first 4 arguments into r0-r3 + cmp r7, #4 + ldrge r0, [r6],#4 + cmp r7, #2*4 + ldrge r1, [r6],#4 + ldrlt r1, [sp] + cmp r7, #3*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #4*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble nomoreargsarmFuncObjLast + + // Load the rest of the arguments onto the stack + sub r7, r7, #4*4 // skip the 4 registers already loaded into r0-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncObjLast +nomoreargsarmFuncObjLast: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + +_armFuncR0ObjLast: + stmdb sp!, {r4-r8, lr} + ldr r7, [sp,#6*4] + str r7, [sp,#-4]! + + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + ldr r1, [sp] // objlast. might get overwritten + + beq nomoreargsarmFuncR0ObjLast + + // Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + ldrlt r2, [sp] + cmp r7, #3*4 + ldrge r3, [r6],#4 + ldrlt r3, [sp] + ble nomoreargsarmFuncR0ObjLast + + // Load the rest of the arguments onto the stack + sub r7, r7, #3*4 // skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0ObjLast: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0ObjLast +nomoreargsarmFuncR0ObjLast: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + add sp, sp, #4 + ldmia sp!, {r4-r8, pc} + + +_armFuncR0: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + + beq nomoreargsarmFuncR0 + + // Load the first 3 arguments into r1-r3 + cmp r7, #1*4 + ldrge r1, [r6],#4 + cmp r7, #2*4 + ldrge r2, [r6],#4 + cmp r7, #3*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0 + + // Load the rest of the arguments onto the stack + sub r7, r7, #3*4 // skip the 3 registers already loaded into r1-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0 +nomoreargsarmFuncR0: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + + +_armFuncR0R1: + stmdb sp!, {r4-r8, lr} + mov r6, r0 // arg table + movs r7, r1 // arg size (also set the condition code flags so that we detect if there are no arguments) + mov r4, r2 // function address + mov r8, #0 + + mov r0, r3 // r0 explicitly set + ldr r1, [sp, #6*4] // r1 explicitly set too + + beq nomoreargsarmFuncR0R1 + + // Load the first 2 arguments into r2-r3 + cmp r7, #1*4 + ldrge r2, [r6],#4 + cmp r7, #2*4 + ldrge r3, [r6],#4 + ble nomoreargsarmFuncR0R1 + + // Load the rest of the arguments onto the stack + sub r7, r7, #2*4 // skip the 2 registers already loaded into r2-r3 + sub sp, sp, r7 + mov r8, r7 +stackargslooparmFuncR0R1: + ldr r5, [r6], #4 + str r5, [sp], #4 + subs r7, r7, #4 + bne stackargslooparmFuncR0R1 +nomoreargsarmFuncR0R1: + sub sp, sp, r8 + blx r4 + add sp, sp, r8 + ldmia sp!, {r4-r8, pc} + +#endif diff --git a/AngelScript/source/as_callfunc_mips.cpp b/AngelScript/source/as_callfunc_mips.cpp new file mode 100644 index 000000000..081906add --- /dev/null +++ b/AngelScript/source/as_callfunc_mips.cpp @@ -0,0 +1,564 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_mips.cpp +// +// These functions handle the actual calling of system functions +// +// This version is MIPS specific and was originally written +// by Manu Evans in April, 2006 +// + + +#include "as_config.h" + +#ifndef MAX_PORTABILITY +#ifdef AS_MIPS + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" + +#include +#include +#include + +BEGIN_AS_NAMESPACE + +#define AS_MIPS_MAX_ARGS 32 +#define AS_NUM_REG_FLOATS 8 +#define AS_NUM_REG_INTS 8 + +// The array used to send values to the correct places. +// first 0-8 regular values to load into the a0-a3, t0-t3 registers +// then 0-8 float values to load into the f12-f19 registers +// then (AS_MIPS_MAX_ARGS - 16) values to load onto the stack +// the +1 is for when CallThis (object methods) is used +// extra +1 when returning in memory +extern "C" { +static asDWORD mipsArgs[AS_MIPS_MAX_ARGS + 1 + 1]; +} + +// Loads all data into the correct places and calls the function. +// intArgSize is the size in bytes for how much data to put in int registers +// floatArgSize is the size in bytes for how much data to put in float registers +// stackArgSize is the size in bytes for how much data to put on the callstack +extern "C" asQWORD mipsFunc(int intArgSize, int floatArgSize, int stackArgSize, asDWORD func); + +// puts the arguments in the correct place in the mipsArgs-array. See comments above. +// This could be done better. +inline void splitArgs(const asDWORD *args, int argNum, int &numRegIntArgs, int &numRegFloatArgs, int &numRestArgs, int hostFlags) +{ + int i; + + int argBit = 1; + for (i = 0; i < argNum; i++) + { + if (hostFlags & argBit) + { + if (numRegFloatArgs < AS_NUM_REG_FLOATS) + { + // put in float register + mipsArgs[AS_NUM_REG_INTS + numRegFloatArgs] = args[i]; + numRegFloatArgs++; + } + else + { + // put in stack + mipsArgs[AS_NUM_REG_INTS + AS_NUM_REG_FLOATS + numRestArgs] = args[i]; + numRestArgs++; + } + } + else + { + if (numRegIntArgs < AS_NUM_REG_INTS) + { + // put in int register + mipsArgs[numRegIntArgs] = args[i]; + numRegIntArgs++; + } + else + { + // put in stack + mipsArgs[AS_NUM_REG_INTS + AS_NUM_REG_FLOATS + numRestArgs] = args[i]; + numRestArgs++; + } + } + argBit <<= 1; + } +} + +asQWORD CallCDeclFunction(const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + // put the arguments in the correct places in the mipsArgs array + if(argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return mipsFunc(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object +asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 1; + int floatArgs = 0; + int restArgs = 0; + + mipsArgs[0] = (asDWORD) obj; + + // put the arguments in the correct places in the mipsArgs array + if (argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return mipsFunc(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + // put the arguments in the correct places in the mipsArgs array + if(argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + if(intArgs < AS_NUM_REG_INTS) + { + mipsArgs[intArgs] = (asDWORD) obj; + intArgs++; + } + else + { + mipsArgs[AS_NUM_REG_INTS + AS_NUM_REG_FLOATS + restArgs] = (asDWORD) obj; + restArgs++; + } + + return mipsFunc(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + + asm("swc1 $f0, %0\n" : "=m"(f)); + + return f; +} + +/* +asDWORD GetReturnedFloat(); + +asm( +" .align 4\n" +" .global GetReturnedFloat\n" +"GetReturnedFloat:\n" +" .set noreorder\n" +" .set nomacro\n" +" j $ra\n" +" mfc1 $v0, $f0\n" +" .set macro\n" +" .set reorder\n" +" .end Func\n" +*/ + + +// sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4 +// so this isn't really used... +asQWORD GetReturnedDouble() +{ + asQWORD d; + + printf("Broken!!!"); +/* + asm("sw $v0, %0\n" : "=m"(d)); +*/ + return d; +} + +int CallSystemFunction(int id, asCContext *context, void *objectPointer) +{ + asCScriptEngine *engine = context->engine; + asSSystemFunctionInterface *sysFunc = engine->scriptFunctions[id]->sysFuncIntf; + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + return context->CallGeneric(id, objectPointer); + + asQWORD retQW = 0; + + asCScriptFunction *descr = engine->scriptFunctions[id]; + + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *args = context->regs.stackPointer; + void *retPointer = 0; + void *obj = 0; + asDWORD *vftable; + int popSize = paramSize; + + context->regs.objectType = descr->returnType.GetObjectType(); + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + // Allocate the memory for the object + retPointer = engine->CallAlloc(descr->returnType.GetObjectType()); + mipsArgs[AS_MIPS_MAX_ARGS+1] = (asDWORD) retPointer; + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + } + } + + if( callConv >= ICC_THISCALL ) + { + if( objectPointer ) + { + obj = objectPointer; + } + else + { + // The object pointer should be popped from the context stack + popSize++; + + // Check for null pointer + obj = (void*)*(args + paramSize); + if( obj == 0 ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + if( retPointer ) + engine->CallFree(retPointer); + return 0; + } + + // Add the base offset for multiple inheritance + obj = (void*)(int(obj) + sysFunc->baseOffset); + } + } + asASSERT(descr->parameterTypes.GetLength() <= AS_MIPS_MAX_ARGS); + + // mark all float arguments + int argBit = 1; + int hostFlags = 0; + int intArgs = 0; + for( size_t a = 0; a < descr->parameterTypes.GetLength(); a++ ) + { + if (descr->parameterTypes[a].IsFloatType()) + hostFlags |= argBit; + else + intArgs++; + argBit <<= 1; + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + context->isCallingSystemFunction = true; + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction(args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], hostFlags); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + context->isCallingSystemFunction = false; + +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) + { + // Need to free the complex objects passed by value + args = context->regs.stackPointer; + if( callConv >= (int)ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( size_t n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && + !descr->parameterTypes[n].IsReference() && + (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) ) + { + void *obj = (void*)args[spos++]; + asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh; + if( beh->destruct ) + engine->CallObjectMethod(obj, beh->destruct); + + engine->CallFree(obj); + } + else + spos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } +#endif + + // Store the returned value in our stack + if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) + { + if( descr->returnType.IsObjectHandle() ) + { + context->regs.objectRegister = (void*)(asDWORD)retQW; + + if( sysFunc->returnAutoHandle && context->regs.objectRegister ) + engine->CallObjectMethod(context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref); + } + else + { + if( !sysFunc->hostReturnInMemory ) + { + // Copy the returned value to the pointer sent by the script engine + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)retPointer = (asDWORD)retQW; + else + *(asQWORD*)retPointer = retQW; + } + + // Store the object in the register + context->regs.objectRegister = retPointer; + } + } + else + { + // Store value in valueRegister + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = GetReturnedFloat(); + else + context->regs.valueRegister = GetReturnedDouble(); + } + else if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = (asDWORD)retQW; + else + context->regs.valueRegister = retQW; + } + + if( sysFunc->hasAutoHandles ) + { + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( sysFunc->paramAutoHandles[n] && args[spos] ) + { + // Call the release method on the type + engine->CallObjectMethod((void*)args[spos], descr->parameterTypes[n].GetObjectType()->beh.release); + args[spos] = 0; + } + + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + spos++; + else + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + return popSize; +} + +asm( +" .text\n" +//" .align 2\n" +" .global mipsFunc\n" +" .ent mipsFunc\n" +"mipsFunc:\n" +//" .frame $fp,64,$31 # vars= 0, regs= 0/0, args= 0, gp= 0\n" +//" .mask 0x00000000,0\n" +//" .fmask 0x00000000,0\n" +" .set noreorder\n" +" .set nomacro\n" +// align the stack frame to 8 bytes +" addiu $12, $6, 7\n" +" li $13, -8\n" // 0xfffffffffffffffc +" and $12, $12, $13\n" // t4 holds the size of the argument block +// and add 8 bytes for the return pointer and s0 backup +" addiu $13, $12, 8\n" // t5 holds the total size of the stack frame (including return pointer) +// save the s0 register (so we can use it to remember where our return pointer is lives) +" sw $16, -4($sp)\n" // store the s0 register (so we can use it to remember how big our stack frame is) +// push the stack +" subu $sp, $sp, $13\n" +// find the return address, place in s0 +" addu $16, $sp, $12\n" +// store the return pointer +" sw $31, 0($16)\n" + +// backup our function params +" addiu $2, $7, 0\n" +" addiu $3, $6, 0\n" + +// get global mipsArgs[] array pointer +//" lui $15, %hi(mipsArgs)\n" +//" addiu $15, $15, %lo(mipsArgs)\n" +// we'll use the macro instead because SN Systems doesnt like %hi/%lo +".set macro\n" +" la $15, mipsArgs\n" +".set nomacro\n" +// load register params +" lw $4, 0($15)\n" +" lw $5, 4($15)\n" +" lw $6, 8($15)\n" +" lw $7, 12($15)\n" +" lw $8, 16($15)\n" +" lw $9, 20($15)\n" +" lw $10, 24($15)\n" +" lw $11, 28($15)\n" + +// load float params +" lwc1 $f12, 32($15)\n" +" lwc1 $f13, 36($15)\n" +" lwc1 $f14, 40($15)\n" +" lwc1 $f15, 44($15)\n" +" lwc1 $f16, 48($15)\n" +" lwc1 $f17, 52($15)\n" +" lwc1 $f18, 56($15)\n" +" lwc1 $f19, 60($15)\n" + +// skip stack paramaters if there are none +" beq $3, $0, andCall\n" + +// push stack paramaters +" addiu $15, $15, 64\n" +"pushArgs:\n" +" addiu $3, -4\n" +// load from $15 + stack bytes ($3) +" addu $14, $15, $3\n" +" lw $14, 0($14)\n" +// store to $sp + stack bytes ($3) +" addu $13, $sp, $3\n" +" sw $14, 0($13)\n" +// if there are more, loop... +" bne $3, $0, pushArgs\n" +" nop\n" + +// and call the function +"andCall:\n" +" jal $2\n" +" nop\n" + +// restore the return pointer +" lw $31, 0($16)\n" +// pop the stack pointer (remembering the return pointer was 8 bytes below the top) +" addiu $sp, $16, 8\n" +// and return from the function +" jr $31\n" +// restore the s0 register (in the branch delay slot) +" lw $16, -4($sp)\n" +" .set macro\n" +" .set reorder\n" +" .end mipsFunc\n" +" .size mipsFunc, .-mipsFunc\n" +); + +END_AS_NAMESPACE + +#endif // AS_MIPS +#endif // AS_MAX_PORTABILITY + + + + diff --git a/AngelScript/source/as_callfunc_ppc.cpp b/AngelScript/source/as_callfunc_ppc.cpp new file mode 100644 index 000000000..5877ae832 --- /dev/null +++ b/AngelScript/source/as_callfunc_ppc.cpp @@ -0,0 +1,913 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_ppc.cpp +// +// These functions handle the actual calling of system functions +// +// This version is PPC specific +// + +#include + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_PPC + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" + +#include + +BEGIN_AS_NAMESPACE + +// This part was originally written by Pecan Heber, June 2006, for +// use on MacOS X with 32bit PPC processor. He based the code on the +// code in as_callfunc_sh4.cpp + +#define AS_PPC_MAX_ARGS 32 + +// The array used to send values to the correct places. +// Contains a byte of argTypes to indicate the register tYpe to load +// or zero if end of arguments +// The +1 is for when CallThis (object methods) is used +// Extra +1 when returning in memory +// Extra +1 in ppcArgsType to ensure zero end-of-args marker + +// TODO: multithread: We need to remove these global variables for thread-safety + +enum argTypes { ppcENDARG, ppcINTARG, ppcFLOATARG, ppcDOUBLEARG }; +static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1]; + +// Using extern "C" because we use this symbol name in the assembly code +extern "C" +{ + static asBYTE ppcArgsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1]; +} + +// NOTE: these values are for PowerPC 32 bit. +#define PPC_LINKAGE_SIZE (24) // how big the PPC linkage area is in a stack frame +#define PPC_NUM_REGSTORE (9) // how many registers of the PPC we need to store/restore for ppcFunc() +#define PPC_REGSTORE_SIZE (4*PPC_NUM_REGSTORE) // how many bytes are required for register store/restore +#define EXTRA_STACK_SIZE (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame +#define PPC_STACK_SIZE(numParams) (-( ( ((((numParams)<8)?8:(numParams))<<2) + EXTRA_STACK_SIZE + 15 ) & ~15 )) // calculates the total stack size needed for ppcFunc64, must pad to 16bytes + +// Loads all data into the correct places and calls the function. +// ppcArgsType is an array containing a byte type (enum argTypes) for each argument. +// stackArgSize is the size in bytes for how much data to put on the stack frame +extern "C" asQWORD ppcFunc(const asDWORD* argsPtr, int StackArgSize, asDWORD func); + +asm(" .text\n" + " .align 2\n" // align the code to 1 << 2 = 4 bytes + " .globl _ppcFunc\n" + "_ppcFunc:\n" + + // We're receiving the following parameters + + // r3 : argsPtr + // r4 : StackArgSize + // r5 : func + + // The following registers are used through out the function + + // r31 : the address of the label address, as reference for all other labels + // r30 : temporary variable + // r29 : arg list pointer + // r28 : number of FPR registers used by the parameters + // r27 : the function pointer that will be called + // r26 : the location of the parameters for the call + // r25 : arg type list pointer + // r24 : temporary variable + // r23 : number of GPR registers used by the parameters + // r1 : this is stack pointer + // r0 : temporary variable + // f0 : temporary variable + + // We need to store some of the registers for restoral before returning to caller + + // lr - always stored in 8(r1) - this is the return address + // cr - not required to be stored, but if it is, its place is in 4(r1) - this is the condition register + // r1 - always stored in 0(r1) - this is the stack pointer + // r11 + // r13 to r31 + // f14 to f31 + + // Store register values and setup our stack frame + " mflr r0 \n" // move the return address into r0 + " stw r0, 8(r1) \n" // Store the return address on the stack + " stmw r23, -36(r1) \n" // Store registers r23 to r31 on the stack + " stwux r1, r1, r4 \n" // Increase the stack with the needed space and store the original value in the destination + + // Obtain an address that we'll use as our position of reference when obtaining addresses of other labels + " bl address \n" + "address: \n" + " mflr r31 \n" + + // initial registers for the function + " mr r29, r3 \n" // (r29) args list + " mr r27, r5 \n" // load the function pointer to call. func actually holds the pointer to our function + " addi r26, r1, 24 \n" // setup the pointer to the parameter area to the function we're going to call + " sub r0, r0, r0 \n" // zero out r0 + " mr r23, r0 \n" // zero out r23, which holds the number of used GPR registers + " mr r28, r0 \n" // zero our r22, which holds the number of used float registers + + // load the global ppcArgsType which holds the types of arguments for each argument + " addis r25, r31, ha16(_ppcArgsType - address) \n" // load the upper 16 bits of the address to r25 + " la r25, lo16(_ppcArgsType - address)(r25) \n" // load the lower 16 bits of the address to r25 + " subi r25, r25, 1 \n" // since we increment r25 on its use, we'll pre-decrement it + + // loop through the arguments + "ppcNextArg: \n" + " addi r25, r25, 1 \n" // increment r25, our arg type pointer + // switch based on the current argument type (0:end, 1:int, 2:float 3:double) + " lbz r24, 0(r25) \n" // load the current argument type (it's a byte) + " mulli r24, r24, 4 \n" // our jump table has 4 bytes per case (1 instruction) + " addis r30, r31, ha16(ppcTypeSwitch - address) \n" // load the address of the jump table for the switch + " la r30, lo16(ppcTypeSwitch - address)(r30) \n" + + " add r0, r30, r24 \n" // offset by our argument type + " mtctr r0 \n" // load the jump address into CTR + " bctr \n" // jump into the jump table/switch + " nop \n" + + // the jump table/switch based on the current argument type + "ppcTypeSwitch: \n" + " b ppcArgsEnd \n" + " b ppcArgIsInteger \n" + " b ppcArgIsFloat \n" + " b ppcArgIsDouble \n" + + // when we get here we have finished processing all the arguments + // everything is ready to go to call the function + "ppcArgsEnd: \n" + " mtctr r27 \n" // the function pointer is stored in r27, load that into CTR + " bctrl \n" // call the function. We have to do it this way so that the LR gets the proper + " nop \n" // return value (the next instruction below). So we have to branch from CTR instead of LR. + + // Restore registers and caller's stack frame, then return to caller + " lwz r1, 0(r1) \n" // restore the caller's stack pointer + " lwz r0, 8(r1) \n" // load in the caller's LR + " mtlr r0 \n" // restore the caller's LR + " lmw r23, -36(r1) \n" // restore registers r23 to r31 from the stack + " blr \n" // return back to the caller + " nop \n" + + // Integer argument (GPR register) + "ppcArgIsInteger: \n" + " addis r30, r31, ha16(ppcLoadIntReg - address) \n" // load the address to the jump table for integer registers + " la r30, lo16(ppcLoadIntReg - address)(r30) \n" + " mulli r0, r23, 8 \n" // each item in the jump table is 2 instructions (8 bytes) + " add r0, r0, r30 \n" // calculate ppcLoadIntReg[numUsedGPRRegs] + " lwz r30, 0(r29) \n" // load the next argument from the argument list into r30 + " cmpwi r23, 8 \n" // we can only load GPR3 through GPR10 (8 registers) + " bgt ppcLoadIntRegUpd \n" // if we're beyond 8 GPR registers, we're in the stack, go there + " mtctr r0 \n" // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers) + " bctr \n" // load the argument into a GPR register + " nop \n" + // jump table for GPR registers, for the first 8 GPR arguments + "ppcLoadIntReg: \n" + " mr r3, r30 \n" // arg0 (to r3) + " b ppcLoadIntRegUpd \n" + " mr r4, r30 \n" // arg1 (to r4) + " b ppcLoadIntRegUpd \n" + " mr r5, r30 \n" // arg2 (to r5) + " b ppcLoadIntRegUpd \n" + " mr r6, r30 \n" // arg3 (to r6) + " b ppcLoadIntRegUpd \n" + " mr r7, r30 \n" // arg4 (to r7) + " b ppcLoadIntRegUpd \n" + " mr r8, r30 \n" // arg5 (to r8) + " b ppcLoadIntRegUpd \n" + " mr r9, r30 \n" // arg6 (to r9) + " b ppcLoadIntRegUpd \n" + " mr r10, r30 \n" // arg7 (to r10) + " b ppcLoadIntRegUpd \n" + // all GPR arguments still go on the stack + "ppcLoadIntRegUpd: \n" + " stw r30, 0(r26) \n" // store the argument into the next slot on the stack's argument list + " addi r23, r23, 1 \n" // count a used GPR register + " addi r29, r29, 4 \n" // move to the next argument on the list + " addi r26, r26, 4 \n" // adjust our argument stack pointer for the next + " b ppcNextArg \n" // next argument + + // single Float argument + "ppcArgIsFloat:\n" + " addis r30, r31, ha16(ppcLoadFloatReg - address) \n" // get the base address of the float register jump table + " la r30, lo16(ppcLoadFloatReg - address)(r30) \n" + " mulli r0, r28, 8 \n" // each jump table entry is 8 bytes + " add r0, r0, r30 \n" // calculate the offset to ppcLoadFloatReg[numUsedFloatReg] + " lfs f0, 0(r29) \n" // load the next argument as a float into f0 + " cmpwi r28, 13 \n" // can't load more than 13 float/double registers + " bgt ppcLoadFloatRegUpd \n" // if we're beyond 13 registers, just fall to inserting into the stack + " mtctr r0 \n" // jump into the float jump table + " bctr \n" + " nop \n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadFloatReg: \n" + " fmr f1, f0 \n" // arg0 (f1) + " b ppcLoadFloatRegUpd \n" + " fmr f2, f0 \n" // arg1 (f2) + " b ppcLoadFloatRegUpd \n" + " fmr f3, f0 \n" // arg2 (f3) + " b ppcLoadFloatRegUpd \n" + " fmr f4, f0 \n" // arg3 (f4) + " b ppcLoadFloatRegUpd \n" + " fmr f5, f0 \n" // arg4 (f5) + " b ppcLoadFloatRegUpd \n" + " fmr f6, f0 \n" // arg5 (f6) + " b ppcLoadFloatRegUpd \n" + " fmr f7, f0 \n" // arg6 (f7) + " b ppcLoadFloatRegUpd \n" + " fmr f8, f0 \n" // arg7 (f8) + " b ppcLoadFloatRegUpd \n" + " fmr f9, f0 \n" // arg8 (f9) + " b ppcLoadFloatRegUpd \n" + " fmr f10, f0 \n" // arg9 (f10) + " b ppcLoadFloatRegUpd \n" + " fmr f11, f0 \n" // arg10 (f11) + " b ppcLoadFloatRegUpd \n" + " fmr f12, f0 \n" // arg11 (f12) + " b ppcLoadFloatRegUpd \n" + " fmr f13, f0 \n" // arg12 (f13) + " b ppcLoadFloatRegUpd \n" + " nop \n" + // all float arguments still go on the stack + "ppcLoadFloatRegUpd: \n" + " stfs f0, 0(r26) \n" // store, as a single float, f0 (current argument) on to the stack argument list + " addi r23, r23, 1 \n" // a float register eats up a GPR register + " addi r28, r28, 1 \n" // ...and, of course, a float register + " addi r29, r29, 4 \n" // move to the next argument in the list + " addi r26, r26, 4 \n" // move to the next stack slot + " b ppcNextArg \n" // on to the next argument + " nop \n" + + // double Float argument + "ppcArgIsDouble: \n" + " addis r30, r31, ha16(ppcLoadDoubleReg - address) \n" // load the base address of the jump table for double registers + " la r30, lo16(ppcLoadDoubleReg - address)(r30) \n" + " mulli r0, r28, 8 \n" // each slot of the jump table is 8 bytes + " add r0, r0, r30 \n" // calculate ppcLoadDoubleReg[numUsedFloatReg] + " lfd f0, 0(r29) \n" // load the next argument, as a double float, into f0 + " cmpwi r28, 13 \n" // the first 13 floats must go into float registers also + " bgt ppcLoadDoubleRegUpd \n" // if we're beyond 13, then just put on to the stack + " mtctr r0 \n" // we're under 13, first load our register + " bctr \n" // jump into the jump table + " nop \n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadDoubleReg: \n" + " fmr f1, f0 \n" // arg0 (f1) + " b ppcLoadDoubleRegUpd \n" + " fmr f2, f0 \n" // arg1 (f2) + " b ppcLoadDoubleRegUpd \n" + " fmr f3, f0 \n" // arg2 (f3) + " b ppcLoadDoubleRegUpd \n" + " fmr f4, f0 \n" // arg3 (f4) + " b ppcLoadDoubleRegUpd \n" + " fmr f5, f0 \n" // arg4 (f5) + " b ppcLoadDoubleRegUpd \n" + " fmr f6, f0 \n" // arg5 (f6) + " b ppcLoadDoubleRegUpd \n" + " fmr f7, f0 \n" // arg6 (f7) + " b ppcLoadDoubleRegUpd \n" + " fmr f8, f0 \n" // arg7 (f8) + " b ppcLoadDoubleRegUpd \n" + " fmr f9, f0 \n" // arg8 (f9) + " b ppcLoadDoubleRegUpd \n" + " fmr f10, f0 \n" // arg9 (f10) + " b ppcLoadDoubleRegUpd \n" + " fmr f11, f0 \n" // arg10 (f11) + " b ppcLoadDoubleRegUpd \n" + " fmr f12, f0 \n" // arg11 (f12) + " b ppcLoadDoubleRegUpd \n" + " fmr f13, f0 \n" // arg12 (f13) + " b ppcLoadDoubleRegUpd \n" + " nop \n" + // all float arguments still go on the stack + "ppcLoadDoubleRegUpd: \n" + " stfd f0, 0(r26) \n" // store f0, as a double, into the argument list on the stack + " addi r23, r23, 2 \n" // a double float eats up two GPRs + " addi r28, r28, 1 \n" // ...and, of course, a float + " addi r29, r29, 8 \n" // increment to our next argument we need to process (8 bytes for the 64bit float) + " addi r26, r26, 8 \n" // increment to the next slot on the argument list on the stack (8 bytes) + " b ppcNextArg \n" // on to the next argument + " nop \n" +); + +asDWORD GetReturnedFloat() +{ + asDWORD f; + asm(" stfs f1, %0\n" : "=m"(f)); + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD f; + asm(" stfd f1, %0\n" : "=m"(f)); + return f; +} + +// puts the arguments in the correct place in the stack array. See comments above. +void stackArgs(const asDWORD *args, const asBYTE *argsType, int& numIntArgs, int& numFloatArgs, int& numDoubleArgs) +{ + int i; + int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2); + int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs; + + int typeIndex; + for( i = 0, typeIndex = 0; ; i++, typeIndex++ ) + { + // store the type + ppcArgsType[typeOffset++] = argsType[typeIndex]; + if( argsType[typeIndex] == ppcENDARG ) + break; + + switch( argsType[typeIndex] ) + { + case ppcFLOATARG: + // stow float + ppcArgs[argWordPos] = args[i]; // it's just a bit copy + numFloatArgs++; + argWordPos++; //add one word + break; + + case ppcDOUBLEARG: + // stow double + memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment + numDoubleArgs++; + argWordPos+=2; //add two words + i++;//doubles take up 2 argument slots + break; + + case ppcINTARG: + // stow register + ppcArgs[argWordPos] = args[i]; + numIntArgs++; + argWordPos++; + break; + } + } + + // close off the argument list (if we have max args we won't close it off until here) + ppcArgsType[typeOffset] = ppcENDARG; +} + +static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs ); + numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots + } + else + { + // no arguments, cap the type list + ppcArgsType[baseArgCount] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object (unless we are returning in memory) +static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory ) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // the first argument is the 'this' of the object + ppcArgs[baseArgCount] = (asDWORD)obj; + ppcArgsType[baseArgCount++] = ppcINTARG; + ppcArgsType[baseArgCount] = ppcENDARG; + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs ); + numTotalArgs = intArgs + floatArgs + 2*doubleArgs; // doubles occupy two slots + } + + // call the function with the arguments + return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +// NOTE: on PPC the order for the args is reversed +static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + UNUSED_VAR(argSize); + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // stack any of the arguments + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs ); + int numTotalArgs = intArgs + floatArgs + doubleArgs; + + // can we fit the object in at the end? + if( numTotalArgs < AS_PPC_MAX_ARGS ) + { + // put the object pointer at the end + int argPos = intArgs + floatArgs + (doubleArgs * 2); + ppcArgs[argPos] = (asDWORD)obj; + ppcArgsType[numTotalArgs++] = ppcINTARG; + ppcArgsType[numTotalArgs] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +int CallSystemFunction(int id, asCContext *context, void *objectPointer) +{ + // use a working array of types, we'll configure the final one in stackArgs + asBYTE argsType[2*AS_PPC_MAX_ARGS + 1 + 1 + 1]; + memset( argsType, 0, sizeof(argsType)); + + asCScriptEngine *engine = context->engine; + asCScriptFunction *descr = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + { + // we're only handling native calls, handle generic calls in here + return context->CallGeneric( id, objectPointer); + } + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + int popSize = paramSize; + asDWORD *args = context->regs.stackPointer; + void *obj = NULL; + asDWORD *vftable = NULL; + void *retObjPointer = NULL; // for system functions that return AngelScript objects + void *retInMemPointer = NULL; // for host functions that need to return data in memory instead of by register + int a, s; + + // convert the parameters that are < 4 bytes from little endian to big endian + int argDwordOffset = 0; + + // if this is a THISCALL function and no object pointer was given, then the + // first argument on the stack is the object pointer -- we MUST skip it for doing + // the endian flipping. + if( ( callConv >= ICC_THISCALL ) && (objectPointer == NULL) ) + { + ++argDwordOffset; + } + + for( a = 0; a < (int)descr->parameterTypes.GetLength(); a++ ) + { + int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes(); + if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() ) + { + argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords(); + continue; + } + + // flip + asASSERT( numBytes == 1 || numBytes == 2 ); + switch( numBytes ) + { + case 1: + { + volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]); + asBYTE t = bPtr[0]; + bPtr[0] = bPtr[3]; + bPtr[3] = t; + t = bPtr[1]; + bPtr[1] = bPtr[2]; + bPtr[2] = t; + } + break; + case 2: + { + volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]); + asWORD t = wPtr[0]; + wPtr[0] = wPtr[1]; + wPtr[1] = t; + } + break; + } + argDwordOffset++; + } + + // Objects returned to AngelScript must be via an object pointer. This goes for + // ALL objects, including those of simple, complex, primitive or float. Whether + // the host system (PPC in this case) returns the 'object' as a pointer depends on the type of object. + context->regs.objectType = descr->returnType.GetObjectType(); + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + // Allocate the memory for the object + retObjPointer = engine->CallAlloc( descr->returnType.GetObjectType() ); + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory on the host system + callConv++; + retInMemPointer = retObjPointer; + } + } + + // make sure that host functions that will be returning in memory have a memory pointer + asASSERT( sysFunc->hostReturnInMemory==false || retInMemPointer!=NULL ); + + if( callConv >= ICC_THISCALL ) + { + if( objectPointer ) + { + obj = objectPointer; + } + else + { + // The object pointer should be popped from the context stack + popSize++; + + // Check for null pointer + obj = (void*)*(args); + if( obj == NULL ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + if( retObjPointer ) + { + engine->CallFree(retObjPointer); + } + return 0; + } + + // Add the base offset for multiple inheritance + obj = (void*)(int(obj) + sysFunc->baseOffset); + + // Skip the object pointer + args++; + } + } + asASSERT( descr->parameterTypes.GetLength() <= AS_PPC_MAX_ARGS ); + + // mark all float/double/int arguments + for( s = 0, a = 0; s < (int)descr->parameterTypes.GetLength(); s++, a++ ) + { + if( descr->parameterTypes[s].IsFloatType() && !descr->parameterTypes[s].IsReference() ) + { + argsType[a] = ppcFLOATARG; + } + else if( descr->parameterTypes[s].IsDoubleType() && !descr->parameterTypes[s].IsReference() ) + { + argsType[a] = ppcDOUBLEARG; + } + else + { + argsType[a] = ppcINTARG; + if( descr->parameterTypes[s].GetSizeOnStackDWords() == 2 ) + { + // Add an extra integer argument for the extra size + a++; + argsType[a] = ppcINTARG; + } + } + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // NOTE: we may have to do endian flipping here + + // Copy the object's memory to the buffer + memcpy( ¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() ); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos) ); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + { + paramBuffer[dpos++] = args[spos++]; + } + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + // one last verification to make sure things are how we expect + asASSERT( (retInMemPointer!=NULL && sysFunc->hostReturnInMemory) || (retInMemPointer==NULL && !sysFunc->hostReturnInMemory) ); + context->isCallingSystemFunction = true; + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retInMemPointer ); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + context->isCallingSystemFunction = false; + +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) + { + // Need to free the complex objects passed by value + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( int n = 0; n < (int)descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && + !descr->parameterTypes[n].IsReference() && + (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) ) + { + void *obj = (void*)args[spos++]; + asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh; + if( beh->destruct ) + { + engine->CallObjectMethod(obj, beh->destruct); + } + + engine->CallFree(obj); + } + else + { + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + } +#endif + + // Store the returned value in our stack + if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) + { + if( descr->returnType.IsObjectHandle() ) + { + // Since we're treating the system function as if it is returning a QWORD we are + // actually receiving the value in the high DWORD of retQW. + retQW >>= 32; + + // returning an object handle + context->regs.objectRegister = (void*)(asDWORD)retQW; + + if( sysFunc->returnAutoHandle && context->regs.objectRegister ) + { + engine->CallObjectMethod(context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref); + } + } + else + { + // returning an object + if( !sysFunc->hostReturnInMemory ) + { + // In this case, AngelScript wants an object pointer back, but the host system + // didn't use 'return in memory', so its results were passed back by the return register. + // We have have to take the results of the return register and store them IN the pointer for the object. + // The data for the object could fit into a register; we need to copy that data to the object pointer's + // memory. + asASSERT( retInMemPointer == NULL ); + asASSERT( retObjPointer != NULL ); + + // Copy the returned value to the pointer sent by the script engine + if( sysFunc->hostReturnSize == 1 ) + { + // Since we're treating the system function as if it is returning a QWORD we are + // actually receiving the value in the high DWORD of retQW. + retQW >>= 32; + + *(asDWORD*)retObjPointer = (asDWORD)retQW; + } + else + { + *(asQWORD*)retObjPointer = retQW; + } + } + else + { + // In this case, AngelScript wants an object pointer back, and the host system + // used 'return in memory'. So its results were already passed back in memory, and + // stored in the object pointer. + asASSERT( retInMemPointer != NULL ); + asASSERT( retObjPointer != NULL ); + } + + // store the return results into the object register + context->regs.objectRegister = retObjPointer; + } + } + else + { + // Store value in valueRegister + if( sysFunc->hostReturnFloat ) + { + // floating pointer primitives + if( sysFunc->hostReturnSize == 1 ) + { + // single float + *(asDWORD*)&context->regs.valueRegister = GetReturnedFloat(); + } + else + { + // double float + context->regs.valueRegister = GetReturnedDouble(); + } + } + else if( sysFunc->hostReturnSize == 1 ) + { + // <= 32 bit primitives + + // Since we're treating the system function as if it is returning a QWORD we are + // actually receiving the value in the high DWORD of retQW. + retQW >>= 32; + + // due to endian issues we need to handle return values, that are + // less than a DWORD (32 bits) in size, special + int numBytes = descr->returnType.GetSizeInMemoryBytes(); + if( descr->returnType.IsReference() ) numBytes = 4; + switch( numBytes ) + { + case 1: + { + // 8 bits + asBYTE *val = (asBYTE*)ARG_DW(context->regs.valueRegister); + val[0] = (asBYTE)retQW; + val[1] = 0; + val[2] = 0; + val[3] = 0; + val[4] = 0; + val[5] = 0; + val[6] = 0; + val[7] = 0; + } + break; + case 2: + { + // 16 bits + asWORD *val = (asWORD*)ARG_DW(context->regs.valueRegister); + val[0] = (asWORD)retQW; + val[1] = 0; + val[2] = 0; + val[3] = 0; + } + break; + default: + { + // 32 bits + asDWORD *val = (asDWORD*)ARG_DW(context->regs.valueRegister); + val[0] = (asDWORD)retQW; + val[1] = 0; + } + break; + } + } + else + { + // 64 bit primitive + context->regs.valueRegister = retQW; + } + } + + if( sysFunc->hasAutoHandles ) + { + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + { + args++; + } + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( sysFunc->paramAutoHandles[n] && args[spos] ) + { + // Call the release method on the type + engine->CallObjectMethod((void*)args[spos], descr->parameterTypes[n].GetObjectType()->beh.release); + args[spos] = 0; + } + + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { + spos++; + } + else + { + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + } + + return popSize; +} + +END_AS_NAMESPACE + +#endif // AS_PPC +#endif // AS_MAX_PORTABILITY + diff --git a/AngelScript/source/as_callfunc_ppc_64.cpp b/AngelScript/source/as_callfunc_ppc_64.cpp new file mode 100644 index 000000000..a0da3361a --- /dev/null +++ b/AngelScript/source/as_callfunc_ppc_64.cpp @@ -0,0 +1,999 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_ppc_64.cpp +// +// These functions handle the actual calling of system functions +// +// This version is 64 bit PPC specific +// + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_PPC_64 + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" + +#include +#include + +BEGIN_AS_NAMESPACE + +// This part was written and tested by Jeff Slutter +// from Reactor Zero, Abril, 2007, for PlayStation 3, which +// is a PowerPC 64bit based architecture. Even though it is +// 64bit it seems the pointer size is still 32bit. + +// It still remains to be seen how well this code works +// on other PPC platforms, such as XBox 360, GameCube. + +#define AS_PPC_MAX_ARGS 32 + +// The array used to send values to the correct places. +// Contains a byte of argTypes to indicate the register type to load +// or zero if end of arguments +// The +1 is for when CallThis (object methods) is used +// Extra +1 when returning in memory +// Extra +1 in ppcArgsType to ensure zero end-of-args marker + +// TODO: multithread: The global variables must be removed to make the code thread safe + +extern "C" +{ + enum argTypes { ppcENDARG = 0, ppcINTARG = 1, ppcFLOATARG = 2, ppcDOUBLEARG = 3, ppcLONGARG = 4 }; + static asBYTE ppcArgsType[AS_PPC_MAX_ARGS + 1 + 1 + 1]; + static asDWORD ppcArgs[2*AS_PPC_MAX_ARGS + 1 + 1]; +} + +// NOTE: these values are for PowerPC 64 bit. I'm sure things are different for PowerPC 32bit, but I don't have one. +// I'm pretty sure that PPC 32bit sets up a stack frame slightly different (only 24 bytes for linkage area for instance) +#define PPC_LINKAGE_SIZE (0x30) // how big the PPC linkage area is in a stack frame +#define PPC_NUM_REGSTORE (10) // how many registers of the PPC we need to store/restore for ppcFunc64() +#define PPC_REGSTORE_SIZE (8*PPC_NUM_REGSTORE) // how many bytes are required for register store/restore +#define EXTRA_STACK_SIZE (PPC_LINKAGE_SIZE + PPC_REGSTORE_SIZE) // memory required, not including parameters, for the stack frame +#define PPC_STACK_SIZE(numParams) ( -(( ( (((numParams)<8)?8:(numParams))<<3) + EXTRA_STACK_SIZE + 15 ) & ~15) ) // calculates the total stack size needed for ppcFunc64, must pad to 16bytes + +// This is PowerPC 64 bit specific +// Loads all data into the correct places and calls the function. +// ppcArgsType is an array containing a byte type (enum argTypes) for each argument. +// StackArgSizeInBytes is the size in bytes of the stack frame (takes into account linkage area, etc. must be multiple of 16) +extern "C" asQWORD ppcFunc64(const asDWORD* argsPtr, int StackArgSizeInBytes, asDWORD func); +asm("" + ".text\n" + ".align 4\n" + ".p2align 4,,15\n" + ".globl .ppcFunc64\n" + ".ppcFunc64:\n" + + // function prolog + "std %r22, -0x08(%r1)\n" // we need a register other than r0, to store the old stack pointer + "mr %r22, %r1\n" // store the old stack pointer, for now (to make storing registers easier) + "stdux %r1, %r1, %r4\n" // atomically store and update the stack pointer for the new stack frame (in case of a signal/interrupt) + "mflr %r0\n" // get the caller's LR register + "std %r0, 0x10(%r22)\n" // store the caller's LR register + "std %r23, -0x10(%r22)\n" // + "std %r24, -0x18(%r22)\n" // + "std %r25, -0x20(%r22)\n" // + "std %r26, -0x28(%r22)\n" // + "std %r27, -0x30(%r22)\n" // + "std %r28, -0x38(%r22)\n" // + "std %r29, -0x40(%r22)\n" // + "std %r30, -0x48(%r22)\n" // + "std %r31, -0x50(%r22)\n" // + "std %r3, 0x30(%r22)\n" // save our parameters + "std %r4, 0x38(%r22)\n" // + "std %r5, 0x40(%r22)\n" // + "mr %r31, %r1\n" // functions tend to store the stack pointer here too + + // initial registers for the function + "mr %r29, %r3\n" // (r29) args list + "lwz %r27, 0(%r5)\n" // load the function pointer to call. func actually holds the pointer to our function + "addi %r26, %r1, 0x30\n" // setup the pointer to the parameter area to the function we're going to call + "sub %r0,%r0,%r0\n" // zero out r0 + "mr %r23,%r0\n" // zero out r23, which holds the number of used GPR registers + "mr %r22,%r0\n" // zero our r22, which holds the number of used float registers + + // load the global ppcArgsType which holds the types of arguments for each argument + "lis %r25, ppcArgsType@ha\n" // load the upper 16 bits of the address to r25 + "addi %r25, %r25, ppcArgsType@l\n" // load the lower 16 bits of the address to r25 + "subi %r25, %r25, 1\n" // since we increment r25 on its use, we'll pre-decrement it + + // loop through the arguments + "ppcNextArg:\n" + "addi %r25, %r25, 1\n" // increment r25, our arg type pointer + // switch based on the current argument type (0:end, 1:int, 2:float 3:double) + "lbz %r24, 0(%r25)\n" // load the current argument type (it's a byte) + "mulli %r24, %r24, 4\n" // our jump table has 4 bytes per case (1 instruction) + "lis %r30, ppcTypeSwitch@ha\n" // load the address of the jump table for the switch + "addi %r30, %r30, ppcTypeSwitch@l\n" + "add %r0, %r30, %r24\n" // offset by our argument type + "mtctr %r0\n" // load the jump address into CTR + "bctr\n" // jump into the jump table/switch + "nop\n" + // the jump table/switch based on the current argument type + "ppcTypeSwitch:\n" + "b ppcArgsEnd\n" + "b ppcArgIsInteger\n" + "b ppcArgIsFloat\n" + "b ppcArgIsDouble\n" + "b ppcArgIsLong\n" + + // when we get here we have finished processing all the arguments + // everything is ready to go to call the function + "ppcArgsEnd:\n" + "mtctr %r27\n" // the function pointer is stored in r27, load that into CTR + "bctrl\n" // call the function. We have to do it this way so that the LR gets the proper + "nop\n" // return value (the next instruction below). So we have to branch from CTR instead of LR. + // when we get here, the function has returned, this is the function epilog + "ld %r11,0x00(%r1)\n" // load in the caller's stack pointer + "ld %r0,0x10(%r11)\n" // load in the caller's LR + "mtlr %r0\n" // restore the caller's LR + "ld %r22, -0x08(%r11)\n" // load registers + "ld %r23, -0x10(%r11)\n" // + "ld %r24, -0x18(%r11)\n" // + "ld %r25, -0x20(%r11)\n" // + "ld %r26, -0x28(%r11)\n" // + "ld %r27, -0x30(%r11)\n" // + "ld %r28, -0x38(%r11)\n" // + "ld %r29, -0x40(%r11)\n" // + "ld %r30, -0x48(%r11)\n" // + "ld %r31, -0x50(%r11)\n" // + "mr %r1, %r11\n" // restore the caller's SP + "blr\n" // return back to the caller + "nop\n" + // Integer argument (GPR register) + "ppcArgIsInteger:\n" + "lis %r30,ppcLoadIntReg@ha\n" // load the address to the jump table for integer registers + "addi %r30, %r30, ppcLoadIntReg@l\n" + "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes) + "add %r0, %r0, %r30\n" // calculate ppcLoadIntReg[numUsedGPRRegs] + "lwz %r30,0(%r29)\n" // load the next argument from the argument list into r30 + "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers) + "bgt ppcLoadIntRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there + "mtctr %r0\n" // load the address of our ppcLoadIntReg jump table (we're below 8 GPR registers) + "bctr\n" // load the argument into a GPR register + "nop\n" + // jump table for GPR registers, for the first 8 GPR arguments + "ppcLoadIntReg:\n" + "mr %r3,%r30\n" // arg0 (to r3) + "b ppcLoadIntRegUpd\n" + "mr %r4,%r30\n" // arg1 (to r4) + "b ppcLoadIntRegUpd\n" + "mr %r5,%r30\n" // arg2 (to r5) + "b ppcLoadIntRegUpd\n" + "mr %r6,%r30\n" // arg3 (to r6) + "b ppcLoadIntRegUpd\n" + "mr %r7,%r30\n" // arg4 (to r7) + "b ppcLoadIntRegUpd\n" + "mr %r8,%r30\n" // arg5 (to r8) + "b ppcLoadIntRegUpd\n" + "mr %r9,%r30\n" // arg6 (to r9) + "b ppcLoadIntRegUpd\n" + "mr %r10,%r30\n" // arg7 (to r10) + "b ppcLoadIntRegUpd\n" + + // all GPR arguments still go on the stack + "ppcLoadIntRegUpd:\n" + "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list + "addi %r23, %r23, 1\n" // count a used GPR register + "addi %r29, %r29, 4\n" // move to the next argument on the list + "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next + "b ppcNextArg\n" // next argument + + // single Float argument + "ppcArgIsFloat:\n" + "lis %r30,ppcLoadFloatReg@ha\n" // get the base address of the float register jump table + "addi %r30, %r30, ppcLoadFloatReg@l\n" + "mulli %r0, %r22 ,8\n" // each jump table entry is 8 bytes + "add %r0, %r0, %r30\n" // calculate the offset to ppcLoadFloatReg[numUsedFloatReg] + "lfs 0, 0(%r29)\n" // load the next argument as a float into f0 + "cmpwi %r22, 13\n" // can't load more than 13 float/double registers + "bgt ppcLoadFloatRegUpd\n" // if we're beyond 13 registers, just fall to inserting into the stack + "mtctr %r0\n" // jump into the float jump table + "bctr\n" + "nop\n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadFloatReg:\n" + "fmr 1,0\n" // arg0 (f1) + "b ppcLoadFloatRegUpd\n" + "fmr 2,0\n" // arg1 (f2) + "b ppcLoadFloatRegUpd\n" + "fmr 3,0\n" // arg2 (f3) + "b ppcLoadFloatRegUpd\n" + "fmr 4,0\n" // arg3 (f4) + "b ppcLoadFloatRegUpd\n" + "fmr 5,0\n" // arg4 (f5) + "b ppcLoadFloatRegUpd\n" + "fmr 6,0\n" // arg5 (f6) + "b ppcLoadFloatRegUpd\n" + "fmr 7,0\n" // arg6 (f7) + "b ppcLoadFloatRegUpd\n" + "fmr 8,0\n" // arg7 (f8) + "b ppcLoadFloatRegUpd\n" + "fmr 9,0\n" // arg8 (f9) + "b ppcLoadFloatRegUpd\n" + "fmr 10,0\n" // arg9 (f10) + "b ppcLoadFloatRegUpd\n" + "fmr 11,0\n" // arg10 (f11) + "b ppcLoadFloatRegUpd\n" + "fmr 12,0\n" // arg11 (f12) + "b ppcLoadFloatRegUpd\n" + "fmr 13,0\n" // arg12 (f13) + "b ppcLoadFloatRegUpd\n" + "nop\n" + // all float arguments still go on the stack + "ppcLoadFloatRegUpd:\n" + "stfs 0, 0x04(%r26)\n" // store, as a single float, f0 (current argument) on to the stack argument list + "addi %r23, %r23, 1\n" // a float register eats up a GPR register + "addi %r22, %r22, 1\n" // ...and, of course, a float register + "addi %r29, %r29, 4\n" // move to the next argument in the list + "addi %r26, %r26, 8\n" // move to the next stack slot + "b ppcNextArg\n" // on to the next argument + "nop\n" + // double Float argument + "ppcArgIsDouble:\n" + "lis %r30, ppcLoadDoubleReg@ha\n" // load the base address of the jump table for double registers + "addi %r30, %r30, ppcLoadDoubleReg@l\n" + "mulli %r0, %r22, 8\n" // each slot of the jump table is 8 bytes + "add %r0, %r0, %r30\n" // calculate ppcLoadDoubleReg[numUsedFloatReg] + "lfd 0, 0(%r29)\n" // load the next argument, as a double float, into f0 + "cmpwi %r22,13\n" // the first 13 floats must go into float registers also + "bgt ppcLoadDoubleRegUpd\n" // if we're beyond 13, then just put on to the stack + "mtctr %r0\n" // we're under 13, first load our register + "bctr\n" // jump into the jump table + "nop\n" + // jump table for float registers, for the first 13 float arguments + "ppcLoadDoubleReg:\n" + "fmr 1,0\n" // arg0 (f1) + "b ppcLoadDoubleRegUpd\n" + "fmr 2,0\n" // arg1 (f2) + "b ppcLoadDoubleRegUpd\n" + "fmr 3,0\n" // arg2 (f3) + "b ppcLoadDoubleRegUpd\n" + "fmr 4,0\n" // arg3 (f4) + "b ppcLoadDoubleRegUpd\n" + "fmr 5,0\n" // arg4 (f5) + "b ppcLoadDoubleRegUpd\n" + "fmr 6,0\n" // arg5 (f6) + "b ppcLoadDoubleRegUpd\n" + "fmr 7,0\n" // arg6 (f7) + "b ppcLoadDoubleRegUpd\n" + "fmr 8,0\n" // arg7 (f8) + "b ppcLoadDoubleRegUpd\n" + "fmr 9,0\n" // arg8 (f9) + "b ppcLoadDoubleRegUpd\n" + "fmr 10,0\n" // arg9 (f10) + "b ppcLoadDoubleRegUpd\n" + "fmr 11,0\n" // arg10 (f11) + "b ppcLoadDoubleRegUpd\n" + "fmr 12,0\n" // arg11 (f12) + "b ppcLoadDoubleRegUpd\n" + "fmr 13,0\n" // arg12 (f13) + "b ppcLoadDoubleRegUpd\n" + "nop\n" + // all float arguments still go on the stack + "ppcLoadDoubleRegUpd:\n" + "stfd 0,0(%r26)\n" // store f0, as a double, into the argument list on the stack + "addi %r23, %r23, 1\n" // a double float eats up one GPR + "addi %r22, %r22, 1\n" // ...and, of course, a float + "addi %r29, %r29, 8\n" // increment to our next argument we need to process (8 bytes for the 64bit float) + "addi %r26, %r26, 8\n" // increment to the next slot on the argument list on the stack (8 bytes) + "b ppcNextArg\n" // on to the next argument + "nop\n" + + // Long (64 bit int) argument + "ppcArgIsLong:\n" + "lis %r30,ppcLoadLongReg@ha\n" // load the address to the jump table for integer64 + "addi %r30, %r30, ppcLoadLongReg@l\n" + "mulli %r0, %r23, 8\n" // each item in the jump table is 2 instructions (8 bytes) + "add %r0, %r0, %r30\n" // calculate ppcLoadLongReg[numUsedGPRRegs] + "ld %r30,0(%r29)\n" // load the next argument from the argument list into r30 + "cmpwi %r23, 8\n" // we can only load GPR3 through GPR10 (8 registers) + "bgt ppcLoadLongRegUpd\n" // if we're beyond 8 GPR registers, we're in the stack, go there + "mtctr %r0\n" // load the address of our ppcLoadLongReg jump table (we're below 8 GPR registers) + "bctr\n" // load the argument into a GPR register + "nop\n" + // jump table for GPR registers, for the first 8 GPR arguments + "ppcLoadLongReg:\n" + "mr %r3,%r30\n" // arg0 (to r3) + "b ppcLoadLongRegUpd\n" + "mr %r4,%r30\n" // arg1 (to r4) + "b ppcLoadLongRegUpd\n" + "mr %r5,%r30\n" // arg2 (to r5) + "b ppcLoadLongRegUpd\n" + "mr %r6,%r30\n" // arg3 (to r6) + "b ppcLoadLongRegUpd\n" + "mr %r7,%r30\n" // arg4 (to r7) + "b ppcLoadLongRegUpd\n" + "mr %r8,%r30\n" // arg5 (to r8) + "b ppcLoadLongRegUpd\n" + "mr %r9,%r30\n" // arg6 (to r9) + "b ppcLoadLongRegUpd\n" + "mr %r10,%r30\n" // arg7 (to r10) + "b ppcLoadLongRegUpd\n" + + // all GPR arguments still go on the stack + "ppcLoadLongRegUpd:\n" + "std %r30,0(%r26)\n" // store the argument into the next slot on the stack's argument list + "addi %r23, %r23, 1\n" // count a used GPR register + "addi %r29, %r29, 8\n" // move to the next argument on the list + "addi %r26, %r26, 8\n" // adjust our argument stack pointer for the next + "b ppcNextArg\n" // next argument +); + +static asDWORD GetReturnedFloat(void) +{ + asDWORD f; + asm(" stfs 1, %0\n" : "=m"(f)); + return f; +} + +static asQWORD GetReturnedDouble(void) +{ + asQWORD f; + asm(" stfd 1, %0\n" : "=m"(f)); + return f; +} + +// puts the arguments in the correct place in the stack array. See comments above. +static void stackArgs( const asDWORD *args, const asBYTE *argsType, int &numIntArgs, int &numFloatArgs, int &numDoubleArgs, int &numLongArgs ) +{ + // initialize our offset based on any already placed arguments + int i; + int argWordPos = numIntArgs + numFloatArgs + (numDoubleArgs*2) + (numLongArgs*2); + int typeOffset = numIntArgs + numFloatArgs + numDoubleArgs + numLongArgs; + + int typeIndex; + for( i = 0, typeIndex = 0; ; i++, typeIndex++ ) + { + // store the type + ppcArgsType[typeOffset++] = argsType[typeIndex]; + if( argsType[typeIndex] == ppcENDARG ) + break; + + switch( argsType[typeIndex] ) + { + case ppcFLOATARG: + { + // stow float + ppcArgs[argWordPos] = args[i]; // it's just a bit copy + numFloatArgs++; + argWordPos++; //add one word + } + break; + + case ppcDOUBLEARG: + { + // stow double + memcpy( &ppcArgs[argWordPos], &args[i], sizeof(double) ); // we have to do this because of alignment + numDoubleArgs++; + argWordPos+=2; //add two words + i++;//doubles take up 2 argument slots + } + break; + + case ppcINTARG: + { + // stow register + ppcArgs[argWordPos] = args[i]; + numIntArgs++; + argWordPos++; + } + break; + + case ppcLONGARG: + { + // stow long + memcpy( &ppcArgs[argWordPos], &args[i], 8 ); // for alignment purposes, we use memcpy + numLongArgs++; + argWordPos += 2; // add two words + i++; // longs take up 2 argument slots + } + break; + } + } + + // close off the argument list (if we have max args we won't close it off until here) + ppcArgsType[typeOffset] = ppcENDARG; +} + +static asQWORD CallCDeclFunction(const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs ); + numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs; + } + else + { + // no arguments, cap the type list + ppcArgsType[baseArgCount] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object (unless we are returning in memory) +static asQWORD CallThisCallFunction(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory ) +{ + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // the first argument is the 'this' of the object + ppcArgs[baseArgCount] = (asDWORD)obj; + ppcArgsType[baseArgCount++] = ppcINTARG; + ppcArgsType[baseArgCount] = ppcENDARG; + + // put the arguments in the correct places in the ppcArgs array + int numTotalArgs = baseArgCount; + if( argSize > 0 ) + { + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs ); + numTotalArgs = intArgs + floatArgs + doubleArgs + longArgs; + } + + // call the function with the arguments + return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +// NOTE: on PPC the order for the args is reversed +static asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD* pArgs, const asBYTE *pArgsType, int argSize, asDWORD func, void *retInMemory) +{ + UNUSED_VAR(argSize); + int baseArgCount = 0; + if( retInMemory ) + { + // the first argument is the 'return in memory' pointer + ppcArgs[0] = (asDWORD)retInMemory; + ppcArgsType[0] = ppcINTARG; + ppcArgsType[1] = ppcENDARG; + baseArgCount = 1; + } + + // stack any of the arguments + int intArgs = baseArgCount, floatArgs = 0, doubleArgs = 0, longArgs = 0; + stackArgs( pArgs, pArgsType, intArgs, floatArgs, doubleArgs, longArgs ); + int numTotalArgs = intArgs + floatArgs + doubleArgs; + + // can we fit the object in at the end? + if( numTotalArgs < AS_PPC_MAX_ARGS ) + { + // put the object pointer at the end + int argPos = intArgs + floatArgs + (doubleArgs * 2) + (longArgs *2); + ppcArgs[argPos] = (asDWORD)obj; + ppcArgsType[numTotalArgs++] = ppcINTARG; + ppcArgsType[numTotalArgs] = ppcENDARG; + } + + // call the function with the arguments + return ppcFunc64( ppcArgs, PPC_STACK_SIZE(numTotalArgs), func ); +} + +// returns true if the given parameter is a 'variable argument' +inline bool IsVariableArgument( asCDataType type ) +{ + return (type.GetTokenType() == ttQuestion) ? true : false; +} + +int CallSystemFunction(int id, asCContext *context, void *objectPointer) +{ + // use a working array of types, we'll configure the final one in stackArgs + asBYTE argsType[AS_PPC_MAX_ARGS + 1 + 1 + 1]; + memset( argsType, 0, sizeof(argsType)); + + asCScriptEngine *engine = context->engine; + asCScriptFunction *descr = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + { + // we're only handling native calls, handle generic calls in here + return context->CallGeneric( id, objectPointer); + } + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + int popSize = paramSize; + asDWORD *args = context->regs.stackPointer; + void *obj = NULL; + asDWORD *vftable = NULL; + void *retObjPointer = NULL; // for system functions that return AngelScript objects + void *retInMemPointer = NULL; // for host functions that need to return data in memory instead of by register + int a; + + // convert the parameters that are < 4 bytes from little endian to big endian + int argDwordOffset = 0; + int totalArgumentCount = 0; + + // if this is a THISCALL function and no object pointer was given, then the + // first argument on the stack is the object pointer -- we MUST skip it for doing + // the endian flipping. + if( ( callConv >= ICC_THISCALL ) && (objectPointer == NULL) ) + { + ++argDwordOffset; + } + + for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a ) + { + // get the size for the parameter + int numBytes = descr->parameterTypes[a].GetSizeInMemoryBytes(); + ++totalArgumentCount; + + // is this a variable argument? + // for variable arguments, the typeID will always follow...but we know it is 4 bytes + // so we can skip that parameter automatically. + bool isVarArg = IsVariableArgument( descr->parameterTypes[a] ); + if( isVarArg ) + { + ++totalArgumentCount; + } + + if( numBytes >= 4 || descr->parameterTypes[a].IsReference() || descr->parameterTypes[a].IsObjectHandle() ) + { + // DWORD or larger parameter --- no flipping needed + argDwordOffset += descr->parameterTypes[a].GetSizeOnStackDWords(); + } + else + { + // flip + assert( numBytes == 1 || numBytes == 2 ); + switch( numBytes ) + { + case 1: + { + volatile asBYTE *bPtr = (asBYTE*)ARG_DW(args[argDwordOffset]); + asBYTE t = bPtr[0]; + bPtr[0] = bPtr[3]; + bPtr[3] = t; + t = bPtr[1]; + bPtr[1] = bPtr[2]; + bPtr[2] = t; + } + break; + case 2: + { + volatile asWORD *wPtr = (asWORD*)ARG_DW(args[argDwordOffset]); + asWORD t = wPtr[0]; + wPtr[0] = wPtr[1]; + wPtr[1] = t; + } + break; + } + ++argDwordOffset; + } + + if( isVarArg ) + { + // skip the implicit typeID + ++argDwordOffset; + } + } + + // Objects returned to AngelScript must be via an object pointer. This goes for + // ALL objects, including those of simple, complex, primitive or float. Whether + // the host system (PPC in this case) returns the 'object' as a pointer depends on the type of object. + context->regs.objectType = descr->returnType.GetObjectType(); + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + // Allocate the memory for the object + retObjPointer = engine->CallAlloc( descr->returnType.GetObjectType() ); + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory on the host system + callConv++; + retInMemPointer = retObjPointer; + } + } + + // make sure that host functions that will be returning in memory have a memory pointer + assert( sysFunc->hostReturnInMemory==false || retInMemPointer!=NULL ); + + if( callConv >= ICC_THISCALL ) + { + if( objectPointer ) + { + obj = objectPointer; + } + else + { + // The object pointer should be popped from the context stack + popSize++; + + // Check for null pointer + obj = (void*)*(args); + if( obj == NULL ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + if( retObjPointer ) + { + engine->CallFree(retObjPointer); + } + return 0; + } + + // Add the base offset for multiple inheritance + obj = (void*)(int(obj) + sysFunc->baseOffset); + + // Skip the object pointer + args++; + } + } + assert( totalArgumentCount <= AS_PPC_MAX_ARGS ); + + // mark all float/double/int arguments + int argIndex = 0; + for( a = 0; a < (int)descr->parameterTypes.GetLength(); ++a, ++argIndex ) + { + // get the base type + argsType[argIndex] = ppcINTARG; + if( descr->parameterTypes[a].IsFloatType() && !descr->parameterTypes[a].IsReference() ) + { + argsType[argIndex] = ppcFLOATARG; + } + if( descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() ) + { + argsType[argIndex] = ppcDOUBLEARG; + } + if( descr->parameterTypes[a].GetSizeOnStackDWords() == 2 && !descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() ) + { + argsType[argIndex] = ppcLONGARG; + } + + // if it is a variable argument, account for the typeID + if( IsVariableArgument(descr->parameterTypes[a]) ) + { + // implicitly add another parameter (AFTER the parameter above), for the TypeID + argsType[++argIndex] = ppcINTARG; + } + } + assert( argIndex == totalArgumentCount ); + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { + #ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + ++paramSize; + } + else + #endif + { + // NOTE: we may have to do endian flipping here + + // Copy the object's memory to the buffer + memcpy( ¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes() ); + + // Delete the original memory + engine->CallFree( *(char**)(args+spos) ); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + { + paramBuffer[dpos++] = args[spos++]; + } + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + + // if this was a variable argument parameter, then account for the implicit typeID + if( IsVariableArgument( descr->parameterTypes[n] ) ) + { + // the TypeID is just a DWORD + paramBuffer[dpos++] = args[spos++]; + ++paramSize; + } + } + + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + // one last verification to make sure things are how we expect + assert( (retInMemPointer!=NULL && sysFunc->hostReturnInMemory) || (retInMemPointer==NULL && !sysFunc->hostReturnInMemory) ); + context->isCallingSystemFunction = true; + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction( args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction( obj, args, argsType, paramSize, vftable[asDWORD(func)>>2], retInMemPointer ); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast( obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction( obj, args, argsType, paramSize, (asDWORD)func, retInMemPointer ); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + context->isCallingSystemFunction = false; + +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) + { + // Need to free the complex objects passed by value + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( int n = 0; n < (int)descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && + !descr->parameterTypes[n].IsReference() && + (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) ) + { + void *obj = (void*)args[spos++]; + asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh; + if( beh->destruct ) + { + engine->CallObjectMethod(obj, beh->destruct); + } + + engine->CallFree(obj); + } + else + { + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + + if( IsVariableArgument(descr->parameterTypes[n]) ) + { + // account for the implicit TypeID + ++spos; + } + } + } +#endif + + // Store the returned value in our stack + if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) + { + if( descr->returnType.IsObjectHandle() ) + { + // returning an object handle + context->regs.objectRegister = (void*)(asDWORD)retQW; + + if( sysFunc->returnAutoHandle && context->regs.objectRegister ) + { + engine->CallObjectMethod(context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref); + } + } + else + { + // returning an object + if( !sysFunc->hostReturnInMemory ) + { + // In this case, AngelScript wants an object pointer back, but the host system + // didn't use 'return in memory', so its results were passed back by the return register. + // We have have to take the results of the return register and store them IN the pointer for the object. + // The data for the object could fit into a register; we need to copy that data to the object pointer's + // memory. + assert( retInMemPointer == NULL ); + assert( retObjPointer != NULL ); + + // Copy the returned value to the pointer sent by the script engine + if( sysFunc->hostReturnSize == 1 ) + { + *(asDWORD*)retObjPointer = (asDWORD)retQW; + } + else + { + *(asQWORD*)retObjPointer = retQW; + } + } + else + { + // In this case, AngelScript wants an object pointer back, and the host system + // used 'return in memory'. So its results were already passed back in memory, and + // stored in the object pointer. + assert( retInMemPointer != NULL ); + assert( retObjPointer != NULL ); + } + + // store the return results into the object register + context->regs.objectRegister = retObjPointer; + } + } + else + { + // Store value in returnVal register + if( sysFunc->hostReturnFloat ) + { + // floating pointer primitives + if( sysFunc->hostReturnSize == 1 ) + { + // single float + *(asDWORD*)&context->regs.valueRegister = GetReturnedFloat(); + } + else + { + // double float + context->regs.valueRegister = GetReturnedDouble(); + } + } + else if( sysFunc->hostReturnSize == 1 ) + { + // <=32 bit primitives + + // due to endian issues we need to handle return values, that are + // less than a DWORD (32 bits) in size, special + int numBytes = descr->returnType.GetSizeInMemoryBytes(); + if( descr->returnType.IsReference() ) numBytes = 4; + switch( numBytes ) + { + case 1: + { + // 8 bits + asBYTE *val = (asBYTE*)ARG_DW(context->regs.valueRegister); + val[0] = (asBYTE)retQW; + val[1] = 0; + val[2] = 0; + val[3] = 0; + val[4] = 0; + val[5] = 0; + val[6] = 0; + val[7] = 0; + } + break; + case 2: + { + // 16 bits + asWORD *val = (asWORD*)ARG_DW(context->regs.valueRegister); + val[0] = (asWORD)retQW; + val[1] = 0; + val[2] = 0; + val[3] = 0; + } + break; + default: + { + // 32 bits + asDWORD *val = (asDWORD*)ARG_DW(context->regs.valueRegister); + val[0] = (asDWORD)retQW; + val[1] = 0; + } + break; + } + } + else + { + // 64 bit primitive + context->regs.valueRegister = retQW; + } + } + + if( sysFunc->hasAutoHandles ) + { + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + { + args++; + } + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( sysFunc->paramAutoHandles[n] && args[spos] ) + { + // Call the release method on the type + engine->CallObjectMethod((void*)args[spos], descr->parameterTypes[n].GetObjectType()->beh.release); + args[spos] = 0; + } + + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { + spos++; + } + else + { + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + + if( IsVariableArgument( descr->parameterTypes[n] ) ) + { + // account for the implicit TypeID + ++spos; + } + } + } + + return popSize; +} + +END_AS_NAMESPACE + +#endif // AS_PPC_64 +#endif // AS_MAX_PORTABILITY + diff --git a/AngelScript/source/as_callfunc_sh4.cpp b/AngelScript/source/as_callfunc_sh4.cpp new file mode 100644 index 000000000..a2edd2e80 --- /dev/null +++ b/AngelScript/source/as_callfunc_sh4.cpp @@ -0,0 +1,517 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_sh4.cpp +// +// These functions handle the actual calling of system functions +// +// This version is SH4 specific and was originally written +// by Fredrik Ehnbom in May, 2004 +// Later updated for angelscript 2.0.0 by Fredrik Ehnbom in Jan, 2005 + +// References: +// * http://www.renesas.com/avs/resource/japan/eng/pdf/mpumcu/e602156_sh4.pdf +// * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcechp40/html/_callsh4_SH_4_Calling_Standard.asp + + +#include "as_config.h" + +#ifndef MAX_PORTABILITY +#ifdef AS_SH4 + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" + +#include +#include + +BEGIN_AS_NAMESPACE + +#define AS_SH4_MAX_ARGS 32 +// The array used to send values to the correct places. +// first 0-4 regular values to load into the r4-r7 registers +// then 0-8 float values to load into the fr4-fr11 registers +// then (AS_SH4_MAX_ARGS - 12) values to load onto the stack +// the +1 is for when CallThis (object methods) is used +// extra +1 when returning in memory +extern "C" { +static asDWORD sh4Args[AS_SH4_MAX_ARGS + 1 + 1]; +} + +// Loads all data into the correct places and calls the function. +// intArgSize is the size in bytes for how much data to put in int registers +// floatArgSize is the size in bytes for how much data to put in float registers +// stackArgSize is the size in bytes for how much data to put on the callstack +extern "C" asQWORD sh4Func(int intArgSize, int floatArgSize, int stackArgSize, asDWORD func); + +asm("" +" .align 4\n" +" .global _sh4Func\n" +"_sh4Func:\n" +" mov.l r14,@-r15\n" +" mov.l r13,@-r15\n" +" mov.l r12,@-r15\n" +" sts.l pr,@-r15\n" // must be saved since we call a subroutine +" mov r7, r14\n" // func +" mov r6, r13\n" // stackArgSize +" mov.l r5,@-r15\n" // floatArgSize +" mov.l sh4Args,r0\n" +" pref @r0\n" +" mov r4, r1\n" // intArgsize +" mov #33*4,r2\n" +" extu.b r2,r2\n" // make unsigned (33*4 = 132 => 128) +" mov.l @(r0,r2), r2\n" // r2 has adress for when returning in memory +"_sh4f_intarguments:\n" // copy all the int arguments to the respective registers +" mov #4*2*2,r3\n" // calculate how many bytes to skip +" sub r1,r3\n" +" braf r3\n" +" add #-4,r1\n" // we are indexing the array backwards, so subtract one (delayed slot) +" mov.l @(r0,r1),r7\n" // 4 arguments +" add #-4,r1\n" +" mov.l @(r0,r1),r6\n" // 3 arguments +" add #-4,r1\n" +" mov.l @(r0,r1),r5\n" // 2 arguments +" add #-4,r1\n" +" mov.l @(r0,r1),r4\n" // 1 argument +" nop\n" +"_sh4f_floatarguments:\n" // copy all the float arguments to the respective registers +" add #4*4, r0\n" +" mov.l @r15+,r1\n" // floatArgSize +" mov #8*2*2,r3\n" // calculate how many bytes to skip +" sub r1,r3\n" +" braf r3\n" +" add #-4,r1\n" // we are indexing the array backwards, so subtract one (delayed slot) +" fmov.s @(r0,r1),fr11\n" // 8 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr10\n" // 7 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr9\n" // 6 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr8\n" // 5 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr7\n" // 4 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr6\n" // 3 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr5\n" // 2 arguments +" add #-4,r1\n" +" fmov.s @(r0,r1),fr4\n" // 1 argument +" nop\n" +"_sh4f_stackarguments:\n" // copy all the stack argument onto the stack +" add #8*4, r0\n" +" mov r0, r1\n" +" mov #0, r0\n" // init position counter (also used as a 0-check on the line after) +" cmp/eq r0, r13\n" +" bt _sh4f_functioncall\n" // no arguments to push onto the stack +" mov r13, r3\n" // stackArgSize +" sub r3,r15\n" // "allocate" space on the stack +" shlr2 r3\n" // make into a counter +"_sh4f_stackloop:\n" +" mov.l @r1+, r12\n" +" mov.l r12, @(r0, r15)\n" +" add #4, r0\n" +" dt r3\n" +" bf _sh4f_stackloop\n" +"_sh4f_functioncall:\n" +" jsr @r14\n" // no arguments +" nop\n" +" add r13, r15\n" // restore stack position +" lds.l @r15+,pr\n" +" mov.l @r15+, r12\n" +" mov.l @r15+, r13\n" +" rts\n" +" mov.l @r15+, r14\n" // delayed slot +"\n" +" .align 4\n" +"sh4Args:\n" +" .long _sh4Args\n" +); + +// puts the arguments in the correct place in the sh4Args-array. See comments above. +// This could be done better. +inline void splitArgs(const asDWORD *args, int argNum, int &numRegIntArgs, int &numRegFloatArgs, int &numRestArgs, int hostFlags) { + int i; + + int argBit = 1; + for (i = 0; i < argNum; i++) { + if (hostFlags & argBit) { + if (numRegFloatArgs < 12 - 4) { + // put in float register + sh4Args[4 + numRegFloatArgs] = args[i]; + numRegFloatArgs++; + } else { + // put in stack + sh4Args[4 + 8 + numRestArgs] = args[i]; + numRestArgs++; + } + } else { + if (numRegIntArgs < 8 - 4) { + // put in int register + sh4Args[numRegIntArgs] = args[i]; + numRegIntArgs++; + } else { + // put in stack + sh4Args[4 + 8 + numRestArgs] = args[i]; + numRestArgs++; + } + } + argBit <<= 1; + } +} +asQWORD CallCDeclFunction(const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + // put the arguments in the correct places in the sh4Args array + if (argNum > 0) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object +asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 1; + int floatArgs = 0; + int restArgs = 0; + + sh4Args[0] = (asDWORD) obj; + + // put the arguments in the correct places in the sh4Args array + if (argNum >= 1) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +asQWORD CallThisCallFunction_objLast(const void *obj, const asDWORD *args, int argSize, asDWORD func, int flags) +{ + int argNum = argSize >> 2; + + int intArgs = 0; + int floatArgs = 0; + int restArgs = 0; + + + // put the arguments in the correct places in the sh4Args array + if (argNum >= 1) + splitArgs(args, argNum, intArgs, floatArgs, restArgs, flags); + + if (intArgs < 4) { + sh4Args[intArgs] = (asDWORD) obj; + intArgs++; + } else { + sh4Args[4 + 8 + restArgs] = (asDWORD) obj; + restArgs++; + } + + + return sh4Func(intArgs << 2, floatArgs << 2, restArgs << 2, func); +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + + asm("fmov.s fr0, %0\n" : "=m"(f)); + + return f; +} + +// sizeof(double) == 4 with sh-elf-gcc (3.4.0) -m4 +// so this isn't really used... +asQWORD GetReturnedDouble() +{ + asQWORD d; + + asm("fmov dr0, %0\n" : "=m"(d)); + + return d; +} + +int CallSystemFunction(int id, asCContext *context, void *objectPointer) +{ + asCScriptEngine *engine = context->engine; + asSSystemFunctionInterface *sysFunc = engine->scriptFunctions[id]->sysFuncIntf; + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + return context->CallGeneric(id, objectPointer); + + asQWORD retQW = 0; + + asCScriptFunction *descr = engine->scriptFunctions[id]; + + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *args = context->regs.stackPointer; + void *retPointer = 0; + void *obj = 0; + asDWORD *vftable; + int popSize = paramSize; + + context->regs.objectType = descr->returnType.GetObjectType(); + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + // Allocate the memory for the object + retPointer = engine->CallAlloc(descr->returnType.GetObjectType()); + sh4Args[AS_SH4_MAX_ARGS+1] = (asDWORD) retPointer; + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + } + } + + if( callConv >= ICC_THISCALL ) + { + if( objectPointer ) + { + obj = objectPointer; + } + else + { + // The object pointer should be popped from the context stack + popSize++; + + // Check for null pointer + obj = (void*)*(args + paramSize); + if( obj == 0 ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + if( retPointer ) + engine->CallFree(retPointer); + return 0; + } + + // Add the base offset for multiple inheritance + obj = (void*)(int(obj) + sysFunc->baseOffset); + } + } + asASSERT(descr->parameterTypes.GetLength() <= 32); + + // mark all float arguments + int argBit = 1; + int hostFlags = 0; + int intArgs = 0; + for( int a = 0; a < descr->parameterTypes.GetLength(); a++ ) { + if (descr->parameterTypes[a].IsFloatType()) { + hostFlags |= argBit; + } else intArgs++; + argBit <<= 1; + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + for( int n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + context->isCallingSystemFunction = true; + switch( callConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + retQW = CallCDeclFunction(args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], hostFlags); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + retQW = CallThisCallFunction_objLast(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); + break; + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + context->isCallingSystemFunction = false; + +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) + { + // Need to free the complex objects passed by value + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( int n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && + !descr->parameterTypes[n].IsReference() && + (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) ) + { + void *obj = (void*)args[spos++]; + asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh; + if( beh->destruct ) + engine->CallObjectMethod(obj, beh->destruct); + + engine->CallFree(obj); + } + else + spos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } +#endif + + // Store the returned value in our stack + if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) + { + if( descr->returnType.IsObjectHandle() ) + { + context->regs.objectRegister = (void*)(asDWORD)retQW; + + if( sysFunc->returnAutoHandle && context->regs.objectRegister ) + engine->CallObjectMethod(context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref); + } + else + { + if( !sysFunc->hostReturnInMemory ) + { + // Copy the returned value to the pointer sent by the script engine + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)retPointer = (asDWORD)retQW; + else + *(asQWORD*)retPointer = retQW; + } + + // Store the object in the register + context->regs.objectRegister = retPointer; + } + } + else + { + // Store value in valueRegister + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = GetReturnedFloat(); + else + context->regs.valueRegister = GetReturnedDouble(); + } + else if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = (asDWORD)retQW; + else + context->regs.valueRegister = retQW; + } + + if( sysFunc->hasAutoHandles ) + { + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( int n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( sysFunc->paramAutoHandles[n] && args[spos] ) + { + // Call the release method on the type + engine->CallObjectMethod((void*)args[spos], descr->parameterTypes[n].GetObjectType()->beh.release); + args[spos] = 0; + } + + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + spos++; + else + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + return popSize; +} + +END_AS_NAMESPACE + +#endif // AS_SH4 +#endif // AS_MAX_PORTABILITY + + diff --git a/AngelScript/source/as_callfunc_x64_gcc.cpp b/AngelScript/source/as_callfunc_x64_gcc.cpp new file mode 100644 index 000000000..daeea7cac --- /dev/null +++ b/AngelScript/source/as_callfunc_x64_gcc.cpp @@ -0,0 +1,593 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +/* + * Implements the AMD64 calling convention for gcc-based 64bit Unices + * + * Author: Ionut "gargltk" Leonte + * + * Initial author: niteice + */ + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_X64_GCC + +#include "as_scriptengine.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +enum argTypes { x64ENDARG = 0, x64INTARG = 1, x64FLOATARG = 2, x64DOUBLEARG = 3, x64VARIABLE = 4 }; +typedef asQWORD ( *funcptr_t )( void ); + +#define X64_MAX_ARGS 32 +#define MAX_CALL_INT_REGISTERS 6 +#define MAX_CALL_SSE_REGISTERS 8 +#define CALLSTACK_MULTIPLIER 2 +#define X64_CALLSTACK_SIZE ( X64_MAX_ARGS + MAX_CALL_SSE_REGISTERS + 3 ) + +#define PUSH_LONG( val ) \ + __asm__ __volatile__ ( \ + "mov %0, %%rax\r\n" \ + "push %%rax" \ + : \ + : "m" ( val ) \ + ) + +#define POP_LONG( reg ) \ + __asm__ __volatile__ ( \ + "popq %rax\r\n" \ + "movq %rax, " reg \ + ) + + +#define ASM_GET_REG( name, dest ) \ + __asm__ __volatile__ ( \ + "mov %" name ", %0\r\n" \ + : \ + : "m" ( dest ) \ + ) + +static asDWORD GetReturnedFloat() +{ + float retval = 0.0f; + asDWORD ret = 0; + + __asm__ __volatile__ ( + "lea %0, %%rax\r\n" + "movss %%xmm0, (%%rax)" + : /* no output */ + : "m" (retval) + : "%rax" + ); + + /* We need to avoid implicit conversions from float to unsigned - we need + a bit-wise-correct-and-complete copy of the value */ + memcpy( &ret, &retval, sizeof( ret ) ); + + return ( asDWORD )ret; +} + +static asQWORD GetReturnedDouble() +{ + double retval = 0.0f; + asQWORD ret = 0; + + __asm__ __volatile__ ( + "lea %0, %%rax\r\n" + "movlpd %%xmm0, (%%rax)" + : /* no optput */ + : "m" (retval) + : "%rax" + ); + /* We need to avoid implicit conversions from double to unsigned long long - we need + a bit-wise-correct-and-complete copy of the value */ + memcpy( &ret, &retval, sizeof( ret ) ); + + return ret; +} + +static asQWORD X64_CallFunction( const asDWORD* pArgs, const asBYTE *pArgsType, void *func ) +{ + asQWORD retval = 0; + asQWORD ( *call )() = (asQWORD (*)())func; + int i = 0; + + /* push the stack parameters */ + for ( i = MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS; pArgsType[i] != x64ENDARG && ( i < X64_MAX_ARGS + MAX_CALL_SSE_REGISTERS + 3 ); i++ ) { + PUSH_LONG( pArgs[i * CALLSTACK_MULTIPLIER] ); + } + + /* push integer parameters */ + for ( i = 0; i < MAX_CALL_INT_REGISTERS; i++ ) { + PUSH_LONG( pArgs[i * CALLSTACK_MULTIPLIER] ); + } + + /* push floating point parameters */ + for ( i = MAX_CALL_INT_REGISTERS; i < MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS; i++ ) { + PUSH_LONG( pArgs[i * CALLSTACK_MULTIPLIER] ); + } + + /* now pop the registers in reverse order and make the call */ + POP_LONG( "%xmm7" ); + POP_LONG( "%xmm6" ); + POP_LONG( "%xmm5" ); + POP_LONG( "%xmm4" ); + POP_LONG( "%xmm3" ); + POP_LONG( "%xmm2" ); + POP_LONG( "%xmm1" ); + POP_LONG( "%xmm0" ); + + POP_LONG( "%r9" ); + POP_LONG( "%r8" ); + POP_LONG( "%rcx" ); + POP_LONG( "%rdx" ); + POP_LONG( "%rsi" ); + POP_LONG( "%rdi" ); + + // call the function with the arguments + retval = call(); + return retval; +} + +// returns true if the given parameter is a 'variable argument' +inline bool IsVariableArgument( asCDataType type ) +{ + return ( type.GetTokenType() == ttQuestion ) ? true : false; +} + +int CallSystemFunction( int id, asCContext *context, void *objectPointer ) +{ + asCScriptEngine *engine = context->engine; + asCScriptFunction *descr = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = engine->scriptFunctions[id]->sysFuncIntf; + int callConv = sysFunc->callConv; + + asQWORD retQW = 0; + asQWORD retQW2 = 0; + void *func = ( void * )sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *args = context->regs.stackPointer; + asDWORD *stack_pointer = context->regs.stackPointer; + void *retPointer = 0; + void *obj = 0; + funcptr_t *vftable = NULL; + int popSize = paramSize; + int totalArgumentCount = 0; + int n = 0; + int base_n = 0; + int a = 0; + int param_pre = 0; + int param_post = 0; + int argIndex = 0; + int argumentCount = 0; + + asDWORD tempBuff[CALLSTACK_MULTIPLIER * X64_CALLSTACK_SIZE] = { 0 }; + asBYTE tempType[X64_CALLSTACK_SIZE] = { 0 }; + + asDWORD paramBuffer[CALLSTACK_MULTIPLIER * X64_CALLSTACK_SIZE] = { 0 }; + asBYTE argsType[X64_CALLSTACK_SIZE] = { 0 }; + + asBYTE argsSet[X64_CALLSTACK_SIZE] = { 0 }; + + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) { + return context->CallGeneric( id, objectPointer ); + } + + context->regs.objectType = descr->returnType.GetObjectType(); + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) { + // Allocate the memory for the object + retPointer = engine->CallAlloc( descr->returnType.GetObjectType() ); + + if( sysFunc->hostReturnInMemory ) { + // The return is made in memory + callConv++; + } + } + + argumentCount = ( int )descr->parameterTypes.GetLength(); + assert( argumentCount <= X64_MAX_ARGS ); + + // TODO: optimize: argsType should be computed in PrepareSystemFunction + for( a = 0; a < argumentCount; ++a, ++argIndex ) { + // get the base type + argsType[argIndex] = x64INTARG; + if ( descr->parameterTypes[a].IsFloatType() && !descr->parameterTypes[a].IsReference() ) { + argsType[argIndex] = x64FLOATARG; + } + if ( descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() ) { + argsType[argIndex] = x64DOUBLEARG; + } + if ( descr->parameterTypes[a].GetSizeOnStackDWords() == 2 && !descr->parameterTypes[a].IsDoubleType() && !descr->parameterTypes[a].IsReference() ) { + argsType[argIndex] = x64INTARG; + } + + if ( IsVariableArgument( descr->parameterTypes[a] ) ) { + argsType[argIndex] = x64VARIABLE; + } + } + assert( argIndex == argumentCount ); + + for ( a = 0; a < argumentCount && totalArgumentCount <= X64_MAX_ARGS; a++ ) { + switch ( argsType[a] ) { + case x64ENDARG: + case x64INTARG: + case x64FLOATARG: + case x64DOUBLEARG: { + if ( totalArgumentCount < X64_MAX_ARGS ) + tempType[totalArgumentCount++] = argsType[a]; + break; + } + case x64VARIABLE: { + if ( totalArgumentCount < X64_MAX_ARGS ) + tempType[totalArgumentCount++] = x64VARIABLE; + if ( totalArgumentCount < X64_MAX_ARGS ) + tempType[totalArgumentCount++] = x64INTARG; + break; + } + } + } + assert( totalArgumentCount <= X64_MAX_ARGS ); + if ( totalArgumentCount > argumentCount ) { + memcpy( argsType, tempType, totalArgumentCount ); + } + memset( tempType, 0, sizeof( tempType ) ); + + // TODO: This should be checked in PrepareSystemFunction +#ifndef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) { + /* I currently know of no way we can predict register usage for passing complex + objects by value when the compiler does not pass them by reference instead. I + will quote the example from the AMD64 ABI to demonstrate this: + + (http://www.x86-64.org/documentation/abi.pdf - page 22) + + ------------------------------ BEGIN EXAMPLE ------------------------------- + + Let us consider the following C code: + + typedef struct { + int a, b; + double d; + } structparm; + + structparm s; + int e, f, g, h, i, j, k; + long double ld; + double m, n; + + extern void func (int e, int f, + structparm s, int g, int h, + long double ld, double m, + double n, int i, int j, int k); + + func (e, f, s, g, h, ld, m, n, i, j, k); + + Register allocation for the call: + --------------------------+--------------------------+------------------- + General Purpose Registers | Floating Point Registers | Stack Frame Offset + --------------------------+--------------------------+------------------- + %rdi: e | %xmm0: s.d | 0: ld + %rsi: f | %xmm1: m | 16: j + %rdx: s.a,s.b | %xmm2: n | 24: k + %rcx: g | | + %r8: h | | + %r9: i | | + --------------------------+--------------------------+------------------- + */ + + context->SetInternalException( TXT_INVALID_CALLING_CONVENTION ); + if( retPointer ) { + engine->CallFree( retPointer ); + } + return 0; + } +#endif + + obj = objectPointer; + if ( !obj && callConv >= ICC_THISCALL ) { + // The object pointer should be popped from the context stack + popSize += AS_PTR_SIZE; + + // Check for null pointer + obj = ( void * )( *( ( asQWORD * )( args ) ) ); + stack_pointer += AS_PTR_SIZE; + if( !obj ) { + context->SetInternalException( TXT_NULL_POINTER_ACCESS ); + if( retPointer ) { + engine->CallFree( retPointer ); + } + return 0; + } + + // Add the base offset for multiple inheritance + obj = ( void * )( ( asQWORD )obj + sysFunc->baseOffset ); + } + + if ( obj && ( callConv == ICC_VIRTUAL_THISCALL || callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ) ) { + vftable = *( ( funcptr_t ** )obj ); + func = ( void * )vftable[( asQWORD )func >> 3]; + } + + switch ( callConv ) { + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL_RETURNINMEM: { + if ( totalArgumentCount ) { + memmove( argsType + 1, argsType, totalArgumentCount ); + } + memcpy( paramBuffer, &retPointer, sizeof( retPointer ) ); + argsType[0] = x64INTARG; + base_n = 1; + + param_pre = 1; + + break; + } + case ICC_THISCALL: + case ICC_VIRTUAL_THISCALL: + case ICC_CDECL_OBJFIRST: { + if ( totalArgumentCount ) { + memmove( argsType + 1, argsType, totalArgumentCount ); + } + memcpy( paramBuffer, &obj, sizeof( obj ) ); + argsType[0] = x64INTARG; + + param_pre = 1; + + break; + } + case ICC_THISCALL_RETURNINMEM: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + case ICC_CDECL_OBJFIRST_RETURNINMEM: { + if ( totalArgumentCount ) { + memmove( argsType + 2, argsType, totalArgumentCount ); + } + memcpy( paramBuffer, &retPointer, sizeof( retPointer ) ); + memcpy( paramBuffer + CALLSTACK_MULTIPLIER, &obj, sizeof( &obj ) ); + argsType[0] = x64INTARG; + argsType[1] = x64INTARG; + + param_pre = 2; + + break; + } + case ICC_CDECL_OBJLAST: { + memcpy( paramBuffer + totalArgumentCount * CALLSTACK_MULTIPLIER, &obj, sizeof( obj ) ); + argsType[totalArgumentCount] = x64INTARG; + + param_post = 1; + + break; + } + case ICC_CDECL_OBJLAST_RETURNINMEM: { + if ( totalArgumentCount ) { + memmove( argsType + 1, argsType, totalArgumentCount ); + } + memcpy( paramBuffer, &retPointer, sizeof( retPointer ) ); + argsType[0] = x64INTARG; + memcpy( paramBuffer + ( totalArgumentCount + 1 ) * CALLSTACK_MULTIPLIER, &obj, sizeof( obj ) ); + argsType[totalArgumentCount + 1] = x64INTARG; + + param_pre = 1; + param_post = 1; + + break; + } + default: { + base_n = 0; + break; + } + } + + int adjust = 0; + for( n = 0; n < ( int )( param_pre + totalArgumentCount + param_post ); n++ ) { + int copy_count = 0; + if ( n >= param_pre && n < ( int )( param_pre + totalArgumentCount ) ) { + copy_count = descr->parameterTypes[n - param_pre - adjust].GetSizeOnStackDWords(); + + if ( argsType[n] == x64VARIABLE ) { + adjust += 1; + argsType[n] = x64INTARG; + n += 1; + } + } + if ( copy_count > CALLSTACK_MULTIPLIER ) { + if ( copy_count > CALLSTACK_MULTIPLIER + 1 ) { + context->SetInternalException( TXT_INVALID_CALLING_CONVENTION ); + return 0; + } + + memcpy( paramBuffer + ( n - 1 ) * CALLSTACK_MULTIPLIER, stack_pointer, AS_PTR_SIZE * sizeof( asDWORD ) ); + stack_pointer += AS_PTR_SIZE; + memcpy( paramBuffer + n * CALLSTACK_MULTIPLIER, stack_pointer, sizeof( asDWORD ) ); + stack_pointer += 1; + } else { + if ( copy_count ) { + memcpy( paramBuffer + n * CALLSTACK_MULTIPLIER, stack_pointer, copy_count * sizeof( asDWORD ) ); + stack_pointer += copy_count; + } + } + } + + /* + * Q: WTF is going on here !? + * + * A: The idea is to pre-arange the parameters so that X64_CallFunction() can do + * it's little magic which must work regardless of how the compiler decides to + * allocate registers. Basically: + * - the first MAX_CALL_INT_REGISTERS entries in tempBuff and tempType will + * contain the values/types of the x64INTARG parameters - that is the ones who + * go into the registers. If the function has less then MAX_CALL_INT_REGISTERS + * integer parameters then the last entries will be set to 0 + * - the next MAX_CALL_SSE_REGISTERS entries will contain the float/double arguments + * that go into the floating point registers. If the function has less than + * MAX_CALL_SSE_REGISTERS floating point parameters then the last entries will + * be set to 0 + * - index MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS marks the start of the + * parameters which will get passed on the stack. These are added to the array + * in reverse order so that X64_CallFunction() can simply push them to the stack + * without the need to perform further tests + */ + int used_int_regs = 0; + int used_sse_regs = 0; + int idx = 0; + base_n = 0; + for ( n = 0; ( n < X64_CALLSTACK_SIZE ) && ( used_int_regs < MAX_CALL_INT_REGISTERS ); n++ ) { + if ( argsType[n] == x64INTARG ) { + idx = base_n; + argsSet[n] = 1; + tempType[idx] = argsType[n]; + memcpy( tempBuff + idx * CALLSTACK_MULTIPLIER, paramBuffer + n * CALLSTACK_MULTIPLIER, CALLSTACK_MULTIPLIER * sizeof( asDWORD ) ); + base_n++; + used_int_regs++; + } + } + base_n = 0; + for ( n = 0; ( n < X64_CALLSTACK_SIZE ) && ( used_sse_regs < MAX_CALL_SSE_REGISTERS ); n++ ) { + if ( argsType[n] == x64FLOATARG || argsType[n] == x64DOUBLEARG ) { + idx = MAX_CALL_INT_REGISTERS + base_n; + argsSet[n] = 1; + tempType[idx] = argsType[n]; + memcpy( tempBuff + idx * CALLSTACK_MULTIPLIER, paramBuffer + n * CALLSTACK_MULTIPLIER, CALLSTACK_MULTIPLIER * sizeof( asDWORD ) ); + base_n++; + used_sse_regs++; + } + } + base_n = 0; + for ( n = X64_CALLSTACK_SIZE - 1; n >= 0; n-- ) { + if ( argsType[n] != x64ENDARG && !argsSet[n] ) { + idx = MAX_CALL_INT_REGISTERS + MAX_CALL_SSE_REGISTERS + base_n; + argsSet[n] = 1; + tempType[idx] = argsType[n]; + memcpy( tempBuff + idx * CALLSTACK_MULTIPLIER, paramBuffer + n * CALLSTACK_MULTIPLIER, CALLSTACK_MULTIPLIER * sizeof( asDWORD ) ); + base_n++; + } + } + + context->isCallingSystemFunction = true; + retQW = X64_CallFunction( tempBuff, tempType, ( asDWORD * )func ); + ASM_GET_REG( "%rdx", retQW2 ); + context->isCallingSystemFunction = false; + +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) { + // Need to free the complex objects passed by value + stack_pointer = context->regs.stackPointer; + if ( !objectPointer && callConv >= ICC_THISCALL ) { + stack_pointer += AS_PTR_SIZE; + } + for( n = 0; n < ( int )descr->parameterTypes.GetLength(); n++ ) { + if ( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsReference() && ( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) ) { + obj = ( void * )( *( asQWORD * )stack_pointer ); + asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh; + if( beh->destruct ) { + engine->CallObjectMethod(obj, beh->destruct); + } + + engine->CallFree(obj); + } + + stack_pointer += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } +#endif + + // Store the returned value in our stack + if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) { + if( descr->returnType.IsObjectHandle() ) { + context->regs.objectRegister = ( void * )( size_t )retQW; + + if( sysFunc->returnAutoHandle && context->regs.objectRegister ) { + engine->CallObjectMethod( context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref ); + } + } else { + if ( !sysFunc->hostReturnInMemory ) { + if ( sysFunc->hostReturnSize == 1 ) { + *( asDWORD * )retPointer = ( asDWORD )retQW; + } else if ( sysFunc->hostReturnSize == 2 ) { + *( asQWORD * )retPointer = retQW; + } else if ( sysFunc->hostReturnSize == 3 ) { + *( asQWORD * )retPointer = retQW; + *( ( ( asDWORD * )retPointer ) + 2 ) = ( asDWORD )retQW2; + } else { + *( asQWORD * )retPointer = retQW; + *( ( ( asQWORD * )retPointer ) + 1 ) = retQW2; + } + } + + // Store the object in the register + context->regs.objectRegister = retPointer; + } + } else { + // Store value in valueRegister + if( sysFunc->hostReturnFloat ) { + if( sysFunc->hostReturnSize == 1 ) { + *(asDWORD*)&context->regs.valueRegister = GetReturnedFloat(); + } else { + context->regs.valueRegister = GetReturnedDouble(); + } + } else if ( sysFunc->hostReturnSize == 1 ) { + *( asDWORD * )&context->regs.valueRegister = ( asDWORD )retQW; + } else { + context->regs.valueRegister = retQW; + } + } + + if( sysFunc->hasAutoHandles ) { + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) { + args += AS_PTR_SIZE; + } + + int spos = 0; + for( n = 0; n < ( int )descr->parameterTypes.GetLength(); n++ ) { + if( sysFunc->paramAutoHandles[n] && (*(size_t*)&args[spos] != 0) ) { + // Call the release method on the type + engine->CallObjectMethod( ( void * )*( size_t * )&args[spos], descr->parameterTypes[n].GetObjectType()->beh.release ); + args[spos] = 0; + } + + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) { + spos += AS_PTR_SIZE; + } else { + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + } + + return popSize; +} + +END_AS_NAMESPACE + +#endif // AS_X64_GCC +#endif // AS_MAX_PORTABILITY diff --git a/AngelScript/source/as_callfunc_x86.cpp b/AngelScript/source/as_callfunc_x86.cpp new file mode 100644 index 000000000..5dd283c09 --- /dev/null +++ b/AngelScript/source/as_callfunc_x86.cpp @@ -0,0 +1,1255 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_x86.cpp +// +// These functions handle the actual calling of system functions +// + + + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_X86 + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" + +BEGIN_AS_NAMESPACE + +typedef asQWORD (*t_CallCDeclQW)(const asDWORD *, int, size_t); +typedef asQWORD (*t_CallCDeclQWObj)(void *obj, const asDWORD *, int, size_t); +typedef asDWORD (*t_CallCDeclRetByRef)(const asDWORD *, int, size_t, void *); +typedef asDWORD (*t_CallCDeclObjRetByRef)(void *obj, const asDWORD *, int, size_t, void *); +typedef asQWORD (*t_CallSTDCallQW)(const asDWORD *, int, size_t); +typedef asQWORD (*t_CallThisCallQW)(const void *, const asDWORD *, int, size_t); +typedef asDWORD (*t_CallThisCallRetByRef)(const void *, const asDWORD *, int, size_t, void *); + +// Prototypes +void CallCDeclFunction(const asDWORD *args, int paramSize, size_t func); +void CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, size_t func); +void CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, size_t func); +void CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, size_t func, void *retPtr); +void CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr); +void CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr); +void CallSTDCallFunction(const asDWORD *args, int paramSize, size_t func); +void CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, size_t func); +void CallThisCallFunctionRetByRef_impl(const void *, const asDWORD *, int, size_t, void *retPtr); + +// Initialize function pointers +const t_CallCDeclQW CallCDeclFunctionQWord = (t_CallCDeclQW)CallCDeclFunction; +const t_CallCDeclQWObj CallCDeclFunctionQWordObjLast = (t_CallCDeclQWObj)CallCDeclFunctionObjLast; +const t_CallCDeclQWObj CallCDeclFunctionQWordObjFirst = (t_CallCDeclQWObj)CallCDeclFunctionObjFirst; +const t_CallCDeclRetByRef CallCDeclFunctionRetByRef = (t_CallCDeclRetByRef)CallCDeclFunctionRetByRef_impl; +const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjLast = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjLast_impl; +const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjFirst = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjFirst_impl; +const t_CallSTDCallQW CallSTDCallFunctionQWord = (t_CallSTDCallQW)CallSTDCallFunction; +const t_CallThisCallQW CallThisCallFunctionQWord = (t_CallThisCallQW)CallThisCallFunction; +const t_CallThisCallRetByRef CallThisCallFunctionRetByRef = (t_CallThisCallRetByRef)CallThisCallFunctionRetByRef_impl; + +asDWORD GetReturnedFloat(); +asQWORD GetReturnedDouble(); + +int CallSystemFunction(int id, asCContext *context, void *objectPointer) +{ + asCScriptEngine *engine = context->engine; + asCScriptFunction *descr = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf; + int callConv = sysFunc->callConv; + if( callConv == ICC_GENERIC_FUNC || callConv == ICC_GENERIC_METHOD ) + return context->CallGeneric(id, objectPointer); + + asQWORD retQW = 0; + void *func = (void*)sysFunc->func; + int paramSize = sysFunc->paramSize; + asDWORD *args = context->regs.stackPointer; + void *retPointer = 0; + void *obj = 0; + asDWORD *vftable; + int popSize = paramSize; + + context->regs.objectType = descr->returnType.GetObjectType(); + if( descr->returnType.IsObject() && !descr->returnType.IsReference() && !descr->returnType.IsObjectHandle() ) + { + // Allocate the memory for the object + retPointer = engine->CallAlloc(descr->returnType.GetObjectType()); + + if( sysFunc->hostReturnInMemory ) + { + // The return is made in memory + callConv++; + } + } + + if( callConv >= ICC_THISCALL ) + { + if( objectPointer ) + { + obj = objectPointer; + } + else + { + // The object pointer should be popped from the context stack + popSize++; + + // Check for null pointer + obj = (void*)*(size_t*)(args); + if( obj == 0 ) + { + context->SetInternalException(TXT_NULL_POINTER_ACCESS); + if( retPointer ) + engine->CallFree(retPointer); + return 0; + } + + // Add the base offset for multiple inheritance + obj = (void*)(size_t(obj) + sysFunc->baseOffset); + + // Skip the object pointer + args++; + } + } + + asDWORD paramBuffer[64]; + if( sysFunc->takesObjByVal ) + { + paramSize = 0; + int spos = 0; + int dpos = 1; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); + + // Delete the original memory + engine->CallFree(*(char**)(args+spos)); + spos++; + dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[dpos++] = args[spos++]; + if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 ) + paramBuffer[dpos++] = args[spos++]; + paramSize += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + // Keep a free location at the beginning + args = ¶mBuffer[1]; + } + + context->isCallingSystemFunction = true; + switch( callConv ) + { + case ICC_CDECL: + retQW = CallCDeclFunctionQWord(args, paramSize<<2, (size_t)func); + break; + + case ICC_CDECL_RETURNINMEM: + retQW = CallCDeclFunctionRetByRef(args, paramSize<<2, (size_t)func, retPointer); + break; + + case ICC_STDCALL: + retQW = CallSTDCallFunctionQWord(args, paramSize<<2, (size_t)func); + break; + + case ICC_STDCALL_RETURNINMEM: + // Push the return pointer on the stack + paramSize++; + args--; + *(size_t*)args = (size_t)retPointer; + + retQW = CallSTDCallFunctionQWord(args, paramSize<<2, (size_t)func); + break; + + case ICC_THISCALL: + retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, (size_t)func); + break; + + case ICC_THISCALL_RETURNINMEM: + retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, (size_t)func, retPointer); + break; + + case ICC_VIRTUAL_THISCALL: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + + retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, vftable[size_t(func)>>2]); + break; + + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + vftable = *(asDWORD**)obj; + + retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, vftable[size_t(func)>>2], retPointer); + break; + + case ICC_CDECL_OBJLAST: + retQW = CallCDeclFunctionQWordObjLast(obj, args, paramSize<<2, (size_t)func); + break; + + case ICC_CDECL_OBJLAST_RETURNINMEM: + // Call the system object method as a cdecl with the obj ref as the last parameter + retQW = CallCDeclFunctionRetByRefObjLast(obj, args, paramSize<<2, (size_t)func, retPointer); + break; + + case ICC_CDECL_OBJFIRST: + // Call the system object method as a cdecl with the obj ref as the first parameter + retQW = CallCDeclFunctionQWordObjFirst(obj, args, paramSize<<2, (size_t)func); + break; + + case ICC_CDECL_OBJFIRST_RETURNINMEM: + // Call the system object method as a cdecl with the obj ref as the first parameter + retQW = CallCDeclFunctionRetByRefObjFirst(obj, args, paramSize<<2, (size_t)func, retPointer); + break; + + default: + context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); + } + context->isCallingSystemFunction = false; + +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( sysFunc->takesObjByVal ) + { + // Need to free the complex objects passed by value + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsObject() && + !descr->parameterTypes[n].IsReference() && + (descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK) ) + { + void *obj = (void*)args[spos++]; + asSTypeBehaviour *beh = &descr->parameterTypes[n].GetObjectType()->beh; + if( beh->destruct ) + engine->CallObjectMethod(obj, beh->destruct); + + engine->CallFree(obj); + } + else + spos += descr->parameterTypes[n].GetSizeInMemoryDWords(); + } + } +#endif + + // Store the returned value in our stack + if( descr->returnType.IsObject() && !descr->returnType.IsReference() ) + { + if( descr->returnType.IsObjectHandle() ) + { + context->regs.objectRegister = (void*)(size_t)retQW; + + if( sysFunc->returnAutoHandle && context->regs.objectRegister ) + engine->CallObjectMethod(context->regs.objectRegister, descr->returnType.GetObjectType()->beh.addref); + } + else + { + if( !sysFunc->hostReturnInMemory ) + { + // Copy the returned value to the pointer sent by the script engine + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)retPointer = (asDWORD)retQW; + else + *(asQWORD*)retPointer = retQW; + } + + // Store the object in the register + context->regs.objectRegister = retPointer; + } + } + else + { + // Store value in value register + if( sysFunc->hostReturnFloat ) + { + if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = GetReturnedFloat(); + else + context->regs.valueRegister = GetReturnedDouble(); + } + else if( sysFunc->hostReturnSize == 1 ) + *(asDWORD*)&context->regs.valueRegister = (asDWORD)retQW; + else + context->regs.valueRegister = retQW; + } + + if( sysFunc->hasAutoHandles ) + { + args = context->regs.stackPointer; + if( callConv >= ICC_THISCALL && !objectPointer ) + args++; + + int spos = 0; + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( sysFunc->paramAutoHandles[n] && args[spos] ) + { + // Call the release method on the type + engine->CallObjectMethod((void*)*(size_t*)&args[spos], descr->parameterTypes[n].GetObjectType()->beh.release); + args[spos] = 0; + } + + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() ) + spos++; + else + spos += descr->parameterTypes[n].GetSizeOnStackDWords(); + } + } + + return popSize; +} + +// On GCC we need to prevent the compiler from inlining these assembler routines when +// optimizing for speed (-O3), as the loop labels get duplicated which cause compile errors. + +#ifdef __GNUC__ + #define NOINLINE __attribute ((__noinline__)) +#else + #define NOINLINE +#endif + + +void NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, size_t func) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 12(%ebp), %eax \n" // paramSize + "addl $4, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "movl 12(%ebp), %ecx \n" // paramSize + "movl 8(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push arguments on the stack + "cmp $0, %ecx \n" + "je endcopy \n" + "copyloop: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop \n" + "endcopy: \n" + "call *16(%ebp) \n" + "addl 12(%ebp), %esp \n" // pop arguments + + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +void NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, size_t func) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Push the object pointer as the last argument to the function + push obj + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + add esp, 4 + + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(obj); + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 16(%ebp), %eax \n" // paramSize + "addl $8, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "pushl 8(%ebp) \n" + "movl 16(%ebp), %ecx \n" // paramSize + "movl 12(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push arguments on the stack + "cmp $0, %ecx \n" + "je endcopy8 \n" + "copyloop8: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop8 \n" + "endcopy8: \n" + "call *20(%ebp) \n" + "addl 16(%ebp), %esp \n" // pop arguments + "addl $4, %esp \n" + + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +void NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, size_t func) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // push object as first parameter + push obj + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + add esp, 4 + + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(obj); + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 16(%ebp), %eax \n" // paramSize + "addl $8, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "movl 16(%ebp), %ecx \n" // paramSize + "movl 12(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push arguments on the stack + "cmp $0, %ecx \n" + "je endcopy6 \n" + "copyloop6: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop6 \n" + "endcopy6: \n" + "pushl 8(%ebp) \n" // push obj + "call *20(%ebp) \n" + "addl 16(%ebp), %esp \n" // pop arguments + "addl $4, %esp \n" + + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +void NOINLINE CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Push the object pointer + push obj + + // Push the return pointer + push retPtr; + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + // Pop the return pointer + add esp, 8 +#else + add esp, 4 +#endif + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(obj); + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + UNUSED_VAR(retPtr); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 16(%ebp), %eax \n" // paramSize + "addl $12, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "movl 16(%ebp), %ecx \n" // paramSize + "movl 12(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push arguments on the stack + "cmp $0, %ecx \n" + "je endcopy5 \n" + "copyloop5: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop5 \n" + "endcopy5: \n" + "pushl 8(%ebp) \n" // push object first + "pushl 24(%ebp) \n" // retPtr + "call *20(%ebp) \n" // func + "addl 16(%ebp), %esp \n" // pop arguments +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + "addl $8, %esp \n" // Pop the return pointer and object pointer +#else + "addl $4, %esp \n" // Pop the object pointer +#endif + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +void NOINLINE CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, size_t func, void *retPtr) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Push the return pointer + push retPtr; + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + // Pop the return pointer + add esp, 4 +#endif + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + UNUSED_VAR(retPtr); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 12(%ebp), %eax \n" // paramSize + "addl $8, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "movl 12(%ebp), %ecx \n" // paramSize + "movl 8(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push arguments on the stack + "cmp $0, %ecx \n" + "je endcopy7 \n" + "copyloop7: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop7 \n" + "endcopy7: \n" + "pushl 20(%ebp) \n" // retPtr + "call *16(%ebp) \n" // func + "addl 12(%ebp), %esp \n" // pop arguments +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + "addl $4, %esp \n" // Pop the return pointer +#endif + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +void NOINLINE CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + push obj + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Push the return pointer + push retPtr; + + // Call function + call [func] + + // Pop arguments from stack + add esp, paramSize + add esp, 4 + +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + // Pop the return pointer + add esp, 4 +#endif + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(obj); + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + UNUSED_VAR(retPtr); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 16(%ebp), %eax \n" // paramSize + "addl $12, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "pushl 8(%ebp) \n" + "movl 16(%ebp), %ecx \n" // paramSize + "movl 12(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push arguments on the stack + "cmp $0, %ecx \n" + "je endcopy4 \n" + "copyloop4: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop4 \n" + "endcopy4: \n" + "pushl 24(%ebp) \n" // retPtr + "call *20(%ebp) \n" // func + "addl 16(%ebp), %esp \n" // pop arguments +#ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER + "addl $8, %esp \n" // Pop the return pointer +#else + "addl $4, %esp \n" // Pop the return pointer +#endif + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +void NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, size_t func) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + + // Call function + call [func] + + // The callee already removed parameters from the stack + + // Restore registers + pop ecx + + // return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 12(%ebp), %eax \n" // paramSize + "addl $4, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "movl 12(%ebp), %ecx \n" // paramSize + "movl 8(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push arguments on the stack + "cmp $0, %ecx \n" + "je endcopy2 \n" + "copyloop2: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop2 \n" + "endcopy2: \n" + "call *16(%ebp) \n" // callee pops the arguments + + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + + +void NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, size_t func) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Push the object pointer on the stack + push obj +#else + // Move object pointer to ECX + mov ecx, obj +#endif + + // Call function + call [func] + +#ifndef THISCALL_CALLEE_POPS_ARGUMENTS + // Pop arguments + add esp, paramSize +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Pop object pointer + add esp, 4 +#endif +#endif + + // Restore registers + pop ecx + + // Return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(obj); + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 16(%ebp), %eax \n" // paramSize + "addl $8, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "movl 16(%ebp), %ecx \n" // paramSize + "movl 12(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push all arguments on the stack + "cmp $0, %ecx \n" + "je endcopy1 \n" + "copyloop1: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop1 \n" + "endcopy1: \n" + "movl 8(%ebp), %ecx \n" // move obj into ECX + "pushl 8(%ebp) \n" // push obj on the stack + "call *20(%ebp) \n" + "addl 16(%ebp), %esp \n" // pop arguments + "addl $4, %esp \n" // pop obj + + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +void NOINLINE CallThisCallFunctionRetByRef_impl(const void *obj, const asDWORD *args, int paramSize, size_t func, void *retPtr) +{ +#if defined ASM_INTEL + + // Copy the data to the real stack. If we fail to do + // this we may run into trouble in case of exceptions. + __asm + { + // We must save registers that are used + push ecx + + // Clear the FPU stack, in case the called function doesn't do it by itself + fninit + + // Copy arguments from script + // stack to application stack + mov ecx, paramSize + mov eax, args + add eax, ecx + cmp ecx, 0 + je endcopy +copyloop: + sub eax, 4 + push dword ptr [eax] + sub ecx, 4 + jne copyloop +endcopy: + +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Push the object pointer on the stack + push obj +#else + // Move object pointer to ECX + mov ecx, obj +#endif + + // Push the return pointer + push retPtr + + // Call function + call [func] + +#ifndef THISCALL_CALLEE_POPS_ARGUMENTS + // Pop arguments + add esp, paramSize +#ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK + // Pop object pointer + add esp, 4 +#endif +#endif + + // Restore registers + pop ecx + + // Return value in EAX or EAX:EDX + } + +#elif defined ASM_AT_N_T + + UNUSED_VAR(obj); + UNUSED_VAR(args); + UNUSED_VAR(paramSize); + UNUSED_VAR(func); + UNUSED_VAR(retPtr); + + asm("pushl %ecx \n" + "fninit \n" + + // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call. + // It is assumed that when entering this function, the stack pointer is already aligned, so we need + // to calculate how much we will put on the stack during this call. + "movl 16(%ebp), %eax \n" // paramSize + "addl $12, %eax \n" // counting esp that we will push on the stack + "movl %esp, %ecx \n" + "subl %eax, %ecx \n" + "andl $15, %ecx \n" + "movl %esp, %eax \n" + "subl %ecx, %esp \n" + "pushl %eax \n" // Store the original stack pointer + + "movl 16(%ebp), %ecx \n" // paramSize + "movl 12(%ebp), %eax \n" // args + "addl %ecx, %eax \n" // push all arguments to the stack + "cmp $0, %ecx \n" + "je endcopy3 \n" + "copyloop3: \n" + "subl $4, %eax \n" + "pushl (%eax) \n" + "subl $4, %ecx \n" + "jne copyloop3 \n" + "endcopy3: \n" + "movl 8(%ebp), %ecx \n" // move obj into ECX + "pushl 8(%ebp) \n" // push obj on the stack + "pushl 24(%ebp) \n" // push retPtr on the stack + "call *20(%ebp) \n" + "addl 16(%ebp), %esp \n" // pop arguments + "addl $4, %esp \n" // pop the object pointer + // the return pointer was popped by the callee + // Pop the alignment bytes + "popl %esp \n" + + "popl %ecx \n"); + +#endif +} + +asDWORD GetReturnedFloat() +{ + asDWORD f; + +#if defined ASM_INTEL + + // Get the float value from ST0 + __asm fstp dword ptr [f] + +#elif defined ASM_AT_N_T + + asm("fstps %0 \n" : "=m" (f)); + +#endif + + return f; +} + +asQWORD GetReturnedDouble() +{ + asQWORD d; + +#if defined ASM_INTEL + + // Get the double value from ST0 + __asm fstp qword ptr [d] + +#elif defined ASM_AT_N_T + + asm("fstpl %0 \n" : "=m" (d)); + +#endif + + return d; +} + +END_AS_NAMESPACE + +#endif // AS_X86 +#endif // AS_MAX_PORTABILITY + + + + diff --git a/AngelScript/source/as_callfunc_xenon.cpp b/AngelScript/source/as_callfunc_xenon.cpp new file mode 100644 index 000000000..51049efd3 --- /dev/null +++ b/AngelScript/source/as_callfunc_xenon.cpp @@ -0,0 +1,826 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_callfunc_xenon.cpp +// +// These functions handle the actual calling of system functions +// +// This version is Xenon specific +// Modified from as_callfunc_ppc.cpp by Laszlo Perneky Februar 2007 + +#include "as_config.h" + +#ifndef AS_MAX_PORTABILITY +#ifdef AS_XENON + +#include "as_callfunc.h" +#include "as_scriptengine.h" +#include "as_texts.h" +#include "as_tokendef.h" + +#include +#include + +BEGIN_AS_NAMESPACE + +#define AS_PPC_MAX_ARGS 32 +#define AS_MAX_REG_FLOATS 13 +#define AS_MAX_REG_INTS 8 +#define AS_PPC_THISCALL_REG 1 +#define AS_PPC_RETURNINMEM_REG 1 +#define AS_PPC_ENDOFARGS 1 + +// The array used to send values to the correct places. +// Contains a byte of argTypes to indicate the register type to load, or zero if end of arguments + +extern "C" { + enum argTypes + { + ppcENDARG = 0, + ppcINTARG, + ppcFLOATARG, + ppcDOUBLEARG + }; + static asBYTE ppcArgsType[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG + AS_PPC_ENDOFARGS]; + static asDWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG]; +} + +// Loads all data into the correct places and calls the function. +// ppcArgsType is an array containing a byte type (enum argTypes) for each argument. +// iStackArgSize is the size in bytes for how much data to put on the stack frame +//-------------------------------------------------------------------- +asQWORD __declspec( naked ) ppcFunc(const asDWORD* pArgs, int iStackArgSize, asDWORD dwFunc) +{ + __asm + { +////////////////////////////////////////////////////////////////////////// +// Prepare args +////////////////////////////////////////////////////////////////////////// +_ppcFunc: + // setup stack + // Read link register + mflr r12 + // Stack the link register + stw r12, -8(r1) + // Move stack pointer + stwu r1, -70h(r1) + + mr r29, r3 //pArgs + mr r30, r4 //iStackArgSize + mr r27, r5 //dwFunc + + // Clear some registers + sub r0, r0, r0 + // Counting of used/assigned GPR's + mr r23, r0 + // Counting of used/assigned Float Registers + mr r22, r0 + + // Fetch argument types array address + lau r25, ppcArgsType + lal r25, r25, ppcArgsType + + // Fetch arguments array address + lau r26, ppcArgs + lal r26, r26, ppcArgs + + // Begin loading and stacking registers + subi r25, r25, 1 + +////////////////////////////////////////////////////////////////////////// +// Fetch the next argument +////////////////////////////////////////////////////////////////////////// +ppcNextArg: + // Increment rArgTypePtr + addi r25, r25, 1 + // Get data type + lbz r24, 0(r25) + + // r24 holds the data type + cmplwi cr6, r24, 0 + beq cr6, ppcArgsEnd + cmplwi cr6, r24, 1 + beq cr6, ppcArgIsInteger + cmplwi cr6, r24, 2 + beq cr6, ppcArgIsFloat + cmplwi cr6, r24, 3 + beq cr6, ppcArgIsDouble + +////////////////////////////////////////////////////////////////////////// +// Load and stack integer arguments +////////////////////////////////////////////////////////////////////////// +ppcArgIsInteger: + // Get the arg from the stack + lwz r11, 0(r26) + + // r23 holds the integer arg count so far + cmplwi cr6, r23, 0 + beq cr6, ppcLoadIntReg0 + cmplwi cr6, r23, 1 + beq cr6, ppcLoadIntReg1 + cmplwi cr6, r23, 2 + beq cr6, ppcLoadIntReg2 + cmplwi cr6, r23, 3 + beq cr6, ppcLoadIntReg3 + cmplwi cr6, r23, 4 + beq cr6, ppcLoadIntReg4 + cmplwi cr6, r23, 5 + beq cr6, ppcLoadIntReg5 + cmplwi cr6, r23, 6 + beq cr6, ppcLoadIntReg6 + cmplwi cr6, r23, 7 + beq cr6, ppcLoadIntReg7 + + // no more than 8 parameters + b ppcLoadIntRegUpd + + ppcLoadIntReg0: + mr r3, r11 + b ppcLoadIntRegUpd + ppcLoadIntReg1: + mr r4, r11 + b ppcLoadIntRegUpd + ppcLoadIntReg2: + mr r5, r11 + b ppcLoadIntRegUpd + ppcLoadIntReg3: + mr r6, r11 + b ppcLoadIntRegUpd + ppcLoadIntReg4: + mr r7, r11 + b ppcLoadIntRegUpd + ppcLoadIntReg5: + mr r8, r11 + b ppcLoadIntRegUpd + ppcLoadIntReg6: + mr r9, r11 + b ppcLoadIntRegUpd + ppcLoadIntReg7: + mr r10, r11 + b ppcLoadIntRegUpd + + ppcLoadIntRegUpd: + // Increment used int register count + addi r23, r23, 1 + // Increment rArgsPtr + addi r29, r29, 4 + // Increment rStackPtr + addi r26, r26, 4 + b ppcNextArg + +////////////////////////////////////////////////////////////////////////// +// Load and stack float arguments +////////////////////////////////////////////////////////////////////////// +ppcArgIsFloat: + // Get the arg from the stack + lfs fr15, 0(r26) + + // r22 holds the float arg count so far + cmplwi cr6, r23, 0 + beq cr6, ppcLoadFloatReg0 + cmplwi cr6, r23, 1 + beq cr6, ppcLoadFloatReg1 + cmplwi cr6, r23, 2 + beq cr6, ppcLoadFloatReg2 + cmplwi cr6, r23, 3 + beq cr6, ppcLoadFloatReg3 + cmplwi cr6, r23, 4 + beq cr6, ppcLoadFloatReg4 + cmplwi cr6, r23, 5 + beq cr6, ppcLoadFloatReg5 + cmplwi cr6, r23, 6 + beq cr6, ppcLoadFloatReg6 + cmplwi cr6, r23, 7 + beq cr6, ppcLoadFloatReg7 + cmplwi cr6, r23, 8 + beq cr6, ppcLoadFloatReg8 + cmplwi cr6, r23, 9 + beq cr6, ppcLoadFloatReg9 + cmplwi cr6, r23, 10 + beq cr6, ppcLoadFloatReg10 + cmplwi cr6, r23, 11 + beq cr6, ppcLoadFloatReg11 + cmplwi cr6, r23, 12 + beq cr6, ppcLoadFloatReg12 + cmplwi cr6, r23, 13 + beq cr6, ppcLoadFloatReg13 + cmplwi cr6, r23, 14 + beq cr6, ppcLoadFloatReg14 + + // no more than 14 parameters + b ppcLoadFloatRegUpd + + ppcLoadFloatReg0: + fmr fr0, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg1: + fmr fr1, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg2: + fmr fr2, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg3: + fmr fr3, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg4: + fmr fr4, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg5: + fmr fr5, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg6: + fmr fr6, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg7: + fmr fr7, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg8: + fmr fr8, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg9: + fmr fr9, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg10: + fmr fr10, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg11: + fmr fr11, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg12: + fmr fr12, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg13: + fmr fr13, fr15 + b ppcLoadFloatRegUpd + ppcLoadFloatReg14: + fmr fr14, fr15 + b ppcLoadFloatRegUpd + + ppcLoadFloatRegUpd: + // Increment used float register count + addi r22, r22, 1 + // Increment used int register count - a float reg eats up a GPR + addi r23, r23, 1 + // Increment rArgsPtr + addi r29, r29, 4 + // Increment rStackPtr + addi r26, r26, 4 + b ppcNextArg + +////////////////////////////////////////////////////////////////////////// +// Load and stack double float arguments +////////////////////////////////////////////////////////////////////////// +ppcArgIsDouble: + // Get the arg from the stack + lfs fr15, 0(r26) + + // r22 holds the float arg count so far + cmplwi cr6, r23, 0 + beq cr6, ppcLoadDoubleReg0 + cmplwi cr6, r23, 1 + beq cr6, ppcLoadDoubleReg1 + cmplwi cr6, r23, 2 + beq cr6, ppcLoadDoubleReg2 + cmplwi cr6, r23, 3 + beq cr6, ppcLoadDoubleReg3 + cmplwi cr6, r23, 4 + beq cr6, ppcLoadDoubleReg4 + cmplwi cr6, r23, 5 + beq cr6, ppcLoadDoubleReg5 + cmplwi cr6, r23, 6 + beq cr6, ppcLoadDoubleReg6 + cmplwi cr6, r23, 7 + beq cr6, ppcLoadDoubleReg7 + cmplwi cr6, r23, 8 + beq cr6, ppcLoadDoubleReg8 + cmplwi cr6, r23, 9 + beq cr6, ppcLoadDoubleReg9 + cmplwi cr6, r23, 10 + beq cr6, ppcLoadDoubleReg10 + cmplwi cr6, r23, 11 + beq cr6, ppcLoadDoubleReg11 + cmplwi cr6, r23, 12 + beq cr6, ppcLoadDoubleReg12 + cmplwi cr6, r23, 13 + beq cr6, ppcLoadDoubleReg13 + cmplwi cr6, r23, 14 + beq cr6, ppcLoadDoubleReg14 + + ppcLoadDoubleReg0: + fmr fr0, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg1: + fmr fr1, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg2: + fmr fr2, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg3: + fmr fr3, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg4: + fmr fr4, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg5: + fmr fr5, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg6: + fmr fr6, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg7: + fmr fr7, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg8: + fmr fr8, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg9: + fmr fr9, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg10: + fmr fr10, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg11: + fmr fr11, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg12: + fmr fr12, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg13: + fmr fr13, fr15 + b ppcLoadDoubleRegUpd + ppcLoadDoubleReg14: + fmr fr14, fr15 + b ppcLoadDoubleRegUpd + + ppcLoadDoubleRegUpd: + // Increment used float register count + addi r22, r22, 1 + // Increment used int register count + addi r23, r23, 1 + // Increment rArgsPtr + addi r29, r29, 4 + // Increment rStackPtr + addi r26, r26, 4 + b ppcNextArg + +////////////////////////////////////////////////////////////////////////// +// Finished +////////////////////////////////////////////////////////////////////////// +ppcArgsEnd: + // Call the function + mtctr r27 + bctrl + + // Function returned + + // Restore callers stack + addi r1, r1, 70h + // Fetch return link to caller + lwz r12, -8(r1) + mtlr r12 + + blr + } +} + +// Puts the arguments in the correct place in the stack array. +//------------------------------------------------------------------- +void stackArgs(const asDWORD *pArgs, int& iNumIntArgs, int& iNumFloatArgs, int& iNumDoubleArgs) +{ + int iArgWordPos = iNumIntArgs + iNumFloatArgs + iNumDoubleArgs; + + for(int iArg = 0; iArg < AS_PPC_MAX_ARGS; iArg++) + { + if ( ppcArgsType[iArg] == ppcENDARG ) + break; + + if( ppcArgsType[iArg] == ppcFLOATARG ) + { + // stow float + ((float*)ppcArgs)[iArgWordPos] = ((float*)(pArgs))[iArg]; + iNumFloatArgs++; + iArgWordPos++; //add one word + } + if ( ppcArgsType[iArg] == ppcDOUBLEARG ) + { + // stow double + ((double*)ppcArgs)[iArgWordPos] = ((double*)(pArgs))[iArg]; + iNumDoubleArgs++; + iArgWordPos++; //add two words + } + + if( ppcArgsType[iArg] == ppcINTARG ) + { + // stow register + ((int*)ppcArgs)[iArgWordPos] = ((int*)(pArgs))[iArg]; + iNumIntArgs++; + iArgWordPos++; + } + } +} + +// Prepare the arg list for a CDecl funtion and then call it +//-------------------------------------------------------------------- +asQWORD CallCDeclFunction(const asDWORD* pArgs, int iArgSize, asDWORD dwFunc) +{ + int iIntArgs = 0; + int iFloatArgs = 0; + int iDoubleArgs = 0; + + // Put the arguments in the correct places in the ppcArgs array + if ( iArgSize > 0 ) + stackArgs( pArgs, iIntArgs, iFloatArgs, iDoubleArgs ); + + return ppcFunc( ppcArgs, iArgSize, dwFunc); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the first parameter is the object +//-------------------------------------------------------------------- +asQWORD CallThisCallFunction(const void* pObj, const asDWORD* pArgs, int iArgSize, asDWORD dwFunc ) +{ + int iIntArgs = 0; + int iFloatArgs = 0; + int iDoubleArgs = 0; + + // Put the arguments in the correct places in the ppcArgs array /the this ptr is already in pArgs/ + if ( iArgSize > 0 ) + stackArgs( pArgs, iIntArgs, iFloatArgs, iDoubleArgs ); + + return ppcFunc( ppcArgs, iArgSize, dwFunc); +} + +// This function is identical to CallCDeclFunction, with the only difference that +// the value in the last parameter is the object +//-------------------------------------------------------------------- +asQWORD CallThisCallFunction_objLast(const void* pObj, const asDWORD* pArgs, int iArgSize, asDWORD dwFunc) +{ + int iIntArgs = 0; + int iFloatArgs = 0; + int iDoubleArgs = 0; + + // Put the arguments in the correct places in the ppcArgs array /the this ptr is already in pArgs/ + if ( iArgSize > 0 ) + stackArgs( pArgs, iIntArgs, iFloatArgs, iDoubleArgs ); + + int iNumArgs = iIntArgs + iFloatArgs + iDoubleArgs; + if ( iNumArgs < AS_PPC_MAX_ARGS ) + { + ppcArgs[iNumArgs] = (asDWORD)pObj; + ppcArgsType[iNumArgs] = ppcINTARG; + } + + return ppcFunc( ppcArgs, iArgSize + sizeof(pObj), dwFunc ); +} + +//-------------------------------------------------------------------- +asDWORD GetReturnedFloat() +{ + asDWORD f; + + __asm + { + stfs fr0, f + } + + return f; +} + + +asQWORD GetReturnedDouble() +//-------------------------------------------------------------------- +{ + asQWORD f; + + __asm + { + stfd fr0, f + } + + return f; +} + +int CallSystemFunction(int iId, asCContext* pContext, void* pObjectPointer) +//-------------------------------------------------------------------- +{ + memset( ppcArgsType, 0, sizeof(ppcArgsType)); + + asCScriptEngine* pEngine = pContext->engine; + asCScriptFunction* pDescr = pEngine->scriptFunctions[iId]; + asSSystemFunctionInterface* pSysFunc = pDescr->sysFuncIntf; + + int iCallConv = pSysFunc->callConv; + if ( iCallConv == ICC_GENERIC_FUNC + || iCallConv == ICC_GENERIC_METHOD ) + return pContext->CallGeneric( iId, pObjectPointer ); + + asQWORD dwRetQW = 0; + + void* pFunc = (void*)pSysFunc->func; + int iParamSize = pSysFunc->paramSize; + asDWORD* pArgs = pContext->regs.stackPointer; + void* pRetPointer = 0; + void* pObj = 0; + int iPopSize = iParamSize; + asDWORD* pVftable; + + // We generate the parameter list to this, so it fits to teh callingconvention + asDWORD fixedArgs[ AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG ]; + memset(fixedArgs, 0, sizeof(fixedArgs)); + int iArgsPtr = 0; + + pContext->regs.objectType = pDescr->returnType.GetObjectType(); + + // If the function returns an object in memory, we allocate the memory and put the ptr to the front (will go to r3) + if ( pDescr->returnType.IsObject() && !pDescr->returnType.IsReference() && !pDescr->returnType.IsObjectHandle() ) + { + pRetPointer = pEngine->CallAlloc(pDescr->returnType.GetObjectType()); + + if( pSysFunc->hostReturnInMemory ) + iCallConv++; + + fixedArgs [ iArgsPtr ] = (asDWORD)pRetPointer; + ppcArgsType[ iArgsPtr ] = ppcINTARG; + iArgsPtr++; + } + + // Find out if we have an object + if ( iCallConv >= ICC_THISCALL ) + { + if ( pObjectPointer ) + { + pObj = pObjectPointer; + } + else + { + // The object pointer should be popped from the context stack + iPopSize++; + + pObj = (void*)*(pArgs); + pArgs++; + + // Check for null pointer + if ( pObj == 0 ) + { + pContext->SetInternalException(TXT_NULL_POINTER_ACCESS); + if( pRetPointer ) + pEngine->CallFree(pRetPointer); + return 0; + } + + // Add the base offset for multiple inheritance + pObj = (void*)(int(pObj) + pSysFunc->baseOffset); + } + } + + // If we have an object and it's not objectlast, then we put it az the first arg + if ( pObj + && iCallConv != ICC_CDECL_OBJLAST + && iCallConv != ICC_CDECL_OBJLAST_RETURNINMEM ) + { + fixedArgs [ iArgsPtr ] = (asDWORD)pObj; + ppcArgsType[ iArgsPtr ] = ppcINTARG; + iArgsPtr++; + } + + asASSERT(pDescr->parameterTypes.GetLength() <= AS_PPC_MAX_ARGS); + + // Parameter calculation magic + asDWORD paramBuffer[64]; + if ( pSysFunc->takesObjByVal ) + { + iParamSize = 0; + int iSpos = 0; + int iDpos = 1; + + for ( asUINT uParam = 0; uParam < pDescr->parameterTypes.GetLength(); uParam++ ) + { + // Parameter object by value + if ( pDescr->parameterTypes[uParam].IsObject() + && !pDescr->parameterTypes[uParam].IsObjectHandle() + && !pDescr->parameterTypes[uParam].IsReference() ) + { +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( pDescr->parameterTypes[uParam].GetObjectType()->flags & COMPLEX_MASK ) + { + paramBuffer[dpos++] = args[spos++]; + paramSize++; + } + else +#endif + { + // Copy the object's memory to the buffer + memcpy( ¶mBuffer[iDpos], *(void**)(pArgs + iSpos), pDescr->parameterTypes[uParam].GetSizeInMemoryBytes() ); + // Delete the original memory + pEngine->CallFree(*(char**)(pArgs + iSpos) ); + pArgs[uParam] = (asDWORD)¶mBuffer[iDpos]; + iSpos++; + iDpos += pDescr->parameterTypes[uParam].GetSizeInMemoryDWords(); + iParamSize += pDescr->parameterTypes[uParam].GetSizeInMemoryDWords(); + } + } + else + { + // Copy the value directly + paramBuffer[iDpos++] = pArgs[iSpos++]; + if( pDescr->parameterTypes[uParam].GetSizeOnStackDWords() > 1 ) + paramBuffer[iDpos++] = pArgs[iSpos++]; + iParamSize += pDescr->parameterTypes[uParam].GetSizeOnStackDWords(); + } + } + } + + // Copy the parameter types for the ppcFunc. Also copy the params for the fixed xenon args buffer /containing this and return ptr/ + for( int iParam = 0; iParam < (int)pDescr->parameterTypes.GetLength(); iParam++, iArgsPtr++ ) + { + ppcArgsType[iArgsPtr] = ppcINTARG; + if (pDescr->parameterTypes[iParam].IsFloatType()) + ppcArgsType[iArgsPtr] = ppcFLOATARG; + else if (pDescr->parameterTypes[iParam].IsDoubleType()) + ppcArgsType[iArgsPtr] = ppcDOUBLEARG; + + fixedArgs[iArgsPtr] = pArgs[iParam]; + + // If the arg is bool, then endian swap it /it would not neccessary if the AS_BIG_ENDIAN would swap the bool from 0x01000000 to 0x00000010/ + if ( pDescr->parameterTypes[iParam].GetTokenType() == ttBool ) + ((asBYTE*)(&fixedArgs[iArgsPtr]))[3] = ((asBYTE*)(&fixedArgs[iArgsPtr]))[0]; + } + + + pContext->isCallingSystemFunction = true; + switch ( iCallConv ) + { + case ICC_CDECL: + case ICC_CDECL_RETURNINMEM: + case ICC_STDCALL: + case ICC_STDCALL_RETURNINMEM: + dwRetQW = CallCDeclFunction( fixedArgs, iArgsPtr, (asDWORD)pFunc ); + break; + case ICC_THISCALL: + case ICC_THISCALL_RETURNINMEM: + dwRetQW = CallThisCallFunction( pObj, fixedArgs, iArgsPtr, (asDWORD)pFunc ); + break; + case ICC_VIRTUAL_THISCALL: + case ICC_VIRTUAL_THISCALL_RETURNINMEM: + // Get virtual function table from the object pointer + pVftable = *(asDWORD**)pObj; + dwRetQW = CallThisCallFunction( pObj, fixedArgs, iArgsPtr, pVftable[asDWORD(pFunc)>>2] ); + break; + case ICC_CDECL_OBJLAST: + case ICC_CDECL_OBJLAST_RETURNINMEM: + dwRetQW = CallThisCallFunction_objLast( pObj, fixedArgs, iArgsPtr, (asDWORD)pFunc ); + break; + case ICC_CDECL_OBJFIRST: + case ICC_CDECL_OBJFIRST_RETURNINMEM: + dwRetQW = CallThisCallFunction( pObj, fixedArgs, iArgsPtr, (asDWORD)pFunc ); + break; + default: + pContext->SetInternalException( TXT_INVALID_CALLING_CONVENTION ); + } + pContext->isCallingSystemFunction = false; + +#ifdef COMPLEX_OBJS_PASSED_BY_REF + if( pSysFunc->takesObjByVal ) + { + // Need to free the complex objects passed by value + pArgs = pContext->regs.stackPointer; + if ( iCallConv >= ICC_THISCALL + && !pObjectPointer ) + pArgs++; + + int iSpos = 0; + for( int iParam = 0; iParam < (int)descr->parameterTypes.GetLength(); iParam++ ) + { + if ( pDescr->parameterTypes[iParam].IsObject() + && !pDescr->parameterTypes[iParam].IsReference() + && (pDescr->parameterTypes[iParam].GetObjectType()->flags & COMPLEX_MASK) ) + { + void *pObj = (void*)pArgs[iSpos++]; + asSTypeBehaviour *pBeh = &pDescr->parameterTypes[iParam].GetObjectType()->beh; + if( pBeh->destruct ) + pEngine->CallObjectMethod(pObj, pBeh->destruct); + + pEngine->CallFree(pObj); + } + else + iSpos += pDescr->parameterTypes[iParam].GetSizeInMemoryDWords(); + } + } +#endif + + // Store the returned value in our stack + if ( pDescr->returnType.IsObject() + && !pDescr->returnType.IsReference() ) + { + if ( pDescr->returnType.IsObjectHandle() ) + { + pContext->regs.objectRegister = (void*)(asDWORD)dwRetQW; + + if ( pSysFunc->returnAutoHandle + && pContext->regs.objectRegister ) + pEngine->CallObjectMethod( pContext->regs.objectRegister, pDescr->returnType.GetObjectType()->beh.addref ); + } + else + { + if ( !pSysFunc->hostReturnInMemory ) + { + // Copy the returned value to the pointer sent by the script engine + if ( pSysFunc->hostReturnSize == 1 ) + *(asDWORD*)pRetPointer = (asDWORD)dwRetQW; + else + *(asQWORD*)pRetPointer = dwRetQW; + } + + // Store the object in the register + pContext->regs.objectRegister = pRetPointer; + } + } + else + { + // If the retval is bool, then endian swap it /it would not neccessary if the AS_BIG_ENDIAN would swap the bool from 0x01000000 to 0x00000010/ + if ( pDescr->returnType.GetTokenType() == ttBool ) + { + ((asBYTE*)(&dwRetQW))[4] = ((asBYTE*)(&dwRetQW))[7]; + ((asBYTE*)(&dwRetQW))[7] = 0; + } + + // Store value in returnVal register + if ( pSysFunc->hostReturnFloat ) + { + if ( pSysFunc->hostReturnSize == 1 ) + *(asDWORD*)&pContext->regs.valueRegister = GetReturnedFloat(); + else + pContext->regs.valueRegister = GetReturnedDouble(); + } + else if ( pSysFunc->hostReturnSize == 1 ) + *(asDWORD*)&pContext->regs.valueRegister = (asDWORD)dwRetQW; + else + pContext->regs.valueRegister = dwRetQW; + } + + if( pSysFunc->hasAutoHandles ) + { + pArgs = pContext->regs.stackPointer; + if ( iCallConv >= ICC_THISCALL + && !pObjectPointer ) + pArgs++; + + int iSpos = 0; + for ( asUINT uParam = 0; uParam < pDescr->parameterTypes.GetLength(); uParam++ ) + { + if ( pSysFunc->paramAutoHandles[uParam] + && pArgs[iSpos] ) + { + // Call the release method on the type + pEngine->CallObjectMethod( (void*)pArgs[iSpos], pDescr->parameterTypes[uParam].GetObjectType()->beh.release ); + pArgs[iSpos] = 0; + } + + if ( pDescr->parameterTypes[uParam].IsObject() + && !pDescr->parameterTypes[uParam].IsObjectHandle() + && !pDescr->parameterTypes[uParam].IsReference() ) + iSpos++; + else + iSpos += pDescr->parameterTypes[uParam].GetSizeOnStackDWords(); + } + } + + return iPopSize; +} + +END_AS_NAMESPACE + +#endif // AS_XENON +#endif // AS_MAX_PORTABILITY + +//------------------------------------------------------------------ + diff --git a/AngelScript/source/as_compiler.cpp b/AngelScript/source/as_compiler.cpp new file mode 100644 index 000000000..80f48021d --- /dev/null +++ b/AngelScript/source/as_compiler.cpp @@ -0,0 +1,9472 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_compiler.cpp +// +// The class that does the actual compilation of the functions +// + +#include // fmodf() + +#include "as_config.h" +#include "as_compiler.h" +#include "as_tokendef.h" +#include "as_tokenizer.h" +#include "as_string_util.h" +#include "as_texts.h" +#include "as_parser.h" + +BEGIN_AS_NAMESPACE + +asCCompiler::asCCompiler(asCScriptEngine *engine) : byteCode(engine) +{ + builder = 0; + script = 0; + + variables = 0; + isProcessingDeferredParams = false; + noCodeOutput = 0; +} + +asCCompiler::~asCCompiler() +{ + while( variables ) + { + asCVariableScope *var = variables; + variables = variables->parent; + + asDELETE(var,asCVariableScope); + } +} + +void asCCompiler::Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc) +{ + this->builder = builder; + this->engine = builder->engine; + this->script = script; + this->outFunc = outFunc; + + hasCompileErrors = false; + + m_isConstructor = false; + m_isConstructorCalled = false; + + nextLabel = 0; + breakLabels.SetLength(0); + continueLabels.SetLength(0); + + byteCode.ClearAll(); + objVariableTypes.SetLength(0); + objVariablePos.SetLength(0); + + globalExpression = false; +} + +int asCCompiler::CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc) +{ + Reset(builder, script, outFunc); + + // If the class is derived from another, then the base class' default constructor must be called + if( outFunc->objectType->derivedFrom ) + { + // Call the base class' default constructor + byteCode.InstrSHORT(asBC_PSF, 0); + byteCode.Instr(asBC_RDSPTR); + byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE); + } + + // Pop the object pointer from the stack + byteCode.Ret(AS_PTR_SIZE); + + FinalizeFunction(); + +#ifdef AS_DEBUG + // DEBUG: output byte code + byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + "__dc.txt").AddressOf(), engine); +#endif + + return 0; +} + +int asCCompiler::CompileFactory(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc) +{ + Reset(builder, script, outFunc); + + unsigned int n; + + // Find the corresponding constructor + asCDataType dt = asCDataType::CreateObject(outFunc->returnType.GetObjectType(), false); + int constructor = 0; + for( n = 0; n < dt.GetBehaviour()->factories.GetLength(); n++ ) + { + if( dt.GetBehaviour()->factories[n] == outFunc->id ) + { + constructor = dt.GetBehaviour()->constructors[n]; + break; + } + } + + // Allocate the class and instanciate it with the constructor + int varOffset = AllocateVariable(dt, true); + + byteCode.Push(AS_PTR_SIZE); + byteCode.InstrSHORT(asBC_PSF, (short)varOffset); + + // Copy all arguments to the top of the stack + int argDwords = (int)outFunc->GetSpaceNeededForArguments(); + for( int a = argDwords-1; a >= 0; a-- ) + byteCode.InstrSHORT(asBC_PshV4, short(-a)); + + byteCode.Alloc(asBC_ALLOC, dt.GetObjectType(), constructor, argDwords + AS_PTR_SIZE); + + // Return a handle to the newly created object + byteCode.InstrSHORT(asBC_LOADOBJ, (short)varOffset); + + byteCode.Pop(AS_PTR_SIZE); + byteCode.Ret(argDwords); + + FinalizeFunction(); + + // Tell the virtual machine not to clean up parameters on exception + outFunc->dontCleanUpOnException = true; + +/* +#ifdef AS_DEBUG + // DEBUG: output byte code + asCString args; + args.Format("%d", outFunc->parameterTypes.GetLength()); + byteCode.DebugOutput(("__" + outFunc->name + "__factory" + args + ".txt").AddressOf(), engine); +#endif +*/ + return 0; +} + +// Entry +int asCCompiler::CompileTemplateFactoryStub(asCBuilder *builder, int trueFactoryId, asCObjectType *objType, asCScriptFunction *outFunc) +{ + Reset(builder, 0, outFunc); + + asCScriptFunction *descr = builder->GetFunctionDescription(trueFactoryId); + + byteCode.InstrPTR(asBC_OBJTYPE, objType); + byteCode.Call(asBC_CALLSYS, trueFactoryId, descr->GetSpaceNeededForArguments()); + byteCode.Ret(outFunc->GetSpaceNeededForArguments()); + + FinalizeFunction(); + + // Tell the virtual machine not to clean up the object on exception + outFunc->dontCleanUpOnException = true; + + return 0; +} + +// Entry +int asCCompiler::CompileFunction(asCBuilder *builder, asCScriptCode *script, asCScriptNode *func, asCScriptFunction *outFunc) +{ + Reset(builder, script, outFunc); + int buildErrors = builder->numErrors; + + int stackPos = 0; + if( outFunc->objectType ) + stackPos = -AS_PTR_SIZE; // The first parameter is the pointer to the object + + // Reserve a label for the cleanup code + nextLabel++; + + // Add the first variable scope, which the parameters and + // variables declared in the outermost statement block is + // part of. + AddVariableScope(); + + //---------------------------------------------- + // Examine return type + bool isDestructor = false; + asCDataType returnType; + if( func->firstChild->nodeType == snDataType ) + { + returnType = builder->CreateDataTypeFromNode(func->firstChild, script); + returnType = builder->ModifyDataTypeFromNode(returnType, func->firstChild->next, script, 0, 0); + + // Make sure the return type is instanciable or is void + if( !returnType.CanBeInstanciated() && + returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + { + asCString str; + str.Format(TXT_DATA_TYPE_CANT_BE_s, returnType.Format().AddressOf()); + Error(str.AddressOf(), func->firstChild); + } + + // TODO: Add support for returning references + // The script language doesn't support returning references yet + if( returnType.IsReference() ) + { + Error(TXT_SCRIPT_FUNCTIONS_DOESNT_SUPPORT_RETURN_REF, func->firstChild); + } + } + else + { + returnType = asCDataType::CreatePrimitive(ttVoid, false); + if( func->firstChild->tokenType == ttBitNot ) + isDestructor = true; + else + m_isConstructor = true; + } + + //---------------------------------------------- + // Declare parameters + // Find first parameter + asCScriptNode *node = func->firstChild; + while( node && node->nodeType != snParameterList ) + node = node->next; + + // Register parameters from last to first, otherwise they will be destroyed in the wrong order + asCVariableScope vs(0); + + if( node ) node = node->firstChild; + while( node ) + { + // Get the parameter type + asCDataType type = builder->CreateDataTypeFromNode(node, script); + + asETypeModifiers inoutFlag = asTM_NONE; + type = builder->ModifyDataTypeFromNode(type, node->next, script, &inoutFlag, 0); + + // Is the data type allowed? + if( (type.IsReference() && inoutFlag != asTM_INOUTREF && !type.CanBeInstanciated()) || + (!type.IsReference() && !type.CanBeInstanciated()) ) + { + asCString str; + str.Format(TXT_PARAMETER_CANT_BE_s, type.Format().AddressOf()); + Error(str.AddressOf(), node); + } + + // If the parameter has a name then declare it as variable + node = node->next->next; + if( node && node->nodeType == snIdentifier ) + { + asCString name(&script->code[node->tokenPos], node->tokenLength); + + if( vs.DeclareVariable(name.AddressOf(), type, stackPos) < 0 ) + Error(TXT_PARAMETER_ALREADY_DECLARED, node); + + outFunc->AddVariable(name, type, stackPos); + + node = node->next; + } + else + vs.DeclareVariable("", type, stackPos); + + // Move to next parameter + stackPos -= type.GetSizeOnStackDWords(); + } + + int n; + for( n = (int)vs.variables.GetLength() - 1; n >= 0; n-- ) + { + variables->DeclareVariable(vs.variables[n]->name.AddressOf(), vs.variables[n]->type, vs.variables[n]->stackOffset); + } + + // Is the return type allowed? + if( (returnType.GetSizeOnStackDWords() == 0 && returnType != asCDataType::CreatePrimitive(ttVoid, false)) || + (returnType.IsReference() && !returnType.CanBeInstanciated()) ) + { + asCString str; + str.Format(TXT_RETURN_CANT_BE_s, returnType.Format().AddressOf()); + Error(str.AddressOf(), node); + } + + variables->DeclareVariable("return", returnType, stackPos); + + //-------------------------------------------- + // Compile the statement block + + // We need to parse the statement block now + + // TODO: memory: We can parse the statement block one statement at a time, thus save even more memory + asCParser parser(builder); + int r = parser.ParseStatementBlock(script, func->lastChild); + if( r < 0 ) return -1; + asCScriptNode *block = parser.GetScriptNode(); + + bool hasReturn; + asCByteCode bc(engine); + LineInstr(&bc, func->lastChild->tokenPos); + CompileStatementBlock(block, false, &hasReturn, &bc); + LineInstr(&bc, func->lastChild->tokenPos + func->lastChild->tokenLength); + + // Make sure there is a return in all paths (if not return type is void) + if( returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + { + if( hasReturn == false ) + Error(TXT_NOT_ALL_PATHS_RETURN, func->lastChild); + } + + //------------------------------------------------ + // Concatenate the bytecode + + // Insert a JitEntry at the start of the function for JIT compilers + byteCode.InstrWORD(asBC_JitEntry, 0); + + // Count total variable size + int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1; + byteCode.Push(varSize); + + if( outFunc->objectType ) + { + // Call the base class' default constructor unless called manually in the code + if( m_isConstructor && !m_isConstructorCalled && outFunc->objectType->derivedFrom ) + { + byteCode.InstrSHORT(asBC_PSF, 0); + byteCode.Instr(asBC_RDSPTR); + byteCode.Call(asBC_CALL, outFunc->objectType->derivedFrom->beh.construct, AS_PTR_SIZE); + } + + // Increase the reference for the object pointer, so that it is guaranteed to live during the entire call + // TODO: optimize: This is probably not necessary for constructors as no outside reference to the object is created yet + byteCode.InstrSHORT(asBC_PSF, 0); + byteCode.Instr(asBC_RDSPTR); + byteCode.Call(asBC_CALLSYS, outFunc->objectType->beh.addref, AS_PTR_SIZE); + } + + // Add the code for the statement block + byteCode.AddCode(&bc); + + // Deallocate all local variables + for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + if( v->stackOffset > 0 ) + { + // Call variables destructors + if( v->name != "return" && v->name != "return address" ) + CallDestructor(v->type, v->stackOffset, &byteCode); + + DeallocateVariable(v->stackOffset); + } + } + + // This is the label that return statements jump to + // in order to exit the function + byteCode.Label(0); + + // Release the object pointer again + if( outFunc->objectType ) + { + byteCode.InstrSHORT(asBC_PSF, 0); + byteCode.InstrPTR(asBC_FREE, outFunc->objectType); + } + + // Call destructors for function parameters + for( n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + if( v->stackOffset <= 0 ) + { + // Call variable destructors here, for variables not yet destroyed + if( v->name != "return" && v->name != "return address" ) + CallDestructor(v->type, v->stackOffset, &byteCode); + } + + // Do not deallocate parameters + } + + // If there are compile errors, there is no reason to build the final code + if( hasCompileErrors || builder->numErrors != buildErrors ) + return -1; + + // At this point there should be no variables allocated + asASSERT(variableAllocations.GetLength() == freeVariables.GetLength()); + + // Remove the variable scope + RemoveVariableScope(); + + byteCode.Pop(varSize); + + byteCode.Ret(-stackPos); + + FinalizeFunction(); + +#ifdef AS_DEBUG + // DEBUG: output byte code + if( outFunc->objectType ) + byteCode.DebugOutput(("__" + outFunc->objectType->name + "_" + outFunc->name + ".txt").AddressOf(), engine); + else + byteCode.DebugOutput(("__" + outFunc->name + ".txt").AddressOf(), engine); +#endif + + return 0; +} + +int asCCompiler::CallCopyConstructor(asCDataType &type, int offset, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool isGlobalVar) +{ + if( !type.IsObject() ) + return 0; + + // CallCopyConstructor should not be called for object handles. + asASSERT(!type.IsObjectHandle()); + + asCArray args; + args.PushLast(arg); + + // The reference parameter must be pushed on the stack + asASSERT( arg->type.dataType.GetObjectType() == type.GetObjectType() ); + + // Since we're calling the copy constructor, we have to trust the function to not do + // anything stupid otherwise we will just enter a loop, as we try to make temporary + // copies of the argument in order to guarantee safety. + + + if( type.GetObjectType()->flags & asOBJ_REF ) + { + asSExprContext ctx(engine); + + int func = 0; + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) func = beh->copyfactory; + + if( func > 0 ) + { + if( !isGlobalVar ) + { + // Call factory and store the handle in the given variable + PerformFunctionCall(func, &ctx, false, &args, type.GetObjectType(), true, offset); + + // Pop the reference left by the function call + ctx.bc.Pop(AS_PTR_SIZE); + } + else + { + // Call factory + PerformFunctionCall(func, &ctx, false, &args, type.GetObjectType()); + + // Store the returned handle in the global variable + ctx.bc.Instr(asBC_RDSPTR); + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType()); + ctx.bc.Pop(AS_PTR_SIZE); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + + bc->AddCode(&ctx.bc); + + return 0; + } + } + else + { + asSTypeBehaviour *beh = type.GetBehaviour(); + int func = beh ? beh->copyconstruct : 0; + if( func > 0 ) + { + // Push the address where the object will be stored on the stack, before the argument + // TODO: When the context is serializable this probably has to be changed, since this + // pointer can remain on the stack while the context is suspended. There is no + // risk the pointer becomes invalid though, there is just no easy way to serialize it. + asCByteCode tmp(engine); + if( isGlobalVar ) + tmp.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + else + tmp.InstrSHORT(asBC_PSF, (short)offset); + tmp.AddCode(bc); + bc->AddCode(&tmp); + + asSExprContext ctx(engine); + PerformFunctionCall(func, &ctx, true, &args, type.GetObjectType()); + + bc->AddCode(&ctx.bc); + + return 0; + } + } + + // Class has no copy constructor/factory. + asCString str; + str.Format(TXT_NO_COPY_CONSTRUCTOR_FOR_s, type.GetObjectType()->GetName()); + Error(str.AddressOf(), node); + + return -1; +} + +int asCCompiler::CallDefaultConstructor(asCDataType &type, int offset, asCByteCode *bc, asCScriptNode *node, bool isGlobalVar) +{ + if( !type.IsObject() || type.IsObjectHandle() ) + return 0; + + if( type.GetObjectType()->flags & asOBJ_REF ) + { + asSExprContext ctx(engine); + + int func = 0; + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) func = beh->factory; + + if( func > 0 ) + { + if( !isGlobalVar ) + { + // Call factory and store the handle in the given variable + PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType(), true, offset); + + // Pop the reference left by the function call + ctx.bc.Pop(AS_PTR_SIZE); + } + else + { + // Call factory + PerformFunctionCall(func, &ctx, false, 0, type.GetObjectType()); + + // Store the returned handle in the global variable + ctx.bc.Instr(asBC_RDSPTR); + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + ctx.bc.InstrPTR(asBC_REFCPY, type.GetObjectType()); + ctx.bc.Pop(AS_PTR_SIZE); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + + bc->AddCode(&ctx.bc); + + return 0; + } + } + else + { + asSTypeBehaviour *beh = type.GetBehaviour(); + + int func = 0; + if( beh ) func = beh->construct; + + // Allocate and initialize with the default constructor + if( func != 0 || (type.GetObjectType()->flags & asOBJ_POD) ) + { + if( isGlobalVar ) + bc->InstrPTR(asBC_PGA, engine->globalProperties[offset]->GetAddressOfValue()); + else + bc->InstrSHORT(asBC_PSF, (short)offset); + + bc->Alloc(asBC_ALLOC, type.GetObjectType(), func, AS_PTR_SIZE); + + return 0; + } + } + + // Class has no default factory/constructor. + asCString str; + str.Format(TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s, type.GetObjectType()->GetName()); + Error(str.AddressOf(), node); + + return -1; +} + +void asCCompiler::CallDestructor(asCDataType &type, int offset, asCByteCode *bc) +{ + if( !type.IsReference() ) + { + // Call destructor for the data type + if( type.IsObject() ) + { + // Free the memory + bc->InstrSHORT(asBC_PSF, (short)offset); + bc->InstrPTR(asBC_FREE, type.GetObjectType()); + } + } +} + +void asCCompiler::LineInstr(asCByteCode *bc, size_t pos) +{ + int r, c; + script->ConvertPosToRowCol(pos, &r, &c); + bc->Line(r, c); +} + +void asCCompiler::CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc) +{ + *hasReturn = false; + bool isFinished = false; + bool hasWarned = false; + + if( ownVariableScope ) + AddVariableScope(); + + asCScriptNode *node = block->firstChild; + while( node ) + { + if( !hasWarned && (*hasReturn || isFinished) ) + { + hasWarned = true; + Warning(TXT_UNREACHABLE_CODE, node); + } + + if( node->nodeType == snBreak || node->nodeType == snContinue ) + isFinished = true; + + asCByteCode statement(engine); + if( node->nodeType == snDeclaration ) + CompileDeclaration(node, &statement); + else + CompileStatement(node, hasReturn, &statement); + + LineInstr(bc, node->tokenPos); + bc->AddCode(&statement); + + if( !hasCompileErrors ) + asASSERT( tempVariables.GetLength() == 0 ); + + node = node->next; + } + + if( ownVariableScope ) + { + + // Deallocate variables in this block, in reverse order + for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + + // Call variable destructors here, for variables not yet destroyed + // If the block is terminated with a break, continue, or + // return the variables are already destroyed + if( !isFinished && !*hasReturn ) + CallDestructor(v->type, v->stackOffset, bc); + + // Don't deallocate function parameters + if( v->stackOffset > 0 ) + DeallocateVariable(v->stackOffset); + } + + RemoveVariableScope(); + } +} + +// Entry +int asCCompiler::CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *node, sGlobalVariableDescription *gvar, asCScriptFunction *outFunc) +{ + Reset(builder, script, outFunc); + globalExpression = true; + + // Add a variable scope (even though variables can't be declared) + AddVariableScope(); + + asSExprContext ctx(engine); + + gvar->isPureConstant = false; + + // Parse the initialization nodes + asCParser parser(builder); + if( node ) + { + int r = parser.ParseGlobalVarInit(script, node); + if( r < 0 ) + return r; + + node = parser.GetScriptNode(); + } + + // Compile the expression + if( node && node->nodeType == snArgList ) + { + // Make sure that it is a registered type, and that it isn't a pointer + if( gvar->datatype.GetObjectType() == 0 || gvar->datatype.IsObjectHandle() ) + { + Error(TXT_MUST_BE_OBJECT, node); + } + else + { + // Compile the arguments + asCArray args; + if( CompileArgumentList(node, args) >= 0 ) + { + // Find all constructors + asCArray funcs; + asSTypeBehaviour *beh = gvar->datatype.GetBehaviour(); + if( beh ) + { + if( gvar->datatype.GetObjectType()->flags & asOBJ_REF ) + funcs = beh->factories; + else + funcs = beh->constructors; + } + + asCString str = gvar->datatype.Format(); + MatchFunctions(funcs, args, node, str.AddressOf()); + + if( funcs.GetLength() == 1 ) + { + if( gvar->datatype.GetObjectType()->flags & asOBJ_REF ) + { + MakeFunctionCall(&ctx, funcs[0], 0, args, node); + + // Store the returned handle in the global variable + ctx.bc.Instr(asBC_RDSPTR); + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[gvar->index]->GetAddressOfValue()); + ctx.bc.InstrPTR(asBC_REFCPY, gvar->datatype.GetObjectType()); + ctx.bc.Pop(AS_PTR_SIZE); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + else + { + // Push the address of the location where the variable will be stored on the stack. + // This reference is safe, because the addresses of the global variables cannot change. + // TODO: When serialization of the context is implemented this will probably have to change, + // because this pointer may be on the stack while the context is suspended, and may + // be difficult to serialize as the context doesn't know that the value represents a + // pointer. + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[gvar->index]->GetAddressOfValue()); + + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); + + PerformFunctionCall(funcs[0], &ctx, true, &args, gvar->datatype.GetObjectType()); + } + } + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n],asSExprContext); + } + } + } + else if( node && node->nodeType == snInitList ) + { + asCTypeInfo ti; + ti.Set(gvar->datatype); + ti.isVariable = false; + ti.isTemporary = false; + ti.stackOffset = (short)gvar->index; + + CompileInitList(&ti, node, &ctx.bc); + + node = node->next; + } + else if( node ) + { + // Compile the right hand expression + asSExprContext expr(engine); + int r = CompileAssignment(node, &expr); if( r < 0 ) return r; + + // Assign the value to the variable + if( gvar->datatype.IsPrimitive() ) + { + if( gvar->datatype.IsReadOnly() && expr.type.isConstant ) + { + ImplicitConversion(&expr, gvar->datatype, node, asIC_IMPLICIT_CONV); + + gvar->isPureConstant = true; + gvar->constantValue = expr.type.qwordValue; + } + + asSExprContext lctx(engine); + lctx.type.Set(gvar->datatype); + lctx.type.dataType.MakeReference(true); + lctx.type.dataType.MakeReadOnly(false); + + // If it is an enum value that is being compiled, then + // we skip this, as the bytecode won't be used anyway + if( !gvar->isEnumValue ) + lctx.bc.InstrPTR(asBC_LDG, engine->globalProperties[gvar->index]->GetAddressOfValue()); + + DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node); + } + else + { + // TODO: copy: Here we should look for the best matching constructor, instead of + // just the copy constructor. Only if no appropriate constructor is + // available should the assignment operator be used. + + if( !gvar->datatype.IsObjectHandle() ) + { + // Call the default constructor to have a valid object for the assignment + CallDefaultConstructor(gvar->datatype, gvar->index, &ctx.bc, gvar->idNode, true); + } + + asSExprContext lexpr(engine); + lexpr.type.Set(gvar->datatype); + lexpr.type.dataType.MakeReference(true); + lexpr.type.dataType.MakeReadOnly(false); + lexpr.type.stackOffset = -1; + + if( gvar->datatype.IsObjectHandle() ) + lexpr.type.isExplicitHandle = true; + + lexpr.bc.InstrPTR(asBC_PGA, engine->globalProperties[gvar->index]->GetAddressOfValue()); + + // If left expression resolves into a registered type + // check if the assignment operator is overloaded, and check + // the type of the right hand expression. If none is found + // the default action is a direct copy if it is the same type + // and a simple assignment. + bool assigned = false; + if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle ) + { + assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx); + if( assigned ) + { + // Pop the resulting value + ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords()); + + // Release the argument + ProcessDeferredParams(&ctx); + } + } + + if( !assigned ) + { + PrepareForAssignment(&lexpr.type.dataType, &expr, node); + + // If the expression is constant and the variable also is constant + // then mark the variable as pure constant. This will allow the compiler + // to optimize expressions with this variable. + if( gvar->datatype.IsReadOnly() && expr.type.isConstant ) + { + gvar->isPureConstant = true; + gvar->constantValue = expr.type.qwordValue; + } + + // Add expression code to bytecode + MergeExprContexts(&ctx, &expr); + + // Add byte code for storing value of expression in variable + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[gvar->index]->GetAddressOfValue()); + + PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr.type, &ctx.bc); + + ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords()); + } + } + } + else if( gvar->datatype.IsObject() && !gvar->datatype.IsObjectHandle() ) + { + // Call the default constructor in case no explicit initialization is given + CallDefaultConstructor(gvar->datatype, gvar->index, &ctx.bc, gvar->idNode, true); + } + + // Concatenate the bytecode + int varSize = GetVariableOffset((int)variableAllocations.GetLength()) - 1; + + // We need to push zeroes on the stack to guarantee + // that temporary object handles are clear + int n; + for( n = 0; n < varSize; n++ ) + byteCode.InstrINT(asBC_PshC4, 0); + + byteCode.AddCode(&ctx.bc); + + // Deallocate variables in this block, in reverse order + for( n = (int)variables->variables.GetLength() - 1; n >= 0; --n ) + { + sVariable *v = variables->variables[n]; + + // Call variable destructors here, for variables not yet destroyed + CallDestructor(v->type, v->stackOffset, &byteCode); + + DeallocateVariable(v->stackOffset); + } + + if( hasCompileErrors ) return -1; + + // At this point there should be no variables allocated + asASSERT(variableAllocations.GetLength() == freeVariables.GetLength()); + + // Remove the variable scope again + RemoveVariableScope(); + + if( varSize ) + byteCode.Pop(varSize); + + byteCode.Ret(0); + + FinalizeFunction(); + +#ifdef AS_DEBUG + // DEBUG: output byte code + byteCode.DebugOutput(("___init_" + gvar->name + ".txt").AddressOf(), engine); +#endif + + return 0; +} + +void asCCompiler::FinalizeFunction() +{ + asUINT n; + + // Tell the bytecode which variables are temporary + for( n = 0; n < (signed)variableIsTemporary.GetLength(); n++ ) + { + if( variableIsTemporary[n] ) + byteCode.DefineTemporaryVariable(GetVariableOffset(n)); + } + + // Finalize the bytecode + byteCode.Finalize(); + + // Compile the list of object variables for the exception handler + for( n = 0; n < (int)variableAllocations.GetLength(); n++ ) + { + if( variableAllocations[n].IsObject() && !variableAllocations[n].IsReference() ) + { + objVariableTypes.PushLast(variableAllocations[n].GetObjectType()); + objVariablePos.PushLast(GetVariableOffset(n)); + } + } + + // Copy byte code to the function + outFunc->byteCode.SetLength(byteCode.GetSize()); + byteCode.Output(outFunc->byteCode.AddressOf()); + outFunc->AddReferences(); + outFunc->stackNeeded = byteCode.largestStackUsed; + outFunc->lineNumbers = byteCode.lineNumbers; + outFunc->objVariablePos = objVariablePos; + outFunc->objVariableTypes = objVariableTypes; +} + +void asCCompiler::PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction, int refType, asCArray *reservedVars) +{ + asCDataType param = *paramType; + if( paramType->GetTokenType() == ttQuestion ) + { + // Since the function is expecting a var type ?, then we don't want to convert the argument to anything else + param = ctx->type.dataType; + param.MakeHandle(ctx->type.isExplicitHandle); + param.MakeReference(paramType->IsReference()); + param.MakeReadOnly(paramType->IsReadOnly()); + } + else + param = *paramType; + + asCDataType dt = param; + + // Need to protect arguments by reference + if( isFunction && dt.IsReference() ) + { + if( paramType->GetTokenType() == ttQuestion ) + { + asCByteCode tmpBC(engine); + + // Place the type id on the stack as a hidden parameter + tmpBC.InstrDWORD(asBC_TYPEID, engine->GetTypeIdFromDataType(param)); + + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); + } + + // Allocate a temporary variable of the same type as the argument + dt.MakeReference(false); + dt.MakeReadOnly(false); + + int offset; + if( refType == 1 ) // &in + { + ProcessPropertyGetAccessor(ctx, node); + + // If the reference is const, then it is not necessary to make a copy if the value already is a variable + // Even if the same variable is passed in another argument as non-const then there is no problem + if( dt.IsPrimitive() || dt.IsNullHandle() ) + { + IsVariableInitialized(&ctx->type, node); + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars); + + if( !(param.IsReadOnly() && ctx->type.isVariable) ) + ConvertToTempVariable(ctx); + + PushVariableOnStack(ctx, true); + ctx->type.dataType.MakeReadOnly(param.IsReadOnly()); + } + else + { + IsVariableInitialized(&ctx->type, node); + + ImplicitConversion(ctx, param, node, asIC_IMPLICIT_CONV, true, reservedVars); + + if( !ctx->type.dataType.IsEqualExceptRef(param) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), param.Format().AddressOf()); + Error(str.AddressOf(), node); + + ctx->type.Set(param); + } + + // If the argument already is a temporary + // variable we don't need to allocate another + + // If the parameter is read-only and the object already is a local + // variable then it is not necessary to make a copy either + if( !ctx->type.isTemporary && !(param.IsReadOnly() && ctx->type.isVariable) ) + { + // Make sure the variable is not used in the expression + asCArray vars; + ctx->bc.GetVarsUsed(vars); + if( reservedVars ) vars.Concatenate(*reservedVars); + offset = AllocateVariableNotIn(dt, true, &vars); + + // TODO: copy: Use copy constructor if available. See PrepareTemporaryObject() + + // Allocate and construct the temporary object + asCByteCode tmpBC(engine); + CallDefaultConstructor(dt, offset, &tmpBC, node); + + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); + + // Assign the evaluated expression to the temporary variable + PrepareForAssignment(&dt, ctx, node); + + dt.MakeReference(true); + asCTypeInfo type; + type.Set(dt); + type.isTemporary = true; + type.stackOffset = (short)offset; + + if( dt.IsObjectHandle() ) + type.isExplicitHandle = true; + + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + + PerformAssignment(&type, &ctx->type, &ctx->bc, node); + + ctx->bc.Pop(ctx->type.dataType.GetSizeOnStackDWords()); + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + ctx->type = type; + + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( dt.IsObject() && !dt.IsObjectHandle() ) + ctx->bc.Instr(asBC_RDSPTR); + + if( paramType->IsReadOnly() ) + ctx->type.dataType.MakeReadOnly(true); + } + } + } + else if( refType == 2 ) // &out + { + // Make sure the variable is not used in the expression + asCArray vars; + ctx->bc.GetVarsUsed(vars); + if( reservedVars ) vars.Concatenate(*reservedVars); + offset = AllocateVariableNotIn(dt, true, &vars); + + if( dt.IsPrimitive() ) + { + ctx->type.SetVariable(dt, offset, true); + PushVariableOnStack(ctx, true); + } + else + { + // Allocate and construct the temporary object + asCByteCode tmpBC(engine); + CallDefaultConstructor(dt, offset, &tmpBC, node); + + // Insert the code before the expression code + tmpBC.AddCode(&ctx->bc); + ctx->bc.AddCode(&tmpBC); + + dt.MakeReference((!dt.IsObject() || dt.IsObjectHandle())); + asCTypeInfo type; + type.Set(dt); + type.isTemporary = true; + type.stackOffset = (short)offset; + + ctx->type = type; + + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + if( dt.IsObject() && !dt.IsObjectHandle() ) + ctx->bc.Instr(asBC_RDSPTR); + } + + // After the function returns the temporary variable will + // be assigned to the expression, if it is a valid lvalue + } + else if( refType == asTM_INOUTREF ) + { + // Literal constants cannot be passed to inout ref arguments + if( !ctx->type.isVariable && ctx->type.isConstant ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + } + + // Only objects that support object handles + // can be guaranteed to be safe. Local variables are + // already safe, so there is no need to add an extra + // references + if( !engine->ep.allowUnsafeReferences && + !ctx->type.isVariable && + ctx->type.dataType.IsObject() && + !ctx->type.dataType.IsObjectHandle() && + ctx->type.dataType.GetBehaviour()->addref && + ctx->type.dataType.GetBehaviour()->release ) + { + // Store a handle to the object as local variable + asSExprContext tmp(engine); + asCDataType dt = ctx->type.dataType; + dt.MakeHandle(true); + dt.MakeReference(false); + + asCArray vars; + ctx->bc.GetVarsUsed(vars); + if( reservedVars ) vars.Concatenate(*reservedVars); + offset = AllocateVariableNotIn(dt, true, &vars); + + // Copy the handle + if( !ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReference() ) + ctx->bc.Instr(asBC_RDSPTR); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType()); + ctx->bc.Pop(AS_PTR_SIZE); + ctx->bc.InstrWORD(asBC_PSF, (asWORD)offset); + + dt.MakeHandle(false); + dt.MakeReference(true); + + // Release previous temporary variable stored in the context (if any) + if( ctx->type.isTemporary ) + { + ReleaseTemporaryVariable(ctx->type.stackOffset, &ctx->bc); + } + + ctx->type.SetVariable(dt, offset, true); + } + + // Make sure the reference to the value is on the stack + if( ctx->type.dataType.IsObject() && ctx->type.dataType.IsReference() ) + Dereference(ctx, true); + else if( ctx->type.isVariable ) + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + else if( ctx->type.dataType.IsPrimitive() ) + ctx->bc.Instr(asBC_PshRPtr); + } + } + else + { + ProcessPropertyGetAccessor(ctx, node); + + if( dt.IsPrimitive() ) + { + IsVariableInitialized(&ctx->type, node); + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + + // Implicitly convert primitives to the parameter type + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars); + + if( ctx->type.isVariable ) + { + PushVariableOnStack(ctx, dt.IsReference()); + } + else if( ctx->type.isConstant ) + { + ConvertToVariable(ctx); + PushVariableOnStack(ctx, dt.IsReference()); + } + } + else + { + IsVariableInitialized(&ctx->type, node); + + // Implicitly convert primitives to the parameter type + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV, true, reservedVars); + + // Was the conversion successful? + if( !ctx->type.dataType.IsEqualExceptRef(dt) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), dt.Format().AddressOf()); + Error(str.AddressOf(), node); + + ctx->type.Set(dt); + } + + if( dt.IsObjectHandle() ) + ctx->type.isExplicitHandle = true; + + if( dt.IsObject() ) + { + if( !dt.IsReference() ) + { + // Objects passed by value must be placed in temporary variables + // so that they are guaranteed to not be referenced anywhere else + PrepareTemporaryObject(node, ctx, reservedVars); + + // The implicit conversion shouldn't convert the object to + // non-reference yet. It will be dereferenced just before the call. + // Otherwise the object might be missed by the exception handler. + dt.MakeReference(true); + } + else + { + // An object passed by reference should place the pointer to + // the object on the stack. + dt.MakeReference(false); + } + } + } + } + + // Don't put any pointer on the stack yet + if( param.IsReference() || param.IsObject() ) + { + // &inout parameter may leave the reference on the stack already + if( refType != 3 ) + { + ctx->bc.Pop(AS_PTR_SIZE); + ctx->bc.InstrSHORT(asBC_VAR, ctx->type.stackOffset); + } + + ProcessDeferredParams(ctx); + } +} + +void asCCompiler::PrepareFunctionCall(int funcID, asCByteCode *bc, asCArray &args) +{ + // When a match has been found, compile the final byte code using correct parameter types + asCScriptFunction *descr = builder->GetFunctionDescription(funcID); + + // Add code for arguments + asSExprContext e(engine); + int n; + for( n = (int)args.GetLength()-1; n >= 0; n-- ) + { + // Make sure PrepareArgument doesn't use any variable that is already + // being used by any of the following argument expressions + asCArray reservedVars; + for( int m = n-1; m >= 0; m-- ) + args[m]->bc.GetVarsUsed(reservedVars); + + PrepareArgument2(&e, args[n], &descr->parameterTypes[n], true, descr->inOutFlags[n], &reservedVars); + } + + bc->AddCode(&e.bc); +} + +void asCCompiler::MoveArgsToStack(int funcID, asCByteCode *bc, asCArray &args, bool addOneToOffset) +{ + asCScriptFunction *descr = builder->GetFunctionDescription(funcID); + + int offset = 0; + if( addOneToOffset ) + offset += AS_PTR_SIZE; + + // Move the objects that are sent by value to the stack just before the call + for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ ) + { + if( descr->parameterTypes[n].IsReference() ) + { + if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() ) + { + if( descr->inOutFlags[n] != asTM_INOUTREF ) + bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset); + if( args[n]->type.dataType.IsObjectHandle() ) + bc->InstrWORD(asBC_ChkNullS, (asWORD)offset); + } + else if( descr->inOutFlags[n] != asTM_INOUTREF ) + { + if( descr->parameterTypes[n].GetTokenType() == ttQuestion && + args[n]->type.dataType.IsObject() && !args[n]->type.dataType.IsObjectHandle() ) + { + // Send the object as a reference to the object, + // and not to the variable holding the object + bc->InstrWORD(asBC_GETOBJREF, (asWORD)offset); + } + else + bc->InstrWORD(asBC_GETREF, (asWORD)offset); + } + } + else if( descr->parameterTypes[n].IsObject() ) + { + bc->InstrWORD(asBC_GETOBJ, (asWORD)offset); + + // The temporary variable must not be freed as it will no longer hold an object + DeallocateVariable(args[n]->type.stackOffset); + args[n]->type.isTemporary = false; + } + + offset += descr->parameterTypes[n].GetSizeOnStackDWords(); + } +} + +int asCCompiler::CompileArgumentList(asCScriptNode *node, asCArray &args) +{ + asASSERT(node->nodeType == snArgList); + + // Count arguments + asCScriptNode *arg = node->firstChild; + int argCount = 0; + while( arg ) + { + argCount++; + arg = arg->next; + } + + // Prepare the arrays + args.SetLength(argCount); + int n; + for( n = 0; n < argCount; n++ ) + args[n] = 0; + + n = argCount-1; + + // Compile the arguments in reverse order (as they will be pushed on the stack) + bool anyErrors = false; + arg = node->lastChild; + while( arg ) + { + asSExprContext expr(engine); + int r = CompileAssignment(arg, &expr); + if( r < 0 ) anyErrors = true; + + args[n] = asNEW(asSExprContext)(engine); + MergeExprContexts(args[n], &expr); + args[n]->type = expr.type; + args[n]->property_get = expr.property_get; + args[n]->property_set = expr.property_set; + args[n]->property_const = expr.property_const; + args[n]->property_handle = expr.property_handle; + args[n]->exprNode = arg; + + n--; + arg = arg->prev; + } + + return anyErrors ? -1 : 0; +} + +void asCCompiler::MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCObjectType *objectType, bool isConstMethod, bool silent, bool allowObjectConstruct, const asCString &scope) +{ + asCArray origFuncs = funcs; // Keep the original list for error message + + asUINT n; + if( funcs.GetLength() > 0 ) + { + // Check the number of parameters in the found functions + for( n = 0; n < funcs.GetLength(); ++n ) + { + asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]); + + if( desc->parameterTypes.GetLength() != args.GetLength() ) + { + // remove it from the list + if( n == funcs.GetLength()-1 ) + funcs.PopLast(); + else + funcs[n] = funcs.PopLast(); + n--; + } + } + + // Match functions with the parameters, and discard those that do not match + asCArray matchingFuncs = funcs; + + for( n = 0; n < args.GetLength(); ++n ) + { + asCArray tempFuncs; + MatchArgument(funcs, tempFuncs, &args[n]->type, n, allowObjectConstruct); + + // Intersect the found functions with the list of matching functions + for( asUINT f = 0; f < matchingFuncs.GetLength(); f++ ) + { + asUINT c; + for( c = 0; c < tempFuncs.GetLength(); c++ ) + { + if( matchingFuncs[f] == tempFuncs[c] ) + break; + } + + // Was the function a match? + if( c == tempFuncs.GetLength() ) + { + // No, remove it from the list + if( f == matchingFuncs.GetLength()-1 ) + matchingFuncs.PopLast(); + else + matchingFuncs[f] = matchingFuncs.PopLast(); + f--; + } + } + } + + funcs = matchingFuncs; + } + + if( !isConstMethod ) + FilterConst(funcs); + + if( funcs.GetLength() != 1 && !silent ) + { + // Build a readable string of the function with parameter types + asCString str; + if( scope != "" ) + { + if( scope == "::" ) + str = scope; + else + str = scope + "::"; + } + str += name; + str += "("; + if( args.GetLength() ) + str += args[0]->type.dataType.Format(); + for( n = 1; n < args.GetLength(); n++ ) + str += ", " + args[n]->type.dataType.Format(); + str += ")"; + + if( isConstMethod ) + str += " const"; + + if( objectType && scope == "" ) + str = objectType->name + "::" + str; + + if( funcs.GetLength() == 0 ) + { + str.Format(TXT_NO_MATCHING_SIGNATURES_TO_s, str.AddressOf()); + Error(str.AddressOf(), node); + + // Print the list of candidates + if( origFuncs.GetLength() > 0 ) + { + int r, c; + script->ConvertPosToRowCol(node->tokenPos, &r, &c); + builder->WriteInfo(script->name.AddressOf(), TXT_CANDIDATES_ARE, r, c, false); + PrintMatchingFuncs(origFuncs, node); + } + } + else + { + str.Format(TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s, str.AddressOf()); + Error(str.AddressOf(), node); + + PrintMatchingFuncs(funcs, node); + } + } +} + +void asCCompiler::CompileDeclaration(asCScriptNode *decl, asCByteCode *bc) +{ + // Get the data type + asCDataType type = builder->CreateDataTypeFromNode(decl->firstChild, script); + + // Declare all variables in this declaration + asCScriptNode *node = decl->firstChild->next; + while( node ) + { + // Is the type allowed? + if( !type.CanBeInstanciated() ) + { + asCString str; + // TODO: Change to "'type' cannot be declared as variable" + str.Format(TXT_DATA_TYPE_CANT_BE_s, type.Format().AddressOf()); + Error(str.AddressOf(), node); + + // Use int instead to avoid further problems + type = asCDataType::CreatePrimitive(ttInt, false); + } + + // Get the name of the identifier + asCString name(&script->code[node->tokenPos], node->tokenLength); + + // Verify that the name isn't used by a dynamic data type + if( engine->GetObjectType(name.AddressOf()) != 0 ) + { + asCString str; + str.Format(TXT_ILLEGAL_VARIABLE_NAME_s, name.AddressOf()); + Error(str.AddressOf(), node); + } + + int offset = AllocateVariable(type, false); + if( variables->DeclareVariable(name.AddressOf(), type, offset) < 0 ) + { + asCString str; + str.Format(TXT_s_ALREADY_DECLARED, name.AddressOf()); + Error(str.AddressOf(), node); + } + + outFunc->AddVariable(name, type, offset); + + // Keep the node for the variable decl + asCScriptNode *varNode = node; + + node = node->next; + if( node && node->nodeType == snArgList ) + { + // Make sure that it is a registered type, and that is isn't a pointer + if( type.GetObjectType() == 0 || type.IsObjectHandle() ) + { + Error(TXT_MUST_BE_OBJECT, node); + } + else + { + // Compile the arguments + asCArray args; + + if( CompileArgumentList(node, args) >= 0 ) + { + // Find all constructors + asCArray funcs; + asSTypeBehaviour *beh = type.GetBehaviour(); + if( beh ) + { + if( type.GetObjectType()->flags & asOBJ_REF ) + funcs = beh->factories; + else + funcs = beh->constructors; + } + + asCString str = type.Format(); + MatchFunctions(funcs, args, node, str.AddressOf()); + + if( funcs.GetLength() == 1 ) + { + sVariable *v = variables->GetVariable(name.AddressOf()); + asSExprContext ctx(engine); + if( v->type.GetObjectType()->flags & asOBJ_REF ) + { + MakeFunctionCall(&ctx, funcs[0], 0, args, node, true, v->stackOffset); + + // Pop the reference left by the function call + ctx.bc.Pop(AS_PTR_SIZE); + } + else + { + ctx.bc.InstrSHORT(asBC_VAR, (short)v->stackOffset); + + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); + + int offset = 0; + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]); + for( asUINT n = 0; n < args.GetLength(); n++ ) + offset += descr->parameterTypes[n].GetSizeOnStackDWords(); + + ctx.bc.InstrWORD(asBC_GETREF, (asWORD)offset); + + PerformFunctionCall(funcs[0], &ctx, true, &args, type.GetObjectType()); + } + bc->AddCode(&ctx.bc); + } + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n],asSExprContext); + } + } + + node = node->next; + } + else if( node && node->nodeType == snInitList ) + { + sVariable *v = variables->GetVariable(name.AddressOf()); + + asCTypeInfo ti; + ti.Set(type); + ti.isVariable = true; + ti.isTemporary = false; + ti.stackOffset = (short)v->stackOffset; + + CompileInitList(&ti, node, bc); + + node = node->next; + } + else if( node && node->nodeType == snAssignment ) + { + asSExprContext ctx(engine); + + // TODO: copy: Here we should look for the best matching constructor, instead of + // just the copy constructor. Only if no appropriate constructor is + // available should the assignment operator be used. + + // Call the default constructor here + CallDefaultConstructor(type, offset, &ctx.bc, varNode); + + // Compile the expression + asSExprContext expr(engine); + int r = CompileAssignment(node, &expr); + if( r >= 0 ) + { + if( type.IsPrimitive() ) + { + if( type.IsReadOnly() && expr.type.isConstant ) + { + ImplicitConversion(&expr, type, node, asIC_IMPLICIT_CONV); + + sVariable *v = variables->GetVariable(name.AddressOf()); + v->isPureConstant = true; + v->constantValue = expr.type.qwordValue; + } + + asSExprContext lctx(engine); + lctx.type.SetVariable(type, offset, false); + lctx.type.dataType.MakeReadOnly(false); + + DoAssignment(&ctx, &lctx, &expr, node, node, ttAssignment, node); + } + else + { + // TODO: We can use a copy constructor here + + asSExprContext lexpr(engine); + lexpr.type.Set(type); + lexpr.type.dataType.MakeReference(true); + // Allow initialization of constant variables + lexpr.type.dataType.MakeReadOnly(false); + + if( type.IsObjectHandle() ) + lexpr.type.isExplicitHandle = true; + + sVariable *v = variables->GetVariable(name.AddressOf()); + lexpr.bc.InstrSHORT(asBC_PSF, (short)v->stackOffset); + lexpr.type.stackOffset = (short)v->stackOffset; + + + // If left expression resolves into a registered type + // check if the assignment operator is overloaded, and check + // the type of the right hand expression. If none is found + // the default action is a direct copy if it is the same type + // and a simple assignment. + bool assigned = false; + if( lexpr.type.dataType.IsObject() && !lexpr.type.isExplicitHandle ) + { + assigned = CompileOverloadedDualOperator(node, &lexpr, &expr, &ctx); + if( assigned ) + { + // Pop the resulting value + ctx.bc.Pop(ctx.type.dataType.GetSizeOnStackDWords()); + + // Release the argument + ProcessDeferredParams(&ctx); + } + } + + if( !assigned ) + { + PrepareForAssignment(&lexpr.type.dataType, &expr, node); + + // If the expression is constant and the variable also is constant + // then mark the variable as pure constant. This will allow the compiler + // to optimize expressions with this variable. + if( v->type.IsReadOnly() && expr.type.isConstant ) + { + v->isPureConstant = true; + v->constantValue = expr.type.qwordValue; + } + + // Add expression code to bytecode + MergeExprContexts(&ctx, &expr); + + // Add byte code for storing value of expression in variable + ctx.bc.AddCode(&lexpr.bc); + lexpr.type.stackOffset = (short)v->stackOffset; + + PerformAssignment(&lexpr.type, &expr.type, &ctx.bc, node->prev); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr.type, &ctx.bc); + + ctx.bc.Pop(expr.type.dataType.GetSizeOnStackDWords()); + + ProcessDeferredParams(&ctx); + } + } + } + + node = node->next; + + bc->AddCode(&ctx.bc); + + // TODO: Can't this leave deferred output params without being compiled? + } + else + { + // Call the default constructor here if no explicit initialization is done + CallDefaultConstructor(type, offset, bc, varNode); + } + } +} + +void asCCompiler::CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByteCode *bc) +{ + // TODO: initlist: Must attempt to find the factory for initialization + // lists, instead checking IsArrayType. + if( var->dataType.IsArrayType() && !var->dataType.IsObjectHandle() ) + { + // Count the number of elements and initialize the array with the correct size + int countElements = 0; + asCScriptNode *el = node->firstChild; + while( el ) + { + countElements++; + el = el->next; + } + + // Construct the array with the size elements + + // Find the constructor that takes an uint + asCArray funcs; + if( var->dataType.GetObjectType()->flags & asOBJ_REF ) + funcs = var->dataType.GetBehaviour()->factories; + else + funcs = var->dataType.GetBehaviour()->constructors; + + asCArray args; + asSExprContext arg1(engine); + arg1.bc.InstrDWORD(asBC_PshC4, countElements); + arg1.type.Set(asCDataType::CreatePrimitive(ttUInt, false)); + args.PushLast(&arg1); + + asCString str = var->dataType.Format(); + MatchFunctions(funcs, args, node, str.AddressOf()); + + if( funcs.GetLength() == 1 ) + { + asSExprContext ctx(engine); + + if( var->dataType.GetObjectType()->flags & asOBJ_REF ) + { + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); + + if( var->isVariable ) + { + // Call factory and store the handle in the given variable + PerformFunctionCall(funcs[0], &ctx, false, &args, 0, true, var->stackOffset); + ctx.bc.Pop(AS_PTR_SIZE); + } + else + { + PerformFunctionCall(funcs[0], &ctx, false, &args); + + // Store the returned handle in the global variable + ctx.bc.Instr(asBC_RDSPTR); + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue()); + ctx.bc.InstrPTR(asBC_REFCPY, var->dataType.GetObjectType()); + ctx.bc.Pop(AS_PTR_SIZE); + ReleaseTemporaryVariable(ctx.type.stackOffset, &ctx.bc); + } + } + else + { + if( var->isVariable ) + ctx.bc.InstrSHORT(asBC_PSF, var->stackOffset); + else + ctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue()); + + PrepareFunctionCall(funcs[0], &ctx.bc, args); + MoveArgsToStack(funcs[0], &ctx.bc, args, false); + + PerformFunctionCall(funcs[0], &ctx, true, &args, var->dataType.GetObjectType()); + } + + bc->AddCode(&ctx.bc); + } + else + return; + + // TODO: initlist: Should we have a special indexing operator for this? + // Find the indexing operator that is not read-only that will be used for all elements + asCDataType retType; + retType = var->dataType.GetSubType(); + retType.MakeReference(true); + retType.MakeReadOnly(false); + int funcId = 0; + asSTypeBehaviour *beh = var->dataType.GetBehaviour(); + for( asUINT n = 0; n < beh->operators.GetLength(); n += 2 ) + { + if( asBEHAVE_INDEX == beh->operators[n] ) + { + asCScriptFunction *desc = builder->GetFunctionDescription(beh->operators[n+1]); + if( !desc->isReadOnly && + desc->parameterTypes.GetLength() == 1 && + (desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttUInt, false) || + desc->parameterTypes[0] == asCDataType::CreatePrimitive(ttInt, false)) && + desc->returnType == retType ) + { + funcId = beh->operators[n+1]; + break; + } + } + } + + if( funcId == 0 ) + { + Error(TXT_NO_APPROPRIATE_INDEX_OPERATOR, node); + return; + } + + asUINT index = 0; + el = node->firstChild; + while( el ) + { + if( el->nodeType == snAssignment || el->nodeType == snInitList ) + { + asSExprContext lctx(engine); + asSExprContext rctx(engine); + + if( el->nodeType == snAssignment ) + { + // Compile the assignment expression + CompileAssignment(el, &rctx); + } + else if( el->nodeType == snInitList ) + { + int offset = AllocateVariable(var->dataType.GetSubType(), true); + + rctx.type.Set(var->dataType.GetSubType()); + rctx.type.isVariable = true; + rctx.type.isTemporary = true; + rctx.type.stackOffset = (short)offset; + + CompileInitList(&rctx.type, el, &rctx.bc); + + // Put the object on the stack + rctx.bc.InstrSHORT(asBC_PSF, rctx.type.stackOffset); + + // It is a reference that we place on the stack + rctx.type.dataType.MakeReference(true); + } + + // Compile the lvalue + lctx.bc.InstrDWORD(asBC_PshC4, index); + if( var->isVariable ) + lctx.bc.InstrSHORT(asBC_PSF, var->stackOffset); + else + lctx.bc.InstrPTR(asBC_PGA, engine->globalProperties[var->stackOffset]->GetAddressOfValue()); + lctx.bc.Instr(asBC_RDSPTR); + lctx.bc.Call(asBC_CALLSYS, funcId, 1+AS_PTR_SIZE); + + if( !var->dataType.GetSubType().IsPrimitive() ) + lctx.bc.Instr(asBC_PshRPtr); + + lctx.type.Set(var->dataType.GetSubType()); + + if( !lctx.type.dataType.IsObject() || lctx.type.dataType.IsObjectHandle() ) + lctx.type.dataType.MakeReference(true); + + // If the element type is handles, then we're expected to do handle assignments + if( lctx.type.dataType.IsObjectHandle() ) + lctx.type.isExplicitHandle = true; + + asSExprContext ctx(engine); + DoAssignment(&ctx, &lctx, &rctx, el, el, ttAssignment, el); + + if( !lctx.type.dataType.IsPrimitive() ) + ctx.bc.Pop(AS_PTR_SIZE); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(ctx.type, &ctx.bc); + + ProcessDeferredParams(&ctx); + + bc->AddCode(&ctx.bc); + } + + el = el->next; + index++; + } + } + else + { + asCString str; + str.Format(TXT_INIT_LIST_CANNOT_BE_USED_WITH_s, var->dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + } +} + +void asCCompiler::CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc) +{ + *hasReturn = false; + + if( statement->nodeType == snStatementBlock ) + CompileStatementBlock(statement, true, hasReturn, bc); + else if( statement->nodeType == snIf ) + CompileIfStatement(statement, hasReturn, bc); + else if( statement->nodeType == snFor ) + CompileForStatement(statement, bc); + else if( statement->nodeType == snWhile ) + CompileWhileStatement(statement, bc); + else if( statement->nodeType == snDoWhile ) + CompileDoWhileStatement(statement, bc); + else if( statement->nodeType == snExpressionStatement ) + CompileExpressionStatement(statement, bc); + else if( statement->nodeType == snBreak ) + CompileBreakStatement(statement, bc); + else if( statement->nodeType == snContinue ) + CompileContinueStatement(statement, bc); + else if( statement->nodeType == snSwitch ) + CompileSwitchStatement(statement, hasReturn, bc); + else if( statement->nodeType == snReturn ) + { + CompileReturnStatement(statement, bc); + *hasReturn = true; + } +} + +void asCCompiler::CompileSwitchStatement(asCScriptNode *snode, bool *, asCByteCode *bc) +{ + // TODO: inheritance: Must guarantee that all options in the switch case call a constructor, or that none call it. + + // Reserve label for break statements + int breakLabel = nextLabel++; + breakLabels.PushLast(breakLabel); + + // Add a variable scope that will be used by CompileBreak + // to know where to stop deallocating variables + AddVariableScope(true, false); + + //--------------------------- + // Compile the switch expression + //------------------------------- + + // Compile the switch expression + asSExprContext expr(engine); + CompileAssignment(snode->firstChild, &expr); + + // Verify that the expression is a primitive type + if( !expr.type.dataType.IsIntegerType() && !expr.type.dataType.IsUnsignedType() && !expr.type.dataType.IsEnumType() ) + { + Error(TXT_SWITCH_MUST_BE_INTEGRAL, snode->firstChild); + return; + } + + // TODO: Need to support 64bit + // Convert the expression to a 32bit variable + asCDataType to; + if( expr.type.dataType.IsIntegerType() || expr.type.dataType.IsEnumType() ) + to.SetTokenType(ttInt); + else if( expr.type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt); + ImplicitConversion(&expr, to, snode->firstChild, asIC_IMPLICIT_CONV, true); + + ConvertToVariable(&expr); + int offset = expr.type.stackOffset; + + //------------------------------- + // Determine case values and labels + //-------------------------------- + + // Remember the first label so that we can later pass the + // correct label to each CompileCase() + int firstCaseLabel = nextLabel; + int defaultLabel = 0; + + asCArray caseValues; + asCArray caseLabels; + + // Compile all case comparisons and make them jump to the right label + asCScriptNode *cnode = snode->firstChild->next; + while( cnode ) + { + // Each case should have a constant expression + if( cnode->firstChild && cnode->firstChild->nodeType == snExpression ) + { + // Compile expression + asSExprContext c(engine); + CompileExpression(cnode->firstChild, &c); + + // Verify that the result is a constant + if( !c.type.isConstant ) + Error(TXT_SWITCH_CASE_MUST_BE_CONSTANT, cnode->firstChild); + + // Verify that the result is an integral number + if( !c.type.dataType.IsIntegerType() && !c.type.dataType.IsUnsignedType() && !c.type.dataType.IsEnumType() ) + Error(TXT_SWITCH_MUST_BE_INTEGRAL, cnode->firstChild); + + ImplicitConversion(&c, to, cnode->firstChild, asIC_IMPLICIT_CONV, true); + + // Has this case been declared already? + if( caseValues.IndexOf(c.type.intValue) >= 0 ) + { + Error(TXT_DUPLICATE_SWITCH_CASE, cnode->firstChild); + } + + // TODO: Optimize: We can insert the numbers sorted already + + // Store constant for later use + caseValues.PushLast(c.type.intValue); + + // Reserve label for this case + caseLabels.PushLast(nextLabel++); + } + else + { + // Is default the last case? + if( cnode->next ) + { + Error(TXT_DEFAULT_MUST_BE_LAST, cnode); + break; + } + + // Reserve label for this case + defaultLabel = nextLabel++; + } + + cnode = cnode->next; + } + + // check for empty switch + if (caseValues.GetLength() == 0) + { + Error(TXT_EMPTY_SWITCH, snode); + return; + } + + if( defaultLabel == 0 ) + defaultLabel = breakLabel; + + //--------------------------------- + // Output the optimized case comparisons + // with jumps to the case code + //------------------------------------ + + // Sort the case values by increasing value. Do the sort together with the labels + // A simple bubble sort is sufficient since we don't expect a huge number of values + for( asUINT fwd = 1; fwd < caseValues.GetLength(); fwd++ ) + { + for( int bck = fwd - 1; bck >= 0; bck-- ) + { + int bckp = bck + 1; + if( caseValues[bck] > caseValues[bckp] ) + { + // Swap the values in both arrays + int swap = caseValues[bckp]; + caseValues[bckp] = caseValues[bck]; + caseValues[bck] = swap; + + swap = caseLabels[bckp]; + caseLabels[bckp] = caseLabels[bck]; + caseLabels[bck] = swap; + } + else + break; + } + } + + // Find ranges of consecutive numbers + asCArray ranges; + ranges.PushLast(0); + asUINT n; + for( n = 1; n < caseValues.GetLength(); ++n ) + { + // We can join numbers that are less than 5 numbers + // apart since the output code will still be smaller + if( caseValues[n] > caseValues[n-1] + 5 ) + ranges.PushLast(n); + } + + // If the value is larger than the largest case value, jump to default + int tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[caseValues.GetLength()-1]); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JP, defaultLabel); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + + // TODO: optimize: We could possibly optimize this even more by doing a + // binary search instead of a linear search through the ranges + + // For each range + int range; + for( range = 0; range < (int)ranges.GetLength(); range++ ) + { + // Find the largest value in this range + int maxRange = caseValues[ranges[range]]; + int index = ranges[range]; + for( ; (index < (int)caseValues.GetLength()) && (caseValues[index] <= maxRange + 5); index++ ) + maxRange = caseValues[index]; + + // If there are only 2 numbers then it is better to compare them directly + if( index - ranges[range] > 2 ) + { + // If the value is smaller than the smallest case value in the range, jump to default + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JS, defaultLabel); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + + int nextRangeLabel = nextLabel++; + // If this is the last range we don't have to make this test + if( range < (int)ranges.GetLength() - 1 ) + { + // If the value is larger than the largest case value in the range, jump to the next range + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, maxRange); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JP, nextRangeLabel); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + } + + // Jump forward according to the value + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[ranges[range]]); + expr.bc.InstrW_W_W(asBC_SUBi, tmpOffset, offset, tmpOffset); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + expr.bc.JmpP(tmpOffset, maxRange - caseValues[ranges[range]]); + + // Add the list of jumps to the correct labels (any holes, jump to default) + index = ranges[range]; + for( int n = caseValues[index]; n <= maxRange; n++ ) + { + if( caseValues[index] == n ) + expr.bc.InstrINT(asBC_JMP, caseLabels[index++]); + else + expr.bc.InstrINT(asBC_JMP, defaultLabel); + } + + expr.bc.Label((short)nextRangeLabel); + } + else + { + // Simply make a comparison with each value + int n; + for( n = ranges[range]; n < index; ++n ) + { + tmpOffset = AllocateVariable(asCDataType::CreatePrimitive(ttInt, false), true); + expr.bc.InstrSHORT_DW(asBC_SetV4, (short)tmpOffset, caseValues[n]); + expr.bc.InstrW_W(asBC_CMPi, offset, tmpOffset); + expr.bc.InstrDWORD(asBC_JZ, caseLabels[n]); + ReleaseTemporaryVariable(tmpOffset, &expr.bc); + } + } + } + + // Catch any value that falls trough + expr.bc.InstrINT(asBC_JMP, defaultLabel); + + // Release the temporary variable previously stored + ReleaseTemporaryVariable(expr.type, &expr.bc); + + //---------------------------------- + // Output case implementations + //---------------------------------- + + // Compile case implementations, each one with the label before it + cnode = snode->firstChild->next; + while( cnode ) + { + // Each case should have a constant expression + if( cnode->firstChild && cnode->firstChild->nodeType == snExpression ) + { + expr.bc.Label((short)firstCaseLabel++); + + CompileCase(cnode->firstChild->next, &expr.bc); + } + else + { + expr.bc.Label((short)defaultLabel); + + // Is default the last case? + if( cnode->next ) + { + // We've already reported this error + break; + } + + CompileCase(cnode->firstChild, &expr.bc); + } + + cnode = cnode->next; + } + + //-------------------------------- + + bc->AddCode(&expr.bc); + + // Add break label + bc->Label((short)breakLabel); + + breakLabels.PopLast(); + RemoveVariableScope(); +} + +void asCCompiler::CompileCase(asCScriptNode *node, asCByteCode *bc) +{ + bool isFinished = false; + bool hasReturn = false; + while( node ) + { + if( hasReturn || isFinished ) + { + Warning(TXT_UNREACHABLE_CODE, node); + break; + } + + if( node->nodeType == snBreak || node->nodeType == snContinue ) + isFinished = true; + + asCByteCode statement(engine); + CompileStatement(node, &hasReturn, &statement); + + LineInstr(bc, node->tokenPos); + bc->AddCode(&statement); + + if( !hasCompileErrors ) + asASSERT( tempVariables.GetLength() == 0 ); + + node = node->next; + } +} + +void asCCompiler::CompileIfStatement(asCScriptNode *inode, bool *hasReturn, asCByteCode *bc) +{ + // We will use one label for the if statement + // and possibly another for the else statement + int afterLabel = nextLabel++; + + // Compile the expression + asSExprContext expr(engine); + CompileAssignment(inode->firstChild, &expr); + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + { + Error(TXT_EXPR_MUST_BE_BOOL, inode->firstChild); + expr.type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 1); + } + + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + if( !expr.type.isConstant ) + { + ProcessPropertyGetAccessor(&expr, inode); + + ConvertToVariable(&expr); + + // Add byte code from the expression + bc->AddCode(&expr.bc); + + // Add a test + bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + bc->Instr(asBC_ClrHi); + bc->InstrDWORD(asBC_JZ, afterLabel); + ReleaseTemporaryVariable(expr.type, bc); + } + else if( expr.type.dwordValue == 0 ) + { + // Jump to the else case + bc->InstrINT(asBC_JMP, afterLabel); + + // TODO: Should we warn? + } + + // Compile the if statement + bool origIsConstructorCalled = m_isConstructorCalled; + + bool hasReturn1; + asCByteCode ifBC(engine); + CompileStatement(inode->firstChild->next, &hasReturn1, &ifBC); + + // Add the byte code + LineInstr(bc, inode->firstChild->next->tokenPos); + bc->AddCode(&ifBC); + + if( inode->firstChild->next->nodeType == snExpressionStatement && inode->firstChild->next->firstChild == 0 ) + { + // Don't allow if( expr ); + Error(TXT_IF_WITH_EMPTY_STATEMENT, inode->firstChild->next); + } + + // If one of the statements call the constructor, the other must as well + // otherwise it is possible the constructor is never called + bool constructorCall1 = false; + bool constructorCall2 = false; + if( !origIsConstructorCalled && m_isConstructorCalled ) + constructorCall1 = true; + + // Do we have an else statement? + if( inode->firstChild->next != inode->lastChild ) + { + // Reset the constructor called flag so the else statement can call the constructor too + m_isConstructorCalled = origIsConstructorCalled; + + int afterElse = 0; + if( !hasReturn1 ) + { + afterElse = nextLabel++; + + // Add jump to after the else statement + bc->InstrINT(asBC_JMP, afterElse); + } + + // Add label for the else statement + bc->Label((short)afterLabel); + + bool hasReturn2; + asCByteCode elseBC(engine); + CompileStatement(inode->lastChild, &hasReturn2, &elseBC); + + // Add byte code for the else statement + LineInstr(bc, inode->lastChild->tokenPos); + bc->AddCode(&elseBC); + + if( inode->lastChild->nodeType == snExpressionStatement && inode->lastChild->firstChild == 0 ) + { + // Don't allow if( expr ) {} else; + Error(TXT_ELSE_WITH_EMPTY_STATEMENT, inode->lastChild); + } + + if( !hasReturn1 ) + { + // Add label for the end of else statement + bc->Label((short)afterElse); + } + + // The if statement only has return if both alternatives have + *hasReturn = hasReturn1 && hasReturn2; + + if( !origIsConstructorCalled && m_isConstructorCalled ) + constructorCall2 = true; + } + else + { + // Add label for the end of if statement + bc->Label((short)afterLabel); + *hasReturn = false; + } + + // Make sure both or neither conditions call a constructor + if( (constructorCall1 && !constructorCall2) || + (constructorCall2 && !constructorCall1) ) + { + Error(TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR, inode); + } + + m_isConstructorCalled = origIsConstructorCalled || constructorCall1 || constructorCall2; +} + +void asCCompiler::CompileForStatement(asCScriptNode *fnode, asCByteCode *bc) +{ + // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables + AddVariableScope(true, true); + + // We will use three labels for the for loop + int beforeLabel = nextLabel++; + int afterLabel = nextLabel++; + int continueLabel = nextLabel++; + + continueLabels.PushLast(continueLabel); + breakLabels.PushLast(afterLabel); + + //--------------------------------------- + // Compile the initialization statement + asCByteCode initBC(engine); + if( fnode->firstChild->nodeType == snDeclaration ) + CompileDeclaration(fnode->firstChild, &initBC); + else + CompileExpressionStatement(fnode->firstChild, &initBC); + + //----------------------------------- + // Compile the condition statement + asSExprContext expr(engine); + asCScriptNode *second = fnode->firstChild->next; + if( second->firstChild ) + { + int r = CompileAssignment(second->firstChild, &expr); + if( r >= 0 ) + { + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, second); + else + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + // If expression is false exit the loop + ConvertToVariable(&expr); + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + expr.bc.Instr(asBC_ClrHi); + expr.bc.InstrDWORD(asBC_JZ, afterLabel); + ReleaseTemporaryVariable(expr.type, &expr.bc); + } + } + } + + //--------------------------- + // Compile the increment statement + asCByteCode nextBC(engine); + asCScriptNode *third = second->next; + if( third->nodeType == snExpressionStatement ) + CompileExpressionStatement(third, &nextBC); + + //------------------------------ + // Compile loop statement + bool hasReturn; + asCByteCode forBC(engine); + CompileStatement(fnode->lastChild, &hasReturn, &forBC); + + //------------------------------- + // Join the code pieces + bc->AddCode(&initBC); + bc->Label((short)beforeLabel); + + // Add a suspend bytecode inside the loop to guarantee + // that the application can suspend the execution + bc->Instr(asBC_SUSPEND); + bc->InstrWORD(asBC_JitEntry, 0); + + + bc->AddCode(&expr.bc); + LineInstr(bc, fnode->lastChild->tokenPos); + bc->AddCode(&forBC); + bc->Label((short)continueLabel); + bc->AddCode(&nextBC); + bc->InstrINT(asBC_JMP, beforeLabel); + bc->Label((short)afterLabel); + + continueLabels.PopLast(); + breakLabels.PopLast(); + + // Deallocate variables in this block, in reverse order + for( int n = (int)variables->variables.GetLength() - 1; n >= 0; n-- ) + { + sVariable *v = variables->variables[n]; + + // Call variable destructors here, for variables not yet destroyed + CallDestructor(v->type, v->stackOffset, bc); + + // Don't deallocate function parameters + if( v->stackOffset > 0 ) + DeallocateVariable(v->stackOffset); + } + + RemoveVariableScope(); +} + +void asCCompiler::CompileWhileStatement(asCScriptNode *wnode, asCByteCode *bc) +{ + // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables + AddVariableScope(true, true); + + // We will use two labels for the while loop + int beforeLabel = nextLabel++; + int afterLabel = nextLabel++; + + continueLabels.PushLast(beforeLabel); + breakLabels.PushLast(afterLabel); + + // Add label before the expression + bc->Label((short)beforeLabel); + + // Compile expression + asSExprContext expr(engine); + CompileAssignment(wnode->firstChild, &expr); + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild); + + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + // Add byte code for the expression + ConvertToVariable(&expr); + bc->AddCode(&expr.bc); + + // Jump to end of statement if expression is false + bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + bc->Instr(asBC_ClrHi); + bc->InstrDWORD(asBC_JZ, afterLabel); + ReleaseTemporaryVariable(expr.type, bc); + + // Add a suspend bytecode inside the loop to guarantee + // that the application can suspend the execution + bc->Instr(asBC_SUSPEND); + bc->InstrWORD(asBC_JitEntry, 0); + + // Compile statement + bool hasReturn; + asCByteCode whileBC(engine); + CompileStatement(wnode->lastChild, &hasReturn, &whileBC); + + // Add byte code for the statement + LineInstr(bc, wnode->lastChild->tokenPos); + bc->AddCode(&whileBC); + + // Jump to the expression + bc->InstrINT(asBC_JMP, beforeLabel); + + // Add label after the statement + bc->Label((short)afterLabel); + + continueLabels.PopLast(); + breakLabels.PopLast(); + + RemoveVariableScope(); +} + +void asCCompiler::CompileDoWhileStatement(asCScriptNode *wnode, asCByteCode *bc) +{ + // Add a variable scope that will be used by CompileBreak/Continue to know where to stop deallocating variables + AddVariableScope(true, true); + + // We will use two labels for the while loop + int beforeLabel = nextLabel++; + int beforeTest = nextLabel++; + int afterLabel = nextLabel++; + + continueLabels.PushLast(beforeTest); + breakLabels.PushLast(afterLabel); + + // Add label before the statement + bc->Label((short)beforeLabel); + + // Compile statement + bool hasReturn; + asCByteCode whileBC(engine); + CompileStatement(wnode->firstChild, &hasReturn, &whileBC); + + // Add byte code for the statement + LineInstr(bc, wnode->firstChild->tokenPos); + bc->AddCode(&whileBC); + + // Add label before the expression + bc->Label((short)beforeTest); + + // Add a suspend bytecode inside the loop to guarantee + // that the application can suspend the execution + bc->Instr(asBC_SUSPEND); + bc->InstrWORD(asBC_JitEntry, 0); + + // Add a line instruction + LineInstr(bc, wnode->lastChild->tokenPos); + + // Compile expression + asSExprContext expr(engine); + CompileAssignment(wnode->lastChild, &expr); + if( !expr.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + Error(TXT_EXPR_MUST_BE_BOOL, wnode->firstChild); + + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + ProcessDeferredParams(&expr); + + // Add byte code for the expression + ConvertToVariable(&expr); + bc->AddCode(&expr.bc); + + // Jump to next iteration if expression is true + bc->InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + bc->Instr(asBC_ClrHi); + bc->InstrDWORD(asBC_JNZ, beforeLabel); + ReleaseTemporaryVariable(expr.type, bc); + + // Add label after the statement + bc->Label((short)afterLabel); + + continueLabels.PopLast(); + breakLabels.PopLast(); + + RemoveVariableScope(); +} + +void asCCompiler::CompileBreakStatement(asCScriptNode *node, asCByteCode *bc) +{ + if( breakLabels.GetLength() == 0 ) + { + Error(TXT_INVALID_BREAK, node); + return; + } + + // Add destructor calls for all variables that will go out of scope + asCVariableScope *vs = variables; + while( !vs->isBreakScope ) + { + for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- ) + CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc); + + vs = vs->parent; + } + + bc->InstrINT(asBC_JMP, breakLabels[breakLabels.GetLength()-1]); +} + +void asCCompiler::CompileContinueStatement(asCScriptNode *node, asCByteCode *bc) +{ + if( continueLabels.GetLength() == 0 ) + { + Error(TXT_INVALID_CONTINUE, node); + return; + } + + // Add destructor calls for all variables that will go out of scope + asCVariableScope *vs = variables; + while( !vs->isContinueScope ) + { + for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- ) + CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc); + + vs = vs->parent; + } + + bc->InstrINT(asBC_JMP, continueLabels[continueLabels.GetLength()-1]); +} + +void asCCompiler::CompileExpressionStatement(asCScriptNode *enode, asCByteCode *bc) +{ + if( enode->firstChild ) + { + // Compile the expression + asSExprContext expr(engine); + CompileAssignment(enode->firstChild, &expr); + + // Pop the value from the stack + if( !expr.type.dataType.IsPrimitive() ) + expr.bc.Pop(expr.type.dataType.GetSizeOnStackDWords()); + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr.type, &expr.bc); + + ProcessDeferredParams(&expr); + + bc->AddCode(&expr.bc); + } +} + +void asCCompiler::PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ctx, asCArray *reservedVars) +{ + // If the object already is stored in temporary variable then nothing needs to be done + if( ctx->type.isTemporary ) return; + + // Allocate temporary variable + asCDataType dt = ctx->type.dataType; + dt.MakeReference(false); + dt.MakeReadOnly(false); + + int offset = AllocateVariableNotIn(dt, true, reservedVars); + + asCTypeInfo lvalue; + dt.MakeReference(true); + lvalue.Set(dt); + lvalue.isTemporary = true; + lvalue.stackOffset = (short)offset; + lvalue.isVariable = true; + lvalue.isExplicitHandle = ctx->type.isExplicitHandle; + + if( !dt.IsObjectHandle() && dt.GetObjectType() && (dt.GetBehaviour()->copyconstruct || dt.GetBehaviour()->copyfactory) ) + { + // Use the copy constructor/factory when available + CallCopyConstructor(dt, offset, &ctx->bc, ctx, node); + } + else + { + // Allocate and construct the temporary object + CallDefaultConstructor(dt, offset, &ctx->bc, node); + + // Assign the object to the temporary variable + PrepareForAssignment(&lvalue.dataType, ctx, node); + + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + PerformAssignment(&lvalue, &ctx->type, &ctx->bc, node); + + // Pop the original reference + ctx->bc.Pop(AS_PTR_SIZE); + } + + // Push the reference to the temporary variable on the stack + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + lvalue.dataType.MakeReference(true); + + ctx->type = lvalue; +} + +void asCCompiler::CompileReturnStatement(asCScriptNode *rnode, asCByteCode *bc) +{ + // Get return type and location + sVariable *v = variables->GetVariable("return"); + if( v->type.GetSizeOnStackDWords() > 0 ) + { + // Is there an expression? + if( rnode->firstChild ) + { + // Compile the expression + asSExprContext expr(engine); + int r = CompileAssignment(rnode->firstChild, &expr); + if( r >= 0 ) + { + // Prepare the value for assignment + IsVariableInitialized(&expr.type, rnode->firstChild); + + if( v->type.IsPrimitive() ) + { + if( expr.type.dataType.IsReference() ) ConvertToVariable(&expr); + + // Implicitly convert the value to the return type + ImplicitConversion(&expr, v->type, rnode->firstChild, asIC_IMPLICIT_CONV); + + // Verify that the conversion was successful + if( expr.type.dataType != v->type ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, expr.type.dataType.Format().AddressOf(), v->type.Format().AddressOf()); + Error(str.AddressOf(), rnode); + r = -1; + } + else + { + ConvertToVariable(&expr); + ReleaseTemporaryVariable(expr.type, &expr.bc); + + // Load the variable in the register + if( v->type.GetSizeOnStackDWords() == 1 ) + expr.bc.InstrSHORT(asBC_CpyVtoR4, expr.type.stackOffset); + else + expr.bc.InstrSHORT(asBC_CpyVtoR8, expr.type.stackOffset); + } + } + else if( v->type.IsObject() ) + { + PrepareArgument(&v->type, &expr, rnode->firstChild); + + // Pop the reference to the temporary variable again + expr.bc.Pop(AS_PTR_SIZE); + + // Load the object pointer into the object register + // LOADOBJ also clears the address in the variable + expr.bc.InstrSHORT(asBC_LOADOBJ, expr.type.stackOffset); + + // LOADOBJ cleared the address in the variable so the object will not be freed + // here, but the temporary variable must still be freed + + // TODO: optimize: Since there is nothing in the variable anymore, + // there is no need to call asBC_FREE on it. + } + + // Release temporary variables used by expression + ReleaseTemporaryVariable(expr.type, &expr.bc); + + bc->AddCode(&expr.bc); + } + } + else + Error(TXT_MUST_RETURN_VALUE, rnode); + } + else + if( rnode->firstChild ) + Error(TXT_CANT_RETURN_VALUE, rnode); + + // Call destructor on all variables except for the function parameters + asCVariableScope *vs = variables; + while( vs ) + { + for( int n = (int)vs->variables.GetLength() - 1; n >= 0; n-- ) + if( vs->variables[n]->stackOffset > 0 ) + CallDestructor(vs->variables[n]->type, vs->variables[n]->stackOffset, bc); + + vs = vs->parent; + } + + // Jump to the end of the function + bc->InstrINT(asBC_JMP, 0); +} + +void asCCompiler::AddVariableScope(bool isBreakScope, bool isContinueScope) +{ + variables = asNEW(asCVariableScope)(variables); + variables->isBreakScope = isBreakScope; + variables->isContinueScope = isContinueScope; +} + +void asCCompiler::RemoveVariableScope() +{ + if( variables ) + { + asCVariableScope *var = variables; + variables = variables->parent; + asDELETE(var,asCVariableScope); + } +} + +void asCCompiler::Error(const char *msg, asCScriptNode *node) +{ + asCString str; + + int r, c; + script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + builder->WriteError(script->name.AddressOf(), msg, r, c); + + hasCompileErrors = true; +} + +void asCCompiler::Warning(const char *msg, asCScriptNode *node) +{ + asCString str; + + int r, c; + script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + builder->WriteWarning(script->name.AddressOf(), msg, r, c); +} + +void asCCompiler::PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node) +{ + int r, c; + script->ConvertPosToRowCol(node->tokenPos, &r, &c); + + for( unsigned int n = 0; n < funcs.GetLength(); n++ ) + { + asIScriptFunction *func = engine->scriptFunctions[funcs[n]]; + + builder->WriteInfo(script->name.AddressOf(), func->GetDeclaration(true), r, c, false); + } +} + +int asCCompiler::AllocateVariable(const asCDataType &type, bool isTemporary) +{ + return AllocateVariableNotIn(type, isTemporary, 0); +} + +int asCCompiler::AllocateVariableNotIn(const asCDataType &type, bool isTemporary, asCArray *vars) +{ + asCDataType t(type); + + if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 1 ) + t.SetTokenType(ttInt); + + if( t.IsPrimitive() && t.GetSizeOnStackDWords() == 2 ) + t.SetTokenType(ttDouble); + + // Find a free location with the same type + for( asUINT n = 0; n < freeVariables.GetLength(); n++ ) + { + int slot = freeVariables[n]; + if( variableAllocations[slot].IsEqualExceptConst(t) && variableIsTemporary[slot] == isTemporary ) + { + // We can't return by slot, must count variable sizes + int offset = GetVariableOffset(slot); + + // Verify that it is not in the list of used variables + bool isUsed = false; + if( vars ) + { + for( asUINT m = 0; m < vars->GetLength(); m++ ) + { + if( offset == (*vars)[m] ) + { + isUsed = true; + break; + } + } + } + + if( !isUsed ) + { + if( n != freeVariables.GetLength() - 1 ) + freeVariables[n] = freeVariables.PopLast(); + else + freeVariables.PopLast(); + + if( isTemporary ) + tempVariables.PushLast(offset); + + return offset; + } + } + } + + variableAllocations.PushLast(t); + variableIsTemporary.PushLast(isTemporary); + + int offset = GetVariableOffset((int)variableAllocations.GetLength()-1); + + if( isTemporary ) + tempVariables.PushLast(offset); + + return offset; +} + +int asCCompiler::GetVariableOffset(int varIndex) +{ + // Return offset to the last dword on the stack + int varOffset = 1; + for( int n = 0; n < varIndex; n++ ) + varOffset += variableAllocations[n].GetSizeOnStackDWords(); + + if( varIndex < (int)variableAllocations.GetLength() ) + { + int size = variableAllocations[varIndex].GetSizeOnStackDWords(); + if( size > 1 ) + varOffset += size-1; + } + + return varOffset; +} + +int asCCompiler::GetVariableSlot(int offset) +{ + int varOffset = 1; + for( asUINT n = 0; n < variableAllocations.GetLength(); n++ ) + { + varOffset += -1 + variableAllocations[n].GetSizeOnStackDWords(); + if( varOffset == offset ) + { + return n; + } + varOffset++; + } + + return -1; +} + +void asCCompiler::DeallocateVariable(int offset) +{ + // Remove temporary variable + int n; + for( n = 0; n < (int)tempVariables.GetLength(); n++ ) + { + if( offset == tempVariables[n] ) + { + if( n == (int)tempVariables.GetLength()-1 ) + tempVariables.PopLast(); + else + tempVariables[n] = tempVariables.PopLast(); + break; + } + } + + n = GetVariableSlot(offset); + if( n != -1 ) + { + freeVariables.PushLast(n); + return; + } + + // We might get here if the variable was implicitly declared + // because it was use before a formal declaration, in this case + // the offset is 0x7FFF + + asASSERT(offset == 0x7FFF); +} + +void asCCompiler::ReleaseTemporaryVariable(asCTypeInfo &t, asCByteCode *bc) +{ + if( t.isTemporary ) + { + if( bc ) + { + // We need to call the destructor on the true variable type + int n = GetVariableSlot(t.stackOffset); + asCDataType dt = variableAllocations[n]; + + // Call destructor + CallDestructor(dt, t.stackOffset, bc); + } + + DeallocateVariable(t.stackOffset); + t.isTemporary = false; + } +} + +void asCCompiler::ReleaseTemporaryVariable(int offset, asCByteCode *bc) +{ + if( bc ) + { + // We need to call the destructor on the true variable type + int n = GetVariableSlot(offset); + asCDataType dt = variableAllocations[n]; + + // Call destructor + CallDestructor(dt, offset, bc); + } + + DeallocateVariable(offset); +} + +void asCCompiler::Dereference(asSExprContext *ctx, bool generateCode) +{ + if( ctx->type.dataType.IsReference() ) + { + if( ctx->type.dataType.IsObject() ) + { + ctx->type.dataType.MakeReference(false); + if( generateCode ) + { + ctx->bc.Instr(asBC_CHKREF); + ctx->bc.Instr(asBC_RDSPTR); + } + } + else + { + // This should never happen as primitives are treated differently + asASSERT(false); + } + } +} + + +bool asCCompiler::IsVariableInitialized(asCTypeInfo *type, asCScriptNode *node) +{ + // Temporary variables are assumed to be initialized + if( type->isTemporary ) return true; + + // Verify that it is a variable + if( !type->isVariable ) return true; + + // Find the variable + sVariable *v = variables->GetVariableByOffset(type->stackOffset); + + // The variable isn't found if it is a constant, in which case it is guaranteed to be initialized + if( v == 0 ) return true; + + if( v->isInitialized ) return true; + + // Complex types don't need this test + if( v->type.IsObject() ) return true; + + // Mark as initialized so that the user will not be bothered again + v->isInitialized = true; + + // Write warning + asCString str; + str.Format(TXT_s_NOT_INITIALIZED, (const char *)v->name.AddressOf()); + Warning(str.AddressOf(), node); + + return false; +} + +void asCCompiler::PrepareOperand(asSExprContext *ctx, asCScriptNode *node) +{ + // Check if the variable is initialized (if it indeed is a variable) + IsVariableInitialized(&ctx->type, node); + + asCDataType to = ctx->type.dataType; + to.MakeReference(false); + + ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV); + + ProcessDeferredParams(ctx); +} + +void asCCompiler::PrepareForAssignment(asCDataType *lvalue, asSExprContext *rctx, asCScriptNode *node, asSExprContext *lvalueExpr) +{ + ProcessPropertyGetAccessor(rctx, node); + + // Make sure the rvalue is initialized if it is a variable + IsVariableInitialized(&rctx->type, node); + + if( lvalue->IsPrimitive() ) + { + if( rctx->type.dataType.IsPrimitive() ) + { + if( rctx->type.dataType.IsReference() ) + { + // Cannot do implicit conversion of references so we first convert the reference to a variable + ConvertToVariableNotIn(rctx, lvalueExpr); + } + } + + // Implicitly convert the value to the right type + asCArray usedVars; + if( lvalueExpr ) lvalueExpr->bc.GetVarsUsed(usedVars); + ImplicitConversion(rctx, *lvalue, node, asIC_IMPLICIT_CONV, true, &usedVars); + + // Check data type + if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf()); + Error(str.AddressOf(), node); + + rctx->type.SetDummy(); + } + + // Make sure the rvalue is a variable + if( !rctx->type.isVariable ) + ConvertToVariableNotIn(rctx, lvalueExpr); + } + else + { + asCDataType to = *lvalue; + to.MakeReference(false); + + // TODO: ImplicitConversion should know to do this by itself + // First convert to a handle which will to a reference cast + if( !lvalue->IsObjectHandle() && + (lvalue->GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) ) + to.MakeHandle(true); + + // Don't allow the implicit conversion to create an object + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, 0, false); + + if( !lvalue->IsObjectHandle() && + (lvalue->GetObjectType()->flags & asOBJ_SCRIPT_OBJECT) ) + { + // Then convert to a reference, which will validate the handle + to.MakeHandle(false); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, 0, false); + } + + // Check data type + if( !lvalue->IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lvalue->Format().AddressOf()); + Error(str.AddressOf(), node); + } + else + { + // If the assignment will be made with the copy behaviour then the rvalue must not be a reference + if( lvalue->IsObject() ) + asASSERT(!rctx->type.dataType.IsReference()); + } + } +} + +bool asCCompiler::IsLValue(asCTypeInfo &type) +{ + if( type.dataType.IsReadOnly() ) return false; + if( !type.dataType.IsObject() && !type.isVariable && !type.dataType.IsReference() ) return false; + if( type.isTemporary ) return false; + return true; +} + +void asCCompiler::PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asCByteCode *bc, asCScriptNode *node) +{ + if( lvalue->dataType.IsReadOnly() ) + Error(TXT_REF_IS_READ_ONLY, node); + + if( lvalue->dataType.IsPrimitive() ) + { + if( lvalue->isVariable ) + { + // Copy the value between the variables directly + if( lvalue->dataType.GetSizeInMemoryDWords() == 1 ) + bc->InstrW_W(asBC_CpyVtoV4, lvalue->stackOffset, rvalue->stackOffset); + else + bc->InstrW_W(asBC_CpyVtoV8, lvalue->stackOffset, rvalue->stackOffset); + + // Mark variable as initialized + sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset); + if( v ) v->isInitialized = true; + } + else if( lvalue->dataType.IsReference() ) + { + // Copy the value of the variable to the reference in the register + int s = lvalue->dataType.GetSizeInMemoryBytes(); + if( s == 1 ) + bc->InstrSHORT(asBC_WRTV1, rvalue->stackOffset); + else if( s == 2 ) + bc->InstrSHORT(asBC_WRTV2, rvalue->stackOffset); + else if( s == 4 ) + bc->InstrSHORT(asBC_WRTV4, rvalue->stackOffset); + else if( s == 8 ) + bc->InstrSHORT(asBC_WRTV8, rvalue->stackOffset); + } + else + { + Error(TXT_NOT_VALID_LVALUE, node); + return; + } + } + else if( !lvalue->isExplicitHandle ) + { + // TODO: Call the assignment operator, or do a BC_COPY if none exist + + asSExprContext ctx(engine); + ctx.type = *lvalue; + Dereference(&ctx, true); + *lvalue = ctx.type; + bc->AddCode(&ctx.bc); + + // TODO: Can't this leave deferred output params unhandled? + + // TODO: Should find the opAssign method that implements the default copy behaviour. + // The beh->copy member will be removed. + asSTypeBehaviour *beh = lvalue->dataType.GetBehaviour(); + if( beh->copy ) + { + // Call the copy operator + bc->Call(asBC_CALLSYS, (asDWORD)beh->copy, 2*AS_PTR_SIZE); + bc->Instr(asBC_PshRPtr); + } + else + { + // Default copy operator + if( lvalue->dataType.GetSizeInMemoryDWords() == 0 || + !(lvalue->dataType.GetObjectType()->flags & asOBJ_POD) ) + { + Error(TXT_NO_DEFAULT_COPY_OP, node); + } + + // Copy larger data types from a reference + bc->InstrWORD(asBC_COPY, (asWORD)lvalue->dataType.GetSizeInMemoryDWords()); + } + } + else + { + // TODO: The object handle can be stored in a variable as well + if( !lvalue->dataType.IsReference() ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return; + } + + // TODO: Convert to register based + bc->InstrPTR(asBC_REFCPY, lvalue->dataType.GetObjectType()); + + // Mark variable as initialized + if( variables ) + { + sVariable *v = variables->GetVariableByOffset(lvalue->stackOffset); + if( v ) v->isInitialized = true; + } + } +} + +bool asCCompiler::CompileRefCast(asSExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode) +{ + bool conversionDone = false; + + asCArray ops; + asUINT n; + + if( ctx->type.dataType.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT ) + { + // We need it to be a reference + if( !ctx->type.dataType.IsReference() ) + { + asCDataType to = ctx->type.dataType; + to.MakeReference(true); + ImplicitConversion(ctx, to, 0, isExplicit ? asIC_EXPLICIT_REF_CAST : asIC_IMPLICIT_CONV, generateCode); + } + + if( isExplicit ) + { + // Allow dynamic cast between object handles (only for script objects). + // At run time this may result in a null handle, + // which when used will throw an exception + conversionDone = true; + if( generateCode ) + { + ctx->bc.InstrDWORD(asBC_Cast, engine->GetTypeIdFromDataType(to)); + + // Allocate a temporary variable for the returned object + int returnOffset = AllocateVariable(to, true); + + // Move the pointer from the object register to the temporary variable + ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset); + + ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset); + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + ctx->type.SetVariable(to, returnOffset, true); + ctx->type.dataType.MakeReference(true); + } + else + { + ctx->type.dataType = to; + ctx->type.dataType.MakeReference(true); + } + } + else + { + if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) ) + { + conversionDone = true; + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + } + } + else + { + // Find a suitable registered behaviour + asSTypeBehaviour *beh = &ctx->type.dataType.GetObjectType()->beh; + for( n = 0; n < beh->operators.GetLength(); n+= 2 ) + { + if( (isExplicit && asBEHAVE_REF_CAST == beh->operators[n]) || + asBEHAVE_IMPLICIT_REF_CAST == beh->operators[n] ) + { + int funcId = beh->operators[n+1]; + + // Is the operator for the output type? + asCScriptFunction *func = engine->scriptFunctions[funcId]; + if( func->returnType.GetObjectType() != to.GetObjectType() ) + continue; + + ops.PushLast(funcId); + } + } + + // Should only have one behaviour for each output type + if( ops.GetLength() == 1 ) + { + if( generateCode ) + { + // Merge the bytecode so that it forms obj.castBehave() + asCTypeInfo objType = ctx->type; + asCArray args; + MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node); + + // Since we're receiving a handle, we can release the original variable + ReleaseTemporaryVariable(objType, &ctx->bc); + } + else + { + asCScriptFunction *func = engine->scriptFunctions[ops[0]]; + ctx->type.Set(func->returnType); + } + } + else if( ops.GetLength() > 1 ) + { + // It shouldn't be possible to have more than one, should it? + asASSERT( false ); + } + } + + return conversionDone; +} + + +// TODO: Re-think the implementation for implicit conversions +// It's currently inefficient and may at times generate unneeded copies of objects +// There are also too many different code paths to test, each working slightly differently +// +// Reference and handle-of should be treated last +// +// - The following conversion categories needs to be implemented in separate functions +// - primitive to primitive +// - primitive to value type +// - primitive to reference type +// - value type to value type +// - value type to primitive +// - value type to reference type +// - reference type to reference type +// - reference type to primitive +// - reference type to value type +// +// Explicit conversion and implicit conversion should use the same functions, only with a flag to enable/disable conversions +// +// If the conversion fails, the type in the asSExprContext must not be modified. This +// causes problems where the conversion is partially done and the compiler continues with +// another option. + +void asCCompiler::ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray *reservedVars) +{ + // Start by implicitly converting constant values + if( ctx->type.isConstant ) + ImplicitConversionConstant(ctx, to, node, convType); + + if( to == ctx->type.dataType ) + return; + + // After the constant value has been converted we have the following possibilities + + // Allow implicit conversion between numbers + if( generateCode ) + { + // Convert smaller types to 32bit first + int s = ctx->type.dataType.GetSizeInMemoryBytes(); + if( s < 4 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + if( ctx->type.dataType.IsIntegerType() ) + { + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_sbTOi, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_swTOi, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(ttInt); + } + else if( ctx->type.dataType.IsUnsignedType() ) + { + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_ubTOi, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_uwTOi, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(ttUInt); + } + } + + if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1) || + (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() || + ctx->type.dataType.IsEnumType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_fTOi, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_dTOi, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + + // Convert to smaller integer if necessary + int s = to.GetSizeInMemoryBytes(); + if( s < 4 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset); + } + } + if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() || + ctx->type.dataType.IsEnumType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + if( ctx->type.dataType.IsUnsignedType() ) + ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset); + else + ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_fTOi64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_dTOi64, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + } + else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() || + ctx->type.dataType.IsEnumType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_i64TOi, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_fTOu, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_dTOu, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + + // Convert to smaller integer if necessary + int s = to.GetSizeInMemoryBytes(); + if( s < 4 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + if( s == 1 ) + ctx->bc.InstrSHORT(asBC_iTOb, ctx->type.stackOffset); + else if( s == 2 ) + ctx->bc.InstrSHORT(asBC_iTOw, ctx->type.stackOffset); + } + } + if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 ) + { + if( ctx->type.dataType.IsIntegerType() || + ctx->type.dataType.IsUnsignedType() || + ctx->type.dataType.IsEnumType() ) + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + if( ctx->type.dataType.IsUnsignedType() ) + ctx->bc.InstrW_W(asBC_uTOi64, offset, ctx->type.stackOffset); + else + ctx->bc.InstrW_W(asBC_iTOi64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_fTOu64, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_dTOu64, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + } + else if( to.IsFloatType() ) + { + if( (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType()) && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_iTOf, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_i64TOf, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_uTOf, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_u64TOf, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsDoubleType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_dTOf, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + else if( to.IsDoubleType() ) + { + if( (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType()) && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_iTOd, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_i64TOd, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_uTOd, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + else if( ctx->type.dataType.IsUnsignedType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ctx->bc.InstrSHORT(asBC_u64TOd, ctx->type.stackOffset); + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + else if( ctx->type.dataType.IsFloatType() ) + { + ConvertToTempVariableNotIn(ctx, reservedVars); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + int offset = AllocateVariableNotIn(to, true, reservedVars); + ctx->bc.InstrW_W(asBC_fTOd, offset, ctx->type.stackOffset); + ctx->type.SetVariable(to, offset, true); + } + } + } + else + { + if( (to.IsIntegerType() || to.IsUnsignedType() || + to.IsFloatType() || to.IsDoubleType() || + (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST)) && + (ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() || + ctx->type.dataType.IsFloatType() || ctx->type.dataType.IsDoubleType() || + ctx->type.dataType.IsEnumType()) ) + { + ctx->type.dataType.SetTokenType(to.GetTokenType()); + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + } + + // Primitive types on the stack, can be const or non-const + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); +} + +void asCCompiler::ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray *reservedVars, bool allowObjectConstruct) +{ + // No conversion from void to any other type + if( ctx->type.dataType.GetTokenType() == ttVoid ) + return; + + // Do we want a var type? + if( to.GetTokenType() == ttQuestion ) + { + // Any type can be converted to a var type, but only when not generating code + asASSERT( !generateCode ); + + ctx->type.dataType = to; + + return; + } + // Do we want a primitive? + else if( to.IsPrimitive() ) + { + if( !ctx->type.dataType.IsPrimitive() ) + ImplicitConvObjectToPrimitive(ctx, to, node, convType, generateCode, reservedVars); + else + ImplicitConvPrimitiveToPrimitive(ctx, to, node, convType, generateCode, reservedVars); + } + else // The target is a complex type + { + if( ctx->type.dataType.IsPrimitive() ) + ImplicitConvPrimitiveToObject(ctx, to, node, convType, generateCode, reservedVars, allowObjectConstruct); + else + ImplicitConvObjectToObject(ctx, to, node, convType, generateCode, reservedVars, allowObjectConstruct); + } +} + +void asCCompiler::ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray *reservedVars) +{ + if( ctx->type.isExplicitHandle ) + { + // An explicit handle cannot be converted to a primitive + if( convType != asIC_IMPLICIT_CONV && node ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + } + return; + } + + // TODO: Must use the const cast behaviour if the object is read-only + + // Find matching value cast behaviours + // Here we're only interested in those that convert the type to a primitive type + asCArray funcs; + asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour(); + if( beh ) + { + if( convType == asIC_EXPLICIT_VAL_CAST ) + { + for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) + { + // accept both implicit and explicit cast + if( (beh->operators[n] == asBEHAVE_VALUE_CAST || + beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) && + builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() ) + funcs.PushLast(beh->operators[n+1]); + } + } + else + { + for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) + { + // accept only implicit cast + if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST && + builder->GetFunctionDescription(beh->operators[n+1])->returnType.IsPrimitive() ) + funcs.PushLast(beh->operators[n+1]); + } + } + } + + // This matrix describes the priorities of the types to search for, for each target type + // The first column is the target type, the priorities goes from left to right + eTokenType matchMtx[10][10] = + { + {ttDouble, ttFloat, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, + {ttFloat, ttDouble, ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8}, + {ttInt64, ttUInt64, ttInt, ttUInt, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt64, ttInt64, ttUInt, ttInt, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt, ttUInt, ttInt64, ttUInt64, ttInt16, ttUInt16, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt, ttInt, ttUInt64, ttInt64, ttUInt16, ttInt16, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttInt8, ttUInt8, ttDouble, ttFloat}, + {ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttUInt8, ttInt8, ttDouble, ttFloat}, + {ttInt8, ttUInt8, ttInt16, ttUInt16, ttInt, ttUInt, ttInt64, ttUInt64, ttDouble, ttFloat}, + {ttUInt8, ttInt8, ttUInt16, ttInt16, ttUInt, ttInt, ttUInt64, ttInt64, ttDouble, ttFloat}, + }; + + // Which row to use? + eTokenType *row = 0; + for( unsigned int type = 0; type < 10; type++ ) + { + if( to.GetTokenType() == matchMtx[type][0] ) + { + row = &matchMtx[type][0]; + break; + } + } + + // Find the best matching cast operator + int funcId = 0; + if( row ) + { + asCDataType target(to); + + // Priority goes from left to right in the matrix + for( unsigned int attempt = 0; attempt < 10 && funcId == 0; attempt++ ) + { + target.SetTokenType(row[attempt]); + for( unsigned int n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[n]); + if( descr->returnType.IsEqualExceptConst(target) ) + { + funcId = funcs[n]; + break; + } + } + } + } + + // Did we find a suitable function? + if( funcId != 0 ) + { + asCScriptFunction *descr = builder->GetFunctionDescription(funcId); + if( generateCode ) + { + asCTypeInfo objType = ctx->type; + + Dereference(ctx, true); + + PerformFunctionCall(funcId, ctx); + + ReleaseTemporaryVariable(objType, &ctx->bc); + } + else + ctx->type.Set(descr->returnType); + + // Allow one more implicit conversion to another primitive type + ImplicitConversion(ctx, to, node, convType, generateCode, reservedVars, false); + } + else + { + if( convType != asIC_IMPLICIT_CONV && node ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + } + } +} + +void asCCompiler::ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode, asCArray *reservedVars, bool allowObjectConstruct) +{ + // Convert null to any object type handle, but not to a non-handle type + if( ctx->type.IsNullConstant() ) + { + if( to.IsObjectHandle() ) + ctx->type.dataType = to; + + return; + } + + // First attempt to convert the base type without instanciating another instance + if( to.GetObjectType() != ctx->type.dataType.GetObjectType() ) + { + // If the to type is an interface and the from type implements it, then we can convert it immediately + if( ctx->type.dataType.GetObjectType()->Implements(to.GetObjectType()) ) + { + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + + // If the to type is a class and the from type derives from it, then we can convert it immediately + if( ctx->type.dataType.GetObjectType()->DerivesFrom(to.GetObjectType()) ) + { + ctx->type.dataType.SetObjectType(to.GetObjectType()); + } + + // If the types are not equal yet, then we may still be able to find a reference cast + if( ctx->type.dataType.GetObjectType() != to.GetObjectType() ) + { + // A ref cast must not remove the constness + bool isConst = false; + if( (ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) || + (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) ) + isConst = true; + + // We may still be able to find an implicit ref cast behaviour + CompileRefCast(ctx, to, convType == asIC_EXPLICIT_REF_CAST, node, generateCode); + + ctx->type.dataType.MakeHandleToConst(isConst); + } + } + + // If the base type is still different, and we are allowed to instance + // another object then we can try an implicit value cast + if( to.GetObjectType() != ctx->type.dataType.GetObjectType() && allowObjectConstruct ) + { + // TODO: Implement support for implicit constructor/factory + + asCArray funcs; + asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour(); + if( beh ) + { + if( convType == asIC_EXPLICIT_VAL_CAST ) + { + for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) + { + // accept both implicit and explicit cast + if( (beh->operators[n] == asBEHAVE_VALUE_CAST || + beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST) && + builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() ) + funcs.PushLast(beh->operators[n+1]); + } + } + else + { + for( unsigned int n = 0; n < beh->operators.GetLength(); n += 2 ) + { + // accept only implicit cast + if( beh->operators[n] == asBEHAVE_IMPLICIT_VALUE_CAST && + builder->GetFunctionDescription(beh->operators[n+1])->returnType.GetObjectType() == to.GetObjectType() ) + funcs.PushLast(beh->operators[n+1]); + } + } + } + + // TODO: If there are multiple valid value casts, then we must choose the most appropriate one + asASSERT( funcs.GetLength() <= 1 ); + + if( funcs.GetLength() == 1 ) + { + asCScriptFunction *f = builder->GetFunctionDescription(funcs[0]); + if( generateCode ) + { + asCTypeInfo objType = ctx->type; + Dereference(ctx, true); + PerformFunctionCall(funcs[0], ctx); + ReleaseTemporaryVariable(objType, &ctx->bc); + } + else + ctx->type.Set(f->returnType); + } + } + + // If we still haven't converted the base type to the correct type, then there is no need to continue + if( to.GetObjectType() != ctx->type.dataType.GetObjectType() ) + return; + + + // TODO: The below code can probably be improved even further. It should first convert the type to + // object handle or non-object handle, and only after that convert to reference or non-reference + + if( to.IsObjectHandle() ) + { + // An object type can be directly converted to a handle of the same type + if( ctx->type.dataType.SupportHandles() ) + { + ctx->type.dataType.MakeHandle(true); + } + + if( ctx->type.dataType.IsObjectHandle() ) + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + + if( to.IsHandleToConst() && ctx->type.dataType.IsObjectHandle() ) + ctx->type.dataType.MakeHandleToConst(true); + } + + if( !to.IsReference() ) + { + if( ctx->type.dataType.IsReference() ) + { + Dereference(ctx, generateCode); + + // TODO: Can't this leave unhandled deferred output params? + } + + if( to.IsObjectHandle() ) + { + // TODO: If the type is handle, then we can't use IsReadOnly to determine the constness of the basetype + + // If the rvalue is a handle to a const object, then + // the lvalue must also be a handle to a const object + if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() ) + { + if( convType != asIC_IMPLICIT_CONV ) + { + asASSERT(node); + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, ctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + } + } + } + else + { + if( ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle ) + { + if( generateCode ) + ctx->bc.Instr(asBC_CHKREF); + + ctx->type.dataType.MakeHandle(false); + } + + // A const object can be converted to a non-const object through a copy + if( ctx->type.dataType.IsReadOnly() && !to.IsReadOnly() && + allowObjectConstruct ) + { + // Does the object type allow a copy to be made? + if( ctx->type.dataType.CanBeCopied() ) + { + if( generateCode ) + { + // Make a temporary object with the copy + PrepareTemporaryObject(node, ctx, reservedVars); + } + else + ctx->type.dataType.MakeReadOnly(false); + } + } + + // A non-const object can be converted to a const object directly + if( !ctx->type.dataType.IsReadOnly() && to.IsReadOnly() ) + { + ctx->type.dataType.MakeReadOnly(true); + } + } + } + else // to.IsReference() + { + if( ctx->type.dataType.IsReference() ) + { + // A reference to a handle can be converted to a reference to an object + // by first reading the address, then verifying that it is not null, then putting the address back on the stack + if( !to.IsObjectHandle() && ctx->type.dataType.IsObjectHandle() && !ctx->type.isExplicitHandle ) + { + ctx->type.dataType.MakeHandle(false); + if( generateCode ) + ctx->bc.Instr(asBC_ChkRefS); + } + + // A reference to a non-const can be converted to a reference to a const + if( to.IsReadOnly() ) + ctx->type.dataType.MakeReadOnly(true); + else if( ctx->type.dataType.IsReadOnly() ) + { + // A reference to a const can be converted to a reference to a + // non-const by copying the object to a temporary variable + ctx->type.dataType.MakeReadOnly(false); + + if( generateCode ) + { + // Allocate a temporary variable + asSExprContext lctx(engine); + asCDataType dt = ctx->type.dataType; + dt.MakeReference(false); + int offset = AllocateVariableNotIn(dt, true, reservedVars); + lctx.type = ctx->type; + lctx.type.isTemporary = true; + lctx.type.stackOffset = (short)offset; + + // TODO: copy: Use copy constructor if available. See PrepareTemporaryObject() + + CallDefaultConstructor(lctx.type.dataType, offset, &lctx.bc, node); + + // Build the right hand expression + asSExprContext rctx(engine); + rctx.type = ctx->type; + rctx.bc.AddCode(&lctx.bc); + rctx.bc.AddCode(&ctx->bc); + + // Build the left hand expression + lctx.bc.InstrSHORT(asBC_PSF, (short)offset); + + // DoAssignment doesn't allow assignment to temporary variable, + // so we temporarily set the type as non-temporary. + lctx.type.isTemporary = false; + + DoAssignment(ctx, &lctx, &rctx, node, node, ttAssignment, node); + + // If the original const object was a temporary variable, then + // that needs to be released now + ProcessDeferredParams(ctx); + + ctx->type = lctx.type; + ctx->type.isTemporary = true; + } + } + } + else + { + if( generateCode ) + { + asCTypeInfo type; + type.Set(ctx->type.dataType); + + // Allocate a temporary variable + int offset = AllocateVariableNotIn(type.dataType, true, reservedVars); + type.isTemporary = true; + type.stackOffset = (short)offset; + if( type.dataType.IsObjectHandle() ) + type.isExplicitHandle = true; + + // TODO: copy: Use copy constructor if available. See PrepareTemporaryObject() + + CallDefaultConstructor(type.dataType, offset, &ctx->bc, node); + type.dataType.MakeReference(true); + + PrepareForAssignment(&type.dataType, ctx, node); + + ctx->bc.InstrSHORT(asBC_PSF, type.stackOffset); + + // If the input type is read-only we'll need to temporarily + // remove this constness, otherwise the assignment will fail + bool typeIsReadOnly = type.dataType.IsReadOnly(); + type.dataType.MakeReadOnly(false); + PerformAssignment(&type, &ctx->type, &ctx->bc, node); + type.dataType.MakeReadOnly(typeIsReadOnly); + + ctx->bc.Pop(ctx->type.dataType.GetSizeOnStackDWords()); + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + ctx->bc.InstrSHORT(asBC_PSF, type.stackOffset); + + ctx->type = type; + } + + // A non-reference can be converted to a reference, + // by putting the value in a temporary variable + ctx->type.dataType.MakeReference(true); + + // Since it is a new temporary variable it doesn't have to be const + ctx->type.dataType.MakeReadOnly(to.IsReadOnly()); + } + } +} + +void asCCompiler::ImplicitConvPrimitiveToObject(asSExprContext * /*ctx*/, const asCDataType & /*to*/, asCScriptNode * /*node*/, EImplicitConv /*isExplicit*/, bool /*generateCode*/, asCArray * /*reservedVars*/, bool /*allowObjectConstruct*/) +{ + // TODO: This function should call the constructor/factory that has been marked as available + // for implicit conversions. The code will likely be similar to CallCopyConstructor() +} + +void asCCompiler::ImplicitConversionConstant(asSExprContext *from, const asCDataType &to, asCScriptNode *node, EImplicitConv convType) +{ + asASSERT(from->type.isConstant); + + // TODO: node should be the node of the value that is + // converted (not the operator that provokes the implicit + // conversion) + + // If the base type is correct there is no more to do + if( to.IsEqualExceptRefAndConst(from->type.dataType) ) return; + + // References cannot be constants + if( from->type.dataType.IsReference() ) return; + + if( (to.IsIntegerType() && to.GetSizeInMemoryDWords() == 1) || + (to.IsEnumType() && convType == asIC_EXPLICIT_VAL_CAST) ) + { + if( from->type.dataType.IsFloatType() || + from->type.dataType.IsDoubleType() || + from->type.dataType.IsUnsignedType() || + from->type.dataType.IsIntegerType() || + from->type.dataType.IsEnumType() ) + { + // Transform the value + // Float constants can be implicitly converted to int + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.floatValue; + int ic = int(fc); + + if( float(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.intValue = ic; + } + // Double constants can be implicitly converted to int + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.doubleValue; + int ic = int(fc); + + if( double(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.intValue = ic; + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Verify that it is possible to convert to signed without getting negative + if( from->type.intValue < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + // Convert to 32bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.intValue = from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.intValue = from->type.wordValue; + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + // Convert to 32bit + from->type.intValue = int(from->type.qwordValue); + } + else if( from->type.dataType.IsIntegerType() && + from->type.dataType.GetSizeInMemoryBytes() < 4 ) + { + // Convert to 32bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.intValue = (signed char)from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.intValue = (short)from->type.wordValue; + } + else if( from->type.dataType.IsEnumType() ) + { + // Enum type is already an integer type + } + + // Set the resulting type + if( to.IsEnumType() ) + from->type.dataType = to; + else + from->type.dataType = asCDataType::CreatePrimitive(ttInt, true); + } + + // Check if a downsize is necessary + if( to.IsIntegerType() && + from->type.dataType.IsIntegerType() && + from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() ) + { + // Verify if it is possible + if( to.GetSizeInMemoryBytes() == 1 ) + { + if( char(from->type.intValue) != from->type.intValue ) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.byteValue = char(from->type.intValue); + } + else if( to.GetSizeInMemoryBytes() == 2 ) + { + if( short(from->type.intValue) != from->type.intValue ) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.wordValue = short(from->type.intValue); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + } + } + else if( to.IsIntegerType() && to.GetSizeInMemoryDWords() == 2 ) + { + // Float constants can be implicitly converted to int + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.floatValue; + asINT64 ic = asINT64(fc); + + if( float(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true); + from->type.qwordValue = ic; + } + // Double constants can be implicitly converted to int + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.doubleValue; + asINT64 ic = asINT64(fc); + + if( double(ic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true); + from->type.qwordValue = ic; + } + else if( from->type.dataType.IsUnsignedType() ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.qwordValue = from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.qwordValue = from->type.wordValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.qwordValue = from->type.dwordValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 8 ) + { + if( asINT64(from->type.qwordValue) < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + } + + from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true); + } + else if( from->type.dataType.IsEnumType() ) + { + from->type.qwordValue = from->type.intValue; + from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true); + } + else if( from->type.dataType.IsIntegerType() ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.qwordValue = (signed char)from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.qwordValue = (short)from->type.wordValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.qwordValue = from->type.intValue; + + from->type.dataType = asCDataType::CreatePrimitive(ttInt64, true); + } + } + else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 1 ) + { + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.floatValue; + int uic = int(fc); + + if( float(uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + else if( uic < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttInt, true); + from->type.intValue = uic; + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.doubleValue; + int uic = int(fc); + + if( double(uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttInt, true); + from->type.intValue = uic; + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsEnumType() ) + { + from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true); + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsIntegerType() ) + { + // Verify that it is possible to convert to unsigned without loosing negative + if( from->type.intValue < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + // Convert to 32bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.intValue = (signed char)from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.intValue = (short)from->type.wordValue; + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true); + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsUnsignedType() && + from->type.dataType.GetSizeInMemoryBytes() < 4 ) + { + // Convert to 32bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.dwordValue = from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.dwordValue = from->type.wordValue; + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt, true); + + // Try once more, in case of a smaller type + ImplicitConversionConstant(from, to, node, convType); + } + else if( from->type.dataType.IsUnsignedType() && + from->type.dataType.GetSizeInMemoryBytes() > to.GetSizeInMemoryBytes() ) + { + // Verify if it is possible + if( to.GetSizeInMemoryBytes() == 1 ) + { + if( asBYTE(from->type.dwordValue) != from->type.dwordValue ) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.byteValue = asBYTE(from->type.dwordValue); + } + else if( to.GetSizeInMemoryBytes() == 2 ) + { + if( asWORD(from->type.dwordValue) != from->type.dwordValue ) + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_VALUE_TOO_LARGE_FOR_TYPE, node); + + from->type.wordValue = asWORD(from->type.dwordValue); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + } + } + else if( to.IsUnsignedType() && to.GetSizeInMemoryDWords() == 2 ) + { + if( from->type.dataType.IsFloatType() ) + { + float fc = from->type.floatValue; + // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers + asQWORD uic = asQWORD(asINT64(fc)); + + // TODO: MSVC6 doesn't permit UINT64 to double + if( float((signed)uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + from->type.qwordValue = uic; + } + else if( from->type.dataType.IsDoubleType() ) + { + double fc = from->type.doubleValue; + // Convert first to int64 then to uint64 to avoid negative float becoming 0 on gnuc base compilers + asQWORD uic = asQWORD(asINT64(fc)); + + // TODO: MSVC6 doesn't permit UINT64 to double + if( double((signed)uic) != fc ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + from->type.qwordValue = uic; + } + else if( from->type.dataType.IsEnumType() ) + { + from->type.qwordValue = (asINT64)from->type.intValue; + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.qwordValue = (asINT64)(signed char)from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.qwordValue = (asINT64)(short)from->type.wordValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.qwordValue = (asINT64)from->type.intValue; + + // Verify that it is possible to convert to unsigned without loosing negative + if( asINT64(from->type.qwordValue) < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + // Verify that it is possible to convert to unsigned without loosing negative + if( asINT64(from->type.qwordValue) < 0 ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_CHANGE_SIGN, node); + } + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + } + else if( from->type.dataType.IsUnsignedType() ) + { + // Convert to 64bit + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + from->type.qwordValue = from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + from->type.qwordValue = from->type.wordValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 4 ) + from->type.qwordValue = from->type.dwordValue; + + from->type.dataType = asCDataType::CreatePrimitive(ttUInt64, true); + } + } + else if( to.IsFloatType() ) + { + if( from->type.dataType.IsDoubleType() ) + { + double ic = from->type.doubleValue; + float fc = float(ic); + + if( double(fc) != ic ) + { + asCString str; + str.Format(TXT_POSSIBLE_LOSS_OF_PRECISION); + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(str.AddressOf(), node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.floatValue = fc; + } + else if( from->type.dataType.IsEnumType() ) + { + float fc = float(from->type.intValue); + + if( int(fc) != from->type.intValue ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.floatValue = fc; + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + int ic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + ic = (signed char)from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + ic = (short)from->type.wordValue; + else + ic = from->type.intValue; + float fc = float(ic); + + if( int(fc) != ic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.floatValue = fc; + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + float fc = float(asINT64(from->type.qwordValue)); + if( asINT64(fc) != asINT64(from->type.qwordValue) ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.floatValue = fc; + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + unsigned int uic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + uic = from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + uic = from->type.wordValue; + else + uic = from->type.dwordValue; + float fc = float(uic); + + if( (unsigned int)(fc) != uic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.floatValue = fc; + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + // TODO: MSVC6 doesn't permit UINT64 to double + float fc = float((signed)from->type.qwordValue); + + if( asQWORD(fc) != from->type.qwordValue ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.floatValue = fc; + } + } + else if( to.IsDoubleType() ) + { + if( from->type.dataType.IsFloatType() ) + { + float ic = from->type.floatValue; + double fc = double(ic); + + // Don't check for float->double + // if( float(fc) != ic ) + // { + // acCString str; + // str.Format(TXT_NOT_EXACT_g_g_g, ic, fc, float(fc)); + // if( !isExplicit ) Warning(str, node); + // } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.doubleValue = fc; + } + else if( from->type.dataType.IsEnumType() ) + { + double fc = double(from->type.intValue); + + if( int(fc) != from->type.intValue ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.doubleValue = fc; + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + int ic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + ic = (signed char)from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + ic = (short)from->type.wordValue; + else + ic = from->type.intValue; + double fc = double(ic); + + if( int(fc) != ic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.doubleValue = fc; + } + else if( from->type.dataType.IsIntegerType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + double fc = double(asINT64(from->type.qwordValue)); + + if( asINT64(fc) != asINT64(from->type.qwordValue) ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.doubleValue = fc; + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + // Must properly convert value in case the from value is smaller + unsigned int uic; + if( from->type.dataType.GetSizeInMemoryBytes() == 1 ) + uic = from->type.byteValue; + else if( from->type.dataType.GetSizeInMemoryBytes() == 2 ) + uic = from->type.wordValue; + else + uic = from->type.dwordValue; + double fc = double(uic); + + if( (unsigned int)(fc) != uic ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.doubleValue = fc; + } + else if( from->type.dataType.IsUnsignedType() && from->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + // TODO: MSVC6 doesn't permit UINT64 to double + double fc = double((signed)from->type.qwordValue); + + if( asQWORD(fc) != from->type.qwordValue ) + { + if( convType != asIC_EXPLICIT_VAL_CAST && node ) Warning(TXT_NOT_EXACT, node); + } + + from->type.dataType.SetTokenType(to.GetTokenType()); + from->type.doubleValue = fc; + } + } +} + +int asCCompiler::DoAssignment(asSExprContext *ctx, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, int op, asCScriptNode *opNode) +{ + // Implicit handle types should always be treated as handles in assignments + if (lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) ) + { + lctx->type.dataType.MakeHandle(true); + lctx->type.isExplicitHandle = true; + } + + // If the left hand expression is a property accessor, then that should be used + // to do the assignment instead of the ordinary operator. The exception is when + // the property accessor is for a handle property, and the operation is a value + // assignment. + if( (lctx->property_get || lctx->property_set) && + !(lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle) ) + { + if( op != ttAssignment ) + { + // TODO: getset: We may actually be able to support this, if we can + // guarantee that the object reference will stay valid + // between the calls to the get and set accessors. + + // Compound assignments are not allowed for properties + Error(TXT_COMPOUND_ASGN_WITH_PROP, opNode); + return -1; + } + + MergeExprContexts(ctx, lctx); + ctx->type = lctx->type; + ctx->property_get = lctx->property_get; + ctx->property_set = lctx->property_set; + ctx->property_const = lctx->property_const; + ctx->property_handle = lctx->property_handle; + + return ProcessPropertySetAccessor(ctx, rctx, opNode); + } + + if( lctx->type.dataType.IsPrimitive() ) + { + if( op != ttAssignment ) + { + // Compute the operator before the assignment + asCTypeInfo lvalue = lctx->type; + + if( lctx->type.isTemporary && !lctx->type.isVariable ) + { + // The temporary variable must not be freed until the + // assignment has been performed. lvalue still holds + // the information about the temporary variable + lctx->type.isTemporary = false; + } + + asSExprContext o(engine); + CompileOperator(opNode, lctx, rctx, &o); + MergeExprContexts(rctx, &o); + rctx->type = o.type; + + // Convert the rvalue to the right type and validate it + PrepareForAssignment(&lvalue.dataType, rctx, rexpr); + + MergeExprContexts(ctx, rctx); + lctx->type = lvalue; + + // The lvalue continues the same, either it was a variable, or a reference in the register + } + else + { + // Convert the rvalue to the right type and validate it + PrepareForAssignment(&lctx->type.dataType, rctx, rexpr, lctx); + + MergeExprContexts(ctx, rctx); + MergeExprContexts(ctx, lctx); + } + + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + + PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode); + + ctx->type = lctx->type; + } + else if( lctx->type.isExplicitHandle ) + { + // Verify that the left hand value isn't a temporary variable + if( lctx->type.isTemporary ) + { + Error(TXT_REF_IS_TEMP, lexpr); + return -1; + } + + // Object handles don't have any compound assignment operators + if( op != ttAssignment ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), lexpr); + return -1; + } + + asCDataType dt = lctx->type.dataType; + dt.MakeReference(false); + + PrepareArgument(&dt, rctx, rexpr, true, 1); + if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), rexpr); + return -1; + } + + MergeExprContexts(ctx, rctx); + MergeExprContexts(ctx, lctx); + + ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE); + + PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode); + + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + + ctx->type = rctx->type; + } + else // if( lctx->type.dataType.IsObject() ) + { + // Verify that the left hand value isn't a temporary variable + if( lctx->type.isTemporary ) + { + Error(TXT_REF_IS_TEMP, lexpr); + return -1; + } + + if( lctx->type.dataType.IsObjectHandle() && !lctx->type.isExplicitHandle ) + { + // Convert the handle to a object reference + asCDataType to; + to = lctx->type.dataType; + to.MakeHandle(false); + ImplicitConversion(lctx, to, lexpr, asIC_IMPLICIT_CONV); + } + + // Check for overloaded assignment operator + if( CompileOverloadedDualOperator(opNode, lctx, rctx, ctx) ) + { + // An overloaded assignment operator was found (or a compilation error occured) + return 0; + } + + // No registered operator was found. In case the operation is a direct + // assignment and the rvalue is the same type as the lvalue, then we can + // still use the byte-for-byte copy to do the assignment + + if( op != ttAssignment ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), lexpr); + return -1; + } + + // Implicitly convert the rvalue to the type of the lvalue + asCDataType dt = lctx->type.dataType; + PrepareArgument(&dt, rctx, rexpr, true, 1); + if( !dt.IsEqualExceptRefAndConst(rctx->type.dataType) ) + { + asCString str; + str.Format(TXT_CANT_IMPLICITLY_CONVERT_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), rexpr); + return -1; + } + + MergeExprContexts(ctx, rctx); + MergeExprContexts(ctx, lctx); + + ctx->bc.InstrWORD(asBC_GETOBJREF, AS_PTR_SIZE); + + PerformAssignment(&lctx->type, &rctx->type, &ctx->bc, opNode); + + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + + ctx->type = lctx->type; + } + + return 0; +} + +int asCCompiler::CompileAssignment(asCScriptNode *expr, asSExprContext *ctx) +{ + asCScriptNode *lexpr = expr->firstChild; + if( lexpr->next ) + { + if( globalExpression ) + { + Error(TXT_ASSIGN_IN_GLOBAL_EXPR, expr); + ctx->type.SetDummy(); + return -1; + } + + // Compile the two expression terms + asSExprContext lctx(engine), rctx(engine); + int rr = CompileAssignment(lexpr->next->next, &rctx); + int lr = CompileCondition(lexpr, &lctx); + + if( lr >= 0 && rr >= 0 ) + return DoAssignment(ctx, &lctx, &rctx, lexpr, lexpr->next->next, lexpr->next->tokenType, lexpr->next); + + // Since the operands failed, the assignment was not computed + ctx->type.SetDummy(); + return -1; + } + + return CompileCondition(lexpr, ctx); +} + +int asCCompiler::CompileCondition(asCScriptNode *expr, asSExprContext *ctx) +{ + asCTypeInfo ctype; + + // Compile the conditional expression + asCScriptNode *cexpr = expr->firstChild; + if( cexpr->next ) + { + //------------------------------- + // Compile the condition + asSExprContext e(engine); + int r = CompileExpression(cexpr, &e); + if( r < 0 ) + e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + if( r >= 0 && !e.type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + { + Error(TXT_EXPR_MUST_BE_BOOL, cexpr); + e.type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + } + ctype = e.type; + + ProcessPropertyGetAccessor(&e, cexpr); + + if( e.type.dataType.IsReference() ) ConvertToVariable(&e); + ProcessDeferredParams(&e); + + //------------------------------- + // Compile the left expression + asSExprContext le(engine); + int lr = CompileAssignment(cexpr->next, &le); + + //------------------------------- + // Compile the right expression + asSExprContext re(engine); + int rr = CompileAssignment(cexpr->next->next, &re); + + if( lr >= 0 && rr >= 0 ) + { + ProcessPropertyGetAccessor(&le, cexpr->next); + ProcessPropertyGetAccessor(&re, cexpr->next->next); + + bool isExplicitHandle = le.type.isExplicitHandle || re.type.isExplicitHandle; + + // Allow a 0 in the first case to be implicitly converted to the second type + if( le.type.isConstant && le.type.intValue == 0 && le.type.dataType.IsUnsignedType() ) + { + asCDataType to = re.type.dataType; + to.MakeReference(false); + to.MakeReadOnly(true); + ImplicitConversionConstant(&le, to, cexpr->next, asIC_IMPLICIT_CONV); + } + + //--------------------------------- + // Output the byte code + int afterLabel = nextLabel++; + int elseLabel = nextLabel++; + + // If left expression is void, then we don't need to store the result + if( le.type.dataType.IsEqualExceptConst(asCDataType::CreatePrimitive(ttVoid, false)) ) + { + // Put the code for the condition expression on the output + MergeExprContexts(ctx, &e); + + // Added the branch decision + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, elseLabel); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Add the left expression + MergeExprContexts(ctx, &le); + ctx->bc.InstrINT(asBC_JMP, afterLabel); + + // Add the right expression + ctx->bc.Label((short)elseLabel); + MergeExprContexts(ctx, &re); + ctx->bc.Label((short)afterLabel); + + // Make sure both expressions have the same type + if( le.type.dataType != re.type.dataType ) + Error(TXT_BOTH_MUST_BE_SAME, expr); + + // Set the type of the result + ctx->type = le.type; + } + else + { + // Allocate temporary variable and copy the result to that one + asCTypeInfo temp; + temp = le.type; + temp.dataType.MakeReference(false); + temp.dataType.MakeReadOnly(false); + // Make sure the variable isn't used in the initial expression + asCArray vars; + e.bc.GetVarsUsed(vars); + int offset = AllocateVariableNotIn(temp.dataType, true, &vars); + temp.SetVariable(temp.dataType, offset, true); + + // TODO: copy: Use copy constructor if available. See PrepareTemporaryObject() + + CallDefaultConstructor(temp.dataType, offset, &ctx->bc, expr); + + // Put the code for the condition expression on the output + MergeExprContexts(ctx, &e); + + // Added the branch decision + ctx->type = e.type; + ConvertToVariable(ctx); + ctx->bc.InstrSHORT(asBC_CpyVtoR4, ctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, elseLabel); + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + // Assign the result of the left expression to the temporary variable + asCTypeInfo rtemp; + rtemp = temp; + if( rtemp.dataType.IsObjectHandle() ) + rtemp.isExplicitHandle = true; + + PrepareForAssignment(&rtemp.dataType, &le, cexpr->next); + MergeExprContexts(ctx, &le); + + if( !rtemp.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + rtemp.dataType.MakeReference(true); + } + PerformAssignment(&rtemp, &le.type, &ctx->bc, cexpr->next); + if( !rtemp.dataType.IsPrimitive() ) + ctx->bc.Pop(le.type.dataType.GetSizeOnStackDWords()); // Pop the original value + + // Release the old temporary variable + ReleaseTemporaryVariable(le.type, &ctx->bc); + + ctx->bc.InstrINT(asBC_JMP, afterLabel); + + // Start of the right expression + ctx->bc.Label((short)elseLabel); + + // Copy the result to the same temporary variable + PrepareForAssignment(&rtemp.dataType, &re, cexpr->next); + MergeExprContexts(ctx, &re); + + if( !rtemp.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + rtemp.dataType.MakeReference(true); + } + PerformAssignment(&rtemp, &re.type, &ctx->bc, cexpr->next); + if( !rtemp.dataType.IsPrimitive() ) + ctx->bc.Pop(le.type.dataType.GetSizeOnStackDWords()); // Pop the original value + + // Release the old temporary variable + ReleaseTemporaryVariable(re.type, &ctx->bc); + + ctx->bc.Label((short)afterLabel); + + // Make sure both expressions have the same type + if( le.type.dataType != re.type.dataType ) + Error(TXT_BOTH_MUST_BE_SAME, expr); + + // Set the temporary variable as output + ctx->type = rtemp; + ctx->type.isExplicitHandle = isExplicitHandle; + + if( !ctx->type.dataType.IsPrimitive() ) + { + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + ctx->type.dataType.MakeReference(true); + } + + // Make sure the output isn't marked as being a literal constant + ctx->type.isConstant = false; + } + } + else + { + ctx->type.SetDummy(); + return -1; + } + } + else + return CompileExpression(cexpr, ctx); + + return 0; +} + +int asCCompiler::CompileExpression(asCScriptNode *expr, asSExprContext *ctx) +{ + asASSERT(expr->nodeType == snExpression); + + // Count the nodes + int count = 0; + asCScriptNode *node = expr->firstChild; + while( node ) + { + count++; + node = node->next; + } + + // Convert to polish post fix, i.e: a+b => ab+ + asCArray stack(count); + asCArray stack2(count); + asCArray postfix(count); + + node = expr->firstChild; + while( node ) + { + int precedence = GetPrecedence(node); + + while( stack.GetLength() > 0 && + precedence <= GetPrecedence(stack[stack.GetLength()-1]) ) + stack2.PushLast(stack.PopLast()); + + stack.PushLast(node); + + node = node->next; + } + + while( stack.GetLength() > 0 ) + stack2.PushLast(stack.PopLast()); + + // We need to swap operands so that the left + // operand is always computed before the right + SwapPostFixOperands(stack2, postfix); + + // Compile the postfix formatted expression + return CompilePostFixExpression(&postfix, ctx); +} + +void asCCompiler::SwapPostFixOperands(asCArray &postfix, asCArray &target) +{ + if( postfix.GetLength() == 0 ) return; + + asCScriptNode *node = postfix.PopLast(); + if( node->nodeType == snExprTerm ) + { + target.PushLast(node); + return; + } + + SwapPostFixOperands(postfix, target); + SwapPostFixOperands(postfix, target); + + target.PushLast(node); +} + +int asCCompiler::CompilePostFixExpression(asCArray *postfix, asSExprContext *ctx) +{ + // Shouldn't send any byte code + asASSERT(ctx->bc.GetLastInstr() == -1); + + // Pop the last node + asCScriptNode *node = postfix->PopLast(); + ctx->exprNode = node; + + // If term, compile the term + if( node->nodeType == snExprTerm ) + return CompileExpressionTerm(node, ctx); + + // Compile the two expression terms + asSExprContext r(engine), l(engine); + + int ret; + ret = CompilePostFixExpression(postfix, &l); if( ret < 0 ) return ret; + ret = CompilePostFixExpression(postfix, &r); if( ret < 0 ) return ret; + + // Compile the operation + return CompileOperator(node, &l, &r, ctx); +} + +int asCCompiler::CompileExpressionTerm(asCScriptNode *node, asSExprContext *ctx) +{ + // Shouldn't send any byte code + asASSERT(ctx->bc.GetLastInstr() == -1); + + // Set the type as a dummy by default, in case of any compiler errors + ctx->type.SetDummy(); + + // Compile the value node + asCScriptNode *vnode = node->firstChild; + while( vnode->nodeType != snExprValue ) + vnode = vnode->next; + + asSExprContext v(engine); + int r = CompileExpressionValue(vnode, &v); if( r < 0 ) return r; + + // Compile post fix operators + asCScriptNode *pnode = vnode->next; + while( pnode ) + { + r = CompileExpressionPostOp(pnode, &v); if( r < 0 ) return r; + pnode = pnode->next; + } + + // Compile pre fix operators + pnode = vnode->prev; + while( pnode ) + { + r = CompileExpressionPreOp(pnode, &v); if( r < 0 ) return r; + pnode = pnode->prev; + } + + // Return the byte code and final type description + MergeExprContexts(ctx, &v); + + ctx->type = v.type; + ctx->property_get = v.property_get; + ctx->property_set = v.property_set; + ctx->property_const = v.property_const; + ctx->property_handle = v.property_handle; + + return 0; +} + +int asCCompiler::CompileExpressionValue(asCScriptNode *node, asSExprContext *ctx) +{ + // Shouldn't receive any byte code + asASSERT(ctx->bc.GetLastInstr() == -1); + + asCScriptNode *vnode = node->firstChild; + if( vnode->nodeType == snVariableAccess ) + { + // Determine the scope resolution of the variable + asCString scope = GetScopeFromNode(vnode); + + // Determine the name of the variable + vnode = vnode->lastChild; + asASSERT(vnode->nodeType == snIdentifier ); + asCString name(&script->code[vnode->tokenPos], vnode->tokenLength); + + sVariable *v = 0; + if( scope == "" ) + v = variables->GetVariable(name.AddressOf()); + if( v == 0 ) + { + // It is not a local variable or parameter + bool found = false; + + // Is it a class member? + if( outFunc && outFunc->objectType && scope == "" ) + { + if( name == THIS_TOKEN ) + { + asCDataType dt = asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly); + + // The object pointer is located at stack position 0 + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(dt, 0, false); + ctx->type.dataType.MakeReference(true); + + found = true; + } + + if( !found ) + { + // See if there are any matching property accessors + asSExprContext access(engine); + access.type.Set(asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly)); + int r = FindPropertyAccessor(name, &access, node); + if( r < 0 ) return -1; + if( access.property_get || access.property_set ) + { + // Prepare the bytecode for the member access + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(asCDataType::CreateObject(outFunc->objectType, outFunc->isReadOnly), 0, false); + ctx->type = access.type; + ctx->property_get = access.property_get; + ctx->property_set = access.property_set; + ctx->property_const = access.property_const; + ctx->property_handle = access.property_handle; + + found = true; + } + } + + if( !found ) + { + asCDataType dt = asCDataType::CreateObject(outFunc->objectType, false); + asCObjectProperty *prop = builder->GetObjectProperty(dt, name.AddressOf()); + if( prop ) + { + // The object pointer is located at stack position 0 + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(dt, 0, false); + ctx->type.dataType.MakeReference(true); + + Dereference(ctx, true); + + // TODO: This is the same as what is in CompileExpressionPostOp + // Put the offset on the stack + ctx->bc.InstrINT(asBC_ADDSi, prop->byteOffset); + + if( prop->type.IsReference() ) + ctx->bc.Instr(asBC_RDSPTR); + + // Reference to primitive must be stored in the temp register + if( prop->type.IsPrimitive() ) + { + // The ADD offset command should store the reference in the register directly + ctx->bc.Instr(asBC_PopRPtr); + } + + // Set the new type (keeping info about temp variable) + ctx->type.dataType = prop->type; + ctx->type.dataType.MakeReference(true); + ctx->type.isVariable = false; + + if( ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle() ) + { + // Objects that are members are not references + ctx->type.dataType.MakeReference(false); + } + + // If the object reference is const, the property will also be const + ctx->type.dataType.MakeReadOnly(outFunc->isReadOnly); + + found = true; + } + } + } + + // Is it a global property? + if( !found && (scope == "" || scope == "::") ) + { + bool isCompiled = true; + bool isPureConstant = false; + asQWORD constantValue; + asCGlobalProperty *prop = builder->GetGlobalProperty(name.AddressOf(), &isCompiled, &isPureConstant, &constantValue); + if( prop ) + { + found = true; + + // Verify that the global property has been compiled already + if( isCompiled ) + { + if( ctx->type.dataType.GetObjectType() && (ctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE) ) + { + ctx->type.dataType.MakeHandle(true); + ctx->type.isExplicitHandle = true; + } + + // If the global property is a pure constant + // we can allow the compiler to optimize it. Pure + // constants are global constant variables that were + // initialized by literal constants. + if( isPureConstant ) + ctx->type.SetConstantQW(prop->type, constantValue); + else + { + ctx->type.Set(prop->type); + ctx->type.dataType.MakeReference(true); + + if( ctx->type.dataType.IsPrimitive() ) + { + // Load the address of the variable into the register + ctx->bc.InstrPTR(asBC_LDG, engine->globalProperties[prop->id]->GetAddressOfValue()); + } + else + { + // Push the address of the variable on the stack + ctx->bc.InstrPTR(asBC_PGA, engine->globalProperties[prop->id]->GetAddressOfValue()); + } + } + } + else + { + asCString str; + str.Format(TXT_UNINITIALIZED_GLOBAL_VAR_s, prop->name.AddressOf()); + Error(str.AddressOf(), vnode); + return -1; + } + } + } + + if( !found ) + { + asCObjectType *scopeType = 0; + if( scope != "" ) + { + // resolve the type before the scope + scopeType = builder->GetObjectType( scope.AddressOf() ); + } + + // Is it an enum value? + asDWORD value = 0; + asCDataType dt; + if( scopeType && builder->GetEnumValueFromObjectType(scopeType, name.AddressOf(), dt, value) ) + { + // scoped enum value found + found = true; + } + else if( scope == "" && !engine->ep.requireEnumScope ) + { + // look for the enum value with no namespace + int e = builder->GetEnumValue(name.AddressOf(), dt, value); + if( e ) + { + found = true; + if( e == 2 ) + { + Error(TXT_FOUND_MULTIPLE_ENUM_VALUES, vnode); + } + } + } + + if( found ) + { + // an enum value was resolved + ctx->type.SetConstantDW(dt, value); + } + } + + if( !found ) + { + // Prepend the scope to the name for the error message + if( scope != "" && scope != "::" ) + scope += "::"; + scope += name; + + asCString str; + str.Format(TXT_s_NOT_DECLARED, scope.AddressOf()); + Error(str.AddressOf(), vnode); + + // Give dummy value + ctx->type.SetDummy(); + + // Declare the variable now so that it will not be reported again + variables->DeclareVariable(name.AddressOf(), asCDataType::CreatePrimitive(ttInt, false), 0x7FFF); + + // Mark the variable as initialized so that the user will not be bother by it again + sVariable *v = variables->GetVariable(name.AddressOf()); + asASSERT(v); + if( v ) v->isInitialized = true; + + return -1; + } + } + else + { + // It is a variable or parameter + + if( v->isPureConstant ) + ctx->type.SetConstantQW(v->type, v->constantValue); + else + { + if( v->type.IsPrimitive() ) + { + if( v->type.IsReference() ) + { + // Copy the reference into the register +#if AS_PTR_SIZE == 1 + ctx->bc.InstrSHORT(asBC_CpyVtoR4, (short)v->stackOffset); +#else + ctx->bc.InstrSHORT(asBC_CpyVtoR8, (short)v->stackOffset); +#endif + ctx->type.Set(v->type); + } + else + ctx->type.SetVariable(v->type, v->stackOffset, false); + } + else + { + ctx->bc.InstrSHORT(asBC_PSF, (short)v->stackOffset); + ctx->type.SetVariable(v->type, v->stackOffset, false); + ctx->type.dataType.MakeReference(true); + + // Implicitly dereference handle parameters sent by reference + if( v->type.IsReference() && (!v->type.IsObject() || v->type.IsObjectHandle()) ) + ctx->bc.Instr(asBC_RDSPTR); + } + } + } + } + else if( vnode->nodeType == snConstant ) + { + if( vnode->tokenType == ttIntConstant ) + { + asCString value(&script->code[vnode->tokenPos], vnode->tokenLength); + + asQWORD val = asStringScanUInt64(value.AddressOf(), 10, 0); + + // Do we need 64 bits? + if( val>>32 ) + ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val); + else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val)); + } + else if( vnode->tokenType == ttBitsConstant ) + { + asCString value(&script->code[vnode->tokenPos+2], vnode->tokenLength-2); + + // TODO: Check for overflow + asQWORD val = asStringScanUInt64(value.AddressOf(), 16, 0); + + // Do we need 64 bits? + if( val>>32 ) + ctx->type.SetConstantQW(asCDataType::CreatePrimitive(ttUInt64, true), val); + else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), asDWORD(val)); + } + else if( vnode->tokenType == ttFloatConstant ) + { + asCString value(&script->code[vnode->tokenPos], vnode->tokenLength); + + // TODO: Check for overflow + + size_t numScanned; + float v = float(asStringScanDouble(value.AddressOf(), &numScanned)); + ctx->type.SetConstantF(asCDataType::CreatePrimitive(ttFloat, true), v); + asASSERT(numScanned == vnode->tokenLength - 1); + } + else if( vnode->tokenType == ttDoubleConstant ) + { + asCString value(&script->code[vnode->tokenPos], vnode->tokenLength); + + // TODO: Check for overflow + + size_t numScanned; + double v = asStringScanDouble(value.AddressOf(), &numScanned); + ctx->type.SetConstantD(asCDataType::CreatePrimitive(ttDouble, true), v); + asASSERT(numScanned == vnode->tokenLength); + } + else if( vnode->tokenType == ttTrue || + vnode->tokenType == ttFalse ) + { +#if AS_SIZEOF_BOOL == 1 + ctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0); +#else + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), vnode->tokenType == ttTrue ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + } + else if( vnode->tokenType == ttStringConstant || + vnode->tokenType == ttMultilineStringConstant || + vnode->tokenType == ttHeredocStringConstant ) + { + asCString str; + asCScriptNode *snode = vnode->firstChild; + if( script->code[snode->tokenPos] == '\'' && engine->ep.useCharacterLiterals ) + { + // Treat the single quoted string as a single character literal + str.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2); + + asDWORD val = 0; + if( str.GetLength() && (unsigned char)str[0] > 127 && engine->ep.scanner == 1 ) + { + // This is the start of a UTF8 encoded character. We need to decode it + val = asStringDecodeUTF8(str.AddressOf(), 0); + if( val == (asDWORD)-1 ) + Error(TXT_INVALID_CHAR_LITERAL, vnode); + } + else + { + val = ProcessStringConstant(str, snode); + if( val == (asDWORD)-1 ) + Error(TXT_INVALID_CHAR_LITERAL, vnode); + } + + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttUInt, true), val); + } + else + { + // Process the string constants + while( snode ) + { + asCString cat; + if( snode->tokenType == ttStringConstant ) + { + cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2); + ProcessStringConstant(cat, snode); + } + else if( snode->tokenType == ttMultilineStringConstant ) + { + if( !engine->ep.allowMultilineStrings ) + Error(TXT_MULTILINE_STRINGS_NOT_ALLOWED, snode); + + cat.Assign(&script->code[snode->tokenPos+1], snode->tokenLength-2); + ProcessStringConstant(cat, snode); + } + else if( snode->tokenType == ttHeredocStringConstant ) + { + cat.Assign(&script->code[snode->tokenPos+3], snode->tokenLength-6); + ProcessHeredocStringConstant(cat, snode); + } + + str += cat; + + snode = snode->next; + } + + // Call the string factory function to create a string object + asCScriptFunction *descr = engine->stringFactory; + if( descr == 0 ) + { + // Error + Error(TXT_STRINGS_NOT_RECOGNIZED, vnode); + + // Give dummy value + ctx->type.SetDummy(); + return -1; + } + else + { + // Register the constant string with the engine + int id = engine->AddConstantString(str.AddressOf(), str.GetLength()); + ctx->bc.InstrWORD(asBC_STR, (asWORD)id); + PerformFunctionCall(descr->id, ctx); + } + } + } + else if( vnode->tokenType == ttNull ) + { +#ifndef AS_64BIT_PTR + ctx->bc.InstrDWORD(asBC_PshC4, 0); +#else + ctx->bc.InstrQWORD(asBC_PshC8, 0); +#endif + ctx->type.SetNullConstant(); + } + else + asASSERT(false); + } + else if( vnode->nodeType == snFunctionCall ) + { + bool found = false; + + // Determine the scope resolution + asCString scope = GetScopeFromNode(vnode); + + if( outFunc && outFunc->objectType && scope != "::" ) + { + // Check if a class method is being called + asCScriptNode *nm = vnode->lastChild->prev; + asCString name; + name.Assign(&script->code[nm->tokenPos], nm->tokenLength); + + asCArray funcs; + + // If we're compiling a constructor and the name of the function called + // is 'super' then the base class' constructor is being called. + // super cannot be called from another scope, i.e. must not be prefixed + if( m_isConstructor && name == SUPER_TOKEN && nm->prev == 0 ) + { + // Actually it is the base class' constructor that is being called, + // but as we won't use the actual function ids here we can take the + // object's own constructors and avoid the need to check if the + // object actually derives from any other class + funcs = outFunc->objectType->beh.constructors; + + // Must not allow calling constructors multiple times + if( continueLabels.GetLength() > 0 ) + { + // If a continue label is set we are in a loop + Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, vnode); + } + else if( breakLabels.GetLength() > 0 ) + { + // TODO: inheritance: Should eventually allow constructors in switch statements + // If a break label is set we are either in a loop or a switch statements + Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, vnode); + } + else if( m_isConstructorCalled ) + { + Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, vnode); + } + m_isConstructorCalled = true; + } + else + builder->GetObjectMethodDescriptions(name.AddressOf(), outFunc->objectType, funcs, false); + + if( funcs.GetLength() ) + { + asCDataType dt = asCDataType::CreateObject(outFunc->objectType, false); + + // The object pointer is located at stack position 0 + ctx->bc.InstrSHORT(asBC_PSF, 0); + ctx->type.SetVariable(dt, 0, false); + ctx->type.dataType.MakeReference(true); + + // TODO: optimize: This adds a CHKREF. Is that really necessary? + Dereference(ctx, true); + + CompileFunctionCall(vnode, ctx, outFunc->objectType, false, scope); + found = true; + } + } + + if( !found ) + CompileFunctionCall(vnode, ctx, 0, false, scope); + } + else if( vnode->nodeType == snConstructCall ) + { + CompileConstructCall(vnode, ctx); + } + else if( vnode->nodeType == snAssignment ) + { + asSExprContext e(engine); + CompileAssignment(vnode, &e); + MergeExprContexts(ctx, &e); + ctx->type = e.type; + } + else if( vnode->nodeType == snCast ) + { + // Implement the cast operator + CompileConversion(vnode, ctx); + } + else + asASSERT(false); + + return 0; +} + +asCString asCCompiler::GetScopeFromNode(asCScriptNode *node) +{ + asCString scope; + asCScriptNode *sn = node->firstChild; + if( sn->tokenType == ttScope ) + { + // Global scope + scope = "::"; + sn = sn->next; + } + else if( sn->next && sn->next->tokenType == ttScope ) + { + scope.Assign(&script->code[sn->tokenPos], sn->tokenLength); + sn = sn->next->next; + } + + if( scope != "" ) + { + // We don't support multiple levels of scope yet + if( sn->next && sn->next->tokenType == ttScope ) + { + Error(TXT_INVALID_SCOPE, sn->next); + } + } + + return scope; +} + +asUINT asCCompiler::ProcessStringConstant(asCString &cstr, asCScriptNode *node, bool processEscapeSequences) +{ + int charLiteral = -1; + + // Process escape sequences + asCArray str((int)cstr.GetLength()); + + for( asUINT n = 0; n < cstr.GetLength(); n++ ) + { +#ifdef AS_DOUBLEBYTE_CHARSET + // Double-byte charset is only allowed for ASCII and not UTF16 encoded strings + if( (cstr[n] & 0x80) && engine->ep.scanner == 0 && engine->ep.stringEncoding != 1 ) + { + // This is the lead character of a double byte character + // include the trail character without checking it's value. + str.PushLast(cstr[n]); + n++; + str.PushLast(cstr[n]); + continue; + } +#endif + + asUINT val; + + if( processEscapeSequences && cstr[n] == '\\' ) + { + ++n; + if( n == cstr.GetLength() ) + { + if( charLiteral == -1 ) charLiteral = 0; + return charLiteral; + } + + // TODO: Consider deprecating use of hexadecimal escape sequences, + // as they do not guarantee proper unicode sequences + if( cstr[n] == 'x' || cstr[n] == 'X' ) + { + ++n; + if( n == cstr.GetLength() ) break; + + val = 0; + int c = engine->ep.stringEncoding == 1 ? 4 : 2; + for( ; c > 0 && n < cstr.GetLength(); c--, n++ ) + { + if( cstr[n] >= '0' && cstr[n] <= '9' ) + val = val*16 + cstr[n] - '0'; + else if( cstr[n] >= 'a' && cstr[n] <= 'f' ) + val = val*16 + cstr[n] - 'a' + 10; + else if( cstr[n] >= 'A' && cstr[n] <= 'F' ) + val = val*16 + cstr[n] - 'A' + 10; + else + break; + } + + // Rewind one, since the loop will increment it again + n--; + + // Hexadecimal escape sequences produce exact value, even if it is not proper unicode chars + if( engine->ep.stringEncoding == 0 ) + { + str.PushLast(val); + } + else + { +#ifndef AS_BIG_ENDIAN + str.PushLast(val); + str.PushLast(val>>8); +#else + str.PushLast(val>>8); + str.PushLast(val); +#endif + } + if( charLiteral == -1 ) charLiteral = val; + continue; + } + else if( cstr[n] == 'u' || cstr[n] == 'U' ) + { + // \u expects 4 hex digits + // \U expects 8 hex digits + bool expect2 = cstr[n] == 'u'; + int c = expect2 ? 4 : 8; + + val = 0; + + for( ; c > 0; c-- ) + { + ++n; + if( n == cstr.GetLength() ) break; + + if( cstr[n] >= '0' && cstr[n] <= '9' ) + val = val*16 + cstr[n] - '0'; + else if( cstr[n] >= 'a' && cstr[n] <= 'f' ) + val = val*16 + cstr[n] - 'a' + 10; + else if( cstr[n] >= 'A' && cstr[n] <= 'F' ) + val = val*16 + cstr[n] - 'A' + 10; + else + break; + } + + if( c != 0 ) + { + // Give warning about invalid code point + // TODO: Need code position for warning + asCString msg; + msg.Format(TXT_INVALID_UNICODE_FORMAT_EXPECTED_d, expect2 ? 4 : 8); + Warning(msg.AddressOf(), node); + continue; + } + } + else + { + if( cstr[n] == '"' ) + val = '"'; + else if( cstr[n] == '\'' ) + val = '\''; + else if( cstr[n] == 'n' ) + val = '\n'; + else if( cstr[n] == 'r' ) + val = '\r'; + else if( cstr[n] == 't' ) + val = '\t'; + else if( cstr[n] == '0' ) + val = '\0'; + else if( cstr[n] == '\\' ) + val = '\\'; + else + { + // Invalid escape sequence + Warning(TXT_INVALID_ESCAPE_SEQUENCE, node); + continue; + } + } + } + else + { + if( engine->ep.scanner == 1 && (cstr[n] & 0x80) ) + { + unsigned int len; + val = asStringDecodeUTF8(&cstr[n], &len); + if( val == 0xFFFFFFFF || len < 0 ) + { + // Incorrect UTF8 encoding. Use only the first byte + // TODO: Need code position for warning + Warning(TXT_INVALID_UNICODE_SEQUENCE_IN_SRC, node); + val = (unsigned char)cstr[n]; + } + else + n += len-1; + } + else + val = (unsigned char)cstr[n]; + } + + // Add the character to the final string + char encodedValue[5]; + int len; + if( engine->ep.stringEncoding == 0 ) + { + len = asStringEncodeUTF8(val, encodedValue); + } + else + { + len = asStringEncodeUTF16(val, encodedValue); + } + + if( len < 0 ) + { + // Give warning about invalid code point + // TODO: Need code position for warning + Warning(TXT_INVALID_UNICODE_VALUE, node); + } + else + { + // Add the encoded value to the final string + str.Concatenate(encodedValue, len); + if( charLiteral == -1 ) charLiteral = val; + } + } + + cstr.Assign(str.AddressOf(), str.GetLength()); + return charLiteral; +} + +void asCCompiler::ProcessHeredocStringConstant(asCString &str, asCScriptNode *node) +{ + // Remove first line if it only contains whitespace + asUINT start; + for( start = 0; start < str.GetLength(); start++ ) + { + if( str[start] == '\n' ) + { + // Remove the linebreak as well + start++; + break; + } + + if( str[start] != ' ' && + str[start] != '\t' && + str[start] != '\r' ) + { + // Don't remove anything + start = 0; + break; + } + } + + // Remove last line break and the line after that if it only contains whitespaces + int end; + for( end = (int)str.GetLength() - 1; end >= 0; end-- ) + { + if( str[end] == '\n' ) + break; + + if( str[end] != ' ' && + str[end] != '\t' && + str[end] != '\r' ) + { + // Don't remove anything + end = (int)str.GetLength(); + break; + } + } + + if( end < 0 ) end = 0; + + asCString tmp; + tmp.Assign(&str[start], end-start); + + ProcessStringConstant(tmp, node, false); + + str = tmp; +} + +void asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) +{ + asSExprContext expr(engine); + asCDataType to; + bool anyErrors = false; + EImplicitConv convType; + if( node->nodeType == snConstructCall ) + { + convType = asIC_EXPLICIT_VAL_CAST; + + // Verify that there is only one argument + if( node->lastChild->firstChild == 0 || + node->lastChild->firstChild != node->lastChild->lastChild ) + { + Error(TXT_ONLY_ONE_ARGUMENT_IN_CAST, node->lastChild); + expr.type.SetDummy(); + anyErrors = true; + } + else + { + // Compile the expression + int r = CompileAssignment(node->lastChild->firstChild, &expr); + if( r < 0 ) + anyErrors = true; + } + + // Determine the requested type + to = builder->CreateDataTypeFromNode(node->firstChild, script); + to.MakeReadOnly(true); // Default to const + asASSERT(to.IsPrimitive()); + } + else + { + convType = asIC_EXPLICIT_REF_CAST; + + // Compile the expression + int r = CompileAssignment(node->lastChild, &expr); + if( r < 0 ) + anyErrors = true; + else + { + // Determine the requested type + to = builder->CreateDataTypeFromNode(node->firstChild, script); + to = builder->ModifyDataTypeFromNode(to, node->firstChild->next, script, 0, 0); + + // If the type support object handles, then use it + if( to.SupportHandles() ) + { + to.MakeHandle(true); + } + else if( !to.IsObjectHandle() ) + { + // The cast operator can only be used for reference casts + Error(TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST, node->firstChild); + anyErrors = true; + } + } + } + + if( anyErrors ) + { + // Assume that the error can be fixed and allow the compilation to continue + ctx->type.SetConstantDW(to, 0); + return; + } + + // We don't want a reference + if( expr.type.dataType.IsReference() ) + { + if( expr.type.dataType.IsObject() ) + Dereference(&expr, true); + else + ConvertToVariable(&expr); + } + + ImplicitConversion(&expr, to, node, convType); + + IsVariableInitialized(&expr.type, node); + + // If no type conversion is really tried ignore it + if( to == expr.type.dataType ) + { + // This will keep information about constant type + MergeExprContexts(ctx, &expr); + ctx->type = expr.type; + return; + } + + if( to.IsEqualExceptConst(expr.type.dataType) && to.IsPrimitive() ) + { + MergeExprContexts(ctx, &expr); + ctx->type = expr.type; + ctx->type.dataType.MakeReadOnly(true); + return; + } + + // The implicit conversion already does most of the conversions permitted, + // here we'll only treat those conversions that require an explicit cast. + + bool conversionOK = false; + if( !expr.type.isConstant ) + { + if( !expr.type.dataType.IsObject() ) + ConvertToTempVariable(&expr); + + if( to.IsObjectHandle() && + expr.type.dataType.IsObjectHandle() && + !(!to.IsHandleToConst() && expr.type.dataType.IsHandleToConst()) ) + { + conversionOK = CompileRefCast(&expr, to, true, node); + + MergeExprContexts(ctx, &expr); + ctx->type = expr.type; + } + } + + if( conversionOK ) + return; + + // Conversion not available + ctx->type.SetDummy(); + + asCString strTo, strFrom; + + strTo = to.Format(); + strFrom = expr.type.dataType.Format(); + + asCString msg; + msg.Format(TXT_NO_CONVERSION_s_TO_s, strFrom.AddressOf(), strTo.AddressOf()); + + Error(msg.AddressOf(), node); +} + +void asCCompiler::AfterFunctionCall(int funcID, asCArray &args, asSExprContext *ctx, bool deferAll) +{ + asCScriptFunction *descr = builder->GetFunctionDescription(funcID); + + // Parameters that are sent by reference should be assigned + // to the evaluated expression if it is an lvalue + + // Evaluate the arguments from last to first + int n = (int)descr->parameterTypes.GetLength() - 1; + for( ; n >= 0; n-- ) + { + if( (descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] & asTM_OUTREF)) || + (descr->parameterTypes[n].IsObject() && deferAll) ) + { + asASSERT( !(descr->parameterTypes[n].IsReference() && (descr->inOutFlags[n] == asTM_OUTREF)) || args[n]->origExpr ); + + // For &inout, only store the argument if it is for a temporary variable + if( engine->ep.allowUnsafeReferences || + descr->inOutFlags[n] != asTM_INOUTREF || args[n]->type.isTemporary ) + { + // Store the argument for later processing + asSDeferredParam outParam; + outParam.argNode = args[n]->exprNode; + outParam.argType = args[n]->type; + outParam.argInOutFlags = descr->inOutFlags[n]; + outParam.origExpr = args[n]->origExpr; + + ctx->deferredParams.PushLast(outParam); + } + } + else + { + // Release the temporary variable now + ReleaseTemporaryVariable(args[n]->type, &ctx->bc); + } + } +} + +void asCCompiler::ProcessDeferredParams(asSExprContext *ctx) +{ + if( isProcessingDeferredParams ) return; + + isProcessingDeferredParams = true; + + for( asUINT n = 0; n < ctx->deferredParams.GetLength(); n++ ) + { + asSDeferredParam outParam = ctx->deferredParams[n]; + if( outParam.argInOutFlags < asTM_OUTREF ) // &in, or not reference + { + // Just release the variable + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + } + else if( outParam.argInOutFlags == asTM_OUTREF ) + { + asSExprContext *expr = outParam.origExpr; + + if( outParam.argType.dataType.IsObjectHandle() ) + { + // Implicitly convert the value to a handle + if( expr->type.dataType.IsObjectHandle() ) + expr->type.isExplicitHandle = true; + } + + // Verify that the expression result in a lvalue, or a property accessor + if( IsLValue(expr->type) || expr->property_get || expr->property_set ) + { + asSExprContext rctx(engine); + rctx.type = outParam.argType; + if( rctx.type.dataType.IsPrimitive() ) + rctx.type.dataType.MakeReference(false); + else + { + rctx.bc.InstrSHORT(asBC_PSF, outParam.argType.stackOffset); + rctx.type.dataType.MakeReference(true); + if( expr->type.isExplicitHandle ) + rctx.type.isExplicitHandle = true; + } + + asSExprContext o(engine); + DoAssignment(&o, expr, &rctx, outParam.argNode, outParam.argNode, ttAssignment, outParam.argNode); + + if( !o.type.dataType.IsPrimitive() ) o.bc.Pop(AS_PTR_SIZE); + + MergeExprContexts(ctx, &o); + } + else + { + // We must still evaluate the expression + MergeExprContexts(ctx, expr); + if( !expr->type.isConstant ) + ctx->bc.Pop(expr->type.dataType.GetSizeOnStackDWords()); + + // Give a warning + Warning(TXT_ARG_NOT_LVALUE, outParam.argNode); + + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + } + + ReleaseTemporaryVariable(expr->type, &ctx->bc); + + // Delete the original expression context + asDELETE(expr,asSExprContext); + } + else // &inout + { + if( outParam.argType.isTemporary ) + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + else if( !outParam.argType.isVariable ) + { + if( outParam.argType.dataType.IsObject() && + outParam.argType.dataType.GetBehaviour()->addref && + outParam.argType.dataType.GetBehaviour()->release ) + { + // Release the object handle that was taken to guarantee the reference + ReleaseTemporaryVariable(outParam.argType, &ctx->bc); + } + } + } + } + + ctx->deferredParams.SetLength(0); + isProcessingDeferredParams = false; +} + + +void asCCompiler::CompileConstructCall(asCScriptNode *node, asSExprContext *ctx) +{ + // The first node is a datatype node + asCString name; + asCTypeInfo tempObj; + asCArray funcs; + + // It is possible that the name is really a constructor + asCDataType dt; + dt = builder->CreateDataTypeFromNode(node->firstChild, script); + if( dt.IsPrimitive() ) + { + // This is a cast to a primitive type + CompileConversion(node, ctx); + return; + } + + if( globalExpression ) + { + Error(TXT_FUNCTION_IN_GLOBAL_EXPR, node); + + // Output dummy code + ctx->type.SetDummy(); + return; + } + + // Compile the arguments + asCArray args; + asCArray temporaryVariables; + if( CompileArgumentList(node->lastChild, args) >= 0 ) + { + // Check for a value cast behaviour + if( args.GetLength() == 1 && args[0]->type.dataType.GetObjectType() ) + { + asSExprContext conv(engine); + conv.type = args[0]->type; + ImplicitConversion(&conv, dt, node->lastChild, asIC_EXPLICIT_VAL_CAST, false); + + if( conv.type.dataType.IsEqualExceptRef(dt) ) + { + ImplicitConversion(args[0], dt, node->lastChild, asIC_EXPLICIT_VAL_CAST); + + ctx->bc.AddCode(&args[0]->bc); + ctx->type = args[0]->type; + + asDELETE(args[0],asSExprContext); + + return; + } + } + + // Check for possible constructor/factory + name = dt.Format(); + + asSTypeBehaviour *beh = dt.GetBehaviour(); + + if( !(dt.GetObjectType()->flags & asOBJ_REF) ) + { + funcs = beh->constructors; + + // Value types and script types are allocated through the constructor + tempObj.dataType = dt; + tempObj.stackOffset = (short)AllocateVariable(dt, true); + tempObj.dataType.MakeReference(true); + tempObj.isTemporary = true; + tempObj.isVariable = true; + + // Push the address of the object on the stack + ctx->bc.InstrSHORT(asBC_VAR, tempObj.stackOffset); + } + else + { + funcs = beh->factories; + } + + // Special case: Allow calling func(void) with a void expression. + if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) ) + { + // Evaluate the expression before the function call + MergeExprContexts(ctx, args[0]); + asDELETE(args[0],asSExprContext); + args.SetLength(0); + } + + // Special case: If this is an object constructor and there are no arguments use the default constructor. + // If none has been registered, just allocate the variable and push it on the stack. + if( args.GetLength() == 0 ) + { + asSTypeBehaviour *beh = tempObj.dataType.GetBehaviour(); + if( beh && beh->construct == 0 && !(dt.GetObjectType()->flags & asOBJ_REF) ) + { + // Call the default constructor + ctx->type = tempObj; + + asASSERT(ctx->bc.GetLastInstr() == asBC_VAR); + ctx->bc.RemoveLastInstr(); + + CallDefaultConstructor(tempObj.dataType, tempObj.stackOffset, &ctx->bc, node); + + // Push the reference on the stack + ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + return; + } + } + + MatchFunctions(funcs, args, node, name.AddressOf(), NULL, false); + + if( funcs.GetLength() != 1 ) + { + // The error was reported by MatchFunctions() + + // Dummy value + ctx->type.SetDummy(); + } + else + { + asCByteCode objBC(engine); + + PrepareFunctionCall(funcs[0], &ctx->bc, args); + + MoveArgsToStack(funcs[0], &ctx->bc, args, false); + + if( !(dt.GetObjectType()->flags & asOBJ_REF) ) + { + int offset = 0; + asCScriptFunction *descr = builder->GetFunctionDescription(funcs[0]); + for( asUINT n = 0; n < args.GetLength(); n++ ) + offset += descr->parameterTypes[n].GetSizeOnStackDWords(); + + ctx->bc.InstrWORD(asBC_GETREF, (asWORD)offset); + + PerformFunctionCall(funcs[0], ctx, true, &args, tempObj.dataType.GetObjectType()); + + // The constructor doesn't return anything, + // so we have to manually inform the type of + // the return value + ctx->type = tempObj; + + // Push the address of the object on the stack again + ctx->bc.InstrSHORT(asBC_PSF, tempObj.stackOffset); + } + else + { + // Call the factory to create the reference type + PerformFunctionCall(funcs[0], ctx, false, &args); + } + } + } + else + { + // Failed to compile the argument list, set the result to the dummy type + ctx->type.SetDummy(); + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n],asSExprContext); + } +} + + +void asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope) +{ + asCString name; + asCTypeInfo tempObj; + asCArray funcs; + + asCScriptNode *nm = node->lastChild->prev; + name.Assign(&script->code[nm->tokenPos], nm->tokenLength); + + if( objectType ) + { + // If we're compiling a constructor and the name of the function is super then + // the constructor of the base class is being called. + // super cannot be prefixed with a scope operator + if( m_isConstructor && name == SUPER_TOKEN && nm->prev == 0 ) + { + // If the class is not derived from anyone else, calling super should give an error + if( objectType->derivedFrom ) + funcs = objectType->derivedFrom->beh.constructors; + } + else + builder->GetObjectMethodDescriptions(name.AddressOf(), objectType, funcs, objIsConst, scope); + } + else + builder->GetFunctionDescriptions(name.AddressOf(), funcs); + + if( globalExpression ) + { + Error(TXT_FUNCTION_IN_GLOBAL_EXPR, node); + + // Output dummy code + ctx->type.SetDummy(); + return; + } + + // Compile the arguments + asCArray args; + asCArray temporaryVariables; + + if( CompileArgumentList(node->lastChild, args) >= 0 ) + { + // Special case: Allow calling func(void) with a void expression. + if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) ) + { + // Evaluate the expression before the function call + MergeExprContexts(ctx, args[0]); + asDELETE(args[0],asSExprContext); + args.SetLength(0); + } + + MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope); + + if( funcs.GetLength() != 1 ) + { + // The error was reported by MatchFunctions() + + // Dummy value + ctx->type.SetDummy(); + } + else + { + MakeFunctionCall(ctx, funcs[0], objectType, args, node); + } + } + else + { + // Failed to compile the argument list, set the dummy type and continue compilation + ctx->type.SetDummy(); + } + + // Cleanup + for( asUINT n = 0; n < args.GetLength(); n++ ) + if( args[n] ) + { + asDELETE(args[n],asSExprContext); + } +} + +int asCCompiler::CompileExpressionPreOp(asCScriptNode *node, asSExprContext *ctx) +{ + int op = node->tokenType; + + IsVariableInitialized(&ctx->type, node); + + if( op == ttHandle ) + { + // Verify that the type allow its handle to be taken + if( ctx->type.isExplicitHandle || !ctx->type.dataType.IsObject() || !ctx->type.dataType.GetObjectType()->beh.addref || !ctx->type.dataType.GetObjectType()->beh.release ) + { + Error(TXT_OBJECT_HANDLE_NOT_SUPPORTED, node); + return -1; + } + + // Objects that are not local variables are not references + if( !ctx->type.dataType.IsReference() && !(ctx->type.dataType.IsObject() && !ctx->type.isVariable) ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + + // If this is really an object then the handle created is a const handle + bool makeConst = !ctx->type.dataType.IsObjectHandle(); + + // Mark the type as an object handle + ctx->type.dataType.MakeHandle(true); + ctx->type.isExplicitHandle = true; + if( makeConst ) + ctx->type.dataType.MakeReadOnly(true); + } + else if( (op == ttMinus || op == ttBitNot) && ctx->type.dataType.IsObject() ) + { + // Look for the opNeg or opCom methods + const char *opName = 0; + switch( op ) + { + case ttMinus: opName = "opNeg"; break; + case ttBitNot: opName = "opCom"; break; + } + + if( opName ) + { + ProcessPropertyGetAccessor(ctx, node); + + // Is it a const value? + bool isConst = false; + if( ctx->type.dataType.IsObjectHandle() ) + isConst = ctx->type.dataType.IsHandleToConst(); + else + isConst = ctx->type.dataType.IsReadOnly(); + + // TODO: If the value isn't const, then first try to find the non const method, and if not found try to find the const method + + // Find the correct method + asCArray funcs; + asCObjectType *ot = ctx->type.dataType.GetObjectType(); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( func->name == opName && + func->parameterTypes.GetLength() == 0 && + (!isConst || func->isReadOnly) ) + { + funcs.PushLast(func->id); + } + } + + // Did we find the method? + if( funcs.GetLength() == 1 ) + { + asCTypeInfo objType = ctx->type; + asCArray args; + MakeFunctionCall(ctx, funcs[0], objType.dataType.GetObjectType(), args, node); + ReleaseTemporaryVariable(objType, &ctx->bc); + return 0; + } + else if( funcs.GetLength() == 0 ) + { + asCString str; + str = asCString(opName) + "()"; + if( isConst ) + str += " const"; + str.Format(TXT_FUNCTION_s_NOT_FOUND, str.AddressOf()); + Error(str.AddressOf(), node); + ctx->type.SetDummy(); + return -1; + } + else if( funcs.GetLength() > 1 ) + { + Error(TXT_MORE_THAN_ONE_MATCHING_OP, node); + PrintMatchingFuncs(funcs, node); + + ctx->type.SetDummy(); + return -1; + } + } + } + else if( op == ttPlus || op == ttMinus ) + { + ProcessPropertyGetAccessor(ctx, node); + + asCDataType to = ctx->type.dataType; + + // TODO: The case -2147483648 gives an unecessary warning of changed sign for implicit conversion + + if( ctx->type.dataType.IsUnsignedType() || ctx->type.dataType.IsEnumType() ) + { + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + to = asCDataType::CreatePrimitive(ttInt8, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + to = asCDataType::CreatePrimitive(ttInt16, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 ) + to = asCDataType::CreatePrimitive(ttInt, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 ) + to = asCDataType::CreatePrimitive(ttInt64, false); + else + { + Error(TXT_INVALID_TYPE, node); + return -1; + } + } + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV); + + if( !ctx->type.isConstant ) + { + ConvertToTempVariable(ctx); + + if( op == ttMinus ) + { + if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_NEGi, ctx->type.stackOffset); + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + ctx->bc.InstrSHORT(asBC_NEGi64, ctx->type.stackOffset); + else if( ctx->type.dataType.IsFloatType() ) + ctx->bc.InstrSHORT(asBC_NEGf, ctx->type.stackOffset); + else if( ctx->type.dataType.IsDoubleType() ) + ctx->bc.InstrSHORT(asBC_NEGd, ctx->type.stackOffset); + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + + return 0; + } + } + else + { + if( op == ttMinus ) + { + if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->type.intValue = -ctx->type.intValue; + else if( ctx->type.dataType.IsIntegerType() && ctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + ctx->type.qwordValue = -(asINT64)ctx->type.qwordValue; + else if( ctx->type.dataType.IsFloatType() ) + ctx->type.floatValue = -ctx->type.floatValue; + else if( ctx->type.dataType.IsDoubleType() ) + ctx->type.doubleValue = -ctx->type.doubleValue; + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + + return 0; + } + } + + if( op == ttPlus ) + { + if( !ctx->type.dataType.IsIntegerType() && + !ctx->type.dataType.IsFloatType() && + !ctx->type.dataType.IsDoubleType() ) + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + } + } + else if( op == ttNot ) + { + if( ctx->type.dataType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, true)) ) + { + if( ctx->type.isConstant ) + { + ctx->type.dwordValue = (ctx->type.dwordValue == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + return 0; + } + + ProcessPropertyGetAccessor(ctx, node); + + ConvertToTempVariable(ctx); + + ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset); + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + } + else if( op == ttBitNot ) + { + ProcessPropertyGetAccessor(ctx, node); + + asCDataType to = ctx->type.dataType; + + if( ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsEnumType() ) + { + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + to = asCDataType::CreatePrimitive(ttUInt8, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + to = asCDataType::CreatePrimitive(ttUInt16, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 ) + to = asCDataType::CreatePrimitive(ttUInt, false); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 8 ) + to = asCDataType::CreatePrimitive(ttUInt64, false); + else + { + Error(TXT_INVALID_TYPE, node); + return -1; + } + } + + if( ctx->type.dataType.IsReference() ) ConvertToVariable(ctx); + ImplicitConversion(ctx, to, node, asIC_IMPLICIT_CONV); + + if( ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.isConstant ) + { + ctx->type.qwordValue = ~ctx->type.qwordValue; + return 0; + } + + ConvertToTempVariable(ctx); + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_BNOT, ctx->type.stackOffset); + else + ctx->bc.InstrSHORT(asBC_BNOT64, ctx->type.stackOffset); + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + } + else if( op == ttInc || op == ttDec ) + { + // Need a reference to the primitive that will be updated + // The result of this expression is the same reference as before + if( globalExpression ) + { + Error(TXT_INC_OP_IN_GLOBAL_EXPR, node); + return -1; + } + + // Make sure the reference isn't a temporary variable + if( ctx->type.isTemporary ) + { + Error(TXT_REF_IS_TEMP, node); + return -1; + } + if( ctx->type.dataType.IsReadOnly() ) + { + Error(TXT_REF_IS_READ_ONLY, node); + return -1; + } + + if( ctx->type.isVariable ) + ConvertToReference(ctx); + else if( !ctx->type.dataType.IsReference() ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + + if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi64); + else + ctx->bc.Instr(asBC_DECi64); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi); + else + ctx->bc.Instr(asBC_DECi); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi16); + else + ctx->bc.Instr(asBC_DECi16); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCi8); + else + ctx->bc.Instr(asBC_DECi8); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttFloat, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCf); + else + ctx->bc.Instr(asBC_DECf); + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttDouble, false)) ) + { + if( op == ttInc ) + ctx->bc.Instr(asBC_INCd); + else + ctx->bc.Instr(asBC_DECd); + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + } + else + { + // Unknown operator + asASSERT(false); + return -1; + } + + return 0; +} + +void asCCompiler::ConvertToReference(asSExprContext *ctx) +{ + if( ctx->type.isVariable ) + { + ctx->bc.InstrSHORT(asBC_LDV, ctx->type.stackOffset); + ctx->type.dataType.MakeReference(true); + ctx->type.Set(ctx->type.dataType); + } +} + +int asCCompiler::FindPropertyAccessor(const asCString &name, asSExprContext *ctx, asCScriptNode *node) +{ + if( !ctx->type.dataType.IsObject() ) + return 0; + + // Check if the object as any methods with the property name prefixed by get_ or set_ + int getId = 0, setId = 0; + asCString getName = "get_" + name; + asCString setName = "set_" + name; + asCArray multipleGetFuncs, multipleSetFuncs; + asCObjectType *ot = ctx->type.dataType.GetObjectType(); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *f = engine->scriptFunctions[ot->methods[n]]; + if( f->name == getName && f->parameterTypes.GetLength() == 0 ) + { + if( getId == 0 ) + getId = ot->methods[n]; + else + { + if( multipleGetFuncs.GetLength() == 0 ) + multipleGetFuncs.PushLast(getId); + + multipleGetFuncs.PushLast(ot->methods[n]); + } + } + // TODO: getset: If the parameter is a reference, it must not be an out reference. Should we allow inout ref? + if( f->name == setName && f->parameterTypes.GetLength() == 1 ) + { + if( setId == 0 ) + setId = ot->methods[n]; + else + { + if( multipleSetFuncs.GetLength() == 0 ) + multipleSetFuncs.PushLast(setId); + + multipleSetFuncs.PushLast(ot->methods[n]); + } + } + } + + // Check for multiple matches + if( multipleGetFuncs.GetLength() > 0 ) + { + asCString str; + str.Format(TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s, name.AddressOf()); + Error(str.AddressOf(), node); + + PrintMatchingFuncs(multipleGetFuncs, node); + + return -1; + } + + if( multipleSetFuncs.GetLength() > 0 ) + { + asCString str; + str.Format(TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s, name.AddressOf()); + Error(str.AddressOf(), node); + + PrintMatchingFuncs(multipleSetFuncs, node); + + return -1; + } + + // Check for type compatibility between get and set accessor + if( getId && setId ) + { + asCScriptFunction *getFunc = engine->scriptFunctions[getId]; + asCScriptFunction *setFunc = engine->scriptFunctions[setId]; + + if( !getFunc->returnType.IsEqualExceptRefAndConst(setFunc->parameterTypes[0]) ) + { + asCString str; + str.Format(TXT_GET_SET_ACCESSOR_TYPE_MISMATCH_FOR_s, name.AddressOf()); + Error(str.AddressOf(), node); + + asCArray funcs; + funcs.PushLast(getId); + funcs.PushLast(setId); + + PrintMatchingFuncs(funcs, node); + + return -1; + } + } + + if( getId || setId ) + { + // Property accessors were found, but we don't know which is to be used yet, so + // we just prepare the bytecode for the method call, and then store the function ids + // so that the right one can be used when we get there. + ctx->property_get = getId; + ctx->property_set = setId; + + // If the object is read-only then we need to remember + if( (!ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsReadOnly()) || + (ctx->type.dataType.IsObjectHandle() && ctx->type.dataType.IsHandleToConst()) ) + ctx->property_const = true; + else + ctx->property_const = false; + + // If the object is a handle then we need to remember that + ctx->property_handle = ctx->type.dataType.IsObjectHandle(); + + asCDataType dt; + if( getId ) + dt = engine->scriptFunctions[getId]->returnType; + else + dt = engine->scriptFunctions[setId]->parameterTypes[0]; + + // Just change the type, the context must still maintain information + // about previous variable offset and the indicator of temporary variable. + int offset = ctx->type.stackOffset; + bool isTemp = ctx->type.isTemporary; + ctx->type.Set(dt); + ctx->type.stackOffset = offset; + ctx->type.isTemporary = isTemp; + + return 1; + } + + // No accessor was found + return 0; +} + +int asCCompiler::ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext *arg, asCScriptNode *node) +{ + // TODO: A lot of this code is similar to ProcessPropertyGetAccessor. Can we unify them? + + if( !ctx->property_set ) + { + Error(TXT_PROPERTY_HAS_NO_SET_ACCESSOR, node); + return -1; + } + + // Setup the context with the original type so the method call gets built correctly + asCTypeInfo objType = ctx->type; + asCScriptFunction *func = engine->scriptFunctions[ctx->property_set]; + ctx->type.dataType = asCDataType::CreateObject(func->objectType, ctx->property_const); + if( ctx->property_handle ) + ctx->type.dataType.MakeHandle(true); + ctx->type.dataType.MakeReference(true); + + // Don't allow the call if the object is read-only and the property accessor is not const + // TODO: This can probably be moved into MakeFunctionCall + if( ctx->property_const && !func->isReadOnly ) + { + Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node); + asCArray funcs; + funcs.PushLast(ctx->property_set); + PrintMatchingFuncs(funcs, node); + } + + // Call the accessor + asCArray args; + args.PushLast(arg); + MakeFunctionCall(ctx, ctx->property_set, func->objectType, args, node); + + // TODO: This is from CompileExpressionPostOp, can we unify the code? + if( objType.isTemporary && + ctx->type.dataType.IsReference() && + !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member + { + // Remember the original object's variable, so that it can be released + // later on when the reference to its member goes out of scope + ctx->type.isTemporary = true; + ctx->type.stackOffset = objType.stackOffset; + } + else + { + // As the method didn't return a reference to a member + // we can safely release the original object now + ReleaseTemporaryVariable(objType, &ctx->bc); + } + + ctx->property_get = 0; + ctx->property_set = 0; + + return 0; +} + +void asCCompiler::ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode *node) +{ + // If no property accessor has been prepared then don't do anything + if( !ctx->property_get && !ctx->property_set ) + return; + + if( !ctx->property_get ) + { + // Raise error on missing accessor + Error(TXT_PROPERTY_HAS_NO_GET_ACCESSOR, node); + ctx->type.SetDummy(); + return; + } + + // Setup the context with the original type so the method call gets built correctly + asCTypeInfo objType = ctx->type; + asCScriptFunction *func = engine->scriptFunctions[ctx->property_get]; + ctx->type.dataType = asCDataType::CreateObject(func->objectType, ctx->property_const); + if( ctx->property_handle ) ctx->type.dataType.MakeHandle(true); + ctx->type.dataType.MakeReference(true); + + // Don't allow the call if the object is read-only and the property accessor is not const + if( ctx->property_const && !func->isReadOnly ) + { + Error(TXT_NON_CONST_METHOD_ON_CONST_OBJ, node); + asCArray funcs; + funcs.PushLast(ctx->property_get); + PrintMatchingFuncs(funcs, node); + } + + // Call the accessor + asCArray args; + MakeFunctionCall(ctx, ctx->property_get, func->objectType, args, node); + + // TODO: This is from CompileExpressionPostOp, can we unify the code? + if( objType.isTemporary && + ctx->type.dataType.IsReference() && + !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member + { + // Remember the original object's variable, so that it can be released + // later on when the reference to its member goes out of scope + ctx->type.isTemporary = true; + ctx->type.stackOffset = objType.stackOffset; + } + else + { + // As the method didn't return a reference to a member + // we can safely release the original object now + ReleaseTemporaryVariable(objType, &ctx->bc); + } + + ctx->property_get = 0; + ctx->property_set = 0; +} + +int asCCompiler::CompileExpressionPostOp(asCScriptNode *node, asSExprContext *ctx) +{ + int op = node->tokenType; + + // Check if the variable is initialized (if it indeed is a variable) + IsVariableInitialized(&ctx->type, node); + + if( op == ttInc || op == ttDec ) + { + if( globalExpression ) + { + Error(TXT_INC_OP_IN_GLOBAL_EXPR, node); + return -1; + } + + // Make sure the reference isn't a temporary variable + if( ctx->type.isTemporary ) + { + Error(TXT_REF_IS_TEMP, node); + return -1; + } + if( ctx->type.dataType.IsReadOnly() ) + { + Error(TXT_REF_IS_READ_ONLY, node); + return -1; + } + + if( ctx->type.isVariable ) + ConvertToReference(ctx); + else if( !ctx->type.dataType.IsReference() ) + { + Error(TXT_NOT_VALID_REFERENCE, node); + return -1; + } + + // Copy the value to a temp before changing it + ConvertToTempVariable(ctx); + + // Increment the value pointed to by the reference still in the register + asEBCInstr iInc = asBC_INCi, iDec = asBC_DECi; + if( ctx->type.dataType.IsDoubleType() ) + { + iInc = asBC_INCd; + iDec = asBC_DECd; + } + else if( ctx->type.dataType.IsFloatType() ) + { + iInc = asBC_INCf; + iDec = asBC_DECf; + } + else if( ctx->type.dataType.IsIntegerType() || ctx->type.dataType.IsUnsignedType() ) + { + if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt16, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt16, false)) ) + { + iInc = asBC_INCi16; + iDec = asBC_DECi16; + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt8, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt8, false)) ) + { + iInc = asBC_INCi8; + iDec = asBC_DECi8; + } + else if( ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttInt64, false)) || + ctx->type.dataType.IsEqualExceptRef(asCDataType::CreatePrimitive(ttUInt64, false)) ) + { + iInc = asBC_INCi64; + iDec = asBC_DECi64; + } + } + else + { + Error(TXT_ILLEGAL_OPERATION, node); + return -1; + } + + if( op == ttInc ) ctx->bc.Instr(iInc); else ctx->bc.Instr(iDec); + } + else if( op == ttDot ) + { + if( node->firstChild->nodeType == snIdentifier ) + { + ProcessPropertyGetAccessor(ctx, node); + + // Get the property name + asCString name(&script->code[node->firstChild->tokenPos], node->firstChild->tokenLength); + + // We need to look for get/set property accessors. + // If found, the context stores information on the get/set accessors + // until it is known which is to be used. + int r = FindPropertyAccessor(name, ctx, node); + if( r != 0 ) + return r; + + if( !ctx->type.dataType.IsPrimitive() ) + Dereference(ctx, true); + + if( ctx->type.dataType.IsObjectHandle() ) + { + // Convert the handle to a normal object + asCDataType dt = ctx->type.dataType; + dt.MakeHandle(false); + + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV); + } + + // Find the property offset and type + if( ctx->type.dataType.IsObject() ) + { + bool isConst = ctx->type.dataType.IsReadOnly(); + + asCObjectProperty *prop = builder->GetObjectProperty(ctx->type.dataType, name.AddressOf()); + if( prop ) + { + // Put the offset on the stack + ctx->bc.InstrINT(asBC_ADDSi, prop->byteOffset); + + if( prop->type.IsReference() ) + ctx->bc.Instr(asBC_RDSPTR); + + // Reference to primitive must be stored in the temp register + if( prop->type.IsPrimitive() ) + { + // The ADD offset command should store the reference in the register directly + ctx->bc.Instr(asBC_PopRPtr); + } + + // Set the new type (keeping info about temp variable) + ctx->type.dataType = prop->type; + ctx->type.dataType.MakeReference(true); + ctx->type.isVariable = false; + + if( ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle() ) + { + // Objects that are members are not references + ctx->type.dataType.MakeReference(false); + } + + ctx->type.dataType.MakeReadOnly(isConst ? true : prop->type.IsReadOnly()); + } + else + { + asCString str; + str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + return -1; + } + } + else + { + asCString str; + str.Format(TXT_s_NOT_MEMBER_OF_s, name.AddressOf(), ctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + return -1; + } + } + else + { + if( globalExpression ) + { + Error(TXT_METHOD_IN_GLOBAL_EXPR, node); + return -1; + } + + // Make sure it is an object we are accessing + if( !ctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, ctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + return -1; + } + + // Process the get property accessor + ProcessPropertyGetAccessor(ctx, node); + + bool isConst = false; + if( ctx->type.dataType.IsObjectHandle() ) + isConst = ctx->type.dataType.IsHandleToConst(); + else + isConst = ctx->type.dataType.IsReadOnly(); + + asCObjectType *trueObj = ctx->type.dataType.GetObjectType(); + + asCTypeInfo objType = ctx->type; + + // Compile function call + CompileFunctionCall(node->firstChild, ctx, trueObj, isConst); + + // If the method returned a reference, then we can't release the original + // object yet, because the reference may be to a member of it + if( objType.isTemporary && + (ctx->type.dataType.IsReference() || (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())) && + !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not a member + { + // Remember the original object's variable, so that it can be released + // later on when the reference to its member goes out of scope + ctx->type.isTemporary = true; + ctx->type.stackOffset = objType.stackOffset; + } + else + { + // As the method didn't return a reference to a member + // we can safely release the original object now + ReleaseTemporaryVariable(objType, &ctx->bc); + } + } + } + else if( op == ttOpenBracket ) + { + if( !ctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + return -1; + } + + ProcessPropertyGetAccessor(ctx, node); + + Dereference(ctx, true); + bool isConst = ctx->type.dataType.IsReadOnly(); + + if( ctx->type.dataType.IsObjectHandle() ) + { + // Convert the handle to a normal object + asCDataType dt = ctx->type.dataType; + dt.MakeHandle(false); + + ImplicitConversion(ctx, dt, node, asIC_IMPLICIT_CONV); + } + + // Compile the expression + asSExprContext expr(engine); + CompileAssignment(node->firstChild, &expr); + + asCTypeInfo objType = ctx->type; + asSTypeBehaviour *beh = ctx->type.dataType.GetBehaviour(); + if( beh == 0 ) + { + asCString str; + str.Format(TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP, ctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + return -1; + } + else + { + // Now find a matching function for the object type and indexing type + asCArray ops; + asUINT n; + if( isConst ) + { + // Only list const behaviours + for( n = 0; n < beh->operators.GetLength(); n += 2 ) + { + if( asBEHAVE_INDEX == beh->operators[n] && engine->scriptFunctions[beh->operators[n+1]]->isReadOnly ) + ops.PushLast(beh->operators[n+1]); + } + } + else + { + // TODO: Prefer non-const over const + for( n = 0; n < beh->operators.GetLength(); n += 2 ) + { + if( asBEHAVE_INDEX == beh->operators[n] ) + ops.PushLast(beh->operators[n+1]); + } + } + + asCArray ops1; + MatchArgument(ops, ops1, &expr.type, 0); + + if( !isConst ) + FilterConst(ops1); + + // Did we find a suitable function? + if( ops1.GetLength() == 1 ) + { + asCScriptFunction *descr = engine->scriptFunctions[ops1[0]]; + + // Store the code for the object + asCByteCode objBC(engine); + objBC.AddCode(&ctx->bc); + + // Add code for arguments + + PrepareArgument(&descr->parameterTypes[0], &expr, node->firstChild, true, descr->inOutFlags[0]); + MergeExprContexts(ctx, &expr); + + if( descr->parameterTypes[0].IsReference() ) + { + if( descr->parameterTypes[0].IsObject() && !descr->parameterTypes[0].IsObjectHandle() ) + ctx->bc.InstrWORD(asBC_GETOBJREF, 0); + else + ctx->bc.InstrWORD(asBC_GETREF, 0); + } + else if( descr->parameterTypes[0].IsObject() ) + { + ctx->bc.InstrWORD(asBC_GETOBJ, 0); + + // The temporary variable must not be freed as it will no longer hold an object + DeallocateVariable(expr.type.stackOffset); + expr.type.isTemporary = false; + } + + // Add the code for the object again + ctx->bc.AddCode(&objBC); + + asCArray args; + args.PushLast(&expr); + PerformFunctionCall(descr->id, ctx, false, &args); + } + else if( ops.GetLength() > 1 ) + { + Error(TXT_MORE_THAN_ONE_MATCHING_OP, node); + PrintMatchingFuncs(ops, node); + + return -1; + } + else + { + asCString str; + str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPE_s, expr.type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + return -1; + } + } + + // If the method returned a reference, then we can't release the original + // object yet, because the reference may be to a member of it + if( objType.isTemporary && + (ctx->type.dataType.IsReference() || (ctx->type.dataType.IsObject() && !ctx->type.dataType.IsObjectHandle())) && + !ctx->type.isVariable ) // If the resulting type is a variable, then the reference is not to a member + { + // Remember the object's variable, so that it can be released + // later on when the reference to its member goes out of scope + ctx->type.isTemporary = true; + ctx->type.stackOffset = objType.stackOffset; + } + else + { + // As the index operator didn't return a reference to a + // member we can release the original object now + ReleaseTemporaryVariable(objType, &ctx->bc); + } + } + + return 0; +} + +int asCCompiler::GetPrecedence(asCScriptNode *op) +{ + // x * y, x / y, x % y + // x + y, x - y + // x <= y, x < y, x >= y, x > y + // x = =y, x != y, x xor y, x is y, x !is y + // x and y + // x or y + + // The following are not used in this function, + // but should have lower precedence than the above + // x ? y : z + // x = y + + // The expression term have the highest precedence + if( op->nodeType == snExprTerm ) + return 1; + + // Evaluate operators by token + int tokenType = op->tokenType; + if( tokenType == ttStar || tokenType == ttSlash || tokenType == ttPercent ) + return 0; + + if( tokenType == ttPlus || tokenType == ttMinus ) + return -1; + + if( tokenType == ttBitShiftLeft || + tokenType == ttBitShiftRight || + tokenType == ttBitShiftRightArith ) + return -2; + + if( tokenType == ttAmp ) + return -3; + + if( tokenType == ttBitXor ) + return -4; + + if( tokenType == ttBitOr ) + return -5; + + if( tokenType == ttLessThanOrEqual || + tokenType == ttLessThan || + tokenType == ttGreaterThanOrEqual || + tokenType == ttGreaterThan ) + return -6; + + if( tokenType == ttEqual || tokenType == ttNotEqual || tokenType == ttXor || tokenType == ttIs || tokenType == ttNotIs ) + return -7; + + if( tokenType == ttAnd ) + return -8; + + if( tokenType == ttOr ) + return -9; + + // Unknown operator + asASSERT(false); + + return 0; +} + +int asCCompiler::MatchArgument(asCArray &funcs, asCArray &matches, const asCTypeInfo *argType, int paramNum, bool allowObjectConstruct) +{ + bool isExactMatch = false; + bool isMatchExceptConst = false; + bool isMatchWithBaseType = false; + bool isMatchExceptSign = false; + bool isMatchNotVarType = false; + + asUINT n; + + matches.SetLength(0); + + for( n = 0; n < funcs.GetLength(); n++ ) + { + asCScriptFunction *desc = builder->GetFunctionDescription(funcs[n]); + + // Does the function have arguments enough? + if( (int)desc->parameterTypes.GetLength() <= paramNum ) + continue; + + // Can we make the match by implicit conversion? + asSExprContext ti(engine); + ti.type = *argType; + if( argType->dataType.IsPrimitive() ) ti.type.dataType.MakeReference(false); + ImplicitConversion(&ti, desc->parameterTypes[paramNum], 0, asIC_IMPLICIT_CONV, false, 0, allowObjectConstruct); + if( desc->parameterTypes[paramNum].IsEqualExceptRef(ti.type.dataType) ) + { + // Is it an exact match? + if( argType->dataType.IsEqualExceptRef(ti.type.dataType) ) + { + if( !isExactMatch ) matches.SetLength(0); + + isExactMatch = true; + + matches.PushLast(funcs[n]); + continue; + } + + if( !isExactMatch ) + { + // Is it a match except const? + if( argType->dataType.IsEqualExceptRefAndConst(ti.type.dataType) ) + { + if( !isMatchExceptConst ) matches.SetLength(0); + + isMatchExceptConst = true; + + matches.PushLast(funcs[n]); + continue; + } + + if( !isMatchExceptConst ) + { + // Is it a size promotion, e.g. int8 -> int? + if( argType->dataType.IsSamePrimitiveBaseType(ti.type.dataType) ) + { + if( !isMatchWithBaseType ) matches.SetLength(0); + + isMatchWithBaseType = true; + + matches.PushLast(funcs[n]); + continue; + } + + if( !isMatchWithBaseType ) + { + // Conversion between signed and unsigned integer is better than between integer and float + + // Is it a match except for sign? + if( (argType->dataType.IsIntegerType() && ti.type.dataType.IsUnsignedType()) || + (argType->dataType.IsUnsignedType() && ti.type.dataType.IsIntegerType()) ) + { + if( !isMatchExceptSign ) matches.SetLength(0); + + isMatchExceptSign = true; + + matches.PushLast(funcs[n]); + continue; + } + + if( !isMatchExceptSign ) + { + // If there was any match without a var type it has higher priority + if( desc->parameterTypes[paramNum].GetTokenType() != ttQuestion ) + { + if( !isMatchNotVarType ) matches.SetLength(0); + + isMatchNotVarType = true; + + matches.PushLast(funcs[n]); + continue; + } + + // Implicit conversion to ?& has the smallest priority + if( !isMatchNotVarType ) + matches.PushLast(funcs[n]); + } + } + } + } + } + } + + return (int)matches.GetLength(); +} + +void asCCompiler::PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asCDataType *paramType, bool isFunction, int refType, asCArray *reservedVars) +{ + asSExprContext e(engine); + + // Reference parameters whose value won't be used don't evaluate the expression + if( !paramType->IsReference() || (refType & 1) ) + { + MergeExprContexts(&e, arg); + } + else + { + // Store the original bytecode so that it can be reused when processing the deferred output parameter + asSExprContext *orig = asNEW(asSExprContext)(engine); + MergeExprContexts(orig, arg); + orig->exprNode = arg->exprNode; + orig->type = arg->type; + orig->property_get = arg->property_get; + orig->property_set = arg->property_set; + orig->property_const = arg->property_const; + orig->property_handle = arg->property_handle; + + arg->origExpr = orig; + } + + e.type = arg->type; + e.property_get = arg->property_get; + e.property_set = arg->property_set; + e.property_const = arg->property_const; + e.property_handle = arg->property_handle; + PrepareArgument(paramType, &e, arg->exprNode, isFunction, refType, reservedVars); + arg->type = e.type; + ctx->bc.AddCode(&e.bc); +} + +bool asCCompiler::CompileOverloadedDualOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +{ + // What type of operator is it? + int token = node->tokenType; + if( token == ttUnrecognizedToken ) + { + // This happens when the compiler is inferring an assignment + // operation from another action, for example in preparing a value + // as a function argument + token = ttAssignment; + } + + // boolean operators are not overloadable + if( token == ttAnd || + token == ttOr || + token == ttXor ) + return false; + + // Dual operators can also be implemented as class methods + if( token == ttEqual || + token == ttNotEqual ) + { + // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used + // Find the matching opEquals method + int r = CompileOverloadedDualOperator2(node, "opEquals", lctx, rctx, ctx, true, asCDataType::CreatePrimitive(ttBool, false)); + if( r == 0 ) + { + // Try again by switching the order of the operands + r = CompileOverloadedDualOperator2(node, "opEquals", rctx, lctx, ctx, true, asCDataType::CreatePrimitive(ttBool, false)); + } + + if( r == 1 ) + { + if( token == ttNotEqual ) + ctx->bc.InstrSHORT(asBC_NOT, ctx->type.stackOffset); + + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); + return true; + } + } + + if( token == ttEqual || + token == ttNotEqual || + token == ttLessThan || + token == ttLessThanOrEqual || + token == ttGreaterThan || + token == ttGreaterThanOrEqual ) + { + bool swappedOrder = false; + + // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used + // Find the matching opCmp method + int r = CompileOverloadedDualOperator2(node, "opCmp", lctx, rctx, ctx, true, asCDataType::CreatePrimitive(ttInt, false)); + if( r == 0 ) + { + // Try again by switching the order of the operands + swappedOrder = true; + r = CompileOverloadedDualOperator2(node, "opCmp", rctx, lctx, ctx, true, asCDataType::CreatePrimitive(ttInt, false)); + } + + if( r == 1 ) + { + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + + int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true); + + ctx->bc.InstrW_DW(asBC_CMPIi, ctx->type.stackOffset, 0); + + if( token == ttEqual ) + ctx->bc.Instr(asBC_TZ); + else if( token == ttNotEqual ) + ctx->bc.Instr(asBC_TNZ); + else if( (token == ttLessThan && !swappedOrder) || + (token == ttGreaterThan && swappedOrder) ) + ctx->bc.Instr(asBC_TS); + else if( (token == ttLessThanOrEqual && !swappedOrder) || + (token == ttGreaterThanOrEqual && swappedOrder) ) + ctx->bc.Instr(asBC_TNP); + else if( (token == ttGreaterThan && !swappedOrder) || + (token == ttLessThan && swappedOrder) ) + ctx->bc.Instr(asBC_TP); + else if( (token == ttGreaterThanOrEqual && !swappedOrder) || + (token == ttLessThanOrEqual && swappedOrder) ) + ctx->bc.Instr(asBC_TNS); + + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), a, true); + + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); + return true; + } + } + + // The rest of the operators are not commutative, and doesn't require specific return type + const char *op = 0, *op_r = 0; + switch( token ) + { + case ttPlus: op = "opAdd"; op_r = "opAdd_r"; break; + case ttMinus: op = "opSub"; op_r = "opSub_r"; break; + case ttStar: op = "opMul"; op_r = "opMul_r"; break; + case ttSlash: op = "opDiv"; op_r = "opDiv_r"; break; + case ttPercent: op = "opMod"; op_r = "opMod_r"; break; + case ttBitOr: op = "opOr"; op_r = "opOr_r"; break; + case ttAmp: op = "opAnd"; op_r = "opAnd_r"; break; + case ttBitXor: op = "opXor"; op_r = "opXor_r"; break; + case ttBitShiftLeft: op = "opShl"; op_r = "opShl_r"; break; + case ttBitShiftRight: op = "opShr"; op_r = "opShr_r"; break; + case ttBitShiftRightArith: op = "opUShr"; op_r = "opUShr_r"; break; + } + + // TODO: Might be interesting to support a concatenation operator, e.g. ~ + + if( op && op_r ) + { + // TODO: Should evaluate which of the two have the best match. If both have equal match, the first version should be used + // Find the matching operator method + int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, ctx); + if( r == 0 ) + { + // Try again by switching the order of the operands, and using the reversed operator + r = CompileOverloadedDualOperator2(node, op_r, rctx, lctx, ctx); + } + + if( r == 1 ) + { + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + ctx->type.SetDummy(); + return true; + } + } + + // Assignment operators + op = 0; + switch( token ) + { + case ttAssignment: op = "opAssign"; break; + case ttAddAssign: op = "opAddAssign"; break; + case ttSubAssign: op = "opSubAssign"; break; + case ttMulAssign: op = "opMulAssign"; break; + case ttDivAssign: op = "opDivAssign"; break; + case ttModAssign: op = "opModAssign"; break; + case ttOrAssign: op = "opOrAssign"; break; + case ttAndAssign: op = "opAndAssign"; break; + case ttXorAssign: op = "opXorAssign"; break; + case ttShiftLeftAssign: op = "opShlAssign"; break; + case ttShiftRightLAssign: op = "opShrAssign"; break; + case ttShiftRightAAssign: op = "opUShrAssign"; break; + } + + if( op ) + { + // TODO: Shouldn't accept const lvalue with the assignment operators + + // Find the matching operator method + int r = CompileOverloadedDualOperator2(node, op, lctx, rctx, ctx); + if( r == 1 ) + { + // Success, don't continue + return true; + } + else if( r < 0 ) + { + // Compiler error, don't continue + ctx->type.SetDummy(); + return true; + } + } + + // No suitable operator was found + return false; +} + +// Returns negative on compile error +// zero on no matching operator +// one on matching operator +int asCCompiler::CompileOverloadedDualOperator2(asCScriptNode *node, const char *methodName, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx, bool specificReturn, const asCDataType &returnType) +{ + // Find the matching method + if( lctx->type.dataType.IsObject() && !lctx->type.isExplicitHandle ) + { + // Is the left value a const? + bool isConst = false; + if( lctx->type.dataType.IsObjectHandle() ) + isConst = lctx->type.dataType.IsHandleToConst(); + else + isConst = lctx->type.dataType.IsReadOnly(); + + asCArray funcs; + asCObjectType *ot = lctx->type.dataType.GetObjectType(); + for( asUINT n = 0; n < ot->methods.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[ot->methods[n]]; + if( func->name == methodName && + (!specificReturn || func->returnType == returnType) && + func->parameterTypes.GetLength() == 1 && + (!isConst || func->isReadOnly) ) + { + // Make sure the method is accessible by the module + asCConfigGroup *group = engine->FindConfigGroupForFunction(func->id); + if( !group || group->HasModuleAccess(builder->module->name.AddressOf()) ) + funcs.PushLast(func->id); + } + } + + // Which is the best matching function? + asCArray ops; + MatchArgument(funcs, ops, &rctx->type, 0); + + // Did we find an operator? + if( ops.GetLength() == 1 ) + { + // Process the lctx expression as get accessor + ProcessPropertyGetAccessor(lctx, node); + + // Merge the bytecode so that it forms lvalue.methodName(rvalue) + asCTypeInfo objType = lctx->type; + asCArray args; + args.PushLast(rctx); + MergeExprContexts(ctx, lctx); + ctx->type = lctx->type; + MakeFunctionCall(ctx, ops[0], objType.dataType.GetObjectType(), args, node); + + // TODO: Can we do this here? + ReleaseTemporaryVariable(objType, &ctx->bc); + + // Found matching operator + return 1; + } + else if( ops.GetLength() > 1 ) + { + Error(TXT_MORE_THAN_ONE_MATCHING_OP, node); + PrintMatchingFuncs(ops, node); + + ctx->type.SetDummy(); + + // Compiler error + return -1; + } + } + + // No matching operator + return 0; +} + +void asCCompiler::MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode *node, bool useVariable, int stackOffset) +{ + if( objectType ) + { + Dereference(ctx, true); + + // Warn if the method is non-const and the object is temporary + // since the changes will be lost when the object is destroyed. + // If the object is accessed through a handle, then it is assumed + // the object is not temporary, even though the handle is. + if( ctx->type.isTemporary && + !ctx->type.dataType.IsObjectHandle() && + !engine->scriptFunctions[funcId]->isReadOnly ) + { + Warning(TXT_CALLING_NONCONST_METHOD_ON_TEMP, node); + } + } + + asCByteCode objBC(engine); + objBC.AddCode(&ctx->bc); + + PrepareFunctionCall(funcId, &ctx->bc, args); + + // Verify if any of the args variable offsets are used in the other code. + // If they are exchange the offset for a new one + asUINT n; + for( n = 0; n < args.GetLength(); n++ ) + { + if( args[n]->type.isTemporary && objBC.IsVarUsed(args[n]->type.stackOffset) ) + { + // Release the current temporary variable + ReleaseTemporaryVariable(args[n]->type, 0); + + asCArray usedVars; + objBC.GetVarsUsed(usedVars); + ctx->bc.GetVarsUsed(usedVars); + + asCDataType dt = args[n]->type.dataType; + dt.MakeReference(false); + int newOffset = AllocateVariableNotIn(dt, true, &usedVars); + + ctx->bc.ExchangeVar(args[n]->type.stackOffset, newOffset); + args[n]->type.stackOffset = (short)newOffset; + args[n]->type.isTemporary = true; + args[n]->type.isVariable = true; + } + } + + ctx->bc.AddCode(&objBC); + + MoveArgsToStack(funcId, &ctx->bc, args, objectType ? true : false); + + PerformFunctionCall(funcId, ctx, false, &args, 0, useVariable, stackOffset); +} + +int asCCompiler::CompileOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +{ + IsVariableInitialized(&lctx->type, node); + IsVariableInitialized(&rctx->type, node); + + if( lctx->type.isExplicitHandle || rctx->type.isExplicitHandle || + node->tokenType == ttIs || node->tokenType == ttNotIs ) + { + CompileOperatorOnHandles(node, lctx, rctx, ctx); + return 0; + } + else + { + // Compile an overloaded operator for the two operands + if( CompileOverloadedDualOperator(node, lctx, rctx, ctx) ) + return 0; + + // If both operands are objects, then we shouldn't continue + if( lctx->type.dataType.IsObject() && rctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s, lctx->type.dataType.Format().AddressOf(), rctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + ctx->type.SetDummy(); + return -1; + } + + // Make sure we have two variables or constants + if( lctx->type.dataType.IsReference() ) ConvertToVariableNotIn(lctx, rctx); + if( rctx->type.dataType.IsReference() ) ConvertToVariableNotIn(rctx, lctx); + + // Process the property get accessors (if any) + ProcessPropertyGetAccessor(lctx, node); + ProcessPropertyGetAccessor(rctx, node); + + // Make sure lctx doesn't end up with a variable used in rctx + if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) ) + { + asCArray vars; + rctx->bc.GetVarsUsed(vars); + int offset = AllocateVariable(lctx->type.dataType, true); + rctx->bc.ExchangeVar(lctx->type.stackOffset, offset); + ReleaseTemporaryVariable(offset, 0); + } + + // Math operators + // + - * / % += -= *= /= %= + int op = node->tokenType; + if( op == ttPlus || op == ttAddAssign || + op == ttMinus || op == ttSubAssign || + op == ttStar || op == ttMulAssign || + op == ttSlash || op == ttDivAssign || + op == ttPercent || op == ttModAssign ) + { + CompileMathOperator(node, lctx, rctx, ctx); + return 0; + } + + // Bitwise operators + // << >> >>> & | ^ <<= >>= >>>= &= |= ^= + if( op == ttAmp || op == ttAndAssign || + op == ttBitOr || op == ttOrAssign || + op == ttBitXor || op == ttXorAssign || + op == ttBitShiftLeft || op == ttShiftLeftAssign || + op == ttBitShiftRight || op == ttShiftRightLAssign || + op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + { + CompileBitwiseOperator(node, lctx, rctx, ctx); + return 0; + } + + // Comparison operators + // == != < > <= >= + if( op == ttEqual || op == ttNotEqual || + op == ttLessThan || op == ttLessThanOrEqual || + op == ttGreaterThan || op == ttGreaterThanOrEqual ) + { + CompileComparisonOperator(node, lctx, rctx, ctx); + return 0; + } + + // Boolean operators + // && || ^^ + if( op == ttAnd || op == ttOr || op == ttXor ) + { + CompileBooleanOperator(node, lctx, rctx, ctx); + return 0; + } + } + + asASSERT(false); + return -1; +} + +void asCCompiler::ConvertToTempVariableNotIn(asSExprContext *ctx, asSExprContext *exclude) +{ + asCArray excludeVars; + if( exclude ) exclude->bc.GetVarsUsed(excludeVars); + ConvertToTempVariableNotIn(ctx, &excludeVars); +} + +void asCCompiler::ConvertToTempVariableNotIn(asSExprContext *ctx, asCArray *reservedVars) +{ + // This is only used for primitive types and null handles + asASSERT( ctx->type.dataType.IsPrimitive() || ctx->type.dataType.IsNullHandle() ); + + ConvertToVariableNotIn(ctx, reservedVars); + if( !ctx->type.isTemporary ) + { + if( ctx->type.dataType.IsPrimitive() ) + { + // Copy the variable to a temporary variable + int offset = AllocateVariableNotIn(ctx->type.dataType, true, reservedVars); + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrW_W(asBC_CpyVtoV4, offset, ctx->type.stackOffset); + else + ctx->bc.InstrW_W(asBC_CpyVtoV8, offset, ctx->type.stackOffset); + ctx->type.SetVariable(ctx->type.dataType, offset, true); + } + else + { + // We should never get here + asASSERT(false); + } + } +} + +void asCCompiler::ConvertToTempVariable(asSExprContext *ctx) +{ + ConvertToTempVariableNotIn(ctx, (asCArray*)0); +} + +void asCCompiler::ConvertToVariable(asSExprContext *ctx) +{ + ConvertToVariableNotIn(ctx, (asCArray*)0); +} + +void asCCompiler::ConvertToVariableNotIn(asSExprContext *ctx, asCArray *reservedVars) +{ + if( !ctx->type.isVariable ) + { + asCArray excludeVars; + if( reservedVars ) excludeVars.Concatenate(*reservedVars); + int offset; + if( ctx->type.dataType.IsObjectHandle() ) + { + offset = AllocateVariableNotIn(ctx->type.dataType, true, &excludeVars); + if( ctx->type.IsNullConstant() ) + { + // TODO: Adapt pointer size + ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, 0); + } + else + { + // Copy the object handle to a variable + ctx->bc.InstrSHORT(asBC_PSF, (short)offset); + ctx->bc.InstrPTR(asBC_REFCPY, ctx->type.dataType.GetObjectType()); + ctx->bc.Pop(AS_PTR_SIZE); + } + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + ctx->type.SetVariable(ctx->type.dataType, offset, true); + } + else if( ctx->type.dataType.IsPrimitive() ) + { + if( ctx->type.isConstant ) + { + offset = AllocateVariableNotIn(ctx->type.dataType, true, &excludeVars); + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, ctx->type.byteValue); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + ctx->bc.InstrSHORT_W(asBC_SetV2, (short)offset, ctx->type.wordValue); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 4 ) + ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, ctx->type.dwordValue); + else + ctx->bc.InstrSHORT_QW(asBC_SetV8, (short)offset, ctx->type.qwordValue); + + ctx->type.SetVariable(ctx->type.dataType, offset, true); + return; + } + else + { + asASSERT(ctx->type.dataType.IsPrimitive()); + asASSERT(ctx->type.dataType.IsReference()); + + ctx->type.dataType.MakeReference(false); + offset = AllocateVariableNotIn(ctx->type.dataType, true, &excludeVars); + + // Read the value from the address in the register directly into the variable + if( ctx->type.dataType.GetSizeInMemoryBytes() == 1 ) + ctx->bc.InstrSHORT(asBC_RDR1, (short)offset); + else if( ctx->type.dataType.GetSizeInMemoryBytes() == 2 ) + ctx->bc.InstrSHORT(asBC_RDR2, (short)offset); + else if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_RDR4, (short)offset); + else + ctx->bc.InstrSHORT(asBC_RDR8, (short)offset); + } + + ReleaseTemporaryVariable(ctx->type, &ctx->bc); + ctx->type.SetVariable(ctx->type.dataType, offset, true); + } + } +} + +void asCCompiler::ConvertToVariableNotIn(asSExprContext *ctx, asSExprContext *exclude) +{ + asCArray excludeVars; + if( exclude ) exclude->bc.GetVarsUsed(excludeVars); + ConvertToVariableNotIn(ctx, &excludeVars); +} + + +void asCCompiler::CompileMathOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +{ + // TODO: If a constant is only using 32bits, then a 32bit operation is preferred + + // Implicitly convert the operands to a number type + asCDataType to; + if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) + to.SetTokenType(ttDouble); + else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() ) + to.SetTokenType(ttFloat); + else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() ) + to.SetTokenType(ttInt64); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt64); + } + else + { + if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() || + lctx->type.dataType.IsEnumType() || rctx->type.dataType.IsEnumType() ) + to.SetTokenType(ttInt); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt); + } + + // If doing an operation with double constant and float variable, the constant should be converted to float + if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) || + (rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) ) + to.SetTokenType(ttFloat); + + // Do the actual conversion + asCArray reservedVars; + rctx->bc.GetVarsUsed(reservedVars); + lctx->bc.GetVarsUsed(reservedVars); + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars); + + // Verify that the conversion was successful + if( !lctx->type.dataType.IsIntegerType() && + !lctx->type.dataType.IsUnsignedType() && + !lctx->type.dataType.IsFloatType() && + !lctx->type.dataType.IsDoubleType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, lctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + + ctx->type.SetDummy(); + return; + } + + if( !rctx->type.dataType.IsIntegerType() && + !rctx->type.dataType.IsUnsignedType() && + !rctx->type.dataType.IsFloatType() && + !rctx->type.dataType.IsDoubleType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_MATH_TYPE, rctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + + ctx->type.SetDummy(); + return; + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + // Verify if we are dividing with a constant zero + int op = node->tokenType; + if( rctx->type.isConstant && rctx->type.qwordValue == 0 && + (op == ttSlash || op == ttDivAssign || + op == ttPercent || op == ttModAssign) ) + { + Error(TXT_DIVIDE_BY_ZERO, node); + } + + if( !isConstant ) + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + if( op == ttAddAssign || op == ttSubAssign || + op == ttMulAssign || op == ttDivAssign || + op == ttModAssign ) + { + // Merge the operands in the different order so that they are evaluated correctly + MergeExprContexts(ctx, rctx); + MergeExprContexts(ctx, lctx); + } + else + { + MergeExprContexts(ctx, lctx); + MergeExprContexts(ctx, rctx); + } + + asEBCInstr instruction = asBC_ADDi; + if( lctx->type.dataType.IsIntegerType() || + lctx->type.dataType.IsUnsignedType() ) + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDi; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBi; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULi; + else if( op == ttSlash || op == ttDivAssign ) + instruction = asBC_DIVi; + else if( op == ttPercent || op == ttModAssign ) + instruction = asBC_MODi; + } + else + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDi64; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBi64; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULi64; + else if( op == ttSlash || op == ttDivAssign ) + instruction = asBC_DIVi64; + else if( op == ttPercent || op == ttModAssign ) + instruction = asBC_MODi64; + } + } + else if( lctx->type.dataType.IsFloatType() ) + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDf; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBf; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULf; + else if( op == ttSlash || op == ttDivAssign ) + instruction = asBC_DIVf; + else if( op == ttPercent || op == ttModAssign ) + instruction = asBC_MODf; + } + else if( lctx->type.dataType.IsDoubleType() ) + { + if( op == ttPlus || op == ttAddAssign ) + instruction = asBC_ADDd; + else if( op == ttMinus || op == ttSubAssign ) + instruction = asBC_SUBd; + else if( op == ttStar || op == ttMulAssign ) + instruction = asBC_MULd; + else if( op == ttSlash || op == ttDivAssign ) + instruction = asBC_DIVd; + else if( op == ttPercent || op == ttModAssign ) + instruction = asBC_MODd; + } + else + { + // Shouldn't be possible + asASSERT(false); + } + + // Do the operation + int a = AllocateVariable(lctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(instruction, a, b, c); + + ctx->type.SetVariable(lctx->type.dataType, a, true); + } + else + { + // Both values are constants + if( lctx->type.dataType.IsIntegerType() || + lctx->type.dataType.IsUnsignedType() ) + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + int v = 0; + if( op == ttPlus ) + v = lctx->type.intValue + rctx->type.intValue; + else if( op == ttMinus ) + v = lctx->type.intValue - rctx->type.intValue; + else if( op == ttStar ) + v = lctx->type.intValue * rctx->type.intValue; + else if( op == ttSlash ) + { + if( rctx->type.intValue == 0 ) + v = 0; + else + v = lctx->type.intValue / rctx->type.intValue; + } + else if( op == ttPercent ) + { + if( rctx->type.intValue == 0 ) + v = 0; + else + v = lctx->type.intValue % rctx->type.intValue; + } + + ctx->type.SetConstantDW(lctx->type.dataType, v); + + // If the right value is greater than the left value in a minus operation, then we need to convert the type to int + if( lctx->type.dataType.GetTokenType() == ttUInt && op == ttMinus && lctx->type.intValue < rctx->type.intValue ) + ctx->type.dataType.SetTokenType(ttInt); + } + else + { + asQWORD v = 0; + if( op == ttPlus ) + v = lctx->type.qwordValue + rctx->type.qwordValue; + else if( op == ttMinus ) + v = lctx->type.qwordValue - rctx->type.qwordValue; + else if( op == ttStar ) + v = lctx->type.qwordValue * rctx->type.qwordValue; + else if( op == ttSlash ) + { + if( rctx->type.qwordValue == 0 ) + v = 0; + else + v = lctx->type.qwordValue / rctx->type.qwordValue; + } + else if( op == ttPercent ) + { + if( rctx->type.qwordValue == 0 ) + v = 0; + else + v = lctx->type.qwordValue % rctx->type.qwordValue; + } + + ctx->type.SetConstantQW(lctx->type.dataType, v); + + // If the right value is greater than the left value in a minus operation, then we need to convert the type to int + if( lctx->type.dataType.GetTokenType() == ttUInt64 && op == ttMinus && lctx->type.qwordValue < rctx->type.qwordValue ) + ctx->type.dataType.SetTokenType(ttInt64); + } + } + else if( lctx->type.dataType.IsFloatType() ) + { + float v = 0.0f; + if( op == ttPlus ) + v = lctx->type.floatValue + rctx->type.floatValue; + else if( op == ttMinus ) + v = lctx->type.floatValue - rctx->type.floatValue; + else if( op == ttStar ) + v = lctx->type.floatValue * rctx->type.floatValue; + else if( op == ttSlash ) + { + if( rctx->type.floatValue == 0 ) + v = 0; + else + v = lctx->type.floatValue / rctx->type.floatValue; + } + else if( op == ttPercent ) + { + if( rctx->type.floatValue == 0 ) + v = 0; + else + v = fmodf(lctx->type.floatValue, rctx->type.floatValue); + } + + ctx->type.SetConstantF(lctx->type.dataType, v); + } + else if( lctx->type.dataType.IsDoubleType() ) + { + double v = 0.0; + if( op == ttPlus ) + v = lctx->type.doubleValue + rctx->type.doubleValue; + else if( op == ttMinus ) + v = lctx->type.doubleValue - rctx->type.doubleValue; + else if( op == ttStar ) + v = lctx->type.doubleValue * rctx->type.doubleValue; + else if( op == ttSlash ) + { + if( rctx->type.doubleValue == 0 ) + v = 0; + else + v = lctx->type.doubleValue / rctx->type.doubleValue; + } + else if( op == ttPercent ) + { + if( rctx->type.doubleValue == 0 ) + v = 0; + else + v = fmod(lctx->type.doubleValue, rctx->type.doubleValue); + } + + ctx->type.SetConstantD(lctx->type.dataType, v); + } + else + { + // Shouldn't be possible + asASSERT(false); + } + } +} + +void asCCompiler::CompileBitwiseOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +{ + // TODO: If a constant is only using 32bits, then a 32bit operation is preferred + + int op = node->tokenType; + if( op == ttAmp || op == ttAndAssign || + op == ttBitOr || op == ttOrAssign || + op == ttBitXor || op == ttXorAssign ) + { + // Convert left hand operand to integer if it's not already one + asCDataType to; + if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || + rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + to.SetTokenType(ttUInt64); + else + to.SetTokenType(ttUInt); + + // Do the actual conversion + asCArray reservedVars; + rctx->bc.GetVarsUsed(reservedVars); + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars); + + // Verify that the conversion was successful + if( !lctx->type.dataType.IsUnsignedType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + } + + // Convert right hand operand to same type as left hand operand + asCArray vars; + lctx->bc.GetVarsUsed(vars); + ImplicitConversion(rctx, lctx->type.dataType, node, asIC_IMPLICIT_CONV, true, &vars); + if( !rctx->type.dataType.IsEqualExceptRef(lctx->type.dataType) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), lctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + if( !isConstant ) + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + if( op == ttAndAssign || op == ttOrAssign || op == ttXorAssign ) + { + // Compound assignments execute the right hand value first + MergeExprContexts(ctx, rctx); + MergeExprContexts(ctx, lctx); + } + else + { + MergeExprContexts(ctx, lctx); + MergeExprContexts(ctx, rctx); + } + + asEBCInstr instruction = asBC_BAND; + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + if( op == ttAmp || op == ttAndAssign ) + instruction = asBC_BAND; + else if( op == ttBitOr || op == ttOrAssign ) + instruction = asBC_BOR; + else if( op == ttBitXor || op == ttXorAssign ) + instruction = asBC_BXOR; + } + else + { + if( op == ttAmp || op == ttAndAssign ) + instruction = asBC_BAND64; + else if( op == ttBitOr || op == ttOrAssign ) + instruction = asBC_BOR64; + else if( op == ttBitXor || op == ttXorAssign ) + instruction = asBC_BXOR64; + } + + // Do the operation + int a = AllocateVariable(lctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(instruction, a, b, c); + + ctx->type.SetVariable(lctx->type.dataType, a, true); + } + else + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + asQWORD v = 0; + if( op == ttAmp ) + v = lctx->type.qwordValue & rctx->type.qwordValue; + else if( op == ttBitOr ) + v = lctx->type.qwordValue | rctx->type.qwordValue; + else if( op == ttBitXor ) + v = lctx->type.qwordValue ^ rctx->type.qwordValue; + + // Remember the result + ctx->type.SetConstantQW(lctx->type.dataType, v); + } + else + { + asDWORD v = 0; + if( op == ttAmp ) + v = lctx->type.dwordValue & rctx->type.dwordValue; + else if( op == ttBitOr ) + v = lctx->type.dwordValue | rctx->type.dwordValue; + else if( op == ttBitXor ) + v = lctx->type.dwordValue ^ rctx->type.dwordValue; + + // Remember the result + ctx->type.SetConstantDW(lctx->type.dataType, v); + } + } + } + else if( op == ttBitShiftLeft || op == ttShiftLeftAssign || + op == ttBitShiftRight || op == ttShiftRightLAssign || + op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + { + // Don't permit object to primitive conversion, since we don't know which integer type is the correct one + if( lctx->type.dataType.IsObject() ) + { + asCString str; + str.Format(TXT_ILLEGAL_OPERATION_ON_s, lctx->type.dataType.Format().AddressOf()); + Error(str.AddressOf(), node); + + // Set an integer value and allow the compiler to continue + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttInt, true), 0); + return; + } + + // Convert left hand operand to integer if it's not already one + asCDataType to = lctx->type.dataType; + if( lctx->type.dataType.IsUnsignedType() && + lctx->type.dataType.GetSizeInMemoryBytes() < 4 ) + { + to = asCDataType::CreatePrimitive(ttUInt, false); + } + else if( !lctx->type.dataType.IsUnsignedType() ) + { + asCDataType to; + if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + to.SetTokenType(ttInt64); + else + to.SetTokenType(ttInt); + } + + // Do the actual conversion + asCArray reservedVars; + rctx->bc.GetVarsUsed(reservedVars); + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars); + + // Verify that the conversion was successful + if( lctx->type.dataType != to ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + } + + // Right operand must be 32bit uint + asCArray vars; + lctx->bc.GetVarsUsed(vars); + ImplicitConversion(rctx, asCDataType::CreatePrimitive(ttUInt, true), node, asIC_IMPLICIT_CONV, true, &vars); + if( !rctx->type.dataType.IsUnsignedType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), "uint"); + Error(str.AddressOf(), node); + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + if( !isConstant ) + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + if( op == ttShiftLeftAssign || op == ttShiftRightLAssign || op == ttShiftRightAAssign ) + { + // Compound assignments execute the right hand value first + MergeExprContexts(ctx, rctx); + MergeExprContexts(ctx, lctx); + } + else + { + MergeExprContexts(ctx, lctx); + MergeExprContexts(ctx, rctx); + } + + asEBCInstr instruction = asBC_BSLL; + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + if( op == ttBitShiftLeft || op == ttShiftLeftAssign ) + instruction = asBC_BSLL; + else if( op == ttBitShiftRight || op == ttShiftRightLAssign ) + instruction = asBC_BSRL; + else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + instruction = asBC_BSRA; + } + else + { + if( op == ttBitShiftLeft || op == ttShiftLeftAssign ) + instruction = asBC_BSLL64; + else if( op == ttBitShiftRight || op == ttShiftRightLAssign ) + instruction = asBC_BSRL64; + else if( op == ttBitShiftRightArith || op == ttShiftRightAAssign ) + instruction = asBC_BSRA64; + } + + // Do the operation + int a = AllocateVariable(lctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(instruction, a, b, c); + + ctx->type.SetVariable(lctx->type.dataType, a, true); + } + else + { + if( lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + asDWORD v = 0; + if( op == ttBitShiftLeft ) + v = lctx->type.dwordValue << rctx->type.dwordValue; + else if( op == ttBitShiftRight ) + v = lctx->type.dwordValue >> rctx->type.dwordValue; + else if( op == ttBitShiftRightArith ) + v = lctx->type.intValue >> rctx->type.dwordValue; + + ctx->type.SetConstantDW(lctx->type.dataType, v); + } + else + { + asQWORD v = 0; + if( op == ttBitShiftLeft ) + v = lctx->type.qwordValue << rctx->type.dwordValue; + else if( op == ttBitShiftRight ) + v = lctx->type.qwordValue >> rctx->type.dwordValue; + else if( op == ttBitShiftRightArith ) + v = asINT64(lctx->type.qwordValue) >> rctx->type.dwordValue; + + ctx->type.SetConstantQW(lctx->type.dataType, v); + } + } + } +} + +void asCCompiler::CompileComparisonOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +{ + // Both operands must be of the same type + + // Implicitly convert the operands to a number type + asCDataType to; + if( lctx->type.dataType.IsDoubleType() || rctx->type.dataType.IsDoubleType() ) + to.SetTokenType(ttDouble); + else if( lctx->type.dataType.IsFloatType() || rctx->type.dataType.IsFloatType() ) + to.SetTokenType(ttFloat); + else if( lctx->type.dataType.GetSizeInMemoryDWords() == 2 || rctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() ) + to.SetTokenType(ttInt64); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt64); + } + else + { + if( lctx->type.dataType.IsIntegerType() || rctx->type.dataType.IsIntegerType() || + lctx->type.dataType.IsEnumType() || rctx->type.dataType.IsEnumType() ) + to.SetTokenType(ttInt); + else if( lctx->type.dataType.IsUnsignedType() || rctx->type.dataType.IsUnsignedType() ) + to.SetTokenType(ttUInt); + else if( lctx->type.dataType.IsBooleanType() || rctx->type.dataType.IsBooleanType() ) + to.SetTokenType(ttBool); + } + + // If doing an operation with double constant and float variable, the constant should be converted to float + if( (lctx->type.isConstant && lctx->type.dataType.IsDoubleType() && !rctx->type.isConstant && rctx->type.dataType.IsFloatType()) || + (rctx->type.isConstant && rctx->type.dataType.IsDoubleType() && !lctx->type.isConstant && lctx->type.dataType.IsFloatType()) ) + to.SetTokenType(ttFloat); + + // Is it an operation on signed values? + bool signMismatch = false; + if( !lctx->type.dataType.IsUnsignedType() || !rctx->type.dataType.IsUnsignedType() ) + { + if( lctx->type.dataType.GetTokenType() == ttUInt64 ) + { + if( !lctx->type.isConstant ) + signMismatch = true; + else if( lctx->type.qwordValue & (I64(1)<<63) ) + signMismatch = true; + } + if( lctx->type.dataType.GetTokenType() == ttUInt ) + { + if( !lctx->type.isConstant ) + signMismatch = true; + else if( lctx->type.dwordValue & (1<<31) ) + signMismatch = true; + } + if( rctx->type.dataType.GetTokenType() == ttUInt64 ) + { + if( !rctx->type.isConstant ) + signMismatch = true; + else if( rctx->type.qwordValue & (I64(1)<<63) ) + signMismatch = true; + } + if( rctx->type.dataType.GetTokenType() == ttUInt ) + { + if( !rctx->type.isConstant ) + signMismatch = true; + else if( rctx->type.dwordValue & (1<<31) ) + signMismatch = true; + } + } + + // Check for signed/unsigned mismatch + if( signMismatch ) + Warning(TXT_SIGNED_UNSIGNED_MISMATCH, node); + + // Do the actual conversion + asCArray reservedVars; + rctx->bc.GetVarsUsed(reservedVars); + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV); + + // Verify that the conversion was successful + bool ok = true; + if( !lctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + ok = false; + } + + if( !rctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + ok = false; + } + + if( !ok ) + { + // It wasn't possible to get two valid operands, so we just return + // a boolean result and let the compiler continue. + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), true); + return; + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + int op = node->tokenType; + + if( !isConstant ) + { + if( to.IsBooleanType() ) + { + int op = node->tokenType; + if( op == ttEqual || op == ttNotEqual ) + { + // Must convert to temporary variable, because we are changing the value before comparison + ConvertToTempVariableNotIn(lctx, rctx); + ConvertToTempVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + // Make sure they are equal if not false + lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset); + rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset); + + MergeExprContexts(ctx, lctx); + MergeExprContexts(ctx, rctx); + + int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + if( op == ttEqual ) + { + ctx->bc.InstrW_W(asBC_CMPi,b,c); + ctx->bc.Instr(asBC_TZ); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + } + else if( op == ttNotEqual ) + { + ctx->bc.InstrW_W(asBC_CMPi,b,c); + ctx->bc.Instr(asBC_TNZ); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + } + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + } + else + { + // TODO: Use TXT_ILLEGAL_OPERATION_ON + Error(TXT_ILLEGAL_OPERATION, node); + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), 0); + } + } + else + { + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + MergeExprContexts(ctx, lctx); + MergeExprContexts(ctx, rctx); + + asEBCInstr iCmp = asBC_CMPi, iT = asBC_TZ; + + if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + iCmp = asBC_CMPi; + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + iCmp = asBC_CMPu; + else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + iCmp = asBC_CMPi64; + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + iCmp = asBC_CMPu64; + else if( lctx->type.dataType.IsFloatType() ) + iCmp = asBC_CMPf; + else if( lctx->type.dataType.IsDoubleType() ) + iCmp = asBC_CMPd; + else + asASSERT(false); + + if( op == ttEqual ) + iT = asBC_TZ; + else if( op == ttNotEqual ) + iT = asBC_TNZ; + else if( op == ttLessThan ) + iT = asBC_TS; + else if( op == ttLessThanOrEqual ) + iT = asBC_TNP; + else if( op == ttGreaterThan ) + iT = asBC_TP; + else if( op == ttGreaterThanOrEqual ) + iT = asBC_TNS; + + int a = AllocateVariable(asCDataType::CreatePrimitive(ttBool, true), true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W(iCmp, b, c); + ctx->bc.Instr(iT); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + } + } + else + { + if( to.IsBooleanType() ) + { + int op = node->tokenType; + if( op == ttEqual || op == ttNotEqual ) + { + // Make sure they are equal if not false + if( lctx->type.dwordValue != 0 ) lctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE; + if( rctx->type.dwordValue != 0 ) rctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE; + + asDWORD v = 0; + if( op == ttEqual ) + { + v = lctx->type.intValue - rctx->type.intValue; + if( v == 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0; + } + else if( op == ttNotEqual ) + { + v = lctx->type.intValue - rctx->type.intValue; + if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0; + } + + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), v); + } + else + { + // TODO: Use TXT_ILLEGAL_OPERATION_ON + Error(TXT_ILLEGAL_OPERATION, node); + } + } + else + { + int i = 0; + if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + int v = lctx->type.intValue - rctx->type.intValue; + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + { + asDWORD v1 = lctx->type.dwordValue; + asDWORD v2 = rctx->type.dwordValue; + if( v1 < v2 ) i = -1; + if( v1 > v2 ) i = 1; + } + else if( lctx->type.dataType.IsIntegerType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + asINT64 v = asINT64(lctx->type.qwordValue) - asINT64(rctx->type.qwordValue); + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + else if( lctx->type.dataType.IsUnsignedType() && lctx->type.dataType.GetSizeInMemoryDWords() == 2 ) + { + asQWORD v1 = lctx->type.qwordValue; + asQWORD v2 = rctx->type.qwordValue; + if( v1 < v2 ) i = -1; + if( v1 > v2 ) i = 1; + } + else if( lctx->type.dataType.IsFloatType() ) + { + float v = lctx->type.floatValue - rctx->type.floatValue; + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + else if( lctx->type.dataType.IsDoubleType() ) + { + double v = lctx->type.doubleValue - rctx->type.doubleValue; + if( v < 0 ) i = -1; + if( v > 0 ) i = 1; + } + + + if( op == ttEqual ) + i = (i == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttNotEqual ) + i = (i != 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttLessThan ) + i = (i < 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttLessThanOrEqual ) + i = (i <= 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttGreaterThan ) + i = (i > 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + else if( op == ttGreaterThanOrEqual ) + i = (i >= 0 ? VALUE_OF_BOOLEAN_TRUE : 0); + + ctx->type.SetConstantDW(asCDataType::CreatePrimitive(ttBool, true), i); + } + } +} + +void asCCompiler::PushVariableOnStack(asSExprContext *ctx, bool asReference) +{ + // Put the result on the stack + ctx->bc.InstrSHORT(asBC_PSF, ctx->type.stackOffset); + if( asReference ) + ctx->type.dataType.MakeReference(true); + else + { + if( ctx->type.dataType.GetSizeInMemoryDWords() == 1 ) + ctx->bc.Instr(asBC_RDS4); + else + ctx->bc.Instr(asBC_RDS8); + } +} + +void asCCompiler::CompileBooleanOperator(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +{ + // Both operands must be booleans + asCDataType to; + to.SetTokenType(ttBool); + + // Do the actual conversion + asCArray reservedVars; + rctx->bc.GetVarsUsed(reservedVars); + lctx->bc.GetVarsUsed(reservedVars); + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV, true, &reservedVars); + + // Verify that the conversion was successful + if( !lctx->type.dataType.IsBooleanType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), "bool"); + Error(str.AddressOf(), node); + // Force the conversion to allow compilation to proceed + lctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + } + + if( !rctx->type.dataType.IsBooleanType() ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), "bool"); + Error(str.AddressOf(), node); + // Force the conversion to allow compilation to proceed + rctx->type.SetConstantB(asCDataType::CreatePrimitive(ttBool, true), true); + } + + bool isConstant = lctx->type.isConstant && rctx->type.isConstant; + + ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true)); + + // What kind of operator is it? + int op = node->tokenType; + if( op == ttXor ) + { + if( !isConstant ) + { + // Must convert to temporary variable, because we are changing the value before comparison + ConvertToTempVariableNotIn(lctx, rctx); + ConvertToTempVariableNotIn(rctx, lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + + // Make sure they are equal if not false + lctx->bc.InstrWORD(asBC_NOT, lctx->type.stackOffset); + rctx->bc.InstrWORD(asBC_NOT, rctx->type.stackOffset); + + MergeExprContexts(ctx, lctx); + MergeExprContexts(ctx, rctx); + + int a = AllocateVariable(ctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + ctx->bc.InstrW_W_W(asBC_BXOR,a,b,c); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + } + else + { + // Make sure they are equal if not false +#if AS_SIZEOF_BOOL == 1 + if( lctx->type.byteValue != 0 ) lctx->type.byteValue = VALUE_OF_BOOLEAN_TRUE; + if( rctx->type.byteValue != 0 ) rctx->type.byteValue = VALUE_OF_BOOLEAN_TRUE; + + asBYTE v = 0; + v = lctx->type.byteValue - rctx->type.byteValue; + if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0; + + ctx->type.isConstant = true; + ctx->type.byteValue = v; +#else + if( lctx->type.dwordValue != 0 ) lctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE; + if( rctx->type.dwordValue != 0 ) rctx->type.dwordValue = VALUE_OF_BOOLEAN_TRUE; + + asDWORD v = 0; + v = lctx->type.intValue - rctx->type.intValue; + if( v != 0 ) v = VALUE_OF_BOOLEAN_TRUE; else v = 0; + + ctx->type.isConstant = true; + ctx->type.dwordValue = v; +#endif + } + } + else if( op == ttAnd || + op == ttOr ) + { + if( !isConstant ) + { + // If or-operator and first value is 1 the second value shouldn't be calculated + // if and-operator and first value is 0 the second value shouldn't be calculated + ConvertToVariable(lctx); + ReleaseTemporaryVariable(lctx->type, &lctx->bc); + MergeExprContexts(ctx, lctx); + + int offset = AllocateVariable(asCDataType::CreatePrimitive(ttBool, false), true); + + int label1 = nextLabel++; + int label2 = nextLabel++; + if( op == ttAnd ) + { + ctx->bc.InstrSHORT(asBC_CpyVtoR4, lctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JNZ, label1); + ctx->bc.InstrW_DW(asBC_SetV4, (asWORD)offset, 0); + ctx->bc.InstrINT(asBC_JMP, label2); + } + else if( op == ttOr ) + { + ctx->bc.InstrSHORT(asBC_CpyVtoR4, lctx->type.stackOffset); + ctx->bc.Instr(asBC_ClrHi); + ctx->bc.InstrDWORD(asBC_JZ, label1); +#if AS_SIZEOF_BOOL == 1 + ctx->bc.InstrSHORT_B(asBC_SetV1, (short)offset, VALUE_OF_BOOLEAN_TRUE); +#else + ctx->bc.InstrSHORT_DW(asBC_SetV4, (short)offset, VALUE_OF_BOOLEAN_TRUE); +#endif + ctx->bc.InstrINT(asBC_JMP, label2); + } + + ctx->bc.Label((short)label1); + ConvertToVariable(rctx); + ReleaseTemporaryVariable(rctx->type, &rctx->bc); + rctx->bc.InstrW_W(asBC_CpyVtoV4, offset, rctx->type.stackOffset); + MergeExprContexts(ctx, rctx); + ctx->bc.Label((short)label2); + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, false), offset, true); + } + else + { +#if AS_SIZEOF_BOOL == 1 + asBYTE v = 0; + if( op == ttAnd ) + v = lctx->type.byteValue && rctx->type.byteValue; + else if( op == ttOr ) + v = lctx->type.byteValue || rctx->type.byteValue; + + // Remember the result + ctx->type.isConstant = true; + ctx->type.byteValue = v; +#else + asDWORD v = 0; + if( op == ttAnd ) + v = lctx->type.dwordValue && rctx->type.dwordValue; + else if( op == ttOr ) + v = lctx->type.dwordValue || rctx->type.dwordValue; + + // Remember the result + ctx->type.isConstant = true; + ctx->type.dwordValue = v; +#endif + } + } +} + +void asCCompiler::CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *lctx, asSExprContext *rctx, asSExprContext *ctx) +{ + // Process the property accessor as get + ProcessPropertyGetAccessor(lctx, node); + ProcessPropertyGetAccessor(rctx, node); + + // Make sure lctx doesn't end up with a variable used in rctx + if( lctx->type.isTemporary && rctx->bc.IsVarUsed(lctx->type.stackOffset) ) + { + asCArray vars; + rctx->bc.GetVarsUsed(vars); + int offset = AllocateVariable(lctx->type.dataType, true); + rctx->bc.ExchangeVar(lctx->type.stackOffset, offset); + ReleaseTemporaryVariable(offset, 0); + } + + // Warn if not both operands are explicit handles + if( (node->tokenType == ttEqual || node->tokenType == ttNotEqual) && + ((!lctx->type.isExplicitHandle && !(lctx->type.dataType.GetObjectType() && (lctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE))) || + (!rctx->type.isExplicitHandle && !(rctx->type.dataType.GetObjectType() && (rctx->type.dataType.GetObjectType()->flags & asOBJ_IMPLICIT_HANDLE)))) ) + { + Warning(TXT_HANDLE_COMPARISON, node); + } + + // Implicitly convert null to the other type + asCDataType to; + if( lctx->type.IsNullConstant() ) + to = rctx->type.dataType; + else if( rctx->type.IsNullConstant() ) + to = lctx->type.dataType; + else + { + // TODO: Use the common base type + to = lctx->type.dataType; + } + + // Need to pop the value if it is a null constant + if( lctx->type.IsNullConstant() ) + lctx->bc.Pop(AS_PTR_SIZE); + if( rctx->type.IsNullConstant() ) + rctx->bc.Pop(AS_PTR_SIZE); + + // Convert both sides to explicit handles + to.MakeHandle(true); + to.MakeReference(false); + + // Do the conversion + ImplicitConversion(lctx, to, node, asIC_IMPLICIT_CONV); + ImplicitConversion(rctx, to, node, asIC_IMPLICIT_CONV); + + // Both operands must be of the same type + + // Verify that the conversion was successful + if( !lctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, lctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + } + + if( !rctx->type.dataType.IsEqualExceptConst(to) ) + { + asCString str; + str.Format(TXT_NO_CONVERSION_s_TO_s, rctx->type.dataType.Format().AddressOf(), to.Format().AddressOf()); + Error(str.AddressOf(), node); + } + + ctx->type.Set(asCDataType::CreatePrimitive(ttBool, true)); + + int op = node->tokenType; + if( op == ttEqual || op == ttNotEqual || op == ttIs || op == ttNotIs ) + { + // If the object handle already is in a variable we must manually pop it from the stack + if( lctx->type.isVariable ) + lctx->bc.Pop(AS_PTR_SIZE); + if( rctx->type.isVariable ) + rctx->bc.Pop(AS_PTR_SIZE); + + // TODO: optimize: Treat the object handles as two integers, i.e. don't do REFCPY + ConvertToVariableNotIn(lctx, rctx); + ConvertToVariable(rctx); + + MergeExprContexts(ctx, lctx); + MergeExprContexts(ctx, rctx); + + int a = AllocateVariable(ctx->type.dataType, true); + int b = lctx->type.stackOffset; + int c = rctx->type.stackOffset; + + if( op == ttEqual || op == ttIs ) + { +#ifdef AS_64BIT_PTR + // TODO: Optimize: Use a 64bit integer comparison instead of double + ctx->bc.InstrW_W(asBC_CMPd, b, c); +#else + ctx->bc.InstrW_W(asBC_CMPi, b, c); +#endif + ctx->bc.Instr(asBC_TZ); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + } + else if( op == ttNotEqual || op == ttNotIs ) + { +#ifdef AS_64BIT_PTR + // TODO: Optimize: Use a 64bit integer comparison instead of double + ctx->bc.InstrW_W(asBC_CMPd, b, c); +#else + ctx->bc.InstrW_W(asBC_CMPi, b, c); +#endif + ctx->bc.Instr(asBC_TNZ); + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)a); + } + + ctx->type.SetVariable(asCDataType::CreatePrimitive(ttBool, true), a, true); + + ReleaseTemporaryVariable(lctx->type, &ctx->bc); + ReleaseTemporaryVariable(rctx->type, &ctx->bc); + } + else + { + // TODO: Use TXT_ILLEGAL_OPERATION_ON + Error(TXT_ILLEGAL_OPERATION, node); + } +} + + +void asCCompiler::PerformFunctionCall(int funcId, asSExprContext *ctx, bool isConstructor, asCArray *args, asCObjectType *objType, bool useVariable, int varOffset) +{ + asCScriptFunction *descr = builder->GetFunctionDescription(funcId); + + int argSize = descr->GetSpaceNeededForArguments(); + + ctx->type.Set(descr->returnType); + + if( isConstructor ) + { + // TODO: When value types are allocated on the stack, this won't be needed anymore + // as the constructor will be called just like any other function + asASSERT(useVariable == false); + + ctx->bc.Alloc(asBC_ALLOC, objType, descr->id, argSize+AS_PTR_SIZE); + + // The instruction has already moved the returned object to the variable + ctx->type.Set(asCDataType::CreatePrimitive(ttVoid, false)); + + // Clean up arguments + if( args ) + AfterFunctionCall(funcId, *args, ctx, false); + + ProcessDeferredParams(ctx); + + return; + } + else if( descr->funcType == asFUNC_IMPORTED ) + ctx->bc.Call(asBC_CALLBND , descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0)); + // TODO: Maybe we need two different byte codes + else if( descr->funcType == asFUNC_INTERFACE || descr->funcType == asFUNC_VIRTUAL ) + ctx->bc.Call(asBC_CALLINTF, descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0)); + else if( descr->funcType == asFUNC_SCRIPT ) + ctx->bc.Call(asBC_CALL , descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0)); + else // if( descr->funcType == asFUNC_SYSTEM ) + ctx->bc.Call(asBC_CALLSYS , descr->id, argSize + (descr->objectType ? AS_PTR_SIZE : 0)); + + if( ctx->type.dataType.IsObject() && !descr->returnType.IsReference() ) + { + int returnOffset = 0; + + if( useVariable ) + { + // Use the given variable + returnOffset = varOffset; + ctx->type.SetVariable(descr->returnType, returnOffset, false); + } + else + { + // Allocate a temporary variable for the returned object + returnOffset = AllocateVariable(descr->returnType, true); + ctx->type.SetVariable(descr->returnType, returnOffset, true); + } + + ctx->type.dataType.MakeReference(true); + + // Move the pointer from the object register to the temporary variable + ctx->bc.InstrSHORT(asBC_STOREOBJ, (short)returnOffset); + + // Clean up arguments + if( args ) + AfterFunctionCall(funcId, *args, ctx, false); + + ProcessDeferredParams(ctx); + + ctx->bc.InstrSHORT(asBC_PSF, (short)returnOffset); + } + else if( descr->returnType.IsReference() ) + { + asASSERT(useVariable == false); + + // We cannot clean up the arguments yet, because the + // reference might be pointing to one of them. + + // Clean up arguments + if( args ) + AfterFunctionCall(funcId, *args, ctx, true); + + // Do not process the output parameters yet, because it + // might invalidate the returned reference + + if( descr->returnType.IsPrimitive() ) + ctx->type.Set(descr->returnType); + else + { + ctx->bc.Instr(asBC_PshRPtr); + if( descr->returnType.IsObject() && !descr->returnType.IsObjectHandle() ) + { + // We are getting the pointer to the object + // not a pointer to a object variable + ctx->type.dataType.MakeReference(false); + } + } + } + else + { + asASSERT(useVariable == false); + + if( descr->returnType.GetSizeInMemoryBytes() ) + { + int offset = AllocateVariable(descr->returnType, true); + + ctx->type.SetVariable(descr->returnType, offset, true); + + // Move the value from the return register to the variable + if( descr->returnType.GetSizeOnStackDWords() == 1 ) + ctx->bc.InstrSHORT(asBC_CpyRtoV4, (short)offset); + else if( descr->returnType.GetSizeOnStackDWords() == 2 ) + ctx->bc.InstrSHORT(asBC_CpyRtoV8, (short)offset); + } + else + ctx->type.Set(descr->returnType); + + // Clean up arguments + if( args ) + AfterFunctionCall(funcId, *args, ctx, false); + + ProcessDeferredParams(ctx); + } +} + + +void asCCompiler::MergeExprContexts(asSExprContext *before, asSExprContext *after) +{ + before->bc.AddCode(&after->bc); + + for( asUINT n = 0; n < after->deferredParams.GetLength(); n++ ) + before->deferredParams.PushLast(after->deferredParams[n]); + + after->deferredParams.SetLength(0); + + asASSERT( after->origExpr == 0 ); +} + +void asCCompiler::FilterConst(asCArray &funcs) +{ + if( funcs.GetLength() == 0 ) return; + + // This is only done for object methods + asCScriptFunction *desc = builder->GetFunctionDescription(funcs[0]); + if( desc->objectType == 0 ) return; + + // Check if there are any non-const matches + asUINT n; + bool foundNonConst = false; + for( n = 0; n < funcs.GetLength(); n++ ) + { + desc = builder->GetFunctionDescription(funcs[n]); + if( !desc->isReadOnly ) + { + foundNonConst = true; + break; + } + } + + if( foundNonConst ) + { + // Remove all const methods + for( n = 0; n < funcs.GetLength(); n++ ) + { + desc = builder->GetFunctionDescription(funcs[n]); + if( desc->isReadOnly ) + { + if( n == funcs.GetLength() - 1 ) + funcs.PopLast(); + else + funcs[n] = funcs.PopLast(); + + n--; + } + } + } +} + +END_AS_NAMESPACE + + + diff --git a/AngelScript/source/as_compiler.h b/AngelScript/source/as_compiler.h new file mode 100644 index 000000000..eed08a1ab --- /dev/null +++ b/AngelScript/source/as_compiler.h @@ -0,0 +1,247 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_compiler.h +// +// The class that does the actual compilation of the functions +// + + + +#ifndef AS_COMPILER_H +#define AS_COMPILER_H + +#include "as_config.h" +#include "as_builder.h" +#include "as_scriptfunction.h" +#include "as_variablescope.h" +#include "as_bytecode.h" +#include "as_array.h" +#include "as_datatype.h" +#include "as_typeinfo.h" + +BEGIN_AS_NAMESPACE + +struct asSExprContext; + +struct asSDeferredParam +{ + asSDeferredParam() {argNode = 0; origExpr = 0;} + + asCScriptNode *argNode; + asCTypeInfo argType; + int argInOutFlags; + asSExprContext *origExpr; +}; + +struct asSExprContext +{ + asSExprContext(asCScriptEngine *engine) : bc(engine) {exprNode = 0; origExpr = 0; property_get = 0; property_set = 0; } + + asCByteCode bc; + asCTypeInfo type; + int property_get; + int property_set; + bool property_const; // If the object that is being accessed through property accessor is read-only + bool property_handle; // If the property accessor is called on an object stored in a handle + asCArray deferredParams; + asCScriptNode *exprNode; + asSExprContext *origExpr; +}; + +enum EImplicitConv +{ + asIC_IMPLICIT_CONV, + asIC_EXPLICIT_REF_CAST, + asIC_EXPLICIT_VAL_CAST +}; + +class asCCompiler +{ +public: + asCCompiler(asCScriptEngine *engine); + ~asCCompiler(); + + int CompileFunction(asCBuilder *builder, asCScriptCode *script, asCScriptNode *func, asCScriptFunction *outFunc); + int CompileDefaultConstructor(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc); + int CompileFactory(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc); + int CompileTemplateFactoryStub(asCBuilder *builder, int trueFactoryId, asCObjectType *objType, asCScriptFunction *outFunc); + int CompileGlobalVariable(asCBuilder *builder, asCScriptCode *script, asCScriptNode *expr, sGlobalVariableDescription *gvar, asCScriptFunction *outFunc); + + asCByteCode byteCode; + asCArray objVariableTypes; + asCArray objVariablePos; + +protected: + friend class asCBuilder; + + void Reset(asCBuilder *builder, asCScriptCode *script, asCScriptFunction *outFunc); + + // Statements + void CompileStatementBlock(asCScriptNode *block, bool ownVariableScope, bool *hasReturn, asCByteCode *bc); + void CompileDeclaration(asCScriptNode *decl, asCByteCode *bc); + void CompileStatement(asCScriptNode *statement, bool *hasReturn, asCByteCode *bc); + void CompileIfStatement(asCScriptNode *node, bool *hasReturn, asCByteCode *bc); + void CompileSwitchStatement(asCScriptNode *node, bool *hasReturn, asCByteCode *bc); + void CompileCase(asCScriptNode *node, asCByteCode *bc); + void CompileForStatement(asCScriptNode *node, asCByteCode *bc); + void CompileWhileStatement(asCScriptNode *node, asCByteCode *bc); + void CompileDoWhileStatement(asCScriptNode *node, asCByteCode *bc); + void CompileBreakStatement(asCScriptNode *node, asCByteCode *bc); + void CompileContinueStatement(asCScriptNode *node, asCByteCode *bc); + void CompileReturnStatement(asCScriptNode *node, asCByteCode *bc); + void CompileExpressionStatement(asCScriptNode *node, asCByteCode *bc); + + // Expressions + int CompileAssignment(asCScriptNode *expr, asSExprContext *out); + int CompileCondition(asCScriptNode *expr, asSExprContext *out); + int CompileExpression(asCScriptNode *expr, asSExprContext *out); + int CompilePostFixExpression(asCArray *postfix, asSExprContext *out); + int CompileExpressionTerm(asCScriptNode *node, asSExprContext *out); + int CompileExpressionPreOp(asCScriptNode *node, asSExprContext *out); + int CompileExpressionPostOp(asCScriptNode *node, asSExprContext *out); + int CompileExpressionValue(asCScriptNode *node, asSExprContext *out); + void CompileFunctionCall(asCScriptNode *node, asSExprContext *out, asCObjectType *objectType, bool objIsConst, const asCString &scope = ""); + void CompileConstructCall(asCScriptNode *node, asSExprContext *out); + void CompileConversion(asCScriptNode *node, asSExprContext *out); + int CompileOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + void CompileOperatorOnHandles(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + void CompileMathOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + void CompileBitwiseOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + void CompileComparisonOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + void CompileBooleanOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + bool CompileOverloadedDualOperator(asCScriptNode *node, asSExprContext *l, asSExprContext *r, asSExprContext *out); + int CompileOverloadedDualOperator2(asCScriptNode *node, const char *methodName, asSExprContext *l, asSExprContext *r, asSExprContext *out, bool specificReturn = false, const asCDataType &returnType = asCDataType::CreatePrimitive(ttVoid, false)); + + void CompileInitList(asCTypeInfo *var, asCScriptNode *node, asCByteCode *bc); + + int CallDefaultConstructor(asCDataType &type, int offset, asCByteCode *bc, asCScriptNode *node, bool isGlobalVar = false); + int CallCopyConstructor(asCDataType &type, int offset, asCByteCode *bc, asSExprContext *arg, asCScriptNode *node, bool isGlobalVar = false); + void CallDestructor(asCDataType &type, int offset, asCByteCode *bc); + int CompileArgumentList(asCScriptNode *node, asCArray &args); + void MatchFunctions(asCArray &funcs, asCArray &args, asCScriptNode *node, const char *name, asCObjectType *objectType = NULL, bool isConstMethod = false, bool silent = false, bool allowObjectConstruct = true, const asCString &scope = ""); + + // Helper functions + void ProcessPropertyGetAccessor(asSExprContext *ctx, asCScriptNode *node); + int ProcessPropertySetAccessor(asSExprContext *ctx, asSExprContext *arg, asCScriptNode *node); + int FindPropertyAccessor(const asCString &name, asSExprContext *ctx, asCScriptNode *node); + void SwapPostFixOperands(asCArray &postfix, asCArray &target); + void PrepareTemporaryObject(asCScriptNode *node, asSExprContext *ctx, asCArray *reservedVars); + void PrepareOperand(asSExprContext *ctx, asCScriptNode *node); + void PrepareForAssignment(asCDataType *lvalue, asSExprContext *rvalue, asCScriptNode *node, asSExprContext *lvalueExpr = 0); + void PerformAssignment(asCTypeInfo *lvalue, asCTypeInfo *rvalue, asCByteCode *bc, asCScriptNode *node); + bool IsVariableInitialized(asCTypeInfo *type, asCScriptNode *node); + void Dereference(asSExprContext *ctx, bool generateCode); + bool CompileRefCast(asSExprContext *ctx, const asCDataType &to, bool isExplicit, asCScriptNode *node, bool generateCode = true); + int MatchArgument(asCArray &funcs, asCArray &matches, const asCTypeInfo *argType, int paramNum, bool allowObjectConstruct = true); + void PerformFunctionCall(int funcId, asSExprContext *out, bool isConstructor = false, asCArray *args = 0, asCObjectType *objTypeForConstruct = 0, bool useVariable = false, int varOffset = 0); + void MoveArgsToStack(int funcId, asCByteCode *bc, asCArray &args, bool addOneToOffset); + void MakeFunctionCall(asSExprContext *ctx, int funcId, asCObjectType *objectType, asCArray &args, asCScriptNode *node, bool useVariable = false, int stackOffset = 0); + void PrepareFunctionCall(int funcId, asCByteCode *bc, asCArray &args); + void AfterFunctionCall(int funcId, asCArray &args, asSExprContext *ctx, bool deferAll); + void ProcessDeferredParams(asSExprContext *ctx); + void PrepareArgument(asCDataType *paramType, asSExprContext *ctx, asCScriptNode *node, bool isFunction = false, int refType = 0, asCArray *reservedVars = 0); + void PrepareArgument2(asSExprContext *ctx, asSExprContext *arg, asCDataType *paramType, bool isFunction = false, int refType = 0, asCArray *reservedVars = 0); + bool IsLValue(asCTypeInfo &type); + int DoAssignment(asSExprContext *out, asSExprContext *lctx, asSExprContext *rctx, asCScriptNode *lexpr, asCScriptNode *rexpr, int op, asCScriptNode *opNode); + void MergeExprContexts(asSExprContext *before, asSExprContext *after); + void FilterConst(asCArray &funcs); + void ConvertToVariable(asSExprContext *ctx); + void ConvertToVariableNotIn(asSExprContext *ctx, asSExprContext *exclude); + void ConvertToVariableNotIn(asSExprContext *ctx, asCArray *reservedVars); + void ConvertToTempVariable(asSExprContext *ctx); + void ConvertToTempVariableNotIn(asSExprContext *ctx, asSExprContext *exclude); + void ConvertToTempVariableNotIn(asSExprContext *ctx, asCArray *reservedVars); + void ConvertToReference(asSExprContext *ctx); + void PushVariableOnStack(asSExprContext *ctx, bool asReference); + asCString GetScopeFromNode(asCScriptNode *node); + + void ImplicitConversion(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, asCArray *reservedVars = 0, bool allowObjectConstruct = true); + void ImplicitConvPrimitiveToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, asCArray *reservedVars = 0); + void ImplicitConvObjectToPrimitive(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, asCArray *reservedVars = 0); + void ImplicitConvPrimitiveToObject(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, asCArray *reservedVars = 0, bool allowObjectConstruct = true); + void ImplicitConvObjectToObject(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType, bool generateCode = true, asCArray *reservedVars = 0, bool allowObjectConstruct = true); + void ImplicitConversionConstant(asSExprContext *ctx, const asCDataType &to, asCScriptNode *node, EImplicitConv convType); + + void LineInstr(asCByteCode *bc, size_t pos); + + asUINT ProcessStringConstant(asCString &str, asCScriptNode *node, bool processEscapeSequences = true); + void ProcessHeredocStringConstant(asCString &str, asCScriptNode *node); + int GetPrecedence(asCScriptNode *op); + + void Error(const char *msg, asCScriptNode *node); + void Warning(const char *msg, asCScriptNode *node); + void PrintMatchingFuncs(asCArray &funcs, asCScriptNode *node); + + + void AddVariableScope(bool isBreakScope = false, bool isContinueScope = false); + void RemoveVariableScope(); + + void FinalizeFunction(); + + bool hasCompileErrors; + + int nextLabel; + + asCVariableScope *variables; + asCBuilder *builder; + asCScriptEngine *engine; + asCScriptCode *script; + asCScriptFunction *outFunc; + + bool m_isConstructor; + bool m_isConstructorCalled; + + asCArray breakLabels; + asCArray continueLabels; + + int AllocateVariable(const asCDataType &type, bool isTemporary); + int AllocateVariableNotIn(const asCDataType &type, bool isTemporary, asCArray *vars); + int GetVariableOffset(int varIndex); + int GetVariableSlot(int varOffset); + void DeallocateVariable(int pos); + void ReleaseTemporaryVariable(asCTypeInfo &t, asCByteCode *bc); + void ReleaseTemporaryVariable(int offset, asCByteCode *bc); + + asCArray variableAllocations; + asCArray variableIsTemporary; + asCArray freeVariables; + asCArray tempVariables; + + bool globalExpression; + bool isProcessingDeferredParams; + int noCodeOutput; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_config.h b/AngelScript/source/as_config.h new file mode 100644 index 000000000..210527c0f --- /dev/null +++ b/AngelScript/source/as_config.h @@ -0,0 +1,731 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_config.h +// +// this file is used for configuring the compilation of the library +// + +#ifndef AS_CONFIG_H +#define AS_CONFIG_H + + + +// +// Features +//----------------------------------------- + +// AS_NO_THREADS +// Turns off support for multithreading. By turning off +// this when it's not needed a bit of performance is gained. + +// AS_WINDOWS_THREADS +// If the library should be compiled using windows threads. + +// AS_POSIX_THREADS +// If the library should be compiled using posix threads. + +// AS_NO_ATOMIC +// If the compiler/platform doesn't support atomic instructions +// then this should be defined to use critical sections instead. + +// AS_DEBUG +// This flag can be defined to make the library write some extra output when +// compiling and executing scripts. + +// AS_DEPRECATED +// If this flag is defined then some backwards compatibility is maintained. +// There is no guarantee for how well deprecated functionality will work though +// so it is best to exchange it for the new functionality as soon as possible. + +// AS_NO_CLASS_METHODS +// Disables the possibility to add class methods. Can increase the +// portability of the library. + +// AS_MAX_PORTABILITY +// Disables all platform specific code. Only the asCALL_GENERIC calling +// convention will be available in with this flag set. + +// AS_DOUBLEBYTE_CHARSET +// When this flag is defined, the parser will treat all characters in strings +// that are greater than 127 as lead characters and automatically include the +// next character in the script without checking its value. This should be +// compatible with common encoding schemes, e.g. Big5. Shift-JIS is not compatible +// though as it encodes some single byte characters above 127. +// +// If support for international text is desired, it is recommended that UTF-8 +// is used as this is supported natively by the compiler without the use for this +// preprocessor flag. + + + + +// +// Library usage +//------------------------------------------ + +// ANGELSCRIPT_EXPORT +// This flag should be defined when compiling the library as a lib or dll. + +// ANGELSCRIPT_DLL_LIBRARY_IMPORT +// This flag should be defined when using AngelScript as a dll with automatic +// library import. + +// ANGELSCRIPT_DLL_MANUAL_IMPORT +// This flag should be defined when using AngelScript as a dll with manual +// loading of the library. + + + + +// +// Compiler differences +//----------------------------------------- + +// asVSNPRINTF(a,b,c,d) +// Some compilers use different names for this function. You must +// define this macro to map to the proper function. + +// ASM_AT_N_T or ASM_INTEL +// You should choose what inline assembly syntax to use when compiling. + +// VALUE_OF_BOOLEAN_TRUE +// This flag allows to customize the exact value of boolean true. + +// AS_SIZEOF_BOOL +// On some target platforms the sizeof(bool) is 4, but on most it is 1. + +// STDCALL +// This is used to declare a function to use the stdcall calling convention. + +// AS_USE_NAMESPACE +// Adds the AngelScript namespace on the declarations. + +// AS_NO_MEMORY_H +// Some compilers don't come with the memory.h header file. + + + +// +// How to identify different compilers +//----------------------------------------- + +// MS Visual C++ +// _MSC_VER is defined +// __MWERKS__ is not defined + +// Metrowerks +// _MSC_VER is defined +// __MWERKS__ is defined + +// GNU C based compilers +// __GNUC__ is defined + + + +// +// CPU differences +//--------------------------------------- + +// AS_ALIGN +// Some CPUs require that data words are aligned in some way. This macro +// should be defined if the words should be aligned to boundaries of the same +// size as the word, i.e. +// 1 byte on 1 byte boundaries +// 2 bytes on 2 byte boundaries +// 4 bytes on 4 byte boundaries +// 8 bytes on 4 byte boundaries (no it's not a typo) + +// AS_USE_DOUBLE_AS_FLOAT +// If there is no 64 bit floating point type, then this constant can be defined +// to treat double like normal floats. + +// AS_X86 +// Use assembler code for the x86 CPU family + +// AS_SH4 +// Use assembler code for the SH4 CPU family + +// AS_MIPS +// Use assembler code for the MIPS CPU family + +// AS_PPC +// Use assembler code for the 32bit PowerPC CPU family + +// AS_PPC_64 +// Use assembler code for the 64bit PowerPC CPU family + +// AS_XENON +// Use assembler code for the Xenon (XBOX360) CPU family + +// AS_ARM +// Use assembler code for the ARM CPU family + +// AS_64BIT_PTR +// Define this to make the engine store all pointers in 64bit words. + +// AS_BIG_ENDIAN +// Define this for CPUs that use big endian memory layout, e.g. PPC + + + +// +// Target systems +//-------------------------------- +// This group shows a few of the flags used to identify different target systems. +// Sometimes there are differences on different target systems, while both CPU and +// compiler is the same for both, when this is so these flags are used to produce the +// right code. + +// AS_WIN - Microsoft Windows +// AS_LINUX - Linux +// AS_MAC - Apple Macintosh +// AS_BSD - FreeBSD +// AS_XBOX - Microsoft XBox +// AS_XBOX360 - Microsoft XBox 360 +// AS_PSP - Sony Playstation Portable +// AS_PS2 - Sony Playstation 2 +// AS_PS3 - Sony Playstation 3 +// AS_DC - Sega Dreamcast +// AS_GC - Nintendo GameCube +// AS_WII - Nintendo Wii +// AS_IPHONE - Apple IPhone +// AS_ANDROID - Android + + + + +// +// Calling conventions +//----------------------------------------- + +// GNU_STYLE_VIRTUAL_METHOD +// This constant should be defined if method pointers store index for virtual +// functions in the same location as the function pointer. In such cases the method +// is identified as virtual if the least significant bit is set. + +// MULTI_BASE_OFFSET(x) +// This macro is used to retrieve the offset added to the object pointer in order to +// implicitly cast the object to the base object. x is the method pointer received by +// the register function. + +// HAVE_VIRTUAL_BASE_OFFSET +// Define this constant if the compiler stores the virtual base offset in the method +// pointers. If it is not stored in the pointers then AngelScript have no way of +// identifying a method as coming from a class with virtual inheritance. + +// VIRTUAL_BASE_OFFSET(x) +// This macro is used to retrieve the offset added to the object pointer in order to +// find the virtual base object. x is the method pointer received by the register +// function; + +// COMPLEX_MASK +// This constant shows what attributes determines if an object is returned in memory +// or in the registers as normal structures + +// THISCALL_RETURN_SIMPLE_IN_MEMORY +// CDECL_RETURN_SIMPLE_IN_MEMORY +// STDCALL_RETURN_SIMPLE_IN_MEMORY +// When these constants are defined then the corresponding calling convention always +// return classes/structs in memory regardless of size or complexity. + +// THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE +// STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE +// CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE +// Specifies the minimum size in dwords a class/struct needs to be to be passed in memory + + +// CALLEE_POPS_HIDDEN_RETURN_POINTER +// This constant should be defined if the callee pops the hidden return pointer, +// used when returning an object in memory. + +// THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK +// With this constant defined AngelScript will pass the object pointer on the stack + +// THISCALL_CALLEE_POPS_ARGUMENTS +// If the callee pops arguments for class methods then define this constant + +// COMPLEX_OBJS_PASSED_BY_REF +// Some compilers always pass certain objects by reference. GNUC for example does +// this if the the class has a defined destructor. + +// HAS_128_BIT_PRIMITIVES +// 64bit processors often support 128bit primitives. These may require special +// treatment when passed in function arguments or returned by functions. + +// SPLIT_OBJS_BY_MEMBER_TYPES +// On some platforms objects with primitive members are split over different +// register types when passed by value to functions. + + + + + +// +// Detect compiler +//------------------------------------------------ + +#define VALUE_OF_BOOLEAN_TRUE 1 +#define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 +#define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 +#define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 0 + +// Microsoft Visual C++ +#if defined(_MSC_VER) && !defined(__MWERKS__) + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) + #define HAVE_VIRTUAL_BASE_OFFSET + #define VIRTUAL_BASE_OFFSET(x) (*((asDWORD*)(&x)+3)) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_PASS_OBJECT_POINTER_IN_ECX + #if _MSC_VER < 1500 // MSVC++ 9 (aka MSVC++ .NET 2008) + #define asVSNPRINTF(a, b, c, d) _vsnprintf(a, b, c, d) + #else + #define asVSNPRINTF(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d) + #endif + #define THISCALL_CALLEE_POPS_ARGUMENTS + #define STDCALL __stdcall + #define AS_SIZEOF_BOOL 1 + #define AS_WINDOWS_THREADS + + #define ASM_INTEL // Intel style for inline assembly on microsoft compilers + + #if defined(WIN32) + #define AS_WIN + #endif + + #if _XBOX_VER >= 200 + // 360 uses a Xenon processor (which is a modified 64bit PPC) + #define AS_XBOX360 + #define AS_XENON + #define AS_BIG_ENDIAN + #else + // Support native calling conventions on x86, but not 64bit yet + #if defined(_XBOX) || (defined(_M_IX86) && !defined(__LP64__)) + #define AS_X86 + #endif + #endif + + #if _MSC_VER <= 1200 // MSVC++ 6 + #define I64(x) x##l + #else + #define I64(x) x##ll + #endif + + #ifdef _ARM_ + #define AS_ALIGN + #define AS_ARM + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define COMPLEX_OBJS_PASSED_BY_REF + #define COMPLEX_MASK asOBJ_APP_CLASS_ASSIGNMENT + #else + #define COMPLEX_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT) + #endif + + #define UNREACHABLE_RETURN +#endif + +// Metrowerks CodeWarrior (experimental, let me know if something isn't working) +#if defined(__MWERKS__) && !defined(EPPC) // JWC -- If Wii DO NOT use this even when using Metrowerks Compiler. Even though they are called Freescale... + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) + #define HAVE_VIRTUAL_BASE_OFFSET + #define VIRTUAL_BASE_OFFSET(x) (*((asDWORD*)(&x)+3)) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_PASS_OBJECT_POINTER_IN_ECX + #define asVSNPRINTF(a, b, c, d) _vsnprintf(a, b, c, d) + #define THISCALL_CALLEE_POPS_ARGUMENTS + #define COMPLEX_MASK (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT) + #define AS_SIZEOF_BOOL 1 + #define AS_WINDOWS_THREADS + #define STDCALL __stdcall + + // Support native calling conventions on x86, but not 64bit yet + #if defined(_M_IX86) && !defined(__LP64__) + #define AS_X86 + #define ASM_INTEL // Intel style for inline assembly + #endif + + #if _MSC_VER <= 1200 // MSVC++ 6 + #define I64(x) x##l + #else + #define I64(x) x##ll + #endif + + #define UNREACHABLE_RETURN +#endif + +// SN Systems ProDG (also experimental, let me know if something isn't working) +#if defined(__SNC__) || defined(SNSYS) + #define GNU_STYLE_VIRTUAL_METHOD + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) + #define CALLEE_POPS_HIDDEN_RETURN_POINTER + #define COMPLEX_OBJS_PASSED_BY_REF + #define ASM_AT_N_T // AT&T style inline assembly + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + #define AS_SIZEOF_BOOL 1 + #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) + + // SN doesnt seem to like STDCALL. + // Maybe it can work with some fiddling, but I can't imagine linking to + // any STDCALL functions with a console anyway... + #define STDCALL + + // Linux specific + #ifdef __linux__ + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #endif + + // Support native calling conventions on x86, but not 64bit yet + #if defined(i386) && !defined(__LP64__) + #define AS_X86 + #endif + + #define I64(x) x##ll + + #define UNREACHABLE_RETURN +#endif + +// GNU C (and MinGW on Windows) +#if (defined(__GNUC__) && !defined(__SNC__)) || defined(EPPC) // JWC -- use this instead for Wii + #define GNU_STYLE_VIRTUAL_METHOD +#if !defined( __amd64__ ) + #define MULTI_BASE_OFFSET(x) (*((asDWORD*)(&x)+1)) +#else + #define MULTI_BASE_OFFSET(x) (*((asQWORD*)(&x)+1)) +#endif + #define asVSNPRINTF(a, b, c, d) vsnprintf(a, b, c, d) + #define CALLEE_POPS_HIDDEN_RETURN_POINTER + #define COMPLEX_OBJS_PASSED_BY_REF + #define COMPLEX_MASK (asOBJ_APP_CLASS_DESTRUCTOR) + #define AS_NO_MEMORY_H + #define AS_SIZEOF_BOOL 1 + #define STDCALL __attribute__((stdcall)) + #define ASM_AT_N_T + + // MacOSX and IPhone + #ifdef __APPLE__ + + // Is this a Mac or an IPhone? + #ifdef TARGET_OS_IPHONE + #define AS_IPHONE + #else + #define AS_MAC + #endif + + // The sizeof bool is different depending on the target CPU + #undef AS_SIZEOF_BOOL + #if defined(__ppc__) + #define AS_SIZEOF_BOOL 4 + // STDCALL is not available on PPC + #undef STDCALL + #define STDCALL + #else + #define AS_SIZEOF_BOOL 1 + #endif + + #if defined(i386) && !defined(__LP64__) + // Support native calling conventions on Mac OS X + Intel 32bit CPU + #define AS_X86 + #elif (defined(__ppc__) || defined(__PPC__)) && !defined(__LP64__) + // Support native calling conventions on Mac OS X + PPC 32bit CPU + #define AS_PPC + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #elif (defined(__ppc__) || defined(__PPC__)) && defined(__LP64__) + #define AS_PPC_64 + #elif (defined(_ARM_) || defined(__arm__)) + // The IPhone use an ARM processor + #define AS_ARM + #define AS_IPHONE + #define AS_ALIGN + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define COMPLEX_OBJS_PASSED_BY_REF + #undef COMPLEX_MASK + #define COMPLEX_MASK asOBJ_APP_CLASS_DESTRUCTOR + #else + // Unknown CPU type + #define AS_MAX_PORTABILITY + #endif + #define AS_POSIX_THREADS + + // Windows + #elif defined(WIN32) + // On Windows the simple classes are returned in the EAX:EDX registers + //#define THISCALL_RETURN_SIMPLE_IN_MEMORY + //#define CDECL_RETURN_SIMPLE_IN_MEMORY + //#define STDCALL_RETURN_SIMPLE_IN_MEMORY + + #if defined(i386) && !defined(__LP64__) + // Support native calling conventions on Intel 32bit CPU + #define AS_X86 + #else + // No support for native calling conventions yet + #define AS_MAX_PORTABILITY + // STDCALL is not available on 64bit Linux + #undef STDCALL + #define STDCALL + #endif + #define AS_WIN + #define AS_WINDOWS_THREADS + + // Linux + #elif defined(__linux__) + #if defined(i386) && !defined(__LP64__) + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + + // Support native calling conventions on Intel 32bit CPU + #define AS_X86 + #else + #define AS_X64_GCC + #define HAS_128_BIT_PRIMITIVES + #define SPLIT_OBJS_BY_MEMBER_TYPES + // STDCALL is not available on 64bit Linux + #undef STDCALL + #define STDCALL + #endif + #define AS_LINUX + #define AS_POSIX_THREADS + + #if !( ( (__GNUC__ == 4) && (__GNUC_MINOR__ >= 1) || __GNUC__ > 4) ) + // Only with GCC 4.1 was the atomic instructions available + #define AS_NO_ATOMIC + #endif + + // Free BSD + #elif __FreeBSD__ + #define AS_BSD + #if defined(i386) && !defined(__LP64__) + #define AS_X86 + #else + #define AS_MAX_PORTABILITY + #endif + #define AS_POSIX_THREADS + #if !( ( (__GNUC__ == 4) && (__GNUC_MINOR__ >= 1) || __GNUC__ > 4) ) + // Only with GCC 4.1 was the atomic instructions available + #define AS_NO_ATOMIC + #endif + + // PSP and PS2 + #elif defined(__PSP__) || defined(__psp__) || defined(_EE_) || defined(_PSP) || defined(_PS2) + // Support native calling conventions on MIPS architecture + #if (defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__)) && !defined(__LP64__) + #define AS_MIPS + #else + #define AS_MAX_PORTABILITY + #endif + + // PS3 + #elif (defined(__PPC__) || defined(__ppc__)) && defined(__PPU__) + // Support native calling conventions on PS3 + #define AS_PS3 + #define AS_PPC_64 + #define SPLIT_OBJS_BY_MEMBER_TYPES + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + // PS3 doesn't have STDCALL + #undef STDCALL + #define STDCALL + + // Dreamcast + #elif __SH4_SINGLE_ONLY__ + // Support native calling conventions on Dreamcast + #define AS_DC + #define AS_SH4 + + // Wii JWC - Close to PS3 just no PPC_64 and AS_PS3 + #elif defined(EPPC) + #define AS_WII + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #undef STDCALL + #define STDCALL + + // Android + #elif defined(ANDROID) + #define AS_ANDROID + #define AS_NO_ATOMIC + + #define CDECL_RETURN_SIMPLE_IN_MEMORY + #define STDCALL_RETURN_SIMPLE_IN_MEMORY + #define THISCALL_RETURN_SIMPLE_IN_MEMORY + + #undef THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + #undef STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE + + #define THISCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + #define STDCALL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2 + + #if (defined(_ARM_) || defined(__arm__)) + #define AS_ARM + #define AS_ALIGN + #endif + #endif + + #define I64(x) x##ll + + #define UNREACHABLE_RETURN +#endif + + +// +// Detect target hardware +//------------------------------------------------ + +// X86, Intel, AMD, etc, i.e. most PCs +#if defined(__i386__) || defined(_M_IX86) + // Nothing special here +#endif + +// MIPS architecture (generally PS2 and PSP consoles, potentially supports N64 as well) +#if defined(_MIPS_ARCH) || defined(_mips) || defined(__MIPSEL__) || defined(__PSP__) || defined(__psp__) || defined(_EE_) || defined(_PSP) || defined(_PS2) + #define AS_ALIGN // align datastructures + #define AS_USE_DOUBLE_AS_FLOAT // use 32bit floats instead of doubles +#endif + +// PowerPC, e.g. Mac, GameCube, PS3, XBox 360, Wii +#if defined(__PPC__) || defined(__ppc__) || defined(_PPC_) || defined(EPPC) + #define AS_BIG_ENDIAN + + // Gamecube + #if defined(_GC) + #define AS_ALIGN + #define AS_USE_DOUBLE_AS_FLOAT + #endif + // XBox 360 + #if (_XBOX_VER >= 200 ) + #define AS_ALIGN + #endif + // PS3 + #if defined(__PPU__) + #define AS_ALIGN + #endif + // Wii + #if defined(EPPC) + #define AS_ALIGN + #endif +#endif + +// Dreamcast console +#ifdef __SH4_SINGLE_ONLY__ + #define AS_ALIGN // align datastructures + #define AS_USE_DOUBLE_AS_FLOAT // use 32bit floats instead of doubles +#endif + +// Is the target a 64bit system? +#if defined(__LP64__) || defined(__amd64__) || defined(_M_X64) + #ifndef AS_64BIT_PTR + #define AS_64BIT_PTR + #endif +#endif + +// If there are no current support for native calling +// conventions, then compile with AS_MAX_PORTABILITY +#if (!defined(AS_X86) && !defined(AS_SH4) && !defined(AS_MIPS) && !defined(AS_PPC) && !defined(AS_PPC_64) && !defined(AS_XENON) && !defined(AS_X64_GCC) && !defined(AS_ARM)) + #ifndef AS_MAX_PORTABILITY + #define AS_MAX_PORTABILITY + #endif +#endif + +// If the form of threads to use hasn't been chosen +// then the library will be compiled without support +// for multithreading +#if !defined(AS_POSIX_THREADS) && !defined(AS_WINDOWS_THREADS) + #define AS_NO_THREADS +#endif + + +// The assert macro +#include +#define asASSERT(x) assert(x) + + + +// +// Internal defines (do not change these) +//---------------------------------------------------------------- + +#ifdef AS_ALIGN + #define ALIGN(b) (((b)+3)&(~3)) +#else + #define ALIGN(b) (b) +#endif + +#define ARG_W(b) ((asWORD*)&b) +#define ARG_DW(b) ((asDWORD*)&b) +#define ARG_QW(b) ((asQWORD*)&b) +#define BCARG_W(b) ((asWORD*)&(b)[1]) +#define BCARG_DW(b) ((asDWORD*)&(b)[1]) +#define BCARG_QW(b) ((asQWORD*)&(b)[1]) + +#ifdef AS_64BIT_PTR + #define AS_PTR_SIZE 2 + #define asPTRWORD asQWORD + #define asBC_RDSPTR asBC_RDS8 +#else + #define AS_PTR_SIZE 1 + #define asPTRWORD asDWORD + #define asBC_RDSPTR asBC_RDS4 +#endif +#define ARG_PTR(b) ((asPTRWORD*)&b) +#define BCARG_PTR(b) ((asPTRWORD*)&(b)[1]) + +// This macro is used to avoid warnings about unused variables. +// Usually where the variables are only used in debug mode. +#define UNUSED_VAR(x) (x)=(x) + +#include "../include/angelscript.h" +#include "as_memory.h" + +#ifdef AS_USE_NAMESPACE +using namespace AngelScript; +#endif + +#endif diff --git a/AngelScript/source/as_configgroup.cpp b/AngelScript/source/as_configgroup.cpp new file mode 100644 index 000000000..dc977800b --- /dev/null +++ b/AngelScript/source/as_configgroup.cpp @@ -0,0 +1,232 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_configgroup.cpp +// +// This class holds configuration groups for the engine +// + + + +#include "as_config.h" +#include "as_configgroup.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCConfigGroup::asCConfigGroup() +{ + refCount = 0; + defaultAccess = true; +} + +asCConfigGroup::~asCConfigGroup() +{ +} + +int asCConfigGroup::AddRef() +{ + refCount++; + return refCount; +} + +int asCConfigGroup::Release() +{ + // Don't delete the object here, the engine will delete the object when ready + refCount--; + return refCount; +} + +asCObjectType *asCConfigGroup::FindType(const char *obj) +{ + for( asUINT n = 0; n < objTypes.GetLength(); n++ ) + if( objTypes[n]->name == obj ) + return objTypes[n]; + + return 0; +} + +void asCConfigGroup::RefConfigGroup(asCConfigGroup *group) +{ + if( group == this || group == 0 ) return; + + // Verify if the group is already referenced + for( asUINT n = 0; n < referencedConfigGroups.GetLength(); n++ ) + if( referencedConfigGroups[n] == group ) + return; + + referencedConfigGroups.PushLast(group); + group->AddRef(); +} + +bool asCConfigGroup::HasLiveObjects() +{ + for( asUINT n = 0; n < objTypes.GetLength(); n++ ) + if( objTypes[n]->GetRefCount() != 0 ) + return true; + + return false; +} + +void asCConfigGroup::RemoveConfiguration(asCScriptEngine *engine) +{ + asASSERT( refCount == 0 ); + + asUINT n; + + // Remove global variables + for( n = 0; n < globalProps.GetLength(); n++ ) + { + int index = engine->registeredGlobalProps.IndexOf(globalProps[n]); + if( index >= 0 ) + { + globalProps[n]->Release(); + + // TODO: global: Should compact the registeredGlobalProps array + engine->registeredGlobalProps[index] = 0; + } + } + + // Remove global functions + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + { + scriptFunctions[n]->Release(); + engine->registeredGlobalFuncs.RemoveValue(scriptFunctions[n]); + if( engine->stringFactory == scriptFunctions[n] ) + engine->stringFactory = 0; + } + scriptFunctions.SetLength(0); + + // Remove behaviours and members of object types + for( n = 0; n < objTypes.GetLength(); n++ ) + { + asCObjectType *obj = objTypes[n]; + + obj->ReleaseAllFunctions(); + } + + + // Remove object types + for( n = 0; n < objTypes.GetLength(); n++ ) + { + asCObjectType *t = objTypes[n]; + int idx = engine->objectTypes.IndexOf(t); + if( idx >= 0 ) + { +#ifdef AS_DEBUG + ValidateNoUsage(engine, t); +#endif + + engine->objectTypes.RemoveIndex(idx); + + if( t->flags & asOBJ_TYPEDEF ) + engine->registeredTypeDefs.RemoveValue(t); + else if( t->flags & asOBJ_ENUM ) + engine->registeredEnums.RemoveValue(t); + else + engine->registeredObjTypes.RemoveValue(t); + + asDELETE(t, asCObjectType); + } + } + + // Release other config groups + for( n = 0; n < referencedConfigGroups.GetLength(); n++ ) + referencedConfigGroups[n]->refCount--; + referencedConfigGroups.SetLength(0); +} + +#ifdef AS_DEBUG +void asCConfigGroup::ValidateNoUsage(asCScriptEngine *engine, asCObjectType *type) +{ + for( asUINT n = 0; n < engine->scriptFunctions.GetLength(); n++ ) + { + asCScriptFunction *func = engine->scriptFunctions[n]; + if( func == 0 ) continue; + + // Ignore factory and members + if( func->name == "_beh_2_" || func->objectType == type ) + continue; + + asASSERT( func->returnType.GetObjectType() != type ); + + for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) + { + asASSERT(func->parameterTypes[p].GetObjectType() != type); + } + } + + // TODO: Check also usage of the type in global variables + + // TODO: Check also usage of the type in local variables in script functions + + // TODO: Check also usage of the type as members of classes + + // TODO: Check also usage of the type as sub types in other types +} +#endif + +int asCConfigGroup::SetModuleAccess(const char *module, bool hasAccess) +{ + if( module == asALL_MODULES ) + { + // Set default module access + defaultAccess = hasAccess; + } + else + { + asCString mod(module ? module : ""); + asSMapNode *cursor = 0; + if( moduleAccess.MoveTo(&cursor, mod) ) + { + moduleAccess.GetValue(cursor) = hasAccess; + } + else + { + moduleAccess.Insert(mod, hasAccess); + } + } + + return 0; +} + +bool asCConfigGroup::HasModuleAccess(const char *module) +{ + asCString mod(module ? module : ""); + asSMapNode *cursor = 0; + if( moduleAccess.MoveTo(&cursor, mod) ) + return moduleAccess.GetValue(cursor); + + return defaultAccess; +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_configgroup.h b/AngelScript/source/as_configgroup.h new file mode 100644 index 000000000..55d1e8abc --- /dev/null +++ b/AngelScript/source/as_configgroup.h @@ -0,0 +1,89 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_configgroup.h +// +// This class holds configuration groups for the engine +// + + + +#ifndef AS_CONFIGGROUP_H +#define AS_CONFIGGROUP_H + +#include "as_config.h" +#include "as_string.h" +#include "as_array.h" +#include "as_objecttype.h" +#include "as_map.h" + +BEGIN_AS_NAMESPACE + +class asCConfigGroup +{ +public: + asCConfigGroup(); + ~asCConfigGroup(); + + // Memory management + int AddRef(); + int Release(); + + asCObjectType *FindType(const char *obj); + void RefConfigGroup(asCConfigGroup *group); + + bool HasLiveObjects(); + void RemoveConfiguration(asCScriptEngine *engine); + + int SetModuleAccess(const char *module, bool hasAccess); + bool HasModuleAccess(const char *module); + +#ifdef AS_DEBUG + void ValidateNoUsage(asCScriptEngine *engine, asCObjectType *type); +#endif + + asCString groupName; + int refCount; + + asCArray objTypes; + asCArray scriptFunctions; + asCArray globalProps; + asCArray referencedConfigGroups; + + // Module access + bool defaultAccess; + asCMap moduleAccess; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_context.cpp b/AngelScript/source/as_context.cpp new file mode 100644 index 000000000..ef39d5f92 --- /dev/null +++ b/AngelScript/source/as_context.cpp @@ -0,0 +1,3816 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_context.cpp +// +// This class handles the execution of the byte code +// + +#include // fmodf() + +#include "as_config.h" +#include "as_context.h" +#include "as_scriptengine.h" +#include "as_tokendef.h" +#include "as_texts.h" +#include "as_callfunc.h" +#include "as_generic.h" +#include "as_debug.h" // mkdir() +#include "as_bytecode.h" +#include "as_scriptobject.h" + +#ifdef _MSC_VER +#pragma warning(disable:4702) // unreachable code +#endif + +BEGIN_AS_NAMESPACE + +// We need at least 2 DWORDs reserved for exception handling +// We need at least 1 DWORD reserved for calling system functions +const int RESERVE_STACK = 2*AS_PTR_SIZE; + +// For each script function call we push 5 DWORDs on the call stack +const int CALLSTACK_FRAME_SIZE = 5; + + +#ifdef AS_DEBUG +// Instruction statistics +int instrCount[256]; + +int instrCount2[256][256]; +int lastBC; + +class asCDebugStats +{ +public: + asCDebugStats() + { + memset(instrCount, 0, sizeof(instrCount)); + } + + ~asCDebugStats() + { +/* + // This code writes out some statistics for the VM. + // It's useful for determining what needs to be optimized. + + _mkdir("AS_DEBUG"); + FILE *f = fopen("AS_DEBUG/total.txt", "at"); + if( f ) + { + // Output instruction statistics + fprintf(f, "\nTotal count\n"); + int n; + for( n = 0; n < BC_MAXBYTECODE; n++ ) + { + if( bcName[n].name && instrCount[n] > 0 ) + fprintf(f, "%-10.10s : %.0f\n", bcName[n].name, instrCount[n]); + } + + fprintf(f, "\nNever executed\n"); + for( n = 0; n < BC_MAXBYTECODE; n++ ) + { + if( bcName[n].name && instrCount[n] == 0 ) + fprintf(f, "%-10.10s\n", bcName[n].name); + } + + fclose(f); + } +*/ + } + + double instrCount[256]; +} stats; +#endif + +AS_API asIScriptContext *asGetActiveContext() +{ + asASSERT(threadManager); + asCThreadLocalData *tld = threadManager->GetLocalData(); + if( tld->activeContexts.GetLength() == 0 ) + return 0; + return tld->activeContexts[tld->activeContexts.GetLength()-1]; +} + +void asPushActiveContext(asIScriptContext *ctx) +{ + asASSERT(threadManager); + asCThreadLocalData *tld = threadManager->GetLocalData(); + tld->activeContexts.PushLast(ctx); +} + +void asPopActiveContext(asIScriptContext *ctx) +{ + asASSERT(threadManager); + asCThreadLocalData *tld = threadManager->GetLocalData(); + + asASSERT(tld->activeContexts.GetLength() > 0); + asASSERT(tld->activeContexts[tld->activeContexts.GetLength()-1] == ctx); + UNUSED_VAR(ctx); + + tld->activeContexts.PopLast(); +} + +asCContext::asCContext(asCScriptEngine *engine, bool holdRef) +{ +#ifdef AS_DEBUG + memset(instrCount, 0, sizeof(instrCount)); + + memset(instrCount2, 0, sizeof(instrCount2)); + + lastBC = 255; +#endif + + holdEngineRef = holdRef; + if( holdRef ) + engine->AddRef(); + this->engine = engine; + + status = asEXECUTION_UNINITIALIZED; + stackBlockSize = 0; + refCount.set(1); + inExceptionHandler = false; + isStackMemoryNotAllocated = false; + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 + stringFunction = 0; +#endif + currentFunction = 0; + regs.objectRegister = 0; + initialFunction = 0; + + lineCallback = false; + exceptionCallback = false; + + regs.doProcessSuspend = false; + doSuspend = false; + + userData = 0; +} + +asCContext::~asCContext() +{ + DetachEngine(); +} + +int asCContext::AddRef() +{ + return refCount.atomicInc(); +} + +int asCContext::Release() +{ + int r = refCount.atomicDec(); + + if( r == 0 ) + { + asDELETE(this,asCContext); + return 0; + } + + return r; +} + +void asCContext::DetachEngine() +{ + if( engine == 0 ) return; + + // Abort any execution + Abort(); + + // Free all resources + Unprepare(); + + // Clear engine pointer + if( holdEngineRef ) + engine->Release(); + engine = 0; +} + +asIScriptEngine *asCContext::GetEngine() +{ + return engine; +} + +void *asCContext::SetUserData(void *data) +{ + void *oldData = userData; + userData = data; + return oldData; +} + +void *asCContext::GetUserData() +{ + return userData; +} + +int asCContext::Prepare(int funcID) +{ + if( status == asEXECUTION_ACTIVE || status == asEXECUTION_SUSPENDED ) + return asCONTEXT_ACTIVE; + + // Clean the stack if not done before + if( status != asEXECUTION_FINISHED && status != asEXECUTION_UNINITIALIZED ) + CleanStack(); + + // Release the returned object (if any) + CleanReturnObject(); + + if( funcID == -1 ) + { + // Use the previously prepared function + if( initialFunction == 0 ) + return asNO_FUNCTION; + + currentFunction = initialFunction; + } + else if( initialFunction && initialFunction->id == funcID ) + { + currentFunction = initialFunction; + } + else + { + // Check engine pointer + asASSERT( engine ); + + if( initialFunction ) + initialFunction->Release(); + + initialFunction = engine->GetScriptFunction(funcID); + if( initialFunction == 0 ) + return asNO_FUNCTION; + + initialFunction->AddRef(); + currentFunction = initialFunction; + + // Determine the minimum stack size needed + // TODO: optimize: GetSpaceNeededForArguments() should be precomputed + int stackSize = currentFunction->GetSpaceNeededForArguments() + currentFunction->stackNeeded + RESERVE_STACK; + + stackSize = stackSize > engine->initialContextStackSize ? stackSize : engine->initialContextStackSize; + + if( stackSize > stackBlockSize ) + { + for( asUINT n = 0; n < stackBlocks.GetLength(); n++ ) + if( stackBlocks[n] ) + { + asDELETEARRAY(stackBlocks[n]); + } + stackBlocks.SetLength(0); + + stackBlockSize = stackSize; + + asDWORD *stack = asNEWARRAY(asDWORD,stackBlockSize); + stackBlocks.PushLast(stack); + } + + // Reserve space for the arguments and return value + returnValueSize = currentFunction->GetSpaceNeededForReturnValue(); + + // TODO: optimize: GetSpaceNeededForArguments() should be precomputed + argumentsSize = currentFunction->GetSpaceNeededForArguments() + (currentFunction->objectType ? AS_PTR_SIZE : 0); + } + + // Reset state + // Most of the time the previous state will be asEXECUTION_FINISHED, in which case the values are already initialized + if( status != asEXECUTION_FINISHED ) + { + exceptionLine = -1; + exceptionFunction = 0; + isCallingSystemFunction = false; + doAbort = false; + doSuspend = false; + regs.doProcessSuspend = lineCallback; + externalSuspendRequest = false; + stackIndex = 0; + } + status = asEXECUTION_PREPARED; + + // Reserve space for the arguments and return value + regs.stackFramePointer = stackBlocks[0] + stackBlockSize - argumentsSize; + regs.stackPointer = regs.stackFramePointer; + + // Set arguments to 0 + memset(regs.stackPointer, 0, 4*argumentsSize); + + if( currentFunction->funcType == asFUNC_SCRIPT ) + { + regs.programPointer = currentFunction->byteCode.AddressOf(); + + // Set all object variables to 0 + for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ ) + { + int pos = currentFunction->objVariablePos[n]; + *(size_t*)®s.stackFramePointer[-pos] = 0; + } + } + else + regs.programPointer = 0; + + return asSUCCESS; +} + +// Free all resources +int asCContext::Unprepare() +{ + if( status == asEXECUTION_ACTIVE || status == asEXECUTION_SUSPENDED ) + return asCONTEXT_ACTIVE; + + // Only clean the stack if the context was prepared but not executed + if( status != asEXECUTION_UNINITIALIZED ) + CleanStack(); + + // Release the returned object (if any) + CleanReturnObject(); + + // Release the initial function + if( initialFunction ) + initialFunction->Release(); + + // Clear function pointers + initialFunction = 0; + currentFunction = 0; + exceptionFunction = 0; + regs.programPointer = 0; + + // Reset status + status = asEXECUTION_UNINITIALIZED; + + // Deallocate the stack blocks + for( asUINT n = 0; n < stackBlocks.GetLength(); n++ ) + { + if( stackBlocks[n] ) + { + asDELETEARRAY(stackBlocks[n]); + } + } + stackBlocks.SetLength(0); + stackBlockSize = 0; + regs.stackFramePointer = 0; + regs.stackPointer = 0; + stackIndex = 0; + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 + // Deallocate string function + if( stringFunction ) + { + stringFunction->Release(); + stringFunction = 0; + } +#endif + + return 0; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 +int asCContext::SetExecuteStringFunction(asCScriptFunction *func) +{ + if( stringFunction ) + stringFunction->Release(); + + // The new function already has the refCount set to 1 + stringFunction = func; + + return 0; +} +#endif + +asBYTE asCContext::GetReturnByte() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( dt->IsObject() || dt->IsReference() ) return 0; + + return *(asBYTE*)®s.valueRegister; +} + +asWORD asCContext::GetReturnWord() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( dt->IsObject() || dt->IsReference() ) return 0; + + return *(asWORD*)®s.valueRegister; +} + +asDWORD asCContext::GetReturnDWord() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( dt->IsObject() || dt->IsReference() ) return 0; + + return *(asDWORD*)®s.valueRegister; +} + +asQWORD asCContext::GetReturnQWord() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( dt->IsObject() || dt->IsReference() ) return 0; + + return regs.valueRegister; +} + +float asCContext::GetReturnFloat() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( dt->IsObject() || dt->IsReference() ) return 0; + + return *(float*)®s.valueRegister; +} + +double asCContext::GetReturnDouble() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( dt->IsObject() || dt->IsReference() ) return 0; + + return *(double*)®s.valueRegister; +} + +void *asCContext::GetReturnAddress() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( dt->IsReference() ) + return *(void**)®s.valueRegister; + else if( dt->IsObject() ) + return regs.objectRegister; + + return 0; +} + +void *asCContext::GetReturnObject() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + if( !dt->IsObject() ) return 0; + + if( dt->IsReference() ) + return *(void**)(size_t)regs.valueRegister; + else + return regs.objectRegister; +} + +void *asCContext::GetAddressOfReturnValue() +{ + if( status != asEXECUTION_FINISHED ) return 0; + + asCDataType *dt = &initialFunction->returnType; + + // An object is stored in the objectRegister + if( !dt->IsReference() && dt->IsObject() ) + { + // Need to dereference objects + if( !dt->IsObjectHandle() ) + return *(void**)®s.objectRegister; + return ®s.objectRegister; + } + + // Primitives and references are stored in valueRegister + return ®s.valueRegister; +} + +int asCContext::SetObject(void *obj) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( !initialFunction->objectType ) + { + status = asEXECUTION_ERROR; + return asERROR; + } + + *(size_t*)®s.stackFramePointer[0] = (size_t)obj; + + return 0; +} + +int asCContext::SetArgByte(asUINT arg, asBYTE value) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeInMemoryBytes() != 1 ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asBYTE*)®s.stackFramePointer[offset] = value; + + return 0; +} + +int asCContext::SetArgWord(asUINT arg, asWORD value) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeInMemoryBytes() != 2 ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asWORD*)®s.stackFramePointer[offset] = value; + + return 0; +} + +int asCContext::SetArgDWord(asUINT arg, asDWORD value) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeInMemoryBytes() != 4 ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asDWORD*)®s.stackFramePointer[offset] = value; + + return 0; +} + +int asCContext::SetArgQWord(asUINT arg, asQWORD value) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeOnStackDWords() != 2 ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(asQWORD*)(®s.stackFramePointer[offset]) = value; + + return 0; +} + +int asCContext::SetArgFloat(asUINT arg, float value) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeOnStackDWords() != 1 ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(float*)(®s.stackFramePointer[offset]) = value; + + return 0; +} + +int asCContext::SetArgDouble(asUINT arg, double value) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + if( dt->GetSizeOnStackDWords() != 2 ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(double*)(®s.stackFramePointer[offset]) = value; + + return 0; +} + +int asCContext::SetArgAddress(asUINT arg, void *value) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( !dt->IsReference() && !dt->IsObjectHandle() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(size_t*)(®s.stackFramePointer[offset]) = (size_t)value; + + return 0; +} + +int asCContext::SetArgObject(asUINT arg, void *obj) +{ + if( status != asEXECUTION_PREPARED ) + return asCONTEXT_NOT_PREPARED; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + { + status = asEXECUTION_ERROR; + return asINVALID_ARG; + } + + // Verify the type of the argument + asCDataType *dt = &initialFunction->parameterTypes[arg]; + if( !dt->IsObject() ) + { + status = asEXECUTION_ERROR; + return asINVALID_TYPE; + } + + // If the object should be sent by value we must make a copy of it + if( !dt->IsReference() ) + { + if( dt->IsObjectHandle() ) + { + // Increase the reference counter + asSTypeBehaviour *beh = &dt->GetObjectType()->beh; + if( obj && beh->addref ) + engine->CallObjectMethod(obj, beh->addref); + } + else + { + obj = engine->CreateScriptObjectCopy(obj, engine->GetTypeIdFromDataType(*dt)); + } + } + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Set the value + *(size_t*)(®s.stackFramePointer[offset]) = (size_t)obj; + + return 0; +} + + +// TODO: Instead of GetAddressOfArg, maybe we need a SetArgValue(int arg, void *value, bool takeOwnership) instead. + +// interface +void *asCContext::GetAddressOfArg(asUINT arg) +{ + if( status != asEXECUTION_PREPARED ) + return 0; + + if( arg >= (unsigned)initialFunction->parameterTypes.GetLength() ) + return 0; + + // Determine the position of the argument + int offset = 0; + if( initialFunction->objectType ) + offset += AS_PTR_SIZE; + for( asUINT n = 0; n < arg; n++ ) + offset += initialFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // We should return the address of the location where the argument value will be placed + + // All registered types are always sent by reference, even if + // the function is declared to receive the argument by value. + return ®s.stackFramePointer[offset]; +} + + +int asCContext::Abort() +{ + // TODO: multithread: Make thread safe + + if( engine == 0 ) return asERROR; + + if( status == asEXECUTION_SUSPENDED ) + status = asEXECUTION_ABORTED; + + doSuspend = true; + regs.doProcessSuspend = true; + externalSuspendRequest = true; + doAbort = true; + + return 0; +} + +// interface +int asCContext::Suspend() +{ + // This function just sets some internal flags and is safe + // to call from a secondary thread, even if the library has + // been built without multi-thread support. + + if( engine == 0 ) return asERROR; + + doSuspend = true; + externalSuspendRequest = true; + regs.doProcessSuspend = true; + + return 0; +} + +// interface +int asCContext::Execute() +{ + asASSERT( engine != 0 ); + + if( status != asEXECUTION_SUSPENDED && status != asEXECUTION_PREPARED ) + return asERROR; + + status = asEXECUTION_ACTIVE; + + asPushActiveContext((asIScriptContext *)this); + + if( regs.programPointer == 0 ) + { + if( currentFunction->funcType == asFUNC_VIRTUAL || + currentFunction->funcType == asFUNC_INTERFACE ) + { + // The currentFunction is a virtual method + + // Determine the true function from the object + asCScriptObject *obj = *(asCScriptObject**)(size_t*)regs.stackFramePointer; + if( obj == 0 ) + { + SetInternalException(TXT_NULL_POINTER_ACCESS); + } + else + { + asCObjectType *objType = obj->objType; + asCScriptFunction *realFunc = 0; + + if( currentFunction->funcType == asFUNC_VIRTUAL ) + { + if( objType->virtualFunctionTable.GetLength() > (asUINT)currentFunction->vfTableIdx ) + { + realFunc = objType->virtualFunctionTable[currentFunction->vfTableIdx]; + } + } + else + { + // Search the object type for a function that matches the interface function + for( asUINT n = 0; n < objType->methods.GetLength(); n++ ) + { + asCScriptFunction *f2 = engine->scriptFunctions[objType->methods[n]]; + if( f2->signatureId == currentFunction->signatureId ) + { + if( f2->funcType == asFUNC_VIRTUAL ) + realFunc = objType->virtualFunctionTable[f2->vfTableIdx]; + else + realFunc = f2; + break; + } + } + } + + if( realFunc ) + { + if( realFunc->signatureId != currentFunction->signatureId ) + { + SetInternalException(TXT_NULL_POINTER_ACCESS); + } + else + { + currentFunction = realFunc; + regs.programPointer = currentFunction->byteCode.AddressOf(); + + // Set the local objects to 0 + for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ ) + { + int pos = currentFunction->objVariablePos[n]; + *(size_t*)®s.stackFramePointer[-pos] = 0; + } + } + } + } + } + else if( currentFunction->funcType == asFUNC_SYSTEM ) + { + // The current function is an application registered function + + // Call the function directly + CallSystemFunction(currentFunction->id, this, 0); + + // Was the call successful? + if( status == asEXECUTION_ACTIVE ) + { + status = asEXECUTION_FINISHED; + } + } + else + { + // This shouldn't happen + asASSERT(false); + } + } + + while( status == asEXECUTION_ACTIVE ) + ExecuteNext(); + + doSuspend = false; + regs.doProcessSuspend = lineCallback; + + asPopActiveContext((asIScriptContext *)this); + + +#ifdef AS_DEBUG +/* + // Output instruction statistics + // This is useful for determining what needs to be optimized. + + _mkdir("AS_DEBUG"); + FILE *f = fopen("AS_DEBUG/stats.txt", "at"); + fprintf(f, "\n"); + asQWORD total = 0; + int n; + for( n = 0; n < 256; n++ ) + { + if( bcName[n].name && instrCount[n] ) + fprintf(f, "%-10.10s : %d\n", bcName[n].name, instrCount[n]); + + total += instrCount[n]; + } + + fprintf(f, "\ntotal : %I64d\n", total); + + fprintf(f, "\n"); + for( n = 0; n < 256; n++ ) + { + if( bcName[n].name ) + { + for( int m = 0; m < 256; m++ ) + { + if( instrCount2[n][m] ) + fprintf(f, "%-10.10s, %-10.10s : %d\n", bcName[n].name, bcName[m].name, instrCount2[n][m]); + } + } + } + fclose(f); +*/ +#endif + + if( status == asEXECUTION_FINISHED ) + { + regs.objectType = initialFunction->returnType.GetObjectType(); + return asEXECUTION_FINISHED; + } + + if( status == asEXECUTION_SUSPENDED ) + return asEXECUTION_SUSPENDED; + + if( doAbort ) + { + doAbort = false; + + status = asEXECUTION_ABORTED; + return asEXECUTION_ABORTED; + } + + if( status == asEXECUTION_EXCEPTION ) + return asEXECUTION_EXCEPTION; + + return asERROR; +} + +void asCContext::PushCallState() +{ + callStack.SetLength(callStack.GetLength() + CALLSTACK_FRAME_SIZE); + + // Separating the loads and stores limits data cache trash, and with a smart compiler + // could turn into SIMD style loading/storing if available. + // The compiler can't do this itself due to potential pointer aliasing between the pointers, + // ie writing to tmp could overwrite the data contained in registers.stackFramePointer for example + // for all the compiler knows. So introducing the local variable s, which is never referred to by + // its address we avoid this issue. + + size_t s[5]; + s[0] = (size_t)regs.stackFramePointer; + s[1] = (size_t)currentFunction; + s[2] = (size_t)regs.programPointer; + s[3] = (size_t)regs.stackPointer; + s[4] = stackIndex; + + size_t *tmp = callStack.AddressOf() + callStack.GetLength() - CALLSTACK_FRAME_SIZE; + tmp[0] = s[0]; + tmp[1] = s[1]; + tmp[2] = s[2]; + tmp[3] = s[3]; + tmp[4] = s[4]; +} + +void asCContext::PopCallState() +{ + // See comments in PushCallState about pointer aliasing and data cache trashing + size_t *tmp = callStack.AddressOf() + callStack.GetLength() - CALLSTACK_FRAME_SIZE; + size_t s[5]; + s[0] = tmp[0]; + s[1] = tmp[1]; + s[2] = tmp[2]; + s[3] = tmp[3]; + s[4] = tmp[4]; + + regs.stackFramePointer = (asDWORD*)s[0]; + currentFunction = (asCScriptFunction*)s[1]; + regs.programPointer = (asDWORD*)s[2]; + regs.stackPointer = (asDWORD*)s[3]; + stackIndex = (int)s[4]; + + callStack.SetLength(callStack.GetLength() - CALLSTACK_FRAME_SIZE); +} + +int asCContext::GetCallstackSize() +{ + return (int)callStack.GetLength() / CALLSTACK_FRAME_SIZE; +} + +int asCContext::GetCallstackFunction(int index) +{ + if( index < 0 || index >= GetCallstackSize() ) return asINVALID_ARG; + + size_t *s = callStack.AddressOf() + index*CALLSTACK_FRAME_SIZE; + asCScriptFunction *func = (asCScriptFunction*)s[1]; + + return func->id; +} + +int asCContext::GetCallstackLineNumber(int index, int *column) +{ + if( index < 0 || index >= GetCallstackSize() ) return asINVALID_ARG; + + size_t *s = callStack.AddressOf() + index*CALLSTACK_FRAME_SIZE; + asCScriptFunction *func = (asCScriptFunction*)s[1]; + asDWORD *bytePos = (asDWORD*)s[2]; + + asDWORD line = func->GetLineNumber(int(bytePos - func->byteCode.AddressOf())); + if( column ) *column = (line >> 20); + + return (line & 0xFFFFF); +} + +void asCContext::CallScriptFunction(asCScriptFunction *func) +{ + // Push the framepointer, function id and programCounter on the stack + PushCallState(); + + currentFunction = func; + + regs.programPointer = currentFunction->byteCode.AddressOf(); + + // Verify if there is enough room in the stack block. Allocate new block if not + if( regs.stackPointer - (func->stackNeeded + RESERVE_STACK) < stackBlocks[stackIndex] ) + { + asDWORD *oldStackPointer = regs.stackPointer; + + // The size of each stack block is determined by the following formula: + // size = stackBlockSize << index + + while( regs.stackPointer - (func->stackNeeded + RESERVE_STACK) < stackBlocks[stackIndex] ) + { + // Make sure we don't allocate more space than allowed + if( engine->ep.maximumContextStackSize ) + { + // This test will only stop growth once it has already crossed the limit + if( stackBlockSize * ((1 << (stackIndex+1)) - 1) > engine->ep.maximumContextStackSize ) + { + isStackMemoryNotAllocated = true; + + // Set the stackFramePointer, even though the stackPointer wasn't updated + regs.stackFramePointer = regs.stackPointer; + + // TODO: Make sure the exception handler doesn't try to free objects that have not been initialized + SetInternalException(TXT_STACK_OVERFLOW); + return; + } + } + + stackIndex++; + if( (int)stackBlocks.GetLength() == stackIndex ) + { + asDWORD *stack = asNEWARRAY(asDWORD,(stackBlockSize << stackIndex)); + stackBlocks.PushLast(stack); + } + + regs.stackPointer = stackBlocks[stackIndex] + (stackBlockSize<GetSpaceNeededForArguments(); + } + + // Copy the function arguments to the new stack space + memcpy(regs.stackPointer, oldStackPointer, sizeof(asDWORD)*func->GetSpaceNeededForArguments()); + } + + // Update framepointer and programCounter + regs.stackFramePointer = regs.stackPointer; + + // Set all object variables to 0 + for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ ) + { + int pos = currentFunction->objVariablePos[n]; + *(size_t*)®s.stackFramePointer[-pos] = 0; + } +} + +void asCContext::CallInterfaceMethod(asCScriptFunction *func) +{ + // Resolve the interface method using the current script type + asCScriptObject *obj = *(asCScriptObject**)(size_t*)regs.stackPointer; + if( obj == 0 ) + { + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + + asCObjectType *objType = obj->objType; + + // TODO: optimize: The object type should have a list of only those methods that + // implement interface methods. This list should be ordered by + // the signatureId so that a binary search can be made, instead + // of a linear search. + // + // When this is done, we must also make sure the signatureId of a + // function never changes, e.g. when if the signature functions are + // released. + + // Search the object type for a function that matches the interface function + asCScriptFunction *realFunc = 0; + if( func->funcType == asFUNC_INTERFACE ) + { + for( asUINT n = 0; n < objType->methods.GetLength(); n++ ) + { + asCScriptFunction *f2 = engine->scriptFunctions[objType->methods[n]]; + if( f2->signatureId == func->signatureId ) + { + if( f2->funcType == asFUNC_VIRTUAL ) + realFunc = objType->virtualFunctionTable[f2->vfTableIdx]; + else + realFunc = f2; + break; + } + } + + if( realFunc == 0 ) + { + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + else /* if( func->funcType == asFUNC_VIRTUAL ) */ + { + realFunc = objType->virtualFunctionTable[func->vfTableIdx]; + } + + // Then call the true script function + CallScriptFunction(realFunc); +} + +void asCContext::ExecuteNext() +{ + asDWORD *l_bc = regs.programPointer; + asDWORD *l_sp = regs.stackPointer; + asDWORD *l_fp = regs.stackFramePointer; + + for(;;) + { + +#ifdef AS_DEBUG + ++stats.instrCount[*(asBYTE*)l_bc]; + + ++instrCount[*(asBYTE*)l_bc]; + + ++instrCount2[lastBC][*(asBYTE*)l_bc]; + lastBC = *(asBYTE*)l_bc; + + // Used to verify that the size of the instructions are correct + asDWORD *old = l_bc; +#endif + + + // Remember to keep the cases in order and without + // gaps, because that will make the switch faster. + // It will be faster since only one lookup will be + // made to find the correct jump destination. If not + // in order, the switch will make two lookups. + switch( *(asBYTE*)l_bc ) + { +//-------------- +// memory access functions + + // Decrease the stack pointer with n dwords (stack grows downward) + case asBC_POP: + l_sp += asBC_WORDARG0(l_bc); + l_bc++; + break; + + // Increase the stack pointer with n dwords + case asBC_PUSH: + l_sp -= asBC_WORDARG0(l_bc); + l_bc++; + break; + + // Push a dword value on the stack + case asBC_PshC4: + --l_sp; + *l_sp = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + // Push the dword value of a variable on the stack + case asBC_PshV4: + --l_sp; + *l_sp = *(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Push the address of a variable on the stack + case asBC_PSF: + l_sp -= AS_PTR_SIZE; + *(asPTRWORD*)l_sp = (asPTRWORD)size_t(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Swap the top 2 dwords on the stack + case asBC_SWAP4: + { + asDWORD d = (asDWORD)*l_sp; + *l_sp = *(l_sp+1); + *(asDWORD*)(l_sp+1) = d; + l_bc++; + } + break; + + // Do a boolean not operation, modifying the value of the variable + case asBC_NOT: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is equal to 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on the pointer. + + volatile asBYTE *ptr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + asBYTE val = (ptr[0] == 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + ptr[0] = val; // The result is stored in the lower byte + ptr[1] = 0; // Make sure the rest of the DWORD is 0 + ptr[2] = 0; + ptr[3] = 0; + } +#else + *(l_fp - asBC_SWORDARG0(l_bc)) = (*(l_fp - asBC_SWORDARG0(l_bc)) == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // Push the dword value of a global variable on the stack + case asBC_PshG4: + --l_sp; + *l_sp = *(asDWORD*)(size_t)asBC_PTRARG(l_bc); + l_bc += 1 + AS_PTR_SIZE; + break; + + // Load the address of a global variable in the register, then + // copy the value of the global variable into a local variable + case asBC_LdGRdR4: + *(void**)®s.valueRegister = (void*)(size_t)asBC_PTRARG(l_bc); + *(l_fp - asBC_SWORDARG0(l_bc)) = **(asDWORD**)®s.valueRegister; + l_bc += 1+AS_PTR_SIZE; + break; + +//---------------- +// path control instructions + + // Begin execution of a script function + case asBC_CALL: + { + int i = asBC_INTARG(l_bc); + l_bc += 2; + + asASSERT( i >= 0 ); + asASSERT( (i & FUNC_IMPORTED) == 0 ); + + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + CallScriptFunction(engine->scriptFunctions[i]); + + // Extract the values from the context again + l_bc = regs.programPointer; + l_sp = regs.stackPointer; + l_fp = regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( status != asEXECUTION_ACTIVE ) + return; + } + break; + + // Return to the caller, and remove the arguments from the stack + case asBC_RET: + { + if( callStack.GetLength() == 0 ) + { + status = asEXECUTION_FINISHED; + return; + } + + asWORD w = asBC_WORDARG0(l_bc); + + // Read the old framepointer, functionid, and programCounter from the call stack + PopCallState(); + + // Extract the values from the context again + l_bc = regs.programPointer; + l_sp = regs.stackPointer; + l_fp = regs.stackFramePointer; + + // Pop arguments from stack + l_sp += w; + } + break; + + // Jump to a relative position + case asBC_JMP: + l_bc += 2 + asBC_INTARG(l_bc); + break; + +//---------------- +// Conditional jumps + + // Jump to a relative position if the value in the register is 0 + case asBC_JZ: + if( *(int*)®s.valueRegister == 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is not 0 + case asBC_JNZ: + if( *(int*)®s.valueRegister != 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is negative + case asBC_JS: + if( *(int*)®s.valueRegister < 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register it not negative + case asBC_JNS: + if( *(int*)®s.valueRegister >= 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is greater than 0 + case asBC_JP: + if( *(int*)®s.valueRegister > 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; + + // Jump to a relative position if the value in the register is not greater than 0 + case asBC_JNP: + if( *(int*)®s.valueRegister <= 0 ) + l_bc += asBC_INTARG(l_bc) + 2; + else + l_bc += 2; + break; +//-------------------- +// test instructions + + // If the value in the register is 0, then set the register to 1, else to 0 + case asBC_TZ: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is equal to 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)®s.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)®s.valueRegister; + asBYTE val = (regPtr[0] == 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)®s.valueRegister = (*(int*)®s.valueRegister == 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // If the value in the register is not 0, then set the register to 1, else to 0 + case asBC_TNZ: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is not equal to 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)®s.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)®s.valueRegister; + asBYTE val = (regPtr[0] == 0) ? 0 : VALUE_OF_BOOLEAN_TRUE; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)®s.valueRegister = (*(int*)®s.valueRegister == 0 ? 0 : VALUE_OF_BOOLEAN_TRUE); +#endif + l_bc++; + break; + + // If the value in the register is negative, then set the register to 1, else to 0 + case asBC_TS: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is less than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)®s.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)®s.valueRegister; + asBYTE val = (regPtr[0] < 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)®s.valueRegister = (*(int*)®s.valueRegister < 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // If the value in the register is not negative, then set the register to 1, else to 0 + case asBC_TNS: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is not less than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)®s.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)®s.valueRegister; + asBYTE val = (regPtr[0] >= 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)®s.valueRegister = (*(int*)®s.valueRegister < 0 ? 0 : VALUE_OF_BOOLEAN_TRUE); +#endif + l_bc++; + break; + + // If the value in the register is greater than 0, then set the register to 1, else to 0 + case asBC_TP: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is greater than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)®s.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)®s.valueRegister; + asBYTE val = (regPtr[0] > 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)®s.valueRegister = (*(int*)®s.valueRegister > 0 ? VALUE_OF_BOOLEAN_TRUE : 0); +#endif + l_bc++; + break; + + // If the value in the register is not greater than 0, then set the register to 1, else to 0 + case asBC_TNP: +#if AS_SIZEOF_BOOL == 1 + { + // Set the value to true if it is not greater than 0 + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on valueRegister. + + volatile int *regPtr = (int*)®s.valueRegister; + volatile asBYTE *regBptr = (asBYTE*)®s.valueRegister; + asBYTE val = (regPtr[0] <= 0) ? VALUE_OF_BOOLEAN_TRUE : 0; + regBptr[0] = val; // The result is stored in the lower byte + regBptr[1] = 0; // Make sure the rest of the register is 0 + regBptr[2] = 0; + regBptr[3] = 0; + regBptr[4] = 0; + regBptr[5] = 0; + regBptr[6] = 0; + regBptr[7] = 0; + } +#else + *(int*)®s.valueRegister = (*(int*)®s.valueRegister > 0 ? 0 : VALUE_OF_BOOLEAN_TRUE); +#endif + l_bc++; + break; + +//-------------------- +// negate value + + // Negate the integer value in the variable + case asBC_NEGi: + *(l_fp - asBC_SWORDARG0(l_bc)) = asDWORD(-int(*(l_fp - asBC_SWORDARG0(l_bc)))); + l_bc++; + break; + + // Negate the float value in the variable + case asBC_NEGf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(float*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Negate the double value in the variable + case asBC_NEGd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(double*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + +//------------------------- +// Increment value pointed to by address in register + + // Increment the short value pointed to by the register + case asBC_INCi16: + (**(short**)®s.valueRegister)++; + l_bc++; + break; + + // Increment the byte value pointed to by the register + case asBC_INCi8: + (**(char**)®s.valueRegister)++; + l_bc++; + break; + + // Decrement the short value pointed to by the register + case asBC_DECi16: + (**(short**)®s.valueRegister)--; + l_bc++; + break; + + // Decrement the byte value pointed to by the register + case asBC_DECi8: + (**(char**)®s.valueRegister)--; + l_bc++; + break; + + // Increment the integer value pointed to by the register + case asBC_INCi: + ++(**(int**)®s.valueRegister); + l_bc++; + break; + + // Decrement the integer value pointed to by the register + case asBC_DECi: + --(**(int**)®s.valueRegister); + l_bc++; + break; + + // Increment the float value pointed to by the register + case asBC_INCf: + ++(**(float**)®s.valueRegister); + l_bc++; + break; + + // Decrement the float value pointed to by the register + case asBC_DECf: + --(**(float**)®s.valueRegister); + l_bc++; + break; + + // Increment the double value pointed to by the register + case asBC_INCd: + ++(**(double**)®s.valueRegister); + l_bc++; + break; + + // Decrement the double value pointed to by the register + case asBC_DECd: + --(**(double**)®s.valueRegister); + l_bc++; + break; + + // Increment the local integer variable + case asBC_IncVi: + (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))++; + l_bc++; + break; + + // Decrement the local integer variable + case asBC_DecVi: + (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))--; + l_bc++; + break; + +//-------------------- +// bits instructions + + // Do a bitwise not on the value in the variable + case asBC_BNOT: + *(l_fp - asBC_SWORDARG0(l_bc)) = ~*(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + // Do a bitwise and of two variables and store the result in a third variable + case asBC_BAND: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) & *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a bitwise or of two variables and store the result in a third variable + case asBC_BOR: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) | *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a bitwise xor of two variables and store the result in a third variable + case asBC_BXOR: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) ^ *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a logical shift left of two variables and store the result in a third variable + case asBC_BSLL: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) << *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do a logical shift right of two variables and store the result in a third variable + case asBC_BSRL: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + // Do an arithmetic shift right of two variables and store the result in a third variable + case asBC_BSRA: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(l_fp - asBC_SWORDARG1(l_bc))) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_COPY: + { + void *d = (void*)*(size_t*)l_sp; l_sp += AS_PTR_SIZE; + void *s = (void*)*(size_t*)l_sp; + if( s == 0 || d == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + memcpy(d, s, asBC_WORDARG0(l_bc)*4); + + // replace the pointer on the stack with the lvalue + *(size_t**)l_sp = (size_t*)d; + } + l_bc++; + break; + + case asBC_PshC8: + l_sp -= 2; + *(asQWORD*)l_sp = asBC_QWORDARG(l_bc); + l_bc += 3; + break; + + case asBC_RDS8: +#ifndef AS_64BIT_PTR + *(asQWORD*)(l_sp-1) = *(asQWORD*)*(size_t*)l_sp; + --l_sp; +#else + *(asQWORD*)l_sp = *(asQWORD*)*(size_t*)l_sp; +#endif + l_bc++; + break; + + case asBC_SWAP8: + { + asQWORD q = *(asQWORD*)l_sp; + *(asQWORD*)l_sp = *(asQWORD*)(l_sp+2); + *(asQWORD*)(l_sp+2) = q; + l_bc++; + } + break; + + //---------------------------- + // Comparisons + case asBC_CMPd: + { + double dbl = *(double*)(l_fp - asBC_SWORDARG0(l_bc)) - *(double*)(l_fp - asBC_SWORDARG1(l_bc)); + if( dbl == 0 ) *(int*)®s.valueRegister = 0; + else if( dbl < 0 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPu: + { + asDWORD d = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + asDWORD d2 = *(asDWORD*)(l_fp - asBC_SWORDARG1(l_bc)); + if( d == d2 ) *(int*)®s.valueRegister = 0; + else if( d < d2 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPf: + { + float f = *(float*)(l_fp - asBC_SWORDARG0(l_bc)) - *(float*)(l_fp - asBC_SWORDARG1(l_bc)); + if( f == 0 ) *(int*)®s.valueRegister = 0; + else if( f < 0 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPi: + { + int i = *(int*)(l_fp - asBC_SWORDARG0(l_bc)) - *(int*)(l_fp - asBC_SWORDARG1(l_bc)); + if( i == 0 ) *(int*)®s.valueRegister = 0; + else if( i < 0 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + //---------------------------- + // Comparisons with constant value + case asBC_CMPIi: + { + int i = *(int*)(l_fp - asBC_SWORDARG0(l_bc)) - asBC_INTARG(l_bc); + if( i == 0 ) *(int*)®s.valueRegister = 0; + else if( i < 0 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPIf: + { + float f = *(float*)(l_fp - asBC_SWORDARG0(l_bc)) - asBC_FLOATARG(l_bc); + if( f == 0 ) *(int*)®s.valueRegister = 0; + else if( f < 0 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPIu: + { + asDWORD d1 = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + asDWORD d2 = asBC_DWORDARG(l_bc); + if( d1 == d2 ) *(int*)®s.valueRegister = 0; + else if( d1 < d2 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_JMPP: + l_bc += 1 + (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))*2; + break; + + case asBC_PopRPtr: + *(asPTRWORD*)®s.valueRegister = *(asPTRWORD*)l_sp; + l_sp += AS_PTR_SIZE; + l_bc++; + break; + + case asBC_PshRPtr: + l_sp -= AS_PTR_SIZE; + *(asPTRWORD*)l_sp = *(asPTRWORD*)®s.valueRegister; + l_bc++; + break; + + case asBC_STR: + { + // Get the string id from the argument + asWORD w = asBC_WORDARG0(l_bc); + // Push the string pointer on the stack + const asCString &b = engine->GetConstantString(w); + l_sp -= AS_PTR_SIZE; + *(asPTRWORD*)l_sp = (asPTRWORD)(size_t)b.AddressOf(); + // Push the string length on the stack + --l_sp; + *l_sp = (asDWORD)b.GetLength(); + l_bc++; + } + break; + + case asBC_CALLSYS: + { + // Get function ID from the argument + int i = asBC_INTARG(l_bc); + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + l_sp += CallSystemFunction(i, this, 0); + + // Update the program position after the call so that line number is correct + l_bc += 2; + + if( regs.doProcessSuspend ) + { + // Should the execution be suspended? + if( doSuspend ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + status = asEXECUTION_SUSPENDED; + return; + } + // An exception might have been raised + if( status != asEXECUTION_ACTIVE ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + return; + } + } + } + break; + + case asBC_CALLBND: + { + // Get the function ID from the stack + int i = asBC_INTARG(l_bc); + l_bc += 2; + + asASSERT( i >= 0 ); + asASSERT( i & FUNC_IMPORTED ); + + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + int funcID = engine->importedFunctions[i&0xFFFF]->boundFunctionId; + if( funcID == -1 ) + { + SetInternalException(TXT_UNBOUND_FUNCTION); + return; + } + else + { + asCScriptFunction *func = engine->GetScriptFunction(funcID); + + CallScriptFunction(func); + } + + // Extract the values from the context again + l_bc = regs.programPointer; + l_sp = regs.stackPointer; + l_fp = regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( status != asEXECUTION_ACTIVE ) + return; + } + break; + + case asBC_SUSPEND: + if( regs.doProcessSuspend ) + { + if( lineCallback ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + CallLineCallback(); + } + if( doSuspend ) + { + l_bc++; + + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + status = asEXECUTION_SUSPENDED; + return; + } + } + + l_bc++; + break; + + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(l_bc); + int func = asBC_INTARG(l_bc+AS_PTR_SIZE); + + if( objType->flags & asOBJ_SCRIPT_OBJECT ) + { + // Pre-allocate the memory + asDWORD *mem = (asDWORD*)engine->CallAlloc(objType); + + // Pre-initialize the memory by calling the constructor for asCScriptObject + ScriptObject_Construct(objType, (asCScriptObject*)mem); + + // Call the constructor to initalize the memory + asCScriptFunction *f = engine->scriptFunctions[func]; + + asDWORD **a = (asDWORD**)*(size_t*)(l_sp + f->GetSpaceNeededForArguments()); + if( a ) *a = mem; + + // Push the object pointer on the stack + l_sp -= AS_PTR_SIZE; + *(size_t*)l_sp = (size_t)mem; + + l_bc += 2+AS_PTR_SIZE; + + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + CallScriptFunction(f); + + // Extract the values from the context again + l_bc = regs.programPointer; + l_sp = regs.stackPointer; + l_fp = regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( status != asEXECUTION_ACTIVE ) + return; + } + else + { + // Pre-allocate the memory + asDWORD *mem = (asDWORD*)engine->CallAlloc(objType); + + if( func ) + { + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + l_sp += CallSystemFunction(func, this, mem); + } + + // Pop the variable address from the stack + asDWORD **a = (asDWORD**)*(size_t*)l_sp; + l_sp += AS_PTR_SIZE; + if( a ) *a = mem; + + l_bc += 2+AS_PTR_SIZE; + + if( regs.doProcessSuspend ) + { + // Should the execution be suspended? + if( doSuspend ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + status = asEXECUTION_SUSPENDED; + return; + } + // An exception might have been raised + if( status != asEXECUTION_ACTIVE ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + engine->CallFree(mem); + *a = 0; + + return; + } + } + } + } + break; + + case asBC_FREE: + { + asDWORD **a = (asDWORD**)*(size_t*)l_sp; + l_sp += AS_PTR_SIZE; + if( a && *a ) + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(l_bc); + asSTypeBehaviour *beh = &objType->beh; + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + if( beh->release ) + { + engine->CallObjectMethod(*a, beh->release); + } + else + { + if( beh->destruct ) + { + engine->CallObjectMethod(*a, beh->destruct); + } + + engine->CallFree(*a); + } + + // Clear the variable + *a = 0; + } + } + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_LOADOBJ: + { + // Move the object pointer from the object variable into the object register + void **a = (void**)(l_fp - asBC_SWORDARG0(l_bc)); + regs.objectType = 0; + regs.objectRegister = *a; + *a = 0; + } + l_bc++; + break; + + case asBC_STOREOBJ: + // Move the object pointer from the object register to the object variable + *(size_t*)(l_fp - asBC_SWORDARG0(l_bc)) = size_t(regs.objectRegister); + regs.objectRegister = 0; + l_bc++; + break; + + case asBC_GETOBJ: + { + // Read variable index from location on stack + size_t *a = (size_t*)(l_sp + asBC_WORDARG0(l_bc)); + asDWORD offset = *(asDWORD*)a; + // Move pointer from variable to the same location on the stack + size_t *v = (size_t*)(l_fp - offset); + *a = *v; + // Clear variable + *v = 0; + } + l_bc++; + break; + + case asBC_REFCPY: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(l_bc); + asSTypeBehaviour *beh = &objType->beh; + + // Pop address of destination pointer from the stack + void **d = (void**)*(size_t*)l_sp; + l_sp += AS_PTR_SIZE; + + // Read wanted pointer from the stack + void *s = (void*)*(size_t*)l_sp; + + // Need to move the values back to the context as the called functions + // may use the debug interface to inspect the registers + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Release previous object held by destination pointer + if( *d != 0 ) + engine->CallObjectMethod(*d, beh->release); + // Increase ref counter of wanted object + if( s != 0 ) + engine->CallObjectMethod(s, beh->addref); + + // Set the new object in the destination + *d = s; + } + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_CHKREF: + { + // Verify if the pointer on the stack is null + // This is used when validating a pointer that an operator will work on + size_t a = *(size_t*)l_sp; + if( a == 0 ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_GETOBJREF: + { + // Get the location on the stack where the reference will be placed + size_t *a = (size_t*)(l_sp + asBC_WORDARG0(l_bc)); + + // Replace the variable index with the object handle held in the variable + *(size_t**)a = *(size_t**)(l_fp - *a); + } + l_bc++; + break; + + case asBC_GETREF: + { + // Get the location on the stack where the reference will be placed + size_t *a = (size_t*)(l_sp + asBC_WORDARG0(l_bc)); + + // Replace the variable index with the address of the variable + *(size_t**)a = (size_t*)(l_fp - (int)*a); + } + l_bc++; + break; + + case asBC_SWAP48: + { + asDWORD d = *(asDWORD*)l_sp; + asQWORD q = *(asQWORD*)(l_sp+1); + *(asQWORD*)l_sp = q; + *(asDWORD*)(l_sp+2) = d; + l_bc++; + } + break; + + case asBC_SWAP84: + { + asQWORD q = *(asQWORD*)l_sp; + asDWORD d = *(asDWORD*)(l_sp+2); + *(asDWORD*)l_sp = d; + *(asQWORD*)(l_sp+1) = q; + l_bc++; + } + break; + + case asBC_OBJTYPE: + l_sp -= AS_PTR_SIZE; + *(asPTRWORD*)l_sp = asBC_PTRARG(l_bc); + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_TYPEID: + // Equivalent to PshC4, but kept as separate instruction for bytecode serialization + --l_sp; + *l_sp = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_SetV4: + *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_SetV8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asBC_QWORDARG(l_bc); + l_bc += 3; + break; + + case asBC_ADDSi: + *(size_t*)l_sp = size_t(asPTRWORD(*(size_t*)l_sp) + asBC_INTARG(l_bc)); + l_bc += 2; + break; + + case asBC_CpyVtoV4: + *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)); + l_bc += 2; + break; + + case asBC_CpyVtoV8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)); + l_bc += 2; + break; + + case asBC_CpyVtoR4: + *(asDWORD*)®s.valueRegister = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_CpyVtoR8: + *(asQWORD*)®s.valueRegister = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_CpyVtoG4: + *(asDWORD*)(size_t)asBC_PTRARG(l_bc) = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc += 1 + AS_PTR_SIZE; + break; + + case asBC_CpyRtoV4: + *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asDWORD*)®s.valueRegister; + l_bc++; + break; + + case asBC_CpyRtoV8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = regs.valueRegister; + l_bc++; + break; + + case asBC_CpyGtoV4: + *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asDWORD*)(size_t)asBC_PTRARG(l_bc); + l_bc += 1 + AS_PTR_SIZE; + break; + + case asBC_WRTV1: + // The pointer in the register points to a byte, and *(l_fp - offset) too + **(asBYTE**)®s.valueRegister = *(asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_WRTV2: + // The pointer in the register points to a word, and *(l_fp - offset) too + **(asWORD**)®s.valueRegister = *(asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_WRTV4: + **(asDWORD**)®s.valueRegister = *(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_WRTV8: + **(asQWORD**)®s.valueRegister = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_RDR1: + { + // The pointer in the register points to a byte, and *(l_fp - offset) will also point to a byte + asBYTE *bPtr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + bPtr[0] = **(asBYTE**)®s.valueRegister; // read the byte + bPtr[1] = 0; // 0 the rest of the DWORD + bPtr[2] = 0; + bPtr[3] = 0; + } + l_bc++; + break; + + case asBC_RDR2: + { + // The pointer in the register points to a word, and *(l_fp - offset) will also point to a word + asWORD *wPtr = (asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + wPtr[0] = **(asWORD**)®s.valueRegister; // read the word + wPtr[1] = 0; // 0 the rest of the DWORD + } + l_bc++; + break; + + case asBC_RDR4: + *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = **(asDWORD**)®s.valueRegister; + l_bc++; + break; + + case asBC_RDR8: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = **(asQWORD**)®s.valueRegister; + l_bc++; + break; + + case asBC_LDG: + *(asPTRWORD*)®s.valueRegister = asBC_PTRARG(l_bc); + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_LDV: + *(asDWORD**)®s.valueRegister = (l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_PGA: + l_sp -= AS_PTR_SIZE; + *(asPTRWORD*)l_sp = asBC_PTRARG(l_bc); + l_bc += 1+AS_PTR_SIZE; + break; + + case asBC_RDS4: +#ifndef AS_64BIT_PTR + *l_sp = *(asDWORD*)*(size_t*)l_sp; +#else + { + asDWORD d = *(asDWORD*)*(size_t*)l_sp; + l_sp++; + *l_sp = d; + } +#endif + l_bc++; + break; + + case asBC_VAR: + l_sp -= AS_PTR_SIZE; + *(size_t*)l_sp = (size_t)asBC_SWORDARG0(l_bc); + l_bc++; + break; + + //---------------------------- + // Type conversions + case asBC_iTOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(int*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_fTOi: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(float*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_uTOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_fTOu: + // We must cast to int first, because on some compilers the cast of a negative float value to uint result in 0 + *(l_fp - asBC_SWORDARG0(l_bc)) = asUINT(int(*(float*)(l_fp - asBC_SWORDARG0(l_bc)))); + l_bc++; + break; + + case asBC_sbTOi: + // *(l_fp - offset) points to a char, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(signed char*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_swTOi: + // *(l_fp - offset) points to a short, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(short*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_ubTOi: + // (l_fp - offset) points to a byte, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_uwTOi: + // *(l_fp - offset) points to a word, and will point to an int afterwards + *(l_fp - asBC_SWORDARG0(l_bc)) = *(asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_dTOi: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(double*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_dTOu: + // We must cast to int first, because on some compilers the cast of a negative float value to uint result in 0 + *(l_fp - asBC_SWORDARG0(l_bc)) = asUINT(int(*(double*)(l_fp - asBC_SWORDARG1(l_bc)))); + l_bc += 2; + break; + + case asBC_dTOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(double*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_iTOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(int*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_uTOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asUINT*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_fTOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(float*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + //------------------------------ + // Math operations + case asBC_ADDi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) + *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) - *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) * *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVi: + { + int divider = *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODi: + { + int divider = *(int*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) % divider; + } + l_bc += 2; + break; + + case asBC_ADDf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) + *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) - *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) * *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVf: + { + float divider = *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODf: + { + float divider = *(float*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = fmodf(*(float*)(l_fp - asBC_SWORDARG1(l_bc)), divider); + } + l_bc += 2; + break; + + case asBC_ADDd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) + *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) - *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) * *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVd: + { + double divider = *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + l_bc += 2; + } + break; + + case asBC_MODd: + { + double divider = *(double*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = fmod(*(double*)(l_fp - asBC_SWORDARG1(l_bc)), divider); + l_bc += 2; + } + break; + + //------------------------------ + // Math operations with constant value + case asBC_ADDIi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) + asBC_INTARG(l_bc+1); + l_bc += 3; + break; + + case asBC_SUBIi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) - asBC_INTARG(l_bc+1); + l_bc += 3; + break; + + case asBC_MULIi: + *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) * asBC_INTARG(l_bc+1); + l_bc += 3; + break; + + case asBC_ADDIf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) + asBC_FLOATARG(l_bc+1); + l_bc += 3; + break; + + case asBC_SUBIf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) - asBC_FLOATARG(l_bc+1); + l_bc += 3; + break; + + case asBC_MULIf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) * asBC_FLOATARG(l_bc+1); + l_bc += 3; + break; + + //----------------------------------- + case asBC_SetG4: + *(asDWORD*)(size_t)asBC_PTRARG(l_bc) = asBC_DWORDARG(l_bc+AS_PTR_SIZE); + l_bc += 2 + AS_PTR_SIZE; + break; + + case asBC_ChkRefS: + { + // Verify if the pointer on the stack refers to a non-null value + // This is used to validate a reference to a handle + asDWORD *a = (asDWORD*)*(size_t*)l_sp; + if( *a == 0 ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_ChkNullV: + { + // Verify if variable (on the stack) is not null + asDWORD *a = *(asDWORD**)(l_fp - asBC_SWORDARG0(l_bc)); + if( a == 0 ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_CALLINTF: + { + int i = asBC_INTARG(l_bc); + l_bc += 2; + + asASSERT( i >= 0 ); + asASSERT( (i & FUNC_IMPORTED) == 0 ); + + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + CallInterfaceMethod(engine->GetScriptFunction(i)); + + // Extract the values from the context again + l_bc = regs.programPointer; + l_sp = regs.stackPointer; + l_fp = regs.stackFramePointer; + + // If status isn't active anymore then we must stop + if( status != asEXECUTION_ACTIVE ) + return; + } + break; + + case asBC_iTOb: + { + // *(l_fp - offset) points to an int, and will point to a byte afterwards + + // We need to use volatile here to tell the compiler not to rearrange + // read and write operations during optimizations. + volatile asDWORD val = *(l_fp - asBC_SWORDARG0(l_bc)); + volatile asBYTE *bPtr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc)); + bPtr[0] = (asBYTE)val; // write the byte + bPtr[1] = 0; // 0 the rest of the DWORD + bPtr[2] = 0; + bPtr[3] = 0; + } + l_bc++; + break; + + case asBC_iTOw: + { + // *(l_fp - offset) points to an int, and will point to word afterwards + + // We need to use volatile here to tell the compiler not to rearrange + // read and write operations during optimizations. + volatile asDWORD val = *(l_fp - asBC_SWORDARG0(l_bc)); + volatile asWORD *wPtr = (asWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + wPtr[0] = (asWORD)val; // write the word + wPtr[1] = 0; // 0 the rest of the DWORD + } + l_bc++; + break; + + case asBC_SetV1: + // TODO: This is exactly the same as SetV4. This is a left over from the time + // when the bytecode instructions were more tightly packed. It can now + // be removed. When removing it, make sure the value is correctly converted + // on big-endian CPUs. + + // The byte is already stored correctly in the argument + *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_SetV2: + // TODO: This is exactly the same as SetV4. This is a left over from the time + // when the bytecode instructions were more tightly packed. It can now + // be removed. When removing it, make sure the value is correctly converted + // on big-endian CPUs. + + // The word is already stored correctly in the argument + *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc); + l_bc += 2; + break; + + case asBC_Cast: + // Cast the handle at the top of the stack to the type in the argument + { + asDWORD **a = (asDWORD**)*(size_t*)l_sp; + if( a && *a ) + { + asDWORD typeId = asBC_DWORDARG(l_bc); + + asCScriptObject *obj = (asCScriptObject *)* a; + asCObjectType *objType = obj->objType; + asCObjectType *to = engine->GetObjectTypeFromTypeId(typeId); + + // This instruction can only be used with script classes and interfaces + asASSERT( objType->flags & asOBJ_SCRIPT_OBJECT ); + asASSERT( to->flags & asOBJ_SCRIPT_OBJECT ); + + if( objType->Implements(to) || objType->DerivesFrom(to) ) + { + regs.objectType = 0; + regs.objectRegister = obj; + obj->AddRef(); + } + else + { + // The object register should already be null, so there + // is no need to clear it if the cast is unsuccessful + asASSERT( regs.objectRegister == 0 ); + } + } + l_sp += AS_PTR_SIZE; + } + l_bc += 2; + break; + + case asBC_i64TOi: + *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_uTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(asUINT*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_iTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(int*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_fTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(float*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_dTOi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(double*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_fTOu64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asQWORD(asINT64(*(float*)(l_fp - asBC_SWORDARG1(l_bc)))); + l_bc += 2; + break; + + case asBC_dTOu64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asQWORD(asINT64(*(double*)(l_fp - asBC_SWORDARG0(l_bc)))); + l_bc++; + break; + + case asBC_i64TOf: + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc))); + l_bc += 2; + break; + + case asBC_u64TOf: +#if _MSC_VER <= 1200 // MSVC6 + { + // MSVC6 doesn't permit UINT64 to double + asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)); + if( v < 0 ) + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = 18446744073709551615.0f+float(v); + else + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(v); + } +#else + *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc))); +#endif + l_bc += 2; + break; + + case asBC_i64TOd: + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asINT64*)(l_fp - asBC_SWORDARG0(l_bc))); + l_bc++; + break; + + case asBC_u64TOd: +#if _MSC_VER <= 1200 // MSVC6 + { + // MSVC6 doesn't permit UINT64 to double + asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)); + if( v < 0 ) + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = 18446744073709551615.0+double(v); + else + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(v); + } +#else + *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc))); +#endif + l_bc++; + break; + + case asBC_NEGi64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_INCi64: + ++(**(asQWORD**)®s.valueRegister); + l_bc++; + break; + + case asBC_DECi64: + --(**(asQWORD**)®s.valueRegister); + l_bc++; + break; + + case asBC_BNOT64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = ~*(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + l_bc++; + break; + + case asBC_ADDi64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) + *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_SUBi64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) - *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_MULi64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) * *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_DIVi64: + { + asQWORD divider = *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) / divider; + } + l_bc += 2; + break; + + case asBC_MODi64: + { + asQWORD divider = *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + if( divider == 0 ) + { + // Need to move the values back to the context + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // Raise exception + SetInternalException(TXT_DIVIDE_BY_ZERO); + return; + } + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) % divider; + } + l_bc += 2; + break; + + case asBC_BAND64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) & *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BOR64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) | *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BXOR64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) ^ *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BSLL64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) << *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BSRL64: + *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_BSRA64: + *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc)); + l_bc += 2; + break; + + case asBC_CMPi64: + { + asINT64 i = *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) - *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)); + if( i == 0 ) *(int*)®s.valueRegister = 0; + else if( i < 0 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_CMPu64: + { + asQWORD d = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)); + asQWORD d2 = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)); + if( d == d2 ) *(int*)®s.valueRegister = 0; + else if( d < d2 ) *(int*)®s.valueRegister = -1; + else *(int*)®s.valueRegister = 1; + l_bc += 2; + } + break; + + case asBC_ChkNullS: + { + // Verify if the pointer on the stack is null + // This is used for example when validating handles passed as function arguments + size_t a = *(size_t*)(l_sp + asBC_WORDARG0(l_bc)); + if( a == 0 ) + { + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + SetInternalException(TXT_NULL_POINTER_ACCESS); + return; + } + } + l_bc++; + break; + + case asBC_ClrHi: +#if AS_SIZEOF_BOOL == 1 + { + // Clear the upper bytes, so that trash data don't interfere with boolean operations + + // We need to use volatile here to tell the compiler it cannot + // change the order of read and write operations on the pointer. + + volatile asBYTE *ptr = (asBYTE*)®s.valueRegister; + ptr[1] = 0; // The boolean value is stored in the lower byte, so we clear the rest + ptr[2] = 0; + ptr[3] = 0; + } +#else + // We don't have anything to do here +#endif + l_bc++; + break; + + case asBC_JitEntry: + { + if( currentFunction->jitFunction ) + { + unsigned int jitOffset = asBC_WORDARG0(l_bc); + + if( jitOffset ) + { + // Resume JIT operation + regs.programPointer = l_bc; + regs.stackPointer = l_sp; + regs.stackFramePointer = l_fp; + + // TODO: JIT: We should return from this function if the jitFunction tells us to + (currentFunction->jitFunction)(®s, jitOffset-1); + + l_bc = regs.programPointer; + l_sp = regs.stackPointer; + l_fp = regs.stackFramePointer; + break; + } + } + + // Not a JIT resume point, treat as nop + l_bc++; + } + break; + + // Don't let the optimizer optimize for size, + // since it requires extra conditions and jumps + case 176: l_bc = (asDWORD*)176; break; + case 177: l_bc = (asDWORD*)177; break; + case 178: l_bc = (asDWORD*)178; break; + case 179: l_bc = (asDWORD*)179; break; + case 180: l_bc = (asDWORD*)180; break; + case 181: l_bc = (asDWORD*)181; break; + case 182: l_bc = (asDWORD*)182; break; + case 183: l_bc = (asDWORD*)183; break; + case 184: l_bc = (asDWORD*)184; break; + case 185: l_bc = (asDWORD*)185; break; + case 186: l_bc = (asDWORD*)186; break; + case 187: l_bc = (asDWORD*)187; break; + case 188: l_bc = (asDWORD*)188; break; + case 189: l_bc = (asDWORD*)189; break; + case 190: l_bc = (asDWORD*)190; break; + case 191: l_bc = (asDWORD*)191; break; + case 192: l_bc = (asDWORD*)192; break; + case 193: l_bc = (asDWORD*)193; break; + case 194: l_bc = (asDWORD*)194; break; + case 195: l_bc = (asDWORD*)195; break; + case 196: l_bc = (asDWORD*)196; break; + case 197: l_bc = (asDWORD*)197; break; + case 198: l_bc = (asDWORD*)198; break; + case 199: l_bc = (asDWORD*)199; break; + case 200: l_bc = (asDWORD*)200; break; + case 201: l_bc = (asDWORD*)201; break; + case 202: l_bc = (asDWORD*)202; break; + case 203: l_bc = (asDWORD*)203; break; + case 204: l_bc = (asDWORD*)204; break; + case 205: l_bc = (asDWORD*)205; break; + case 206: l_bc = (asDWORD*)206; break; + case 207: l_bc = (asDWORD*)207; break; + case 208: l_bc = (asDWORD*)208; break; + case 209: l_bc = (asDWORD*)209; break; + case 210: l_bc = (asDWORD*)210; break; + case 211: l_bc = (asDWORD*)211; break; + case 212: l_bc = (asDWORD*)212; break; + case 213: l_bc = (asDWORD*)213; break; + case 214: l_bc = (asDWORD*)214; break; + case 215: l_bc = (asDWORD*)215; break; + case 216: l_bc = (asDWORD*)216; break; + case 217: l_bc = (asDWORD*)217; break; + case 218: l_bc = (asDWORD*)218; break; + case 219: l_bc = (asDWORD*)219; break; + case 220: l_bc = (asDWORD*)220; break; + case 221: l_bc = (asDWORD*)221; break; + case 222: l_bc = (asDWORD*)222; break; + case 223: l_bc = (asDWORD*)223; break; + case 224: l_bc = (asDWORD*)224; break; + case 225: l_bc = (asDWORD*)225; break; + case 226: l_bc = (asDWORD*)226; break; + case 227: l_bc = (asDWORD*)227; break; + case 228: l_bc = (asDWORD*)228; break; + case 229: l_bc = (asDWORD*)229; break; + case 230: l_bc = (asDWORD*)230; break; + case 231: l_bc = (asDWORD*)231; break; + case 232: l_bc = (asDWORD*)232; break; + case 233: l_bc = (asDWORD*)233; break; + case 234: l_bc = (asDWORD*)234; break; + case 235: l_bc = (asDWORD*)235; break; + case 236: l_bc = (asDWORD*)236; break; + case 237: l_bc = (asDWORD*)237; break; + case 238: l_bc = (asDWORD*)238; break; + case 239: l_bc = (asDWORD*)239; break; + case 240: l_bc = (asDWORD*)240; break; + case 241: l_bc = (asDWORD*)241; break; + case 242: l_bc = (asDWORD*)242; break; + case 243: l_bc = (asDWORD*)243; break; + case 244: l_bc = (asDWORD*)244; break; + case 245: l_bc = (asDWORD*)245; break; + case 246: l_bc = (asDWORD*)246; break; + case 247: l_bc = (asDWORD*)247; break; + case 248: l_bc = (asDWORD*)248; break; + case 249: l_bc = (asDWORD*)249; break; + case 250: l_bc = (asDWORD*)250; break; + case 251: l_bc = (asDWORD*)251; break; + case 252: l_bc = (asDWORD*)252; break; + case 253: l_bc = (asDWORD*)253; break; + case 254: l_bc = (asDWORD*)254; break; + case 255: l_bc = (asDWORD*)255; break; + +#ifdef AS_DEBUG + default: + asASSERT(false); +#endif +/* + default: + // This Microsoft specific code allows the + // compiler to optimize the switch case as + // it will know that the code will never + // reach this point + __assume(0); +*/ } + +#ifdef AS_DEBUG + asDWORD instr = *(asBYTE*)old; + if( instr != asBC_JMP && instr != asBC_JMPP && (instr < asBC_JZ || instr > asBC_JNP) && + instr != asBC_CALL && instr != asBC_CALLBND && instr != asBC_CALLINTF && instr != asBC_RET && instr != asBC_ALLOC ) + { + asASSERT( (l_bc - old) == asBCTypeSize[asBCInfo[instr].type] ); + } +#endif + } + + SetInternalException(TXT_UNRECOGNIZED_BYTE_CODE); +} + +int asCContext::SetException(const char *descr) +{ + // Only allow this if we're executing a CALL byte code + if( !isCallingSystemFunction ) return asERROR; + + SetInternalException(descr); + + return 0; +} + +void asCContext::SetInternalException(const char *descr) +{ + if( inExceptionHandler ) + { + asASSERT(false); // Shouldn't happen + return; // but if it does, at least this will not crash the application + } + + status = asEXECUTION_EXCEPTION; + regs.doProcessSuspend = true; + + exceptionString = descr; + exceptionFunction = currentFunction->id; + exceptionLine = currentFunction->GetLineNumber(int(regs.programPointer - currentFunction->byteCode.AddressOf())); + exceptionColumn = exceptionLine >> 20; + exceptionLine &= 0xFFFFF; + + if( exceptionCallback ) + CallExceptionCallback(); +} + +void asCContext::CleanReturnObject() +{ + if( regs.objectRegister == 0 ) return; + + asASSERT( regs.objectType != 0 ); + + if( regs.objectType ) + { + // Call the destructor on the object + asSTypeBehaviour *beh = &((asCObjectType*)regs.objectType)->beh; + if( beh->release ) + { + engine->CallObjectMethod(regs.objectRegister, beh->release); + regs.objectRegister = 0; + + // The release method is responsible for freeing the memory + } + else + { + if( beh->destruct ) + engine->CallObjectMethod(regs.objectRegister, beh->destruct); + + // Free the memory + engine->CallFree(regs.objectRegister); + regs.objectRegister = 0; + } + } +} + +void asCContext::CleanStack() +{ + inExceptionHandler = true; + + // Run the clean up code for each of the functions called + CleanStackFrame(); + + while( callStack.GetLength() > 0 ) + { + PopCallState(); + + CleanStackFrame(); + } + inExceptionHandler = false; +} + +void asCContext::CleanStackFrame() +{ + // Clean object variables + if( !isStackMemoryNotAllocated ) + { + for( asUINT n = 0; n < currentFunction->objVariablePos.GetLength(); n++ ) + { + int pos = currentFunction->objVariablePos[n]; + if( *(size_t*)®s.stackFramePointer[-pos] ) + { + // Call the object's destructor + asSTypeBehaviour *beh = ¤tFunction->objVariableTypes[n]->beh; + if( beh->release ) + { + engine->CallObjectMethod((void*)*(size_t*)®s.stackFramePointer[-pos], beh->release); + *(size_t*)®s.stackFramePointer[-pos] = 0; + } + else + { + if( beh->destruct ) + engine->CallObjectMethod((void*)*(size_t*)®s.stackFramePointer[-pos], beh->destruct); + + // Free the memory + engine->CallFree((void*)*(size_t*)®s.stackFramePointer[-pos]); + *(size_t*)®s.stackFramePointer[-pos] = 0; + } + } + } + } + else + isStackMemoryNotAllocated = false; + + // Functions that do not own the object and parameters shouldn't do any clean up + if( currentFunction->dontCleanUpOnException ) + return; + + // Clean object and parameters + int offset = 0; + if( currentFunction->objectType ) + { + offset += AS_PTR_SIZE; + + // If the object is a script declared object, then we must release it + // as the compiler adds a reference at the entry of the function + asSTypeBehaviour *beh = ¤tFunction->objectType->beh; + if( beh->release && *(size_t*)®s.stackFramePointer[0] != 0 ) + { + engine->CallObjectMethod((void*)*(size_t*)®s.stackFramePointer[0], beh->release); + *(size_t*)®s.stackFramePointer[0] = 0; + } + } + for( asUINT n = 0; n < currentFunction->parameterTypes.GetLength(); n++ ) + { + if( currentFunction->parameterTypes[n].IsObject() && !currentFunction->parameterTypes[n].IsReference() ) + { + if( *(size_t*)®s.stackFramePointer[offset] ) + { + // Call the object's destructor + asSTypeBehaviour *beh = currentFunction->parameterTypes[n].GetBehaviour(); + if( beh->release ) + { + engine->CallObjectMethod((void*)*(size_t*)®s.stackFramePointer[offset], beh->release); + *(size_t*)®s.stackFramePointer[offset] = 0; + } + else + { + if( beh->destruct ) + engine->CallObjectMethod((void*)*(size_t*)®s.stackFramePointer[offset], beh->destruct); + + // Free the memory + engine->CallFree((void*)*(size_t*)®s.stackFramePointer[offset]); + *(size_t*)®s.stackFramePointer[offset] = 0; + } + } + } + + offset += currentFunction->parameterTypes[n].GetSizeOnStackDWords(); + } +} + +int asCContext::GetExceptionLineNumber(int *column) +{ + if( GetState() != asEXECUTION_EXCEPTION ) return asERROR; + + if( column ) *column = exceptionColumn; + + return exceptionLine; +} + +int asCContext::GetExceptionFunction() +{ + if( GetState() != asEXECUTION_EXCEPTION ) return asERROR; + + return exceptionFunction; +} + +int asCContext::GetCurrentFunction() +{ + if( status == asEXECUTION_SUSPENDED || status == asEXECUTION_ACTIVE ) + return currentFunction->id; + + return -1; +} + +int asCContext::GetCurrentLineNumber(int *column) +{ + if( status == asEXECUTION_SUSPENDED || status == asEXECUTION_ACTIVE ) + { + asDWORD line = currentFunction->GetLineNumber(int(regs.programPointer - currentFunction->byteCode.AddressOf())); + if( column ) *column = line >> 20; + + return line & 0xFFFFF; + } + + return -1; +} + +const char *asCContext::GetExceptionString() +{ + if( GetState() != asEXECUTION_EXCEPTION ) return 0; + + return exceptionString.AddressOf(); +} + +asEContextState asCContext::GetState() +{ + return status; +} + +int asCContext::SetLineCallback(asSFuncPtr callback, void *obj, int callConv) +{ + lineCallback = true; + regs.doProcessSuspend = true; + lineCallbackObj = obj; + bool isObj = false; + if( (unsigned)callConv == asCALL_GENERIC ) + { + lineCallback = false; + regs.doProcessSuspend = doSuspend; + return asNOT_SUPPORTED; + } + if( (unsigned)callConv >= asCALL_THISCALL ) + { + isObj = true; + if( obj == 0 ) + { + lineCallback = false; + regs.doProcessSuspend = doSuspend; + return asINVALID_ARG; + } + } + + int r = DetectCallingConvention(isObj, callback, callConv, &lineCallbackFunc); + if( r < 0 ) lineCallback = false; + + regs.doProcessSuspend = doSuspend || lineCallback; + + return r; +} + +void asCContext::CallLineCallback() +{ + if( lineCallbackFunc.callConv < ICC_THISCALL ) + engine->CallGlobalFunction(this, lineCallbackObj, &lineCallbackFunc, 0); + else + engine->CallObjectMethod(lineCallbackObj, this, &lineCallbackFunc, 0); +} + +int asCContext::SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv) +{ + exceptionCallback = true; + exceptionCallbackObj = obj; + bool isObj = false; + if( (unsigned)callConv == asCALL_GENERIC ) + return asNOT_SUPPORTED; + if( (unsigned)callConv >= asCALL_THISCALL ) + { + isObj = true; + if( obj == 0 ) + { + exceptionCallback = false; + return asINVALID_ARG; + } + } + int r = DetectCallingConvention(isObj, callback, callConv, &exceptionCallbackFunc); + if( r < 0 ) exceptionCallback = false; + return r; +} + +void asCContext::CallExceptionCallback() +{ + if( exceptionCallbackFunc.callConv < ICC_THISCALL ) + engine->CallGlobalFunction(this, exceptionCallbackObj, &exceptionCallbackFunc, 0); + else + engine->CallObjectMethod(exceptionCallbackObj, this, &exceptionCallbackFunc, 0); +} + +void asCContext::ClearLineCallback() +{ + lineCallback = false; + regs.doProcessSuspend = doSuspend; +} + +void asCContext::ClearExceptionCallback() +{ + exceptionCallback = false; +} + +int asCContext::CallGeneric(int id, void *objectPointer) +{ + asCScriptFunction *sysFunction = engine->scriptFunctions[id]; + asSSystemFunctionInterface *sysFunc = sysFunction->sysFuncIntf; + void (*func)(asIScriptGeneric*) = (void (*)(asIScriptGeneric*))sysFunc->func; + int popSize = sysFunc->paramSize; + asDWORD *args = regs.stackPointer; + + // Verify the object pointer if it is a class method + void *currentObject = 0; + if( sysFunc->callConv == ICC_GENERIC_METHOD ) + { + if( objectPointer ) + { + currentObject = objectPointer; + + // Don't increase the reference of this pointer + // since it will not have been constructed yet + } + else + { + // The object pointer should be popped from the context stack + popSize += AS_PTR_SIZE; + + // Check for null pointer + currentObject = (void*)*(size_t*)(args); + if( currentObject == 0 ) + { + SetInternalException(TXT_NULL_POINTER_ACCESS); + return 0; + } + + // Add the base offset for multiple inheritance + currentObject = (void*)(size_t(currentObject) + sysFunc->baseOffset); + + // Skip object pointer + args += AS_PTR_SIZE; + } + } + + asCGeneric gen(engine, sysFunction, currentObject, args); + + isCallingSystemFunction = true; + func(&gen); + isCallingSystemFunction = false; + + regs.valueRegister = gen.returnVal; + regs.objectRegister = gen.objectRegister; + regs.objectType = sysFunction->returnType.GetObjectType(); + + // Clean up function parameters + int offset = 0; + for( asUINT n = 0; n < sysFunction->parameterTypes.GetLength(); n++ ) + { + if( sysFunction->parameterTypes[n].IsObject() && !sysFunction->parameterTypes[n].IsReference() ) + { + void *obj = *(void**)&args[offset]; + if( obj ) + { + // Release the object + asSTypeBehaviour *beh = &sysFunction->parameterTypes[n].GetObjectType()->beh; + if( beh->release ) + engine->CallObjectMethod(obj, beh->release); + else + { + // Call the destructor then free the memory + if( beh->destruct ) + engine->CallObjectMethod(obj, beh->destruct); + + engine->CallFree(obj); + } + } + } + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + } + + // Return how much should be popped from the stack + return popSize; +} + +int asCContext::GetVarCount(int stackLevel) +{ + if( stackLevel < -1 || stackLevel >= GetCallstackSize() ) return asINVALID_ARG; + + asCScriptFunction *func; + if( stackLevel == -1 ) + func = currentFunction; + else + { + size_t *s = callStack.AddressOf() + stackLevel*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + } + + if( func == 0 ) + return asERROR; + + return (int)func->variables.GetLength(); +} + +const char *asCContext::GetVarName(int varIndex, int stackLevel) +{ + if( stackLevel < -1 || stackLevel >= GetCallstackSize() ) return 0; + + asCScriptFunction *func; + if( stackLevel == -1 ) + func = currentFunction; + else + { + size_t *s = callStack.AddressOf() + stackLevel*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + } + + if( func == 0 ) + return 0; + + if( varIndex < 0 || varIndex >= (signed)func->variables.GetLength() ) + return 0; + + return func->variables[varIndex]->name.AddressOf(); +} + +const char *asCContext::GetVarDeclaration(int varIndex, int stackLevel) +{ + if( stackLevel < -1 || stackLevel >= GetCallstackSize() ) return 0; + + asCScriptFunction *func; + if( stackLevel == -1 ) + func = currentFunction; + else + { + size_t *s = callStack.AddressOf() + stackLevel*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + } + + if( func == 0 ) + return 0; + + if( varIndex < 0 || varIndex >= (signed)func->variables.GetLength() ) + return 0; + + asASSERT(threadManager); + asCString *tempString = &threadManager->GetLocalData()->string; + *tempString = func->variables[varIndex]->type.Format(); + *tempString += " " + func->variables[varIndex]->name; + + return tempString->AddressOf(); +} + +int asCContext::GetVarTypeId(int varIndex, int stackLevel) +{ + if( stackLevel < -1 || stackLevel >= GetCallstackSize() ) return asINVALID_ARG; + + asCScriptFunction *func; + if( stackLevel == -1 ) + func = currentFunction; + else + { + size_t *s = callStack.AddressOf() + stackLevel*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + } + + if( func == 0 ) + return asINVALID_ARG; + + if( varIndex < 0 || varIndex >= (signed)func->variables.GetLength() ) + return asINVALID_ARG; + + return engine->GetTypeIdFromDataType(func->variables[varIndex]->type); +} + +void *asCContext::GetAddressOfVar(int varIndex, int stackLevel) +{ + if( stackLevel < -1 || stackLevel >= GetCallstackSize() ) return 0; + + asCScriptFunction *func; + asDWORD *sf; + if( stackLevel == -1 ) + { + func = currentFunction; + sf = regs.stackFramePointer; + } + else + { + size_t *s = callStack.AddressOf() + stackLevel*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + sf = (asDWORD*)s[0]; + } + + if( func == 0 ) + return 0; + + if( varIndex < 0 || varIndex >= (signed)func->variables.GetLength() ) + return 0; + + // For object variables it's necessary to dereference the pointer to get the address of the value + if( func->variables[varIndex]->type.IsObject() && !func->variables[varIndex]->type.IsObjectHandle() ) + return *(void**)(sf - func->variables[varIndex]->stackOffset); + + return sf - func->variables[varIndex]->stackOffset; +} + +// returns the typeId of the 'this' object at the given call stack level (-1 for current) +// returns 0 if the function call at the given stack level is not a method +int asCContext::GetThisTypeId(int stackLevel) +{ + if( stackLevel < -1 || stackLevel >= GetCallstackSize() ) + return 0; + + asCScriptFunction *func = 0; + if( stackLevel == -1 ) + { + func = currentFunction; + } + else + { + size_t *s = callStack.AddressOf() + stackLevel*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + } + + if( func == 0 ) + return 0; + + if( func->objectType == 0 ) + return 0; // not in a method + + // create a datatype + asCDataType dt = asCDataType::CreateObject( func->objectType, false); + + // return a typeId from the data type + return engine->GetTypeIdFromDataType( dt ); +} + +// returns the 'this' object pointer at the given call stack level (-1 for current) +// returns 0 if the function call at the given stack level is not a method +void *asCContext::GetThisPointer(int stackLevel) +{ + if( stackLevel < -1 || stackLevel >= GetCallstackSize() ) + return 0; + + asCScriptFunction *func; + asDWORD *sf; + if( stackLevel == -1 ) + { + func = currentFunction; + sf = regs.stackFramePointer; + } + else + { + size_t *s = callStack.AddressOf() + stackLevel*CALLSTACK_FRAME_SIZE; + func = (asCScriptFunction*)s[1]; + sf = (asDWORD*)s[0]; + } + + if( func == 0 ) + return 0; + + if( func->objectType == 0 ) + return 0; // not in a method + + void *thisPointer = (void*)*(size_t*)(sf); + if( thisPointer == 0 ) + { + return 0; + } + + // NOTE: this returns the pointer to the 'this' while the GetVarPointer functions return + // a pointer to a pointer. I can't imagine someone would want to change the 'this' + return thisPointer; +} + +END_AS_NAMESPACE + + + diff --git a/AngelScript/source/as_context.h b/AngelScript/source/as_context.h new file mode 100644 index 000000000..54d3fde42 --- /dev/null +++ b/AngelScript/source/as_context.h @@ -0,0 +1,210 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_context.h +// +// This class handles the execution of the byte code +// + + +#ifndef AS_CONTEXT_H +#define AS_CONTEXT_H + +#include "as_config.h" +#include "as_atomic.h" +#include "as_array.h" +#include "as_string.h" +#include "as_objecttype.h" +#include "as_callfunc.h" + +BEGIN_AS_NAMESPACE + +class asCScriptFunction; +class asCScriptEngine; + +// TODO: The context should be renamed to something that better describes it, e.g. asIVirtualMachine, asIExecuter, asIProcessor, asIScriptThread, or something like that + +class asCContext : public asIScriptContext +{ +public: + // From asIScriptContext + int AddRef(); + int Release(); + + asIScriptEngine *GetEngine(); + + asEContextState GetState(); + + int Prepare(int functionID); + int Unprepare(); + + int SetArgByte(asUINT arg, asBYTE value); + int SetArgWord(asUINT arg, asWORD value); + int SetArgDWord(asUINT arg, asDWORD value); + int SetArgQWord(asUINT arg, asQWORD value); + int SetArgFloat(asUINT arg, float value); + int SetArgDouble(asUINT arg, double value); + int SetArgAddress(asUINT arg, void *addr); + int SetArgObject(asUINT arg, void *obj); + void *GetAddressOfArg(asUINT arg); + + int SetObject(void *obj); + + asBYTE GetReturnByte(); + asWORD GetReturnWord(); + asDWORD GetReturnDWord(); + asQWORD GetReturnQWord(); + float GetReturnFloat(); + double GetReturnDouble(); + void *GetReturnAddress(); + void *GetReturnObject(); + void *GetAddressOfReturnValue(); + + int Execute(); + int Abort(); + int Suspend(); + + int GetCurrentLineNumber(int *column); + int GetCurrentFunction(); + + int SetException(const char *descr); + int GetExceptionLineNumber(int *column); + int GetExceptionFunction(); + const char *GetExceptionString(); + + int SetLineCallback(asSFuncPtr callback, void *obj, int callConv); + void ClearLineCallback(); + int SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv); + void ClearExceptionCallback(); + + int GetCallstackSize(); + int GetCallstackFunction(int index); + int GetCallstackLineNumber(int index, int *column); + + int GetVarCount(int stackLevel); + const char *GetVarName(int varIndex, int stackLevel); + const char *GetVarDeclaration(int varIndex, int stackLevel); + int GetVarTypeId(int varIndex, int stackLevel); + void *GetAddressOfVar(int varIndex, int stackLevel); + int GetThisTypeId(int stackLevel); + void *GetThisPointer(int stackLevel); + + void *SetUserData(void *data); + void *GetUserData(); + +public: + // Internal public functions + asCContext(asCScriptEngine *engine, bool holdRef); + virtual ~asCContext(); + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 + int SetExecuteStringFunction(asCScriptFunction *func); +#endif + +//protected: + friend class asCScriptEngine; + + void CallLineCallback(); + void CallExceptionCallback(); + + int CallGeneric(int funcID, void *objectPointer); + + void DetachEngine(); + + void ExecuteNext(); + void CleanStack(); + void CleanStackFrame(); + void CleanReturnObject(); + + void PushCallState(); + void PopCallState(); + void CallScriptFunction(asCScriptFunction *func); + void CallInterfaceMethod(asCScriptFunction *func); + + void SetInternalException(const char *descr); + + // Must be protected for multiple accesses + asCAtomic refCount; + + bool holdEngineRef; + asCScriptEngine *engine; + + asEContextState status; + bool doSuspend; + bool doAbort; + bool externalSuspendRequest; + bool isCallingSystemFunction; + + asCScriptFunction *currentFunction; + bool isStackMemoryNotAllocated; + + asCArray callStack; + asCArray stackBlocks; + int stackBlockSize; + int stackIndex; + + bool inExceptionHandler; + asCString exceptionString; + int exceptionFunction; + int exceptionLine; + int exceptionColumn; + + int returnValueSize; + int argumentsSize; + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 + // String function + asCScriptFunction *stringFunction; +#endif + + asCScriptFunction *initialFunction; + + // callbacks + bool lineCallback; + asSSystemFunctionInterface lineCallbackFunc; + void *lineCallbackObj; + + bool exceptionCallback; + asSSystemFunctionInterface exceptionCallbackFunc; + void *exceptionCallbackObj; + + void *userData; + + // Registers available to JIT compiler functions + asSVMRegisters regs; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_criticalsection.h b/AngelScript/source/as_criticalsection.h new file mode 100644 index 000000000..ba4817569 --- /dev/null +++ b/AngelScript/source/as_criticalsection.h @@ -0,0 +1,107 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2008 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_criticalsection.h +// +// Classes for multi threading support +// + +#ifndef AS_CRITICALSECTION_H +#define AS_CRITICALSECTION_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +#ifdef AS_NO_THREADS + +#define DECLARECRITICALSECTION(x) +#define ENTERCRITICALSECTION(x) +#define LEAVECRITICALSECTION(x) + +#else + +#define DECLARECRITICALSECTION(x) asCThreadCriticalSection x +#define ENTERCRITICALSECTION(x) x.Enter() +#define LEAVECRITICALSECTION(x) x.Leave() + +#ifdef AS_POSIX_THREADS + +END_AS_NAMESPACE +#include +BEGIN_AS_NAMESPACE + +class asCThreadCriticalSection +{ +public: + asCThreadCriticalSection(); + ~asCThreadCriticalSection(); + + void Enter(); + void Leave(); + +protected: + pthread_mutex_t criticalSection; +}; + +#elif defined(AS_WINDOWS_THREADS) + +END_AS_NAMESPACE +#define WIN32_LEAN_AND_MEAN +#include +BEGIN_AS_NAMESPACE + +// Undefine macros that cause problems in our code +#undef GetObject + +class asCThreadCriticalSection +{ +public: + asCThreadCriticalSection(); + ~asCThreadCriticalSection(); + + void Enter(); + void Leave(); + +protected: + CRITICAL_SECTION criticalSection; +}; + +#endif + +#endif + +END_AS_NAMESPACE + +#endif + diff --git a/AngelScript/source/as_datatype.cpp b/AngelScript/source/as_datatype.cpp new file mode 100644 index 000000000..78ead677b --- /dev/null +++ b/AngelScript/source/as_datatype.cpp @@ -0,0 +1,568 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_datatype.cpp +// +// This class describes the datatype for expressions during compilation +// + +#include "as_config.h" +#include "as_datatype.h" +#include "as_tokendef.h" +#include "as_objecttype.h" +#include "as_scriptengine.h" +#include "as_arrayobject.h" +#include "as_tokenizer.h" + +BEGIN_AS_NAMESPACE + +asCDataType::asCDataType() +{ + tokenType = ttUnrecognizedToken; + objectType = 0; + isReference = false; + isReadOnly = false; + isObjectHandle = false; + isConstHandle = false; +} + +asCDataType::asCDataType(const asCDataType &dt) +{ + tokenType = dt.tokenType; + objectType = dt.objectType; + isReference = dt.isReference; + isReadOnly = dt.isReadOnly; + isObjectHandle = dt.isObjectHandle; + isConstHandle = dt.isConstHandle; +} + +asCDataType::~asCDataType() +{ +} + +asCDataType asCDataType::CreateObject(asCObjectType *ot, bool isConst) +{ + asCDataType dt; + + dt.tokenType = ttIdentifier; + dt.objectType = ot; + dt.isReadOnly = isConst; + + return dt; +} + +asCDataType asCDataType::CreateObjectHandle(asCObjectType *ot, bool isConst) +{ + asCDataType dt; + + dt.tokenType = ttIdentifier; + dt.objectType = ot; + dt.isObjectHandle = true; + dt.isConstHandle = isConst; + + return dt; +} + +asCDataType asCDataType::CreatePrimitive(eTokenType tt, bool isConst) +{ + asCDataType dt; + + dt.tokenType = tt; + dt.isReadOnly = isConst; + + return dt; +} + +asCDataType asCDataType::CreateDefaultArray(asCScriptEngine *engine) +{ + asCDataType dt; + + // _builtin_array_ represents the default array + dt.objectType = engine->defaultArrayObjectType; + dt.tokenType = ttIdentifier; + + return dt; +} + +asCDataType asCDataType::CreateNullHandle() +{ + asCDataType dt; + + dt.tokenType = ttUnrecognizedToken; + dt.isReadOnly = true; + dt.isObjectHandle = true; + dt.isConstHandle = true; + + return dt; +} + +bool asCDataType::IsNullHandle() const +{ + if( tokenType == ttUnrecognizedToken && + objectType == 0 && + isObjectHandle ) + return true; + + return false; +} + +asCString asCDataType::Format() const +{ + if( IsNullHandle() ) + return ""; + + asCString str; + + if( isReadOnly ) + str = "const "; + + if( tokenType != ttIdentifier ) + { + str += asGetTokenDefinition(tokenType); + } + else if( IsArrayType() ) + { + str += objectType->templateSubType.Format(); + str += "[]"; + } + else if( objectType ) + { + str += objectType->name; + if( objectType->flags & asOBJ_TEMPLATE ) + { + str += "<"; + str += objectType->templateSubType.Format(); + str += ">"; + } + } + else + { + str = ""; + } + + if( isObjectHandle ) + { + str += "@"; + if( isConstHandle ) + str += "const"; + } + + if( isReference ) + str += "&"; + + return str; +} + + +asCDataType &asCDataType::operator =(const asCDataType &dt) +{ + tokenType = dt.tokenType; + isReference = dt.isReference; + objectType = dt.objectType; + isReadOnly = dt.isReadOnly; + isObjectHandle = dt.isObjectHandle; + isConstHandle = dt.isConstHandle; + + return (asCDataType &)*this; +} + +int asCDataType::MakeHandle(bool b, bool acceptHandleForScope) +{ + if( !b ) + { + isObjectHandle = b; + isConstHandle = false; + } + else if( b && !isObjectHandle ) + { + // Only reference types are allowed to be handles, + // but not nohandle reference types, and not scoped references (except when returned from registered function) + if( !objectType || + !((objectType->flags & asOBJ_REF) || (objectType->flags & asOBJ_TEMPLATE_SUBTYPE)) || + (objectType->flags & asOBJ_NOHANDLE) || + ((objectType->flags & asOBJ_SCOPED) && !acceptHandleForScope) ) + return -1; + + isObjectHandle = b; + isConstHandle = false; + } + + return 0; +} + +int asCDataType::MakeArray(asCScriptEngine *engine) +{ + bool tmpIsReadOnly = isReadOnly; + isReadOnly = false; + asCObjectType *at = engine->GetTemplateInstanceType(engine->defaultArrayObjectType, *this); + isReadOnly = tmpIsReadOnly; + + isObjectHandle = false; + isConstHandle = false; + + objectType = at; + tokenType = ttIdentifier; + + return 0; +} + +int asCDataType::MakeReference(bool b) +{ + isReference = b; + + return 0; +} + +int asCDataType::MakeReadOnly(bool b) +{ + if( isObjectHandle ) + { + isConstHandle = b; + return 0; + } + + isReadOnly = b; + return 0; +} + +int asCDataType::MakeHandleToConst(bool b) +{ + if( !isObjectHandle ) return -1; + + isReadOnly = b; + return 0; +} + +bool asCDataType::SupportHandles() const +{ + if( objectType && + (objectType->flags & asOBJ_REF) && + !(objectType->flags & asOBJ_NOHANDLE) && + !isObjectHandle ) + return true; + + return false; +} + +bool asCDataType::CanBeInstanciated() const +{ + if( GetSizeOnStackDWords() == 0 || + (IsObject() && + (objectType->flags & asOBJ_REF) && // It's a ref type and + ((objectType->flags & asOBJ_NOHANDLE) || // the ref type doesn't support handles or + (!IsObjectHandle() && // it's not a handle and + objectType->beh.factories.GetLength() == 0))) ) // the ref type cannot be instanciated + return false; + + return true; +} + +bool asCDataType::CanBeCopied() const +{ + // All primitives can be copied + if( IsPrimitive() ) return true; + + // Plain-old-data structures can always be copied + if( objectType->flags & asOBJ_POD ) return true; + + // It must be possible to instanciate the type + if( !CanBeInstanciated() ) return false; + + // It must have a default constructor or factory + if( objectType->beh.construct == 0 && + objectType->beh.factory == 0 ) return false; + + // It must be possible to copy the type + if( objectType->beh.copy == 0 ) return false; + + return true; +} + +bool asCDataType::IsReadOnly() const +{ + if( isObjectHandle ) + return isConstHandle; + + return isReadOnly; +} + +bool asCDataType::IsHandleToConst() const +{ + if( !isObjectHandle ) return false; + return isReadOnly; +} + +// TODO: 3.0.0: This should be removed +bool asCDataType::IsArrayType() const +{ + // TODO: array: The default array type should be defined by the application + return objectType ? (objectType->name == objectType->engine->defaultArrayObjectType->name) : false; +} + +bool asCDataType::IsTemplate() const +{ + if( objectType && (objectType->flags & asOBJ_TEMPLATE) ) + return true; + + return false; +} + +bool asCDataType::IsScriptObject() const +{ + if( objectType && (objectType->flags & asOBJ_SCRIPT_OBJECT) ) + return true; + + return false; +} + +asCDataType asCDataType::GetSubType() const +{ + asASSERT(objectType); + return objectType->templateSubType; +} + + +bool asCDataType::operator !=(const asCDataType &dt) const +{ + return !(*this == dt); +} + +bool asCDataType::operator ==(const asCDataType &dt) const +{ + if( !IsEqualExceptRefAndConst(dt) ) return false; + if( isReference != dt.isReference ) return false; + if( isReadOnly != dt.isReadOnly ) return false; + if( isConstHandle != dt.isConstHandle ) return false; + + return true; +} + +bool asCDataType::IsEqualExceptRef(const asCDataType &dt) const +{ + if( !IsEqualExceptRefAndConst(dt) ) return false; + if( isReadOnly != dt.isReadOnly ) return false; + if( isConstHandle != dt.isConstHandle ) return false; + + return true; +} + +bool asCDataType::IsEqualExceptRefAndConst(const asCDataType &dt) const +{ + // Check base type + if( tokenType != dt.tokenType ) return false; + if( objectType != dt.objectType ) return false; + if( isObjectHandle != dt.isObjectHandle ) return false; + if( isObjectHandle ) + if( isReadOnly != dt.isReadOnly ) return false; + + return true; +} + +bool asCDataType::IsEqualExceptConst(const asCDataType &dt) const +{ + if( !IsEqualExceptRefAndConst(dt) ) return false; + if( isReference != dt.isReference ) return false; + + return true; +} + +bool asCDataType::IsEqualExceptInterfaceType(const asCDataType &dt) const +{ + if( tokenType != dt.tokenType ) return false; + if( isReference != dt.isReference ) return false; + if( isObjectHandle != dt.isObjectHandle ) return false; + if( isReadOnly != dt.isReadOnly ) return false; + if( isConstHandle != dt.isConstHandle ) return false; + + if( objectType != dt.objectType ) + { + if( !objectType || !dt.objectType ) return false; + if( !objectType->IsInterface() || !dt.objectType->IsInterface() ) return false; + } + + return true; +} + +bool asCDataType::IsPrimitive() const +{ + // Enumerations are primitives + if( IsEnumType() ) + return true; + + // A registered object is never a primitive neither is a pointer, nor an array + if( objectType ) + return false; + + // Null handle doesn't have an objectType, but it is not a primitive + if( tokenType == ttUnrecognizedToken ) + return false; + + return true; +} + +bool asCDataType::IsSamePrimitiveBaseType(const asCDataType &dt) const +{ + if( !IsPrimitive() || !dt.IsPrimitive() ) return false; + + if( IsIntegerType() && dt.IsIntegerType() ) return true; + if( IsUnsignedType() && dt.IsUnsignedType() ) return true; + if( IsFloatType() && dt.IsFloatType() ) return true; + if( IsDoubleType() && dt.IsDoubleType() ) return true; + if( IsBooleanType() && dt.IsBooleanType() ) return true; + if( IsFloatType() && dt.IsDoubleType() ) return true; + if( IsDoubleType() && dt.IsFloatType() ) return true; + + return false; +} + +bool asCDataType::IsIntegerType() const +{ + if( tokenType == ttInt || + tokenType == ttInt8 || + tokenType == ttInt16 || + tokenType == ttInt64 ) + return true; + + return false; +} + +bool asCDataType::IsUnsignedType() const +{ + if( tokenType == ttUInt || + tokenType == ttUInt8 || + tokenType == ttUInt16 || + tokenType == ttUInt64 ) + return true; + + return false; +} + +bool asCDataType::IsFloatType() const +{ + if( tokenType == ttFloat ) + return true; + + return false; +} + +bool asCDataType::IsDoubleType() const +{ + if( tokenType == ttDouble ) + return true; + + return false; +} + +bool asCDataType::IsBooleanType() const +{ + if( tokenType == ttBool ) + return true; + + return false; +} + +bool asCDataType::IsObject() const +{ + // Enumerations are not objects, even though they are described with an objectType. + if( IsEnumType() ) + return false; + + if( objectType ) return true; + + return false; +} + +int asCDataType::GetSizeInMemoryBytes() const +{ + if( objectType != 0 ) + return objectType->size; + + if( tokenType == ttVoid ) + return 0; + + if( tokenType == ttInt8 || + tokenType == ttUInt8 ) + return 1; + + if( tokenType == ttInt16 || + tokenType == ttUInt16 ) + return 2; + + if( tokenType == ttDouble || + tokenType == ttInt64 || + tokenType == ttUInt64 ) + return 8; + + if( tokenType == ttBool ) + return AS_SIZEOF_BOOL; + + // null handle + if( tokenType == ttUnrecognizedToken ) + return 4*AS_PTR_SIZE; + + return 4; +} + +int asCDataType::GetSizeInMemoryDWords() const +{ + int s = GetSizeInMemoryBytes(); + if( s == 0 ) return 0; + if( s <= 4 ) return 1; + + return s/4; +} + +int asCDataType::GetSizeOnStackDWords() const +{ + int size = tokenType == ttQuestion ? 1 : 0; + + if( isReference ) return AS_PTR_SIZE + size; + if( objectType ) return AS_PTR_SIZE + size; + + return GetSizeInMemoryDWords() + size; +} + +asSTypeBehaviour *asCDataType::GetBehaviour() const +{ + return objectType ? &objectType->beh : 0; +} + +bool asCDataType::IsEnumType() const +{ + if( objectType && (objectType->flags & asOBJ_ENUM) ) + return true; + + return false; +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_datatype.h b/AngelScript/source/as_datatype.h new file mode 100644 index 000000000..c7cf44fe8 --- /dev/null +++ b/AngelScript/source/as_datatype.h @@ -0,0 +1,135 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_datatype.h +// +// This class describes the datatype for expressions during compilation +// + + + +#ifndef AS_DATATYPE_H +#define AS_DATATYPE_H + +#include "as_tokendef.h" +#include "as_string.h" + +BEGIN_AS_NAMESPACE + +struct asSTypeBehaviour; +class asCScriptEngine; +class asCObjectType; + +class asCDataType +{ +public: + asCDataType(); + asCDataType(const asCDataType &); + ~asCDataType(); + + asCString Format() const; + + static asCDataType CreatePrimitive(eTokenType tt, bool isConst); + static asCDataType CreateObject(asCObjectType *ot, bool isConst); + static asCDataType CreateObjectHandle(asCObjectType *ot, bool isConst); + static asCDataType CreateDefaultArray(asCScriptEngine *engine); + static asCDataType CreateNullHandle(); + + int MakeHandle(bool b, bool acceptHandleForScope = false); + int MakeArray(asCScriptEngine *engine); + int MakeReference(bool b); + int MakeReadOnly(bool b); + int MakeHandleToConst(bool b); + + bool IsTemplate() const; + bool IsScriptObject() const; + bool IsPrimitive() const; + bool IsObject() const; + bool IsReference() const {return isReference;} + bool IsReadOnly() const; + bool IsIntegerType() const; + bool IsUnsignedType() const; + bool IsFloatType() const; + bool IsDoubleType() const; + bool IsBooleanType() const; + bool IsObjectHandle() const {return isObjectHandle;} + bool IsHandleToConst() const; + bool IsArrayType() const; + bool IsEnumType() const; + + bool IsSamePrimitiveBaseType(const asCDataType &dt) const; + bool IsEqualExceptRef(const asCDataType &) const; + bool IsEqualExceptRefAndConst(const asCDataType &) const; + bool IsEqualExceptConst(const asCDataType &) const; + bool IsEqualExceptInterfaceType(const asCDataType &dt) const; + bool IsNullHandle() const; + + bool SupportHandles() const; + bool CanBeInstanciated() const; + bool CanBeCopied() const; + + bool operator ==(const asCDataType &) const; + bool operator !=(const asCDataType &) const; + + asCDataType GetSubType() const; + eTokenType GetTokenType() const {return tokenType;} + asCObjectType *GetObjectType() const {return objectType;} + + int GetSizeOnStackDWords() const; + int GetSizeInMemoryBytes() const; + int GetSizeInMemoryDWords() const; + + void SetTokenType(eTokenType tt) {tokenType = tt;} + void SetObjectType(asCObjectType *obj) {objectType = obj;} + + asCDataType &operator =(const asCDataType &); + + asSTypeBehaviour *GetBehaviour() const; + +protected: + // Base object type + eTokenType tokenType; + + // Behaviour type + asCObjectType *objectType; + + // Top level + bool isReference:1; + bool isReadOnly:1; + bool isObjectHandle:1; + bool isConstHandle:1; + char dummy:4; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_debug.h b/AngelScript/source/as_debug.h new file mode 100644 index 000000000..78490a614 --- /dev/null +++ b/AngelScript/source/as_debug.h @@ -0,0 +1,59 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_debug.h +// + +#ifndef AS_DEBUG_H +#define AS_DEBUG_H + +#ifndef AS_WII +// The Wii SDK doesn't have these, we'll survive without AS_DEBUG + +#ifndef _WIN32_WCE +// Neither does WinCE + + +#if defined(__GNUC__) +// Define mkdir for GNUC +#include +#include +#define _mkdir(dirname) mkdir(dirname, S_IRWXU) +#else +#include +#endif +#endif +#endif + +#endif + + diff --git a/AngelScript/source/as_gc.cpp b/AngelScript/source/as_gc.cpp new file mode 100644 index 000000000..e6af74c18 --- /dev/null +++ b/AngelScript/source/as_gc.cpp @@ -0,0 +1,536 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_gc.cpp +// +// The implementation of the garbage collector +// + + +#include + +#include "as_gc.h" +#include "as_scriptengine.h" +#include "as_scriptobject.h" + +BEGIN_AS_NAMESPACE + +asCGarbageCollector::asCGarbageCollector() +{ + engine = 0; + detectState = clearCounters_init; + destroyState = destroyGarbage_init; + numDestroyed = 0; + numDetected = 0; +} + +void asCGarbageCollector::AddScriptObjectToGC(void *obj, asCObjectType *objType) +{ + engine->CallObjectMethod(obj, objType->beh.addref); + asSObjTypePair ot = {obj, objType}; + + // Add the data to the gcObjects array in a critical section as + // another thread might be calling this method at the same time + ENTERCRITICALSECTION(gcCritical); + gcObjects.PushLast(ot); + LEAVECRITICALSECTION(gcCritical); +} + +int asCGarbageCollector::GarbageCollect(asDWORD flags) +{ + // The application is responsible for making sure + // the gc is only executed by one thread at a time. + + bool doDetect = (flags & asGC_DETECT_GARBAGE) || !(flags & asGC_DESTROY_GARBAGE); + bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE); + + if( flags & asGC_FULL_CYCLE ) + { + // Reset the state + if( doDetect ) + detectState = clearCounters_init; + if( doDestroy ) + destroyState = destroyGarbage_init; + + int r = 1; + unsigned int count = (unsigned int)gcObjects.GetLength(); + for(;;) + { + // Detect all garbage with cyclic references + if( doDetect ) + while( (r = IdentifyGarbageWithCyclicRefs()) == 1 ); + + // Now destroy all known garbage + if( doDestroy ) + while( (r = DestroyGarbage()) == 1 ); + + // Run another iteration if any garbage was destroyed + if( count != gcObjects.GetLength() ) + count = (unsigned int)gcObjects.GetLength(); + else + break; + } + + // Take the opportunity to clear unused types as well + engine->ClearUnusedTypes(); + + return 0; + } + else + { + // Destroy the garbage that we know of + if( doDestroy ) + DestroyGarbage(); + + // Run another incremental step of the identification of cyclic references + if( doDetect ) + IdentifyGarbageWithCyclicRefs(); + } + + // Return 1 to indicate that the cycle wasn't finished + return 1; +} + +void asCGarbageCollector::GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected) +{ + // It's not necessary to protect this access, as + // it doesn't matter if another thread is currently + // appending a new object. + if( currentSize ) + *currentSize = (asUINT)gcObjects.GetLength(); + + if( totalDestroyed ) + *totalDestroyed = numDestroyed; + + if( totalDetected ) + *totalDetected = numDetected; +} + +void asCGarbageCollector::ClearMap() +{ + // Decrease reference counter for all objects removed from the map + asSMapNode *cursor = 0; + gcMap.MoveFirst(&cursor); + while( cursor ) + { + void *obj = gcMap.GetKey(cursor); + asSIntTypePair it = gcMap.GetValue(cursor); + + engine->CallObjectMethod(obj, it.type->beh.release); + + gcMap.MoveNext(&cursor, cursor); + } + + gcMap.EraseAll(); +} + +asCGarbageCollector::asSObjTypePair asCGarbageCollector::GetObjectAtIdx(int idx) +{ + // We need to protect this access with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + asSObjTypePair gcObj = gcObjects[idx]; + LEAVECRITICALSECTION(gcCritical); + + return gcObj; +} + +void asCGarbageCollector::RemoveObjectAtIdx(int idx) +{ + // We need to protect this update with a critical section as + // another thread might be appending an object at the same time + ENTERCRITICALSECTION(gcCritical); + if( idx == (int)gcObjects.GetLength() - 1) + gcObjects.PopLast(); + else + gcObjects[idx] = gcObjects.PopLast(); + LEAVECRITICALSECTION(gcCritical); +} + +int asCGarbageCollector::DestroyGarbage() +{ + for(;;) + { + switch( destroyState ) + { + case destroyGarbage_init: + { + // If there are no objects to be freed then don't start + if( gcObjects.GetLength() == 0 ) + return 0; + + destroyIdx = (asUINT)-1; + destroyState = destroyGarbage_loop; + } + break; + + case destroyGarbage_loop: + case destroyGarbage_haveMore: + { + // If the refCount has reached 1, then only the GC still holds a + // reference to the object, thus we don't need to worry about the + // application touching the objects during collection. + + // Destroy all objects that have refCount == 1. If any objects are + // destroyed, go over the list again, because it may have made more + // objects reach refCount == 1. + while( ++destroyIdx < gcObjects.GetLength() ) + { + asSObjTypePair gcObj = GetObjectAtIdx(destroyIdx); + if( engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount) == 1 ) + { + // Release the object immediately + + // Make sure the refCount is really 0, because the + // destructor may have increased the refCount again. + bool addRef = false; + if( gcObj.type->flags & asOBJ_SCRIPT_OBJECT ) + { + // Script objects may actually be resurrected in the destructor + int refCount = ((asCScriptObject*)gcObj.obj)->Release(); + if( refCount > 0 ) addRef = true; + } + else + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.release); + + // Was the object really destroyed? + if( !addRef ) + { + numDestroyed++; + RemoveObjectAtIdx(destroyIdx); + destroyIdx--; + } + else + { + // Since the object was resurrected in the + // destructor, we must add our reference again + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref); + } + + destroyState = destroyGarbage_haveMore; + + // Allow the application to work a little + return 1; + } + } + + // Only move to the next step if no garbage was detected in this step + if( destroyState == destroyGarbage_haveMore ) + destroyState = destroyGarbage_init; + else + return 0; + } + break; + } + } + + // Shouldn't reach this point + UNREACHABLE_RETURN; +} + +int asCGarbageCollector::IdentifyGarbageWithCyclicRefs() +{ + for(;;) + { + switch( detectState ) + { + case clearCounters_init: + { + ClearMap(); + detectState = clearCounters_loop; + detectIdx = 0; + } + break; + + case clearCounters_loop: + { + // Build a map of objects that will be checked, the map will + // hold the object pointer as key, and the gcCount and the + // object's type as value. As objects are added to the map the + // gcFlag must be set in the objects, so we can be verify if + // the object is accessed during the GC cycle. + + // If an object is removed from the gcObjects list during the + // iteration of this step, it is possible that an object won't + // be used during the analyzing for cyclic references. This + // isn't a problem, as the next time the GC cycle starts the + // object will be verified. + while( detectIdx < gcObjects.GetLength() ) + { + // Add the gc count for this object + asSObjTypePair gcObj = GetObjectAtIdx(detectIdx); + int refCount = engine->CallObjectMethodRetInt(gcObj.obj, gcObj.type->beh.gcGetRefCount); + if( refCount > 1 ) + { + asSIntTypePair it = {refCount-1, gcObj.type}; + gcMap.Insert(gcObj.obj, it); + + // Increment the object's reference counter when putting it in the map + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.addref); + + // Mark the object so that we can + // see if it has changed since read + engine->CallObjectMethod(gcObj.obj, gcObj.type->beh.gcSetFlag); + + detectIdx++; + + // Let the application work a little + return 1; + } + else + detectIdx++; + } + + detectState = countReferences_init; + } + break; + + case countReferences_init: + { + detectIdx = (asUINT)-1; + gcMap.MoveFirst(&gcMapCursor); + detectState = countReferences_loop; + } + break; + + case countReferences_loop: + { + // Call EnumReferences on all objects in the map to count the number + // of references reachable from between objects in the map. If all + // references for an object in the map is reachable from other objects + // in the map, then we know that no outside references are held for + // this object, thus it is a potential dead object in a circular reference. + + // If the gcFlag is cleared for an object we consider the object alive + // and referenced from outside the GC, thus we don't enumerate its references. + + // Any new objects created after this step in the GC cycle won't be + // in the map, and is thus automatically considered alive. + while( gcMapCursor ) + { + void *obj = gcMap.GetKey(gcMapCursor); + asCObjectType *type = gcMap.GetValue(gcMapCursor).type; + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + + if( engine->CallObjectMethodRetBool(obj, type->beh.gcGetFlag) ) + { + engine->CallObjectMethod(obj, engine, type->beh.gcEnumReferences); + + // Allow the application to work a little + return 1; + } + } + + detectState = detectGarbage_init; + } + break; + + case detectGarbage_init: + { + detectIdx = (asUINT)-1; + gcMap.MoveFirst(&gcMapCursor); + liveObjects.SetLength(0); + detectState = detectGarbage_loop1; + } + break; + + case detectGarbage_loop1: + { + // All objects that are known not to be dead must be removed from the map, + // along with all objects they reference. What remains in the map after + // this pass is sure to be dead objects in circular references. + + // An object is considered alive if its gcFlag is cleared, or all the + // references were not found in the map. + + // Add all alive objects from the map to the liveObjects array + while( gcMapCursor ) + { + asSMapNode *cursor = gcMapCursor; + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + + void *obj = gcMap.GetKey(cursor); + asSIntTypePair it = gcMap.GetValue(cursor); + + bool gcFlag = engine->CallObjectMethodRetBool(obj, it.type->beh.gcGetFlag); + if( !gcFlag || it.i > 0 ) + { + liveObjects.PushLast(obj); + + // Allow the application to work a little + return 1; + } + } + + detectState = detectGarbage_loop2; + } + break; + + case detectGarbage_loop2: + { + // In this step we are actually removing the alive objects from the map. + // As the object is removed, all the objects it references are added to the + // liveObjects list, by calling EnumReferences. Only objects still in the map + // will be added to the liveObjects list. + while( liveObjects.GetLength() ) + { + void *gcObj = liveObjects.PopLast(); + asCObjectType *type = 0; + + // Remove the object from the map to mark it as alive + asSMapNode *cursor = 0; + if( gcMap.MoveTo(&cursor, gcObj) ) + { + type = gcMap.GetValue(cursor).type; + gcMap.Erase(cursor); + + // We need to decrease the reference count again as we remove the object from the map + engine->CallObjectMethod(gcObj, type->beh.release); + + // Enumerate all the object's references so that they too can be marked as alive + engine->CallObjectMethod(gcObj, engine, type->beh.gcEnumReferences); + } + + // Allow the application to work a little + return 1; + } + + detectState = verifyUnmarked; + } + break; + + case verifyUnmarked: + { + // In this step we must make sure that none of the objects still in the map + // has been touched by the application. If they have then we must run the + // detectGarbage loop once more. + gcMap.MoveFirst(&gcMapCursor); + while( gcMapCursor ) + { + void *gcObj = gcMap.GetKey(gcMapCursor); + asCObjectType *type = gcMap.GetValue(gcMapCursor).type; + + bool gcFlag = engine->CallObjectMethodRetBool(gcObj, type->beh.gcGetFlag); + if( !gcFlag ) + { + // The unmarked object was touched, rerun the detectGarbage loop + detectState = detectGarbage_init; + return 1; + } + + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + } + + // No unmarked object was touched, we can now be sure + // that objects that have gcCount == 0 really is garbage + detectState = breakCircles_init; + } + break; + + case breakCircles_init: + { + detectIdx = (asUINT)-1; + gcMap.MoveFirst(&gcMapCursor); + detectState = breakCircles_loop; + } + break; + + case breakCircles_loop: + case breakCircles_haveGarbage: + { + // All objects in the map are now known to be dead objects + // kept alive through circular references. To be able to free + // these objects we need to force the breaking of the circle + // by having the objects release their references. + while( gcMapCursor ) + { + numDetected++; + void *gcObj = gcMap.GetKey(gcMapCursor); + asCObjectType *type = gcMap.GetValue(gcMapCursor).type; + engine->CallObjectMethod(gcObj, engine, type->beh.gcReleaseAllReferences); + + gcMap.MoveNext(&gcMapCursor, gcMapCursor); + + detectState = breakCircles_haveGarbage; + + // Allow the application to work a little + return 1; + } + + // If no garbage was detected we can finish now + if( detectState != breakCircles_haveGarbage ) + { + // Restart the GC + detectState = clearCounters_init; + return 0; + } + else + { + // Restart the GC + detectState = clearCounters_init; + return 1; + } + } + break; + } // switch + } + + // Shouldn't reach this point + UNREACHABLE_RETURN; +} + +void asCGarbageCollector::GCEnumCallback(void *reference) +{ + if( detectState == countReferences_loop ) + { + // Find the reference in the map + asSMapNode *cursor = 0; + if( gcMap.MoveTo(&cursor, reference) ) + { + // Decrease the counter in the map for the reference + gcMap.GetValue(cursor).i--; + } + } + else if( detectState == detectGarbage_loop2 ) + { + // Find the reference in the map + asSMapNode *cursor = 0; + if( gcMap.MoveTo(&cursor, reference) ) + { + // Add the object to the list of objects to mark as alive + liveObjects.PushLast(reference); + } + } +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_gc.h b/AngelScript/source/as_gc.h new file mode 100644 index 000000000..944144692 --- /dev/null +++ b/AngelScript/source/as_gc.h @@ -0,0 +1,122 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_gc.h +// +// The garbage collector is used to resolve cyclic references +// + + + +#ifndef AS_GC_H +#define AS_GC_H + +#include "as_config.h" +#include "as_array.h" +#include "as_map.h" +#include "as_thread.h" + +BEGIN_AS_NAMESPACE + +class asCScriptEngine; +class asCObjectType; + +class asCGarbageCollector +{ +public: + asCGarbageCollector(); + + int GarbageCollect(asDWORD flags); + void GetStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected); + void GCEnumCallback(void *reference); + void AddScriptObjectToGC(void *obj, asCObjectType *objType); + + asCScriptEngine *engine; + +protected: + struct asSObjTypePair {void *obj; asCObjectType *type;}; + struct asSIntTypePair {int i; asCObjectType *type;}; + + enum egcDestroyState + { + destroyGarbage_init = 0, + destroyGarbage_loop, + destroyGarbage_haveMore + }; + + enum egcDetectState + { + clearCounters_init = 0, + clearCounters_loop, + countReferences_init, + countReferences_loop, + detectGarbage_init, + detectGarbage_loop1, + detectGarbage_loop2, + verifyUnmarked, + breakCircles_init, + breakCircles_loop, + breakCircles_haveGarbage + }; + + int DestroyGarbage(); + int IdentifyGarbageWithCyclicRefs(); + void ClearMap(); + asSObjTypePair GetObjectAtIdx(int idx); + void RemoveObjectAtIdx(int idx); + + // Holds all the objects known by the garbage collector + asCArray gcObjects; + + // This array temporarily holds references to objects known to be live objects + asCArray liveObjects; + + // This map holds objects currently being searched for cyclic references, it also holds a + // counter that gives the number of references to the object that the GC can't reach + asCMap gcMap; + + // State variables + egcDestroyState destroyState; + asUINT destroyIdx; + asUINT numDestroyed; + egcDetectState detectState; + asUINT detectIdx; + asUINT numDetected; + asSMapNode *gcMapCursor; + + // Critical section for multithreaded access + DECLARECRITICALSECTION(gcCritical); +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_generic.cpp b/AngelScript/source/as_generic.cpp new file mode 100644 index 000000000..d0b32484e --- /dev/null +++ b/AngelScript/source/as_generic.cpp @@ -0,0 +1,507 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_generic.cpp +// +// This class handles the call to a function registered with asCALL_GENERIC +// + +#include "as_generic.h" +#include "as_scriptfunction.h" +#include "as_objecttype.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +// TODO: optimize: The access to the arguments should be optimized so that code +// doesn't have to count the position of the argument with every call + +// internal +asCGeneric::asCGeneric(asCScriptEngine *engine, asCScriptFunction *sysFunction, void *currentObject, asDWORD *stackPointer) +{ + this->engine = engine; + this->sysFunction = sysFunction; + this->currentObject = currentObject; + this->stackPointer = stackPointer; + + objectRegister = 0; + returnVal = 0; +} + +// internal +asCGeneric::~asCGeneric() +{ +} + +// interface +asIScriptEngine *asCGeneric::GetEngine() +{ + return (asIScriptEngine*)engine; +} + +// interface +int asCGeneric::GetFunctionId() +{ + return sysFunction->id; +} + +// interface +void *asCGeneric::GetObject() +{ + return currentObject; +} + +// interface +int asCGeneric::GetObjectTypeId() +{ + asCDataType dt = asCDataType::CreateObject(sysFunction->objectType, false); + return engine->GetTypeIdFromDataType(dt); +} + +// interface +int asCGeneric::GetArgCount() +{ + return (int)sysFunction->parameterTypes.GetLength(); +} + +// interface +asBYTE asCGeneric::GetArgByte(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 1 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asBYTE*)&stackPointer[offset]; +} + +// interface +asWORD asCGeneric::GetArgWord(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 2 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asWORD*)&stackPointer[offset]; +} + +// interface +asDWORD asCGeneric::GetArgDWord(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 4 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asDWORD*)&stackPointer[offset]; +} + +// interface +asQWORD asCGeneric::GetArgQWord(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 8 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(asQWORD*)(&stackPointer[offset]); +} + +// interface +float asCGeneric::GetArgFloat(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 4 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(float*)(&stackPointer[offset]); +} + +// interface +double asCGeneric::GetArgDouble(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->IsObject() || dt->IsReference() ) + return 0; + + if( dt->GetSizeInMemoryBytes() != 8 ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(double*)(&stackPointer[offset]); +} + +// interface +void *asCGeneric::GetArgAddress(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( !dt->IsReference() && !dt->IsObjectHandle() ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return (void*)*(size_t*)(&stackPointer[offset]); +} + +// interface +void *asCGeneric::GetArgObject(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Verify that the type is correct + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( !dt->IsObject() ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Get the value + return *(void**)(&stackPointer[offset]); +} + +// interface +void *asCGeneric::GetAddressOfArg(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + // Determine the position of the argument + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // For object variables it's necessary to dereference the pointer to get the address of the value + if( !sysFunction->parameterTypes[arg].IsReference() && + sysFunction->parameterTypes[arg].IsObject() && + !sysFunction->parameterTypes[arg].IsObjectHandle() ) + return *(void**)&stackPointer[offset]; + + // Get the address of the value + return &stackPointer[offset]; +} + +// interface +int asCGeneric::GetArgTypeId(asUINT arg) +{ + if( arg >= (unsigned)sysFunction->parameterTypes.GetLength() ) + return 0; + + asCDataType *dt = &sysFunction->parameterTypes[arg]; + if( dt->GetTokenType() != ttQuestion ) + return engine->GetTypeIdFromDataType(*dt); + else + { + int offset = 0; + for( asUINT n = 0; n < arg; n++ ) + offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); + + // Skip the actual value to get to the type id + offset += AS_PTR_SIZE; + + // Get the value + return stackPointer[offset]; + } +} + +// interface +int asCGeneric::SetReturnByte(asBYTE val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsObject() || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeInMemoryBytes() != 1 ) + return asINVALID_TYPE; + + // Store the value + *(asBYTE*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnWord(asWORD val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsObject() || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeInMemoryBytes() != 2 ) + return asINVALID_TYPE; + + // Store the value + *(asWORD*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnDWord(asDWORD val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsObject() || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeInMemoryBytes() != 4 ) + return asINVALID_TYPE; + + // Store the value + *(asDWORD*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnQWord(asQWORD val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsObject() || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeOnStackDWords() != 2 ) + return asINVALID_TYPE; + + // Store the value + returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnFloat(float val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsObject() || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeOnStackDWords() != 1 ) + return asINVALID_TYPE; + + // Store the value + *(float*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnDouble(double val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsObject() || sysFunction->returnType.IsReference() ) + return asINVALID_TYPE; + + if( sysFunction->returnType.GetSizeOnStackDWords() != 2 ) + return asINVALID_TYPE; + + // Store the value + *(double*)&returnVal = val; + + return 0; +} + +// interface +int asCGeneric::SetReturnAddress(void *val) +{ + // Verify the type of the return value + if( sysFunction->returnType.IsReference() ) + { + // Store the value + *(void**)&returnVal = val; + return 0; + } + else if( sysFunction->returnType.IsObjectHandle() ) + { + // Store the handle without increasing reference + objectRegister = val; + return 0; + } + + return asINVALID_TYPE; +} + +// interface +int asCGeneric::SetReturnObject(void *obj) +{ + asCDataType *dt = &sysFunction->returnType; + if( !dt->IsObject() ) + return asINVALID_TYPE; + + if( dt->IsReference() ) + { + *(void**)&returnVal = obj; + return 0; + } + + if( dt->IsObjectHandle() ) + { + // Increase the reference counter + asSTypeBehaviour *beh = &dt->GetObjectType()->beh; + if( obj && beh->addref ) + engine->CallObjectMethod(obj, beh->addref); + } + else + { + obj = engine->CreateScriptObjectCopy(obj, engine->GetTypeIdFromDataType(*dt)); + } + + objectRegister = obj; + + return 0; +} + +// internal +void *asCGeneric::GetReturnPointer() +{ + asCDataType &dt = sysFunction->returnType; + + if( dt.IsObject() && !dt.IsReference() ) + return &objectRegister; + + return &returnVal; +} + +// interface +void *asCGeneric::GetAddressOfReturnLocation() +{ + asCDataType &dt = sysFunction->returnType; + + if( dt.IsObject() && !dt.IsReference() ) + { + if( dt.GetObjectType()->flags & asOBJ_VALUE ) + { + // Allocate the necessary memory for this object, + // but do not initialize it, as the caller will do that. + objectRegister = engine->CallAlloc(dt.GetObjectType()); + + // TODO: How will we know if the initialization was successful? + + return objectRegister; + } + + // Reference types store the handle in the objectReference + return &objectRegister; + } + + // Primitive types and references are stored in the returnVal property + return &returnVal; +} + +// interface +int asCGeneric::GetReturnTypeId() +{ + return sysFunction->GetReturnTypeId(); +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_generic.h b/AngelScript/source/as_generic.h new file mode 100644 index 000000000..30662af37 --- /dev/null +++ b/AngelScript/source/as_generic.h @@ -0,0 +1,107 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_generic.h +// +// This class handles the call to a function registered with asCALL_GENERIC +// + + +#ifndef AS_GENERIC_H +#define AS_GENERIC_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +class asCScriptEngine; +class asCScriptFunction; + +class asCGeneric : public asIScriptGeneric +{ +public: +//------------------------------ +// asIScriptGeneric +//------------------------------ + // Miscellaneous + asIScriptEngine *GetEngine(); + int GetFunctionId(); + + // Object + void *GetObject(); + int GetObjectTypeId(); + + // Arguments + int GetArgCount(); + int GetArgTypeId(asUINT arg); + asBYTE GetArgByte(asUINT arg); + asWORD GetArgWord(asUINT arg); + asDWORD GetArgDWord(asUINT arg); + asQWORD GetArgQWord(asUINT arg); + float GetArgFloat(asUINT arg); + double GetArgDouble(asUINT arg); + void *GetArgAddress(asUINT arg); + void *GetArgObject(asUINT arg); + void *GetAddressOfArg(asUINT arg); + + // Return value + int GetReturnTypeId(); + int SetReturnByte(asBYTE val); + int SetReturnWord(asWORD val); + int SetReturnDWord(asDWORD val); + int SetReturnQWord(asQWORD val); + int SetReturnFloat(float val); + int SetReturnDouble(double val); + int SetReturnAddress(void *addr); + int SetReturnObject(void *obj); + void *GetAddressOfReturnLocation(); + +//------------------------ +// internal +//------------------------- + asCGeneric(asCScriptEngine *engine, asCScriptFunction *sysFunction, void *currentObject, asDWORD *stackPointer); + virtual ~asCGeneric(); + + void *GetReturnPointer(); + + asCScriptEngine *engine; + asCScriptFunction *sysFunction; + void *currentObject; + asDWORD *stackPointer; + void *objectRegister; + + asQWORD returnVal; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_globalproperty.cpp b/AngelScript/source/as_globalproperty.cpp new file mode 100644 index 000000000..1d075b5a2 --- /dev/null +++ b/AngelScript/source/as_globalproperty.cpp @@ -0,0 +1,107 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + + +#include "as_config.h" +#include "as_property.h" + +BEGIN_AS_NAMESPACE + +asCGlobalProperty::asCGlobalProperty() +{ + memory = 0; + memoryAllocated = false; + realAddress = 0; + initFunc = 0; + + refCount.set(1); +} + +asCGlobalProperty::~asCGlobalProperty() +{ + if( memoryAllocated ) { asDELETEARRAY(memory); } + if( initFunc ) + initFunc->Release(); +} + +void asCGlobalProperty::AddRef() +{ + refCount.atomicInc(); +} + +void asCGlobalProperty::Release() +{ + // The property doesn't delete itself. The + // engine will do that at a later time + if( refCount.atomicDec() == 1 && initFunc ) + { + // Since the initFunc holds references to the property, + // we'll release it when we reach refCount 1. This will + // break the circle and allow the engine to free the property + // without the need for a GC run. + initFunc->Release(); + initFunc = 0; + } +} + +void *asCGlobalProperty::GetAddressOfValue() +{ + return (memoryAllocated || realAddress) ? memory : &storage; +} + +// The global property structure is responsible for allocating the storage +// method for script declared variables. Each allocation is independent of +// other global properties, so that variables can be added and removed at +// any time. +void asCGlobalProperty::AllocateMemory() +{ + if( type.GetSizeOnStackDWords() > 2 ) + { + memory = asNEWARRAY(asDWORD, type.GetSizeOnStackDWords()); + memoryAllocated = true; + } +} + +void asCGlobalProperty::SetRegisteredAddress(void *p) +{ + realAddress = p; + if( type.IsObject() && !type.IsReference() && !type.IsObjectHandle() ) + { + // The global property is a pointer to a pointer + memory = &realAddress; + } + else + memory = p; +} + + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_map.h b/AngelScript/source/as_map.h new file mode 100644 index 000000000..d14fd1618 --- /dev/null +++ b/AngelScript/source/as_map.h @@ -0,0 +1,744 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_map.h +// +// This class is used for mapping a value to another +// + + +#ifndef AS_MAP_H +#define AS_MAP_H + +template struct asSMapNode; + +template class asCMap +{ +public: + asCMap(); + ~asCMap(); + + int Insert(const KEY &key, const VAL &value); + int GetCount(); + + const KEY &GetKey(const asSMapNode *cursor) const; + const VAL &GetValue(const asSMapNode *cursor) const; + VAL &GetValue(asSMapNode *cursor); + + void Erase(asSMapNode *cursor); + void EraseAll(); + + // Returns true as long as cursor is valid + + bool MoveTo(asSMapNode **out, const KEY &key); + bool MoveFirst(asSMapNode **out); + bool MoveLast(asSMapNode **out); + bool MoveNext(asSMapNode **out, asSMapNode *cursor); + bool MovePrev(asSMapNode **out, asSMapNode *cursor); + + // For debugging only + + int CheckIntegrity(asSMapNode *node); + +protected: + void BalanceInsert(asSMapNode *node); + void BalanceErase(asSMapNode *child, asSMapNode *parent); + + int EraseAll(asSMapNode *node); + int RotateLeft(asSMapNode *node); + int RotateRight(asSMapNode *node); + + asSMapNode *root; + asSMapNode dummy; + + int count; +}; + +//--------------------------------------------------------------------------- +// Implementation + +// Properties of a Red-Black Tree +// +// 1. The root is always black +// 2. All single paths from the root to leafs +// contain the same amount of black nodes +// 3. No red node can have a red node as parent + +#define ISRED(x) ((x != 0) && (x)->isRed) +#define ISBLACK(x) (!ISRED(x)) + +template struct asSMapNode +{ + asSMapNode() {parent = 0; left = 0; right = 0; isRed = true;} + + asSMapNode *parent; + asSMapNode *left; + asSMapNode *right; + bool isRed; + + KEY key; + VAL value; +}; + +template +asCMap::asCMap() +{ + root = 0; + count = 0; +} + +template +asCMap::~asCMap() +{ + EraseAll(); +} + +template +void asCMap::EraseAll() +{ + EraseAll(root); + root = 0; +} + +template +int asCMap::EraseAll(asSMapNode *p) +{ + if( p == 0 ) return -1; + + EraseAll( p->left ); + EraseAll( p->right ); + + typedef asSMapNode node_t; + asDELETE(p,node_t); + + count--; + + return 0; +} + +template +int asCMap::GetCount() +{ + return count; +} + +template +int asCMap::Insert(const KEY &key, const VAL &value) +{ + typedef asSMapNode node_t; + asSMapNode *nnode = asNEW(node_t); + nnode->key = key; + nnode->value = value; + + // Insert the node + if( root == 0 ) + root = nnode; + else + { + asSMapNode *p = root; + for(;;) + { + if( nnode->key < p->key ) + { + if( p->left == 0 ) + { + nnode->parent = p; + p->left = nnode; + break; + } + else + p = p->left; + } + else + { + if( p->right == 0 ) + { + nnode->parent = p; + p->right = nnode; + break; + } + else + p = p->right; + } + } + } + + BalanceInsert(nnode); + + count++; + + return 0; +} + +template +void asCMap::BalanceInsert(asSMapNode *node) +{ + // The node, that is red, can't have a red parent + while( node != root && node->parent->isRed ) + { + // Check color of uncle + if( node->parent == node->parent->parent->left ) + { + asSMapNode *uncle = node->parent->parent->right; + if( ISRED(uncle) ) + { + // B + // R R + // N + + // Change color on parent, uncle, and grand parent + node->parent->isRed = false; + uncle->isRed = false; + node->parent->parent->isRed = true; + + // Continue balancing from grand parent + node = node->parent->parent; + } + else + { + // B + // R B + // N + + if( node == node->parent->right ) + { + // Make the node a left child + node = node->parent; + RotateLeft(node); + } + + // Change color on parent and grand parent + // Then rotate grand parent to the right + node->parent->isRed = false; + node->parent->parent->isRed = true; + RotateRight(node->parent->parent); + } + } + else + { + asSMapNode *uncle = node->parent->parent->left; + if( ISRED(uncle) ) + { + // B + // R R + // N + + // Change color on parent, uncle, and grand parent + // Continue balancing from grand parent + node->parent->isRed = false; + uncle->isRed = false; + node = node->parent->parent; + node->isRed = true; + } + else + { + // B + // B R + // N + + if( node == node->parent->left ) + { + // Make the node a right child + node = node->parent; + RotateRight(node); + } + + // Change color on parent and grand parent + // Then rotate grand parent to the right + node->parent->isRed = false; + node->parent->parent->isRed = true; + RotateLeft(node->parent->parent); + } + } + } + + root->isRed = false; +} + +// For debugging purposes only +template +int asCMap::CheckIntegrity(asSMapNode *node) +{ + if( node == 0 ) + { + if( root == 0 ) + return 0; + else if( ISRED(root) ) + return -1; + else + node = root; + } + + int left = 0, right = 0; + if( node->left ) + left = CheckIntegrity(node->left); + if( node->right ) + right = CheckIntegrity(node->right); + + if( left != right || left == -1 ) + return -1; + + if( ISBLACK(node) ) + return left+1; + + return left; +} + +// Returns true if successful +template +bool asCMap::MoveTo(asSMapNode **out, const KEY &key) +{ + asSMapNode *p = root; + while( p ) + { + if( key < p->key ) + p = p->left; + else if( key == p->key ) + { + *out = p; + return true; + } + else + p = p->right; + } + + *out = 0; + return false; +} + +template +void asCMap::Erase(asSMapNode *cursor) +{ + if( cursor == 0 ) return; + + asSMapNode *node = cursor; + + //--------------------------------------------------- + // Choose the node that will replace the erased one + asSMapNode *remove; + if( node->left == 0 || node->right == 0 ) + remove = node; + else + { + remove = node->right; + while( remove->left ) remove = remove->left; + } + + //-------------------------------------------------- + // Remove the node + asSMapNode *child; + if( remove->left ) + child = remove->left; + else + child = remove->right; + + if( child ) child->parent = remove->parent; + if( remove->parent ) + { + if( remove == remove->parent->left ) + remove->parent->left = child; + else + remove->parent->right = child; + } + else + root = child; + + // If we remove a black node we must make sure the tree is balanced + if( ISBLACK(remove) ) + BalanceErase(child, remove->parent); + + //---------------------------------------- + // Replace the erased node with the removed one + if( remove != node ) + { + if( node->parent ) + { + if( node->parent->left == node ) + node->parent->left = remove; + else + node->parent->right = remove; + } + else + root = remove; + + remove->isRed = node->isRed; + remove->parent = node->parent; + + remove->left = node->left; + if( remove->left ) remove->left->parent = remove; + remove->right = node->right; + if( remove->right ) remove->right->parent = remove; + } + + typedef asSMapNode node_t; + asDELETE(node,node_t); + + count--; +} + +// Call method only if removed node was black +// child is the child of the removed node +template +void asCMap::BalanceErase(asSMapNode *child, asSMapNode *parent) +{ + // If child is red + // Color child black + // Terminate + + // These tests assume brother is to the right. + + // 1. Brother is red + // Color parent red and brother black + // Rotate parent left + // Transforms to 2b + // 2a. Parent and brother is black, brother's children are black + // Color brother red + // Continue test with parent as child + // 2b. Parent is red, brother is black, brother's children are black + // Color parent black and brother red + // Terminate + // 3. Brother is black, and brother's left is red and brother's right is black + // Color brother red and brother's left black + // Rotate brother to right + // Transforms to 4. + // 4. Brother is black, brother's right is red + // Color brother's right black + // Color brother to color of parent + // Color parent black + // Rotate parent left + // Terminate + + while( child != root && ISBLACK(child) ) + { + if( child == parent->left ) + { + asSMapNode *brother = parent->right; + + // Case 1 + if( ISRED(brother) ) + { + brother->isRed = false; + parent->isRed = true; + RotateLeft(parent); + brother = parent->right; + } + + // Case 2 + if( brother == 0 ) break; + if( ISBLACK(brother->left) && ISBLACK(brother->right) ) + { + // Case 2b + if( ISRED(parent) ) + { + parent->isRed = false; + brother->isRed = true; + break; + } + + brother->isRed = true; + child = parent; + parent = child->parent; + } + else + { + // Case 3 + if( ISBLACK(brother->right) ) + { + brother->left->isRed = false; + brother->isRed = true; + RotateRight(brother); + brother = parent->right; + } + + // Case 4 + brother->isRed = parent->isRed; + parent->isRed = false; + brother->right->isRed = false; + RotateLeft(parent); + break; + } + } + else + { + asSMapNode *brother = parent->left; + + // Case 1 + if( ISRED(brother) ) + { + brother->isRed = false; + parent->isRed = true; + RotateRight(parent); + brother = parent->left; + } + + // Case 2 + if( brother == 0 ) break; + if( ISBLACK(brother->left) && ISBLACK(brother->right) ) + { + // Case 2b + if( ISRED(parent) ) + { + parent->isRed = false; + brother->isRed = true; + break; + } + + brother->isRed = true; + child = parent; + parent = child->parent; + } + else + { + // Case 3 + if( ISBLACK(brother->left) ) + { + brother->right->isRed = false; + brother->isRed = true; + RotateLeft(brother); + brother = parent->left; + } + + // Case 4 + brother->isRed = parent->isRed; + parent->isRed = false; + brother->left->isRed = false; + RotateRight(parent); + break; + } + } + } + + if( child ) + child->isRed = false; +} + +template +int asCMap::RotateRight(asSMapNode *node) +{ + // P L // + // / \ / \ // + // L R => Ll P // + // / \ / \ // + // Ll Lr Lr R // + + if( node->left == 0 ) return -1; + + asSMapNode *left = node->left; + + // Update parent + if( node->parent ) + { + asSMapNode *parent = node->parent; + if( parent->left == node ) + parent->left = left; + else + parent->right = left; + + left->parent = parent; + } + else + { + root = left; + left->parent = 0; + } + + // Move left's right child to node's left child + node->left = left->right; + if( node->left ) node->left->parent = node; + + // Put node as left's right child + left->right = node; + node->parent = left; + + return 0; +} + +template +int asCMap::RotateLeft(asSMapNode *node) +{ + // P R // + // / \ / \ // + // L R => P Rr // + // / \ / \ // + // Rl Rr L Rl // + + if( node->right == 0 ) return -1; + + asSMapNode *right = node->right; + + // Update parent + if( node->parent ) + { + asSMapNode *parent = node->parent; + if( parent->right == node ) + parent->right = right; + else + parent->left = right; + + right->parent = parent; + } + else + { + root = right; + right->parent = 0; + } + + // Move right's left child to node's right child + node->right = right->left; + if( node->right ) node->right->parent = node; + + // Put node as right's left child + right->left = node; + node->parent = right; + + return 0; +} + +template +const VAL &asCMap::GetValue(const asSMapNode *cursor) const +{ + if( cursor == 0 ) + return dummy.value; + + return cursor->value; +} + +template +VAL &asCMap::GetValue(asSMapNode *cursor) +{ + if( cursor == 0 ) + return dummy.value; + + return cursor->value; +} + +template +const KEY &asCMap::GetKey(const asSMapNode *cursor) const +{ + if( cursor == 0 ) + return dummy.key; + + return cursor->key; +} + +template +bool asCMap::MoveFirst(asSMapNode **out) +{ + *out = root; + if( root == 0 ) return false; + + while( (*out)->left ) + *out = (*out)->left; + + return true; +} + +template +bool asCMap::MoveLast(asSMapNode **out) +{ + *out = root; + if( root == 0 ) return false; + + while( (*out)->right ) + *out = (*out)->right; + + return true; +} + +template +bool asCMap::MoveNext(asSMapNode **out, asSMapNode *cursor) +{ + if( cursor == 0 ) + { + *out = 0; + return false; + } + + if( cursor->right == 0 ) + { + // Move upwards until we find a parent node to the right + while( cursor->parent && cursor->parent->right == cursor ) + cursor = cursor->parent; + + cursor = cursor->parent; + *out = cursor; + if( cursor == 0 ) + return false; + + return true; + } + + cursor = cursor->right; + while( cursor->left ) + cursor = cursor->left; + + *out = cursor; + return true; +} + +template +bool asCMap::MovePrev(asSMapNode **out, asSMapNode *cursor) +{ + if( cursor == 0 ) + { + *out = 0; + return false; + } + + if( cursor->left == 0 ) + { + // Move upwards until we find a parent node to the left + while( cursor->parent && cursor->parent->left == cursor ) + cursor = cursor->parent; + + cursor = cursor->parent; + + *out = cursor; + if( cursor == 0 ) + return false; + + return true; + } + + cursor = cursor->left; + while( cursor->right ) + cursor = cursor->right; + + *out = cursor; + return true; +} + + + + +#endif + diff --git a/AngelScript/source/as_memory.cpp b/AngelScript/source/as_memory.cpp new file mode 100644 index 000000000..60c2bac2f --- /dev/null +++ b/AngelScript/source/as_memory.cpp @@ -0,0 +1,163 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_memory.cpp +// +// Overload the default memory management functions so that we +// can let the application decide how to do it. +// + +#include + +#include "as_config.h" +#include "as_memory.h" +#include "as_scriptnode.h" +#include "as_bytecode.h" + +BEGIN_AS_NAMESPACE + +// By default we'll use the standard memory management functions +asALLOCFUNC_t userAlloc = malloc; +asFREEFUNC_t userFree = free; + +extern "C" +{ + +int asSetGlobalMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc) +{ + userAlloc = allocFunc; + userFree = freeFunc; + + return 0; +} + +int asResetGlobalMemoryFunctions() +{ + asThreadCleanup(); + + userAlloc = malloc; + userFree = free; + + return 0; +} + +} // extern "C" + +asCMemoryMgr::asCMemoryMgr() +{ +} + +asCMemoryMgr::~asCMemoryMgr() +{ + FreeUnusedMemory(); +} + +void asCMemoryMgr::FreeUnusedMemory() +{ + // It's necessary to protect the scriptNodePool from multiple + // simultaneous accesses, as the parser is used by several methods + // that can be executed simultaneously. + ENTERCRITICALSECTION(cs); + + int n; + for( n = 0; n < (signed)scriptNodePool.GetLength(); n++ ) + userFree(scriptNodePool[n]); + scriptNodePool.Allocate(0, false); + + LEAVECRITICALSECTION(cs); + + // The engine already protects against multiple threads + // compiling scripts simultaneously so this pool doesn't have + // to be protected again. + for( n = 0; n < (signed)byteInstructionPool.GetLength(); n++ ) + userFree(byteInstructionPool[n]); + byteInstructionPool.Allocate(0, false); +} + +void *asCMemoryMgr::AllocScriptNode() +{ + ENTERCRITICALSECTION(cs); + + if( scriptNodePool.GetLength() ) + { + void *tRet = scriptNodePool.PopLast(); + LEAVECRITICALSECTION(cs); + return tRet; + } + + LEAVECRITICALSECTION(cs); + +#if defined(AS_DEBUG) + return ((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(asCScriptNode), __FILE__, __LINE__); +#else + return userAlloc(sizeof(asCScriptNode)); +#endif +} + +void asCMemoryMgr::FreeScriptNode(void *ptr) +{ + ENTERCRITICALSECTION(cs); + + // Pre allocate memory for the array to avoid slow growth + if( scriptNodePool.GetLength() == 0 ) + scriptNodePool.Allocate(100, 0); + + scriptNodePool.PushLast(ptr); + + LEAVECRITICALSECTION(cs); +} + +void *asCMemoryMgr::AllocByteInstruction() +{ + if( byteInstructionPool.GetLength() ) + return byteInstructionPool.PopLast(); + +#if defined(AS_DEBUG) + return ((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(cByteInstruction), __FILE__, __LINE__); +#else + return userAlloc(sizeof(cByteInstruction)); +#endif +} + +void asCMemoryMgr::FreeByteInstruction(void *ptr) +{ + // Pre allocate memory for the array to avoid slow growth + if( byteInstructionPool.GetLength() == 0 ) + byteInstructionPool.Allocate(100, 0); + + byteInstructionPool.PushLast(ptr); +} + +END_AS_NAMESPACE + + + diff --git a/AngelScript/source/as_memory.h b/AngelScript/source/as_memory.h new file mode 100644 index 000000000..7708ebae4 --- /dev/null +++ b/AngelScript/source/as_memory.h @@ -0,0 +1,103 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_memory.h +// +// Overload the default memory management functions so that we +// can let the application decide how to do it. +// + + + +#ifndef AS_MEMORY_H +#define AS_MEMORY_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +extern asALLOCFUNC_t userAlloc; +extern asFREEFUNC_t userFree; + +// We don't overload the new operator as that would affect the application as well + +#ifndef AS_DEBUG + + #define asNEW(x) new(userAlloc(sizeof(x))) x + #define asDELETE(ptr,x) {void *tmp = ptr; (ptr)->~x(); userFree(tmp);} + + #define asNEWARRAY(x,cnt) (x*)userAlloc(sizeof(x)*cnt) + #define asDELETEARRAY(ptr) userFree(ptr) + +#else + + typedef void *(*asALLOCFUNCDEBUG_t)(size_t, const char *, unsigned int); + + #define asNEW(x) new(((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(x), __FILE__, __LINE__)) x + #define asDELETE(ptr,x) {void *tmp = ptr; (ptr)->~x(); userFree(tmp);} + + #define asNEWARRAY(x,cnt) (x*)((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(x)*cnt, __FILE__, __LINE__) + #define asDELETEARRAY(ptr) userFree(ptr) + +#endif + +END_AS_NAMESPACE + +#include +#include "as_criticalsection.h" +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +class asCMemoryMgr +{ +public: + asCMemoryMgr(); + ~asCMemoryMgr(); + + void FreeUnusedMemory(); + + void *AllocScriptNode(); + void FreeScriptNode(void *ptr); + + void *AllocByteInstruction(); + void FreeByteInstruction(void *ptr); + +protected: + DECLARECRITICALSECTION(cs); + asCArray scriptNodePool; + asCArray byteInstructionPool; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_module.cpp b/AngelScript/source/as_module.cpp new file mode 100644 index 000000000..6399a85b7 --- /dev/null +++ b/AngelScript/source/as_module.cpp @@ -0,0 +1,1358 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_module.cpp +// +// A class that holds a script module +// + +#include "as_config.h" +#include "as_module.h" +#include "as_builder.h" +#include "as_context.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +// internal +asCModule::asCModule(const char *name, asCScriptEngine *engine) +{ + this->name = name; + this->engine = engine; + + builder = 0; + isGlobalVarInitialized = false; +} + +// internal +asCModule::~asCModule() +{ + InternalReset(); + + if( builder ) + { + asDELETE(builder,asCBuilder); + builder = 0; + } + + // Remove the module from the engine + if( engine ) + { + if( engine->lastModule == this ) + engine->lastModule = 0; + + engine->scriptModules.RemoveValue(this); + } +} + +// interface +asIScriptEngine *asCModule::GetEngine() +{ + return engine; +} + +// interface +void asCModule::SetName(const char *name) +{ + this->name = name; +} + +// interface +const char *asCModule::GetName() +{ + return name.AddressOf(); +} + +// interface +int asCModule::AddScriptSection(const char *name, const char *code, size_t codeLength, int lineOffset) +{ + if( !builder ) + builder = asNEW(asCBuilder)(engine, this); + + builder->AddCode(name, code, (int)codeLength, lineOffset, (int)engine->GetScriptSectionNameIndex(name ? name : ""), engine->ep.copyScriptSections); + + return asSUCCESS; +} + +// internal +void asCModule::JITCompile() +{ + for (unsigned int i = 0; i < scriptFunctions.GetLength(); i++) + { + scriptFunctions[i]->JITCompile(); + } +} + +// interface +int asCModule::Build() +{ + // Only one thread may build at one time + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + engine->PrepareEngine(); + if( engine->configFailed ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_INVALID_CONFIGURATION); + engine->BuildCompleted(); + return asINVALID_CONFIGURATION; + } + + InternalReset(); + + if( !builder ) + { + engine->BuildCompleted(); + return asSUCCESS; + } + + // Compile the script + r = builder->Build(); + asDELETE(builder,asCBuilder); + builder = 0; + + if( r < 0 ) + { + // Reset module again + InternalReset(); + + engine->BuildCompleted(); + return r; + } + + JITCompile(); + + engine->PrepareEngine(); + engine->BuildCompleted(); + + // Initialize global variables + if( r >= 0 && engine->ep.initGlobalVarsAfterBuild ) + r = ResetGlobalVars(); + + return r; +} + +// interface +int asCModule::ResetGlobalVars() +{ + if( isGlobalVarInitialized ) + CallExit(); + + // TODO: The application really should do this manually through a context + // otherwise it cannot properly handle script exceptions that may be + // thrown by object initializations. + return CallInit(); +} + +// interface +int asCModule::GetFunctionIdByIndex(int index) +{ + if( index < 0 || index >= (int)scriptFunctions.GetLength() ) + return asNO_FUNCTION; + + return scriptFunctions[index]->id; +} + +// internal +int asCModule::CallInit() +{ + if( isGlobalVarInitialized ) + return asERROR; + + // Each global variable needs to be cleared individually + asUINT n; + for( n = 0; n < scriptGlobals.GetLength(); n++ ) + { + if( scriptGlobals[n] ) + { + memset(scriptGlobals[n]->GetAddressOfValue(), 0, sizeof(asDWORD)*scriptGlobals[n]->type.GetSizeOnStackDWords()); + } + } + + // Call the init function for each of the global variables + asIScriptContext *ctx = 0; + int r = 0; + for( n = 0; n < scriptGlobals.GetLength() && r == 0; n++ ) + { + if( scriptGlobals[n]->initFunc ) + { + if( ctx == 0 ) + { + r = engine->CreateContext(&ctx, true); + if( r < 0 ) + break; + } + + r = ctx->Prepare(scriptGlobals[n]->initFunc->id); + if( r >= 0 ) + r = ctx->Execute(); + } + } + + if( ctx ) + { + ctx->Release(); + ctx = 0; + } + + // Even if the initialization failed we need to set the + // flag that the variables have been initialized, otherwise + // the module won't free those variables that really were + // initialized. + isGlobalVarInitialized = true; + + if( r != asEXECUTION_FINISHED ) + return asINIT_GLOBAL_VARS_FAILED; + + return asSUCCESS; +} + +// internal +void asCModule::CallExit() +{ + if( !isGlobalVarInitialized ) return; + + for( size_t n = 0; n < scriptGlobals.GetLength(); n++ ) + { + if( scriptGlobals[n]->type.IsObject() ) + { + void *obj = *(void**)scriptGlobals[n]->GetAddressOfValue(); + if( obj ) + { + asCObjectType *ot = scriptGlobals[n]->type.GetObjectType(); + + if( ot->beh.release ) + engine->CallObjectMethod(obj, ot->beh.release); + else + { + if( ot->beh.destruct ) + engine->CallObjectMethod(obj, ot->beh.destruct); + + engine->CallFree(obj); + } + } + } + } + + isGlobalVarInitialized = false; +} + +// internal +void asCModule::InternalReset() +{ + CallExit(); + + size_t n; + + // Release all global functions + for( n = 0; n < globalFunctions.GetLength(); n++ ) + { + if( globalFunctions[n] ) + globalFunctions[n]->Release(); + } + globalFunctions.SetLength(0); + + // First release all compiled functions + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + { + if( scriptFunctions[n] ) + { + // Remove the module reference in the functions + scriptFunctions[n]->module = 0; + scriptFunctions[n]->Release(); + } + } + scriptFunctions.SetLength(0); + + // Release the global properties declared in the module + for( n = 0; n < scriptGlobals.GetLength(); n++ ) + scriptGlobals[n]->Release(); + scriptGlobals.SetLength(0); + + UnbindAllImportedFunctions(); + + // Free bind information + for( n = 0; n < bindInformations.GetLength(); n++ ) + { + engine->importedFunctions[bindInformations[n]->importedFunctionSignature->id & 0xFFFF] = 0 ; + + asDELETE(bindInformations[n]->importedFunctionSignature, asCScriptFunction); + asDELETE(bindInformations[n], sBindInfo); + } + bindInformations.SetLength(0); + + // Free declared types, including classes, typedefs, and enums + for( n = 0; n < classTypes.GetLength(); n++ ) + classTypes[n]->Release(); + classTypes.SetLength(0); + for( n = 0; n < enumTypes.GetLength(); n++ ) + enumTypes[n]->Release(); + enumTypes.SetLength(0); + for( n = 0; n < typeDefs.GetLength(); n++ ) + typeDefs[n]->Release(); + typeDefs.SetLength(0); +} + +// interface +int asCModule::GetFunctionIdByName(const char *name) +{ + // TODO: optimize: Improve linear search + // Find the function id + int id = -1; + for( size_t n = 0; n < globalFunctions.GetLength(); n++ ) + { + if( globalFunctions[n]->name == name ) + { + if( id == -1 ) + id = globalFunctions[n]->id; + else + return asMULTIPLE_FUNCTIONS; + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + +// interface +int asCModule::GetImportedFunctionCount() +{ + return (int)bindInformations.GetLength(); +} + +// interface +int asCModule::GetImportedFunctionIndexByDecl(const char *decl) +{ + asCBuilder bld(engine, this); + + asCScriptFunction func(engine, this, -1); + bld.ParseFunctionDeclaration(0, decl, &func, false); + + // TODO: optimize: Improve linear search + // Search script functions for matching interface + int id = -1; + for( asUINT n = 0; n < bindInformations.GetLength(); ++n ) + { + if( func.name == bindInformations[n]->importedFunctionSignature->name && + func.returnType == bindInformations[n]->importedFunctionSignature->returnType && + func.parameterTypes.GetLength() == bindInformations[n]->importedFunctionSignature->parameterTypes.GetLength() ) + { + bool match = true; + for( asUINT p = 0; p < func.parameterTypes.GetLength(); ++p ) + { + if( func.parameterTypes[p] != bindInformations[n]->importedFunctionSignature->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( id == -1 ) + id = n; + else + return asMULTIPLE_FUNCTIONS; + } + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + +// interface +int asCModule::GetFunctionCount() +{ + return (int)globalFunctions.GetLength(); +} + +// interface +int asCModule::GetFunctionIdByDecl(const char *decl) +{ + asCBuilder bld(engine, this); + + asCScriptFunction func(engine, this, -1); + int r = bld.ParseFunctionDeclaration(0, decl, &func, false); + if( r < 0 ) + return asINVALID_DECLARATION; + + // TODO: optimize: Improve linear search + // Search script functions for matching interface + int id = -1; + for( size_t n = 0; n < globalFunctions.GetLength(); ++n ) + { + if( globalFunctions[n]->objectType == 0 && + func.name == globalFunctions[n]->name && + func.returnType == globalFunctions[n]->returnType && + func.parameterTypes.GetLength() == globalFunctions[n]->parameterTypes.GetLength() ) + { + bool match = true; + for( size_t p = 0; p < func.parameterTypes.GetLength(); ++p ) + { + if( func.parameterTypes[p] != globalFunctions[n]->parameterTypes[p] ) + { + match = false; + break; + } + } + + if( match ) + { + if( id == -1 ) + id = globalFunctions[n]->id; + else + return asMULTIPLE_FUNCTIONS; + } + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + +// interface +int asCModule::GetGlobalVarCount() +{ + return (int)scriptGlobals.GetLength(); +} + +// interface +int asCModule::GetGlobalVarIndexByName(const char *name) +{ + // Find the global var id + int id = -1; + for( size_t n = 0; n < scriptGlobals.GetLength(); n++ ) + { + if( scriptGlobals[n]->name == name ) + { + id = (int)n; + break; + } + } + + if( id == -1 ) return asNO_GLOBAL_VAR; + + return id; +} + +// interface +int asCModule::RemoveGlobalVar(int index) +{ + if( index < 0 || index >= (int)scriptGlobals.GetLength() ) + return asINVALID_ARG; + + scriptGlobals[index]->Release(); + scriptGlobals.RemoveIndex(index); + + return 0; +} + +// interface +asIScriptFunction *asCModule::GetFunctionDescriptorByIndex(int index) +{ + if( index < 0 || index >= (int)globalFunctions.GetLength() ) + return 0; + + return globalFunctions[index]; +} + +// interface +asIScriptFunction *asCModule::GetFunctionDescriptorById(int funcId) +{ + return engine->GetFunctionDescriptorById(funcId); +} + +// interface +int asCModule::GetGlobalVarIndexByDecl(const char *decl) +{ + asCBuilder bld(engine, this); + + asCObjectProperty gvar; + bld.ParseVariableDeclaration(decl, &gvar); + + // TODO: optimize: Improve linear search + // Search script functions for matching interface + int id = -1; + for( size_t n = 0; n < scriptGlobals.GetLength(); ++n ) + { + if( gvar.name == scriptGlobals[n]->name && + gvar.type == scriptGlobals[n]->type ) + { + id = (int)n; + break; + } + } + + if( id == -1 ) return asNO_GLOBAL_VAR; + + return id; +} + +// interface +void *asCModule::GetAddressOfGlobalVar(int index) +{ + if( index < 0 || index >= (int)scriptGlobals.GetLength() ) + return 0; + + // TODO: value types shouldn't need dereferencing + // For object variables it's necessary to dereference the pointer to get the address of the value + if( scriptGlobals[index]->type.IsObject() && !scriptGlobals[index]->type.IsObjectHandle() ) + return *(void**)(scriptGlobals[index]->GetAddressOfValue()); + + return (void*)(scriptGlobals[index]->GetAddressOfValue()); +} + +// interface +const char *asCModule::GetGlobalVarDeclaration(int index) +{ + if( index < 0 || index >= (int)scriptGlobals.GetLength() ) + return 0; + + asCGlobalProperty *prop = scriptGlobals[index]; + + asASSERT(threadManager); + asCString *tempString = &threadManager->GetLocalData()->string; + *tempString = prop->type.Format(); + *tempString += " " + prop->name; + + return tempString->AddressOf(); +} + +// interface +const char *asCModule::GetGlobalVarName(int index) +{ + if( index < 0 || index >= (int)scriptGlobals.GetLength() ) + return 0; + + return scriptGlobals[index]->name.AddressOf(); +} + +// interface +// TODO: If the typeId ever encodes the const flag, then the isConst parameter should be removed +int asCModule::GetGlobalVarTypeId(int index, bool *isConst) +{ + if( index < 0 || index >= (int)scriptGlobals.GetLength() ) + return asINVALID_ARG; + + if( isConst ) + *isConst = scriptGlobals[index]->type.IsReadOnly(); + + return engine->GetTypeIdFromDataType(scriptGlobals[index]->type); +} + +// interface +int asCModule::GetObjectTypeCount() +{ + return (int)classTypes.GetLength(); +} + +// interface +asIObjectType *asCModule::GetObjectTypeByIndex(asUINT index) +{ + if( index >= classTypes.GetLength() ) + return 0; + + return classTypes[index]; +} + +// interface +int asCModule::GetTypeIdByDecl(const char *decl) +{ + asCDataType dt; + asCBuilder bld(engine, this); + int r = bld.ParseDataType(decl, &dt); + if( r < 0 ) + return asINVALID_TYPE; + + return engine->GetTypeIdFromDataType(dt); +} + +// interface +int asCModule::GetEnumCount() +{ + return (int)enumTypes.GetLength(); +} + +// interface +const char *asCModule::GetEnumByIndex(asUINT index, int *enumTypeId) +{ + if( index >= enumTypes.GetLength() ) + return 0; + + if( enumTypeId ) + *enumTypeId = GetTypeIdByDecl(enumTypes[index]->name.AddressOf()); + + return enumTypes[index]->name.AddressOf(); +} + +// interface +int asCModule::GetEnumValueCount(int enumTypeId) +{ + const asCDataType *dt = engine->GetDataTypeFromTypeId(enumTypeId); + asCObjectType *t = dt->GetObjectType(); + if( t == 0 || !(t->GetFlags() & asOBJ_ENUM) ) + return asINVALID_TYPE; + + return (int)t->enumValues.GetLength(); +} + +// interface +const char *asCModule::GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) +{ + const asCDataType *dt = engine->GetDataTypeFromTypeId(enumTypeId); + asCObjectType *t = dt->GetObjectType(); + if( t == 0 || !(t->GetFlags() & asOBJ_ENUM) ) + return 0; + + if( index >= t->enumValues.GetLength() ) + return 0; + + if( outValue ) + *outValue = t->enumValues[index]->value; + + return t->enumValues[index]->name.AddressOf(); +} + +// interface +int asCModule::GetTypedefCount() +{ + return (int)typeDefs.GetLength(); +} + +// interface +const char *asCModule::GetTypedefByIndex(asUINT index, int *typeId) +{ + if( index >= typeDefs.GetLength() ) + return 0; + + if( typeId ) + *typeId = GetTypeIdByDecl(typeDefs[index]->name.AddressOf()); + + return typeDefs[index]->name.AddressOf(); +} + + +// internal +int asCModule::GetNextImportedFunctionId() +{ + return FUNC_IMPORTED | (asUINT)engine->importedFunctions.GetLength(); +} + +// internal +int asCModule::AddScriptFunction(int sectionIdx, int id, const char *name, const asCDataType &returnType, asCDataType *params, asETypeModifiers *inOutFlags, int paramCount, bool isInterface, asCObjectType *objType, bool isConstMethod, bool isGlobalFunction) +{ + asASSERT(id >= 0); + + // Store the function information + asCScriptFunction *func = asNEW(asCScriptFunction)(engine, this, isInterface ? asFUNC_INTERFACE : asFUNC_SCRIPT); + func->name = name; + func->id = id; + func->returnType = returnType; + func->scriptSectionIdx = sectionIdx; + for( int n = 0; n < paramCount; n++ ) + { + func->parameterTypes.PushLast(params[n]); + func->inOutFlags.PushLast(inOutFlags[n]); + } + func->objectType = objType; + func->isReadOnly = isConstMethod; + + // The script function's refCount was initialized to 1 + scriptFunctions.PushLast(func); + engine->SetScriptFunction(func); + + // Compute the signature id + if( objType ) + func->ComputeSignatureId(); + + // Add reference + if( isGlobalFunction ) + { + globalFunctions.PushLast(func); + func->AddRef(); + } + + return 0; +} + +// internal +int asCModule::AddScriptFunction(asCScriptFunction *func) +{ + scriptFunctions.PushLast(func); + func->AddRef(); + engine->SetScriptFunction(func); + + return 0; +} + + + + +// internal +int asCModule::AddImportedFunction(int id, const char *name, const asCDataType &returnType, asCDataType *params, asETypeModifiers *inOutFlags, int paramCount, const asCString &moduleName) +{ + asASSERT(id >= 0); + + // Store the function information + asCScriptFunction *func = asNEW(asCScriptFunction)(engine, this, asFUNC_IMPORTED); + func->name = name; + func->id = id; + func->returnType = returnType; + for( int n = 0; n < paramCount; n++ ) + { + func->parameterTypes.PushLast(params[n]); + func->inOutFlags.PushLast(inOutFlags[n]); + } + func->objectType = 0; + + sBindInfo *info = asNEW(sBindInfo); + info->importedFunctionSignature = func; + info->boundFunctionId = -1; + info->importFromModule = moduleName; + bindInformations.PushLast(info); + + // Add the info to the array in the engine + engine->importedFunctions.PushLast(info); + + return 0; +} + +// internal +asCScriptFunction *asCModule::GetImportedFunction(int index) +{ + return bindInformations[index]->importedFunctionSignature; +} + +// interface +int asCModule::BindImportedFunction(int index, int sourceId) +{ + // First unbind the old function + int r = UnbindImportedFunction(index); + if( r < 0 ) return r; + + // Must verify that the interfaces are equal + asCScriptFunction *dst = GetImportedFunction(index); + if( dst == 0 ) return asNO_FUNCTION; + + asCScriptFunction *src = engine->GetScriptFunction(sourceId); + if( src == 0 ) + return asNO_FUNCTION; + + // Verify return type + if( dst->returnType != src->returnType ) + return asINVALID_INTERFACE; + + if( dst->parameterTypes.GetLength() != src->parameterTypes.GetLength() ) + return asINVALID_INTERFACE; + + for( size_t n = 0; n < dst->parameterTypes.GetLength(); ++n ) + { + if( dst->parameterTypes[n] != src->parameterTypes[n] ) + return asINVALID_INTERFACE; + } + + bindInformations[index]->boundFunctionId = sourceId; + engine->scriptFunctions[sourceId]->AddRef(); + + return asSUCCESS; +} + +// interface +int asCModule::UnbindImportedFunction(int index) +{ + if( index < 0 || index > (int)bindInformations.GetLength() ) + return asINVALID_ARG; + + // Remove reference to old module + int oldFuncID = bindInformations[index]->boundFunctionId; + if( oldFuncID != -1 ) + { + bindInformations[index]->boundFunctionId = -1; + engine->scriptFunctions[oldFuncID]->Release(); + } + + return asSUCCESS; +} + +// interface +const char *asCModule::GetImportedFunctionDeclaration(int index) +{ + asCScriptFunction *func = GetImportedFunction(index); + if( func == 0 ) return 0; + + asASSERT(threadManager); + asCString *tempString = &threadManager->GetLocalData()->string; + *tempString = func->GetDeclarationStr(); + + return tempString->AddressOf(); +} + +// interface +const char *asCModule::GetImportedFunctionSourceModule(int index) +{ + if( index >= (int)bindInformations.GetLength() ) + return 0; + + return bindInformations[index]->importFromModule.AddressOf(); +} + +// inteface +int asCModule::BindAllImportedFunctions() +{ + bool notAllFunctionsWereBound = false; + + // Bind imported functions + int c = GetImportedFunctionCount(); + for( int n = 0; n < c; ++n ) + { + asCScriptFunction *func = GetImportedFunction(n); + if( func == 0 ) return asERROR; + + asCString str = func->GetDeclarationStr(); + + // Get module name from where the function should be imported + const char *moduleName = GetImportedFunctionSourceModule(n); + if( moduleName == 0 ) return asERROR; + + asCModule *srcMod = engine->GetModule(moduleName, false); + int funcId = -1; + if( srcMod ) + funcId = srcMod->GetFunctionIdByDecl(str.AddressOf()); + + if( funcId < 0 ) + notAllFunctionsWereBound = true; + else + { + if( BindImportedFunction(n, funcId) < 0 ) + notAllFunctionsWereBound = true; + } + } + + if( notAllFunctionsWereBound ) + return asCANT_BIND_ALL_FUNCTIONS; + + return asSUCCESS; +} + +// interface +int asCModule::UnbindAllImportedFunctions() +{ + int c = GetImportedFunctionCount(); + for( int n = 0; n < c; ++n ) + UnbindImportedFunction(n); + + return asSUCCESS; +} + +// internal +asCObjectType *asCModule::GetObjectType(const char *type) +{ + size_t n; + + // TODO: optimize: Improve linear search + for( n = 0; n < classTypes.GetLength(); n++ ) + if( classTypes[n]->name == type ) + return classTypes[n]; + + for( n = 0; n < enumTypes.GetLength(); n++ ) + if( enumTypes[n]->name == type ) + return enumTypes[n]; + + for( n = 0; n < typeDefs.GetLength(); n++ ) + if( typeDefs[n]->name == type ) + return typeDefs[n]; + + return 0; +} + +// internal +asCGlobalProperty *asCModule::AllocateGlobalProperty(const char *name, const asCDataType &dt) +{ + asCGlobalProperty *prop = engine->AllocateGlobalProperty(); + prop->name = name; + + // Allocate the memory for this property based on its type + prop->type = dt; + prop->AllocateMemory(); + + // Store the variable in the module scope (the reference count is already set to 1) + scriptGlobals.PushLast(prop); + + return prop; +} + +// internal +void asCModule::ResolveInterfaceIds() +{ + // For each of the interfaces declared in the script find identical interface in the engine. + // If an identical interface was found then substitute the current id for the identical interface's id, + // then remove this interface declaration. If an interface was modified by the declaration, then + // retry the detection of identical interface for it since it may now match another. + + // For an interface to be equal to another the name and methods must match. If the interface + // references another interface, then that must be checked as well, which can lead to circular references. + + // Example: + // + // interface A { void f(B@); } + // interface B { void f(A@); void f(C@); } + // interface C { void f(A@); } + // + // A1 equals A2 if and only if B1 equals B2 + // B1 equals B2 if and only if A1 equals A2 and C1 equals C2 + // C1 equals C2 if and only if A1 equals A2 + + + unsigned int i; + + // The interface can only be equal to interfaces declared in other modules. + // Interfaces registered by the application will conflict with this one if it has the same name. + // This means that we only need to look for the interfaces in the engine->classTypes, but not in engine->objectTypes. + asCArray equals; + for( i = 0; i < classTypes.GetLength(); i++ ) + { + asCObjectType *intf1 = classTypes[i]; + if( !intf1->IsInterface() ) + continue; + + // The interface may have been determined to be equal to another already + bool found = false; + for( unsigned int e = 0; e < equals.GetLength(); e++ ) + { + if( equals[e].a == intf1 ) + { + found = true; + break; + } + } + + if( found ) + break; + + for( unsigned int n = 0; n < engine->classTypes.GetLength(); n++ ) + { + // Don't compare against self + if( engine->classTypes[n] == intf1 ) + continue; + + asCObjectType *intf2 = engine->classTypes[n]; + + // Assume the interface are equal, then validate this + sObjectTypePair pair = {intf1,intf2}; + equals.PushLast(pair); + + if( AreInterfacesEqual(intf1, intf2, equals) ) + break; + + // Since they are not equal, remove them from the list again + equals.PopLast(); + } + } + + // For each of the interfaces that have been found to be equal we need to + // remove the new declaration and instead have the module use the existing one. + for( i = 0; i < equals.GetLength(); i++ ) + { + // Substitute the old object type from the module's class types + unsigned int c; + for( c = 0; c < classTypes.GetLength(); c++ ) + { + if( classTypes[c] == equals[i].a ) + { + classTypes[c] = equals[i].b; + equals[i].b->AddRef(); + break; + } + } + + // Remove the old object type from the engine's class types + for( c = 0; c < engine->classTypes.GetLength(); c++ ) + { + if( engine->classTypes[c] == equals[i].a ) + { + engine->classTypes[c] = engine->classTypes[engine->classTypes.GetLength()-1]; + engine->classTypes.PopLast(); + break; + } + } + + // Substitute all uses of this object type + // Only interfaces in the module is using the type so far + for( c = 0; c < classTypes.GetLength(); c++ ) + { + if( classTypes[c]->IsInterface() ) + { + asCObjectType *intf = classTypes[c]; + for( int m = 0; m < intf->GetMethodCount(); m++ ) + { + asCScriptFunction *func = engine->GetScriptFunction(intf->methods[m]); + if( func ) + { + if( func->returnType.GetObjectType() == equals[i].a ) + func->returnType.SetObjectType(equals[i].b); + + for( int p = 0; p < func->GetParamCount(); p++ ) + { + if( func->parameterTypes[p].GetObjectType() == equals[i].a ) + func->parameterTypes[p].SetObjectType(equals[i].b); + } + } + } + } + } + + // Substitute all interface methods in the module. Delete all methods for the old interface + for( unsigned int m = 0; m < equals[i].a->methods.GetLength(); m++ ) + { + for( c = 0; c < scriptFunctions.GetLength(); c++ ) + { + if( scriptFunctions[c]->id == equals[i].a->methods[m] ) + { + scriptFunctions[c]->Release(); + + scriptFunctions[c] = engine->GetScriptFunction(equals[i].b->methods[m]); + scriptFunctions[c]->AddRef(); + } + } + } + + // Deallocate the object type + asDELETE(equals[i].a, asCObjectType); + } +} + +// internal +bool asCModule::AreInterfacesEqual(asCObjectType *a, asCObjectType *b, asCArray &equals) +{ + // An interface is considered equal to another if the following criterias apply: + // + // - The interface names are equal + // - The number of methods is equal + // - All the methods are equal + // - The order of the methods is equal + // - If a method returns or takes an interface by handle or reference, both interfaces must be equal + + + // ------------ + // TODO: Study the possiblity of allowing interfaces where methods are declared in different orders to + // be considered equal. The compiler and VM can handle this, but it complicates the comparison of interfaces + // where multiple methods take different interfaces as parameters (or return values). Example: + // + // interface A + // { + // void f(B, C); + // void f(B); + // void f(C); + // } + // + // If 'void f(B)' in module A is compared against 'void f(C)' in module B, then the code will assume + // interface B in module A equals interface C in module B. Thus 'void f(B, C)' in module A won't match + // 'void f(C, B)' in module B. + // ------------ + + + // Are both interfaces? + if( !a->IsInterface() || !b->IsInterface() ) + return false; + + // Are the names equal? + if( a->name != b->name ) + return false; + + // Are the number of methods equal? + if( a->methods.GetLength() != b->methods.GetLength() ) + return false; + + // Keep the number of equals in the list so we can restore it later if necessary + int prevEquals = (int)equals.GetLength(); + + // Are the methods equal to each other? + bool match = true; + for( unsigned int n = 0; n < a->methods.GetLength(); n++ ) + { + match = false; + + asCScriptFunction *funcA = (asCScriptFunction*)engine->GetFunctionDescriptorById(a->methods[n]); + asCScriptFunction *funcB = (asCScriptFunction*)engine->GetFunctionDescriptorById(b->methods[n]); + + // funcB can be null if the module that created the interface has been + // discarded but the type has not yet been released by the engine. + if( funcB == 0 ) + break; + + // The methods must have the same name and the same number of parameters + if( funcA->name != funcB->name || + funcA->parameterTypes.GetLength() != funcB->parameterTypes.GetLength() ) + break; + + // The return types must be equal. If the return type is an interface the interfaces must match. + if( !AreTypesEqual(funcA->returnType, funcB->returnType, equals) ) + break; + + match = true; + for( unsigned int p = 0; p < funcA->parameterTypes.GetLength(); p++ ) + { + if( !AreTypesEqual(funcA->parameterTypes[p], funcB->parameterTypes[p], equals) || + funcA->inOutFlags[p] != funcB->inOutFlags[p] ) + { + match = false; + break; + } + } + + if( !match ) + break; + } + + // For each of the new interfaces that we're assuming to be equal, we need to validate this + if( match ) + { + for( unsigned int n = prevEquals; n < equals.GetLength(); n++ ) + { + if( !AreInterfacesEqual(equals[n].a, equals[n].b, equals) ) + { + match = false; + break; + } + } + } + + if( !match ) + { + // The interfaces doesn't match. + // Restore the list of previous equals before we go on, so + // the caller can continue comparing with another interface + equals.SetLength(prevEquals); + } + + return match; +} + +// internal +bool asCModule::AreTypesEqual(const asCDataType &a, const asCDataType &b, asCArray &equals) +{ + if( !a.IsEqualExceptInterfaceType(b) ) + return false; + + asCObjectType *ai = a.GetObjectType(); + asCObjectType *bi = b.GetObjectType(); + + if( ai && ai->IsInterface() ) + { + // If the interface is in the equals list, then the pair must match the pair in the list + bool found = false; + unsigned int e; + for( e = 0; e < equals.GetLength(); e++ ) + { + if( equals[e].a == ai ) + { + found = true; + break; + } + } + + if( found ) + { + // Do the pairs match? + if( equals[e].b != bi ) + return false; + } + else + { + // Assume they are equal from now on + sObjectTypePair pair = {ai, bi}; + equals.PushLast(pair); + } + } + + return true; +} + +// interface +int asCModule::SaveByteCode(asIBinaryStream *out) +{ + if( out == 0 ) return asINVALID_ARG; + + asCRestore rest(this, out, engine); + return rest.Save(); +} + +// interface +int asCModule::LoadByteCode(asIBinaryStream *in) +{ + if( in == 0 ) return asINVALID_ARG; + + // Only permit loading bytecode if no other thread is currently compiling + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + asCRestore rest(this, in, engine); + r = rest.Restore(); + + JITCompile(); + + engine->BuildCompleted(); + + return r; +} + +// interface +int asCModule::CompileGlobalVar(const char *sectionName, const char *code, int lineOffset) +{ + // Validate arguments + if( code == 0 ) + return asINVALID_ARG; + + // Only one thread may build at one time + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + // Prepare the engine + engine->PrepareEngine(); + if( engine->configFailed ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_INVALID_CONFIGURATION); + engine->BuildCompleted(); + return asINVALID_CONFIGURATION; + } + + // Compile the global variable and add it to the module scope + asCBuilder builder(engine, this); + asCString str = code; + r = builder.CompileGlobalVar(sectionName, str.AddressOf(), lineOffset); + + engine->BuildCompleted(); + + // Initialize the variable + if( r >= 0 && engine->ep.initGlobalVarsAfterBuild ) + { + // Clear the memory + asCGlobalProperty *prop = scriptGlobals[scriptGlobals.GetLength()-1]; + memset(prop->GetAddressOfValue(), 0, sizeof(asDWORD)*prop->type.GetSizeOnStackDWords()); + + if( prop->initFunc ) + { + // Call the init function for the global variable + asIScriptContext *ctx = 0; + int r = engine->CreateContext(&ctx, true); + if( r < 0 ) + return r; + + r = ctx->Prepare(prop->initFunc->id); + if( r >= 0 ) + r = ctx->Execute(); + + ctx->Release(); + } + } + + return r; +} + +// interface +int asCModule::CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD compileFlags, asIScriptFunction **outFunc) +{ + asASSERT(outFunc == 0 || *outFunc == 0); + + // Validate arguments + if( code == 0 || + (compileFlags != 0 && compileFlags != asCOMP_ADD_TO_MODULE) ) + return asINVALID_ARG; + + // Only one thread may build at one time + int r = engine->RequestBuild(); + if( r < 0 ) + return r; + + // Prepare the engine + engine->PrepareEngine(); + if( engine->configFailed ) + { + engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_INVALID_CONFIGURATION); + engine->BuildCompleted(); + return asINVALID_CONFIGURATION; + } + + // Compile the single function + asCBuilder builder(engine, this); + asCString str = code; + asCScriptFunction *func = 0; + r = builder.CompileFunction(sectionName, str.AddressOf(), lineOffset, compileFlags, &func); + + engine->BuildCompleted(); + + if( r >= 0 && outFunc ) + { + // Return the function to the caller + *outFunc = func; + func->AddRef(); + } + + // Release our reference to the function + if( func ) + func->Release(); + + return r; +} + +// interface +int asCModule::RemoveFunction(int funcId) +{ + // Find the global function + for( asUINT n = 0; n < globalFunctions.GetLength(); n++ ) + { + if( globalFunctions[n] && globalFunctions[n]->id == funcId ) + { + asCScriptFunction *func = globalFunctions[n]; + globalFunctions.RemoveIndex(n); + func->Release(); + + scriptFunctions.RemoveValue(func); + func->Release(); + + return 0; + } + } + + return asNO_FUNCTION; +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_module.h b/AngelScript/source/as_module.h new file mode 100644 index 000000000..841303107 --- /dev/null +++ b/AngelScript/source/as_module.h @@ -0,0 +1,213 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_module.h +// +// A class that holds a script module +// + +#ifndef AS_MODULE_H +#define AS_MODULE_H + +#include "as_config.h" +#include "as_atomic.h" +#include "as_string.h" +#include "as_array.h" +#include "as_datatype.h" +#include "as_scriptfunction.h" +#include "as_property.h" + +BEGIN_AS_NAMESPACE + +// TODO: import: Remove this when the imported functions are removed +const int FUNC_IMPORTED = 0x40000000; + +class asCScriptEngine; +class asCCompiler; +class asCBuilder; +class asCContext; +class asCConfigGroup; + +struct sBindInfo +{ + asCScriptFunction *importedFunctionSignature; + asCString importFromModule; + int boundFunctionId; +}; + +struct sObjectTypePair +{ + asCObjectType *a; + asCObjectType *b; +}; + +// TODO: import: Remove function imports. When I have implemented function +// pointers the function imports should be deprecated. + +// TODO: Need a separate interface for compiling scripts. The asIScriptCompiler +// will have a target module, and will allow the compilation of an entire +// script or just individual functions within the scope of the module +// +// With this separation it will be possible to compile the library without +// the compiler, thus giving a much smaller binary executable. + + +class asCModule : public asIScriptModule +{ +//------------------------------------------- +// Public interface +//-------------------------------------------- +public: + virtual asIScriptEngine *GetEngine(); + virtual void SetName(const char *name); + virtual const char *GetName(); + + // Compilation + virtual int AddScriptSection(const char *name, const char *code, size_t codeLength, int lineOffset); + virtual int Build(); + virtual int CompileFunction(const char *sectionName, const char *code, int lineOffset, asDWORD reserved, asIScriptFunction **outFunc); + virtual int CompileGlobalVar(const char *sectionName, const char *code, int lineOffset); + + // Script functions + virtual int GetFunctionCount(); + virtual int GetFunctionIdByIndex(int index); + virtual int GetFunctionIdByName(const char *name); + virtual int GetFunctionIdByDecl(const char *decl); + virtual asIScriptFunction *GetFunctionDescriptorByIndex(int index); + virtual asIScriptFunction *GetFunctionDescriptorById(int funcId); + virtual int RemoveFunction(int funcId); + + // Script global variables + virtual int ResetGlobalVars(); + virtual int GetGlobalVarCount(); + virtual int GetGlobalVarIndexByName(const char *name); + virtual int GetGlobalVarIndexByDecl(const char *decl); + virtual const char *GetGlobalVarDeclaration(int index); + virtual const char *GetGlobalVarName(int index); + virtual int GetGlobalVarTypeId(int index, bool *isConst); + virtual void *GetAddressOfGlobalVar(int index); + virtual int RemoveGlobalVar(int index); + + // Type identification + virtual int GetObjectTypeCount(); + virtual asIObjectType *GetObjectTypeByIndex(asUINT index); + virtual int GetTypeIdByDecl(const char *decl); + + // Enums + virtual int GetEnumCount(); + virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId); + virtual int GetEnumValueCount(int enumTypeId); + virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue); + + // Typedefs + virtual int GetTypedefCount(); + virtual const char *GetTypedefByIndex(asUINT index, int *typeId); + + // Dynamic binding between modules + virtual int GetImportedFunctionCount(); + virtual int GetImportedFunctionIndexByDecl(const char *decl); + virtual const char *GetImportedFunctionDeclaration(int importIndex); + virtual const char *GetImportedFunctionSourceModule(int importIndex); + virtual int BindImportedFunction(int index, int sourceID); + virtual int UnbindImportedFunction(int importIndex); + virtual int BindAllImportedFunctions(); + virtual int UnbindAllImportedFunctions(); + + // Bytecode Saving/Loading + virtual int SaveByteCode(asIBinaryStream *out); + virtual int LoadByteCode(asIBinaryStream *in); + +//----------------------------------------------- +// Internal +//----------------------------------------------- + asCModule(const char *name, asCScriptEngine *engine); + ~asCModule(); + +//protected: + friend class asCScriptEngine; + friend class asCBuilder; + friend class asCCompiler; + friend class asCContext; + friend class asCRestore; + + void InternalReset(); + + int CallInit(); + void CallExit(); + + void JITCompile(); + + int AddScriptFunction(int sectionIdx, int id, const char *name, const asCDataType &returnType, asCDataType *params, asETypeModifiers *inOutFlags, int paramCount, bool isInterface, asCObjectType *objType = 0, bool isConstMethod = false, bool isGlobalFunction = false); + int AddScriptFunction(asCScriptFunction *func); + int AddImportedFunction(int id, const char *name, const asCDataType &returnType, asCDataType *params, asETypeModifiers *inOutFlags, int paramCount, const asCString &moduleName); + + int GetNextImportedFunctionId(); + + void ResolveInterfaceIds(); + bool AreInterfacesEqual(asCObjectType *a, asCObjectType *b, asCArray &equals); + bool AreTypesEqual(const asCDataType &a, const asCDataType &b, asCArray &equals); + + asCScriptFunction *GetImportedFunction(int funcId); + + asCObjectType *GetObjectType(const char *type); + + asCGlobalProperty *AllocateGlobalProperty(const char *name, const asCDataType &dt); + + + asCString name; + + asCScriptEngine *engine; + asCBuilder *builder; + + // This array holds all functions, class members, factories, etc that were compiled with the module + asCArray scriptFunctions; + // This array holds global functions declared in the module + asCArray globalFunctions; + // This array holds imported functions in the module + asCArray bindInformations; + + // This array holds the global variables declared in the script + asCArray scriptGlobals; + bool isGlobalVarInitialized; + + // This array holds class and interface types + asCArray classTypes; + // This array holds enum types + asCArray enumTypes; + // This array holds typedefs + asCArray typeDefs; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_objecttype.cpp b/AngelScript/source/as_objecttype.cpp new file mode 100644 index 000000000..1ef256a27 --- /dev/null +++ b/AngelScript/source/as_objecttype.cpp @@ -0,0 +1,676 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_objecttype.cpp +// +// A class for storing object type information +// + + +#include + +#include "as_config.h" +#include "as_objecttype.h" +#include "as_configgroup.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +#ifdef AS_MAX_PORTABILITY + +static void ObjectType_AddRef_Generic(asIScriptGeneric *gen) +{ + asCObjectType *self = (asCObjectType*)gen->GetObject(); + self->AddRef(); +} + +static void ObjectType_Release_Generic(asIScriptGeneric *gen) +{ + asCObjectType *self = (asCObjectType*)gen->GetObject(); + self->Release(); +} + +static void ObjectType_GetRefCount_Generic(asIScriptGeneric *gen) +{ + asCObjectType *self = (asCObjectType*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ObjectType_SetGCFlag_Generic(asIScriptGeneric *gen) +{ + asCObjectType *self = (asCObjectType*)gen->GetObject(); + self->SetGCFlag(); +} + +static void ObjectType_GetGCFlag_Generic(asIScriptGeneric *gen) +{ + asCObjectType *self = (asCObjectType*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag(); +} + +static void ObjectType_EnumReferences_Generic(asIScriptGeneric *gen) +{ + asCObjectType *self = (asCObjectType*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ObjectType_ReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + asCObjectType *self = (asCObjectType*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +#endif + + +void RegisterObjectTypeGCBehaviours(asCScriptEngine *engine) +{ + // Register the gc behaviours for the object types + int r; + engine->objectTypeBehaviours.engine = engine; + engine->objectTypeBehaviours.flags = asOBJ_REF | asOBJ_GC; + engine->objectTypeBehaviours.name = "_builtin_objecttype_"; +#ifndef AS_MAX_PORTABILITY + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCObjectType,AddRef), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCObjectType,Release), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCObjectType,GetRefCount), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCObjectType,SetGCFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCObjectType,GetGCFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCObjectType,EnumReferences), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCObjectType,ReleaseAllHandles), asCALL_THISCALL); asASSERT( r >= 0 ); +#else + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ObjectType_AddRef_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ObjectType_Release_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ObjectType_GetRefCount_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ObjectType_SetGCFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ObjectType_GetGCFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ObjectType_EnumReferences_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->objectTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ObjectType_ReleaseAllHandles_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); +#endif +} + +asCObjectType::asCObjectType() +{ + engine = 0; + refCount.set(0); + derivedFrom = 0; + + acceptValueSubType = true; + acceptRefSubType = true; +} + +asCObjectType::asCObjectType(asCScriptEngine *engine) +{ + this->engine = engine; + refCount.set(0); + derivedFrom = 0; + + acceptValueSubType = true; + acceptRefSubType = true; +} + +int asCObjectType::AddRef() +{ + gcFlag = false; + return refCount.atomicInc(); +} + +int asCObjectType::Release() +{ + gcFlag = false; + return refCount.atomicDec(); +} + +int asCObjectType::GetRefCount() +{ + return refCount.get(); +} + +bool asCObjectType::GetGCFlag() +{ + return gcFlag; +} + +void asCObjectType::SetGCFlag() +{ + gcFlag = true; +} + +asCObjectType::~asCObjectType() +{ + // Release the object type held by the templateSubType + if( templateSubType.GetObjectType() ) + templateSubType.GetObjectType()->Release(); + + if( derivedFrom ) + derivedFrom->Release(); + + asUINT n; + for( n = 0; n < properties.GetLength(); n++ ) + if( properties[n] ) + { + if( flags & asOBJ_SCRIPT_OBJECT ) + { + // Release the config group for script classes that are being destroyed + asCConfigGroup *group = engine->FindConfigGroupForObjectType(properties[n]->type.GetObjectType()); + if( group != 0 ) group->Release(); + } + + asDELETE(properties[n],asCObjectProperty); + } + + properties.SetLength(0); + + ReleaseAllFunctions(); + + for( n = 0; n < enumValues.GetLength(); n++ ) + { + if( enumValues[n] ) + asDELETE(enumValues[n],asSEnumValue); + } + + enumValues.SetLength(0); +} + +bool asCObjectType::Implements(const asCObjectType *objType) const +{ + if( this == objType ) + return true; + + for( asUINT n = 0; n < interfaces.GetLength(); n++ ) + if( interfaces[n] == objType ) return true; + + return false; +} + +bool asCObjectType::DerivesFrom(const asCObjectType *objType) const +{ + if( this == objType ) + return true; + + asCObjectType *base = derivedFrom; + while( base ) + { + if( base == objType ) + return true; + + base = base->derivedFrom; + } + + return false; +} + +// interface +const char *asCObjectType::GetName() const +{ + return name.AddressOf(); +} + +// interface +asDWORD asCObjectType::GetFlags() const +{ + return flags; +} + +// interface +asUINT asCObjectType::GetSize() const +{ + return size; +} + +// interface +int asCObjectType::GetTypeId() const +{ + // We need a non const pointer to create the asCDataType object. + // We're not breaking anything here because this function is not + // modifying the object, so this const cast is safe. + asCObjectType *ot = const_cast(this); + + return engine->GetTypeIdFromDataType(asCDataType::CreateObject(ot, false)); +} + +// interface +int asCObjectType::GetSubTypeId() const +{ + // TODO: template: This method should allow indexing multiple template subtypes + + if( flags & asOBJ_TEMPLATE ) + { + return engine->GetTypeIdFromDataType(templateSubType); + } + + // Only template types have sub types + return asERROR; +} + +int asCObjectType::GetInterfaceCount() const +{ + return (int)interfaces.GetLength(); +} + +asIObjectType *asCObjectType::GetInterface(asUINT index) const +{ + assert(index < interfaces.GetLength()); + + return interfaces[index]; +} + +// internal +bool asCObjectType::IsInterface() const +{ + if( (flags & asOBJ_SCRIPT_OBJECT) && size == 0 ) + return true; + + return false; +} + +asIScriptEngine *asCObjectType::GetEngine() const +{ + return engine; +} + +int asCObjectType::GetFactoryCount() const +{ + return (int)beh.factories.GetLength(); +} + +int asCObjectType::GetFactoryIdByIndex(int index) const +{ + if( index < 0 || (unsigned)index >= beh.factories.GetLength() ) + return asINVALID_ARG; + + return beh.factories[index]; +} + +int asCObjectType::GetFactoryIdByDecl(const char *decl) const +{ + if( beh.factories.GetLength() == 0 ) + return asNO_FUNCTION; + + // Let the engine parse the string and find the appropriate factory function + return engine->GetFactoryIdByDecl(this, decl); +} + +int asCObjectType::GetMethodCount() const +{ + return (int)methods.GetLength(); +} + +int asCObjectType::GetMethodIdByIndex(int index) const +{ + if( index < 0 || (unsigned)index >= methods.GetLength() ) + return asINVALID_ARG; + + return methods[index]; +} + +int asCObjectType::GetMethodIdByName(const char *name) const +{ + int id = -1; + for( size_t n = 0; n < methods.GetLength(); n++ ) + { + if( engine->scriptFunctions[methods[n]]->name == name ) + { + if( id == -1 ) + id = methods[n]; + else + return asMULTIPLE_FUNCTIONS; + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + +int asCObjectType::GetMethodIdByDecl(const char *decl) const +{ + // Get the module from one of the methods + if( methods.GetLength() == 0 ) + return asNO_FUNCTION; + + asCModule *mod = engine->scriptFunctions[methods[0]]->module; + if( mod == 0 ) + { + if( engine->scriptFunctions[methods[0]]->funcType == asFUNC_INTERFACE ) + return engine->GetMethodIdByDecl(this, decl, 0); + + return asNO_MODULE; + } + + return engine->GetMethodIdByDecl(this, decl, mod); +} + +asIScriptFunction *asCObjectType::GetMethodDescriptorByIndex(int index) const +{ + if( index < 0 || (unsigned)index >= methods.GetLength() ) + return 0; + + return engine->scriptFunctions[methods[index]]; +} + +int asCObjectType::GetPropertyCount() const +{ + return (int)properties.GetLength(); +} + +int asCObjectType::GetPropertyTypeId(asUINT prop) const +{ + if( prop >= properties.GetLength() ) + return asINVALID_ARG; + + return engine->GetTypeIdFromDataType(properties[prop]->type); +} + +const char *asCObjectType::GetPropertyName(asUINT prop) const +{ + if( prop >= properties.GetLength() ) + return 0; + + return properties[prop]->name.AddressOf(); +} + +asIObjectType *asCObjectType::GetBaseType() const +{ + return derivedFrom; +} + +int asCObjectType::GetPropertyOffset(asUINT prop) const +{ + if( prop >= properties.GetLength() ) + return 0; + + return properties[prop]->byteOffset; +} + +int asCObjectType::GetBehaviourCount() const +{ + // Count the number of behaviours (except factory functions) + int count = 0; + + if( beh.destruct ) count++; + if( beh.addref ) count++; + if( beh.release ) count++; + if( beh.gcGetRefCount ) count++; + if( beh.gcSetFlag ) count++; + if( beh.gcGetFlag ) count++; + if( beh.gcEnumReferences ) count++; + if( beh.gcReleaseAllReferences ) count++; + if( beh.templateCallback ) count++; + + // For reference types, the factories are also stored in the constructor + // list, so it is sufficient to enumerate only those + count += (int)beh.constructors.GetLength(); + count += (int)beh.operators.GetLength() / 2; + + return count; +} + +int asCObjectType::GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const +{ + // Find the correct behaviour + int count = 0; + + if( beh.destruct && count++ == (int)index ) // only increase count if the behaviour is registered + { + if( outBehaviour ) *outBehaviour = asBEHAVE_DESTRUCT; + return beh.destruct; + } + + if( beh.addref && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_ADDREF; + return beh.addref; + } + + if( beh.release && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_RELEASE; + return beh.release; + } + + if( beh.gcGetRefCount && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_GETREFCOUNT; + return beh.gcGetRefCount; + } + + if( beh.gcSetFlag && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_SETGCFLAG; + return beh.gcSetFlag; + } + + if( beh.gcGetFlag && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_GETGCFLAG; + return beh.gcGetFlag; + } + + if( beh.gcEnumReferences && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_ENUMREFS; + return beh.gcEnumReferences; + } + + if( beh.gcReleaseAllReferences && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_RELEASEREFS; + return beh.gcReleaseAllReferences; + } + + if( beh.templateCallback && count++ == (int)index ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_TEMPLATE_CALLBACK; + return beh.templateCallback; + } + + // For reference types, the factories are also stored in the constructor + // list, so it is sufficient to enumerate only those + if( index - count < beh.constructors.GetLength() ) + { + if( outBehaviour ) *outBehaviour = asBEHAVE_CONSTRUCT; + return beh.constructors[index - count]; + } + else + count += (int)beh.constructors.GetLength(); + + if( index - count < beh.operators.GetLength() / 2 ) + { + index = 2*(index - count); + + if( outBehaviour ) *outBehaviour = static_cast(beh.operators[index]); + return beh.operators[index + 1]; + } + + return asINVALID_ARG; +} + +// interface +const char *asCObjectType::GetConfigGroup() const +{ + asCConfigGroup *group = engine->FindConfigGroupForObjectType(this); + if( group == 0 ) + return 0; + + return group->groupName.AddressOf(); +} + +// internal +void asCObjectType::ReleaseAllHandles(asIScriptEngine *) +{ + ReleaseAllFunctions(); +} + +// internal +void asCObjectType::ReleaseAllFunctions() +{ + beh.factory = 0; + beh.copyfactory = 0; + for( asUINT a = 0; a < beh.factories.GetLength(); a++ ) + { + if( engine->scriptFunctions[beh.factories[a]] ) + engine->scriptFunctions[beh.factories[a]]->Release(); + } + beh.factories.SetLength(0); + + beh.construct = 0; + beh.copyconstruct = 0; + for( asUINT b = 0; b < beh.constructors.GetLength(); b++ ) + { + if( engine->scriptFunctions[beh.constructors[b]] ) + engine->scriptFunctions[beh.constructors[b]]->Release(); + } + beh.constructors.SetLength(0); + + if( beh.templateCallback ) + engine->scriptFunctions[beh.templateCallback]->Release(); + beh.templateCallback = 0; + + if( beh.destruct ) + engine->scriptFunctions[beh.destruct]->Release(); + beh.destruct = 0; + + if( beh.addref ) + engine->scriptFunctions[beh.addref]->Release(); + beh.addref = 0; + + if( beh.release ) + engine->scriptFunctions[beh.release]->Release(); + beh.release = 0; + + if( beh.copy ) + engine->scriptFunctions[beh.copy]->Release(); + beh.copy = 0; + + if( beh.gcEnumReferences ) + engine->scriptFunctions[beh.gcEnumReferences]->Release(); + beh.gcEnumReferences = 0; + + if( beh.gcGetFlag ) + engine->scriptFunctions[beh.gcGetFlag]->Release(); + beh.gcGetFlag = 0; + + if( beh.gcGetRefCount ) + engine->scriptFunctions[beh.gcGetRefCount]->Release(); + beh.gcGetRefCount = 0; + + if( beh.gcReleaseAllReferences ) + engine->scriptFunctions[beh.gcReleaseAllReferences]->Release(); + beh.gcReleaseAllReferences = 0; + + if( beh.gcSetFlag ) + engine->scriptFunctions[beh.gcSetFlag]->Release(); + beh.gcSetFlag = 0; + + for( asUINT e = 1; e < beh.operators.GetLength(); e += 2 ) + { + if( engine->scriptFunctions[beh.operators[e]] ) + engine->scriptFunctions[beh.operators[e]]->Release(); + } + beh.operators.SetLength(0); + + for( asUINT c = 0; c < methods.GetLength(); c++ ) + { + if( engine->scriptFunctions[methods[c]] ) + engine->scriptFunctions[methods[c]]->Release(); + } + methods.SetLength(0); + + for( asUINT d = 0; d < virtualFunctionTable.GetLength(); d++ ) + { + if( virtualFunctionTable[d] ) + virtualFunctionTable[d]->Release(); + } + virtualFunctionTable.SetLength(0); +} + +// internal +void asCObjectType::EnumReferences(asIScriptEngine *) +{ + for( asUINT a = 0; a < beh.factories.GetLength(); a++ ) + if( engine->scriptFunctions[beh.factories[a]] ) + engine->GCEnumCallback(engine->scriptFunctions[beh.factories[a]]); + + for( asUINT b = 0; b < beh.constructors.GetLength(); b++ ) + if( engine->scriptFunctions[beh.constructors[b]] ) + engine->GCEnumCallback(engine->scriptFunctions[beh.constructors[b]]); + + if( beh.templateCallback ) + engine->GCEnumCallback(engine->scriptFunctions[beh.templateCallback]); + + if( beh.destruct ) + engine->GCEnumCallback(engine->scriptFunctions[beh.destruct]); + + if( beh.addref ) + engine->GCEnumCallback(engine->scriptFunctions[beh.addref]); + + if( beh.release ) + engine->GCEnumCallback(engine->scriptFunctions[beh.release]); + + if( beh.copy ) + engine->GCEnumCallback(engine->scriptFunctions[beh.copy]); + + if( beh.gcEnumReferences ) + engine->GCEnumCallback(engine->scriptFunctions[beh.gcEnumReferences]); + + if( beh.gcGetFlag ) + engine->GCEnumCallback(engine->scriptFunctions[beh.gcGetFlag]); + + if( beh.gcGetRefCount ) + engine->GCEnumCallback(engine->scriptFunctions[beh.gcGetRefCount]); + + if( beh.gcReleaseAllReferences ) + engine->GCEnumCallback(engine->scriptFunctions[beh.gcReleaseAllReferences]); + + if( beh.gcSetFlag ) + engine->GCEnumCallback(engine->scriptFunctions[beh.gcSetFlag]); + + for( asUINT e = 1; e < beh.operators.GetLength(); e += 2 ) + if( engine->scriptFunctions[beh.operators[e]] ) + engine->GCEnumCallback(engine->scriptFunctions[beh.operators[e]]); + + for( asUINT c = 0; c < methods.GetLength(); c++ ) + if( engine->scriptFunctions[methods[c]] ) + engine->GCEnumCallback(engine->scriptFunctions[methods[c]]); + + for( asUINT d = 0; d < virtualFunctionTable.GetLength(); d++ ) + if( virtualFunctionTable[d] ) + engine->GCEnumCallback(virtualFunctionTable[d]); +} + +END_AS_NAMESPACE + + + diff --git a/AngelScript/source/as_objecttype.h b/AngelScript/source/as_objecttype.h new file mode 100644 index 000000000..1fce87757 --- /dev/null +++ b/AngelScript/source/as_objecttype.h @@ -0,0 +1,224 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_objecttype.h +// +// A class for storing object type information +// + + + +#ifndef AS_OBJECTTYPE_H +#define AS_OBJECTTYPE_H + +#include "as_atomic.h" +#include "as_string.h" +#include "as_property.h" +#include "as_array.h" +#include "as_scriptfunction.h" + +BEGIN_AS_NAMESPACE + +// TODO: memory: Need to minimize used memory here, because not all types use all properties of the class + +// TODO: The type id should have flags for diferenciating between value types and reference types. It should also have a flag for differenciating interface types. + +// Additional flag to the class object type +const asDWORD asOBJ_IMPLICIT_HANDLE = 0x40000; +const asDWORD asOBJ_TYPEDEF = 0x40000000; +const asDWORD asOBJ_ENUM = 0x10000000; +const asDWORD asOBJ_TEMPLATE_SUBTYPE = 0x20000000; + + + + +// asOBJ_GC is used to indicate that the type can potentially +// form circular references, thus is garbage collected. + +// The fact that an object is garbage collected doesn't imply that an object that +// can references it also must be garbage collected, only if the garbage collected +// object can reference it as well. + +// For registered types however, we set the flag asOBJ_GC if the GC +// behaviours are registered. For script types that contain any such type we +// automatically make garbage collected as well, because we cannot know what type +// of references that object can contain, and must assume the worst. + + +struct asSTypeBehaviour +{ + asSTypeBehaviour() + { + factory = 0; + copyfactory = 0; + construct = 0; + copyconstruct = 0; + destruct = 0; + copy = 0; + addref = 0; + release = 0; + gcGetRefCount = 0; + gcSetFlag = 0; + gcGetFlag = 0; + gcEnumReferences = 0; + gcReleaseAllReferences = 0; + templateCallback = 0; + } + + int factory; + int copyfactory; + int construct; + int copyconstruct; + int destruct; + int copy; + int addref; + int release; + int templateCallback; + + // GC behaviours + int gcGetRefCount; + int gcSetFlag; + int gcGetFlag; + int gcEnumReferences; + int gcReleaseAllReferences; + + asCArray factories; + asCArray constructors; + asCArray operators; +}; + +struct asSEnumValue +{ + asCString name; + int value; +}; + +class asCScriptEngine; + +void RegisterObjectTypeGCBehaviours(asCScriptEngine *engine); + +class asCObjectType : public asIObjectType +{ +public: +//===================================== +// From asIObjectType +//===================================== + asIScriptEngine *GetEngine() const; + const char *GetConfigGroup() const; + + // Memory management + int AddRef(); + int Release(); + + // Type info + const char *GetName() const; + asIObjectType *GetBaseType() const; + asDWORD GetFlags() const; + asUINT GetSize() const; + int GetTypeId() const; + int GetSubTypeId() const; + + // Behaviours + int GetBehaviourCount() const; + int GetBehaviourByIndex(asUINT index, asEBehaviours *outBehaviour) const; + + // Interfaces + int GetInterfaceCount() const; + asIObjectType *GetInterface(asUINT index) const; + + // Factories + int GetFactoryCount() const; + int GetFactoryIdByIndex(int index) const; + int GetFactoryIdByDecl(const char *decl) const; + + // Methods + int GetMethodCount() const; + int GetMethodIdByIndex(int index) const; + int GetMethodIdByName(const char *name) const; + int GetMethodIdByDecl(const char *decl) const; + asIScriptFunction *GetMethodDescriptorByIndex(int index) const; + + // Properties + int GetPropertyCount() const; + int GetPropertyTypeId(asUINT prop) const; + const char *GetPropertyName(asUINT prop) const; + int GetPropertyOffset(asUINT prop) const; + +//=========================================== +// Internal +//=========================================== +public: + asCObjectType(); + asCObjectType(asCScriptEngine *engine); + ~asCObjectType(); + + int GetRefCount(); + void SetGCFlag(); + bool GetGCFlag(); + void EnumReferences(asIScriptEngine *); + void ReleaseAllHandles(asIScriptEngine *); + + void ReleaseAllFunctions(); + + bool Implements(const asCObjectType *objType) const; + bool DerivesFrom(const asCObjectType *objType) const; + bool IsInterface() const; + + asCString name; + int size; + asCArray properties; + asCArray methods; + asCArray interfaces; + asCArray enumValues; + asCObjectType * derivedFrom; + asCArray virtualFunctionTable; + + asDWORD flags; + + asSTypeBehaviour beh; + + // Used for template types + asCDataType templateSubType; + bool acceptValueSubType; + bool acceptRefSubType; + + asCScriptEngine *engine; + +protected: + asCAtomic refCount; + bool gcFlag; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_outputbuffer.cpp b/AngelScript/source/as_outputbuffer.cpp new file mode 100644 index 000000000..a2f30d05a --- /dev/null +++ b/AngelScript/source/as_outputbuffer.cpp @@ -0,0 +1,100 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_outputbuffer.cpp +// +// This class appends strings to one large buffer that can later +// be sent to the real output stream +// + +#include "as_config.h" +#include "as_outputbuffer.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCOutputBuffer::~asCOutputBuffer() +{ + Clear(); +} + +void asCOutputBuffer::Clear() +{ + for( asUINT n = 0; n < messages.GetLength(); n++ ) + { + if( messages[n] ) + { + asDELETE(messages[n],message_t); + } + } + messages.SetLength(0); +} + +void asCOutputBuffer::Callback(asSMessageInfo *msg) +{ + message_t *msgInfo = asNEW(message_t); + msgInfo->section = msg->section; + msgInfo->row = msg->row; + msgInfo->col = msg->col; + msgInfo->type = msg->type; + msgInfo->msg = msg->message; + + messages.PushLast(msgInfo); +} + +void asCOutputBuffer::Append(asCOutputBuffer &in) +{ + for( asUINT n = 0; n < in.messages.GetLength(); n++ ) + messages.PushLast(in.messages[n]); + in.messages.SetLength(0); +} + +void asCOutputBuffer::SendToCallback(asCScriptEngine *engine, asSSystemFunctionInterface *func, void *obj) +{ + for( asUINT n = 0; n < messages.GetLength(); n++ ) + { + asSMessageInfo msg; + msg.section = messages[n]->section.AddressOf(); + msg.row = messages[n]->row; + msg.col = messages[n]->col; + msg.type = messages[n]->type; + msg.message = messages[n]->msg.AddressOf(); + + if( func->callConv < ICC_THISCALL ) + engine->CallGlobalFunction(&msg, obj, func, 0); + else + engine->CallObjectMethod(obj, &msg, func, 0); + } + Clear(); +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_outputbuffer.h b/AngelScript/source/as_outputbuffer.h new file mode 100644 index 000000000..5b7b26fa9 --- /dev/null +++ b/AngelScript/source/as_outputbuffer.h @@ -0,0 +1,75 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2008 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_outputbuffer.h +// +// This class appends strings to one large buffer that can later +// be sent to the real output stream +// + + +#ifndef AS_OUTPUTBUFFER_H +#define AS_OUTPUTBUFFER_H + +#include "as_config.h" +#include "as_string.h" +#include "as_array.h" + +BEGIN_AS_NAMESPACE + +struct asSSystemFunctionInterface; +class asCScriptEngine; + +class asCOutputBuffer +{ +public: + ~asCOutputBuffer (); + void Clear(); + void Callback(asSMessageInfo *msg); + void Append(asCOutputBuffer &in); + void SendToCallback(asCScriptEngine *engine, asSSystemFunctionInterface *func, void *obj); + + struct message_t + { + asCString section; + int row; + int col; + asEMsgType type; + asCString msg; + }; + + asCArray messages; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_parser.cpp b/AngelScript/source/as_parser.cpp new file mode 100644 index 000000000..91f15451e --- /dev/null +++ b/AngelScript/source/as_parser.cpp @@ -0,0 +1,2928 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_parser.cpp +// +// This class parses the script code and builds a tree for compilation +// + + + +#include "as_config.h" +#include "as_parser.h" +#include "as_tokendef.h" +#include "as_texts.h" + +#ifdef _MSC_VER +#pragma warning(disable:4702) // unreachable code +#endif + +BEGIN_AS_NAMESPACE + +asCParser::asCParser(asCBuilder *builder) +{ + this->builder = builder; + this->engine = builder->engine; + + script = 0; + scriptNode = 0; + checkValidTypes = false; + isParsingAppInterface = false; +} + +asCParser::~asCParser() +{ + Reset(); +} + +void asCParser::Reset() +{ + errorWhileParsing = false; + isSyntaxError = false; + checkValidTypes = false; + isParsingAppInterface = false; + + sourcePos = 0; + + if( scriptNode ) + { + scriptNode->Destroy(engine); + } + + scriptNode = 0; + + script = 0; +} + +asCScriptNode *asCParser::GetScriptNode() +{ + return scriptNode; +} + +int asCParser::ParseScript(asCScriptCode *script) +{ + Reset(); + + this->script = script; + + scriptNode = ParseScript(); + + if( errorWhileParsing ) + return -1; + + return 0; +} + +int asCParser::ParseFunctionDefinition(asCScriptCode *script) +{ + Reset(); + + // Set flag that permits ? as datatype for parameters + isParsingAppInterface = true; + + this->script = script; + + scriptNode = ParseFunctionDefinition(); + + // The declaration should end after the definition + if( !isSyntaxError ) + { + sToken t; + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asGetTokenDefinition(ttEnd)).AddressOf(), &t); + return -1; + } + } + + if( errorWhileParsing ) + return -1; + + return 0; +} + +int asCParser::ParseDataType(asCScriptCode *script) +{ + Reset(); + + this->script = script; + + scriptNode = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDataType); + + scriptNode->AddChildLast(ParseType(true)); + if( isSyntaxError ) return -1; + + // The declaration should end after the type + sToken t; + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asGetTokenDefinition(ttEnd)).AddressOf(), &t); + return -1; + } + + if( errorWhileParsing ) + return -1; + + return 0; +} + + +// Parse a template declaration: IDENTIFIER < class IDENTIFIER > +int asCParser::ParseTemplateDecl(asCScriptCode *script) +{ + Reset(); + + this->script = script; + scriptNode = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snUndefined); + + scriptNode->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return -1; + + sToken t; + GetToken(&t); + if( t.type != ttLessThan ) + { + Error(ExpectedToken(asGetTokenDefinition(ttLessThan)).AddressOf(), &t); + return -1; + } + + GetToken(&t); + if( t.type != ttClass ) + { + Error(ExpectedToken(asGetTokenDefinition(ttClass)).AddressOf(), &t); + return -1; + } + + scriptNode->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return -1; + + GetToken(&t); + if( t.type != ttGreaterThan ) + { + Error(ExpectedToken(asGetTokenDefinition(ttGreaterThan)).AddressOf(), &t); + return -1; + } + + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asGetTokenDefinition(ttEnd)).AddressOf(), &t); + return -1; + } + + if( errorWhileParsing ) + return -1; + + return 0; +} + +int asCParser::ParsePropertyDeclaration(asCScriptCode *script) +{ + Reset(); + + this->script = script; + + scriptNode = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDeclaration); + + scriptNode->AddChildLast(ParseType(true)); + if( isSyntaxError ) return -1; + + scriptNode->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return -1; + + // The declaration should end after the identifier + sToken t; + GetToken(&t); + if( t.type != ttEnd ) + { + Error(ExpectedToken(asGetTokenDefinition(ttEnd)).AddressOf(), &t); + return -1; + } + + return 0; +} + +asCScriptNode *asCParser::ParseImport() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snImport); + + sToken t; + GetToken(&t); + if( t.type != ttImport ) + { + Error(ExpectedToken(asGetTokenDefinition(ttImport)).AddressOf(), &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + node->AddChildLast(ParseFunctionDefinition()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttIdentifier ) + { + Error(ExpectedToken(FROM_TOKEN).AddressOf(), &t); + return node; + } + + asCString str; + str.Assign(&script->code[t.pos], t.length); + if( str != FROM_TOKEN ) + { + Error(ExpectedToken(FROM_TOKEN).AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttStringConstant ) + { + Error(TXT_EXPECTED_STRING, &t); + return node; + } + + asCScriptNode *mod = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snConstant); + node->AddChildLast(mod); + + mod->SetToken(&t); + mod->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(asGetTokenDefinition(ttEndStatement)).AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseFunctionDefinition() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snFunction); + + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseParameterList()); + if( isSyntaxError ) return node; + + // Parse an optional const after the function definition (used for object methods) + sToken t1; + GetToken(&t1); + RewindTo(&t1); + if( t1.type == ttConst ) + node->AddChildLast(ParseToken(ttConst)); + + return node; +} + +asCScriptNode *asCParser::ParseScript() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snScript); + + // Determine type of node + sToken t1; + + for(;;) + { + while( !isSyntaxError ) + { + GetToken(&t1); + RewindTo(&t1); + + if( t1.type == ttImport ) + node->AddChildLast(ParseImport()); + else if( t1.type == ttEnum ) + node->AddChildLast(ParseEnumeration()); // Handle enumerations + else if( t1.type == ttTypedef ) + node->AddChildLast(ParseTypedef()); // Handle primitive typedefs + else if( t1.type == ttClass ) + node->AddChildLast(ParseClass()); + else if( t1.type == ttInterface ) + node->AddChildLast(ParseInterface()); + else if( t1.type == ttConst || IsDataType(t1) ) + { + if( IsVarDecl() ) + node->AddChildLast(ParseGlobalVar()); + else + node->AddChildLast(ParseFunction()); + } + else if( t1.type == ttEndStatement ) + { + // Ignore a semicolon by itself + GetToken(&t1); + } + else if( t1.type == ttEnd ) + return node; + else + { + asCString str; + const char *t = asGetTokenDefinition(t1.type); + if( t == 0 ) t = ""; + + str.Format(TXT_UNEXPECTED_TOKEN_s, t); + + Error(str.AddressOf(), &t1); + } + } + + if( isSyntaxError ) + { + // Search for either ';' or '{' or end + GetToken(&t1); + while( t1.type != ttEndStatement && t1.type != ttEnd && + t1.type != ttStartStatementBlock ) + GetToken(&t1); + + if( t1.type == ttStartStatementBlock ) + { + // Find the end of the block and skip nested blocks + int level = 1; + while( level > 0 ) + { + GetToken(&t1); + if( t1.type == ttStartStatementBlock ) level++; + if( t1.type == ttEndStatementBlock ) level--; + if( t1.type == ttEnd ) break; + } + } + + isSyntaxError = false; + } + } + UNREACHABLE_RETURN; +} + +int asCParser::ParseStatementBlock(asCScriptCode *script, asCScriptNode *block) +{ + Reset(); + + // Tell the parser to validate the identifiers as valid types + checkValidTypes = true; + + this->script = script; + sourcePos = block->tokenPos; + + scriptNode = ParseStatementBlock(); + + if( isSyntaxError || errorWhileParsing ) + return -1; + + return 0; +} + +asCScriptNode *asCParser::ParseEnumeration() +{ + asCScriptNode *ident; + asCScriptNode *dataType; + + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snEnum); + + sToken token; + + // Check for enum + GetToken(&token); + if( token.type != ttEnum ) + { + Error(ExpectedToken(asGetTokenDefinition(ttEnum)).AddressOf(), &token); + return node; + } + + node->SetToken(&token); + node->UpdateSourcePos(token.pos, token.length); + + // Get the identifier + GetToken(&token); + if(ttIdentifier != token.type) + { + Error(TXT_EXPECTED_IDENTIFIER, &token); + return node; + } + + dataType = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDataType); + node->AddChildLast(dataType); + + ident = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snIdentifier); + ident->SetToken(&token); + ident->UpdateSourcePos(token.pos, token.length); + dataType->AddChildLast(ident); + + // check for the start of the declaration block + GetToken(&token); + if( token.type != ttStartStatementBlock ) + { + RewindTo(&token); + Error(ExpectedToken(asGetTokenDefinition(token.type)).AddressOf(), &token); + return node; + } + + while(ttEnd != token.type) + { + GetToken(&token); + + if( ttEndStatementBlock == token.type ) + { + RewindTo(&token); + break; + } + + if(ttIdentifier != token.type) + { + Error(TXT_EXPECTED_IDENTIFIER, &token); + return node; + } + + // Add the enum element + ident = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snIdentifier); + ident->SetToken(&token); + ident->UpdateSourcePos(token.pos, token.length); + node->AddChildLast(ident); + + GetToken(&token); + + if( token.type == ttAssignment ) + { + asCScriptNode *tmp; + + RewindTo(&token); + + tmp = SuperficiallyParseGlobalVarInit(); + + node->AddChildLast(tmp); + if( isSyntaxError ) return node; + GetToken(&token); + } + + if(ttListSeparator != token.type) + { + RewindTo(&token); + break; + } + } + + // check for the end of the declaration block + GetToken(&token); + if( token.type != ttEndStatementBlock ) + { + RewindTo(&token); + Error(ExpectedToken(asGetTokenDefinition(token.type)).AddressOf(), &token); + return node; + } + + // Parse the declarations + return node; +} + +bool asCParser::CheckTemplateType(sToken &t) +{ + // Is this a template type? + asCString typeName; + typeName.Assign(&script->code[t.pos], t.length); + if( engine->IsTemplateType(typeName.AddressOf()) ) + { + // Expect the sub type within < > + GetToken(&t); + if( t.type != ttLessThan ) + return false; + + // Now there must be a data type + GetToken(&t); + if( !IsDataType(t) ) + return false; + + if( !CheckTemplateType(t) ) + return false; + + GetToken(&t); + + // Is it a handle or array? + while( t.type == ttHandle || t.type == ttOpenBracket ) + { + if( t.type == ttOpenBracket ) + { + GetToken(&t); + if( t.type != ttCloseBracket ) + return false; + } + + GetToken(&t); + } + + // Accept >> and >>> tokens too. But then force the tokenizer to move + // only 1 character ahead (thus splitting the token in two). + if( script->code[t.pos] != '>' ) + return false; + else if( t.length != 1 ) + { + // We need to break the token, so that only the first character is parsed + sToken t2 = t; + t2.pos = t.pos + 1; + RewindTo(&t2); + } + } + + return true; +} + +bool asCParser::IsVarDecl() +{ + // Set start point so that we can rewind + sToken t; + GetToken(&t); + RewindTo(&t); + + // A variable decl can start with a const + sToken t1; + GetToken(&t1); + if( t1.type == ttConst ) + GetToken(&t1); + + if( !IsDataType(t1) ) + { + RewindTo(&t); + return false; + } + + if( !CheckTemplateType(t1) ) + { + RewindTo(&t); + return false; + } + + // Object handles can be interleaved with the array brackets + sToken t2; + GetToken(&t2); + while( t2.type == ttHandle || t2.type == ttOpenBracket ) + { + if( t2.type == ttOpenBracket ) + { + GetToken(&t2); + if( t2.type != ttCloseBracket ) + { + RewindTo(&t); + return false; + } + } + + GetToken(&t2); + } + + if( t2.type != ttIdentifier ) + { + RewindTo(&t); + return false; + } + + GetToken(&t2); + if( t2.type == ttEndStatement || t2.type == ttAssignment || t2.type == ttListSeparator ) + { + RewindTo(&t); + return true; + } + if( t2.type == ttOpenParanthesis ) + { + // If the closing paranthesis is followed by a statement + // block or end-of-file, then treat it as a function. + while( t2.type != ttCloseParanthesis && t2.type != ttEnd ) + GetToken(&t2); + + if( t2.type == ttEnd ) + return false; + else + { + GetToken(&t1); + RewindTo(&t); + if( t1.type == ttStartStatementBlock || t1.type == ttEnd ) + return false; + } + + RewindTo(&t); + + return true; + } + + RewindTo(&t); + return false; +} + +bool asCParser::IsFuncDecl(bool isMethod) +{ + // Set start point so that we can rewind + sToken t; + GetToken(&t); + RewindTo(&t); + + // A class constructor starts with identifier followed by parenthesis + // A class destructor starts with the ~ token + if( isMethod ) + { + sToken t1, t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t); + if( (t1.type == ttIdentifier && t2.type == ttOpenParanthesis) || t1.type == ttBitNot ) + return true; + } + + // A function decl can start with a const + sToken t1; + GetToken(&t1); + if( t1.type == ttConst ) + GetToken(&t1); + + if( !IsDataType(t1) ) + { + RewindTo(&t); + return false; + } + + // Object handles can be interleaved with the array brackets + sToken t2; + GetToken(&t2); + while( t2.type == ttHandle || t2.type == ttOpenBracket ) + { + if( t2.type == ttOpenBracket ) + { + GetToken(&t2); + if( t2.type != ttCloseBracket ) + { + RewindTo(&t); + return false; + } + } + + GetToken(&t2); + } + + if( t2.type != ttIdentifier ) + { + RewindTo(&t); + return false; + } + + GetToken(&t2); + if( t2.type == ttOpenParanthesis ) + { + // If the closing paranthesis is not followed by a + // statement block then it is not a function. + while( t2.type != ttCloseParanthesis && t2.type != ttEnd ) + GetToken(&t2); + + if( t2.type == ttEnd ) + return false; + else + { + // A class method can have a 'const' token after the parameter list + if( isMethod ) + { + GetToken(&t1); + if( t1.type != ttConst ) + RewindTo(&t1); + } + + GetToken(&t1); + RewindTo(&t); + if( t1.type == ttStartStatementBlock ) + return true; + } + + RewindTo(&t); + return false; + } + + RewindTo(&t); + return false; +} + +asCScriptNode *asCParser::ParseFunction(bool isMethod) +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snFunction); + + sToken t1,t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + + // If it is a global function, or a method, except constructor and destructor, then the return type is parsed + if( !isMethod || (t1.type != ttBitNot && t2.type != ttOpenParanthesis) ) + { + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + } + + // If this is a class destructor then it starts with ~, and no return type is declared + if( isMethod && t1.type == ttBitNot ) + { + node->AddChildLast(ParseToken(ttBitNot)); + if( isSyntaxError ) return node; + } + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseParameterList()); + if( isSyntaxError ) return node; + + if( isMethod ) + { + // Is the method a const? + GetToken(&t1); + RewindTo(&t1); + if( t1.type == ttConst ) + node->AddChildLast(ParseToken(ttConst)); + } + + // We should just find the end of the statement block here. The statements + // will be parsed on request by the compiler once it starts the compilation. + node->AddChildLast(SuperficiallyParseStatementBlock()); + + return node; +} + +asCScriptNode *asCParser::ParseInterfaceMethod() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snFunction); + + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseParameterList()); + if( isSyntaxError ) return node; + + // Parse an optional const after the method definition + sToken t1; + GetToken(&t1); + RewindTo(&t1); + if( t1.type == ttConst ) + node->AddChildLast(ParseToken(ttConst)); + + GetToken(&t1); + if( t1.type != ttEndStatement ) + { + Error(ExpectedToken(";").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseInterface() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snInterface); + + sToken t; + GetToken(&t); + if( t.type != ttInterface ) + { + Error(ExpectedToken("interface").AddressOf(), &t); + return node; + } + + node->SetToken(&t); + + node->AddChildLast(ParseIdentifier()); + + GetToken(&t); + if( t.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{").AddressOf(), &t); + return node; + } + + // Parse interface methods + GetToken(&t); + RewindTo(&t); + while( t.type != ttEndStatementBlock && t.type != ttEnd ) + { + // Parse the method signature + node->AddChildLast(ParseInterfaceMethod()); + if( isSyntaxError ) return node; + + GetToken(&t); + RewindTo(&t); + } + + GetToken(&t); + if( t.type != ttEndStatementBlock ) + { + Error(ExpectedToken("}").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseClass() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snClass); + + sToken t; + GetToken(&t); + if( t.type != ttClass ) + { + Error(ExpectedToken("class").AddressOf(), &t); + return node; + } + + node->SetToken(&t); + + if( engine->ep.allowImplicitHandleTypes ) + { + // Parse 'implicit handle class' construct + GetToken(&t); + + if ( t.type == ttHandle ) + node->SetToken(&t); + else + RewindTo(&t); + } + + node->AddChildLast(ParseIdentifier()); + + GetToken(&t); + + // Optional list of interfaces that are being implemented and classes that are being inherited + if( t.type == ttColon ) + { + node->AddChildLast(ParseIdentifier()); + GetToken(&t); + while( t.type == ttListSeparator ) + { + node->AddChildLast(ParseIdentifier()); + GetToken(&t); + } + } + + if( t.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{").AddressOf(), &t); + return node; + } + + // Parse properties + GetToken(&t); + RewindTo(&t); + while( t.type != ttEndStatementBlock && t.type != ttEnd ) + { + // Is it a property or a method? + if( IsFuncDecl(true) ) + { + // Parse the method + node->AddChildLast(ParseFunction(true)); + } + else if( IsVarDecl() ) + { + // Parse a property declaration + asCScriptNode *prop = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDeclaration); + node->AddChildLast(prop); + + prop->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + prop->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";").AddressOf(), &t); + return node; + } + prop->UpdateSourcePos(t.pos, t.length); + } + else + { + Error(TXT_EXPECTED_METHOD_OR_PROPERTY, &t); + return node; + } + + GetToken(&t); + RewindTo(&t); + } + + GetToken(&t); + if( t.type != ttEndStatementBlock ) + { + Error(ExpectedToken("}").AddressOf(), &t); + return node; + } + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseGlobalVar() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snGlobalVar); + + // Parse data type + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + sToken t; + + for(;;) + { + // Parse identifier + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + // Only superficially parse the initialization info for the variable + GetToken(&t); + RewindTo(&t); + if( t.type == ttAssignment || t.type == ttOpenParanthesis ) + { + node->AddChildLast(SuperficiallyParseGlobalVarInit()); + if( isSyntaxError ) return node; + } + + // continue if list separator, else terminate with end statement + GetToken(&t); + if( t.type == ttListSeparator ) + continue; + else if( t.type == ttEndStatement ) + { + node->UpdateSourcePos(t.pos, t.length); + + return node; + } + else + { + Error(ExpectedTokens(",", ";").AddressOf(), &t); + return node; + } + } + UNREACHABLE_RETURN; +} + +int asCParser::ParseGlobalVarInit(asCScriptCode *script, asCScriptNode *init) +{ + Reset(); + + // Tell the parser to validate the identifiers as valid types + checkValidTypes = true; + + this->script = script; + sourcePos = init->tokenPos; + + // If next token is assignment, parse expression + sToken t; + GetToken(&t); + if( t.type == ttAssignment ) + { + GetToken(&t); + RewindTo(&t); + if( t.type == ttStartStatementBlock ) + scriptNode = ParseInitList(); + else + scriptNode = ParseAssignment(); + } + else if( t.type == ttOpenParanthesis ) + { + RewindTo(&t); + scriptNode = ParseArgList(); + } + else + { + int tokens[] = {ttAssignment, ttOpenParanthesis}; + Error(ExpectedOneOf(tokens, 2).AddressOf(), &t); + } + + if( isSyntaxError || errorWhileParsing ) + return -1; + + return 0; +} + +asCScriptNode *asCParser::SuperficiallyParseGlobalVarInit() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snAssignment); + + sToken t; + GetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + if( t.type == ttAssignment ) + { + GetToken(&t); + if( t.type == ttStartStatementBlock ) + { + // Find the end of the initialization list + int indent = 1; + while( indent ) + { + GetToken(&t); + if( t.type == ttStartStatementBlock ) + indent++; + else if( t.type == ttEndStatementBlock ) + indent--; + else if( t.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + break; + } + } + } + else + { + // Find the end of the expression + int indent = 0; + while( indent || (t.type != ttListSeparator && t.type != ttEndStatement && t.type != ttEndStatementBlock) ) + { + if( t.type == ttOpenParanthesis ) + indent++; + else if( t.type == ttCloseParanthesis ) + indent--; + else if( t.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + break; + } + GetToken(&t); + } + + // Rewind so that the next token read is the list separator, end statement, or end statement block + RewindTo(&t); + } + } + else if( t.type == ttOpenParanthesis ) + { + // Find the end of the argument list + int indent = 1; + while( indent ) + { + GetToken(&t); + if( t.type == ttOpenParanthesis ) + indent++; + else if( t.type == ttCloseParanthesis ) + indent--; + else if( t.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t); + break; + } + } + } + else + { + int tokens[] = {ttAssignment, ttOpenParanthesis}; + Error(ExpectedOneOf(tokens, 2).AddressOf(), &t); + } + + return node; +} + +asCScriptNode *asCParser::ParseTypeMod(bool isParam) +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDataType); + + sToken t; + + // Parse possible & token + GetToken(&t); + RewindTo(&t); + if( t.type == ttAmp ) + { + node->AddChildLast(ParseToken(ttAmp)); + if( isSyntaxError ) return node; + + if( isParam ) + { + GetToken(&t); + RewindTo(&t); + + if( t.type == ttIn || t.type == ttOut || t.type == ttInOut ) + { + int tokens[3] = {ttIn, ttOut, ttInOut}; + node->AddChildLast(ParseOneOf(tokens, 3)); + } + } + } + + // Parse possible + token + GetToken(&t); + RewindTo(&t); + if( t.type == ttPlus ) + { + node->AddChildLast(ParseToken(ttPlus)); + if( isSyntaxError ) return node; + } + + return node; +} + +asCScriptNode *asCParser::ParseType(bool allowConst, bool allowVariableType) +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDataType); + + sToken t; + + if( allowConst ) + { + GetToken(&t); + RewindTo(&t); + if( t.type == ttConst ) + { + node->AddChildLast(ParseToken(ttConst)); + if( isSyntaxError ) return node; + } + } + + node->AddChildLast(ParseDataType(allowVariableType)); + + // If the datatype is a template type, then parse the subtype within the < > + asCScriptNode *type = node->lastChild; + asCString typeName; + typeName.Assign(&script->code[type->tokenPos], type->tokenLength); + if( engine->IsTemplateType(typeName.AddressOf()) ) + { + GetToken(&t); + if( t.type != ttLessThan ) + { + Error(ExpectedToken(asGetTokenDefinition(ttLessThan)).AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseType(true, false)); + if( isSyntaxError ) return node; + + // Accept >> and >>> tokens too. But then force the tokenizer to move + // only 1 character ahead (thus splitting the token in two). + GetToken(&t); + if( script->code[t.pos] != '>' ) + { + Error(ExpectedToken(asGetTokenDefinition(ttGreaterThan)).AddressOf(), &t); + return node; + } + else + { + // Break the token so that only the first > is parsed + sToken t2 = t; + t2.pos = t.pos + 1; + RewindTo(&t2); + } + } + + // Parse [] and @ + GetToken(&t); + RewindTo(&t); + while( t.type == ttOpenBracket || t.type == ttHandle) + { + if( t.type == ttOpenBracket ) + { + node->AddChildLast(ParseToken(ttOpenBracket)); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseBracket ) + { + Error(ExpectedToken("]").AddressOf(), &t); + return node; + } + } + else + { + node->AddChildLast(ParseToken(ttHandle)); + if( isSyntaxError ) return node; + } + + GetToken(&t); + RewindTo(&t); + } + + return node; +} + +asCScriptNode *asCParser::ParseToken(int token) +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snUndefined); + + sToken t1; + + GetToken(&t1); + if( t1.type != token ) + { + Error(ExpectedToken(asGetTokenDefinition(token)).AddressOf(), &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseOneOf(int *tokens, int count) +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snUndefined); + + sToken t1; + + GetToken(&t1); + int n; + for( n = 0; n < count; n++ ) + { + if( tokens[n] == t1.type ) + break; + } + if( n == count ) + { + Error(ExpectedOneOf(tokens, count).AddressOf(), &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + + +asCScriptNode *asCParser::ParseDataType(bool allowVariableType) +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDataType); + + sToken t1; + + GetToken(&t1); + if( !IsDataType(t1) && !(allowVariableType && t1.type == ttQuestion) ) + { + Error(TXT_EXPECTED_DATA_TYPE, &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseRealType() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDataType); + + sToken t1; + + GetToken(&t1); + if( !IsRealType(t1.type) ) + { + Error(TXT_EXPECTED_DATA_TYPE, &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseIdentifier() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snIdentifier); + + sToken t1; + + GetToken(&t1); + if( t1.type != ttIdentifier ) + { + Error(TXT_EXPECTED_IDENTIFIER, &t1); + return node; + } + + node->SetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseCast() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snCast); + + sToken t1; + GetToken(&t1); + if( t1.type != ttCast ) + { + Error(ExpectedToken("cast").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + GetToken(&t1); + if( t1.type != ttLessThan ) + { + Error(ExpectedToken("<").AddressOf(), &t1); + return node; + } + + // Parse the data type + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(false)); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttGreaterThan ) + { + Error(ExpectedToken(">").AddressOf(), &t1); + return node; + } + + GetToken(&t1); + if( t1.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t1); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseParameterList() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snParameterList); + + sToken t1; + GetToken(&t1); + if( t1.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + GetToken(&t1); + if( t1.type == ttCloseParanthesis ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + RewindTo(&t1); + + for(;;) + { + // Parse data type + node->AddChildLast(ParseType(true, isParsingAppInterface)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseTypeMod(true)); + if( isSyntaxError ) return node; + + // Parse identifier + GetToken(&t1); + if( t1.type == ttIdentifier ) + { + RewindTo(&t1); + + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + GetToken(&t1); + } + + // Check if list continues + if( t1.type == ttCloseParanthesis ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + return node; + } + else if( t1.type == ttListSeparator ) + continue; + else + { + Error(ExpectedTokens(")", ",").AddressOf(), &t1); + return node; + } + } + } + UNREACHABLE_RETURN; +} + +asCScriptNode *asCParser::ParseExprValue() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExprValue); + + sToken t1, t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + + // TODO: namespace: Datatypes can be defined in namespaces, thus types too must allow scope prefix + if( IsDataType(t1) && t2.type != ttScope ) + node->AddChildLast(ParseConstructCall()); + else if( t1.type == ttIdentifier || t1.type == ttScope ) + { + if( IsFunctionCall() ) + node->AddChildLast(ParseFunctionCall()); + else + node->AddChildLast(ParseVariableAccess()); + } + else if( t1.type == ttCast ) + node->AddChildLast(ParseCast()); + else if( IsConstant(t1.type) ) + node->AddChildLast(ParseConstant()); + else if( t1.type == ttOpenParanthesis ) + { + GetToken(&t1); + node->UpdateSourcePos(t1.pos, t1.length); + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type != ttCloseParanthesis ) + Error(ExpectedToken(")").AddressOf(), &t1); + + node->UpdateSourcePos(t1.pos, t1.length); + } + else + Error(TXT_EXPECTED_EXPRESSION_VALUE, &t1); + + return node; +} + +asCScriptNode *asCParser::ParseConstant() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snConstant); + + sToken t; + GetToken(&t); + if( !IsConstant(t.type) ) + { + Error(TXT_EXPECTED_CONSTANT, &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + // We want to gather a list of string constants to concatenate as children + if( t.type == ttStringConstant || t.type == ttMultilineStringConstant || t.type == ttHeredocStringConstant ) + RewindTo(&t); + + while( t.type == ttStringConstant || t.type == ttMultilineStringConstant || t.type == ttHeredocStringConstant ) + { + node->AddChildLast(ParseStringConstant()); + + GetToken(&t); + RewindTo(&t); + } + + return node; +} + +asCScriptNode *asCParser::ParseStringConstant() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snConstant); + + sToken t; + GetToken(&t); + if( t.type != ttStringConstant && t.type != ttMultilineStringConstant && t.type != ttHeredocStringConstant ) + { + Error(TXT_EXPECTED_STRING, &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseFunctionCall() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snFunctionCall); + + // Parse scope prefix + sToken t1, t2; + GetToken(&t1); + if( t1.type == ttScope ) + { + RewindTo(&t1); + node->AddChildLast(ParseToken(ttScope)); + GetToken(&t1); + } + GetToken(&t2); + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + RewindTo(&t1); + node->AddChildLast(ParseIdentifier()); + node->AddChildLast(ParseToken(ttScope)); + GetToken(&t1); + GetToken(&t2); + } + + RewindTo(&t1); + + // Parse the function name followed by the argument list + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseArgList()); + + return node; +} + +asCScriptNode *asCParser::ParseVariableAccess() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snVariableAccess); + + // Parse scope prefix + sToken t1, t2; + GetToken(&t1); + if( t1.type == ttScope ) + { + RewindTo(&t1); + node->AddChildLast(ParseToken(ttScope)); + GetToken(&t1); + } + GetToken(&t2); + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + RewindTo(&t1); + node->AddChildLast(ParseIdentifier()); + node->AddChildLast(ParseToken(ttScope)); + GetToken(&t1); + GetToken(&t2); + } + + RewindTo(&t1); + + // Parse the variable name + node->AddChildLast(ParseIdentifier()); + + return node; +} + +asCScriptNode *asCParser::ParseConstructCall() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snConstructCall); + + node->AddChildLast(ParseType(false)); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseArgList()); + + return node; +} + +asCScriptNode *asCParser::ParseArgList() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snArgList); + + sToken t1; + GetToken(&t1); + if( t1.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + GetToken(&t1); + if( t1.type == ttCloseParanthesis ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + RewindTo(&t1); + + for(;;) + { + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + // Check if list continues + GetToken(&t1); + if( t1.type == ttCloseParanthesis ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + return node; + } + else if( t1.type == ttListSeparator ) + continue; + else + { + Error(ExpectedTokens(")", ",").AddressOf(), &t1); + return node; + } + } + } + return 0; +} + +asCScriptNode *asCParser::SuperficiallyParseStatementBlock() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snStatementBlock); + + // This function will only superficially parse the statement block in order to find the end of it + sToken t1; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + int level = 1; + while( level > 0 && !isSyntaxError ) + { + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + level--; + else if( t1.type == ttStartStatementBlock ) + level++; + else if( t1.type == ttEnd ) + Error(TXT_UNEXPECTED_END_OF_FILE, &t1); + } + + node->UpdateSourcePos(t1.pos, t1.length); + + return node; +} + +asCScriptNode *asCParser::ParseStatementBlock() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snStatementBlock); + + sToken t1; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + for(;;) + { + while( !isSyntaxError ) + { + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + RewindTo(&t1); + + if( IsVarDecl() ) + node->AddChildLast(ParseDeclaration()); + else + node->AddChildLast(ParseStatement()); + } + } + + if( isSyntaxError ) + { + // Search for either ';', '{', '}', or end + GetToken(&t1); + while( t1.type != ttEndStatement && t1.type != ttEnd && + t1.type != ttStartStatementBlock && t1.type != ttEndStatementBlock ) + { + GetToken(&t1); + } + + // Skip this statement block + if( t1.type == ttStartStatementBlock ) + { + // Find the end of the block and skip nested blocks + int level = 1; + while( level > 0 ) + { + GetToken(&t1); + if( t1.type == ttStartStatementBlock ) level++; + if( t1.type == ttEndStatementBlock ) level--; + if( t1.type == ttEnd ) break; + } + } + else if( t1.type == ttEndStatementBlock ) + { + RewindTo(&t1); + } + else if( t1.type == ttEnd ) + { + Error(TXT_UNEXPECTED_END_OF_FILE, &t1); + return node; + } + + isSyntaxError = false; + } + } + UNREACHABLE_RETURN; +} + +asCScriptNode *asCParser::ParseInitList() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snInitList); + + sToken t1; + + GetToken(&t1); + if( t1.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{").AddressOf(), &t1); + return node; + } + + node->UpdateSourcePos(t1.pos, t1.length); + + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + RewindTo(&t1); + for(;;) + { + GetToken(&t1); + if( t1.type == ttListSeparator ) + { + // No expression + node->AddChildLast(new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snUndefined)); + + GetToken(&t1); + if( t1.type == ttEndStatementBlock ) + { + // No expression + node->AddChildLast(new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snUndefined)); + node->UpdateSourcePos(t1.pos, t1.length); + return node; + } + RewindTo(&t1); + } + else if( t1.type == ttEndStatementBlock ) + { + // No expression + node->AddChildLast(new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snUndefined)); + + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else if( t1.type == ttStartStatementBlock ) + { + RewindTo(&t1); + node->AddChildLast(ParseInitList()); + if( isSyntaxError ) return node; + + GetToken(&t1); + if( t1.type == ttListSeparator ) + continue; + else if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + Error(ExpectedTokens("}", ",").AddressOf(), &t1); + return node; + } + } + else + { + RewindTo(&t1); + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + + GetToken(&t1); + if( t1.type == ttListSeparator ) + continue; + else if( t1.type == ttEndStatementBlock ) + { + node->UpdateSourcePos(t1.pos, t1.length); + + // Statement block is finished + return node; + } + else + { + Error(ExpectedTokens("}", ",").AddressOf(), &t1); + return node; + } + } + } + } + UNREACHABLE_RETURN; +} + +bool asCParser::IsFunctionCall() +{ + sToken s; + sToken t1, t2; + + GetToken(&s); + t1 = s; + + // A function call may be prefixed with scope resolution + if( t1.type == ttScope ) + GetToken(&t1); + GetToken(&t2); + + while( t1.type == ttIdentifier && t2.type == ttScope ) + { + GetToken(&t1); + GetToken(&t2); + } + + // A function call starts with an identifier followed by an argument list + if( t1.type != ttIdentifier || IsDataType(t1) ) + { + RewindTo(&s); + return false; + } + + if( t2.type == ttOpenParanthesis ) + { + RewindTo(&s); + return true; + } + + RewindTo(&s); + return false; +} + +asCScriptNode *asCParser::ParseDeclaration() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDeclaration); + + // Parse data type + node->AddChildLast(ParseType(true)); + if( isSyntaxError ) return node; + + sToken t; + + for(;;) + { + // Parse identifier + node->AddChildLast(ParseIdentifier()); + if( isSyntaxError ) return node; + + // If next token is assignment, parse expression + GetToken(&t); + if( t.type == ttOpenParanthesis ) + { + RewindTo(&t); + node->AddChildLast(ParseArgList()); + if( isSyntaxError ) return node; + } + else if( t.type == ttAssignment ) + { + GetToken(&t); + RewindTo(&t); + if( t.type == ttStartStatementBlock ) + { + node->AddChildLast(ParseInitList()); + if( isSyntaxError ) return node; + } + else + { + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + } + } + else + RewindTo(&t); + + // continue if list separator, else terminate with end statement + GetToken(&t); + if( t.type == ttListSeparator ) + continue; + else if( t.type == ttEndStatement ) + { + node->UpdateSourcePos(t.pos, t.length); + + return node; + } + else + { + Error(ExpectedTokens(",", ";").AddressOf(), &t); + return node; + } + } + UNREACHABLE_RETURN; +} + +asCScriptNode *asCParser::ParseStatement() +{ + sToken t1; + + GetToken(&t1); + RewindTo(&t1); + + if( t1.type == ttIf ) + return ParseIf(); + else if( t1.type == ttFor ) + return ParseFor(); + else if( t1.type == ttWhile ) + return ParseWhile(); + else if( t1.type == ttReturn ) + return ParseReturn(); + else if( t1.type == ttStartStatementBlock ) + return ParseStatementBlock(); + else if( t1.type == ttBreak ) + return ParseBreak(); + else if( t1.type == ttContinue ) + return ParseContinue(); + else if( t1.type == ttDo ) + return ParseDoWhile(); + else if( t1.type == ttSwitch ) + return ParseSwitch(); + else + return ParseExpressionStatement(); +} + +asCScriptNode *asCParser::ParseExpressionStatement() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExpressionStatement); + + sToken t; + GetToken(&t); + if( t.type == ttEndStatement ) + { + node->UpdateSourcePos(t.pos, t.length); + + return node; + } + + RewindTo(&t); + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseSwitch() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snSwitch); + + sToken t; + GetToken(&t); + if( t.type != ttSwitch ) + { + Error(ExpectedToken("switch").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")").AddressOf(), &t); + return node; + } + + GetToken(&t); + if( t.type != ttStartStatementBlock ) + { + Error(ExpectedToken("{").AddressOf(), &t); + return node; + } + + while( !isSyntaxError ) + { + GetToken(&t); + + if( t.type == ttEndStatementBlock || t.type == ttDefault) + break; + + RewindTo(&t); + + if( t.type != ttCase ) + { + Error(ExpectedToken("case").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseCase()); + if( isSyntaxError ) return node; + } + + if( t.type == ttDefault) + { + RewindTo(&t); + + node->AddChildLast(ParseCase()); + if( isSyntaxError ) return node; + + GetToken(&t); + } + + if( t.type != ttEndStatementBlock ) + { + Error(ExpectedToken("}").AddressOf(), &t); + return node; + } + + return node; +} + +asCScriptNode *asCParser::ParseCase() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snCase); + + sToken t; + GetToken(&t); + if( t.type != ttCase && t.type != ttDefault ) + { + Error(ExpectedTokens("case", "default").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + if(t.type == ttCase) + { + node->AddChildLast(ParseExpression()); + } + + GetToken(&t); + if( t.type != ttColon ) + { + Error(ExpectedToken(":").AddressOf(), &t); + return node; + } + + // Parse statements until we find either of }, case, default, and break + GetToken(&t); + RewindTo(&t); + while( t.type != ttCase && + t.type != ttDefault && + t.type != ttEndStatementBlock && + t.type != ttBreak ) + { + + node->AddChildLast(ParseStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + RewindTo(&t); + } + + // If the case was ended with a break statement, add it to the node + if( t.type == ttBreak ) + node->AddChildLast(ParseBreak()); + + return node; +} + +asCScriptNode *asCParser::ParseIf() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snIf); + + sToken t; + GetToken(&t); + if( t.type != ttIf ) + { + Error(ExpectedToken("if").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttElse ) + { + // No else statement return already + RewindTo(&t); + return node; + } + + node->AddChildLast(ParseStatement()); + + return node; +} + +asCScriptNode *asCParser::ParseFor() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snFor); + + sToken t; + GetToken(&t); + if( t.type != ttFor ) + { + Error(ExpectedToken("for").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t); + return node; + } + + if( IsVarDecl() ) + node->AddChildLast(ParseDeclaration()); + else + node->AddChildLast(ParseExpressionStatement()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseExpressionStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + RewindTo(&t); + + asCScriptNode *n = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExpressionStatement); + node->AddChildLast(n); + n->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")").AddressOf(), &t); + return node; + } + } + + node->AddChildLast(ParseStatement()); + + return node; +} + + + + + +asCScriptNode *asCParser::ParseWhile() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snWhile); + + sToken t; + GetToken(&t); + if( t.type != ttWhile ) + { + Error(ExpectedToken("while").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseStatement()); + + return node; +} + +asCScriptNode *asCParser::ParseDoWhile() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snDoWhile); + + sToken t; + GetToken(&t); + if( t.type != ttDo ) + { + Error(ExpectedToken("do").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + node->AddChildLast(ParseStatement()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttWhile ) + { + Error(ExpectedToken("while").AddressOf(), &t); + return node; + } + + GetToken(&t); + if( t.type != ttOpenParanthesis ) + { + Error(ExpectedToken("(").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttCloseParanthesis ) + { + Error(ExpectedToken(")").AddressOf(), &t); + return node; + } + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";").AddressOf(), &t); + return node; + } + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseReturn() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snReturn); + + sToken t; + GetToken(&t); + if( t.type != ttReturn ) + { + Error(ExpectedToken("return").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type == ttEndStatement ) + { + node->UpdateSourcePos(t.pos, t.length); + return node; + } + + RewindTo(&t); + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttEndStatement ) + { + Error(ExpectedToken(";").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseBreak() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snBreak); + + sToken t; + GetToken(&t); + if( t.type != ttBreak ) + { + Error(ExpectedToken("break").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttEndStatement ) + Error(ExpectedToken(";").AddressOf(), &t); + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseContinue() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snContinue); + + sToken t; + GetToken(&t); + if( t.type != ttContinue ) + { + Error(ExpectedToken("continue").AddressOf(), &t); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + + GetToken(&t); + if( t.type != ttEndStatement ) + Error(ExpectedToken(";").AddressOf(), &t); + + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseAssignment() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snAssignment); + + node->AddChildLast(ParseCondition()); + if( isSyntaxError ) return node; + + sToken t; + GetToken(&t); + RewindTo(&t); + + if( IsAssignOperator(t.type) ) + { + node->AddChildLast(ParseAssignOperator()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + } + + return node; +} + +asCScriptNode *asCParser::ParseCondition() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snCondition); + + node->AddChildLast(ParseExpression()); + if( isSyntaxError ) return node; + + sToken t; + GetToken(&t); + if( t.type == ttQuestion ) + { + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + + GetToken(&t); + if( t.type != ttColon ) + { + Error(ExpectedToken(":").AddressOf(), &t); + return node; + } + + node->AddChildLast(ParseAssignment()); + if( isSyntaxError ) return node; + } + else + RewindTo(&t); + + return node; +} + +asCScriptNode *asCParser::ParseExpression() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExpression); + + node->AddChildLast(ParseExprTerm()); + if( isSyntaxError ) return node; + + for(;;) + { + sToken t; + GetToken(&t); + RewindTo(&t); + + if( !IsOperator(t.type) ) + return node; + + node->AddChildLast(ParseExprOperator()); + if( isSyntaxError ) return node; + + node->AddChildLast(ParseExprTerm()); + if( isSyntaxError ) return node; + } + UNREACHABLE_RETURN; +} + +asCScriptNode *asCParser::ParseExprTerm() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExprTerm); + + for(;;) + { + sToken t; + GetToken(&t); + RewindTo(&t); + if( !IsPreOperator(t.type) ) + break; + + node->AddChildLast(ParseExprPreOp()); + if( isSyntaxError ) return node; + } + + node->AddChildLast(ParseExprValue()); + if( isSyntaxError ) return node; + + + for(;;) + { + sToken t; + GetToken(&t); + RewindTo(&t); + if( !IsPostOperator(t.type) ) + return node; + + node->AddChildLast(ParseExprPostOp()); + if( isSyntaxError ) return node; + } + UNREACHABLE_RETURN; +} + +asCScriptNode *asCParser::ParseExprPreOp() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExprPreOp); + + sToken t; + GetToken(&t); + if( !IsPreOperator(t.type) ) + { + Error(TXT_EXPECTED_PRE_OPERATOR, &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseExprPostOp() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExprPostOp); + + sToken t; + GetToken(&t); + if( !IsPostOperator(t.type) ) + { + Error(TXT_EXPECTED_POST_OPERATOR, &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + if( t.type == ttDot ) + { + sToken t1, t2; + GetToken(&t1); + GetToken(&t2); + RewindTo(&t1); + if( t2.type == ttOpenParanthesis ) + node->AddChildLast(ParseFunctionCall()); + else + node->AddChildLast(ParseIdentifier()); + } + else if( t.type == ttOpenBracket ) + { + node->AddChildLast(ParseAssignment()); + + GetToken(&t); + if( t.type != ttCloseBracket ) + { + ExpectedToken("]"); + return node; + } + + node->UpdateSourcePos(t.pos, t.length); + } + + return node; +} + +asCScriptNode *asCParser::ParseExprOperator() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExprOperator); + + sToken t; + GetToken(&t); + if( !IsOperator(t.type) ) + { + Error(TXT_EXPECTED_OPERATOR, &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +asCScriptNode *asCParser::ParseAssignOperator() +{ + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snExprOperator); + + sToken t; + GetToken(&t); + if( !IsAssignOperator(t.type) ) + { + Error(TXT_EXPECTED_OPERATOR, &t); + return node; + } + + node->SetToken(&t); + node->UpdateSourcePos(t.pos, t.length); + + return node; +} + +void asCParser::GetToken(sToken *token) +{ + size_t sourceLength = script->codeLength; + + do + { + if( sourcePos >= sourceLength ) + { + token->type = ttEnd; + token->length = 0; + } + else + token->type = tokenizer.GetToken(&script->code[sourcePos], sourceLength - sourcePos, &token->length); + + token->pos = sourcePos; + + // Update state + sourcePos += token->length; + } + // Filter out whitespace and comments + while( token->type == ttWhiteSpace || + token->type == ttOnelineComment || + token->type == ttMultilineComment ); +} + +void asCParser::RewindTo(const sToken *token) +{ + sourcePos = token->pos; +} + +void asCParser::Error(const char *text, sToken *token) +{ + RewindTo(token); + + isSyntaxError = true; + errorWhileParsing = true; + + int row, col; + script->ConvertPosToRowCol(token->pos, &row, &col); + + if( builder ) + builder->WriteError(script->name.AddressOf(), text, row, col); +} + +bool asCParser::IsRealType(int tokenType) +{ + if( tokenType == ttVoid || + tokenType == ttInt || + tokenType == ttInt8 || + tokenType == ttInt16 || + tokenType == ttInt64 || + tokenType == ttUInt || + tokenType == ttUInt8 || + tokenType == ttUInt16 || + tokenType == ttUInt64 || + tokenType == ttFloat || + tokenType == ttBool || + tokenType == ttDouble ) + return true; + + return false; +} + + +bool asCParser::IsDataType(const sToken &token) +{ + if( token.type == ttIdentifier ) + { + if( checkValidTypes ) + { + // Check if this is a registered type + asCString str; + str.Assign(&script->code[token.pos], token.length); + if( !builder->GetObjectType(str.AddressOf()) ) + return false; + } + return true; + } + + if( IsRealType(token.type) ) + return true; + + return false; +} + +bool asCParser::IsOperator(int tokenType) +{ + if( tokenType == ttPlus || + tokenType == ttMinus || + tokenType == ttStar || + tokenType == ttSlash || + tokenType == ttPercent || + tokenType == ttAnd || + tokenType == ttOr || + tokenType == ttXor || + tokenType == ttEqual || + tokenType == ttNotEqual || + tokenType == ttLessThan || + tokenType == ttLessThanOrEqual || + tokenType == ttGreaterThan || + tokenType == ttGreaterThanOrEqual || + tokenType == ttAmp || + tokenType == ttBitOr || + tokenType == ttBitXor || + tokenType == ttBitShiftLeft || + tokenType == ttBitShiftRight || + tokenType == ttBitShiftRightArith || + tokenType == ttIs || + tokenType == ttNotIs ) + return true; + + return false; +} + +bool asCParser::IsAssignOperator(int tokenType) +{ + if( tokenType == ttAssignment || + tokenType == ttAddAssign || + tokenType == ttSubAssign || + tokenType == ttMulAssign || + tokenType == ttDivAssign || + tokenType == ttModAssign || + tokenType == ttAndAssign || + tokenType == ttOrAssign || + tokenType == ttXorAssign || + tokenType == ttShiftLeftAssign || + tokenType == ttShiftRightLAssign || + tokenType == ttShiftRightAAssign ) + return true; + + return false; +} + +bool asCParser::IsPreOperator(int tokenType) +{ + if( tokenType == ttMinus || + tokenType == ttPlus || + tokenType == ttNot || + tokenType == ttInc || + tokenType == ttDec || + tokenType == ttBitNot || + tokenType == ttHandle ) + return true; + return false; +} + +bool asCParser::IsPostOperator(int tokenType) +{ + if( tokenType == ttInc || + tokenType == ttDec || + tokenType == ttDot || + tokenType == ttOpenBracket ) + return true; + return false; +} + +bool asCParser::IsConstant(int tokenType) +{ + if( tokenType == ttIntConstant || + tokenType == ttFloatConstant || + tokenType == ttDoubleConstant || + tokenType == ttStringConstant || + tokenType == ttMultilineStringConstant || + tokenType == ttHeredocStringConstant || + tokenType == ttTrue || + tokenType == ttFalse || + tokenType == ttBitsConstant || + tokenType == ttNull ) + return true; + + return false; +} + +asCString asCParser::ExpectedToken(const char *token) +{ + asCString str; + + str.Format(TXT_EXPECTED_s, token); + + return str; +} + +asCString asCParser::ExpectedTokens(const char *t1, const char *t2) +{ + asCString str; + + str.Format(TXT_EXPECTED_s_OR_s, t1, t2); + + return str; +} + +asCString asCParser::ExpectedOneOf(int *tokens, int count) +{ + asCString str; + + str = TXT_EXPECTED_ONE_OF; + for( int n = 0; n < count; n++ ) + { + str += asGetTokenDefinition(tokens[n]); + if( n < count-1 ) + str += ", "; + } + + return str; +} + +// TODO: typedef: Typedefs should accept complex types as well +asCScriptNode *asCParser::ParseTypedef() +{ + // Create the typedef node + asCScriptNode *node = new(engine->memoryMgr.AllocScriptNode()) asCScriptNode(snTypedef); + + sToken token; + + GetToken(&token); + if( token.type != ttTypedef) + { + Error(ExpectedToken(asGetTokenDefinition(token.type)).AddressOf(), &token); + return node; + } + + node->SetToken(&token); + node->UpdateSourcePos(token.pos, token.length); + + // Parse the base type + GetToken(&token); + RewindTo(&token); + + // Make sure it is a primitive type (except ttVoid) + if( !IsRealType(token.type) || token.type == ttVoid ) + { + asCString str; + str.Format(TXT_UNEXPECTED_TOKEN_s, asGetTokenDefinition(token.type)); + Error(str.AddressOf(), &token); + return node; + } + + node->AddChildLast(ParseRealType()); + node->AddChildLast(ParseIdentifier()); + + // Check for the end of the typedef + GetToken(&token); + if( token.type != ttEndStatement ) + { + RewindTo(&token); + Error(ExpectedToken(asGetTokenDefinition(token.type)).AddressOf(), &token); + } + + return node; +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_parser.h b/AngelScript/source/as_parser.h new file mode 100644 index 000000000..d7c698000 --- /dev/null +++ b/AngelScript/source/as_parser.h @@ -0,0 +1,205 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_parser.h +// +// This class parses the script code and builds a tree for compilation +// + + + +/* + +TYPEDEF = 'typedef' REALTYPE IDENTIFIER ';' +ENUM = 'enum' IDENTIFIER '{' ENUMELEMENT? (',' ENUMELEMENT)* '}' +ENUMELEMENT = IDENTIFIER ('=' EXPRESSION) +SCRIPT = (FUNCTION | GLOBVAR | IMPORT | STRUCT | INTERFACE | TYPEDEF | ENUM)* +TYPE = 'const'? DATATYPE +TYPEMOD = ('&' ('in' | 'out' | 'inout')?)? +FUNCTION = TYPE TYPEMOD IDENTIFIER PARAMLIST BLOCK +IMPORT = 'import' TYPE TYPEMOD IDENTIFIER PARAMLIST 'from' STRING ';' +INTERFACE = 'interface' IDENTIFIER '{' (TYPE TYPEMOD IDENTIFIER PARAMLIST ';')* '}' ';' +GLOBVAR = TYPE IDENTIFIER ('=' (INITLIST | ASSIGNMENT))? (',' IDENTIFIER ('=' (INITLIST | ASSIGNMENT))?)* ';' +DATATYPE = REALTYPE | IDENTIFIER +REALTYPE = 'void' | 'bool' | 'float' | 'int' | 'uint' | 'bits' +PARAMLIST = '(' (TYPE TYPEMOD IDENTIFIER? (',' TYPE TYPEMOD IDENTIFIER?)*)? ')' +BLOCK = '{' (DECLARATION | STATEMENT)* '}' +DECLARATION = TYPE IDENTIFIER ('=' (INITLIST | ASSIGNMENT))? (',' IDENTIFIER ('=' (INITLIST | ASSIGNMENT))?)* ';' +STATEMENT = BLOCK | IF | WHILE | DOWHILE | RETURN | EXPRSTATEMENT | BREAK | CONTINUE +BREAK = 'break' ';' +CONTINUE = 'continue' ';' +EXPRSTATEMENT = ASSIGNMENT? ';' +FOR = 'for' '(' (DECLARATION | EXPRSTATEMENT) EXPRSTATEMENT ASSIGNMENT? ')' STATEMENT +IF = 'if' '(' ASSIGNMENT ')' STATEMENT ('else' STATEMENT)? +WHILE = 'while' '(' ASSIGNMENT ')' STATEMENT +DOWHILE = 'do' STATEMENT 'while' '(' ASSIGNMENT ')' ';' +RETURN = 'return' ASSIGNMENT? ';' +ASSIGNMENT = CONDITION (ASSIGNOP ASSIGNMENT)? +CONDITION = EXPRESSION ('?' ASSIGNMENT ':' ASSIGNMENT)? +EXPRESSION = TERM (OP TERM)* +TERM = PRE* VALUE POST* +VALUE = '(' ASSIGNMENT ')' | CONSTANT | IDENTIFIER | FUNCTIONCALL | CONVERSION | CAST +PRE = '-' | '+' | 'not' | '++' | '--' | '~' +POST = '++' | '--' | ('.' | '->') (IDENTIFIER | FUNCTIONCALL) | '[' ASSIGNMENT ']' +FUNCTIONCALL = IDENTIFIER ARGLIST +ARGLIST = '(' (ASSIGNMENT (',' ASSIGNMENT)*)? ')' +CONSTANT = "abc" | 123 | 123.1 | 'true' | 'false' | 0xFFFF +OP = 'and' | 'or' | + '==' | '!=' | '<' | '<=' | '>=' | '>' | + '+' | '-' | '*' | '/' | '%' | '|' | '&' | '^' | '<<' | '>>' | '>>>' +ASSIGNOP = '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '|=' | '&=' | '^=' | '<<=' | '>>=' | '>>>=' +CONVERSION = TYPE '(' ASSIGNMENT ')' +INITLIST = '{' ((INITLIST | ASSIGNMENT)? (',' (INITLIST | ASSIGNMENT)?)*)? '}' +CAST = 'cast' '<' TYPE '>' '(' ASSIGNMENT ')' + +*/ + +#ifndef AS_PARSER_H +#define AS_PARSER_H + +#include "as_scriptnode.h" +#include "as_scriptcode.h" +#include "as_builder.h" +#include "as_tokenizer.h" + +BEGIN_AS_NAMESPACE + +class asCParser +{ +public: + asCParser(asCBuilder *builder); + ~asCParser(); + + int ParseScript(asCScriptCode *script); + int ParseFunctionDefinition(asCScriptCode *script); + int ParsePropertyDeclaration(asCScriptCode *script); + int ParseDataType(asCScriptCode *script); + int ParseTemplateDecl(asCScriptCode *script); + + int ParseStatementBlock(asCScriptCode *script, asCScriptNode *block); + int ParseGlobalVarInit(asCScriptCode *script, asCScriptNode *init); + + asCScriptNode *GetScriptNode(); + +protected: + void Reset(); + + void GetToken(sToken *token); + void RewindTo(const sToken *token); + void Error(const char *text, sToken *token); + + asCScriptNode *ParseImport(); + asCScriptNode *ParseFunctionDefinition(); + + asCScriptNode *ParseScript(); + asCScriptNode *ParseType(bool allowConst, bool allowVariableType = false); + asCScriptNode *ParseTypeMod(bool isParam); + asCScriptNode *ParseFunction(bool isMethod = false); + asCScriptNode *ParseGlobalVar(); + asCScriptNode *ParseParameterList(); + asCScriptNode *SuperficiallyParseStatementBlock(); + asCScriptNode *SuperficiallyParseGlobalVarInit(); + asCScriptNode *ParseStatementBlock(); + asCScriptNode *ParseDeclaration(); + asCScriptNode *ParseStatement(); + asCScriptNode *ParseExpressionStatement(); + asCScriptNode *ParseSwitch(); + asCScriptNode *ParseCase(); + asCScriptNode *ParseIf(); + asCScriptNode *ParseFor(); + asCScriptNode *ParseWhile(); + asCScriptNode *ParseDoWhile(); + asCScriptNode *ParseReturn(); + asCScriptNode *ParseBreak(); + asCScriptNode *ParseContinue(); + asCScriptNode *ParseAssignment(); + asCScriptNode *ParseAssignOperator(); + asCScriptNode *ParseCondition(); + asCScriptNode *ParseExpression(); + asCScriptNode *ParseExprTerm(); + asCScriptNode *ParseExprOperator(); + asCScriptNode *ParseExprPreOp(); + asCScriptNode *ParseExprPostOp(); + asCScriptNode *ParseExprValue(); + asCScriptNode *ParseArgList(); + asCScriptNode *ParseDataType(bool allowVariableType = false); + asCScriptNode *ParseRealType(); + asCScriptNode *ParseIdentifier(); + asCScriptNode *ParseConstant(); + asCScriptNode *ParseStringConstant(); + asCScriptNode *ParseFunctionCall(); + asCScriptNode *ParseVariableAccess(); + asCScriptNode *ParseConstructCall(); + asCScriptNode *ParseToken(int token); + asCScriptNode *ParseOneOf(int *tokens, int num); + asCScriptNode *ParseClass(); + asCScriptNode *ParseInitList(); + asCScriptNode *ParseInterface(); + asCScriptNode *ParseInterfaceMethod(); + asCScriptNode *ParseCast(); + asCScriptNode *ParseEnumeration(); // Parse enumeration enum { X, Y } + asCScriptNode *ParseTypedef(); // Parse named type declaration + + bool IsVarDecl(); + bool IsFuncDecl(bool isMethod); + bool IsRealType(int tokenType); + bool IsDataType(const sToken &token); + bool IsOperator(int tokenType); + bool IsPreOperator(int tokenType); + bool IsPostOperator(int tokenType); + bool IsConstant(int tokenType); + bool IsAssignOperator(int tokenType); + bool IsFunctionCall(); + + bool CheckTemplateType(sToken &t); + + asCString ExpectedToken(const char *token); + asCString ExpectedTokens(const char *token1, const char *token2); + asCString ExpectedOneOf(int *tokens, int count); + + bool errorWhileParsing; + bool isSyntaxError; + bool checkValidTypes; + bool isParsingAppInterface; + + asCScriptEngine *engine; + asCBuilder *builder; + asCScriptCode *script; + asCScriptNode *scriptNode; + + asCTokenizer tokenizer; + size_t sourcePos; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_property.h b/AngelScript/source/as_property.h new file mode 100644 index 000000000..d4958c1ae --- /dev/null +++ b/AngelScript/source/as_property.h @@ -0,0 +1,100 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_property.h +// +// A class for storing object property information +// + + + +#ifndef AS_PROPERTY_H +#define AS_PROPERTY_H + +#include "as_string.h" +#include "as_datatype.h" +#include "as_atomic.h" +#include "as_scriptfunction.h" + +BEGIN_AS_NAMESPACE + +class asCObjectProperty +{ +public: + asCString name; + asCDataType type; + int byteOffset; +}; + +// TODO: functions: When function pointers are available, it will be possible to create a circular +// reference between a function pointer in global variable and a function. To +// resolve this I need to use a garbage collector. + +class asCGlobalProperty +{ +public: + asCGlobalProperty(); + ~asCGlobalProperty(); + + void AddRef(); + void Release(); + + void *GetAddressOfValue(); + void AllocateMemory(); + void SetRegisteredAddress(void *p); + + asCString name; + asCDataType type; + asUINT id; + asCScriptFunction *initFunc; + +protected: + // This is only stored for registered properties, and keeps the pointer given by the application + void *realAddress; + + bool memoryAllocated; + union + { + void *memory; + asQWORD storage; + }; + +protected: + // The global property structure is reference counted, so that the + // engine can keep track of how many references to the property there are. + friend class asCScriptEngine; + asCAtomic refCount; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_restore.cpp b/AngelScript/source/as_restore.cpp new file mode 100644 index 000000000..f3ec27ef0 --- /dev/null +++ b/AngelScript/source/as_restore.cpp @@ -0,0 +1,1478 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_restore.cpp +// +// Functions for saving and restoring module bytecode +// asCRestore was originally written by Dennis Bollyn, dennis@gyrbo.be + +#include "as_config.h" +#include "as_restore.h" +#include "as_bytecode.h" +#include "as_arrayobject.h" + +BEGIN_AS_NAMESPACE + +#define WRITE_NUM(N) stream->Write(&(N), sizeof(N)) +#define READ_NUM(N) stream->Read(&(N), sizeof(N)) + +asCRestore::asCRestore(asCModule* _module, asIBinaryStream* _stream, asCScriptEngine* _engine) + : module(_module), stream(_stream), engine(_engine) +{ +} + +int asCRestore::Save() +{ + unsigned long i, count; + + // Store everything in the same order that the builder parses scripts + + // Store enums + count = (asUINT)module->enumTypes.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; i++ ) + { + WriteObjectTypeDeclaration(module->enumTypes[i], false); + WriteObjectTypeDeclaration(module->enumTypes[i], true); + } + + // Store type declarations first + count = (asUINT)module->classTypes.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; i++ ) + { + // Store only the name of the class/interface types + WriteObjectTypeDeclaration(module->classTypes[i], false); + } + + // Now store all interface methods + for( i = 0; i < count; i++ ) + { + if( module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], true); + } + + // Then store the class methods, properties, and behaviours + for( i = 0; i < count; ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + WriteObjectTypeDeclaration(module->classTypes[i], true); + } + + // Store typedefs + count = (asUINT)module->typeDefs.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; i++ ) + { + WriteObjectTypeDeclaration(module->typeDefs[i], false); + WriteObjectTypeDeclaration(module->typeDefs[i], true); + } + + // scriptGlobals[] + count = (asUINT)module->scriptGlobals.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; ++i ) + WriteGlobalProperty(module->scriptGlobals[i]); + + // scriptFunctions[] + count = 0; + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + if( module->scriptFunctions[i]->objectType == 0 ) + count++; + WRITE_NUM(count); + for( i = 0; i < module->scriptFunctions.GetLength(); ++i ) + if( module->scriptFunctions[i]->objectType == 0 ) + WriteFunction(module->scriptFunctions[i]); + + // globalFunctions[] + count = (int)module->globalFunctions.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; i++ ) + { + WriteFunction(module->globalFunctions[i]); + } + + // bindInformations[] + count = (asUINT)module->bindInformations.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; ++i ) + { + WriteFunction(module->bindInformations[i]->importedFunctionSignature); + WriteString(&module->bindInformations[i]->importFromModule); + } + + // usedTypes[] + count = (asUINT)usedTypes.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; ++i ) + { + WriteObjectType(usedTypes[i]); + } + + // usedTypeIds[] + WriteUsedTypeIds(); + + // usedFunctions[] + WriteUsedFunctions(); + + // usedGlobalProperties[] + WriteUsedGlobalProps(); + + // usedStringConstants[] + WriteUsedStringConstants(); + + // TODO: Store script section names + + return asSUCCESS; +} + +int asCRestore::Restore() +{ + // Before starting the load, make sure that + // any existing resources have been freed + module->InternalReset(); + + unsigned long i, count; + + asCScriptFunction* func; + + // Read enums + READ_NUM(count); + module->enumTypes.Allocate(count, 0); + for( i = 0; i < count; i++ ) + { + asCObjectType *ot = asNEW(asCObjectType)(engine); + ReadObjectTypeDeclaration(ot, false); + engine->classTypes.PushLast(ot); + module->enumTypes.PushLast(ot); + ot->AddRef(); + ReadObjectTypeDeclaration(ot, true); + } + + // structTypes[] + // First restore the structure names, then the properties + READ_NUM(count); + module->classTypes.Allocate(count, 0); + for( i = 0; i < count; ++i ) + { + asCObjectType *ot = asNEW(asCObjectType)(engine); + ReadObjectTypeDeclaration(ot, false); + engine->classTypes.PushLast(ot); + module->classTypes.PushLast(ot); + ot->AddRef(); + + // Add script classes to the GC + if( (ot->GetFlags() & asOBJ_SCRIPT_OBJECT) && ot->GetSize() > 0 ) + engine->gc.AddScriptObjectToGC(ot, &engine->objectTypeBehaviours); + } + + // Read interface methods + for( i = 0; i < module->classTypes.GetLength(); i++ ) + { + if( module->classTypes[i]->IsInterface() ) + ReadObjectTypeDeclaration(module->classTypes[i], true); + } + + module->ResolveInterfaceIds(); + + // Read class methods, properties, and behaviours + for( i = 0; i < module->classTypes.GetLength(); ++i ) + { + if( !module->classTypes[i]->IsInterface() ) + ReadObjectTypeDeclaration(module->classTypes[i], true); + } + + // Read typedefs + READ_NUM(count); + module->typeDefs.Allocate(count, 0); + for( i = 0; i < count; i++ ) + { + asCObjectType *ot = asNEW(asCObjectType)(engine); + ReadObjectTypeDeclaration(ot, false); + engine->classTypes.PushLast(ot); + module->typeDefs.PushLast(ot); + ot->AddRef(); + ReadObjectTypeDeclaration(ot, true); + } + + // scriptGlobals[] + READ_NUM(count); + module->scriptGlobals.Allocate(count, 0); + for( i = 0; i < count; ++i ) + { + ReadGlobalProperty(); + } + + // scriptFunctions[] + READ_NUM(count); + for( i = 0; i < count; ++i ) + { + func = ReadFunction(); + } + + // globalFunctions[] + READ_NUM(count); + for( i = 0; i < count; ++i ) + { + func = ReadFunction(false, false); + + module->globalFunctions.PushLast(func); + func->AddRef(); + } + + // bindInformations[] + READ_NUM(count); + module->bindInformations.SetLength(count); + for(i=0;iimportedFunctionSignature = ReadFunction(false, false); + info->importedFunctionSignature->id = int(FUNC_IMPORTED + engine->importedFunctions.GetLength()); + engine->importedFunctions.PushLast(info); + ReadString(&info->importFromModule); + info->boundFunctionId = -1; + module->bindInformations[i] = info; + } + + // usedTypes[] + READ_NUM(count); + usedTypes.Allocate(count, 0); + for( i = 0; i < count; ++i ) + { + asCObjectType *ot = ReadObjectType(); + usedTypes.PushLast(ot); + } + + // usedTypeIds[] + ReadUsedTypeIds(); + + // usedFunctions[] + ReadUsedFunctions(); + + // usedGlobalProperties[] + ReadUsedGlobalProps(); + + // usedStringConstants[] + ReadUsedStringConstants(); + + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + TranslateFunction(module->scriptFunctions[i]); + for( i = 0; i < module->scriptGlobals.GetLength(); i++ ) + if( module->scriptGlobals[i]->initFunc ) + TranslateFunction(module->scriptGlobals[i]->initFunc); + + // Init system functions properly + engine->PrepareEngine(); + + // Add references for all functions + for( i = 0; i < module->scriptFunctions.GetLength(); i++ ) + module->scriptFunctions[i]->AddReferences(); + for( i = 0; i < module->scriptGlobals.GetLength(); i++ ) + if( module->scriptGlobals[i]->initFunc ) + module->scriptGlobals[i]->initFunc->AddReferences(); + + module->CallInit(); + + return 0; +} + +int asCRestore::FindStringConstantIndex(int id) +{ + for( asUINT i = 0; i < usedStringConstants.GetLength(); i++ ) + if( usedStringConstants[i] == id ) + return i; + + usedStringConstants.PushLast(id); + return int(usedStringConstants.GetLength() - 1); +} + +void asCRestore::WriteUsedStringConstants() +{ + asUINT count = (asUINT)usedStringConstants.GetLength(); + WRITE_NUM(count); + for( asUINT i = 0; i < count; ++i ) + WriteString(engine->stringConstants[i]); +} + +void asCRestore::ReadUsedStringConstants() +{ + asCString str; + + asUINT count; + READ_NUM(count); + usedStringConstants.SetLength(count); + for( asUINT i = 0; i < count; ++i ) + { + ReadString(&str); + usedStringConstants[i] = engine->AddConstantString(str.AddressOf(), str.GetLength()); + } +} + +void asCRestore::WriteUsedFunctions() +{ + asUINT count = (asUINT)usedFunctions.GetLength(); + WRITE_NUM(count); + + for( asUINT n = 0; n < usedFunctions.GetLength(); n++ ) + { + char c; + + // Write enough data to be able to uniquely identify the function upon load + + // Is the function from the module or the application? + c = usedFunctions[n]->module ? 'm' : 'a'; + WRITE_NUM(c); + + WriteFunctionSignature(usedFunctions[n]); + } +} + +void asCRestore::ReadUsedFunctions() +{ + asUINT count; + READ_NUM(count); + usedFunctions.SetLength(count); + + for( asUINT n = 0; n < usedFunctions.GetLength(); n++ ) + { + char c; + + // Read the data to be able to uniquely identify the function + + // Is the function from the module or the application? + READ_NUM(c); + + asCScriptFunction func(engine, c == 'm' ? module : 0, -1); + ReadFunctionSignature(&func); + + // Find the correct function + if( c == 'm' ) + { + for( asUINT i = 0; i < module->scriptFunctions.GetLength(); i++ ) + { + asCScriptFunction *f = module->scriptFunctions[i]; + if( !func.IsSignatureEqual(f) || + func.objectType != f->objectType || + func.funcType != f->funcType ) + continue; + + usedFunctions[n] = f; + break; + } + } + else + { + for( asUINT i = 0; i < engine->scriptFunctions.GetLength(); i++ ) + { + asCScriptFunction *f = engine->scriptFunctions[i]; + if( f == 0 || + !func.IsSignatureEqual(f) || + func.objectType != f->objectType ) + continue; + + usedFunctions[n] = f; + break; + } + } + + // Set the type to dummy so it won't try to release the id + func.funcType = -1; + } +} + +void asCRestore::WriteFunctionSignature(asCScriptFunction *func) +{ + asUINT i, count; + + WriteString(&func->name); + WriteDataType(&func->returnType); + count = (asUINT)func->parameterTypes.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; ++i ) + WriteDataType(&func->parameterTypes[i]); + + count = (asUINT)func->inOutFlags.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; ++i ) + WRITE_NUM(func->inOutFlags[i]); + + WRITE_NUM(func->funcType); + + WriteObjectType(func->objectType); + + WRITE_NUM(func->isReadOnly); +} + +void asCRestore::ReadFunctionSignature(asCScriptFunction *func) +{ + int i, count; + asCDataType dt; + int num; + + ReadString(&func->name); + ReadDataType(&func->returnType); + READ_NUM(count); + func->parameterTypes.Allocate(count, 0); + for( i = 0; i < count; ++i ) + { + ReadDataType(&dt); + func->parameterTypes.PushLast(dt); + } + + READ_NUM(count); + func->inOutFlags.Allocate(count, 0); + for( i = 0; i < count; ++i ) + { + READ_NUM(num); + func->inOutFlags.PushLast(static_cast(num)); + } + + READ_NUM(func->funcType); + + func->objectType = ReadObjectType(); + + READ_NUM(func->isReadOnly); +} + +void asCRestore::WriteFunction(asCScriptFunction* func) +{ + char c; + + // If there is no function, then store a null char + if( func == 0 ) + { + c = '\0'; + WRITE_NUM(c); + return; + } + + // First check if the function has been saved already + for( asUINT f = 0; f < savedFunctions.GetLength(); f++ ) + { + if( savedFunctions[f] == func ) + { + c = 'r'; + WRITE_NUM(c); + WRITE_NUM(f); + return; + } + } + + // Keep a reference to the function in the list + savedFunctions.PushLast(func); + + c = 'f'; + WRITE_NUM(c); + + asUINT i, count; + + WriteFunctionSignature(func); + + count = (asUINT)func->byteCode.GetLength(); + WRITE_NUM(count); + WriteByteCode(func->byteCode.AddressOf(), count); + + count = (asUINT)func->objVariablePos.GetLength(); + WRITE_NUM(count); + for( i = 0; i < count; ++i ) + { + WriteObjectType(func->objVariableTypes[i]); + WRITE_NUM(func->objVariablePos[i]); + } + + WRITE_NUM(func->stackNeeded); + + asUINT length = (asUINT)func->lineNumbers.GetLength(); + WRITE_NUM(length); + for( i = 0; i < length; ++i ) + WRITE_NUM(func->lineNumbers[i]); + + WRITE_NUM(func->vfTableIdx); + + // TODO: Write variables + + // TODO: Store script section index +} + +asCScriptFunction *asCRestore::ReadFunction(bool addToModule, bool addToEngine) +{ + char c; + READ_NUM(c); + + if( c == '\0' ) + { + // There is no function, so return a null pointer + return 0; + } + + if( c == 'r' ) + { + // This is a reference to a previously saved function + int index; + READ_NUM(index); + + return savedFunctions[index]; + } + + // Load the new function + asCScriptFunction *func = asNEW(asCScriptFunction)(engine,module,-1); + savedFunctions.PushLast(func); + + int i, count; + asCDataType dt; + int num; + + ReadFunctionSignature(func); + + if( func->funcType == asFUNC_SCRIPT ) + engine->gc.AddScriptObjectToGC(func, &engine->functionBehaviours); + + func->id = engine->GetNextScriptFunctionId(); + + READ_NUM(count); + func->byteCode.Allocate(count, 0); + ReadByteCode(func->byteCode.AddressOf(), count); + func->byteCode.SetLength(count); + + READ_NUM(count); + func->objVariablePos.Allocate(count, 0); + func->objVariableTypes.Allocate(count, 0); + for( i = 0; i < count; ++i ) + { + func->objVariableTypes.PushLast(ReadObjectType()); + READ_NUM(num); + func->objVariablePos.PushLast(num); + } + + READ_NUM(func->stackNeeded); + + int length; + READ_NUM(length); + func->lineNumbers.SetLength(length); + for( i = 0; i < length; ++i ) + READ_NUM(func->lineNumbers[i]); + + READ_NUM(func->vfTableIdx); + + if( addToModule ) + { + // The refCount is already 1 + module->scriptFunctions.PushLast(func); + } + if( addToEngine ) + engine->SetScriptFunction(func); + if( func->objectType ) + func->ComputeSignatureId(); + + return func; +} + +void asCRestore::WriteObjectTypeDeclaration(asCObjectType *ot, bool writeProperties) +{ + if( !writeProperties ) + { + // name + WriteString(&ot->name); + // size + int size = ot->size; + WRITE_NUM(size); + // flags + asDWORD flags = ot->flags; + WRITE_NUM(flags); + } + else + { + if( ot->flags & asOBJ_ENUM ) + { + // enumValues[] + int size = (int)ot->enumValues.GetLength(); + WRITE_NUM(size); + + for( int n = 0; n < size; n++ ) + { + WriteString(&ot->enumValues[n]->name); + WRITE_NUM(ot->enumValues[n]->value); + } + } + else if( ot->flags & asOBJ_TYPEDEF ) + { + eTokenType t = ot->templateSubType.GetTokenType(); + WRITE_NUM(t); + } + else + { + WriteObjectType(ot->derivedFrom); + + // interfaces[] + int size = (asUINT)ot->interfaces.GetLength(); + WRITE_NUM(size); + asUINT n; + for( n = 0; n < ot->interfaces.GetLength(); n++ ) + { + WriteObjectType(ot->interfaces[n]); + } + + // properties[] + size = (asUINT)ot->properties.GetLength(); + WRITE_NUM(size); + for( n = 0; n < ot->properties.GetLength(); n++ ) + { + WriteObjectProperty(ot->properties[n]); + } + + // behaviours + if( !ot->IsInterface() && ot->flags != asOBJ_TYPEDEF && ot->flags != asOBJ_ENUM ) + { + WriteFunction(engine->scriptFunctions[ot->beh.construct]); + WriteFunction(engine->scriptFunctions[ot->beh.destruct]); + WriteFunction(engine->scriptFunctions[ot->beh.factory]); + size = (int)ot->beh.constructors.GetLength() - 1; + WRITE_NUM(size); + for( n = 1; n < ot->beh.constructors.GetLength(); n++ ) + { + WriteFunction(engine->scriptFunctions[ot->beh.constructors[n]]); + WriteFunction(engine->scriptFunctions[ot->beh.factories[n]]); + } + } + + // methods[] + size = (int)ot->methods.GetLength(); + WRITE_NUM(size); + for( n = 0; n < ot->methods.GetLength(); n++ ) + { + WriteFunction(engine->scriptFunctions[ot->methods[n]]); + } + + // virtualFunctionTable[] + size = (int)ot->virtualFunctionTable.GetLength(); + WRITE_NUM(size); + for( n = 0; n < (asUINT)size; n++ ) + { + WriteFunction(ot->virtualFunctionTable[n]); + } + } + } +} + +void asCRestore::ReadObjectTypeDeclaration(asCObjectType *ot, bool readProperties) +{ + if( !readProperties ) + { + // name + ReadString(&ot->name); + // size + int size; + READ_NUM(size); + ot->size = size; + // flags + asDWORD flags; + READ_NUM(flags); + ot->flags = flags; + + // Use the default script class behaviours + ot->beh = engine->scriptTypeBehaviours.beh; + engine->scriptFunctions[ot->beh.addref]->AddRef(); + engine->scriptFunctions[ot->beh.release]->AddRef(); + engine->scriptFunctions[ot->beh.gcEnumReferences]->AddRef(); + engine->scriptFunctions[ot->beh.gcGetFlag]->AddRef(); + engine->scriptFunctions[ot->beh.gcGetRefCount]->AddRef(); + engine->scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRef(); + engine->scriptFunctions[ot->beh.gcSetFlag]->AddRef(); + engine->scriptFunctions[ot->beh.copy]->AddRef(); + engine->scriptFunctions[ot->beh.factory]->AddRef(); + engine->scriptFunctions[ot->beh.construct]->AddRef(); + for( asUINT i = 1; i < ot->beh.operators.GetLength(); i += 2 ) + engine->scriptFunctions[ot->beh.operators[i]]->AddRef(); + } + else + { + if( ot->flags & asOBJ_ENUM ) + { + int count; + READ_NUM(count); + ot->enumValues.Allocate(count, 0); + for( int n = 0; n < count; n++ ) + { + asSEnumValue *e = asNEW(asSEnumValue); + ReadString(&e->name); + READ_NUM(e->value); + ot->enumValues.PushLast(e); + } + } + else if( ot->flags & asOBJ_TYPEDEF ) + { + eTokenType t; + READ_NUM(t); + ot->templateSubType = asCDataType::CreatePrimitive(t, false); + } + else + { + ot->derivedFrom = ReadObjectType(); + if( ot->derivedFrom ) + ot->derivedFrom->AddRef(); + + // interfaces[] + int size; + READ_NUM(size); + ot->interfaces.Allocate(size,0); + int n; + for( n = 0; n < size; n++ ) + { + asCObjectType *intf = ReadObjectType(); + ot->interfaces.PushLast(intf); + } + + // properties[] + READ_NUM(size); + ot->properties.Allocate(size,0); + for( n = 0; n < size; n++ ) + { + asCObjectProperty *prop = asNEW(asCObjectProperty); + ReadObjectProperty(prop); + ot->properties.PushLast(prop); + } + + // behaviours + if( !ot->IsInterface() && ot->flags != asOBJ_TYPEDEF && ot->flags != asOBJ_ENUM ) + { + asCScriptFunction *func = ReadFunction(); + engine->scriptFunctions[ot->beh.construct]->Release(); + ot->beh.construct = func->id; + ot->beh.constructors[0] = func->id; + func->AddRef(); + + func = ReadFunction(); + if( func ) + { + ot->beh.destruct = func->id; + func->AddRef(); + } + + func = ReadFunction(); + engine->scriptFunctions[ot->beh.factory]->Release(); + ot->beh.factory = func->id; + ot->beh.factories[0] = func->id; + func->AddRef(); + + READ_NUM(size); + for( n = 0; n < size; n++ ) + { + asCScriptFunction *func = ReadFunction(); + ot->beh.constructors.PushLast(func->id); + func->AddRef(); + + func = ReadFunction(); + ot->beh.factories.PushLast(func->id); + func->AddRef(); + } + } + + // methods[] + READ_NUM(size); + for( n = 0; n < size; n++ ) + { + asCScriptFunction *func = ReadFunction(); + ot->methods.PushLast(func->id); + func->AddRef(); + } + + // virtualFunctionTable[] + READ_NUM(size); + for( n = 0; n < size; n++ ) + { + asCScriptFunction *func = ReadFunction(); + ot->virtualFunctionTable.PushLast(func); + func->AddRef(); + } + } + } +} + +void asCRestore::WriteString(asCString* str) +{ + asUINT len = (asUINT)str->GetLength(); + WRITE_NUM(len); + stream->Write(str->AddressOf(), (asUINT)len); +} + +void asCRestore::ReadString(asCString* str) +{ + asUINT len; + READ_NUM(len); + str->SetLength(len); + stream->Read(str->AddressOf(), len); +} + +void asCRestore::WriteGlobalProperty(asCGlobalProperty* prop) +{ + // TODO: We might be able to avoid storing the name and type of the global + // properties twice if we merge this with the WriteUsedGlobalProperties. + WriteString(&prop->name); + WriteDataType(&prop->type); + + // Store the initialization function + if( prop->initFunc ) + { + bool f = true; + WRITE_NUM(f); + + WriteFunction(prop->initFunc); + } + else + { + bool f = false; + WRITE_NUM(f); + } +} + +void asCRestore::ReadGlobalProperty() +{ + asCString name; + asCDataType type; + + ReadString(&name); + ReadDataType(&type); + + asCGlobalProperty *prop = module->AllocateGlobalProperty(name.AddressOf(), type); + + // Read the initialization function + bool f; + READ_NUM(f); + if( f ) + { + asCScriptFunction *func = ReadFunction(false, true); + + // refCount was already set to 1 + prop->initFunc = func; + } +} + +void asCRestore::WriteObjectProperty(asCObjectProperty* prop) +{ + WriteString(&prop->name); + WriteDataType(&prop->type); + WRITE_NUM(prop->byteOffset); +} + +void asCRestore::ReadObjectProperty(asCObjectProperty* prop) +{ + ReadString(&prop->name); + ReadDataType(&prop->type); + READ_NUM(prop->byteOffset); +} + +void asCRestore::WriteDataType(const asCDataType *dt) +{ + bool b; + int t = dt->GetTokenType(); + WRITE_NUM(t); + WriteObjectType(dt->GetObjectType()); + b = dt->IsObjectHandle(); + WRITE_NUM(b); + b = dt->IsReadOnly(); + WRITE_NUM(b); + b = dt->IsHandleToConst(); + WRITE_NUM(b); + b = dt->IsReference(); + WRITE_NUM(b); +} + +void asCRestore::ReadDataType(asCDataType *dt) +{ + eTokenType tokenType; + READ_NUM(tokenType); + asCObjectType *objType = ReadObjectType(); + bool isObjectHandle; + READ_NUM(isObjectHandle); + bool isReadOnly; + READ_NUM(isReadOnly); + bool isHandleToConst; + READ_NUM(isHandleToConst); + bool isReference; + READ_NUM(isReference); + + if( tokenType == ttIdentifier ) + *dt = asCDataType::CreateObject(objType, false); + else + *dt = asCDataType::CreatePrimitive(tokenType, false); + if( isObjectHandle ) + { + dt->MakeReadOnly(isHandleToConst); + dt->MakeHandle(true); + } + dt->MakeReadOnly(isReadOnly); + dt->MakeReference(isReference); +} + +void asCRestore::WriteObjectType(asCObjectType* ot) +{ + char ch; + + // Only write the object type name + if( ot ) + { + // Check for template instances/specializations + if( ot->templateSubType.GetTokenType() != ttUnrecognizedToken && + ot != engine->defaultArrayObjectType ) + { + ch = 'a'; + WRITE_NUM(ch); + + if( ot->templateSubType.IsObject() ) + { + ch = 's'; + WRITE_NUM(ch); + WriteObjectType(ot->templateSubType.GetObjectType()); + + if( ot->templateSubType.IsObjectHandle() ) + ch = 'h'; + else + ch = 'o'; + WRITE_NUM(ch); + } + else + { + ch = 't'; + WRITE_NUM(ch); + eTokenType t = ot->templateSubType.GetTokenType(); + WRITE_NUM(t); + } + } + else if( ot->flags & asOBJ_TEMPLATE_SUBTYPE ) + { + ch = 's'; + WRITE_NUM(ch); + WriteString(&ot->name); + } + else + { + ch = 'o'; + WRITE_NUM(ch); + WriteString(&ot->name); + } + } + else + { + ch = '\0'; + WRITE_NUM(ch); + // Write a null string + asDWORD null = 0; + WRITE_NUM(null); + } +} + +asCObjectType* asCRestore::ReadObjectType() +{ + asCObjectType *ot; + char ch; + READ_NUM(ch); + if( ch == 'a' ) + { + READ_NUM(ch); + if( ch == 's' ) + { + ot = ReadObjectType(); + asCDataType dt = asCDataType::CreateObject(ot, false); + + READ_NUM(ch); + if( ch == 'h' ) + dt.MakeHandle(true); + + dt.MakeArray(engine); + ot = dt.GetObjectType(); + + asASSERT(ot); + } + else + { + eTokenType tokenType; + READ_NUM(tokenType); + asCDataType dt = asCDataType::CreatePrimitive(tokenType, false); + dt.MakeArray(engine); + ot = dt.GetObjectType(); + + asASSERT(ot); + } + } + else if( ch == 's' ) + { + // Read the name of the template subtype + asCString typeName; + ReadString(&typeName); + + // Find the template subtype + for( asUINT n = 0; n < engine->templateSubTypes.GetLength(); n++ ) + { + if( engine->templateSubTypes[n] && engine->templateSubTypes[n]->name == typeName ) + { + ot = engine->templateSubTypes[n]; + break; + } + } + + // TODO: Should give a friendly error in case the template type isn't found + asASSERT(ot); + } + else + { + // Read the object type name + asCString typeName; + ReadString(&typeName); + + if( typeName.GetLength() && typeName != "_builtin_object_" ) + { + // Find the object type + ot = module->GetObjectType(typeName.AddressOf()); + if( !ot ) + ot = engine->GetObjectType(typeName.AddressOf()); + + asASSERT(ot); + } + else if( typeName == "_builtin_object_" ) + { + ot = &engine->scriptTypeBehaviours; + } + else + ot = 0; + } + + return ot; +} + +void asCRestore::WriteByteCode(asDWORD *bc, int length) +{ + while( length ) + { + asDWORD c = *(asBYTE*)bc; + + if( c == asBC_ALLOC ) + { + WRITE_NUM(*bc++); + asDWORD tmp[MAX_DATA_SIZE]; + int n; + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + tmp[n] = *bc++; + + // Translate the object type + asCObjectType *ot = *(asCObjectType**)tmp; + *(int*)tmp = FindObjectTypeIdx(ot); + + // Translate the constructor func id, if it is a script class + if( ot->flags & asOBJ_SCRIPT_OBJECT ) + *(int*)&tmp[AS_PTR_SIZE] = FindFunctionIndex(engine->scriptFunctions[*(int*)&tmp[AS_PTR_SIZE]]); + + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + WRITE_NUM(tmp[n]); + } + else if( c == asBC_FREE || + c == asBC_REFCPY || + c == asBC_OBJTYPE ) + { + WRITE_NUM(*bc++); + // Translate object type pointers into indices + asDWORD tmp[MAX_DATA_SIZE]; + int n; + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + tmp[n] = *bc++; + + *(int*)tmp = FindObjectTypeIdx(*(asCObjectType**)tmp); + + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + WRITE_NUM(tmp[n]); + } + else if( c == asBC_TYPEID ) + { + WRITE_NUM(*bc++); + + // Translate type ids into indices + asDWORD tmp[MAX_DATA_SIZE]; + int n; + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + tmp[n] = *bc++; + + *(int*)tmp = FindTypeIdIdx(*(int*)tmp); + + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + WRITE_NUM(tmp[n]); + } + else if( c == asBC_CALL || + c == asBC_CALLINTF || + c == asBC_CALLSYS ) + { + WRITE_NUM(*bc++); + + // Translate the function id + asDWORD tmp[MAX_DATA_SIZE]; + int n; + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + tmp[n] = *bc++; + + *(int*)tmp = FindFunctionIndex(engine->scriptFunctions[*(int*)tmp]); + + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + WRITE_NUM(tmp[n]); + } + else if( c == asBC_STR ) + { + asDWORD tmp = *bc++; + + // Translate the string constant id + asWORD *arg = ((asWORD*)&tmp)+1; + *arg = FindStringConstantIndex(*arg); + WRITE_NUM(tmp); + } + else if( c == asBC_CALLBND ) + { + WRITE_NUM(*bc++); + + // Translate the function id + int funcId = *bc++; + for( asUINT n = 0; n < module->bindInformations.GetLength(); n++ ) + if( module->bindInformations[n]->importedFunctionSignature->id == funcId ) + { + funcId = n; + break; + } + + WRITE_NUM(funcId); + } + else if( c == asBC_PGA || + c == asBC_LDG || + c == asBC_PshG4 || + c == asBC_LdGRdR4 || + c == asBC_CpyGtoV4 || + c == asBC_CpyVtoG4 || + c == asBC_SetG4 ) + { + WRITE_NUM(*bc++); + + // Translate global variable pointers into indices + asDWORD tmp[MAX_DATA_SIZE]; + int n; + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + tmp[n] = *bc++; + + *(int*)tmp = FindGlobalPropPtrIndex(*(void**)tmp); + + for( n = 0; n < asBCTypeSize[asBCInfo[c].type]-1; n++ ) + WRITE_NUM(tmp[n]); + } + else + { + // Store the bc as is + for( int n = 0; n < asBCTypeSize[asBCInfo[c].type]; n++ ) + WRITE_NUM(*bc++); + } + + length -= asBCTypeSize[asBCInfo[c].type]; + } +} + +void asCRestore::ReadByteCode(asDWORD *bc, int length) +{ + while( length ) + { + asDWORD c; + READ_NUM(c); + *bc = c; + bc += 1; + c = *(asBYTE*)&c; + + // Read the bc as is + for( int n = 1; n < asBCTypeSize[asBCInfo[c].type]; n++ ) + READ_NUM(*bc++); + + length -= asBCTypeSize[asBCInfo[c].type]; + } +} + +void asCRestore::WriteUsedTypeIds() +{ + asUINT count = (asUINT)usedTypeIds.GetLength(); + WRITE_NUM(count); + for( asUINT n = 0; n < count; n++ ) + WriteDataType(engine->GetDataTypeFromTypeId(usedTypeIds[n])); +} + +void asCRestore::ReadUsedTypeIds() +{ + asUINT n; + asUINT count; + READ_NUM(count); + usedTypeIds.SetLength(count); + for( n = 0; n < count; n++ ) + { + asCDataType dt; + ReadDataType(&dt); + usedTypeIds[n] = engine->GetTypeIdFromDataType(dt); + } +} + +int asCRestore::FindGlobalPropPtrIndex(void *ptr) +{ + int i = usedGlobalProperties.IndexOf(ptr); + if( i >= 0 ) return i; + + usedGlobalProperties.PushLast(ptr); + return (int)usedGlobalProperties.GetLength()-1; +} + +void asCRestore::WriteUsedGlobalProps() +{ + int c = (int)usedGlobalProperties.GetLength(); + WRITE_NUM(c); + + for( int n = 0; n < c; n++ ) + { + size_t *p = (size_t*)usedGlobalProperties[n]; + + // First search for the global in the module + char moduleProp = 0; + asCGlobalProperty *prop = 0; + for( int i = 0; i < (signed)module->scriptGlobals.GetLength(); i++ ) + { + if( p == module->scriptGlobals[i]->GetAddressOfValue() ) + { + prop = module->scriptGlobals[i]; + moduleProp = 1; + break; + } + } + + // If it is not in the module, it must be an application registered property + if( !prop ) + { + for( int i = 0; i < (signed)engine->registeredGlobalProps.GetLength(); i++ ) + { + if( engine->registeredGlobalProps[i]->GetAddressOfValue() == p ) + { + prop = engine->registeredGlobalProps[i]; + break; + } + } + } + + asASSERT(prop); + + // Store the name and type of the property so we can find it again on loading + WriteString(&prop->name); + WriteDataType(&prop->type); + + // Also store whether the property is a module property or a registered property + WRITE_NUM(moduleProp); + } +} + +void asCRestore::ReadUsedGlobalProps() +{ + int c; + READ_NUM(c); + + usedGlobalProperties.SetLength(c); + + for( int n = 0; n < c; n++ ) + { + asCString name; + asCDataType type; + char moduleProp; + + ReadString(&name); + ReadDataType(&type); + READ_NUM(moduleProp); + + // Find the real property + void *prop = 0; + if( moduleProp ) + { + for( asUINT p = 0; p < module->scriptGlobals.GetLength(); p++ ) + { + if( module->scriptGlobals[p]->name == name && + module->scriptGlobals[p]->type == type ) + { + prop = module->scriptGlobals[p]->GetAddressOfValue(); + break; + } + } + } + else + { + for( asUINT p = 0; p < engine->registeredGlobalProps.GetLength(); p++ ) + { + if( engine->registeredGlobalProps[p] && + engine->registeredGlobalProps[p]->name == name && + engine->registeredGlobalProps[p]->type == type ) + { + prop = engine->registeredGlobalProps[p]->GetAddressOfValue(); + break; + } + } + } + + // TODO: If the property isn't found, we must give an error + asASSERT(prop); + + usedGlobalProperties[n] = prop; + } +} + +//--------------------------------------------------------------------------------------------------- +// Miscellaneous +//--------------------------------------------------------------------------------------------------- + +int asCRestore::FindFunctionIndex(asCScriptFunction *func) +{ + asUINT n; + for( n = 0; n < usedFunctions.GetLength(); n++ ) + { + if( usedFunctions[n] == func ) + return n; + } + + usedFunctions.PushLast(func); + return (int)usedFunctions.GetLength() - 1; +} + +asCScriptFunction *asCRestore::FindFunction(int idx) +{ + return usedFunctions[idx]; +} + +void asCRestore::TranslateFunction(asCScriptFunction *func) +{ + asUINT n; + asDWORD *bc = func->byteCode.AddressOf(); + for( n = 0; n < func->byteCode.GetLength(); ) + { + int c = *(asBYTE*)&bc[n]; + if( c == asBC_FREE || + c == asBC_REFCPY || c == asBC_OBJTYPE ) + { + // Translate the index to the true object type + asPTRWORD *ot = (asPTRWORD*)&bc[n+1]; + *(asCObjectType**)ot = FindObjectType(*(int*)ot); + } + else if( c == asBC_TYPEID ) + { + // Translate the index to the type id + int *tid = (int*)&bc[n+1]; + *tid = FindTypeId(*tid); + } + else if( c == asBC_CALL || + c == asBC_CALLINTF || + c == asBC_CALLSYS ) + { + // Translate the index to the func id + int *fid = (int*)&bc[n+1]; + *fid = FindFunction(*fid)->id; + } + else if( c == asBC_ALLOC ) + { + // Translate the index to the true object type + asPTRWORD *arg = (asPTRWORD*)&bc[n+1]; + *(asCObjectType**)arg = FindObjectType(*(int*)arg); + + // If the object type is a script class then the constructor id must be translated + asCObjectType *ot = *(asCObjectType**)arg; + if( ot->flags & asOBJ_SCRIPT_OBJECT ) + { + int *fid = (int*)&bc[n+1+AS_PTR_SIZE]; + *fid = FindFunction(*fid)->id; + } + } + else if( c == asBC_STR ) + { + // Translate the index to the true string id + asWORD *arg = ((asWORD*)&bc[n])+1; + + *arg = usedStringConstants[*arg]; + } + else if( c == asBC_CALLBND ) + { + // Translate the function id + int *fid = (int*)&bc[n+1]; + *fid = module->bindInformations[*fid]->importedFunctionSignature->id; + } + else if( c == asBC_PGA || + c == asBC_LDG || + c == asBC_PshG4 || + c == asBC_LdGRdR4 || + c == asBC_CpyGtoV4 || + c == asBC_CpyVtoG4 || + c == asBC_SetG4 ) + { + // Translate the global var index to pointer + asPTRWORD *index = (asPTRWORD*)&bc[n+1]; + *(void**)index = usedGlobalProperties[*(int*)index]; + } + + n += asBCTypeSize[asBCInfo[c].type]; + } +} + +int asCRestore::FindTypeIdIdx(int typeId) +{ + asUINT n; + for( n = 0; n < usedTypeIds.GetLength(); n++ ) + { + if( usedTypeIds[n] == typeId ) + return n; + } + + usedTypeIds.PushLast(typeId); + return (int)usedTypeIds.GetLength() - 1; +} + +int asCRestore::FindTypeId(int idx) +{ + return usedTypeIds[idx]; +} + +int asCRestore::FindObjectTypeIdx(asCObjectType *obj) +{ + asUINT n; + for( n = 0; n < usedTypes.GetLength(); n++ ) + { + if( usedTypes[n] == obj ) + return n; + } + + usedTypes.PushLast(obj); + return (int)usedTypes.GetLength() - 1; +} + +asCObjectType *asCRestore::FindObjectType(int idx) +{ + return usedTypes[idx]; +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_restore.h b/AngelScript/source/as_restore.h new file mode 100644 index 000000000..d8548121c --- /dev/null +++ b/AngelScript/source/as_restore.h @@ -0,0 +1,120 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_restore.h +// +// Functions for saving and restoring module bytecode +// asCRestore was originally written by Dennis Bollyn, dennis@gyrbo.be + + +// TODO: This should be split in two, so that an application that doesn't compile any +// code but only loads precompiled code can link with only the bytecode loader + +#ifndef AS_RESTORE_H +#define AS_RESTORE_H + +#include "as_scriptengine.h" +#include "as_context.h" +#include "as_map.h" + +BEGIN_AS_NAMESPACE + +class asCRestore +{ +public: + asCRestore(asCModule *module, asIBinaryStream *stream, asCScriptEngine *engine); + + int Save(); + int Restore(); + +protected: + asCModule *module; + asIBinaryStream *stream; + asCScriptEngine *engine; + + void WriteString(asCString *str); + void WriteFunction(asCScriptFunction *func); + void WriteFunctionSignature(asCScriptFunction *func); + void WriteGlobalProperty(asCGlobalProperty *prop); + void WriteObjectProperty(asCObjectProperty *prop); + void WriteDataType(const asCDataType *dt); + void WriteObjectType(asCObjectType *ot); + void WriteObjectTypeDeclaration(asCObjectType *ot, bool writeProperties); + void WriteByteCode(asDWORD *bc, int length); + + void ReadString(asCString *str); + asCScriptFunction *ReadFunction(bool addToModule = true, bool addToEngine = true); + void ReadFunctionSignature(asCScriptFunction *func); + void ReadGlobalProperty(); + void ReadObjectProperty(asCObjectProperty *prop); + void ReadDataType(asCDataType *dt); + asCObjectType *ReadObjectType(); + void ReadObjectTypeDeclaration(asCObjectType *ot, bool readProperties); + void ReadByteCode(asDWORD *bc, int length); + + // Helper functions for storing variable data + int FindObjectTypeIdx(asCObjectType*); + asCObjectType *FindObjectType(int idx); + int FindTypeIdIdx(int typeId); + int FindTypeId(int idx); + int FindFunctionIndex(asCScriptFunction *func); + asCScriptFunction *FindFunction(int idx); + int FindGlobalPropPtrIndex(void *); + int FindStringConstantIndex(int id); + + // Intermediate data used for storing that which isn't constant, function id's, pointers, etc + void WriteUsedTypeIds(); + void WriteUsedFunctions(); + void WriteUsedGlobalProps(); + void WriteUsedStringConstants(); + + void ReadUsedTypeIds(); + void ReadUsedFunctions(); + void ReadUsedGlobalProps(); + void ReadUsedStringConstants(); + + // After loading, each function needs to be translated to update pointers, function ids, etc + void TranslateFunction(asCScriptFunction *func); + + // Temporary storage for persisting variable data + asCArray usedTypeIds; + asCArray usedTypes; + asCArray usedFunctions; + asCArray usedGlobalProperties; + asCArray usedStringConstants; + + asCArray savedFunctions; +}; + +END_AS_NAMESPACE + +#endif //AS_RESTORE_H diff --git a/AngelScript/source/as_scriptcode.cpp b/AngelScript/source/as_scriptcode.cpp new file mode 100644 index 000000000..1d4441e4e --- /dev/null +++ b/AngelScript/source/as_scriptcode.cpp @@ -0,0 +1,141 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2008 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptcode.cpp +// +// A container class for the script code to be compiled +// + + + +#include "as_config.h" +#include "as_scriptcode.h" + +BEGIN_AS_NAMESPACE + +asCScriptCode::asCScriptCode() +{ + lineOffset = 0; + code = 0; + codeLength = 0; + sharedCode = false; +} + +asCScriptCode::~asCScriptCode() +{ + if( !sharedCode && code ) + { + asDELETEARRAY(code); + } +} + +int asCScriptCode::SetCode(const char *name, const char *code, bool makeCopy) +{ + return SetCode(name, code, strlen(code), makeCopy); +} + +int asCScriptCode::SetCode(const char *name, const char *code, size_t length, bool makeCopy) +{ + this->name = name; + if( !sharedCode && this->code ) + { + asDELETEARRAY(this->code); + } + if( length == 0 ) + length = strlen(code); + if( makeCopy ) + { + this->code = asNEWARRAY(char,length); + memcpy((char*)this->code, code, length); + codeLength = length; + sharedCode = false; + } + else + { + codeLength = length; + this->code = const_cast(code); + sharedCode = true; + } + + // Find the positions of each line + linePositions.PushLast(0); + for( size_t n = 0; n < length; n++ ) + if( code[n] == '\n' ) linePositions.PushLast(n+1); + linePositions.PushLast(length); + + return 0; +} + +void asCScriptCode::ConvertPosToRowCol(size_t pos, int *row, int *col) +{ + if( linePositions.GetLength() == 0 ) + { + if( row ) *row = lineOffset; + if( col ) *col = 1; + return; + } + + // Do a binary search in the buffer + int max = (int)linePositions.GetLength() - 1; + int min = 0; + int i = max/2; + + for(;;) + { + if( linePositions[i] < pos ) + { + // Have we found the largest number < programPosition? + if( min == i ) break; + + min = i; + i = (max + min)/2; + } + else if( linePositions[i] > pos ) + { + // Have we found the smallest number > programPoisition? + if( max == i ) break; + + max = i; + i = (max + min)/2; + } + else + { + // We found the exact position + break; + } + } + + if( row ) *row = i + 1 + lineOffset; + if( col ) *col = (int)(pos - linePositions[i]) + 1; +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_scriptcode.h b/AngelScript/source/as_scriptcode.h new file mode 100644 index 000000000..6b2ed7de0 --- /dev/null +++ b/AngelScript/source/as_scriptcode.h @@ -0,0 +1,70 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptcode.h +// +// A container class for the script code to be compiled +// + + + +#ifndef AS_SCRIPTCODE_H +#define AS_SCRIPTCODE_H + +#include "as_array.h" +#include "as_string.h" + +BEGIN_AS_NAMESPACE + +class asCScriptCode +{ +public: + asCScriptCode(); + ~asCScriptCode(); + + int SetCode(const char *name, const char *code, bool makeCopy); + int SetCode(const char *name, const char *code, size_t length, bool makeCopy); + + void ConvertPosToRowCol(size_t pos, int *row, int *col); + + asCString name; + char *code; + size_t codeLength; + bool sharedCode; + int idx; + int lineOffset; + asCArray linePositions; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_scriptengine.cpp b/AngelScript/source/as_scriptengine.cpp new file mode 100644 index 000000000..efb6a0cfe --- /dev/null +++ b/AngelScript/source/as_scriptengine.cpp @@ -0,0 +1,4000 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptengine.cpp +// +// The implementation of the script engine interface +// + + +#include + +#include "as_config.h" +#include "as_scriptengine.h" +#include "as_builder.h" +#include "as_context.h" +#include "as_string_util.h" +#include "as_tokenizer.h" +#include "as_texts.h" +#include "as_module.h" +#include "as_callfunc.h" +#include "as_arrayobject.h" +#include "as_generic.h" +#include "as_scriptobject.h" +#include "as_compiler.h" + +BEGIN_AS_NAMESPACE + +extern "C" +{ + +AS_API const char * asGetLibraryVersion() +{ +#ifdef _DEBUG + return ANGELSCRIPT_VERSION_STRING " DEBUG"; +#else + return ANGELSCRIPT_VERSION_STRING; +#endif +} + +AS_API const char * asGetLibraryOptions() +{ + const char *string = " " + + // Options +#ifdef AS_MAX_PORTABILITY + "AS_MAX_PORTABILITY " +#endif +#ifdef AS_DEBUG + "AS_DEBUG " +#endif +#ifdef AS_NO_CLASS_METHODS + "AS_NO_CLASS_METHODS " +#endif +#ifdef AS_USE_DOUBLE_AS_FLOAT + "AS_USE_DOUBLE_AS_FLOAT " +#endif +#ifdef AS_64BIT_PTR + "AS_64BIT_PTR " +#endif +#ifdef AS_NO_THREADS + "AS_NO_THREADS " +#endif +#ifdef AS_NO_ATOMIC + "AS_NO_ATOMIC " +#endif + + // Target system +#ifdef AS_WIN + "AS_WIN " +#endif +#ifdef AS_LINUX + "AS_LINUX " +#endif +#ifdef AS_MAC + "AS_MAC " +#endif +#ifdef AS_BSD + "AS_BSD " +#endif +#ifdef AS_XBOX + "AS_XBOX " +#endif +#ifdef AS_XBOX360 + "AS_XBOX360 " +#endif +#ifdef AS_PSP + "AS_PSP " +#endif +#ifdef AS_PS2 + "AS_PS2 " +#endif +#ifdef AS_PS3 + "AS_PS3 " +#endif +#ifdef AS_DC + "AS_DC " +#endif +#ifdef AS_GC + "AS_GC " +#endif +#ifdef AS_WII + "AS_WII " +#endif +#ifdef AS_IPHONE + "AS_IPHONE " +#endif +#ifdef AS_ANDROID + "AS_ANDROID " +#endif + + // CPU family +#ifdef AS_PPC + "AS_PPC " +#endif +#ifdef AS_PPC_64 + "AS_PPC_64 " +#endif +#ifdef AS_X86 + "AS_X86 " +#endif +#ifdef AS_MIPS + "AS_MIPS " +#endif +#ifdef AS_SH4 + "AS_SH4 " +#endif +#ifdef AS_XENON + "AS_XENON " +#endif +#ifdef AS_ARM + "AS_ARM " +#endif + ; + + return string; +} + +AS_API asIScriptEngine *asCreateScriptEngine(asDWORD version) +{ + // Verify the version that the application expects + if( (version/10000) != (ANGELSCRIPT_VERSION/10000) ) + return 0; + + if( (version/100)%100 != (ANGELSCRIPT_VERSION/100)%100 ) + return 0; + + if( (version%100) > (ANGELSCRIPT_VERSION%100) ) + return 0; + + // Verify the size of the types + asASSERT( sizeof(asBYTE) == 1 ); + asASSERT( sizeof(asWORD) == 2 ); + asASSERT( sizeof(asDWORD) == 4 ); + asASSERT( sizeof(asQWORD) == 8 ); + asASSERT( sizeof(asPWORD) == sizeof(void*) ); + + // Verify the boolean type + asASSERT( sizeof(bool) == AS_SIZEOF_BOOL ); + asASSERT( true == VALUE_OF_BOOLEAN_TRUE ); + + // Verify endianess +#ifdef AS_BIG_ENDIAN + asASSERT( *(asDWORD*)"\x00\x01\x02\x03" == 0x00010203 ); + asASSERT( *(asQWORD*)"\x00\x01\x02\x03\x04\x05\x06\x07" == I64(0x0001020304050607) ); +#else + asASSERT( *(asDWORD*)"\x00\x01\x02\x03" == 0x03020100 ); + asASSERT( *(asQWORD*)"\x00\x01\x02\x03\x04\x05\x06\x07" == I64(0x0706050403020100) ); +#endif + + return asNEW(asCScriptEngine)(); +} + +int asCScriptEngine::SetEngineProperty(asEEngineProp property, asPWORD value) +{ + switch( property ) + { + case asEP_ALLOW_UNSAFE_REFERENCES: + ep.allowUnsafeReferences = value ? true : false; + break; + + case asEP_OPTIMIZE_BYTECODE: + ep.optimizeByteCode = value ? true : false; + break; + + case asEP_COPY_SCRIPT_SECTIONS: + ep.copyScriptSections = value ? true : false; + break; + + case asEP_MAX_STACK_SIZE: + // The size is given in bytes, but we only store dwords + ep.maximumContextStackSize = (int)value/4; + if( initialContextStackSize > ep.maximumContextStackSize ) + initialContextStackSize = ep.maximumContextStackSize; + break; + + case asEP_USE_CHARACTER_LITERALS: + ep.useCharacterLiterals = value ? true : false; + break; + + case asEP_ALLOW_MULTILINE_STRINGS: + ep.allowMultilineStrings = value ? true : false; + break; + + case asEP_ALLOW_IMPLICIT_HANDLE_TYPES: + ep.allowImplicitHandleTypes = value ? true : false; + break; + + case asEP_BUILD_WITHOUT_LINE_CUES: + ep.buildWithoutLineCues = value ? true : false; + break; + + case asEP_INIT_GLOBAL_VARS_AFTER_BUILD: + ep.initGlobalVarsAfterBuild = value ? true : false; + break; + + case asEP_REQUIRE_ENUM_SCOPE: + ep.requireEnumScope = value ? true : false; + break; + + case asEP_SCRIPT_SCANNER: + if( value <= 1 ) + ep.scanner = (int)value; + else + return asINVALID_ARG; + break; + + case asEP_INCLUDE_JIT_INSTRUCTIONS: + ep.includeJitInstructions = value ? true : false; + break; + + case asEP_STRING_ENCODING: + if( value <= 1 ) + ep.stringEncoding = (int)value; + else + return asINVALID_ARG; + break; + + default: + return asINVALID_ARG; + } + + return asSUCCESS; +} + +asPWORD asCScriptEngine::GetEngineProperty(asEEngineProp property) +{ + switch( property ) + { + case asEP_ALLOW_UNSAFE_REFERENCES: + return ep.allowUnsafeReferences; + + case asEP_OPTIMIZE_BYTECODE: + return ep.optimizeByteCode; + + case asEP_COPY_SCRIPT_SECTIONS: + return ep.copyScriptSections; + + case asEP_MAX_STACK_SIZE: + return ep.maximumContextStackSize*4; + + case asEP_USE_CHARACTER_LITERALS: + return ep.useCharacterLiterals; + + case asEP_ALLOW_MULTILINE_STRINGS: + return ep.allowMultilineStrings; + + case asEP_ALLOW_IMPLICIT_HANDLE_TYPES: + return ep.allowImplicitHandleTypes; + + case asEP_BUILD_WITHOUT_LINE_CUES: + return ep.buildWithoutLineCues; + + case asEP_INIT_GLOBAL_VARS_AFTER_BUILD: + return ep.initGlobalVarsAfterBuild; + + case asEP_REQUIRE_ENUM_SCOPE: + return ep.requireEnumScope; + + case asEP_SCRIPT_SCANNER: + return ep.scanner; + + case asEP_INCLUDE_JIT_INSTRUCTIONS: + return ep.includeJitInstructions; + + case asEP_STRING_ENCODING: + return ep.stringEncoding; + } + + return 0; +} + +} // extern "C" + + + + + +asCScriptEngine::asCScriptEngine() +{ + // Instanciate the thread manager + if( threadManager == 0 ) + threadManager = asNEW(asCThreadManager); + else + threadManager->AddRef(); + + // Engine properties + ep.allowUnsafeReferences = false; + ep.optimizeByteCode = true; + ep.copyScriptSections = true; + ep.maximumContextStackSize = 0; // no limit + ep.useCharacterLiterals = false; + ep.allowMultilineStrings = false; + ep.allowImplicitHandleTypes = false; + ep.buildWithoutLineCues = false; + ep.initGlobalVarsAfterBuild = true; + ep.requireEnumScope = false; + ep.scanner = 1; // utf8. 0 = ascii + ep.includeJitInstructions = false; + ep.stringEncoding = 0; // utf8. 1 = utf16 + + gc.engine = this; + + refCount.set(1); + stringFactory = 0; + configFailed = false; + isPrepared = false; + isBuilding = false; + lastModule = 0; + + + userData = 0; + + initialContextStackSize = 1024; // 1 KB + + + typeIdSeqNbr = 0; + currentGroup = &defaultGroup; + + msgCallback = 0; + jitCompiler = 0; + + // Reserve function id 0 for no function + scriptFunctions.PushLast(0); + + // Make sure typeId for the built-in primitives are defined according to asETypeIdFlags + int id; + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttVoid, false)); asASSERT( id == asTYPEID_VOID ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttBool, false)); asASSERT( id == asTYPEID_BOOL ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt8, false)); asASSERT( id == asTYPEID_INT8 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt16, false)); asASSERT( id == asTYPEID_INT16 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt, false)); asASSERT( id == asTYPEID_INT32 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttInt64, false)); asASSERT( id == asTYPEID_INT64 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt8, false)); asASSERT( id == asTYPEID_UINT8 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt16, false)); asASSERT( id == asTYPEID_UINT16 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt, false)); asASSERT( id == asTYPEID_UINT32 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttUInt64, false)); asASSERT( id == asTYPEID_UINT64 ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttFloat, false)); asASSERT( id == asTYPEID_FLOAT ); + id = GetTypeIdFromDataType(asCDataType::CreatePrimitive(ttDouble, false)); asASSERT( id == asTYPEID_DOUBLE ); + + defaultArrayObjectType = 0; + + RegisterArrayObject(this); + RegisterScriptObject(this); + RegisterScriptFunction(this); + RegisterObjectTypeGCBehaviours(this); +} + +asCScriptEngine::~asCScriptEngine() +{ + asASSERT(refCount.get() == 0); + asUINT n; + + // The modules must be deleted first, as they may use + // object types from the config groups + for( n = (asUINT)scriptModules.GetLength(); n-- > 0; ) + { + if( scriptModules[n] ) + { + asDELETE(scriptModules[n],asCModule); + } + } + scriptModules.SetLength(0); + + GarbageCollect(asGC_FULL_CYCLE); + + // Delete the functions for template types that may references object types + for( n = 0; n < templateTypes.GetLength(); n++ ) + { + if( templateTypes[n] ) + { + asUINT f; + + // Delete the factory stubs first + for( f = 0; f < templateTypes[n]->beh.factories.GetLength(); f++ ) + { + scriptFunctions[templateTypes[n]->beh.factories[f]]->Release(); + } + templateTypes[n]->beh.factories.Allocate(0, false); + + // Delete the specialized functions + for( f = 1; f < templateTypes[n]->beh.operators.GetLength(); f += 2 ) + { + if( scriptFunctions[templateTypes[n]->beh.operators[f]]->objectType == templateTypes[n] ) + { + scriptFunctions[templateTypes[n]->beh.operators[f]]->Release(); + templateTypes[n]->beh.operators[f] = 0; + } + } + } + } + + // Do one more garbage collect to free gc objects that were global variables + GarbageCollect(asGC_FULL_CYCLE); + FreeUnusedGlobalProperties(); + ClearUnusedTypes(); + + // Break all relationship between remaining class types and functions + for( n = 0; n < classTypes.GetLength(); n++ ) + { + if( classTypes[n] ) + classTypes[n]->ReleaseAllFunctions(); + + if( classTypes[n]->derivedFrom ) + { + classTypes[n]->derivedFrom->Release(); + classTypes[n]->derivedFrom = 0; + } + } + + GarbageCollect(asGC_FULL_CYCLE); + FreeUnusedGlobalProperties(); + ClearUnusedTypes(); + + asSMapNode *cursor = 0; + while( mapTypeIdToDataType.MoveFirst(&cursor) ) + { + asDELETE(mapTypeIdToDataType.GetValue(cursor),asCDataType); + mapTypeIdToDataType.Erase(cursor); + } + + defaultGroup.RemoveConfiguration(this); + while( configGroups.GetLength() ) + { + // Delete config groups in the right order + asCConfigGroup *grp = configGroups.PopLast(); + if( grp ) + { + asDELETE(grp,asCConfigGroup); + } + } + + for( n = 0; n < registeredGlobalProps.GetLength(); n++ ) + { + if( registeredGlobalProps[n] ) + { + asDELETE(registeredGlobalProps[n],asCGlobalProperty); + } + } + registeredGlobalProps.SetLength(0); + FreeUnusedGlobalProperties(); + + for( n = 0; n < templateTypes.GetLength(); n++ ) + { + if( templateTypes[n] ) + { + // Clear the sub type before deleting the template type so that the sub type isn't freed to soon + templateTypes[n]->templateSubType = asCDataType::CreateNullHandle(); + asDELETE(templateTypes[n],asCObjectType); + } + } + templateTypes.SetLength(0); + + for( n = 0; n < objectTypes.GetLength(); n++ ) + { + if( objectTypes[n] ) + { + // Clear the sub type before deleting the template type so that the sub type isn't freed to soon + objectTypes[n]->templateSubType = asCDataType::CreateNullHandle(); + asDELETE(objectTypes[n],asCObjectType); + } + } + objectTypes.SetLength(0); + for( n = 0; n < templateSubTypes.GetLength(); n++ ) + { + if( templateSubTypes[n] ) + { + asDELETE(templateSubTypes[n], asCObjectType); + } + } + templateSubTypes.SetLength(0); + registeredTypeDefs.SetLength(0); + registeredEnums.SetLength(0); + registeredObjTypes.SetLength(0); + + for( n = 0; n < registeredGlobalFuncs.GetLength(); n++ ) + { + if( registeredGlobalFuncs[n] ) + registeredGlobalFuncs[n]->Release(); + } + registeredGlobalFuncs.SetLength(0); + + scriptTypeBehaviours.ReleaseAllFunctions(); + functionBehaviours.ReleaseAllFunctions(); + objectTypeBehaviours.ReleaseAllFunctions(); + + // Free string constants + for( n = 0; n < stringConstants.GetLength(); n++ ) + { + asDELETE(stringConstants[n],asCString); + } + stringConstants.SetLength(0); + + // Free the script section names + for( n = 0; n < scriptSectionNames.GetLength(); n++ ) + { + asDELETE(scriptSectionNames[n],asCString); + } + scriptSectionNames.SetLength(0); + + // Release the thread manager + threadManager->Release(); +} + +// interface +int asCScriptEngine::AddRef() +{ + return refCount.atomicInc(); +} + +// interface +int asCScriptEngine::Release() +{ + int r = refCount.atomicDec(); + + if( r == 0 ) + { + asDELETE(this,asCScriptEngine); + return 0; + } + + return r; +} + +// interface +void *asCScriptEngine::SetUserData(void *data) +{ + void *old = userData; + userData = data; + return old; +} + +// interface +void *asCScriptEngine::GetUserData() +{ + return userData; +} + +// interface +int asCScriptEngine::SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv) +{ + msgCallback = true; + msgCallbackObj = obj; + bool isObj = false; + if( (unsigned)callConv == asCALL_GENERIC ) + { + msgCallback = false; + return asNOT_SUPPORTED; + } + if( (unsigned)callConv >= asCALL_THISCALL ) + { + isObj = true; + if( obj == 0 ) + { + msgCallback = false; + return asINVALID_ARG; + } + } + int r = DetectCallingConvention(isObj, callback, callConv, &msgCallbackFunc); + if( r < 0 ) msgCallback = false; + return r; +} + +// interface +int asCScriptEngine::ClearMessageCallback() +{ + msgCallback = false; + return 0; +} + +// interface +int asCScriptEngine::WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message) +{ + // Validate input parameters + if( section == 0 || + message == 0 ) + return asINVALID_ARG; + + // If there is no callback then there's nothing to do + if( !msgCallback ) + return 0; + + asSMessageInfo msg; + msg.section = section; + msg.row = row; + msg.col = col; + msg.type = type; + msg.message = message; + + if( msgCallbackFunc.callConv < ICC_THISCALL ) + CallGlobalFunction(&msg, msgCallbackObj, &msgCallbackFunc, 0); + else + CallObjectMethod(msgCallbackObj, &msg, &msgCallbackFunc, 0); + + return 0; +} + +int asCScriptEngine::SetJITCompiler(asIJITCompiler *compiler) +{ + jitCompiler = compiler; + return asSUCCESS; +} + +asIJITCompiler *asCScriptEngine::GetJITCompiler() +{ + return jitCompiler; +} + +// interface +asETokenClass asCScriptEngine::ParseToken(const char *string, size_t stringLength, int *tokenLength) +{ + if( stringLength == 0 ) + stringLength = strlen(string); + + size_t len; + asCTokenizer t; + asETokenClass tc; + t.GetToken(string, stringLength, &len, &tc); + + if( tokenLength ) + *tokenLength = (int)len; + + return tc; +} + +// interface +asIScriptModule *asCScriptEngine::GetModule(const char *module, asEGMFlags flag) +{ + asCModule *mod = GetModule(module, false); + + if( flag == asGM_ALWAYS_CREATE ) + { + if( mod != 0 ) + { + asDELETE(mod, asCModule); + } + return GetModule(module, true); + } + + if( mod == 0 && flag == asGM_CREATE_IF_NOT_EXISTS ) + { + return GetModule(module, true); + } + + return mod; +} + +// interface +int asCScriptEngine::DiscardModule(const char *module) +{ + asCModule *mod = GetModule(module, false); + if( mod == 0 ) return asNO_MODULE; + + asDELETE(mod, asCModule); + + FreeUnusedGlobalProperties(); + ClearUnusedTypes(); + + return 0; +} + +void asCScriptEngine::ClearUnusedTypes() +{ + // Build a list of all types to check for + asCArray types; + types = classTypes; + types.Concatenate(templateInstanceTypes); + + // Go through all modules + asUINT n; + for( n = 0; n < scriptModules.GetLength() && types.GetLength(); n++ ) + { + asCModule *mod = scriptModules[n]; + if( mod ) + { + // Functions/Methods/Globals are handled after this + + // Go through all type declarations + asUINT m; + for( m = 0; m < mod->classTypes.GetLength() && types.GetLength(); m++ ) + RemoveTypeAndRelatedFromList(types, mod->classTypes[m]); + for( m = 0; m < mod->enumTypes.GetLength() && types.GetLength(); m++ ) + RemoveTypeAndRelatedFromList(types, mod->enumTypes[m]); + for( m = 0; m < mod->typeDefs.GetLength() && types.GetLength(); m++ ) + RemoveTypeAndRelatedFromList(types, mod->typeDefs[m]); + } + } + + // Go through all function parameters and remove used types + for( n = 0; n < scriptFunctions.GetLength() && types.GetLength(); n++ ) + { + asCScriptFunction *func = scriptFunctions[n]; + if( func ) + { + // Ignore factory stubs + if( func->name == "factstub" ) + continue; + + asCObjectType *ot = func->returnType.GetObjectType(); + if( ot != 0 && ot != func->objectType ) + if( func->name != ot->name ) + RemoveTypeAndRelatedFromList(types, ot); + + for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) + { + ot = func->parameterTypes[p].GetObjectType(); + if( ot != 0 && ot != func->objectType ) + if( func->name != ot->name ) + RemoveTypeAndRelatedFromList(types, ot); + } + } + } + + // Go through all global properties + for( n = 0; n < globalProperties.GetLength() && types.GetLength(); n++ ) + { + if( globalProperties[n] && globalProperties[n]->type.GetObjectType() ) + RemoveTypeAndRelatedFromList(types, globalProperties[n]->type.GetObjectType()); + } + + // All that remains in the list after this can be discarded, since they are no longer used + for(;;) + { + bool didClearTemplateInstanceType = false; + + for( n = 0; n < types.GetLength(); n++ ) + { + // Template types and script classes will have two references for each factory stub + int refCount = ((types[n]->flags & asOBJ_TEMPLATE) || (types[n]->flags & asOBJ_SCRIPT_OBJECT)) ? 2*(int)types[n]->beh.factories.GetLength() : 0; + + if( types[n]->GetRefCount() == refCount ) + { + if( types[n]->flags & asOBJ_TEMPLATE ) + { + didClearTemplateInstanceType = true; + RemoveTemplateInstanceType(types[n]); + } + else + { + RemoveFromTypeIdMap(types[n]); + asDELETE(types[n],asCObjectType); + + int i = classTypes.IndexOf(types[n]); + if( i == (signed)classTypes.GetLength() - 1 ) + classTypes.PopLast(); + else + classTypes[i] = classTypes.PopLast(); + } + + // Remove the type from the array + if( n < types.GetLength() - 1 ) + types[n] = types.PopLast(); + else + types.PopLast(); + n--; + } + } + + if( didClearTemplateInstanceType == false ) + break; + } +} + +void asCScriptEngine::RemoveTypeAndRelatedFromList(asCArray &types, asCObjectType *ot) +{ + // Remove the type from the list + int i = types.IndexOf(ot); + if( i == -1 ) return; + + if( i == (signed)types.GetLength() - 1 ) + types.PopLast(); + else + types[i] = types.PopLast(); + + // If the type is an template type, then remove all sub types as well + if( ot->templateSubType.GetObjectType() ) + { + while( ot->templateSubType.GetObjectType() ) + { + ot = ot->templateSubType.GetObjectType(); + RemoveTypeAndRelatedFromList(types, ot); + } + return; + } + + // If the type is a class, then remove all properties types as well + if( ot->properties.GetLength() ) + { + for( asUINT n = 0; n < ot->properties.GetLength(); n++ ) + RemoveTypeAndRelatedFromList(types, ot->properties[n]->type.GetObjectType()); + } +} + + +// internal +int asCScriptEngine::GetFactoryIdByDecl(const asCObjectType *ot, const char *decl) +{ + asCModule *mod = 0; + + // Is this a script class? + if( ot->flags & asOBJ_SCRIPT_OBJECT && ot->size > 0 ) + mod = scriptFunctions[ot->beh.factory]->module; + + asCBuilder bld(this, mod); + + asCScriptFunction func(this, mod,-1); + int r = bld.ParseFunctionDeclaration(0, decl, &func, false); + if( r < 0 ) + return asINVALID_DECLARATION; + + // Search for matching factory function + int id = -1; + for( size_t n = 0; n < ot->beh.factories.GetLength(); n++ ) + { + asCScriptFunction *f = scriptFunctions[ot->beh.factories[n]]; + if( f->IsSignatureEqual(&func) ) + { + id = ot->beh.factories[n]; + break; + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + + +// internal +int asCScriptEngine::GetMethodIdByDecl(const asCObjectType *ot, const char *decl, asCModule *mod) +{ + asCBuilder bld(this, mod); + + asCScriptFunction func(this, mod, -1); + int r = bld.ParseFunctionDeclaration(0, decl, &func, false); + if( r < 0 ) + return asINVALID_DECLARATION; + + // Set the object type so that the signature can be properly compared + // This cast is OK, it will only be used for comparison + func.objectType = const_cast(ot); + + // Search script functions for matching interface + int id = -1; + for( size_t n = 0; n < ot->methods.GetLength(); ++n ) + { + if( func.IsSignatureEqual(scriptFunctions[ot->methods[n]]) ) + { + if( id == -1 ) + id = ot->methods[n]; + else + return asMULTIPLE_FUNCTIONS; + } + } + + if( id == -1 ) return asNO_FUNCTION; + + return id; +} + + +// Internal +asCString asCScriptEngine::GetFunctionDeclaration(int funcID) +{ + asCString str; + asCScriptFunction *func = GetScriptFunction(funcID); + if( func ) + str = func->GetDeclarationStr(); + + return str; +} + +asCScriptFunction *asCScriptEngine::GetScriptFunction(int funcId) +{ + if( funcId < 0 || funcId >= (int)scriptFunctions.GetLength() ) + return 0; + + return scriptFunctions[funcId]; +} + + + +asIScriptContext *asCScriptEngine::CreateContext() +{ + asIScriptContext *ctx = 0; + CreateContext(&ctx, false); + return ctx; +} + +int asCScriptEngine::CreateContext(asIScriptContext **context, bool isInternal) +{ + *context = asNEW(asCContext)(this, !isInternal); + + // We need to make sure the engine has been + // prepared before any context is executed + PrepareEngine(); + + return 0; +} + + +int asCScriptEngine::RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset) +{ + int r; + asCDataType dt; + asCBuilder bld(this, 0); + r = bld.ParseDataType(obj, &dt); + if( r < 0 ) + return ConfigError(r); + + // Verify that the correct config group is used + if( currentGroup->FindType(dt.GetObjectType()->name.AddressOf()) == 0 ) + return ConfigError(asWRONG_CONFIG_GROUP); + + asCDataType type; + asCString name; + + if( (r = bld.VerifyProperty(&dt, declaration, name, type)) < 0 ) + return ConfigError(r); + + // Store the property info + if( dt.GetObjectType() == 0 ) + return ConfigError(asINVALID_OBJECT); + + asCObjectProperty *prop = asNEW(asCObjectProperty); + prop->name = name; + prop->type = type; + prop->byteOffset = byteOffset; + + dt.GetObjectType()->properties.PushLast(prop); + + currentGroup->RefConfigGroup(FindConfigGroupForObjectType(type.GetObjectType())); + + return asSUCCESS; +} + +int asCScriptEngine::RegisterInterface(const char *name) +{ + if( name == 0 ) return ConfigError(asINVALID_NAME); + + // Verify if the name has been registered as a type already + asUINT n; + for( n = 0; n < objectTypes.GetLength(); n++ ) + { + if( objectTypes[n] && objectTypes[n]->name == name ) + return asALREADY_REGISTERED; + } + + // Use builder to parse the datatype + asCDataType dt; + asCBuilder bld(this, 0); + bool oldMsgCallback = msgCallback; msgCallback = false; + int r = bld.ParseDataType(name, &dt); + msgCallback = oldMsgCallback; + if( r >= 0 ) return ConfigError(asERROR); + + // Make sure the name is not a reserved keyword + asCTokenizer t; + size_t tokenLen; + int token = t.GetToken(name, strlen(name), &tokenLen); + if( token != ttIdentifier || strlen(name) != tokenLen ) + return ConfigError(asINVALID_NAME); + + r = bld.CheckNameConflict(name, 0, 0); + if( r < 0 ) + return ConfigError(asNAME_TAKEN); + + // Don't have to check against members of object + // types as they are allowed to use the names + + // Register the object type for the interface + asCObjectType *st = asNEW(asCObjectType)(this); + st->flags = asOBJ_REF | asOBJ_SCRIPT_OBJECT; + st->size = 0; // Cannot be instanciated + st->name = name; + + // Use the default script class behaviours + st->beh.factory = 0; + st->beh.addref = scriptTypeBehaviours.beh.addref; + scriptFunctions[st->beh.addref]->AddRef(); + st->beh.release = scriptTypeBehaviours.beh.release; + scriptFunctions[st->beh.release]->AddRef(); + st->beh.copy = 0; + + objectTypes.PushLast(st); + registeredObjTypes.PushLast(st); + + currentGroup->objTypes.PushLast(st); + + return asSUCCESS; +} + +int asCScriptEngine::RegisterInterfaceMethod(const char *intf, const char *declaration) +{ + // Verify that the correct config group is set. + if( currentGroup->FindType(intf) == 0 ) + return ConfigError(asWRONG_CONFIG_GROUP); + + asCDataType dt; + asCBuilder bld(this, 0); + int r = bld.ParseDataType(intf, &dt); + if( r < 0 ) + return ConfigError(r); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_INTERFACE); + func->objectType = dt.GetObjectType(); + + r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, false); + if( r < 0 ) + { + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_DECLARATION); + } + + // Check name conflicts + r = bld.CheckNameConflictMember(dt.GetObjectType(), func->name.AddressOf(), 0, 0); + if( r < 0 ) + { + asDELETE(func,asCScriptFunction); + return ConfigError(asNAME_TAKEN); + } + + func->id = GetNextScriptFunctionId(); + SetScriptFunction(func); + func->objectType->methods.PushLast(func->id); + // The refCount was already set to 1 + + func->ComputeSignatureId(); + + // If parameter type from other groups are used, add references + // TODO: The code for adding references to config groups is repeated in a lot of places + if( func->returnType.GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); + currentGroup->RefConfigGroup(group); + } + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( func->parameterTypes[n].GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType()); + currentGroup->RefConfigGroup(group); + } + } + + // Return function id as success + return func->id; +} + +int asCScriptEngine::RegisterObjectType(const char *name, int byteSize, asDWORD flags) +{ + int r; + + isPrepared = false; + + // Verify flags + // Must have either asOBJ_REF or asOBJ_VALUE + if( flags & asOBJ_REF ) + { + // Can optionally have the asOBJ_GC, asOBJ_NOHANDLE, asOBJ_SCOPED, or asOBJ_TEMPLATE flag set, but nothing else + if( flags & ~(asOBJ_REF | asOBJ_GC | asOBJ_NOHANDLE | asOBJ_SCOPED | asOBJ_TEMPLATE) ) + return ConfigError(asINVALID_ARG); + + // flags are exclusive + if( (flags & asOBJ_GC) && (flags & (asOBJ_NOHANDLE|asOBJ_SCOPED)) ) + return ConfigError(asINVALID_ARG); + if( (flags & asOBJ_NOHANDLE) && (flags & (asOBJ_GC|asOBJ_SCOPED)) ) + return ConfigError(asINVALID_ARG); + if( (flags & asOBJ_SCOPED) && (flags & (asOBJ_GC|asOBJ_NOHANDLE)) ) + return ConfigError(asINVALID_ARG); + } + else if( flags & asOBJ_VALUE ) + { + // Cannot use reference flags + // TODO: template: Should be possible to register a value type as template type + if( flags & (asOBJ_REF | asOBJ_GC | asOBJ_SCOPED) ) + return ConfigError(asINVALID_ARG); + + // If the app type is given, we must validate the flags + if( flags & asOBJ_APP_CLASS ) + { + // Must not set the primitive or float flag + if( flags & (asOBJ_APP_PRIMITIVE | + asOBJ_APP_FLOAT) ) + return ConfigError(asINVALID_ARG); + } + else if( flags & asOBJ_APP_PRIMITIVE ) + { + // Must not set the class flags nor the float flag + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_CLASS_CONSTRUCTOR | + asOBJ_APP_CLASS_DESTRUCTOR | + asOBJ_APP_CLASS_ASSIGNMENT | + asOBJ_APP_FLOAT) ) + return ConfigError(asINVALID_ARG); + } + else if( flags & asOBJ_APP_FLOAT ) + { + // Must not set the class flags nor the primitive flag + if( flags & (asOBJ_APP_CLASS | + asOBJ_APP_CLASS_CONSTRUCTOR | + asOBJ_APP_CLASS_DESTRUCTOR | + asOBJ_APP_CLASS_ASSIGNMENT | + asOBJ_APP_PRIMITIVE) ) + return ConfigError(asINVALID_ARG); + } + else if( flags & (asOBJ_APP_CLASS_CONSTRUCTOR | + asOBJ_APP_CLASS_DESTRUCTOR | + asOBJ_APP_CLASS_ASSIGNMENT) ) + { + // Must not set the class properties, without the class flag + return ConfigError(asINVALID_ARG); + } + } + else + return ConfigError(asINVALID_ARG); + + // Don't allow anything else than the defined flags + if( flags - (flags & asOBJ_MASK_VALID_FLAGS) ) + return ConfigError(asINVALID_ARG); + + // Value types must have a defined size + if( (flags & asOBJ_VALUE) && byteSize == 0 ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_VALUE_TYPE_MUST_HAVE_SIZE); + return ConfigError(asINVALID_ARG); + } + + // Verify type name + if( name == 0 ) + return ConfigError(asINVALID_NAME); + + asCString typeName; + asCBuilder bld(this, 0); + if( flags & asOBJ_TEMPLATE ) + { + asCString subtypeName; + r = bld.ParseTemplateDecl(name, &typeName, &subtypeName); + if( r < 0 ) + return r; + + // Verify that the template name hasn't been registered as a type already + asUINT n; + for( n = 0; n < objectTypes.GetLength(); n++ ) + { + if( objectTypes[n] && objectTypes[n]->name == typeName ) + return asALREADY_REGISTERED; + } + + asCObjectType *type = asNEW(asCObjectType)(this); + type->name = typeName; + type->size = byteSize; + type->flags = flags; + + // Store it in the object types + objectTypes.PushLast(type); + + // Define a template subtype + asCObjectType *subtype = 0; + for( n = 0; n < templateSubTypes.GetLength(); n++ ) + { + if( templateSubTypes[n]->name == subtypeName ) + { + subtype = templateSubTypes[n]; + break; + } + } + if( subtype == 0 ) + { + // Create the new subtype if not already existing + subtype = asNEW(asCObjectType)(this); + subtype->name = subtypeName; + subtype->size = 0; + subtype->flags = asOBJ_TEMPLATE_SUBTYPE; + templateSubTypes.PushLast(subtype); + subtype->AddRef(); + } + type->templateSubType = asCDataType::CreateObject(subtype, false); + subtype->AddRef(); + + currentGroup->objTypes.PushLast(type); + + if( defaultArrayObjectType == 0 ) + { + // TODO: The default array object type should be defined by the application + // The default array object type is registered by the engine itself + defaultArrayObjectType = type; + type->AddRef(); + } + else + { + registeredObjTypes.PushLast(type); + } + } + else + { + typeName = name; + + // Verify if the name has been registered as a type already + asUINT n; + for( n = 0; n < objectTypes.GetLength(); n++ ) + { + if( objectTypes[n] && objectTypes[n]->name == typeName ) + return asALREADY_REGISTERED; + } + + for( n = 0; n < templateTypes.GetLength(); n++ ) + { + if( templateTypes[n] && templateTypes[n]->name == typeName ) + return asALREADY_REGISTERED; + } + + // Verify the most recently created template instance type + asCObjectType *mostRecentTemplateInstanceType = 0; + if( templateInstanceTypes.GetLength() ) + mostRecentTemplateInstanceType = templateInstanceTypes[templateInstanceTypes.GetLength()-1]; + + // Use builder to parse the datatype + asCDataType dt; + bool oldMsgCallback = msgCallback; msgCallback = false; + r = bld.ParseDataType(name, &dt); + msgCallback = oldMsgCallback; + + // If the builder fails, then the type name + // is new and it should be registered + if( r < 0 ) + { + // Make sure the name is not a reserved keyword + asCTokenizer t; + size_t tokenLen; + int token = t.GetToken(name, typeName.GetLength(), &tokenLen); + if( token != ttIdentifier || typeName.GetLength() != tokenLen ) + return ConfigError(asINVALID_NAME); + + int r = bld.CheckNameConflict(name, 0, 0); + if( r < 0 ) + return ConfigError(asNAME_TAKEN); + + // Don't have to check against members of object + // types as they are allowed to use the names + + // Put the data type in the list + asCObjectType *type = asNEW(asCObjectType)(this); + type->name = typeName; + type->size = byteSize; + type->flags = flags; + + objectTypes.PushLast(type); + registeredObjTypes.PushLast(type); + + currentGroup->objTypes.PushLast(type); + } + else + { + // The application is registering a template specialization so we + // need to replace the template instance type with the new type. + + // TODO: Template: We don't require the lower dimensions to be registered first for registered template types + // int[][] must not be allowed to be registered + // if int[] hasn't been registered first + if( dt.GetSubType().IsTemplate() ) + return ConfigError(asLOWER_ARRAY_DIMENSION_NOT_REGISTERED); + + if( dt.IsReadOnly() || + dt.IsReference() ) + return ConfigError(asINVALID_TYPE); + + // Was the template instance type created before? + if( templateInstanceTypes[templateInstanceTypes.GetLength()-1] == mostRecentTemplateInstanceType || + mostRecentTemplateInstanceType == dt.GetObjectType() ) + // TODO: Should have a better error message + return ConfigError(asNOT_SUPPORTED); + + // TODO: Add this again. The type is used by the factory stubs so we need to discount that + // Is the template instance type already being used? +// if( dt.GetObjectType()->GetRefCount() > 1 ) +// return ConfigError(asNOT_SUPPORTED); + + // Put the data type in the list + asCObjectType *type = asNEW(asCObjectType)(this); + type->name = dt.GetObjectType()->name; + type->templateSubType = dt.GetSubType(); + if( type->templateSubType.GetObjectType() ) type->templateSubType.GetObjectType()->AddRef(); + type->size = byteSize; + type->flags = flags; + + templateTypes.PushLast(type); + + currentGroup->objTypes.PushLast(type); + + // Remove the template instance type, which will no longer be used. + RemoveTemplateInstanceType(dt.GetObjectType()); + } + } + + return asSUCCESS; +} + +// interface +int asCScriptEngine::RegisterObjectBehaviour(const char *datatype, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv) +{ + if( datatype == 0 ) return ConfigError(asINVALID_ARG); + + // Determine the object type + asCBuilder bld(this, 0); + asCDataType type; + int r = bld.ParseDataType(datatype, &type); + if( r < 0 ) + return ConfigError(r); + + if( type.GetObjectType() == 0 ) + return ConfigError(asINVALID_TYPE); + + if( type.IsReadOnly() || type.IsReference() ) + return ConfigError(asINVALID_TYPE); + + return RegisterBehaviourToObjectType(type.GetObjectType(), behaviour, decl, funcPointer, callConv); +} + +// internal +int asCScriptEngine::RegisterBehaviourToObjectType(asCObjectType *objectType, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv) +{ + asSSystemFunctionInterface internal; + if( behaviour == asBEHAVE_FACTORY || + behaviour == asBEHAVE_TEMPLATE_CALLBACK ) + { +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#endif + int r = DetectCallingConvention(false, funcPointer, callConv, &internal); + if( r < 0 ) + return ConfigError(r); + } + else + { +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#else + if( callConv != asCALL_THISCALL && + callConv != asCALL_CDECL_OBJLAST && + callConv != asCALL_CDECL_OBJFIRST && + callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#endif + + int r = DetectCallingConvention(true, funcPointer, callConv, &internal); + if( r < 0 ) + return ConfigError(r); + } + + isPrepared = false; + + asSTypeBehaviour *beh = &objectType->beh; + + // Verify function declaration + asCScriptFunction func(this, 0, -1); + + asCBuilder bld(this, 0); + int r = bld.ParseFunctionDeclaration(objectType, decl, &func, true, &internal.paramAutoHandles, &internal.returnAutoHandle); + if( r < 0 ) + return ConfigError(asINVALID_DECLARATION); + func.name.Format("_beh_%d_", behaviour); + + if( behaviour != asBEHAVE_FACTORY ) + func.objectType = objectType; + + // Check if the method restricts that use of the template to value types or reference types + if( objectType->flags & asOBJ_TEMPLATE ) + { + if( func.returnType.GetObjectType() == objectType->templateSubType.GetObjectType() ) + { + if( func.returnType.IsObjectHandle() ) + objectType->acceptValueSubType = false; + else if( !func.returnType.IsReference() ) + objectType->acceptRefSubType = false; + } + + for( asUINT n = 0; n < func.parameterTypes.GetLength(); n++ ) + { + if( func.parameterTypes[n].GetObjectType() == objectType->templateSubType.GetObjectType() ) + { + // TODO: If unsafe references are allowed, then inout references allow value types + if( func.parameterTypes[n].IsObjectHandle() || (func.parameterTypes[n].IsReference() && func.inOutFlags[n] == asTM_INOUTREF) ) + objectType->acceptValueSubType = false; + else if( !func.parameterTypes[n].IsReference() ) + objectType->acceptRefSubType = false; + } + } + } + + if( behaviour == asBEHAVE_CONSTRUCT ) + { + // TODO: Add asBEHAVE_IMPLICIT_CONSTRUCT + + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION); + + if( objectType->flags & asOBJ_SCRIPT_OBJECT ) + { + // The script object is a special case + asASSERT(func.parameterTypes.GetLength() == 1); + + beh->construct = AddBehaviourFunction(func, internal); + beh->factory = beh->construct; + scriptFunctions[beh->factory]->AddRef(); + beh->constructors.PushLast(beh->construct); + beh->factories.PushLast(beh->factory); + func.id = beh->construct; + } + else + { + // Verify that it is a value type + if( !(func.objectType->flags & asOBJ_VALUE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE); + } + + // TODO: Add support for implicit constructors + + // TODO: Verify that the same constructor hasn't been registered already + + // Store all constructors in a list + func.id = AddBehaviourFunction(func, internal); + beh->constructors.PushLast(func.id); + if( func.parameterTypes.GetLength() == 0 ) + { + beh->construct = func.id; + } + else if( func.parameterTypes.GetLength() == 1 ) + { + // Is this the copy constructor? + asCDataType paramType = func.parameterTypes[0]; + + // If the parameter is object, and const reference for input, + // and same type as this class, then this is a copy constructor. + if( paramType.IsObject() && paramType.IsReference() && paramType.IsReadOnly() && func.inOutFlags[0] == asTM_INREF && paramType.GetObjectType() == objectType ) + beh->copyconstruct = func.id; + } + } + } + else if( behaviour == asBEHAVE_DESTRUCT ) + { + // Must be a value type + if( !(func.objectType->flags & asOBJ_VALUE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE); + } + + if( beh->destruct ) + return ConfigError(asALREADY_REGISTERED); + + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION); + + // Verify that there are no parameters + if( func.parameterTypes.GetLength() > 0 ) + return ConfigError(asINVALID_DECLARATION); + + func.id = beh->destruct = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_FACTORY ) + { + // TODO: Add asBEHAVE_IMPLICIT_FACTORY + + // Must be a ref type and must not have asOBJ_NOHANDLE + if( !(objectType->flags & asOBJ_REF) || (objectType->flags & asOBJ_NOHANDLE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE); + } + + // Verify that the return type is a handle to the type + if( func.returnType != asCDataType::CreateObjectHandle(objectType, false) ) + return ConfigError(asINVALID_DECLARATION); + + // TODO: Add support for implicit factories + + // TODO: Verify that the same factory function hasn't been registered already + + // The templates take a hidden parameter with the object type + if( (objectType->flags & asOBJ_TEMPLATE) && + (func.parameterTypes.GetLength() == 0 || + !func.parameterTypes[0].IsReference()) ) + { + return ConfigError(asINVALID_DECLARATION); + } + + // Store all factory functions in a list + func.id = AddBehaviourFunction(func, internal); + beh->factories.PushLast(func.id); + + if( (func.parameterTypes.GetLength() == 0) || + (func.parameterTypes.GetLength() == 1 && (objectType->flags & asOBJ_TEMPLATE)) ) + { + beh->factory = func.id; + } + else if( (func.parameterTypes.GetLength() == 1) || + (func.parameterTypes.GetLength() == 2 && (objectType->flags & asOBJ_TEMPLATE)) ) + { + // Is this the copy factory? + asCDataType paramType = func.parameterTypes[func.parameterTypes.GetLength()-1]; + + // If the parameter is object, and const reference for input, + // and same type as this class, then this is a copy constructor. + if( paramType.IsObject() && paramType.IsReference() && paramType.IsReadOnly() && func.inOutFlags[func.parameterTypes.GetLength()-1] == asTM_INREF && paramType.GetObjectType() == objectType ) + beh->copyfactory = func.id; + } + } + else if( behaviour == asBEHAVE_ADDREF ) + { + // Must be a ref type and must not have asOBJ_NOHANDLE, nor asOBJ_SCOPED + if( !(func.objectType->flags & asOBJ_REF) || + (func.objectType->flags & asOBJ_NOHANDLE) || + (func.objectType->flags & asOBJ_SCOPED) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE); + } + + if( beh->addref ) + return ConfigError(asALREADY_REGISTERED); + + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION); + + // Verify that there are no parameters + if( func.parameterTypes.GetLength() > 0 ) + return ConfigError(asINVALID_DECLARATION); + + func.id = beh->addref = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_RELEASE ) + { + // Must be a ref type and must not have asOBJ_NOHANDLE + if( !(func.objectType->flags & asOBJ_REF) || (func.objectType->flags & asOBJ_NOHANDLE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE); + } + + if( beh->release ) + return ConfigError(asALREADY_REGISTERED); + + // Verify that the return type is void + if( func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION); + + // Verify that there are no parameters + if( func.parameterTypes.GetLength() > 0 ) + return ConfigError(asINVALID_DECLARATION); + + func.id = beh->release = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_TEMPLATE_CALLBACK ) + { + // Must be a template type + if( !(func.objectType->flags & asOBJ_TEMPLATE) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE); + } + + if( beh->templateCallback ) + return ConfigError(asALREADY_REGISTERED); + + // Verify that the return type is bool + if( func.returnType != asCDataType::CreatePrimitive(ttBool, false) ) + return ConfigError(asINVALID_DECLARATION); + + // Verify that there is one parameters + if( func.parameterTypes.GetLength() != 1 ) + return ConfigError(asINVALID_DECLARATION); + + func.id = beh->templateCallback = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_INDEX ) + { + // Verify that the var type is not used + if( VerifyVarTypeNotInFunction(&func) < 0 ) + return ConfigError(asINVALID_DECLARATION); + + // Verify that there is only one parameter + if( func.parameterTypes.GetLength() != 1 ) + return ConfigError(asINVALID_DECLARATION); + + // Verify that the return type is not void + if( func.returnType.GetTokenType() == ttVoid ) + return ConfigError(asINVALID_DECLARATION); + + // TODO: Verify that the operator hasn't been registered already + + beh->operators.PushLast(behaviour); + func.id = AddBehaviourFunction(func, internal); + beh->operators.PushLast(func.id); + } + else if( behaviour >= asBEHAVE_FIRST_GC && + behaviour <= asBEHAVE_LAST_GC ) + { + // Only allow GC behaviours for types registered to be garbage collected + if( !(func.objectType->flags & asOBJ_GC) ) + { + WriteMessage("", 0, 0, asMSGTYPE_ERROR, TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE); + return ConfigError(asILLEGAL_BEHAVIOUR_FOR_TYPE); + } + + // Verify parameter count + if( (behaviour == asBEHAVE_GETREFCOUNT || + behaviour == asBEHAVE_SETGCFLAG || + behaviour == asBEHAVE_GETGCFLAG) && + func.parameterTypes.GetLength() != 0 ) + return ConfigError(asINVALID_DECLARATION); + + if( (behaviour == asBEHAVE_ENUMREFS || + behaviour == asBEHAVE_RELEASEREFS) && + func.parameterTypes.GetLength() != 1 ) + return ConfigError(asINVALID_DECLARATION); + + // Verify return type + if( behaviour == asBEHAVE_GETREFCOUNT && + func.returnType != asCDataType::CreatePrimitive(ttInt, false) ) + return ConfigError(asINVALID_DECLARATION); + + if( behaviour == asBEHAVE_GETGCFLAG && + func.returnType != asCDataType::CreatePrimitive(ttBool, false) ) + return ConfigError(asINVALID_DECLARATION); + + if( (behaviour == asBEHAVE_SETGCFLAG || + behaviour == asBEHAVE_ENUMREFS || + behaviour == asBEHAVE_RELEASEREFS) && + func.returnType != asCDataType::CreatePrimitive(ttVoid, false) ) + return ConfigError(asINVALID_DECLARATION); + + if( behaviour == asBEHAVE_GETREFCOUNT ) + func.id = beh->gcGetRefCount = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_SETGCFLAG ) + func.id = beh->gcSetFlag = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_GETGCFLAG ) + func.id = beh->gcGetFlag = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_ENUMREFS ) + func.id = beh->gcEnumReferences = AddBehaviourFunction(func, internal); + else if( behaviour == asBEHAVE_RELEASEREFS ) + func.id = beh->gcReleaseAllReferences = AddBehaviourFunction(func, internal); + } + else if( behaviour == asBEHAVE_IMPLICIT_VALUE_CAST || + behaviour == asBEHAVE_VALUE_CAST ) + { + // Verify parameter count + if( func.parameterTypes.GetLength() != 0 ) + return ConfigError(asINVALID_DECLARATION); + + // Verify return type + if( func.returnType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttBool, false)) ) + return ConfigError(asNOT_SUPPORTED); + + if( func.returnType.IsEqualExceptRefAndConst(asCDataType::CreatePrimitive(ttVoid, false)) ) + return ConfigError(asINVALID_DECLARATION); + + // TODO: verify that the same cast is not registered already (const or non-const is treated the same for the return type) + + beh->operators.PushLast(behaviour); + func.id = AddBehaviourFunction(func, internal); + beh->operators.PushLast(func.id); + } + else if( behaviour == asBEHAVE_REF_CAST || + behaviour == asBEHAVE_IMPLICIT_REF_CAST ) + { + // Verify parameter count + if( func.parameterTypes.GetLength() != 0 ) + return ConfigError(asINVALID_DECLARATION); + + // Verify return type + if( !func.returnType.IsObjectHandle() ) + return ConfigError(asINVALID_DECLARATION); + + // TODO: verify that the same cast is not registered already (cosnt or non-const is treated the same for the return type) + + beh->operators.PushLast(behaviour); + func.id = AddBehaviourFunction(func, internal); + beh->operators.PushLast(func.id); + } + else + { + asASSERT(false); + + return ConfigError(asINVALID_ARG); + } + + // Return function id as success + return func.id; +} + + +int asCScriptEngine::VerifyVarTypeNotInFunction(asCScriptFunction *func) +{ + // Don't allow var type in this function + if( func->returnType.GetTokenType() == ttQuestion ) + return asINVALID_DECLARATION; + + for( unsigned int n = 0; n < func->parameterTypes.GetLength(); n++ ) + if( func->parameterTypes[n].GetTokenType() == ttQuestion ) + return asINVALID_DECLARATION; + + return 0; +} + +int asCScriptEngine::AddBehaviourFunction(asCScriptFunction &func, asSSystemFunctionInterface &internal) +{ + asUINT n; + + int id = GetNextScriptFunctionId(); + + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface); + newInterface->func = internal.func; + newInterface->baseOffset = internal.baseOffset; + newInterface->callConv = internal.callConv; + newInterface->scriptReturnSize = internal.scriptReturnSize; + newInterface->hostReturnInMemory = internal.hostReturnInMemory; + newInterface->hostReturnFloat = internal.hostReturnFloat; + newInterface->hostReturnSize = internal.hostReturnSize; + newInterface->paramSize = internal.paramSize; + newInterface->takesObjByVal = internal.takesObjByVal; + newInterface->paramAutoHandles = internal.paramAutoHandles; + newInterface->returnAutoHandle = internal.returnAutoHandle; + newInterface->hasAutoHandles = internal.hasAutoHandles; + + asCScriptFunction *f = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + asASSERT(func.name != "" && func.name != "f"); + f->name = func.name; + f->sysFuncIntf = newInterface; + f->returnType = func.returnType; + f->objectType = func.objectType; + f->id = id; + f->isReadOnly = func.isReadOnly; + for( n = 0; n < func.parameterTypes.GetLength(); n++ ) + { + f->parameterTypes.PushLast(func.parameterTypes[n]); + f->inOutFlags.PushLast(func.inOutFlags[n]); + } + + SetScriptFunction(f); + + // If parameter type from other groups are used, add references + if( f->returnType.GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(f->returnType.GetObjectType()); + currentGroup->RefConfigGroup(group); + } + for( n = 0; n < f->parameterTypes.GetLength(); n++ ) + { + if( f->parameterTypes[n].GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(f->parameterTypes[n].GetObjectType()); + currentGroup->RefConfigGroup(group); + } + } + + return id; +} + +// interface +int asCScriptEngine::RegisterGlobalProperty(const char *declaration, void *pointer) +{ + asCDataType type; + asCString name; + + int r; + asCBuilder bld(this, 0); + if( (r = bld.VerifyProperty(0, declaration, name, type)) < 0 ) + return ConfigError(r); + + // Don't allow registering references as global properties + if( type.IsReference() ) + return ConfigError(asINVALID_TYPE); + + // Store the property info + asCGlobalProperty *prop = AllocateGlobalProperty(); + prop->name = name; + prop->type = type; + + prop->SetRegisteredAddress(pointer); + + registeredGlobalProps.PushLast(prop); + currentGroup->globalProps.PushLast(prop); + + // If from another group add reference + if( type.GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(type.GetObjectType()); + currentGroup->RefConfigGroup(group); + } + + return asSUCCESS; +} + +// internal +asCGlobalProperty *asCScriptEngine::AllocateGlobalProperty() +{ + asCGlobalProperty *prop = asNEW(asCGlobalProperty); + + // First check the availability of a free slot + if( freeGlobalPropertyIds.GetLength() ) + { + prop->id = freeGlobalPropertyIds.PopLast(); + globalProperties[prop->id] = prop; + return prop; + } + + prop->id = (asUINT)globalProperties.GetLength(); + globalProperties.PushLast(prop); + return prop; +} + +// internal +void asCScriptEngine::FreeUnusedGlobalProperties() +{ + for( asUINT n = 0; n < globalProperties.GetLength(); n++ ) + { + if( globalProperties[n] && globalProperties[n]->refCount.get() == 0 ) + { + freeGlobalPropertyIds.PushLast(n); + asDELETE(globalProperties[n], asCGlobalProperty); + globalProperties[n] = 0; + } + } +} + +// interface +int asCScriptEngine::GetGlobalPropertyCount() +{ + return (int)registeredGlobalProps.GetLength(); +} + +// interface +// TODO: If the typeId ever encodes the const flag, then the isConst parameter should be removed +int asCScriptEngine::GetGlobalPropertyByIndex(asUINT index, const char **name, int *typeId, bool *isConst, const char **configGroup, void **pointer) +{ + if( index >= registeredGlobalProps.GetLength() ) + return asINVALID_ARG; + + if( name ) + *name = registeredGlobalProps[index]->name.AddressOf(); + + if( configGroup ) + { + asCConfigGroup *group = FindConfigGroupForGlobalVar(index); + if( group ) + *configGroup = group->groupName.AddressOf(); + else + *configGroup = 0; + } + + if( typeId ) + *typeId = GetTypeIdFromDataType(registeredGlobalProps[index]->type); + + if( isConst ) + *isConst = registeredGlobalProps[index]->type.IsReadOnly(); + + if( pointer ) + *pointer = registeredGlobalProps[index]->realAddress; + + return asSUCCESS; +} + +// interface +int asCScriptEngine::RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) +{ + if( obj == 0 ) + return ConfigError(asINVALID_ARG); + + // Determine the object type + asCDataType dt; + asCBuilder bld(this, 0); + int r = bld.ParseDataType(obj, &dt); + if( r < 0 ) + return ConfigError(r); + + if( dt.GetObjectType() == 0 ) + return ConfigError(asINVALID_ARG); + + return RegisterMethodToObjectType(dt.GetObjectType(), declaration, funcPointer, callConv); +} + +// internal +int asCScriptEngine::RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) +{ + asSSystemFunctionInterface internal; + int r = DetectCallingConvention(true, funcPointer, callConv, &internal); + if( r < 0 ) + return ConfigError(r); + + // We only support these calling conventions for object methods +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#else + if( callConv != asCALL_THISCALL && + callConv != asCALL_CDECL_OBJLAST && + callConv != asCALL_CDECL_OBJFIRST && + callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#endif + + isPrepared = false; + + // Put the system function in the list of system functions + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + func->sysFuncIntf = newInterface; + func->objectType = objectType; + + asCBuilder bld(this, 0); + r = bld.ParseFunctionDeclaration(func->objectType, declaration, func, true, &newInterface->paramAutoHandles, &newInterface->returnAutoHandle); + if( r < 0 ) + { + // Set as dummy function before deleting + func->funcType = -1; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_DECLARATION); + } + + // Check name conflicts + r = bld.CheckNameConflictMember(objectType, func->name.AddressOf(), 0, 0); + if( r < 0 ) + { + asDELETE(func,asCScriptFunction); + return ConfigError(asNAME_TAKEN); + } + + func->id = GetNextScriptFunctionId(); + func->objectType->methods.PushLast(func->id); + SetScriptFunction(func); + + // TODO: This code is repeated in many places + // If parameter type from other groups are used, add references + if( func->returnType.GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); + currentGroup->RefConfigGroup(group); + } + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( func->parameterTypes[n].GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType()); + currentGroup->RefConfigGroup(group); + } + } + + // Check if the method restricts that use of the template to value types or reference types + if( func->objectType->flags & asOBJ_TEMPLATE ) + { + if( func->returnType.GetObjectType() == func->objectType->templateSubType.GetObjectType() ) + { + if( func->returnType.IsObjectHandle() ) + func->objectType->acceptValueSubType = false; + else if( !func->returnType.IsReference() ) + func->objectType->acceptRefSubType = false; + } + + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( func->parameterTypes[n].GetObjectType() == func->objectType->templateSubType.GetObjectType() ) + { + // TODO: If unsafe references are allowed, then inout references allow value types + if( func->parameterTypes[n].IsObjectHandle() || (func->parameterTypes[n].IsReference() && func->inOutFlags[n] == asTM_INOUTREF) ) + func->objectType->acceptValueSubType = false; + else if( !func->parameterTypes[n].IsReference() ) + func->objectType->acceptRefSubType = false; + } + } + } + + // TODO: beh.copy member will be removed, so this is not necessary + // Is this the default copy behaviour? + if( func->name == "opAssign" && func->parameterTypes.GetLength() == 1 && func->isReadOnly == false && + (objectType->flags & asOBJ_SCRIPT_OBJECT || func->parameterTypes[0].IsEqualExceptRefAndConst(asCDataType::CreateObject(func->objectType, false))) ) + { + func->objectType->beh.copy = func->id; + func->AddRef(); + } + + // Return the function id as success + return func->id; +} + +// interface +int asCScriptEngine::RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv) +{ + asSSystemFunctionInterface internal; + int r = DetectCallingConvention(false, funcPointer, callConv, &internal); + if( r < 0 ) + return ConfigError(r); + +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#else + if( callConv != asCALL_CDECL && + callConv != asCALL_STDCALL && + callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#endif + + isPrepared = false; + + // Put the system function in the list of system functions + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + func->sysFuncIntf = newInterface; + + asCBuilder bld(this, 0); + r = bld.ParseFunctionDeclaration(0, declaration, func, true, &newInterface->paramAutoHandles, &newInterface->returnAutoHandle); + if( r < 0 ) + { + // Set as dummy function before deleting + func->funcType = -1; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_DECLARATION); + } + + // Check name conflicts + r = bld.CheckNameConflict(func->name.AddressOf(), 0, 0); + if( r < 0 ) + { + asDELETE(func,asCScriptFunction); + return ConfigError(asNAME_TAKEN); + } + + func->id = GetNextScriptFunctionId(); + SetScriptFunction(func); + + currentGroup->scriptFunctions.PushLast(func); + registeredGlobalFuncs.PushLast(func); + + // If parameter type from other groups are used, add references + if( func->returnType.GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); + currentGroup->RefConfigGroup(group); + } + for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ ) + { + if( func->parameterTypes[n].GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(func->parameterTypes[n].GetObjectType()); + currentGroup->RefConfigGroup(group); + } + } + + // Return the function id as success + return func->id; +} + +// interface +int asCScriptEngine::GetGlobalFunctionCount() +{ + return (int)registeredGlobalFuncs.GetLength(); +} + +// interface +int asCScriptEngine::GetGlobalFunctionIdByIndex(asUINT index) +{ + if( index >= registeredGlobalFuncs.GetLength() ) + return asINVALID_ARG; + + return registeredGlobalFuncs[index]->id; +} + + + + + +asCObjectType *asCScriptEngine::GetObjectType(const char *type) +{ + // TODO: optimize: Improve linear search + for( asUINT n = 0; n < objectTypes.GetLength(); n++ ) + if( objectTypes[n] && + objectTypes[n]->name == type ) // TODO: template: Should we check the subtype in case of template instances? + return objectTypes[n]; + + return 0; +} + + + + +void asCScriptEngine::PrepareEngine() +{ + if( isPrepared ) return; + if( configFailed ) return; + + asUINT n; + for( n = 0; n < scriptFunctions.GetLength(); n++ ) + { + // Determine the host application interface + if( scriptFunctions[n] && scriptFunctions[n]->funcType == asFUNC_SYSTEM ) + { + if( scriptFunctions[n]->sysFuncIntf->callConv == ICC_GENERIC_FUNC || + scriptFunctions[n]->sysFuncIntf->callConv == ICC_GENERIC_METHOD ) + PrepareSystemFunctionGeneric(scriptFunctions[n], scriptFunctions[n]->sysFuncIntf, this); + else + PrepareSystemFunction(scriptFunctions[n], scriptFunctions[n]->sysFuncIntf, this); + } + } + + // Validate object type registrations + for( n = 0; n < objectTypes.GetLength(); n++ ) + { + if( objectTypes[n] && !(objectTypes[n]->flags & asOBJ_SCRIPT_OBJECT) ) + { + bool missingBehaviour = false; + const char *infoMsg = 0; + + // Verify that GC types have all behaviours + if( objectTypes[n]->flags & asOBJ_GC ) + { + if( objectTypes[n]->beh.addref == 0 || + objectTypes[n]->beh.release == 0 || + objectTypes[n]->beh.gcGetRefCount == 0 || + objectTypes[n]->beh.gcSetFlag == 0 || + objectTypes[n]->beh.gcGetFlag == 0 || + objectTypes[n]->beh.gcEnumReferences == 0 || + objectTypes[n]->beh.gcReleaseAllReferences == 0 ) + { + infoMsg = TXT_GC_REQUIRE_ADD_REL_GC_BEHAVIOUR; + missingBehaviour = true; + } + } + // Verify that scoped ref types have the release behaviour + else if( objectTypes[n]->flags & asOBJ_SCOPED ) + { + if( objectTypes[n]->beh.release == 0 ) + { + infoMsg = TXT_SCOPE_REQUIRE_REL_BEHAVIOUR; + missingBehaviour = true; + } + } + // Verify that ref types have add ref and release behaviours + else if( (objectTypes[n]->flags & asOBJ_REF) && + !(objectTypes[n]->flags & asOBJ_NOHANDLE) ) + { + if( objectTypes[n]->beh.addref == 0 || + objectTypes[n]->beh.release == 0 ) + { + infoMsg = TXT_REF_REQUIRE_ADD_REL_BEHAVIOUR; + missingBehaviour = true; + } + } + // Verify that non-pod value types have the constructor and destructor registered + else if( (objectTypes[n]->flags & asOBJ_VALUE) && + !(objectTypes[n]->flags & asOBJ_POD) ) + { + if( objectTypes[n]->beh.construct == 0 || + objectTypes[n]->beh.destruct == 0 ) + { + infoMsg = TXT_NON_POD_REQUIRE_CONSTR_DESTR_BEHAVIOUR; + missingBehaviour = true; + } + } + + if( missingBehaviour ) + { + asCString str; + str.Format(TXT_TYPE_s_IS_MISSING_BEHAVIOURS, objectTypes[n]->name.AddressOf()); + WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf()); + WriteMessage("", 0, 0, asMSGTYPE_INFORMATION, infoMsg); + ConfigError(asINVALID_CONFIGURATION); + } + } + } + + isPrepared = true; +} + +int asCScriptEngine::ConfigError(int err) +{ + configFailed = true; + return err; +} + + +// interface +int asCScriptEngine::RegisterStringFactory(const char *datatype, const asSFuncPtr &funcPointer, asDWORD callConv) +{ + asSSystemFunctionInterface internal; + int r = DetectCallingConvention(false, funcPointer, callConv, &internal); + if( r < 0 ) + return ConfigError(r); + +#ifdef AS_MAX_PORTABILITY + if( callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#else + if( callConv != asCALL_CDECL && + callConv != asCALL_STDCALL && + callConv != asCALL_GENERIC ) + return ConfigError(asNOT_SUPPORTED); +#endif + + // Put the system function in the list of system functions + asSSystemFunctionInterface *newInterface = asNEW(asSSystemFunctionInterface)(internal); + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SYSTEM); + func->name = "_string_factory_"; + func->sysFuncIntf = newInterface; + + asCBuilder bld(this, 0); + + asCDataType dt; + r = bld.ParseDataType(datatype, &dt); + if( r < 0 ) + { + // Set as dummy before deleting + func->funcType = -1; + asDELETE(func,asCScriptFunction); + return ConfigError(asINVALID_TYPE); + } + + func->returnType = dt; + func->parameterTypes.PushLast(asCDataType::CreatePrimitive(ttInt, true)); + asCDataType parm1 = asCDataType::CreatePrimitive(ttUInt8, true); + parm1.MakeReference(true); + func->parameterTypes.PushLast(parm1); + func->id = GetNextScriptFunctionId(); + SetScriptFunction(func); + + stringFactory = func; + + if( func->returnType.GetObjectType() ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(func->returnType.GetObjectType()); + if( group == 0 ) group = &defaultGroup; + group->scriptFunctions.PushLast(func); + } + + // Register function id as success + return func->id; +} + +// interface +int asCScriptEngine::GetStringFactoryReturnTypeId() +{ + if( stringFactory == 0 ) + return asNO_FUNCTION; + + return GetTypeIdFromDataType(stringFactory->returnType); +} + +// interface +asCModule *asCScriptEngine::GetModule(const char *_name, bool create) +{ + // Accept null as well as zero-length string + const char *name = ""; + if( _name != 0 ) name = _name; + + if( lastModule && lastModule->name == name ) + return lastModule; + + // TODO: optimize: Improve linear search + for( asUINT n = 0; n < scriptModules.GetLength(); ++n ) + if( scriptModules[n] && scriptModules[n]->name == name ) + { + lastModule = scriptModules[n]; + return lastModule; + } + + if( create ) + { + asCModule *module = asNEW(asCModule)(name, this); + + scriptModules.PushLast(module); + + lastModule = module; + + return lastModule; + } + + return 0; +} + +asCModule *asCScriptEngine::GetModuleFromFuncId(int id) +{ + if( id < 0 ) return 0; + if( id >= (int)scriptFunctions.GetLength() ) return 0; + asCScriptFunction *func = scriptFunctions[id]; + if( func == 0 ) return 0; + return func->module; +} + +// internal +int asCScriptEngine::RequestBuild() +{ + ENTERCRITICALSECTION(engineCritical); + if( isBuilding ) + { + LEAVECRITICALSECTION(engineCritical); + return asBUILD_IN_PROGRESS; + } + isBuilding = true; + LEAVECRITICALSECTION(engineCritical); + + return 0; +} + +// internal +void asCScriptEngine::BuildCompleted() +{ + // Always free up pooled memory after a completed build + memoryMgr.FreeUnusedMemory(); + + isBuilding = false; +} + +#ifdef AS_DEPRECATED +// Deprecated since 2009-12-08, 2.18.0 +// interface +int asCScriptEngine::ExecuteString(const char *module, const char *script, asIScriptContext **ctx, asDWORD flags) +{ + int r; + if( (r = RequestBuild()) < 0 ) + return r; + + PrepareEngine(); + + // Make sure the config worked + if( configFailed ) + { + if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) ) + *ctx = 0; + + WriteMessage("",0,0,asMSGTYPE_ERROR,TXT_INVALID_CONFIGURATION); + isBuilding = false; + return asINVALID_CONFIGURATION; + } + + asIScriptContext *exec = 0; + if( !(flags & asEXECSTRING_USE_MY_CONTEXT) ) + { + int r = CreateContext(&exec, false); + if( r < 0 ) + { + if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) ) + *ctx = 0; + isBuilding = false; + return r; + } + if( ctx ) + { + *ctx = exec; + exec->AddRef(); + } + } + else + { + if( *ctx == 0 ) + { + isBuilding = false; + return asINVALID_ARG; + } + exec = *ctx; + exec->AddRef(); + } + + // Make sure the context isn't holding a reference to the previous ExecuteString function() + exec->Unprepare(); + + // Get the module to compile the string in + asCModule *mod = GetModule(module, true); + + // Compile string function + asCBuilder builder(this, mod); + asCString str = script; + str = "void ExecuteString(){\n" + str + "\n;}"; + + r = builder.BuildString(str.AddressOf(), (asCContext*)exec); + + BuildCompleted(); + + if( r < 0 ) + { + if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) ) + { + (*ctx)->Release(); + *ctx = 0; + } + exec->Release(); + return asERROR; + } + + // Prepare and execute the context + r = ((asCContext*)exec)->Prepare(((asCContext*)exec)->stringFunction->id); + if( r < 0 ) + { + if( ctx && !(flags & asEXECSTRING_USE_MY_CONTEXT) ) + { + (*ctx)->Release(); + *ctx = 0; + } + exec->Release(); + return r; + } + + if( flags & asEXECSTRING_ONLY_PREPARE ) + r = asEXECUTION_PREPARED; + else + r = exec->Execute(); + + exec->Release(); + return r; +} +#endif + +void asCScriptEngine::RemoveTemplateInstanceType(asCObjectType *t) +{ + int n; + + // Destroy the factory stubs + for( n = 0; n < (int)t->beh.factories.GetLength(); n++ ) + { + // Make sure the factory stub isn't referencing this object anymore + scriptFunctions[t->beh.factories[n]]->ReleaseAllHandles(this); + scriptFunctions[t->beh.factories[n]]->Release(); + } + t->beh.factories.SetLength(0); + + // Destroy the specialized functions + for( n = 1; n < (int)t->beh.operators.GetLength(); n += 2 ) + { + if( t->beh.operators[n] && scriptFunctions[t->beh.operators[n]]->objectType == t ) + { + scriptFunctions[t->beh.operators[n]]->Release(); + } + } + t->beh.operators.SetLength(0); + + // Start searching from the end of the list, as most of + // the time it will be the last two types + for( n = (int)templateTypes.GetLength()-1; n >= 0; n-- ) + { + if( templateTypes[n] == t ) + { + if( n == (signed)templateTypes.GetLength()-1 ) + templateTypes.PopLast(); + else + templateTypes[n] = templateTypes.PopLast(); + } + } + + for( n = (int)templateInstanceTypes.GetLength()-1; n >= 0; n-- ) + { + if( templateInstanceTypes[n] == t ) + { + if( n == (signed)templateInstanceTypes.GetLength()-1 ) + templateInstanceTypes.PopLast(); + else + templateInstanceTypes[n] = templateInstanceTypes.PopLast(); + } + } + + asDELETE(t,asCObjectType); +} + +asCObjectType *asCScriptEngine::GetTemplateInstanceType(asCObjectType *templateType, asCDataType &subType) +{ + asUINT n; + + // Is there any template instance type or template specialization already with this subtype? + for( n = 0; n < templateTypes.GetLength(); n++ ) + { + if( templateTypes[n] && + templateTypes[n]->name == templateType->name && + templateTypes[n]->templateSubType == subType ) + return templateTypes[n]; + } + + // No previous template instance exists + + // Make sure this template supports the subtype + if( !templateType->acceptValueSubType && (subType.IsPrimitive() || (subType.GetObjectType()->flags & asOBJ_VALUE)) ) + return 0; + + if( !templateType->acceptRefSubType && (subType.IsObject() && (subType.GetObjectType()->flags & asOBJ_REF)) ) + return 0; + + // Create a new template instance type based on the templateType + asCObjectType *ot = asNEW(asCObjectType)(this); + ot->templateSubType = subType; + ot->flags = templateType->flags; + ot->size = templateType->size; + ot->name = templateType->name; + + // Before filling in the methods, call the template instance callback behaviour to validate the type + if( templateType->beh.templateCallback ) + { + asCScriptFunction *callback = scriptFunctions[templateType->beh.templateCallback]; + if( !CallGlobalFunctionRetBool(ot, 0, callback->sysFuncIntf, callback) ) + { + // The type cannot be instanciated + ot->templateSubType = asCDataType(); + asDELETE(ot, asCObjectType); + return 0; + } + } + + ot->methods = templateType->methods; + for( n = 0; n < ot->methods.GetLength(); n++ ) + scriptFunctions[ot->methods[n]]->AddRef(); + // Store the real factory in the constructor + ot->beh.construct = templateType->beh.factory; + ot->beh.constructors = templateType->beh.factories; + for( n = 0; n < ot->beh.constructors.GetLength(); n++ ) + scriptFunctions[ot->beh.constructors[n]]->AddRef(); + + // Generate factory stubs for each of the factories + for( n = 0; n < templateType->beh.factories.GetLength(); n++ ) + { + int factoryId = templateType->beh.factories[n]; + asCScriptFunction *factory = scriptFunctions[factoryId]; + + asCScriptFunction *func = asNEW(asCScriptFunction)(this, 0, asFUNC_SCRIPT); + func->name = "factstub"; + func->id = GetNextScriptFunctionId(); + func->returnType = asCDataType::CreateObjectHandle(ot, false); + + // Skip the first parameter as this is the object type pointer that the stub will add + for( asUINT p = 1; p < factory->parameterTypes.GetLength(); p++ ) + { + func->parameterTypes.PushLast(factory->parameterTypes[p]); + func->inOutFlags.PushLast(factory->inOutFlags[p]); + } + + SetScriptFunction(func); + + asCBuilder builder(this, 0); + asCCompiler compiler(this); + compiler.CompileTemplateFactoryStub(&builder, factoryId, ot, func); + + // The function's refCount was already initialized to 1 + ot->beh.factories.PushLast(func->id); + } + if( ot->beh.factories.GetLength() ) + ot->beh.factory = ot->beh.factories[0]; + else + { + asASSERT(false); + ot->beh.factory = templateType->beh.factory; + } + + + + ot->beh.addref = templateType->beh.addref; + if( scriptFunctions[ot->beh.addref] ) scriptFunctions[ot->beh.addref]->AddRef(); + ot->beh.release = templateType->beh.release; + if( scriptFunctions[ot->beh.release] ) scriptFunctions[ot->beh.release]->AddRef(); + ot->beh.copy = templateType->beh.copy; + if( scriptFunctions[ot->beh.copy] ) scriptFunctions[ot->beh.copy]->AddRef(); + ot->beh.operators = templateType->beh.operators; + for( n = 1; n < ot->beh.operators.GetLength(); n += 2 ) + { + scriptFunctions[ot->beh.operators[n]]->AddRef(); + } + ot->beh.gcGetRefCount = templateType->beh.gcGetRefCount; + if( scriptFunctions[ot->beh.gcGetRefCount] ) scriptFunctions[ot->beh.gcGetRefCount]->AddRef(); + ot->beh.gcSetFlag = templateType->beh.gcSetFlag; + if( scriptFunctions[ot->beh.gcSetFlag] ) scriptFunctions[ot->beh.gcSetFlag]->AddRef(); + ot->beh.gcGetFlag = templateType->beh.gcGetFlag; + if( scriptFunctions[ot->beh.gcGetFlag] ) scriptFunctions[ot->beh.gcGetFlag]->AddRef(); + ot->beh.gcEnumReferences = templateType->beh.gcEnumReferences; + if( scriptFunctions[ot->beh.gcEnumReferences] ) scriptFunctions[ot->beh.gcEnumReferences]->AddRef(); + ot->beh.gcReleaseAllReferences = templateType->beh.gcReleaseAllReferences; + if( scriptFunctions[ot->beh.gcReleaseAllReferences] ) scriptFunctions[ot->beh.gcReleaseAllReferences]->AddRef(); + + // As the new template type is instanciated, the engine should + // generate new functions to substitute the ones with the template subtype. + for( n = 1; n < ot->beh.operators.GetLength(); n += 2 ) + { + int funcId = ot->beh.operators[n]; + asCScriptFunction *func = scriptFunctions[funcId]; + + if( GenerateNewTemplateFunction(templateType, ot, subType, func, &func) ) + { + // Release the old function, the new one already has its ref count set to 1 + scriptFunctions[ot->beh.operators[n]]->Release(); + ot->beh.operators[n] = func->id; + } + } + + // As the new template type is instanciated, the engine should + // generate new functions to substitute the ones with the template subtype. + for( n = 0; n < ot->methods.GetLength(); n++ ) + { + int funcId = ot->methods[n]; + asCScriptFunction *func = scriptFunctions[funcId]; + + if( GenerateNewTemplateFunction(templateType, ot, subType, func, &func) ) + { + // Release the old function, the new one already has its ref count set to 1 + scriptFunctions[ot->methods[n]]->Release(); + ot->methods[n] = func->id; + } + } + + // Increase ref counter for sub type if it is an object type + if( ot->templateSubType.GetObjectType() ) ot->templateSubType.GetObjectType()->AddRef(); + + // Verify if the subtype contains a garbage collected object, in which case this template is a potential circular reference + // TODO: We may be a bit smarter here. If we can guarantee that the array type cannot be part of the + // potential circular reference then we don't need to set the flag + + if( ot->templateSubType.GetObjectType() && (ot->templateSubType.GetObjectType()->flags & asOBJ_GC) ) + ot->flags |= asOBJ_GC; + else if( ot->name == defaultArrayObjectType->name ) + ot->flags &= ~asOBJ_GC; + + templateTypes.PushLast(ot); + + // We need to store the object type somewhere for clean-up later + // TODO: Why do we need both templateTypes and templateInstanceTypes? It is possible to differ between template instance and template specialization by checking for the asOBJ_TEMPLATE flag + templateInstanceTypes.PushLast(ot); + + return ot; +} + +bool asCScriptEngine::GenerateNewTemplateFunction(asCObjectType *templateType, asCObjectType *ot, asCDataType &subType, asCScriptFunction *func, asCScriptFunction **newFunc) +{ + bool needNewFunc = false; + if( func->returnType.GetObjectType() == templateType->templateSubType.GetObjectType() || + func->returnType.GetObjectType() == templateType ) + needNewFunc = true; + else + { + for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) + { + if( func->parameterTypes[p].GetObjectType() == templateType->templateSubType.GetObjectType() || + func->parameterTypes[p].GetObjectType() == templateType ) + { + needNewFunc = true; + break; + } + } + } + + if( needNewFunc ) + { + asCScriptFunction *func2 = asNEW(asCScriptFunction)(this, 0, func->funcType); + func2->name = func->name; + func2->id = GetNextScriptFunctionId(); + + if( func->returnType.GetObjectType() == templateType->templateSubType.GetObjectType() ) + { + func2->returnType = subType; + if( func->returnType.IsObjectHandle() ) + func2->returnType.MakeHandle(true, true); + func2->returnType.MakeReference(func->returnType.IsReference()); + func2->returnType.MakeReadOnly(func->returnType.IsReadOnly()); + } + else if( func->returnType.GetObjectType() == templateType ) + { + if( func2->returnType.IsObjectHandle() ) + func2->returnType = asCDataType::CreateObjectHandle(ot, false); + else + func2->returnType = asCDataType::CreateObject(ot, false); + + func2->returnType.MakeReference(func->returnType.IsReference()); + func2->returnType.MakeReadOnly(func->returnType.IsReadOnly()); + } + else + func2->returnType = func->returnType; + + func2->parameterTypes.SetLength(func->parameterTypes.GetLength()); + for( asUINT p = 0; p < func->parameterTypes.GetLength(); p++ ) + { + if( func->parameterTypes[p].GetObjectType() == templateType->templateSubType.GetObjectType() ) + { + func2->parameterTypes[p] = subType; + if( func->parameterTypes[p].IsObjectHandle() ) + func2->parameterTypes[p].MakeHandle(true); + func2->parameterTypes[p].MakeReference(func->parameterTypes[p].IsReference()); + func2->parameterTypes[p].MakeReadOnly(func->parameterTypes[p].IsReference()); + } + else if( func->parameterTypes[p].GetObjectType() == templateType ) + { + if( func2->parameterTypes[p].IsObjectHandle() ) + func2->parameterTypes[p] = asCDataType::CreateObjectHandle(ot, false); + else + func2->parameterTypes[p] = asCDataType::CreateObject(ot, false); + + func2->parameterTypes[p].MakeReference(func->parameterTypes[p].IsReference()); + func2->parameterTypes[p].MakeReadOnly(func->parameterTypes[p].IsReadOnly()); + } + else + func2->parameterTypes[p] = func->parameterTypes[p]; + } + + // TODO: template: Must be careful when instanciating templates for garbage collected types + // If the template hasn't been registered with the behaviours, it shouldn't + // permit instanciation of garbage collected types that in turn may refer to + // this instance. + + func2->inOutFlags = func->inOutFlags; + func2->isReadOnly = func->isReadOnly; + func2->objectType = ot; + func2->stackNeeded = func->stackNeeded; + func2->sysFuncIntf = asNEW(asSSystemFunctionInterface)(*func->sysFuncIntf); + + SetScriptFunction(func2); + + // Return the new function + *newFunc = func2; + } + + return needNewFunc; +} + +void asCScriptEngine::CallObjectMethod(void *obj, int func) +{ + asCScriptFunction *s = scriptFunctions[func]; + CallObjectMethod(obj, s->sysFuncIntf, s); +} + +void asCScriptEngine::CallObjectMethod(void *obj, asSSystemFunctionInterface *i, asCScriptFunction *s) +{ +#ifdef __GNUC__ + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else if( i->callConv == ICC_VIRTUAL_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method + // so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asPWORD baseOffset; // Same size as the pointer + } f; + } p; + p.f.func = (void (*)())(i->func); + p.f.baseOffset = asPWORD(i->baseOffset); + void (asCSimpleDummy::*f)() = p.mthd; + (((asCSimpleDummy*)obj)->*f)(); + } + else /*if( i->callConv == ICC_THISCALL || i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + // Non-virtual thiscall can be called just like any global function, passing the object as the first parameter + void (*f)(void *) = (void (*)(void *))(i->func); + f(obj); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (void (*)())(i->func); + void (asCSimpleDummy::*f)() = p.mthd; + (((asCSimpleDummy*)obj)->*f)(); + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void (*f)(void *) = (void (*)(void *))(i->func); + f(obj); + } +#endif +} + +bool asCScriptEngine::CallObjectMethodRetBool(void *obj, int func) +{ + asCScriptFunction *s = scriptFunctions[func]; + asSSystemFunctionInterface *i = s->sysFuncIntf; + +#ifdef __GNUC__ + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(bool*)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_VIRTUAL_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asDWORD baseOffset; + } f; + } p; + p.f.func = (void (*)())(i->func); + p.f.baseOffset = i->baseOffset; + bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(); + } + else /*if( i->callConv == ICC_THISCALL || i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + // Non-virtual thiscall can be called just like any global function, passing the object as the first parameter + bool (*f)(void *) = (bool (*)(void *))(i->func); + return f(obj); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (void (*)())(i->func); + bool (asCSimpleDummy::*f)() = (bool (asCSimpleDummy::*)())p.mthd; + return (((asCSimpleDummy*)obj)->*f)(); + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(bool*)gen.GetReturnPointer(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + bool (*f)(void *) = (bool (*)(void *))(i->func); + return f(obj); + } +#endif +} + +int asCScriptEngine::CallObjectMethodRetInt(void *obj, int func) +{ + asCScriptFunction *s = scriptFunctions[func]; + asSSystemFunctionInterface *i = s->sysFuncIntf; + +#ifdef __GNUC__ + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(int*)gen.GetReturnPointer(); + } + else if( i->callConv == ICC_VIRTUAL_THISCALL ) + { + // For virtual thiscalls we must call the method as a true class method so that the compiler will lookup the function address in the vftable + union + { + asSIMPLEMETHOD_t mthd; + struct + { + asFUNCTION_t func; + asDWORD baseOffset; + } f; + } p; + p.f.func = (void (*)())(i->func); + p.f.baseOffset = i->baseOffset; + int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())(p.mthd); + return (((asCSimpleDummy*)obj)->*f)(); + } + else /*if( i->callConv == ICC_THISCALL || i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + // Non-virtual thiscall can be called just like any global function, passing the object as the first parameter + int (*f)(void *) = (int (*)(void *))(i->func); + return f(obj); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (void (*)())(i->func); + int (asCSimpleDummy::*f)() = (int (asCSimpleDummy::*)())p.mthd; + return (((asCSimpleDummy*)obj)->*f)(); + } + else +#endif + if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(int*)gen.GetReturnPointer(); + } + else /*if( i->callConv == ICC_CDECL_OBJLAST || i->callConv == ICC_CDECL_OBJFIRST )*/ + { + int (*f)(void *) = (int (*)(void *))(i->func); + return f(obj); + } +#endif +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(int func) +{ + asCScriptFunction *s = scriptFunctions[func]; + return CallGlobalFunctionRetPtr(s->sysFuncIntf, s); +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(int func, void *param1) +{ + asCScriptFunction *s = scriptFunctions[func]; + return CallGlobalFunctionRetPtr(s->sysFuncIntf, s, param1); +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s) +{ + if( i->callConv == ICC_CDECL ) + { + void *(*f)() = (void *(*)())(i->func); + return f(); + } + else if( i->callConv == ICC_STDCALL ) + { + void *(STDCALL *f)() = (void *(STDCALL *)())(i->func); + return f(); + } + else + { + asCGeneric gen(this, s, 0, 0); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void**)gen.GetReturnPointer(); + } +} + +void *asCScriptEngine::CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s, void *param1) +{ + if( i->callConv == ICC_CDECL ) + { + void *(*f)(void *) = (void *(*)(void *))(i->func); + return f(param1); + } + else if( i->callConv == ICC_STDCALL ) + { + void *(STDCALL *f)(void *) = (void *(STDCALL *)(void *))(i->func); + return f(param1); + } + else + { + asCGeneric gen(this, s, 0, (asDWORD*)¶m1); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(void**)gen.GetReturnPointer(); + } +} + +void asCScriptEngine::CallObjectMethod(void *obj, void *param, int func) +{ + asCScriptFunction *s = scriptFunctions[func]; + CallObjectMethod(obj, param, s->sysFuncIntf, s); +} + +void asCScriptEngine::CallObjectMethod(void *obj, void *param, asSSystemFunctionInterface *i, asCScriptFunction *s) +{ +#ifdef __GNUC__ + if( i->callConv == ICC_CDECL_OBJLAST ) + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(param, obj); + } + else if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, (asDWORD*)¶m); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else /*if( i->callConv == ICC_CDECL_OBJFIRST || i->callConv == ICC_THISCALL )*/ + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(obj, param); + } +#else +#ifndef AS_NO_CLASS_METHODS + if( i->callConv == ICC_THISCALL ) + { + union + { + asSIMPLEMETHOD_t mthd; + asFUNCTION_t func; + } p; + p.func = (void (*)())(i->func); + void (asCSimpleDummy::*f)(void *) = (void (asCSimpleDummy::*)(void *))(p.mthd); + (((asCSimpleDummy*)obj)->*f)(param); + } + else +#endif + if( i->callConv == ICC_CDECL_OBJLAST ) + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(param, obj); + } + else if( i->callConv == ICC_GENERIC_METHOD ) + { + asCGeneric gen(this, s, obj, (asDWORD*)¶m); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } + else /*if( i->callConv == ICC_CDECL_OBJFIRST )*/ + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(obj, param); + } +#endif +} + +void asCScriptEngine::CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *i, asCScriptFunction *s) +{ + if( i->callConv == ICC_CDECL ) + { + void (*f)(void *, void *) = (void (*)(void *, void *))(i->func); + f(param1, param2); + } + else if( i->callConv == ICC_STDCALL ) + { + void (STDCALL *f)(void *, void *) = (void (STDCALL *)(void *, void *))(i->func); + f(param1, param2); + } + else + { + asCGeneric gen(this, s, 0, (asDWORD*)¶m1); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + } +} + +bool asCScriptEngine::CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *i, asCScriptFunction *s) +{ + if( i->callConv == ICC_CDECL ) + { + bool (*f)(void *, void *) = (bool (*)(void *, void *))(i->func); + return f(param1, param2); + } + else if( i->callConv == ICC_STDCALL ) + { + bool (STDCALL *f)(void *, void *) = (bool (STDCALL *)(void *, void *))(i->func); + return f(param1, param2); + } + else + { + // TODO: When simulating a 64bit environment by defining AS_64BIT_PTR on a 32bit platform this code + // fails, because the stack given to asCGeneric is not prepared with two 64bit arguments. + asCGeneric gen(this, s, 0, (asDWORD*)¶m1); + void (*f)(asIScriptGeneric *) = (void (*)(asIScriptGeneric *))(i->func); + f(&gen); + return *(bool*)gen.GetReturnPointer(); + } +} + +void *asCScriptEngine::CallAlloc(asCObjectType *type) +{ + // Allocate 4 bytes as the smallest size. Otherwise CallSystemFunction may try to + // copy a DWORD onto a smaller memory block, in case the object type is return in registers. +#if defined(AS_DEBUG) + return ((asALLOCFUNCDEBUG_t)(userAlloc))(type->size < 4 ? 4 : type->size, __FILE__, __LINE__); +#else + return userAlloc(type->size < 4 ? 4 : type->size); +#endif +} + +void asCScriptEngine::CallFree(void *obj) +{ + userFree(obj); +} + +// interface +void asCScriptEngine::NotifyGarbageCollectorOfNewObject(void *obj, int typeId) +{ + asCObjectType *objType = GetObjectTypeFromTypeId(typeId); + gc.AddScriptObjectToGC(obj, objType); +} + +// interface +int asCScriptEngine::GarbageCollect(asDWORD flags) +{ + return gc.GarbageCollect(flags); +} + +// interface +void asCScriptEngine::GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected) +{ + gc.GetStatistics(currentSize, totalDestroyed, totalDetected); +} + +// interface +void asCScriptEngine::GCEnumCallback(void *reference) +{ + gc.GCEnumCallback(reference); +} + + +// TODO: multithread: The mapTypeIdToDataType must be protected with critical sections in all functions that access it +int asCScriptEngine::GetTypeIdFromDataType(const asCDataType &dt) +{ + if( dt.IsNullHandle() ) return 0; + + // Find the existing type id + asSMapNode *cursor = 0; + mapTypeIdToDataType.MoveFirst(&cursor); + while( cursor ) + { + if( mapTypeIdToDataType.GetValue(cursor)->IsEqualExceptRefAndConst(dt) ) + return mapTypeIdToDataType.GetKey(cursor); + + mapTypeIdToDataType.MoveNext(&cursor, cursor); + } + + // The type id doesn't exist, create it + + // Setup the basic type id + int typeId = typeIdSeqNbr++; + if( dt.GetObjectType() ) + { + if( dt.GetObjectType()->flags & asOBJ_SCRIPT_OBJECT ) typeId |= asTYPEID_SCRIPTOBJECT; + else if( dt.GetObjectType()->flags & asOBJ_TEMPLATE ) typeId |= asTYPEID_SCRIPTARRAY; // TODO: Should be asTYPEID_TEMPLATE + else if( dt.GetObjectType()->flags & asOBJ_ENUM ); // TODO: Should we have a specific bit for this? + else typeId |= asTYPEID_APPOBJECT; + } + + // Insert the basic object type + asCDataType *newDt = asNEW(asCDataType)(dt); + newDt->MakeReference(false); + newDt->MakeReadOnly(false); + newDt->MakeHandle(false); + + mapTypeIdToDataType.Insert(typeId, newDt); + + // If the object type supports object handles then register those types as well + // Note: Don't check for addref, as asOBJ_SCOPED don't have this + if( dt.IsObject() && dt.GetObjectType()->beh.release ) + { + newDt = asNEW(asCDataType)(dt); + newDt->MakeReference(false); + newDt->MakeReadOnly(false); + newDt->MakeHandle(true); + newDt->MakeHandleToConst(false); + + mapTypeIdToDataType.Insert(typeId | asTYPEID_OBJHANDLE, newDt); + + newDt = asNEW(asCDataType)(dt); + newDt->MakeReference(false); + newDt->MakeReadOnly(false); + newDt->MakeHandle(true); + newDt->MakeHandleToConst(true); + + mapTypeIdToDataType.Insert(typeId | asTYPEID_OBJHANDLE | asTYPEID_HANDLETOCONST, newDt); + } + + // Call the method recursively to get the correct type id + return GetTypeIdFromDataType(dt); +} + +const asCDataType *asCScriptEngine::GetDataTypeFromTypeId(int typeId) +{ + asSMapNode *cursor = 0; + if( mapTypeIdToDataType.MoveTo(&cursor, typeId) ) + return mapTypeIdToDataType.GetValue(cursor); + + return 0; +} + +asCObjectType *asCScriptEngine::GetObjectTypeFromTypeId(int typeId) +{ + asSMapNode *cursor = 0; + if( mapTypeIdToDataType.MoveTo(&cursor, typeId) ) + return mapTypeIdToDataType.GetValue(cursor)->GetObjectType(); + + return 0; +} + +void asCScriptEngine::RemoveFromTypeIdMap(asCObjectType *type) +{ + asSMapNode *cursor = 0; + mapTypeIdToDataType.MoveFirst(&cursor); + while( cursor ) + { + asCDataType *dt = mapTypeIdToDataType.GetValue(cursor); + asSMapNode *old = cursor; + mapTypeIdToDataType.MoveNext(&cursor, cursor); + if( dt->GetObjectType() == type ) + { + asDELETE(dt,asCDataType); + mapTypeIdToDataType.Erase(old); + } + } +} + +// interface +int asCScriptEngine::GetTypeIdByDecl(const char *decl) +{ + asCDataType dt; + asCBuilder bld(this, 0); + int r = bld.ParseDataType(decl, &dt); + if( r < 0 ) + return asINVALID_TYPE; + + return GetTypeIdFromDataType(dt); +} + + + +const char *asCScriptEngine::GetTypeDeclaration(int typeId) +{ + const asCDataType *dt = GetDataTypeFromTypeId(typeId); + if( dt == 0 ) return 0; + + asASSERT(threadManager); + asCString *tempString = &threadManager->GetLocalData()->string; + *tempString = dt->Format(); + + return tempString->AddressOf(); +} + +int asCScriptEngine::GetSizeOfPrimitiveType(int typeId) +{ + const asCDataType *dt = GetDataTypeFromTypeId(typeId); + if( dt == 0 ) return 0; + if( !dt->IsPrimitive() ) return 0; + + return dt->GetSizeInMemoryBytes(); +} + +void *asCScriptEngine::CreateScriptObject(int typeId) +{ + // Make sure the type id is for an object type, and not a primitive or a handle + if( (typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR)) != typeId ) return 0; + if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return 0; + + const asCDataType *dt = GetDataTypeFromTypeId(typeId); + + // Is the type id valid? + if( !dt ) return 0; + + // Allocate the memory + asCObjectType *objType = dt->GetObjectType(); + void *ptr = 0; + + // Construct the object + if( objType->flags & asOBJ_SCRIPT_OBJECT ) + ptr = ScriptObjectFactory(objType, this); + else if( objType->flags & asOBJ_TEMPLATE ) + // The registered factory is moved to the construct behaviour when the type is instanciated + ptr = CallGlobalFunctionRetPtr(objType->beh.construct, objType); + else if( objType->flags & asOBJ_REF ) + ptr = CallGlobalFunctionRetPtr(objType->beh.factory); + else + { + ptr = CallAlloc(objType); + int funcIndex = objType->beh.construct; + if( funcIndex ) + CallObjectMethod(ptr, funcIndex); + } + + return ptr; +} + +void *asCScriptEngine::CreateScriptObjectCopy(void *origObj, int typeId) +{ + void *newObj = CreateScriptObject(typeId); + if( newObj == 0 ) return 0; + + CopyScriptObject(newObj, origObj, typeId); + + return newObj; +} + +void asCScriptEngine::CopyScriptObject(void *dstObj, void *srcObj, int typeId) +{ + // Make sure the type id is for an object type, and not a primitive or a handle + if( (typeId & (asTYPEID_MASK_OBJECT | asTYPEID_MASK_SEQNBR)) != typeId ) return; + if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return; + + // Copy the contents from the original object, using the assignment operator + const asCDataType *dt = GetDataTypeFromTypeId(typeId); + + // Is the type id valid? + if( !dt ) return; + + asCObjectType *objType = dt->GetObjectType(); + // TODO: beh.copy will be removed, so we need to find the default opAssign method instead + if( objType->beh.copy ) + { + CallObjectMethod(dstObj, srcObj, objType->beh.copy); + } + else if( objType->size ) + { + memcpy(dstObj, srcObj, objType->size); + } +} + +void asCScriptEngine::AddRefScriptObject(void *obj, int typeId) +{ + // Make sure it is not a null pointer + if( obj == 0 ) return; + + // Make sure the type id is for an object type or a handle + if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return; + + const asCDataType *dt = GetDataTypeFromTypeId(typeId); + + // Is the type id valid? + if( !dt ) return; + + asCObjectType *objType = dt->GetObjectType(); + + if( objType->beh.addref ) + { + // Call the addref behaviour + CallObjectMethod(obj, objType->beh.addref); + } +} + +void asCScriptEngine::ReleaseScriptObject(void *obj, int typeId) +{ + // Make sure it is not a null pointer + if( obj == 0 ) return; + + // Make sure the type id is for an object type or a handle + if( (typeId & asTYPEID_MASK_OBJECT) == 0 ) return; + + const asCDataType *dt = GetDataTypeFromTypeId(typeId); + + // Is the type id valid? + if( !dt ) return; + + asCObjectType *objType = dt->GetObjectType(); + + if( objType->beh.release ) + { + // Call the release behaviour + CallObjectMethod(obj, objType->beh.release); + } + else + { + // Call the destructor + if( objType->beh.destruct ) + CallObjectMethod(obj, objType->beh.destruct); + + // Then free the memory + CallFree(obj); + } +} + +bool asCScriptEngine::IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId) +{ + // if equal, then it is obvious they are compatible + if( objTypeId == handleTypeId ) + return true; + + // Get the actual data types from the type ids + const asCDataType *objDt = GetDataTypeFromTypeId(objTypeId); + const asCDataType *hdlDt = GetDataTypeFromTypeId(handleTypeId); + + // A handle to const cannot be passed to a handle that is not referencing a const object + if( objDt->IsHandleToConst() && !hdlDt->IsHandleToConst() ) + return false; + + if( objDt->GetObjectType() == hdlDt->GetObjectType() ) + { + // The object type is equal + return true; + } + else if( objDt->IsScriptObject() && obj ) + { + // There's still a chance the object implements the requested interface + asCObjectType *objType = ((asCScriptObject*)obj)->objType; + if( objType->Implements(hdlDt->GetObjectType()) ) + return true; + } + + return false; +} + + +int asCScriptEngine::BeginConfigGroup(const char *groupName) +{ + // Make sure the group name doesn't already exist + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + if( configGroups[n]->groupName == groupName ) + return asNAME_TAKEN; + } + + if( currentGroup != &defaultGroup ) + return asNOT_SUPPORTED; + + asCConfigGroup *group = asNEW(asCConfigGroup)(); + group->groupName = groupName; + + configGroups.PushLast(group); + currentGroup = group; + + return 0; +} + +int asCScriptEngine::EndConfigGroup() +{ + // Raise error if trying to end the default config + if( currentGroup == &defaultGroup ) + return asNOT_SUPPORTED; + + currentGroup = &defaultGroup; + + return 0; +} + +int asCScriptEngine::RemoveConfigGroup(const char *groupName) +{ + // It is not allowed to remove a group that is still in use. + + // It would be possible to change the code in such a way that + // the group could be removed even though it was still in use, + // but that would cause severe negative impact on runtime + // performance, since the VM would then have to be able handle + // situations where the types, functions, and global variables + // can be removed at any time. + + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + if( configGroups[n]->groupName == groupName ) + { + asCConfigGroup *group = configGroups[n]; + + // Make sure the group isn't referenced by anyone + if( group->refCount > 0 ) + return asCONFIG_GROUP_IS_IN_USE; + + // Verify if any objects registered in this group is still alive + if( group->HasLiveObjects() ) + return asCONFIG_GROUP_IS_IN_USE; + + // Remove the group from the list + if( n == configGroups.GetLength() - 1 ) + configGroups.PopLast(); + else + configGroups[n] = configGroups.PopLast(); + + // Remove the configurations registered with this group + group->RemoveConfiguration(this); + + asDELETE(group,asCConfigGroup); + } + } + + return 0; +} + +asCConfigGroup *asCScriptEngine::FindConfigGroupForFunction(int funcId) +{ + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + // Check global functions + asUINT m; + for( m = 0; m < configGroups[n]->scriptFunctions.GetLength(); m++ ) + { + if( configGroups[n]->scriptFunctions[m]->id == funcId ) + return configGroups[n]; + } + } + + return 0; +} + + +asCConfigGroup *asCScriptEngine::FindConfigGroupForGlobalVar(int gvarId) +{ + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + for( asUINT m = 0; m < configGroups[n]->globalProps.GetLength(); m++ ) + { + if( configGroups[n]->globalProps[m]->id == gvarId ) + return configGroups[n]; + } + } + + return 0; +} + +asCConfigGroup *asCScriptEngine::FindConfigGroupForObjectType(const asCObjectType *objType) +{ + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + for( asUINT m = 0; m < configGroups[n]->objTypes.GetLength(); m++ ) + { + if( configGroups[n]->objTypes[m] == objType ) + return configGroups[n]; + } + } + + return 0; +} + +int asCScriptEngine::SetConfigGroupModuleAccess(const char *groupName, const char *module, bool hasAccess) +{ + asCConfigGroup *group = 0; + + // Make sure the group name doesn't already exist + for( asUINT n = 0; n < configGroups.GetLength(); n++ ) + { + if( configGroups[n]->groupName == groupName ) + { + group = configGroups[n]; + break; + } + } + + if( group == 0 ) + return asWRONG_CONFIG_GROUP; + + return group->SetModuleAccess(module, hasAccess); +} + +int asCScriptEngine::GetNextScriptFunctionId() +{ + if( freeScriptFunctionIds.GetLength() ) + return freeScriptFunctionIds.PopLast(); + + int id = (int)scriptFunctions.GetLength(); + scriptFunctions.PushLast(0); + return id; +} + +void asCScriptEngine::SetScriptFunction(asCScriptFunction *func) +{ + scriptFunctions[func->id] = func; +} + +void asCScriptEngine::FreeScriptFunctionId(int id) +{ + if( id < 0 ) return; + id &= 0xFFFF; + if( id >= (int)scriptFunctions.GetLength() ) return; + + if( scriptFunctions[id] ) + { + asCScriptFunction *func = scriptFunctions[id]; + + // Remove the function from the list of script functions + if( id == (int)scriptFunctions.GetLength() - 1 ) + { + scriptFunctions.PopLast(); + } + else + { + scriptFunctions[id] = 0; + freeScriptFunctionIds.PushLast(id); + } + + // Is the function used as signature id? + if( func->signatureId == id ) + { + // Remove the signature id + signatureIds.RemoveValue(func); + + // Update all functions using the signature id + int newSigId = 0; + for( asUINT n = 0; n < scriptFunctions.GetLength(); n++ ) + { + if( scriptFunctions[n] && scriptFunctions[n]->signatureId == id ) + { + if( newSigId == 0 ) + { + newSigId = scriptFunctions[n]->id; + signatureIds.PushLast(scriptFunctions[n]); + } + + scriptFunctions[n]->signatureId = newSigId; + } + } + } + } +} + +// interface +// TODO: typedef: Accept complex types for the typedefs +int asCScriptEngine::RegisterTypedef(const char *type, const char *decl) +{ + if( type == 0 ) return ConfigError(asINVALID_NAME); + + // Verify if the name has been registered as a type already + asUINT n; + for( n = 0; n < objectTypes.GetLength(); n++ ) + { + if( objectTypes[n] && objectTypes[n]->name == type ) + return asALREADY_REGISTERED; + } + + // Grab the data type + asCTokenizer t; + size_t tokenLen; + eTokenType token; + asCDataType dataType; + + // Create the data type + token = t.GetToken(decl, strlen(decl), &tokenLen); + switch(token) + { + case ttBool: + case ttInt: + case ttInt8: + case ttInt16: + case ttInt64: + case ttUInt: + case ttUInt8: + case ttUInt16: + case ttUInt64: + case ttFloat: + case ttDouble: + if( strlen(decl) != tokenLen ) + { + return ConfigError(asINVALID_TYPE); + } + break; + + default: + return ConfigError(asINVALID_TYPE); + } + + dataType = asCDataType::CreatePrimitive(token, false); + + // Make sure the name is not a reserved keyword + token = t.GetToken(type, strlen(type), &tokenLen); + if( token != ttIdentifier || strlen(type) != tokenLen ) + return ConfigError(asINVALID_NAME); + + asCBuilder bld(this, 0); + int r = bld.CheckNameConflict(type, 0, 0); + if( r < 0 ) + return ConfigError(asNAME_TAKEN); + + // Don't have to check against members of object + // types as they are allowed to use the names + + // Put the data type in the list + asCObjectType *object= asNEW(asCObjectType)(this); + object->flags = asOBJ_TYPEDEF; + object->size = dataType.GetSizeInMemoryBytes(); + object->name = type; + object->templateSubType = dataType; + + objectTypes.PushLast(object); + registeredTypeDefs.PushLast(object); + + currentGroup->objTypes.PushLast(object); + + return asSUCCESS; +} + +// interface +int asCScriptEngine::GetTypedefCount() +{ + return (int)registeredTypeDefs.GetLength(); +} + +// interface +const char *asCScriptEngine::GetTypedefByIndex(asUINT index, int *typeId, const char **configGroup) +{ + if( index >= registeredTypeDefs.GetLength() ) + return 0; + + if( typeId ) + *typeId = GetTypeIdByDecl(registeredTypeDefs[index]->name.AddressOf()); + + if( configGroup ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(registeredTypeDefs[index]); + if( group ) + *configGroup = group->groupName.AddressOf(); + else + *configGroup = 0; + } + + return registeredTypeDefs[index]->name.AddressOf(); +} + +// interface +int asCScriptEngine::RegisterEnum(const char *name) +{ + // Check the name + if( NULL == name ) + return ConfigError(asINVALID_NAME); + + // Verify if the name has been registered as a type already + asUINT n; + for( n = 0; n < objectTypes.GetLength(); n++ ) + if( objectTypes[n] && objectTypes[n]->name == name ) + return asALREADY_REGISTERED; + + // Use builder to parse the datatype + asCDataType dt; + asCBuilder bld(this, 0); + bool oldMsgCallback = msgCallback; msgCallback = false; + int r = bld.ParseDataType(name, &dt); + msgCallback = oldMsgCallback; + if( r >= 0 ) + return ConfigError(asERROR); + + // Make sure the name is not a reserved keyword + asCTokenizer t; + size_t tokenLen; + int token = t.GetToken(name, strlen(name), &tokenLen); + if( token != ttIdentifier || strlen(name) != tokenLen ) + return ConfigError(asINVALID_NAME); + + r = bld.CheckNameConflict(name, 0, 0); + if( r < 0 ) + return ConfigError(asNAME_TAKEN); + + asCObjectType *st = asNEW(asCObjectType)(this); + + asCDataType dataType; + dataType.CreatePrimitive(ttInt, false); + + st->flags = asOBJ_ENUM; + st->size = dataType.GetSizeInMemoryBytes(); + st->name = name; + + objectTypes.PushLast(st); + registeredEnums.PushLast(st); + + currentGroup->objTypes.PushLast(st); + + return asSUCCESS; +} + +// interface +int asCScriptEngine::RegisterEnumValue(const char *typeName, const char *valueName, int value) +{ + // Verify that the correct config group is used + if( currentGroup->FindType(typeName) == 0 ) + return asWRONG_CONFIG_GROUP; + + asCDataType dt; + int r; + asCBuilder bld(this, 0); + r = bld.ParseDataType(typeName, &dt); + if( r < 0 ) + return ConfigError(r); + + // Store the enum value + asCObjectType *ot = dt.GetObjectType(); + if( ot == 0 || !(ot->flags & asOBJ_ENUM) ) + return ConfigError(asINVALID_TYPE); + + if( NULL == valueName ) + return ConfigError(asINVALID_NAME); + + for( unsigned int n = 0; n < ot->enumValues.GetLength(); n++ ) + { + if( ot->enumValues[n]->name == valueName ) + return ConfigError(asALREADY_REGISTERED); + } + + asSEnumValue *e = asNEW(asSEnumValue); + e->name = valueName; + e->value = value; + + ot->enumValues.PushLast(e); + + return asSUCCESS; +} + +// interface +int asCScriptEngine::GetEnumCount() +{ + return (int)registeredEnums.GetLength(); +} + +// interface +const char *asCScriptEngine::GetEnumByIndex(asUINT index, int *enumTypeId, const char **configGroup) +{ + if( index >= registeredEnums.GetLength() ) + return 0; + + if( configGroup ) + { + asCConfigGroup *group = FindConfigGroupForObjectType(registeredEnums[index]); + if( group ) + *configGroup = group->groupName.AddressOf(); + else + *configGroup = 0; + } + + if( enumTypeId ) + *enumTypeId = GetTypeIdByDecl(registeredEnums[index]->name.AddressOf()); + + return registeredEnums[index]->name.AddressOf(); +} + +// interface +int asCScriptEngine::GetEnumValueCount(int enumTypeId) +{ + const asCDataType *dt = GetDataTypeFromTypeId(enumTypeId); + asCObjectType *t = dt->GetObjectType(); + if( t == 0 || !(t->GetFlags() & asOBJ_ENUM) ) + return asINVALID_TYPE; + + return (int)t->enumValues.GetLength(); +} + +// interface +const char *asCScriptEngine::GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue) +{ + // TODO: This same function is implemented in as_module.cpp as well. Perhaps it should be moved to asCObjectType? + const asCDataType *dt = GetDataTypeFromTypeId(enumTypeId); + asCObjectType *t = dt->GetObjectType(); + if( t == 0 || !(t->GetFlags() & asOBJ_ENUM) ) + return 0; + + if( index >= t->enumValues.GetLength() ) + return 0; + + if( outValue ) + *outValue = t->enumValues[index]->value; + + return t->enumValues[index]->name.AddressOf(); +} + +// interface +int asCScriptEngine::GetObjectTypeCount() +{ + return (int)registeredObjTypes.GetLength(); +} + +// interface +asIObjectType *asCScriptEngine::GetObjectTypeByIndex(asUINT index) +{ + if( index >= registeredObjTypes.GetLength() ) + return 0; + + return registeredObjTypes[index]; +} + +// interface +asIObjectType *asCScriptEngine::GetObjectTypeById(int typeId) +{ + const asCDataType *dt = GetDataTypeFromTypeId(typeId); + + // Is the type id valid? + if( !dt ) return 0; + + // Enum types are not objects, so we shouldn't return an object type for them + if( dt->GetObjectType() && dt->GetObjectType()->GetFlags() & asOBJ_ENUM ) + return 0; + + return dt->GetObjectType(); +} + + +asIScriptFunction *asCScriptEngine::GetFunctionDescriptorById(int funcId) +{ + return GetScriptFunction(funcId); +} + + +// internal +bool asCScriptEngine::IsTemplateType(const char *name) +{ + // TODO: optimize: Improve linear search + for( unsigned int n = 0; n < objectTypes.GetLength(); n++ ) + { + if( objectTypes[n] && objectTypes[n]->name == name ) + { + return objectTypes[n]->flags & asOBJ_TEMPLATE ? true : false; + } + } + + return false; +} + +// internal +int asCScriptEngine::AddConstantString(const char *str, size_t len) +{ + // The str may contain null chars, so we cannot use strlen, or strcmp, or strcpy + + // TODO: optimize: Improve linear search + // Has the string been registered before? + for( size_t n = 0; n < stringConstants.GetLength(); n++ ) + { + if( stringConstants[n]->Compare(str, len) == 0 ) + { + return (int)n; + } + } + + // No match was found, add the string + asCString *cstr = asNEW(asCString)(str, len); + stringConstants.PushLast(cstr); + + // The VM currently doesn't handle string ids larger than 65535 + asASSERT(stringConstants.GetLength() <= 65536); + + return (int)stringConstants.GetLength() - 1; +} + +// internal +const asCString &asCScriptEngine::GetConstantString(int id) +{ + return *stringConstants[id]; +} + +// internal +int asCScriptEngine::GetScriptSectionNameIndex(const char *name) +{ + // TODO: These names are only released when the engine is freed. The assumption is that + // the same script section names will be reused instead of there always being new + // names. Is this assumption valid? Do we need to add reference counting? + + // Store the script section names for future reference + for( asUINT n = 0; n < scriptSectionNames.GetLength(); n++ ) + { + if( scriptSectionNames[n]->Compare(name) == 0 ) + return n; + } + + scriptSectionNames.PushLast(asNEW(asCString)(name)); + return int(scriptSectionNames.GetLength()-1); +} + + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_scriptengine.h b/AngelScript/source/as_scriptengine.h new file mode 100644 index 000000000..e736eded7 --- /dev/null +++ b/AngelScript/source/as_scriptengine.h @@ -0,0 +1,378 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptengine.h +// +// The implementation of the script engine interface +// + + + +#ifndef AS_SCRIPTENGINE_H +#define AS_SCRIPTENGINE_H + +#include "as_config.h" +#include "as_atomic.h" +#include "as_scriptfunction.h" +#include "as_array.h" +#include "as_datatype.h" +#include "as_objecttype.h" +#include "as_module.h" +#include "as_restore.h" +#include "as_callfunc.h" +#include "as_configgroup.h" +#include "as_memory.h" +#include "as_gc.h" + +BEGIN_AS_NAMESPACE + +class asCBuilder; +class asCContext; + +// TODO: Remove this when import is removed +struct sBindInfo; + +// TODO: Deprecate CreateScriptObject. Objects should be created by calling the factory function instead. +// TODO: Deprecate GetSizeOfPrimitiveType. This function is not necessary now that all primitive types have fixed typeIds + + +// TODO: DiscardModule should take an optional pointer to asIScriptModule instead of module name. If null, nothing is done. + +// TODO: Should have a CreateModule/GetModule instead of just GetModule with parameters. + +// TODO: Should allow enumerating modules, in case they have not been named. + + +class asCScriptEngine : public asIScriptEngine +{ +//============================================================= +// From asIScriptEngine +//============================================================= +public: + // Memory management + virtual int AddRef(); + virtual int Release(); + + // Engine properties + virtual int SetEngineProperty(asEEngineProp property, asPWORD value); + virtual asPWORD GetEngineProperty(asEEngineProp property); + + // Compiler messages + virtual int SetMessageCallback(const asSFuncPtr &callback, void *obj, asDWORD callConv); + virtual int ClearMessageCallback(); + virtual int WriteMessage(const char *section, int row, int col, asEMsgType type, const char *message); + + // JIT Compiler + virtual int SetJITCompiler(asIJITCompiler *compiler); + virtual asIJITCompiler *GetJITCompiler(); + + // Global functions + virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv); + virtual int GetGlobalFunctionCount(); + virtual int GetGlobalFunctionIdByIndex(asUINT index); + + // Global properties + virtual int RegisterGlobalProperty(const char *declaration, void *pointer); + virtual int GetGlobalPropertyCount(); + virtual int GetGlobalPropertyByIndex(asUINT index, const char **name, int *typeId = 0, bool *isConst = 0, const char **configGroup = 0, void **pointer = 0); + + // Type registration + virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags); + virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset); + virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv); + virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv); + virtual int RegisterInterface(const char *name); + virtual int RegisterInterfaceMethod(const char *intf, const char *declaration); + virtual int GetObjectTypeCount(); + virtual asIObjectType *GetObjectTypeByIndex(asUINT index); + + // String factory + virtual int RegisterStringFactory(const char *datatype, const asSFuncPtr &factoryFunc, asDWORD callConv); + virtual int GetStringFactoryReturnTypeId(); + + // Enums + virtual int RegisterEnum(const char *type); + virtual int RegisterEnumValue(const char *type, const char *name, int value); + virtual int GetEnumCount(); + virtual const char *GetEnumByIndex(asUINT index, int *enumTypeId, const char **configGroup = 0); + virtual int GetEnumValueCount(int enumTypeId); + virtual const char *GetEnumValueByIndex(int enumTypeId, asUINT index, int *outValue); + + // Typedefs + virtual int RegisterTypedef(const char *type, const char *decl); + virtual int GetTypedefCount(); + virtual const char *GetTypedefByIndex(asUINT index, int *typeId, const char **configGroup = 0); + + // Configuration groups + virtual int BeginConfigGroup(const char *groupName); + virtual int EndConfigGroup(); + virtual int RemoveConfigGroup(const char *groupName); + virtual int SetConfigGroupModuleAccess(const char *groupName, const char *module, bool hasAccess); + + // Script modules + virtual asIScriptModule *GetModule(const char *module, asEGMFlags flag); + virtual int DiscardModule(const char *module); + + // Script functions + virtual asIScriptFunction *GetFunctionDescriptorById(int funcId); + + // Type identification + virtual asIObjectType *GetObjectTypeById(int typeId); + virtual int GetTypeIdByDecl(const char *decl); + virtual const char *GetTypeDeclaration(int typeId); + virtual int GetSizeOfPrimitiveType(int typeId); + + // Script execution + virtual asIScriptContext *CreateContext(); + virtual void *CreateScriptObject(int typeId); + virtual void *CreateScriptObjectCopy(void *obj, int typeId); + virtual void CopyScriptObject(void *dstObj, void *srcObj, int typeId); + virtual void ReleaseScriptObject(void *obj, int typeId); + virtual void AddRefScriptObject(void *obj, int typeId); + virtual bool IsHandleCompatibleWithObject(void *obj, int objTypeId, int handleTypeId); + + // String interpretation + virtual asETokenClass ParseToken(const char *string, size_t stringLength = 0, int *tokenLength = 0); + + // Garbage collection + virtual int GarbageCollect(asDWORD flags = asGC_FULL_CYCLE); + virtual void GetGCStatistics(asUINT *currentSize, asUINT *totalDestroyed, asUINT *totalDetected); + virtual void NotifyGarbageCollectorOfNewObject(void *obj, int typeId); + virtual void GCEnumCallback(void *reference); + + // User data + virtual void *SetUserData(void *data); + virtual void *GetUserData(); + +#ifdef AS_DEPRECATED + // deprecated since 2009-12-08, 2.18.0 + virtual int ExecuteString(const char *module, const char *script, asIScriptContext **ctx, asDWORD flags); +#endif + +//=========================================================== +// internal methods +//=========================================================== +public: + asCScriptEngine(); + virtual ~asCScriptEngine(); + +//protected: + friend class asCBuilder; + friend class asCCompiler; + friend class asCContext; + friend class asCDataType; + friend class asCModule; + friend class asCRestore; + friend class asCByteCode; + friend int PrepareSystemFunction(asCScriptFunction *func, asSSystemFunctionInterface *internal, asCScriptEngine *engine); + + int RegisterMethodToObjectType(asCObjectType *objectType, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv); + int RegisterBehaviourToObjectType(asCObjectType *objectType, asEBehaviours behaviour, const char *decl, const asSFuncPtr &funcPointer, asDWORD callConv); + + int VerifyVarTypeNotInFunction(asCScriptFunction *func); + + void *CallAlloc(asCObjectType *objType); + void CallFree(void *obj); + void *CallGlobalFunctionRetPtr(int func); + void *CallGlobalFunctionRetPtr(int func, void *param1); + void *CallGlobalFunctionRetPtr(asSSystemFunctionInterface *func, asCScriptFunction *desc); + void *CallGlobalFunctionRetPtr(asSSystemFunctionInterface *i, asCScriptFunction *s, void *param1); + void CallObjectMethod(void *obj, int func); + void CallObjectMethod(void *obj, void *param, int func); + void CallObjectMethod(void *obj, asSSystemFunctionInterface *func, asCScriptFunction *desc); + void CallObjectMethod(void *obj, void *param, asSSystemFunctionInterface *func, asCScriptFunction *desc); + bool CallObjectMethodRetBool(void *obj, int func); + int CallObjectMethodRetInt(void *obj, int func); + void CallGlobalFunction(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc); + bool CallGlobalFunctionRetBool(void *param1, void *param2, asSSystemFunctionInterface *func, asCScriptFunction *desc); + + void ClearUnusedTypes(); + void RemoveTemplateInstanceType(asCObjectType *t); + void RemoveTypeAndRelatedFromList(asCArray &types, asCObjectType *ot); + + asCConfigGroup *FindConfigGroupForFunction(int funcId); + asCConfigGroup *FindConfigGroupForGlobalVar(int gvarId); + asCConfigGroup *FindConfigGroupForObjectType(const asCObjectType *type); + + int RequestBuild(); + void BuildCompleted(); + + void PrepareEngine(); + bool isPrepared; + + int CreateContext(asIScriptContext **context, bool isInternal); + + asCObjectType *GetObjectType(const char *type); + + int AddBehaviourFunction(asCScriptFunction &func, asSSystemFunctionInterface &internal); + + asCString GetFunctionDeclaration(int funcID); + + asCScriptFunction *GetScriptFunction(int funcID); + + asCModule *GetModule(const char *name, bool create); + asCModule *GetModuleFromFuncId(int funcId); + + int GetMethodIdByDecl(const asCObjectType *ot, const char *decl, asCModule *mod); + int GetFactoryIdByDecl(const asCObjectType *ot, const char *decl); + + int GetNextScriptFunctionId(); + void SetScriptFunction(asCScriptFunction *func); + void FreeScriptFunctionId(int id); + + int ConfigError(int err); + + int GetTypeIdFromDataType(const asCDataType &dt); + const asCDataType *GetDataTypeFromTypeId(int typeId); + asCObjectType *GetObjectTypeFromTypeId(int typeId); + void RemoveFromTypeIdMap(asCObjectType *type); + + bool IsTemplateType(const char *name); + asCObjectType *GetTemplateInstanceType(asCObjectType *templateType, asCDataType &subType); + bool GenerateNewTemplateFunction(asCObjectType *templateType, asCObjectType *templateInstanceType, asCDataType &subType, asCScriptFunction *templateFunc, asCScriptFunction **newFunc); + + // String constants + // TODO: Must free unused string constants, thus the ref count for each must be tracked + int AddConstantString(const char *str, size_t length); + const asCString &GetConstantString(int id); + + // Global property management + asCGlobalProperty *AllocateGlobalProperty(); + void FreeUnusedGlobalProperties(); + + int GetScriptSectionNameIndex(const char *name); + +//=========================================================== +// internal properties +//=========================================================== + asCMemoryMgr memoryMgr; + + int initialContextStackSize; + + asCObjectType *defaultArrayObjectType; + asCObjectType scriptTypeBehaviours; + asCObjectType functionBehaviours; + asCObjectType objectTypeBehaviours; + + // Registered interface + asCArray registeredObjTypes; + asCArray registeredTypeDefs; + asCArray registeredEnums; + asCArray registeredGlobalProps; + asCArray registeredGlobalFuncs; + asCScriptFunction *stringFactory; + bool configFailed; + + // Stores all known object types, both application registered, and script declared + asCArray objectTypes; + asCArray templateSubTypes; + + // Store information about template types + asCArray templateTypes; + + // Stores all global properties, both those registered by application, and those declared by scripts. + // The id of a global property is the index in this array. + asCArray globalProperties; + asCArray freeGlobalPropertyIds; + + // Stores all functions, i.e. registered functions, script functions, class methods, behaviours, etc. + asCArray scriptFunctions; + asCArray freeScriptFunctionIds; + asCArray signatureIds; + + // An array with all module imported functions + asCArray importedFunctions; + + // These resources must be protected for multiple accesses + asCAtomic refCount; + asCArray scriptModules; + asCModule *lastModule; + bool isBuilding; + + // Stores script declared object types + asCArray classTypes; + // This array stores the template instances types, that have been generated from template types + asCArray templateInstanceTypes; + + // Stores the names of the script sections for debugging purposes + asCArray scriptSectionNames; + + // Type identifiers + int typeIdSeqNbr; + asCMap mapTypeIdToDataType; + + // Garbage collector + asCGarbageCollector gc; + + // Dynamic groups + asCConfigGroup defaultGroup; + asCArray configGroups; + asCConfigGroup *currentGroup; + + // Message callback + bool msgCallback; + asSSystemFunctionInterface msgCallbackFunc; + void *msgCallbackObj; + + asIJITCompiler *jitCompiler; + + // String constants + asCArray stringConstants; + + // User data + void *userData; + + // Critical sections for threads + DECLARECRITICALSECTION(engineCritical); + + // Engine properties + struct + { + bool allowUnsafeReferences; + bool optimizeByteCode; + bool copyScriptSections; + int maximumContextStackSize; + bool useCharacterLiterals; + bool allowMultilineStrings; + bool allowImplicitHandleTypes; + bool buildWithoutLineCues; + bool initGlobalVarsAfterBuild; + bool requireEnumScope; + int scanner; + bool includeJitInstructions; + int stringEncoding; + } ep; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_scriptfunction.cpp b/AngelScript/source/as_scriptfunction.cpp new file mode 100644 index 000000000..284450a26 --- /dev/null +++ b/AngelScript/source/as_scriptfunction.cpp @@ -0,0 +1,839 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptfunction.cpp +// +// A container for a compiled script function +// + + + +#include "as_config.h" +#include "as_scriptfunction.h" +#include "as_tokendef.h" +#include "as_scriptengine.h" +#include "as_callfunc.h" +#include "as_bytecode.h" +#include "as_texts.h" + +BEGIN_AS_NAMESPACE + +#ifdef AS_MAX_PORTABILITY + +static void ScriptFunction_AddRef_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + self->AddRef(); +} + +static void ScriptFunction_Release_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + self->Release(); +} + +static void ScriptFunction_GetRefCount_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ScriptFunction_SetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + self->SetFlag(); +} + +static void ScriptFunction_GetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); +} + +static void ScriptFunction_EnumReferences_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptFunction_ReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + asCScriptFunction *self = (asCScriptFunction*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +#endif + + +void RegisterScriptFunction(asCScriptEngine *engine) +{ + // Register the gc behaviours for the script functions + int r; + engine->functionBehaviours.engine = engine; + engine->functionBehaviours.flags = asOBJ_REF | asOBJ_GC; + engine->functionBehaviours.name = "_builtin_function_"; +#ifndef AS_MAX_PORTABILITY + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptFunction,AddRef), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptFunction,Release), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCScriptFunction,GetRefCount), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCScriptFunction,SetFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptFunction,GetFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptFunction,EnumReferences), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptFunction,ReleaseAllHandles), asCALL_THISCALL); asASSERT( r >= 0 ); +#else + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFunction_AddRef_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptFunction_Release_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptFunction_GetRefCount_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptFunction_SetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptFunction_GetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptFunction_EnumReferences_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->functionBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptFunction_ReleaseAllHandles_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); +#endif +} + +// internal +asCScriptFunction::asCScriptFunction(asCScriptEngine *engine, asCModule *mod, int _funcType) +{ + refCount.set(1); + this->engine = engine; + funcType = _funcType; + module = mod; + objectType = 0; + name = ""; + isReadOnly = false; + stackNeeded = 0; + sysFuncIntf = 0; + signatureId = 0; + scriptSectionIdx = -1; + dontCleanUpOnException = false; + vfTableIdx = -1; + jitFunction = 0; + gcFlag = false; + + // Notify the GC of script functions + if( funcType == asFUNC_SCRIPT ) + engine->gc.AddScriptObjectToGC(this, &engine->functionBehaviours); +} + +// internal +asCScriptFunction::~asCScriptFunction() +{ + // Imported functions are not reference counted, nor are dummy + // functions that are allocated on the stack + asASSERT( funcType == -1 || + funcType == asFUNC_IMPORTED || + refCount.get() == 0 ); + + ReleaseReferences(); + + // Tell engine to free the function id + if( funcType != -1 && funcType != asFUNC_IMPORTED && id ) + engine->FreeScriptFunctionId(id); + + for( asUINT n = 0; n < variables.GetLength(); n++ ) + { + asDELETE(variables[n],asSScriptVariable); + } + + if( sysFuncIntf ) + { + asDELETE(sysFuncIntf,asSSystemFunctionInterface); + } +} + +// interface +int asCScriptFunction::GetId() const +{ + return id; +} + +// interface +int asCScriptFunction::AddRef() +{ + gcFlag = false; + asASSERT( funcType != asFUNC_IMPORTED ); + return refCount.atomicInc(); +} + +// interface +int asCScriptFunction::Release() +{ + gcFlag = false; + asASSERT( funcType != asFUNC_IMPORTED ); + int r = refCount.atomicDec(); + if( r == 0 && funcType != -1 ) // Dummy functions are allocated on the stack and cannot be deleted + asDELETE(this,asCScriptFunction); + + return r; +} + +// interface +const char *asCScriptFunction::GetModuleName() const +{ + if( module ) + { + return module->name.AddressOf(); + } + + return 0; +} + +// interface +asIObjectType *asCScriptFunction::GetObjectType() const +{ + return objectType; +} + +// interface +const char *asCScriptFunction::GetObjectName() const +{ + if( objectType ) + return objectType->GetName(); + + return 0; +} + +// interface +const char *asCScriptFunction::GetName() const +{ + return name.AddressOf(); +} + +// interface +bool asCScriptFunction::IsClassMethod() const +{ + return objectType && objectType->IsInterface() == false; +} + +// interface +bool asCScriptFunction::IsInterfaceMethod() const +{ + return objectType && objectType->IsInterface(); +} + +// interface +bool asCScriptFunction::IsReadOnly() const +{ + return isReadOnly; +} + +// internal +int asCScriptFunction::GetSpaceNeededForArguments() +{ + // We need to check the size for each type + int s = 0; + for( asUINT n = 0; n < parameterTypes.GetLength(); n++ ) + s += parameterTypes[n].GetSizeOnStackDWords(); + + return s; +} + +// internal +int asCScriptFunction::GetSpaceNeededForReturnValue() +{ + return returnType.GetSizeOnStackDWords(); +} + +// internal +asCString asCScriptFunction::GetDeclarationStr(bool includeObjectName) const +{ + asCString str; + + str = returnType.Format(); + str += " "; + if( objectType && includeObjectName ) + { + if( objectType->name != "" ) + str += objectType->name + "::"; + else + str += "_unnamed_type_::"; + } + asASSERT(name.GetLength() > 0); + if( name == "" ) + str += "_unnamed_function_("; + else + str += name + "("; + + if( parameterTypes.GetLength() > 0 ) + { + asUINT n; + for( n = 0; n < parameterTypes.GetLength() - 1; n++ ) + { + str += parameterTypes[n].Format(); + if( parameterTypes[n].IsReference() && inOutFlags.GetLength() > n ) + { + if( inOutFlags[n] == asTM_INREF ) str += "in"; + else if( inOutFlags[n] == asTM_OUTREF ) str += "out"; + else if( inOutFlags[n] == asTM_INOUTREF ) str += "inout"; + } + str += ", "; + } + + str += parameterTypes[n].Format(); + if( parameterTypes[n].IsReference() && inOutFlags.GetLength() > n ) + { + if( inOutFlags[n] == asTM_INREF ) str += "in"; + else if( inOutFlags[n] == asTM_OUTREF ) str += "out"; + else if( inOutFlags[n] == asTM_INOUTREF ) str += "inout"; + } + } + + str += ")"; + + if( isReadOnly ) + str += " const"; + + return str; +} + +// internal +int asCScriptFunction::GetLineNumber(int programPosition) +{ + if( lineNumbers.GetLength() == 0 ) return 0; + + // Do a binary search in the buffer + int max = (int)lineNumbers.GetLength()/2 - 1; + int min = 0; + int i = max/2; + + for(;;) + { + if( lineNumbers[i*2] < programPosition ) + { + // Have we found the largest number < programPosition? + if( max == i ) return lineNumbers[i*2+1]; + if( lineNumbers[i*2+2] > programPosition ) return lineNumbers[i*2+1]; + + min = i + 1; + i = (max + min)/2; + } + else if( lineNumbers[i*2] > programPosition ) + { + // Have we found the smallest number > programPosition? + if( min == i ) return lineNumbers[i*2+1]; + + max = i - 1; + i = (max + min)/2; + } + else + { + // We found the exact position + return lineNumbers[i*2+1]; + } + } +} + +// internal +void asCScriptFunction::AddVariable(asCString &name, asCDataType &type, int stackOffset) +{ + asSScriptVariable *var = asNEW(asSScriptVariable); + var->name = name; + var->type = type; + var->stackOffset = stackOffset; + variables.PushLast(var); +} + +// internal +void asCScriptFunction::ComputeSignatureId() +{ + // This function will compute the signatureId based on the + // function name, return type, and parameter types. The object + // type for methods is not used, so that class methods and + // interface methods match each other. + for( asUINT n = 0; n < engine->signatureIds.GetLength(); n++ ) + { + if( !IsSignatureEqual(engine->signatureIds[n]) ) continue; + + // We don't need to increment the reference counter here, because + // asCScriptEngine::FreeScriptFunctionId will maintain the signature + // id as the function is freed. + signatureId = engine->signatureIds[n]->signatureId; + return; + } + + signatureId = id; + engine->signatureIds.PushLast(this); +} + +// internal +bool asCScriptFunction::IsSignatureEqual(const asCScriptFunction *func) const +{ + if( name != func->name ) return false; + if( returnType != func->returnType ) return false; + if( isReadOnly != func->isReadOnly ) return false; + if( inOutFlags != func->inOutFlags ) return false; + if( parameterTypes != func->parameterTypes ) return false; + if( (objectType != 0) != (func->objectType != 0) ) return false; + + return true; +} + +// internal +void asCScriptFunction::AddReferences() +{ + asUINT n; + + // This array will be used to make sure we only add the reference to the same resource once + // This is especially important for global variables, as it expects the initialization function + // to hold only one reference to the variable. However, if the variable is initialized through + // the default constructor followed by the assignment operator we will have two references to + // the variable in the function. + asCArray ptrs; + + // Only count references if there is any bytecode + if( byteCode.GetLength() ) + { + if( returnType.IsObject() ) + returnType.GetObjectType()->AddRef(); + + for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) + if( parameterTypes[p].IsObject() ) + parameterTypes[p].GetObjectType()->AddRef(); + } + + // Go through the byte code and add references to all resources used by the function + for( n = 0; n < byteCode.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&byteCode[n]].type] ) + { + switch( *(asBYTE*)&byteCode[n] ) + { + // Object types + case asBC_OBJTYPE: + case asBC_FREE: + case asBC_REFCPY: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + objType->AddRef(); + } + break; + + // Object type and function + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + objType->AddRef(); + + int func = asBC_INTARG(&byteCode[n]+AS_PTR_SIZE); + if( func ) + engine->scriptFunctions[func]->AddRef(); + } + break; + + // Global variables + case asBC_PGA: + case asBC_LDG: + case asBC_PshG4: + case asBC_LdGRdR4: + case asBC_CpyGtoV4: + case asBC_CpyVtoG4: + case asBC_SetG4: + // Need to increase the reference for each global variable + { + void *gvarPtr = (void*)(size_t)asBC_PTRARG(&byteCode[n]); + asCGlobalProperty *prop = GetPropertyByGlobalVarPtr(gvarPtr); + + // Only addref the properties once + if( !ptrs.Exists(gvarPtr) ) + { + prop->AddRef(); + ptrs.PushLast(gvarPtr); + } + + asCConfigGroup *group = engine->FindConfigGroupForGlobalVar(prop->id); + if( group != 0 ) group->AddRef(); + } + break; + + // System functions + case asBC_CALLSYS: + { + int funcId = asBC_INTARG(&byteCode[n]); + asCConfigGroup *group = engine->FindConfigGroupForFunction(funcId); + if( group != 0 ) group->AddRef(); + + engine->scriptFunctions[funcId]->AddRef(); + } + break; + + // Functions + case asBC_CALL: + case asBC_CALLINTF: + { + int func = asBC_INTARG(&byteCode[n]); + engine->scriptFunctions[func]->AddRef(); + } + break; + } + } +} + +// internal +void asCScriptFunction::ReleaseReferences() +{ + asUINT n; + + asCArray ptrs; + + // Only count references if there is any bytecode + if( byteCode.GetLength() ) + { + if( returnType.IsObject() ) + returnType.GetObjectType()->Release(); + + for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) + if( parameterTypes[p].IsObject() ) + parameterTypes[p].GetObjectType()->Release(); + } + + // Go through the byte code and release references to all resources used by the function + for( n = 0; n < byteCode.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&byteCode[n]].type] ) + { + switch( *(asBYTE*)&byteCode[n] ) + { + // Object types + case asBC_OBJTYPE: + case asBC_FREE: + case asBC_REFCPY: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + if( objType ) + objType->Release(); + } + break; + + // Object type and function + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + if( objType ) + objType->Release(); + + int func = asBC_INTARG(&byteCode[n]+AS_PTR_SIZE); + if( func ) + engine->scriptFunctions[func]->Release(); + } + break; + + // Global variables + case asBC_PGA: + case asBC_LDG: + case asBC_PshG4: + case asBC_LdGRdR4: + case asBC_CpyGtoV4: + case asBC_CpyVtoG4: + case asBC_SetG4: + // Need to increase the reference for each global variable + { + void *gvarPtr = (void*)(size_t)asBC_PTRARG(&byteCode[n]); + asCGlobalProperty *prop = GetPropertyByGlobalVarPtr(gvarPtr); + + // Only release the properties once + if( !ptrs.Exists(gvarPtr) ) + { + prop->Release(); + ptrs.PushLast(gvarPtr); + } + + asCConfigGroup *group = engine->FindConfigGroupForGlobalVar(prop->id); + if( group != 0 ) group->Release(); + } + break; + + // System functions + case asBC_CALLSYS: + { + int funcId = asBC_INTARG(&byteCode[n]); + asCConfigGroup *group = engine->FindConfigGroupForFunction(funcId); + if( group != 0 ) group->Release(); + + if( funcId ) + engine->scriptFunctions[funcId]->Release(); + } + break; + + // Functions + case asBC_CALL: + case asBC_CALLINTF: + { + int func = asBC_INTARG(&byteCode[n]); + if( func ) + engine->scriptFunctions[func]->Release(); + } + break; + } + } + + // Release the jit compiled function + if( jitFunction ) + engine->jitCompiler->ReleaseJITFunction(jitFunction); + jitFunction = 0; +} + +// interface +int asCScriptFunction::GetReturnTypeId() const +{ + return engine->GetTypeIdFromDataType(returnType); +} + +// interface +int asCScriptFunction::GetParamCount() const +{ + return (int)parameterTypes.GetLength(); +} + +// interface +int asCScriptFunction::GetParamTypeId(int index, asDWORD *flags) const +{ + if( index < 0 || (unsigned)index >= parameterTypes.GetLength() ) + return asINVALID_ARG; + + if( flags ) + *flags = inOutFlags[index]; + + return engine->GetTypeIdFromDataType(parameterTypes[index]); +} + +// interface +asIScriptEngine *asCScriptFunction::GetEngine() const +{ + return engine; +} + +// interface +const char *asCScriptFunction::GetDeclaration(bool includeObjectName) const +{ + asASSERT(threadManager); + asCString *tempString = &threadManager->GetLocalData()->string; + *tempString = GetDeclarationStr(includeObjectName); + return tempString->AddressOf(); +} + +// interface +const char *asCScriptFunction::GetScriptSectionName() const +{ + if( scriptSectionIdx >= 0 ) + return engine->scriptSectionNames[scriptSectionIdx]->AddressOf(); + + return 0; +} + +// interface +const char *asCScriptFunction::GetConfigGroup() const +{ + asCConfigGroup *group = engine->FindConfigGroupForFunction(id); + if( group == 0 ) + return 0; + + return group->groupName.AddressOf(); +} + +// internal +void asCScriptFunction::JITCompile() +{ + asIJITCompiler *jit = engine->GetJITCompiler(); + if( !jit ) + return; + + // Release the previous function, if any + if( jitFunction ) + { + engine->jitCompiler->ReleaseJITFunction(jitFunction); + jitFunction = 0; + } + + // Compile for native system + int r = jit->CompileFunction(this, &jitFunction); + if( r < 0 ) + { + asASSERT( jitFunction == 0 ); + } +} + +// interface +asDWORD *asCScriptFunction::GetByteCode(asUINT *length) +{ + if( length ) + *length = (asUINT)byteCode.GetLength(); + + if( byteCode.GetLength() ) + { + return byteCode.AddressOf(); + } + + return 0; +} + +// internal +asCGlobalProperty *asCScriptFunction::GetPropertyByGlobalVarPtr(void *gvarPtr) +{ + for( asUINT g = 0; g < engine->globalProperties.GetLength(); g++ ) + if( engine->globalProperties[g] && engine->globalProperties[g]->GetAddressOfValue() == gvarPtr ) + return engine->globalProperties[g]; + + return 0; +} + +// internal +int asCScriptFunction::GetRefCount() +{ + return refCount.get(); +} + +// internal +void asCScriptFunction::SetFlag() +{ + gcFlag = true; +} + +// internal +bool asCScriptFunction::GetFlag() +{ + return gcFlag; +} + +// internal +void asCScriptFunction::EnumReferences(asIScriptEngine *) +{ + // Notify the GC of all object types used + if( returnType.IsObject() ) + engine->GCEnumCallback(returnType.GetObjectType()); + + for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) + if( parameterTypes[p].IsObject() ) + engine->GCEnumCallback(parameterTypes[p].GetObjectType()); + + // Notify the GC of all script functions that is accessed + for( asUINT n = 0; n < byteCode.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&byteCode[n]].type] ) + { + switch( *(asBYTE*)&byteCode[n] ) + { + case asBC_OBJTYPE: + case asBC_FREE: + case asBC_REFCPY: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + engine->GCEnumCallback(objType); + } + break; + + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + engine->GCEnumCallback(objType); + + int func = asBC_INTARG(&byteCode[n]+AS_PTR_SIZE); + if( func ) + engine->GCEnumCallback(engine->scriptFunctions[func]); + } + break; + + case asBC_CALL: + case asBC_CALLINTF: + { + int func = asBC_INTARG(&byteCode[n]); + if( func ) + engine->GCEnumCallback(engine->scriptFunctions[func]); + } + break; + } + } +} + +// internal +void asCScriptFunction::ReleaseAllHandles(asIScriptEngine *) +{ + // Release paramaters + if( byteCode.GetLength() ) + { + if( returnType.IsObject() ) + { + returnType.GetObjectType()->Release(); + returnType = asCDataType::CreatePrimitive(ttVoid, false); + } + + for( asUINT p = 0; p < parameterTypes.GetLength(); p++ ) + if( parameterTypes[p].IsObject() ) + { + parameterTypes[p].GetObjectType()->Release(); + parameterTypes[p] = asCDataType::CreatePrimitive(ttInt, false); + } + } + + // Release all script functions + for( asUINT n = 0; n < byteCode.GetLength(); n += asBCTypeSize[asBCInfo[*(asBYTE*)&byteCode[n]].type] ) + { + switch( *(asBYTE*)&byteCode[n] ) + { + // Object types + case asBC_OBJTYPE: + case asBC_FREE: + case asBC_REFCPY: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + objType->Release(); + *(void**)&byteCode[n+1] = 0; + } + break; + + case asBC_ALLOC: + { + asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(&byteCode[n]); + objType->Release(); + *(void**)&byteCode[n+1] = 0; + + int func = asBC_INTARG(&byteCode[n]+AS_PTR_SIZE); + if( func ) + { + engine->scriptFunctions[func]->Release(); + byteCode[n+AS_PTR_SIZE+1] = 0; + } + } + break; + + case asBC_CALL: + case asBC_CALLINTF: + { + int func = asBC_INTARG(&byteCode[n]); + if( func ) + { + engine->scriptFunctions[func]->Release(); + byteCode[n+1] = 0; + } + } + break; + } + } +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_scriptfunction.h b/AngelScript/source/as_scriptfunction.h new file mode 100644 index 000000000..5358c720f --- /dev/null +++ b/AngelScript/source/as_scriptfunction.h @@ -0,0 +1,187 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptfunction.h +// +// A container for a compiled script function +// + + + +#ifndef AS_SCRIPTFUNCTION_H +#define AS_SCRIPTFUNCTION_H + +#include "as_config.h" +#include "as_string.h" +#include "as_array.h" +#include "as_datatype.h" +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +class asCScriptEngine; +class asCModule; +class asCConfigGroup; +class asCGlobalProperty; + +struct asSScriptVariable +{ + asCString name; + asCDataType type; + int stackOffset; +}; + +const int asFUNC_SYSTEM = 0; +const int asFUNC_SCRIPT = 1; +const int asFUNC_INTERFACE = 2; +const int asFUNC_IMPORTED = 3; +const int asFUNC_VIRTUAL = 4; + +struct asSSystemFunctionInterface; + +// TODO: Need a method for obtaining the function type, so that the application can differenciate between the types +// This should replace the IsClassMethod and IsInterfaceMethod + +// TODO: GetModuleName should be removed. A function won't belong to a specific module anymore +// as the function can be removed from the module, but still remain alive. For example +// for dynamically generated functions held by a function pointer. + +// TODO: Might be interesting to allow enumeration of accessed global variables, and +// also functions/methods that are being called. + +void RegisterScriptFunction(asCScriptEngine *engine); + +class asCScriptFunction : public asIScriptFunction +{ +public: + // From asIScriptFunction + asIScriptEngine *GetEngine() const; + + // Memory management + int AddRef(); + int Release(); + + int GetId() const; + const char *GetModuleName() const; + asIObjectType *GetObjectType() const; + const char *GetObjectName() const; + const char *GetName() const; + const char *GetDeclaration(bool includeObjectName = true) const; + const char *GetScriptSectionName() const; + const char *GetConfigGroup() const; + + bool IsClassMethod() const; + bool IsInterfaceMethod() const; + bool IsReadOnly() const; + + int GetParamCount() const; + int GetParamTypeId(int index, asDWORD *flags = 0) const; + int GetReturnTypeId() const; + + // For JIT compilation + asDWORD *GetByteCode(asUINT *length = 0); + +public: + //----------------------------------- + // Internal methods + + asCScriptFunction(asCScriptEngine *engine, asCModule *mod, int funcType); + ~asCScriptFunction(); + + void AddVariable(asCString &name, asCDataType &type, int stackOffset); + + int GetSpaceNeededForArguments(); + int GetSpaceNeededForReturnValue(); + asCString GetDeclarationStr(bool includeObjectName = true) const; + int GetLineNumber(int programPosition); + void ComputeSignatureId(); + bool IsSignatureEqual(const asCScriptFunction *func) const; + + void JITCompile(); + + void AddReferences(); + void ReleaseReferences(); + + asCGlobalProperty *GetPropertyByGlobalVarPtr(void *gvarPtr); + + // GC methods + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + +public: + //----------------------------------- + // Properties + + asCAtomic refCount; + bool gcFlag; + asCScriptEngine *engine; + asCModule *module; + + // Function signature + asCString name; + asCDataType returnType; + asCArray parameterTypes; + asCArray inOutFlags; + bool isReadOnly; + asCObjectType *objectType; + int signatureId; + + int id; + + int funcType; + + // Used by asFUNC_SCRIPT + asCArray byteCode; + asCArray objVariableTypes; + asCArray objVariablePos; + int stackNeeded; + asCArray lineNumbers; // debug info + asCArray variables; // debug info + int scriptSectionIdx; // debug info + bool dontCleanUpOnException; // Stub functions don't own the object and parameters + + // Used by asFUNC_VIRTUAL + int vfTableIdx; + + // Used by asFUNC_SYSTEM + asSSystemFunctionInterface *sysFuncIntf; + + // JIT compiled code of this function + asJITFunction jitFunction; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_scriptnode.cpp b/AngelScript/source/as_scriptnode.cpp new file mode 100644 index 000000000..7360f6197 --- /dev/null +++ b/AngelScript/source/as_scriptnode.cpp @@ -0,0 +1,149 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptnode.cpp +// +// A node in the script tree built by the parser for compilation +// + + + +#include "as_scriptnode.h" +#include "as_scriptengine.h" + +BEGIN_AS_NAMESPACE + +asCScriptNode::asCScriptNode(eScriptNode type) +{ + nodeType = type; + tokenType = ttUnrecognizedToken; + tokenPos = 0; + tokenLength = 0; + + parent = 0; + next = 0; + prev = 0; + firstChild = 0; + lastChild = 0; +} + +void asCScriptNode::Destroy(asCScriptEngine *engine) +{ + // Destroy all children + asCScriptNode *node = firstChild; + asCScriptNode *next; + + while( node ) + { + next = node->next; + node->Destroy(engine); + node = next; + } + + // Return the memory to the memory manager + engine->memoryMgr.FreeScriptNode(this); +} + +void asCScriptNode::SetToken(sToken *token) +{ + tokenType = token->type; +} + +void asCScriptNode::UpdateSourcePos(size_t pos, size_t length) +{ + if( pos == 0 && length == 0 ) return; + + if( tokenPos == 0 && tokenLength == 0 ) + { + tokenPos = pos; + tokenLength = length; + } + else + { + if( tokenPos > pos ) + { + tokenLength = tokenPos + tokenLength - pos; + tokenPos = pos; + } + + if( pos + length > tokenPos + tokenLength ) + { + tokenLength = pos + length - tokenPos; + } + } +} + +void asCScriptNode::AddChildLast(asCScriptNode *node) +{ + if( lastChild ) + { + lastChild->next = node; + node->next = 0; + node->prev = lastChild; + node->parent = this; + lastChild = node; + } + else + { + firstChild = node; + lastChild = node; + node->next = 0; + node->prev = 0; + node->parent = this; + } + + UpdateSourcePos(node->tokenPos, node->tokenLength); +} + +void asCScriptNode::DisconnectParent() +{ + if( parent ) + { + if( parent->firstChild == this ) + parent->firstChild = next; + if( parent->lastChild == this ) + parent->lastChild = prev; + } + + if( next ) + next->prev = prev; + + if( prev ) + prev->next = next; + + parent = 0; + next = 0; + prev = 0; +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_scriptnode.h b/AngelScript/source/as_scriptnode.h new file mode 100644 index 000000000..6ad1e5c16 --- /dev/null +++ b/AngelScript/source/as_scriptnode.h @@ -0,0 +1,130 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_scriptnode.h +// +// A node in the script tree built by the parser for compilation +// + + +#ifndef AS_SCRIPTNODE_H +#define AS_SCRIPTNODE_H + +#include "as_config.h" +#include "as_tokendef.h" + +BEGIN_AS_NAMESPACE + +enum eScriptNode +{ + snUndefined, + snScript, + snFunction, + snConstant, + snDataType, + snIdentifier, + snParameterList, + snStatementBlock, + snDeclaration, + snExpressionStatement, + snIf, + snFor, + snWhile, + snReturn, + snExpression, + snExprTerm, + snFunctionCall, + snConstructCall, + snArgList, + snExprPreOp, + snExprPostOp, + snExprOperator, + snExprValue, + snBreak, + snContinue, + snDoWhile, + snAssignment, + snCondition, + snGlobalVar, + snSwitch, + snCase, + snImport, + snClass, + snInitList, + snInterface, + snEnum, + snTypedef, + snCast, + snVariableAccess +}; + +struct sToken +{ + eTokenType type; + size_t pos; + size_t length; +}; + +class asCScriptEngine; + +class asCScriptNode +{ +public: + asCScriptNode(eScriptNode nodeType); + + void Destroy(asCScriptEngine *engine); + + void SetToken(sToken *token); + void AddChildLast(asCScriptNode *node); + void DisconnectParent(); + + void UpdateSourcePos(size_t pos, size_t length); + + eScriptNode nodeType; + eTokenType tokenType; + size_t tokenPos; + size_t tokenLength; + + asCScriptNode *parent; + asCScriptNode *next; + asCScriptNode *prev; + asCScriptNode *firstChild; + asCScriptNode *lastChild; + +protected: + // Must call Destroy instead + ~asCScriptNode() {} +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_scriptobject.cpp b/AngelScript/source/as_scriptobject.cpp new file mode 100644 index 000000000..e5858d286 --- /dev/null +++ b/AngelScript/source/as_scriptobject.cpp @@ -0,0 +1,527 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +#include + +#include "as_config.h" + +#include "as_scriptengine.h" + +#include "as_scriptobject.h" +#include "as_arrayobject.h" + +BEGIN_AS_NAMESPACE + +// This helper function will call the default factory, that is a script function +asIScriptObject *ScriptObjectFactory(asCObjectType *objType, asCScriptEngine *engine) +{ + asIScriptContext *ctx; + int r = engine->CreateContext(&ctx, true); + if( r < 0 ) + return 0; + + r = ctx->Prepare(objType->beh.factory); + if( r < 0 ) + { + ctx->Release(); + return 0; + } + + r = ctx->Execute(); + if( r != asEXECUTION_FINISHED ) + { + // TODO: Verify that the memory for the structure have been released already + ctx->Release(); + return 0; + } + + asIScriptObject *ptr = (asIScriptObject*)ctx->GetReturnAddress(); + + // Increase the reference, because the context will release it's pointer + ptr->AddRef(); + + ctx->Release(); + + return ptr; +} + +#ifdef AS_MAX_PORTABILITY + +static void ScriptObject_AddRef_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + self->AddRef(); +} + +static void ScriptObject_Release_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + self->Release(); +} + +static void ScriptObject_GetRefCount_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + *(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount(); +} + +static void ScriptObject_SetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + self->SetFlag(); +} + +static void ScriptObject_GetFlag_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + *(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag(); +} + +static void ScriptObject_EnumReferences_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->EnumReferences(engine); +} + +static void ScriptObject_ReleaseAllHandles_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0); + self->ReleaseAllHandles(engine); +} + +#endif + +void RegisterScriptObject(asCScriptEngine *engine) +{ + // Register the default script class behaviours + int r; + engine->scriptTypeBehaviours.engine = engine; + engine->scriptTypeBehaviours.flags = asOBJ_SCRIPT_OBJECT | asOBJ_REF | asOBJ_GC; + engine->scriptTypeBehaviours.name = "_builtin_object_"; +#ifndef AS_MAX_PORTABILITY + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asMETHOD(asCScriptObject,AddRef), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asMETHOD(asCScriptObject,Release), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment), asCALL_CDECL_OBJLAST); asASSERT( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(asCScriptObject,GetRefCount), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asMETHOD(asCScriptObject,SetFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(asCScriptObject,GetFlag), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(asCScriptObject,EnumReferences), asCALL_THISCALL); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(asCScriptObject,ReleaseAllHandles), asCALL_THISCALL); asASSERT( r >= 0 ); +#else + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptObject_Construct_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptObject_AddRef_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptObject_Release_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterMethodToObjectType(&engine->scriptTypeBehaviours, "int &opAssign(int &in)", asFUNCTION(ScriptObject_Assignment_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + + // Register GC behaviours + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptObject_GetRefCount_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptObject_SetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptObject_GetFlag_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptObject_EnumReferences_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); + r = engine->RegisterBehaviourToObjectType(&engine->scriptTypeBehaviours, asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptObject_ReleaseAllHandles_Generic), asCALL_GENERIC); asASSERT( r >= 0 ); +#endif +} + +void ScriptObject_Construct_Generic(asIScriptGeneric *gen) +{ + asCObjectType *objType = *(asCObjectType**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + ScriptObject_Construct(objType, self); +} + +void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self) +{ + new(self) asCScriptObject(objType); +} + +asCScriptObject::asCScriptObject(asCObjectType *ot) +{ + refCount.set(1); + objType = ot; + objType->AddRef(); + isDestructCalled = false; + + // Notify the garbage collector of this object + if( objType->flags & asOBJ_GC ) + objType->engine->gc.AddScriptObjectToGC(this, objType); + + // Construct all properties + asCScriptEngine *engine = objType->engine; + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() ) + { + size_t *ptr = (size_t*)(((char*)this) + prop->byteOffset); + + if( prop->type.IsObjectHandle() ) + *ptr = 0; + else + { + // Allocate the object and call it's constructor + *ptr = (size_t)AllocateObject(prop->type.GetObjectType(), engine); + } + } + } +} + +void asCScriptObject::Destruct() +{ + // Call the destructor, which will also call the GCObject's destructor + this->~asCScriptObject(); + + // Free the memory + userFree(this); +} + +asCScriptObject::~asCScriptObject() +{ + objType->Release(); + + // The engine pointer should be available from the objectType + asCScriptEngine *engine = objType->engine; + + // Destroy all properties + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() ) + { + // Destroy the object + void **ptr = (void**)(((char*)this) + prop->byteOffset); + if( *ptr ) + { + FreeObject(*ptr, prop->type.GetObjectType(), engine); + *(asDWORD*)ptr = 0; + } + } + } +} + +asIScriptEngine *asCScriptObject::GetEngine() const +{ + return objType->engine; +} + +int asCScriptObject::AddRef() +{ + // Increase counter and clear flag set by GC + gcFlag = false; + return refCount.atomicInc(); +} + +int asCScriptObject::Release() +{ + // Clear the flag set by the GC + gcFlag = false; + + // Call the script destructor behaviour if the reference counter is 1. + if( refCount.get() == 1 && !isDestructCalled ) + { + CallDestructor(); + } + + // Now do the actual releasing + int r = refCount.atomicDec(); + if( r == 0 ) + { + Destruct(); + return 0; + } + + return r; +} + +void asCScriptObject::CallDestructor() +{ + // Make sure the destructor is called once only, even if the + // reference count is increased and then decreased again + isDestructCalled = true; + + asIScriptContext *ctx = 0; + + // Call the destructor for this class and all the super classes + asCObjectType *ot = objType; + while( ot ) + { + int funcIndex = ot->beh.destruct; + if( funcIndex ) + { + if( ctx == 0 ) + { + // Setup a context for calling the default constructor + asCScriptEngine *engine = objType->engine; + int r = engine->CreateContext(&ctx, true); + if( r < 0 ) return; + } + + int r = ctx->Prepare(funcIndex); + if( r >= 0 ) + { + ctx->SetObject(this); + ctx->Execute(); + + // There's not much to do if the execution doesn't + // finish, so we just ignore the result + } + } + + ot = ot->derivedFrom; + } + + if( ctx ) + { + ctx->Release(); + } +} + +asIObjectType *asCScriptObject::GetObjectType() const +{ + return objType; +} + +int asCScriptObject::GetRefCount() +{ + return refCount.get(); +} + +void asCScriptObject::SetFlag() +{ + gcFlag = true; +} + +bool asCScriptObject::GetFlag() +{ + return gcFlag; +} + +// interface +int asCScriptObject::GetTypeId() const +{ + asCDataType dt = asCDataType::CreateObject(objType, false); + return objType->engine->GetTypeIdFromDataType(dt); +} + +int asCScriptObject::GetPropertyCount() const +{ + return (int)objType->properties.GetLength(); +} + +int asCScriptObject::GetPropertyTypeId(asUINT prop) const +{ + if( prop >= objType->properties.GetLength() ) + return asINVALID_ARG; + + return objType->engine->GetTypeIdFromDataType(objType->properties[prop]->type); +} + +const char *asCScriptObject::GetPropertyName(asUINT prop) const +{ + if( prop >= objType->properties.GetLength() ) + return 0; + + return objType->properties[prop]->name.AddressOf(); +} + +void *asCScriptObject::GetAddressOfProperty(asUINT prop) +{ + if( prop >= objType->properties.GetLength() ) + return 0; + + // Objects are stored by reference, so this must be dereferenced + asCDataType *dt = &objType->properties[prop]->type; + if( dt->IsObject() && !dt->IsObjectHandle() ) + return *(void**)(((char*)this) + objType->properties[prop]->byteOffset); + + return (void*)(((char*)this) + objType->properties[prop]->byteOffset); +} + +void asCScriptObject::EnumReferences(asIScriptEngine *engine) +{ + // We'll notify the GC of all object handles that we're holding + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() ) + { + void *ptr = *(void**)(((char*)this) + prop->byteOffset); + if( ptr ) + ((asCScriptEngine*)engine)->GCEnumCallback(ptr); + } + } +} + +void asCScriptObject::ReleaseAllHandles(asIScriptEngine *engine) +{ + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() && prop->type.IsObjectHandle() ) + { + void **ptr = (void**)(((char*)this) + prop->byteOffset); + if( *ptr ) + { + ((asCScriptEngine*)engine)->CallObjectMethod(*ptr, prop->type.GetBehaviour()->release); + *ptr = 0; + } + } + } +} + +void ScriptObject_Assignment_Generic(asIScriptGeneric *gen) +{ + asCScriptObject *other = *(asCScriptObject**)gen->GetAddressOfArg(0); + asCScriptObject *self = (asCScriptObject*)gen->GetObject(); + + *self = *other; + + *(asCScriptObject**)gen->GetAddressOfReturnLocation() = self; +} + +asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self) +{ + return (*self = *other); +} + +asCScriptObject &asCScriptObject::operator=(const asCScriptObject &other) +{ + if( &other != this ) + { + asASSERT( other.objType->DerivesFrom(objType) ); + + asCScriptEngine *engine = objType->engine; + + // Copy all properties + for( asUINT n = 0; n < objType->properties.GetLength(); n++ ) + { + asCObjectProperty *prop = objType->properties[n]; + if( prop->type.IsObject() ) + { + void **dst = (void**)(((char*)this) + prop->byteOffset); + void **src = (void**)(((char*)&other) + prop->byteOffset); + if( !prop->type.IsObjectHandle() ) + CopyObject(*src, *dst, prop->type.GetObjectType(), engine); + else + CopyHandle((asDWORD*)src, (asDWORD*)dst, prop->type.GetObjectType(), engine); + } + else + { + void *dst = ((char*)this) + prop->byteOffset; + void *src = ((char*)&other) + prop->byteOffset; + memcpy(dst, src, prop->type.GetSizeInMemoryBytes()); + } + } + } + + return *this; +} + +int asCScriptObject::CopyFrom(asIScriptObject *other) +{ + if( other == 0 ) return asINVALID_ARG; + + if( GetTypeId() != other->GetTypeId() ) + return asINVALID_TYPE; + + *this = *(asCScriptObject*)other; + + return 0; +} + +void *asCScriptObject::AllocateObject(asCObjectType *objType, asCScriptEngine *engine) +{ + void *ptr = 0; + + if( objType->flags & asOBJ_SCRIPT_OBJECT ) + { + ptr = ScriptObjectFactory(objType, engine); + } + else if( objType->flags & asOBJ_TEMPLATE ) + { + ptr = ArrayObjectFactory(objType); + } + else if( objType->flags & asOBJ_REF ) + { + ptr = engine->CallGlobalFunctionRetPtr(objType->beh.factory); + } + else + { + ptr = engine->CallAlloc(objType); + int funcIndex = objType->beh.construct; + if( funcIndex ) + engine->CallObjectMethod(ptr, funcIndex); + } + + return ptr; +} + +void asCScriptObject::FreeObject(void *ptr, asCObjectType *objType, asCScriptEngine *engine) +{ + if( !objType->beh.release ) + { + if( objType->beh.destruct ) + engine->CallObjectMethod(ptr, objType->beh.destruct); + + engine->CallFree(ptr); + } + else + { + engine->CallObjectMethod(ptr, objType->beh.release); + } +} + +void asCScriptObject::CopyObject(void *src, void *dst, asCObjectType *objType, asCScriptEngine *engine) +{ + int funcIndex = objType->beh.copy; + + if( funcIndex ) + engine->CallObjectMethod(dst, src, funcIndex); + else + memcpy(dst, src, objType->size); +} + +void asCScriptObject::CopyHandle(asDWORD *src, asDWORD *dst, asCObjectType *objType, asCScriptEngine *engine) +{ + if( *dst ) + engine->CallObjectMethod(*(void**)dst, objType->beh.release); + *dst = *src; + if( *dst ) + engine->CallObjectMethod(*(void**)dst, objType->beh.addref); +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_scriptobject.h b/AngelScript/source/as_scriptobject.h new file mode 100644 index 000000000..8a98d9339 --- /dev/null +++ b/AngelScript/source/as_scriptobject.h @@ -0,0 +1,120 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_scriptobject.h +// +// A generic class for handling script declared structures +// + + + +#ifndef AS_SCRIPTOBJECT_H +#define AS_SCRIPTOBJECT_H + +#include "as_config.h" +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +class asCObjectType; + + +class asCScriptObject : public asIScriptObject +{ +public: +//=================================== +// From asIScriptObject +//=================================== + asIScriptEngine *GetEngine() const; + + // Memory management + int AddRef(); + int Release(); + + // Type info + int GetTypeId() const; + asIObjectType *GetObjectType() const; + + // Class properties + int GetPropertyCount() const; + int GetPropertyTypeId(asUINT prop) const; + const char *GetPropertyName(asUINT prop) const; + void *GetAddressOfProperty(asUINT prop); + + int CopyFrom(asIScriptObject *other); + +//==================================== +// Internal +//==================================== + asCScriptObject(asCObjectType *objType); + virtual ~asCScriptObject(); + + asCScriptObject &operator=(const asCScriptObject &other); + + // GC methods + void Destruct(); + int GetRefCount(); + void SetFlag(); + bool GetFlag(); + void EnumReferences(asIScriptEngine *engine); + void ReleaseAllHandles(asIScriptEngine *engine); + + // Used for properties + void *AllocateObject(asCObjectType *objType, asCScriptEngine *engine); + void FreeObject(void *ptr, asCObjectType *objType, asCScriptEngine *engine); + void CopyObject(void *src, void *dst, asCObjectType *objType, asCScriptEngine *engine); + void CopyHandle(asDWORD *src, asDWORD *dst, asCObjectType *objType, asCScriptEngine *engine); + + void CallDestructor(); + + asCObjectType *objType; + +protected: + asCAtomic refCount; + bool gcFlag; + bool isDestructCalled; +}; + +void ScriptObject_Construct(asCObjectType *objType, asCScriptObject *self); +asCScriptObject &ScriptObject_Assignment(asCScriptObject *other, asCScriptObject *self); + +void ScriptObject_Construct_Generic(asIScriptGeneric *gen); +void ScriptObject_Assignment_Generic(asIScriptGeneric *gen); + +void RegisterScriptObject(asCScriptEngine *engine); + +asIScriptObject *ScriptObjectFactory(asCObjectType *objType, asCScriptEngine *engine); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_string.cpp b/AngelScript/source/as_string.cpp new file mode 100644 index 000000000..f2ade6924 --- /dev/null +++ b/AngelScript/source/as_string.cpp @@ -0,0 +1,392 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +#include "as_config.h" + +#include // va_list, va_start(), etc +#include // strtod(), strtol() +#include // some compilers declare memcpy() here + +#if !defined(AS_NO_MEMORY_H) +#include +#endif + +#include "as_string.h" + +asCString::asCString() +{ + length = 0; + local[0] = 0; +} + +// Copy constructor +asCString::asCString(const asCString &str) +{ + length = 0; + local[0] = 0; + + Assign(str.AddressOf(), str.length); +} + +asCString::asCString(const char *str, size_t len) +{ + length = 0; + local[0] = 0; + + Assign(str, len); +} + +asCString::asCString(const char *str) +{ + length = 0; + local[0] = 0; + + size_t len = strlen(str); + Assign(str, len); +} + +asCString::asCString(char ch) +{ + length = 0; + local[0] = 0; + + Assign(&ch, 1); +} + +asCString::~asCString() +{ + if( length > 11 && dynamic ) + { + asDELETEARRAY(dynamic); + } +} + +char *asCString::AddressOf() +{ + if( length <= 11 ) + return local; + else + return dynamic; +} + +const char *asCString::AddressOf() const +{ + if( length <= 11 ) + return local; + else + return dynamic; +} + +void asCString::SetLength(size_t len) +{ + Allocate(len, true); +} + +void asCString::Allocate(size_t len, bool keepData) +{ + // If we stored the capacity of the dynamically allocated buffer it would be possible + // to save some memory allocations if a string decreases in size then increases again, + // but this would require extra bytes in the string object itself, or a decrease of + // the static buffer, which in turn would mean extra memory is needed. I've tested each + // of these options, and it turned out that the current choice is what best balanced + // the number of allocations against the size of the allocations. + + if( len > 11 && len > length ) + { + // Allocate a new dynamic buffer if the new one is larger than the old + char *buf = asNEWARRAY(char,len+1); + + if( keepData ) + { + int l = (int)len < (int)length ? (int)len : (int)length; + memcpy(buf, AddressOf(), l); + } + + if( length > 11 ) + { + asDELETEARRAY(dynamic); + } + + dynamic = buf; + } + else if( len <= 11 && length > 11 ) + { + // Free the dynamic buffer, since it is no longer needed + char *buf = dynamic; + if( keepData ) + { + memcpy(&local, buf, len); + } + asDELETEARRAY(buf); + } + + length = (int)len; + + // Make sure the buffer is null terminated + AddressOf()[length] = 0; +} + +void asCString::Assign(const char *str, size_t len) +{ + Allocate(len, false); + + // Copy the string + memcpy(AddressOf(), str, length); + AddressOf()[length] = 0; +} + +asCString &asCString::operator =(const char *str) +{ + size_t len = str ? strlen(str) : 0; + Assign(str, len); + + return *this; +} + +asCString &asCString::operator =(const asCString &str) +{ + Assign(str.AddressOf(), str.length); + + return *this; +} + +asCString &asCString::operator =(char ch) +{ + Assign(&ch, 1); + + return *this; +} + +void asCString::Concatenate(const char *str, size_t len) +{ + asUINT oldLength = length; + SetLength(length + len); + + memcpy(AddressOf() + oldLength, str, len); + AddressOf()[length] = 0; +} + +asCString &asCString::operator +=(const char *str) +{ + size_t len = strlen(str); + Concatenate(str, len); + + return *this; +} + +asCString &asCString::operator +=(const asCString &str) +{ + Concatenate(str.AddressOf(), str.length); + + return *this; +} + +asCString &asCString::operator +=(char ch) +{ + Concatenate(&ch, 1); + + return *this; +} + +size_t asCString::GetLength() const +{ + return length; +} + +// Returns the length +size_t asCString::Format(const char *format, ...) +{ + va_list args; + va_start(args, format); + + char tmp[256]; + int r = asVSNPRINTF(tmp, 255, format, args); + + if( r > 0 ) + { + Assign(tmp, r); + } + else + { + size_t n = 512; + asCString str; // Use temporary string in case the current buffer is a parameter + str.Allocate(n, false); + + while( (r = asVSNPRINTF(str.AddressOf(), n, format, args)) < 0 ) + { + n *= 2; + str.Allocate(n, false); + } + + Assign(str.AddressOf(), r); + } + + va_end(args); + + return length; +} + +char &asCString::operator [](size_t index) +{ + asASSERT(index < length); + + return AddressOf()[index]; +} + +const char &asCString::operator [](size_t index) const +{ + asASSERT(index < length); + + return AddressOf()[index]; +} + +asCString asCString::SubString(size_t start, size_t length) const +{ + if( start >= GetLength() || length == 0 ) + return asCString(""); + + if( length == (size_t)(-1) ) length = GetLength() - start; + + asCString tmp; + tmp.Assign(AddressOf() + start, length); + + return tmp; +} + +int asCString::Compare(const char *str) const +{ + return Compare(str, strlen(str)); +} + +int asCString::Compare(const asCString &str) const +{ + return Compare(str.AddressOf(), str.GetLength()); +} + +int asCString::Compare(const char *str, size_t len) const +{ + if( length == 0 ) + { + if( str == 0 || len == 0 ) return 0; // Equal + + return 1; // The other string is larger than this + } + + if( str == 0 ) + { + if( length == 0 ) + return 0; // Equal + + return -1; // The other string is smaller than this + } + + if( len < length ) + { + int result = memcmp(AddressOf(), str, len); + if( result == 0 ) return -1; // The other string is smaller than this + + return result; + } + + int result = memcmp(AddressOf(), str, length); + if( result == 0 && length < len ) return 1; // The other string is larger than this + + return result; +} + +size_t asCString::RecalculateLength() +{ + SetLength(strlen(AddressOf())); + + return length; +} + +//----------------------------------------------------------------------------- +// Helper functions + +bool operator ==(const asCString &a, const char *b) +{ + return a.Compare(b) == 0; +} + +bool operator !=(const asCString &a, const char *b) +{ + return a.Compare(b) != 0; +} + +bool operator ==(const asCString &a, const asCString &b) +{ + return a.Compare(b) == 0; +} + +bool operator !=(const asCString &a, const asCString &b) +{ + return a.Compare(b) != 0; +} + +bool operator ==(const char *a, const asCString &b) +{ + return b.Compare(a) == 0; +} + +bool operator !=(const char *a, const asCString &b) +{ + return b.Compare(a) != 0; +} + +bool operator <(const asCString &a, const asCString &b) +{ + return a.Compare(b) < 0; +} + +asCString operator +(const asCString &a, const asCString &b) +{ + asCString res = a; + res += b; + + return res; +} + +asCString operator +(const char *a, const asCString &b) +{ + asCString res = a; + res += b; + + return res; +} + +asCString operator +(const asCString &a, const char *b) +{ + asCString res = a; + res += b; + + return res; +} + diff --git a/AngelScript/source/as_string.h b/AngelScript/source/as_string.h new file mode 100644 index 000000000..40b962b1e --- /dev/null +++ b/AngelScript/source/as_string.h @@ -0,0 +1,106 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + +// This class has been designed to be easy to use, but not necessarily efficiency. +// It doesn't use shared string memory, or reference counting. It keeps track of +// string length, memory size. It also makes sure that the string is null-terminated. + +#ifndef AS_STRING_H +#define AS_STRING_H + +#include +#include + +class asCString +{ +public: + asCString(); + ~asCString(); + + asCString(const asCString &); + asCString(const char *); + asCString(const char *, size_t length); + explicit asCString(char); + + void Allocate(size_t len, bool keepData); + void SetLength(size_t len); + size_t GetLength() const; + + void Concatenate(const char *str, size_t length); + asCString &operator +=(const asCString &); + asCString &operator +=(const char *); + asCString &operator +=(char); + + void Assign(const char *str, size_t length); + asCString &operator =(const asCString &); + asCString &operator =(const char *); + asCString &operator =(char); + + asCString SubString(size_t start, size_t length = (size_t)(-1)) const; + + size_t Format(const char *fmt, ...); + + int Compare(const char *str) const; + int Compare(const asCString &str) const; + int Compare(const char *str, size_t length) const; + + char *AddressOf(); + const char *AddressOf() const; + char &operator [](size_t index); + const char &operator[](size_t index) const; + size_t RecalculateLength(); + +protected: + unsigned int length; + union + { + char *dynamic; + char local[12]; + }; +}; + +// Helper functions + +bool operator ==(const asCString &, const asCString &); +bool operator !=(const asCString &, const asCString &); + +bool operator ==(const asCString &, const char *); +bool operator !=(const asCString &, const char *); + +bool operator ==(const char *, const asCString &); +bool operator !=(const char *, const asCString &); + +bool operator <(const asCString &, const asCString &); + +asCString operator +(const asCString &, const char *); +asCString operator +(const char *, const asCString &); +asCString operator +(const asCString &, const asCString &); + +#endif diff --git a/AngelScript/source/as_string_util.cpp b/AngelScript/source/as_string_util.cpp new file mode 100644 index 000000000..3ccc15563 --- /dev/null +++ b/AngelScript/source/as_string_util.cpp @@ -0,0 +1,264 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com + +*/ + +#include "as_config.h" + +#include // va_list, va_start(), etc +#include // strtod(), strtol() +#include // _vsnprintf() +#include // some compilers declare memcpy() here +#include // setlocale() + +#if !defined(AS_NO_MEMORY_H) +#include +#endif + +#include "as_string.h" +#include "as_string_util.h" + +BEGIN_AS_NAMESPACE + +double asStringScanDouble(const char *string, size_t *numScanned) +{ + char *end; + + // WinCE doesn't have setlocale. Some quick testing on my current platform + // still manages to parse the numbers such as "3.14" even if the decimal for the + // locale is ",". +#if !defined(_WIN32_WCE) && !defined(ANDROID) + // Set the locale to C so that we are guaranteed to parse the float value correctly + asCString orig = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, "C"); +#endif + + double res = strtod(string, &end); + +#if !defined(_WIN32_WCE) && !defined(ANDROID) + // Restore the locale + setlocale(LC_NUMERIC, orig.AddressOf()); +#endif + + if( numScanned ) + *numScanned = end - string; + + return res; +} + +asQWORD asStringScanUInt64(const char *string, int base, size_t *numScanned) +{ + asASSERT(base == 10 || base == 16); + + const char *end = string; + + asQWORD res = 0; + if( base == 10 ) + { + while( *end >= '0' && *end <= '9' ) + { + res *= 10; + res += *end++ - '0'; + } + } + else if( base == 16 ) + { + while( (*end >= '0' && *end <= '9') || + (*end >= 'a' && *end <= 'f') || + (*end >= 'A' && *end <= 'F') ) + { + res *= 16; + if( *end >= '0' && *end <= '9' ) + res += *end++ - '0'; + else if( *end >= 'a' && *end <= 'f' ) + res += *end++ - 'a' + 10; + else if( *end >= 'A' && *end <= 'F' ) + res += *end++ - 'A' + 10; + } + } + + if( numScanned ) + *numScanned = end - string; + + return res; +} + +// +// The function will encode the unicode code point into the outEncodedBuffer, and then +// return the length of the encoded value. If the input value is not a valid unicode code +// point, then the function will return -1. +// +// This function is taken from the AngelCode ToolBox. +// +int asStringEncodeUTF8(unsigned int value, char *outEncodedBuffer) +{ + unsigned char *buf = (unsigned char*)outEncodedBuffer; + + int length = -1; + + if( value <= 0x7F ) + { + buf[0] = static_cast(value); + return 1; + } + else if( value >= 0x80 && value <= 0x7FF ) + { + // Encode it with 2 characters + buf[0] = static_cast(0xC0 + (value >> 6)); + length = 2; + } + else if( (value >= 0x800 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFF) ) + { + // Note: Values 0xD800 to 0xDFFF are not valid unicode characters + buf[0] = static_cast(0xE0 + (value >> 12)); + length = 3; + } + else if( value >= 0x10000 && value <= 0x10FFFF ) + { + buf[0] = static_cast(0xF0 + (value >> 18)); + length = 4; + } + + int n = length-1; + for( ; n > 0; n-- ) + { + buf[n] = static_cast(0x80 + (value & 0x3F)); + value >>= 6; + } + + return length; +} + +// +// The function will decode an UTF8 character and return the unicode code point. +// outLength will receive the number of bytes that were decoded. +// +// This function is taken from the AngelCode ToolBox. +// +int asStringDecodeUTF8(const char *encodedBuffer, unsigned int *outLength) +{ + const unsigned char *buf = (const unsigned char*)encodedBuffer; + + int value = 0; + int length = -1; + unsigned char byte = buf[0]; + if( (byte & 0x80) == 0 ) + { + // This is the only byte + if( outLength ) *outLength = 1; + return byte; + } + else if( (byte & 0xE0) == 0xC0 ) + { + // There is one more byte + value = int(byte & 0x1F); + length = 2; + + // The value at this moment must not be less than 2, because + // that should have been encoded with one byte only. + if( value < 2 ) + length = -1; + } + else if( (byte & 0xF0) == 0xE0 ) + { + // There are two more bytes + value = int(byte & 0x0F); + length = 3; + } + else if( (byte & 0xF8) == 0xF0 ) + { + // There are three more bytes + value = int(byte & 0x07); + length = 4; + } + + int n = 1; + for( ; n < length; n++ ) + { + byte = buf[n]; + if( (byte & 0xC0) == 0x80 ) + value = (value << 6) + int(byte & 0x3F); + else + break; + } + + if( n == length ) + { + if( outLength ) *outLength = (unsigned)length; + return value; + } + + // The byte sequence isn't a valid UTF-8 byte sequence. + return -1; +} + +// +// The function will encode the unicode code point into the outEncodedBuffer, and then +// return the length of the encoded value. If the input value is not a valid unicode code +// point, then the function will return -1. +// +// This function is taken from the AngelCode ToolBox. +// +int asStringEncodeUTF16(unsigned int value, char *outEncodedBuffer) +{ + if( value < 0x10000 ) + { +#ifndef AS_BIG_ENDIAN + outEncodedBuffer[0] = (value & 0xFF); + outEncodedBuffer[1] = ((value >> 8) & 0xFF); +#else + outEncodedBuffer[1] = (value & 0xFF); + outEncodedBuffer[0] = ((value >> 8) & 0xFF); +#endif + return 2; + } + else + { + value -= 0x10000; + int surrogate1 = ((value >> 10) & 0x3FF) + 0xD800; + int surrogate2 = (value & 0x3FF) + 0xDC00; + +#ifndef AS_BIG_ENDIAN + outEncodedBuffer[0] = (surrogate1 & 0xFF); + outEncodedBuffer[1] = ((surrogate1 >> 8) & 0xFF); + outEncodedBuffer[2] = (surrogate2 & 0xFF); + outEncodedBuffer[3] = ((surrogate2 >> 8) & 0xFF); +#else + outEncodedBuffer[1] = (surrogate1 & 0xFF); + outEncodedBuffer[0] = ((surrogate1 >> 8) & 0xFF); + outEncodedBuffer[3] = (surrogate2 & 0xFF); + outEncodedBuffer[2] = ((surrogate2 >> 8) & 0xFF); +#endif + + return 4; + } +} + + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_string_util.h b/AngelScript/source/as_string_util.h new file mode 100644 index 000000000..d7c048389 --- /dev/null +++ b/AngelScript/source/as_string_util.h @@ -0,0 +1,49 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +#ifndef AS_STRING_UTIL_H +#define AS_STRING_UTIL_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +double asStringScanDouble(const char *string, size_t *numScanned); +asQWORD asStringScanUInt64(const char *string, int base, size_t *numScanned); + +int asStringEncodeUTF8(unsigned int value, char *outEncodedBuffer); +int asStringDecodeUTF8(const char *encodedBuffer, unsigned int *outLength); + +int asStringEncodeUTF16(unsigned int value, char *outEncodedBuffer); + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_texts.h b/AngelScript/source/as_texts.h new file mode 100644 index 000000000..2f244a870 --- /dev/null +++ b/AngelScript/source/as_texts.h @@ -0,0 +1,219 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_texts.h +// +// These are text strings used through out the library +// + + +#ifndef AS_TEXTS_H +#define AS_TEXTS_H + +// Compiler messages + +#define TXT_s_ALREADY_DECLARED "'%s' already declared" +#define TXT_ARG_NOT_LVALUE "Argument cannot be assigned. Output will be discarded." +#define TXT_ASSIGN_IN_GLOBAL_EXPR "Assignments are not allowed in global expressions" + +#define TXT_BOTH_MUST_BE_SAME "Both expressions must have the same type" +#define TXT_BOTH_CONDITIONS_MUST_CALL_CONSTRUCTOR "Both conditions must call constructor" + +#define TXT_CALLING_NONCONST_METHOD_ON_TEMP "A non-const method is called on temporary object. Changes to the object may be lost." +#define TXT_CANDIDATES_ARE "Candidates are:" +#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS "Can't call a constructor in loops" +#define TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH "Can't call a constructor in switch" +#define TXT_CANNOT_CALL_CONSTRUCTOR_TWICE "Can't call a constructor multiple times" +#define TXT_CANNOT_INHERIT_FROM_s "Can't inherit from '%s'" +#define TXT_CANNOT_INHERIT_FROM_MULTIPLE_CLASSES "Can't inherit from multiple classes" +#define TXT_CANNOT_INHERIT_FROM_SELF "Can't inherit from itself, or another class that inherits from this class" +#define TXT_CANNOT_INSTANCIATE_TEMPLATE_s_WITH_s "Can't instanciate template '%s' with subtype '%s'" +#define TXT_CANT_IMPLICITLY_CONVERT_s_TO_s "Can't implicitly convert from '%s' to '%s'." +#define TXT_CANT_RETURN_VALUE "Can't return value when return type is 'void'" +#define TXT_CHANGE_SIGN "Implicit conversion changed sign of value" +#define TXT_COMPILING_s "Compiling %s" +#define TXT_COMPOUND_ASGN_WITH_PROP "Compound assignments with property accessors are not allowed" +#define TXT_CONSTRUCTOR_NAME_ERROR "The constructor name must be the same as the class" + +#define TXT_DATA_TYPE_CANT_BE_s "Data type can't be '%s'" +#define TXT_DEFAULT_MUST_BE_LAST "The default case must be the last one" +#define TXT_DESTRUCTOR_MAY_NOT_HAVE_PARM "The destructor must not have any parameters" +#define TXT_DUPLICATE_SWITCH_CASE "Duplicate switch case" + +#define TXT_ELSE_WITH_EMPTY_STATEMENT "Else with empty statement" +#define TXT_EMPTY_SWITCH "Empty switch statement" +#define TXT_EXPECTED_s "Expected '%s'" +#define TXT_EXPECTED_CONSTANT "Expected constant" +#define TXT_EXPECTED_DATA_TYPE "Expected data type" +#define TXT_EXPECTED_EXPRESSION_VALUE "Expected expression value" +#define TXT_EXPECTED_IDENTIFIER "Expected identifier" +#define TXT_EXPECTED_METHOD_OR_PROPERTY "Expected method or property" +#define TXT_EXPECTED_ONE_OF "Expected one of: " +#define TXT_EXPECTED_OPERATOR "Expected operator" +#define TXT_EXPECTED_s_OR_s "Expected '%s' or '%s'" +#define TXT_EXPECTED_POST_OPERATOR "Expected post operator" +#define TXT_EXPECTED_PRE_OPERATOR "Expected pre operator" +#define TXT_EXPECTED_STRING "Expected string" +#define TXT_EXPR_MUST_BE_BOOL "Expression must be of boolean type" + +#define TXT_FOUND_MULTIPLE_ENUM_VALUES "Found multiple matching enum values" +#define TXT_FUNCTION_IN_GLOBAL_EXPR "Function calls are not allowed in global expressions" +#define TXT_FUNCTION_ALREADY_EXIST "A function with the same name and parameters already exist" +#define TXT_FUNCTION_s_NOT_FOUND "Function '%s' not found" + +#define TXT_GET_SET_ACCESSOR_TYPE_MISMATCH_FOR_s "The property '%s' has mismatching types for the get and set accessors" + +#define TXT_HANDLE_COMPARISON "The operand is implicitly converted to handle in order to compare them" + +#define TXT_IDENTIFIER_s_NOT_DATA_TYPE "Identifier '%s' is not a data type" +#define TXT_IF_WITH_EMPTY_STATEMENT "If with empty statement" +#define TXT_ILLEGAL_MEMBER_TYPE "Illegal member type" +// TODO: Should be TXT_ILLEGAL_OPERATION_ON_s +#define TXT_ILLEGAL_OPERATION "Illegal operation on this datatype" +#define TXT_ILLEGAL_OPERATION_ON_s "Illegal operation on '%s'" +#define TXT_ILLEGAL_TARGET_TYPE_FOR_REF_CAST "Illegal target type for reference cast" +#define TXT_ILLEGAL_VARIABLE_NAME_s "Illegal variable name '%s'." +#define TXT_INC_OP_IN_GLOBAL_EXPR "Incremental operators are not allowed in global expressions" +#define TXT_INIT_LIST_CANNOT_BE_USED_WITH_s "Initialization lists cannot be used with '%s'" +#define TXT_INTERFACE_s_ALREADY_IMPLEMENTED "The interface '%s' is already implemented" +#define TXT_INVALID_BREAK "Invalid 'break'" +#define TXT_INVALID_CHAR_LITERAL "Invalid character literal" +#define TXT_INVALID_CONTINUE "Invalid 'continue'" +#define TXT_INVALID_ESCAPE_SEQUENCE "Invalid escape sequence" +#define TXT_INVALID_SCOPE "Invalid scope resolution" +#define TXT_INVALID_TYPE "Invalid type" +#define TXT_INVALID_UNICODE_FORMAT_EXPECTED_d "Invalid unicode escape sequence, expected %d hex digits" +#define TXT_INVALID_UNICODE_VALUE "Invalid unicode code point" +#define TXT_INVALID_UNICODE_SEQUENCE_IN_SRC "Invalid unicode sequence in source" + +#define TXT_METHOD_IN_GLOBAL_EXPR "Object method calls are not allowed in global expressions" +#define TXT_MISSING_IMPLEMENTATION_OF_s "Missing implementation of '%s'" +#define TXT_MORE_THAN_ONE_MATCHING_OP "Found more than one matching operator" +#define TXT_MULTIPLE_MATCHING_SIGNATURES_TO_s "Multiple matching signatures to '%s'" +#define TXT_MULTIPLE_PROP_GET_ACCESSOR_FOR_s "Found multiple get accessors for property '%s'" +#define TXT_MULTIPLE_PROP_SET_ACCESSOR_FOR_s "Found multiple set accessors for property '%s'" +#define TXT_MULTILINE_STRINGS_NOT_ALLOWED "Multiline strings are not allowed in this application" +#define TXT_MUST_BE_OBJECT "Only objects have constructors" +#define TXT_MUST_RETURN_VALUE "Must return a value" + +#define TXT_NAME_CONFLICT_s_EXTENDED_TYPE "Name conflict. '%s' is an extended data type." +#define TXT_NAME_CONFLICT_s_GLOBAL_PROPERTY "Name conflict. '%s' is a global property." +#define TXT_NAME_CONFLICT_s_IS_NAMED_TYPE "Name conflict. '%s' is a named type." +#define TXT_NAME_CONFLICT_s_STRUCT "Name conflict. '%s' is a class." +#define TXT_NAME_CONFLICT_s_OBJ_PROPERTY "Name conflict. '%s' is an object property." +#define TXT_NO_APPROPRIATE_INDEX_OPERATOR "No appropriate indexing operator found" +#define TXT_NO_CONVERSION_s_TO_s "No conversion from '%s' to '%s' available." +#define TXT_NO_CONVERSION_s_TO_MATH_TYPE "No conversion from '%s' to math type available." +#define TXT_NO_DEFAULT_CONSTRUCTOR_FOR_s "No default constructor for object of type '%s'." +#define TXT_NO_DEFAULT_COPY_OP "There is no copy operator for this type available." +#define TXT_NO_COPY_CONSTRUCTOR_FOR_s "No copy constructor for object of type '%s'." +#define TXT_NO_MATCHING_SIGNATURES_TO_s "No matching signatures to '%s'" +#define TXT_NO_MATCHING_OP_FOUND_FOR_TYPE_s "No matching operator that takes the type '%s' found" +#define TXT_NO_MATCHING_OP_FOUND_FOR_TYPES_s_AND_s "No matching operator that takes the types '%s' and '%s' found" +#define TXT_NON_CONST_METHOD_ON_CONST_OBJ "Non-const method call on read-only object reference" +#define TXT_NOT_ALL_PATHS_RETURN "Not all paths return a value" +#define TXT_s_NOT_DECLARED "'%s' is not declared" +#define TXT_NOT_EXACT "Implicit conversion of value is not exact" +#define TXT_s_NOT_INITIALIZED "'%s' is not initialized." +#define TXT_s_NOT_MEMBER_OF_s "'%s' is not a member of '%s'" +#define TXT_NOT_VALID_REFERENCE "Not a valid reference" +#define TXT_NOT_VALID_LVALUE "Not a valid lvalue" + +#define TXT_OBJECT_DOESNT_SUPPORT_INDEX_OP "Type '%s' doesn't support the indexing operator" +#define TXT_OBJECT_HANDLE_NOT_SUPPORTED "Object handle is not supported for this type" +#define TXT_ONLY_OBJECTS_MAY_USE_REF_INOUT "Only object types that support object handles can use &inout. Use &in or &out instead" +#define TXT_ONLY_ONE_ARGUMENT_IN_CAST "A cast operator has one argument" +#define TXT_ONLY_ONE_FUNCTION_ALLOWED "The code must contain one and only one function" +#define TXT_ONLY_ONE_VARIABLE_ALLOWED "The code must contain one and only one global variable" + +#define TXT_PARAMETER_ALREADY_DECLARED "Parameter already declared" +#define TXT_PARAMETER_CANT_BE_s "Parameter type can't be '%s'" +#define TXT_POSSIBLE_LOSS_OF_PRECISION "Conversion from double to float, possible loss of precision" +#define TXT_PROPERTY_CANT_BE_CONST "Class properties cannot be declared as const" +#define TXT_PROPERTY_HAS_NO_GET_ACCESSOR "The property has no get accessor" +#define TXT_PROPERTY_HAS_NO_SET_ACCESSOR "The property has no set accessor" + +#define TXT_REF_IS_READ_ONLY "Reference is read-only" +#define TXT_REF_IS_TEMP "Reference is temporary" +#define TXT_RETURN_CANT_BE_s "Return type can't be '%s'" + +#define TXT_SCRIPT_FUNCTIONS_DOESNT_SUPPORT_RETURN_REF "Script functions must not return references" +#define TXT_SIGNED_UNSIGNED_MISMATCH "Signed/Unsigned mismatch" +#define TXT_STRINGS_NOT_RECOGNIZED "Strings are not recognized by the application" +#define TXT_SWITCH_CASE_MUST_BE_CONSTANT "Case expressions must be constants" +#define TXT_SWITCH_MUST_BE_INTEGRAL "Switch expressions must be integral numbers" + +#define TXT_TOO_MANY_ARRAY_DIMENSIONS "Too many array dimensions" +#define TXT_TYPE_s_NOT_AVAILABLE_FOR_MODULE "Type '%s' is not available for this module" + +#define TXT_UNEXPECTED_END_OF_FILE "Unexpected end of file" +#define TXT_UNEXPECTED_TOKEN_s "Unexpected token '%s'" +#define TXT_UNINITIALIZED_GLOBAL_VAR_s "Use of uninitialized global variable '%s'." +#define TXT_UNREACHABLE_CODE "Unreachable code" +#define TXT_UNUSED_SCRIPT_NODE "Unused script node" + +#define TXT_VALUE_TOO_LARGE_FOR_TYPE "Value is too large for data type" + +// Engine message + +#define TXT_INVALID_CONFIGURATION "Invalid configuration" +#define TXT_VALUE_TYPE_MUST_HAVE_SIZE "A value type must be registered with a non-zero size" +#define TXT_TYPE_s_IS_MISSING_BEHAVIOURS "Type '%s' is missing behaviours" +#define TXT_ILLEGAL_BEHAVIOUR_FOR_TYPE "The behaviour is not compatible with the type" +#define TXT_GC_REQUIRE_ADD_REL_GC_BEHAVIOUR "A garbage collected type must have the addref, release, and all gc behaviours" +#define TXT_SCOPE_REQUIRE_REL_BEHAVIOUR "A scoped reference type must have the release behaviour" +#define TXT_REF_REQUIRE_ADD_REL_BEHAVIOUR "A reference type must have the addref and release behaviours" +#define TXT_NON_POD_REQUIRE_CONSTR_DESTR_BEHAVIOUR "A non-pod value type must have the constructor and destructor behaviours" +#define TXT_CANNOT_PASS_TYPE_s_BY_VAL "Can't pass type '%s' by value unless the application type is informed in the registration" +#define TXT_CANNOT_RET_TYPE_s_BY_VAL "Can't return type '%s' by value unless the application type is informed in the registration" + +// Internal names + +#ifdef AS_DEPRECATED +// Deprecated since 2.18.0, 2009-12-08 +#define TXT_EXECUTESTRING "ExecuteString" +#endif +#define TXT_PROPERTY "Property" +#define TXT_SYSTEM_FUNCTION "System function" +#define TXT_VARIABLE_DECL "Variable declaration" + +// Exceptions + +#define TXT_STACK_OVERFLOW "Stack overflow" +#define TXT_NULL_POINTER_ACCESS "Null pointer access" +#define TXT_DIVIDE_BY_ZERO "Divide by zero" +#define TXT_UNRECOGNIZED_BYTE_CODE "Unrecognized byte code" +#define TXT_INVALID_CALLING_CONVENTION "Invalid calling convention" +#define TXT_UNBOUND_FUNCTION "Unbound function called" +#define TXT_OUT_OF_BOUNDS "Out of range" + +#endif diff --git a/AngelScript/source/as_thread.cpp b/AngelScript/source/as_thread.cpp new file mode 100644 index 000000000..8f9a72209 --- /dev/null +++ b/AngelScript/source/as_thread.cpp @@ -0,0 +1,270 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2008 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_thread.cpp +// +// Functions for multi threading support +// + +#include "as_config.h" +#include "as_thread.h" +#include "as_atomic.h" + +BEGIN_AS_NAMESPACE + +// Singleton +asCThreadManager *threadManager = 0; + +//====================================================================== + +extern "C" +{ + +AS_API int asThreadCleanup() +{ + // As this function can be called globally, + // we can't assume the threadManager exists + if( threadManager ) + return threadManager->CleanupLocalData(); + + return 0; +} + +} + +//====================================================================== + +asCThreadManager::asCThreadManager() +{ +#ifdef AS_NO_THREADS + tld = 0; +#endif + refCount.set(1); +} + +void asCThreadManager::AddRef() +{ + refCount.atomicInc(); +} + +void asCThreadManager::Release() +{ + if( refCount.atomicDec() == 0 ) + { + // The last engine has been destroyed, so we + // need to delete the thread manager as well + asDELETE(this,asCThreadManager); + threadManager = 0; + } +} + +asCThreadManager::~asCThreadManager() +{ +#ifndef AS_NO_THREADS + ENTERCRITICALSECTION(criticalSection); + + // Delete all thread local datas + asSMapNode *cursor = 0; + if( tldMap.MoveFirst(&cursor) ) + { + do + { + if( tldMap.GetValue(cursor) ) + { + asDELETE(tldMap.GetValue(cursor),asCThreadLocalData); + } + } while( tldMap.MoveNext(&cursor, cursor) ); + } + + LEAVECRITICALSECTION(criticalSection); +#else + if( tld ) + { + asDELETE(tld,asCThreadLocalData); + } + tld = 0; +#endif +} + +int asCThreadManager::CleanupLocalData() +{ +#ifndef AS_NO_THREADS + int r = 0; +#if defined AS_POSIX_THREADS + asDWORD id = (asDWORD)pthread_self(); +#elif defined AS_WINDOWS_THREADS + asDWORD id = GetCurrentThreadId(); +#endif + + ENTERCRITICALSECTION(criticalSection); + + asSMapNode *cursor = 0; + if( tldMap.MoveTo(&cursor, id) ) + { + asCThreadLocalData *tld = tldMap.GetValue(cursor); + + // Can we really remove it at this time? + if( tld->activeContexts.GetLength() == 0 ) + { + asDELETE(tld,asCThreadLocalData); + tldMap.Erase(cursor); + r = 0; + } + else + r = asCONTEXT_ACTIVE; + } + + LEAVECRITICALSECTION(criticalSection); + + return r; +#else + if( tld ) + { + if( tld->activeContexts.GetLength() == 0 ) + { + asDELETE(tld,asCThreadLocalData); + tld = 0; + } + else + return asCONTEXT_ACTIVE; + } + return 0; +#endif +} + +#ifndef AS_NO_THREADS +asCThreadLocalData *asCThreadManager::GetLocalData(asDWORD threadId) +{ + asCThreadLocalData *tld = 0; + + ENTERCRITICALSECTION(criticalSection); + + asSMapNode *cursor = 0; + if( tldMap.MoveTo(&cursor, threadId) ) + tld = tldMap.GetValue(cursor); + + LEAVECRITICALSECTION(criticalSection); + + return tld; +} + +void asCThreadManager::SetLocalData(asDWORD threadId, asCThreadLocalData *tld) +{ + ENTERCRITICALSECTION(criticalSection); + + tldMap.Insert(threadId, tld); + + LEAVECRITICALSECTION(criticalSection); +} +#endif + +asCThreadLocalData *asCThreadManager::GetLocalData() +{ +#ifndef AS_NO_THREADS +#if defined AS_POSIX_THREADS + asDWORD id = (asDWORD)pthread_self(); +#elif defined AS_WINDOWS_THREADS + asDWORD id = GetCurrentThreadId(); +#endif + + asCThreadLocalData *tld = GetLocalData(id); + if( tld == 0 ) + { + // Create a new tld + tld = asNEW(asCThreadLocalData)(); + SetLocalData(id, tld); + } + + return tld; +#else + if( tld == 0 ) + tld = asNEW(asCThreadLocalData)(); + + return tld; +#endif +} + +//========================================================================= + +asCThreadLocalData::asCThreadLocalData() +{ +} + +asCThreadLocalData::~asCThreadLocalData() +{ +} + +//========================================================================= + +#ifndef AS_NO_THREADS +asCThreadCriticalSection::asCThreadCriticalSection() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_init(&criticalSection, 0); +#elif defined AS_WINDOWS_THREADS + InitializeCriticalSection(&criticalSection); +#endif +} + +asCThreadCriticalSection::~asCThreadCriticalSection() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_destroy(&criticalSection); +#elif defined AS_WINDOWS_THREADS + DeleteCriticalSection(&criticalSection); +#endif +} + +void asCThreadCriticalSection::Enter() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_lock(&criticalSection); +#elif defined AS_WINDOWS_THREADS + EnterCriticalSection(&criticalSection); +#endif +} + +void asCThreadCriticalSection::Leave() +{ +#if defined AS_POSIX_THREADS + pthread_mutex_unlock(&criticalSection); +#elif defined AS_WINDOWS_THREADS + LeaveCriticalSection(&criticalSection); +#endif +} +#endif + +//======================================================================== + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_thread.h b/AngelScript/source/as_thread.h new file mode 100644 index 000000000..d88fbaead --- /dev/null +++ b/AngelScript/source/as_thread.h @@ -0,0 +1,100 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2008 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + + +// +// as_thread.h +// +// Classes for multi threading support +// + +#ifndef AS_THREAD_H +#define AS_THREAD_H + +#include "as_config.h" +#include "as_string.h" +#include "as_array.h" +#include "as_map.h" +#include "as_atomic.h" +#include "as_criticalsection.h" + +BEGIN_AS_NAMESPACE + +class asCThreadLocalData; + +class asCThreadManager +{ +public: + asCThreadManager(); + + asCThreadLocalData *GetLocalData(); + int CleanupLocalData(); + + void AddRef(); + void Release(); + +protected: + ~asCThreadManager(); + asCAtomic refCount; + +#ifndef AS_NO_THREADS + asCThreadLocalData *GetLocalData(asDWORD threadId); + void SetLocalData(asDWORD threadId, asCThreadLocalData *tld); + + asCMap tldMap; + DECLARECRITICALSECTION(criticalSection); +#else + asCThreadLocalData *tld; +#endif +}; + +extern asCThreadManager *threadManager; + +//====================================================================== + +class asIScriptContext; + +class asCThreadLocalData +{ +public: + asCArray activeContexts; + asCString string; + +protected: + friend class asCThreadManager; + + asCThreadLocalData(); + ~asCThreadLocalData(); +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_tokendef.h b/AngelScript/source/as_tokendef.h new file mode 100644 index 000000000..1e50bd85e --- /dev/null +++ b/AngelScript/source/as_tokendef.h @@ -0,0 +1,290 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_tokendef.h +// +// Definitions for tokens identifiable by the tokenizer +// + + +#ifndef AS_TOKENDEF_H +#define AS_TOKENDEF_H + +#include "as_config.h" + +BEGIN_AS_NAMESPACE + +enum eTokenType +{ + ttUnrecognizedToken, + + ttEnd, // End of file + + // White space and comments + ttWhiteSpace, // ' ', '\t', '\r', '\n', UTF8 byte-order-mark + ttOnelineComment, // // \n + ttMultilineComment, // /* */ + + // Atoms + ttIdentifier, // abc123 + ttIntConstant, // 1234 + ttFloatConstant, // 12.34e56f + ttDoubleConstant, // 12.34e56 + ttStringConstant, // "123" + ttMultilineStringConstant, // + ttHeredocStringConstant, // """text""" + ttNonTerminatedStringConstant, // "123 + ttBitsConstant, // 0xFFFF + + // Math operators + ttPlus, // + + ttMinus, // - + ttStar, // * + ttSlash, // / + ttPercent, // % + + ttHandle, // @ + + ttAddAssign, // += + ttSubAssign, // -= + ttMulAssign, // *= + ttDivAssign, // /= + ttModAssign, // %= + + ttOrAssign, // |= + ttAndAssign, // &= + ttXorAssign, // ^= + ttShiftLeftAssign, // <<= + ttShiftRightLAssign, // >>= + ttShiftRightAAssign, // >>>= + + ttInc, // ++ + ttDec, // -- + + ttDot, // . + ttScope, // :: + + // Statement tokens + ttAssignment, // = + ttEndStatement, // ; + ttListSeparator, // , + ttStartStatementBlock, // { + ttEndStatementBlock, // } + ttOpenParanthesis, // ( + ttCloseParanthesis, // ) + ttOpenBracket, // [ + ttCloseBracket, // ] + ttAmp, // & + + // Bitwise operators + ttBitOr, // | + ttBitNot, // ~ + ttBitXor, // ^ + ttBitShiftLeft, // << + ttBitShiftRight, // >> + ttBitShiftRightArith, // >>> + + // Compare operators + ttEqual, // == + ttNotEqual, // != + ttLessThan, // < + ttGreaterThan, // > + ttLessThanOrEqual, // <= + ttGreaterThanOrEqual, // >= + + ttQuestion, // ? + ttColon, // : + + // Reserved keywords + ttIf, // if + ttElse, // else + ttFor, // for + ttWhile, // while + ttBool, // bool + ttImport, // import + ttInt, // int + ttInt8, // int8 + ttInt16, // int16 + ttInt64, // int64 + ttInterface, // interface + ttIs, // is + ttNotIs, // !is + ttUInt, // uint + ttUInt8, // uint8 + ttUInt16, // uint16 + ttUInt64, // uint64 + ttFloat, // float + ttVoid, // void + ttTrue, // true + ttFalse, // false + ttReturn, // return + ttNot, // not + ttAnd, // and + ttOr, // or + ttXor, // xor + ttBreak, // break + ttContinue, // continue + ttConst, // const + ttDo, // do + ttDouble, // double + ttSwitch, // switch + ttCase, // case + ttDefault, // default + ttIn, // in + ttOut, // out + ttInOut, // inout + ttNull, // null + ttClass, // class + ttTypedef, // typedef + ttEnum, // enum + ttCast // cast +}; + +struct sTokenWord +{ + const char *word; + eTokenType tokenType; +}; + +sTokenWord const tokenWords[] = +{ + {"+" , ttPlus}, + {"-" , ttMinus}, + {"*" , ttStar}, + {"/" , ttSlash}, + {"%" , ttPercent}, + {"=" , ttAssignment}, + {"." , ttDot}, + {"+=" , ttAddAssign}, + {"-=" , ttSubAssign}, + {"*=" , ttMulAssign}, + {"/=" , ttDivAssign}, + {"%=" , ttModAssign}, + {"|=" , ttOrAssign}, + {"&=" , ttAndAssign}, + {"^=" , ttXorAssign}, + {"<<=" , ttShiftLeftAssign}, + {">>=" , ttShiftRightLAssign}, + {">>>=" , ttShiftRightAAssign}, + {"|" , ttBitOr}, + {"~" , ttBitNot}, + {"^" , ttBitXor}, + {"<<" , ttBitShiftLeft}, + {">>" , ttBitShiftRight}, + {">>>" , ttBitShiftRightArith}, + {";" , ttEndStatement}, + {"," , ttListSeparator}, + {"{" , ttStartStatementBlock}, + {"}" , ttEndStatementBlock}, + {"(" , ttOpenParanthesis}, + {")" , ttCloseParanthesis}, + {"[" , ttOpenBracket}, + {"]" , ttCloseBracket}, + {"?" , ttQuestion}, + {":" , ttColon}, + {"::" , ttScope}, + {"==" , ttEqual}, + {"!=" , ttNotEqual}, + {"<" , ttLessThan}, + {">" , ttGreaterThan}, + {"<=" , ttLessThanOrEqual}, + {">=" , ttGreaterThanOrEqual}, + {"++" , ttInc}, + {"--" , ttDec}, + {"&" , ttAmp}, + {"!" , ttNot}, + {"||" , ttOr}, + {"&&" , ttAnd}, + {"^^" , ttXor}, + {"@" , ttHandle}, + {"and" , ttAnd}, + {"bool" , ttBool}, + {"break" , ttBreak}, + {"cast" , ttCast}, + {"const" , ttConst}, + {"continue" , ttContinue}, + {"do" , ttDo}, +#ifdef AS_USE_DOUBLE_AS_FLOAT + {"double" , ttFloat}, +#else + {"double" , ttDouble}, +#endif + {"else" , ttElse}, + {"false" , ttFalse}, + {"float" , ttFloat}, + {"for" , ttFor}, + {"if" , ttIf}, + {"in" , ttIn}, + {"inout" , ttInOut}, + {"import" , ttImport}, + {"int" , ttInt}, + {"int8" , ttInt8}, + {"int16" , ttInt16}, + {"int32" , ttInt}, + {"int64" , ttInt64}, + {"interface" , ttInterface}, + {"is" , ttIs}, + {"!is" , ttNotIs}, + {"not" , ttNot}, + {"null" , ttNull}, + {"or" , ttOr}, + {"out" , ttOut}, + {"return" , ttReturn}, + {"true" , ttTrue}, + {"void" , ttVoid}, + {"while" , ttWhile}, + {"uint" , ttUInt}, + {"uint8" , ttUInt8}, + {"uint16" , ttUInt16}, + {"uint32" , ttUInt}, + {"uint64" , ttUInt64}, + {"switch" , ttSwitch}, + {"class" , ttClass}, + {"case" , ttCase}, + {"default" , ttDefault}, + {"xor" , ttXor}, + {"typedef" , ttTypedef}, + {"enum" , ttEnum}, +}; + +const unsigned int numTokenWords = sizeof(tokenWords)/sizeof(sTokenWord); + +const char * const whiteSpace = " \t\r\n"; + +// Some keywords that are not considered tokens by the parser +const char * const THIS_TOKEN = "this"; +const char * const FROM_TOKEN = "from"; +const char * const SUPER_TOKEN = "super"; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_tokenizer.cpp b/AngelScript/source/as_tokenizer.cpp new file mode 100644 index 000000000..20ac995d6 --- /dev/null +++ b/AngelScript/source/as_tokenizer.cpp @@ -0,0 +1,463 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2009 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_tokenizer.cpp +// +// This class identifies tokens from the script code +// + +#include "as_config.h" +#include "as_tokenizer.h" +#include "as_tokendef.h" + +#if !defined(AS_NO_MEMORY_H) +#include +#endif +#include // strcmp() + +BEGIN_AS_NAMESPACE + +asCTokenizer::asCTokenizer() +{ +} + +asCTokenizer::~asCTokenizer() +{ +} + +const char *asGetTokenDefinition(int tokenType) +{ + if( tokenType == ttUnrecognizedToken ) return ""; + if( tokenType == ttEnd ) return ""; + if( tokenType == ttWhiteSpace ) return ""; + if( tokenType == ttOnelineComment ) return ""; + if( tokenType == ttMultilineComment ) return ""; + if( tokenType == ttIdentifier ) return ""; + if( tokenType == ttIntConstant ) return ""; + if( tokenType == ttFloatConstant ) return ""; + if( tokenType == ttDoubleConstant ) return ""; + if( tokenType == ttStringConstant ) return ""; + if( tokenType == ttMultilineStringConstant ) return ""; + if( tokenType == ttNonTerminatedStringConstant ) return ""; + if( tokenType == ttBitsConstant ) return ""; + if( tokenType == ttHeredocStringConstant ) return ""; + + for( asUINT n = 0; n < numTokenWords; n++ ) + if( tokenWords[n].tokenType == tokenType ) + return tokenWords[n].word; + + return 0; +} + +eTokenType asCTokenizer::GetToken(const char *source, size_t sourceLength, size_t *tokenLength, asETokenClass *tc) +{ + asASSERT(source != 0); + asASSERT(tokenLength != 0); + + this->source = source; + this->sourceLength = sourceLength; + + asETokenClass t = ParseToken(); + if( tc ) *tc = t; + + // Copy the output to the token + *tokenLength = this->tokenLength; + + return tokenType; +} + +asETokenClass asCTokenizer::ParseToken() +{ + if( IsWhiteSpace() ) return asTC_WHITESPACE; + if( IsComment() ) return asTC_COMMENT; + if( IsConstant() ) return asTC_VALUE; + if( IsIdentifier() ) return asTC_IDENTIFIER; + if( IsKeyWord() ) return asTC_KEYWORD; + + // If none of the above this is an unrecognized token + // We can find the length of the token by advancing + // one step and trying to identify a token there + tokenType = ttUnrecognizedToken; + tokenLength = 1; + + return asTC_UNKNOWN; +} + +bool asCTokenizer::IsWhiteSpace() +{ + // Treat UTF8 byte-order-mark (EF BB BF) as whitespace + if( sourceLength >= 3 && + asBYTE(source[0]) == 0xEFu && + asBYTE(source[1]) == 0xBBu && + asBYTE(source[2]) == 0xBFu ) + { + tokenType = ttWhiteSpace; + tokenLength = 3; + return true; + } + + // Group all other white space characters into one + size_t n; + int numWsChars = (int)strlen(whiteSpace); + for( n = 0; n < sourceLength; n++ ) + { + bool isWhiteSpace = false; + for( int w = 0; w < numWsChars; w++ ) + { + if( source[n] == whiteSpace[w] ) + { + isWhiteSpace = true; + break; + } + } + if( !isWhiteSpace ) break; + } + + if( n > 0 ) + { + tokenType = ttWhiteSpace; + tokenLength = n; + return true; + } + + return false; +} + +bool asCTokenizer::IsComment() +{ + if( sourceLength < 2 ) + return false; + + if( source[0] != '/' ) + return false; + + if( source[1] == '/' ) + { + // One-line comment + + // Find the length + size_t n; + for( n = 2; n < sourceLength; n++ ) + { + if( source[n] == '\n' ) + break; + } + + tokenType = ttOnelineComment; + tokenLength = n+1; + + return true; + } + + if( source[1] == '*' ) + { + // Multi-line comment + + // Find the length + size_t n; + for( n = 2; n < sourceLength-1; ) + { + if( source[n++] == '*' && source[n] == '/' ) + break; + } + + tokenType = ttMultilineComment; + tokenLength = n+1; + + return true; + } + + return false; +} + +bool asCTokenizer::IsConstant() +{ + // Starting with number + if( source[0] >= '0' && source[0] <= '9' ) + { + // Is it a hexadecimal number? + if( source[0] == '0' && sourceLength >= 1 && (source[1] == 'x' || source[1] == 'X') ) + { + size_t n; + for( n = 2; n < sourceLength; n++ ) + { + if( !(source[n] >= '0' && source[n] <= '9') && + !(source[n] >= 'a' && source[n] <= 'f') && + !(source[n] >= 'A' && source[n] <= 'F') ) + break; + } + + tokenType = ttBitsConstant; + tokenLength = n; + return true; + } + + size_t n; + for( n = 1; n < sourceLength; n++ ) + { + if( source[n] < '0' || source[n] > '9' ) + break; + } + + if( n < sourceLength && source[n] == '.' ) + { + n++; + for( ; n < sourceLength; n++ ) + { + if( source[n] < '0' || source[n] > '9' ) + break; + } + + if( n < sourceLength && (source[n] == 'e' || source[n] == 'E') ) + { + n++; + if( n < sourceLength && (source[n] == '-' || source[n] == '+') ) + n++; + + for( ; n < sourceLength; n++ ) + { + if( source[n] < '0' || source[n] > '9' ) + break; + } + } + + if( n < sourceLength && (source[n] == 'f' || source[n] == 'F') ) + { + tokenType = ttFloatConstant; + tokenLength = n + 1; + } + else + { +#ifdef AS_USE_DOUBLE_AS_FLOAT + tokenType = ttFloatConstant; +#else + tokenType = ttDoubleConstant; +#endif + tokenLength = n; + } + return true; + } + + tokenType = ttIntConstant; + tokenLength = n; + return true; + } + + // String constant between double or single quotes + if( source[0] == '"' || source[0] == '\'' ) + { + // Is it a normal string constant or a heredoc string constant? + if( sourceLength >= 6 && source[0] == '"' && source[1] == '"' && source[2] == '"' ) + { + // Heredoc string constant (spans multiple lines, no escape sequences) + + // Find the length + size_t n; + for( n = 3; n < sourceLength-2; n++ ) + { + if( source[n] == '"' && source[n+1] == '"' && source[n+2] == '"' ) + break; + } + + tokenType = ttHeredocStringConstant; + tokenLength = n+3; + } + else + { + // Normal string constant + tokenType = ttStringConstant; + char quote = source[0]; + bool evenSlashes = true; + size_t n; + for( n = 1; n < sourceLength; n++ ) + { +#ifdef AS_DOUBLEBYTE_CHARSET + // Double-byte characters are only allowed for ASCII + if( (source[n] & 0x80) && engine->ep.scanner == 0 ) + { + // This is a leading character in a double byte character, + // include both in the string and continue processing. + n++; + continue; + } +#endif + + if( source[n] == '\n' ) + tokenType = ttMultilineStringConstant; + if( source[n] == quote && evenSlashes ) + { + tokenLength = n+1; + return true; + } + if( source[n] == '\\' ) evenSlashes = !evenSlashes; else evenSlashes = true; + } + + tokenType = ttNonTerminatedStringConstant; + tokenLength = n; + } + + return true; + } + + return false; +} + +bool asCTokenizer::IsIdentifier() +{ + // Starting with letter or underscore + if( (source[0] >= 'a' && source[0] <= 'z') || + (source[0] >= 'A' && source[0] <= 'Z') || + source[0] == '_' ) + { + tokenType = ttIdentifier; + tokenLength = 1; + + for( size_t n = 1; n < sourceLength; n++ ) + { + if( (source[n] >= 'a' && source[n] <= 'z') || + (source[n] >= 'A' && source[n] <= 'Z') || + (source[n] >= '0' && source[n] <= '9') || + source[n] == '_' ) + tokenLength++; + else + break; + } + + // Make sure the identifier isn't a reserved keyword + if( tokenLength > 50 ) return true; + + char test[51]; + memcpy(test, source, tokenLength); + test[tokenLength] = 0; + + for( asUINT i = 0; i < numTokenWords; i++ ) + { + if( strcmp(test, tokenWords[i].word) == 0 ) + return false; + } + + return true; + } + + return false; +} + +bool asCTokenizer::IsKeyWord() +{ + // Fill the list with all possible keywords + // Check each character against all the keywords in the list, + // remove keywords that don't match. When only one remains and + // it matches the source completely we have found a match. + int words[numTokenWords]; + asUINT n; + for( n = 0; n < numTokenWords; n++ ) + words[n] = n; + + int numWords = numTokenWords; + int lastPossible = -1; + + n = 0; + while( n < sourceLength && numWords >= 0 ) + { + for( int i = 0; i < numWords; i++ ) + { + if( tokenWords[words[i]].word[n] == '\0' ) + { + // tokens that end with a character that can be part of an + // identifier require an extra verification to guarantee that + // we don't split an identifier token, e.g. the "!is" token + // and the "!isTrue" expression. + if( ((tokenWords[words[i]].word[n-1] >= 'a' && tokenWords[words[i]].word[n-1] <= 'z') || + (tokenWords[words[i]].word[n-1] >= 'A' && tokenWords[words[i]].word[n-1] <= 'Z')) && + ((source[n] >= 'a' && source[n] <= 'z') || + (source[n] >= 'A' && source[n] <= 'Z') || + (source[n] >= '0' && source[n] <= '9') || + (source[n] == '_')) ) + { + // The token doesn't really match, even though + // the start of the source matches the token + words[i--] = words[--numWords]; + } + else if( numWords > 1 ) + { + // It's possible that a longer token matches, so let's + // remember this match and continue searching + lastPossible = words[i]; + words[i--] = words[--numWords]; + continue; + } + else + { + // Only one token matches, so we return it + tokenType = tokenWords[words[i]].tokenType; + tokenLength = n; + return true; + } + } + else if( tokenWords[words[i]].word[n] != source[n] ) + { + // The token doesn't match + words[i--] = words[--numWords]; + } + } + n++; + } + + // The source length ended or there where no more matchable words + if( numWords ) + { + // If any of the tokenWords also end at this + // position then we have found the matching token + for( int i = 0; i < numWords; i++ ) + { + if( tokenWords[words[i]].word[n] == '\0' ) + { + tokenType = tokenWords[words[i]].tokenType; + tokenLength = n; + return true; + } + } + } + + // It is still possible that a shorter token was found + if( lastPossible > -1 ) + { + tokenType = tokenWords[lastPossible].tokenType; + tokenLength = strlen(tokenWords[lastPossible].word); + return true; + } + + return false; +} + +END_AS_NAMESPACE + diff --git a/AngelScript/source/as_tokenizer.h b/AngelScript/source/as_tokenizer.h new file mode 100644 index 000000000..0ed1073ae --- /dev/null +++ b/AngelScript/source/as_tokenizer.h @@ -0,0 +1,75 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2008 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_tokenizer.cpp +// +// This class identifies tokens from the script code +// + + + +#ifndef AS_TOKENIZER_H +#define AS_TOKENIZER_H + +#include "as_tokendef.h" + +BEGIN_AS_NAMESPACE + +const char *asGetTokenDefinition(int tokenType); + +class asCTokenizer +{ +public: + asCTokenizer(); + ~asCTokenizer(); + + eTokenType GetToken(const char *source, size_t sourceLength, size_t *tokenLength, asETokenClass *tc = 0); + +protected: + asETokenClass ParseToken(); + bool IsWhiteSpace(); + bool IsComment(); + bool IsConstant(); + bool IsKeyWord(); + bool IsIdentifier(); + + const char *source; + size_t sourceLength; + + eTokenType tokenType; + size_t tokenLength; +}; + +END_AS_NAMESPACE + +#endif + diff --git a/AngelScript/source/as_typeinfo.cpp b/AngelScript/source/as_typeinfo.cpp new file mode 100644 index 000000000..42a6c8217 --- /dev/null +++ b/AngelScript/source/as_typeinfo.cpp @@ -0,0 +1,135 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_typeinfo.cpp +// +// This class holds extra type info for the compiler +// + +#include "as_config.h" +#include "as_typeinfo.h" + +BEGIN_AS_NAMESPACE + +asCTypeInfo::asCTypeInfo() +{ + isTemporary = false; + stackOffset = 0; + isConstant = false; + isVariable = false; + isExplicitHandle = false; + qwordValue = 0; +} + +void asCTypeInfo::Set(const asCDataType &dt) +{ + dataType = dt; + + isTemporary = false; + stackOffset = 0; + isConstant = false; + isVariable = false; + isExplicitHandle = false; + qwordValue = 0; +} + +void asCTypeInfo::SetVariable(const asCDataType &dt, int stackOffset, bool isTemporary) +{ + Set(dt); + + this->isVariable = true; + this->isTemporary = isTemporary; + this->stackOffset = (short)stackOffset; +} + +void asCTypeInfo::SetConstantQW(const asCDataType &dt, asQWORD value) +{ + Set(dt); + + isConstant = true; + qwordValue = value; +} + +void asCTypeInfo::SetConstantDW(const asCDataType &dt, asDWORD value) +{ + Set(dt); + + isConstant = true; + dwordValue = value; +} + +void asCTypeInfo::SetConstantB(const asCDataType &dt, asBYTE value) +{ + Set(dt); + + isConstant = true; + byteValue = value; +} + +void asCTypeInfo::SetConstantF(const asCDataType &dt, float value) +{ + Set(dt); + + isConstant = true; + floatValue = value; +} + +void asCTypeInfo::SetConstantD(const asCDataType &dt, double value) +{ + Set(dt); + + isConstant = true; + doubleValue = value; +} + +void asCTypeInfo::SetNullConstant() +{ + Set(asCDataType::CreateNullHandle()); + isConstant = true; + isExplicitHandle = true; + qwordValue = 0; +} + +void asCTypeInfo::SetDummy() +{ + SetConstantQW(asCDataType::CreatePrimitive(ttInt, true), 0); +} + +bool asCTypeInfo::IsNullConstant() +{ + if( isConstant && dataType.IsObjectHandle() ) + return true; + + return false; +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_typeinfo.h b/AngelScript/source/as_typeinfo.h new file mode 100644 index 000000000..357e7978e --- /dev/null +++ b/AngelScript/source/as_typeinfo.h @@ -0,0 +1,85 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_typeinfo.h +// +// This class holds extra type info for the compiler +// + + + +#ifndef AS_TYPEINFO_H +#define AS_TYPEINFO_H + +#include "as_config.h" +#include "as_datatype.h" + +BEGIN_AS_NAMESPACE + +struct asCTypeInfo +{ + asCTypeInfo(); + void Set(const asCDataType &dataType); + + void SetVariable(const asCDataType &dataType, int stackOffset, bool isTemporary); + void SetConstantB(const asCDataType &dataType, asBYTE value); + void SetConstantQW(const asCDataType &dataType, asQWORD value); + void SetConstantDW(const asCDataType &dataType, asDWORD value); + void SetConstantF(const asCDataType &dataType, float value); + void SetConstantD(const asCDataType &dataType, double value); + void SetNullConstant(); + void SetDummy(); + + bool IsNullConstant(); + + asCDataType dataType; + bool isTemporary : 1; + bool isConstant : 1; + bool isVariable : 1; + bool isExplicitHandle : 1; + short dummy : 12; + short stackOffset; + union + { + asQWORD qwordValue; + double doubleValue; + asDWORD dwordValue; + float floatValue; + int intValue; + asWORD wordValue; + asBYTE byteValue; + }; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AngelScript/source/as_variablescope.cpp b/AngelScript/source/as_variablescope.cpp new file mode 100644 index 000000000..c02d5503d --- /dev/null +++ b/AngelScript/source/as_variablescope.cpp @@ -0,0 +1,129 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_variablescope.cpp +// +// A manager class for variable declarations +// + + +#include "as_config.h" +#include "as_variablescope.h" + +BEGIN_AS_NAMESPACE + +asCVariableScope::asCVariableScope(asCVariableScope *parent) +{ + this->parent = parent; + Reset(); +} + +asCVariableScope::~asCVariableScope() +{ + Reset(); +} + +void asCVariableScope::Reset() +{ + isBreakScope = false; + isContinueScope = false; + + for( asUINT n = 0; n < variables.GetLength(); n++ ) + if( variables[n] ) + { + asDELETE(variables[n],sVariable); + } + variables.SetLength(0); +} + +int asCVariableScope::DeclareVariable(const char *name, const asCDataType &type, int stackOffset) +{ + // TODO: optimize: Improve linear search + // See if the variable is already declared + if( strcmp(name, "") != 0 ) + { + for( asUINT n = 0; n < variables.GetLength(); n++ ) + { + if( variables[n]->name == name ) + return -1; + } + } + + sVariable *var = asNEW(sVariable); + var->name = name; + var->type = type; + var->stackOffset = stackOffset; + var->isInitialized = false; + var->isPureConstant = false; + + // Parameters are initialized + if( stackOffset <= 0 ) + var->isInitialized = true; + + variables.PushLast(var); + + return 0; +} + +sVariable *asCVariableScope::GetVariable(const char *name) +{ + // TODO: optimize: Improve linear search + // Find the variable + for( asUINT n = 0; n < variables.GetLength(); n++ ) + { + if( variables[n]->name == name ) + return variables[n]; + } + + if( parent ) + return parent->GetVariable(name); + + return 0; +} + +sVariable *asCVariableScope::GetVariableByOffset(int offset) +{ + // TODO: optimize: Improve linear search + // Find the variable + for( asUINT n = 0; n < variables.GetLength(); n++ ) + { + if( variables[n]->stackOffset == offset ) + return variables[n]; + } + + if( parent ) + return parent->GetVariableByOffset(offset); + + return 0; +} + +END_AS_NAMESPACE diff --git a/AngelScript/source/as_variablescope.h b/AngelScript/source/as_variablescope.h new file mode 100644 index 000000000..cd6dd7013 --- /dev/null +++ b/AngelScript/source/as_variablescope.h @@ -0,0 +1,80 @@ +/* + AngelCode Scripting Library + Copyright (c) 2003-2007 Andreas Jonsson + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + The original version of this library can be located at: + http://www.angelcode.com/angelscript/ + + Andreas Jonsson + andreas@angelcode.com +*/ + + +// +// as_variablescope.h +// +// A manager class for variable declarations +// + + +#ifndef AS_VARIABLESCOPE_H +#define AS_VARIABLESCOPE_H + +#include "as_array.h" +#include "as_string.h" +#include "as_datatype.h" + +BEGIN_AS_NAMESPACE + +struct sVariable +{ + asCString name; + asCDataType type; + int stackOffset; + bool isInitialized; + bool isPureConstant; + asQWORD constantValue; +}; + +class asCVariableScope +{ +public: + asCVariableScope(asCVariableScope *parent); + ~asCVariableScope(); + + void Reset(); + + int DeclareVariable(const char *name, const asCDataType &type, int stackOffset); + sVariable *GetVariable(const char *name); + sVariable *GetVariableByOffset(int offset); + + asCVariableScope *parent; + + bool isBreakScope; + bool isContinueScope; + + asCArray variables; +}; + +END_AS_NAMESPACE + +#endif diff --git a/AudioEncode/CMakeLists.txt b/AudioEncode/CMakeLists.txt new file mode 100644 index 000000000..7ba57ebb8 --- /dev/null +++ b/AudioEncode/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (HEADERS ) +SET (CPPS + encoder.cpp) + +ADD_LIBRARY(AudioEncode STATIC ${HEADERS} ${CPPS}) +target_link_libraries(AudioEncode libacm) \ No newline at end of file diff --git a/AudioEncode/encoder.cpp b/AudioEncode/encoder.cpp new file mode 100644 index 000000000..934d62898 --- /dev/null +++ b/AudioEncode/encoder.cpp @@ -0,0 +1,125 @@ +#include +#include +#include +#include "audio_encode.h" +#include "mono.h" +#include "Aencode.h" + +long aenc_ReadSamp(void *data) +{ + FILE *f = (FILE *)data; + int a,b; + a = getc(f); + if (a==EOF) + return ReadSampleEof; + + b = getc(f); + if (b==EOF) + return ReadSampleEof; + return (short)((b<<8) + a); +} + +int aenc_Compress(char *input_filename,char *output_filename,int *input_levels,int *input_samples,int *input_rate,int *input_channels,float *input_factor,float *input_volscale) +{ + FILE *in, *out; + long result; + + int levels, samples_per_subband; + unsigned sample_rate, channels; + float factor, volume_scale; + int levels_set=0, samples_per_subband_set=0, sample_rate_set=0, + channels_set=0, factor_set=0, volume_scale_set=0; + + + in = fopen(input_filename, "rb"); + if (!in){ + mprintf((0,"AENC: Unable to open %s for input.\n", input_filename)); + return 0; + } + + if(input_levels){ + levels = *input_levels; //Levels (default 7 or for 2k total) + levels_set = 1; + if (levels < 0 || levels > 16){ + mprintf((0,"AENC: Warning: levels outside of the range 0 to 16\n")); + } + } + + if(input_samples){ + samples_per_subband = *input_samples; //Samples per subband (default 16 or for 2k total) + samples_per_subband_set = 1; + if (samples_per_subband < 1 || samples_per_subband > 1024){ + mprintf((0,"AENC: Warning: samples per subband not in the range 1 to 1024\n")); + } + } + + if(input_rate){ + sample_rate = *input_rate; //Sample rate (default 22K) + sample_rate_set = 1; + if (sample_rate != 11025 && sample_rate != 22050 && sample_rate != 44100){ + mprintf((0,"AENC: Warning: sample rate not 11025, 22050, or 44100\n")); + } + } + + if(input_channels){ + channels = *input_channels; + channels_set = 1; + if (channels!=1 && channels!=2){ + mprintf((0,"AENC: Warning: /C channels not 1 or 2\n")); + } + } + + if(input_factor){ + factor = *input_factor;// Factor of compression (default 4 for 22K, 8 for 44K) + factor_set = 1; + + if (factor < 1.0f && factor) + factor = 1.0f/factor; + + if (factor <= 0.0f){ + mprintf((0,"AENC: Warning: compression factor <= 0.0\n")); + factor = 1.0f; + } + } + + if(input_volscale){ + volume_scale = *input_volscale;// Volume scaling (slightly <= 1.0, default ,97) + volume_scale_set = 1; + } + + if (!levels_set && !samples_per_subband_set) + { + levels=7, samples_per_subband=16; + } + else if (!samples_per_subband_set) + { + samples_per_subband = 2048 / (1<>1; + + for (levels=0; subbands; subbands>>=1, ++levels); + } + + if (!sample_rate_set) sample_rate = 22050; + if (!channels_set) channels = 1; + if (!factor_set) factor = sample_rate<=22050?4.0f:8.0f; + if (!volume_scale_set) volume_scale = .97f; + + out = fopen(output_filename, "wb"); + if (!out){ + mprintf((0,"AENC: Unable to open %s for output.\n", output_filename)); + return 0; + } + + result = AudioEncode( aenc_ReadSamp, in, channels, sample_rate, volume_scale, out, levels, samples_per_subband, 1.0f/factor ); + + fclose(out); + fclose(in); + + return 1; +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..79ed2dc7b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,133 @@ +# CMake compatibility issues: don't modify this, please! +CMAKE_MINIMUM_REQUIRED( VERSION 2.4.6 ) + +MARK_AS_ADVANCED(CMAKE_BACKWARDS_COMPATIBILITY) +# allow more human readable "if then else" constructs +SET( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE ) + + + +PROJECT(Descent3) + +IF (UNIX) +SET (D3_GAMEDIR "~/Descent3/") + + SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wno-write-strings -m32") + SET(CMAKE_CXX_COMPILER "g++") + SET(CMAKE_CXX_FLAGS "-O0 -g -Wno-write-strings -Wno-multichar -m32") + SET(CMAKE_C_FLAGS "-O0 -g -m32") + SET(CMAKE_C_COMPILER "gcc") + SET(CMAKE_FIND_LIBRARY_PREFIXES "lib") + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + FIND_PACKAGE( SDL REQUIRED ) + FIND_PACKAGE( SDL_image REQUIRED ) +MESSAGE( "SDL Include Dir is " ${SDL_INCLUDE_DIR} ) +ENDIF() + +IF (UNIX AND NOT APPLE) + MESSAGE("Building for Linux") + + ADD_DEFINITIONS( -D_DEBUG -D__LINUX__ -DLINUX -D_MAX_PATH=260 -D_MAX_FNAME=256 -D_REENRANT -DMONO -D__32BIT__ -DHAVEALLOCA_H -D_USE_OGL_ACTIVE_TEXTURES) + SET(PLATFORM_INCLUDES "lib/linux" ${SDL_INCLUDE_DIR} ) +ENDIF() + +IF (APPLE) + SET(D3_GAMEDIR "~/Descent3X") + MESSAGE("Building for MAC OSX") + ADD_DEFINITIONS( -DMONO -D_DEBUG -D__LINUX__ -DLINUX -D_MAX_PATH=260 -D_MAX_FNAME=256 -D_REENRANT -DMACOSX=1 -D_USE_OGL_ACTIVE_TEXTURES) + SET(PLATFORM_INCLUDES "lib/linux" ${SDL_INCLUDE_DIR} "/usr/X11/include" ) +ENDIF() + +IF (WIN32) + SET(D3_GAMEDIR "c:/games/Descent3/") + set (CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "lib/win" "lib/win/directx") + SET(CMAKE_CXX_FLAGS_DEBUG "/Od /Gm /EHsc /RTC1 /MTd /W3 /nologo /c /ZI /TP /errorReport:prompt") + SET(CMAKE_CXX_FLAGS_RELEASE "/O2 /GL /FD /EHsc /MT /W3 /nologo /c /Zi /TP /errorReport:prompt") + + + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBC") + SET(CMAKE_MODULE_LINKER_FLAGS "/SAFESEH:NO /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBC") + ADD_DEFINITIONS (-DIS_WINDOWS -D_CRT_SECURE_NO_WARNINGS -DMONO -DWIN32 -D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) + + SET(PLATFORM_INCLUDES "lib/win/directx" "lib/win") + + SET(CMAKE_FIND_LIBRARY_PREFIXES "") + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") + + FIND_LIBRARY(DSOUND_LIBRARY NAMES dsound "${CMAKE_SOURCE_DIR}/lib/win" "${CMAKE_SOURCE_DIR}/lib/win/directx") + FIND_LIBRARY(DINPUT_LIBRARY NAMES dinput "${CMAKE_SOURCE_DIR}/lib/win" "${CMAKE_SOURCE_DIR}/lib/win/directx") + FIND_LIBRARY(DXGUID_LIBRARY NAMES dxguid "${CMAKE_SOURCE_DIR}/lib/win" "${CMAKE_SOURCE_DIR}/lib/win/directx") + FIND_LIBRARY(DDRAW_LIBRARY NAMES ddraw "${CMAKE_SOURCE_DIR}/lib/win" "${CMAKE_SOURCE_DIR}/lib/win/directx") +ENDIF () + +MESSAGE("Install will copy files to ${D3_GAMEDIR}") + +include_directories("lib" "Descent3" ${PLATFORM_INCLUDES}) + +# file(GLOB_RECURSE INCS "*.h") + +# project version +SET( ${PROJECT_NAME}_MAJOR_VERSION 1 ) +SET( ${PROJECT_NAME}_MINOR_VERSION 5 ) +SET( ${PROJECT_NAME}_PATCH_LEVEL 500 ) + +ADD_SUBDIRECTORY (2dlib) +ADD_SUBDIRECTORY (AngelScript) +ADD_SUBDIRECTORY (AudioEncode) +ADD_SUBDIRECTORY (bitmap) +ADD_SUBDIRECTORY (cfile) +ADD_SUBDIRECTORY (czip) +ADD_SUBDIRECTORY (d3music) + +IF (WIN32) +ADD_SUBDIRECTORY (dd_grwin32) +ADD_SUBDIRECTORY (dd_vidwin32) +ADD_SUBDIRECTORY (ddio_win) +ADD_SUBDIRECTORY (win32) +ADD_SUBDIRECTORY (dd_sndlib) +ENDIF() + +IF (UNIX) +ADD_SUBDIRECTORY(linux) +ADD_SUBDIRECTORY(dd_lnxsound) +ADD_SUBDIRECTORY(ddvid_lnx) +ADD_SUBDIRECTORY(lnxcontroller) +ADD_SUBDIRECTORY(ddio_lnx) +ENDIF() + +ADD_SUBDIRECTORY (ddio_common) +ADD_SUBDIRECTORY (fix) +ADD_SUBDIRECTORY (grtext) +ADD_SUBDIRECTORY (manage) +ADD_SUBDIRECTORY (mem) +ADD_SUBDIRECTORY (misc) +ADD_SUBDIRECTORY (model) +ADD_SUBDIRECTORY (module) +ADD_SUBDIRECTORY (movie) +ADD_SUBDIRECTORY (music) +ADD_SUBDIRECTORY (networking) +ADD_SUBDIRECTORY (physics) +ADD_SUBDIRECTORY (renderer) +ADD_SUBDIRECTORY (rtperformance) +ADD_SUBDIRECTORY (sndlib) +ADD_SUBDIRECTORY (stream_audio) +ADD_SUBDIRECTORY (ui) +ADD_SUBDIRECTORY (unzip) +ADD_SUBDIRECTORY (vecmat) +ADD_SUBDIRECTORY (libmve) +ADD_SUBDIRECTORY (md5) +ADD_SUBDIRECTORY (libacm) +ADD_SUBDIRECTORY (Descent3 ) + +# For now we don't need to build the scripts under windows, so we'll only include +# the directory when building for linux/osx. In the future we may want to to fix bugs, etc. +IF(UNIX) +ADD_SUBDIRECTORY (netgames) +ADD_SUBDIRECTORY (netcon) +ADD_SUBDIRECTORY (scripts) +ENDIF() + +# set default cmake build type to Debug (None Debug Release RelWithDebInfo MinSizeRel) +IF( NOT CMAKE_BUILD_TYPE ) +SET( CMAKE_BUILD_TYPE "Debug" ) +ENDIF() diff --git a/Descent3.sln b/Descent3.sln new file mode 100644 index 000000000..96bba3a6f --- /dev/null +++ b/Descent3.sln @@ -0,0 +1,265 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Descent3", "Descent3\Descent3.vcproj", "{B39FE17E-EB71-47B1-9BF5-B5BD978629C9}" + ProjectSection(ProjectDependencies) = postProject + {8FD88770-B02A-4FF5-BC39-AF8931F8C77F} = {8FD88770-B02A-4FF5-BC39-AF8931F8C77F} + {437ECD7F-A465-4011-88FE-013EB3EBF87E} = {437ECD7F-A465-4011-88FE-013EB3EBF87E} + {8B3ACFD1-6DC7-40B6-9432-068804C6FB79} = {8B3ACFD1-6DC7-40B6-9432-068804C6FB79} + {6B0E26F9-0E59-4E0E-9A15-9D063B99F18B} = {6B0E26F9-0E59-4E0E-9A15-9D063B99F18B} + {2263C3F8-F157-4B2E-8F1D-92BFA1B1ED9F} = {2263C3F8-F157-4B2E-8F1D-92BFA1B1ED9F} + {1BC569F8-69B9-4D3E-A800-FDD1587F0FB3} = {1BC569F8-69B9-4D3E-A800-FDD1587F0FB3} + {BB253BE6-03B8-43EF-93CC-136650A5E2F3} = {BB253BE6-03B8-43EF-93CC-136650A5E2F3} + {B52DBAD2-47AC-4F7C-9814-94BD16B95201} = {B52DBAD2-47AC-4F7C-9814-94BD16B95201} + {2755A2CE-3BF3-46B6-A01E-D8FB5F67B17A} = {2755A2CE-3BF3-46B6-A01E-D8FB5F67B17A} + {94B371BC-FE5F-4EAF-988B-D61B95F5456E} = {94B371BC-FE5F-4EAF-988B-D61B95F5456E} + {E35D73B9-68D4-4368-80F2-5F48BB8C698F} = {E35D73B9-68D4-4368-80F2-5F48BB8C698F} + {0F09F4B7-76BB-4752-8FA1-A05CB548E572} = {0F09F4B7-76BB-4752-8FA1-A05CB548E572} + {8D4BC4B6-91AE-4AAF-8006-2914317769D0} = {8D4BC4B6-91AE-4AAF-8006-2914317769D0} + {A0D95EAB-2AAA-4D11-B6D0-733B8E85D61E} = {A0D95EAB-2AAA-4D11-B6D0-733B8E85D61E} + {F770A8A4-7A9C-4154-9198-8B6E1AB03BE0} = {F770A8A4-7A9C-4154-9198-8B6E1AB03BE0} + {7D86068E-A828-4739-8FA9-6551EEACB816} = {7D86068E-A828-4739-8FA9-6551EEACB816} + {8E47638A-0FB0-4363-8DC0-8D12CD7C9609} = {8E47638A-0FB0-4363-8DC0-8D12CD7C9609} + {EB468887-5B0E-48F6-91A8-FDF0C86868AC} = {EB468887-5B0E-48F6-91A8-FDF0C86868AC} + {86B52775-0CD5-40F3-AA84-E1B6B5095B7B} = {86B52775-0CD5-40F3-AA84-E1B6B5095B7B} + {464EB673-F74E-40CB-BB9F-3025584A15E4} = {464EB673-F74E-40CB-BB9F-3025584A15E4} + {FA086773-496F-404C-9F9F-197189652E57} = {FA086773-496F-404C-9F9F-197189652E57} + {ADCC916D-6162-472C-BF06-11340783CC75} = {ADCC916D-6162-472C-BF06-11340783CC75} + {1D5E5F6D-493B-4842-AF75-EAB964156B3F} = {1D5E5F6D-493B-4842-AF75-EAB964156B3F} + {0994A269-A8A0-4EC2-B277-2EC55AACD9A8} = {0994A269-A8A0-4EC2-B277-2EC55AACD9A8} + {D6A4235C-22C0-4692-8C04-EEDFFE616790} = {D6A4235C-22C0-4692-8C04-EEDFFE616790} + {D335E14B-F7DE-459B-AA2D-B4240D3E754E} = {D335E14B-F7DE-459B-AA2D-B4240D3E754E} + {E3DDFA42-B171-48A7-9AF4-4A119BD567BC} = {E3DDFA42-B171-48A7-9AF4-4A119BD567BC} + {ACB4642D-3E0E-4001-BB48-1919D25542AC} = {ACB4642D-3E0E-4001-BB48-1919D25542AC} + {EAE27F25-5F86-482C-B790-C231BA53210F} = {EAE27F25-5F86-482C-B790-C231BA53210F} + {6F8C5125-ABA9-4D4A-AEF5-AAF0F0CE67AD} = {6F8C5125-ABA9-4D4A-AEF5-AAF0F0CE67AD} + {4EF1901F-6248-4B10-80A9-A07916C697CA} = {4EF1901F-6248-4B10-80A9-A07916C697CA} + {84763615-0D99-47D6-BAD8-272EF381AB3F} = {84763615-0D99-47D6-BAD8-272EF381AB3F} + {FAE4E806-4DC4-4346-917F-87F87EEDBFC3} = {FAE4E806-4DC4-4346-917F-87F87EEDBFC3} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "2dlib", "2dlib\2dlib.vcproj", "{0F09F4B7-76BB-4752-8FA1-A05CB548E572}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bitmap", "bitmap\bitmap.vcproj", "{84763615-0D99-47D6-BAD8-272EF381AB3F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cfile", "cfile\cfile.vcproj", "{464EB673-F74E-40CB-BB9F-3025584A15E4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "czip", "czip\czip.vcproj", "{94B371BC-FE5F-4EAF-988B-D61B95F5456E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "d3music", "d3music\d3music.vcproj", "{BB253BE6-03B8-43EF-93CC-136650A5E2F3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dd_sndlib", "dd_sndlib\dd_sndlib.vcproj", "{1D5E5F6D-493B-4842-AF75-EAB964156B3F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dd_grwin32", "dd_grwin32\dd_grwin32.vcproj", "{EB468887-5B0E-48F6-91A8-FDF0C86868AC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddio_common", "ddio_common\ddio_common.vcproj", "{8D4BC4B6-91AE-4AAF-8006-2914317769D0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddio_win", "ddio_win\ddio_win.vcproj", "{4EF1901F-6248-4B10-80A9-A07916C697CA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dd_vidwin32", "dd_vidwin32\dd_vidwin32.vcproj", "{D6A4235C-22C0-4692-8C04-EEDFFE616790}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fix", "fix\fix.vcproj", "{FA086773-496F-404C-9F9F-197189652E57}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grtext", "grtext\grtext.vcproj", "{ACB4642D-3E0E-4001-BB48-1919D25542AC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "manage", "manage\manage.vcproj", "{D335E14B-F7DE-459B-AA2D-B4240D3E754E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mem", "mem\mem.vcproj", "{86B52775-0CD5-40F3-AA84-E1B6B5095B7B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "misc", "misc\misc.vcproj", "{1BC569F8-69B9-4D3E-A800-FDD1587F0FB3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "model", "model\model.vcproj", "{FAE4E806-4DC4-4346-917F-87F87EEDBFC3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "module", "module\module.vcproj", "{EAE27F25-5F86-482C-B790-C231BA53210F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "movie", "movie\movie.vcproj", "{0994A269-A8A0-4EC2-B277-2EC55AACD9A8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "music", "music\music.vcproj", "{6B0E26F9-0E59-4E0E-9A15-9D063B99F18B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "networking", "networking\networking.vcproj", "{2263C3F8-F157-4B2E-8F1D-92BFA1B1ED9F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "physics", "physics\physics.vcproj", "{ADCC916D-6162-472C-BF06-11340783CC75}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "renderer", "renderer\renderer.vcproj", "{E35D73B9-68D4-4368-80F2-5F48BB8C698F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rtperformance", "rtperformance\rtperformance.vcproj", "{B52DBAD2-47AC-4F7C-9814-94BD16B95201}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sndlib", "sndlib\sndlib.vcproj", "{2755A2CE-3BF3-46B6-A01E-D8FB5F67B17A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stream_audio", "stream_audio\stream_audio.vcproj", "{6F8C5125-ABA9-4D4A-AEF5-AAF0F0CE67AD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ui", "ui\ui.vcproj", "{A0D95EAB-2AAA-4D11-B6D0-733B8E85D61E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unzip", "unzip\unzip.vcproj", "{7D86068E-A828-4739-8FA9-6551EEACB816}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vecmat", "vecmat\vecmat.vcproj", "{E3DDFA42-B171-48A7-9AF4-4A119BD567BC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win32", "win32\win32.vcproj", "{8E47638A-0FB0-4363-8DC0-8D12CD7C9609}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib", "lib\lib.vcproj", "{2CD488EC-D91B-465C-8614-FFC84AF08150}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmve", "libmve\libmve.vcproj", "{8B3ACFD1-6DC7-40B6-9432-068804C6FB79}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "md5", "md5\md5.vcproj", "{F770A8A4-7A9C-4154-9198-8B6E1AB03BE0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libacm", "libacm\libacm.vcproj", "{8FD88770-B02A-4FF5-BC39-AF8931F8C77F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AudioEncode", "AudioEncode\AudioEncode.vcproj", "{437ECD7F-A465-4011-88FE-013EB3EBF87E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AngelScript", "AngelScript\AngelScript.vcproj", "{14BD4CCC-DF5A-4DDA-8AE4-D10153143713}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B39FE17E-EB71-47B1-9BF5-B5BD978629C9}.Debug|Win32.ActiveCfg = Debug|Win32 + {B39FE17E-EB71-47B1-9BF5-B5BD978629C9}.Debug|Win32.Build.0 = Debug|Win32 + {B39FE17E-EB71-47B1-9BF5-B5BD978629C9}.Release|Win32.ActiveCfg = Release|Win32 + {B39FE17E-EB71-47B1-9BF5-B5BD978629C9}.Release|Win32.Build.0 = Release|Win32 + {0F09F4B7-76BB-4752-8FA1-A05CB548E572}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F09F4B7-76BB-4752-8FA1-A05CB548E572}.Debug|Win32.Build.0 = Debug|Win32 + {0F09F4B7-76BB-4752-8FA1-A05CB548E572}.Release|Win32.ActiveCfg = Release|Win32 + {0F09F4B7-76BB-4752-8FA1-A05CB548E572}.Release|Win32.Build.0 = Release|Win32 + {84763615-0D99-47D6-BAD8-272EF381AB3F}.Debug|Win32.ActiveCfg = Debug|Win32 + {84763615-0D99-47D6-BAD8-272EF381AB3F}.Debug|Win32.Build.0 = Debug|Win32 + {84763615-0D99-47D6-BAD8-272EF381AB3F}.Release|Win32.ActiveCfg = Release|Win32 + {84763615-0D99-47D6-BAD8-272EF381AB3F}.Release|Win32.Build.0 = Release|Win32 + {464EB673-F74E-40CB-BB9F-3025584A15E4}.Debug|Win32.ActiveCfg = Debug|Win32 + {464EB673-F74E-40CB-BB9F-3025584A15E4}.Debug|Win32.Build.0 = Debug|Win32 + {464EB673-F74E-40CB-BB9F-3025584A15E4}.Release|Win32.ActiveCfg = Release|Win32 + {464EB673-F74E-40CB-BB9F-3025584A15E4}.Release|Win32.Build.0 = Release|Win32 + {94B371BC-FE5F-4EAF-988B-D61B95F5456E}.Debug|Win32.ActiveCfg = Debug|Win32 + {94B371BC-FE5F-4EAF-988B-D61B95F5456E}.Debug|Win32.Build.0 = Debug|Win32 + {94B371BC-FE5F-4EAF-988B-D61B95F5456E}.Release|Win32.ActiveCfg = Release|Win32 + {94B371BC-FE5F-4EAF-988B-D61B95F5456E}.Release|Win32.Build.0 = Release|Win32 + {BB253BE6-03B8-43EF-93CC-136650A5E2F3}.Debug|Win32.ActiveCfg = Debug|Win32 + {BB253BE6-03B8-43EF-93CC-136650A5E2F3}.Debug|Win32.Build.0 = Debug|Win32 + {BB253BE6-03B8-43EF-93CC-136650A5E2F3}.Release|Win32.ActiveCfg = Release|Win32 + {BB253BE6-03B8-43EF-93CC-136650A5E2F3}.Release|Win32.Build.0 = Release|Win32 + {1D5E5F6D-493B-4842-AF75-EAB964156B3F}.Debug|Win32.ActiveCfg = Debug|Win32 + {1D5E5F6D-493B-4842-AF75-EAB964156B3F}.Debug|Win32.Build.0 = Debug|Win32 + {1D5E5F6D-493B-4842-AF75-EAB964156B3F}.Release|Win32.ActiveCfg = Release|Win32 + {1D5E5F6D-493B-4842-AF75-EAB964156B3F}.Release|Win32.Build.0 = Release|Win32 + {EB468887-5B0E-48F6-91A8-FDF0C86868AC}.Debug|Win32.ActiveCfg = Debug|Win32 + {EB468887-5B0E-48F6-91A8-FDF0C86868AC}.Debug|Win32.Build.0 = Debug|Win32 + {EB468887-5B0E-48F6-91A8-FDF0C86868AC}.Release|Win32.ActiveCfg = Release|Win32 + {EB468887-5B0E-48F6-91A8-FDF0C86868AC}.Release|Win32.Build.0 = Release|Win32 + {8D4BC4B6-91AE-4AAF-8006-2914317769D0}.Debug|Win32.ActiveCfg = Debug|Win32 + {8D4BC4B6-91AE-4AAF-8006-2914317769D0}.Debug|Win32.Build.0 = Debug|Win32 + {8D4BC4B6-91AE-4AAF-8006-2914317769D0}.Release|Win32.ActiveCfg = Release|Win32 + {8D4BC4B6-91AE-4AAF-8006-2914317769D0}.Release|Win32.Build.0 = Release|Win32 + {4EF1901F-6248-4B10-80A9-A07916C697CA}.Debug|Win32.ActiveCfg = Debug|Win32 + {4EF1901F-6248-4B10-80A9-A07916C697CA}.Debug|Win32.Build.0 = Debug|Win32 + {4EF1901F-6248-4B10-80A9-A07916C697CA}.Release|Win32.ActiveCfg = Release|Win32 + {4EF1901F-6248-4B10-80A9-A07916C697CA}.Release|Win32.Build.0 = Release|Win32 + {D6A4235C-22C0-4692-8C04-EEDFFE616790}.Debug|Win32.ActiveCfg = Debug|Win32 + {D6A4235C-22C0-4692-8C04-EEDFFE616790}.Debug|Win32.Build.0 = Debug|Win32 + {D6A4235C-22C0-4692-8C04-EEDFFE616790}.Release|Win32.ActiveCfg = Release|Win32 + {D6A4235C-22C0-4692-8C04-EEDFFE616790}.Release|Win32.Build.0 = Release|Win32 + {FA086773-496F-404C-9F9F-197189652E57}.Debug|Win32.ActiveCfg = Debug|Win32 + {FA086773-496F-404C-9F9F-197189652E57}.Debug|Win32.Build.0 = Debug|Win32 + {FA086773-496F-404C-9F9F-197189652E57}.Release|Win32.ActiveCfg = Release|Win32 + {FA086773-496F-404C-9F9F-197189652E57}.Release|Win32.Build.0 = Release|Win32 + {ACB4642D-3E0E-4001-BB48-1919D25542AC}.Debug|Win32.ActiveCfg = Debug|Win32 + {ACB4642D-3E0E-4001-BB48-1919D25542AC}.Debug|Win32.Build.0 = Debug|Win32 + {ACB4642D-3E0E-4001-BB48-1919D25542AC}.Release|Win32.ActiveCfg = Release|Win32 + {ACB4642D-3E0E-4001-BB48-1919D25542AC}.Release|Win32.Build.0 = Release|Win32 + {D335E14B-F7DE-459B-AA2D-B4240D3E754E}.Debug|Win32.ActiveCfg = Debug|Win32 + {D335E14B-F7DE-459B-AA2D-B4240D3E754E}.Debug|Win32.Build.0 = Debug|Win32 + {D335E14B-F7DE-459B-AA2D-B4240D3E754E}.Release|Win32.ActiveCfg = Release|Win32 + {D335E14B-F7DE-459B-AA2D-B4240D3E754E}.Release|Win32.Build.0 = Release|Win32 + {86B52775-0CD5-40F3-AA84-E1B6B5095B7B}.Debug|Win32.ActiveCfg = Debug|Win32 + {86B52775-0CD5-40F3-AA84-E1B6B5095B7B}.Debug|Win32.Build.0 = Debug|Win32 + {86B52775-0CD5-40F3-AA84-E1B6B5095B7B}.Release|Win32.ActiveCfg = Release|Win32 + {86B52775-0CD5-40F3-AA84-E1B6B5095B7B}.Release|Win32.Build.0 = Release|Win32 + {1BC569F8-69B9-4D3E-A800-FDD1587F0FB3}.Debug|Win32.ActiveCfg = Debug|Win32 + {1BC569F8-69B9-4D3E-A800-FDD1587F0FB3}.Debug|Win32.Build.0 = Debug|Win32 + {1BC569F8-69B9-4D3E-A800-FDD1587F0FB3}.Release|Win32.ActiveCfg = Release|Win32 + {1BC569F8-69B9-4D3E-A800-FDD1587F0FB3}.Release|Win32.Build.0 = Release|Win32 + {FAE4E806-4DC4-4346-917F-87F87EEDBFC3}.Debug|Win32.ActiveCfg = Debug|Win32 + {FAE4E806-4DC4-4346-917F-87F87EEDBFC3}.Debug|Win32.Build.0 = Debug|Win32 + {FAE4E806-4DC4-4346-917F-87F87EEDBFC3}.Release|Win32.ActiveCfg = Release|Win32 + {FAE4E806-4DC4-4346-917F-87F87EEDBFC3}.Release|Win32.Build.0 = Release|Win32 + {EAE27F25-5F86-482C-B790-C231BA53210F}.Debug|Win32.ActiveCfg = Debug|Win32 + {EAE27F25-5F86-482C-B790-C231BA53210F}.Debug|Win32.Build.0 = Debug|Win32 + {EAE27F25-5F86-482C-B790-C231BA53210F}.Release|Win32.ActiveCfg = Release|Win32 + {EAE27F25-5F86-482C-B790-C231BA53210F}.Release|Win32.Build.0 = Release|Win32 + {0994A269-A8A0-4EC2-B277-2EC55AACD9A8}.Debug|Win32.ActiveCfg = Debug|Win32 + {0994A269-A8A0-4EC2-B277-2EC55AACD9A8}.Debug|Win32.Build.0 = Debug|Win32 + {0994A269-A8A0-4EC2-B277-2EC55AACD9A8}.Release|Win32.ActiveCfg = Release|Win32 + {0994A269-A8A0-4EC2-B277-2EC55AACD9A8}.Release|Win32.Build.0 = Release|Win32 + {6B0E26F9-0E59-4E0E-9A15-9D063B99F18B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6B0E26F9-0E59-4E0E-9A15-9D063B99F18B}.Debug|Win32.Build.0 = Debug|Win32 + {6B0E26F9-0E59-4E0E-9A15-9D063B99F18B}.Release|Win32.ActiveCfg = Release|Win32 + {6B0E26F9-0E59-4E0E-9A15-9D063B99F18B}.Release|Win32.Build.0 = Release|Win32 + {2263C3F8-F157-4B2E-8F1D-92BFA1B1ED9F}.Debug|Win32.ActiveCfg = Debug|Win32 + {2263C3F8-F157-4B2E-8F1D-92BFA1B1ED9F}.Debug|Win32.Build.0 = Debug|Win32 + {2263C3F8-F157-4B2E-8F1D-92BFA1B1ED9F}.Release|Win32.ActiveCfg = Release|Win32 + {2263C3F8-F157-4B2E-8F1D-92BFA1B1ED9F}.Release|Win32.Build.0 = Release|Win32 + {ADCC916D-6162-472C-BF06-11340783CC75}.Debug|Win32.ActiveCfg = Debug|Win32 + {ADCC916D-6162-472C-BF06-11340783CC75}.Debug|Win32.Build.0 = Debug|Win32 + {ADCC916D-6162-472C-BF06-11340783CC75}.Release|Win32.ActiveCfg = Release|Win32 + {ADCC916D-6162-472C-BF06-11340783CC75}.Release|Win32.Build.0 = Release|Win32 + {E35D73B9-68D4-4368-80F2-5F48BB8C698F}.Debug|Win32.ActiveCfg = Debug|Win32 + {E35D73B9-68D4-4368-80F2-5F48BB8C698F}.Debug|Win32.Build.0 = Debug|Win32 + {E35D73B9-68D4-4368-80F2-5F48BB8C698F}.Release|Win32.ActiveCfg = Release|Win32 + {E35D73B9-68D4-4368-80F2-5F48BB8C698F}.Release|Win32.Build.0 = Release|Win32 + {B52DBAD2-47AC-4F7C-9814-94BD16B95201}.Debug|Win32.ActiveCfg = Debug|Win32 + {B52DBAD2-47AC-4F7C-9814-94BD16B95201}.Debug|Win32.Build.0 = Debug|Win32 + {B52DBAD2-47AC-4F7C-9814-94BD16B95201}.Release|Win32.ActiveCfg = Release|Win32 + {B52DBAD2-47AC-4F7C-9814-94BD16B95201}.Release|Win32.Build.0 = Release|Win32 + {2755A2CE-3BF3-46B6-A01E-D8FB5F67B17A}.Debug|Win32.ActiveCfg = Debug|Win32 + {2755A2CE-3BF3-46B6-A01E-D8FB5F67B17A}.Debug|Win32.Build.0 = Debug|Win32 + {2755A2CE-3BF3-46B6-A01E-D8FB5F67B17A}.Release|Win32.ActiveCfg = Release|Win32 + {2755A2CE-3BF3-46B6-A01E-D8FB5F67B17A}.Release|Win32.Build.0 = Release|Win32 + {6F8C5125-ABA9-4D4A-AEF5-AAF0F0CE67AD}.Debug|Win32.ActiveCfg = Debug|Win32 + {6F8C5125-ABA9-4D4A-AEF5-AAF0F0CE67AD}.Debug|Win32.Build.0 = Debug|Win32 + {6F8C5125-ABA9-4D4A-AEF5-AAF0F0CE67AD}.Release|Win32.ActiveCfg = Release|Win32 + {6F8C5125-ABA9-4D4A-AEF5-AAF0F0CE67AD}.Release|Win32.Build.0 = Release|Win32 + {A0D95EAB-2AAA-4D11-B6D0-733B8E85D61E}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0D95EAB-2AAA-4D11-B6D0-733B8E85D61E}.Debug|Win32.Build.0 = Debug|Win32 + {A0D95EAB-2AAA-4D11-B6D0-733B8E85D61E}.Release|Win32.ActiveCfg = Release|Win32 + {A0D95EAB-2AAA-4D11-B6D0-733B8E85D61E}.Release|Win32.Build.0 = Release|Win32 + {7D86068E-A828-4739-8FA9-6551EEACB816}.Debug|Win32.ActiveCfg = Debug|Win32 + {7D86068E-A828-4739-8FA9-6551EEACB816}.Debug|Win32.Build.0 = Debug|Win32 + {7D86068E-A828-4739-8FA9-6551EEACB816}.Release|Win32.ActiveCfg = Release|Win32 + {7D86068E-A828-4739-8FA9-6551EEACB816}.Release|Win32.Build.0 = Release|Win32 + {E3DDFA42-B171-48A7-9AF4-4A119BD567BC}.Debug|Win32.ActiveCfg = Debug|Win32 + {E3DDFA42-B171-48A7-9AF4-4A119BD567BC}.Debug|Win32.Build.0 = Debug|Win32 + {E3DDFA42-B171-48A7-9AF4-4A119BD567BC}.Release|Win32.ActiveCfg = Release|Win32 + {E3DDFA42-B171-48A7-9AF4-4A119BD567BC}.Release|Win32.Build.0 = Release|Win32 + {8E47638A-0FB0-4363-8DC0-8D12CD7C9609}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E47638A-0FB0-4363-8DC0-8D12CD7C9609}.Debug|Win32.Build.0 = Debug|Win32 + {8E47638A-0FB0-4363-8DC0-8D12CD7C9609}.Release|Win32.ActiveCfg = Release|Win32 + {8E47638A-0FB0-4363-8DC0-8D12CD7C9609}.Release|Win32.Build.0 = Release|Win32 + {2CD488EC-D91B-465C-8614-FFC84AF08150}.Debug|Win32.ActiveCfg = Debug|Win32 + {2CD488EC-D91B-465C-8614-FFC84AF08150}.Debug|Win32.Build.0 = Debug|Win32 + {2CD488EC-D91B-465C-8614-FFC84AF08150}.Release|Win32.ActiveCfg = Release|Win32 + {2CD488EC-D91B-465C-8614-FFC84AF08150}.Release|Win32.Build.0 = Release|Win32 + {8B3ACFD1-6DC7-40B6-9432-068804C6FB79}.Debug|Win32.ActiveCfg = Debug|Win32 + {8B3ACFD1-6DC7-40B6-9432-068804C6FB79}.Debug|Win32.Build.0 = Debug|Win32 + {8B3ACFD1-6DC7-40B6-9432-068804C6FB79}.Release|Win32.ActiveCfg = Release|Win32 + {8B3ACFD1-6DC7-40B6-9432-068804C6FB79}.Release|Win32.Build.0 = Release|Win32 + {F770A8A4-7A9C-4154-9198-8B6E1AB03BE0}.Debug|Win32.ActiveCfg = Debug|Win32 + {F770A8A4-7A9C-4154-9198-8B6E1AB03BE0}.Debug|Win32.Build.0 = Debug|Win32 + {F770A8A4-7A9C-4154-9198-8B6E1AB03BE0}.Release|Win32.ActiveCfg = Release|Win32 + {F770A8A4-7A9C-4154-9198-8B6E1AB03BE0}.Release|Win32.Build.0 = Release|Win32 + {8FD88770-B02A-4FF5-BC39-AF8931F8C77F}.Debug|Win32.ActiveCfg = Debug|Win32 + {8FD88770-B02A-4FF5-BC39-AF8931F8C77F}.Debug|Win32.Build.0 = Debug|Win32 + {8FD88770-B02A-4FF5-BC39-AF8931F8C77F}.Release|Win32.ActiveCfg = Release|Win32 + {8FD88770-B02A-4FF5-BC39-AF8931F8C77F}.Release|Win32.Build.0 = Release|Win32 + {437ECD7F-A465-4011-88FE-013EB3EBF87E}.Debug|Win32.ActiveCfg = Debug|Win32 + {437ECD7F-A465-4011-88FE-013EB3EBF87E}.Debug|Win32.Build.0 = Debug|Win32 + {437ECD7F-A465-4011-88FE-013EB3EBF87E}.Release|Win32.ActiveCfg = Release|Win32 + {437ECD7F-A465-4011-88FE-013EB3EBF87E}.Release|Win32.Build.0 = Release|Win32 + {14BD4CCC-DF5A-4DDA-8AE4-D10153143713}.Debug|Win32.ActiveCfg = Debug|Win32 + {14BD4CCC-DF5A-4DDA-8AE4-D10153143713}.Debug|Win32.Build.0 = Debug|Win32 + {14BD4CCC-DF5A-4DDA-8AE4-D10153143713}.Release|Win32.ActiveCfg = Release|Win32 + {14BD4CCC-DF5A-4DDA-8AE4-D10153143713}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Descent3/AIGoal.cpp b/Descent3/AIGoal.cpp new file mode 100644 index 000000000..d51fec63c --- /dev/null +++ b/Descent3/AIGoal.cpp @@ -0,0 +1,1741 @@ +/* +* $Logfile: /DescentIII/main/AIGoal.cpp $ +* $Revision: 137 $ +* $Date: 4/19/00 5:07p $ +* $Author: Matt $ +* +* AI Goal Functions +* +* $Log: /DescentIII/main/AIGoal.cpp $ + * + * 137 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 136 3/20/00 12:02p Matt + * Merge of Duane's post-1.3 changes. + * Added check for bad wb index + * + * 135 10/23/99 2:43a Chris + * Added the PutObjectOnObject AI Goal + * + * 134 10/21/99 2:17p Matt + * Mac merge + * + * 133 10/08/99 4:31p Nate + * Allow custom scripts to overide normal AI animation changes + * + * 132 7/27/99 4:16p Chris + * Now dodge goals can get killed + * + * 131 5/23/99 12:34a Chris + * Removed experimental code + * + * 130 5/22/99 3:36p Chris + * Further bulletproof the path stuff + * + * 129 5/22/99 3:12p Chris + * + * 128 5/22/99 3:11p Chris + * Fixed a bug in the wander code + * + * 127 5/21/99 7:46p Chris + * + * 126 5/21/99 7:22p Chris + * Fixed bugs in wander code + * + * 125 5/20/99 1:15a Chris + * Improved BNode Path Following for end points on the path. Fixed bugs + * with non-auto targetting + * + * 124 5/18/99 10:57a Chris + * Various bug fixes + * + * 123 5/17/99 6:06p Chris + * Adding robot debug code + * + * 122 5/09/99 8:11p Chris + * Made the whole see/hear distintion work (mostly) + * + * 121 5/01/99 2:21a Chris + * Use the GF_SPEED_XXX stuff + * + * 120 4/28/99 5:33a Chris + * Further improved the BNode system. (It actually works inside and + * outside now) + * + * 119 4/24/99 2:19a Chris + * Fixed Tubbs and paths + * + * 118 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 117 4/20/99 8:55p Chris + * Fixed problem with robots not being able to open locked doors that a + * player has the key for. + * + * 116 4/18/99 5:38a Chris + * Drastically improved the find_random_room function and added the + * makenextroomlist function + * + * 115 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 114 4/09/99 10:33a Chris + * Improvements to Freud + * + * 113 4/06/99 11:03p Chris + * Added goal unique id's. Improved FindRandomRoom (not so close to the + * edge of the terrain) + * + * 112 4/06/99 11:18a Chris + * Fixed a big bug in the attach system where object's properties where + * not correctly reset after the attach + * + * 111 4/05/99 5:38p Chris + * Fixed a bug where a wander_around goal could end up with a room 0 pos + * 0,0,0 path when first init'ed + * + * 110 4/05/99 3:18p Chris + * Patching wandering code + * + * 109 4/05/99 2:37p Chris + * Stalkers are now wandering robots + * + * 108 3/31/99 12:50p Chris + * Evader goal doesn't do avoid stuff for robots with a circle distance of + * zero + * + * 107 3/30/99 4:32p Chris + * Moved subtype to the main goal sturct (from goal_info). Made move + * relative object vec finishable. (Like get behind player) + * + * 106 3/27/99 3:00p Chris + * Fixed bug where high priority paths wouldn't reset the path if the + * previously active path was a static path + * + * 105 3/27/99 12:29p Chris + * Fixed problems with assigned lower priority goals that used paths (it + * would confuse the bot because the .path field was getting updated for + * the new goal even though it was lower priority) + * + * 104 3/23/99 11:51a Matt + * Made wander code not crash if the room selected was an outside room + * (from an inside wander) I.e. two rooms in two different mines with + * terrain between them. + * + * 103 3/22/99 5:47p Chris + * Improved AI difficulties when a goal is being deleted and, in script, a + * new goal uses that slot in middle of the delete code + * + * 102 3/22/99 10:58a Chris + * Awareness code was tweaked. Multisafe stuff added for objects. + * + * 101 3/17/99 5:23p Chris + * Fixed problems with low-priority paths + * + * 100 3/17/99 12:25p Chris + * Low priority paths + * + * 99 3/03/99 10:51a Chris + * Got rid of goal_ptr + * + * 98 2/15/99 9:03p Chris + * Added the base FOV off UVEC code and converted all the turrets + * + * 97 2/12/99 11:19a Chris + * If a path doesn't exist, look for it next frame + * + * 96 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 95 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 94 2/02/99 3:52p Luke + * Fixed a circular loop in the AINotify stuff + * + * 93 1/30/99 4:41p Luke + * Make the guard goal work + * + * 92 1/26/99 2:51p Chris + * AIG_WANDER improvements + * + * 91 1/25/99 8:16a Chris + * Made sure the DALLAS is informed about code forced cleared goals that + * have GUIDs. + * + * 90 1/25/99 7:43a Chris + * Added the GUID (Goal Unique Id) and added the ability for weapon + * batteries to always fire exactly forward. + * + * 89 1/24/99 8:17p Chris + * Updated AI and OSIRIS. Externalized fireball constants for spew and + * sparks. Added CreateRandomSparks to OSIRIS, renamed a bunch of AI + * Notify names to use underscores. Fixed a memory access leak in the + * matcen effect code. + * + * 88 1/22/99 6:53p Chris + * Fixed LoadandBind Aux notify problems, fixed static path problems, + * fixed AIF_FORCE_AWARE Problems, improved path code + * + * 87 1/20/99 1:01a Chris + * Improved AI and OSIRIS intergration + * + * 86 1/18/99 8:07p Chris + * Added the no-collide same flag (for flocks and nests) + * + * 85 1/18/99 9:05a Chris + * Improved OSIRIS, AI, and ATTACH system, changed wiggle code, changed + * attach code for rad attaches, and added the AIG_ATTACH_OBJ goal + * + * 84 1/15/99 5:54p Chris + * + * 83 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 82 12/16/98 3:55p Chris + * Improved the sickles + * + * 81 12/14/98 1:07p Chris + * Allowed OSIRIS to change AI types + * + * 80 12/13/98 9:18p Chris + * Improved influence values for in-code goals (10000 to 1.0). Added + * GF_ORIENT stuff. :) + * + * 79 12/08/98 4:26p Chris + * Fixed problems with same anim animations (both with .to of zero no + * longer will do it) + * + * 78 12/03/98 5:45p Chris + * I just added full code support for OSIRIS/DLL controlled goals, + * enablers, and influence levels. :) + * + * 77 12/03/98 12:24p Chris + * Improved new anim code for instant updating + * + * 76 12/03/98 12:04p Chris + * Added the instant mode switching for shared frame anims! + * + * 75 12/02/98 5:50p Chris + * GoalDetermineTrackDist is more complete + * + * 74 12/01/98 6:17p Chris + * Height bias is only for outside and GoalDetermineTrackDist is closer to + * final + * + * 73 12/01/98 5:30p Chris + * + * 72 12/01/98 5:25p Chris + * fixed switch statement for AIT_ init stuff + * + * 71 12/01/98 4:29p Chris + * Checked in a massive amount of AI work. Improved flocking code. :) + * (Still hacked lightly). In addition, I am moving toward using the + * composite dir. :) + * + * 70 11/19/98 9:24p Chris + * + * 69 11/19/98 8:25p Chris + * Starting to add generic team avoidance code + * + * 68 11/18/98 3:59p Chris + * I added the avoid goal. I also improved the AIs for Evader1 and + * Evader2 by utilizing this goal when the target is well within the + * circle distance. + * + * 67 11/11/98 6:31p Chris + * AIF_DISABLE_FIRING and AIF_DISABLE_MELEE are now functional + * + * 66 11/06/98 11:39a Chris + * Robots with flamethrowers and Omega cannons work in single player + * + * 65 10/22/98 7:55a Chris + * + * 64 10/22/98 7:07a Chris + * Improved Evader2 + * + * 63 10/21/98 7:33a Chris + * Dead objects are not-targetable + * + * 62 10/20/98 11:58p Chris + * AIGetToObj is not enabled when the target is NULL or OBJ_GHOST + * + * 61 10/13/98 1:08p Chris + * Greatly improved the AI's use of paths. Improved visability checking + * algorithm. Probably needs a second pass for further cleanup. + * + * 60 10/07/98 3:37p Chris + * Improved the melee attack code + * + * 59 9/28/98 4:09p Chris + * Added birth animations + * + * 58 9/17/98 11:11a Chris + * Worked on goal system and improved integration with OSIRIS (better + * EVT_AI_INIT event calling) + * + * 57 9/15/98 7:29p Chris + * Improved OSIRIS and AI intergration + * + * 56 9/01/98 3:50p Chris + * + * 55 8/17/98 4:55p Chris + * Added GF_FORCE_AWARENESS for goals + * + * 54 8/06/98 5:27p Chris + * Fixed a bug when AIs regenerate their paths (they where not storing the + * goal pos/room) with the AIGoalGetToPos goal. + * + * 53 6/29/98 5:34p Chris + * Do general goal stuff before the currently active goal + * + * 52 6/29/98 5:03p Chris + * + * 51 6/29/98 4:36p Chris + * Fixed a bug with melee robots + * + * 50 6/15/98 3:23p Chris + * Single point walker update + * + * 49 5/22/98 6:45p Chris + * Fixed a bug with flinching and melee attacks + * + * 48 5/22/98 6:22p Chris + * Improved Dynamic path allocation + * + * 47 5/22/98 4:44p Chris + * Added better melee hit prediction + * + * 46 5/20/98 10:19a Chris + * Fixed some bugs with status_reg's and circle distance + * + * 45 5/18/98 12:40p Chris + * Added a new flag (F_BLINE_IF_SEE_GOAL) + * + * 44 5/17/98 9:07p Chris + * Fixed melee attacks. Thanks to Ala. :) + * + * 43 5/17/98 7:54p Chris + * Correctly integrated the goal system and circle distance stuff. Added + * support for "target goals". + * + * 42 5/15/98 2:58p Chris + * More on the big AI update + * + * 41 5/14/98 3:01p Chris + * More new AI code + * + * 40 5/14/98 12:21p Chris + * Updating AI system... Incomplete + * + * 39 5/12/98 3:46p Chris + * Added some notify/goal system stuff and orientation to velocity + * + * 38 5/11/98 4:39p Chris + * Improved AI system (goals have notifies and are more flexable). + * + * 37 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 36 5/01/98 3:41p Chris + * + * 35 5/31/98 3:05p Chris + * Allowed death anims (fixed problem with ctype union of CT_DEBRIS and + * CT_AI) + * + * 34 4/30/98 11:31a Chris + * Massive upgrades to the AI system + * + * 33 4/22/98 1:15p Chris + * Incremental work on paths and goals + * + * 32 4/21/98 11:25a Chris + * Improving GET_TO_OBJ goal + * + * 31 4/20/98 3:19p Chris + * Added more goal/path support + * + * 30 4/20/98 12:27p Chris + * Made AIG_GET_TO_OBJECT work. + * + * 29 4/19/98 9:56p Chris + * AI system is integrated with OSIRIS. Path system is integrated with + * the AI system. Bugs will ensue. + * + * 28 4/14/98 11:42a Chris + * Added quirks and a better way of getting from idle to alert and from + * alert to idle. + * + * 27 4/13/98 6:54p Chris + * Improved the animation system. + * + * 26 4/13/98 2:20p Chris + * IDLe works (kindof) + * + * 25 3/30/98 10:50a Chris + * AI's will not go into fire if a flinch is lined up. + * + * 24 3/25/98 5:51p Chris + * Added full model/body animations for weapon firing + * + * 23 3/25/98 11:02a Chris + * version 1.0 of the new AI ranged firing code. + * + * 22 3/13/98 5:55p Chris + * Fixed problems with animations + * + * 21 3/12/98 5:55p Chris + * Updated flinch + * + * 20 3/12/98 5:51p Chris + * Working on Bashing anim's to other anims + * + * 19 3/03/98 5:02p Chris + * Added a status register to the ai_frame. Also, I enabled a bunch of + * the fields from the AI Dialog. In addiition, I tweaked how melee + * attacks work and animate. + * + * 18 2/16/98 12:52p Chris + * Added some Flinch support + * + * 17 2/11/98 7:00p Chris + * Removed priority (changed to influence) and added activation_level + * + * 16 2/02/98 8:14p Chris + * Updated the AI system + * + * 15 1/19/98 10:04a Matt + * Added new object handle system + * + * 14 1/14/98 7:57p Chris + * Improving the awareness system + * + * 13 11/17/97 9:48p Chris + * Added guarding + * + * 12 11/17/97 5:46p Chris + * Added some support for a Theif-like robot + * + * 11 11/14/97 11:55p Chris + * Dodge goals are semi-functional + * + * 10 9/02/97 3:28p Matt + * Got rid of warnings + * + * 9 8/05/97 12:29p Chris + * + * 8 7/30/97 1:31p Chris + * Made helicopters slightly more interesting. + * + * 7 7/29/97 12:20p Chris + * Incremental improvements. Fixed a memory bug. + * + * 6 7/15/97 7:29p Chris + * Added a sound for helicopter blades. + * + * 5 7/15/97 5:35p Chris + * New AI code + * + * 4 7/15/97 4:59p Chris + * added support for AI and animations +* +* $NoKeywords: $ +*/ + +#include +#include "AIGoal.h" +#include "aistruct.h" +#include "aipath.h" +#include "object.h" +#include "game.h" +#include "terrain.h" +#include "objinfo.h" +#include "AIMain.h" +#include "room.h" +#include "psrand.h" +#include "BOA.h" +#include "findintersection.h" + +extern int AI_unique_goal_id; + +#define BASH_TO_ANIM_SCALER 10.0f +#define MAX_BASH_TO_FLINCH_TIME 2.5f + +void GoalInitWanderAround(object *obj, goal *goal_ptr) +{ + ai_frame *ai_info = obj->ai_info; + int roomnum; + vector pos; + +// mprintf((0, "Wander around\n")); + + goal *cur_goal = GoalGetCurrentGoal(obj); + if(cur_goal != goal_ptr) + return; + + if(!ROOMNUM_OUTSIDE(obj->roomnum)) + { + roomnum = AIFindRandomRoom(obj, ai_info, goal_ptr, obj->roomnum, -1, -1, true, false, NULL); + if(!ROOMNUM_OUTSIDE(roomnum)) + { + pos = Rooms[roomnum].path_pnt; + } + else + { + ComputeTerrainSegmentCenter(&pos, CELLNUM(roomnum)); + pos.y += (ai_info->biased_flight_max + ai_info->biased_flight_min)/2.0f; + } + } + else + { + int tx = (ps_rand()%(TERRAIN_WIDTH-56)) + (56/2); + int tz = (ps_rand()%(TERRAIN_DEPTH-56)) + (56/2); + + int cell = tx + TERRAIN_WIDTH * tz; + roomnum = 0x80000000 | cell; + ComputeTerrainSegmentCenter (&pos, cell); + pos.y += (ai_info->biased_flight_max + ai_info->biased_flight_min)/2.0f; + } + + goal_ptr->g_info.pos = pos; + goal_ptr->g_info.roomnum = roomnum; + + goal_ptr->next_path_time = Gametime + MIN_NEXT_PATH_INTERVAL + ps_rand()/(float)RAND_MAX; + goal_ptr->flags |= GF_HAS_PATH; +} + +void GoalClearGoal(object *obj, goal *cur_goal, int reason) +{ + ai_frame *ai_info = obj->ai_info; + bool f_continue = true; + + // Set the in clear flag so that we know if this goal is updated in script + cur_goal->flags |= GF_IN_CLEAR; + + if(reason != AI_INVALID_INDEX) + { + int goal_index = (cur_goal - ai_info->goals); + + f_continue = AINotify(obj, reason, (void *) &goal_index); + } + else if(cur_goal->guid != -1 && reason != AI_INVALID_INDEX) + { + int goal_index = (cur_goal - ai_info->goals); + AINotify(obj, AIN_GOAL_FAIL, (void *) &goal_index); + } + + // Don't continue if this is a different goal + if(!(cur_goal->flags & GF_IN_CLEAR)) + return; + + // Clear the in clear flag + cur_goal->flags &= ~GF_IN_CLEAR; + + if((reason == AIN_GOAL_COMPLETE) && (cur_goal->flags & GF_KEEP_AT_COMPLETION)) + { + if(cur_goal->type == AIG_WANDER_AROUND) + { + GoalInitWanderAround(obj, cur_goal); + } + return; + } + + if((cur_goal->flags & GF_NONFLUSHABLE) && (reason != AI_INVALID_INDEX)) + return; + + if(!f_continue) + return; + +// cur_goal->goal_type = AIG_NOT_USED; + cur_goal->used = false; + + + if(obj->ai_info->path.goal_uid == cur_goal->goal_uid) + { + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + AIPathFreePath(&ai_info->path); + } + +// for(i = 0; i < MAX_ENDERS_PER_GOAL; i++) +// { +// cur_goal->enders[i].ender_type = AIE_NOT_USED; +// } +} + +#define MAX_NEAR_OBJ_SEARCH_DIST 200.0f + +float GoalDetermineTrackDist(object *obj) +{ + float dist = 0.0f; + int i; + + for(i = 0; i < MAX_GOALS; i++) + { + goal *cur_goal = &obj->ai_info->goals[i]; + + if(cur_goal->used && ISTRACKGOAL(cur_goal)) + { + if(cur_goal->flags & GF_RAMPED_INFLUENCE) + { + if(cur_goal->ramp_influence_dists[3] > dist) + { + // Get the maximum distance this goal has influence + dist = cur_goal->ramp_influence_dists[3]; + } + } + else + { + // Influence isn't distnace based; so, check out to max dist + dist = MAX_NEAR_OBJ_SEARCH_DIST; + } + } + } + + if(dist > 0.0f) + return dist + obj->size; + else + return 0.0f; +} + +void GoalDoFrame(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + goal *cur_goal; + int i; + + goal *cur_task_goal = GoalGetCurrentGoal(obj); + if(ai_info->path.num_paths > 0 && (cur_task_goal == NULL || (cur_task_goal->goal_uid != ai_info->path.goal_uid))) + { + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + AIPathFreePath(&ai_info->path); + } + + // Process all goals + cur_goal = &ai_info->goals[0]; + for(i = 0; i < MAX_GOALS; i++) + { + if(cur_goal->used) + { + if((cur_goal->flags & GF_CLEAR_IF_NOT_CURRENT_GOAL) && cur_goal != cur_task_goal) + { + GoalClearGoal(obj, cur_goal); + continue; + } + + // Does the + if(cur_goal->flags & GF_OBJ_IS_TARGET) + { + cur_goal->g_info.handle = ai_info->target_handle; + + if(ai_info->status_reg & AISR_SEES_GOAL) + { + cur_goal->status_reg |= AISR_SEES_GOAL; + } + else + { + cur_goal->status_reg &= ~AISR_SEES_GOAL; + } + } + else + { + // chrishack -- Currently, we can only see target based goals and position goals + cur_goal->status_reg &= ~AISR_SEES_GOAL; + if(POSGOAL(cur_goal) && cur_goal->g_info.roomnum == obj->roomnum) + { + cur_goal->status_reg |= AISR_SEES_GOAL; + } + } + + if(cur_goal->flags & GF_CIRCLE_OBJ) + { + int handle = cur_goal->g_info.handle; + object *goal_obj = ObjGet(handle); + + if(cur_goal->flags & GF_OBJ_IS_TARGET) + { + cur_goal->dist_to_goal = ai_info->dist_to_target_actual; + } + else + { + if(goal_obj) + { + cur_goal->dist_to_goal = vm_VectorDistance(&obj->pos, &goal_obj->pos) - obj->size - goal_obj->size; + if(cur_goal->dist_to_goal < 0.0f) + { + cur_goal->dist_to_goal = 0.0f; + } + } + } + + if(goal_obj) + { + AIStatusCircleFrame(obj, goal_obj, cur_goal->dist_to_goal, cur_goal->circle_distance, &cur_goal->status_reg); + } + else + { + cur_goal->status_reg &= ~AISR_CIRCLE_DIST; + } + } + else if(POSGOAL(cur_goal)) + { + float dist = vm_VectorDistance(&obj->pos, &cur_goal->g_info.pos); + +// mprintf((0, "Dist is %f\n", dist)); +// mprintf((0, "Dist %f, %f, %f\n", XYZ(&cur_goal->g_info.pos))); +// mprintf((0, "Obj %f, %f, %f\n", XYZ(&obj->pos))); + + cur_goal->dist_to_goal = dist; + AIStatusCircleFrame(obj, NULL, dist, cur_goal->circle_distance, &cur_goal->status_reg); + } + } + + cur_goal++; + } + + // cur_task_goal might get cleared out above, so we should just recompute the current task goal + cur_goal = GoalGetCurrentGoal(obj); + + if(cur_goal) + { + int handle = OBJECT_HANDLE_NONE; + object *goal_obj = NULL; + + if(cur_goal->flags & GF_FORCE_AWARENESS) + { + if(obj->ai_info->awareness < AWARE_MOSTLY) + obj->ai_info->awareness = AWARE_MOSTLY; + } + + // Does the save and restore of path information for static paths (i.e. if the path_info is + // taken away by a higher priority goal and then returned at a later time + if(cur_goal->type == AIG_FOLLOW_PATH) + { + if(ai_info->path.goal_uid == cur_goal->goal_uid && ai_info->path.num_paths > 0) + { + cur_goal->g_info.static_path_info.cur_node = ai_info->path.cur_node; + cur_goal->g_info.static_path_info.start_node = ai_info->path.path_start_node[0]; + cur_goal->g_info.static_path_info.end_node = ai_info->path.path_end_node[0]; + } + else + { + AIPathSetAsStaticPath(obj, cur_goal, cur_goal->g_info.id, cur_goal->g_info.static_path_info.start_node, cur_goal->g_info.static_path_info.end_node, cur_goal->g_info.static_path_info.cur_node); + } + } + + if(cur_goal->type == AIG_WANDER_AROUND) + { + if(!(cur_goal->flags & GF_HAS_PATH) || ai_info->path.num_paths == 0) + { + GoalInitWanderAround(obj, cur_goal); + } +/* else if((cur_goal->flags & GF_HAS_PATH) && ai_info->path.num_paths != 0) + { + vector *posp = &cur_goal->g_info.pos; + float dist = vm_VectorDistance(&AIDynamicPath[ai_info->path.num_paths - 1].pos[ai_info->path.path_end_node[ai_info->path.num_paths - 1]], posp); + + if(dist > 5.0f) + { + mprintf((0, "In wander path case for obj %d - %s....\n", OBJNUM(obj), Object_info[obj->id].name)); + cur_goal->next_path_time = Gametime - 1.0f; + } + }*/ + } + + if(OBJGOAL(cur_goal)) + { + handle = cur_goal->g_info.handle; + goal_obj = ObjGet(handle); + } + + // Automatically end + if((COMPLETEATOBJ(cur_goal)) && + (goal_obj) && + (cur_goal->status_reg & AISR_CIRCLE_DIST)) + { + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } + else if((COMPLETEATPOS(cur_goal)) && (cur_goal->status_reg & AISR_CIRCLE_DIST)) + { + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } + else if((Gametime > cur_goal->next_path_time || ai_info->path.num_paths == 0) && (cur_goal->type != AIG_FOLLOW_PATH)) + { + bool f_make_path = false; + int ignore_obj = -1; + bool f_kill_goal = false; + int kill_reason; + + vector *posp; + int roomnum; + + if(OBJGOAL(cur_goal)) + { + if(goal_obj) + { +// if(cur_goal->g_info.pos != goal_obj->pos) + f_make_path = true; + ignore_obj = OBJNUM(goal_obj); + + posp = &goal_obj->pos; + roomnum = goal_obj->roomnum; + + float dist = vm_VectorDistance(&AIDynamicPath[ai_info->path.num_paths - 1].pos[ai_info->path.path_end_node[ai_info->path.num_paths - 1]], posp); + + if(dist < 5.0f && ai_info->path.num_paths != 0) + { + int obj_room = BOA_INDEX(obj->roomnum); + int path_room = BOA_INDEX(AIDynamicPath[ai_info->path.cur_path].roomnum[ai_info->path.cur_node]); + vector path_pos = AIDynamicPath[ai_info->path.cur_path].pos[ai_info->path.cur_node]; + + if(obj_room != path_room) + { + fvi_query fq; + fvi_info hit_info; + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &path_pos; + fq.rad = obj->size / 4.0f; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_NO_RELINK | FQ_IGNORE_NON_LIGHTMAP_OBJECTS; + + if(fvi_FindIntersection(&fq, &hit_info) == HIT_NONE) + { + mprintf((0, "AI OBJ Path: No need to update the path for obj %d\n", OBJNUM(obj))); + f_make_path = false; + } + } + } + } + else + { + kill_reason = AIN_GOAL_INVALID; + f_kill_goal = true; + } + } + else if (POSGOAL(cur_goal)) + { + f_make_path = true; + + posp = &cur_goal->g_info.pos; + roomnum = cur_goal->g_info.roomnum; + + float dist = vm_VectorDistance(&AIDynamicPath[ai_info->path.num_paths - 1].pos[ai_info->path.path_end_node[ai_info->path.num_paths - 1]], posp); + + if(dist < 5.0f && ai_info->path.num_paths != 0) + { + int obj_room = BOA_INDEX(obj->roomnum); + int path_room = BOA_INDEX(AIDynamicPath[ai_info->path.cur_path].roomnum[ai_info->path.cur_node]); + vector path_pos = AIDynamicPath[ai_info->path.cur_path].pos[ai_info->path.cur_node]; + + if(obj_room != path_room) + { + fvi_query fq; + fvi_info hit_info; + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &path_pos; + fq.rad = obj->size / 4.0f; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_NO_RELINK | FQ_IGNORE_NON_LIGHTMAP_OBJECTS; + + if(fvi_FindIntersection(&fq, &hit_info) == HIT_NONE) + { + mprintf((0, "AI POS Path: No need to update the path for obj %d\n", OBJNUM(obj))); + f_make_path = false; + } + } + } + } + + // If we are supposed to make it, do it. + if(f_make_path) + { + AIPathAllocPath(obj, ai_info, cur_goal, &obj->roomnum, &obj->pos, &roomnum, posp, 0.0f, 0, obj->handle, ignore_obj); + } + + // Kill it or update the time + if(f_kill_goal) + { + GoalClearGoal(obj, cur_goal, kill_reason); + } + else + { + // Update time regardless of if we made the path (so dont don't do this every frame + cur_goal->next_path_time = Gametime + MIN_NEXT_PATH_INTERVAL + ps_rand()/(float)RAND_MAX; + } + } + } +} + +void GoalPathComplete(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + goal *cur_goal = GoalGetCurrentGoal(obj); + + if(!cur_goal) + return; + + if(COMPLETEATOBJ(cur_goal)) + { + int handle = cur_goal->g_info.handle; + object *goal_obj = ObjGet(handle); + + if((goal_obj) && (cur_goal->g_info.pos != goal_obj->pos)) + { + int ignore_obj = OBJNUM(goal_obj); + + if(AIPathAllocPath(obj, ai_info, cur_goal, &obj->roomnum, &obj->pos, &goal_obj->roomnum, &goal_obj->pos, 0.0f, 0, obj->handle, ignore_obj)) + { + cur_goal->next_path_time = Gametime + MIN_NEXT_PATH_INTERVAL + ps_rand()/(float)RAND_MAX; + } + } + else + { + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } + } + else + { + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } +} + +void GoalClearAll(object *obj) +{ + int i; + ai_frame *ai_info = obj->ai_info; + + for(i = 0; i < MAX_GOALS; i++) + { + GoalClearGoal(obj, &ai_info->goals[i]); + } +} + +int GoalAllocSlot(object *obj, int level, float influence) +{ + float lowest_influence_slot = -1.0; + float lowest_influence = MAX_INFLUENCE + 1.0; + int alloc_slot = AI_INVALID_INDEX; + ai_frame *ai_info = obj->ai_info; + int cur_slot; + + ASSERT((level >= 0 && level < NUM_ACTIVATION_LEVELS) || + (level == ACTIVATION_BLEND_LEVEL)); + + if(influence > MAX_INFLUENCE) + { + mprintf((0, "Goal added with too much influence -- bashing down\n")); + influence = MAX_INFLUENCE; + } + else if(influence < 0.0f) + { + mprintf((0, "Goal added with negative influence -- bashing to zero\n")); + influence = 0.0f; + } + + if(level < NUM_ACTIVATION_LEVELS) + { + cur_slot = level; + + if(level < 0) + { + mprintf((0, "AI: Bashed an invalid activation level to zero\n")); + level = 0; + } + + if(ai_info->goals[cur_slot].used) + { + if(!(ai_info->goals[cur_slot].flags & GF_NONFLUSHABLE)) + GoalClearGoal(obj, &ai_info->goals[cur_slot]); + else + cur_slot = AI_INVALID_INDEX; + } + } + else + { + cur_slot = NUM_ACTIVATION_LEVELS; + goal *cur_goal = &ai_info->goals[cur_slot]; + + if(level != ACTIVATION_BLEND_LEVEL) + { + mprintf((0, "AI: Bashed an invalid activation blend level to blend level\n")); + level = ACTIVATION_BLEND_LEVEL; + } + + while(cur_slot < MAX_GOALS && ai_info->goals[cur_slot].used == true) + { + + if(!(cur_goal->used)) + { + lowest_influence_slot = cur_slot; + lowest_influence = -1.0f; + } + + if(cur_goal->influence < lowest_influence && !(cur_goal->flags & GF_NONFLUSHABLE)) + { + lowest_influence_slot = cur_slot; + lowest_influence = cur_goal->influence; + } + + cur_goal++; + cur_slot++; + } + + // If there are no free slots but the lowest influence goal has less than the new goal's + // influence, kill the old goal + if(cur_slot >= MAX_GOALS && lowest_influence < influence) + { + cur_slot = lowest_influence_slot; + GoalClearGoal(obj, &ai_info->goals[cur_slot]); + } + } + + if(cur_slot < MAX_GOALS) + { + ai_info->goals[cur_slot].used = true; + } + else + { + cur_slot = AI_INVALID_INDEX; + } + + return cur_slot; +} + +// Adds a goal to the object +int GoalAddGoal(object *obj, unsigned int goal_type, void *arg_struct, int level, float influence, int f_goal, int guid, char subtype) +{ + ai_frame *ai_info = obj->ai_info; + int goal_index = AI_INVALID_INDEX; + goal *goal_ptr = NULL; + + if(!(goal_type & (AIG_SET_ANIM | AIG_DO_MELEE_ANIM | AIG_FIRE_AT_OBJ))) + { + goal_index = GoalAllocSlot(obj, level, influence); + if(goal_index == AI_INVALID_INDEX) + { + //mprintf((0, "WARNING: Object %d has too many goals\n", OBJNUM(obj))); + return AI_INVALID_INDEX; + } + + goal_ptr = &ai_info->goals[goal_index]; + + goal_ptr->type = goal_type; + goal_ptr->influence = influence; + goal_ptr->start_time = Gametime; + goal_ptr->flags = f_goal; + goal_ptr->status_reg = 0; + goal_ptr->num_enablers = 0; + goal_ptr->circle_distance = ai_info->circle_distance; + goal_ptr->guid = guid; + goal_ptr->creation_time = Gametime; + goal_ptr->subtype = subtype; + goal_ptr->goal_uid = AI_unique_goal_id++; + } + + switch(goal_type) + { + case AIG_WANDER_AROUND: + { + goal_ptr->next_path_time = Gametime; + } + break; + + case AIG_SCRIPTED: + goal_ptr->g_info.scripted_data_ptr = arg_struct; + break; + + case AIG_DODGE_OBJ: + case AIG_GET_AROUND_OBJ: + case AIG_MOVE_RELATIVE_OBJ: + goal_ptr->g_info.handle = *((int *) arg_struct); + break; + + case AIG_MOVE_RELATIVE_OBJ_VEC: + goal_ptr->g_info.handle = ((goal_info *) arg_struct)->handle; + break; + + case AIG_GUARD_AREA: + goal_ptr->g_info.roomnum = ((goal_info *) arg_struct)->roomnum; + goal_ptr->g_info.pos = ((goal_info *) arg_struct)->pos; + break; + + case AIG_PLACE_OBJ_ON_OBJ: + { + int handle = ((goal_info *)arg_struct)->handle; + object *goal_obj = ObjGet(handle); + + goal_ptr->g_info.attach_info.flags = 0; + + if(goal_obj) + { + goal_ptr->g_info = *((goal_info *)arg_struct); + goal_ptr->g_info.pos = goal_obj->pos; + int ignore_obj = OBJNUM(goal_obj); + + if(AIPathAllocPath(obj, ai_info, &ai_info->goals[goal_index], &obj->roomnum, &obj->pos, &goal_obj->roomnum, &goal_obj->pos, 0.0f, 0, obj->handle, ignore_obj)) + { + goal_ptr->next_path_time = Gametime + MIN_NEXT_PATH_INTERVAL + ps_rand()/(float)RAND_MAX; + } + else + { + goal_ptr->next_path_time = Gametime; + } + } + else + goal_ptr->g_info.handle = OBJECT_HANDLE_NONE; + } + break; + + case AIG_ATTACH_TO_OBJ: + { + int handle = ((goal_info *)arg_struct)->handle; + object *goal_obj = ObjGet(handle); + + goal_ptr->g_info.attach_info.flags = 0; + + if(goal_obj) + { + goal_ptr->g_info = *((goal_info *)arg_struct); + goal_ptr->g_info.pos = goal_obj->pos; + int ignore_obj = OBJNUM(goal_obj); + + if(AIPathAllocPath(obj, ai_info, &ai_info->goals[goal_index], &obj->roomnum, &obj->pos, &goal_obj->roomnum, &goal_obj->pos, 0.0f, 0, obj->handle, ignore_obj)) + { + goal_ptr->next_path_time = Gametime + MIN_NEXT_PATH_INTERVAL + ps_rand()/(float)RAND_MAX; + } + else + { + goal_ptr->next_path_time = Gametime; + } + } + else + goal_ptr->g_info.handle = OBJECT_HANDLE_NONE; + } + break; + + case AIG_GET_TO_OBJ: + { + int handle = *((int *) arg_struct); + object *goal_obj = ObjGet(handle); + + if(goal_obj) + { + goal_ptr->g_info.handle = handle; + goal_ptr->g_info.pos = goal_obj->pos; + } + else + goal_ptr->g_info.handle = OBJECT_HANDLE_NONE; + + goal_ptr->next_path_time = Gametime; + } + break; + + case AIG_GET_TO_POS: + { + int roomnum = ((goal_info *) arg_struct)->roomnum; + vector pos = ((goal_info *) arg_struct)->pos; + + goal_ptr->g_info.pos = pos; + goal_ptr->g_info.roomnum = roomnum; + + goal_ptr->next_path_time = Gametime; + } + break; + + case AIG_FOLLOW_PATH: + { + path_information path_info = *((path_information *) arg_struct); + + goal_ptr->g_info.id = path_info.path_id; + goal_ptr->g_info.static_path_info.cur_node = path_info.next_node; + goal_ptr->g_info.static_path_info.start_node = path_info.start_node; + goal_ptr->g_info.static_path_info.end_node = path_info.end_node; + + goal_ptr->next_path_time = Gametime; + } + break; + + case AIG_FIRE_AT_OBJ: + { + gi_fire *attack_info = (gi_fire *) arg_struct; + if(attack_info->cur_wb > MAX_WBS_PER_OBJ) { //DAJ + mprintf((2, "GoalAddGoal wb_index %d > MAX_WBS_PER_OBJ\n", attack_info->cur_wb)); + return 0; + } + if((ai_info->animation_type == AS_ALERT && !(ai_info->next_animation_type == AS_FLINCH)) || ai_info->animation_type == AS_RANGED_RECOIL) + { + ai_info->current_wb_firing = attack_info->cur_wb; + obj->dynamic_wb[attack_info->cur_wb].wb_anim_mask = attack_info->cur_mask; + + obj->rtype.pobj_info.anim_flags |= AIAF_NOTIFY; + ai_info->next_animation_type = AS_RANGED_ATTACK; + + if(Object_info[obj->id].static_wb[attack_info->cur_wb].flags & (WBF_SPRAY | WBF_ON_OFF)) + { + ai_info->last_special_wb_firing = (char) attack_info->cur_wb; + } + + return 0; + } + else if(ai_info->animation_type == AS_IDLE) + { + obj->rtype.pobj_info.anim_flags |= AIAF_NOTIFY; + ai_info->next_animation_type = AS_GOTO_ALERT_OFFSET + ai_info->movement_type; + } + } + break; + + case AIG_DO_MELEE_ANIM: + { + gi_fire *attack_info = (gi_fire *) arg_struct; + if((ai_info->animation_type == AS_ALERT) || + (ai_info->animation_type == AS_MELEE1 + 1) || + (ai_info->animation_type == AS_MELEE2 + 1)) + { + ubyte m_num = attack_info->melee_number; + char new_anim; + + if(m_num) + new_anim = AS_MELEE1; + else + new_anim = AS_MELEE2; + + obj->rtype.pobj_info.anim_flags |= AIAF_NOTIFY; + ai_info->next_animation_type = new_anim; + return 0; + } + else if(ai_info->animation_type == AS_IDLE) + { + obj->rtype.pobj_info.anim_flags |= AIAF_NOTIFY; + ai_info->next_animation_type = AS_GOTO_ALERT_OFFSET + ai_info->movement_type; + } + } + break; + + case AIG_MELEE_TARGET: + break; + + case AIG_SET_ANIM: + { + char new_anim = *((int *) arg_struct); + polyobj_info *p_info = &obj->rtype.pobj_info; + +// mprintf((0, "Anim Goal %d\n", new_anim)); + // Custom animations cannot be overriden + if(ai_info->next_animation_type == AS_CUSTOM) + { + return -1; + } + + if(new_anim >= 0 && new_anim < NUM_ANIMS_PER_CLASS) + { + if((Object_info[obj->id].anim[ai_info->movement_type].elem[ai_info->animation_type].from == + Object_info[obj->id].anim[ai_info->movement_type].elem[new_anim].from) && + (Object_info[obj->id].anim[ai_info->movement_type].elem[ai_info->animation_type].to == + Object_info[obj->id].anim[ai_info->movement_type].elem[new_anim].to) && + (Object_info[obj->id].anim[ai_info->movement_type].elem[ai_info->animation_type].to) && + (Object_info[obj->id].anim[ai_info->movement_type].elem[new_anim].to)) + { + ai_info->next_animation_type = new_anim; + AIUpdateAnim(obj); + return 0; + } + } + + if(ai_info->animation_type == AS_ALERT) + { + if(new_anim == AS_FLINCH) // Get to flinch quickly + { + if(p_info->anim_end_frame != p_info->anim_start_frame) + { + float time_per_frame = p_info->anim_time / (p_info->anim_end_frame - p_info->anim_start_frame); + float time_left = time_per_frame * (p_info->anim_end_frame - p_info->anim_frame); + + if(time_left > MAX_BASH_TO_FLINCH_TIME) + { + return -1; + } + } + p_info->anim_time /= BASH_TO_ANIM_SCALER; + } + else if (new_anim == AS_TAUNT) + { + if(ai_info->next_animation_type != AI_INVALID_INDEX) + { + return -1; + } + } + else if (new_anim == AS_IDLE) + { + new_anim = AS_GOTO_IDLE_OFFSET + ai_info->movement_type; + + if(Object_info[obj->id].anim[ai_info->movement_type].elem[new_anim].to == 0.0f) + { + return -1; + } + } + + obj->rtype.pobj_info.anim_flags |= AIAF_NOTIFY; + ai_info->next_animation_type = new_anim; + return 0; + } + else if(ai_info->animation_type == AS_IDLE) + { + if((new_anim == AS_ALERT) || + (new_anim == AS_QUIRK && Object_info[obj->id].anim[ai_info->movement_type].elem[new_anim].to != 0.0f)) + { + obj->rtype.pobj_info.anim_flags |= AIAF_NOTIFY; + + if(new_anim == AS_ALERT) + ai_info->next_animation_type = AS_GOTO_ALERT_OFFSET + ai_info->movement_type; + else + ai_info->next_animation_type = new_anim; + } + return 0; + } + else if(ai_info->animation_type == AS_QUIRK || ai_info->animation_type == AS_BIRTH) + { + if(new_anim == AS_ALERT) + { + ai_info->next_animation_type = AS_GOTO_ALERT_OFFSET + ai_info->movement_type; + return 0; + } + + return -1; + } + } + break; + + default: + ASSERT(0); + break; + } + + // Do init. stuff + if(goal_index != AI_INVALID_INDEX) + { + if(OBJGOAL(&ai_info->goals[goal_index])) + { + goal_ptr->flags |= GF_CIRCLE_OBJ; + } + else if(POSGOAL(&ai_info->goals[goal_index])) + { + goal_ptr->flags |= GF_CIRCLE_POS; + } + + if(TARGETONLYGOAL(&ai_info->goals[goal_index])) + { + goal_ptr->flags |= GF_OBJ_IS_TARGET; + } + + if(goal_ptr->flags & GF_OBJ_IS_TARGET) + { + goal_ptr->g_info.handle = ai_info->target_handle; + if(ai_info->status_reg & AISR_CIRCLE_DIST) + { + goal_ptr->status_reg |= AISR_CIRCLE_DIST; + } + else + { + goal_ptr->status_reg &= ~AISR_CIRCLE_DIST; + } + + goal_ptr->dist_to_goal = ai_info->dist_to_target_actual; + } + + goal *cur_goal = GoalGetCurrentGoal(obj); + if(cur_goal != NULL && cur_goal == goal_ptr) + { + if(cur_goal->flags & GF_FORCE_AWARENESS) + { + if(obj->ai_info->awareness < AWARE_MOSTLY) + obj->ai_info->awareness = AWARE_MOSTLY; + } + } + } + + return goal_index; +} + +int GoalAddEnabler(object *obj, int goal_index, ubyte enabler_type, void *arg_struct, float percent, float interval) +{ + ai_frame *ai_info = obj->ai_info; + goal_enabler *e_info; + + int enabler_index = ai_info->goals[goal_index].num_enablers; + + if(ai_info->goals[goal_index].num_enablers >= MAX_ENABLERS_PER_GOAL) + { + mprintf((0, "Object %d with goal %d has to many enablers\n", OBJNUM(obj), goal_index)); + return AI_INVALID_INDEX; + } + + e_info = &ai_info->goals[goal_index].enabler[enabler_index]; + + e_info->enabler_type = enabler_type; + e_info->percent_enable = percent; + e_info->check_interval = interval; + e_info->last_check_time = Gametime; + + // Hmmm... What about 2 seconds to be enabled???? I think it is possible --chrishack + switch(enabler_type) + { + case(AIE_AI_STATUS_FLAG): + e_info->flags = *((int *) arg_struct); + break; + case(AIE_NEAR): + case(AIE_FAR): + e_info->dist = *((float *) arg_struct); + break; + case(AIE_SCRIPTED): + break; + case(AIE_GT_AWARENESS): + case(AIE_LTE_AWARENESS): + e_info->awareness = *((float *) arg_struct); + break; + case(AIE_GT_LAST_SEE_TARGET_INTERVAL): + case(AIE_LTE_LAST_SEE_TARGET_INTERVAL): + e_info->time = *((float *) arg_struct); + break; + case(AIE_FEAR): + e_info->float_value = *((float *) arg_struct); + break; + case(AIE_ANGRY): + e_info->float_value = *((float *) arg_struct); + break; + case(AIE_CURIOUS): + e_info->float_value = *((float *) arg_struct); + break; + case(AIE_FRUSTRATED): + e_info->float_value = *((float *) arg_struct); + break; + case(AIE_DELAY_TIME): + e_info->time = *((float *) arg_struct); + break; + case(AIE_CLEAR_TIME): + e_info->time = *((float *) arg_struct); + break; + default: + ASSERT(0); + break; + } + + ai_info->goals[goal_index].num_enablers++; + + return enabler_index; +} + +void GoalInitTypeGoals(object *obj, int ai_type) +{ + ai_frame *ai_info = obj->ai_info; + float aware = AWARE_BARELY; + float time = 25.0f; + + GoalClearAll(obj); + + // This goal has a proirity of 50. Mabye I should split up intermediate goals + // and long term goals. -- Wander around within 2000 units of your starting + // position (goal has priority of 100) The AI type decides what to do with + // a goal when it is done. + switch(ai_type) + { + case AIT_STALKER: + { + int goal_room; + + goal_room = obj->roomnum; + + GoalAddGoal(obj, AIG_WANDER_AROUND, (void *)&goal_room, 1, 1.0f, GF_NONFLUSHABLE | GF_KEEP_AT_COMPLETION); +// GoalAddEnabler(obj, 1, AIE_GT_AWARENESS, (void *)&aware, 1.0, 0.0); +// GoalAddEnabler(obj, 1, AIE_LTE_LAST_SEE_TARGET_INTERVAL, (void *)&time, 1.0, 0.0); + } + break; + + case AIT_EVADER2: + { + goal_info g_info; + + g_info.roomnum = obj->roomnum; + g_info.pos = obj->pos; + + GoalAddGoal(obj, AIG_GUARD_AREA, (void *)&g_info, 1, 1.0f, GF_NONFLUSHABLE); + obj->ai_info->goals[1].circle_distance = obj->size * 2.0f; + GoalAddEnabler(obj, 1, AIE_GT_AWARENESS, (void *)&aware, 1.0, 0.0); + GoalAddEnabler(obj, 1, AIE_LTE_LAST_SEE_TARGET_INTERVAL, (void *)&time, 1.0, 0.0); + + int handle = OBJECT_HANDLE_NONE; + + int gi = GoalAddGoal(obj, AIG_GET_AROUND_OBJ, (void *)&handle, ACTIVATION_BLEND_LEVEL, 3.0f, GF_NONFLUSHABLE|GF_OBJ_IS_TARGET); + if(gi != -1) { //DAJ -1FIX + float temp = ai_info->circle_distance; + GoalAddEnabler(obj, gi, AIE_NEAR, (void *)&temp, 1.0, 0.0); + ai_info->goals[gi].circle_distance = ai_info->circle_distance; + } + } + break; + + case AIT_EVADER1: + { + goal_info g_info; + g_info.handle = ai_info->target_handle; + + GoalAddGoal(obj, AIG_MOVE_RELATIVE_OBJ, (void *)&g_info, 1, 1.0f, GF_SPEED_ATTACK | GF_NONFLUSHABLE); + GoalAddEnabler(obj, 1, AIE_GT_AWARENESS, (void *)&aware, 1.0, 0.0); + GoalAddEnabler(obj, 1, AIE_LTE_LAST_SEE_TARGET_INTERVAL, (void *)&time, 1.0, 0.0); + ai_info->goals[1].flags |= GF_OBJ_IS_TARGET; + + if(ai_info->circle_distance > 0.0f) + { + int handle = OBJECT_HANDLE_NONE; + int gi = GoalAddGoal(obj, AIG_GET_AROUND_OBJ, (void *)&handle, ACTIVATION_BLEND_LEVEL, 3.0f, GF_NONFLUSHABLE | GF_OBJ_IS_TARGET); + if(gi != -1) { //DAJ -1FIX + float temp = ai_info->circle_distance * 0.55f; + GoalAddEnabler(obj, gi, AIE_NEAR, (void *)&temp, 1.0, 0.0); + ai_info->goals[gi].circle_distance = ai_info->circle_distance * 0.55f; + } + } + } + break; + + case AIT_MELEE1: + { + goal_info g_info; + int flags = AISR_MELEE; + int m_goal; + + g_info.handle = ai_info->target_handle; + + GoalAddGoal(obj, AIG_MOVE_RELATIVE_OBJ, (void *)&g_info, 1, 1.0f, GF_NONFLUSHABLE | GF_OBJ_IS_TARGET | GF_KEEP_AT_COMPLETION); + GoalAddEnabler(obj, 1, AIE_GT_AWARENESS, (void *)&aware, 1.0, 0.0); + GoalAddEnabler(obj, 1, AIE_LTE_LAST_SEE_TARGET_INTERVAL, (void *)&time, 1.0, 0.0); + + GoalAddGoal(obj, AIG_GET_TO_OBJ, (void *) &ai_info->target_handle, 2, 1.0f, GF_SPEED_ATTACK | GF_NONFLUSHABLE | GF_OBJ_IS_TARGET | GF_USE_BLINE_IF_SEES_GOAL | GF_KEEP_AT_COMPLETION); + GoalAddEnabler(obj, 2, AIE_AI_STATUS_FLAG, (void *)&flags, 1.0, 0.0); + ai_info->goals[2].circle_distance = -100.0f; + GoalAddEnabler(obj, 2, AIE_GT_AWARENESS, (void *)&aware, 1.0, 0.0); + GoalAddEnabler(obj, 2, AIE_LTE_LAST_SEE_TARGET_INTERVAL, (void *)&time, 1.0, 0.0); + + m_goal = GoalAddGoal(obj, AIG_MELEE_TARGET, NULL, ACTIVATION_BLEND_LEVEL, 1.0f, GF_NONFLUSHABLE); + if(m_goal != -1) { //DAJ -1FIX + ai_info->goals[m_goal].flags |= GF_OBJ_IS_TARGET | GF_KEEP_AT_COMPLETION; + GoalAddEnabler(obj, m_goal, AIE_GT_AWARENESS, (void *)&aware, 1.0, 0.0); + GoalAddEnabler(obj, m_goal, AIE_LTE_LAST_SEE_TARGET_INTERVAL, (void *)&time, 1.0, 0.0); + } + } + break; + + case AIT_BIRD_FLOCK1: + { + } + break; + + case AIT_HERD1: + { + } + break; + + case AIT_STATIONARY_TURRET: + break; + + case AIT_AIS: + break; + + case AIT_FLYLANDER: + break; + + default: + { + Int3(); + } + } +} + +bool GoalIsGoalEnabled(object *obj, int goal_index) +{ + ai_frame *ai_info = obj->ai_info; + goal *cur_goal = &ai_info->goals[goal_index]; + bool f_enabled = true; + int i; + goal_enabler *e_info = &cur_goal->enabler[0]; + + ASSERT(goal_index >= 0 && goal_index < MAX_GOALS); + + if(OBJGOAL(cur_goal)) + { + object *gobj; + + if(cur_goal->flags & GF_OBJ_IS_TARGET) + { + gobj = ObjGet(obj->ai_info->target_handle); + } + else if(cur_goal->flags & (GF_OBJS_ARE_SPECIES | GF_OBJS_ARE_ENEMIES | GF_OBJS_ARE_FRIENDS)) + { + bool done = false; + + if(cur_goal->flags & (GF_OBJS_ARE_SPECIES | GF_OBJS_ARE_FRIENDS)) + { + // chrishack -- not done + } + } + else + { + gobj = ObjGet(cur_goal->g_info.handle); + } + + if(gobj == NULL && cur_goal->type == AIG_DODGE_OBJ) + return true; + + if(!gobj || gobj->type == OBJ_GHOST || (gobj->flags & (OF_DYING | OF_DESTROYED)) || (gobj->type == OBJ_PLAYER && Players[gobj->id].flags & (PLAYER_FLAGS_DEAD + PLAYER_FLAGS_DYING))) + { + return false; + } + } + + if(cur_goal->type == AIG_MELEE_TARGET) + { + if(ai_info->flags & AIF_DISABLE_MELEE) + { + return false; + } + } + + if(cur_goal->num_enablers == 0) + { + return true; + } + + for(i = 0; i < cur_goal->num_enablers; i++) + { + // Hmmm... What about 2 seconds to be enabled???? I think it is possible --chrishack + switch(e_info->enabler_type) + { + case(AIE_AI_STATUS_FLAG): + f_enabled = ((e_info->flags) & (ai_info->status_reg)) != 0; + break; + + case(AIE_NEAR): + { + float a_dist = e_info->dist; + f_enabled = (cur_goal->dist_to_goal <= a_dist) != 0; + } + break; + case(AIE_FAR): + { + float a_dist = e_info->dist; + f_enabled = (cur_goal->dist_to_goal >= a_dist) != 0; + } + break; + case(AIE_SCRIPTED): + { + notify se_info; + se_info.goal_num = goal_index; + se_info.enabler_num = i; + + f_enabled = AINotify(obj, AIN_SCRIPTED_ENABLER, &se_info); + } + break; + case(AIE_GT_AWARENESS): + { + f_enabled = (ai_info->awareness > e_info->awareness); + } + break; + case(AIE_LTE_AWARENESS): + { + f_enabled = (ai_info->awareness <= e_info->awareness); + } + break; + case(AIE_LTE_LAST_SEE_TARGET_INTERVAL): + { + f_enabled = (Gametime - ai_info->last_see_target_time <= e_info->time) || (Gametime - ai_info->last_hear_target_time <= e_info->time); + } + break; + case(AIE_GT_LAST_SEE_TARGET_INTERVAL): + { + f_enabled = (Gametime - ai_info->last_see_target_time > e_info->time) || (Gametime - ai_info->last_hear_target_time > e_info->time); + } + break; + case(AIE_FEAR): + { + f_enabled = e_info->float_value <= ai_info->cur_life_preservation; + } + break; + case(AIE_ANGRY): + { + f_enabled = e_info->float_value <= ai_info->cur_agression; + } + break; + case(AIE_CURIOUS): + { + f_enabled = e_info->float_value <= ai_info->cur_curiousity; + } + break; + case(AIE_FRUSTRATED): + { + f_enabled = e_info->float_value <= ai_info->cur_frustration; + } + break; + case(AIE_DELAY_TIME): + { + f_enabled = e_info->time < Gametime - cur_goal->creation_time; + } + break; + case(AIE_CLEAR_TIME): + { + f_enabled = e_info->time >= Gametime - cur_goal->creation_time; + if(!f_enabled) + { + GoalClearGoal(obj, cur_goal); + } + } + break; + default: + ASSERT(0); + break; + } + + // We are currently and'ing enablers + if(f_enabled == false) + break; + + e_info++; + } + + return f_enabled; +} + +goal *GoalGetCurrentGoal(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + int i; + goal *cur_goal = NULL; + + for(i = NUM_ACTIVATION_LEVELS - 1; i >= 0; i--) + { + if(ai_info->goals[i].used && GoalIsGoalEnabled(obj, i)) + { + cur_goal = &ai_info->goals[i]; + break; + } + } + + return cur_goal; +} + diff --git a/Descent3/AIGoal.h b/Descent3/AIGoal.h new file mode 100644 index 000000000..702cb5d1c --- /dev/null +++ b/Descent3/AIGoal.h @@ -0,0 +1,124 @@ +/* +* $Logfile: /DescentIII/main/AIGoal.h $ +* $Revision: 24 $ +* $Date: 3/30/99 4:32p $ +* $Author: Chris $ +* +* AI Goal Header +* +* $Log: /DescentIII/main/AIGoal.h $ + * + * 24 3/30/99 4:32p Chris + * Moved subtype to the main goal sturct (from goal_info). Made move + * relative object vec finishable. (Like get behind player) + * + * 23 1/25/99 7:43a Chris + * Added the GUID (Goal Unique Id) and added the ability for weapon + * batteries to always fire exactly forward. + * + * 22 12/14/98 1:07p Chris + * Allowed OSIRIS to change AI types + * + * 21 12/13/98 9:18p Chris + * Improved influence values for in-code goals (10000 to 1.0). Added + * GF_ORIENT stuff. :) + * + * 20 12/01/98 5:52p Chris + * Made the default influence 1.0 + * + * 19 12/01/98 4:30p Chris + * Checked in a massive amount of AI work. Improved flocking code. :) + * (Still hacked lightly). In addition, I am moving toward using the + * composite dir. :) + * + * 18 9/17/98 12:08p Chris + * Improved support of AIGoalAddEnabler + * + * 17 9/17/98 11:11a Chris + * Worked on goal system and improved integration with OSIRIS (better + * EVT_AI_INIT event calling) + * + * 16 5/15/98 2:58p Chris + * More on the big AI update + * + * 15 5/14/98 12:21p Chris + * Updating AI system... Incomplete + * + * 14 5/11/98 4:39p Chris + * Improved AI system (goals have notifies and are more flexable). + * + * 13 4/30/98 11:31a Chris + * Massive upgrades to the AI system + * + * 12 4/22/98 1:15p Chris + * Incremental work on paths and goals + * + * 11 4/21/98 11:25a Chris + * Improving GET_TO_OBJ goal + * + * 10 4/20/98 3:19p Chris + * Added more goal/path support + * + * 9 4/20/98 12:27p Chris + * Made AIG_GET_TO_OBJECT work. + * + * 8 2/11/98 7:00p Chris + * Removed priority (changed to influence) and added activation_level + * + * 7 1/14/98 7:57p Chris + * Improving the awareness system + * + * 6 11/14/97 11:55p Chris + * Dodge goals are semi-functional + * + * 5 7/15/97 7:29p Chris + * Added a sound for helicopter blades. + * + * 4 7/15/97 5:35p Chris + * New AI code + * + * 3 7/15/97 4:59p Chris + * added support for AI and animations +* +* $NoKeywords: $ +*/ + +#ifndef AIGOAL_H_ +#define AIGOAL_H_ + +#include "object.h" + +// Clears and removes all goals for a robot +void GoalClearAll(object *obj); + +// Adds a new goal +int GoalAddGoal(object *obj, unsigned int goal_type, void *args, int level = 0, float influence = 1.0f, int f_goal = 0, int guid = -1, char subtype = 0); + +// Adds a ending condition to a goal +int GoalAddDisabler(object *obj, int goal_index, ubyte ender_type, void *args, float percent = 1.0f, float interval = 0.0f); + +// Adds a enabler condition to a goal +int GoalAddEnabler(object *obj, int goal_index, ubyte enabler_type, void *arg_struct, float percent, float interval); + +// Clears one goal +void GoalClearGoal(object *obj, goal *goal_ptr, int notify_reason = AI_INVALID_INDEX); + +// Init's an AI to no goals +void GoalInitTypeGoals(object *obj, int ai_type); + +// Does the goal related stuff for an object per frame +void GoalDoFrame(object *obj); + +// Goal's path is complete. What next? +void GoalPathComplete(object *obj); + +// Is a used goal currently enabled? +bool GoalIsGoalEnabled(object *obj, int goal_index); + +// Determines minimum distance to look for nearby objects +float GoalDetermineTrackDist(object *obj); + +// Returns a pointer to the highest priority non-blended goal +goal *GoalGetCurrentGoal(object *obj); + +#endif \ No newline at end of file diff --git a/Descent3/AIMain.h b/Descent3/AIMain.h new file mode 100644 index 000000000..952e3f0b8 --- /dev/null +++ b/Descent3/AIMain.h @@ -0,0 +1,178 @@ +/* +* $Logfile: /DescentIII/main/AIMain.h $ +* $Revision: 41 $ +* $Date: 5/17/99 6:06p $ +* $Author: Chris $ +* +* General AI functions for use in Descent III +* +* $Log: /DescentIII/main/AIMain.h $ + * + * 41 5/17/99 6:06p Chris + * Adding robot debug code + * + * 40 5/08/99 4:42p Chris + * + * 39 5/08/99 4:12p Chris + * Added AI hearing noises... version 1 + * + * 38 5/01/99 2:21a Chris + * Use the GF_SPEED_XXX stuff + * + * 37 4/18/99 5:38a Chris + * Drastically improved the find_random_room function and added the + * makenextroomlist function + * + * 36 3/12/99 12:16p Chris + * Blending primary and blend goals!!!!!! Awesome. + * + * 35 2/28/99 11:30p Chris + * FindObjOfType and OSIRIS controllable deaths + * + * 34 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 33 1/29/99 5:10p Chris + * Added an optional parent handle check for FindObjOfType + * + * 32 1/20/99 1:01a Chris + * Improved AI and OSIRIS intergration + * + * 31 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 30 12/15/98 6:05p Chris + * Improved the path system + * + * 29 12/15/98 4:34p Chris + * + * 28 12/03/98 12:24p Chris + * Improved new anim code for instant updating + * + * 27 12/01/98 4:31p Chris + * Checked in a massive amount of AI work. Improved flocking code. :) + * (Still hacked lightly). In addition, I am moving toward using the + * composite dir. :) + * + * 26 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 25 10/13/98 1:08p Chris + * Greatly improved the AI's use of paths. Improved visability checking + * algorithm. Probably needs a second pass for further cleanup. + * + * 24 10/06/98 6:19p Chris + * Improved OSIRIS/AI intergration + * + * 23 9/28/98 4:09p Chris + * Added birth animations + * + * 22 9/14/98 12:16p Chris + * Vastly improved the multiplayer code + * + * 21 8/19/98 12:10p Chris + * Improved coop behavior + * + * 20 8/10/98 12:16p Chris + * Added AI_NumHostileAlert + * + * 19 6/30/98 6:36p Chris + * Added rev .1 of multiplayer animations - BTW It is totally not done. + * + * 18 5/17/98 7:54p Chris + * Correctly integrated the goal system and circle distance stuff. Added + * support for "target goals". + * + * 17 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 16 4/30/98 11:31a Chris + * Massive upgrades to the AI system + * + * 15 4/16/98 2:56p Chris + * Updated buddy support and intergrated scripting and AI + * + * 14 4/07/98 4:25p Chris + * Added support for buddy bot + * + * 13 3/31/98 4:23p Chris + * Added a new AIpath system + * + * 12 3/10/98 12:51p Chris + * Added a truely D2 style homing missile + * + * 11 2/06/98 5:51p Chris + * Added the ability turn robots on and off + * + * 10 1/29/98 3:29p Chris + * Major update to the AI system. + * + * 9 11/14/97 11:55p Chris + * Dodge goals are semi-functional + * + * 8 11/12/97 5:47p Chris + * Incremental improvements + * + * 7 11/12/97 10:43a Chris + * Incremental + * + * 6 8/05/97 12:17p Chris + * + * 5 7/15/97 7:29p Chris + * Added a sound for helicopter blades. + * + * 4 7/15/97 5:35p Chris + * New AI code + * + * 3 7/15/97 4:59p Chris + * added support for AI and animations +* +* $NoKeywords: $ +*/ + +#ifndef AIMAIN_H_ +#define AIMAIN_H_ + +#include "object.h" +#include "player.h" + +#define AI_SOUND_SHORT_DIST 60.0f + +extern int AI_NumRendered; +extern int AI_RenderedList[MAX_OBJECTS]; + +extern int AI_NumHostileAlert; // A rough number of alert/hostile robots + // that have seen the player recently +#ifdef _DEBUG +extern bool AI_debug_robot_do; +extern int AI_debug_robot_index; +#endif + +// Is my buddy in the level? (no if the handle is invalid) +extern int Buddy_handle[MAX_PLAYERS]; + +bool AINotify(object *obj, ubyte notify_type, void *info = NULL); +void AIDoFrame(object *obj); +void AIFrameAll(void); +bool AIInit(object *obj, ubyte ai_class, ubyte ai_type, ubyte ai_movement); +void AIInitAll(void); +void AIPowerSwitch(object *obj, bool f_on); +void AITurnTowardsDir(object *obj, /*velocity *new_vel,*/ vector *goal_dir/*, bool remain_level*/, float turn_rate); +void AIMoveTowardsDir(object *obj, vector *dir, float scale = 1.0f); +bool AIMoveTowardsPosition(object *obj, /*velocity *new_vel,*/ vector *pos, float scale, bool stop_at_end_point, vector *mdir, bool *f_moved); +void AITurnTowardsPosition(object *obj, /*velocity *new_vel,*/ vector *pos/*, bool remain_level*/); +bool AIFindHidePos(object *hide_obj, object *view_obj, vector *hpos, int *hroom, float max_hide_time = 3.0f); +int AIFindRoomWithFlag(object *obj, int flag); +object *AIFindObjOfType(object *obj, int type, int id, bool f_ignore_init_room, int parent_handle = OBJECT_HANDLE_NONE); +float AIFindDist(vector *s_pos, int s_roomnum, vector *e_pos, int e_roomnum, int flags); +bool AIStatusCircleFrame(object *obj, object *g_obj, float dist, float c_dist, int *status_reg); +bool AIObjEnemy(object *obj, object *target); +bool AISetTarget(object *obj, int handle); +void AIDestroyObj(object *obj); +bool AIObjFriend(object *obj, object *target); +void AIUpdateAnim(object *obj); +bool AITurnTowardsMatrix(object *obj, float turn_rate, matrix *g_orient); +int AIFindRandomRoom(object *obj, ai_frame *ai_info, goal *goal_ptr, int avoid_room, int min_depth, int max_depth, bool f_check_path, bool f_cur_room_ok, int *depth); +int AIMakeNextRoomList(int roomnum, int *next_rooms, int max_rooms); + +#endif \ No newline at end of file diff --git a/Descent3/AImain.cpp b/Descent3/AImain.cpp new file mode 100644 index 000000000..b70d58edf --- /dev/null +++ b/Descent3/AImain.cpp @@ -0,0 +1,7234 @@ +/* +* $Logfile: /DescentIII/main/AImain.cpp $ +* $Revision: 479 $ +* $Date: 4/19/00 5:08p $ +* $Author: Matt $ +* +* The Main of the AI code. +* +* $Log: /DescentIII/main/AImain.cpp $ + * + * 479 4/19/00 5:08p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * Added error checking and minor optimization + * + * 478 3/20/00 12:00p Matt + * Merge of Duane's post-1.3 changes. + * Bail from anim update if no anim data. + * + * 477 11/02/99 12:17p Chris + * Improved the targetting code (no max dist when in the same room as + * target) + * + * 476 10/24/99 10:46p Chris + * Added the target_died notification + * + * 475 10/23/99 2:43a Chris + * Added the PutObjectOnObject AI Goal + * + * 474 10/22/99 10:43p Jeff + * put in check to handle new code from mac + * + * 473 10/22/99 3:40p Kevin + * Mac merge fixes + * + * 472 10/21/99 2:12p Matt + * Mac merge + * + * 471 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 470 10/17/99 11:58p Chris + * Improved the hearing/seeing code, Added AIN_FIRED_WEAPON notify, and + * added a *hack* so that the final boss could fire his weapons during a + * cutscene... (usually this is a no-no). + * + * 469 10/08/99 4:53p Nate + * Added ability to disable individual melee attacks + * + * 468 10/08/99 4:14p 3dsmax + * creeper fix (attached objs and FOV) (chris) + * + * 467 7/27/99 6:39p Chris + * Turned off the GB's player weapon dodging when he is in RTYPE mode + * + * 466 7/27/99 4:20p Chris + * Improved GB avoidance code + * + * 465 7/26/99 3:52p Chris + * Wall pulsing for the GB + * + * 464 7/26/99 1:18p Chris + * He now only dodges player shots + * + * 463 7/26/99 1:16p Chris + * GB now dodges player (and targetted robot) shots + * + * 462 5/24/99 3:23p Chris + * Fixed team anarchy bug. + * + * 461 5/23/99 10:39p Chris + * Fixed client gunboys so they not fire at the server + * + * 460 5/23/99 5:37a Chris + * Fixed bugs with non-AIF_DODGE objects dodging. Made stopping + * tolerances bigger + * + * 459 5/23/99 12:35a Chris + * Fixed the stop exactly problems + * + * 458 5/21/99 7:45p Chris + * Tweaked the cdist code... (again) + * + * 457 5/21/99 6:01p Chris + * Fixed turret speed scaling problems + * + * 456 5/21/99 7:29a Chris + * More tweaking... + * + * 455 5/20/99 3:48p Chris + * Rebel stuff isn't usually scaled by diff anymore + * + * 454 5/20/99 1:44a Chris + * Improved scaling of velocity for walkers (I.e. dont scale max speed or + * rotational speed because it make them look slow and dumb) + * + * 453 5/20/99 1:15a Chris + * Improved BNode Path Following for end points on the path. Fixed bugs + * with non-auto targetting + * + * 452 5/19/99 2:16a Chris + * Fixed gunboys in coop (will not fire on teammates now) + * + * 451 5/18/99 10:54p Chris + * Added some ASSERTs + * + * 450 5/18/99 6:58p Chris + * Hmmmm.... Not sure how that crash happened + * + * 449 5/18/99 10:57a Chris + * Various bug fixes + * + * 448 5/17/99 6:06p Chris + * Adding robot debug code + * + * 447 5/12/99 11:15p Chris + * Adjusted fvi_find for vis. + * + * 446 5/12/99 6:18a Chris + * Vastly improved FindObjOfType. Ignores non-rendered objects, if type + * is robot it ignores cameras, dead robots, or robots without any + * gunpoints (like a mine). + * + * 445 5/11/99 5:49p Chris + * Scaled energy drain by diff level... Increased HotShot energy drain, + * Fixed problem with Thief sound getting muffled by melee hit sound + * + * 444 5/10/99 5:11p Chris + * Fixed another hearing/seeing bug + * + * 443 5/10/99 2:59p Chris + * Fixed fov bug + * + * 442 5/10/99 8:21a Chris + * Reduced wall collision size for GB + * + * 441 5/10/99 12:23a Chris + * Fixed another hearing/seeing case. :) Buddy bot now is in the player + * ship at respawn + * + * 440 5/09/99 8:11p Chris + * Made the whole see/hear distintion work (mostly) + * + * 439 5/08/99 10:38p Chris + * Further scaled the game. :) + * + * 438 5/08/99 6:18p Chris + * Fixed up no hearing cases + * + * 437 5/08/99 4:12p Chris + * Added AI hearing noises... version 1 + * + * 436 5/07/99 10:56p Chris + * GB avoids forcefields, lava, and volatile surfaces... If he accidently + * hits one of these, he ignores the collision (i.e. no effects). It make + * him look smarter. + * + * 435 5/07/99 9:33p Chris + * Improve wall avoidance when never volatile, lava, and water surfaces + * + * 434 5/07/99 1:43p Chris + * Made in-game cinematics use the ORIENT_PATH_NODE for player ships + * + * 433 5/07/99 11:51a Chris + * Improve the algorithm for zero awareness firing + * + * 432 5/07/99 11:24a Chris + * Made 0 agresion robots only fire when they have a clear shot + * + * 431 5/04/99 6:49p Jason + * fixed dumb bug with spray weapons + * + * 430 5/02/99 1:59a Jason + * possibly fixed a bug related to spray weapons and dying + * + * 429 5/01/99 3:49a Chris + * Added the "No scale movement properties by diff level" checkbox + * + * 428 5/01/99 2:21a Chris + * Use the GF_SPEED_XXX stuff + * + * 427 4/30/99 2:09a Chris + * Gunboys will not target triggers anymore + * + * 426 4/29/99 3:59p Chris + * Fixed AI_SEE_SOUND playing too often (like all the time...) + * + * 425 4/27/99 4:41a Jeff + * only create guidebots if a single player game, or if its a multiplayer + * game and the correct netflag is set + * + * 424 4/26/99 11:11a Chris + * Updated Bnode system + * + * 423 4/24/99 2:20a Chris + * Added the Neutral_till_hit flag + * + * 422 4/21/99 2:53p Matt + * Added a new type for dying objects that have AI, instead of keeping a + * flag in the dying info. + * + * 421 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 420 4/20/99 8:55p Chris + * Fixed problem with robots not being able to open locked doors that a + * player has the key for. + * + * 419 4/18/99 10:23a Chris + * + * 418 4/18/99 5:38a Chris + * Drastically improved the find_random_room function and added the + * makenextroomlist function + * + * 417 4/15/99 1:32a Jeff + * linux changes to compile /Main0 /Main1 /Main2 /Main3 /Main4 /Main5 + * /Main6 /Main7 /Main8 /Main9 /descent3/Main0 /descent3/Main1 + * /descent3/Main2 /descent3/Main3 /descent3/Main4 /descent3/Main5 + * /descent3/Main6 /descent3/Main7 /descent3/Main8 /descent3/Main9 + * AImain.cpp0 AImain.cpp1 AImain.cpp2 AImain.cpp3 AImain.cpp4 AImain.cpp5 + * AImain.cpp6 AImain.cpp7 AImain.cpp8 AImain.cpp9 linux0 linux1 linux2 + * linux3 linux4 linux5 linux6 linux7 linux8 + * + * 416 4/14/99 10:31p Jeff + * fixed name of ai_info struct to t_ai_info + * + * 415 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 414 4/12/99 6:15p Samir + * Sound priorities pass 1 + * + * 413 4/12/99 5:45p Chris + * Improved Freud + * + * 412 4/10/99 6:39p Matt + * Only save the designer-editable AI data in the Object_info array, + * instead of the whole ai_frame structure. This saves 3200 bytes per + * Object_info entry, which is about 2 MB overall. + * + * 411 4/09/99 10:33a Chris + * Improvements to Freud + * + * 410 4/08/99 7:46p Chris + * Improving Freud + * + * 409 4/08/99 6:03p Chris + * Improved Freud + * + * 408 4/08/99 3:35p Chris + * Improved the Fear factor in Freud + * + * 407 4/07/99 3:55a Chris + * Independant (non-parented) Rebel robots will now fire on PTMC and + * Hostile robots. :) + * + * 406 4/06/99 11:04p Chris + * Global Unique id's for goals and circle dist stuff can be set when + * aware barely + * + * 405 4/06/99 6:36p Jason + * CHRIS -- Fixed a bug with AIDetermineFovVec() + * + * 404 4/06/99 11:18a Chris + * Fixed a big bug in the attach system where object's properties where + * not correctly reset after the attach + * + * 403 4/05/99 3:18p Chris + * Patching wandering code + * + * 402 4/05/99 11:36a Chris + * + * 401 4/05/99 11:36a Chris + * Fixed a few bugs with Orient to Node + * + * 400 4/02/99 3:55p Chris + * Fixed or at least commented about guidebot bugs + * + * 399 4/02/99 3:49p Chris + * Create GBs in multiplayer + * + * 398 4/02/99 10:18a Chris + * We can now mess with the Object_info anim stuff + * + * 397 3/30/99 4:32p Chris + * Moved subtype to the main goal sturct (from goal_info). Made move + * relative object vec finishable. (Like get behind player) + * + * 396 3/29/99 5:32p Kevin + * Build fixes + * + * 395 3/27/99 1:08p Chris + * AIPowerSwitch now prop's though all objects that are attached to the + * object that is turned on/off + * + * 394 3/26/99 6:57p Chris + * Fixed a bug with the landing code + * + * 393 3/26/99 2:58p Chris + * See sounds play 90% of the time + * + * 392 3/25/99 4:56p Chris + * Added code for the land on object DALLAS goal + * + * 391 3/25/99 12:00p Chris + * + * 390 3/25/99 11:56a Jason + * changed some sequencing bugs + * + * 389 3/23/99 11:51a Matt + * Made wander code not crash if the room selected was an outside room + * (from an inside wander) I.e. two rooms in two different mines with + * terrain between them. + * + * 388 3/22/99 7:10p Chris + * Added path node orientation + * + * 387 3/22/99 3:56p Chris + * + * 386 3/22/99 10:58a Chris + * Awareness code was tweaked. Multisafe stuff added for objects. + * + * 385 3/18/99 12:46p Chris + * Fixed an FOV and awareness conflict + * + * 384 3/17/99 5:23p Chris + * Fixed problems with low-priority paths + * + * 383 3/15/99 2:39p Chris + * Added some terrain avoidance + * + * 382 3/12/99 7:10p Chris + * Avoid avoid walls version 1.0 + * + * 381 3/12/99 12:16p Chris + * Blending primary and blend goals!!!!!! Awesome. + * + * 380 3/10/99 4:15p Chris + * Patches birds for now + * + * 379 3/09/99 2:42p Chris + * Backwards gone + * + * 378 3/09/99 2:13p Chris + * Robots flying backwards + * + * 377 3/05/99 6:07p Chris + * Fixed bug where OSIRIS wasn't informed at the 'level' level that a + * goal_uid was completed (it was and still is fine at the object level) + * + * 376 3/05/99 10:33a Chris + * Fixed the bashed it_handle bug + * + * 375 3/04/99 12:44p Chris + * Commented out Ignore height diff code -- chrishack + * + * 374 3/03/99 5:35p Kevin + * HAck + * + * 373 3/03/99 3:12p Chris + * No AI firing during cinematics + * + * 372 3/03/99 1:18p Chris + * Init'ed the perceived_vec_to_target + * + * 371 3/03/99 7:22a Chris + * Added Napalmed to the list + * + * 370 3/03/99 6:52a Chris + * Fixed headlight + * + * 369 3/03/99 5:47a Chris + * + * 368 3/03/99 5:47a Chris + * Fixed the headlight thing + * + * 367 3/03/99 5:45a Chris + * Further cased out the auto-turn stuff + * + * 366 3/02/99 11:41p Chris + * + * 365 3/02/99 10:53p Chris + * + * 364 3/02/99 10:46p Chris + * Improved the vis code + * + * 363 3/02/99 5:39p Chris + * Fixed the gunboy rotation problem + * + * 362 3/01/99 7:19p Chris + * Fixed dist problems with finding nearby objects + * + * 361 2/28/99 11:30p Chris + * FindObjOfType and OSIRIS controllable deaths + * + * 360 2/25/99 5:43p Chris + * Massive improvement to BOA and AI (again) + * + * 359 2/25/99 10:57a Matt + * Added new explosion system. + * + * 358 2/24/99 12:27p Chris + * Fixed problems with GB finding robots/room he couldn't get to. Fixed + * problems with forcefields(sound prop. and path finding). Fixed + * problems with small portals. + * + * 357 2/18/99 3:50p Chris + * Updated the exact stopping code for paths. Scaled acceleraion by + * max_speed scalar. + * + * 356 2/17/99 8:47p Chris + * See if this fixes Dan's rotating walker problem + * + * 355 2/17/99 1:21a Chris + * Updated the AI movement algorithm, fixed many bugs, added the + * last_dodge_dir vector so that robots can dodge the vauss and the like + * + * 354 2/15/99 9:03p Chris + * Added the base FOV off UVEC code and converted all the turrets + * + * 353 2/15/99 8:59a Chris + * + * 352 2/15/99 8:58a Chris + * + * 351 2/15/99 8:55a Chris + * Fixed a bug with AIF_XZ_DIST (It incorrectly computed the distance from + * a target) + * + * 350 2/12/99 11:19a Chris + * If max_turn_rate is zero, then don't call the orient code + * + * 349 2/10/99 4:02p Chris + * Updated the attach system so that if a robot is idling, the AI will not + * update the attached subobjects (if the object is still moving, physics + * will do it). + * + * 348 2/10/99 2:41p Chris + * Added debug info + * + * 347 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 346 2/09/99 3:52p Chris + * Fixed melee attacks + * + * 345 2/09/99 12:40p Chris + * More aipath stuff has been merged with the new BOA + * + * 344 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 343 2/03/99 11:09a Chris + * Stop at position goals will now get the AIN_GOAL_COMPLETE correctly + * + * 342 2/02/99 12:24p Sean + * CHRIS -- Fixed a bug with dummy turrets. They wehre using a bogus + * index. + * + * 341 2/02/99 11:27a Chris + * Added the AIN_MOVIE_START and AIN_MOVIE_END notifies + * + * 340 2/02/99 9:23a Chris + * Fixed turning problems caused by zero vectors (which return a random + * vector) + * Fixed jitteryness problem caused by having too tight of a need-to-turn + * tolerance on large robots + * + * 339 2/02/99 9:06a Chris + * Fixed a bug where robots where starting in AS_ALERT instead of AS_IDLE + * + * 338 1/31/99 10:43p Chris + * added WBF_AIM_FVEC + * + * 337 1/29/99 5:27p Luke + * CHRIS - Updated the Gaurd goal + * + * 336 1/29/99 5:10p Chris + * Added an optional parent handle check for FindObjOfType + * + * 335 1/26/99 2:51p Chris + * AIG_WANDER improvements + * + * 334 1/25/99 7:43a Chris + * Added the GUID (Goal Unique Id) and added the ability for weapon + * batteries to always fire exactly forward. + * + * 333 1/24/99 10:58p Chris + * Fixed problems with AvoidObj code + * + * 332 1/24/99 8:17p Chris + * Updated AI and OSIRIS. Externalized fireball constants for spew and + * sparks. Added CreateRandomSparks to OSIRIS, renamed a bunch of AI + * Notify names to use underscores. Fixed a memory access leak in the + * matcen effect code. + * + * 331 1/23/99 2:20p Chris + * Removed a walker hack + * + * 330 1/22/99 6:53p Chris + * Fixed LoadandBind Aux notify problems, fixed static path problems, + * fixed AIF_FORCE_AWARE Problems, improved path code + * + * 329 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 328 1/20/99 2:13a Chris + * It is now possible for robots to have special immunities, resistances, + * and vunerabilities + * + * 327 1/20/99 1:01a Chris + * Improved AI and OSIRIS intergration + * + * 326 1/18/99 8:07p Chris + * Added the no-collide same flag (for flocks and nests) + * + * 325 1/18/99 10:14a Chris + * + * 324 1/18/99 9:05a Chris + * Improved OSIRIS, AI, and ATTACH system, changed wiggle code, changed + * attach code for rad attaches, and added the AIG_ATTACH_OBJ goal + * + * 323 1/15/99 5:57p Chris + * fixed some awareness bugs + * + * 322 1/13/99 6:37a Jeff + * fixed object.h. There were numerous struct declarations that were the + * same name as the instance of the struct (gcc doesn't like this). + * Changed the struct name. Also added some #ifdef's for linux build, + * along with fixing case-sensitive includes + * + * 321 1/13/99 2:28a Chris + * Massive AI, OSIRIS update + * + * 320 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 319 1/06/99 5:06p Chris + * Improving OSIRIS/game intergration - improved support for custom + * animations + * + * 318 1/05/99 6:28p Chris + * Fix OSIRIS AI_Notify problem + * + * 317 12/30/98 3:46p Chris + * Incremental AI changes + * + * 316 12/23/98 6:10p Matt + * Fixed compiler warnings + * + * 315 12/18/98 5:40p Chris + * Fixed a crash bug in the new OSIRIS + * + * 314 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 313 12/16/98 3:55p Chris + * Improved the sickles + * + * 312 12/15/98 4:58p Chris + * Improved the path system + * + * 311 12/15/98 4:08p Chris + * Removed the annoying stop mprintf + * + * 310 12/15/98 4:04p Chris + * Wall landers work + * + * 309 12/14/98 2:04p Chris + * Fixed an evader1 blend problem + * + * 308 12/14/98 1:07p Chris + * Allowed OSIRIS to change AI types + * + * 307 12/14/98 12:03a Chris + * Turning and orientation are done in one (minus a few hacks) spot + * + * 306 12/13/98 9:18p Chris + * Improved influence values for in-code goals (10000 to 1.0). Added + * GF_ORIENT stuff. :) + * + * 305 12/12/98 10:11p Chris + * Cleaned up some CT stuff + * + * 304 12/11/98 6:24p Chris + * Additional Comments + * + * 303 12/11/98 1:57p Chris + * Improved wall walking code + * + * 302 12/11/98 12:04p Chris + * Improved the flocking hack. :) + * + * 301 12/08/98 4:27p Chris + * Fixed all avoid code from avoiding attached robots + * + * 300 12/08/98 2:14p Chris + * Fixed bugs when an object is avoiding a robot the is attached to it + * + * 299 12/03/98 5:45p Chris + * I just added full code support for OSIRIS/DLL controlled goals, + * enablers, and influence levels. :) + * + * 298 12/03/98 2:31p Chris + * The auto-avoid friends flag is now cooler now (they avoid at greater + * distances when they are within thier circle distance from a targetted + * enemy) + * + * 297 12/03/98 12:24p Chris + * Improved new anim code for instant updating + * + * 296 12/03/98 12:04p Chris + * Further de-hacked flocking and other major AI systems + * + * 295 12/02/98 5:51p Chris + * Lint cleaning + * + * 294 12/02/98 2:27p Chris + * Algorithm swap stuff for target leading + * + * 293 12/02/98 2:13p Chris + * We now use the target lead accuracy slider + * + * 292 12/01/98 6:17p Chris + * Height bias is only for outside and GoalDetermineTrackDist is closer to + * final + * + * 291 12/01/98 5:09p Chris + * Improved the height bias code + * + * 290 12/01/98 4:31p Chris + * Checked in a massive amount of AI work. Improved flocking code. :) + * (Still hacked lightly). In addition, I am moving toward using the + * composite dir. :) + * + * 289 12/01/98 3:31p Keneta + * Added a temp fix for avoid_obj when there are no objs + * + * 288 11/23/98 3:11p Kevin + * Demo system + * + * 287 11/23/98 11:26a Chris + * More improvements to the AI system + * + * 286 11/23/98 11:09a Chris + * Incremental improvements + * + * 285 11/20/98 11:37a Chris + * Improved avoidance code + * + * 284 11/19/98 8:55p Chris + * + * 283 11/19/98 8:55p Chris + * + * 282 11/19/98 8:55p Chris + * I am being to clean up the structure + * + * 281 11/19/98 8:26p Chris + * Starting to add generic team avoidance code + * + * 280 11/18/98 6:25p Chris + * Dodge->avoid->normal. That is the order. To do it right, dodge will + * get a BIG scalar, avoid will get a smaller scalar, and normal stuff + * will get a tiny scalar. + * + * 279 11/18/98 3:59p Chris + * I added the avoid goal. I also improved the AIs for Evader1 and + * Evader2 by utilizing this goal when the target is well within the + * circle distance. + * + * 278 11/17/98 4:16p Kevin + * Demo recording system + * + * 277 11/11/98 7:18p Jeff + * changes made so that a dedicated server's team is always -1 (team game + * or not) + * + * 276 11/11/98 6:31p Chris + * AIF_DISABLE_FIRING and AIF_DISABLE_MELEE are now functional + * + * 275 11/11/98 2:46p Kevin + * Demo recording system work + * + * 274 11/11/98 12:11p Chris + * The attach system and weapon firing (continous and spray) are now + * network friendly + * + * 273 11/10/98 6:17p Chris + * Improved AI - added support for manual max firing distances + * + * 272 11/09/98 3:08p Kevin + * Turned off AI while playing back a demo + * + * 271 11/06/98 5:34p Chris + * + * 270 11/06/98 11:39a Chris + * Robots with flamethrowers and Omega cannons work in single player + * + * 269 10/29/98 1:45p Chris + * Checked in the better collision response code + * + * 268 10/28/98 4:37p Chris + * Robots who are parented by ghosts still know their team. + * + * 267 10/22/98 5:36p Chris + * Fixed ObjGet bugs + * + * 266 10/22/98 2:58p Chris + * Difficulty levels are in beta + * + * 265 10/21/98 5:35p Chris + * Added friend forgiving to the aggression slider + * + * 264 10/21/98 7:33a Chris + * Dead objects are not-targetable + * + * 263 10/20/98 11:56p Chris + * + * 262 10/20/98 10:52p Chris + * Improved the single player targeting when a player is dead + * (Player->buddy->none) + * + * 261 10/19/98 7:20p Chris + * Added custom fire dot code + * + * 260 10/19/98 7:17p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 259 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 258 10/15/98 3:26p Chris + * Fixed known ground plane issues and used PhysCalcGround everywhere + * + * 257 10/14/98 7:04p Chris + * Update turret sound code + * + * 256 10/14/98 6:54p Chris + * Added turret change direction sounds and level goal ability to toggle + * auto level end + * + * 255 10/14/98 4:45p Chris + * Added support for a endlevel in-game movie + * + * 254 10/14/98 3:39p Chris + * Improved robot dodging and firing + * + * 253 10/13/98 1:08p Chris + * Greatly improved the AI's use of paths. Improved visability checking + * algorithm. Probably needs a second pass for further cleanup. + * + * 252 10/09/98 4:01p Chris + * + * 251 10/09/98 2:24a Chris + * + * 250 10/09/98 2:22a Chris + * Improving vis checking + * + * 249 10/09/98 2:17a Chris + * + * 248 10/09/98 1:39a Chris + * Added very infrequent target updating for robots with an awareness less + * than AWARE_BARELY + * + * 247 10/09/98 1:35a Chris + * Fixed a bug with robots only attacking after they are rendered + * + * 246 10/08/98 8:49p Chris + * Improving AI initialization? + * + * 245 10/07/98 10:06p Chris + * Improved the attach system's updating + * + * 244 10/07/98 3:37p Chris + * Improved the melee attack code + * + * 243 10/07/98 12:39p Chris + * Added support for zero latency attacks + * + * 242 10/07/98 10:54a Chris + * Matcens use the birth animations + * + * 241 10/06/98 6:19p Chris + * Improved OSIRIS/AI intergration + * + * 240 10/06/98 5:45p Kevin + * Added new configuration for demo + * + * 239 9/30/98 4:40p Chris + * + * 238 9/30/98 4:36p Chris + * Fixed a targetting bug + * + * 237 9/30/98 3:49p Chris + * Changed comment + * + * 236 9/29/98 3:11p Chris + * + * 235 9/28/98 7:23p Chris + * same as prev + * + * 234 9/28/98 7:03p Chris + * Improved targetting of objects by AIs + * + * 233 9/28/98 6:23p Chris + * Changed multi_anim to custom_anim + * + * 232 9/28/98 4:09p Chris + * Added birth animations + * + * 231 9/28/98 1:15p Chris + * Fixed the targetting of parents(oops) after parent-collide-timeout + * + * 230 9/28/98 10:34a Chris + * Fixed a semi-colon bug + * + * 229 9/22/98 6:03p Samir + * ifdef out DoAI if not in debug version. + * + * 228 9/17/98 11:11a Chris + * Worked on goal system and improved integration with OSIRIS (better + * EVT_AI_INIT event calling) + * + * 227 9/16/98 4:30p Chris + * Added target by distance + * + * 226 9/15/98 7:29p Chris + * Improved OSIRIS and AI intergration + * + * 225 9/14/98 1:14p Chris + * Improved multiplayer team/non-team support + * + * 224 9/14/98 12:56p Chris + * Automatically put player parented objects as part of AIF_TEAM_REBEL + * + * 223 9/14/98 12:23p Chris + * + * 222 9/14/98 12:16p Chris + * Vastly improved the multiplayer code + * + * 221 9/11/98 5:27p Chris + * Further improved the cooperative multiplayer ai + * + * 220 9/11/98 11:58a Chris + * Fixed too many sounds in cooperative multiplayer + * + * 219 8/25/98 10:44a Chris + * Worked on leading code a bit + * + * 218 8/25/98 10:21a Chris + * Removed 2 annoying mprintfs + * + * 217 8/19/98 6:25p Chris + * Added the infighting sliders + * + * 216 8/17/98 4:55p Chris + * Reduced infighting + * + * 215 8/15/98 6:10p Chris + * Added OSIRIS controlled firing + * + * 214 8/12/98 6:10p Chris + * Added more to the attach code. Added support for OBJ_DUMMY + * + * 213 8/10/98 12:16p Chris + * Added AI_NumHostileAlert + * + * 212 8/06/98 11:49a Chris + * OBJ_NONE objects where getting notified of stuff + * + * 211 8/03/98 3:59p Chris + * Added support for FQ_IGNORE_WEAPONS, added .000001 attach code, fix a + * bug in polymodel collision detection + * + * 210 7/29/98 10:24a Chris + * Improved target updating code for multiplayer + * + * 209 7/28/98 5:48p Chris + * + * 208 7/28/98 5:41p Chris + * + * 207 7/28/98 5:04p Chris + * Added some new multiplayer support (for dodging and targetting) + * + * 206 7/24/98 6:06p Chris + * Initial robot leading code -- needs multiple wb support + * + * 205 7/17/98 6:08p Chris + * Greatly improved homing performance + * + * 204 7/14/98 5:52p Kevin + * Packet loss measurements and auto pps adjusting + * + * 203 7/09/98 3:36p Chris + * + * 201 7/09/98 1:05p Chris + * Added some parenthesis + * + * 200 7/09/98 12:44p Chris + * + * 199 7/09/98 12:34p Chris + * Fixed a problem with the current interp code + * + * 198 7/09/98 12:31p Chris + * Improved the turret interp code + * + * 197 7/09/98 11:34a Chris + * Turrets are interpolated. + * + * 196 7/08/98 1:02p Chris + * + * 195 7/08/98 12:11p Chris + * + * 194 7/08/98 11:38a Chris + * Improved the turret info passing in multiplayer + * + * 193 7/07/98 11:29a Chris + * + * 192 7/07/98 10:10a Kevin + * Added basic turret support for coop + * + * 191 7/06/98 10:27a Chris + * Re-added general awareness + * + * 190 7/02/98 6:08p Chris + * Removed the quick hack way of doing awareness + * + * 189 7/01/98 7:11p Chris + * Added multi blocks + * + * 188 7/01/98 6:36p Chris + * Added more multiplayer support + * + * 187 7/01/98 4:35p Chris + * More multiplayer sync issues + * + * 186 7/01/98 2:02p Chris + * Added the sound for animations + * + * 185 7/01/98 10:58a Chris + * Working on multiplayer AI stuff + * + * 184 6/30/98 6:36p Chris + * Added rev .1 of multiplayer animations - BTW It is totally not done. + * + * 183 6/29/98 1:50p Chris + * Fixed a turning multiplayer bug + * + * 182 6/29/98 1:35p Chris + * Incremental improvements + * + * 181 6/29/98 1:12p Chris + * Dodge updates + * + * 180 6/29/98 12:34p Chris + * Fighting robots get some notification of hostile fire + * + * 179 6/29/98 10:20a Chris + * Someone changed the way ObjGet works. + * + * 178 6/26/98 4:57p Chris + * Less flinching + * + * 177 6/26/98 4:03p Chris + * Added support for AI's changing targets -- VERY NOT DONE + * + * 176 6/26/98 2:52p Chris + * AI now reports when it updates its orientation + * + * 175 6/25/98 3:40p Chris + * Made AINotify work correctly (i.e. not at all) on clients + * + * 174 6/25/98 11:02a Chris + * Made clients bail. + * + * 173 6/15/98 6:29p Chris + * Added FQ_NO_RELINK to the homing code + * + * 172 6/15/98 5:45p Chris + * + * 171 6/15/98 5:18p Chris + * Added version 1 of the multiple ground point code + * + * 170 6/15/98 3:23p Chris + * Single point walker update + * + * 169 6/03/98 6:42p Chris + * Added multipoint collision detection an Assert on invalid (infinite + * endpoint). + * + * 168 6/01/98 9:24p Chris + * Improved the code for AIs that are off + * + * 167 5/27/98 5:54p Chris + * If AI is off, no notifies are received + * + * 166 5/27/98 3:19p Chris + * Made disabled AIs ignore dodge notifications + * + * 165 5/26/98 7:57p Chris + * Worked on the avoid hack + * + * 164 5/26/98 7:41p Chris + * Made hoppers not converge + * + * 163 5/26/98 5:04p Chris + * rev .2 of walking + * + * 162 5/26/98 4:39p Chris + * rev. .1 of walker code + * + * 161 5/26/98 4:10p Chris + * Removed a hack + * + * 160 5/26/98 2:49p Chris + * More fixes + * + * 159 5/26/98 2:26p Chris + * Fixed the circle distance bug in AIGoalMoveRelativeObjectVec + * + * 158 5/26/98 10:03a Chris + * move relative object and orient to vel where causing problems with each + * other. I found a temp solution + * + * 157 5/26/98 9:45a Chris + * DIstance and vec_to_target are independant + * + * 156 5/26/98 9:34a Chris + * Added XZ distances for circle dist. :) + * + * 155 5/22/98 6:50p Chris + * Improving the melee attacks + * + * 154 5/22/98 6:45p Chris + * Fixed a bug with flinching and melee attacks + * + * 153 5/22/98 6:28p Chris + * Working on melee attacks + * + * 152 5/22/98 6:22p Chris + * Improved Dynamic path allocation + * + * 151 5/22/98 4:44p Chris + * Added better melee hit prediction + * + * 150 5/22/98 11:59a Chris + * Fixed improper uses of FindSoundName and made the proper static sounds + * + * 149 5/20/98 5:12p Chris + * Print the notify number a robot gets when it is invalid + * + * 148 5/20/98 10:22a Chris + * Improved targetting stuff + * + * 147 5/20/98 10:19a Chris + * Fixed some bugs with status_reg's and circle distance + * + * 146 5/19/98 2:53p Chris + * CreateObject in OSIRIS works outside now. + * + * 145 5/18/98 8:06p Chris + * Made melee attacks closer to what I want in terms of functionality + * + * 144 5/18/98 12:40p Chris + * Added a new flag (F_BLINE_IF_SEE_GOAL) + * + * 143 5/18/98 11:16a Chris + * Fixed a problem with the GetToObj goal (an if statement was f*cked up) + * + * 142 5/17/98 9:07p Chris + * Fixed melee attacks. Thanks to Ala. :) + * + * 141 5/17/98 8:05p Chris + * + * 140 5/17/98 7:54p Chris + * Correctly integrated the goal system and circle distance stuff. Added + * support for "target goals". + * + * 139 5/15/98 2:58p Chris + * More on the big AI update + * + * 138 5/14/98 3:01p Chris + * More new AI code + * + * 137 5/14/98 12:21p Chris + * Updating AI system... Incomplete + * + * 136 5/12/98 3:46p Chris + * Added some notify/goal system stuff and orientation to velocity + * + * 135 5/11/98 4:39p Chris + * Improved AI system (goals have notifies and are more flexable). + * + * 134 5/04/98 4:20p Chris + * Energy Drain + * + * 133 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 132 5/04/98 12:22p Chris + * Hacked some buddy bot code + * + * 131 5/04/98 11:00a Chris + * Hacks + * + * 130 5/03/98 7:02p Chris + * Improved sound support for AI system + * + * 129 5/03/98 6:03p Chris + * Added sound support to the animation system + * + * 128 5/03/98 5:38p Chris + * Added sounds to anim page + * + * 127 5/01/98 7:53p Chris + * + * 126 5/01/98 7:52p Chris + * Turn toward player - not last see position + * + * 125 5/01/98 3:41p Chris + * + * 124 5/31/98 3:03p Chris + * Added death animations + * + * 123 4/30/98 2:03p Chris + * Fixed the robot jiggyness problem. :) + * + * 122 4/30/98 11:31a Chris + * Massive upgrades to the AI system + * + * 121 4/22/98 9:43p Chris + * More AI improvements + * + * 120 4/22/98 4:15p Chris + * Improved DebugBlockPrint + * + * 119 4/22/98 3:50p Chris + * Added DebugBlockPrint + * + * 118 4/22/98 1:15p Chris + * Incremental work on paths and goals + * + * 117 4/21/98 11:25a Chris + * Improving GET_TO_OBJ goal + * + * 116 4/20/98 12:27p Chris + * Made AIG_GET_TO_OBJECT work. + * + * 115 4/19/98 9:56p Chris + * AI system is integrated with OSIRIS. Path system is integrated with + * the AI system. Bugs will ensue. + * + * 114 4/17/98 1:59p Jason + * added cool object effects + * + * 113 4/16/98 2:56p Chris + * Updated buddy support and intergrated scripting and AI + * + * 112 4/15/98 5:56p Chris + * Made the AI system script friendly + * + * 111 4/14/98 3:20p Chris + * Made quirks happen more often + * + * 110 4/14/98 11:42a Chris + * Added quirks and a better way of getting from idle to alert and from + * alert to idle. + * + * 109 4/13/98 7:38p Chris + * Added more support for a 'real' AI. + * + * 108 4/13/98 6:54p Chris + * Improved the animation system. + * + * 107 4/13/98 2:20p Chris + * IDLe works (kindof) + * + * 106 4/07/98 4:25p Chris + * Added support for buddy bot + * + * 105 3/31/98 4:23p Chris + * Added a new AIpath system + * + * 104 3/25/98 5:51p Chris + * Added full model/body animations for weapon firing + * + * 103 3/25/98 11:02a Chris + * version 1.0 of the new AI ranged firing code. + * + * 102 3/24/98 3:20p Chris + * Improved local wb anim system + * + * 101 3/23/98 6:16p Chris + * added some soar_helpers and some debug mprintf's + * + * 100 3/23/98 10:01a Chris + * Added independant wb animations + * + * 99 3/17/98 11:27a Chris + * Added object bump notifies for AI + * + * 98 3/13/98 5:55p Chris + * Fixed problems with animations + * + * 97 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 96 3/12/98 5:48p Chris + * Fixed a bug in the animation cycle times + * + * 95 3/10/98 2:15p Chris + * Made turrets with a zero angle fov usable to join sub-objects (for fire + * animations) + * + * 94 3/10/98 12:51p Chris + * Added a truely D2 style homing missile + * + * 93 3/05/98 11:34a Chris + * Added the move_behind_object function + * + * 92 3/03/98 5:02p Chris + * Added a status register to the ai_frame. Also, I enabled a bunch of + * the fields from the AI Dialog. In addiition, I tweaked how melee + * attacks work and animate. + * + * 91 3/02/98 6:52p Chris + * Changed melee_dist to melee_damage + * + * 90 2/27/98 7:22p Chris + * Started to add new AI fields -- no way near complete + * + * 89 2/20/98 3:24p Chris + * + * 88 2/19/98 6:16p Chris + * Working on AI + * + * 87 2/18/98 8:42p Chris + * Improving on the big object problem + * + * 86 2/18/98 4:47a Chris + * + * 85 2/18/98 4:38a Chris + * + * 84 2/18/98 4:37a Chris + * + * 83 2/18/98 4:18a Chris + * + * 82 2/18/98 4:13a Chris + * + * 81 2/18/98 4:05a Chris + * + * 80 2/17/98 11:31p Chris + * + * 79 2/17/98 4:24p Chris + * Allow for simple walking robots + * + * 78 2/17/98 1:34p Chris + * Improved timing of tear sound for melee + * + * 77 2/17/98 1:17p Chris + * Improving the melee robots + * + * 76 2/17/98 1:05p Chris + * Added in melee hit sound and effect + * + * 75 2/16/98 11:19p Chris + * Added support for melee robots + * + * 74 2/16/98 4:56p Chris + * Added melee attack support + * + * 73 2/16/98 3:50p Chris + * Robots take melee swipes + * + * 72 2/16/98 1:05p Chris + * Added some Flinch support + * + * 71 2/16/98 2:45a Chris + * Massive improvements to the animation system and the AI + * + * 70 2/11/98 11:39a Chris + * + * 69 2/11/98 11:38a Chris + * Added additional support to the beginning points on paths + * + * 68 2/11/98 11:06a Chris + * Fixed the orient to node code. Thanks be to AI. + * + * 67 2/10/98 5:48p Chris + * Improving the heuristic for when we update_next_path nodes. + * + * 66 2/10/98 4:45p Chris + * Incremental changes: Made a new node_orient heuristic. Removed some + * bad fields from the ai_info struct. + * + * 65 2/10/98 12:09a Chris + * Improving the awareness system. Re-enabled super dodging. Added the + * auto-fluctuate speed flag (allows some diversity). Enabled FOV + * information. + * + * 64 2/06/98 5:51p Chris + * Added the ability turn robots on and off + * + * 63 2/06/98 4:19p Chris + * Added a boat load of path stuff + * + * 62 2/06/98 2:15a Chris + * Activated the max_velocity, max_delta_velocity, and max_turn_rate + * fields for AI objects. + * + * 61 2/05/98 6:55p Chris + * Align orientation to path + * + * 60 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 59 2/03/98 6:33p Chris + * Improving the path system + * + * 58 2/03/98 3:35p Chris + * Added path support for AI and OSIRIS + * + * 57 2/02/98 8:15p Chris + * Updated the AI system + * + * 56 2/02/98 11:06a Chris + * + * 55 1/30/98 4:09p Chris + * + * 54 1/30/98 4:07p Chris + * + * 53 1/30/98 3:42p Chris + * Removed a bad memset (it zerod out static values) + * + * 52 1/30/98 2:19p Chris + * More improvements (target vis stuff and setting of the AIN_ALWAYS_ON is + * now done correctly) + * + * 51 1/29/98 3:29p Chris + * Major update to the AI system. + * + * 50 1/21/98 3:54p Chris + * + * 49 1/20/98 4:40p Chris + * Fixed some visability stuff. + * + * 48 1/20/98 12:21p Chris + * Removed some printf's + * + * 47 1/20/98 11:34a Chris + * Fixed problems with player visability and thus problems with awareness + * + * 46 1/19/98 10:04a Matt + * Added new object handle system + * + * 45 1/14/98 7:58p Chris + * + * 44 12/09/97 2:48p Chris + * Temporary less dodgy bots + * + * 43 11/24/97 4:12p Chris + * Cleaner code + * + * 42 11/24/97 10:17a Chris + * Handles dead, dying, and cloaked players + * + * 41 11/21/97 3:05p Chris + * Robots will not fired at the player after he is dead. + * + * 40 11/21/97 1:14p Chris + * Improved hueristic for robot firing. + * + * 39 11/17/97 9:48p Chris + * Added some guarding + * + * 38 11/17/97 6:01p Chris + * thief update + * + * 37 11/17/97 5:59p Chris + * Allowed some thief stuff + * + * 36 11/17/97 5:46p Chris + * Added some support for a Theif-like robot + * + * 35 11/15/97 6:39p Chris + * Made robot firing signifantly less CPU costly. But, turrets are now + * more choppy moving. + * + * 34 11/14/97 11:55p Chris + * Dodge goals are semi-functional + * + * 33 11/13/97 3:56p Chris + * Evading a little more random + * + * 32 11/13/97 3:34p Chris + * Improved AI + * + * 31 11/12/97 5:47p Chris + * Incremental improvements + * + * 30 11/12/97 10:43a Chris + * Incremental + * + * 29 11/11/97 12:22p Chris + * Incremental improvements + * + * 28 11/06/97 12:47p Chris + * Some new incremental stuff + * + * 27 11/04/97 6:20p Chris + * Added some incremental improvements + * + * 26 10/28/97 12:25p Chris + * Added support for initial class, type, and movement types. Starting to + * flush out the AI structure + * + * 25 9/23/97 5:47p Jason + * return if "animation system not done" message is printed + * + * 24 9/18/97 1:27p Matt + * Cleaned up object struct + * + * 23 9/17/97 11:31a Chris + * + * 22 9/15/97 4:07p Chris + * AI only fires if you are in the same render as they are. + * + * 21 9/10/97 1:24p Chris + * Fixed a bug with the wb firing masks + * + * 20 9/10/97 12:56p Chris + * Added more support for weapon batteries + * + * 19 9/08/97 11:50a Chris + * Added support for entering seconds-per-cycle animation information + * + * 18 9/05/97 8:52p Chris + * Furthered the weapon bank support + * + * 17 9/04/97 3:50p Chris + * Added new turret support + * + * 16 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 15 8/20/97 3:13p Chris + * + * 14 8/11/97 1:53p Matt + * Ripped out robot & powerup pages, and added generic page + * + * 13 8/06/97 1:35p Matt + * Changes for new generic object_info struct which replace robot-specific + * structure + * + * 12 8/05/97 12:29p Chris + * + * 11 7/30/97 4:10p Chris + * Made test bot more interesting. + * + * 10 7/30/97 1:31p Chris + * Made helicopters slightly more interesting. + * + * 9 7/29/97 12:20p Chris + * Incremental improvements. Fixed a memory bug. + * + * 8 7/28/97 1:19p Chris + * Expanding the AI system + * + * 7 7/16/97 5:15p Chris + * Fixed use of updating 3d sounds + * + * 6 7/15/97 7:29p Chris + * Added a sound for helicopter blades. + * + * 5 7/15/97 5:35p Chris + * New AI code + * + * 4 7/15/97 4:59p Chris + * added support for AI and animations +* +* $NoKeywords: $ +*/ + +#include +#include "AIMain.h" +#include "mono.h" +#include "game.h" +#include "weapon.h" +#include "findintersection.h" +#include "vecmat.h" +#include "AIGoal.h" +#include "terrain.h" +#include "hlsoundlib.h" +#include "sounds.h" +#include "aiterrain.h" +#include "weapon.h" +#include "objinfo.h" +#include "polymodel.h" +#include "robotfire.h" +#include "BOA.h" +#include "player.h" +#include "memory.h" +#include "gamepath.h" +#include "soundload.h" +#include "damage.h" +#include "aipath.h" +#include "robot.h" +#include "attach.h" +#include "demofile.h" +#include "matcen.h" +#include "PHYSICS.H" +#include "difficulty.h" +#include "osiris_dll.h" +#include "multi.h" +#include "gamecinematics.h" +#include "room.h" +#include "psrand.h" +#include "gametexture.h" +#include "difficulty.h" + +// Define's +#define MAX_SEE_TARGET_DIST 500.0f +#define MAX_TRACK_TARGET_DIST 800.0f +#define MIN_VIS_RECENT_CHECK_INTERVAL 0.35f +#define MIN_VIS_CHECK_INTERVAL 0.15f +#define CHECK_VIS_INFREQUENTLY_TIME 7.0f +#define CHECK_VIS_INFREQUENTLY_INTERVAL 2.0f + +#define MIN_TARGET_UPDATE_INTERVAL 2.0f +#define MAX_TARGET_UPDATE_INTERVAL 4.5f + +float AI_last_time_room_noise_alert_time[MAX_ROOMS+8]; +int AI_unique_goal_id = 0; + +#ifdef _DEBUG +bool AI_debug_robot_do = false; +int AI_debug_robot_index = -2; +#endif + +bool compute_dodge_dir(/* vector *dodge_dir, */object *obj, object *dodge_obj); + +// chrishack -- AI problems + +// first frame problems: +// If a robot has an initial velocity it might fly off because of excessive Frametime of the first frame +// If a player fires on the first frame, robots will not know + +// Inside walkers will suck resources + +extern char *Ai_class_strings[MAX_AI_INIT_CLASSES] = +{ +"Static", +"Pure Path", +"Fully AIS" +}; + +extern char *Ai_type_strings[MAX_AI_INIT_TYPES] = +{ +"Fly Lander", +"Stalker", +"Evader 1", +"Evader 2", +"Stationary Turret", +"AIS", +"Melee1", +"Bird Flock 1", +"Herd 1" +}; + +extern char *Ai_movement_type_strings[MAX_AI_INIT_MOVEMENT_TYPES] = +{ +"Standing", +"Flying", +"Rolling", +"Walking" +}; + +// AI Movement flying types +extern char *Ai_movement_subtype_flying_strings[MAX_AI_INIT_MOVEMENT_SUBTYPES] = +{ +"Normal", +"Path", +"Helicopter", +"Hovercraft", +"Jet", +"Player", +"Buddy" +}; + +// AI Movement walking types + +extern char *Ai_movement_subtype_walking_strings[MAX_AI_INIT_MOVEMENT_SUBTYPES] = +{ +"Restricted Flat", +"Restricted Low-angle", +"Restricted High_angle", +"Non-restricted", +"Water Only", +"", +"" +}; + +#define AI_MAX_MELEE_RANGE 5.0f + +int AI_NumRendered; +int AI_RenderedList[MAX_OBJECTS]; + +int AI_NumHostileAlert = 0; + +int Buddy_handle[MAX_PLAYERS]; + +int AI_FriendNumNear = 0; // Number of friends found +object *AI_FriendObj[2]; // Friend objects +float AI_FriendDist[2]; // Distances to the friends +vector AI_FriendDir[2]; // Direction to the friends +int AI_EnemyNumNear = 0; // Number of enemies found +object *AI_EnemyObj[2]; // Enemy objects +float AI_EnemyDist[2]; // Distances to the enemies +vector AI_EnemyDir[2]; // Direction to the enemies + +#define AIVIS_NONE 0.0f +#define AIVIS_BARELY 1.0f +#define AIVIS_MOSTLY 2.0f +#define AIVIS_FULL 3.0f + +// Allows the vis sliders to work +float AIDetermineObjVisLevel(object *obj, object *target) +{ + float vis_level = AIVIS_FULL; + + if(target == NULL) + return AIVIS_NONE; + + if((target->effect_info) && (target->effect_info->type_flags & EF_CLOAKED)) + { + vis_level = AIVIS_NONE; + + if(target->type == OBJ_PLAYER && (Players[target->id].flags & PLAYER_FLAGS_AFTERBURN_ON)) + { + vis_level += 1.0f; + } + + if(target->type == OBJ_PLAYER && (Players[target->id].flags & PLAYER_FLAGS_HEADLIGHT)) + { + vector from_target = obj->pos - target->pos; + vm_NormalizeVector(&from_target); + + if((target->orient.fvec) * (from_target) > 0.965f) + { + vis_level += 1.0f; + } + } + } + + if((target->effect_info) && (target->effect_info->type_flags & EF_NAPALMED)) + { + vis_level += 1.75; + } + + if(vis_level < AIVIS_NONE) + vis_level = AIVIS_NONE; + else if(vis_level > AIVIS_FULL) + vis_level = AIVIS_FULL; + + return vis_level; +} + +inline bool ai_target_valid(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + bool f_valid = false; + + if(ai_info->target_handle != OBJECT_HANDLE_NONE) + { + if(ObjGet(ai_info->target_handle)) + f_valid = true; + else + { + AINotify(obj, AIN_TARGET_DIED, (void *)&ai_info->target_handle); + ai_info->target_handle = OBJECT_HANDLE_NONE; + } + } + + return f_valid; +} + +bool AISetTarget(object *obj, int handle) +{ + ai_frame *ai_info = obj->ai_info; + + ai_info->target_handle = handle; + if(handle == OBJECT_HANDLE_NONE) + ai_info->last_see_target_pos = obj->pos; + + return ai_target_valid(obj); +} + +// Note ASSUMES that dir is normalized +void AIMoveTowardsDir(object *obj, vector *dir, float scale) +{ + physics_info *phys_info = &obj->mtype.phys_info; + vector goal_velocity; + vector vel_diff; + float delta_vel; + float max_delta_vel; + ai_frame *ai_info = obj->ai_info; + float acc_scale; + + if(scale < 1.0f) + acc_scale = 1.0f; + else + acc_scale = scale; + + goal_velocity = *dir * ai_info->max_velocity * scale; + + vel_diff = goal_velocity - phys_info->velocity; + delta_vel = vm_NormalizeVector(&vel_diff); + + max_delta_vel = Frametime * ai_info->max_delta_velocity * acc_scale; + + if(delta_vel > max_delta_vel) + { + phys_info->velocity += (vel_diff * max_delta_vel); + } + else + { + phys_info->velocity = goal_velocity; + } +} + +bool AIMoveTowardsPosition(object *obj, /*velocity *new_vel,*/ vector *pos, float scale, bool stop_at_end_point, vector *mdir, bool *f_moved) +{ + vector dir; + float distance; + ai_frame *ai_info = obj->ai_info; + float acc_scale; + + if(scale < 1.0f) + acc_scale = 1.0f; + else + acc_scale = scale; + + if(stop_at_end_point) + { + if(vm_VectorDistance(pos, &obj->pos) <= .2f) + { + obj->mtype.phys_info.velocity = Zero_vector; + *f_moved = true; + return true; + } + + float s_to_stop = (ai_info->max_velocity * scale) / (ai_info->max_delta_velocity * acc_scale); + float d_to_stop = ai_info->max_velocity * scale * s_to_stop; + + dir = *pos - obj->pos; + float d_to_target = vm_NormalizeVector(&dir); + if(d_to_target < d_to_stop) + { + float speed = ai_info->max_velocity * scale * (d_to_target/d_to_stop); + + if(d_to_target <= speed * Frametime) + { + obj->mtype.phys_info.velocity = ((d_to_target/Frametime) + .09f) * dir; + } + else + { + obj->mtype.phys_info.velocity = dir * (speed + .1f); + } + + *f_moved = true; + return false; + } + } + + if(*pos == obj->pos) + { + physics_info *phys_info = &obj->mtype.phys_info; + + if(phys_info->velocity != Zero_vector) + { + vector vel_diff = -phys_info->velocity; + float delta_vel = vm_NormalizeVector(&vel_diff); + float max_delta_vel = Frametime * ai_info->max_delta_velocity * acc_scale; + + if(delta_vel > max_delta_vel) + { + phys_info->velocity += (vel_diff * max_delta_vel); + } + else + { + vm_MakeZero(&phys_info->velocity); + } + } + + *f_moved = true; + return false; + } + else + { + dir = *pos - obj->pos; + distance = vm_NormalizeVector(&dir); + } + + *mdir = dir; + *f_moved = false; + +// AIMoveTowardsDir(obj, &dir); + return false; +} + +bool move_relative_object_vec(object *obj, vector *vec, object *target, float circle_dist, float scalar, bool f_toward, vector *mdir, bool *f_moved) +{ + // Getting behind an object is a 2 step process: Get to the side and then get behind. + vector from_target; + vector opposite_fvec; + vector goal_pos; + ai_frame *ai_info = obj->ai_info; + bool f_done = false; + + from_target = obj->pos - target->pos; + + if(f_toward) + { + opposite_fvec = -(*vec); + } + else + { + opposite_fvec = *vec; + } + + if(from_target * opposite_fvec > 0.0f) + { + // I am currently on the side of the object that I do not want to be on + vector goal_dir; + vector vec_to_plane; + vector normal_component; + vector plane_component; + + vec_to_plane = obj->pos - target->pos; + normal_component = opposite_fvec * (opposite_fvec * vec_to_plane); + plane_component = vec_to_plane - normal_component; + + if(plane_component == Zero_vector) + { + goal_dir = target->orient.rvec; + } + else + { + goal_dir = plane_component; + vm_NormalizeVector(&goal_dir); + } + + goal_pos = target->pos + goal_dir * (obj->size + target->size + circle_dist); + } + else + { + // else, I am going to get behind/in-front the object + goal_pos = target->pos - (opposite_fvec * (obj->size + target->size + circle_dist)); + if(vm_VectorDistance(&goal_pos, &obj->pos) < obj->size) + { + f_done = true; + } + } + + AIMoveTowardsPosition(obj, &goal_pos, scalar, false, mdir, f_moved); + return f_done; +} + +void move_away_from_position(object *obj, vector *pos/*, bool random_evade*/, float scale, vector *mdir, bool *f_moved) +{ + vector dir; + float distance; + + // Reverse of move towards + dir = obj->pos - *pos; + distance = vm_NormalizeVector(&dir); + +// AIMoveTowardsDir(obj, &dir, scale); + *mdir = dir; + *f_moved = false; +} + +#define NO_DODGE_SIZE_MULTIPLIER 2.0f +#define MIN_DODGE_DIST 5.0f + +#define MAX_DODGE_INFLUENCE 10.0f +#define MIN_DODGE_INFLUENCE 2.5f // Just less than max avoiding fiends + +// returns false if no dodge is necessary +bool compute_dodge_dir(vector *movement_dir, object *obj, object *dodge_obj) +{ + vector vec_to_obj = obj->pos - dodge_obj->pos; + vector dodge_vec; + vector dobj_motion = dodge_obj->mtype.phys_info.velocity; + vm_NormalizeVector(&dobj_motion); + + float closest_dist; + + float p; + + float max_dodge_dist = obj->size * NO_DODGE_SIZE_MULTIPLIER + dodge_obj->size; + if(max_dodge_dist < MIN_DODGE_DIST) + { + max_dodge_dist = MIN_DODGE_DIST; + } + + if(IS_GUIDEBOT(obj)) + { + max_dodge_dist += 10.0f; + } + + vector dpoint; + + p = dobj_motion * vec_to_obj; + + if(p <= 0.0) return false; + + dpoint = dodge_obj->pos + (p * dobj_motion); + + dodge_vec = obj->pos - dpoint; + closest_dist = vm_NormalizeVector(&dodge_vec); + + if(closest_dist > max_dodge_dist) return false; + if(closest_dist == 0.0) dodge_vec = obj->orient.rvec; + + // CHRISHACK -- Use the MIN/MAX INFLUENCE and INFLUENCE RAMP + float scale = MAX_DODGE_INFLUENCE*((max_dodge_dist - closest_dist)/max_dodge_dist); + if(scale < MIN_DODGE_INFLUENCE) + { + scale = MIN_DODGE_INFLUENCE; + } + + if(IS_GUIDEBOT(obj)) + { + scale *= 20.0f; + } + + dodge_vec *= scale; + + if(dodge_vec != Zero_vector && (obj->ai_info->dodge_till_time < Gametime || vm_GetMagnitude(&dodge_vec) > vm_GetMagnitude(&obj->ai_info->last_dodge_dir))) + { + obj->ai_info->last_dodge_dir = dodge_vec; + } + + obj->ai_info->dodge_till_time = Gametime + ((float)ps_rand()/(float)RAND_MAX) * ( 3.0f * obj->ai_info->life_preservation) + 1.0f; + + *movement_dir += dodge_vec; + + return true; +} + + +bool goal_do_dodge(object *obj, int goal_index) +{ + ai_frame *ai_info = obj->ai_info; + object *other_obj = ObjGet(ai_info->goals[goal_index].g_info.handle); + int reason = AIN_GOAL_COMPLETE; + + if (other_obj) + { + if(compute_dodge_dir(&ai_info->movement_dir, obj, other_obj)) + { + return true; + } + } + else + { + reason = AIN_GOAL_INVALID; + } + + GoalClearGoal(obj, &ai_info->goals[goal_index], reason); + return false; +} + +extern uint check_point_to_face(vector *colp, vector* face_normal,int nv,vector **vertex_ptr_list); + +#define MAX_WALL_AVOID_INFLUENCE 0.9f +#define MAX_TERRAIN_AVOID_INFLUENCE 0.9f +#define GB_WALL_PULSE_INTERVAL 7 + +bool goal_do_avoid_walls(object *obj, vector *mdir) +{ + fvi_face_room_list facelist[200]; + int num_faces; + float rad; + int i; + vector awall_dir = Zero_vector; + float closest_dist; + vector pos; + bool f_danger = false; + + float wall_size = Poly_models[obj->rtype.pobj_info.model_num].wall_size; + + if(IS_GUIDEBOT(obj)) + { + rad = 10.0f; + } + else + { + if(wall_size < 7.0f) + rad = wall_size + 2.2f; + else + rad = wall_size + 1.0f; + } + + pos = obj->pos + obj->wall_sphere_offset; + + closest_dist = rad + 1.0f; + + if(obj->mtype.phys_info.velocity == Zero_vector) + { + return false; + } + + if(!ROOMNUM_OUTSIDE(obj->roomnum)) + { + num_faces = fvi_QuickDistFaceList(obj->roomnum, &pos, rad, facelist, 200); + + for(i = 0; i < num_faces; i++) + { + room *rp = &Rooms[facelist[i].room_index]; + face *fp = &rp->faces[facelist[i].face_index]; + + int face_info = GetFacePhysicsFlags(rp, fp); + if(!(face_info & FPF_SOLID)) + continue; + + vector fpnt = rp->verts[fp->face_verts[0]]; + + // Ignore backfaces + if((pos - fpnt) * fp->normal > 0.0f) + { + float dist = vm_DistToPlane(&pos, &fp->normal, &fpnt); + + if(dist < rad) + { + vector pnt_on_face = pos - (dist * fp->normal); + vector *vertex_ptr_list[MAX_VERTS_PER_FACE]; + int j; + + for (j = 0; j < fp->num_verts; j++) + { + vertex_ptr_list[j] = &rp->verts[fp->face_verts[j]]; + } + + int edgemask = check_point_to_face(&pnt_on_face, &fp->normal, fp->num_verts, vertex_ptr_list); + + if(edgemask == 0) + { + float scale = 1.0f - ((dist - wall_size*.5f)/(rad - wall_size*.5f)); + if(scale < 0.0) + scale = 0.0f; + else if(scale > 1.0f) + scale = 1.0f; + + awall_dir += (scale * fp->normal); + +// mprintf((0, "%d\n", facelist[i].face_index)); + if(GameTextures[fp->tmap].flags & (TF_VOLATILE | TF_FORCEFIELD | TF_LAVA)) + { + if(!(fp->portal_num >= 0 && !(rp->portals[fp->portal_num].flags & PF_RENDER_FACES))) + { + f_danger = true; + } + } + +// mprintf((0, "Dist %f %f (%d, %d)\n", dist, scale, facelist[i].room_index, facelist[i].face_index)); + + if(dist < closest_dist) + { + closest_dist = dist; + } + } + } + } + } + } + else + { + int num_cells; + int cell_list[100]; + + num_cells = fvi_QuickDistCellList(CELLNUM(obj->roomnum), &pos, rad, cell_list, 100); + + for(i = 0; i < num_cells; i++) + { + const int cur_node = cell_list[i]; + terrain_segment *tseg = &Terrain_seg[cur_node]; + + vector fpnt; + ComputeTerrainSegmentCenter(&fpnt, cur_node); + + vector normal = TerrainNormals[MAX_TERRAIN_LOD-1][cur_node].normal1 + TerrainNormals[MAX_TERRAIN_LOD-1][cur_node].normal2; + vm_NormalizeVector(&normal); + + if(obj->mtype.phys_info.velocity * normal <= 0.0f) + { + vector no_y_vec = pos - fpnt; + no_y_vec.y = 0.0f; + + float dist; + + dist = vm_NormalizeVector(&no_y_vec); + + if(dist <= rad) + { + awall_dir += ((1.0f - (dist/rad)) * normal); + + if(GameTextures[Terrain_tex_seg[tseg->texseg_index].tex_index].flags & (TF_VOLATILE | TF_FORCEFIELD | TF_LAVA)) + { + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Danger - NEAR FORCEFIELD, VOLATILE, OR LAVA\n")); + } + #endif + f_danger = true; + } + + if(dist < closest_dist) + { + closest_dist = dist; + } + } + } + } + } + + if(closest_dist <= rad) + { + vm_NormalizeVector(&awall_dir); + + float max_influence = (ROOMNUM_OUTSIDE(obj->roomnum))?MAX_TERRAIN_AVOID_INFLUENCE:MAX_WALL_AVOID_INFLUENCE; + if(f_danger) + { + max_influence *= 7.1f; + } + + float scale = (1.0f - ((closest_dist - wall_size)/(rad - wall_size))) * max_influence; + if(scale > max_influence) + scale = max_influence; + *mdir += scale * awall_dir; + + if(IS_GUIDEBOT(obj) && closest_dist < 8.0f) + { + int test_time = Gametime; + + if((test_time%GB_WALL_PULSE_INTERVAL) == 0) + { + *mdir *= 10.0f; + } + } + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Avoid wall %f\n", scale)); + } + #endif + + return true; + } + + return false; +} + +#define ONE_DEGREE_ARC_COS 0.99985f +#define FIVE_DEGREE_ARC_COS 0.9962f + +// NOTE: Assumes that the vector is normalized +void AITurnTowardsDir(object *obj, /*velocity *new_vel,*/ vector *goal_dir/*, bool remain_level*/, float turn_rate) +{ + vector u_axis; + matrix rot_matrix; + angle goal_angle; + float max_angle; + + matrix saved_orient = obj->orient; + vector saved_uvec = obj->orient.uvec; + + if(*goal_dir == Zero_vector || (obj->orient.fvec * (*goal_dir)) >= ONE_DEGREE_ARC_COS) + return; // No goal_dir or less than 1 degree off goal + + if(obj->size > 32.0f && (obj->orient.fvec * (*goal_dir)) >= FIVE_DEGREE_ARC_COS) + return; // Big objects have more play + + if(obj->type != OBJ_WEAPON) + { + obj->ai_info->flags |= AIF_REPORT_NEW_ORIENT; + if(obj->ai_info) + obj->ai_info->saved_orient = obj->orient; + } + + goal_angle = vm_DeltaAngVecNorm(&obj->orient.fvec, goal_dir, &u_axis); + + if(goal_angle == 0) return; + + max_angle = turn_rate * Frametime; + + if((float)goal_angle > max_angle) + { + matrix turn_matrix; + + if(max_angle != 32767 && max_angle != 32768) + { + // Get the up axis + vm_CrossProduct (&u_axis, &obj->orient.fvec, goal_dir); + + // Using the forward(original orient's forward) and the up (computed), get the orientation matrix + vm_VectorToMatrix(&rot_matrix, &obj->orient.fvec, &u_axis, NULL); + } + else + { + rot_matrix = obj->orient; + } + + vm_AnglesToMatrix(&turn_matrix, 0.0, max_angle, 0.0); + + rot_matrix = rot_matrix * turn_matrix; + + obj->orient.fvec = rot_matrix.fvec; // ObjSetOrient below + } + else + { + obj->orient.fvec = *goal_dir; // ObjSetOrient below + } + + if(obj->movement_type == MT_WALKING) + { + obj->orient.uvec = saved_uvec; + + float f_proj = obj->orient.uvec * obj->orient.fvec; + float r_proj = obj->orient.uvec * obj->orient.rvec; + + if(f_proj <= -1.0f || f_proj >= 1.0f || r_proj >= 1.0f || r_proj <= -1.0f) + { + obj->orient = saved_orient; + } + else + { + obj->orient.fvec -= (obj->orient.uvec * f_proj); + obj->orient.rvec -= (obj->orient.uvec * r_proj); + + vm_NormalizeVector(&obj->orient.fvec); + vm_NormalizeVector(&obj->orient.rvec); + } + } + + vm_Orthogonalize(&obj->orient); + ObjSetOrient(obj, &obj->orient); +} + +bool AITurnTowardsMatrix(object *obj, float turn_rate, matrix *g_orient) +{ + float max_angles = turn_rate * Frametime; + + matrix t_s_matrix = obj->orient; + vm_TransposeMatrix(&t_s_matrix); + + matrix rot_matrix; + + rot_matrix = t_s_matrix * *g_orient; + + angvec a; + vm_ExtractAnglesFromMatrix(&a, &rot_matrix); + vector dist; + + if(a.b > 32768) + dist.x = 65536 - a.b; + else + dist.x = a.b; + + if(a.h > 32768) + dist.y = 65536 - a.h; + else + dist.y = a.h; + + if(a.p > 32768) + dist.z = 65536 - a.p; + else + dist.z = a.p; + + float angles = vm_GetMagnitude(&dist); + + if(angles <= max_angles) + { + obj->orient = *g_orient; + vm_Orthogonalize(&obj->orient); + ObjSetOrient(obj, &obj->orient); + return true; + } + + float scale = max_angles/angles; + dist *= scale; + + if(a.b > 32768) + a.b = 65535 - dist.x; + else + a.b = dist.x; + + if(a.h > 32768) + a.h = 65535 - dist.y; + else + a.h = dist.y; + + if(a.p > 32768) + a.p = 65535 - dist.z; + else + a.p = dist.z; + + vm_AnglesToMatrix(&rot_matrix, a.p, a.h, a.b); + obj->orient *= rot_matrix; + vm_Orthogonalize(&obj->orient); + ObjSetOrient(obj, &obj->orient); + + return false; +} + +void AITurnTowardsPosition(object *obj, /*velocity *new_vel,*/ vector *pos/*, bool remain_level*/) +{ + vector goal_dir = *pos - obj->pos; + ai_frame *ai_info = obj->ai_info; + + // If we want to face ourselves, we are done. :) + float dist = vm_NormalizeVector(&goal_dir); + if(dist < .1f) + return; + + AITurnTowardsDir(obj, &goal_dir, ai_info->max_turn_rate); +} + +#define AI_AVE_MELEE_TIME .45f + +bool MeleeHitOk(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + vector intp; + float col_dist; + int num_attacks = 0; + float delay = 0.0f; + const int ai_movement = ai_info->movement_type; + polyobj_info *p_info = &obj->rtype.pobj_info; + float time_left; + + if(p_info->anim_end_frame != p_info->anim_start_frame) + { + float time_per_frame = p_info->anim_time / ((float)p_info->anim_end_frame - (float)p_info->anim_start_frame); + + time_left = time_per_frame * (p_info->anim_end_frame - p_info->anim_frame); + } + else + { + time_left = 0.0f; + } + + vector relative_vel; + vector g_end_pos; + + object *target = ObjGet(ai_info->target_handle); + + if(target == NULL || ai_info->dist_to_target_perceived > 100.0f) + { + return false; + } + + float dist = vm_VectorDistance(&obj->pos, &target->pos); + + if(dist <= AI_MAX_MELEE_RANGE) + { + return true; + } + + if(ai_info->flags & AIF_MELEE1) + { + num_attacks++; + delay += Object_info[obj->id].anim[ai_movement].elem[AS_MELEE1].spc; + } + + if(ai_info->flags & AIF_MELEE2) + { + num_attacks++; + delay += Object_info[obj->id].anim[ai_movement].elem[AS_MELEE2].spc; + } + + if(!num_attacks) + { + return false; + } + else if(num_attacks > 1) + { + delay = delay / (float) num_attacks; + } + + delay += time_left + Frametime; + + // Determines the relative velocities + if(obj->movement_type == MT_PHYSICS || obj->movement_type == MT_PHYSICS) + { + if(target->movement_type == MT_PHYSICS || target->movement_type == MT_PHYSICS) + { + relative_vel = obj->mtype.phys_info.velocity - target->mtype.phys_info.velocity; + } + else + { + relative_vel = obj->mtype.phys_info.velocity; + } + } + else + { + if(target->movement_type == MT_PHYSICS || target->movement_type == MT_PHYSICS) + { + relative_vel = -target->mtype.phys_info.velocity; + } + else + { + relative_vel = Zero_vector; + } + } + + if(obj->movement_type == MT_PHYSICS || obj->movement_type == MT_PHYSICS) + { + if((relative_vel * ai_info->vec_to_target_perceived) < 0.0f) + { + return false; + } + } + + g_end_pos = obj->pos + (delay * relative_vel); + + if(check_vector_to_sphere_1(&intp, &col_dist, &obj->pos, &g_end_pos, &target->pos, target->size + obj->size + (.9f * AI_MAX_MELEE_RANGE), false, true) != 0) + { + return true; + } + + return false; +} + +bool AiMelee(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + vector f_vec; + float dot; + + object *target = ObjGet(ai_info->target_handle); + + if(target == NULL) + { + ai_info->status_reg &= ~AISR_MELEE; + return false; + } + + if(((Gametime - ai_info->last_see_target_time) > 4.0 && ((Gametime - ai_info->last_hear_target_time) > 4.0)) || ai_info->awareness <= AWARE_BARELY) + { + ai_info->status_reg &= ~AISR_MELEE; + return false; + } + + if(ai_info->next_melee_time > Gametime) + { + ai_info->status_reg &= ~AISR_MELEE; + return false; + } + + if(ai_info->next_animation_type == AS_MELEE1 || ai_info->next_animation_type == AS_MELEE2) + { + ai_info->status_reg |= AISR_MELEE; + return false; + } + + if(!(ai_info->flags & (AIF_MELEE1 | AIF_MELEE2))) + { + ai_info->status_reg &= ~AISR_MELEE; + return false; + } + + // The AI wants to attack!!!!! + ai_info->status_reg |= AISR_MELEE; + + // Determine if we are in melee range + if(!MeleeHitOk(obj)) + { + return false; + } + + f_vec = obj->orient.fvec; + vm_NormalizeVector(&f_vec); + + dot = f_vec * ai_info->vec_to_target_perceived; + + if(dot >= 0.8f) + { + gi_fire attack_info; + int attack_num; + + if(!(ai_info->flags & AIF_MELEE1) || ((ai_info->flags & AIF_MELEE2) && (ps_rand() > (RAND_MAX >> 1)))) + { + attack_num = 1; + } + else + { + attack_num = 0; + } + + // mprintf((0, "Melee attack now!!!!\n")); + + attack_info.melee_number = attack_num; + if(GoalAddGoal(obj, AIG_DO_MELEE_ANIM, (void *)&attack_info ,ACTIVATION_BLEND_LEVEL) == 0) + return true; + } + + return false; +} + +void do_melee_attack(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + float damage; + notify melee_info; + int attack_num; + object *target = ObjGet(ai_info->target_handle); + + if(ai_info->animation_type == AS_MELEE1) + { + damage = ai_info->melee_damage[0]; + ai_info->next_melee_time = Gametime + ai_info->melee_latency[0]; + attack_num = 0; + } + else + { + damage = ai_info->melee_damage[1]; + ai_info->next_melee_time = Gametime + ai_info->melee_latency[1]; + attack_num = 1; + } + + melee_info.obj_handle = ai_info->target_handle; + melee_info.attack_num = attack_num; + + AINotify(obj, AIN_MELEE_ATTACK_FRAME, (void *)&melee_info); + + if(target) + { + float dist = ai_info->dist_to_target_actual; + + if(dist <= AI_MAX_MELEE_RANGE) + { + AINotify(obj, AIN_MELEE_HIT, (void *)&melee_info); + + if(damage >= 1.0f) + { + object *objptr = ObjGet(ai_info->target_handle); + + if(objptr) + { + if(ps_rand() > RAND_MAX/2) + { + Sound_system.Play3dSound(SOUND_MELEE_HIT_0, SND_PRIORITY_HIGHEST, objptr); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(SOUND_MELEE_HIT_0, OBJNUM(objptr), SND_PRIORITY_HIGHEST); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(SOUND_MELEE_HIT_0, OBJNUM(objptr), SND_PRIORITY_HIGHEST); + } + else + { + Sound_system.Play3dSound(SOUND_MELEE_HIT_1, SND_PRIORITY_HIGHEST, objptr); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(SOUND_MELEE_HIT_1, OBJNUM(objptr), SND_PRIORITY_HIGHEST); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(SOUND_MELEE_HIT_1, OBJNUM(objptr), SND_PRIORITY_HIGHEST); + } + + if(objptr->type == OBJ_PLAYER) + ApplyDamageToPlayer(objptr, obj, PD_MELEE_ATTACK, damage); + else + ApplyDamageToGeneric(objptr, obj, GD_MELEE_ATTACK, damage); + } + } + else if(damage == 0.0f) + { + float energy; + object *objptr = ObjGet(ai_info->target_handle); + + // chrishack - milestone - replace with real code + if(objptr) + { + Sound_system.Play3dSound(SOUND_ENERGY_DRAIN, SND_PRIORITY_HIGHEST, objptr); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(SOUND_ENERGY_DRAIN, OBJNUM(objptr), SND_PRIORITY_HIGHEST); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(SOUND_ENERGY_DRAIN, OBJNUM(objptr), SND_PRIORITY_HIGHEST); + + energy = (14 + ps_rand()%5) * Diff_general_inv_scalar[DIFF_LEVEL]; + + if(objptr->type == OBJ_PLAYER) + { + if(Players[objptr->id].energy < energy) + energy = Players[objptr->id].energy; + + DecreasePlayerEnergy(objptr->id, energy); + } + } + } + } + } +} + +void do_ranged_attack(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + int cur_wb = ai_info->current_wb_firing; + + //Fire Weapon + WBFireBattery(obj, &Object_info[obj->id].static_wb[cur_wb], cur_wb); + ai_info->status_reg &= ~AISR_RANGED_ATTACK; +} + +void AIUpdateAnim(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + polyobj_info *p_info = &obj->rtype.pobj_info; + int new_anim = ai_info->next_animation_type; + int ai_movement = ai_info->movement_type; + + if(ai_info->anim_sound_handle != 0) + Sound_system.StopSoundLooping(ai_info->anim_sound_handle); + + if(!Object_info[obj->id].anim) + return; + + if((Demo_flags == DF_PLAYBACK) || ((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT))) + { + p_info->anim_flags &= ~AIAF_NOTIFY; + + if(!(p_info->multi_anim_info.flags & FMA_VALID)) + { + mprintf((0, "Update Anim: Earily bail\n")); + return; + } + + custom_anim *multi_anim_info = &p_info->multi_anim_info; + + multi_anim_info->server_time = Gametime; + p_info->anim_frame = ((float)multi_anim_info->server_anim_frame)/256.0f; + + p_info->anim_start_frame = multi_anim_info->anim_start_frame; + p_info->anim_end_frame = multi_anim_info->anim_end_frame; + p_info->anim_time = multi_anim_info->anim_time; + p_info->max_speed = multi_anim_info->max_speed; + + p_info->anim_flags = 0; + + if(multi_anim_info->flags & FMA_LOOPING) + p_info->anim_flags |= AIAF_LOOPING; + + // Make sure we mark it as current and + p_info->multi_anim_info.flags |= FMA_CURRENT; + + if(multi_anim_info->anim_sound_index >= 0) + { + Sound_system.StopSoundLooping(ai_info->anim_sound_handle); + ai_info->anim_sound_handle = Sound_system.Play3dSound(multi_anim_info->anim_sound_index, SND_PRIORITY_LOW, obj); + } + + return; + } + + if(new_anim == AS_FLINCH) + { + float delta_time = 1.0f + obj->shields/10.0f; + if(delta_time > 4.3) + { + delta_time = 4.3f; + } + + ai_info->next_flinch_time = Gametime + delta_time; + } +// mprintf((0, "D %d to %d\n", ai_info->animation_type, ai_info->next_animation_type)); + + // Update the physics and movement class info + switch(ai_info->animation_type) + { + case AS_GOTO_IDLE_STANDING: + case AS_GOTO_ALERT_STANDING: + if(obj->movement_type != MT_NONE) + obj->movement_type = MT_WALKING; + ai_movement = ai_info->movement_type = MC_STANDING; + break; + + case AS_GOTO_IDLE_FLYING: + case AS_GOTO_ALERT_FLYING: + obj->movement_type = MT_PHYSICS; + ai_movement = ai_info->movement_type = MC_FLYING; + break; + + case AS_GOTO_IDLE_ROLLING: + case AS_GOTO_ALERT_ROLLING: + obj->movement_type = MT_WALKING; + ai_movement = ai_info->movement_type = MC_ROLLING; + break; + + case AS_GOTO_IDLE_WALKING: + case AS_GOTO_ALERT_WALKING: + obj->movement_type = MT_WALKING; + ai_movement = ai_info->movement_type = MC_WALKING; + break; + + case AS_GOTO_IDLE_JUMPING: + case AS_GOTO_ALERT_JUMPING: + obj->movement_type = MT_PHYSICS; + ai_movement = ai_info->movement_type = MC_JUMPING; + break; + } + + if(new_anim == AS_RANGED_ATTACK) + { + if(ai_info->sound[AI_ATTACK_SOUND] != SOUND_NONE_INDEX) + { + // Plays the sound and makes absolute sure that it is not looping + if(Gametime - ai_info->last_sound_time[AI_ATTACK_SOUND] > 5.0f) + { + // A 75% chance of playing it + if(ps_rand()%4 != 0) + { + Sound_system.StopSoundLooping(Sound_system.Play3dSound(ai_info->sound[AI_ATTACK_SOUND], SND_PRIORITY_NORMAL, obj)); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ai_info->sound[AI_ATTACK_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ai_info->sound[AI_ATTACK_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + } + ai_info->last_sound_time[AI_ATTACK_SOUND] = Gametime; + } + } + } + else if(new_anim != AI_INVALID_INDEX && + new_anim != AS_RANGED_RECOIL) + { + if((new_anim != AS_CUSTOM && Object_info[obj->id].anim[ai_movement].elem[new_anim].anim_sound_index != SOUND_NONE_INDEX) || + (new_anim == AS_CUSTOM && p_info->custom_anim_info.anim_sound_index != SOUND_NONE_INDEX)) + { + if(new_anim != AS_CUSTOM) + ai_info->last_played_sound_index = Object_info[obj->id].anim[ai_movement].elem[new_anim].anim_sound_index; + else + ai_info->last_played_sound_index = p_info->custom_anim_info.anim_sound_index; + + ai_info->anim_sound_handle = Sound_system.Play3dSound(ai_info->last_played_sound_index, SND_PRIORITY_LOW, obj); + } + else + { + ai_info->last_played_sound_index = -1; + } + } + + new_anim_label: + + if(new_anim != AI_INVALID_INDEX) + { + // Melee stuff + if(ai_info->animation_type == AS_MELEE1 || + ai_info->animation_type == AS_MELEE2) + { + do_melee_attack(obj); + } + else if(ai_info->animation_type == AS_RANGED_ATTACK) + { + ASSERT(new_anim == AS_RANGED_RECOIL); + + int cur_wb = ai_info->current_wb_firing; + int cur_mask = obj->dynamic_wb[cur_wb].wb_anim_mask; + float fire = Object_info[obj->id].static_wb[cur_wb].anim_fire_frame[cur_mask]; + + float temp_anim = p_info->anim_frame; + p_info->anim_frame = fire; + do_ranged_attack(obj); + p_info->anim_frame = temp_anim; + } + + // Set the new animation + ai_info->animation_type = new_anim; + + if(new_anim == AS_RANGED_ATTACK) + { + int cur_wb = ai_info->current_wb_firing; + int cur_mask = obj->dynamic_wb[cur_wb].wb_anim_mask; + + float start = Object_info[obj->id].static_wb[cur_wb].anim_start_frame[cur_mask]; + float fire = Object_info[obj->id].static_wb[cur_wb].anim_fire_frame[cur_mask]; + float end = Object_info[obj->id].static_wb[cur_wb].anim_end_frame[cur_mask]; + float total_time = Object_info[obj->id].static_wb[cur_wb].anim_time[cur_mask]; + + p_info->anim_start_frame = start; + p_info->anim_end_frame = fire; + + if(start < fire) + { + p_info->anim_time = total_time * ((fire - start)/(end - start)); + } + else + { + new_anim = AS_RANGED_RECOIL; + goto new_anim_label; + } + } + else if(new_anim == AS_RANGED_RECOIL) + { + int cur_wb = ai_info->current_wb_firing; + int cur_mask = obj->dynamic_wb[cur_wb].wb_anim_mask; + + float start = Object_info[obj->id].static_wb[cur_wb].anim_start_frame[cur_mask]; + float fire = Object_info[obj->id].static_wb[cur_wb].anim_fire_frame[cur_mask]; + float end = Object_info[obj->id].static_wb[cur_wb].anim_end_frame[cur_mask]; + float total_time = Object_info[obj->id].static_wb[cur_wb].anim_time[cur_mask]; + + p_info->anim_start_frame = fire; + p_info->anim_end_frame = end; + + if(fire < end) + p_info->anim_time = total_time * ((end - fire)/(end - start)); + else + { + new_anim = AS_ALERT; + goto new_anim_label; + } + } + else if(new_anim == AS_CUSTOM) + { + p_info->anim_start_frame = p_info->custom_anim_info.anim_start_frame; + p_info->anim_end_frame = p_info->custom_anim_info.anim_end_frame; + p_info->anim_time = p_info->custom_anim_info.anim_time; + p_info->anim_flags = p_info->custom_anim_info.flags; + ai_info->next_animation_type = p_info->custom_anim_info.next_anim_type; + } + else + { + p_info->anim_start_frame = Object_info[obj->id].anim[ai_movement].elem[new_anim].from; + p_info->anim_end_frame = Object_info[obj->id].anim[ai_movement].elem[new_anim].to; + p_info->anim_time = Object_info[obj->id].anim[ai_movement].elem[new_anim].spc; + } + + p_info->anim_frame = p_info->anim_start_frame; + + // Handle the next anim's + switch(new_anim) + { + case AS_MELEE1: + case AS_MELEE2: + { + p_info->anim_flags = AIAF_NOTIFY; + ai_info->next_animation_type = new_anim + 1; /* Recoil */ + } + break; + + case AS_RANGED_ATTACK: + { + p_info->anim_flags = AIAF_NOTIFY; + ai_info->next_animation_type = AS_RANGED_RECOIL; /* Recoil */ + } + break; + + case AS_DEATH: + { + p_info->anim_flags = 0; + ai_info->next_animation_type = AI_INVALID_INDEX; + } + break; + + case AS_ALERT: + case AS_IDLE: + { + p_info->anim_flags = AIAF_LOOPING; + ai_info->next_animation_type = AI_INVALID_INDEX; + } + break; + + case (AS_MELEE1 + 1): + + { + if(!AiMelee(obj)) + { + p_info->anim_flags = AIAF_NOTIFY; + ai_info->next_animation_type = AS_ALERT; + } + } + break; + + case (AS_MELEE2 + 1): + { + if(!AiMelee(obj)) + { + p_info->anim_flags = AIAF_NOTIFY; + ai_info->next_animation_type = AS_ALERT; + } + } + break; + + case AS_GOTO_IDLE_STANDING: + case AS_GOTO_IDLE_FLYING: + case AS_GOTO_IDLE_ROLLING: + case AS_GOTO_IDLE_WALKING: + case AS_GOTO_IDLE_JUMPING: + case AS_QUIRK: + case AS_BIRTH: + { + p_info->anim_flags = AIAF_NOTIFY; + ai_info->next_animation_type = AS_IDLE; + } + break; + + case AS_CUSTOM: + break; + + default: + { + p_info->anim_flags = AIAF_NOTIFY; + ai_info->next_animation_type = AS_ALERT; + } + break; + } + } + + if((Game_mode & GM_MULTI) && (Netgame.local_role == LR_SERVER)) + { + MultiAddObjAnimUpdate(OBJNUM(obj)); + } + if(Demo_flags==DF_RECORDING) + { + DemoWriteObjAnimChanged(OBJNUM(obj)); + } +} + +inline void ApplyConstantForce(object *objp, vector *new_pos, vector *force, float delta_time) +{ + const vector velocity = objp->mtype.phys_info.velocity; + const float drag = objp->mtype.phys_info.drag; + const float mass = objp->mtype.phys_info.mass; + + // Standard motion with a linear air drag (drag is proportional to velocity) + *new_pos = objp->pos + (*force / drag) * (delta_time) + (mass / drag) * (velocity - (*force / drag)) * (1 - exp(-(drag / mass) * delta_time)); +} + +bool AIDetermineAimPoint(object *robot, object *target, vector *aim_pt, float weapon_speed = 0.0f) +{ + if(DIFF_LEVEL == DIFFICULTY_TRAINEE && ((robot->ai_info->flags & AIF_TEAM_MASK) != AIF_TEAM_REBEL)) + { + *aim_pt = target->pos; + return true; + } + + float vl = AIDetermineObjVisLevel(robot, target); + + ai_frame *ai_info = robot->ai_info; + vector to_target = target->pos - robot->pos; + float dist_to_target = vm_NormalizeVector(&to_target); + + if(weapon_speed == 0.0f && ai_info) + weapon_speed = ai_info->weapon_speed; + + if(weapon_speed == 0.0f) + { + *aim_pt = target->pos; + return false; + } + + float wsp = weapon_speed - (to_target * target->mtype.phys_info.velocity); + + if(wsp <= 0.0f || vl < AIVIS_BARELY) + { + *aim_pt = target->pos; + return false; + } + + float dt = dist_to_target/wsp; + + float scale; + + if(ai_info) + scale = ai_info->lead_accuracy; + else + scale = 1.0f; + + // chrishack -- add stuff so that rebels get better as DIFF lowers and + // ptmc gets better as DIFF increases + if(target->type != OBJ_PLAYER || (DIFF_LEVEL < DIFFICULTY_HOTSHOT && ((robot->ai_info->flags & AIF_TEAM_MASK) != AIF_TEAM_REBEL)) || scale < 0.4f || vl <= AIVIS_MOSTLY) + *aim_pt = target->pos + (target->mtype.phys_info.velocity * dt) * scale; + else + ApplyConstantForce(target, aim_pt, &target->mtype.phys_info.thrust, dt * scale); + + return true; +} + +vector *AIDetermineFovVec(object *obj, vector *fov) +{ + ai_frame *ai_info = obj->ai_info; + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + + if((ai_info->flags & AIF_AIM_PNT_FOV) && pm->num_wbs > 0) + { + int aiming_gp_index = Object_info[obj->id].static_wb[0].aiming_gp_index; + WeaponCalcGun(NULL, fov, obj, pm->poly_wb[0].gp_index[aiming_gp_index]); + } + else if(ai_info->flags & AIF_UVEC_FOV) + { + *fov = obj->orient.uvec; + } + else + { + *fov = obj->orient.fvec; + } + + return fov; +} + +#ifdef _DEBUG +extern int DoAI; +#else +#define DoAI 1 +#endif + +void AISeeTarget(object *obj, bool f_see) +{ + ai_frame *ai_info = obj->ai_info; + + if(f_see) + { + if((Gametime - ai_info->last_see_target_time > CHECK_VIS_INFREQUENTLY_TIME) || + !(ai_info->awareness)) + { + if(ai_info->sound[AI_SEE_SOUND] != SOUND_NONE_INDEX) + { + // Plays the sound and makes absolute sure that it is not looping + if((ps_rand()%10) != 0) // 90% chance of playing it + { + if(Gametime - ai_info->last_sound_time[AI_SEE_SOUND] >= CHECK_VIS_INFREQUENTLY_TIME) + { + Sound_system.StopSoundLooping(Sound_system.Play3dSound(ai_info->sound[AI_SEE_SOUND], SND_PRIORITY_NORMAL, obj)); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ai_info->sound[AI_SEE_SOUND], OBJNUM(obj), SND_PRIORITY_LOW); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ai_info->sound[AI_SEE_SOUND], OBJNUM(obj), SND_PRIORITY_LOW); + } + } + + ai_info->last_sound_time[AI_SEE_SOUND] = Gametime; + } + } + } + + if(ai_info->awareness < AWARE_MOSTLY) + ai_info->awareness = AWARE_MOSTLY; + + // Note: Player position is also updated in the visability test function + // for MIN_VIS_RECENT_CHECK_INTERVAL seconds + object *targetptr; + object *other_obj = targetptr = ObjGet(ai_info->target_handle); + + if(targetptr) + { + if(f_see) + ai_info->last_see_target_time = Gametime; + else + ai_info->last_hear_target_time = Gametime; + + AIDetermineAimPoint(obj, targetptr, &ai_info->last_see_target_pos); + } +} + +bool AINotify(object *obj, ubyte notify_type, void *info) +{ + int next_anim; + ai_frame *ai_info = obj->ai_info; + //@$-tD3XEventArgs evtargs; + object *other_obj = NULL; + object *targetptr; + int parent; + object *new_enemy; + tOSIRISEventInfo ei; + bool f_it_set = false; + + if(Demo_flags == DF_PLAYBACK) + { + return false; + } + ASSERT(obj); + ASSERT(notify_type >= 0 && notify_type < 32); + + if(((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT))) + return false; + + if(!DoAI) + return false; + + if(obj->type == OBJ_DUMMY && notify_type != AIN_USER_DEFINED) + return false; + + if(obj && obj->control_type == CT_AI && ai_info->flags & AIF_DISABLED) + return false; + + // All events use + //@$-evtargs.args[0] = MAKE_NUM_EVTARG((float)notify_type); + ei.evt_ai_notify.notify_type = notify_type; + + switch (notify_type) + { + case AIN_ANIM_COMPLETE: + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + AIUpdateAnim(obj); + } + break; // parent robot + case AIN_SCRIPTED_ENABLER: + { + //@$-evtargs.args[1] = MAKE_NUM_EVTARG(((notify *)info)->goal_num); + //@$-evtargs.args[2] = MAKE_NUM_EVTARG(((notify *)info)->enabler_num); + ei.evt_ai_notify.goal_num = ((notify *)info)->goal_num; + ei.evt_ai_notify.enabler_num= ((notify *)info)->enabler_num; + } + break; + case AIN_NEW_MOVEMENT: break; // parent robot + case AIN_OBJ_KILLED: break; // parent robot + case AIN_MELEE_HIT: + case AIN_MELEE_ATTACK_FRAME: + { + notify *melee_info = (notify *)info; + + other_obj = ObjGet(melee_info->obj_handle); + + //@$-evtargs.args[1] = MAKE_NUM_EVTARG(((notify *)info)->attack_num); + ei.evt_ai_notify.attack_num = melee_info->attack_num; + } + break; + case AIN_WHIT_BY_OBJ: break; // parent robot + case AIN_SEE_TARGET: // parent robot (who sees, type, player_obj) + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + AISeeTarget(obj, true); + } + other_obj = (object *) info; + break; + case AIN_PLAYER_SEES_YOU: // parent robot (who got seen, type, player_obj) + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + if(ai_info->awareness < AWARE_BARELY) + ai_info->awareness = AWARE_BARELY; + ai_info->last_render_time = Gametime; + } + break; + case AIN_WHIT_OBJECT: // A robots (the one being notified, hit something) --chrishack -- if friend, no fire for X seconds + + ai_info->memory[0].num_hit_enemy++; + + if(ai_info->awareness < AWARE_MOSTLY) + { + ai_info->awareness = AWARE_MOSTLY; + } + // chrishack -- if hitobject is target then... But, this is ok for now + targetptr = ObjGet(ai_info->target_handle); + + // chrishack - not really done - quick implementation + if(targetptr) + { + ai_info->last_see_target_time = Gametime; + AIDetermineAimPoint(obj, targetptr, &ai_info->last_see_target_pos); + } + + // chrishack! + other_obj = (object *) info; + break; + + case AIN_MOVIE_START: + case AIN_MOVIE_END: + break; + case AIN_TARGET_DIED: + ei.evt_ai_notify.it_handle = *(int *)info; + f_it_set = true; + break; + + case AIN_OBJ_FIRED: // All visable (obj fired, AIN_OBJFIRED, obj who fired) -- all rendered bots + { + int i; + other_obj = (object *) info; + object *targetptr; + + if(other_obj->control_type == CT_AI) + { + tOSIRISEventInfo ei; + + ei.evt_ai_notify.notify_type = AIN_FIRED_WEAPON; + ei.evt_ai_notify.attack_num = obj->ctype.laser_info.src_gun_num; + ei.evt_ai_notify.it_handle = obj->handle; + + Osiris_CallEvent(other_obj,EVT_AI_NOTIFY,&ei); + } + + if(other_obj->type == OBJ_PLAYER) + { + ain_hear hear; + + hear.f_directly_player = true; + hear.hostile_level = 1.0f; + hear.curiosity_level = 0.0f; + hear.max_dist = AI_SOUND_SHORT_DIST; + AINotify(other_obj, AIN_HEAR_NOISE, (void *)&hear); + } + + if(other_obj->control_type == CT_AI) + { + targetptr = ObjGet(other_obj->ai_info->target_handle); + } + else + { + targetptr = NULL; + } + +// CHRISHACK - WTF - What was this actually for???? Hmmm..... +// if(obj == targetptr) +// { +// ei.evt_ai_notify.it_handle = ai_info->target_handle; +// +// ai_info->memory[0].num_enemy_shots_fired++; +// +// //@$-D3XExecScript(obj, EVT_AI_NOTIFY, obj, REF_OBJTYPE, &evtargs); +// Osiris_CallEvent(obj,EVT_AI_NOTIFY,&ei); +// } + + if(targetptr && targetptr->control_type == CT_AI) + { + if((targetptr->ai_info->flags & AIF_DODGE) && !(IS_GUIDEBOT(targetptr))) + { + if(BOA_IsVisible(targetptr->roomnum, other_obj->roomnum)) + { + targetptr->ai_info->memory[0].num_enemy_shots_dodged++; + GoalAddGoal(targetptr, AIG_DODGE_OBJ, (void *) &obj->handle, ACTIVATION_BLEND_LEVEL); + + //@$-D3XExecScript(targetptr, EVT_AI_NOTIFY, obj, REF_OBJTYPE, &evtargs); + ei.evt_ai_notify.it_handle = obj->handle; + Osiris_CallEvent(targetptr,EVT_AI_NOTIFY,&ei); + } + } + } + else if(other_obj->type == OBJ_PLAYER) // chrishack -- we might be able use the target handle and last see time! + // use BOA line-of-sight if any player then notify! Damn big improvement. + { + if(!(Game_mode & GM_MULTI)) + { + for(i = 0; i < AI_NumRendered; i++) + { +// mprintf((0, "I know that I could dodge, if I was aware, says robot %d.\n", AI_RenderedList[i])); + + if(!BOA_IsVisible(Objects[AI_RenderedList[i]].roomnum, other_obj->roomnum)) + continue; + + ai_frame *ai_info = Objects[AI_RenderedList[i]].ai_info; + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + if(!(ai_info->flags & AIF_DISABLED)) + { + if(ai_info->flags & AIF_DODGE) + { + if(ai_info->awareness > AWARE_BARELY) + { + if(ps_rand() < ai_info->dodge_percent * RAND_MAX) + { + vector fov_vec; + + if(ai_info->vec_to_target_actual * (*AIDetermineFovVec(&Objects[AI_RenderedList[i]], &fov_vec)) >= Objects[AI_RenderedList[i]].ai_info->fov) + { +// mprintf((0, "I am fired upon says robot %d\n", AI_RenderedList[i])); + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + ai_info->memory[0].num_enemy_shots_dodged++; + GoalAddGoal(&Objects[AI_RenderedList[i]], AIG_DODGE_OBJ, (void *) &obj->handle, ACTIVATION_BLEND_LEVEL, 1.0f, GF_SPEED_DODGE); + } + } + } + } + } + } + } + //@$-D3XExecScript(&Objects[AI_RenderedList[i]], EVT_AI_NOTIFY, NULL, REF_OBJTYPE, &evtargs); + Osiris_CallEvent(&Objects[AI_RenderedList[i]], EVT_AI_NOTIFY, &ei); + + // This might be removed + AISeeTarget(&Objects[AI_RenderedList[i]], false); + } + } + else + { + for(i = 0; i <= Highest_object_index; i++) + { + // mprintf((0, "I know that I could dodge, if I was aware, says robot %d.\n", AI_RenderedList[i])); + if(Objects[i].control_type != CT_AI || Objects[i].type == OBJ_NONE) + continue; + + if(!BOA_IsVisible(Objects[i].roomnum, other_obj->roomnum)) + continue; + + ai_frame *ai_info = Objects[i].ai_info; + + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + if(!(ai_info->flags & AIF_DISABLED)) + { + if(ai_info->flags & AIF_DODGE) + { + if(ps_rand() < ai_info->dodge_percent * RAND_MAX) + { + vector to_weapon = other_obj->pos - Objects[i].pos; + vm_NormalizeVector(&to_weapon); + + vector fov_vec; + AIDetermineFovVec(&Objects[i], &fov_vec); + + if(to_weapon * fov_vec >= Objects[i].ai_info->fov) + { + // mprintf((0, "I am fired upon says robot %d\n", AI_RenderedList[i])); + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + ai_info->memory[0].num_enemy_shots_dodged++; + GoalAddGoal(&Objects[i], AIG_DODGE_OBJ, (void *) &obj->handle, ACTIVATION_BLEND_LEVEL, 1.0f, GF_SPEED_DODGE); + } + } + } + } + } + } + //@$-D3XExecScript(&Objects[i], EVT_AI_NOTIFY, NULL, REF_OBJTYPE, &evtargs); + Osiris_CallEvent(&Objects[i], EVT_AI_NOTIFY, &ei); + + // This might be removed + AISeeTarget(&Objects[i], false); + } + } + } + } + break; + case AIN_GOAL_COMPLETE: + case AIN_GOAL_INVALID: + case AIN_GOAL_FAIL: + case AIN_GOAL_ERROR: + case AIN_SCRIPTED_GOAL: + case AIN_SCRIPTED_INFLUENCE: + case AIN_SCRIPTED_ORIENT: + ei.evt_ai_notify.goal_num = *((int *)info); + ei.evt_ai_notify.goal_uid = ai_info->goals[ei.evt_ai_notify.goal_num].guid; + ei.evt_ai_notify.it_handle = obj->handle; + f_it_set = true; + Osiris_CallLevelEvent(EVT_AI_NOTIFY,&ei); + break; + case AIN_HEAR_NOISE: + { + ain_hear *hptr = (ain_hear *)info; + int i; + + if(hptr->f_directly_player) + { + short heard_noise_obj[50]; + int num_objs = fvi_QuickDistObjectList(&obj->pos, obj->roomnum, hptr->max_dist, heard_noise_obj, 50, false, true, false, true); + + for(i = 0; i < num_objs; i++) + { + if(Objects[heard_noise_obj[i]].control_type == CT_AI) + { + if(vm_VectorDistance(&obj->pos, &Objects[heard_noise_obj[i]].pos) < hptr->max_dist * Objects[heard_noise_obj[i]].ai_info->hearing) + { + AISeeTarget(&Objects[heard_noise_obj[i]], false); + } + } + } + } + } + break; // parent robot -- chrishack -- Not done + case AIN_NEAR_TARGET: + other_obj = (object *) info; + break; + case AIN_HIT_BY_WEAPON: + { + ai_info->memory[0].num_times_hit++; + + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + ai_info->awareness = AWARE_FULLY; + + // Flinching + if(Gametime > ai_info->next_flinch_time) + { + if(ps_rand()%10 > 8) + { + if(ai_info->flags & AIF_FLINCH) + { + next_anim = AS_FLINCH; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + } + } + } + } + + if(ai_info->sound[AI_ANGRY_SOUND] != SOUND_NONE_INDEX) + { + // Plays the sound and makes absolute sure that it is not looping + if(Gametime - ai_info->last_sound_time[AI_ANGRY_SOUND] > 5.0f) + { + Sound_system.StopSoundLooping(Sound_system.Play3dSound(ai_info->sound[AI_ANGRY_SOUND], SND_PRIORITY_NORMAL, obj)); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ai_info->sound[AI_ANGRY_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ai_info->sound[AI_ANGRY_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + ai_info->last_sound_time[AI_ANGRY_SOUND] = Gametime; + } + } + + other_obj = (object *) info; + parent = other_obj->parent_handle; + new_enemy = ObjGet(parent); + + if(ai_info->flags & AIF_ACT_AS_NEUTRAL_UNTIL_SHOT) + { + ai_info->flags &= ~AIF_ACT_AS_NEUTRAL_UNTIL_SHOT; + if(!AIObjEnemy(obj, new_enemy)) + { + ai_info->flags |= AIF_ACT_AS_NEUTRAL_UNTIL_SHOT; + } + } + + bool f_enemy = AIObjEnemy(obj, new_enemy); + + float rand_val = ps_rand()/(float)RAND_MAX; + + if(new_enemy && (new_enemy != obj)) + { + // 20% per hit of switching to hitting player + if((new_enemy->control_type == CT_AI && + ((new_enemy->ai_info->target_handle == obj->handle) || + (!f_enemy && new_enemy->id == obj->id && new_enemy->type == obj->type && rand_val < ai_info->fight_same) || + (!f_enemy && !(new_enemy->id == obj->id && new_enemy->type == obj->type) && rand_val < ai_info->fight_team))) || + (f_enemy && new_enemy->type == OBJ_PLAYER && (ai_info->awareness <= AWARE_BARELY || rand_val > .8 || ai_info->target_handle == OBJECT_HANDLE_NONE))) + { +// if(obj->ai_info->flags & AIF_DETERMINE_TARGET) + { + ai_info->flags |= AIF_DETERMINE_TARGET; + obj->ai_info->next_target_update_time = Gametime + MIN_TARGET_UPDATE_INTERVAL + ((float)ps_rand()/(float)RAND_MAX) * (MAX_TARGET_UPDATE_INTERVAL - MIN_TARGET_UPDATE_INTERVAL); + AISetTarget(obj, new_enemy->handle); + } + } + } + } + break; + + case AIN_BUMPED_OBJ: + other_obj = (object *) info; + + // 100% is previously not aware, 50% if already aware + if(AIObjEnemy(obj, other_obj)) + { + if((ai_info->awareness <= AWARE_BARELY) || ((ai_info->awareness > AWARE_BARELY) && ((ps_rand()%100) > 50))) + { +// if(ai_info->flags & AIF_DETERMINE_TARGET) + { + ai_info->flags |= AIF_DETERMINE_TARGET; + ai_info->next_target_update_time = Gametime + MIN_TARGET_UPDATE_INTERVAL + ((float)ps_rand()/(float)RAND_MAX) * (MAX_TARGET_UPDATE_INTERVAL - MIN_TARGET_UPDATE_INTERVAL); + AISetTarget(obj, other_obj->handle); + } + } + + if(ai_info->notify_flags & (0x00000001 << notify_type)) + { + ai_info->awareness = AWARE_FULLY; + AISeeTarget(obj, false); + } + } + + if(ai_info->sound[AI_ANGRY_SOUND] != SOUND_NONE_INDEX) + { + // Plays the sound and makes absolute sure that it is not looping + if(Gametime - ai_info->last_sound_time[AI_ANGRY_SOUND] > 5.0f) + { + Sound_system.StopSoundLooping(Sound_system.Play3dSound(ai_info->sound[AI_ANGRY_SOUND], SND_PRIORITY_NORMAL, obj)); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ai_info->sound[AI_ANGRY_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ai_info->sound[AI_ANGRY_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + ai_info->last_sound_time[AI_ANGRY_SOUND] = Gametime; + } + } + break; + case AIN_USER_DEFINED: + //@$-evtargs.args[1] = MAKE_NUM_EVTARG((float)(*((int *)info))); + ei.extra_info = info; + break; + default: + mprintf((0, "Warning, %d has been notified with an unhandled notification %d\n", OBJNUM(obj), notify_type)); + return true; + } + + if(notify_type != AIN_OBJ_FIRED && notify_type != AIN_HEAR_NOISE) + { + //@$-return (D3XExecScript(obj, EVT_AI_NOTIFY, other_obj, REF_OBJTYPE, &evtargs)); + if(!f_it_set) + { + ei.evt_ai_notify.it_handle = ((other_obj != NULL)?other_obj->handle:OBJECT_HANDLE_NONE); + } + + return Osiris_CallEvent(obj,EVT_AI_NOTIFY,&ei); + } + + return true; +} + +// Returns the amount of time left +void ai_do_animation(object *obj, float anim_time) +{ + float *anim_frame = &obj->rtype.pobj_info.anim_frame; + + float to; + float from; + float delta; + float spc; + bool f_looped; + bool f_notify; + int i; + + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + polyobj_info *p_info = &obj->rtype.pobj_info; + + if (!(Object_info[obj->id].anim)) + return; + + if((Demo_flags == DF_PLAYBACK) || ((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT))) + { + + // Update animation keyframe + if(p_info->multi_anim_info.flags & FMA_VALID) + { + if(!(p_info->multi_anim_info.flags & FMA_CURRENT)) + { + AIUpdateAnim(obj); + } + } + + if(p_info->multi_turret_info.flags & (FMT_UPDATING | FMT_NEW_DATA)) + { + int count = 0; + int i,j; + if(p_info->multi_turret_info.flags & FMT_NEW_DATA) + { + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + p_info->multi_turret_info.last_time = Gametime; + p_info->multi_turret_info.last_keyframes[count++] = obj->dynamic_wb[i].norm_turret_angle[j]; + } + } + + p_info->multi_turret_info.flags = FMT_UPDATING; + } + + count = 0; + + if((Demo_flags == DF_PLAYBACK) ||((p_info->multi_turret_info.last_time + 1.0/(float)NetPlayers[Player_num].pps) >= Gametime)) + { + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + obj->dynamic_wb[i].norm_turret_angle[j] = p_info->multi_turret_info.keyframes[count++]; + } + } + + p_info->multi_turret_info.flags = 0; + } + else + { + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + float pd = (Gametime - p_info->multi_turret_info.last_time)/NetPlayers[Player_num].pps; + float diff; + + diff = p_info->multi_turret_info.keyframes[count] - p_info->multi_turret_info.last_keyframes[count]; + + if( fabsf(diff) < 0.5f ) + { + obj->dynamic_wb[i].norm_turret_angle[j] = (1.0 - pd)*p_info->multi_turret_info.last_keyframes[count] + pd*p_info->multi_turret_info.keyframes[count]; + } + else + { + diff = -(1.0f - diff)*pd; + obj->dynamic_wb[i].norm_turret_angle[j] = p_info->multi_turret_info.last_keyframes[count] + diff; + + if(obj->dynamic_wb[i].norm_turret_angle[j] < 0.0) + obj->dynamic_wb[i].norm_turret_angle[j] += 1.0f; + else if(obj->dynamic_wb[i].norm_turret_angle[j] > 1.0) + obj->dynamic_wb[i].norm_turret_angle[j] -= 1.0f; + } + + count++; + } + } + } + } + } + else + { + if(obj->ai_info->animation_type == AS_RANGED_ATTACK) + { + char wb = obj->ai_info->last_special_wb_firing; + + if(Object_info[obj->id].static_wb[wb].flags & WBF_SPRAY) + { + obj->weapon_fire_flags |= WFF_SPRAY; + } + + if(Object_info[obj->id].static_wb[wb].flags & WBF_ON_OFF) + { + obj->weapon_fire_flags |= WFF_ON_OFF; + } + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(OBJNUM(obj)); + } + } + } + + for(i = 0; i < pm->num_wbs; i++) + { + if(obj->dynamic_wb[i].flags & DWBF_ANIMATING) + { + object_info *obj_info = &Object_info[obj->id]; + if(obj_info->static_wb) + WBFireAnimFrame(obj, &obj_info->static_wb[i], i); + } + } + + start_loop: + + from = obj->rtype.pobj_info.anim_start_frame; + to = obj->rtype.pobj_info.anim_end_frame; + spc = obj->rtype.pobj_info.anim_time; + + if(obj->movement_type == MT_WALKING && obj->ai_info->movement_type == MC_WALKING) + { + if(obj->ai_info->animation_type == AS_ALERT || obj->ai_info->animation_type == AS_IDLE) + { + float scaler; + + float speed = vm_GetMagnitude(&obj->mtype.phys_info.velocity); + scaler = speed/obj->ai_info->max_velocity; + + // If slow, use some rotational vel + if(scaler < .7f) + { + float speed = vm_GetMagnitude(&obj->mtype.phys_info.rotvel); + scaler += speed/40000.0f; + + if(scaler > 1.0f) + scaler = 1.0f; + } + + if((obj->ai_info->next_animation_type == AS_GOTO_ALERT_STANDING || + obj->ai_info->next_animation_type == AS_GOTO_IDLE_STANDING) && scaler < .15f) + { + scaler = .15f; + } + + if(scaler <= 0.0f) + { + return; + } + + spc /= scaler; + } + } + // mprintf((0, "AI ANIM %d %d\n", from, to)); + + f_looped = (obj->rtype.pobj_info.anim_flags & AIAF_LOOPING) != 0; + f_notify = (obj->rtype.pobj_info.anim_flags & AIAF_NOTIFY) != 0; + + if(!f_looped && *anim_frame == to) + { + goto done; + } + + if(to == from) + { + anim_time = 0.0f; + goto done; + } + + if(spc <= 0) + { + *anim_frame = to; + goto done; + } + + ASSERT(from <= to); + + if(*anim_frame < from || *anim_frame > to) + { + mprintf((0, "AI/Animation: Correcting for an incorrect frame number\n")); + *anim_frame = from; + } + + delta = to - from; + + ASSERT(delta >= 0.0f); + ASSERT(spc > 0.0f); + + if (delta > 0.0f) + { + // step is how much we move this frame + float max_step = anim_time*((delta)/spc); + + if(*anim_frame + max_step >= to) + { + anim_time -= anim_time * (to - *anim_frame)/max_step; + + if(f_looped) + *anim_frame = from; + else + *anim_frame = to; + + if(f_notify) + { +// mprintf((0, "Animation ended\n")); + AINotify(obj, AIN_ANIM_COMPLETE, NULL); + } + + + goto start_loop; + } + else + { + *anim_frame += max_step; + } + } + else + { + obj->rtype.pobj_info.anim_frame = from; + } + +#ifdef MACINTOSH //DAJ + if(obj->rtype.pobj_info.anim_frame+0.001 < from || obj->rtype.pobj_info.anim_frame > to) + mprintf((2, "AI ANIM from %0.6f (%0.6f) to %0.6f\n", from, obj->rtype.pobj_info.anim_frame, to)); + if(obj->rtype.pobj_info.anim_frame < from) + obj->rtype.pobj_info.anim_frame = from; + if(obj->rtype.pobj_info.anim_frame > to) + obj->rtype.pobj_info.anim_frame = to; +#else + + ASSERT(obj->rtype.pobj_info.anim_frame >= from && obj->rtype.pobj_info.anim_frame <= to); +#endif + return; + + done: + + + if(f_notify) + { + AINotify(obj, AIN_ANIM_COMPLETE, NULL); + } + + return; +} + +#define FRR_MAX_TRIES 15 + +int AIGoalGotoRandomRoom() +{ + return -1; +} + +int AIFindRandomRoom(object *obj, ai_frame *ai_info, goal *goal_ptr, int avoid_room, int min_depth, int max_depth, bool f_check_path, bool f_cur_room_ok, int *depth) +{ + int random_room = obj->roomnum; + int n_tries = 0; + bool valid = false; + bool f_use_depth = false; + int cur_depth; + int cur_room = obj->roomnum; + int mine_rooms[MAX_ROOMS]; + int num_mine_rooms = 0; + int cur_mine = MINE_INDEX(obj->roomnum); + int i; + + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used && !(Rooms[i].flags & RF_EXTERNAL) && cur_mine == MINE_INDEX(i)) + { + mine_rooms[num_mine_rooms++] = i; + ASSERT(Rooms[mine_rooms[num_mine_rooms - 1]].used); + } + } + + if(max_depth >= 0 && min_depth >= 0) + { + f_use_depth = true; + cur_depth = min_depth + (float)ps_rand()/(float)RAND_MAX*(max_depth - min_depth); + } + + do + { + random_room = mine_rooms[ps_rand()%num_mine_rooms]; + valid = true; + + if(random_room == avoid_room || ((!f_cur_room_ok) && (random_room == obj->roomnum)) || (!Rooms[random_room].used)) + valid = false; + + if(valid && f_check_path) + { + ASSERT(Rooms[random_room].used); + ASSERT(Rooms[obj->roomnum].used); + valid = AIPathAllocPath(obj, ai_info, goal_ptr, &obj->roomnum, &obj->pos, &random_room, &Rooms[random_room].path_pnt, 0.0f, 0, obj->handle); + } + + n_tries++; + } + while(!valid && n_tries <= FRR_MAX_TRIES && num_mine_rooms > 1); + + if(valid && f_use_depth) + { + while(cur_room != random_room && cur_depth > 0) + { + cur_room = BOA_NEXT_ROOM(cur_room, random_room); + cur_depth--; + } + + random_room = cur_room; + } + + if(!valid) + { + int next_rooms[100]; + int i; + int pick_list[100]; + int num_pickable = 0; + + int num_next_rooms = AIMakeNextRoomList(obj->roomnum, next_rooms, 100); + for(i = 0; i < num_next_rooms; i++) + { + if(next_rooms[i] <= Highest_room_index && BOA_DetermineStartRoomPortal(obj->roomnum, NULL, next_rooms[i], NULL) >= 0) + { + pick_list[num_pickable++] = next_rooms[i]; + } + } + + if(num_pickable) + { + random_room = pick_list[ps_rand()%num_pickable]; + valid = true; + } + + if(!valid) + { + mprintf((0, "AI: Wander is generating the same room :(\n")); + random_room = obj->roomnum; + } + } + + ASSERT(random_room >= 0 || random_room <= Highest_room_index + BOA_num_terrain_regions); + if(random_room > Highest_room_index) + { + // This is a temporary chrishack -- we need to select a random cell in the region + random_room = ps_rand()%(TERRAIN_WIDTH * TERRAIN_DEPTH); + random_room |= 0x80000000; + } + + return random_room; +} + +// +/- 8% fluctuation in speed parameters +#define MAX_FLUCTUATION_PERCENT 0.08f + +void AIDestroyObj(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + AIPathFreePath(&ai_info->path); +} + +//Copies AI settings from the object info struct to the object struct +void ObjSetAIInfo(object *objp) +{ + int i; + + ai_frame *dest = objp->ai_info; ASSERT(dest != NULL); + if(!(Object_info[objp->id].ai_info)) + return; + + t_ai_info *src = Object_info[objp->id].ai_info; + + dest->ai_class = src->ai_class; + dest->ai_type = src->ai_type; + + dest->max_velocity = src->max_velocity; + dest->max_delta_velocity = src->max_delta_velocity; + dest->max_turn_rate = src->max_turn_rate; + dest->max_delta_turn_rate = src->max_delta_turn_rate; + + dest->attack_vel_percent = src->attack_vel_percent; + dest->flee_vel_percent = src->flee_vel_percent; + dest->dodge_vel_percent = src->dodge_vel_percent; + + dest->circle_distance = src->circle_distance; + dest->dodge_percent = src->dodge_percent; + + //Note: Chris is evil. There should be a symbolic to describe the size of these arrays + for (i=0;i<2;i++) { + dest->melee_damage[i] = src->melee_damage[i]; + dest->melee_latency[i] = src->melee_latency[i]; + } + + for (i=0;isound[i] = src->sound[i]; + + dest->movement_type = src->movement_type; + dest->movement_subtype = src->movement_subtype; + + dest->flags = src->flags; + dest->notify_flags = src->notify_flags; + + dest->fov = src->fov; + + dest->avoid_friends_distance = src->avoid_friends_distance; + + dest->frustration = src->frustration; + dest->curiousity = src->curiousity; + dest->life_preservation = src->life_preservation; + dest->agression = src->agression; + + dest->fire_spread = src->fire_spread; + dest->night_vision = src->night_vision; + dest->fog_vision = src->fog_vision; + dest->lead_accuracy = src->lead_accuracy; + dest->lead_varience = src->lead_varience; + dest->fight_team = src->fight_team; + dest->fight_same = src->fight_same; + dest->hearing = src->hearing; + dest->roaming = src->roaming; + dest->biased_flight_importance = src->biased_flight_importance; + dest->biased_flight_min = src->biased_flight_min; + dest->biased_flight_max = src->biased_flight_max; +} + +bool AIInit(object *obj, ubyte ai_class, ubyte ai_type, ubyte ai_movement) +{ + ai_frame *ai_info = obj->ai_info; + ASSERT(ai_info); + ASSERT(obj->control_type == CT_AI && obj->type != OBJ_NONE); + + //Get AI data from the object type info + ASSERT(IS_GENERIC(obj->type)); + ObjSetAIInfo(obj); + + polyobj_info *p_info = &obj->rtype.pobj_info; + ai_path_info *path = &ai_info->path; + int i; + int anim; + + AIPathInitPath(path); + + bool f_no_scale = (IS_GENERIC(obj->type) && (Object_info[obj->id].flags & OIF_NO_DIFF_SCALE_MOVE)) || ((ai_info->flags & AIF_TEAM_MASK) == AIF_TEAM_REBEL); + + ai_info->mem_time_till_next_update = 3.0f + (float)ps_rand()/(float)RAND_MAX * 2.0f; + memset(ai_info->memory, 0, sizeof(ai_mem) * AI_MEM_DEPTH); + for(i = 0; i < AI_MEM_DEPTH; i++) + { + ai_info->memory[0].shields = obj->shields; + } + + // In case there is none + obj->rtype.pobj_info.anim_frame = 0.0; + + ai_info->ai_class = ai_class; + ai_info->ai_type = ai_type; + ai_info->movement_type = ai_movement; + ai_info->next_movement = AI_INVALID_INDEX; + ai_info->anim_sound_handle = 0; + ai_info->status_reg = 0; + ai_info->last_played_sound_index = -1; + ai_info->weapon_speed = 0.0f; + + ai_info->vec_to_target_perceived = obj->orient.fvec; + + ai_info->last_dodge_dir = Zero_vector; + ai_info->dodge_till_time = Gametime - 1.0f; + + if(ObjGet(obj->parent_handle)) + { + object *parent = ObjGet(obj->parent_handle); + + if(parent && (parent->type == OBJ_PLAYER || parent->type == OBJ_GHOST)) + { + obj->ai_info->flags &= ~AIF_TEAM_MASK; + obj->ai_info->flags |= AIF_TEAM_REBEL; + } + } + + for(i = 0; i < MAX_AI_SOUNDS; i++) + { + ai_info->last_sound_time[i] = Gametime; + } + + // Set up the robot to walk if it is applicable + if(ai_info->movement_type == MC_WALKING) + { + if(Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].to) + ai_info->movement_type = MC_STANDING; + + obj->movement_type = MT_WALKING; + } + + if((ObjGet(obj->parent_handle) || Matcen_created) && (Object_info[obj->id].anim[ai_movement].elem[AS_BIRTH].to)) + { + anim = AS_BIRTH; + ai_info->next_animation_type = AS_IDLE; + p_info->anim_flags = AIAF_NOTIFY; + + p_info->anim_start_frame = Object_info[obj->id].anim[ai_movement].elem[anim].from; + p_info->anim_end_frame = Object_info[obj->id].anim[ai_movement].elem[anim].to; + p_info->anim_time = Object_info[obj->id].anim[ai_movement].elem[anim].spc; + p_info->anim_frame = Object_info[obj->id].anim[ai_movement].elem[anim].from; + } + else + { + anim = AS_IDLE; + + if(Object_info[obj->id].anim[ai_movement].elem[anim].to == 0.0f) + anim = AS_ALERT; + + ai_info->next_animation_type = AI_INVALID_INDEX; + p_info->anim_flags = AIAF_LOOPING; + + // Setup the initial animation state info + float rand_offset = ps_rand()/((float)RAND_MAX); + p_info->anim_start_frame = Object_info[obj->id].anim[ai_movement].elem[anim].from; + p_info->anim_end_frame = Object_info[obj->id].anim[ai_movement].elem[anim].to; + p_info->anim_time = Object_info[obj->id].anim[ai_movement].elem[anim].spc; + p_info->anim_frame = (rand_offset) * Object_info[obj->id].anim[ai_movement].elem[anim].from + + (1.0f - rand_offset) * Object_info[obj->id].anim[ai_movement].elem[anim].to; + } + + ai_info->animation_type = anim; + + if(Object_info[obj->id].anim[ai_movement].elem[anim].anim_sound_index != SOUND_NONE_INDEX) + { + + if(!Viewer_object || !Player_object) + { + Player_object = &Objects[0]; + Viewer_object = Player_object; + } + + // Accounts for sound loading + if((obj->handle & HANDLE_COUNT_MASK) != 0) + { + ai_info->last_played_sound_index = Object_info[obj->id].anim[ai_movement].elem[anim].anim_sound_index; + ai_info->anim_sound_handle = Sound_system.Play3dSound(Object_info[obj->id].anim[ai_movement].elem[anim].anim_sound_index, SND_PRIORITY_LOW, obj); + } + else + { + ai_info->last_played_sound_index = -1; + } + } + else + { + ai_info->last_played_sound_index = -1; + } + + ai_info->next_melee_time = Gametime; + ai_info->next_flinch_time = Gametime; + AISetTarget(obj, OBJECT_HANDLE_NONE); + ai_info->next_check_see_target_time = Gametime + (float)ps_rand()/(float)RAND_MAX; + ai_info->last_see_target_time = Gametime - CHECK_VIS_INFREQUENTLY_TIME * 2.0f; + ai_info->last_hear_target_time = Gametime - CHECK_VIS_INFREQUENTLY_TIME * 2.0f; + ai_info->last_render_time = -1.0f; + ai_info->next_target_update_time = Gametime; + + if(ai_info->flags & AIF_FLUCTUATE_SPEED_PROPERTIES) + { + ai_info->max_velocity *= 1.0f + (((float)ps_rand() - RAND_MAX * 0.5f)/(RAND_MAX * 0.5f)) * MAX_FLUCTUATION_PERCENT; + ai_info->max_delta_velocity *= 1.0f + (((float)ps_rand() - RAND_MAX * 0.5f)/(RAND_MAX * 0.5f)) * MAX_FLUCTUATION_PERCENT; + ai_info->max_turn_rate *= 1.0f + (((float)ps_rand() - RAND_MAX * 0.5f)/(RAND_MAX * 0.5f)) * MAX_FLUCTUATION_PERCENT; + } + + ai_info->notify_flags |= AI_NOTIFIES_ALWAYS_ON; + + ai_info->awareness = AWARE_NONE; + + // Apply difficulty settings + ai_info->dodge_percent *= (f_no_scale)?1.0f:Diff_ai_dodge_percent[DIFF_LEVEL]; + ai_info->dodge_vel_percent *= (f_no_scale)?1.0f:Diff_ai_dodge_speed[DIFF_LEVEL]; + ai_info->max_velocity *= (f_no_scale || obj->movement_type == MT_WALKING)?1.0f:Diff_ai_speed[DIFF_LEVEL]; + ai_info->max_turn_rate *= (f_no_scale || obj->movement_type == MT_WALKING)?1.0f:Diff_ai_rotspeed[DIFF_LEVEL]; + ai_info->circle_distance *= (f_no_scale)?1.0f:Diff_ai_circle_dist[DIFF_LEVEL]; + + ai_info->last_see_target_pos = obj->pos; + + ai_info->vec_to_target_actual = obj->orient.fvec; + ai_info->vec_to_target_perceived = obj->orient.fvec; + + ai_info->dist_to_target_actual = vm_NormalizeVector(&ai_info->vec_to_target_actual); + ai_info->dist_to_target_perceived = 10000.0f; + + // Clear out the goals + GoalInitTypeGoals(obj, ai_info->ai_type); + + if(IS_GUIDEBOT(obj)) + ai_info->flags |= AIF_DODGE; + + return true; +} + +// chrishack -- fix the problem with the object being needed for AIInit +void AISetDefault(t_ai_info *ai_info_ptr) +{ +} + +void AIInitAll() +{ + int i; + + mprintf((0, "Initializing AI systems\n")); + + // Initialize the terrain AI system + ait_Init(); + + // Make sure that the buddies are located + for(i = 0; i < MAX_PLAYERS; i++) + { + Buddy_handle[i] = OBJECT_HANDLE_NONE; + } + + // Initialize the room AI system + + // Now, initialize each AI object + for(i = 0; i <= Highest_object_index; i++) + if(Objects[i].type != OBJ_NONE && Objects[i].control_type == CT_AI) + { + ASSERT(Objects[i].ai_info); + AIInit(&Objects[i], Objects[i].ai_info->ai_class, Objects[i].ai_info->ai_type, Objects[i].ai_info->movement_type); + } + + AI_NumRendered = 0; + AI_NumHostileAlert = 0; + + if(!(Game_mode & GM_MULTI)) + { + int objnum = ObjCreate(OBJ_ROBOT, ROBOT_GUIDEBOT, Player_object->roomnum, &Player_object->pos, NULL, Player_object->handle); + if(objnum > -1) { //DAJ -1FIX + Buddy_handle[0] = Objects[objnum].handle; + ObjGhostObject(objnum); + } + } + else if(Netgame.flags & NF_ALLOWGUIDEBOT) + { + int i; + + for(i = 0; i < MAX_PLAYERS; i++) + { + if(Netgame.local_role==LR_CLIENT) + { + Buddy_handle[i] = OBJECT_HANDLE_NONE; + }else + { + int parent_handle; + + if(Players[i].objnum < 0 || Players[i].objnum > Highest_object_index || Objects[Players[i].objnum].type == OBJ_NONE) + { + parent_handle = OBJECT_HANDLE_NONE; + } + else + { + parent_handle = Objects[Players[i].objnum].handle; + } + + //BLACKPYRO + int objnum = ObjCreate(OBJ_ROBOT, ROBOT_GUIDEBOT, Player_object->roomnum, &Player_object->pos, NULL, parent_handle); + if(objnum > -1) { //DAJ -1FIX + Buddy_handle[i] = Objects[objnum].handle; + ObjGhostObject(objnum); + } + } + } + } + + mprintf((0, "Done Initializing AI systems\n")); +} + +void AICheckTargetVis(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + object *target = ObjGet(ai_info->target_handle); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 1\n")); + } + #endif + + + #ifdef _DEBUG + if(!Game_do_ai_vis) + { + ai_info->status_reg &= ~AISR_SEES_GOAL; + ai_info->last_see_target_pos = obj->pos; + return; + } + #endif + + if(target == NULL) + { + ai_info->status_reg &= ~AISR_SEES_GOAL; + ai_info->last_see_target_pos = obj->pos; + return; + } + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 2\n")); + } + #endif + + if(!BOA_IsVisible(obj->roomnum, target->roomnum)) + { + ai_info->status_reg &= ~AISR_SEES_GOAL; + return; + } + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 3\n")); + } + #endif + + vector pos; + AIDetermineAimPoint(obj, target, &pos); + ai_info->vec_to_target_actual = pos - obj->pos; + ai_info->dist_to_target_actual = vm_NormalizeVector(&ai_info->vec_to_target_actual); + ai_info->dist_to_target_actual -= (obj->size + target->size); + if(ai_info->dist_to_target_actual < 0.0f) + ai_info->dist_to_target_actual = 0.0f; + + vector fov_vec; + AIDetermineFovVec(obj, &fov_vec); + + if(ai_info->vec_to_target_actual * fov_vec < ai_info->fov) + { + ai_info->status_reg &= ~AISR_SEES_GOAL; + return; + } + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 4\n")); + } + #endif + + if(ai_info->dist_to_target_actual > MAX_TRACK_TARGET_DIST * Diff_ai_vis_dist[DIFF_LEVEL] && (!ObjGet(ai_info->target_handle) || (obj->roomnum != ObjGet(ai_info->target_handle)->roomnum))) + { + ai_info->status_reg &= ~AISR_SEES_GOAL; + return; + } + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 5\n")); + } + #endif + + if(ai_info->awareness == AWARE_NONE && (target->roomnum != obj->roomnum) && ai_info->dist_to_target_actual > MAX_SEE_TARGET_DIST * Diff_ai_vis_dist[DIFF_LEVEL]) + { + ai_info->status_reg &= ~AISR_SEES_GOAL; + return; + } + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 6\n")); + } + #endif + + if((ai_info->dist_to_target_actual > MAX_SEE_TARGET_DIST * Diff_ai_vis_dist[DIFF_LEVEL] && ai_info->awareness <= AWARE_BARELY && (target->roomnum != obj->roomnum)) || + (target->type == OBJ_PLAYER && (Players[target->id].flags & (PLAYER_FLAGS_DEAD | PLAYER_FLAGS_DYING))) || target->type == OBJ_GHOST || + !AIDetermineObjVisLevel(obj, target)) + { +//. mprintf((0, "No check vis\n")); + ai_info->status_reg &= ~AISR_SEES_GOAL; + return; + } + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 7\n")); + } + #endif + + // Can I see the target? + if(Gametime - ai_info->last_see_target_time > MIN_VIS_RECENT_CHECK_INTERVAL) + { + if(Gametime >= ai_info->next_check_see_target_time) + { +// float dist; + fvi_info hit_info; + fvi_query fq; + int fate; + + //Project a ray and see if target is around. -- We can use a quick room check to see if we should even do it. :) --chrishack (do this later when room structure is in the game) + // if we are in the same room, see see the target + //Do FVI_stuff (maybe just a room connection check) + + // shoot a ray from the light position to the current vertex + fq.p0 = &obj->pos; + fq.p1 = &target->pos; + fq.startroom = obj->roomnum; + + fq.rad = 0.0f; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_NO_RELINK/* | FQ_IGNORE_MOVING_OBJECTS*/; + if(ai_info->agression > .7f) + { + fq.flags |= FQ_IGNORE_MOVING_OBJECTS; + } + + fq.thisobjnum = -1; + + int ignore_obj_list[100]; + ignore_obj_list[0] = OBJNUM(obj); + int num_ignored = 1; + int i; + + // CHRISHACK - ONLY IGNORES FIRST LEVEL OF CHILDREN - DO RECERSIVE + for(i = 0; i < Poly_models[obj->rtype.pobj_info.model_num].n_attach; i++) + { + object *child; + + if((child = ObjGet(obj->attach_children[i])) != NULL && num_ignored < 99) + { + ignore_obj_list[num_ignored++] = OBJNUM(child); + } + } + + ignore_obj_list[num_ignored] = -1; + fq.ignore_obj_list = ignore_obj_list; + + fate = fvi_FindIntersection(&fq, &hit_info); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis 8\n")); + } + #endif + + if(((fate == HIT_OBJECT || fate == HIT_SPHERE_2_POLY_OBJECT) && hit_info.hit_object[0] == OBJNUM(target)) || (fate == HIT_NONE)) + { + ai_info->status_reg |= AISR_SEES_GOAL; // chrishack -- need to do this stuff correctly + //if(ai_info->highest_vis > ) chrishack -- need to do this stuff + + AINotify(obj, AIN_SEE_TARGET, target); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Vis SEE TARGET\n")); + } + #endif + } + + if((ai_info->status_reg & AISR_SEES_GOAL) || Gametime - ai_info->last_see_target_time < CHECK_VIS_INFREQUENTLY_TIME) + ai_info->next_check_see_target_time = Gametime + .9 * MIN_VIS_CHECK_INTERVAL + .2 * MIN_VIS_CHECK_INTERVAL * ((float)ps_rand()/(float)RAND_MAX); + else + ai_info->next_check_see_target_time = Gametime + .9 * CHECK_VIS_INFREQUENTLY_INTERVAL + .2 * CHECK_VIS_INFREQUENTLY_INTERVAL * ((float)ps_rand()/(float)RAND_MAX); + } + } + + if((ai_info->status_reg & AISR_SEES_GOAL)) + { + ai_info->vec_to_target_perceived = ai_info->vec_to_target_actual; + ai_info->dist_to_target_perceived = ai_info->dist_to_target_actual; + + // Assumes the the AI and the target are on the same XZ plane +/* if(ai_info->flags & AIF_XZ_DIST) + { + vector xz; + + xz = ai_info->vec_to_target_perceived * ai_info->dist_to_target_perceived; + xz.y = 0.0f; // chrishack - E3 - it this a good way to do this? + +// vm_NormalizeVector(&ai_info->vec_to_target_perceived); + ai_info->dist_to_target_perceived = vm_GetMagnitude(&xz); + }*/ + } +} + +bool AIStatusCircleFrame(object *obj, object *g_obj, float dist, float c_dist, int *status_reg) +{ + if((*status_reg) & AISR_CIRCLE_DIST) + { + if(dist > c_dist * 1.5f || + (obj->ai_info->awareness <= AWARE_BARELY && !(obj->ai_info->flags & AIF_PERSISTANT))) + { + (*status_reg) &= ~AISR_CIRCLE_DIST; + return false; + } + else + { + return true; + } + } + else if(dist <= c_dist + .1f && + (obj->ai_info->awareness >= AWARE_BARELY || (obj->ai_info->flags & AIF_PERSISTANT))) + { + (*status_reg) |= AISR_CIRCLE_DIST; + + if(g_obj) + { + AINotify(obj, AIN_NEAR_TARGET, (void *)g_obj); + } + + return true; + } + else + { + return false; + } +} + +bool ai_target_need_path(object *obj) +{ + return true; +} + +bool ai_move_need_path(object *obj, vector *pos, int roomnum) +{ + if(obj->roomnum == roomnum) + { + return false; + } + + return true; +} + +void ai_update_registers(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + + if(ObjGet(ai_info->target_handle)) + { + AIStatusCircleFrame(obj, ObjGet(ai_info->target_handle), ai_info->dist_to_target_perceived, ai_info->circle_distance, &ai_info->status_reg); + } + else + { + ai_info->status_reg &= ~AISR_CIRCLE_DIST; + } + + ai_info->status_reg &= ~AISR_MELEE; +} + +bool AiGoalAvoid(vector *adir, object *obj, object *a_obj, float dist) +{ + float full_dist = dist; + vector a_vel; + + if(obj->movement_type != MT_PHYSICS && obj->movement_type != MT_WALKING) + return false; + + if(a_obj->movement_type == MT_PHYSICS || a_obj->movement_type == MT_WALKING) + a_vel = a_obj->mtype.phys_info.velocity; + else + a_vel = Zero_vector; + + vector to_avoid = a_obj->pos - obj->pos; + vector mdir; + mdir = obj->mtype.phys_info.velocity - a_vel; + + if(fabsf(mdir.x) < 0.1f && fabsf(mdir.y) < 0.1f && fabsf(mdir.z) < 0.1f ) + { + *adir = -to_avoid; + vm_NormalizeVector(adir); + + return true; + } + + vm_NormalizeVector(&mdir); + float temp = mdir * to_avoid; + + if(temp <= 0.0f) + { + *adir = -to_avoid; + vm_NormalizeVector(adir); + + return true; + } + + vector vtemp = -(to_avoid - temp * mdir); + +// float cdist = vm_NormalizeVector(&vtemp); +// if(cdist >= full_dist) +// return false; + + vector apnt = a_obj->pos + full_dist * vtemp; + + *adir = apnt - obj->pos; + vm_NormalizeVector(adir); + + return true; +} + +inline bool IsTargetLocal(object *obj, object *target) +{ + int target_room = target->roomnum; + int cur_room = obj->roomnum; + int i; + + // If they are in the same room + if(target_room = obj->roomnum) + { + return true; + } + + // If the object is outside + if(OBJECT_OUTSIDE(obj)) + { + return false; + } + + // If they are in directly adjacent rooms + for(i = 0; i < Rooms[cur_room].num_portals; i++) + { + if(Rooms[cur_room].portals[i].croom == target_room) + { + return true; + } + } + + return false; +} + +#define AVOID_PLAYER_MAX_SCALAR 3.0f +#define AVOID_PLAYER_MIN_SCALAR 2.0f + +#define ALIGNMENT_OPTI_DIST 16.0f +#define MAX_ALIGNMENT_DIST 6.0f + +#define COHESION_OPTI1_DIST 90.0f +#define COHESION_OPTI2_DIST 90.0f +#define COHESION_FALL_OFF 110.0f + +float AIGoalIsEnabledForDist(goal *goal, float dist) +{ + return true; // chrishack -- test code -- temp +} + +void AIGoalDoRepulse(object *obj, float dist, vector *dir, goal *goal, vector *mdir) +{ + float influence = 2.0f; // chrishack - temp - test code + + if(/*scale = AIGoalIsEnabledForDist(goal, dist)*/1) // chrishack - temp - test code - works like an enabler What about AIDetermineScale???? + { + if(dist <= 12.0f) // chrishack - temp - test code + { + float scale; + + if(dist < 9.0f) + scale = 1.0f; + else + scale = 1.0f - (dist - 9.0f)/3.0; + + *mdir += (*dir * -scale) * influence; + } + } +} + +void AIGoalDoCohesion(object *obj, object *g_obj, float dist, goal *goal, vector *mdir) +{ + float influence = 2.0f; // chrishack - temp - test code + + if(/*scale = AIGoalIsEnabledForDist(goal, dist)*/1) // chrishack - temp - test code - works like an enabler What about AIDetermineScale???? + { + if(dist >= COHESION_OPTI1_DIST - COHESION_FALL_OFF && + dist <= COHESION_OPTI2_DIST + COHESION_FALL_OFF) + { + vector cohesion_pnt; + float scale; + + if(dist < COHESION_OPTI1_DIST) + scale = 1.0f - (COHESION_OPTI1_DIST - dist)/COHESION_FALL_OFF; + else if( dist <= COHESION_OPTI2_DIST) + scale = 1.0f; + else + scale = 1.0f - (dist - COHESION_OPTI2_DIST)/COHESION_FALL_OFF; + + AIDetermineAimPoint(obj, g_obj, &cohesion_pnt, obj->ai_info->max_velocity); + + vector cur_cohesion_dir = cohesion_pnt - obj->pos; + vm_NormalizeVector(&cur_cohesion_dir); + *mdir += (cur_cohesion_dir * scale) * influence; + } + } +} + +void AIGoalDoAlignment(object *obj, float dist, vector *fvec, goal *goal, vector *mdir) +{ + float influence = 3.0f; // chrishack - temp - test code + + if(/*scale = AIGoalIsEnabledForDist(goal, dist)*/1) // chrishack - temp - test code - works like an enabler What about AIDetermineScale???? + { + float align_dist = fabs(dist - ALIGNMENT_OPTI_DIST); + if(MAX_ALIGNMENT_DIST >= align_dist) + { + float scale = 1.0f - (align_dist/MAX_ALIGNMENT_DIST); + *mdir += (*fvec * scale) * influence; + } + } +} + +void AIDoTrackFrame(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + float track_distance; + + AI_FriendNumNear = 0; + AI_EnemyNumNear = 0; + + if(ai_info->ai_type == AIT_BIRD_FLOCK1 || ai_info->flags & (AIF_TRACK_CLOSEST_2_FRIENDS | AIF_TRACK_CLOSEST_2_ENEMIES)) + { + + if(ai_info->ai_type == AIT_BIRD_FLOCK1) + track_distance = 100.0f; + else + track_distance = GoalDetermineTrackDist(obj); + + if(track_distance > 0.0f) + { + short near_objs[10]; + int num_near = fvi_QuickDistObjectList(&obj->pos, obj->roomnum, ai_info->avoid_friends_distance + track_distance, near_objs, 10, false, true, false, true); + float dist; + vector to; + + AI_EnemyDist[0] = AI_EnemyDist[1] = 9999999.0f; + AI_FriendDist[0] = AI_FriendDist[1] = 9999999.0f; + + for(int xxx = 0; xxx < num_near; xxx++) + { + object *g_obj = &Objects[near_objs[xxx]]; + + if(g_obj->type == OBJ_POWERUP || (obj == g_obj) || ObjectsAreRelated(OBJNUM(obj), near_objs[xxx])) + continue; + + int *cur_num_near; + object **cur_obj; + float *cur_dist; + vector *cur_dir; + + if((ai_info->flags & AIF_TRACK_CLOSEST_2_ENEMIES) && AIObjEnemy(obj, g_obj)) + { + cur_num_near = &AI_EnemyNumNear; + cur_obj = AI_EnemyObj; + cur_dist = AI_EnemyDist; + cur_dir = AI_EnemyDir; + } + else if((ai_info->flags & AIF_TRACK_CLOSEST_2_FRIENDS) && AIObjFriend(obj, g_obj)) + { + cur_num_near = &AI_FriendNumNear; + cur_obj = AI_FriendObj; + cur_dist = AI_FriendDist; + cur_dir = AI_FriendDir; + } + else + continue; + + to = g_obj->pos - obj->pos; + dist = vm_NormalizeVector(&to); + + dist -= (obj->size + g_obj->size); + if(dist < 0.0f) + dist = 0.0f; + + vector fov_vec; + AIDetermineFovVec(obj, &fov_vec); + + if(to * fov_vec < ai_info->fov) + continue; + + if(cur_dist[0] > dist) + { + cur_dist[1] = cur_dist[0]; + cur_obj[1] = cur_obj[0]; + cur_dir[1] = cur_dir[0]; + + cur_dist[0] = dist; + cur_obj[0] = g_obj; + cur_dir[0] = to; + + (*cur_num_near)++; + } + else if(cur_dist[1] > dist) + { + cur_dist[1] = dist; + cur_obj[1] = g_obj; + cur_dir[1] = to; + + (*cur_num_near)++; + } + + if(*cur_num_near > 2) + *cur_num_near = 2; + } + } + } +} + +float AIDetermineGoalInfluence(object *obj, goal *goal) +{ + float influence = goal->influence; + int g_index = goal - obj->ai_info->goals; + + if(goal->flags & GF_SCRIPTED_INFLUENCE) + { + AINotify(obj, AIN_SCRIPTED_INFLUENCE, &g_index); + influence = goal->influence; + } + else if(goal->flags & GF_RAMPED_INFLUENCE) + { + float scale; + const float dist = goal->dist_to_goal; // chrishack -- dist to goal must be properly updated for TRACK goals + + if(dist < goal->ramp_influence_dists[1]) + { + float delta = dist - goal->ramp_influence_dists[0]; + float width = goal->ramp_influence_dists[1] - goal->ramp_influence_dists[0]; + + if(width > 0.0f) + scale = delta/width; + else + scale = 0.0f; + } + else if(dist <= goal->ramp_influence_dists[2]) + { + scale = 1.0f; + } + else + { + float delta = goal->ramp_influence_dists[3] - dist; + float width = goal->ramp_influence_dists[3] - goal->ramp_influence_dists[2]; + + if(width > 0.0f) + scale = delta/width; + else + scale = 0.0f; + } + + influence *= scale; + } + + if((goal->flags & GF_MIN_MAX_INFLUENCE) && influence < goal->min_influence) + { + influence = goal->min_influence; + } + + return influence; +} + +void AIDoOrientVelocity(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + + vector dir = obj->mtype.phys_info.velocity; + float mag = vm_NormalizeVector(&dir); + + if(mag > 0.1f) + { + AITurnTowardsDir(obj, &dir, ai_info->max_turn_rate); + } +} + +void AIDoOrientDefault(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + + if((obj->movement_type != MT_WALKING && obj->movement_type != MT_PHYSICS) || !(ai_info)) + { + return; + } + + // The aware barely stuff can be improved by using emotional sliders to determine if the robot will + // 'lose track' of its target -- chrishack -- also use last see target time with the agression slider + if((ai_info->flags & AIF_ORIENT_TO_VEL) || (ai_info->awareness <= AWARE_BARELY && ai_info->agression < 0.4f) || !ObjGet(ai_info->target_handle)) + { + AIDoOrientVelocity(obj); + } + else + { + vector to = ai_info->last_see_target_pos - obj->pos; + float dist = vm_NormalizeVector(&to); + + if(dist > .1f) + AITurnTowardsDir(obj, &to, ai_info->max_turn_rate); + } +} + +extern bool AIPathAtEnd(ai_path_info *aip); +extern bool AIPathAtStart(ai_path_info *aip); +extern bool AIPathGetNextNodePos (ai_path_info *aip, vector *pos, int *room = NULL); +extern bool AIPathGetPrevNodePos (ai_path_info *aip, vector *pos, int *room = NULL); +extern bool AIPathGetCurrentNodePos(ai_path_info *aip, vector *pos, int *room = NULL); + +void AIDoOrient(object *obj, int g_index) +{ + ai_frame *ai_info = obj->ai_info; + goal *goal_ptr = &ai_info->goals[g_index]; + + if((obj->movement_type != MT_WALKING && obj->movement_type != MT_PHYSICS) || !(ai_info)) + { + return; + } + + if((g_index >= 0) && ISORIENTGOAL(&ai_info->goals[g_index])) + { + object *g_obj = NULL; + + if(goal_ptr->flags & GF_ORIENT_FOR_ATTACH) + { + Int3(); // chrishack -- not done + } + else if(goal_ptr->flags & GF_ORIENT_TARGET) + { + target_goal: + + g_obj = ObjGet(ai_info->target_handle); + if(g_obj) + { + vector to = ai_info->last_see_target_pos - obj->pos; + float dist = vm_NormalizeVector(&to); + + if(dist > .1f) + AITurnTowardsDir(obj, &to, ai_info->max_turn_rate); + } + else + { + AIDoOrientVelocity(obj); + } + } + else if(OBJGOAL(goal_ptr) && (goal_ptr->flags & GF_ORIENT_GOAL_OBJ)) + { + object *t_obj = ObjGet(ai_info->target_handle); + g_obj = ObjGet(goal_ptr->g_info.handle); + + if(g_obj && (g_obj == t_obj)) + goto target_goal; + + if(g_obj) + { + vector to = g_obj->pos - obj->pos; + float dist = vm_NormalizeVector(&to); + + if(dist > .1f) + AITurnTowardsDir(obj, &to, ai_info->max_turn_rate); + } + else + { + AIDoOrientVelocity(obj); + } + } + else if(goal_ptr->flags & GF_ORIENT_VELOCITY) + { + AIDoOrientVelocity(obj); + } + else if(goal_ptr->flags & GF_ORIENT_SCRIPTED) + { + AINotify(obj, AIN_SCRIPTED_ORIENT, &g_index); + } + else if(goal_ptr->flags & GF_ORIENT_PATH_NODE) + { + vector uvec = obj->orient.uvec; + vector fvec = obj->orient.fvec; + + ai_path_info *aip = &obj->ai_info->path; + + int n = aip->cur_node; + int p = aip->cur_path; + + int p_id = aip->path_id[p]; + + bool f_reverse = (goal_ptr->flags & GF_PATH_MOVE_REVERSE_DIR) != 0; + + if(aip->path_type[p] == AIP_STATIC) + { + game_path *gp = &GamePaths[p_id]; + vector cur_pos; + AIPathGetCurrentNodePos(aip, &cur_pos); + + if((f_reverse) && (!AIPathAtEnd(aip))) + { + vector next_pos; + AIPathGetNextNodePos(aip, &next_pos, NULL); + + vector proj = obj->pos - next_pos; + vector line = cur_pos - next_pos; + float line_len = vm_NormalizeVector(&line); + float proj_len = proj * line; + + if(proj_len > 0.0) + { + if(proj_len > line_len) return; // We should have updated nodes + + float scale1 = proj_len/line_len; + float scale2 = 1.0f - scale1; + + fvec = (scale1 * gp->pathnodes[n].fvec + scale2 * gp->pathnodes[n + 1].fvec); + uvec = (scale1 * gp->pathnodes[n].uvec + scale2 * gp->pathnodes[n + 1].uvec); + } + } + else if((!f_reverse) && (!AIPathAtStart(aip))) + { + vector prev_pos; + AIPathGetPrevNodePos(aip, &prev_pos, NULL); + + vector proj = obj->pos - prev_pos; + vector line = cur_pos - prev_pos; + float line_len = vm_NormalizeVector(&line); + float proj_len = proj * line; + + if(proj_len > 0.0) + { + if(proj_len > line_len) return; // We should have updated nodes + + float scale1 = proj_len/line_len; + float scale2 = 1.0f - scale1; + + fvec = (scale1 * gp->pathnodes[n].fvec + scale2 * gp->pathnodes[n - 1].fvec); + uvec = (scale1 * gp->pathnodes[n].uvec + scale2 * gp->pathnodes[n - 1].uvec); + } + } + + matrix orient; + vm_VectorToMatrix(&orient, &fvec, &uvec, NULL); + + AITurnTowardsMatrix(obj, ai_info->max_turn_rate, &orient); + } + } + else if(goal_ptr->flags & GF_ORIENT_SET_FVEC_UVEC) + { + matrix orient; + vm_VectorToMatrix(&orient, &goal_ptr->set_fvec, &goal_ptr->set_uvec, NULL); + + AITurnTowardsMatrix(obj, ai_info->max_turn_rate, &orient); + } + else if(goal_ptr->flags & GF_ORIENT_SET_FVEC) + { + AITurnTowardsDir(obj, &goal_ptr->set_fvec, ai_info->max_turn_rate); + } + else + { + AIDoOrientDefault(obj); + } + } + else + { + AIDoOrientDefault(obj); + } +} + +void AIDetermineSpeed(object *obj, int flags, float *speed) +{ + float n_speed = 1.0f; + + switch(flags & GF_SPEED_MASK) + { + case GF_SPEED_ATTACK: + n_speed = obj->ai_info->attack_vel_percent; + break; + case GF_SPEED_DODGE: + n_speed = obj->ai_info->dodge_vel_percent; + break; + case GF_SPEED_FLEE: + n_speed = obj->ai_info->flee_vel_percent; + break; + } + + if(n_speed > *speed) + { + *speed = n_speed; + } +} + +void ai_move(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + int i; + bool f_dodge = false; // Are there any dodge goals + bool f_avoid = false; // Are there any avoid goals + int highest_influence_goal = -1; + float highest_influence = 0.0f; + float highest_speed = 0.0f; + bool f_turned = true; + bool f_goal_found = false; + + // Post project GB Hacks to make him more combat friendly... + if(IS_GUIDEBOT(obj)) + { + if(ai_info->flags & AIF_GB_MIMIC_PLAYER_FIRING_HACK) + ai_info->flags &= ~AIF_DODGE; + else + ai_info->flags |= AIF_DODGE; + } + //int num_objects; + //object *g_objs[5]; // 1 target + 2 enemies + 2 freinds + + object *targetptr = ObjGet(ai_info->target_handle); // The target of this AI + ai_info->movement_dir = Zero_vector; + + // Hacked flocking code + if(ai_info->ai_type == AIT_BIRD_FLOCK1) + { + vector composite_dir = Zero_vector; + + for(int temp = 0; temp < AI_FriendNumNear; temp++) + { + object *g_obj = AI_FriendObj[temp]; + + ASSERT(g_obj->type != OBJ_WEAPON); + ASSERT(g_obj != obj); + + AIGoalDoRepulse(obj, AI_FriendDist[temp], &AI_FriendDir[temp], NULL, &composite_dir); + AIGoalDoAlignment(obj, AI_FriendDist[temp], &g_obj->orient.fvec, NULL, &composite_dir); + AIGoalDoCohesion(obj, g_obj, AI_FriendDist[temp], NULL, &composite_dir); + f_goal_found = true; + } + + // Facing code + if(composite_dir == Zero_vector) + composite_dir = obj->orient.fvec; + + // FLOCK HEIGHT CODE + if(ROOMNUM_OUTSIDE(obj->roomnum) && (ai_info->flags & AIF_BIASED_FLIGHT_HEIGHT)) + { + composite_dir.y *= .5f; + + float delta = obj->pos.y - GetTerrainGroundPoint(&obj->pos) - obj->size; + if(delta < ai_info->biased_flight_min) + { + if(composite_dir.y < 0.0) + composite_dir.y *= .5f; + else + composite_dir.y *= 4.0f; + } + else if(delta > ai_info->biased_flight_max) + { + if(composite_dir.y < 0.0) + composite_dir.y *= 4.0f; + else + composite_dir.y *= 0.5f; + } + + float max_fly_upward_height = ai_info->biased_flight_min * ai_info->biased_flight_importance; + if(delta < max_fly_upward_height) + { + composite_dir.y += (1.0f - (delta/max_fly_upward_height)) * ai_info->biased_flight_importance; + } + } + + vm_NormalizeVector(&composite_dir); + + AIMoveTowardsDir(obj, &composite_dir, 1.0f); + AITurnTowardsDir(obj, &composite_dir, ai_info->max_turn_rate); + return; + } + + // Stop objects that have not been active lately + if(!(ai_info->flags & AIF_PERSISTANT) && + Gametime - ai_info->last_see_target_time > CHECK_VIS_INFREQUENTLY_TIME && + Gametime - ai_info->last_hear_target_time > CHECK_VIS_INFREQUENTLY_TIME && + ai_info->awareness == AWARE_NONE) + { + obj->mtype.phys_info.velocity = Zero_vector; + obj->mtype.phys_info.rotvel = Zero_vector; + } + else + { + // Determine movement stuff + if(obj->movement_type == MT_PHYSICS || obj->movement_type == MT_WALKING) + { + if(targetptr && IsTargetLocal(obj, targetptr) || + (ai_info->awareness >= AWARE_BARELY) || + (ai_info->flags & AIF_PERSISTANT)) + { + for(i = NUM_ACTIVATION_LEVELS; i < MAX_GOALS; i++) + { + if(ai_info->goals[i].used) + { + if(GoalIsGoalEnabled(obj, i)) + { + if(ai_info->goals[i].type == AIG_DODGE_OBJ) + { + f_goal_found = true; + if(goal_do_dodge(obj, i)) + { + f_dodge = true; + AIDetermineSpeed(obj, ai_info->goals[i].flags, &highest_speed); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Dodging\n")); + } + #endif + } + } + else if(ai_info->goals[i].type == AIG_MELEE_TARGET) + { + f_goal_found = true; + AiMelee(obj); + AIDetermineSpeed(obj, ai_info->goals[i].flags, &highest_speed); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Melee\n")); + } + #endif + } + else if(ai_info->goals[i].type == AIG_GET_AROUND_OBJ) + { + f_goal_found = true; + + goal *cur_goal = &ai_info->goals[i]; + object *g_obj = ObjGet(cur_goal->g_info.handle); + float dist = cur_goal->circle_distance; + + vector adir; + + if(!g_obj || (g_obj == obj) || ObjectsAreRelated(OBJNUM(obj), OBJNUM(g_obj))) + continue; + + float cur_dist = vm_VectorDistance(&obj->pos, &g_obj->pos) - obj->size - g_obj->size; + if(cur_dist < 0.0) + cur_dist = 0.0f; + + if(cur_dist > dist) + continue; + + // Scale the life perservation too (if enemy DAMN GOOD IDEA - expand AVOID SIZE too) -- chrishack + // Linear scale by distance + float scale = cur_goal->influence * ((dist - cur_dist)/dist); + + if(AiGoalAvoid(&adir, obj, g_obj, dist)) + { + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Avoiding object\n")); + } + #endif + + ai_info->movement_dir += (adir * scale); + f_avoid = true; + AIDetermineSpeed(obj, ai_info->goals[i].flags, &highest_speed); + } + } + } + } + } + + if (ai_info->flags & AIF_AUTO_AVOID_FRIENDS) + { + object *g_obj; + vector adir; + + f_goal_found = true; + + short near_objs[10]; + + float avoid_dist; + + avoid_dist = ai_info->avoid_friends_distance; + + // We avoid friends more when we are in close proximity of our target + if(AIObjEnemy(obj, ObjGet(ai_info->target_handle)) && (ai_info->status_reg & AISR_CIRCLE_DIST)) + avoid_dist *= 2.3f; + + int num_near = fvi_QuickDistObjectList(&obj->pos, obj->roomnum, ai_info->avoid_friends_distance + obj->size, near_objs, 10, false, true); + + for(int temp = 0; temp < num_near; temp++) + { + g_obj = &Objects[near_objs[temp]]; + + if(g_obj->type == OBJ_POWERUP || (g_obj == obj) || ObjectsAreRelated(OBJNUM(obj), near_objs[temp])) + continue; + + if(!AIObjFriend(obj, g_obj)) + continue; + + float cur_dist = vm_VectorDistance(&obj->pos, &g_obj->pos) - obj->size - g_obj->size; + if(cur_dist < 0.0) + cur_dist = 0.0f; + + if(cur_dist > ai_info->avoid_friends_distance) + continue; + + if(AiGoalAvoid(&adir, obj, g_obj, ai_info->avoid_friends_distance)) + { + float scale = 1.0f - cur_dist/ai_info->avoid_friends_distance; + if(scale < 0.01f) + scale = 0.01f; + + ai_info->movement_dir += (adir * scale); + f_avoid = true; + AIDetermineSpeed(obj, GF_SPEED_NORMAL, &highest_speed); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Auto avoid friends.\n")); + } + #endif + } + } + } + + if(ai_info->flags & AIF_AVOID_WALLS) + { + if(goal_do_avoid_walls(obj, &ai_info->movement_dir)) + { + f_avoid = true; + AIDetermineSpeed(obj, GF_SPEED_NORMAL, &highest_speed); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: Avoiding walls\n")); + } + #endif + } + } + + goal *cur_goal = GoalGetCurrentGoal(obj); + vector goal_mdir; + bool goal_f_moved = false; + bool goal_mset = false; + + if(cur_goal) + { + f_goal_found = true; + highest_influence_goal = cur_goal - ai_info->goals; + f_turned = false; + AIDetermineSpeed(obj, cur_goal->flags, &highest_speed); + + switch(cur_goal->type) + { + case AIG_SCRIPTED: + { + int g_index = cur_goal - ai_info->goals; + AINotify(obj, AIN_SCRIPTED_GOAL, &g_index); // Assume that it modifies the movement vec + + int goal_mscale = vm_NormalizeVector(&ai_info->movement_dir); + goal_mdir = ai_info->movement_dir; + + if(goal_mscale > highest_speed) + { + highest_speed = goal_mscale; + } + + goal_mset = true; + } + break; + + case AIG_GUARD_AREA: // Chrishack -- this goal could probably be removed + { + if(!(cur_goal->status_reg & AISR_CIRCLE_DIST)) + { + if(cur_goal->g_info.roomnum != obj->roomnum) + { + cur_goal->g_info.roomnum = obj->roomnum; + cur_goal->g_info.pos = obj->pos; + } + + AIMoveTowardsPosition(obj, &cur_goal->g_info.pos, 1.0f, false, &goal_mdir, &goal_f_moved); + goal_mset = true; + } + } + break; + + case AIG_GET_TO_OBJ: + case AIG_GET_TO_POS: + case AIG_WANDER_AROUND: + case AIG_FOLLOW_PATH: + case AIG_PLACE_OBJ_ON_OBJ: + case AIG_ATTACH_TO_OBJ: + { + if((cur_goal->type == AIG_ATTACH_TO_OBJ || cur_goal->type == AIG_PLACE_OBJ_ON_OBJ) && (cur_goal->status_reg & AISR_CIRCLE_DIST)) + { + object *g_obj = ObjGet(cur_goal->g_info.handle); + object *child_obj = NULL; + + if(cur_goal->type == AIG_PLACE_OBJ_ON_OBJ) + { + child_obj = ObjGet(obj->attach_children[0]); + + if(child_obj == NULL) + { + cur_goal->flags &= ~(GF_NONFLUSHABLE | GF_KEEP_AT_COMPLETION); + GoalClearGoal(obj, cur_goal, AIN_GOAL_INVALID); + return; + } + } + + vector pos; + matrix orient; + char p_ap = cur_goal->g_info.attach_info.parent_ap; + char c_ap = cur_goal->g_info.attach_info.child_ap; + float rad = cur_goal->g_info.attach_info.rad; + + // Investigate a better means of doing this... + if(!(obj->mtype.phys_info.flags & PF_NO_ROBOT_COLLISIONS)) + { + obj->mtype.phys_info.flags |= PF_NO_ROBOT_COLLISIONS; + cur_goal->g_info.attach_info.flags |= GAF_TEMP_CLEAR_ROBOT_COLLISIONS; + } + if(!(obj->mtype.phys_info.flags & PF_POINT_COLLIDE_WALLS)) + { + obj->mtype.phys_info.flags |= PF_POINT_COLLIDE_WALLS; + cur_goal->g_info.attach_info.flags |= GAF_TEMP_POINT_COLLIDE_WALLS; + } + if((cur_goal->g_info.attach_info.flags & GAF_ALIGNED)&&(obj->mtype.phys_info.flags & PF_LEVELING)) + { + obj->mtype.phys_info.flags &= (~PF_LEVELING); + cur_goal->g_info.attach_info.flags |= GAF_TEMP_CLEAR_AUTOLEVEL; + } + + //Determine that attach pos and orient + if((cur_goal->g_info.attach_info.flags & GAF_ALIGNED) && AttachDoPosOrient(obj, p_ap, g_obj, c_ap, true, false, &pos, &orient, (cur_goal->type == AIG_PLACE_OBJ_ON_OBJ))) + { + bool f_at_pos = AIMoveTowardsPosition(obj, &pos, 1.0f, true, &goal_mdir, &goal_f_moved); + goal_mset = true; + if(!goal_f_moved) + { + AIMoveTowardsDir(obj, &goal_mdir); + } + + bool f_orient = AITurnTowardsMatrix(obj, obj->ai_info->max_turn_rate, &orient); + + if(f_at_pos && f_orient) + { + int goal_index = cur_goal - ai_info->goals; + + if(cur_goal->type == AIG_PLACE_OBJ_ON_OBJ) + { + UnattachFromParent(child_obj); + AttachObject(g_obj, c_ap, child_obj, p_ap, true); + } + else + { + if(cur_goal->flags & GF_IS_ATTACH_CHILD) + AttachObject(g_obj, c_ap, obj, p_ap, true); + else + AttachObject(obj, p_ap, g_obj, c_ap, true); + } + + if(cur_goal->g_info.attach_info.flags & GAF_TEMP_CLEAR_ROBOT_COLLISIONS) + { + obj->mtype.phys_info.flags &= (~PF_NO_ROBOT_COLLISIONS); + } + if(cur_goal->g_info.attach_info.flags & GAF_TEMP_POINT_COLLIDE_WALLS) + { + obj->mtype.phys_info.flags &= (~PF_POINT_COLLIDE_WALLS); + } + if(cur_goal->g_info.attach_info.flags & GAF_TEMP_CLEAR_AUTOLEVEL) + { + obj->mtype.phys_info.flags |= PF_LEVELING; + } + cur_goal->g_info.attach_info.flags &= ~(GAF_TEMP_CLEAR_AUTOLEVEL|GAF_TEMP_POINT_COLLIDE_WALLS|GAF_TEMP_CLEAR_ROBOT_COLLISIONS); + + cur_goal->flags &= ~(GF_NONFLUSHABLE | GF_KEEP_AT_COMPLETION); + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } + } + else if((cur_goal->g_info.attach_info.flags & GAF_SPHERE) && AttachDoPosOrientRad(obj, p_ap, g_obj, rad, &pos)) + { + bool f_at_pos = AIMoveTowardsPosition(obj, &pos, 1.0f, true, &goal_mdir, &goal_f_moved); + goal_mset = true; + if(!goal_f_moved) + { + AIMoveTowardsDir(obj, &goal_mdir); + } + + if(f_at_pos) + { + int goal_index = cur_goal - ai_info->goals; + + AttachObject(obj, p_ap, g_obj, rad); + + if(cur_goal->g_info.attach_info.flags & GAF_TEMP_CLEAR_ROBOT_COLLISIONS) + { + obj->mtype.phys_info.flags &= (~PF_NO_ROBOT_COLLISIONS); + } + if(cur_goal->g_info.attach_info.flags & GAF_TEMP_POINT_COLLIDE_WALLS) + { + obj->mtype.phys_info.flags &= (~PF_POINT_COLLIDE_WALLS); + } + if(cur_goal->g_info.attach_info.flags & GAF_TEMP_CLEAR_AUTOLEVEL) + { + obj->mtype.phys_info.flags |= PF_LEVELING; + } + cur_goal->g_info.attach_info.flags &= ~(GAF_TEMP_CLEAR_AUTOLEVEL|GAF_TEMP_POINT_COLLIDE_WALLS|GAF_TEMP_CLEAR_ROBOT_COLLISIONS); + + cur_goal->flags &= ~(GF_NONFLUSHABLE | GF_KEEP_AT_COMPLETION); + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } + } + else + { + cur_goal->flags &= ~(GF_NONFLUSHABLE | GF_KEEP_AT_COMPLETION); + GoalClearGoal(obj, cur_goal, AIN_GOAL_INVALID); + } + return; + } + else if(ai_info->path.num_paths && !((cur_goal->status_reg & AISR_SEES_GOAL) && (cur_goal->flags & GF_USE_BLINE_IF_SEES_GOAL))) + { + AIPathMoveTurnTowardsNode(obj, &goal_mdir, &goal_f_moved); + goal_mset = true; + } + else if(cur_goal->type == AIG_GET_TO_POS || cur_goal->type == AIG_WANDER_AROUND) + { + bool result = AIMoveTowardsPosition(obj, &cur_goal->g_info.pos, 1.0f, fabs(cur_goal->circle_distance) < .001, &goal_mdir, &goal_f_moved); + goal_mset = true; + + if(result && (fabs(cur_goal->circle_distance) < .001)) + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } + else if(OBJGOAL(cur_goal)) + { + object *g_obj = ObjGet(cur_goal->g_info.handle); + + if(g_obj) + { + AIMoveTowardsPosition(obj, &g_obj->pos, 1.0f, false, &goal_mdir, &goal_f_moved); + goal_mset = true; + } + else + { + mprintf((0, "AIG Warning: No obj for GetToObj.\n")); + } + } + else if(cur_goal->type == AIG_FOLLOW_PATH) + { + mprintf((0, "AIG Warning: Follow path has no path\n")); + } + } + break; + + case AIG_MOVE_RELATIVE_OBJ_VEC: + { + object *goal_obj = ObjGet(cur_goal->g_info.handle); + float g_circle_dist = cur_goal->circle_distance; + + if(goal_obj) + { + int subtype = cur_goal->subtype; + int vec_id = (subtype & 0xFFFFFFFE); + bool f_toward = (subtype & 0x00000001); + vector *vec; + +// mprintf((0, "Moving relative a type %d\n", obj->type)); + + switch(vec_id) + { + case GST_FVEC: + vec = &goal_obj->orient.fvec; + break; + case GST_RVEC: + vec = &goal_obj->orient.rvec; + break; + case GST_UVEC: + vec = &goal_obj->orient.uvec; + break; + default: + mprintf((0, "Invalid vec in AIG_MOVE_RELATIVE_OBJ_VEC bashing to fvec\n")); + cur_goal->subtype = GST_FVEC | (int)f_toward; + vec_id = GST_FVEC; + } + + if(move_relative_object_vec(obj, vec, goal_obj, g_circle_dist, 1.0f, f_toward, &goal_mdir, &goal_f_moved)) + { + GoalClearGoal(obj, cur_goal, AIN_GOAL_COMPLETE); + } + goal_mset = true; + } + else + { + GoalClearGoal(obj, cur_goal, AIN_GOAL_INVALID); + } + } + break; + + case AIG_MOVE_RELATIVE_OBJ: // chrishack make this out of other goals + { + object *goal_obj = ObjGet(cur_goal->g_info.handle); + float g_circle_dist = cur_goal->circle_distance; + + int g_status = cur_goal->status_reg; + +// if((ai_info->ai_type == AIT_MELEE1) && (ai_info->status_reg & AISR_MELEE)) +// { +// AIMoveTowardsPosition(obj, &ai_info->last_see_target_pos, ai_info->attack_vel_percent); +// } +// else + + if(ai_info->dist_to_target_perceived < .7f * g_circle_dist) + { + move_away_from_position(obj, &ai_info->last_see_target_pos, ai_info->flee_vel_percent, &goal_mdir, &goal_f_moved); + goal_mset = true; + + if(ai_info->sound[AI_FLEE_SOUND] != SOUND_NONE_INDEX) + { + // Plays the sound and makes absolute sure that it is not looping + if(Gametime - ai_info->last_sound_time[AI_FLEE_SOUND] > 5.0f) + { + // A 25% chance of playing it + if(ps_rand()%4 == 0) + { + Sound_system.StopSoundLooping(Sound_system.Play3dSound(ai_info->sound[AI_FLEE_SOUND], SND_PRIORITY_NORMAL, obj)); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ai_info->sound[AI_FLEE_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ai_info->sound[AI_FLEE_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + } + + ai_info->last_sound_time[AI_FLEE_SOUND] = Gametime; + } + } + } + else if(g_status & AISR_CIRCLE_DIST) + { + vector movement_vec; + int gtime = Gametime + OBJNUM(obj); + gtime = gtime % 8; + + object *rel_obj; + + if(ai_info->flags & AIF_ORIENT_TO_VEL) + rel_obj = goal_obj; + else + rel_obj = obj; + + if(rel_obj) + { + if(gtime < 2) + movement_vec = rel_obj->orient.rvec; + else if(gtime < 4) + movement_vec = -rel_obj->orient.rvec; + else if(gtime < 6) + { + if(obj->movement_type != MT_WALKING) + { + movement_vec = rel_obj->orient.uvec; + } + else + { + movement_vec = -rel_obj->orient.rvec; + } + } + else + { + if(obj->movement_type != MT_WALKING) + { + movement_vec = -rel_obj->orient.uvec; + } + else + { + movement_vec = rel_obj->orient.rvec; + } + } + } + else + { + movement_vec = obj->orient.rvec; + } + + if(obj->movement_type == MT_WALKING) + { + short robots[6]; + int num_robots = fvi_QuickDistObjectList(&obj->pos, obj->roomnum, 30.0f, robots, 6, false, true, false, true); + vector d = Zero_vector; + int i; + float closest = 100000.0f; + + if(num_robots > 0) + { + for(i = 0; i < num_robots; i++) + { + if(Objects[robots[i]].type == obj->type && Objects[robots[i]].id == obj->id) + { + vector dir; + float distance; + + // Reverse of move towards + dir = obj->pos - Objects[robots[i]].pos; + distance = vm_NormalizeVector(&dir); + + if(distance > 0.0) + { + if(distance < closest) + { + closest = distance; + } + + if(distance < 30.0) + { + d += (1.0f - distance/30.f) * dir; + } + } + } + } + } + + if(d != Zero_vector) + { + if(closest < 30.0f) + { + float scale = 1.0f - closest/30.0f; + vm_NormalizeVector(&d); + + movement_vec = (scale * d) + (1.0f - scale) * movement_vec; + vm_NormalizeVector(&movement_vec); + } + } + + goal_mdir = movement_vec; + goal_mset = true; +// AIMoveTowardsDir(obj, &movement_vec); + } + else + { + goal_mdir = movement_vec; + goal_mset = true; +// AIMoveTowardsDir(obj, &movement_vec); + } + } + else + { + AIMoveTowardsPosition(obj, &ai_info->last_see_target_pos, 1.0f, false, &goal_mdir, &goal_f_moved); + goal_mset = true; + } + } + break; + + default: + { + mprintf((0, "AI ERROR: Object %d trying a non-implemented goal\n", OBJNUM(obj))); + AIMoveTowardsPosition(obj, &ai_info->last_see_target_pos, 1.0f, true, &goal_mdir, &goal_f_moved); + goal_mset = true; + } + } + } + else + { + if(!(f_dodge || f_avoid || (ai_info->dodge_till_time >= Gametime && ai_info->last_dodge_dir != Zero_vector))) + { + AIMoveTowardsPosition(obj, &obj->pos, 1.0f, false, &goal_mdir, &goal_f_moved); + goal_mset = true; + } + } + + // BLEND THIS! + if((f_dodge || f_avoid || (ai_info->dodge_till_time >= Gametime && ai_info->last_dodge_dir != Zero_vector)) || + (!goal_f_moved && goal_mset)) + { + if(!f_dodge && (ai_info->dodge_till_time >= Gametime && ai_info->last_dodge_dir != Zero_vector)) + { + f_dodge = true; + ai_info->movement_dir += ai_info->last_dodge_dir; + + if(ai_info->dodge_vel_percent > highest_speed) + { + highest_speed = ai_info->dodge_vel_percent; + } + } + + if(!goal_f_moved && goal_mset) + { + ai_info->movement_dir += goal_mdir * cur_goal->influence; + } + + float move_len = vm_NormalizeVector(&ai_info->movement_dir); + if (move_len == 0.0f) + { + vm_MakeRandomVector(&ai_info->movement_dir); + vm_NormalizeVector(&ai_info->movement_dir); + } + + AIMoveTowardsDir(obj, &ai_info->movement_dir, highest_speed); + f_turned = false; + } + } + } + + // Handle orientation now + if((!f_turned || + ((!f_goal_found && Gametime - ai_info->last_see_target_time < 5.0f) && + (!f_goal_found && Gametime - ai_info->last_hear_target_time < 5.0f) && (ai_info->flags & AIF_FIRE))) && + ai_info->max_turn_rate > 0.0f) + AIDoOrient(obj, highest_influence_goal); + } +} + +#define MAX_NOT_SEE_TARGET_FIRE_TIME 2.0f +#define MIN_TURRET_SOUND_TIME 0.8f +#define DEFAULT_FIRE_DOT .93f + +void ai_fire(object *obj) +{ + if(!Object_info[obj->id].static_wb) + { + // something is hosed due to 'Mac memory savings' (c) + Int3(); + return;//object can't fire anything... + } + + ai_frame *ai_info = obj->ai_info; + int i; + int j; + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + object_info *obj_info = &Object_info[obj->id]; + bool f_turret_next_move_still; + +//. mprintf((0, "Aiming\n")); + if(!(ai_info->flags & AIF_FIRE)) + { + if(ai_info->status_reg & AISR_RANGED_ATTACK) + { + for(i = 0; i < pm->num_wbs; i++) + { + if(!(obj->dynamic_wb[i].flags & DWBF_ENABLED)) + continue; + + if(WBIsBatteryReady(obj, &obj_info->static_wb[i], i) && !(Cinematic_inuse && !(ai_info->status_reg & AISR_OK_TO_FIRE_DURING_CINEMATICS))) + { + int anim_type = obj_info->static_wb[i].flags & WBF_ANIM_MASKS; + + if(anim_type == WBF_ANIM_LOCAL) + { + WBSetupFireAnim(obj, &obj_info->static_wb[i], i); + } + else if(anim_type == WBF_ANIM_FULL) + { + gi_fire attack_info; + + attack_info.cur_wb = i; + attack_info.cur_mask = obj->dynamic_wb[i].cur_firing_mask; + + GoalAddGoal(obj, AIG_FIRE_AT_OBJ, (void *)&attack_info , ACTIVATION_BLEND_LEVEL); + } + else + { + WBFireBattery(obj, &obj_info->static_wb[i], i); + ai_info->status_reg &= ~AISR_RANGED_ATTACK; + } + } + } + } + + return; + } + + if(((Gametime - ai_info->last_see_target_time) > 4.0 && + (Gametime - ai_info->last_hear_target_time) > 4.0) || + ai_info->awareness <= AWARE_BARELY) + { + ai_info->status_reg &= ~AISR_RANGED_ATTACK; + return; + } + + for(i = 0; i < pm->num_wbs; i++) + { + vector target_dir[3]; + float dot[3]; + vector gun_point[3]; + vector gun_normal[3]; + float ta[3]; + int best_dot = WB_MOVE_STILL; + + if(!(obj->dynamic_wb[i].flags & DWBF_ENABLED)) + continue; + + if(obj_info->static_wb[i].flags & WBF_USE_CUSTOM_MAX_DIST) + { + float max_dist = obj_info->static_wb[i].aiming_3d_dist; + + if(max_dist < ai_info->dist_to_target_actual) + continue; + } + + int aiming_gp_index = obj_info->static_wb[i].aiming_gp_index; + + // The WBF_AIM_FVEC and WBF_FIRE_FVEC stuff will work with a dummy turret or with no turrets + if(pm->poly_wb[i].num_turrets == 0 || (pm->poly_wb[i].num_turrets == 1 && pm->submodel[pm->poly_wb[i].turret_index[0]].fov == 0.0f)) + { + if(WBIsBatteryReady(obj, &obj_info->static_wb[i], i) && !(Cinematic_inuse && !(ai_info->status_reg & AISR_OK_TO_FIRE_DURING_CINEMATICS))) + { + if(obj_info->static_wb[i].flags & WBF_AIM_FVEC) + { + gun_point[WB_MOVE_STILL] = obj->pos; + gun_normal[WB_MOVE_STILL] = obj->orient.fvec; + + if(obj_info->static_wb[i].flags & WBF_FIRE_FVEC) + { + gun_normal[WB_MOVE_STILL] = obj->orient.fvec; + } + } + if(obj_info->static_wb[i].flags & WBF_FIRE_FVEC) + { + WeaponCalcGun(&gun_point[WB_MOVE_STILL], NULL, obj, pm->poly_wb[i].gp_index[aiming_gp_index]); + gun_normal[WB_MOVE_STILL] = obj->orient.fvec; + } + else + { + WeaponCalcGun(&gun_point[WB_MOVE_STILL], &gun_normal[WB_MOVE_STILL], obj, pm->poly_wb[i].gp_index[aiming_gp_index]); + } + + target_dir[WB_MOVE_STILL] = ai_info->last_see_target_pos - gun_point[WB_MOVE_STILL]; + vm_NormalizeVector(&target_dir[WB_MOVE_STILL]); + dot[WB_MOVE_STILL] = gun_normal[WB_MOVE_STILL] * target_dir[WB_MOVE_STILL]; + + best_dot = WB_MOVE_STILL; + } + } + + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + f_turret_next_move_still = false; + best_dot = WB_MOVE_STILL; + float min_invalid_ang; + float max_invalid_ang; + bool f_constrain = true; + + float s_scale = (((obj->ai_info->flags & AIF_TEAM_MASK) != AIF_TEAM_REBEL))?Diff_ai_turret_speed[DIFF_LEVEL]:1.0f; + const float rps = pm->submodel[pm->poly_wb[i].turret_index[j]].rps * s_scale; + + if(pm->submodel[pm->poly_wb[i].turret_index[j]].fov > 0.0f)//Gametime >= obj->dynamic_wb[i].turret_next_think_time[j]) + { + min_invalid_ang = pm->submodel[pm->poly_wb[i].turret_index[j]].fov; + max_invalid_ang = 1.0 - min_invalid_ang; + + if (min_invalid_ang >= 0.5f) f_constrain = false; + + ta[WB_MOVE_STILL] = obj->dynamic_wb[i].norm_turret_angle[j]; + + ta[WB_MOVE_RIGHT] = obj->dynamic_wb[i].norm_turret_angle[j] - Frametime * rps; + while(ta[WB_MOVE_RIGHT] < 0.0) ta[WB_MOVE_RIGHT] += 1.0f; + + if(f_constrain && ta[WB_MOVE_RIGHT] > min_invalid_ang && ta[WB_MOVE_RIGHT] < max_invalid_ang ) ta[WB_MOVE_RIGHT] = max_invalid_ang; + + ta[WB_MOVE_LEFT] = obj->dynamic_wb[i].norm_turret_angle[j] + Frametime * rps; + while(ta[WB_MOVE_LEFT] > 1.0) ta[WB_MOVE_LEFT] -= 1.0f; + + if(f_constrain && ta[WB_MOVE_LEFT] > min_invalid_ang && ta[WB_MOVE_LEFT] < max_invalid_ang ) ta[WB_MOVE_LEFT] = min_invalid_ang; + + WeaponCalcGun(&gun_point[WB_MOVE_STILL], &gun_normal[WB_MOVE_STILL], obj, pm->poly_wb[i].gp_index[aiming_gp_index]); + + obj->dynamic_wb[i].norm_turret_angle[j] = ta[WB_MOVE_RIGHT]; + WeaponCalcGun(&gun_point[WB_MOVE_RIGHT], &gun_normal[WB_MOVE_RIGHT], obj, pm->poly_wb[i].gp_index[aiming_gp_index]); + + obj->dynamic_wb[i].norm_turret_angle[j] = ta[WB_MOVE_LEFT]; + WeaponCalcGun(&gun_point[WB_MOVE_LEFT], &gun_normal[WB_MOVE_LEFT], obj, pm->poly_wb[i].gp_index[aiming_gp_index]); + + // mprintf((0, "Weapon %f, %f, %f and normal %f, %f, %f\n", XYZ(&gun_point), XYZ(&gun_normal))); + + target_dir[WB_MOVE_STILL] = ai_info->last_see_target_pos - gun_point[WB_MOVE_STILL]; + target_dir[WB_MOVE_RIGHT] = ai_info->last_see_target_pos - gun_point[WB_MOVE_RIGHT]; + target_dir[WB_MOVE_LEFT] = ai_info->last_see_target_pos - gun_point[WB_MOVE_LEFT]; + + vm_NormalizeVector(&target_dir[WB_MOVE_STILL]); + vm_NormalizeVector(&target_dir[WB_MOVE_RIGHT]); + vm_NormalizeVector(&target_dir[WB_MOVE_LEFT]); + + dot[WB_MOVE_STILL] = gun_normal[WB_MOVE_STILL] * target_dir[WB_MOVE_STILL]; + dot[WB_MOVE_RIGHT] = gun_normal[WB_MOVE_RIGHT] * target_dir[WB_MOVE_RIGHT]; + dot[WB_MOVE_LEFT] = gun_normal[WB_MOVE_LEFT] * target_dir[WB_MOVE_LEFT]; + + if(dot[WB_MOVE_RIGHT] > dot[WB_MOVE_STILL]) + best_dot = WB_MOVE_RIGHT; + if(dot[WB_MOVE_LEFT] > dot[best_dot]) + best_dot = WB_MOVE_LEFT; + + obj->dynamic_wb[i].norm_turret_angle[j] = ta[best_dot]; + + ubyte last_t_d = obj->dynamic_wb[i].turret_direction[j]; + + if(dot[WB_MOVE_RIGHT] > dot[WB_MOVE_LEFT]) + obj->dynamic_wb[i].turret_direction[j] = WB_MOVE_RIGHT; + else + obj->dynamic_wb[i].turret_direction[j] = WB_MOVE_LEFT; + + if(last_t_d != obj->dynamic_wb[i].turret_direction[j]) + { + if(ai_info->sound[AI_TURRET_SOUND] != SOUND_NONE_INDEX && (ai_info->last_sound_time[AI_TURRET_SOUND] + MIN_TURRET_SOUND_TIME <= Gametime)) + { + // Plays the sound and makes absolute sure that it is not looping + Sound_system.StopSoundLooping(Sound_system.Play3dSound(ai_info->sound[AI_TURRET_SOUND], SND_PRIORITY_NORMAL, obj)); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ai_info->sound[AI_TURRET_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ai_info->sound[AI_TURRET_SOUND], OBJNUM(obj), SND_PRIORITY_NORMAL); + + ai_info->last_sound_time[AI_TURRET_SOUND] = Gametime; + } + } + + obj->dynamic_wb[i].turret_next_think_time[j] += pm->submodel[pm->poly_wb[i].turret_index[j]].think_interval; + if(Demo_flags==DF_RECORDING) + { + DemoWriteTurretChanged(OBJNUM(obj)); + } + if(Game_mode & GM_MULTI) + { + MultiAddObjTurretUpdate(OBJNUM(obj)); + } + + } + else + { + best_dot = obj->dynamic_wb[i].turret_direction[j]; + + if(best_dot != WB_MOVE_STILL) + { + min_invalid_ang = pm->submodel[pm->poly_wb[i].turret_index[j]].fov; + max_invalid_ang = 1.0 - min_invalid_ang; + + if (min_invalid_ang >= 0.5f) f_constrain = false; + } + + // chrishack -- make next still + switch(best_dot) + { + case WB_MOVE_STILL: + ta[WB_MOVE_STILL] = obj->dynamic_wb[i].norm_turret_angle[j]; + break; + + case WB_MOVE_RIGHT: + ta[WB_MOVE_RIGHT] = obj->dynamic_wb[i].norm_turret_angle[j] - Frametime * rps; + while(ta[WB_MOVE_RIGHT] < 0.0) ta[WB_MOVE_RIGHT] += 1.0f; + + if(f_constrain && ta[WB_MOVE_RIGHT] > min_invalid_ang && ta[WB_MOVE_RIGHT] < max_invalid_ang ) + { + ta[WB_MOVE_RIGHT] = max_invalid_ang; + f_turret_next_move_still = true; + } + break; + + case WB_MOVE_LEFT: + ta[WB_MOVE_LEFT] = obj->dynamic_wb[i].norm_turret_angle[j] + Frametime * rps; + while(ta[WB_MOVE_LEFT] > 1.0) ta[WB_MOVE_LEFT] -= 1.0f; + + if(f_constrain && ta[WB_MOVE_LEFT] > min_invalid_ang && ta[WB_MOVE_LEFT] < max_invalid_ang ) + { + ta[WB_MOVE_LEFT] = min_invalid_ang; + f_turret_next_move_still = true; + } + break; + } + + obj->dynamic_wb[i].norm_turret_angle[j] = ta[best_dot]; + + if(WBIsBatteryReady(obj, &obj_info->static_wb[i], i) && !(Cinematic_inuse && !(ai_info->status_reg & AISR_OK_TO_FIRE_DURING_CINEMATICS))) + { + WeaponCalcGun(&gun_point[best_dot], &gun_normal[best_dot], obj, pm->poly_wb[i].gp_index[aiming_gp_index]); + + target_dir[best_dot] = ai_info->last_see_target_pos - gun_point[best_dot]; + vm_NormalizeVector(&target_dir[best_dot]); + dot[best_dot] = gun_normal[best_dot] * target_dir[best_dot]; + } + } + + // This happens on a non-thinking frame the the turret hits a constrain + if(f_turret_next_move_still) obj->dynamic_wb[i].turret_direction[j] = WB_MOVE_STILL; + } + + if(Gametime - ai_info->last_see_target_time < MAX_NOT_SEE_TARGET_FIRE_TIME || + Gametime - ai_info->last_hear_target_time < MAX_NOT_SEE_TARGET_FIRE_TIME) + { + if(WBIsBatteryReady(obj, &obj_info->static_wb[i], i) && !(Cinematic_inuse && !(ai_info->status_reg & AISR_OK_TO_FIRE_DURING_CINEMATICS))) + { + float fire_dot; + + if(obj_info->static_wb[i].flags & WBF_USE_CUSTOM_FOV) + fire_dot = obj_info->static_wb[i].aiming_3d_dot; + else + fire_dot = DEFAULT_FIRE_DOT; + + if(dot[best_dot] >= fire_dot) + { + bool f_no_fire = false; + int anim_type = obj_info->static_wb[i].flags & WBF_ANIM_MASKS; + + if(ai_info->agression == 0.0f && ai_info->memory[0].num_times_hit == 0) + { + // Assume no fire + f_no_fire = true; + + object *target = ObjGet(ai_info->target_handle); + if(target) + { + fvi_query fq; + fvi_info hit_info; + int fate; + int ignore_obj_list[100]; + ignore_obj_list[0] = OBJNUM(obj); + int num_ignored = 1; + int i; + + // CHRISHACK - ONLY IGNORES FIRST LEVEL OF CHILDREN - DO RECERSIVE + for(i = 0; i < Poly_models[obj->rtype.pobj_info.model_num].n_attach; i++) + { + object *child; + + if((child = ObjGet(obj->attach_children[i])) != NULL && num_ignored < 99) + { + ignore_obj_list[num_ignored++] = OBJNUM(child); + } + } + + ignore_obj_list[num_ignored] = -1; + + fq.p0 = &obj->pos; + fq.p1 = &target->pos; + fq.startroom = obj->roomnum; + + fq.rad = 0.01f; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_WEAPONS | FQ_IGNORE_POWERUPS | FQ_IGNORE_RENDER_THROUGH_PORTALS | FQ_NO_RELINK; + fq.thisobjnum = -1; + fq.ignore_obj_list = ignore_obj_list; + + fate = fvi_FindIntersection(&fq, &hit_info); + + if(fate != HIT_TERRAIN && fate != HIT_WALL && (((fate == HIT_OBJECT || fate == HIT_SPHERE_2_POLY_OBJECT) && hit_info.hit_object[0] == OBJNUM(target)) || fate == HIT_NONE)) + { + f_no_fire = false; + } + } + } + + if(!f_no_fire) + { + if(anim_type == WBF_ANIM_LOCAL) + { + WBSetupFireAnim(obj, &obj_info->static_wb[i], i); + } + else if(anim_type == WBF_ANIM_FULL) + { + gi_fire attack_info; + + attack_info.cur_wb = i; + attack_info.cur_mask = obj->dynamic_wb[i].cur_firing_mask; + + GoalAddGoal(obj, AIG_FIRE_AT_OBJ, (void *)&attack_info , ACTIVATION_BLEND_LEVEL); + } + else + { + WBFireBattery(obj, &obj_info->static_wb[i], i); + ai_info->status_reg &= ~AISR_RANGED_ATTACK; + } + } + else + { + ai_info->status_reg &= ~AISR_RANGED_ATTACK; + obj->dynamic_wb[i].last_fire_time = Gametime + 1.0f + ps_rand()/(float)RAND_MAX; + } + } + } + } + } + +} + + +#define PERCENT_QUIRK_PER_SEC .1 +#define PERCENT_TAUNT_PER_SEC .1 + +inline void do_awareness_based_anim_stuff(object *obj) +{ + int next_anim; + ai_frame *ai_info = obj->ai_info; + + if(ai_info->awareness <= AWARE_BARELY) + { + if(ai_info->animation_type == AS_ALERT && ai_info->next_animation_type != AS_GOTO_ALERT_STANDING) + { + next_anim = AS_IDLE; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim, ACTIVATION_BLEND_LEVEL); + } + else if(ai_info->animation_type == AS_IDLE && ai_info->next_animation_type != AS_GOTO_IDLE_STANDING) + { + float local_seed = OBJNUM(obj)/32.0f; + + int new_time_int = Gametime + local_seed; + int last_time_int = Gametime - Frametime + local_seed; + + // Once a second we have a chance of doing a quirk + if(new_time_int != last_time_int) + { + if(ps_rand() < RAND_MAX * PERCENT_QUIRK_PER_SEC) + { + next_anim = AS_QUIRK; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + } + } + } + } + else if(ai_info->animation_type == AS_IDLE || ai_info->animation_type == AS_QUIRK || ai_info->animation_type == AS_BIRTH) + { + next_anim = AS_ALERT; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim, ACTIVATION_BLEND_LEVEL); + } + else if(ai_info->animation_type == AS_ALERT) + { + if(!(ai_info->flags & AIF_ONLY_TAUNT_AT_DEATH) && + (Object_info[obj->id].anim[ai_info->movement_type].elem[AS_TAUNT].to != 0.0f)) + { + float local_seed = OBJNUM(obj)/32.0f; + + int new_time_int = Gametime + local_seed; + int last_time_int = Gametime - Frametime + local_seed; + + // Once a second we have a chance of doing a quirk + if(new_time_int != last_time_int) + { + if(ps_rand() < RAND_MAX * PERCENT_TAUNT_PER_SEC) + { + next_anim = AS_TAUNT; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + } + } + } + } +} + +inline void ai_decrease_awareness(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + + if(ai_info->awareness == AWARE_NONE && !(ai_info->flags & AIF_PERSISTANT)) + { + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + AIPathFreePath(&ai_info->path); + } + + if(Gametime - ai_info->last_render_time < AWARENESS_RENDER_RECENTLY_INTERVAL || + Gametime - ai_info->last_see_target_time < AWARENESS_SEE_TARGET_RECENTLY_INTERVAL || + Gametime - ai_info->last_hear_target_time < AWARENESS_SEE_TARGET_RECENTLY_INTERVAL) + ai_info->awareness -= (AWARE_RENDER_RECENTLY_FALLOFF * Frametime); + else + ai_info->awareness -= (AWARE_FALLOFF * Frametime); + + if(ai_info->awareness < AWARE_NONE) + { + ai_info->awareness = AWARE_NONE; + } + +// mprintf((0, "Awareness %f", ai_info->awareness)); +} + +inline bool ai_do_script_stuff(object *obj) +{ + tOSIRISEventInfo ei; + Osiris_CallEvent(obj,EVT_AI_FRAME,&ei); + //@$-D3XExecScript(obj, EVT_AI_FRAME, NULL, REF_OBJTYPE, NULL); + + return true; +} + +inline void ai_walker_stuff(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + + // Do standing->walking and walking->standing stuff + if(ai_info->movement_type == MC_STANDING) + { + int next_anim; + + if(vm_GetMagnitude(&obj->mtype.phys_info.velocity) > 0.01f || + vm_GetMagnitude(&obj->mtype.phys_info.rotvel) > 0.01) + { + if(ai_info->next_animation_type == AI_INVALID_INDEX) + { + if(ai_info->animation_type == AS_ALERT) + { + next_anim = AS_GOTO_ALERT_WALKING; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + } + else if(ai_info->animation_type == AS_IDLE || ai_info->animation_type == AS_QUIRK || ai_info->animation_type == AS_BIRTH) + { + next_anim = AS_GOTO_IDLE_WALKING; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + } + } + + obj->mtype.phys_info.velocity = Zero_vector; + } + } + else if(ai_info->movement_type == MC_WALKING) + { + int next_anim; + + if(vm_GetMagnitude(&obj->mtype.phys_info.velocity) <= 0.01f && + vm_GetMagnitude(&obj->mtype.phys_info.rotvel) <= 0.01) + { + if(ai_info->animation_type == AS_ALERT) + { + next_anim = AS_GOTO_ALERT_STANDING; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + } + else if(ai_info->animation_type == AS_IDLE) + { + next_anim = AS_GOTO_IDLE_STANDING; + GoalAddGoal(obj, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + } + } + } +} + +int AIGetTeam(object *obj) +{ + int t_team; + + if(obj->type == OBJ_PLAYER) + { + if(Num_teams > 1 || ((Game_mode & GM_MULTI) && Num_teams <= 1 && !(Netgame.flags & NF_DAMAGE_FRIENDLY))) + { + t_team = AIF_TEAM_REBEL + PlayerGetTeam(obj->id) + 1; + //@@t_team = AIF_TEAM_REBEL + Players[obj->id].team + 1; + } + else + { + t_team = AIF_TEAM_REBEL + obj->id + 1; + } + } + else + { + if(obj->ai_info->flags & AIF_ACT_AS_NEUTRAL_UNTIL_SHOT) + { + t_team = AIF_TEAM_NEUTRAL; + } + else + { + t_team = obj->ai_info->flags & AIF_TEAM_MASK; + + if(t_team == AIF_TEAM_REBEL) + { + object *p = ObjGet(obj->parent_handle); + + if(p && (p->type == OBJ_PLAYER || p->type == OBJ_GHOST)) + { + if(Num_teams > 1) + { + t_team += PlayerGetTeam(p->id) + 1; + //@@t_team += Players[p->id].team + 1; + } + else + { + t_team += p->id + 1; + } + } + } + } + } + + return t_team; +} + +bool AIObjFriend(object *obj, object *target) +{ + bool f_friend = false; + + // If an object isn't an AI object, it isn't a friend + if(!obj || !target || (target->type != OBJ_PLAYER && !target->ai_info) || (obj->type != OBJ_PLAYER && !obj->ai_info)) + return false; + + if(obj->type == OBJ_GHOST || obj->type == OBJ_POWERUP || (obj->flags & OF_DESTROYED)) + return false; + + if((target->control_type == CT_AI) && (target->movement_type == MT_NONE) && Poly_models[target->rtype.pobj_info.model_num].num_wbs == 0) + { + return false; + } + + if(obj->type == OBJ_PLAYER && (Players[obj->id].flags & PLAYER_FLAGS_DEAD)) + return false; + + int team = AIGetTeam(obj); + int t_team = AIGetTeam(target); + + // If this object is targetting you - it is your enemy + if((target->control_type == CT_AI) && (target->ai_info->target_handle == obj->handle)) + { + return false; + } + + // If its neutral and not targetting you -- its not an enemy (unless your hostile) + if(team == AIF_TEAM_HOSTILE) + return false; + + // Related objects shouldn't kill each other + if(ObjectsAreRelated(OBJNUM(obj), OBJNUM(target))) + { + return true; + } + + if(obj->parent_handle == target->handle || target->parent_handle == obj->handle) + { + return true; + } + + switch(team) + { + case AIF_TEAM_PTMC: + if(t_team == AIF_TEAM_PTMC) + { + f_friend = true; + } + break; + + case AIF_TEAM_HOSTILE: + f_friend = false; + break; + + case AIF_TEAM_NEUTRAL: + + if(t_team == AIF_TEAM_NEUTRAL) + { + f_friend = true; + } + break; + + // A player team + default: + // All REBEL sub teams are friends with non player-parented rebels + if(t_team == AIF_TEAM_REBEL || team == AIF_TEAM_REBEL || t_team == team) + { + f_friend = true; + } + break; + } + + return f_friend; +} + +bool AIObjEnemy(object *obj, object *target) +{ + bool f_enemy = false; + + // If an object isn't an AI object or player, it isn't an enemy + if(!obj || !target || (target->type != OBJ_PLAYER && !target->ai_info) || (obj->type != OBJ_PLAYER && !obj->ai_info)) + return false; + + if(obj->type == OBJ_GHOST || obj->type == OBJ_POWERUP || (obj->flags & OF_DESTROYED)) + return false; + + if(obj->type == OBJ_PLAYER && (Players[obj->id].flags & PLAYER_FLAGS_DEAD)) + return false; + + int team = AIGetTeam(obj); + int t_team = AIGetTeam(target); + + // If this object is targetting you - it is your enemy + if((target->control_type == CT_AI) && (target->ai_info->target_handle == obj->handle)) + { + return true; + } + + if((target->control_type == CT_AI) && (target->movement_type == MT_NONE) && Poly_models[target->rtype.pobj_info.model_num].num_wbs == 0) + { + return false; + } + + // If its neutral and not targetting you -- its not an enemy (unless your hostile) + if((team != AIF_TEAM_HOSTILE) && (t_team == AIF_TEAM_NEUTRAL && target->ai_info->target_handle != obj->handle)) + { + return false; + } + + // Related objects shouldn't kill each other + if(ObjectsAreRelated(OBJNUM(obj), OBJNUM(target))) + { + return false; + } + + if(obj->parent_handle == target->handle || target->parent_handle == obj->handle) + { + return false; + } + + switch(team) + { + case AIF_TEAM_PTMC: + if(t_team != AIF_TEAM_PTMC) + { + f_enemy = true; + } + break; + + case AIF_TEAM_HOSTILE: + f_enemy = true; + break; + + case AIF_TEAM_NEUTRAL: + + ASSERT(obj->ai_info); + + if(obj->ai_info->target_handle == target->handle) + { + f_enemy = true; + } + break; + + // A player team + default: + // All REBEL sub teams are friends with non player-parented rebels + if(t_team == AIF_TEAM_PTMC || + t_team == AIF_TEAM_HOSTILE || + (t_team != AIF_TEAM_REBEL && team != AIF_TEAM_REBEL && t_team != team && + !(((Game_mode & GM_MULTI) && Num_teams <= 1 && !(Netgame.flags & NF_DAMAGE_FRIENDLY))))) + { + f_enemy = true; + } + break; + } + + return f_enemy; +} + +#define AI_FORGIVE_AGRESSION_MULTIPLIER 1.2f + + +void AITargetCheck(object *obj, object *target, object **best_obj, float *best_dot, float *best_dist) +{ + bool f_use_dot = (obj->ai_info->flags & AIF_TARGET_BY_DIST) != 0; + + if(AIObjEnemy(obj, target)) + { + float dot; + float dist; + vector to_obj; + fvi_query fq; + fvi_info hit_info; + int fate; + + if(obj == target) + return; + + if(!BOA_IsVisible(obj->roomnum, target->roomnum)) + return; + + if(!AIDetermineObjVisLevel(obj, target)) + return; + + if(target->type == OBJ_PLAYER && (Players[target->id].flags & (PLAYER_FLAGS_DEAD | PLAYER_FLAGS_DYING))) + return; + + to_obj = target->pos - obj->pos; + + dist = vm_NormalizeVector(&to_obj); + + if(dist > MAX_SEE_TARGET_DIST) + return; + + vector look_dir; + AIDetermineFovVec(obj, &look_dir); + + dot = to_obj * look_dir; + + if(f_use_dot && dot <= *best_dot) + { + return; + } + else if(!f_use_dot && dist >= *best_dist) + { + return; + } + + fq.p0 = &obj->pos; + fq.p1 = &target->pos; + fq.startroom = obj->roomnum; + + fq.rad = 0.0f; + fq.flags = FQ_CHECK_OBJS | FQ_ONLY_DOOR_OBJ | FQ_NO_RELINK; + fq.thisobjnum = -1; + + int ignore_obj_list[100]; + ignore_obj_list[0] = OBJNUM(obj); + int num_ignored = 1; + int i; + + // CHRISHACK - ONLY IGNORES FIRST LEVEL OF CHILDREN - DO RECERSIVE + for(i = 0; i < Poly_models[obj->rtype.pobj_info.model_num].n_attach; i++) + { + object *child; + + if((child = ObjGet(obj->attach_children[i])) != NULL && num_ignored < 99) + { + ignore_obj_list[num_ignored++] = OBJNUM(child); + } + } + + ignore_obj_list[num_ignored] = -1; + fq.ignore_obj_list = ignore_obj_list; + + fate = fvi_FindIntersection(&fq, &hit_info); + + if(fate != HIT_NONE) + return; + + *best_dot = dot; + *best_dist= dist; + *best_obj = target; + } +} + +void AIDetermineTarget(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + object *best_obj = NULL; + float best_dot = -1.0f; + float best_dist = MAX_SEE_TARGET_DIST + 1; + int i; + + if(Gametime < ai_info->next_target_update_time) + return; + + if(ai_info->awareness >= AWARE_BARELY) + ai_info->next_target_update_time = Gametime + MIN_TARGET_UPDATE_INTERVAL + ((float)ps_rand()/(float)RAND_MAX) * (MAX_TARGET_UPDATE_INTERVAL - MIN_TARGET_UPDATE_INTERVAL); + else + ai_info->next_target_update_time = Gametime + 2.0f * MIN_TARGET_UPDATE_INTERVAL + ((float)ps_rand()/(float)RAND_MAX) * 2.0f * (MAX_TARGET_UPDATE_INTERVAL - MIN_TARGET_UPDATE_INTERVAL); + + // Chrishack -- if agression is over a value, NO switching targets!!!!!!!!! Need to implement + // Chrishack -- if frustration is over a value, act as hostile -- temp stuff AIF_TEAM_HOSTILE + + // They forget their enemies after a few seconds + if((ai_info->flags & AIF_TEAM_MASK) == AIF_TEAM_NEUTRAL || (ai_info->flags & AIF_ACT_AS_NEUTRAL_UNTIL_SHOT)) + { + best_obj = NULL; + } + else if((ai_info->flags & AIF_TEAM_MASK) != AIF_TEAM_PTMC) + { + short list[50]; + int num = fvi_QuickDistObjectList(&obj->pos, obj->roomnum, MAX_SEE_TARGET_DIST, list, 50, false, true, false, true); + + for(i = 0; i < num; i++) + { + AITargetCheck(obj, &Objects[list[i]], &best_obj, &best_dot, &best_dist); + } + } + else // Team PTMC :) + { + if(Game_mode & GM_MULTI) + { + // Multiplayer targetting (Major difference is that robot will ignore players while infighting in single player) + for(i = 0; i < MAX_PLAYERS; i++) + { + if((NetPlayers[i].flags & NPF_CONNECTED) && (NetPlayers[i].sequence >= NETSEQ_PLAYING)) + { + object *target = &Objects[Players[i].objnum]; + + AITargetCheck(obj, target, &best_obj, &best_dot, &best_dist); + } + } + } + else + { + object *t = ObjGet(ai_info->target_handle); + bool f_forgive_friend = false; + + if((t) && (t->control_type == CT_AI) && ((t->ai_info->flags & AIF_TEAM_MASK) == AIF_TEAM_PTMC)) + { + // Do the divide because we don't want RAND_MAX to go too high + if(ps_rand()/AI_FORGIVE_AGRESSION_MULTIPLIER > ai_info->agression * RAND_MAX) + { + f_forgive_friend = true; + } + } + + // chrishack -- FUCK a multi-guidebot bug!!!!!! + if(f_forgive_friend || !ai_target_valid(obj) || (ai_info->target_handle != OBJECT_HANDLE_NONE && ai_info->target_handle == Buddy_handle[0] && ObjGet(Buddy_handle[0])->type != OBJ_GHOST)) + { + if(Player_object->type != OBJ_GHOST) + best_obj = Player_object; + else if(Buddy_handle[0] != OBJECT_HANDLE_NONE && ObjGet(Buddy_handle[0])->type != OBJ_GHOST) + best_obj = ObjGet(Buddy_handle[0]); + else + best_obj = NULL; + } + else + { + best_obj = ObjGet(obj->ai_info->target_handle); + } + } + } + + if(best_obj) + { + AISetTarget(obj, best_obj->handle); + } + else + { + AISetTarget(obj, OBJECT_HANDLE_NONE); + } +} + +// chrishack -- make sure that some checks are done with a ps_rand() based on the emotion involved +// also current emotional levels should influence the percent chance of the check being successful +void AIDoFreud(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + ai_mem *mem = ai_info->memory; + + int fear_depth = (ai_info->life_preservation) * (AI_MEM_DEPTH - 1) + 1; + int anger_depth = (ai_info->agression) * (AI_MEM_DEPTH - 1) + 1; + int frust_depth = (ai_info->frustration) * (AI_MEM_DEPTH - 1) + 1; + + if(fear_depth >= AI_MEM_DEPTH) + fear_depth = AI_MEM_DEPTH - 1; + if(anger_depth >= AI_MEM_DEPTH) + anger_depth = AI_MEM_DEPTH - 1; + if(frust_depth >= AI_MEM_DEPTH) + frust_depth = AI_MEM_DEPTH - 1; + + if(ai_info->goals[3].used == 0 && ai_info->awareness > AWARE_BARELY) + { + if(IS_GENERIC(obj->type) && + mem[0].shields/Object_info[obj->id].hit_points < ai_info->life_preservation && + mem[0].num_enemy_shots_dodged > 0 && + mem[0].num_friends < 2 && + (float)ps_rand()/(float)RAND_MAX < ai_info->life_preservation) + { + float time = 10.0f * ai_info->life_preservation + 5.0f; + + GoalAddGoal(obj, AIG_WANDER_AROUND, NULL, 3, 2.0f, GF_SPEED_FLEE | GF_ORIENT_VELOCITY | GF_NONFLUSHABLE); + GoalAddEnabler(obj, 3, AIE_CLEAR_TIME, (void *)&time, 1.0, 0.0); + + mprintf((0, "Fear!!!!\n")); + return; + } + + // Accounts for massive loss of shields + if(IS_GENERIC(obj->type) && + (mem[fear_depth].shields - mem[0].shields)/mem[fear_depth].shields > 0.25f * (1.0f - ai_info->life_preservation) && + (float)ps_rand()/(float)RAND_MAX < ai_info->life_preservation) + { + float time = 10.0f * ai_info->life_preservation + 5.0f; + + GoalAddGoal(obj, AIG_WANDER_AROUND, NULL, 3, 2.0f, GF_SPEED_FLEE | GF_ORIENT_VELOCITY | GF_NONFLUSHABLE); + GoalAddEnabler(obj, 3, AIE_CLEAR_TIME, (void *)&time, 1.0, 0.0); + + mprintf((0, "Fear!!!!\n")); + return; + } + + // If others have died and/or left... (Me too) + if(ai_info->life_preservation > 0.5f && + mem[fear_depth].num_friends > 0 && + mem[0].num_friends == 0 && + mem[0].num_enemies > 0) + { + float time = 10.0f * ai_info->life_preservation + 5.0f; + + GoalAddGoal(obj, AIG_WANDER_AROUND, NULL, 3, 2.0f, GF_SPEED_FLEE | GF_ORIENT_VELOCITY | GF_NONFLUSHABLE); + GoalAddEnabler(obj, 3, AIE_CLEAR_TIME, (void *)&time, 1.0, 0.0); + + mprintf((0, "Fear!!!!\n")); + return; + } + } +} + +void AIDoMemFrame(object *obj) +{ + ai_frame *ai_info = obj->ai_info; + ai_info->mem_time_till_next_update -= Frametime; + + if(ai_info->mem_time_till_next_update <= 0.0f) + { + int i; + + // Compute next analyze time + ai_info->mem_time_till_next_update = 3.0f + (float)ps_rand()/(float)RAND_MAX * 2.0f; + + // Do the amount of friends/enemies left and the current shields before running Freud + short near_objs[100]; + float dist = 100.0f; + + // chrishack -- there must be a better way... + if(ai_info->coop_team * 300.0f > dist) + { + dist = ai_info->coop_team * 300.0f > dist; + } + + int num_near = fvi_QuickDistObjectList(&obj->pos, obj->roomnum, dist, near_objs, 100, false, true, false, true); + + ai_info->memory[0].num_enemies = 0; + ai_info->memory[0].num_friends = 0; + + for(i = 0; i < num_near; i++) + { + object *check_obj = &Objects[near_objs[i]]; + + if(check_obj == obj) + continue; + + if(AIObjEnemy(obj, check_obj)) + ai_info->memory[0].num_enemies++; + else if(AIObjFriend(obj, check_obj)) + ai_info->memory[0].num_friends++; + } + + ai_info->memory[0].shields = obj->shields; + + // Do emotions based on new data + AIDoFreud(obj); + + // Move emotions through the stack + for(i = AI_MEM_DEPTH - 1; i > 0; i--) + { + ai_info->memory[i] = ai_info->memory[i - 1]; + } + + // Reset the accumulators + + // These are imcremented as this memory time slice is active + ai_info->memory[0].num_enemy_shots_dodged = 0; + ai_info->memory[0].num_enemy_shots_fired = 0; + ai_info->memory[0].num_hit_enemy = 0; + ai_info->memory[0].num_times_hit = 0; + + // Dummy values, as these are computed at the end of the time frame (as seen above) + ai_info->memory[0].shields = obj->shields; + ai_info->memory[0].num_enemies = ai_info->memory[1].num_enemies; + ai_info->memory[0].num_friends = ai_info->memory[1].num_friends; + } +} + +void AIDoFrame(object *obj) +{ + + bool f_attach_done = false; + int multi_saved_weapon_flags; + char multi_saved_wb_firing; + + // AI objects don't use thrust (in general) + obj->mtype.phys_info.flags &= ~PF_USES_THRUST; + obj->mtype.phys_info.rotthrust = Zero_vector; + obj->mtype.phys_info.thrust = Zero_vector; + + if(obj->type == OBJ_DUMMY) + return; + + if((Game_mode & GM_MULTI) && (Netgame.local_role==LR_SERVER)) + { + multi_saved_wb_firing = obj->ai_info->last_special_wb_firing; + multi_saved_weapon_flags = obj->weapon_fire_flags; + } + + ai_frame *ai_info = obj->ai_info; + ASSERT((obj->control_type == CT_AI) || (obj->control_type == CT_DYING_AND_AI)); + + DebugBlockPrint("A "); + +// mprintf((0, "Awareness = %d\n", ai_info->awareness)); + + if((ai_info->flags & AIF_DISABLED) || (obj->type == OBJ_DUMMY)) + { + DebugBlockPrint("DA"); + return; + } + + // Animate the object + ai_do_animation(obj, Frametime); + if(ai_info->animation_type == AS_IDLE) + { + f_attach_done = true; + } + + if(Demo_flags == DF_PLAYBACK) + { + // All we want is animation + return; + } + // Handle On/off and spray weapons + { + if ((obj->weapon_fire_flags & WFF_SPRAY) && !(obj->flags & (OF_DESTROYED|OF_DYING))) + { + char wb_index = ai_info->last_special_wb_firing; + DoSprayEffect (obj, &Object_info[obj->id].static_wb[wb_index], wb_index); + if(!((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT))) + { + if(WBIsBatteryReady(obj, &Object_info[obj->id].static_wb[wb_index], wb_index) && !(Cinematic_inuse && !(ai_info->status_reg & AISR_OK_TO_FIRE_DURING_CINEMATICS))) + { + WBFireBattery(obj, &Object_info[obj->id].static_wb[wb_index], wb_index); + } + } + } + + if (obj->weapon_fire_flags & WFF_ON_OFF) + { + FireOnOffWeapon (obj); + } + } + + if((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT)) + { + DebugBlockPrint("DA"); + if(!f_attach_done) + { + AttachUpdateSubObjects(obj); + f_attach_done = true; + } + return; + } + + // If awareness if forced, do so + if((ai_info->flags & AIF_FORCE_AWARENESS) && (ai_info->awareness < AWARE_MOSTLY)) + { + ai_info->awareness = AWARE_MOSTLY; + } + + // If not multiplayer - these must be set each frame + obj->weapon_fire_flags = 0; + + // If the object is dead, it does not think + if(obj->flags & OF_DEAD) + { + DebugBlockPrint("DA"); + if(!f_attach_done) + { + AttachUpdateSubObjects(obj); + f_attach_done = true; + } + return; + } + + AIDoMemFrame(obj); + + if(ai_info->flags & AIF_DETERMINE_TARGET) + AIDetermineTarget(obj); + + // Update the robot's eyes -- gets player distance -- (only if AI cares about player) + if(ai_info->notify_flags & (0x00000001 << AIN_SEE_TARGET)) + AICheckTargetVis(obj); + + // Updates some of the AI status registers + ai_update_registers(obj); + + // Have goals result in movement + // I will think if either the player or me sees anything within 4 seconds + if((ai_info->awareness) || (ai_info->flags & AIF_PERSISTANT)) + { + // Does the scripting stuff for an AI + ai_do_script_stuff(obj); + + if(ai_info->ai_type == AIT_BIRD_FLOCK1) + ai_info->flags |= AIF_TRACK_CLOSEST_2_FRIENDS; // chrishack -- remove -- testing only + + // Determines all tracked objects + AIDoTrackFrame(obj); + + // Does goal updating for the frame + GoalDoFrame(obj); + + // Allow the robot to move + if(obj->control_type == CT_AI) + { + if(ai_info->animation_type != AS_FLINCH) + { + float speed; + + #ifdef _DEBUG + if(!Game_do_ai_movement) + { + goto after_move; + } + #endif + + ai_move(obj); + + speed = vm_GetMagnitude(&obj->mtype.phys_info.velocity); + + // Removes the framerate independance from objects moving within 2x of there normal max speed + if(speed > 0.1f && speed <= ai_info->max_velocity * 2.0) + { + if(obj->mtype.phys_info.drag > 0.0f && obj->mtype.phys_info.mass > 0.0f) + { + obj->mtype.phys_info.flags |= PF_USES_THRUST; + obj->mtype.phys_info.rotthrust = Zero_vector; + obj->mtype.phys_info.thrust = obj->mtype.phys_info.velocity * obj->mtype.phys_info.drag; + } + } + + #ifdef _DEBUG + after_move: + ; + #endif + } + + if(!f_attach_done) + { + AttachUpdateSubObjects(obj); + f_attach_done = true; + } + + if(!((ai_info->status_reg & AISR_MELEE) || (ai_info->flags & AIF_DISABLE_FIRING))) + ai_fire(obj); + } + else + { + if(obj->movement_type == MT_WALKING && ai_info->animation_type == AS_DEATH) + { + vector mdir; + bool f_moved; + + AIMoveTowardsPosition(obj, &obj->pos, 1.5f, false, &mdir, &f_moved); + if(!f_moved) + { + AIMoveTowardsDir(obj, &mdir, 1.0f); + } + } + } + } + + if(!f_attach_done) + { + AttachUpdateSubObjects(obj); + f_attach_done = true; + } + + if(obj->movement_type == MT_WALKING) + ai_walker_stuff(obj); + + // Animation state changes based on current level of awareness + if(obj->control_type == CT_AI) + do_awareness_based_anim_stuff(obj); + + // Decrease awareness + if(obj->control_type == CT_AI) + ai_decrease_awareness(obj); + + if(obj->ai_info->awareness > AWARE_BARELY && + obj->ai_info->target_handle == Player_object->handle && + (obj->ai_info->last_see_target_time + (CHECK_VIS_INFREQUENTLY_INTERVAL * 2.0f)) >= Gametime && + (obj->ai_info->last_hear_target_time + (CHECK_VIS_INFREQUENTLY_INTERVAL * 2.0f)) >= Gametime && + obj->parent_handle != Player_object->handle) + { + AI_NumHostileAlert++; + } + + if((Game_mode & GM_MULTI) && (Netgame.local_role==LR_SERVER)) + { + if((multi_saved_wb_firing != obj->ai_info->last_special_wb_firing) || + (multi_saved_weapon_flags != obj->weapon_fire_flags)) + { + MultiSendAiWeaponFlags(obj, obj->weapon_fire_flags, obj->ai_info->last_special_wb_firing); + } + } + + DebugBlockPrint("DA"); +} + + +void AIFrameAll(void) +{ + int i; + + if((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT)) + return; + + // Currently, -- chrishack -- In multiplayer, all robots are aware. + if(Game_mode & GM_MULTI) + { + for(i = 0; i <= Highest_object_index; i++) + { + if(Objects[i].ai_info) + { + AINotify(&Objects[i], AIN_PLAYER_SEES_YOU, NULL); + } + } + } + else + { + for(i = 0; i < AI_NumRendered; i++) + { + AINotify(&Objects[AI_RenderedList[i]], AIN_PLAYER_SEES_YOU, NULL); + } + } +} + +void AIPowerSwitch(object *obj, bool f_on) +{ + ai_frame *ai_info = obj->ai_info; + if(obj->control_type != CT_AI) return; + + if(f_on) + ai_info->flags &= (~AIF_DISABLED); + else + ai_info->flags |= AIF_DISABLED; + + poly_model *parent_pm = &Poly_models[obj->rtype.pobj_info.model_num]; + int i; + + if(!obj->attach_children) + return; + + for(i = 0; i < parent_pm->n_attach; i++) + { + object *child; + + if((child = ObjGet(obj->attach_children[i])) != NULL) + { + AIPowerSwitch(child, f_on); + } + } +} + +int AIFindRoomWithFlag(object *obj, int flag) +{ + int cur_room = obj->roomnum; + float best_dist = 0.0f; + int best_room = -1; + int i; + + for(i = 0; i <= Highest_room_index; i++) + { + if((Rooms[i].used) && (Rooms[i].flags & flag)) + { + float cur_dist; + + if(BOA_ComputeMinDist(obj->roomnum, i, 0.0f, &cur_dist, NULL)) + { + if(best_room == -1 || cur_dist < best_dist) + { + bool f_path_exists = AIFindAltPath(obj, cur_room, i, &cur_dist); + + if(f_path_exists && (best_room == -1 || cur_dist < best_dist)) + { + best_dist = cur_dist; + best_room = i; + } + } + } + } + + } + + return best_room; +} + +bool AIFindHidePos(object *hide_obj, object *view_obj, vector *hpos, int *hroom, float max_hide_time) +{ + return false; +} + +object *AIFindObjOfType(object *obj, int type, int id, bool f_ignore_init_room, int parent_handle) +{ + int cur_room = obj->roomnum; + float best_dist = 0.0f; + object *best_obj = NULL; + int i; + int my_obj_index = OBJNUM(obj); + + for(i = 0; i <= Highest_object_index; i++) + { + if((Objects[i].type == type || (type == OBJ_ROBOT && Objects[i].type == OBJ_BUILDING)) && i != my_obj_index) + { + float cur_dist; + + if(f_ignore_init_room && (Objects[i].roomnum == obj->roomnum)) + continue; + + if(id != -1 && id != Objects[i].id) + continue; + + // Allow us to find a players powerups + if(parent_handle != OBJECT_HANDLE_NONE && parent_handle != Objects[i].parent_handle) + continue; + + // Invisible powerups + if(Objects[i].render_type == RT_NONE) + continue; + + // Dying robots, other non-active robots + if(Objects[i].type == OBJ_ROBOT && Objects[i].control_type != CT_AI) + continue; + + // Robots that don't shoot + poly_model *pm = &Poly_models[Objects[i].rtype.pobj_info.model_num]; + if(Objects[i].type == OBJ_ROBOT && pm->num_wbs == 0) + continue; + + // cameras + if(Objects[i].type == OBJ_ROBOT && pm->num_wbs == 1 && Object_info[Objects[i].id].static_wb[0].num_masks == 1 && Object_info[Objects[i].id].static_wb[0].gp_fire_masks[0] == 0) + continue; + + if(BOA_ComputeMinDist(obj->roomnum, Objects[i].roomnum, 0.0f, &cur_dist, NULL)) + { + if(best_obj == NULL || cur_dist < best_dist) + { + bool f_path_exists = AIFindAltPath(obj, cur_room, Objects[i].roomnum, &cur_dist); + + if(f_path_exists && (best_obj == NULL || cur_dist < best_dist)) + { + best_dist = cur_dist; + best_obj = &Objects[i]; + } + } + } + } + + } + + return best_obj; +} + +int AIMakeNextRoomList(int roomnum, int *next_rooms, int max_rooms) +{ + int num_next_rooms = 0; + int i,j; + int croom; + + if(!ROOMNUM_OUTSIDE(roomnum) && roomnum <= Highest_room_index) + { + for(i = 0; i < Rooms[roomnum].num_portals; i++) + { + bool f_found = false; + + if(Rooms[roomnum].portals[i].croom >= 0) + { + if(Rooms[Rooms[roomnum].portals[i].croom].flags & RF_EXTERNAL) + { + croom = Highest_room_index + TERRAIN_REGION(GetTerrainRoomFromPos(&Rooms[Rooms[roomnum].portals[i].croom].portals[Rooms[roomnum].portals[i].cportal].path_pnt)) + 1; + } + else + { + croom = Rooms[roomnum].portals[i].croom; + } + + for(j = 0; j < num_next_rooms; j++) + { + if(next_rooms[j] == croom) + { + f_found = true; + break; + } + } + + if(!f_found) + { + // If you hit assert, get chris -- make constant larger + ASSERT(num_next_rooms < max_rooms); + + ASSERT((croom >= 0 && croom <= Highest_room_index + 8) || (ROOMNUM_OUTSIDE(croom) && CELLNUM(roomnum) > 0 && CELLNUM(roomnum) < TERRAIN_WIDTH * TERRAIN_DEPTH)); + + next_rooms[num_next_rooms] = croom; + num_next_rooms++; + } + } + } + } + else + { + int t_index; + + if(BOA_num_terrain_regions == 0) + { + return 0; + } + + if(roomnum > Highest_room_index && roomnum <= Highest_room_index + 8) + { + t_index = roomnum - Highest_room_index - 1; + } + else + { + t_index = TERRAIN_REGION(roomnum); + } + + ASSERT(t_index >= 0 && t_index < BOA_num_terrain_regions); + + for(i = 0; i < BOA_num_connect[t_index]; i++) + { + bool f_found = false; + croom = BOA_connect[t_index][i].roomnum; + + for(j = 0; j < num_next_rooms; j++) + { + if(next_rooms[j] == croom) + { + f_found = true; + break; + } + } + + if(!f_found) + { + // If you hit assert, get chris -- make constant larger + ASSERT(num_next_rooms < max_rooms); + next_rooms[num_next_rooms++] = croom; + } + } + } + + return num_next_rooms; +} + diff --git a/Descent3/BOA.cpp b/Descent3/BOA.cpp new file mode 100644 index 000000000..8773b95b7 --- /dev/null +++ b/Descent3/BOA.cpp @@ -0,0 +1,3447 @@ +/* +* $Logfile: /DescentIII/main/BOA.cpp $ +* $Revision: 121 $ +* $Date: 4/19/00 5:09p $ +* $Author: Matt $ +* +* Description goes here +* +* $Log: /DescentIII/main/BOA.cpp $ + * + * 121 4/19/00 5:09p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * Mac-only optimizations and mem alloc/free change + * + * 120 3/20/00 12:01p Matt + * Merge of Duane's post-1.3 changes. + * Changed some malloc/free calls to mem_malloc/mem_free (Mac only) + * + * 119 10/08/99 4:27p Chris + * Fixed a bug with terrain regioning + * + * 118 6/11/99 6:06p Chris + * + * 117 6/11/99 6:00p Chris + * Added status bars for boa_vis + * + * 116 6/11/99 12:22p Chris + * NewEditor changes + * + * 115 6/11/99 12:20p Chris + * New Editor changes + * + * 114 5/23/99 12:32a Chris + * Forcefields are now ignored in vis stuff + * + * 113 5/21/99 3:27a Chris + * Fuck me... Broken fix... Fixed? I hope. + * + * 112 5/21/99 1:35a Chris + * Fixed a bug in vis (outside areas could only see the first room down) + * :( + * + * 111 5/20/99 1:16a Chris + * Made going outside more expensive + * + * 110 5/10/99 10:10p Ardussi + * changes to compile on Mac + * + * 109 5/09/99 3:25p Jason + * fixed potential BOA problem + * + * 108 5/03/99 5:54p Kevin + * removed logging code + * + * 107 5/03/99 5:45p Jason + * trying to track down boa problem + * + * 106 5/03/99 5:12p Jason + * fixing BOA vis problem... + * + * 105 5/01/99 5:12p Jason + * made BOA checksum finally reliable + * + * 104 5/01/99 4:45p Chris + * Fixed a bug with bogus vis checksums + * + * 103 4/30/99 2:46p Chris + * Added a pragma for turning optimazatons off for checksum functions + * + * 102 4/30/99 11:54a Chris + * + * 101 4/29/99 5:43p Chris + * Added the check for bad center points + * + * 100 4/28/99 1:20p Chris + * Added the ability to block portals + * + * 99 4/27/99 12:57p Chris + * + * 98 4/27/99 11:42a Chris + * Fixed a multi-system induced bug + * + * 97 4/27/99 10:48a Chris + * Slowly and carefully improving BNodes + * + * 96 4/21/99 1:30p Matt + * Make breakable glass use the new breakable flag, instead of + * piggybacking on the destroyable flag. + * + * 95 4/21/99 3:03a Chris + * Fixed bug with blastable doors and the AI code. :) + * + * 94 4/20/99 8:55p Chris + * Fixed problem with robots not being able to open locked doors that a + * player has the key for. + * + * 93 4/18/99 5:39a Chris + * Vastly improved the path node system +* +* $NoKeywords: $ +*/ + +#ifdef EDITOR +#include "editor\d3edit.h" +#endif +#ifdef NEWEDITOR +#include "neweditor\globals.h" +#endif + +#include "BOA.h" +#include "vecmat.h" +#include "room.h" +#include +#include +#include +#include "object.h" +#include "bsp.h" +#include "pserror.h" +#include "findintersection.h" +#include "mem.h" +#include "doorway.h" +#include "string.h" + +#define BOA_VERSION 25 + +const ubyte bbf_lookup[27] = +{ +(0), +(0x01), +(0x02), +(0x04), +(0x08), +(0x10), +(0x20), +(0x01 | 0x02), +(0x01 | 0x04), +(0x01 | 0x10), +(0x01 | 0x20), +(0x02 | 0x04), +(0x02 | 0x08), +(0x02 | 0x20), +(0x04 | 0x08), +(0x04 | 0x10), +(0x08 | 0x10), +(0x08 | 0x20), +(0x10 | 0x20), +(0x01 | 0x02 | 0x04), +(0x01 | 0x02 | 0x20), +(0x01 | 0x04 | 0x10), +(0x01 | 0x10 | 0x20), +(0x08 | 0x02 | 0x04), +(0x08 | 0x02 | 0x20), +(0x08 | 0x04 | 0x10), +(0x08 | 0x10 | 0x20) +}; + +unsigned short BOA_Array[MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS][MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS]; +float BOA_cost_array[MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS][MAX_PATH_PORTALS]; +int BOA_mine_checksum = 0; +int BOA_vis_checksum=0; // this checksum is for the VIS bit of the boa array +bool BOA_vis_valid=0; // Is the vis table up to date and valid to use? +int BOA_AABB_checksum = 0; +int BOA_AABB_ROOM_checksum[MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS]; + +bool BOA_f_making_boa = false; + +int BOA_num_mines = 0; +int BOA_num_terrain_regions = 0; + +int BOA_num_connect[MAX_BOA_TERRAIN_REGIONS]; +connect_data BOA_connect[MAX_BOA_TERRAIN_REGIONS][MAX_PATH_PORTALS]; + +void ComputeBOAVisFaceUpperLeft (room *rp,face *fp,vector *upper_left,float *xdiff,float *ydiff,vector *center); + +bool BOA_PassablePortal(int room, int portal_index, bool f_for_sound, bool f_making_robot_path_invalid_list) +{ + if(room == -1) + { + return false; + } + + room = BOA_INDEX(room); + + if(room > Highest_room_index && room <= Highest_room_index + BOA_num_terrain_regions) + { + int tr; + + tr = room - Highest_room_index - 1; + + // Inside room/portal + int temp_room = BOA_connect[tr][portal_index].roomnum; + int temp_portal_index = BOA_connect[tr][portal_index].portal; + + // External room/portal + room = Rooms[temp_room].portals[temp_portal_index].croom; + portal_index = Rooms[temp_room].portals[temp_portal_index].cportal; + } + + ASSERT(room >= 0 && room <= Highest_room_index && Rooms[room].used); + face *fp = &Rooms[room].faces[Rooms[room].portals[portal_index].portal_face]; + + if(Rooms[room].portals[portal_index].croom < 0) + return false; + + if(!BOA_f_making_boa) + { + if(BOA_cost_array[room][portal_index] < 0.0f && !(room <= Highest_room_index && (Rooms[room].flags & RF_EXTERNAL))) + return false; + + if(!f_for_sound) + { + if(Rooms[room].portals[portal_index].flags & PF_TOO_SMALL_FOR_ROBOT) + return false; + } + + if((Rooms[room].portals[portal_index].flags & PF_RENDER_FACES) && !(Rooms[room].portals[portal_index].flags & PF_RENDERED_FLYTHROUGH) || (Rooms[room].portals[portal_index].flags & PF_BLOCK)) + { + return false; + } + } + else + { + if(f_making_robot_path_invalid_list) + { + if(Rooms[room].portals[portal_index].flags & PF_TOO_SMALL_FOR_ROBOT) + return false; + } + + if((Rooms[room].portals[portal_index].flags & PF_BLOCK) && !(Rooms[room].portals[portal_index].flags & PF_BLOCK_REMOVABLE)) + return false; + + if((Rooms[room].portals[portal_index].flags & PF_RENDER_FACES) && !(Rooms[room].portals[portal_index].flags & PF_RENDERED_FLYTHROUGH)) + { + if(!(GameTextures[fp->tmap].flags & (TF_BREAKABLE | TF_FORCEFIELD))) + { + return false; + } + } + } + + return true; +} + +extern object *GetDoorObject(room *rp); + +bool BOA_LockedDoor(object *obj, int roomnum) +{ + if(roomnum >= 0 && roomnum <= Highest_room_index && Rooms[roomnum].used && (Rooms[roomnum].flags & RF_DOOR)) + { + if(!obj) + { + return DoorwayLocked(&Rooms[roomnum]) && DoorwayPosition(&Rooms[roomnum]) < 0.5f; + } + else + { + object *d_obj = GetDoorObject(&Rooms[roomnum]); + + if(d_obj) + return (!DoorwayOpenable(d_obj->handle, obj->handle)) && DoorwayPosition(&Rooms[roomnum]) < 0.5f; + else + return false; + } + } + + return false; +} + +//bool BOA_IsPathClearForRobot(int start_room, int end_room) +//{ +// start_room = BOA_INDEX(start_room); +// end_room = BOA_INDEX(end_room); +// +// if(BOA_TOO_SMALL_FOR_ROBOT(start_room, end_room)) +// { +// return false; +// } +// +// if(!BOA_HasPossibleBlockage(start_room, end_room)) +// { +// return true; +// } +// +// int last_room; +// int next_room = start_room; +// ASSERT(next_room >= 0 && next_room <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); +// +// while(BOA_INDEX(next_room) != BOA_INDEX(start_room) && (BOA_INDEX(next_room) != BOA_INDEX(end_room)) && (next_room != BOA_NO_PATH)) +// { +// if(BOA_LockedDoor(next_room)) +// { +// return false; +// } +// +// last_room = next_room; +// next_room = BOA_GetNextRoom(next_room, end_room); +// +// if(BOA_DetermineStartRoomPortal(last_room, NULL, next_room, NULL, false, NULL) == -1) +// { +// return false; +// } +// } +// +// return true; +//} + +int BOA_DetermineStartRoomPortal(int start_room, vector *start_pos, int end_room, vector *end_pos, bool f_for_sound, bool f_making_robot_path_invalid_list, int *blocked_portal) +{ + int i; + + if(start_room > Highest_room_index && end_room > Highest_room_index) + return -1; + + start_room = BOA_INDEX(start_room); + end_room = BOA_INDEX(end_room); + + if(start_room <= Highest_room_index) + { + for(i = 0; i < Rooms[start_room].num_portals; i++) + { + if(!BOA_PassablePortal(start_room, i, f_for_sound, f_making_robot_path_invalid_list)) + continue; + + if(end_room <= Highest_room_index) + { + if(Rooms[start_room].portals[i].croom == end_room) + break; + } + else + { + if(Rooms[Rooms[start_room].portals[i].croom].flags & RF_EXTERNAL) + { + int cell = GetTerrainCellFromPos(&Rooms[start_room].portals[i].path_pnt); + ASSERT(cell != -1); //DAJ -1FIX + + if(Highest_room_index + TERRAIN_REGION(cell) + 1 == end_room) + break; + } + } + } + + if(i >= Rooms[start_room].num_portals) + i = -1; + } + else + { + for(i = 0; i < BOA_num_connect[start_room - Highest_room_index - 1]; i++) + { + ASSERT(end_room <= Highest_room_index); + + if(BOA_connect[start_room - Highest_room_index - 1][i].roomnum == end_room) + { + int next_portal = BOA_connect[start_room - Highest_room_index - 1][i].portal; + int external_room = Rooms[end_room].portals[next_portal].croom; + int external_portal = Rooms[end_room].portals[next_portal].cportal; + + if(BOA_PassablePortal(external_room, external_portal, f_for_sound, f_making_robot_path_invalid_list)) + { + break; + } + } + } + + if(i >= BOA_num_connect[start_room - Highest_room_index - 1]) + i = -1; + } + + return i; +} + +bool BOA_ComputeMinDist(int start_room, int end_room, float max_check_dist, float *dist, int *num_blockages) +{ + *dist = 0.0f; + + start_room = BOA_INDEX(start_room); + end_room = BOA_INDEX(end_room); + + if(start_room == end_room) + { + return true; + } + + if(start_room == Highest_room_index + 1 && end_room > Highest_room_index) + { + return true; + } + + if(end_room == Highest_room_index + 1 && start_room > Highest_room_index) + { + return true; + } + + int cur_room = end_room; + int last_room; + + if(start_room < 0 || end_room < 0) + { + return false; + } + + if(start_room > Highest_room_index + BOA_num_terrain_regions || + end_room > Highest_room_index + BOA_num_terrain_regions) + { + return false; + } + + if(start_room <= Highest_room_index && !Rooms[start_room].used) + return false; + + if(end_room <= Highest_room_index && !Rooms[end_room].used) + return false; + + do + { + last_room = cur_room; + + if(cur_room <= Highest_room_index && num_blockages && (Rooms[cur_room].flags & RF_DOOR) && (cur_room != end_room)) + { + float door_position = DoorwayGetPosition(&Rooms[cur_room]); + + *num_blockages += 1.0f - door_position; + } + + cur_room = BOA_NEXT_ROOM(cur_room, start_room); + int last_portal; + + if(last_room == cur_room || cur_room == BOA_NO_PATH) + return false; + + if(BOA_INDEX(last_room) != BOA_INDEX(cur_room)) + { + last_portal= BOA_DetermineStartRoomPortal(last_room, NULL, cur_room, NULL); + } + + if(last_room == end_room) + { + int this_portal = BOA_DetermineStartRoomPortal(cur_room, NULL, last_room, NULL); + + if(cur_room != start_room) + *dist += BOA_cost_array[cur_room][this_portal]; + if(max_check_dist > 0.0 && max_check_dist < *dist) + return false; + } + else if(cur_room == start_room) + { + *dist += BOA_cost_array[last_room][last_portal]; + if(max_check_dist > 0.0 && max_check_dist < *dist) + return false; + } + else if((cur_room != last_room) && (cur_room != BOA_NO_PATH)) + { + int this_portal = BOA_DetermineStartRoomPortal(cur_room, NULL, last_room, NULL); + + *dist += BOA_cost_array[last_room][last_portal] + BOA_cost_array[cur_room][this_portal]; + if(max_check_dist > 0.0f && max_check_dist < *dist) + return false; + } + + } while((cur_room != start_room) && (cur_room != last_room) && (cur_room != BOA_NO_PATH)); + + if(cur_room == BOA_NO_PATH) + { + return false; + } + return true; +} + +bool BOA_IsSoundAudible(int start_room, int end_room) +{ + int s_index = start_room; + int e_index = end_room; + + if(start_room == -1 || end_room == -1) + { + return false; + } + + if((!ROOMNUM_OUTSIDE(s_index)) && s_index <= Highest_room_index) + { + if(!Rooms[s_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(s_index)) + { + s_index = TERRAIN_REGION(start_room) + Highest_room_index + 1; + } + else + { + ASSERT(s_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + if((!ROOMNUM_OUTSIDE(e_index)) && e_index <= Highest_room_index) + { + if(!Rooms[e_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(e_index)) + { + e_index = TERRAIN_REGION(end_room) + Highest_room_index + 1; + } + else + { + ASSERT(e_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + return ((BOA_Array[s_index][e_index] & BOA_SOUND_PROP) != 0); +} + +bool BOA_HasPossibleBlockage(int start_room, int end_room) +{ + int s_index = start_room; + int e_index = end_room; + + if(start_room == -1 || end_room == -1) + { + return false; + } + + if((!ROOMNUM_OUTSIDE(s_index)) && s_index <= Highest_room_index) + { + if(!Rooms[s_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(s_index)) + { + s_index = TERRAIN_REGION(start_room) + Highest_room_index + 1; + } + else + { + ASSERT(s_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + if((!ROOMNUM_OUTSIDE(e_index)) && e_index <= Highest_room_index) + { + if(!Rooms[e_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(e_index)) + { + e_index = TERRAIN_REGION(end_room) + Highest_room_index + 1; + } + else + { + ASSERT(e_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + return ((BOA_Array[s_index][e_index] & BOAF_BLOCKAGE) != 0); +} +bool BOA_IsVisible(int start_room, int end_room) +{ + int s_index = start_room; + int e_index = end_room; + + if(!BOA_vis_valid) + return true; + + if (start_room==end_room) + return true; + + if(start_room == -1 || end_room == -1) + { + return false; + } + + if((!ROOMNUM_OUTSIDE(s_index)) && s_index <= Highest_room_index) + { + if(!Rooms[s_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(s_index)) + { + s_index = TERRAIN_REGION(start_room) + Highest_room_index + 1; + } + else + { + ASSERT(s_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + if((!ROOMNUM_OUTSIDE(e_index)) && e_index <= Highest_room_index) + { + if(!Rooms[e_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(e_index)) + { + e_index = TERRAIN_REGION(end_room) + Highest_room_index + 1; + } + else + { + ASSERT(e_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + return ((BOA_Array[s_index][e_index] & BOAF_VIS) != 0); +} + +int BOA_GetNextRoom(int start_room, int end_room) +{ + int s_index = start_room; + int e_index = end_room; + + if(start_room == -1 || end_room == -1) + { + return false; + } + + if((!ROOMNUM_OUTSIDE(s_index)) && s_index <= Highest_room_index) + { + if(!Rooms[s_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(s_index)) + { + s_index = TERRAIN_REGION(start_room) + Highest_room_index + 1; + } + else + { + ASSERT(s_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + if((!ROOMNUM_OUTSIDE(e_index)) && e_index <= Highest_room_index) + { + if(!Rooms[e_index].used) + { + return false; + } + } + else if(ROOMNUM_OUTSIDE(e_index)) + { + e_index = TERRAIN_REGION(end_room) + Highest_room_index + 1; + } + else + { + ASSERT(e_index <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + } + + return ((BOA_Array[s_index][e_index] & BOA_ROOM_MASK)); +} + +void add_mine_room(int room, int mine, char *checked) +{ + int i; + + Rooms[room].flags |= (mine << 20); + checked[room] = 1; + + for(i = 0; i < Rooms[room].num_portals; i++) + { + if(Rooms[room].portals[i].croom >= 0 && !checked[Rooms[room].portals[i].croom]) + { + add_mine_room(Rooms[room].portals[i].croom, mine, checked); + } + } +} + +void compute_mine_info() +{ + int i; + char checked[MAX_ROOMS]; + bool done = false; + int first_free; + int cur_mine = 0; + + for(i = 0; i <= Highest_room_index; i++) + { + room *rp = &Rooms[i]; + + if(rp->used) + { + rp->flags &= ~RFM_MINE; + } + + checked[i] = 0; + } + + while(!done) + { + ASSERT(cur_mine < 32); + + done = true; + + for(i = 0; i <= Highest_room_index; i++) + { + room *rp = &Rooms[i]; + + if(rp->used && !checked[i]) + { + first_free = i; + done = false; + break; + } + } + + if(!done) + { + add_mine_room(first_free, cur_mine, checked); + cur_mine++; + } + } + + BOA_num_mines = cur_mine; +} + +void add_terrain_cell(int cell, int t_region, char *checked) +{ + int depth = 0; + int i; +#ifdef MACINTOSH // JCA: Mac compiler cannot allocate more than 32k on stack as local variables + unsigned short *stack; + char *on_stack; + + stack = (unsigned short*)mem_malloc(TERRAIN_WIDTH * TERRAIN_DEPTH * sizeof(unsigned short)); + on_stack = (char*)mem_malloc(TERRAIN_WIDTH * TERRAIN_DEPTH * sizeof(char)); +#else + unsigned short stack[TERRAIN_WIDTH * TERRAIN_DEPTH]; + char on_stack[TERRAIN_WIDTH * TERRAIN_DEPTH]; +#endif + + for(i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) + on_stack[i] = false; + + stack[depth++] = cell; + on_stack[cell] = true; + + while(depth > 0) + { + cell = stack[--depth]; + Terrain_seg[cell].flags |= (t_region << 5); + checked[cell] = 1; + + int xcounter, ycounter; + + int xstart = cell%TERRAIN_WIDTH - 1; + int xend = cell%TERRAIN_WIDTH + 1; + int ystart = cell/TERRAIN_WIDTH - 1; + int yend = cell/TERRAIN_WIDTH + 1; + + if(xstart < 0) xstart = 0; + if(xend >= TERRAIN_WIDTH) xend = TERRAIN_WIDTH - 1; + if(ystart < 0) ystart = 0; + if(yend >= TERRAIN_DEPTH) yend = TERRAIN_DEPTH - 1; + + // This should be a faster interative why to do a square with center at original position + int cur_node = TERRAIN_WIDTH * ystart + xstart; + int next_y_delta = TERRAIN_WIDTH - (xend - xstart) - 1; + + for(ycounter = ystart; ycounter <= yend; ycounter++) + { + for(xcounter = xstart; xcounter <= xend; xcounter++) + { + if(!on_stack[cur_node] && !checked[cur_node] && Terrain_seg[cur_node].y < MAX_TERRAIN_HEIGHT) + { + stack[depth++] = cur_node; + on_stack[cur_node] = true; + } + cur_node += 1; + } + + cur_node += next_y_delta; + } + } +#ifdef MACINTOSH + mem_free(stack); + mem_free(on_stack); +#endif +} + +void compute_terrain_region_info() +{ + int i; + bool done = false; + bool f_warning = false; +#ifdef MACINTOSH // JCA: Mac compiler cannot allocate more than 32k on stack as local variables + char *checked; + + checked = (char*)mem_malloc(TERRAIN_WIDTH * TERRAIN_DEPTH * sizeof(char)); +#else + char checked[TERRAIN_WIDTH * TERRAIN_DEPTH]; +#endif + + for(i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) + { + Terrain_seg[i].flags &= (~TFM_REGION_MASK); + ASSERT((Terrain_seg[i].flags&TFM_REGION_MASK) == 0); + checked[i] = 0; + } + +#ifdef _DEBUG + for(i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) + { + ASSERT((Terrain_seg[i].flags&TFM_REGION_MASK) == 0); + ASSERT(((Terrain_seg[i].flags&TFM_REGION_MASK)>>5) == 0); + } +#endif + + // Find saturated points + for(i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) + { + if(Terrain_seg[i].y > MAX_TERRAIN_HEIGHT - 6.0f) + { + checked[i] = 1; + } + } + + char t_region = 1; + + while(!done) + { + if(t_region >= MAX_BOA_TERRAIN_REGIONS) + { +#ifdef EDITOR + OutrageMessageBox("This terrain has too many regions!\nAI will not work correctly outside!\nSee Chris and-or saturate useless areas!"); +#endif + break; + } + + int first_free; + + done = true; + + for(i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) + { + if(!checked[i]) + { + first_free = i; + done = false; + break; + } + } + + if(!done) + { + add_terrain_cell(first_free, t_region, checked); + t_region++; + } + } + + BOA_num_terrain_regions = t_region; + +#ifdef _DEBUG + for(i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) + { + ASSERT(TERRAIN_REGION(i) < BOA_num_terrain_regions); + } +#endif + + for(i = 0; i < MAX_BOA_TERRAIN_REGIONS; i++) + { + BOA_num_connect[i] = 0; + } + + for(i = 0; i <= Highest_room_index; i++) + { + if((Rooms[i].used) && (Rooms[i].flags & RF_EXTERNAL)) + { + int j; + + for(j = 0; j < Rooms[i].num_portals; j++) + { + int cell = GetTerrainCellFromPos(&Rooms[i].portals[j].path_pnt); + ASSERT(cell != -1); //DAJ -1FIX + + int region = TERRAIN_REGION(cell); + + if(!(BOA_num_connect[region] < MAX_PATH_PORTALS)) + { + f_warning = true; + break; + } + +// if(region != 0) +// { +// BOA_connect[0][BOA_num_connect[region]].roomnum = Rooms[i].portals[j].croom; +// BOA_connect[0][BOA_num_connect[region]].portal = Rooms[i].portals[j].cportal; +// } + + if(!(Rooms[Rooms[i].portals[j].croom].flags & RF_EXTERNAL)) + { + if(BOA_PassablePortal(i, j)) + { + BOA_connect[region][BOA_num_connect[region]].roomnum = Rooms[i].portals[j].croom; + BOA_connect[region][BOA_num_connect[region]].portal = Rooms[i].portals[j].cportal; + BOA_num_connect[region]++; + } + } + } + } + } + +#ifdef EDITOR + if(f_warning) + { + OutrageMessageBox("This terrain has too many fly through\nterrain-mine connections!\n\nAI will not work correctly outside!\nIf you really cannot fly outside\nignore this message.\n\nSee Chris for specific instructions!"); + } +#endif +#ifdef MACINTOSH + mem_free(checked); +#endif +} + +#define MAX_SOUND_PROP_DIST 400.0f + +void compute_sound_dist_info() +{ + int i; + int j; + + for(i = 0; i <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS; i++) + { + for(j = 0; j <= i; j++) + { + BOA_Array[i][j] |= BOA_SOUND_PROP; + BOA_Array[j][i] |= BOA_SOUND_PROP; + + if((i > Highest_room_index || j > Highest_room_index) && (i != j)) + { + BOA_Array[i][j] &= ~BOA_SOUND_PROP; + BOA_Array[j][i] &= ~BOA_SOUND_PROP; + } + } + } + + for(i = 0; i <= Highest_room_index; i++) + { + for(j = 0; j < i; j++) + { + float dist; + bool f_ok = BOA_ComputeMinDist(i, j, MAX_SOUND_PROP_DIST, &dist); + + if(!f_ok || dist > MAX_SOUND_PROP_DIST) + { + BOA_Array[i][j] &= ~BOA_SOUND_PROP; + BOA_Array[j][i] &= ~BOA_SOUND_PROP; + } + } + } + + for(i = 0; i < BOA_num_terrain_regions; i++) + { + int j; + int k; + + for(j = 0; j < BOA_num_connect[i]; j++) + { + int croom = BOA_connect[i][j].roomnum; + + for(k = 0; k <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS; k++) + { + if(BOA_Array[croom][k] & BOA_SOUND_PROP) + { + BOA_Array[Highest_room_index + i + 1][k] |= BOA_SOUND_PROP; + BOA_Array[k][Highest_room_index + i + 1] |= BOA_SOUND_PROP; + } + } + } + } +} + +void clear_BOA() +{ + int i,j; + + for(i = 0; i <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS; i++) + { + for(j = 0; j <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS; j++) + { + BOA_Array[i][j] = i; // No flags are set and i to j points to i (so, no path exists) + } + } + + for(i = 0; i < MAX_ROOMS; i++) + { + BOA_AABB_ROOM_checksum[i] = 0; + } + + BOA_num_mines = 0; + BOA_num_terrain_regions = 0; +} + +void compute_costs() +{ + int i, j; + vector from_pnt;//, to_pnt; + vector portal_pnt; + + for(i = 0; i <= Highest_room_index; i++) + { + + if(Rooms[i].used) + { + ASSERT(Rooms[i].num_portals <= MAX_PATH_PORTALS); + + for(j = 0; j < Rooms[i].num_portals; j++) + { + if(BOA_PassablePortal(i, j)) + { + ComputeRoomCenter(&from_pnt, &Rooms[i]); + //ComputeRoomCenter(&to_pnt, &Rooms[Rooms[i].portals[j].croom]); + + ComputePortalCenter(&portal_pnt, &Rooms[i], j); + + BOA_cost_array[i][j] = vm_VectorDistance(&from_pnt, &portal_pnt); + } + else + { + BOA_cost_array[i][j] = -1.0; + } + } + } + else + { + for(j = 0; j < Rooms[i].num_portals; j++) + { + BOA_cost_array[i][j] = -1.0; + } + } + } + + for(i = Highest_room_index + 1; i <= Highest_room_index + BOA_num_terrain_regions; i++) + { + int j; + for(j = 0; j < BOA_num_connect[i]; j++) + { + BOA_cost_array[i][j] = 100000.0f; + } + } +} + +void update_path_info(q_item *node_list[MAX_ROOMS], int start, int end) +{ + int cur_room; + int par_room; + + while (end != start) + { + cur_room = end; + par_room = node_list[end]->parent; + + while(par_room != -1) + { + BOA_Array[par_room][end] = cur_room; + + cur_room = node_list[cur_room]->parent; + par_room = node_list[cur_room]->parent; + } + + end = node_list[end]->parent; + } +} + +void FindPath(int i, int j) +{ + pq PQPath; + int counter; + q_item *start_node = new q_item(BOA_INDEX(i), -1, 0.0); + q_item *cur_node; + + q_item *node_list[MAX_ROOMS + MAX_BOA_TERRAIN_REGIONS]; + +// mprintf((0, "Find path for %d to %d\n", i, j)); + + if(i == -1 || j == -1) + { + delete start_node; + return; + } + + memset(node_list, 0, sizeof(q_item *) * (MAX_ROOMS + MAX_BOA_TERRAIN_REGIONS)); + + PQPath.push(start_node); + ASSERT(start_node->roomnum <= Highest_room_index + BOA_num_terrain_regions); + + while(cur_node = PQPath.pop()) + { + node_list[BOA_INDEX(cur_node->roomnum)] = cur_node; + ASSERT(BOA_INDEX(cur_node->roomnum) >= 0 && BOA_INDEX(cur_node->roomnum) <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS); + + if(cur_node->roomnum == j) + { + update_path_info(node_list, i, j); + goto done; + } + + if(i > j) + { + int num_portals; + bool f_room = true; + int t_index; + + if(cur_node->roomnum <= Highest_room_index) + { + num_portals = Rooms[cur_node->roomnum].num_portals; + } + else + { + t_index = cur_node->roomnum - Highest_room_index - 1; + num_portals = BOA_num_connect[t_index]; + f_room = false; + } + + for(counter = 0; counter < num_portals; counter++) + { + int next_room; + q_item *list_item; + float new_cost; + + if(!BOA_PassablePortal(cur_node->roomnum, counter)) + continue; + + if(f_room) + next_room = Rooms[cur_node->roomnum].portals[counter].croom; + else + next_room = BOA_connect[t_index][counter].roomnum; + + if(next_room < 0 || next_room == BOA_NO_PATH) + continue; + + if((next_room <= Highest_room_index) && (Rooms[next_room].flags & RF_EXTERNAL)) + { + ASSERT(cur_node->roomnum <= Highest_room_index); + + int cell = GetTerrainCellFromPos(&Rooms[cur_node->roomnum].portals[counter].path_pnt); + ASSERT(cell >= 0 && cell < TERRAIN_WIDTH * TERRAIN_DEPTH); + + next_room = Highest_room_index + TERRAIN_REGION(cell) + 1; + ASSERT(next_room <= Highest_room_index + BOA_num_terrain_regions); + } + + int next_portal; + if(BOA_INDEX(next_room) != BOA_INDEX(cur_node->roomnum)) + { + next_portal = BOA_DetermineStartRoomPortal(next_room, NULL, cur_node->roomnum, NULL); + } + + new_cost = cur_node->cost + BOA_cost_array[BOA_INDEX(cur_node->roomnum)][counter] + BOA_cost_array[BOA_INDEX(next_room)][next_portal]; + + list_item = node_list[BOA_INDEX(next_room)]; + if(list_item != NULL && list_item->cost <= new_cost) + continue; + + if(list_item == NULL) + { + list_item = new q_item(BOA_INDEX(next_room), cur_node->roomnum, new_cost); + node_list[BOA_INDEX(next_room)] = list_item; + PQPath.push(list_item); + ASSERT(list_item->roomnum <= Highest_room_index + BOA_num_terrain_regions); + } + else + { + list_item->cost = new_cost; + list_item->parent = cur_node->roomnum; + } + } + } + else + { + int num_portals; + bool f_room = true; + int t_index; + + if(cur_node->roomnum <= Highest_room_index) + { + num_portals = Rooms[cur_node->roomnum].num_portals; + } + else + { + t_index = cur_node->roomnum - Highest_room_index - 1; + num_portals = BOA_num_connect[t_index]; + f_room = false; + } + + for(counter = 0; counter < num_portals; counter++) + { + int next_room; + q_item *list_item; + float new_cost; + + if(!BOA_PassablePortal(cur_node->roomnum, counter)) + continue; + + if(f_room) + next_room = Rooms[cur_node->roomnum].portals[counter].croom; + else + next_room = BOA_connect[t_index][counter].roomnum; + + if(next_room < 0 || next_room == BOA_NO_PATH) + continue; + + if((next_room <= Highest_room_index) && (Rooms[next_room].flags & RF_EXTERNAL)) + { + ASSERT(cur_node->roomnum <= Highest_room_index); + int cell = GetTerrainCellFromPos(&Rooms[cur_node->roomnum].portals[counter].path_pnt); + ASSERT(cell != -1); //DAJ -1FIX + next_room = Highest_room_index + TERRAIN_REGION(cell) + 1; + } + + int next_portal; + if(BOA_INDEX(next_room) != BOA_INDEX(cur_node->roomnum)) + { + next_portal = BOA_DetermineStartRoomPortal(next_room, NULL, cur_node->roomnum, NULL); + } + + new_cost = cur_node->cost + BOA_cost_array[BOA_INDEX(cur_node->roomnum)][counter] + BOA_cost_array[BOA_INDEX(next_room)][next_portal]; + + list_item = node_list[BOA_INDEX(next_room)]; + if(list_item != NULL && list_item->cost <= new_cost) + continue; + + if(list_item == NULL) + { + list_item = new q_item(BOA_INDEX(next_room), cur_node->roomnum, new_cost); + node_list[BOA_INDEX(next_room)] = list_item; + PQPath.push(list_item); + ASSERT(list_item->roomnum <= Highest_room_index + BOA_num_terrain_regions); + } + else + { + list_item->cost = new_cost; + list_item->parent = cur_node->roomnum; + } + } + } + } + + //Mark as an impossible path. + BOA_Array[i][j] = BOA_NO_PATH; + +// mprintf((0, "Found an impossible path\n")); + +done: + for(counter = 0; counter <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS; counter++) + { + if (node_list[counter]) delete node_list[counter]; + } + + return; +} + +void compute_next_segs() +{ + int i, j; + + for(i = 0; i <= Highest_room_index + MAX_BOA_TERRAIN_REGIONS; i++) + { + if(i <= Highest_room_index && (!Rooms[i].used)) + continue; + + if(i <= Highest_room_index && (Rooms[i].flags & RF_EXTERNAL)) + continue; + + if(i > Highest_room_index + BOA_num_terrain_regions) + continue; + + for(j = Highest_room_index + MAX_BOA_TERRAIN_REGIONS; j >= 0; j--) + { + if(j <= Highest_room_index && (!Rooms[j].used)) + continue; + + if(j <= Highest_room_index && (Rooms[j].flags & RF_EXTERNAL)) + continue; + + if(j > Highest_room_index + BOA_num_terrain_regions) + continue; + + if(i == Highest_room_index + 1 && j > Highest_room_index) + { + BOA_Array[i][j] = j; + BOA_Array[j][i] = i; + continue; + } + + if(j == Highest_room_index + 1 && i > Highest_room_index) + { + BOA_Array[i][j] = j; + BOA_Array[j][i] = i; + continue; + } + + if(i != j && BOA_Array[i][j] == i) FindPath(i, j); + if(i != j && BOA_Array[j][i] == j) FindPath(j, i); + } + } +} + +void compute_blockage_info() +{ + int i,j; + + for(i = 0; i <= Highest_room_index + BOA_num_terrain_regions; i++) + { + if(i <= Highest_room_index && (!Rooms[i].used)) + continue; + + if(i <= Highest_room_index && (Rooms[i].flags & RF_EXTERNAL)) + continue; + + if(i > Highest_room_index + BOA_num_terrain_regions) + continue; + + for(j = 0; j <= Highest_room_index + BOA_num_terrain_regions; j++) + { + int cur_room = i; + + if(i == j) + continue; + + if(i == Highest_room_index + 1 && j > Highest_room_index) + continue; + + if(j == Highest_room_index + 1 && i > Highest_room_index) + continue; + + if(j <= Highest_room_index && (!Rooms[i].used)) + continue; + + if(j <= Highest_room_index && (Rooms[i].flags & RF_EXTERNAL)) + continue; + + if(BOA_NEXT_ROOM(cur_room, j) != BOA_NO_PATH && BOA_NEXT_ROOM(cur_room, j) != cur_room) + { + int last_room = cur_room; + + do + { + if(cur_room <= Highest_room_index && (Rooms[cur_room].flags & RF_DOOR)) + { + BOA_Array[i][j] |= BOAF_BLOCKAGE; + break; + } + + last_room = cur_room; + cur_room = BOA_NEXT_ROOM(cur_room, j); + + if(last_room != cur_room) + { + BOA_f_making_boa = false; + if(BOA_DetermineStartRoomPortal(last_room, NULL, cur_room, NULL, true) == -1) + { + BOA_Array[i][j] |= BOAF_BLOCKAGE; + BOA_f_making_boa = true; + break; + } + BOA_f_making_boa = true; + } + + } while (cur_room != j); + } + } + } +} + +// Goes through all the valid points in the indoor engine and returns a unique +// checksum + +#pragma optimize( "", off ) + +int BOAGetMineChecksum () +{ + int i,t,k; + int total=0; + + for (i=0;i<=Highest_room_index;i++) + { + room *rp=&Rooms[i]; + + if (!Rooms[i].used) + continue; + + for (t=0;tnum_faces;t++) + { + face *fp=&rp->faces[t]; + + for (k=0;knum_verts;k++) + { +#ifdef MACINTOSH + int x, y, z; + x=floor(rp->verts[fp->face_verts[k]].x); + y=floor(rp->verts[fp->face_verts[k]].y); + z=floor(rp->verts[fp->face_verts[k]].z); + total += x + y + z; +#else + total+=rp->verts[fp->face_verts[k]].x; + total+=rp->verts[fp->face_verts[k]].y; + total+=rp->verts[fp->face_verts[k]].z; +#endif + } + + total += fp->num_verts << 4; + + if (fp->portal_num!=-1) + { + // factor in portal + portal *pp=&rp->portals[fp->portal_num]; + + total+=fp->portal_num; + + int flags=pp->flags; + + flags&=(PF_BLOCK|PF_BLOCK_REMOVABLE|PF_RENDERED_FLYTHROUGH); + + total+=flags; + } + } + total += rp->num_faces << 8; + + total += (rp->num_portals) << (i % 16); + + total += i; + + } + + // Now do terrain + for (i=0;inum_faces;t++) + { + face *fp=&rp->faces[t]; + + for (k=0;knum_verts;k++) + { +#ifdef MACINTOSH + float x, y, z; + x=floor(rp->verts[fp->face_verts[k]].x); + y=floor(rp->verts[fp->face_verts[k]].y); + z=floor(rp->verts[fp->face_verts[k]].z); + total += x + y + z; +#else + total+=rp->verts[fp->face_verts[k]].x; + total+=rp->verts[fp->face_verts[k]].y; + total+=rp->verts[fp->face_verts[k]].z; +#endif + } + + total += fp->num_verts << 4; + } + total += rp->num_faces << 8; + + total += (rp->num_portals) << (i % 16); + + total += i; + } + + total += BOA_VERSION << 24; + + return (int)total; +} + +#pragma optimize( "", on ) + +bool IsPathPointValid(int room, vector *pos) +{ + //vector c_pnt = Rooms[room].path_pnt; + int i; + + if(Rooms[room].flags & RF_EXTERNAL) + return true; + + for(i = 0; i < Rooms[room].num_portals; i++) + { + fvi_info hit_info; + fvi_query fq; + int fate; + + if(!BOA_PassablePortal(room, i)) + continue; + + vector portal_pnt = Rooms[room].portals[i].path_pnt; + + // shoot a ray from the light position to the current vertex + fq.p0 = &portal_pnt; + fq.p1 = pos; + fq.startroom = room; + + fq.rad = 5.0f; + fq.flags = FQ_SOLID_PORTALS | FQ_NO_RELINK; // chrishack -- Might want to make FQ_IGNORE_MOVING_OBJECTS into a passed arg + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + + fate = fvi_FindIntersection(&fq, &hit_info); + + if(fate != HIT_NONE) + { + return false; + } + } + + return true; +} + +#define MAX_SUBDIVISIONS 12 // Actually 19 (we don't do extremities) + +void ValidateRoomPathPoint(int room, char *message, int len) +{ + vector pos; + int i, j, k; + + pos = Rooms[room].path_pnt; + + if(IsPathPointValid(room, &pos)) return; + + bool f_found = false; + //vector best_pnt; + //float best_distance; + + vector diff = (Rooms[room].max_xyz - Rooms[room].min_xyz)/MAX_SUBDIVISIONS; + + for(i = 1; i < MAX_SUBDIVISIONS - 1; i++) + { + for(j = 1; j < MAX_SUBDIVISIONS - 1; j++) + { + for(k = 1; k < MAX_SUBDIVISIONS - 1; k++) + { + vector t_pnt = Rooms[room].min_xyz; + t_pnt.x += diff.x * i; + t_pnt.y += diff.y * j; + t_pnt.z += diff.z * k; + + if(IsPathPointValid(room, &t_pnt)) + { + Rooms[room].path_pnt = t_pnt; + return; + } + } + } + } + + if(!message) + mprintf((0, "Room %d has a bad center point\n", room)); + else + { + char new_message[300]; + sprintf(new_message, "Room %d has a bad center point\n", room); + + if(strlen(message) + strlen(new_message) < (unsigned int)len) + { + strcat(message, new_message); + } + } +} + +void BOA_ComputePathPoints(char *message, int len) +{ + int i; + int j; + + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + for(j = 0; j < Rooms[i].num_portals; j++) + { + ComputePortalCenter(&Rooms[i].portals[j].path_pnt, &Rooms[i], j); + } + + if(!(Rooms[i].flags & RF_MANUAL_PATH_PNT)) + { + Rooms[i].path_pnt = (Rooms[i].max_xyz + Rooms[i].min_xyz)/2.0f; + ValidateRoomPathPoint(i, message, len); + } + } + } +} + + +// Given a face, computes the upper left corner of the face +void ComputeBOAVisFaceUpperLeft (room *rp,face *fp,vector *upper_left,float *xdiff,float *ydiff,vector *center) +{ + matrix face_matrix,trans_matrix; + vector fvec; + vector avg_vert; + vector verts[MAX_VERTS_PER_FACE]; + vector rot_vert; + int i; + + // find the center point of this face + vm_MakeZero (&avg_vert); + for (i=0;inum_verts;i++) + avg_vert+=rp->verts[fp->face_verts[i]]; + + avg_vert/=fp->num_verts; + + // Make the orientation matrix + // Reverse the normal because we're looking "at" the face, not from it + fvec=-fp->normal; + + vm_VectorToMatrix(&face_matrix,&fvec,NULL,NULL); + // Make the transformation matrix + + angvec avec; + vm_ExtractAnglesFromMatrix(&avec,&face_matrix); + vm_AnglesToMatrix (&trans_matrix,avec.p,avec.h,avec.b); + + // Rotate all the points + for (i=0;inum_verts;i++) + { + vector vert=rp->verts[fp->face_verts[i]]; + + vert-=avg_vert; + vm_MatrixMulVector (&rot_vert,&vert,&trans_matrix); + + verts[i]=rot_vert; + } + + // Find left most point + int leftmost_point=-1; + float leftmost_x=900000.00f; // a big number + + for (i=0;inum_verts;i++) + { + if (verts[i].xnum_verts;i++) + { + if (verts[i].y>topmost_y) + { + topmost_point=i; + topmost_y=verts[i].y; + } + } + + ASSERT (topmost_point!=-1); + + // Find right most point + int rightmost_point=-1; + float rightmost_x=-900000.00f; // a big number + + for (i=0;inum_verts;i++) + { + if (verts[i].x>rightmost_x) + { + rightmost_point=i; + rightmost_x=verts[i].x; + } + } + + ASSERT (rightmost_point!=-1); + + // Find bottom most point + int bottommost_point=-1; + float bottommost_y=900000.0f; // a big number + + for (i=0;inum_verts;i++) + { + if (verts[i].y100) pos = 100; + + static CProgressDialog *gProgressDialog = NULL; + switch(state) + { + case 0: + { + gProgressDialog = new CProgressDialog; + + gProgressDialog->Create(IDD_LOADLEVELPROGRESS,NULL); + gProgressDialog->m_TitleText = message; + gProgressDialog->ShowWindow(SW_SHOW); + + gProgressDialog->UpdateData(false); + gProgressDialog->m_ProgressBar.SetStep(100); + gProgressDialog->m_ProgressBar.SetPos(pos); + + }break; + + case 1: + { + if(gProgressDialog && gProgressDialog->m_hWnd) + { + gProgressDialog->m_ProgressBar.SetPos(pos); + defer(); + } + }break; + + case 2: + { + if(gProgressDialog && gProgressDialog->m_hWnd) + { + gProgressDialog->DestroyWindow(); + delete gProgressDialog; + gProgressDialog = NULL; + } + + }break; + } +} +#endif + +// Higher res means longer vis times :( +#define VIS_TABLE_RESOLUTION 6 +// Goes through all the rooms and determines their visibility in relation to one another +void MakeBOAVisTable (bool from_lighting) +{ + int cur_check = BOAGetMineChecksum(); + int vis_stack[MAX_ROOMS*10]; + int stack_count=0; + ubyte already_checked[MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS]; + ubyte precomputed[MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS][MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS]; + +// Removed this as we have to make a better checksum + + if(cur_check == BOA_vis_checksum) + { + // Already done, so bail + if (!from_lighting) + EditorMessageBox ("BOA vis table already computed."); + return; + } + + BOA_vis_checksum = BOA_mine_checksum = 0; +#ifdef NEWEDITOR + DoBOAVisProgressDialog(0.0f, 0, "Computing BOA"); + DoBOAVisProgressDialog(0.0f, 1); +#endif + MakeBOA(); +#ifdef NEWEDITOR + DoBOAVisProgressDialog(100.0f, 2); +#endif + + // Now compute all room to room visibility stuff + int i,t,j; + + mprintf ((0,"Computing visibility for %d rooms.\n",Highest_room_index)); + + for (i=0;i<=Highest_room_index+MAX_BOA_TERRAIN_REGIONS;i++) + { + for (t=0;t<= Highest_room_index+MAX_BOA_TERRAIN_REGIONS;t++) + { + BOA_Array[i][t] &= ~BOAF_VIS; + precomputed[i][t] = 255; + } + + BOA_Array[i][i] |= BOAF_VIS; + } + +#ifdef NEWEDITOR + DoBOAVisProgressDialog(0.0f, 0, "Computing BOA Vis. Table"); +#endif + + for (i = 0; i <= Highest_room_index; i++) + { + if (Rooms[i].used == 0) + continue; + + room *rp = &Rooms[i]; + +#ifdef NEWEDITOR + DoBOAVisProgressDialog((float)(i+1)/(float)(Highest_room_index + 1), 1); +#endif + mprintf_at((2,4,0,"Room=%d ",i)); + + if (rp->flags & RF_EXTERNAL) + continue; + + // Clear the already_checked array + memset (already_checked,0,MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS); + + already_checked[i] = 1; // don't check self + + // Make all the rooms connecting to this room automatically visible + // Make all the rooms connecting to this room automatically visible + for (t = 0; t < rp->num_portals; t++) + { + int croom=rp->portals[t].croom; + + if (croom >= 0) + { + BOA_Array[i][croom] |= BOAF_VIS; + already_checked[croom] = 1; + + // If this is an external room, mark the terrain as visible + if (Rooms[croom].flags & RF_EXTERNAL) + { + int cp; + int xxx; + + BOA_Array[i][Highest_room_index + 1] |= BOAF_VIS; + + for(xxx = 0; xxx < Rooms[croom].num_portals; xxx++) + { + int cell = GetTerrainCellFromPos(&Rooms[croom].portals[xxx].path_pnt); + ASSERT(cell != -1); //DAJ -1FIX + int region = TERRAIN_REGION(cell); + + BOA_Array[i][Highest_room_index + region + 1] |= BOAF_VIS; + } + + for(cp = 0; cp <= Highest_room_index; cp++) + { + if(Rooms[cp].used && (Rooms[cp].flags & RF_EXTERNAL) && !already_checked[cp]) + { + vis_stack[stack_count] = cp; + stack_count++; + ASSERT (stack_countnum_portals; t++) + { + int roomnum = rp->portals[t].croom; + for (j=0; j < Rooms[roomnum].num_portals; j++) + { + int croom = Rooms[roomnum].portals[j].croom; + + if (croom >= 0 && !already_checked[croom]) + { + vis_stack[stack_count] = croom; + stack_count++; + ASSERT (stack_count < MAX_ROOMS*10); + } + } + } + + // Check visibility until we are done with this room + while (stack_count > 0) + { + int check_room=vis_stack[--stack_count]; + if (already_checked[check_room]) + continue; + + already_checked[check_room] = 1; + + if (precomputed[check_room][i] != 255) + { + precomputed[i][check_room] = precomputed[check_room][i]; + if (precomputed[i][check_room] == 1) + { + BOA_Array[i][check_room] |= BOAF_VIS; + + // if this portal can be seen, add all its portals to the stack + // and then set this room to be visible + for (int tk=0; tk=0) + { + vis_stack[stack_count]=croom; + stack_count++; + ASSERT (stack_count < MAX_ROOMS*10); + } + } + } + + continue; + } + + int done=0; + + for (t = 0; t < rp->num_portals && !done; t++) + { + face *src_fp=&rp->faces[rp->portals[t].portal_face]; + float src_width,src_height; + vector src_upper_left,src_center; + matrix src_matrix; + vector src_verts[MAX_VERTS_PER_FACE],*src_vertp[MAX_VERTS_PER_FACE]; + + for (j=0;jnum_verts;j++) + { + src_verts[j]=rp->verts[src_fp->face_verts[j]]; + src_vertp[j]=&src_verts[j]; + } + + vector fvec=-src_fp->normal; + vm_VectorToMatrix(&src_matrix,&fvec,NULL,NULL); + + ComputeBOAVisFaceUpperLeft (rp,src_fp,&src_upper_left,&src_width,&src_height,&src_center); + + if (src_width>VIS_TABLE_RESOLUTION) + { + float num=src_width/VIS_TABLE_RESOLUTION; + src_width=VIS_TABLE_RESOLUTION; + + src_upper_left+=(src_matrix.rvec*(num/2)); + src_matrix.rvec*=num; + } + + if (src_height>VIS_TABLE_RESOLUTION) + { + float num=src_height/VIS_TABLE_RESOLUTION; + src_height=VIS_TABLE_RESOLUTION; + + src_upper_left-=(src_matrix.uvec*(num/2)); + src_matrix.uvec*=num; + } + + for (j=0;jnum_verts;tj++) + { + dest_verts[tj]=Rooms[check_room].verts[dest_fp->face_verts[tj]]; + dest_vertp[tj]=&dest_verts[tj]; + } + + fvec=-dest_fp->normal; + vm_VectorToMatrix(&dest_matrix,&fvec,NULL,NULL); + + ComputeBOAVisFaceUpperLeft (&Rooms[check_room],dest_fp,&dest_upper_left,&dest_width,&dest_height,&dest_center); + + if (dest_width>VIS_TABLE_RESOLUTION) + { + float num=dest_width/VIS_TABLE_RESOLUTION; + dest_width=VIS_TABLE_RESOLUTION; + + dest_upper_left+=(dest_matrix.rvec*(num/2)); + dest_matrix.rvec*=num; + } + + if (dest_height>VIS_TABLE_RESOLUTION) + { + float num=dest_height/VIS_TABLE_RESOLUTION; + dest_height=VIS_TABLE_RESOLUTION; + + dest_upper_left-=(dest_matrix.uvec*(num/2)); + dest_matrix.uvec*=num; + } + + vector src_vector,src_ybase; + src_ybase=src_upper_left; + + for (int sy=0;synormal,src_fp->num_verts,src_vertp))) + continue; + + vector dest_vector,dest_ybase; + dest_ybase=dest_upper_left; + + for (int dy=0;dyfaces[rp->portals[t].portal_face].normal; + dest_vector += 0.1f * Rooms[check_room].faces[Rooms[check_room].portals[j].portal_face].normal; + + subvec=dest_vector-src2; + if (subvec * src_fp->normal> 0.0f) + continue; + /*subvec=src2-dest_vector; + if (subvec * dest_fp->normal>0) + continue;*/ + + if ((check_point_to_face(&dest_vector, &dest_fp->normal,dest_fp->num_verts,dest_vertp))) + continue; + + if(!fvi_QuickRoomCheck(&src2, rp, false) && !fvi_QuickRoomCheck(&src2, rp, true)) + continue; + + if(!(Rooms[check_room].flags & RF_EXTERNAL) && (!fvi_QuickRoomCheck(&dest_vector, &Rooms[check_room], false) && !fvi_QuickRoomCheck(&dest_vector, &Rooms[check_room], true))) + continue; + + // Check to see if we can see to this portal point + fvi_query fq; + fvi_info hit_data; + int fate; + + fq.p0 = &src2; + fq.startroom = i; + fq.p1 = &dest_vector; + fq.rad = .01f; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_BACKFACE | FQ_IGNORE_RENDER_THROUGH_PORTALS; // Gaurentees in wierd geometry cases (as the ray will hit as it comes back in the mine) + // that the ray cannot start outside the mine like with a slightly non-planar + // portal or other non-nice situations like zero-width door portal/room-face combos + + fate = fvi_FindIntersection(&fq, &hit_data); + if (fate == HIT_NONE) + { + // if this portal can be seen, add all its portals to the stack + // and then set this room to be visible + for (int k=0;k=0) + { + vis_stack[stack_count] = croom; + stack_count++; + ASSERT (stack_countfaces[rp->portals[t].portal_face].normal; + dest_vector -= 0.1f * Rooms[check_room].faces[Rooms[check_room].portals[j].portal_face].normal; + } + } + } + } + } + } + + if (done == 0) + precomputed[i][check_room] = 0; + } + } + + for (i=0;i<=Highest_room_index;i++) + { + if (Rooms[i].used==0) + continue; + + for (t=0;t<=Highest_room_index+MAX_BOA_TERRAIN_REGIONS;t++) + { + if ((BOA_Array[i][t] & BOAF_VIS)) + { + BOA_Array[t][i] |= BOAF_VIS; + } + + } + } + +#ifdef NEWEDITOR + DoBOAVisProgressDialog(100.0f, 2); +#endif + + if (!from_lighting) + EditorMessageBox ("BOA vis table computed!"); + + BOA_vis_checksum = BOA_mine_checksum; + BOA_vis_valid = 1; +} +#endif + +void verify_connections() +{ + int i; + int j; + + for(i = 0; i <= Highest_room_index + BOA_num_terrain_regions; i++) + { + if(i <= Highest_room_index && (!Rooms[i].used)) + continue; + + if(i <= Highest_room_index && (Rooms[i].flags & RF_EXTERNAL)) + continue; + + for(j = 0; j <= Highest_room_index + BOA_num_terrain_regions; j++) + { + if(j <= Highest_room_index && (!Rooms[j].used)) + continue; + + if(j <= Highest_room_index && (Rooms[j].flags & RF_EXTERNAL)) + continue; + + if(i == Highest_room_index + 1 && j > Highest_room_index) + { + continue; + } + + if(j == Highest_room_index + 1 && i > Highest_room_index) + { + continue; + } + + if(i == j) + continue; + + int next_room = BOA_NEXT_ROOM(i, j); + + if(next_room != i && next_room != BOA_NO_PATH) + { + int portal; + portal = BOA_DetermineStartRoomPortal(i, NULL, next_room, NULL); + portal = BOA_DetermineStartRoomPortal(next_room, NULL, i, NULL); + } + } + } +} + +void find_small_portals() +{ + int i,j; + int counter = 0; + + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + for(j = 0; j < Rooms[i].num_portals; j++) + { + face *fp = &Rooms[i].faces[Rooms[i].portals[j].portal_face]; + + float xdiff; + float ydiff; + ComputeBOAVisFaceUpperLeft(&Rooms[i], fp, NULL, &xdiff, &ydiff, NULL); + + if(xdiff < 6.0f || ydiff < 6.0f) + { + counter++; + Rooms[i].portals[j].flags |= PF_TOO_SMALL_FOR_ROBOT; + } + else + { + Rooms[i].portals[j].flags &= (~PF_TOO_SMALL_FOR_ROBOT); + } + } + } + } + + mprintf((0, " Found %d small portals... :)\n", counter)); +} + +void compute_robot_path_info() +{ + int i,j; + + for(i = 0; i <= Highest_room_index + BOA_num_terrain_regions; i++) + { + if(i <= Highest_room_index && (!Rooms[i].used)) + continue; + + if(i <= Highest_room_index && (Rooms[i].flags & RF_EXTERNAL)) + continue; + + if(i > Highest_room_index + BOA_num_terrain_regions) + continue; + + for(j = 0; j <= Highest_room_index + BOA_num_terrain_regions; j++) + { + int cur_room = i; + + if(j <= Highest_room_index && (!Rooms[i].used)) + continue; + + if(j <= Highest_room_index && (Rooms[i].flags & RF_EXTERNAL)) + continue; + + if(i == j) + continue; + + if(i == Highest_room_index + 1 && j > Highest_room_index) + continue; + + if(j == Highest_room_index + 1 && i > Highest_room_index) + continue; + + if(BOA_NEXT_ROOM(cur_room, j) != BOA_NO_PATH && BOA_NEXT_ROOM(cur_room, j) != cur_room) + { + int last_room = cur_room; + + do + { + last_room = cur_room; + cur_room = BOA_NEXT_ROOM(cur_room, j); + + if(last_room != cur_room) + { + if(BOA_DetermineStartRoomPortal(last_room, NULL, cur_room, NULL, false, true) == -1) + { + BOA_Array[i][j] |= BOAF_TOO_SMALL_FOR_ROBOT; + break; + } + } + + } while (cur_room != j); + } + } + } +} + +void MakeBOA(void) +{ + ASSERT(BOA_ROOM_MASK > MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS); + int cur_check = BOAGetMineChecksum(); + + if (cur_check==BOA_vis_checksum) + BOA_vis_valid=1; + else + BOA_vis_valid=0; + + if(cur_check == BOA_mine_checksum) return; + + //OutrageMessageBox("Reminder: You need to make BOA Vis on this level.\nThis is either because it hasn't\nbeen done or Chris updated BOA."); + + BOA_mine_checksum = cur_check; + BOA_f_making_boa = true; + + mprintf((0, "Making BOA and friends\n")); + + mprintf((0, "Finding small portals\n")); + find_small_portals(); + mprintf((0, "Done Finding small portals\n")); + + mprintf((0, " Start computing path points.\n")); + BOA_ComputePathPoints(); + mprintf((0, " Done computing path points.\n")); + + clear_BOA(); + compute_costs(); + + mprintf((0, " Start computing mines.\n")); + compute_mine_info(); + mprintf((0, " Done computing %d mines.\n", BOA_num_mines)); + + mprintf((0, " Start computing terrain regions.\n")); + compute_terrain_region_info(); + mprintf((0, " Done computing %d terrain regions.\n", BOA_num_terrain_regions)); + + mprintf((0, " Making designers wait for no particular reason...\n")); + compute_next_segs(); + mprintf((0, " Done with the sodomy...\n")); + + mprintf((0, " Start computing blockage info.\n")); + compute_blockage_info(); + mprintf((0, " Done computing blockage info.\n")); + + mprintf((0, " Start computing sound prop.\n")); + compute_sound_dist_info(); + mprintf((0, " Done computing sound prop.\n")); + + mprintf((0, " Start computing invalid robot path info.\n")); + compute_robot_path_info(); + mprintf((0, " Done computing invalid robot path info.\n")); + + mprintf((0, " Verifying connections\n")); + verify_connections(); + mprintf((0, " Done with verification\n")); + +// { +// int cur_seg = 0; +// vector pos; +// matrix orient = IDENTITY_MATRIX; +// +// while (cur_seg != Highest_segment_index) { +// +// pos = average_pnt(&Segments[cur_seg]); +// +// ObjCreate( 7, 0, cur_seg, &pos, &orient, 0); +// cur_seg = BOA_Array[cur_seg][Highest_segment_index] & BOA_SEG_MASK; +// +// } +// } + + BOA_f_making_boa = false; + mprintf((0, "BOA is done\n")); + +} + +int Current_sort_room; + +static int face_sort_func1(const short *a, const short *b) +{ + if (Rooms[Current_sort_room].faces[*a].min_xyz.y > Rooms[Current_sort_room].faces[*b].min_xyz.y) + return -1; + else if (Rooms[Current_sort_room].faces[*a].min_xyz.y < Rooms[Current_sort_room].faces[*b].min_xyz.y) + return 1; + else + return 0; +} + +static int face_sort_func2(const short *a, const short *b) +{ + if (Rooms[Current_sort_room].faces[*a].max_xyz.y < Rooms[Current_sort_room].faces[*b].max_xyz.y) + return -1; + else if (Rooms[Current_sort_room].faces[*a].max_xyz.y > Rooms[Current_sort_room].faces[*b].max_xyz.y) + return 1; + else + return 0; +} + +#define MAX_REGIONS_PER_ROOM 200 + +void ComputeAABB(bool f_full) +{ + int i; + int j; + int k; + + int computed_room_check[MAX_ROOMS]; + + mprintf((0, "Computing AABB's")); + + // Determines the room and face min/max information + int cur_check = BOAGetMineChecksum(); + + if(cur_check == BOA_AABB_checksum && !f_full) + { + mprintf((0, " (partial)!\n")); + } + else + { + for(i = 0; i < MAX_ROOMS; i++) + { + if(Rooms[i].used) + { + computed_room_check[i] = BOAGetRoomChecksum(i); + } + else + { + BOA_AABB_ROOM_checksum[i] = computed_room_check[i] = 0; + } + } + + short *num_structs_per_room = (short *) mem_malloc((Highest_room_index + 1) * sizeof(short)); + short **r_struct_list; + + // Allocate the structure that tells what struct each face is in + r_struct_list = (short **) mem_malloc((Highest_room_index + 1) * sizeof(short *)); + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + r_struct_list[i] = (short *) mem_malloc(Rooms[i].num_faces * sizeof(short)); + } + } + + short count; + + BOA_AABB_checksum = cur_check; + mprintf((0, " (full)!\n")); + + for(i = 0; i <= Highest_room_index; i++) + { + Current_sort_room = i; + if(Rooms[i].used) + { + float average_y; + average_y = 0.0f; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + for(j = 0; j < Rooms[i].num_faces; j++) + { + vector face_min; + vector face_max; + + for(k = 0; k < Rooms[i].faces[j].num_verts; k++) + { + if(k == 0 || Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].x < face_min.x) + face_min.x = Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].x; + + if(k == 0 || Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].y < face_min.y) + face_min.y = Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].y; + + if(k == 0 || Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].z < face_min.z) + face_min.z = Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].z; + + if(k == 0 || Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].x > face_max.x) + face_max.x = Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].x; + + if(k == 0 || Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].y > face_max.y) + face_max.y = Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].y; + + if(k == 0 || Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].z > face_max.z) + face_max.z = Rooms[i].verts[Rooms[i].faces[j].face_verts[k]].z; + + } + + Rooms[i].faces[j].min_xyz = face_min; + Rooms[i].faces[j].max_xyz = face_max; + + average_y += ((face_min.y + face_max.y)/2); + } + + average_y /= Rooms[i].num_faces; + + // Do the rooms now + + room *rp=&Rooms[i]; + + rp->max_xyz.x=rp->max_xyz.y=rp->max_xyz.z=-9999999.0; + rp->min_xyz.x=rp->min_xyz.y=rp->min_xyz.z=9999999.0; + + for (int t=0;tnum_faces;t++) + { + face *fp=&rp->faces[t]; + + if (fp->max_xyz.x>rp->max_xyz.x) + rp->max_xyz.x=fp->max_xyz.x; + + if (fp->max_xyz.y>rp->max_xyz.y) + rp->max_xyz.y=fp->max_xyz.y; + + if (fp->max_xyz.z>rp->max_xyz.z) + rp->max_xyz.z=fp->max_xyz.z; + + if (fp->min_xyz.xmin_xyz.x) + rp->min_xyz.x=fp->min_xyz.x; + + if (fp->min_xyz.ymin_xyz.y) + rp->min_xyz.y=fp->min_xyz.y; + + if (fp->min_xyz.zmin_xyz.z) + rp->min_xyz.z=fp->min_xyz.z; + } + } + } + + // Determine number of independant structures and classify each face + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + room *rp=&Rooms[i]; + int num_struct = 0; + int count1, count2; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + short *nfaces; + bool *used; + + int n_new; + + nfaces = (short *) mem_malloc(sizeof(short) * rp->num_faces); + used = (bool *) mem_malloc(sizeof(bool) * rp->num_faces); + + for(count1 = 0; count1 < rp->num_faces; count1++) + { + used[count1] = false; + } + + next_struct: + + for(count1 = 0; count1 < rp->num_faces; count1++) + { + if(!used[count1]) + break; + } + + if(count1 >= rp->num_faces) + goto done; + + num_struct++; + + n_new = 1; + nfaces[0] = count1; + used[count1] = true; + r_struct_list[i][count1] = num_struct - 1; + // r_struct_list[i][count1] = 0; + + while(n_new > 0) + { + int seed = nfaces[--n_new]; + int count3; + + for(count1 = 0; count1 < rp->faces[seed].num_verts; count1++) + { + int c_vert = rp->faces[seed].face_verts[count1]; + + for(count2 = 0; count2 < rp->num_faces; count2++) + { + if(used[count2] == false) + { + for(count3 = 0; count3 < rp->faces[count2].num_verts; count3++) + { + if(c_vert == rp->faces[count2].face_verts[count3]) + { + nfaces[n_new++] = count2; + used[count2] = true; + r_struct_list[i][count2] = num_struct - 1; + // r_struct_list[i][count2] = 0; + break; + } + } + } + } + } + } + + goto next_struct; + + done: + + mem_free(nfaces); + mem_free(used); + + ASSERT(num_struct < MAX_REGIONS_PER_ROOM); // get chris + num_structs_per_room[i] = num_struct; + // mprintf((0, "%d structs in r %d\n", num_struct, i)); + } + } + + //Determine Area of each region for external shell remap + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + room *rp = &Rooms[i]; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + if(num_structs_per_room[i] == 1) + continue; + // else + // num_structs_per_room[i] = 1; + // + // continue; + + vector *s_max_xyz = (vector *) mem_malloc(num_structs_per_room[i] * sizeof(vector)); + vector *s_min_xyz = (vector *) mem_malloc(num_structs_per_room[i] * sizeof(vector)); + + for(count = 0; count < num_structs_per_room[i]; count++) + { + + s_max_xyz[count].x=s_max_xyz[count].y=s_max_xyz[count].z=-9999999.0; + s_min_xyz[count].x=s_min_xyz[count].y=s_min_xyz[count].z=9999999.0; + + for (int t=0;tnum_faces;t++) + { + if(r_struct_list[i][t] != count) + continue; + + face *fp=&rp->faces[t]; + + if (fp->max_xyz.x>s_max_xyz[count].x) + s_max_xyz[count].x=fp->max_xyz.x; + + if (fp->max_xyz.y>s_max_xyz[count].y) + s_max_xyz[count].y=fp->max_xyz.y; + + if (fp->max_xyz.z>s_max_xyz[count].z) + s_max_xyz[count].z=fp->max_xyz.z; + + if (fp->min_xyz.xmin_xyz.x; + + if (fp->min_xyz.ymin_xyz.y; + + if (fp->min_xyz.zmin_xyz.z; + } + } + + int best = 0; + vector diff = s_max_xyz[0] - s_min_xyz[0]; + float best_size = fabs(diff.x * diff.y * diff.z); + + for(count = 1; count < num_structs_per_room[i]; count++) + { + diff = s_max_xyz[count] - s_min_xyz[count]; + float size = fabs(diff.x * diff.y * diff.z); + + if(size > best_size) + { + best = count; + best_size = size; + } + } + + // mprintf((0, "Room %d Best Shell is %d\n", i, best)); + if(best != 0) + { + for(count = 0; count < rp->num_faces; count++) + { + if(r_struct_list[i][count] == 0) + { + r_struct_list[i][count] = best; + } + else if (r_struct_list[i][count] == best) + { + r_struct_list[i][count] = 0; + } + } + } + + mem_free(s_max_xyz); + mem_free(s_min_xyz); + } + } + + // Breaks up the main shell by cube and pushes faces into the appropriate region list + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + int x; + Current_sort_room = i; + room *rp=&Rooms[i]; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + vector min_xyz; + vector max_xyz; + int pamount[6]; + int nonpart = 0; + + if(rp->num_bbf_regions != 0) + { + for(x = 0; x < rp->num_bbf_regions; x++) + { + mem_free(rp->bbf_list[x]); + } + mem_free(rp->bbf_list); + mem_free(rp->num_bbf); + mem_free(rp->bbf_list_min_xyz); + mem_free(rp->bbf_list_max_xyz); + mem_free(rp->bbf_list_sector); + rp->num_bbf_regions = 0; + } + + // temporary malloc + rp->num_bbf_regions = 27 + num_structs_per_room[i] - 1; + rp->bbf_list = (short **) mem_malloc(MAX_REGIONS_PER_ROOM * sizeof(short *)); + for(x = 0; x < MAX_REGIONS_PER_ROOM; x++) + { + rp->bbf_list[x] = (short *) mem_malloc(rp->num_faces * sizeof(short)); + } + rp->num_bbf = (short *) mem_malloc(MAX_REGIONS_PER_ROOM * sizeof(short)); + rp->bbf_list_min_xyz = (vector *) mem_malloc(MAX_REGIONS_PER_ROOM * sizeof(vector)); + rp->bbf_list_max_xyz = (vector *) mem_malloc(MAX_REGIONS_PER_ROOM * sizeof(vector)); + rp->bbf_list_sector = (unsigned char *) mem_malloc(MAX_REGIONS_PER_ROOM * sizeof(unsigned char)); + + for(x = 0; x < 27; x++) + { + rp->bbf_list_sector[x] = bbf_lookup[x]; + } + + vector diff; + // vector min_diff; + // vector max_diff; + diff.x = diff.y = diff.z = 15.0f; + + min_xyz = max_xyz = (rp->min_xyz + rp->max_xyz)/2.0f; + + min_xyz -= diff; + max_xyz += diff; + + if(min_xyz.x <= rp->min_xyz.x) min_xyz.x = rp->min_xyz.x + 2.5f; + if(min_xyz.y <= rp->min_xyz.y) min_xyz.y = rp->min_xyz.y + 2.5f; + if(min_xyz.z <= rp->min_xyz.z) min_xyz.z = rp->min_xyz.z + 2.5f; + if(max_xyz.x >= rp->max_xyz.x) max_xyz.x = rp->max_xyz.x - 2.5f; + if(max_xyz.y >= rp->max_xyz.y) max_xyz.y = rp->max_xyz.y - 2.5f; + if(max_xyz.z >= rp->max_xyz.z) max_xyz.z = rp->max_xyz.z - 2.5f; + + if(min_xyz.x >= max_xyz.x) + { + min_xyz.x = rp->min_xyz.x; + max_xyz.x = rp->max_xyz.x; + } + if(min_xyz.y >= max_xyz.y) + { + min_xyz.y = rp->min_xyz.y; + max_xyz.y = rp->max_xyz.y; + } + if(min_xyz.z >= max_xyz.z) + { + min_xyz.z = rp->min_xyz.z; + max_xyz.z = rp->max_xyz.z; + } + + rp->bbf_min_xyz = min_xyz; + rp->bbf_max_xyz = max_xyz; + + // chrishack -- add post face add snap + + for(count = 0; count < 6; count++) + { + pamount[count] = 0; + } + + for(count = 0; count < rp->num_bbf_regions; count++) + { + rp->num_bbf[count] = 0; + } + + for(count = 0; count < rp->num_faces; count++) + { + face *fp=&rp->faces[count]; + bool f_part = false; + bool list[6]; + int x; + + for(x = 0; x < 6; x++) + list[x] = false; + + if(fp->max_xyz.x <= min_xyz.x) + { + list[0] = true; + f_part = true; + } + if(fp->max_xyz.y <= min_xyz.y) + { + list[1] = true; + f_part = true; + } + if(fp->max_xyz.z <= min_xyz.z) + { + list[2] = true; + f_part = true; + } + if((!list[0]) && fp->min_xyz.x >= max_xyz.x) + { + list[3] = true; + f_part = true; + } + if((!list[1]) && fp->min_xyz.y >= max_xyz.y) + { + list[4] = true; + f_part = true; + } + if((!list[2]) && fp->min_xyz.z >= max_xyz.z) + { + list[5] = true; + f_part = true; + } + + ubyte sector = 0; + + if(f_part == false) + { + if(r_struct_list[i][count] == 0) + rp->bbf_list[0][rp->num_bbf[0]++] = count; + else + { + if(rp->num_bbf[27 + r_struct_list[i][count] - 1] == 0) + rp->bbf_list_sector[27 + r_struct_list[i][count] - 1] = sector; + else + rp->bbf_list_sector[27 + r_struct_list[i][count] - 1] &= sector; + + rp->bbf_list[27 + r_struct_list[i][count] - 1][rp->num_bbf[27 + r_struct_list[i][count] - 1]++] = count; + } + + nonpart++; + } + else + { + // bool f_found = false; + // int best; + + for(x = 0; x < 6; x++) + { + if(list[x]) + { + sector |= 0x01 << x; + } + } + + int slot; + + switch(sector) + { + case 0x01: + slot = 1; + break; + case 0x02: + slot = 2; + break; + case 0x04: + slot = 3; + break; + case 0x08: + slot = 4; + break; + case 0x10: + slot = 5; + break; + case 0x20: + slot = 6; + break; + case (0x01 | 0x02): + slot = 7; + break; + case (0x01 | 0x04): + slot = 8; + break; + case (0x01 | 0x10): + slot = 9; + break; + case (0x01 | 0x20): + slot = 10; + break; + case (0x02 | 0x04): + slot = 11; + break; + case (0x02 | 0x08): + slot = 12; + break; + case (0x02 | 0x20): + slot = 13; + break; + case (0x04 | 0x08): + slot = 14; + break; + case (0x04 | 0x10): + slot = 15; + break; + case (0x08 | 0x10): + slot = 16; + break; + case (0x08 | 0x20): + slot = 17; + break; + case (0x10 | 0x20): + slot = 18; + break; + case 0x01 | 0x02 | 0x04: + slot = 19; + break; + case 0x01 | 0x02 | 0x20: + slot = 20; + break; + case 0x01 | 0x04 | 0x10: + slot = 21; + break; + case 0x01 | 0x10 | 0x20: + slot = 22; + break; + case 0x08 | 0x02 | 0x04: + slot = 23; + break; + case 0x08 | 0x02 | 0x20: + slot = 24; + break; + case 0x08 | 0x04 | 0x10: + slot = 25; + break; + case 0x08 | 0x10 | 0x20: + slot = 26; + break; + default: + Int3(); + } + + if(r_struct_list[i][count] == 0) + rp->bbf_list[slot][rp->num_bbf[slot]++] = count; + else + { + if(rp->num_bbf[27 + r_struct_list[i][count] - 1] == 0) + rp->bbf_list_sector[27 + r_struct_list[i][count] - 1] = sector; + else + rp->bbf_list_sector[27 + r_struct_list[i][count] - 1] &= sector; + + rp->bbf_list[27 + r_struct_list[i][count] - 1][rp->num_bbf[27 + r_struct_list[i][count] - 1]++] = count; + } + + ASSERT(slot > 0 && slot < rp->num_bbf_regions); + } + } + } + } + + mem_free(num_structs_per_room); + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + mem_free(r_struct_list[i]); + } + } + mem_free(r_struct_list); + num_structs_per_room = NULL; + r_struct_list = NULL; + + // Finds the min/max of each region + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + room *rp = &Rooms[i]; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + for(count = 0; count < rp->num_bbf_regions; count++) + { + rp->bbf_list_max_xyz[count].x=rp->bbf_list_max_xyz[count].y=rp->bbf_list_max_xyz[count].z=-9999999.0; + rp->bbf_list_min_xyz[count].x=rp->bbf_list_min_xyz[count].y=rp->bbf_list_min_xyz[count].z=9999999.0; + + for (int t=0;tnum_bbf[count];t++) + { + face *fp=&rp->faces[rp->bbf_list[count][t]]; + + if (fp->max_xyz.x>rp->bbf_list_max_xyz[count].x) + rp->bbf_list_max_xyz[count].x=fp->max_xyz.x; + + if (fp->max_xyz.y>rp->bbf_list_max_xyz[count].y) + rp->bbf_list_max_xyz[count].y=fp->max_xyz.y; + + if (fp->max_xyz.z>rp->bbf_list_max_xyz[count].z) + rp->bbf_list_max_xyz[count].z=fp->max_xyz.z; + + if (fp->min_xyz.xbbf_list_min_xyz[count].x) + rp->bbf_list_min_xyz[count].x=fp->min_xyz.x; + + if (fp->min_xyz.ybbf_list_min_xyz[count].y) + rp->bbf_list_min_xyz[count].y=fp->min_xyz.y; + + if (fp->min_xyz.zbbf_list_min_xyz[count].z) + rp->bbf_list_min_xyz[count].z=fp->min_xyz.z; + } + } + } + } + + // Remove unneccessary groups + for(count = 0; count <= Highest_room_index; count++) + { + if(Rooms[count].used) + { + // mprintf((0, "==================\n", count)); + // mprintf((0, "Room %d\n", count)); + + if(BOA_AABB_ROOM_checksum[count] != 0 && BOA_AABB_ROOM_checksum[count] == computed_room_check[count]) + continue; + + room *rp = &Rooms[count]; + for(i = 0; i < rp->num_bbf_regions; i++) + { + // mprintf((0, "Region %d has %d faces in it.\n", i, rp->num_bbf[i])); + + if(rp->num_bbf[i] == 0) + { + for(j = i + 1; j < rp->num_bbf_regions; j++) + { + short *temp = rp->bbf_list[j - 1]; + + rp->bbf_list[j - 1] = rp->bbf_list[j]; + rp->bbf_list[j] = temp; + + rp->num_bbf[j - 1] = rp->num_bbf[j]; + rp->bbf_list_min_xyz[j - 1] = rp->bbf_list_min_xyz[j]; + rp->bbf_list_max_xyz[j - 1] = rp->bbf_list_max_xyz[j]; + rp->bbf_list_sector[j - 1] = rp->bbf_list_sector[j]; + } + + rp->num_bbf_regions--; + i--; + } + } + } + } + + // Sub-divide structures + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + room *rp = &Rooms[i]; + int original_bbf_regions = rp->num_bbf_regions; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + for(j = 0; j < original_bbf_regions; j++) + { + if(rp->num_bbf[j] > 15) + { + //Test 3 split type + vector split = (rp->bbf_list_min_xyz[j] + rp->bbf_list_max_xyz[j])/2.0f; + int num_faces[3][3]; + + int sp; + int gel; + + for(sp = 0; sp < 3; sp++) + { + for(gel = 0; gel < 3; gel++) + { + num_faces[sp][gel] = 0; + } + } + + for(sp = 0; sp < 3; sp++) + { + float val = ((float *) &split)[sp]; + + for(count = 0; count < rp->num_bbf[j]; count++) + { + if(((float *)(&rp->faces[rp->bbf_list[j][count]].max_xyz))[sp] <= val) + gel = 2; + else if(((float *)(&rp->faces[rp->bbf_list[j][count]].min_xyz))[sp] >= val) + gel = 0; + else + gel = 1; + + num_faces[sp][gel]++; + } + } + + int diff[3]; + // mprintf((0, "Split plane results r %d s %d n %d\n", i, j, rp->num_bbf[j])); + for(sp = 0; sp < 3; sp++) + { + // mprintf((0, "%d %d %d\n", num_faces[sp][0], num_faces[sp][1], num_faces[sp][2])); + + if(abs(num_faces[sp][0] - num_faces[sp][1]) > abs(num_faces[sp][1] - num_faces[sp][2])) + diff[sp] = abs(num_faces[sp][0] - num_faces[sp][1]); + else + diff[sp] = abs(num_faces[sp][1] - num_faces[sp][2]); + } + + int best = 0; + float b_diff = diff[0]; + + for(sp = 1; sp < 3; sp++) + { + if(b_diff > diff[sp]) + { + best = sp; + b_diff = diff[sp]; + } + } + + // mprintf((0, "Split Plane is %d\n", best)); + + if(rp->num_bbf_regions <= MAX_REGIONS_PER_ROOM - 2) + { + rp->num_bbf[rp->num_bbf_regions] = 0; + rp->num_bbf[rp->num_bbf_regions + 1] = 0; + + sp = best; + for(count = 0; count < rp->num_bbf[j]; count++) + { + float val = ((float *) &split)[sp]; + + if(((float *)(&rp->faces[rp->bbf_list[j][count]].max_xyz))[sp] <= val) + { + rp->bbf_list[rp->num_bbf_regions][rp->num_bbf[rp->num_bbf_regions]++] = rp->bbf_list[j][count]; + + int t; + for(t = count; t < rp->num_bbf[j] - 1; t++) + { + rp->bbf_list[j][t] = rp->bbf_list[j][t + 1]; + } + + rp->num_bbf[j]--; + count--; + } + else if(((float *)(&rp->faces[rp->bbf_list[j][count]].min_xyz))[sp] >= val) + { + rp->bbf_list[rp->num_bbf_regions + 1][rp->num_bbf[rp->num_bbf_regions + 1]++] = rp->bbf_list[j][count]; + + int t; + for(t = count; t < rp->num_bbf[j] - 1; t++) + { + rp->bbf_list[j][t] = rp->bbf_list[j][t + 1]; + } + + rp->num_bbf[j]--; + count--; + } + } + + rp->num_bbf_regions += 2; + } + } + } + } + } + + // Remove unneccessary groups + for(count = 0; count <= Highest_room_index; count++) + { + if(Rooms[count].used) + { + // mprintf((0, "==================\n", count)); + // mprintf((0, "Room %d\n", count)); + if(BOA_AABB_ROOM_checksum[count] != 0 && BOA_AABB_ROOM_checksum[count] == computed_room_check[count]) + continue; + + room *rp = &Rooms[count]; + for(i = 0; i < rp->num_bbf_regions; i++) + { + // mprintf((0, "Region %d has %d faces in it.\n", i, rp->num_bbf[i])); + + if(rp->num_bbf[i] == 0) + { + for(j = i + 1; j < rp->num_bbf_regions; j++) + { + short *temp = rp->bbf_list[j - 1]; + + rp->bbf_list[j - 1] = rp->bbf_list[j]; + rp->bbf_list[j] = temp; + + rp->num_bbf[j - 1] = rp->num_bbf[j]; + rp->bbf_list_min_xyz[j - 1] = rp->bbf_list_min_xyz[j]; + rp->bbf_list_max_xyz[j - 1] = rp->bbf_list_max_xyz[j]; + rp->bbf_list_sector[j - 1] = rp->bbf_list_sector[j]; + } + + rp->num_bbf_regions--; + i--; + } + } + } + } + + // Finds the min/max of each region + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + room *rp = &Rooms[i]; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + for(count = 0; count < rp->num_bbf_regions; count++) + { + rp->bbf_list_max_xyz[count].x=rp->bbf_list_max_xyz[count].y=rp->bbf_list_max_xyz[count].z=-9999999.0; + rp->bbf_list_min_xyz[count].x=rp->bbf_list_min_xyz[count].y=rp->bbf_list_min_xyz[count].z=9999999.0; + + for (int t=0;tnum_bbf[count];t++) + { + face *fp=&rp->faces[rp->bbf_list[count][t]]; + + if (fp->max_xyz.x>rp->bbf_list_max_xyz[count].x) + rp->bbf_list_max_xyz[count].x=fp->max_xyz.x; + + if (fp->max_xyz.y>rp->bbf_list_max_xyz[count].y) + rp->bbf_list_max_xyz[count].y=fp->max_xyz.y; + + if (fp->max_xyz.z>rp->bbf_list_max_xyz[count].z) + rp->bbf_list_max_xyz[count].z=fp->max_xyz.z; + + if (fp->min_xyz.xbbf_list_min_xyz[count].x) + rp->bbf_list_min_xyz[count].x=fp->min_xyz.x; + + if (fp->min_xyz.ybbf_list_min_xyz[count].y) + rp->bbf_list_min_xyz[count].y=fp->min_xyz.y; + + if (fp->min_xyz.zbbf_list_min_xyz[count].z) + rp->bbf_list_min_xyz[count].z=fp->min_xyz.z; + } + } + } + } + + // Remaps all the regions to their best sectors + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + room *rp = &Rooms[i]; + vector min_xyz = rp->bbf_min_xyz; + vector max_xyz = rp->bbf_max_xyz; + + if(BOA_AABB_ROOM_checksum[i] != 0 && BOA_AABB_ROOM_checksum[i] == computed_room_check[i]) + continue; + + for(j = 0; j < rp->num_bbf_regions; j++) + { + for(count = 0; count < rp->num_bbf[j]; count++) + { + face *fp=&rp->faces[rp->bbf_list[j][count]]; + bool list[6]; + int x; + + for(x = 0; x < 6; x++) + list[x] = false; + + if(fp->max_xyz.x <= min_xyz.x) + { + list[0] = true; + } + if(fp->max_xyz.y <= min_xyz.y) + { + list[1] = true; + } + if(fp->max_xyz.z <= min_xyz.z) + { + list[2] = true; + } + if((!list[0]) && fp->min_xyz.x >= max_xyz.x) + { + list[3] = true; + } + if((!list[1]) && fp->min_xyz.y >= max_xyz.y) + { + list[4] = true; + } + if((!list[2]) && fp->min_xyz.z >= max_xyz.z) + { + list[5] = true; + } + + ubyte sector = 0; + + for(x = 0; x < 6; x++) + { + if(list[x]) + { + sector |= 0x01 << x; + } + } + + if(count == 0) + rp->bbf_list_sector[j] = sector; + else + rp->bbf_list_sector[j] &= sector; + } + } + } + } + + // Reallocate remaining data structures + // Remove extra slots + for(count = 0; count <= Highest_room_index; count++) + { + if(Rooms[count].used) + { + room *rp = &Rooms[count]; + if(BOA_AABB_ROOM_checksum[count] != 0 && BOA_AABB_ROOM_checksum[count] == computed_room_check[count]) + continue; + + for(j = 0; j < rp->num_bbf_regions; j++) + { + rp->bbf_list[j] = (short *) mem_realloc(rp->bbf_list[j], (sizeof(short) * rp->num_bbf[j])); + } + for(j = rp->num_bbf_regions; j < MAX_REGIONS_PER_ROOM; j++) + { + mem_free(rp->bbf_list[j]); + } + + rp->bbf_list = (short **) mem_realloc(rp->bbf_list, rp->num_bbf_regions * sizeof(short *)); + rp->num_bbf = (short *) mem_realloc(rp->num_bbf, rp->num_bbf_regions * sizeof(short)); + rp->bbf_list_min_xyz = (vector *) mem_realloc(rp->bbf_list_min_xyz, rp->num_bbf_regions * sizeof(vector)); + rp->bbf_list_max_xyz = (vector *) mem_realloc(rp->bbf_list_max_xyz, rp->num_bbf_regions * sizeof(vector)); + rp->bbf_list_sector = (unsigned char *) mem_realloc(rp->bbf_list_sector, rp->num_bbf_regions * sizeof(unsigned char)); + } + } + + // // Print out remainding groups + // for(count = 0; count <= Highest_room_index; count++) + // { + // if(Rooms[count].used) + // { + // mprintf((0, "==================\n", count)); + // mprintf((0, "Room %d\n", count)); + // + // room *rp = &Rooms[count]; + // for(i = 0; i < rp->num_bbf_regions; i++) + // { + // mprintf((0, "Region %d has %d faces in it.\n", i, rp->num_bbf[i])); + // } + // } + // } + + for(i = 0; i < MAX_ROOMS; i++) + { + BOA_AABB_ROOM_checksum[i] = computed_room_check[i]; + } + } + + // I had to add it :( Rooms need this done even the checksum is correct + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + room *rp=&Rooms[i]; + + rp->max_xyz.x=rp->max_xyz.y=rp->max_xyz.z=-9999999.0; + rp->min_xyz.x=rp->min_xyz.y=rp->min_xyz.z=9999999.0; + + for (int t=0;tnum_faces;t++) + { + face *fp=&rp->faces[t]; + + if (fp->max_xyz.x>rp->max_xyz.x) + rp->max_xyz.x=fp->max_xyz.x; + + if (fp->max_xyz.y>rp->max_xyz.y) + rp->max_xyz.y=fp->max_xyz.y; + + if (fp->max_xyz.z>rp->max_xyz.z) + rp->max_xyz.z=fp->max_xyz.z; + + if (fp->min_xyz.xmin_xyz.x) + rp->min_xyz.x=fp->min_xyz.x; + + if (fp->min_xyz.ymin_xyz.y) + rp->min_xyz.y=fp->min_xyz.y; + + if (fp->min_xyz.zmin_xyz.z) + rp->min_xyz.z=fp->min_xyz.z; + } + } + } + + for(i = 0; i <= Highest_object_index; i++) + { + if(Objects[i].type != OBJ_NONE) + { + if(!(Objects[i].flags & OF_BIG_OBJECT)) + if(Objects[i].size >= MIN_BIG_OBJ_RAD) + BigObjAdd(i); + + if(Objects[i].type == OBJ_ROOM) + { + Objects[i].min_xyz = Rooms[Objects[i].id].min_xyz; + Objects[i].max_xyz = Rooms[Objects[i].id].max_xyz; + } + else + { + ObjSetAABB(&Objects[i]); + ObjSetOrient(&Objects[i], &Objects[i].orient); + } + } + } + + mprintf((0, "Done Computing AABB's.\n")); +} diff --git a/Descent3/BOA.h b/Descent3/BOA.h new file mode 100644 index 000000000..44d1a0900 --- /dev/null +++ b/Descent3/BOA.h @@ -0,0 +1,259 @@ +/* + * $Logfile: /DescentIII/main/BOA.h $ + * $Revision: 34 $ + * $Date: 4/30/99 5:40p $ + * $Author: Jason $ + * + * BOA Header + * + * $Log: /DescentIII/main/BOA.h $ + * + * 34 4/30/99 5:40p Jason + * fixed some issues with release builds + * + * 33 4/29/99 5:43p Chris + * Added the check for bad center points + * + * 32 4/26/99 11:11a Chris + * Updated Bnode system + * + * 31 4/20/99 8:55p Chris + * Fixed problem with robots not being able to open locked doors that a + * player has the key for. + * + * 30 4/18/99 5:39a Chris + * Vastly improved the path node system + * + * 29 2/25/99 5:43p Chris + * Massive improvement to BOA and AI (again) + * + * 28 2/24/99 12:27p Chris + * Fixed problems with GB finding robots/room he couldn't get to. Fixed + * problems with forcefields(sound prop. and path finding). Fixed + * problems with small portals. + * + * 27 2/12/99 11:22a Chris + * Updated what BOA uses for BOA_cost_array. It is not just the distance + * from the center of the room to the center of the portal. It was this + + * the dist to the next room's center. The reason for this change was it + * makes it easier to compute the exact BOA_DIST for sounds. This is + * because the two distances are seperated (so from the start pos, we do a + * vm_Vect dist from the start point to the portal)... Hmmm.... + * + * 26 2/09/99 12:41p Chris + * + * 25 2/09/99 12:40p Chris + * More aipath stuff has been merged with the new BOA + * + * 24 2/09/99 12:17p Chris + * + * 23 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 22 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 21 12/11/98 1:55p Chris + * Reduced BOA's size and changed how it works with no path situations + * + * 20 12/09/98 10:41a Jason + * fixed BOA problems with lighting + * + * 19 9/16/98 12:07p Chris + * Improved BOA AABB computation with room checksums + * + * 18 8/04/98 2:32p Chris + * Improved attach code added more fixes to the AABB partial computation + * patch + * + * 17 8/03/98 5:47p Chris + * Improved the partial AABB computation + * + * 16 7/21/98 2:14p Chris + * Some FVI speedups - not done + * + * 15 7/20/98 5:39p Jason + * fixed MACRO bug + * + * 14 7/20/98 4:45p Jason + * added BOA vis table stuff + * + * 13 7/16/98 8:29p Chris + * Intermediate checkin + * + * 12 7/16/98 8:29p Chris + * Partial implementation of the new collide code + * + * 11 5/03/98 8:36p Chris + * Additional debug info + * + * 10 4/28/98 12:01p Matt + * Dropped MAX_PATH_PORTALS from 100 down to 40. + * + * 9 4/22/98 6:38p Matt + * Added SourceSafe header + * + */ + +#ifndef _BOA_H__ +#define _BOA_H__ + +#include "room.h" +#include "terrain.h" + +#define MAX_PATH_PORTALS 40 +#define MAX_BOA_TERRAIN_REGIONS 8 + +extern float BOA_cost_array[MAX_ROOMS + MAX_BOA_TERRAIN_REGIONS][MAX_PATH_PORTALS]; +extern unsigned short BOA_Array[MAX_ROOMS + MAX_BOA_TERRAIN_REGIONS][MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS]; +extern int BOA_mine_checksum; +extern int BOA_AABB_checksum; +extern int BOA_vis_checksum; +extern bool BOA_vis_valid; +extern int BOA_AABB_ROOM_checksum[MAX_ROOMS+MAX_BOA_TERRAIN_REGIONS]; + +//-- Priority Queue +class q_item +{ + public: + q_item(int room_index, int par, float n_cost) + { + roomnum = room_index; + parent = par; + cost = n_cost; + next = NULL; + } + + int roomnum; + int parent; + float cost; + + q_item *next; +}; + +class pq +{ + q_item *q_head; + + public: + pq() {q_head = NULL;} + + void push(q_item *new_node) + { + new_node->next = q_head; + q_head = new_node; + } + + q_item *pop() + { + q_item *cur_item = q_head; + q_item *prev_item = NULL; + q_item *best_item = q_head; + q_item *best_prev_item = NULL; + + if(q_head == NULL) return NULL; + + while(cur_item != NULL) + { + if(cur_item->cost < best_item->cost) + { + + best_prev_item = prev_item; + best_item = cur_item; + } + + prev_item = cur_item; + cur_item = cur_item->next; + } + + + // Actually remove this item from the list + if(best_item == q_head) + { + q_head = best_item->next; + } + else + { + best_prev_item->next = best_item->next; + } + + return best_item; + } +}; + +// Next Segment Info +#define BOA_ROOM_MASK 0x03FF + +#define BOA_TERRAIN_INDEX (Highest_room_index+1) +#define BOA_INDEX(x) ((ROOMNUM_OUTSIDE(x)?(TERRAIN_REGION(x)+Highest_room_index+1):x)) +#define BOA_NO_PATH (Highest_room_index+9) + +#define BOA_NEXT_ROOM(a, b) (BOA_Array[a][b] & BOA_ROOM_MASK) + +// Visability Info + +#define BOAF_VIS 0x0400 + +#define BOA_GET_VIS(a, b) (BOA_vis_valid?((BOA_Array[a][b] & BOAF_VIS)!=0):1) + +// Set if a sound could prop between these to areas +#define BOA_SOUND_PROP 0x0800 + +// Distance from segment to segment Info + +#define BOA_DIST_MASK 0x3000 + +#define BOA_VNEAR_DIST 0.0 +#define BOA_NEAR_DIST 100.0 +#define BOA_FAR_DIST 200.0 +#define BOA_VFAR_DIST 300.0 + +#define BOAF_VNEAR_DIST 0x0000 // 0.0 to 100.0 +#define BOAF_NEAR_DIST 0x1000 // 100.0 to 200.0 +#define BOAF_FAR_DIST 0x2000 // 200.0 to 300.0 +#define BOAF_VFAR_DIST 0x3000 // 300.0+ + +#define BOA_GET_DIST(a, b) ((BOA_Array[a][b]&BOA_DIST_MASK)!=0) + +// Possible Blockage Info + +#define BOAF_BLOCKAGE 0x4000 +#define BOA_HAS_POSSIBLE_BLOCKAGE(a, b) ((BOA_Array[a][b]&BOAF_BLOCKAGE)!=0) + +// Path have a sport in it that is too small for a robot to get through +#define BOAF_TOO_SMALL_FOR_ROBOT 0x8000 +#define BOA_TOO_SMALL_FOR_ROBOT(a, b) ((BOA_Array[a][b]&BOAF_TOO_SMALL_FOR_ROBOT)!=0) + +typedef struct connect_data +{ + int roomnum; + int portal; +} connect_data; + +extern int BOA_num_mines; +extern int BOA_num_terrain_regions; +extern int BOA_num_connect[MAX_BOA_TERRAIN_REGIONS]; +extern connect_data BOA_connect[MAX_BOA_TERRAIN_REGIONS][MAX_PATH_PORTALS]; + +void MakeBOA(void); + +// Goes through all the rooms and determines their visibility in relation to one another +void MakeBOAVisTable (bool from_lighting=0); + +// Computes the AABBs for the level +void ComputeAABB(bool f_full); + +bool BOA_ComputeMinDist(int start_room, int end_room, float max_check_dist, float *dist, int *num_blockages = NULL); +bool BOA_ComputePropDist(int start_room, vector *start_pos, int end_room, vector *end_pos, float *dist, int *num_blockages); +bool BOA_IsSoundAudible(int start_room, int end_room); +bool BOA_IsVisible(int start_room, int end_room); +bool BOA_HasPossibleBlockage(int start_room, int end_room); +int BOA_GetNextRoom(int start_room, int end_room); +int BOA_DetermineStartRoomPortal(int start_room, vector *start_pos, int end_room, vector *end_pos, bool f_for_sound = false, bool f_making_robot_path_invalid_list = false, int *blocked_portal = NULL); +bool BOA_PassablePortal(int room, int portal_index, bool f_for_sound = false, bool f_making_robot_path_invalid_list = false); +bool BOA_LockedDoor(object *obj, int roomnum); +void BOA_ComputePathPoints(char *message = NULL, int len = 0); +int BOAGetMineChecksum (); + +#endif \ No newline at end of file diff --git a/Descent3/Briefing.cpp b/Descent3/Briefing.cpp new file mode 100644 index 000000000..840142831 --- /dev/null +++ b/Descent3/Briefing.cpp @@ -0,0 +1,666 @@ +/* + * $Logfile: /DescentIII/main/Briefing.cpp $ + * $Revision: 57 $ + * $Date: 4/14/99 2:50a $ + * $Author: Jeff $ + * + * Briefing system + * + * $Log: /DescentIII/main/Briefing.cpp $ + * + * 57 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 56 2/25/99 11:56a Jeff + * added embedded hottag support + * + * 55 1/04/99 6:12p Jeff + * put in handling of mission state flags + * + * 54 1/04/99 12:32p Jeff + * added support for mission flag parsing + * + * 53 12/15/98 4:28p Jeff + * don't die if the briefing file doesn't exist + * + * 52 10/12/98 8:32p Jeff + * changed the way focus is handled + * + * 51 9/11/98 10:34a Jeff + * Briefing Editor completed + * + * 50 9/09/98 7:02p Jeff + * + * 49 8/28/98 12:57p Jeff + * added sounds and some key functionality + * + * 48 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 47 8/19/98 1:38p Jeff + * removed update2dsound + * + * 46 8/18/98 11:57a Jeff + * fixed sound problem + * + * 45 7/11/98 9:16p Jeff + * moved automatically drawing monitor overlay graphics from update() to a + * function of it's own, that way the TelCom API can be avoided if needed + * + * 44 7/09/98 8:33p Samir + * changes for new streaming interface. + * + * 43 6/26/98 2:49p Jeff + * fixed the bug where is you aborted parsing in the middle of a screen + * you'd crash + * + * 42 6/25/98 12:44p Jeff + * Added ESC to escape out of TelCom while it's initializing. Added + * DestroyScratch to Monitor + * + * 41 6/19/98 6:05p Jeff + * changes made due to voice system + * + * 40 6/18/98 5:18p Jeff + * use new streamaudio system + * + * 39 6/17/98 8:08p Jeff + * removed call to DoVoiceFrame + * + * 38 6/08/98 3:56p Jeff + * added voice support + * + * 37 5/19/98 3:40p Jeff + * poly models functional + * + * 36 5/18/98 4:20p Jeff + * setup TelCom so D3_FAST zips it through + * + * 35 5/18/98 3:37p Jeff + * added glow parameter to $button + * + * 34 5/15/98 5:16p Jeff + * various changes + * + * 33 5/11/98 6:20p Jeff + * added glitch and static keywords + * + * 32 5/08/98 12:39p Jeff + * various cleanups + * + * 31 5/06/98 6:34p Jeff + * capped framerate for mouse + * + * 30 5/04/98 5:29p Jeff + * Added sound frames and mouse cursor drawing + * + * 29 5/04/98 1:35p Jeff + * Changes made for mouse handling + * + * 28 5/04/98 11:01a Jeff + * added scroll param to text + * + * 27 5/03/98 7:59p Jeff + * little changes + * + * 26 5/01/98 2:16p Jeff + * added $title and $sound + * + * 25 4/30/98 7:16p Jeff + * added $font, exiting to mainmenu + * + * 24 4/29/98 4:36p Jeff + * increased text buffer size + * + * 23 4/28/98 6:58p Jeff + * Added new poly model effect driver + * + * 22 4/26/98 7:22p Jeff + * fixed fade ping pong bug, implemented new invert and stretch bitmaps + * + * 21 4/26/98 2:53a Jeff + * Made changes needed for new Effect driver system + * + * 20 4/24/98 6:35p Jeff + * Added space bar page forward + * + * 19 4/24/98 3:29p Jeff + * fixed powerup effect + * + * 18 4/23/98 7:09p Jeff + * added power up/down support + * + * 17 4/22/98 7:44p Jeff + * added flashing buttons + * + * 16 4/21/98 4:22p Jeff + * Call a general purpose function to draw entire telcom screen + * + * 15 4/20/98 6:45p Jeff + * added multiple screen handling and on screen buttons + * + * 14 4/17/98 6:55p Jeff + * Added button support + * + * 13 4/15/98 6:28p Jeff + * Got parsing working for most of the keywords + * + * 12 3/23/98 9:55a Jeff + * Made changes to remove old telcom + * + * 11 3/12/98 3:32p Jeff + * Initial changes started for New TelCom + * + * 10 2/03/98 12:21p Jeff + * changed default font to briefing font + * + * 9 1/26/98 4:19p Jeff + * Added "blur in" bitmap effect + * + * 8 1/20/98 9:16p Jeff + * Added back in fading bitmaps + * + * 7 1/20/98 6:04p Jeff + * Added support for fading fonts + * + * 6 1/20/98 10:43a Jeff + * + * 5 1/19/98 5:37p Jeff + * Got briefing up to par, and even better than before, added timer so + * scroll and type fonts are time based. + * + * 4 1/16/98 2:40p Jeff + * Adjusted so everything is displayed in correct spots, took out + * DefaultControl timer, added support for non fading bitmaps + * + * 3 1/15/98 11:14a Jeff + * Changed some code in TelComCallMission() to get Telcom working again + * + * 2 11/17/97 3:56p Matt + * Added the briefing system code + * + * 1 11/12/97 10:44a Matt + * + * $NoKeywords: $ + */ + +#include "Briefing.h" +#include "BriefingParse.h" +#include +#include +#include +#include "game.h" +#include "mem.h" +#include "hlsoundlib.h" +#include "voice.h" +#include "streamaudio.h" +#include "pserror.h" +#include "ddio.h" +#include "descent.h" +#include "TelCom.h" +#include "TelComEffects.h" +#include "Mission.h" + +typedef struct +{ + char *name; + int id; + int length; +}tBriefingTag; + +#define TAG_LEVELNUM 0 +#define TAG_PLEVELNUM 1 +#define TAG_NLEVELNUM 2 + +tBriefingTag HotTags[] = +{ + {"#LEVELNUM#",TAG_LEVELNUM,-1}, + {"#PLEVELNUM#",TAG_PLEVELNUM,-1}, + {"#NLEVELNUM#",TAG_NLEVELNUM,-1}, +}; +int NumHotTags = sizeof(HotTags)/sizeof(tBriefingTag); + +int osb_xoff = 0,osb_yoff = 0; +int current_screen = -1; +tTelComInfo *pb_tcs = NULL; +bool gottitle = false; +bool pbfirst_call; +char pbtitle[100]; +bool ok_to_parse_screen = false; +int skipped_screens; + +bool IsMissionMaskOK(uint set,uint unset) +{ + uint Gamemissionmask = Current_mission.game_state_flags; + + uint fGamemissionmask; + fGamemissionmask = Gamemissionmask^0xFFFFFFFF; + + if(!( ((set&Gamemissionmask)==set) && ((unset&fGamemissionmask)==unset) )){ + return false; + } + + return true; +} + +void ReplaceHotTag(char *string,int tag) +{ + if(tag<0 || tag>=NumHotTags) + { + *string = '\0'; + return; + } + + switch(tag) + { + case TAG_LEVELNUM: + sprintf(string,"%d",Current_mission.cur_level); + break; + case TAG_PLEVELNUM: + sprintf(string,"%d",Current_mission.cur_level-1); + break; + case TAG_NLEVELNUM: + sprintf(string,"%d",Current_mission.cur_level+1); + break; + } +} + +//returns true if there were hot tags and it had to allocate memory for dest (so it needs to be freed) +#define MEMORY_BLOCK 50 +bool ParseForHotTags(char *src,char **dest) +{ + bool ret = false; + char *curr_ptr = src; + char *dest_ptr; + *dest = NULL; + + bool done = false; + int length = strlen(src)+1; + int curr_size = length; + int curr_index = 0; + + dest_ptr = *dest = (char *)mem_malloc(length); + if(!dest_ptr) + return false; + char replacement[256]; + + while(!done) + { + if( *curr_ptr != '#' ) + { + if(*curr_ptr=='\0') + { + done = true; + } + else + { + *dest_ptr = *curr_ptr; + dest_ptr++; + curr_ptr++; + curr_index++; + length--; + + if(length<1) + { + //we need to allocate some more memory + char *new_mem = (char *)mem_malloc(curr_size+MEMORY_BLOCK); + if(!new_mem) + { + //out of memory + mem_free((*dest)); + *dest = NULL; + return false; + } + + memcpy(new_mem,(*dest),curr_index); + mem_free(*dest); + *dest = new_mem; + dest_ptr = &new_mem[curr_index]; + curr_size += MEMORY_BLOCK; + length += MEMORY_BLOCK; + } + } + }else + { + int is_hot_tag = -1; + + //see if it's a hot tag + for(int i=0;i 0 ) + { + *dest_ptr = *r_ptr; + dest_ptr++; + curr_index++; + length--; + + if(length<1) + { + //we need to allocate some more memory + char *new_mem = (char *)mem_malloc(curr_size+MEMORY_BLOCK); + if(!new_mem) + { + //out of memory + mem_free((*dest)); + *dest = NULL; + return false; + } + + memcpy(new_mem,(*dest),curr_index); + mem_free(*dest); + *dest = new_mem; + dest_ptr = &new_mem[curr_index]; + curr_size += MEMORY_BLOCK; + length += MEMORY_BLOCK; + } + + dest_left--; + r_ptr++; + } + } + } + } + *dest_ptr = '\0'; + + if(!ret) + { + mem_free((*dest)); + *dest = NULL; + } + + return ret; +} + +bool PlayBriefing(tTelComInfo *tcs) +{ + TelcomRenderSetScreen(0); + bool done = false; + + while(!done){ + Sound_system.BeginSoundFrame(Telcom_called_from_game); + + if(tcs->state==TCS_POWEROFF || tcs->current_status!=TS_MISSION){ + //we're done with the loop + done = true; + } + //Process all waiting events for the TelCom (we may only want to handle this once a frame!) + TelComHandleAllEvents(tcs); + + TelcomRenderScreen(); + Descent->defer(); + if(KEY_STATE(KEY_ESC)) + tcs->state = TCS_POWEROFF; + + Sound_system.EndSoundFrame(); + } + return true; +} + +void PBAddTextEffect(LPTCTEXTDESC desc,char *text,char *description,int id) +{ + if(IsMissionMaskOK(desc->mission_mask_set,desc->mission_mask_unset) && ok_to_parse_screen) + { + char *new_text; + bool new_stuff; + + new_stuff = ParseForHotTags(text,&new_text); + + if(!new_stuff) + new_text = text; + + CreateTextEffect(desc,new_text,MONITOR_MAIN,current_screen,id); + + if(new_stuff) + { + mem_free(new_text); + } + } +} + +void PBAddBmpEffect(LPTCBMPDESC desc,char *description) +{ + if(IsMissionMaskOK(desc->mission_mask_set,desc->mission_mask_unset) && ok_to_parse_screen) + CreateBitmapEffect(desc,MONITOR_MAIN,current_screen); +} + +void PBAddMovieEffect(LPTCMOVIEDESC desc,char *description) +{ + if(IsMissionMaskOK(desc->mission_mask_set,desc->mission_mask_unset) && ok_to_parse_screen) + CreateMovieEffect(desc,MONITOR_MAIN,current_screen); +} + +void PBAddBkgEffect(LPTCBKGDESC desc,char *description) +{ + if(IsMissionMaskOK(desc->mission_mask_set,desc->mission_mask_unset) && ok_to_parse_screen) + { + mprintf((0,"PB: Add Bkg\n")); + } +} + +void PBAddPolyEffect(LPTCPOLYDESC desc,char *description) +{ + if(IsMissionMaskOK(desc->mission_mask_set,desc->mission_mask_unset) && ok_to_parse_screen) + CreatePolyModelEffect(desc,MONITOR_MAIN,current_screen); +} + +void PBAddSoundEffect(LPTCSNDDESC desc,char *description) +{ + if(IsMissionMaskOK(desc->mission_mask_set,desc->mission_mask_unset) && ok_to_parse_screen) + CreateSoundEffect(desc,MONITOR_MAIN,current_screen); +} + +void PBAddButtonEffect(LPTCBUTTONDESC desc,char *description,int id) +{ + if(IsMissionMaskOK(desc->mission_mask_set,desc->mission_mask_unset) && ok_to_parse_screen){ + desc->x += osb_xoff; + desc->y += osb_yoff; + desc->tab_stop = false; //no briefing buttons can have focus + CreateButtonEffect(desc,MONITOR_MAIN,current_screen,id); + } +} + +void PBStartScreen(int screen_num,char *description,char *layout,uint mask_set,uint mask_unset) +{ + if(!IsMissionMaskOK(mask_set,mask_unset)){ + ok_to_parse_screen = false; + skipped_screens++; + return; + } + int real_screen_num = screen_num - skipped_screens; + ok_to_parse_screen = true; + + TelcomStartScreen(real_screen_num); + current_screen = real_screen_num; + + TCBKGDESC backg; + backg.color = BACKGROUND_COLOR; + backg.caps = TCBGD_COLOR; + backg.type = TC_BACK_STATIC; + CreateBackgroundEffect(&backg,MONITOR_TOP,current_screen); + if(gottitle){ + TCTEXTDESC textd; + textd.color = GR_RGB(200,200,200); + textd.type = TC_TEXT_STATIC; + textd.font = BBRIEF_FONT_INDEX; + textd.textbox.left = 4; + textd.textbox.right = 300; + textd.textbox.top = 2; + textd.textbox.bottom = 80; + textd.caps = TCTD_COLOR|TCTD_FONT|TCTD_TEXTBOX; + CreateTextEffect(&textd,pbtitle,MONITOR_TOP,current_screen); + } + + if(layout){ + TCBMPDESC bmpd; + bmpd.caps = TCBD_XY; + bmpd.type = TC_BMP_STATIC; + bmpd.flags = 0; + bmpd.x = bmpd.y = 0; + strcpy(bmpd.filename,layout); + CreateBitmapEffect(&bmpd,MONITOR_MAIN,current_screen); + } +} + +void PBEndScreen() +{ + if(ok_to_parse_screen){ + TelcomEndScreen(); + current_screen = -1; + } +} + +bool PBLoopCallback() +{ + if(!pbfirst_call){ + Sound_system.EndSoundFrame(); + }else + pbfirst_call = false; + + bool ret = false; + Sound_system.BeginSoundFrame(Telcom_called_from_game); + + if(pb_tcs->state==TCS_POWEROFF || pb_tcs->current_status!=TS_MISSION){ + //we're done with the loop + ret = true; + } + + //Process all waiting events for the TelCom (we may only want to handle this once a frame!) + TelComHandleAllEvents(pb_tcs); + TelcomRenderScreen(); + + Descent->defer(); + if(KEY_STATE(KEY_ESC)){ + pb_tcs->state = TCS_POWEROFF; + ret = true; + } + + return ret; +} + +void PBSetTitle(char *title) +{ + gottitle = true; + strcpy(pbtitle,title); +} + +void PBSetStatic(float amount) +{ + if(amount>0) + TelcomEnableStatic(amount); +} + +void PBSetGlitch(float amount) +{ + if(amount>0) + TelcomEnableGlitch(amount); +} + +void PBAddVoice(char *filename,int flags,char *description) +{ +} + +bool ParseBriefing(char *filename,tTelComInfo *tcs) +{ + if(!cfexist(filename)){ + tcs->state = TCS_POWEROFF; + return false; + } + + osb_xoff = tcs->Monitor_coords[MONITOR_MAIN].left; + osb_yoff = tcs->Monitor_coords[MONITOR_MAIN].top; + current_screen = -1; + pb_tcs = tcs; + gottitle = false; + pbtitle[0] = '\0'; + pbfirst_call = true; + + CBriefParse engine; + tBriefParseCallbacks pb_callbacks; + + pb_callbacks.AddTextEffect = PBAddTextEffect; + pb_callbacks.AddBmpEffect = PBAddBmpEffect; + pb_callbacks.AddMovieEffect = PBAddMovieEffect; + pb_callbacks.AddBkgEffect = PBAddBkgEffect; + pb_callbacks.AddPolyEffect = PBAddPolyEffect; + pb_callbacks.AddSoundEffect = PBAddSoundEffect; + pb_callbacks.AddButtonEffect = PBAddButtonEffect; + pb_callbacks.StartScreen = PBStartScreen; + pb_callbacks.EndScreen = PBEndScreen; + pb_callbacks.LoopCallback = PBLoopCallback; + pb_callbacks.SetTitle = PBSetTitle; + pb_callbacks.SetStatic = PBSetStatic; + pb_callbacks.SetGlitch = PBSetGlitch; + pb_callbacks.AddVoice = PBAddVoice; + + engine.SetCallbacks(&pb_callbacks); + + DestroyAllScreens(); + TelcomRenderSetScreen(DUMMY_SCREEN); + + skipped_screens = 0; + int ret = engine.ParseBriefing(filename); + Sound_system.EndSoundFrame(); + + if(ret==PBERR_NOERR){ + PlayBriefing(tcs); + } + + DestroyAllScreens(true); + TelcomCreateDummyScreen(); + TelcomRenderSetScreen(DUMMY_SCREEN); + + TelcomDisableStatic(); + TelcomDisableGlitch(); + + return false; +} diff --git a/Descent3/Briefing.h b/Descent3/Briefing.h new file mode 100644 index 000000000..afcde22fa --- /dev/null +++ b/Descent3/Briefing.h @@ -0,0 +1,54 @@ +/* + * $Logfile: /DescentIII/Main/Briefing.h $ + * $Revision: 11 $ + * $Date: 4/14/99 3:56a $ + * $Author: Jeff $ + * + * Header for briefing system + * + * $Log: /DescentIII/Main/Briefing.h $ + * + * 11 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 10 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 9 4/26/98 2:53a Jeff + * Made changes needed for new Effect driver system + * + * 8 4/22/98 7:44p Jeff + * added flashing buttons + * + * 7 4/15/98 6:28p Jeff + * Got parsing working for most of the keywords + * + * 6 1/20/98 6:04p Jeff + * Added support for fading fonts + * + * 5 1/20/98 12:08p Jeff + * + * 4 1/19/98 5:37p Jeff + * Got briefing up to par, and even better than before, added timer so + * scroll and type fonts are time based. + * + * 3 1/16/98 2:40p Jeff + * Adjusted so everything is displayed in correct spots, took out + * DefaultControl timer, added support for non fading bitmaps + * + * 2 11/17/97 3:56p Matt + * Added the briefing system code + * + * 1 11/12/97 10:44a Matt + * + * $NoKeywords: $ + */ + +#ifndef __BRIEFING_H_ +#define __BRIEFING_H_ + +#include "TelCom.h" + +bool ParseBriefing(char *filename,tTelComInfo *tcs); + +#endif \ No newline at end of file diff --git a/Descent3/BriefingParse.cpp b/Descent3/BriefingParse.cpp new file mode 100644 index 000000000..8884b5ba0 --- /dev/null +++ b/Descent3/BriefingParse.cpp @@ -0,0 +1,1511 @@ +/* +* $Logfile: /DescentIII/main/BriefingParse.cpp $ +* $Revision: 11 $ +* $Date: 4/14/99 2:50a $ +* $Author: Jeff $ +* +* Parse functions to parse a briefing file +* +* $Log: /DescentIII/main/BriefingParse.cpp $ + * + * 11 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 10 2/17/99 6:55p Jeff + * added jump button type. Added no early render flag for bitmaps. Fixed + * color bug for type text + * + * 9 2/17/99 11:12a Doug + * set movie type to TC_MOVIE_STATIC (Jeff) + * + * 8 1/13/99 8:01p Jeff + * initialize text_ptr (even though it really doesn't get used till it's + * init), to get rid of warning + * + * 7 1/04/99 12:32p Jeff + * added support for mission flag parsing + * + * 6 11/17/98 3:39p Jeff + * handle any length line when parsing + * + * 5 10/12/98 8:32p Jeff + * changed the way focus is handled + * + * 4 9/17/98 2:28p Jeff + * added focus filenames to button effect + * + * 3 9/11/98 6:06p Jeff + * Briefing Editor completed + * + * 2 9/09/98 7:02p Jeff + * Initial Creation +* +* $NoKeywords: $ +*/ +#include "BriefingParse.h" +#include "TelComEffects.h" +#include +#include +#include +#include "CFILE.H" +#include "pserror.h" +#include "game.h" +#include "mem.h" +#include "voice.h" +#include "streamaudio.h" +#include "ddio.h" + +// constructor +CBriefParse::CBriefParse() +{ + AddTextEffect = NULL; + AddBmpEffect = NULL; + AddMovieEffect = NULL; + AddBkgEffect = NULL; + AddPolyEffect = NULL; + AddSoundEffect = NULL; + AddButtonEffect = NULL; + StartScreen = NULL; + EndScreen = NULL; + LoopCallback = NULL; + SetTitle = NULL; + SetStatic = NULL; + SetGlitch = NULL; + AddVoice = NULL; + linenum = 0; + parse_error = false; +} + +// destructor +CBriefParse::~CBriefParse() +{ +} + +// SetCallbacks +// +// Registers (or changes) the callbacks of the class +void CBriefParse::SetCallbacks(tBriefParseCallbacks *cb) +{ + AddTextEffect = cb->AddTextEffect; + AddBmpEffect = cb->AddBmpEffect; + AddMovieEffect = cb->AddMovieEffect; + AddBkgEffect = cb->AddBkgEffect; + AddPolyEffect = cb->AddPolyEffect; + AddSoundEffect = cb->AddSoundEffect; + AddButtonEffect = cb->AddButtonEffect; + StartScreen = cb->StartScreen; + EndScreen = cb->EndScreen; + LoopCallback = cb->LoopCallback; + SetTitle = cb->SetTitle; + SetStatic = cb->SetStatic; + SetGlitch = cb->SetGlitch; + AddVoice = cb->AddVoice; +} + +//////////////////////////////////////////////////////////////////////////////// + + +//Keyword IDs. These must match the keyword strings below +#define K_NONE -1 +#define K_SCREEN 0 +#define K_BUTTON 1 +#define K_TEXT 2 +#define K_ENDTEXT 3 +#define K_BITMAP 4 +#define K_ENDSCREEN 5 +#define K_MOVIE 6 +#define K_POLY 7 +#define K_TITLE 8 +#define K_SOUND 9 +#define K_STATIC 10 +#define K_GLITCH 11 +#define K_VOICE 12 +#define NUM_KEYWORDS 13 + +//Button types +#define B_DISP 1 //display a screen +#define B_BACK 2 //go back a page +#define B_FORWARD 3 //go forward a page +#define B_QUIT 4 //exit TelCom + +//Keywords. These must match the keyword IDs above +char *keywords[] = { "screen", + "button", + "text", + "endtext", + "bitmap", + "endscreen", + "movie", + "poly", + "title", + "sound", + "static", + "glitch", + "voice" + }; + +#define tfxNONE 0 +#define tfxFLASH 1 +#define tfxSCROLL_L2R 2 +#define tfxSCROLL_R2L 3 +#define tfxSCROLL_T2B 4 +#define tfxSCROLL_B2T 5 +#define tfxFADE_IN_AND_OUT 6 +#define tfxFADE_IN 7 +#define tfxFADE_OUT 8 + +#define fntSMBRIEF 0 +#define fntLGBRIEF 1 +char *FontNames[] = { + "sm_brief", + "lg_brief", + NULL +}; + +char *TextEffectstr[] = { + "None", + "flash", + "scroll_l2r", + "scroll_r2l", + "scroll_t2b", + "scroll_b2t", + "fade_in_and_out", + "fade_in", + "fade_out", + NULL +}; +#define bfxNONE 0 +#define bfxFADE_IN 1 +#define bfxFADE_OUT 2 +#define bfxBLUR_IN 3 +#define bfxBLUR_OUT 4 +#define bfxSCAN_IN 5 +#define bfxSCAN_OUT 6 +#define bfxINVERT_IN 7 +#define bfxINVERT_OUT 8 +#define bfxSTRETCH_IN 9 +#define bfxSTRETCH_OUT 10 + +char *BitmapEffectstr[] = { + "None", + "Fade_in", + "Fade_out", + "Blur_in", + "Blur_out", + "Scan_in", + "Scan_out", + "Invert_in", + "Invert_out", + "Stretch_in", + "Stretch_out", + NULL +}; + + +//////////////////////////////////////////// +// These are the types of on screen buttons +#define osbDOWN_ARROW 0 +#define osbUP_ARROW 1 +#define osbNEXT_PAGE 2 +#define osbPREV_PAGE 3 +#define osbQUIT 4 +#define osbJUMP_PAGE 5 + +char *OnScreenButtonTypes[] = { + "Down", + "Up", + "Next", + "Prev", + "Quit", + "Jump", + NULL +}; + +///////////////////////////////////////////// +// These are the different ways an onscreen button will respond to mouse clicks +#define oscHOLD_DOWN 0 +#define oscCLICK_DOWN 1 +#define oscCLICK_UP 2 + +char *OnScreenButtonClickTypes[] = { + "HoldDown", + "ClickDown", + "ClickUp", + NULL +}; + +#define PARSE_INT(i) do {p = ParseInt(p,&(i)); if (!p) goto done_parsing;} while (0) +#define PARSE_FLOAT(i) do {p = ParseFloat(p,&(i)); if (!p) goto done_parsing;} while (0) +#define PARSE_COMMA() do {p = ParseComma(p); if (!p) goto done_parsing;} while (0) +#define PARSE_STRING(buf) do {p = ParseString(p,buf,sizeof(buf)); if (!p) goto done_parsing;} while (0) +#define PARSE_TOKEN(buf) do {p = ParseToken(p,buf,sizeof(buf)); if (!p) goto done_parsing;} while (0) + + +int ReadFullLine(char **data,CFILE *ifile) +{ + int counter,readin; + char buffer[512]; + bool done; + + counter = 0; + done = false; + readin = 0; + *data = NULL; + char c; + + while(!done){ + + //read in a byte + c = cfgetc(ifile); + + if ((c==EOF)||(!(ifile->flags&CF_TEXT)&&(c==0))||((ifile->flags&CF_TEXT)&&(c=='\n'))){ + //we've hit the end of the line + done = true; + }else{ + buffer[readin] = c; + readin++; + } + + //check if we have a full temp buffer + if(readin==sizeof(buffer)){ + //we have a full temp buffer + char *temp_buffer; + temp_buffer = (char *)mem_malloc(counter + sizeof(buffer));//allocate another buffer full + if(*data){ + //copy over existing data + memcpy(temp_buffer,*data,counter); + //free it + mem_free(*data); + } + //copy over new data + memcpy(&temp_buffer[counter],buffer,sizeof(buffer)); + + //reset our buffer for the temp buffer + counter += readin; + readin = 0; + + *data = temp_buffer; + } + } + + //append all the data thats in the tempbuffer into the final buffer + char *temp_buffer; + temp_buffer = (char *)mem_malloc(counter + readin + 1); //allocate what we need (plus for NULL term) + if(*data){ + //copy over existing data + memcpy(temp_buffer,*data,counter); + //free it + mem_free(*data); + } + //copy over new data + memcpy(&temp_buffer[counter],buffer,readin); + + counter += (readin+1); + *data = temp_buffer; + + temp_buffer[counter-1]='\0'; + + return counter; +} + + + +// ParseBriefing +// +// Parses the briefing file (calling the callbacks throughout), check return code +int CBriefParse::ParseBriefing(char *filename) +{ + CFILE *ifile; + bool retvalue = false; + bool voice_def_screen = false; + char text_buf[8192],*text_ptr = NULL; + bool abort=0; + bool gottitle = false; + char *linebuf = NULL; + + int beffect = bfxNONE; + int x=0,y=0; + char filen[40]; + bool emergency_exit = false; + + //the following block of 'globals' are for when parsing a text block, the information is stored + //here until a $endtext is hit + char title_buf[128]; + bool reading_text=false; + tTextBufferDesc text_buffer_desc; + text_buffer_desc.text_id = -1; //id # for current textblock + text_buffer_desc.teffect = tfxNONE; + + int current_screen = -1; + char title[100]; + + sprintf(title," "); + + mprintf((0,"Parsing <%s>\n",filename)); + + //Open the file + ifile = cfopen(filename,"rt"); + if (! ifile) + return PBERR_FILENOTEXIST; + + //Initialize vars + linenum = 0; + parse_error = 0; + title_buf[0] = 0; + + //Read & parse lines + int bytes_read; + char *p; + + linebuf = NULL; + + while((!cfeof(ifile) && !parse_error && !abort)) + { + if(linebuf){ + mem_free(linebuf); + linebuf = NULL; + } + + if(LoopCallback){ + abort = (*LoopCallback)(); + } + + //Read the line + bytes_read = ReadFullLine(&linebuf,ifile); + linenum++; + + if(!linebuf){ + //no more data? or empty line + continue; + } + + //Check for line too long + //@@if (bytes_read >= sizeof(linebuf)-1) { + //@@ ParseError("Line too long"); + //@@ break; + //@@} + + //Check for keyword + if (linebuf[0] == '$') { + int keyword_id; + + //Get the keyword id + p = ParseKeyword(linebuf,&keyword_id); + + switch (keyword_id) + { + case K_TITLE: + { + if(gottitle) + ParseError("$TITLE already defined\n"); + PARSE_STRING(title); + gottitle = true; + if(SetTitle) + (*SetTitle)(title); + } + break; + case K_STATIC: + { + float s; + PARSE_FLOAT(s); + if(SetStatic) + (*SetStatic)(s); + } + break; + case K_GLITCH: + { + float g; + PARSE_FLOAT(g); + if(SetGlitch) + (*SetGlitch)(g); + } + break; + case K_SOUND: + { + //add a sound effect + char buffer[20]; + char d[128]; + d[0]='\0'; + TCSNDDESC snddesc; + snddesc.caps = 0; + bool play = false; + snddesc.mission_mask_set = snddesc.mission_mask_unset = 0; + + while(!play){ + PARSE_TOKEN(buffer); + if(!stricmp(buffer,"play")) + play = true; + else + if(!stricmp(buffer,"once")){ + snddesc.caps |= TCSD_ONCE; + snddesc.once = true; + } + else + if(!stricmp(buffer,"starttime")){ + snddesc.caps |= TCSD_WAITTIME; + PARSE_FLOAT(snddesc.waittime); + } + else + if(!stricmp(buffer,"isset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + snddesc.mission_mask_set |= bit; + } + } + else + if(!stricmp(buffer,"isnset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + snddesc.mission_mask_unset |= bit; + } + } + else + if(!stricmp(buffer,"desc")){ + PARSE_STRING(d); + }else + ParseError("Illegal parameter in $SOUND"); + } + PARSE_STRING(snddesc.filename); + if(AddSoundEffect) + (*AddSoundEffect)(&snddesc,d); + } + break; + case K_VOICE: + { + //define the voice for this screen + bool play = false; + char d[128]; + d[0] = '\0'; + if(current_screen==-1) + ParseError("Voice keyword found outside a $screen block\n"); + if(voice_def_screen) + ParseError("A Voice is already defined for screen\n"); + + char buffer[_MAX_PATH]; + char token[30]; + int vflags = 0; + bool bitdepthset = false; + while(!play){ + PARSE_TOKEN(token); + if(!stricmp(token,"play")) + play = true; + else + if(!stricmp(token,"8bit")){ + vflags |= VF_8BIT; + bitdepthset = true; + }else + if(!stricmp(token,"compressed")){ + vflags |= VF_COMPRESSED; + }else + if(!stricmp(token,"stereo")){ + vflags |= VF_STEREO; + }else + if(!stricmp(buffer,"isset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + //snddesc.mission_mask_set |= bit; + } + } + else + if(!stricmp(buffer,"isnset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + //snddesc.mission_mask_unset |= bit; + } + } + else + if(!stricmp(token,"desc")){ + PARSE_STRING(d); + }else + ParseError("Illegal parameter in $VOICE"); + } + if(!bitdepthset) + vflags |= VF_16BIT|VF_INTERUPT|VF_FORCE; + + PARSE_STRING(buffer); + + /* + if(AddVoice) + (*AddVoice)(buffer,flags); + */ + + voice_def_screen = true; + } + break; + case K_POLY: + { + //add a poly effect + char name[32]; + float pos_x,pos_y,pos_z; + float rot_x,rot_y,rot_z; + float ori_x,ori_y,ori_z; + + PARSE_FLOAT(pos_x); + PARSE_COMMA(); + PARSE_FLOAT(pos_y); + PARSE_COMMA(); + PARSE_FLOAT(pos_z); + + PARSE_FLOAT(ori_x); + PARSE_COMMA(); + PARSE_FLOAT(ori_y); + PARSE_COMMA(); + PARSE_FLOAT(ori_z); + + PARSE_FLOAT(rot_x); + PARSE_COMMA(); + PARSE_FLOAT(rot_y); + PARSE_COMMA(); + PARSE_FLOAT(rot_z); + + PARSE_STRING(name); + TCPOLYDESC polydesc; + polydesc.caps = 0; + polydesc.pos_x = pos_x; + polydesc.pos_y = pos_y; + polydesc.pos_z = pos_z; + polydesc.ori_x = ori_x; + polydesc.ori_y = ori_y; + polydesc.ori_z = ori_z; + polydesc.rot_x = rot_x; + polydesc.rot_y = rot_y; + polydesc.rot_z = rot_z; + polydesc.mission_mask_set = 0; + polydesc.mission_mask_unset = 0; + strcpy(polydesc.polyname,name); + if(AddPolyEffect) + (*AddPolyEffect)(&polydesc,NULL); + } + break; + case K_BUTTON:{ + if(!ParseButtonEffect(p)) + goto done_parsing; + }break; + case K_SCREEN: + { + //define a screen + char description[128]; + char buffer[64]; + bool done = false; + bool layout_found = false; + char layout_scr[_MAX_PATH]; + uint mission_mask_set = 0,mission_mask_unset = 0; + description[0] = '\0'; + PARSE_INT(current_screen); + + if( (current_screen<0) || (current_screen>=MAX_TELCOM_SCREENS) ) + ParseError("Illegal Screen number\n"); + + while(!done){ + PARSE_TOKEN(buffer); + if(!stricmp(buffer,"start")){ + done = 1; + } + else + if(!stricmp(buffer,"desc")){ + PARSE_STRING(description); + } + else + if(!stricmp(buffer,"isset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + mission_mask_set |= bit; + } + } + else + if(!stricmp(buffer,"isnset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + mission_mask_unset |= bit; + } + } + else + if(!stricmp(buffer,"layout")){ + PARSE_STRING(layout_scr); + layout_found = true; + } + } + + if(StartScreen) + (*StartScreen)(current_screen,description,(layout_found)?layout_scr:NULL,mission_mask_set,mission_mask_unset); + + voice_def_screen = false; + } + break; + case K_ENDSCREEN: + { + if( (current_screen>=0) && (current_screen=0 && value<32){ + bit = bit << value; + bmpdesc.mission_mask_set |= bit; + } + } + else + if(!stricmp(buffer,"isnset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + bmpdesc.mission_mask_unset |= bit; + } + } + else + if(!stricmp(buffer,"desc")){ + PARSE_STRING(d); + } + else + ParseError("Illegal parameter in $BITMAP"); + } + PARSE_STRING(filen); + strcpy(bmpdesc.filename,filen); + if(AddBmpEffect) + (*AddBmpEffect)(&bmpdesc,d); + } + break; + case K_MOVIE: + { + int x = 0,y = 0; + char movie_name[128]; + char d[128]; + d[0] = '\0'; + float fps = 20.0; + TCMOVIEDESC moviedesc; + float starttime = 0; + moviedesc.caps = 0; + bool done = false; + char buffer[30]; + moviedesc.caps = 0; + moviedesc.mission_mask_set = 0; + moviedesc.mission_mask_unset = 0; + moviedesc.type = TC_MOVIE_STATIC; + while(!done){ + PARSE_TOKEN(buffer); + + if(!stricmp(buffer,"show")){ + done = 1; + } + else + if(!stricmp(buffer,"position")){ + PARSE_INT(x); + PARSE_COMMA(); + PARSE_INT(y); + moviedesc.x = x; + moviedesc.y = y; + moviedesc.caps|=TCMD_XY; + } + else + if(!stricmp(buffer,"fps")){ + PARSE_FLOAT(fps); + moviedesc.fps = fps; + moviedesc.caps|=TCMD_FPS; + } + else + if(!stricmp(buffer,"looping")){ + moviedesc.looping = true; + moviedesc.caps|=TCMD_LOOPING; + } + else + if(!stricmp(buffer,"starttime")){ + PARSE_FLOAT(starttime); + moviedesc.waittime = starttime; + moviedesc.caps|=TCMD_WAITTIME; + }else + if(!stricmp(buffer,"isset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + moviedesc.mission_mask_set |= bit; + } + } + else + if(!stricmp(buffer,"isnset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + moviedesc.mission_mask_unset |= bit; + } + } + else + if(!stricmp(buffer,"desc")){ + PARSE_STRING(d); + }else + ParseError("Illegal parameter in $MOVIE"); + } + + PARSE_STRING(movie_name); + strcpy(moviedesc.filename,movie_name); + if(AddMovieEffect) + (*AddMovieEffect)(&moviedesc,d); + } + break; + case K_NONE: + ParseError("Unknown keyword",p); + break; + default: + Int3(); //Programming error: unknown keyword ID + } + continue; + } + else if (reading_text && text_ptr) { //a line of text + if (text_ptr != text_buf) + *text_ptr++ = '\n'; + if (text_ptr - text_buf + strlen(linebuf) > sizeof(text_buf)) + ParseError("Text buffer overflow"); + else { + strcpy(text_ptr,linebuf); + text_ptr += strlen(text_ptr); + } + } + else if ((linebuf[0] == ';') || (linebuf[0] == 0)) //Check for comment or blank line + continue; + else { + //Not a comment, keyword, or text, so it's an error + ParseError("Unexpected text on line",p); + } + } + +done_parsing:; + + if (abort) + mprintf((0,"Parse aborted\n")); + else if (! parse_error) + if (reading_text) + ParseError("Missing '$endtext'"); + else + mprintf((0,"Parse sucessful\n")); + + //Close the file + cfclose(ifile); + + if(linebuf){ + //make sure all memory is freed + mem_free(linebuf); + linebuf = NULL; + } + + + return PBERR_NOERR; +} + + +// Parses the keyword description for an OnScreenButton +bool CBriefParse::ParseButtonEffect(char *p) +{ + int type = osbPREV_PAGE,ctype = oscCLICK_UP; + //creates an input button + int x,y,screen; + int osflags = 0; + char buffer[20]; + bool show = false,flasher = false; + int p_id=-1,s_id = -1; + int id = -1; + float flash_time = -1; + char flash_name[256]; + char flash_name_focus[256]; + char d[128]; + uint mission_mask_set = 0,mission_mask_unset = 0; + d[0] = '\0'; + + strcpy(flash_name," "); + strcpy(flash_name_focus," "); + screen = -1; + + PARSE_INT(x); + PARSE_COMMA(); + PARSE_INT(y); + PARSE_TOKEN(buffer); + + while(!show){ + if(!stricmp(buffer,"show")) + show = true; + else + if(!stricmp(buffer,"flash")){ + flasher = true; + osflags |= OBF_FLASH; + PARSE_FLOAT(flash_time); + PARSE_STRING(flash_name); + PARSE_STRING(flash_name_focus); + } + else + if(!stricmp(buffer,"id")){ + PARSE_INT(id); + } + else + if(!stricmp(buffer,"parent_id")){ + PARSE_INT(p_id); + } + else + if(!stricmp(buffer,"sibling_id")){ + PARSE_INT(s_id); + } + else + if(!stricmp(buffer,"desc")){ + PARSE_STRING(d); + } + else + if(!stricmp(buffer,"type")){ + char btype_buf[30]; + bool found = false; + int i = 0; + + PARSE_TOKEN(btype_buf); + while(!found){ + if(!OnScreenButtonTypes[i]) + found = true; + else if(!strnicmp(OnScreenButtonTypes[i],btype_buf,strlen(OnScreenButtonTypes[i]))){ + type = i; + found = true; + } + i++; + } + if(type==osbJUMP_PAGE){ + //parse what screen to jump to + PARSE_INT(screen); + } + } + else + if(!stricmp(buffer,"click")){ + char ctype_buf[30]; + bool found = false; + int i = 0; + + PARSE_TOKEN(ctype_buf); + while(!found){ + if(!OnScreenButtonClickTypes[i]) + found = true; + else if(!strnicmp(OnScreenButtonClickTypes[i],ctype_buf,strlen(OnScreenButtonClickTypes[i]))){ + ctype = i; + found = true; + } + i++; + } + } + else + if(!stricmp(buffer,"isset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + mission_mask_set |= bit; + } + } + else + if(!stricmp(buffer,"isnset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + mission_mask_unset |= bit; + } + } + else + if(!stricmp(buffer,"glow")){ + flasher = true; + osflags |= OBF_GLOW; + }else + ParseError("Illegal parameter in $BUTTON"); + + if(!show) + PARSE_TOKEN(buffer); + } + + char name[256]; + char name_focus[256]; + PARSE_STRING(name); + PARSE_STRING(name_focus); + + if(flasher && osflags==OBF_GLOW){ + //see if user specified a glow, but gave no flash, so we need to get a filename + PARSE_STRING(flash_name); + PARSE_STRING(flash_name_focus); + flash_time = -1; + } + + // Fill in the button description struct + TCBUTTONDESC desc; + desc.sibling_id = s_id; + desc.parent_id = p_id; + desc.mission_mask_set = mission_mask_set; + desc.mission_mask_unset = mission_mask_unset; + + //determine what type of button this is + switch(type){ + case osbDOWN_ARROW: desc.button_type = BUTT_DOWNARROW; break; + case osbUP_ARROW: desc.button_type = BUTT_UPARROW; break; + case osbNEXT_PAGE: desc.button_type = BUTT_NEXTPAGE; break; + case osbPREV_PAGE: desc.button_type = BUTT_PREVPAGE; break; + case osbQUIT: desc.button_type = BUTT_QUIT; break; + case osbJUMP_PAGE: desc.button_type = BUTT_JUMP; desc.jump_page = screen; break; + default: + Int3(); //Illegal button type + desc.button_type = BUTT_NEXTPAGE; + break; + }; + + //determine what type of click the button wants + switch(ctype){ + case oscHOLD_DOWN: desc.click_type = CLICKTYPE_DOWN; break; + case oscCLICK_DOWN: desc.click_type = CLICKTYPE_CLICKDOWN; break; + case oscCLICK_UP: desc.click_type = CLICKTYPE_CLICKUP; break; + default: + Int3(); //Illegal click type + desc.click_type = CLICKTYPE_CLICKUP; + break; + }; + + //fill in the rest of the information and create the button + desc.x = x; + desc.y = y; + strcpy(desc.filename,name); + strcpy(desc.filename_focus,name_focus); + strcpy(desc.flash_filename,flash_name); + strcpy(desc.flash_filename_focus,flash_name_focus); + desc.flash_time = flash_time; + desc.osflags = osflags; + desc.flasher = flasher; + + if(AddButtonEffect) + (*AddButtonEffect)(&desc,d,id); + return true; + +done_parsing: + return false; +} + +// Parses a text keyword description +bool CBriefParse::ParseTextEffect(char *p,tTextBufferDesc *tbd) +{ + tbd->textdesc.caps = 0; + tbd->textdesc.type = TC_TEXT_STATIC; + tbd->textdesc.flags = 0; + tbd->textdesc.mission_mask_set = 0; + tbd->textdesc.mission_mask_unset = 0; + char buffer[64]; + bool done = 0; + bool found; + float speed,starttime; + int i,lx,rx,ty,by; + tbd->description[0] = '\0'; + + while(!done){ + PARSE_TOKEN(buffer); + + if(!stricmp(buffer,"show")){ + done = 1; + } + else + if(!stricmp(buffer,"id")){ + PARSE_INT(tbd->text_id); + } + else + if(!stricmp(buffer,"desc")){ + PARSE_STRING(tbd->description); + } + else + if(!stricmp(buffer,"isset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + tbd->textdesc.mission_mask_set |= bit; + } + } + else + if(!stricmp(buffer,"isnset")){ + int value,bit = 0x01; + PARSE_INT(value); + if(value>=0 && value<32){ + bit = bit << value; + tbd->textdesc.mission_mask_unset |= bit; + } + } + else + if(!stricmp(buffer,"tabstop")){ + tbd->textdesc.caps|=TCTD_TABSTOP; + } + else + if(!stricmp(buffer,"effect")){ + found = false; + i=0; + PARSE_TOKEN(buffer); + while(!found){ + if(!TextEffectstr[i]) + found = true; + else + if(!strnicmp(TextEffectstr[i],buffer,strlen(TextEffectstr[i]))){ + tbd->teffect = i; + found = true; + } + i++; + } + switch(tbd->teffect){ + case tfxFLASH: + tbd->textdesc.type = TC_TEXT_FLASH; + break; + case tfxSCROLL_L2R: + tbd->textdesc.type = TC_TEXT_SCROLL; + tbd->textdesc.flags = TC_TEXTF_L2R; + break; + case tfxSCROLL_R2L: + tbd->textdesc.type = TC_TEXT_SCROLL; + tbd->textdesc.flags = TC_TEXTF_R2L; + break; + case tfxSCROLL_T2B: + tbd->textdesc.type = TC_TEXT_SCROLL; + tbd->textdesc.flags = TC_TEXTF_T2B; + break; + case tfxSCROLL_B2T: + tbd->textdesc.type = TC_TEXT_SCROLL; + tbd->textdesc.flags = TC_TEXTF_B2T; + break; + case tfxFADE_IN: + tbd->textdesc.type = TC_TEXT_FADE; + tbd->textdesc.flags = TC_TEXTF_IN; + break; + case tfxFADE_OUT: + tbd->textdesc.type = TC_TEXT_FADE; + tbd->textdesc.flags = TC_TEXTF_OUT; + break; + case tfxFADE_IN_AND_OUT: + tbd->textdesc.type = TC_TEXT_FADE; + tbd->textdesc.flags = TC_TEXTF_PINGPONG; + break; + default: + break; + } + } + else + if(!stricmp(buffer,"scroll")){ + tbd->textdesc.caps|=TCTD_SCROLL; + } + else + if(!stricmp(buffer,"speed")){ + PARSE_FLOAT(speed); + tbd->textdesc.speed = speed; + tbd->textdesc.caps|=TCTD_SPEED; + } + else + if(!stricmp(buffer,"color")){ + int r,g,b; + PARSE_INT(r); + PARSE_COMMA(); + PARSE_INT(g); + PARSE_COMMA(); + PARSE_INT(b); + tbd->textdesc.color = GR_RGB(r,g,b); + tbd->textdesc.caps |= TCTD_COLOR; + } + else + if(!stricmp(buffer,"font")){ + char buffer[20]; + bool found = false; + int i=0; + int feffect = 0; + int text_font; + + PARSE_TOKEN(buffer); + while(!found){ + if(!FontNames[i]) + found = true; + else + if(!strnicmp(FontNames[i],buffer,strlen(FontNames[i]))){ + feffect = i; + found = true; + } + i++; + } + switch(feffect){ + case fntSMBRIEF: + text_font = BRIEF_FONT_INDEX; + break; + case fntLGBRIEF: + text_font = BBRIEF_FONT_INDEX; + break; + } + + tbd->textdesc.font = text_font; + tbd->textdesc.caps |= TCTD_FONT; + } + else + if(!stricmp(buffer,"starttime")){ + PARSE_FLOAT(starttime); + tbd->textdesc.waittime = starttime; + tbd->textdesc.caps|=TCTD_WAITTIME; + } + else + if(!stricmp(buffer,"box")){ + PARSE_INT(lx); + PARSE_COMMA(); + PARSE_INT(rx); + PARSE_COMMA(); + PARSE_INT(ty); + PARSE_COMMA(); + PARSE_INT(by); + tbd->textdesc.textbox.left=lx; + tbd->textdesc.textbox.right=rx; + tbd->textdesc.textbox.top=ty; + tbd->textdesc.textbox.bottom=by; + tbd->textdesc.caps|=TCTD_TEXTBOX; + } + else + ParseError("Illegal parameter in $TEXT"); + } + + return true; + +done_parsing: + return false; +} + +//Generates an parsing error +void CBriefParse::ParseError(char *msg,char *p) +{ + mprintf((0,"ERROR, line %d: %s\n",linenum,msg)); + if (p) { + mprintf((0," %s\n",p)); + } + + parse_error = 1; +} + +//Parses a keyword +char *CBriefParse::ParseKeyword(char *p,int *keyword_id) +{ + int i; + + *keyword_id = K_NONE; + + ASSERT(*p == '$'); + + for (i=0,p++;i +#include +#include +#include "stringtable.h" + +#define UI_CHECKBOX_OFF_CHAR 28 +#define UI_CHECKBOX_ON_CHAR 29 + +ConfigItem::ConfigItem() +{ + m_fInitial = 0; + m_X = m_Y = m_W = m_H = 0; + curr_x = curr_y = 0; + m_fCurrentValue = 0; + m_iFlags = 0; + m_iType = 0; + m_iID = NULL; + m_iNumIDs = 0; + m_iItems = 0; + m_iValueType = 0; + m_hWnd = NULL; + m_tiList = NULL; + m_tiCount = 0; + m_hsList = NULL; + m_hsCount = 0; + m_sList = NULL; + m_sCount = 0; + m_rbList = NULL; + m_rbCount = 0; + m_bList = NULL; + m_bCount = 0; + m_lbList = NULL; + m_lbCount = 0; + m_fMin = 0; + m_fMax = 100; + m_iRange = 100; + m_fUnit = 1; + m_auxX = 0; + m_bAlive = false; + m_labeltext = NULL; + m_fCallback = NULL; + m_iCallback = NULL; + m_bCallback = NULL; +} + +ConfigItem::~ConfigItem() +{ + if(m_bAlive) + Destroy(); +} + +bool ConfigItem::Create(NewUIGameWindow *parentwnd,int type,int flags,int x,int y,int w,int h,char *label,int item_x) +{ + if(m_bAlive) + return false; + if(!parentwnd) + return false; + switch(type) + { + case CIT_HOTSPOTLIST: + case CIT_SLIDER: + case CIT_ONOFFBUTTON: + case CIT_LISTBOX: + case CIT_YESNOBUTTON: + case CIT_RADIOBUTTON: + case CIT_CHECKBOX: + m_iType = type; + break; + default: + mprintf((0,"Bad config item type in Create\n")); + return false; + } + if(!label) + return false; + + m_hWnd = parentwnd; + m_iFlags = flags; + + if( (m_iFlags&CIF_USEGROUP) && (type!=CIT_RADIOBUTTON) ){ + //only radiobutton items support the CIF_USEGROUP + mprintf((0,"CONFIGITEM: Only CIT_RADIOBUTTON supports CIF_USEGROUP flag\n")); + m_iFlags &= ~CIF_USEGROUP; + Int3(); + } + + if(type==CIT_SLIDER)//slider's need to have the label centered vertically + m_tLabel.Create(parentwnd,&UITextItem(label,UICOL_TEXT_NORMAL),x,y+7,UIF_FIT); + else{ + if(!(m_iFlags&CIF_USEGROUP)){ + //create a regular label + m_tLabel.Create(parentwnd,&UITextItem(label,UICOL_TEXT_NORMAL),x,y,UIF_FIT); + }else{ + //save the label text + m_labeltext = mem_strdup(label); + } + } + + m_auxX = x; + m_auxY = y; + m_X = item_x; + m_Y = y; + m_W = w; + m_H = h; + return true; +} + +bool ConfigItem::Create(NewUIGameWindow *parentwnd,int type,char *label,int x,int y,int w,int h,int initialvalue,int flags,int item_x) +{ + if(!Create(parentwnd,type,flags,x,y,w,h,label,item_x)) + return false; + + m_iValueType = CIV_INT; + m_iInitial = m_iCurrentValue = initialvalue; + + m_bAlive = true; + return true; +} + +bool ConfigItem::Create(NewUIGameWindow *parentwnd,int type,char *label,int x,int y,int w,int h,bool initialvalue,int flags,int item_x) +{ + if(!Create(parentwnd,type,flags,x,y,w,h,label,item_x)) + return false; + + m_iValueType = CIV_BOOL; + m_bInitial = m_bCurrentValue = initialvalue; + + m_bAlive = true; + return true; +} + +bool ConfigItem::Create(NewUIGameWindow *parentwnd,int type,char *label,int x,int y,int w,int h,float initialvalue,int flags,int item_x) +{ + if(!Create(parentwnd,type,flags,x,y,w,h,label,item_x)) + return false; + + m_iValueType = CIV_FLOAT; + m_fInitial = m_fCurrentValue = initialvalue; + + m_bAlive = true; + return true; +} + +void ConfigItem::Destroy(void) +{ + if(!m_bAlive) + return; + + int i; + + if (m_labeltext){ + mem_free(m_labeltext); + m_labeltext = NULL; + } + + if( (m_tiCount) && (m_tiList) ) + { + for(i=0;iGetSelectedIndex(); + *currvalue = m_iCurrentValue; + return true; +} + +bool ConfigItem::GetCurrentValue(bool *currvalue) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_BOOL) + return false; + *currvalue = m_bCurrentValue; + return true; +} + +bool ConfigItem::GetCurrentValue(float *currvalue) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_FLOAT) + return false; + *currvalue = m_fCurrentValue; + return true; +} + +bool ConfigItem::SetCurrentValue(int currvalue,bool call_back) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_INT) + return false; + + float f_cv = (float)currvalue; + int i_cv = currvalue; + + //do some error checking + switch(m_iType) + { + case CIT_HOTSPOTLIST: + case CIT_RADIOBUTTON: + if(i_cv<0) + i_cv = 0; + if(i_cv>=m_iNumIDs) + i_cv = m_iNumIDs-1; + break; + case CIT_SLIDER: + if(f_cv=m_fMax) + f_cv = m_fMax; + i_cv = (int)(((float)(f_cv-m_fMin))/m_fUnit); + break; + case CIT_LISTBOX: + //handles error checking in itself + break; + case CIT_CHECKBOX: + case CIT_ONOFFBUTTON: + case CIT_YESNOBUTTON: + Int3(); + break; + } + + switch(m_iType) + { + case CIT_HOTSPOTLIST: + case CIT_RADIOBUTTON: + m_rbList[i_cv]->Activate(); + break; + case CIT_SLIDER: + m_sList[0]->SetPos(i_cv); + UpdateSlider(0,call_back); + break; + case CIT_LISTBOX: + m_lbList[0]->SetSelectedIndex(i_cv); + break; + case CIT_CHECKBOX: + case CIT_ONOFFBUTTON: + case CIT_YESNOBUTTON: + Int3(); + break; + } + + //set the value than update + m_iCurrentValue = currvalue; + + //Update(int result); + + return true; +} + +bool ConfigItem::SetCurrentValue(bool currvalue,bool call_back) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_BOOL) + return false; + + //do some error checking + switch(m_iType) + { + case CIT_HOTSPOTLIST: + case CIT_RADIOBUTTON: + case CIT_SLIDER: + case CIT_LISTBOX: + Int3(); + break; + case CIT_CHECKBOX: + case CIT_ONOFFBUTTON: + case CIT_YESNOBUTTON: + break; + } + + switch(m_iType) + { + case CIT_HOTSPOTLIST: + case CIT_RADIOBUTTON: + case CIT_SLIDER: + case CIT_LISTBOX: + Int3(); + break; + case CIT_CHECKBOX: + if(currvalue) + m_hsList[0]->SetStates(m_tiList[2],m_tiList[3]);//Checked + else + m_hsList[0]->SetStates(m_tiList[0],m_tiList[1]);//Unchecked + break; + case CIT_ONOFFBUTTON: + case CIT_YESNOBUTTON: + if(currvalue) + m_bList[0]->SetTitle(m_tiList[1]);//Yes + else + m_bList[0]->SetTitle(m_tiList[0]);//No + break; + } + + //set the value than update + m_bCurrentValue = currvalue; + + //Update(int result); + + return true; +} + +bool ConfigItem::SetCurrentValue(float currvalue,bool call_back) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_FLOAT) + return false; + + //do some error checking + switch(m_iType) + { + case CIT_SLIDER: + if(currvalue=m_fMax) + currvalue = m_fMax; + currvalue = ((currvalue-m_fMin)/m_fUnit); + break; + case CIT_LISTBOX: + case CIT_HOTSPOTLIST: + case CIT_RADIOBUTTON: + case CIT_CHECKBOX: + case CIT_ONOFFBUTTON: + case CIT_YESNOBUTTON: + Int3(); + break; + } + + switch(m_iType) + { + case CIT_SLIDER: + m_sList[0]->SetPos((int)currvalue); + UpdateSlider(0,call_back); + break; + case CIT_HOTSPOTLIST: + case CIT_RADIOBUTTON: + case CIT_LISTBOX: + case CIT_CHECKBOX: + case CIT_ONOFFBUTTON: + case CIT_YESNOBUTTON: + Int3(); + break; + } + + //Update(int result); + + //set the value than update + m_fCurrentValue = currvalue; + + return true; +} + + +bool ConfigItem::GetInitialValue(int *initvalue) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_INT) + return false; + *initvalue = m_iInitial; + return true; +} + +bool ConfigItem::GetInitialValue(bool *initvalue) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_BOOL) + return false; + *initvalue = m_bInitial; + return true; +} + +bool ConfigItem::GetInitialValue(float *initvalue) +{ + if(!m_bAlive) + return false; + if(m_iValueType != CIV_FLOAT) + return false; + *initvalue = m_fInitial; + return true; +} + +void ConfigItem::Update(int result) +{ + if(!m_bAlive) + return; + if(!m_iID) + return; + + //first see if this is one of our items + int index = -1; + for(int i=0;i=0) && (indexActivate(); + if(m_iCallback) + (*m_iCallback)(m_iCurrentValue); +} + +void ConfigItem::UpdateSlider(int index,bool call_callback) +{ + ASSERT( (index>=0) && (indexGetPos()) * m_fUnit + m_fMin + 0.5f); + if(m_iCurrentValue>m_fMax) + m_iCurrentValue = m_fMax; + sprintf(temp,"%d",m_iCurrentValue); + if(m_iFlags&CIF_PERCENT) + strcat(temp,"%"); + if(m_iCallback && call_callback) + (*m_iCallback)(m_iCurrentValue); + break; + case CIV_FLOAT: + m_fCurrentValue = ((float)m_sList[index]->GetPos()) * m_fUnit + m_fMin; + if(m_fCurrentValue>m_fMax) + m_fCurrentValue = m_fMax; + if(m_iFlags&CIF_NODECIMAL) + sprintf(temp,"%.0f",m_fCurrentValue); + else + sprintf(temp,"%.2f",m_fCurrentValue); + if(m_iFlags&CIF_PERCENT) + strcat(temp,"%"); + if(m_fCallback && call_callback) + (*m_fCallback)(m_fCurrentValue); + break; + default: + Int3();//Get Jeff + } + m_tLabel2.SetTitle(&UITextItem(temp,UICOL_TEXT_NORMAL)); +} + +void ConfigItem::UpdateOnOffButton(int index) +{ + ASSERT( (index>=0) && (indexSetTitle(m_tiList[1]);//On + else + m_bList[index]->SetTitle(m_tiList[0]);//Off + if(m_bCallback) + (*m_bCallback)(m_bCurrentValue); +} + +void ConfigItem::UpdateListBox(int index) +{ + ASSERT( (index>=0) && (indexGetSelectedIndex(); + if(m_iCallback) + (*m_iCallback)(m_iCurrentValue); +} + +void ConfigItem::UpdateYesNoButton(int index) +{ + ASSERT( (index>=0) && (indexSetTitle(m_tiList[1]);//Yes + else + m_bList[index]->SetTitle(m_tiList[0]);//No + if(m_bCallback) + (*m_bCallback)(m_bCurrentValue); +} + +void ConfigItem::UpdateCheckBox(int index) +{ + ASSERT( (index>=0) && (indexSetStates(m_tiList[2],m_tiList[3]);//Checked + else + m_hsList[index]->SetStates(m_tiList[0],m_tiList[1]);//Unchecked + + if(m_bCallback) + (*m_bCallback)(m_bCurrentValue); +} + +void ConfigItem::UpdateRadioButton(int index) +{ + ASSERT( (index>=0) && (indexActivate(); + m_iCurrentValue = index; + + if(m_iCallback) + (*m_iCallback)(m_iCurrentValue); +} + +void ConfigItem::Add(int count, ... ) +{ + if(!m_bAlive) + return; + + ASSERT(m_tiCount==0); + + va_list marker; + va_start(marker,count); + int i; + UITextItem *uitemp; + + switch(m_iType) + { + case CIT_HOTSPOTLIST: + ASSERT(count>0); + ASSERT(m_rbCount==0); + //we need count text items + //we need count radiobuttons + //we need count IDs + m_tiCount = count; + m_tiList = (UITextItem**)mem_malloc(sizeof(UITextItem*)*m_tiCount); + m_rbCount = count; + m_rbList = (UIRadioButton**)mem_malloc(sizeof(UIRadioButton*)*m_rbCount); + m_iNumIDs = count; + m_iID = (int*)mem_malloc(sizeof(int)*m_iNumIDs); + //make sure mallocs are ok + ASSERT( (m_tiList) && (m_rbList) && (m_iID) ); + //initialize variables + //adjust the curr x/y for the list + curr_x = 0; + curr_y = 0; + for(i=0;iCreate(m_hWnd,m_rbList[i-1],m_iID[i],m_tiList[i],m_X+curr_x,m_Y+curr_y,m_tiList[i]->width(),m_tiList[i]->height(),UIF_FIT|UIRB_NOBUTTON); + else + m_rbList[i]->Create(m_hWnd,NULL,m_iID[i],m_tiList[i],m_X+curr_x,m_Y+curr_y,m_tiList[i]->width(),m_tiList[i]->height(),UIF_FIT|UIRB_NOBUTTON); + + (*m_tiList[i]).set_color(UICOL_HOTSPOT_HI); + m_rbList[i]->SetStateItem(UI_BTS_ACTIVATED,m_tiList[i]); + (*m_tiList[i]).set_color(UICOL_HOTSPOT_LO); + m_rbList[i]->SetStateItem(UI_BTS_INACTIVE,m_tiList[i]); + (*m_tiList[i]).set_color(UICOL_HOTSPOT_HI); + m_rbList[i]->SetStateItem(UI_BTS_HILITE,m_tiList[i]); + + //adjust curr x/y + curr_x += (m_rbList[i]->W() + HOTSPOTLIST_GAP); + } + //Initialize Config Item to initial value + ASSERT(m_iValueType==CIV_INT); + ASSERT( (m_iInitial>=0) && (m_iInitialActivate(); + break; + case CIT_SLIDER: + //only 1 slider per config item + ASSERT(m_sCount==0); + //we need 1 slider + //we need 1 ID + m_sCount = 1; + m_sList = (NewUISlider**)mem_malloc(sizeof(NewUISlider*)*1); + m_iNumIDs = 1; + m_iID = (int*)mem_malloc(sizeof(int)*1); + //make sure mallocs are ok + ASSERT( (m_sList) && (m_iID) ); + //get unique ID + m_iID[0] = GetUniqueID(); + //allocate slider + m_sList[0] = new NewUISlider; + //make sure allocation ok + ASSERT(m_sList[0]); + m_sList[0]->Create(m_hWnd,m_iID[0],m_X,m_Y,UIF_FIT); + char temp[10]; + switch(m_iValueType){ + case CIV_INT: + sprintf(temp,"%d",m_iInitial); + if(m_iFlags&CIF_PERCENT) + strcat(temp,"%"); + break; + case CIV_FLOAT: + if(m_iFlags&CIF_NODECIMAL) + sprintf(temp,"%.0f",m_fInitial); + else + sprintf(temp,"%.2f",m_fInitial); + if(m_iFlags&CIF_PERCENT) + strcat(temp,"%"); + break; + } + m_tLabel2.Create(m_hWnd,&UITextItem(temp,UICOL_TEXT_NORMAL),m_X+155,m_Y+7,UIF_FIT); + m_sList[0]->SetSelectChangeCallback(CISliderCallback,this); + break; + case CIT_ONOFFBUTTON: + //only 1 on/off button per config item + ASSERT(m_bCount==0); + //we need 2 text items ("On" and "Off") + //we need 1 button + //we need 1 ID + m_bCount = 1; + m_bList = (NewUIButton**)mem_malloc(sizeof(NewUIButton*)*1); + m_tiCount = 2; + m_tiList = (UITextItem**)mem_malloc(sizeof(UITextItem*)*2); + m_iNumIDs = 1; + m_iID = (int*)mem_malloc(sizeof(int)*1); + //make sure mallocs mallocs are ok + ASSERT( (m_tiList) && (m_bList) && (m_iID) ); + //adjust the curr x/y for the button + curr_x = 0; + curr_y = 0; + //get unique ID + m_iID[0] = GetUniqueID(); + //allocate text items + m_tiList[0] = new UITextItem(TXT_OFF,UICOL_BUTTON_LO); + m_tiList[1] = new UITextItem(TXT_ON,UICOL_BUTTON_HI); + //make sure allocation ok + ASSERT( (m_tiList[0]) && (m_tiList[1]) ); + //allocate buttonCreate + m_bList[0] = new NewUIButton; + //make sure allocation ok + ASSERT(m_bList[0]); + m_bList[0]->Create(m_hWnd,m_iID[0],m_tiList[0],m_X+curr_x,m_Y+curr_y,m_tiList[0]->width(),m_tiList[0]->height(),UIF_FIT); + //Initialize Config Item to initial value + ASSERT(m_iValueType==CIV_BOOL); + if(m_bInitial) + m_bList[0]->SetTitle(m_tiList[1]);//On + else + m_bList[0]->SetTitle(m_tiList[0]);//Off + break; + case CIT_LISTBOX: + ASSERT(count>0); + ASSERT(m_lbCount==0); + //we need count text items (one for each item in list box) + //we need 1 listbox + //we need 1 ID + m_lbCount = 1; + m_lbList = (NewUIListBox**)mem_malloc(sizeof(NewUIListBox*)*1); + m_tiCount = count; + m_tiList = (UITextItem**)mem_malloc(sizeof(UITextItem*)*m_tiCount); + m_iNumIDs = 1; + m_iID = (int*)mem_malloc(sizeof(int)*1); + //make sure mallocs ok + ASSERT( (m_lbList) && (m_tiList) && (m_iID) ); + //adjust the curr x/y for the listbox + curr_x = 0; + curr_y = m_tLabel.H() + LISTBOX_LABELGAP; + //get unique ID + m_iID[0] = GetUniqueID(); + //allocate list box + m_lbList[0] = new NewUIListBox; + //check allocation + ASSERT(m_lbList[0]); + m_lbList[0]->Create(m_hWnd,m_iID[0],m_X+curr_x,m_Y+curr_y,m_W,m_H,UIF_FIT); + //allocate text items and put them in + for(i=0;iAddItem(m_tiList[i]); + } + //Initialize Config Item to initial value + ASSERT(m_iValueType==CIV_INT); + ASSERT( (m_iInitial>=0) && (m_iInitialSetSelectChangeCallback(CIListBoxCallback,this); + m_lbList[0]->SetSelectedIndex(m_iInitial); + break; + case CIT_YESNOBUTTON: + //only 1 yes/no button per config item + ASSERT(m_bCount==0); + //we need 2 text items ("Yes" and "No") + //we need 1 button + //we need 1 ID + m_bCount = 1; + m_bList = (NewUIButton**)mem_malloc(sizeof(NewUIButton*)*1); + m_tiCount = 2; + m_tiList = (UITextItem**)mem_malloc(sizeof(UITextItem*)*2); + m_iNumIDs = 1; + m_iID = (int*)mem_malloc(sizeof(int)*1); + //make sure mallocs mallocs are ok + ASSERT( (m_tiList) && (m_bList) && (m_iID) ); + //adjust the curr x/y for the button + curr_x = 0; + curr_y = 0; + //get unique ID + m_iID[0] = GetUniqueID(); + //allocate text items + m_tiList[0] = new UITextItem(TXT_NO,UICOL_BUTTON_LO); + m_tiList[1] = new UITextItem(TXT_YES,UICOL_BUTTON_HI); + //make sure allocation ok + ASSERT( (m_tiList[0]) && (m_tiList[1]) ); + //allocate buttonCreate + m_bList[0] = new NewUIButton; + //make sure allocation ok + ASSERT(m_bList[0]); + m_bList[0]->Create(m_hWnd,m_iID[0],m_tiList[0],m_X+curr_x,m_Y+curr_y,m_tiList[0]->width(),m_tiList[0]->height(),UIF_FIT); + //Initialize Config Item to initial value + ASSERT(m_iValueType==CIV_BOOL); + if(m_bInitial) + m_bList[0]->SetTitle(m_tiList[1]);//Yes + else + m_bList[0]->SetTitle(m_tiList[0]);//No + break; + case CIT_CHECKBOX: + //only 1 checkbox per config item + ASSERT(m_bCount==0); + //we need 4 text items (a checked and unchecked box hightlited and not) + //we need 1 hotspot + //we need 1 ID + m_hsCount = 1; + m_hsList = (UIHotspot**)mem_malloc(sizeof(UIHotspot*)*1); + m_tiCount = 4; + m_tiList = (UITextItem**)mem_malloc(sizeof(UITextItem*)*4); + m_iNumIDs = 1; + m_iID = (int *)mem_malloc(sizeof(int)*1); + //make sure mallocs are ok + ASSERT( (m_hsList) && (m_tiList) && (m_iID) ); + //adjust the curr x/y for the checkbox + curr_x = 0; + curr_y = 0; + //get unique ID + m_iID[0] = GetUniqueID(); + //allocate text items + char buffer[256]; + sprintf(buffer,"%c %s",UI_CHECKBOX_OFF_CHAR,((UITextItem *)m_tLabel.GetItem())->GetBuffer()); + m_tiList[0] = new UITextItem(buffer,UICOL_HOTSPOT_LO); + m_tiList[1] = new UITextItem(buffer,UICOL_HOTSPOT_HI); + sprintf(buffer,"%c %s",UI_CHECKBOX_ON_CHAR,((UITextItem *)m_tLabel.GetItem())->GetBuffer()); + m_tiList[2] = new UITextItem(buffer,UICOL_HOTSPOT_LO); + m_tiList[3] = new UITextItem(buffer,UICOL_HOTSPOT_HI); + //make sure allocation ok + ASSERT( (m_tiList[0]) && (m_tiList[1]) ); + //allocate hot spots + m_hsList[0] = new UIHotspot; + //make sure allocation ok + ASSERT(m_hsList[0]); + m_hsList[0]->Create(m_hWnd,m_iID[0],-1,m_tiList[0],m_tiList[1],m_X+curr_x,m_Y+curr_y,m_tiList[0]->width(),m_tiList[0]->height(),UIF_FIT); + //Initialize Config Item to initial value + ASSERT(m_iValueType==CIV_BOOL); + if(m_bInitial) + m_hsList[0]->SetStates(m_tiList[2],m_tiList[3]); + else + m_hsList[0]->SetStates(m_tiList[0],m_tiList[1]); + //remove the title so it's not drawn + m_tLabel.Destroy(); + break; + case CIT_RADIOBUTTON: + ASSERT(count>0); + ASSERT(m_rbCount==0); + //we need count text items + //we need count radio buttons + //we need count IDs + m_tiCount = count; + m_tiList = (UITextItem**)mem_malloc(sizeof(UITextItem*)*m_tiCount); + m_rbCount = count; + m_rbList = (UIRadioButton**)mem_malloc(sizeof(UIRadioButton*)*m_rbCount); + m_iNumIDs = count; + m_iID = (int*)mem_malloc(sizeof(int)*m_iNumIDs); + //make sure mallocs are ok + ASSERT( (m_tiList) && (m_rbList) && (m_iID) ); + //adjust the curr x/y for the list of radio buttons + curr_x = 0; + + int offset; + bool use_group; + use_group = (bool)((m_iFlags&CIF_USEGROUP)!=0); + + if(m_X){ + curr_y = 0; + offset = 8; + }else{ + m_X = m_auxX; + curr_y = RADIOBUTTON_LABELGAP + 10; + + if(use_group){ + offset = 5; + }else{ + offset = 0; + } + } + + int start_y,w,max_width; + + start_y = curr_y; + max_width = 0; + + for(i=0;iset_color(UICOL_HOTSPOT_LO); + + //m_tiList[i]->set_alpha(UIALPHA_HOTSPOT_LO); + //allocate radio button + m_rbList[i] = new UIRadioButton; + ASSERT(m_rbList[i]); + if(i) + m_rbList[i]->Create(m_hWnd,m_rbList[i-1],m_iID[i],m_tiList[i],m_X+curr_x+offset,m_Y+curr_y,m_W,m_H,UIF_FIT); + else + m_rbList[i]->Create(m_hWnd,NULL,m_iID[i],m_tiList[i],m_X+curr_x+offset,m_Y+curr_y,m_W,m_H,UIF_FIT); + + //store the max width + w = m_rbList[i]->W(); + if(w>max_width) + max_width = w; + + //adjust curr x/y + curr_y += (m_tiList[i]->height() + RADIOBUTTON_GAP); + } + + if( m_iFlags & CIF_USEGROUP && m_labeltext){ + //create the group box now + + //we may need to update the width depending on width of title + grtext_SetFont(SMALL_FONT); + int w = grtext_GetTextLineWidth(m_labeltext); + if( w > max_width ) + max_width = w; + + m_H = curr_y - start_y + 15; //generate real height + m_W = max_width + 20; //generate real width + + //Create the group box + m_gGroup.Create(m_hWnd,m_labeltext,m_auxX,m_Y,m_W,m_H,UICOL_TEXT_NORMAL,UICOL_HOTSPOT_LO); + + } + + + //Initialize Config Item to initial value + ASSERT(m_iValueType==CIV_INT); + ASSERT( (m_iInitial>=0) && (m_iInitialActivate(); + break; + default: + mprintf((0,"Bad ConfigItem Type in Add\n")); + } + va_end(marker); +} +void ConfigItem::SetRange(int min,int max,int range) +{ + if(!m_bAlive) + return; + ASSERT(m_iType==CIT_SLIDER); + ASSERT(m_iValueType==CIV_INT); + ASSERT(max>min); + ASSERT( (m_iCurrentValue>=min) && (m_iCurrentValue<=max) ); + + m_fMin = min; + m_fMax = max; + m_sList[0]->SetRange(range+1); + m_fUnit = ((float)max-min)/((float)range); + + //figure out how far in is the position + m_sList[0]->SetPos((int)(((float)(m_iInitial-min))/m_fUnit)); +} + +void ConfigItem::SetRange(float min,float max,int range) +{ + if(!m_bAlive) + return; + ASSERT(m_iType==CIT_SLIDER); + ASSERT(m_iValueType==CIV_FLOAT); + ASSERT(max>min); + ASSERT( (m_fCurrentValue>=min) && (m_fCurrentValue<=max) ); + + m_fMin = min; + m_fMax = max; + m_sList[0]->SetRange(range+1); + m_fUnit = (max-min)/((float)range); + + //figure out the real positions + m_sList[0]->SetPos((int)((m_fInitial-min)/m_fUnit)); +} + +void ConfigItem::SetCallback(void (*callback)(float)) +{ + if(!m_bAlive) + return; + if(m_iValueType != CIV_FLOAT) + return; + m_fCallback = callback; +} + +void ConfigItem::SetCallback(void (*callback)(int)) +{ + if(!m_bAlive) + return; + if(m_iValueType != CIV_INT) + return; + m_iCallback = callback; +} + +void ConfigItem::SetCallback(void (*callback)(bool)) +{ + if(!m_bAlive) + return; + if(m_iValueType != CIV_BOOL) + return; + m_bCallback = callback; +} + + +int GetUniqueID(void) +{ + static int count = 0xB0; + count++; + return count; +} + +void CIListBoxCallback(int ID,void *ptr) +{ +} + +void CISliderCallback(int ID,void *ptr) +{ + ConfigItem *ci = (ConfigItem *)ptr; + if(!ci) + return; + ci->Update(ID); +} diff --git a/Descent3/ConfigItem.h b/Descent3/ConfigItem.h new file mode 100644 index 000000000..77ed7afc7 --- /dev/null +++ b/Descent3/ConfigItem.h @@ -0,0 +1,180 @@ +/* +* $Logfile: /DescentIII/main/ConfigItem.h $ +* $Revision: 8 $ +* $Date: 10/19/98 6:30p $ +* $Author: Jeff $ +* +* Contains the class info for ConfigItem [config dialog UI objects] +* +* $Log: /DescentIII/main/ConfigItem.h $ + * + * 8 10/19/98 6:30p Jeff + * changes made for detail variables. Put in preset values. Preset + * options. Removed terrain_cast from detail. Put new callbacks in + * UIListBox and UISlider + * + * 7 10/16/98 1:42p Jeff + * added CIF_USEGROUP and general dialog look + * + * 6 9/02/98 2:54p Jeff + * added defines for text colors to be used throughout the game...fixed up + * buddy menu too + * + * 5 6/05/98 5:59p Jeff + * Added a CIT_CHECKBOX style + * + * 4 5/23/98 6:33p Jeff + * Added some flags for the slider, and fixed it up a bit + * + * 3 5/22/98 5:02p Jeff + * Increased functionality + * + * 2 5/21/98 10:35p Jeff + * Initial creation +* +* $NoKeywords: $ +*/ + +#ifndef __CONFIGITEM_H_ +#define __CONFIGITEM_H_ + + +#include "newui.h" + +#if defined(LINUX) + void CIListBoxCallback(int ID,void *); + void CISliderCallback(int ID,void *); +#endif + +//config item types +#define CIT_HOTSPOTLIST 0x01 +#define CIT_SLIDER 0x02 +#define CIT_ONOFFBUTTON 0x03 +#define CIT_LISTBOX 0x04 +#define CIT_YESNOBUTTON 0x05 +#define CIT_RADIOBUTTON 0x06 +#define CIT_CHECKBOX 0x07 + +//config item value types +#define CIV_BOOL 0x01 +#define CIV_INT 0x02 +#define CIV_FLOAT 0x03 + +//config item flags +#define CIF_PERCENT 0x01 //tells a slider to display a percent sign +#define CIF_NODECIMAL 0x02 //tells a slider not to display a decimal point (float sliders only) +#define CIF_USEGROUP 0x04 //places a group box around the configitem (CIT_RADIOBUTTON only) + +#define HOTSPOTLIST_GAP 8 //the horizontal gap between hotspot lists +#define LISTBOX_LABELGAP 3 //the vertical gap between label and listbox +#define RADIOBUTTON_LABELGAP 3 //the vertical gap between label and radiobutton list +#define RADIOBUTTON_GAP 3 //the vertical gap between radiobuttons + +int GetUniqueID(void); + +class ConfigItem +{ +public: + ConfigItem(); + ~ConfigItem(); + + //create functions + bool Create(NewUIGameWindow *parentwnd,int type,char *label,int x,int y,int w,int h,int initialvalue,int flags,int item_x); + bool Create(NewUIGameWindow *parentwnd,int type,char *label,int x,int y,int w,int h,bool initialvalue,int flags,int item_x); + bool Create(NewUIGameWindow *parentwnd,int type,char *label,int x,int y,int w,int h,float initialvalue,int flags,int item_x); + + //destroy function + void Destroy(void); + + //gets the current value (returns true on success) + bool GetCurrentValue(int *currvalue); + bool GetCurrentValue(bool *currvalue); + bool GetCurrentValue(float *currvalue); + + //sets the current value for the item + bool SetCurrentValue(int currvalue,bool call_callback=true); + bool SetCurrentValue(bool currvalue,bool call_callback=true); + bool SetCurrentValue(float currvalue,bool call_callback=true); + + //gets the initial value (returns true on success) + bool GetInitialValue(int *initvalue); + bool GetInitialValue(bool *initvalue); + bool GetInitialValue(float *initvalue); + + //set range + void SetRange(int min,int max,int range); + void SetRange(float min,float max,int range); + + //sets a callback for when an item changes + void SetCallback(void (*callback)(float)); + void SetCallback(void (*callback)(int)); + void SetCallback(void (*callback)(bool)); + + //updates the config item (pass in result from DoUI) + void Update(int result); + + //adds items to the config item (count is number of items, followed by a list of UITextItems) + void Add(int count, ... ); + + friend void CIListBoxCallback(int ID,void *); + friend void CISliderCallback(int ID,void *); + +private: + bool Create(NewUIGameWindow *parentwnd,int type,int flags,int x,int y,int w,int h,char *label,int item_x); + void UpdateHotSpotList(int index); + void UpdateSlider(int index,bool call_callback=true); + void UpdateOnOffButton(int index); + void UpdateListBox(int index); + void UpdateYesNoButton(int index); + void UpdateRadioButton(int index); + void UpdateCheckBox(int index); + + bool m_bAlive; //true is the config item has been created not destroyed yet + //Initial value of the config item + union{ + bool m_bInitial; + int m_iInitial; + float m_fInitial; + }; + //current value of the config item + union{ + bool m_bCurrentValue; + int m_iCurrentValue; + float m_fCurrentValue; + }; + int m_X,m_Y,m_W,m_H; //x,y, width and height for the config item + int curr_x,curr_y; //current x and y (relative to m_X and m_Y) + int m_iFlags; //flags for config item + int m_iType; //how is this config item represented + int *m_iID; //unique ID[s] of the config item + int m_iNumIDs; //number of IDs + int m_iItems; //number of items associated with this config item + int m_iValueType; //what kind of value is this config item holding + UIText m_tLabel; //Text item for the label + UIGroup m_gGroup; //Group box if needed + int m_auxX; //contains the original x passed in + int m_auxY; //contains the original y passed in + UIText m_tLabel2; //Used for Sliders + NewUIGameWindow *m_hWnd; //parent window + UITextItem **m_tiList; //list of text items for config item + int m_tiCount; //number of text items + UIHotspot **m_hsList; //list of hotspot items + int m_hsCount; //number of hotspot items + NewUISlider **m_sList; //list of slider items + int m_sCount; //number of sliders + UIRadioButton **m_rbList; //list of radio buttons + int m_rbCount; //number of radio buttons + NewUIButton **m_bList; //list of button items + int m_bCount; //number of buttons + NewUIListBox **m_lbList; //list of list boxes + int m_lbCount; //number of list boxes + float m_fMin,m_fMax; //range min/max + int m_iRange; //range + float m_fUnit; //scale between min->max to fit in range + char *m_labeltext; //we need to save the label for group boxes + void (*m_fCallback)(float); + void (*m_iCallback)(int); + void (*m_bCallback)(bool); +}; + +#endif diff --git a/Descent3/Controls.cpp b/Descent3/Controls.cpp new file mode 100644 index 000000000..2807117d5 --- /dev/null +++ b/Descent3/Controls.cpp @@ -0,0 +1,1604 @@ +/* + * $Logfile: /DescentIII/main/Controls.cpp $ + * $Revision: 106 $ + * $Date: 4/19/00 5:28p $ + * $Author: Matt $ + * + * Game controls processor + * + * $Log: /DescentIII/main/Controls.cpp $ + * + * 106 4/19/00 5:28p Matt + * From Duane for 1.4 + * Changed "ifdef Mac" to "ifndef Mac" + * + * 105 3/20/00 12:03p Matt + * Merge of Duane's post-1.3 changes. + * Misc Mac controller stuff (Mac only) + * + * 104 1/25/00 12:11a Jason + * Fixed keyboard ramping that got commented out during a mac merge. + * + * 103 10/22/99 1:16p Matt + * Fixed bug from previous integration. + * + * 102 10/22/99 1:12p Matt + * Mac merge + * + * 101 10/22/99 12:50p Matt + * Restored whitespace removed in version 99, and changed the controller + * table to its version 1.0 state. (This actually removes all the changes + * made for version 99.) + * + * 100 8/11/99 6:06p Samir + * mouse invert toggle slide functional. + * + * 99 7/28/99 4:20p Kevin + * Mac + * + * 98 7/20/99 4:53p Samir + * added ability to manually set the deadzone for a controller. + * + * 97 7/16/99 11:16a Samir + * fixed afterburner thrust clipping bug. + * + * 96 5/10/99 9:25p Jeff + * first phase of Rock 'n' Ride support added + * + * 95 4/30/99 10:52p Samir + * fixed energy-to-shield usage and made Load and Save COnfig take a + * pilot. + * + * 94 4/24/99 6:44p Jeff + * added functions for theif so he can steal things other than weapons + * + * 93 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 92 4/01/99 11:27a Samir + * added default mouse button bindings. + * + * 91 3/25/99 9:57a Kevin + * Fixed the way toggle slide works + * + * 90 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 89 3/05/99 5:23p Samir + * joystick button audio taunt control fixed. + * + * 88 3/05/99 2:44p Samir + * needs to be cleaned up later, but mouse and joystick sensitivities are + * read in always, and set by the controller system. A cancel method + * needs to be implemented for these functions. + * + * 87 3/04/99 8:07p Samir + * pre assignment of r axis enabled once again. + * + * 86 3/01/99 12:19a Samir + * mask controller states on create controller. + * + * 85 2/26/99 2:13a Samir + * added audio taunt keys. + * + * 84 2/23/99 7:33p Samir + * don't configure bank through joystick by default. + * + * 83 2/23/99 2:00p Samir + * added keyboard ramping slider. + * + * 82 2/15/99 7:48p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 81 2/09/99 1:28a Jeff + * added code to let D3 process multiplayer games in the background if + * game is not in focus. + * + * 80 2/06/99 6:58p Jeff + * removed mprintf for suspend/resume (annoying in automap) + * + * 79 2/04/99 4:22p Jeff + * put in sanity mprintf's in suspend/resume controls + * + * 78 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 77 1/28/99 3:58p Jeff + * localization update + * + * 76 1/06/99 6:45p Kevin + * + * 75 12/14/98 11:06a Jason + * changes for 1.1 + * + * 74 12/08/98 10:27a Samir + * default player controls include mouse controls. + * + * 73 12/07/98 5:37p Jason + * first pass at automap + * + * 72 12/03/98 11:06a Samir + * added axis sensitivity + * + * 71 12/02/98 7:02p Samir + * reduces key pitch and heading ramp times. + * + * 70 12/02/98 12:23p Samir + * fixed behavior of bank and slide toggles so they are read before both + * keyboard and joystick. + * + * 69 12/02/98 11:37a Samir + * improved ramping of keyboard. + * + * 68 11/30/98 4:56p Samir + * oops. took out mprintf. + * + * 67 11/30/98 4:54p Samir + * added rear view config item. + * + * 66 11/30/98 1:09p Samir + * added rear view toggle. + * + * 65 10/24/98 2:16p Samir + * moved pollcontrols to controls.cpp, readplayercontrols. + * + * 64 10/22/98 4:11p Samir + * put inventory keys back in. + * + * 63 10/22/98 2:40p Samir + * took out inventory keys for demo. + * + * 62 10/21/98 11:55p Samir + * added sensitivity slider functionality. + * + * 61 10/21/98 7:03p Samir + * D2 Default controls + * + * 60 10/21/98 10:36a Samir + * added code to turn on or off joystick or mouse. + * + * 59 10/18/98 7:24p Samir + * cycle down weapons, use Frametime for controller buttons. + * + * 58 10/17/98 7:32p Samir + * added to controller needs, the extra flags section per function. + * + * 57 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 56 10/12/98 5:12p Samir + * toggle bank key works. + * + * 55 10/11/98 2:58a Jeff + * hooked in Automap key + * + * 54 10/08/98 6:43p Samir + * took out mprintf. + * + * 53 10/08/98 1:24p Samir + * use ramping technique for keyboard pitch and heading. + * + * 52 10/08/98 10:52a Samir + * added countermeasure and weapon cycling. + * + * 51 9/28/98 10:07a Jason + * fixed MISC keyboard input while inputting messages + * + * 50 9/21/98 3:21p Jason + * bound headlight to a key + * + * 49 9/17/98 3:24p Samir + * added headlight configuration. + * + * 48 9/01/98 5:11p Samir + * no additvie afterburner control. + * + * 47 8/31/98 6:49p Jeff + * made inventory and countermeasure keys customizable + * + * 46 8/31/98 12:39p Samir + * added code to resume controllers when restoring game mode. + * + * 45 8/15/98 2:45p Matt + * Took out unneeded include + * + * 44 6/29/98 7:17p Samir + * took out annoying mprintf + * + * 43 6/29/98 6:42p Samir + * Properly handle controller pausing and resumption. + * + * 42 6/18/98 4:44p Samir + * added changes for multiple configs for joystick controls. + * + * 41 6/16/98 10:38a Jeff + * localization, strings pulled out to stringtable.h and d3.str + * + * 40 5/28/98 1:08p Samir + * flipped bank button calcs. + * + * 39 5/26/98 7:09p Samir + * pitch now 75% of maximum instead of 50% as asked by Chris and Craig. + * + * 38 5/19/98 3:36p Samir + * added automap key. + * + * 37 5/12/98 3:04p Samir + * added button control for pbh. + * + * 36 5/12/98 10:38a Samir + * pitch rate halved. + * + * 35 5/11/98 4:53p Samir + * added axis sliding, config. + * + * 34 5/01/98 3:52p Samir + * fixed up hat and keyboard sliding. + * + * 33 4/30/98 7:00p Samir + * reset afterburner thrust every frame. + * + * 32 4/28/98 11:45a Samir + * make keyboard controls match to framerate. + * + * 31 4/27/98 7:24p Samir + * poll controller at specified rate. + * + * 30 4/09/98 2:22p Jason + * added guided/afterburner stuff + * + * 29 4/08/98 3:44p Samir + * added toggle bank and configuration for afterburner + * + * 28 4/02/98 7:58p Samir + * Fixed up control setting saving and restoring. + * + * 27 3/24/98 11:21a Samir + * adjusted key sensitivity. + * + * 26 3/20/98 1:19p Jeff + * Changes made to use Default_pilot string for pilot filename to use. + * + * 25 3/16/98 3:53p Jason + * added hud input message stuff + * + * 24 3/16/98 3:26p Samir + * Fixed controller need ID and index discrepancy. + * + * 23 3/16/98 11:03a Luke + * Don't reset controls again., + * + * 22 3/13/98 8:55p Jeff + * Various changes to move control configuration into Pilot file + * + * 21 3/10/98 5:21p Samir + * banking should be proper. + * + * 20 3/02/98 5:54p Samir + * Don't run any controls if not in game interface mode. + * + * 19 2/16/98 9:27p Samir + * Added banking. + * + * 18 2/16/98 3:01p Samir + * Added fire_primary_down_state and changed some configuration. + * + * 17 2/15/98 7:06p Samir + * Added functions to save controller state. + * + * 16 2/12/98 8:48p Matt + * Changed controls system to keep the reading of the controls seperate + * from using the results. Got rid of the Controls global. + * + * 15 1/23/98 6:25p Jason + * Got spray weapons working + * + * 14 1/12/98 6:28p Samir + * New keys for sliding and use RCTRL for firing too. + * + * 13 12/10/97 12:18p Samir + * Tooke oute annoiing printe Fs. + * + * 12 12/10/97 11:16a Samir + * Joystick button timing should be fixed. + * + * 11 12/05/97 12:49p Samir + * Much better hat sliding. + * + * 10 12/05/97 10:59a Samir + * Somewhat revised controls for keyboard. Joystick now has variable + * sensitivity. + * + * 9 12/03/97 7:32p Samir + * More D2 like keyboard movement. + * + * 8 11/12/97 1:13p Jason + * added weapons that can ramp up + * + * 7 11/05/97 3:45p Samir + * InitControls will close the previous call to InitControls. + * + * 6 11/04/97 4:20p Samir + * replaced ctDigital with ctDownCount in some joystick button functions. + * + * 5 10/30/97 4:02p Matt + * Added the flare + * + * 4 9/04/97 4:01p Samir + * This should fix slide toggling with joystick. + * + * 3 8/29/97 1:49p Samir + * Added toggling slide. + * + * 2 8/20/97 5:03p Samir + * Fixed banking controls. + * + * 24 5/21/97 3:53p Samir + * changed ct_need to ct_function. + * + * 23 5/12/97 1:22p Samir + * Don't use timer hook functions anymore. taken care of in controller + * library. + * + * 22 4/24/97 2:09p Samir + * Fixed firing key problems. + * + * 21 4/24/97 12:32p Samir + * Fixed booboo in controls structure. + * + * 20 4/24/97 12:10p Samir + * Added weapon firing for primary and secondary weapons. + * + * 19 4/23/97 1:06p Samir + * Implemented Suspend and Resume Controls as well as newer control system + * + * 18 4/17/97 2:48p Samir + * ReadFlyingControls renamed from read_flying_controls to match naming + * convention rules. + * + * 17 4/16/97 3:28p Samir + * Poll twice for better keeping track of buttons. Should find a lot + * better way of doing this. + * + * 16 4/16/97 1:03p Samir + * Added fire primary button information + * + * 15 4/14/97 3:29p Samir + * Implemented game controls structure. + * + * 14 4/14/97 12:55p Samir + * Added primary fire button + * + * 13 4/11/97 5:11p Samir + * Added external controller throttle control. + * + * 12 4/11/97 4:54p Samir + * Fixed joystick pitch translation (invert for real flight stick control) + * + * 11 4/11/97 4:09 PM Jeremy + * changed include of "stdlib.h" to + * + * 10 4/11/97 2:14p Samir + * Revamped input of controller data. + * + * $NoKeywords: $ + */ + +#include "controls.h" + +#include "object.h" +#include "pserror.h" +#include "game.h" +#include "ddio.h" +#include "joystick.h" +#include "descent.h" +#include "mono.h" +#include "weapon.h" +#include "Controller.h" +#include "Macros.h" +#include "gamesequence.h" +#include "pilot.h" +#include "hud.h" +#include "stringtable.h" +#include "TelCom.h" +#include "multi.h" +#include "args.h" + +#include "player.h" + +#include "hlsoundlib.h" +#include "sounds.h" +#include "soundload.h" + +#include "rocknride.h" + +#include +#include + +#ifdef MACINTOSH +#include "insprocket.h" +#endif + +float Key_ramp_speed = 0.5f; + + +#define CONTROL_POLL_RATE (1.0f/25.0f) +#define KEY_RAMPUP_TIME Key_ramp_speed +#define POV_SENSITIVITY 1.3f +#define LIMIT_PITCH 0.75f +#define LIMIT_HEADING 1.0f +#define LIMIT_BANK 1.0f +#define LIMIT_HORIZ 1.0f +#define LIMIT_VERT 1.0f +#define LIMIT_FORWARD 1.0f + + +typedef struct tSpace +{ + float p,b,h,x,y,z; + float op, ob, oh, ox, oy, oz; +} +tSensitivity; + +// GLOBALS + +char *Controller_ip = NULL; // IP of controller, if any. +gameController *Controller = NULL; +bool Control_poll_flag = false; // determines if system is polling controls now. + +static bool Control_system_init = false; +static float Control_interval_time = 0.0f; +static float Control_current_time; +static float Control_frametime; +static float Key_pitch_ramp_time = 0.0f; +static float Key_heading_ramp_time = 0.0f; + +static tSpace Key_ramp; + + +// PROTOTYPES + +void DoMovement(game_controls *controls); +void DoKeyboardMovement(game_controls *controls); +void DoControllerMovement(game_controls *controls); +void DoWeapons(game_controls *controls); +void DoKeyboardWeapons(game_controls *controls); +void DoControllerWeapons(game_controls *controls); +void DoMisc(game_controls *contols); +void DoKeyboardMisc(game_controls *controls); +void DoControllerMisc(game_controls *controls); +void DoCommands(); + + +void ToggleHeadlightControlState (); + + +// LIST OF NEEDS +#ifdef MACINTOSH +ct_function Controller_needs[NUM_CONTROLLER_FUNCTIONS] = { + { ctfFORWARD_THRUSTAXIS, ctAnalog, ctAxis, ctAxis, CT_Z_AXIS, CT_Z_AXIS ,0,0}, //DAJ added missing CT_Z_AXIS + { ctfFORWARD_THRUSTKEY, ctTime, ctKey, ctKey, KEY_W, 0 ,0,0}, + { ctfREVERSE_THRUSTKEY, ctTime, ctKey, ctKey, KEY_S, 0 ,0,0}, + { ctfFORWARD_BUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfREVERSE_BUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfUP_THRUSTAXIS, ctAnalog, ctAxis, ctAxis, CT_V_AXIS, CT_V_AXIS ,0,0}, //DAJ + { ctfUP_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_UP, 0 ,0,0}, + { ctfUP_THRUSTKEY, ctTime, ctKey, ctKey, KEY_R, 0 ,0,0}, + { ctfDOWN_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_DOWN, 0 ,0,0}, + { ctfDOWN_THRUSTKEY, ctTime, ctKey, ctKey, KEY_F, 0 ,0,0}, + { ctfRIGHT_THRUSTAXIS, ctAnalog, ctAxis, ctAxis, CT_U_AXIS, CT_U_AXIS ,0,0}, //DAJ + { ctfRIGHT_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_RIGHT, 0 ,0,0}, + { ctfRIGHT_THRUSTKEY, ctTime, ctKey, ctKey, KEY_D, 0 ,0,0}, + { ctfLEFT_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_LEFT, 0 ,0,0}, + { ctfLEFT_THRUSTKEY, ctTime, ctKey, ctKey, KEY_A, 0 ,0,0}, + { ctfPITCH_DOWNAXIS, ctAnalog, ctAxis, ctMouseAxis,CT_Y_AXIS, CT_Y_AXIS ,0,0}, + { ctfPITCH_DOWNKEY, ctTime, ctKey, ctKey, KEY_UP, KEY_PAD8,0,0}, + { ctfPITCH_DOWNBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfPITCH_UPKEY, ctTime, ctKey, ctKey, KEY_DOWN, KEY_PAD2,0,0}, + { ctfPITCH_UPBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfBANK_RIGHTAXIS, ctAnalog, ctAxis, ctAxis, CT_R_AXIS, CT_R_AXIS, 0,0}, //DAJ + { ctfBANK_RIGHTKEY, ctTime, ctKey, ctKey, KEY_E, KEY_PAD9 ,0,0}, + { ctfBANK_RIGHTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfBANK_LEFTKEY, ctTime, ctKey, ctKey, KEY_Q, KEY_PAD7 ,0,0}, + { ctfBANK_LEFTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfHEADING_RIGHTAXIS, ctAnalog, ctAxis, ctMouseAxis,CT_X_AXIS, CT_X_AXIS ,0,0}, + { ctfHEADING_RIGHTKEY, ctTime, ctKey, ctKey, KEY_RIGHT, KEY_PAD6,0,0}, + { ctfHEADING_RIGHTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfHEADING_LEFTKEY, ctTime, ctKey, ctKey, KEY_LEFT, KEY_PAD4,0,0}, + { ctfHEADING_LEFTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfFIREPRIMARY_BUTTON, ctTime, ctButton, ctMouseButton, 1, 1,0,0}, + { ctfFIREPRIMARY_KEY, ctTime, ctKey, ctKey, KEY_SPACEBAR, KEY_RCTRL,0,0}, + { ctfFIREPRIMARY_KEY2, ctTime, ctKey, ctKey, 0, 0 ,0,0}, + { ctfFIRESECONDARY_BUTTON, ctTime, ctButton, ctMouseButton, 2, 2 ,0,0}, + { ctfFIRESECONDARY_KEY, ctTime, ctKey, ctKey, KEY_V, 0 ,0,0}, + { ctfTOGGLE_SLIDEBUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfTOGGLE_SLIDEKEY, ctTime, ctKey, ctKey, KEY_LALT, 0 ,0,0}, + { ctfTOGGLE_BANKBUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfTOGGLE_BANKKEY, ctTime, ctKey, ctKey, 0, 0 ,0,0}, + { ctfFIREFLARE_BUTTON, ctTime, ctButton, ctMouseButton, 3, 3 ,0,0}, + { ctfFIREFLARE_KEY, ctDownCount,ctKey, ctKey, KEY_G, 0 ,0,0}, + { ctfAFTERBURN_BUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfAFTERBURN_KEY, ctTime, ctKey, ctKey, KEY_C, 0 ,0,0}, + { ctfAUTOMAP_KEY, ctDownCount,ctKey, ctKey, KEY_TAB, 0 ,0,0}, + { ctfPREV_INVKEY, ctDownCount,ctKey, ctKey, KEY_LBRACKET, 0 ,0,0}, + { ctfNEXT_INVKEY, ctDownCount,ctKey, ctKey, KEY_RBRACKET, 0 ,0,0}, + { ctfINV_USEKEY, ctDownCount,ctKey, ctKey, KEY_BACKSLASH, 0 ,0,0}, + { ctfPREV_CNTMSKEY, ctDownCount,ctKey, ctKey, KEY_SEMICOL, 0 ,0,0}, + { ctfNEXT_CNTMSKEY, ctDownCount,ctKey, ctKey, KEY_RAPOSTRO, 0 ,0,0}, + { ctfCNTMS_USEKEY, ctDownCount,ctKey, ctKey, KEY_ENTER, 0 ,0,0}, + { ctfHEADLIGHT_KEY, ctDownCount,ctKey, ctKey, KEY_H, 0 ,0,0}, + { ctfHEADLIGHT_BUTTON, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUTOMAP_BUTTON, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfPREV_INVBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfNEXT_INVBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfINV_USEBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfPREV_CNTMSBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfNEXT_CNTMSBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfCNTMS_USEBTN, ctDownCount,ctButton, ctMouseButton, 4, 4 ,0,0}, + { ctfWPNSEL_PCYCLEKEY, ctDownCount,ctKey, ctKey, KEY_COMMA, 0 ,0,0}, + { ctfWPNSEL_PCYCLEBTN, ctDownCount,ctButton, ctMouseButton, 0, 5 ,0,0}, + { ctfWPNSEL_SCYCLEKEY, ctDownCount,ctKey, ctKey, KEY_PERIOD, 0 ,0,0}, + { ctfWPNSEL_SCYCLEBTN, ctDownCount,ctButton, ctMouseButton, 0, 6 ,0,0}, + { ctfREARVIEW_KEY, ctDownCount,ctKey, ctKey, KEY_B, 0 ,0,0}, + { ctfREARVIEW_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT1_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT1_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT2_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT2_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT3_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT3_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT4_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT4_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0} +}; +#else +ct_function Controller_needs[NUM_CONTROLLER_FUNCTIONS] = { + { ctfFORWARD_THRUSTAXIS, ctAnalog, ctAxis, ctAxis, 0, 0 ,0,0}, + { ctfFORWARD_THRUSTKEY, ctTime, ctKey, ctKey, KEY_A, 0 ,0,0}, + { ctfREVERSE_THRUSTKEY, ctTime, ctKey, ctKey, KEY_Z, 0 ,0,0}, + { ctfFORWARD_BUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfREVERSE_BUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfUP_THRUSTAXIS, ctAnalog, ctAxis, ctAxis, CT_V_AXIS, 0 ,0,0}, + { ctfUP_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_UP, 0 ,0,0}, + { ctfUP_THRUSTKEY, ctTime, ctKey, ctKey, KEY_PADMINUS, 0 ,0,0}, + { ctfDOWN_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_DOWN, 0 ,0,0}, + { ctfDOWN_THRUSTKEY, ctTime, ctKey, ctKey, KEY_PADPLUS, 0 ,0,0}, + { ctfRIGHT_THRUSTAXIS, ctAnalog, ctAxis, ctAxis, CT_U_AXIS, 0 ,0,0}, + { ctfRIGHT_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_RIGHT, 0 ,0,0}, + { ctfRIGHT_THRUSTKEY, ctTime, ctKey, ctKey, KEY_PAD3, 0 ,0,0}, + { ctfLEFT_BUTTON, ctDigital, ctPOV, ctButton, JOYPOV_LEFT, 0 ,0,0}, + { ctfLEFT_THRUSTKEY, ctTime, ctKey, ctKey, KEY_PAD1, 0 ,0,0}, + { ctfPITCH_DOWNAXIS, ctAnalog, ctAxis, ctMouseAxis,CT_Y_AXIS, CT_Y_AXIS ,0,0}, + { ctfPITCH_DOWNKEY, ctTime, ctKey, ctKey, KEY_UP, KEY_PAD8,0,0}, + { ctfPITCH_DOWNBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfPITCH_UPKEY, ctTime, ctKey, ctKey, KEY_DOWN, KEY_PAD2,0,0}, + { ctfPITCH_UPBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfBANK_RIGHTAXIS, ctAnalog, ctAxis, ctAxis, CT_R_AXIS, 0 ,0,0}, + { ctfBANK_RIGHTKEY, ctTime, ctKey, ctKey, KEY_E, KEY_PAD9 ,0,0}, + { ctfBANK_RIGHTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfBANK_LEFTKEY, ctTime, ctKey, ctKey, KEY_Q, KEY_PAD7 ,0,0}, + { ctfBANK_LEFTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfHEADING_RIGHTAXIS, ctAnalog, ctAxis, ctMouseAxis,CT_X_AXIS, CT_X_AXIS ,0,0}, + { ctfHEADING_RIGHTKEY, ctTime, ctKey, ctKey, KEY_RIGHT, KEY_PAD6,0,0}, + { ctfHEADING_RIGHTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfHEADING_LEFTKEY, ctTime, ctKey, ctKey, KEY_LEFT, KEY_PAD4,0,0}, + { ctfHEADING_LEFTBUTTON, ctDigital, ctButton, ctButton, 0, 0 ,0,0}, + { ctfFIREPRIMARY_BUTTON, ctTime, ctButton, ctMouseButton, 1, 1,0,0}, + { ctfFIREPRIMARY_KEY, ctTime, ctKey, ctKey, KEY_LCTRL, KEY_RCTRL,0,0}, + { ctfFIREPRIMARY_KEY2, ctTime, ctKey, ctKey, 0, 0 ,0,0}, + { ctfFIRESECONDARY_BUTTON, ctTime, ctButton, ctMouseButton, 2, 2 ,0,0}, + { ctfFIRESECONDARY_KEY, ctTime, ctKey, ctKey, KEY_SPACEBAR, 0 ,0,0}, + { ctfTOGGLE_SLIDEBUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfTOGGLE_SLIDEKEY, ctTime, ctKey, ctKey, KEY_LALT, 0 ,0,0}, + { ctfTOGGLE_BANKBUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfTOGGLE_BANKKEY, ctTime, ctKey, ctKey, 0, 0 ,0,0}, + { ctfFIREFLARE_BUTTON, ctTime, ctButton, ctMouseButton, 3, 3 ,0,0}, + { ctfFIREFLARE_KEY, ctDownCount,ctKey, ctKey, KEY_F, 0 ,0,0}, + { ctfAFTERBURN_BUTTON, ctTime, ctButton, ctButton, 0, 0 ,0,0}, + { ctfAFTERBURN_KEY, ctTime, ctKey, ctKey, KEY_S, 0 ,0,0}, + { ctfAUTOMAP_KEY, ctDownCount,ctKey, ctKey, KEY_TAB, 0 ,0,0}, + { ctfPREV_INVKEY, ctDownCount,ctKey, ctKey, KEY_LBRACKET, 0 ,0,0}, + { ctfNEXT_INVKEY, ctDownCount,ctKey, ctKey, KEY_RBRACKET, 0 ,0,0}, + { ctfINV_USEKEY, ctDownCount,ctKey, ctKey, KEY_BACKSLASH, 0 ,0,0}, + { ctfPREV_CNTMSKEY, ctDownCount,ctKey, ctKey, KEY_SEMICOL, 0 ,0,0}, + { ctfNEXT_CNTMSKEY, ctDownCount,ctKey, ctKey, KEY_RAPOSTRO, 0 ,0,0}, + { ctfCNTMS_USEKEY, ctDownCount,ctKey, ctKey, KEY_ENTER, 0 ,0,0}, + { ctfHEADLIGHT_KEY, ctDownCount,ctKey, ctKey, KEY_H, 0 ,0,0}, + { ctfHEADLIGHT_BUTTON, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUTOMAP_BUTTON, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfPREV_INVBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfNEXT_INVBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfINV_USEBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfPREV_CNTMSBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfNEXT_CNTMSBTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfCNTMS_USEBTN, ctDownCount,ctButton, ctMouseButton, 4, 4 ,0,0}, + { ctfWPNSEL_PCYCLEKEY, ctDownCount,ctKey, ctKey, KEY_COMMA, 0 ,0,0}, + { ctfWPNSEL_PCYCLEBTN, ctDownCount,ctButton, ctMouseButton, 0, 5 ,0,0}, + { ctfWPNSEL_SCYCLEKEY, ctDownCount,ctKey, ctKey, KEY_PERIOD, 0 ,0,0}, + { ctfWPNSEL_SCYCLEBTN, ctDownCount,ctButton, ctMouseButton, 0, 6 ,0,0}, + { ctfREARVIEW_KEY, ctDownCount,ctKey, ctKey, KEY_R, 0 ,0,0}, + { ctfREARVIEW_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT1_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT1_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT2_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT2_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT3_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT3_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0}, + { ctfAUDIOTAUNT4_KEY, ctDownCount,ctKey, ctKey, 0, 0 ,0,0}, + { ctfAUDIOTAUNT4_BTN, ctDownCount,ctButton, ctButton, 0, 0 ,0,0} +}; +#endif + +// ramping macros +inline float ramp_control_value(float val, float limit, float& ramp_state, float& old_ramp_delta) +{ + float sign = val/fabs(val), old_sign = old_ramp_delta/fabs(old_ramp_delta); + if (sign != old_sign) ramp_state = -ramp_state; + ramp_state = (val != 0) ? (ramp_state + val) : 0.0f; + ramp_state = (ramp_state > limit) ? limit : (ramp_state < -limit) ? (-limit) : ramp_state; + old_ramp_delta = val; + + return ramp_state; +} + + + + +// INITIALIZATION FUNCTIONS + +void InitControls() +{ +// Create controller object + if (Control_system_init) return; + + Control_system_init = true; + + Controller = CreateController(NUM_CONTROLLER_FUNCTIONS, Controller_needs, Controller_ip); + if (Controller == NULL) + Error(TXT_ERRUNINITCNT); + Controller->mask_controllers(false, false); + Controller->mask_controllers(true, true); + + + Controller->mask_controllers((Current_pilot.read_controller&READF_JOY)?true:false, + (Current_pilot.read_controller&READF_MOUSE)?true:false); + + Controller->set_axis_sensitivity(ctMouseAxis, CT_X_AXIS, Current_pilot.mouse_sensitivity[0]); + Controller->set_axis_sensitivity(ctMouseAxis, CT_Y_AXIS, Current_pilot.mouse_sensitivity[1]); + Controller->set_axis_sensitivity(ctMouseAxis, CT_Z_AXIS, Current_pilot.mouse_sensitivity[0]); + Controller->set_axis_sensitivity(ctAxis, CT_X_AXIS, Current_pilot.joy_sensitivity[0]); + Controller->set_axis_sensitivity(ctAxis, CT_Y_AXIS, Current_pilot.joy_sensitivity[1]); + Controller->set_axis_sensitivity(ctAxis, CT_Z_AXIS, Current_pilot.joy_sensitivity[2]); + Controller->set_axis_sensitivity(ctAxis, CT_R_AXIS, Current_pilot.joy_sensitivity[3]); + Controller->set_axis_sensitivity(ctAxis, CT_U_AXIS, Current_pilot.joy_sensitivity[4]); + Controller->set_axis_sensitivity(ctAxis, CT_V_AXIS, Current_pilot.joy_sensitivity[5]); + +#ifndef MACINTOSH + int i; + i = FindArg("-deadzone0"); + if (i > 0) { + Controller->set_controller_deadzone(0, atof(GameArgs[i+1])); + } + i = FindArg("-deadzone1"); + if (i > 0) { + Controller->set_controller_deadzone(1, atof(GameArgs[i+1])); + } +#endif + + Key_ramp_speed = Current_pilot.key_ramping; //DAJ added to restore ramping + + SuspendControls(); + +// start controller time. + Control_current_time = Control_interval_time = timer_GetTime(); + Key_ramp.op = Key_ramp.p = 0.0f; + Key_ramp.ob = Key_ramp.b = 0.0f; + Key_ramp.oh = Key_ramp.h = 0.0f; + Key_ramp.ox = Key_ramp.x = 0.0f; + Key_ramp.oy = Key_ramp.y = 0.0f; + Key_ramp.oz = Key_ramp.z = 0.0f; + +// Initialize preemptive controller system for non-positonal data. +#ifdef MACINTOSH + inSprocket_Init(); +#endif + mprintf((0, "Initialized control system.\n")); +} + + +void CloseControls() +{ + if (!Control_system_init) return; + ResumeControls(); + DestroyController(Controller); + Controller = NULL; +#ifdef MACINTOSH + inSprocket_Exit(); +#endif + mprintf((0, "Closing control system.\n")); + Control_system_init = false; +} + + +// reinits the controller, hence restoring default controller configurations +void RestoreDefaultControls() +{ + ResumeControls(); + DestroyController(Controller); + Controller = CreateController(NUM_CONTROLLER_FUNCTIONS, Controller_needs, Controller_ip); + if (Controller == NULL) + Error(TXT_ERRUNINITCNT); + SuspendControls(); + + Controller->mask_controllers((Current_pilot.read_controller&READF_JOY)?true:false, + (Current_pilot.read_controller&READF_MOUSE)?true:false); + Controller->set_axis_sensitivity(ctMouseAxis, CT_X_AXIS, Current_pilot.mouse_sensitivity[0]); + Controller->set_axis_sensitivity(ctMouseAxis, CT_Y_AXIS, Current_pilot.mouse_sensitivity[1]); + Controller->set_axis_sensitivity(ctMouseAxis, CT_Z_AXIS, Current_pilot.mouse_sensitivity[0]); + Controller->set_axis_sensitivity(ctAxis, CT_X_AXIS, Current_pilot.joy_sensitivity[0]); + Controller->set_axis_sensitivity(ctAxis, CT_Y_AXIS, Current_pilot.joy_sensitivity[1]); + Controller->set_axis_sensitivity(ctAxis, CT_Z_AXIS, Current_pilot.joy_sensitivity[2]); + Controller->set_axis_sensitivity(ctAxis, CT_R_AXIS, Current_pilot.joy_sensitivity[3]); + Controller->set_axis_sensitivity(ctAxis, CT_U_AXIS, Current_pilot.joy_sensitivity[4]); + Controller->set_axis_sensitivity(ctAxis, CT_V_AXIS, Current_pilot.joy_sensitivity[5]); +#ifdef MACINTOSH + Controller->set_controller_deadzone(2, Current_pilot.mouse_sensitivity[0]); +#endif +} + + +void SuspendControls() +{ + if(!Controller) + return; + + Control_poll_flag = false; + Controller->suspend(); +} + + +void ResumeControls() +{ + if(!Controller) + return; + + Controller->resume(); + Controller->flush(); + ddio_KeyFlush(); + ddio_MouseQueueFlush(); + + Control_poll_flag = true; + +// set pilot defaults for controller outside of assignment + Controller->mask_controllers((Current_pilot.read_controller&READF_JOY)?true:false, + (Current_pilot.read_controller&READF_MOUSE)?true:false); + Controller->set_axis_sensitivity(ctMouseAxis, CT_X_AXIS, Current_pilot.mouse_sensitivity[0]); + Controller->set_axis_sensitivity(ctMouseAxis, CT_Y_AXIS, Current_pilot.mouse_sensitivity[1]); + Controller->set_axis_sensitivity(ctMouseAxis, CT_Z_AXIS, Current_pilot.mouse_sensitivity[0]); + Controller->set_axis_sensitivity(ctAxis, CT_X_AXIS, Current_pilot.joy_sensitivity[0]); + Controller->set_axis_sensitivity(ctAxis, CT_Y_AXIS, Current_pilot.joy_sensitivity[1]); + Controller->set_axis_sensitivity(ctAxis, CT_Z_AXIS, Current_pilot.joy_sensitivity[2]); + Controller->set_axis_sensitivity(ctAxis, CT_R_AXIS, Current_pilot.joy_sensitivity[3]); + Controller->set_axis_sensitivity(ctAxis, CT_U_AXIS, Current_pilot.joy_sensitivity[4]); + Controller->set_axis_sensitivity(ctAxis, CT_V_AXIS, Current_pilot.joy_sensitivity[5]); + +#ifdef MACINTOSH + Controller->set_controller_deadzone(2, Current_pilot.mouse_sensitivity[0]); +#else + int i; + i = FindArg("-deadzone0"); + if (i > 0) { + Controller->set_controller_deadzone(0, atof(GameArgs[i+1])); + } + i = FindArg("-deadzone1"); + if (i > 0) { + Controller->set_controller_deadzone(1, atof(GameArgs[i+1])); + } +#endif +} + + + +// INTERFACE FUNCTIONS + +void PollControls() +{ +// only read at the specified rate to keep things consistant. manager control system timer + if (!Control_poll_flag) + return; + if (Controller) + Controller->poll(); + /* + Control_current_time += Frametime; + Control_frametime = Control_current_time - Control_interval_time; + if (Control_frametime >= CONTROL_POLL_RATE) { + Control_interval_time = Control_current_time; + if (Controller) + Controller->poll(); + } + */ +} + + +//Read the keyboard & other controllers. Fills in the specified structure. +void ReadPlayerControls(game_controls *controls) +{ + if( !Control_system_init ) + { + memset(controls,0,sizeof(game_controls)); + return; + } +//@@ Control_frametime = Frametime; +// keyboard uses game frametime +// controller uses polled control frametime + + if (!Control_poll_flag) { + memset(controls, 0, sizeof(game_controls)); + return; + } + + PollControls(); + DoWeapons(controls); // controls for weapon firing operations + DoMisc(controls); + DoMovement(controls); // controls for moving the object + + //Do any rock'n'ride updates + RNR_UpdateControllerInfo(controls); + + // only read at the specified rate to keep things consistant. manager control system timer + //Control_current_time += Frametime; + //Control_frametime = Control_current_time - Control_interval_time; + //if (Control_frametime >= CONTROL_POLL_RATE) { + // Control_interval_time = Control_current_time; + // DoMovement(controls); // controls for moving the object + // return 1; + //} + //else + // return 0; +} + +// --------------------------------------------------------------------------- +// CONTROL FUNCTIONS +// MOVEMENT +// --------------------------------------------------------------------------- + +void DoMovement(game_controls *controls) +{ + controls->sideways_thrust = 0.0f; + controls->vertical_thrust = 0.0f; + controls->forward_thrust = 0.0f; + controls->pitch_thrust = 0.0f; + controls->bank_thrust = 0.0f; + controls->heading_thrust = 0.0f; + controls->afterburn_thrust = 0.0f; + +// only read controls in game interface mode. + if (Game_interface_mode != GAME_INTERFACE) + return; + +// keyboard (always do this first.) + if (!Doing_input_message) + DoKeyboardMovement(controls); + +// controller + DoControllerMovement(controls); + +#ifndef MACINTOSH +// clip controller values + if (controls->pitch_thrust > LIMIT_PITCH) controls->pitch_thrust = LIMIT_PITCH; + if (controls->pitch_thrust < -LIMIT_PITCH) controls->pitch_thrust = -LIMIT_PITCH; + if (controls->heading_thrust > LIMIT_HEADING) controls->heading_thrust = LIMIT_HEADING; + if (controls->heading_thrust < -LIMIT_HEADING) controls->heading_thrust = -LIMIT_HEADING; + if (controls->bank_thrust > LIMIT_BANK) controls->bank_thrust = LIMIT_BANK; + if (controls->bank_thrust < -LIMIT_BANK) controls->bank_thrust = -LIMIT_BANK; +#endif + if (controls->sideways_thrust > LIMIT_HORIZ) controls->sideways_thrust = LIMIT_HORIZ; + if (controls->sideways_thrust < -LIMIT_HORIZ) controls->sideways_thrust = -LIMIT_HORIZ; + if (controls->vertical_thrust > LIMIT_VERT) controls->vertical_thrust = LIMIT_VERT; + if (controls->vertical_thrust < -LIMIT_VERT) controls->vertical_thrust = -LIMIT_VERT; + + if (controls->forward_thrust > LIMIT_FORWARD) controls->forward_thrust = LIMIT_FORWARD; + if (controls->forward_thrust < -LIMIT_FORWARD) controls->forward_thrust = -LIMIT_FORWARD; + + if (controls->afterburn_thrust > LIMIT_FORWARD) controls->afterburn_thrust = LIMIT_FORWARD; + if (controls->afterburn_thrust < -LIMIT_FORWARD) controls->afterburn_thrust = -LIMIT_FORWARD; + + mprintf_at((1,5,30, "ch:%.2f ", controls->heading_thrust)); + +// if (controls->pitch_thrust || controls->heading_thrust) +// mprintf((0, "p:%.2f h:%.2f\n", controls->pitch_thrust, controls->heading_thrust)); +} + + +void DoKeyboardMovement(game_controls *controls) +{ + float dx,dy,dz,dp,dh,db,d_afterburn; + ct_packet key_x1, key_x0, key_y1, key_y0, key_z1, key_z0; + ct_packet key_p1, key_p0, key_h1, key_h0, key_b1, key_b0; + ct_packet key_afterburn; + +// read controls + Controller->get_packet(ctfFORWARD_THRUSTKEY, &key_z1); + Controller->get_packet(ctfREVERSE_THRUSTKEY, &key_z0); + Controller->get_packet(ctfUP_THRUSTKEY, &key_y1); + Controller->get_packet(ctfDOWN_THRUSTKEY, &key_y0); + Controller->get_packet(ctfRIGHT_THRUSTKEY, &key_x1); + Controller->get_packet(ctfLEFT_THRUSTKEY, &key_x0); + Controller->get_packet(ctfPITCH_DOWNKEY, &key_p1); + Controller->get_packet(ctfPITCH_UPKEY, &key_p0); + Controller->get_packet(ctfBANK_RIGHTKEY, &key_b1); + Controller->get_packet(ctfBANK_LEFTKEY, &key_b0); + Controller->get_packet(ctfHEADING_RIGHTKEY, &key_h1); + Controller->get_packet(ctfHEADING_LEFTKEY, &key_h0); + + Controller->get_packet (ctfAFTERBURN_KEY,&key_afterburn); + +// check keyboard controls first. + +// do thrust and orientation controls. since packet contains time since last call, frametime is taken into account. +// note that the ctDigital packets are for those rare cases where a key is down, but there is no key down time +// registered as of yet, which happens on some systems. + dx = (key_x1.value - key_x0.value); + dy = (key_y1.value - key_y0.value); + dz = (key_z1.value - key_z0.value); + dp = (key_p1.value - key_p0.value); + db = (key_b0.value - key_b1.value); + dh = (key_h1.value - key_h0.value); + + d_afterburn=(key_afterburn.value); + + controls->sideways_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dx, KEY_RAMPUP_TIME, Key_ramp.x, Key_ramp.ox)/KEY_RAMPUP_TIME) : dx; + controls->vertical_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dy, KEY_RAMPUP_TIME, Key_ramp.y, Key_ramp.oy)/KEY_RAMPUP_TIME) : dy; + controls->forward_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dz, KEY_RAMPUP_TIME, Key_ramp.z, Key_ramp.oz)/KEY_RAMPUP_TIME) : dz; + + controls->afterburn_thrust += d_afterburn/Frametime; + + if (!controls->toggle_slide && !controls->toggle_bank) { + // clamp pitch ramp time to limits. + controls->pitch_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dp, KEY_RAMPUP_TIME, Key_ramp.p, Key_ramp.op)/KEY_RAMPUP_TIME) : dp; + controls->heading_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dh, KEY_RAMPUP_TIME, Key_ramp.h, Key_ramp.oh)/KEY_RAMPUP_TIME) : dh; + + } + + if (controls->toggle_slide) { + controls->sideways_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dh, KEY_RAMPUP_TIME, Key_ramp.h, Key_ramp.oh)/KEY_RAMPUP_TIME) : dh; + controls->vertical_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dp, KEY_RAMPUP_TIME, Key_ramp.p, Key_ramp.op)/KEY_RAMPUP_TIME) : dp; + } + if (controls->toggle_bank) { + controls->bank_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(dh, KEY_RAMPUP_TIME, Key_ramp.h, Key_ramp.oh)/KEY_RAMPUP_TIME) : dh; + } + else { + controls->bank_thrust += (KEY_RAMPUP_TIME) ? (ramp_control_value(db, KEY_RAMPUP_TIME, Key_ramp.b, Key_ramp.ob)/KEY_RAMPUP_TIME) : db; + } +} + + + +// VERY IMPORTANT: All the movement thrust values are set in the keyboard movement +// handler. So we adjust those values in this function based off of the remaining +// controller element stataes. + +void DoControllerMovement(game_controls *controls) +{ + ct_packet ctl_x, ctl_y, ctl_z, ctl_p, ctl_b, ctl_h; + ct_packet ctl_povl, ctl_povr, ctl_povu, ctl_povd, ctl_fb, ctl_rb; + ct_packet ctl_afterburn; + ct_packet ctl_pub, ctl_pdb, ctl_hlb, ctl_hrb, ctl_blb, ctl_brb; + +// read controls + Controller->get_packet(ctfFORWARD_THRUSTAXIS, &ctl_z); + Controller->get_packet(ctfUP_THRUSTAXIS, &ctl_y); + Controller->get_packet(ctfRIGHT_THRUSTAXIS, &ctl_x); + Controller->get_packet(ctfPITCH_DOWNAXIS, &ctl_p); + Controller->get_packet(ctfBANK_RIGHTAXIS, &ctl_b); + Controller->get_packet(ctfHEADING_RIGHTAXIS, &ctl_h); + Controller->get_packet(ctfUP_BUTTON, &ctl_povu); + Controller->get_packet(ctfDOWN_BUTTON, &ctl_povd); + Controller->get_packet(ctfRIGHT_BUTTON, &ctl_povr); + Controller->get_packet(ctfLEFT_BUTTON, &ctl_povl); + Controller->get_packet(ctfFORWARD_BUTTON, &ctl_fb); + Controller->get_packet(ctfREVERSE_BUTTON, &ctl_rb); + Controller->get_packet(ctfAFTERBURN_BUTTON, &ctl_afterburn); + Controller->get_packet(ctfHEADING_LEFTBUTTON, &ctl_hlb); + Controller->get_packet(ctfHEADING_RIGHTBUTTON, &ctl_hrb); + Controller->get_packet(ctfPITCH_UPBUTTON, &ctl_pub); + Controller->get_packet(ctfPITCH_DOWNBUTTON, &ctl_pdb); + Controller->get_packet(ctfBANK_LEFTBUTTON, &ctl_blb); + Controller->get_packet(ctfBANK_RIGHTBUTTON, &ctl_brb); + +// check for joystick movement +// mprintf((0, "p:%f h:%f b:%f\n", ctl_p.value, ctl_h.value, ctl_b.value)); +// mprintf((0, "u:%d d:%d l:%d r:%d\n", (int)ctl_povu.value, (int)ctl_povd.value, (int)ctl_povl.value, (int)ctl_povr.value)); + +// do x and y thrust + controls->sideways_thrust += ctl_x.value; + controls->vertical_thrust += -ctl_y.value; + +// invert throttle alue for real flight model controls + controls->forward_thrust += -ctl_z.value; + +// do banking + if (controls->toggle_bank) { + controls->bank_thrust += -ctl_h.value; + } + else { + controls->bank_thrust += -ctl_b.value; + } + +// do slide if toggled + if (controls->toggle_slide) { + controls->sideways_thrust += ctl_h.value; + if ((ctl_p.flags & CTPK_MOUSE) && !FindArg("-invertmouseslide")){ + controls->vertical_thrust += (-ctl_p.value); + } + else { + controls->vertical_thrust += ctl_p.value; + } + } + +// do standard pitch and heading. + if (!controls->toggle_slide && !controls->toggle_bank) { + controls->pitch_thrust += -ctl_p.value; + controls->heading_thrust += ctl_h.value; + } + +// do afterburn button control + if (!controls->afterburn_thrust) { +// mprintf((0, "aft=%.4f\n", ctl_afterburn.value)); + controls->afterburn_thrust = ((ctl_afterburn.value)/Frametime); + } + +#ifdef MACINTOSH +// do sidways thrust based off of button values. + controls->sideways_thrust += ctl_povr.value; + controls->sideways_thrust -= ctl_povl.value; + +// do vertical thrust based off of button values. + controls->vertical_thrust += ctl_povu.value; + controls->vertical_thrust -= ctl_povd.value; +// do forward thrust based off of button values. + controls->forward_thrust += ctl_fb.value; + controls->forward_thrust -= ctl_rb.value; +// do button banking + if (controls->toggle_bank) { + controls->bank_thrust += ctl_hlb.value; + controls->bank_thrust -= ctl_hrb.value; + } else { + controls->bank_thrust += ctl_blb.value; + controls->bank_thrust -= ctl_brb.value; + } + +// do slide if toggled + if (controls->toggle_slide) { + controls->sideways_thrust += ctl_hrb.value; + controls->sideways_thrust -= ctl_hlb.value; + controls->vertical_thrust += ctl_pub.value; + controls->vertical_thrust -= ctl_pdb.value; + } +// do standard pitch and heading. + if (!controls->toggle_slide && !controls->toggle_bank) { + controls->heading_thrust -= ctl_hlb.value; + controls->heading_thrust += ctl_hrb.value; + controls->pitch_thrust -= ctl_pub.value; + controls->pitch_thrust += ctl_pdb.value; + } +#else +// do button heading + if (ctl_hlb.value) + controls->heading_thrust += (-1.0f); + if (ctl_hrb.value) + controls->heading_thrust += (1.0f); + +// do button pitch + if (ctl_pub.value) + controls->pitch_thrust += (-1.0f); + if (ctl_pdb.value) + controls->pitch_thrust += (1.0f); + +// do forward thrust based off of button values. + controls->forward_thrust += ((ctl_fb.value - ctl_rb.value)/Frametime); + +// do button banking + if (ctl_blb.value) + controls->bank_thrust += (1.0f); + if (ctl_brb.value) + controls->bank_thrust += (-1.0f); + +// do button sliding. use control frametime to set sliding times per frame. +// note that vertical and sideways thrusts are dependent on what the keyboard controller +// set these values to. so we save our own slidetimes. + if (ctl_povu.value) { + controls->vertical_thrust += (1.0f); + } + if (ctl_povd.value) { + controls->vertical_thrust -= (1.0f); + } + if (ctl_povr.value) { + controls->sideways_thrust += (1.0f); + } + if (ctl_povl.value) { + controls->sideways_thrust -= (1.0f); + } +#endif +} + + + +// --------------------------------------------------------------------------- +// CONTROL FUNCTIONS +// WEAPONS +// --------------------------------------------------------------------------- + +void DoWeapons(game_controls *controls) +{ +// reset some values + controls->fire_primary_down_count = 0; + controls->fire_primary_down_time = 0; + controls->fire_primary_down_state = false; + controls->fire_secondary_down_count = 0; + controls->fire_secondary_down_time = 0; + controls->fire_flare_down_count = 0; + +// only read controls in game interface mode. + if (Game_interface_mode != GAME_INTERFACE) + return; + +// Reads in values to Controls structure + if (!Doing_input_message) + DoKeyboardWeapons(controls); + + DoControllerWeapons(controls); +} + + +void DoKeyboardWeapons(game_controls *controls) +{ + ct_packet fire_primary_key_time; + ct_packet fire_secondary_key_time; + ct_packet fire_flare_key_count; + ct_packet automap_key; + ct_packet cycle_prim, cycle_sec; + int i; + +// read controls + Controller->get_packet(ctfFIREPRIMARY_KEY, &fire_primary_key_time); + Controller->get_packet(ctfFIRESECONDARY_KEY, &fire_secondary_key_time); + Controller->get_packet(ctfFIREFLARE_KEY, &fire_flare_key_count); + Controller->get_packet(ctfAUTOMAP_KEY, &automap_key); + Controller->get_packet(ctfWPNSEL_PCYCLEKEY, &cycle_prim); + Controller->get_packet(ctfWPNSEL_SCYCLEKEY, &cycle_sec); + +// weapon fire + if (fire_primary_key_time.value > 0) { + controls->fire_primary_down_state = true; + controls->fire_primary_down_count = 1; + controls->fire_primary_down_time = fire_primary_key_time.value; + } + + if (fire_secondary_key_time.value > 0) { + controls->fire_secondary_down_count = 1; + controls->fire_secondary_down_time = fire_secondary_key_time.value; + } + + //flare + controls->fire_flare_down_count = fire_flare_key_count.value; + +// automap + if (automap_key.value) { + Game_interface_mode = GAME_TELCOM_AUTOMAP; + } + +// do cycling weapons + for (i = 0; i < (int)cycle_prim.value; i++) + SwitchPlayerWeapon(PW_PRIMARY); + + for (i = 0; i < (int)cycle_sec.value; i++) + SwitchPlayerWeapon(PW_SECONDARY); +} + + +void DoControllerWeapons(game_controls *controls) +{ + ct_packet fire_primary_count, fire_primary_time; + ct_packet fire_secondary_count, fire_secondary_time; + ct_packet fire_flare_count; + ct_packet cycle_prim, cycle_sec; + ct_packet automap_key; + int i; + +// read controls + Controller->get_packet(ctfFIREPRIMARY_BUTTON, &fire_primary_time); + Controller->get_packet(ctfFIREPRIMARY_BUTTON, &fire_primary_count, ctDownCount); + Controller->get_packet(ctfFIRESECONDARY_BUTTON, &fire_secondary_time); + Controller->get_packet(ctfFIRESECONDARY_BUTTON, &fire_secondary_count, ctDownCount); + Controller->get_packet(ctfFIREFLARE_BUTTON, &fire_flare_count, ctDownCount); + Controller->get_packet(ctfWPNSEL_PCYCLEBTN, &cycle_prim); + Controller->get_packet(ctfWPNSEL_SCYCLEBTN, &cycle_sec); + Controller->get_packet(ctfAUTOMAP_BUTTON, &automap_key); + +// weapon fire + if (fire_primary_count.value > 0 || fire_primary_time.value) { + controls->fire_primary_down_state = true; + controls->fire_primary_down_count = (int)fire_primary_count.value; + controls->fire_primary_down_time = fire_primary_time.value; + } + if (fire_secondary_count.value > 0 || fire_secondary_time.value) { +#ifndef MACINTOSH + controls->fire_secondary_down_count = (int)fire_secondary_count.value; +#else + controls->fire_secondary_down_count = 1; //DAJ this makes the guided second fire go back to the ship view +#endif + controls->fire_secondary_down_time = fire_secondary_time.value; + } + + //Flare + if (fire_flare_count.value > 0) + controls->fire_flare_down_count = (int)fire_flare_count.value; + +// automap + if (automap_key.value) { + Game_interface_mode = GAME_TELCOM_AUTOMAP; + } + +// do cycling weapons + for (i = 0; i < (int)cycle_prim.value; i++) + SwitchPlayerWeapon(PW_PRIMARY); + + for (i = 0; i < (int)cycle_sec.value; i++) + SwitchPlayerWeapon(PW_SECONDARY); +} + + +void DoMisc(game_controls *controls) +{ + controls->toggle_slide = false; + controls->toggle_bank = false; + controls->rearview_down_count = 0; + controls->rearview_down_state = false; + + if (!Doing_input_message) + DoKeyboardMisc(controls); + DoControllerMisc(controls); + +//@@ if (!toggle_rearview_switch.value && Rearview_key_down) { +//@@ Rearview_key_down = false; +//@@ ToggleRearView(0); +//@@ } +//@@ else if (toggle_rearview_switch.value && !Rearview_key_down) { +//@@ Rearview_key_down = true; +//@@ ToggleRearView(1); +//@@ } +//@@ else if (!Rearview_key_down) { +//@@ for (i = 0; i < (int)toggle_rearview.value; i++) +//@@ ToggleRearView(); +//@@ } +} + +//use the currently selected inventory item +bool UseInventoryItem(); +//use the currently selected countermeasure +bool UseCountermeasure(); + +//Inventory/CounterMeasure states +void DoKeyboardMisc(game_controls *controls) +{ + static int energy_to_shields_id = -1; + int type,id, i; + + ct_packet prev_inv_key,next_inv_key; + ct_packet prev_cntm_key,next_cntm_key; + ct_packet use_inv_key,use_inv_key_digital,use_cntm_key, use_taunt[4]; + ct_packet toggle_headlight; + ct_packet toggle_rearview, toggle_rearview_switch; + ct_packet key_slide1, key_bank; + +// read controls + Controller->get_packet(ctfPREV_INVKEY, &prev_inv_key); + Controller->get_packet(ctfNEXT_INVKEY, &next_inv_key); + Controller->get_packet(ctfPREV_CNTMSKEY, &prev_cntm_key); + Controller->get_packet(ctfNEXT_CNTMSKEY, &next_cntm_key); + Controller->get_packet(ctfINV_USEKEY, &use_inv_key); + Controller->get_packet(ctfINV_USEKEY, &use_inv_key_digital, ctDigital); + Controller->get_packet(ctfCNTMS_USEKEY, &use_cntm_key); + Controller->get_packet(ctfHEADLIGHT_KEY, &toggle_headlight); + Controller->get_packet(ctfREARVIEW_KEY, &toggle_rearview); + Controller->get_packet(ctfREARVIEW_KEY, &toggle_rearview_switch, ctDigital); + Controller->get_packet(ctfAUDIOTAUNT1_KEY, &use_taunt[0]); + Controller->get_packet(ctfAUDIOTAUNT2_KEY, &use_taunt[1]); + Controller->get_packet(ctfAUDIOTAUNT3_KEY, &use_taunt[2]); + Controller->get_packet(ctfAUDIOTAUNT4_KEY, &use_taunt[3]); + + Controller->get_packet(ctfTOGGLE_SLIDEKEY, &key_slide1); + Controller->get_packet(ctfTOGGLE_BANKKEY, &key_bank); + +// check modifiers like toggles + if (key_slide1.value) { + controls->toggle_slide = true; + } + if (key_bank.value) { + controls->toggle_bank = true; + } + +// downcount, only once for audiotaunts + if (use_taunt[0].value != 0.0f) { + MultiSendRequestPlayTaunt(0); + } + if (use_taunt[1].value != 0.0f) { + MultiSendRequestPlayTaunt(1); + } + if (use_taunt[2].value != 0.0f) { + MultiSendRequestPlayTaunt(2); + } + if (use_taunt[3].value != 0.0f) { + MultiSendRequestPlayTaunt(3); + } + +// these are all down count values (so do operation X times) + for (i = 0; i < (int)prev_cntm_key.value; i++) + CounterMeasuresSwitch(false); + + for (i = 0; i < (int)next_cntm_key.value; i++) + CounterMeasuresSwitch(true); + + for (i = 0; i < (int)use_cntm_key.value; i++) + UseCountermeasure(); + + for (i = 0; i < (int)prev_inv_key.value; i++) + { + if (Players[Player_num].inventory.Size() == 1) { + if (Players[Player_num].inventory.GetPosName()) + AddHUDMessage(TXT_WPNSELECT,Players[Player_num].inventory.GetPosName()); + } + InventorySwitch(false); + } + + for (i = 0; i < (int)next_inv_key.value; i++) + { + if (Players[Player_num].inventory.Size() == 1) { + if (Players[Player_num].inventory.GetPosName()) + AddHUDMessage(TXT_WPNSELECT,Players[Player_num].inventory.GetPosName()); + } + InventorySwitch(true); + } + +// do energy to shield hack + if(energy_to_shields_id==-1) { + energy_to_shields_id = FindObjectIDName("Converter"); + } + Players[Player_num].inventory.GetPosTypeID(type,id); + if (use_inv_key_digital.value) { + if(type==OBJ_POWERUP && id==energy_to_shields_id) { + //the player has an energy->shields converter + DoEnergyToShields(Player_num); + } + } + +// do standard inventory use. + for (i = 0; i < (int)use_inv_key.value; i++) + { + //Inventory use is special cased for Energy->Shields converter + if(type==OBJ_POWERUP && id==energy_to_shields_id){ + continue; + } + else { + //handle use inventory keypress + UseInventoryItem(); + } + } + + for (i = 0; i < (int)toggle_headlight.value; i++) + ToggleHeadlightControlState(); + +// rear view toggling. + controls->rearview_down_count += toggle_rearview.value; + controls->rearview_down_state = toggle_rearview_switch.value ? true : false; +} + + +void DoControllerMisc(game_controls *controls) +{ + static int energy_to_shields_id = -1; + int i; + + ct_packet prev_inv,next_inv; + ct_packet prev_cntm,next_cntm; + ct_packet use_inv,use_cntm; + ct_packet toggle_headlight; + ct_packet toggle_rearview, toggle_rearview_switch; + ct_packet ctl_bank, ctl_slide, use_taunt[4]; + +// read controls + Controller->get_packet(ctfPREV_INVBTN, &prev_inv); + Controller->get_packet(ctfNEXT_INVBTN, &next_inv); + Controller->get_packet(ctfPREV_CNTMSBTN, &prev_cntm); + Controller->get_packet(ctfNEXT_CNTMSBTN, &next_cntm); + Controller->get_packet(ctfINV_USEBTN, &use_inv); + Controller->get_packet(ctfCNTMS_USEBTN, &use_cntm); + Controller->get_packet(ctfHEADLIGHT_BUTTON, &toggle_headlight); + Controller->get_packet(ctfREARVIEW_BTN, &toggle_rearview); + Controller->get_packet(ctfREARVIEW_BTN, &toggle_rearview_switch, ctDigital); + Controller->get_packet(ctfAUDIOTAUNT1_BTN, &use_taunt[0]); + Controller->get_packet(ctfAUDIOTAUNT2_BTN, &use_taunt[1]); + Controller->get_packet(ctfAUDIOTAUNT3_BTN, &use_taunt[2]); + Controller->get_packet(ctfAUDIOTAUNT4_BTN, &use_taunt[3]); + + Controller->get_packet(ctfTOGGLE_SLIDEBUTTON, &ctl_slide); + Controller->get_packet(ctfTOGGLE_BANKBUTTON, &ctl_bank); + +// check modifiers like toggles + if (ctl_slide.value) { + controls->toggle_slide = true; + } + if (ctl_bank.value) { + controls->toggle_bank = true; + } + +// downcount, only once for audiotaunts + if (use_taunt[0].value != 0.0f) { + MultiSendRequestPlayTaunt(0); + } + if (use_taunt[1].value != 0.0f) { + MultiSendRequestPlayTaunt(1); + } + if (use_taunt[2].value != 0.0f) { + MultiSendRequestPlayTaunt(2); + } + if (use_taunt[3].value != 0.0f) { + MultiSendRequestPlayTaunt(3); + } + +// these are all down count values (so do operation X times) + for (i = 0; i < (int)prev_cntm.value; i++) + CounterMeasuresSwitch(false); + + for (i = 0; i < (int)next_cntm.value; i++) + CounterMeasuresSwitch(true); + + for (i = 0; i < (int)use_cntm.value; i++) + UseCountermeasure(); + + for (i = 0; i < (int)prev_inv.value; i++) + { + if (Players[Player_num].inventory.Size() == 1) { + if (Players[Player_num].inventory.GetPosName()) + AddHUDMessage(TXT_WPNSELECT,Players[Player_num].inventory.GetPosName()); + } + InventorySwitch(false); + } + + for (i = 0; i < (int)next_inv.value; i++) + { + if (Players[Player_num].inventory.Size() == 1) { + if (Players[Player_num].inventory.GetPosName()) + AddHUDMessage(TXT_WPNSELECT,Players[Player_num].inventory.GetPosName()); + } + InventorySwitch(true); + } + + for (i = 0; i < (int)use_inv.value; i++) + { + int type,id; + + if(energy_to_shields_id==-1) + energy_to_shields_id = FindObjectIDName("Converter"); + + //Inventory use is special cased for Energy->Shields converter + Players[Player_num].inventory.GetPosTypeID(type,id); + if(type==OBJ_POWERUP && id==energy_to_shields_id){ + //the player has an energy->shields converter + DoEnergyToShields(Player_num); + }else { + //handle use inventory keypress + UseInventoryItem(); + } + } + + for (i = 0; i < (int)toggle_headlight.value; i++) + ToggleHeadlightControlState(); + +// rear view toggling. + controls->rearview_down_count += toggle_rearview.value; + if (!controls->rearview_down_state) + controls->rearview_down_state = toggle_rearview_switch.value ? true : false; +} + + +// sets the internal controller config to the current pilot's configuration. +void LoadControlConfig(pilot *plt) +{ + int j; + if (!plt) plt = &Current_pilot; + + for (int i=0;i < NUM_CONTROLLER_FUNCTIONS; i++) + { + int id = Current_pilot.controls[i].id; + ct_type type[2]; + ct_config_data ccfgdata; + ubyte flags[2]; + + type[0] = plt->controls[i].type[0]; + type[1] = plt->controls[i].type[1]; + ccfgdata = plt->controls[i].value; + flags[0] = plt->controls[i].flags[0]; + flags[1] = plt->controls[i].flags[1]; + + Controller->set_controller_function(id, type, ccfgdata, flags); + } +#ifdef MACINTOSH + Controller->set_controller_deadzone(1, plt->mouse_sensitivity[0]); +#else + for (j= 0; j < N_MOUSE_AXIS; j++) + { + Controller->set_axis_sensitivity(ctMouseAxis, j+1, plt->mouse_sensitivity[j]); + } +#endif + for (j= 0; j < N_JOY_AXIS; j++) + { + Controller->set_axis_sensitivity(ctAxis, j+1, plt->joy_sensitivity[j]); + } + + Key_ramp_speed = plt->key_ramping; +} + + +// saves the internal controller config of current pilot. if the controller changes, then calling +// LoadControlConfig will restore ONLY VALID configurations. +void SaveControlConfig(pilot *plt) +{ + int j; + if (!plt) plt = &Current_pilot; + + for (int i=0;i < NUM_CONTROLLER_FUNCTIONS; i++) + { + ct_type ctype[2]; + ct_config_data ccfgdata; + ubyte flags[2]; + Controller->get_controller_function(Controller_needs[i].id, ctype, &ccfgdata, flags); + + plt->controls[i].id = Controller_needs[i].id; + plt->controls[i].type[0] = ctype[0]; + plt->controls[i].type[1] = ctype[1]; + plt->controls[i].value = ccfgdata; + plt->controls[i].flags[0] = flags[0]; + plt->controls[i].flags[1] = flags[1]; + } + +#ifdef MACINTOSH + plt->mouse_sensitivity[0] = Controller->get_controller_deadzone(2); +#else + for (j= 0; j < N_MOUSE_AXIS; j++) + { + float sens = Controller->get_axis_sensitivity(ctMouseAxis, j+1); + plt->mouse_sensitivity[j] = sens; + } +#endif + for (j= 0; j < N_JOY_AXIS; j++) + { + float sens = Controller->get_axis_sensitivity(ctAxis, j+1); + plt->joy_sensitivity[j] = sens; + } + + plt->key_ramping = Key_ramp_speed; +} + + +void ToggleHeadlightControlState () +{ + if(Players[Player_num].flags&PLAYER_FLAGS_HEADLIGHT_STOLEN) + { + //player has no headlight right now + AddHUDMessage (TXT_DONTHAVEHEADLIGHT); + return; + } + + if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT) + Players[Player_num].flags &=~PLAYER_FLAGS_HEADLIGHT; + else + Players[Player_num].flags |=PLAYER_FLAGS_HEADLIGHT; + + Sound_system.Play2dSound(SOUND_HEADLIGHT); + AddHUDMessage (TXT_HEADLIGHTTURNED,Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT?TXT_STRING_ON:TXT_STRING_OFF); +} + + diff --git a/Descent3/CtlCfgElem.cpp b/Descent3/CtlCfgElem.cpp new file mode 100644 index 000000000..becba1896 --- /dev/null +++ b/Descent3/CtlCfgElem.cpp @@ -0,0 +1,934 @@ +/* + * $Logfile: /DescentIII/main/CtlCfgElem.cpp $ + * $Revision: 26 $ + * $Date: 3/20/00 12:03p $ + * $Author: Matt $ + * + * Control config element gadgets + * + * $Log: /DescentIII/main/CtlCfgElem.cpp $ + * + * 26 3/20/00 12:03p Matt + * Merge of Duane's post-1.3 changes. + * Added Mac international keys (Mac only) + * + * 25 10/22/99 10:47a Matt + * Mac merge + * + * 24 7/30/99 1:05p Samir + * read POVs before buttons since most controllers map hat positions to + * buttons as well. + * + * 23 7/16/99 11:15a Samir + * multiple hat support + * + * 22 6/11/99 1:15a Samir + * localization issues. + * + * 21 5/20/99 9:11p Samir + * no bind '-' and '=' keys. + * + * 20 5/06/99 1:40a Samir + * adjusted some text. + * + * 19 4/29/99 2:59p Samir + * added help and made CTRL-C clear for controller screens. + * + * 18 4/29/99 2:23a Samir + * moved binding text functions to wincontroller.cpp and new text for + * multiple joysticks. + * + * 17 4/15/99 1:38a Jeff + * changes for linux compile + * + * 16 4/14/99 12:35p Samir + * localization issues. + * + * 15 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 14 3/23/99 9:04p Samir + * moved mouse binding text to mouse library. + * + * 13 3/09/99 6:26p Samir + * flush mouse queue after configuring button. + * + * 12 3/05/99 4:34p Samir + * multiplayer bug in controller config. + * + * 11 3/02/99 1:17p Samir + * resume and suspend controls for config instead of directly going + * through controller. this is done so mask_controllers gets called with + * the latest Current_pillot.read_controller values... + * + * 10 2/26/99 2:09a Samir + * added '?' button. + * + * 9 2/21/99 6:36p Samir + * focusing changes and key input changes to ui., + * + * 8 2/16/99 12:07p Samir + * redid controller config with new ui. + * + * 7 1/28/99 3:58p Jeff + * localization update + * + * 6 11/30/98 4:54p Samir + * added rear view config item. + * + * 5 10/23/98 12:51p Samir + * bail out of config if server says so: note there still is a bug with + * this system. + * + * 4 10/18/98 1:07p Samir + * tweaked user interface for controller config. + * + * 3 10/17/98 7:31p Samir + * added invertible axes + * + * 2 9/30/98 4:37p Samir + * 'incremental checkin' + * + * 1 9/28/98 3:47p Samir + * initial revision. + * + * $NoKeywords: $ + */ + + +#include "CtlCfgElem.h" +#include "descent.h" + + +#include "Macros.h" +#include "ddio.h" +#include "application.h" +#include "renderer.h" +#include "stringtable.h" +#include "gamefont.h" +#include "localization.h" + +#include +#include "joystick.h" + + +// all controller binding texts +char Ctltext_KeyBindings[][16] = { + "","","","","","","","","","","","","", + "", + "bspc\0\0\0\0\0\0", + "tab\0\0\0\0\0\0", + "q","w","e","r","t","y","u","i","o","p","[","]", + "enter\0\0\0\0\0", +#ifdef MACINTOSH + "ctrl\0\0\0\0\0", +#else + "lctrl\0\0\0\0\0", +#endif + "a","s","d","f","g","h","j","k","l",";","'","`", +#ifdef MACINTOSH + "shift\0\0\0\0\0", +#else + "lshft\0\0\0\0\0", +#endif + "\\","z","x","c","v","b","n","m",",",".","/", + "rshft\0\0\0\0\0", + "pad*\0\0\0\0\0", +#ifdef MACINTOSH + "opt\0\0\0\0\0", +#else + "lalt\0\0\0\0\0", +#endif + "spc\0\0\0\0\0", + "caps\0\0\0\0\0", + "","","","","","","","","","", + "num\0\0\0\0\0", + "sclk\0\0\0\0\0", + "pad7\0\0\0\0\0", + "pad8\0\0\0\0\0", + "pad9\0\0\0\0\0", + "pad-\0\0\0\0\0", + "pad4\0\0\0\0\0", + "pad5\0\0\0\0\0", + "pad6\0\0\0\0\0", + "pad+\0\0\0\0\0", + "pad1\0\0\0\0\0", + "pad2\0\0\0\0\0", + "pad3\0\0\0\0\0", + "pad0\0\0\0\0\0", + "pad.\0\0\0\0\0", + "","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","","","", + "padƒ\0\0\0\0\0\0", +#ifdef MACINTOSH + "ctrl\0\0\0\0\0", +#else + "rctrl\0\0\0\0\0", +#endif + "","","","","","","","","","","","","","","","","","","","","","","", + "pad/\0\0\0\0\0\0", + "","", +#ifdef MACINTOSH + "opt\0\0\0\0\0\0", +#else + "ralt\0\0\0\0\0\0", +#endif + "","","","","","","","","","","","","","", + "home\0\0\0\0\0\0", + "up\0\0\0\0\0\0\0", + "pgup\0\0\0\0\0\0", + "", + "left\0\0\0\0\0\0", + "", + "right\0\0\0\0\0\0", + "", + "end\0\0\0\0\0\0\0", + "down\0\0\0\0\0\0", + "pgdn\0\0\0\0\0\0", + "ins\0\0\0\0\0\0", + "del\0\0\0\0\0\0", + "","","","","","","","","","","","", +#ifdef MACINTOSH + "cmd\0\0\0\0\0\0", +#else + "", +#endif + "","","","", + "","","","","","","","","","","","","","","","","","","","", + "","","","","","","" +}; +static short key_binding_indices[] = { + KEY_BACKSP, KEY_TAB, KEY_ENTER, KEY_LCTRL, KEY_LSHIFT, KEY_RSHIFT,KEY_PADMULTIPLY, KEY_LALT, + KEY_SPACEBAR, KEY_CAPSLOCK, 0x45, KEY_SCROLLOCK, KEY_PAD7, KEY_PAD8, KEY_PAD9, KEY_PADMINUS, KEY_PAD4, + KEY_PAD5, KEY_PAD6, KEY_PADPLUS, KEY_PAD1, KEY_PAD2, KEY_PAD3, KEY_PAD0, KEY_PADPERIOD, KEY_PADENTER, + KEY_RCTRL, KEY_PADDIVIDE, KEY_RALT, KEY_HOME, KEY_UP, KEY_PAGEUP, KEY_LEFT, KEY_RIGHT, KEY_END, KEY_DOWN, + KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, +#ifdef MACINTOSH + KEY_CMD, KEY_LBRACKET, KEY_RBRACKET, KEY_BACKSLASH, KEY_PERIOD, KEY_SLASH, //DAJ for mac international +#endif + 0xff +}; + + +#define NUM_KEYBINDSTRINGS (sizeof(Ctltext_KeyBindings)/sizeof(char*)) + +#define F_ELEM_HILITE 0x1 +#define F_ELEM_ACTIVE 0x2 +#define F_ELEM_INVERTED 0x4 + +#define CFGELEM_BTN_X 132 + +/* +$$TABLE_GAMEFILE "SmallButton.ogf" +$$TABLE_GAMEFILE "SmallButtonLit.ogf" +$$TABLE_GAMEFILE "TinyButton.ogf" +$$TABLE_GAMEFILE "TinyButtonLit.ogf" +*/ +#define NEWUI_BTN_FILE "SmallButton.ogf" +#define NEWUI_BTNLIT_FILE "SmallButtonLit.ogf" +#define NEWUI_TINYBTN_FILE "TinyButton.ogf" +#define NEWUI_TINYBTNLIT_FILE "TinyButtonLit.ogf" + +extern char Ctltext_AxisBindings[][16]; +extern char Ctltext_PovBindings[][16]; +extern char Ctltext_MseBtnBindings[][32]; +extern char Ctltext_MseAxisBindings[][32]; + + +void Localize_ctl_bindings() +{ + char **strtable; + int n_strings, i; + +// keyboard translations. +// no need to do for english! + if (Localization_GetLanguage() == LANGUAGE_ENGLISH) + return; + + if (CreateStringTable("bindkey.str",&strtable,&n_strings)) { + i = 0; + while (key_binding_indices[i] != 0xff) + { + if (i >= n_strings) { + break; + } + strcpy(Ctltext_KeyBindings[key_binding_indices[i]], strtable[i]); + i++; + } + DestroyStringTable(strtable,n_strings); + } + +// mouse translations. + if (CreateStringTable("bindmse.str",&strtable,&n_strings)) { + for (i = 0; i < 6; i++) + { + if (i >= n_strings) { + break; + } + strcpy(Ctltext_MseBtnBindings[i], strtable[i]); + } + for (i = 0; i < 3; i++) + { + if ((i+6) >= n_strings) { + break; + } + strcpy(Ctltext_MseAxisBindings[i], strtable[i+6]); + } + DestroyStringTable(strtable,n_strings); + } + +// joystick translations. + if (CreateStringTable("bindjoy.str",&strtable,&n_strings)) { + for (i = 0; i < 6; i++) + { + if (i >= n_strings) { + break; + } + strcpy(Ctltext_AxisBindings[i+1], strtable[i]); + } + for (i = 0; i < 4; i++) + { + if ((i+6) >= n_strings) { + break; + } + strcpy(Ctltext_PovBindings[i+1], strtable[i+6]); + } + DestroyStringTable(strtable,n_strings); + } +} + + +////////////////////////////////////////////////////////////////////////////// +const char *cfg_binding_text(ct_type ctype, ubyte ctrl, ubyte binding) +{ + const char *str; + + if (ctrl == NULL_CONTROLLER) { + return NULL; + } + + switch (ctype) + { + case ctKey: + { + ASSERT(binding < NUM_KEYBINDSTRINGS); + str = Ctltext_KeyBindings[binding]; + break; + } + + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + case ctMouseButton: + case ctButton: + case ctMouseAxis: + case ctAxis: + str = Controller->get_binding_text(ctype, ctrl, binding); + break; + + default: + if (ctype == ctNone) { + Int3(); + } + return (""); + } + + return str; +} + + + +////////////////////////////////////////////////////////////////////////////// +// +class cfg_element_ui: public newuiMessageBox +{ + ubyte m_element; // element passed and returned. + ubyte m_controller; // controller. + sbyte m_alpha; // used for fx. + ct_type m_type; + + +public: + void Create(const char *title, ct_type type, ubyte controller, ubyte element); + ubyte GetElement() const { return m_element; }; + ubyte GetController() const { return m_controller; }; + ct_type GetType() const { return m_type; }; + int DoUI(); + +protected: + virtual void OnDraw(); +}; + + + +////////////////////////////////////////////////////////////////////////////// +// + +UIBitmapItem *cfg_element::m_btn_bmp_lit = NULL; +UIBitmapItem *cfg_element::m_btn_bmp = NULL; +UIBitmapItem *cfg_element::m_xbtn_bmp_lit = NULL; +UIBitmapItem *cfg_element::m_xbtn_bmp = NULL; +short cfg_element::m_count = 0; + +const char *cfg_binding_text(ct_type ctype, ubyte binding); +bool key_cfg_element(ubyte *element, ubyte *flags); + +////////////////////////////////////////////////////////////////////////////// +// + +void cfg_element::Create(UIWindow *wnd, int str_i, int x, int y, int fnid, int id) +{ + m_title = TXT(str_i); + m_fnid = (sbyte)fnid; + m_slot = 0; + m_curslot = -1; + m_flags = 0; + m_slot_alpha = 255; + m_blink_state = 0; + + + if (cfg_element::m_count == 0) { + cfg_element::m_btn_bmp_lit = newui_LoadBitmap(IGNORE_TABLE(NEWUI_BTNLIT_FILE)); + cfg_element::m_btn_bmp = newui_LoadBitmap(IGNORE_TABLE(NEWUI_BTN_FILE)); + cfg_element::m_xbtn_bmp_lit = newui_LoadBitmap(IGNORE_TABLE(NEWUI_TINYBTNLIT_FILE)); + cfg_element::m_xbtn_bmp = newui_LoadBitmap(IGNORE_TABLE(NEWUI_TINYBTN_FILE)); + } + cfg_element::m_count++; + + UIGadget::Create(wnd, id, x, y, 10, 10, 0); + + if (id == -1) { + UIGadget::Disable(); + } +} + + +void cfg_element::OnFormat() +{ + UITextItem title(MONITOR9_NEWUI_FONT, m_title, NEWUI_MONITORFONT_COLOR); + m_W = CFGELEM_BTN_X+ (N_CFGELEM_SLOTS*(m_btn_bmp->width()+2)); + m_H = title.height(); +} + + +void cfg_element::OnDraw() +{ +// draw text, then buttons, then... + UITextItem title(MONITOR9_NEWUI_FONT, m_title, NEWUI_MONITORFONT_COLOR); + + int x = 0, i; + + if (m_infocus) { + title.set_alpha(m_slot_alpha); + } + + if (m_fnid != -1) { + UITextItem text(GADGET9_NEWUI_FONT, "?", NEWUI_GADGETFONT_COLOR); + if (m_curslot == CFGELEM_SLOT_CLEAR && CHECK_FLAG(m_flags, F_ELEM_HILITE)) { + m_xbtn_bmp_lit->draw(x,0); + } + else { + m_xbtn_bmp->draw(x,0); + } + text.draw(x+(m_xbtn_bmp->width()-text.width())/2, 1+(m_xbtn_bmp->height()-text.height())/2); + x+= 28; + } + + title.draw(x,0); + + if (m_fnid != -1) { + ct_type ctype[CTLBINDS_PER_FUNC]; + ubyte cfgflags[CTLBINDS_PER_FUNC]; + ct_config_data cfgdata; + tCfgDataParts cfgparts; + + x = CFGELEM_BTN_X; + + Controller->get_controller_function(m_fnid, ctype, &cfgdata, cfgflags); + parse_config_data(&cfgparts, ctype[0], ctype[1], cfgdata); + + for (i = 0; i < N_CFGELEM_SLOTS; i++) + { + const char *txt; + ubyte one_binding = (i==0) ? cfgparts.bind_0 : cfgparts.bind_1; + ubyte one_ctrlbind = (i==0) ? cfgparts.ctrl_0 : cfgparts.ctrl_1; + + txt = cfg_binding_text(ctype[i], one_ctrlbind, one_binding); + + // draw button + if (CHECK_FLAG(m_flags, F_ELEM_HILITE) && m_curslot == i) { + m_btn_bmp_lit->draw(x,0); + } + else { + m_btn_bmp->draw(x, 0); + } + + // draw text + if (txt) { + UITextItem text(GADGET9_NEWUI_FONT, txt, NEWUI_GADGETFONT_COLOR); + + if (m_infocus) { + if (m_curslot == i) { + text.set_alpha(m_slot_alpha); + } + } + + text.draw(x+(m_btn_bmp->width()-text.width())/2, (m_btn_bmp->height()-text.height())/2); + } + + x += (m_btn_bmp->width()+2); + } + + m_slot_alpha += m_blink_state; + if (m_slot_alpha == 25) { + m_blink_state = 25; + } + else if (m_slot_alpha == 250) { + m_blink_state = -25; + } + } +} + + +void cfg_element::OnKeyDown(int key) +{ + if (key == KEY_RIGHT && m_curslot < (N_CFGELEM_SLOTS-1)) { + m_curslot++; + } + else if (key == KEY_LEFT && m_curslot > 0) { + m_curslot--; + } + else if (key == (KEY_CTRLED+KEY_C)) { + // clear binding. + tCfgDataParts cfgparts; + ct_type ctype_fn[CTLBINDS_PER_FUNC]; + ubyte cfgflags_fn[CTLBINDS_PER_FUNC]; + ct_config_data ccfgdata_fn; + + Controller->get_controller_function(m_fnid, ctype_fn, &ccfgdata_fn, cfgflags_fn); + parse_config_data(&cfgparts, ctype_fn[0], ctype_fn[1], ccfgdata_fn); + if (m_curslot == 0) { + cfgparts.bind_0 = NULL_BINDING; + cfgparts.ctrl_0 = NULL_CONTROLLER; + } + else if (m_curslot == 1) { + cfgparts.bind_1 = NULL_BINDING; + cfgparts.ctrl_1 = NULL_CONTROLLER; + } + ccfgdata_fn = unify_config_data(&cfgparts); + Controller->set_controller_function(m_fnid, ctype_fn, ccfgdata_fn, cfgflags_fn); + + } + else if (key == (KEY_SHIFTED+KEY_SLASH)) { + m_slot = CFGELEM_SLOT_CLEAR; + OnSelect(); + } +} + + +void cfg_element::OnMouseBtnDown(int btn) +{ + if (btn == UILMSEBTN) { + int gadmx = SCREEN_TO_GAD_X(this, UI_input.mx), gadmy = SCREEN_TO_GAD_Y(this, UI_input.my); + int x = 0; + sbyte curslot; + + // determine what is the current slot + curslot = -1; + + if (PT_IN_RECT(gadmx, gadmy, x, 0, x+m_xbtn_bmp->width(), m_xbtn_bmp->height())) { + curslot = CFGELEM_SLOT_CLEAR; + } + + x = CFGELEM_BTN_X; + if (PT_IN_RECT(gadmx, gadmy, x, 0, x+m_btn_bmp->width(), m_btn_bmp->height())) { + curslot = 0; + } + + x += m_btn_bmp->width()+2; + if (PT_IN_RECT(gadmx, gadmy, x, 0, x+m_btn_bmp->width(), m_btn_bmp->height())) { + curslot = 1; + } + + // if not active and mouse button is down in a box, flag as active if not already active. + if (!CHECK_FLAG(m_flags, F_ELEM_ACTIVE)) { + m_slot = -1; + if (curslot != -1) { + m_flags |= F_ELEM_ACTIVE; + LOCK_FOCUS(this); + m_curslot = curslot; + } + } + + // if gadget is active, then check if we should be highlighted + if (CHECK_FLAG(m_flags, F_ELEM_ACTIVE)) { + if (curslot != -1 && m_curslot == curslot) { + m_flags |= F_ELEM_HILITE; + } + else { + m_flags &= (~F_ELEM_HILITE); + } + } + } +} + + +void cfg_element::OnMouseBtnUp(int btn) +{ + if (btn == UILMSEBTN) { + // only do something if active. + if (CHECK_FLAG(m_flags, F_ELEM_ACTIVE)) { + UNLOCK_FOCUS(this); + if (CHECK_FLAG(m_flags, F_ELEM_HILITE)) { + OnSelect(); + m_slot = m_curslot; + } + else { + m_slot = -1; + } + } + m_flags &= ~(F_ELEM_HILITE+F_ELEM_ACTIVE); +// m_curslot = -1; + } +} + + +void cfg_element::OnLostFocus() +{ + m_blink_state = 0; + m_slot_alpha = 250; +} + + +void cfg_element::OnGainFocus() +{ + m_slot_alpha = 250; + m_blink_state = -25; + m_curslot = 0; +} + + +void cfg_element::OnDestroy() +{ + cfg_element::m_count--; + if (cfg_element::m_count <= 0) { + cfg_element::m_count = 0; + newui_FreeBitmap(cfg_element::m_btn_bmp); + newui_FreeBitmap(cfg_element::m_btn_bmp_lit); + newui_FreeBitmap(cfg_element::m_xbtn_bmp); + newui_FreeBitmap(cfg_element::m_xbtn_bmp_lit); + } +} + + +// calls configuration routines +bool cfg_element::Configure(ct_type *new_elem_type, ubyte *controller, ubyte *new_cfg_element, sbyte *cfg_slot) +{ + cfg_element_ui cfg_box; + ct_type ctype_fn[CTLBINDS_PER_FUNC]; + ubyte cfgflags_fn[CTLBINDS_PER_FUNC]; + ct_config_data ccfgdata_fn; + tCfgDataParts cfgparts; + ubyte element, ctrl; + bool configure = false; + + sbyte fnid = m_fnid; + sbyte slot = m_slot; + + if (m_fnid == -1) { + Int3(); // get samir + return false; + } + +// determine which configuration pipe to go down + Controller->get_controller_function(fnid, ctype_fn, &ccfgdata_fn, cfgflags_fn); + + if (!new_elem_type) { + cfgparts.bind_0 = NULL_BINDING; + cfgparts.bind_1 = NULL_BINDING; + cfgparts.ctrl_0 = NULL_CONTROLLER; + cfgparts.ctrl_1 = NULL_CONTROLLER; + + ccfgdata_fn = unify_config_data(&cfgparts); + cfgflags_fn[0] = 0; + cfgflags_fn[1] = 0; + + Controller->set_controller_function(fnid, ctype_fn, ccfgdata_fn, cfgflags_fn); + return false; + } + + ASSERT(slot < N_CFGELEM_SLOTS && slot >= 0); + + parse_config_data(&cfgparts, ctype_fn[0], ctype_fn[1], ccfgdata_fn); + element = (slot == 0) ? cfgparts.bind_0 : cfgparts.bind_1; + ctrl = (slot==0) ? cfgparts.ctrl_0 : cfgparts.ctrl_1; + +// check if we can configure this slot. + switch (ctype_fn[slot]) + { + case ctKey: + configure = true; + break; + case ctMouseButton: + case ctButton: + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + case ctAxis: + case ctMouseAxis: + configure = true; + break; + } + +// do configuration requested + *new_elem_type = ctNone; + *new_cfg_element = NULL_BINDING; + *controller = NULL_CONTROLLER; + *cfg_slot = -1; + if (configure) { + const char *txt = m_title; + cfg_box.Create(txt, ctype_fn[slot], ctrl, element); + cfg_box.Open(); + if (cfg_box.DoUI() == UID_OK) { + *new_elem_type = cfg_box.GetType(); + *new_cfg_element = cfg_box.GetElement(); + *controller = cfg_box.GetController(); + *cfg_slot = m_slot; + configure = true; + } + else { + configure = false; + } + cfg_box.Close(); + cfg_box.Destroy(); + } + + return configure; +} + + + +////////////////////////////////////////////////////////////////////////////// +bool key_cfg_element(ubyte *element, ubyte *flags) +{ +// put up configuration dialog + + return true; +} + + + + +void cfg_element_ui::Create(const char *title, ct_type type, ubyte controller, ubyte element) +{ + m_controller = controller; + m_element = element; + m_type = type; + + newuiMessageBox::Create(title, MSGBOX_NULL); + +// put appropriate instructions in messagebox + newuiSheet *sheet = newuiMessageBox::GetSheet(); + + switch (type) + { + case ctKey: + sheet->AddText(TXT_CTLBINDHELP1); + break; + case ctButton: + case ctMouseButton: + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + sheet->AddText(TXT_CTLBINDHELP2_0); + sheet->AddText(TXT_CTLBINDHELP2_1); + break; + + case ctAxis: + case ctMouseAxis: + sheet->AddText(TXT_CTLBINDHELP3_0); + sheet->AddText(TXT_CTLBINDHELP3_1); + break; + } +} + + +#define GCV_CONTROLLER(_r) (CONTROLLER_CTL1_INFO(CONTROLLER_INFO(ccfgdata))) +#define GCV_VALUE(_r) (CONTROLLER_CTL1_VALUE(CONTROLLER_VALUE(ccfgdata))) +#define GCV_VALID_RESULT(_r) (CONTROLLER_CTL1_INFO(CONTROLLER_INFO(ccfgdata)) != (sbyte)NULL_CONTROLLER) + +int cfg_element_ui::DoUI() +{ + extern void ddio_MouseQueueFlush(); + + int retval = UID_OK; + bool quit = false, catch_press=false; + sbyte adj; + + m_alpha = 16; + adj = -1; + + ui_HideCursor(); + + Descent->delay(0.3f); + ResumeControls(); + Control_poll_flag = false; // under multiplayer, the game frame is still running, so don't let + // assignments make it down to game frame (HACK) + newuiMessageBox::GetSheet()->Realize(); + while (!quit) + { + int key; + + Descent->defer(); + key = ddio_KeyInKey(); + + // quit if escape pressed + if (key == KEY_ESC) { + retval = UID_CANCEL; + quit = true; + } + + // input must go here. + switch (m_type) + { + case ctKey: + { + // perform simple key processing. + if (key > 0) { + if (Ctltext_KeyBindings[key&0xff]) { + m_element = (key&0xff); + quit = true; + } + else { + // here tell user that this key can't be bound. + } + } + break; + } + case ctButton: + case ctMouseButton: + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + { + ct_config_data ccfgdata; + ct_type new_type; + + // get each type of input. + Controller->poll(); + + ccfgdata = Controller->get_controller_value(ctMouseButton); + new_type = ctMouseButton; + if (!GCV_VALID_RESULT(ccfgdata)) { + ccfgdata = Controller->get_controller_value(ctPOV); + new_type = ctPOV; + if (!GCV_VALID_RESULT(ccfgdata)) { + ccfgdata = Controller->get_controller_value(ctPOV2); + new_type = ctPOV2; + if (!GCV_VALID_RESULT(ccfgdata)) { + ccfgdata = Controller->get_controller_value(ctPOV3); + new_type = ctPOV3; + if (!GCV_VALID_RESULT(ccfgdata)) { + ccfgdata = Controller->get_controller_value(ctPOV4); + new_type = ctPOV4; + if (!GCV_VALID_RESULT(ccfgdata)) { + ccfgdata = Controller->get_controller_value(ctButton); // read hats before buttons + new_type = ctButton; + } + } + } + } + } + if (GCV_VALID_RESULT(ccfgdata) && !catch_press) { + m_type = new_type; + m_element = GCV_VALUE(ccfgdata); + m_controller = GCV_CONTROLLER(ccfgdata); + catch_press = true; + // mprintf((0, "HERE?\n")); + } + else if (catch_press) { + quit = true; + // mprintf((0, "THERE?\n")); + } + break; + } + + case ctAxis: + case ctMouseAxis: + { + ct_config_data ccfgdata; + ct_type new_type; + + // get each type of input. + Controller->poll(); + + ccfgdata = Controller->get_controller_value(ctAxis); + new_type = ctAxis; + + if (!GCV_VALID_RESULT(ccfgdata)) { + ccfgdata = Controller->get_controller_value(ctMouseAxis); + new_type = ctMouseAxis; + } + if (GCV_VALID_RESULT(ccfgdata)) { + m_type = new_type; + m_element = GCV_VALUE(ccfgdata); + m_controller = GCV_CONTROLLER(ccfgdata); + quit = true; + } + break; + } + + case ctNone: + quit = true; + break; + + default: + Int3(); + quit = true; + } + + DoUIFrameWithoutInput(); + rend_Flip(); + + if (GetUIFrameResult() == NEWUIRES_FORCEQUIT) { + quit = true; + } + + m_alpha += adj; + if (m_alpha < -15) { + m_alpha = -15; + adj = 1; + } + else if (m_alpha > 16) { + m_alpha = 16; + adj = -1; + } + } + newuiMessageBox::GetSheet()->Unrealize(); + + SuspendControls(); + + ui_ShowCursor(); + ddio_MouseQueueFlush(); + ui_Flush(); + + return UID_OK; +} + + +void cfg_element_ui::OnDraw() +{ + UITextItem prompt(MONITOR9_NEWUI_FONT, "?", NEWUI_MONITORFONT_COLOR, (ubyte)(m_alpha*8)+127); + + newuiMessageBox::OnDraw(); + + prompt.draw(16, 60); +} + + diff --git a/Descent3/CtlCfgElem.h b/Descent3/CtlCfgElem.h new file mode 100644 index 000000000..12e746048 --- /dev/null +++ b/Descent3/CtlCfgElem.h @@ -0,0 +1,149 @@ +/* + * $Logfile: /DescentIII/Main/CtlCfgElem.h $ + * $Revision: 15 $ + * $Date: 7/16/99 11:15a $ + * $Author: Samir $ + * + * Header file + * + * $Log: /DescentIII/Main/CtlCfgElem.h $ + * + * 15 7/16/99 11:15a Samir + * multiple hat support + * + * 14 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 13 2/26/99 2:09a Samir + * added '?' button. + * + * 12 2/21/99 6:36p Samir + * focusing changes and key input changes to ui., + * + * 11 2/16/99 12:07p Samir + * redid controller config with new ui. + * + * 10 11/30/98 4:54p Samir + * added rear view config item. + * + * 9 10/22/98 4:11p Samir + * put inventory keys back in. + * + * 8 10/22/98 2:40p Samir + * took out inventory keys for demo. + * + * 7 10/21/98 12:32p Samir + * fixed problem with size of gadget. + * + * 6 10/18/98 1:07p Samir + * tweaked user interface for controller config. + * + * 5 10/17/98 7:31p Samir + * added invertible axes + * + * 4 10/08/98 10:52a Samir + * added countermeasure and weapon cycling. + * + * 3 10/02/98 4:15p Samir + * added fancy artwork for config screens. + * + * 2 9/30/98 4:37p Samir + * 'incremental checkin' + * + * 1 9/28/98 3:48p Samir + * initial revision + * + * $NoKeywords: $ + */ + +#ifndef CTLCFGELEM_H +#define CTLCFGELEM_H + +#include "newui.h" +#include "Controller.h" +#include "controls.h" +#include "stringtable.h" + + +////////////////////////////////////////////////////////////////////////////// +// Data types +#define N_CFGELEM_SLOTS CTLBINDS_PER_FUNC // number of controller slots per function +#define CFGELEM_SLOT_CLEAR 2 + + +////////////////////////////////////////////////////////////////////////////// +// +class cfg_element: public UIGadget +{ + static UIBitmapItem *m_btn_bmp_lit, *m_btn_bmp, *m_xbtn_bmp_lit, *m_xbtn_bmp; + static short m_count; + const char *m_title; + sbyte m_fnid, m_flags; // fnflags : 1 if invert btn visible + sbyte m_slot, m_curslot; // what slow is in focus and number of slots. + ubyte m_slot_alpha; + sbyte m_blink_state; + +public: + void Create(UIWindow *wnd, int str_i, int x, int y, int fnid, int id); + sbyte GetActiveSlot() const { return m_slot; }; + bool Configure(ct_type *elem_type, ubyte *controller, ubyte *new_elem, sbyte *slot); // calls configuration routines (returns true if approved) + +protected: + virtual void OnDraw(); + virtual void OnKeyDown(int key); + virtual void OnMouseBtnDown(int btn); + virtual void OnMouseBtnUp(int btn); + virtual void OnLostFocus(); + virtual void OnGainFocus(); + virtual void OnDestroy(); + virtual void OnFormat(); +}; + + +typedef struct tCfgDataParts +{ + ubyte bind_0, bind_1, ctrl_0, ctrl_1; +} +tCfgDataParts; + +inline void parse_config_data(tCfgDataParts *parts, ct_type type0, ct_type type1, ct_config_data cfgdata) +{ + switch (type0) + { + case ctKey: parts->bind_0 = CONTROLLER_KEY1_VALUE(CONTROLLER_VALUE(cfgdata)); break; + case ctAxis: + case ctMouseAxis: + case ctButton: + case ctMouseButton: + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: parts->bind_0 = CONTROLLER_CTL1_VALUE(CONTROLLER_VALUE(cfgdata)); break; + default: parts->bind_0 = 0; + } + + switch (type1) + { + case ctKey: parts->bind_1 = CONTROLLER_KEY2_VALUE(CONTROLLER_VALUE(cfgdata)); break; + case ctAxis: + case ctMouseAxis: + case ctButton: + case ctMouseButton: + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: parts->bind_1 = CONTROLLER_CTL2_VALUE(CONTROLLER_VALUE(cfgdata)); break; + default: parts->bind_1 = 0; + } + + parts->ctrl_0 = CONTROLLER_CTL1_INFO(CONTROLLER_INFO(cfgdata)); + parts->ctrl_1 = CONTROLLER_CTL2_INFO(CONTROLLER_INFO(cfgdata)); +} + + +inline ct_config_data unify_config_data(tCfgDataParts *parts) +{ + return MAKE_CONFIG_DATA(CONTROLLER_CTL_INFO(parts->ctrl_0, parts->ctrl_1), CONTROLLER_CTL_VALUE(parts->bind_0, parts->bind_1)); +} + +#endif \ No newline at end of file diff --git a/Descent3/D3ForceFeedback.cpp b/Descent3/D3ForceFeedback.cpp new file mode 100644 index 000000000..ef6974eaf --- /dev/null +++ b/Descent3/D3ForceFeedback.cpp @@ -0,0 +1,755 @@ +/* +* $Logfile: /DescentIII/main/D3ForceFeedback.cpp $ +* $Revision: 19 $ +* $Date: 5/10/99 9:25p $ +* $Author: Jeff $ +* +* High level force-feedback implementation +* +* $Log: /DescentIII/main/D3ForceFeedback.cpp $ + * + * 19 5/10/99 9:25p Jeff + * first phase of Rock 'n' Ride support added + * + * 18 4/24/99 7:45p Jeff + * fixed recoil + * + * 17 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 16 4/15/99 1:38a Jeff + * changes for linux compile + * + * 15 4/12/99 1:06p Jeff + * hooked force feedback into recoil of weapon struct + * + * 14 3/28/99 5:53p Jeff + * fixed iforce crashes + * + * 13 2/09/99 3:32p Jeff + * table file parser takes quotes strings for force keywords + * + * 12 2/05/99 7:04p Jeff + * table file parsing macros put in + * + * 11 1/30/99 11:27p Jeff + * added immersion support + * + * 10 1/28/99 12:09p Jeff + * added force feedback to player shake...fixed spelling error in define + * for forcefeedback + * + * 9 1/25/99 7:28p Jeff + * fixed alt-tab bug (finally)...wasn't releasing effects on alt-tab out, + * and re-creating them on restart + * + * 8 11/18/98 5:50p Jeff + * added some cheap recoil effects for ForceFeedback...not fully + * implemented + * + * 7 11/10/98 5:16p Jeff + * updated forcefeedback system...pretty complete now + * + * 6 11/06/98 7:00p Jeff + * first round of new force feedback installed + * + * 5 11/03/98 6:43p Jeff + * new low-level & high level Force Feedback system implemented, handles + * window losing focus, etc. + * + * 4 10/12/98 3:49p Jeff + * struct changes + * + * 3 9/21/98 11:09a Jeff + * general update, new low level, small high level implementation + * + * 2 9/18/98 7:38p Jeff + * creation of low-level forcefeedback and beginning of high-level + * forcefeedback +* +* $NoKeywords: $ +*/ + +#include + +#include "forcefeedback.h" +#include "D3ForceFeedback.h" +#include "pserror.h" +#include "mono.h" +#include "weapon.h" +#include "ddio.h" +#include "psrand.h" +#include "rocknride.h" + +extern float Gametime; + +//D3Force_init +//true if ForceInit() has been called, meaning hi-level ForceFeedback has been initialized +//and any of the Hi-Level ForceFeedback commands can be called. +bool D3Force_init = false; + +//D3Force_pause +//true if the ForceFeedback effects are currently being paused. ForceContinue() must be called +//before any other ForceFeedback functions are called. +bool D3Force_pause = false; + +//D3Force_auto_center +//true if the user wants his joystick to be autocentered (if available) +bool D3Force_auto_center = false; + +//D3Force_gain +//the gain setting of the ForceFeedback system +float D3Force_gain = 1.0f; + +//D3Use_force_feedback +//true if the user wants force feedback during play (if available) +bool D3Use_force_feedback = true; + + +int Force_hi_to_low_map[DDIO_FF_MAXEFFECTS]; + +// ----------------------------------------------------------------- +// ForceInit +// Purpose: +// Initializes the Hi-Level Force Feedback system, creating all +// of the effects so they are ready to be used. +// ----------------------------------------------------------------- +void ForceInit(void) +{ + static bool first_call = true; + + ASSERT(!D3Force_init); + + bool dd_found,dd_enabled; + ddio_ff_GetInfo(&dd_found,&dd_enabled); + + if (dd_found){ + D3Force_init = true; + D3Force_pause = false; + + mprintf ((0,"Force: High Level Force Feedback system initialized\n")); + }else{ + D3Force_init = false; + D3Force_pause = false; + + mprintf ((0,"Force: Force Feedback System Not Found\n")); + } + + for( int i=0; i 1) +// ------------------------------------------------------------------ +void ForceSetGain(float val) +{ + if(val<0.0f) val = 0.0f; + if(val>1.0f) val = 1.0f; + + D3Force_gain = val; + ddio_ffjoy_SetGain(kJoy1,D3Force_gain); +} + +// ------------------------------------------------------------------ +// ForceGetGain +// Purpose: +// Returns the current gain setting of the ForceFeedback system (0-1) +// ------------------------------------------------------------------ +float ForceGetGain(void) +{ + return D3Force_gain; +} + +/* +============================================================================= +*/ + +// ----------------------------------------------------------------- +// ForceEffectsClose +// Purpose: +// Destroys all the effects created +// ----------------------------------------------------------------- +void ForceEffectsClose(void) +{ + ddio_ffb_DestroyAll(); +} + +// ----------------------------------------------------------------- +// ForceEffectsPlay +// Purpose: +// Plays an effect +// ----------------------------------------------------------------- +void ForceEffectsPlay(int id,float *scale,int *direction) +{ + if(RocknRide_enabled && direction) + { + RNR_UpdateForceFeedbackInfo((scale)?*scale:1.0f,direction); + } + + if (!D3Force_init || !D3Use_force_feedback ) + return; + + if(id<0 || id>=DDIO_FF_MAXEFFECTS) + return; + + int low_id = Force_hi_to_low_map[id]; + if(low_id==-1) + return; + + if(scale || direction){ + unsigned int *ns = NULL; + unsigned int new_gain; + + if(scale){ + new_gain = (unsigned int)(10000.0f * (*scale)); + ns = &new_gain; + } + + ddio_ffb_effectModify(low_id,direction,NULL,ns,NULL,NULL,NULL); + } + + ddio_ffb_effectPlay(low_id); +} +void ForceEffectsPlay(int id,float *scale,vector *direction) +{ + if(RocknRide_enabled && direction) + { + RNR_UpdateForceFeedbackInfo((scale)?*scale:1.0f,direction); + } + + if (!D3Force_init || !D3Use_force_feedback ) + return; + + if(id<0 || id>=DDIO_FF_MAXEFFECTS) + return; + + int low_id = Force_hi_to_low_map[id]; + if(low_id==-1) + return; + + int new_dir = -1; + + if(direction){ + matrix mat = Identity_matrix; + angvec ag; + + vm_VectorToMatrix(&mat,direction); + vm_ExtractAnglesFromMatrix(&ag,&mat); + + new_dir = ((((float)ag.h)/65535.0f)*360.0f); + + new_dir = (new_dir) * FF_DEGREES; + + + } + + if(scale || direction){ + int *d = NULL; + unsigned int *ns = NULL; + unsigned int new_gain; + + if(direction) + d = &new_dir; + + if(scale){ + new_gain = (unsigned int)(10000.0f * (*scale)); + ns = &new_gain; + } + + ddio_ffb_effectModify(low_id,d,NULL,ns,NULL,NULL,NULL); + } + + ddio_ffb_effectPlay(low_id); +} + + +void DoForceForWeapon(object *me_obj,object *it_obj,vector *force_vec) +{ + if (!RocknRide_enabled && (!D3Force_init || !D3Use_force_feedback) ) + return; + + if(it_obj->id<0 || it_obj->id>=MAX_WEAPONS) + return; + + weapon *weap = &Weapons[it_obj->id]; + + if(weap->flags&WF_MICROWAVE){ + vector local_norm,v; + float scale = 1.00f; + v.x = ps_rand()%10; + v.y = 0; + v.z = ps_rand()%10; + vm_NormalizeVector(&v); + vm_MatrixMulVector (&local_norm,&v,&me_obj->orient); + + local_norm *= -1.0f; + + ForceEffectsPlay(FORCE_MICROWAVE,&scale,&local_norm); + return; + } + +} + +void DoForceForWall(object *playerobj, float hitspeed, int hitseg, int hitwall, vector *wall_normal) +{ + if (!RocknRide_enabled && (!D3Force_init || !D3Use_force_feedback) ) + return; + + vector local_norm; + float scale = 1.00f; + + if(hitspeed<20) + return; + + scale = hitspeed/80.0f; + if(scale<0.0f) scale = 0.0f; + if(scale>1.0f) scale = 1.0f; + + + vm_MatrixMulVector (&local_norm,wall_normal,&playerobj->orient); + local_norm *= -1.0f; + + ForceEffectsPlay(FORCE_WALLHIT,&scale,&local_norm); +} + +void DoForceForRecoil(object *playerobj,object *weap) +{ + if (!RocknRide_enabled && (!D3Force_init || !D3Use_force_feedback) ) + return; + + weapon *w_ptr = &Weapons[weap->id]; + + vector local_norm; + float scale; +#define MIN_RECOIL 1000.0f +#define MAX_RECOIL 5000.0f +#define RECOIL_THRESHOLD 1100.0f + + if(w_ptr->recoil_forcepos - playerobj->pos; + vm_MatrixMulVector(&local_norm,&offset,&playerobj->orient); + + if(weap->movement_type!=MT_PHYSICS) + return; + + ASSERT( w_ptr->recoil_force>=MIN_RECOIL && w_ptr->recoil_force<=MAX_RECOIL); + scale = (w_ptr->recoil_force - MIN_RECOIL)/(MAX_RECOIL-MIN_RECOIL); + + ForceEffectsPlay(FORCE_WEAPON_RECOIL,&scale,&local_norm); +} + +float Force_time_since_last_shake; +#define SHAKE_TIME 0.2f +void DoForceForShake(float magnitude) +{ + if (!RocknRide_enabled && (!D3Force_init || !D3Use_force_feedback) ) + return; + + if( Force_time_since_last_shake + SHAKE_TIME > Gametime ){ + if (Force_time_since_last_shake < Gametime){ + return; + } + } + + Force_time_since_last_shake = Gametime; + + vector local_norm; + + if(magnitude<0.0f) magnitude = 0.0f; + if(magnitude>1.0f) magnitude = 1.0f; + + local_norm.x = (ps_rand()%5); + local_norm.y = (ps_rand()%5); + local_norm.z = (ps_rand()%5); + vm_NormalizeVector(&local_norm); + + ForceEffectsPlay(FORCE_SHIPSHAKE,&magnitude,&local_norm); +} + +//************************************************************************ + +void FFECreate_Test(tFFB_Effect *ef) +{ + memset(ef,0,sizeof(tFFB_Effect)); + + ef->Type = kConstant; + ef->TypeInfo.Constant.Mag = FF_NOMINALMAX; + ef->Duration = 0.1*FF_SECONDS; + ef->Gain = FF_NOMINALMAX; + ef->Period = 0; + ef->Axis = kBothAxes; + ef->Trigger = kNoButton; + ef->Direction = 0; +} + +void FFECreate_Microwave(tFFB_Effect *ef) +{ + memset(ef,0,sizeof(tFFB_Effect)); + + ef->Flags = FF_USEENVELOPE; + ef->Type = kWave_Sine; + ef->TypeInfo.Wave.Mag = FF_NOMINALMAX; + ef->TypeInfo.Wave.Offset = 0; + ef->TypeInfo.Wave.Phase = 0; + ef->TypeInfo.Wave.Period = 0.2*FF_SECONDS; + + ef->Duration = 0.6*FF_SECONDS; + ef->Gain = FF_NOMINALMAX; + ef->Period = 0; + ef->Axis = kBothAxes; + ef->Trigger = kNoButton; + ef->Direction = 0; + + ef->Envelope.AttackLevel = FF_NOMINALMAX*0.5f; + ef->Envelope.AttackTime = 0; + ef->Envelope.FadeLevel = FF_NOMINALMAX*0.1f; + ef->Envelope.FadeTime = 0.6*FF_SECONDS; +} + +void FFECreate_Wallhit(tFFB_Effect *ef) +{ + memset(ef,0,sizeof(tFFB_Effect)); + + ef->Type = kConstant; + ef->TypeInfo.Constant.Mag = FF_NOMINALMAX; + ef->Duration = 0.1*FF_SECONDS; + ef->Gain = FF_NOMINALMAX; + ef->Period = 0; + ef->Axis = kBothAxes; + ef->Trigger = kNoButton; + ef->Direction = 0; +} + +void FFECreate_WeaponRecoil(tFFB_Effect *ef) +{ + memset(ef,0,sizeof(tFFB_Effect)); + + ef->Type = kConstant; + ef->TypeInfo.Constant.Mag = FF_NOMINALMAX; + ef->Duration = 0.1*FF_SECONDS; + ef->Gain = FF_NOMINALMAX; + ef->Period = 0; + ef->Axis = kBothAxes; + ef->Trigger = kNoButton; + ef->Direction = 0; +} + +void FFECreate_VaussRecoil(tFFB_Effect *ef) +{ + memset(ef,0,sizeof(tFFB_Effect)); + + ef->Type = kWave_Square; + ef->TypeInfo.Wave.Mag = FF_NOMINALMAX; + ef->TypeInfo.Wave.Offset = 0; + ef->TypeInfo.Wave.Phase = 0; + ef->TypeInfo.Wave.Period = 0.05*FF_SECONDS; + + ef->Duration = 0.2*FF_SECONDS; + ef->Gain = FF_NOMINALMAX; + ef->Period = 0; + ef->Axis = kBothAxes; + ef->Trigger = kNoButton; + ef->Direction = 0; +} + +void FFECreate_Afterburner(tFFB_Effect *ef) +{ + memset(ef,0,sizeof(tFFB_Effect)); + + ef->Type = kWave_Square; + ef->TypeInfo.Wave.Mag = FF_NOMINALMAX; + ef->TypeInfo.Wave.Offset = 0; + ef->TypeInfo.Wave.Phase = 0; + ef->TypeInfo.Wave.Period = 0.05*FF_SECONDS; + + ef->Duration = 0.2*FF_SECONDS; + ef->Gain = FF_NOMINALMAX; + ef->Period = 0; + ef->Axis = kBothAxes; + ef->Trigger = kNoButton; + ef->Direction = 0; +} + +void FFECreate_ShipShake(tFFB_Effect *ef) +{ + memset(ef,0,sizeof(tFFB_Effect)); + + ef->Type = kWave_Sine; + ef->TypeInfo.Wave.Mag = (FF_NOMINALMAX/3); + ef->TypeInfo.Wave.Offset = 0; + ef->TypeInfo.Wave.Phase = 0; + ef->TypeInfo.Wave.Period = (SHAKE_TIME/2)*FF_SECONDS; + + ef->Duration = SHAKE_TIME*FF_SECONDS; + ef->Gain = FF_NOMINALMAX; + ef->Period = 0; + ef->Axis = kBothAxes; + ef->Trigger = kNoButton; + ef->Direction = 0; +} + +//************************************************************* +// ------------------------------------------------------------------ +// ForceEffectsInit +// Purpose: +// Initializes the force feedback effects for Descent 3 +// ------------------------------------------------------------------ +void ForceEffectsInit(void) +{ + tFFB_Effect eff; + int lowid; + Force_time_since_last_shake = 0; + FORCEPROJECT prj; + char path[_MAX_PATH]; + + if(cfexist("D3Force.ifr")) + { + ddio_MakePath(path,LocalD3Dir,"custom","cache","D3Force.ifr",NULL); + cf_CopyFile(path,"D3Force.ifr",0); + prj = ddio_ForceLoadProject(IGNORE_TABLE(path),kJoy1); + }else + { + prj = NULL; + } +/* +$$TABLE_GAMEFILE "D3Force.ifr" +*/ + lowid = ddio_CreateForceFromProject(prj,"Test"); + if(lowid==-1){ + FFECreate_Test(&eff); + lowid = ddio_ffb_effectCreate(kJoy1,&eff); + } + Force_hi_to_low_map[FORCE_TEST_FORCE] = lowid; + + lowid = ddio_CreateForceFromProject(prj,"Microwave"); + if(lowid==-1){ + FFECreate_Microwave(&eff); + lowid = ddio_ffb_effectCreate(kJoy1,&eff); + } + Force_hi_to_low_map[FORCE_MICROWAVE] = lowid; + + lowid = ddio_CreateForceFromProject(prj,"WallHit"); + if(lowid==-1){ + FFECreate_Wallhit(&eff); + lowid = ddio_ffb_effectCreate(kJoy1,&eff); + } + Force_hi_to_low_map[FORCE_WALLHIT] = lowid; + + lowid = ddio_CreateForceFromProject(prj,"Recoil"); + if(lowid==-1){ + FFECreate_WeaponRecoil(&eff); + lowid = ddio_ffb_effectCreate(kJoy1,&eff); + } + Force_hi_to_low_map[FORCE_WEAPON_RECOIL] = lowid; + + lowid = ddio_CreateForceFromProject(prj,"VaussRecoil"); + if(lowid==-1){ + FFECreate_VaussRecoil(&eff); + lowid = ddio_ffb_effectCreate(kJoy1,&eff); + } + Force_hi_to_low_map[FORCE_VAUSS_RECOIL] = lowid; + + lowid = ddio_CreateForceFromProject(prj,"Afterburner"); + if(lowid==-1){ + FFECreate_Afterburner(&eff); + lowid = ddio_ffb_effectCreate(kJoy1,&eff); + } + Force_hi_to_low_map[FORCE_AFTERBURNER] = lowid; + + lowid = ddio_CreateForceFromProject(prj,"ShipShake"); + if(lowid==-1){ + FFECreate_ShipShake(&eff); + lowid = ddio_ffb_effectCreate(kJoy1,&eff); + } + Force_hi_to_low_map[FORCE_SHIPSHAKE] = lowid; + + ddio_ForceUnloadProject(prj); + if(cfexist(path)) + { + ddio_DeleteFile(path); + } +} diff --git a/Descent3/D3ForceFeedback.h b/Descent3/D3ForceFeedback.h new file mode 100644 index 000000000..01c7d56b4 --- /dev/null +++ b/Descent3/D3ForceFeedback.h @@ -0,0 +1,184 @@ +/* +* $Logfile: /DescentIII/main/D3ForceFeedback.h $ +* $Revision: 9 $ +* $Date: 1/28/99 12:09p $ +* $Author: Jeff $ +* +* High Level Force Feedback implementation +* +* $Log: /DescentIII/main/D3ForceFeedback.h $ + * + * 9 1/28/99 12:09p Jeff + * added force feedback to player shake...fixed spelling error in define + * for forcefeedback + * + * 8 11/18/98 5:50p Jeff + * added some cheap recoil effects for ForceFeedback...not fully + * implemented + * + * 7 11/10/98 5:16p Jeff + * updated forcefeedback system...pretty complete now + * + * 6 11/06/98 7:00p Jeff + * first round of new force feedback installed + * + * 5 11/03/98 6:43p Jeff + * new low-level & high level Force Feedback system implemented, handles + * window losing focus, etc. + * + * 4 10/12/98 3:49p Jeff + * struct changes + * + * 3 9/21/98 11:10a Jeff + * general update, new low level, small high level implementation + * + * 2 9/18/98 7:38p Jeff + * creation of low-level forcefeedback and beginning of high-level + * forcefeedback +* +* $NoKeywords: $ +*/ + +#ifndef __D3FORCEFEEDBACK_H_ +#define __D3FORCEFEEDBACK_H_ + +#include "forcefeedback.h" +#include "vecmat.h" +#include "object.h" + +//D3Use_force_feedback +//true if the user wants force feedback during play (if available) +extern bool D3Use_force_feedback; +//D3Force_auto_center +//true if the user wants his joystick to be autocentered (if available) +extern bool D3Force_auto_center; +//D3Force_gain +//the gain setting of the ForceFeedback system +extern float D3Force_gain; + +#define FORCE_TEST_FORCE 0 +#define FORCE_MICROWAVE 1 +#define FORCE_WALLHIT 2 +#define FORCE_WEAPON_RECOIL 3 +#define FORCE_VAUSS_RECOIL 4 +#define FORCE_AFTERBURNER 5 +#define FORCE_SHIPSHAKE 6 + +// ----------------------------------------------------------------- +// ForceInit +// Purpose: +// Initializes the Hi-Level Force Feedback system, creating all +// of the effects so they are ready to be used. +// ----------------------------------------------------------------- +void ForceInit(void); + +// ----------------------------------------------------------------- +// ForceClose +// Purpose: +// Shutsdown the Hi-Level Force Feedback system +// ----------------------------------------------------------------- +void ForceClose(void); + +// ----------------------------------------------------------------- +// ForceShutdown +// Purpose: +// Puts the Force Feedback system on pause while the application +// is doing other things (like it lost focus) +// ----------------------------------------------------------------- +void ForceShutdown(void); + +// ------------------------------------------------------------------ +// ForceRestart +// Purpose: +// Restores the Force Feedback system that has been put on pause +// from ForceShutdown. +// ------------------------------------------------------------------ +void ForceRestart(void); + +// ------------------------------------------------------------------ +// ForceDisable +// Purpose: +// Disables Force Feedback on a Force Feedback system +// ------------------------------------------------------------------ +void ForceDisable(void); + +// ------------------------------------------------------------------ +// ForceEnable +// Purpose: +// Enables Force Feedback that has been previously disabled +// ------------------------------------------------------------------ +void ForceEnable(void); + +// ------------------------------------------------------------------ +// ForceIsEnabled +// Purpose: +// Returns true if Force Feedback is enabled on the system +// ------------------------------------------------------------------ +bool ForceIsEnabled(void); + +// ------------------------------------------------------------------ +// ForceEnableAutoCenter +// Purpose: +// Enables autocentering on the joystick +// ------------------------------------------------------------------ +void ForceEnableAutoCenter(void); + +// ------------------------------------------------------------------ +// ForceDisableAutoCenter +// Purpose: +// Disables autocentering on the joystick +// ------------------------------------------------------------------ +void ForceDisableAutoCenter(void); + +// ------------------------------------------------------------------ +// ForceIsAutoCenter +// Purpose: +// Returns true if Force Feedback joystick is autocentering +// ------------------------------------------------------------------ +bool ForceIsAutoCenter(void); + +// ------------------------------------------------------------------ +// ForceSetGain +// Purpose: +// Sets the gain of the ForceFeedback system (0 -> 1) +// ------------------------------------------------------------------ +void ForceSetGain(float val); + +// ------------------------------------------------------------------ +// ForceGetGain +// Purpose: +// Returns the current gain setting of the ForceFeedback system (0-1) +// ------------------------------------------------------------------ +float ForceGetGain(void); + +// ------------------------------------------------------------------ +// ForceEffectsInit +// Purpose: +// Initializes the force feedback effects for Descent 3 +// ------------------------------------------------------------------ +void ForceEffectsInit(void); + +// ----------------------------------------------------------------- +// ForceEffectsClose +// Purpose: +// Destroys all the effects created +// ----------------------------------------------------------------- +void ForceEffectsClose(void); + +// ----------------------------------------------------------------- +// ForceEffectsPlay +// Purpose: +// Plays an effect +// ----------------------------------------------------------------- +void ForceEffectsPlay(int id,float *scale,int *direction); +void ForceEffectsPlay(int id,float *scale,vector *direction); + + +void DoForceForWeapon(object *me_obj,object *it_obj,vector *force); +void DoForceForWall(object *playerobj, float hitspeed, int hitseg, int hitwall, vector *wall_normal); +void DoForceForRecoil(object *playerobj,object *weap); +void DoForceForShake(float magnitude); + +extern bool D3Use_force_feedback; + +#endif \ No newline at end of file diff --git a/Descent3/DeathInfo.h b/Descent3/DeathInfo.h new file mode 100644 index 000000000..f796b8552 --- /dev/null +++ b/Descent3/DeathInfo.h @@ -0,0 +1,46 @@ +/* + * $Logfile: /DescentIII/Main/DeathInfo.h $ + * $Revision: 6 $ + * $Date: 4/02/99 2:46p $ + * $Author: Matt $ + * + * Info for object deaths + * + * $Log: /DescentIII/Main/DeathInfo.h $ + * + * 6 4/02/99 2:46p Matt + * Moved flags from deathinfo to deathinfo_external, because the arhive + * builder only copies the latter into the archive. + * + * 5 4/02/99 11:23a Matt + * Made KillObject not take a death_info struct, but rather the death info + * as individual parameters. Moved death_info into objinfo.h, since it's + * only used for generic objects. Took out fade-away death hack, now that + * fade-away deaths can be explicitely set. + * + * 4 2/28/99 11:30p Chris + * FindObjOfType and OSIRIS controllable deaths + * + * 3 2/25/99 11:01a Matt + * Added new explosion system. + * + * 2 1/14/99 8:21p Matt + * Added dialog for defining object deaths + * + * 1 1/14/99 8:13p Matt + * + */ + +#ifndef _DEATHINFO_H +#define _DEATHINFO_H + +//Get the death delay type +#define DEATH_DELAY(f) (f & DF_DELAY_MASK) + +//Get the explosion size +#define DEATH_EXPL_SIZE(f) (f & DF_EXPL_SIZE_MASK) + +//Include the actual flags +#include "deathinfo_external.h" + +#endif \ No newline at end of file diff --git a/Descent3/DllWrappers.cpp b/Descent3/DllWrappers.cpp new file mode 100644 index 000000000..db60fa26c --- /dev/null +++ b/Descent3/DllWrappers.cpp @@ -0,0 +1,502 @@ +#include "DllWrappers.h" +#include "pserror.h" +#include "pstring.h" +#include "CFILE.H" +#include "gamefont.h" +#include "grdefs.h" +#include "descent.h" +#include "ddio.h" +#include "movie.h" +#include "program.h" +#include "object.h" +#include "objinit.h" +#include "player.h" +#include "newui.h" +#include "hlsoundlib.h" +#include "appdatabase.h" +#include "attach.h" +#include "game.h" +#include "demofile.h" +#include "pilot.h" +#include "audiotaunts.h" +#include "ship.h" +#include "hud.h" +#include "cockpit.h" + +int D3W_Play3dSound(int sound_index, object *cur_obj, float volume, int flags) +{ + if(Demo_flags==DF_RECORDING) + { + DemoWrite3DSound(sound_index,OBJNUM(cur_obj),1,volume); + } + + return Sound_system.Play3dSound(sound_index,cur_obj,volume,flags); +} + +int D3W_Play2dSound(int sound_index, float volume) +{ + if(Demo_flags==DF_RECORDING) + { + DemoWrite2DSound(sound_index,volume); + } + + return Sound_system.Play2dSound(sound_index,volume); +} + +void D3W_TouchSound(int sound_index) +{ + Sound_system.CheckAndForceSoundDataAlloc(sound_index); +} + +//Inventory CheckItem wrapper +bool InvCheckItem(int playernum,int type,int id) +{ + ASSERT( (playernum>=0) && (playernum=0) && (playernum=0) && (playernum=0) && (playernum=0) && (playernumread(label,entry,entrylen); +} + +bool dDatabaseRead2(const char *label, void *entry, int wordsize) +{ + return Database->read(label,entry,wordsize); +} + +bool dDatabaseRead3(const char *label, bool *entry) +{ + return Database->read(label,entry); +} + +bool dDatabaseWrite1(const char *label, const char *entry, int entrylen) +{ + return Database->write(label,entry,entrylen); +} + +bool dDatabaseWrite2(const char *label, int entry) +{ + return Database->write(label,entry); +} + +// Attaches 2 objects via attach points on each. The f_used_aligned allows for an aligned connection. +// NOTE: The child always moves to the parent +bool dAttachObject(object *parent, char parent_ap, object *child, char child_ap, bool f_use_aligned) +{ + return AttachObject(parent,parent_ap,child,child_ap,f_use_aligned); +} + +// Attaches a child object to a parent object by a percent of the radius of the child. +// NOTE: The child always moves to the parent and not the reverse +bool dAttachObjectRadius(object *parent, char parent_ap, object *child, float percent_rad) +{ + return AttachObject(parent,parent_ap,child,percent_rad); +} + +// Unattaches a child from an attach point +bool dUnattachChild(object *parent, char parent_ap) +{ + return UnattachChild(parent,parent_ap); +} + +// Unattaches an object from its parent +bool dUnattachFromParent(object *child) +{ + return UnattachFromParent(child); +} + +// Unattaches all children from a parent object +bool dUnattachChildren(object *parent) +{ + return UnattachChildren(parent); +} + +bool dObjGet(int handle,object **obj) +{ + *obj = ObjGet(handle); + return (*obj)?true:false; +} + +// Set a vector to {0,0,0} +void dvm_MakeZeroVector(vector *v) +{ + vm_MakeZero(v); +} + +void dvm_MakeZeroAngle(angvec *v) +{ + vm_MakeZero(v); +} + +void dStartFrame(int x, int y, int x2, int y2, bool clear) +{ + StartFrame(x,y,x2,y2,clear); +} + +// ObjSetPos, that automatically sets the OF_MOVED_THIS_FRAME +void ObjSetPosAndMarkMoved(object *obj,vector *pos,int roomnum,matrix *orient,bool f_update_attached_children) +{ + if(obj) + { + ObjSetPos(obj,pos,roomnum,orient,f_update_attached_children); + if(Demo_flags==DF_RECORDING) + DemoWriteChangedObj(obj); //using this instead of setting the flag, since the flag + //has a good chance of getting cleared before it is + //written + //obj->flags |= OF_MOVED_THIS_FRAME; + } +} + +// SetObjectDeadFlag that automatically writes out demo data +void dSetObjectDeadFlagWDemo(object *obj,bool tell_clients_to_remove,bool tell_clients_to_play_sound) +{ + if(Demo_flags==DF_RECORDING) + { + DemoWriteSetObjDead(obj); + } + + SetObjectDeadFlag(obj,tell_clients_to_remove,tell_clients_to_play_sound); +} + +void dSetAudioTaunts(bool enable) +{ + // If the F6 menu can make perm changes, uncomment the next two lines (and comment the 3rd) + //Current_pilot.set_audiotaunts(enable); + //PltWriteFile(&Current_pilot,false); + + //Leave this line uncommented for non-perm changes + taunt_Enable(enable); +} + +// Changes the ship_index for a given player +void ChangePlayerShipIndex(int pnum,int ship_index) +{ +int ObjInitTypeSpecific(object *objp,bool reinitializing); + + if(pnum<0 || pnum>=MAX_PLAYERS) + return; + if (Players[pnum].start_roomnum==-1) + return; // this player doesn't exist + if(ship_index<0||ship_index>=MAX_SHIPS) + return; + if(!Ships[ship_index].used) + return; + + bool isme = (pnum==Player_num)?true:false; + + if(isme) + { + FreeCockpit(); + CloseShipHUD(); + } + + PlayerChangeShip(pnum,ship_index); + + //reset physics, model, etc. + ObjInitTypeSpecific(&Objects[Players[pnum].objnum],0); + + if(isme) + { + InitShipHUD(Players[pnum].ship_index); + InitCockpit(Players[pnum].ship_index); + + if (GetHUDMode() == HUD_COCKPIT) + SetHUDMode(HUD_COCKPIT); + else if (GetHUDMode() == HUD_FULLSCREEN) + SetHUDMode(HUD_FULLSCREEN); + } +} + + + + +/////////////////////////////////////////////////////////////////// +//adds a type/id item to the inventory (returns true on success) +bool dInven_Add(Inventory *inven,int type,int id,object *parent,int aux_type,int aux_id,int flags,char *description) +{ + return inven->Add(type,id,parent,aux_type,aux_id,flags,description); +} + +//adds an object to the inventory (marked by it's objhandle) +bool dInven_AddObject(Inventory *inven,int object_handle,int flags,char *description) +{ + return inven->AddObject(object_handle,flags,description); +} + +//adds a special cased CounterMeasure into the inventory +bool dInven_AddCounterMeasure(Inventory *inven,int id,int aux_type,int aux_id,int flags,char *description) +{ + return inven->AddCounterMeasure(id,aux_type,aux_id,flags,description); +} + +// removes an item from the inventory (reduces it's count by one...if there is no more, then it goes bye-bye) +// to remove an object that was added via objhandle, then pass the objhandle +// in the 'type' parameter, and don't pass in an id. +bool dInven_Remove(Inventory *inven,int type,int id) +{ + return inven->Remove(type,id); +} + +//uses an item in the inventory (also reduces its count by one...if there is no more, then it goes bye-bye) +bool dInven_Use(Inventory *inven,int type,int id,object *parent) +{ + return inven->Use(type,id,parent); +} + +//uses an item in the inventory (given it's objhandle). +bool dInven_UseObjHandle(Inventory *inven,int objhandle,object *parent) +{ + return inven->Use(objhandle,parent); +} + +//returns how many unique type/ids are in the inventory +int dInven_Size(Inventory *inven) +{ + return inven->Size(); +} + +//returns true if there is an item in the inventory with the given type/id (or objhandle if id is -1, pass the object handle as the type parameter) +bool dInven_CheckItem(Inventory *inven,int type,int id) +{ + return inven->CheckItem(type,id); +} + +//Resets the inventory, cleaning it out +//in_game: set to true if this is being called from during gameplay +// reset_stage: +// INVRESET_ALL: Reset _EVERYTHING_ +// INVRESET_LEVELCHANGE: Remove everything except those that last across levels +// INVRESET_DEATHSPEW: Remove everything except those that do not spew (Default) +void dInven_Reset(Inventory *inven,bool in_game,int reset_stage) +{ + inven->Reset(in_game,reset_stage); +} + +//resets the position pointer in the list to the beginning +void dInven_ResetPos(Inventory *inven) +{ + inven->ResetPos(); +} + +//moves the position pointer to the next inventory item +//skip : if true then skip over non-selectable items +void dInven_NextPos(Inventory *inven,bool skip) +{ + inven->NextPos(skip); +} + +//moves the position pointer to the previous inventory item +//skip : if true then skip over non-selectable items +void dInven_PrevPos(Inventory *inven,bool skip) +{ + inven->PrevPos(skip); +} + +//returns the type/id of the item at the current position +//return true if it is a real object (meaning it was placed in the inventory +//via an object handle...so id will be -1) +bool dInven_GetPosTypeID(Inventory *inven,int &type,int &id) +{ + return inven->GetPosTypeID(type,id); +} + +//returns the aux type/id of the item +//return true if it is a real object (meaning it was placed in the inventory +//via an object handle...so id will be -1) +bool dInven_GetAuxPosTypeID(Inventory *inven,int &type,int &id) +{ + return inven->GetAuxPosTypeID(type,id); +} + +//returns the description of the item at the current position +char *dInven_GetPosDescription(Inventory *inven) +{ + return inven->GetPosDescription(); +} + +//returns the icon name of the item at the current position +char *dInven_GetPosIconName(Inventory *inven) +{ + return inven->GetPosIconName(); +} + +//returns the name of the item at the current position +char *dInven_GetPosName(Inventory *inven) +{ + return inven->GetPosName(); +} + +//return information about the current position item +//return true if it is a real object +bool dInven_GetPosInfo(Inventory *inven,ushort &iflags,int &flags) +{ + return inven->GetPosInfo(iflags,flags); +} + +//returns the count of the item at the current position +int dInven_GetPosCount(Inventory *inven) +{ + return inven->GetPosCount(); +} + +//returns true if the position pointer is at the begining of the inventory list +bool dInven_AtBeginning(Inventory *inven) +{ + return inven->AtBeginning(); +} + +//returns false if the position pointer is at the end of the inventory list +bool dInven_AtEnd(Inventory *inven) +{ + return inven->AtEnd(); +} + +//jump right to an item in the inventory +void dInven_GotoPos(Inventory *inven,int newpos) +{ + inven->GotoPos(newpos); +} + +//jump right to an item in the inventory give it's type and id +//to jump to an item that was added via object handle, pass the object +//handle in as the type, and make id -1. +void dInven_GotoPosTypeID(Inventory *inven,int type,int id) +{ + inven->GotoPos(type,id); +} + +//uses the currently selected item +bool dInven_UsePos(Inventory *inven,object *parent) +{ + return inven->UsePos(parent); +} + +//returns the 'index' position of the current position +int dInven_GetPos(Inventory *inven) +{ + return inven->GetPos(); +} + +//checks the pos, if its on a nonselectable item it will move to the next selectable (NULL if none) +void dInven_ValidatePos(Inventory *inven,bool forward) +{ + inven->ValidatePos(forward); +} + +//returns whether an item is selectable +bool dInven_IsSelectable(Inventory *inven) +{ + return inven->IsSelectable(); +} + +bool dInven_IsUsable(Inventory *inven) +{ + return inven->IsUsable(); +} + +//returns how many of an type/id is in the inventory +//for object handle items, pass the object handle in as the type and leave +//id as -1 +int dInven_GetTypeIDCount(Inventory *inven,int type,int id) +{ + return inven->GetTypeIDCount(type,id); +} + +//searches the inventory for the specified type/id, sets the pos to it +//for object handle items, pass the object handle in as the type and leave +//id as -1 +bool dInven_FindPos(Inventory *inven,int type,int id) +{ + return inven->FindPos(type,id); +} + +//gets a detailed list of information about what is in the inventory +//returns the number of items filled in. +int dInven_GetInventoryItemList(Inventory *inven,tInvenList *list,int max_amount,int *cur_sel) +{ + return inven->GetInventoryItemList(list,max_amount,cur_sel); +} diff --git a/Descent3/DllWrappers.h b/Descent3/DllWrappers.h new file mode 100644 index 000000000..cfc51b0f6 --- /dev/null +++ b/Descent3/DllWrappers.h @@ -0,0 +1,162 @@ +#ifndef DLLWRAPPERS_H_ +#define DLLWRAPPERS_H_ + +#include "pserror.h" +#include "CFILE.H" +#include "gamefont.h" +#include "grdefs.h" +#include "descent.h" +#include "ddio.h" +#include "movie.h" +#include "program.h" +#include "object.h" +#include "hlsoundlib.h" +#include "Inventory.h" + +//plays a 3dsound +int D3W_Play3dSound(int sound_index, object *cur_obj, float volume = MAX_GAME_VOLUME, int flags = 0); +int D3W_Play2dSound(int sound_index, float volume); +void D3W_TouchSound(int sound_index); + +//for mprintfs +void MonoPrintf(int n, char *format, ... ); +//checks a players inventory for an object type/id +bool InvCheckItem(int playernum,int type,int id); +//adds an object to a player inventory +bool InvAdd(int playernum,object *obj,bool remove=true); +//adds a type/id to a player inventory +bool InvAddTypeID(int playernum,int type,int id,int aux_type=-1,int aux_id=-1,int flags=0,char *description=NULL); +//removes a type/id from a players inventory +bool InvRemove(int playernum,int type,int id); +//returns how many of a type/id a player has in his inventory +int InvGetTypeIDCount(int playernum,int type,int id); +//Inventory Reset wrapper +void InvReset(int playernum,bool reset_all=true); +//wraps cfopen +void OpenCFILE(CFILE **handle,const char *filename,const char *mode); +//wrapper for ObjGetUltimateParent +void GetUltimateParentForObject(object **parent,object *child); +//sets an object's flag dead +void SetObjectDeadFlagDLL(object *obj,bool tell_client_to_remove=false,bool tell_clients_to_play_sound=false); +//Assert for the DLLs +void assertdll(int x,char *expression,char *file,int line); +void RetrieveUICallback(void **fn); + +// Registry database functions +bool dDatabaseRead1(const char *label, char *entry, int *entrylen); +bool dDatabaseRead2(const char *label, void *entry, int wordsize); +bool dDatabaseRead3(const char *label, bool *entry); +bool dDatabaseWrite1(const char *label, const char *entry, int entrylen); +bool dDatabaseWrite2(const char *label, int entry); +bool dObjGet(int handle,object **obj); + +// Attaches 2 objects via attach points on each. The f_used_aligned allows for an aligned connection. +// NOTE: The child always moves to the parent +bool dAttachObject(object *parent, char parent_ap, object *child, char child_ap, bool f_use_aligned=false); +// Attaches a child object to a parent object by a percent of the radius of the child. +// NOTE: The child always moves to the parent and not the reverse +bool dAttachObjectRadius(object *parent, char parent_ap, object *child, float percent_rad); +// Unattaches all children from a parent object +bool dUnattachChildren(object *parent); +// Unattaches a child from an attach point +bool dUnattachChild(object *parent, char parent_ap); +// Unattaches an object from its parent +bool dUnattachFromParent(object *child); +// Set a vector to {0,0,0} +void dvm_MakeZeroVector(vector *v); +void dvm_MakeZeroAngle(angvec *v); +void dStartFrame(int x, int y, int x2, int y2, bool clear); + +// ObjSetPos, that automatically sets the OF_MOVED_THIS_FRAME +void ObjSetPosAndMarkMoved(object *obj,vector *pos,int roomnum,matrix *orient,bool f_update_attached_children); +// SetObjectDeadFlag that automatically writes out demo data +void dSetObjectDeadFlagWDemo(object *obj,bool tell_clients_to_remove=false,bool tell_clients_to_play_sound=false); +void dSetAudioTaunts(bool enable); + +// Changes the ship_index for a given player +void ChangePlayerShipIndex(int pnum,int ship_index); + +//adds a type/id item to the inventory (returns true on success) +bool dInven_Add(Inventory *inven,int type,int id,object *parent=NULL,int aux_type=-1,int aux_id=-1,int flags=0,char *description=NULL); +//adds an object to the inventory (marked by it's objhandle) +bool dInven_AddObject(Inventory *inven,int object_handle,int flags=0,char *description=NULL); +//adds a special cased CounterMeasure into the inventory +bool dInven_AddCounterMeasure(Inventory *inven,int id,int aux_type=-1,int aux_id=-1,int flags=0,char *description=NULL); +// removes an item from the inventory (reduces it's count by one...if there is no more, then it goes bye-bye) +// to remove an object that was added via objhandle, then pass the objhandle +// in the 'type' parameter, and don't pass in an id. +bool dInven_Remove(Inventory *inven,int type,int id=-1); +//uses an item in the inventory (also reduces its count by one...if there is no more, then it goes bye-bye) +bool dInven_Use(Inventory *inven,int type,int id,object *parent=NULL); +//uses an item in the inventory (given it's objhandle). +bool dInven_UseObjHandle(Inventory *inven,int objhandle,object *parent=NULL); +//returns how many unique type/ids are in the inventory +int dInven_Size(Inventory *inven); +//returns true if there is an item in the inventory with the given type/id (or objhandle if id is -1, pass the object handle as the type parameter) +bool dInven_CheckItem(Inventory *inven,int type,int id=-1); +//Resets the inventory, cleaning it out +//in_game: set to true if this is being called from during gameplay +// reset_stage: +// INVRESET_ALL: Reset _EVERYTHING_ +// INVRESET_LEVELCHANGE: Remove everything except those that last across levels +// INVRESET_DEATHSPEW: Remove everything except those that do not spew (Default) +void dInven_Reset(Inventory *inven,bool in_game,int reset_stage=INVRESET_DEATHSPEW); +//resets the position pointer in the list to the beginning +void dInven_ResetPos(Inventory *inven); +//moves the position pointer to the next inventory item +//skip : if true then skip over non-selectable items +void dInven_NextPos(Inventory *inven,bool skip=false); +//moves the position pointer to the previous inventory item +//skip : if true then skip over non-selectable items +void dInven_PrevPos(Inventory *inven,bool skip=false); +//returns the type/id of the item at the current position +//return true if it is a real object (meaning it was placed in the inventory +//via an object handle...so id will be -1) +bool dInven_GetPosTypeID(Inventory *inven,int &type,int &id); +//returns the aux type/id of the item +//return true if it is a real object (meaning it was placed in the inventory +//via an object handle...so id will be -1) +bool dInven_GetAuxPosTypeID(Inventory *inven,int &type,int &id); +//returns the description of the item at the current position +char *dInven_GetPosDescription(Inventory *inven); +//returns the icon name of the item at the current position +char *dInven_GetPosIconName(Inventory *inven); +//returns the name of the item at the current position +char *dInven_GetPosName(Inventory *inven); +//return information about the current position item +//return true if it is a real object +bool dInven_GetPosInfo(Inventory *inven,ushort &iflags,int &flags); +//returns the count of the item at the current position +int dInven_GetPosCount(Inventory *inven); +//returns true if the position pointer is at the begining of the inventory list +bool dInven_AtBeginning(Inventory *inven); +//returns false if the position pointer is at the end of the inventory list +bool dInven_AtEnd(Inventory *inven); +//jump right to an item in the inventory +void dInven_GotoPos(Inventory *inven,int newpos); +//jump right to an item in the inventory give it's type and id +//to jump to an item that was added via object handle, pass the object +//handle in as the type, and make id -1. +void dInven_GotoPosTypeID(Inventory *inven,int type,int id); +//uses the currently selected item +bool dInven_UsePos(Inventory *inven,object *parent=NULL); +//returns the 'index' position of the current position +int dInven_GetPos(Inventory *inven); +//checks the pos, if its on a nonselectable item it will move to the next selectable (NULL if none) +void dInven_ValidatePos(Inventory *inven,bool forward=true); +//returns whether an item is selectable +bool dInven_IsSelectable(Inventory *inven); +bool dInven_IsUsable(Inventory *inven); +//returns how many of an type/id is in the inventory +//for object handle items, pass the object handle in as the type and leave +//id as -1 +int dInven_GetTypeIDCount(Inventory *inven,int type,int id); +//searches the inventory for the specified type/id, sets the pos to it +//for object handle items, pass the object handle in as the type and leave +//id as -1 +bool dInven_FindPos(Inventory *inven,int type,int id=-1); +//gets a detailed list of information about what is in the inventory +//returns the number of items filled in. +int dInven_GetInventoryItemList(Inventory *inven,tInvenList *list,int max_amount,int *cur_sel); + +#endif diff --git a/Descent3/Game2DLL.cpp b/Descent3/Game2DLL.cpp new file mode 100644 index 000000000..3bbfa0dce --- /dev/null +++ b/Descent3/Game2DLL.cpp @@ -0,0 +1,863 @@ +#include "pstypes.h" +#include "pserror.h" +#include "game.h" +#include "game2dll.h" +#include "room.h" +#include "object.h" +#include "terrain.h" +#include "player.h" +#include "mono.h" +#include "hud.h" +#include "Inventory.h" +#include "multi_server.h" +#include "ship.h" +#include "DllWrappers.h" +#include "soundload.h" +#include "spew.h" +#include "objinfo.h" +#include "module.h" +#include "localization.h" +#include "weapon.h" +#include "voice.h" +#include "gametexture.h" +#include "Mission.h" +#include "damage.h" +#include "ui.h" +#include "newui.h" +#include "multi_dll_mgr.h" +#include "controls.h" +#include "gameloop.h" +#include "gamesequence.h" +#include "dedicated_server.h" +#include "attach.h" +#include "PilotPicsAPI.h" +#include "vclip.h" +#include "osiris_dll.h" +#include "manage.h" +#include "PHYSICS.H" +#include "collide.h" +#include "render.h" +#include "audiotaunts.h" +#include "polymodel.h" +#include "osiris_share.h" +#include "viseffect.h" +#include "ObjScript.h" +#include "args.h" + +void SelectNextCameraView(int window); +#define NUM_CAMERA_VIEWS 3 +extern int Camera_view_mode[NUM_CAMERA_VIEWS]; + + +// Osiris_CreateModuleInitStruct +// Purpose: +// This function initializes a Module Init Struct with all the needed data to get sent +// to the module during initialization. +void Osiris_CreateModuleInitStruct(tOSIRISModuleInit *mi); +module GameDLLHandle = {NULL}; +extern ddgr_color Player_colors[]; +typedef struct +{ + int *objs; + int *rooms; + int *terrain; + int *players; + int *netgame; + int *netplayers; + int *ships; + int *weapons; + int *Current_mission; + int *GameTextures; + int *GameVClips; + int *fp[450]; // function pointers + int *vp[50]; // variable pointers + tOSIRISModuleInit *osiris_functions; +} game_api; +// The exported DLL function call prototypes +#if defined(__LINUX__) +typedef void DLLFUNCCALL(*DLLGameCall_fp )( int eventnum, dllinfo *data ); +typedef void DLLFUNCCALL(*DLLGameInit_fp )(int *api_func,ubyte *all_ok,int num_teams_to_use); +typedef void DLLFUNCCALL(*DLLGameClose_fp )(); +typedef void DLLFUNCCALL(*DLLGameGetName_fp )(char *buffer,int maxsize ); +typedef void DLLFUNCCALL(*DLLGameGetDesc_fp )(char **buffer,int maxsize,int lines); +typedef void DLLFUNCCALL(*DLLGetGameInfo_fp )(tDLLOptions *options); +#else +typedef void( DLLFUNCCALL *DLLGameCall_fp )( int eventnum, dllinfo *data ); +typedef void( DLLFUNCCALL *DLLGameInit_fp )(int *api_func,ubyte *all_ok,int num_teams_to_use); +typedef void( DLLFUNCCALL *DLLGameClose_fp )(); +typedef void( DLLFUNCCALL *DLLGameGetName_fp )(char *buffer,int maxsize ); +typedef void( DLLFUNCCALL *DLLGameGetDesc_fp )(char **buffer,int maxsize,int lines); +typedef void( DLLFUNCCALL *DLLGetGameInfo_fp )(tDLLOptions *options); +#endif +DLLGameCall_fp DLLGameCall=NULL; +DLLGameInit_fp DLLGameInit=NULL; +DLLGameClose_fp DLLGameClose=NULL; +DLLGetGameInfo_fp DLLGetGameInfo=NULL; +dllinfo DLLInfo; +tOSIRISModuleInit Multi_d3m_osiris_funcs; +char Multi_game_dll_name[_MAX_PATH*2]; +#define GAMEDLL_FUNCPOINTERS 400 +#ifdef _DEBUG +#define NEXTFP do{i++;if(i>=GAMEDLL_FUNCPOINTERS)Int3();}while(0); api->fp[i] +#else +#define NEXTFP i++; api->fp[i] +#endif + +static void DUMMYrend_DrawScaledBitmap (int x1,int y1,int x2,int y2,int bm,float u0,float v0,float u1,float v1,float zval,int color,float *alphas) +{ + ASSERT( zval >= 0.0f && zval <= 1.0f ); + rend_DrawScaledBitmap( x1, y1, x2, y2, bm, u0, v0, u1, v1, color, alphas ); +} + +void GetGameAPI (game_api *api) +{ + api->objs=(int *)Objects; + api->rooms=(int *)Rooms; + api->terrain=(int *)Terrain_seg; + api->players=(int *)Players; + api->netgame=(int *)&Netgame; + api->netplayers=(int *)&NetPlayers; + api->ships = (int *)&Ships; + api->weapons = (int *)&Weapons; + api->Current_mission = (int *)&Current_mission; + api->GameTextures = (int *)GameTextures; + api->GameVClips = (int *)GameVClips; + // Fill in function pointers here. The order here must match the order on the + // DLL side + int i = -1; //start at -1 because it increments right away + + NEXTFP=(int *)AddHUDMessage; + NEXTFP=(int *)MultiSendClientExecuteDLL; + NEXTFP=(int *)FindObjectIDName; + NEXTFP=(int *)GetGoalRoomForTeam; + NEXTFP=(int *)SetMaxTeams; + NEXTFP=(int *)ComputeRoomCenter; + NEXTFP=(int *)ObjCreate; + NEXTFP=(int *)MultiSendObject; + NEXTFP=(int *)MultiPaintGoalRooms; + NEXTFP=(int *)MultiSendSpecialPacket; + NEXTFP=(int *)IncTeamScore; + NEXTFP=(int *)MonoPrintf; + NEXTFP=(int *)ObjSetPos; + NEXTFP=(int *)InvCheckItem; + NEXTFP=(int *)InvAddTypeID; + NEXTFP=(int *)InvRemove; + NEXTFP=(int *)PlayerSetLighting; + NEXTFP=(int *)FindShipName; + NEXTFP=(int *)PlayerSetRotatingBall; + NEXTFP=(int *)ChangePlayerShipIndex; + NEXTFP=(int *)InvGetTypeIDCount; + NEXTFP=(int *)D3W_Play3dSound; + NEXTFP=(int *)FindSoundName; + NEXTFP=(int *)SpewCreate; + NEXTFP=(int *)SpewClearEvent; + NEXTFP=(int *)bm_AllocLoadFileBitmap; + NEXTFP=(int *)bm_FreeBitmap; + NEXTFP=(int *)DUMMYrend_DrawScaledBitmap; + NEXTFP=(int *)grtext_Printf; + NEXTFP=(int *)grtext_Flush; + NEXTFP=(int *)grtext_SetColor; + NEXTFP=(int *)grtext_SetFancyColor; + NEXTFP=(int *)grtext_SetAlpha; + NEXTFP=(int *)grtext_GetAlpha; + NEXTFP=(int *)grtext_SetFont; + NEXTFP=(int *)grtext_GetFont; + NEXTFP=(int *)grtext_GetTextLineWidth; + NEXTFP=(int *)grfont_GetHeight; + NEXTFP=(int *)grtext_CenteredPrintf; + NEXTFP=(int *)AddColoredHUDMessage; + NEXTFP=(int *)bm_w; + NEXTFP=(int *)bm_h; + NEXTFP=(int *)rend_DrawSimpleBitmap; + NEXTFP=(int *)MultiClientSendSpecialPacket; + NEXTFP=(int *)AddBlinkingHUDMessage; + NEXTFP=(int *)InvReset; + NEXTFP=(int *)AddHUDItem; + NEXTFP=(int *)RenderHUDQuad; + NEXTFP=(int *)RenderHUDText; + NEXTFP=(int *)MultiEndLevel; + NEXTFP=(int *)bm_data; + NEXTFP=(int *)bm_AllocBitmap; + NEXTFP=(int *)rend_FillRect; + NEXTFP=(int *)bm_CreateChunkedBitmap; + NEXTFP=(int *)bm_DestroyChunkedBitmap; + NEXTFP=(int *)rend_DrawChunkedBitmap; + NEXTFP=(int *)rend_DrawScaledChunkedBitmap; + NEXTFP=(int *)OpenCFILE; + NEXTFP=(int *)cfclose; + NEXTFP=(int *)cfeof; + NEXTFP=(int *)cfexist; + NEXTFP=(int *)cf_ReadBytes; + NEXTFP=(int *)cf_ReadInt; + NEXTFP=(int *)cf_ReadShort; + NEXTFP=(int *)cf_ReadByte; + NEXTFP=(int *)cf_ReadFloat; + NEXTFP=(int *)cf_ReadDouble; + NEXTFP=(int *)cf_ReadString; + NEXTFP=(int *)cf_WriteBytes; + NEXTFP=(int *)cf_WriteString; + NEXTFP=(int *)cf_WriteInt; + NEXTFP=(int *)cf_WriteShort; + NEXTFP=(int *)cf_WriteByte; + NEXTFP=(int *)cf_WriteFloat; + NEXTFP=(int *)cf_WriteDouble; + NEXTFP=(int *)cf_CopyFile; + NEXTFP=(int *)cf_Diff; + NEXTFP=(int *)MultiDisconnectPlayer; + NEXTFP=(int *)nw_GetNumbersFromHostAddress; + NEXTFP=(int *)nw_GetThisIP; + NEXTFP=(int *)CreateStringTable; + NEXTFP=(int *)DestroyStringTable; + NEXTFP=(int *)RenderHUDTextFlags; + NEXTFP=(int *)PlayerSpewInventory; + NEXTFP=(int *)PlayerSetHUDNameFOV; + NEXTFP=(int *)GetUltimateParentForObject; + NEXTFP=(int *)SetObjectDeadFlagDLL; + NEXTFP=(int *)DLLFatalError; + NEXTFP=(int *)assertdll; + NEXTFP=(int *)MultiMatchWeapon; + NEXTFP=(int *)MultiGetMatchChecksum; + NEXTFP=(int *)FindWeaponName; + NEXTFP=(int *)cf_OpenLibrary; + NEXTFP=(int *)cf_CloseLibrary; + NEXTFP=(int *)MultiSendRequestToObserve; + NEXTFP=(int *)FindTextureName; + NEXTFP=(int *)ApplyDamageToPlayer; + NEXTFP=(int *)MultiMatchGeneric; + NEXTFP=(int *)SetUITextItemText; + NEXTFP=(int *)NewUIWindowCreate; + NEXTFP=(int *)NewUIWindowDestroy; + NEXTFP=(int *)NewUIWindowOpen; + NEXTFP=(int *)NewUIWindowClose; + NEXTFP=(int *)TextCreate; + NEXTFP=(int *)EditCreate; + NEXTFP=(int *)ButtonCreate; + NEXTFP=(int *)ListCreate; + NEXTFP=(int *)ListRemoveAll; + NEXTFP=(int *)ListAddItem; + NEXTFP=(int *)ListRemoveItem; + NEXTFP=(int *)ListSelectItem; + NEXTFP=(int *)ListGetItem; + NEXTFP=(int *)ListGetSelectedIndex; + NEXTFP=(int *)EditSetText; + NEXTFP=(int *)EditGetText; + NEXTFP=(int *)DoUI; + NEXTFP=(int *)DoMessageBox; + NEXTFP=(int *)DescentDefer; + NEXTFP=(int *)NewUIGameWindowCreate; + NEXTFP=(int *)NewUIGameWindowDestroy; + NEXTFP=(int *)NewUIGameWindowOpen; + NEXTFP=(int *)NewUIGameWindowClose; + NEXTFP=(int *)HotSpotCreate; + NEXTFP=(int *)PollUI; + NEXTFP=(int *)RemoveUITextItem; + NEXTFP=(int *)CreateNewUITextItem; + NEXTFP=(int *)UIConsoleGadgetCreate; + NEXTFP=(int *)UIConsoleGadgetputs; + NEXTFP=(int *)NewUIWindowSetFocusOnEditGadget; + NEXTFP=(int *)OldEditCreate; + NEXTFP=(int *)OldListCreate; + NEXTFP=(int *)OldListRemoveAll; + NEXTFP=(int *)OldListAddItem; + NEXTFP=(int *)OldListRemoveItem; + NEXTFP=(int *)OldListSelectItem; + NEXTFP=(int *)OldListGetItem; + NEXTFP=(int *)OldListGetSelectedIndex; + NEXTFP=(int *)OldEditSetText; + NEXTFP=(int *)OldEditGetText; + NEXTFP=(int *)ToggleUICallback; + NEXTFP=(int *)SetOldEditBufferLen; + NEXTFP=(int *)NewUIWindowLoadBackgroundImage; + NEXTFP=(int *)DeleteUIItem; + NEXTFP=(int *)HotSpotSetStates; + NEXTFP=(int *)SetUICallback; + NEXTFP=(int *)RetrieveUICallback; + NEXTFP=(int *)SuspendControls; + NEXTFP=(int *)ResumeControls; + NEXTFP=(int *)ui_ShowCursor; + NEXTFP=(int *)ui_HideCursor; + NEXTFP=(int *)GameFrame; + NEXTFP=(int *)PrintDedicatedMessage; + NEXTFP=(int *)ddio_MakePath; + NEXTFP=(int *)ddio_SplitPath; + NEXTFP=(int *)D3W_Play2dSound; + NEXTFP=(int *)D3W_TouchSound; + NEXTFP=(int *)dDatabaseRead1; + NEXTFP=(int *)dDatabaseRead2; + NEXTFP=(int *)dDatabaseRead3; + NEXTFP=(int *)dDatabaseWrite1; + NEXTFP=(int *)dDatabaseWrite2; + NEXTFP=(int *)dAttachObject; + NEXTFP=(int *)dObjGet; + NEXTFP=(int *)ListSetSelectedIndex; + NEXTFP=(int *)RemoveUIBmpItem; + NEXTFP=(int *)CreateNewUIBmpItem; + NEXTFP=(int *)GetUIItemWidth; + NEXTFP=(int *)GetUIItemHeight; + NEXTFP=(int *)SliderCreate; + NEXTFP=(int *)SliderSetRange; + NEXTFP=(int *)SliderGetRange; + NEXTFP=(int *)SliderSetPos; + NEXTFP=(int *)SliderGetPos; + NEXTFP=(int *)SliderSetSelectChangeCallback; + NEXTFP=(int *)SliderSetSelectChangeCallbackWData; + NEXTFP=(int *)TextSetTitle; + NEXTFP=(int *)PPic_GetPilot; + NEXTFP=(int *)PPic_GetBitmapHandle; + NEXTFP=(int *)rend_DrawLine; + NEXTFP=(int *)rend_SetFlatColor; + NEXTFP=(int *)MultiSetLogoState; + NEXTFP=(int *)PlayerGetRandomStartPosition; + NEXTFP=(int *)InitPlayerNewShip; + NEXTFP=(int *)CheckBoxCreate; + NEXTFP=(int *)CheckBoxSetCheck; + NEXTFP=(int *)CheckBoxIsChecked; + NEXTFP=(int *)nw_GetHostAddressFromNumbers; + NEXTFP=(int *)mng_ClearAddonTables; + NEXTFP=(int *)mng_SetAddonTable; + NEXTFP=(int *)DebugBreak_callback_stop; + NEXTFP=(int *)DebugBreak_callback_resume; + NEXTFP=(int *)Int3MessageBox; + NEXTFP=(int *)GetUIItemPosition; + NEXTFP=(int *)dAttachObjectRadius; + NEXTFP=(int *)dUnattachChildren; + NEXTFP=(int *)dUnattachChild; + NEXTFP=(int *)dUnattachFromParent; + NEXTFP=(int *)vm_GetMagnitude; + NEXTFP=(int *)vm_MatrixMulVector; + NEXTFP=(int *)phys_apply_force; + NEXTFP=(int *)phys_apply_rot; + NEXTFP=(int *)vm_TransposeMatrix; + NEXTFP=(int *)vm_CrossProduct; + NEXTFP=(int *)vm_NormalizeVector; + NEXTFP=(int *)ConvertEulerToAxisAmount; + NEXTFP=(int *)ConvertAxisAmountToEuler; + NEXTFP=(int *)vm_GetMagnitudeFast; + NEXTFP=(int *)vm_MakeIdentity; + NEXTFP=(int *)dvm_MakeZeroVector; + NEXTFP=(int *)dvm_MakeZeroAngle; + NEXTFP=(int *)vm_VectorMulTMatrix; + NEXTFP=(int *)vm_MatrixMul; + NEXTFP=(int *)vm_MatrixMulTMatrix; + NEXTFP=(int *)vm_DotProduct; + NEXTFP=(int *)vm_SubVectors; + NEXTFP=(int *)vm_AddVectors; + NEXTFP=(int *)vm_AverageVector; + NEXTFP=(int *)vm_ScaleVector; + NEXTFP=(int *)vm_ScaleAddVector; + NEXTFP=(int *)vm_DivVector; + NEXTFP=(int *)vm_NormalizeVectorFast; + NEXTFP=(int *)vm_ClearMatrix; + NEXTFP=(int *)vm_AnglesToMatrix; + NEXTFP=(int *)vm_Orthogonalize; + NEXTFP=(int *)vm_VectorToMatrix; + NEXTFP=(int *)vm_VectorAngleToMatrix; + NEXTFP=(int *)vm_SinCos; + NEXTFP=(int *)vm_GetSlope; + NEXTFP=(int *)vm_GetPerp; + NEXTFP=(int *)vm_GetNormal; + NEXTFP=(int *)vm_VectorDistance; + NEXTFP=(int *)vm_VectorDistanceQuick; + NEXTFP=(int *)vm_GetNormalizedDir; + NEXTFP=(int *)vm_GetNormalizedDirFast; + NEXTFP=(int *)vm_ExtractAnglesFromMatrix; + NEXTFP=(int *)vm_DeltaAngVec; + NEXTFP=(int *)vm_DeltaAngVecNorm; + NEXTFP=(int *)vm_DistToPlane; + NEXTFP=(int *)calc_det_value; + NEXTFP=(int *)vm_MakeInverseMatrix; + NEXTFP=(int *)vm_SinCosToMatrix; + NEXTFP=(int *)vm_GetCentroid; + NEXTFP=(int *)vm_MakeRandomVector; + NEXTFP=(int *)vm_ComputeBoundingSphere; + NEXTFP=(int *)vm_GetCentroidFast; + NEXTFP=(int *)RenderHUDGetTextLineWidth; + NEXTFP=(int *)RenderHUDGetTextHeight; + NEXTFP=(int *)dStartFrame; + NEXTFP=(int *)EndFrame; + NEXTFP=(int *)ResetFacings; + NEXTFP=(int *)GameRenderWorld; + NEXTFP=(int *)GetFrameParameters; + NEXTFP=(int *)rend_SetZBufferState; + NEXTFP=(int *)rend_SetLighting; + NEXTFP=(int *)rend_SetColorModel; + NEXTFP=(int *)rend_SetTextureType; + NEXTFP=(int *)rend_DrawPolygon3D; + NEXTFP=(int *)rend_SetMipState; + NEXTFP=(int *)rend_SetFogState; + NEXTFP=(int *)rend_SetFiltering; + NEXTFP=(int *)rend_SetOverlayMap; + NEXTFP=(int *)rend_SetOverlayType; + NEXTFP=(int *)rend_ClearScreen; + NEXTFP=(int *)rend_SetPixel; + NEXTFP=(int *)rend_GetPixel; + NEXTFP=(int *)rend_FillCircle; + NEXTFP=(int *)rend_DrawCircle; + NEXTFP=(int *)rend_SetAlphaType; + NEXTFP=(int *)rend_SetAlphaValue; + NEXTFP=(int *)rend_SetWrapType; + NEXTFP=(int *)rend_SetZBias; + NEXTFP=(int *)rend_SetZBufferWriteMask; + NEXTFP=(int *)rend_GetLFBLock; + NEXTFP=(int *)rend_ReleaseLFBLock; + NEXTFP=(int *)rend_DrawLFBBitmap; + NEXTFP=(int *)rend_DrawSpecialLine; + NEXTFP=(int *)fvi_FindIntersection; + NEXTFP=(int *)fvi_QuickDistFaceList; + NEXTFP=(int *)fvi_QuickDistCellList; + NEXTFP=(int *)fvi_QuickDistObjectList; + NEXTFP=(int *)fvi_QuickRoomCheck; + NEXTFP=(int *)ObjSetPosAndMarkMoved; + NEXTFP=(int *)dSetObjectDeadFlagWDemo; + NEXTFP=(int *)taunt_AreEnabled; + NEXTFP=(int *)dSetAudioTaunts; + NEXTFP=(int *)GetRankIndex; + NEXTFP=(int *)VisEffectAllocate; + NEXTFP=(int *)VisEffectFree; + NEXTFP=(int *)VisEffectInitType; + NEXTFP=(int *)VisEffectCreate; + NEXTFP=(int *)VisEffectLink; + NEXTFP=(int *)VisEffectUnlink; + NEXTFP=(int *)VisEffectRelink; + NEXTFP=(int *)VisEffectDelete; + NEXTFP=(int *)CreateRandomSparks; + NEXTFP=(int *)CreateRandomLineSparks; + NEXTFP=(int *)VisEffectCreateControlled; + NEXTFP=(int *)CreateRandomParticles; + NEXTFP=(int *)AttachRandomNapalmEffectsToObject; + NEXTFP=(int *)InitObjectScripts; + NEXTFP=(int *)g3_StartFrame; + NEXTFP=(int *)g3_EndFrame; + NEXTFP=(int *)g3_GetViewPosition; + NEXTFP=(int *)g3_GetViewMatrix; + NEXTFP=(int *)g3_GetUnscaledMatrix; + NEXTFP=(int *)g3_StartInstanceMatrix; + NEXTFP=(int *)g3_StartInstanceAngles; + NEXTFP=(int *)g3_DoneInstance; + NEXTFP=(int *)g3_CheckNormalFacing; + NEXTFP=(int *)g3_RotatePoint; + NEXTFP=(int *)g3_ProjectPoint; + NEXTFP=(int *)g3_CalcPointDepth; + NEXTFP=(int *)g3_Point2Vec; + NEXTFP=(int *)g3_CodePoint; + NEXTFP=(int *)g3_RotateDeltaX; + NEXTFP=(int *)g3_RotateDeltaY; + NEXTFP=(int *)g3_RotateDeltaZ; + NEXTFP=(int *)g3_RotateDeltaVec; + NEXTFP=(int *)g3_AddDeltaVec; + NEXTFP=(int *)g3_DrawPoly; + NEXTFP=(int *)g3_DrawSphere; + NEXTFP=(int *)g3_CheckAndDrawPoly; + NEXTFP=(int *)g3_DrawLine; + NEXTFP=(int *)g3_DrawBitmap; + NEXTFP=(int *)g3_DrawRotatedBitmap; + NEXTFP=(int *)g3_DrawBox; + NEXTFP=(int *)g3_SetCustomClipPlane; + NEXTFP=(int *)g3_SetFarClipZ; + NEXTFP=(int *)g3_ClipPolygon; + NEXTFP=(int *)g3_FreeTempPoints; + NEXTFP=(int *)g3_GetMatrixScale ; + NEXTFP=(int *)g3_SetTriangulationTest; + NEXTFP=(int *)g3_DrawSpecialLine; + NEXTFP=(int *)g3_DrawPlanarRotatedBitmap; + NEXTFP=(int *)PlayerStopSounds; + NEXTFP=(int *)FindArg; + NEXTFP=(int *)FireWeaponFromObject; + NEXTFP=(int *)CreateAndFireWeapon; + NEXTFP=(int *)SelectNextCameraView; + NEXTFP=(int *)dInven_Add; + NEXTFP=(int *)dInven_AddObject; + NEXTFP=(int *)dInven_AddCounterMeasure; + NEXTFP=(int *)dInven_Remove; + NEXTFP=(int *)dInven_Use; + NEXTFP=(int *)dInven_UseObjHandle; + NEXTFP=(int *)dInven_Size; + NEXTFP=(int *)dInven_CheckItem; + NEXTFP=(int *)dInven_Reset; + NEXTFP=(int *)dInven_ResetPos; + NEXTFP=(int *)dInven_NextPos; + NEXTFP=(int *)dInven_PrevPos; + NEXTFP=(int *)dInven_GetPosTypeID; + NEXTFP=(int *)dInven_GetAuxPosTypeID; + NEXTFP=(int *)dInven_GetPosDescription; + NEXTFP=(int *)dInven_GetPosIconName; + NEXTFP=(int *)dInven_GetPosName; + NEXTFP=(int *)dInven_GetPosInfo; + NEXTFP=(int *)dInven_GetPosCount; + NEXTFP=(int *)dInven_AtBeginning; + NEXTFP=(int *)dInven_AtEnd; + NEXTFP=(int *)dInven_GotoPos; + NEXTFP=(int *)dInven_GotoPosTypeID; + NEXTFP=(int *)dInven_UsePos; + NEXTFP=(int *)dInven_GetPos; + NEXTFP=(int *)dInven_ValidatePos; + NEXTFP=(int *)dInven_IsSelectable; + NEXTFP=(int *)dInven_IsUsable; + NEXTFP=(int *)dInven_GetTypeIDCount; + NEXTFP=(int *)dInven_FindPos; + NEXTFP=(int *)dInven_GetInventoryItemList; + + // Variable pointers + api->vp[0]=(int *)&Player_num; + api->vp[1]=(int *)&Highest_room_index; + api->vp[2]=(int *)&Game_window_w; + api->vp[3]=(int *)&Game_window_h; + api->vp[4]=(int *)&Game_fonts; + api->vp[5]=(int *)&Frametime; + api->vp[6]=(int *)&Gametime; + api->vp[7]=(int *)&Multi_additional_damage; + api->vp[8]=(int *)&Game_interface_mode; + api->vp[9]=(int *)LocalD3Dir; + api->vp[10]=(int *)&Game_is_master_tracker_game; + api->vp[11]=(int *)Local_object_list; + api->vp[12]=(int *)Server_object_list; + api->vp[13]=(int *)&Dedicated_server; + api->vp[14]=(int *)Player_colors; + api->vp[15]=(int *)&Hud_aspect_x; + api->vp[16]=(int *)&Hud_aspect_y; + api->vp[17]=(int *)&Viewer_object; + api->vp[18]=(int *)&Render_zoom; + api->vp[19]=(int *)&Game_window_x; + api->vp[20]=(int *)&Game_window_y; + api->vp[21]=(int *)&Poly_models; + api->vp[22]=(int *)VisEffects; + api->vp[23]=(int *)&Highest_vis_effect_index; + api->vp[24]=(int *)&Multi_next_level; + api->vp[25]=(int *)&Level_info; + api->vp[26]=(int *)GameArgs; + api->vp[27]=(int *)Camera_view_mode; + api->vp[28]=(int *)Object_info; + + api->osiris_functions = &Multi_d3m_osiris_funcs; + Osiris_CreateModuleInitStruct(&Multi_d3m_osiris_funcs); + Multi_d3m_osiris_funcs.string_table = NULL; + Multi_d3m_osiris_funcs.string_count = 0; + Multi_d3m_osiris_funcs.module_identifier = 0xEDF7; + Multi_d3m_osiris_funcs.module_is_static = false; + Multi_d3m_osiris_funcs.game_checksum = Osiris_game_checksum; + Multi_d3m_osiris_funcs.script_identifier = Netgame.scriptname; +} +//Closes and deletes any tempfiles for the game module +void CloseGameModule(module *mod) +{ + //Clear out error queue + mod_GetLastError(); + mod_FreeModule(mod); + if(Multi_game_dll_name[0]!='\0'){ + //Try deleting the file now! + if(!ddio_DeleteFile(Multi_game_dll_name)){ + mprintf((0,"Couldn't delete the tmp dll")); + } + } + mod->handle = NULL; +} +//this function will load up the DLL, but not get any symbols +bool InitGameModule(char *name,module *mod) +{ + char lib_name[_MAX_PATH*2]; + char dll_name[_MAX_PATH*2]; + char tmp_dll_name[_MAX_PATH*2]; + //Make the hog filename + ddio_MakePath(lib_name,Base_directory,"netgames",name,NULL); + strcat(lib_name,".d3m"); + //Make the dll filename + #if defined (WIN32) + sprintf(dll_name,"%s.dll",name); + #elif defined (MACINTOSH) + sprintf(dll_name,"%s.msl",name); + #else + sprintf(dll_name,"%s.so",name); + #endif + + //Open the hog file + if(!cf_OpenLibrary(lib_name)) + { + ddio_MakePath(tmp_dll_name,Base_directory,"netgames",name,NULL); + strcat(tmp_dll_name,".d3m"); + Multi_game_dll_name[0] = NULL; + goto loaddll; + } + //get a temp file name + if(!ddio_GetTempFileName(Descent3_temp_directory,"d3m",tmp_dll_name)) + { + return false; + } + //Copy the DLL + if(!cf_CopyFile(tmp_dll_name,dll_name)) + { + mprintf((0,"DLL copy failed!\n")); + return false; + } + strcpy(Multi_game_dll_name,tmp_dll_name); +loaddll: + //Clear out error queue + mod_GetLastError(); + if(!mod_LoadModule(mod,tmp_dll_name)) + { + int err = mod_GetLastError(); + mprintf((0,"You are missing the DLL %s!\n",name)); + return false; + } + return true; +} +// Frees the dll if its in memory +void FreeGameDLL () +{ + if (!GameDLLHandle.handle) + return; + if (DLLGameClose) + DLLGameClose (); + Osiris_UnloadMissionModule(); + CloseGameModule(&GameDLLHandle); + DLLGameCall=NULL; + DLLGameInit=NULL; + DLLGameClose=NULL; + DLLGetGameInfo=NULL; +} +// Loads the game dll. Returns 1 on success, else 0 on failure +int LoadGameDLL (char *name,int num_teams_to_use) +{ + static int first=1; + //char lib_name[_MAX_PATH*2]; + //char dll_name[_MAX_PATH*2]; + //char tmp_dll_name[_MAX_PATH*2]; + //char dll_path_name[_MAX_PATH*2]; + //char fulldll[_MAX_PATH*2]; + //ddio_MakePath(fulldll,Base_directory,"netgames",name,NULL); + if (GameDLLHandle.handle) + FreeGameDLL(); + if(num_teams_to_use==-1) + { + int mint,maxt; + if(GetDLLNumTeamInfo(name,&mint,&maxt)) + { + //multi team game + num_teams_to_use = mint; + }else + { + //non-team game + num_teams_to_use = 1; + } + } + mprintf((0,"Loading '%s', setting up for %d teams\n",name,num_teams_to_use)); + if(!InitGameModule(name,&GameDLLHandle)) + return 0; + + //Clear out error queue + mod_GetLastError(); + DLLGameInit=(DLLGameInit_fp)mod_GetSymbol(&GameDLLHandle,"DLLGameInit",12); + if (!DLLGameInit) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLGameInit!\n")); + Int3(); + FreeGameDLL (); + return 0; + } + //Clear out error queue + mod_GetLastError(); + DLLGameCall=(DLLGameCall_fp)mod_GetSymbol(&GameDLLHandle,"DLLGameCall",8); + if (!DLLGameCall) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLGameCall!\n")); + Int3(); + FreeGameDLL (); + return 0; + } + //Clear out error queue + mod_GetLastError(); + DLLGameClose=(DLLGameClose_fp)mod_GetSymbol(&GameDLLHandle,"DLLGameClose",0); + if (!DLLGameClose) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLGameClose!\n")); + Int3(); + FreeGameDLL (); + return 0; + } + //Clear out error queue + mod_GetLastError(); + DLLGetGameInfo=(DLLGetGameInfo_fp)mod_GetSymbol(&GameDLLHandle,"DLLGetGameInfo",4); + if (!DLLGetGameInfo) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLGetGameInfo!\n")); + Int3(); + FreeGameDLL(); + return 0; + } + if (first) + { + //Jeff: Linux for some reason dies if you try to + //free a DLL/so on atexit, they should be freed + //anyway during game sequencing + //atexit (FreeGameDLL); + first=0; + } + void *api_fp; + ubyte ok = 1; + api_fp=(void *)GetGameAPI; + + DLLGameInit((int *)api_fp,&ok,num_teams_to_use); + if(!ok){ + //The DLL said no to the load + mprintf((0,"DLLGameInit returned false, couldn't init DLL\n")); + Int3(); + FreeGameDLL(); + return 0; + } + Osiris_LoadMissionModule(&GameDLLHandle,name); + + return 1; +} +// If this function is called than the DLL is to be closed, because there was an error running it +// if reason is not NULL than that is the reason why +void DLLFatalError(char *reason) +{ + mprintf((0,"============================\n")); + mprintf((0,"= DLL Fatal Error =\n")); + mprintf((0,"============================\n")); + mprintf((0,"%s\n",(reason)?reason:"")); + Netgame.flags |= NF_EXIT_NOW; + Int3(); +} +// The chokepoint function to call the dll function +void CallGameDLL (int eventnum,dllinfo *data) +{ + if (GameDLLHandle.handle && DLLGameCall) + { + data->iRet = 0; + DLLGameCall (eventnum,data); + } +} +// Call this function right after a player connects to the game to see if a player is banned +bool GameDLLIsAddressBanned(network_address *addr,char *tracker_id) +{ + ASSERT(addr); + DLLInfo.special_data = (ubyte *)addr; + // This used to be tracker_id, but storing a pointer as an int is a problem in 64 bit + // and no code ever was populating the tracker id that I know of. -Kevin + DLLInfo.iParam = 0; + DLLInfo.iRet = 0; + CallGameDLL (EVT_GAMECHECKBAN,&DLLInfo); + if(DLLInfo.iRet) + return true; + else + return false; +} +// Call this function to get the team that a connecting player should be placed on +// Callsign and network address of the player MUST be filled in at this point +// PXO id must also be set if mastertracker game +// +// Return values: +// -2: GameDLL can't determine a team (user specified that they want to handle), so pick a random team +// -1: Dedicated Server +// 0-4: Team +int GameDLLGetConnectingPlayersTeam(int slot) +{ + ASSERT(slot>=0 && slot=0); + ASSERT(requirements); + tDLLOptions opt; + if(!GetDLLGameInfo(name,&opt)) + { + mprintf((0,"Unable to get %s's requirements\n",name)); + return -1; + } + strncpy(requirements,opt.requirements,buflen-1); + requirements[buflen-1] = '\0'; + unsigned int opt_req_len = strlen(opt.requirements); + if(opt_req_len>strlen(requirements)) + { + //too small of a buffer! + mprintf((0,"Too small of a buffer to fill in all requirements!...need %d\n",opt_req_len+1)); + Int3(); + //cut off last requirement (which is shortened) + char *p = strrchr(requirements,','); + if(p) + { + *p = '\0'; + }else + { + *requirements = '\0'; + } + } + //go through requirements and count em + char *p = requirements; + int count = 0; + while(*p) + { + if(*p==',') + count++; + p++; + } + count++; + return count; +} +// Call this function to get information about the number of teams for the game +// Returns true if it's a team game...false if it's a non-team game. +// If it returns true, then min is filled in with the minumum number of teams needed for the game +// and max is filled in with the maximum number of teams for the game...if they are the same +// value, then it is the only number of teams supported. +bool GetDLLNumTeamInfo(char *name,int *mint,int *maxt) +{ + tDLLOptions dllo; + if(!GetDLLGameInfo(name,&dllo)) + { + *mint = 1; + *maxt = 1; + return false; + } + if(!(dllo.flags&DOF_MAXTEAMS)) + { + *mint = 1; + *maxt = 1; + return false; + } + *maxt = (dllo.max_teams==0||dllo.max_teams==1||dllo.max_teams<0)?1:min(dllo.max_teams,4); + if((*maxt)==1) + { + *mint = 1; + }else + { + *mint = 2; + } + if(dllo.flags&DOF_MINTEAMS && dllo.min_teams>=0) + { + *mint = (dllo.min_teams==0||dllo.min_teams==1)?1:min(*maxt,dllo.min_teams); + } + return ((*maxt)==1)?false:true; +} diff --git a/Descent3/GameCheat.cpp b/Descent3/GameCheat.cpp new file mode 100644 index 000000000..584d8468d --- /dev/null +++ b/Descent3/GameCheat.cpp @@ -0,0 +1,640 @@ +/* +* $Logfile: /DescentIII/Main/GameCheat.cpp $ +* $Revision: 32 $ +* $Date: 10/20/99 5:40p $ +* $Author: Chris $ +* +* Code for in-game cheats +* +* $Log: /DescentIII/Main/GameCheat.cpp $ + * + * 32 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 31 7/15/99 6:38p Jeff + * added outlinemode cheat code and rendering stats cheat + * + * 30 5/21/99 10:43p Jeff + * don't delete guidebot on kill robots cheat + * + * 29 5/10/99 1:00a Jeff + * release cheat codes + * + * 28 5/09/99 10:48p Jason + * change game cheats slightly + * + * 27 5/04/99 9:27p Jeff + * quad super lasers on cheat + * + * 26 5/04/99 8:45p Jeff + * quad super lasers on cheat + * + * 25 5/01/99 12:53a Jeff + * made fullmap cheat a 'cheater' cheat + * + * 24 4/30/99 8:31p Jeff + * added fullmap cheat + * + * 23 4/19/99 12:09a Matt + * Added Teletubbies cheat. + * + * 22 3/05/99 5:36p Kevin + * Changed framrate cheat back to 'frametime' + * + * 21 3/04/99 7:08p Jeff + * only give max ammo on weapon cheat + * + * 20 3/04/99 3:20p Jeff + * include only whats allowed for oem + * + * 19 3/03/99 4:07p Jeff + * new oem cheat codes + * + * 18 2/28/99 9:28p Jeff + * + * 17 2/24/99 11:21a Jason + * Fixed bugs for OEM build + * + * 16 2/10/99 6:55p Jeff + * added proxmines to the game + * + * 15 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 14 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 13 1/29/99 2:08p Jeff + * localization + * + * 12 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 11 12/15/98 4:28p Jeff + * added mission data information to the pilot files to save what the + * highest level they achieved on a mission is. Added level select dialog + * (not hooked up) and level warp cheat. + * + * 10 12/11/98 10:36a Jason + * fixed external + * + * 9 10/20/98 6:33p Jeff + * added cool texture cheat (badtexture) + * + * 8 10/19/98 10:23p Jeff + * added cheats to start/stop rt log profiling + * + * 7 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 6 10/18/98 9:24p Jeff + * fixed frametime cheat + * + * 5 10/17/98 6:08p Jeff + * weapon cheat gives quad + * + * 4 10/15/98 1:32p Jeff + * added suicide cheat + * + * 3 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 2 10/11/98 3:01a Jeff + * moved from TelCom into here +* +* $NoKeywords: $ +*/ + +#include "player.h" +#include "object.h" +#include "hud.h" +#include "weapon.h" +#include "hlsoundlib.h" +#include "game.h" +#include "ddio.h" +#include "soundload.h" +#include "sounds.h" +#include "damage.h" +#include "rtperformance.h" +#include "object_lighting.h" +#include "gamesequence.h" +#include "multi.h" +#include "stringtable.h" +#include "ship.h" +#include "render.h" +#include "renderer.h" + +#define CHEATSPOT 14 +bool IsCheater = false; +bool Display_renderer_stats = false; +ubyte Outline_release_mode = 0; + +char CheatBuffer[]="AAAAAAAAAAAAAAA"; +char OldCheatBuffer[]="AAAAAAAAAAAAAAA"; +extern bool Force_one_texture; +extern ubyte AutomapVisMap[MAX_ROOMS]; + +char *LamerCheats[]={ "?E9FI=()", // gabbagabbahey + "=-OQESN1", // motherlode + "COQB9T@", // eatangelos + ",/,JCQ/7", // alifalafel + "-4ACVPFF", // delshiftb + "-JQ\"B(F$",// farmerjoe + "TIHB;B&H", // freespace + "=!9:S>!?", // gowingnut + ":3FGG1MP", // honestbob + "$'R&?FTU", // rockrgrl + "EP=$>I<%", // blimpiebest + "#+'T50IS", // mightyaphrodite + "J4D!JG56", // freeitup + "-KAM'2:)", // zodisgod + "U1/C;>AH", // chowyunfat + "JRF0H(!P", // phantasm + "!@3/L5KE", // blackdove + "F1*GOG2(", // mortality + "6\"BI<-BE", // monkeydance + "#/;=3J8F", // nosferatu + "J#.DEJCT", // pandorasbox +}; +#define N_LAMER_CHEATS (sizeof(LamerCheats) / sizeof(*LamerCheats)) + +/* +Demo Cheat Codes +================ +BLIMPIEBEST = all weapons +MIGHTYAPHRODITE = cloak on/off +FREEITUP = all robots dead +YUMMYFUNYON = invulnerable on/off +FRAMETIME = framerate +LONGCHIMP = 3rd person view +BADTEXTURE = destroy all textures, use bad bitmap +ZODISGOD = suicide +*/ + +/* +OEM Cheat Codes +=============== +CHOWYUNFAT = all weapons +PHANTASM = cloak on/off +BLACKDOVE = all robots dead +MORTALITY = invulnerable on/off +MRSHOWBIZ = framerate +MONKEYDANCE = 3rd person view +GARBARGE = destroy all textures, use bad bitmap +NOSFERATU = suicide +PANDORASBOX = level warp +*/ + +/* +Release Cheat Codes +=================== +IVEGOTIT = all weapons +TESTICUS = cloak on/off +DEADOFNIGHT = all robots dead +BURGERGOD = invulnerable on/off +FRAMELENGTH = framerate +BYEBYEMONKEY = 3rd person view +SHENANIGANS = destroy all textures, use bad bitmap +TUBERACER = suicide +MORECLANG = level warp +TELETUBBIES = TeletubbiesCheat +TREESQUID = Fullmap +RENDERSTAT = Rendering stats +OUTLINEM = Cycle outline modes +*/ + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// NOTE TO ADD A CHEATCODE +// GRAB THE LATEST VERSION OF WINCRYPT FROM JEFF +// BELOW, ONLY THE FIRST QUOTED STRING PER CHEAT MATTERS +// THE OTHER QUOTED STRINGS ARE TO TOSS THE HACKERS +// SOME FUN. +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +char *WeaponsCheat ="BH;URJH,""8DS#"; +char *CloakCheat ="L_QM[=^)""@-:!("; +char *KillRobotsCheat ="KMGKKG4D""~"; +char *InvulnCheat ="BBG\\Q90L""}FQC.>$"; +char *FrametimeCheat =";;24E1)]""=="; +char *CameraCheat ="X2OHN*2(""K:("; +char *FullmapCheat ="4V\\9EI:'""\"#$NNW@"; +char *OutlineModeCheat ="*HAFW1ZI"" (-'\"#"; +char *PolygonCountCheat ="MMQ6TXHU""hj@(x`"; + +#ifdef USE_RTP +char *StartLogCheat =";IT.*+E3""%^VXKS;JKS"; //startrtlog +char *StopLogCheat ="GE8FHH6*""_=JJDK"; //stoprtlog +#endif + +// these are cheat codes from the demo/oem carried over as they aren't cheats +char *OLDDEMO_FrametimeCheat= "B'&>;6V4"; // frametime +char *OLDOEM_FrametimeCheat = "F+NDOJ'<"; // mrshowbiz +char *OLDDEMO_CoolTextures = "QG1EG+H+"; // badtexture +char *OLDOEM_CoolTextures = "5UU(I'0="; // garbarge + +char *jcrypt (char *plainstring) +{ + int i,t,len; + static char cryptstring[20]; + + len=strlen (plainstring); + if (len>8) + len=8; + + for (i=0;i8) + len=8; + + for (i=0;ieffect_info->type_flags & EF_CLOAKED) { + MakeObjectVisible(Player_object); + AddHUDMessage(TXT_CLOAKCHEATOFF); + }else { + MakeObjectInvisible(Player_object,10000); + AddHUDMessage(TXT_CLOAKCHEATON); + if(snd_cheat!=-1) + Sound_system.Play2dSound(snd_cheat); + } + IsCheater = true; + Players[Player_num].score = 0; + } + + if (!(memcmp (cryptstring,WeaponsCheat,8))){ + + #ifdef DEMO + Players[Player_num].weapon_flags = HAS_FLAG(LASER_INDEX)|HAS_FLAG(VAUSS_INDEX)|HAS_FLAG(SUPER_LASER_INDEX)| + HAS_FLAG(NAPALM_INDEX)|HAS_FLAG(CONCUSSION_INDEX)|HAS_FLAG(HOMING_INDEX)| + HAS_FLAG(FRAG_INDEX)|HAS_FLAG(FLARE_INDEX); + #elif OEM + Players[Player_num].weapon_flags = HAS_FLAG(LASER_INDEX)|HAS_FLAG(VAUSS_INDEX)|HAS_FLAG(MICROWAVE_INDEX)| + HAS_FLAG(PLASMA_INDEX)|HAS_FLAG(SUPER_LASER_INDEX)|HAS_FLAG(MASSDRIVER_INDEX)|HAS_FLAG(NAPALM_INDEX)| + HAS_FLAG(CONCUSSION_INDEX)|HAS_FLAG(HOMING_INDEX)|HAS_FLAG(IMPACTMORTAR_INDEX)|HAS_FLAG(FRAG_INDEX)| + HAS_FLAG(GUIDED_INDEX)|HAS_FLAG(NAPALMROCKET_INDEX)|HAS_FLAG(CYCLONE_INDEX); + + #else + Players[Player_num].weapon_flags=0xFFFFFFFF; + #endif + + + int i; + for (i=0;i-1) + { + Players[Player_num].inventory.Add(OBJ_POWERUP,quad_id,NULL,-1,-1,INVF_MISSIONITEM|INVF_TIMEOUTONSPEW); + } + + int c_id,b_id,s_id,g_id,p_id; + int c_pid,b_pid,s_pid,g_pid,p_pid; + int amount; +#ifdef DEMO + c_id = -1; + b_id = -1; + s_id = -1; + g_id = FindWeaponName("Gunboy"); + p_id = -1; + + c_pid = -1; + b_pid = -1; + s_pid = -1; + g_pid = FindObjectIDName("gunboypowerup"); + p_pid = -1; + + amount = 12; +#elif OEM + c_id = -1; + b_id = FindWeaponName("Betty"); + s_id = FindWeaponName("SeekerMine"); + g_id = FindWeaponName("Gunboy"); + p_id = -1; + + c_pid = -1; + b_pid = FindObjectIDName("Betty4pack"); + s_pid = FindObjectIDName("Seeker3pack"); + g_pid = FindObjectIDName("gunboypowerup"); + p_pid = -1; + + amount = 12; +#else + c_id = FindWeaponName("Chaff"); + b_id = FindWeaponName("Betty"); + s_id = FindWeaponName("SeekerMine"); + g_id = FindWeaponName("Gunboy"); + p_id = FindWeaponName("ProxMine"); + + c_pid = FindObjectIDName("chaff"); + b_pid = FindObjectIDName("Betty4pack"); + s_pid = FindObjectIDName("Seeker3pack"); + g_pid = FindObjectIDName("gunboypowerup"); + p_pid = FindObjectIDName("ProxMinepowerup"); + + amount = 30; + +#endif + + if(c_pid==-1) + c_pid = FindObjectIDName("Shield"); + if(b_pid==-1) + b_pid = FindObjectIDName("Energy"); + if(s_pid==-1) + s_pid = FindObjectIDName("Shield"); + if(g_pid==-1) + g_pid = FindObjectIDName("Energy"); + if(p_pid==-1) + p_pid = FindObjectIDName("Shield"); + + object *objp = &Objects[Players[Player_num].objnum]; + for(i=0;i to limit frame and fixed timer bug and old + * framerate limiter code. + * + * 330 2/16/99 5:24p Kevin + * Converted timer to use 64bit int. + * + * 329 2/15/99 7:49p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 328 2/13/99 12:31a Jeff + * debug key fixups (now in alphabetical order) + * + * 327 2/12/99 6:47p Jeff + * removed tilde as telcom key + * + * 326 2/12/99 3:54a Jeff + * added function to restart a level, and a cheat key to do so (del-alt-e) + * + * 325 2/10/99 6:55p Jeff + * added proxmines to the game + * + * 324 2/10/99 2:41p Chris + * Added debug info + * + * 323 2/09/99 12:09p Jason + * rewriting indoor engine... + * + * 322 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 321 2/09/99 1:29a Jeff + * added code to let D3 process multiplayer games in the background if + * game is not in focus. + * + * 320 2/08/99 6:39p Jason + * first pass at new indoor engine + * + * 319 2/05/99 7:23p Kevin + * OEM Changes + * + * 318 1/31/99 8:48p Jeff + * new in game cinematics system finished + * + * 317 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 316 1/31/99 3:44p Matt + * Streamlined game sequencing + * + * 315 1/30/99 4:42p Jeff + * changed keys for telcom + * + * 314 1/29/99 6:29p Jason + * first pass at adding bumpmaps + * + * 313 1/29/99 6:29p Samir + * implemented hud scrollback for hud messages. + * + * 312 1/28/99 6:17p Jason + * added markers + * + * 311 1/28/99 2:22p Samir + * simplified music system for D3. + * + * 310 1/28/99 11:32a Jason + * added marker cameras + * + * 309 1/27/99 6:54p Keneta + * fixed merge problwm + * + * 308 1/27/99 6:08p Jason + * first pass at markers + * + * 307 1/27/99 6:05p Samir + * added scrollback for game messages on HUD. + * + * 306 1/27/99 6:02p Jeff + * test keys for audio taunts + * + * 305 1/22/99 5:15p Jeff + * added a key to toggle osiris debug messages + * + * 304 1/22/99 4:06p Jeff + * added hud messages that can be sent to just teammates or individual + * people + * + * 303 1/21/99 7:02p Jason + * took out liquid effect from key c + * + * 302 1/21/99 3:34p Jason + * added liquid code + * + * 301 1/20/99 3:45a Jeff + * added call to update any in-game cinematics (boss introduction + * cinematics) + * + * 300 1/19/99 11:25a Jason + * added room (fog and wind) changing functions + * + * 299 1/15/99 7:52p Chris + * Updated ObjSetPos() to include a f_update_attach_children flag + * + * 298 1/15/99 7:16p Kevin + * Added GameGauge Configuration & code + * + * 297 1/13/99 12:43p Jason + * fixed flickering exit menu screen + * + * 296 1/11/99 4:08p Jason + * added multiplayer taunt macros + * + * 295 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 294 1/04/99 12:48p Jason + * fixed clear screen problems with triple buffer + * + * 293 12/21/98 9:44a Chris + * Ambient life stuff is all tied in to game sequencing + * + * 292 12/18/98 10:42a Jeff + * call to process osiris's timers added in GameFrame + * + * 291 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 290 12/16/98 12:04p Kevin + * GameSpy! + * + * 289 12/15/98 6:41p Jason + * took out mprintf + * + * 288 12/07/98 11:30a Kevin + * Added some features from the 1.1 demo patch + * + * 287 12/07/98 10:35a Jason + * added adjustable FOV + * + * 286 12/01/98 3:34p Matt + * Got rear view working. + * + * 285 11/24/98 3:57p Kevin + * Demo system immprovements + * + * 284 11/24/98 10:41a Kevin + * Demo system + * + * 283 11/23/98 4:52p Kevin + * Demo system enhancments + * + * 282 11/19/98 5:40p Kevin + * Demo system + * + * 281 11/19/98 12:43p Samir + * added functions to start ui menu immediately without using + * Game_interface_mode + * + * 280 11/18/98 2:54p Jason + * added snow effect + * + * 279 11/16/98 4:15p Samir + * added death. + * + * 278 11/13/98 2:28p Samir + * new music system. + * + * 276 11/10/98 5:18p Jeff + * removed force feedback test keys + * + * 275 11/09/98 3:08p Kevin + * Added demo code + * + * 274 11/06/98 7:00p Jeff + * first round of new force feedback installed + * + * 273 11/05/98 7:35p Samir + * StartTime function altered to return if timer paused == 0;. + * + * 272 11/05/98 5:54p Kevin + * Demo system work + * + * 271 11/03/98 6:43p Jeff + * new low-level & high level Force Feedback system implemented, handles + * window losing focus, etc. + * + * 270 10/29/98 11:08a Samir + * don't allow sizing of letterbox screens. + * + * 269 10/24/98 2:16p Samir + * processbuttons doesn't call lowlevel input code anymore. + * + * 268 10/21/98 11:14a Samir + * added generic code to skip rendering while in game controller config or + * telcom. + * + * 267 10/20/98 12:42p Matt + * Made the small views work on the cockpit. + * + * 266 10/19/98 7:51p Kevin + * performance testing + * + * 265 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 264 10/19/98 2:48p Jeff + * changed define for rtp performance...just define USE_RTP in + * rtperformance.h in order to enable it + * + * 263 10/19/98 1:10p Kevin + * Took demo recording out of demo build + * + * 262 10/19/98 11:57a Chris + * Update the sound system to use the import volume + * + * 261 10/18/98 7:26p Samir + * added ability to poll mouse and joystick for input in death screen. + * clear screen when toggling console screen. + * + * 260 10/17/98 5:50p Jeff + * hooked in rtperformance (run time performance) library + * + * 259 10/17/98 12:46p Kevin + * Beta 4 fixes + * + * 258 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 257 10/16/98 2:44p Kevin + * working on getting demo compiling + * + * 256 10/16/98 2:24p Jason + * changes for the demo + * + * 255 10/16/98 12:44a Jeff + * created frametime info log stuff + * + * 254 10/15/98 8:19p Samir + * damn stupid bug when growing window. + * + * 253 10/14/98 6:41p Samir + * if in UI system, clear screen every frame to prevent hall of mirrors. + * + * 252 10/14/98 4:27p Samir + * must press spacebar to quit death. + * + * 251 10/12/98 10:16a Matt + * Took out D3XDebugIO() call. which didn't really do anything, so I could + * use the backquote key elsewhere. + * + * 250 10/11/98 3:00a Jeff + * TelCom now calls GameFrame() if it's being run in multiplayer, so calls + * were put into GameFrame to handle certain cases when it's in TelCom + * + * 249 10/09/98 12:22p Jeff + * DemoCheats() + * + * 248 10/08/98 3:38p Jeff + * DEL-E doesn't work in multiplayer games + * + * 247 10/08/98 3:12p Matt + * Fixed toggle outline key, and added key to turn on all outline options. + * + * 246 10/06/98 5:45p Kevin + * Added new configuration for demo + * + * 245 10/06/98 3:07p Jeff + * fixed del-x cheat code so it adds counter measures right + * + * 244 10/06/98 2:59p Jason + * added timedemo function + * + * 243 10/05/98 7:23p Jason + * implemented destroyable lights (first draft) + * + * 242 10/05/98 12:09p Kevin + * Converted projects to VC6 and demo file stuff added + * + * 241 10/05/98 11:08a Jason + * implemented player message log + * + * 238 9/29/98 10:44a Jeff + * renamed gunbouy to gunboy + * + * 237 9/25/98 4:50p Jeff + * + * 236 9/24/98 12:57p Jason + * more state limited optimizations + * + * 235 9/22/98 12:40p Matt + * Cleaned up the object per-frame processing code. + * + * 234 9/21/98 11:10a Jeff + * Calls to init/close forcefeedback + * + * 233 9/18/98 7:38p Jeff + * + * 232 9/18/98 1:27p Jason + * cleaned up renderer initting + * + * 231 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 229 9/16/98 5:38p Jason + * made dedicated server bail on key events + * + * 228 9/16/98 5:10p Jason + * added first pass at thrid-person camera system + * + * 227 9/14/98 6:28p Jason + * first pass at getting dedicated server working + * + * 226 9/10/98 12:01p Chris + * Made matcen:DoRenderFrame() functional + * + * 225 9/08/98 4:56p Chris + * Matcens rev.5 almost functional + * + * 224 9/03/98 12:11p Chris + * Adding matcen support + * + * 223 9/01/98 4:11p Samir + * moved screenshot code from gameloop to game.cpp + * + * 222 8/31/98 6:50p Jeff + * made inventory and countermeasure keys customizable + * + * 221 8/31/98 5:44p Matt + * Added a key (Del-N) to restart a level + * + * 220 8/26/98 4:30p Jason + * added directional lights (headlight for player) + * + * 219 8/25/98 11:27p Matt + * Added key to toggle fog. + * + * 218 8/25/98 3:08p Samir + * Del-F10 does ship (and cockpit) transitions. + * + * 217 8/25/98 1:40p Jason + * fixed message bug + * + * 216 8/25/98 12:36p Jason + * added specular debug key + * + * 215 8/24/98 6:07p Samir + * change cockpits with ships. + * + * 214 8/24/98 10:40a Jason + * fixed some rendering problems + * + * 213 8/18/98 5:54p Jeff + * added countermeasures and afterburner when you DEL-X + * + * 212 8/18/98 3:02p Samir + * added game saving and loading. + * + * 211 8/17/98 6:40p Matt + * Added ambient sound system + * + * 210 8/16/98 12:20a Jeff + * moved out actual energy->shields to player function...keypress still + * handled here though + * + * 209 8/15/98 11:23p Matt + * Re-added keyboard flush when the game dll is swallowing keys. + * + * 208 8/15/98 10:53p Matt + * Rewrote key handling routines to deal with keys pressed while dead, and + * to generally clean things up a bit. + * + * 207 8/15/98 5:16p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 206 8/14/98 5:28p Jeff + * test key added + * + * 205 8/14/98 4:57p Jeff + * + * 204 8/13/98 7:23p Jeff + * moved in UI system + * + * 203 8/13/98 3:10p Jeff + * keyflush if multiplayer dll requests so + * + * 202 8/12/98 2:54p Matt + * Renamed the slash and backslash key constants. + * + * 201 8/10/98 5:51p Samir + * new keys for music system. + * + * 200 8/10/98 12:16p Chris + * Added AI_NumHostileAlert + * + * 199 8/07/98 7:12p Samir + * changed d3xexecscript. + * + * 198 8/04/98 10:26a Kevin + * Custom sound and bmp exchange system + * + * 197 7/28/98 5:40p Samir + * added call to music system per frame. + * + * 196 7/27/98 5:59p Jason + * added piggyback mode plus multiplayer colors + * + * 195 7/24/98 5:30p Samir + * took out extranneous stream cheats. + * + * 194 7/24/98 4:45p Jason + * added notification if any subsystem takes more than .2 seconds to + * execute + * + * 193 7/23/98 11:43a Jeff + * fixed observer mode bug + * + * 192 7/22/98 3:16p Jason + * added observer mode + * + * 191 7/21/98 10:25a Kevin + * commented out heapcheck + * + * 190 7/20/98 7:00p Sean + * SOMEONE: Added a heapcheck call + * + * 189 7/09/98 8:33p Samir + * changes for new streaming interface. + * + * 188 7/08/98 6:00p Jeff + * handles inventory use in a multiplayer game + * + * 187 7/06/98 7:17p Jeff + * added keys for countermeasures (temporary...probably want these + * configurable) + * + * 186 6/30/98 6:36p Chris + * Added rev .1 of multiplayer animations - BTW It is totally not done. + * + * 185 6/30/98 6:14p Samir + * Call DoUIFrameWithoutInput instead of DoUIFrame. + * + * 184 6/25/98 5:22p Kevin + * Req/Send gametime to clients + * + * 183 6/25/98 12:42p Jeff + * Added exit game confirmation + * + * 182 6/24/98 5:59p Chris + * + * 181 6/24/98 5:55p Chris + * Added simple support for on the fly mixer/quality switching - Kills all + * sounds (which is incorrect). + * + * 180 6/24/98 5:20p Matt + * Added key for pause; Cleaned up key handling; ripped out some E3 demo + * code. + * + * 179 6/24/98 4:39p Chris + * Improved the multiple mixer support + * + * 178 6/24/98 12:09p Chris + * Update + * + * 177 6/24/98 11:12a Jeff + * fixed accidental checkin + * + * 176 6/24/98 11:14a Chris + * Added more support and bug fixes + * + * 175 6/22/98 12:00p Chris + * Working on sound system to make it in a nice shape. + * + * 174 6/19/98 6:42p Jason + * made specular mapping a config detail item + * + * 173 6/19/98 6:21p Chris + * Moved Del+F5 to DEL+F6 and F6 to Del+F11 + * + * 172 6/19/98 6:05p Jeff + * call to update voices to handle the voice queue + * + * 171 6/19/98 3:03p Chris + * Made CheckAndForceSoundDataAlloc a SoundSystem function - useful for + * multiple mixers. Added IsSoundPlaying to the high level sound lib. + * + * 170 6/18/98 5:21p Jeff + * put in call to update streaming audio buffers in the game loop + * + * 169 6/18/98 1:09p Chris + * + * 168 6/16/98 5:27p Chris + * Revision version 1.0 (should all be functional for 16 bit mono samples) + * + * 167 6/16/98 3:48p Chris + * Updated the sound system and added the start of sound streaming + * + * 166 6/16/98 10:38a Jeff + * localization, strings pulled out to stringtable.h and d3.str + * + * 165 6/04/98 4:52p Jeff + * Added a suspend/resume controls call on return from EVT_CLIENT_KEYPRESS + * if the DLL requests it + * + * 164 6/02/98 11:01a Jason + * Post E3 Checkin + * + * 163 6/01/98 11:00a Jeff + * Moved the call to the multiplayer EVT_CLIENT_KEYPRESS up in the + * function + * + * 162 5/27/98 12:35a Samir + * level restart for E3 by default on, this time. + * + * 161 5/26/98 10:51p Samir + * ability to activate/deactivate restart level sequence timer. + * + * + * 159 5/26/98 5:03p Samir + * Set E3 time limit for level to 15 minutes. + * + * 158 5/26/98 1:20p Samir + * added special weapons key for E3! + * + * 157 5/26/98 11:36a Matt + * Changed small view system to allow the popup window in any of the three + * positions, to allow any window to be the "bigger" size, and to restore + * the old view when a popup view goes away. + * + * 156 5/25/98 8:29p Samir + * Added E3_DEMO define for E3 stuff like game time limits and special + * keys. + * + * 155 5/25/98 3:46p Jason + * added better light glows + * + * 154 5/22/98 8:36p Samir + * added E3 keyboard lock to hide the not so cool looking stuff in D3 + * + * 153 5/22/98 3:27p Jason + * added specular lighting + * + * 152 5/19/98 10:44a Jeff + * changed D3_QUICK to D3_FAST + * + * 151 5/18/98 4:36p Jeff + * TelCom uses Game_interface_mode variable now + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include "gameloop.h" +#include "game.h" +#include "render.h" +#include "descent.h" +#include "slew.h" +#include "mono.h" +#include "doorway.h" +#include "weapon.h" +#include "hlsoundlib.h" +#include "multi.h" +#include "player.h" +#include "damage.h" +#include "ship.h" +#include "objinit.h" +#include "gameevent.h" +#include "gametexture.h" +#include "AIMain.h" +#include "ddvid.h" +#include "ddio.h" +#include "hud.h" +#include "terrain.h" +#include "BOA.h" +#include "lighting.h" +#include "findintersection.h" +#include "soar.h" +#include "multi.h" +#include "hud.h" +#include "bsp.h" +#include "gauges.h" +#include "SmallViews.h" +#include "newui.h" +#include "Inventory.h" +#include "PHYSICS.H" +#include "Controller.h" +#include "controls.h" +#include "gamesequence.h" +#include "cockpit.h" +#include "help.h" +#include "game.h" +#include "aipath.h" +#include "game2dll.h" +#include "Mission.h" +#include "object_lighting.h" +#include "fireball.h" +#include "weather.h" +#include "stringtable.h" +#include "streamaudio.h" +#include "voice.h" +#include "soundload.h" +#include "sounds.h" +#include "ambient.h" +#include "ship.h" +#include "config.h" +#include "matcen.h" +#include "dedicated_server.h" +#include "D3ForceFeedback.h" +#include "levelgoal.h" +#include "demofile.h" +#include "pilot.h" +#include "rtperformance.h" +#include "demofile.h" +#include "d3music.h" +//#include "gamespy.h" +#include "osiris_dll.h" +#include "aiambient.h" +#include "marker.h" +#include "gamecinematics.h" +#include "postrender.h" +#include "debuggraph.h" +#include "gamesave.h" +#include "psrand.h" +#include "spew.h" +#include "grtext.h" +#include "gamefont.h" +#include "renderobject.h" +#include "vibeinterface.h" + +#ifdef EDITOR +#include "editor\d3edit.h" +#endif + +extern bool Display_renderer_stats; + +//Current zoom factor (this is the tan of 29.25, which is half our FOV of 58.5) +float Render_FOV = D3_DEFAULT_FOV; +float Render_zoom = D3_DEFAULT_ZOOM; + +//How long (in seconds) the last frame took +float Frametime=.1f; + +//How long (in seconds) since the game started +float Gametime; + +int Timedemo_frame=-1; + +//How many frames have been renered. +//NOTE: this is a count of 3d frames, not game frames +int FrameCount=0; + +bool HUD_disabled = 0; +#ifdef _DEBUG +int DoAI = 1; +bool System_locked=false; +int Stream_type = 0; +#else +int Stream_type = 0; +#define DoAI 1 +#endif + +bool Tracking_FVI=false; + +//Set this to force window clear +int Clear_screen=0; + +// determines if game is paused. +bool Game_paused = false; + +// Used for limiting the framerate +int Min_allowed_frametime = 0; + +// determines if we're rendering the main view +bool Rendering_main_view=false; +bool Skip_render_game_frame=false; +bool Menu_interface_mode=false; + + +//#ifdef GAMEGAUGE +int frames_one_second=0; +int min_one_second = 0x7fffffff; +int max_one_second = 0; +float gamegauge_start_time; +int gamegauge_total_frame_seconds = 0; +int gamegauge_total_frames = 0; +float gamegauge_total_time = 0.0; + +short gamegauge_fpslog[GAMEGAUGE_MAX_LOG]; + +bool Game_gauge_do_time_test = false; +char Game_gauge_usefile[_MAX_PATH] = "gg.dem"; +//#endif + +longlong last_timer = 0; + +// contains information for the music system. +tMusicSeqInfo Game_music_info; + +//Defines for changing the window size +#define WINDOW_W_DELTA ((Max_window_w / 16)&~1) +#define WINDOW_H_DELTA ((Max_window_h / 16)&~1) + +#define WINDOW_MIN_W ((Max_window_w * 10) / 22) +#define WINDOW_MIN_H ((Max_window_h * 10) / 22) + +// Functions +void ApplyShadowsToRooms (); +void StartGameMenu(); +void EndGameMenu(); +void ProcessGuidebotKeys(int key); + +//Make the 3D window larger +void GrowWindow() +{ + if (GetHUDMode() == HUD_LETTERBOX) + return; + + Game_window_w += WINDOW_W_DELTA; + Game_window_h += WINDOW_H_DELTA; + + if (Game_window_h > Max_window_h) + Game_window_h = Max_window_h; + + if (Game_window_w > Max_window_w) + Game_window_w = Max_window_w; + + Game_window_x = (Max_window_w - Game_window_w)/2; + Game_window_y = (Max_window_h - Game_window_h)/2; + + Current_pilot.set_hud_data(NULL,NULL,NULL,&Game_window_w,&Game_window_h); + + Clear_screen = 4; + + ResizeCockpit(); +} + +//Make the 3D window smaller +void ShrinkWindow() +{ + if (GetHUDMode() == HUD_LETTERBOX) + return; + + if (Game_window_w > WINDOW_MIN_W) { + + Game_window_w -= WINDOW_W_DELTA; + Game_window_h -= WINDOW_H_DELTA; + + if ( Game_window_w < WINDOW_MIN_W ) + Game_window_w = WINDOW_MIN_W; + + if ( Game_window_h < WINDOW_MIN_H ) + Game_window_h = WINDOW_MIN_H; + + Game_window_x = (Max_window_w - Game_window_w)/2; + Game_window_y = (Max_window_h - Game_window_h)/2; + + Clear_screen = 4; + + ResizeCockpit(); + + Current_pilot.set_hud_data(NULL,NULL,NULL,&Game_window_w,&Game_window_h); + } +} + +//Data and code for camera views + +#define CV_NONE 0 //View not active +#define CV_REARVIEW 1 //Rear view +#define CV_MARKER1 2 //Marker view +#define CV_MARKER2 3 //Marker view +#define CV_MARKER3 4 //Marker view +#define CV_MARKER4 5 //Marker view +#define CV_MARKER5 6 //Marker view +#define CV_MARKER6 7 //Marker view +#define CV_MARKER7 8 //Last marker view +#define CV_MARKER8 9 //Last marker view +#define CV_GUIDEBOT 10 //Guide-Bot +//#define CV_COOP 3 //View from a coop player + +#define NUM_CAMERA_VIEWS 3 + +int Camera_view_mode[NUM_CAMERA_VIEWS]; + +//Selects the next mode for this view +void SelectNextCameraView(int window) +{ + int current = Camera_view_mode[window]; + + //If the window has gone away because there's no object, then the current state is none + if (GetSmallViewer(window) == OBJECT_HANDLE_NONE) + current = CV_NONE; + + switch (current) { + case CV_NONE: + Camera_view_mode[window] = CV_REARVIEW; + CreateSmallView(window,Player_object->handle,SVF_REARVIEW,0.0,D3_DEFAULT_ZOOM,-1,TXT_VIEW_REAR); + if (window == 0) Current_pilot.lrearview_enabled = true; // set to true. + else if (window == 2) Current_pilot.rrearview_enabled = true; + break; + + case CV_REARVIEW: { + object *objp = ObjGet(Buddy_handle[Player_num]); + if (window == 0) Current_pilot.lrearview_enabled = false; // leaving rearview + else if (window == 2) Current_pilot.rrearview_enabled = false; + + if (objp && objp->type == OBJ_ROBOT) { //Not hidden + Camera_view_mode[window] = CV_GUIDEBOT; + CreateSmallView(window,objp->handle,0,0.0,D3_DEFAULT_ZOOM,-1,TXT_VIEW_GUIDEBOT); + break; + } + //If no GB, then fall into next case + } + case CV_GUIDEBOT: + case CV_MARKER1: + case CV_MARKER2: + case CV_MARKER3: + case CV_MARKER4: + case CV_MARKER5: + case CV_MARKER6: + case CV_MARKER7: + case CV_MARKER8: { + if (window == 0) Current_pilot.lrearview_enabled = false; // leaving rearview + else if (window == 2) Current_pilot.rrearview_enabled = false; + + if (Players[Player_num].num_markers > 0) { + int num = (current == CV_GUIDEBOT) ? 0 : (current-CV_MARKER1)+1; + int max = (Game_mode & GM_MULTI) ? 2 : 8; //Why no symbolic constants? Ask Jason. + int id = Player_num*2 + num; + + if (num < max) { + int i; + + // Search for the marker + for (i=0;i<=Highest_object_index;i++) { + if ((Objects[i].type==OBJ_MARKER) && (Objects[i].id==id)) + break; + } + + if (i <= Highest_object_index) { + char buf[25]; + sprintf(buf,TXT_VIEW_MARKER,num + 1); + + CreateSmallView(window,Objects[i].handle,0,0.0,D3_DEFAULT_ZOOM,-1,buf); + Camera_view_mode[window] = CV_MARKER1+num; + + break; + } + } + } + //If couldn't find marker or have cycled though all, close window + Camera_view_mode[window] = CV_NONE; + CloseSmallView(window); + break; + } + default: + Int3(); //all cases should be handled + } +} + +//Turn off all camera views +//If total reset is true, set all views to none, otherwise kill object view but keep rear views. +void InitCameraViews(bool total_reset) +{ + for (int i=0;ihandle,SVF_REARVIEW,0.0,D3_DEFAULT_ZOOM,-1,TXT_VIEW_REAR); + } + } +} + + +void RestoreCameraRearviews() +{ + if (Camera_view_mode[0] != CV_REARVIEW) { + if (Current_pilot.lrearview_enabled) { + Camera_view_mode[0] = CV_REARVIEW; + CreateSmallView(0,Player_object->handle,SVF_REARVIEW,0.0,D3_DEFAULT_ZOOM,-1,TXT_VIEW_REAR); + } + } + if (Camera_view_mode[2] != CV_REARVIEW) { + if (Current_pilot.rrearview_enabled) { + Camera_view_mode[2] = CV_REARVIEW; + CreateSmallView(2,Player_object->handle,SVF_REARVIEW,0.0,D3_DEFAULT_ZOOM,-1,TXT_VIEW_REAR); + } + } +} + +void ProcessButtons() +{ + //If dead, any key not handled above will eand the death sequence + // this shouldn't be called in ReadPlayerControls since the player is DEAD! + if (Players[Player_num].flags & PLAYER_FLAGS_DEAD) { + int x,y; + PollControls(); + if (Controller->get_joy_raw_values(&x,&y) || Controller->get_mouse_raw_values(&x,&y)) { + // death. + mprintf((0, "here?")); + + if (Total_time_deadflush(); + } + } + return; + } +} + + +#define GBC_FIND_ACTIVE_GOAL_0 3 // Find next primary +#define GBC_FIND_SPEW 19 // Find Spew +#define GBC_FIND_POWERUP 20 // Find Powerup +#define GBC_FIND_ENERGY_CENTER 21 // Find energy +#define GBC_ESCORT_SHIP 35 // Escort Ship +#define GBC_EXTINGUISH 36 // Use Extinguisher +#define GBC_GO_WINGNUT 37 // Use Wingnut Powerup +#define GBC_RTYPE 39 // Use Gaurdian Powerup +#define GBC_ANTI_VIRUS 40 // Use Anti-virus Powerup +#define GBC_RETURN_TO_SHIP 43 // Return to ship +extern int Buddy_handle[MAX_PLAYERS]; +extern void MultiSendGuidebotMenuSelection(gb_com *); +// Handles guidebot shortcut keys +void ProcessGuidebotKeys(int key) +{ + if(!(key&KEY_SHIFTED)) + return; + key &= ~KEY_SHIFTED; + + //check to make sure our Guidebot is out of the ship + object *buddy = ObjGet(Buddy_handle[Player_num]); + if(!buddy || buddy->type!=OBJ_ROBOT) + { + //not out of the ship + mprintf((0,"Guidebot: mrmph mmrump..mrmph...LET ME OUT OF YOUR SHIP!\n")); + return; + } + int command_id = -1; + + switch(key) + { + case KEY_1: + command_id = GBC_FIND_ACTIVE_GOAL_0; + break; + case KEY_2: + command_id = GBC_FIND_SPEW; + break; + case KEY_3: + command_id = GBC_FIND_POWERUP; + break; + case KEY_4: + command_id = GBC_FIND_ENERGY_CENTER; + break; + case KEY_5: + command_id = GBC_ESCORT_SHIP; + break; + case KEY_6: + command_id = GBC_EXTINGUISH; + break; + case KEY_7: + command_id = GBC_GO_WINGNUT; + break; + case KEY_8: + command_id = GBC_RTYPE; + break; + case KEY_9: + command_id = GBC_ANTI_VIRUS; + break; + case KEY_0: + command_id = GBC_RETURN_TO_SHIP; + break; + default: + //nope + return; + break; + } + + // Process the command + gb_com command; + command.action = COM_DO_ACTION; + command.index = command_id; + command.ptr = NULL; + + if( (!(Game_mode&GM_MULTI)) || (Netgame.local_role!=LR_CLIENT) ) + { + AINotify(buddy, AIN_USER_DEFINED, (void *)&command); + }else + { + MultiSendGuidebotMenuSelection(&command); + } +} + +//Deals with a normal (non-debug) key. Once the key is handled, this function returns + +void ProcessNormalKey(int key) +{ + //First do keys that work normally even when dead + switch (key) { + + //ingnore the modifier keys +#ifdef MACINTOSH + case KEY_CMD: +#endif + case KEY_LALT: + case KEY_RALT: + case KEY_LSHIFT: + case KEY_RSHIFT: + case KEY_LCTRL: + case KEY_RCTRL: + return; + + case KEY_ESC: + Game_interface_mode = GAME_EXIT_CONFIRM; + return; + + case KEY_PAUSE: +#ifdef MACINTOSH + case KEY_PAGEDOWN: +#endif + Game_interface_mode = GAME_PAUSE_INTERFACE; + return; + + case KEY_F1: + Game_interface_mode = GAME_HELP_INTERFACE; + return; + + case KEY_F2: + Game_interface_mode = GAME_OPTIONS_INTERFACE; + return; + + case KEY_F8: + if (Game_mode & GM_MULTI) { + StartHUDInputMessage(); + return; + } + break; + + case KEY_SHIFTED+KEY_F8: + ToggleGameMessageConsole(); + Clear_screen = 4; + break; + + case KEY_SHIFTED+KEY_F9: + ToggleHUDMessageConsole(); + Clear_screen = 4; // clears screen. + break; + +//#ifndef DEMO + case KEY_F5: Game_interface_mode = GAME_TOGGLE_DEMO; return; + case KEY_ALTED+KEY_F5: DemoAbort(true); return; +//#endif + //case KEY_SHIFTED+KEY_LAPOSTRO: Game_interface_mode = GAME_TELCOM_CARGO;return; + //case KEY_ALTED+KEY_LAPOSTRO: Game_interface_mode = GAME_TELCOM_BRIEFINGS; return; + case KEY_SHIFTED+KEY_TAB: Game_interface_mode = GAME_TELCOM_BRIEFINGS; return; + case KEY_ALTED+KEY_F3: + if(!Cinematic_inuse) + { + Game_interface_mode = GAME_LOAD_INTERFACE; + return; + }break; + + case KEY_CTRLED+KEY_F8: + if (Game_mode & GM_MULTI) { + StartTeamHUDInputMessage(); + return; + } + break; + case KEY_CTRLED+KEY_1: + case KEY_CTRLED+KEY_2: + case KEY_CTRLED+KEY_3: + case KEY_CTRLED+KEY_4: + case KEY_CTRLED+KEY_5: + case KEY_CTRLED+KEY_6: + case KEY_CTRLED+KEY_7: + case KEY_CTRLED+KEY_8: + { + int index=key & ~KEY_CTRLED; + index=index-KEY_1; + + if (Game_mode & GM_MULTI) + { + char str[80]; + + mprintf ((0,"Printing message %d!\n",index)); + + int save_do=Doing_input_message; + int save_len=HudInputMessageLen; + strcpy (str,HudInputMessage); + + //strcpy (HudInputMessage,Current_pilot.taunts[index]); + //decode the text macro by sending it off to the DLL + //for processesing + DLLInfo.input_string = Current_pilot.taunts[index]; + DLLInfo.special_data = (ubyte *)HudInputMessage; + DLLInfo.iParam = MAX_HUD_INPUT_LEN; + CallGameDLL (EVT_CLIENT_DECODETEXTMACRO,&DLLInfo); + + Doing_input_message=1; + SendOffHUDInputMessage (); + strcpy (HudInputMessage,str); + HudInputMessageLen=save_len; + Doing_input_message=save_do; + } + + break; + } + + case KEY_PRINT_SCREEN: + case KEY_SHIFTED+KEY_PRINT_SCREEN: +#ifdef MACINTOSH + case KEY_INSERT: + case KEY_SHIFTED+KEY_INSERT: +#endif + mprintf ((0,"Doing screenshot!\n")); + DoScreenshot(); + return; + + case KEY_SHIFTED+KEY_MINUS: + case KEY_MINUS: ShrinkWindow(); return; + + case KEY_SHIFTED+KEY_EQUAL: + case KEY_EQUAL: GrowWindow(); return; + case KEY_SPACEBAR: + if (Players[Player_num].flags & PLAYER_FLAGS_DEAD) { + + if (Total_time_deadflush(); + } + + return; + } + + } + if(Demo_flags == DF_PLAYBACK) + { + switch(key) + { + case KEY_C: + { + // goto next player + int viewer_objnum=Viewer_object-Objects; + int found=0; + + for (int i=viewer_objnum+1;i<=Highest_object_index && !found;i++,i%=(Highest_object_index+1)) + { + if (Objects[i].type==OBJ_CAMERA) + { + found=1; + Viewer_object=&Objects[i]; + } + } + } + break; + case KEY_P: + { + // goto next player + int viewer_objnum=Viewer_object-Objects; + int found=0; + + for (int i=viewer_objnum+1;i<=Highest_object_index && !found;i++,i%=(Highest_object_index+1)) + { + if (Objects[i].type==OBJ_PLAYER) + { + found=1; + Viewer_object=&Objects[i]; + } + } + } + break; + case KEY_R: + { + // goto next robot + int viewer_objnum=Viewer_object-Objects; + int found=0; + + for (int i=viewer_objnum+1;i<=Highest_object_index && !found;i++,i%=(Highest_object_index+1)) + { + if (Objects[i].type==OBJ_ROBOT) + { + found=1; + Viewer_object=&Objects[i]; + } + } + } + break; + case KEY_SPACEBAR: + //Reset view + Viewer_object=Player_object; + break; + case KEY_DOWN: + Game_paused = true; + Demo_paused = true; + break; + case KEY_RIGHT: + Demo_do_one_frame = true; + break; + case KEY_CTRLED+KEY_LEFT: + { + char sztmp[_MAX_PATH*2]; + strcpy(sztmp,Demo_fname); + DemoAbort(); + Game_interface_mode = GAME_DEMO_LOOP; + Demo_restart = true; + strcpy(Demo_fname,sztmp); + } + break; + case KEY_UP: + Game_paused = false; + Demo_paused = false; + break; + default: + break; + } + + } + + //If dead, any key not handled above will eand the death sequence + if (Players[Player_num].flags & PLAYER_FLAGS_DEAD) { + return; + } + + //If dying, only the keys handled above do anything, so bail + if (Players[Player_num].flags & PLAYER_FLAGS_DYING) + return; + + //These keys only work when not dying or dead + ProcessGuidebotKeys(key); + + switch (key) { + case KEY_SHIFTED+KEY_F1: SelectNextCameraView(0); return; + case KEY_SHIFTED+KEY_F2: SelectNextCameraView(2); return; + //case KEY_SHIFTED+KEY_F3: SelectNextCameraView(2); return; + + case KEY_1: SelectWeapon(0); return; + case KEY_2: SelectWeapon(1); return; + case KEY_3: SelectWeapon(2); return; + case KEY_4: SelectWeapon(3); return; + case KEY_5: SelectWeapon(4); return; + case KEY_6: SelectWeapon(5); return; + case KEY_7: SelectWeapon(6); return; + case KEY_8: SelectWeapon(7); return; + case KEY_9: SelectWeapon(8); return; + case KEY_0: SelectWeapon(9); return; + + case KEY_F3: if (GetHUDMode() != HUD_OBSERVER) ToggleHUDMode(); return; + case KEY_F12: + Marker_message=1; + StartHUDInputMessage(); + break; + case KEY_F4: + { + if ( (!(Game_mode&GM_MULTI)) || (Netgame.flags&NF_ALLOWGUIDEBOT) ) + { + Game_interface_mode = GAME_BUDDY_INTERFACE; + } + return; + }break; + case KEY_F9: + // quick save key! + if(!Cinematic_inuse) + { + StartGameMenu(); + QuickSaveGame(); + EndGameMenu(); + } + break; + case KEY_ALTED+KEY_F2: + if(!Cinematic_inuse) + { + Game_interface_mode = GAME_SAVE_INTERFACE; + return; + }break; + } +} +bool Force_one_texture=false; + +#ifdef _DEBUG +static int test_kill_objs = 0; + +void InitTestSystems() +{ + test_kill_objs = 0; +} + +// Tells others that we are cheating +void SendCheaterText () +{ + //Notify other players that you're using debug/test keys. We may want to change this to + //only send the message for some keys. + if (Game_mode & GM_MULTI) + { + char str[255]; + sprintf (str,TXT_CHEATER,Players[Player_num].callsign); + + if (Netgame.local_role==LR_SERVER) + MultiSendMessageFromServer (GR_RGB(255,0,0),str); + else + MultiSendMessageToServer (0,str); + } +} + +#include "osiris_dll.h" +#include "audiotaunts.h" + +void InitObjectScripts(object *obj,bool call_evt_created=true); + +#ifdef _DEBUG +extern bool Cinematics_enabled; +#endif + +//Deal with keys that only work for testing & debugging (eg., slew) +void ProcessTestKeys(int key) +{ + static int ffret = -1; + int i; + + //Debug break + if (key == KEY_SHIFTED+KEY_BACKSP) + Int3(); + + //Handle slew keys + if (Player_object->control_type == CT_SLEW) { + switch (key) //Special for slew keys. Add all new debugging keys to the second switch statement. + { + case KEY_PAD5: SlewStop(Player_object); break; + case KEY_PAD0: SlewResetOrient (Player_object); break; + } + } + + if(key == (KEY_SHIFTED+KEY_LBRACKET)){ + MultiSendRequestPlayTaunt(0); + } + + if(key == (KEY_SHIFTED+KEY_RBRACKET)){ + MultiSendRequestPlayTaunt(1); + } + + //All other debug/test keys require the DEBUGGED modifier, so bail if it's not set + if (! (key & KEY_DEBUGGED)) + return; + + switch (key & ~KEY_DEBUGGED) //for simplicity, do swich on keycode without the flag + { + + /* + ************************************************** + Keys A-Z + ************************************************** + */ + case KEY_A: + + DoAI=!DoAI; + AddHUDMessage ("AI turned %s.",DoAI?"on":"off"); + break; + + case KEY_B: + Game_show_sphere = (Game_show_sphere + 1)%4; + + if(Game_show_sphere == 0) + AddHUDMessage("No visible spheres."); + else if (Game_show_sphere == 1) + AddHUDMessage("Wall collision spheres(only encompass first anim frame)."); + else if (Game_show_sphere == 2) + AddHUDMessage("Spheres for FVI(encompass all anims)."); + else if (Game_show_sphere == 3) + AddHUDMessage("Object size spheres."); + + break; + + case KEY_C: + SendCheaterText (); + if (Player_object->effect_info->type_flags & EF_CLOAKED) + { + MakeObjectVisible(Player_object); + AddHUDMessage("You escape the darkness!"); + } + else + { + MakeObjectInvisible(Player_object,10000); + AddHUDMessage("You enter the darkness!"); + } + break; + + case (KEY_C+KEY_SHIFTED): + { + Cinematics_enabled = !Cinematics_enabled; + + AddHUDMessage("Game Cinematics: %s",Cinematics_enabled?"enabled":"disabled"); + }break; + + case KEY_D: + { + + int GetRankIndex (int pnum,char *rankbuf); + void ChangeRankIndex (int old_index,int pnum); + + int oldrank=GetRankIndex(1,NULL); + Players[1].rank+=100; + ChangeRankIndex (oldrank,1); + + break; + } + + case KEY_SHIFTED+KEY_D: + { + int done=0; + for (i=0;i<=Highest_room_index && !done;i++) + { + for (int t=0;ttype==OBJ_OBSERVER) + { + if (Game_mode & GM_MULTI) + MultiSendRequestToObserve (OBSERVER_MODE_ROAM,0,0); + else + PlayerStopObserving (Player_num); + + } + else + { + if (Game_mode & GM_MULTI) + MultiSendRequestToObserve (OBSERVER_MODE_ROAM,1,0); + else + PlayerSwitchToObserver (Player_num,OBSERVER_MODE_ROAM); + } + } + break; + + case KEY_H: + HUD_disabled = !HUD_disabled; + if (! HUD_disabled) + AddHUDMessage(TXT_HUDENABLED); + break; + + case KEY_I: + + SendCheaterText (); + if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) + { + MakePlayerVulnerable(Player_num); + AddHUDMessage("Your mother wont protect you now!"); + } + else + { + MakePlayerInvulnerable(Player_num,10000); + AddHUDMessage("You're a wimp!"); + } + break; + + case KEY_J: + { + object *obj = &Objects[Player_num]; + for(i=0;i<21;i++){ + if(obj->dynamic_wb[i].flags&DWBF_QUAD){ + obj->dynamic_wb[i].flags &= ~DWBF_QUAD; + AddHUDMessage("Quad Flag Turned Off"); + }else{ + obj->dynamic_wb[i].flags |= DWBF_QUAD; + AddHUDMessage("Quad Flag Turned On"); + } + } + + } + break; + + case KEY_K: + { + if (Game_mode & GM_MULTI) + break; + + for (i=0;ifilename) { + vector player_pos = Player_object->pos; + matrix player_orient = Player_object->orient; + int player_roomnum = Player_object->roomnum; + + //Load the level + LoadAndStartCurrentLevel(); + + //Restore player position + ObjSetPos(Player_object,&player_pos,player_roomnum,&player_orient, true); + } + break; + + case KEY_O: + Outline_mode ^= OM_ON; + AddHUDMessage("Outline mode turned %s",OUTLINE_ON(0)?"ON":"OFF"); + break; + + case KEY_O+KEY_SHIFTED: + Outline_mode = OM_ON + OM_ALL; + AddHUDMessage("All outline options turned on."); + break; + + case KEY_P: + MakeTestPath(&Player_object->roomnum, &Player_object->pos); + break; + + case KEY_Q: + if (StateLimited) + { + StateLimited=0; + AddHUDMessage ("State limited turned off."); + } + else + { + StateLimited=1; + AddHUDMessage ("State limited turned on."); + } + break; + + + case KEY_R: + if (Weather.flags & WEATHER_FLAGS_SNOW) + { + Weather.flags &=~WEATHER_FLAGS_SNOW; + AddHUDMessage ("Snow turned off."); + } + else + { + Weather.flags |=WEATHER_FLAGS_SNOW; + AddHUDMessage ("Snow turned on."); + } + break; + + case KEY_S: + if(Sound_system.IsActive()) { + AddHUDMessage("Sound system off."); + Sound_system.KillSoundLib(false); + } + else { + AddHUDMessage("Sound system on."); + Sound_system.InitSoundLib(Descent, Sound_mixer, Sound_quality, false); + } + break; + + case KEY_S+KEY_SHIFTED: + { + //extern spewinfo SpewEffects[MAX_SPEW_EFFECTS]; + int count; + + AddHUDMessage("Spewers Killed"); + + for(count = 0;count < MAX_SPEW_EFFECTS;count++) + { + SpewEffects[count].inuse = 0; + } + + }break; + + case KEY_T: + if (Timedemo_frame==-1) + Timedemo_frame=0; + break; + + case KEY_U: + AIPath_test_end_room = Player_object->roomnum; + AIPath_test_end_pos = Player_object->pos; + break; + + case KEY_V: + /* +#ifndef MACINTOSH //DAJ what does this do?? + if(Debug_print_block) + { + AddHUDMessage("No block debug info"); + Debug_print_block = false; + } + else + { + AddHUDMessage("Block debug info on"); + Debug_print_block = true; + } + break; +#endif + */ + + case KEY_W: + if (Weather.flags & WEATHER_FLAGS_LIGHTNING) + { + Weather.flags &=~WEATHER_FLAGS_LIGHTNING; + AddHUDMessage ("Lighting turned off."); + + } + else + { + Weather.flags |=WEATHER_FLAGS_LIGHTNING; + Weather.lightning_sequence=0; + Weather.lightning_rand_value=70; + Weather.lightning_interval_time=.01f; + AddHUDMessage ("Lighting turned on."); + } + break; + + case KEY_X: + { + SendCheaterText (); + Players[Player_num].energy=200.0f; + Players[Player_num].weapon_flags=0xFFFFFFFF; + int i; + + for (i=0;iroomnum,&Player_object->pos,&Player_object->orient); + + if(objnum!=-1) + { + if(Game_mode&GM_MULTI) + MultiSendObject(&Objects[objnum],false); + + InitObjectScripts(&Objects[objnum],true); + Players[Player_num].inventory.AddObject(Objects[objnum].handle,true); + } + }break; + + case KEY_Z+KEY_ALTED: + { + //look for a betty4pack and remove the sucker + int id; + id = FindObjectIDName("Betty4Pack"); + for(i=0;i 2) + debug_level = 2; + break; +#endif + /* + ************************************************** + Keypad Keys + ************************************************** + */ + + case KEY_PAD0: + { + Game_update_attach = 1 - Game_update_attach; + + if(Game_update_attach) + { + mprintf((0, "Update attach on\n")); + } + else + { + mprintf((0, "Update attach off\n")); + } + } + break; + + case KEY_PAD1: + { + Game_do_flying_sim = 1 - Game_do_flying_sim; + if(Game_do_flying_sim) + { + mprintf((0, "Game_do_flying_sim on\n")); + } + else + { + mprintf((0, "Game_do_flying_sim off\n")); + } + } + break; + + case KEY_PAD2: + { + Game_do_walking_sim = 1 - Game_do_walking_sim; + if(Game_do_walking_sim) + { + mprintf((0, "Game_do_walking_sim on\n")); + } + else + { + mprintf((0, "Game_do_walking_sim off\n")); + } + } + break; + + case KEY_PAD3: + { + Game_do_vis_sim = 1 - Game_do_vis_sim; + if(Game_do_vis_sim) + { + mprintf((0, "Game_do_vis_sim on\n")); + } + else + { + mprintf((0, "Game_do_vis_sim off\n")); + } + } + break; + + case KEY_PAD4: + { + Game_do_ai_movement = 1 - Game_do_ai_movement; + if(Game_do_ai_movement) + { + mprintf((0, "Game_do_ai_movement on\n")); + } + else + { + mprintf((0, "Game_do_ai_movement off\n")); + } + } + break; + + case KEY_PAD5: + { + Game_do_ai_vis = 1 - Game_do_ai_vis; + if(Game_do_ai_vis) + { + mprintf((0, "Game_do_ai_vis on\n")); + } + else + { + mprintf((0, "Game_do_ai_vis off\n")); + } + } + break; + + case KEY_PAD6: + { + if(AI_debug_robot_do) + { + AI_debug_robot_do = false; + mprintf((0, "AI Debug Info Off\n")); + } + else + { + AI_debug_robot_do = true; + mprintf((0, "AI Debug Info On\n")); + } + } + break; + + case KEY_PAD7: + { + bool killed_something = false; + int killed_count = 0; + int i,a; + for (i=0;i4) + break; + */ + } + if(killed_something) + break; + } + if(!killed_something) + AddHUDMessage("Nothing to kill!"); + else + mprintf((0,"Killed %d objects of type %s!\n",killed_count,Object_info[Objects[i].id].name)); + } + break; + /* + ************************************************** + Number Keys + ************************************************** + */ + + case KEY_1: + { + // goto prev viewer + int viewer_objnum=Viewer_object-Objects; + int found=0; + + for (i=viewer_objnum-1;i>=0 && !found;i--,i<0?i=Highest_object_index:i=i) + { + if (Objects[i].type==OBJ_POWERUP || Objects[i].type==OBJ_PLAYER || Objects[i].type==OBJ_ROBOT || + Objects[i].type==OBJ_CLUTTER || Objects[i].type==OBJ_BUILDING || Objects[i].type==OBJ_MARKER) + { + found=1; + Viewer_object=&Objects[i]; + } + } + + AddHUDMessage ("Viewer set to previous object."); + + } + break; + + case KEY_2: + { + // reset viewer + AddHUDMessage ("Viewer set to player object."); + Viewer_object=Player_object; + } + break; + + case KEY_3: + { + // goto next viewer + int viewer_objnum=Viewer_object-Objects; + int found=0; + + for (i=viewer_objnum+1;i<=Highest_object_index && !found;i++,i%=(Highest_object_index+1)) + { + if (Objects[i].type==OBJ_POWERUP || Objects[i].type==OBJ_PLAYER || Objects[i].type==OBJ_ROBOT || + Objects[i].type==OBJ_CLUTTER || Objects[i].type==OBJ_BUILDING || Objects[i].type==OBJ_MARKER) + { + found=1; + Viewer_object=&Objects[i]; + } + } + + AddHUDMessage ("Viewer set to next object."); + } + break; + + case KEY_4: + if (Tracking_FVI) + Tracking_FVI=false; + else + Tracking_FVI=true; + + AddHUDMessage ("Tracking FVI now set to %s.",Tracking_FVI?"on":"off"); + + break; + + + case KEY_6: + Render_FOV-=1; + AddHUDMessage ("FOV is now %f",Render_FOV); + break; + + case KEY_7: + Render_FOV+=1; + AddHUDMessage ("FOV is now %f",Render_FOV); + break; + + case KEY_8: + Render_FOV=D3_DEFAULT_FOV; + AddHUDMessage ("FOV has been reset to %f",Render_FOV); + break; + + case KEY_9: + Force_one_texture=!Force_one_texture; + break; + + /* + ************************************************** + Function Keys + ************************************************** + */ + + case KEY_F5: + D3MusicToggle(); + break; + + case KEY_F6: + { + vector test_pos; + fvi_info hit_info; + fvi_query fq; + int fate; + int this_key=key & ~KEY_DEBUGGED; + + test_pos = Player_object->pos + Player_object->orient.fvec * 1000000.0f; + + fq.p0 = &Player_object->pos; + fq.startroom = Player_object->roomnum; + fq.p1 = &test_pos; + fq.rad = 0.0; + fq.thisobjnum = OBJNUM(Player_object); + fq.ignore_obj_list = NULL; + if(this_key == KEY_F6) + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_RENDER_THROUGH_PORTALS; + else + fq.flags = 0; + + fate = fvi_FindIntersection(&fq,&hit_info); + + switch (fate) + { + case HIT_NONE: + mprintf((0, "Hit nothing\n")); + break; + case HIT_WALL: + mprintf((0, "Hit wall\n")); + break; + case HIT_OBJECT: + mprintf((0, "Hit Object %d\n", hit_info.hit_object)); + break; + case HIT_TERRAIN: + mprintf((0, "Hit Terrain %d\n", CELLNUM(hit_info.hit_room))); + break; + case HIT_OUT_OF_TERRAIN_BOUNDS: + mprintf((0, "Hit nothing: Leaving Terrain\n")); + break; + case HIT_SPHERE_2_POLY_OBJECT: + mprintf((0, "Hit Poly_object\n")); + break; + default: + mprintf((0, "Hit %d not printed, add to list\n", fate)); + break; + } + + if(fate) + { + int objnum; + + objnum = ObjCreate( OBJ_DEBUG_LINE, 1, Player_object->roomnum, &Player_object->pos, NULL); + if(objnum >= 0) { //DAJ -1FIX + Objects[objnum].rtype.line_info.end_pos = hit_info.hit_pnt; + Objects[objnum].size = vm_VectorDistance(&Player_object->pos, &hit_info.hit_pnt); + ObjSetAABB(&Objects[objnum]); + } + } + + Sound_system.CheckAndForceSoundDataAlloc(SOUND_REFUELING); + Sound_system.PlayStream(0,SoundFiles[Sounds[SOUND_REFUELING].sample_index].sample_8bit, SoundFiles[Sounds[SOUND_REFUELING].sample_index].sample_length, SIF_STREAMING_8_M, 1.0, NULL); + } + break; + + case KEY_F7: + // Toggle Music Debug Info!! + Music_debug_verbose = !Music_debug_verbose; + break; + + case KEY_F8: + D3MusicSetRegion((D3MusicGetRegion()>0) ? 0 : 1); + break; + + case KEY_F9: + { + vector vec=Player_object->pos+(Player_object->orient.fvec*20); + if (BSPRayOccluded(&Player_object->pos,&vec,MineBSP.root)) + mprintf ((0,"Occluded!\n")); + else + mprintf ((0,"NOT occluded!\n")); + }break; + + case KEY_F10: + int ObjInitTypeSpecific(object *objp,bool reinitializing); + FreeCockpit(); + CloseShipHUD(); + Players[0].ship_index = GetNextShip(Players[0].ship_index); + AddHUDMessage("Player ship set to %s\n",Ships[Players[0].ship_index].name); + ObjInitTypeSpecific(Player_object,0); //reset physics, model, etc. + InitShipHUD(Players[0].ship_index); + InitCockpit(Players[0].ship_index); + if (GetHUDMode() == HUD_COCKPIT) + SetHUDMode(HUD_COCKPIT); + else if (GetHUDMode() == HUD_FULLSCREEN) + SetHUDMode(HUD_FULLSCREEN); + break; + + case KEY_F11: + /* + if(Player_object->control_type != CT_SOAR) + { + AddHUDMessage("SOAR ON. Auto-pilot engaged."); + + SetObjectControlType(Player_object, CT_SOAR); + SoarCreateAgent(OBJNUM(Player_object), 0); + } + else + { + AddHUDMessage("SOAR OFF. Manual control returned."); + + SoarDestroyAgent(OBJNUM(Player_object), 0); + SetObjectControlType(Player_object, CT_FLYING); + } + break; + + case KEY_F12: + */ + { + char test[MAX_MATCEN_NAME_LEN]; + bool f_name_changed; + + strcpy(test, "test"); + + int m_id = CreateMatcen(test, &f_name_changed); + + if(m_id >= 0) + { + int id = FindObjectIDName("Tubbs"); + int p = 100; + float t = 5.0f; + int m = 5; + + mprintf((0, "Matcen alive!\n")); + + vector centerPt = Player_object->pos + (Player_object->orient.fvec * 2.0f); + Matcen[m_id]->SetAttachType(MT_ROOM); + Matcen[m_id]->SetAttach(Player_object->roomnum); + Matcen[m_id]->SetCreateRoom(Player_object->roomnum); + Matcen[m_id]->SetCreatePnt( ¢erPt ); + Matcen[m_id]->SetControlType(MPC_WHILE_PLAYER_NEAR); + + Matcen[m_id]->SetMaxProd(12); + Matcen[m_id]->SetNumProdTypes(3); + Matcen[m_id]->SetMaxAliveChildren(1); + Matcen[m_id]->SetProdInfo(0, &id, &p, &t, &m); + id = FindObjectIDName("Sixgun"); + Matcen[m_id]->SetProdInfo(1, &id, &p, &t, &m); + id = FindObjectIDName("bladescaled"); + Matcen[m_id]->SetProdInfo(2, &id, &p, &t, &m); + + Matcen[m_id]->SetStatus(MSTAT_ACTIVE | MSTAT_RANDOM_PROD_ORDER, true); + } + else + { + mprintf((0, "Nope!\n")); + } + } + break; + + /* + ************************************************** + Other Keys + ************************************************** + */ + case KEY_TAB: + Game_interface_mode = GAME_DEBUGGRAPH_INTERFACE; + break; + + case KEY_COMMA: + rtp_StartLog(); + break; + + case KEY_PERIOD: + rtp_StopLog(); + break; + + case KEY_LAPOSTRO: + Show_osiris_debug = !Show_osiris_debug; + AddHUDMessage("%s osiris debug messages",(Show_osiris_debug)?"Enabling":"Disabling"); + break; + + case KEY_BACKSLASH: + //attempt to restore video + if (DebugBreak_callback_stop) + (*DebugBreak_callback_stop)(); + if (DebugBreak_callback_resume) + (*DebugBreak_callback_resume)(); + break; + + case KEY_SPACEBAR: + if (Player_object->control_type == CT_FLYING) + { + SetObjectControlType(Player_object, CT_SLEW); + Player_object->movement_type = MT_NONE; + SlewStop(Player_object); + AddHUDMessage("Slew on"); + } + else { + SlewStop(Player_object); + SetObjectControlType(Player_object, CT_FLYING); + Player_object->movement_type = MT_PHYSICS; + AddHUDMessage("Physics on"); + } + break; + + case KEY_BACKSP: + { + if(!ROOMNUM_OUTSIDE(Player_object->roomnum)) + { + int i; + + Game_show_portal_vis_pnts = 1 - Game_show_portal_vis_pnts; + + mprintf((0, "Vis info for room %d\n", Player_object->roomnum)); + + for(i = 0; i <= Highest_room_index; i++) + { + if(BOA_IsVisible(i, Player_object->roomnum)) + { + mprintf((0, "%d can see you in %d\n", i, Player_object->roomnum)); + } + } + + for(i = Highest_room_index + 1; i <= Highest_room_index + 8; i++) + { + if(BOA_IsVisible(i, Player_object->roomnum)) + { + mprintf((0, "Terrain %d can see you in %d\n", i - Highest_room_index - 1, Player_object->roomnum)); + } + } + } + } + break; + } +} +#endif + +//Sends this key to the multiplayer game DLL +//Return: true means to handle the key normally +// false means to do no further processing on this key +bool SendKeyToGameDLL(int key) +{ + DLLInfo.input_key=key; + + DLLInfo.iRet = 0; + CallGameDLL(EVT_CLIENT_KEYPRESS,&DLLInfo); + + if(DLLInfo.iRet!=0) { + + ddio_KeyFlush(); //keep the controls stuff from getting any keys + return 0; //Don't process this key + } + + //Handle key normally + return 1; +} + +void DemoCheats(int key); +//Get and handle all pending keys +void ProcessKeys() +{ + int key; +#ifdef GAMEGAUGE + if(1) +#else + if(Game_gauge_do_time_test) +#endif + { + return; + } + + if (Dedicated_server) + return; // No key processing for dedicated server! + + // if we are in some menu then don't process game keys! + if (Game_interface_mode != GAME_INTERFACE || Menu_interface_mode) + return; + + //Process all pending keys + while ((key = ddio_KeyInKey()) != 0) { + + //Where does this belong? + // do d3x debugging console + //D3XDebugIO(key); + + //If inputting a multiplayer message, send keys there + if (Doing_input_message) { + DoHUDInputMessageKey(key); + continue; + } + + //If multiplayer, send the key to the game DLL + if (Game_mode & GM_MULTI) { + if (! SendKeyToGameDLL(key)) //returns true if should keep processing this key + continue; //the DLL says not process this key + } + + //Handle normal key + ProcessNormalKey(key); + + //Handle debugging & test keys + #ifdef _DEBUG + ProcessTestKeys(key); + #endif + + //#ifdef DEMO + DemoCheats(key); + //#endif + } +} + + + +//Render the world into a game window +//Parameters: viewer - if not null, this object disabled from rendering. Not used otherwise. +// viewer_eye - where we're rendering from +// viewer_roomnum - the roomnum viewer_eye is in +// viewer_orient - the oriention for this view +// zoom - the zoom for this view +// rear_view - if true, we're looking out the rear of this object + +#ifdef _DEBUG +extern void DrawRoomVisPnts(object *obj); +#endif + +void GameRenderWorld(object *viewer,vector *viewer_eye,int viewer_roomnum,matrix *viewer_orient,float zoom,bool rear_view) +{ + matrix temp_orient,save_orient; + + //Get the viewer orientation + if (rear_view) { + temp_orient.fvec = -viewer_orient->fvec; + temp_orient.rvec = -viewer_orient->rvec; + temp_orient.uvec = viewer_orient->uvec; + viewer_orient = &temp_orient; + save_orient=viewer->orient; + viewer->orient=temp_orient; + } + + //Start the 3D + g3_StartFrame(viewer_eye,viewer_orient,zoom); + + // Reset fog,zbuffer + Num_fogged_rooms_this_frame=0; + rend_SetZBufferState (1); + rend_SetZBufferWriteMask (1); + + // Reset our postrenderings + ResetPostrenderList(); + + //Render! + if (ROOMNUM_OUTSIDE(viewer_roomnum)) + RenderTerrain(0); + else { + bool flag_automap = (viewer != NULL)?(viewer->type != OBJ_ROBOT):0; + RenderMine(viewer_roomnum,flag_automap,0); + + #ifdef _DEBUG + if(Game_show_portal_vis_pnts) + { + DrawRoomVisPnts(Player_object); + } + #endif + + } + + //Done with 3D + PostRender(viewer_roomnum); + g3_EndFrame (); + + // Restore viewer orientation + if (rear_view) + viewer->orient=save_orient; + +} + +//Render into the big window +void GameDrawMainView() +{ + extern bool Guided_missile_smallview; // smallviews.cpp + + bool rear_view = 0; + object *save_view; + + DebugBlockPrint ("SR"); + + //Start rendering + StartFrame(false); + + // Set guided view + if (!Cinematic_inuse && Players[Player_num].guided_obj!=NULL && !Guided_missile_smallview) { + save_view=Viewer_object; + Viewer_object=Players[Player_num].guided_obj; + + } + else if ((Viewer_object == Player_object) && (Players[Player_num].flags & PLAYER_FLAGS_REARVIEW)) + rear_view = 1; + + //Draw the world + Rendering_main_view=true; + GameRenderWorld(Viewer_object,&Viewer_object->pos,Viewer_object->roomnum,&Viewer_object->orient,Render_zoom,rear_view); + Rendering_main_view=false; + + // Restore viewer object if guided + if (!Cinematic_inuse && Players[Player_num].guided_obj!=NULL && !Guided_missile_smallview) + Viewer_object=save_view; + + // Room changes + DoRoomChangeFrame(); + + // Draw Matcen Effects + DoMatcensRenderFrame(); + + // Draw any render events + ProcessRenderEvents(); + + //We're done with this window + EndFrame(); + + DebugBlockPrint ("DR"); +} + + +// Added by Samir +#define HUD_RENDER_ZOOM 0.56f + +// Do Cockpit/Hud +void GameDrawHud() +{ +// Start frame and 3d frame + StartFrame(false); + g3_StartFrame(&Viewer_object->pos,&Viewer_object->orient,HUD_RENDER_ZOOM); + +// render HUD + RenderHUDFrame(); + +// End frame + g3_EndFrame(); + EndFrame(); + +// render auxillary consoles + StartFrame(0,0,Max_window_w, Max_window_h,false); + g3_StartFrame(&Viewer_object->pos,&Viewer_object->orient,HUD_RENDER_ZOOM); + + RenderAuxHUDFrame(); + +// End frame + g3_EndFrame(); + EndFrame(); +} + +//Draw a frame of the game +void GameRenderFrame(void) +{ + bool no_render=false; + AI_NumRendered = 0; + AI_NumHostileAlert = 0; + + if (Dedicated_server) + return; + + #ifndef RELEASE + Mine_depth=0; + #endif + + // increase our timing for the powerup sparkles, used globally by all + Last_powerup_sparkle_time += Frametime; + + PreUpdateAllLightGlows(); + + // Don't render if receiving data + if ((Game_mode & GM_MULTI) && NetPlayers[Player_num].sequence!=NETSEQ_PLAYING) + { + no_render=true; + ShowProgressScreen (TXT_RECEIVINGDATA,NULL,0); + } + + //Generate "shaken" view for player + if (!Game_paused) + ShakePlayer(); + + rend_SetZBufferState (1); + rend_SetZBufferWriteMask (1); + + // clear screen if needed + if (Clear_screen) + { + StartFrame(0,0,Max_window_w, Max_window_h,false); + rend_ClearScreen(GR_BLACK); + EndFrame(); + Clear_screen--; + } + + //Render the mine + if (!no_render) + { + // render preliminary hud view (for dirty rectangles) + if (Small_hud_flag) { // small hud flag is set in RenderHUDFrame in GameDrawHud. + StartFrame(0,0,Max_window_w, Max_window_h,false); + RenderPreHUDFrame(); + EndFrame(); + } + + // Draw the big 3d view + GameDrawMainView(); + + //Do the small views. These should be before GameDrawHUD() for the small windows + DrawSmallViews(); + + // Do Cockpit/Hud + if (! HUD_disabled) + GameDrawHud(); + + // Render Ingame Cinematics + Cinematic_RenderFrame(); + + // Process the debug visual graph + DebugGraph_Render(); + + if(Display_renderer_stats) + { + //display some rendering stats + tRendererStats stats; + rend_GetStatistics(&stats); + grtext_SetFont(HUD_FONT); + + char buffer[128]; + int x,y,height; + + x = 15; + y = 240; + StartFrame(0,0,Game_window_w,Game_window_h); + rend_StartFrame(0,0,Game_window_w,Game_window_h,0); + height = grfont_GetHeight(HUD_FONT) + 1; + sprintf(buffer,"Polys=%d",stats.poly_count); + RenderHUDText(GR_RGB(255,40,40),255,1,x,y,buffer); y+=height; + sprintf(buffer,"Verts=%d",stats.vert_count); + RenderHUDText(GR_RGB(255,40,40),255,1,x,y,buffer); y+=height; + sprintf(buffer,"Uploads=%d",stats.texture_uploads); + RenderHUDText(GR_RGB(255,40,40),255,1,x,y,buffer); y+=height; + grtext_Flush(); + EndFrame(); + } + } + + //Do UI Frame + if (Game_interface_mode == GAME_INTERFACE && !Menu_interface_mode) { + DoUIFrameWithoutInput(); + rend_Flip(); + } + + //Restore normal view + if (!Game_paused) + UnshakePlayer(); + + //Done with the dynamic lights + ClearDynamicLightmaps(); + + //Increment frame count + FrameCount++; +//#ifdef GAMEGAUGE + frames_one_second++; + gamegauge_total_frames++; +//#endif + // Update our glows + PostUpdateAllLightGlows(); + + // Reset our powerup sparkle time if it has overflowed + while( Last_powerup_sparkle_time >= POWERUP_SPARKLE_INTERVAL ) + { + Last_powerup_sparkle_time -= POWERUP_SPARKLE_INTERVAL; + } +} + + +void GameProcessMusic() +{ +// the last game state in the sequencer. + extern tGameState Last_game_state; + + if (Last_game_state == GAMESTATE_LVLSTART) { + Game_music_info.started_level = true; + } + else { + Game_music_info.started_level = false; + } + +// player_damaged set in damage.cpp DecreasePlayerShields. + + if ((Players[Player_num].flags & PLAYER_FLAGS_DYING) || (Players[Player_num].flags & PLAYER_FLAGS_DEAD)) { + Game_music_info.player_dead = true; + } + else { + Game_music_info.player_dead = false; + } + + Game_music_info.n_hostiles = AI_NumHostileAlert; + Game_music_info.frametime = Frametime; + + D3MusicDoFrame(&Game_music_info); +} + +//variables for Frametime system + +//timer_GetMSTime() +//float last_timer=0.0; +int timer_paused=0; + +//Stop the Frametime clock +void StopTime() +{ + if (! timer_paused) { + last_timer = timer_GetMSTime() - last_timer; + if (last_timer < 0) { + //Int3(); + last_timer = 0; + } + } + + timer_paused++; +} + +//Restart the Frametime clock +void StartTime() +{ + if (timer_paused == 0) + return; + + timer_paused--; + + ASSERT(timer_paused >= 0); + + if (! timer_paused) + last_timer = timer_GetMSTime() - last_timer; +} + +float Min_frametime = 500; +float Max_frametime = 0; +float Avg_frametime = 0; +unsigned int Frames_counted = 0; + + + +//Compute how long last frame took +void CalcFrameTime(void) +{ + longlong current_timer; + + if (timer_paused) + return; + + current_timer = timer_GetMSTime(); + if (current_timer >= last_timer) + { + Frametime = static_cast(current_timer - last_timer) / 1000.0f; + } + else + { + Frametime = 0.0f; + } + + last_timer = current_timer; + + if(Min_frametime>Frametime) + { + mprintf((0,"This was the fastest frame yet!\n")); + Min_frametime = Frametime-Demo_frame_ofs; + } + else if(Max_frametime1.0) + { + //Skip the first frame, it's always more than one second + if(gamegauge_total_frames>1) + { + if(frames_one_secondmax_one_second) + max_one_second = frames_one_second; + if(gamegauge_total_frame_seconds 1.0) + Terrain_sound_fade = 1.0; + } + } + else { + if (Terrain_sound_fade > 0.0) { //fading out + Terrain_sound_fade -= Frametime / FADE_TIME; + if (Terrain_sound_fade < 0.0) + Terrain_sound_fade = 0.0; + } + else + return; //already faded out + } + + //Get player altitude + int alt = Player_object->pos.y / TERRAIN_HEIGHT_INCREMENT; + if(alt > 255) + { + alt = 255; + } + + //Update each sound + for (int b=0;bsound_index != -1) { + float volume; + + if ((alt < tb->low_alt) || (alt > tb->high_alt)) + volume = 0.0; + else + volume = tb->low_volume + ((tb->high_volume - tb->low_volume) * (alt - tb->low_alt) / (tb->high_alt - tb->low_alt)); + + Sound_system.Update2dSound(Terrain_sound_handles[b], volume * Terrain_sound_fade, 0.0); + } + } +} + +//Clear out all the terrain sound bands +void ClearTerrainSound() +{ + for (int b=0;bsound_index != -1) { + Terrain_sound_handles[b] = Sound_system.Play2dSound(tb->sound_index); + } + } + + //Set the volumes + UpdateTerrainSound(); +} + +//The main loop for D3. It renders, gets input, etc. for one frame +extern bool Skip_render_game_frame; +void GameFrame(void) +{ +#ifdef USE_RTP + INT64 curr_time; +#endif + + bool is_game_idle = !Descent->active(); + + if (Tracking_FVI) + { + mprintf ((0,"Beginning frame!\n")); + } + + // Begin Gameloop stuff + Physics_normal_counter = 0; + Physics_normal_looping_counter = 0; + Physics_walking_counter = 0; + Physics_walking_looping_counter = 0; + Physics_vis_counter = 0; + + FVI_counter = 0; + FVI_room_counter = 0; + +#ifdef _DEBUG + // Dump networking stats to virtual window + tNetworkStatus netstat; + static tNetworkStatus old_netstat; + static bool netstat_init = false; + static float netstat_time = 0; + + nw_GetNetworkStats(&netstat); + mprintf_at((5,0,0,"TCP/IP Network Stats:")); + mprintf_at((5,1,0,"TCP: tx: %d/%d rtx: %d/%d rx: %d/%d",netstat.tcp_total_packets_sent,netstat.tcp_total_bytes_sent,netstat.tcp_total_packets_resent,netstat.tcp_total_bytes_resent,netstat.tcp_total_packets_rec,netstat.tcp_total_bytes_rec)); + mprintf_at((5,2,0,"UDP: tx: %d/%d rx: %d/%d",netstat.udp_total_packets_sent,netstat.udp_total_bytes_sent,netstat.udp_total_packets_rec,netstat.udp_total_bytes_rec)); + + mprintf_at((6,0,0,"IPX/SPX Network Stats:")); + mprintf_at((6,1,0,"SPX: tx: %d/%d rtx: %d/%d rx: %d/%d",netstat.spx_total_packets_sent,netstat.spx_total_bytes_sent,netstat.spx_total_packets_resent,netstat.spx_total_bytes_resent,netstat.spx_total_packets_rec,netstat.spx_total_bytes_rec)); + mprintf_at((6,2,0,"IPX: tx: %d/%d rx: %d/%d",netstat.ipx_total_packets_sent,netstat.ipx_total_bytes_sent,netstat.ipx_total_packets_rec,netstat.ipx_total_bytes_rec)); + + if(!netstat_init) + { + netstat_time = timer_GetTime(); + + old_netstat = netstat; + netstat_init = true; + }else + { + float newt = timer_GetTime(); + if(netstat_time + 0.5f <= newt) + { + //time to update + float tcp_rx,tcp_rtx,tcp_tx; + float udp_rx,udp_tx; + float ipx_rx,ipx_tx; + float spx_rx,spx_tx,spx_rtx; + float time_diff = newt - netstat_time; + + tcp_rx = ((float)(netstat.tcp_total_bytes_rec - old_netstat.tcp_total_bytes_rec))/time_diff; + tcp_tx = ((float)(netstat.tcp_total_bytes_sent - old_netstat.tcp_total_bytes_sent))/time_diff; + tcp_rtx = ((float)(netstat.tcp_total_bytes_resent - old_netstat.tcp_total_bytes_resent))/time_diff; + + udp_rx = ((float)(netstat.udp_total_bytes_rec - old_netstat.udp_total_bytes_rec))/time_diff; + udp_tx = ((float)(netstat.udp_total_bytes_sent - old_netstat.udp_total_bytes_sent))/time_diff; + + spx_rx = ((float)(netstat.spx_total_bytes_rec - old_netstat.spx_total_bytes_rec))/time_diff; + spx_tx = ((float)(netstat.spx_total_bytes_sent - old_netstat.spx_total_bytes_sent))/time_diff; + spx_rtx = ((float)(netstat.spx_total_bytes_resent - old_netstat.spx_total_bytes_resent))/time_diff; + + ipx_rx = ((float)(netstat.ipx_total_bytes_rec - old_netstat.ipx_total_bytes_rec))/time_diff; + ipx_tx = ((float)(netstat.ipx_total_bytes_sent - old_netstat.ipx_total_bytes_sent))/time_diff; + + mprintf_at((5,3,0,"TCP/s: TX: % 5.1f RTX: % 5.1f RX: % 5.1f",tcp_tx,tcp_rtx,tcp_rx)); + mprintf_at((5,4,0,"UDP/s: TX: % 5.1f RX: % 5.1f",udp_tx,udp_rx)); + mprintf_at((6,3,0,"SPX/s: TX: % 5.1f RTX: % 5.1f RX: % 5.1f",spx_tx,spx_rtx,spx_rx)); + mprintf_at((6,4,0,"IPX/s: TX: % 5.1f RX: % 5.1f",ipx_tx,ipx_rx)); + + old_netstat = netstat; + netstat_time = newt; + } + } + +#endif + // Do our first quaterframe of IntelliVIBE + VIBE_DoQuaterFrame(true); + + // clear out music struct + memset(&Game_music_info, 0, sizeof(Game_music_info)); + + // if in user interface, clear screen every frame for small game windows. + if (Game_interface_mode != GAME_INTERFACE || Menu_interface_mode) + Clear_screen = 4; + + // Setup sound for new frame + Sound_system.BeginSoundFrame(); + UpdateVoices(); + +#ifdef USE_RTP + RTP_GETCLOCK(curr_time); +#endif + + if ((!Game_paused)||(Demo_do_one_frame)) + { + //if we are recording a demo, all moved objects will be written + if(!Skip_render_game_frame){ + DemoWriteChangedObjects(); + } + // Demo Frame (do this first so all subsequent demo items are marked for this frame) + if(!Skip_render_game_frame){ + DemoStartNewFrame(); + } + if(Demo_flags==DF_PLAYBACK) + { + DemoFrame(); + } + + if(!is_game_idle) + { + //Get and process keys + RTP_tSTARTTIME(processkeys_time,curr_time); + ProcessKeys(); + ProcessButtons(); + RTP_tENDTIME(processkeys_time,curr_time); + } + + //Global AI Frame Stuff -- must be before ObjMoveAll + RTP_tSTARTTIME(aiframeall_time,curr_time); + if (DoAI) + { + AIFrameAll(); + } + a_life.DoFrame(); + RTP_tENDTIME(aiframeall_time,curr_time); + + //ApplyShadowsToRooms (); + + //Move objects for this frame + RTP_tSTARTTIME(objframe_time,curr_time); + ObjDoFrameAll(); + RTP_tENDTIME(objframe_time,curr_time); + + RTP_tSTARTTIME(matcenframe_time,curr_time); + DoMatcensFrame(); + RTP_tENDTIME(matcenframe_time,curr_time); + + // Checks for goal completion + RTP_tSTARTTIME(levelgoal_time,curr_time); + Level_goals.DoFrame(); + RTP_tENDTIME(levelgoal_time,curr_time); + + //Do doorways + RTP_tSTARTTIME(doorframe_time,curr_time); + DoorwayDoFrame(); + RTP_tENDTIME(doorframe_time,curr_time); + + // Do player frame + RTP_tSTARTTIME(playerframe_time,curr_time); + DoPlayerFrame(); + RTP_tENDTIME(playerframe_time,curr_time); + + // Do our second quaterframe of IntelliVIBE + VIBE_DoQuaterFrame(false); + + // Weather frame + RTP_tSTARTTIME(weatherframe_time,curr_time); + DoWeatherForFrame (); + RTP_tENDTIME(weatherframe_time,curr_time); + + // Ambient sounds + RTP_tSTARTTIME(ambsound_frame_time,curr_time); + DoAmbientSounds(); + RTP_tENDTIME(ambsound_frame_time,curr_time); + + //Terrain sound + UpdateTerrainSound(); + + + //Call the interval script for the level script + //@$-D3XExecScript(Current_level->d3xthread, Current_mission.cur_level, REF_LEVELTYPE, EVT_INTERVAL, 0, 0); + tOSIRISEventInfo ei; + ei.evt_interval.frame_time = Frametime; + ei.evt_interval.game_time = Gametime; + Osiris_CallLevelEvent(EVT_INTERVAL,&ei); + Osiris_ProcessTimers(); + + // Process any in-game cinematics + Cinematic_Frame(); + + // Do our third quaterframe of IntelliVIBE + VIBE_DoQuaterFrame(false); + + }else + { + // Do our second quaterframe of IntelliVIBE + VIBE_DoQuaterFrame(false); + Sleep(3); + // Do our third quaterframe of IntelliVIBE + VIBE_DoQuaterFrame(false); + } + +// do music always. + RTP_tSTARTTIME(musicframe_time,curr_time); + GameProcessMusic(); + RTP_tENDTIME(musicframe_time,curr_time); + + if((Demo_paused) && (Demo_flags == DF_PLAYBACK) && !is_game_idle) + { + Demo_do_one_frame = false; + ProcessKeys(); + } + + // Always do multiplayer frames before render frame + // + // Do multiplayer stuff + RTP_tSTARTTIME(multiframe_time,curr_time); + MultiDoFrame(); + RTP_tENDTIME(multiframe_time,curr_time); + + //Do Gamespy stuff +// gspy_DoFrame(); + + // Do our fourth quaterframe of IntelliVIBE + VIBE_DoQuaterFrame(false); + +#ifdef USE_RTP + RTP_GETCLOCK(curr_time); //update the current time, since something has happened since ENDFTIME +#endif + + if(!is_game_idle) + { + RTP_tSTARTTIME(renderframe_time,curr_time); + if(!Skip_render_game_frame){ + //Render the frame + GameRenderFrame(); + } + RTP_tENDTIME(renderframe_time,curr_time); + } + + if (!Game_paused) { + // Do pending events + RTP_tSTARTTIME(normalevent_time,curr_time); + ProcessNormalEvents(); + RTP_tENDTIME(normalevent_time,curr_time); + + //float start_delay = timer_GetTime(); + //Slow down the game if the user asked us to + + longlong current_timer; + unsigned int sleeptime; + current_timer = timer_GetMSTime(); + if((current_timer-last_timer)=0) + { + float fps; + + if(Frametime>0) + { + fps = 1.0/Frametime; + DebugGraph_Update(graph_id,fps); + } + } + + //Compute how long frame took + CalcFrameTime(); + + //Update Gametime + Gametime += Frametime; + } + + + + // End Gameloop Loop stuff + Sound_system.EndSoundFrame(); + DoDestroyedLightsForFrame (); + + // Clear lod stuff + ClearLODOffs(); + + static int fvi_graph_id = -2; + if(fvi_graph_id==-2) + { + fvi_graph_id = DebugGraph_Add(0,1000,"FVI Calls"); + } + if(fvi_graph_id>=0) + { + DebugGraph_Update(fvi_graph_id,FVI_counter); + } + + #ifdef USE_RTP + RTP_RECORDVALUE(frame_time,Frametime); + rtp_RecordFrame(); + +/* + if (frame_info.ai_time>.1) + mprintf ((0,"NOTE: AI frame took longer than .1 seconds! %f\n",frame_info.ai_time)); + if (frame_info.render_time>.1) + mprintf ((0,"NOTE: Render frame took longer than .1 seconds! %f\n",frame_info.render_time)); + if (frame_info.multi_time>.1) + mprintf ((0,"NOTE: Multi frame took longer than .1 seconds! %f\n",frame_info.multi_time)); + if (frame_info.obj_time>.1) + mprintf ((0,"NOTE: Object frame took longer than .1 seconds! %f\n",frame_info.obj_time)); +*/ + #endif + + mprintf_at((1, 0, 39, "Pn %05d, L %05d", Physics_normal_counter, Physics_normal_looping_counter)); + mprintf_at((1, 1, 39, "Pw %05d, L %05d", Physics_walking_counter, Physics_walking_looping_counter)); + mprintf_at((1, 2, 39, "Pv %05d", Physics_vis_counter)); + mprintf_at((1, 3, 39, "Fc %05d, R %05d", FVI_counter, FVI_room_counter)); + +#ifdef D3_FAST + if (FrameCount > 20) + SetFunctionMode(MENU_MODE); + else { + int key; + int rand_num = ps_rand() % 4; + + if (rand_num == 0) + key = KEY_LCTRL; + else if (rand_num == 1) + key = KEY_SPACEBAR; + else if (rand_num == 2) + key = KEY_A; + else + key = KEY_Z; + + ddio_UpdateKeyState(key, timer_GetTime(), 1); + ddio_UpdateKeyState(key, timer_GetTime()+.5f, 0); + } +#endif + + if (Tracking_FVI) + { + mprintf ((0,"Ending frame!\n")); + } + + if( !is_game_idle ) + { + Descent->defer(); + } +} + + +// game menu functions +void StartGameMenu() +{ + SetUICallback(GameFrame); + SuspendControls(); + Menu_interface_mode = true; + +// reset bail flag. + Multi_bail_ui_menu = false; + + if (!(Game_mode & GM_MULTI)) { + PauseGame(); + } + + ui_ShowCursor(); +} + + +void EndGameMenu() +{ + ui_HideCursor(); + + if (!(Game_mode & GM_MULTI)) { + ResumeGame(); + } + + Menu_interface_mode = false; + + ResumeControls(); + SetUICallback(NULL); + Clear_screen = 4; +} + diff --git a/Descent3/Inventory.cpp b/Descent3/Inventory.cpp new file mode 100644 index 000000000..90604f5a0 --- /dev/null +++ b/Descent3/Inventory.cpp @@ -0,0 +1,1666 @@ +/* +* $Logfile: /DescentIII/main/Inventory.cpp $ +* $Revision: 80 $ +* $Date: 5/19/99 4:23p $ +* $Author: Chris $ +* +* Inventory control source file +* +* $Log: /DescentIII/main/Inventory.cpp $ + * + * 80 5/19/99 4:23p Chris + * Fixed level goal problems with keys and persistant inventory items + * + * 79 5/19/99 3:24p Jason + * fixed wrong ordering of InitObjectScripts and MultiSendObject + * + * 78 5/12/99 6:01p Jeff + * sanity check. fixed reset + * + * 76 5/08/99 4:12p Chris + * Added AI hearing noises... version 1 + * + * 75 4/29/99 1:28p Jeff + * play sound when switching inven/countermeasres + * + * 74 4/28/99 8:32p Jeff + * fixed bug adding an object to the inventory, was forgetting to set + * count + * + * 73 4/28/99 5:20p Jeff + * fixed missing return + * + * 72 4/28/99 5:22a Jeff + * added function to get list of inventory items, without having to go + * through inventory one by one + * + * 71 4/25/99 5:20p Chris + * Made the GB work with the inventory system... CT_AI not remapped for + * non-vis USED inventory items. + * + * 70 4/20/99 3:06p Jeff + * fixed inventory displaying of non-usable items + * + * 69 4/20/99 1:14p Samir + * added function to determine if inventory item is usable. + * + * 68 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 67 3/02/99 4:41p Jeff + * Fixed inventory save/load + * + * 66 2/25/99 8:54p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 65 2/23/99 7:37p Jeff + * fixed reset so it doesn't remove non-spewable items...made multiplayer + * friendly + * + * 64 2/22/99 1:20a Jeff + * added support for inventory (simple) in dallas. Moved end-level + * sequence to use IGC. Add position clipboard stuff for dallas. Fixed + * some inventory bug with storing object handles + * + * 63 2/14/99 4:29a Jeff + * able to set custom descriptions for an item when adding it + * + * 62 2/13/99 12:37a Jeff + * new inventory system. Supports objects that don't die when put in (by + * objhandles). Also changed Inventory::Reset() + * + * 61 2/08/99 5:24p Jeff + * removed all calls to MultiSendRemoveObject, incorportated into + * SetObjectDeadFlag. Fixes sequencing issues in multiplayer + * + * 60 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 59 1/13/99 2:28a Chris + * Massive AI, OSIRIS update + * + * 58 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 57 1/04/99 12:23p Jeff + * added to evt_use and support for mission module scripts + * + * 56 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 55 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 54 10/06/98 2:37p Jeff + * fixed next,prev pos bugs + * + * 53 10/03/98 8:05p Matt + * Added asserts + * + * 52 8/31/98 6:50p Jeff + * made inventory and countermeasure keys customizable + * + * 51 8/25/98 1:35p Jeff + * fixed 0 byte malloc and removed mprintfs + * + * 50 8/19/98 2:17p Jeff + * added a function to get the aux type/id + * + * 49 8/19/98 12:38p Jason + * made countermeasure spewing work correctly + * + * 48 8/16/98 2:00a Jeff + * fixed ugly mprintf + * + * 47 8/13/98 11:56a Jeff + * handle new flags for inventory use + * + * 46 8/12/98 10:38a Jeff + * fixed some major bugs with reset + * + * 45 8/10/98 11:18a Jeff + * reset takes a bool now whether to reset everything + * + * 44 7/23/98 6:26p Jeff + * added checks to make sure scripts match + * + * 43 7/11/98 9:14p Jeff + * fixed an mprintf + * + * 42 7/10/98 7:49p Jeff + * fixed way of getting icon info for countermeasure + * + * 41 7/09/98 7:51p Jeff + * forgot to tell the clients to remove countermeasures on use + * + * 40 7/09/98 5:44p Jeff + * fixed bug with unique count for countermeasures + * + * 39 7/08/98 6:01p Jeff + * first chance at making multiplayer friendly + * + * 38 7/06/98 7:17p Jeff + * countermeasure support added + * + * 37 7/03/98 3:10p Jeff + * some error handling and added functions to get inventory information + * + * 36 5/25/98 6:38p Matt + * Added needed include. + * + * 35 5/11/98 4:40p Chris + * AI info is now a malloc'ed pointer + * + * 34 5/08/98 1:38p Jeff + * D3XExecScript returns true by default, not false (made change in + * EVT_USE) + * + * 33 5/08/98 1:31p Jeff + * if EVT_USE returns true, it won't remove item from inventory on use + * + * 32 4/24/98 7:09p Jeff + * added a flag for non-useable inventory items + * + * 31 4/23/98 12:02p Jeff + * added a limit to how many items you can put in your inventory + * + * 30 4/19/98 7:32p Jeff + * Moved inventory wrappers to DLLWrappers + * + * 29 4/09/98 6:43p Craig + * + * 28 4/09/98 6:09p Craig + * + * 27 4/09/98 6:02p Craig + * Fixed the non-linked player getting an inventory add crash. + * + * 26 4/03/98 11:49a Jeff + * Added another function wrapper, renamed others to fit naming convention + * + * 25 3/31/98 3:55p Jeff + * Added some inventory wrappers + * + * 24 3/26/98 2:58p Jeff + * Added a GetTypeIDCount function + * + * 23 3/23/98 5:36p Jeff + * added a parameter to Add to specify if you want it destroyed when it's + * added + * + * 22 3/20/98 9:34p Jason + * added SetObjectDeadFlag inlined function + * + * 21 3/20/98 2:58p Jason + * fixed Jeff's dumb error :P + * + * 20 3/20/98 2:44p Jeff + * removed ValidatePos() from ResetPos()....ValidatePos() should be called + * after a ResetPos() if you want to make sure you are on a selectable + * item + * + * 19 2/20/98 5:50p Jeff + * Changed it so that whether an inventory item was selected was placed in + * the objinfo flags + * + * 18 2/20/98 5:02p Jeff + * fixed bug so you can switch to a nonselectable item while playing the + * game + * + * 17 2/20/98 4:56p Jeff + * Changed inventory so it now supports non selectable items, plus made + * the list into a circular list + * + * 16 2/15/98 4:49p Jeff + * Added a reset function to clear the inventory + * + * 15 2/12/98 1:41p Jeff + * commented out constructor/destructor mprintfs + * + * 14 2/11/98 4:54p Jeff + * Moved the inventory into the Player struct + * + * 13 2/09/98 4:58p Jeff + * Connected inventory to hud + * + * 12 2/07/98 6:34p Jeff + * + * 11 2/06/98 2:04p Jeff + * inventory system much more stable now, and works with telcom + * + * 10 2/05/98 7:38p Jeff + * changed inventory system completly...now is classes...works in telcom + * to, added use feature + * + * 9 2/04/98 7:42p Jeff + * began connecting telcom to inventory + * + * 8 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 7 2/04/98 12:18p Jeff + * + * 6 2/03/98 6:15p Jeff + * + * 5 1/31/98 8:49p Jeff + * Added a function to count the number of items in the inventory + * + * 4 1/31/98 8:12p Jeff + * Added more inventory functions + * + * 3 1/30/98 7:02p Jeff + * wrote basic internals inven system + * + * 2 1/29/98 3:52p Jeff + * initial creation +* +* $NoKeywords: $ +*/ + +#include "Inventory.h" + +#include "mono.h" +#include "player.h" +#include "pserror.h" +#include "objinfo.h" +#include "room.h" +#include "weapon.h" +#include "game.h" +#include "multi.h" +#include "osiris_dll.h" +#include "mem.h" +#include "ObjScript.h" +#include "osiris_share.h" +#include "stringtable.h" +#include "hud.h" +#include "hlsoundlib.h" +#include "sounds.h" +#include "AIMain.h" +#include "levelgoal.h" + +//constructor +Inventory::Inventory(void) +{ + //mprintf((0,"Inventory System: Initialize\n")); + root = NULL; + count = 0; + pos = NULL; +} + +//destructor +Inventory::~Inventory(void) +{ + Reset(false); +} + +//Resets the inventory, cleaning it out +//in_game: set to true if this is being called from during gameplay +// reset_stage: +// INVRESET_ALL: Reset _EVERYTHING_ +// INVRESET_LEVELCHANGE: Remove everything except those that last across levels +// INVRESET_DEATHSPEW: Remove everything except those that do not spew (Default) +void Inventory::Reset(bool in_game,int reset_stage) +{ + if(reset_stage<0 || reset_stage>2) + reset_stage = 2; + + inven_item *current = root,*next,*new_root; + + int item_count = count; + new_root = NULL; + object *obj; + + bool remove_nonspewers = false; + bool remove_levelchangers = false; + + switch(reset_stage) + { + case INVRESET_ALL: //everything (mission over) + remove_nonspewers = true; + remove_levelchangers = true; + break; + case INVRESET_LEVELCHANGE: //leave level static (level change) + remove_nonspewers = true; + remove_levelchangers = false; + break; + case INVRESET_DEATHSPEW: //leave non spewers (player death spew reset) + remove_nonspewers = false; + remove_levelchangers = true; + break; + } + + bool no_spew,leave_across_level,should_spew; + + while(item_count) + { + no_spew = false; + leave_across_level = false; + should_spew = true; + + next = current->next; + + if(current->iflags&INVF_NOTSPEWABLE) + no_spew = true; + if(current->iflags&INVF_MISSIONITEM) + leave_across_level = true; + + //determine if we should spew + if(leave_across_level && !remove_levelchangers && (!(current->iflags&INVF_OBJECT)) ) + should_spew = false; + if(no_spew && !remove_nonspewers) + should_spew = false; + + if(in_game && current->iflags&INVF_OBJECT && should_spew) + { + //unmark this object as being in an inventory + obj = ObjGet(current->type); + if(obj) + { + obj->flags &= ~OF_INPLAYERINVENTORY; + } + } + + if(should_spew) + RemoveNode(current); + + item_count--; + current = next; + } + ValidatePos(true); +} + +//adds an object to the inventory (marked by it's objhandle) +bool Inventory::AddObject(int object_handle,int flags,char *description) +{ + //make sure we can fit another object + if(count>=MAX_UNIQUE_INVEN_ITEMS){ + mprintf((0,"Max unique count hit on add to inventory\n")); + return false; + } + + object *obj = ObjGet(object_handle); + if(!obj){ + mprintf((0,"INVEN: Invalid object trying to be added\n")); + return false; + } + + if(obj->flags & OF_INFORM_DESTROY_TO_LG) + { + Level_goals.Inform(LIT_OBJECT, LGF_COMP_DESTROY, obj->handle); + } + + bool in_as_dummy = false; + if(obj->type==OBJ_DUMMY) + { + //type coming in is already dummy, un-dummy before adding it + ObjUnGhostObject(OBJNUM(obj)); + in_as_dummy = true; + } + + ASSERT(obj->type==OBJ_BUILDING || obj->type==OBJ_ROBOT || obj->type==OBJ_POWERUP || obj->type==OBJ_CLUTTER); + + inven_item *current = root, *prev = root, *newnode; + + if(count==0) + { + //there are no items in the list...time to add + root = new inven_item; + newnode = root; + root->next = root; + root->prev = root; + newnode->iflags = INVF_OBJECT; + newnode->count = 1; + } + else + { + newnode = new inven_item; + + prev = root->prev; + + newnode->prev = prev; + prev->next = newnode; + newnode->next = root; + root->prev = newnode; + + newnode->iflags = INVF_OBJECT; + newnode->count = 1; + } + + newnode->type = object_handle; + newnode->id = -1; + newnode->flags = 0; + newnode->otype = obj->type; + newnode->oid = obj->id; + + if(Object_info[newnode->oid].description) + { + newnode->description = mem_strdup(Object_info[newnode->oid].description); + } + else + { + newnode->description = (char *)mem_malloc(sizeof(char)); + newnode->description[0] = 0; + } + + if(Object_info[newnode->oid].flags & OIF_INVEN_SELECTABLE) + newnode->iflags |= INVF_SELECTABLE; + //if(!(Object_info[newnode->oid].flags & OIF_INVEN_NONUSEABLE)) + // newnode->iflags |= INVF_USEABLE; + if(Object_info[newnode->oid].flags & OIF_INVEN_TYPE_MISSION) + newnode->iflags |= INVF_MISSIONITEM; + if(Object_info[newnode->oid].flags & OIF_INVEN_NOREMOVE) + newnode->iflags |= INVF_NOREMOVEONUSE; + if(Object_info[newnode->oid].flags & OIF_INVEN_VISWHENUSED) + newnode->iflags |= INVF_VISWHENUSED; + + if(flags&INVAF_NOTSPEWABLE) + newnode->iflags |= INVF_NOTSPEWABLE; + if(flags&INVAF_TIMEOUTONSPEW) + newnode->iflags |= INVF_TIMEOUTONSPEW; + if(flags&INVAF_LEVELLAST) + newnode->iflags |= INVF_MISSIONITEM; + + + obj->flags |= OF_INPLAYERINVENTORY; + + if( in_as_dummy || (!(Game_mode&GM_MULTI)) || Netgame.local_role==LR_SERVER ) + { + ObjGhostObject(OBJNUM(obj)); + + if(Game_mode&GM_MULTI && Netgame.local_role==LR_SERVER) + { + MultiSendGhostObject( obj, true); + } + } + + newnode->icon_name = mem_strdup(Object_info[newnode->oid].icon_name); + + if(description) + { + newnode->name = mem_strdup(description); + }else + { + newnode->name = mem_strdup(Object_info[newnode->oid].name); + } + + count++; + + if(newnode->iflags & INVF_SELECTABLE) + pos = newnode; + + return true; +} + +//adds a new type/id item to the inventory +bool Inventory::Add(int type,int id,object *parent,int aux_type,int aux_id,int flags,char *description) +{ + //make sure we can fit another object + if(count>=MAX_UNIQUE_INVEN_ITEMS){ + mprintf((0,"Max unique count hit on add to inventory\n")); + return false; + } + + if( (type<0) || (type==OBJ_NONE) ){ + mprintf((0,"Invalid type on add to inventory\n")); + return false; + } + + if(type!=OBJ_WEAPON){ + ASSERT(type==OBJ_BUILDING || type==OBJ_ROBOT || type==OBJ_POWERUP || type==OBJ_CLUTTER); + return AddObjectItem(type,id,(aux_type!=-1)?aux_type:type,(aux_id!=-1)?aux_id:id,flags,description); + }else{ + //special case for countermeasures + return AddCounterMeasure(id,aux_type,aux_id,flags,description); + } + + return false; +} + +//adds a special cased CounterMeasure into the inventory +bool Inventory::AddCounterMeasure(int id,int aux_type,int aux_id,int flags,char *description) +{ + //make sure we can fit another object + if(count>=MAX_UNIQUE_INVEN_ITEMS){ + mprintf((0,"Hit max unique in counter measure add\n")); + return false; + } + + inven_item *current = root, *prev = root, *newnode; + + if(count==0) + { + //there are no items in the list...time to add + root = new inven_item; + newnode = root; + root->next = root; + root->prev = root; + newnode->count = 1; + } + else + { + newnode = FindItem(OBJ_WEAPON,id); + + if(!newnode) + { + //NEEDTODO: adjust so it adds in order + + newnode = new inven_item; + + prev = root->prev; + + newnode->prev = prev; + prev->next = newnode; + newnode->next = root; + root->prev = newnode; + + newnode->count = 1; + } + else + { + //there is an item of that type/id already, just increase it's count + newnode->count++; + //mprintf((0,"Inventory: Item #%d (%s) Count increased to %d\n",count,newnode->name,newnode->count)); + } + } + + //its a new item type/id, so fill in its info + if(newnode->count==1) + { + newnode->type = OBJ_WEAPON; + newnode->id = id; + newnode->flags = 0; + newnode->iflags = 0; + newnode->otype = aux_type; + newnode->oid = aux_id; + + if( (aux_type!=-1) && (aux_id!=-1) && (Object_info[aux_id].description)){ + newnode->description = mem_strdup(Object_info[aux_id].description); + } + else{ + newnode->description = mem_strdup(Weapons[id].name); + } + + newnode->iflags |= INVF_SELECTABLE|INVF_USEABLE|INVF_MISSIONITEM|INVF_TIMEOUTONSPEW; + + newnode->icon_name = (char *)mem_malloc(strlen(GameBitmaps[GameTextures[Weapons[id].icon_handle].bm_handle].name)+1); + strcpy(newnode->icon_name,GameBitmaps[GameTextures[Weapons[id].icon_handle].bm_handle].name); + + if(description) + { + newnode->name = mem_strdup(description); + }else + { + newnode->name = mem_strdup(Weapons[id].name); + } + + count++; + //mprintf((0,"Inventory: Item #%d Added Countermeasure (%s) ID=%d\n",count,newnode->name,newnode->id)); + } + + pos = newnode; + return true; +} + +//adds an object to the inventory +bool Inventory::AddObjectItem(int otype,int oid,int oauxt,int oauxi,int flags,char *description) +{ + //make sure we can fit another object + if(count>=MAX_UNIQUE_INVEN_ITEMS) + return false; + + inven_item *current = root, *prev = root, *newnode; + + if(count==0) + { + //there are no items in the list...time to add + root = new inven_item; + newnode = root; + root->next = root; + root->prev = root; + newnode->count = 1; + } + else + { + newnode = FindItem(otype,oid); + + if(!newnode) + { + //NEEDTODO: adjust so it adds in order + + newnode = new inven_item; + + prev = root->prev; + + newnode->prev = prev; + prev->next = newnode; + newnode->next = root; + root->prev = newnode; + + newnode->count = 1; + } + else + { + //there is an item of that type/id already, just increase it's count + newnode->count++; + } + } + + //its a new item type/id, so fill in its info + if(newnode->count==1) + { + newnode->type = otype; + newnode->id = oid; + newnode->flags = 0; + newnode->iflags = 0; + newnode->otype = oauxt; + newnode->oid = oauxi; + + if(Object_info[oid].description) + { + newnode->description = (char *)mem_malloc(strlen(Object_info[oid].description)+1); + strcpy(newnode->description,Object_info[oid].description); + } + else + { + newnode->description = (char *)mem_malloc(sizeof(char)); + newnode->description[0] = 0; + } + + if(Object_info[oid].flags & OIF_INVEN_SELECTABLE) + newnode->iflags |= INVF_SELECTABLE; + //if(!(Object_info[oid].flags & OIF_INVEN_NONUSEABLE)) + // newnode->iflags |= INVF_USEABLE; + if(Object_info[oid].flags & OIF_INVEN_TYPE_MISSION) + newnode->iflags |= INVF_MISSIONITEM; + if(Object_info[oid].flags & OIF_INVEN_NOREMOVE) + newnode->iflags |= INVF_NOREMOVEONUSE; + if(Object_info[oid].flags & OIF_INVEN_VISWHENUSED) + newnode->iflags |= INVF_VISWHENUSED; + + if(flags&INVAF_NOTSPEWABLE) + newnode->iflags |= INVF_NOTSPEWABLE; + if(flags&INVAF_TIMEOUTONSPEW) + newnode->iflags |= INVF_TIMEOUTONSPEW; + if(flags&INVAF_LEVELLAST) + newnode->iflags |= INVAF_LEVELLAST; + + newnode->icon_name = (char *)mem_malloc(strlen(Object_info[oid].icon_name)+1); + strcpy(newnode->icon_name,Object_info[oid].icon_name); + + if(description) + { + newnode->name = mem_strdup(description); + }else + { + newnode->name = mem_strdup(Object_info[oid].name); + } + + count++; + } + + if(newnode->iflags & INVF_SELECTABLE) + pos = newnode; + + return true; +} + +//uses an item in the inventory (returns false if the item doesn't exist) +bool Inventory::Use(int type,int id,object *parent) +{ + inven_item *node; + + node = FindItem(type,id); + + if(!node) + return false; + + if(!(node->iflags&INVF_USEABLE)) + return false; + + bool multiplayer = (bool)((Game_mode&GM_MULTI)!=0); + bool client; + bool server; + bool ret = false; + + if(multiplayer){ + if(Netgame.local_role&LR_SERVER){ + client = false; + server = true; + }else{ + client = true; + server = false; + } + }else{ + client = false; + server = false; + } + + if(client){ + //OK, we're a client in a multiplayer game, so send a request to the server to use this Item + SendRequestToServerToUse(node->type,node->id); + + return false; + } + + //If we got here, then we are either the server in a multiplayer game, or in a single player game + + + //get player object (needed when we recreate the object) + object *player; + ASSERT(parent); + player = parent; + + if(player==NULL) + { + Int3(); + return false; + } + + //if type is OBJ_WEAPON then it's a countermeasure + if(type==OBJ_WEAPON){ + mprintf((0,"CounterMeasures: Use\n")); + //countermeasure + CreateCountermeasureFromObject(player,id); + Remove(node->type,node->id); + ret = true; + }else{ + mprintf((0,"Inventory: Use\n")); + //regular + //recreate the object + int objnum; + int roomnum; + bool remove_on_use; + bool vis_when_created = false; + + if(node->iflags & INVF_NOREMOVEONUSE ) + remove_on_use = false; + else + remove_on_use = true; + + if(node->iflags & INVF_VISWHENUSED ) + vis_when_created = true; + else + vis_when_created = false; + + roomnum = player->roomnum; + + if(node->iflags&INVF_OBJECT) + { + //don't recreate the object..it already exists + object *obj = ObjGet(node->type); + if(!obj){ + Int3(); //object no longer exists + return false; + } + objnum = OBJNUM(obj); + + obj->flags &= ~OF_INPLAYERINVENTORY; + + if( vis_when_created ) + { + ObjUnGhostObject(objnum); + MultiSendGhostObject( obj, false); + } + + }else + { + + objnum = ObjCreate(node->type,node->id,roomnum,&player->pos,NULL,player->handle); + if(objnum==-1){ + Int3(); + return false; + } + + if( !vis_when_created ){ + if(Objects[objnum].control_type != CT_AI) + { + SetObjectControlType(&Objects[objnum], CT_NONE); + } + Objects[objnum].movement_type = MT_NONE; + Objects[objnum].render_type = RT_NONE; + } + + Objects[objnum].flags = node->flags; + + if(server)//if we're the server, then we need to send this object to the clients + MultiSendObject(&Objects[objnum],0); + + InitObjectScripts(&Objects[objnum]); + + } + + tOSIRISEventInfo ei; + ei.evt_use.it_handle = player->handle; + if(Osiris_CallEvent(&Objects[objnum],EVT_USE, &ei)){ + //if we're the server tell the clients to remove this item from their inventory + Remove(node->type,node->id); + ret = true; + }else{ + if(node->iflags&INVF_OBJECT) + Objects[objnum].flags |= OF_INPLAYERINVENTORY; //mark as being in inventory + } + + if( remove_on_use ){ + //now we need to kill the object + SetObjectDeadFlag(&Objects[objnum],true); + } + } + return ret; +} + +//sends a request to the server to use a particular item in the inventory +void Inventory::SendRequestToServerToUse(int type,int id) +{ + //mprintf((0,"Sending request to server for T=%d ID=%d\n",type,id)); + inven_item *node = FindItem(type,id); + if(node){ + MultiSendClientInventoryUseItem(type,id); + }else{ + mprintf((0,"Sorry couldn't find it in your inventory\n")); + } +} + +//searches the inventory for the specified type/id, sets the pos to it +bool Inventory::FindPos(int type,int id) +{ + int oldt,oldi; + int ttype,tid; + + //save current pos + GetPosTypeID(oldt,oldi); + + //try to move to the specified pos + GotoPos(type,id); + + //see if we got there + GetPosTypeID(ttype,tid); + if( (ttype==type) && (tid==id) ) + return true; + else{ + //nope, so restore the old pos + GotoPos(oldt,oldi); + return false; + } +} + +//uses an item in the inventory (currently selected one) (returns false if the item doesn't exist) +bool Inventory::UsePos(object *parent) +{ + if(pos) + { + return Use(pos->type,pos->id,parent); + } + else + return false; +} + +bool Inventory::Use(int objhandle,object *parent) +{ + return Use(objhandle,-1,parent); +} + +//removes an item from inventory, without using it (returns true on success, false if object didn't exist) +bool Inventory::Remove(int type,int id) +{ + inven_item *node; + + node = FindItem(type,id); + + if(!node) + return false; + + if(node->iflags&INVF_OBJECT) + { + //always remove + object *obj = ObjGet(type); + ASSERT(obj); + if(obj) + { + obj->flags &= ~OF_INPLAYERINVENTORY; + } + + RemoveNode(node); + }else + { + node->count--; + mprintf((0,"Inventory System: Remove\n")); + + if(node->count<=0) + RemoveNode(node); + } + + return true; +} + +//removes a node from the list, decrementing the count +void Inventory::RemoveNode(inven_item *node) +{ + if(!node) + return; + + bool movepos = false; + count--; + inven_item *prev,*next; + + prev = node->prev; + next = node->next; + + if(node->description) + mem_free(node->description); + if(node->icon_name) + mem_free(node->icon_name); + if(node->name) + mem_free(node->name); + + if(pos==node) + { + if(pos->next!=node) + { + movepos=true; + pos=pos->next; + } + else + pos=NULL; + } + + + if(node==root) + { + if(root->next!=root) + { + inven_item *n = root->next; + inven_item *p = root->prev; + delete node; + root = n; + root->prev = p; + p->next = root; + } + else + { + delete node; + root = NULL; + } + } + else + { + delete node; + prev->next = next; + next->prev = prev; + } + + if(movepos) + ValidatePos(); +} + + +//given a type and id, it returns the first matching inventory item +inven_item *Inventory::FindItem(int type,int id) +{ + inven_item *current = root; + + if(count==0) + return NULL; //there are no items, don't even bother + + int counter = count; + + while(counter) + { + if( (current->type==type) && (current->id==id) ) //we got a match + { + //mprintf((0,"Inventory: FindItem found Type(%d) ID(%d)\n",type,id)); + return current; + } + + current = current->next; + counter--; + } + + //mprintf((0,"Inventory: FindItem couldn't find Type(%d) ID(%d)\n",type,id)); + return NULL; +} + + +//returns how many items are in the inventory +int Inventory::Size(void) +{ + //mprintf((0,"Inventory System: Size\n")); + return count; +} + +//returns true if there is an item in the inventory with the given type/id +bool Inventory::CheckItem(int type,int id) +{ + //mprintf((0,"Inventory System: CheckItem\n")); + if(FindItem(type,id)) + return true; + else + return false; +} + +//saves the inventory to the file (returns number of bytes written) +int Inventory::SaveInventory(CFILE *file) +{ + int num_items = Size(); + + int start_pos = cftell(file); + + int pos_pos = 0; + int pos_count = 0; + + cf_WriteInt(file,num_items); + if(num_items>0) + { + inven_item *curr; + curr = root; + + while(num_items>0) + { + if(pos==curr) + { + pos_pos = pos_count; + } + + if(curr->id==-1) + { + //make sure it is a valid object + object *obj = ObjGet(curr->type); + ASSERT(obj); + if(!obj) + { + mprintf((0,"Invalid object saving inventory\n")); + curr = curr->next; + num_items--; + pos_count++; + continue; + } + } + + cf_WriteInt(file,curr->type); + cf_WriteInt(file,curr->otype); + cf_WriteInt(file,curr->id); + cf_WriteInt(file,curr->oid); + cf_WriteInt(file,curr->flags); + cf_WriteInt(file,curr->count); + + if(curr->description) + cf_WriteString(file,curr->description); + else + cf_WriteByte(file,0); + + if(curr->icon_name) + cf_WriteString(file,curr->icon_name); + else + cf_WriteByte(file,0); + + if(curr->name) + cf_WriteString(file,curr->name); + else + cf_WriteByte(file,0); + + cf_WriteInt(file,curr->iflags); + + curr = curr->next; + num_items--; + pos_count++; + } + } + + cf_WriteInt(file,pos_pos); + + int end_pos = cftell(file); + + return (end_pos-start_pos); +} + + +//restores the inventory from file (returns number of bytes read) +int Inventory::ReadInventory(CFILE *file) +{ + int start_pos = cftell(file); + + int num_items = cf_ReadInt(file); + count = num_items; + root = NULL; + char temp[512]; + + int t,i,otype; + + if(num_items>0) + { + inven_item *item,*prev; + + while(num_items>0) + { + t = cf_ReadInt(file); + otype = cf_ReadInt(file); + i = cf_ReadInt(file); + + //make sure the object is valid + if(i==-1) + { + object *obj = ObjGet(t); + ASSERT(obj); + if(!obj) + { + mprintf((0,"Invalid object restoring inventory\n")); + //skip this object + cf_ReadInt(file); + cf_ReadInt(file); + cf_ReadInt(file); + cf_ReadString(temp,512,file); + cf_ReadString(temp,512,file); + cf_ReadString(temp,512,file); + cf_ReadInt(file); + num_items--; + count--; + continue; + } + } + + if(root==NULL) + { + //there are no items in the list...time to add + root = new inven_item; + item = root; + root->next = root; + root->prev = root; + } + else + { + item = new inven_item; + + prev = root->prev; + + item->prev = prev; + prev->next = item; + item->next = root; + root->prev = item; + } + + item->type = t; + item->id = i; + item->otype = otype; + item->oid = cf_ReadInt(file); + item->flags = cf_ReadInt(file); + item->count = cf_ReadInt(file); + + cf_ReadString(temp,512,file); + item->description = mem_strdup(temp); + + cf_ReadString(temp,512,file); + item->icon_name = mem_strdup(temp); + + cf_ReadString(temp,512,file); + item->name = mem_strdup(temp); + + item->iflags = cf_ReadInt(file); + + num_items--; + } + } + + int pos_index = cf_ReadInt(file); + GotoPos(pos_index); + ValidatePos(); + + int end_pos = cftell(file); + + return (end_pos-start_pos); +} + + +//resets the position pointer in the list to the beginning +void Inventory::ResetPos(void) +{ + pos = root; +} + +//moves the position pointer to the next inventory item +void Inventory::NextPos(bool skip) +{ + if( (pos) && (pos->next) ) + { + pos = pos->next; + }else if(!pos){ + pos = root; + }else + return; + + if(!skip) + ValidatePos(); +} + +//moves the position pointer to the previous inventory item +void Inventory::PrevPos(bool skip) +{ + if((pos) && (pos->prev)) + { + pos = pos->prev; + }else if(!pos){ + pos = root; + }else + return; + + if(!skip) + ValidatePos(false); +} + +//returns true if the position pointer is at the begining of the inventory list +bool Inventory::AtBeginning(void) +{ + if(!pos) + return true; + if(pos==root) + return true; + else + return false; +} + +//returns false if the position pointer is at the end of the inventory list +bool Inventory::AtEnd(void) +{ + if(!pos) + return true; + if(pos->next==root) + return true; + else + return false; +} + +//returns the type/id of the item at the current position +//returns true if the pos is a real object in the game +//returns false if the pos is just a type/id inventory item +bool Inventory::GetPosTypeID(int &type,int &id) +{ + if(!pos) + { + type = id = 0; + return false; + } + + type = pos->type; + id = pos->id; + + if(pos->iflags&INVF_OBJECT) + return true; + else + return false; +} + +//returns the aux type/id of the item +//returns true if the pos is a real object in the game +//returns false if the pos is just a type/id inventory item +bool Inventory::GetAuxPosTypeID(int &type,int &id) +{ + if(!pos){ + type = id = 0; + return false; + } + type = pos->otype; + id = pos->oid; + + ASSERT(type != OBJ_NONE); + + if(pos->iflags&INVF_OBJECT) + return true; + else + return false; +} + +//returns the description of the item at the current position +char *Inventory::GetPosDescription(void) +{ + //mprintf((0,"Getting Pos Description (%s)\n",pos->description)); + if(!pos) + return NULL; + return pos->description; +} + +//returns the name of the item at the current position +char *Inventory::GetPosName(void) +{ + if(!pos) + return NULL; + return pos->name; +} + +//returns the icon name of the item at the current position +char *Inventory::GetPosIconName(void) +{ + if(!pos) + return NULL; + return pos->icon_name; +} + +//returns the count of the item at the current position +int Inventory::GetPosCount(void) +{ + //mprintf((0,"Getting Pos Count (%d)\n",pos->count)); + if(!pos) + return 0; + + if(pos->iflags&INVF_OBJECT) + return 1; + + return pos->count; +} + +//return information about the current position item +//returns true if the pos is a real object in the game +//returns false if the pos is just a type/id inventory item +bool Inventory::GetPosInfo(ushort &iflags,int &flags) +{ + if(!pos){ + iflags = 0; + flags = 0; + return false; + } + + iflags = pos->iflags; + flags = pos->flags; + + if(pos->iflags&INVF_OBJECT) + return true; + else + return false; +} + + +//goes to a position in the list +void Inventory::GotoPos(int newpos) +{ + //mprintf((0,"Going to Pos (%d)\n",newpos)); + ResetPos(); + int i; + for(i=0;inext!=NULL)) + pos = pos->next; + } +} + +//moves the current item pointer to the specified type/id +void Inventory::GotoPos(int type,int id) +{ + inven_item *node = FindItem(type,id); + + if(node) + pos = node; +} + +//returns the "index" position of the current item +int Inventory::GetPos(void) +{ + int type,id; + int ctype,cid; + int count = 0; + bool done = false; + + if(!Size()) + return -1; + + bool is_object,cis_object; + + is_object = GetPosTypeID(type,id); + ResetPos(); + + done = AtEnd(); + + while(!done) + { + cis_object = GetPosTypeID(ctype,cid); + if( (ctype==type) && (cid==id) && (cis_object==is_object) ) + return count; + + count++; + done = AtEnd(); + NextPos(false); + } + return 0; +} + +//moves to the next/prev item in the inventory list (forward==TRUE means forward, forward==FALSE means go backwards) +void InventorySwitch(bool forward) +{ + int ctype,cid; + + Players[Player_num].inventory.GetPosTypeID(ctype,cid); + + if(ctype!=0) + { + if(forward) + { + Players[Player_num].inventory.NextPos(); + } + else + { + Players[Player_num].inventory.PrevPos(); + } + + int ntype,nid; + Players[Player_num].inventory.GetPosTypeID(ntype,nid); + + if(ntype!=ctype || nid!=cid) + { + //AddHUDMessage(TXT_WPNSELECT, Players[Player_num].inventory.GetPosName()); + Sound_system.Play2dSound(SOUND_CHANGE_INVENTORY); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.0f; + hear.curiosity_level = 0.3f; + hear.max_dist = AI_SOUND_SHORT_DIST; + AINotify(&Objects[Players[Player_num].objnum], AIN_HEAR_NOISE, (void *)&hear); + } + } +} + +//moves to the next/prev item in the counter measures list (forward==TRUE means forward, forward==FALSE means go backwards) +void CounterMeasuresSwitch(bool forward) +{ + int ctype,cid; + + Players[Player_num].counter_measures.GetPosTypeID(ctype,cid); + + if(ctype!=0) + { + if(forward) + { + Players[Player_num].counter_measures.NextPos(); + } + else + { + Players[Player_num].counter_measures.PrevPos(); + } + + int ntype,nid; + Players[Player_num].counter_measures.GetPosTypeID(ntype,nid); + + if(ntype!=ctype || nid!=cid) + { + AddHUDMessage(TXT_WPNSELECT, Players[Player_num].counter_measures.GetPosName()); + Sound_system.Play2dSound(SOUND_CHANGE_COUNTERMEASURE); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.0f; + hear.curiosity_level = 0.3f; + hear.max_dist = AI_SOUND_SHORT_DIST; + AINotify(&Objects[Players[Player_num].objnum], AIN_HEAR_NOISE, (void *)&hear); + } + } +} + +//repositions the pos so its in the correct spot +void Inventory::ValidatePos(bool forward) +{ + if(!pos) + return; + if(pos->iflags & INVF_SELECTABLE) + return; + + inven_item *node; + if(forward) + node = pos->next; + else + node = pos->prev; + + while(node!=pos) + { + if(node->iflags & INVF_SELECTABLE) + { + pos = node; + return; + } + + if(forward) + node = node->next; + else + node = node->prev; + } + pos = NULL; +} + +//returns how many of a type/id exists in the inventory +int Inventory::GetTypeIDCount(int type,int id) +{ + inven_item *node = FindItem(type,id); + + if(!node) + return 0; + if(node->flags&INVF_OBJECT) + { + return 1; + } + + return node->count; +} + +//determines whether the position is selectable +bool Inventory::IsSelectable(void) +{ + if(!pos) + return false; + + return ((pos->iflags & INVF_SELECTABLE)!=0); +} + +//determines whether the position is selectable +bool Inventory::IsUsable(void) +{ + if(!pos) + return false; + + return ((pos->iflags & INVF_USEABLE)!=0); +} + + +//gets a detailed list of information about what is in the inventory +//returns the number of items filled in. +int Inventory::GetInventoryItemList(tInvenList *list,int max_amount,int *cur_sel) +{ + ASSERT(cur_sel); + *cur_sel = -1; + + if(max_amount<=0) + return 0; + + inven_item *current = root; + if(count==0) + return 0; //there are no items, don't even bother + + int counter = count; + int cur_count = 0; + + while(counter) + { + if(cur_countcount; + list[cur_count].hud_name = current->name; + list[cur_count].selectable = (current->iflags&INVF_SELECTABLE)?true:false; + cur_count++; + } + + current = current->next; + counter--; + } + + return count; +} + + +//use the currently selected inventory item +bool UseInventoryItem() +{ + int type,id; + + Players[Player_num].inventory.GetPosTypeID(type,id); + if( !type && !id ) + return false; + + if(Players[Player_num].inventory.UsePos(&Objects[Players[Player_num].objnum])){ + if(Game_mode&GM_MULTI && (Netgame.local_role==LR_SERVER)){ + MultiSendInventoryRemoveItem(Player_num,type,id); + } + } + return true; +} + +//use the currently selected countermeasure +bool UseCountermeasure() +{ + int type,id; + + Players[Player_num].counter_measures.GetPosTypeID(type,id); + if( !type && !id ) + return false; + + if(Players[Player_num].counter_measures.UsePos(&Objects[Players[Player_num].objnum])){ + if(Game_mode&GM_MULTI && (Netgame.local_role==LR_SERVER)){ + MultiSendInventoryRemoveItem(Player_num,type,id); + } + } + return true; +} + +// Checks for an object in any of the players inventorys and removes it +void InventoryRemoveObject(int objhandle) +{ + object *obj = ObjGet(objhandle); + ASSERT(obj); + if(!obj) + return; + + if(!(obj->flags&OF_INPLAYERINVENTORY)) //not in the player's inventory + return; + + //go through all the players and look for the object + for(int i=0;icreation_time + * + * 12 7/24/97 3:23p Matt + * Saved several additional editor state variables + * + * 11 7/23/97 6:27p Jason + * added code to support terrain simplification + * + * 10 7/22/97 10:32a Matt + * Added code to load and save portals + * + * 9 7/20/97 7:37p Jason + * added new sky + * + * 8 7/17/97 7:22p Matt + * Save and load level scripts + * + * 7 7/17/97 3:57p Matt + * Re-orthogonalize the orientation matrices of objects when read it + * + * 6 7/17/97 3:00p Jason + * changed "moons" to be called satellites since we're going to have suns + * and other things in orbit + * + * + * 35 6/24/97 4:23p Jason + * changes for y only terrain positions + * + * 34 6/17/97 4:16p Jason + * added some terrain features + * + * 33 6/02/97 1:06p Samir + * Initialize scripts for objects (for now, NULL) + * + * 32 5/22/97 5:16p Jason + * added tmap2 capability to the terrain + * + * 31 5/15/97 2:16p Matt + * Set Highest_segment_index when reading in a level (for when no editor) + * + * 30 5/13/97 10:29p Matt + * Fixed a stupid bug + * + * 29 5/13/97 7:47p Matt + * Added code to compress mine before saving. + * Cleaned up code dealing with num vertices & segments. + * + * 28 5/13/97 12:01p Matt + * Added support for floating segments + * + * 27 5/09/97 1:44p Matt + * Fixed code that was deleting a terrain object because it thought it had + * a bad segnum. Not only that, but it was deleting the object + * improperly. + * + * 26 5/08/97 3:21p Jason + * made terrain save/restore mine links correctly + * + * 25 5/06/97 3:46p Matt + * Init terrain data structs before loading from disk + * + * 24 5/01/97 6:15p Jason + * fixed stupid sky bug...I wasn't reading in the correct number of satellite + * vectors + * + * 23 4/17/97 12:14p Jason + * now saves terrain sky info + * + * 22 4/04/97 2:57p Matt + * Added code to initialize all the type-specific data for an object from + * the page for that object type. This means that we need to pass less + * info to ObjCreate(), and that we save less info in the level save file. + * It also makes it easy to reset all the objects when an object page has + * changed. + * + * 21 4/03/97 12:46p Matt + * Added code to remap robots, powerups, & doors + * + * 20 4/01/97 10:48p Matt + * Record player starts after loading in level + * + * 19 3/21/97 5:25p Matt + * Fixed problem with writing out textures for remapping if array had + * unused textures in it. + * + * 18 3/21/97 5:01p Jason + * incremental terrain improvments + * + * + * 17 3/17/97 7:36p Samir + * Stupid index counter bugs. + * + * 16 3/14/97 12:38p Samir + * Resets active doorways to 0 when loading level. + * + * 15 3/14/97 12:17p Chris + * Abstract type for the low level sound lib + * + * 14 3/12/97 3:23p Matt + * Make ReadSegment() and WriteSegment() public, and removed a bunch of + * version number passing that wasn't being used. + * + * 13 3/05/97 3:33p Samir + * Saving and loading of triggers, doorways. + * + * 12 3/03/97 6:21p Matt + * Changed cfile functions to use D3 naming convention + * + * 11 3/03/97 11:29a Matt + * Save & load selected segment list + * + * 10 2/28/97 2:29 PM Jeremy + * #ifdef EDITOR around ResetFreeSegList and ResetVertices calls. + * + * 9 2/27/97 4:35p Matt + * Fixed up loading of objects + * + * 8 2/27/97 11:04a Matt + * Save texture names to file, & remap textures when loading + * + * 7 2/26/97 12:14p Matt + * Fixed bug when Markedsegp == NULL + * + * 6 2/24/97 5:44p Matt + * Save & load curseg & markedseg + * + * 5 2/21/97 7:30p Matt + * New names for a few segment elements + * + * 4 2/21/97 5:23p Matt + * Added new ResetVertices() to set Vertex_active array after level + * loaded. Renamed old ResetVertices() to InitVertices(); + * + * 3 2/11/97 6:52p Matt + * Made level save/read now do objects + * + * 2 2/10/97 5:38p Matt + * Got code working + * + * 1 2/10/97 11:21a Matt + * + * $NoKeywords: $ + */ + +#ifdef NEWEDITOR +#include "..\neweditor\stdafx.h" +#endif + +#include +#include +#include + +#include "LoadLevel.h" + +#include "CFILE.H" + +#include "descent.h" +#include "object.h" +#include "gametexture.h" + +#ifdef NEWEDITOR +#include "..\neweditor\ned_gametexture.h" +#include "..\neweditor\ned_Object.h" +#include "editor\Erooms.h" +#endif + +#include "trigger.h" +#include "doorway.h" +#include "terrain.h" +#include "player.h" +#include "door.h" +#include "objinit.h" +#include "room.h" +#include "objinfo.h" +#include "lightmap.h" +#include "lightmap_info.h" +#include "findintersection.h" +#include "polymodel.h" +#include "object_lighting.h" +#include "bsp.h" +#include "gamepath.h" +#include "game.h" +#include "BOA.h" +#include "mem.h" +#include "lighting.h" +#include "Mission.h" +#include "render.h" +#include "weapon.h" +#include "special_face.h" +#include "stringtable.h" +#include "ambient.h" +#include "matcen.h" +#include "dedicated_server.h" +#include "PHYSICS.H" +#include "levelgoal.h" +#include "aiambient.h" +#include "args.h" +#include "ddio.h" +#include "ship.h" +#include "fireball.h" +#include "sounds.h" +#include "soundload.h" +#include "bnode.h" +#include "localization.h" + +#ifdef EDITOR +#include "editor\d3edit.h" +#include "editor\HFile.h" +#include "editor\Erooms.h" +#include "editor\moveworld.h" +#include "editor\editor_lighting.h" +#endif + +#ifdef NEWEDITOR +#include "..\neweditor\neweditor.h" +#include "..\neweditor\globals.h" +#endif + +MD5 *Level_md5 = NULL; + +char * LocalizeLevelName(char *level); + +#define LEVEL_FILE_TAG "D3LV" + +// Lightmap remap array +int Num_lightmap_infos_read=0; +ushort LightmapInfoRemap[MAX_LIGHTMAP_INFOS]; + +//Arrays for mapping saved data to the current data +short texture_xlate[MAX_TEXTURES]; +short door_xlate[MAX_DOORS]; +short generic_xlate[MAX_OBJECT_IDS]; + +#ifdef EDITOR +extern float GlobalMultiplier; +#endif + + +//Xlate types +#define XT_GENERIC 0 +#define XT_DOOR 1 + +#if (defined(EDITOR) || defined(NEWEDITOR)) +//Code to keep track of failed xlate items so can print out a nice message if the item is used + +#define MAX_FAILED_XLATE_ITEMS 1500 + +struct { + short type,id; + char name[PAGENAME_LEN]; +} Failed_xlate_items[MAX_FAILED_XLATE_ITEMS]; + +int Num_failed_xlate_items; + +void AddFailedXLateItem(int type,int id,char *name) +{ + ASSERT(Num_failed_xlate_items < MAX_FAILED_XLATE_ITEMS); + + Failed_xlate_items[Num_failed_xlate_items].type = type; + Failed_xlate_items[Num_failed_xlate_items].id = id; + strcpy(Failed_xlate_items[Num_failed_xlate_items].name,name); + + Num_failed_xlate_items++; +} + +char *GetFailedXLateItemName(int type,int id) +{ + for (int i=0;i"; +} + +#endif + +//Useful macros +#define cf_ReadVector(f,v) do {(v)->x=cf_ReadFloat(f); (v)->y=cf_ReadFloat(f); (v)->z=cf_ReadFloat(f); } while (0) +#define cf_ReadMatrix(f,m) do {cf_ReadVector((f),&(m)->rvec); cf_ReadVector((f),&(m)->uvec); cf_ReadVector((f),&(m)->fvec); } while (0) + +// Lets put some function prototypes here +void ReadAllTriggers(CFILE *ifile); +void ReadAllDoorways(CFILE *ifile); +void WriteAllTriggers(CFILE *ofile); +void WriteAllDoorways(CFILE *ofile); + +//Prior to version 49, terrain objects had this flag set +#define OLD_OBJECT_OVER_TERRAIN_FLAG 256 + +//Macro to translate old file handles (pre-version 94) to new ones +#define OLD_HANDLE_OBJNUM_MASK 0x3ff //to mask off the object number part of the handle +#define OLD_HANDLE_COUNT_MASK 0xfffffc00 //to maks off the count part of the handle +#define XLATE_HANDLE(handle) ((((handle) & OLD_HANDLE_COUNT_MASK) << 1) + ((handle) & OLD_HANDLE_OBJNUM_MASK)) + + +/* + + The following struct/array is used for (right now only in OEM) to convert + a type/id object when loading a level to another type/id. It's only for + generic objects. For the object_convert[]: + + index 0 = old object index 1 = new object + index 2 = old object index 3 = new object + ... + + Note: Set the id to -2, if it is -2, then I resolve it. +*/ +#define CONV_MULTI 0x01 +#define CONV_SINGLE 0x02 +typedef struct +{ + int type,id; + char *name; + ubyte flag; +}tConvertObject; + +tConvertObject object_convert[] = +{ +#ifdef DEMO + {OBJ_POWERUP,-2,"Blackshark",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"ImpactMortar",0}, + {OBJ_POWERUP,-2,"Mega",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"ImpactMortar",0}, + {OBJ_POWERUP,-2,"EMDlauncher",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"SuperLaser",0}, + {OBJ_POWERUP,-2,"Omegacannon",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Massdriver",0}, +#else + {OBJ_POWERUP,-2,"Blackshark",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"SuperLaser",0}, + {OBJ_POWERUP,-2,"Mega",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Plasmacannon",0}, + {OBJ_POWERUP,-2,"EMDlauncher",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Microwave",0}, + {OBJ_POWERUP,-2,"Omegacannon",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Massdriver",0}, +#endif + +#ifdef DEMO + {OBJ_POWERUP,-2,"Plasmacannon",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"SuperLaser",0}, + {OBJ_POWERUP,-2,"Microwave",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"SuperLaser",0}, + {OBJ_POWERUP,-2,"NapalmRocket",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Homing",0}, + {OBJ_POWERUP,-2,"Guided",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Homing",0}, + {OBJ_POWERUP,-2,"4packGuided",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Homing",0}, + {OBJ_POWERUP,-2,"Cyclone",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"ImpactMortar",0}, +#endif + {OBJ_POWERUP,-2,"Fusioncannon",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Vauss",0}, + {OBJ_POWERUP,-2,"Smart",CONV_MULTI|CONV_SINGLE},{OBJ_POWERUP,-2,"Homing",0}, + {OBJ_POWERUP,-2,"chaff",CONV_MULTI},{OBJ_POWERUP,-2,"energy",0}, + {OBJ_POWERUP,-2,"Betty4pack",CONV_MULTI},{OBJ_POWERUP,-2,"energy",0}, + {OBJ_POWERUP,-2,"Seeker3pack",CONV_MULTI},{OBJ_POWERUP,-2,"energy",0}, + {OBJ_POWERUP,-2,"ProxMinepowerup",CONV_MULTI},{OBJ_POWERUP,-2,"energy",0} +}; + + +int object_convert_size = sizeof(object_convert)/sizeof(tConvertObject); + +int chunk_start,chunk_size,filelen; + +int CountDataToPageIn(); + +//Find a valid, usable ID of the specified type +int FindValidID(int type) +{ + int id = -1; + + switch (type) { + case OBJ_ROBOT: + case OBJ_POWERUP: + case OBJ_BUILDING: + case OBJ_CLUTTER: + id = GetObjectID(type); + break; + + case OBJ_DOOR: { + int i; + + for (i=0;i=0); + + if(*id == object_convert[i].id) + { + convert = false; + c_multi = (object_convert[i].flag&CONV_MULTI)?true:false; + c_single = (object_convert[i].flag&CONV_SINGLE)?true:false; + + if(is_multi && c_multi) + convert = true; + if( (!is_multi) && c_single) + convert = true; + + //convert! + if(convert) + { + int convert_to = i+1; + + if(object_convert[convert_to].id == -2) + { + object_convert[convert_to].id = FindObjectIDName(object_convert[convert_to].name); + } + + ASSERT(object_convert[convert_to].id>=0); + + if(object_convert[convert_to].id>=0) + { + mprintf((0,"LEVELLOAD: Converting: '%s' -> '%s'\n",object_convert[i].name,object_convert[convert_to].name)); + + new_id = object_convert[convert_to].id; + new_type = object_convert[convert_to].type; + } + } + } + } + } + + *type = new_type; + *id = new_id; +#endif +#endif +} + +//reads an object +//returns 1 if read ok +int ReadObject(CFILE *ifile,object *objp,int handle,int fileversion) +{ + int type,id,old_id,i; + int roomnum; + float door_shields; + char tempname[OBJ_NAME_LEN+1]=""; + + type = cf_ReadByte(ifile); + + if (fileversion >= 34) + id = cf_ReadShort(ifile); + else + id = (ubyte) cf_ReadByte(ifile); + + //Translate id + old_id = id; + switch (type) { + case OBJ_ROBOT: + case OBJ_POWERUP: + case OBJ_BUILDING: + case OBJ_CLUTTER: + id = generic_xlate[id]; + break; + + case OBJ_DOOR: + id = door_xlate[id]; + break; + } + //Check for object not found + if (id == -1) { + id = FindValidID(type); //find any valid id + + ASSERT(id != -1); + + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (GetFunctionMode() == EDITOR_MODE) { + char *old_name = GetFailedXLateItemName((type==OBJ_DOOR)?XT_DOOR:XT_GENERIC,old_id); + char *new_name = (type == OBJ_DOOR) ? Doors[id].name : Object_info[id].name; + OutrageMessageBox("Object %d (type %s) has undefined ID %d, \"%s\".\n\n" + "This object will be converted to ID %d, \"%s\".", + objp-Objects,Object_type_names[type],old_id,old_name,id,new_name); + } + #endif + } + + //Convert un-allowed OEM objects + if(IS_GENERIC(type)) + { + ConvertObject(&type,&id); + } + + //Read the object's name + if (fileversion >= 95) { + cf_ReadString(tempname,sizeof(tempname),ifile); + if (strlen(tempname)) { + if (type == OBJ_PLAYER) { + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (GetFunctionMode() == EDITOR_MODE) + OutrageMessageBox("Object %d, Player %d has a name (\"%s\") which has been deleted.", + objp-Objects,id,tempname); + #endif + tempname[0] = 0; //kill name + } + } + } + + //Read the flags + uint flags; + if (fileversion >= 101) + flags = cf_ReadInt(ifile); + else + flags = (ushort) cf_ReadShort(ifile); + + //Make sure no objects except viewers have the outside mine flags set + ASSERT((type == OBJ_VIEWER) || !(flags & OF_OUTSIDE_MINE)); + + //If a door, read the shields + if ((type == OBJ_DOOR) && (fileversion >= 109)) + door_shields = cf_ReadShort(ifile); + + //Get the room number + roomnum = cf_ReadInt(ifile); + + //For old files, check if this object is on terrain + if (fileversion < 49) + { + if (flags & OLD_OBJECT_OVER_TERRAIN_FLAG) + { + flags &= ~OLD_OBJECT_OVER_TERRAIN_FLAG; + roomnum = MAKE_ROOMNUM(roomnum); + } + } + + //Get the position + vector pos; + cf_ReadVector(ifile,&pos); + + //Initialize the object + ObjInit(objp,type,id,handle,&pos,0.0); + + //Set the stuff we've already read in + objp->flags |= (flags & OBJECT_SAVE_LOAD_FLAGS); + objp->roomnum = roomnum; + + //Set shields if this is a door + if ((type == OBJ_DOOR) && (fileversion >= 109)) + objp->shields = door_shields; + + //Do some wacky weapon thing + if (fileversion<=36) + { + if (objp->type==OBJ_PLAYER || objp->type==OBJ_POWERUP || objp->type==OBJ_ROBOT || + objp->type==OBJ_BUILDING || objp->type==OBJ_DOOR || objp->type==OBJ_DEBRIS) + objp->flags|=OF_POLYGON_OBJECT; + if (objp->type==OBJ_WEAPON) + { + if (!((Weapons[objp->id].flags & WF_IMAGE_BITMAP) || (Weapons[objp->id].flags & WF_IMAGE_VCLIP))) + objp->flags|=OF_POLYGON_OBJECT; + } + } + + //Load and set the orientation + matrix orient; + cf_ReadMatrix(ifile,&orient); + vm_Orthogonalize(&orient); + ObjSetOrient(objp, &orient); + ObjSetAABB(objp); + + //Set the name + if (tempname[0]) { + objp->name = (char *) mem_malloc(strlen(tempname)+1); + strcpy(objp->name,tempname); + } + + // Update checksum + AppendToLevelChecksum(objp->pos.x); + AppendToLevelChecksum(objp->pos.y); + AppendToLevelChecksum(objp->pos.z); + + objp->contains_type = cf_ReadByte(ifile); + objp->contains_id = cf_ReadByte(ifile); + objp->contains_count = cf_ReadByte(ifile); + + objp->lifeleft = cf_ReadFloat(ifile); + + if ((fileversion >= 65) && (fileversion < 111)) + cf_ReadInt(ifile); //was parent_handle + + //Read sound info if this is a soundsource object + if (objp->control_type == CT_SOUNDSOURCE) { + ASSERT(objp->type == OBJ_SOUNDSOURCE); + if (fileversion < 119) + objp->ctype.soundsource_info.sound_index = cf_ReadInt(ifile); + else { + char soundname[PAGENAME_LEN]; + cf_ReadString(soundname,sizeof(soundname),ifile); + objp->ctype.soundsource_info.sound_index = soundname[0] ? FindSoundName(IGNORE_TABLE(soundname)) : -1; + } + objp->ctype.soundsource_info.volume = cf_ReadFloat(ifile); + } + + if( fileversion >= 105 ) + { + //read in default script override information + i = cf_ReadByte(ifile); + if(i>0){ + objp->custom_default_script_name = (char *)mem_malloc(i+1); + cf_ReadBytes((ubyte *)objp->custom_default_script_name,i,ifile); + objp->custom_default_script_name[i] = '\0'; + }else{ + objp->custom_default_script_name = NULL; + } + + i = cf_ReadByte(ifile); + if(i>0){ + objp->custom_default_module_name = (char *)mem_malloc(i+1); + cf_ReadBytes((ubyte *)objp->custom_default_module_name,i,ifile); + objp->custom_default_module_name[i] = '\0'; + }else{ + objp->custom_default_module_name = NULL; + } + }else + { + objp->custom_default_module_name = NULL; + objp->custom_default_script_name = NULL; + } + +// load in obsolete script info + if (fileversion < LEVEL_FILE_OSIRIS1DEAD) { + if (fileversion < LEVEL_FILE_SCRIPTNAMES) { + cf_ReadInt(ifile); + } + else { + char name[MAX_D3XID_NAME]; + cf_ReadString(name, MAX_D3XID_NAME, ifile); + } + + if (fileversion >= LEVEL_FILE_SCRIPTPARMS) { + short s = cf_ReadShort(ifile); + + for (i = 0; i < s; i++) + { + sbyte stype = cf_ReadByte(ifile); + if (stype == PARMTYPE_NUMBER || stype == PARMTYPE_REF) { + cf_ReadFloat(ifile); + } + else if (stype == PARMTYPE_VECTOR) { + cf_ReadFloat(ifile); + cf_ReadFloat(ifile); + cf_ReadFloat(ifile); + } + else { + Int3(); // -get samir. + } + } + } + + + if (fileversion >= LEVEL_FILE_SCRIPTCHECK) { + cf_ReadByte(ifile); + } + } + +// lightmap data. + int lmdata; + if (fileversion<=34) + lmdata=0; + else + lmdata=cf_ReadByte (ifile); + + + if (lmdata) // lightmap data follows + { + poly_model *pm=&Poly_models[objp->rtype.pobj_info.model_num]; + int num_models,num_faces,num_verts; + int i,t,k; + int model_changed=0,clear_lightmaps=0; + + num_models=cf_ReadByte (ifile); + if (pm->n_models!=num_models) + { + model_changed=1; + mprintf ((0,"Polymodel %s has changed since this level was lit!\n",pm->name)); + } + + if (!model_changed) + SetupObjectLightmapMemory (objp); + + for (i=0;ilm_object.num_faces[i]) + { + submodel_changed=1; + clear_lightmaps=1; + } + + for (t=0;tlm_object.lightmap_faces[i][t]; + fp->lmi_handle=LightmapInfoRemap[(ushort)cf_ReadShort(ifile)]; + + if (!Dedicated_server) + LightmapInfo[fp->lmi_handle].used++; + else + clear_lightmaps=1; + + if (fileversion<=88) + { + cf_ReadByte (ifile); + cf_ReadByte (ifile); + cf_ReadByte (ifile); + cf_ReadByte (ifile); + } + + if (fileversion>=58) + { + // Read normal,uvec,rvec + if (fileversion<=59) + { + vector tvec; + cf_ReadVector (ifile,&tvec); + } + cf_ReadVector (ifile,&fp->rvec); + cf_ReadVector (ifile,&fp->uvec); + } + else + { + vm_MakeZero (&fp->rvec); + vm_MakeZero (&fp->uvec); + fp->rvec.x=1; + fp->uvec.y=1; + } + + num_verts=cf_ReadByte(ifile); + if (num_verts==fp->num_verts) + { + for (k=0;ku2[k]=cf_ReadFloat(ifile); + fp->v2[k]=cf_ReadFloat(ifile); + } + } + else + { + for (k=0;k=58) + { + vector tvec; + + // Read normal,uvec,rvec + if (fileversion<=59) + { + cf_ReadVector (ifile,&tvec); + } + cf_ReadVector (ifile,&tvec); + cf_ReadVector (ifile,&tvec); + } + + num_verts=cf_ReadByte(ifile); + for (k=0;kname)); + ClearObjectLightmaps (objp); + } + + } + return 1; +} + +//Old portal trigger flag +#define OLD_TF_PORTAL 1 + +//Reads a trigger +//Returns: true if read ok, else false +int ReadTrigger(CFILE *ifile,trigger *tp,int fileversion) +{ + if (fileversion >= 99) + cf_ReadString(tp->name, sizeof(tp->name), ifile); + else + tp->name[0] = 0; + + tp->roomnum = cf_ReadShort(ifile); + tp->facenum = cf_ReadShort(ifile); + tp->flags = cf_ReadShort(ifile); + tp->activator = cf_ReadShort(ifile); + + //Kill old portal trigger flag + if (fileversion < 103 ) { + if (tp->flags & OLD_TF_PORTAL) { + tp->facenum = Rooms[tp->roomnum].portals[tp->facenum].portal_face; + tp->flags &= ~OLD_TF_PORTAL; + } + } + + ASSERT(Rooms[tp->roomnum].faces[tp->facenum].flags & FF_HAS_TRIGGER); + + if (fileversion < LEVEL_FILE_OSIRIS1DEAD) { + if (fileversion < LEVEL_FILE_SCRIPTNAMES) + cf_ReadInt(ifile); + else { + char name[MAX_D3XID_NAME]; + cf_ReadString(name, MAX_D3XID_NAME, ifile); + if (fileversion >= LEVEL_FILE_TRIGPARMS) { + int i; + short s = cf_ReadShort(ifile); + for (i = 0; i < s; i++) + { + sbyte type = cf_ReadByte(ifile); + if (type == PARMTYPE_NUMBER || type == PARMTYPE_REF) { + cf_ReadFloat(ifile); + } + else if (type == PARMTYPE_VECTOR) { + cf_ReadFloat(ifile); + cf_ReadFloat(ifile); + cf_ReadFloat(ifile); + } + else { + Int3(); // -get samir. + } + } + } + } + + if (fileversion >= LEVEL_FILE_SCRIPTCHECK) { + cf_ReadByte(ifile); + } + } + + return 1; +} + +//Old portal trigger face flag +#define OLD_FF_PORTAL_TRIG 0x0020 + +//Reads a face from a disk file +//Parameters: ifile - file to read from +// fp - face to read +// version - the version number of the file being read +//Returns: 1 if read ok, else 0 +int ReadFace(CFILE *ifile,face *fp,int version) +{ + int nverts,i; + + //Get number of verts + nverts = cf_ReadByte(ifile); + + //Initialize & alloc memory + InitRoomFace(fp,nverts); + + //Read vertices + for (i=0;inum_verts;i++) + fp->face_verts[i] = cf_ReadShort(ifile); + + //Read uvls, and adjust alpha settings + int alphaed=0; + + for (i=0;inum_verts;i++) { + fp->face_uvls[i].u = cf_ReadFloat(ifile); + fp->face_uvls[i].v = cf_ReadFloat(ifile); + + if (version<56) + { + // Read old lrgb stuff + cf_ReadFloat(ifile); + cf_ReadFloat(ifile); + cf_ReadFloat(ifile); + cf_ReadFloat(ifile); + } + + if (version>=21) + { + if (version<61) + fp->face_uvls[i].alpha=Float_to_ubyte(cf_ReadFloat(ifile)); + else + fp->face_uvls[i].alpha=cf_ReadByte(ifile); + } + else + fp->face_uvls[i].alpha=1.0; + + if (fp->face_uvls[i].alpha!=255) + alphaed=1; + } + + //Read flags + if (version < 27) + fp->flags = cf_ReadByte(ifile); + else + fp->flags = cf_ReadShort(ifile); + + //Kill old portal trigger flag + if (version < 103) + fp->flags &= ~OLD_FF_PORTAL_TRIG; + + //Set vertex alpha flag + if (alphaed) + fp->flags|=FF_VERTEX_ALPHA; + else + fp->flags&=~FF_VERTEX_ALPHA; + + //Read the portal number + if (version >= 23) + fp->portal_num = cf_ReadByte(ifile); + else + fp->portal_num = cf_ReadShort(ifile); + + //Read and translate the texture number + fp->tmap = texture_xlate[cf_ReadShort(ifile)]; + + //Check for failed xlate + if (fp->tmap == -1) { + fp->tmap = 0; + } + + // Check to see if there is a lightmap + if ((fp->flags & FF_LIGHTMAP) && (version>=19)) + { + if (version<=29) + { + ubyte w,h; + + w=cf_ReadByte (ifile); + h=cf_ReadByte (ifile); + + for (i=0;iflags&=~FF_LIGHTMAP; + } + else + { + // Read lightmap info handle + int lmi_handle=(ushort)cf_ReadShort (ifile); + if (!Dedicated_server) + { + fp->lmi_handle=LightmapInfoRemap[lmi_handle]; + LightmapInfo[fp->lmi_handle].used++; + } + + if (version<=88) + { + cf_ReadByte (ifile); + cf_ReadByte (ifile); + cf_ReadByte (ifile); + cf_ReadByte (ifile); + } + } + + + // Read UV2s + for (i=0;inum_verts;i++) + { + fp->face_uvls[i].u2 = cf_ReadFloat(ifile); + fp->face_uvls[i].v2 = cf_ReadFloat(ifile); + + // Stupid fix for bad lightmap uvs + if (fp->face_uvls[i].u2<0) + fp->face_uvls[i].u2=0; + if (fp->face_uvls[i].u2>1) + fp->face_uvls[i].u2=1.0; + + if (fp->face_uvls[i].v2<0) + fp->face_uvls[i].v2=0; + if (fp->face_uvls[i].v2>1) + fp->face_uvls[i].v2=1.0; + + } + } + + if (version>=22 && version<=29) + { + vector vec; + cf_ReadVector(ifile,&vec); + } + + if (version>=40 && version<=60) // was shadow room,face + { + cf_ReadShort(ifile); + cf_ReadShort(ifile); + } + + if (version>=50) + { + fp->light_multiple=cf_ReadByte (ifile); + + if (fp->light_multiple==186) + fp->light_multiple=4; // Get Jason, I'm looking for this bug! Its safe to go past it, but I'm just on the lookout + + + if (version<=52) + { + if (fp->light_multiple>=32) + Int3(); // Get Jason + fp->light_multiple*=4; + } + } + else + fp->light_multiple=4; + + if (version>=71) + { + ubyte special=cf_ReadByte (ifile); + if (special) + { + if (version<77) // Ignore old specular data + { + vector center; + cf_ReadByte(ifile); + cf_ReadVector (ifile,¢er); + cf_ReadShort (ifile); + } + else + { + vector center; + + ubyte smooth=0; + ubyte num_smooth_verts=0; + ubyte type=cf_ReadByte(ifile); + ubyte num=cf_ReadByte(ifile); + + if (version>=117) + { + // Read if smoothed + smooth=cf_ReadByte (ifile); + if (smooth) + { + num_smooth_verts=cf_ReadByte (ifile); + fp->special_handle=AllocSpecialFace (type,num,true,num_smooth_verts); + } + else + fp->special_handle=AllocSpecialFace (type,num); + + } + else + fp->special_handle=AllocSpecialFace (type,num); + + ASSERT (fp->special_handle!=BAD_SPECIAL_FACE_INDEX); + + for (i=0;ispecial_handle].spec_instance[i].bright_center=center; + SpecialFaces[fp->special_handle].spec_instance[i].bright_color=color; + } + + if (smooth) + { + for (i=0;ispecial_handle].vertnorms[i]=vertnorm; + } + } + + } + } + } + + return 1; +} + +//Old has trigger portal flag +#define OLD_PF_HAS_TRIGGER 4 + +//Reads a portal to a disk file +//Parameters: ifile - file to Read to +// pp - portal to Read +//Returns: 1 if written ok, else 0 +int ReadPortal(CFILE *ifile,portal *pp, int version) +{ + int i,num_faces; + + pp->flags = cf_ReadInt(ifile); + + //Kill old trigger flag + if (version < 103) + pp->flags &= ~OLD_PF_HAS_TRIGGER; + + if (version< 80) { //read old list of portal verts + int num_verts = cf_ReadShort(ifile); + for (i=0;iportal_verts[i] + + num_faces = cf_ReadShort(ifile); + ASSERT(num_faces == 1); + } + + pp->portal_face = cf_ReadShort(ifile); + + pp->croom = cf_ReadInt(ifile); + pp->cportal = cf_ReadInt(ifile); + + if(version >= 123) + pp->bnode_index = cf_ReadShort(ifile); + else + pp->bnode_index = -1; + + if(version >= 63) + { + cf_ReadVector(ifile, &pp->path_pnt); + } + + if (version>=100) + pp->combine_master=cf_ReadInt(ifile); + + return 1; +} + +int n_degenerate_faces_removed; + +//Remove faces that have no area, as determined by their surface normal being NULL +//This code should come out after the clipper is fixed to not create degenerate faces +#ifdef EDITOR +void RemoveDegenerateFaces(room *rp) +{ + int f; + int n_removed = 0; + + for (f=rp->num_faces-1;f>=0;f--) { + face *fp = &rp->faces[f]; + + if ((fp->normal.x == 0.0) && (fp->normal.y == 0.0) && (fp->normal.z == 0.0)) { + mprintf((0,"Deleting face %d from room %d\n",f,ROOMNUM(rp))); + DeleteRoomFace(rp,f); + n_degenerate_faces_removed++; + } + } +} +#endif + +#define NO_COMPRESS 0 +#define COMPRESS 1 + +// Does a RLE compression run of the values given the byte array 'val'. +int WriteCompressionByte (CFILE *fp,ubyte *val,int total,int just_count,int compress) +{ + int done=0; + int written=0; + int curptr=0; + + ASSERT (!(just_count==1 && compress==0)); + + if (compress==NO_COMPRESS) + { + int i; + + // Write 0 to indicate no compression + cf_WriteByte (fp,NO_COMPRESS); + for (i=0;i=total) + WriteCompressionByte (fp,vals,total,0,NO_COMPRESS); + else + WriteCompressionByte (fp,vals,total,0,COMPRESS); +} + +// Given an array of values, checks to see if it would be better to write it out +// as a raw array or RLE array +void CheckToWriteCompressShort (CFILE *fp,ushort *vals,int total) +{ + int count=WriteCompressionShort (fp,vals,total,1,COMPRESS); + + if (count>=total) + WriteCompressionShort (fp,vals,total,0,NO_COMPRESS); + else + WriteCompressionShort (fp,vals,total,0,COMPRESS); +} + + + +void ReadCompressionByte (CFILE *fp,ubyte *vals,int total) +{ + int count=0; + ubyte compressed=cf_ReadByte (fp); + + if (compressed==0) + { + for (int i=0;i=2 && command<=250) // next pixel is run of pixels + { + ubyte height=cf_ReadByte (fp); + for (int k=0;k=2 && command<=250) // next pixel is run of pixels + { + ushort height=cf_ReadShort (fp); + for (int k=0;kused == 0); //should be free + + //Get counts for room elements + nverts = cf_ReadInt(ifile); + nfaces = cf_ReadInt(ifile); + nportals = cf_ReadInt(ifile); + + //Initialize the room + InitRoom(rp,nverts,nfaces,nportals); + + //Get room name + rp->name = NULL; + if (version >= 96) { + char tempname[ROOM_NAME_LEN+1]; + cf_ReadString(tempname,sizeof(tempname),ifile); + if (strlen(tempname)) { + rp->name = (char *) mem_malloc(strlen(tempname)+1); + strcpy(rp->name,tempname); + } + } + + //Read path point + if(version >= 63) + { + cf_ReadVector(ifile, &rp->path_pnt); + } + + //Read in verts + for (i=0;inum_verts;i++) + { + cf_ReadVector(ifile,&rp->verts[i]); + if (version>=52 && version<=67) + { + cf_ReadByte(ifile); + cf_ReadByte(ifile); + } + else if (version>=68 && version<71) + { + vector tempv; + cf_ReadVector (ifile,&tempv); + cf_ReadShort (ifile); + } + + // Update checksum + AppendToLevelChecksum(rp->verts[i]); + + } + + //Read in faces + for (i=0;inum_faces;i++) { + bool t; + + //Load the face + ReadFace(ifile,&rp->faces[i],version); + + for(int k=0;kfaces[i].num_verts;k++) + { + AppendToLevelChecksum(rp->faces[i].face_verts[k]); + } + AppendToLevelChecksum(rp->faces[i].tmap); + + //Get the surface normal for this face + // Note we're not computing the checksum on the normal because of + // differences between cpu architectures having different internal precision + // models for floating point. Instead we took the face verts into account above. + t = ComputeFaceNormal(rp,i); + + //Check for bad normal + if (! t) + { + mprintf((1,"WARNING: Room %d face %d has bad normal!\n",rp-Rooms,i)); + } + } + + //Read in portals + for (i=0;inum_portals;i++) + ReadPortal(ifile,&rp->portals[i], version); + + //Read rest of fields + rp->flags = cf_ReadInt(ifile); + if (version < 29) + cf_ReadFloat(ifile); //was rp->static_light + + if (version>=68) + { + rp->pulse_time=cf_ReadByte (ifile); + rp->pulse_offset=cf_ReadByte (ifile); + } + + if (version>=79) + { + rp->mirror_face=cf_ReadShort (ifile); + } + else + rp->mirror_face=-1; + + if ((rp->flags & RF_DOOR)) + { + int doornum,flags=0,keys=0,position=0.0; + + if (version>=28 && version<=32) + { + doornum = door_xlate[cf_ReadInt(ifile)]; + cf_ReadFloat (ifile); //was open_time + cf_ReadFloat (ifile); //was wait_time + cf_ReadFloat (ifile); //was close_time + } + else if (version>=33) + { + if (version < 106) + cf_ReadInt(ifile); //was doorway_num + + flags = cf_ReadByte(ifile); + if (version < 106) + flags |= DF_AUTO; + + if (version>=36) + keys = cf_ReadByte (ifile); + + doornum = door_xlate[cf_ReadInt(ifile)]; + + if (version >= 106) + position = cf_ReadFloat(ifile); + + if (version < 106) { + cf_ReadFloat (ifile); //was open_time + cf_ReadFloat (ifile); //was wait_time + cf_ReadFloat (ifile); //was close_time + } + } + + if (doornum == -1) + doornum = FindValidID(OBJ_DOOR); //find any valid id + + ASSERT(doornum != -1); + doorway *dp = DoorwayAdd(rp,doornum); + dp->position = position; + dp->flags = flags; + dp->keys_needed = keys; + dp->dest_pos = dp->position; + } + + if (version>=67) + { + // Read in volume lights + if (cf_ReadByte (ifile)==1) + { + int w=cf_ReadInt (ifile); + int h=cf_ReadInt (ifile); + int d=cf_ReadInt (ifile); + + int size = w*h*d; + + if (size) { + + rp->volume_lights = (ubyte *)mem_malloc(size); + ASSERT (rp->volume_lights); // ran out of memory! + } + else + rp->volume_lights = NULL; + + ReadCompressionByte (ifile,rp->volume_lights,w*h*d); + + rp->volume_width=w; + rp->volume_height=h; + rp->volume_depth=d; + + + } + } + + if (version>=73) + { + // Read fog stuff + rp->fog_depth=cf_ReadFloat (ifile); + rp->fog_r=cf_ReadFloat (ifile); + rp->fog_g=cf_ReadFloat (ifile); + rp->fog_b=cf_ReadFloat (ifile); + } + + //Read ambient sound pattern name + if (version >= 78) { + char tbuf[PAGENAME_LEN]; + cf_ReadString(tbuf,sizeof(tbuf),ifile); + rp->ambient_sound = FindAmbientSoundPattern(tbuf); + } + else + rp->ambient_sound = -1; + +// read reverb value for room. + rp->env_reverb = (version >= 98) ? ((ubyte)cf_ReadByte(ifile)) : 0; + + //Read damage + if (version >= 108) { + rp->damage = cf_ReadFloat(ifile); + rp->damage_type = cf_ReadByte(ifile); + } + +#if (defined(EDITOR) || defined(NEWEDITOR)) + //Get rid of bogus faces + //RemoveDegenerateFaces(rp); +#endif + + + if (Katmai) + { + // If katmai, copy all of our verts into our verts4 array + for (i=0;iverts4[i].x=rp->verts[i].x; + rp->verts4[i].y=rp->verts[i].y; + rp->verts4[i].z=rp->verts[i].z; + } + } + + return 1; +} + +//Build a translation table for various items +void BuildXlateTable(CFILE *ifile,int (*lookup_func)(char *),short *xlate_table,int max_items,int type) +{ + char name[PAGENAME_LEN]; + int n; + int i; + + n = cf_ReadInt(ifile); // get num items + for (i=0;i=5) + { + if((i%(nummaps/5))==0) + { + LoadLevelProgress(LOAD_PROGRESS_LOADING_LEVEL,(filelen) ? (float)(chunk_start+((((chunk_size)/nummaps)*i)/2))/(float)filelen : 0.0f,CHUNK_LIGHTMAPS); + } + } + w=cf_ReadShort(fp); + h=cf_ReadShort(fp); + + int lm_handle=lm_AllocLightmap (w,h); + + lightmap_remap[i]=lm_handle; + ushort *data=(ushort *)lm_data(lm_handle); + ReadCompressionShort (fp,data,w*h); + } + + nummaps=cf_ReadInt (fp); + + Num_lightmap_infos_read=nummaps; + ASSERT (nummaps=50) + { + if((i%(nummaps/50))==0) + { + LoadLevelProgress(LOAD_PROGRESS_LOADING_LEVEL,(filelen) ? (float)(chunk_start+(chunk_size/2)+(((chunk_size/2)/nummaps)*i))/(float)filelen : 0.0f,CHUNK_LIGHTMAPS); + } + } + int remap_handle=cf_ReadShort (fp); + w=cf_ReadShort(fp); + h=cf_ReadShort(fp); + type=cf_ReadByte (fp); + + if (!Dedicated_server) + { + lmi=AllocLightmapInfo (w,h,type,false); + ASSERT (lmi!=BAD_LMI_INDEX); + LightmapInfoRemap[i]=lmi; + LightmapInfo[lmi].lm_handle=lightmap_remap[remap_handle]; + } + + if (version>=91) + { + int x1=cf_ReadShort(fp); + int y1=cf_ReadShort(fp); + + if (!Dedicated_server) + { + LightmapInfo[lmi].x1=x1; + LightmapInfo[lmi].y1=y1; + } + } + + int xspacing; + int yspacing; + vector ul,norm; + + xspacing=cf_ReadByte (fp); + yspacing=cf_ReadByte (fp); + cf_ReadVector (fp,&ul); + cf_ReadVector (fp,&norm); + + if (!Dedicated_server) + { + LightmapInfo[lmi].xspacing=xspacing; + LightmapInfo[lmi].yspacing=yspacing; + + LightmapInfo[lmi].upper_left=ul; + LightmapInfo[lmi].normal=norm; + } + } + + mem_free (lightmap_remap); +} + +// Reads info about our lightmaps +void ReadLightmapChunk (CFILE *fp,int version) +{ + //Don't read lightmaps for dedicated server + if (Dedicated_server) { + Num_lightmap_infos_read=0; //I don't think this will be used, but clear it just in case + return; + } + + int nummaps; + int i,t; + ushort *ded_dummy_data; + + if (Dedicated_server) + { + ded_dummy_data=(ushort *)mem_malloc (128*128*2); + ASSERT (ded_dummy_data); + } + + + nummaps=cf_ReadInt (fp); + Num_lightmap_infos_read=nummaps; + ASSERT (nummaps=59) + { + cf_ReadVector (fp,&v); + if (!Dedicated_server) + LightmapInfo[lmi].normal=v; + } + else + { + LightmapInfo[lmi].normal.x=0; + LightmapInfo[lmi].normal.y=0; + LightmapInfo[lmi].normal.z=1; + } + + + ushort *data; + + if (Dedicated_server) + data=ded_dummy_data; + else + data=(ushort *)lm_data(LightmapInfo[lmi].lm_handle); + + if (version<=37) + { + for (t=0;t>11)<<3; + int g=((pixel & 0x07e0) >>5)<<2; + int b=(pixel & 0x001f)<<3; + + pixel=OPAQUE_FLAG|GR_RGB16(r,g,b); + } + + data[t]=pixel; + + } + } + } + + // Free dummy data for dedicated server + if (Dedicated_server) + mem_free (ded_dummy_data); +} + +// Reads information about the terrain mesh +void ReadTerrainHeightChunk (CFILE *fp,int version) +{ + int i; + + if (version<=31) + { + for (i=0;i= 51) + { + cf_ReadVector(fp, &GamePaths[i].pathnodes[j].fvec); + cf_ReadVector(fp, &GamePaths[i].pathnodes[j].uvec); + } + else + { + GamePaths[i].pathnodes[j].fvec.x = 0.0; + GamePaths[i].pathnodes[j].fvec.y = 0.0; + GamePaths[i].pathnodes[j].fvec.z = 1.0; + + GamePaths[i].pathnodes[j].uvec.x = 0.0; + GamePaths[i].pathnodes[j].uvec.y = 1.0; + GamePaths[i].pathnodes[j].uvec.z = 0.0; + } + } + } +} + +void ReadOverrideSoundChunk(CFILE *fp, int version) +{ + char soundname[PAGENAME_LEN]; + + cf_ReadString(soundname,sizeof(soundname),fp); + if(strcmp(soundname, "") != 0) + { + sound_override_force_field = FindSoundName(soundname); + } + + cf_ReadString(soundname,sizeof(soundname),fp); + if(strcmp(soundname, "") != 0) + { + sound_override_glass_breaking = FindSoundName(soundname); + } +} + +void ReadFFTMChunk(CFILE *fp, int version) +{ + char texturename[PAGENAME_LEN]; + int i; + + int num_items = MAX_FORCE_FIELD_BOUNCE_TEXTURES; + + if(version < 132) + { + num_items = 2; + } + + for(i = 0; i < num_items; i++) + { + cf_ReadString(texturename,sizeof(texturename),fp); + + if(strcmp(texturename, "") != 0) + { + force_field_bounce_texture[i] = FindTextureName(texturename); + force_field_bounce_multiplier[i] = cf_ReadFloat(fp); + GameTextures[force_field_bounce_texture[i]].flags |= TF_FORCEFIELD; + } + } +} + +void ReadBNodeChunk(CFILE *fp, int version) +{ + int i; + int j; + int k; + + int hr_index = cf_ReadShort(fp); + ASSERT(hr_index == Highest_room_index + 8); + + for(i = 0; i <= hr_index; i++) + { + char f_good_room = cf_ReadByte(fp); + if(f_good_room) + { + bn_list *bnlist = BNode_GetBNListPtr(i, true); + + bnlist->num_nodes = cf_ReadShort(fp); + ASSERT(!(i <= Highest_room_index && (Rooms[i].flags & RF_EXTERNAL) && bnlist->num_nodes > 0)); + + if(bnlist->num_nodes) + { + bnlist->nodes = (bn_node *) mem_malloc(sizeof(bn_node) * bnlist->num_nodes); + for(j = 0; j < bnlist->num_nodes; j++) + { + bnlist->nodes[j].pos.x = cf_ReadFloat(fp); + bnlist->nodes[j].pos.y = cf_ReadFloat(fp); + bnlist->nodes[j].pos.z = cf_ReadFloat(fp); + + bnlist->nodes[j].num_edges = cf_ReadShort(fp); + if(bnlist->nodes[j].num_edges) + { + bnlist->nodes[j].edges = (bn_edge *) mem_malloc(sizeof(bn_edge) * bnlist->nodes[j].num_edges); + for(k = 0; k < bnlist->nodes[j].num_edges; k++) + { + bnlist->nodes[j].edges[k].end_room = cf_ReadShort(fp); + bnlist->nodes[j].edges[k].end_index = cf_ReadByte(fp); + if(version < 125) + cf_ReadByte(fp); + + bnlist->nodes[j].edges[k].flags = cf_ReadShort(fp); + bnlist->nodes[j].edges[k].cost = cf_ReadShort(fp); + + // Chrisnote -- fixes earlier versions of the bnode system that would allow for zero length edges + if(bnlist->nodes[j].edges[k].cost < 1) + bnlist->nodes[j].edges[k].cost = 1; + + bnlist->nodes[j].edges[k].max_rad = cf_ReadFloat(fp); + } + } + else + { + bnlist->nodes[j].edges = NULL; + } + } + } + else + { + bnlist->nodes = NULL; + } + } + } + + if(version <= 123) + BNode_verified = false; + else + BNode_verified = cf_ReadByte(fp) != 0; + + BNode_allocated = true; +} + +void ReadBOAChunk(CFILE *fp, int version) +{ + // Currently, we will have no version specific info + + int i, j; + int max_rooms; + int max_path_portals; + + // Get the number of paths + BOA_AABB_checksum = BOA_mine_checksum = cf_ReadInt(fp); + + if (version>=76) + BOA_vis_checksum = cf_ReadInt(fp); + else + BOA_vis_checksum=0; + + max_rooms = cf_ReadInt(fp); + + if(version < 62) + { + cfseek( fp, sizeof(short)*max_rooms*max_rooms, SEEK_CUR); + + mprintf((0, "We will need to remake boa. New cost structure added\n")); + BOA_AABB_checksum = BOA_mine_checksum = 0; + } + else + { + max_path_portals = cf_ReadInt(fp); + + ASSERT(max_rooms - 1 <= MAX_ROOMS + 8); + + if(version < 110 || (max_path_portals != MAX_PATH_PORTALS)) + { + cfseek( fp, sizeof(short)*max_rooms*max_rooms + max_rooms*max_path_portals*sizeof(float), SEEK_CUR); + + if (version>=107) + { + // Read BOA terrain info (temporary, just so vis data works with multiplay) + cfseek( fp, max_rooms*sizeof(float), SEEK_CUR); + } + + mprintf((0, "We will need to remake boa. Data size changed\n")); + BOA_AABB_checksum = BOA_mine_checksum = 0; + } + else + { + for(i = 0; i <= max_rooms; i++) + { + for(j = 0; j <= max_rooms; j++) + { + BOA_Array[i][j] = cf_ReadShort(fp); + } + } + + for(i = 0; i <= max_rooms; i++) + { + for(j = 0; j < max_path_portals; j++) + { + BOA_cost_array[i][j] = cf_ReadFloat(fp); + } + } + + BOA_num_mines = cf_ReadInt(fp); + BOA_num_terrain_regions = cf_ReadInt(fp); + + if(version < 112) + { + mprintf((0, "We will need to remake boa.\n")); + BOA_AABB_checksum = BOA_mine_checksum = 0; + } + else + { + int i, j; + + for(i = 0; i < BOA_num_terrain_regions; i++) + { + BOA_num_connect[i] = cf_ReadInt(fp); + + for(j = 0; j < BOA_num_connect[i]; j++) + { + BOA_connect[i][j].roomnum = cf_ReadInt(fp); + BOA_connect[i][j].portal = cf_ReadInt(fp); + } + } + } + } + } +} + +void ReadRoomAABBChunk(CFILE *fp, int version) +{ + int save_highest_room_index; + int i; + + save_highest_room_index = cf_ReadInt(fp); + ASSERT(save_highest_room_index < MAX_ROOMS); // Someone decreased the max amount of rooms + ASSERT(save_highest_room_index == Highest_room_index); // Someone decreased the max amount of rooms + + for(i = 0; i <= save_highest_room_index; i++) + { + BOA_AABB_ROOM_checksum[i] = cf_ReadInt(fp); + } + + for(i = 0; i <= Highest_room_index; i++) + { + int used = cf_ReadInt(fp); + ASSERT(Rooms[i].used == used); + + if(used) + { + int j; + int k; + + int n_faces = cf_ReadInt(fp); + ASSERT(Rooms[i].num_faces == n_faces); + + for(j = 0; j < Rooms[i].num_faces; j++) + { + Rooms[i].faces[j].min_xyz.x = cf_ReadFloat(fp); + Rooms[i].faces[j].min_xyz.y = cf_ReadFloat(fp); + Rooms[i].faces[j].min_xyz.z = cf_ReadFloat(fp); + + Rooms[i].faces[j].max_xyz.x = cf_ReadFloat(fp); + Rooms[i].faces[j].max_xyz.y = cf_ReadFloat(fp); + Rooms[i].faces[j].max_xyz.z = cf_ReadFloat(fp); + } + + BOA_AABB_ROOM_checksum[i] = cf_ReadInt(fp); + + Rooms[i].bbf_min_xyz.x = cf_ReadFloat(fp); + Rooms[i].bbf_min_xyz.y = cf_ReadFloat(fp); + Rooms[i].bbf_min_xyz.z = cf_ReadFloat(fp); + + Rooms[i].bbf_max_xyz.x = cf_ReadFloat(fp); + Rooms[i].bbf_max_xyz.y = cf_ReadFloat(fp); + Rooms[i].bbf_max_xyz.z = cf_ReadFloat(fp); + + Rooms[i].num_bbf_regions = cf_ReadShort(fp); + Rooms[i].num_bbf = (short *) mem_malloc(sizeof(short) * Rooms[i].num_bbf_regions); + Rooms[i].bbf_list = (short **) mem_malloc(sizeof(short *) * Rooms[i].num_bbf_regions); + Rooms[i].bbf_list_min_xyz = (vector *) mem_malloc(sizeof(vector) * Rooms[i].num_bbf_regions); + Rooms[i].bbf_list_max_xyz = (vector *) mem_malloc(sizeof(vector) * Rooms[i].num_bbf_regions); + Rooms[i].bbf_list_sector = (unsigned char *) mem_malloc(sizeof(char) * Rooms[i].num_bbf_regions); + + for(j = 0; j < Rooms[i].num_bbf_regions; j++) + { + Rooms[i].num_bbf[j] = cf_ReadShort(fp); + Rooms[i].bbf_list[j] = (short *) mem_malloc(sizeof(short) * Rooms[i].num_bbf[j]); + } + + for(j = 0; j < Rooms[i].num_bbf_regions; j++) + { + for(k = 0; k < Rooms[i].num_bbf[j]; k++) + { + Rooms[i].bbf_list[j][k] = cf_ReadShort(fp); + } + + Rooms[i].bbf_list_min_xyz[j].x = cf_ReadFloat(fp); + Rooms[i].bbf_list_min_xyz[j].y = cf_ReadFloat(fp); + Rooms[i].bbf_list_min_xyz[j].z = cf_ReadFloat(fp); + + Rooms[i].bbf_list_max_xyz[j].x = cf_ReadFloat(fp); + Rooms[i].bbf_list_max_xyz[j].y = cf_ReadFloat(fp); + Rooms[i].bbf_list_max_xyz[j].z = cf_ReadFloat(fp); + + Rooms[i].bbf_list_sector[j] = cf_ReadByte(fp); + } + } + else + { + BOA_AABB_ROOM_checksum[i] = 0; // Not used + } + } +} + +void ReadMatcenChunk(CFILE *fp, int version) +{ + int i; + + Num_matcens = cf_ReadInt(fp); + for(i = 0; i < Num_matcens; i++) + { + Matcen[i] = new matcen; + Matcen[i]->LoadData(fp); + } +} + +void ReadALifeChunk(CFILE *fp, int version) +{ +#ifndef NEWEDITOR + a_life.LoadData(fp); +#endif +} + +void ReadLevelGoalsChunk(CFILE *fp, int version) +{ + Level_goals.LoadLevelGoalInfo(fp); +} + +// Reads relevant info about the terrain sky and lights +void ReadTerrainSkyAndLightChunk (CFILE *fp,int version) +{ + int i,num_sats; + + if (version>=41) + { + if (version>=88) + Terrain_sky.fog_scalar=cf_ReadFloat(fp); + + if (version>=87) + Terrain_sky.damage_per_second=cf_ReadFloat(fp); + + Terrain_sky.textured=cf_ReadByte(fp); + + if (version<43) + { + for (i=0;i0) + { + Terrain_sky.flags &= ~TF_FOG; + } + #endif + + if (version>=69) + { + Terrain_sky.radius=cf_ReadFloat (fp); + if (version<74) + Terrain_sky.radius=2500.0; + + SetupSky (Terrain_sky.radius,Terrain_sky.flags,1); + } + + if (version>=104) + Terrain_sky.rotate_rate=cf_ReadFloat (fp); + else + Terrain_sky.rotate_rate=0; + + + num_sats=cf_ReadInt (fp); + + ASSERT (num_sats<=MAX_SATELLITES); + + Terrain_sky.num_satellites=num_sats; + + for (i=0;i=44) + Terrain_sky.satellite_flags[i]=cf_ReadByte (fp); + else + Terrain_sky.satellite_flags[i]=0; + + if (version>=116) + Terrain_sky.satellite_size[i]=cf_ReadFloat (fp); + else + { + vector subvec=Terrain_sky.satellite_vectors[i]; + vm_NormalizeVector (&subvec); + + Terrain_sky.satellite_vectors[i]=subvec*(Terrain_sky.radius*3); + + Terrain_sky.satellite_size[i]=500; + } + + if (version>=75) + { + Terrain_sky.satellite_r[i]=cf_ReadFloat (fp); + Terrain_sky.satellite_g[i]=cf_ReadFloat (fp); + Terrain_sky.satellite_b[i]=cf_ReadFloat (fp); + } + } + + // Now read misc stuff + if (version<=56) + { + cf_ReadFloat (fp); + cf_ReadFloat (fp); + } + + // Read lighting + if (version<=31) + { + for (i=0;i=24) + { + Terrain_seg[i].l=cf_ReadByte (fp); + Terrain_seg[i].r=cf_ReadByte (fp); + Terrain_seg[i].g=cf_ReadByte (fp); + Terrain_seg[i].b=cf_ReadByte (fp); + } + else + { + Terrain_seg[i].l=cf_ReadByte (fp); + Terrain_seg[i].r=Terrain_seg[i].l; + Terrain_seg[i].g=Terrain_seg[i].l; + Terrain_seg[i].b=Terrain_seg[i].l; + } + } + if (version>=25) + { + for (i=0;i=72) + { + Terrain_occlusion_checksum=cf_ReadInt (fp); + ReadCompressionByte (fp,byte_vals,OCCLUSION_SIZE*OCCLUSION_SIZE*32); + for (i=0;i=16) + { + for (i=0;i>4)==0) + Terrain_tex_seg[i].rotation|=(1<<4); + } + } + + // Read flags + ReadCompressionByte (fp,byte_vals,TERRAIN_DEPTH*TERRAIN_WIDTH); + for (i=0;i= 120) ? cf_ReadShort(infile) : 32; + + for (i=0;iflags & RF_EXTERNAL) + continue; + if (rp->used==0) + continue; + for (int objnum=rp->objects;objnum!=-1;objnum=Objects[objnum].next) + { + ASSERT (already_listed[objnum]==0); + already_listed[objnum]=1; + } + } + + for (i=0;iobjects;objnum!=-1;objnum=Objects[objnum].next) + { + ASSERT (already_listed[objnum]==0); + already_listed[objnum]=1; + } + } + +} + +//Data to deal with a bunch of renamed doors +char *Old_door_names[] = { + "markroomdoor.OOF1", + "cellblockdoor.OOF1", + "towerringdoor.OOF1", + "hangdoorinverse.oof1", + "Big Lock Door", + "Oval Door", + "Layered Door", + "steamdoor1", + "heatsinkdoornew1", + "Vault Door Large", + "Ceddoor1", + "trapdoor1", + "Lukesecretdoor1", + "ptmc11", + "ptmc31", + "ptmc021", + "Bulkhead1", + "Placeholderdoor1", + "ptmcbd1", + "energysecretdoor1", + "PTMC Industrial 1", + "PTMC Covert 1" +}; + +char *New_door_names[] = { + "MARK'S OLD DOOR", + "SEAN'S NOVAK DOOR 1", + "SEAN'S NOVAK DOOR 2", + "SEAN'S NOVAK DOOR 3", + "PTMC Industrial 2", + "SEAN'S DUCTWORK DOOR", + "PTMC Industrial 4", + "SEAN'S STEAMVENT DOOR", + "SEAN'S HEATSINK DOOR", + "DAN'S VAULT DOOR", + "CED 1", + "SEAN'S TRAPDOOR", + "LUKE'S SECRET DOOR", + "PTMC Industrial 5", + "PTMC Industrial 6", + "PTMC Industrial 7", + "!!!CED BROKEN!!!", + "PLACEHOLDER DOOR", + "PTMC Industrial 3", + "SEAN'S ENERGY SECRET DOOR", + "PTMC Industrial 1", + "PTMC Covert 1" +}; + +#define NUM_RENAMED_DOORS (sizeof(Old_door_names) / sizeof(*Old_door_names)) + +//Deals with some renamed doors. Translates the old name to the new name, then looks up the id +int SpecialFindDoorName(char *name) +{ + int i,id; + + //Look up the old name, and return it if found + id = FindDoorName(name); + if (id != -1) + return id; + + //Didn't find old name, so see if there's a new name, and look for that + for (i=0;i LEVEL_FILE_VERSION) { + cfclose(ifile); + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (GetFunctionMode() == EDITOR_MODE) + EditorMessageBox("Can't load level file \"%s\": Its version (%d) is newer than current version (%d).",filename,version,LEVEL_FILE_VERSION); + #endif + retval = 0; + goto end_loadlevel; + } + + //Check for too-old version + if (version < LEVEL_FILE_OLDEST_COMPATIBLE_VERSION) { + cfclose(ifile); + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (GetFunctionMode() == EDITOR_MODE) + EditorMessageBox("Can't load level file \"%s\": Its version (%d) is older than the oldest compatible version (%d).",filename,version,LEVEL_FILE_OLDEST_COMPATIBLE_VERSION); + #endif + retval = 0; + goto end_loadlevel; + } + + //Get rid of old mine + FreeAllRooms(); + + // Get rid of old matcens + DestroyAllMatcens(); + +#ifndef NEWEDITOR + // Resets the ambient life controller + a_life.ALReset(); +#endif + + // Get rid of old level goals + Level_goals.CleanupAfterLevel(); + + // Get rid of old objects + FreeAllObjects(); + +#ifndef NEWEDITOR + // Clear glows + ResetLightGlows(); +#endif + + //Reset all the objects + ResetObjectList(); + + //Free all triggers + FreeTriggers(); + + // Resets the game paths to blank + InitGamePaths(); + + //Clear terrain sounds + ClearTerrainSound(); + + //Init level info + strcpy(Level_info.name,"Unnamed"); + strcpy(Level_info.designer,"Anonymous"); + strcpy(Level_info.copyright,""); + strcpy(Level_info.notes,""); + + BOA_AABB_checksum = BOA_mine_checksum = 0; + for(i = 0; i < MAX_ROOMS; i++) + { + BOA_AABB_ROOM_checksum[i] = 0; + } + + BNode_ClearBNodeInfo(); + FVI_always_check_ceiling = false; + + sound_override_force_field = -1; + sound_override_glass_breaking = -1; + + for(i = 0; i < MAX_FORCE_FIELD_BOUNCE_TEXTURES; i++) + { + force_field_bounce_texture[i] = -1; + force_field_bounce_multiplier[i] = 1.0f; + } + + Level_powerups_ignore_wind = false; + + LoadLevelProgress(LOAD_PROGRESS_LOADING_LEVEL,0.01f); + + //Read and parse chunks + while (! cfeof(ifile)) { + char chunk_name[4]; + + + cf_ReadBytes((ubyte *) chunk_name,4,ifile); + chunk_start = cftell(ifile); + chunk_size = cf_ReadInt(ifile); + mprintf((0,"Chunk: %c%c%c%c, size=%d\n",chunk_name[0],chunk_name[1],chunk_name[2],chunk_name[3],chunk_size)); + + if (ISCHUNK(CHUNK_TEXTURE_NAMES)) + ReadTextureList(ifile); + else if (ISCHUNK(CHUNK_GENERIC_NAMES)) { + BuildXlateTable(ifile,FindObjectIDName,generic_xlate,MAX_OBJECT_IDS,XT_GENERIC); + } + else if (ISCHUNK(CHUNK_DOOR_NAMES)) { + if (version < 82) + BuildXlateTable(ifile,SpecialFindDoorName,door_xlate,MAX_DOORS,XT_DOOR); + else + BuildXlateTable(ifile,FindDoorName,door_xlate,MAX_DOORS,XT_DOOR); + } + else if (ISCHUNK(CHUNK_ROOMS)) { + int num_rooms; + + num_rooms = cf_ReadInt(ifile); + + extern void RoomMemInit(int nverts,int nfaces,int nfaceverts,int nportals); //MOVE TO HEADER FILE + if (version >= 85) { + int nverts,nfaces,nfaceverts,nportals; + nverts = cf_ReadInt(ifile); + nfaces = cf_ReadInt(ifile); + nfaceverts = cf_ReadInt(ifile); + nportals = cf_ReadInt(ifile); + RoomMemInit(nverts,nfaces,nfaceverts,nportals); + } + else + RoomMemInit(0,0,0,0); //0 means we don't know how much memory we need + + n_degenerate_faces_removed = 0; + int roomnum; + for (i=0;i= 96) ? cf_ReadShort(ifile) : i; + ReadRoom(ifile,&Rooms[roomnum],version); + } + mprintf((1,"%d degenerate faces removed\n",n_degenerate_faces_removed)); + + Highest_room_index = roomnum; + ASSERT(Highest_room_index < MAX_ROOMS); + + } + else if (ISCHUNK(CHUNK_ROOM_WIND)) { + int num_rooms = cf_ReadInt(ifile); + + for (i=0;i= 45) + { + handle = cf_ReadInt(ifile); + if (version < 94) + handle = XLATE_HANDLE(handle); + objnum = handle & HANDLE_OBJNUM_MASK; + } + else { + objnum = i; + handle = i + HANDLE_COUNT_INCREMENT; + } + + ReadObject(ifile,&Objects[objnum],handle,version); + + //Link the object into the mine + roomnum = Objects[objnum].roomnum; + + Objects[objnum].roomnum = -1; //ObjLink() expects the roomnum to be -1 + + if ((roomnum > Highest_room_index) && !ROOMNUM_OUTSIDE(roomnum)) { //bad roomnum + Int3(); //loading object with invalid room number + Objects[objnum].type = OBJ_NONE; //kill the object + } + else { + if (!ROOMNUM_OUTSIDE(roomnum) && Rooms[roomnum].flags & RF_EXTERNAL) { + mprintf((0, "Internal object %d linked to external room %d (type = %d)!!!\n", objnum, roomnum, Objects[objnum].type)); + if (Objects[objnum].type == OBJ_VIEWER) + Objects[objnum].type = OBJ_NONE; //kill the object + else + { + Int3(); + int k; + int done=0; + for (k=0;k<=Highest_room_index && !done;k++) + { + + if (Rooms[k].used && !(Rooms[k].flags & RF_EXTERNAL)) + { + done=1; + ObjLink (objnum,k); + } + } + + } + } + else + ObjLink(objnum,roomnum); + } + +#ifndef NEWEDITOR + ObjInitPositionHistory(&Objects[objnum]); +#endif + } + + //Rebuild the free object list + ResetFreeObjects(); + } + else if (ISCHUNK(CHUNK_TRIGGERS)) { + Num_triggers = cf_ReadInt(ifile); + for (i=0;i= 83) + { + Gravity_strength = cf_ReadFloat(ifile); + } + else + { + Gravity_strength = -32.2f; + } + + if(version >= 131) + { + Level_powerups_ignore_wind = (cf_ReadInt(ifile) != 0); + } + else + { + Level_powerups_ignore_wind = false; + } + + if(version >= 122) + { + FVI_always_check_ceiling = (cf_ReadInt(ifile) != 0); + } + else + { + FVI_always_check_ceiling = false; + } + + if (version >= 127) { + Ceiling_height = cf_ReadFloat(ifile); + AppendToLevelChecksum(Ceiling_height); + } + else + Ceiling_height = MAX_TERRAIN_HEIGHT; + } + #if (defined(EDITOR) || defined(NEWEDITOR)) + else if (ISCHUNK(CHUNK_EDITOR_INFO)) { + int t; + + //Read misc editor vars + t = cf_ReadShort(ifile); + Curroomp = &Rooms[t]; + Curface = cf_ReadShort(ifile); + if (version >= 81) { + Curedge = cf_ReadShort(ifile); + Curvert = cf_ReadShort(ifile); + } + t = cf_ReadShort(ifile); + Markedroomp = (t==-1)?NULL:&Rooms[t]; + Markedface = cf_ReadShort(ifile); + if (version >= 81) { + Markededge = cf_ReadShort(ifile); + Markedvert = cf_ReadShort(ifile); + } + + //Read selected list + N_selected_rooms = cf_ReadInt(ifile); + for (i=0;i= 14) { + Cur_object_index = cf_ReadInt(ifile); + Current_trigger = cf_ReadInt(ifile); + if (version < 106) + cf_ReadInt(ifile); //was Current_doorway + Editor_view_mode = cf_ReadInt(ifile); + Editor_viewer_id = cf_ReadInt(ifile); + if (version < 47) + cf_ReadInt(ifile); //was Editor_viewer_id[VM_TERRAIN] + } + + if (version >= 55) { + cf_ReadVector(ifile,&Wireframe_view_mine.target); + cf_ReadMatrix(ifile,&Wireframe_view_mine.orient); + Wireframe_view_mine.dist = cf_ReadFloat(ifile); + } + + if (version>=113) + { + for (i=0;i=118) + { + Room_ambience_r[i]=cf_ReadFloat (ifile); + Room_ambience_g[i]=cf_ReadFloat (ifile); + Room_ambience_b[i]=cf_ReadFloat (ifile); + } + } + } + if (version>=126) + LightSpacing=cf_ReadInt (ifile); + + if (version>=128) + { + GlobalMultiplier=cf_ReadFloat(ifile); + Ambient_red=cf_ReadFloat (ifile); + Ambient_green=cf_ReadFloat (ifile); + Ambient_blue=cf_ReadFloat (ifile); + rad_MaxStep=cf_ReadInt (ifile); + } + + } + #endif //ifdef EDITOR + else { //unknown chunk + mprintf((0," Unknown chunk: %c%c%c%c, size=%d\n",chunk_name[0],chunk_name[1],chunk_name[2],chunk_name[3],chunk_size)); + } + + //Go to end of chunk + cfseek(ifile,chunk_start+chunk_size,SEEK_SET); + + LoadLevelProgress(LOAD_PROGRESS_LOADING_LEVEL,LEVEL_LOADED_PCT_CALC,chunk_name); + } + + } //try + catch(cfile_error *cfe) { + mprintf((0,"Error reading: file = <%s>, error = \"%s\"\n",cfe->file->name,cfe->msg)); + ASSERT(cfe->read_write == CFE_READING); + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (GetFunctionMode() == EDITOR_MODE) + EditorMessageBox("Error reading file \"%s\": %s",cfe->file->name,cfe->msg); + #endif + cfclose(ifile); + return 0; + } + + //Close the file + cfclose(ifile); + + //Look for player objects & set player starts + FindPlayerStarts(); + if (!Player_object && !(Game_mode & GM_MULTI)) + Player_object = &Objects[Players[0].objnum]; + +// aabbs + LoadLevelProgress(LOAD_PROGRESS_LOADING_LEVEL,1.0f,NULL); + + //Compute the bounding boxes + if(!f_read_AABB) + ComputeAABB(true); + else + ComputeAABB(false); + + //Create the room objects + CreateRoomObjects(); + +#ifndef NEWEDITOR /* we call MakeBoa AFTER textures are marked in use */ + MakeBOA(); +#endif + + // Decrement lightmap counters - this must be done because multiple faces can + // share 1 lightmap + + if (version>=34 && !Dedicated_server) + { + ubyte *lightmap_spoken_for=(ubyte *)mem_malloc (MAX_LIGHTMAPS); + ubyte *free_lightmap_info=(ubyte *)mem_malloc(MAX_LIGHTMAP_INFOS); + ASSERT (lightmap_spoken_for); + memset (lightmap_spoken_for,0,MAX_LIGHTMAPS); + + ASSERT (free_lightmap_info); + memset (free_lightmap_info,0,MAX_LIGHTMAP_INFOS); + + for (i=0;i1) + { + LightmapInfo[index].used--; + + // Increase to account for shared + GameLightmaps[LightmapInfo[index].lm_handle].used++; + lightmap_spoken_for[LightmapInfo[index].lm_handle]=1; + + if (LightmapInfo[index].width==128 || LightmapInfo[index].height==128) + no_128s=false; + } + } + + // Decrease to account for basic allocation + for (i=0;ix); cf_WriteFloat((f),(v)->y); cf_WriteFloat((f),(v)->z); } while (0) +#define cf_WriteMatrix(f,m) do {cf_WriteVector((f),&(m)->rvec); cf_WriteVector((f),&(m)->uvec); cf_WriteVector((f),&(m)->fvec); } while (0) + +//writes an object +//returns 1 if written ok +int WriteObject(CFILE *ofile,object *objp) +{ + int i,t,k; + + cf_WriteByte(ofile,objp->type); + cf_WriteShort(ofile,objp->id); + + cf_WriteString(ofile,objp->name?objp->name:""); + + cf_WriteInt(ofile,objp->flags); + + if (objp->type == OBJ_DOOR) + cf_WriteShort(ofile,objp->shields); + + cf_WriteInt(ofile,objp->roomnum); + cf_WriteVector(ofile,&objp->pos); + cf_WriteMatrix(ofile,&objp->orient); + + cf_WriteByte(ofile,objp->contains_type); + cf_WriteByte(ofile,objp->contains_id); + cf_WriteByte(ofile,objp->contains_count); + + cf_WriteFloat(ofile,objp->lifeleft); + + ASSERT(objp->parent_handle == OBJECT_HANDLE_NONE); //not writing, so make sure none + + //Write sound info if this is a soundsource object + if (objp->control_type == CT_SOUNDSOURCE) { + ASSERT(objp->type == OBJ_SOUNDSOURCE); + cf_WriteString(ofile,(objp->ctype.soundsource_info.sound_index == -1) ? "" : Sounds[objp->ctype.soundsource_info.sound_index].name); + cf_WriteFloat(ofile,objp->ctype.soundsource_info.volume); + } + + i = (objp->custom_default_script_name)?strlen(objp->custom_default_script_name):0; + cf_WriteByte(ofile,i); + if(i>0) + cf_WriteBytes((ubyte *)objp->custom_default_script_name,i,ofile); + + i = (objp->custom_default_module_name)?strlen(objp->custom_default_module_name):0; + cf_WriteByte(ofile,i); + if(i>0) + cf_WriteBytes((ubyte *)objp->custom_default_module_name,i,ofile); + + +// If there is lightmap data for this object, write it out. + if (objp->lighting_render_type==LRT_LIGHTMAPS) + { + if (objp->lm_object.used==0) + { + mprintf ((0,"Warning: Object %d is set for lightmaps but has no lightmap data!\n",objp-Objects)); + cf_WriteByte (ofile,0); + } + else + { + cf_WriteByte (ofile,1); + cf_WriteByte (ofile,objp->lm_object.num_models); + for (i=0;ilm_object.num_models;i++) + { + cf_WriteShort (ofile,objp->lm_object.num_faces[i]); + for (t=0;tlm_object.num_faces[i];t++) + { + lightmap_object_face *fp=&objp->lm_object.lightmap_faces[i][t]; + ASSERT (LightmapInfoRemap[fp->lmi_handle]!=BAD_LMI_INDEX); + cf_WriteShort (ofile,(ushort)LightmapInfoRemap[fp->lmi_handle]); + + cf_WriteVector (ofile,&fp->rvec); + cf_WriteVector (ofile,&fp->uvec); + + cf_WriteByte (ofile,fp->num_verts); + + for (k=0;knum_verts;k++) + { + cf_WriteFloat(ofile,fp->u2[k]); + cf_WriteFloat(ofile,fp->v2[k]); + } + } + } + } + } + else + cf_WriteByte (ofile,0); + + return 1; +} + +//Writes a trigger +//Returns: true if written ok, else false +int WriteTrigger(CFILE *ofile,trigger *tp) +{ + cf_WriteString(ofile,tp->name); + cf_WriteShort(ofile,tp->roomnum); + cf_WriteShort(ofile,tp->facenum); + cf_WriteShort(ofile,tp->flags); + cf_WriteShort(ofile,tp->activator); + + return 1; +} + +//Writes a face to a disk file +//Parameters: ofile - file to write to +// fp - face to write +//Returns: 1 if written ok, else 0 +int WriteFace(CFILE *ofile,face *fp) +{ + int i; + + //Write number of verts + cf_WriteByte(ofile,fp->num_verts); + + //Verts + for (i=0;inum_verts;i++) + cf_WriteShort(ofile,fp->face_verts[i]); + + //uvls + fp->flags&=~FF_VERTEX_ALPHA; + + for (i=0;inum_verts;i++) { + cf_WriteFloat(ofile,fp->face_uvls[i].u); + cf_WriteFloat(ofile,fp->face_uvls[i].v); + cf_WriteByte(ofile,fp->face_uvls[i].alpha); + + if (fp->face_uvls[i].alpha!=255) + fp->flags|=FF_VERTEX_ALPHA; + } + + //Other stuff + if ((fp->flags & FF_LIGHTMAP) && (fp->lmi_handle==BAD_LMI_INDEX || LightmapInfoRemap[fp->lmi_handle]==BAD_LMI_INDEX)) + { + fp->flags&=~FF_LIGHTMAP; + mprintf ((0,"Almost saved a bogus lightmap!\n")); + } + + cf_WriteShort(ofile,fp->flags); + cf_WriteByte(ofile,fp->portal_num); + cf_WriteShort(ofile,fp->tmap); + + if (fp->flags & FF_LIGHTMAP) + { + // Write UV2's + + ASSERT (LightmapInfoRemap[fp->lmi_handle]!=BAD_LMI_INDEX); + cf_WriteShort (ofile,(ushort)LightmapInfoRemap[fp->lmi_handle]); + + for (i=0;inum_verts;i++) + { + cf_WriteFloat(ofile,fp->face_uvls[i].u2); + cf_WriteFloat(ofile,fp->face_uvls[i].v2); + } + } + + if (fp->light_multiple==186) + { + fp->light_multiple=4; // Get Jason, I'm looking for this bug! Its safe to go past it, but I'm just on the lookout + mprintf ((0,"Bogus light multiple detected...bashing!\n")); + } + + cf_WriteByte (ofile,fp->light_multiple); + + + if (fp->special_handle!=BAD_SPECIAL_FACE_INDEX) + { + // Write out special face stuff + ubyte smooth=0; + cf_WriteByte (ofile,1); + cf_WriteByte (ofile,SpecialFaces[fp->special_handle].type); + cf_WriteByte (ofile,4); + + if ((GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) && (SpecialFaces[fp->special_handle].flags & SFF_SPEC_SMOOTH)) + { + smooth=1; + cf_WriteByte (ofile,1); + cf_WriteByte (ofile,fp->num_verts); + } + else + { + cf_WriteByte (ofile,0); + } + + + for (i=0;i<4;i++) + { + cf_WriteVector (ofile,&SpecialFaces[fp->special_handle].spec_instance[i].bright_center); + cf_WriteShort (ofile,SpecialFaces[fp->special_handle].spec_instance[i].bright_color); + } + if (smooth) + { + for (i=0;inum_verts;i++) + { + cf_WriteVector (ofile,&SpecialFaces[fp->special_handle].vertnorms[i]); + } + } + } + else + cf_WriteByte (ofile,0); + + + + return 1; +} + +//Writes a portal to a disk file +//Parameters: ofile - file to write to +// pp - portal to write +//Returns: 1 if written ok, else 0 +int WritePortal(CFILE *ofile,portal *pp) +{ + cf_WriteInt(ofile,pp->flags); + + cf_WriteShort(ofile,pp->portal_face); + + cf_WriteInt(ofile,pp->croom); + cf_WriteInt(ofile,pp->cportal); + cf_WriteShort(ofile,pp->bnode_index); + + cf_WriteVector(ofile, &pp->path_pnt); + + cf_WriteInt (ofile,pp->combine_master); + + return 1; +} + +//Writes a room to a disk file +//Parameters: ofile - file to write to +// rp - room to write +//Returns: 1 if written ok, else 0 +int WriteRoom(CFILE *ofile,room *rp) +{ + int i; + + //Write counts + cf_WriteInt(ofile,rp->num_verts); + cf_WriteInt(ofile,rp->num_faces); + cf_WriteInt(ofile,rp->num_portals); + + //Write name + cf_WriteString(ofile,rp->name?rp->name:""); + + // Write out the path point + cf_WriteVector(ofile, &rp->path_pnt); + + //Write verts + for (i=0;inum_verts;i++) + { + cf_WriteVector(ofile,&rp->verts[i]); + } + + //Write faces + for (i=0;inum_faces;i++) + WriteFace(ofile,&rp->faces[i]); + + //Write portals + for (i=0;inum_portals;i++) + WritePortal(ofile,&rp->portals[i]); + + //Write rest of data + cf_WriteInt(ofile,rp->flags); + + // Write pulse time + cf_WriteByte (ofile,rp->pulse_time); + cf_WriteByte (ofile,rp->pulse_offset); + + // Write mirrored face + cf_WriteShort (ofile,rp->mirror_face); + + // Save doorway data if this room has a door + if (rp->flags & RF_DOOR) + { + doorway *dp= rp->doorway_data; + + cf_WriteByte(ofile,dp->flags); + cf_WriteByte (ofile,dp->keys_needed); + cf_WriteInt (ofile,dp->doornum); + cf_WriteFloat(ofile,dp->position); + } + + if (!rp->volume_lights) + cf_WriteByte (ofile,0); + else + { + cf_WriteByte (ofile,1); + int w=rp->volume_width; + int h=rp->volume_height; + int d=rp->volume_depth; + + cf_WriteInt (ofile,w); + cf_WriteInt (ofile,h); + cf_WriteInt (ofile,d); + + CheckToWriteCompressByte (ofile,rp->volume_lights,w*h*d); + } + + cf_WriteFloat (ofile,rp->fog_depth); + cf_WriteFloat (ofile,rp->fog_r); + cf_WriteFloat (ofile,rp->fog_g); + cf_WriteFloat (ofile,rp->fog_b); + + //Write ambient sound pattern name + cf_WriteString(ofile,(rp->ambient_sound == -1)? "" : AmbientSoundPatternName(rp->ambient_sound)); + + cf_WriteByte(ofile, (sbyte)rp->env_reverb); + + //Write damage + cf_WriteFloat(ofile, rp->damage); + cf_WriteByte(ofile, rp->damage_type); + + return 1; +} + +//Writes a chunk header. Writes chunk id & placeholder length. Returns chunk start pos +int StartChunk(CFILE *ofile,char *chunk_name) +{ + int chunk_start_pos; + + cf_WriteBytes((ubyte *) chunk_name,4,ofile); + chunk_start_pos = cftell(ofile); + cf_WriteInt(ofile,0); //placeholder for chunk len + + return chunk_start_pos; +} + +//Fill in chunk length when done writing +void EndChunk(CFILE *ofile,int chunk_start_pos) +{ + int save_pos = cftell(ofile); + int len = save_pos-chunk_start_pos; + + //pad the chunk if necessary to make multiple of four bytes + while (len & 3) { + cf_WriteByte(ofile,0); + len++; + save_pos++; + } + + //seek back to len field and fill in value + cfseek(ofile,chunk_start_pos,SEEK_SET); + cf_WriteInt(ofile,len); //write chunk length + + //go back to end of file + cfseek(ofile,save_pos,SEEK_SET); +} + +void WriteTerrainHeightChunk (CFILE *fp) +{ + int i; + ubyte heightvals[TERRAIN_WIDTH*TERRAIN_DEPTH]; + + int start_pos; + start_pos=StartChunk(fp,CHUNK_TERRAIN_HEIGHT); + + for (i=0;inum_nodes); + if(bnlist->num_nodes) + { + for(j = 0; j < bnlist->num_nodes; j++) + { + cf_WriteFloat(fp, bnlist->nodes[j].pos.x); + cf_WriteFloat(fp, bnlist->nodes[j].pos.y); + cf_WriteFloat(fp, bnlist->nodes[j].pos.z); + + cf_WriteShort(fp, bnlist->nodes[j].num_edges); + if(bnlist->nodes[j].num_edges) + { + for(k = 0; k < bnlist->nodes[j].num_edges; k++) + { + cf_WriteShort(fp, bnlist->nodes[j].edges[k].end_room); + cf_WriteByte(fp, bnlist->nodes[j].edges[k].end_index); + + cf_WriteShort(fp, bnlist->nodes[j].edges[k].flags); + cf_WriteShort(fp, bnlist->nodes[j].edges[k].cost); + + cf_WriteFloat(fp, bnlist->nodes[j].edges[k].max_rad); + } + } + } + } + } + } + cf_WriteByte(fp, BNode_verified?1:0); + + EndChunk (fp,start_pos); +} + +void WriteBOAChunk(CFILE *fp) +{ + int start_pos; + int i, j; + + start_pos = StartChunk(fp, CHUNK_BOA); + + cf_WriteInt(fp, BOA_mine_checksum); + cf_WriteInt(fp, BOA_vis_checksum); + cf_WriteInt(fp, Highest_room_index + 8); + cf_WriteInt(fp, MAX_PATH_PORTALS); + + for(i = 0; i <= Highest_room_index + 8; i++) + { + for(j = 0; j <= Highest_room_index + 8; j++) + { + cf_WriteShort(fp, BOA_Array[i][j]); + } + } + + for(i = 0; i <= Highest_room_index + 8; i++) + { + for(j = 0; j < MAX_PATH_PORTALS; j++) + { + cf_WriteFloat(fp, BOA_cost_array[i][j]); + } + } + + cf_WriteInt(fp, BOA_num_mines); + cf_WriteInt(fp, BOA_num_terrain_regions); + + for(i = 0; i < BOA_num_terrain_regions; i++) + { + cf_WriteInt(fp, BOA_num_connect[i]); + + for(j = 0; j < BOA_num_connect[i]; j++) + { + cf_WriteInt(fp, BOA_connect[i][j].roomnum); + cf_WriteInt(fp, BOA_connect[i][j].portal); + } + } + + EndChunk (fp,start_pos); +} + +void WriteRoomAABBChunk(CFILE *fp) +{ + int start_pos; + int i; + + start_pos = StartChunk(fp, CHUNK_ROOM_AABB); + + cf_WriteInt(fp, Highest_room_index); + + for(i = 0; i <= Highest_room_index; i++) + { + cf_WriteInt(fp, BOA_AABB_ROOM_checksum[i]); + } + + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + int j; + int k; + + ASSERT(Rooms[i].bbf_list && Rooms[i].bbf_list_min_xyz && Rooms[i].bbf_list_max_xyz && Rooms[i].bbf_list_sector); + + cf_WriteInt(fp, 1); // used + + cf_WriteInt(fp, Rooms[i].num_faces); + for(j = 0; j < Rooms[i].num_faces; j++) + { + cf_WriteFloat(fp, Rooms[i].faces[j].min_xyz.x); + cf_WriteFloat(fp, Rooms[i].faces[j].min_xyz.y); + cf_WriteFloat(fp, Rooms[i].faces[j].min_xyz.z); + + cf_WriteFloat(fp, Rooms[i].faces[j].max_xyz.x); + cf_WriteFloat(fp, Rooms[i].faces[j].max_xyz.y); + cf_WriteFloat(fp, Rooms[i].faces[j].max_xyz.z); + } + + cf_WriteInt(fp, BOA_AABB_ROOM_checksum[i]); + + cf_WriteFloat(fp, Rooms[i].bbf_min_xyz.x); + cf_WriteFloat(fp, Rooms[i].bbf_min_xyz.y); + cf_WriteFloat(fp, Rooms[i].bbf_min_xyz.z); + + cf_WriteFloat(fp, Rooms[i].bbf_max_xyz.x); + cf_WriteFloat(fp, Rooms[i].bbf_max_xyz.y); + cf_WriteFloat(fp, Rooms[i].bbf_max_xyz.z); + + cf_WriteShort(fp, Rooms[i].num_bbf_regions); + for(j = 0; j < Rooms[i].num_bbf_regions; j++) + { + cf_WriteShort(fp, Rooms[i].num_bbf[j]); + } + + for(j = 0; j < Rooms[i].num_bbf_regions; j++) + { + for(k = 0; k < Rooms[i].num_bbf[j]; k++) + { + cf_WriteShort(fp, Rooms[i].bbf_list[j][k]); + } + + cf_WriteFloat(fp, Rooms[i].bbf_list_min_xyz[j].x); + cf_WriteFloat(fp, Rooms[i].bbf_list_min_xyz[j].y); + cf_WriteFloat(fp, Rooms[i].bbf_list_min_xyz[j].z); + + cf_WriteFloat(fp, Rooms[i].bbf_list_max_xyz[j].x); + cf_WriteFloat(fp, Rooms[i].bbf_list_max_xyz[j].y); + cf_WriteFloat(fp, Rooms[i].bbf_list_max_xyz[j].z); + + cf_WriteByte(fp, Rooms[i].bbf_list_sector[j]); + } + } + else + { + cf_WriteInt(fp, 0); // Not used + } + } + + EndChunk (fp,start_pos); +} + +void WriteMatcenChunk(CFILE *fp) +{ + int start_pos; + + start_pos = StartChunk(fp, CHUNK_MATCEN_DATA); + + cf_WriteInt(fp, Num_matcens); + + int i; + + for(i = 0; i < Num_matcens; i++) + { + ASSERT(Matcen[i]); + Matcen[i]->Reset(); // Makes it like it hasn't run yet + Matcen[i]->SaveData(fp); + } + + EndChunk(fp, start_pos); +} + + +void WriteALifeChunk(CFILE *fp) +{ +#ifndef NEWEDITOR + int start_pos; + + start_pos = StartChunk(fp, CHUNK_ALIFE_DATA); + + a_life.SaveData(fp); + + EndChunk(fp, start_pos); +#endif +} + + +void WriteLevelGoalsChunk(CFILE *fp) +{ + int start_pos; + + start_pos = StartChunk(fp, CHUNK_LEVEL_GOALS); + Level_goals.SaveLevelGoalInfo(fp); + EndChunk(fp, start_pos); +} + +void WriteTerrainSkyAndLightChunk (CFILE *fp) +{ + int i; + int max_sats; + + max_sats=Terrain_sky.num_satellites; + + int start_pos; + start_pos=StartChunk(fp,CHUNK_TERRAIN_SKY); + + cf_WriteFloat (fp,Terrain_sky.fog_scalar); + cf_WriteFloat (fp,Terrain_sky.damage_per_second); + + cf_WriteByte (fp,Terrain_sky.textured); + + cf_WriteShort (fp,Terrain_sky.dome_texture); + + cf_WriteInt (fp,Terrain_sky.sky_color); + cf_WriteInt (fp,Terrain_sky.horizon_color); + cf_WriteInt (fp,Terrain_sky.fog_color); + + cf_WriteInt (fp,Terrain_sky.flags); + cf_WriteFloat (fp,Terrain_sky.radius); + cf_WriteFloat (fp,Terrain_sky.rotate_rate); + + cf_WriteInt (fp,max_sats); + + + for (i=0;i=2 && w<=128); + ASSERT (h>=2 && h<=128); + + cf_WriteShort (fp,w); + cf_WriteShort (fp,h); + + ushort *data=lm_data(LightmapInfo[i].lm_handle); + ASSERT (data!=NULL); + + CheckToWriteCompressShort (fp,data,w*h); + } + } + } + + cf_WriteInt (fp,lightmap_info_count); + + for (i=0;i MAX_PATH_PORTALS) + if (EditorMessageBox(MBOX_YESNO,"Room %d has %d portals, which is more than the %d portals that the path system allows." + "\n\nDo you want to save anyway?",r,MAX_PATH_PORTALS) != IDYES) + return 0; + } +#ifndef NEWEDITOR + //Get the level ready to save + ClearTransientObjects(1); //1 means clear proximity bombs +#endif + //CompressMine(); //get rid of holes in room list + MakeBOA(); + EBNode_VerifyGraph(); + ConsolidateMineMirrors(); + + if(f_save_room_AABB) + { + //Compute the bounding boxes + ComputeAABB(true); + } + + //Find shells for all the rooms + if (! FindArg("-noshells")) + ComputeAllRoomShells(); + + ofile = cfopen(filename,"wb"); + + if (!ofile) { + EditorMessageBox("Can't open file <%s> for writing: %s.",filename,sys_errlist[errno]); + return 0; + } + + try { //catch cfile errors + + //Write tag & version number + cf_WriteBytes((ubyte *) LEVEL_FILE_TAG,4,ofile); + cf_WriteInt(ofile,LEVEL_FILE_VERSION); + + + WriteGamePathsChunk(ofile); + + //Write terrain sounds + chunk_start_pos = StartChunk (ofile,CHUNK_TERRAIN_SOUND); + int n_bands=0,b; + for (b=0;bused) { + nrooms++; + nverts += rp->num_verts; + nfaces += rp->num_faces; + nfaceverts += CountRoomFaceVerts(rp); + nportals += rp->num_portals; + } + cf_WriteInt(ofile,nrooms); + cf_WriteInt(ofile,nverts); + cf_WriteInt(ofile,nfaces); + cf_WriteInt(ofile,nfaceverts); + cf_WriteInt(ofile,nportals); + + //Write out the rooms + for (i=0;i<=Highest_room_index;i++) { + if (Rooms[i].used) { + cf_WriteShort(ofile,i); //write out room number + WriteRoom(ofile,&Rooms[i]); + } + } + + //Done with rooms + EndChunk(ofile,chunk_start_pos); + + //Write room wind, if any rooms have wind + for (i=nrooms=0,rp=Rooms;i<=Highest_room_index;i++,rp++) //Count the number of rooms with wind + if ((rp->wind.x != 0.0) || (rp->wind.y != 0.0) || (rp->wind.z != 0.0)) + nrooms++; + if (nrooms) { + chunk_start_pos = StartChunk(ofile,CHUNK_ROOM_WIND); + cf_WriteInt(ofile,nrooms); + for (i=0,rp=Rooms;i<=Highest_room_index;i++,rp++) { //write the wind values + if ((rp->wind.x != 0.0) || (rp->wind.y != 0.0) || (rp->wind.z != 0.0)) { + cf_WriteShort(ofile,i); + cf_WriteVector(ofile,&rp->wind); + } + } + EndChunk(ofile,chunk_start_pos); + } + + //Write terrain + chunk_start_pos = StartChunk(ofile,CHUNK_TERRAIN); + WriteTerrainChunks (ofile); + EndChunk(ofile,chunk_start_pos); + + //count the number of deleted object handles we need to write + for (i=0,count=0;i, msg = \"%s\"\n",cfe->file->name,cfe->msg)); + ASSERT(cfe->read_write == CFE_WRITING); + EditorMessageBox("Error writing file %s: %s",cfe->file->name,cfe->msg); + cfclose(ofile); + return 0; + } + + //Close the file + cfclose(ofile); + + return 1; + +} + +#endif //ifdef EDITOR + +#ifndef NEWEDITOR + +int need_to_page_in = 0; +int need_to_page_num = 0; +void AlmostPageInGeneric (int id); + +ubyte texture_counted[MAX_BITMAPS]; +ubyte sound_counted[MAX_SOUNDS]; +ubyte poly_counted[MAX_POLY_MODELS]; + + +void AlmostPageInLevelTexture (int id) +{ + if (id==-1 || id==0) + return; + + if((texture_counted[id]==0) &&(GameBitmaps[id].flags & BF_NOT_RESIDENT)) + { + texture_counted[id] = 1; + need_to_page_num++; + /* + //Get the size and add it to the count + CFILE * infile = cfopen (GameBitmaps[id].name,"rb"); + //Get the size and add it to the count + if(infile) + { + need_to_page_in += cfilelength(infile); + cfclose(infile); + } + */ + if (GameTextures[id].flags & TF_DESTROYABLE && GameTextures[id].destroy_handle!=-1) + AlmostPageInLevelTexture (GameTextures[id].destroy_handle); + } + +} + + +void AlmostPageInSound (int id) +{ + if (id==-1) + return; + + // Check if the sample data is already loaded + if(SoundFiles[id].sample_16bit != NULL || SoundFiles[id].sample_8bit != NULL || sound_counted[id]) + return ; + sound_counted[id] = 1; + need_to_page_num++; + /* + //Check for space this sound takes up, and add it to our counter + CFILE * infile = cfopen (SoundFiles[id].name,"rb"); + //Get the size and add it to the count + if(infile) + { + need_to_page_in += cfilelength(infile); + cfclose(infile); + } + */ +} + +void AlmostPageInPolymodel(int num) +{ + if((poly_counted[num]==0) && (Poly_models[num].flags & PMF_NOT_RESIDENT)) + { + poly_counted[num] = 1; + need_to_page_num++; + /* + CFILE * infile = cfopen (Poly_models[num].name,"rb"); + //Get the size and add it to the count + if(infile) + { + need_to_page_in += cfilelength(infile); + cfclose(infile); + } + */ + } +} + + +void AlmostPageInDoor (int id) +{ + //Set sounds + door *doorpointer=&Doors[id]; + + AlmostPageInPolymodel (doorpointer->model_handle); + + poly_model *pm=&Poly_models[doorpointer->model_handle]; + for (int t=0;tn_textures;t++) + AlmostPageInLevelTexture (pm->textures[t]); + + + if (doorpointer->open_sound!=-1 && doorpointer->open_sound!=SOUND_NONE_INDEX) + AlmostPageInSound (doorpointer->open_sound); + if (doorpointer->close_sound!=-1 && doorpointer->close_sound!=SOUND_NONE_INDEX) + AlmostPageInSound (doorpointer->close_sound); +} + +void AlmostPageInWeapon (int id) +{ + weapon *weaponpointer=&Weapons[id]; + + if (id==-1) + return; + + int i; + + if (!(weaponpointer->flags & (WF_IMAGE_BITMAP|WF_IMAGE_VCLIP))) + { + AlmostPageInPolymodel (weaponpointer->fire_image_handle); + + poly_model *pm=&Poly_models[weaponpointer->fire_image_handle]; + for (int t=0;tn_textures;t++) + AlmostPageInLevelTexture (pm->textures[t]); + } + + // Load the various textures associated with this weapon + if (weaponpointer->explode_image_handle!=-1) + { + AlmostPageInLevelTexture (weaponpointer->explode_image_handle); + } + + if (weaponpointer->particle_handle!=-1) + { + AlmostPageInLevelTexture (weaponpointer->particle_handle); + } + + if (weaponpointer->smoke_handle!=-1) + { + AlmostPageInLevelTexture(weaponpointer->smoke_handle); + } + + if (weaponpointer->scorch_handle!=-1) + { + AlmostPageInLevelTexture (weaponpointer->scorch_handle); + } + + if (weaponpointer->icon_handle!=-1) + { + AlmostPageInLevelTexture (weaponpointer->icon_handle); + } + + // Try to load spawn weapons + if (weaponpointer->spawn_handle!=-1 && weaponpointer->spawn_count>0 && weaponpointer->spawn_handle!=id) + { + AlmostPageInWeapon (weaponpointer->spawn_handle); + } + + if (weaponpointer->alternate_spawn_handle!=-1 && weaponpointer->spawn_count>0 && weaponpointer->alternate_spawn_handle!=id) + { + AlmostPageInWeapon (weaponpointer->alternate_spawn_handle); + } + + if (weaponpointer->robot_spawn_handle!=-1) + { + AlmostPageInGeneric (weaponpointer->robot_spawn_handle); + } + + // Try and load the various sounds + for (i=0;isounds[i]!=SOUND_NONE_INDEX) + { + AlmostPageInSound (weaponpointer->sounds[i]); + } + } +} + +void AlmostPageInShip (int id) +{ + int i,t; + + ship *shippointer=&Ships[id]; + + // Page in all textures for this object + + AlmostPageInPolymodel (shippointer->model_handle); + + + poly_model *pm=&Poly_models[shippointer->model_handle]; + + for (t=0;tn_textures;t++) + AlmostPageInLevelTexture (pm->textures[t]); + + if (shippointer->med_render_handle!=-1) + { + AlmostPageInPolymodel (shippointer->med_render_handle); + + pm=&Poly_models[shippointer->med_render_handle]; + for (t=0;tn_textures;t++) + AlmostPageInLevelTexture (pm->textures[t]); + } + + if (shippointer->lo_render_handle!=-1) + { + AlmostPageInPolymodel (shippointer->lo_render_handle); + + pm=&Poly_models[shippointer->lo_render_handle]; + for (t=0;tn_textures;t++) + AlmostPageInLevelTexture (pm->textures[t]); + } + + if (shippointer->dying_model_handle!=-1) + { + AlmostPageInPolymodel (shippointer->dying_model_handle); + + + pm=&Poly_models[shippointer->dying_model_handle]; + for (t=0;tn_textures;t++) + AlmostPageInLevelTexture(pm->textures[t]); + } + + // Try and load the various weapons + int j; + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if (shippointer->static_wb[i].gp_weapon_index[j] != LASER_INDEX) + { + AlmostPageInWeapon (shippointer->static_wb[i].gp_weapon_index[j]); + } + } + } + + // Try and load the various weapons + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if (shippointer->static_wb[i].fm_fire_sound_index[j] != SOUND_NONE_INDEX) + AlmostPageInSound (shippointer->static_wb[i].fm_fire_sound_index[j]); + } + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + if (shippointer->firing_sound[i]!=-1) + AlmostPageInSound (shippointer->firing_sound[i]); + + if(shippointer->firing_release_sound[i] != -1) + AlmostPageInSound (shippointer->firing_release_sound[i]); + + if (shippointer->spew_powerup[i]!=-1) + AlmostPageInGeneric (shippointer->spew_powerup[i]); + + } +} + +void AlmostPageInGeneric (int id) +{ + int i,t; + + if (id==-1) + return; + + object_info *objinfopointer=&Object_info[id]; + + // Page in all textures for this object + + AlmostPageInPolymodel (objinfopointer->render_handle); + + poly_model *pm=&Poly_models[objinfopointer->render_handle]; + + for (t=0;tn_textures;t++) + AlmostPageInLevelTexture(pm->textures[t]); + + + if (objinfopointer->med_render_handle!=-1) + { + AlmostPageInPolymodel (objinfopointer->med_render_handle); + + + + pm=&Poly_models[objinfopointer->med_render_handle]; + for (t=0;tn_textures;t++) + AlmostPageInLevelTexture (pm->textures[t]); + } + + if (objinfopointer->lo_render_handle!=-1) + { + AlmostPageInPolymodel (objinfopointer->lo_render_handle); + + + pm=&Poly_models[objinfopointer->lo_render_handle]; + for (t=0;tn_textures;t++) + AlmostPageInLevelTexture (pm->textures[t]); + } + + // Process all sounds for this object + for (i=0;isounds[i]!=SOUND_NONE_INDEX) + { + AlmostPageInSound (objinfopointer->sounds[i]); + } + } + + if (objinfopointer->ai_info) { + for (i=0;iai_info->sound[i]!=SOUND_NONE_INDEX) + { + AlmostPageInSound (objinfopointer->ai_info->sound[i]); + } + } + } + + // Try and load the various wb sounds + int j; + if(objinfopointer->static_wb) + { + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if(objinfopointer->static_wb[i].fm_fire_sound_index[j]!=SOUND_NONE_INDEX) + { + AlmostPageInSound (objinfopointer->static_wb[i].fm_fire_sound_index[j]); + } + } + } + } + + // Try and load the various wb sounds + if(objinfopointer->anim) { + for(i = 0; i < NUM_MOVEMENT_CLASSES; i++) + { + for(j = 0; j < NUM_ANIMS_PER_CLASS; j++) + { + if(objinfopointer->anim[i].elem[j].anim_sound_index!=SOUND_NONE_INDEX) + { + AlmostPageInSound (objinfopointer->anim[i].elem[j].anim_sound_index); + } + } + } + } + + // Load the spew types + for(i=0;idspew_number[i]>0 && objinfopointer->dspew[i]!=0 && objinfopointer->dspew[i]!=id) + { + AlmostPageInGeneric (objinfopointer->dspew[i]); + } + } + + // Try and load the various weapons + + if(objinfopointer->static_wb){ + // Automatically include laser + AlmostPageInWeapon (LASER_INDEX); + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if (objinfopointer->static_wb[i].gp_weapon_index[j]!=LASER_INDEX) + { + AlmostPageInWeapon (objinfopointer->static_wb[i].gp_weapon_index[j]); + } + } + } + } +} + +extern char *Static_sound_names[]; +void AlmostPageInAllData () +{ + int i; + + AlmostPageInShip (Players[Player_num].ship_index); + AlmostPageInLevelTexture (FindTextureName("LightFlareStar")); + AlmostPageInLevelTexture (FindTextureName("LightFlare")); + +//mprintf((0,"%d bytes to page in for the ship.\n",need_to_page_in)); + // Get static fireballs + for (i=0;inum_faces;t++) + { + AlmostPageInLevelTexture(rp->faces[t].tmap); + } + } +//mprintf((0,"%d bytes to page in for room textures.\n",need_to_page_in)); + // Touch all terrain textures + for (i=0;itype==OBJ_POWERUP || obj->type==OBJ_ROBOT || obj->type==OBJ_CLUTTER || obj->type==OBJ_BUILDING) + { + AlmostPageInGeneric (obj->id); + continue; + } + + if (obj->type==OBJ_DOOR) + { + AlmostPageInDoor (obj->id); + continue; + } + } +//mprintf((0,"%d bytes to page in for objects.\n",need_to_page_in)); +} + + +//Go through all the data needing to be paged in, add it all up. +int CountDataToPageIn() +{ + + need_to_page_num = 0; + need_to_page_in = 0; + + int i; + + for(i=0;i 1 Save curseg & markedseg to file +//1 -> 2 Save texture names to file & remap when load +//2 -> 3 Save selected list +//3 -> 4 Save triggers and doorways +//4 -> 5 Save segnums as ints, not shorts. Also, save terrain height array +//5 -> 6 Save & xlate names for robots, powerups, & doors +//6 -> 7 Instead of saving a bunch of type-specific data, read it from the data page +//7 -> 8 Now saves terrain sky data +//8 -> 9 Now saves the mine/terrain links +//9 ->10 Changes for floating segments +//10->11 Added tmap2 textures to terrain +//11->12 save u,v coords for terrain +//12->13 New chunk-based file format to save room data +//13->14 Save some more data in editor chunk +//14->15 Saves terrain info in chunk format +//15->16 Changes UV terrain chunk format +//16->17 Generic objects replace robots & powerups +//17->18 Custom script handle read and written for objects 8-11-97 +//18->19 Now saves lightmap info with room faces +//19->20 Now saves mine/terrain links and sky info +//20->21 Now saves alpha component per vertex +//21->22 Now saves upper left vertex +//22->23 Save portal num as byte +//23->24 Saves RGB lighting for terrain +//24->25 Saves terrain dynamic lighting table +//25->26 If level isn't version 26 or above, then we need to ignore any script chunk, create a new one. +//26->27 Save face flags as short +//27->28 Store doorway information +//28->29 Killed static light field in room struct +//29->30 Store new lightmap_info information +//30->31 Read in script names instead of handles for all objects. +//31->32 Do RLE compression for terrain data +//32->33 Save more info about doorways +//33->34 Save object id as short +//34->35 Save info about object lightmaps +//35->36 Save "keys_needed" field for doorways +//36->37 Do tricks to restore OF_POLYGON_OBJECT flag +//37->38 Do RLE compression for lightmaps +//38->39 Store lightmap spacing as floats,not ubytes +//39->40 Save/load shadow rooms/faces +//40->41 Save horizon texture info +//41->42 Save BSP info for the mine +//42->43 Save extra texture pieces for terrain sky +//43->44 Save terrain satellite flags +//44->45 Objects no longer compressed, so save objnum (handle,actually) with each object +//45->46 Objects and triggers have optional script parameters. Also, compressed script info in object. +//46->47 Only save one viewer id, not two +//47->48 Read in trigger parameters too. +//48->49 Objects now store terrain flag as part of roomnum +//49->50 Store light multiplier per face +//50->51 Store fvec/uvec of path nodes +//51->52 Now saves/loads wall pulsing data +//52->53 Face light multiple now works in quarter steps +//54->55 Add wireframe view info to editor chunk +//55->56 Don't read/write useless face info such as rgba +//56->57 Don't read/write pixel error or terrain distance +//57->58 Read uvec,rvec for lightmap infos +//58->59 Store lightmap normals +//59->60 Don't store lightmap normals for dynamic objects +//60->61 Trimmed some fat from rooms,faces structures +//61->62 BOA now saves out the portal cost array +//62->63 We now save path_pnts with rooms and portals +//62->64 Translate old 565 lightmaps into 1555 format +//64->65 Added object parent_handles +//65->66 Store lightmap spacing as bytes, not floats +//66->67 Save volume lighting for rooms +//67->68 Save specular lighting for rooms +//68->69 Save terrain sky radius +//69->70 Read in whether attached script is default or custom. +//70->71 Threw out vertex based specularity and went with lightmap based specularity +//71->72 Added terrain occlusion data +//72->73 Now store volumetric fog info with room +//73->74 Support for new banded-dome skies +//74->75 Now supports satellite lighting values +//75->76 Saves BOA_vis_checksum to file +//76->77 Saves multiple specular values +//77->78 Added ambient sound field to room +//78->79 Added mirrored faces +//79->80 New single-face portals +//80->81 Save marked face & vert +//81->82 Door name translation +//82->83 Added gravity to level info +//83->84 Added level goal information +//84->85 Save the amount of memory needed by the rooms +//85->86 Added room AABB information to save +//86->87 Added damage per second for terrain sky +//87->88 Add fog scalar for adjustable fog distance +//88->89 Took out dynamic light data saving +//89->90 Save out more than one lightmap_info per lightmap +//90->91 Save out lightmap_info x1,y1 origin in megalightmap +//91->92 Reduced boa save size +//92->93 Ambeint Life +//93->94 Changed object handle bit allocation +//94->95 Added object names +//95->96 Added room names, & took out room compression (so saved list can have holes) +//96->97 Ripped out OSIRIS v1.0 +//97->98 ??? +//98->99 Added trigger names +//99->100 Add combinable portals +//100->101 Changed object flags from short to int, and only read certain flags +//101->102 Changed terrain textures system +//102->103 Removed portal triggers +//103->104 Changed terrain sky system +//104->105 Added custom_default_script_name and custom_default_module_name to objects +//105->106 Rewrote doorway system +//106->107 Save out BOA terrain info (temp, for vis and multiplay only) +//107->108 Added room damage +//108->109 Added blastable doors +//109->110 Update BOA to its final form +//109->111 Don't save an object's parent handle +//111->112 Added BOA_connect information +//112->113 Save room multipliers out with editor chunk +//113->114 Took out band textures +//114->115 Added soundsource objects +//115->116 Save satellite sizes +//116->117 added smooth specular mapping +//117->118 added room ambience settings +//118->119 Save sound names, not indices +//119->120 Save the number of player starts to the file +//120->121 Added the BOA Node chunk +//121->122 Added the ability for a level to always have the ceiling checked +//122->123 Added the bnode_index to portals... +//122->124 Added the bnode verify flag +//124->125 Removed the portal field from BNode edges as the room portals now contain bnode info (so it isn't necessary) +//125->126 Save lightmap spacing info +//126->127 Added ceiling value +//127->128 Save rest of lighting parameters +//128->129 Add the Override sound chunk +//129->130 Added the modified force field bounce textures +//130->131 Added the powerups ignore gravity checkbox +//131->132 Added another ff bounce texture for Dan (his last day wish) + +//Load a level file +//Returns 1 if file read ok, else 0 +// cb_fn returns current chunk, parm1 = # bytes in chunk, parm2 = number of bytes in file +// parm1 = -1, for 1st just opened level +// parm2 = -2, for done loading. +int LoadLevel(char *filename, void (*cb_fn)(const char *,int, int) = NULL); + +//Save a level file +//Returns 1 if file saved ok, else 0 +int SaveLevel(char *filename, bool f_save_room_AABB = true); + +//Reads a room from a disk file +//Parameters: ifile - file to read from +// rp - room to read +// version - version number of file +//Returns: 1 if read ok, else 0 +int ReadRoom(CFILE *ifile,room *rp,int version); + +//Writes a room to a disk file +//Parameters: ofile - file to write to +// rp - room to write +//Returns: 1 if written ok, else 0 +int WriteRoom(CFILE *ofile,room *rp); + +//Write the texture names for remapping when level is loaded +void WriteTextureList(CFILE *ofile); + +//Read the texture names & build the xlate table +void ReadTextureList(CFILE *ifile); + +// Primarily for multiplayer, makes sure the client and server levels are the same + +#include "../md5/md5.h" +extern MD5 *Level_md5; +inline void RestartLevelMD5() +{ + if(Level_md5) + delete Level_md5; + Level_md5 = new MD5(); + Level_md5->MD5Init(); +} + +inline void AppendToLevelChecksum(int val) +{ + if(!Level_md5) + { + return; + } + Level_md5->MD5Update(val); +} + +inline void AppendToLevelChecksum(unsigned int val) +{ + if(!Level_md5) + { + return; + } + Level_md5->MD5Update(val); +} + +inline void AppendToLevelChecksum(unsigned short val) +{ + if(!Level_md5) + { + return; + } + Level_md5->MD5Update(val); +} + +inline void AppendToLevelChecksum(short val) +{ + if(!Level_md5) + { + return; + } + Level_md5->MD5Update(val); +} + +inline void AppendToLevelChecksum(float val) +{ + if(!Level_md5) + { + return; + } + Level_md5->MD5Update(val); +} + +inline void AppendToLevelChecksum(vector val) +{ + if(!Level_md5) + { + return; + } + Level_md5->MD5Update(val.x); + Level_md5->MD5Update(val.y); + Level_md5->MD5Update(val.z); +} + +inline void AppendToLevelChecksum(unsigned char val) +{ + if(!Level_md5) + { + return; + } + Level_md5->MD5Update(val); +} + +inline void GetLevelMD5Sum(unsigned char digest[16]) +{ + if(!Level_md5) + { + for(int i=0;i<16;i++) + digest[i] = 0; + return; + } + Level_md5->MD5Final(digest); +} +#include +inline char * GetCurrentSumString() +{ + static char output_buf[100]; + output_buf[0] = '\0'; + // Make a copy of the context so we don't mess + // up an in progress md5 sum. + MD5 *checksum = Level_md5->Clone(); + + unsigned char digest[16]; + checksum->MD5Final(digest); + char bytestr[10] = ""; + // Do level checksum + for(int i=0;i<16;i++) + { + sprintf(bytestr,"%.2x",digest[i]); + strcat(output_buf,bytestr); + } + MD5::Destroy(checksum); + return output_buf; +} + diff --git a/Descent3/Mission.cpp b/Descent3/Mission.cpp new file mode 100644 index 000000000..a202e7028 --- /dev/null +++ b/Descent3/Mission.cpp @@ -0,0 +1,2228 @@ +/* + * $Logfile: /DescentIII/Main/Mission.cpp $ + * $Revision: 193 $ + * $Date: 10/21/01 4:19p $ + * $Author: Kevin $ + * + * Mission level sequencing and management. + * + * $Log: /DescentIII/Main/Mission.cpp $ + * + * 193 10/21/01 4:19p Kevin + * Fix for bug 5566. Reset the URL structure on mission init + * + * 192 10/08/01 4:21p Matt + * If the mission has data errors, tell the user on the loading screen. + * + * 191 3/20/00 12:17p Matt + * Merge of Duane's post-1.3 changes. + * Mission name changes (Mac only) + * + * 190 11/05/99 12:18p Jay + * Fixed bugs with Jeff's fix + * + * 189 11/05/99 11:05a Jeff + * fixed mission movie playing + * + * 188 11/04/99 12:35a Chris + * Added support for Merc + * + * 187 10/25/99 9:52a Matt + * Mac merge + * + * 186 10/12/99 11:05a Jeff + * new msn keyword "SHIP" to override default ship to something else + * + * 185 10/04/99 9:58a Kevin + * Demo fixes for mac demo + * + * 184 8/10/99 5:12p Jeff + * fixed memory leak + * + * 183 7/28/99 4:03p Kevin + * Macintosh! + * + * 182 7/08/99 10:30a Kevin + * when loading a save game >level 4 from a minimal install, don't ask for + * CD1 + * + * 181 6/16/99 12:03p Kevin + * Fix a stupid bug where minimal installs won't open d3voicex.hog + * + * 180 6/10/99 4:12p Kevin + * Fixed SetLevel in dedicated server for HEAT.NET, and added + * level_names.str for level name localization. + * + * 179 6/03/99 8:48a Kevin + * fixes for new OEM version.... + * + * 178 5/20/99 6:21p Kevin + * Fixed HOARD keyword thingy + * + * 177 5/20/99 4:54p Jason + * added heartbeats to server + * + * 176 5/20/99 4:10p Jason + * added heartbeat to multiplayer so clients wouldn't time out, also + * various multiplayer fixes + * + * 175 5/20/99 3:40p Jason + * made mission looping work correctly in multiplayer + * + * 174 5/07/99 5:06p Jason + * display level name on loading screen + * + * 173 5/03/99 5:12p Jason + * fixing BOA vis problem... + * + * 172 5/03/99 3:35p Kevin + * bug fixes + * + * 171 5/02/99 2:32p Kevin + * fixed various dedicated server problems. + * + * 170 5/01/99 5:12p Jason + * put BOA check back in + * + * 169 5/01/99 4:06p Jeff + * fixed #include for linux + * + * 168 5/01/99 12:42p Kevin + * removed BOA warning. It was showing up for all levels with no terrain + * + * 167 4/30/99 5:41p Jason + * fixed some boa/terrain level issues + * + * 166 4/30/99 3:03p Kevin + * improved times restored count + * + * 165 4/30/99 2:21p Jason + * changes to verify level completeness + * + * 164 4/25/99 2:18p Kevin + * moved d3 main mission voice files into seperate hogs + * + * 163 4/23/99 10:34p Kevin + * fixed bad things with multiple CD code + * + * 162 4/23/99 5:32p Kevin + * Fixed a few mission bugs. + * + * 161 4/23/99 3:33p Kevin + * mission file/multiplayer mod keyword system + * + * 160 4/23/99 10:35a Kevin + * minor cosmetic changes to the load level screen + * + * 159 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 158 4/22/99 9:53a Kevin + * Fixed some dedicated server crashes + * + * 157 4/21/99 2:15p Samir + * table file filter fixes. + * + * 156 4/20/99 3:32p Kevin + * new prepare for descent behaivor + * + * 155 4/20/99 2:47p Matt + * When playing from the editor, set the level number to 1. + * + * 154 4/20/99 12:44a Matt + * Use briefing font instead of HUD font for level loading messages. + * + * 153 4/19/99 6:10p Kevin + * Demo now only has one multiplayer level + * + * 152 4/18/99 1:56p Kevin + * Movie player fixes to avoid crashes running under the editor + * + * 151 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 150 4/17/99 3:44p Kevin + * Demo2 changes & fixes + * + * 149 4/16/99 6:00p Kevin + * Bunches of Demo stuff + * + * 148 4/15/99 1:40a Jeff + * changes for linux compile + * + * 147 4/14/99 3:07p Kevin + * Fixed some multiple CD bugs + * + * 146 4/14/99 12:35p Samir + * localization issues. + * + * 145 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 144 4/13/99 6:15p Kevin + * added TXT_PREPARE_FOR_DESCENT + * + * 143 4/11/99 4:45p Matt + * Added code for localization of level goal text. + * + * 142 4/08/99 3:13p Matt + * Finished cleaning up level sequencing code. Got rid of all the "level + * minus one" stuff. + * + * 141 4/07/99 3:40p Kevin + * Fixes for Beta 1 + * + * 140 4/07/99 12:30p Matt + * Added code for failed missions. + * + * 139 4/05/99 2:54p Samir + * display more errors for mission file load failures. + * + * 138 4/03/99 5:23p Matt + * Removed name, author, & description from the mission level structure. + * + * 137 4/03/99 5:05p Samir + * added ENDMISSION keyword to end a mission after a certain level. + * + * 136 3/31/99 10:28a Samir + * I think this is abug in DoMissionMovie. Passed the parameter + * (filename) to GetMultiCDPath. + * + * 135 3/29/99 5:32p Kevin + * Build fixes + * + * 134 3/24/99 3:54p Jeff + * much prettier dedicated server load progress indicators + * + * 133 3/24/99 2:56p Kevin + * Fixed level specific progress indicator to have a default for single + * player missions, and fixed the default mission name (put it in the + * stringtable) + * + * 132 3/24/99 1:23p Kevin + * Support for level specific progress screens. + * + * 131 3/24/99 11:42a Kevin + * Fixed multiplayer missions + * + * 130 3/24/99 10:54a Kevin + * Fixed some problems related to splitting up the main d3.mn3 file across + * 2 CDs + * + * 129 3/19/99 4:08p Kevin + * Multiple CD installation support + * + * 128 3/18/99 12:51p Samir + * debug info. + * + * 127 3/09/99 6:34p Kevin + * Made the training mission not be branching, and fixed the crash with + * people dying in the demo playback + * + * 126 3/05/99 5:24p Jeff + * fixed mission names for multiplayer levels in oem + * + * 125 3/04/99 5:24p Kevin + * Added filenames for OEM + * + * 124 3/03/99 7:20p Jeff + * music score added for oem levels + * + * 123 3/03/99 12:33a Kevin + * Minor OEM changes + * + * 122 3/02/99 7:20p Kevin + * + * 121 3/02/99 1:18p Samir + * reset srclinenum in LoadMission so error box reports correct line num. + * + * 120 3/02/99 11:52a Kevin + * Fixes for OEM Beta 4.1 + * + * 119 3/02/99 1:23a Kevin + * + * 117 3/02/99 12:32a Kevin + * + * 116 3/01/99 11:47p Kevin + * + * 115 3/01/99 11:38p Kevin + * + * 113 3/01/99 9:39p Kevin + * Doh! #elseif should have been #else + * + * 112 3/01/99 9:03p Kevin + * OEM Beta 4 + * + * 111 2/28/99 3:26a Samir + * redid newgame dialog. + * + * 110 2/26/99 2:11a Samir + * messagebox if misison load failed. + * + * 109 2/24/99 3:15p Kevin + * OEM menu changes, and bug fixes for the save/load system + * + * 108 2/24/99 2:03p Kevin + * fixed bug with flickering status bar + * + * 107 2/17/99 8:33p Samir + * fixed potential bugs with mission files that fail top open. + * + * 106 2/16/99 12:39p Kevin + * Improved paging data progress indicator + * + * 105 2/16/99 12:36a Kevin + * Fixes for release builds of OEM V3 and KAtmai + * + * 104 2/13/99 1:56p Kevin + * + * 103 2/10/99 4:56p Kevin + * Better progress indicator & prepare for Descent message + * + * 102 2/10/99 11:25a Kevin + * + * 101 2/09/99 7:01p Kevin + * First work for new and improved progress screen while loading a level. + * Note that this is a hack at this point, while I get the details worked + * out, then I'll make it cleaner. + * + * 100 2/04/99 9:28a Kevin + * Added a few OEM #ifdefs + * + * 99 2/03/99 4:20p Kevin + * Got multiplayer working with .mn3 files, and setup autodownloading + * + * 98 2/03/99 2:54p Jeff + * display telcom if there is a briefing and/or ships to choose from + * + * 97 1/29/99 5:22p Jeff + * localization + * + * 96 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 95 1/19/99 1:15p Samir + * add on missions. + * + * 94 1/16/99 10:39a Jeff + * added mission memory management to Osiris...only slightly tested. Need + * to solve game save/restore problem still + * + * 93 1/13/99 12:43p Jason + * fixed flickering exit menu screen + * + * 92 1/11/99 12:47p Jason + * misc changes for polishing + * + * 91 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 90 1/04/99 5:44p Samir + * added game state flags. + * + * 89 12/30/98 12:15p Kevin + * Auto Mission Download system + * + * 88 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 87 12/16/98 3:24p Samir + * new way to get info on a mission (used to determine training mission + * too.) + * + * 86 12/16/98 1:57p Samir + * Replaced CleanupString2 with CleanupStr + * + * 85 12/03/98 12:51p Samir + * music score specified per level. + * + * 84 11/02/98 6:00p Jeff + * began adding single player ship selection + * + * 83 10/23/98 8:28p Samir + * enhanced load level screen. + * + * 82 10/23/98 12:52p Samir + * added gray backbar for non splash loading screens. + * + * 81 10/22/98 10:49p Samir + * print out real messages for loadlevel and clear bar when collating. + * + * 80 10/22/98 12:46a Matt + * Got rid of the loading screen before the briefing. Instead, menu.cpp + * shows the menu background screen (without the menu). + * + * 79 10/20/98 6:34p Jeff + * changes made to get dedicated server working + * + * 78 10/19/98 10:35p Kevin + * + * 77 10/19/98 7:51p Kevin + * performance testing + * + * 76 10/19/98 5:41p Samir + * fixed booboo. + * + * 75 10/19/98 5:39p Samir + * added loading level screen. + * + * 74 10/18/98 2:58p Jason + * fixes for beta4 + * + * 73 10/18/98 12:14p Jeff + * demo multiplayer mission loops + * + * 72 10/14/98 7:30p Matt + * Fixed compile warning + * + * 71 10/14/98 7:19p Kevin + * More dsp changes... + * + * 70 10/14/98 6:40p Samir + * ShowProgressScreen uses Max_window_w, and height. + * + * 69 10/13/98 3:48p Samir + * added error checking if script failed to load (default). + * + * 68 10/11/98 3:04a Jeff + * for the demo, new was being used to alloc the level node, when it + * should have been mem_malloc + * + * 67 10/09/98 6:58p Kevin + * changed demo level to polaris.d3l + * + * 66 10/08/98 7:29p Samir + * revamped sequencing. + * + * 65 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 64 10/07/98 11:34a Kevin + * + * 63 10/06/98 10:36a Kevin + * updated level names for demo + * + * 62 10/06/98 10:34a Kevin + * Put in demo code for multi + * + * 61 10/02/98 5:46p Samir + * took out mission file objective text and replaced with level goals. + * + * 60 9/22/98 3:56p Samir + * special demo code doesn't allow pilot and mission stuff. + * + * 59 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 58 9/14/98 6:28p Jason + * first pass at getting dedicated server working + * + * 57 8/28/98 12:57p Jeff + * parm added to TelComShow + * + * 56 8/27/98 2:51p Jeff + * made it so SHIFT-ESC exits the TelCom back to Main Menu + * + * 55 8/24/98 5:04p Kevin + * Made msn files have the option to not be playable in multiplayer + * + * 54 8/20/98 10:53a Samir + * check Current_level pointer for validity. + * + * 53 8/18/98 1:10a Samir + * some reorg of scripting and intrasave mission managment. + * + * 52 7/31/98 5:19p Samir + * mission filenames are dynamically allocated now to allow for pathnames + * (since we'd have too many 256 char arrays per level. + * + * 51 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 50 6/16/98 10:54a Jeff + * + * 49 5/25/98 8:19p Samir + * skip telcom if no briefing file and other stuff. + * + * 48 5/21/98 2:32p Samir + * added full support for intra-mission level branching. + * + * 47 5/18/98 3:56p Samir + * added D3_FAST mode to quickly enter and exit games. + * + * 46 5/05/98 5:17p Samir + * took out loading level message. + * + * 45 4/24/98 1:53a Samir + * took care of a lot of scripting memory leaks. + * + * 44 4/21/98 4:15a Samir + * memory stuf. + * + * 43 4/20/98 11:30a Jason + * Added ShowProgressScreen function + * + * 42 4/18/98 7:17a Samir + * Added level objective descriptions to mission file. + * + * 41 4/14/98 7:50p Matt + * Added system to keep info for each level + * + * 40 4/02/98 11:11a Samir + * Error checking for level load/misison init fail. + * + * 39 3/31/98 3:49p Jason + * added memory lib + * + * 38 3/30/98 6:29p Samir + * Load default null script. + * + * 37 2/17/98 6:54p Samir + * clear screen. + * + * 36 2/17/98 6:13p Jeff + * Changed it so TelCom is forced to go into Briefings when called + * + * 35 2/17/98 4:42p Samir + * Current_level is now defined after calling DoLevelIntro. + * + * 34 2/13/98 10:57a Samir + * Changed some gamescript initialization. + * + * 33 2/12/98 5:08p Matt + * Reset cockpit mode when starting a level. Unfortunately, this involved + * some semi-major mucking with game sequencing. + * + * 32 2/08/98 5:01p Samir + * New game sequencing changes. + * + * 31 1/21/98 5:27p Jason + * Don't reposition player object if going from editor to game + * + * 30 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 29 1/20/98 6:01p Jason + * first pass at getting multiplayer deaths working + * + * 28 1/15/98 11:10a Jeff + * Added call to set font color before 'loading level' so its white, also + * turned back on Telcom + * + * 27 1/08/98 12:32p Samir + * Initialize new player ship when quick starting mission. + * + * 26 12/29/97 5:51p Samir + * Use new text system to draw "loading" screen. + * + * 25 12/19/97 12:33p Samir + * Dont call telcom for now. + * + * 24 11/16/97 3:43p Samir + * Briefing filename now defined in mission level structure. + * + * 23 11/10/97 12:37p Samir + * When leaving telcom, restore to cinematics mode for simple 2d message. + * + * 22 10/16/97 4:41p Samir + * Set initial level state to start when quick starting mission. + * + * 21 10/03/97 11:58a Samir + * Added mission name + * + * 20 10/02/97 12:36p Samir + * Redid game sequencing flow. + * + * 19 9/30/97 5:33p Samir + * Added GameSequencer. + * + * 18 9/22/97 5:59p Samir + * Changed ObjCScript system, so everything is broken, but it shouldn't + * crash the game. + * + * 17 9/15/97 6:26p Samir + * Added loading level message. + * + * 16 9/12/97 4:07p Samir + * Simplified movie playing. + * + * 15 9/10/97 4:40p Samir + * Fixed a boo-boo + * + * 14 9/10/97 3:59p Samir + * Inititalize a mini mission when playing from editor, and compile level + * script when playing from editor always. + * + * 13 9/10/97 1:56p Samir + * Added more scripting initialization. + * + * 12 9/05/97 1:21p Samir + * Moved around a bunch of initialization code to InitThisLevel which is + * located in game.cpp. This is called every time you die, start a + * level, etc. + * + * 11 8/21/97 5:57p Samir + * Added some scripti loading/saving calls and took out some useless + * interface stuff + * + * 10 8/15/97 6:33p Samir + * Changes reflecting enhanced editor scripting. + * + * 9 8/12/97 5:31p Jason + * nulled out pointer when quitting mission. + * + * 8 8/12/97 3:22p Samir + * Initialize scripts at start of level. + * + * 7 8/06/97 10:38a Samir + * Fixed call to D3XReallocProgram + * + * 6 8/05/97 9:59a Samir + * Scripting stuff. + * + * 5 7/23/97 3:55p Jeff + * fixed TeleCom* calls to TelCom + * + * 4 7/23/97 3:53p Jeff + * + * 3 7/23/97 3:43p Samir + * Added telecom stuff. + * + * 7 6/11/97 2:20p Samir + * Changed gameos to new system. + * + * 6 6/05/97 12:21p Samir + * Added more mission commands. + * + * 5 5/16/97 6:13p Samir + * Check for EOF in mission file this time. + * + * 4 5/16/97 3:11p Samir + * Added a mission name and mission initializer. + * + * 3 5/15/97 6:58p Samir + * Small briefing changes. + * + * 2 5/15/97 2:09p Samir + * Added a bunch of sequencing from level to level, loading mission. + * + * 1 4/29/97 11:57a Samir + * Initial revision + * + * $NoKeywords: $ + */ +#include "Mission.h" +#include "3d.h" +#include "LoadLevel.h" +#include "pserror.h" +#include "CFILE.H" +#include "gamefont.h" +#include "grdefs.h" +#include "descent.h" +#include "ddio.h" +#include "movie.h" +#include "program.h" +#include "object.h" +#include "objinit.h" +#include "ObjScript.h" +#include "application.h" +#include "TelCom.h" +#include "game.h" +#include "cinematics.h" +#include "player.h" +#include "gamesequence.h" +#include "mem.h" +#include "newui.h" +#include "stringtable.h" +#include "AppConsole.h" +#include "pstring.h" +#include "dedicated_server.h" +#include "osiris_dll.h" +#include "mission_download.h" +#include "manage.h" +#include +#include +#include "ship.h" +#include "BOA.h" +#include "terrain.h" +#include "multi.h" +// --------------------------------------------------------------------------- +// Data +// --------------------------------------------------------------------------- +//Info about the current level +level_info Level_info; +tMission Current_mission; +tLevelNode *Current_level=NULL; +char D3MissionsDir[PSPATHNAME_LEN]; +extern int Times_game_restored; +extern msn_urls Net_msn_URLs; + +// --------------------------------------------------------------------------- +// Function prototypes +// --------------------------------------------------------------------------- +bool InitMissionScript(); +void DoEndMission(); +void DoMissionMovie(char *movie); +void FreeMission(); +// used in load level callback +void LoadLevelCB(const char *chunk, int curlen, int filelen); +// MN3 based mission functions. +// loads the msn file from the mn3 file specified, specifies the hog and table file. +bool mn3_Open(const char *mn3file); +// returns mission information given the mn3 file. +bool mn3_GetInfo(const char *mn3file, tMissionInfo *msn); +// closes the current mn3 file +void mn3_Close(); + + +inline bool IS_MN3_FILE(const char *fname) +{ + char name[PSFILENAME_LEN]; + char ext[PSFILENAME_LEN]; + ddio_SplitPath(fname, NULL, name, ext); + return (strcmpi(ext, ".mn3") == 0) ? true : false; +} + +inline char *MN3_TO_MSN_NAME(const char *mn3name, char *msnname) +{ + char fname[PSFILENAME_LEN]; + ddio_SplitPath(mn3name, NULL, fname, NULL); + + if(stricmp(fname,"d3_2")==0) + { + strcpy(fname,"d3"); + } + strcat(fname, ".msn"); + strcpy(msnname, fname); + return msnname; +} + + +// --------------------------------------------------------------------------- +// Functions +// --------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////// +// High level mission stuff +/////////////////////////////////////////////////////////////////////////////// +void InitMission() +{ + mprintf((0,"In InitMission()\n")); + Current_mission.num_levels = 0; + Current_mission.cur_level = 0; + memset(Current_mission.desc, 0, sizeof(Current_mission.desc)); + memset(Current_mission.name, 0, MSN_NAMELEN); + memset(Current_mission.author, 0, MSN_NAMELEN); + memset(Current_mission.email, 0, MSN_URLLEN); + memset(Current_mission.web, 0, MSN_URLLEN); + Current_mission.hog = NULL; + Current_mission.levels = NULL; + Current_mission.filename = NULL; + Current_mission.game_state_flags = 0; + Current_mission.mn3_handle = 0; +// create add on mission directory + ddio_MakePath(D3MissionsDir, LocalD3Dir, "missions", NULL); + if (!ddio_DirExists(D3MissionsDir)) { + if (!ddio_CreateDir(D3MissionsDir)) { + Error(TXT_MSN_FAILEDCREATEDIR); + } + } + atexit(ResetMission); +} +// reset all states for a mission +void ResetMission() +{ + mprintf((0,"In ResetMission()\n")); + FreeMission(); + Current_mission.num_levels = 0; + Current_mission.cur_level = 0; + memset(Current_mission.desc, 0, sizeof(Current_mission.desc)); + memset(Current_mission.name, 0, MSN_NAMELEN); + memset(Current_mission.author, 0, MSN_NAMELEN); + memset(Current_mission.email, 0, MSN_URLLEN); + memset(Current_mission.web, 0, MSN_URLLEN); + Current_mission.hog = NULL; + Current_mission.levels = NULL; + Current_mission.filename = NULL; + Current_mission.game_state_flags = 0; + + // clear out old URLs from memory + for(int a=0;acur_level = 1; + msn->num_levels = 1; + msn->levels = lvls; + + msn->multiplayable = true; + msn->singleplayable = true; + + memset(lvls, 0, sizeof(tLevelNode) * msn->num_levels); + strncpy(msn->author, "Outrage", MSN_NAMELEN-1); + if(!mode) + { + strcpy(msn->name,"Descent 3: Sol Ascent"); + msn->num_levels = 5; + memset(lvls, 0, sizeof(tLevelNode) * msn->num_levels); +#ifdef DEMO + msn->multiplayable = false; + strcpy(msn->name,"Descent 3: Demo2"); + msn->num_levels = 1; + msn->filename = mem_strdup("d3demo.mn3"); +#else + strcpy(msn->name,"Descent 3: Sol Ascent"); + msn->num_levels = 5; + msn->filename = mem_strdup("d3oem.mn3"); +#endif + //strncpy(lvls[0].name, "Search for Sweitzer", MSN_NAMELEN-1); + lvls[0].flags = LVLFLAG_BRIEFING|LVLFLAG_SCORE; + lvls[0].filename = mem_strdup("level1.d3l"); + lvls[0].briefname = mem_strdup("level1.brf"); + lvls[0].score = mem_strdup("level1.omf"); + lvls[0].progress = mem_strdup("l1load.ogf"); + lvls[0].moviename = mem_strdup("level1.mve"); +#ifndef DEMO + //strncpy(lvls[0].name, "Into the Heart of the Ship", MSN_NAMELEN-1); + lvls[1].flags = LVLFLAG_BRIEFING|LVLFLAG_SCORE; + lvls[1].filename = mem_strdup("level3.d3l"); + lvls[1].briefname = mem_strdup("level2o.brf"); + lvls[1].score = mem_strdup("level3.omf"); + lvls[1].progress = mem_strdup("l3load.ogf"); + //strncpy(lvls[0].name, "The Nomad Caves", MSN_NAMELEN-1); + lvls[2].flags = LVLFLAG_BRIEFING|LVLFLAG_SCORE; + lvls[2].filename = mem_strdup("level6.d3l"); + lvls[2].briefname = mem_strdup("level3o.brf"); + lvls[2].score = mem_strdup("level6.omf"); + lvls[2].progress = mem_strdup("l6load.ogf"); + //strncpy(lvls[0].name, "The Transmode Virus", MSN_NAMELEN-1); + lvls[3].flags = LVLFLAG_BRIEFING|LVLFLAG_SCORE; + lvls[3].filename = mem_strdup("level7.d3l"); + lvls[3].briefname = mem_strdup("level4o.brf"); + lvls[3].score = mem_strdup("level7.omf"); + lvls[3].progress = mem_strdup("l7load.ogf"); + //strncpy(lvls[0].name, "The Rescue", MSN_NAMELEN-1); + lvls[4].flags = LVLFLAG_BRIEFING|LVLFLAG_SCORE; + lvls[4].filename = mem_strdup("level11.d3l"); + lvls[4].briefname = mem_strdup("level5o.brf"); + lvls[4].score = mem_strdup("level11.omf"); + lvls[4].progress = mem_strdup("l11load.ogf"); + mn3_Open("d3oem.mn3"); +#endif + + } + + else if(mode==1) + { + strcpy(msn->name,"Polaris"); + msn->filename = mem_strdup("Polaris.d3l"); + //strncpy(lvls[0].name, "Polaris", MSN_NAMELEN-1); + lvls[0].filename = mem_strdup("polaris.d3l"); + lvls[0].flags |= LVLFLAG_BRANCH; + lvls[0].lvlbranch0 = 1; + lvls[0].progress = mem_strdup("polaris.ogf"); + } + else if(mode==2) + { + strcpy(msn->name,"The Core"); + msn->filename = mem_strdup("TheCore.d3l"); + //strncpy(lvls[0].name, "The Core", MSN_NAMELEN-1); + lvls[0].filename = mem_strdup("thecore.d3l"); + lvls[0].flags |= LVLFLAG_BRANCH; + lvls[0].lvlbranch0 = 1; + lvls[0].progress = mem_strdup("thecore.ogf"); + } + else if(mode==3) + { + strcpy(msn->name,"Taurus"); + msn->filename = mem_strdup("Taurus.d3l"); + //strncpy(lvls[0].name, "Taurus", MSN_NAMELEN-1); + lvls[0].filename = mem_strdup("taurus.d3l"); + lvls[0].flags |= LVLFLAG_BRANCH; + lvls[0].lvlbranch0 = 1; + lvls[0].progress = mem_strdup("taurus.ogf"); + } +#ifndef DEMO + else if(mode==4) + { + strcpy(msn->name,"Pilot Training"); + msn->filename = mem_strdup("training.mn3"); + //strncpy(lvls[0].name, "Training", MSN_NAMELEN-1); + lvls[0].filename = mem_strdup("trainingmission.d3l"); + lvls[0].briefname = NULL;//mem_strdup("training.brf"); + lvls[0].flags = 0; + lvls[0].lvlbranch0 = 0; + lvls[0].progress = mem_strdup("trainingload.ogf"); + mn3_Open("training.mn3"); + } +#endif +// load default script here. + InitMissionScript(); + return true; +} +#endif + +bool LoadMission(const char *mssn) +{ + Times_game_restored = 0; + mprintf((0,"In LoadMission()\n")); +// ShowProgressScreen(TXT_LOADINGLEVEL); +#if (defined(OEM) || defined(DEMO)) +#ifdef OEM + if(strcmpi(mssn,"d3oem.mn3")==0) + return DemoMission(0); +#elif defined(DEMO) + if(strcmpi(mssn,"d3demo.mn3")==0) + return DemoMission(0); +#endif + else if(strcmpi(mssn,"polaris.d3l")==0) + return DemoMission(1); + else if(strcmpi(mssn,"thecore.d3l")==0) + return DemoMission(2); + else if(strcmpi(mssn,"taurus.d3l")==0) + return DemoMission(3); +#ifdef OEM + else if(strcmpi(mssn,"training.mn3")==0) + return DemoMission(4); +#endif +#else + +//#endif + tLevelNode *lvls = NULL; // Temporary storage for level data. + tMission *msn; + CFILE *fp=NULL; // Mission file + char errtext[80]; // Stores error if unable to read mission + char msnfname[PSFILENAME_LEN]; + char mission[_MAX_PATH*2]; + int srclinenum=0; // Current line of source. + int curlvlnum; // Current level number + int numlevels; // Number of levels required to read in. + int cur_objective; // current objective reading. + bool indesc; // are we in a multi-line block + bool res=false; // used to specify if no error has occurred. + char pathname[_MAX_PATH*2]; + ResetMission(); // Reset everything. +// open MN3 if filename passed was an mn3 file. + + //Correct for mission split hack + + if(strcmpi(mssn,"d3_2.mn3")==0) + { +#ifdef MACINTOSH + strcpy(mission, "d3.mn3"); +#else + strcpy(mission, "d3_2.mn3"); +#endif + strcpy(pathname, "d3_2.mn3"); + + } + else if(strcmpi(mssn,"d3.mn3")==0) + { + strcpy(mission, "d3.mn3"); + strcpy(pathname, "d3.mn3"); + + } +#ifdef MACINTOSH + else if(strcmpi(mssn,"training.mn3")==0) + { + strcpy(mission, "training.mn3"); + strcpy(pathname, "training.mn3"); + + } +#endif + else if (IS_MN3_FILE(mssn)) + { + strcpy(mission, mssn); + ddio_MakePath(pathname, D3MissionsDir, mission, NULL); + } + else + { + strcpy(mission, mssn); + strcpy(pathname, mssn); + strcpy(msnfname,mssn); + //ddio_MakePath(pathname, D3MissionsDir, mission, NULL); + } + if (IS_MN3_FILE(mission)) { + + if (!mn3_Open(pathname)) { + strcpy(errtext, TXT_MSN_OPENMN3FAILED); + goto msnfile_error; + } + MN3_TO_MSN_NAME(mission, msnfname); + } +// open mission file + fp = cfopen(msnfname, "rt"); + if (!fp) { + strcpy(errtext, TXT_MISSIONNOTFOUND); + goto msnfile_error; + } + msn = &Current_mission; + +// read in mission file + srclinenum = 1; + numlevels = -1; + curlvlnum = 0; + indesc = 0; + cur_objective = -1; + msn->multiplayable = true; + msn->singleplayable = true; + msn->training_mission = false; + + while (!cfeof(fp)) + { + char srcline[128]; // One line of mission source + char command[32]; // Command read from line. + char operand[96]; // operand read from line + char *keyword; // parsed keyword + int readcount; // read-in count + readcount = cf_ReadString(srcline, sizeof(srcline), fp); + if (readcount) { + // we have a line of source. parse out primary keyword + // then parse out remainder. + keyword = strtok(srcline, " \t"); + CleanupStr(command, srcline, sizeof(command)); + CleanupStr(operand, srcline+strlen(command)+1, sizeof(operand)); + if (strlen(command) && indesc) indesc = 0; + if (!strcmpi(command, "NAME")) { + strncpy(msn->name, operand, MSN_NAMELEN-1); + } + else if (!strcmpi(command, "MULTI")) { + if(strcmpi("no",operand)==0) + msn->multiplayable = false; + } + else if (!strcmpi(command, "SINGLE")) { + if(strcmpi("no",operand)==0) + msn->singleplayable = false; + } + else if (!strcmpi(command, "TRAINER")) { + if (curlvlnum == 0) { + msn->training_mission = true; + } + else { + strcpy(errtext, TXT_MSN_MSNCOMMAND); + goto msnfile_error; + } + } + else if (!strcmpi(command, "AUTHOR")) { + strncpy(msn->author, operand, MSN_NAMELEN-1); + } + else if (!strcmpi(command, "KEYWORDS")) { + //Don't do anything with this + } + else if (!strcmpi(command, "DESCRIPTION") || indesc) { + // multi-line descriptions require the strcat. the initial + // strings should be empty for this to work. + strcat(msn->desc, operand); + if (indesc) strcat(msn->desc, "\n"); + indesc = 1; // this is a multiline command + } + else if (!strcmpi(command, "URL")) { + if (curlvlnum != 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else + { + for(int a=0;aemail, operand, MSN_URLLEN-1); + } + else if (!strcmpi(command, "SCORE")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + lvls[curlvlnum-1].flags |= LVLFLAG_SCORE; + lvls[curlvlnum-1].score = mem_strdup(operand); + if (!lvls[curlvlnum-1].score) goto fatal_error; + } + } + else if (!strcmpi(command, "PROGRESS")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + lvls[curlvlnum-1].progress = mem_strdup(operand); + if (!lvls[curlvlnum-1].progress) goto fatal_error; + } + } + else if (!strcmpi(command, "HOG")) { + if (curlvlnum == 0) { + msn->hog = mem_strdup(operand); + if (!msn->hog) goto fatal_error; + } + else { + lvls[curlvlnum-1].flags |= LVLFLAG_SPECIALHOG; + if (!(lvls[curlvlnum-1].hog = mem_strdup(operand))) goto fatal_error; + } + } + else if (!strcmpi(command, "NUMLEVELS")) { + if (curlvlnum != 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + // get number of levels + int value = atoi(operand); + if (value == 0) { + strcpy(errtext, TXT_MSN_LVLNUMINVALID); + goto msnfile_error; + } + lvls = (tLevelNode *)mem_malloc(sizeof(tLevelNode) * value); + memset(lvls, 0, sizeof(tLevelNode)*value); + numlevels = value; + } + } + else if (!strcmpi(command, "LEVEL")) { + // first check if number of level is greater than num_levels + if ((curlvlnum == numlevels) && (numlevels != -1)) { + strcpy(errtext, TXT_MSN_NUMLVLSINVALID); + goto msnfile_error; + } + curlvlnum = atoi(operand); + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLNUMINVALID); + goto msnfile_error; + } + else if (curlvlnum > numlevels || curlvlnum < 0) { + strcpy(errtext, TXT_MSN_LVLNUMINVALID); + goto msnfile_error; + } + } + else if (!strcmpi(command, "INTROMOVIE")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + lvls[curlvlnum-1].flags |= LVLFLAG_STARTMOVIE; + if (!(lvls[curlvlnum-1].moviename = mem_strdup(operand))) goto fatal_error; + } + } + else if (!strcmpi(command, "INTRODEFAULT")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else if(!Descent_overrided_intro) + { + lvls[curlvlnum-1].flags |= LVLFLAG_STARTMOVIE; + if (!(lvls[curlvlnum-1].moviename = mem_strdup(operand))) goto fatal_error; + } + } + else if (!strcmpi(command, "ENDMOVIE")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + lvls[curlvlnum-1].flags |= LVLFLAG_ENDMOVIE; + if (!(lvls[curlvlnum-1].endmovie = mem_strdup(operand))) goto fatal_error; + } + } + else if (!strcmpi(command, "MINE")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + if (!(lvls[curlvlnum-1].filename = mem_strdup(operand))) goto fatal_error; + } + } + else if (!strcmpi(command, "SECRET")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + lvls[curlvlnum-1].flags |= LVLFLAG_SPAWNSECRET; + lvls[curlvlnum-1].secretlvl = atoi(operand); + } + } + else if (!strcmpi(command, "BRIEFING")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + lvls[curlvlnum-1].flags |= LVLFLAG_BRIEFING; + if (!(lvls[curlvlnum-1].briefname = mem_strdup(operand))) goto fatal_error; + } + } + else if (!strcmpi(command, "BRANCH")) { + // first check if number of level is greater than num_levels + int lvlnum; + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + lvlnum = atoi(operand); + if (lvlnum == 0 || lvlnum > numlevels) { + strcpy(errtext, TXT_MSN_LVLNUMINVALID); + goto msnfile_error; + } + lvls[curlvlnum-1].flags |= LVLFLAG_BRANCH; + lvls[curlvlnum-1].lvlbranch0 = lvlnum; + } + else if (!strcmpi(command, "ENDMISSION")) { + if (curlvlnum == 0) { + strcpy(errtext, TXT_MSN_LVLCOMMAND); + goto msnfile_error; + } + else { + lvls[curlvlnum-1].flags |= LVLFLAG_FINAL; + } + } + else { + sprintf(errtext, TXT_MSN_ILLEGALCMD, command); + goto msnfile_error; + } + } + srclinenum++; + } +// set up current mission (movies are already set above) + msn->cur_level = 1; + msn->num_levels = numlevels; + msn->levels = lvls; + msn->filename = mem_strdup((char *)mission); + msn->game_state_flags = 0; + strcpy(Net_msn_URLs.msnname,mission); + res = true; // everthing is ok. +// if error, print it out, else end. +msnfile_error: + if (!res) { + char str_text[128]; + sprintf(str_text, "%s\nline %d.", errtext, srclinenum); + if(!Dedicated_server) + { + DoMessageBox(TXT_ERROR, str_text, MSGBOX_OK); + } + else + { + PrintDedicatedMessage("%s: %s\n",TXT_ERROR,str_text); + } + if (lvls) mem_free(lvls); + } + if (fp) cfclose(fp); +// load default script here. + if (!InitMissionScript()) { + return false; + } + return res; +fatal_error: + mem_error(); + return false; +#endif + return false; +} +void FreeMission() +{ +// Free up mission script + int i; //,j; + // close MN3 file if there is one. + mn3_Close(); + // Tell Osiris to shutdown the Osiris Mission Memory System, freeing all memory + Osiris_CloseOMMS(); + if (Current_mission.levels) { + // free up any data allocated per level node. + for (i = 0; i < Current_mission.num_levels; i++) + { + if (Current_mission.levels[i].filename) + mem_free(Current_mission.levels[i].filename); + if (Current_mission.levels[i].briefname) + mem_free(Current_mission.levels[i].briefname); + if (Current_mission.levels[i].hog) + mem_free(Current_mission.levels[i].hog); + if (Current_mission.levels[i].moviename) + mem_free(Current_mission.levels[i].moviename); + if (Current_mission.levels[i].endmovie) + mem_free(Current_mission.levels[i].endmovie); + if (Current_mission.levels[i].score) + mem_free(Current_mission.levels[i].score); + if (Current_mission.levels[i].progress) + mem_free(Current_mission.levels[i].progress); + } + mem_free(Current_mission.levels); + Current_mission.levels = NULL; + } +//@@ if (Current_mission.d3xmod) { +//@@ D3XFreeProgram(Current_mission.d3xmod); +//@@ Current_mission.d3xmod = NULL; +//@@ } + if (Current_mission.hog) + mem_free(Current_mission.hog); + if (Current_mission.filename) { + //these DON't USE mem_free since we use _strdup, which doesn't use our memory routines. + mem_free(Current_mission.filename); + Current_mission.filename = NULL; + } + Current_mission.hog = NULL; + Current_level = NULL; +} +#include "localization.h" +#include "levelgoal.h" +//Load the text (goal strings) for a level +void LoadLevelText(char *level_filename) +{ + char pathname[_MAX_FNAME],filename[_MAX_FNAME]; + int n_strings; + ddio_SplitPath(level_filename,pathname,filename,NULL); + strcat(pathname,filename); + strcat(pathname,".str"); + char **goal_strings; + if (CreateStringTable(pathname,&goal_strings,&n_strings)) { + int n_goals = Level_goals.GetNumGoals(); + ASSERT(n_strings == (n_goals * 3)); + for (int i=0;i1.0f) + { + percent = 1.0f; + } + else if(percent<0.0f) + { + percent = 0.0f; + } + if((!Progress_screen_loaded)&&(step!=LOAD_PROGRESS_START)) + return; + switch(step) + { + case LOAD_PROGRESS_START: + { + lvl_percent_loaded = 0.0f; + pag_percent_loaded = 0.0f; + dedicated_last_string_len = -1; + char *p = NULL; + if( (!(Game_mode & GM_MULTI)) && (!Current_mission.levels[Current_mission.cur_level-1].progress) ) + { + p = "tunnelload.ogf"; + } + else + { + p = Current_mission.levels[Current_mission.cur_level-1].progress; + } + + if(p) + { + if (LoadLargeBitmap(IGNORE_TABLE(p), &level_bmp)) + { + SetScreenMode(SM_MENU); + level_bmp_loaded = true; + n_text_msgs = 0; + } + } + /* + else + { + ShowProgressScreen (TXT_LOADINGLEVEL); + } + */ + Progress_screen_loaded = true; + return; + } + break; + case LOAD_PROGRESS_LOADING_LEVEL: + { + lvl_percent_loaded = percent; + } + break; + case LOAD_PROGRESS_PAGING_DATA: + { + lvl_percent_loaded = 1.0f; + pag_percent_loaded = percent; + } + break; + case LOAD_PROGRESS_PREPARE: + if (Dedicated_server) + { + PrintDedicatedMessage("\n"); + } + lvl_percent_loaded = 1.0f; + pag_percent_loaded = 1.0f; + mprintf((0,"Prepare for Descent goes here...\n")); + //ShowProgressScreen(TXT_PREPARE_FOR_DESCENT,NULL,true); + //return; + break; + case LOAD_PROGRESS_DONE: + { + if (level_bmp_loaded) + { + // print out final message + FreeLargeBitmap(&level_bmp); + level_bmp_loaded = false; + } + Progress_screen_loaded = false; + if (Dedicated_server) + { + PrintDedicatedMessage("\n"); + } + return; + } + break; + default: + mprintf((0,"Unknown step in LoadLevelProgress()\n")); + Int3(); + } + if (Dedicated_server) + { + char tbuffer[512]; + char tbuffer1[512]; + nw_DoNetworkIdle(); + if(dedicated_last_string_len!=-1) + { + memset(tbuffer,'\b',dedicated_last_string_len); + tbuffer[dedicated_last_string_len] = '\0'; + PrintDedicatedMessage(tbuffer); + } + char levelname[100]; + sprintf(levelname,"%s level %d",Current_mission.name,Current_mission.cur_level); + if(lvl_percent_loaded<1.0f) + { + sprintf(tbuffer1,TXT_DS_LEVELLOADSTATUS,levelname,lvl_percent_loaded*100.0f); + sprintf(tbuffer,"%s%s",TXT_LOADINGLEVEL,tbuffer1); + PrintDedicatedMessage(tbuffer); + dedicated_last_string_len = strlen(tbuffer); + } + else + { + sprintf(tbuffer1,TXT_DS_LEVELLOADSTATUS,levelname,pag_percent_loaded*100.0f); + sprintf(tbuffer,"%s%s",TXT_LL_PAGINGDATA,tbuffer1); + PrintDedicatedMessage(tbuffer); + dedicated_last_string_len = strlen(tbuffer); + } + return; + } + StartFrame(0,0,Max_window_w, Max_window_h); + // do background. + if (!level_bmp_loaded) + { + int text_height; + ASSERT(Current_level); + rend_ClearScreen(GR_BLACK); + grtext_SetFont(MENU_FONT); + text_height=grfont_GetHeight(MENU_FONT); + grtext_SetColor(GR_WHITE); + grtext_CenteredPrintf(0, Max_window_h/2, TXT_LOADINGLEVEL); + //grtext_CenteredPrintf(0, (Max_window_h/2)+(text_height*2), Current_level->name); + } + else + { +#ifdef STEALTH //DAJ + rend_ClearScreen(GR_BLACK); +#else + DrawLargeBitmap(&level_bmp,0,0,1.0f); +#endif + // do relevent text. + str[0] = 0; + if (chunk) { + if (strncmp(CHUNK_TERRAIN, chunk, strlen(CHUNK_TERRAIN))==0) + strcpy(str, TXT_LL_TERRAIN); + else if (strncmp(CHUNK_SCRIPT_CODE, chunk, strlen(CHUNK_SCRIPT_CODE))==0) + strcpy(str, TXT_LL_SCRIPTLOADED); + else if (strncmp(CHUNK_ROOMS, chunk, strlen(CHUNK_ROOMS))==0) + strcpy(str, TXT_LL_ROOMSLOADED); + else if (strncmp(CHUNK_OBJECTS, chunk, strlen(CHUNK_OBJECTS))==0) + strcpy(str, TXT_LL_OBJECTSLOADED); + } + if (str[0]) { + if (n_text_msgs == N_LOAD_MSGS) { + for (i = 1; i < n_text_msgs; i++) + { + strcpy(text_msgs[i-1], text_msgs[i]); + } + } + else { + n_text_msgs++; + } + strcpy(text_msgs[n_text_msgs-1], str); + } + grtext_SetFont(BRIEFING_FONT); + grtext_SetColor(GR_WHITE); + grtext_SetAlpha(230); + for (i =0 ; i < n_text_msgs; i++) + { + grtext_Printf(14, 32+(i*15), text_msgs[i]); + } + } + g3Point *pntlist[4],points[4]; + points[0].p3_sx=LOADBAR_X; + points[0].p3_sy=LOADBAR_Y1; + points[1].p3_sx=LOADBAR_X+LOADBAR_W; + points[1].p3_sy=LOADBAR_Y1; + points[2].p3_sx=LOADBAR_X+LOADBAR_W; + points[2].p3_sy=LOADBAR_Y1+LOADBAR_H; + points[3].p3_sx=LOADBAR_X; + points[3].p3_sy=LOADBAR_Y1+LOADBAR_H; + for (i=0;i<4;i++) + { + points[i].p3_z=0; + points[i].p3_flags=PF_PROJECTED; + pntlist[i]=&points[i]; + } + rend_SetZBufferState(0); + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetLighting (LS_NONE); + rend_SetAlphaValue(230); + if (!level_bmp_loaded) + { + rend_SetFlatColor (GR_RGB(192,192,192)); + rend_DrawPolygon2D( 0, pntlist, 4 ); + } + + pntlist[1]->p3_sx = LOADBAR_X+((float)LOADBAR_W*lvl_percent_loaded); + pntlist[2]->p3_sx = LOADBAR_X+((float)LOADBAR_W*lvl_percent_loaded); + + rend_SetFlatColor (GR_RGB(166,7,7)); + rend_DrawPolygon2D( 0, pntlist, 4 ); + + grtext_SetFont(BRIEFING_FONT); + grtext_SetColor(GR_WHITE); + if(lvl_percent_loaded!=1.0) + { + strcpy(str, TXT_LOADINGLEVEL); + } + else + { + strcpy(str, TXT_DONE); + } + grtext_Printf(LOADBAR_X+(LOADBAR_W - grtext_GetLineWidth(str))/2, LOADBAR_Y1+(LOADBAR_H - grtext_GetHeight(str))/2,str); + points[0].p3_sx=LOADBAR_X; + points[0].p3_sy=LOADBAR_Y2; + points[1].p3_sx=LOADBAR_X+LOADBAR_W; + points[1].p3_sy=LOADBAR_Y2; + points[2].p3_sx=LOADBAR_X+LOADBAR_W; + points[2].p3_sy=LOADBAR_Y2+LOADBAR_H; + points[3].p3_sx=LOADBAR_X; + points[3].p3_sy=LOADBAR_Y2+LOADBAR_H; + for (i=0;i<4;i++) + { + points[i].p3_z=0; + points[i].p3_flags=PF_PROJECTED; + pntlist[i]=&points[i]; + } + rend_SetZBufferState(0); + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetLighting (LS_NONE); + rend_SetAlphaValue(230); + if (!level_bmp_loaded) + { + rend_SetFlatColor (GR_RGB(192,192,192)); + rend_DrawPolygon2D( 0, pntlist, 4 ); + } + + pntlist[1]->p3_sx = LOADBAR_X+((float)LOADBAR_W*pag_percent_loaded); + pntlist[2]->p3_sx = LOADBAR_X+((float)LOADBAR_W*pag_percent_loaded); + + rend_SetFlatColor (GR_RGB(166,7,7)); + rend_DrawPolygon2D( 0, pntlist, 4 ); + + grtext_SetFont(BRIEFING_FONT); + grtext_SetColor(GR_WHITE); + if(pag_percent_loaded==1.0) + { + strcpy(str, TXT_DONE); + } + else + { + strcpy(str, TXT_LL_PAGINGDATA); + } + + if(pag_percent_loaded>0.0) + grtext_Printf(LOADBAR_X+(LOADBAR_W - grtext_GetLineWidth(str))/2, LOADBAR_Y2+(LOADBAR_H - grtext_GetHeight(str))/2,str); + + //Display data errors + extern int Data_error_count; + if (Data_error_count) { + char buf[1024]; + sprintf(buf,"This mission has %d data errors",Data_error_count); + int y = 200+40; + int x = 320-(grtext_GetLineWidth(buf)/2); + grtext_Printf(x,y,buf); + } + + if(step==LOAD_PROGRESS_PREPARE) + { + grtext_SetFont(BIG_BRIEFING_FONT); + grtext_SetColor(GR_WHITE); + + int preparey = 200; + int preparex = 320-(grtext_GetLineWidth(TXT_PREPARE_FOR_DESCENT)/2); + if(preparex<0) + preparex = 0; + grtext_Printf(preparex,preparey,TXT_PREPARE_FOR_DESCENT); + } + // Display level name if still loading + if((pag_percent_loaded>0.0) && step!=LOAD_PROGRESS_PREPARE) + { + char str[255]; + + sprintf (str,"\"%s\"",Level_info.name); + grtext_SetFont(BIG_BRIEFING_FONT); + grtext_SetColor(GR_WHITE); + + int preparey = 200; + int preparex = 320-(grtext_GetLineWidth(str)/2); + if(preparex<0) + preparex = 0; + grtext_Printf(preparex,preparey,str); + } + grtext_Flush(); + EndFrame(); + rend_Flip(); +} +/* this functions performs the end mission code +*/ +void DoEndMission() +{ + if (Game_mode & GM_MULTI) // If multiplayer, just loop + { + if (Dedicated_server) + PrintDedicatedMessage (TXT_DS_MISSIONDONE); + SetCurrentLevel (1); + return; + } +} +// Shows some text on a background, useful for telling the player what is going on +// ie "Loading level...", "Receiving data...", etc +void ShowProgressScreen (char *str,char *str2,bool flip) +{ + if (Dedicated_server) + { + PrintDedicatedMessage ("%s\n",str); + if (str2) + PrintDedicatedMessage ("%s\n",str2); + return; + } + StartFrame(0,0,Max_window_w, Max_window_h); + rend_ClearScreen(GR_BLACK); + grtext_SetFont(MENU_FONT); + int text_height=grfont_GetHeight(MENU_FONT); + grtext_SetColor(GR_WHITE); + grtext_CenteredPrintf(0, Max_window_h/2, str); + if (str2) + grtext_CenteredPrintf(0, (Max_window_h/2)+(text_height*2), str2); + grtext_Flush(); + EndFrame(); + if (flip) + rend_Flip(); + +} +/* does a mission briefing, returns if mission was canceled, a false, or 0 value. + first displays mission goals and some warnings or advice. + may allow for selection of player ships +*/ +bool DoMissionBriefing(int level) +{ + tLevelNode *lvl = Current_level; + bool ret = true; + int num_ships = 0; + for(int i=0;ibriefname[0] || num_ships>1) + ret = !TelComShow(TS_MISSION,false,true); + return ret; +} +extern bool FirstGame; +bool Skip_next_movie = false; +// --------------------------------------------------------------------------- +// play movie +void DoMissionMovie(char *movie) +{ + char temppath[PSPATHNAME_LEN+PSFILENAME_LEN]; + if (PROGRAM(windowed)) { + mprintf((0, "Skipping movie...can't do in windowed mode!\n")); + return; + } + //Don't play this movie the first time through. This is a horrible hack. + if(Skip_next_movie) + { + Skip_next_movie = false; + return; + } +#ifdef D3_FAST + return; +#endif + if(movie && *movie) + { + char *moviepath; + + if(Current_mission.filename && !(!stricmp(Current_mission.filename,"d3.mn3")||!stricmp(Current_mission.filename,"d3_2.mn3"))) + { + char mpath[_MAX_PATH]; + ddio_MakePath(mpath,LocalD3Dir,"movies",movie,NULL); + PlayMovie(mpath); + }else + { + moviepath = GetMultiCDPath(movie); + if(moviepath) + { + strcpy(temppath,moviepath); + PlayMovie(temppath); + } + } + } + //PlayMovie(movie); +} +/////////////////////////////////////////////////////////////////////////////// +// Script Management for Missions and Levels +/////////////////////////////////////////////////////////////////////////////// +// Initializes a mission's default script +bool InitMissionScript() +{ +// need to do this so when UI runs, it has a background. +// SetUICallback(DEFAULT_UICALLBACK); +// if there is no default script, we will load a null script, which means that whatever object +// uses scripts, will not do anything. +//@@ fp = cfopen(DEFAULT_SCROBJ_NAME, "rb"); +//@@ if (!fp) { +//@@ DoMessageBox("Error", "Unable to find mission script.", MSGBOX_OK, UICOL_WINDOW_TITLE, UICOL_TEXT_NORMAL); +//@@ mprintf((0, "Unable to open default script. Loading null script...\n")); +//@@ return false; +//@@ } +//@@ else { +//@@ Current_mission.d3xmod = D3XLoadProgram(fp); +//@@ if (!Current_mission.d3xmod) { +//@@ DoMessageBox("Error", "Unable to find mission script.", MSGBOX_OK, UICOL_WINDOW_TITLE, UICOL_TEXT_NORMAL); +//@@ mprintf((0, "Unable to open default script. Loading null script...\n")); +//@@ return false; +//@@ } +//@@ cfclose(fp); +//@@ } + return true; +} +extern bool IsRestoredGame; +void InitLevelScript() +{ + if(Current_level->filename){ + char filename[_MAX_PATH],ext[_MAX_EXT]; + ddio_SplitPath(Current_level->filename,NULL,filename,ext); + #if defined (WIN32) + strcat(filename,".dll"); + #elif defined (MACINTOSH) + strcat(filename,".msl"); + #else + #if defined(MACOSX) + strcat(filename,".dylib"); + #else + strcat(filename,".so"); + #endif + #endif + Osiris_LoadLevelModule(filename); + } + //@$-D3XExecScript(Current_level->d3xthread, Current_mission.cur_level, REF_LEVELTYPE, EVT_LEVELSTART, 0, 0); + tOSIRISEventInfo ei; + //This is a hack... we don't want the level start script to be called when restoring a save game + if(!IsRestoredGame) + Osiris_CallLevelEvent(EVT_LEVELSTART,&ei); + AssignScriptsForLevel(); // initialize all scripts for level. +} +void FreeLevelScript() +{ + Osiris_UnloadLevelModule(); + if (Current_level) { + // free level's script and thread + //@$-D3XExecScript(Current_level->d3xthread, Current_mission.cur_level, REF_LEVELTYPE, EVT_LEVELEND, 0, 0); + tOSIRISEventInfo ei; + Osiris_CallLevelEvent(EVT_LEVELEND,&ei); + } +} +// return information about a mission +bool GetMissionInfo(const char *msnfile, tMissionInfo *msn) +{ + CFILE *fp; + bool indesc=false; // are we in a multi-line block + // open mission file + if (IS_MN3_FILE(msnfile)) { + return mn3_GetInfo(msnfile, msn); + } + fp = cfopen(msnfile, "rt"); + if (!fp) { + mprintf((0, "Failed to open mission file %s in GetMissionInfo.\n", msnfile)); + return false; + } + msn->multi = true; + msn->single = true; + msn->training = false; + msn->author[0] = 0; + msn->desc[0] = 0; + while (!cfeof(fp)) + { + char srcline[128]; // One line of mission source + char command[32]; // Command read from line. + char operand[96]; // operand read from line + char *keyword; // parsed keyword + int readcount; // read-in count + readcount = cf_ReadString(srcline, sizeof(srcline), fp); + if (readcount) { + // we have a line of source. parse out primary keyword + // then parse out remainder. + keyword = strtok(srcline, " \t"); + CleanupStr(command, srcline, sizeof(command)); + CleanupStr(operand, srcline+strlen(command)+1, sizeof(operand)); + if (strlen(command) && indesc) + indesc = false; + if (!strcmpi(command, "NAME")) { + strncpy(msn->name, operand, MSN_NAMELEN-1); + } + else if (!strcmpi(command, "MULTI")) { + if(strcmpi("no",operand)==0) + msn->multi = false; + } + else if (!strcmpi(command, "SINGLE")) { + if(strcmpi("no",operand)==0) + msn->single = false; + } + else if (!strcmpi(command, "TRAINER")) { + msn->training = true; + } + else if (!strcmpi(command, "AUTHOR")) { + strncpy(msn->author, operand, MSN_NAMELEN-1); + } + else if (!strcmpi(command, "DESCRIPTION") || indesc) { + // multi-line descriptions require the strcat. the initial + // strings should be empty for this to work. + strcat(msn->desc, operand); + if (indesc) + strcat(msn->desc, "\n"); + indesc = true; // this is a multiline command + } + else if (!strcmpi(command, "NUMLEVELS")) { + // get number of levels + int value = atoi(operand); + msn->n_levels = value; + } + else if (!strcmpi(command, "LEVEL")) { + break; + } + else if (!strcmpi(command, "KEYWORDS")) { + //Read in all the keywords + strncpy(msn->keywords,operand,MAX_KEYWORDLEN); + } + } + } + cfclose(fp); + return true; +} +// --------------------------------------------------------------------------- +char * GetMissionName(char *mission) +{ + tMissionInfo msninfo; + static char msnname[MSN_NAMELEN]; + msnname[0] = 0; + if (GetMissionInfo(mission, &msninfo)) { + strcpy(msnname, msninfo.name); + } + else { + mprintf((0, "MISSION:GetMissionName failed from call to GetMissionInfo\n")); + } + return msnname; +} +bool IsMissionMultiPlayable(char *mission) +{ + tMissionInfo msninfo; + if (GetMissionInfo(mission, &msninfo)) { + return msninfo.multi; + } + return false; +} +bool IsMissionSinglePlayable(char *mission) +{ + tMissionInfo msninfo; + if (GetMissionInfo(mission, &msninfo)) { + return msninfo.single; + } + return false; +} +int Mission_voice_hog_handle = 0; +// MN3 based mission functions. +// loads the msn file from the mn3 file specified, specifies the hog and table file. +bool mn3_Open(const char *mn3file) +{ + char pathname[PSPATHNAME_LEN+PSFILENAME_LEN]; + char filename[PSFILENAME_LEN]; + char ext[PSFILENAME_LEN]; + int mn3_handle; +// concatanate the mn3 extension if it isn't there. + if (!IS_MN3_FILE(mn3file)) { + strcat(filename, ".mn3"); + } + + char *p = GetMultiCDPath((char *)mn3file); + //ddio_MakePath(pathname, D3MissionsDir, mn3file, NULL); + if(!p) + return false; + strcpy(pathname,p); +// open MN3 HOG. + mn3_handle = cf_OpenLibrary(pathname); + if (mn3_handle == 0) { + return false; + } + else + { + Osiris_ExtractScriptsFromHog(mn3_handle,true); + } +// do table file stuff. + ddio_SplitPath(mn3file, NULL, filename, ext); + +// char voice_hog[_MAX_PATH*2]; + if( (strcmpi(filename,"d3")==0) || (strcmpi(filename,"training")==0) ) + { + //Open audio hog file + //ddio_MakePath(voice_hog, D3MissionsDir, "d3voice1.hog", NULL);//Audio for levels 1-4 + char *v = GetMultiCDPath("d3voice1.hog"); + Mission_voice_hog_handle = cf_OpenLibrary(v); + } + else if(strcmpi(filename,"d3_2")==0) + { + //Open audio hog file + //ddio_MakePath(voice_hog, D3MissionsDir, "d3voice2.hog", NULL);//Audio for levels 5-17 + char *v = GetMultiCDPath("d3voice2.hog"); + Mission_voice_hog_handle = cf_OpenLibrary(v); + } + strcat(filename, ".gam"); + mng_SetAddonTable(filename); + Current_mission.mn3_handle = mn3_handle; + return true; +} +// returns mission information given the mn3 file. +bool mn3_GetInfo(const char *mn3file, tMissionInfo *msn) +{ + int handle; + bool retval; + char pathname[PSPATHNAME_LEN+PSFILENAME_LEN]; + char filename[PSFILENAME_LEN]; + + + if(strcmpi(mn3file,"d3.mn3")==0) + { + char *p = GetMultiCDPath((char *)mn3file); + if(!p) + return false; + strcpy(pathname,p); + } + else + { + ddio_MakePath(pathname, D3MissionsDir, mn3file, NULL); + } + handle = cf_OpenLibrary(pathname); + if (handle == 0) { + mprintf((0, "MISSION: MN3 failed to open.\n")); + return false; + } + MN3_TO_MSN_NAME(mn3file, filename); + retval = GetMissionInfo(filename, msn); + cf_CloseLibrary(handle); + return retval; +} +// closes the current mn3 file +void mn3_Close() +{ + if (Current_mission.mn3_handle) { + Osiris_ClearExtractedScripts(true); + cf_CloseLibrary(Current_mission.mn3_handle); + + } + if(Mission_voice_hog_handle) + { + cf_CloseLibrary(Mission_voice_hog_handle); + Mission_voice_hog_handle = 0; + } + Current_mission.mn3_handle = 0; +} +#define KEYWORD_LEN 16 +#define NUM_KEYWORDS 16 +#define GOALSTEXT "GOALS" +#define GOALSTEXTLEN strlen(GOALSTEXT) +#define MODMINGOALS "MINGOALS" +#define MODMINGOALSLEN strlen(MODMINGOALS) +//Returns the max number of teams this mission can support for this mod, or +//-1 if this level shouldn't be played with this mission +//Return values: +// -1 Bad match -- this level and this mod shouldn't be played together! +// MAX_NET_PLAYERS -- This is playable with any number of teams the mod wants +int MissionGetKeywords(char *mission,char *keywords) +{ + ASSERT(mission); + ASSERT(keywords); + + char msn_keywords[NUM_KEYWORDS][KEYWORD_LEN]; + char mod_keywords[NUM_KEYWORDS][KEYWORD_LEN]; + int i; + char *parse_keys = mem_strdup(keywords); + char seps[] = ","; + int teams = MAX_NET_PLAYERS; + int goals = 0; + int goalsneeded = 0; + int mod_key_count = 0; + int msn_key_count = 0; + bool goal_per_team = false; + tMissionInfo msn_info; + + memset(msn_keywords,0,sizeof(msn_keywords)); + memset(mod_keywords,0,sizeof(mod_keywords)); + mprintf((0,"MissionGetKeywords(%s,%s)\n",mission,keywords)); + if(!GetMissionInfo(mission,&msn_info)) + { + return -1; + } + + if(!*parse_keys) + { + return MAX_NET_PLAYERS; + } + //Break up the mod keywords into an array + char *tokp = strtok(parse_keys,seps); + if(tokp) + { + do + { + strcpy(mod_keywords[mod_key_count],tokp); + mod_key_count++; + tokp = strtok(NULL,seps); + }while((tokp)&&(mod_key_countgame to load all necessary elements for level playing for systems +// not initialized in editor, but only in game. +extern char Editor_quickplay_levelname[_MAX_PATH]; +void QuickStartMission() +{ +// create the level script code here, if we really need to. + ResetGamemode(); +// this initializes a mini one level mission with no frills. + Current_mission.cur_level = 1; + Current_mission.num_levels = 1; + Current_mission.levels = (tLevelNode *)mem_malloc(sizeof(tLevelNode)); + memset(Current_mission.levels, 0, sizeof(tLevelNode)); + Current_level = Current_mission.levels; + if(Editor_quickplay_levelname[0]!='\0') + Current_level->filename = mem_strdup(Editor_quickplay_levelname); + else + Current_level->filename = NULL; +// proceed with initialization. + InitMissionScript(); + SetGameState(GAMESTATE_LVLSTART); +} +// Used by game->editor to free up all extra game mission elements not needed in editor. +void QuickEndMission() +{ +//@@ FreeScriptsForLevel(); +//@@ D3XFreeProgram(Current_mission.d3xmod); +//@@ Current_mission.d3xmod = NULL; +// free up mini mission. +//@@ Current_level = NULL; +//@@ mem_free(Current_mission.levels); +//@@ Current_mission.levels = NULL; + ResetMission(); +} +#endif diff --git a/Descent3/Mission.h b/Descent3/Mission.h new file mode 100644 index 000000000..d506ea503 --- /dev/null +++ b/Descent3/Mission.h @@ -0,0 +1,341 @@ +/* + * $Logfile: /DescentIII/main/Mission.h $ + * $Revision: 41 $ + * $Date: 4/23/99 3:33p $ + * $Author: Kevin $ + * + * Mission level sequencing and management. + * + * $Log: /DescentIII/main/Mission.h $ + * + * 41 4/23/99 3:33p Kevin + * mission file/multiplayer mod keyword system + * + * 40 4/22/99 6:21p Kevin + * mission keywords + * + * 39 4/08/99 3:13p Matt + * Finished cleaning up level sequencing code. Got rid of all the "level + * minus one" stuff. + * + * 38 4/03/99 5:23p Matt + * Removed name, author, & description from the mission level structure. + * + * 37 4/03/99 5:05p Samir + * added ENDMISSION keyword to end a mission after a certain level. + * + * 36 3/24/99 1:23p Kevin + * Support for level specific progress screens. + * + * 35 2/10/99 4:56p Kevin + * Better progress indicator & prepare for Descent message + * + * 34 1/20/99 4:20p Samir + * finished secret level implementation. + * + * 33 1/19/99 1:15p Samir + * add on missions. + * + * 32 1/13/99 12:43p Jason + * fixed flickering exit menu screen + * + * 31 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 30 1/05/99 10:59a Samir + * readded level objective flags. + * + * 29 1/04/99 5:44p Samir + * added game state flags. + * + * 28 12/16/98 3:24p Samir + * new way to get info on a mission (used to determine training mission + * too.) + * + * 27 12/16/98 1:57p Samir + * Replaced CleanupString2 with CleanupStr + * + * 26 12/03/98 12:51p Samir + * music score specified per level. + * + * 25 10/18/98 2:58p Jason + * fixes for beta4 + * + * 24 10/13/98 3:48p Samir + * added error checking if script failed to load (default). + * + * 23 10/08/98 7:29p Samir + * revamped sequencing. + * + * 22 10/02/98 5:46p Samir + * took out mission file objective text and replaced with level goals. + * + * 21 8/24/98 5:04p Kevin + * Made msn files have the option to not be playable in multiplayer + * + * 20 8/18/98 1:10a Samir + * some reorg of scripting and intrasave mission managment. + * + * 19 7/31/98 5:19p Samir + * mission filenames are dynamically allocated now to allow for pathnames + * (since we'd have too many 256 char arrays per level. + * + * 18 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 17 5/21/98 2:32p Samir + * added full support for intra-mission level branching. + * + * 16 4/20/98 11:30a Jason + * Added ShowProgressScreen function + * + * 15 4/18/98 7:17a Samir + * Added level objective descriptions to mission file. + * + * 14 4/14/98 7:50p Matt + * Added system to keep info for each level + * + * 13 4/02/98 11:11a Samir + * Error checking for level load/misison init fail. + * + * 12 3/13/98 12:09p Samir + * Added InitMissionScript function header. + * + * 11 2/08/98 5:01p Samir + * New game sequencing changes. + * + * 10 11/12/97 2:47p Samir + * Added mission briefing name. + * + * 9 10/03/97 11:58a Samir + * Added mission name + * + * 8 10/02/97 12:36p Samir + * Redid game sequencing flow. + * + * 7 9/30/97 5:33p Samir + * Added GameSequencer. + * + * 6 9/24/97 2:58p Samir + * Moved some script defines from mission.h to ObjScript.h + * + * 5 9/10/97 1:59p Samir + * Added D3XProgram var to level structure. + * + * 4 8/15/97 6:33p Samir + * Changes reflecting enhanced editor scripting. + * + * 3 8/12/97 3:25p Samir + * Added QuickStart functions. + * + * 2 8/05/97 9:59a Samir + * Scripting stuff. + * + * 4 6/05/97 12:21p Samir + * Added more mission commands. + * + * 3 5/16/97 3:11p Samir + * Added a mission name and mission initializer. + * + * 2 5/15/97 2:09p Samir + * Added a bunch of sequencing from level to level, loading mission. + * + * 1 4/29/97 11:57a Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef MISSION_H +#define MISSION_H + +#include "pstypes.h" +#include "descent.h" + +// *** CONSTANTS *** +#define LOAD_PROGRESS_START 1 +#define LOAD_PROGRESS_LOADING_LEVEL 2 +#define LOAD_PROGRESS_PAGING_DATA 3 +#define LOAD_PROGRESS_PREPARE 4 +#define LOAD_PROGRESS_DONE 200 + +//Load level progress worker +void LoadLevelProgress(int step,float percent,char *chunk=NULL); + +// array constants +const int MSN_FILENAMELEN = PSPATHNAME_LEN, + MSN_URLLEN = 256; + +#define MAX_KEYWORDLEN 300 + +// increase this value if you are going to add more levels to a mission than the max. +const int MAX_LEVELS_PER_MISSION = 30; + +// mission flags. +const unsigned LVLFLAG_STARTMOVIE = 1, + LVLFLAG_ENDMOVIE = 2, + LVLFLAG_BRIEFING = 4, + LVLFLAG_SHIPSELECT = 8, + LVLFLAG_SPAWNSECRET = 16, + LVLFLAG_SPECIALHOG = 32, + LVLFLAG_BRANCH = 64, + LVLFLAG_UNUSED = 128, + LVLFLAG_SCORE = 256, + LVLFLAG_FINAL = 512; + + +const int LVLOBJ_NUM = 4; +const ushort LVLOBJF_SECONDARY1 = 1, + LVLOBJF_SECONDARY2 = 2, + LVLOBJF_SECONDARY3 = 4, + LVLOBJF_SECONDARY4 = 8; + +//Struct for info about the current level +typedef struct level_info { + char name[100]; + char designer[100]; + char copyright[100]; + char notes[1000]; +} level_info; + +//Info about the current level +extern level_info Level_info; + +// level information +typedef struct tLevelNode +{ +// level flags + unsigned flags; // level flags + unsigned objective_flags; // level objective flags + +// movies + char *moviename; + char *endmovie; + +// level filename + char *filename; // mine filename. + char *briefname; // briefing filename + char *hog; // hog file name for this level + char *score; // music score of level + char *progress; // File name containing the progress background screen + +// level branching + ubyte lvlbranch0, lvlbranch1; // FORK or BRANCH command + ubyte secretlvl; // SECRET command + ubyte pad; +} +tLevelNode; + + +// predefine mission state flags +#define MSN_STATE_SECRET_LEVEL 0x80000000 + +typedef struct tMission +{ + int mn3_handle; // if this mission was loaded from an MN3, this is the handle. + + unsigned game_state_flags; // game state information stored here (manipulated by scripting) + char *filename; // filename of mission. + char name[MSN_NAMELEN]; // name of mission + char author[MSN_NAMELEN]; // author of mission. + char desc[MSN_NAMELEN*4]; // description of mission + char *hog; // default hog filename + char email[MSN_URLLEN]; // email and web location + char web[MSN_URLLEN]; + bool multiplayable; // this level is multiplayer playable + bool singleplayable; // this level is playable as a single player game + bool training_mission; // is this mission a training mission? + +// listing of levels. + int num_levels; // number of levels + int cur_level; // current level playing. + tLevelNode *levels; // array of levels +} +tMission; + + +// structyre used to get information about a mission +typedef struct tMissionInfo +{ + char name[MSN_NAMELEN]; + char author[MSN_NAMELEN]; + char desc[MSN_NAMELEN*4]; + bool multi; + bool single; + bool training; + int n_levels; + char keywords[MAX_KEYWORDLEN]; // Keywords for mods, so you can see if this mission supports a given mod +} +tMissionInfo; + +// Scripting information + +// the current mission being played. +extern tMission Current_mission; +extern tLevelNode *Current_level; + +extern char D3MissionsDir[]; + +//Get the name field out of the mission file +char * GetMissionName(char *mission); + +// initializes mission system. +void InitMission(); + +// reset all states for a mission +void ResetMission(); + +// loads and verifies a mission as the current mission, returns if valid of not. +bool LoadMission(const char *msn); + +// initializes a level's script. +void InitLevelScript(); + +// frees a level's script +void FreeLevelScript(); + +// does a mission briefing, returns if mission was canceled, a false, or 0 value. +bool DoMissionBriefing(int level); + +// does the end mission stuff +void DoEndMission(); + +// does the mission movie stuff +void DoMissionMovie(char *movie); + +// loads a level and sets it as current level in mission +bool LoadMissionLevel(int level); + +// initializes the mission script +bool InitMissionScript(); + +// Objectives +void CompletedPrimaryObjective(); + +// Shows text on a background +void ShowProgressScreen (char *str,char *str2=NULL,bool flip=true); + +// See if a mission file is multiplayer playable. +bool IsMissionMultiPlayable(char *mission); + +// return information about a mission +bool GetMissionInfo(const char *msnfile, tMissionInfo *msn); + +//Returns the max number of teams this mission can support for this mod, or +//-1 if this level shouldn't be played with this mission +//Return values: +// -1 Bad match -- this level and this mod shouldn't be played together! +// MAX_NET_PLAYERS -- This is playable with any number of teams the mod wants +int MissionGetKeywords(char *mission,char *keywords); + +#ifdef EDITOR +// Used by editor to load all necessary elements for level playing for systems +// not initialized in editor, but only in game. +void QuickStartMission(); + +// Used by game->editor to free up all extra game mission elements not needed in editor. +void QuickEndMission(); + +#endif + +#endif \ No newline at end of file diff --git a/Descent3/NewPyroGauges.cpp b/Descent3/NewPyroGauges.cpp new file mode 100644 index 000000000..65743c8a3 --- /dev/null +++ b/Descent3/NewPyroGauges.cpp @@ -0,0 +1,440 @@ +/* + * $Logfile: /DescentIII/main/NewPyroGauges.cpp $ + * $Revision: 10 $ + * $Date: 4/17/99 6:15p $ + * $Author: Samir $ + * + * New Pyro GX Gauge rendering functions + * + * $Log: /DescentIII/main/NewPyroGauges.cpp $ + * + * 10 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 9 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * 8 11/17/97 10:49a Samir + * Fadeouts and Fadeins 90% working. + * + * 7 11/16/97 6:56p Samir + * Started fade out system for weapon gauges. Also put name of weapon in + * box. + * + * 6 11/14/97 5:30p Samir + * Now we get more information about the gauge's world position. + * + * 5 11/14/97 12:54p Samir + * Bad shield ring gauge. + * + * 4 11/11/97 1:27p Samir + * Weapon gauges partially working. Still needs work. + * + * 3 11/04/97 7:27p Jason + * made shield ring draw correctly + * + * 2 11/04/97 6:24p Samir + * Semi functioning shield gauges. + * + * 1 10/30/97 3:27p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "NewPyroGauges.h" +#include "gauges.h" +#include "gamefont.h" +#include "game.h" +#include "hud.h" +#include "grdefs.h" +#include "bitmap.h" +#include "player.h" +#include "polymodel.h" +#include "3d.h" +#include "renderer.h" +#include "gametexture.h" + + +// how long it takes for a weapon gauge to fade out/in +#define GAUGE_WEAPON_FADE_TIME 1.0f + + +////////////////////////////////////////////////////////////////////////////// + +static struct +{ + int ringbmp; +} +NewPyroGaugeData; + +////////////////////////////////////////////////////////////////////////////// + +void GetCenterPoint(int *x, int *y, vector *wpos); + + + +void NewPyroInitGauges() +{ + int texhandle; + texhandle = FindTextureName("GaugeShieldRing"); + if (texhandle == -1) { + Int3(); // Samir. + NewPyroGaugeData.ringbmp = -1; + return; + } + else { + NewPyroGaugeData.ringbmp = GetTextureBitmap(texhandle, 0); + } +} + + + +////////////////////////////////////////////////////////////////////////////// +// Energy gauges + +// shield gauge handler +void NewPyroShieldGauge(tGauge *gauge, tGaugePos *pos) +{ +// we need to get the rectangular position in screen coords of the submodel for this gauge. +// find center point of face/polygon. +//@@ int cx, cy,x,y; +//@@ char str[8]; +//@@ +//@@ GetCenterPoint(&cx, &cy, &pos->center); + +//@@ START_TEXT_BLOCK_ALPHA(0.7f) +//@@ { +//@@ int text_spacing = parentvp->get_text_spacing(); +//@@ parentvp->set_font("small"); +//@@ parentvp->set_text_color(GR_RGB(255,128,128)); +//@@ parentvp->set_text_spacing(0); +//@@ rend_SetCharacterParameters (parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color()); +//@@ sprintf(str, "%d", (int)Players[Player_num].shields); +//@@ x = cx - (parentvp->get_text_line_width(str)/2); +//@@ y = cy - (parentvp->get_current_font_height()/2); +//@@ parentvp->printf(x,y,str); +//@@ parentvp->set_text_spacing(text_spacing); +//@@ } +//@@ END_TEXT_BLOCK +} + + +#define NUM_SHIELD_SEGMENTS 36 + +float Zfactor = 2.0f; + +// ship status gauge +void NewPyroShipGauge(tGauge *gauge, tGaugePos *pos) +{ +// we should draw at least an octogonal polygon with the shield gauge texture. +//@@ g3Point inner_points[NUM_SHIELD_SEGMENTS]; +//@@ g3Point outer_points[NUM_SHIELD_SEGMENTS]; +//@@ g3Point center_pnt; +//@@ g3Point *pntlist[4]; +//@@ poly_model *pm = HudGetPolyModel(); +//@@ bsp_info *sm = &pm->submodel[gauge->submodel]; +//@@ +//@@ float lifenorm, outoffset, zdepth, zdiff; +//@@ int ring_increment, ring_angle; +//@@ int num_segs, i; +//@@ int bmphandle = NewPyroGaugeData.ringbmp; +//@@ +//@@// determine depth of ring. +//@@ lifenorm = Players[Player_num].shields/(float)INITIAL_SHIELDS; +//@@ if (lifenorm > 1.0f) lifenorm = 1.0f; +//@@ outoffset = 32.0f + (lifenorm * 16.0f); +//@@ +//@@// prepare renderer +//@@ rend_SetLighting(LS_NONE); +//@@ rend_SetTextureType (TT_LINEAR); +//@@ rend_SetAlphaType (AT_CONSTANT+AT_TEXTURE); +//@@ rend_SetAlphaValue (0.75f * 255); +//@@ rend_SetZBufferState (0); +//@@ +//@@// do 3d stuff +//@@ g3_RotatePoint (¢er_pnt,&pos->center); +//@@ g3_ProjectPoint (¢er_pnt); +//@@ +//@@ num_segs=NUM_SHIELD_SEGMENTS; +//@@ ring_increment=65536/num_segs; +//@@ ring_angle=0; +//@@ zdiff = (sm->max.z - sm->min.z); +//@@ +//@@ for (i=0;icenter); +//@@ +//@@ START_TEXT_BLOCK_ALPHA(0.7f) +//@@ { +//@@ int text_spacing = parentvp->get_text_spacing(); +//@@ parentvp->set_font("small"); +//@@ parentvp->set_text_color(GR_RGB(192,192,128)); +//@@ parentvp->set_text_spacing(0); +//@@ rend_SetCharacterParameters (parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color()); +//@@ sprintf(str, "%d", (int)Players[Player_num].energy); +//@@ x = cx - (parentvp->get_text_line_width(str)/2); +//@@ y = cy - (parentvp->get_current_font_height()/2); +//@@ parentvp->printf(x,y,str); +//@@ parentvp->set_text_spacing(text_spacing); +//@@ } +//@@ END_TEXT_BLOCK +} + +void NewPyroEnergyAnalogGauge(tGauge *gauge, tGaugePos *pos) +{ + +} + + +////////////////////////////////////////////////////////////////////////////// +// Weapon gauges + +void NewPyroPrimaryWeaponGauge(tGauge *gauge, tGaugePos *pos) +{ +//@@ static float fade_time = 0.0f; +//@@ static int fade_state = 0; +//@@ float gauge_alpha; +//@@ poly_model *pm; +//@@ bsp_info *sm; +//@@ +//@@ pm = HudGetPolyModel(); +//@@ sm = &pm->submodel[gauge->submodel]; +//@@ +//@@// first time initialization of gauge +//@@ if (!gauge->init) { +//@@ GameTextures[pm->textures[gauge->texindex]].bm_handle = +//@@ Weapons[Players[Player_num].primary_weapon].hud_image_handle; +//@@ fade_state = 0; +//@@ return; +//@@ } +//@@ +//@@ gauge_alpha = 1.0f; +//@@ +//@@// handle starting fade in and fade out. +//@@ if (gauge->modified) { +//@@ // alter texture of gauge submodel face 0 to param once faded out. +//@@ fade_state = 1; // go to fade out. +//@@ gauge->modified = false; +//@@ fade_time = 0.0f; +//@@ } +//@@ +//@@// handle fade. +//@@ if (fade_state == 1) { +//@@ // fade out. +//@@ gauge_alpha = 1.0f - (fade_time/GAUGE_WEAPON_FADE_TIME); +//@@ if (fade_time >= GAUGE_WEAPON_FADE_TIME) { +//@@ GameTextures[pm->textures[gauge->texindex]].bm_handle = +//@@ Weapons[Players[Player_num].primary_weapon].hud_image_handle; +//@@ fade_time = 0.0f; +//@@ fade_state = 2; // advance to fade in +//@@ } +//@@ else +//@@ fade_time += Frametime; +//@@ GameTextures[pm->textures[gauge->texindex]].alpha = gauge_alpha; +//@@ } +//@@ else if (fade_state == 2) { +//@@ // fade in! +//@@ gauge_alpha = (fade_time/GAUGE_WEAPON_FADE_TIME); +//@@ if (fade_time >= GAUGE_WEAPON_FADE_TIME) { +//@@ fade_time = 0.0f; +//@@ fade_state = 0; // null fade state +//@@ } +//@@ else +//@@ fade_time += Frametime; +//@@ GameTextures[pm->textures[gauge->texindex]].alpha = gauge_alpha; +//@@ } +//@@ +//@@// do weapon name text. +//@@ if (fade_state == 0) { +//@@ int x,y,cx,cy; +//@@ char str[PAGENAME_LEN]; +//@@ GetCenterPoint(&cx, &cy, &pos->center); +//@@ +//@@ START_TEXT_BLOCK_ALPHA(gauge_alpha) +//@@ { +//@@ int text_spacing = parentvp->get_text_spacing(); +//@@ parentvp->set_font("small"); +//@@ parentvp->set_text_color(GR_RGB(0,192,0)); +//@@ parentvp->set_text_spacing(0); +//@@ rend_SetCharacterParameters (parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color()); +//@@ sprintf(str, "%s", Weapons[Players[Player_num].primary_weapon].name); +//@@ x = cx - (parentvp->get_text_line_width(str)/3); +//@@ y = cy - (parentvp->get_current_font_height()/3); +//@@ parentvp->printf(x,y,str); +//@@ parentvp->set_text_spacing(text_spacing); +//@@ } +//@@ END_TEXT_BLOCK +//@@ GameTextures[pm->textures[gauge->texindex]].alpha = gauge_alpha; +//@@ } +} + + +void NewPyroSecondaryWeaponGauge(tGauge *gauge, tGaugePos *pos) +{ +//@@ static float fade_time = 0.0f; +//@@ static int fade_state = 0; +//@@ float gauge_alpha; +//@@ poly_model *pm; +//@@ bsp_info *sm; +//@@ +//@@ pm = HudGetPolyModel(); +//@@ sm = &pm->submodel[gauge->submodel]; +//@@ +//@@// first time initialization of gauge +//@@ if (!gauge->init) { +//@@ GameTextures[pm->textures[gauge->texindex]].bm_handle = +//@@ Weapons[Players[Player_num].secondary_weapon].hud_image_handle; +//@@ fade_state = 0; +//@@ return; +//@@ } +//@@ +//@@ gauge_alpha = 1.0f; +//@@ +//@@// handle starting fade in and fade out. +//@@ if (gauge->modified) { +//@@ // alter texture of gauge submodel face 0 to param once faded out. +//@@ fade_state = 1; // go to fade out. +//@@ gauge->modified = false; +//@@ fade_time = 0.0f; +//@@ } +//@@ +//@@// handle fade. +//@@ if (fade_state == 1) { +//@@ // fade out. +//@@ gauge_alpha = 1.0f - (fade_time/GAUGE_WEAPON_FADE_TIME); +//@@ if (fade_time >= GAUGE_WEAPON_FADE_TIME) { +//@@ GameTextures[pm->textures[gauge->texindex]].bm_handle = +//@@ Weapons[Players[Player_num].secondary_weapon].hud_image_handle; +//@@ fade_time = 0.0f; +//@@ fade_state = 2; // advance to fade in +//@@ } +//@@ else +//@@ fade_time += Frametime; +//@@ GameTextures[pm->textures[gauge->texindex]].alpha = gauge_alpha; +//@@ } +//@@ else if (fade_state == 2) { +//@@ // fade in! +//@@ gauge_alpha = (fade_time/GAUGE_WEAPON_FADE_TIME); +//@@ if (fade_time >= GAUGE_WEAPON_FADE_TIME) { +//@@ fade_time = 0.0f; +//@@ fade_state = 0; // null fade state +//@@ } +//@@ else +//@@ fade_time += Frametime; +//@@ GameTextures[pm->textures[gauge->texindex]].alpha = gauge_alpha; +//@@ } +//@@ +//@@// do weapon name text. +//@@ if (fade_state == 0) { +//@@ int x,y,cx,cy; +//@@ char str[PAGENAME_LEN]; +//@@ GetCenterPoint(&cx, &cy, &pos->center); +//@@ +//@@ START_TEXT_BLOCK_ALPHA(gauge_alpha) +//@@ { +//@@ int text_spacing = parentvp->get_text_spacing(); +//@@ parentvp->set_font("small"); +//@@ parentvp->set_text_color(GR_RGB(0,192,0)); +//@@ parentvp->set_text_spacing(0); +//@@ rend_SetCharacterParameters (parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color(), parentvp->get_text_color()); +//@@ sprintf(str, "%s", Weapons[Players[Player_num].secondary_weapon].name); +//@@ x = cx - (parentvp->get_text_line_width(str)/1.5f); +//@@ y = cy - (parentvp->get_current_font_height()/3); +//@@ parentvp->printf(x,y,str); +//@@ parentvp->set_text_spacing(text_spacing); +//@@ } +//@@ END_TEXT_BLOCK +//@@ GameTextures[pm->textures[gauge->texindex]].alpha = gauge_alpha; +//@@ } +} + + + +////////////////////////////////////////////////////////////////////////////////////////// + +// This function will return the center point of a flat face in global screen coordinates +void GetCenterPoint(int *x, int *y, vector *wpos) +{ + g3Point pt; + + g3_RotatePoint(&pt, wpos); + g3_ProjectPoint(&pt); + + *x = (int)pt.p3_sx; + *y = (int)pt.p3_sy; +} + + diff --git a/Descent3/NewPyroGauges.h b/Descent3/NewPyroGauges.h new file mode 100644 index 000000000..17923d162 --- /dev/null +++ b/Descent3/NewPyroGauges.h @@ -0,0 +1,49 @@ +/* + * $Logfile: /DescentIII/main/NewPyroGauges.h $ + * $Revision: 5 $ + * $Date: 12/29/97 5:44p $ + * $Author: Samir $ + * + * New Pyro GX Gauge rendering functions + * + * $Log: /DescentIII/main/NewPyroGauges.h $ + * + * 5 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * 4 11/14/97 5:30p Samir + * Now we get more information about the gauge's world position. + * + * 3 11/11/97 1:27p Samir + * Weapon gauges partially working. Still needs work. + * + * 2 11/04/97 6:24p Samir + * Semi functioning shield gauges. + * + * 1 10/30/97 3:27p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef NEWPYRO_H +#define NEWPYRO_H + +struct tGauge; +struct tGaugePos; + +#include "vecmat.h" + +// rendering functions + + +// shield gauge handlers (one for number, one for status) +void NewPyroInitGauges(); +void NewPyroShieldGauge(tGauge *gauge, tGaugePos *pos); +void NewPyroShipGauge(tGauge *gauge, tGaugePos *pos); +void NewPyroEnergyDigitalGauge(tGauge *gauge, tGaugePos *pos); +void NewPyroEnergyAnalogGauge(tGauge *gauge, tGaugePos *pos); +void NewPyroPrimaryWeaponGauge(tGauge *gauge, tGaugePos *pos); +void NewPyroSecondaryWeaponGauge(tGauge *gauge, tGaugePos *pos); + +#endif \ No newline at end of file diff --git a/Descent3/ObjInit.cpp b/Descent3/ObjInit.cpp new file mode 100644 index 000000000..875f2b2f1 --- /dev/null +++ b/Descent3/ObjInit.cpp @@ -0,0 +1,1198 @@ +/* + * $Logfile: /DescentIII/main/ObjInit.cpp $ + * $Revision: 170 $ + * $Date: 4/19/00 5:18p $ + * $Author: Matt $ + * + * Functions to initialize objects + * + * $Log: /DescentIII/main/ObjInit.cpp $ + * + * 170 4/19/00 5:18p Matt + * From Duane for 1.4 + * Added check for ai_info + * + * 169 3/20/00 12:18p Matt + * Merge of Duane's post-1.3 changes. + * msize change (Mac only) + * + * 168 2/22/00 10:00a Matt + * Fixed Jason's typo + * + * 167 2/19/00 6:44p Jason + * Fixed out of bounds weapon problem + * + * 166 10/24/99 9:33a Chris + * Cleaned a little more Mac code + * + * 165 10/22/99 10:59p Matt + * Added check for anim data before renferencing it. + * + * 164 10/22/99 11:30a Matt + * Mac merge + * + * 163 7/28/99 5:18p Kevin + * Mac merge fixes + * + * 162 7/28/99 3:50p Kevin + * Mac! + * + * 161 5/20/99 7:54p 3dsmax + * added better memory support changing ships + * + * 160 5/19/99 3:54p Jeff + * error is scripts are being bound to an object more than once + * + * 159 5/08/99 6:20p Jason + * fixed framerate wiggle problem + * + * 158 4/28/99 5:09p Jason + * fixed some bugs with latest build + * + * 157 4/27/99 4:44p Jason + * fixed moving buildings not getting lit + * + * 156 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 155 4/21/99 1:23p Chris + * Added the check ceiling checkbox for generic objects + * + * 154 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 153 4/20/99 3:33p Matt + * ObjInit() was bashing creation time after setting it. + * + * 152 4/20/99 2:44p Matt + * Deal with initializing a generic object whose ID isn't defined. This + * can happen if an object is deleted and the level in memory uses that + * object. + * + * 151 4/20/99 12:24p Matt + * Re-revised the ObjInit() system. ObjInit() again does all the + * initialization stuff, but now it's passed a lot more information so + * those fields can be set before the rest of the initialization takes + * place. + * + * 150 4/18/99 5:33p Jason + * fixed object multiplayer problem + * + * 149 4/18/99 4:50p Matt + * Took out code that hacked in ammo amounts for weapon powerups, and + * added code to set the counts on the page and read and write to the + * table file. + * + * 148 4/15/99 1:42a Jeff + * changes for linux compile + * + * 147 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 146 4/12/99 6:11p Jason + * fix clutter objects so that they are volume lit as well + * + * 145 4/12/99 3:53p Matt + * Page in polymodel for markers so model size is right. + * + * 144 4/10/99 5:56p Matt + * Cleaned up object initialization code, including create object & load + * object. + * + * 143 4/08/99 6:38p Matt + * Improved message. + * + * 142 4/08/99 6:15p Matt + * Deal with generic objects that have changed type. + * + * 141 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 140 3/02/99 4:54p Matt + * Took out assert + * + * 139 3/02/99 12:22p Matt + * Revisions to weapon & ammo powerups. Not fully tested. + * + * 138 2/25/99 11:01a Matt + * Added new explosion system. + * + * 137 2/22/99 2:04p Jason + * added different damages for players and generics + * + * 136 2/21/99 4:20p Matt + * Added SoundSource objects (and reformatted parts of the object header + * files). + * + * 135 2/15/99 6:10p Chris + * + * 134 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 133 2/04/99 2:05p Matt + * Added blastable doors + * + * 132 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 131 1/28/99 11:32a Jason + * added marker cameras + * + * 130 1/27/99 6:08p Jason + * first pass at markers + * + * 129 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 128 1/13/99 6:38a Jeff + * fixed object.h. There were numerous struct declarations that were the + * same name as the instance of the struct (gcc doesn't like this). + * Changed the struct name. Also added some #ifdef's for linux build, + * along with fixing case-sensitive includes + * + * 127 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 126 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 125 10/29/98 6:15p Chris + * Made a minimum rotdrag for robots + * + * 124 10/22/98 5:36p Chris + * Fixed ObjGet bugs + * + * 123 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 122 10/15/98 6:46p Chris + * Added custom size for weapons + * + * 121 10/13/98 5:48p Jeff + * CHRISHACK -- multiplayer ship hack for attach points -- DEMO SAFE + * + * 120 10/13/98 3:58p Kevin + * changed free to mem_free + * + * 119 10/11/98 10:53p Matt + * Fixed some zero-length malloc()s + * + * 118 10/11/98 2:40p Matt + * Added some error checking to ObjInitGeneric() + * + * 117 10/09/98 3:32p Kevin + * New memory library + * + * 116 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 115 10/02/98 1:47p Jason + * added lod player ships + * + * 114 9/28/98 4:09p Chris + * Added birth animations + * + * 113 9/22/98 3:55p Samir + * ifdef out stuff for non-debug version. + * + * 112 8/26/98 12:09p Jason + * made object reinitialization not undo lightmaps + * + * 111 8/25/98 4:40p Samir + * small sequencing prob, + * + * 110 8/25/98 3:44p Sean + * moved control intitalization of player to after polymodel is paged in. + * + * 109 8/18/98 12:48p Jason + * made debris objects have volume lighting + * + * 108 8/12/98 6:37p Jeff + * removed init stuff for OBJ_DUMMY + * + * 107 8/03/98 4:29p Chris + * Added additional support for the attach system + * + * 106 8/03/98 4:29p Jason + * took out presets for ship physics flags + * + * 105 7/28/98 12:29p Jason + * added rotational velocity to multiplayer position packets + * + * 104 7/20/98 2:06p Chris + * Added the precomputation of some weapons (fixed speed weapons). This + * algorithm can be expanded to include some missiles with some minor + * work. + * + * 103 7/07/98 7:34p Jeff + * created object type, OBJ_DUMMY + * + * 102 7/06/98 12:22p Keneta + * Fixed a bug with dynamically allocated weapon batteries + * + * 101 7/02/98 2:47p Chris + * Dynamic weapon info is now dynamically allocated + * + * 100 6/12/98 4:07p Jason + * added dynamic volume lighting to objects + * + * 99 6/11/98 12:48p Jason + * added better spewing weapons + * + * 98 5/21/98 12:34p Jason + * made particles drop differently + * + * 97 5/19/98 4:42a Chris + * Added shockwave's -- enjoy. :) + * + * 96 5/15/98 5:41p Jason + * implemented volume lighting system + * + * 95 5/14/98 12:56p Jason + * changes to help lower memory usage + * + * 94 5/12/98 11:58a Chris + * Offset animation's for generic objects + * + * 93 5/11/98 4:40p Chris + * AI info is now a malloc'ed pointer + * + * 92 5/07/98 2:22p Chris + * Hit die dot + * + * 91 5/04/98 3:51p Matt + * Finished (for now) with breaking glass. + * + * 90 5/04/98 12:28p Matt + * Added shard objects + * + * 89 5/01/98 3:38p Chris + * Leveling defaults off + * + * 88 5/30/98 10:16p Chris + * Fixed some bad spelling + * + * 87 4/30/98 11:32a Chris + * ClearWB to WBClear + * + * 86 4/22/98 3:25p Jason + * changes for multiplayer debugging + * + * 85 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 84 4/17/98 1:59p Jason + * added cool object effects + * + * 83 4/16/98 2:56p Chris + * Updated buddy support and intergrated scripting and AI + * + * 82 4/15/98 8:38p Jason + * fixed some multiplayer issues + * + * 81 4/15/98 12:56p Chris + * Updated parent_handle support and added buddy bot support + * + * 80 4/03/98 11:27a Chris + * Doubled the size of powerups + * + * 79 4/03/98 10:07a Chris + * Added support for objects getting their size computed when the + * polymodel is paged in the first time as an object + * + * 78 4/02/98 6:38p Jason + * page in low-res models with generic objects + * + * 77 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 76 3/31/98 6:11p Jason + * took out auto ship selection + * + * 75 3/30/98 6:58p Matt + * When reinitializing an object, don't bash its custom script. + * + * 74 3/30/98 6:20p Matt + * Renamed ResetObject() to be ObjReInitAll() + * + * 73 3/20/98 5:51p Jason + * more changes for multiplayer + * + * 72 3/19/98 7:15p Jason + * more changes for multiplayer + * + * 71 3/13/98 5:55p Chris + * Added the new collision spheres + * + * 70 3/11/98 4:59p Chris + * Changed the ComputeDefualtSize function call + * + * 69 3/09/98 10:49a Chris + * Worked on the homing code + * + * 68 2/25/98 2:06p Jason + * fixed objinit player to give you different ships in the correct order + * + * 67 2/25/98 12:32p Jason + * added a new player ship for multiplayer games + * + * 66 2/23/98 4:52p Chris + * Removed size hack + * + * 65 2/17/98 8:17p Jeff + * hacked a player ship size in for the milestone + * + * 64 2/17/98 8:16p Jeff + * Call compute default size for player ships + * + * 63 2/16/98 4:41p Jason + * took out starhawk ship (again!) + * + * 62 2/16/98 2:47a Chris + * Massive improvements to the animation system and the AI + * + * 61 2/11/98 6:58p Matt + * Changed the way cameras are rendered, so they show up on the terrain as + * well. + * + * 60 2/10/98 7:02p Jason + * fixed lighting render type for rooms + * + * 59 2/09/98 7:28p Matt + * Added a rendering type for external room so we could check against + * render type == RT_NONE + * + * 58 2/03/98 1:06p Jason + * tidied up some multiplayer loose ends + * + * 57 1/30/98 12:13p Jason + * give weapons lifetime + * + * 56 1/29/98 5:49p Matt + * Changed old camera object type to be viewer object (for editor), and + * now camera objects will just be for game cameras. + * + * 55 1/29/98 3:29p Chris + * Major update to the AI system. + * + * 54 1/28/98 12:00p Jason + * more changes for multiplayer + * + * 53 1/23/98 6:25p Jason + * Got spray weapons working + * + * 52 1/23/98 11:21a Jason + * incremental multiplayer checkin + * + * 51 1/20/98 4:12p Samir + * New script housekeeping system. + * + * 50 1/18/98 9:05p Matt + * Changed a comment, and deleted some unused code + * + * 49 1/10/98 2:24p Jason + * better multiplayer code checked in + * + * 48 12/08/97 6:18p Jason + * more tweaks for destroyable buildings + * + * 47 12/05/97 6:38p Chris + * Fixed bug with impact_size initialization + * + * 46 12/01/97 9:54a Chris + * Added support for concussive forces, generalized robot collisions to + * generic collisions + * + * 45 11/04/97 6:18p Chris + * Added support so that the player ship uses the robot's firing dialog. + * + * 44 10/28/97 12:21p Chris + * Unified AIInit + * + * 43 10/23/97 11:34p Chris + * Fixed problems with idle animations + * + * 42 10/23/97 5:36p Jason + * added splinter objects (probably temporary) + * + * 41 10/21/97 11:57a Jason + * added ability to set dying model for player ship + * + * 40 10/20/97 6:10p Jason + * added of_polygon_object flag to debris stuff + * + * 39 10/20/97 6:02p Jason + * fixed dumb objweapon bug + * + * 38 10/20/97 5:54p Jason + * added polygon_object flag + * + * 37 10/20/97 4:59p Jason + * made player ships render with gouraud shading + * + * 36 10/20/97 4:46p Jason + * changes for explosions + * + * 35 10/17/97 5:09p Jason + * added more fireball stuff + * + * 34 10/05/97 5:31a Chris + * Make AI init match ai_init_all in aimain.cpp + * + * 33 10/03/97 5:18p Chris + * Added OBJ_LINE as a new type + * + * 32 10/03/97 4:43p Chris + * added debug fvi call and object type line + * + * 31 10/01/97 7:51p Matt + * Added code for external rooms + * + * 30 10/01/97 12:39p Chris + * Added support for new physics values in generic objects + * + * 29 9/30/97 6:40p Jason + * got lightmap stuff sort of working with objects + * + * 28 9/30/97 1:15p Matt + * Made player objects other than player 0 have physics and control types + * of none + * + * 27 9/24/97 6:18p Samir + * Use script names instead of script id values to identify scripts. + * + * 26 9/22/97 6:18p Matt + * Removed include of stub.h + * + * 25 9/19/97 9:37p Chris + * Attempted to fix door collisions -- almost there, but not quite + * + * 24 9/15/97 5:21a Chris + * Fixed the weapon fixed rotate initialization + * + * 23 9/11/97 5:38p Jason + * initial door coding for room engine + * + * 22 9/10/97 3:58p Samir + * Initialize custom script handle to -1 + * + * 21 9/05/97 1:29p Jason + * revamped generic object lighting + * + * 20 9/04/97 3:21p Jason + * added pulse timing for lit objects + * + * 19 9/03/97 6:17p Jason + * added code to support powerups/robots/etc that cast light + * + * 18 9/03/97 3:54p Jason + * got objects to cast light + * + * 17 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 16 8/21/97 5:56p Samir + * Took out script specific stuff from ObjInit and moved to ObjScript + * + * 15 8/18/97 1:42a Chris + * Fixed the flags = 0 bug. Remove an ASSERT that broke the debris code. + * Until the debris creation is changed, the assert will have to remain + * removed. + * + * 14 8/13/97 4:54p Matt + * Added destroyable flag & hitpoints + * + * 13 8/12/97 5:29p Matt + * Added support for clutter & building types + * + * 12 8/12/97 3:23p Samir + * Added code to initialize scripts for objects. + * + * 11 8/11/97 1:53p Matt + * Ripped out robot & powerup pages, and added generic page + * + * 10 8/07/97 4:47p Chris + * Massively expanded the weapon system. Not done yet. + * + * 9 8/06/97 4:33p Chris + * Expanded the weapons page + * + * 8 8/06/97 1:35p Matt + * Changes for new generic object_info struct which replace robot-specific + * structure + * + * 7 7/28/97 1:14p Chris + * Added support for sub-object visability. Plus, debris. + * + * 6 7/15/97 7:29p Chris + * Added a sound for helicopter blades. + * + * 5 7/15/97 5:35p Chris + * New AI code + * + * 4 7/15/97 4:59p Chris + * added support for AI and animations + * + * 10 6/30/97 5:23p Chris + * + * 9 6/25/97 9:28p Chris + * Added code to allow for robots that are unmovable and/or with/without + * AI. + * + * 8 5/15/97 6:09p Chris + * + * 7 4/24/97 5:42p Jason + * got fireball vclips working + * + * 6 4/16/97 11:50a Jason + * finally got weapons to fire + * + * 5 4/15/97 6:32p Chris + * Added some temporary stuff to get weapons firing. + * + * 4 4/15/97 2:28p Jason + * trying to get weapon firing working...it won't due to a (i think) + * physics bug. Checked in so Chris can have a look. + * + * + * 3 4/08/97 2:00a Chris + * Made powerups always bouncy and have unlimitted + * bounce. This will change as time goes on. + * + * 2 4/04/97 2:57p Matt + * Added code to initialize all the type-specific data for an object from + * the page for that object type. This means that we need to pass less + * info to ObjCreate(), and that we save less info in the level save file. + * It also makes it easy to reset all the objects when an object page has + * changed. + * + * 1 4/04/97 10:01a Matt + * + * $NoKeywords: $ + */ +#include "objinit.h" +#include "player.h" +#include "ship.h" +#include "pserror.h" +#include "PHYSICS.H" +#include "weapon.h" +#include "AIMain.h" +#include "fireball.h" +#include "objinfo.h" +#include "Mission.h" +#include "robotfire.h" +#include "door.h" +#include "vclip.h" +#include "polymodel.h" +#include "robot.h" +#include "sounds.h" +#include "mem.h" +#include "marker.h" +//#include +#include +#include "psrand.h" +//Allocate and initialize an effect_info struct for an object +void ObjCreateEffectInfo(object *objp) +{ + if (objp->effect_info) + mem_free (objp->effect_info); + + objp->effect_info=(effect_info_s *)mem_malloc (sizeof(effect_info_s)); + memset (objp->effect_info,0,sizeof(effect_info_s)); + ASSERT (objp->effect_info); + objp->effect_info->sound_handle = SOUND_NONE_INDEX; +} +void ObjSetRenderPolyobj(object *objp,int model_num,int dying_model_num=-1); +#define NUM_PLAYER_ATTACH_POINTS 3 +//Initialize the polygon object information for an object +void ObjSetRenderPolyobj(object *objp,int model_num,int dying_model_num) +{ + objp->render_type = RT_POLYOBJ; + objp->flags |= OF_POLYGON_OBJECT; + polyobj_info *p_info = &objp->rtype.pobj_info; + p_info->model_num = model_num; + p_info->dying_model_num = dying_model_num; + p_info->anim_frame = 0.0f; + p_info->tmap_override = -1; + p_info->subobj_flags = 0xFFFFFFFF; + p_info->multi_turret_info.num_turrets = 0; + p_info->multi_turret_info.keyframes = NULL; + p_info->multi_turret_info.last_keyframes = NULL; + //Initialize attach slots + if (model_num != -1) { + poly_model *pm = GetPolymodelPointer(model_num); //get also pages in + if (objp->attach_children!=NULL) + { + mem_free (objp->attach_children); + objp->attach_children=NULL; + } + if ((objp->attach_children == NULL) && pm->n_attach) { + objp->attach_children = (int *) mem_malloc(sizeof(int) * pm->n_attach); + if (objp->type==OBJ_PLAYER) + ASSERT (pm->n_attach>=NUM_PLAYER_ATTACH_POINTS); + for(int i = 0; i < pm->n_attach; i++) + objp->attach_children[i] = OBJECT_HANDLE_NONE; + } + } +} +//Initialize a player object +//Returns 1 if ok, 0 if error +int ObjInitPlayer(object *objp) +{ + ship *ship; + int ret=1; + ASSERT(objp->type == OBJ_PLAYER); + objp->shields = INITIAL_SHIELDS; + ship = &Ships[Players[objp->id].ship_index]; + if (! ship->used) { //this ship doesn't exist + int new_ship; + Int3(); + ret = 0; + new_ship = GetNextShip(0); + ASSERT(new_ship != -1); + Players[objp->id].ship_index = new_ship; + ship = &Ships[Players[objp->id].ship_index]; + } + //Set up render info + ObjSetRenderPolyobj(objp,ship->model_handle,ship->dying_model_handle); + ASSERT(Poly_models[ship->model_handle].n_attach >= 3); + objp->lighting_render_type=LRT_GOURAUD; + objp->lm_object.used=0; + // Page in those models + PageInPolymodel (ship->model_handle, OBJ_PLAYER, &ship->size); + if (ship->dying_model_handle>=0) + PageInPolymodel (ship->dying_model_handle); + if (ship->lo_render_handle>=0) + PageInPolymodel (ship->lo_render_handle); + if (ship->med_render_handle>=0) + PageInPolymodel (ship->med_render_handle); + //Set size + objp->size = ship->size; + //Set up control info. The player flies, other players do nothing + // MUST be after paging in polymodel cause SetObjectControlType does some + // stuff with the object's polymodel. + if (objp->id == Player_num) { + SetObjectControlType(objp, CT_FLYING); + objp->movement_type = MT_PHYSICS; + objp->mtype.phys_info = ship->phys_info; + Player_object=objp; + } + else { + SetObjectControlType(objp, CT_NONE); + objp->movement_type = MT_NONE; + objp->mtype.phys_info = ship->phys_info; + objp->mtype.phys_info.flags = PF_FIXED_VELOCITY; + } + //Set up physics info + //These are always set for a player + objp->mtype.phys_info.num_bounces = PHYSICS_UNLIMITED_BOUNCE; + if(objp->dynamic_wb == NULL) + { + objp->dynamic_wb = (dynamic_wb_info *) mem_malloc(sizeof(dynamic_wb_info) * MAX_WBS_PER_OBJ); + } + WBClearInfo(objp); + // Set a few misc things + Players[objp->id].team=objp->id%2; + ObjCreateEffectInfo(objp); + objp->effect_info->type_flags=EF_VOLUME_LIT; + + return ret; +} +//Initialize a generic object (robot, powerup, building, etc.) +//Returns 1 if ok, 0 if error +int ObjInitGeneric(object *objp,bool reinit) +{ + object_info *obj_info; + int ret=1; + float r_val = (float)ps_rand()/(float)RAND_MAX; + if ((objp->id < 0) || (objp->id >= MAX_OBJECT_IDS)) { + Int3(); + return 0; + } + obj_info = &Object_info[objp->id]; + //Deal with deleted type + if (obj_info->type == OBJ_NONE) { + int i; + for (i=0,obj_info=Object_info;itype) + break; + } + ASSERT(i < MAX_OBJECT_IDS); //There should (in real life) always be at least one of each type + #ifdef EDITOR + if (GetFunctionMode() == EDITOR_MODE) + OutrageMessageBox("Object %d (\"%s\") had ID %d which no longer exists. Changing to %d, \"%s\".",OBJNUM(objp),objp->name?objp->name:"",objp->id,i,obj_info->name); + #endif + } + if (obj_info->type != objp->type) { + #ifdef EDITOR + if (GetFunctionMode() == EDITOR_MODE) + OutrageMessageBox("Object %d (\"%s\"), type name \"%s\", changed from type %s to %s",OBJNUM(objp),objp->name?objp->name:"",obj_info->name,Object_type_names[objp->type],Object_type_names[obj_info->type]); + #endif + objp->type = obj_info->type; + } + //Set size & shields + objp->shields = obj_info->hit_points; + //Set impact stuff for non-weapons + objp->impact_size = obj_info->impact_size; + objp->impact_time = obj_info->impact_time; + objp->impact_player_damage = obj_info->damage; + objp->impact_generic_damage = obj_info->damage; + objp->impact_force = obj_info->damage * 50.0; + //Set flags + if (obj_info->flags & OIF_DESTROYABLE) + objp->flags |= OF_DESTROYABLE; + if (obj_info->flags & OIF_AI_SCRIPTED_DEATH) + objp->flags |= OF_AI_DO_DEATH; + if (obj_info->flags & OIF_DO_CEILING_CHECK) + objp->flags |= OF_FORCE_CEILING_CHECK; + //Set up movement info + if (obj_info->flags & OIF_USES_PHYSICS) + { + objp->movement_type = MT_PHYSICS; + // Setup some physics things + objp->mtype.phys_info = obj_info->phys_info; // Set the initial velocity + //Warn about initial velocity & rotvel + if (obj_info->phys_info.velocity.z > 0.0) + Int3(); //Warning: This object has an initial velocity. This is not supported. + //If your object does not need an initial velocity, set it to zero on + //the page. If you do need an initial velocity, someone will have to + //add code to deal with it, perhaps here or perhaps at level start. + } + else + objp->movement_type = MT_NONE; + + //Set up render info + ObjSetRenderPolyobj(objp,obj_info->render_handle); + PageInPolymodel (obj_info->render_handle, objp->type, &obj_info->size); + objp->size = obj_info->size; + + //Page in low-res models + if (obj_info->lo_render_handle!=-1) + PageInPolymodel (obj_info->lo_render_handle); + if (obj_info->med_render_handle!=-1) + PageInPolymodel (obj_info->med_render_handle); + + //Set anim frame + if (Object_info[objp->id].anim) + { + objp->rtype.pobj_info.anim_frame = ((1.0f - r_val) * (float)Object_info[objp->id].anim[MC_STANDING].elem[AS_ALERT].from) + + (r_val * (float)Object_info[objp->id].anim[MC_STANDING].elem[AS_ALERT].to); + } + else + { + objp->rtype.pobj_info.anim_frame = 0.0f; + } + + //Set up control info (must be after wb stuff) + if(obj_info->ai_info && obj_info->flags & OIF_CONTROL_AI) //DAJ 12/18/99 added check for ai_info + { + SetObjectControlType(objp, CT_AI); + AIInit(objp, obj_info->ai_info->ai_class, obj_info->ai_info->ai_type, obj_info->ai_info->movement_type); + } + else + SetObjectControlType(objp, CT_NONE); + if(objp->control_type == CT_AI) + { + poly_model *pm=&Poly_models[objp->rtype.pobj_info.model_num]; + int num_wbs = pm->num_wbs; + +#ifndef MACINTOSH + if((objp->dynamic_wb != NULL) && ((unsigned int)num_wbs != mem_size(objp->dynamic_wb)/sizeof(dynamic_wb_info))) +#else +//FIXME _msize is not available on the MAC so + if((objp->dynamic_wb != NULL) && ((unsigned int)num_wbs != MAX_WBS_PER_OBJ)) +#endif + { + mem_free(objp->dynamic_wb); + objp->dynamic_wb = NULL; + } + if((objp->dynamic_wb == NULL) && num_wbs) + { + objp->dynamic_wb = (dynamic_wb_info *) mem_malloc(sizeof(dynamic_wb_info) * num_wbs); + } + //Setup the weapon batteries (must be after polymodel stuff) + WBClearInfo(objp); + } + objp->lighting_render_type=obj_info->lighting_info.lighting_render_type; + if (!reinit) + objp->lm_object.used=0; + // Allocate effect memory + ObjCreateEffectInfo(objp); + + if (objp->type!=OBJ_POWERUP) + objp->effect_info->type_flags=EF_VOLUME_LIT; + if(objp->movement_type == MT_PHYSICS) + { + if(objp->mtype.phys_info.rotdrag < 60.0f) + objp->mtype.phys_info.rotdrag = 60.0; // CHRISHACK - MAKES SURE ROBOTS DONT SPIN FOREVER + } + + //Set ammo amounts for powerups + if (objp->type == OBJ_POWERUP) { + if (objp->control_type == CT_NONE) { //some powerups have AI; don't mess with them + SetObjectControlType(objp, CT_POWERUP); + objp->ctype.powerup_info.count = obj_info->ammo_count; + } + } + // Clear wiggle flag to supersede those pesky designer errors! + objp->mtype.phys_info.flags&=~PF_WIGGLE; + return ret; +} +int ObjInitDebris(object *objp) +{ + ASSERT(objp->type == OBJ_DEBRIS); + objp->movement_type = MT_PHYSICS; + SetObjectControlType(objp, CT_DEBRIS); + objp->lifeleft = DEBRIS_LIFE; + objp->flags |= OF_USES_LIFELEFT; + objp->ctype.debris_info.death_flags = 0; + //Set up render info + ObjSetRenderPolyobj(objp,-1); + objp->lighting_render_type=LRT_GOURAUD; + objp->lm_object.used=0; + // Allocate effect memory + ObjCreateEffectInfo(objp); + + objp->effect_info->type_flags=EF_VOLUME_LIT; + + return 1; +} +int ObjInitShard(object *objp) +{ + ASSERT(objp->type == OBJ_SHARD); + objp->movement_type = MT_PHYSICS; + SetObjectControlType(objp, CT_NONE); + objp->lifeleft = DEBRIS_LIFE * 5; + objp->flags |= OF_USES_LIFELEFT; + //Set physics info + objp->mtype.phys_info.flags = PF_GRAVITY + PF_BOUNCE; + objp->mtype.phys_info.mass = 1.0; + objp->mtype.phys_info.drag = 0.0001f; + objp->mtype.phys_info.coeff_restitution = 0.3f; + objp->mtype.phys_info.num_bounces = 0; + + //Set up render info + objp->render_type = RT_SHARD; + return 1; +} +#ifdef _DEBUG +int ObjInitLine(object *objp) +{ + ASSERT(objp->type == OBJ_DEBUG_LINE); + objp->movement_type = MT_NONE; + SetObjectControlType(objp, CT_NONE); + objp->render_type = RT_LINE; + return 1; +} +#endif +//Initialize a camera +//Returns 1 if ok, 0 if error +int ObjInitCamera(object *objp) +{ + //Set size & shields + objp->size = 0.5; + objp->shields = 0; + //Set up various info + SetObjectControlType(objp, CT_NONE); + objp->movement_type = MT_NONE; + objp->lm_object.used=0; + #ifdef EDITOR + objp->render_type = RT_EDITOR_SPHERE; //draw cameras as spheres in the editor + objp->rtype.sphere_color = GR_RGB(255,255,0); + #else + objp->render_type = RT_NONE; + #endif + return 1; +} +//Initialize a sound object +//Returns 1 if ok, 0 if error +int ObjInitSoundSource(object *objp) +{ + //Set size & shields + objp->size = 0.5; + objp->shields = 0; + //Set up various info + SetObjectControlType(objp, CT_SOUNDSOURCE); + objp->movement_type = MT_NONE; + objp->lm_object.used=0; + #ifdef EDITOR + objp->render_type = RT_EDITOR_SPHERE; //draw cameras as spheres in the editor + objp->rtype.sphere_color = GR_RGB(0,255,0); + #else + objp->render_type = RT_NONE; + #endif + return 1; +} +//Initialize a waypoint object +//Returns 1 if ok, 0 if error +int ObjInitWaypoint(object *objp) +{ + //Set size & shields + objp->size = 0.5; + objp->shields = 0; + //Set up various info + SetObjectControlType(objp, CT_NONE); + objp->movement_type = MT_NONE; + objp->lm_object.used=0; + #ifdef EDITOR + objp->render_type = RT_EDITOR_SPHERE; //draw cameras as spheres in the editor + objp->rtype.sphere_color = GR_RGB(255,130,33); + #else + objp->render_type = RT_NONE; + #endif + return 1; +} +//Initialize an editor viewer object +//Returns 1 if ok, 0 if error +int ObjInitViewer(object *objp) +{ + //Set size & shields + objp->size = 5.0; + objp->shields = 0; + //Set up various info + SetObjectControlType(objp, CT_NONE); + objp->movement_type = MT_NONE; + objp->render_type = RT_NONE; + objp->lm_object.used=0; + return 1; +} +//Initialize a weapon +//Returns 1 if ok, 0 if error +int ObjInitWeapon(object *objp) +{ + weapon *weap; + int ret=1; + ASSERT(objp->type == OBJ_WEAPON); + + if (objp->id<0 || objp->id>=MAX_WEAPONS) + return 0; + + weap = &Weapons[objp->id]; + ASSERT (weap->used>0); + //Set up shields + objp->shields = 0; + //Set up control info + SetObjectControlType(objp, CT_WEAPON); + // Not tracking anything + objp->ctype.laser_info.track_handle = OBJECT_HANDLE_NONE; + //Set up physics info + objp->movement_type = MT_PHYSICS; + objp->mtype.phys_info.mass = weap->phys_info.mass; + objp->mtype.phys_info.drag = weap->phys_info.drag; + objp->mtype.phys_info.rotdrag = weap->phys_info.rotdrag; + objp->mtype.phys_info.flags = weap->phys_info.flags; + + objp->mtype.phys_info.full_rotthrust = weap->phys_info.full_rotthrust; + objp->mtype.phys_info.full_thrust = weap->phys_info.full_thrust; + objp->mtype.phys_info.rotvel = weap->phys_info.rotvel; + //objp->mtype.phys_info.rot_thrust = weap->phys_info.full_rot_thrust; + objp->mtype.phys_info.wiggle_amplitude = weap->phys_info.wiggle_amplitude; + objp->mtype.phys_info.wiggles_per_sec = weap->phys_info.wiggles_per_sec; + objp->mtype.phys_info.num_bounces = weap->phys_info.num_bounces; + objp->mtype.phys_info.coeff_restitution = weap->phys_info.coeff_restitution; + objp->lifeleft = weap->life_time; + objp->lifetime = weap->life_time; + objp->ctype.laser_info.thrust_left = weap->thrust_time; + objp->ctype.laser_info.last_drop_time=0; + objp->mtype.phys_info.hit_die_dot = weap->phys_info.hit_die_dot; + //Set impact stuff + objp->impact_size = weap->impact_size; + objp->impact_time = weap->impact_time; + objp->impact_player_damage = weap->impact_player_damage; + objp->impact_generic_damage = weap->impact_generic_damage; + objp->impact_force = weap->impact_force; + // Set up a few flags + objp->mtype.phys_info.flags |= PF_NO_COLLIDE_PARENT; + objp->flags |= OF_USES_LIFELEFT; + // Set up rendering info + objp->render_type=RT_WEAPON; + + if (!((weap->flags & WF_IMAGE_BITMAP) || (weap->flags & WF_IMAGE_VCLIP))) + { + objp->rtype.pobj_info.model_num = weap->fire_image_handle; + objp->rtype.pobj_info.dying_model_num = -1; + PageInPolymodel (weap->fire_image_handle, OBJ_WEAPON, &weap->size); + + objp->rtype.pobj_info.subobj_flags = 0xFFFFFFFF; + objp->flags |= OF_POLYGON_OBJECT; + } + else if (weap->flags & WF_IMAGE_VCLIP) + { + PageInVClip (weap->fire_image_handle); + } + + objp->size = weap->size; + objp->ctype.laser_info.hit_status = WPC_NOT_USED; + if(weap->flags & WF_CUSTOM_SIZE) + { + objp->size = weap->custom_size; + } + objp->lighting_render_type=LRT_STATIC; + objp->lm_object.used=0; + + return ret; +} +int ObjInitFireball (object *objp) +{ + int ret=1; + ASSERT(objp->type == OBJ_FIREBALL); + //Set size & shields + objp->shields = 0; + objp->size= 0; + //Set up control info + SetObjectControlType(objp, CT_EXPLOSION); + //Set up physics info + objp->movement_type = MT_NONE; + objp->mtype.phys_info.num_bounces = PHYSICS_UNLIMITED_BOUNCE; + + //Set up render info + objp->render_type = RT_FIREBALL; + objp->size=Fireballs[objp->id].size; + objp->flags|=OF_USES_LIFELEFT; + objp->lifeleft=Fireballs[objp->id].total_life; + objp->lighting_render_type=LRT_STATIC; + objp->lm_object.used=0; + return ret; +} +int ObjInitMarker(object *objp) +{ + int ret=1; + static int first=1; + static int polynum=-1; + ASSERT(objp->type == OBJ_MARKER); + //Set size & shields + objp->shields = 100; + objp->size= 2.0; + //Set up control info + SetObjectControlType(objp, CT_NONE); + objp->movement_type = MT_NONE; + ObjSetRenderPolyobj(objp,Marker_polynum); + PageInPolymodel(Marker_polynum, OBJ_MARKER, &objp->size); + objp->lm_object.used=0; + objp->lighting_render_type=LRT_STATIC; + return ret; +} +int ObjInitDoor (object *objp,bool reinit) +{ + //Set up movement info + objp->movement_type = MT_NONE; + SetObjectControlType(objp, CT_NONE); + //Set up render info + ObjSetRenderPolyobj(objp,GetDoorImage(objp->id)); + PageInPolymodel (objp->rtype.pobj_info.model_num); + ComputeDefaultSize(OBJ_DOOR, objp->rtype.pobj_info.model_num, &objp->size); + objp->lighting_render_type=LRT_LIGHTMAPS; + //Set shields + if (Doors[objp->id].flags & DF_BLASTABLE) { + objp->flags |= OF_DESTROYABLE; + objp->shields = Doors[objp->id].hit_points; + } + if (!reinit) + objp->lm_object.used=0; + + return 1; +} +int ObjInitRoom(object *objp) +{ + //Set up movement info + objp->movement_type = MT_NONE; + SetObjectControlType(objp, CT_NONE); + //Set up render info + objp->render_type = RT_ROOM; //rendering handled as special case + //I have no idea about this stuff + objp->lighting_render_type=LRT_STATIC; + objp->lm_object.used=0; + objp->flags |= OF_POLYGON_OBJECT; + + return 1; +} +//Set all the type-specific info for this object +//Returns 1 if ok, 0 if error +int ObjInitTypeSpecific(object *objp,bool reinitializing) +{ + switch (objp->type) + { + case OBJ_CLUTTER: + case OBJ_BUILDING: + case OBJ_ROBOT: + case OBJ_POWERUP: return ObjInitGeneric(objp,reinitializing); break; + case OBJ_SHOCKWAVE: return 1; break; + case OBJ_PLAYER: return ObjInitPlayer(objp); break; + case OBJ_CAMERA: return ObjInitCamera(objp); break; + case OBJ_MARKER: return ObjInitMarker(objp); break; + case OBJ_VIEWER: return ObjInitViewer(objp); break; + case OBJ_WEAPON: return ObjInitWeapon(objp); break; + case OBJ_FIREBALL: return ObjInitFireball(objp); break; + case OBJ_DOOR: return ObjInitDoor(objp,reinitializing); break; + case OBJ_ROOM: return ObjInitRoom(objp); break; + case OBJ_DEBRIS: return ObjInitDebris(objp); break; + case OBJ_SHARD: return ObjInitShard(objp); break; + case OBJ_SOUNDSOURCE:return ObjInitSoundSource(objp); break; + case OBJ_WAYPOINT: return ObjInitWaypoint(objp); break; + case OBJ_SPLINTER: return 1; break; +#ifdef _DEBUG + case OBJ_DEBUG_LINE: return ObjInitLine(objp); break; +#endif + break; + default: + Int3(); + return 0; + } +} +//Initializes a new object. All fields not passed in set to defaults. +//Returns 1 if ok, 0 if error +int ObjInit(object *objp,int type,int id,int handle,vector *pos,float creation_time,int parent_handle) +{ + //Zero out object structure to keep weird bugs from happening in uninitialized fields. + //I hate doing this because it seems sloppy, but it's probably better to do it + memset(objp, 0, sizeof(object)); + //Set the stuff that's passed in + objp->type = type; + objp->id = id; + objp->handle = handle; + objp->pos = objp->last_pos = *pos; + objp->parent_handle = parent_handle; + objp->creation_time = creation_time; + objp->osiris_script = NULL; + //Initialize some general stuff + objp->roomnum = -1; + objp->orient = Identity_matrix; + objp->next = objp->prev = -1; + objp->dummy_type = OBJ_NONE; + objp->flags = 0; + objp->size = 0; + objp->change_flags = 0; + objp->generic_nonvis_flags=0; + objp->generic_sent_nonvis=0; + objp->name = NULL; + objp->custom_default_script_name = NULL; + objp->custom_default_module_name = NULL; + objp->contains_type = -1; + objp->lifeleft = 0; + objp->effect_info = NULL; + objp->ai_info = NULL; + objp->dynamic_wb = NULL; + objp->attach_children = NULL; + //Now initialize the type-specific data + return ObjInitTypeSpecific(objp,0); +} +//Re-copies data to each object from the appropriate page for that object type. +//Called after an object page has changed. +void ObjReInitAll() +{ + int objnum; + object *objp; + for (objnum=0,objp=Objects;objnum<=Highest_object_index;objnum++,objp++) + if (objp->type != OBJ_NONE) + ObjInitTypeSpecific(objp,1); +} diff --git a/Descent3/ObjScript.cpp b/Descent3/ObjScript.cpp new file mode 100644 index 000000000..3a9b9f8e2 --- /dev/null +++ b/Descent3/ObjScript.cpp @@ -0,0 +1,247 @@ +/* + * $Logfile: /DescentIII/main/ObjScript.cpp $ + * $Revision: 37 $ + * $Date: 5/22/99 12:14a $ + * $Author: Jeff $ + * + * Object scripting + * + * $Log: /DescentIII/main/ObjScript.cpp $ + * + * 37 5/22/99 12:14a Jeff + * don't init scripts if the object already has em + * + * 36 5/04/99 6:40p Chris + * + * 35 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 34 4/02/99 2:13p Chris + * + * 33 1/13/99 2:27p Jeff + * added an is_dying flag for evt_destroy to determine whether the event + * is due to level end or because it really is dying + * + * 32 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 31 12/31/98 7:34p Jeff + * call EVT_DESTROY from FreeObjectScripts (if a direct call to ObjDelete + * was called, it would never get the event) + * + * 30 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 29 10/18/98 2:59p Jason + * fixes for beta4 + * + * 28 10/08/98 2:39p Chris + * + * 27 10/08/98 2:25p Chris + * Clutter and building can now have scripts + * + * 26 9/17/98 11:11a Chris + * Worked on goal system and improved integration with OSIRIS (better + * EVT_AI_INIT event calling) + * + * 25 8/26/98 1:30p Jason + * fixed client creation bugs + * + * 24 8/19/98 5:37p Samir + * fixed bug in ReinitTriggerScripts. + * + * 23 8/19/98 1:35p Samir + * added ReinitTrigger + * + * 22 8/19/98 12:22p Samir + * is_custom is now valid. + * + * 21 8/18/98 12:57a Samir + * created function to reinitialize scripts from savegame. + * + * 20 8/07/98 7:21p Samir + * changed references to level script exec. + * + * 19 7/28/98 5:44p Samir + * added hooks for level create and destroy + * + * 18 5/26/98 7:06p Samir + * is_custom added to determine if named script is custom of default. + * + * 17 3/19/98 12:57p Chris + * Changed ' + + +void InitTriggerScript(trigger *tp); +void FreeTriggerScript(trigger *tp); + +// We assign all the scripts needed for the level right here. +void AssignScriptsForLevel() +{ + int i; + +//Initialize scripts for the objects + for (i = 0; i <= Highest_object_index; i++) + { + if (Objects[i].type != OBJ_NONE && Objects[i].osiris_script==NULL) { + InitObjectScripts(&Objects[i]); + } + } + +//Initialize scripts for the triggers + for (i=0;icontrol_type == CT_AI ) + { + Osiris_CallEvent(objp,EVT_AI_INIT,&ei); + } + + Osiris_CallEvent(objp,EVT_CREATED,&ei); + + /* + if (Game_mode & GM_MULTI && Netgame.local_role==LR_CLIENT) + D3XExecScript(objp, EVT_CLIENT_CREATED, objp); + else + { + D3XExecScript(objp, EVT_CREATED, objp); + if(objp->control_type == CT_AI) + { + D3XExecScript(objp, EVT_AI_INIT, NULL, REF_OBJTYPE, NULL); + } + } + */ + } +} + + +//frees all scripts for an object. +void FreeObjectScripts(object *objp,bool level_end) +{ + tOSIRISEventInfo ei; + ei.evt_destroy.is_dying = (level_end)?1:0; + + if(!level_end) + Osiris_CallEvent(objp,EVT_DESTROY,&ei); + + Osiris_DetachScriptsFromObject(objp); +} + + +//allocates and initializes the script for a trigger +void InitTriggerScript(trigger *tp) +{ +} + + +//frees the script for a trigger +void FreeTriggerScript(trigger *tp,bool level_end) +{ +} + + diff --git a/Descent3/ObjScript.h b/Descent3/ObjScript.h new file mode 100644 index 000000000..be5d43f36 --- /dev/null +++ b/Descent3/ObjScript.h @@ -0,0 +1,120 @@ +/* + * $Logfile: /DescentIII/main/ObjScript.h $ + * $Revision: 21 $ + * $Date: 1/13/99 2:27p $ + * $Author: Jeff $ + * + * Object scripting + * + * $Log: /DescentIII/main/ObjScript.h $ + * + * 21 1/13/99 2:27p Jeff + * added an is_dying flag for evt_destroy to determine whether the event + * is due to level end or because it really is dying + * + * 20 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 19 10/18/98 2:59p Jason + * fixes for beta4 + * + * 18 8/19/98 1:35p Samir + * added ReinitTrigger + * + * 17 8/18/98 12:57a Samir + * created function to reinitialize scripts from savegame. + * + * 16 5/26/98 7:06p Samir + * is_custom added to determine if named script is custom of default. + * + * 15 4/19/98 9:56p Chris + * AI system is integrated with OSIRIS. Path system is integrated with + * the AI system. Bugs will ensue. + * + * 14 4/15/98 5:56p Chris + * Made the AI system script friendly + * + * 13 3/16/98 11:19a Jason + * got scripts working with multiplayer + * + * 12 2/09/98 11:07a Samir + * Added reset function to script_info. + * + * 11 1/23/98 6:11p Samir + * Addded some trigger functions. + * + * 10 1/23/98 3:06p Samir + * Cleaned up and enhanced script_info structure. + * + * 9 1/22/98 6:23p Samir + * Moved constants from ObjCScript to ObjScript.h + * + * 8 1/21/98 5:02p Samir + * Don't delete pointers in script_info when destroyed. + * + * 7 1/21/98 4:33p Luke + * Null pointers when destroying script_info. + * + * 6 1/20/98 4:12p Samir + * New script housekeeping system. + * + * 5 9/24/97 2:58p Samir + * Moved some script defines from mission.h to ObjScript.h + * + * 4 9/10/97 4:00p Samir + * added define for default level script id. + * + * 3 9/10/97 2:00p Samir + * added extern to Level_script D3X Program pointer. + * + * 2 8/21/97 5:56p Samir + * Took out script specific stuff from ObjInit and moved to ObjScript + * + * 2 6/02/97 1:06p Samir + * Added some more scripting functions. + * + * $NoKeywords: $ + */ + + +#ifndef OBJSCRIPT_H +#define OBJSCRIPT_H + +#include "pstypes.h" +#include "d3x_op.h" +#include "vecmat.h" + +struct object; +struct trigger; + + +// assigns scripts for a level. +void AssignScriptsForLevel(); + +// free scripts for a level +void FreeScriptsForLevel(); + +//allocates and initializes the scripts for an object. +void InitObjectScripts(object *objp,bool do_evt_created=true); + +//frees all scripts for an object. +void FreeObjectScripts(object *objp,bool level_end=false); + +//allocates and initializes the scripts for a trigger. +void InitTriggerScript(trigger *tp); + +//frees all scripts for an trigger. +void FreeTriggerScript(trigger *tp,bool level_end=false); + +//@@// called to reinitialize an object's 'state' given the current script element of object. +//@@// refuses to call EVT_CREATED too. this just restores the state of a script given the current script_info +//@@// op is the object where the script will be assigned. the script should already be freed and ready +//@@void ReinitObjectScripts(object *op, script_info *script, int mem_size, vector *mem); +//@@ +//@@// called to reinitialize a trigger's 'state' given the current script element of trigger. +//@@// refuses to call EVT_CREATED too. this just restores the state of a script given the current script_info +//@@// tp is the trogger where the script will be assigned. the script should already be freed and ready +//@@void ReinitTriggerScripts(trigger *tp, script_info *script, int mem_size, vector *mem); + + +#endif \ No newline at end of file diff --git a/Descent3/OsirisLoadandBind.cpp b/Descent3/OsirisLoadandBind.cpp new file mode 100644 index 000000000..c4f16c43b --- /dev/null +++ b/Descent3/OsirisLoadandBind.cpp @@ -0,0 +1,4143 @@ +/* +* $Logfile: /DescentIII/main/OsirisLoadandBind.cpp $ +* $Revision: 116 $ +* $Date: 4/04/00 12:40p $ +* $Author: Matt $ +* +* OSIRIS functions to load a script DLL and bind to objects +* +* $Log: /DescentIII/main/OsirisLoadandBind.cpp $ + * + * 116 4/04/00 12:40p Matt + * Fixed mem leak in debugging code + * + * 115 10/25/99 11:57a Jeff + * ifdef'd some Macintosh pragmas + * + * 114 10/22/99 11:33a Matt + * Mac merge + * + * 113 9/29/99 8:04a Kevin + * Demo playback fixes + * + * 112 5/19/99 3:54p Jeff + * error is scripts are being bound to an object more than once + * + * 111 5/19/99 11:26a Matt + * Added multisafe functions & Dallas actions for showing a timer on the + * screen and adding custom HUD messages. + * + * 110 5/12/99 2:24p Jeff + * Descent3 now has a setable temp directory for all temp files + * + * 109 5/10/99 11:55a 3dsmax + * removed an int3 + * + * 108 5/08/99 6:45p Chris + * safety precausion to unload modules if they are not already + * + * 107 5/08/99 4:31a Jeff + * don't int3 on multiplayer modules + * + * 106 5/07/99 8:27p Jeff + * trying to hunt down why some modules hang around after they are done + * + * 105 5/02/99 6:41a Jeff + * created 'test/temp' function to force unload all osiris modules + * + * 104 5/01/99 1:40a Jeff + * put in hack to keep levelscripts from even being attempted to be called + * by a client during disconnect + * + * 103 4/24/99 12:09a Jeff + * added path value functions + * + * 102 4/20/99 8:56p Chris + * Fixed problem with robots not being able to open locked doors that a + * player has the key for. + * + * 101 4/20/99 12:59p Jeff + * fixed loading script bug + * + * 100 4/20/99 12:02a Jeff + * made linux filename friendly + * + * 99 4/16/99 12:19a Jeff + * linux wants stdcall modifiers before parens, unlike windows + * + * 98 4/14/99 4:19a Jeff + * more case mismatch fixes in #includes + * + * 97 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 96 3/17/99 11:48a Jeff + * exported function to get what language we are using + * + * 95 3/11/99 9:10p Jeff + * int3 only when restore system state if not playing back demo (because + * scripts are not really used in demos) + * + * 94 3/10/99 6:44p Jeff + * fixed exported game cinematic function for canned cinematics, so it's + * prototype doesn't change (oem patch friendly) + * + * 93 3/10/99 6:21p Jeff + * many fixes to demo system. Fixed IGC so cameras move...fixed osiris to + * be restored correctly, and it handles errors on restore + * + * 92 3/09/99 12:34p Jeff + * extract scripts to custom\cache + * + * 91 3/05/99 5:25p Jeff + * fixed saving demos in multiplayer...required adding a parameter to + * Osiris_LoadMissionModule() to pass in the name of the d3m file (for + * storage) + * + * 90 3/03/99 5:13a Jeff + * fix bug loading dlls from hog files (wouldn't load..comparing something + * like aigame to aigame.dll) + * + * 89 3/03/99 3:01a Chris + * Exported the difficulty stuff to OSIRIS + * + * 88 3/03/99 12:12a Chris + * + * 87 3/03/99 12:07a Chris + * Added the two new OSIRIS predefs for AI path finding + * + * 86 3/01/99 3:45p Jeff + * fixed up save and restore a little bit + * + * 85 3/01/99 11:50a Jeff + * call EVT_SAVESTATE and EVT_RESTORESTATE for levels + * + * 84 2/28/99 11:30p Chris + * FindObjOfType and OSIRIS controllable deaths + * + * 83 2/28/99 8:52p Jeff + * added functions to enable/disable creation events + * + * 82 2/23/99 12:54p Matt + * Removed Int3() (Jeff on MattT's machine) + * + * 81 2/22/99 8:17p Chris + * Added the LGoal_Value() function to OSIRIS + * + * 80 2/21/99 8:36p Jeff + * misc changes to handle new matcen and path types of dallas + * + * 79 2/21/99 8:04p Jeff + * better handling of out-of-sync scripts + * + * 78 2/21/99 5:51p Chris + * FIxed a bug with OBJV_C_TYPE (It should have been OBJV_I_TYPE). Added + * Obj_FindType() + * + * 77 2/18/99 4:28p Jeff + * added lookup functions for matcen, paths, level goals + * + * 76 2/18/99 11:06a Jeff + * added event masks (so you can enable/disable object/trigger/level + * events) + * + * 75 2/17/99 3:27a Jeff + * added game checksum function to handle out-of-sync dlls + * + * 74 2/15/99 11:38a Jeff + * debug mprintfs when extracting dlls + * + * 73 2/14/99 1:16a Jeff + * exported canned cinematic + * + * 72 2/13/99 6:11p Jeff + * fixed bug when extracting scripts for the second+ time + * + * 71 2/12/99 6:24p Kevin + * + * 70 2/12/99 6:22p Kevin + * Fixed a thingy for Jeffy + * + * 69 2/11/99 2:56a Jeff + * improvements to introcam + * + * 68 2/10/99 7:12p Jeff + * oops...OpenLibrary returns 0 not -1 + * + * 67 2/10/99 6:56p Jeff + * handle invalid libraries when extracting scripts + * + * 66 2/10/99 3:41p Jeff + * removed unneeded assert + * + * 65 2/10/99 3:29p Jeff + * extracted dll manager knows the difference between game hogs and + * mission hogs + * + * 64 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 63 2/08/99 2:51p Jeff + * removed dead evt_ain events that were commented out + * + * 62 2/06/99 7:05p Jeff + * turned off osiris debug messages by default + * + * 61 2/05/99 9:06p Jeff + * added aux events for a couple new ainotify's + * + * 60 2/03/99 1:51a Jeff + * added predefs for ship permissions + * + * 59 2/03/99 12:43a Chris + * Added Obj_GetGroundPos + * + * 58 2/02/99 12:46p Jeff + * handy mprintfs on save/restore state + * + * 57 2/01/99 5:29p Jeff + * obj_dummy's can be assigned scripts + * + * 56 1/31/99 10:40p Jeff + * exported Osiris_CancelTimerID to DLLs + * + * 55 1/31/99 10:16p Matt + * Added Osiris_CancelTimerID() + * + * 54 1/31/99 8:52p Jeff + * new in game cinematics system finished + * + * 51 1/25/99 8:18a Chris + * Made the AUX Notify remap more obvious + * + * 50 1/25/99 8:13a Chris + * Made the AUX goal remap more obvious... + * + * 49 1/24/99 8:18p Chris + * Updated AI and OSIRIS. Externalized fireball constants for spew and + * sparks. Added CreateRandomSparks to OSIRIS, renamed a bunch of AI + * Notify names to use underscores. Fixed a memory access leak in the + * matcen effect code. + * + * 48 1/24/99 6:36p Jeff + * fixed trigger call event bug (wasn't using the correct DLLid) + * + * 47 1/22/99 8:54p Jeff + * added custom-default script overrides + * + * 46 1/22/99 6:53p Chris + * Fixed LoadandBind Aux notify problems, fixed static path problems, + * fixed AIF_FORCE_AWARE Problems, improved path code + * + * 45 1/22/99 5:15p Jeff + * added a key to toggle osiris debug messages + * + * 44 1/22/99 4:47p Jeff + * + * 43 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 42 1/20/99 4:17p Jeff + * added ai notify child events and predefs to get ids of sound, room, + * trigger, object names + * + * 41 1/20/99 3:46a Jeff + * exported boss introduction functions + * + * 39 1/16/99 2:19p Jeff + * only save and use the base filename of a dll (to keep it platform + * independent) + * + * 38 1/16/99 1:29p Jeff + * updated the OMMS system to be faster. Removed the event for restoring + * it, since it is require to surround code in attach/detach blocks. + * + * 37 1/16/99 10:39a Jeff + * added mission memory management to Osiris...only slightly tested. Need + * to solve game save/restore problem still + * + * 36 1/15/99 5:59p Chris + * + * 35 1/13/99 5:20p Jeff + * added 4 new file operation predefs + * + * 34 1/13/99 4:05p Jeff + * when loading a script dll, a string table is looked for and + * automatically loaded and passed to the newly loaded dll on init for + * use. + * + * 33 1/13/99 3:25a Chris + * Added Obj_Burning and Obj_IsEffect to OSIRIS + * + * 32 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 31 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 30 1/11/99 12:53p Jeff + * added a function that given a module name it will make sure it has an + * extension. Made Osiris friendly with modules with no extension + * + * 29 1/08/99 2:02p Jeff + * handle the case of extracting too many scripts + * + * 28 1/08/99 2:00p Jeff + * fixed minor bug with hog extraction (mem leak) and made it so you can + * keep calling ExtractScripts for multiple hogs + * + * 27 1/08/99 1:50p Jeff + * finished support on loading scripts from hogs + * + * 26 1/07/99 10:52p Jeff + * wrote skeleton of hog script file extraction + * + * 25 1/04/99 6:43p Jeff + * added support to get/set mission flag values + * + * 24 1/04/99 12:42p Chris + * Updated OSIRIS with "xxxx_external.h" files and Obj_Value, and + * Matcen_XXX functions + * + * 23 1/04/99 12:23p Jeff + * added to evt_use and support for mission module scripts + * + * 22 12/31/98 7:33p Jeff + * improved Osiris timers to have unique IDs, along with adding an + * EVT_TIMERCANCEL and a new flag to autodestruct the timer if a given + * object handle dies. + * + * 21 12/31/98 2:37p Jeff + * added SaveRestoreState import function + * + * 20 12/30/98 5:17p Jeff + * changes made to handle script name override, to override the name of + * the script to use in a module. + * + * 19 12/30/98 3:46p Chris + * Incremental AI changes + * + * 18 12/30/98 12:31p Jeff + * when freeing object scripts, the type is changed before it's freed in + * ObjDelete. Also, when destroyed, OBJ_ROBOT becomes OBJ_DEBRIS and + * Osiris was not handling this. + * + * 17 12/23/98 12:10p Chris + * Added Gametime and Frametime to OSIRIS + * + * 16 12/22/98 4:51p Chris + * Added a ObjCreate predefined function + * + * 15 12/22/98 4:19p Jeff + * exported msafe_DoPowerup + * + * 14 12/22/98 2:46p Chris + * Added the AIValue predefined function + * + * 13 12/18/98 6:00p Jeff + * added support for door scripts in new osiris + * + * 12 12/18/98 3:10p Mark + * removed int3 + * + * 11 12/18/98 11:07a Jeff + * triggers are implemented + * + * 10 12/18/98 10:42a Jeff + * added timer support and auto-save memory manager for new osiris. New + * events evt_memrestore state added also + * + * 9 12/17/98 7:26p Jeff + * added memory manager for autosaving script data + * + * 8 12/17/98 5:44p Jeff + * created timer system for osiris and save restore events + * + * 7 12/17/98 2:27p Keneta + * Commented out an Int3(), as per Jeff's directions. (MattT) + * + * 6 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 5 12/16/98 10:21p Jeff + * added level and trigger event call functions. added trigger script + * functionality (still primative) + * + * 4 12/16/98 5:37p Jason + * added new multisafe architecture + * + * 3 12/16/98 10:58a Jeff + * checked in so Jason can use + * + * 2 12/14/98 11:32a Jeff + * started work on osiris load and bind functions +* +* $NoKeywords: $ +*/ + + +#include "osiris_dll.h" +#include "pserror.h" +#include "mono.h" +#include "CFILE.H" +#include "ddio.h" +#include "manage.h" +#include +#include "mem.h" +#include "DllWrappers.h" +#include "objinfo.h" +#include "multisafe.h" +#include "osiris_predefs.h" +#include "trigger.h" +#include "game.h" +#include "multi.h" +#include "door.h" +#include "localization.h" +#include "player.h" +#include "gamecinematics.h" +#include "demofile.h" + +#ifdef _DEBUG +#define OSIRISDEBUG +#endif + +#ifdef MACINTOSH +#pragma opt_dead_code off +#endif + +bool Show_osiris_debug = false; + +#define MAX_LOADED_MODULES 64 //maximum number of dlls that can be loaded at a time + +#define OSIMF_INUSE 0x1 //slot in use +#define OSIMF_LEVEL 0x2 //level module +#define OSIMF_DLLELSEWHERE 0x4 //mission module +#define OSIMF_INTEMPDIR 0x8 //the dll was extracted from a hog and it is in a temp file +#define OSIMF_NOUNLOAD 0x10 //the dll should not be unloaded if the reference count is + //0, only when the level ends + +// The exported DLL function call prototypes +#if defined(__LINUX__) +typedef char DLLFUNCCALL (*InitializeDLL_fp )( tOSIRISModuleInit *function_list ); +typedef void DLLFUNCCALL (*ShutdownDLL_fp )( void ); +typedef int DLLFUNCCALL (*GetGOScriptID_fp )( char *name ,ubyte isdoor); +typedef void DLLFUNCCALL *(*CreateInstance_fp )( int id ); +typedef void DLLFUNCCALL (*DestroyInstance_fp )( int id, void *ptr ); +typedef short DLLFUNCCALL (*CallInstanceEvent_fp )( int id, void *ptr, int event, tOSIRISEventInfo *data ); +typedef int DLLFUNCCALL (*GetTriggerScriptID_fp)( int trigger_room, int trigger_face ); +typedef int DLLFUNCCALL (*GetCOScriptList_fp )( int **list, int **id_list ); +typedef int DLLFUNCCALL (*SaveRestoreState_fp )( void *file_ptr, ubyte saving_state ); +#else +typedef char( DLLFUNCCALL *InitializeDLL_fp )( tOSIRISModuleInit *function_list ); +typedef void( DLLFUNCCALL *ShutdownDLL_fp )( void ); +typedef int ( DLLFUNCCALL *GetGOScriptID_fp )( char *name ,ubyte isdoor); +typedef void*( DLLFUNCCALL *CreateInstance_fp )( int id ); +typedef void( DLLFUNCCALL *DestroyInstance_fp )( int id, void *ptr ); +typedef short( DLLFUNCCALL *CallInstanceEvent_fp )( int id, void *ptr, int event, tOSIRISEventInfo *data ); +typedef int ( DLLFUNCCALL *GetTriggerScriptID_fp)( int trigger_room, int trigger_face ); +typedef int ( DLLFUNCCALL *GetCOScriptList_fp )( int **list, int **id_list ); +typedef int ( DLLFUNCCALL *SaveRestoreState_fp )( void *file_ptr, ubyte saving_state ); +#endif + +typedef struct tRefObj +{ + int objnum; + int type,id; + bool dummy; + tRefObj *next; +}tRefObj; + +typedef struct{ + ubyte flags; + ubyte extracted_id; + ushort reference_count; + InitializeDLL_fp InitializeDLL; + ShutdownDLL_fp ShutdownDLL; + GetGOScriptID_fp GetGOScriptID; + CreateInstance_fp CreateInstance; + DestroyInstance_fp DestroyInstance; + CallInstanceEvent_fp CallInstanceEvent; + GetTriggerScriptID_fp GetTriggerScriptID; + GetCOScriptList_fp GetCOScriptList; + SaveRestoreState_fp SaveRestoreState; + module mod; + char *module_name; + char **string_table; + int strings_loaded; + +#ifdef OSIRISDEBUG + tRefObj *RefRoot; +#endif +}tOSIRISModule; + +tOSIRISModule OSIRIS_loaded_modules[MAX_LOADED_MODULES]; +tOSIRISModuleInit Osiris_module_init; +struct{ + bool level_loaded; + ushort num_customs; + ushort dll_id; + int *custom_ids; + int *custom_handles; + void *instance; +}tOSIRISCurrentLevel; +struct{ + bool mission_loaded; + ushort dll_id; +}tOSIRISCurrentMission; + +#define OESF_USED 0x0001 +#define OESF_MISSION 0x0002 //mission dlls +typedef struct +{ + ubyte flags; + char *temp_filename; + char *real_filename; +}tExtractedScriptInfo; +tExtractedScriptInfo OSIRIS_Extracted_scripts[MAX_LOADED_MODULES]; +char *OSIRIS_Extracted_script_dir = NULL; + +// Osiris_CreateModuleInitStruct +// Purpose: +// This function initializes a Module Init Struct with all the needed data to get sent +// to the module during initialization. +void Osiris_CreateModuleInitStruct(tOSIRISModuleInit *mi); + +void Osiris_RestoreOMMS(CFILE *file); +void Osiris_SaveOMMS(CFILE *file); + +// Osiris_CreateGameChecksum +// Purpose: +// Generates a checksum of the game's structures, to give to the modules +// so they can use to compare to the time when they were compiled, to see +// if they are compatible. +uint Osiris_CreateGameChecksum(void); +// Osiris_IsEventEnabled +// Purpose: +// Returns true if the event is allowed to be called +bool Osiris_IsEventEnabled(int event); + +void Cinematic_StartCannedScript(tCannedCinematicInfo *info); + +// Osiris_DumpLoadedObjects +// +// Debug. Dumps all loaded objects to given file +void Osiris_DumpLoadedObjects(char *file); +void Osiris_ForceUnloadModules(void); + + +uint Osiris_game_checksum; + +ubyte Osiris_event_mask = OEM_OBJECTS|OEM_TRIGGERS|OEM_LEVELS; +bool Osiris_create_events_disabled = false; +bool Osiris_level_script_loaded = false; + +#define HAVECUSTOMONLY(type) (type==OBJ_CAMERA) +#define CANBEASSIGNEDSCRIPT(obj) ( (obj) && ( (obj->type==OBJ_ROBOT) || (obj->type==OBJ_BUILDING) || (obj->type==OBJ_POWERUP) || (obj->type==OBJ_CLUTTER) || (obj->type==OBJ_DOOR) || (obj->type==OBJ_CAMERA) || (obj->type==OBJ_DUMMY) ) ) +#define CANHAVEANYSCRIPT(obj) ( CANBEASSIGNEDSCRIPT(obj) || ( obj && ( (obj->type==OBJ_DEBRIS) || (obj->type==OBJ_GHOST)) ) ) +#define CANTYPEBEASSIGNEDSCRIPT(type) ( (type==OBJ_ROBOT) || (type==OBJ_BUILDING) || (type==OBJ_POWERUP) || (type==OBJ_CLUTTER) || (type==OBJ_DOOR) || (type==OBJ_CAMERA) ) +//======================================================================== + +// Osiris_ShutdownModuleLoader +// Purpose: +// Closes down the OSIRIS module loader and handling system +void Osiris_ShutdownModuleLoader(void) +{ + mprintf((0,"OSIRIS: Shutting down module manager\n")); + int i; + for( i=0; i=MAX_LOADED_MODULES) + return; + + if(OSIRIS_loaded_modules[id].flags&OSIMF_INUSE){ + +#ifdef OSIRISDEBUG + ASSERT(OSIRIS_loaded_modules[id].RefRoot==NULL);//not all objects freed +#endif + + if(OSIRIS_loaded_modules[id].flags&OSIMF_LEVEL){ + //we're freeing the level dll + tOSIRISCurrentLevel.level_loaded = false; + tOSIRISCurrentLevel.num_customs = 0; + tOSIRISCurrentLevel.dll_id = 0; + tOSIRISCurrentLevel.custom_ids = NULL; + tOSIRISCurrentLevel.custom_handles = NULL; + tOSIRISCurrentLevel.instance = NULL; + } + + if(!(OSIRIS_loaded_modules[id].flags&OSIMF_DLLELSEWHERE)){ + + //this is a DLL belonging to us + if(OSIRIS_loaded_modules[id].ShutdownDLL){ + //call the shutdown function so the DLL can perform what it needs + OSIRIS_loaded_modules[id].ShutdownDLL(); + } + + if(OSIRIS_loaded_modules[id].string_table!=NULL){ + DestroyStringTable(OSIRIS_loaded_modules[id].string_table,OSIRIS_loaded_modules[id].strings_loaded); + } + mod_FreeModule(&OSIRIS_loaded_modules[id].mod); + } + + if(tOSIRISCurrentMission.mission_loaded && id==tOSIRISCurrentMission.dll_id){ + //we're freeing the mission dll + tOSIRISCurrentMission.mission_loaded = false; + tOSIRISCurrentMission.dll_id = 0; + } + + OSIRIS_loaded_modules[id].CallInstanceEvent = NULL; + OSIRIS_loaded_modules[id].CreateInstance = NULL; + OSIRIS_loaded_modules[id].DestroyInstance = NULL; + OSIRIS_loaded_modules[id].GetCOScriptList = NULL; + OSIRIS_loaded_modules[id].GetGOScriptID = NULL; + OSIRIS_loaded_modules[id].GetTriggerScriptID = NULL; + OSIRIS_loaded_modules[id].InitializeDLL = NULL; + OSIRIS_loaded_modules[id].SaveRestoreState = NULL; + OSIRIS_loaded_modules[id].string_table = NULL; + OSIRIS_loaded_modules[id].strings_loaded = 0; + OSIRIS_loaded_modules[id].flags = 0; + OSIRIS_loaded_modules[id].reference_count = 0; + +#ifdef OSIRISDEBUG + ASSERT(OSIRIS_loaded_modules[id].RefRoot == NULL); + OSIRIS_loaded_modules[id].RefRoot = NULL; +#endif + + if(OSIRIS_loaded_modules[id].module_name){ + mem_free(OSIRIS_loaded_modules[id].module_name); + OSIRIS_loaded_modules[id].module_name = NULL; + } + } +} + +void Osiris_ForceUnloadModules(void) +{ + for(int j=0;jobjnum,node->type,(node->dummy)?"(Dummy)":"",node->id); + cf_WriteString(f,buffy); + node = node->next; + } + } + } + + cfclose(f); +#endif +} + + +// Osiris_FindLoadedModule +// Purpose: +// Given the name of a module, it returns the id of a loaded OSIRIS module. -1 if it isn't loaded. +int Osiris_FindLoadedModule(char *module_name) +{ + //search through the list of loaded modules and see if we can find a match + //strip off the extension + char real_name[_MAX_PATH]; + ddio_SplitPath(module_name,NULL,real_name,NULL); + + int i; + for( i=0; i=MAX_LOADED_MODULES) + return; + if(OSIRIS_loaded_modules[module_id].flags&OSIMF_INUSE) + { + //the module is in use + if(Show_osiris_debug) + { + mprintf((0,"OSIRIS: Decrementing reference count for module (%s)\n",OSIRIS_loaded_modules[module_id].module_name)); + } + OSIRIS_loaded_modules[module_id].reference_count--; + + ASSERT(OSIRIS_loaded_modules[module_id].reference_count>=0); + + if(OSIRIS_loaded_modules[module_id].reference_count<=0) + { + + if(OSIRIS_loaded_modules[module_id].flags&OSIMF_NOUNLOAD) + { + + ASSERT(!(OSIRIS_loaded_modules[module_id].flags&OSIMF_LEVEL)); //level modules cannot be set static + ASSERT(!(OSIRIS_loaded_modules[module_id].flags&OSIMF_DLLELSEWHERE)); //mission modules cannot be set static + + //do not unload this module, due to the forced stay in memory + mprintf((0,"OSIRIS: Module (%s) staying in memory due to static flag\n",OSIRIS_loaded_modules[module_id].module_name)); + }else{ + //time to unload this module + if(Show_osiris_debug) + { + mprintf((0,"OSIRIS: Module (%s) reference count is at 0, unloading\n",OSIRIS_loaded_modules[module_id].module_name)); + } + Osiris_FreeModule(module_id); + } + } + }else{ + mprintf((0,"OSIRIS: Trying to unload a module (%d) that is not in use!\n",module_id)); + Int3(); + } +} + +// Osiris_UnloadLevelModule +// Purpose: +// Instead of saving the handle returned from Osiris_LoadLevelModule() and calling +// Osiris_UnloadModule() with that handle, you can just call this, instead of the call +// to Osiris_UnloadModule(). +void Osiris_UnloadLevelModule(void) +{ + Osiris_level_script_loaded = false; + + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return; + } + + //free allocated scripts (we can do this, because the root node will just become + //NULL and once the first call to allocate memory is used, it'll automatically work) + Osiris_CloseMemoryManager(); + + if(tOSIRISCurrentLevel.level_loaded){ + int dll_id = tOSIRISCurrentLevel.dll_id; + + // free up all instances of trigger scripts still around + for (int i=0;i0) + { + ddio_MakePath(adjusted_name,ppath,adjusted_fname,NULL); + }else + { + strcpy(adjusted_name,adjusted_fname); + } + + //determine real name of script + mod_GetRealModuleName(adjusted_name,modfilename); + + int exist = cfexist(modfilename); + switch(exist){ + case CF_ON_DISK: + ddio_MakePath(fullpath,LocalScriptDir,modfilename,NULL); + return -1; + break; + case CF_IN_LIBRARY: + { + ASSERT(OSIRIS_Extracted_script_dir); + if(!OSIRIS_Extracted_script_dir) + return -2; + + //search through our list of extracted files to find it... + for(int i=0;i=MAX_LOADED_MODULES){ + //no slots available + mprintf((0,"OSIRIS: Osiris_LoadLevelModule(%s): No available slots\n",module_name)); + Int3(); + return -4; + } + + OSIRIS_loaded_modules[loaded_id].flags = 0; //set this to 0 as we fill in the data + + char fullpath[_MAX_PATH],basename[_MAX_PATH]; + int ret_val = _get_full_path_to_module(module_name,fullpath,basename); + switch(ret_val){ + case -2: + //the module does not exist + mprintf((0,"OSIRIS: Osiris_LoadLevelModule(%s): Module doesn't exist\n",module_name)); + return -1; + break; + case -1: + //the module is in data\scripts + break; + default: + //the module was an extracted file + mprintf((0,"OSIRIS: Found module (%s) in a temp file\n",basename)); + OSIRIS_loaded_modules[loaded_id].flags |= OSIMF_INTEMPDIR; + OSIRIS_loaded_modules[loaded_id].extracted_id = ret_val; + break; + } + + //the module exists, now attempt to load it + if(!mod_LoadModule(&OSIRIS_loaded_modules[loaded_id].mod,fullpath)){ + //there was an error trying to load the module + mprintf((0,"OSIRIS: Osiris_LoadLevelModule(%s): Unable to load module\n",module_name)); + Int3(); + return -3; + } + + //the module has loaded, attempt to import all the level functions + tOSIRISModule *osm = &OSIRIS_loaded_modules[loaded_id]; + module *mod = &osm->mod; + + //there are 9 functions we need to import + // InitializeDLL@4 + // ShutdownDLL@0 + // GetGOScriptID@4 + // GetTriggerScriptID@4 + // GetCOScriptList@8 + // CreateInstance@4 + // DestroyInstance@8 + // CallInstanceEvent@16 + // SaveRestoreState@8 + + osm->InitializeDLL = (InitializeDLL_fp) mod_GetSymbol(mod,"InitializeDLL",4); + osm->ShutdownDLL = (ShutdownDLL_fp) mod_GetSymbol(mod,"ShutdownDLL",0); + osm->GetGOScriptID = (GetGOScriptID_fp) mod_GetSymbol(mod,"GetGOScriptID",8); + osm->GetTriggerScriptID = (GetTriggerScriptID_fp)mod_GetSymbol(mod,"GetTriggerScriptID",8); + osm->GetCOScriptList = (GetCOScriptList_fp) mod_GetSymbol(mod,"GetCOScriptList",8); + osm->CreateInstance = (CreateInstance_fp) mod_GetSymbol(mod,"CreateInstance",4); + osm->DestroyInstance = (DestroyInstance_fp) mod_GetSymbol(mod,"DestroyInstance",8); + osm->CallInstanceEvent = (CallInstanceEvent_fp)mod_GetSymbol(mod,"CallInstanceEvent",16); + osm->SaveRestoreState = (SaveRestoreState_fp) mod_GetSymbol(mod,"SaveRestoreState",8); + + osm->flags |= OSIMF_INUSE|OSIMF_LEVEL; + osm->module_name = mem_strdup(basename); + osm->reference_count= 1; + +#ifdef OSIRISDEBUG + ASSERT(osm->RefRoot == NULL); + osm->RefRoot = NULL; +#endif + + + //make sure all of the functions imported ok + if( !osm->InitializeDLL || + !osm->ShutdownDLL || + !osm->GetGOScriptID || + !osm->GetTriggerScriptID|| + !osm->GetCOScriptList || + !osm->CreateInstance || + !osm->DestroyInstance || + !osm->SaveRestoreState || + !osm->CallInstanceEvent){ + //there was an error importing a function + mprintf((0,"OSIRIS: Osiris_LoadLevelModule(%s) couldn't import function.\n",module_name)); + Int3(); + osm->flags = 0; + if(osm->module_name) + mem_free(osm->module_name); + osm->module_name = NULL; + mod_FreeModule(mod); + return -3; + } + + //check to see if there is a corresponding string table to load + char stringtablename[_MAX_PATH]; + strcpy(stringtablename,basename); + strcat(stringtablename,".str"); + + if(cfexist(stringtablename)){ + //there is a string table, load it up + bool ret = CreateStringTable(stringtablename,&osm->string_table,&osm->strings_loaded); + if(!ret){ + mprintf((0,"OSIRIS: Unable to load string table (%s) for (%s)\n",stringtablename,basename)); + Int3(); + osm->string_table = NULL; + osm->strings_loaded = 0; + } + }else{ + osm->string_table = NULL; + osm->strings_loaded = 0; + } + + Osiris_module_init.string_count = osm->strings_loaded; + Osiris_module_init.string_table = osm->string_table; + Osiris_module_init.module_is_static = false; + Osiris_module_init.script_identifier = osm->module_name; + Osiris_module_init.module_identifier = loaded_id; + + //when we get to this point we nearly have a loaded module, we just need to initialize it + if(! osm->InitializeDLL(&Osiris_module_init) ){ + //there was an error initializing the module + mprintf((0,"OSIRIS: Osiris_LoadLevelModule(%s) error initializing module.\n",basename)); + if(osm->string_table){ + DestroyStringTable(osm->string_table,osm->strings_loaded); + } + osm->flags = 0; + if(osm->module_name) + mem_free(osm->module_name); + osm->module_name = NULL; + osm->string_table = NULL; + osm->strings_loaded = 0; + mod_FreeModule(mod); + return -2; + } + + // intialize trigger scripts + for (int i = 0; i < Num_triggers; i++ ){ + int script_id; + void *instance; + + Triggers[i].osiris_script.script_id = -1; + Triggers[i].osiris_script.script_instance = NULL; + + script_id = osm->GetTriggerScriptID(Triggers[i].roomnum,Triggers[i].facenum); + if(script_id!=-1){ + //the trigger was found + instance = osm->CreateInstance(script_id); + if(!instance){ + mprintf((0,"OSIRIS: Unable to create instance for trigger script (%d)\n",i)); + }else{ + Triggers[i].osiris_script.script_id = script_id; + Triggers[i].osiris_script.script_instance = instance; + } + } + } + + //we have a successful module load + tOSIRISCurrentLevel.level_loaded = true; + tOSIRISCurrentLevel.dll_id = loaded_id; + tOSIRISCurrentLevel.num_customs = OSIRIS_loaded_modules[loaded_id].GetCOScriptList(&tOSIRISCurrentLevel.custom_handles,&tOSIRISCurrentLevel.custom_ids); + tOSIRISCurrentLevel.instance = OSIRIS_loaded_modules[loaded_id].CreateInstance(0);//level scripts always have id of 0 in a level dll + + mprintf((0,"OSIRIS: Level Module (%s) loaded successfully (%d custom handles)\n",basename,tOSIRISCurrentLevel.num_customs)); + Osiris_level_script_loaded = true; + return loaded_id; +} + +// Osiris_LoadGameModule +// Purpose: +// Given a module name, it will attempt to load the module as a game module. If it succeeds +// it will return the id of the module where it has been loaded. If the module was already loaded +// before calling this function, it will return the id to where the module is, and will not reload +// the module. Returns -1 if the module does not exist. Returns -2 if the module couldn't initialize. +// Returns -3 if the module is not a game module. Returns -4 if no module slots are available. +int Osiris_LoadGameModule(char *module_name) +{ + if(module_name[0]=='\0'){ + return -1; + } + + int loaded_id; + loaded_id = Osiris_FindLoadedModule(module_name); + if(loaded_id!=-1){ + //the module is already loaded + OSIRIS_loaded_modules[loaded_id].reference_count++; + if(Show_osiris_debug) + { + mprintf((0,"OSIRIS: Game Module (%s) reference count increased to %d\n",module_name,OSIRIS_loaded_modules[loaded_id].reference_count)); + } + return loaded_id; + } + + //the module hasn't been loaded yet, find an available slot. + for(loaded_id = 0; loaded_id=MAX_LOADED_MODULES){ + //no slots available + mprintf((0,"OSIRIS: Osiris_LoadGameModule(%s): No available slots\n",module_name)); + Int3(); + return -4; + } + + OSIRIS_loaded_modules[loaded_id].flags = 0; //set this to 0 as we fill in the data + + char fullpath[_MAX_PATH],basename[_MAX_PATH]; + int ret_val = _get_full_path_to_module(module_name,fullpath,basename); + switch(ret_val){ + case -2: + //the module does not exist + mprintf((0,"OSIRIS: Osiris_LoadLevelModule(%s): Module doesn't exist\n",module_name)); + return -1; + break; + case -1: + //the module is in data\scripts + break; + default: + //the module was an extracted file + mprintf((0,"OSIRIS: Found module (%s) in a temp file\n",basename)); + OSIRIS_loaded_modules[loaded_id].flags |= OSIMF_INTEMPDIR; + OSIRIS_loaded_modules[loaded_id].extracted_id = ret_val; + break; + } + + //the module exists, now attempt to load it + if(!mod_LoadModule(&OSIRIS_loaded_modules[loaded_id].mod,fullpath)){ + //there was an error trying to load the module + mprintf((0,"OSIRIS: Osiris_LoadGameModule(%s): Unable to load module\n",module_name)); + Int3(); + return -3; + } + + //the module has loaded, attempt to import all the level functions + tOSIRISModule *osm = &OSIRIS_loaded_modules[loaded_id]; + module *mod = &osm->mod; + + //there are 7 functions we need to import + // InitializeDLL@4 + // ShutdownDLL@0 + // GetGOScriptID@4 + // CreateInstance@4 + // DestroyInstance@8 + // CallInstanceEvent@16 + // SaveRestoreState@8 + + osm->InitializeDLL = (InitializeDLL_fp) mod_GetSymbol(mod,"InitializeDLL",4); + osm->ShutdownDLL = (ShutdownDLL_fp) mod_GetSymbol(mod,"ShutdownDLL",0); + osm->GetGOScriptID = (GetGOScriptID_fp) mod_GetSymbol(mod,"GetGOScriptID",8); + osm->GetTriggerScriptID = NULL; + osm->GetCOScriptList = NULL; + osm->CreateInstance = (CreateInstance_fp) mod_GetSymbol(mod,"CreateInstance",4); + osm->DestroyInstance = (DestroyInstance_fp) mod_GetSymbol(mod,"DestroyInstance",8); + osm->CallInstanceEvent = (CallInstanceEvent_fp)mod_GetSymbol(mod,"CallInstanceEvent",16); + osm->SaveRestoreState = (SaveRestoreState_fp) mod_GetSymbol(mod,"SaveRestoreState",8); + + osm->flags |= OSIMF_INUSE; + osm->module_name = mem_strdup(basename); + osm->reference_count= 1; + +#ifdef OSIRISDEBUG + ASSERT(osm->RefRoot == NULL); + osm->RefRoot = NULL; +#endif + + + //make sure all of the functions imported ok + if( !osm->InitializeDLL || + !osm->ShutdownDLL || + !osm->GetGOScriptID || + !osm->CreateInstance || + !osm->DestroyInstance || + !osm->SaveRestoreState || + !osm->CallInstanceEvent){ + //there was an error importing a function + mprintf((0,"OSIRIS: Osiris_LoadGameModule(%s) couldn't import function.\n",basename)); + Int3(); + osm->flags = 0; + if(osm->module_name) + mem_free(osm->module_name); + osm->module_name = NULL; + mod_FreeModule(mod); + return -3; + } + + //check to see if there is a corresponding string table to load + char stringtablename[_MAX_PATH]; + strcpy(stringtablename,basename); + strcat(stringtablename,".str"); + + if(cfexist(stringtablename)){ + //there is a string table, load it up + bool ret = CreateStringTable(stringtablename,&osm->string_table,&osm->strings_loaded); + if(!ret){ + mprintf((0,"OSIRIS: Unable to load string table (%s) for (%s)\n",stringtablename,basename)); + Int3(); + osm->string_table = NULL; + osm->strings_loaded = 0; + } + }else{ + osm->string_table = NULL; + osm->strings_loaded = 0; + } + Osiris_module_init.string_count = osm->strings_loaded; + Osiris_module_init.string_table = osm->string_table; + Osiris_module_init.module_is_static = false; //set this to false to avoid a fake true + Osiris_module_init.script_identifier = osm->module_name; + Osiris_module_init.module_identifier = loaded_id; + + //when we get to this point we nearly have a loaded module, we just need to initialize it + if(! osm->InitializeDLL(&Osiris_module_init) ){ + //there was an error initializing the module + mprintf((0,"OSIRIS: Osiris_LoadGameModule(%s) error initializing module.\n",basename)); + if(osm->string_table){ + DestroyStringTable(osm->string_table,osm->strings_loaded); + } + osm->string_table = NULL; + osm->strings_loaded = 0; + osm->flags = 0; + if(osm->module_name) + mem_free(osm->module_name); + osm->module_name = NULL; + mod_FreeModule(mod); + return -2; + } + + if(Osiris_module_init.module_is_static){ + //the module is requesting to be static + mprintf((0,"OSIRIS: Module (%s) is requesting to be static\n",osm->module_name)); + osm->flags |= OSIMF_NOUNLOAD; + } + + //we have a successful module load + mprintf((0,"OSIRIS: Game Module (%s) loaded successfully\n",basename)); + return loaded_id; +} + +// Osiris_LoadMissionModule +// Purpose: +// It will attempt to load the module as a game module. If it succeeds +// it will return the id of the module where it has been loaded. If the module was already loaded +// before calling this function, it will return the id to where the module is, and will not reload +// the module. Returns -1 if the module does not exist. Returns -2 if the module couldn't initialize. +// Returns -3 if the module is not a game module. Returns -4 if no module slots are available. +// This technically doesn't load a mission module, as it should already be loaded by +// Descent 3 prior. +int Osiris_LoadMissionModule(module *module_handle,char *filename) +{ + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return -1; + } + + if(!module_handle){ + return -1; + } + + int loaded_id; + + //find an available slot. + for(loaded_id = 0; loaded_id=MAX_LOADED_MODULES){ + //no slots available + mprintf((0,"OSIRIS: Osiris_LoadMissionModule(%s): No available slots\n",filename)); + Int3(); + return -4; + } + + //make sure the module exists so we can load it + if(!module_handle->handle){ + //the module does not exist + mprintf((0,"OSIRIS: Osiris_LoadMissionModule(%s): Module doesn't exist\n",filename)); + //Int3(); + return -1; + } + + //the module has loaded, attempt to import all the game functions + tOSIRISModule *osm = &OSIRIS_loaded_modules[loaded_id]; + memcpy(&osm->mod,module_handle,sizeof(module)); + module *mod = &osm->mod; + + //there are 5 functions we need to import + // GetGOScriptID@4 + // CreateInstance@4 + // DestroyInstance@8 + // CallInstanceEvent@16 + // SaveRestoreState@8 + + osm->InitializeDLL = NULL; + osm->ShutdownDLL = NULL; + osm->GetTriggerScriptID = NULL; + osm->GetCOScriptList = NULL; + osm->GetGOScriptID = (GetGOScriptID_fp) mod_GetSymbol(mod,"GetGOScriptID",8); + osm->CreateInstance = (CreateInstance_fp) mod_GetSymbol(mod,"CreateInstance",4); + osm->DestroyInstance = (DestroyInstance_fp) mod_GetSymbol(mod,"DestroyInstance",8); + osm->CallInstanceEvent = (CallInstanceEvent_fp)mod_GetSymbol(mod,"CallInstanceEvent",16); + osm->SaveRestoreState = (SaveRestoreState_fp) mod_GetSymbol(mod,"SaveRestoreState",8); + + osm->flags = OSIMF_INUSE|OSIMF_DLLELSEWHERE; + osm->module_name = mem_strdup(filename); + osm->reference_count= 1; + +#ifdef OSIRISDEBUG + ASSERT(osm->RefRoot == NULL); + osm->RefRoot = NULL; +#endif + + //make sure all of the functions imported ok + if( !osm->GetGOScriptID || + !osm->CreateInstance || + !osm->DestroyInstance || + !osm->SaveRestoreState || + !osm->CallInstanceEvent){ + //there was an error importing a function + mprintf((0,"OSIRIS: Osiris_LoadMissionModule(%s) couldn't import function.\n",filename)); + osm->flags = 0; + tOSIRISCurrentMission.mission_loaded = false; + Int3(); + //mod_FreeModule(mod); //don't unload it! it's needed by the mission + return -3; + } + + //we have a successful module load + tOSIRISCurrentMission.mission_loaded = true; + tOSIRISCurrentMission.dll_id = loaded_id; + if(Show_osiris_debug) + { + mprintf((0,"OSIRIS: Mission Game Module (%s) loaded successfully\n",filename)); + } + return loaded_id; +} + +// Osiris_UnloadMissionModule +// Purpose: +// Instead of saving the handle returned from Osiris_LoadMissionModule() and calling +// Osiris_UnloadModule() with that handle, you can just call this, instead of the call +// to Osiris_UnloadModule(). +void Osiris_UnloadMissionModule(void) +{ + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return; + } + + if(!tOSIRISCurrentMission.mission_loaded) + return; + + int dll_id = tOSIRISCurrentMission.dll_id; + + Osiris_FreeModule(dll_id); +} + + +// Osiris_BindScriptsToObject +// Purpose: +// Call this function after an object has been created to bind all the scripts associated +// with it to the object. This function must be called near the end of it's initialization, +// to make sure that all fields have been filled in. This function does not call any events. +// This function will also load any dll's needed for it's game script. +// returns false if nothing was bound. +bool Osiris_BindScriptsToObject(object *obj) +{ + ASSERT(obj->osiris_script== NULL); + if(obj->osiris_script)//already has a script bound! + return true; + + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return false; + } + + //make sure the object is valid + if( !CANBEASSIGNEDSCRIPT(obj) ){ + //invalid object + return false; + } + int real_type; + real_type = (obj->type==OBJ_DUMMY)?obj->dummy_type:obj->type; + + if( !CANTYPEBEASSIGNEDSCRIPT(real_type) ){ + //invalid type + return false; + } + + int dll_id,gos_id; + void *gos_instance; + tOSIRISScript *os; + + bool isdoor = (bool)(real_type==OBJ_DOOR); + bool iscustomonly = (bool)HAVECUSTOMONLY(real_type); + char *default_module_name; + char *page_name; + + if(iscustomonly){ + //this object can only have a custom script for it (i.e. OBJ_CAMERA) + default_module_name = NULL; + page_name = NULL; + }else{ + if(isdoor){ + default_module_name = Doors[obj->id].module_name; + page_name = Doors[obj->id].name; + }else{ + if(obj->custom_default_module_name){ + //we have a custom default script + default_module_name = obj->custom_default_module_name; + }else{ + //use what was set in Object_info + default_module_name = Object_info[obj->id].module_name; + } + + if(obj->custom_default_script_name){ + //we have a custom default script + page_name = obj->custom_default_script_name; + }else{ + //use what was set in Object_info + if(Object_info[obj->id].script_name_override[0]!='\0') + page_name = Object_info[obj->id].script_name_override; + else + page_name = Object_info[obj->id].name; + } + } + } + + + if(!iscustomonly){ + //this object can have a default script + ASSERT(default_module_name); + ASSERT(page_name); + + //load up the dllname associated with the object and get the id + dll_id = Osiris_LoadGameModule(default_module_name); + if(dll_id<0){ + //there was an error finding this object's dll + if(Show_osiris_debug) + { + mprintf((0,"OSIRIS: Unable to load module (%s) to bind to object (%s)\n",default_module_name,page_name)); + } + }else{ + //allocate the memory for the object's scripts + obj->osiris_script = (tOSIRISScript *)mem_malloc(sizeof(tOSIRISScript)); + if(!obj->osiris_script){ + //out of memory + mprintf((0,"OSIRIS: Out of memory trying to bind script\n")); + return false; + } + + obj->osiris_script->default_script.script_instance = NULL; + obj->osiris_script->custom_script.script_instance = NULL; + obj->osiris_script->mission_script.script_instance = NULL; + obj->osiris_script->level_script.script_instance = NULL; + os = obj->osiris_script; + + //we have the module loaded for the object, now we need to setup it's default script + gos_id = OSIRIS_loaded_modules[dll_id].GetGOScriptID(page_name,isdoor); + + if(gos_id==-1){ + //the default script for this object does not exist in the dll set for it + mprintf((0,"OSIRIS: Unable to find GOS ID for (%s) in (%s)!\n",page_name,default_module_name)); + Int3(); + Osiris_UnloadModule(dll_id); + }else{ + + //we now have the GOS ID for the script. All future communication with the default script-dll is + //to use this GOS ID. Now try to create an instance of the object. Hopefully we have enough memory. + gos_instance = OSIRIS_loaded_modules[dll_id].CreateInstance(gos_id); + if(!gos_instance){ + //we had an error obtaining the instance of the GOS...ugh + mprintf((0,"OSIRIS: Unable to create GOS instance for (%s)\n",page_name)); + Int3(); + Osiris_UnloadModule(dll_id); + }else{ + + //all looks good! we're guaranteed to have at least one script for this object + //setup default script values + os->default_script.DLLID = dll_id; + os->default_script.script_id = gos_id; + os->default_script.script_instance = gos_instance; + + //add to the list for this object +#ifdef OSIRISDEBUG + tRefObj *node; + if(OSIRIS_loaded_modules[dll_id].RefRoot==NULL) + { + node = OSIRIS_loaded_modules[dll_id].RefRoot = (tRefObj *)mem_malloc(sizeof(tRefObj)); + }else + { + node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node->next) node = node->next; + node->next = (tRefObj *)mem_malloc(sizeof(tRefObj)); + node = node->next; + } + node->objnum = OBJNUM(obj); + node->id = obj->id; + node->dummy = (obj->type==OBJ_DUMMY)?true:false; + node->type = (node->dummy==false)?obj->type:obj->dummy_type; + node->next = NULL; +#endif + + } + } + } + } + + //now check for a GOS and COS in the level dll (if available) + if(tOSIRISCurrentLevel.level_loaded){ + dll_id = tOSIRISCurrentLevel.dll_id; + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + + //check first for a GOS + if(!iscustomonly){ + //this script can have a GOS in the level script + ASSERT(page_name); + + gos_id = OSIRIS_loaded_modules[dll_id].GetGOScriptID(page_name,isdoor); + if(gos_id!=-1){ + if(!obj->osiris_script){ + //we need to allocate memory for a script + obj->osiris_script = (tOSIRISScript *)mem_malloc(sizeof(tOSIRISScript)); + if(!obj->osiris_script){ + //out of memory + mprintf((0,"OSIRIS: Out of memory trying to bind script\n")); + return false; + } + + obj->osiris_script->default_script.script_instance = NULL; + obj->osiris_script->custom_script.script_instance = NULL; + obj->osiris_script->mission_script.script_instance = NULL; + obj->osiris_script->level_script.script_instance = NULL; + } + os = obj->osiris_script; + + //ok, the level dll has a script for us + gos_instance = OSIRIS_loaded_modules[dll_id].CreateInstance(gos_id); + if(!gos_instance){ + //we had an error obtaining the instance of the GOS...ick + mprintf((0,"OSIRIS: Unable to create GOS instance from level dll for (%s)\n",page_name)); + Int3(); + }else{ + //ok, we got a valid instance + os->level_script.DLLID = dll_id; + os->level_script.script_id = gos_id; + os->level_script.script_instance = gos_instance; + +#ifdef OSIRISDEBUG + tRefObj *node; + if(OSIRIS_loaded_modules[dll_id].RefRoot==NULL) + { + node = OSIRIS_loaded_modules[dll_id].RefRoot = (tRefObj *)mem_malloc(sizeof(tRefObj)); + }else + { + node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node->next) node = node->next; + node->next = (tRefObj *)mem_malloc(sizeof(tRefObj)); + node = node->next; + } + node->objnum = OBJNUM(obj); + node->id = obj->id; + node->dummy = (obj->type==OBJ_DUMMY)?true:false; + node->type = (node->dummy==false)?obj->type:obj->dummy_type; + node->next = NULL; +#endif + + } + } + }//end if(!iscustomonly) + + //now check to see if we have a custom script for our handle + int i; + for( i = 0; i < tOSIRISCurrentLevel.num_customs; i++ ){ + if( obj->handle == tOSIRISCurrentLevel.custom_handles[i] ){ + //we found a matching handle, connect it to the script + gos_id = tOSIRISCurrentLevel.custom_ids[i]; + if(gos_id!=-1){ + + if(!obj->osiris_script){ + //we need to allocate memory for a script + obj->osiris_script = (tOSIRISScript *)mem_malloc(sizeof(tOSIRISScript)); + if(!obj->osiris_script){ + //out of memory + mprintf((0,"OSIRIS: Out of memory trying to bind script\n")); + return false; + } + + obj->osiris_script->default_script.script_instance = NULL; + obj->osiris_script->custom_script.script_instance = NULL; + obj->osiris_script->mission_script.script_instance = NULL; + obj->osiris_script->level_script.script_instance = NULL; + } + os = obj->osiris_script; + + gos_instance = OSIRIS_loaded_modules[dll_id].CreateInstance(gos_id); + if(!gos_instance){ + //we had an error obtaining the instance of the COS...doh! + mprintf((0,"OSIRIS: Unable to create COS instance from level dll for (%s)\n",(page_name)?(page_name):"")); + Int3(); + }else{ + //ok, everything is valid + os->custom_script.DLLID = dll_id; + os->custom_script.script_id = gos_id; + os->custom_script.script_instance = gos_instance; + +#ifdef OSIRISDEBUG + tRefObj *node; + if(OSIRIS_loaded_modules[dll_id].RefRoot==NULL) + { + node = OSIRIS_loaded_modules[dll_id].RefRoot = (tRefObj *)mem_malloc(sizeof(tRefObj)); + }else + { + node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node->next) node = node->next; + node->next = (tRefObj *)mem_malloc(sizeof(tRefObj)); + node = node->next; + } + node->objnum = OBJNUM(obj); + node->id = obj->id; + node->dummy = (obj->type==OBJ_DUMMY)?true:false; + node->type = (node->dummy==false)?obj->type:obj->dummy_type; + node->next = NULL; +#endif + + if(iscustomonly){ + mprintf((0,"OSIRIS: Attached custom script to 'custom only' object 0x%x\n",obj->handle)); + } + } + } + } + } + } + } + + // last we need to check the mission dll + if(!iscustomonly){ + //this object can have a script in the mission dll + ASSERT(page_name); + + if(tOSIRISCurrentMission.mission_loaded){ + dll_id = tOSIRISCurrentMission.dll_id; + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + + //check first for a GOS + gos_id = OSIRIS_loaded_modules[dll_id].GetGOScriptID(page_name,isdoor); + if(gos_id!=-1){ + if(!obj->osiris_script){ + //we need to allocate memory for a script + obj->osiris_script = (tOSIRISScript *)mem_malloc(sizeof(tOSIRISScript)); + if(!obj->osiris_script){ + //out of memory + mprintf((0,"OSIRIS: Out of memory trying to bind script\n")); + return false; + } + + obj->osiris_script->default_script.script_instance = NULL; + obj->osiris_script->custom_script.script_instance = NULL; + obj->osiris_script->mission_script.script_instance = NULL; + obj->osiris_script->level_script.script_instance = NULL; + } + os = obj->osiris_script; + + //ok, the mission dll has a script for us + gos_instance = OSIRIS_loaded_modules[dll_id].CreateInstance(gos_id); + if(!gos_instance){ + //we had an error obtaining the instance of the GOS...ick + mprintf((0,"OSIRIS: Unable to create GOS instance from mission dll for (%s)\n",page_name)); + Int3(); + }else{ + //ok, we got a valid instance + os->mission_script.DLLID = dll_id; + os->mission_script.script_id = gos_id; + os->mission_script.script_instance = gos_instance; + +#ifdef OSIRISDEBUG + tRefObj *node; + if(OSIRIS_loaded_modules[dll_id].RefRoot==NULL) + { + node = OSIRIS_loaded_modules[dll_id].RefRoot = (tRefObj *)mem_malloc(sizeof(tRefObj)); + }else + { + node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node->next) node = node->next; + node->next = (tRefObj *)mem_malloc(sizeof(tRefObj)); + node = node->next; + } + node->objnum = OBJNUM(obj); + node->id = obj->id; + node->dummy = (obj->type==OBJ_DUMMY)?true:false; + node->type = (node->dummy==false)?obj->type:obj->dummy_type; + node->next = NULL; +#endif + + } + } + } + } + }//end if(!iscustomonly) + + return true; +} + +// Osiris_DetachScriptsFromObject +// Purpose: +// Call this function when an object is about to be destroyed. This will unbind and remove +// all scripts associated with that object. This function does not call any events. +void Osiris_DetachScriptsFromObject(object *obj) +{ + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return; + } + + //make sure the object is valid + if( ! CANHAVEANYSCRIPT(obj) ){ + //invalid object + return; + } + + if(!obj->osiris_script){ + return; + } + + //free the memory for the allocated script + tOSIRISSCRIPTID osid; + osid.objhandle = obj->handle; + osid.type = OBJECT_SCRIPT; + Osiris_FreeMemoryForScript(&osid); + + int dll_id; + tOSIRISScript *os; + + os = obj->osiris_script; + + //free up the scripts for the object, starting with it's default script + if(os->default_script.script_instance){ + //first we need to free the instance + dll_id = os->default_script.DLLID; + +#ifdef OSIRISDEBUG + tRefObj *prev = NULL, *node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node) + { + if(node->objnum == OBJNUM(obj)) + { + //free this node + if(prev) + prev->next = node->next; + else { + ASSERT(node == OSIRIS_loaded_modules[dll_id].RefRoot); + OSIRIS_loaded_modules[dll_id].RefRoot = node->next; + } + + mem_free(node); + break; + } + prev = node; + node = node->next; + } + ASSERT(node); //make sure we found the node +#endif + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + + OSIRIS_loaded_modules[dll_id].DestroyInstance(os->default_script.script_id,os->default_script.script_instance); + + //now decrement the dll's count + Osiris_UnloadModule(dll_id); + } + + os->default_script.DLLID = 0; + os->default_script.script_id = 0; + os->default_script.script_instance = NULL; + } + + //now check for the level script + if(os->level_script.script_instance){ + //free the instance + dll_id = os->level_script.DLLID; + +#ifdef OSIRISDEBUG + tRefObj *prev = NULL, *node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node) + { + if(node->objnum == OBJNUM(obj)) + { + //free this node + if(prev) + prev->next = node->next; + else { + ASSERT(node == OSIRIS_loaded_modules[dll_id].RefRoot); + OSIRIS_loaded_modules[dll_id].RefRoot = node->next; + } + + mem_free(node); + break; + } + prev = node; + node = node->next; + } + ASSERT(node); //make sure we found the node +#endif + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + + OSIRIS_loaded_modules[dll_id].DestroyInstance(os->level_script.script_id,os->level_script.script_instance); + + } + + os->level_script.DLLID = 0; + os->level_script.script_id = 0; + os->level_script.script_instance = NULL; + } + + //check for an attached custom script + if(os->custom_script.script_instance){ + //free the instance + dll_id = os->custom_script.DLLID; + +#ifdef OSIRISDEBUG + tRefObj *prev = NULL, *node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node) + { + if(node->objnum == OBJNUM(obj)) + { + //free this node + if(prev) + prev->next = node->next; + else { + ASSERT(node == OSIRIS_loaded_modules[dll_id].RefRoot); + OSIRIS_loaded_modules[dll_id].RefRoot = node->next; + } + + mem_free(node); + break; + } + prev = node; + node = node->next; + } + ASSERT(node); //make sure we found the node +#endif + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + + OSIRIS_loaded_modules[dll_id].DestroyInstance(os->custom_script.script_id,os->custom_script.script_instance); + + } + + os->custom_script.DLLID = 0; + os->custom_script.script_id = 0; + os->custom_script.script_instance = NULL; + } + + //check for a mission dll script + if(os->mission_script.script_instance){ + //free the instance + dll_id = os->mission_script.DLLID; + +#ifdef OSIRISDEBUG + tRefObj *prev = NULL, *node = OSIRIS_loaded_modules[dll_id].RefRoot; + while(node) + { + if(node->objnum == OBJNUM(obj)) + { + //free this node + if(prev) + prev->next = node->next; + else { + ASSERT(node == OSIRIS_loaded_modules[dll_id].RefRoot); + OSIRIS_loaded_modules[dll_id].RefRoot = node->next; + } + + mem_free(node); + break; + } + prev = node; + node = node->next; + } + ASSERT(node); //make sure we found the node +#endif + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + + OSIRIS_loaded_modules[dll_id].DestroyInstance(os->mission_script.script_id,os->mission_script.script_instance); + + } + + os->mission_script.DLLID = 0; + os->mission_script.script_id = 0; + os->mission_script.script_instance = NULL; + } + + //finally free up the memory allocated for the script struct + mem_free(obj->osiris_script); + obj->osiris_script = NULL; +} + +// Osiris_CallLevelEvent +// Purpose: +// Triggers an event for a level script. Returns true if the default action should continue +// to process. +bool Osiris_CallLevelEvent(int event, tOSIRISEventInfo *data ) +{ + if(!Osiris_level_script_loaded) + return true; + + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return true; + } + + if(!(Osiris_event_mask&OEM_LEVELS)) + return true; //level events are disabled + + if(!Osiris_IsEventEnabled(event)) + return true; + + int aux_event = -1; + if(event==EVT_AI_NOTIFY){ + switch(data->evt_ai_notify.notify_type){ + case AIN_GOAL_COMPLETE: aux_event = EVT_AIN_GOALCOMPLETE; break; + case AIN_GOAL_INVALID: aux_event = EVT_AIN_GOALFAIL; break; + case AIN_GOAL_FAIL: aux_event = EVT_AIN_GOALFAIL; break; + case AIN_GOAL_ERROR: aux_event = EVT_AIN_GOALFAIL; break; + } + } + + if(tOSIRISCurrentLevel.level_loaded){ + //there is a loaded level, get it's dll id and call it's event + int dll_id = tOSIRISCurrentLevel.dll_id; + void *instance = tOSIRISCurrentLevel.instance; + + if(instance){ + data->me_handle = OBJECT_HANDLE_NONE; //its a level script!...no me + short ret; + + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent(0, instance, event, data ); + if(aux_event!=-1){ + //call child event + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent(0, instance, aux_event, data ); + } + + return (bool)((ret&CONTINUE_DEFAULT)!=0); + } + } + + return true; +} + +// Osiris_CallTriggerEvent +// Purpose: +// Triggers an event for a trigger script. Returns true if the default action should continue +// to process. +bool Osiris_CallTriggerEvent(int trignum,int event,tOSIRISEventInfo *ei) +{ + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return true; + } + + if(!(Osiris_event_mask&OEM_TRIGGERS)) + return true; //trigger events are disabled + + if(!Osiris_IsEventEnabled(event)) + return true; + + if(trignum<0 || trignum>=Num_triggers) + return true; + + if(!tOSIRISCurrentLevel.level_loaded) + return true; + + int dll_id,script_id; + int aux_event = -1; + void *instance; + + if(event==EVT_AI_NOTIFY){ + switch(ei->evt_ai_notify.notify_type){ + case AIN_GOAL_COMPLETE: aux_event = EVT_AIN_GOALCOMPLETE; break; + case AIN_GOAL_INVALID: aux_event = EVT_AIN_GOALFAIL; break; + case AIN_GOAL_FAIL: aux_event = EVT_AIN_GOALFAIL; break; + case AIN_GOAL_ERROR: aux_event = EVT_AIN_GOALFAIL; break; + } + } + + dll_id = tOSIRISCurrentLevel.dll_id; + script_id = Triggers[trignum].osiris_script.script_id; + instance = Triggers[trignum].osiris_script.script_instance; + + if(instance){ + short ret; + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent(script_id,instance,event,ei); + + if(aux_event!=-1){ + //call child event + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent(script_id,instance,aux_event,ei); + } + + return (bool)((ret&CONTINUE_DEFAULT)!=0); + } + + return true; +} + +// Osiris_EnableEvents +// Purpose: +// Enables the passed in mask of event types to be called +void Osiris_EnableEvents(ubyte mask) +{ + Osiris_event_mask |= mask; +} + +// Osiris_DisableEvents +// Purpose: +// Disables the passed in mask of event types +void Osiris_DisableEvents(ubyte mask) +{ + Osiris_event_mask &= ~mask; +} + +// Osiris_DisableCreateEvents +// Purpose: +// Disables any events involved when an object is created. This is to be used for +// Loading games/viewing demos, as so not to re-initialize good data. +void Osiris_DisableCreateEvents(void) +{ + Osiris_create_events_disabled = true; +} + +// Osiris_EnablesCreateEvents +// Purpose: +// Enables any events involved when an object is created. This is to be used for +// Loading games/viewing demos, as so not to re-initialize good data. (call when done with Osiris_DisableCreateEvents()) +void Osiris_EnableCreateEvents(void) +{ + Osiris_create_events_disabled = false; +} + +// Osiris_IsEventEnabled +// Purpose: +// Returns true if the event is allowed to be called +bool Osiris_IsEventEnabled(int event) +{ + switch(event) + { + case EVT_CREATED: + case EVT_AI_INIT: + return !Osiris_create_events_disabled; + break; + default: + return true; + break; + } + return true; +} + + +// Osiris_CallEvent +// Purpose: +// Triggers an event for an object. Pass in the event number and the associated +// structure of data. All events will be chained through the associated scripts of the +// object (as long as they are available) in the order: custom script, level script, +// mission script, and finally it's default script. The chain breaks if one of the scripts +// returns false on the call to their CallInstanceEvent(). +bool Osiris_CallEvent(object *obj, int event, tOSIRISEventInfo *data ) +{ + if( (Game_mode&GM_MULTI) && (Netgame.local_role!=LR_SERVER) ){ + //no scripts for a client! + return true; + } + + if(!(Osiris_event_mask&OEM_OBJECTS)) + return true; //object events are disabled + + if(!Osiris_IsEventEnabled(event)) + return true; + + //make sure the object is valid + if (!CANHAVEANYSCRIPT(obj) ) { + //invalid object + return true; + } + + if(!obj->osiris_script){ + //mprintf((0,"OSIRIS: Unhandled event (%d) for object id=%d\n",event,obj->id)); + return true; //no script for this object...hmm + } + + int dll_id; + int aux_event = -1; //event value + tOSIRISScript *os; + short ret = CONTINUE_CHAIN|CONTINUE_DEFAULT; + + if(event==EVT_AI_NOTIFY){ + switch(data->evt_ai_notify.notify_type){ + case AIN_OBJ_KILLED: aux_event = EVT_AIN_OBJKILLED; break; + case AIN_SEE_TARGET: aux_event = EVT_AIN_SEEPLAYER; break; + case AIN_WHIT_OBJECT: aux_event = EVT_AIN_WHITOBJECT; break; + case AIN_GOAL_COMPLETE: aux_event = EVT_AIN_GOALCOMPLETE; break; + case AIN_GOAL_INVALID: aux_event = EVT_AIN_GOALFAIL; break; + case AIN_GOAL_FAIL: aux_event = EVT_AIN_GOALFAIL; break; + case AIN_GOAL_ERROR: aux_event = EVT_AIN_GOALFAIL; break; + case AIN_MELEE_HIT: aux_event = EVT_AIN_MELEE_HIT; break; + case AIN_MELEE_ATTACK_FRAME: aux_event = EVT_AIN_MELEE_ATTACK_FRAME; break; + case AIN_MOVIE_START: aux_event = EVT_AIN_MOVIE_START; break; + case AIN_MOVIE_END: aux_event = EVT_AIN_MOVIE_END; break; + } + } + + os = obj->osiris_script; + data->me_handle = obj->handle; + + if(os->custom_script.script_instance){ + dll_id = os->custom_script.DLLID; + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(aux_event!=-1) + { + //call the child event + OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->custom_script.script_id, + os->custom_script.script_instance, + aux_event, + data); + } + + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE) + { + + //call the event + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->custom_script.script_id, + os->custom_script.script_instance, + event, + data); + } + } + + if((ret&CONTINUE_CHAIN) && os->level_script.script_instance){ + dll_id = os->level_script.DLLID; + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + + if(aux_event!=-1) + { + //call the child event + OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->level_script.script_id, + os->level_script.script_instance, + aux_event, + data); + } + + //call the event + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->level_script.script_id, + os->level_script.script_instance, + event, + data); + + } + } + + if((ret&CONTINUE_CHAIN) && os->mission_script.script_instance){ + dll_id = os->mission_script.DLLID; + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE){ + if(aux_event!=-1) + { + //call the child event + OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->mission_script.script_id, + os->mission_script.script_instance, + aux_event, + data); + } + + //call the event + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->mission_script.script_id, + os->mission_script.script_instance, + event, + data); + + } + } + + if((ret&CONTINUE_CHAIN) && os->default_script.script_instance){ + dll_id = os->default_script.DLLID; + + ASSERT(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE); + if(OSIRIS_loaded_modules[dll_id].flags&OSIMF_INUSE) + { + + if(aux_event!=-1) + { + //call the child event + OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->default_script.script_id, + os->default_script.script_instance, + aux_event, + data); + } + + //call the event + ret = OSIRIS_loaded_modules[dll_id].CallInstanceEvent( os->default_script.script_id, + os->default_script.script_instance, + event, + data); + + } + } + + return (bool)((ret&CONTINUE_DEFAULT)!=0); +} + +#define MAX_OSIRIS_TIMERS 64 +#define OITF_USED 0x0001 +#define OITF_REPEATCALL 0x0002 +#define OITF_TRIGGERTIMER 0x0004 +#define OITF_LEVELTIMER 0x0008 +#define OITF_CANCELONDEAD 0x0010 +typedef struct{ + ushort flags; + union{ + int objhandle; + int trignum; + }; + int id; + int repeat_count; + int objhandle_detonator; + int handle; + float timer_interval; + float timer_next_signal; + float timer_end; +}tOSIRISINTERNALTIMER; +tOSIRISINTERNALTIMER OsirisTimers[MAX_OSIRIS_TIMERS]; + +inline int FORM_HANDLE(int counter, int slot) +{ + return (((counter&0xFFFFFF)<<8) | (slot&0xFF)); +} + +inline int GET_SLOT(int handle) +{ + return (handle&0xFF); +} + +int Osiris_timer_counter = 0; + +// Osiris_ProcessTimers +// Purpose: +// This function checks all timers currently running, if any need to be signaled it signals them. +void Osiris_ProcessTimers(void) +{ + object *obj; + bool signal,kill; + tOSIRISEventInfo ei; + + for( int i = 0; i < MAX_OSIRIS_TIMERS; i++ ){ + signal = false; + kill = false; + + if(OsirisTimers[i].flags&OITF_USED){ + + if(OsirisTimers[i].flags&OITF_CANCELONDEAD){ + obj = ObjGet(OsirisTimers[i].objhandle_detonator); + if(!obj || (obj->type==OBJ_GHOST) || (obj->type==OBJ_PLAYER && Players[obj->id].flags&(PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD) ) ){ + //the detontator died...cancel the timer! + OsirisTimers[i].flags &= ~OITF_USED; + OsirisTimers[i].repeat_count = 0; + + tOSIRISEventInfo ei; + ei.evt_timercancel.detonated = 1; + ei.evt_timercancel.handle = OsirisTimers[i].handle; + + if(OsirisTimers[i].flags&OITF_TRIGGERTIMER){ + Osiris_CallTriggerEvent(OsirisTimers[i].trignum,EVT_TIMERCANCEL,&ei); + }else if(OsirisTimers[i].flags&OITF_LEVELTIMER){ + Osiris_CallLevelEvent(EVT_TIMERCANCEL,&ei); + }else{ + object *obj = ObjGet(OsirisTimers[i].objhandle); + if(obj) + Osiris_CallEvent(obj,EVT_TIMERCANCEL,&ei); + } + + mprintf((0,"OSIRIS TIMER: Cancelling Timer (%d/%d)\n",OsirisTimers[i].handle,i)); + continue; + } + } + + if(OsirisTimers[i].timer_next_signal<=Gametime){ + //this timer needs to be signaled + signal = true; + + if(OsirisTimers[i].flags&OITF_REPEATCALL){ + //this is a repeater + if(OsirisTimers[i].repeat_count!=-1){ + //it has a finite repeat + OsirisTimers[i].repeat_count--; + if(OsirisTimers[i].repeat_count<=0){ + //remove the timer + kill = true; + OsirisTimers[i].repeat_count = 0; + }else{ + //adjust for next signal + OsirisTimers[i].timer_next_signal += OsirisTimers[i].timer_interval; + } + }else{ + //infinite repeat + OsirisTimers[i].timer_next_signal += OsirisTimers[i].timer_interval; + } + + + } + } + + if( (!((OsirisTimers[i].flags&OITF_REPEATCALL) && (OsirisTimers[i].repeat_count==-1))) && + OsirisTimers[i].timer_end<=Gametime){ + + //this timer has expired, remove it + kill = true; + //signal timer + signal = true; + } + + if(signal){ + ei.evt_timer.game_time = Gametime; + ei.evt_timer.id = OsirisTimers[i].id; + } + + //this timer is currently in use, see what kind of trigger it is + if(OsirisTimers[i].flags&OITF_TRIGGERTIMER){ + //trigger timer + if(signal){ + Osiris_CallTriggerEvent(OsirisTimers[i].trignum,EVT_TIMER,&ei); + } + }else if(OsirisTimers[i].flags&OITF_LEVELTIMER){ + //level timer + if(signal){ + Osiris_CallLevelEvent(EVT_TIMER,&ei); + } + }else{ + //object timer + obj = ObjGet(OsirisTimers[i].objhandle); + if(obj){ + //process this object timer + if(signal){ + Osiris_CallEvent(obj,EVT_TIMER,&ei); + } + }else{ + //this object no longer exists, remove the timer + kill = true; + } + } + + if(kill) + OsirisTimers[i].flags &= ~OITF_USED; + } + } +} + +// Osiris_ResetAllTimers +// Purpose: +// Flushes all the timers +void Osiris_ResetAllTimers(void) +{ + for(int i=0;i=MAX_OSIRIS_TIMERS) + return -1;//no more slots available + + //clear out the slots + OsirisTimers[i].flags = 0; + OsirisTimers[i].id = 0; + OsirisTimers[i].objhandle = 0; + OsirisTimers[i].repeat_count = 0; + OsirisTimers[i].timer_end = 0; + OsirisTimers[i].timer_interval = 0; + OsirisTimers[i].timer_next_signal = 0; + OsirisTimers[i].handle = 0; + OsirisTimers[i].objhandle_detonator = OBJECT_HANDLE_NONE; + + //fill in the data + OsirisTimers[i].flags |= OITF_USED; + OsirisTimers[i].id = ot->id; + OsirisTimers[i].timer_end = ot->timer_interval + Gametime; + OsirisTimers[i].timer_interval = ot->timer_interval; + OsirisTimers[i].timer_next_signal = ot->timer_interval + Gametime; + + if(ot->flags&OTF_TRIGGER){ + OsirisTimers[i].trignum = ot->trigger_number; + OsirisTimers[i].flags |= OITF_TRIGGERTIMER; + }else if(ot->flags&OTF_LEVEL){ + OsirisTimers[i].flags |= OITF_LEVELTIMER; + }else{ + OsirisTimers[i].objhandle= ot->object_handle; + } + + if(ot->flags&OTF_REPEATER){ + //this is a repeater timer + OsirisTimers[i].flags |= OITF_REPEATCALL; + OsirisTimers[i].repeat_count = ot->repeat_count; + if(ot->repeat_count!=-1){ + OsirisTimers[i].timer_end = (ot->timer_interval*ot->repeat_count)+Gametime; + } + } + + if(ot->flags&OTF_CANCELONDEAD){ + //there is a detonator + OsirisTimers[i].flags |= OITF_CANCELONDEAD; + OsirisTimers[i].objhandle_detonator = ot->object_handle_detonator; + } + + //create a handle + Osiris_timer_counter++; + int handle = FORM_HANDLE(Osiris_timer_counter,i); + + OsirisTimers[i].handle = handle; + + return handle; +} + +// Osiris_CancelTimer +// Purpose: +// Cancels a timer that's in use, given its handle +void Osiris_CancelTimer(int handle) +{ + int slot; + + slot = GET_SLOT(handle); + + if(slot<0 || slot>=MAX_OSIRIS_TIMERS) + return; + + if(OsirisTimers[slot].handle != handle ) //not the same timer + return; + + OsirisTimers[slot].flags &= ~OITF_USED; + + tOSIRISEventInfo ei; + ei.evt_timercancel.detonated = 0; + ei.evt_timercancel.handle = handle; + + if(OsirisTimers[slot].flags&OITF_TRIGGERTIMER){ + Osiris_CallTriggerEvent(OsirisTimers[slot].trignum,EVT_TIMERCANCEL,&ei); + }else if(OsirisTimers[slot].flags&OITF_LEVELTIMER){ + Osiris_CallLevelEvent(EVT_TIMERCANCEL,&ei); + }else{ + object *obj = ObjGet(OsirisTimers[slot].objhandle); + if(obj) + Osiris_CallEvent(obj,EVT_TIMERCANCEL,&ei); + } +} + +// Osiris_GetTimerHandle +// Purpose: +// Gets the handle for the timer with the specified id +int Osiris_GetTimerHandle(int id) +{ + //Look for timer with this ID + for (int i=0;i=MAX_OSIRIS_TIMERS) + return false; + + if(!(OsirisTimers[id].flags&OITF_USED)) + return false; + + if(OsirisTimers[id].handle != handle ) //not the same timer + return false; + + return true; +} + +// Osiris_TimerTimeRemaining +// Purpose: +// Returns the amount of time remaining on the specified timer +float Osiris_TimerTimeRemaining(int handle) +{ + int id = GET_SLOT(handle); + + if(id<0 || id>=MAX_OSIRIS_TIMERS) + return -1.0; + + if(!(OsirisTimers[id].flags&OITF_USED)) + return -1.0; + + if(OsirisTimers[id].handle != handle ) //not the same timer + return -1.0; + + return (OsirisTimers[id].timer_end - Gametime); +} + +#define OSIRIS_SYSTEM_FILEVERSION 0x01 +// Osiris_SaveSystemState +// Purpose: +// Saves the current state of the system (not the scripts!) to file +void Osiris_SaveSystemState(CFILE *file) +{ + int i; + int save_start = cftell(file); + int checksum_pos; + + cf_WriteString(file,"OSIRIS"); + + cf_WriteByte(file,OSIRIS_SYSTEM_FILEVERSION); + + checksum_pos = cftell(file); + cf_WriteInt(file,0); //this will be # of bytes writtens + + cf_WriteInt(file,Osiris_timer_counter); + + //save out the timer state + for(i=0;inext; + if(curr->memory) + mem_free(curr->memory); + mem_free(curr); + curr = next; + } + Osiris_mem_root = NULL; +} + +// Osiris_AllocateMemory +// Purpose: +// Allocates a chunk of memory to be associated with a script. It will automatically +// save this memory to disk on game save, and will pass the pointer to this memory on EVT_RESTORE +void *Osiris_AllocateMemory(tOSIRISMEMCHUNK *mc) +{ + //find the end of the list + tOSIRISMEMNODE *curr,**error_node; + + if(!Osiris_mem_root){ + //it'll be the first node + Osiris_mem_root = (tOSIRISMEMNODE *)mem_malloc(sizeof(tOSIRISMEMNODE)); + if(!Osiris_mem_root) + return NULL; + + curr = Osiris_mem_root; + error_node = &Osiris_mem_root; + }else{ + //traverse to the tail + curr = Osiris_mem_root; + while(curr->next){ + curr = curr->next; + } + + curr->next = (tOSIRISMEMNODE *)mem_malloc(sizeof(tOSIRISMEMNODE)); + if(!curr->next) + return NULL; + error_node = &curr->next; + curr = curr->next; + } + + //setup the data + curr->next = NULL; + curr->memory = mem_malloc(mc->size); + if(!curr->memory){ + //ack...out of memory + *error_node = NULL; //make sure we cut off the list + mem_free(curr); + return NULL; + } + memcpy(&curr->chunk_id,mc,sizeof(tOSIRISMEMCHUNK)); + + return curr->memory; +} + +// Osiris_FreeMemory +// Purpose: +// Frees a chunk of memory that was allocated by Osiris_AllocateMemory(). +void Osiris_FreeMemory(void *mem_ptr) +{ + tOSIRISMEMNODE *tofree; + + if(!Osiris_mem_root) + return; + + if(Osiris_mem_root->memory==mem_ptr){ + //free the base pointer + tofree = Osiris_mem_root; + Osiris_mem_root = Osiris_mem_root->next; + }else{ + //traverse until we find the node + tOSIRISMEMNODE *curr; + curr = Osiris_mem_root; + bool done = false; + tofree = NULL; + while(!done){ + if(!curr->next){ + done = true;//we didn't find it + }else if(curr->next->memory==mem_ptr){ + //this is the guy to free + tofree = curr->next; + done = true; + }else + curr = curr->next; + } + + if(tofree){ + //ok there is a value to free, pull the list back one from curr on + curr->next = tofree->next; + }else{ + //nothing to do + return; + } + } + + //free the memory associated with tofree + if(tofree->memory) + mem_free(tofree->memory); + mem_free(tofree); +} + +bool compareid(tOSIRISSCRIPTID *sid,tOSIRISSCRIPTID *oid) +{ + if(sid->type==oid->type){ + if(sid->type==LEVEL_SCRIPT) + return true; + if(sid->type==TRIGGER_SCRIPT && sid->triggernum==oid->triggernum) + return true; + if(sid->type==OBJECT_SCRIPT && sid->objhandle==oid->objhandle) + return true; + } + return false; +} + +// Osiris_FreeMemoryForScript +// Purpose: +// Frees all memory allocated for a given script +void Osiris_FreeMemoryForScript(tOSIRISSCRIPTID *sid) +{ + if(!Osiris_mem_root) + return; + bool done = false; + tOSIRISMEMNODE *curr,*next,*prev; + curr = Osiris_mem_root; + prev = NULL; + + while(!done){ + if(!curr){ + done = true; + }else{ + next = curr->next; + + if(compareid(sid,&curr->chunk_id.my_id)){ + //this has to go + if(curr==Osiris_mem_root){ + //removing root + Osiris_mem_root = curr->next; + + if(curr->memory) + mem_free(curr->memory); + mem_free(curr); + }else{ + //remove from list and pull back + prev->next = curr->next; + + if(curr->memory) + mem_free(curr->memory); + mem_free(curr); + } + }else{ + prev = curr; + } + + curr = next; + } + } +} + +// Osiris_SaveMemoryChunks +// Purpose: +// Saves out the 'auto manage' script memory to file +void Osiris_SaveMemoryChunks(CFILE *file) +{ + tOSIRISMEMNODE *curr; + curr = Osiris_mem_root; + + while(curr){ + cf_WriteInt(file,curr->chunk_id.size); + cf_WriteByte(file,curr->chunk_id.my_id.type); + switch(curr->chunk_id.my_id.type){ + case OBJECT_SCRIPT: + cf_WriteInt(file,curr->chunk_id.my_id.objhandle); + break; + case TRIGGER_SCRIPT: + cf_WriteInt(file,curr->chunk_id.my_id.triggernum); + break; + case LEVEL_SCRIPT: + break; + } + cf_WriteInt(file,curr->chunk_id.id); + cf_WriteBytes((ubyte *)curr->memory,curr->chunk_id.size,file); + curr = curr->next; + } + + cf_WriteInt(file,0); +} + +// Osiris_RestoreMemoryChunks +// Purpose: +// Restores the 'auto manage' from file, calls the EVT_MEMRESTORE +void Osiris_RestoreMemoryChunks(CFILE *file) +{ + tOSIRISMEMNODE *curr; + tOSIRISMEMNODE *memchunk; + + //Osiris_CloseMemoryManager(); We might not want to close this down...due to some objects don't get destroyed + //Osiris_mem_root = NULL; + curr = Osiris_mem_root; + if(curr) + { + while(curr->next) { curr = curr->next; } + } + + bool done = false; + while(!done){ + int size = cf_ReadInt(file); + if(size==0){ + //we're done + done = true; + }else{ + //handle this node + memchunk = (tOSIRISMEMNODE *)mem_malloc(sizeof(tOSIRISMEMNODE)); + if(!memchunk){ + Error("Out of memory"); + } + + memchunk->chunk_id.size = size; + memchunk->chunk_id.my_id.type = (script_type)cf_ReadByte(file); + switch(memchunk->chunk_id.my_id.type){ + case OBJECT_SCRIPT: + memchunk->chunk_id.my_id.objhandle = cf_ReadInt(file); + break; + case TRIGGER_SCRIPT: + memchunk->chunk_id.my_id.triggernum = cf_ReadInt(file); + break; + case LEVEL_SCRIPT: + break; + } + memchunk->chunk_id.id = cf_ReadInt(file); + memchunk->memory = mem_malloc(memchunk->chunk_id.size); + if(!memchunk->memory){ + Error("Out of memory"); + } + cf_ReadBytes((ubyte *)memchunk->memory,memchunk->chunk_id.size,file); + memchunk->next = NULL; + + //ok, the node is setup, add it to the linked list + if(!Osiris_mem_root){ + Osiris_mem_root = curr = memchunk; + }else{ + curr->next = memchunk; + curr = curr->next; + } + + tOSIRISEventInfo ei; + ei.evt_memrestore.id = memchunk->chunk_id.id; + ei.evt_memrestore.memory_ptr = memchunk->memory; + + //send an event to the object + switch(memchunk->chunk_id.my_id.type){ + case OBJECT_SCRIPT: + { + object *obj = ObjGet(memchunk->chunk_id.my_id.objhandle); + if(obj){ + Osiris_CallEvent(obj,EVT_MEMRESTORE,&ei); + } + }break; + case TRIGGER_SCRIPT: + Osiris_CallTriggerEvent(memchunk->chunk_id.my_id.triggernum,EVT_MEMRESTORE,&ei); + break; + case LEVEL_SCRIPT: + Osiris_CallLevelEvent(EVT_MEMRESTORE,&ei); + break; + }; + } + } +} + +void _extractscript(char *script,char *tempfilename) +{ + cf_CopyFile(tempfilename,script); +} + +int _getfreeextractslot(void) +{ + //find a free slot + for(int q=0;qbase_id); + + //write out the info about this hash node + int length; + length = (currhash->script_name)?strlen(currhash->script_name):0; + cf_WriteByte(file,length); + if(length) + cf_WriteBytes((ubyte *)currhash->script_name,length,file); + + //now go through all the nodes and write their data + tOMMSNode *node; + node = currhash->root; + while(node){ + //write out 1, for valid hash + cf_WriteByte(file,1); + cf_WriteShort(file,node->id); + + //write out all the node data + cf_WriteByte(file,node->free_called); + cf_WriteShort(file,node->reference_count); + cf_WriteInt(file,node->unique_id); + cf_WriteInt(file,node->size_of_memory); + if(node->size_of_memory) + cf_WriteBytes((ubyte *)node->memory_ptr,node->size_of_memory,file); + + node = node->next; + } + //write out terminating 0 + cf_WriteByte(file,0); + + //go to the next hash node + currhash = currhash->next; + } + + // write out terminating 0 + cf_WriteByte(file,0); +} + +void Osiris_RestoreOMMS(CFILE *file) +{ + ASSERT(OMMS_Hash_node_root==NULL); + + OMMS_Current_base_id = (unsigned short)cf_ReadShort(file); + OMMS_Current_id = (unsigned short)cf_ReadShort(file); + + tOMMSHashNode *currhash; + tOMMSNode *node; + + currhash = NULL; + + //we have to rebuild the nodes + while(cf_ReadByte(file)){ + if(!currhash){ + currhash = OMMS_Hash_node_root = (tOMMSHashNode *)mem_malloc(sizeof(tOMMSHashNode)); + }else{ + currhash->next = (tOMMSHashNode *)mem_malloc(sizeof(tOMMSHashNode)); + currhash = currhash->next; + } + + if(!currhash) + Error("Out of memory"); + + currhash->next = NULL; + currhash->root = NULL; + currhash->script_name = NULL; + + currhash->base_id = (unsigned short)cf_ReadShort(file); + + //read length of string + int length = cf_ReadByte(file); + if(length){ + currhash->script_name = (char *)mem_malloc(length+1); + if(!currhash->script_name) + Error("Out of Memory"); + else + cf_ReadBytes((ubyte *)currhash->script_name,length,file); + } + + node = NULL; + + //now go through all the nodes and right their data + while(cf_ReadByte(file)){ + if(!node){ + node = currhash->root = (tOMMSNode *)mem_malloc(sizeof(tOMMSNode)); + }else{ + node->next = (tOMMSNode *)mem_malloc(sizeof(tOMMSNode)); + node = node->next; + } + + if(!node) + Error("Out of memory"); + + node->memory_ptr = NULL; + node->next = NULL; + + node->id = (unsigned short)cf_ReadShort(file); + + node->free_called = (cf_ReadByte(file))?true:false; + node->reference_count = (ushort) cf_ReadShort(file); + node->unique_id = cf_ReadInt(file); + node->size_of_memory = cf_ReadInt(file); + if(node->size_of_memory){ + node->memory_ptr = mem_malloc(node->size_of_memory); + if(!node->memory_ptr) + Error("Out of memory"); + + cf_ReadBytes((ubyte *)node->memory_ptr,node->size_of_memory,file); + } + }//end reading nodes + }//end reading hash nodes + +} + +// Searches through the hash nodes and looks for the one associated with +// the script name, if one doesn't exist it will create one (if autocreate is true). +tOMMSHashNode *Osiris_OMMS_FindHashNode(char *script_name,bool autocreate) +{ + tOMMSHashNode *curr = OMMS_Hash_node_root; + + if(!OMMS_Hash_node_root){ + //allocate the root node + if(!autocreate) + return NULL; + + curr = OMMS_Hash_node_root = (tOMMSHashNode *)mem_malloc(sizeof(tOMMSHashNode)); + }else{ + if(curr->script_name && !stricmp(curr->script_name,script_name)){ + //the root node matches + return curr; + } + + //search through to see if we can find the node + while(curr->next){ + if(curr->next->script_name && !stricmp(curr->next->script_name,script_name)){ + //curr->next is a matching node + return curr->next; + } + + curr = curr->next; + } + + //allocate a node and tack it on the end + if(!autocreate) + return NULL; + + curr->next = (tOMMSHashNode *)mem_malloc(sizeof(tOMMSHashNode)); + curr = curr->next; + } + + //if we get here then curr is a freshly allocated node + curr->next = NULL; + curr->root = NULL; + curr->script_name = mem_strdup(script_name); + curr->base_id = ++OMMS_Current_base_id; + + return curr; +} + +// frees all memory associated with the OMMSHashNode given (includes all script +// memory...returns the next node in the list, NULL if none +tOMMSHashNode *Osiris_OMMS_DeleteHashNode(tOMMSHashNode *node) +{ + if(!node) + return NULL; + tOMMSHashNode *ret = node->next; + + if(node->script_name) + mem_free(node->script_name); + + tOMMSNode *curr,*next; + curr = next = node->root; + + while(curr){ + next = curr->next; + mem_free(curr->memory_ptr); + mem_free(curr); + curr = next; + } + + mem_free(node); + + return ret; +} + +// finds an OMMS node, given the hash node to start at. If it isn't +// found than one is created. NULL if out of memory. +tOMMSNode *Osiris_OMMS_FindNode(tOMMSHashNode *root,unsigned int uid,bool autocreate) +{ + tOMMSNode *curr = root->root; + + if(!root->root){ + //allocate the root node + if(!autocreate) + return NULL; + + curr = root->root = (tOMMSNode *)mem_malloc(sizeof(tOMMSNode)); + }else{ + if(curr->unique_id == uid){ + //the root node matches + return curr; + } + + //search through to see if we can find the node + while(curr->next){ + if(curr->next->unique_id == uid){ + //curr->next is a matching node + return curr->next; + } + + curr = curr->next; + } + + //allocate a node and tack it on the end + if(!autocreate) + return NULL; + + curr->next = (tOMMSNode *)mem_malloc(sizeof(tOMMSNode)); + curr = curr->next; + } + + //if we get here then curr is a freshly allocated node + curr->free_called = 0; + curr->memory_ptr = NULL; + curr->next = NULL; + curr->reference_count = 0; + curr->size_of_memory = 0; + curr->unique_id = uid; + curr->id = ++OMMS_Current_id; + + return curr; +} + +// Removes the OMMS node for the given HashNode (completly remove) +void Osiris_OMMS_RemoveNode(tOMMSHashNode *root,unsigned int uid) +{ + tOMMSNode *curr = root->root; + tOMMSNode *node_to_free = NULL; + + if(!root->root) + return; + + if(root->root->unique_id == uid){ + //the root node matches + node_to_free = root->root; + root->root = NULL; + goto free_mem; + } + + //search through to see if we can find the node + while(curr->next){ + if(curr->next->unique_id == uid){ + //curr->next is a matching node + node_to_free = curr->next; + curr->next = curr->next->next; + goto free_mem; + } + curr = curr->next; + } + +free_mem: + if(!node_to_free) + return; + + mprintf((0,"OMMS: deleting uid 0x%x\n",node_to_free->unique_id)); + if(node_to_free->memory_ptr) + mem_free(node_to_free->memory_ptr); + mem_free(node_to_free); +} + +// Reduces the reference count for an OMMSNode by 1 +void Osiris_OMMS_ReduceRefCount(tOMMSHashNode *root,tOMMSNode *node) +{ + if(!node) + return; + + node->reference_count--; + + if(node->reference_count<=0 && node->free_called){ + //free the node + Osiris_OMMS_RemoveNode(root,node->unique_id); + } +} + +// Calls free on a node +void Osiris_OMMS_CallFreeForNode(tOMMSHashNode *root,tOMMSNode *node) +{ + if(!node) + return; + + node->free_called = 1; + + if(node->reference_count<=0 && node->free_called){ + //free the node + Osiris_OMMS_RemoveNode(root,node->unique_id); + } +} + +// Searches for the given OMMSHANDLE, returns NULL if not found. if hash is +// given it will return the hash root that the node was found in. +tOMMSNode *Osiris_OMMS_FindHandle(OMMSHANDLE handle,tOMMSHashNode **hash) +{ + tOMMSHashNode *hashcurr = OMMS_Hash_node_root; + tOMMSNode *nodecurr; + unsigned short base_id; + unsigned short id; + base_id = ((handle&0xFFFF0000)>>16); + id = (handle&0x0000FFFF); + + while(hashcurr){ + if(hashcurr->base_id==base_id){ + + nodecurr = hashcurr->root; + while(nodecurr){ + if(nodecurr->id == id){ + if(hash) + *hash = hashcurr; + return nodecurr; + } + + nodecurr = nodecurr->next; + } + } + + hashcurr = hashcurr->next; + } + + return NULL; +} + +// Allocates a block of global memory for this module, of size amount_of_memory. +// unique_identfier is the script provided unique ID which is used throughout. +// script_identifier is the pointer of data provided to the script in it's InitializeDLL function (really the name of the script) +// Returns -1 if there isn't enough available memory +// Returns -2 if the unique identifier passed in is already used, but the requested amount_of_memory is different +// If the memory has already been allocated, it will return the handle. +OMMSHANDLE Osiris_OMMS_Malloc(size_t amount_of_memory,uint unique_identifier,char *script_identifier) +{ + ASSERT(amount_of_memory>0); + if(amount_of_memory<=0) + return -1; + + //find the correct hash node + tOMMSHashNode *hash = Osiris_OMMS_FindHashNode(script_identifier,true); + if(!hash) + return -1; + + tOMMSNode *node = Osiris_OMMS_FindNode(hash,unique_identifier,true); + if(!node) + return -1; + + if(node->memory_ptr){ + //the memory already has been allocated + if(node->size_of_memory==amount_of_memory) + return ((hash->base_id<<16)|(node->id)); + + //the size's requested don't match + return -2; + } + + //we need to allocate the memory + node->memory_ptr = mem_malloc(amount_of_memory); + if(!node->memory_ptr) + return -1; //out of memory + + node->size_of_memory = amount_of_memory; + node->unique_id = unique_identifier; + + mprintf((0,"OMMS: malloc handle (0x%x%x) size = %d\n",hash->base_id,node->id,node->size_of_memory)); + return ((hash->base_id<<16)|(node->id)); +} + +// Attaches to a block of global OMMS memory. As long as at least one module (or script) is +// attached to a module, the memory will not be deleted. (Increments the reference count) +// Returns NULL if the memory couldn't be attached (it has been either free'd or never malloced) +void *Osiris_OMMS_Attach(OMMSHANDLE handle) +{ + tOMMSNode *node = Osiris_OMMS_FindHandle(handle); + if(!node) + return NULL; + + if(node->memory_ptr){ + node->reference_count++; + } + return node->memory_ptr; +} + +// Detaches a block of global OMMS memory. (Reduces the reference count). +void Osiris_OMMS_Detach(OMMSHANDLE handle) +{ + tOMMSHashNode *hash; + tOMMSNode *node = Osiris_OMMS_FindHandle(handle,&hash); + if(!node) + return; + + Osiris_OMMS_ReduceRefCount(hash,node); +} + +// Frees a block of global memory +// Only has affect if you are attached to the memory. Memory will _ONLY_ be deleted when the +// following conditions are meant for the shared memory block: +// 1) The reference count for the global memory is at 0 (OMMS_Attach() adds to reference count, OMMS_Detach() subtracts). +// 2) OMMS_Free() has been called for the block of global memory. +// The conditions can happen in any order, but as soon as both are satisfied, the memory +// becomes deleted and any pointer returned by OMMS_Attach() becomes invalid. +// +// handle : the value returned by OMMS_Malloc() +void Osiris_OMMS_Free(OMMSHANDLE handle) +{ + tOMMSHashNode *hash; + tOMMSNode *node = Osiris_OMMS_FindHandle(handle,&hash); + if(!node) + return; + + Osiris_OMMS_CallFreeForNode(hash,node); +} + +// Returns an OMMSHANDLE to a block of global memory allocated by a module/script. Pass +// in the unique_identifier and the script_identifier that was passed in the OMMS_Malloc(). +// Note: script_identifier is really the filename of the module that called the OMMS_Malloc(). +// Returns -1 if the module was never OMMS_Malloc()'d. +OMMSHANDLE Osiris_OMMS_Find(uint unique_identifier,char *script_identifier) +{ + tOMMSHashNode *hash = Osiris_OMMS_FindHashNode(script_identifier,false); + if(!hash) + return -1; + + tOMMSNode *node = Osiris_OMMS_FindNode(hash,unique_identifier,false); + if(!node) + return -1; + if(!node->memory_ptr) + return -1; + + return ((hash->base_id<<16)|(node->id)); +} + +// Returns information about the OMMS memory given it's handle returned from the OMMS_Find() or +// OMMS_Malloc(). Returns 0 if the handle was invalid, 1 if the information has been filled in; +// Pass NULL in for those parameters you don't need information about. +char Osiris_OMMS_GetInfo(OMMSHANDLE handle,uint *mem_size,uint *uid,ushort *reference_count,ubyte *has_free_been_called) +{ + if(mem_size) + *mem_size = 0; + if(uid) + *uid = 0; + if(reference_count) + *reference_count = 0; + if(has_free_been_called) + *has_free_been_called = 0; + + tOMMSHashNode *hash; + tOMMSNode *node = Osiris_OMMS_FindHandle(handle,&hash); + if(!node) + return 0; + + if(mem_size) + *mem_size = node->size_of_memory; + if(uid) + *uid = node->unique_id; + if(reference_count) + *reference_count = node->reference_count; + if(has_free_been_called) + *has_free_been_called = node->free_called; + + return 1; +} + + +//***************************************************************************** +// Osiris_CreateModuleInitStruct +// Purpose: +// This function initializes a Module Init Struct with all the needed data to get sent +// to the module during initialization. +void Osiris_CreateModuleInitStruct(tOSIRISModuleInit *mi) +{ + int i = 0; + + //fill in function pointers here + mi->fp[i++] = (int *)MonoPrintf; + mi->fp[i++] = (int *)msafe_CallFunction; + mi->fp[i++] = (int *)msafe_GetValue; + mi->fp[i++] = (int *)osipf_CallObjectEvent; + mi->fp[i++] = (int *)osipf_CallTriggerEvent; + mi->fp[i++] = (int *)osipf_SoundTouch; + mi->fp[i++] = (int *)osipf_ObjectFindID; + mi->fp[i++] = (int *)osipf_WeaponFindID; + mi->fp[i++] = (int *)osipf_ObjectGetTimeLived; + mi->fp[i++] = (int *)osipf_GetGunPos; + mi->fp[i++] = (int *)osipf_RoomValue; + mi->fp[i++] = (int *)osipf_IsRoomValid; + mi->fp[i++] = (int *)osipf_GetAttachParent; + mi->fp[i++] = (int *)osipf_GetNumAttachSlots; + mi->fp[i++] = (int *)osipf_GetAttachChildHandle; + mi->fp[i++] = (int *)osipf_AttachObjectAP; + mi->fp[i++] = (int *)osipf_AttachObjectRad; + mi->fp[i++] = (int *)osipf_UnattachFromParent; + mi->fp[i++] = (int *)osipf_UnattachChild; + mi->fp[i++] = (int *)osipf_UnattachChildren; + mi->fp[i++] = (int *)osipf_RayCast; + mi->fp[i++] = (int *)osipf_AIGetPathID; + mi->fp[i++] = (int *)osipf_AIGoalFollowPathSimple; + mi->fp[i++] = (int *)osipf_AIPowerSwitch; + mi->fp[i++] = (int *)osipf_AITurnTowardsVectors; + mi->fp[i++] = (int *)osipf_AISetType; + mi->fp[i++] = (int *)osipf_AIFindHidePos; + mi->fp[i++] = (int *)osipf_AIGoalAddEnabler; + mi->fp[i++] = (int *)osipf_AIGoalAdd; + mi->fp[i++] = (int *)osipf_AIGoalClear; + mi->fp[i++] = (int *)osipf_AIValue; + mi->fp[i++] = (int *)osipf_AIFindObjOfType; + mi->fp[i++] = (int *)osipf_AIGetRoomPathPoint; + mi->fp[i++] = (int *)osipf_AIFindEnergyCenter; + mi->fp[i++] = (int *)osipf_AIGetDistToObj; + mi->fp[i++] = (int *)osipf_AISetGoalFlags; + mi->fp[i++] = (int *)osipf_AISetGoalCircleDist; + mi->fp[i++] = (int *)osipf_CFReadBytes; + mi->fp[i++] = (int *)osipf_CFReadInt; + mi->fp[i++] = (int *)osipf_CFReadShort; + mi->fp[i++] = (int *)osipf_CFReadByte; + mi->fp[i++] = (int *)osipf_CFReadFloat; + mi->fp[i++] = (int *)osipf_CFReadDouble; + mi->fp[i++] = (int *)osipf_CFReadString; + mi->fp[i++] = (int *)osipf_CFWriteBytes; + mi->fp[i++] = (int *)osipf_CFWriteString; + mi->fp[i++] = (int *)osipf_CFWriteInt; + mi->fp[i++] = (int *)osipf_CFWriteShort; + mi->fp[i++] = (int *)osipf_CFWriteByte; + mi->fp[i++] = (int *)osipf_CFWriteFloat; + mi->fp[i++] = (int *)osipf_CFWriteDouble; + mi->fp[i++] = (int *)Osiris_AllocateMemory; + mi->fp[i++] = (int *)Osiris_FreeMemory; + mi->fp[i++] = (int *)Osiris_CancelTimer; + mi->fp[i++] = (int *)Osiris_CreateTimer; + mi->fp[i++] = (int *)msafe_DoPowerup; + mi->fp[i++] = (int *)osipf_ObjCreate; + mi->fp[i++] = (int *)osipf_GameTime; + mi->fp[i++] = (int *)osipf_FrameTime; + mi->fp[i++] = (int *)osipf_ObjWBValue; + mi->fp[i++] = (int *)Osiris_TimerExists; + mi->fp[i++] = (int *)osipf_ObjectValue; + mi->fp[i++] = (int *)osipf_MatcenValue; + mi->fp[i++] = (int *)osipf_MatcenReset; + mi->fp[i++] = (int *)osipf_MatcenCopy; + mi->fp[i++] = (int *)osipf_MatcenCreate; + mi->fp[i++] = (int *)osipf_MatcenFindId; + mi->fp[i++] = (int *)osipf_MissionFlagSet; + mi->fp[i++] = (int *)osipf_MissionFlagGet; + mi->fp[i++] = (int *)osipf_PlayerValue; + mi->fp[i++] = (int *)osipf_ObjectCustomAnim; + mi->fp[i++] = (int *)osipf_PlayerAddHudMessage; + mi->fp[i++] = (int *)osipf_ObjGhost; + mi->fp[i++] = (int *)osipf_ObjBurning; + mi->fp[i++] = (int *)osipf_ObjIsEffect; + mi->fp[i++] = (int *)osipf_CFopen; + mi->fp[i++] = (int *)osipf_CFclose; + mi->fp[i++] = (int *)osipf_CFtell; + mi->fp[i++] = (int *)osipf_CFeof; + mi->fp[i++] = (int *)osipf_SoundStop; + mi->fp[i++] = (int *)osipf_SoundPlay2d; + mi->fp[i++] = (int *)osipf_SoundPlay3d; + mi->fp[i++] = (int *)osipf_SoundFindId; + mi->fp[i++] = (int *)osipf_AIIsObjFriend; + mi->fp[i++] = (int *)osipf_AIIsObjEnemy; + mi->fp[i++] = (int *)osipf_AIGoalValue; + mi->fp[i++] = (int *)osipf_AIGetNearbyObjs; + mi->fp[i++] = (int *)osipf_AIGetCurGoalIndex; + mi->fp[i++] = (int *)Osiris_OMMS_Malloc; + mi->fp[i++] = (int *)Osiris_OMMS_Attach; + mi->fp[i++] = (int *)Osiris_OMMS_Detach; + mi->fp[i++] = (int *)Osiris_OMMS_Free; + mi->fp[i++] = (int *)Osiris_OMMS_Find; + mi->fp[i++] = (int *)Osiris_OMMS_GetInfo; + mi->fp[i++] = (int *)Cinematic_Start; + mi->fp[i++] = (int *)Cinematic_Stop; + mi->fp[i++] = (int *)osipf_FindSoundName; + mi->fp[i++] = (int *)osipf_FindRoomName; + mi->fp[i++] = (int *)osipf_FindTriggerName; + mi->fp[i++] = (int *)osipf_FindObjectName; + mi->fp[i++] = (int *)osipf_GetTriggerRoom; + mi->fp[i++] = (int *)osipf_GetTriggerFace; + mi->fp[i++] = (int *)osipf_FindDoorName; + mi->fp[i++] = (int *)osipf_FindTextureName; + mi->fp[i++] = (int *)osipf_CreateRandomSparks; + mi->fp[i++] = (int *)Osiris_CancelTimerID; + mi->fp[i++] = (int *)osipf_GetGroundPos; + mi->fp[i++] = (int *)osipf_EnableShip; + mi->fp[i++] = (int *)osipf_IsShipEnabled; + mi->fp[i++] = (int *)osipf_PathGetInformation; + mi->fp[i++] = (int *)Cinematic_StartCannedScript; + mi->fp[i++] = (int *)osipf_FindMatcenName; + mi->fp[i++] = (int *)osipf_FindPathName; + mi->fp[i++] = (int *)osipf_FindLevelGoalName; + mi->fp[i++] = (int *)osipf_ObjectFindType; + mi->fp[i++] = (int *)osipf_LGoalValue; + mi->fp[i++] = (int *)osipf_ObjMakeListOfType; + mi->fp[i++] = (int *)osipf_ObjKill; +// mi->fp[i++] = (int *)osipf_AIAreRoomsReachable; + mi->fp[i++] = (int *)osipf_AIIsDestReachable; + mi->fp[i++] = (int *)osipf_AIIsObjReachable; + mi->fp[i++] = (int *)osipf_GameGetDiffLevel; + mi->fp[i++] = (int *)osipf_GetLanguageSetting; + mi->fp[i++] = (int *)osipf_PathValue; + + //fill in the remaining with NULL + for(;ifp[i] = NULL; + } +} + +#ifdef MACINTOSH +#pragma opt_dead_code on +#endif diff --git a/Descent3/PilotPicsAPI.cpp b/Descent3/PilotPicsAPI.cpp new file mode 100644 index 000000000..a21565e23 --- /dev/null +++ b/Descent3/PilotPicsAPI.cpp @@ -0,0 +1,748 @@ +/* +* $Logfile: /DescentIII/main/PilotPicsAPI.cpp $ +* $Revision: 9 $ +* $Date: 10/21/99 9:28p $ +* $Author: Jeff $ +* +* API implementation for Pilot Pictures +* +* $Log: /DescentIII/main/PilotPicsAPI.cpp $ + * + * 9 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 8 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 7 3/30/99 4:26a Jeff + * reset variable when closing down system + * + * 6 3/29/99 7:19p Jeff + * increased buffer for importing pilot pics + * + * 5 2/05/99 7:04p Jeff + * table file parsing macros put in + * + * 4 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 3 11/24/98 5:16p Jeff + * fixed bugs in the api during initial testing + * + * 2 11/23/98 6:30p Jeff + * initial creation +* +* $NoKeywords: $ +*/ + +#include +#include +#include "bitmap.h" +#include "player.h" +#include "pilot.h" +#include "CFILE.H" +#include "mono.h" +#include "ddio.h" +#include "manage.h" +#include "PilotPicsAPI.h" +#include "mem.h" + +#define PILOTPIC_DATABASE_HOG "PPics.Hog" +#define PILOTPIC_DATABASE_INDEX "PPics.idx" + +CFILE *PilotPic_database_index_handle = NULL; +int PilotPic_database_hog_handle = 0; +int PilotPic_find_offset; +char PilotPic_find_name[PILOT_STRING_SIZE]; +bool PilotPic_init = false; + + +// ----------------------------------------------- +// PPic_BuildDatabase +// Purpose: +// Builds up some databases for fast access +// based off the index file. +// ----------------------------------------------- +bool PPic_BuildDatabases(void); +// ----------------------------------------------- +// PPic_FreeDatabase +// Purpose: +// Frees up memory associated with the databases +// ----------------------------------------------- +void PPic_FreeDatabase(void); +// ------------------------------------------------- +// PPic_JumpToPilot +// Purpose: +// Returns an offset into the file to where the +// the first pilot that matches pilot_name is. -1 +// is returned if no match +// -------------------------------------------------- +int PPic_JumpToPilot(char *pilot_name); +// ------------------------------------- +// PPic_GetIndexFromID +// Purpose: +// Returns the file index into the Pilot_id_to_offset +// list for the given id, -1 if not found. +// ------------------------------------- +int PPic_GetIndexFromID(int id); +// ------------------------------------- +// PPic_GetOffsetByID +// Purpose: +// Returns the file offset of the given pilot id. -1 if not found +// ------------------------------------- +int PPic_GetOffsetByID(ushort pilot_id); + +//=========================================================================== + +// --------------------------------------------------------- +// PPic_InitDatabase +// Purpose: +// Initializes the database for the pictures. Call before +// any other Pilot Picture function +// --------------------------------------------------------- +bool PPic_InitDatabase(void) +{ + if(PilotPic_init){ + mprintf((0,"PPIC: InitDatabase already called\n")); + return true; + } + + // attempt to open the hog database + // -------------------------------- +#if defined (MACINTOSH) && !(defined (_DEBUG) || defined (DEMO)) + char *fullpath; + fullpath = GetMultiCDPath(PILOTPIC_DATABASE_HOG); +#else + char fullpath[_MAX_PATH]; + ddio_MakePath(fullpath,LocalD3Dir,PILOTPIC_DATABASE_HOG,NULL); +#endif + PilotPic_database_hog_handle = cf_OpenLibrary(fullpath); + + if(PilotPic_database_hog_handle==0){ + // there was an error opening the hog database + // ----------------------------------------- + mprintf((0,"PPIC: Error opening %s database\n",PILOTPIC_DATABASE_HOG)); + PilotPic_database_index_handle = NULL; + return false; + } + + // attempt to open the pilotpicture database index + // ----------------------------------------------- + PilotPic_database_index_handle = cfopen(PILOTPIC_DATABASE_INDEX,"rb"); + + if(PilotPic_database_index_handle==NULL){ + // there was an error opening the database index + // --------------------------------------------- + mprintf((0,"PPIC: Error opening database index '%s'\n",PILOTPIC_DATABASE_INDEX)); + cf_CloseLibrary(PilotPic_database_hog_handle); + PilotPic_database_hog_handle = 0; + return false; + } + + // when we get to this point, all of our files have been opened successfully, so to + // make searching, etc fast, we'll build some databases in memory, for quick lookup + // --------------------------------------------------------------------------------- + if(!PPic_BuildDatabases()){ + // there was an error building the databases + // ----------------------------------------- + mprintf((0,"PPIC: Error building databases\n")); + cfclose(PilotPic_database_index_handle); + PilotPic_database_index_handle = NULL; + cf_CloseLibrary(PilotPic_database_hog_handle); + PilotPic_database_hog_handle = 0; + return false; + } + + PilotPic_init = true; + + atexit(PPic_CloseDatabase); + + return true; +} + +// --------------------------------------------------------- +// PPic_CloseDatabase +// Purpose: +// Closes down the picture database. +// --------------------------------------------------------- +void PPic_CloseDatabase(void) +{ + if(!PilotPic_init) + return; + + //close up the database + PPic_FreeDatabase(); + + //close up files + if(PilotPic_database_index_handle){ + cfclose(PilotPic_database_index_handle); + PilotPic_database_index_handle = NULL; + } + + if(PilotPic_database_hog_handle){ + cf_CloseLibrary(PilotPic_database_hog_handle); + PilotPic_database_hog_handle = 0; + } + + PilotPic_init = false; +} + +// --------------------------------------------------------- +// PPic_QueryPilot +// Purpose: +// Given a pilot callsign it will search the database +// and return the number of pilots that match the name +// --------------------------------------------------------- +ushort PPic_QueryPilot(char *pilot_name) +{ + if(!PilotPic_init) + return 0; + + int letter_offset = PPic_JumpToPilot(pilot_name); + if(letter_offset==-1) + return 0; + + int count = 0; + bool done = false; + char name_buffer[PILOT_STRING_SIZE]; + CFILE *file = PilotPic_database_index_handle; + + //now read through the pilots until we don't get a match anymore + while(!done){ + //first read in the pilot name + ubyte name_size; + name_size = cf_ReadByte(file); + + if(cfeof(file)){ + done = true; + continue; + } + cf_ReadBytes((ubyte *)name_buffer,name_size,file); + name_buffer[name_size]='\0'; + + //next read in pilot_id + ushort pilot_id; + pilot_id = (ushort)cf_ReadShort(file); + + //next read past the bitmap name + ubyte bmp_size; + bmp_size = cf_ReadByte(file); + cfseek(file,bmp_size,SEEK_CUR); + + if(stricmp(name_buffer,pilot_name)){ + //we're done + done = true; + }else + count++; + + if(cfeof(file)) + done = true; + } + + return count; +} + +// --------------------------------------------------------- +// PPic_FindFirst +// Purpose: +// Given a pilot callsign it will return the first pilot +// that matches the search. Returns true on success, false +// if it couldn't find any pilots matching. It returns +// the pilot id. +// ---------------------------------------------------------- +bool PPic_FindFirst(char *pilot_name,ushort *pilot_id) +{ + if(!PilotPic_init) + return false; + + *pilot_id = 65535; + + int letter_offset = PPic_JumpToPilot(pilot_name); + if(letter_offset==-1) + return false; + + strcpy(PilotPic_find_name,pilot_name); + + char name_buffer[PILOT_STRING_SIZE]; + CFILE *file = PilotPic_database_index_handle; + + //first read in the pilot name + ubyte name_size; + name_size = cf_ReadByte(file); + if(cfeof(file)){ + return false; + } + cf_ReadBytes((ubyte *)name_buffer,name_size,file); + name_buffer[name_size]='\0'; + + //next read in pilot_id + ushort pid; + pid = (ushort)cf_ReadShort(file); + + //next read past the bitmap name + ubyte bmp_size; + bmp_size = cf_ReadByte(file); + cfseek(file,bmp_size,SEEK_CUR); + + if(stricmp(name_buffer,pilot_name)){ + return false; //weird + } + + *pilot_id = pid; + PilotPic_find_offset = cftell(file); + + return true; +} + +// ---------------------------------------------------------- +// PPic_FindNext +// Purpose: +// Call repeatedly until false is returned to get information +// about all the pilots that match pilot_name passed into +// an initial call to PPic_FindFirst(). It returns the +// pilot id. +// ---------------------------------------------------------- +bool PPic_FindNext(ushort *pilot_id) +{ + if(!PilotPic_init) + return false; + + *pilot_id = 65535; + + char name_buffer[PILOT_STRING_SIZE]; + CFILE *file = PilotPic_database_index_handle; + + cfseek(file,PilotPic_find_offset,SEEK_SET); + if(cfeof(file)){ + return false; + } + + //first read in the pilot name + ubyte name_size; + name_size = cf_ReadByte(file); + if(cfeof(file)){ + return false; + } + cf_ReadBytes((ubyte *)name_buffer,name_size,file); + name_buffer[name_size]='\0'; + + //next read in pilot_id + ushort pid; + pid = (ushort)cf_ReadShort(file); + + //next read past the bitmap name + ubyte bmp_size; + bmp_size = cf_ReadByte(file); + cfseek(file,bmp_size,SEEK_CUR); + + if(stricmp(name_buffer,PilotPic_find_name)){ + return false; //we're done + } + + *pilot_id = pid; + PilotPic_find_offset = cftell(file); + + return true; +} + +// ---------------------------------------------------------- +// PPic_FindClose +// Purpose: +// Stops a PPic_FindFirst/PPic_FindNext session +// ---------------------------------------------------------- +void PPic_FindClose(void) +{ + PilotPic_find_offset = 0; + PilotPic_find_name[0] = 0; +} + +// ---------------------------------------------------------- +// PPic_GetPilot +// Purpose: +// Given a pilot id, it will return the pilot name of +// the pilot name. Returns false if it's an invalid pilot id. +// ---------------------------------------------------------- +bool PPic_GetPilot(ushort pilot_id,char *pilot_name,int buffersize) +{ + if(!PilotPic_init) + return false; + + int oldoffset = cftell(PilotPic_database_index_handle); + + int offset = PPic_GetOffsetByID(pilot_id); + if(offset==-1) + return false; + + // jump to the offset + // ------------------ + cfseek(PilotPic_database_index_handle,offset,SEEK_SET); + if(cfeof(PilotPic_database_index_handle)){ + cfseek(PilotPic_database_index_handle,oldoffset,SEEK_SET); + return false; + } + + // read in the pilot name + // ---------------------- + ubyte name_size; + name_size = cf_ReadByte(PilotPic_database_index_handle); + if(cfeof(PilotPic_database_index_handle)){ + cfseek(PilotPic_database_index_handle,oldoffset,SEEK_SET); + return false; + } + int toread = min(name_size,buffersize-1); + cf_ReadBytes((ubyte *)pilot_name,toread,PilotPic_database_index_handle); + pilot_name[toread] = '\0'; + + cfseek(PilotPic_database_index_handle,oldoffset,SEEK_SET); + return true; +} + +// ---------------------------------------------------------- +// PPic_GetBitmapHandle +// Purpose: +// Given a pilot id, it will return a handle to the +// bitmap for the pilot. MAKE SURE YOU FREE THE BITMAP WITH +// bm_FreeBitmap(). Returns -1 if it was an illegal pilot id. +// Returns BAD_BITMAP_HANDLE if it couldn't open the bitmap. +// ---------------------------------------------------------- +int PPic_GetBitmapHandle(ushort pilot_id) +{ + if(!PilotPic_init) + return -1; + + int oldoffset = cftell(PilotPic_database_index_handle); + + int offset = PPic_GetOffsetByID(pilot_id); + if(offset==-1) + return -1; + + // jump to the offset + // ------------------ + cfseek(PilotPic_database_index_handle,offset,SEEK_SET); + if(cfeof(PilotPic_database_index_handle)){ + cfseek(PilotPic_database_index_handle,oldoffset,SEEK_SET); + return -1; + } + + // read in the pilot name + // ---------------------- + ubyte name_size; + char name_buffer[PILOT_STRING_SIZE]; + name_size = cf_ReadByte(PilotPic_database_index_handle); + if(cfeof(PilotPic_database_index_handle)){ + cfseek(PilotPic_database_index_handle,oldoffset,SEEK_SET); + return -1; + } + cf_ReadBytes((ubyte *)name_buffer,name_size,PilotPic_database_index_handle); + name_buffer[name_size]='\0'; + + //next read in pilot_id + ushort pid; + pid = (ushort)cf_ReadShort(PilotPic_database_index_handle); + + //next read past the bitmap name + ubyte bmp_size; + char bitmap_name[_MAX_PATH]; + bmp_size = cf_ReadByte(PilotPic_database_index_handle); + cf_ReadBytes((ubyte *)bitmap_name,bmp_size,PilotPic_database_index_handle); + bitmap_name[bmp_size]='\0'; + + cfseek(PilotPic_database_index_handle,oldoffset,SEEK_SET); + + int bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(bitmap_name),0); + if(bm_handle<=BAD_BITMAP_HANDLE) + return BAD_BITMAP_HANDLE; + + return bm_handle; +} + +// =============================================================================== + +//this maps a pilot_id to an index file offset +typedef struct{ + int offset; + ushort id; +}tPilotPicIdOffset; +tPilotPicIdOffset *Pilot_id_to_offset = NULL; +ushort *Sorted_Pilot_id_to_offset = NULL; +int PilotPic_count = 0; + +//this maps the alphabet to an index file offset (first pilot_name that begins with the letter) +int Alphabet_to_offset[27]; + +// ----------------------------------------------- +// PPic_BuildDatabase +// Purpose: +// Builds up some databases for fast access +// based off the index file. +// ----------------------------------------------- +bool PPic_BuildDatabases(void) +{ + CFILE *file = PilotPic_database_index_handle; + if(!file) + return false; + + // first rewind to the beginning and find out how many pilots we have + // ------------------------------------------------------------------- + cf_Rewind(file); + + PilotPic_count = cf_ReadInt(file); + if(PilotPic_count<0){ + //hmm a negative! + mprintf((0,"PPIC: Invalid number of pilot pictures (%d)\n",PilotPic_count)); + Int3(); + PilotPic_count = 0; + cf_Rewind(file); + return false; + } + if(PilotPic_count>65535){ + //too many!!!! + mprintf((0,"PPIC: Invalid number of pilot pictures (%d)\n",PilotPic_count)); + Int3(); + PilotPic_count = 0; + cf_Rewind(file); + return false; + } + + // allocate all the memory we're going to need + // ------------------------------------------- + Pilot_id_to_offset = (tPilotPicIdOffset *)mem_malloc(sizeof(tPilotPicIdOffset)*PilotPic_count); + Sorted_Pilot_id_to_offset = (ushort *)mem_malloc(sizeof(ushort)*PilotPic_count); + if(!Pilot_id_to_offset){ + //out of memory!!! + mprintf((0,"PPIC: Out of memory allocating index database\n")); + Int3(); + PilotPic_count = 0; + cf_Rewind(file); + return false; + } + + // now it is time to go through the index file and build the databases + // ------------------------------------------------------------------- + int count; + char name_buffer[256]; + + for(count = 0; count < 27; count++) Alphabet_to_offset[count] = -1; + + for(count = 0; count < PilotPic_count; count++ ){ + + //get the current file position, we'll need it + int file_pos; + file_pos = cftell(file); + + //first read in the pilot name + ubyte name_size; + name_size = cf_ReadByte(file); + cf_ReadBytes((ubyte *)name_buffer,name_size,file); + name_buffer[name_size]='\0'; + if(name_size>=PILOT_STRING_SIZE) + { + mprintf((0,"PPIC: Too big: (%s)%d %d",name_buffer,count,name_size)); + } + + //next read in pilot_id + ushort pilot_id; + pilot_id = (ushort)cf_ReadShort(file); + + //next read past the bitmap name + ubyte bmp_size; + bmp_size = cf_ReadByte(file); + cfseek(file,bmp_size,SEEK_CUR); + + //add the info to the database + Pilot_id_to_offset[count].offset = file_pos; + Pilot_id_to_offset[count].id = pilot_id; + + char let_pos; + if(isalpha(name_buffer[0])){ + let_pos = toupper(name_buffer[0]) - 'A'; + }else{ + let_pos = 26;//not alpha letter + } + + if(Alphabet_to_offset[let_pos]==-1){ + Alphabet_to_offset[let_pos] = file_pos; + } + } + + //now sort the pilot ids + int i,t,j; + + for(i=0;i=0 && (Pilot_id_to_offset[Sorted_Pilot_id_to_offset[j]].id0){ + //we can stop reading/searching + return -1; + } + } + + if(cfeof(file)) + done = true; + } + + return -1; +} + +// ------------------------------------- +// PPic_GetOffsetByID +// Purpose: +// Returns the file offset of the given pilot id. -1 if not found +// ------------------------------------- +int PPic_GetOffsetByID(ushort pilot_id) +{ + int index = PPic_GetIndexFromID(pilot_id); + if(index==-1) + return -1; + return Pilot_id_to_offset[index].offset; +} + + +// ------------------------------------- +// PPic_GetIndexFromID +// Purpose: +// Returns the file index into the Pilot_id_to_offset +// list for the given id, -1 if not found. +// ------------------------------------- +int PPic_GetIndexFromID(int id) +{ + //do a binary search for the id,return -1 if not found + int min = 0,max = PilotPic_count-1; + int index_to_check; + int sort_index,sorted_val; + + while(1){ + index_to_check = (min+max)/2; + sort_index = Sorted_Pilot_id_to_offset[index_to_check]; + sorted_val = Pilot_id_to_offset[sort_index].id; + + if(sorted_val==id){ + //found it! + return sort_index; + } + + if (min >= max) //exhausted search + break; + + if (sorted_val>id) //search key after check key + min = index_to_check+1; + else //search key before check key + max = index_to_check-1; + } + return -1; +} \ No newline at end of file diff --git a/Descent3/PilotPicsAPI.h b/Descent3/PilotPicsAPI.h new file mode 100644 index 000000000..39bc7f39f --- /dev/null +++ b/Descent3/PilotPicsAPI.h @@ -0,0 +1,90 @@ +/* +* $Logfile: /DescentIII/Main/PilotPicsAPI.h $ +* $Revision: 2 $ +* $Date: 11/23/98 6:30p $ +* $Author: Jeff $ +* +* API header file for Pilot Pictures +* +* $Log: /DescentIII/Main/PilotPicsAPI.h $ + * + * 2 11/23/98 6:30p Jeff + * initial creation +* +* $NoKeywords: $ +*/ + + +#ifndef _PILOTPICS_H__ +#define _PILOTPICS_H__ + +// --------------------------------------------------------- +// PPic_InitDatabase +// Purpose: +// Initializes the database for the pictures. Call before +// any other Pilot Picture function +// --------------------------------------------------------- +bool PPic_InitDatabase(void); + +// --------------------------------------------------------- +// PPic_CloseDatabase +// Purpose: +// Closes down the picture database. +// --------------------------------------------------------- +void PPic_CloseDatabase(void); + +// --------------------------------------------------------- +// PPic_QueryPilot +// Purpose: +// Given a pilot callsign it will search the database +// and return the number of pilots that match the name +// --------------------------------------------------------- +ushort PPic_QueryPilot(char *pilot_name); + +// --------------------------------------------------------- +// PPic_FindFirst +// Purpose: +// Given a pilot callsign it will return the first pilot +// that matches the search. Returns true on success, false +// if it couldn't find any pilots matching. It returns +// the pilot id. +// ---------------------------------------------------------- +bool PPic_FindFirst(char *pilot_name,ushort *pilot_id); + +// ---------------------------------------------------------- +// PPic_FindNext +// Purpose: +// Call repeatedly until false is returned to get information +// about all the pilots that match pilot_name passed into +// an initial call to PPic_FindFirst(). It returns the +// pilot id. +// ---------------------------------------------------------- +bool PPic_FindNext(ushort *pilot_id); + +// ---------------------------------------------------------- +// PPic_FindClose +// Purpose: +// Stops a PPic_FindFirst/PPic_FindNext session +// ---------------------------------------------------------- +void PPic_FindClose(void); + +// ---------------------------------------------------------- +// PPic_GetPilot +// Purpose: +// Given a pilot id, it will return the pilot name of +// the pilot name. Returns false if it's an invalid pilot id. +// ---------------------------------------------------------- +bool PPic_GetPilot(ushort pilot_id,char *pilot_name,int buffersize); + +// ---------------------------------------------------------- +// PPic_GetBitmapHandle +// Purpose: +// Given a pilot id, it will return a handle to the +// bitmap for the pilot. MAKE SURE YOU FREE THE BITMAP WITH +// bm_FreeBitmap(). Returns -1 if it was an illegal pilot id. +// Returns BAD_BITMAP_HANDLE if it couldn't open the bitmap. +// ---------------------------------------------------------- +int PPic_GetBitmapHandle(ushort pilot_id); + + +#endif \ No newline at end of file diff --git a/Descent3/Player.cpp b/Descent3/Player.cpp new file mode 100644 index 000000000..36ec42890 --- /dev/null +++ b/Descent3/Player.cpp @@ -0,0 +1,4413 @@ +/* + * $Logfile: /DescentIII/Main/Player.cpp $ + * $Revision: 331 $ + * $Date: 10/22/01 12:43p $ + * $Author: Matt $ + * + * Code for dealing with players + * + * $Log: /DescentIII/Main/Player.cpp $ + * + * 331 10/22/01 12:43p Matt + * On player death/exit, don't try to spew a red guidebot if Mercenary is + * not installed. + * + * 330 3/20/00 12:19p Matt + * Merge of Duane's post-1.3 changes. + * Change for no avilable vis effects. + * + * 329 1/26/00 9:20p Jeff + * added support for IntelliVIBE DLL + * + * 328 11/02/99 1:56p Chris + * Fixed bug with black pyro spewing a blue gb on death + * + * 327 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 326 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 325 7/28/99 3:47p Kevin + * Mac! + * + * 324 7/26/99 4:56p Kevin + * more demo bug fixes + * + * 323 7/26/99 12:40p Kevin + * Fixed some multiplayer client recorded demo bugs + * + * 322 7/08/99 5:45p Jason + * Bug fixes for the patch + * + * 321 5/23/99 3:06a Jason + * fixed bug with player rankings not being updated correctly + * + * 320 5/21/99 7:09p Jason + * fixed some weird multiplayer rejoin anomalies + * + * 319 5/20/99 7:54p 3dsmax + * added better memory support changing ships + * + * 318 5/20/99 2:53p Jason + * made autowaypoints work in coop + * + * 317 5/20/99 2:48a Matt + * Auto-waypoints now stored & used per player. Manual waypoints will all + * players, once Jason makes it work. + * + * 316 5/19/99 3:25p Jason + * fixed wrong ordering of InitObjectScripts and MultiSendObject + * + * 315 5/17/99 11:03a Matt + * When initializing the player ship for a new level, limit the ammo to + * the max the ship can carry. + * + * 314 5/13/99 3:41p Ardussi + * changes for compiling on the Mac + * + * 313 5/12/99 3:26p Jason + * made terrain damage effect multiplayer friendly + * + * 312 5/10/99 10:23p Ardussi + * changes to compile on Mac + * + * 311 5/10/99 9:25p Jeff + * first phase of Rock 'n' Ride support added + * + * 310 5/10/99 2:01a Jason + * make player invulnerable for 2 seconds during respawn in a single + * player game + * + * 309 5/08/99 4:12p Chris + * Added AI hearing noises... version 1 + * + * 308 5/07/99 1:44p Chris + * Made in-game cinematics use the ORIENT_PATH_NODE for player ships + * + * 307 5/05/99 2:33p Jason + * Room 18 face 116: No connection for edge 3 + * Room 18 face 116: No connection for edge 4 + * Room 18 face 116: No connection for edge 5 + * Room 18 face 140: No connection for edge 0 + * Fixed some multiplayer issues + * + * 306 5/05/99 1:32a Jeff + * save/restore player energy also + * + * 305 5/05/99 1:28a Jeff + * save/restore player's shields in single player game if they are over + * 100 + * + * 304 5/04/99 9:27p Jeff + * cheaters don't win! (no score for cheaters) + * + * 303 5/04/99 7:09p Jason + * changed sun damage effect just a little + * + * 302 4/29/99 3:03p Jason + * fixed player popping in and out in a multiplayer game + * + * 301 4/28/99 5:55p Matt + * Don't spew powerup for secondaries if have none. This fixes spewing + * multipacks with count == 0. + * + * 300 4/28/99 5:10p Jeff + * fixed inventory spew in multiplayer (with non-spewable items) + * + * 299 4/28/99 2:25a Jeff + * added call to add guidebot in selected multiplayer games to the clients + * + * 298 4/27/99 10:33p Matt + * Added code to spew multi-pack powerups upon death if available for a + * weapon. Now you're not limited to spewing three homing missiles, for + * example -- you can spew a multi-pack with 8 missiles in it. + * + * 297 4/27/99 4:27p Jeff + * added player respawn osiris event + * + * 296 4/27/99 4:44a Jeff + * only add guidebots to inventory in a single player game...must figure + * out a way to handle initial join of multiplayer + * + * 295 4/27/99 1:38a Jason + * fixed sparse array bug with players + * + * 294 4/26/99 7:53p Jason + * temp fix for multiplayer waypoints + * + * 293 4/24/99 10:39p Samir + * turn off any current ship engine sounds if ship noises are turned off + * + * 292 4/24/99 8:40p Samir + * ship toggle sounds implemented. + * + * 291 4/24/99 7:22p Jason + * fixed another respawn problem + * + * 290 4/24/99 6:45p Jeff + * added functions for theif so he can steal things other than weapons + * + * 289 4/24/99 2:20a Chris + * Added the Neutral_till_hit flag + * + * 288 4/23/99 1:56a Matt + * Added system for counting kills of friendly objects + * + * 287 4/22/99 4:31p Jason + * fixed team spawning problems + * + * 286 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 285 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 284 4/10/99 5:56p Matt + * Cleaned up object initialization code, including create object & load + * object. + * + * 283 4/09/99 7:04p Jason + * changed some texture defines + * + * 282 4/06/99 6:25p Jason + * various fixes for multiplayer + * + * 281 4/06/99 6:02p Matt + * Added score system + * + * 280 4/06/99 2:08p Jason + * fixed crashes with dying models not being set + * + * 279 4/05/99 3:34p Matt + * Cleaned up player start flags + * + * 278 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 277 4/03/99 2:20p Jason + * added better heat damage effect + * + * 276 4/02/99 6:31p Jason + * Fixed multiplayer powerup/player start bugs + * + * 275 4/02/99 3:55p Chris + * Fixed or at least commented about guidebot bugs + * + * 274 3/31/99 10:27a Samir + * code to reset auto select states when ship is initializiaed and code to + * save and load these states from disk. + * + * 273 3/23/99 12:33p Kevin + * More post level results improvements + * + * 272 3/16/99 6:43p Kevin + * OEM Split fixes + * + * 271 3/12/99 7:45p Jeff + * handle demo items (lifeleft, player change type, etc). Reset quad fire + * masks on inventory spew. + * + * 270 3/11/99 6:31p Jeff + * numerous fixes to demo system in multiplayer games (when + * recording/playback a demo in a multiplayer game) + * + * 269 3/10/99 2:25p Kevin + * Save/Load and Demo file fixes + * + * 268 3/09/99 6:34p Kevin + * Made the training mission not be branching, and fixed the crash with + * people dying in the demo playback + * + * 267 3/09/99 6:17p Jason + * fixed observer mode changing + * + * 266 3/09/99 3:19p Jason + * fixed persistent player death message in multiplayer game + * + * 265 3/08/99 5:14p Jason + * added delay to player respawn after death + * + * 264 3/05/99 11:57a Jason + * fixes for OEM + * + * 263 3/04/99 7:39p Matt + * Added sound effects to FreeSpace-style persistent HUD messages. + * + * 262 3/03/99 5:35p Matt + * Added fade-out for goal complete messages + * + * 261 3/03/99 3:38a Samir + * reset reticle when you die. + * + * 260 3/02/99 2:50p Samir + * hud observer mode works with rest of hud. + * + * 259 3/02/99 1:17p Jason + * made player textures play at a fixed rate + * + * 258 3/02/99 12:22p Matt + * Revisions to weapon & ammo powerups. Not fully tested. + * + * 257 3/01/99 7:20p Chris + * Fixed dist problems with finding nearby objects + * + * 256 3/01/99 4:17p Jeff + * + * 255 2/25/99 8:55p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 254 2/25/99 10:28a Jason + * made player able to fire flares in netgames + * + * 253 2/24/99 11:25a Matt + * Small improvement + * + * 251 2/23/99 7:35p Jeff + * handle not spewing of inventory items marked as such + * + * 250 2/22/99 2:04p Jason + * added different damages for players and generics + * + * 249 2/18/99 5:27p Matt + * Added color parm to AddPersistentHUDMessage() and fixed the timeout. + * + * 248 2/17/99 2:08p Jason + * fixed observermode bug + * + * 247 2/17/99 2:04p Jason + * changed spew timeouts just a little + * + * 246 2/16/99 3:47p Jason + * added marker updates to multiplayer server stream + * + * 245 2/15/99 7:50p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 244 2/15/99 11:22a Jason + * took out client-side prediciton + * + * 243 2/13/99 12:32a Jeff + * updated inventory spew to handle 'new' inventory system. Inventory + * reset takes a different parameter too now. + * + * 242 2/12/99 3:38p Jason + * added client side interpolation + * + * 241 2/11/99 1:22a Jeff + * made function to switch a player into AI mode, converted code that did + * this to call this function. + * + * 240 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 239 2/09/99 6:53p Jeff + * implemented 'typing inidcator' in multiplayer...players that are typing + * messages have an icon on them + * + * 238 2/08/99 5:26p Jeff + * removed all calls to MultiSendRemoveObject, incorportated into + * SetObjectDeadFlag. Fixes sequencing issues in multiplayer + * + * 237 2/08/99 3:06a Jeff + * added a function to reset a player's control type + * + * 236 2/06/99 10:03p Matt + * Added keys system + * + * 235 2/04/99 11:20a Jason + * added waypoint message + * + * 234 2/04/99 11:01a Jason + * added anti cheating to multiplayer + * + * 233 2/03/99 4:26p Jason + * made multiplayer coop actually work! + * + * 232 2/03/99 1:50a Jeff + * fixed bug on checking if a player ship is available (if -1 is passed + * in) + * + * 231 2/01/99 4:25p Jeff + * fixed player invulnerabilty after cinematic sequence and death bug + * + * 230 2/01/99 12:56p Jeff + * correctly restore hud if in cinematics + * + * 229 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 228 1/29/99 7:13p Jeff + * localization + * + * 227 1/28/99 6:17p Jason + * added markers + * + * 226 1/27/99 6:37p Jason + * fixed player spawning over terrain + * + * 225 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 224 1/20/99 4:23p Jason + * made player flags work again + * + * 223 1/18/99 6:18p Kevin + * Added controller masking to DALLAS + * + * 222 1/15/99 7:52p Chris + * Updated ObjSetPos() to include a f_update_attach_children flag + * + * 221 1/14/99 4:24p Jason + * made damage effect go away much faster + * + * 220 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 219 1/12/99 7:11p Jeff + * init scripts on player spew + * + * 218 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 217 1/06/99 12:34p Jason + * misc changes for multiplayer + * + * 216 12/01/98 3:35p Matt + * Got rear view working. + * + * 215 11/23/98 1:50p Jason + * added thruster sounds + * + * 214 11/13/98 12:30p Jason + * changes for weapons + * + * 213 11/11/98 7:18p Jeff + * changes made so that a dedicated server's team is always -1 (team game + * or not) + * + * 212 11/11/98 11:41a Jason + * added sunlight damage + * + * 211 11/06/98 11:39a Chris + * Robots with flamethrowers and Omega cannons work in single player + * + * 210 11/03/98 6:16p Chris + * Starting to make on/off and spray weapons accessable to robots + * + * 209 10/29/98 4:54p Jason + * made ClearTransientObjects not work in multiplayer + * + * 208 10/28/98 5:25p Jason + * fixed player death problems in multiplayer + * + * 207 10/22/98 10:45p Samir + * try more death options. + * + * 206 10/22/98 2:58p Chris + * Difficulty levels are in beta + * + * 205 10/19/98 9:22p Matt + * Stop effects sound when resetting player object. + * + * 204 10/19/98 11:35a Chris + * Made a change in when the player gets GB + * + * 203 10/18/98 11:11p Matt + * Added call to ClearTransientObjects() and cleaned up some sequencing + * stuff. + * + * 202 10/18/98 7:27p Samir + * spin on y axis for d2 death. + * + * 201 10/17/98 3:07p Jason + * respawn players not near each other + * + * 200 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 199 10/14/98 4:27p Samir + * use new persistent hud message system for death message. + * + * 198 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 197 10/13/98 4:12p Jason + * toned down speed of observer mode + * + * 196 10/13/98 3:41p Kevin + * bug fixes + * + * 195 10/13/98 2:31p Jason + * automatically create new player starts + * + * 194 10/13/98 1:53p Jason + * added some asserts to track down explosion bug + * + * 193 10/08/98 5:49p Sean + * (Jeff) fixed reset of Quad fire flag when resetting a player ship + * + * 192 10/08/98 3:36p Jason + * fixes for the demo + * + * 191 10/07/98 9:29p Jeff + * reset quad firing masks on init new ship + * + * 190 10/07/98 12:35p Jason + * fixed player cameras going from level to level + * + * 189 10/05/98 6:48p Jason + * took out annoying mprintf + * + * 188 10/03/98 8:05p Matt + * Added asserts + * + * 187 10/02/98 8:54p Jason + * fixed bad parentin on badass damage + * + * 186 10/02/98 12:31p Samir + * restored player breakup with fixed code (from renderobject.cpp too). + * + * 185 10/01/98 11:30a Jeff + * made the observer mode events into just a client event + * + * 184 9/30/98 4:05p Jason + * more parenting fun + * + * 183 9/30/98 3:49p Jeff + * added events for when a player enters/exits observer mode + * + * 182 9/30/98 3:17p Jason + * fixed countermeasure spewing problem + * + * 181 9/29/98 1:59p Jason + * don't do breakup death + * + * 180 9/28/98 8:39p Jason + * give spewed objects parents + * + * 179 9/28/98 10:59a Samir + * clarified passing of pointer to CreateFireball in + * PlayerShipSpewPartSub. Adding two vectors should return a vector + * reference, and that should be valid, but I guess not. + * + * 178 9/23/98 6:07p Samir + * player death cam should fit nicely on walls. + * + * 177 9/23/98 2:55p Chris + * Improved auto-leveling and added newbie leveling + * + * 176 9/22/98 6:48p Jason + * fixed dedicated server problems + * + * 175 9/22/98 5:20p Jason + * fixed some player bugs + * + * 174 9/22/98 11:15a Jeff + * took out bad ASSERT + * + * 173 9/21/98 12:56p Jason + * made camera even smoother + * + * 172 9/21/98 12:11p Jason + * fixed camera problems + * + * 171 9/18/98 6:09p Jason + * added smoother camera + * + * 170 9/18/98 4:52p Jason + * Incremental checkin for camera + * + * 169 9/18/98 1:27p Matt + * Added afterburner release sound + * + * 168 9/18/98 11:18a Jason + * fixed miscellaneous bugs with camera system + * + * 167 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 166 9/16/98 5:10p Jason + * added first pass at thrid-person camera system + * + * 165 9/11/98 12:26p Jason + * got energy to shield and fusion damage working in multiplayer + * + * 164 9/10/98 12:18p Jason + * more changes to afterburner/thrust effect + * + * 163 9/09/98 7:09p Jason + * changed afterburner effect for ships + * + * 162 8/28/98 1:34p Matt + * Added code to reset the waypoint when starting a new level, and while I + * was at it cleaned up the new level start sequencing. + * + * 161 8/24/98 2:45p Jason + * added team start position stuff + * + * 160 8/24/98 12:24p Jason + * added waypoints and player start position flags + * + * 159 8/20/98 7:01p Jeff + * changed sound on energy->shields converter + * + * 158 8/19/98 12:38p Jason + * made countermeasure spewing work correctly + * + * 157 8/17/98 10:48a Jason + * added more sparks when players explode + * + * 156 8/16/98 6:18p Chris + * Fixed player spewing + * + * 155 8/16/98 2:34a Jeff + * fixed a hud message + * + * 154 8/16/98 12:19a Jeff + * added energy->shields function + * + * 153 8/10/98 2:21p Jeff + * changes made due to adding flag for inventory reset + * + * 152 8/07/98 2:44p Jeff + * some changes to ship permissions (reset can take a -1 for pnum in order + * to reset everyone) + * + * 151 8/07/98 10:51a Jason + * made custom textures 64x64 + * + * 150 8/06/98 2:44p Jeff + * added ship permissions functions (first round) + * + * 149 8/03/98 5:56p Jason + * got fusion cannon working correctly + * + * 148 8/03/98 4:29p Jason + * fixed afterburner bug + * + * 147 7/31/98 3:24p Samir + * allow breakup death outside only. + * + * 146 7/31/98 1:55p Jason + * added a function for custom textures + * + * 145 7/31/98 11:51a Jason + * added player ship choosing to multiplayer games + * + * 144 7/29/98 12:47p Jason + * multiplayer changes + * + * 143 7/28/98 5:39p Samir + * tweaked death sequence. + * + * 142 7/28/98 12:29p Jason + * added rotational velocity to multiplayer position packets + * + * 141 7/27/98 5:59p Jason + * added piggyback mode plus multiplayer colors + * + * 140 7/22/98 4:36p Jason + * took out bugs from observer mode + * + * 139 7/22/98 3:16p Jason + * added observer mode + * + * 138 7/20/98 10:42a Jason + * added more player scalars, plus afterburner changes + * + * 137 7/19/98 6:48p Jeff + * got rid of annoying mprintf on death + * + * 136 7/17/98 5:57p Jason + * misc multiplayer changes + * + * 135 7/15/98 2:33p Jason + * added scalar variables for various player skills + * + * 134 7/03/98 3:09p Jeff + * added counter measures inventory + * + * 133 7/02/98 6:37p Jason + * fixed bug with last rev + * + * 132 7/02/98 6:30p Jason + * added some multiplayer stuff for Jeff + * + * 131 7/02/98 12:57p Jason + * various changes for multiplayer positional sequencing + * + * 130 6/29/98 3:08p Jason + * added on/off weapons + * + * 129 6/25/98 5:17p Jason + * added multiple colored balls for players + * + * 128 6/22/98 6:02p Samir + * don't show cockpit when dying. + * + * 127 6/17/98 6:31p Samir + * Added anti-grav warning when dying. + * + * 126 6/16/98 10:54a Jeff + * + * 125 6/15/98 6:08p Chris + * Hit_type to hit_type[0] + * + * 124 6/11/98 12:48p Jason + * added better spewing weapons + * + * 123 6/05/98 4:54p Samir + * fixed bugs in MoveDeathCam. + * + * 122 6/03/98 6:42p Chris + * Added multipoint collision detection an Assert on invalid (infinite + * endpoint). + * + * 121 6/02/98 5:49p Jeff + * changed assert in PlayerSpewObject so it handles a ghost along with + * player type + * + * 120 6/01/98 10:37a Matt + * Added system to spew powerups when the player dies. Still needs Jason + * to deal with mng_FindSpecificGenericPage(). + * + * 119 5/27/98 12:36a Samir + * invulnerablize player at start of E3 demo level, and use default + * weapons. + * + * 118 5/26/98 10:48p Samir + * for E3, new level = special weapons. + * + * 117 5/25/98 6:50p Matt + * Added needed include. + * + * 116 5/22/98 6:27p Samir + * reimplemented spinning D2 death, a bit modified. + * + * 115 5/22/98 11:59a Chris + * Fixed improper uses of FindSoundName and made the proper static sounds + * + * 114 5/15/98 5:41p Jason + * implemented volume lighting system + * + * 113 5/12/98 3:45p Chris + * Added a "dead" buddy when the player dies with the buddy in his + * inventory + * + * 112 5/11/98 4:40p Chris + * AI info is now a malloc'ed pointer + * + * 111 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 110 5/04/98 12:22p Chris + * Improved Guidebot spewing + * + * 109 5/04/98 10:56a Kevin + * Mastertracker fixes/enhancements + * + * 108 5/01/98 6:50p Samir + * changed death sequence to fall on melee kill (single player) and death + * camera improvements. + * + * 107 5/01/98 4:32p Jason + * take care afterburner sound when a player dies + * + * 106 4/30/98 2:48p Kevin + * mastertracker + * + * 105 4/30/98 2:34p Kevin + * Movet mastertracker stuff to playerinit + * + * 104 4/30/98 1:15p Kevin + * Added lifetime deaths and kills for MT + * + * 103 4/30/98 11:32a Chris + * ClearWB to WBClear + * + * 102 4/29/98 12:51p Kevin + * Added mastertracker values + * + * 101 4/27/98 1:14p Jason + * cleaned up afterburner stuff + * + * 100 4/22/98 4:20p Jason + * reset player effects when dead + * + * 99 4/22/98 3:25p Jason + * changes for multiplayer debugging + * + * 98 4/20/98 3:03p Craig + * FROM JASON: Fixed effect resetting bug + * + * 97 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 96 4/18/98 7:17a Samir + * Update level time each player frame. + * + * 95 4/17/98 1:59p Jason + * added cool object effects + * + * 94 4/15/98 2:33p Jason + * changed some smoke trail stuff + * + * 93 4/13/98 12:42p Jason + * changed afterburner effect + * + * 92 4/10/98 8:59p Jason + * toned down red damage effect + * + * 91 4/10/98 3:28p Jason + * fixed afterburner bug + * + * 90 4/10/98 2:42p Jason + * fixed afterburner blobs to be a stream + * + * 89 4/09/98 7:15p Craig + * Remove inventory add + * + * 88 4/09/98 5:18p Jason + * got guided working in multiplayer + * + * 87 4/09/98 2:23p Jason + * added guided/afterburner stuff + * + * 86 4/07/98 7:17p Chris + * Removed guidebots from multiplayer + * + * 85 4/07/98 4:50p Jason + * made red damage glow fade much quicker + * + * 84 4/07/98 4:25p Chris + * Added support for buddy bot + * + * 83 4/06/98 2:54p Jason + * yet more multiplayer changes + * + * 82 4/06/98 12:14p Jason + * changes to multiplayer + * + * 81 4/03/98 12:28p Jason + * fixed confusion with clients changing rooms in multiplayer + * + * 80 4/03/98 11:55a Jason + * fixed polymodel paging problem + * + * 79 4/02/98 6:32p Jason + * get multiple items in inventory spewing + * + * 78 4/01/98 6:23p Jason + * added a slew of stuff for multiplayer + * + * 77 4/01/98 2:13p Jason + * added PlayerChangeShip + * + * 76 4/01/98 12:02p Jason + * incremental checkin for rendering changes + * + * 75 3/31/98 11:15a Jason + * added the ability for player objects to cast light + * + * 74 3/30/98 5:11p Jason + * more changes for game/dll integration + * + * 73 3/27/98 6:53p Samir + * lessen death glitter. + * + * 72 3/23/98 7:37p Jason + * fixed multiplayer shield problem + * + * 71 3/20/98 6:05p Chris + * Fixed a bug with player sounds + * + * 70 3/20/98 5:51p Jason + * more changes for multiplayer + * + * 69 3/20/98 2:18p Jason + * changes for multiplayer + * + * 68 3/19/98 7:15p Jason + * more changes for multiplayer + * + * 67 3/18/98 5:45p Jason + * more multiplayer script integration + * + * 66 3/18/98 12:11p Jason + * fixed non-existant killer problems + * + * 65 3/18/98 11:08a Jason + * more changes for script and multiplayer + * + * 64 3/17/98 3:25p Jason + * added number of team functions + * + * 63 3/17/98 2:40p Samir + * reorg of hud/gauge system. + * + * 62 3/17/98 11:34a Jason + * added GetGoalRoom stuff + * + * 61 3/13/98 12:09p Samir + * made changes to reflect new cockpit.cpp and cockpit.h + * + * 60 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 59 2/25/98 2:05p Jason + * did FOV and object visibility changes + * + * 58 2/25/98 1:01p Jason + * fixed FOV effect showing up for everyone in a multiplayer game + * + * 57 2/24/98 2:31p Jason + * added FOV warp effect for when coming back to life + * + * 56 2/23/98 4:52p Chris + * Removed size hack + * + * 55 2/19/98 6:38p Chris + * Added the ship size hack back in. + * + * 54 2/19/98 6:31p Jason + * fixed bug where a player killer object wasn't valid + * + * 53 2/18/98 11:03p Chris + * Removed the false ship size of 4.0 that we used for the milestone + * + * 52 2/18/98 2:13a Chris + * hacked size to 4 + * + * 51 2/17/98 11:33p Matt + * Stop player weapon firing activity when player dies + * + * 50 2/17/98 3:46p Matt + * Revamped weapon system and got sounds for spray and fusion weapons + * working. Still need to implements getting continuous & cutoff sounds + * from player ship. + * + * 49 2/15/98 4:50p Jeff + * added a call to reset the player's inventory + * + * 48 2/09/98 5:38p Jeff + * changed EVT_PLAYEREXPLODED to EVT_GAMEPLAYEREXPLODED + * + * 47 2/06/98 6:07p Jason + * made multiplayer objects get deleted in single-player mode + * + * 46 2/05/98 2:57p Matt + * Changed code to use ObjSetPos() + * + * 45 2/05/98 10:44a Jason + * changed Red_blast_ring_index define + * + * 44 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 43 2/03/98 4:04p Jeff + * added OSIRIS events for player killed and exploded + * + * 42 1/28/98 1:12p Jason + * took shields field out of Player struct + * + * 41 1/28/98 12:00p Jason + * more changes for multiplayer + * + * 40 1/23/98 6:25p Jason + * Got spray weapons working + * + * 39 1/23/98 11:21a Jason + * incremental multiplayer checkin + * + * 38 1/21/98 6:09p Jason + * Got player deaths working in multiplayer + * + * 37 1/21/98 1:31p Samir + * No use thrust because of multiplayer. + * + * 36 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 35 1/20/98 6:35p Samir + * Death sequence more multiplayer friendly? + * + * 34 1/20/98 6:01p Jason + * first pass at getting multiplayer deaths working + * + * 33 1/20/98 12:10p Jason + * implemented vis effect system + * + * 32 1/19/98 5:49p Samir + * Some more death stuff. + * + * 31 1/08/98 12:31p Samir + * Called InitPlayerNewShip when died. + * + * 30 1/07/98 6:39p Jason + * Fixed player object number stuff + * + * 29 12/17/97 4:02p Samir + * Fixed bug. + * + * 28 12/16/97 4:44p Samir + * Some newer player death stuff. + * + * 27 12/10/97 3:28p Samir + * Some randomness. + * + * 26 12/01/97 2:45p Samir + * Press space bar to quit death sequence. + * + * 25 11/17/97 4:58p Samir + * Fixed player death cam problems. + * + * 24 11/14/97 2:59p Mark + * FROM JASON:changed the way paging weapons and player weapons work + * + * 23 11/11/97 12:31p Samir + * Hopefully fixed bug when death cam is moving from terrain to mine and + * vice-versa. + * + * 22 10/28/97 6:38p Samir + * Took out include to gauges.h + * + * 21 10/27/97 4:31p Samir + * Commented out annoying mprintfs. + * + * 20 10/27/97 10:57a Samir + * A little better close corner death scenes. + * + * 19 10/23/97 6:46p Samir + * Mostly functional player explosions with special FX like fireballs and + * splinters, breakups, etc. + * + * 18 10/22/97 7:26p Samir + * Player death mostly working. Damn hangup still occurs. Freaky. + * + * 17 10/21/97 3:16p Samir + * Death with explosions (not customized yet.) + * + * 16 10/20/97 3:00p Samir + * Tweaked some random behavior in death and 'fixed' the camera on room + * side problem. + * + * 15 10/16/97 4:10p Chris + * Added some bounciness to the player object + * + * 14 10/15/97 3:05p Samir + * Death cam 99% working. + * + * 13 10/10/97 3:57p Samir + * Death sequence mostly perfect. Needs explosions still. + * + * 12 10/08/97 6:36p Samir + * Except for player explosions, player death cycle implemented. May + * change due to asthetics. + * + * 11 10/03/97 11:59a Samir + * Take care of setting player weapon usage stats. + * + * 10 10/01/97 4:51p Samir + * Moved ApplyDamagePlayer to damage.cpp + * + * 9 9/30/97 5:34p Samir + * Moved level script code out of this file. + * + * 8 9/17/97 5:40p Samir + * Fixed some vulnerability problems. + * + * 7 9/17/97 11:35a Samir + * BIG SEGMENT RIPOUT + * + * 6 9/12/97 4:16p Samir + * Added cloaking and invulnerability. + * + * 5 9/05/97 4:37p Samir + * Some nonsense. + * + * 4 9/05/97 3:53p Samir + * Fixed some more initial player stats. + * + * 3 9/05/97 2:33p Samir + * Simple player death should work. + * + * 2 9/05/97 12:26p Samir + * Player takes damage, and dies. + * + * 5 5/06/97 3:45p Matt + * Save all player flags in player struct, not just over_terrain + * + * 4 5/06/97 12:53p Matt + * Save over terrain flag with start position in player struct + * + * 3 4/23/97 6:06p Jason + * made player ship and weapons work correctly together (first pass) + * + * 2 4/01/97 10:49p Matt + * Added code + * + * 1 4/01/97 10:27p Matt + * + * $NoKeywords: $ + */ + +#include "pserror.h" +#include "player.h" +#include "game.h" +#include "hud.h" +#include "gauges.h" +#include "Mission.h" +#include "vecmat.h" +#include "fireball.h" +#include "polymodel.h" +#include "findintersection.h" +#include "hud.h" +#include "splinter.h" +#include "PHYSICS.H" +#include "viseffect.h" +#include "damage.h" +#include "multi.h" +#include "ship.h" +#include "gameevent.h" +#include "gameloop.h" +#include "descent.h" +#include "cockpit.h" +#include "game2dll.h" +#include "robotfire.h" +#include "robot.h" +#include "AIMain.h" +#include "aipath.h" +#include "AIGoal.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "sounds.h" +#include "weapon.h" +#include "stringtable.h" +#include "pilot.h" +#include "vclip.h" +#include +#include "objinit.h" +#include "difficulty.h" +#include "ddio.h" +#include "ObjScript.h" +#include "gamecinematics.h" +#include "demofile.h" +#include "psrand.h" +#include "osiris_share.h" +#include "config.h" +#include "osiris_dll.h" +#include "gamesequence.h" +#include "rocknride.h" +#include "vibeinterface.h" + +#ifdef MACINTOSH +#include "Macros.h" +#endif + +player Players[MAX_PLAYERS]; +int Player_num; +extern bool IsCheater; + +int Num_teams=0; +int Team_game=0; +int Default_ship_permission = 0x01; +float HudNameTan=-1; + +team Teams[MAX_TEAMS]; + +int Highest_player_start=0; + +//Only one of these waypoint variable can be active at one time; the other will be -1 +int Current_waypoint=0; //the most recent manually-set waypoint, or -1 + +//Stuff for the new score info on the HUD +int Score_added=0; //the recently-added amount +float Score_added_timer=0.0; //how long the added value will be displayed + +// Camera stuff +#define MAX_CAMERA_SAMPLES 100 +#define MAX_CAMERA_SUB_SAMPLES 50 +#define CAMERA_LAG_TIME .10f // How far behind the camera is in time +#define CAMERA_SAMPLE_INCREMENT (CAMERA_LAG_TIME/(float)MAX_CAMERA_SUB_SAMPLES) +bool Player_has_camera=false; +int Player_camera_objnum=-1; +float Player_shields_saved_from_last_level = -1.0f; +float Player_energy_saved_from_last_level = -1.0f; + +uint Players_typing; //information about which players are typing messages (to display an icon) + +float Player_camera_last_sample_time=0; +float Player_camera_last_follow_time=0; + +int Camera_sample_index=0; +int Camera_follow_index=0; +vector Camera_sample_vectors[MAX_CAMERA_SAMPLES]; +matrix Camera_sample_matrix[MAX_CAMERA_SAMPLES]; +int Camera_sample_rooms[MAX_CAMERA_SAMPLES]; + +float Total_time_dead=0; + +void StartPlayerDeath(int slot, float damage, bool melee,int fate); + +// Sets up the players array +void InitPlayers () +{ + int i; + + //find the ships that are allowed by default + Default_ship_permission = 0; + for (i=0;i=0); + GameTextures[index].bm_handle=0; + sprintf (GameTextures[index].name,"Player %d texture",i); + Players[i].custom_texture_handle=index; + } + PlayerResetShipPermissions(-1,true); + Players_typing = 0; +} + +//Look for player objects & set player starts +void FindPlayerStarts() +{ + int i; + int unique=0; + + //Flag all players as unused + for (i=0;iHighest_player_start) + Highest_player_start=Objects[i].id; + + unique++; + } + + mprintf ((0,"There are %d unique start positions in this level\n",unique)); + + // Now create the extra players + if (Game_mode & GM_MULTI) + { + // Build a list so we can fill in the rest + int choose_list[MAX_PLAYERS]; + int num_choose=0; + for (i=0;i=0); + Players[Objects[objnum].id].start_pos = Players[oldplaynum].start_pos; + Players[Objects[objnum].id].start_roomnum = Players[oldplaynum].start_roomnum; + Players[Objects[objnum].id].start_orient = Players[oldplaynum].start_orient; + Players[Objects[objnum].id].startpos_flags = Players[oldplaynum].startpos_flags; + Players[Objects[objnum].id].objnum=objnum; + if (Objects[objnum].id>Highest_player_start) + Highest_player_start=Objects[objnum].id; + } + + for (i=0;i=0); + Players[Objects[objnum].id].start_pos = Players[oldplaynum].start_pos; + Players[Objects[objnum].id].start_roomnum = Players[oldplaynum].start_roomnum; + Players[Objects[objnum].id].start_orient = Players[oldplaynum].start_orient; + Players[Objects[objnum].id].startpos_flags = Players[oldplaynum].startpos_flags; + Players[Objects[objnum].id].objnum=objnum; + if (Objects[objnum].id>Highest_player_start) + Highest_player_start=Objects[objnum].id; + } + } + } + +} + +// Returns a random player starting position +int PlayerGetRandomStartPosition (int slot) +{ + if (Netgame.flags & NF_RESPAWN_WAYPOINT) + { + // Grab an autowaypoint + if (Players[slot].current_auto_waypoint_room!=-1) + { + int waypointnum=-(Players[slot].current_auto_waypoint_room-1); + return waypointnum; + } + else + { + if (Current_waypoint<0) + return 0; + else + return Current_waypoint; + } + } + + ps_srand((timer_GetTime()*1000)); + // If this is a team game then pick a spot on that team + if (Team_game) + { + + int team= PlayerGetTeam(slot); + mprintf ((0,"Picking team start position, team=%d.\n",team)); + int num_avail=0; + int avail_array[MAX_PLAYERS]; + + for (int i=0;i<=Highest_player_start;i++) + { + if ((Players[i].startpos_flags & (1<0) + { + int num; + int done=0; + while (!done) + { + num=avail_array[ps_rand()%(num_avail)]; + if (Players[num].start_roomnum!=-1) + done=1; + } + return num; + } + } + + // Default to non-team mode + int num; + int done=0; + int badcount=0; + mprintf ((0,"Picking non-team start position.\n")); + while (!done) + { + num=ps_rand()%(Highest_player_start+1); + if (Players[num].start_roomnum!=-1) + { + // Check to see if there are any other players in this room + int objnum=-1; + if (Players[num].start_roomnum>=0) + { + room *rp=&Rooms[Players[num].start_roomnum]; + objnum=rp->objects; + } + else + { + objnum=Terrain_seg[Players[num].start_roomnum].objects; + } + int bad=0; + for (;objnum!=-1 && !bad;objnum=Objects[objnum].next) + { + if (Objects[objnum].type==OBJ_PLAYER) + bad=1; + } + + if (bad) + { + badcount++; + if (badcount>=15) // give up after fifteen tries + { + mprintf ((0,"Stopping cuz I couldn't find a valid player position!\n")); + done=1; + } + } + else + done=1; + } + } + + mprintf ((0,"Picked index for start position %d\n",num)); + + + + return num; +} + +// Called from a single player game to get rid of all multiplayer ships +void DeleteMultiplayerObjects () +{ + for (int i=0;i<=Highest_object_index;i++) + { + if (Objects[i].type==OBJ_PLAYER && Objects[i].id!=Player_num) + ObjDelete(i); + } +} + +// Stops all sounds for a player +void PlayerStopSounds (int slot) +{ + if (Players[slot].afterburner_sound_handle!=-1) + { + Sound_system.StopSoundImmediate (Players[slot].afterburner_sound_handle); + Players[slot].afterburner_sound_handle=-1; + } + + if (Players[slot].thruster_sound_handle!=-1) + { + Sound_system.StopSoundImmediate (Players[slot].thruster_sound_handle); + Players[slot].thruster_sound_handle=-1; + } + +} + + +// Called when a player is entering the game for the first time +void InitPlayerNewGame (int slot) +{ + Players[slot].num_kills_total = 0; + Players[slot].num_deaths_total = 0; + Players[slot].score = 0; + + uint bit = (0x01<=0 && slotINITIAL_SHIELDS + if(Player_shields_saved_from_last_level>INITIAL_SHIELDS) + Objects[Players[slot].objnum].shields = Player_shields_saved_from_last_level; + if(Player_energy_saved_from_last_level>INITIAL_ENERGY) + Players[slot].energy = Player_energy_saved_from_last_level; + + //set this to -1 to prevent any unneeded madness + Player_shields_saved_from_last_level = -1.0f; + Player_energy_saved_from_last_level = -1.0f; + } + + Players[slot].num_kills_level = 0; + Players[slot].friendly_kills_level = 0; + Players[slot].num_hits_level = 0; + Players[slot].num_discharges_level = 0; + Players[slot].num_markers=0; + Players[slot].inventory.Reset(true,(Game_mode&GM_MULTI)?INVRESET_ALL:INVRESET_LEVELCHANGE); + Players[slot].counter_measures.Reset(true,(Game_mode&GM_MULTI)?INVRESET_ALL:INVRESET_LEVELCHANGE); + Players[slot].keys = 0; + Players[slot].num_deaths_level = 0; + + if (Game_mode & GM_MULTI) + NetPlayers[slot].packet_time=0; + + if (slot==Player_num) + Player_camera_objnum=-1; + + //Turn off rear view + Players[slot].flags &= ~PLAYER_FLAGS_REARVIEW; + + // Kill autowaypoint + Players[slot].current_auto_waypoint_room=-1; + + // Restore Quad Lasers if they are still in inventory + static int quad_laser_id = -2; + if(quad_laser_id==-2) + quad_laser_id = FindObjectIDName("QuadLaser"); + if(quad_laser_id!=-1) + { + if(Players[slot].inventory.CheckItem(OBJ_POWERUP,quad_laser_id)) + { + object *pobj = &Objects[Players[slot].objnum]; + pobj->dynamic_wb[LASER_INDEX].flags |= DWBF_QUAD; + pobj->dynamic_wb[SUPER_LASER_INDEX].flags |= DWBF_QUAD; + } + } + + uint bit = (0x01<type != OBJ_ROBOT)) + Players[slot].inventory.Add(OBJ_ROBOT, ROBOT_GUIDEBOT); + } + + //Limit the player's ammo to what the current ship can hold + player *player = &Players[slot]; + ship *ship = &Ships[player->ship_index]; + for (int i=0;i= SECONDARY_INDEX) || ship->static_wb[i].ammo_usage) + player->weapon_ammo[i] = min(ship->max_ammo[i],player->weapon_ammo[i]); + } + + +#ifdef E3_DEMO + extern float E3_TIME_LIMIT; + + MakePlayerInvulnerable(slot, E3_TIME_LIMIT*2); +//@@ Players[slot].energy=200.0f; +//@@ Players[slot].weapon_flags=HAS_FLAG(LASER_INDEX)+HAS_FLAG(PLASMA_INDEX)+HAS_FLAG(VAUSS_INDEX) +//@@ +HAS_FLAG(MICROWAVE_INDEX); +//@@ Players[slot].weapon_flags+= HAS_FLAG(GUIDED_INDEX)+HAS_FLAG(NAPALMROCKET_INDEX)+HAS_FLAG(CYCLONE_INDEX); +//@@ +//@@ for (int i=0;i=0 && slot= 0 && Players[slot].objnum <= Highest_object_index && Objects[Players[slot].objnum].type != OBJ_NONE) + Objects[Players[slot].objnum].weapon_fire_flags = 0; + + //reset the inventory + Players[slot].inventory.Reset(true,inven_reset); + Players[slot].counter_measures.Reset(true,inven_reset); + + Players[slot].last_homing_warning_sound_time = 0.0f; + Players[slot].last_hit_wall_sound_time = 0.0f; + Players[slot].multiplayer_flags=0; + Players[slot].last_multiplayer_flags=0; + Players[slot].afterburner_mag=0; + Players[slot].thrust_mag=0; + Players[slot].last_guided_time=0; + Players[slot].afterburner_sound_handle=-1; + + Players[slot].thruster_sound_state=0; + Players[slot].thruster_sound_handle=-1; + + Players[slot].movement_scalar=1; + Players[slot].armor_scalar=1; + Players[slot].damage_scalar=1; + Players[slot].turn_scalar=1; + Players[slot].weapon_recharge_scalar=1; + Players[slot].weapon_speed_scalar=1; + //Enable all controller input + Players[slot].controller_bitflags = 0xffffffff; + + ResetWeaponSelectStates(); // reset storage of current weapon class selected per slot. + ResetReticle(); + + //add his guidebot (if it is a guidebot game) + //this is here in case DMFC calls this function (which would remove the guidebot) + if(GetGameState()==GAMESTATE_LVLPLAYING && Game_mode&GM_MULTI && Netgame.local_role==LR_SERVER && Netgame.flags&NF_ALLOWGUIDEBOT) + { + if((!Players[slot].inventory.CheckItem(OBJ_ROBOT, ROBOT_GUIDEBOT)) && (ObjGet(Buddy_handle[slot])->type != OBJ_ROBOT)) + Players[slot].inventory.Add(OBJ_ROBOT, ROBOT_GUIDEBOT); + } + + // update IntelliVIBE + if(slot==Player_num) + { + VIBE_PlayerRespawn(); + } +} + +// Gives the named player an afterburner +void PlayerGiveAfterburner (int slot) +{ + ASSERT (slot>=0 && slot=0 && slot=0 && slot=0); + ASSERT (Player_camera_last_follow_time>=0); + + while (Player_camera_last_sample_time>=CAMERA_SAMPLE_INCREMENT) + { + Player_camera_last_sample_time-=CAMERA_SAMPLE_INCREMENT; + total_increments++; + } + + if (total_increments>0) + { + vector delta_vec=playerobj->pos-Camera_sample_vectors[Camera_sample_index]; + + vector delta_r=playerobj->orient.rvec-Camera_sample_matrix[Camera_sample_index].rvec; + vector delta_u=playerobj->orient.uvec-Camera_sample_matrix[Camera_sample_index].uvec; + vector delta_f=playerobj->orient.fvec-Camera_sample_matrix[Camera_sample_index].fvec; + + delta_vec/=total_increments; + delta_r/=total_increments; + delta_u/=total_increments; + delta_f/=total_increments; + + vector current_pos=Camera_sample_vectors[Camera_sample_index]; + vector current_r=Camera_sample_matrix[Camera_sample_index].rvec; + vector current_u=Camera_sample_matrix[Camera_sample_index].uvec; + vector current_f=Camera_sample_matrix[Camera_sample_index].fvec; + + int current_room=Camera_sample_rooms[Camera_sample_index]; + + for (int i=0;iroomnum; + + Camera_sample_matrix[Camera_sample_index].rvec=current_r; + Camera_sample_matrix[Camera_sample_index].uvec=current_u; + Camera_sample_matrix[Camera_sample_index].fvec=current_f; + + Camera_sample_index++; + + if (Camera_sample_index==MAX_CAMERA_SAMPLES) + Camera_sample_index=0; + + current_pos+=delta_vec; + current_r+=delta_r; + current_u+=delta_u; + current_f+=delta_f; + } + + Camera_sample_vectors[Camera_sample_index]=current_pos; + Camera_sample_rooms[Camera_sample_index]=playerobj->roomnum; + Camera_sample_matrix[Camera_sample_index]=playerobj->orient; + } + + while (Player_camera_last_follow_time>=CAMERA_SAMPLE_INCREMENT) + { + Camera_follow_index++; + + if (Camera_follow_index==MAX_CAMERA_SAMPLES) + Camera_follow_index=0; + + Player_camera_last_follow_time-=CAMERA_SAMPLE_INCREMENT; + } + + // Get normalize position of the following camera + int follow_start=Camera_follow_index; + int follow_end=(Camera_follow_index+1)%MAX_CAMERA_SAMPLES; + + float follow_norm=Player_camera_last_follow_time/CAMERA_SAMPLE_INCREMENT; + + ASSERT (follow_norm>=0 && follow_norm<=1); + + // Set position + dest_pos=((1.0-follow_norm)*Camera_sample_vectors[follow_start]) + +(follow_norm*Camera_sample_vectors[follow_end]); + + // Set room + if (follow_norm>.5) + dest_room=Camera_sample_rooms[follow_start]; + else + dest_room=Camera_sample_rooms[follow_end]; + + // Set orientation matrix + dest_mat.rvec=((1.0-follow_norm)*Camera_sample_matrix[follow_start].rvec)+((follow_norm)*Camera_sample_matrix[follow_end].rvec); + dest_mat.uvec=((1.0-follow_norm)*Camera_sample_matrix[follow_start].uvec)+((follow_norm)*Camera_sample_matrix[follow_end].uvec); + dest_mat.fvec=((1.0-follow_norm)*Camera_sample_matrix[follow_start].fvec)+((follow_norm)*Camera_sample_matrix[follow_end].fvec); + + // Set camera position + fvi_info hit_data; + fvi_query fq; + vector new_pos=dest_pos; + new_pos-=dest_mat.fvec*(playerobj->size*3); + new_pos+=dest_mat.uvec*(playerobj->size/2); + + fq.p1 = &new_pos; + fq.p0 = &dest_pos; + fq.startroom = dest_room; + fq.rad = .5; + fq.thisobjnum = OBJNUM(playerobj); + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS|FQ_IGNORE_NON_LIGHTMAP_OBJECTS; + fvi_FindIntersection(&fq, &hit_data); + + ObjSetPos (&Objects[Player_camera_objnum],&hit_data.hit_pnt,hit_data.hit_room,&dest_mat, false); +} + +// Creates or destroys a player camera depending on what the situation calls for +void SetupPlayerCamera () +{ + object *playerobj=&Objects[Players[Player_num].objnum]; + + if (Player_has_camera) + { + if (Player_camera_objnum==-1) + { + // We have to create a new camera + int objnum = ObjCreate(OBJ_CAMERA, 0, playerobj->roomnum, &playerobj->pos, &playerobj->orient); + if (objnum<0) + { + // Couldn't create a camera for some reason + Player_has_camera=false; + Player_camera_objnum=0; + return; + } + else + { + Player_camera_objnum=objnum; + Camera_sample_index=MAX_CAMERA_SUB_SAMPLES; + Player_camera_last_sample_time=0; + Player_camera_last_follow_time=0; + Camera_follow_index=0; + + for (int i=0;iorient; + Camera_sample_vectors[i]=playerobj->pos; + Camera_sample_rooms[i]=playerobj->roomnum; + } + } + } + + if (Viewer_object==playerobj) + { + // Set viewer to be camera + Viewer_object=&Objects[Player_camera_objnum]; + } + + UpdatePlayerCameraPosition (); + } + else + { + if (Player_camera_objnum!=-1) + { + SetObjectDeadFlag (&Objects[Player_camera_objnum]); + Player_camera_objnum=-1; + } + } +} + + +// Does actions for the given player slot +void DoPlayerFrameForOne (int slot) +{ + ASSERT (slot>=0 && slottype==OBJ_OBSERVER && Players[slot].piggy_objnum!=-1) + { + object *observed=&Objects[Players[slot].piggy_objnum]; + ObjSetPos (obj,&observed->pos,observed->roomnum,&observed->orient,false); + + obj->mtype.phys_info=observed->mtype.phys_info; + + } + + if (obj->type==OBJ_PLAYER || obj->type==OBJ_OBSERVER && obj->id==Player_num) + { + // Do camera stuff if needed + SetupPlayerCamera (); + } + + if(Demo_flags != DF_PLAYBACK) + { + if ((Players[slot].flags & PLAYER_FLAGS_DYING) || (Players[slot].flags & PLAYER_FLAGS_DEAD)) + DoNewPlayerDeathFrame(slot); + } + + // do invulnerable stuff + if (Players[slot].flags & PLAYER_FLAGS_INVULNERABLE) + { + Players[slot].invulnerable_time -= Frametime; + if (Players[slot].invulnerable_time <= 0.0f) + { + // deinvulnerablize the player!! + MakePlayerVulnerable(slot); + } + } + + // Do terrain damage stuff + if (Terrain_sky.damage_per_second>0 && !(Players[slot].flags & (PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD))) + { + if (OBJECT_OUTSIDE (obj)) + { + float scalar=GetTerrainDynamicScalar (&obj->pos,CELLNUM(obj->roomnum)); + + // White out the screen + if (scalar>0 && slot==Player_num) + { + float vals[4]; + + vals[0]=1.0; + vals[1]=1.0; + vals[2]=.8f; + vals[3]=scalar/4; + + CreateNewEvent (RENDER_EVENT,0,0,&vals,sizeof(float)*4,DrawAlphaEvent); + } + + if (scalar>.7) + { + SetNapalmDamageEffect (obj,NULL,napalm_id); + obj->effect_info->damage_time=1; + obj->effect_info->damage_per_second=2; + } + } + } + + Players[slot].invul_magnitude-=(Frametime *2); + + // Drop the red effect of the console player faster than other players + float alpha_blend_scalar_time=1.0; + if (slot==Player_num) + alpha_blend_scalar_time=2.0; + + Players[slot].damage_magnitude-=(Frametime*(MAX_DAMAGE_MAG)*alpha_blend_scalar_time); + Players[slot].edrain_magnitude-=(Frametime*(MAX_EDRAIN_MAG)*alpha_blend_scalar_time); + + if (Players[slot].invul_magnitude<0) + Players[slot].invul_magnitude=0; + + if (Players[slot].damage_magnitude<0) + Players[slot].damage_magnitude=0; + if (Players[slot].edrain_magnitude<0) + Players[slot].edrain_magnitude=0; + + if ((obj->weapon_fire_flags & WFF_SPRAY) && !(Players[slot].flags & PLAYER_FLAGS_DEAD)) + { + int ship_index=Players[slot].ship_index; + int wb_index=Players[slot].weapon[PW_PRIMARY].index; + DoSprayEffect (&Objects[Players[slot].objnum],&Ships[ship_index].static_wb[wb_index],wb_index); + } + + // Do On/Off weapons for multiplayer + if (slot!=Player_num && (obj->weapon_fire_flags & WFF_ON_OFF) && !(Players[slot].flags & PLAYER_FLAGS_DEAD)) + { + FireOnOffWeapon (&Objects[Players[slot].objnum]); + } + + // Do thrust stuff here + if (Player_num == slot && Game_toggles.ship_noises == false) { + if (Players[slot].thruster_sound_handle > -1) { + Sound_system.StopSoundImmediate (Players[slot].thruster_sound_handle); + Players[slot].thruster_sound_handle = -1; + } + } + + if (Players[slot].flags & PLAYER_FLAGS_THRUSTED) + { + Players[slot].thrust_mag+=(Frametime*2); + if (Players[slot].thrust_mag>1) + Players[slot].thrust_mag=1; + + // Play thrust sound + if (slot==Player_num) + { + if (Players[slot].thruster_sound_state==0) + { + if (Players[slot].thruster_sound_handle!=-1) + { + Sound_system.StopSoundImmediate (Players[slot].thruster_sound_handle); + Players[slot].thruster_sound_handle=-1; + } + if (Players[slot].thruster_sound_handle==-1) { + if (Game_toggles.ship_noises) { + Players[slot].thruster_sound_handle=Sound_system.Play3dSound (SOUND_SHIP_FORWARD_THRUST, obj); + } + } + + Players[slot].thruster_sound_state=1; + } + } + } + else + { + // Cool down thrust + Players[slot].thrust_mag-=(Frametime*2); + if (Players[slot].thrust_mag<0) + Players[slot].thrust_mag=0; + + // Stop thrust sound + if (slot==Player_num) + { + if (Players[slot].thruster_sound_state==1) + { + if (Players[slot].thruster_sound_handle!=-1) + { + Sound_system.StopSoundImmediate (Players[slot].thruster_sound_handle); + + } + + Players[slot].thruster_sound_handle=-1; + + if (Game_toggles.ship_noises) { + Sound_system.Play3dSound (SOUND_SHIP_FORWARD_RELEASE, obj); + } + } + + if (Players[slot].thruster_sound_handle==-1) + { + if (Game_toggles.ship_noises) { + Players[slot].thruster_sound_handle=Sound_system.Play3dSound (SOUND_SHIP_IDLE, obj); + } + } + Players[slot].thruster_sound_state=0; + } + } + + + // Do afterburner stuff + if ((Players[slot].flags & PLAYER_FLAGS_AFTERBURN_ON) && !(Players[slot].flags & (PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD))) + { + if (Players[slot].afterburner_sound_handle==-1) + { + // turn on the afterburner! + Players[slot].afterburner_sound_handle=Sound_system.Play3dSound (SOUND_AFTERBURNER, obj); + } + + Players[slot].afterburner_mag+=(Frametime*2); + if (Players[slot].afterburner_mag>1) + Players[slot].afterburner_mag=1; + } + else + { + // Cool down afterburner + Players[slot].afterburner_mag-=(Frametime*2); + if (Players[slot].afterburner_mag<0) + Players[slot].afterburner_mag=0; + + if (Players[slot].afterburner_sound_handle!=-1) + { + Sound_system.StopSoundImmediate (Players[slot].afterburner_sound_handle); + Players[slot].afterburner_sound_handle=-1; + Sound_system.Play3dSound(SOUND_AFTERBURNER_TAIL, obj); + } + } +} + +// Do player automatic frame (all player automatic actions) +// Does all player, single or multiplayer games +void DoPlayerFrame() +{ + if ( Demo_flags==DF_PLAYBACK ) + { + for (int i=0;iid].flags & (PLAYER_FLAGS_DEAD+PLAYER_FLAGS_DYING))) { + // player is dying. We don't want to make player dead yet. + Players[playerobj->id].flags |= PLAYER_FLAGS_DYING; + + //Take care of stuff that should happen when you die + ClearPlayerFiring(playerobj,PW_PRIMARY); + ClearPlayerFiring(playerobj,PW_SECONDARY); + + //Start the death sequence + StartPlayerDeath(playerobj->id, -playerobj->shields, melee,fate); + + //Send the Game status to the rock'n'ride chair + if(playerobj->id==Player_num) + { + RNR_UpdateGameStatus(RNRGSC_PLAYERDIES); + } + } +} + +// Chooses the style of death a player is going to use +int PlayerChooseDeathFate (int slot,float damage,bool melee) +{ + int fate; + bool is_moving; + object *playerobj=&Objects[Players[slot].objnum]; + + ASSERT (slot>=0 && slotmtype.phys_info.velocity)) + is_moving = true; + else + is_moving = false; + + fate=(damage <= DEATH_BREAKUP_THRESHOLD && is_moving && !melee && OBJECT_OUTSIDE(playerobj)) ? DEATH_BREAKUP : + (damage <= DEATH_EXPLODE_THRESHOLD || melee) ? DEATH_FALL: DEATH_INSTANT; + + if (fate == DEATH_BREAKUP) + { + if ((ps_rand()%4) < 2) + fate = DEATH_D2STYLE; + } + else if (fate == DEATH_FALL) + { + if ((ps_rand()%4) != 3) + fate = DEATH_D2STYLE; + } + +// split ship into subobjects. they should still be one object but now with several parts. + if (playerobj->rtype.pobj_info.dying_model_num==-1) + { + if (fate==DEATH_BREAKUP) + { + fate=DEATH_FALL; + } + } + + return fate; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Starts player death sequence. + +void StartPlayerDeath(int slot, float damage, bool melee,int fate) +{ + int objnum; + object *playerobj = &Objects[Players[slot].objnum]; + int killer_num=Players[playerobj->id].killer_objnum; + object *killer; + + ASSERT (slot>=0 && slotid].num_deaths_level++; + Players[playerobj->id].num_deaths_total++; + // Do killer object stuff + if (killer_num<0 || killer_num>MAX_OBJECTS) + killer=NULL; + else + { + killer = &Objects[killer_num]; + if (killer->type==OBJ_NONE) + killer=NULL; + } + +// determine current state of player object. +// create death camera if it doesn't exit + if (slot == Player_num) { + objnum = ObjCreate(OBJ_CAMERA, 0, playerobj->roomnum, &playerobj->pos, &playerobj->orient); + if (objnum == -1) { + mprintf((0, "Failed to create death cam.\n")); + Int3(); + } + else { + Death[slot].camera = &Objects[objnum]; + if (Objects[objnum].type == OBJ_NONE) + Int3(); // Samir- This really shouldn't happen. will cause assertion + } + + Death[slot].oldviewer = Viewer_object; + + // set screen mode + if(Cinematic_inuse){ + Death[slot].saved_cockpit = Cinematic_GetOldHudMode(); + }else{ + Death[slot].saved_cockpit = GetHUDMode(); + } + + Death[slot].in_cockpit = true; + } + +// save player ship information that will be changed here. + Death[slot].saved_ctrl_type = playerobj->control_type; + Death[slot].saved_phys_flags = playerobj->mtype.phys_info.flags; + Death[slot].saved_drag = playerobj->mtype.phys_info.drag; + Death[slot].saved_player_modelnum = playerobj->rtype.pobj_info.model_num; + Death[slot].saved_rotthrust = playerobj->mtype.phys_info.rotthrust; + Death[slot].breakup_count = 0; + + if (killer==NULL) // If the killer isn't valid, just use the reverse forward direction + Death[slot].force_dir = -playerobj->orient.fvec; + else + Death[slot].force_dir = playerobj->pos - killer->pos; + + vm_NormalizeVector(&Death[slot].force_dir); + Death[slot].force_dir = Death[slot].force_dir * playerobj->orient; + + // determine fate of player ship + if (fate==-1) + Death[slot].fate=PlayerChooseDeathFate (slot,damage,melee); + else + Death[slot].fate=fate; + +// split ship into subobjects. they should still be one object but now with several parts. + if (playerobj->rtype.pobj_info.dying_model_num!=-1) + { + playerobj->rtype.pobj_info.model_num = playerobj->rtype.pobj_info.dying_model_num; + Death[slot].dying_model = GetPolymodelPointer(playerobj->rtype.pobj_info.model_num); + playerobj->rtype.pobj_info.subobj_flags = 0xffffffff; +// playerobj->rtype.pobj_info.subobj_flags = ~(0xffffffff << (Death[slot].dying_model->n_models)); +// mprintf((0, "initflags=%08x\n", playerobj->rtype.pobj_info.subobj_flags)); + } + else + { + // This ship has no dying model so dont break up + if (Death[slot].fate==DEATH_BREAKUP) + Int3(); // Get Jason - a death was chosen that this ship doesn't support + + Death[slot].dying_model = GetPolymodelPointer(playerobj->rtype.pobj_info.model_num); + playerobj->rtype.pobj_info.subobj_flags = 0xffffffff; + } + +// determine physics properties of death + SetObjectControlType(playerobj, CT_NONE); + float rotate_adj = 0.0f; + + if (Death[slot].fate == DEATH_BREAKUP) + { + // a breakup typically means the ship starts to fall apart. + Death[slot].accel_mod = 1.0f; + playerobj->mtype.phys_info.flags = (PF_FIXED_ROT_VELOCITY); + playerobj->mtype.phys_info.drag=.005f; + playerobj->mtype.phys_info.mass = 3000.0f; + playerobj->mtype.phys_info.coeff_restitution=0.3f; + rotate_adj = 32768.0f; + } + else if (Death[slot].fate == DEATH_D2STYLE) { + Death[slot].accel_mod = 0.960f; + playerobj->mtype.phys_info.drag=50.0; + playerobj->mtype.phys_info.mass = 3000.0f; + playerobj->mtype.phys_info.flags = PF_FIXED_ROT_VELOCITY; + playerobj->mtype.phys_info.coeff_restitution=0.3f; + rotate_adj = 32768.0f; + } + else + { + // if we're falling or dying immediately, let gravity take control of the ship while it + // deaccelerates. + Death[slot].accel_mod = 0.960f; + playerobj->mtype.phys_info.flags = PF_GRAVITY|PF_FIXED_ROT_VELOCITY; + playerobj->mtype.phys_info.num_bounces=0; + playerobj->mtype.phys_info.coeff_restitution=.03f; + playerobj->mtype.phys_info.mass=2500.0f; + playerobj->mtype.phys_info.drag=50.0f; + rotate_adj = 32768.0f; + } + + playerobj->mtype.phys_info.rotvel.x = (float)((rotate_adj)); + playerobj->mtype.phys_info.rotvel.y = (float)((rotate_adj)); + playerobj->mtype.phys_info.rotvel.z = (float)((rotate_adj)); + +// set times and other stuff for dying. + if (Death[slot].fate == DEATH_FALL) + Death[slot].max_time_dying = 3.0f; + else if (Death[slot].fate == DEATH_INSTANT) + Death[slot].max_time_dying = 2.5f; + else if (Death[slot].fate == DEATH_BREAKUP) + Death[slot].max_time_dying = 4.0f; + else if (Death[slot].fate == DEATH_D2STYLE) + Death[slot].max_time_dying = 2.0f; + + Total_time_dead=0; + + Death[slot].initial_death_time = Gametime; + Death[slot].physics_frametime = Gametime; + Death[slot].fate_frametime = Gametime; + +#ifndef RELEASE +// debug_deathtype(slot, damage); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +// The Death sequencer. + +void DoNewPlayerDeathFrame(int slot) +{ + object *playerobj; + + ASSERT (slot>=0 && slotDEATH_RESPAWN_TIME) + { + AddPersistentHUDMessage(GR_RGB(0,255,0),HUD_MSG_PERSISTENT_CENTER, Game_window_y + Game_window_h - 20, + HUD_MSG_PERSISTENT_INFINITE, 0, SOUND_NONE_INDEX, TXT_SPACETOCONT); + } + } + + +// depending on state of player ship, perform some operation + if (Players[playerobj->id].flags & PLAYER_FLAGS_DYING) + { + // modify player ship physics as needed. + // player ship will fall to the ground and deaccelerate + // modify physics properties every INTERVAL + if ((Death[slot].physics_frametime+DEATH_PHYS_INTERVAL) > Gametime) + { + if (Death[slot].fate==DEATH_FALL) { + playerobj->mtype.phys_info.rotvel.x /= 1.15f; + playerobj->mtype.phys_info.rotvel.y /= 1.1f; + playerobj->mtype.phys_info.rotvel.z /= 1.0f; + } + else if (Death[slot].fate == DEATH_D2STYLE) { + playerobj->mtype.phys_info.rotvel.x /= 1.15f; + playerobj->mtype.phys_info.rotvel.y /= 1.0f; + playerobj->mtype.phys_info.rotvel.z /= 1.05f; + } + else { + playerobj->mtype.phys_info.rotvel.x /= 1.10f; + playerobj->mtype.phys_info.rotvel.y /= 1.05f; + playerobj->mtype.phys_info.rotvel.z /= 1.0f; + } + + playerobj->mtype.phys_info.velocity *= Death[slot].accel_mod; + Death[slot].physics_frametime = Gametime; + } + + // move death camera if distance between death camera and player is less than a certain amount. + if (slot == Player_num && Death[slot].in_cockpit) { + if (Death[slot].time_dying > DEATH_COCKPIT_TIME) { + Death[slot].in_cockpit = false; + SetHUDMode(HUD_LETTERBOX); + Viewer_object = Death[slot].camera; // set the current viewer to be this new camera. + } + } + else if (slot == Player_num) + { + vector directional; + float death_cam_dist = vm_VectorDistanceQuick(&Death[slot].camera->pos, &playerobj->pos); + + if (death_cam_dist < MIN_DEATHCAM_DIST) + { + vm_MakeRandomVector(&directional); + MoveDeathCam(slot, &directional, MEDIAN_DEATHCAM_DIST); + } + else if (death_cam_dist > MAX_DEATHCAM_DIST) + { + vm_MakeRandomVector(&directional); + MoveDeathCam(slot, &directional, MEDIAN_DEATHCAM_DIST); + } + + // check to see if the vector from death cam to player is obstructed by something. + fvi_info hit_data; + fvi_query fq; + + hit_data.hit_type[0] = (OBJECT_OUTSIDE(playerobj) ? HIT_TERRAIN : HIT_WALL); + fq.p1 = &playerobj->pos; + fq.p0 = &Death[slot].camera->pos; + fq.startroom = Death[slot].camera->roomnum; + fq.rad = .5; + fq.thisobjnum = OBJNUM(Death[slot].camera); + fq.ignore_obj_list = NULL; + fq.flags = 0; + fvi_FindIntersection(&fq, &hit_data); + + if (hit_data.hit_type[0] == HIT_WALL) { + // death camera's view is obstructed, move the death cam + // mprintf((0, "Death cam view obstructed, changing view...\n")); + vm_MakeRandomVector(&directional); + MoveDeathCam(slot, &directional, MEDIAN_DEATHCAM_DIST); + } + + // orient death camera towards player ship + directional = playerobj->pos - Death[slot].camera->pos; + if (vm_GetMagnitudeFast(&directional) == 0.0f) + directional.x += 1.0f; + vm_VectorToMatrix(&Death[slot].camera->orient, &directional, NULL, NULL); + } + + // perform fate specific actions here + if (Death[slot].time_dying >= Death[slot].max_time_dying) + { + PlayerShipExplode(playerobj, 40.0f); + Players[playerobj->id].flags |= PLAYER_FLAGS_DEAD; + Players[playerobj->id].flags &= ~PLAYER_FLAGS_DYING; + } + // fate action occurs every fate interval. + else if ((Death[slot].fate_frametime+DEATH_FATE_INTERVAL) > Gametime) { + if (Death[slot].fate == DEATH_BREAKUP) { + if (ps_rand() % 8) { + // break up a piece of the ship + if (Death[slot].breakup_count == 2) { + Death[slot].time_dying = Death[slot].max_time_dying; + } + else { + PlayerShipBreakup(playerobj, 20.0f+(ps_rand() % 10)); + Death[slot].breakup_count++; + // mprintf((0, "Breakup!\n")); + } + } + + } + Death[slot].fate_frametime = Gametime; + } + + // do smoke trails and special visual effects. + if ((ps_rand()%6)==0) + { + // smoke + int visnum; + vector smoke_pt; + smoke_pt = (-playerobj->orient.fvec) * (playerobj->size*0.5f); + smoke_pt = playerobj->pos + smoke_pt; + visnum=CreateFireball (&smoke_pt,BLACK_SMOKE_INDEX,playerobj->roomnum,VISUAL_FIREBALL); + if (visnum>=0) + VisEffects[visnum].size=1.0+((ps_rand()%3)/3.0); // Make small! + } + + // Create an explosion that follows every now and then + if ((ps_rand()%5)==0) + { + + vector dest; + poly_model *pm=Death[slot].dying_model; + bsp_info *sm=&pm->submodel[0]; + int vertnum=ps_rand()%sm->nverts; + + GetPolyModelPointInWorld (&dest,pm, &playerobj->pos, &playerobj->orient, 0, &sm->verts[vertnum] ); + int visnum=CreateFireball (&dest,GetRandomSmallExplosion(),playerobj->roomnum,VISUAL_FIREBALL); + + if(visnum >= 0) { //DAJ added to pervent -1 array index + VisEffects[visnum].size+=((ps_rand()%20)/20.0)*3.0; + + if ((ps_rand()%2)) + { + VisEffects[visnum].movement_type=MT_PHYSICS; + VisEffects[visnum].velocity=playerobj->mtype.phys_info.velocity; + VisEffects[visnum].mass=playerobj->mtype.phys_info.mass; + VisEffects[visnum].drag=playerobj->mtype.phys_info.drag; + } + } + } + } + + //mprintf ((0,"Playerobj size=%f\n",playerobj->size)); + + +} + +#define TARGET_DEGREE (170/2) +#define FOV_CHANGE_TIME .7f + +// Alters the field of view over a period of time +void DoFOVEvent (int eventnum,void *data) +{ + float *val=(float *)data; + float new_time=*val; + float norm=new_time/FOV_CHANGE_TIME; + + new_time-=Frametime; + + if (new_time<0) + { + Render_zoom=D3_DEFAULT_ZOOM; + } + else + { + + float num=(Render_FOV/2)+(norm*(TARGET_DEGREE-(Render_FOV/2))); + num=(3.14*(float)num/180.0); + Render_zoom=tan(num); + CreateNewEvent (OBJECT_EVENT,FOV_CHANGE_EVENT,0,&new_time,sizeof(float),DoFOVEvent); + } + +} + + +// forces an end to the player death sequence +void EndPlayerDeath(int slot) +{ + ASSERT (slot>=0 && slotid].flags & PLAYER_FLAGS_DEAD)) + PlayerShipExplode(playerobj, 50.0f); + + // Stop sounds for this player + PlayerStopSounds (slot); + + //Get rid of explosions, weapons etc. that are still alive + if (!(Game_mode & GM_MULTI)) + ClearTransientObjects(0); + + //Reset the player object + InitPlayerNewShip(slot,INVRESET_DEATHSPEW); + ResetPlayerObject (slot); + + //Do the zoom-in effect and set him for invulernable in single player + if (slot==Player_num) + { + float val=FOV_CHANGE_TIME; + float *valp=&val; + DoFOVEvent (-1,(void *)valp); + + if (!(Game_mode & GM_MULTI)) + { + MakePlayerInvulnerable(slot,2.0); + } + } + + //call osiris level event + tOSIRISEventInfo evt; + evt.evt_player_respawn.it_handle = Objects[Players[slot].objnum].handle; + Osiris_CallLevelEvent(EVT_PLAYER_RESPAWN, &evt); +} + +void MovePlayerToWaypoint(object *objp); + +void ResetPlayerObject(int slot, bool f_reset_pos) +{ +// Init physics + object *playobj=&Objects[Players[slot].objnum]; + ship *ship=&Ships[Players[slot].ship_index]; + + playobj->shields = INITIAL_SHIELDS; + Players[slot].energy = INITIAL_ENERGY; + playobj->mtype.phys_info=Ships[Players[slot].ship_index].phys_info; + Objects[Players[slot].objnum].effect_info->type_flags = EF_VOLUME_LIT; + + if (playobj->effect_info->sound_handle != SOUND_NONE_INDEX) { + Sound_system.StopSoundLooping(playobj->effect_info->sound_handle); + playobj->effect_info->sound_handle = SOUND_NONE_INDEX; + } + + if(f_reset_pos) + { + if ((Game_mode & GM_MULTI) && !(Netgame.flags & NF_RESPAWN_WAYPOINT)) + { + ObjSetPos(playobj,&Players[slot].start_pos,Players[slot].start_roomnum,&Players[slot].start_orient, false); + } + else + { + MovePlayerToWaypoint(playobj); + } + } + + playobj->render_type=RT_POLYOBJ; + playobj->rtype.pobj_info.model_num = ship->model_handle; + playobj->rtype.pobj_info.dying_model_num = ship->dying_model_handle; + playobj->rtype.pobj_info.tmap_override = -1; + playobj->rtype.pobj_info.subobj_flags = 0xFFFFFFFF; + playobj->lighting_render_type=LRT_GOURAUD; + playobj->lm_object.used=0; + playobj->flags = (OF_POLYGON_OBJECT|OF_DESTROYABLE); + + if (Demo_flags==DF_PLAYBACK) + { + playobj->flags|=OF_SERVER_OBJECT; + } + + if (Game_mode & GM_MULTI) + { + if (Netgame.local_role==LR_CLIENT) + { + playobj->flags|=OF_SERVER_OBJECT; + } + else + playobj->flags|=OF_CLIENT_KNOWS; + } + + + playobj->size = Poly_models[playobj->rtype.pobj_info.model_num].rad; + + vm_MakeZero(&playobj->mtype.phys_info.velocity); + vm_MakeZero(&playobj->mtype.phys_info.thrust); + vm_MakeZero(&playobj->mtype.phys_info.rotvel); + vm_MakeZero(&playobj->mtype.phys_info.rotthrust); + + playobj->mtype.phys_info.turnroll = 0; + + if (slot==Player_num) + { + SetObjectControlType(playobj, CT_FLYING); + playobj->movement_type = MT_PHYSICS; + } + else + { + SetObjectControlType(playobj, CT_NONE); + playobj->movement_type = MT_PHYSICS; + playobj->mtype.phys_info.flags=PF_FIXED_VELOCITY; + + } + + if (Game_mode & GM_MULTI) + { + MultiMakePlayerReal (slot); + if (Netgame.flags & NF_PERMISSABLE) + playobj->mtype.phys_info.flags&=~PF_WIGGLE; + } + + if (slot==Player_num) + { + Viewer_object = Player_object = playobj; + } + + // Reset quad firing mask + object *objp = &Objects[Players[slot].objnum]; + for(int weapon_battery=0;weapon_batterydynamic_wb[weapon_battery].flags &= ~DWBF_QUAD; + } + + //Give the player a GuideBot if he doesn't have one and if the GB isn't out there + if( (!(Game_mode&GM_MULTI)) && (Demo_flags != DF_PLAYBACK) ) + { + //The first if is to prevent a tough demo playback bug where + //the game would crash *after* the demo is done playing back. + if(ObjGet(Buddy_handle[slot])) + if((!Players[slot].inventory.CheckItem(OBJ_ROBOT, ROBOT_GUIDEBOT)) && (ObjGet(Buddy_handle[slot])->type != OBJ_ROBOT)) + Players[slot].inventory.Add(OBJ_ROBOT, ROBOT_GUIDEBOT); + } + + // reinit the reticle + ResetReticle(); +} + +// Resets a player's control type back to it's default setting +void ResetPlayerControlType (int slot) +{ + ASSERT( slot>=0 && slotmtype.phys_info=Ships[Players[slot].ship_index].phys_info; + + vm_MakeZero(&playobj->mtype.phys_info.velocity); + vm_MakeZero(&playobj->mtype.phys_info.thrust); + vm_MakeZero(&playobj->mtype.phys_info.rotvel); + vm_MakeZero(&playobj->mtype.phys_info.rotthrust); + + playobj->mtype.phys_info.turnroll = 0; + + if (slot==Player_num) + { + SetObjectControlType(playobj, CT_FLYING); + playobj->movement_type = MT_PHYSICS; + } + else + { + SetObjectControlType(playobj, CT_NONE); + playobj->movement_type = MT_PHYSICS; + playobj->mtype.phys_info.flags=PF_FIXED_VELOCITY; + + } +} + +// Makes the player into an AI controlled physics object +void PlayerSetControlToAI(int slot,float velocity) +{ + ASSERT( slot>=0 && slot=MAX_PLAYERS) + return; + + object *pobj = &Objects[Players[slot].objnum]; + + //Turn off some player physics stuff + pobj->mtype.phys_info.flags &= ~PF_USES_THRUST; + pobj->mtype.phys_info.drag = .1f; + pobj->mtype.phys_info.flags &= ~PF_LEVELING; + + //pobj->flags &= ~OF_DESTROYABLE; + vm_MakeZero(&pobj->mtype.phys_info.thrust); + vm_MakeZero(&pobj->mtype.phys_info.rotthrust); + vm_MakeZero(&pobj->mtype.phys_info.rotvel); + vm_MakeZero(&pobj->mtype.phys_info.velocity); + + //Put the player in AI mode + SetObjectControlType(pobj, CT_AI); + + memset(pobj->ai_info, 0, sizeof(ai_frame)); + + pobj->ai_info->ai_class = AIC_STATIC; + pobj->ai_info->ai_type = AIT_AIS; + + GoalInitTypeGoals(pobj, AIT_AIS); + + pobj->ai_info->flags = AIF_PERSISTANT | AIF_DISABLE_FIRING | AIF_DISABLE_MELEE | AIF_ORIENT_TO_VEL; + pobj->ai_info->max_velocity = velocity; + pobj->ai_info->max_delta_velocity = 40.0f; + pobj->ai_info->max_turn_rate = 14000; + pobj->ai_info->awareness = AWARE_MOSTLY; + pobj->ai_info->movement_type = MC_FLYING; + pobj->ai_info->next_movement = AI_INVALID_INDEX; + pobj->ai_info->anim_sound_handle = 0; + pobj->ai_info->status_reg = 0; + pobj->ai_info->last_played_sound_index = -1; + pobj->ai_info->weapon_speed = 0.0f; + pobj->ai_info->animation_type = AS_ALERT; + pobj->ai_info->next_melee_time = Gametime; + pobj->ai_info->next_flinch_time = Gametime; + pobj->ai_info->target_handle = OBJECT_HANDLE_NONE; + pobj->ai_info->next_check_see_target_time = Gametime; + pobj->ai_info->last_see_target_time = Gametime; + pobj->ai_info->last_render_time = -1.0f; + pobj->ai_info->next_target_update_time = Gametime; + pobj->ai_info->notify_flags |= AI_NOTIFIES_ALWAYS_ON; + pobj->ai_info->last_see_target_pos = Zero_vector; + pobj->ai_info->dodge_vel_percent = 1.0f; + pobj->ai_info->attack_vel_percent = 1.0f; + pobj->ai_info->fight_same = 0.0f; + pobj->ai_info->agression = 0.0f; + pobj->ai_info->avoid_friends_distance = 0.0f; + pobj->ai_info->biased_flight_importance = 0.0f; + pobj->ai_info->circle_distance = 10.0f; + pobj->ai_info->dodge_percent = 0.0f; + pobj->ai_info->fight_team = 0.0f; + + AIPathInitPath(&pobj->ai_info->path); +} + + +// Moves a player to a specified start position +void PlayerMoveToStartPos (int player_slot,int start_slot) +{ + object *playobj=&Objects[Players[player_slot].objnum]; + + if (start_slot>=0) // Non autowaypoint + { + Players[player_slot].current_auto_waypoint_room=-1; + ObjSetPos(playobj,&Players[start_slot].start_pos,Players[start_slot].start_roomnum,&Players[start_slot].start_orient, true); + } + else + { + // Do autowaypoint stuff + int waypointnum=(-start_slot)+1; + Players[player_slot].current_auto_waypoint_room=waypointnum; + MovePlayerToWaypoint(playobj); + } +} + +// Force player to explode (this should be called to intercept the death sequencer's control of explosions +void StartPlayerExplosion(int slot) +{ + Death[slot].max_time_dying = 0.0f; + + if (Death[slot].in_cockpit) { + Death[slot].in_cockpit = false; + SetHUDMode(HUD_LETTERBOX); + Viewer_object = Death[slot].camera; // set the current viewer to be this new camera. + } +} + + +// Detaches a subobject from the player ship +void DeadPlayerShipHit(object *obj, int hit_room, vector *hitpt, float magnitude) +{ + fvi_info hit_data; + fvi_query fq; + vector hitvec, hitpt2; + int subobjnum = -1; + + // vector from hitpoint to object center position + hitvec = obj->pos - (*hitpt); + hitpt2 = *hitpt; + +// get first subobject to be hit when looking for a hit subobject. + hit_data.hit_type[0] = HIT_SPHERE_2_POLY_OBJECT; + + while (hit_data.hit_type[0] != HIT_NONE) + { + fq.p0 = &hitpt2; + fq.p1 = &obj->pos; + fq.startroom = hit_room; + fq.rad = obj->size; + fq.thisobjnum = obj-Objects; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_OBJ_BACKFACE | FQ_ONLY_PLAYER_OBJ; + + fvi_FindIntersection(&fq, &hit_data); + + if (hit_data.hit_type[0] == HIT_SPHERE_2_POLY_OBJECT) { + // we probably hit a subobject of the player object. + // NOTE: we should check if the hit object is still the player object! + subobjnum = hit_data.hit_subobject[0]; + } + else if (hit_data.hit_type[0] != HIT_NONE) + { + hitpt2 = hit_data.hit_pnt; + hit_data.hit_type[0] = HIT_NONE; + } + } + + if (subobjnum > -1) { + // we hit a subobject. new debris object, and delete this subobject from the player model. note that + // we should must the suobject handle to the polymodel when we're done. + PlayerShipSpewPart(obj, subobjnum, magnitude); + } +} + +//Spews a guidebot +void PlayerSpewGuidebot(object *parent,int type,int id) +{ + int objnum; + object *obj; + int model_num=Object_info[ROBOT_GUIDEBOT].render_handle; + float size; + + if(stricmp(Ships[Players[parent->id].ship_index].name, "Black Pyro") == 0) + { + //Only spew red GB if merc is installed + extern bool MercInstalled(); + if (MercInstalled()) + model_num=Object_info[ROBOT_GUIDEBOTRED].render_handle; + } + + PageInPolymodel (model_num, OBJ_ROBOT, &size); + + objnum = ObjCreate(OBJ_DEBRIS, 0,parent->roomnum,&parent->pos, &parent->orient); + + if(objnum < 0 || objnum > Highest_object_index) + { + mprintf((0, "WARNING: No object slots. Dead GB not created!\n")); + return; + } + + obj = &Objects[objnum]; + + //Set polygon-object-specific data + obj->rtype.pobj_info.model_num = model_num; + obj->rtype.pobj_info.subobj_flags = 0xFFFFFFFF; + + //Set physics data for this object + + obj->mtype.phys_info.velocity = parent->mtype.phys_info.velocity + parent->orient.fvec * 30.0; + + vm_MakeZero(&obj->mtype.phys_info.rotthrust); + + obj->size = Object_info[ROBOT_GUIDEBOT].size; + obj->mtype.phys_info.mass = 40.0; + obj->mtype.phys_info.drag=.001f; + + obj->mtype.phys_info.flags = (PF_GRAVITY | PF_BOUNCE | PF_FIXED_ROT_VELOCITY); + obj->mtype.phys_info.coeff_restitution=.25f; + + obj->mtype.phys_info.rotvel.x = (float)((120000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + obj->mtype.phys_info.rotvel.y = (float)((120000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + obj->mtype.phys_info.rotvel.z = (float)((120000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + + obj->mtype.phys_info.num_bounces = 5; + + obj->movement_type = MT_PHYSICS; + + ASSERT (obj!=Player_object); + obj->type = OBJ_DEBRIS; + SetObjectControlType(obj, CT_DEBRIS); //become debris while exploding + obj->lifeleft = 5.0+((ps_rand()%50)*.05); + obj->flags |= OF_USES_LIFELEFT; + DemoWriteObjLifeLeft(obj); +} + +// Spews an object in a random direction +void PlayerSpewObject (object *objp,int timed,int room,vector *pos,matrix *orient) +{ + ASSERT( (!(Game_mode&GM_MULTI)) || (Netgame.local_role==LR_SERVER) ); + + ObjSetPos(objp,pos,room,orient,true); + + //Set random velocity for powerups + objp->mtype.phys_info.velocity.x = ((ps_rand() / (float)RAND_MAX) - .5f) * 40.0; // +20 to -20 + objp->mtype.phys_info.velocity.z = ((ps_rand() / (float)RAND_MAX) - .5f) * 40.0; // +20 to -20 + objp->mtype.phys_info.velocity.y = ((ps_rand() / (float)RAND_MAX) - .5f) * 40.0; // +20 to -20 + + + + //Send object to other players + if (Game_mode & GM_MULTI) + { + if (Netgame.flags & NF_COOP) + timed=0; + + if (Netgame.local_role==LR_SERVER) + { + if (timed==2) + { + objp->flags |=OF_USES_LIFELEFT; + objp->lifeleft=20.0; + } + else if (timed==1) + { + objp->flags |=OF_USES_LIFELEFT; + objp->lifeleft=180.0; + } + DemoWriteObjLifeLeft(objp); + } + } + +} + +// Spews an object in a random direction +//Returns the new object +int PlayerSpewObject (object *parent,int type,int id,int timed,void *sinfo) +{ + if (id<0) // Don't spew non-existant stuff + return -1; + + ASSERT((parent->type == OBJ_PLAYER)||(parent->type == OBJ_GHOST)); + + if(type == OBJ_POWERUP) + { + if (Game_mode & GM_MULTI) + { + if(Object_info[id].multi_allowed == 0) + return -1; + } + } + // A dead GB is spew'ed on player death (if he had one) + if(type == OBJ_ROBOT && id == ROBOT_GUIDEBOT) + { + PlayerSpewGuidebot(parent,type,id); + return -1; + } + + int objnum=ObjCreate (type,id,parent->roomnum,&parent->pos,NULL,parent->handle); + + if (objnum<0) + { + mprintf ((0,"Couldn't spew object!\n")); + return -1; + } + + object *obj = &Objects[objnum]; + + PlayerSpewObject (obj,timed,parent->roomnum,&parent->pos,NULL); + + //Send object to other players + if (Game_mode & GM_MULTI) + { + if (Netgame.local_role==LR_SERVER) + { + MultiSendObject (obj,0); + } + } + + // Setup the script used for this object + InitObjectScripts(obj); + + return objnum; +} + +//TEMP: this should be in a header file +#define FIRST_SECONDARY_WEAPON MAX_PRIMARY_WEAPONS + +//How many at most to spew of each secondary weapon +#define MAX_SECONDARY_SPEW 3 + +//This is a terrible hack -- it maps powerup to multi-pack versions +//This mapping should really be on the powerup page +char *powerup_multipacks[] = { "Concussion", "4packConc", + "Frag", "4packFrag", + "Guided", "4packGuided", + "Homing", "4packHoming"}; +#define N_POWERUP_MULTIPACKS (sizeof(powerup_multipacks) / sizeof(*powerup_multipacks) / 2) + +//Returns the index of the multipack version of the specified powerup, or -1 if none +int FindMultipackPowerup(int single_index) +{ + for (int i=0;itype==OBJ_PLAYER || obj->type==OBJ_GHOST || obj->type==OBJ_OBSERVER); + int slot=obj->id; + int id,type; + player *playp=&Players[slot]; + + if(slot==Player_num || (Game_mode&GM_MULTI && Netgame.local_role==LR_SERVER)) + { + //turn off quad firing flag (becuase they will be spewing quad fire here + for(int weapon_battery=0;weapon_batterydynamic_wb[weapon_battery].flags &= ~DWBF_QUAD; + } + } + + if (obj->type==OBJ_OBSERVER) + return; // don't do anything if observer + + if (Game_mode & GM_MULTI) + { + if (Netgame.local_role!=LR_SERVER) + { + playp->inventory.Reset(true,(spew_nonspewable)?INVRESET_ALL:INVRESET_DEATHSPEW); + playp->counter_measures.Reset(true,(spew_nonspewable)?INVRESET_ALL:INVRESET_DEATHSPEW); + return; + } + } + + // spew a shield and energy + if (spew_energy_and_shield) + { + id=FindObjectIDName ("Shield"); + if (id>=0) + PlayerSpewObject (obj,OBJ_POWERUP,id,1,NULL); + + id=FindObjectIDName ("Energy"); + if (id>=0) + PlayerSpewObject (obj,OBJ_POWERUP,id,1,NULL); + } + + //Spew the primary weapon powerups + if (Game_mode & GM_MULTI) + { + if (playp->weapon[PW_PRIMARY].index>0) + { + id = Ships[playp->ship_index].spew_powerup[playp->weapon[PW_PRIMARY].index]; + if (id != -1) + { + ASSERT(Object_info[id].type == OBJ_POWERUP); + PlayerSpewObject(obj,OBJ_POWERUP,id,2,NULL); + } + } + + //Spew the secondary weapon powerups + id = Ships[playp->ship_index].spew_powerup[playp->weapon[PW_SECONDARY].index]; + if (id != -1) + { + ASSERT(Object_info[id].type == OBJ_POWERUP); + int count = __min(playp->weapon_ammo[playp->weapon[PW_SECONDARY].index],MAX_SECONDARY_SPEW); + for (int t=0;tweapon_flags & HAS_FLAG(w)) + { + id = Ships[playp->ship_index].spew_powerup[w]; + if (id != -1) + { + ASSERT(Object_info[id].type == OBJ_POWERUP); + int objnum = PlayerSpewObject(obj,OBJ_POWERUP,id,0,NULL); + + //Set ammo + if (objnum != -1) { + object *objp = &Objects[objnum]; + ASSERT(objp->control_type == CT_POWERUP); + if(Game_mode&GM_MULTI) + objp->ctype.powerup_info.count = max(objp->ctype.powerup_info.count/4,playp->weapon_ammo[w]); + else + objp->ctype.powerup_info.count = playp->weapon_ammo[w]; + } + } + } + } + + //Spew the secondary weapon powerups + for (w=FIRST_SECONDARY_WEAPON;wweapon_flags & HAS_FLAG(w)) && playp->weapon_ammo[w]) + { + id = Ships[playp->ship_index].spew_powerup[w]; + if (id != -1) + { + ASSERT(Object_info[id].type == OBJ_POWERUP); + + //Check for multipack version + int multi_id = FindMultipackPowerup(id); + if (multi_id != -1) { + int objnum; + ASSERT(Object_info[multi_id].type == OBJ_POWERUP); + objnum = PlayerSpewObject(obj,OBJ_POWERUP,multi_id,0,NULL); + if (objnum != -1) + Objects[objnum].ctype.powerup_info.count = playp->weapon_ammo[w]; + } + else { + int count = __min(playp->weapon_ammo[w],MAX_SECONDARY_SPEW); + for (int t=0;tinventory.ResetPos(); + int count=playp->inventory.Size(); + int i; + for (i=0;iinventory.GetPosCount(); + for (int t=0;tinventory.GetPosTypeID (type,id); + playp->inventory.GetPosInfo(inven_flags,object_flags); + + + if(spew_nonspewable) + spew_it = true; + else + { + if(!(inven_flags&INVF_NOTSPEWABLE)) + { + spew_it = true; + }else + { + spew_it = false; + } + } + + + if(spew_it) + { + if(id==-1) + { + //this is an already exisiting object, no creating + playp->inventory.Remove(type,id); + + //type is really the object handle + temp_obj = ObjGet(type); + ASSERT(temp_obj); + if(temp_obj) + { + ObjUnGhostObject(OBJNUM(temp_obj)); + if(Game_mode&GM_MULTI){ + //tell the clients to unghost + MultiSendGhostObject(temp_obj,false); + } + + if(Game_mode&GM_MULTI) + PlayerSpewObject (temp_obj,(inven_flags&INVF_TIMEOUTONSPEW)?2:0,obj->roomnum,&obj->pos,NULL); + else + PlayerSpewObject (temp_obj,0,obj->roomnum,&obj->pos,NULL); + } + + }else + { + ASSERT(type != OBJ_NONE); + if(Game_mode&GM_MULTI) + PlayerSpewObject (obj,type,id,(inven_flags&INVF_TIMEOUTONSPEW)?2:0,NULL); + else + PlayerSpewObject (obj,type,id,0,NULL); + playp->inventory.Remove(type,id); + } + } + + } + playp->inventory.NextPos(true); + } + + //Spew out the countermeasure items + playp->counter_measures.ResetPos(); + count=playp->counter_measures.Size(); + int done=0; + int total_spewed=0; + + for (i=0;icounter_measures.GetPosCount(); + limit=min (2,limit); + for (int t=0;tcounter_measures.GetAuxPosTypeID (type,id); + playp->counter_measures.GetPosInfo(inven_flags,object_flags); + ASSERT(id!=-1); + ASSERT(type != OBJ_NONE); + + if (Game_mode & GM_MULTI) + PlayerSpewObject (obj,type,id,(inven_flags&INVF_TIMEOUTONSPEW)?2:0,NULL); + else + PlayerSpewObject (obj,type,id,0,NULL); + + playp->counter_measures.GetPosTypeID (type,id); + playp->counter_measures.Remove(type,id); + } + + total_spewed++; + if (total_spewed>=3) + done=1; + } + playp->counter_measures.NextPos(true); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +// Blow up the entire ship (all remaining subobjects. +void PlayerShipExplode(object *obj, float magnitude) +{ + int visnum; + + ASSERT (obj!=NULL); + + DLLInfo.me_handle=obj->handle; + DLLInfo.it_handle=obj->handle; + CallGameDLL (EVT_GAMEPLAYEREXPLODED,&DLLInfo); + + if(Death[obj->id].dying_model && (Death[obj->id].dying_model->n_models > 1) ) + { + int i; + + // Create debris pieces from subobjects + for (i=1;iid].dying_model->n_models;i++) + { + if (obj->rtype.pobj_info.subobj_flags & (1<pos,BLAST_RING_INDEX,DAMAGE_RING_TIME,obj->size*3,obj->roomnum); + + obj->rtype.pobj_info.subobj_flags=0; + + // Player explosion sound + Sound_system.Play3dSound (SOUND_BUILDING_EXPLODE, obj); + + CreateRandomSparks (80,&obj->pos,obj->roomnum,HOT_SPARK_INDEX,(ps_rand()%3)+2); + visnum = CreateFireball(&obj->pos,BIG_EXPLOSION_INDEX,obj->roomnum,VISUAL_FIREBALL); + if (visnum>=0) + VisEffects[visnum].size *= (3.0f * (magnitude/MAX_EXPLOSION_MAG)); + + // Do badass damage + obj->impact_time=0; + obj->impact_size=obj->size*3; + obj->impact_force=obj->impact_size*2; + obj->impact_generic_damage=obj->impact_size*3; + obj->impact_player_damage=obj->impact_size*3; + DoConcussiveForce(obj, obj->handle,1.0); + + //Spew out any weapons, powerups, inventory + PlayerSpewInventory (obj); + + if (Game_mode & GM_MULTI) + MultiMakePlayerGhost (obj->id); + + obj->effect_info->type_flags=0; + + // Stop sounds for this player + PlayerStopSounds (obj->id); + + // Clear keyboard buffer + if (obj->id==Player_num) + { + ddio_KeyFlush(); + Controller->flush(); + } +} + + +// breaks off a random piece. +void PlayerShipBreakup(object *obj, float magnitude) +{ + int subobjnum,i,j, idx; + unsigned test_flag; + int n_models = Death[obj->id].dying_model?Death[obj->id].dying_model->n_models:0; + int n_remaining_models; + +// don't break off object 0. + if (n_models == 1) + return; + +// count number of valid subobjects skip 1st subobject + n_remaining_models = 0; + test_flag = obj->rtype.pobj_info.subobj_flags; + for (i = 1; i < n_models; i++) + { + if (test_flag & (1<id); + } + else { + PlayerShipSpewPart(obj, subobjnum, magnitude); + } +} + + +// spews a part of the player ship and all its child subobjects +void PlayerShipSpewPartSub(object *obj, bsp_info *submodel, float magnitude); + +void PlayerShipSpewPart(object *obj, int subobjnum, float magnitude) +{ + PlayerShipSpewPartSub(obj, &Poly_models[obj->rtype.pobj_info.model_num].submodel[subobjnum], magnitude); +} + + +void PlayerShipSpewPartSub(object *obj, bsp_info *submodel, float magnitude) +{ + vector rand_vec; + int subobjnum = submodel - Poly_models[obj->rtype.pobj_info.model_num].submodel; + int i; + int visnum; + + ASSERT(subobjnum > -1 && subobjnum < Poly_models[obj->rtype.pobj_info.model_num].n_models); + +// first go through all children. +// find child index, and use that index into the submodel array of the polymodel to pass a new submodel down +// the chain. + i = 0; + while (i < submodel->num_children) + { + PlayerShipSpewPartSub(obj, &Poly_models[obj->rtype.pobj_info.model_num].submodel[submodel->children[i]], magnitude); + i++; + } + + rand_vec.x= (float)(RAND_MAX/2 - ps_rand()); + if(obj->movement_type != MT_PHYSICS) + rand_vec.y = (float)((float)ps_rand()/2.0); // A habit of moving upward + else + rand_vec.y = (float)((float)RAND_MAX/1.5f - (float)ps_rand()); // A habit of moving upward + rand_vec.z = (float)(RAND_MAX/2 - ps_rand()); + vm_NormalizeVectorFast(&rand_vec); + + object *subobj = CreateSubobjectDebrisDirected(obj, subobjnum, &rand_vec, magnitude); + if (subobj) { + // create mini-explosion at start of debris fall. + vector newpos = subobj->pos+submodel->offset; + visnum = CreateFireball(&newpos,MED_EXPLOSION_INDEX,subobj->roomnum,VISUAL_FIREBALL); + if (visnum>=0) { + VisEffects[visnum].size *=(2.5f * (magnitude/MAX_EXPLOSION_MAG)); + VisEffects[visnum].lifeleft *= 2.0f; + rand_vec = (-rand_vec) * (magnitude*25.0f); + phys_apply_force(obj, &rand_vec); + // mprintf((0, "Breakoff fireball size: %f, lifeleft: %f\n", Objects[objnum].size, Objects[objnum].lifeleft)); + } + + } + +// mark subobject dead + obj->rtype.pobj_info.subobj_flags &= (~(1<rtype.pobj_info.subobj_flags)); +} + + +// Moves death camera a certain distance based off of direction from vec from +// the player. returns new dist +// +static vector Death_cam_vectors[] = { + {0,1,0}, {0,1,-1}, {0,0,-1}, {0,-1,-1}, {0,-1,0}, {0,-1, 1}, {0,0,1},{0,1,1}, + {1,0,0}, {1,0,-1}, {-1,0,-1}, {-1,0,0}, {-1,0,1}, {1,0,1}, + {1,1,0}, {1,-1,0}, {-1,-1,0}, {-1,1,0} +}; + +#define NUM_DEATH_VECS (sizeof(Death_cam_vectors)/sizeof(vector)) + +float MoveDeathCam(int slot, vector *vec, float distance) +{ + fvi_info hit_data; + fvi_query fq; + vector best_vec, next_vec, cam_vec; + float far_scale; + int cur_vec_index, init_vec_index, loop_count; + object *playobj=&Objects[Players[slot].objnum]; + +// once we've reached our distance goal, then we stop, otherwise keep moving. +// actually move the camera. make sure we don't leave the mine though. + hit_data.hit_type[0] = (OBJECT_OUTSIDE(playobj) ? HIT_TERRAIN : HIT_WALL); + far_scale = 1.0f; + + init_vec_index = ps_rand() % NUM_DEATH_VECS; + cur_vec_index = init_vec_index; + loop_count = 0; + + do + { + // randomize cam_vec just a little bit if possible. + cam_vec = Death_cam_vectors[cur_vec_index++]; + cam_vec.x += ((ps_rand()%5)-2)*0.1f; + cam_vec.y += ((ps_rand()%5)-2)*0.1f; + vm_NormalizeVector(&cam_vec); + + cam_vec = cam_vec * distance; + best_vec = playobj->pos + cam_vec; + cam_vec = cam_vec * far_scale; + next_vec = playobj->pos + cam_vec; + + if (cur_vec_index == init_vec_index) { + distance = distance / 1.25f; + far_scale = far_scale * 1.05f; + loop_count++; + } + + fq.p0 = &playobj->pos; + fq.p1 = &next_vec; + fq.startroom = playobj->roomnum; + fq.rad = .5; + fq.thisobjnum = OBJNUM(playobj); + fq.ignore_obj_list = NULL; + fq.flags = 0; + + ASSERT(_finite(next_vec.x) != 0); + ASSERT(_finite(next_vec.y) != 0); + ASSERT(_finite(next_vec.z) != 0); + + fvi_FindIntersection(&fq, &hit_data); + + if (hit_data.hit_type[0] == HIT_NONE || loop_count == 32 ) { + // okay, we need to find the room that best_vec is in. + fq.p0 = &playobj->pos; + fq.p1 = &best_vec; + fq.startroom = playobj->roomnum; + fq.rad = 0.75f; + fq.thisobjnum = OBJNUM(playobj); + fq.ignore_obj_list = NULL; + fq.flags = 0; + fvi_FindIntersection(&fq, &hit_data); + + if (hit_data.hit_type[0] != HIT_NONE) + ObjSetPos(Death[slot].camera,&hit_data.hit_pnt, hit_data.hit_room,NULL, false); + else + ObjSetPos(Death[slot].camera,&best_vec,hit_data.hit_room,NULL, false); + } + else { + float ray_dist = vm_VectorDistanceQuick(&hit_data.hit_pnt, &playobj->pos); + if (ray_dist < distance) { + hit_data.hit_type[0] = HIT_NONE; + } + } + + // go back to start of list if at end. + if (cur_vec_index == NUM_DEATH_VECS) + cur_vec_index = 0; + } + while (hit_data.hit_type[0] !=HIT_NONE && loop_count < 32); + + *vec = cam_vec; + + return distance; +} + + + +// Returns the goal room of the passed in team +int GetGoalRoomForTeam (int teamnum) +{ + ASSERT (teamnum>=0 && teamnum<=3); + int flags[]={RF_GOAL1,RF_GOAL2,RF_GOAL3,RF_GOAL4}; + + for (int i=0;i<=Highest_room_index;i++) + { + if (Rooms[i].used && (Rooms[i].flags & flags[teamnum])) + { + return i; + } + } + + return -1; // No goal for this team! +} + +// Returns the goal room of the passed in player +int GetGoalRoomForPlayer (int slot) +{ + ASSERT (slot>=0 && slot<=MAX_PLAYERS); + int team=PlayerGetTeam(slot); + + if (team==-1) + return -1; + + return (GetGoalRoomForTeam (team)); +} + +// Sets the maximum number of teams in a game +void SetMaxTeams (int num) +{ + if (num>MAX_TEAMS) + num=MAX_TEAMS; + + if (num<2) + num=1; + + if (num>=2) + Team_game=1; + else + Team_game=0; + + Num_teams=num; +} + +int IncTeamScore (int teamnum,int add) +{ + ASSERT (teamnum>=0 && teamnumorient * rotmat; + + // Get world position + if (num==1) + *dest=obj->pos+(tempm.fvec*(obj->size+1)); + else + *dest=obj->pos+(tempm.uvec*(obj->size+1)); +} + +// Sets a wacky rotating ball around the player ship +void PlayerSetRotatingBall (int slot,int num,float speed,float *r,float *g,float *b) +{ + ASSERT (num>=0 && num<=3); + + Players[slot].ballspeed=speed; + Players[slot].num_balls=num; + + for (int i=0;iused) + { //this ship doesn't exist + Int3(); + ship_index = GetNextShip(0); + ASSERT(ship_index != -1); + ship = &Ships[ship_index]; + } + + //Set size & shields + objp->size = ship->size; + objp->mtype.phys_info = ship->phys_info; + + //Set up render info, but save render type first + int save_rt=objp->render_type; + ObjSetRenderPolyobj(objp,ship->model_handle,ship->dying_model_handle); + objp->render_type=save_rt; + + objp->rtype.pobj_info.model_num = ship->model_handle; + objp->rtype.pobj_info.dying_model_num = ship->dying_model_handle; + PageInPolymodel (ship->model_handle); + if (ship->dying_model_handle!=-1) + PageInPolymodel (ship->dying_model_handle); + + if (ship->lo_render_handle>=0) + PageInPolymodel (ship->lo_render_handle); + if (ship->med_render_handle>=0) + PageInPolymodel (ship->med_render_handle); + + + ComputeDefaultSize(OBJ_PLAYER, objp->rtype.pobj_info.model_num, &objp->size); + + WBClearInfo(objp); + + Players[slot].ship_index=ship_index; +} + +// Sets the FOV range at which the hud names will come on +void PlayerSetHUDNameFOV (int fov) +{ + if (fov<0) + { + HudNameTan=-1; + return; + } + + + float rad=(float)(3.14*(float)(fov/2)/180.0); + HudNameTan=tan(rad); +} + +// Switches a player object to observer mode +void PlayerSwitchToObserver (int slot,int observer_mode,int piggy_objnum) +{ + object *obj=&Objects[Players[slot].objnum]; + + //call the event to the multiplayer DLL's so they can do what they need + if( Game_mode&GM_MULTI){ + DLLInfo.me_handle=obj->handle; + + if (observer_mode==OBSERVER_MODE_PIGGYBACK) { + DLLInfo.it_handle=Objects[piggy_objnum].handle; + }else { + DLLInfo.it_handle=OBJECT_HANDLE_NONE; + } + + CallGameDLL (EVT_CLIENT_GAMEPLAYERENTERSOBSERVER,&DLLInfo); + } + + // Stop sounds for this player + PlayerStopSounds (slot); + + if (slot==Player_num) + { + AddHUDMessage (TXT_ENTEROBS); + SetHUDMode(HUD_OBSERVER); + Players[slot].movement_scalar=2.0; + } + else + AddHUDMessage (TXT_PLYRENTEROBS,Players[slot].callsign); + + CreateRandomSparks (30,&obj->pos,obj->roomnum,HOT_SPARK_INDEX,(ps_rand()%3)+2); + + if(Demo_flags!=DF_PLAYBACK) + { + PlayerSpewInventory (obj,false); + } + + obj->type=OBJ_OBSERVER; + obj->render_type=RT_NONE; + + if(Demo_flags==DF_RECORDING) + { + DemoWritePlayerTypeChange(slot,false,observer_mode,piggy_objnum); + } + + if (observer_mode==OBSERVER_MODE_ROAM) + { + SetObjectControlType(obj, CT_FLYING); + Players[slot].piggy_objnum=-1; + obj->movement_type=MT_PHYSICS; + } + else if (observer_mode==OBSERVER_MODE_PIGGYBACK) + { + SetObjectControlType(obj, CT_NONE); + Players[slot].piggy_objnum=piggy_objnum; + Players[slot].piggy_sig=Objects[piggy_objnum].handle & HANDLE_COUNT_MASK; + mprintf ((0,"Object %d is observing object %d!\n",obj-Objects,piggy_objnum)); + } +} + +// Stops a player from observing +void PlayerStopObserving (int slot) +{ + //call the event to the multiplayer DLL's so they can do what they need + if( Game_mode&GM_MULTI ){ + DLLInfo.me_handle=Objects[Players[slot].objnum].handle; + DLLInfo.it_handle=OBJECT_HANDLE_NONE; + CallGameDLL (EVT_CLIENT_GAMEPLAYEREXITSOBSERVER,&DLLInfo); + } + + if (slot==Player_num) + { + ubyte hud_mode; + Current_pilot.get_hud_data(&hud_mode); + AddHUDMessage (TXT_LEAVEOBS); + SetHUDMode((tHUDMode)hud_mode); + } + else + AddHUDMessage (TXT_PLYRLEAVEOBS,Players[slot].callsign); + + // Stop sounds for this player + PlayerStopSounds (slot); + + object *obj=&Objects[Players[slot].objnum]; + + obj->type=OBJ_PLAYER; + Players[slot].piggy_objnum=-1; + + InitPlayerNewShip(slot,INVRESET_ALL); + ResetPlayerObject (slot); + + if(Demo_flags==DF_RECORDING) + { + DemoWritePlayerTypeChange(slot,true); + } +} + + +#define PLAYER_TEXTURE_FPS 8.0f +// Sets the players custom texture. If filename=NULL then sets to no texture +// Returns 1 if filename was successfully loaded, else 0 +int PlayerSetCustomTexture (int slot,char *filename) +{ + // Free up previous vclip/bitmap if there is one + texture *texp=&GameTextures[Players[slot].custom_texture_handle]; + int bm_handle=texp->bm_handle; + + if (bm_handle!=0) + { + if (texp->flags & TF_ANIMATED) + FreeVClip (bm_handle); + else + bm_FreeBitmap (bm_handle); + + texp->bm_handle=0; + } + + // Now load the new one + texp->bm_handle=0; + if (filename==NULL) + return 1; + + int anim; + int new_handle=LoadTextureImage (filename,&anim,SMALL_TEXTURE,1,0); + + if (new_handle<0) + return 0; + + texp->bm_handle=new_handle; + if (anim) + texp->flags|=TF_ANIMATED; + else + texp->flags &=~TF_ANIMATED; + + texp->flags|=TF_TEXTURE_64; + + // Set texture speed + if (texp->flags & TF_ANIMATED) + { + vclip *vc=&GameVClips[texp->bm_handle]; + texp->speed=vc->num_frames*(1.0/PLAYER_TEXTURE_FPS); + } + + return 1; +} + +// Sets/Clears a permission for a ship on a given player +// if pnum is -1 then all players will be set, else player is the player number +// returns true on success +bool PlayerSetShipPermission(int pnum,char *ship_name,bool allowed) +{ + ASSERT(ship_name); + + if( pnum<-1 || pnum>=MAX_PLAYERS ) //illegal value + return false; + + int ship_index = FindShipName (ship_name); + if(ship_index==-1){ + ASSERT(ship_index!=-1); + return false; + } + int bit = 0x01; + bit = bit<=MAX_PLAYERS ) + return false; + + int perm; + + mprintf((0,"Reseting ship permissions\n")); + + if(set_default) + perm = Default_ship_permission; + else + perm = 0; + + if(pnum==-1){ + for(int i=0;i=MAX_PLAYERS ) //illegal value + return false; + + int ship_index = FindShipName (ship_name); + if(ship_index==-1){ + ASSERT(ship_index!=-1); + return false; + } + int bit = 0x01; + bit = bit<=MAX_PLAYERS ) //illegal value + return false; + + ASSERT( ship_index>=0 && ship_index=MAX_SHIELDS){ + if(pnum==Player_num) + AddHUDMessage(TXT_SHIELDSATMAX); + return; + } + + amount = min(Frametime*CONVERTER_RATE,Players[pnum].energy-INITIAL_ENERGY); + amount = min(amount,(MAX_SHIELDS-Objects[Players[pnum].objnum].shields)*CONVERTER_SCALE); + + Players[pnum].energy -= amount; + + if ((!(Game_mode & GM_MULTI)) || Netgame.local_role==LR_SERVER) + Objects[Players[pnum].objnum].shields += amount/CONVERTER_SCALE; + else + Multi_additional_shields[SHIELD_REQUEST_ENERGY_TO_SHIELD]+=Frametime; + + if(pnum==Player_num){ + if (last_play_time > Gametime) + last_play_time = 0; + + if (Gametime > last_play_time+CONVERTER_SOUND_DELAY) + { + Sound_system.Play3dSound(SOUND_ENERGY_CONVERTER,&Objects[Players[pnum].objnum],MAX_GAME_VOLUME/2); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.3f; + hear.curiosity_level = 1.0f; + hear.max_dist = Sounds[SOUND_ENERGY_CONVERTER].max_distance * 0.5f; + AINotify(&Objects[Players[Player_num].objnum], AIN_HEAR_NOISE, (void *)&hear); + + last_play_time = Gametime; + } + } +} + +// Sets the start position that the player will respawn at +void PlayerAddWaypoint (int index) +{ + ASSERT (Players[index].start_roomnum!=-1); // Invalid start position specified + mprintf ((0,"Current waypoint is now %d\n",index)); + Current_waypoint=index; + + //A manual waypoint blows away all auto-waypoints + for (int i=0;iflags &= ~RF_WAYPOINT; + + //Look through objects for waypoints + for(i=0,objp=Objects;i<=Highest_object_index;i++,objp++) { + if (objp->type == OBJ_WAYPOINT) { + if (Num_waypoints < MAX_WAYPOINTS) { + Waypoints[Num_waypoints].pos = objp->pos; + Waypoints[Num_waypoints].roomnum = objp->roomnum; + Waypoints[Num_waypoints].orient = objp->orient; + Num_waypoints++; + + if (OBJECT_OUTSIDE(objp)) + ; //Terrain_seg[CELLNUM(objp->roomnum)].flags |= TF_WAYPOINT; + else + Rooms[objp->roomnum].flags |= RF_WAYPOINT; + } + + ObjDelete(i); + } + } +} + +//Sets the player's waypoint to the one in the specified room +void SetAutoWaypoint(object *objp) +{ + ASSERT(objp->type == OBJ_PLAYER); + + //Get the player we're setting + player *pp = &Players[objp->id]; + + if (! OBJECT_OUTSIDE(objp)) { + //delete the next two lines + if (pp->current_auto_waypoint_room != objp->roomnum) + { + mprintf((0,"Setting auto-waypoint in room %d\n",objp->roomnum)); + } + pp->current_auto_waypoint_room = objp->roomnum; + } +} + +//Moves the player to his last waypoint +void MovePlayerToWaypoint(object *objp) +{ + ASSERT(objp->type == OBJ_PLAYER || objp->type==OBJ_GHOST); + + player *pp = &Players[objp->id]; + + //If this player has an auto-waypoint, use it. Else use global waypoint. + if (pp->current_auto_waypoint_room != -1) { + waypoint *wp; + int i; + + //Find the waypoint + for (i=0,wp=Waypoints;iroomnum == pp->current_auto_waypoint_room) + break; + ASSERT(i < Num_waypoints); + + ObjSetPos(objp,&wp->pos,wp->roomnum,&wp->orient,false); + } + else { + ASSERT(Current_waypoint != -1); + + mprintf ((0,"Resetting player ship to waypoint %d\n",Current_waypoint)); + ObjSetPos(objp,&Players[Current_waypoint].start_pos,Players[Current_waypoint].start_roomnum,&Players[Current_waypoint].start_orient, false); + } +} + +//Adds to the player's score & kill total +void PlayerScoreAdd(int playernum,int points) +{ + //Add score + if (points) { + + if (points > 0) + Players[playernum].num_kills_level++; + else + Players[playernum].friendly_kills_level++; + + if(!(playernum != Player_num || IsCheater)) + { + Players[playernum].score += points; + + Score_added = points; + Score_added_timer = SCORE_ADDED_TIME; + } + } +} + +//////////////////////////////////////////////////// +// Thief interface functions +//////////////////////////////////////////////////// +extern ubyte AutomapVisMap[MAX_ROOMS]; +void MakeObjectVisible(object *obj); + +// steals an item from the given player +void ThiefStealItem(int player_object_handle,int item) +{ + object *pobj = ObjGet(player_object_handle); + ASSERT(pobj); + ASSERT(pobj->type==OBJ_PLAYER); + + int playernum = pobj->id; + + static int conv_id = -2; + static int quad_id = -2; + + bool is_multi = false; + if(Game_mode&GM_MULTI) + is_multi = true; + + if(is_multi) + { + ASSERT(item!=THIEFITEM_AUTOMAP); + } + + if(is_multi && Netgame.local_role==LR_SERVER) + { + //send it off + MultiSendThiefSteal(playernum,item); + } + + switch(item) + { + case THIEFITEM_AUTOMAP: + if(playernum==Player_num) + { + for (int i=0;ieffect_info) && (pobj->effect_info->type_flags&EF_FADING_OUT)||(pobj->effect_info->type_flags&EF_CLOAKED)){ + MakeObjectVisible(pobj); + } + }break; + + case THIEFITEM_INVULN: + { + if(Players[playernum].flags&PLAYER_FLAGS_INVULNERABLE){ + MakePlayerVulnerable(playernum); + } + }break; + + case THIEFITEM_QUADFIRE: + { + pobj->dynamic_wb[LASER_INDEX].flags &= ~DWBF_QUAD; + pobj->dynamic_wb[SUPER_LASER_INDEX].flags &= ~DWBF_QUAD; + + //remove from inventory + if(quad_id==-2) + quad_id = FindObjectIDName("QuadLaser"); + + if(quad_id>-1) + { + Players[playernum].inventory.Remove(OBJ_POWERUP,quad_id); + } + + }break; + + case THIEFITEM_RAPIDFIRE: + { + if(Players[playernum].weapon_recharge_scalar<1.0f) + { + Players[playernum].weapon_recharge_scalar = 1.0f; + } + }break; + + } +} + +// returns a stolen item to a player +void ThiefReturnItem(int player_object_handle,int item) +{ + object *pobj = ObjGet(player_object_handle); + ASSERT(pobj); + ASSERT(pobj->type==OBJ_PLAYER); + + int playernum = pobj->id; + + switch(item) + { + case THIEFITEM_AUTOMAP: + if(playernum==Player_num) + { + for (int i=0;itype==OBJ_PLAYER); + + int playernum = pobj->id; + + static int conv_id = -2; + + bool is_multi = false; + if(Game_mode&GM_MULTI) + is_multi = true; + + if( (playernum<0 || playernum>=MAX_PLAYERS) ) + return false; + if(is_multi && !(NetPlayers[playernum].flags&NPF_CONNECTED)) + return false; + + switch(item) + { + case THIEFITEM_AUTOMAP: + return !is_multi; + break; + case THIEFITEM_HEADLIGHT: + if(Players[playernum].flags&PLAYER_FLAGS_HEADLIGHT_STOLEN) + return false; + return true; + break; + case THIEFITEM_ETOSCONV: + { + if(conv_id==-2) + conv_id = FindObjectIDName("Converter"); + + if(conv_id!=-1) + { + int inv_count = Players[playernum].inventory.GetTypeIDCount(OBJ_POWERUP,conv_id); + if(inv_count>0) + return true; + } + return false; + }break; + case THIEFITEM_CLOAK: + { + if(pobj->effect_info){ + if(! ((pobj->effect_info->type_flags&EF_FADING_OUT)||(pobj->effect_info->type_flags&EF_CLOAKED))){ + return false; + }else{ + return true; + } + } + return false; + }break; + case THIEFITEM_INVULN: + { + if(Players[playernum].flags&PLAYER_FLAGS_INVULNERABLE){ + return true; + } + return false; + }break; + case THIEFITEM_QUADFIRE: + { + if(pobj->dynamic_wb[LASER_INDEX].flags&DWBF_QUAD) + return true; + else + return false; + }break; + case THIEFITEM_RAPIDFIRE: + { + if(Players[playernum].weapon_recharge_scalar<1.0f) + { + return true; + } + return false; + }break; + } + + return false; +} + diff --git a/Descent3/SDLMain.h b/Descent3/SDLMain.h new file mode 100644 index 000000000..c56d90cbe --- /dev/null +++ b/Descent3/SDLMain.h @@ -0,0 +1,16 @@ +/* SDLMain.m - main entry point for our Cocoa-ized SDL app + Initial Version: Darrell Walisser + Non-NIB-Code & other changes: Max Horn + + Feel free to customize this file to suit your needs +*/ + +#ifndef _SDLMain_h_ +#define _SDLMain_h_ + +#import + +@interface SDLMain : NSObject +@end + +#endif /* _SDLMain_h_ */ diff --git a/Descent3/SDLMain.m b/Descent3/SDLMain.m new file mode 100644 index 000000000..2434f81aa --- /dev/null +++ b/Descent3/SDLMain.m @@ -0,0 +1,381 @@ +/* SDLMain.m - main entry point for our Cocoa-ized SDL app + Initial Version: Darrell Walisser + Non-NIB-Code & other changes: Max Horn + + Feel free to customize this file to suit your needs +*/ + +#include "SDL.h" +#include "SDLMain.h" +#include /* for MAXPATHLEN */ +#include + +/* For some reaon, Apple removed setAppleMenu from the headers in 10.4, + but the method still is there and works. To avoid warnings, we declare + it ourselves here. */ +@interface NSApplication(SDL_Missing_Methods) +- (void)setAppleMenu:(NSMenu *)menu; +@end + +/* Use this flag to determine whether we use SDLMain.nib or not */ +#define SDL_USE_NIB_FILE 0 + +/* Use this flag to determine whether we use CPS (docking) or not */ +#define SDL_USE_CPS 1 +#ifdef SDL_USE_CPS +/* Portions of CPS.h */ +typedef struct CPSProcessSerNum +{ + UInt32 lo; + UInt32 hi; +} CPSProcessSerNum; + +extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); +extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); +extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); + +#endif /* SDL_USE_CPS */ + +static int gArgc; +static char **gArgv; +static BOOL gFinderLaunch; +static BOOL gCalledAppMainline = FALSE; + +static NSString *getApplicationName(void) +{ + const NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} + +#if SDL_USE_NIB_FILE +/* A helper category for NSString */ +@interface NSString (ReplaceSubString) +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; +@end +#endif + +@interface NSApplication (SDLApplication) +@end + +@implementation NSApplication (SDLApplication) +/* Invoked from the Quit menu item */ +- (void)terminate:(id)sender +{ + /* Post a SDL_QUIT event */ + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); +} +@end + +/* The main class of the application, the application's delegate */ +@implementation SDLMain + +/* Set the working directory to the .app's parent directory */ +- (void) setupWorkingDirectory:(BOOL)shouldChdir +{ + if (shouldChdir) + { + char parentdir[MAXPATHLEN]; + CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); + if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) { + chdir(parentdir); /* chdir to the binary app's parent */ + } + CFRelease(url); + CFRelease(url2); + } +} + +#if SDL_USE_NIB_FILE + +/* Fix menu to contain the real app name instead of "SDL App" */ +- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName +{ + NSRange aRange; + NSEnumerator *enumerator; + NSMenuItem *menuItem; + + aRange = [[aMenu title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; + + enumerator = [[aMenu itemArray] objectEnumerator]; + while ((menuItem = [enumerator nextObject])) + { + aRange = [[menuItem title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; + if ([menuItem hasSubmenu]) + [self fixMenu:[menuItem submenu] withAppName:appName]; + } +} + +#else + +static void setApplicationMenu(void) +{ + /* warning: this code is very odd */ + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + + /* Finally give up our references to the objects */ + [appleMenu release]; + [menuItem release]; +} + +/* Create a window menu */ +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + + /* Finally give up our references to the objects */ + [windowMenu release]; + [windowMenuItem release]; +} + +/* Replacement for NSApplicationMain */ +static void CustomApplicationMain (int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDLMain *sdlMain; + + /* Ensure the application object is initialised */ + [NSApplication sharedApplication]; + +#ifdef SDL_USE_CPS + { + CPSProcessSerNum PSN; + /* Tell the dock about us */ + if (!CPSGetCurrentProcess(&PSN)) + if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) + if (!CPSSetFrontProcess(&PSN)) + [NSApplication sharedApplication]; + } +#endif /* SDL_USE_CPS */ + + /* Set up the menubar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setApplicationMenu(); + setupWindowMenu(); + + /* Create SDLMain and make it the app delegate */ + sdlMain = [[SDLMain alloc] init]; + [NSApp setDelegate:sdlMain]; + + /* Start the main event loop */ + [NSApp run]; + + [sdlMain release]; + [pool release]; +} + +#endif + + +/* + * Catch document open requests...this lets us notice files when the app + * was launched by double-clicking a document, or when a document was + * dragged/dropped on the app's icon. You need to have a + * CFBundleDocumentsType section in your Info.plist to get this message, + * apparently. + * + * Files are added to gArgv, so to the app, they'll look like command line + * arguments. Previously, apps launched from the finder had nothing but + * an argv[0]. + * + * This message may be received multiple times to open several docs on launch. + * + * This message is ignored once the app's mainline has been called. + */ +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + const char *temparg; + size_t arglen; + char *arg; + char **newargv; + + if (!gFinderLaunch) /* MacOS is passing command line args. */ + return FALSE; + + if (gCalledAppMainline) /* app has started, ignore this document. */ + return FALSE; + + temparg = [filename UTF8String]; + arglen = SDL_strlen(temparg) + 1; + arg = (char *) SDL_malloc(arglen); + if (arg == NULL) + return FALSE; + + newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); + if (newargv == NULL) + { + SDL_free(arg); + return FALSE; + } + gArgv = newargv; + + SDL_strlcpy(arg, temparg, arglen); + gArgv[gArgc++] = arg; + gArgv[gArgc] = NULL; + return TRUE; +} + + +/* Called when the internal event loop has just started running */ +- (void) applicationDidFinishLaunching: (NSNotification *) note +{ + int status; + + /* Set the working directory to the .app's parent directory */ + [self setupWorkingDirectory:gFinderLaunch]; + +#if SDL_USE_NIB_FILE + /* Set the main menu to contain the real app name instead of "SDL App" */ + [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; +#endif + + /* Hand off to main application code */ + gCalledAppMainline = TRUE; + status = SDL_main (gArgc, gArgv); + + /* We're done, thank you for playing */ + exit(status); +} +@end + + +@implementation NSString (ReplaceSubString) + +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString +{ + unsigned int bufferSize; + unsigned int selfLen = [self length]; + unsigned int aStringLen = [aString length]; + unichar *buffer; + NSRange localRange; + NSString *result; + + bufferSize = selfLen + aStringLen - aRange.length; + buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); + + /* Get first part into buffer */ + localRange.location = 0; + localRange.length = aRange.location; + [self getCharacters:buffer range:localRange]; + + /* Get middle part into buffer */ + localRange.location = 0; + localRange.length = aStringLen; + [aString getCharacters:(buffer+aRange.location) range:localRange]; + + /* Get last part into buffer */ + localRange.location = aRange.location + aRange.length; + localRange.length = selfLen - localRange.location; + [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; + + /* Build output string */ + result = [NSString stringWithCharacters:buffer length:bufferSize]; + + NSDeallocateMemoryPages(buffer, bufferSize); + + return result; +} + +@end + + + +#ifdef main +# undef main +#endif + + +/* Main entry point to executable - should *not* be SDL_main! */ +int main (int argc, char **argv) +{ + /* Copy the arguments into a global variable */ + /* This is passed if we are launched by double-clicking */ + if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { + gArgv = (char **) SDL_malloc(sizeof (char *) * 2); + gArgv[0] = argv[0]; + gArgv[1] = NULL; + gArgc = 1; + gFinderLaunch = YES; + } else { + int i; + gArgc = argc; + gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); + for (i = 0; i <= argc; i++) + gArgv[i] = argv[i]; + gFinderLaunch = NO; + } + +#if SDL_USE_NIB_FILE + NSApplicationMain (argc, argv); +#else + CustomApplicationMain (argc, argv); +#endif + return 0; +} + diff --git a/Descent3/SLEW.cpp b/Descent3/SLEW.cpp new file mode 100644 index 000000000..7ee70cbcb --- /dev/null +++ b/Descent3/SLEW.cpp @@ -0,0 +1,497 @@ +/* + * $Logfile: /DescentIII/main/SLEW.cpp $ + * $Revision: 46 $ + * $Date: 4/18/99 5:42a $ + * $Author: Chris $ + * + * Basic slew system + * + * $Log: /DescentIII/main/SLEW.cpp $ + * + * 46 4/18/99 5:42a Chris + * Added the FQ_IGNORE_RENDER_THROUGH_PORTALS flag + * + * 45 4/17/99 2:41p Matt + * Added code to limit movement by axis. + * + * 44 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 43 4/10/99 5:53p Matt + * Re-added terrain bound limit, on terrain only + * + * 42 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 41 3/01/99 6:27p Matt + * Cleaned up handling of the outside-mine flag + * + * 40 2/25/99 10:44p Matt + * Return separate flags for move & rotate + * + * 39 2/24/99 9:39p Matt + * Disabled terrain bounds limitation + * + * 38 2/23/99 5:47p Matt + * Fixed terrain bound limit code + * + * 37 1/15/99 7:52p Chris + * Updated ObjSetPos() to include a f_update_attach_children flag + * + * 36 10/20/98 5:35p Sean + * Temporary slew fix + * + * 35 10/15/98 8:47a Matt + * Changed _DEBUG/RELEASE inconsistancy with slew + * + * 34 10/14/98 4:39p Matt + * Changed OutrageMessageBox() calls to use either Error() or + * EditorMessageBox() + * + * 33 9/22/98 3:54p Samir + * no include slew in release version. + * + * 32 9/11/98 3:00p Craig + * Added ifdef around some editor code + * + * 31 9/04/98 12:23p Samir + * control whether to allow joystick slewing. + * + * 30 7/01/98 4:58p Samir + * moved slew joysitck init from Init.cpp to here. + * + * 29 6/15/98 1:02p Samir + * added pageup and pagedown. + * + * 28 5/20/98 6:59p Samir + * added slew speed slider. + * + * 27 5/18/98 6:58p Samir + * took out annoying mprinrfs. + * + * 26 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 25 2/17/98 5:31p Samir + * Upped joy slew. + * + * 24 2/15/98 7:57p Matt + * Renamed NewSlewFrame() to be SlewFrame(), and added some mprintfs. + * + * 23 2/13/98 1:21p Samir + * Maybe fixed evil slewing problem. + * + * 22 2/13/98 11:47a Samir + * debug info. + * + * 21 2/12/98 4:08p Samir + * Fixed a boo-boo. + * + * 20 2/12/98 4:01p Samir + * Maybe this will fix slewing. + * + * 19 2/05/98 2:58p Matt + * Cleaned up slew code, and made view mode switch when slewing between + * mine and terrain happen in editor, not slew. + * + * 18 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 17 1/21/98 1:08p Matt + * Made SetViewMode() not take new_viewer parm, since it's not needed + * after my revamp of the editor view system. + * + * 16 1/21/98 12:25p Matt + * Removed unneeded include + * + * 15 1/07/98 10:50a Chris + * Made it safe to slew to the edge of the terrain + * + * 14 12/03/97 7:35p Samir + * New joystick library sturff. + * + * 13 11/16/97 5:53p Matt + * Added some code so that slewing in Room View doesn't call FVI to update + * the room number + * + * 12 11/12/97 12:57p Chris + * Now compile under the main project + * + * 11 10/22/97 4:34p Chris + * Removed a warning + * + * 10 10/22/97 4:30p Chris + * We can now slew between the mine and the terrain + * + * 9 9/22/97 6:18p Matt + * Removed include of stub.h + * + * 8 9/03/97 2:14p Chris + * Made a few const int's to const float's as they actually are and + * removed a literal and replaced it with a FVIF_OUTSIDE + * + * 7 8/25/97 12:41p Chris + * Made sure that all object movements and mine changes result in AABB + * updates. + * + * 6 8/22/97 4:57p Matt + * Only search for new room if slew object has moved + * + * 5 8/05/97 10:50a Matt + * Fixed slewing on terrain + * + * 4 8/04/97 7:40p Matt + * Use FVI to update room number when slewing + * + * 3 8/01/97 10:48a Samir + * Increased null-zone for joystick. Still some calibration problems. + * + * 2 7/17/97 4:34p Chris + * Made sure the orientations get orthagonalized. Yikes. No more + * degenerating orientations. :) + * + * 13 5/13/97 11:40a Samir + * No joystick stuff for noneditor + * + * 12 4/01/97 9:45p Matt + * Don't require an object to have control type of slew in order to slew + * (so we can slew the editor's viewer object) + * + * 11 2/27/97 10:35a Matt + * Got joystick slewing working + * + * 10 2/20/97 4:30p Samir + * Adjust slewing code to return whether a key was pressed as well as + * disallowing slewing when control keys are down. + * + * 9 2/18/97 6:31p Matt + * Changed Frame_time to Frametime, & added Gametime + * + * 8 2/07/97 5:48p Matt + * Renamed vector.h to vecmat.h to fix DevStudio problem + * + * $NoKeywords: $ + */ + +#ifdef _DEBUG + +#ifdef EDITOR +#include "editor\mainfrm.h" +#include "editor\d3edit.h" +#endif + +#include +#include "descent.h" +#include "slew.h" +#include "vecmat.h" +#include "ddio.h" +#include "object.h" +#include "mono.h" +#include "game.h" +#include "joystick.h" +#include "findintersection.h" +#include "room.h" + +//variables for slew system + +#define ROT_SPEED (1.0 / 8.0) //rate of rotation while key held down +#define VEL_SPEED (110.0) //rate of acceleration while key held down +#define JOY_NULL 32 + +float Slew_key_speed = 1.0f; +int Joystick_active = -1; + +// ------------------------------------------------------------------- + +#ifdef EDITOR +void SlewControlInit() +{ + Joystick_active = -1; + + #ifdef EDITOR + if (!D3EditState.joy_slewing) + return; + #endif + + if (joy_IsValid(JOYSTICK_1)) { + tJoyPos joystate; + tJoystick joyid = JOYSTICK_1; + + Joystick_active = (int)joyid; + joy_GetPos((tJoystick)Joystick_active,&joystate); //get all the stick values + + if ((abs(joystate.x) > 32) || (abs(joystate.y) > 32)) + EditorMessageBox("Warning: Your joystick is not centered. You should either center it now or recalibrate."); + } +} +#endif + + +int SlewStop(object *obj) +{ + if (!obj) + return 0; + + vm_MakeZero(&obj->mtype.phys_info.velocity); + + return 1; +} + +// Resets object's orientation +void SlewResetOrient(object *obj) +{ + if (!obj) + return; + + ObjSetOrient(obj, &Identity_matrix); +} + + +// Moves the object for one frame +int SlewFrame(object *obj,int movement_limitations) +{ + static short old_joy_x=0,old_joy_y=0; //position last time around + int ret_flags=0; + vector svel, movement; //scaled velocity (per this frame) + matrix rotmat,new_pm; + angvec rotang; + vector rottime; + vector new_pos; + int new_room; + fvi_query fq; + fvi_info hit_info; + int fate; + + float key_timex1=0, key_timex0=0; + float key_timey1=0, key_timey0=0; + float key_timez1=0, key_timez0=0; + float key_timep1=0, key_timep0=0; + float key_timeh1=0, key_timeh0=0; + float key_timeb1=0, key_timeb0=0; + + + if (!obj) + return 0; + +// check keyboard for slewing. + key_timex1 = ddio_KeyDownTime(KEY_PAD9); + key_timex0 = ddio_KeyDownTime(KEY_PAD7); + key_timey1 = ddio_KeyDownTime(KEY_PADMINUS); + key_timey0 = ddio_KeyDownTime(KEY_PADPLUS); + key_timez1 = ddio_KeyDownTime(KEY_PAD8); + key_timez0 = ddio_KeyDownTime(KEY_PAD2); + key_timep1 = ddio_KeyDownTime(KEY_LBRACKET); + key_timep0 = ddio_KeyDownTime(KEY_RBRACKET); + key_timeh1 = ddio_KeyDownTime(KEY_PAD6); + key_timeh0 = ddio_KeyDownTime(KEY_PAD4); + key_timeb1 = ddio_KeyDownTime(KEY_PAD1); + key_timeb0 = ddio_KeyDownTime(KEY_PAD3); + + if (!key_timep1) + key_timep1 = ddio_KeyDownTime(KEY_PAGEDOWN); + if (!key_timep0) + key_timep0 = ddio_KeyDownTime(KEY_PAGEUP); + + if (key_timex1 || key_timex0 || key_timey1 || key_timey0 || key_timez1 || key_timez0) + ret_flags |= SLEW_KEY; + if (key_timep1 || key_timep0 || key_timeh1 || key_timeh0 || key_timeb1 || key_timeb0) + ret_flags |= SLEW_KEY; + + if (key_timez0 || key_timez1) { + mprintf_at((1,0,0,"Timez0: %.2f ",key_timez0)); + mprintf_at((1,1,0,"Timez1: %.2f ",key_timez1)); + } + +// adjust physics info of object accordingly to keyboard input. + obj->mtype.phys_info.velocity.x += VEL_SPEED * (key_timex1 - key_timex0) * Slew_key_speed; + obj->mtype.phys_info.velocity.y += VEL_SPEED * (key_timey1 - key_timey0) * Slew_key_speed; + obj->mtype.phys_info.velocity.z += VEL_SPEED * (key_timez1 - key_timez0) * Slew_key_speed; + + //mprintf((0,"<%x %x %x> ",obj->mtype.phys_info.velocity.x,obj->mtype.phys_info.velocity.y,obj->mtype.phys_info.velocity.z)); + + rottime.x = key_timep1 - key_timep0; + rottime.y = key_timeh1 - key_timeh0; + rottime.z = key_timeb1 - key_timeb0; + rotang.p = (short) (65536.0 * rottime.x * ROT_SPEED * Slew_key_speed); + rotang.h = (short) (65536.0 * rottime.y * ROT_SPEED * Slew_key_speed); + rotang.b = (short) (65536.0 * rottime.z * ROT_SPEED * Slew_key_speed); + +// joystick movement +#ifdef EDITOR + if (Joystick_active!=-1) { + int joy_x,joy_y,btns; + tJoyPos joystate; + bool joyx_moved=false; + bool joyy_moved=false; + + joy_GetPos((tJoystick)Joystick_active,&joystate); //get all the stick values + + joy_x = joystate.x; + joy_y = joystate.y; + btns = joystate.buttons; + mprintf_at((2,1,0,"JoyX: %d ",joy_x)); + mprintf_at((2,2,0,"JoyY: %d ",joy_y)); + + if (abs(joy_x) < JOY_NULL) joy_x = 0; + if (abs(joy_y) < JOY_NULL) joy_y = 0; + + joyx_moved = (abs(joy_x - old_joy_x)>JOY_NULL); + joyy_moved = (abs(joy_y - old_joy_y)>JOY_NULL); + +//@@ if (joyx_moved) +//@@ mprintf((1,"SLEW: Joy X moved\n")); +//@@ if (joyy_moved) +//@@ mprintf((1,"SLEW: Joy Y moved\n")); + + if (btns) { + if (!rotang.p) + rotang.p = -joy_y * 256 * Frametime; + } + else { + if (joyy_moved) + obj->mtype.phys_info.velocity.z = (float) -joy_y / 4.0; + } + + if (!rotang.h) + rotang.h = joy_x * 256 * Frametime; + + if (joyx_moved) + old_joy_x = joy_x; + if (joyy_moved) + old_joy_y = joy_y; + } +#endif + + vm_AnglesToMatrix(&rotmat,rotang.p,rotang.h,rotang.b); + + new_pm = obj->orient * rotmat; + vm_Orthogonalize(&new_pm); + + ObjSetOrient(obj, &new_pm); + + vm_TransposeMatrix(&new_pm); //make those columns rows + + svel = obj->mtype.phys_info.velocity * Frametime; + movement = svel * new_pm; + + if (movement_limitations & 1) + movement.x = 0; + if (movement_limitations & 2) + movement.y = 0; + if (movement_limitations & 4) + movement.z = 0; + + new_pos = obj->pos + movement; + + //Did the object position change? + if ((movement.x != 0.0f) || (movement.y != 0.0f) || (movement.z != 0.0f)) + ret_flags |= SLEW_MOVE; + + if (ret_flags & SLEW_MOVE) { //Get the new room + bool outside_mine = ((obj->flags & OF_OUTSIDE_MINE) != 0); + + mprintf((1,"SLEW: Moved\n")); + + #ifdef EDITOR + if (Editor_view_mode == VM_ROOM) { + //Room number is bogus in room view, so don't update it + new_room = obj->roomnum; + } + else + #endif + //NOTE LINK TO ABOVE IF + if (outside_mine) { //starting outside the mine? + + //See if we've moved back into a room + new_room = FindPointRoom(&new_pos); + + if (new_room != -1) { //back in the mine + outside_mine = 0; + mprintf((0,"SLEW: Re-entered mine at room %d\n",new_room)); + } + else //not back in the mine + new_room = obj->roomnum; + } + else { + bool was_outside = (ROOMNUM_OUTSIDE(obj->roomnum) != 0); + + //Limit new position to terrain bounds if outside + if (was_outside) { + if(new_pos.x < 1.0) + new_pos.x = 1.0; + if(new_pos.x > TERRAIN_WIDTH * TERRAIN_SIZE - 1.0) + new_pos.x = TERRAIN_WIDTH * TERRAIN_SIZE - 1.0; + if(new_pos.z < 1.0) + new_pos.z = 1.0; + if(new_pos.z > TERRAIN_DEPTH * TERRAIN_SIZE - 1.0) + new_pos.z = TERRAIN_WIDTH * TERRAIN_SIZE - 1.0; + } + + //Call FVI up get updated room number + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &new_pos; + fq.rad = 0; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = FQ_IGNORE_RENDER_THROUGH_PORTALS; + fate = fvi_FindIntersection(&fq, &hit_info); + + //If bad room, don't move + if((fate == HIT_OUT_OF_TERRAIN_BOUNDS) || (hit_info.hit_room == -1)) { + new_room = obj->roomnum; + new_pos = obj->pos; + } + else + new_room = hit_info.hit_room; + + //The object hit a wall, and maybe went outside the mine. + if (fate == HIT_WALL) { + int t; + + mprintf((0,"SLEW: hit wall\n")); + + //Check if we're in a room + t = FindPointRoom(&new_pos); + + if (t != -1) { //We're in a room + new_room = t; + mprintf((0,"SLEW: still in mine in room %d\n",new_room)); + } + else { //Not in a room. Set a special flag + outside_mine = 1; + mprintf((0,"SLEW: left mine from room %d\n",new_room)); + } + } + + if (new_room != obj->roomnum) { //if we've changed rooms, say so + if (ROOMNUM_OUTSIDE(new_room)) + if (was_outside) + mprintf((0,"SLEW: Moved to cell %d, BOA TR %d\n",CELLNUM(new_room), TERRAIN_REGION(new_room))); + else + mprintf((0,"SLEW: Moved outside to cell %d\n",CELLNUM(new_room))); + else + if (was_outside) + mprintf((0,"SLEW: Moved inside to room %d\n",new_room)); + else + mprintf((0,"SLEW: Moved into room %d\n",new_room)); + } + } + + //Now we have the new room, so update the object position + ObjSetPos(obj,&new_pos,new_room,NULL, false); + + //Set outside-mine flag if we're outside + if (outside_mine) + obj->flags |= OF_OUTSIDE_MINE; + } + + //Set flag if rotation changed + if ((rotang.p != 0) || (rotang.h != 0) || (rotang.b != 0)) + ret_flags |= SLEW_ROTATE; + + return ret_flags; +} + +#endif diff --git a/Descent3/SmallViews.cpp b/Descent3/SmallViews.cpp new file mode 100644 index 000000000..ff7a42781 --- /dev/null +++ b/Descent3/SmallViews.cpp @@ -0,0 +1,580 @@ +/* + * $Logfile: /DescentIII/main/SmallViews.cpp $ + * $Revision: 33 $ + * $Date: 4/19/00 5:07p $ + * $Author: Matt $ + * + * System to draw small 3D views on the screen + * + * $Log: /DescentIII/main/SmallViews.cpp $ + * + * 33 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 32 4/06/00 9:25a Matt + * Fixed a screen clear problem on at ATI Rage Fury Maxx in dual-chip mode + * by forcing the screen to clear four times (instead of three). + * + * 31 5/17/99 11:19a Samir + * scale small views in full hud when shrinking screen. + * + * 30 5/08/99 1:06a Samir + * shrink font according to screen size. + * + * 29 5/01/99 12:41a Matt + * Added static when a small viewer object dies. + * + * 28 4/29/99 2:00a Jason + * fixed guided missile zbuffer problem + * + * 27 4/19/99 1:31p Matt + * Made guided missile small view use title and crosshairs flag, instead + * of doing special check for guided missile. + * + * 26 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 25 4/12/99 1:10a Matt + * Added labels for small views. + * + * 24 3/31/99 3:01p Jason + * fixed bug with guided missile + * + * 23 3/22/99 4:26p Samir + * added toggles for guided missile view and reticle. + * + * 22 3/04/99 2:56p Samir + * clear out flags for displaying small views for cockpit in + * ResetSmallViews. + * + * 21 2/27/99 5:09p Jason + * clear screen 3 times with z buffer + * + * 20 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 19 2/08/99 5:37p Jason + * added comment + * + * 18 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 17 1/13/99 12:42p Matt + * Added code to close a popup view + * + * 16 1/08/99 4:34p Matt + * Added an Int3() + * + * 15 12/08/98 1:47p Matt + * If the main view is showing the rear view, make the small rear views + * show the forward view (got it?) + * + * 14 10/23/98 12:05p Matt + * Position the small views in the cockpit relative to the window + * position. + * + * 13 10/21/98 10:18a Matt + * Don't do small views in letterbox mode + * + * 12 10/20/98 9:23p Matt + * When rendering the small views, set Viewer_object to the local viewer, + * to keep the player ship from rendering in the small views when using + * the external camera. + * + * 11 10/20/98 12:42p Matt + * Made the small views work on the cockpit. + * + * 10 10/20/98 12:28a Matt + * When turning off a small view, redraw the background screen. + * + * 9 10/14/98 6:01p Jason + * fixed small views going too high on the terrain + * + * 8 10/08/98 5:59p Jason + * fixed object and room popping with small views + * + * 7 5/26/98 11:36a Matt + * Changed small view system to allow the popup window in any of the three + * positions, to allow any window to be the "bigger" size, and to restore + * the old view when a popup view goes away. + * + * 6 4/30/98 6:46p Jason + * more framerate testing + * + * 5 2/09/98 3:19p Matt + * Added function to return the viewer object for a small view + * + * 4 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 3 2/04/98 12:50a Matt + * Added ability to view from a gun point in small views. + * Made pop-up view separate from and bigger than other small views. + * Added real keys to set view mode in the three small views. + * Changed rendering functions a bit to accommodate smallview changes. + * + * 2 1/30/98 2:55p Matt + * Added SmallViews system + * + * 1 1/29/98 11:15a Matt + * + */ + +#include "SmallViews.h" + +#include "descent.h" +#include "pserror.h" + +#include "game.h" +#include "object.h" +#include "terrain.h" +#include "render.h" +#include "gameloop.h" +#include "weapon.h" +#include "findintersection.h" +#include "config.h" +#include "terrain.h" +#include "gauges.h" +#include "cockpit.h" +#include "player.h" +#include "grtext.h" +#include "stringtable.h" +#include "gamefont.h" + + +//How many small views +#define NUM_SMALL_VIEWS 3 + +//How many chars to allow for the window label +#define LABEL_LEN 19 + +//Struct to keep data for small views +typedef struct { + int objhandle; //the object we're viewing from, or OBJECT_HANDLE_NONE if view not active + int flags; //various flags + float timer; //how much longer time window stays alive, if SVF_TIMED flag is set + float zoom; //the zoom for this window + int gun_num; //which gun to view from, or -1 if none + char label[LABEL_LEN+1]; //the label for the window +} small_view; + +//The array of small views +small_view Small_views[NUM_SMALL_VIEWS]; + +//Saved copies of small views for while a pop-up is active +small_view Small_views_save[NUM_SMALL_VIEWS]; + +// guided missile in small view? if so, then true. +bool Guided_missile_smallview = false; +static int Guided_missile_objhandle = OBJECT_HANDLE_NONE; + +//Returns the viewer object for the specified small view. +//If the view isn't active, returns OBJECT_HANDLE_NONE. +int GetSmallViewer(int window) +{ + ASSERT((window >= 0) && (window < NUM_SMALL_VIEWS)); + + return Small_views[window].objhandle; +} + +//Create a new small view. If there is already a view in the given window, the old view gets blown away. +//Parameters: window - which window to open. See constants in SmallViews.h +// objhandle - handle for the object to view from +// flags - various view attributes. See defines in header file. +// time - how long to keep the window up. If 0, keep up indefinitely +// zoom - the zoom for this window. If 0, use the default zoom +// gun_num - which gun to view from. if -1, use viewer's center and orientation. +// label - the label for the window +//Returns: which window was opened, or -1 if window couldn't be created +int CreateSmallView(int window,int objhandle,int flags,float time,float zoom,int gun_num,char *label) +{ + small_view *svp; + + //Check for valid window + if ((window < 0) || (window >= NUM_SMALL_VIEWS)) { + Int3(); + window = SVW_LEFT; + } + + //Make sure the object exists + if (! ObjGet(objhandle)) + return -1; + + svp = &Small_views[window]; + + //If this is a pop-up, and there was a non-popup active, save previous view + if ((flags & SVF_POPUP) && (svp->objhandle != OBJECT_HANDLE_NONE) && !(svp->flags & SVF_POPUP)) + Small_views_save[window] = *svp; + + //Set vars for new view + svp->objhandle = objhandle; + svp->zoom = zoom; + svp->gun_num = gun_num; + svp->flags = flags; + svp->timer = time; + if (time != 0.0) + svp->flags |= SVF_TIMED; + + strncpy(svp->label,label?label:"",LABEL_LEN); + svp->label[LABEL_LEN] = 0; //strncpy won't terminate if at max len + + if (window == SVW_LEFT) + Disable_primary_monitor = 1; + else if (window == SVW_RIGHT) + Disable_secondary_monitor = 1; + +// if guided view, set flag to true + if (ObjGet(objhandle) == Players[Player_num].guided_obj) { + Guided_missile_smallview = true; + Guided_missile_objhandle = objhandle; + } + + return window; +} + + +//Called to get rid of all the small views & init system +void ResetSmallViews() +{ + for (int v=0;vnum_frames * (STATIC_TIME - timer) / STATIC_TIME; + if (frame == vc->num_frames) + frame--; + int bm_handle = vc->frames[frame]; + + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (255); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + + rend_DrawScaledBitmap(0,0,width-1,height-1,bm_handle,0.0,0.0,1.0,1.0); + } + else + { + //draw 3D view + ASSERT( viewer != NULL ); + ASSERT( viewer_eye != NULL ); + object *save_viewer_object = Viewer_object; + bool rear_view = ((flags & SVF_REARVIEW) != 0); + + // Reset facings for mine stuff + ResetFacings(); + + //Set the viewer for this render + Viewer_object = viewer; + + //Render the world + GameRenderWorld(viewer,viewer_eye,viewer_roomnum,viewer_orient,zoom,rear_view); + + //Restore the viewer + Viewer_object = save_viewer_object; + } + + //Draw window outline + if (outline_color != -1) { + rend_SetFlatColor(outline_color); + rend_DrawLine(0,0,width-1,0); + rend_DrawLine(width-1,0,width-1,height-1); + rend_DrawLine(width-1,height-1,0,height-1); + rend_DrawLine(0,height-1,0,0); + } + + //Draw the label + if (label[0]) { + int w = grtext_GetLineWidth(label); + grtext_SetFont(HUD_FONT); + grtext_SetColor(HUD_COLOR); + grtext_SetAlpha(255); + grtext_SetFlags(0); + grtext_Puts((width - w)/2, 2, label); + grtext_Flush(); + } + + //Draw the crosshairs + if (flags & SVF_CROSSHAIRS) { + int cx = width/2, cy = height/2; + int line_w = width / 50, line_h = height / 50; + rend_SetZBufferState (0); + rend_SetFlatColor(GR_GREEN); + rend_DrawLine(cx-line_w, cy, cx+line_w,cy); + rend_DrawLine(cx, cy-line_h, cx, cy+line_h); + rend_SetZBufferState (1); + } + + //Done rendering + EndFrame(); +} + +//Find the room number of a gun point +int GetGunRoom(vector *p0,int roomnum,int objnum,vector *p1) +{ + fvi_query fq; + fvi_info hit_data; + int fate; + + fq.p0 = p0; + fq.startroom = roomnum; + fq.p1 = p1; + fq.rad = 0; + fq.thisobjnum = objnum; + fq.ignore_obj_list = NULL; + fq.flags = 0; + + fate = fvi_FindIntersection(&fq, &hit_data); + if (fate != HIT_NONE) + Int3(); + + return hit_data.hit_room; +} + +#define SMALL_WINDOW_W 120 +#define SMALL_WINDOW_H 120 +#define SPACE_BETWEEN_WINDOWS ((640 - 3 * SMALL_WINDOW_W) / 3) + +extern int Point_visible_last_frame; + +//Draw all the active small views +void DrawSmallViews() +{ + int v; + small_view *svp; + int left,top,right,bot; + ddgr_color outline_color; + vector *viewer_eye,gun_pos; + matrix *viewer_orient,gun_orient; + matrix save_orient; + vector save_eye; + int viewer_roomnum; + + //if letterbox, don't draw the small views + if (GetHUDMode() == HUD_LETTERBOX) + return; + + bool lsave=Detail_settings.Dynamic_lighting; + + g3_GetUnscaledMatrix (&save_orient); + g3_GetViewPosition (&save_eye); + + Detail_settings.Dynamic_lighting=false; + + for (v=0,svp=Small_views;vobjhandle == OBJECT_HANDLE_NONE) + continue; + + object *viewer = ObjGet(svp->objhandle); + bool kill_viewer=false; + + //Bail if object not around any more, or if its gone too high + if (!viewer || (viewer->type == OBJ_DUMMY)) + kill_viewer=true; + + if (viewer && OBJECT_OUTSIDE (viewer) && viewer->pos.y>=MAX_TERRAIN_HEIGHT) + kill_viewer=true; + + //If not view & not already static, switch to static + if (kill_viewer && !(svp->flags & SVF_STATIC)) + { + //Viewer is dead, so switch to static + svp->flags |= SVF_STATIC|SVF_TIMED; + svp->timer = STATIC_TIME; + svp->gun_num = -1; + } + + if (v == SVW_CENTER) //no center window for now + Int3(); + + if (CockpitState() == COCKPIT_STATE_QUASI) + { + continue; + } + + else if (CockpitState() == COCKPIT_STATE_FUNCTIONAL) + { + //draw on the cockpit + if (! GetCockpitWindowCoords((v==SVW_LEFT)?0:1,&left,&top,&right,&bot)) { + continue; + } + + //Move relative to the current window + left += Game_window_x; + right += Game_window_x; + top += Game_window_y; + bot += Game_window_y; + + outline_color = -1; + } + else + { + //draw windows on HUD based on the screen size + ASSERT(CockpitState() == COCKPIT_STATE_DORMANT); + + //Compute size of window + int x,y,w,h,spacing; + w = Game_window_h / 4; + spacing = ((Game_window_w - (NUM_SMALL_VIEWS) * w) / 3); + x = ((spacing + w) / 2) + (v * (spacing + w))+Game_window_x; + h = w; + y = Game_window_y + (Game_window_h - h/2 - Game_window_h/24); + + //If bigger window, increase size + if (svp->flags & SVF_BIGGER) + { + w += w/4; + h += h/4; + } + + left = x-(w/2); + top = y-(h/2); + right = x+(w/2); + bot = y+(h/2); + + //Set window color + outline_color = (svp->flags & SVF_POPUP) ? GR_RGB(255,0,0) : GR_RGB(0,0,255); + } + + //Get view info from the gun or from the object itself + if (svp->gun_num != -1) + { + //using a gun + vector gun_vector; + WeaponCalcGun(&gun_pos,&gun_vector,viewer,svp->gun_num); + vm_VectorToMatrix(&gun_orient,&gun_vector,NULL,NULL); + viewer_eye = &gun_pos; + viewer_orient = &gun_orient; + viewer_roomnum = GetGunRoom(&viewer->pos,viewer->roomnum,OBJNUM(viewer),&gun_pos); + } + else if (viewer) + { + //no gun, so just use the object + viewer_eye = &viewer->pos; + viewer_orient = &viewer->orient; + viewer_roomnum = viewer->roomnum; + } + else + { + ASSERT( svp->flags & SVF_STATIC ); + viewer_eye = NULL; + viewer_orient = NULL; + viewer_roomnum = -1; + } + + //Determine whether to draw rear view. If view has rear view set and the viewer is + //a player and the player has rear view on, then draw forward view + int flags = svp->flags; + if ((flags & SVF_REARVIEW) && (viewer == Viewer_object) && (viewer->type == OBJ_PLAYER) && (Players[viewer->id].flags & PLAYER_FLAGS_REARVIEW)) + flags &= ~SVF_REARVIEW; + + //Let's render + Point_visible_last_frame=-1; + RenderSmallWindow(left,top,right,bot,viewer,viewer_eye,viewer_roomnum,viewer_orient,flags,svp->zoom,outline_color,svp->label,svp->timer); + Point_visible_last_frame=-1; + + //Update time for this view + if (svp->flags & SVF_TIMED) + { + svp->timer -= Frametime; + if (svp->timer < 0.0) + { + CloseSmallView(v); + } + } + } + + Detail_settings.Dynamic_lighting=lsave; + + // Resets aspect ratio + g3_StartFrame(&save_eye,&save_orient,Render_zoom); + g3_EndFrame(); +} + +//Parameters: window - the window to get rid of +void CloseSmallView(int window) +{ + //Mark the window as inactive + if (Small_views[window].objhandle == Guided_missile_objhandle) { + // mark guided missile small view as inactive. + Guided_missile_objhandle = OBJECT_HANDLE_NONE; + Guided_missile_smallview = false; + } + + Small_views[window].objhandle = OBJECT_HANDLE_NONE; + + //Restore old view if there was one + if (Small_views_save[window].objhandle != OBJECT_HANDLE_NONE) { + Small_views[window] = Small_views_save[window]; + Small_views_save[window].objhandle = OBJECT_HANDLE_NONE; + } + else { + Clear_screen = 4; //force background redraw + + if (window == SVW_LEFT) + Disable_primary_monitor = 0; + else if (window == SVW_RIGHT) + Disable_secondary_monitor = 0; + + } +} + +//Get rid of a small view if it's a popup window +//Parameters: window - the window to get rid of +void ClosePopupView(int window) +{ + if (Small_views[window].flags & SVF_POPUP) + CloseSmallView(window); +} + diff --git a/Descent3/SmallViews.h b/Descent3/SmallViews.h new file mode 100644 index 000000000..d97d0279d --- /dev/null +++ b/Descent3/SmallViews.h @@ -0,0 +1,96 @@ +/* + * $Logfile: /DescentIII/Main/SmallViews.h $ + * $Revision: 11 $ + * $Date: 5/01/99 12:41a $ + * $Author: Matt $ + * + * Header for SmallViews.cpp + * + * $Log: /DescentIII/Main/SmallViews.h $ + * + * 11 5/01/99 12:41a Matt + * Added static when a small viewer object dies. + * + * 10 4/19/99 1:31p Matt + * Made guided missile small view use title and crosshairs flag, instead + * of doing special check for guided missile. + * + * 9 4/12/99 1:10a Matt + * Added labels for small views. + * + * 8 3/22/99 4:26p Samir + * added toggles for guided missile view and reticle. + * + * 7 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 6 1/13/99 12:42p Matt + * Added code to close a popup view + * + * 5 5/26/98 11:37a Matt + * Changed small view system to allow the popup window in any of the three + * positions, to allow any window to be the "bigger" size, and to restore + * the old view when a popup view goes away. + * + * 4 2/09/98 3:19p Matt + * Added function to return the viewer object for a small view + * + * 3 2/04/98 12:50a Matt + * Added ability to view from a gun point in small views. + * Made pop-up view separate from and bigger than other small views. + * Added real keys to set view mode in the three small views. + * Changed rendering functions a bit to accommodate smallview changes. + * + * 2 1/30/98 2:55p Matt + * Added SmallViews system + * + * 1 1/29/98 11:15a Matt + * + */ + +#include "descent.h" + +//Values for the small view windows +#define SVW_LEFT 0 +#define SVW_CENTER 1 +#define SVW_RIGHT 2 + +//Small view flags +#define SVF_POPUP 1 //This is a temporary window +#define SVF_BIGGER 2 //This window is drawn a little bigger than the normal window +#define SVF_REARVIEW 4 //Draw looking backward from the viewer +#define SVF_TIMED 8 //This window is timer-based. DO NOT USE THIS FLAG WHEN CALLING CreateSmallView(). +#define SVF_CROSSHAIRS 16 //This window has crosshairs +#define SVF_STATIC 32 //Window is showing static. The object handle is unused + +// if guided missile smallview is up, this will be true. +extern bool Guided_missile_smallview; + +//Create a new small view. If there is already a view in the given window, the old view gets blown away. +//Parameters: window - which window to open. See constants in SmallViews.h +// objhandle - handle for the object to view from +// flags - various view attributes. See defines in header file. +// time - how long to keep the window up. If 0, keep up indefinitely +// zoom - the zoom for this window. If 0, use the default zoom +// gun_num - which gun to view from. if -1, use viewer's center and orientation. +// label - the label for the window +//Returns: which window was opened, or -1 if window couldn't be created +int CreateSmallView(int window,int objhandle,int flags=0,float time=0.0,float zoom=D3_DEFAULT_ZOOM,int gun_num=-1,char *label=NULL); + +//Called to get rid of all the small views & init system +void ResetSmallViews(); + +//Draw all the active small views +void DrawSmallViews(); + +//Returns the viewer object for the specified small view. +//If the view isn't active, returns OBJECT_HANDLE_NONE. +int GetSmallViewer(int window); + +//Get rid of a small view +//Parameters: window - the window to get rid of +void CloseSmallView(int window); + +//Get rid of a small view if it's a popup window +//Parameters: window - the window to get rid of +void ClosePopupView(int window); diff --git a/Descent3/TelCom.cpp b/Descent3/TelCom.cpp new file mode 100644 index 000000000..78b56b698 --- /dev/null +++ b/Descent3/TelCom.cpp @@ -0,0 +1,4321 @@ +/* + * $Logfile: /DescentIII/main/TelCom.cpp $ + * $Revision: 152 $ + * $Date: 4/19/00 5:13p $ + * $Author: Matt $ + * + * Contains the code to initialize, use and control TelCom system + * + * $Log: /DescentIII/main/TelCom.cpp $ + * + * 152 4/19/00 5:13p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * Removed Mac OpenGL hack + * + * 151 3/20/00 12:21p Matt + * Merge of Duane's post-1.3 changes. + * Minor optimization. + * + * 150 10/21/99 9:29p Jeff + * B.A. Macintosh code merge + * + * 149 10/12/99 11:08a Jeff + * fixed bug regarding ship permissions to start (for level 1) if the ship + * is non-code default + * + * 148 7/13/99 3:33p Samir + * music plays in automap and all telcom screens from within game. + * + * 147 5/23/99 7:43p Jason + * fixed briefing problem with lowmem textures + * + * 146 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 145 5/12/99 1:58p Jason + * made telcom use the global AllowedShips array instead of its own local + * array + * + * 144 5/05/99 3:02a Jeff + * added single player ship selection text + * + * 143 5/03/99 1:16p Jeff + * play sounds at full volume + * + * 142 4/26/99 4:33p Samir + * mouse input changed a little so that ddio_MouseGetState is called once + * per frame. + * + * 141 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 140 4/20/99 12:46p Jeff + * telcom main menu, mouse over button sets focus. if you go into telcom + * main menu, when you leave a system it will return you to main menu. + * + * 139 4/17/99 3:44p Kevin + * Demo2 changes & fixes + * + * 138 4/15/99 10:06p Matt + * Changed briefing file foreign-language filenames to be in the form + * "level1_frn.brf". + * + * 137 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 136 4/06/99 1:53p Samir + * added improved progress bar. + * + * 135 4/02/99 8:32p Jeff + * added system event queue. Arrow keys work in main menu and ship + * selection + * + * 134 3/31/99 5:36p Jeff + * cargo shouldn't display in main menu (not done...if it will be done) + * + * 133 3/31/99 5:28p Jeff + * new way of key processesing...can capture printscreen now + * + * 132 3/28/99 12:44p Jeff + * fixed table file parsing to add back in the ships + * + * 131 3/24/99 2:22p Jeff + * fixed power button bug + * + * 130 3/02/99 6:59p Jeff + * no cargo in oem + * + * 129 2/28/99 3:17p Jeff + * single player ship selection not called in OEM + * + * 128 2/25/99 4:14a Jeff + * shift-esc always exits to Main Menu, skips past ship selection + * + * 127 2/22/99 3:37p Jason + * fixed markers in telcom + * + * 126 2/22/99 3:19p Jeff + * random bug fixes + * + * 125 2/20/99 9:22p Jeff + * finished telcom level goals screen. Made it so if you go into the + * telcom from the game it goes to main menu instead of briefings. + * + * 124 2/17/99 6:55p Jeff + * added jump button type. Added no early render flag for bitmaps. Fixed + * color bug for type text + * + * 123 2/10/99 4:45p Jeff + * table file parser stuff + * + * 122 2/09/99 6:49p Jeff + * removed extra call to startframe...no longer needed with stack based + * + * 121 2/09/99 3:32p Jeff + * table file parser takes quotes strings for force keywords + * + * 120 2/08/99 1:03a Jeff + * click made when changing ships in single player ship selection + * + * 119 2/05/99 7:04p Jeff + * table file parsing macros put in + * + * 118 2/04/99 7:19p Jeff + * added sound id for button click. main menu handling., briefing missing + * fix + * + * 117 2/03/99 1:23p Jeff + * more updates to single player ship selection...all thats left is stats + * + * 116 2/03/99 11:44a Jeff + * selected ship hooked into the game + * + * 115 2/03/99 1:03a Jeff + * almost done with single player ship selection...added rotating model + * + * 113 2/02/99 7:32p Jeff + * begining of single player ship selection + * + * 112 2/01/99 4:52p Jeff + * screenshots work in telcom + * + * 111 1/30/99 2:43p Jeff + * fixed infinite loop bug + * + * 110 1/29/99 7:13p Jeff + * localization + * + * 109 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 108 12/15/98 4:39p Jason + * added some more telcom functions + * + * 107 12/15/98 10:53a Jason + * yet more changes for 1.1 + * + * 106 12/11/98 11:30p Jeff + * oops forgot to remove test #define DEMO + * + * 105 12/11/98 11:22p Jeff + * fixed telcom briefings so they can be called within game in the + * demo...fixed some minor bugs that needed to be rounded up (typing sound + * and initialization screen text) + * + * 104 12/11/98 1:48p Kevin + * Took out automap ifdefs for demo + * + * 103 12/11/98 11:37a Jason + * + * 102 12/09/98 2:36p Jeff + * added function to enable/disable telcom system keys and disabled most + * system keys while in Automap + * + * 101 12/09/98 1:09p Jason + * second draft of automap + * + * 100 11/02/98 6:00p Jeff + * began adding single player ship selection + * + * 99 10/27/98 4:22p Jeff + * changed the framerate cap to use Sleep(), to possibly help sound + * performance + * + * 98 10/22/98 1:31a Jeff + * optimized creating the static bitmaps + * + * 97 10/22/98 12:03a Matt + * Disable creation of static bitmaps to speed the briefing startup. + * + * 96 10/21/98 11:12p Jeff + * page in all data on Telcom init + * + * 95 10/21/98 4:51p Jeff + * removed automap + * + * 94 10/21/98 11:14a Samir + * added generic code to skip rendering while in game controller config or + * telcom. + * + * 93 10/16/98 2:43p Jeff + * only call Gameframe if not multi_ui_bail_menu + * + * 92 10/14/98 11:24p Jeff + * turn off bilinear filtering in TC now + * + * 91 10/12/98 11:39p Jeff + * finished up new focus system of telcom + * + * 90 10/12/98 8:32p Jeff + * changed the way focus is handled + * + * 89 10/12/98 3:03p Jeff + * fixed Shift-esc, and right arrow doesn't exit out of telcom if on the + * last page + * + * 88 10/11/98 2:59a Jeff + * TelCom is completly multiplayer friendly. Removed cheat codes to + * GameCheat.cpp. Fixed up TelCom Mainmenu to handle certain systems not + * available + * + * 87 10/09/98 7:47p Jeff + * Fixed killrobot cheat + * + * 86 10/09/98 6:20p Jeff + * fixed weapon cheat + * + * 85 10/09/98 6:18p Jeff + * added camera cheat + * + * 84 10/09/98 3:05p Jeff + * commented out slew cheat... + * + * 83 10/09/98 1:10p Jeff + * removed call to SlewStop in cheat code section (not defined in release) + * + * 82 10/09/98 12:22p Jeff + * cheat code system put in here (temporary?) + * + * 81 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 80 9/29/98 2:51p Jeff + * put internal serial number check function in here to keep it from the + * rest of the serialization stuff + * + * 79 8/28/98 12:57p Jeff + * added sounds and some key functionality + * + * 78 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 77 8/19/98 2:29p Jeff + * if not a release build then you can escape to main menu + * + * 76 8/19/98 1:38p Jeff + * removed update2dsound + * + * 75 8/18/98 11:57a Jeff + * fixed sound problem + * + * 74 8/15/98 2:46p Matt + * Took out unneeded include + * + * 73 7/13/98 4:11p Jeff + * sped up entering telcom by about 3-4 times + * + * 72 7/11/98 9:16p Jeff + * moved automatically drawing monitor overlay graphics from update() to a + * function of it's own, that way the TelCom API can be avoided if needed + * + * 71 7/08/98 8:06p Jeff + * Initial creation of TelComCargo + * + * 70 6/16/98 10:54a Jeff + * + * 69 5/24/98 2:56a Jeff + * Adjust x offset for main menu buttons + * + * 68 5/19/98 10:48a Jeff + * if D3_FAST is set, TelCom doesn't run + * + * 67 5/18/98 4:21p Jeff + * setup TelCom so D3_FAST zips it through + * + * 66 5/18/98 2:23p Jeff + * Hooked up Automap + * + * 65 5/15/98 5:16p Jeff + * Added Main Menu + * + * 64 5/11/98 6:22p Jeff + * adjusted sound stopping position and removed calls to show/hide mouse + * + * 63 5/05/98 6:50p Jeff + * Telcom doesn't use rend_DrawLFBitmap anymore...more speed! + * + * 62 5/04/98 6:22p Jeff + * fixed possible sound bugs + * + * 61 5/04/98 5:29p Jeff + * Added sounds to TelCom events + * + * 60 5/04/98 1:35p Jeff + * Changes made for mouse handling + * + * 59 5/03/98 7:58p Jeff + * changes made to handle mouse and input from within TelCom instead of UI + * + * 58 5/01/98 2:16p Jeff + * changed default background colors for monitors + * + * 57 4/30/98 7:17p Jeff + * added a main menu and going back and forth between it and systems. + * Added a poweron initializing screen + * + * 56 4/26/98 7:19p Jeff + * Power down effect done only when in hardware (some weird bug puts it in + * an infinite loop in software + * + * 55 4/24/98 6:35p Jeff + * Move prototypes to telcom.h + * + * 54 4/24/98 3:28p Jeff + * fixed power down effect + * + * 53 4/23/98 7:09p Jeff + * added power up/down support + * + * 52 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 51 4/22/98 7:43p Jeff + * Added support for monitor hilites + * + * 50 4/21/98 4:22p Jeff + * Added functions for drawing hotspot in on/off positions + * + * 49 4/20/98 6:44p Jeff + * removed extra code after briefings, so when you exit briefings you + * don't hang around in limbo + * + * 48 4/17/98 6:54p Jeff + * added scanline and changed order or drawing screen and zbuffer state + * set on exit + * + * 47 4/15/98 6:28p Jeff + * removed some built in code for the briefings parser + * + * 46 4/02/98 12:07p Jeff + * Added framework for desktop effects, fixed corner bug + * + * 45 4/01/98 5:10p Jeff + * Added speed for Flash text + * + * 44 3/30/98 10:50a Jeff + * Initial Flash text put in + * + * 43 3/23/98 2:18p Jeff + * Moved structures to hotspotmap.h + * + * 42 3/23/98 11:08a Jeff + * Moved monitor and input stuff to seperate file + * + * 41 3/23/98 9:55a Jeff + * Made changes to remove old telcom + * + * 40 3/18/98 8:00p Jeff + * Added some functionality for text and using the monitor borders + * correctly in the new Telcom + * + * 39 3/12/98 3:32p Jeff + * Initial changes started for New TelCom + * + * 38 2/06/98 2:03p Jeff + * added start/stop time calls + * + * 37 2/04/98 5:26p Jeff + * Added movie support back into Telcom, and inventory stuff + * + * 36 1/31/98 8:50p Jeff + * Added a very very crude inventory screen to telcom + * + * 35 1/26/98 6:35p Jeff + * added a 'quick fast forward' key (spacebar) + * + * 34 1/21/98 1:09p Jeff + * Prettied up the code, comments + * + * 33 1/20/98 9:16p Jeff + * Added back in fading bitmaps + * + * 32 1/20/98 6:12p Jeff + * + * 31 1/20/98 12:08p Jeff + * + * 30 1/19/98 5:37p Jeff + * Got briefing up to par, and even better than before, added timer so + * scroll and type fonts are time based. + * + * 29 1/16/98 2:40p Jeff + * Adjusted so everything is displayed in correct spots, took out + * DefaultControl timer, added support for non fading bitmaps + * + * 28 1/15/98 11:11a Jeff + * Got TelCom in a working state again, switch from 2d to 3d + * + * 27 12/19/97 2:26p Jason + * more fixes for 2d/3d intergration + * + * 26 11/10/97 12:36p Samir + * Telcom sets screen mode itself, and use menu font, not d2menu. + * + * 25 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 24 9/15/97 4:21p Samir + * Added ddio_KeyFlush when quitting telecom to prevent untreated + * keystrokes. + * + * 23 8/19/97 2:11p Matt + * Closed files after they've been opened + * + * 22 8/15/97 3:17p Jeff + * Fixed some functions affected by last change + * + * 21 8/15/97 12:24p Jeff + * Changed things around a bit, added code to handle the round corners + * + * 20 8/13/97 3:13p Jeff + * optimized for memory usage (removed the second background from being + * stored in memory) + * + * 19 8/12/97 6:10p Jeff + * + * 18 8/12/97 5:34p Jeff + * Added some comments, made the code a bit spiffier + * + * 17 8/12/97 12:35p Jeff + * added scroll speed, fixed some more bugs and tweaks + * + * 16 8/11/97 6:43p Jeff + * Added user defined function hook for subwindows, fixed some bugs + * + * 15 8/08/97 6:36p Jeff + * fixed various bugs, did some tweaking + * + * 14 8/07/97 6:18p Jeff + * + * 13 8/07/97 12:03p Jeff + * + * 12 8/06/97 6:35p Jeff + * added rough multitasking to subwindows + * + * 11 8/06/97 3:51p Jeff + * fixed some bugs with the new subwindowing + * + * 10 8/06/97 11:57a Jeff + * + * 9 8/04/97 7:23p Jeff + * + * 8 8/04/97 6:23p Jeff + * + * 7 8/01/97 6:57p Jeff + * Added enable/disable for vcr and system buttons. Added screensaver + * + * 6 8/01/97 2:35p Jeff + * Added windows to the hotspotmap + * + * 5 7/30/97 3:59p Jeff + * Fixed VCR controls, updated for new artwork + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include "mono.h" +#include "renderer.h" +#include "render.h" +#include "ddio.h" +#include "descent.h" +#include "game.h" +#include "CFILE.H" +#include "application.h" +#include "TelCom.h" +#include "TelComEffects.h" +#include "Briefing.h" +#include "TelComAutoMap.h" +#include "TelComCargo.h" +#include "TelComGoals.h" +#include "mem.h" +#include "Mission.h" +#include "stringtable.h" +#include "multi.h" +#include "ship.h" +#include "polymodel.h" +#include "localization.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "textaux.h" +#include "psrand.h" +#include "controls.h" +#include "d3music.h" + +#define FRAME_RATE 30.0f +#define TCPE_TIME 0.5f + + +//------------------------------New TelCom Stuff--------------------------------------- + +void TelCom_BltToMem(int dest_bmp, int dx, int dy, int src_bmp, int sx, int sy, int sw, int sh, bool trans=true); +void TelCom_BltToScreen(int dx, int dy, chunked_bitmap *src_bmp); +void TelCom_BltToScreen(int dx, int dy, chunked_bitmap *src_bmp, int sx, int sy, int sw, int sh); +void DrawHotSpotOn(int hotspot); + +//************************************************************************************* + +//Globals--------------------------------------------------------------------------- + +bool TelCom_init=false; //Whether the TelComInit() function was called +bool Telcom_first_render = true; +bool Telcom_exit_to_mainmenu = false; +bool Telcom_called_from_game = false; +int Telcom_mouse_last_effect = -1; //the last efxnum the mouse was over + +hotspotmap_t hotspotmap; //Holds hotspot data (position) +windowmap_t windowmap; //Holds window data (position) +tTelComInfo Telcom_system; + +chunked_bitmap Telcom_bitmap; // background bitmap for telcom system +chunked_bitmap *hotspot_bitmaps; +int LoadTelcomBitmap(char *filename, chunked_bitmap *bmp); +bool TelComMainMenu(tTelComInfo *tcs); + + +void TelCom_PrepareCustomKeyEvents(void); +void TelCom_PostProcessCustomKeyEvents(void); + +static int Telcom_mouse_x, Telcom_mouse_y; + +//=================================================================================================== +//Functions +//=================================================================================================== + +//Initializes the TelCom system, only to be called once during game initialization +void TelComInit(void) +{ + mprintf((0,"Initializing TelCom System\n")); + + //load background screens + if(! (cfexist(TELCOM_DISPLAY_OGF))){ + mprintf((0,"%s file not found, exiting TelCom Init\n",TELCOM_DISPLAY_OGF)); + return; + } + + if (!LoadTelcomBitmap(TELCOM_DISPLAY_OGF, &Telcom_bitmap)) + Error("Unable to open %s in TelCom system.", TELCOM_DISPLAY_OGF); + + + TelCom_init = true; + + //init telcom system structs + InitSystems(); + + atexit(TelComFree); + + + TelcomPageAllIn(); +} + +//Cleanup crew, don't worry about calling this guy, it's an atexit() in the init +void TelComFree() +{ + bm_DestroyChunkedBitmap(&Telcom_bitmap); +} + + +//This is the main loop for the active TelCom...think of it as a void main(void) +void TelComMain(bool ingame,bool SelectShip) +{ + //Initialize sound system + TelcomInitSounds(); + + //Initialize screens + InitAllScreens(); + + //Initialize the Effects + EfxInit(); + + //Initialize the Telcom Render Engine + TelcomRenderInit(); + + //Initialize the TelCom System Event Manager + TelComInitEventManager(); + TelcomRenderSetScreen(DUMMY_SCREEN); + + //kinda a quick hack...it may stay only if there is no way to enter the briefings directly from the game + bool noescape = false; + if(!ingame) + noescape = true; + + Telcom_system.state = TCS_POWERON; + + TelcomCreateStartupScreen(); + + TelcomStartSound(TCSND_STARTUP); + TelcomStartSound(TCSND_RUNNING); + TelcomStopSound(TCSND_STARTUP); + +//#ifdef DEMO +// Telcom_system.current_status = TS_MISSION; +//#endif + + //initialize this to -1 so we get events right away + Telcom_mouse_last_effect = -1; + + //if we are coming from the game into the main menu, then any system we select, when + //we exit should take us back to main menu + bool return_to_main_menu = false; + if(ingame && Telcom_system.current_status == TS_MAINMENU) + { + return_to_main_menu = true; + } + + while(Telcom_system.state!=TCS_POWEROFF){ + + TelComEnableSystemKey(TCSYS_MAXKEYS,true); + + switch(Telcom_system.current_status){ + case TS_MISSION: + //if we are in briefings, then show the briefing + if(Current_level->flags&LVLFLAG_BRIEFING){ + //determine the correct language of briefing to look at + char basefilename[_MAX_FNAME]; + char briefingname[_MAX_FNAME]; + char extension[_MAX_FNAME]; + char *tag[] = {"","_GER","_SPN","_ITN","_FRN"}; + bool ok_to_go = false; + + ddio_SplitPath(Current_level->briefname,NULL,basefilename,extension); + + strcpy(briefingname,basefilename); + strcat(briefingname,tag[Localization_GetLanguage()]); + strcat(briefingname,extension); + + if(!cfexist(briefingname)) + { + //language version doesn't exist + strcpy(briefingname,Current_level->briefname); + + if(cfexist(briefingname)) + { + ok_to_go = true; + } + + }else + { + ok_to_go = true; + } + + if(ok_to_go) + { + ParseBriefing(briefingname,&Telcom_system); + + if(return_to_main_menu && (!((Game_mode&GM_MULTI) && Multi_bail_ui_menu))) + { + //return to main menu + Telcom_system.current_status = TS_MAINMENU; + Telcom_system.state = TCS_POWERON; + } + + }else + { + Telcom_system.current_status = TS_OFF; + } + }else{ + Telcom_system.current_status = TS_OFF; + } +#ifdef DEMO + //Telcom_system.state=TCS_POWEROFF; +#endif + break; + case TS_MAP: + TelComAutoMap(&Telcom_system); + if(return_to_main_menu && (!((Game_mode&GM_MULTI) && Multi_bail_ui_menu))) + { + //return to main menu + Telcom_system.current_status = TS_MAINMENU; + Telcom_system.state = TCS_POWERON; + } + +#ifdef DEMO + //Telcom_system.state=TCS_POWEROFF; +#endif + break; + case TS_CARGO: +#ifdef DEMO + //Telcom_system.state=TCS_POWEROFF; +#else + //show the inventory information + TelComCargo(&Telcom_system); + if(return_to_main_menu && (!((Game_mode&GM_MULTI) && Multi_bail_ui_menu))) + { + //return to main menu + Telcom_system.current_status = TS_MAINMENU; + Telcom_system.state = TCS_POWERON; + } +#endif + break; + case TS_GOALS: + //show goal screen + TelComGoalStatus(&Telcom_system); + if(return_to_main_menu && (!((Game_mode&GM_MULTI) && Multi_bail_ui_menu))) + { + //return to main menu + Telcom_system.current_status = TS_MAINMENU; + Telcom_system.state = TCS_POWERON; + } + break; + default: + if(!noescape){ + //display main menu + TelComMainMenu(&Telcom_system); + }else{ + Telcom_system.state=TCS_POWEROFF; + } + break; + } + } + + TelcomStopSound(TCSND_TYPING); + + //see if we should display the Single Player Ship Selection +#if (!defined (DEMO)) && (!defined (OEM)) + if(SelectShip && !Telcom_exit_to_mainmenu) + TelComSingleShipSelect(&Telcom_system); +#endif + + TelcomStopSound(TCSND_STARTUP); + TelcomStopSound(TCSND_RUNNING); + TelcomStartSound(TCSND_SHUTDOWN); + + TelcomCreateShutdownScreen(); + TelcomRenderSetScreen(DUMMY_SCREEN); + + float starttime = timer_GetTime(); + + while(timer_GetTime()-starttimedefer(); + } + + TelcomStopSound(TCSND_SHUTDOWN); + + TelcomCloseSounds(); + + TelcomRenderClose(); + + DestroyAllScreens(true); + + EfxClose(); +} + +//This will make the TelCom system active, displaying it on the screen. This version of TelComShow() takes +//a system as an argument and it will display that system +//Returns true on success +bool TelComShow(int system,bool ingame,bool ShipSelect) +{ + if(system>NUMBER_OF_SYSTEMS) + Telcom_system.current_status=TS_OFF; + + Telcom_system.current_status=system; + return TelComShow(ingame,ShipSelect); +} + +//This will make the TelCom system active, displaying it on the screen. This version of TelComShow() will make +//whatever the current system (either whatever the last system to be displayed or whatever TelComSetSystem() set +//it to. +//Returns true on success + +// externed from GameLoop.cpp +extern bool Skip_render_game_frame; + +bool TelComShow(bool ingame,bool ShipSelect) +{ + Skip_render_game_frame = true; + Telcom_called_from_game = ingame; + + bool result=false; + int old_sm; + + Telcom_exit_to_mainmenu = false; + + // must do this to clear out keyboard events + ddio_KeyFlush(); + + ubyte oldmip = Render_preferred_state.mipping; + ubyte oldbil = Render_preferred_state.filtering; + + + if(!TelCom_init) { + mprintf((0,"TELCOM SYSTEM WARNING: TelComInit() error!\n")); + return false; + } + + //Set Screen mode + old_sm = GetScreenMode(); + SetScreenMode(SM_MENU); + + hotspot_bitmaps = NULL; + + int TelCom_bitmap = bm_AllocLoadFileBitmap(IGNORE_TABLE(TELCOM_DISPLAY_OGF),0); + if(!cfexist(HOTSPOT_DISPLAY)){ + mprintf((0,"Couldn't find HotSpot map, so I'm going to make one\n")); + if(!cfexist(TELCOM_DISPLAY_TGA)){ + mprintf((0,"Unable to find %s to extract HotSqpots...skipping TelCom System\n",TELCOM_DISPLAY_TGA)); + FreeViewports(); + if(windowmap.wm) mem_free(windowmap.wm); + goto telcom_bad_error; + } + menutga_ConvertTGAtoHSM(TELCOM_DISPLAY_TGA); + } + + //Load up the hotspot map into the structure + menutga_LoadHotSpotMap(TelCom_bitmap,IGNORE_TABLE(HOTSPOT_DISPLAY),&hotspotmap,&windowmap); + if (TelCom_bitmap >= 0) + bm_FreeBitmap(TelCom_bitmap); + + if(hotspotmap.num_of_hotspots){ + int TelCom_onbmp = bm_AllocLoadFileBitmap(IGNORE_TABLE(TELCOM_DISPLAY_OGF_ON),0); + if(TelCom_onbmp!=-1){ + hotspot_bitmaps = (chunked_bitmap *)mem_malloc(sizeof(chunked_bitmap)*hotspotmap.num_of_hotspots); + ASSERT(hotspot_bitmaps); + CompressTelComOnImage(TelCom_onbmp, hotspot_bitmaps); + bm_FreeBitmap(TelCom_onbmp); + } + } + + //turn off mipmapping + Render_preferred_state.mipping = 0; + //turn off bilinear filtering + Render_preferred_state.filtering = 1; + rend_SetPreferredState(&Render_preferred_state); + rend_SetFiltering(0); + + //keep the mouse in view + ddio_MouseReset(); + ddio_MouseSetVCoords(Game_window_w, Game_window_h); + + //hang out in here until the user presses POWER + TelComMain(ingame,ShipSelect); + + //do some final deactivation + TelComDeactivate(); + + result = true; + +telcom_bad_error: + + if(hotspot_bitmaps) + mem_free(hotspot_bitmaps); + + // must do this to clear out keyboard events + ddio_KeyFlush(); + + //reset screen mode + SetScreenMode(old_sm); + + //reset some states + rend_SetZBufferState(1); + Render_preferred_state.mipping = oldmip; + Render_preferred_state.filtering = oldbil; + rend_SetPreferredState(&Render_preferred_state); + rend_SetFiltering(1); + rend_SetWrapType (WT_WRAP); + + Skip_render_game_frame = false; + + return Telcom_exit_to_mainmenu; +} + +//Initializes the systems so it has correct values +void InitSystems() +{ + Telcom_system.current_status=TS_MISSION; + Telcom_system.state = TCS_POWERON; +} + +//Frees allocated memory used by the windows +void FreeViewports() +{ + int count; + + if(windowmap.wm){ + for(count=0;count BAD_BITMAP_HANDLE) bm_FreeBitmap(windowmap.wm[count].lt_bmp); + if(windowmap.wm[count].rt_bmp > BAD_BITMAP_HANDLE) bm_FreeBitmap(windowmap.wm[count].rt_bmp); + if(windowmap.wm[count].lb_bmp > BAD_BITMAP_HANDLE) bm_FreeBitmap(windowmap.wm[count].lb_bmp); + if(windowmap.wm[count].rb_bmp > BAD_BITMAP_HANDLE) bm_FreeBitmap(windowmap.wm[count].rb_bmp); + } + } +} + + +//This function 'compresses' the on-state background, to save space +void CompressTelComOnImage(int bitmap, chunked_bitmap *array) +{ + if(!array) + return; + int count; + int width,height; + int handle; + + //the first hotspot bitmap doesn't exist so we can skip it + for(count=1;count=hotspotmap.num_of_hotspots) ) + return; + TelCom_BltToScreen(HotSpotL(hotspot),HotSpotT(hotspot),&hotspot_bitmaps[hotspot]); +} + +// --------------------------------------------------------------------------- +// performs 16-bit blts. + +void TelCom_BltToScreen(int dx, int dy, chunked_bitmap *src_bmp) +{ + TelCom_BltToScreen(dx,dy,src_bmp,0,0,src_bmp->pw, src_bmp->ph); +} + +void TelCom_BltToScreen(int dx, int dy, chunked_bitmap *src_bmp, int sx, int sy, int sw, int sh) +{ + rend_DrawScaledChunkedBitmap(src_bmp,dx,dy,sw,sh,255); +} + +void TelCom_BltToMem(int dest_bmp, int dx, int dy, int src_bmp, int sx, int sy, int sw, int sh, bool trans) +{ + ushort *dbits; + ushort *sbits; + int srowsize_w, drowsize_w, row, col; + +// set up blt. + srowsize_w = bm_w(src_bmp, 0); // rowsize in shorts + drowsize_w = bm_w(dest_bmp, 0); + dbits = (ushort *)bm_data(dest_bmp,0) + (dy * drowsize_w) + dx; + sbits = (ushort *)bm_data(src_bmp, 0) + (sy * srowsize_w) + sx; + + if (trans) { + for (row = 0; row < sh; row++){ + for (col = 0; col < sw; col++) + if (sbits[col] & OPAQUE_FLAG) + dbits[col] = sbits[col]; + + sbits += srowsize_w; + dbits += drowsize_w; + } + } + else { + for (row = 0; row < sh; row++){ + for (col = 0; col < sw; col++) + dbits[col] = sbits[col]; + + sbits += srowsize_w; + dbits += drowsize_w; + } + } +} + + +//returns left edge of hotspot +int HotSpotL(int hotspot) +{ + if(hotspot >= hotspotmap.num_of_hotspots) + return -1; + return hotspotmap.hs[hotspot].x[0].start; +} + +//returns width of hotspot +int HotSpotW(int hotspot) +{ + if(hotspot >= hotspotmap.num_of_hotspots) + return -1; + return hotspotmap.hs[hotspot].x[0].end - hotspotmap.hs[hotspot].x[0].start + 1; +} + +//returns the top edge of a hotspot +int HotSpotT(int hotspot) +{ + if(hotspot >= hotspotmap.num_of_hotspots) + return -1; + return hotspotmap.hs[hotspot].starting_y; +} + +//returns the height of a hotspot +int HotSpotH(int hotspot) +{ + if(hotspot >= hotspotmap.num_of_hotspots) + return -1; + return hotspotmap.hs[hotspot].scanlines; +} + +//returns the right edge of a hotspot +int HotSpotR(int hotspot) +{ + if(hotspot >= hotspotmap.num_of_hotspots) + return -1; + return hotspotmap.hs[hotspot].x[0].end; +} + +//returns the bottom edge of a hotspot +int HotSpotB(int hotspot) +{ + if(hotspot >= hotspotmap.num_of_hotspots) + return -1; + return hotspotmap.hs[hotspot].starting_y + hotspotmap.hs[hotspot].scanlines; +} + + +int LoadTelcomBitmap(char *filename, chunked_bitmap *chunk) +{ + int bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename), 0); + if (bm_handle == -1) + return 0; + + //make monitor windows transparent + menutga_LoadHotSpotMap(bm_handle,IGNORE_TABLE(HOTSPOT_DISPLAY),&hotspotmap,&windowmap); + + ushort *data = bm_data(bm_handle,0); + int wnd,x,y,bmw; + int bord_left,bord_right,bord_top,bord_bottom; + + bmw = bm_w(bm_handle,0); + + int starty,startx,endy,endx; + for(wnd=0;wnd BAD_BITMAP_HANDLE) + bm_FreeBitmap(bm_handle); + + return 1; +} + + +#define MAINMENU_BUTTON "TelComMenuButton.ogf" +#define MAINMENU_BUTTONGLOW "TelComMenuButtonGlow.ogf" +#define MAINMENU_BUTTONFOCUS "Forward.ogf" +#define MAINMENU_BUTTONGLOWFOCUS "ForwardFocus.ogf" +/* +$$TABLE_GAMEFILE "TelComMenuButton.ogf" +$$TABLE_GAMEFILE "TelComMenuButtonGlow.ogf" +$$TABLE_GAMEFILE "Forward.ogf" +$$TABLE_GAMEFILE "ForwardFocus.ogf" +*/ +#define BOFF_L 40 +#define BOFF_T 15 +#define MM_BUTTONX 99 +#define MM_BUTTONY 60 +#define MM_BUTTONOFFSET 50 + + + +//Main Menu button info +#define TCBRIEFING 0 +#define TCCARGO 1 +#define TCAUTOMAP 2 +#define TCGOALS 3 +#define TCMAX_MMBUTTONS 4 + + +int TCMMButtonDesc[] = { TXI_TCMM_BRIEFINGS, + TXI_TCMM_CARGO, + TXI_TCMM_AUTOMAP, + TXI_TCMM_GOALS}; + + +int mainmenu_system; + +typedef struct{ + bool enabled; + int efxid; + int system; + char text[128]; +}tMenuButton; +tMenuButton MMButtons[TCMAX_MMBUTTONS]; + +void TCMainMenuCallback(int efxnum) +{ + mainmenu_system = efxnum; +} +void TCMainMenuRenderCallback(void) +{ + grtext_SetFont(BIG_BRIEFING_FONT); + grtext_SetColor(GR_RGB(255,255,255)); + grtext_SetAlpha(255); + + int mm_y,mm_x; + mm_y = Telcom_system.Monitor_coords[MONITOR_MAIN].top + BOFF_T + MM_BUTTONY; + mm_x = Telcom_system.Monitor_coords[MONITOR_MAIN].left + BOFF_L + MM_BUTTONX; + + for(int i=0;iflags&LVLFLAG_BRIEFING){ + MMButtons[mm].enabled = true; + MMButtons[mm].system = TS_MISSION; + ok_to_run = true; + }else{ + MMButtons[mm].enabled = false; + MMButtons[mm].system = TS_OFF; + } + break; + case TCCARGO: +//#if (defined DEMO)||(defined OEM) + MMButtons[mm].enabled = false; + MMButtons[mm].system = TS_OFF; +/* +#else + //Cargo should only be enabled if we are in the game + if(Telcom_called_from_game){ + MMButtons[mm].enabled = true; + MMButtons[mm].system = TS_CARGO; + ok_to_run = true; + }else{ + MMButtons[mm].enabled = false; + MMButtons[mm].system = TS_OFF; + } +#endif +*/ + break; + case TCGOALS: + if(Telcom_called_from_game){ + MMButtons[mm].enabled = true; + MMButtons[mm].system = TS_GOALS; + ok_to_run = true; + }else{ + MMButtons[mm].enabled = false; + MMButtons[mm].system = TS_OFF; + } + break; + case TCAUTOMAP: + //Automap should only be enabled if we are in the game + if(Telcom_called_from_game){ + MMButtons[mm].enabled = true; + MMButtons[mm].system = TS_MAP; + ok_to_run = true; + }else{ + MMButtons[mm].enabled = false; + MMButtons[mm].system = TS_OFF; + } + break; + } + + strcpy(MMButtons[mm].text,TXT(TCMMButtonDesc[mm])); + + //now if it's enabled,fill in it's info + //create the button + if(MMButtons[mm].enabled){ + buttdesc.x = mm_x; + buttdesc.y = mm_y; + MMButtons[mm].efxid = CreateButtonEffect(&buttdesc,MONITOR_MAIN,0); + //adjust y + mm_y += MM_BUTTONOFFSET; + }else{ + MMButtons[mm].efxid = -1; + } + } + + if(!ok_to_run){ + //no systems available + tcs->state=TCS_POWEROFF; + } + + CreateBackgroundEffect(&backg,MONITOR_TOP,0); + CreateTextEffect(&textdesc,format(TXT_TCMAINMENU),MONITOR_TOP,0); + TelcomEndScreen(); + + TelcomRenderSetCallback(TCMainMenuRenderCallback); + + //Set the screen active + TelcomRenderSetScreen(0); + + bool done = false; + + TelCom_ClearCustomKeyEvents(); + TelCom_AddCustomKeyEvent(KEY_DOWN,0x13); //down arrow + TelCom_AddCustomKeyEvent(KEY_UP,0x14); //up arrow + + while(!done){ + Sound_system.BeginSoundFrame(Telcom_called_from_game); + + if(tcs->state==TCS_POWEROFF || mainmenu_system!=-1 || tcs->current_status!=TS_OFF){ + //we're done with the loop + done = true; + + if(mainmenu_system!=-1){ + //go through the buttons and compare efx id's + for(int i=0;icurrent_status = MMButtons[i].system; + }//endif + }//endfor + }//endif + } + + //Process all waiting events for the TelCom (we may only want to handle this once a frame!) + TelComHandleAllEvents(&Telcom_system); + + //Process custom events + tTCEvent evt; + while(TelCom_PopSystemEvent(&evt)) + { + switch(evt.id) + { + case 0x13: //up arrow + TelComSendEvent(TEVT_FAKEKEYPRESS,TCSYS_TAB); + break; + case 0x14: //down arrow + TelComSendEvent(TEVT_FAKEKEYPRESS,TCSYS_REVERSETAB); + break; + } + } + + TelcomRenderScreen(); + Descent->defer(); + if(KEY_STATE(KEY_ESC)) + Telcom_system.state = TCS_POWEROFF; + + Sound_system.EndSoundFrame(); + } + TelCom_ClearCustomKeyEvents(); + + DestroyAllScreens(); + TelcomRenderSetScreen(DUMMY_SCREEN); + TelcomRenderSetCallback(NULL); + return true; +} + +/* + ************************************************************************ + * TelCom Render Functions * + ************************************************************************ + * +*/ +///internal prototypes ************************************* +// Creates the screen overlays for the main monitor +void TelcomCreateScreenOverlays(void); +// Destroys the screen overlays that were used for the main monitor +void TelcomDestroyScreenOverlays(void); +// Renders the screen overlays +void TelcomRenderOverlays(void); +// Renders the screen hilights +void TelcomRenderDrawHiLites(void); +// Loads the Hilites for a monitor +void TelcomLoadHiLites(char *filelist[],int monitor,int xoff,int yoff); +// Frees all the memory allocated for the hilites +void TelcomFreeHiLites(void); +// Renders the scanline to be drawn +void TelcomRenderScanline(void); +// Creates the scanline +void TelcomCreateScanLine(void); +// Destroys the scanline +void TelcomDestroyScanLine(void); +// Loads the mouse cursor +void TelcomLoadMouseCursor(void); +// Frees the mouse cursor +void TelcomFreeMouseCursor(void); +// Renders the mouse cursor +void TelcomRenderMouse(void); +//draws the telcom background, handles flickering lights, power button etc +void TelcomDrawScreen(bool poweron,bool powerup); +//used to display the rounded corners of the monitor screens +void TelcomDisplayCorners(void); +// Frees the bitmaps used for the corners of each monitor +void TelcomFreeCorners(void); +// Allocates the bitmaps for corners of each monitor +void TelcomCreateCorners(void); +// Displays static on the screen +void TelcomDisplayStatic(float amount); +// Frees the static bitmap overlays +void TelcomFreeStaticOverlays(void); +// Creates the static bitmap overlays +void TelcomCreateStaticOverlays(void); +// Initializes the bitmaps, etc needed for power up/down effect +void TelcomInitPowerEffect(void); +// Frees the bitmaps, etc for power up/down effect +void TelcomFreePowerEffect(void); +// Draws a frame of the power up/down effect +void TelcomDoPowerEffect(bool power_down,float frametime); + + +///global variables **************************************** +//Telcom rendering globals +static void (*TC_callback)() = NULL; +int TC_current_screen = DUMMY_SCREEN; +int TC_cursor = -1; + +//Monitor hilight variables +int TelcomHiLiteCount[MAX_MONITOR]; +int *TelcomHiLites[MAX_MONITOR]; +struct{ + int x,y; +}TelcomHiLiteOffset[MAX_MONITOR]; +//Main monitor scanline info +float scanline_nexttime,scanline_speed; +int scanliney,scanline_handle; +float last_rendertime = 0,last_frametime = 0; +//Monitor corner variables +chunked_bitmap lt_corners[MAX_MONITOR]; +chunked_bitmap rt_corners[MAX_MONITOR]; +chunked_bitmap lb_corners[MAX_MONITOR]; +chunked_bitmap rb_corners[MAX_MONITOR]; +bool has_corners[MAX_MONITOR]; +//Screen variables +int TCWorking_screen; +//Static variables +#define STATIC_BMPS 12 +int StaticBmps[STATIC_BMPS]; +bool Telcom_show_static = false; +bool Telcom_static_last_frame = false; +float Telcom_static_setting = 0; +//Glitch variables +bool Telcom_show_glitch = false; +bool Telcom_glitch_screen = false; +float Telcom_glitch_setting = 0; +extern int glitch_dx,glitch_dy; +float myrand(float max); +//Powereffect variables +int PowerBmps[2]; + +// Initializes the Telcom rendering engine +void TelcomRenderInit(void) +{ + for(int monitor=0;monitornewtime){ + Descent->delay(cap_time - newtime); + newtime = timer_GetTime(); + } + + frametime = newtime - last_rendertime; + last_rendertime = newtime; + last_frametime = frametime; + + + + if(Telcom_first_render){ + Telcom_first_render = false; + frametime = 0; + } + + StartFrame(0,0,Game_window_w,Game_window_h); + + if(Telcom_show_glitch){ + if(((int)myrand(100*(1.0-Telcom_glitch_setting)))==0){ + Telcom_glitch_screen = true; + glitch_dx = (int)(myrand(20)-10.0); + glitch_dy = (int)(myrand(12)-6.0); + } + } + + //first render all the effects + RenderScreen(TC_current_screen,&Telcom_system,frametime); + + //call the callback if it exists to render anything on the monitor screen + if(TC_callback) + TC_callback(); + + //Render the screen overlays + TelcomRenderOverlays(); + + grtext_Flush(); + + switch(power_effect){ + case 1: + TelcomDoPowerEffect(false,frametime); + break; + case 2: + TelcomDoPowerEffect(true,frametime); + break; + }; + + //Draw the TelCom System + TelcomDrawScreen(poweron,powerup); + + TelcomRenderMouse(); + + EndFrame(); + rend_Flip(); + + Telcom_glitch_screen = false; +} + +// Sets the callback of the Render. This will get called after Effects are drawn, before the screen overlays +void TelcomRenderSetCallback(void (*callback)()) +{ + TC_callback = callback; +} + +// Sets what screen should be drawn by the Telcom +void TelcomRenderSetScreen(int screen) +{ + ASSERT( screen>=0 && screenoff_time) && (myrand(100.0)>=NEON_STATEOFF) ){ + start_time = timer_GetTime(); + neon_state = NEON_ON1; + } + break; + case NEON_ON1: + TelcomStartSound(TCSND_LIGHTBULB); + if((timer_GetTime()-start_time)>NEON_ON1_TIME){ + start_time = timer_GetTime(); + neon_state = NEON_OFF2; + } + break; + case NEON_OFF2: + if((timer_GetTime()-start_time)>NEON_OFF2_TIME){ + start_time = timer_GetTime(); + neon_state = NEON_ON2; + } + break; + case NEON_ON2: + if((timer_GetTime()-start_time)>NEON_ON2_TIME){ + start_time = timer_GetTime(); + neon_state = NEON_OFF3; + } + break; + case NEON_OFF3: + if((timer_GetTime()-start_time)>NEON_OFF3_TIME){ + start_time = timer_GetTime(); + neon_state = NEON_ON3; + on_time = myrand(3); + } + break; + case NEON_ON3: + if( ((timer_GetTime()-start_time)>on_time) && (myrand(100.0)=NEON_ON1) + DrawHotSpotOn(NEON_LIGHT); + if(poweron) + DrawHotSpotOn(POWER_BUTTON); + + rend_SetFiltering(1); + + TelcomDisplayCorners(); +} + +// Allocates the bitmaps for corners of each monitor +void TelcomCreateCorners(void) +{ + for(int monitor=0;monitor0){ + Telcom_static_last_frame = true; + float amp = stat*Telcom_static_setting/HIGH_STATIC; + //TCStartStaticSound(amp); + TelcomDisplayStatic(amp); + }else{ + if(Telcom_glitch_screen){ + float amp = myrand(1)-0.3; + Telcom_static_last_frame = true; + //TCStartStaticSound(amp); + TelcomDisplayStatic(amp); + }else{ + /* + if(Telcom_static_last_frame) + TCEndStaticSound(); + */ + Telcom_static_last_frame = false; + } + } + + if(stat>HIGH_STATIC) + stat = HIGH_LEVEL; + if(stat=0 && monitor scanline_nexttime){ + for(x = start_x; x < end_x; x+=32) + rend_DrawScaledBitmap(x,scanliney,x+32,scanliney+16,scanline_handle,0,0,1,1,-1,alphas); + scanliney++; + scanline_nexttime += scanline_speed; + } + + if(scanliney>Telcom_system.Monitor_coords[MONITOR_MAIN].bottom) + scanliney = Telcom_system.Monitor_coords[MONITOR_MAIN].top-32; + + rend_SetFiltering (1); +} + +// Creates the scanline +void TelcomCreateScanLine(void) +{ + scanliney = Telcom_system.Monitor_coords[MONITOR_MAIN].top-32; + scanline_speed = 1.0f/50.0f; + scanline_nexttime = timer_GetTime() + scanline_speed; + scanline_handle = bm_AllocBitmap(32,32,0); + + if(scanline_handle<=BAD_BITMAP_HANDLE){ + return; + } + + int start_pixel; + start_pixel = 0; + + ushort *data = bm_data(scanline_handle,0); + + int x,y; + for(y=0;y<32;y++){ + for(x=0;x<32;x++){ + if(y>=start_pixel) + data[y*32+x] = OPAQUE_FLAG | GR_RGB16(128,128,200); + else + data[y*32+x] = NEW_TRANSPARENT_COLOR; + } + } +} + +// Destroys the scanline +void TelcomDestroyScanLine(void) +{ + if(scanline_handle<=BAD_BITMAP_HANDLE) + return; + + bm_FreeBitmap(scanline_handle); + scanline_handle = -1; +} + +// Loads the mouse cursor +void TelcomLoadMouseCursor(void) +{ + TC_cursor = bm_AllocLoadFileBitmap("StdCursor.ogf",0); + if(TC_cursor==-1) + TC_cursor = BAD_BITMAP_HANDLE; +} + +// Renders the mouse cursor +void TelcomRenderMouse(void) +{ + rend_SetOverlayType (OT_NONE); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetZBufferState (0); + rend_SetAlphaType (AT_CONSTANT+AT_TEXTURE); + rend_SetAlphaValue(255); + + if(TC_cursor > -1){ + int mx, my; +#ifdef MACINTOSH + // JEFF: Why is this like this? Why doesn't this use Telcom_mouse_* ? + ddio_MouseGetState(&mx, &my, NULL, NULL); +#else + mx = Telcom_mouse_x; + my = Telcom_mouse_y; +#endif + + float u0=0.0f, v0=0.0f, u1=1.0f, v1=1.0f; + int cur_w = bm_w(TC_cursor,0); + int cur_h = bm_h(TC_cursor,0); + + if (mx >(Game_window_w - cur_w)) + u1 = ((float)(Game_window_w - mx)) / ((float)cur_w); + if (my >(Game_window_h - cur_h)) + v1 = ((float)(Game_window_h - my)) / ((float)cur_h); + + rend_DrawScaledBitmap(mx,my,mx+(int)((float)cur_w*u1),my+(int)((float)cur_h*v1),TC_cursor,u0,v0,u1,v1); + } + rend_SetZBufferState(1); +} + +// Creates the static bitmap overlays +void TelcomCreateStaticOverlays(void) +{ + int i; + for(i=0;i1 ) + alpha = 1; + + type = (int) myrand(21); + if( (type<7) ) + type = 0; + else + if( (type<14) ) + type = 4; + else + type = 8; + + int xoff = Telcom_system.Monitor_coords[MONITOR_MAIN].left; + int yoff = Telcom_system.Monitor_coords[MONITOR_MAIN].top; + + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (alpha*255.0); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + + int dy = ((int)myrand(32))-16; + + int h = bm_h(StaticBmps[type],0); + int w = bm_w(StaticBmps[type],0); + int j = 0; + for(y = dy; y < height;y+=h) + for(x = 0; x < width; x+=w){ + rend_DrawScaledBitmap(x+xoff,y+yoff,x+w+xoff,y+h+yoff,StaticBmps[type+(j%4)],0,0,1,1); + h = bm_h(StaticBmps[type+(j%4)],0); + w = bm_w(StaticBmps[type+(j%4)],0); + j++; + } + + rend_SetFiltering (1); +} + +// Frees the mouse cursor +void TelcomFreeMouseCursor(void) +{ + //remove the mouse cursor + if(TC_cursor>BAD_BITMAP_HANDLE){ + bm_FreeBitmap(TC_cursor); + TC_cursor=-1; + } +} + +float TC_effect_position; + +// Initializes the bitmaps, etc needed for power up/down effect +void TelcomInitPowerEffect(void) +{ + PowerBmps[0] = bm_AllocBitmap(32,32,0); + if(PowerBmps[0]==-1) + return; + + int i; + ushort *bmpdata = bm_data(PowerBmps[0],0); + for(i=0;i<32*32;i++) + bmpdata[i] = OPAQUE_FLAG|GR_RGB(0,0,0); + + PowerBmps[1] = FindTextureName("TelcomPowerbar"); + if(PowerBmps[1]==-1) + return; + PowerBmps[1] = GetTextureBitmap(PowerBmps[1],0); + TC_effect_position = 0; +} + +// Frees the bitmaps, etc for power up/down effect +void TelcomFreePowerEffect(void) +{ + if( (PowerBmps[0]!=-1) && (PowerBmps[0]!=BAD_BITMAP_HANDLE) ) + bm_FreeBitmap(PowerBmps[0]); +} + +#define TCPE_START 0.1f +#define TCPE_STAGE1 0.3f +#define TCPE_STAGE2 0.8f +#define TCPE_END 1.0f + +// Draws a frame of the power up/down effect +void TelcomDoPowerEffect(bool power_down,float frametime) +{ + int height = (Telcom_system.Monitor_coords[MONITOR_MAIN].bottom) - (Telcom_system.Monitor_coords[MONITOR_MAIN].top); + int width = (Telcom_system.Monitor_coords[MONITOR_MAIN].right) - (Telcom_system.Monitor_coords[MONITOR_MAIN].left); + int xoff = Telcom_system.Monitor_coords[MONITOR_MAIN].left; + int yoff = Telcom_system.Monitor_coords[MONITOR_MAIN].top; + + if(!power_down && TC_effect_position>=TCPE_STAGE2) + goto tcpe_adjust; + + //draw the power effect at it's current state + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (255); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + rend_SetZBufferState(0); + + if(TC_effect_position<=TCPE_STAGE1){ + //stage 1 + int i,x,y; + int line_count = (TC_effect_position-TCPE_START)/(TCPE_STAGE1-TCPE_START) * ((float)((width/32)+3)); + if(line_count<0) + line_count = 0; + + for(y = 0; y < height;y+=32) + for(x = 0; x < width; x+=32){ + rend_DrawScaledBitmap(x+xoff,y+yoff,x+32+xoff,y+32+yoff,PowerBmps[0],0,0,1,1); + } + + if(TC_effect_position1) + TC_effect_position = 1; + + rend_SetFiltering (1); + rend_SetZBufferState(1); +} + +/*************************************************** + * TelCom Event Handling + *************************************************** + */ + +extern tceffect TCEffects[MAX_TCEFFECTS]; +extern int Screen_roots[MAX_TELCOM_SCREENS]; +// returns the true if there is a next screen (from the current) +bool TelComIsThereANextScreen(void); + +/* + * Initializes the TelCom Event manager system + * Call this as soon as possible + * You can call it whenever the events need to be flushed + */ +void TelComInitEventManager(void) +{ + int i; + + for(i=0;iTCSYS_MAXKEYS) + return; + + int bit = 0x01; + + if(key==TCSYS_MAXKEYS){ + bit = 0xFFFFFFFF; + }else{ + bit = (bit<current_status=TS_OFF; + }break; + case TEVT_SCREENSHOT: + { + // take a screenshot + mprintf((0,"Taking Screenshot\n")); + DoScreenshot(); + }break; + case TEVT_TCNEXT: + { + //the user wants to go to the next screen + if(TelcomAdvanceScreen() && tcs->current_status==TS_MISSION && !Telcom_called_from_game){ + //exit out of TelCom + tcs->state=TCS_POWEROFF; + } + }break; + case TEVT_TCPREV: + { + //the user wants to go to the prev screen + TelcomBackupScreen(); + }break; + case TEVT_TCJUMP: + { + int screen = event.parms[0]; + ASSERT(screen>=0 && screenget_mouse_raw_values(&x, &y); + } + else + { + buttons = ddio_MouseGetState(&x,&y,NULL,NULL); + } + + Telcom_mouse_x = x; + Telcom_mouse_y = y; + + //check to see if the power button was clicked + if(!buttons && Telcom_system.Telcom_mouse_downtime>0 && (x>=HotSpotL(POWER_BUTTON)) && (x<=HotSpotR(POWER_BUTTON)) && (y>=HotSpotT(POWER_BUTTON)) && (y<=HotSpotB(POWER_BUTTON)) ) + { + Telcom_system.state=TCS_POWEROFF; + } + + frametime = last_frametime; + + //see if the mouse is over any button + int efxnum = FindButtonEffectByXY(x,y,screen); + + if(efxnum!=-1){ + //check to see if this is the effect num the mouse was last over, if not, then + //register it as mouse enter + if(Telcom_mouse_last_effect!=efxnum) + { + SendEventToEffect(efxnum,TEVT_MOUSEENTER); + Telcom_mouse_last_effect = efxnum; + } + + //the mouse is over a button + SendEventToEffect(efxnum,TEVT_MOUSEOVER); + + effect_clicktype = TCEffects[efxnum].buttoninfo.click_type; + + if(buttons){ + if(effect_clicktype==CLICKTYPE_DOWN){ + SendEventToEffect(efxnum,TEVT_MOUSEDOWN); + }else if(Telcom_system.Telcom_mouse_downtime==0 && effect_clicktype==CLICKTYPE_CLICKDOWN){ + SendEventToEffect(efxnum,TEVT_MOUSECLICK); + } + }else{ + if(Telcom_system.Telcom_mouse_downtime>0 && effect_clicktype==CLICKTYPE_CLICKUP){ + //send the mouse up event + SendEventToEffect(efxnum,TEVT_MOUSEUP); + } + } + }else + { + //the mouse is not over any effect, reset it's efxnum owner (we can do a mouseleave event here) + Telcom_mouse_last_effect = -1; + } + + if(buttons) + { + Telcom_system.Telcom_mouse_downtime += frametime; + }else + { + Telcom_system.Telcom_mouse_downtime = 0; + } + + // If we are calling the telcom from inside the game, than we have some processing to do + if(Telcom_called_from_game && (Game_mode&GM_MULTI)){ + // Process all game frames + if(!Multi_bail_ui_menu) + GameFrame(); + + //If we are in a multiplayer game and we have been told to bail, than do so! + if (Multi_bail_ui_menu) { + tcs->current_status=TS_OFF; + tcs->state = TCS_POWEROFF; + } + } + + //handle music if in game. + if (Telcom_called_from_game) { + tMusicSeqInfo music_info; + music_info.frametime = frame_time; + music_info.player_dead = false; + music_info.started_level = false; + D3MusicDoFrame(&music_info); + } +} + +/* + * Given the key that has been pressed, or being held down, it processes the event depending on what key + * it is. + * + */ + +extern int AM_terrain,AM_realign,AM_center_on_player,AM_current_marker; +void TelComHandleKeyPress(int key,bool click,int screen_id) +{ + int bit = 0x01; + bit = (bit<=0 && curr_focus=MAX_TELCOM_SCREENS) + return true; + switch(Telcom_system.Screen_state[next_screen]){ + case SS_EMPTY: + //no screen even being worked on...return true + return true; + break; + case SS_BEING_MADE: + return false; + break; + case SS_READY: + TelcomRenderSetScreen(TC_current_screen+1); + return false; + break; + } + return true; +} + +// returns the true if there is a next screen (from the current) +bool TelComIsThereANextScreen(void) +{ + int next_screen = TC_current_screen+1; + if(next_screen>=MAX_TELCOM_SCREENS) + return false; + switch(Telcom_system.Screen_state[next_screen]){ + case SS_EMPTY: + //no screen even being worked on...return true + return false; + break; + case SS_READY: + case SS_BEING_MADE: + return true; + break; + } + return false; +} + +// Backs up a screen +// returns true is there is no screen (it's not being made, nor ready) +bool TelcomBackupScreen(void) +{ + int next_screen = TC_current_screen-1; + if(next_screen<0) + return true; + switch(Telcom_system.Screen_state[next_screen]){ + case SS_EMPTY: + //no screen even being worked on...return true + return true; + break; + case SS_BEING_MADE: + return false; + break; + case SS_READY: + TelcomRenderSetScreen(TC_current_screen-1); + return false; + break; + } + return true; +} + +bool TelComJumpScreen(int screen) +{ + if(screen<0 || screen>=MAX_TELCOM_SCREENS) + return true; + switch(Telcom_system.Screen_state[screen]){ + case SS_EMPTY: + //no screen even being worked on...return true + return true; + break; + case SS_BEING_MADE: + return false; + break; + case SS_READY: + TelcomRenderSetScreen(screen); + return false; + break; + } + + return true; +} + +// Destroys and clears all screens of the TelCom +void DestroyAllScreens(bool remove_dummy) +{ + int end = (remove_dummy)?MAX_TELCOM_SCREENS:DUMMY_SCREEN; + + for(int i=0;i=0 && screen='A' && c<='F'){ + value = value * 16 + (c-'A'+10); + }else + goto get_num; + } + + if(value==0xFCABBFFA) + return 0; + } + +get_num: + //we got to get the serial num if we can + int value = 0; + char c; + for(int i=0;i<8;i++){ + c = mydata[i+1]; + if(c>='0' && c<='9'){ + value = value*16 + c - '0'; + }else if (c>='A' && c<='F'){ + value = value*16 + c - 'A'+10; + }else + return -1; + } + + //now byte swap the value to get it to it's original value + value = ( ((value&0xFF000000)>>24) | ((value&0xFF0000)>>8) | ((value&0xFF)<<24) | ((value&0xFF00)<<8) ); + + *num = value; + return 1; +} + + +/* + **************************************************************** + * TelCom Sound Functions * + **************************************************************** + * +*/ + + +typedef struct{ + int hlhandle,handle; +}TCSound; + +TCSound TelcomSounds[TCSND_SOUNDCOUNT]; + +char *TCSoundFiles[] = {"Briefstartup1","BriefStatic","Briefmonitoroff1","Briefingrunning","Briefbulb1","Briefingtype","Menu Slider Click"}; + +/* +$$TABLE_SOUND "Briefstartup1" +$$TABLE_SOUND "BriefStatic" +$$TABLE_SOUND "Briefmonitoroff1" +$$TABLE_SOUND "Briefingrunning" +$$TABLE_SOUND "Briefbulb1" +$$TABLE_SOUND "Briefingtype" +$$TABLE_SOUND "Menu Slider Click" +*/ + +// Initalizes the Telcom sound sytem +void TelcomInitSounds(void) +{ + for(int i=0;i=0 && sid=0 && sid 3 ){ + //not supported + mprintf((0,"Skipping Single Player Ship Selecting, %d ships found\n",found_ships)); + + int ship_index_to_use = 0; + int bit = 0x01; + bool found = true; + + for(ship_index_to_use=0;ship_index_to_usecurrent_status = TS_SHIPSELECT; + + //set focus to the ship we currently have selected + int focus_ship = -1; + for(int j=0;jcurrent_status!=TS_SHIPSELECT){ + //we're done with the loop + done = true; + } + + //Process all waiting events for the TelCom (we may only want to handle this once a frame!) + TelComHandleAllEvents(tcs); + + //Process custom events + tTCEvent evt; + while(TelCom_PopSystemEvent(&evt)) + { + switch(evt.id) + { + case 0x13: //left arrow + TelComSendEvent(TEVT_FAKEKEYPRESS,TCSYS_REVERSETAB); + break; + case 0x14: //right arrow + TelComSendEvent(TEVT_FAKEKEYPRESS,TCSYS_TAB); + break; + case 0x15: //enter key + tcs->state = TCS_POWEROFF; + break; + } + } + + TelcomRenderScreen(); + Descent->defer(); + if(KEY_STATE(KEY_ESC)) + tcs->state = TCS_POWEROFF; + + Sound_system.EndSoundFrame(); + } + + Telcom_system.state = TCS_POWEROFF; //turn back off telcom + + DestroyAllScreens(); + TelcomRenderSetScreen(DUMMY_SCREEN); + TelcomRenderSetCallback(NULL); + + int ship_index_to_use = TCSSGetSelectedShipIndex(); + mprintf((0,"Selected: %s\n",Ships[ship_index_to_use].name)); + Players[Player_num].ship_index = ship_index_to_use; + + TelCom_ClearCustomKeyEvents(); +} + +//returns the ship index that is selected +int TCSSGetSelectedShipIndex(void) +{ + int id = SSShips[SHIP_PYRO_ID].ship_index; + for(int i=0;ianim_size / Render_zoom + 6; + + TelcomStopSound(TCSND_CLICK); + if(!first_click) + TelcomStartSound(TCSND_CLICK); +} + +void TCSSSCallback(void) +{ + if(TCShipSelect.ship_model==-1){ + //no ship selected yet + return; + } + + vector viewer_eye = {0,0,0}; + matrix viewer_orient = IDENTITY_MATRIX; + viewer_eye.z = -TCShipSelect.cam_dist; + + grtext_Flush(); + StartFrame(325,142,535,280); + g3_StartFrame(&viewer_eye,&viewer_orient,D3_DEFAULT_ZOOM); + rend_SetFlatColor (0); + + float normalized_time[MAX_SUBOBJECTS]; + float light_scalar,size; + PageInPolymodel (TCShipSelect.ship_model, OBJ_PLAYER, &size); + poly_model *pm = GetPolymodelPointer(TCShipSelect.ship_model); + vector view_pos; + vector light_vec; + matrix view_orient = IDENTITY_MATRIX; + matrix final_mat = IDENTITY_MATRIX; + matrix rot_mat; + + // draw model. + SetNormalizedTimeAnim(0, normalized_time, pm); + + view_pos = pm->anim_size_offset; + + //move 30 degrees a sec + vm_AnglesToMatrix(&rot_mat,0,(last_frametime)*(65535/360)*30,0); + + vm_MatrixMul (&view_orient,&rot_mat,&TCShipSelect.orient); + vm_Orthogonalize(&view_orient); + TCShipSelect.orient = view_orient; + + light_vec.x = 0.0f; light_vec.y = -1.0f; light_vec.z = -1.0f; + light_scalar = 0.8f; + vm_NormalizeVector(&light_vec); + + rend_SetZBufferState(1); + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (255); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + + DrawPolygonModel(&view_pos, &view_orient, TCShipSelect.ship_model, normalized_time,0, &light_vec, light_scalar,light_scalar,light_scalar); + + g3_EndFrame(); + EndFrame(); + + // Render the name of the selected ship + ubyte old_alpha = grtext_GetAlpha(); + int old_font = grtext_GetFont(); + ddgr_color old_color = grtext_GetColor(); + grtext_SetAlpha(255); + grtext_SetFont(BIG_BRIEFING_FONT); + grtext_SetColor(GR_WHITE); + + char *name = Ships[SSShips[TCShipSelect.CurrentSelectID].ship_index].name; + int line_length = grtext_GetTextLineWidth(name); + int line_height = grfont_GetHeight(BIG_BRIEFING_FONT); + int bar_width = 177; + int bar_height = 34; + int bar_x = 287 + Telcom_system.Monitor_coords[MONITOR_MAIN].left; + int bar_y = 246 + Telcom_system.Monitor_coords[MONITOR_MAIN].top; + + int x,y; + x = bar_x + ( (bar_width/2) - (line_length/2) ); + y = bar_y + ( (bar_height/2) - (line_height/2) ); + + grtext_Printf(x,y,name); + + //write ship descriptions + tShipInfo *si = &TCSSInfo[TCShipSelect.CurrentSelectID]; + x = 115; + y = 130; + int width = 210; + int max_height = y + 150; + grtext_SetFont(BRIEFING_FONT); + int height = grfont_GetHeight(BRIEFING_FONT); + char *line_text; + char buffy[512]; + char linebuffer[512]; + + for(int q=0;q<8;q++) + { + switch(q) + { + case 0: + line_text = TXT(si->max_speed); + break; + case 1: + line_text = TXT(si->maneuverability); + break; + case 2: + line_text = TXT(si->shield); + break; + case 3: + line_text = TXT(si->ordnance); + break; + case 4: + line_text = TXT(si->wingspan); + break; + case 5: + line_text = TXT(si->length); + break; + case 6: + line_text = TXT(si->height); + break; + case 7: + line_text = TXT(si->weight); + break; + } + + textaux_WordWrap(line_text,buffy,width,BRIEFING_FONT); + + line_text = textaux_CopyTextLine(buffy,linebuffer); + while((line_text)&&((y+height)next; + + mem_free(curr); + curr = next; + } + Telcom_custom_key_event_root = NULL; +} + +/* + Adds a custom key event translation mask +*/ +void TelCom_AddCustomKeyEvent(int key_id,int event_id) +{ + tCustomKeyEventID *curr = Telcom_custom_key_event_root; + + if(!curr) + { + curr = Telcom_custom_key_event_root = (tCustomKeyEventID *)mem_malloc(sizeof(tCustomKeyEventID)); + }else + { + while(curr->next) + { + curr = curr->next; + } + curr->next = (tCustomKeyEventID *)mem_malloc(sizeof(tCustomKeyEventID)); + curr = curr->next; + } + + curr->next = NULL; + curr->event_id = event_id; + curr->key_id = key_id; +} + +/* + Prepares to handle custom keys +*/ +void TelCom_PrepareCustomKeyEvents(void) +{ + tCustomKeyEventID *curr = Telcom_custom_key_event_root; + + while(curr) + { + curr->downcount = 0; + curr = curr->next; + } +} + +/* + Post process custom keys +*/ +void TelCom_PostProcessCustomKeyEvents(void) +{ + tCustomKeyEventID *curr = Telcom_custom_key_event_root; + + while(curr) + { + if(curr->downcount) + { + TelCom_SendSystemEvent(curr->event_id); + return; + } + + curr = curr->next; + } +} + +/* + Handles a possible custom key event +*/ +void TelCom_ProcessCustomKeyEvent(int key_id) +{ + tCustomKeyEventID *curr = Telcom_custom_key_event_root; + + while(curr) + { + if(key_id == curr->key_id) + { + curr->downcount++; + return; + } + + curr = curr->next; + } +} diff --git a/Descent3/TelCom.h b/Descent3/TelCom.h new file mode 100644 index 000000000..0ced78c79 --- /dev/null +++ b/Descent3/TelCom.h @@ -0,0 +1,324 @@ +#ifndef __TELCOM_H_ +#define __TELCOM_H_ + +#include "hotspotmap.h" +#include "gamefont.h" +#include "bitmap.h" + +//TelCom Briefing System Header File + +//TelCom Function Prototypes +//these are the functions that can/should be called outside of TelCom*.cpp + +//inits the Telcom system...needs to be called before anything else, only needs to be called once +void TelComInit(void); + +//handles input processing, etc +void TelComDefer(void); + +// Pages in all the necessary bitmaps, textures & sounds for the TelCom so that they are in memory +void TelcomPageAllIn(void); + +//Cleanup crew, don't worry about calling this guy, it's an atexit() in the init +void TelComFree(); + +//actually displays the Telcom system, if you specify a system it will display showing that system, +//else it will display the last used system +//returns true on success + +bool TelComShow(bool ingame=true,bool ShipSelect=false); +bool TelComShow(int system,bool ingame=true,bool ShipSelect=false); + +//deactivates the TelCom system if it is running +void TelComDeactivate(void); + + +#define HOTSPOT_DISPLAY "TelComon.HSM" //string name for the HotSpot map + + +#define TELCOM_DISPLAY_TGA "TelComOff.TGA" //string name for the TGA file that needs to be converted +#define TELCOM_DISPLAY_OGF "TelComOff.OGF" //converted OGF filename +#define TELCOM_DISPLAY_OGF_ON "TelComOn.OGF" //TelCom in the on state file +#define BACKGROUND_COLOR GR_RGB(49,29,150) + +/* +$$TABLE_GAMEFILE "TelComon.HSM" +$$TABLE_GAMEFILE "TelComOff.OGF" +$$TABLE_GAMEFILE "TelComOn.OGF" +*/ + +extern chunked_bitmap Telcom_bitmap; + +/* +// Note for above: In TelCom.h there is a section of defines which give names to the above files +// for use when calling a function like DisplayStillScreen() + +*/ +//NOTE: The following #defines MUST match up with Sean's alpha values +//TelCom Systems + +#define TS_POWER 1 +#define TS_OFF -3 //The TelCom is powered on...but nothing selected + +#define POWER_BUTTON 1 +#define NEON_LIGHT 2 + +#define NUMBER_OF_SYSTEMS 5 //Total number of systems listed below +#define TS_MAINMENU TS_OFF +#define TS_MISSION 1 //Mission Objective system +#define TS_CARGO 2 //Inventory system +#define TS_MAP 3 //Automap system +#define TS_SHIPSELECT 4 //SinglePlayer Ship Selection +#define TS_GOALS 5 //Goal status + + +//Windows (=alpha value-MAX_HOTSPOTS) used internally +#define MAX_TELCOM_SCREENS 20 +#define MAX_TELCOM_EVENTS 10 +#define DUMMY_SCREEN MAX_TELCOM_SCREENS - 1 + +//Monitors +#define MONITOR_MAIN 0 +#define MONITOR_TOP 1 +#define MAX_MONITOR 2 + +//Telcom keys +#define TCSYS_TAB 0 +#define TCSYS_UP 1 +#define TCSYS_DOWN 2 +#define TCSYS_LEFT 3 +#define TCSYS_RIGHT 4 +#define TCSYS_ENTER 5 +#define TCSYS_SPACE 6 +#define TCSYS_Q 7 +#define TCSYS_F1 8 +#define TCSYS_F2 9 +#define TCSYS_F3 10 +#define TCSYS_PRINTSCRN 11 +#define TCSYS_1 12 +#define TCSYS_2 13 +#define TCSYS_3 14 +#define TCSYS_4 15 +#define TCSYS_5 16 +#define TCSYS_6 17 +#define TCSYS_7 18 +#define TCSYS_8 19 +#define TCSYS_REVERSETAB 20 + +#define TCSYS_MAXKEYS 21 +//Screen states +#define SS_EMPTY 0 +#define SS_BEING_MADE 1 +#define SS_READY 2 + +//Telcom system states +#define TCS_POWEROFF 0 +#define TCS_POWERON 1 + +//A cleanup function, freeing up memory used for the monitor windows +void FreeViewports(); + +//Initializes the system buttons for use +void InitSystems(); + +//This function 'compresses' the on-state background, to save space +void CompressTelComOnImage(int bitmap, chunked_bitmap *array); +//frees up on-state bitmaps +void FreeTelComOnBitmaps(chunked_bitmap *array); + +//returns the screen coordinates of a hotspot +int HotSpotW(int hotspot); +int HotSpotH(int hotspot); +int HotSpotL(int hotspot); +int HotSpotR(int hotspot); +int HotSpotT(int hotspot); +int HotSpotB(int hotspot); +/* +void TCStartPowerupSound(void); +void TCStopPowerupSound(void); +void TCStartPowerDownSound(void); +void TCStopPowerDownSound(void); +void TCStartRunningSound(void); +void TCStopRunningSound(void); +void TCStartStaticSound(float amp); +*/ +void TCStartBulbSound(void); +void DoCursor(void); + +extern hotspotmap_t hotspotmap; +extern windowmap_t windowmap; +extern bool TelCom_running; //Used within TelComMain(), when POWER button is pressed become false + +//structure for a bounding box (used when getting a hotspot bounding box) +typedef struct button_box{ + int top,bottom,left,right; +}box; + +typedef struct{ + int id; + int parms[2]; +}tTCEvent; + + +typedef struct tTelComInfo{ + box Monitor_coords[MAX_MONITOR]; + ubyte Screen_state[MAX_TELCOM_SCREENS]; + tTCEvent Telcom_event_queue[MAX_TELCOM_EVENTS]; //Event queue for TelCom System + int TranslateSysKey[TCSYS_MAXKEYS]; + int current_status; + bool Telcom_systemkey_states[TCSYS_MAXKEYS]; + float Telcom_mouse_downtime; + ubyte state; + int TelComSysKeyEnable; +}tTelComInfo; +extern tTelComInfo Telcom_system; + + +// This is the function called by TelCom to choose a ship +void TelComSingleShipSelect(tTelComInfo *tcs); + + +//********************************************** +// TelCom Rendering Functions +//********************************************** +// Initializes the Telcom rendering engine +void TelcomRenderInit(void); +// Closes down the Telcom rendering engine +void TelcomRenderClose(void); +// Renders the current screen for 1 frame +void TelcomRenderScreen(bool poweron=true,bool powerup=false,ubyte power_effect=1); +// Sets what screen should be drawn by the Telcom +void TelcomRenderSetScreen(int screen); +// Sets the callback of the Render. This will get called after Effects are drawn, before the screen overlays +void TelcomRenderSetCallback(void (*callback)()); +// Creates a placeholder screen that can be used if no screens are available +void TelcomCreateDummyScreen(void); +// Creates a placeholder screen that can be used for startup +void TelcomCreateStartupScreen(void); +// Creates a placeholder screen that can be used for shutdown +void TelcomCreateShutdownScreen(void); +// Enables Static on the Telcom main screen, setting is the probability of static happening +void TelcomEnableStatic(float setting); +// Disables static on the Telcom main screen +void TelcomDisableStatic(void); +// Enables glitching on the Telcom main screen +void TelcomEnableGlitch(float setting); +// Disables glitching on the TelCom +void TelcomDisableGlitch(void); + +/*************************************************** + * TelCom Screen Control + *************************************************** + * +*/ +// Initializes the TelCom Screens so they're ready for input +void InitAllScreens(void); +// Destroys and clears all screens of the TelCom +void DestroyAllScreens(bool remove_dummy=false); +// Starts a TelCom screen so it can be created +void TelcomStartScreen(int screen); +// Ends a TelCom screen. Calling this enables the screen to be displayed +void TelcomEndScreen(void); +// Advances to the next screen +// returns true is there is no screen (it's not being made, nor ready) +bool TelcomAdvanceScreen(void); +// Backs up a screen +// returns true is there is no screen (it's not being made, nor ready) +bool TelcomBackupScreen(void); +// Jumps to a screen +// returns true if there is no screen (not being made, nor ready) +bool TelComJumpScreen(int screen); + +/*************************************************** + * TelCom Event Handling + *************************************************** + */ + +//Prototypes +/* + * Initializes the TelCom Event manager system + * Call this as soon as possible + * You can call it whenever the events need to be flushed + */ +void TelComInitEventManager(void); +/* + * Calling this sends an event message to the TelCom, adding it to it's event queue + * + */ +void TelComSendEvent(int event_num,int parm1=0,int parm2=0); +/* + * Call this when handling the TelCom system events, returns true if there is an event waiting, it will then fill + * in the passed struct. You should call this repeatedly until a value of false is returned, signaling no more + * events in the queue + */ +bool TelComPopEvent(tTCEvent *evt); + +// Similar to the TelCom's main system event queue, these are for use by individual systems +void TelCom_ClearSystemQueue(void); +void TelCom_SendSystemEvent(int event_num,int parm1=-1,int parm2=-1); +bool TelCom_PopSystemEvent(tTCEvent *evt); + +/* + Handles a possible custom key event +*/ +void TelCom_ProcessCustomKeyEvent(int key_id); +/* + Removes any custom key event translation masks +*/ +void TelCom_ClearCustomKeyEvents(void); +/* + Adds a custom key event translation mask +*/ +void TelCom_AddCustomKeyEvent(int key_id,int event_id); + +/* + * This should be called once a frame, it will handle all events in the TelCom system and send off any system + * events to the effects/buttons + */ +void TelComHandleAllEvents(tTelComInfo *tcs); +/* + * Given the key that has been pressed, or being held down, it processes the event depending on what key + * it is. + * + */ +void TelComHandleKeyPress(int key,bool click,int screen_id); +/* + * Enables/Disables a TelCom system key, pass in TCSYS_MAXKEYS to enable/disable all keys + * + */ +void TelComEnableSystemKey(int key,bool enable); + +/* + * Forces an effect to have focus (if is allowed to have focus) + * + */ +void TelComSetFocusOnEffect(int efxnum); + +/* + **************************************************************** + * TelCom Sound Functions * + **************************************************************** + * +*/ + +#define TCSND_STARTUP 0 +#define TCSND_STATIC 1 +#define TCSND_SHUTDOWN 2 +#define TCSND_RUNNING 3 +#define TCSND_LIGHTBULB 4 +#define TCSND_TYPING 5 +#define TCSND_CLICK 6 +#define TCSND_SOUNDCOUNT 7 + +// Initalizes the Telcom sound sytem +void TelcomInitSounds(void); +// Closes down the Telcom sound system +void TelcomCloseSounds(void); +// Starts a sound playing (if it isn't already) +void TelcomStartSound(int sid); +// Stops a sound playing +void TelcomStopSound(int sid); + +extern bool Telcom_called_from_game; + +#endif \ No newline at end of file diff --git a/Descent3/TelComAutoMap.cpp b/Descent3/TelComAutoMap.cpp new file mode 100644 index 000000000..1b9592e2d --- /dev/null +++ b/Descent3/TelComAutoMap.cpp @@ -0,0 +1,891 @@ +/* +* $Logfile: /DescentIII/main/TelComAutoMap.cpp $ +* $Revision: 37 $ +* $Date: 7/20/99 4:59p $ +* $Author: Samir $ +* +* TelCom Automap +* +* $Log: /DescentIII/main/TelComAutoMap.cpp $ + * + * 37 7/20/99 4:59p Samir + * automap key will close automap. + * + * 36 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 35 4/24/99 6:46p Jeff + * added functions for theif so he can steal things other than weapons + * + * 34 4/24/99 5:45p Matt + * Made the marker message use a localizable string. + * + * 33 4/24/99 1:54a Matt + * Changed to use briefing font for labels at bottom, and HUD font for + * name at top. + * + * 32 4/14/99 11:44a Jason + * fixes for secret rooms + * + * 31 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 30 4/11/99 2:24p Kevin + * made AM_rotated_points dynamically allocated so it's only used if you + * go into the automap. Should help dedicated servers quite a bit. + * + * 29 4/02/99 5:42p Jason + * display level info name instead of current level name. Confused? + * Yeah, so am I. + * + * 28 4/01/99 5:58p Jason + * automap now tells you exactly where you haven't gone yet...dunno if + * this is a good thing. If enough people bitch then I will change it. + * + * 27 3/31/99 5:29p Jeff + * new way of key processesing...can capture printscreen now + * + * 26 3/29/99 7:30p Jason + * added fading doors + * + * 25 2/25/99 11:02p Jeff + * fixed automap rendering + * + * 24 2/24/99 6:23p Samir + * poll controls if in automap, not suspend and resume per frame. + * + * 23 2/22/99 3:37p Jason + * fixed markers in telcom + * + * 22 2/15/99 5:32p Jason + * made ship blink in automap + * + * 21 2/01/99 4:35p Jeff + * tab exits the automap + * + * 20 1/29/99 7:13p Jeff + * localization + * + * 19 1/29/99 11:29a Jason + * added misc bug fixes/features + * + * 18 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 17 12/23/98 3:08p Jason + * added new player ship + * + * 16 12/16/98 4:17p Jason + * draw external rooms no matter what + * + * 15 12/15/98 4:39p Jason + * added some more telcom functions + * + * 14 12/14/98 11:57a Jason + * changes for visibile automap + * + * 13 12/14/98 11:06a Jason + * changes for 1.1 + * + * 12 12/11/98 11:37a Jason + * + * 11 12/09/98 2:52p Jason + * fixed automap problem on terrain + * + * 10 12/09/98 2:36p Jeff + * added function to enable/disable telcom system keys and disabled most + * system keys while in Automap + * + * 9 12/09/98 1:09p Jason + * second draft of automap + * + * 8 12/07/98 5:37p Jason + * first pass at automap + * + * 7 8/28/98 12:57p Jeff + * added sounds and some key functionality + * + * 6 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 5 7/28/98 12:02p Jeff + * got file prepared for Matt to implement Automap + * + * 4 7/11/98 9:16p Jeff + * moved automatically drawing monitor overlay graphics from update() to a + * function of it's own, that way the TelCom API can be avoided if needed + * + * 3 6/16/98 10:54a Jeff + * + * 2 5/18/98 2:23p Jeff + * Automap displays stand-in art right now +* +* $NoKeywords: $ +*/ + +#include "TelComAutoMap.h" +#include +#include +#include + +#include "CFILE.H" +#include "pserror.h" +#include "ddio.h" +#include "bitmap.h" +#include "room.h" + +#include "TelCom.h" +#include "TelComEffects.h" +#include "renderer.h" +#include "game.h" +#include "mem.h" +#include "stringtable.h" +#include "lightmap_info.h" +#include "lightmap.h" +#include "controls.h" +#include "terrain.h" +#include "config.h" +#include "gameloop.h" +#include "multi.h" +#include "fireball.h" +#include "gamesequence.h" +#include "Mission.h" +#include "polymodel.h" +#include "player.h" +#include "marker.h" +#include "hlsoundlib.h" + +// Variables needed for automap +vector AM_view_pos={0,0,0}; +matrix AM_view_orient; +tTelComInfo *AM_tcs; +int AM_ship_model_handle=-1; + +ubyte AutomapVisMap[MAX_ROOMS]; + +float AM_heading; +float AM_pitch; + +int AM_terrain=0,AM_realign=0,AM_center_on_player=0,AM_current_marker; + +int Num_AM_rooms; +ushort AM_room_list[MAX_ROOMS]; +ubyte AM_rooms_seen[MAX_ROOMS]; +g3Point *AM_rotated_points = NULL;//[MAX_VERTS_PER_ROOM]; + +void TCAMCallback(void); + +static ubyte *Small_faces[MAX_ROOMS]; + +// clears out the list of visible rooms that we've seen +void AutomapClearVisMap () +{ + memset (AutomapVisMap,0,MAX_ROOMS); +} + +void ClassifyAMFaces() +{ + int i=0; + for (i=0;i<=Highest_room_index;i++) + { + if (Rooms[i].used) + { + Small_faces[i]=(ubyte *)mem_malloc (Rooms[i].num_faces); + ASSERT (Small_faces[i]); + + memset (Small_faces[i],0,Rooms[i].num_faces); + + room *rp=&Rooms[i]; + + for (int t=0;tnum_faces;t++) + { + face *fp=&rp->faces[t]; + + vector verts[MAX_VERTS_PER_FACE]; + vector center; + for (int k=0;knum_verts;k++) + verts[k]=rp->verts[fp->face_verts[k]]; + + float size=vm_GetCentroidFast (¢er,verts,fp->num_verts); + + if (size<120) + Small_faces[i][t]=1; + if (size<60) + Small_faces[i][t]=2; + + } + } + } +} + +void FreeAMFaces() +{ + for (int i=0;i<=Highest_room_index;i++) + { + if (Rooms[i].used) + { + mem_free (Small_faces[i]); + Small_faces[i]=NULL; + } + } +} + +// Returns the camera position to the player +void TCAMCenterOnPlayer() +{ + + vm_MakeZero (&AM_view_pos); + vm_MakeIdentity (&AM_view_orient); + + matrix newmat; + angvec heading; + AM_view_pos=Viewer_object->pos-(Viewer_object->orient.fvec*10); + vm_ExtractAnglesFromMatrix(&heading,&Player_object->orient); + + vm_AnglesToMatrix (&newmat,0,heading.h,0); + AM_view_orient=newmat; + + AM_heading=heading.h; + AM_pitch=heading.p; +} + +// This is the function called by TelCom +// return true if TelCom should exit to TelCom Main Menu +// return false if you should exit out of TelCom completly +// mmonitor = main monitor class +// lmonitor = local monitor class (the monitor at the top of the screen) +bool TelComAutoMap(tTelComInfo *tcs) +{ + bool done = false; + + if(!AM_rotated_points) + { + AM_rotated_points = (g3Point *) mem_malloc(sizeof(g3Point)*MAX_VERTS_PER_ROOM); + } + AM_tcs=tcs; + AM_current_marker=-1; + TelComEnableSystemKey(TCSYS_MAXKEYS,false); //disable all telcom system keys + TelComEnableSystemKey(TCSYS_F1,true); //enable F1 and enter, those are all we may need in automap + TelComEnableSystemKey(TCSYS_F2,true); //enable F2 and enter, those are all we may need in automap + TelComEnableSystemKey(TCSYS_F3,true); //enable F3 and enter, those are all we may need in automap + TelComEnableSystemKey(TCSYS_1,true); //enable marker key + TelComEnableSystemKey(TCSYS_2,true); //enable marker key + TelComEnableSystemKey(TCSYS_3,true); //enable marker key + TelComEnableSystemKey(TCSYS_4,true); //enable marker key + TelComEnableSystemKey(TCSYS_5,true); //enable marker key + TelComEnableSystemKey(TCSYS_6,true); //enable marker key + TelComEnableSystemKey(TCSYS_7,true); //enable marker key + TelComEnableSystemKey(TCSYS_8,true); //enable marker key + TelComEnableSystemKey(TCSYS_ENTER,true); //processing other keys is a waste of cpu + TelComEnableSystemKey(TCSYS_PRINTSCRN,true); + + // Classify faces + ClassifyAMFaces(); + + // Load ship model + AM_ship_model_handle=LoadPolyModel("automapship.oof",0); + + // Setup intial pos/orient + TCAMCenterOnPlayer(); + if (OBJECT_OUTSIDE(Viewer_object)) + { + AM_terrain=1; + } + + + TelcomStartScreen(0); + + //setup general background, plain color + TCBKGDESC backg; + backg.color = BACKGROUND_COLOR; + backg.caps = TCBGD_COLOR; + backg.type = TC_BACK_STATIC; + + /*//load up the "fake automap" <----Start Remove----> + TCBMPDESC bmpdesc; + bmpdesc.type = TC_BMP_STATIC; + bmpdesc.flags = 0; + bmpdesc.caps=TCBD_XY; + bmpdesc.x = 0; bmpdesc.y = 0; + strcpy(bmpdesc.filename,"TCMap.ogf"); + // <----End Remove ---->*/ + + //setup text for the top monitor "Automap" + TCTEXTDESC textdesc; + textdesc.type = TC_TEXT_STATIC; + textdesc.font = BBRIEF_FONT_INDEX; + textdesc.caps = TCTD_TEXTBOX|TCTD_COLOR|TCTD_FONT; + textdesc.textbox.left = 65; + textdesc.textbox.right = 380; + textdesc.textbox.top = 2; + textdesc.textbox.bottom = 50; + textdesc.color = GR_RGB(255,255,255); + + //Setup Main Monitor + //CreateBitmapEffect(&bmpdesc,MONITOR_MAIN,0); + CreateBackgroundEffect(&backg,MONITOR_MAIN,0);//<---- Replace above line with this line to get a background + + //Setup Location Monitor (small window at top) + CreateBackgroundEffect(&backg,MONITOR_TOP,0); + CreateTextEffect(&textdesc,TXT_TCAUTOMAP,MONITOR_TOP,0); + TelcomEndScreen(); + TelcomRenderSetScreen(0); + TelcomRenderSetCallback(TCAMCallback); + + ResumeControls(); // need to do because we're most likely coming from game. + + while(!done){ + ct_packet automap_key; + + Sound_system.BeginSoundFrame(Telcom_called_from_game); + + if(Telcom_system.state!=TCS_POWERON || tcs->current_status!=TS_MAP){ + //we're done with the loop + done = true; + } + + //Process all waiting events for the TelCom (we may only want to handle this once a frame!) + TelComHandleAllEvents(tcs); + + TelcomRenderScreen(); + Descent->defer(); + if(KEY_STATE(KEY_ESC)){ + AM_tcs->state = TCS_POWEROFF; + } +// if(KEY_STATE(KEY_TAB)){ +// AM_tcs->state = TCS_POWEROFF; +// } + + // read controls + Controller->get_packet(ctfAUTOMAP_KEY, &automap_key); + if (automap_key.value > 0.0f) { + AM_tcs->state = TCS_POWEROFF; + } + + Sound_system.EndSoundFrame(); + } + + SuspendControls(); + + DestroyAllScreens(); + TelcomRenderSetScreen(DUMMY_SCREEN); + TelcomRenderSetCallback(NULL); + + FreeAMFaces(); + + if (AM_ship_model_handle!=-1) + { + FreePolyModel(AM_ship_model_handle); + } + + TelComEnableSystemKey(TCSYS_MAXKEYS,true); //re-enable all telcom keys + TelComEnableSystemKey(TCSYS_F1,false); //disable the T key, not needed outside of automap + if(AM_rotated_points) + { + mem_free(AM_rotated_points); + AM_rotated_points = NULL; + } + return true; +} + +// Builds a list of rooms to draw for the automap +void TCAMBuildRoomList (int startroom) +{ + memset (AM_rooms_seen,0,MAX_ROOMS); + Num_AM_rooms=0; + + if (startroom>0) + { + AM_room_list[Num_AM_rooms++]=startroom; + AM_rooms_seen[startroom]=1; + } + + int i=0; + for (i=0;i<=Highest_room_index;i++) + { + if (Rooms[i].used && !AM_rooms_seen[i] && (AutomapVisMap[i]==1 || (Rooms[i].flags & RF_EXTERNAL))) + { + AM_room_list[Num_AM_rooms++]=i; + AM_rooms_seen[i]=1; + } + } + +} + +// Takes a min,max vector and makes a surrounding cube from it +void MakePointsFromMinMax (vector *corners,vector *minp,vector *maxp); +// Returns true if the external room is in the view cone +// Else returns false +bool IsRoomVisible (room *rp,float *nearz) +{ + g3Point pnt; + ubyte ccode; + + vector corners[8]; + + MakePointsFromMinMax (corners,&rp->min_xyz,&rp->max_xyz); + + *nearz=90000000; + ubyte andbyte=0xff; + for (int t=0;t<8;t++) + { + g3_RotatePoint(&pnt,&corners[t]); + + if (pnt.p3_z<*nearz) + *nearz=pnt.p3_z; + + ccode=g3_CodePoint (&pnt); + if (!ccode) + return true; + andbyte&=ccode; + } + + if (andbyte) + return false; + + return true; +} + + +void TCAMRenderRoom (int roomnum) +{ + ASSERT (Rooms[roomnum].used); + room *rp=&Rooms[roomnum]; + static int first=1; + static float lm_red[32],lm_green[32],lm_blue[32]; + float nearz; + int i; + + if (first) + { + first=0; + for (int i=0;i<32;i++) + { + lm_red[i]=((float)i/31.0); + lm_green[i]=((float)i/31.0); + lm_blue[i]=((float)i/31.0); + } + } + + if ((rp->flags & RF_EXTERNAL) && !AM_terrain) + return; + + if (!IsRoomVisible(rp,&nearz)) + return; + + // rotate all the points for this room + for (i=0;inum_verts;i++) + { + g3_RotatePoint (&AM_rotated_points[i],&rp->verts[i]); + g3_ProjectPoint (&AM_rotated_points[i]); + + } + + for (i=0;inum_faces;i++) + { + g3Point *pointlist[MAX_VERTS_PER_FACE]; + g3Point pointbuffer[MAX_VERTS_PER_FACE]; + + face *fp=&rp->faces[i]; + + // See if this face is backfaced + vector subvec=rp->verts[fp->face_verts[0]]-AM_view_pos; + if (subvec * fp->normal>0) + continue; // backfaced + + int black_face=0,wacky_face=0; + + if (fp->portal_num!=-1) + { + if (!AutomapVisMap[rp->portals[fp->portal_num].croom]) + { + // Don't give away secret rooms + if (Rooms[rp->portals[fp->portal_num].croom].flags & RF_SECRET) + black_face=1; + else + wacky_face=1; + } + else + continue; + } + + if (nearz>600) + { + if (Small_faces[roomnum][i]!=0) + continue; + } + else if (nearz>300) + { + if (Small_faces[roomnum][i]==2) + continue; + } + + // Color this face from the lightmaps + if (!black_face && !wacky_face && (fp->flags & FF_LIGHTMAP)) + { + int lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + ushort *data=(ushort *)lm_data(lm_handle); + int w=lm_w(lm_handle); + int h=lm_h(lm_handle); + + for (int t=0;tnum_verts;t++) + { + pointbuffer[t]=AM_rotated_points[fp->face_verts[t]]; + g3Point *p=&pointbuffer[t]; + pointlist[t]=p; + + float u=fp->face_uvls[t].u2*w; + float v=fp->face_uvls[t].v2*h; + + int int_u=u; + int int_v=v; + + ushort texel=data[int_v*w+int_u]; + + int r=(texel>>10) & 0x1f; + int g=(texel>>5) & 0x1f; + int b=(texel) & 0x1f; + + p->p3_r=lm_red[r]; + p->p3_g=lm_green[g]; + p->p3_b=lm_blue[b]; + p->p3_a=1.0; + p->p3_flags |= PF_RGBA; + } + + g3_DrawPoly(fp->num_verts,pointlist,0); + } + else + { + for (int t=0;tnum_verts;t++) + { + pointbuffer[t]=AM_rotated_points[fp->face_verts[t]]; + g3Point *p=&pointbuffer[t]; + pointlist[t]=p; + + p->p3_flags |= PF_RGBA; + p->p3_r=0.0; + p->p3_g=0.0; + p->p3_b=0.0; + p->p3_a=1.0; + + if (wacky_face) + { + float curtime=timer_GetTime(); + int val=curtime; + float fval=curtime-val; + p->p3_r=fval; + p->p3_g=fval; + p->p3_b=fval; + p->p3_a=1.0; + } + + } + g3_DrawPoly(fp->num_verts,pointlist,0); + } + } +} + +#define AM_TRANSLATION_SCALAR 200.0f +#define AM_ROTATIONAL_SCALAR 90 + +// Reads the controls for the automap, and adjusts viewpoint accordingly +extern float last_frametime; +void TCAMReadControls() +{ + Frametime=last_frametime; + int save_mode=Game_interface_mode; + game_controls controls; + + controls.toggle_slide=0; + controls.toggle_bank=0; + Game_interface_mode=GAME_INTERFACE; + + PollControls(); + DoMovement (&controls); + + // Do translational movement + AM_view_pos+=(controls.forward_thrust*last_frametime*AM_TRANSLATION_SCALAR)*AM_view_orient.fvec; + AM_view_pos+=(controls.vertical_thrust*last_frametime*AM_TRANSLATION_SCALAR)*AM_view_orient.uvec; + AM_view_pos+=(controls.sideways_thrust*last_frametime*AM_TRANSLATION_SCALAR)*AM_view_orient.rvec; + + // Do rotational movement + + matrix newmat; + float norm=((65536/360)*AM_ROTATIONAL_SCALAR)*last_frametime; + + AM_pitch+=(norm*controls.pitch_thrust); + AM_heading+=(norm*controls.heading_thrust); + + if (AM_pitch<0) + AM_pitch+=65536; + if (AM_pitch>=65536) + AM_pitch-=65536; + + if (AM_heading<0) + AM_heading+=65536; + if (AM_heading>=65536) + AM_heading-=65536; + + vm_AnglesToMatrix (&newmat,(ushort)AM_pitch,(ushort)AM_heading,0); + AM_view_orient=newmat; + + vm_Orthogonalize(&AM_view_orient); + Game_interface_mode=save_mode; +} + +// Renders the low-res terrain for the automap +void TCAMRenderTerrain() +{ + int nt; + int render_width,render_height; + float w2,h2; + + int save_check=Terrain_occlusion_checksum; + int save_pix=Detail_settings.Pixel_error; + float save_dist=Detail_settings.Terrain_render_distance; + + Detail_settings.Pixel_error=31; + Detail_settings.Terrain_render_distance=300*TERRAIN_SIZE; + Terrain_occlusion_checksum=-1; + + //Get the size of the current render window + rend_GetProjectionParameters(&render_width,&render_height); + w2=((float) render_width-1)/2.0; + h2=((float) render_height-1)/2.0; + + //Set up vars for (psuedo-)clipping window + Check_terrain_portal=0; + + Clip_scale_left = 0; + Clip_scale_right = render_width-1; + Clip_scale_top = 0; + Clip_scale_bot = render_height-1; + + Terrain_from_mine=0; + + VisibleTerrainZ=(Detail_settings.Terrain_render_distance)*Matrix_scale.z; + + // Set up our z wall + g3_SetFarClipZ (VisibleTerrainZ); + + vector viewer_eye; + matrix viewer_orient; + // Get scaled matrix + g3_GetViewPosition(&viewer_eye); + g3_GetViewMatrix(&viewer_orient); + + // Get all of the cells visible to us + nt=GetVisibleTerrain(&viewer_eye,&viewer_orient); + + // And display! + if (nt>0) + DisplayTerrainList (nt,1); + + Detail_settings.Pixel_error=save_pix; + Detail_settings.Terrain_render_distance=save_dist; + Terrain_occlusion_checksum=save_check; + +} + +#define AM_BLINK_TIME .25f + +// Draws all the relevant objects for automap +void TCAMDrawObjects() +{ + int i; + int drawit=(timer_GetTime()*4); + drawit=(drawit % 2); + + if (Game_mode & GM_MULTI) + { + for (i=0;ipos,&obj->orient,AM_ship_model_handle,NULL,0,1,1,1); + } + else if (NetPlayers[i].flags & NPF_CONNECTED) + { + object *obj=&Objects[Players[i].objnum]; + if ((obj->type==OBJ_PLAYER && obj->render_type==RT_POLYOBJ) || obj->type==OBJ_OBSERVER) + DrawColoredDisk (&Objects[Players[i].objnum].pos,1,1,.5f,1.0,.3f,3.0); + } + } + } + else + { + object *obj=&Objects[Players[Player_num].objnum]; + + if (AM_ship_model_handle!=-1 && drawit) + DrawPolygonModel (&obj->pos,&obj->orient,AM_ship_model_handle,NULL,0,1,1,1); + } + + + float norm=timer_GetTime(); + int int_norm=norm; + norm-=int_norm; + for (i=0;i<=Highest_object_index;i++) + { + object *obj=&Objects[i]; + + if (obj->type==OBJ_MARKER && obj->parent_handle==Player_object->handle) + { + float size=2.5+(norm*2.5); + float r=1.0; + float g=0; + float b=1.0; + + if (AM_current_marker!=-1) + { + int index=0; + + if (Game_mode & GM_MULTI) + index=obj->id-(Player_num*2); + else + index=obj->id; + + if (AM_current_marker==index) + { + size=9.0+(norm*9.0); + r=0; + g=1.0; + b=1.0; + + } + } + + DrawColoredDisk (&obj->pos,r,g,b,1.0,.3f,size); + } + } + +} + +#include "hud.h" + +void TCAMDrawMenu () +{ + char str[255]; + g3Point *pntlist[4],points[4]; + grtext_SetFont(BRIEFING_FONT); + int fontheight=grfont_GetHeight(BRIEFING_FONT); + + points[0].p3_sx=AM_tcs->Monitor_coords[MONITOR_MAIN].left; + points[0].p3_sy=AM_tcs->Monitor_coords[MONITOR_MAIN].bottom-(fontheight*3); + + points[1].p3_sx=AM_tcs->Monitor_coords[MONITOR_MAIN].right; + points[1].p3_sy=AM_tcs->Monitor_coords[MONITOR_MAIN].bottom-(fontheight*3); + + points[2].p3_sx=AM_tcs->Monitor_coords[MONITOR_MAIN].right; + points[2].p3_sy=AM_tcs->Monitor_coords[MONITOR_MAIN].bottom; + + points[3].p3_sx=AM_tcs->Monitor_coords[MONITOR_MAIN].left; + points[3].p3_sy=AM_tcs->Monitor_coords[MONITOR_MAIN].bottom; + + for (int i=0;i<4;i++) + { + points[i].p3_z=0; + points[i].p3_flags=PF_PROJECTED; + pntlist[i]=&points[i]; + } + + rend_SetZBufferState(0); + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetLighting (LS_NONE); + rend_SetFlatColor (GR_BLACK); + rend_SetAlphaValue(192); + rend_DrawPolygon2D( 0, pntlist, 4 ); + + grtext_SetAlpha(250); + grtext_SetColor(GR_RGB(255,255,255)); + + sprintf (str,TXT_TCAM_TERRAIN,AM_terrain?TXT_STRING_OFF:TXT_STRING_ON); + grtext_CenteredPrintf(0, AM_tcs->Monitor_coords[MONITOR_MAIN].bottom-(fontheight*3)+(fontheight/2), str); + sprintf (str,TXT_TCAM_INSTRUCTIONS); + grtext_CenteredPrintf(0, AM_tcs->Monitor_coords[MONITOR_MAIN].bottom-(fontheight*2)+(fontheight/2), str); + + //Use HUD font for part that overlays the 3D + fontheight=grfont_GetHeight(HUD_FONT); + grtext_SetFont(HUD_FONT); + grtext_SetColor(HUD_COLOR); + + sprintf (str,"%s",Level_info.name); + grtext_CenteredPrintf(0, AM_tcs->Monitor_coords[MONITOR_MAIN].top+(fontheight/2), str); + + if (AM_current_marker!=-1) + { + int markernum = (Game_mode & GM_MULTI) ? (Player_num*2+AM_current_marker) : AM_current_marker; + sprintf(str,TXT_VIEW_MARKER,AM_current_marker+1); + strcat(str,": "); + strcat(str,MarkerMessages[markernum]); + grtext_CenteredPrintf(0, AM_tcs->Monitor_coords[MONITOR_MAIN].top+(fontheight/2)+fontheight, str); + } + + + grtext_Flush(); +} + +// The callback function that the telcom calls every frame +void TCAMCallback(void) +{ + StartFrame(Telcom_system.Monitor_coords[MONITOR_MAIN].left,Telcom_system.Monitor_coords[MONITOR_MAIN].top, + Telcom_system.Monitor_coords[MONITOR_MAIN].right,Telcom_system.Monitor_coords[MONITOR_MAIN].bottom); + + // Start a 3d frame + g3_StartFrame (&AM_view_pos,&AM_view_orient,Render_zoom); + + if (AM_center_on_player) + { + TCAMCenterOnPlayer(); + AM_center_on_player=0; + } + + if (AM_realign) + { + AM_pitch=0; + AM_realign=0; + } + + rend_SetFiltering(1); + rend_SetAlphaType (AT_VERTEX); + rend_SetTextureType (TT_FLAT); + rend_SetLighting(LS_GOURAUD); + rend_SetColorModel (CM_RGB); + rend_SetOverlayType (OT_NONE); + rend_SetZBufferState (1); + + if (AM_terrain) + rend_SetZValues(0,VisibleTerrainZ); + + TCAMBuildRoomList (Viewer_object->roomnum); + // Render all rooms in view + for (int i=0;i +#include +#include + +#include "CFILE.H" +#include "pserror.h" +#include "ddio.h" +#include "bitmap.h" + +#include "TelCom.h" +#include "renderer.h" +#include "game.h" +#include "mem.h" +#include "stringtable.h" +#include "Inventory.h" +#include "player.h" +#include "gametexture.h" +#include "textaux.h" +#include "TelComEfxStructs.h" +#include "TelComEffects.h" +#include "weapon.h" +#include "hlsoundlib.h" + +#define TCBACK_BUTTON_X 500 +#define TCBACK_BUTTON_Y 350 +#define TCFORW_BUTTON_X 532 +#define TCFORW_BUTTON_Y 350 + +#define LIT_TITLE 0 +#define LIT_VALUE 1 + +#define LID_NONE -1 +#define LID_SHIELDS 0 +#define LID_ENERGY 1 +#define LID_PRIMARIES 2 +#define LID_SECONDARIES 3 +#define LID_COUNTERMEASURES 4 +#define LID_INVENTORY 5 + +#ifdef __LINUX__ +#define max(a,b) ((a>b)?a:b) +#endif + +typedef struct{ + ubyte type; + char *name; + int id; +}tLineInfo; + +tLineInfo StatusLines[] = { + {LIT_TITLE,"Ship Status", LID_NONE}, + {LIT_VALUE,"Shields", LID_SHIELDS}, + {LIT_VALUE,"Energy", LID_ENERGY}, + {LIT_TITLE,"Primaries", LID_PRIMARIES}, + {LIT_TITLE,"Secondaries", LID_SECONDARIES}, + {LIT_TITLE,"Counter Measures", LID_COUNTERMEASURES}, + {LIT_TITLE,"Inventory", LID_INVENTORY} +}; + +#define NUM_LINES (sizeof(StatusLines)/sizeof(tLineInfo)) +#define TITLE_X 30+TCminx +#define VALUE_X 400+TCminx +int TCminx,TCmaxx,TCminy,TCmaxy; +#define SM_FONT BRIEFING_FONT +#define BG_FONT BIG_BRIEFING_FONT + +// Returns true if the player has this weapon (this fuction should be moved) +bool TCCPlayerHasWeapon (int weapon_index) +{ + if (Players[Player_num].weapon_flags & HAS_FLAG(weapon_index)) + return true; + return false; +} + + +int TCCargoCreateLine(int id,int y,char *title,int type) +{ + //char buffer[100]; + int small_height = grfont_GetHeight(SM_FONT); + int big_height = grfont_GetHeight(BG_FONT); + + switch(id){ + case LID_NONE: + return y; + + case LID_SHIELDS: + { + grtext_SetFont(SM_FONT); + + float shields = Player_object->shields; + shields = max(shields,0); + int perc = (int) ((shields/INITIAL_SHIELDS)*100.0f); + grtext_Printf(TITLE_X,y,title); + grtext_Printf(VALUE_X,y,"%d%c",perc,'%'); y+=small_height; + + }break; + + case LID_ENERGY: + { + grtext_SetFont(SM_FONT); + + float energy = Players[Player_num].energy; + energy = max(energy,0); + int perc = (int) ((energy/INITIAL_ENERGY)*100.0f); + grtext_Printf(TITLE_X,y,title); + grtext_Printf(VALUE_X,y,"%d%c",perc,'%'); y+=small_height; + + }break; + + case LID_PRIMARIES: + { + grtext_SetFont(SM_FONT); + int prim_start = PRIMARY_INDEX; + int prim_end = (PRIMARY_INDEX= 0 ); + ASSERT( prim_end <= MAX_STATIC_WEAPONS ); + ASSERT( prim_start < prim_end ); + + for( weap_index = prim_start; weap_index= 0 ); + ASSERT( sec_end <= MAX_STATIC_WEAPONS ); + ASSERT( sec_start < sec_end ); + + for( weap_index = sec_start; weap_indexMonitor_coords[MONITOR_MAIN].left + 20; + TCminy = tcs->Monitor_coords[MONITOR_MAIN].top +10; + TCmaxy = tcs->Monitor_coords[MONITOR_MAIN].bottom - 10; + TCmaxx = tcs->Monitor_coords[MONITOR_MAIN].right - 20; + + TCTEXTDESC textdesc; + textdesc.type = TC_TEXT_STATIC; + textdesc.font = BBRIEF_FONT_INDEX; + textdesc.caps = TCTD_TEXTBOX|TCTD_COLOR|TCTD_FONT; + textdesc.textbox.left = 75; + textdesc.textbox.right = 380; + textdesc.textbox.top = 2; + textdesc.textbox.bottom = 50; + textdesc.color = GR_RGB(255,255,255); + + CreateTextEffect(&textdesc,TXT_TCCARGO,MONITOR_TOP,0); + TelcomEndScreen(); + TelcomRenderSetScreen(0); + + TelcomRenderSetCallback(TCCargoRenderCallback); + + while(!done){ + Sound_system.BeginSoundFrame(Telcom_called_from_game); + + if(tcs->state!=TCS_POWERON || tcs->current_status!=TS_CARGO){ + //we're done with the loop + done = true; + } + + /* + if(tccargo_button_pressed!=-1){ + if(tccargo_button_pressed==efxmap[0]) + curr_cargo_page = &Regular; + else + if(tccargo_button_pressed==efxmap[1]) + curr_cargo_page = &CounterMeasures; + tccargo_button_pressed = -1; + } + */ + + //Process all waiting events for the TelCom (we may only want to handle this once a frame!) + TelComHandleAllEvents(&Telcom_system); + + TelcomRenderScreen(); + Descent->defer(); + if(KEY_STATE(KEY_ESC)) + Telcom_system.state = TCS_POWEROFF; + + Sound_system.EndSoundFrame(); + } + + DestroyAllScreens(); + TelcomRenderSetScreen(DUMMY_SCREEN); + TelcomRenderSetCallback(NULL); + + return true; +} diff --git a/Descent3/TelComCargo.h b/Descent3/TelComCargo.h new file mode 100644 index 000000000..35bd1e89b --- /dev/null +++ b/Descent3/TelComCargo.h @@ -0,0 +1,33 @@ +/* +* $Logfile: /DescentIII/main/TelComCargo.h $ +* $Revision: 4 $ +* $Date: 4/14/99 3:57a $ +* $Author: Jeff $ +* +* TelCom Cargo Header +* +* $Log: /DescentIII/main/TelComCargo.h $ + * + * 4 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 3 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 2 7/08/98 8:06p Jeff + * Initial creation of TelComCargo +* +* $NoKeywords: $ +*/ + + +#ifndef __TCCARGO_H_ +#define __TCCARGO_H_ +#include "TelCom.h" + +// This is the function called by TelCom +// return true if TelCom should exit to TelCom Main Menu +// return false if you should exit out of TelCom completly +bool TelComCargo(tTelComInfo *tcs); + +#endif \ No newline at end of file diff --git a/Descent3/TelComEffects.cpp b/Descent3/TelComEffects.cpp new file mode 100644 index 000000000..c4ebc58dc --- /dev/null +++ b/Descent3/TelComEffects.cpp @@ -0,0 +1,1258 @@ +/* +* $Logfile: /DescentIII/Main/TelComEffects.cpp $ +* $Revision: 33 $ +* $Date: 5/13/99 3:42p $ +* $Author: Ardussi $ +* +* TelCom Effect class defines +* +* $Log: /DescentIII/Main/TelComEffects.cpp $ + * + * 33 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 32 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 31 2/17/99 6:55p Jeff + * added jump button type. Added no early render flag for bitmaps. Fixed + * color bug for type text + * + * 30 2/16/99 11:54a Jeff + * fixed bug with 0 length strings + * + * 29 2/05/99 7:04p Jeff + * table file parsing macros put in + * + * 28 2/03/99 1:23p Jeff + * more updates to single player ship selection...all thats left is stats + * + * 27 2/03/99 12:14a Jeff + * updated single player ship selection to full functional...needs effects + * added in. Had to add flag to buttons for gain focus to register as a + * click + * + * 26 2/02/99 7:32p Jeff + * begining of single player ship selection + * + * 25 11/01/98 1:57a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 24 10/12/98 11:39p Jeff + * finished up new focus system of telcom + * + * 23 10/12/98 8:32p Jeff + * changed the way focus is handled + * + * 22 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 21 9/17/98 2:29p Jeff + * added focus filenames to button effect + * + * 20 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 19 8/19/98 1:38p Jeff + * removed update2dsound + * + * 18 6/15/98 4:00p Jason + * replaced monochromatic polymodel lighting with rgb lighting + * + * 17 5/19/98 3:40p Jeff + * poly models functional + * + * 16 5/15/98 5:16p Jeff + * use regular draw chunk bitmap + * + * 15 5/11/98 6:21p Jeff + * fixed setting glitch offsets + * + * 14 5/07/98 3:31p Jeff + * various optimizations, removed Copy and similar functions + * + * 13 5/05/98 6:50p Jeff + * Telcom doesn't use rend_DrawLFBitmap anymore...more speed! + * + * 12 5/04/98 6:22p Jeff + * fixed possible sound bugs + * + * 11 5/04/98 5:29p Jeff + * Added sounds to TelCom events + * + * 10 5/04/98 1:35p Jeff + * Changes made for mouse handling + * + * 9 5/04/98 11:01a Jeff + * added scroll parameter to text + * + * 8 5/03/98 7:57p Jeff + * Got scrolling textboxes working + * + * 7 5/01/98 2:17p Jeff + * Added sound effect support + * + * 6 4/30/98 7:18p Jeff + * fixed poly model rotation + * + * 5 4/29/98 4:36p Jeff + * fixed text going beyond bottom of text box + * + * 4 4/28/98 6:58p Jeff + * Added new poly model effect driver + * + * 3 4/26/98 7:20p Jeff + * Added the rest of the effect drivers except for scrolling text. Added + * invert bitmaps and stretch bitmaps + * + * 2 4/26/98 2:54a Jeff + * Initial Creation + * + * 1 4/25/98 4:23p Jeff +* +* $NoKeywords: $ +*/ + + +#include "TelComEffects.h" +#include "pstring.h" +#include "mem.h" +#include "vecmat.h" +#include +#include "textaux.h" +#include "hlsoundlib.h" +#include "soundload.h" + +#ifndef MACINTOSH +#define IGNORE_TABLE(x) x +#endif + +tceffect TCEffects[MAX_TCEFFECTS]; +int Screen_roots[MAX_TELCOM_SCREENS]; +bool Screen_has_effect_with_focus[MAX_TELCOM_SCREENS]; + +// Initializes the effects +void EfxInit(void) +{ + int i; + + for ( i = 0; i < MAX_TCEFFECTS; i++ ){ + tceffect *tce = &TCEffects[i]; + tce->type = EFX_NONE; + tce->monitor = -1; + tce->screen = -1; + tce->id = INVALID_EFFECT_ID; + tce->prev = -1; + tce->next = -1; + tce->text_buffer = NULL; + tce->tab_stop = false; + tce->has_focus = false; + for(int j=0;j < MAX_EFFECT_EVENTS;j++) + tce->event_queue[j].id = -1; + } + + for ( i = 0; i < MAX_TELCOM_SCREENS; i++ ){ + Screen_has_effect_with_focus[i] = false; + Screen_roots[i] = -1; + } +} + +// Shutdown of effects +void EfxClose(void) +{ + for ( int i = 0; i < MAX_TCEFFECTS; i++ ){ + if(TCEffects[i].type!=EFX_NONE){ + tceffect *tce = &TCEffects[i]; + EfxFreeEffect(tce); + } + } +} + +// Destroys all the screens +void EfxDestroyAllScreens(void) +{ + for ( int i = 0; i < MAX_TCEFFECTS; i++ ){ + if(TCEffects[i].type!=EFX_NONE){ + tceffect *tce = &TCEffects[i]; + EfxFreeEffect(tce); + } + } + EfxInit(); +} + +// Destroys the screens within a monitor +void EfxDestroyScreen(int screen) +{ + ASSERT( screen>=0 && screentype = EFX_NONE; + tce->monitor = -1; + tce->screen = -1; + tce->prev = tce->next = -1; + tce->id = INVALID_EFFECT_ID; + tce->text_buffer = NULL; + for(int j=0;jevent_queue[j].id = -1; + } + Screen_roots[screen] = -1; + Screen_has_effect_with_focus[screen] = false; +} + +// Creates an instance of an Effect +int EfxCreate(int type,int monitor,int screen,int id,bool is_tab_stop) +{ + ASSERT( type!=EFX_NONE ); + ASSERT( monitor>=0 && monitor=0 && screenprev; + next = tce->next; + + if(prev == -1){ + //removing a root + if(next!=-1){ + Screen_roots[tce->screen] = next; + }else{ + Screen_roots[tce->screen] = -1; + } + }else{ + //removing a middle node + TCEffects[prev].next = next; + if(next!=-1) + TCEffects[next].prev = prev; + } + tce->prev = tce->next = -1; + + if(tce->text_buffer){ + mem_free(tce->text_buffer); + tce->text_buffer = NULL; + } + + if(tce->has_focus){ + //disable the tab stop + tce->tab_stop = false; + + //uh oh, we're removing the item on the screen that has focus + Screen_has_effect_with_focus[tce->screen] = false; + + //see if we can find another item and give it focus + int node = Screen_roots[tce->screen]; + while(node!=-1){ + if(TCEffects[node].tab_stop){ + //ok we found some one with a tab stop, give em focus + TCEffects[node].has_focus = true; + Screen_has_effect_with_focus[tce->screen] = true; + break; + } + node = TCEffects[node].next; + } + } + + int i; + + switch(tce->type) + { + case EFX_TEXT_TYPE: + TelcomStopSound(TCSND_TYPING); + break; + case EFX_TEXT_STATIC: + case EFX_TEXT_FADE: + break; + case EFX_BMP_STATIC: + case EFX_BMP_BLUR: + case EFX_BMP_SCANLINE: + case EFX_BMP_INVERT: + case EFX_BMP_STRETCH: + if(tce->bmpinfo.bm_handle>BAD_BITMAP_HANDLE) + bm_FreeBitmap(tce->bmpinfo.bm_handle); + tce->bmpinfo.bm_handle = -1; + + if(tce->bmpinfo.temp_bmhandle>BAD_BITMAP_HANDLE) + bm_FreeBitmap(tce->bmpinfo.temp_bmhandle); + tce->bmpinfo.temp_bmhandle = -1; + + if(tce->bmpinfo.bitmaps){ + for(i=0;ibmpinfo.bm_count;i++){ + if(tce->bmpinfo.bitmaps[i]>BAD_BITMAP_HANDLE) + bm_FreeBitmap(tce->bmpinfo.bitmaps[i]); + } + mem_free(tce->bmpinfo.bitmaps); + tce->bmpinfo.bitmaps = NULL; + } + + if(tce->bmpinfo.chunk_bmp.pw!=0 || tce->bmpinfo.chunk_bmp.ph!=0){ + bm_DestroyChunkedBitmap(&tce->bmpinfo.chunk_bmp); + tce->bmpinfo.chunk_bmp.pw = tce->bmpinfo.chunk_bmp.ph = 0; + } + break; + case EFX_BACKGROUND: + break; + case EFX_MOVIE: + if(tce->movieinfo.handle){ + EndMovie(tce->movieinfo.handle); + tce->movieinfo.handle = NULL; + } + + if(tce->movieinfo.filename){ + mem_free(tce->movieinfo.filename); + tce->movieinfo.filename = NULL; + } + break; + case EFX_POLYMODEL: + break; + case EFX_SOUND: + break; + case EFX_BUTTON: + if(tce->buttoninfo.bm_handle>BAD_BITMAP_HANDLE) + bm_FreeBitmap(tce->buttoninfo.bm_handle); + tce->buttoninfo.bm_handle = -1; + + if(tce->buttoninfo.bmfocus_handle>BAD_BITMAP_HANDLE) + bm_FreeBitmap(tce->buttoninfo.bmfocus_handle); + tce->buttoninfo.bmfocus_handle = -1; + + if(tce->buttoninfo.chunk_bmp.pw!=0 || tce->buttoninfo.chunk_bmp.ph!=0){ + bm_DestroyChunkedBitmap(&tce->buttoninfo.chunk_bmp); + tce->buttoninfo.chunk_bmp.pw = tce->buttoninfo.chunk_bmp.ph = 0; + } + + if(tce->buttoninfo.chunkfocus_bmp.pw!=0 || tce->buttoninfo.chunkfocus_bmp.ph!=0){ + bm_DestroyChunkedBitmap(&tce->buttoninfo.chunkfocus_bmp); + tce->buttoninfo.chunkfocus_bmp.pw = tce->buttoninfo.chunkfocus_bmp.ph = 0; + } + + if(tce->buttoninfo.flash_handle>BAD_BITMAP_HANDLE) + bm_FreeBitmap(tce->buttoninfo.flash_handle); + tce->buttoninfo.flash_handle = -1; + + if(tce->buttoninfo.flashfocus_handle>BAD_BITMAP_HANDLE) + bm_FreeBitmap(tce->buttoninfo.flashfocus_handle); + tce->buttoninfo.flashfocus_handle = -1; + + if(tce->buttoninfo.flash_chunk.pw!=0 || tce->buttoninfo.flash_chunk.ph!=0){ + bm_DestroyChunkedBitmap(&tce->buttoninfo.flash_chunk); + tce->buttoninfo.flash_chunk.pw = tce->buttoninfo.flash_chunk.ph = 0; + } + + if(tce->buttoninfo.flashfocus_chunk.pw!=0 || tce->buttoninfo.flashfocus_chunk.ph!=0){ + bm_DestroyChunkedBitmap(&tce->buttoninfo.flashfocus_chunk); + tce->buttoninfo.flashfocus_chunk.pw = tce->buttoninfo.flashfocus_chunk.ph = 0; + } + + break; + default: + Int3(); //invalid type + break; + } + tce->type = EFX_NONE; + tce->monitor = -1; +} + +// Initializes an effect based on it's type +void EfxInit(tceffect *tce,bool tab_stop,bool gets_focus) +{ + + ASSERT(tce); + ASSERT(tce->type!=EFX_NONE); + ASSERT(tce->monitor>=0 && tce->monitorscreen>=0 && tce->screen=TCEffects ); + + //Link effect in + int root = Screen_roots[tce->screen]; + int effect_num = (tce - TCEffects); + tce->tab_stop = tab_stop; + tce->has_focus = (tab_stop)?gets_focus:false; + if(tce->has_focus){ + //the screen has a definite focus, so set the global + Screen_has_effect_with_focus[tce->screen] = true; + } + + if(root==-1){ + //no roots for this monitor, add it + Screen_roots[tce->screen] = effect_num; + tce->prev = -1; + tce->next = -1; + }else{ + //traverse until we get to the end, then add it + int curr,next = -1; + bool done = false; + + curr = root; + + if(tce->type==EFX_BUTTON){ + //go to the end of the list + while(!done){ + if(TCEffects[curr].next==-1){ + done = true; + }else{ + curr = TCEffects[curr].next; + } + } + TCEffects[curr].next = effect_num; + TCEffects[effect_num].prev = curr; + TCEffects[effect_num].next = -1; + }else{ + //go to where the buttons start + if(TCEffects[root].type==EFX_BUTTON){ + //replace the root + Screen_roots[tce->screen] = effect_num; + TCEffects[effect_num].prev = -1; + TCEffects[effect_num].next = root; + TCEffects[root].prev = effect_num; + }else{ + while(!done){ + if(TCEffects[curr].next==-1 || TCEffects[TCEffects[curr].next].type==EFX_BUTTON){ + next = TCEffects[curr].next; + done = true; + }else{ + curr = TCEffects[curr].next; + } + } + TCEffects[curr].next = effect_num; + TCEffects[effect_num].prev = curr; + TCEffects[effect_num].next = next; + } + }//end else + }//end else + + tce->pos_x = tce->pos_y = 0; + tce->text_buffer = NULL; + tce->age = 0; + tce->flags = 0; + tce->color = GR_GREEN; + tce->w = tce->h = 0; + tce->speed = 1.0f; + tce->start_time = 0; + + //clear the event queue + for(int j=0;j < MAX_EFFECT_EVENTS;j++) + tce->event_queue[j].id = -1; + + switch(tce->type){ + case EFX_TEXT_STATIC: + case EFX_TEXT_TYPE: + case EFX_TEXT_FADE: + tce->textinfo.last_letter = 0; + tce->textinfo.sound_index = -1; + tce->textinfo.font_index = BRIEF_FONT_INDEX;; + tce->textinfo.line_index = 0; + tce->textinfo.scroll_u=tce->textinfo.scroll_d = false; + tce->textinfo.scroll = false; + break; + case EFX_BMP_STATIC: + case EFX_BMP_BLUR: + case EFX_BMP_SCANLINE: + case EFX_BMP_INVERT: + case EFX_BMP_STRETCH: + tce->bmpinfo.temp_bmhandle = -1; + tce->bmpinfo.stage = 0; + tce->bmpinfo.bitmaps = NULL; + tce->bmpinfo.bm_count = 0; + tce->bmpinfo.bm_handle = -1; + tce->bmpinfo.chunk_bmp.pw = tce->bmpinfo.chunk_bmp.ph = 0; + break; + case EFX_BACKGROUND: + break; + case EFX_MOVIE: + tce->movieinfo.handle = NULL; + tce->movieinfo.filename = NULL; + tce->movieinfo.fps = 20.0f; + break; + case EFX_POLYMODEL: + vm_MakeZero(&tce->polyinfo.m_Rot); + vm_MakeZero(&tce->polyinfo.m_Pos); + vm_MakeZero(&tce->polyinfo.m_Ori); + vm_MakeIdentity(&tce->polyinfo.m_mOrient); + tce->polyinfo.handle = -1; + break; + case EFX_SOUND: + tce->soundinfo.started = false; + tce->soundinfo.handle = -1; + break; + case EFX_BUTTON: + tce->buttoninfo.click_type = CLICKTYPE_CLICKUP; + tce->buttoninfo.button_type = BUTT_NEXTPAGE; + tce->buttoninfo.sibling = -1; + tce->buttoninfo.parent = -1; + tce->buttoninfo.internal = NULL; + tce->buttoninfo.jump_page = 0; + break; + default: + Int3(); //Invalid Effect + } +} + + +// Renders a Monitor +void RenderScreen(int screen,tTelComInfo *tsys,float Frametime) +{ + ASSERT( screen>=-1 && screenMonitor_coords[tce->monitor].left; + y = tsys->Monitor_coords[tce->monitor].top; + + if(tce->start_time<=0){ + RenderEffect(tce,Frametime,x,y,true); + count++; + }else{ + RenderEffect(tce,Frametime,x,y,false); + tce->start_time -= Frametime; + } + + node = TCEffects[node].next; + } + //mprintf((0,"Effect Count: %d effects\n",count)); +} + +// Renders an effect +void RenderEffect(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + ASSERT(tce->type!=EFX_NONE); + + switch(tce->type){ + case EFX_TEXT_STATIC: RenderTextStatic(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_TEXT_TYPE: RenderTextType(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_TEXT_FADE: RenderTextFade(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_BMP_STATIC: RenderBmpStatic(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_BMP_BLUR: RenderBmpBlur(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_BMP_SCANLINE: RenderBmpScanline(tce,frametime,xoff,yoff,ok_to_render);break; + case EFX_BMP_INVERT: RenderBmpInvert(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_BMP_STRETCH: RenderBmpStretch(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_BACKGROUND: RenderBackground(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_MOVIE: RenderMovie(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_POLYMODEL: RenderPolyModel(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_SOUND: RenderSound(tce,frametime,xoff,yoff,ok_to_render); break; + case EFX_BUTTON: RenderButton(tce,frametime,xoff,yoff,ok_to_render); break; + default: + Int3(); + }; + +} + +// Sends an event to an effect +void SendEventToEffect(int effect,int event,int parm1,int parm2) +{ + ASSERT( effect>=0 && effect=0 ); + + tceffect *tce = &TCEffects[effect]; + + for(int j=0;jevent_queue[j].id==-1){ + tce->event_queue[j].id = event; + tce->event_queue[j].parms[0] = parm1; + tce->event_queue[j].parms[1] = parm2; + return; + } + } + + //if we got here than the event queue is filled up + mprintf((0,"Warning: Event queue filled up for effect %d\n",effect)); +} + +// Pops off an event for processesing, returns -1 if no event available +bool PopEvent(tceffect *tce,tTCEvent *evt) +{ + ASSERT(tce); + int ret = tce->event_queue[0].id; + + if(ret==-1) + return false; + + memcpy(evt,&tce->event_queue[0],sizeof(tTCEvent)); + + //slide everything up (if it isn't -1) + for(int j=1;j < MAX_EFFECT_EVENTS;j++){ + memcpy(&tce->event_queue[j-1],&tce->event_queue[j],sizeof(tTCEvent)); + } + tce->event_queue[MAX_EFFECT_EVENTS-1].id = -1; + return true; +} + +// Returns the efxnum given an id & screen, -1 if not found +int GetEfxNumFromID(int id,int screen) +{ + ASSERT( screen>=0 && screen=0 && screenpos_x+glitch_dx<=x) && (x<=tce->pos_x+tce->w+glitch_dx) && + (tce->pos_y+glitch_dy<=y) && (y<=tce->pos_y+tce->h+glitch_dy) ){ + //we got a winner + return node; + } + } + node = TCEffects[node].next; + } + return -1; +} + +///////////////////////////////////////////////////////////////////////// +char *format(char *format,...) +{ + static char tempbuffer[8192]; + + va_list ap; + va_start(ap,format); + Pvsprintf(tempbuffer,8192,format,ap); + va_end(ap); + return tempbuffer; +} + +bool CreateTextEffect(LPTCTEXTDESC desc,char *text,int monitor,int screen,int id) +{ + ASSERT(desc); + ASSERT(text); + + int efxtype = -1; + switch(desc->type){ + case TC_TEXT_STATIC: efxtype = EFX_TEXT_STATIC; break; + case TC_TEXT_FADE: efxtype = EFX_TEXT_FADE; break; + case TC_TEXT_FLASH: efxtype = EFX_TEXT_TYPE; break; + default: + Int3(); //not supported + } + if(efxtype==-1) + return false; + + // Creates an instance of the Effect + int efxnum = EfxCreate(efxtype,monitor,screen,id,(desc->caps&TCTD_TABSTOP)?true:false); + if(efxnum==-1) + return false; + tceffect *tce = &TCEffects[efxnum]; + + if(desc->caps&TCTD_FONT) + tce->textinfo.font_index = desc->font; + if(desc->caps&TCTD_COLOR) + tce->color = desc->color; + if(desc->caps&TCTD_SPEED) + tce->speed = desc->speed; + else + tce->speed = 1.0f; + if(desc->caps&TCTD_TEXTBOX){ + tce->pos_x = desc->textbox.left; + tce->pos_y = desc->textbox.top; + tce->w = desc->textbox.right - desc->textbox.left; + tce->h = desc->textbox.bottom - desc->textbox.top; + }else + Int3(); //need a text box + + if(desc->caps&TCTD_SCROLL) + tce->textinfo.scroll = true; + + tce->flags = desc->flags; + if(desc->caps&TCTD_WAITTIME) + tce->start_time = desc->waittime; + else + tce->start_time = 0; + if(desc->caps&TCTD_TABSTOP) + tce->tab_stop = true; + else + tce->tab_stop = false; + + tce->id = id; + + switch(tce->type){ + case EFX_TEXT_STATIC: return CreateTextStatic(tce,text); + case EFX_TEXT_FADE: return CreateTextFade(tce,text); + case EFX_TEXT_TYPE: return CreateTextType(tce,text); + } + return false; +} + +bool CreateBitmapEffect(LPTCBMPDESC desc,int monitor,int screen,int id) +{ + ASSERT(desc); + int efxtype = -1; + switch(desc->type){ + case TC_BMP_STATIC: efxtype = EFX_BMP_STATIC; break; + case TC_BMP_BLUR: efxtype = EFX_BMP_BLUR; break; + case TC_BMP_SCANLINE: efxtype = EFX_BMP_SCANLINE; break; + case TC_BMP_INVERT: efxtype = EFX_BMP_INVERT; break; + case TC_BMP_STRETCH: efxtype = EFX_BMP_STRETCH; break; + default: + Int3(); + } + if(efxtype==-1) + return false; + + // Creates an instance of the Effect + int efxnum = EfxCreate(efxtype,monitor,screen,id); + if(efxnum==-1) + return false; + tceffect *tce = &TCEffects[efxnum]; + + if(desc->caps&TCBD_XY){ + tce->pos_x = desc->x; + tce->pos_y = desc->y; + }else{ + tce->pos_x = tce->pos_y = 0; + } + if(desc->caps&TCBD_WAITTIME) + tce->start_time = desc->waittime; + else + tce->start_time = 0; + if(desc->caps&TCBD_SPEED) + tce->speed = desc->speed; + else + tce->speed = 1; + tce->flags = desc->flags; + + switch(tce->type){ + case EFX_BMP_STATIC: return CreateBmpStatic(tce,desc->filename); + case EFX_BMP_BLUR: return CreateBmpBlur(tce,desc->filename); + case EFX_BMP_SCANLINE: return CreateBmpScanline(tce,desc->filename); + case EFX_BMP_INVERT: return CreateBmpInvert(tce,desc->filename); + case EFX_BMP_STRETCH: return CreateBmpStretch(tce,desc->filename); + } + return false; +} + +bool CreateMovieEffect(LPTCMOVIEDESC desc,int monitor,int screen,int id) +{ + ASSERT(desc); + int efxtype = -1; + switch(desc->type){ + case TC_MOVIE_STATIC: efxtype = EFX_MOVIE; break; + default: + Int3(); + } + if(efxtype==-1) + return false; + // Creates an instance of the Effect + int efxnum = EfxCreate(efxtype,monitor,screen,id); + if(efxnum==-1) + return false; + tceffect *tce = &TCEffects[efxnum]; + + if(desc->caps&TCMD_XY) + tce->movieinfo.fps = desc->fps; + else + tce->movieinfo.fps = 20.0f; + if(desc->caps&TCMD_WAITTIME) + tce->start_time = desc->waittime; + else + tce->start_time = 0; + if(desc->caps&TCMD_XY){ + tce->pos_x = desc->x; + tce->pos_y = desc->y; + }else{ + tce->pos_x = tce->pos_y = 0; + } + + switch(tce->type){ + case EFX_MOVIE: return CreateMovie(tce,desc->filename); + } + + return false; +} + +extern tTelComInfo Telcom_system; +bool CreateBackgroundEffect(LPTCBKGDESC desc,int monitor,int screen,int id) +{ + ASSERT(desc); + int efxtype = -1; + switch(desc->type){ + case TC_BACK_STATIC: efxtype = EFX_BACKGROUND; break; + default: + Int3(); + } + if(efxtype==-1) + return false; + // Creates an instance of the Effect + int efxnum = EfxCreate(efxtype,monitor,screen,id); + if(efxnum==-1) + return false; + tceffect *tce = &TCEffects[efxnum]; + + if(desc->caps&TCBGD_COLOR) + tce->color = desc->color; + else + tce->color = GR_BLUE; + if(desc->caps&TCBGD_WAITTIME) + tce->start_time = desc->waittime; + else + tce->start_time = 0; + + tce->pos_x = 0; + tce->pos_y = 0; + tce->w = Telcom_system.Monitor_coords[monitor].right - Telcom_system.Monitor_coords[monitor].left; + tce->h = Telcom_system.Monitor_coords[monitor].bottom - Telcom_system.Monitor_coords[monitor].top; + + switch(tce->type){ + case EFX_BACKGROUND: return CreateBackground(tce); + } + return false; +} + +bool CreatePolyModelEffect(LPTCPOLYDESC desc,int monitor,int screen,int id) +{ + ASSERT(desc); + int efxtype = -1; + switch(desc->type){ + case TC_POLY_STATIC: efxtype = EFX_POLYMODEL; break; + default: + Int3(); + } + Int3(); //polymodel not supported yet + return false; +} + +bool CreateSoundEffect(LPTCSNDDESC desc,int monitor,int screen,int id) +{ + ASSERT(desc); + int efxtype = -1; + switch(desc->type){ + case TC_SND_STATIC: efxtype = EFX_SOUND; break; + default: + Int3(); + } + if(efxtype==-1) + return false; + // Creates an instance of the Effect + int efxnum = EfxCreate(efxtype,monitor,screen,id); + if(efxnum==-1) + return false; + tceffect *tce = &TCEffects[efxnum]; + if(desc->caps&TCSD_WAITTIME) + tce->start_time = desc->waittime; + else + tce->start_time = 0; + + switch(tce->type){ + case EFX_SOUND: return CreateSound(tce,desc->filename); + } + return false; +} + +int CreateButtonEffect(LPTCBUTTONDESC desc,int monitor,int screen,int id) +{ + ASSERT(desc); + ASSERT(monitor==MONITOR_MAIN); + + // Creates an instance of the Effect + int efxnum = EfxCreate(EFX_BUTTON,monitor,screen,id,desc->tab_stop); + if(efxnum==-1) + return -1; + tceffect *tce = &TCEffects[efxnum]; + + //Load in regular bitmap + tce->buttoninfo.chunk_bmp.pw = tce->buttoninfo.chunk_bmp.ph = 0; + tce->buttoninfo.bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(desc->filename),0); + if(tce->buttoninfo.bm_handle==-1){ + desc->w = desc->h = 0; + tce->buttoninfo.bm_handle = BAD_BITMAP_HANDLE; + mprintf((0,"Unable to load bmp '%s'\n",desc->filename)); + }else{ + desc->w = bm_w(tce->buttoninfo.bm_handle,0); + desc->h = bm_h(tce->buttoninfo.bm_handle,0); + bm_CreateChunkedBitmap(tce->buttoninfo.bm_handle,&tce->buttoninfo.chunk_bmp); + } + + tce->buttoninfo.chunkfocus_bmp.pw = tce->buttoninfo.chunkfocus_bmp.ph = 0; + tce->buttoninfo.bmfocus_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(desc->filename_focus),0); + if(tce->buttoninfo.bmfocus_handle==-1){ + tce->buttoninfo.bmfocus_handle = BAD_BITMAP_HANDLE; + mprintf((0,"Unable to load bmp '%s'\n",desc->filename_focus)); + }else{ + bm_CreateChunkedBitmap(tce->buttoninfo.bmfocus_handle,&tce->buttoninfo.chunkfocus_bmp); + } + + //Load in flash/glow bitmap + tce->buttoninfo.flash_chunk.pw = tce->buttoninfo.flash_chunk.ph = 0; + if(desc->flasher){ + tce->buttoninfo.flash_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(desc->flash_filename),0); + if(tce->buttoninfo.flash_handle==-1){ + mprintf((0,"Unable to load bmp '%s'\n",desc->flash_filename)); + }else{ + bm_CreateChunkedBitmap(tce->buttoninfo.flash_handle,&tce->buttoninfo.flash_chunk); + } + }else{ + tce->buttoninfo.flash_handle = -1; + } + + tce->buttoninfo.flashfocus_chunk.pw = tce->buttoninfo.flashfocus_chunk.ph = 0; + if(desc->flasher){ + tce->buttoninfo.flashfocus_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(desc->flash_filename_focus),0); + if(tce->buttoninfo.flashfocus_handle==-1){ + mprintf((0,"Unable to load bmp '%s'\n",desc->flash_filename_focus)); + }else{ + bm_CreateChunkedBitmap(tce->buttoninfo.flashfocus_handle,&tce->buttoninfo.flashfocus_chunk); + } + }else{ + tce->buttoninfo.flashfocus_handle = -1; + } + + if(desc->button_type==BUTT_INTERNAL){ + tce->buttoninfo.internal = desc->internal; + }else{ + tce->buttoninfo.internal = NULL; + } + + tce->buttoninfo.sibling = desc->sibling_id; + tce->buttoninfo.parent = desc->parent_id; + tce->buttoninfo.button_type = desc->button_type; + tce->buttoninfo.click_type = desc->click_type; + tce->buttoninfo.flash_time = desc->flash_time; + tce->w = desc->w; + tce->h = desc->h; + tce->pos_x = desc->x; + tce->pos_y = desc->y; + tce->flags = desc->osflags; + tce->buttoninfo.jump_page = desc->jump_page; + + return efxnum; +} + +//////////////////////////////////////////////////////////////////// +bool CreateTextStatic(tceffect *tce,char *text) +{ + ASSERT(text); + if(!text) + return false; + + tce->text_buffer = (char *)mem_malloc(strlen(text)+10); + if(!tce->text_buffer) + return false; + + tce->alpha = 255; + + char *tempbuffer = (char *)mem_malloc(strlen(text)+10); + if(!tempbuffer) + return false; + strcpy(tempbuffer,text); + + //apply the word wrap + textaux_WordWrap(tempbuffer,tce->text_buffer,tce->w,Game_fonts[tce->textinfo.font_index]); + + mem_free(tempbuffer); + + return true; +} + +bool CreateTextFade(tceffect *tce,char *text) +{ + ASSERT(text); + if(!text || text[0]=='\0') + return false; + tce->text_buffer = (char *)mem_malloc(strlen(text)+10); + if(!tce->text_buffer) + return false; + + char *tempbuffer = (char *)mem_malloc(strlen(text)+10); + if(!tempbuffer) + return false; + strcpy(tempbuffer,text); + + //apply the word wrap + textaux_WordWrap(tempbuffer,tce->text_buffer,tce->w,Game_fonts[tce->textinfo.font_index]); + + mem_free(tempbuffer); + + switch(tce->flags){ + case TC_TEXTF_OUT: + tce->alpha = 255.0f; + break; + case TC_TEXTF_IN: + case TC_TEXTF_PINGPONG: + default: + tce->alpha = 0.01f; + break; + } + return true; +} + +bool CreateTextType(tceffect *tce,char *text) +{ + ASSERT(text); + if(!text || text[0]=='\0') + return false; + tce->text_buffer = (char *)mem_malloc(strlen(text)+10); + if(!tce->text_buffer) + return false; + + char *tempbuffer = (char *)mem_malloc(strlen(text)+10); + if(!tempbuffer) + return false; + strcpy(tempbuffer,text); + + //apply the word wrap + textaux_WordWrap(tempbuffer,tce->text_buffer,tce->w,Game_fonts[tce->textinfo.font_index]); + + mem_free(tempbuffer); + + tce->textinfo.last_letter = 0; + return true; +} + +bool CreateBmpStatic(tceffect *tce,char *filename) +{ + ASSERT(filename); + + tce->bmpinfo.chunk_bmp.pw = tce->bmpinfo.chunk_bmp.ph = 0; + tce->bmpinfo.bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename),0); + if(tce->bmpinfo.bm_handle==-1){ + tce->bmpinfo.bm_handle = BAD_BITMAP_HANDLE; + mprintf((0,"Unable to load bmp '%s'\n",filename)); + }else + bm_CreateChunkedBitmap(tce->bmpinfo.bm_handle,&tce->bmpinfo.chunk_bmp); + return true; +} + +bool CreateBmpBlur(tceffect *tce,char *filename) +{ + ASSERT(filename); + + tce->bmpinfo.chunk_bmp.pw = tce->bmpinfo.chunk_bmp.ph = 0; + tce->bmpinfo.bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename),0); + if(tce->bmpinfo.bm_handle==-1){ + tce->bmpinfo.bm_handle = BAD_BITMAP_HANDLE; + mprintf((0,"Unable to load bmp '%s'\n",filename)); + }else{ + tce->bmpinfo.temp_bmhandle = bm_AllocBitmap(bm_w(tce->bmpinfo.bm_handle,0),bm_h(tce->bmpinfo.bm_handle,0),0); + if(tce->bmpinfo.temp_bmhandle==-1) + tce->bmpinfo.temp_bmhandle = BAD_BITMAP_HANDLE; + + if(tce->flags==TC_BMPF_OUT ) + tce->bmpinfo.stage = BLUR_STAGES; + else + tce->bmpinfo.stage = 0; + + //create the chunk bitmap for use + bm_CreateChunkedBitmap(tce->bmpinfo.bm_handle,&tce->bmpinfo.chunk_bmp); + } + return true; +} + +bool CreateBmpScanline(tceffect *tce,char *filename) +{ + ASSERT(filename); + + tce->bmpinfo.chunk_bmp.pw = tce->bmpinfo.chunk_bmp.ph = 0; + tce->bmpinfo.bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename),0); + tce->bmpinfo.temp_bmhandle = BAD_BITMAP_HANDLE; + if(tce->bmpinfo.bm_handle==-1){ + tce->bmpinfo.bm_handle = BAD_BITMAP_HANDLE; + mprintf((0,"Unable to load bmp '%s'\n",filename)); + }else{ + tce->bmpinfo.temp_bmhandle = bm_AllocBitmap(bm_w(tce->bmpinfo.bm_handle,0),bm_h(tce->bmpinfo.bm_handle,0),0); + if(tce->bmpinfo.temp_bmhandle==-1) + tce->bmpinfo.temp_bmhandle = BAD_BITMAP_HANDLE; + + //create the chunk bitmap for use + bm_CreateChunkedBitmap(tce->bmpinfo.bm_handle,&tce->bmpinfo.chunk_bmp); + } + return true; +} + +bool CreateBmpInvert(tceffect *tce,char *filename) +{ + ASSERT(filename); + + tce->bmpinfo.chunk_bmp.pw = tce->bmpinfo.chunk_bmp.ph = 0; + tce->bmpinfo.bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename),0); + tce->bmpinfo.temp_bmhandle = BAD_BITMAP_HANDLE; + if(tce->bmpinfo.bm_handle==-1){ + tce->bmpinfo.bm_handle = BAD_BITMAP_HANDLE; + mprintf((0,"Unable to load bmp '%s'\n",filename)); + }else{ + tce->bmpinfo.temp_bmhandle = bm_AllocBitmap(bm_w(tce->bmpinfo.bm_handle,0),bm_h(tce->bmpinfo.bm_handle,0),0); + if(tce->bmpinfo.temp_bmhandle==-1) + tce->bmpinfo.temp_bmhandle = BAD_BITMAP_HANDLE; + + //create the chunk bitmap for use + bm_CreateChunkedBitmap(tce->bmpinfo.bm_handle,&tce->bmpinfo.chunk_bmp); + } + return true; +} + +bool CreateBmpStretch(tceffect *tce,char *filename) +{ + ASSERT(filename); + + tce->bmpinfo.chunk_bmp.pw = tce->bmpinfo.chunk_bmp.ph = 0; + tce->bmpinfo.bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename),0); + tce->bmpinfo.bitmaps = NULL; + tce->bmpinfo.bm_count = 0; + if(tce->bmpinfo.bm_handle==-1){ + tce->bmpinfo.bm_handle = BAD_BITMAP_HANDLE; + mprintf((0,"Unable to load bmp '%s'\n",filename)); + }else{ + //determine how many wide we need + int src_w = bm_w(tce->bmpinfo.bm_handle,0); + int w_count = 0; + w_count = ((src_w+63)/64); + //determine how many tall we need + int src_h = bm_h(tce->bmpinfo.bm_handle,0); + int h_count = 0; + h_count = ((src_h+63)/64); + + int total = w_count * h_count; + int index; + tce->bmpinfo.bm_count = total; + tce->bmpinfo.bitmaps = (int *)mem_malloc(sizeof(int)*total); + if(!tce->bmpinfo.bitmaps) + return false; + + for(index=0;indexbmpinfo.bitmaps[index] = bm_AllocBitmap(64,64,0); + if(tce->bmpinfo.bitmaps[index]==-1) + tce->bmpinfo.bitmaps[index] = BAD_BITMAP_HANDLE; + } + + //now fill in the bitmaps + int realx,realy; + realx = 0; + realy = 0; + + int wi,hi; + ushort *srcdata = bm_data(tce->bmpinfo.bm_handle,0); + ushort *destdata; + + realx = 0; + realy = 0; + index = 0; + for(hi=0,realy=0;hibmpinfo.bitmaps[index],0); + + for(y=realy,iy=0;ymovieinfo.handle = NULL; + tce->movieinfo.filename = NULL; + tce->w = tce->h = 100; + + tce->movieinfo.filename = (char *)mem_malloc(strlen(filename)+1); + if(!tce->movieinfo.filename) + return false; + strcpy(tce->movieinfo.filename,filename); + tce->movieinfo.handle = StartMovie(filename); + if(!tce->movieinfo.handle) + return false; + return true; +} + +bool CreateBackground(tceffect *tce) +{ + return true; +} + +bool CreateSound(tceffect *tce,char *filename) +{ + tce->soundinfo.handle = FindSoundName(IGNORE_TABLE(filename)); + if(tce->soundinfo.handle==-1) + { + mprintf((0,"Unable to find sound '%s'\n",filename)); + } + return true; +} + + +void BltBmpToScreen(int dx, int dy,chunked_bitmap *src_bmp) +{ + BltBmpToScreen(dx,dy,src_bmp,0,0,src_bmp->pw, src_bmp->ph); +} +void BltBmpToScreen(int dx, int dy, chunked_bitmap *src_bmp, int sx, int sy, int sw, int sh) +{ + rend_DrawChunkedBitmap(src_bmp,dx,dy,255); +} +//forces a chunk bitmap to be uploaded +void UploadChunk(chunked_bitmap *bmp) +{ + ASSERT(bmp); + + int count = bmp->w * bmp->h; + + for(int index=0;indexbm_array[index]; + GameBitmaps[bm].flags|=BF_CHANGED; + } +} diff --git a/Descent3/TelComEffects.h b/Descent3/TelComEffects.h new file mode 100644 index 000000000..d560e7c72 --- /dev/null +++ b/Descent3/TelComEffects.h @@ -0,0 +1,355 @@ +/* +* $Logfile: /DescentIII/main/TelComEffects.h $ +* $Revision: 27 $ +* $Date: 4/20/99 12:46p $ +* $Author: Jeff $ +* +* TelCom Effect class defines +* +* $Log: /DescentIII/main/TelComEffects.h $ + * + * 27 4/20/99 12:46p Jeff + * telcom main menu, mouse over button sets focus. if you go into telcom + * main menu, when you leave a system it will return you to main menu. + * + * 26 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 25 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 24 4/02/99 8:32p Jeff + * added system event queue. Arrow keys work in main menu and ship + * selection + * + * 23 2/17/99 6:55p Jeff + * added jump button type. Added no early render flag for bitmaps. Fixed + * color bug for type text + * + * 22 2/03/99 12:14a Jeff + * updated single player ship selection to full functional...needs effects + * added in. Had to add flag to buttons for gain focus to register as a + * click + * + * 21 2/01/99 4:52p Jeff + * screenshots work in telcom + * + * 20 10/12/98 8:32p Jeff + * changed the way focus is handled + * + * 19 9/17/98 2:29p Jeff + * added focus filenames to button effect + * + * 18 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 17 7/11/98 9:17p Jeff + * moved wordwrap functions out so they can be used externally + * + * 16 6/17/98 4:15p Jeff + * adjusted scroll button positions + * + * 15 5/19/98 3:40p Jeff + * poly models functional + * + * 14 5/15/98 5:16p Jeff + * use regular draw chunk bitmap + * + * 13 5/11/98 6:21p Jeff + * fixed setting glitch offsets + * + * 12 5/07/98 3:31p Jeff + * various optimizations, removed Copy and similar functions + * + * 11 5/05/98 6:50p Jeff + * Telcom doesn't use rend_DrawLFBitmap anymore...more speed! + * + * 10 5/04/98 5:29p Jeff + * Added sounds to TelCom events + * + * 9 5/04/98 1:35p Jeff + * Changes made for mouse handling + * + * 8 5/04/98 11:01a Jeff + * added scroll parameter to text + * + * 7 5/03/98 7:57p Jeff + * Got scrolling textboxes working + * + * 6 5/01/98 2:17p Jeff + * Added sound effect support + * + * 5 4/29/98 4:36p Jeff + * added auto word wrap + * + * 4 4/28/98 6:58p Jeff + * Added new poly model effect driver + * + * 3 4/26/98 7:20p Jeff + * Added the rest of the effect drivers except for scrolling text. Added + * invert bitmaps and stretch bitmaps + * + * 2 4/26/98 2:54a Jeff + * Initial Creation + * + * 1 4/25/98 4:23p Jeff +* +* $NoKeywords: $ +*/ + +#ifndef __TELCOM_EFFECTS_H_ +#define __TELCOM_EFFECTS_H_ + +#include "pstypes.h" +#include "renderer.h" +#include "grdefs.h" +#include "cinematics.h" +#include "TelComEfxStructs.h" +#include "TelCom.h" +#include "vecmat_external.h" + +/* +*************************************************************************************** + Start of New TelCom +*************************************************************************************** +*/ + +#define MAX_TCEFFECTS 256 +#define MAX_EFFECT_EVENTS 5 +#define INVALID_EFFECT_HANDLE 0xFFFFFFFF + +//Effect types +#define EFX_NONE 0 +#define EFX_TEXT_STATIC 1 +#define EFX_TEXT_TYPE 2 +#define EFX_TEXT_FADE 3 +#define EFX_BMP_STATIC 4 +#define EFX_BMP_BLUR 5 +#define EFX_BMP_SCANLINE 6 +#define EFX_BMP_INVERT 7 +#define EFX_BMP_STRETCH 8 +#define EFX_BACKGROUND 9 +#define EFX_MOVIE 10 +#define EFX_POLYMODEL 11 +#define EFX_SOUND 12 +#define EFX_BUTTON 13 + +//Events +#define TEVT_GAINFOCUS 0 +#define TEVT_LOSTFOCUS 1 +#define TEVT_MOUSEOVER 2 +#define TEVT_MOUSECLICK 3 +#define TEVT_MOUSEDOWN 4 +#define TEVT_MOUSEUP 5 +#define TEVT_TCQUIT 6 +#define TEVT_TCNEXT 7 +#define TEVT_TCPREV 8 +#define TEVT_TCJUMP 9 +#define TEVT_SCROLLDOWN 10 +#define TEVT_SCROLLUP 11 +#define TEVT_SCREENSHOT 12 +#define TEVT_FAKEKEYPRESS 13 +#define TEVT_MOUSEENTER 14 + + +//others +#define BLUR_STAGE_MULTIPLE 0.12f +#define BLUR_STAGES 5 +#define INVALID_EFFECT_ID 0x6FEFCD01 + +//Button types +#define BUTT_UPARROW 0 +#define BUTT_DOWNARROW 1 +#define BUTT_NEXTPAGE 2 +#define BUTT_PREVPAGE 3 +#define BUTT_QUIT 4 +#define BUTT_INTERNAL 5 +#define BUTT_JUMP 6 + +//Button Click types +#define CLICKTYPE_CLICKDOWN 0 +#define CLICKTYPE_CLICKUP 1 +#define CLICKTYPE_DOWN 2 + + +typedef struct{ + float last_letter; //Last letter printed + int sound_index; //handle to sound for action + int font_index; //Font index + int line_index; //index of first text line (scrolling purposes) + bool scroll_u,scroll_d; //if the scrollup down buttons are appearing + bool scroll; //set to true if it should be allowed to scroll +}tTextInfo; + +typedef struct{ + int temp_bmhandle; //handle of temporary bitmap + float stage; //stage that bitmap effect is in + int *bitmaps; //array of bitmaps (for scale effect) + int bm_count; //how many bitmaps (for scale effect) + int bm_handle; //handle to the bitmap + chunked_bitmap chunk_bmp; //the chunk bitmap +}tBitmapInfo; + +typedef struct{ + vector m_Rot; //current rotation + vector m_Pos; //current position + vector m_Ori; //current orientation + matrix m_mOrient; //orientation + int handle; //handle to polymodel +}tPolyInfo; + +typedef struct{ + tCinematic *handle; //handle to the movie + char *filename; //filename of the movie + float fps; //fps +}tMovieInfo; + +typedef struct{ + bool started; //has the sound started? + int handle; //handle of the sound +}tSoundInfo; + +typedef struct{ + bool flash_state; //Is the button glowing? + ubyte click_type; //CLICKTYPE_DOWN or CLICKTYPE_CLICK (what the button responds to) + ubyte button_type; + int sibling; //Sibling effect (for down/up arrows), -1 if none + int parent; //Parent effect (text for down/up arrows), -1 if it works with TelCom System + int bm_handle; + int bmfocus_handle; + int flash_handle; + int flashfocus_handle; + int real_x,real_y; + int jump_page; + float flash_time; + void (*internal)(int); + chunked_bitmap chunk_bmp; + chunked_bitmap chunkfocus_bmp; + chunked_bitmap flash_chunk; + chunked_bitmap flashfocus_chunk; +}tButtonInfo; + +typedef struct +{ + ubyte type; //Type of effect + ubyte monitor; //Which monitor it belongs to + ubyte screen; //What screen the effect belongs to + bool tab_stop; //This effect can have focus + bool has_focus; //The effect currently has focus + + int id; //user given ID of effect (should be unique) + + char *text_buffer; //Pointer to allocated memory for text string + + int flags; //flags for effect + + int pos_x,pos_y; //Position of upper left corner + int w,h; //width and height + ddgr_color color; //Color of effect + + float speed; //speed of effect + float alpha; //alpha of effect + float age; //current age of effect + float start_time; //how long until the effect starts + + union{ + tTextInfo textinfo; + tBitmapInfo bmpinfo; + tPolyInfo polyinfo; + tMovieInfo movieinfo; + tSoundInfo soundinfo; + tButtonInfo buttoninfo; + }; + + tTCEvent event_queue[MAX_EFFECT_EVENTS]; //event queue for an effect + + int prev,next; //Links to previous and next effect +}tceffect; + + +extern int glitch_dx,glitch_dy; + +///////////////////////////////////////////////// +// Prototypes + +// Initializes the effects +void EfxInit(void); + +// Shutdown of effects +void EfxClose(void); + +// Creates an instance of an Effect +int EfxCreate(int type,int monitor,int screen,int id=INVALID_EFFECT_ID,bool is_tab_stop=false); + + +// Destroys all the screens +void EfxDestroyAllScreens(void); + +// Destroys the screens within a monitor +void EfxDestroyScreen(int screen); + +// Frees up any memory allocated for effect, sets type to EFX_NONE +void EfxFreeEffect(tceffect *tce); + +// Initializes an effect based on it's type +void EfxInit(tceffect *tce,bool tab_stop,bool gets_focus); + +// Renders a Monitor +void RenderScreen(int screen,tTelComInfo *tsys,float Frametime); + +// Renders an effect +void RenderEffect(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); + +// Sends an event to an effect +void SendEventToEffect(int effect,int event,int parm1=0,int parm2=0); + +// returns the efxnum of the button that contains the X,Y on the given monitor/screen, -1 if none +int FindButtonEffectByXY(int x,int y,int screen); + +// Returns the efxnum given an id & screen, -1 if not found +int GetEfxNumFromID(int id,int screen); + + +// Pops off an event for processesing, returns -1 if no event available +bool PopEvent(tceffect *tce,tTCEvent *event); +void RenderTextStatic(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderTextType(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderTextFade(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderBmpStatic(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderBmpBlur(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderBmpScanline(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderBmpInvert(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderBmpStretch(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderBackground(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderMovie(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderPolyModel(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderSound(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); +void RenderButton(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render); + +bool CreateTextEffect(LPTCTEXTDESC desc,char *text,int monitor,int screen,int id=INVALID_EFFECT_ID); +bool CreateBitmapEffect(LPTCBMPDESC desc,int monitor,int screen,int id=INVALID_EFFECT_ID); +bool CreateMovieEffect(LPTCMOVIEDESC desc,int monitor,int screen,int id=INVALID_EFFECT_ID); +bool CreateBackgroundEffect(LPTCBKGDESC desc,int monitor,int screen,int id=INVALID_EFFECT_ID); +bool CreatePolyModelEffect(LPTCPOLYDESC desc,int monitor,int screen,int id=INVALID_EFFECT_ID); +bool CreateSoundEffect(LPTCSNDDESC desc,int monitor,int screen,int id=INVALID_EFFECT_ID); +int CreateButtonEffect(LPTCBUTTONDESC desc,int monitor,int screen,int id=INVALID_EFFECT_ID); +bool CreateTextStatic(tceffect *tce,char *text); +bool CreateTextFade(tceffect *tce,char *text); +bool CreateTextType(tceffect *tce,char *text); +bool CreateBmpStatic(tceffect *tce,char *filename); +bool CreateBmpBlur(tceffect *tce,char *filename); +bool CreateBmpScanline(tceffect *tce,char *filename); +bool CreateBmpInvert(tceffect *tce,char *filename); +bool CreateBmpStretch(tceffect *tce,char *filename); +bool CreateMovie(tceffect *tce,char *filename); +bool CreateBackground(tceffect *tce); +bool CreateSound(tceffect *tce,char *filename); + +void BltBmpToScreen(int dx, int dy,chunked_bitmap *src_bmp); +void BltBmpToScreen(int dx, int dy, chunked_bitmap *src_bmp, int sx, int sy, int sw, int sh); +//forces a chunk bitmap to be uploaded +void UploadChunk(chunked_bitmap *bmp); + +char *format(char *format,...); + +#endif \ No newline at end of file diff --git a/Descent3/TelComEfxStructs.h b/Descent3/TelComEfxStructs.h new file mode 100644 index 000000000..0bbcef63a --- /dev/null +++ b/Descent3/TelComEfxStructs.h @@ -0,0 +1,296 @@ +/* +* $Logfile: /DescentIII/main/TelComEfxStructs.h $ +* $Revision: 19 $ +* $Date: 4/20/99 12:46p $ +* $Author: Jeff $ +* +* Structures used for TelCom effects +* +* $Log: /DescentIII/main/TelComEfxStructs.h $ + * + * 19 4/20/99 12:46p Jeff + * telcom main menu, mouse over button sets focus. if you go into telcom + * main menu, when you leave a system it will return you to main menu. + * + * 18 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 17 2/17/99 6:56p Jeff + * added jump button type. Added no early render flag for bitmaps. Fixed + * color bug for type text + * + * 16 2/03/99 12:14a Jeff + * updated single player ship selection to full functional...needs effects + * added in. Had to add flag to buttons for gain focus to register as a + * click + * + * 15 1/04/99 12:33p Jeff + * added support for mission flag parsing + * + * 14 10/12/98 8:32p Jeff + * changed the way focus is handled + * + * 13 9/17/98 2:29p Jeff + * added focus filenames to button effect + * + * 12 9/03/98 2:20p Jeff + * creation of dialogs in Brief editor + * + * 11 8/27/98 2:51p Jeff + * New TelCom finally checked in + * + * 10 5/19/98 3:40p Jeff + * poly models functional + * + * 9 5/18/98 2:29p Jeff + * added new type of on screen button, a glow type (if mouse is over it, + * it will do the glow) + * + * 8 5/05/98 6:50p Jeff + * Telcom doesn't use rend_DrawLFBitmap anymore...more speed! + * + * 7 5/04/98 1:35p Jeff + * Changes made for mouse handling + * + * 6 5/04/98 11:01a Jeff + * added scroll parameter to text + * + * 5 5/01/98 2:17p Jeff + * Added sound effect support + * + * 4 4/28/98 6:58p Jeff + * Added new poly model effect driver + * + * 3 4/26/98 7:21p Jeff + * added stretch bitmap effect define + * + * 2 4/26/98 2:54a Jeff + * Initial Creation +* +* $NoKeywords: $ +*/ + +#ifndef __TELCOMEFXSTRUCTS_H_ +#define __TELCOMEFXSTRUCTS_H_ + +#include "grdefs.h" + +#define MAX_FILELEN 32 +//=================== +// tc_text +// - Contains information on the text coordinates of an effect +//=================== +typedef struct +{ + int left,right,top,bottom; +} tc_text; + +//TCTEXTDESC caps flags +#define TCTD_FONT 0x0001 +#define TCTD_COLOR 0x0002 +#define TCTD_SPEED 0x0004 +#define TCTD_LOOPING 0x0008 +#define TCTD_WAITTIME 0x0010 +#define TCTD_TEXTBOX 0x0020 +#define TCTD_SCROLL 0x0040 +#define TCTD_TABSTOP 0x0080 +//TCBMPDESC caps flags +#define TCBD_XY 0x0001 +#define TCBD_LOOPING 0x0002 +#define TCBD_WAITTIME 0x0004 +#define TCBD_SPEED 0x0008 +//TCMOVIEDESC caps flags +#define TCMD_XY 0x0001 +#define TCMD_LOOPING 0x0002 +#define TCMD_WAITTIME 0x0004 +#define TCMD_FPS 0x0008 +//TCBKGDESC caps flags +#define TCBGD_ID 0x0001 +#define TCBGD_WAITTIME 0x0002 +#define TCBGD_COLOR 0x0004 +//TCPOLYDESC caps flags +#define TCPM_SPEED 0x0001 +//TCSNDDESC caps flags +#define TCSD_WAITTIME 0x0001 +#define TCSD_ONCE 0x0002 + +//Type values +#define TC_TEXT_STATIC 0 +#define TC_TEXT_SCROLL 1 +#define TC_TEXT_FADE 2 +#define TC_TEXT_FLASH 3 +#define TC_BMP_STATIC 0 +#define TC_BMP_BLUR 1 +#define TC_BMP_SCANLINE 2 +#define TC_BMP_INVERT 3 +#define TC_BMP_STRETCH 4 +#define TC_BACK_STATIC 0 +#define TC_MOVIE_STATIC 0 +#define TC_POLY_STATIC 0 +#define TC_SND_STATIC 0 + +//text flags +#define TC_TEXTF_L2R 0 //scroll left 2 right +#define TC_TEXTF_R2L 1 //scroll right 2 left +#define TC_TEXTF_T2B 2 //scroll top 2 bottom +#define TC_TEXTF_B2T 3 //scroll bottom 2 top +#define TC_TEXTF_IN 4 //fade in +#define TC_TEXTF_OUT 5 //fade out +#define TC_TEXTF_PINGPONG 6 //fade in then out +//bmp flags +#define TC_BMPF_IN 0 //blur/scanline/invert in +#define TC_BMPF_OUT 1 //blur/scanline/invert out +#define TC_NOEARLYRENDER 2 //don't start rendering until time is up + + +//=================== +// TCTEXTDESC +// - Contains all the info for a text effect passed to the monitor call +//=================== +// The following structure should be filled in, and passed to the Monitor::Text() function +// caps must be set with the above flags. If a member isn't set to be defined by the caps, then +// if it's needed default values will be used (defaults listed after each member in structure) +typedef struct +{ + //what values are set/defined + int caps; //(MUST BE DEFINED!) + //the text box of the effect + tc_text textbox; //(Default:entire monitor screen) + //what kind of font to use + int font; //(Default: BRIEF_FONT_INDEX) + //default color of the text + ddgr_color color; //(Default: GR_GREEN) + //flags + int flags; + //speed + float speed; + //does the text effect loop + bool looping; //(Default: false) + //how much time to wait before effect starts + float waittime; //(Default: 0.0f) + //type + int type; + //mission flag mask + uint mission_mask_set,mission_mask_unset; +}TCTEXTDESC, *LPTCTEXTDESC; +typedef struct +{ + //type + int type; + //flags + int flags; + //what members are defined + int caps; //MUST BE FILLED IN + //upped left corner to display bitmap + int x,y; //(Default: (0,0) ) + //does the effect loop + bool looping; //(Default: false) + //how long to wait until displaying the bitmap + float waittime; //(Default: 0.0f) + //how fast the bitmap effect should be (only useful for non-static) + float speed; //(Default: 2.0f) + //filename + char filename[MAX_FILELEN]; //MUST BE FILLED IN + //mission flag mask + uint mission_mask_set,mission_mask_unset; +}TCBMPDESC, *LPTCBMPDESC; +typedef struct +{ + //fill in with whats valid + int caps; //MUST BE FILLED IN + //type + int type; + //upper left coordinate of the movie + int x,y; //(Default: (0,0) ) + //filename of the movie to play + char filename[MAX_FILELEN]; //MUST BE FILLED IN + //whether the movie is to loop + bool looping; //(Default: false) + //Frames per second + float fps; //(Default: 20.0f) + //time to wait until starting the movie + float waittime; //(Default: 0.0f) + //mission flag mask + uint mission_mask_set,mission_mask_unset; +}TCMOVIEDESC,*LPTCMOVIEDESC; +typedef struct +{ + //whats defined for the struct + int caps; //MUST BE SET! + //what type of background effect + int type; //(Default: MBS_DESKTOP) + //id used by the type + int id; //(if type is set to Desktop then it MUST BE SET to the bitmap handle, else MBI_QUICKCLEAR) + //color if needed + ddgr_color color; //(Default: GR_BLUE) + //how long to wait until effect start + float waittime; //(Default: 0.0f) + //mission flag mask + uint mission_mask_set,mission_mask_unset; +}TCBKGDESC,*LPTCBKGDESC; +typedef struct +{ + //whats defined for the struct + int caps; //MUST BE SET! + //what type of poly effect + int type; + //position + float pos_x,pos_y,pos_z; //coordinates (MUST BE SET!) + //rotation (degrees per sec) + float rot_x,rot_y,rot_z; + //orientation + float ori_x,ori_y,ori_z; + //flags to use + int flags; + //how long to wait until effect starts + float waittime; + //polymodel name + char polyname[MAX_FILELEN]; + //mission flag mask + uint mission_mask_set,mission_mask_unset; +}TCPOLYDESC,*LPTCPOLYDESC; +typedef struct +{ + //whats defined for the struct + int caps; //MUST BE SET + //type of sound effect + int type; + //true if this sound should only play once + bool once; + //how long to wait until effect starts + float waittime; + //sound name + char filename[MAX_FILELEN]; + //mission flag mask + uint mission_mask_set,mission_mask_unset; +}TCSNDDESC,*LPTCSNDDESC; +typedef struct +{ + char filename[MAX_FILELEN]; + char filename_focus[MAX_FILELEN]; + char flash_filename[MAX_FILELEN]; + char flash_filename_focus[MAX_FILELEN]; + int sibling_id; //Sibling effect ID(for down/up arrows), -1 if none + int parent_id; //Parent effect ID (text for down/up arrows), -1 if it works with TelCom System + int x,y,w,h; + int osflags; + int jump_page; + void (*internal)(int); + float flash_time; + ubyte button_type; //Up arrow, Down Arrow, TelCom System + ubyte click_type; //CLICKTYPE_DOWN or CLICKTYPE_CLICK (what the button responds to) + bool flasher; + bool tab_stop; + uint mission_mask_set,mission_mask_unset; //mission flag mask +}TCBUTTONDESC,*LPTCBUTTONDESC; + +//================== +// tc_button +// - contains info on an on screen button +//================== +#define OBF_FLASH 0x01 +#define OBF_GLOW 0x02 +#define OBF_CHANGEFOCUSISCLICK 0x04 +#define OBF_MOUSEOVERFOCUS 0x08 + +#endif \ No newline at end of file diff --git a/Descent3/TelComGoals.cpp b/Descent3/TelComGoals.cpp new file mode 100644 index 000000000..12577c7c4 --- /dev/null +++ b/Descent3/TelComGoals.cpp @@ -0,0 +1,827 @@ +/* +* $Logfile: /DescentIII/main/TelComGoals.cpp $ +* $Revision: 17 $ +* $Date: 4/20/99 1:45a $ +* $Author: Matt $ +* +* TelCom Goal Status screen +* +* $Log: /DescentIII/main/TelComGoals.cpp $ + * + * 17 4/20/99 1:45a Matt + * Fixed a dereferencing bug that was causing this screen to use totally + * different fonts than the code intended. It now uses the two briefing + * fonts. + * + * 16 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 15 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 14 4/05/99 3:28p Jeff + * failed mission added to telcom goal screen + * + * 13 4/02/99 9:24p Jeff + * fixed tooltip detection + * + * 12 3/31/99 5:34p Jeff + * fixed 0 byte malloc + * + * 11 3/27/99 2:59p Jeff + * handle multiple line goals...tooltips don't go off screen now + * + * 10 3/24/99 3:01p Jeff + * fixed 0 byte malloc + * + * 9 2/27/99 2:48p Jeff + * adjust to bottom right corner of mouse pointer (tool tips) + * + * 7 2/26/99 5:01p Jeff + * added background screen and some new artwork + * + * 6 2/26/99 12:07a Jeff + * fixed up tooltip stuff + * + * 5 2/24/99 9:39p Jeff + * updated + * + * 4 2/21/99 9:06p Jeff + * fixed bug where nothing would display on the screen if there were + * goals, but none are enabled or viewable by telcom + * + * 3 2/20/99 9:22p Jeff + * finished telcom level goals screen. Made it so if you go into the + * telcom from the game it goes to main menu instead of briefings. + * + * 2 2/20/99 4:37p Jeff + * added telcomgoals + * + * 1 2/20/99 4:31p Jeff +* +* $NoKeywords: $ +*/ + +#include "TelComGoals.h" +#include +#include +#include + +#include "CFILE.H" +#include "pserror.h" +#include "ddio.h" +#include "bitmap.h" + +#include "TelCom.h" +#include "renderer.h" +#include "game.h" +#include "mem.h" +#include "stringtable.h" +#include "gametexture.h" +#include "textaux.h" +#include "TelComEfxStructs.h" +#include "TelComEffects.h" +#include "levelgoal.h" +#include "textaux.h" +#include "Mission.h" +#include "Macros.h" +#include "hlsoundlib.h" + +#define TITLE_X 30+TGminx +#define VALUE_X 400+TGminx +int TGminx,TGmaxx,TGminy,TGmaxy; +#define SM_FONT BRIEFING_FONT +#define BG_FONT BIG_BRIEFING_FONT + +#define UI_RIGHT_ARROW_CHAR 26 + +typedef struct +{ + bool primary; + bool is_objective; + bool is_active; + int goal_index; + int lx,rx,ty,by; //bounding box +}tGoalLineInfo; +tGoalLineInfo *TG_Lines; +int TG_NumLines; +int *TG_SortedList; +int TG_CurrObjectiveArrow; +typedef struct +{ + int w,h; +}tToolTipData; + +tToolTipData TG_TT_dest; +tToolTipData TG_TT_curr; +chunked_bitmap TG_Tooltipbitmap; +int TG_MouseOffset_x = -1,TG_MouseOffset_y = -1; +extern int TC_cursor; +//returns true if left < right +bool TG_compare_slots(int left,int right) +{ + int left_index,right_index; + int left_priority,right_priority; + char left_list,right_list; + + left_index = TG_Lines[left].goal_index; + right_index = TG_Lines[right].goal_index; + + //Primaries have precedence over secondaries + if(TG_Lines[left].primary && !TG_Lines[right].primary) + return false; //right is a secondary, left is a primary + if(!TG_Lines[left].primary && TG_Lines[right].primary) + return true; //right is a primary, left is secondary + + Level_goals.GoalPriority(left_index,LO_GET_SPECIFIED,&left_priority); + Level_goals.GoalPriority(right_index,LO_GET_SPECIFIED,&right_priority); + Level_goals.GoalGoalList(left_index,LO_GET_SPECIFIED,&left_list); + Level_goals.GoalGoalList(right_index,LO_GET_SPECIFIED,&right_list); + + //check goal list...the lower has priority + if(left_list < right_list) + return false; + if(right_list < left_list) + return true; + + //the have same goal list, if it's an object, then that has priority, else sort by + //goal priority + if(TG_Lines[left].is_objective) + return false; + if(TG_Lines[right].is_objective) + return true; + + //they are both goals, and not objectives, check based on priority + return (left_priority>right_priority); +} + +void TCGoalsBuildLineData(void) +{ + int number_of_goals = Level_goals.GetNumGoals(); + int index,goal_status,count=0,i; + + //see how many of these are something we want + for(index=0;index=0 && TG_compare_slots(TG_SortedList[j],t); j--){ + TG_SortedList[j+1] = TG_SortedList[j]; + } + // insert + TG_SortedList[j+1] = t; + } + }else + { + TG_Lines = NULL; + TG_NumLines = 0; + TG_SortedList = NULL; + } + +} + +int FindHighlightedItem(int x,int y) +{ + for(int i=0;i= TG_Lines[i].lx && + x<= TG_Lines[i].rx && + y>= TG_Lines[i].ty && + y<= TG_Lines[i].by){ + return TG_Lines[i].goal_index; + } + } + return -1; +} + +#define ACTIVE_ALPHA_PER_SEC 255.0f +#define ACTIVE_ALPHA_MIN 100.0f +#define ACTIVE_ALPHA_MAX 255.0f + +#define COLOR_OBJECTIVE_HEADING GR_WHITE +#define COLOR_OBJECTIVE_NAME GR_RGB(255,255,40) +#define COLOR_GOAL_NAME GR_WHITE +#define COLOR_COMPLETED GR_RGB(40,255,40) +#define COLOR_INCOMPLETE GR_RGB(255,40,40) +#define COLOR_FAILED GR_RGB(255,255,40) + +extern float last_frametime; +void TCGoalsRenderCallback(void) +{ + static bool active_alpha_in = false; + static float active_alpha = ACTIVE_ALPHA_MAX; + + //recalculate alpha + float alpha_change = last_frametime*ACTIVE_ALPHA_PER_SEC; + float alpha_amount; + while(alpha_change>0) + { + if(active_alpha_in) + { + alpha_amount = min(ACTIVE_ALPHA_MAX-active_alpha,alpha_change); + active_alpha += alpha_amount; + + if(active_alpha>(ACTIVE_ALPHA_MAX-1.0f)) + active_alpha_in = false; + }else + { + alpha_amount = min(alpha_change,active_alpha-ACTIVE_ALPHA_MIN); + active_alpha -= alpha_amount; + + if(active_alpha<(ACTIVE_ALPHA_MIN+1.0f)) + active_alpha_in = true; + } + alpha_change -= alpha_amount; + } + + int old_font = grtext_GetFont(); + + grtext_SetAlpha(255); + rend_SetOverlayType (OT_NONE); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetZBufferState (0); + rend_SetAlphaType (AT_CONSTANT+AT_TEXTURE); + rend_SetAlphaValue(255); + rend_SetZBufferState (0); + + int y = TGminy; + + int obj_x,obj_max_width,goal_x,goal_max_width,status_x,status_max_width; + obj_x = TGminx + 25; + obj_max_width = 315; + status_x = obj_x + obj_max_width; + status_max_width = TGmaxx-status_x; + goal_x = obj_x + 20; + goal_max_width = status_x - goal_x; + /* + int goal_name_x,goal_status_x,goal_name_max_width,goal_status_max_width; + goal_name_x = TGminx + 20; + goal_name_max_width = 320; + goal_status_x = goal_name_x + goal_name_max_width; + goal_status_max_width = TGmaxx-goal_status_x; + */ + + int period_length; + period_length = grfont_GetCharWidth(SM_FONT,'.')+Grtext_spacing; + + //@@@ASSERT(goal_status_max_width>0); + ASSERT(status_max_width>0); + + int big_height = grfont_GetHeight(BG_FONT); + int small_height = grfont_GetHeight(SM_FONT); + + grtext_SetAlpha(255); + grtext_SetColor(GR_WHITE); + + // render here + //grtext_SetFont(BRIEFING_FONT); + //@@@int number_of_goals = Level_goals.GetNumGoals(); + int goal_index; + int goal_status,i,width,x; + bool first_primary,first_secondary; + first_primary = first_secondary = true; + +#define GOAL_NAME_BUFFER_SIZE 256 +#define BUFFER_SIZE GOAL_NAME_BUFFER_SIZE*4 + char buffer[BUFFER_SIZE]; + + //Print out Heading + char level_string[32]; + sprintf(level_string,TXT_LEVELSTRING,Current_mission.cur_level); + grtext_SetFont(BG_FONT); + grtext_SetColor(GR_RGB(255,255,255)); + int level_name_x = TGmaxx - grtext_GetTextLineWidth(level_string) - 15; + grtext_Printf(level_name_x,y,level_string); + if( !Level_info.name[0] ) + { + grtext_Printf(TGminx+15,y,TXT_UNNAMED); + }else + { + grtext_Printf(TGminx+15,y,Level_info.name); + } + y+=(big_height+15); + + for(i=0;iperiod_length && index<(BUFFER_SIZE-1)) + { + buffer[index] = '.'; + index++; + length_to_fill -= period_length; + } + buffer[index] = '\0'; + grtext_SetColor(GR_WHITE); + grtext_Printf(end_x,y,buffer); + + if(TG_Lines[goal_index].is_active){ + + if(TG_CurrObjectiveArrow>BAD_BITMAP_HANDLE) + { + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (active_alpha); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + + rend_DrawScaledBitmap(x-small_height-2,wrap_y,x-2,wrap_y+small_height,TG_CurrObjectiveArrow,0,0,1,1); + + rend_SetAlphaValue (255.0); + rend_SetFiltering (1); + }else + { + ubyte alpha_to_use; + alpha_to_use = (ubyte)active_alpha; + grtext_SetAlpha(alpha_to_use); + } + } + + //get the status of the goal + Level_goals.GoalStatus(TG_Lines[goal_index].goal_index,LO_GET_SPECIFIED,&goal_status); + if(goal_status&LGF_COMPLETED) + { + //completed goal + grtext_SetColor(COLOR_COMPLETED); + grtext_Printf(status_x,y,TXT_COMPLETED); + }else if(goal_status&LGF_FAILED) + { + //failed goal + grtext_SetColor(COLOR_FAILED); + grtext_Printf(status_x,y,TXT_FAILED); + }else + { + //not completed + grtext_SetColor(COLOR_INCOMPLETE); + grtext_Printf(status_x,y,TXT_NOTCOMPLETEDYET); + } + + y+= (small_height+2); + } + + if( (TG_NumLines<=0) || (first_primary && first_secondary)) + { + //no goals for this level + grtext_SetFont(BG_FONT); + + y = TGminy + ((TGmaxy - TGminy)/2) - (grfont_GetHeight(BG_FONT)/2); + grtext_CenteredPrintf(0,y,TXT_NOGOALSAVAIL); + + } + + grtext_Flush(); + + + // Display tooltip box + int mouse_x,mouse_y; + ddio_MouseGetState( &mouse_x, &mouse_y, NULL, NULL ); + int index = FindHighlightedItem(mouse_x,mouse_y); + char desc_buffer[1024],s_desc_buffer[1024],*ptr; + ptr = desc_buffer; + *ptr = '\0'; + + if(index!=-1) + { +#define MIN_TOOLTIP_WIDTH 100 +#define MAX_TOOLTIP_WIDTH 300 +#define MIN_TOOLTIP_HEIGHT 40 + //we're over an item + + Level_goals.GoalGetDesc(index,desc_buffer,1024); + while( *ptr && (*ptr==' '||*ptr=='\t')) ptr++; + if(strlen(ptr)>0) + { + int width = grtext_GetTextLineWidth(ptr); + + if(width > MAX_TOOLTIP_WIDTH) + width = MAX_TOOLTIP_WIDTH; + textaux_WordWrap(ptr,s_desc_buffer,width-45,SM_FONT); + + int num_lines = 0; + char *dptr = s_desc_buffer; + while ( *dptr ) + { + if(*dptr=='\n') + num_lines++; + dptr++; + } + num_lines++; + ptr = s_desc_buffer; + + //figure out true width + char *next_line; + next_line = textaux_CopyTextLine(ptr,desc_buffer); + width = grtext_GetTextLineWidth(desc_buffer); + int t; + while(next_line) + { + next_line = textaux_CopyTextLine(next_line,desc_buffer); + t = grtext_GetTextLineWidth(desc_buffer); + if(t>width) + width = t; + } + + TG_TT_dest.w = width + 20; + TG_TT_dest.h = 10 + (num_lines*small_height); + }else + { + memset(&TG_TT_dest,0,sizeof(tToolTipData)); //start shrinking + } + }else + { + memset(&TG_TT_dest,0,sizeof(tToolTipData)); //start shrinking + } + +#define TOOLTIP_SPEED 400.0f + + // adjust box + if(TG_TT_curr.w>TG_TT_dest.w) + { + //shrinking + TG_TT_curr.w -= min( TG_TT_curr.w-TG_TT_dest.w , (int)(TOOLTIP_SPEED*last_frametime) ); + }else + { + //enlarging + TG_TT_curr.w += min( TG_TT_dest.w-TG_TT_curr.w , (int)(TOOLTIP_SPEED*last_frametime) ); + } + + if(TG_TT_curr.h > TG_TT_dest.h) + { + //shrinking + TG_TT_curr.h -= min( TG_TT_curr.h-TG_TT_dest.h , (int)(TOOLTIP_SPEED*last_frametime) ); + }else + { + //enlarging + TG_TT_curr.h += min( TG_TT_dest.h-TG_TT_curr.h , (int)(TOOLTIP_SPEED*last_frametime) ); + } + + //rend_SetAlphaType (AT_CONSTANT_TEXTURE); + //rend_SetAlphaValue (200.0); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + + if(TG_MouseOffset_x==-1) + { + //determine bottom-right x/y of mouse cursor + ushort *data = bm_data(TC_cursor,0); + int width = bm_w(TC_cursor,0); + int index = width * bm_h(TC_cursor,0) - 1; + while( index>0 && (!(data[index]&OPAQUE_FLAG)) ) index--; + + TG_MouseOffset_x = index%width; + TG_MouseOffset_y = index/width; + } + + //Adjust X box if it is to far to the right + if(mouse_x+TG_TT_curr.w>Telcom_system.Monitor_coords[MONITOR_MAIN].right||mouse_x+TG_TT_dest.w>Telcom_system.Monitor_coords[MONITOR_MAIN].right) + { + //we must adjust + mouse_x -= TG_TT_curr.w; + } + + rend_DrawScaledChunkedBitmap(&TG_Tooltipbitmap,mouse_x+TG_MouseOffset_x,mouse_y+TG_MouseOffset_y,TG_TT_curr.w,TG_TT_curr.h,255); + rend_SetFiltering (1); + + if(TG_TT_curr.w!=0 && TG_TT_curr.h!=0) + { + //See if we need to draw any text + //only when curr==dest + if( TG_TT_curr.w==TG_TT_dest.w && + TG_TT_curr.h==TG_TT_dest.h) + { + //write text description + grtext_SetAlpha(255); + grtext_SetColor(GR_WHITE); + grtext_SetFont(SM_FONT); + + char *next_line; + int c_x,c_y; + c_x = mouse_x+TG_MouseOffset_x+10; + c_y = mouse_y+TG_MouseOffset_y+5; + + next_line = textaux_CopyTextLine(ptr,desc_buffer); + while(next_line) + { + grtext_Printf(c_x,c_y,desc_buffer); + next_line = textaux_CopyTextLine(next_line,desc_buffer); + c_y += small_height; + } + grtext_Printf(c_x,c_y,desc_buffer); + grtext_Flush(); + } + } + + grtext_SetFont(old_font); + rend_SetZBufferState (1); +} + +// This is the function called by TelCom +// return true if TelCom should exit to TelCom Main Menu +// return false if you should exit out of TelCom completly +bool TelComGoalStatus(tTelComInfo *tcs) +{ + bool done = false; + + TelcomStartScreen(0); + + TCBKGDESC backg; + backg.color = BACKGROUND_COLOR; + backg.caps = TCBGD_COLOR; + backg.type = TC_BACK_STATIC; + CreateBackgroundEffect(&backg,MONITOR_MAIN,0); + CreateBackgroundEffect(&backg,MONITOR_TOP,0); + + TCBMPDESC bmpdesc; + bmpdesc.type = TC_BMP_STATIC; + bmpdesc.flags = 0; + bmpdesc.caps=TCBD_XY; + bmpdesc.x = 0; bmpdesc.y = 0; + strcpy(bmpdesc.filename,"TelcomGoalBkg.ogf"); + CreateBitmapEffect(&bmpdesc,MONITOR_MAIN,0); +/* +$$TABLE_GAMEFILE "TelcomGoalBkg.ogf" +*/ + + + TGminx = tcs->Monitor_coords[MONITOR_MAIN].left + 10; + TGminy = tcs->Monitor_coords[MONITOR_MAIN].top +10; + TGmaxy = tcs->Monitor_coords[MONITOR_MAIN].bottom - 10; + TGmaxx = tcs->Monitor_coords[MONITOR_MAIN].right - 10; + TG_Lines = NULL; + TG_SortedList = NULL; + TG_NumLines = 0; + memset(&TG_TT_dest,0,sizeof(tToolTipData)); + memset(&TG_TT_curr,0,sizeof(tToolTipData)); + + int bm_handle = bm_AllocLoadFileBitmap("TelcomToolTip.ogf",0); + if(bm_handle<=BAD_BITMAP_HANDLE) + { + bm_handle = bm_AllocBitmap(32,32,0); + if(bm_handle>BAD_BITMAP_HANDLE) + { + ushort *data = bm_data(bm_handle,0); + for(int i=0;i<1024;i++) + { + data[i] = GR_RGB16(0,0,0)|OPAQUE_FLAG; + } + } + } + + bm_CreateChunkedBitmap(bm_handle,&TG_Tooltipbitmap); + + TCTEXTDESC textdesc; + textdesc.type = TC_TEXT_STATIC; + textdesc.font = BG_FONT; + textdesc.caps = TCTD_TEXTBOX|TCTD_COLOR|TCTD_FONT; + textdesc.textbox.left = 0; + textdesc.textbox.right = 380; + textdesc.textbox.top = 4; + textdesc.textbox.bottom = 50; + textdesc.color = GR_RGB(255,255,255); + + CreateTextEffect(&textdesc,TXT_GOALSTATUSHEADING,MONITOR_TOP,0); + TelcomEndScreen(); + TelcomRenderSetScreen(0); + + TelcomRenderSetCallback(TCGoalsRenderCallback); + + TCGoalsBuildLineData(); + + TG_CurrObjectiveArrow = bm_AllocLoadFileBitmap("CurrObjectiveArrow.ogf",0); + + while(!done){ + Sound_system.BeginSoundFrame(Telcom_called_from_game); + + if(tcs->state!=TCS_POWERON || tcs->current_status!=TS_GOALS){ + //we're done with the loop + done = true; + } + + //Process all waiting events for the TelCom (we may only want to handle this once a frame!) + TelComHandleAllEvents(&Telcom_system); + + TelcomRenderScreen(); + Descent->defer(); + if(KEY_STATE(KEY_ESC)) + Telcom_system.state = TCS_POWEROFF; + + Sound_system.EndSoundFrame(); + } + + DestroyAllScreens(); + TelcomRenderSetScreen(DUMMY_SCREEN); + TelcomRenderSetCallback(NULL); + + if(TG_Lines) + { + mem_free(TG_Lines); + TG_Lines = NULL; + } + if(TG_SortedList) + { + mem_free(TG_SortedList); + TG_SortedList = NULL; + } + TG_NumLines = 0; + + if(TG_CurrObjectiveArrow>BAD_BITMAP_HANDLE) + bm_FreeBitmap(TG_CurrObjectiveArrow); + + bm_DestroyChunkedBitmap(&TG_Tooltipbitmap); + + return true; +} diff --git a/Descent3/TelComGoals.h b/Descent3/TelComGoals.h new file mode 100644 index 000000000..3e08e0604 --- /dev/null +++ b/Descent3/TelComGoals.h @@ -0,0 +1,29 @@ +/* +* $Logfile: /DescentIII/main/TelComGoals.h $ +* $Revision: 2 $ +* $Date: 4/14/99 3:57a $ +* $Author: Jeff $ +* +* TelCom Goal Status screen +* +* $Log: /DescentIII/main/TelComGoals.h $ + * + * 2 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 1 2/20/99 4:31p Jeff +* +* $NoKeywords: $ +*/ + + +#ifndef __TCGOALS_H_ +#define __TCGOALS_H_ +#include "TelCom.h" + +// This is the function called by TelCom +// return true if TelCom should exit to TelCom Main Menu +// return false if you should exit out of TelCom completly +bool TelComGoalStatus(tTelComInfo *tcs); + +#endif \ No newline at end of file diff --git a/Descent3/TelcomEffectsRender.cpp b/Descent3/TelcomEffectsRender.cpp new file mode 100644 index 000000000..86064d296 --- /dev/null +++ b/Descent3/TelcomEffectsRender.cpp @@ -0,0 +1,1037 @@ +/* +* $Logfile: /DescentIII/main/TelcomEffectsRender.cpp $ +* $Revision: 15 $ +* $Date: 3/20/00 12:21p $ +* $Author: Matt $ +* +* +* +* $Log: /DescentIII/main/TelcomEffectsRender.cpp $ + * + * 15 3/20/00 12:21p Matt + * Merge of Duane's post-1.3 changes. + * Minor optimization. + * + * 14 7/28/99 3:41p Kevin + * Macintosh! + * + * 13 5/17/99 1:46p Ardussi + * changed to compile on Mac + * + * 12 5/03/99 1:16p Jeff + * play sounds at full volume + * + * 11 4/20/99 12:46p Jeff + * telcom main menu, mouse over button sets focus. if you go into telcom + * main menu, when you leave a system it will return you to main menu. + * + * 10 4/14/99 3:57a Jeff + * fixed case mismatch in #includes +* +* $NoKeywords: $ +*/ +#include "TelComEffects.h" +#include "textaux.h" +#include "mem.h" +#include "hlsoundlib.h" +#include + +#include + + +int glitch_dx = 0,glitch_dy = 0; +extern tceffect TCEffects[MAX_TCEFFECTS]; +extern int Screen_roots[MAX_TELCOM_SCREENS]; +#define DISABLED_TEXT_COLOR GR_RGB(180,180,180); +int GetTextEffectWithTabStopsOnScreen(int screen) +{ + int count = 0; + ASSERT(screen>=0 && screentype==EFX_TEXT_STATIC); + ASSERT(tce->text_buffer); + //See if we have any events waiting + tTCEvent evt; + while(PopEvent(tce,&evt)){ + switch(evt.id){ + case TEVT_SCROLLDOWN: + if(tce->textinfo.scroll_d){ + //mprintf((0,"Down Baby Down!\n")); + tce->textinfo.line_index++; + }break; + case TEVT_SCROLLUP: + if(tce->textinfo.line_index>0){ + //mprintf((0,"Up Baby Up!\n")); + tce->textinfo.line_index--; + }break; + case TEVT_GAINFOCUS: + { + //mprintf((0,"Text %d Gain Focus\n",tce-TCEffects)); + }break; + case TEVT_LOSTFOCUS: + { + //mprintf((0,"Text %d Lost Focus\n",tce-TCEffects)); + }break; + } + } + ddgr_color color_to_use = tce->color; + int tab_count = GetTextEffectWithTabStopsOnScreen(tce->screen); + if(tab_count>1){ + //we need to see what color to use for the text, cause there is more than + //one text item with a tab stop. + if(!tce->has_focus){ + //use the disable text color + color_to_use = DISABLED_TEXT_COLOR; + } + } + ubyte old_alpha = grtext_GetAlpha(); + grtext_SetFont(Game_fonts[tce->textinfo.font_index]); + grtext_SetColor(color_to_use); + grtext_SetAlpha(tce->alpha); + int curry = tce->pos_y + yoff; + int height = grfont_GetHeight(Game_fonts[tce->textinfo.font_index]) + 1; + char buff[256]; + char *nextline; + int count = 0; //get to the first line of text (scrolling) + nextline = textaux_CopyTextLine(tce->text_buffer,buff); + if((tce->textinfo.line_index)&&(nextline)){ + count++; + while(count<=tce->textinfo.line_index){ + nextline = textaux_CopyTextLine(nextline,buff); + count++; + } + } + int lcount = 0; + while((nextline)&&((curry+height)pos_y+tce->h) ) + { + grtext_Printf(tce->pos_x+glitch_dx+xoff,curry+glitch_dy,buff); lcount++; + curry += height; + nextline = textaux_CopyTextLine(nextline,buff); + } + grtext_Printf(tce->pos_x+glitch_dx+xoff,curry+glitch_dy,buff); lcount++; + grtext_SetAlpha(old_alpha); + + if( (lcount * height) > tce->h ){ + //we can scroll down + tce->textinfo.scroll_d = true; + }else{ + //we can't scroll down + tce->textinfo.scroll_d = false; + } + tce->age += frametime; +} +void RenderTextType(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_TEXT_TYPE); + ASSERT(tce->text_buffer); + //See if we have any events waiting + tTCEvent evt; + while(PopEvent(tce,&evt)){ + switch(evt.id){ + case TEVT_SCROLLDOWN: + if(tce->textinfo.scroll_d){ + //mprintf((0,"Down Baby Down!\n")); + tce->textinfo.line_index++; + }break; + case TEVT_SCROLLUP: + if(tce->textinfo.line_index>0){ + //mprintf((0,"Up Baby Up!\n")); + tce->textinfo.line_index--; + }break; + case TEVT_GAINFOCUS: + { + //mprintf((0,"Text %d Gain Focus\n",tce-TCEffects)); + }break; + case TEVT_LOSTFOCUS: + { + //mprintf((0,"Text %d Lost Focus\n",tce-TCEffects)); + }break; + } + } + if( (!tce->text_buffer) || (tce->text_buffer[0]=='\0') ) + return; + float flash_step; + ddgr_color hi_color = tce->color;//@JEFF: GR_RGB(255,255,255); + int strlength = strlen(tce->text_buffer); + if(tce->textinfo.last_letter>=strlength-1){ + TelcomStopSound(TCSND_TYPING); + } + flash_step = frametime * (((float)strlength)/tce->speed); + char *temp; + if(tce->textinfo.last_lettertextinfo.last_letter+=flash_step; + if(tce->textinfo.last_letter>strlength) + tce->textinfo.last_letter = strlength; + int ll = (int)tce->textinfo.last_letter; + temp = (char *)mem_malloc(strlength+10); + if(!temp){ + tce->age+=frametime; + return; + } + strcpy(temp,tce->text_buffer); + temp[ll] = '\0'; + + ubyte old_alpha = grtext_GetAlpha(); + grtext_SetFont(Game_fonts[tce->textinfo.font_index]); + char buff[1024]; + char *nextline; + int height = grfont_GetHeight(Game_fonts[tce->textinfo.font_index]) + 1; + int curry = tce->pos_y; + ddgr_color color_to_use = hi_color; + int tab_count = GetTextEffectWithTabStopsOnScreen(tce->screen); + if(tab_count>1){ + //we need to see what color to use for the text, cause there is more than + //one text item with a tab stop. + if(!tce->has_focus){ + //use the disable text color + color_to_use = DISABLED_TEXT_COLOR; + } + } + grtext_SetColor(color_to_use); + grtext_SetAlpha(255.0); + + int count = 0; //get to the first line of text (scrolling) + nextline = textaux_CopyTextLine(temp,buff); + if((tce->textinfo.line_index)&&(nextline)){ + count++; + while(count<=tce->textinfo.line_index){ + nextline = textaux_CopyTextLine(nextline,buff); + count++; + } + } + int lcount = 0; + while((nextline)&&((curry+height)<=tce->pos_y+tce->h) ){ + grtext_Printf(tce->pos_x+xoff+glitch_dx,curry+yoff+glitch_dy,buff); lcount++; + curry += height; + nextline = textaux_CopyTextLine(nextline,buff); + } + grtext_Printf(tce->pos_x+xoff+glitch_dx,curry+yoff+glitch_dy,buff); lcount++; + if( (lcount * height) > tce->h ){ + //we can scroll down + tce->textinfo.scroll_d = true; + }else{ + //we can't scroll down + tce->textinfo.scroll_d = false; + } + /* + if( ll>2 ){ + grtext_SetColor(tce->color); //!<<_---------USE CORRECT COLOR HERE WHEN IMPLEMENTED + if(lltext_buffer); + temp[11]='\0'; + } + curry = tce->pos_y; + count = 0; //get to the first line of text (scrolling) + nextline = textaux_CopyTextLine(temp,buff); + if((tce->textinfo.line_index)&&(nextline)){ + count++; + while(count<=tce->textinfo.line_index){ + nextline = textaux_CopyTextLine(nextline,buff); + count++; + } + } + while((nextline)&&((curry+height)<=tce->pos_y+tce->h) ){ + grtext_Printf(tce->pos_x+xoff+glitch_dx,curry+yoff+glitch_dy,buff); + curry += height; + nextline = textaux_CopyTextLine(nextline,buff); + } + grtext_Printf(tce->pos_x+xoff+glitch_dx,curry+yoff+glitch_dy,buff); + } + //draw scroll buttons now (if needed) + //if m_iIndex!=0 then we can scroll up + if(tce->textinfo.line_index){ + tce->textinfo.scroll_u = true; + }else + tce->textinfo.scroll_u = false; + //if curry+height>m_iBottomY then we can scroll down + if( (curry+height)>tce->pos_y+tce->h){ + tce->textinfo.scroll_d = true; + } + else + tce->textinfo.scroll_d = false; + */ + grtext_SetAlpha(old_alpha); + mem_free(temp); + tce->age += frametime; +} +void RenderTextFade(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_TEXT_FADE); + ASSERT(tce->text_buffer); + //See if we have any events waiting + tTCEvent evt; + while(PopEvent(tce,&evt)){ + switch(evt.id){ + case TEVT_SCROLLDOWN: + if(tce->textinfo.scroll_d){ + //mprintf((0,"Down Baby Down!\n")); + tce->textinfo.line_index++; + }break; + case TEVT_SCROLLUP: + if(tce->textinfo.line_index>0){ + //mprintf((0,"Up Baby Up!\n")); + tce->textinfo.line_index--; + }break; + case TEVT_GAINFOCUS: + { + //mprintf((0,"Text %d Gain Focus\n",tce-TCEffects)); + }break; + case TEVT_LOSTFOCUS: + { + //mprintf((0,"Text %d Lost Focus\n",tce-TCEffects)); + }break; + } + } + + + if(tce->speed==0) + tce->speed = 0.0001f; + float amount = tce->age * (255.0/tce->speed); + unsigned char alpha; + if(tce->flags==TC_TEXTF_IN){ + //fade in, but not fade out + tce->alpha += amount; + if(tce->alpha>255.0f){ + tce->alpha = 255.0f; + } + alpha = tce->alpha; + }else + if(tce->flags==TC_TEXTF_OUT){ + //fade out, but not fade in + tce->alpha -= amount; + if(tce->alpha<0.0f){ + tce->alpha = 0.0f; + } + alpha = tce->alpha; + }else{ + //fade in, then fade out (if alpha is negative, then we are fading out) + if(tce->alpha<0.0f){ + tce->alpha += amount; + alpha = (-tce->alpha); + if(tce->alpha>0.0f){ + //we're done fading out + tce->alpha = 1234.0f; + alpha = 0; + } + }else + if(tce->alpha!=1234.0f){ + tce->alpha += amount; + alpha = tce->alpha; + if(tce->alpha>255.0f){ + //switch to fading out + alpha = 255; + tce->alpha = -255.0f; + } + }else + alpha = 0; + } + ddgr_color color_to_use = tce->color; + int tab_count = GetTextEffectWithTabStopsOnScreen(tce->screen); + if(tab_count>1){ + //we need to see what color to use for the text, cause there is more than + //one text item with a tab stop. + if(!tce->has_focus){ + //use the disable text color + color_to_use = DISABLED_TEXT_COLOR; + } + } + ubyte old_alpha = grtext_GetAlpha(); + grtext_SetFont(Game_fonts[tce->textinfo.font_index]); + grtext_SetColor(color_to_use); + grtext_SetAlpha(alpha); + int curry = tce->pos_y; + int height = grfont_GetHeight(Game_fonts[tce->textinfo.font_index]) + 1; + char buff[256]; + char *nextline; + int count = 0; //get to the first line of text (scrolling) + nextline = textaux_CopyTextLine(tce->text_buffer,buff); + if((tce->textinfo.line_index)&&(nextline)){ + count++; + while(count<=tce->textinfo.line_index){ + nextline = textaux_CopyTextLine(nextline,buff); + count++; + } + } + int lcount = 0; + while((nextline)&&((curry+height)pos_y+tce->h) ){ + grtext_Printf(tce->pos_x+glitch_dx+xoff,curry+glitch_dy+yoff,buff);lcount++; + curry += height; + nextline = textaux_CopyTextLine(nextline,buff); + } + grtext_Printf(tce->pos_x+glitch_dx+xoff,curry+glitch_dy+yoff,buff);lcount++; + grtext_SetAlpha(old_alpha); + if( (lcount * height) > tce->h ){ + //we can scroll down + tce->textinfo.scroll_d = true; + }else{ + //we can't scroll down + tce->textinfo.scroll_d = false; + } + tce->age += frametime; +} +void RenderBmpStatic(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_BMP_STATIC); + BltBmpToScreen(tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff,&tce->bmpinfo.chunk_bmp); + tce->age += frametime; +} +void BlurBitmapArea(ushort* srcbm,ushort *dstbm,short width,short height,short startx,short starty,short bmw) +{ + int pixel_count,blue_total,red_total,green_total; + pixel_count=width*height; + blue_total=red_total=green_total=0; + short x,y; + short pos; + if(!pixel_count) + return; + + for(y=starty;ytype==EFX_BMP_BLUR); + if(!ok_to_render){ + if(tce->flags==TC_BMPF_OUT && (!(tce->flags&TC_NOEARLYRENDER)) ){ + UploadChunk(&tce->bmpinfo.chunk_bmp); + BltBmpToScreen(tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff,&tce->bmpinfo.chunk_bmp); + } + return; + } + + if( (tce->flags==TC_BMPF_OUT) && (tce->bmpinfo.stage<=0) ) + return; + int sbm_handle=tce->bmpinfo.bm_handle; + int dbm_handle=tce->bmpinfo.temp_bmhandle; + bm_ClearBitmap(dbm_handle); + int x=0; + int y=0; + bool done = false; + if( (tce->flags==TC_BMPF_OUT) && (tce->bmpinfo.stage<0) ) + done = true; + if( (tce->flags==TC_BMPF_IN) && (tce->bmpinfo.stage>BLUR_STAGES) ) + done = true; + + ushort *src,*dest; + int rowsize,w,h; + + if(!done){ + src = bm_data(sbm_handle,0); + dest = bm_data (dbm_handle,0); + rowsize = bm_w(sbm_handle,0); + w = bm_w(sbm_handle,0); + h = bm_h(sbm_handle,0); + int current_stage; + int block_size; + current_stage = tce->bmpinfo.stage; + block_size = 1<<(BLUR_STAGES-current_stage); + int x=0,y=0,bs_w,bs_h; + + for(x=0;xw) + bs_w = w-x; + else + bs_w = block_size; + if(y+block_size>h) + bs_h = h-y; + else + bs_h = block_size; + BlurBitmapArea(src,dest,bs_w,bs_h,x,y,w); + } + int dim = bm_w(tce->bmpinfo.chunk_bmp.bm_array[0],0); + int how_many_across = tce->bmpinfo.chunk_bmp.w; + int bh = tce->bmpinfo.chunk_bmp.ph; + int bw = tce->bmpinfo.chunk_bmp.pw; + ushort *src_data=bm_data(dbm_handle,0); + for (y=0;ybmpinfo.chunk_bmp.bm_array[piece_y*how_many_across+piece_x]; + ushort *dest_data=bm_data(bm,0); + ushort pix=src_data[y*bw+x]; + dest_data[sub_y*dim+sub_x]=pix; + } + } + UploadChunk(&tce->bmpinfo.chunk_bmp); + BltBmpToScreen(tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff,&tce->bmpinfo.chunk_bmp); + + float amt = tce->speed*frametime; + if(tce->flags==TC_BMPF_IN) + tce->bmpinfo.stage += amt; + else + tce->bmpinfo.stage -= amt; + } + else{ + BltBmpToScreen(tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff,&tce->bmpinfo.chunk_bmp); + } + tce->age += frametime; +} +#define N_FADE_STAGES 4 +void RenderBmpScanline(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + ASSERT(tce->type==EFX_BMP_SCANLINE); + if(!ok_to_render){ + if(tce->flags==TC_BMPF_OUT && (!(tce->flags&TC_NOEARLYRENDER)) ){ + UploadChunk(&tce->bmpinfo.chunk_bmp); + BltBmpToScreen(tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff,&tce->bmpinfo.chunk_bmp); + } + return; + } + + bool changed = true; + bool done; + int x,y; + int sbm_handle=tce->bmpinfo.bm_handle; + int dbm_handle=tce->bmpinfo.temp_bmhandle; + ushort *src,*dest; + int rowsize,w,h,xx,yy,stage; + float k; + if(dbm_handle==BAD_BITMAP_HANDLE) + goto done_scanline; + bm_ClearBitmap(dbm_handle); + + x=0;y=0;done = false; + + k= tce->age/tce->speed; + + if (k > 1.2) + goto done_scanline; + if (k > 1.0) { + k = 1.0; + done = 1; + } + if(tce->flags==TC_BMPF_OUT){ + k = 1.0 - k; + } + src = bm_data(sbm_handle,0); + dest = bm_data (dbm_handle,0); + rowsize = bm_w(sbm_handle,0); + w = bm_w(sbm_handle,0); + h = bm_h(sbm_handle,0); + for (yy=stage=0;yy w) + span_width = w; + for (xx=0;xxbmpinfo.chunk_bmp.bm_array[0],0); + int how_many_across = tce->bmpinfo.chunk_bmp.w; + int bh = tce->bmpinfo.chunk_bmp.ph; + int bw = tce->bmpinfo.chunk_bmp.pw; + ushort *src_data=bm_data(dbm_handle,0); + int x,y; + for (y=0;ybmpinfo.chunk_bmp.bm_array[piece_y*how_many_across+piece_x]; + ushort *dest_data=bm_data(bm,0); + ushort pix=src_data[y*bw+x]; + dest_data[sub_y*dim+sub_x]=pix; + } + } + UploadChunk(&tce->bmpinfo.chunk_bmp); + } +done_scanline: + BltBmpToScreen(tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff,&tce->bmpinfo.chunk_bmp); + tce->age += frametime; +} +void RenderBmpInvert(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + ASSERT(tce->type==EFX_BMP_INVERT); + int srcbmph = tce->bmpinfo.bm_handle; + bool changed = true; + if(!ok_to_render && tce->flags&TC_NOEARLYRENDER) + return; + float k; + + if(ok_to_render){ + k = tce->age/tce->speed; + }else{ + k = 0; + } + int inv; + if (k > 1.0) { + k = 1.0; + changed = false; + }else + if (k < 0){ + mprintf((0,"k<0 on inverse bitmap...bashing (%.2f/%.2f)\n",tce->age,tce->speed)); + if( tce->age < 0 ) + Int3(); + if( tce->speed < 0 ) + tce->speed = 0.1f; + k = 0; + changed = false; + } + if(tce->flags==TC_BMPF_OUT) + k = 1-k; + inv = 255 - (255.0f*k); + ubyte r,g,b; + int bh = bm_h(srcbmph,0); + int bw = bm_w(srcbmph,0); + int how_many = tce->bmpinfo.chunk_bmp.w * tce->bmpinfo.chunk_bmp.h; + int how_many_across = tce->bmpinfo.chunk_bmp.w; + int dim = bm_h(tce->bmpinfo.chunk_bmp.bm_array[0],0); + int shift; //speed up (since dim is always a power of 2) + switch(dim) + { + case 32: + shift = 5; + break; + case 64: + shift = 6; + break; + case 128: + shift = 7; + break; + } + ushort *src_data=bm_data(srcbmph,0); + ushort *sdata; + ushort *ddata; + ushort pix; + int maxx,maxy; + int windex,hindex; + int s_y,s_x,d_y,d_x; + //is there any need to do this if !changed!?!? + for(hindex=0;hindexbmpinfo.chunk_bmp.h;hindex++){ + for(windex=0;windexbmpinfo.chunk_bmp.w;windex++){ + //loop through the chunks + //find end x and y + if(windexbmpinfo.chunk_bmp.w-1) + maxx=dim; + else + maxx = bw - (windex<bmpinfo.chunk_bmp.h-1) + maxy=dim; + else + maxy = bh - (hindex<bmpinfo.chunk_bmp.bm_array[hindex*how_many_across+windex],0); + sdata = &src_data[s_y * bw + s_x]; + //copy the data + for(d_y=0;d_ybmpinfo.chunk_bmp); + BltBmpToScreen(tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff,&tce->bmpinfo.chunk_bmp); + if(ok_to_render) + tce->age+=frametime; +} +void RenderBmpStretch(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + ASSERT(tce->type==EFX_BMP_STRETCH); + if(!ok_to_render){ + return; + } + + float k = (tce->age) / tce->speed; + if (k > 1.0) { + k = 1.0; + }else + if (k < 0){ + k = 0; + } + if(tce->flags==TC_BMPF_OUT) + k= 1-k; + int w,h; + //width + //h = 64; + //w = k*64.0; + //height + //w = 64; + //h = k*64.0; + //width & height + h = w = (k*64.0); + int src_w = bm_w(tce->bmpinfo.bm_handle,0); + int src_h = bm_h(tce->bmpinfo.bm_handle,0); + int realx,realy; + int w_count = (src_w+63)/64; + int h_count = (src_h+63)/64; + float tw = ((float)k*src_w)/((float)w_count); + float th = ((float)k*src_h)/((float)h_count); + int startx = tce->pos_x + (src_w/2)-(((float)w_count)*tw/2.0f); + int starty = tce->pos_y + (src_h/2)-(((float)h_count)*th/2.0f); + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (255.0); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + int dx,dy; + int index; + index = 0; + for(dy=0,realy=starty;dybmpinfo.bitmaps[index],0,0,1,1); + index++; + } + rend_SetFiltering(1); + tce->age+=frametime; +} +void RenderBackground(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_BACKGROUND); + int lx,rx,ty,by; + lx = tce->pos_x + xoff; + rx = tce->pos_x + xoff + tce->w; + ty = tce->pos_y + yoff; + by = tce->pos_y + yoff + tce->h; + rend_FillRect(tce->color,lx,ty,rx,by); + tce->age += frametime; +} +void RenderMovie(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_MOVIE); + + if(!tce->movieinfo.handle) + return; + bool done = false; + FrameMovie(tce->movieinfo.handle,tce->pos_x+glitch_dx,tce->pos_y+glitch_dy,false); + float end_time = tce->age + frametime; + while( (end_time>tce->age) && (!done) ){ + if(!FrameMovie(tce->movieinfo.handle,tce->pos_x+glitch_dx+xoff,tce->pos_y+glitch_dy+yoff)){ + /* + if(IsLooping()) //looping? + { + EndMovie(m_MovieHandle); + m_MovieHandle = StartMovie(m_szMovieName); + } + else + { + EndMovie(m_MovieHandle); + m_MovieHandle = NULL; + done = true; + }*/ + EndMovie(tce->movieinfo.handle); + tce->movieinfo.handle = NULL; + done = true; + } + else + tce->age += 1.0f/tce->movieinfo.fps; + } +} +void RenderPolyModel(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + /* + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_POLYMODEL); + if(tce->polyinfo.handle==-1) + return false; + float k = tce->age; + //setup how much we're gonna rotate + float xrot = k*tce->polyinfo.m_Rot.x + tce->polyinfo.m_Ori.x; + float yrot = k*tce->polyinfo.m_Rot.y + tce->polyinfo.m_Ori.y; + float zrot = k*tce->polyinfo.m_Rot.z + tce->polyinfo.m_Ori.z; + //fade in speed 2.0f + k*=(1.0f/2.0f); + if (k > 1.3) { + k = 1.3f; + } + else + if (k < 0){ + k = 0.0f; + } + poly_model *pm=GetPolymodelPointer(tce->polyinfo.handle); + vector view_vec; + matrix view_orient; + float light_scalar; + //set all animation to default position + float norm_angles[MAX_SUBOBJECTS]; + for (int i=0;ipolyinfo.m_Pos.x + (((float)glitch_dx)/tce->polyinfo.m_Pos.z)); + pos.y = - (tce->polyinfo.m_Pos.y + (((float)glitch_dy)/tce->polyinfo.m_Pos.z)); + pos.z = - tce->polyinfo.m_Pos.z; + + g3_StartFrame (&pos,&view_orient,D3_DEFAULT_ZOOM); + //shed some light on the subject + light_scalar = 0.86f; + //we need to rotate our model about Y 180 degrees so it faces the camera + vm_AnglesToMatrix(&view_orient,xrot,yrot,zrot); + tce->polyinfo.m_mOrient += view_orient; + vm_Orthogonalize(&tce->polyinfo.m_mOrient); + + //draw the polymodel with our little fade in effect + polymodel_effect pe={0}; + pe.type=PEF_ALPHA; + if(k>=0.3) + pe.alpha=k-0.3f; + else + pe.alpha=0.005f; + SetPolymodelEffect(&pe); + vm_MakeZero (&view_vec); + rend_SetZBufferState(1); + DrawPolygonModel(&view_vec,&tce->polyinfo.m_mOrient,tce->polyinfo.handle,norm_angles,0,light_scalar,light_scalar,light_scalar,0xFFFFFFFF,1); + //DrawPolygonModel(&m_Pos,&m_mOrient,m_iHandle,norm_angles,0,light_scalar,0xFFFFFFFF,1); + if(k<1) + DrawModelOutline(GR_RGB(255,255,255),1.0-k); + g3_EndFrame(); + rend_SetZBufferState(0); + */ + tce->age+=frametime; +} +void RenderSound(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_SOUND); + tce->age += frametime; + if(tce->soundinfo.started) + return; + tce->soundinfo.started = true; + if(tce->soundinfo.handle!=-1) + Sound_system.Play2dSound(tce->soundinfo.handle,MAX_GAME_VOLUME); +} +#define FLASH_SWITCH 1.0f +extern tceffect TCEffects[MAX_TCEFFECTS]; +void RenderButton(tceffect *tce,float frametime,int xoff,int yoff,bool ok_to_render) +{ + if(!ok_to_render) + return; + ASSERT(tce->type==EFX_BUTTON); + tTCEvent evt; + bool time_to_flash = false; + bool glow = false; + while(PopEvent(tce,&evt)){ + switch(evt.id){ + case TEVT_GAINFOCUS: + { + int num = tce - TCEffects; + //mprintf((0,"Button: Recv'd GAIN FOCUS Event (%d)\n",num)); + }break; + case TEVT_LOSTFOCUS: + { + int num = tce - TCEffects; + //mprintf((0,"Button: Recv'd LOST FOCUS Event (%d)\n",num)); + }break; + case TEVT_MOUSEOVER: + { + if(tce->flags & OBF_GLOW) + { + if(tce->flags & OBF_MOUSEOVERFOCUS) + { + //for effects with this flag set, only glow if we have focus + if(tce->tab_stop && tce->has_focus) + { + time_to_flash = true; + glow = true; + } + }else + { + time_to_flash = true; + glow = true; + } + } + }break; + case TEVT_MOUSEENTER: + { + if(tce->flags & OBF_MOUSEOVERFOCUS){ + //check to see if we have focus, if not, and we are a tab, set focus + if(tce->tab_stop && !tce->has_focus) + { + //set the focus on us! + TelComSetFocusOnEffect(tce-TCEffects); + } + } + }break; + case TEVT_MOUSECLICK: + case TEVT_MOUSEDOWN: + case TEVT_MOUSEUP: + { + //send out the event to the appropriate system/effect + switch(tce->buttoninfo.button_type){ + case BUTT_UPARROW: + { + //mprintf((0,"Button: Telling Text to Scroll Up\n")); + int efxnum = GetEfxNumFromID(tce->buttoninfo.parent,tce->screen); + if(efxnum!=-1){ + SendEventToEffect(efxnum,TEVT_SCROLLUP); + } + }break; + case BUTT_DOWNARROW: + { + //mprintf((0,"Button: Telling Text to Scroll Down\n")); + int efxnum = GetEfxNumFromID(tce->buttoninfo.parent,tce->screen); + if(efxnum!=-1){ + SendEventToEffect(efxnum,TEVT_SCROLLDOWN); + } + }break; + case BUTT_NEXTPAGE: + //mprintf((0,"Button: Telling TelCom to go to Next Page\n")); + TelComSendEvent(TEVT_TCNEXT); + break; + case BUTT_PREVPAGE: + //mprintf((0,"Button: Telling TelCom to go to Prev Page\n")); + TelComSendEvent(TEVT_TCPREV); + break; + case BUTT_QUIT: + //mprintf((0,"Button: Telling TelCom to go to Quit\n")); + TelComSendEvent(TEVT_TCQUIT); + break; + case BUTT_JUMP: + //mprintf((0,"Button: Telling TelCom to jump to %d\n",tce->buttoninfo.jump_page)); + TelComSendEvent(TEVT_TCJUMP,tce->buttoninfo.jump_page); + break; + case BUTT_INTERNAL: + //mprintf((0,"Button: Calling internal callback\n")); + if(tce->buttoninfo.internal) + tce->buttoninfo.internal((int)(tce-TCEffects)); + break; + default: + mprintf((0,"Button: Invalid Button Type\n")); + Int3(); //invalid button type + //missing JUMP!!!!!!!!!!!!!!!! + }; + }break; + default: + { + mprintf((0,"Button: Recv'd Evt %d\n",evt.id)); + }break; + }; + } + tce->age += frametime; + if(tce->age>FLASH_SWITCH){ + tce->buttoninfo.flash_state = !tce->buttoninfo.flash_state; + tce->age = 0; + } + if(tce->buttoninfo.flash_state && tce->flags & OBF_FLASH) + { + time_to_flash = true; + } + if(tce->buttoninfo.flash_time>0) + tce->buttoninfo.flash_time -= frametime; + if(tce->buttoninfo.flash_time>0 && !glow ) + time_to_flash = false; + if(tce->buttoninfo.flash_state && (tce->buttoninfo.button_type==BUTT_UPARROW||tce->buttoninfo.button_type==BUTT_DOWNARROW)) + { + //this is an up/down arrow check to see if we can scroll in the appropriate direction + if(tce->buttoninfo.parent>-1) + { + int pid = GetEfxNumFromID(tce->buttoninfo.parent,tce->screen); + ASSERT(pid!=-1); + if(pid!=-1) + { + tceffect *t = &TCEffects[pid]; + ASSERT(t->type==EFX_TEXT_STATIC||t->type==EFX_TEXT_TYPE||t->type==EFX_TEXT_FADE); + switch(tce->buttoninfo.button_type) + { + case BUTT_UPARROW: + { + if(t->textinfo.line_index>0){ + time_to_flash = true; + } + } + break; + case BUTT_DOWNARROW: + { + if(t->textinfo.scroll_d){ + time_to_flash = true; + } + } + break; + } + } + } + } + if(tce->buttoninfo.flash_handle==-1 && time_to_flash) + time_to_flash = false; + if(tce->has_focus){ + BltBmpToScreen(tce->pos_x+glitch_dx,tce->pos_y+glitch_dy,(time_to_flash)?&tce->buttoninfo.flashfocus_chunk:&tce->buttoninfo.chunkfocus_bmp); + }else{ + BltBmpToScreen(tce->pos_x+glitch_dx,tce->pos_y+glitch_dy,(time_to_flash)?&tce->buttoninfo.flash_chunk:&tce->buttoninfo.chunk_bmp); + } +} diff --git a/Descent3/TerrainSearch.cpp b/Descent3/TerrainSearch.cpp new file mode 100644 index 000000000..dcdf09ce6 --- /dev/null +++ b/Descent3/TerrainSearch.cpp @@ -0,0 +1,966 @@ +/* + * $Logfile: /DescentIII/Main/TerrainSearch.cpp $ + * $Revision: 56 $ + * $Date: 12/27/99 7:48a $ + * $Author: Gwar $ + * + * + * + * $Log: /DescentIII/Main/TerrainSearch.cpp $ + * + * 56 12/27/99 7:48a Gwar + * added NEWEDITOR #ifdefs + * + * 55 10/12/99 10:19a Gwar + * adding terrain support to NEWEDITOR + * + * 54 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 53 4/27/99 3:38p Jason + * fixed occlusion bug + * + * 52 4/16/99 12:37a Matt + * Made Windows & Linux versions use the same qsort calls. + * + * 51 4/15/99 1:43a Jeff + * changes for linux compile + * + * 50 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 49 4/12/99 3:04p Jason + * changes for low memory + * + * 48 3/04/99 6:12p Jason + * fixed bug with terrain lod + * + * 47 3/04/99 4:53p Jason + * added LODOffs + * + * 46 2/06/99 10:00p Jason + * fixed bug with last rev + * + * 45 2/06/99 7:55p Jason + * Misc speedups + * + * 44 1/20/99 10:50a Jason + * added new terrain + * + * 43 9/30/98 8:05p Jason + * Don't do occlusion test if inside + * + * 42 9/29/98 10:49a Jason + * fixed invisible terrain problelm + * + * 41 9/25/98 9:25p Jason + * did some graphics optimizations + * + * 40 8/19/98 2:19p Jeff + * moved detail globals to a struct + * + * 39 6/15/98 12:10p Jason + * made SearchQuadTree intelligently bail if we run out of cell buffers + * + * 38 6/04/98 6:45p Jason + * made objects use the terrain occlusion system + * + * 37 6/04/98 6:17p Jason + * added terrain occlusion system + * + * 36 5/14/98 12:56p Jason + * changes to help lower memory usage + * + * 35 4/30/98 6:45p Jason + * more changes for weather + * + * 34 4/27/98 1:15p Jason + * made lod engine use current fov + * + * 33 4/15/98 8:39p Jason + * inlined some functions + * + * 32 3/17/98 5:02p Jason + * sped up rendering the terrain from the mine by a large amount + * + * 31 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 30 1/30/98 12:12p Jason + * fixed LOD bug. This gives us twice the framerate in hilly areas!!! + * + * 29 1/12/98 6:01p Jason + * fixed memory corruption bug + * + * 28 1/12/98 3:34p Jason + * sped up indoor rendering by clipping faces against portals + * + * 27 1/02/98 7:06p Jason + * fixed mprintf + * + * 26 10/10/97 6:31p Jason + * added extra level of detail level + * + * 25 10/03/97 1:17p Jason + * tweaked to get more speed + * + * 24 10/02/97 6:45p Jason + * optimized terrain rotation + * + * 23 9/19/97 1:02p Chris + * Better big object support + * + * 22 9/17/97 6:29p Jason + * took out qsorting + * + * 21 9/15/97 9:09p Chris + * Make invalid locations for GetColTerrainSegFromPos link to zero and not + * error with a -1 + * + * 20 9/15/97 5:35p Jason + * took out tracing drops + * + * 19 9/12/97 6:36p Chris + * Added collision terrain segment support + * Added some LARGE OBJECT collision support + * + * 18 9/02/97 10:47a Jason + * use separate variables for game and editor LOD engine status + * + * 17 8/21/97 5:15p Samir + * Put some ifdef EDITOR stuff around Drop_seg and Tracing_drop + * references. + * + * 16 8/20/97 6:18p Jason + * fixed memory corruption bug + * + * 15 8/07/97 3:19p Jason + * replaced old terrain tmap system with new one + * + * 14 8/06/97 11:43a Jason + * made lightmaps work correctly in the terrain + * + * 13 8/03/97 3:32p Jason + * sped up LodSortingFunction after VTune said this was a HotSpot + * + * 12 8/03/97 2:26p Jason + * tried to eke out a few more cycles per frame from the terrain engine + * + * 11 7/29/97 5:33p Jason + * cleaned up code + * + * 10 7/28/97 5:23p Jason + * added features to lod engine - namely dynamic texture generation + * + * 9 7/25/97 5:25p Jason + * fixed multiple bugs in terrain LOD engine + * + * 8 7/23/97 6:29p Jason + * took out annoying mprintf + * + * 7 7/23/97 6:27p Jason + * added code to support terrain simplification + * + * 6 7/18/97 7:58p Jason + * fixed small bug with dropped cells + * + * 5 7/16/97 4:07p Jason + * fixed all the remaining drop-out bugs + * + * 4 7/16/97 12:46p Jason + * fixed some bugs with terrain searching + * and freed up lots of memory + * + * 3 7/16/97 11:26a Samir + * + * 2 7/15/97 8:09p Jason + * made it faster! + * + * 23 6/25/97 1:55p Jason + * added Flat_terrain variaable for viewing terrain as a flat plane + * + * 22 6/24/97 4:47p Mark + * FROM JASON:Took out non-sorting + * + * 21 6/24/97 4:24p Jason + * changes for y only terrain position + * + * 20 6/24/97 12:49p Jason + * + * 19 5/21/97 12:29p Jason + * added the ability to draw terrain dots in mine view + * + * 18 5/11/97 1:31p Matt + * Added code to only render terrain that might be visible when terrain + * called from the mine renderer. + * + * $NoKeywords: $ + */ + +#include "terrain.h" +#include "3d.h" +#include "mono.h" +#include "vecmat.h" +#include "pserror.h" +#include "pstypes.h" +#include "descent.h" +#include "game.h" +#include "gameloop.h" +#include +#include +#include +#include "config.h" +#include "dedicated_server.h" + +#ifdef MACINTOSH +#include +#endif + +int EvaluateBlock (int x,int z,int lod); + +ushort TS_FrameCount=0xFFFF; + +int SearchQuadTree (int x1,int y1,int x2,int y2,int dir,int *ccount); +int GlobalTransCount=0; +int TotalDepth; + +// LOD shutoff stuff +#define MAX_LODOFFS 100 +lodoff LODOffs[MAX_LODOFFS]; +int Num_lodoffs=0; + +// Render the terrain as flat? +ubyte Flat_terrain=0; + +// Variables for LOD engine +float ViewDistance1=.68f,ViewDistance2=1.0f; +float TS_Lambda,TS_SimplifyCondition; + +matrix *TS_View_matrix; +vector *TS_View_position; + +vector TS_PreRows[TERRAIN_DEPTH]; +vector TS_FVectorAdd,TS_RVectorAdd; + +ubyte Terrain_y_flags[256]; +vector Terrain_y_cache[256]; + +ushort LOD_sort_bucket[MAX_TERRAIN_LOD][MAX_CELLS_TO_RENDER]; +ushort LOD_sort_num [MAX_TERRAIN_LOD]; + +// Variable to determine if we're in editor or game +extern function_mode View_mode; +extern ubyte Terrain_from_mine; + +vector TJoint_VectorAddZ; +vector TJoint_VectorAddX; + + +// Since our terrain points increment in finite steps we can rotate 2 points (x,z) +// just add them to a base point to get the final rotated point +void PreRotateTerrain () +{ + vector rvec={1*TERRAIN_SIZE,0,0}; + vector fvec={0,0,1*TERRAIN_SIZE}; + + vector tj_rvec={4,0,0}; + vector tj_fvec={0,0,4}; + + TS_FVectorAdd = fvec * *TS_View_matrix; + TS_RVectorAdd = rvec * *TS_View_matrix; + + vector start_vec = { 0.0f, 0.0f, 0.0f }; + vector temp_vec = start_vec - *TS_View_position; + start_vec = temp_vec * *TS_View_matrix; + + TS_PreRows[0] = start_vec; + + int i; + for (i=1;i=0 && yvalue<=255); + + // If the terrain is supposed to be flat, bash this to zero +#ifdef EDITOR + if(Flat_terrain) + { + yvalue = 0; + } +#endif + + *dest = TS_PreRows[z]; + *dest += TS_RVectorAdd * x; + *dest += *GetDYVector(yvalue); +} + + +void GetPreRotatedPoint(g3Point *dest,int x,int z,int yvalue) +{ + ASSERT (yvalue>=0 && yvalue<=255); + + // If the terrain is supposed to be flat, bash this to zero + #ifdef EDITOR + if(Flat_terrain) + { + yvalue = 0; + } + #endif + + dest->p3_vec = TS_PreRows[z]; + dest->p3_vec += TS_RVectorAdd * x; + dest->p3_vec += *GetDYVector(yvalue); + + // also store the unrotated point + dest->p3_flags |= PF_ORIGPOINT; + dest->p3_vecPreRot.x = TERRAIN_SIZE * x; + dest->p3_vecPreRot.y = TERRAIN_HEIGHT_INCREMENT * yvalue; + dest->p3_vecPreRot.z = TERRAIN_SIZE * z; +} + +// Gets a pre-rotated point that does not fall exactly on one of our 255 height values +void GetSpecialRotatedPoint(g3Point *dest,int x,int z,float yvalue) +{ + if (yvalue<0) + yvalue=0; + if (yvalue>MAX_TERRAIN_HEIGHT) + yvalue=MAX_TERRAIN_HEIGHT; + + vector up_vector={0,yvalue,0}; + vector dyp = up_vector * *TS_View_matrix; + + dest->p3_vec = TS_PreRows[z]; + dest->p3_vec += TS_RVectorAdd * x; + dest->p3_vec += dyp; + + // also store the unrotated point + dest->p3_flags |= PF_ORIGPOINT; + dest->p3_vecPreRot.x = TERRAIN_SIZE * x; + dest->p3_vecPreRot.y = yvalue; + dest->p3_vecPreRot.z = TERRAIN_SIZE * z; +} + +void Terrain_start_frame( vector *eye, matrix *view_orient ) +{ + TS_View_position = eye; + TS_View_matrix = view_orient; + + // Set up variables for LOD switching + int width, height; + rend_GetProjectionParameters( &width, &height ); + TS_Lambda = (float)height / (2*ViewDistance1*Render_zoom); + + #ifndef NEWEDITOR + TS_SimplifyCondition = Detail_settings.Pixel_error / (TS_Lambda*ViewDistance2); + #else + TS_SimplifyCondition = 10.0f / (TS_Lambda*ViewDistance2); + #endif + TS_SimplifyCondition *= TS_SimplifyCondition; + + // Increment TS_framecount before searching... + TS_FrameCount++; + + if (TS_FrameCount==0) + { + TS_FrameCount=1; + } + + memset (Terrain_y_flags,0,256); + memset (LOD_sort_num,0,MAX_TERRAIN_LOD*sizeof(ushort)); + + PreRotateTerrain(); +} + +// Sorts our visible terrain blocks by lod level. The lower resolution blocks +// go first - this prevents cracking + +int LodSortingFunction (const terrain_render_info *a,terrain_render_info *b) +{ + return a->lod-b->lod; +} + +void SortLodLevels(int count) +{ + qsort (Terrain_list,count,sizeof(terrain_render_info),(int (*)(const void*,const void*)) LodSortingFunction); +} +// returns number of cells in visible terrain +int GetVisibleTerrain (vector *eye,matrix *view_orient) +{ + int cellcount=0; + int i,t,count; + + GlobalTransCount=0; + TotalDepth=0; + + Terrain_start_frame(eye,view_orient); + + SearchQuadTree (0,0,TERRAIN_WIDTH,TERRAIN_DEPTH,0,&cellcount); + + // Make sure lower levels of detail come first + for (count=0,i=0;i=MAX_CELLS_TO_RENDER) + { + mprintf ((0,"Trying to render too many cells! Cell limit=%d\n",MAX_CELLS_TO_RENDER)); +#ifndef NEWEDITOR + Detail_settings.Terrain_render_distance=80.0*TERRAIN_SIZE; +#endif + return; + } + + + n=y*TERRAIN_WIDTH+x; + + + if (Show_invisible_terrain==0 && lod==MAX_TERRAIN_LOD-1 && Terrain_seg[n].flags & TF_INVISIBLE) + return; + + (*ccount)++; + + // Fill in our presorter + LOD_sort_bucket[lod][LOD_sort_num[lod]]=n; + LOD_sort_num[lod]++; + + // Fill in the join map so we know what edges touch what edges + if (lod!=MAX_TERRAIN_LOD-1) + { + simplemul=1<<((MAX_TERRAIN_LOD-1)-lod); + + + for (i=0;ipos.z/TERRAIN_SIZE)/OCCLUSION_SIZE; + int ox=(Viewer_object->pos.x/TERRAIN_SIZE)/OCCLUSION_SIZE; + + if (oz<0 || oz>=OCCLUSION_SIZE || ox<0 || ox>=OCCLUSION_SIZE) + use_occlusion=0; + + src_occlusion_index=oz*OCCLUSION_SIZE+ox; + } + + PUSH_STACK_TREE (x1,y1,x2,y2,0); + + while (si>0) + { + TotalDepth++; + + POP_STACK_TREE(); + ASSERT ((x2-x1)==(y2-y1)); + + + if ((x2-x1)==16) + { + if (use_occlusion) + { + int dest_occlusion_index=((y1/OCCLUSION_SIZE)*OCCLUSION_SIZE); + dest_occlusion_index+=x1/OCCLUSION_SIZE; + + int occ_byte=dest_occlusion_index/8; + int occ_bit=dest_occlusion_index%8; + + if (!(Terrain_occlusion_map[src_occlusion_index][occ_byte] & (1<=MAX_CELLS_TO_RENDER) + return 0; + #endif + + check_portal=0; + + continue; + } + + + // Starts at lower left, goes clockwise + n[0]=y1*TERRAIN_WIDTH+x1; + n[1]=y2*TERRAIN_WIDTH+x1; + n[2]=y2*TERRAIN_WIDTH+x2; + n[3]=y1*TERRAIN_WIDTH+x2; + + x[0]=x[1]=x1; + x[2]=x[3]=x2; + z[0]=z[3]=y1; + z[1]=z[2]=y2; + + if (x2==TERRAIN_WIDTH) + { + x[2]--; + x[3]--; + + n[2]--; + n[3]--; + + } + + if (y2==TERRAIN_DEPTH) + { + z[1]--; + z[2]--; + + n[1]-=TERRAIN_WIDTH; + n[2]-=TERRAIN_WIDTH; + } + + close=0; + first=1; + + anded=0xff; + same_side=0xff; + ubyte portal_and=0xff; + + for (i=0;i<4;i++) + { + if (first) + { + int lx=x[i]>>(8-level); + int lz=z[i]>>(8-level); + + int cell=(lz<p3_vec,x[i],z[i],ymin_int[0]); + + if (pnt->p3_vec.z>=0) + same_side&=1; + else + same_side&=2; + + if ((fabs(pnt->p3_vec.z)<=testdist)) + close=1; + + anded &= g3_CodePoint(pnt); + if (Check_terrain_portal && check_portal) + { + pnt->p3_flags&=~PF_PROJECTED; + // Automatically flag the ones behind us as visible + if (pnt->p3_codes & CC_BEHIND) + check_portal=0; + else + { + g3_ProjectPoint (pnt); + portal_and &=CodeTerrainPoint (pnt); + } + } + + // Do max height for region + GetPreRotatedPointFast( &pnt->p3_vec,x[i],z[i],ymax_int[0]); + + if (pnt->p3_vec.z>=0) + same_side&=1; + else + same_side&=2; + + if ((fabs(pnt->p3_vec.z)<=testdist)) + close=1; + + anded &= g3_CodePoint(pnt); + + if (Check_terrain_portal && check_portal) + { + pnt->p3_flags&=~PF_PROJECTED; + // Automatically flag the ones behind us as visible + if (pnt->p3_codes & CC_BEHIND) + check_portal=0; + else + { + g3_ProjectPoint (pnt); + portal_and &=CodeTerrainPoint (pnt); + } + } + } + + if ((!anded && close) || (!anded && !close && !same_side)) + { + // Recurse into this quadtree + + // Check to see if this is in our portal window + if (Check_terrain_portal && check_portal && portal_and) + continue; + + int midx=((x2-x1)/2)+x1; + int midy=((y2-y1)/2)+y1; + + PUSH_STACK_TREE(x1,midy,midx,y2,level+1); + PUSH_STACK_TREE(midx,midy,x2,y2,level+1); + PUSH_STACK_TREE(midx,y1,x2,midy,level+1); + PUSH_STACK_TREE(x1,y1,midx,midy,level+1); + } + else + { + /*#ifdef EDITOR + if (Tracing_drops) + { + int dx=Drop_seg%TERRAIN_WIDTH; + int dz=Drop_seg/TERRAIN_WIDTH; + + if (x1<=dx && dx<=x2 && y1<=dz && dz<=y2) + { + Tracing_drops=0; + Int3(); + } + + } + #endif*/ + } + + } + return 0; +} + + +// Given a position, returns the terrain segment that that position is in/over +// returns -1 if not over terrain +int GetTerrainCellFromPos (vector *pos) +{ + int x=pos->x/TERRAIN_SIZE; + int z=pos->z/TERRAIN_SIZE; + + if (x<0 || x>=TERRAIN_WIDTH || z<0 || z>=TERRAIN_DEPTH) + return -1; + + return (z*TERRAIN_WIDTH+x); +} + +int GetTerrainRoomFromPos(vector *pos) +{ + return MAKE_ROOMNUM(GetTerrainCellFromPos(pos)); +} + +// Computes the center of the segment in x,z and also sets y touching the ground +void ComputeTerrainSegmentCenter (vector *pos,int segnum) +{ + int segx=segnum%TERRAIN_WIDTH; + int segz=segnum/TERRAIN_WIDTH; + + pos->x=(segx*TERRAIN_SIZE)+(TERRAIN_SIZE/2); + pos->z=(segz*TERRAIN_SIZE)+(TERRAIN_SIZE/2); + + pos->y=GetTerrainGroundPoint(pos); +} + +// Given an position, returns the terrain Y coord at that location +float GetTerrainGroundPoint (vector *pos,vector *normal) +{ + float y; + vector pnt,norm; + int t; + int x,z; + + x=pos->x/TERRAIN_SIZE; + z=pos->z/TERRAIN_SIZE; + + if (x<0 || x>=TERRAIN_WIDTH || z<0 || z>=TERRAIN_DEPTH) + return (0); + + t=z*TERRAIN_WIDTH+x; + + pnt=*pos; + pnt.x-=(x * TERRAIN_SIZE); + pnt.z-=(z * TERRAIN_SIZE); + + if (pnt.x>pnt.z) + norm=TerrainNormals[MAX_TERRAIN_LOD-1][t].normal2; + else + norm=TerrainNormals[MAX_TERRAIN_LOD-1][t].normal1; + + // Do plane equation on x,z and then interpolate y + y=((pnt.x*norm.x)+(pnt.z*norm.z))/norm.y; + y=-y; + y+=Terrain_seg[t].y; + + if (normal!=NULL) + *normal=norm; + + return (y); +} + +int SimplifyVertexSlow (int x,int z,float delta) +{ + vector *eye=TS_View_position; + g3Point p1,p2; + + p1.p3_codes=0; + GetPreRotatedPoint( &p1, x,z,Terrain_seg[z*TERRAIN_WIDTH+x].ypos); + + p2.p3_codes=0; + GetSpecialRotatedPoint( &p2, x,z,Terrain_seg[z*TERRAIN_WIDTH+x].y+delta); + + g3_ProjectPoint (&p1); + g3_ProjectPoint (&p2); + + float len=((p1.p3_sx-p2.p3_sx)*(p1.p3_sx-p2.p3_sx))+((p1.p3_sy-p2.p3_sy)*(p1.p3_sy-p2.p3_sy)); + +#ifndef NEWEDITOR + if (len<(Detail_settings.Pixel_error*Detail_settings.Pixel_error)) +#else + if (len<(10.0f*10.0f)) +#endif + return 1; + + return 0; +} + +int SimplifyVertex (int x,int z,float delta) +{ + vector *eye=TS_View_position; + vector vec; + float inner; + + vec.x=x*TERRAIN_SIZE; + vec.z=z*TERRAIN_SIZE; + vec.y=Terrain_seg[z*TERRAIN_WIDTH+x].y; + + float delta_squared=delta*delta; + float first_part,second_part; + + float ex_minus_vx_squared=(eye->x-vec.x)*(eye->x-vec.x); + float ez_minus_vz_squared=(eye->z-vec.z)*(eye->z-vec.z); + float ey_minus_vy_squared=(eye->y-vec.y)*(eye->y-vec.y); + + first_part=delta_squared*(ex_minus_vx_squared+ez_minus_vz_squared); + + inner=ex_minus_vx_squared+ez_minus_vz_squared+ey_minus_vy_squared; + + second_part=(TS_SimplifyCondition)*(inner*inner); + + if (first_part<=second_part) + return 1; + + return 0; +} + +// Returns 1 if the given block (specified by the upper left corder x,z) lod can +// be simplified +// Returns -1 if the block is invisible +// Returns 0 if not +int EvaluateBlock (int x,int z,int lod) +{ + float delta; + int simplemul=1<<((MAX_TERRAIN_LOD-1)-lod); + + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (View_mode==EDITOR_MODE && Editor_LOD_engine_off) + return 0; + #endif + + if (View_mode!=EDITOR_MODE && Terrain_LOD_engine_off) + return 0; + + delta=TerrainDeltaBlocks[lod][((z/simplemul)*(TERRAIN_WIDTH/simplemul))+(x/simplemul)]; + + if (delta==SHUTOFF_LOD_INVISIBLE) + return -1; // This block is completely invisible + + //if (SimplifyVertexSlow (x+(simplemul/2),z+(simplemul/2),delta)) + // return 1; + + if (SimplifyVertex (x+(simplemul/2),z+(simplemul/2),delta)) + return 1; + + return 0; +} + + +// Shuts off LOD for a given cell +void TurnOffLODForCell (int cellnum) +{ + if (Dedicated_server) + return; + + ASSERT (cellnum>=0 && cellnum<(TERRAIN_WIDTH*TERRAIN_DEPTH)); + int x=cellnum % TERRAIN_WIDTH; + int z=cellnum / TERRAIN_WIDTH; + + ASSERT (Num_lodoffs=0;t--) + { + int cellnum=LODOffs[t].cellnum; + + int x=cellnum % TERRAIN_WIDTH; + int z=cellnum / TERRAIN_WIDTH; + + for (int i=0;i +#include +#include "findintersection.h" +#include "robotfire.h" +#include "AIMain.h" +#include "controls.h" +#include "damage.h" +#include "sounds.h" +#include "viseffect.h" +#include "vclip.h" +#include "SmallViews.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "stringtable.h" +#include "multi.h" +#include "game2dll.h" +#include "gameloop.h" +#include "collide.h" +#include "demofile.h" +#include "difficulty.h" +#include "D3ForceFeedback.h" +#include "config.h" +#include "ObjScript.h" +#include "doorway.h" +#include "psrand.h" +#include "BOA.h" +#include "vibeinterface.h" + +bool AreObjectsAttached(const object *obj1, const object *obj2) +{ + const bool f_o1_a = (obj1->flags & OF_ATTACHED) != 0; + const bool f_o2_a = (obj2->flags & OF_ATTACHED) != 0; + + if(f_o1_a || f_o2_a) + { + const int o1_uh = obj1->attach_ultimate_handle; + const int o2_uh = obj2->attach_ultimate_handle; + + if((f_o1_a) && ((o1_uh == obj2->handle) || (f_o2_a && (o1_uh == o2_uh)))) + return true; + + if((f_o2_a) && (o2_uh == obj1->handle)) + return true; + } + + return false; +} + +bool ObjectsAreRelated( int o1, int o2 ) +{ + if ( (o1 < 0) || (o2 < 0) ) + return false; + + const object *obj1 = &Objects[o1]; + const object *obj2 = &Objects[o2]; + + ASSERT(obj1->handle != OBJECT_HANDLE_NONE); + ASSERT(obj2->handle != OBJECT_HANDLE_NONE); + + if(obj1->movement_type == MT_OBJ_LINKED || obj2->movement_type == MT_OBJ_LINKED) + return true; + + if(obj1->type != OBJ_SHOCKWAVE && (obj1->mtype.phys_info.flags & PF_NO_COLLIDE)) + { + return true; + } + + if(obj2->type != OBJ_SHOCKWAVE && (obj2->mtype.phys_info.flags & PF_NO_COLLIDE)) + { + return true; + } + + if (((obj1->type == OBJ_PLAYER) && ((obj2->type == OBJ_ROBOT) && (obj2->id == GENOBJ_CHAFFCHUNK))) || + ((obj2->type == OBJ_PLAYER) && ((obj1->type == OBJ_ROBOT) && (obj1->id == GENOBJ_CHAFFCHUNK)))) + return true; + + if(((obj1->type == OBJ_BUILDING) && (obj1->movement_type != MT_NONE) && (obj2->type == OBJ_POWERUP)) || + ((obj2->type == OBJ_BUILDING) && (obj2->movement_type != MT_NONE) && (obj1->type == OBJ_POWERUP))) + { + return true; + } + + if(obj1->type == OBJ_DOOR && DoorwayGetPosition(&Rooms[obj1->roomnum]) == 1.0f && obj2->type == OBJ_ROBOT) + return true; + + if(obj2->type == OBJ_DOOR && DoorwayGetPosition(&Rooms[obj2->roomnum]) == 1.0f && obj1->type == OBJ_ROBOT) + return true; + + if(AreObjectsAttached(obj1, obj2)) + return true; + + if ( obj1->type != OBJ_WEAPON && obj2->type != OBJ_WEAPON ) + { + if(((Gametime < obj1->creation_time + 3.0f) && obj1->parent_handle == obj2->handle) || + ((Gametime < obj2->creation_time + 3.0f) && obj2->parent_handle == obj1->handle)) + return true; + else + return false; + } + + if(obj1->type == OBJ_WEAPON && obj1->movement_type == MT_PHYSICS && (obj1->mtype.phys_info.flags & PF_PERSISTENT) && + obj1->ctype.laser_info.last_hit_handle == obj2->handle) + return true; + + + if(obj2->type == OBJ_WEAPON && obj2->movement_type == MT_PHYSICS && (obj2->mtype.phys_info.flags & PF_PERSISTENT) && + obj2->ctype.laser_info.last_hit_handle == obj1->handle) + return true; + + // See if o2 is the parent of o1 + if ( obj1->type == OBJ_WEAPON && (obj1->mtype.phys_info.flags & PF_NO_COLLIDE_PARENT)) + { + if (obj1->parent_handle == obj2->handle) + return true; + + object *t1 = ObjGet(obj1->parent_handle); + + if(t1) + { + if(AreObjectsAttached(obj2, t1)) + return true; + } + } + + // See if o1 is the parent of o2 + if ( obj2->type == OBJ_WEAPON && (obj2->mtype.phys_info.flags & PF_NO_COLLIDE_PARENT)) + { + if (obj2->parent_handle == obj1->handle) + return true; + + object *t2 = ObjGet(obj2->parent_handle); + + if(t2) + { + if(AreObjectsAttached(obj1, t2)) + return true; + } + } + + // They must both be weapons + if ( obj1->type != OBJ_WEAPON || obj2->type != OBJ_WEAPON ) + { + return false; + } + + // Here is the 09/07/94 change -- Siblings must be identical, others can hurt each other + // See if they're siblings... + if ( obj1->parent_handle == obj2->parent_handle) + if ((obj1->mtype.phys_info.flags & PF_HITS_SIBLINGS) || (obj2->mtype.phys_info.flags & PF_HITS_SIBLINGS)) + return false; //if either is proximity, then can blow up, so say not related + else + return true; + + // Otherwise, it is two weapons and by default, they should not collide + return true; +} + +bool Enable_omega_collions = false; + +// Picks out a target for an elecrical weapon to fire on +void AquireElectricalTarget (object *obj) +{ + int i; + float closest_dist=99999,dist; + object *closest_obj=NULL,*hit_obj_ptr; + + + if (obj->mtype.phys_info.flags & PF_HOMING) + { + for (i=0; i<=Highest_object_index; i++ ) + { + hit_obj_ptr = &Objects[i]; + + if ((hit_obj_ptr->render_type==RT_NONE) || !(hit_obj_ptr->type==OBJ_PLAYER || hit_obj_ptr->type==OBJ_ROBOT || (hit_obj_ptr->type==OBJ_BUILDING && hit_obj_ptr->ai_info))) + continue; + + if(ObjectsAreRelated(OBJNUM(obj), i)) + continue; + + if (obj==hit_obj_ptr) + continue; + + if (hit_obj_ptr->flags & OF_DEAD) + continue; + + // The GB has an "electric shield" that works kind of like a Faraday's Cage (it also + // make him less annoying as a player's own Omega will not track his GB) + if(IS_GUIDEBOT(hit_obj_ptr)) + continue; + + // Make sure this target is within our view cone + vector subvec=hit_obj_ptr->pos-obj->pos; + vm_NormalizeVectorFast (&subvec); + if (vm_DotProduct (&subvec,&obj->orient.fvec)<.8) + continue; + + // The distance is actually the objects' centers minus some of the hit object's radius (I set it to 80%) + if((hit_obj_ptr->flags & OF_POLYGON_OBJECT) && hit_obj_ptr->type != OBJ_WEAPON && hit_obj_ptr->type != OBJ_POWERUP && hit_obj_ptr->type != OBJ_DEBRIS) + { + vector pos; + pos = hit_obj_ptr->pos + hit_obj_ptr->wall_sphere_offset; + + dist = vm_VectorDistanceQuick( &pos, &obj->pos ) - Poly_models[hit_obj_ptr->rtype.pobj_info.model_num].wall_size; + } + else + { + dist = vm_VectorDistanceQuick( &hit_obj_ptr->pos, &obj->pos ) - hit_obj_ptr->size; + } + + if(dist < 0.0f) + dist = 0.0f; + if (dist>50) + continue; + + if ( dist <= closest_dist) + { + closest_dist=dist; + closest_obj=hit_obj_ptr; + } + } + } + + if (closest_obj!=NULL) + { + float old_shield,new_shield; + if (closest_obj->effect_info && closest_obj!=Viewer_object) + { + closest_obj->effect_info->type_flags|=EF_DEFORM; + closest_obj->effect_info->deform_time=1.0; + closest_obj->effect_info->deform_range=.03f; + } + + obj->ctype.laser_info.track_handle = closest_obj->handle; + + vector subvec=obj->pos-closest_obj->pos; + vm_NormalizeVectorFast (&subvec); + + old_shield=closest_obj->shields; + + vector save_pos=obj->pos; + vector col_norm={0,1,0}; + obj->pos=closest_obj->pos; + + Enable_omega_collions = true; + collide_two_objects( obj, closest_obj, &closest_obj->pos, &col_norm ); + Enable_omega_collions = false; + + obj->pos=save_pos; + new_shield=closest_obj->shields; + + object *parent_obj = ObjGet(obj->parent_handle); + + if (parent_obj->shields<100) + { + float shields_to_add=(old_shield-new_shield) * .1; + if (parent_obj->shields+shields_to_add>100) + shields_to_add=100-parent_obj->shields; + + if (Game_mode & GM_MULTI) + { + if (Netgame.local_role==LR_SERVER) + { + parent_obj->shields+=shields_to_add; + Multi_additional_damage[parent_obj->id]-=shields_to_add; + Multi_additional_damage_type[parent_obj->id]=PD_ENERGY_WEAPON; + + + DLLInfo.it_handle=-1; + DLLInfo.me_handle=parent_obj->handle; + DLLInfo.fParam = shields_to_add; + CallGameDLL (EVT_GAMEOBJECTSHIELDSCHANGED,&DLLInfo); + } + } + else + parent_obj->shields+=shields_to_add; + } + } + else + { + obj->ctype.laser_info.track_handle = OBJECT_HANDLE_NONE; + + // Cast a ray to see what we've hit + int fate; // Collision type for response code + fvi_info hit_info; // Hit information + fvi_query fq; // Movement query + vector dest=obj->pos+(obj->orient.fvec*50.0); + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &dest; + fq.rad = .0001f; + fq.thisobjnum = Objects-obj; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS|FQ_IGNORE_POWERUPS|FQ_IGNORE_WEAPONS; + + fate = fvi_FindIntersection(&fq,&hit_info); + + vector save_pos=obj->pos; + + if (fate==HIT_WALL || fate==HIT_TERRAIN) + { + obj->pos=hit_info.hit_pnt; + collide_object_with_wall( obj, 0, hit_info.hit_face_room[0], hit_info.hit_face[0], &hit_info.hit_face_pnt[0], &hit_info.hit_wallnorm[0], 0 ); + } + else if (fate==HIT_OBJECT || fate==HIT_SPHERE_2_POLY_OBJECT) + { + if (CollisionResult[obj->type][Objects[hit_info.hit_object[0]].type] != RESULT_NOTHING) + { + obj->pos=hit_info.hit_pnt; + Enable_omega_collions = true; + collide_two_objects( obj, &Objects[hit_info.hit_object[0]], &hit_info.hit_face_pnt[0], &hit_info.hit_wallnorm[0]); + Enable_omega_collions = false; + } + } + + obj->pos=save_pos; + obj->flags&=~OF_DEAD; // Bring it back from the dead in case the collide code killed it! + } + +} + + +// Creates an weapon and sends it speeding on its way +int CreateAndFireWeapon (vector *pos,vector *dir,object *parent,int weapon_num) +{ + int objnum; + object *obj; + int parentnum=parent-Objects; + ubyte terrain=0; + + + ASSERT (Weapons[weapon_num].used ); + + // This can happen in multiplayer -- not single player + if (parent->flags & OF_DEAD) + return-1; + + //Find out if it is o.k. to create a weapon here. + fvi_query fq; + fvi_info hit_data; + int fate; + + fq.p0 = &parent->pos; + fq.startroom = parent->roomnum; + fq.p1 = pos; + fq.rad = 0; + fq.thisobjnum = OBJNUM(parent); + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_WEAPONS | FQ_IGNORE_POWERUPS | FQ_IGNORE_NON_LIGHTMAP_OBJECTS; //what about trans walls??? + + if(parent->type == OBJ_BUILDING) + { + fq.flags &= ~FQ_CHECK_OBJS; + } + + fate = fvi_FindIntersection(&fq, &hit_data); + if (fate != HIT_NONE) { + mprintf((0, "Warning: Laser from parent=%d weapon point is in a wall or object, didn't fire!\n", OBJNUM(parent))); + return -1; + } + + objnum = ObjCreate( OBJ_WEAPON, weapon_num, hit_data.hit_room, pos, NULL,parent->handle); + + if (objnum < 0 ) + { + mprintf((1, "Can't create laser - Out of objects!\n" )); + return -1; + } + + obj = &Objects[objnum]; + + // Fill in laser-specific data + obj->ctype.laser_info.multiplier=1.0; + obj->ctype.laser_info.parent_type = parent->type; + + if(parent->flags & OF_ATTACHED) + obj->parent_handle = parent->attach_ultimate_handle; + else + obj->parent_handle = parent->handle; + + // Assign parent type to highest level creator. This propagates parent type down from + // the original creator through weapons which create children of their own (ie, smart missile) + object *p = parent; + while (p && (p->type == OBJ_WEAPON)) + { + p = ObjGet(p->parent_handle); + if (p) + { + obj->ctype.laser_info.parent_type = p->type; + obj->parent_handle = p->handle; + } + } + + // Set direction + vm_VectorToMatrix( &obj->orient,dir,&parent->orient.uvec ,NULL); + + float scalar = 1.0f; + if(!ObjGet(obj->parent_handle) || ObjGet(obj->parent_handle)->type != OBJ_PLAYER) + scalar = (ObjGet(obj->parent_handle)->control_type == CT_AI && ((ObjGet(obj->parent_handle)->ai_info->flags & AIF_TEAM_MASK) == AIF_TEAM_REBEL))?1.0f:Diff_ai_weapon_speed[DIFF_LEVEL]; + + // Set thrust + if(obj->mtype.phys_info.flags & PF_USES_THRUST) + { + vm_ScaleVector (&obj->mtype.phys_info.thrust,dir, obj->mtype.phys_info.full_thrust * scalar); + // If this is a player, scale the thrust based on the players weapon_speed scalar + if (parent->type==OBJ_PLAYER) + obj->mtype.phys_info.thrust *= Players[parent->id].weapon_speed_scalar; + } + else + { + vm_MakeZero(&obj->mtype.phys_info.thrust); + vm_MakeZero(&obj->mtype.phys_info.rotthrust); + } +// vm_MakeZero(&obj->mtype.phys_info.rotvel); + + // Set the initial velocity + vm_ScaleVector (&obj->mtype.phys_info.velocity,dir, Weapons[weapon_num].phys_info.velocity.z * scalar); + + // If this is a player, scale the velocity based on the players weapon_speed scalar + if (parent->type==OBJ_PLAYER) + obj->mtype.phys_info.velocity*=Players[parent->id].weapon_speed_scalar; + + // Set initial velocity to that of the firing object + // Don't do it though if its a spawned weapon + if((obj->mtype.phys_info.flags & PF_USES_PARENT_VELOCITY) && parent->type!=OBJ_WEAPON) + { + + float fdot = (parent->mtype.phys_info.velocity * parent->orient.fvec); + vector fvel; + + if (fdot > 0.0) + fvel = parent->orient.fvec * fdot; + else + fvel = Zero_vector; + + vector rvel = 0.1f * parent->orient.rvec * (parent->mtype.phys_info.velocity * parent->orient.rvec); + vector uvel = 0.1f * parent->orient.uvec * (parent->mtype.phys_info.velocity * parent->orient.uvec); + + obj->mtype.phys_info.velocity += fvel + rvel + uvel; + } + + if (Weapons[obj->id].flags & WF_ELECTRICAL) + { + AquireElectricalTarget (obj); + } + else if(obj->movement_type == MT_PHYSICS) + { + if(!((obj->mtype.phys_info.flags & PF_USES_PARENT_VELOCITY) && (obj->mtype.phys_info.flags & PF_USES_THRUST))) + { + // For now, it only works on fixed velocity weapons + if((obj->mtype.phys_info.flags & PF_FIXED_VELOCITY) && !(obj->mtype.phys_info.flags & (PF_GRAVITY_MASK | PF_HOMING | PF_WIGGLE | PF_GUIDED))) + { + fvi_query fq; + fvi_info hit_info; + vector end_pos = obj->pos + obj->mtype.phys_info.velocity * obj->lifeleft; + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &end_pos; + fq.rad = obj->size; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = 0; + + fq.flags |= FQ_TRANSPOINT; + + fate = fvi_FindIntersection(&fq,&hit_info); + + if(fate != HIT_NONE && fate != HIT_OUT_OF_TERRAIN_BOUNDS) + { + float try_dist = vm_VectorDistance(&obj->pos, &end_pos); + float got_dist = vm_VectorDistance(&obj->pos, &hit_info.hit_pnt); + + obj->ctype.laser_info.hit_pnt = hit_info.hit_pnt; + obj->ctype.laser_info.hit_wall_pnt = hit_info.hit_face_pnt[0]; + obj->ctype.laser_info.hit_room = hit_info.hit_room; + obj->ctype.laser_info.hit_pnt_room = hit_info.hit_face_room[0]; + obj->ctype.laser_info.hit_wall_normal = hit_info.hit_wallnorm[0]; + obj->ctype.laser_info.hit_face = hit_info.hit_face[0]; + + obj->ctype.laser_info.hit_status = WPC_HIT_WALL; +// mprintf((0, "WPC: Hit r%d rw%d w%d (%f,%f,%f)\n", hit_info.hit_room, hit_info.hit_face_room[0], hit_info.hit_face[0], XYZ(&hit_info.hit_wallnorm[0]))); + } + else + { + obj->ctype.laser_info.hit_status = WPC_NO_COLLISIONS; +// mprintf((0, "WPC: No collisions\n")); + } + } + } + } + + if(parent && (parent->type==OBJ_PLAYER||parent->type==OBJ_GHOST) && (parent->id==Player_num) ){ + //Do ForceFeeback for recoil + DoForceForRecoil(parent,obj); + } + + // For smoke effects + obj->ctype.laser_info.last_smoke_pos=obj->pos-(obj->orient.fvec*(obj->size/2)); + obj->ctype.laser_info.casts_light=true; + return objnum; + +} + +// Steers a homing missile +void HomingTurnTowardObj(object *weapon, object *target) +{ + vector dir_to_target; + vector movement = Zero_vector; + + if(target == NULL) return; + + if(target->movement_type == MT_PHYSICS || target->movement_type == MT_WALKING) + { + movement = 0.1f * (target->mtype.phys_info.velocity); + } + + dir_to_target = target->pos + movement - weapon->pos; + + if(weapon->mtype.phys_info.rotdrag > 0.0f) + { + if(dir_to_target * weapon->orient.rvec > 0.0) + { + weapon->mtype.phys_info.rotthrust.y = weapon->mtype.phys_info.full_rotthrust; + } + else + { + weapon->mtype.phys_info.rotthrust.y = -weapon->mtype.phys_info.full_rotthrust; + } + + if(dir_to_target * weapon->orient.uvec > 0.0) + { + weapon->mtype.phys_info.rotthrust.x = -weapon->mtype.phys_info.full_rotthrust; + } + else + { + weapon->mtype.phys_info.rotthrust.x = weapon->mtype.phys_info.full_rotthrust; + } + + weapon->mtype.phys_info.rotthrust.z = 0.0; + + if(!ObjGet(weapon->parent_handle) || ObjGet(weapon->parent_handle)->type != OBJ_PLAYER) + weapon->mtype.phys_info.rotthrust *= Diff_homing_strength[DIFF_LEVEL]; + } + else + { + float scalar; + vm_NormalizeVector(&dir_to_target); + + if(!ObjGet(weapon->parent_handle) || ObjGet(weapon->parent_handle)->type != OBJ_PLAYER) + scalar = Diff_homing_strength[DIFF_LEVEL]; + else + scalar = 1.0f; + + AITurnTowardsDir(weapon, &dir_to_target, weapon->mtype.phys_info.full_rotthrust * scalar); + weapon->mtype.phys_info.velocity = weapon->orient.fvec * weapon->mtype.phys_info.full_thrust/weapon->mtype.phys_info.drag; + weapon->mtype.phys_info.thrust = weapon->orient.fvec * weapon->mtype.phys_info.full_thrust; + } +} + +#define MAX_HOMING_DIST 500.0f +#define CLOSE_HOMING_DIST 15.0 + +#define MAX_HOMING_SOUND_TIME_DIST 250.00f +#define MAX_HOMING_SOUND_TIME 1.00f +#define MIN_HOMING_SOUND_TIME 0.05f +#define HOMING_CHECK_VIS_INTERVAL 0.25f +#define HOMING_FIND_LOC_INTERVAL 0.20f + +object *HomingAquireTarget(object *obj) +{ + object *track_goal = ObjGet(obj->ctype.laser_info.track_handle); + object *weapon_parent = ObjGet(obj->parent_handle); + + // Forces its parent to itself if there is no parent + if(weapon_parent == NULL) + { + weapon_parent = obj; + } + + // Determine if the current track_goal is valid + if(track_goal) + { + bool f_locked = false; + + vector to_target = track_goal->pos - obj->pos; + float dist_to_target = vm_NormalizeVector(&to_target) - track_goal->size - obj->size; + + if(obj->type == OBJ_GHOST || obj->type == OBJ_DUMMY || (obj->type == OBJ_PLAYER && (Players[obj->id].flags & PLAYER_FLAGS_DEAD))) + { + f_locked = false; + } + else if(obj->effect_info && (obj->effect_info->type_flags & EF_CLOAKED)) + { + f_locked = false; + } + else if(to_target * obj->orient.fvec > Weapons[obj->id].homing_fov) + { + if(track_goal == Player_object && Player_object->type == OBJ_PLAYER) + { + float sound_delta; + float volume; + + if(dist_to_target > MAX_HOMING_SOUND_TIME_DIST) + { + sound_delta = MAX_HOMING_SOUND_TIME; + volume = MAX_GAME_VOLUME/2.0f; + } + else + { + float percent_near = dist_to_target/(float)MAX_HOMING_SOUND_TIME_DIST; + + sound_delta = MAX_HOMING_SOUND_TIME * percent_near; + volume = MAX_GAME_VOLUME/2.0f + MAX_GAME_VOLUME/2.0 * (1.0f - percent_near); + + if(sound_delta < MIN_HOMING_SOUND_TIME) + { + sound_delta = MIN_HOMING_SOUND_TIME; + volume = MAX_GAME_VOLUME; + } + } + + if(!(obj->type==OBJ_WEAPON && Weapons[obj->id].flags & WF_SILENT_HOMING) && !(obj->mtype.phys_info.flags & PF_GUIDED) && Players[Player_object->id].last_homing_warning_sound_time + sound_delta < Gametime) + { + fvi_query fq; + fvi_info hit_info; + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &track_goal->pos; + fq.rad = 0.0f; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = /*FQ_TRANSPOINT | */FQ_CHECK_OBJS | FQ_ONLY_DOOR_OBJ | FQ_NO_RELINK; + + fvi_FindIntersection(&fq, &hit_info); + + // Only plays sound if there is a LOS between the player and the homing weapon + if(hit_info.hit_type[0] == HIT_NONE) + { + Sound_system.Play2dSound(SOUND_MISSLE_LOCK, SND_PRIORITY_HIGHEST, volume); + Players[Player_object->id].last_homing_warning_sound_time = Gametime; + } + + + } + } + + if(dist_to_target < CLOSE_HOMING_DIST) + { + f_locked = true; + } + else if(dist_to_target < MAX_HOMING_DIST) + { + if(BOA_IsVisible(obj->roomnum, track_goal->roomnum)) + { + if(Gametime > obj->ctype.laser_info.last_track_time + HOMING_CHECK_VIS_INTERVAL) + { + obj->ctype.laser_info.last_track_time = Gametime; + fvi_query fq; + fvi_info hit_info; + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &track_goal->pos; + fq.rad = 0.0f; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = /*FQ_TRANSPOINT | */FQ_CHECK_OBJS | FQ_ONLY_DOOR_OBJ | FQ_NO_RELINK; + + fvi_FindIntersection(&fq, &hit_info); + if(hit_info.hit_type[0] == HIT_NONE) + { + f_locked = true; + } + } + else + { + f_locked = true; + } + } + } + } + + if(!f_locked) + { + track_goal = NULL; + obj->ctype.laser_info.track_handle = OBJECT_HANDLE_NONE; + } + } + + // Determine a track_goal if there is not one + if(!track_goal) + { + int i; + int best_index = -1; + int best_dist = MAX_HOMING_DIST + 1; + float best_dot = -1.0f; + + if(Gametime > obj->ctype.laser_info.last_track_time + HOMING_FIND_LOC_INTERVAL) + { + obj->ctype.laser_info.last_track_time = Gametime; + + for(i = 0; i <= Highest_object_index; i++) + { + if(BOA_IsVisible(obj->roomnum, Objects[i].roomnum)) + { + if((i != OBJNUM(weapon_parent)) && ((Objects[i].type == OBJ_BUILDING && Objects[i].ai_info) || (Objects[i].type == OBJ_ROBOT) || (Objects[i].type == OBJ_PLAYER))) + { + vector to_target = Objects[i].pos - obj->pos; + + if(Objects[i].effect_info && Objects[i].effect_info->type_flags & EF_CLOAKED) + continue; + + if(weapon_parent && !AIObjEnemy(ObjGetUltimateParent(weapon_parent), ObjGetUltimateParent(&Objects[i]))) + continue; + + float dist_to_target = vm_NormalizeVector(&to_target) - obj->size - Objects[i].size; + float cur_dot = to_target * obj->orient.fvec; + + if(cur_dot > Weapons[obj->id].homing_fov) + { + //Pick chaff over other objects + if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].id == GENOBJ_CHAFFCHUNK)) + { + // Make multiplayer safe! + if (Game_mode & GM_MULTI) + { + if (Netgame.local_role==LR_CLIENT) + { + if (!(Local_object_list[i] & 0x3)) + { + best_index=i; + break; + } + } + else + { + if (!(i & 0x3)) + { + best_index=i; + break; + } + } + } + else + { + if (ps_rand() < RAND_MAX/4) + { // 1/4 chance of picking chaff + best_index = i; + break; + } + } + } + else if(dist_to_target < CLOSE_HOMING_DIST) + { + best_index = i; + break; + } + else if(dist_to_target < MAX_HOMING_DIST) + { + if(cur_dot > best_dot) + { + + fvi_query fq; + fvi_info hit_info; + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &Objects[i].pos; + fq.rad = 0.0f; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = FQ_TRANSPOINT | FQ_CHECK_OBJS | FQ_ONLY_DOOR_OBJ | FQ_NO_RELINK; + + fvi_FindIntersection(&fq, &hit_info); + if(hit_info.hit_type[0] == HIT_NONE) + { + best_index = i; + best_dist = dist_to_target; + best_dot = cur_dot; + } + } + } + } + } + } + } + if(best_index >= 0) + { + track_goal = &Objects[best_index]; + obj->ctype.laser_info.track_handle = Objects[best_index].handle; + } + } + } + + return track_goal; +} + +// Does homing code +void HomingDoFrame(object *obj) +{ + obj->mtype.phys_info.rotthrust = Zero_vector; + + HomingTurnTowardObj(obj, HomingAquireTarget(obj)); +} + +void WeaponDoFrame(object *obj) +{ + bool draw_effects=1; + + if (!Detail_settings.Weapon_coronas_enabled) + draw_effects=0; + + if(MAX_WEAPON_NOT_HIT_PARENT_TIME + obj->creation_time < Gametime) + obj->mtype.phys_info.flags &= (~PF_NO_COLLIDE_PARENT); + + + if((obj->mtype.phys_info.flags & PF_USES_THRUST) && (obj->ctype.laser_info.thrust_left >= 0.0)) + { + obj->ctype.laser_info.thrust_left -= Frametime; + + if((obj->creation_time + 0.1f < Gametime) && (obj->mtype.phys_info.flags & PF_HOMING) && !(obj->mtype.phys_info.flags & PF_GUIDED)) + { + obj->ctype.laser_info.last_track_time = Gametime - HOMING_CHECK_VIS_INTERVAL; + HomingDoFrame(obj); + } + + if(obj->ctype.laser_info.thrust_left <= 0.0) + { + obj->mtype.phys_info.flags &= (~PF_USES_THRUST); + vm_MakeZero(&obj->mtype.phys_info.thrust); + vm_MakeZero(&obj->mtype.phys_info.rotthrust); + + obj->mtype.phys_info.drag = 0.005f; + obj->mtype.phys_info.rotdrag = 0.005f; + + obj->mtype.phys_info.flags |= PF_GRAVITY; + } + } + + if ((Weapons[obj->id].flags & WF_SMOKE) && draw_effects) + { + if (Weapons[obj->id].flags & WF_PLANAR_SMOKE) + { + vector newpos=obj->pos-(obj->orient.fvec*(obj->size/2)); + int visnum=VisEffectCreate (VIS_FIREBALL,BILLBOARD_SMOKETRAIL_INDEX,obj->roomnum,&newpos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->custom_handle=Weapons[obj->id].smoke_handle; + vis->lifeleft=1.0; + vis->lifetime=1.0; + + vis->end_pos=obj->ctype.laser_info.last_smoke_pos; + vis->billboard_info.width=4; + vis->lighting_color=GR_RGB16(Weapons[obj->id].lighting_info.red_light1*255.0,Weapons[obj->id].lighting_info.green_light1*255.0,Weapons[obj->id].lighting_info.blue_light1*255.0); + obj->ctype.laser_info.last_smoke_pos=vis->pos; + } + } + else + { + // Create blobby smoke + // Create extras + vector blobpos=obj->pos-(obj->orient.fvec*obj->size); + + float delta_time=Frametime; + vector delta_vec=obj->mtype.phys_info.velocity*delta_time; + + float mag=vm_GetMagnitudeFast (&delta_vec); + float extras=(mag*4)+.5; + int int_extras=extras+1; + + int_extras=min(int_extras,2); + + delta_vec/=int_extras; + delta_time/=int_extras; + + vector new_blobpos=blobpos; + + for (int i=0;iroomnum,VISUAL_FIREBALL); + + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + + vis->custom_handle=Weapons[obj->id].smoke_handle; + vis->lifeleft-=(i*delta_time); + vis->creation_time-=(i*delta_time); + + if (Weapons[obj->id].flags & WF_REVERSE_SMOKE) + vis->flags|=VF_REVERSE; + } + + new_blobpos-=delta_vec; + } + } + } + + if (Weapons[obj->id].particle_count>0 && Weapons[obj->id].particle_handle!=-1 && draw_effects) + { + vector pos=obj->pos-(obj->orient.fvec*obj->size); + weapon *weap=&Weapons[obj->id]; + float particle_interval=(1.0/(float)weap->particle_count); + + if (Gametime-obj->ctype.laser_info.last_drop_time>particle_interval) + { + CreateRandomParticles (1,&pos,obj->roomnum,weap->particle_handle,weap->particle_size,weap->particle_life); + obj->ctype.laser_info.last_drop_time=Gametime; + } + + } + + + if (Weapons[obj->id].flags & WF_RING) + { + float norm=((Gametime-obj->creation_time)/obj->lifetime); + obj->size=Weapons[obj->id].size+(norm*Weapons[obj->id].size*8); + } + +} + +bool WeaponCalcGun(vector *gun_point, vector *gun_normal, object *obj, int gun_num) +{ + poly_model *pm; + vector pnt; + vector normal; + matrix m; + int mn; //submodel number + float normalized_time[MAX_SUBOBJECTS]; + bool f_good_gp = true; + + if (!(obj->flags & OF_POLYGON_OBJECT)) + { + // mprintf ((0,"Object type %d is not a polyobj!\n",obj->type)); + + if(gun_point) + *gun_point = obj->pos; + if(gun_normal) + *gun_normal = obj->orient.fvec; + + return false; + } + + pm = &Poly_models[obj->rtype.pobj_info.model_num]; + + if(pm->n_guns == 0) + { + mprintf((0, "WARNING: Object with no weapons is firing.\n", gun_num)); + + if(gun_point) + *gun_point = obj->pos; + if(gun_normal) + *gun_normal = obj->orient.fvec; + + return false; + } + + SetNormalizedTimeObj(obj, normalized_time); + SetModelAnglesAndPos (pm,normalized_time); + + if (gun_num < 0 || gun_num >= pm->n_guns) + { + mprintf((0, "WARNING: Bashing gun num %d to 0.\n", gun_num)); + if(gun_point) + *gun_point = obj->pos; + if(gun_normal) + *gun_normal = obj->orient.fvec; + return false; + } + + pnt = pm->gun_slots[gun_num].pnt; + normal = pm->gun_slots[gun_num].norm; + mn = pm->gun_slots[gun_num].parent; + + // Instance up the tree for this gun + while (mn != -1) + { + vector tpnt; + + vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b); + vm_TransposeMatrix(&m); + + if(gun_point) + tpnt = pnt * m; + + if(gun_normal) + normal = normal * m; + + if(gun_point) + pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos; + + mn = pm->submodel[mn].parent; + } + + m = obj->orient; + vm_TransposeMatrix(&m); + + if(gun_point) + *gun_point = pnt * m; + if(gun_normal) + *gun_normal = normal * m; + + if(gun_point) + *gun_point += obj->pos; + + return f_good_gp; +} + +#define MIN_FIRE_SPREAD 5000 +#define MAX_FIRE_SPREAD 16768 // We vary by difficulty level!!!!!!!! Its the total fire cone (half each way) + +// Given an object and a weapon, fires a shot from that object +int FireWeaponFromObject (object *obj,int weapon_num,int gun_num, bool f_force_forward, bool f_force_target) +{ + ASSERT (Weapons[weapon_num].used); + + + + // Mass driver is hack + static int mass_driver_id=-2; + static int mass_driver_ring=-2; + + if (mass_driver_id==-2) + { + mass_driver_id=FindWeaponName ("Mass Driver"); + mass_driver_ring=FindTextureName ("CloakRing.tga1"); + } + + vector laser_pos, laser_dir; + int objnum; + + if (gun_num==-1) + { + laser_pos=obj->pos; + laser_dir=obj->orient.fvec; + } + else + { + // Find the initial position of the laser + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; +// vector *pnt = &pm->gun_slots[gun_num].pnt; +// vector gun_point; +// matrix m; + + if (gun_num>=pm->n_guns) + { + // We don't have a gun point for this gun! + mprintf ((0,"Trying to fire from gun %d...we don't have that gun!\n",gun_num)); + laser_pos=obj->pos; + laser_dir=obj->orient.fvec; + } + else + { + if(f_force_forward) + { + WeaponCalcGun(&laser_pos, NULL, obj, gun_num); + laser_dir = obj->orient.fvec; + } + else if(f_force_target && obj->ai_info && obj->ai_info->vec_to_target_perceived != Zero_vector) + { + WeaponCalcGun(&laser_pos, NULL, obj, gun_num); + laser_dir = obj->ai_info->vec_to_target_perceived; + } + else + { + WeaponCalcGun(&laser_pos, &laser_dir, obj, gun_num); + } + } + } + + if((obj->control_type==CT_AI) && (obj->ai_info->fire_spread || (Diff_ai_min_fire_spread[DIFF_LEVEL]))) + { + matrix rot_mat; + angle p, h, b; + + float diff_scale = DIFF_LEVEL / (MAX_DIFFICULTY_LEVELS - 1); + float fs = (MAX_FIRE_SPREAD * (1.0f - diff_scale)) + (MIN_FIRE_SPREAD * diff_scale); + + short fire_spread = obj->ai_info->fire_spread * fs; + + if((obj->control_type == CT_AI && ((obj->ai_info->flags & AIF_TEAM_MASK) != AIF_TEAM_REBEL)) && fire_spread < Diff_ai_min_fire_spread[DIFF_LEVEL] * fs) + { + fire_spread = Diff_ai_min_fire_spread[DIFF_LEVEL] * fs; + } + + short half_fire_spread = fire_spread >> 1; + + if (fire_spread > 0) { + p = (ps_rand()%fire_spread) - half_fire_spread; + h = (ps_rand()%fire_spread) - half_fire_spread; + b = (ps_rand()%fire_spread) - half_fire_spread; + + vm_AnglesToMatrix(&rot_mat, p, h, b); + + laser_dir = laser_dir * rot_mat; + } + } + + objnum = CreateAndFireWeapon( &laser_pos, &laser_dir, obj, weapon_num); + if (Game_mode & GM_MULTI) + { + if(Netgame.local_role==LR_SERVER) + { + if(obj->control_type==CT_AI) + { + MultiSendRobotFireWeapon (obj->handle&HANDLE_OBJNUM_MASK,&laser_pos,&laser_dir,(unsigned short)weapon_num); + } + } + } + if(Demo_flags==DF_RECORDING) + { + DemoWriteWeaponFire(obj->handle&HANDLE_OBJNUM_MASK,&laser_pos,&laser_dir,(unsigned short)weapon_num,objnum,gun_num); + } + + // bail out of their was an error creating this weapon object + if (objnum == -1) + return -1; + + // Record gun number + Objects[objnum].ctype.laser_info.src_gun_num=gun_num; + + AINotify(&Objects[objnum], AIN_OBJ_FIRED, obj); + + if(obj->type == OBJ_PLAYER && obj == Player_object) + { + //Check if missile camera enabled, and if this weapon allows it + if ((Missile_camera_window != -1) && (Weapons[weapon_num].flags & WF_ENABLE_CAMERA)) { + + //Check if there's already a missile camera + object *viewer_obj = ObjGet(GetSmallViewer(Missile_camera_window)); + if ((viewer_obj == NULL) || (viewer_obj->type != OBJ_WEAPON)) + CreateSmallView(Missile_camera_window,Objects[objnum].handle,SVF_POPUP+SVF_BIGGER); + } + } + +// if (Weapons[weapon_num].sounds[WSI_FIRE]!=SOUND_NONE_INDEX) +// Sound_system.Play3dSound(Weapons[weapon_num].sounds[WSI_FIRE], &Objects[objnum]); + + if (Weapons[weapon_num].sounds[WSI_FLYING]!=SOUND_NONE_INDEX) + Sound_system.Play3dSound(Weapons[weapon_num].sounds[WSI_FLYING], SND_PRIORITY_HIGHEST, &Objects[objnum]); + + // Do a muzzle flash from this gun + if (Weapons[weapon_num].flags & WF_MUZZLE) + { + int visnum; + vector newpos; + + if (obj==&Objects[Players[Player_num].objnum]) + { + newpos=laser_pos+(laser_dir); + visnum=VisEffectCreate (VIS_FIREBALL,MUZZLE_FLASH_INDEX,obj->roomnum,&newpos); + } + else + { + newpos=laser_pos+(laser_dir/2); + visnum=VisEffectCreate (VIS_FIREBALL,MUZZLE_FLASH_INDEX,obj->roomnum,&newpos); + } + + // Make this guy live for a very short time + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + + vis->lifetime=.08f; + vis->lifeleft=.08f; + vis->size=1.0f; + + vis->movement_type=MT_PHYSICS; + vis->velocity=obj->mtype.phys_info.velocity; + + // Make some smoke! + visnum=VisEffectCreate (VIS_FIREBALL,MED_SMOKE_INDEX,obj->roomnum,&newpos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + + vis->lifetime=.3f; + vis->lifeleft=.3f; + + vis->movement_type=MT_PHYSICS; + vis->velocity=obj->mtype.phys_info.velocity; + vis->velocity.y+=10; + } + + } + } + + // Check for mass driver + if (weapon_num==mass_driver_id) + { + // Create a fire trail for this weapon + // find out where this weapon hits + fvi_query fq; + fvi_info hit_data; + + vector dest_vec=laser_pos+(laser_dir*5000); + + fq.p0 = &laser_pos; + fq.startroom = obj->roomnum; + fq.p1 = &dest_vec; + fq.rad = 0; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS|FQ_IGNORE_POWERUPS|FQ_IGNORE_WEAPONS; + + fvi_FindIntersection(&fq, &hit_data); + + float mag=vm_VectorDistanceQuick (&hit_data.hit_pnt,&obj->pos); + + int visnum; + + if(obj == NULL || obj->type != OBJ_BUILDING || stricmp(Object_info[obj->id].name, "FinalbossLITTLETIT") != 0) + visnum=VisEffectCreate (VIS_FIREBALL,MASSDRIVER_EFFECT_INDEX,obj->roomnum,&laser_pos); + else + visnum=VisEffectCreate (VIS_FIREBALL,MERCBOSS_MASSDRIVER_EFFECT_INDEX,obj->roomnum,&laser_pos); + + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->custom_handle=mass_driver_ring; + vis->lifeleft=1.5; + vis->lifetime=1.5; + + vis->end_pos=hit_data.hit_pnt; + vis->billboard_info.width=1; + if(obj == NULL || obj->type != OBJ_BUILDING || stricmp(Object_info[obj->id].name, "FinalbossLITTLETIT") != 0) + vis->lighting_color=GR_RGB16(100,100,170); + else + vis->lighting_color=GR_RGB16(240,10,10); + vis->flags|=VF_LINK_TO_VIEWER|VF_EXPAND; + vis->size=mag; + } + } + + if (Game_mode & GM_MULTI) + { + DLLInfo.me_handle=Objects[objnum].handle; + DLLInfo.it_handle=obj->handle; + CallGameDLL(EVT_WEAPONFIRED,&DLLInfo); + } + + + return objnum; +} + +// This define determines how wide this arc is +#define ELECTRICAL_WIDTH .3f +#define ELECTRICAL_ALPHA .2 + +#include "args.h" + +// Draws a lighting like chain of electricity +void DrawElectricalWeapon (object *obj) +{ + int i,t; + vector center_vecs[20]; + vector arc_vec; + g3Point arc_points[100]; + vector dest_vector,src_vector,line_norm; + float line_mag; + g3Point *pntlist[20]; + vector dir; + object *parent_obj = ObjGet(obj->parent_handle); + float view_dp=0; + + if (!parent_obj) + return; + + if (parent_obj->type==OBJ_PLAYER) + { + if (Players[parent_obj->id].flags & (PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD)) + return; + } + else + { + if (parent_obj->flags & OF_DYING) + return; + } + + int bm_handle=GetWeaponFireImage (obj->id,0); + ASSERT (bm_handle>=0); + + WeaponCalcGun(&src_vector, &dir, parent_obj, obj->ctype.laser_info.src_gun_num); + + if (obj->ctype.laser_info.track_handle == OBJECT_HANDLE_NONE || ObjGet(obj->ctype.laser_info.track_handle) == NULL) + { + dest_vector=src_vector+(obj->orient.fvec*50); + } + else + { + object *obj_to_track=ObjGet(obj->ctype.laser_info.track_handle); + dest_vector=obj_to_track->pos; + if (obj_to_track==Player_object) + { + vector subvec=src_vector-dest_vector; + float mag=vm_GetMagnitudeFast (&subvec); + + if (mag<.5) + { + obj->lifeleft=0; + return; + } + + subvec/=mag; + + dest_vector+=subvec; // This prevents rendering errors + } + } + + + //Find out if it is o.k. to create a weapon here. + fvi_query fq; + fvi_info hit_data; + int fate; + + fq.p0 = &src_vector; + fq.startroom = obj->roomnum; + fq.p1 = &dest_vector; + fq.rad = 0; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = 0; //what about trans walls??? + + fate = fvi_FindIntersection(&fq, &hit_data); + + if (fate==HIT_WALL || fate==HIT_TERRAIN) + { + dest_vector=hit_data.hit_pnt; + CreateRandomSparks ((ps_rand()%4),&dest_vector,obj->roomnum,COOL_SPARK_INDEX); + } + + line_norm=dest_vector-src_vector; + line_mag=vm_GetMagnitudeFast (&line_norm); + + if (line_mag<.5) + { + obj->lifeleft=0; + return; + } + + + line_norm/=line_mag; + + // Get view dot product for drawing blobs + + if (parent_obj!=Viewer_object) + { + vector temp_line_norm=-line_norm; + view_dp=Viewer_object->orient.fvec * temp_line_norm; + } + + matrix mat; + + vm_VectorToMatrix (&mat,&line_norm,NULL,NULL); + + // Create the center points of our chain + int num_segments=20; + int circle_pieces=5; + + if (!Detail_settings.Weapon_coronas_enabled) + num_segments=10; + + float line_scalar=(line_mag/(float)num_segments); + line_norm*=line_scalar; + + // Get the src and dest vectors in view space so we know how to compute z + + center_vecs[0]=src_vector; // Set first one equal to our origin + center_vecs[num_segments-1]=dest_vector; + vector from=src_vector; + int cur_sin=FrameCount*5000; + + for (i=1;iorient.uvec*size*FixSin(norm*65536)); + arc_vec-=(obj->orient.rvec*size*FixCos(norm*65536)); + } + + g3_RotatePoint(&arc_points[i*circle_pieces+t],&arc_vec); + arc_points[i*circle_pieces+t].p3_flags |= PF_UV; + } + } + + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (ELECTRICAL_ALPHA*255); + rend_SetOverlayType (OT_NONE); + rend_SetLighting(LS_NONE); + rend_SetZBufferWriteMask (0); + + float float_time=(Gametime*300); + int int_time=float_time; + int_time%=100; + float vchange=int_time/100.0; + + //Check for no sat flag + bool sat = true; + if (FindArg("-nosatomega")) + sat = false; + + // Form our vectors into points and draw! + for (t=(num_segments-1)*circle_pieces,i=num_segments-1;i>=1;i--,t-=circle_pieces) + { + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaType (sat ? AT_SATURATE_TEXTURE : AT_CONSTANT); + rend_SetZBufferWriteMask (0); + rend_SetLighting(LS_NONE); + rend_SetAlphaValue (ELECTRICAL_ALPHA*255); + + for (int k=0;klifeleft=0; + +} + +#define FUSION_RAMP_TIME 3.0 +#define FUSION_DAMAGE_SOUND_INTERVAL .5 + +float Last_fusion_damage_time=0; + +// Does that crazy fusion effect, including damaging/shaking your ship +void DoFusionEffect(object *objp,int weapon_type) +{ + float norm; + int move; + + ASSERT(objp->type == OBJ_PLAYER); + ASSERT((weapon_type == PW_PRIMARY) || (weapon_type == PW_SECONDARY)); + + norm = (Players[objp->id].weapon[weapon_type].firing_time / FUSION_RAMP_TIME); + + if (norm>.1) + { + float over=norm; + + if (over>1.0) + over=1.0; + + over=(over-.1)/.9; + + move = (ps_rand()%5)-2; + + objp->mtype.phys_info.velocity += (objp->orient.rvec) * ((float)move * over * Frametime * 175.0); + + move = (ps_rand()%5)-2; + + objp->mtype.phys_info.velocity += (objp->orient.uvec) * ((float)move * over * Frametime * 175.0); + + } + + if (norm>1.0) + { + norm=1.0; + + if ((!(Game_mode & GM_MULTI)) || Netgame.local_role==LR_SERVER) + { + if ((Gametime-Last_fusion_damage_time)flags & OF_DYING)); + + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + + // Go through each gun point + for(cur_m_bit = 0; cur_m_bit < pm->poly_wb[0].num_gps; cur_m_bit++) + { + // Figure out out if this weapon fires from this gunpoint + if(static_wb->gp_fire_masks[obj->dynamic_wb[wb_index].cur_firing_mask] & (0x01 << cur_m_bit)) + { + int weapon_num=static_wb->gp_weapon_index[cur_m_bit]; + + WeaponCalcGun(&laser_pos, &laser_dir, obj, pm->poly_wb[0].gp_index[cur_m_bit]); + int visnum=VisEffectCreate( VIS_FIREBALL, SPRAY_INDEX, obj->roomnum, &laser_pos); + + if (visnum<0) + return; + + vis_effect *vis = &VisEffects[visnum]; + + + // Set the initial velocity + vm_ScaleVector (&vis->velocity,&laser_dir, Weapons[weapon_num].phys_info.velocity.z); + + // Set initial velocity to that of the firing object + if(Weapons[weapon_num].phys_info.flags & PF_USES_PARENT_VELOCITY) + { + float fdot = (obj->mtype.phys_info.velocity * obj->orient.fvec); + vector fvel; + + if (fdot > 0.0) + fvel = obj->orient.fvec * fdot; + else + fvel = Zero_vector; + + vector rvel = 0.1f * obj->orient.rvec * (obj->mtype.phys_info.velocity * obj->orient.rvec); + vector uvel = 0.1f * obj->orient.uvec * (obj->mtype.phys_info.velocity * obj->orient.uvec); + + vis->velocity += fvel+rvel+uvel; + } + + vis->movement_type=MT_PHYSICS; + vis->custom_handle=Weapons[weapon_num].fire_image_handle; + vis->drag=Weapons[weapon_num].phys_info.drag; + vis->phys_flags=Weapons[weapon_num].phys_info.flags; + vis->mass=Weapons[weapon_num].phys_info.mass; + vis->size=Weapons[weapon_num].size; + vis->lifetime=Weapons[weapon_num].life_time; + vis->lifeleft=vis->lifetime; + + // Now create extras so that there are no gaps + + if (obj!=Viewer_object) + { + // Find hit point so we know when to stop + float life_scalar=1.0; + fvi_query fq; + fvi_info hit_data; + int fate; + vector dest_vector=laser_pos+(vis->velocity*vis->lifetime); + + fq.p0 = &laser_pos; + fq.startroom = obj->roomnum; + fq.p1 = &dest_vector; + fq.rad = vis->size; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS|FQ_IGNORE_NON_LIGHTMAP_OBJECTS; + + fate = fvi_FindIntersection(&fq, &hit_data); + + if (fate!=HIT_NONE) + { + vector subvec=hit_data.hit_pnt-laser_pos; + vis->lifetime=vm_GetMagnitudeFast (&subvec)/vm_GetMagnitudeFast (&vis->velocity); + life_scalar=vis->lifetime/vis->lifeleft; + vis->lifeleft=vis->lifetime; + + } + + float velmag=vm_GetMagnitudeFast (&vis->velocity); + vector velnorm=vis->velocity/velmag; + float len=Frametime*velmag; + float fextras=len*2; + float fps=1.0/Frametime; + + int extras=fextras; + + extras=min(extras,8); + + for(int t=0;tpos+(((velnorm*len)/extras)*(t+1)); + + int extranum=VisEffectCreate( VIS_FIREBALL, SPRAY_INDEX, obj->roomnum, &newpos); + if (extranum>=0) + { + vis_effect *extravis=&VisEffects[extranum]; + extravis->mass=vis->mass; + extravis->drag=vis->drag; + extravis->velocity=vis->velocity; + extravis->size=vis->size; + extravis->phys_flags=vis->phys_flags; + extravis->movement_type=MT_PHYSICS; + extravis->custom_handle=vis->custom_handle; + extravis->lifetime=vis->lifetime; + + float life_adjust=((Frametime/(float)extras)*(t+1)); + + extravis->lifeleft=Frametime*(fps/2); + extravis->lifeleft*=life_scalar; + life_adjust*=life_scalar; + + + if (extravis->lifeleft>0) + extravis->creation_time=vis->creation_time-life_adjust; + else + { + VisEffectDelete (extranum); + } + } + } + } + else + { + // Find hit point so we know when to stop + fvi_query fq; + fvi_info hit_data; + int fate; + vector dest_vector=laser_pos+(vis->velocity*vis->lifetime); + + fq.p0 = &laser_pos; + fq.startroom = obj->roomnum; + fq.p1 = &dest_vector; + fq.rad = vis->size; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS|FQ_IGNORE_NON_LIGHTMAP_OBJECTS; + + fate = fvi_FindIntersection(&fq, &hit_data); + + if (fate!=HIT_NONE) + { + vector subvec=hit_data.hit_pnt-laser_pos; + vis->lifetime=vm_GetMagnitudeFast (&subvec)/vm_GetMagnitudeFast (&vis->velocity); + vis->lifeleft=vis->lifetime; + } + } + + } + } + +} + +// Draws a streamer behind a weapon +void DrawWeaponStreamer (object *obj) +{ + float time_live=Gametime-obj->creation_time; + float len; + float norm_time=time_live/obj->lifetime; + + if (norm_time>=1) + norm_time=.99999f; // don't go over! + + // Get length + object *parent_obj=ObjGet(obj->parent_handle); + + if (!parent_obj) + return; + + len=vm_VectorDistance(&parent_obj->pos,&obj->pos); + + rend_SetAlphaType (AT_VERTEX); + rend_SetTextureType (TT_FLAT); + rend_SetLighting(LS_GOURAUD); + rend_SetColorModel (CM_RGB); + + vector vecs[2]; + g3Point pnts[2]; + int i; + + vecs[0]=obj->pos; + vecs[1]=obj->pos-(obj->orient.fvec*len); + + for (i=0;i<2;i++) + { + g3_RotatePoint (&pnts[i],&vecs[i]); + pnts[i].p3_flags|=PF_RGBA; + pnts[i].p3_r=Weapons[obj->id].lighting_info.red_light1; + pnts[i].p3_g=Weapons[obj->id].lighting_info.green_light1; + pnts[i].p3_b=Weapons[obj->id].lighting_info.blue_light1; + } + + pnts[0].p3_a=(1.0-norm_time)*.3f; + pnts[1].p3_a=0; + + g3_DrawSpecialLine (&pnts[0],&pnts[1]); +} + +void DrawBlobbyWeaponRing (object *obj) +{ + vector vecs[30]; + float lifenorm=(obj->lifetime-obj->lifeleft)/obj->lifetime; + int i; + + int bm_handle; + int num_segments=20; + + int ring_increment=65536/num_segments; + int ring_angle=0; + + if (lifenorm>=1.0) + lifenorm=.999f; + + if (Weapons[obj->id].flags & WF_IMAGE_BITMAP) + bm_handle=GetWeaponFireImage (obj->id,0); + else + { + vclip *vc=&GameVClips[Weapons[obj->id].fire_image_handle]; + int int_frame=vc->num_frames*lifenorm; + bm_handle=vc->frames[int_frame]; + } + + rend_SetLighting(LS_NONE); + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue ((1.0-lifenorm)*255.0); + + // Now draw a ring of blobs + ring_angle=0; + float blob_size=(obj->size/4); + + for (i=0;iorient.rvec*(ring_cos*obj->size); + vecs[i]+=obj->orient.uvec*(ring_sin*obj->size); + vecs[i]+=obj->pos; + + + + g3_DrawRotatedBitmap (&vecs[i],ring_angle,blob_size,(blob_size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + } +} + +void DrawPolygonalWeaponRing (object *obj) +{ + vector inner_vecs[30],outer_vecs[30]; + g3Point inner_points[30],outer_points[30]; + float lifenorm=(obj->lifetime-obj->lifeleft)/obj->lifetime; + int i; + g3Point *pntlist[4]; + float inner_size=(obj->size*.8); + int objnum=obj-Objects; + int odd=objnum%2; + + int num_segments=20; + + int ring_increment=65536/num_segments; + int ring_angle=0; + + if (lifenorm>1.0) + lifenorm=1.0; + + rend_SetTextureType (TT_FLAT); + rend_SetColorModel (CM_RGB); + rend_SetLighting(LS_GOURAUD); + rend_SetAlphaType (AT_SATURATE_VERTEX); + + for (i=0;iorient.rvec*(ring_cos*inner_size); + inner_vecs[i]+=obj->orient.uvec*(ring_sin*inner_size); + inner_vecs[i]+=obj->pos; + + outer_vecs[i]=obj->orient.rvec*(ring_cos*obj->size); + outer_vecs[i]+=obj->orient.uvec*(ring_sin*obj->size); + outer_vecs[i]+=obj->pos; + + g3_RotatePoint (&inner_points[i],&inner_vecs[i]); + g3_RotatePoint (&outer_points[i],&outer_vecs[i]); + + outer_points[i].p3_flags|=PF_RGBA|PF_UV; + inner_points[i].p3_flags|=PF_RGBA|PF_UV; + + outer_points[i].p3_a=(1.0-lifenorm)*.3; + inner_points[i].p3_a=(1.0-lifenorm)*.1; + + outer_points[i].p3_r=Weapons[obj->id].lighting_info.red_light1; + outer_points[i].p3_g=Weapons[obj->id].lighting_info.green_light1; + outer_points[i].p3_b=Weapons[obj->id].lighting_info.blue_light1; + + inner_points[i].p3_r=Weapons[obj->id].lighting_info.red_light1; + inner_points[i].p3_g=Weapons[obj->id].lighting_info.green_light1; + inner_points[i].p3_b=Weapons[obj->id].lighting_info.blue_light1; + } + + for (i=0;iid].flags & WF_IMAGE_BITMAP) || (Weapons[obj->id].flags & WF_IMAGE_VCLIP)) + { + DrawBlobbyWeaponRing (obj); + return; + } + else + DrawPolygonalWeaponRing(obj); + +} + +// Draws a weapon +void DrawWeaponObject (object *obj) +{ + ASSERT (obj->type==OBJ_WEAPON || obj->type==OBJ_POWERUP); + ASSERT (Weapons[obj->id].used>0); + + + // Don't draw if spray + if (!(Weapons[obj->id].flags & WF_INVISIBLE)) + { + if (Weapons[obj->id].flags & WF_ELECTRICAL) + { + DrawElectricalWeapon (obj); + } + else if ((Weapons[obj->id].flags & WF_IMAGE_BITMAP) || (Weapons[obj->id].flags & WF_IMAGE_VCLIP)) + { + int bm_handle; + int objnum=obj-Objects; + float rot_temp=Weapons[obj->id].phys_info.rotvel.z/65536.0; + int int_game=Gametime/rot_temp; + float diff=Gametime-(int_game*rot_temp); + int rot_angle=diff*65536; + + if (Weapons[obj->id].flags & WF_NO_ROTATE) + rot_angle=(objnum*1000)%65536; + + bm_handle=GetWeaponFireImage (obj->id,0); + ASSERT (bm_handle>=0); + + if (Weapons[obj->id].flags & WF_SATURATE) + rend_SetAlphaType (AT_SATURATE_TEXTURE); + else + rend_SetAlphaType (AT_CONSTANT + AT_TEXTURE); + + rend_SetAlphaValue (Weapons[obj->id].alpha*255); + rend_SetOverlayType (OT_NONE); + rend_SetLighting(LS_NONE); + + if (Weapons[obj->id].flags & WF_PLANAR) + { + vector norm=obj->mtype.phys_info.velocity; + vm_NormalizeVectorFast (&norm); + g3_DrawPlanarRotatedBitmap (&obj->pos,&norm,rot_angle,obj->size,(obj->size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + } + else + g3_DrawRotatedBitmap (&obj->pos,rot_angle,obj->size,(obj->size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + } + else + { + + polymodel_effect pe={0}; + int use_effect=0; + + if (obj->type==OBJ_WEAPON && !Detail_settings.Weapon_coronas_enabled) + { + pe.type|=PEF_NO_GLOWS; + use_effect=1; + } + + if (use_effect) + SetPolymodelEffect (&pe); + + DrawPolygonModel (&obj->pos,&obj->orient,obj->rtype.pobj_info.model_num,NULL,0,1.0,1.0,1.0,0xFFFFFFFF,use_effect); + } + } + + if (Weapons[obj->id].flags & WF_STREAMER) + { + DrawWeaponStreamer (obj); + } + + if (Weapons[obj->id].flags & WF_RING) + { + DrawWeaponRing (obj); + } +} + +// Stops any on/off weapons that are firing +void StopOnOffWeapon (object *obj) +{ + //mprintf ((0,"Stopping on/off weapon!\n")); + obj->weapon_fire_flags &= ~WFF_ON_OFF; + + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(OBJNUM(obj)); + } +} + +// Starts an on/off weapon firing +void StartOnOffWeapon (object *obj, ubyte wb_index) +{ + //mprintf ((0,"Starting on/off weapon!\n")); + obj->weapon_fire_flags|=WFF_ON_OFF; + + dynamic_wb_info *p_dwb=&obj->dynamic_wb[wb_index]; + p_dwb->cur_firing_mask=0; + + + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(OBJNUM(obj)); + } +} + +float Zoom_fov_time=0; + +#define ZOOM_STAY_TIME .25 +#define ZOOM_FOV_TARGET 18 + +void DoZoomStay () +{ + if (Zoom_fov_time>0) + { + Render_FOV=ZOOM_FOV_TARGET; + Zoom_fov_time-=Frametime; + if (Zoom_fov_time<0) + { + Zoom_fov_time=0; + Players[Player_num].flags&=~PLAYER_FLAGS_ZOOMED; + Render_FOV=D3_DEFAULT_FOV; + } + } +} + +// Zooms in for this weapon +void DoZoomEffect (player_weapon *pw,ubyte clear) +{ + if (pw->firing_time<.5) + { + Players[Player_num].turn_scalar=1.0; + Render_FOV=D3_DEFAULT_FOV; + + if (!clear) + DoZoomStay (); + return; + } + + Players[Player_num].turn_scalar=.3f; + + if (pw->firing_time>1.0) + { + // We are fully zoomed in + + Players[Player_num].flags|=PLAYER_FLAGS_ZOOMED; + Render_FOV=ZOOM_FOV_TARGET; + Zoom_fov_time=ZOOM_STAY_TIME; + + // See if we hit anything + // Cast a ray to see what we've hit + Players[Player_num].flags&=~PLAYER_FLAGS_BULLSEYE; + object *obj=&Objects[Players[Player_num].objnum]; + int fate; // Collision type for response code + fvi_info hit_info; // Hit information + fvi_query fq; // Movement query + vector dest=obj->pos+(obj->orient.fvec*5000.0); + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &dest; + fq.rad = .0001f; + fq.thisobjnum = Objects-obj; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS|FQ_IGNORE_POWERUPS|FQ_IGNORE_WEAPONS; + + fate = fvi_FindIntersection(&fq,&hit_info); + + Players[Player_num].zoom_distance=vm_VectorDistance (&obj->pos,&hit_info.hit_pnt); + + if (fate == HIT_SPHERE_2_POLY_OBJECT || fate == HIT_OBJECT) + { + object *hit_obj=&Objects[hit_info.hit_object[0]]; + if (hit_obj->type==OBJ_ROBOT || hit_obj->type==OBJ_PLAYER || (hit_obj->type == OBJ_BUILDING && hit_obj->ai_info)) + Players[Player_num].flags|=PLAYER_FLAGS_BULLSEYE; + } + return; + } + + // calculate zoom effect + float norm=(pw->firing_time-.5)*2; + + Render_FOV=(ZOOM_FOV_TARGET*norm)+((1.0-norm)*D3_DEFAULT_FOV); + + + return; +} + +//Called when a player dies or switches weapons to clear out any active weapon stuff +void ClearPlayerFiring(object *objp,int weapon_type) +{ + //Check parms + ASSERT(objp->type == OBJ_PLAYER); +// ASSERT(objp->id == Player_num); + + //Set up various vars + player *player = &Players[objp->id]; + player_weapon *pw = &player->weapon[weapon_type]; + + //Clear firing flags + if (objp->weapon_fire_flags & WFF_ON_OFF) + StopOnOffWeapon (objp); + + //Clear firing time + pw->firing_time = 0.0; + objp->weapon_fire_flags = 0; + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(OBJNUM(objp)); + } + + // Clear zoom if there is one + if (objp->id==Player_num) + { + player_weapon *pw = &player->weapon[weapon_type]; + int weapon_battery_index = pw->index; + ship *ship = &Ships[player->ship_index]; + + if (ship->fire_flags[weapon_battery_index] & SFF_ZOOM) + { + DoZoomEffect(pw,1); + Players[Player_num].flags&=~PLAYER_FLAGS_ZOOMED; + Zoom_fov_time=0; + } + } + + //Stop firing sound if one playing + if (pw->sound_handle != -1) { + Sound_system.StopSoundImmediate(pw->sound_handle); + pw->sound_handle = -1; + } +} + +//Stops weapon sound & visual effects +void StopWeapon(object *obj, player_weapon *pw, otype_wb_info *wb) +{ + // Stop any on/off weapons + if (obj->weapon_fire_flags & WFF_ON_OFF) + StopOnOffWeapon (obj); + + //Stop spray weapons + if (wb->flags & WBF_SPRAY) + obj->weapon_fire_flags &= ~WFF_SPRAY; + + //Stop firing sound if one playing + if (pw && pw->sound_handle != -1) + { + Sound_system.StopSoundImmediate(pw->sound_handle); + pw->sound_handle = -1; + } + + //Clear firing time + if(pw) + { + pw->firing_time = 0.0; + } + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(OBJNUM(obj)); + } +} + +// Does weapon battery stuff for the permissable network architecture +void DoPermissableWeaponMask (int weapon_battery_index) +{ + player *player = &Players[Player_num]; + object *objp=&Objects[player->objnum]; + ship *ship = &Ships[player->ship_index]; + otype_wb_info *wb = &ship->static_wb[weapon_battery_index]; + dynamic_wb_info *p_dwb = &objp->dynamic_wb[weapon_battery_index]; + + player->last_fire_weapon_time = Gametime; + + + p_dwb->cur_firing_mask++; + p_dwb->last_fire_time = Gametime; + + if(p_dwb->cur_firing_mask >= wb->num_masks) + p_dwb->cur_firing_mask = 0; +} + +// Fires a weapon from our player. Won't fire if ammo/energy requirements aren't met. +// Parameters: weapon_type - either PW_PRIMARY or PW_SECONDARY +void FireWeaponFromPlayer(object *objp,int weapon_type,int down_count,bool down_state,float down_time) +{ + //Check parms + ASSERT(objp->type == OBJ_PLAYER); +// ASSERT(objp->id == Player_num); + ASSERT((weapon_type == PW_PRIMARY) || (weapon_type == PW_SECONDARY)); + + //Set up various vars + player *player = &Players[objp->id]; + player_weapon *pw = &player->weapon[weapon_type]; + int weapon_battery_index = pw->index; + ship *ship = &Ships[player->ship_index]; + otype_wb_info *wb = &ship->static_wb[weapon_battery_index]; + int fire_on_release = (ship->fire_flags[weapon_battery_index] & (SFF_FUSION|SFF_ZOOM)); + bool can_fire_now=WBIsBatteryReady(objp,wb,weapon_battery_index); + dynamic_wb_info *p_dwb = &objp->dynamic_wb[weapon_battery_index]; + float ammo_scalar=1.0; + + if (p_dwb->flags & DWBF_QUAD) + ammo_scalar=2.0; + + //Don't allow down_time to be zero if we have a down count + if (down_count && (down_time == 0.0)) + down_time = 1.0f/100.0f; + + // Check to see if we can release a guided + if (down_count && down_time!=0.0 && Players[objp->id].guided_obj!=NULL && weapon_type==PW_SECONDARY && can_fire_now) + { + ReleaseGuidedMissile (objp->id); + p_dwb->last_fire_time=Gametime; + return; + } + + // Check to see if we can release a user timeout weapon + if (down_count && down_time!=0.0 && Players[objp->id].user_timeout_obj!=NULL && weapon_type==PW_SECONDARY && can_fire_now) + { + if (Game_mode & GM_MULTI) + MultiSendReleaseTimeoutMissile (); + + ReleaseUserTimeoutMissile (objp->id); + p_dwb->last_fire_time=Gametime; + return; + } + + //Check for weapon stopped firing + if ((down_count == 0) && (down_time == 0)) + { + if (ship->fire_flags[weapon_battery_index] & SFF_ZOOM) + DoZoomEffect(pw,0); + + //Abort if we weren't firing last frame either + if (pw->firing_time == 0.0) + return; + + //Check for fire-on-release weapon + if (fire_on_release) + { + if (can_fire_now) + { + float scalar=1.0; + + //Ready? + if (ship->fire_flags[weapon_battery_index] & SFF_FUSION) + { + scalar=1.0+((pw->firing_time/FUSION_RAMP_TIME)*3.0); + + } + + if ((Game_mode & GM_MULTI) && (Netgame.flags & NF_PERMISSABLE)) + { + MultiSendRequestToFire (weapon_battery_index,p_dwb->cur_firing_mask,scalar); + DoPermissableWeaponMask (weapon_battery_index); + } + else + WBFireBattery(objp, wb, 0, weapon_battery_index,scalar); + + player->energy -= wb->energy_usage*ammo_scalar; + if (player->energy < 0) + player->energy = 0; + + if (player->weapon_ammo[weapon_battery_index] < (wb->ammo_usage*ammo_scalar)) + player->weapon_ammo[weapon_battery_index] = 0; + else + player->weapon_ammo[weapon_battery_index] -= (wb->ammo_usage*ammo_scalar); + + player->num_discharges_level++; + + if (ship->fire_flags[weapon_battery_index] & SFF_ZOOM) + { + AddToShakeMagnitude (ship->phys_info.mass*2); + } + } + } + + //Play cut-off sound if one (but not if in permissable game) + if (!(Game_mode & GM_MULTI) || !(Netgame.flags & NF_PERMISSABLE)) + { + int cutoff_sound = ship->firing_release_sound[weapon_battery_index]; + if (cutoff_sound != -1) + Sound_system.Play2dSound(cutoff_sound, SND_PRIORITY_HIGHEST); + } + + //Stop the weapon sound & visual effects + if (weapon_type == PW_PRIMARY) + StopWeapon(objp,pw,wb); + + //We're done + return; + } + + //Check for adequate energy + if ((wb->energy_usage*ammo_scalar) && (player->energy <= 0)) { + AddHUDMessage (TXT_WPNNONRG); + if (weapon_type == PW_PRIMARY) + StopWeapon(objp,pw,wb); + AutoSelectWeapon(weapon_type); + return; + } + + //Check for adequate ammo + if ((wb->ammo_usage*ammo_scalar) && (player->weapon_ammo[weapon_battery_index] <= 0)) { + if (weapon_type == PW_PRIMARY) + AddHUDMessage (TXT_WPNNOAMMO); + else + AddHUDMessage (TXT_WPNNOPROJ); + + if (weapon_type == PW_PRIMARY) + StopWeapon(objp,pw,wb); + AutoSelectWeapon(weapon_type); + return; + } + + //Check for adequate ammo for secondaries... + if (weapon_battery_index >= 10 && (wb->ammo_usage*ammo_scalar > player->weapon_ammo[weapon_battery_index])) + { + if (weapon_type == PW_PRIMARY) + AddHUDMessage (TXT_WPNNOAMMO); + else + AddHUDMessage (TXT_WPNNOPROJ); + + if (weapon_type == PW_PRIMARY) + StopWeapon(objp,pw,wb); + AutoSelectWeapon(weapon_type); + return; + } + + if (!can_fire_now) + return; + + //If continous sound weapon, start the sound if wasn't firing last frame + int firing_sound = ship->firing_sound[weapon_battery_index]; + + if (can_fire_now && (firing_sound != -1) && (pw->firing_time == 0.0)) + pw->sound_handle = Sound_system.Play2dSound(firing_sound, SND_PRIORITY_HIGHEST); + + //Set spray flag if spray weapon + if (wb->flags & WBF_SPRAY) + objp->weapon_fire_flags |= WFF_SPRAY; + + if (ship->fire_flags[weapon_battery_index] & SFF_ZOOM) + DoZoomEffect(pw,0); + + //Do fusion effect if fusion weapon + if (can_fire_now && ship->fire_flags[weapon_battery_index] & SFF_FUSION) + DoFusionEffect(objp,weapon_type); + + //Start any on/off weapons + if ((wb->flags & WBF_ON_OFF) && !(objp->weapon_fire_flags & WFF_ON_OFF)) + StartOnOffWeapon (objp,weapon_battery_index); + + //If not a fire-on-release weapon, then fire if ready + if (!fire_on_release && can_fire_now) { + + if ((Game_mode & GM_MULTI) && (Netgame.flags & NF_PERMISSABLE)) + { + MultiSendRequestToFire (weapon_battery_index,p_dwb->cur_firing_mask); + DoPermissableWeaponMask (weapon_battery_index); + } + else + WBFireBattery(objp, wb, 0, weapon_battery_index); + + //If on/off weapon, scale energy by framerate based on "reference" rate of 20 fps. + if (wb->flags & WBF_ON_OFF) + player->energy -= (wb->energy_usage*ammo_scalar*Frametime/(1.0/20.0)); + else + player->energy -= (wb->energy_usage*ammo_scalar); + if (player->energy < 0) + player->energy = 0; + + if (player->weapon_ammo[weapon_battery_index] < (wb->ammo_usage*ammo_scalar)) + player->weapon_ammo[weapon_battery_index] = 0; + else + player->weapon_ammo[weapon_battery_index] -= (wb->ammo_usage*ammo_scalar); + + player->num_discharges_level++; + } + + + //Update the firing time +#ifdef MACINTOSH + pw->firing_time = down_time; +#else + pw->firing_time += down_time; +#endif + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(OBJNUM(objp)); + } + + // tell IntelliVIBE that we fired a weapon + VIBE_WeaponFire(weapon_type); +} + +// Fires a flare from our player. +// It might make sense to combine this with FireWeaponFromPlayer(), or maybe not +void FireFlareFromPlayer(object *objp) +{ + //Check parms + ASSERT(objp->type == OBJ_PLAYER); +// ASSERT(objp->id == Player_num || objp->control_type == CT_SOAR); + + //Set up various vars + int weapon_battery_index = FLARE_INDEX; + ship *ship = &Ships[Players[objp->id].ship_index]; + otype_wb_info *wb = &ship->static_wb[weapon_battery_index]; + player *player = &Players[objp->id]; + dynamic_wb_info *p_dwb = &objp->dynamic_wb[weapon_battery_index]; + + if (player->energy < wb->energy_usage) { + AddHUDMessage (TXT_WPNFLARENONRG); + return; + } + + if(WBIsBatteryReady(Player_object, wb, weapon_battery_index)) { + if ((Game_mode & GM_MULTI) && (Netgame.flags & NF_PERMISSABLE)) + { + MultiSendRequestToFire (weapon_battery_index,p_dwb->cur_firing_mask); + DoPermissableWeaponMask (weapon_battery_index); + } + else + WBFireBattery(Player_object, wb, 0, weapon_battery_index); + + player->energy -= wb->energy_usage; + if (player->energy < 0) + player->energy = 0; + + //Samir & I think that firing a flare should not count as discharge, even though + //it would allow people push up their kill effiency by killing things with flares. -MT, 10/29/97 + //Players[Player_num].num_discharges_level++; + } +} + +// Creates a gravity field that sucks objects into it +int CreateGravityField (vector *pos,int roomnum,float size,float time,int parent_handle); + + +// Plays the animation that accompanies a weapon death +void DoWeaponExploded (object *obj,vector *norm,vector *collision_point,object *hit_object) +{ + weapon *w=&Weapons[obj->id]; + light_info *li=&w->lighting_info; + vector col_point,normal; + + if (w->flags & WF_ELECTRICAL) + return; + + MakeShockwave(obj, obj->parent_handle); + + if (collision_point==NULL || !(Weapons[obj->id].flags & WF_PLANAR_BLAST)) + col_point=obj->pos; + else + col_point=*collision_point; + + if (norm==NULL) + normal=obj->orient.fvec; + else + normal=*norm; + + + if (Weapons[obj->id].flags & WF_GRAVITY_FIELD) + CreateGravityField (&obj->pos,obj->roomnum,Weapons[obj->id].gravity_size,Weapons[obj->id].gravity_time,obj->parent_handle); + + + if (hit_object==Viewer_object && !(Weapons[obj->id].flags & WF_MATTER_WEAPON)) + return; // Don't draw if its viewer who is getting hit + + if (Weapons[obj->id].flags & WF_PLANAR_BLAST) + { + if (Weapons[obj->id].flags & WF_BLAST_RING) + { + // Create a planar ring + int visnum=VisEffectCreate (VIS_FIREBALL,BLAST_RING_INDEX,obj->roomnum,&col_point); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->size=Weapons[obj->id].explode_size; + vis->lifetime=Weapons[obj->id].explode_time; + vis->lifeleft=Weapons[obj->id].explode_time; + vis->custom_handle=GetTextureBitmap (Weapons[obj->id].explode_image_handle,0); + vis->flags|=VF_PLANAR; + vis->end_pos=normal; + vis->lighting_color=OPAQUE_FLAG|GR_RGB16(li->red_light2*255,li->green_light2*255,li->blue_light2*255); + } + } + else + { + // Create a planar explosion + int visnum; + + if (Weapons[obj->id].explode_image_handle>0) + { + visnum=VisEffectCreate (VIS_FIREBALL,CUSTOM_EXPLOSION_INDEX,obj->roomnum,&col_point); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->size=Weapons[obj->id].explode_size; + vis->lifetime=Weapons[obj->id].explode_time; + vis->lifeleft=Weapons[obj->id].explode_time; + vis->custom_handle=Weapons[obj->id].explode_image_handle; + vis->lighting_color=OPAQUE_FLAG|GR_RGB16(li->red_light2*255,li->green_light2*255,li->blue_light2*255); + } + } + else + visnum=VisEffectCreate (VIS_FIREBALL,GetRandomSmallExplosion(),obj->roomnum,&col_point); + + if (visnum>=0) + { + VisEffects[visnum].flags|=VF_PLANAR; + VisEffects[visnum].end_pos=normal; + + if (Weapons[obj->id].flags & WF_EXPAND) + VisEffects[visnum].flags|=VF_EXPAND; + + } + } + } + else + { + if (Weapons[obj->id].flags & WF_BLAST_RING) + { + // Create a non-planar ring + int visnum=VisEffectCreate (VIS_FIREBALL,BLAST_RING_INDEX,obj->roomnum,&col_point); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->size=Weapons[obj->id].explode_size; + vis->lifetime=Weapons[obj->id].explode_time; + vis->lifeleft=Weapons[obj->id].explode_time; + vis->custom_handle=GetTextureBitmap (Weapons[obj->id].explode_image_handle,0); + vis->lighting_color=OPAQUE_FLAG|GR_RGB16(li->red_light2*255,li->green_light2*255,li->blue_light2*255); + } + } + else + { + // Create a normal "always facing you" explosion + if (Weapons[obj->id].explode_image_handle>0) + { + int visnum=VisEffectCreate (VIS_FIREBALL,CUSTOM_EXPLOSION_INDEX,obj->roomnum,&col_point); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->size=Weapons[obj->id].explode_size; + vis->lifetime=Weapons[obj->id].explode_time; + vis->lifeleft=Weapons[obj->id].explode_time; + vis->custom_handle=Weapons[obj->id].explode_image_handle; + vis->lighting_color=OPAQUE_FLAG|GR_RGB16(li->red_light2*255,li->green_light2*255,li->blue_light2*255); + + if (Weapons[obj->id].flags & WF_EXPAND) + VisEffects[visnum].flags|=VF_EXPAND; + + } + } + else + VisEffectCreate (VIS_FIREBALL,GetRandomSmallExplosion(),obj->roomnum,&col_point); + } + } + +} + +// Creates chidren from a dying weapon +void CreateTimeoutSpawnFromWeapon (object *obj) +{ + int n=obj->id; + + + ASSERT (Weapons[n].spawn_count>0 && Weapons[n].spawn_handle>=0); + + int num=Weapons[n].spawn_count; + int spawn_index=Weapons[n].spawn_handle; + vector pos; + matrix orient; + + // See if we should spawn the alternate + if (Weapons[n].alternate_spawn_handle>=0 && Weapons[n].alternate_chance>0) + { + if ((ps_rand()%100)pos+(obj->orient.fvec*obj->size/2); + + CreateAndFireWeapon (&pos,&obj->orient.fvec,obj,spawn_index); + } + else + { + matrix temp_mat; + pos=obj->pos+(obj->orient.fvec*obj->size/2); + + float norm=((float)(i-1))/((float)num-1); + + float mysin=FixSin (norm*65536); + float mycos=FixCos (norm*65536); + + int x_twist=mycos*4096; + int y_twist=mysin*4096; + + vm_AnglesToMatrix (&temp_mat,x_twist,y_twist,0); + vm_TransposeMatrix (&temp_mat); + + orient=obj->orient*temp_mat; + + CreateAndFireWeapon (&pos,&orient.fvec,obj,spawn_index); + + } + } +} + +// Creates a robot as a countermeasure +void CreateRobotSpawnFromWeapon (object *obj) +{ + int index=obj->id; + weapon *wp=&Weapons[index]; + + if ((Game_mode & GM_MULTI) && Netgame.local_role!=LR_SERVER) + return; // clients do not create robots without the servers permission + + if (wp->robot_spawn_handle==-1) + { + mprintf ((0,"Trying to create an invalid robot spawn!\n")); + return; + } + object *parent_obj=ObjGetUltimateParent (obj); + + int objnum=ObjCreate(OBJ_ROBOT, wp->robot_spawn_handle, obj->roomnum, &obj->pos, &obj->orient, parent_obj->handle); + + if (objnum<0) + return; + + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER) + { + MultiSendObject (&Objects[objnum],0); + } + + // Create the scripts for it + InitObjectScripts (&Objects[objnum]); +} + +// Returns a object to home on for spawned weapons +object *FindSpawnHomingTarget (object *obj) +{ + int i; + float best_dist=MAX_HOMING_DIST/2; + int best_index=-1; + object *weapon_parent = ObjGet(obj->parent_handle); + + for(i = 0; i <= Highest_object_index; i++) + { + if((i != OBJNUM(weapon_parent)) && ((Objects[i].type == OBJ_ROBOT) || (Objects[i].type == OBJ_PLAYER) || (Objects[i].type == OBJ_BUILDING && Objects[i].ai_info))) + { + if(BOA_IsVisible(obj->roomnum, Objects[i].roomnum)) + { + vector to_target = Objects[i].pos - obj->pos; + + if(Objects[i].effect_info && Objects[i].effect_info->type_flags & EF_CLOAKED) + continue; + + if(ObjGet(obj->parent_handle) && !AIObjEnemy(ObjGetUltimateParent(ObjGet(obj->parent_handle)), ObjGetUltimateParent(&Objects[i]))) + continue; + + + float dist_to_target = vm_NormalizeVector(&to_target) - obj->size - Objects[i].size; + + if(dist_to_target < best_dist) + { + fvi_query fq; + fvi_info hit_info; + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &Objects[i].pos; + fq.rad = 0.0f; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = FQ_TRANSPOINT | FQ_CHECK_OBJS | FQ_ONLY_DOOR_OBJ | FQ_NO_RELINK; + + fvi_FindIntersection(&fq, &hit_info); + if(hit_info.hit_type[0] == HIT_NONE) + { + best_index = i; + best_dist = dist_to_target; + } + } + } + } + } + if(best_index >= 0) + return &Objects[best_index]; + + return NULL; +} + +// Creates chidren from a dying weapon +void CreateImpactSpawnFromWeapon (object *obj,vector *normal) +{ + int n=obj->id; + object *home_target=NULL; + + ASSERT (Weapons[n].spawn_count>0 && Weapons[n].spawn_handle>=0); + + int num=Weapons[n].spawn_count; + int spawn_index=Weapons[n].spawn_handle; + + // See if we should spawn the alternate + if (Weapons[n].alternate_spawn_handle>=0 && Weapons[n].alternate_chance>0) + { + if ((ps_rand()%100)pos-obj->pos; + float mag=vm_GetMagnitudeFast (&subvec); + subvec/=mag; + + int objnum=CreateAndFireWeapon (&obj->pos,&subvec,obj,spawn_index); + + // Stagger the weapons a bit + if (objnum>-1) + { + object *created_obj=&Objects[objnum]; + + float scalar=.2+((float)(ps_rand()%1000)/1000.0); + + created_obj->mtype.phys_info.velocity*=scalar; + } + } + else + { + matrix temp_mat; + matrix rot_mat; + vector new_norm; + + float norm=((float)(ps_rand()%1000))/1000.0; + int x_twist=16384-(norm*32768); + if (x_twist<0) + x_twist=65536+x_twist; + + norm=((float)(ps_rand()%1000))/1000.0; + int y_twist=16384-(norm*32768); + if (y_twist<0) + y_twist=65536+y_twist; + + + vm_VectorToMatrix (&rot_mat,normal,NULL,NULL); + vm_TransposeMatrix (&rot_mat); + vm_AnglesToMatrix (&temp_mat,x_twist,y_twist,0); + + new_norm=temp_mat.fvec * rot_mat; + + CreateAndFireWeapon (&obj->pos,&new_norm,obj,spawn_index); + } + } + +} + +// Does whatever to be done when a weapon times out +void TimeoutWeapon (object *obj) +{ + int n=obj->id; + + if ((Weapons[n].flags & WF_SPAWNS_TIMEOUT) && Weapons[n].spawn_count>0 && Weapons[n].spawn_handle>=0) + { + DoWeaponExploded (obj,&obj->orient.fvec); + CreateTimeoutSpawnFromWeapon(obj); + } + + if((Weapons[n].flags & WF_MATTER_WEAPON) && !(Weapons[n].flags & WF_SPAWNS_TIMEOUT) && (Weapons[n].spawn_count <= 0)) + { + vector temp = Zero_vector; + + if (Weapons[obj->id].sounds[WSI_IMPACT_WALL]!=SOUND_NONE_INDEX) + Sound_system.Play3dSound(Weapons[obj->id].sounds[WSI_IMPACT_WALL], SND_PRIORITY_HIGH, obj); + + temp.y = 1.0; + + DoWeaponExploded (obj, &temp); + } + + if (Weapons[n].flags & WF_TIMEOUT_WALL) + { + if (Weapons[obj->id].sounds[WSI_IMPACT_WALL]!=SOUND_NONE_INDEX) + Sound_system.Play3dSound(Weapons[obj->id].sounds[WSI_IMPACT_WALL], SND_PRIORITY_HIGH, obj); + + DoWeaponExploded (obj); + } + + if ((Weapons[obj->id].flags & WF_SPAWNS_ROBOT) && (Weapons[obj->id].flags & WF_COUNTERMEASURE) && Weapons[obj->id].robot_spawn_handle>=0) + CreateRobotSpawnFromWeapon (obj); +} + + +// Given a parent object and a weapon id, creates that countermeasure +void CreateCountermeasureFromObject (object *parent,int weapon_id) +{ + ASSERT (parent!=NULL); + ASSERT (Weapons[weapon_id].used); + + if (Game_mode & GM_MULTI) + MultiSendRequestCountermeasure (parent-Objects,weapon_id); + else + FireWeaponFromObject (parent,weapon_id,5); +} + + +// Releases the guided missile of a passed in player +void ReleaseGuidedMissile (int slot) +{ + ASSERT (Players[slot].guided_obj); + if(!Players[slot].guided_obj) + return; + + if( (Game_mode&GM_MULTI) && (slot==Player_num) ) + { + MultiSendMissileRelease(slot,true); + } + + Players[slot].guided_obj->mtype.phys_info.flags&=~PF_GUIDED; + Players[slot].guided_obj=NULL; +} + + +// Releases the timeout missile of a passed in player +void ReleaseUserTimeoutMissile (int slot) +{ + ASSERT (Players[slot].user_timeout_obj); + if(!Players[slot].user_timeout_obj) + return; + + if( (Game_mode&GM_MULTI) && (slot==Player_num) ) + { + MultiSendMissileRelease(slot,false); + } + + TimeoutWeapon (Players[slot].user_timeout_obj); + SetObjectDeadFlag (Players[slot].user_timeout_obj); + + Players[slot].user_timeout_obj=NULL; +} + + + + diff --git a/Descent3/aiambient.cpp b/Descent3/aiambient.cpp new file mode 100644 index 000000000..69d583801 --- /dev/null +++ b/Descent3/aiambient.cpp @@ -0,0 +1,218 @@ +#include "aiambient.h" +#include "string.h" +#include "objinfo.h" +#include +#include "game.h" +#include "psrand.h" + +ambient_life a_life; + +#define AL_VERSION 1 + +void ambient_life::DoFrame(void) +{ +} + +void ambient_life::InitForLevel(void) +{ + char i; + + for(i = 0; i < MAX_AL_TYPES; i++) + { + ComputeNextSize(i); + m_cur_num[i] = 0; + m_next_do_time[i] = Gametime; + } + + DoFrame(); +} + +void ambient_life::GetALValue(char i, char field, void *ptr) +{ + switch(field) + { + case ALI_TYPE: + *((int *)ptr) = m_type[i]; + break; + case ALI_TOTAL: + *((unsigned char *)ptr) = m_total[i]; + break; + case ALI_MAX: + *((unsigned char *)ptr) = m_max[i]; + break; + case ALI_MIN: + *((unsigned char *)ptr) = m_min[i]; + break; + case ALI_FLAGS: + *((unsigned char *)ptr) = m_flags[i]; + break; + } +} + +void ambient_life::ComputeNextSize(char i) +{ + char diff = m_max[i] - m_min[i]; + if(diff > 0) + { + char offset = ps_rand()%diff; + m_next_size[i] = m_min[i] + offset; + } + else + { + m_next_size[i] = m_max[i]; + } +} + +void ambient_life::SetALValue(char i, char field, void *ptr) +{ + switch(field) + { + case ALI_TYPE: + m_type[i] = *((int *)ptr); + break; + case ALI_TOTAL: + m_total[i] = *((unsigned char *)ptr); + break; + case ALI_MAX: + m_max[i] = *((unsigned char *)ptr); + break; + case ALI_MIN: + m_min[i] = *((unsigned char *)ptr); + break; + case ALI_FLAGS: + m_flags[i] = *((unsigned char *)ptr); + break; + } + + //Jeff(Linux): Commented out because m_total[] is unsigned, so never negative + //if(m_total[i] < 0) + // m_total[i] = 0; + if(m_total[i] > MAX_ALS_PER_TYPE) + m_total[i] = MAX_ALS_PER_TYPE; + + //Jeff(Linux): Commented out because m_max[] is unsigned, so never negative + //if(m_max[i] < 0) + // m_max[i] = 0; + if(m_max[i] > m_total[i]) + m_max[i] = m_total[i]; + + //Jeff(Linux): Commented out because m_min[] is unsigned, so never negative + //if(m_min[i] < 0) + // m_min[i] = 0; + if(m_min[i] > m_max[i]) + m_min[i] = m_max[i]; + + ComputeNextSize(i); +} + +void ambient_life::ALReset() +{ + int i; + int j; + + for(i = 0; i < MAX_AL_TYPES; i++) + { + m_type[i] = -1; + + for(j = 0; j < MAX_ALS_PER_TYPE; j++) + { + m_handle[i][j] = 0; + } + + m_total[i] = 0; + m_cur_num[i] = 0; + m_flags[i] = 0; + + m_min[i] = 0; + m_max[i] = 0; + + m_next_do_time[i] = 0; + m_next_size[i] = 0; + } +} + +void ambient_life::SaveData(CFILE *fp) +{ + int i, j; + short len; + + cf_WriteInt(fp, AL_VERSION); + + for(i = 0; i < MAX_AL_TYPES; i++) + { + int type = m_type[i]; + + if(type >= 0) + { + len = strlen(Object_info[type].name) + 1; // Accounts for NULL charactor + cf_WriteShort(fp, len); + for(j = 0; j < len; j++) + { + cf_WriteByte(fp, Object_info[type].name[j]); + } + } + else + { + cf_WriteShort(fp, 1); + cf_WriteByte(fp, '\0'); + } + + cf_WriteByte(fp, m_total[i]); + cf_WriteByte(fp, m_flags[i]); + cf_WriteByte(fp, m_min[i]); + cf_WriteByte(fp, m_max[i]); + cf_WriteByte(fp, m_next_size[i]); + cf_WriteFloat(fp, m_next_do_time[i]); + } + + for(i = 0; i < MAX_AL_TYPES; i++) + { + cf_WriteByte(fp, m_cur_num[i]); + + for(j = 0; j < m_cur_num[i]; j++) + { + cf_WriteInt(fp, m_handle[i][j]); + } + } +} + +void ambient_life::LoadData(CFILE *fp) +{ + int version = cf_ReadInt(fp); + int i, j; + int len; + char temp_name[256]; + + if(version < 1) + return; + + for(i = 0; i < MAX_AL_TYPES; i++) + { + len = cf_ReadShort(fp); + for(j = 0; j < len; j++) + { + { + temp_name[j] = cf_ReadByte(fp); + } + } + + m_type[i] = FindObjectIDName(temp_name); + + m_total[i] = cf_ReadByte(fp); + m_flags[i] = cf_ReadByte(fp); + m_min[i] = cf_ReadByte(fp); + m_max[i] = cf_ReadByte(fp); + m_next_size[i] = cf_ReadByte(fp); + m_next_do_time[i] = cf_ReadFloat(fp); + } + + for(i = 0; i < MAX_AL_TYPES; i++) + { + m_cur_num[i] = cf_ReadByte(fp); + + for(j = 0; j < m_cur_num[i]; j++) + { + m_handle[i][j] = cf_ReadInt(fp); + } + } +} \ No newline at end of file diff --git a/Descent3/aiambient.h b/Descent3/aiambient.h new file mode 100644 index 000000000..d5372abdf --- /dev/null +++ b/Descent3/aiambient.h @@ -0,0 +1,54 @@ +#ifndef _AIAMBIENT_H_ +#define _AIAMBIENT_H_ + +#include "CFILE.H" + +#define MAX_AL_TYPES 6 +#define MAX_ALS_PER_TYPE 130 + +#define ALF_INSIDE 0x01 + +#define ALI_TYPE 0 +#define ALI_TOTAL 1 +#define ALI_MAX 2 +#define ALI_MIN 3 +#define ALI_FLAGS 4 + +#define CHECK_INTERVAL_MIN 5.0f +#define CHECK_INTERVAL_MAX 10.0f + +class ambient_life +{ + // Editor settable values + int m_type[MAX_AL_TYPES]; + unsigned char m_total[MAX_AL_TYPES]; + unsigned char m_max[MAX_AL_TYPES]; + unsigned char m_min[MAX_AL_TYPES]; + unsigned char m_flags[MAX_AL_TYPES]; + + // These are never set or gotten from outside of the class + unsigned char m_cur_num[MAX_AL_TYPES]; + int m_handle[MAX_AL_TYPES][MAX_ALS_PER_TYPE]; + + // Don't save these... + unsigned char m_next_size[MAX_AL_TYPES]; + float m_next_do_time[MAX_AL_TYPES]; + + void ComputeNextSize(char index); + + public: + ambient_life(){ALReset();} + + void GetALValue(char index, char field, void *ptr); + void SetALValue(char index, char field, void *ptr); + void SaveData(CFILE *fptr); + void LoadData(CFILE *fptr); + + void DoFrame(); + void InitForLevel(void); + void ALReset(); +}; + +extern ambient_life a_life; + +#endif \ No newline at end of file diff --git a/Descent3/aipath.cpp b/Descent3/aipath.cpp new file mode 100644 index 000000000..dbd386563 --- /dev/null +++ b/Descent3/aipath.cpp @@ -0,0 +1,1405 @@ +#include "aipath.h" +#include "gamepath.h" +#include "AIGoal.h" +#include "BOA.h" +#include "AIMain.h" +#include "object.h" +#include "objinfo.h" +#include "memory.h" +#include "doorway.h" +#include "aistruct.h" +#include "terrain.h" +#include "psrand.h" +#include "findintersection.h" + +#define MAX_DYNAMIC_PATHS 50 +#define MAX_NODES 50 + +ai_dynamic_path AIDynamicPath[MAX_DYNAMIC_PATHS]; +int AIAltPath[MAX_ROOMS]; +int AIAltPathNumNodes; + +void AIUpdatePathInfo(q_item **node_list, int start, int end) +{ + int cur_room = end; + int i; + + AIAltPathNumNodes = 0; + +// mprintf((0, "start crash loop\n")); + while(cur_room != -1) + { + AIAltPath[AIAltPathNumNodes++] = cur_room; + cur_room = node_list[cur_room]->parent; + } +// mprintf((0, "end crash loop\n")); + + // Reverse the list (so it is what we want) + for(i = 0; i < AIAltPathNumNodes>>1; i++) + { + int temp; + + temp = AIAltPath[i]; + AIAltPath[i] = AIAltPath[AIAltPathNumNodes - i - 1]; + AIAltPath[AIAltPathNumNodes - i - 1] = temp; + } +} + +// Ok to use Highest_room_index offset stuff +bool AIFindAltPath(object *obj, int i, int j, float *dist) +{ + i = BOA_INDEX(i); + j = BOA_INDEX(j); + + pq PQPath; + int counter; + q_item *start_node = new q_item(i, -1, 0.0f); + q_item *cur_node; + bool f_found = false; + + q_item *node_list[MAX_ROOMS + 8]; + +// mprintf((0, "AI: Finding an alternate path from %d to %d\n", i, j)); + + if(i == -1 || j == -1) + { + delete start_node; + return false; + } + + memset(node_list, 0, sizeof(q_item *) * (MAX_ROOMS + 8)); + + PQPath.push(start_node); + + while(cur_node = PQPath.pop()) + { + node_list[BOA_INDEX(cur_node->roomnum)] = cur_node; + ASSERT(BOA_INDEX(cur_node->roomnum) >= 0 && BOA_INDEX(cur_node->roomnum) <= Highest_room_index + 8); + + if(cur_node->roomnum == j) + { + AIUpdatePathInfo(node_list, i, j); + f_found = true; + goto done; + } + + int num_portals; + bool f_room = true; + int t_index; + + if(cur_node->roomnum <= Highest_room_index) + { + num_portals = Rooms[cur_node->roomnum].num_portals; + } + else + { + t_index = cur_node->roomnum - Highest_room_index - 1; + num_portals = BOA_num_connect[t_index]; + f_room = false; + } + + for(counter = 0; counter < num_portals; counter++) + { + int next_room; + q_item *list_item; + float new_cost; + + if(!BOA_PassablePortal(cur_node->roomnum, counter, false, false)) + continue; + + if(f_room) + next_room = Rooms[cur_node->roomnum].portals[counter].croom; + else + next_room = BOA_connect[t_index][counter].roomnum; + + if(next_room <= Highest_room_index && Rooms[next_room].flags & RF_EXTERNAL) + { + int next_portal; + next_portal = BOA_DetermineStartRoomPortal(BOA_INDEX(next_room), NULL, BOA_INDEX(cur_node->roomnum), NULL); + + int cell = GetTerrainCellFromPos(&Rooms[next_room].portals[next_portal].path_pnt); + ASSERT(cell != -1); //DAJ -1FIX + next_room = Highest_room_index + TERRAIN_REGION(cell) + 1; + } + + if(next_room < 0 || next_room == BOA_NO_PATH) + continue; + + if(BOA_LockedDoor(obj, next_room)) + continue; + + int next_portal; + + if(BOA_INDEX(next_room) != BOA_INDEX(cur_node->roomnum)) + { + next_portal = BOA_DetermineStartRoomPortal(BOA_INDEX(next_room), NULL, BOA_INDEX(cur_node->roomnum), NULL, false, false, NULL); + } + + if(next_portal == -1) + continue; + + if(BOA_cost_array[cur_node->roomnum][counter] >= 0.0f) + new_cost = cur_node->cost + (1.0f + 0.1f*((float)ps_rand() - (float)RAND_MAX/2.0f)/((float)RAND_MAX/2.0f)) * (BOA_cost_array[cur_node->roomnum][counter] + BOA_cost_array[BOA_INDEX(next_room)][next_portal]); + else + continue; + + //new_cost = cur_node->cost + BOA_cost_array[cur_node->roomnum][counter]; + list_item = node_list[BOA_INDEX(next_room)]; + + if(list_item != NULL && list_item->cost < new_cost) + continue; + + if(list_item == NULL) + { + list_item = new q_item(BOA_INDEX(next_room), cur_node->roomnum, new_cost); + ASSERT(BOA_INDEX(cur_node->roomnum) >= 0 && BOA_INDEX(cur_node->roomnum) <= Highest_room_index + 8); + + node_list[BOA_INDEX(next_room)] = list_item; + ASSERT(BOA_INDEX(next_room) >= 0 && BOA_INDEX(next_room) <= Highest_room_index + 8); + PQPath.push(list_item); + } + else + { + list_item->cost = new_cost; + list_item->parent = cur_node->roomnum; + } + } + } + + //Mark as an impossible path. + +// mprintf((0, "Found an impossible path\n")); + +done: + for(counter = 0; counter < MAX_ROOMS + 8; counter++) + { + if (node_list[counter]) delete node_list[counter]; + } + + if(f_found && dist) + { + *dist = 0.0f; + + if(AIAltPathNumNodes > 1) + { + int x; + + for(x = 0; x < AIAltPathNumNodes - 1; x++) + { + int cur_room = AIAltPath[x]; + int next_room = AIAltPath[x + 1]; + + int cur_portal = BOA_DetermineStartRoomPortal(cur_room, NULL, next_room, NULL); + int next_portal = BOA_DetermineStartRoomPortal(next_room, NULL, cur_room, NULL); + + ASSERT(cur_portal != -1 && next_portal != -1); + + *dist += BOA_cost_array[cur_room][cur_portal] + BOA_cost_array[next_room][next_portal]; + } + } + } + +// mprintf((0, "Done\n")); + return f_found; +} + +bool AIPathGetCurrentNodePos(ai_path_info *aip, vector *pos, int *room = NULL) +{ + int c_path = aip->cur_path; + int c_node = aip->cur_node; + + int p_type = aip->path_type[c_path]; + int p_index = aip->path_id[c_path]; + + if(p_type == AIP_DYNAMIC) + { + // chrishack -- add asserts + + *pos = AIDynamicPath[p_index].pos[c_node]; + if(room) + { + *room = AIDynamicPath[p_index].roomnum[c_node]; + } + + return true; + } + else if (p_type == AIP_STATIC) + { + // chrishack -- add asserts + + *pos = GamePaths[p_index].pathnodes[c_node].pos; + if(room) + { + *room = GamePaths[p_index].pathnodes[c_node].roomnum; + } + + return true; + } + + return false; +} + +// Chrishack -- need to add static path flag stuff +bool AIPathMoveToNextNode(ai_path_info *aip) +{ + int c_path = aip->cur_path; + int c_node = aip->cur_node; + + int p_type = aip->path_type[c_path]; + int p_index = aip->path_id[c_path]; + + c_node++; + + if(c_node <= aip->path_end_node[c_path]) + { + aip->cur_node++; + return true; + } + + c_path++; + + // We must want to check the next path + if(c_path < aip->num_paths) + { + aip->cur_path++; + aip->cur_node = aip->path_start_node[aip->cur_path]; + + return true; + } + + return false; +} + +bool AIPathAtStart(ai_path_info *aip) +{ + if(aip->cur_path == 0 && + aip->cur_node == aip->path_start_node[0]) + return true; + else + return false; +} + +bool AIPathAtEnd(ai_path_info *aip) +{ + if(aip->cur_path == aip->num_paths - 1 && + aip->cur_node == aip->path_end_node[aip->num_paths - 1]) + return true; + else + return false; +} + +void AIPathSetAtEnd(ai_path_info *aip) +{ + aip->cur_path = aip->num_paths - 1; + aip->cur_node = aip->path_end_node[aip->num_paths - 1]; +} + +void AIPathSetAtStart(ai_path_info *aip) +{ + aip->cur_path = 0; + aip->cur_node = aip->path_start_node[0]; +} + +bool AIPathMoveToPrevNode(ai_path_info *aip) +{ + int c_path = aip->cur_path; + int c_node = aip->cur_node; + + c_node--; + + if(c_node >= 0) + { + aip->cur_node--; + return true; + } + + c_path--; + + // We must want to check the next path + if(c_path > 0) + { + aip->cur_path--; + + if(aip->path_type[c_path] == AIP_DYNAMIC) + { + aip->cur_node = AIDynamicPath[aip->path_id[c_path]].num_nodes - 1; + } + else if(aip->path_type[c_path] == AIP_STATIC) + { + aip->cur_node = aip->path_end_node[c_path]; + } + return true; + } + + return false; +} + +bool AIPathGetNextNodePos (ai_path_info *aip, vector *pos, int *room = NULL) +{ + if(AIPathMoveToNextNode(aip)) + { + AIPathGetCurrentNodePos(aip, pos, room); + AIPathMoveToPrevNode(aip); + + return true; + } + + return false; +} + +bool AIPathGetPrevNodePos (ai_path_info *aip, vector *pos, int *room = NULL) +{ + if(AIPathMoveToPrevNode(aip)) + { + AIPathGetCurrentNodePos(aip, pos, room); + AIPathMoveToNextNode(aip); + + return true; + } + + return false; +} + +void AIPathComplete(object *obj) +{ + GoalPathComplete(obj); +} + +void AIPathMoveTurnTowardsNode(object *obj, vector *mdir, bool *f_moved) +{ + ai_path_info *aip = &obj->ai_info->path; + goal *g_ptr = &obj->ai_info->goals[obj->ai_info->path.goal_index]; + + bool f_too_far_from_end = false; + +// game_path *p = &GamePaths[path_info->path_id]; + int n = aip->cur_node; + int p = aip->cur_path; + + bool f_reverse = (g_ptr->flags & GF_PATH_MOVE_REVERSE_DIR) != 0; + + ai_frame *ai_info = obj->ai_info; + + vector cur_pos; + + AIPathGetCurrentNodePos(aip, &cur_pos); +// mprintf((0, "Cur pos = %f,%f,%f\n", XYZ(&cur_pos))); + +// mprintf((0, "1 R %d, Goal p %d, n %d\n", f_reverse, p, n)); + +// mprintf((0, "s %f, %f, %f\n", XYZ(&obj->pos))); +// mprintf((0, "l %f, %f, %f\n", XYZ(&obj->last_pos))); + +// mprintf((0, "%d is at Node %d\n", OBJNUM(obj), n)); + + bool f_force_complete_stopping = false; + + if(((!f_reverse && AIPathAtEnd(aip)) || (f_reverse && AIPathAtStart(aip))) && (((goal *)(&obj->ai_info->goals[aip->goal_index]))->circle_distance == 0.0f) && obj->movement_type == MT_PHYSICS) + { + f_too_far_from_end = !AIMoveTowardsPosition(obj, &cur_pos, 1.0, true, mdir, f_moved); + if(!f_too_far_from_end) + { + f_force_complete_stopping = true; + } + } + else + { + vector dir = cur_pos - obj->pos; + + if(obj->movement_type == MT_WALKING) + { + float dot = dir * obj->orient.uvec; + dir -= dot * obj->orient.uvec; + } + vm_NormalizeVector(&dir); + + *mdir = dir; + *f_moved = false; +// AIMoveTowardsDir(obj, &dir); + } +// removed check if(f_reverse && (path_info->next_node < path_info->start_node)) +// removed check { +// removed check mprintf((0, "AI ERROR: Fixed a bad predefined path next_node to goal_node")); +// removed check path_info->next_node = path_info->start_node; +// removed check Int3(); +// removed check } +// removed check +// removed check if(!(f_reverse) && path_info->next_node > path_info->end_node) +// removed check { +// removed check mprintf((0, "AI ERROR: Fixed a bad predefined path next_node to goal_node")); +// removed check path_info->next_node = path_info->end_node; +// removed check Int3(); +// removed check } + + bool f_pass_node; + int last_n; + int last_p; + +pass_node: + + f_pass_node = false; + last_n = n; + last_p = p; + + if(obj->movement_type == MT_WALKING) + { + vector dir = cur_pos - obj->pos; + + if(obj->movement_type == MT_WALKING) + { + float dot = dir * obj->orient.uvec; + dir -= dot * obj->orient.uvec; + } + float dist = vm_GetMagnitude(&dir); + + if(dist < 1.5f) + { + f_pass_node = true; + } + } + + if(f_force_complete_stopping) + { + f_pass_node = true; + } + + if(!f_pass_node) + { + if(!f_reverse) + { + if(AIPathAtStart(aip)) + { + if(AIPathAtEnd(aip)) + { + if((obj->pos - cur_pos) * (obj->last_pos - cur_pos) <= 0.0f && !f_too_far_from_end) + f_pass_node = true; + } + else + { + vector next_pos; + AIPathGetNextNodePos(aip, &next_pos, NULL); + + vector proj_line = obj->pos - cur_pos; + vector line = next_pos - cur_pos; + float proj = proj_line * line; + + if(proj >= 0.0f) f_pass_node = true; + } + } + else + { + vector prev_pos; + AIPathGetPrevNodePos(aip, &prev_pos, NULL); + + vector proj = obj->pos - prev_pos; + vector line = cur_pos - prev_pos; + float line_len = vm_NormalizeVector(&line); + float proj_len = proj * line; + + if(proj_len >= line_len) f_pass_node = true; + } + } + else + { + if(AIPathAtEnd(aip)) + { + if(AIPathAtStart(aip)) + { + if((obj->pos - cur_pos) * (obj->last_pos - cur_pos) <= 0.0f && !f_too_far_from_end) + f_pass_node = true; + } + else + { + vector prev_pos; + AIPathGetPrevNodePos(aip, &prev_pos, NULL); + + vector proj_line = obj->pos - cur_pos; + vector line =prev_pos - cur_pos; + float proj = proj_line * line; + + if(proj >= 0.0f) f_pass_node = true; + } + } + else + { + vector next_pos; + AIPathGetNextNodePos(aip, &next_pos, NULL); + + vector proj = obj->pos - next_pos; + vector line = cur_pos - next_pos; + float line_len = vm_NormalizeVector(&line); + float proj_len = proj * line; + + if(proj_len >= line_len) f_pass_node = true; + } + } + } + + if (f_pass_node) + { +// mprintf((0, "f_pass_node\n")); + + if(((f_reverse) && AIPathAtStart(aip)) || + ((!f_reverse) && AIPathAtEnd(aip))) + { +// mprintf((0, "At end node: ")); + + switch(g_ptr->flags & GFM_END_OF_PATH) + { + case GF_PATH_REVERSE_AT_END: +// mprintf((0, "End reverse dir\n")); + if(f_reverse) + g_ptr->flags &= ~GF_PATH_MOVE_REVERSE_DIR; + else + g_ptr->flags |= GF_PATH_MOVE_REVERSE_DIR; + return; + case GF_PATH_CIRCLE_AT_END: +// mprintf((0, "End circle dir\n")); + if(f_reverse) + AIPathSetAtEnd(aip); + else + AIPathSetAtStart(aip); + return; + default: +// mprintf((0, "End kill goal\n")); + AIPathComplete(obj); + return; + } + } + else + { +// mprintf((0, "Passed node\n")); + if(g_ptr->flags & GF_PATH_MOVE_REVERSE_DIR) + AIPathMoveToPrevNode(aip); + else + AIPathMoveToNextNode(aip); + } + + AIPathGetCurrentNodePos(aip, &cur_pos); + + n = aip->cur_node; + p = aip->cur_path; + + f_reverse = (g_ptr->flags & GF_PATH_MOVE_REVERSE_DIR) != 0; + + if(n != last_n || p != last_p) goto pass_node; + } +} + +bool AIPathFreeDPathSlot(int slot) +{ + ASSERT(AIDynamicPath[slot].use_count >= 0); + ASSERT(slot >= 0 && slot < MAX_DYNAMIC_PATHS && AIDynamicPath[slot].use_count != 0); + + AIDynamicPath[slot].use_count--; + + if(!AIDynamicPath[slot].use_count) + { + AIDynamicPath[slot].num_nodes = 0; + AIDynamicPath[slot].owner_handle = OBJECT_HANDLE_NONE; + } + + return true; +} + +bool AIPathGetDPathSlot(int *slot, int handle) +{ + int i; + + for(i = 0; i < MAX_DYNAMIC_PATHS; i++) + { + if(!AIDynamicPath[i].use_count) + { + AIDynamicPath[i].use_count++; + *slot = i; + AIDynamicPath[i].owner_handle = handle; + return true; + } + else if(ObjGet(AIDynamicPath[i].owner_handle) == NULL && AIDynamicPath[i].use_count == 1) + { + AIPathFreeDPathSlot(i); + + AIDynamicPath[i].use_count++; + *slot = i; + AIDynamicPath[i].owner_handle = handle; + return true; + } + } + + mprintf((0, "Out of dynamic paths")); + ASSERT(0); // -- get chris + return false; +} + +bool AIPathInitPath(ai_path_info *aip) +{ + ASSERT(aip); + + aip->num_paths = 0; + aip->cur_path = 0; + aip->cur_node = 0; + + aip->goal_index = -1; + aip->goal_uid = -1; + + return true; +} + +bool AIPathFreePath(ai_path_info *aip) +{ + int i; + + for(i = 0; i < aip->num_paths; i++) + { + if(aip->path_type[i] == AIP_DYNAMIC) + { + AIPathFreeDPathSlot(aip->path_id[i]); + } + } + + AIPathInitPath(aip); + + return true; +} + +bool AIPathAddDPath(ai_path_info *aip, int handle) +{ + int slot; + bool status; + + if(aip->num_paths >= MAX_JOINED_PATHS) + { + mprintf((0, "AI ERROR: Tried to join too many paths (Adding dpath)\n")); + return false; + } + + status = AIPathGetDPathSlot(&slot, handle); + + if(status) + { + aip->path_id[aip->num_paths] = slot; + aip->path_type[aip->num_paths] = AIP_DYNAMIC; + + aip->path_start_node[aip->num_paths] = 0; + aip->path_end_node[aip->num_paths] = 0; + + aip->path_flags[aip->num_paths] = 0; + + aip->num_paths++; + } + + return status; +} + +bool AIPathAddStaticPath(ai_path_info *aip, int path_id, int start_index, int end_index) +{ + // chrishack -- validation code is needed + + if(aip->num_paths >= MAX_JOINED_PATHS) + { + mprintf((0, "AI ERROR: Tried to join too many paths (Adding spath)\n")); + return false; + } + + aip->path_id[aip->num_paths] = path_id; + aip->path_type[aip->num_paths] = AIP_STATIC; + aip->path_start_node[aip->num_paths] = start_index; + aip->path_end_node[aip->num_paths] = end_index; + + aip->num_paths++; + + return true; +} + +inline bool AIPathAddDPathNode(ai_path_info *aip, int *slot, int *cur_node, vector *pos, int room, int handle) +{ + int status; + + if(*cur_node == MAX_NODES) + { + status = AIPathAddDPath(aip, handle); + if(!status) Int3(); //chrishack -- out of dynamic paths + + AIDynamicPath[*slot].num_nodes = MAX_NODES; + *cur_node = 0; + *slot = aip->path_id[aip->num_paths - 1]; + } + + AIDynamicPath[*slot].pos[*cur_node] = *pos; + AIDynamicPath[*slot].roomnum[(*cur_node)++] = room; + + aip->path_end_node[aip->num_paths - 1] = *cur_node - 1; + + return true; +} + +bool AIGenerateAltBNodePath(object *obj, vector *start_pos, int *start_room, vector *end_pos, int *end_room, ai_path_info *aip, int *slot, int *cur_node, int handle) +{ + int x; + vector *pos; + bool f_path_exists = true; + + bn_list *bnlist = BNode_GetBNListPtr(*start_room); + int last_node = BNode_FindDirLocalVisibleBNode(*start_room, start_pos, &obj->orient.fvec, obj->size); + if(last_node == -1) + { + f_path_exists = false; + goto done; + } + ASSERT(last_node >= 0); + + for(x = 0; x < AIAltPathNumNodes - 1 && f_path_exists; x++) + { + int cur_room = BOA_INDEX(AIAltPath[x]); + int next_room = BOA_INDEX(AIAltPath[x + 1]); + + bnlist = BNode_GetBNListPtr(cur_room); + ASSERT(bnlist); + + if(next_room != cur_room && next_room != BOA_NO_PATH) + { + int portal = BOA_DetermineStartRoomPortal(cur_room, NULL, next_room, NULL); + if(portal == -1) + { + f_path_exists = false; + } + else + { + int bnode = -1; + + if(cur_room >= 0 && cur_room <= Highest_room_index) + { + bnode = Rooms[cur_room].portals[portal].bnode_index; + ASSERT(bnode >= 0 && bnode < BNode_GetBNListPtr(cur_room)->num_nodes); + } + else + { + int croom = BOA_connect[cur_room - Highest_room_index - 1][portal].roomnum; + int cportal = BOA_connect[cur_room - Highest_room_index - 1][portal].portal; + + int r = Rooms[croom].portals[cportal].croom; + int p = Rooms[croom].portals[cportal].cportal; + + ASSERT(Rooms[r].used && (Rooms[r].flags & RF_EXTERNAL)); + bnode = Rooms[r].portals[p].bnode_index; +// ASSERT(bnode >= 0 && bnode < BNode_GetBNListPtr(r)->num_nodes); + } + + // Add the last room... + bool f_ok = BNode_FindPath(cur_room, last_node, bnode, obj->size); + ASSERT(f_ok); + ASSERT(BNode_PathNumNodes); + + int i; + for(i = 0; i < BNode_PathNumNodes; i++) + { + pos = &bnlist->nodes[BNode_Path[i]].pos; + AIPathAddDPathNode(aip, slot, cur_node, pos, cur_room, handle); + } + + int portal = BOA_DetermineStartRoomPortal(next_room, NULL, cur_room, NULL); + if(next_room <= Highest_room_index) + { + bnode = Rooms[next_room].portals[portal].bnode_index; + ASSERT(bnode >= 0); + } + else + { + int croom = BOA_connect[next_room - Highest_room_index - 1][portal].roomnum; + int cportal = BOA_connect[next_room - Highest_room_index - 1][portal].portal; + + int r = Rooms[croom].portals[cportal].croom; + int p = Rooms[croom].portals[cportal].cportal; + + ASSERT(Rooms[r].used && (Rooms[r].flags & RF_EXTERNAL)); + bnode = Rooms[r].portals[p].bnode_index; +// ASSERT(bnode >= 0 && bnode < BNode_GetBNListPtr(r)->num_nodes); + } + + last_node = bnode; + ASSERT(last_node >= 0); + } + } + else + { + if(next_room == BOA_NO_PATH) + { + f_path_exists = false; + } + } + } + + if(f_path_exists) + { + int bnode; + + bn_list *bnlist = BNode_GetBNListPtr(*end_room); + bnode = BNode_FindClosestLocalVisibleBNode(*end_room, end_pos, obj->size); + if(bnode == -1) + { + f_path_exists = false; + goto done; + } + + // Add the last room... + bool f_ok = BNode_FindPath(*end_room, last_node, bnode, obj->size); + ASSERT(f_ok); + ASSERT(BNode_PathNumNodes); + int i; + for(i = 0; i < BNode_PathNumNodes; i++) + { + pos = &bnlist->nodes[BNode_Path[i]].pos; + AIPathAddDPathNode(aip, slot, cur_node, pos, *end_room, handle); + } + } + + done: + if(!f_path_exists) + AIPathFreePath(aip); + + return f_path_exists; +} + +void AIGenerateAltBOAPath(vector *start_pos, vector *end_pos, ai_path_info *aip, int *slot, int *cur_node, int handle) +{ + int x; + vector *pos; + + for(x = 0; x < AIAltPathNumNodes - 1; x++) + { + int cur_room = AIAltPath[x]; + int next_room = AIAltPath[x + 1]; + + int i; + + if(next_room >= 0 && next_room <= Highest_room_index) + { + if(cur_room > Highest_room_index) + { + i = BOA_DetermineStartRoomPortal(next_room, NULL, cur_room, NULL, false, false, NULL); + ASSERT(i != -1); + pos = &Rooms[next_room].portals[i].path_pnt; + } + else + { + for (i = 0; i < Rooms[next_room].num_portals; i++) + { + if(cur_room <= Highest_room_index && Rooms[next_room].portals[i].croom == cur_room) + { + pos = &Rooms[next_room].portals[i].path_pnt; + break; + } + } + } + + // Add the adjoining room's portal point + ASSERT(i < Rooms[next_room].num_portals); + AIPathAddDPathNode(aip, slot, cur_node, pos, next_room, handle); + + // Add its center point + pos = &Rooms[next_room].path_pnt; + AIPathAddDPathNode(aip, slot, cur_node, pos, next_room, handle); + } + else + { + if(BOA_INDEX(cur_room) <= Highest_room_index && cur_room != next_room) + { + int p_index = BOA_DetermineStartRoomPortal(cur_room, NULL, next_room, NULL); + + if(p_index >= 0) + { + pos = &Rooms[cur_room].portals[p_index].path_pnt; + AIPathAddDPathNode(aip, slot, cur_node, pos, cur_room, handle); + } + } + } + } +} + +// if(ROOMNUM_OUTSIDE(next_room) && ROOMNUM_OUTSIDE(*end_room) && (TERRAIN_REGION(next_room) == TERRAIN_REGION(*end_room) || +// TERRAIN_REGION(*end_room) == 0 || TERRAIN_REGION(next_room) == 0)) +// { +// next_room = *end_room; +// } + +bool AIGenerateBNodePath(object *obj, vector *start_pos, int *start_room, vector *end_pos, int *end_room, ai_path_info *aip, int *slot, int *cur_node, int handle) +{ + int next_room = BOA_INDEX(*start_room); + bool f_path_exists = true; + vector *pos; + + bn_list *bnlist;// = BNode_GetBNListPtr(*start_room); + int last_node = BNode_FindDirLocalVisibleBNode(*start_room, start_pos, &obj->orient.fvec, obj->size); + if(last_node == -1) + { + f_path_exists = false; + goto done; + } + + ASSERT(!(next_room >= 0 && next_room <= Highest_room_index && !Rooms[next_room].used)); + +// pos = &bnlist->nodes[last_node].pos; +// AIPathAddDPathNode(aip, slot, cur_node, pos, *start_room, handle); + + while((BOA_INDEX(next_room) != BOA_GetNextRoom(next_room, *end_room)) && (next_room != BOA_NO_PATH) && f_path_exists) + { + int cur_room; + + cur_room = next_room; + next_room = BOA_GetNextRoom(next_room, *end_room); + bnlist = BNode_GetBNListPtr(cur_room); + ASSERT(bnlist); + + if(next_room != cur_room && next_room != BOA_NO_PATH) + { + int portal = BOA_DetermineStartRoomPortal(cur_room, NULL, next_room, NULL); + if(portal == -1) + { + f_path_exists = false; + } + else + { + int bnode; + + if(cur_room <= Highest_room_index) + { + bnode = Rooms[cur_room].portals[portal].bnode_index; + ASSERT(bnode >= 0 && bnode < BNode_GetBNListPtr(cur_room)->num_nodes); + } + else + { + int croom = BOA_connect[cur_room - Highest_room_index - 1][portal].roomnum; + int cportal = BOA_connect[cur_room - Highest_room_index - 1][portal].portal; + + int r = Rooms[croom].portals[cportal].croom; + int p = Rooms[croom].portals[cportal].cportal; + + ASSERT(Rooms[r].used && (Rooms[r].flags & RF_EXTERNAL)); + bnode = Rooms[r].portals[p].bnode_index; +// ASSERT(bnode >= 0 && bnode < BNode_GetBNListPtr(r)->num_nodes); + } + + // Add the last room... + bool f_ok = BNode_FindPath(cur_room, last_node, bnode, obj->size); + ASSERT(f_ok); + ASSERT(BNode_PathNumNodes); + + int i; + for(i = 0; i < BNode_PathNumNodes; i++) + { + pos = &bnlist->nodes[BNode_Path[i]].pos; + AIPathAddDPathNode(aip, slot, cur_node, pos, cur_room, handle); + } + + int portal = BOA_DetermineStartRoomPortal(next_room, NULL, cur_room, NULL); + if(portal < 0) + { + f_path_exists = false; + } + else + { + if(next_room <= Highest_room_index) + { + last_node = Rooms[next_room].portals[portal].bnode_index; + ASSERT(last_node >= 0 && last_node < BNode_GetBNListPtr(next_room)->num_nodes); + } + else + { + int croom = BOA_connect[next_room - Highest_room_index - 1][portal].roomnum; + int cportal = BOA_connect[next_room - Highest_room_index - 1][portal].portal; + + int r = Rooms[croom].portals[cportal].croom; + int p = Rooms[croom].portals[cportal].cportal; + + ASSERT(Rooms[r].used && (Rooms[r].flags & RF_EXTERNAL)); + last_node = Rooms[r].portals[p].bnode_index; + // ASSERT(last_node >= 0 && last_node < BNode_GetBNListPtr(r)->num_nodes); + } + } + } + } + else + { + if(next_room == BOA_NO_PATH) + { + f_path_exists = false; + } + } + } + + if(f_path_exists) + { + int bnode; + + bn_list *bnlist = BNode_GetBNListPtr(*end_room); + bnode = BNode_FindClosestLocalVisibleBNode(*end_room, end_pos, obj->size); + if(bnode == -1) + { + f_path_exists = false; + goto done; + } + + // Add the last room... + bool f_ok = BNode_FindPath(*end_room, last_node, bnode, obj->size); + ASSERT(f_ok); + ASSERT(BNode_PathNumNodes); + int i; + for(i = 0; i < BNode_PathNumNodes; i++) + { + pos = &bnlist->nodes[BNode_Path[i]].pos; + AIPathAddDPathNode(aip, slot, cur_node, pos, *end_room, handle); + } + } + + done: + if(!f_path_exists) + AIPathFreePath(aip); + + return f_path_exists; +} + +bool AIGenerateBOAPath(vector *start_pos, int *start_room, vector *end_pos, int *end_room, ai_path_info *aip, int *slot, int *cur_node, int handle) +{ + int next_room = *start_room; + bool f_path_exists = true; + vector *pos; + + ASSERT(ROOMNUM_OUTSIDE(next_room) || (next_room >= 0 && next_room <= Highest_room_index && Rooms[next_room].used)); + + // Add in the center of the first room + if(!ROOMNUM_OUTSIDE(next_room)) + { + pos = &Rooms[*start_room].path_pnt; + AIPathAddDPathNode(aip, slot, cur_node, pos, *start_room, handle); + } + + if(ROOMNUM_OUTSIDE(next_room) && ROOMNUM_OUTSIDE(*end_room) && (TERRAIN_REGION(next_room) == TERRAIN_REGION(*end_room) || + TERRAIN_REGION(*end_room) == 0 || TERRAIN_REGION(next_room) == 0)) + { + next_room = *end_room; + } + + while((BOA_INDEX(next_room) != BOA_GetNextRoom(next_room, *end_room)) && (next_room != BOA_NO_PATH)) + { + int cur_room; + + cur_room = next_room; + next_room = BOA_GetNextRoom(next_room, *end_room); + + if(next_room != cur_room && next_room != BOA_NO_PATH && BOA_INDEX(next_room) <= Highest_room_index) + { + int i; + + for (i = 0; i < Rooms[next_room].num_portals; i++) + { + if(Rooms[next_room].portals[i].croom == cur_room) + { + pos = &Rooms[next_room].portals[i].path_pnt; + break; + } + + if(Rooms[Rooms[next_room].portals[i].croom].flags & RF_EXTERNAL) + { + int cell = GetTerrainCellFromPos(&Rooms[next_room].portals[i].path_pnt); + ASSERT(cell != -1); //DAJ -1FIX + int region = TERRAIN_REGION(cell); + + if(Highest_room_index + region + 1 == BOA_INDEX(cur_room)) + { + pos = &Rooms[next_room].portals[i].path_pnt; + break; + } + } + } + + // Add the adjoining room's portal point + ASSERT(i < Rooms[next_room].num_portals); + AIPathAddDPathNode(aip, slot, cur_node, pos, next_room, handle); + + // Add its center point + pos = &Rooms[next_room].path_pnt; + AIPathAddDPathNode(aip, slot, cur_node, pos, next_room, handle); + } + else + { + if(next_room == BOA_NO_PATH) + { + f_path_exists = false; + } + else if(BOA_INDEX(cur_room) <= Highest_room_index && cur_room != next_room) + { + int p_index = BOA_DetermineStartRoomPortal(cur_room, NULL, next_room, NULL); + + if(p_index >= 0) + { + pos = &Rooms[cur_room].portals[p_index].path_pnt; + AIPathAddDPathNode(aip, slot, cur_node, pos, cur_room, handle); + } + } + } + } + + return f_path_exists; +} + +bool AIPathAllocPath(object *obj, ai_frame *ai_info, void *goal_ptr, int *start_room, vector *start_pos, int *end_room, vector *end_pos, float rad, int flags, int handle, int ignore_obj) +{ + bool status; + bool f_path_exists = true; + + ai_path_info *aip = &ai_info->path; + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + AIPathFreePath(aip); + +// if(ai_info->awareness <= AWARE_NONE && !(ai_info->flags & AIF_PERSISTANT)) // chrishack -- scalar max_nodes in path by awareness +// return false; + + aip->goal_index = (goal *)goal_ptr - ai_info->goals; + aip->goal_uid = ((goal *)goal_ptr)->goal_uid; + + status = AIPathAddDPath(aip, handle); + + if(status) + { + int slot = aip->path_id[0]; + int cur_node = 0; + + // Account for closed doors + if(BOA_GetNextRoom(*start_room, *end_room) != BOA_NO_PATH) + { + bool f_make_alt = false; + bool f_bline_ok = false; + int last_room; + fvi_query fq; + fvi_info hit_info; + + fq.p0 = start_pos; + fq.startroom = *start_room; + fq.p1 = end_pos; + fq.rad = obj->size - .1f; + if(fq.rad <= 0.0f) + fq.rad = .1f; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_NO_RELINK | FQ_IGNORE_WEAPONS | FQ_IGNORE_POWERUPS; + + int fate = fvi_FindIntersection(&fq, &hit_info); + + if(fate == HIT_NONE || ((fate == HIT_OBJECT || fate == HIT_SPHERE_2_POLY_OBJECT) && hit_info.hit_object[0] == ignore_obj)) + { + f_make_alt = false; + f_bline_ok = true; + } + else if(BOA_Array[BOA_INDEX(*start_room)][BOA_INDEX(*end_room)] & BOAF_TOO_SMALL_FOR_ROBOT) + { + f_make_alt = true; + } + else if(BOA_HasPossibleBlockage(*start_room, *end_room)) + { + int next_room = BOA_INDEX(*start_room); + + ASSERT(next_room >= 0 && next_room <= Highest_room_index + 8); + + do + { + if(next_room != BOA_INDEX(*start_room) && BOA_LockedDoor(obj, next_room)) + { + f_make_alt = true; + break; + } + + last_room = next_room; + next_room = BOA_GetNextRoom(next_room, *end_room); + + if(BOA_INDEX(last_room) != BOA_INDEX(next_room) && next_room != BOA_NO_PATH) + { + if(BOA_DetermineStartRoomPortal(last_room, NULL, next_room, NULL) == -1) + { + f_make_alt = true; + break; + } + } + } + while(BOA_INDEX(next_room) != BOA_INDEX(*start_room) && (BOA_INDEX(next_room) != BOA_INDEX(*end_room)) && (next_room != BOA_NO_PATH)); + } + + error_make_alt: + + if(f_make_alt) + { + f_path_exists = AIFindAltPath(obj, *start_room, *end_room); + + if(f_path_exists) + { + bool f_bnode_ok = BNode_allocated && + BNode_verified && + !((ROOMNUM_OUTSIDE(*start_room) && TERRAIN_REGION(*start_room) == 0) || + (ROOMNUM_OUTSIDE(*end_room) && TERRAIN_REGION(*end_room) == 0)) && + !(ROOMNUM_OUTSIDE(*start_room) && ROOMNUM_OUTSIDE(*end_room) && + TERRAIN_REGION(*start_room) != TERRAIN_REGION(*end_room)); + + if(f_bnode_ok) + f_path_exists = AIGenerateAltBNodePath(obj, start_pos, start_room, end_pos, end_room, aip, &slot, &cur_node, handle); + else + AIGenerateAltBOAPath(start_pos, end_pos, aip, &slot, &cur_node, handle); + + if(!f_path_exists) + { + mprintf((0, "Warning Alt path from %d to %d failed.\n", *start_room, *end_room)); + } + } + } + else if(!f_bline_ok) + { + bool f_bnode_ok = BNode_allocated && + BNode_verified && + !((ROOMNUM_OUTSIDE(*start_room) && TERRAIN_REGION(*start_room) == 0) || + (ROOMNUM_OUTSIDE(*end_room) && TERRAIN_REGION(*end_room) == 0)) && + !(ROOMNUM_OUTSIDE(*start_room) && ROOMNUM_OUTSIDE(*end_room) && + TERRAIN_REGION(*start_room) != TERRAIN_REGION(*end_room)); + + if(f_bnode_ok) + f_path_exists = AIGenerateBNodePath(obj, start_pos, start_room, end_pos, end_room, aip, &slot, &cur_node, handle); + else + f_path_exists = AIGenerateBOAPath(start_pos, start_room, end_pos, end_room, aip, &slot, &cur_node, handle); + + if(!f_path_exists) + { + f_make_alt = true; + goto error_make_alt; + } + } + + if(f_path_exists) + { + // Add the end point + AIPathAddDPathNode(aip, &slot, &cur_node, end_pos, *end_room, handle); + + // Makes sure that the last dynamic path + AIDynamicPath[slot].num_nodes = cur_node; + } + } + else + { + f_path_exists = false; + //if(!(0x80000000 & *start_room) || !(0x80000000 & *end_room)) + //mprintf((0, "No path available from %d to %d\n", *start_room, *end_room)); + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + + AIPathFreePath(aip); + return false; + } + } + else + { + f_path_exists = false; + mprintf((0, "No dynamic paths left\n")); + } + + if(!f_path_exists) + { + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + + AIPathFreePath(aip); + return false; + } + + return true; +} + +bool AIPathSetAsStaticPath(object *obj, void *goal_ptr, int path_id, int start_node, int end_node, int cur_node) +{ + bool status; + ai_frame *ai_info = obj->ai_info; + ai_path_info *aip = &ai_info->path; + + #ifdef _DEBUG + if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) + { + mprintf((0, "AI Note: In free path\n")); + } + #endif + AIPathFreePath(aip); + + aip->goal_index = (goal *)goal_ptr - ai_info->goals; + aip->goal_uid = ((goal *)goal_ptr)->goal_uid; + + status = AIPathAddStaticPath(aip, path_id, start_node, end_node); + + if(status) + { + aip->cur_node = cur_node; + } + + return status; +} + + +#ifdef _DEBUG + +#include "hud.h" + +int AIPath_test_end_room = 0; +vector AIPath_test_end_pos = {0,0,0}; + +bool MakeTestPath(int *start_room, vector *pos) +{ + bool status; + ai_frame ai_info; + int count = 0; + + ai_info.awareness = AWARE_MOSTLY; + + AIPathInitPath(&ai_info.path); + + status = AIPathAllocPath(NULL, &ai_info, &ai_info.goals[0], start_room, pos, &AIPath_test_end_room, &AIPath_test_end_pos, 0.0f, 0, 0); + + while(status) + { + vector pos; + int room; + + status = AIPathGetCurrentNodePos(&ai_info.path, &pos, &room); + + if(status) + { + int id=FindObjectIDName ("Shield"); + ObjCreate(OBJ_POWERUP, id, room, &pos, &Identity_matrix); + count++; + } + + status = AIPathMoveToNextNode(&ai_info.path); + } + + AddHUDMessage("The test path has %d nodes.", count); + +// #ifdef _DEBUG +// if(AI_debug_robot_do && OBJNUM(obj) == AI_debug_robot_index) +// { +// mprintf((0, "AI Note: In free path\n")); +// } +// #endif + AIPathFreePath(&ai_info.path); + + return true; +} + +#endif + diff --git a/Descent3/aipath.h b/Descent3/aipath.h new file mode 100644 index 000000000..3ff01d91d --- /dev/null +++ b/Descent3/aipath.h @@ -0,0 +1,24 @@ +#ifndef AIPATH_H_ +#define AIPATH_H_ + +#include "object.h" +#include "pstypes.h" +#include "aistruct.h" + +// Exported functions +void AIPathMoveTurnTowardsNode(object *obj, vector *mdir, bool *f_moved); +//void AIPathMoveTowardsRoom(object *obj, int room_index); +bool AIPathSetAsStaticPath(object *obj, void *goal_ptr, int path_id, int start_node, int end_node, int cur_node); +bool AIPathAllocPath(object *obj, ai_frame *ai_info, void *goal_ptr, int *start_room, vector *start_pos, int *end_room, vector *end_pos, float rad, int flags, int handle, int ignore_obj = -1); +bool AIPathInitPath(ai_path_info *aip); +bool AIPathFreePath(ai_path_info *aip); +bool AIFindAltPath(object *obj, int i, int j, float *dist = NULL); + +#ifdef _DEBUG +bool MakeTestPath(int *start_room, vector *pos); + +extern int AIPath_test_end_room; +extern vector AIPath_test_end_pos; +#endif + +#endif \ No newline at end of file diff --git a/Descent3/aistruct.h b/Descent3/aistruct.h new file mode 100644 index 000000000..433bc4761 --- /dev/null +++ b/Descent3/aistruct.h @@ -0,0 +1,888 @@ +/* +* $Logfile: /DescentIII/main/aistruct.h $ +* $Revision: 112 $ +* $Date: 10/23/99 2:43a $ +* $Author: Chris $ +* +* This header is for AI structures +* +* $Log: /DescentIII/main/aistruct.h $ + * + * 112 10/23/99 2:43a Chris + * Added the PutObjectOnObject AI Goal + * + * 111 5/09/99 8:11p Chris + * Made the whole see/hear distintion work (mostly) + * + * 110 5/08/99 4:12p Chris + * Added AI hearing noises... version 1 + * + * 109 4/10/99 6:39p Matt + * Only save the designer-editable AI data in the Object_info array, + * instead of the whole ai_frame structure. This saves 3200 bytes per + * Object_info entry, which is about 2 MB overall. + * + * 108 4/09/99 10:33a Chris + * Improvements to Freud + * + * 107 4/08/99 3:35p Chris + * Improved the Fear factor in Freud + * + * 106 4/06/99 11:05p Chris + * Goal unique id's + * + * 105 4/02/99 10:18a Chris + * We can now mess with the Object_info anim stuff + * + * 104 3/30/99 4:32p Chris + * Moved subtype to the main goal sturct (from goal_info). Made move + * relative object vec finishable. (Like get behind player) + * + * 103 3/27/99 12:29p Chris + * Fixed problems with assigned lower priority goals that used paths (it + * would confuse the bot because the .path field was getting updated for + * the new goal even though it was lower priority) + * + * 102 3/22/99 10:58a Chris + * Awareness code was tweaked. Multisafe stuff added for objects. + * + * 101 3/17/99 5:23p Chris + * Fixed problems with low-priority paths + * + * 100 3/03/99 7:20p Jeff + * save/restore some ai path information + * + * 99 3/03/99 10:51a Chris + * Got rid of goal_ptr + * + * 98 2/17/99 1:21a Chris + * Updated the AI movement algorithm, fixed many bugs, added the + * last_dodge_dir vector so that robots can dodge the vauss and the like + * + * 97 2/15/99 9:03p Chris + * Added the base FOV off UVEC code and converted all the turrets + * + * 96 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 95 1/25/99 7:43a Chris + * Added the GUID (Goal Unique Id) and added the ability for weapon + * batteries to always fire exactly forward. + * + * 94 1/24/99 8:17p Chris + * Updated AI and OSIRIS. Externalized fireball constants for spew and + * sparks. Added CreateRandomSparks to OSIRIS, renamed a bunch of AI + * Notify names to use underscores. Fixed a memory access leak in the + * matcen effect code. + * + * 93 1/22/99 6:53p Chris + * Fixed LoadandBind Aux notify problems, fixed static path problems, + * fixed AIF_FORCE_AWARE Problems, improved path code + * + * 92 1/18/99 9:05a Chris + * Improved OSIRIS, AI, and ATTACH system, changed wiggle code, changed + * attach code for rad attaches, and added the AIG_ATTACH_OBJ goal + * + * 91 1/15/99 5:58p Chris + * + * 90 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 89 12/28/98 3:28p Chris + * Added aistruct_external.h + * + * 88 12/16/98 3:55p Chris + * Improved the sickles + * + * 87 12/14/98 12:03a Chris + * Turning and orientation are done in one (minus a few hacks) spot + * + * 86 12/13/98 9:18p Chris + * Improved influence values for in-code goals (10000 to 1.0). Added + * GF_ORIENT stuff. :) + * + * 85 12/11/98 1:57p Chris + * Improved wall walking code + * + * 84 12/03/98 5:45p Chris + * I just added full code support for OSIRIS/DLL controlled goals, + * enablers, and influence levels. :) + * + * 83 12/03/98 12:04p Chris + * Further de-hacked flocking and other major AI systems + * + * 82 12/02/98 5:51p Chris + * Added the start of scripted goals and scripted influence + * + * 81 12/01/98 6:04p Chris + * Made the goal flags look like a short (as the data element is a short) + * + * 80 12/01/98 4:31p Chris + * Checked in a massive amount of AI work. Improved flocking code. :) + * (Still hacked lightly). In addition, I am moving toward using the + * composite dir. :) + * + * 79 11/19/98 8:26p Chris + * Starting to add generic team avoidance code + * + * 78 11/18/98 3:59p Chris + * I added the avoid goal. I also improved the AIs for Evader1 and + * Evader2 by utilizing this goal when the target is well within the + * circle distance. + * + * 77 11/11/98 6:31p Chris + * AIF_DISABLE_FIRING and AIF_DISABLE_MELEE are now functional + * + * 76 11/06/98 11:39a Chris + * Robots with flamethrowers and Omega cannons work in single player + * + * 75 10/14/98 6:55p Chris + * Added turret change direction sounds and level goal ability to toggle + * auto level end + * + * 74 10/13/98 1:08p Chris + * Greatly improved the AI's use of paths. Improved visability checking + * algorithm. Probably needs a second pass for further cleanup. + * + * 73 10/09/98 7:47p Chris + * Added ObjSetDeadFlag + * + * 72 9/16/98 4:31p Chris + * Added target by distance + * + * 71 8/19/98 6:24p Chris + * Added more unused water support + * + * 70 8/17/98 4:56p Chris + * Added a new goal flag + * + * 69 8/15/98 6:11p Chris + * Added 13 new fields. AI fire spread works. + * + * 68 7/28/98 5:04p Chris + * Added some new multiplayer support (for dodging and targetting) + * + * 67 7/24/98 6:06p Chris + * Initial robot leading code -- needs multiple wb support + * + * 66 7/01/98 4:35p Chris + * More multiplayer sync issues + * + * 65 7/01/98 10:58a Chris + * Working on multiplayer AI stuff + * + * 64 6/30/98 6:36p Chris + * Added rev .1 of multiplayer animations - BTW It is totally not done. + * + * 63 6/26/98 2:52p Chris + * AI now reports when it updates its orientation + * + * 62 5/26/98 9:34a Chris + * Added XZ distances for circle dist. :) + * + * 61 5/18/98 8:06p Chris + * Made melee attacks closer to what I want in terms of functionality + * + * 60 5/18/98 12:40p Chris + * Added a new flag (F_BLINE_IF_SEE_GOAL) + * + * 59 5/17/98 9:07p Chris + * Fixed melee attacks. Thanks to Ala. :) + * + * 58 5/17/98 7:54p Chris + * Correctly integrated the goal system and circle distance stuff. Added + * support for "target goals". + * + * 57 5/15/98 2:58p Chris + * More on the big AI update + * + * 56 5/14/98 3:01p Chris + * More new AI code + * + * 55 5/14/98 12:21p Chris + * Updating AI system... Incomplete + * + * 54 5/12/98 3:51p Chris + * Oops + * + * 53 5/12/98 3:46p Chris + * Added some notify/goal system stuff and orientation to velocity + * + * 52 5/11/98 4:39p Chris + * Improved AI system (goals have notifies and are more flexable). + * + * 51 5/07/98 6:01p Chris + * More AI/OSIRIS stuff... + * + * 50 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 49 5/03/98 7:02p Chris + * Improved sound support for AI system + * + * 48 5/03/98 6:03p Chris + * Added sound support to the animation system + * + * 47 5/01/98 3:41p Chris + * + * 46 5/31/98 3:05p Chris + * Allowed death anims (fixed problem with ctype union of CT_DEBRIS and + * CT_AI) + * + * 45 4/30/98 11:31a Chris + * Massive upgrades to the AI system + * + * 44 4/22/98 9:43p Chris + * More AI improvements + * + * 43 4/21/98 11:25a Chris + * Improving GET_TO_OBJ goal + * + * 42 4/19/98 9:56p Chris + * AI system is integrated with OSIRIS. Path system is integrated with + * the AI system. Bugs will ensue. + * + * 41 4/16/98 2:56p Chris + * Updated buddy support and intergrated scripting and AI + * + * 40 4/13/98 7:38p Chris + * Added more support for a 'real' AI. + * + * 39 3/31/98 4:23p Chris + * Added a new AIpath system + * + * 38 3/25/98 11:02a Chris + * version 1.0 of the new AI ranged firing code. + * + * 37 3/23/98 10:03a Chris + * Added independant wb animations + * + * 36 3/17/98 12:14p Chris + * Fixed bug with bumping player causing awareness + * + * 35 3/17/98 11:27a Chris + * Added object bump notifies for AI + * + * 34 3/03/98 5:02p Chris + * Added a status register to the ai_frame. Also, I enabled a bunch of + * the fields from the AI Dialog. In addiition, I tweaked how melee + * attacks work and animate. + * + * 33 3/02/98 6:56p Chris + * Changed melee_dist to melee_damage + * + * 32 3/02/98 4:16p Chris + * Added 14 new fields to the AI Settings Dialog/page. + * + * 31 2/27/98 7:23p Chris + * Started to add new AI fields -- no way near complete + * + * 30 2/16/98 11:19p Chris + * Added support for melee robots + * + * 29 2/16/98 1:05p Chris + * Added some Flinch support + * + * 28 2/16/98 2:47a Chris + * Massive improvements to the animation system and the AI + * + * 27 2/11/98 7:00p Chris + * Removed priority (changed to influence) and added activation_level + * + * 26 2/10/98 4:45p Chris + * Incremental changes: Made a new node_orient heuristic. Removed some + * bad fields from the ai_info struct. + * + * 25 2/10/98 12:14a Chris + * Improving the awareness system. Re-enabled super dodging. Added the + * auto-fluctuate speed flag (allows some diversity). Enabled FOV + * information. + * + * 24 2/06/98 5:51p Chris + * Added the ability turn robots on and off + * + * 23 2/06/98 4:19p Chris + * Added a boat load of path stuff + * + * 22 2/06/98 2:15a Chris + * Activated the max_velocity, max_delta_velocity, and max_turn_rate + * fields for AI objects. + * + * 21 2/02/98 8:15p Chris + * Updated the AI system + * + * 20 1/30/98 2:19p Chris + * More improvements (target vis stuff and setting of the AIN_ALWAYS_ON is + * now done correctly) + * + * 19 1/29/98 3:29p Chris + * Major update to the AI system. + * + * 18 1/20/98 4:40p Chris + * Fixed some visability stuff. + * + * 17 1/19/98 10:04a Matt + * Added new object handle system + * + * 16 1/14/98 7:57p Chris + * Improving the awareness system + * + * 15 11/17/97 5:59p Chris + * Allowed some thief stuff + * + * 14 11/14/97 11:55p Chris + * Dodge goals are semi-functional + * + * 13 11/12/97 5:47p Chris + * Incremental improvements + * + * 12 10/28/97 12:25p Chris + * Added support for initial class, type, and movement types. Starting to + * flush out the AI structure + * + * 11 10/05/97 5:29a Chris + * Fixed problems at ubytes cause when setting then to -1 (made them + * chars) + * + * 10 9/18/97 1:26p Matt + * Moved next_movement from object struct to ai_frame + * + * 9 8/05/97 12:17p Chris + * + * 8 7/30/97 1:31p Chris + * Made helicopters slightly more interesting. + * + * 7 7/29/97 12:20p Chris + * Incremental improvements. Fixed a memory bug. + * + * 6 7/28/97 1:19p Chris + * Expanding the AI system + * + * 5 7/15/97 7:29p Chris + * Added a sound for helicopter blades. + * + * 4 7/15/97 5:35p Chris + * New AI code + * + * 3 7/15/97 4:59p Chris + * added support for AI and animations +* +* $NoKeywords: $ +*/ + +#ifndef AISTRUCT_H_ +#define AISTRUCT_H_ + +#include "pstypes.h" +#include "vecmat.h" +#include "aistruct_external.h" +#include "room.h" + +//------------------------------------------------- +// GLOBAL Info and Constants for AI +//------------------------------------------------- + +#define AI_INVALID_INDEX -1 + +// AI system state. + +#define AISTAT_ENABLED 1 +#define AISTAT_DISABLED 2 + +extern char AI_Status; + +// Last know player info -- might make it a [MAX_PLAYERS] + +extern vector ai_lkplayer_pos; +extern vector ai_lkplayer_velocity; +extern float ai_lkplayer_time; + +#define MAX_AI_INIT_CLASSES 3 +extern char *Ai_class_strings[MAX_AI_INIT_CLASSES]; + +#define MAX_AI_INIT_TYPES 9 +extern char *Ai_type_strings[MAX_AI_INIT_TYPES]; + +#define MAX_AI_INIT_MOVEMENT_TYPES 4 +extern char *Ai_movement_type_strings[MAX_AI_INIT_MOVEMENT_TYPES]; + +#define MAX_AI_INIT_MOVEMENT_SUBTYPES 7 +extern char *Ai_movement_subtype_flying_strings[MAX_AI_INIT_MOVEMENT_SUBTYPES]; +extern char *Ai_movement_subtype_walking_strings[MAX_AI_INIT_MOVEMENT_SUBTYPES]; + +extern float AI_last_time_room_noise_alert_time[MAX_ROOMS+8]; + +//------------------------------------------------- +// AI Class, Type, and Action indices +//------------------------------------------------- + +// AI Class -- make sure to update MAX_AI_CLASSES if you add a new class + +#define AIC_STATIC 0 +#define AIC_PURE_PATH 1 +#define AIC_AIS_FULL 2 +//#define AIC_DYNAMIC 3 +//#define AIC_AIS_MOVEMENT 4 +//#define AIC_AIS_FIRING 5 + +// AI Action -- What is it doing? + +#define AID_SLEEPING 0 +#define AID_ATTACKING_OBJ 1 +#define AID_WANDERING 2 +#define AID_NOT_THINKING 3 +#define AID_CHASE_OBJECT 4 +#define AID_RUN_FROM_OBJECT 5 +#define AID_HIDE 6 +#define AID_FOLLOW_PATH 7 +#define AID_OPEN_DOOR 8 + +//------------------------------------------------- +// Notification structures +//------------------------------------------------- + +typedef struct ain_see +{ + bool f_use_fov; + float max_dist; +} ain_see; + +typedef struct ain_hear +{ + bool f_directly_player; + float max_dist; + float hostile_level; // 0 - 1 + float curiosity_level; +} ain_hear; + +//------------------------------------------------- +// Path Structures +//------------------------------------------------- + +#define MAX_JOINED_PATHS 5 + +// Allow for linked paths + +#define AIP_STATIC 0 +#define AIP_DYNAMIC 1 +#define AIP_MOVE_LIST 2 + +typedef struct ai_path_info +{ + ushort cur_path; + ushort cur_node; + + ushort num_paths; + + int goal_uid; // which goal generated this path + int goal_index; + + // Used by all paths + ubyte path_id[MAX_JOINED_PATHS]; + ubyte path_type[MAX_JOINED_PATHS]; + + // Used for static game paths + ushort path_start_node[MAX_JOINED_PATHS]; + ushort path_end_node[MAX_JOINED_PATHS]; + ushort path_flags[MAX_JOINED_PATHS]; +} ai_path_info; + +// Used for predefined move lists (off of normal static paths) +typedef struct ai_move_path +{ + vector pos; + matrix orient; + + short path_id; +} ai_move_path; + +typedef struct path_information +{ + int path_id; + int start_node; + int next_node; + int end_node; +} path_information; + +// Goal Ender Structure + +typedef struct goal_enabler +{ + char enabler_type; + + union + { + float float_value; + float time; + char movement_type; + int flags; // Flags that enable/disable this goal + float dist; + int awareness; + }; + + float percent_enable; + float check_interval; + float last_check_time; + + char bool_next_enabler_op; + +} goal_enabler; + +//------------------------------------------------- +// Goal Structures +//------------------------------------------------- + +// MAX of 32 goal types unless the bitfield is made wider. + +// I wonder if goals can be classified. If so, we could reserve X goal for class a, and Y goal slots for class b +// plus it would make sure the our slots do not fill up in bad or degenerate ways. + +typedef struct gi_fire +{ + short cur_wb; // for ranged attack + ubyte cur_mask; // for ranged attack + ubyte melee_number; // this could be union'ed but it makes this struct word aligned +} gi_fire; + +typedef struct g_steer +{ + float min_dist; + float max_dist; + float max_strength; +} g_steer; + +typedef struct g_floats +{ + float fp1; + float fp2; + float fp3; +} g_floats; + +typedef struct +{ + int avoid_handle; + char min_rooms; + char max_rooms; + char flags; + char mine_index; // Used for if this object accidently goes on to the terrain +} g_wander_extra; + + +#define GAF_ALIGNED 0x01 +#define GAF_SPHERE 0x02 +#define GAF_TEMP_CLEAR_AUTOLEVEL 0x04 +#define GAF_TEMP_CLEAR_ROBOT_COLLISIONS 0x08 +#define GAF_TEMP_POINT_COLLIDE_WALLS 0x10 + +typedef struct +{ + float rad; + short flags; + char parent_ap; + char child_ap; +} g_attach; + +typedef struct +{ + short start_node; + short end_node; + short cur_node; +} g_static_path; + +typedef struct goal_info +{ + union + { + int handle; + int roomnum; + int f_actions; + int id; // Type of CUSTOM -- Id determines which one it was + // Also used as the Path ID for static path followers + }; + + union + { + float time; + vector vec; + vector pos; + g_floats fs; // goal floats or a vector + }; + + union + { + g_steer dist_info; + g_attach attach_info; + g_wander_extra wander_extra_info; + g_static_path static_path_info; + void *scripted_data_ptr; + }; + +} goal_info; + + +// Goal structure +typedef struct goal +{ + unsigned int type; + char subtype; + ubyte activation_level; + float creation_time; + + float min_influence; + union + { + float influence; + float max_influence; + }; + + float ramp_influence_dists[4]; // Sorted by distance + + goal_info g_info; + + char num_enablers; + goal_enabler enabler[MAX_ENABLERS_PER_GOAL]; + + float circle_distance; + int status_reg; + + float start_time; + float next_path_time; // used of goals with paths associated with them + + float dist_to_goal; + + vector vec_to_target; + float next_check_see_target_time; + vector last_see_target_pos; + float last_see_target_time; + float next_target_update_time; + + int flags; + int guid; // Designer assigned + + int goal_uid; // used by the AI system for paths + + vector set_fvec; + vector set_uvec; + + bool used; +} goal; + +#define OBJGOAL(x) (((goal *)x)->type&(AIG_GET_AWAY_FROM_OBJ|AIG_HIDE_FROM_OBJ|AIG_GET_TO_OBJ|AIG_ATTACH_TO_OBJ|AIG_FIRE_AT_OBJ|AIG_MELEE_TARGET|AIG_GUARD_OBJ|AIG_DODGE_OBJ|AIG_MOVE_AROUND_OBJ|AIG_MOVE_RELATIVE_OBJ_VEC|AIG_MOVE_RELATIVE_OBJ|AIG_GET_AROUND_OBJ|AIG_AVOID_OBJ|AIG_COHESION_OBJ|AIG_ALIGN_OBJ|AIG_PLACE_OBJ_ON_OBJ)) +#define COMPLETEATOBJ(x) (((goal *)x)->type&(AIG_GET_TO_OBJ)) +#define POSGOAL(x) (((goal *)x)->type&(AIG_WANDER_AROUND|AIG_GUARD_AREA|AIG_GET_TO_POS|AIG_GET_AROUND_POS)) +#define TARGETONLYGOAL(x) (((goal *)x)->type&(AIG_MELEE_TARGET)) +#define COMPLETEATPOS(x) (((goal *)x)->type&(AIG_WANDER_AROUND|AIG_GET_TO_POS)) + +typedef struct notify +{ + union + { + int obj_handle; + int goal_num; + }; + + union + { + vector pos; + int movement_type; + int anim_type; + int attack_num; + int enabler_num; + }; + + float time; +} notify; + +typedef struct ain_weapon_hit_info +{ + int parent_handle; + int weapon_handle; + int hit_face; + int hit_subobject; + float hit_damage; + vector hit_pnt; +} weapon_hit_info; + +//------------------------------------------------- +// AI awareness scale +//------------------------------------------------- + + +//------------------------------------------------- +// AI Sounds +//------------------------------------------------- + +#define MAX_AI_SOUNDS 5 + + +#define AI_MEM_DEPTH 5 + +typedef struct ai_mem +{ + // Computed at end of memory frame + float shields; + short num_enemies; + short num_friends; + + // Incremented during the memory frame + short num_times_hit; + short num_enemy_shots_fired; + short num_hit_enemy; + short num_enemy_shots_dodged; +} ai_mem; + +//------------------------------------------------- +// AI tracking information +//------------------------------------------------- + +//------------------------------------------------- +// AI framework per robot +//------------------------------------------------- + +typedef struct ai_frame +{ + char ai_class; // Static, DLL, Soar, Flock, and other will be here -- chrishack + char ai_type; // Used for some coded types + + ai_path_info path; + + float max_velocity; + float max_delta_velocity; + float max_turn_rate; + float max_delta_turn_rate; + + float attack_vel_percent; + float flee_vel_percent; + float dodge_vel_percent; + + float circle_distance; + float dodge_percent; + + float melee_damage[2]; + float melee_latency[2]; + + int sound[MAX_AI_SOUNDS]; // AI sounds, + float last_sound_time[MAX_AI_SOUNDS]; + short last_played_sound_index; + + char movement_type; + char movement_subtype; + + char animation_type; + char next_animation_type; + + char next_movement; // For queueing actions :) + char current_wb_firing; + char last_special_wb_firing; + + goal goals[MAX_GOALS]; + + //Standard memory + int target_handle; + float next_target_update_time; + + float dist_to_target_actual; + float dist_to_target_perceived; + vector vec_to_target_actual; + vector vec_to_target_perceived; + + float next_check_see_target_time; + vector last_see_target_pos; + float last_see_target_time; + float last_hear_target_time; + + //int rand_val; + //float next_rand_time; + + float weapon_speed; + + float next_melee_time; + float last_render_time; // Last time I was rendered -- BAD IN MULTIPLAYER -- chrisnote + float next_flinch_time; // Next valid time to flinch + + int status_reg; + + int flags; + int notify_flags; // Agent is only notified of some event types + + // Normalized movement and facing information + vector movement_dir; + vector rot_thrust_vector; + + float fov; + + int anim_sound_handle; // Goes with Animation sounds which can loop -- not for AI sounds + + float avoid_friends_distance; + + float frustration; + float curiousity; + float life_preservation; + float agression; + + // Current emotional levels + float cur_frustration; + float cur_curiousity; + float cur_life_preservation; + float cur_agression; + + // X Second memory + float mem_time_till_next_update; + ai_mem memory[AI_MEM_DEPTH]; + + float fire_spread; + float night_vision; + float fog_vision; + float lead_accuracy; + float lead_varience; + float fight_team; + float fight_same; + float hearing; + float roaming; + float leadership; + float coop_same; + float coop_team; + + float biased_flight_importance; + float biased_flight_min; + float biased_flight_max; + + vector last_dodge_dir; + float dodge_till_time; + + float awareness; + + matrix saved_orient; + +} ai_frame; + +//Etern'ed functions that depend of aistruct stuff + +#include "object_external.h" +#include "room.h" + + +#define MAX_DYNAMIC_PATHS 50 +#define MAX_NODES 50 + +class ai_dynamic_path +{ + public: + ai_dynamic_path(){num_nodes = 0; use_count = 0; owner_handle = OBJECT_HANDLE_NONE;}; + + vector pos[MAX_NODES]; + int roomnum[MAX_NODES]; + + short num_nodes; + short use_count; + + int owner_handle; +}; + +extern ai_dynamic_path AIDynamicPath[MAX_DYNAMIC_PATHS]; +extern int AIAltPath[MAX_ROOMS]; +extern int AIAltPathNumNodes; + + +#endif \ No newline at end of file diff --git a/Descent3/aistruct_external.h b/Descent3/aistruct_external.h new file mode 100644 index 000000000..75ace8410 --- /dev/null +++ b/Descent3/aistruct_external.h @@ -0,0 +1,423 @@ +/* +* $Logfile: /DescentIII/main/aistruct_external.h $ +* $Revision: 29 $ +* $Date: 10/23/99 2:43a $ +* $Author: Chris $ +* +* Description goes here +* +* $Log: /DescentIII/main/aistruct_external.h $ + * + * 29 10/23/99 2:43a Chris + * Added the PutObjectOnObject AI Goal + * + * 28 10/17/99 11:58p Chris + * Improved the hearing/seeing code, Added AIN_FIRED_WEAPON notify, and + * added a *hack* so that the final boss could fire his weapons during a + * cutscene... (usually this is a no-no). + * + * 27 5/05/99 4:56p Chris + * Added the RType powerup for the GB + * + * 26 5/01/99 2:21a Chris + * Use the GF_SPEED_XXX stuff + * + * 25 4/30/99 2:47p Chris + * Added the GF_SPEED stuff + * + * 24 4/28/99 5:33a Chris + * Further improved the BNode system. (It actually works inside and + * outside now) + * + * 23 4/24/99 2:20a Chris + * Added the Neutral_till_hit flag + * + * 22 4/18/99 7:27a Chris + * Added AIAF_IMMEDIATE to force non-tiling and instant anim loop changes + * + * 21 4/09/99 10:33a Chris + * Improvements to Freud + * + * 20 3/30/99 4:32p Chris + * Moved subtype to the main goal sturct (from goal_info). Made move + * relative object vec finishable. (Like get behind player) + * + * 19 3/27/99 3:25p Chris + * Added comment headers +* +* $NoKeywords: $ +*/ + +#ifndef _AISTRUCT_EXTERNAL_H_ +#define _AISTRUCT_EXTERNAL_H_ + +// AI Type -- make sure to update MAX_AI_TYPES if you add a new class + +#define AIT_FLYLANDER 0 +#define AIT_STALKER 1 +#define AIT_EVADER1 2 +#define AIT_EVADER2 3 +#define AIT_STATIONARY_TURRET 4 +#define AIT_AIS 5 +#define AIT_MELEE1 6 +#define AIT_BIRD_FLOCK1 7 +#define AIT_HERD1 8 + +// AI Movement flying types + +#define AIMF_NORMAL 0 +#define AIMF_PATH 1 +#define AIMF_HELICOPTER 2 +#define AIMF_HOVERCRAFT 3 +#define AIMF_JET 4 +#define AIMF_PLAYERLIKE 5 // For NPC ships +#define AIMF_BUDDYLIKE 6 // For theif/buddy bots + +//------------------------------------------------- +// AI Movement Information +//------------------------------------------------- + + +// AI Movement walking types + +#define AIMW_RESTRICTED_FLAT 0 // Specify a min/max angle of incline (so we can restrict movement -- i.e we can even do + // ceiling only robots) +#define AIMW_RESTRICTED_LOW_ANGLE 1 +#define AIMW_RESTRICTED_HIGH_ANGLE 2 +#define AIMW_NON_RESTRICTED 3 +#define AIMW_UNDERWATERONLY 4 // Stays in water +#define AIMW_WATERSURFACE 5 + +//------------------------------------------------- +// AI Animation Info +//------------------------------------------------- + +// animation flags + +#define AIAF_LOOPING 1 +#define AIAF_NOTIFY 2 +#define AIAF_UPDATE_WBS 4 +#define AIAF_USE_SPEED 8 +#define AIAF_IMMEDIATE 16 // Force to not tile and set immediately + +#define AIPM_ORIENT 0x00000018 // Masks for Orientation +#define AIPF_NEXT_NODE 0x00000000 // Orientate to velocity +#define AIPF_VELOCITY 0x00000008 // No automatic updating of orientation +#define AIPF_NODE_ORIENT 0x00000010 // Node orientate +#define AIPF_USER_DEFINED 0x00000018 // Always face the next node + +//------------------------------------------------- +// AI Goal Enders +//------------------------------------------------- + +#define MAX_ENABLERS_PER_GOAL 5 // We might malloc this depending of space and varitions between AIs + +// Ender types + +/* +#define AIE_NOT_USED 0 +#define AIE_PLAYER_DEAD 1 +#define AIE_PLAYER_AWAY 2 +#define AIE_SEE_OBJ 3 +#define AIE_TOUCH_OBJ 5 +#define AIE_HIDE_FROM_OBJ 6 +#define AIE_NOT_COLLIDE_VECTOR 7 // OBJ and sig. make sure the dead/new objects are not avoided. +#define AIE_TIME_TILL_GOAL_KILL 8 +#define AIE_MOVEMENT_TYPE 9 +#define AIE_AFTER_TASK 10 // Endable goals (like touch obj, fire on obj) +#define AIE_POSITION 11 +#define AIE_VELOCITY 12 +#define AIE_NOT_MOVEMENT_TYPE 13 +#define AIE_ADD_GOAL_TYPE 14 +#define AIE_FUZZY_TIME 15 +#define AIE_DIFF_ROOM 16 // On the terrain it means a distance of more than ??? +#define AIE_SAME_ROOM 18 +*/ + +#define AIE_NEAR 1 +#define AIE_FAR 2 +#define AIE_LTE_AWARENESS 3 +#define AIE_GT_AWARENESS 4 +#define AIE_LTE_LAST_SEE_TARGET_INTERVAL 5 +#define AIE_GT_LAST_SEE_TARGET_INTERVAL 6 +#define AIE_AI_STATUS_FLAG 7 +#define AIE_SCRIPTED 8 +#define AIE_FEAR 9 +#define AIE_ANGRY 10 +#define AIE_CURIOUS 11 +#define AIE_FRUSTRATED 12 +#define AIE_DELAY_TIME 13 +#define AIE_CLEAR_TIME 14 + +// How are the ending conditions related (BOOLEAN INFO) + +#define ENABLER_AND_NEXT 0 +#define ENABLER_OR_NEXT 1 +#define ENABLER_XOR_NEXT 3 +#define ENABLER_NEW_BOOL_NEXT 4 + +// Maximum goals a robot can have at any given time +#define MAX_GOALS 10 + +// AI Goal Types +#define AIG_GET_AWAY_FROM_OBJ 0x00000001 // Distance +#define AIG_HIDE_FROM_OBJ 0x00000002 // Line of sight +#define AIG_GET_TO_OBJ 0x00000004 // Butt up to an object +#define AIG_ATTACH_TO_OBJ 0x00000008 // Within a distance from obj +#define AIG_FIRE_AT_OBJ 0x00000010 +#define AIG_MELEE_TARGET 0x00000020 +#define AIG_WANDER_AROUND 0x00000040 +#define AIG_USE_MOVEMENT_TYPE 0x00000080 +#define AIG_PLACE_OBJ_ON_OBJ 0x00000100 +#define AIG_GUARD_OBJ 0x00000200 +#define AIG_GUARD_AREA 0x00000400 +#define AIG_BLANK3 0x00000800 +#define AIG_FACE_DIR 0x00001000 +#define AIG_DODGE_OBJ 0x00002000 +#define AIG_FOLLOW_PATH 0x00004000 +#define AIG_SET_ANIM 0x00008000 +#define AIG_SCRIPTED 0x00010000 +#define AIG_MOVE_AROUND_OBJ 0x00020000 +#define AIG_MOVE_RELATIVE_OBJ_VEC 0x00040000 +#define AIG_MOVE_RELATIVE_OBJ 0x00080000 +#define AIG_GET_TO_POS 0x00100000 +#define AIG_DO_MELEE_ANIM 0x00200000 +#define AIG_GET_AROUND_OBJ 0x00400000 +#define AIG_GET_AROUND_POS 0x00800000 +#define AIG_ALIGNMENT 0x01000000 +#define AIG_AVOID_OBJ 0x02000000 +#define AIG_COHESION_OBJ 0x04000000 +#define AIG_ALIGN_OBJ 0x08000000 + +// Goal end types (Not used yet) +#define AIGE_HANDLE_INVALID 0 +#define AIGE_SUCESSFUL 1 +#define AIGE_OVERLOAD 2 + +// Goal Flags +#define GF_NONFLUSHABLE 0x00000001 // A perminent goal +#define GF_HAS_PATH 0x00000002 // This goal has a path (not used yet) +#define GF_KEEP_AT_COMPLETION 0x00000004 // Keeps the goal as long at it is valid +#define GF_NOTIFIES 0x00000008 // By default, goals do not notify (unless an error occurs) +#define GF_OBJ_IS_TARGET 0x00000010 +#define GF_CIRCLE_OBJ 0x00000020 +#define GF_CIRCLE_POS 0x00000040 +#define GF_USE_BLINE_IF_SEES_GOAL 0x00000080 +#define GF_FORCE_AWARENESS 0x00000100 +#define GF_OBJS_ARE_FRIENDS 0x00000200 +#define GF_OBJS_ARE_SPECIES 0x00000400 +#define GF_OBJS_ARE_ENEMIES 0x00000800 +#define GF_ORIENT_VELOCITY 0x00001000 // Defaults to target if there is one (otherwise, to velocity) This flag forces velocity. +#define GF_RAMPED_INFLUENCE 0x00002000 // By default, distance based goals are (less dist = more influence) +#define GF_MIN_MAX_INFLUENCE 0x00004000 +#define GF_SCRIPTED_INFLUENCE 0x00008000 +#define GF_ORIENT_TARGET 0x00010000 +#define GF_ORIENT_SCRIPTED 0x00020000 +#define GF_ORIENT_GOAL_OBJ 0x00040000 +#define GF_ORIENT_FOR_ATTACH 0x00080000 +#define GF_ORIENT_PATH_NODE 0x00100000 +#define GF_PATH_FOLLOW_EXACTLY 0x00200000 +#define GF_PATH_REVERSE_AT_END 0x00400000 +#define GF_PATH_CIRCLE_AT_END 0x00800000 +#define GFM_END_OF_PATH (GF_PATH_REVERSE_AT_END|GF_PATH_CIRCLE_AT_END) +#define GF_PATH_MOVE_REVERSE_DIR 0x01000000 +#define GF_IN_CLEAR 0x02000000 // (Internal flag) Accounts for new goals overriding old goals within a GoalClearGoal +#define GF_IS_ATTACH_CHILD 0x04000000 // The moving object (for the attach goal) is actually the child +#define GF_CLEAR_IF_NOT_CURRENT_GOAL 0x08000000 // Removes the goal if it isn't the current one +#define GF_ORIENT_SET_FVEC 0x10000000 // Face the direction of the fvec +#define GF_ORIENT_SET_FVEC_UVEC 0x20000000 // Face the fvec and uvec +#define GF_SPEED_MASK (0x40000000 | 0x80000000) +#define GF_SPEED_NORMAL 0x00000000 +#define GF_SPEED_DODGE 0x40000000 +#define GF_SPEED_FLEE 0x80000000 +#define GF_SPEED_ATTACK (0x40000000 | 0x80000000) + +#define GWF_ONLY_IN_CUR_MINE 0x00 +#define GWF_ONLY_MINES 0x01 +#define GWF_ONLY_TERRAIN 0x02 + +#define ISORIENTGOAL(x) ((x)->flags&(GF_ORIENT_VELOCITY|GF_ORIENT_TARGET|GF_ORIENT_SCRIPTED|GF_ORIENT_GOAL_OBJ|GF_ORIENT_FOR_ATTACH|GF_ORIENT_PATH_NODE|GF_ORIENT_SET_FVEC|GF_ORIENT_SET_FVEC_UVEC)) +#define ISTRACKGOAL(x) ((x)->flags&(GF_OBJS_ARE_FRIENDS|GF_OBJS_ARE_SPECIES|GF_OBJS_ARE_ENEMIES)) + +// Goal Sub-Types -- game.consts +#define GST_FVEC 0 +#define GST_NEG_FVEC 1 +#define GST_RVEC 2 +#define GST_NEG_RVEC 3 +#define GST_UVEC 4 +#define GST_NEG_UVEC 5 + +// Influence constants +#define MAX_INFLUENCE 10000.0f +#define HIGH_INFLUENCE 10.0f +#define NORMAL_INFLUENCE 1.0f +#define LOW_INFLUENCE 0.5f + +#define MAX_INFLUENCE_DELTA_CONSIDER 100.0f + +#define NUM_ACTIVATION_LEVELS 4 +#define ACTIVATION_BLEND_LEVEL 100 + +//------------------------------------------------- +// AI notification of events +//------------------------------------------------- + +#define AIN_NEW_MOVEMENT 1 +#define AIN_OBJ_KILLED 2 +#define AIN_WHIT_BY_OBJ 3 +#define AIN_SEE_TARGET 4 +#define AIN_PLAYER_SEES_YOU 5 +#define AIN_WHIT_OBJECT 6 +#define AIN_TARGET_DIED 7 // In code, it only notifies if the target is gone +#define AIN_OBJ_FIRED 8 +#define AIN_GOAL_COMPLETE 9 +#define AIN_GOAL_FAIL 10 +#define AIN_GOAL_ERROR 11 +#define AIN_HEAR_NOISE 12 +#define AIN_NEAR_TARGET 13 +#define AIN_HIT_BY_WEAPON 14 +#define AIN_NEAR_WALL 15 +#define AIN_USER_DEFINED 16 // Processed in script +#define AIN_TARGET_INVALID 17 // Goal is killed +#define AIN_GOAL_INVALID 18 +#define AIN_SCRIPTED_GOAL 19 +#define AIN_SCRIPTED_ENABLER 20 +#define AIN_ANIM_COMPLETE 21 +#define AIN_BUMPED_OBJ 22 +#define AIN_MELEE_HIT 23 +#define AIN_MELEE_ATTACK_FRAME 24 +#define AIN_SCRIPTED_INFLUENCE 25 +#define AIN_SCRIPTED_ORIENT 26 +#define AIN_MOVIE_START 27 +#define AIN_MOVIE_END 28 +#define AIN_FIRED_WEAPON 29 + +#define AI_NOTIFIES_ALWAYS_ON ((0x00000001< + +#define AI_MAX_SEGS_CHECKED 200 + +int ai_num_segs_checked = 0; +ubyte ai_terrain_check_list[((TERRAIN_WIDTH*TERRAIN_DEPTH)>>3) + 1]; +int ai_segs_checked[AI_MAX_SEGS_CHECKED]; +#ifdef _DEBUG + int ai_num_checks_since_init = 0; +#endif + +float ai_rad; +ground_information *ai_ground_info_ptr; + +void ait_Init() +{ + ai_num_segs_checked = 0; + memset(ai_terrain_check_list, 0, ((TERRAIN_WIDTH*TERRAIN_DEPTH)>>3) + 1); + +#ifdef _DEBUG + ai_num_checks_since_init = 0; +#endif + +} + +void ait_terrain_clean() +{ + int i; + + assert(ai_num_segs_checked >= 0 && ai_num_segs_checked <= AI_MAX_SEGS_CHECKED); + + for(i = 0; i < ai_num_segs_checked; i++) + { + ASSERT((CELLNUM(ai_segs_checked[i]) >= 0) && (CELLNUM(ai_segs_checked[i]) < TERRAIN_WIDTH * TERRAIN_DEPTH)); + ai_terrain_check_list[CELLNUM(ai_segs_checked[i])>>3] = 0; + } + +//#ifdef _DEBUG +// for(i = 0; i < ((TERRAIN_WIDTH*TERRAIN_DEPTH)>>3)+1; i++) +// { +// ASSERT(ai_terrain_check_list[i] == 0); +// } +//#endif + + ai_num_segs_checked = 0; + +} + +void ai_check_terrain_node(int cur_node, int f_check_local_nodes) { + int check_x, check_y; + int new_node; + int xcounter, ycounter; + int xstart, xend; + int ystart, yend; + int f_ignore_terrain_in_this_node = 0; + + // Chrishack -- Note + + ASSERT(cur_node >= 0 && cur_node < TERRAIN_WIDTH * TERRAIN_DEPTH); + ASSERT((ai_terrain_check_list[cur_node >> 3] & (0x00000001 << (cur_node % 8))) == 0); + + // Mark the current node as visited + ai_terrain_check_list[cur_node >> 3] |= 0x00000001 << (cur_node % 8); + ai_segs_checked[ai_num_segs_checked] = MAKE_ROOMNUM(cur_node); + ai_num_segs_checked++; + ASSERT(ai_num_segs_checked < AI_MAX_SEGS_CHECKED); + + if(Terrain_seg[cur_node].y > ai_ground_info_ptr->highest_y) + ai_ground_info_ptr->highest_y = Terrain_seg[cur_node].y; + + if(Terrain_seg[cur_node].y < ai_ground_info_ptr->lowest_y) + ai_ground_info_ptr->lowest_y = Terrain_seg[cur_node].y; + + // check local nodes for any collision type, but no recursion for them :) + if(f_check_local_nodes) { + int next_y_delta; + // Check worst-case collisions. This includes all nodes within a radius edge of the current node + check_x = ai_rad/TERRAIN_SIZE + 1; + check_y = ai_rad/TERRAIN_SIZE + 1; + + xstart = cur_node%TERRAIN_WIDTH - check_x; + xend = cur_node%TERRAIN_WIDTH + check_x; + ystart = cur_node/TERRAIN_WIDTH - check_y; + yend = cur_node/TERRAIN_WIDTH + check_y; + + if(xstart < 0) xstart = 0; + if(xend >= TERRAIN_WIDTH) xend = TERRAIN_WIDTH - 1; + if(ystart < 0) ystart = 0; + if(yend >= TERRAIN_DEPTH) yend = TERRAIN_DEPTH - 1; + + // This should be a faster interative why to do a square with center at original position + new_node = TERRAIN_WIDTH * ystart + xstart; + next_y_delta = TERRAIN_WIDTH - (xend - xstart) - 1; + + for(ycounter = ystart; ycounter <= yend; ycounter++) { + for(xcounter = xstart; xcounter <= xend; xcounter++) { + if((ai_terrain_check_list[new_node >> 3] & (0x00000001 << (new_node % 8))) == 0) ai_check_terrain_node(new_node, 0); + new_node += 1; + } + + new_node += next_y_delta; + } + } + + return; +} + +// Returns true if the new point is on the terrain and false if the path results in leaving the terrain +bool ait_GetGroundInfo(ground_information *ground_info, vector *p0, vector *p1, float rad, angle fov) +{ + int start_node, end_node; + int x1, x2, y1, y2, x, y, delta_y, delta_x, change_x, change_y, length, cur_node, error_term, i; + + // Clean up the last call. This will make the info available till the next call. Maybe + // useful at a later date. Plus, we have multiple exits so, this nice spot so that + // we need only one call. :) + ait_terrain_clean(); + +#ifdef _DEBUG + ai_num_checks_since_init++; +#endif + + ai_ground_info_ptr = ground_info; + ai_rad = rad; + + start_node = GetTerrainCellFromPos(p0); + end_node = GetTerrainCellFromPos(p1); + + if (start_node == -1) return false; + + if(end_node == -1) + { + float delta = 1.0; + vector movement = *p1 - *p0; + + if(p1->x < (0.5f * TERRAIN_SIZE)) + { + delta = (p0->x - (0.5f * TERRAIN_SIZE))/(-movement.x); + } + else if(p1->x > (float)(TERRAIN_WIDTH * TERRAIN_SIZE) - (0.5f * TERRAIN_SIZE)) + { + delta = ((float)(TERRAIN_WIDTH * TERRAIN_SIZE) - (0.5f * TERRAIN_SIZE) - p0->x)/(movement.x); + } + + if(p1->z < (0.5f * TERRAIN_SIZE)) + { + if((p0->z - (0.5f * TERRAIN_SIZE))/(-movement.z) < delta) delta = (p0->z - (0.5f * TERRAIN_SIZE))/(-movement.z); + } + else if(p1->z > (float)(TERRAIN_DEPTH * TERRAIN_SIZE) - (0.5f * TERRAIN_SIZE)) + { + if(((float)(TERRAIN_WIDTH * TERRAIN_SIZE) - (0.5f * TERRAIN_SIZE) - p0->z)/(movement.z) < delta) + delta = ((float)(TERRAIN_WIDTH * TERRAIN_SIZE) - (0.5f * TERRAIN_SIZE) - p0->z)/(movement.z); + } + + *p1 = *p0 + delta * movement; + + end_node = GetTerrainCellFromPos(p1); + ASSERT(end_node != -1); + } + + ai_ground_info_ptr->highest_y = Terrain_seg[start_node].y; + ai_ground_info_ptr->lowest_y = Terrain_seg[start_node].y; + + // Determine the start end end nodes + x1 = start_node%TERRAIN_WIDTH; + y1 = start_node/TERRAIN_WIDTH; + + x2 = end_node%TERRAIN_WIDTH; + y2 = end_node/TERRAIN_WIDTH; + + x = x1; + y = y1; + + // How many nodes did I move? + delta_x = x2 - x1; + delta_y = y2 - y1; + + // check the current node for collsions (if we are done, return) + ASSERT((ai_terrain_check_list[start_node >> 3] & (0x00000001 << (start_node % 8))) == 0); + ai_check_terrain_node(start_node, 1); + if(delta_x == 0 && delta_y == 0) return true; + + // Do a Breshenham line algorithm + if(delta_x < 0) { + change_x = -1; + delta_x = -delta_x; + + } else { + change_x = 1; + } + + if(delta_y < 0) { + change_y = -1; + delta_y = -delta_y; + + } else { + change_y = 1; + } + + error_term = 0; + i = 1; + + if(delta_x < delta_y) { + length = delta_y + 1; + + while(i < length) { + y += change_y; + error_term += delta_x; + + + if(error_term >= delta_y) { + x += change_x; + error_term -= delta_y; + } + + if(y >= TERRAIN_DEPTH || y < 0 || x < 0 || x >= TERRAIN_WIDTH) { + return false; + } + + // Check the current node for collisions + cur_node = y*TERRAIN_WIDTH + x; + if((ai_terrain_check_list[cur_node >> 3] & (0x00000001 << (cur_node % 8))) == 0) ai_check_terrain_node(cur_node, 1); + + i++; + } + + } else { + length = delta_x + 1; + + while(i < length) { + x += change_x; + error_term += delta_y; + + if(error_term >= delta_x) { + y += change_y; + error_term -= delta_x; + } + + if(y >= TERRAIN_DEPTH || y < 0 || x < 0 || x >= TERRAIN_WIDTH) { + return false; + } + + // Check the current node for collisions + cur_node = y*TERRAIN_WIDTH + x; + if((ai_terrain_check_list[cur_node >> 3] & (0x00000001 << (cur_node % 8))) == 0) ai_check_terrain_node(cur_node, 1); + + i++; + } + } + + ASSERT(x == x2 && y == y2); + + return true; +} + diff --git a/Descent3/aiterrain.h b/Descent3/aiterrain.h new file mode 100644 index 000000000..160c2b1eb --- /dev/null +++ b/Descent3/aiterrain.h @@ -0,0 +1,37 @@ +/* +* $Logfile: /DescentIII/Main/aiterrain.h $ +* $Revision: 5 $ +* $Date: 8/05/97 12:17p $ +* $Author: Chris $ +* +* Terrain specific AI stuff +* +* $Log: /DescentIII/Main/aiterrain.h $ + * + * 5 8/05/97 12:17p Chris + * + * 4 7/30/97 1:31p Chris + * Made helicopters slightly more interesting. + * + * 3 7/29/97 12:20p Chris + * Incremental improvements. Fixed a memory bug. + * + * 2 7/28/97 1:19p Chris + * Expanding the AI system +* +* $NoKeywords: $ +*/ + +#ifndef AITERRAIN_H_ +#define AITERRAIN_H_ + +typedef struct ground_information +{ + float highest_y; + float lowest_y; +} ground_information; + +bool ait_GetGroundInfo(ground_information *ground_info, vector *p0, vector *p1, float rad = 0.0f, angle fov = 0); +void ait_Init(void); + +#endif \ No newline at end of file diff --git a/Descent3/ambient.cpp b/Descent3/ambient.cpp new file mode 100644 index 000000000..a771efb23 --- /dev/null +++ b/Descent3/ambient.cpp @@ -0,0 +1,334 @@ +/* + * $Logfile: /DescentIII/Main/ambient.cpp $ + * $Revision: 13 $ + * $Date: 8/10/99 2:10p $ + * $Author: Gwar $ + * + * Ambient sound system + * + * $Log: /DescentIII/Main/ambient.cpp $ + * + * 13 8/10/99 2:10p Gwar + * added sound support to neweditor + * + * 12 5/10/99 10:00p Ardussi + * changes to compile on Mac + * + * 11 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 10 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 9 3/24/99 7:49p Jason + * fixed multiplayer bugs + * + * 8 2/10/99 4:44p Jeff + * table file parser stuff + * + * 7 2/10/99 2:31p Matt + * Deleted some unused code and fixed a slightly stupid thing. + * + * 6 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 5 10/09/98 4:13p Matt + * Don't specify explicit path for ambient sound pattern file. + * + * 4 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 3 8/20/98 12:35p Matt + * Added nice editing for ambient sound patterns + * + * 2 8/17/98 8:32p Craig + * Fixed Matt's bug. + * + * 1 8/17/98 7:00p Matt + * Added ambient sound system + * + */ + +#include +#include + +#include "ambient.h" + +#include "hlsoundlib.h" + +#include "room.h" +#include "game.h" +#include "psrand.h" + +#define MAX_AMBIENT_SOUND_PATTERNS 100 +asp Ambient_sound_patterns[MAX_AMBIENT_SOUND_PATTERNS]; +int Num_ambient_sound_patterns=0; + +//Computes a floating-point pseudo-random number. +//Returns value in the range 0..1, with the precision 1/RAND_MAX +float randf() +{ + return ((float) ps_rand()) / ((float) RAND_MAX); +} + +//Process an Ambient Sound Pattern +void ProcessASP(asp *asp) +{ + //Check for empty ASP + if (! asp->num_sounds) + return; + + //Decrement delay + asp->delay -= Frametime; + + //Time to play? + if (asp->delay < 0.0) { + + //Figure out which sound to play + int roll = (ps_rand() * 100) / (RAND_MAX+1); //roll = 0..99 + int s; + + for (s=0;snum_sounds;s++) + if ((roll -= asp->sounds[s].probability) < 0) + break; + + ASSERT(s < asp->num_sounds); + + //Play sound i + ase *sound = &asp->sounds[s]; + float volume = sound->min_volume + (sound->max_volume - sound->min_volume) * randf(); + Sound_system.Play2dSound(sound->handle,volume); + + //Compute delay until next sound + asp->delay = asp->min_delay + (asp->max_delay - asp->min_delay) * randf(); + } +} + +//Does the ambient sound processing for one frame, maybe playing a sound or two +void DoAmbientSounds() +{ + if (OBJECT_OUTSIDE(Viewer_object)) + return; + + room *rp = &Rooms[Viewer_object->roomnum]; + + if (rp->ambient_sound != -1) + ProcessASP(&Ambient_sound_patterns[rp->ambient_sound]); +} + +//Initializes the ambient sounds for the current level +void InitAmbientSounds() +{ + //Loop through all ambient sound patterns + for (int p=0;pdelay = asp->min_delay + (asp->max_delay - asp->min_delay) * randf(); + + //Make sure the probabilities add up + int prob = 0; + for (int s=0;snum_sounds;s++) + prob += asp->sounds[s].probability; + if (asp->num_sounds && prob != 100) { + Int3(); + asp->sounds[0].probability += 100 - prob; //make it total 100 + } + } +} + +#include "ddio.h" +#include "CFILE.H" +#include "soundload.h" +#include "descent.h" +#include "mem.h" + +//Close down ambient sound system and free data +void FreeAmbientSoundData() +{ + for (int p=0;pname[0]) { + Ambient_sound_patterns[p] = Ambient_sound_patterns[Num_ambient_sound_patterns-1]; + Num_ambient_sound_patterns--; + } + } +} + +//Return the index of a named ambient sound pattern +//Returns number, or -1 if can't find +int FindAmbientSoundPattern(char *aspname) +{ + if (! aspname[0]) + return -1; + + for (int i=0;i AMBIENT_FILE_VERSION) { + Int3(); + cfclose(ifile); + return; + } + + //Get the number of patterns + Num_ambient_sound_patterns = cf_ReadInt(ifile); + + //Read the patterns + for (int p=0;pname,sizeof(asp->name),ifile); + + asp->min_delay = cf_ReadFloat(ifile); + asp->max_delay = cf_ReadFloat(ifile); + + asp->num_sounds = cf_ReadInt(ifile); + + if (asp->num_sounds>0) + asp->sounds = (ase *) mem_malloc(sizeof(*asp->sounds) * asp->num_sounds); + else + asp->sounds=NULL; + + int prob=0; + for (int s=0;snum_sounds;s++) { + char tbuf[PAGENAME_LEN]; + + cf_ReadString(tbuf,sizeof(tbuf),ifile); + asp->sounds[s].handle = FindSoundName(IGNORE_TABLE(tbuf)); + + asp->sounds[s].min_volume = cf_ReadFloat(ifile); + asp->sounds[s].max_volume = cf_ReadFloat(ifile); + asp->sounds[s].probability = cf_ReadInt(ifile); + + prob += asp->sounds[s].probability; + } + + if (asp->num_sounds && (prob != 100)) { + Int3(); + asp->sounds[0].probability += 100 - prob; //make it total 100 + } + } + + cfclose(ifile); +} + +#ifdef NEWEDITOR +extern char D3HogDir[_MAX_PATH*2]; +#endif + +//Writes data from the ambient sound data file +void WriteAmbientData() +{ + char filename[_MAX_PATH]; + CFILE *ofile; + +#ifndef NEWEDITOR + ddio_MakePath(filename,Base_directory,"data","misc",AMBIENT_FILE_NAME,NULL); +#else + ddio_MakePath(filename,D3HogDir,"data","misc",AMBIENT_FILE_NAME,NULL); +#endif + ofile = cfopen(filename,"wb"); + + if (!ofile) { + Int3(); + return; + } + + //Write file ID & version + cf_WriteBytes((ubyte *) AMBIENT_FILE_ID,strlen(AMBIENT_FILE_ID),ofile); + cf_WriteInt(ofile,AMBIENT_FILE_VERSION); + + //Write the number of patterns + cf_WriteInt(ofile,Num_ambient_sound_patterns); + + //Read the patterns + for (int p=0;pname); + + cf_WriteFloat(ofile,asp->min_delay); + cf_WriteFloat(ofile,asp->max_delay); + + cf_WriteInt(ofile,asp->num_sounds); + + for (int s=0;snum_sounds;s++) { + cf_WriteString(ofile,Sounds[asp->sounds[s].handle].name); + + cf_WriteFloat(ofile,asp->sounds[s].min_volume); + cf_WriteFloat(ofile,asp->sounds[s].max_volume); + cf_WriteInt(ofile,asp->sounds[s].probability); + } + } + + cfclose(ofile); +} + diff --git a/Descent3/ambient.h b/Descent3/ambient.h new file mode 100644 index 000000000..8532ed2e4 --- /dev/null +++ b/Descent3/ambient.h @@ -0,0 +1,76 @@ +/* + * $Logfile: /DescentIII/main/ambient.h $ + * $Revision: 4 $ + * $Date: 2/12/99 5:33p $ + * $Author: Kevin $ + * + * Header for ambient sound system + * + * $Log: /DescentIII/main/ambient.h $ + * + * 4 2/12/99 5:33p Kevin + * Added table file flag thingy + * + * 3 2/10/99 2:31p Matt + * Deleted some unused code and fixed a slightly stupid thing. + * + * 2 8/20/98 12:35p Matt + * Added nice editing for ambient sound patterns + * + * 1 8/17/98 7:00p Matt + * Added ambient sound system + * + */ + +#ifndef _AMBIENT_H +#define _AMBIENT_H + +#include "manage.h" + +//An Ambient Sound Element, one part of an Ambient Sound Pattern (ASP) +typedef struct { + int handle; //the sample's handle + float min_volume,max_volume; //minimum and maximum volume + int probability; //between 0 and 100 +} ase; + +//An Ambient Sound Element, one part of an Ambient Sound Pattern (ASP) +typedef struct { + char name[PAGENAME_LEN]; //the name of this pattern + float min_delay,max_delay; //delay time between sounds + int num_sounds; //how many sounds in this pattern + ase *sounds; //array of sounds + float delay; //how long until the next sound +} asp; + +extern int Num_ambient_sound_patterns; +extern asp Ambient_sound_patterns[]; + +#define AMBIENT_FILE_NAME "ambient.dat" +/* +$$TABLE_GENERIC "ambient.dat" +*/ + +//Initialize the ambient sound system +void InitAmbientSoundSystem(); + +//Does the ambient sound processing for one frame, maybe playing a sound or two +void DoAmbientSounds(); + +//Initializes the ambient sounds for the current level +void InitAmbientSounds(); + +//Return the index of a named ambient sound pattern +//Returns number, or -1 if can't find +int FindAmbientSoundPattern(char *aspname); + +//Returns a pointer to the name of the specified ambient sound pattern +char *AmbientSoundPatternName(int n); + +//Reads data from the ambient sound data file +void ReadAmbientData(); + +//Writes data from the ambient sound data file +void WriteAmbientData(); + +#endif \ No newline at end of file diff --git a/Descent3/args.cpp b/Descent3/args.cpp new file mode 100644 index 000000000..61e19ffd9 --- /dev/null +++ b/Descent3/args.cpp @@ -0,0 +1,141 @@ +#include "args.h" +#include "mono.h" +#include +#ifdef __LINUX__ +#include "lnxfix.h" +#else +#define strcasecmp stricmp +#endif + +//rcg07062000 used to be static. +int TotalArgs=0; +char GameArgs[MAX_ARGS][MAX_CHARS_PER_ARG]; + +const char* GetArg( int index ) +{ + if( index >= TotalArgs || index == 0 ) + return NULL; + return GameArgs[ index ]; +} + +// Gathers all arguments +void GatherArgs (char *str) +{ + int i,t; + static int curarg=1; //DAJ made static + int len=strlen(str); + bool gotquote = false; + for (t=0,i=0;i= -1.1f && dot <= 1.1f); + + // Bounds checking to save us from floating point round-off + if(dot > 1.0f) + dot = 1.0f; + if(dot < -1.0f) + dot = -1.0f; + + // If the dot is 1.0 we are are already perfectly aligned! + if(dot != 1.0f) + { + // if it is -1.0, then we are perfectly unaligned + if(dot != -1.0f) + { + vm_CrossProduct(&rot_vec, &start_fvec, &goal_fvec); + vm_NormalizeVector(&rot_vec); + + // returns the angle between two normalized vectors and a forward vector +// rot_angle = (vm_DeltaAngVecNorm(&start_fvec, &goal_fvec, &rot_vec)/65535.0f) * (2.0f * PI); + rot_angle = acos(dot); + } + else + { + vector random_vector; + float dot; + + do + { + vm_MakeRandomVector(&random_vector); + vm_NormalizeVector(&random_vector); + + dot = random_vector * goal_fvec; + } + while(dot >= 1.0f || dot <= -1.0f); + + vm_CrossProduct(&rot_vec, &random_vector, &goal_fvec); + vm_NormalizeVector(&rot_vec); + + rot_angle = PI; + } + + ConvertAxisAmountMatrix(&rot_vec, rot_angle, &rot_mat); + vm_TransposeMatrix(&rot_mat); + start_obj->orient *= rot_mat; + } + + // Determines the offset of the start object's ap position (now that it is correctly aligned + // with the fvec) + f_use_uvec = true; + vector temp; + AttachPointPos(start_obj, start_ap, true, &start_pos, true, &temp, &f_use_uvec, &start_uvec); + + start_obj->pos += (goal_pos - start_pos); + + // If we have UVEC's then align them! + if(f_use_uvec) + { + float dot = start_uvec * goal_uvec; + ASSERT(dot >= -1.1f && dot <= 1.1f); + + // Bounds checking to save us from floating point round-off + if(dot > 1.0f) + dot = 1.0f; + if(dot < -1.0f) + dot = -1.0f; + + // If the dot is 1.0 we are are already perfectly aligned! + if(dot != 1.0f) + { + // if it is -1.0, then we are perfectly unaligned + if(dot != -1.0f) + { + vm_CrossProduct(&rot_vec, &start_uvec, &goal_uvec); + vm_NormalizeVector(&rot_vec); + +// rot_angle = (vm_DeltaAngVecNorm(&start_uvec, &goal_uvec, &rot_vec)/65535.0f) * (2.0f * PI); + rot_angle = acos(dot); + } + else + { + vector random_vector; + float dot; + + do + { + vm_MakeRandomVector(&random_vector); + vm_NormalizeVector(&random_vector); + + dot = random_vector * goal_uvec; + } + while(dot >= 1.0f || dot <= -1.0f); + + vm_CrossProduct(&rot_vec, &random_vector, &goal_uvec); + vm_NormalizeVector(&rot_vec); + + rot_angle = PI; + } + + ConvertAxisAmountMatrix(&rot_vec, rot_angle, &rot_mat); + vm_TransposeMatrix(&rot_mat); + start_obj->orient *= rot_mat; + } + } +*/ + +// Finds the position of a attach point on an object +// The uvec is optional as most attaching objects don't need at complete orientation set (only an fvec) +bool AttachPointPos(object *obj, char ap, bool f_compute_pos, vector *attach_pos, bool f_compute_fvec, vector *attach_fvec, bool *f_computed_uvec = NULL, vector *attach_uvec = NULL) +{ + poly_model *pm; + vector pnt; + vector normal; + vector uvec; + matrix m; + int mn; //submodel number + float normalized_time[MAX_SUBOBJECTS]; + + pm = &Poly_models[obj->rtype.pobj_info.model_num]; + + if(ap < 0 || ap >= pm->n_attach) + { + mprintf((0, "WARNING: No ap %d on object %d.\n", ap, OBJNUM(obj))); + return false; + } + + SetNormalizedTimeObj(obj, normalized_time); + SetModelAnglesAndPos (pm,normalized_time); + + if(f_compute_pos) + pnt = pm->attach_slots[ap].pnt; + + if(f_compute_fvec) + { + normal = pm->attach_slots[ap].norm; + // mprintf((0, "Original fvec is %f long.\n", vm_GetMagnitude(&normal))); + } + + if(pm->attach_slots[ap].f_uvec && attach_uvec && f_computed_uvec) + { + *f_computed_uvec = true; + uvec = pm->attach_slots[ap].uvec; + + // mprintf((0, "Original uvec is %f long.\n", vm_GetMagnitude(&uvec))); + } + else if(f_computed_uvec) + { + *f_computed_uvec = false; + } + + mn = pm->attach_slots[ap].parent; + + // Instance up the tree for this gun + while (mn != -1) + { + vector tpnt; + + vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b); + vm_TransposeMatrix(&m); + + if(f_compute_pos) + tpnt = pnt * m; + + if(f_compute_fvec) + normal = normal * m; + + if(f_computed_uvec && *f_computed_uvec) + { + uvec = uvec * m; + } + + if(f_compute_pos) + pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos; + + mn = pm->submodel[mn].parent; + } + + //now instance for the entire object + m = obj->orient; + vm_TransposeMatrix(&m); + + if(f_compute_pos) + *attach_pos = pnt * m; + + if(f_compute_fvec) + { + *attach_fvec = normal * m; + // mprintf((0, "Rotated fvec is %f long.\n", vm_GetMagnitude(attach_fvec))); + } + + if(f_computed_uvec && *f_computed_uvec) + { + *attach_uvec = uvec * m; + // mprintf((0, "Rotated uvec is %f long.\n", vm_GetMagnitude(attach_uvec))); + } + + if(f_compute_pos) + *attach_pos += obj->pos; + + return true; +} + +void ConvertAxisAmountMatrix(vector *n, float w, matrix *rotmat) +{ + float s; + float c; + float t; + +// float scale = *w/.0001f; + //float w_n = .0001f; + //vector s_result; + + if(w == 0.0f) + { + matrix temp = IDENTITY_MATRIX; + + *rotmat = temp; + return; + } + + s = sin(w); + c = cos(w); + t = 1.0f - c; + + const float sx = s * n->x; + const float sy = s * n->y; + const float sz = s * n->z; + const float txy = t * n->x * n->y; + const float txz = t * n->x * n->z; + const float tyz = t * n->y * n->z; + const float txx = t * n->x * n->x; + const float tyy = t * n->y * n->y; + const float tzz = t * n->z * n->z; + + rotmat->rvec.x = txx + c; + rotmat->rvec.y = txy - sz; + rotmat->rvec.z = txz + sy; + rotmat->uvec.x = txy + sz; + rotmat->uvec.y = tyy + c; + rotmat->uvec.z = tyz - sx; + rotmat->fvec.x = txz - sy; + rotmat->fvec.y = tyz + sx; + rotmat->fvec.z = tzz + c; +} + +bool AttachDoPosOrientRad(object *parent, char p_ap, object *child, float rad_percent, vector *pos) +{ + vector attach_pos; + vector attach_fvec; + + if(AttachPointPos(parent, p_ap, true, &attach_pos, true, &attach_fvec)) + { + vector child_ap_pos = child->pos - attach_fvec * (rad_percent * child->size); + + *pos = (child_ap_pos - attach_pos) + parent->pos; + return true; + } + else + { + return false; + } +} + +bool AttachDoPosOrient(object *parent, char parent_ap, object *child, char child_ap, bool f_parent, bool f_move_obj, vector *pos, matrix *orient, bool f_dropping_off) +{ + vector goal_fvec; + vector goal_uvec; + vector goal_pos; + + bool f_use_uvec = true; + + vector start_fvec; + vector start_uvec; + vector start_pos; + + object *goal_obj; + object *start_obj; + + matrix rot_mat; + + char goal_ap; + char start_ap; + + if(f_parent) + { + goal_obj = child; + goal_ap = child_ap; + + start_obj = parent; + start_ap = parent_ap; + } + else + { + goal_obj = parent; + goal_ap = parent_ap; + + start_obj = child; + start_ap = child_ap; + } + + vector saved_pos = start_obj->pos; + matrix saved_orient = start_obj->orient; + + f_use_uvec = true; + AttachPointPos(goal_obj, goal_ap, true, &goal_pos, true, &goal_fvec, &f_use_uvec, &goal_uvec); + goal_fvec *= -1.0f; + + f_use_uvec = true; + if(f_dropping_off) + { + object *attached_obj = ObjGet(start_obj->attach_children[0]); + ASSERT(attached_obj); + AttachPointPos(attached_obj, start_ap, true, &start_pos, true, &start_fvec, &f_use_uvec, &start_uvec); + } + else + { + AttachPointPos(start_obj, start_ap, true, &start_pos, true, &start_fvec, &f_use_uvec, &start_uvec); + } + + vm_NormalizeVector(&start_fvec); + vm_NormalizeVector(&start_uvec); + vm_NormalizeVector(&goal_fvec); + vm_NormalizeVector(&goal_uvec); + + matrix start_m; + matrix goal_m; + + vm_VectorToMatrix(&start_m, &start_fvec, &start_uvec, NULL); + vm_VectorToMatrix(&goal_m, &goal_fvec, &goal_uvec, NULL); + + matrix sot; + matrix srt; + + sot = start_obj->orient; + vm_TransposeMatrix(&sot); + + srt = sot * start_m; + vm_TransposeMatrix(&srt); + + rot_mat = sot * goal_m * srt; + + start_obj->orient *= rot_mat; + vm_Orthogonalize(&start_obj->orient); + + if(f_dropping_off) + { + object *attached_obj = ObjGet(start_obj->attach_children[0]); + ASSERT(attached_obj); + AttachPointPos(attached_obj, start_ap, true, &start_pos, false, NULL, NULL, NULL); + } + else + { + AttachPointPos(start_obj, start_ap, true, &start_pos, false, NULL, NULL, NULL); + } + start_obj->pos += (goal_pos - start_pos); + + if(pos) + *pos = start_obj->pos; + + if(orient) + *orient = start_obj->orient; + + if(f_move_obj) + { + int room = goal_obj->roomnum; + if(room != -1) + { + if(ROOMNUM_OUTSIDE(room)) + { + room = GetTerrainRoomFromPos(&start_obj->pos); + if(room == -1) + { + SetObjectDeadFlag(start_obj); + return false; + } + } + + ObjSetPos(start_obj, &start_obj->pos, room, &start_obj->orient, false); + + return true; + } + else + { + SetObjectDeadFlag(start_obj); + return false; + } + } + else + { + start_obj->pos = saved_pos; + start_obj->orient = saved_orient; + } + + return true; +} + +// Adds and subtracts from mass from connections and disconnections +void AttachPropagateMass(object *child, bool f_attach = true) +{ + object *new_parent; + + if(!child) + return; + + do + { + new_parent = ObjGet(child->attach_parent_handle); + ASSERT(new_parent); + if(!new_parent) + { + return; + } + + float mass; + + mass = Object_info[child->id].phys_info.mass; + + if(mass < 0.0f) + mass = 0.0f; + + ASSERT(new_parent->mtype.phys_info.mass >= 0.0f && mass >= 0.0f); + + if(f_attach) + new_parent->mtype.phys_info.mass += mass; + else + new_parent->mtype.phys_info.mass -= mass; + + if(new_parent->mtype.phys_info.mass < 0.0f) + { + new_parent->mtype.phys_info.mass = 0.0f; + } + + child = new_parent; + + ASSERT(new_parent->mtype.phys_info.mass >= 0.0f); + } + while(child->flags & OF_ATTACHED); +} + +inline void ComputeUltimateAttachParent(object *child) +{ + object *cur_obj = child; + + do + { + child->attach_ultimate_handle = cur_obj->attach_parent_handle; + cur_obj = ObjGet(child->attach_ultimate_handle); + ASSERT(cur_obj); + + } while(cur_obj->flags & OF_ATTACHED); +} + +void ProprogateUltimateAttachParent(object *parent, int ultimate_handle) +{ + int i; + + ASSERT(parent); + ASSERT(ultimate_handle != OBJECT_HANDLE_NONE); + + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + + if(!parent->attach_children) + return; + + for(i = 0; i < parent_pm->n_attach; i++) + { + object *child; + + if((child = ObjGet(parent->attach_children[i])) != NULL) + { + child->attach_ultimate_handle = ultimate_handle; + ProprogateUltimateAttachParent(child, ultimate_handle); + } + } +} + +// Attaches 2 objects via attach points on each. The f_used_aligned allows for an aligned connection. +// NOTE: The child always moves to the parent +bool AttachObject(object *parent, char parent_ap, object *child, char child_ap, bool f_use_aligned) +{ + ASSERT(parent); + ASSERT(child); + + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + poly_model *child_pm = &Poly_models[child->rtype.pobj_info.model_num]; + + if(parent_ap < 0 || parent_ap >= parent_pm->n_attach) + { + mprintf((0, "ATTACH: Parent AP invalid\n")); + return false; + } + + if(child->flags & OF_ATTACHED) + { + mprintf((0, "ATTACH: Child already attached to someone\n")); + return false; + } + + if(child_ap >= 0 && child_ap < child_pm->n_attach) + { + if(AttachDoPosOrient(parent, parent_ap, child, child_ap, false)) + { + child->flags |= OF_ATTACHED; + child->attach_type = AT_ALIGNED; + child->attach_index = child_ap; + child->attach_parent_handle = parent->handle; + + parent->attach_children[parent_ap] = child->handle; + ComputeUltimateAttachParent(child); + ProprogateUltimateAttachParent(child, child->attach_ultimate_handle); + + AttachPropagateMass(child); + + AttachUpdateSubObjects(child); + if((Game_mode & GM_MULTI) && (Netgame.local_role == LR_SERVER)) + { + MultiSendAttach(parent, parent_ap, child, child_ap, f_use_aligned); + } + if(Demo_flags == DF_RECORDING) + { + DemoWriteAttachObj(parent, parent_ap, child, child_ap, f_use_aligned); + } + + return true; + } + + mprintf((0, "ATTACH: AP attach failed\n")); + return false; + } + + mprintf((0, "ATTACH: Child AP (%d) - child only has %d attach points\n", child_ap, child_pm->n_attach)); + return false; +} + + +// Attaches a child object to a parent object by a percent of the radius of the child. +// NOTE: The child always moves to the parent and not the reverse +bool AttachObject(object *parent, char parent_ap, object *child, float percent_rad) +{ + ASSERT(parent); + ASSERT(child); + + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + poly_model *child_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + + if(parent_ap < 0 || parent_ap >= parent_pm->n_attach) + { + mprintf((0, "ATTACH: Parent AP invalid\n")); + return false; + } + + if(child->flags & OF_ATTACHED) + { + mprintf((0, "ATTACH: Child already attached to someone\n")); + return false; + } + + if(percent_rad >= 0.0f) + { + vector attach_pos; + vector attach_fvec; + + if(AttachPointPos(parent, parent_ap, true, &attach_pos, true, &attach_fvec)) + { + child->flags |= OF_ATTACHED; + child->attach_type = AT_RAD; + child->attach_dist = percent_rad * child->size; + child->attach_parent_handle = parent->handle; + + parent->attach_children[parent_ap] = child->handle; + ComputeUltimateAttachParent(child); + ProprogateUltimateAttachParent(child, child->attach_ultimate_handle); + + AttachPropagateMass(child); + + vector new_child_pos = attach_pos + attach_fvec * child->attach_dist; + + int room = parent->roomnum; + if(ROOMNUM_OUTSIDE(room)) + room = GetTerrainRoomFromPos(&new_child_pos); + + ObjSetPos(child, &new_child_pos, room, NULL,false); + + AttachUpdateSubObjects(child); + if((Game_mode & GM_MULTI) && (Netgame.local_role == LR_SERVER)) + { + MultiSendAttachRad(parent, parent_ap, child, percent_rad); + } + if(Demo_flags == DF_RECORDING) + { + DemoWriteAttachObjRad(parent, parent_ap, child, percent_rad); + } + + return true; + } + + mprintf((0, "ATTACH: RAD attach failed\n")); + return false; + } + + mprintf((0, "ATTACH: Child percent rad is invalid\n")); + return false; +} + +// Updates the position of all the subobjects off the ultimate attach parent +void AttachUpdateSubObjects(object *parent) +{ + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + int i; + +#ifdef _DEBUG + if(!Game_update_attach) + return; +#endif + + if(parent->attach_children == NULL) + return; + + for(i = 0; i < parent_pm->n_attach; i++) + { + object *child; + + if((child = ObjGet(parent->attach_children[i])) != NULL) + { + ASSERT(child->flags & OF_ATTACHED); + + child->mtype.phys_info.velocity = parent->mtype.phys_info.velocity; + child->mtype.phys_info.rotvel = parent->mtype.phys_info.rotvel; + + switch(child->attach_type) + { + case AT_UNALIGNED: + AttachDoPosOrient(parent, i, child, child->attach_index, false); + break; + case AT_ALIGNED: + AttachDoPosOrient(parent, i, child, child->attach_index, false); + break; + case AT_RAD: + { + vector attach_pos; + vector attach_fvec; + + if(AttachPointPos(parent, i, true, &attach_pos, true, &attach_fvec)) + { + vector new_child_pos = attach_pos + attach_fvec * child->attach_dist; + + int room = parent->roomnum; + if(ROOMNUM_OUTSIDE(room)) + room = GetTerrainRoomFromPos(&new_child_pos); + + ObjSetPos(child, &new_child_pos, room, NULL,false); + } + else + { + Int3(); // Get chris + } + } + break; + default: + Int3(); // Non-supported attach type - Get Chris + } + + AttachUpdateSubObjects(child); + } + } +} + +// Unattaches an object from its parent +bool UnattachFromParent(object *child) +{ + if(!(child->flags & OF_ATTACHED)) + return false; + + AttachPropagateMass(child, false); + + object *parent = ObjGet(child->attach_parent_handle); + ASSERT(parent); + + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + int i; + + for(i = 0; i < parent_pm->n_attach; i++) + { + if(parent->attach_children[i] == child->handle) + { + parent->attach_children[i] = OBJECT_HANDLE_NONE; + break; + } + } + +// ASSERT(i < parent_pm->n_attach); // Didn't find its parent? Get chris! + + child->flags &= ~OF_ATTACHED; + child->attach_parent_handle = OBJECT_HANDLE_NONE; + child->attach_ultimate_handle = OBJECT_HANDLE_NONE; + + ProprogateUltimateAttachParent(child, child->handle); + if((Game_mode & GM_MULTI) && (Netgame.local_role == LR_SERVER)) + { + MultiSendUnattach(child); + } + if(Demo_flags == DF_RECORDING) + { + DemoWriteUnattachObj(child); + } + + return true; +} + +// Unattaches a child from an attach point +bool UnattachChild(object *parent, char parent_ap) +{ + object *child; + + if((child = ObjGet(parent->attach_children[parent_ap])) != NULL) + { + return UnattachFromParent(child); + } + + return false; +} + +// Unattaches all children from a parent object +bool UnattachChildren(object *parent) +{ + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + int i; + + if(!parent->attach_children) + return false; + + for(i = 0; i < parent_pm->n_attach; i++) + { + object *child; + + if((child = ObjGet(parent->attach_children[i])) != NULL) + { + UnattachFromParent(child); + } + } + + return true; +} \ No newline at end of file diff --git a/Descent3/attach.h b/Descent3/attach.h new file mode 100644 index 000000000..7fcd516f6 --- /dev/null +++ b/Descent3/attach.h @@ -0,0 +1,16 @@ +#ifndef _ATTACH_H_ +#define _ATTACH_H_ + +#include "object.h" + +bool AttachDoPosOrient(object *parent, char parent_ap, object *child, char child_ap, bool f_parent, bool f_move_obj = true, vector *pos = NULL, matrix *orient = NULL, bool f_dropping_off = false); +bool AttachDoPosOrientRad(object *parent, char p_ap, object *child, float rad_percent, vector *pos); +void AttachUpdateSubObjects(object *obj); + +bool AttachObject(object *parent, char parent_ap, object *child, char child_ap, bool f_use_aligned = false); +bool AttachObject(object *parent, char parent_ap, object *child, float percent_rad); +bool UnattachFromParent(object *child); +bool UnattachChild(object *parent, char parent_ap); +bool UnattachChildren(object *parent); + +#endif \ No newline at end of file diff --git a/Descent3/audiotaunts.cpp b/Descent3/audiotaunts.cpp new file mode 100644 index 000000000..d7a75d783 --- /dev/null +++ b/Descent3/audiotaunts.cpp @@ -0,0 +1,753 @@ +#include +#include + +#include "mono.h" +#include "debug.h" +#include "pserror.h" +#include "pstypes.h" +#include "audiotaunts.h" +#include "CFILE.H" +#include "audio_encode.h" +#include "BYTESWAP.H" +#include "mem.h" +#include "ddio.h" +#include "manage.h" +#include "streamaudio.h" +#include "multi.h" +#include "game.h" +#include "stringtable.h" +#include "game2dll.h" +#include "player.h" + +typedef struct{ + int sample_length; + int np_sample_length; + int samples_per_second; + short bits_per_sample; + short number_channels; + unsigned char *sample_8bit; + short *sample_16bit; +}tWaveFile; + +//returns: +// 0: no error +// 1: file not found +// 2: not RIFF format +// 3: not WAVE file +// 4: invalid block length +// 5: invalid file/unknown +// 6: format not supported +// 7: invalid num of channels +// 8: invalid samples +// 9: invalid bit (8 or 16) +// 10: no data +char taunt_LoadWaveFile(char *filename,tWaveFile *wave); + + +int TauntLastError = TAUNTIMPERR_NOERROR; +bool Audio_taunts_enabled = true; +float Audio_taunt_delay_time = 5.0f; + +// taunt_Enable +// +// Enables/Disables audio_taunts for the player +void taunt_Enable(bool enable) +{ + Audio_taunts_enabled = enable; +} + +// taunt_AreEnabled +// +// Returns true if taunts are enabled +bool taunt_AreEnabled(void) +{ + return Audio_taunts_enabled; +} + +// taunt_DelayTime() +// +// Returns the delay time between taunts +float taunt_DelayTime(void) +{ + if(Audio_taunt_delay_time<0) + Audio_taunt_delay_time = 5.0f; + + return Audio_taunt_delay_time; +} + +// taunt_SetDelayTime() +// +// Sets the taunt delay time +void taunt_SetDelayTime(float t) +{ + Audio_taunt_delay_time = t; +} + +// taunt_PlayTauntFile +// +// Given a path to an .osf file, it will play it +bool taunt_PlayTauntFile(const char *filename) +{ + if(!Audio_taunts_enabled) + return false; + + int ret = StreamPlay(filename,(MAX_GAME_VOLUME/2.0f),0); + return true; +} + +// taunt_PlayPlayerTaunt +// +// Given a playernum, and which taunt (0 or 1) it will play that taunt +bool taunt_PlayPlayerTaunt(int pnum,int index) +{ + if(!(Game_mode&GM_MULTI)) + return false; + + if(index<0 || index>3){ + mprintf((0,"TAUNT: invalid index %d\n",index)); + return false; + } + + if ((NetPlayers[pnum].flags & NPF_CONNECTED)&&(NetPlayers[pnum].sequence==NETSEQ_PLAYING)){ + char fullpath[_MAX_PATH]; + char *file; + switch(index) + { + case 0: + file = NetPlayers[pnum].voice_taunt1; + break; + case 1: + file = NetPlayers[pnum].voice_taunt2; + break; + case 2: + file = NetPlayers[pnum].voice_taunt3; + break; + case 3: + file = NetPlayers[pnum].voice_taunt4; + break; + } + + ddio_MakePath(fullpath,LocalCustomSoundsDir,file,NULL); + + if(!cfexist(fullpath)){ + mprintf((0,"TAUNT: file %s doesn't exist (pnum=%d)\n",fullpath,pnum)); + return false; + } + + DLLInfo.me_handle = Objects[Players[pnum].objnum].handle; + DLLInfo.it_handle = OBJECT_HANDLE_NONE; + CallGameDLL (EVT_CLIENT_PLAYERPLAYSAUDIOTAUNT,&DLLInfo); + + return taunt_PlayTauntFile(fullpath); + } + + return false; +} + + +// taunt_GetError +// +// Returns more information about a failed import +int taunt_GetError(void) +{ + return TauntLastError; +} + +// taunt_GetErrorString +// Returns a string describing an error code +char *taunt_GetErrorString(int error) +{ + static char str[256]; + + switch(error){ + case TAUNTIMPERR_NOTFOUND: + strcpy(str,TXT_FILENOTFOUND); + break; + case TAUNTIMPERR_NOTRIFF: + strcpy(str,TXT_NOTRIFF); + break; + case TAUNTIMPERR_NOTWAVE: + strcpy(str,TXT_NOTWAVE); + break; + case TAUNTIMPERR_INVALIDFILE: + strcpy(str,TXT_INVALIDFILE); + break; + case TAUNTIMPERR_NOTSUPPORTED: + strcpy(str,TXT_FORMATNOTSUPPORTED); + break; + case TAUNTIMPERR_INVALIDCHANNELS: + strcpy(str,TXT_MUSTBEMONO); + break; + case TAUNTIMPERR_INVALIDSAMPLES: + strcpy(str,TXT_BADSAMPLERATE); + break; + case TAUNTIMPERR_INVALIDBITDEPTH: + strcpy(str,TXT_BADBITDEPTH); + break; + case TAUNTIMPERR_NODATA: + strcpy(str,TXT_NODATAINWAVE); + break; + case TAUNTIMPERR_OSFEXISTS: + strcpy(str,TXT_OSFALREADYEXISTS); + break; + case TAUNTIMPERR_INTERNALERR: + strcpy(str,TXT_INTERNALERROR); + break; + case TAUNTIMPERR_COMPRESSIONFAILURE: + strcpy(str,TXT_COMPRESSFAIL); + break; + case TAUNTIMPERR_OUTOFMEM: + strcpy(str,TXT_OUTOFMEMORY); + break; + case TAUNTIMPERR_NOOPENOSF: + strcpy(str,TXT_CANTOPENOSF); + break; + default: + strcpy(str,TXT_NOERROR); + break; + } + return str; +} + +// taunt_ImportWave +// Given a fully qualified wave_filename (location of a .wav) and a fully +// qualified outputfilename (where the .osf is to go), it will convert and +// compress the wav file. +#define FILEBUFFER_LENGTH (4 * 1024) +bool taunt_ImportWave(char *wave_filename,char *outputfilename) +{ + ASSERT( wave_filename); + ASSERT( outputfilename); + bool ret = true; + CFILE *file = NULL; + int amount_to_flush; + tWaveFile wavdata; + int samples,rate,chan; + char temp_filename[_MAX_PATH]; + char osftemp_filename[_MAX_PATH]; + ubyte *StaticFileBuffer = NULL; + *temp_filename = *osftemp_filename = '\0'; + OSFArchive osf; + CFILE *fpin = NULL; + bool osfopened; + osfopened = false; + + if( !wave_filename || !outputfilename ){ + TauntLastError = TAUNTIMPERR_NOTFOUND; + return false; + } + + //first make sure the wave file exists + if(!cfexist(wave_filename)){ + mprintf((0,"AudioTaunt: Can't Import wav, missing %s\n",wave_filename)); + TauntLastError = TAUNTIMPERR_NOTFOUND; + return false; + } + + //make sure the output file doesn't exist so we don't overwrite + if(cfexist(outputfilename)){ + mprintf((0,"AudioTaunt: Can't Import wav, %s already exists\n",outputfilename)); + TauntLastError = TAUNTIMPERR_OSFEXISTS; + return false; + } + + //Open the file and import + char wave_ret = taunt_LoadWaveFile(wave_filename,&wavdata); + if(wave_ret!=0){ + mprintf((0,"TAUNT: Unable to load wave\n")); + switch(wave_ret){ + case 1: TauntLastError = TAUNTIMPERR_NOTFOUND; break; + case 2: TauntLastError = TAUNTIMPERR_NOTRIFF; break; + case 3: TauntLastError = TAUNTIMPERR_NOTWAVE; break; + case 4: + case 5: TauntLastError = TAUNTIMPERR_INVALIDFILE; break; + case 6: TauntLastError = TAUNTIMPERR_NOTSUPPORTED; break; + case 7: TauntLastError = TAUNTIMPERR_INVALIDCHANNELS; break; + case 8: TauntLastError = TAUNTIMPERR_INVALIDSAMPLES; break; + case 9: TauntLastError = TAUNTIMPERR_INVALIDBITDEPTH; break; + case 10: TauntLastError = TAUNTIMPERR_NODATA; break; + } + ret = false; + goto error; + } + + //at this point we have a loaded wave file in memory and information about it + //now we need to compress it, first it must be written as raw data to a temp + //file. + + if(!ddio_GetTempFileName(Descent3_temp_directory,"d3o",temp_filename)){ + mprintf((0,"TAUNT: Unable to create temp filename\n")); + ret = false; + TauntLastError = TAUNTIMPERR_INTERNALERR; + goto error; + } + + file = cfopen(temp_filename,"wb"); + if(!file){ + mprintf((0,"TAUNT: Unable to open temp filename\n")); + ret = false; + TauntLastError = TAUNTIMPERR_INTERNALERR; + goto error; + } + + /* + amount_to_flush = (wavdata.bits_per_sample==8)?wavdata.sample_length:wavdata.sample_length*2; + + if(wavdata.bits_per_sample==8 && wavdata.sample_8bit ){ + cf_WriteBytes(wavdata.sample_8bit,amount_to_flush,file); + }else if(wavdata.bits_per_sample==16 && wavdata.sample_16bit ){ + //flip bytes(?)..NOTE SPEEDUP WARNING..THIS IS DONE IN WAVE LOAD..REMOVE IT? + for(int count = 0; count < (int)wavdata.sample_length; count++) + { + wavdata.sample_16bit[count] = INTEL_SHORT(wavdata.sample_16bit[count]); + } + + cf_WriteBytes((ubyte *)wavdata.sample_16bit,amount_to_flush,file); + }else{ + mprintf((0,"TAUNT: Confusion to bits per sample (%d)\n",wavdata.bits_per_sample)); + ret = false; + goto error; + } + mprintf((0,"TAUNT: Wrote to temp file %s\n",temp_filename)); + cfclose(file); file = NULL; + */ + + amount_to_flush = (wavdata.sample_8bit)?wavdata.sample_length:wavdata.sample_length*2; + + ASSERT (wavdata.sample_16bit!=NULL); //only 16 bit should be coming out + + //flip bytes(?)..NOTE SPEEDUP WARNING..THIS IS DONE IN WAVE LOAD..REMOVE IT? + int count; + for(count = 0; count < (int)wavdata.sample_length; count++) + { + wavdata.sample_16bit[count] = INTEL_SHORT(wavdata.sample_16bit[count]); + } + + cf_WriteBytes((ubyte *)wavdata.sample_16bit,amount_to_flush,file); + cfclose(file); file = NULL; + + samples = wavdata.bits_per_sample; + rate = wavdata.samples_per_second; + chan = wavdata.number_channels; + + if(!ddio_GetTempFileName(Descent3_temp_directory,"d3o",osftemp_filename)){ + mprintf((0,"TAUNT: Unable to create osftemp filename\n")); + TauntLastError = TAUNTIMPERR_INTERNALERR; + ret = false; + goto error; + } + + if(!aenc_Compress(temp_filename,osftemp_filename,NULL,&samples,&rate,&chan,NULL,NULL)){ + //unable to compress + mprintf((0,"Unable to compress\n")); + ret = false; + TauntLastError = TAUNTIMPERR_COMPRESSIONFAILURE; + goto error; + } + + // at this point osftemp_filename is a compressed file, now to convert it + // to osf format + // start writing out data. + tOSFDigiHdr digihdr; + uint filelen,nblocks,i; + int format; + + StaticFileBuffer = (ubyte *)mem_malloc(FILEBUFFER_LENGTH); + if(!StaticFileBuffer){ + ret = false; + mprintf((0,"Out of memory\n")); + TauntLastError = TAUNTIMPERR_OUTOFMEM; + goto error; + } + + fpin = cfopen(osftemp_filename, "rb"); + if(!fpin){ + mprintf((0,"Unable to open temp file to make osf\n")); + ret = false; + TauntLastError = TAUNTIMPERR_INTERNALERR; + goto error; + } + + filelen = (uint)cfilelength(fpin); + nblocks = filelen / FILEBUFFER_LENGTH; + + if(!osf.Open(outputfilename,true)){ + ret = false; + mprintf((0,"Unable to open osf to write\n")); + TauntLastError = TAUNTIMPERR_NOOPENOSF; + goto error; + } + osfopened = true; + + // write out data. + for (i = 0; i < nblocks; i++) + { + if (!cf_ReadBytes(StaticFileBuffer, FILEBUFFER_LENGTH, fpin)) { + mprintf((0,"Error reading from osf temp\n")); + ret = false; + TauntLastError = TAUNTIMPERR_INTERNALERR; + goto error; + } + if (!osf.WriteBlock(StaticFileBuffer, FILEBUFFER_LENGTH)) { + mprintf((0,"Error writing to osf\n")); + TauntLastError = TAUNTIMPERR_INTERNALERR; + ret = false; + goto error; + } + } + + if (filelen % FILEBUFFER_LENGTH) { + if (!cf_ReadBytes(StaticFileBuffer, filelen % FILEBUFFER_LENGTH,fpin)) { + mprintf((0,"Error reading from osf temp\n")); + TauntLastError = TAUNTIMPERR_INTERNALERR; + ret = false; + goto error; + } + if (!osf.WriteBlock(StaticFileBuffer, filelen % FILEBUFFER_LENGTH)) { + mprintf((0,"Error writing to osf\n")); + TauntLastError = TAUNTIMPERR_INTERNALERR; + ret = false; + goto error; + } + } + + digihdr.measure = 44100; + + format = 0; + if(wavdata.number_channels==1){ + format |= SAF_MONO_MASK; + }else{ + format |= SAF_STEREO_MASK; + } + + if(wavdata.sample_16bit){ + format |= SAF_16BIT_MASK; + }else{ + format |= SAF_8BIT_MASK; + } + + // save header info + char osftitle[256]; + ddio_SplitPath(outputfilename, NULL, osftitle, NULL); + + if (!osf.SaveHeader(OSF_DIGITAL_STRM, OSF_DIGIACM_STRM, format, samples, filelen, &digihdr, osftitle)) { + mprintf((0,"Failed to write out OSF header info.")); + TauntLastError = TAUNTIMPERR_INTERNALERR; + ret = false; + goto error; + } + + osf.Close(); + +error: + if(file){ + cfclose(file); + } + + if(osfopened) + osf.Close(); + + if(fpin){ + cfclose(fpin); + } + + if(cfexist(osftemp_filename)){ + ddio_DeleteFile (osftemp_filename); + } + + if(cfexist(temp_filename)){ + ddio_DeleteFile (temp_filename); + } + + if(StaticFileBuffer){ + mem_free(StaticFileBuffer); + } + + //free memory for wave file + if(wavdata.sample_16bit) + mem_free(wavdata.sample_16bit); + if(wavdata.sample_8bit) + mem_free(wavdata.sample_8bit); + + return ret; +} + +#define SOUND_FILE_SAMPLE_ALIGNMENT 4 +char taunt_LoadWaveFile(char *filename,tWaveFile *wave) +{ + // File pointer to sound file + CFILE *cfptr; + + wave->sample_16bit = NULL; + wave->sample_8bit = NULL; + wave->np_sample_length = 0; + wave->sample_length = 0; + + cfptr = NULL; + char format_type[80]; // ASCII name of format type + unsigned short fmttag = 0; // Numerical format type + unsigned long ckid; // Current chunk's ID + unsigned long cksize; // Current chunk's size in bytes + unsigned long filesize; // Size of the sound file + unsigned long nextseek = 0; // Location of the next seek + unsigned long aligned_size; // Sound files are aligned to SOUND_FILE_SAMPLE_ALIGNMENT samples + + // Sound format information + int samples_per_second; + short bits_per_sample; + short number_channels; + char error_code = 0; + + // Used to read temporary long values + unsigned long temp_long; + + // Flags for if we previously read data or a format + char f_data, f_fmt = 0; + + // Loop counter + int count; + + // Open the wave file + if((cfptr = cfopen(filename,"rb")) == NULL){ + error_code = 1; + mprintf((0, "TAUNT: Wav %s not found\n", filename)); + goto error_state; + } + + // Make sure that it is a RIFF format + temp_long = (unsigned long) cf_ReadInt(cfptr); + if(temp_long != 0x46464952) + { + error_code = 2; + mprintf((0, "TAUNT: Wav Load: %s is not a RIFF format file\n", filename)); + goto error_state; + } + + // The first item is the filesize minus the RIFF type specifier and filesize (add current location for total filesize) + filesize = cf_ReadInt(cfptr); + filesize += cftell(cfptr); + + // Make sure it is a wave file + temp_long = (unsigned long) cf_ReadInt(cfptr); + if(temp_long != 0x45564157) + { + error_code = 3; + mprintf((0, "TAUNT: Wav Load: %s is not a WAVE file\n", filename)); + goto error_state; + } + nextseek = cftell(cfptr); + + // Read in the chunks + while(nextseek < filesize - 9) // Need room for chunk and its header + { + // Move the the beginning of the next chunck + cfseek(cfptr, nextseek, SEEK_SET); + + // Read in the chunk size and type + ckid = cf_ReadInt(cfptr); + cksize = cf_ReadInt(cfptr); + if(cksize <= 0) + { + error_code = 4; + mprintf((0, "TAUNT: Wav Load: %s has an invalid block length\n", filename)); + goto error_state; + } + + // Determine where the next chunk begins + nextseek = cksize + cftell(cfptr); + + // Determine what to do with the chunk + switch(ckid) + { + case 0x20746D66: //Format Chunk + // Make sure that this format was not preceeded by another format without data inbetween them. + if(f_fmt) + { + error_code = 5; + mprintf((0, "TAUNT: Wav Load: Found 2 formats in a row in file %s\n", filename)); + goto error_state; + } + + // Read in the format type + fmttag = (unsigned short) cf_ReadShort(cfptr); + + switch(fmttag) + { + // We only support WAVE_FORMAT_PCM currently + default: + case 0x0000: strcpy(format_type, "WAVE_FORMAT_UNKNOWN"); break; + case 0x0001: strcpy(format_type, "WAVE_FORMAT_PCM"); break; + case 0x0002: strcpy(format_type, "WAVE_FORMAT_ADPCM"); break; + case 0x0005: strcpy(format_type, "WAVE_FORMAT_IBM_CVSD"); break; + case 0x0006: strcpy(format_type, "WAVE_FORMAT_ALAW"); break; + case 0x0007: strcpy(format_type, "WAVE_FORMAT_MULAW"); break; + case 0x0010: strcpy(format_type, "WAVE_FORMAT_OKI_ADPCM"); break; + case 0x0011: strcpy(format_type, "WAVE_FORMAT_DVI_ADPCM"); break; + case 0x0015: strcpy(format_type, "WAVE_FORMAT_DIGISTD"); break; + case 0x0016: strcpy(format_type, "WAVE_FORMAT_DIGIFIX"); break; + case 0x0020: strcpy(format_type, "WAVE_FORMAT_YAMAHA_ADPCM"); break; + case 0x0021: strcpy(format_type, "WAVE_FORMAT_SONARC"); break; + case 0x0022: strcpy(format_type, "WAVE_FORMAT_DSPGROUP_TRUESPEECH"); break; + case 0x0023: strcpy(format_type, "WAVE_FORMAT_ECHOSC1"); break; + case 0x0024: strcpy(format_type, "WAVE_FORMAT_AUDIOFILE_AF18"); break; + case 0x0101: strcpy(format_type, "IBM_FORMAT_MULAW"); break; + case 0x0102: strcpy(format_type, "IBM_FORMAT_ALAW"); break; + case 0x0103: strcpy(format_type, "IBM_FORMAT_ADPCM"); break; + case 0x0200: strcpy(format_type, "WAVE_FORMAT_CREATIVE_ADPCM"); break; + } + + // Currently, we only support PCM wave files + if (fmttag != 0x0001) + { + error_code = 6; + mprintf((0, "TAUNT Wav Load: %s is a type %s wavefile, we only support WAVE_FORMAT_PCM waves.\n", filename, format_type)); + goto error_state; + } + + // Get the number of channels + number_channels = cf_ReadShort(cfptr); + if(number_channels != 1) + { + error_code = 7; + mprintf((0, "TAUNT Wav Load: Invalid number of channels(%d)in %s, we want mono samples only.\n", number_channels, filename)); + goto error_state; + } + + // Get the number of samples per second + samples_per_second = cf_ReadInt(cfptr); + if(samples_per_second != 22050) + { + error_code = 8; + mprintf((0, "TAUNT Wav Load: Invalid sample per second(%d)in %s, we want 22k samples only.\n", samples_per_second, filename)); + goto error_state; + } + + // get the Average bytes per second (ignore it) + cf_ReadInt(cfptr); + + // Get the Block Alignment (ignore it) + cf_ReadShort(cfptr); + + // Get the Bits per sample + bits_per_sample = cf_ReadShort(cfptr); + if(bits_per_sample != 8 && bits_per_sample != 16) + { + error_code = 9; + mprintf((0, "TAUNT Wav Load: Invalid bits per sample(%d)in %s, we want 8 or 16 bit samples only.\n", bits_per_sample, filename)); + goto error_state; + } + + // We did find a new format type + f_fmt = 1; + f_data = 0; + break; + + // Data Chunk + case 0x61746164: + if(!f_fmt) + { + error_code = 5; + mprintf((0, "TAUNT Wav Load: Format Chunk not defined before Data Chunk\n")); + goto error_state; + } + + // It is either 8 or 16 bit as validated from above + if (bits_per_sample == 8) + { + int num_needed = 0; + + // Force sample alignment + if((cksize-1) % SOUND_FILE_SAMPLE_ALIGNMENT) + { + num_needed = SOUND_FILE_SAMPLE_ALIGNMENT - ((cksize-1) % SOUND_FILE_SAMPLE_ALIGNMENT); + aligned_size = cksize + num_needed; + } + else + { + aligned_size = cksize; + } + + wave->sample_length = aligned_size; + wave->np_sample_length = cksize; + wave->sample_8bit = (ubyte *)mem_malloc(aligned_size); + + cf_ReadBytes ((ubyte *)wave->sample_8bit,cksize,cfptr); + + if(aligned_size != cksize) memset(wave->sample_8bit + cksize, 128, num_needed); + } + else + { + int num_needed = 0; + + wave->sample_length = cksize/2; + wave->np_sample_length = cksize/2; + + if((wave->sample_length - 1)% SOUND_FILE_SAMPLE_ALIGNMENT) + { + num_needed = (SOUND_FILE_SAMPLE_ALIGNMENT - ((wave->sample_length - 1) % SOUND_FILE_SAMPLE_ALIGNMENT)) * 2; + wave->sample_length = cksize/2 + num_needed/2; + } + + wave->sample_16bit = (short *)mem_malloc(cksize + num_needed); + + cf_ReadBytes ((ubyte *)wave->sample_16bit,cksize,cfptr); + for(count = 0; count < (int)cksize/2; count++) + { + wave->sample_16bit[count] = INTEL_SHORT(wave->sample_16bit[count]); + } + + if(num_needed) + memset(wave->sample_16bit + (cksize/2), 0, num_needed); + } + + // We found data, clear format flag + f_data = 1; + f_fmt = 0; // Multiple waveforms, per file, are allowed + break; + + // Undefined chunk + default: + break; + } + } + + // Close the file + cfclose(cfptr); cfptr = NULL; + + if(wave->sample_16bit == NULL) + { + ASSERT(wave->sample_8bit); + + wave->sample_16bit = (short *)mem_malloc(wave->sample_length*sizeof(short)); + + // NOTE: Interesting note on sound conversion: 16 bit sounds are signed (0 biase). 8 bit sounds are unsigned (+128 biase). + for(count = 0; count < (int)wave->sample_length; count++) + { + wave->sample_16bit[count] = (((short)wave->sample_8bit[count]) - 128) * 256; + } + + mem_free(wave->sample_8bit); + wave->sample_8bit = NULL; + } + +error_state: + + if(error_code!=0){ + //free memory and set to NULL + if(wave->sample_16bit) + mem_free(wave->sample_16bit); + if(wave->sample_8bit) + mem_free(wave->sample_8bit); + wave->sample_16bit = NULL; + wave->sample_8bit = NULL; + }else{ + wave->samples_per_second = samples_per_second; + wave->bits_per_sample = bits_per_sample; + wave->number_channels = number_channels; + } + + if(cfptr) + cfclose(cfptr); + + return error_code; +} \ No newline at end of file diff --git a/Descent3/audiotaunts.h b/Descent3/audiotaunts.h new file mode 100644 index 000000000..39a0705a4 --- /dev/null +++ b/Descent3/audiotaunts.h @@ -0,0 +1,94 @@ +/* +* $Logfile: /DescentIII/main/audiotaunts.h $ +* $Revision: 4 $ +* $Date: 4/29/99 11:02p $ +* $Author: Jeff $ +* +* in-game audio taunt API +* +* $Log: /DescentIII/main/audiotaunts.h $ + * + * 4 4/29/99 11:02p Jeff + * added the ability for the server to set audio taunt delay time via + * command line option and/or dedicated server console + * + * 3 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 2 1/27/99 5:47p Jeff + * audio taunts implemented! + * + * 1 1/27/99 1:31a Jeff +* +* $NoKeywords: $ +*/ + +#ifndef __AUDIO_TAUNT_H_ +#define __AUDIO_TAUNT_H_ + +extern bool Audio_taunts_enabled; + +// Error codes: +#define TAUNTIMPERR_NOERROR 0 +#define TAUNTIMPERR_NOTFOUND 1 +#define TAUNTIMPERR_NOTRIFF 2 +#define TAUNTIMPERR_NOTWAVE 3 +#define TAUNTIMPERR_INVALIDFILE 4 +#define TAUNTIMPERR_NOTSUPPORTED 5 +#define TAUNTIMPERR_INVALIDCHANNELS 6 +#define TAUNTIMPERR_INVALIDSAMPLES 7 +#define TAUNTIMPERR_INVALIDBITDEPTH 8 +#define TAUNTIMPERR_NODATA 9 +#define TAUNTIMPERR_OSFEXISTS 10 +#define TAUNTIMPERR_INTERNALERR 11 +#define TAUNTIMPERR_COMPRESSIONFAILURE 12 +#define TAUNTIMPERR_OUTOFMEM 13 +#define TAUNTIMPERR_NOOPENOSF 14 + +// taunt_GetError +// +// Returns more information about a failed import +int taunt_GetError(void); + +// taunt_GetErrorString +// Returns a string describing an error code +char *taunt_GetErrorString(int error); + +// taunt_ImportWave +// Given a fully qualified wave_filename (location of a .wav) and a fully +// qualified outputfilename (where the .osf is to go), it will convert and +// compress the wav file. +bool taunt_ImportWave(char *wave_filename,char *outputfilename); + +// taunt_PlayTauntFile +// +// Given a path to an .osf file, it will play it +bool taunt_PlayTauntFile(const char *filename); + +// taunt_PlayPlayerTaunt +// +// Given a playernum, and which taunt (0 or 1) it will play that taunt +bool taunt_PlayPlayerTaunt(int pnum,int index); + +// taunt_AreEnabled +// +// Returns true if taunts are enabled +bool taunt_AreEnabled(void); + +// taunt_Enable +// +// Enables/Disables audio_taunts for the player +void taunt_Enable(bool enable); + +// taunt_DelayTime() +// +// Returns the delay time between taunts +float taunt_DelayTime(void); + +// taunt_SetDelayTime() +// +// Sets the taunt delay time +void taunt_SetDelayTime(float t); + +#endif diff --git a/Descent3/bnode.cpp b/Descent3/bnode.cpp new file mode 100644 index 000000000..12bc75e5b --- /dev/null +++ b/Descent3/bnode.cpp @@ -0,0 +1,622 @@ +/* +* $Logfile: /DescentIII/Main/bnode.cpp $ +* $Revision: 23 $ +* $Date: 10/21/99 2:36p $ +* $Author: Matt $ +* +* BOA Helper Node functions +* +* $Log: /DescentIII/Main/bnode.cpp $ + * + * 23 10/21/99 2:36p Matt + * Mac merge + * + * 22 10/08/99 4:27p Chris + * Removed a needless ASSERT + * + * 21 5/20/99 8:09p Chris + * Patched up the bnode crashes + * + * 20 5/19/99 2:40p Chris + * + * 19 5/13/99 4:23p Matt + * Fixed, hopefully, a problem remapping nodes. + * + * 18 5/10/99 10:07p Ardussi + * changes to compile on Mac + * + * 17 5/09/99 11:43p Chris + * Fixed a bug with no generating a random node (worst case for the + * terrain) + * + * 16 5/09/99 5:24p Chris + * Maxed the bnode distance to the AI_SEE distance + * + * 15 5/06/99 5:18p Chris + * Updated the BNode system - vastly improved error checking and verify + * + * 14 5/02/99 6:16a Chris + * Massive bnode error checking improvements + * + * 13 5/01/99 4:06p Jeff + * fixed #include for linux + * + * 12 4/28/99 8:27a Chris + * + * 11 4/28/99 5:33a Chris + * Further improved the BNode system. (It actually works inside and + * outside now) + * + * 10 4/27/99 11:37p Chris + * Improving the BNode system + * + * 9 4/27/99 3:01a Chris + * Improving Bnode stuff + * + * 8 4/26/99 11:11a Chris + * Updated Bnode system + * + * 7 4/25/99 8:57p Chris + * Improving the Bnode system + * + * 6 4/18/99 4:35p Matt + * Changed BNode_FreeRoom() to take a room pointer instead of a room + * number, since it was being called for rooms in the scrap buffer, which + * weren't in the room list. Also added code to InitRoom() to init the + * bnode data. + * + * 5 4/18/99 6:50a Chris + * + * 4 4/18/99 5:39a Chris + * Vastly improved the path node system + * + * 3 4/15/99 5:48p Chris + * Fixed a bug with rendering the BOAPath nodes + * + * 2 4/14/99 3:12p Chris + * Beginning to add BoaNode stuff +* +* $NoKeywords: $ +*/ + +#ifdef MACINTOSH +#include +#endif + +#include "memory.h" +#include "bnode.h" +#include "room.h" +#include "mem.h" +#include "vecmat.h" +#include "terrain.h" +#include "room.h" +#include "findintersection.h" +#include "BOA.h" +#include "psrand.h" + +bn_list BNode_terrain_list[8]; +bool BNode_allocated = false; +bool BNode_verified = false; + +//-- Priority Queue +class pq_item +{ + public: + pq_item(int node_index, int parent_node, float n_cost) + { + node = node_index; + p_node = parent_node; + cost = n_cost; + next = NULL; + } + + int node; + int p_node; + float cost; + + pq_item *next; +}; + +class bpq +{ + pq_item *q_head; + + public: + bpq() {q_head = NULL;} + + void push(pq_item *new_node) + { + new_node->next = q_head; + q_head = new_node; + } + + pq_item *pop() + { + pq_item *cur_item = q_head; + pq_item *prev_item = NULL; + pq_item *best_item = q_head; + pq_item *best_prev_item = NULL; + + if(q_head == NULL) return NULL; + + while(cur_item != NULL) + { + if(cur_item->cost < best_item->cost) + { + + best_prev_item = prev_item; + best_item = cur_item; + } + + prev_item = cur_item; + cur_item = cur_item->next; + } + + + // Actually remove this item from the list + if(best_item == q_head) + { + q_head = best_item->next; + } + else + { + best_prev_item->next = best_item->next; + } + + return best_item; + } +}; + +float BNode_QuickDist(vector *pos1, vector *pos2) +{ + return fabs(pos1->x - pos2->x) + fabs(pos1->y - pos2->y) + fabs(pos1->z - pos2->z); +} + +int BNode_Path[MAX_BNODES_PER_ROOM]; +int BNode_PathNumNodes; + +void BNode_UpdatePathInfo(pq_item **node_list, int start, int end) +{ + int cur_node = end; + int i; + + BNode_PathNumNodes = 0; + +// mprintf((0, "start crash loop\n")); + while(cur_node != -1) + { + BNode_Path[BNode_PathNumNodes++] = cur_node; + cur_node = node_list[cur_node]->p_node; + } +// mprintf((0, "end crash loop\n")); + + // Reverse the list (so it is what we want) + for(i = 0; i < BNode_PathNumNodes>>1; i++) + { + int temp; + + temp = BNode_Path[i]; + BNode_Path[i] = BNode_Path[BNode_PathNumNodes - i - 1]; + BNode_Path[BNode_PathNumNodes - i - 1] = temp; + } + +/* for(i = 0; i < BNode_PathNumNodes; i++) + { + mprintf((0, "Node %d\n", BNode_Path[i])); + } +*/ +} + +// Ok to use Highest_room_index offset stuff +bool BNode_FindPath(int start_room, int i, int j, float rad) +{ + bpq PQPath; + int counter; + pq_item *start_node = new pq_item(i, -1, 0.0f); + pq_item *cur_node; + bool f_found = false; + +// mprintf((0, "++++++++++\nFind path from %d to %d in room %d\n", i, j, start_room)); + + start_room = BOA_INDEX(start_room); + + pq_item **node_list; + bn_list *bnlist = BNode_GetBNListPtr(start_room); + + ASSERT(bnlist); + ASSERT(i >= 0 && i < bnlist->num_nodes && j >= 0 && j < bnlist->num_nodes); + + node_list = (pq_item **) mem_malloc(bnlist->num_nodes * sizeof(pq_item *)); + memset(node_list, 0, bnlist->num_nodes * sizeof(pq_item *)); + + PQPath.push(start_node); + + while(cur_node = PQPath.pop()) + { + node_list[cur_node->node] = cur_node; + + if(cur_node->node == j) + { + BNode_UpdatePathInfo(node_list, i, j); + f_found = true; + goto done; + } + + int num_edges; + + num_edges = bnlist->nodes[cur_node->node].num_edges; + + for(counter = 0; counter < num_edges; counter++) + { + int next_node; + pq_item *list_item; + float new_cost; + +// if(!BOA_PassablePortal(cur_node->roomnum, counter, false, false)) +// continue; + + if(next_node = bnlist->nodes[cur_node->node].edges[counter].end_room != start_room) + continue; + + next_node = bnlist->nodes[cur_node->node].edges[counter].end_index; + + ASSERT(bnlist->nodes[cur_node->node].edges[counter].cost > 0); + new_cost = cur_node->cost + bnlist->nodes[cur_node->node].edges[counter].cost; + + list_item = node_list[next_node]; + + if(list_item != NULL && list_item->cost < new_cost) + continue; + + if(list_item == NULL) + { + list_item = new pq_item(next_node, cur_node->node, new_cost); + + node_list[next_node] = list_item; + PQPath.push(list_item); + } + else + { + list_item->cost = new_cost; + list_item->p_node = cur_node->node; + } + } + } + +done: + for(counter = 0; counter < bnlist->num_nodes; counter++) + { + if (node_list[counter]) delete node_list[counter]; + } + + mem_free(node_list); //DAJ LEAKFIX + return f_found; +} + +int BNode_GenerateBestPathThroughRoom(int sroom, int spnt, int croom, int eroom, int eportal, int max_nodes, vector *pos_list) +{ + return -1; +} + +char BNode_vis[MAX_BNODES_PER_ROOM]; +#define VIS_NO_CHECK 2 +#define VIS_OK 1 +#define VIS_NO 2 + +int BNode_FindDirLocalVisibleBNode(int roomnum, vector *pos, vector *fvec, float rad) +{ + int i; + float best_dot = -1.01f; + float closest_dist = 800.0f; + int closest_node = -1; + bool f_retry = false; + + float min_bn_rad = rad / 4.0f; + if(min_bn_rad > MAX_BNODE_SIZE) + { + min_bn_rad = MAX_BNODE_SIZE; + } + + bn_list *bnlist = BNode_GetBNListPtr(roomnum); + + retry: + + for(i = 0; i < bnlist->num_nodes; i++) + { + vector to = bnlist->nodes[i].pos - *pos; + float dist = vm_NormalizeVector(&to); + + if(dist < closest_dist) + { + float dot = *fvec * to; + + if(dot > 0.0f || f_retry) + { + float node_size = 0.0f; + +/* if(!f_retry) + { + for(j = 0; j < bnlist->nodes[i].num_edges; j++) + { + if(bnlist->nodes[i].edges[j].max_rad > node_size) + { + node_size = bnlist->nodes[i].edges[j].max_rad; + } + } + } +*/ + if(!f_retry || (f_retry && BNode_vis[i] == VIS_NO_CHECK)) + { + fvi_query fq; + fvi_info hit_info; + + fq.p0 = pos; + fq.startroom = (roomnum > Highest_room_index && roomnum <= Highest_room_index + 8)?GetTerrainRoomFromPos(pos):roomnum; + fq.p1 = &bnlist->nodes[i].pos; + fq.rad = min_bn_rad; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_NO_RELINK | FQ_IGNORE_NON_LIGHTMAP_OBJECTS; + + if(fvi_FindIntersection(&fq, &hit_info) == HIT_NONE) + { + BNode_vis[i] = VIS_OK; + best_dot = dot; + closest_node = i; + } + else + { + if(!f_retry) + { + BNode_vis[i] = VIS_NO; + } + else + { + if(BNode_vis[i] == VIS_OK) + { + closest_node = i; + } + } + } + } + else + { + if(f_retry && BNode_vis[i] == VIS_OK) + { + closest_node = i; + } + } + } + else + { + BNode_vis[i] = VIS_NO_CHECK; + } + } + else + { + BNode_vis[i] = VIS_NO_CHECK; + } + } + + if(closest_node == -1 && !f_retry) + { + f_retry = true; + goto retry; + } + + ASSERT(bnlist->num_nodes > 0); + if(closest_node == -1 && bnlist->num_nodes > 0) + { + closest_node = ps_rand() % bnlist->num_nodes; + } + + ASSERT(closest_node != -1); + + return closest_node; +} + +int BNode_FindClosestLocalVisibleBNode(int roomnum, vector *pos, float rad) +{ + int i, j; + float closest_dist = 800.0f; + int closest_node = -1; + bool f_retry = false; + + float min_bn_rad = rad / 4.0f; + if(min_bn_rad > MAX_BNODE_SIZE) + { + min_bn_rad = MAX_BNODE_SIZE; + } + + bn_list *bnlist = BNode_GetBNListPtr(roomnum); + + retry: + + for(i = 0; i < bnlist->num_nodes; i++) + { + float dist = BNode_QuickDist(&bnlist->nodes[i].pos, pos); + + if(dist < closest_dist) + { + float node_size = 0.0f; + + if(!f_retry) + { + for(j = 0; j < bnlist->nodes[i].num_edges; j++) + { + if(bnlist->nodes[i].edges[j].max_rad > node_size) + { + node_size = bnlist->nodes[i].edges[j].max_rad; + } + } + } + + if(f_retry || node_size >= min_bn_rad) + { + fvi_query fq; + fvi_info hit_info; + + if(!f_retry) + { + fq.p0 = pos; + fq.startroom = (roomnum > Highest_room_index && roomnum <= Highest_room_index + 8)?GetTerrainRoomFromPos(pos):roomnum; + fq.p1 = &bnlist->nodes[i].pos; + fq.rad = min_bn_rad; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = 0; + } + + if(f_retry || fvi_FindIntersection(&fq, &hit_info) == HIT_NONE) + { + closest_dist = dist; + closest_node = i; + } + } + } + } + + if(closest_node == -1 && !f_retry) + { + f_retry = true; + goto retry; + } + + ASSERT(bnlist->num_nodes > 0); + if(closest_node == -1 && bnlist->num_nodes > 0) + { + closest_node = ps_rand() % bnlist->num_nodes; + } + + ASSERT(closest_node != -1); + + return closest_node; +} + +bn_list *BNode_GetBNListPtr(int roomnum, bool f_in_load_level) +{ + if(roomnum == -1) + { + return NULL; + } + else if(roomnum >= 0 && roomnum <= Highest_room_index) + { +// if(!f_in_load_level) +// { +// ASSERT(!(Rooms[roomnum].flags & RF_EXTERNAL)); +// } + + if(!Rooms[roomnum].used) + { + return NULL; + } + + room *rp = &Rooms[roomnum]; + return &rp->bn_info; + } + else if(ROOMNUM_OUTSIDE(roomnum)) + { + return &BNode_terrain_list[TERRAIN_REGION(roomnum)]; + } + else if(roomnum <= Highest_room_index + 8) + { + return &BNode_terrain_list[roomnum - Highest_room_index - 1]; + } + + return NULL; +} + +void BNode_ClearBNodeInfo(void) +{ + int i, j; + + for(i = 0; i < 8; i++) + { + if(BNode_allocated) + { + for(j = 0; j < BNode_terrain_list[i].num_nodes; j++) { + if(BNode_terrain_list[i].nodes[j].num_edges) + { + mem_free(BNode_terrain_list[i].nodes[j].edges); + } + } + mem_free(BNode_terrain_list[i].nodes); + } + + BNode_terrain_list[i].num_nodes = 0; + BNode_terrain_list[i].nodes = NULL; + } + + BNode_allocated = false; + BNode_verified = false; +} + +bool BNode_MakeSubPath(short sroom, short spnt, short eroom, short epnt, int flags, float size, short *roomlist, short *pnts, int max_elements) +{ + return false; +} + +void BNode_FreeRoom(room *rp) +{ + int i; + + for(i = 0; i < rp->bn_info.num_nodes; i++) + { + if(rp->bn_info.nodes[i].num_edges) + { + mem_free(rp->bn_info.nodes[i].edges); + } + } + + if(rp->bn_info.num_nodes) + { + mem_free(rp->bn_info.nodes); + rp->bn_info.nodes = NULL; + rp->bn_info.num_nodes = 0; + } +} + +void BNode_RemapTerrainRooms(int old_hri, int new_hri) +{ + int delta = new_hri - old_hri; + int i; + + if(!BNode_allocated) + return; + + if(delta == 0) + return; + + if(new_hri < 0) + return; + + ASSERT(delta <= 1); + + for(i = 0; i <= new_hri + BOA_num_terrain_regions; i++) + { + + if((i <= new_hri && Rooms[i].used) || (i > new_hri)) + { + int j; + int k; + + //Skip external rooms + if ((i <= new_hri) && (Rooms[i].flags & RF_EXTERNAL)) + continue; + + bn_list *bnlist = BNode_GetBNListPtr(i); + ASSERT(bnlist); + + for(j = 0; j < bnlist->num_nodes; j++) + { + for(k = 0; k < bnlist->nodes[j].num_edges; k++) + { + if(bnlist->nodes[j].edges[k].end_room >= new_hri) + { + bnlist->nodes[j].edges[k].end_room += delta; + } + } + } + } + } +} \ No newline at end of file diff --git a/Descent3/bnode.h b/Descent3/bnode.h new file mode 100644 index 000000000..b1b23c208 --- /dev/null +++ b/Descent3/bnode.h @@ -0,0 +1,106 @@ +/* +* $Logfile: /DescentIII/main/bnode.h $ +* $Revision: 13 $ +* $Date: 5/06/99 5:18p $ +* $Author: Chris $ +* +* BOA Helper Node structs and externs +* +* $Log: /DescentIII/main/bnode.h $ + * + * 13 5/06/99 5:18p Chris + * Updated the BNode system - vastly improved error checking and verify + * + * 12 5/03/99 12:56a 3dsmax + * Knocked MAX_BNODES_PER_ROOM down to 127 after reading the comment about + * a byte count in the portal struct. + * + * 11 5/03/99 12:50a 3dsmax + * Changed MAX_BNODES_PER_ROOM from 100 to 200 for Luke. (MattT) + * + * 10 4/29/99 1:59a Chris + * Added the portal blockage support + * + * 9 4/28/99 5:33a Chris + * Further improved the BNode system. (It actually works inside and + * outside now) + * + * 8 4/27/99 3:01a Chris + * Improving Bnode stuff + * + * 7 4/26/99 11:11a Chris + * Updated Bnode system + * + * 6 4/25/99 9:02p Chris + * Improving the Bnode system + * + * 5 4/18/99 4:35p Matt + * Changed BNode_FreeRoom() to take a room pointer instead of a room + * number, since it was being called for rooms in the scrap buffer, which + * weren't in the room list. Also added code to InitRoom() to init the + * bnode data. + * + * 4 4/18/99 5:39a Chris + * Vastly improved the path node system + * + * 3 4/15/99 5:48p Chris + * Fixed a bug with rendering the BOAPath nodes + * + * 2 4/14/99 3:13p Chris + * Beginning to add BoaNode stuff +* +* $NoKeywords: $ +*/ + +#ifndef BNODE_H_ +#define BNODE_H_ + +#include "vecmat_external.h" + +#define MAX_BNODE_SIZE 20.0f +// Not bigger than 127 - char bnode - in portal struct +#define MAX_BNODES_PER_ROOM 127 + +typedef struct bn_edge +{ + short end_room; + char end_index; + + short flags; + short cost; + + float max_rad; +} bn_edge; + +typedef struct bn_node +{ + vector pos; + int num_edges; + bn_edge *edges; +} bn_node; + +typedef struct bn_list +{ + int num_nodes; + bn_node *nodes; +} bn_list; + +struct room; + +extern void BNode_FreeRoom(room *rp); +extern void BNode_ClearBNodeInfo(void); + +bn_list *BNode_GetBNListPtr(int roomnum, bool f_in_load_level = false); + +extern bn_list BNode_terrain_list[8]; +extern bool BNode_allocated; +extern bool BNode_verified; +void BNode_RemapTerrainRooms(int old_hri, int new_hri); + +extern int BNode_Path[MAX_BNODES_PER_ROOM]; +extern int BNode_PathNumNodes; +extern bool BNode_FindPath(int start_room, int i, int j, float rad); +extern int BNode_FindDirLocalVisibleBNode(int roomnum, vector *pos, vector *fvec, float rad); +extern int BNode_FindClosestLocalVisibleBNode(int roomnum, vector *pos, float rad); + +#endif \ No newline at end of file diff --git a/Descent3/bsp.cpp b/Descent3/bsp.cpp new file mode 100644 index 000000000..c4dfcc4f8 --- /dev/null +++ b/Descent3/bsp.cpp @@ -0,0 +1,1316 @@ +/* + * $Logfile: /DescentIII/main/bsp.cpp $ + * $Revision: 32 $ + * $Date: 4/19/00 10:18a $ + * $Author: Matt $ + * + * Some more of BSP tree code, I presume. + * + * $Log: /DescentIII/main/bsp.cpp $ + * + * 32 4/19/00 10:18a Matt + * Added check for BSP tree initialized before freeing it. + * + * 31 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 30 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 29 9/22/98 12:01p Matt + * Added SourceSafe headers + * + */ + +#include "bsp.h" +#include "room.h" +#include "mem.h" +#include "polymodel.h" +#include +#include "object.h" +#include "psrand.h" + +#define BSP_TREE_VERSION 10003 + + +bsptree MineBSP; +ubyte BSP_initted=0; +int ConvexSubspaces=0,ConvexPolys=0; +int Solids=0,Empty=0; + +int BSPChecksum=-1; +ubyte UseBSP=0; + +// Goes through all the valid points in the indoor engine and returns a unique +// checksum +int BSPGetMineChecksum () +{ + int i,t,k; + float total=0; + int checksum; + + for (i=0;i<=Highest_room_index;i++) + { + room *rp=&Rooms[i]; + + if (!Rooms[i].used) + continue; + + if (Rooms[i].flags & RF_EXTERNAL) + continue; + + for (t=0;tnum_faces;t++) + { + face *fp=&rp->faces[t]; + + for (k=0;knum_verts;k++) + { + total+=rp->verts[fp->face_verts[k]].x; + total+=rp->verts[fp->face_verts[k]].y; + total+=rp->verts[fp->face_verts[k]].z; + } + } + } + + for (t=0;t<=Highest_object_index;t++) + { + object *obj=&Objects[t]; + vector vert; + + if (obj->type==OBJ_NONE) + continue; + + if (OBJECT_OUTSIDE(obj)) + continue; + + if (obj->lighting_render_type==LRT_LIGHTMAPS) + { + poly_model *po=&Poly_models[obj->rtype.pobj_info.model_num]; + if (!po->new_style) + continue; + + for (k=0;kn_models;k++) + { + bsp_info *sm=&po->submodel[k]; + + if (IsNonRenderableSubmodel (po,k)) + continue; + + for (int j=0;jnum_faces;j++) + { + for (int x=0;xfaces[j].nverts;x++) + { + GetObjectPointInWorld (&vert,obj,k,sm->faces[j].vertnums[x]); + total+=vert.x; + total+=vert.y; + total+=vert.z; + } + + } + } + } + } + + checksum=total*BSP_TREE_VERSION; + + return checksum; +} + +// Allocates memory for a bspnode +// Returns pointer to node +bspnode *NewBSPNode(void) +{ + bspnode *node; + + if((node = (bspnode *) mem_malloc(sizeof(bspnode))) == NULL) + { + return NULL; + } + + /* Initialize the node, set its variables to zero */ + + node->type=BSP_NODE; + node->polylist=NULL; + node->num_polys=0; + node->node_facenum=0; + node->node_roomnum=0; + node->node_subnum=0; + + node->plane.a = 0.0; + node->plane.b = 0.0; + node->plane.c = 0.0; + node->plane.d = 0.0; + node->front = NULL; + node->back = NULL; + + return node; +} + +// Allocates memory for a polygon +// Returns pointer to polygon +bsppolygon *NewPolygon(int roomnum,int facenum,int numverts) +{ + bsppolygon *newpoly; + vector *verts; + + newpoly = (bsppolygon *) mem_malloc(sizeof(bsppolygon)); + + if(newpoly==NULL) + { + mprintf((0,"NewPolygon: Couldn't allocate polygon\n")); + return NULL; + } + + verts=(vector *)mem_malloc (sizeof(vector)*numverts); + ASSERT (verts!=NULL); + + newpoly->nv = numverts; + newpoly->verts=verts; + + newpoly->roomnum=roomnum; + newpoly->facenum=facenum; + newpoly->color=ps_rand()*ps_rand(); + + return newpoly; +} + +// Saves and BSP node to an open file and recurses with the nodes children +void SaveBSPNode (CFILE *outfile,bspnode *node) +{ + if (node->type==BSP_SOLID_LEAF) + { + cf_WriteByte (outfile,BSP_SOLID_LEAF); + } + else if (node->type==BSP_EMPTY_LEAF) + { + cf_WriteByte (outfile,BSP_EMPTY_LEAF); + } + else + { + ASSERT (node->front); + ASSERT (node->back); + + cf_WriteByte (outfile,BSP_NODE); + + // Write out this planes node + cf_WriteFloat (outfile,node->plane.a); + cf_WriteFloat (outfile,node->plane.b); + cf_WriteFloat (outfile,node->plane.c); + cf_WriteFloat (outfile,node->plane.d); + cf_WriteShort (outfile,node->node_roomnum); + cf_WriteShort (outfile,node->node_facenum); + cf_WriteByte (outfile,node->node_subnum); + + // Write out its children + SaveBSPNode (outfile,node->front); + SaveBSPNode (outfile,node->back); + } +} + + +// Loads a bsp node from an open file and recurses with its children +void LoadBSPNode (CFILE *infile,bspnode **node) +{ + // Get the node type + ubyte type=cf_ReadByte (infile); + + // Allocate the node and set its type + *node=NewBSPNode (); + ASSERT (*node); + bspnode *localnode=*node; + + localnode->type=type; + + if (type==BSP_EMPTY_LEAF || type==BSP_SOLID_LEAF) + return; + + // Load the nodes plane + localnode->plane.a=cf_ReadFloat(infile); + localnode->plane.b=cf_ReadFloat(infile); + localnode->plane.c=cf_ReadFloat(infile); + localnode->plane.d=cf_ReadFloat(infile); + localnode->node_roomnum=cf_ReadShort (infile); + localnode->node_facenum=cf_ReadShort (infile); + localnode->node_subnum=cf_ReadByte (infile); + + // Recurse to get the children + LoadBSPNode (infile,&localnode->front); + LoadBSPNode (infile,&localnode->back); +} + + +// Frees up a polygon and all the vertices associated with it +void FreePolygon (bsppolygon *poly) +{ + ASSERT (poly!=NULL); + ASSERT (poly->nv>2); + + if (poly->nv>0) + { + mem_free (poly->verts); + } + + mem_free (poly); +} + +// Calculates the plane equation for a given polygon +void CalculatePolygonPlane (bsppolygon *poly) +{ + ASSERT (poly->nv>=3); + + vector *vec=&poly->verts[0]; + + poly->plane.d=-(vec->x*poly->plane.a+vec->y*poly->plane.b+vec->z*poly->plane.c); +} + +// Classifies whether or not a point is in front,behind, or on a plane +int ClassifyVector(bspplane *plane, vector *vec) +{ + float dist; + + dist = vec->x*plane->a+vec->y*plane->b+vec->z*plane->c+plane->d; + + if(dist > BSP_EPSILON) + return BSP_IN_FRONT; + else if (dist < -BSP_EPSILON) + return BSP_BEHIND; + + return BSP_ON_PLANE; +} + +// Classifies whether or not a polygon is behind,in front, or straddles a plane +int ClassifyPolygon(bspplane *plane, bsppolygon *poly) +{ + int numfront, numback, numon; + int i; + int nv=poly->nv; + + ASSERT (nv>=3); + + numfront = numback = numon = 0; + + for (i=0;iverts[i]; + + fate=ClassifyVector(plane, vec); + + switch (fate) + { + case BSP_IN_FRONT: + numfront++; + break; + case BSP_BEHIND: + numback++; + break; + case BSP_ON_PLANE: + numfront++; + numback++; + numon++; + break; + } + } + + + if(numon == nv) + { + return BSP_COINCIDENT; + } + else if(numfront == nv) + { + return BSP_IN_FRONT; + } + else if(numback == nv) + { + return BSP_BEHIND; + } + + // The polygon straddles the plane + return BSP_SPANNING; +} + + +// Tries to split a polygon across a plane +int SplitPolygon(bspplane *plane,bsppolygon *testpoly, bsppolygon **frontpoly,bsppolygon **backpoly) +{ + float dists[256], t; + int numvert, numfront, numback, i, codes[256]; + vector *frontvert[256], *backvert[256], *polyvert[256]; + vector *vertptr1, *vertptr2; + vector delta,*newvert[256]; + int num_new_verts=0; + + /* Set up the function. Set all counters, variables, lists etc to + * zero or NULL, which provides a fall back result, should nothing + * be changed, and simply means that particular result is to be + * ignored + */ + + numvert = testpoly->nv; + numfront = numback = 0; + + /* Now, we shall classify each vertex in the polygon, with both + * a plane distance, and a classification code. This is done here, + * as the results are often re-used, which saves time. + */ + + for(i=0; iverts[i]; + polyvert[i] = vertptr1; + codes[i] = ClassifyVector(plane, vertptr1); + dists[i] = plane->a*vertptr1->x + plane->b*vertptr1->y + plane->c*vertptr1->z + plane->d; + } + + /* We must duplicate the first entry in the numvert+1 slot, so that + * we can use wraparound to easily check things + */ + + codes[numvert] = codes[0]; + dists[numvert] = dists[0]; + + /* Now, the actual splitting work. We must work through each vertex + * of the polygon, examining it with regard to the plane + * Where the classification codes differ, we have to split + */ + + for(i=0; i=3); + ASSERT (numback>=3); + + (*frontpoly)=NewPolygon (testpoly->roomnum,testpoly->facenum,numfront); + (*backpoly)=NewPolygon (testpoly->roomnum,testpoly->facenum,numback); + + for(i=0; iverts[i]=*frontvert[i]; + temppoly->plane = testpoly->plane; + temppoly->facenum=testpoly->facenum; + temppoly->roomnum=testpoly->roomnum; + temppoly->subnum=testpoly->subnum; + } + + for(i=0; iverts[i]=*backvert[i]; + temppoly->plane = testpoly->plane; + temppoly->facenum=testpoly->facenum; + temppoly->roomnum=testpoly->roomnum; + temppoly->subnum=testpoly->subnum; + } + + for (i=0;inext) + { + outerpoly = (bsppolygon *) outer->data; + splits = front = back = planar = 0; + score = 0.0; + + if (outerpoly->plane.used) + continue; + + // Only check 300 times + if (checked>=300) + return bestpoly; + + for(inner=*polylist; inner!=NULL; inner=inner->next) + { + innerpoly = (bsppolygon *) inner->data; + if(innerpoly != outerpoly) + { + result = ClassifyPolygon(&outerpoly->plane,innerpoly); + switch(result) + { + case BSP_SPANNING: + splits++; + break; + case BSP_IN_FRONT: + front++; + break; + case BSP_BEHIND: + back++; + break; + case BSP_COINCIDENT: + planar++; + break; + } + } + } + + /* Calculate the score. If we have a "perfect" tree, ie + * it has either no splits, or is perfectly balanced, + * return the plane to the caller + */ + + balance = abs(front - back); + score = g_kbalance*balance + g_ksplit*splits + g_kplanar*planar; + + checked++; + + if(score <0.005) + return outerpoly; + + if(score < bestscore) + { + bestscore = score; + bestpoly = outerpoly; + } + } + + return bestpoly; +} + + +// This is the routine that recursively builds the bsptree +int BuildBSPNode (bspnode *tree,list **polylist,int numpolys) +{ + bsppolygon *partition_poly; + listnode *lnode=*polylist; + listnode *next; + bspnode *frontnode,*backnode; + list *frontlist=NULL,*backlist=NULL; + bspplane partition_plane; + int numfront=0,numback=0,numsplits=0; + + ASSERT (numpolys>0); + partition_poly=SelectPlane (polylist); + + mprintf_at((2,5,0,"Node = %c",Twirly[(Node_twirl++)%4])); + + if (partition_poly==NULL) + { + // We hit a leaf! Fill in the appropriate leaf stuff + + /*vector center; + int total=0; + + vm_MakeZero (¢er); + + for (int i=0;ipolylist,poly); + tree->num_polys++; + vector polycenter; + + ConvexPolys++; + vm_MakeZero (&polycenter); + + // Find out if this is a solid or empty leaf + // This routine may cause some problems because of convex subspace + for (int t=0;tnv;t++) + { + polycenter+=poly->verts[t]; + + } + + polycenter/=poly->nv; + center+=polycenter; + + } + + center/=numpolys; + int ins=0,outs=0; + + for (i=0;iplane,¢er); + + if (fate==BSP_IN_FRONT || fate==BSP_ON_PLANE) + { + outs++; + } + else + ins++; + + } + + if (ins==numpolys) + { + tree->type=BSP_SOLID_LEAF; + Solids++; + } + else if (outs==numpolys) + { + tree->type=BSP_EMPTY_LEAF; + Empty++; + } + else + { + mprintf ((0,"Ins/outs don't match! ins=%d outs=%d np=%d\n",ins,outs,numpolys)); + + for (i=0;iroomnum,thispoly->facenum)); + } + + if (ins>outs) + { + tree->type=BSP_SOLID_LEAF; + Solids++; + } + else + { + tree->type=BSP_EMPTY_LEAF; + Empty++; + } + + }*/ + + tree->type=BSP_EMPTY_LEAF; + + ConvexSubspaces++; + return 1; + } + + + + // We need to process this node and classify all child polygons + tree->node_facenum=partition_poly->facenum; + tree->node_roomnum=partition_poly->roomnum; + tree->node_subnum=partition_poly->subnum; + + partition_poly->plane.used=1; + tree->plane=partition_poly->plane; + partition_plane=partition_poly->plane; + + frontlist = backlist = NULL; + + for (lnode=*polylist;lnode!=NULL;lnode=next) + { + next=lnode->next; + + bsppolygon *testpoly=(bsppolygon *)lnode->data; + int fate=ClassifyPolygon (&partition_plane,testpoly); + + if (fate==BSP_IN_FRONT) + { + AddListItem (&frontlist,testpoly); + numfront++; + } + else if (fate==BSP_BEHIND) + { + AddListItem (&backlist,testpoly); + numback++; + } + else if (fate==BSP_COINCIDENT) + { + // Test to see if this plane is exactly the same as the partition plane + // If so, remove it from the list. If its facing the other way, send it down the back list + float dot=testpoly->plane.a*partition_plane.a+testpoly->plane.b*partition_plane.b+testpoly->plane.c*partition_plane.c; + + if (dot>-BSP_EPSILON) + { + // Send down front list and deactivate this plane + AddListItem (&frontlist,testpoly); + numfront++; + //testpoly->plane.used=1; + } + else + { + AddListItem (&frontlist,testpoly); + //testpoly->plane.used=1; + numfront++; + } + } + else + { + // Must split this polygon + ASSERT (fate==BSP_SPANNING); + bsppolygon *frontpoly,*backpoly; + + numsplits++; + + SplitPolygon (&partition_plane,testpoly,&frontpoly,&backpoly); + + numfront++; numback++; + AddListItem (&frontlist,frontpoly); + AddListItem (&backlist,backpoly); + + RemoveListItem (polylist,testpoly); + FreePolygon (testpoly); + + } + } + + //mprintf ((0,"BuildBSPNode: Incoming=%d numfront=%d numback=%d splits=%d\n",numpolys,numfront,numback,numsplits)); + + if(frontlist != NULL) + { + if((frontnode = NewBSPNode()) == NULL) + { + mprintf((0,"BuildBSPNode: Error, can't allocate front node\n")); + return 0; + } + + if(!BuildBSPNode((bspnode *)frontnode, &frontlist,numfront)) + { + mprintf((0,"BuildBSPNode: Error building front node\n")); + return 0; + } + + tree->front = frontnode; + DestroyList(&frontlist); + } + else + { + if((frontnode = NewBSPNode()) == NULL) + { + mprintf((0,"BuildBSPNode: Error, can't allocate front node\n")); + return 0; + } + frontnode->type=BSP_EMPTY_LEAF; + tree->front = frontnode; + } + + if(backlist != NULL) + { + if((backnode = NewBSPNode()) == NULL) + { + mprintf((0,"BuildBSPNode: Error, can't allocate front node\n")); + return 0; + } + + if(!BuildBSPNode((bspnode *)backnode, &backlist,numback)) + { + mprintf((0,"BuildBSPNode: Error building back node\n")); + return 0; + } + + tree->back = backnode; + DestroyList(&backlist); + } + else + { + if((backnode = NewBSPNode()) == NULL) + { + mprintf((0,"BuildBSPNode: Error, can't allocate front node\n")); + return 0; + } + + backnode->type=BSP_SOLID_LEAF; + tree->back = backnode; + } + + return 1; + + +} + +// Releases memory for a bspnode. This function calls itself recursively +void DestroyBSPNode (bspnode *node) +{ + if (node->type==BSP_NODE) + { + if (node->front) + DestroyBSPNode (node->front); + if (node->back) + DestroyBSPNode (node->back); + } + + int np=CountListItems (&node->polylist); + + for (int i=0;ipolylist,i); + FreePolygon (poly); + } + if (np>0) + DestroyList (&node->polylist); + + mem_free (node); +} + +// Walks the BSP tree and frees up any nodes/polygons that we might be using +void DestroyBSPTree (bsptree *tree) +{ + if (BSP_initted==0) + return; + + mprintf ((0,"Destroying bsptree!\n")); + DestroyBSPNode (tree->root); + BSP_initted=0; +} + +// Destroys the mines BSP tree at shutdown +void DestroyDefaultBSPTree () +{ + DestroyBSPTree (&MineBSP); +} + +// Given an object, a submodel, and a vertex number, calculates the world position +// of that point +void GetObjectPointInWorld (vector *dest,object *obj,int subnum,int vertnum); + +// Builds a bsp tree for the indoor rooms +void BuildBSPTree () +{ + int i,t,k,j,x; + int numpolys=0; + int check; + + if (!UseBSP) + return; + + // Check to see if we even need to build a new tree + check=BSPGetMineChecksum(); + + if (check==BSPChecksum) + { + mprintf ((0,"BSP tree already built!\n")); + return; // The BSP tree has already been built for this mine + } + + BSPChecksum=check; + + // Free up any BSP trees that we might have lying around + InitDefaultBSP (); + + MineBSP.root=NewBSPNode (); + ASSERT (MineBSP.root); + + mprintf ((0,"Building BSP Tree...\n")); + + mprintf ((0,"Adding polygons to tree\n")); + + // Go through the whole mine and add each polygon to the possible BSP + // partition list. Don't include portals... + for (i=0;i<=Highest_room_index;i++) + { + bsppolygon *newpoly; + + if (Rooms[i].used==0 || (Rooms[i].flags & RF_EXTERNAL)) + continue; + + room *rp=&Rooms[i]; + + for (t=rp->objects;(t!=-1);t=Objects[t].next) + { + object *obj=&Objects[t]; + + if (obj->type==OBJ_NONE) + continue; + + if (OBJECT_OUTSIDE(obj)) + continue; + + if (obj->lighting_render_type==LRT_LIGHTMAPS) + { + poly_model *po=&Poly_models[obj->rtype.pobj_info.model_num]; + + if (!po->new_style) + continue; + + for (k=0;kn_models;k++) + { + bsp_info *sm=&po->submodel[k]; + + if (IsNonRenderableSubmodel (po,k)) + continue; + + for (j=0;jnum_faces;j++) + { + vector world_verts[64]; + + newpoly=NewPolygon (i,t,sm->faces[j].nverts); + if (!newpoly) + { + mprintf ((0,"Couldn't get a new polygon!\n")); + Int3(); + return; + } + + ASSERT (sm->faces[j].nverts<64); + for (x=0;xfaces[j].nverts;x++) + GetObjectPointInWorld (&world_verts[x],obj,k,sm->faces[j].vertnums[x]); + + vector norm; + + vm_GetNormal(&norm,&world_verts[0],&world_verts[1],&world_verts[2]); + + // Add the vertices to this poly + for (x=0;xfaces[j].nverts;x++) + newpoly->verts[x]=world_verts[x]; + + newpoly->plane.a=norm.x; + newpoly->plane.b=norm.y; + newpoly->plane.c=norm.z; + + CalculatePolygonPlane (newpoly); + + // Add it to our bsp list + AddListItem (&MineBSP.polylist,newpoly); + + newpoly->plane.used=0; + numpolys++; + + newpoly->roomnum=t; + newpoly->facenum=j; + newpoly->subnum=k; + + } + } + } + } + + for (t=0;t=0) + continue; + } + + // Construct a new polygon + newpoly=NewPolygon (i,t,Rooms[i].faces[t].num_verts); + if (!newpoly) + { + mprintf ((0,"Couldn't get a new polygon!\n")); + Int3(); + return; + } + + // Add the vertices to this poly + for (k=0;kverts[k]=Rooms[i].verts[Rooms[i].faces[t].face_verts[k]]; + + newpoly->plane.a=Rooms[i].faces[t].normal.x; + newpoly->plane.b=Rooms[i].faces[t].normal.y; + newpoly->plane.c=Rooms[i].faces[t].normal.z; + + CalculatePolygonPlane (newpoly); + + // Add it to our bsp list + AddListItem (&MineBSP.polylist,newpoly); + + newpoly->plane.used=0; + + newpoly->roomnum=i; + newpoly->facenum=t; + newpoly->subnum=-1; + + numpolys++; + } + } + + ConvexSubspaces=0; + ConvexPolys=0; + Solids=0; + Empty=0; + + mprintf ((0,"%d polygons added, starting node building...\n",numpolys)); + + // Build the BSP tree! + BuildBSPNode (MineBSP.root,&MineBSP.polylist,numpolys); + DestroyList (&MineBSP.polylist); + + // Print some stats + mprintf ((0,"Total number of convex subspaces=%d\n",ConvexSubspaces)); + mprintf ((0,"Total number of convex polys=%d\n",ConvexPolys)); + mprintf ((0,"Solid=%d Empty=%d\n",Solids,Empty)); + +} + +// Builds a bsp tree for a single room +void BuildSingleBSPTree (int roomnum) +{ + int i,t,k,j,x; + int numpolys=0; + + if (!UseBSP) + return; + + // Free up any BSP trees that we might have lying around + InitDefaultBSP (); + + MineBSP.root=NewBSPNode (); + ASSERT (MineBSP.root); + + mprintf ((0,"Building BSP Tree...\n")); + + // Go through the whole mine and add each polygon to the possible BSP + // partition list. Don't include portals... + + i=roomnum; + bsppolygon *newpoly; + + for (t=0;t<=Highest_object_index;t++) + { + object *obj=&Objects[t]; + + if (obj->type==OBJ_NONE) + continue; + + if (OBJECT_OUTSIDE(obj)) + continue; + if (obj->roomnum!=i) + continue; + + if (obj->lighting_render_type==LRT_LIGHTMAPS) + { + poly_model *po=&Poly_models[obj->rtype.pobj_info.model_num]; + if (!po->new_style) + continue; + + for (k=0;kn_models;k++) + { + bsp_info *sm=&po->submodel[k]; + + if (IsNonRenderableSubmodel (po,k)) + continue; + + for (j=0;jnum_faces;j++) + { + vector world_verts[64]; + newpoly=NewPolygon (i,t,sm->faces[j].nverts); + if (!newpoly) + { + mprintf ((0,"Couldn't get a new polygon!\n")); + Int3(); + return; + } + + for (x=0;xfaces[j].nverts;x++) + GetObjectPointInWorld (&world_verts[x],obj,k,sm->faces[j].vertnums[x]); + + vector norm; + + vm_GetNormal(&norm,&world_verts[0],&world_verts[1],&world_verts[2]); + + // Add the vertices to this poly + for (x=0;xfaces[j].nverts;x++) + newpoly->verts[x]=world_verts[x]; + + newpoly->plane.a=norm.x; + newpoly->plane.b=norm.y; + newpoly->plane.c=norm.z; + + CalculatePolygonPlane (newpoly); + + // Add it to our bsp list + AddListItem (&MineBSP.polylist,newpoly); + + newpoly->plane.used=0; + numpolys++; + } + } + } + } + + for (t=0;tverts[k]=Rooms[i].verts[Rooms[i].faces[t].face_verts[k]]; + + newpoly->plane.a=Rooms[i].faces[t].normal.x; + newpoly->plane.b=Rooms[i].faces[t].normal.y; + newpoly->plane.c=Rooms[i].faces[t].normal.z; + + CalculatePolygonPlane (newpoly); + + // Add it to our bsp list + AddListItem (&MineBSP.polylist,newpoly); + + newpoly->plane.used=0; + numpolys++; + } + + ConvexSubspaces=0; + ConvexPolys=0; + Solids=0; + Empty=0; + + // Build the BSP tree! + BuildBSPNode (MineBSP.root,&MineBSP.polylist,numpolys); + DestroyList (&MineBSP.polylist); + + // Print some stats + mprintf ((0,"Total number of convex subspaces=%d\n",ConvexSubspaces)); + mprintf ((0,"Total number of convex polys=%d\n",ConvexPolys)); + mprintf ((0,"Solid=%d Empty=%d\n",Solids,Empty)); + + BSPChecksum=-1; + +} + + + +// Returns true if the point is inside the min,max +inline int BSPInMinMax (vector *pos,vector *min_xyz,vector *max_xyz) +{ + if (pos->xx || pos->yy || pos->zz || + pos->x>max_xyz->x || pos->y>max_xyz->y || pos->z>max_xyz->z) + return 0; + + return 1; +} +//see if a point in inside a face by projecting into 2d +extern uint check_point_to_face(vector *colp, vector* face_normal,int nv,vector **vertex_ptr_list); + +// Returns true if passed in point collides with a nodes polygon +inline int BSPPointInPolygon (vector *pos,bspnode *node) +{ + if (node->node_subnum<0) // Room face + { + room *rp=&Rooms[node->node_roomnum]; + face *fp=&rp->faces[node->node_facenum]; + if (!(BSPInMinMax (pos,&fp->min_xyz,&fp->max_xyz))) + return 0; + + // Test to see if this point in inside the polygon + vector verts[MAX_VERTS_PER_FACE],*vertp[MAX_VERTS_PER_FACE]; + for (int i=0;inum_verts;i++) + { + verts[i]=rp->verts[fp->face_verts[i]]; + vertp[i]=&verts[i]; + } + + if ((check_point_to_face(pos, &fp->normal,fp->num_verts,vertp))) + return 0; + + return 1; + } + else // Object face + { + object *obj=&Objects[node->node_roomnum]; + + if (!(BSPInMinMax(pos,&obj->min_xyz,&obj->max_xyz))) + return 0; + + // Test to see if this point in inside the polygon + vector verts[MAX_VERTS_PER_FACE],*vertp[MAX_VERTS_PER_FACE]; + vector norm; + poly_model *po=&Poly_models[obj->rtype.pobj_info.model_num]; + bsp_info *sm=&po->submodel[node->node_subnum]; + + for (int i=0;ifaces[node->node_facenum].nverts;i++) + { + GetObjectPointInWorld (&verts[i],obj,node->node_subnum,sm->faces[node->node_facenum].vertnums[i]); + vertp[i]=&verts[i]; + } + + norm.x=node->plane.a; + norm.y=node->plane.b; + norm.z=node->plane.c; + + if ((check_point_to_face(pos, &norm,sm->faces[node->node_facenum].nverts,vertp))) + return 0; + + return 1; + } + + return 0; +} + + +// Runs a ray through the bsp tree +// Returns true if a ray is occludes +#define MAX_RAY_STACK 1000 +#define PUSH_BSP_RAY(s,e,n) {stack_start[si]=s;stack_end[si]=e;stack_node[si]=n; si++; ASSERT(si0) + { + POP_BSP_RAY(); + while (node->type==BSP_NODE) + { + float dist1 = node->plane.a*start.x+node->plane.b*start.y+node->plane.c*start.z+node->plane.d; + float dist2 = node->plane.a*end.x+node->plane.b*end.y+node->plane.c*end.z+node->plane.d; + + if (dist1>=0 && dist2>=0) + { + node=(bspnode *)node->front; + } + else if (dist1<0 && dist2<0) + { + node=(bspnode *)node->back; + } + else + { + vector mid,delta; + float t; + + // Generate split point + t = dist1 / (dist1 - dist2); + delta=end-start; + mid=start+(t*delta); + if (BSPPointInPolygon (&mid,node)) + return 1; + + //vm_NormalizeVectorFast (&delta); + + vector mid1=mid;//-(delta*BSP_EPSILON); + vector mid2=mid;//+(delta*BSP_EPSILON); + + if (dist1 >= 0.0) + { + PUSH_BSP_RAY (mid1,end,(bspnode *)node->back); + PUSH_BSP_RAY (start,mid2,(bspnode *)node->front); + } + else + { + PUSH_BSP_RAY (mid2,end,(bspnode *)node->front); + PUSH_BSP_RAY (start,mid1,(bspnode *)node->back); + } + + goto break_out; + } + } + break_out:; + } + + return 0; +} + +// Initializes some variables for the indoor bsp tree +void InitDefaultBSP () +{ + if (BSP_initted) + { + DestroyBSPTree (&MineBSP); + } + else + { + atexit (DestroyDefaultBSPTree); + } + + MineBSP.polylist=NULL; + MineBSP.vertlist=NULL; + + BSP_initted=1; +} diff --git a/Descent3/bsp.h b/Descent3/bsp.h new file mode 100644 index 000000000..fc625935b --- /dev/null +++ b/Descent3/bsp.h @@ -0,0 +1,121 @@ +/* + * $Logfile: /DescentIII/Main/bsp.h $ + * $Revision: 13 $ + * $Date: 4/19/00 5:30p $ + * $Author: Matt $ + * + * Header for bsp.cpp + * + * $Log: /DescentIII/Main/bsp.h $ + * + * 13 4/19/00 5:30p Matt + * From Duane for 1.4 + * Added extern + * + * 12 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 11 9/22/98 12:01p Matt + * Added SourceSafe headers + * + */ + + +#ifndef BSP_H +#define BSP_H + +#include "list.h" +#include "vecmat.h" +#include "CFILE.H" + +#define BSP_IN_FRONT 1 +#define BSP_BEHIND 2 +#define BSP_ON_PLANE 3 +#define BSP_SPANNING 4 +#define BSP_COINCIDENT 5 + +#define BSP_EPSILON .00005f + +#define BSP_NODE 0 +#define BSP_EMPTY_LEAF 1 +#define BSP_SOLID_LEAF 2 + +typedef struct +{ + float a,b,c,d; + ubyte used; +} bspplane; + +typedef struct +{ + vector *verts; + int nv; + bspplane plane; + + short roomnum; + short facenum; + sbyte subnum; + + int color; + +} bsppolygon; + +typedef struct bspnode +{ + ubyte type; + bspplane plane; + ushort node_facenum; + ushort node_roomnum; + sbyte node_subnum; + + bspnode *front; + bspnode *back; + + list *polylist; + int num_polys; +} bspnode; + +typedef struct bsptree +{ + list *vertlist; + list *polylist; + bspnode *root; +} bsptree; + +// Builds a bsp tree for the indoor rooms +void BuildBSPTree (); + +// Runs a ray through the bsp tree +// Returns true if a ray is occludes +int BSPRayOccluded(vector *start, vector *end, bspnode *node); +int BSPReportStatus(vector *start, bspnode *node); + +// Walks the BSP tree and frees up any nodes/polygons that we might be using +void DestroyBSPTree (bsptree *tree); + +// Saves and BSP node to an open file and recurses with the nodes children +void SaveBSPNode (CFILE *outfile,bspnode *node); + +// Loads a bsp node from an open file and recurses with its children +void LoadBSPNode (CFILE *infile,bspnode **node); + +// Initializes some variables for the indoor bsp tree +void InitDefaultBSP (); + +// Destroy indoor bsp tree +void DestroyDefaultBSPTree (); + +// Builds a bsp tree for a single room +void BuildSingleBSPTree (int roomnum); + +// Reports the current mine's checksum +int BSPGetMineChecksum(); + +extern bsptree MineBSP; +extern int BSPChecksum; +extern ubyte BSP_initted; +extern ubyte UseBSP; + +#endif + + diff --git a/Descent3/buddymenu.cpp b/Descent3/buddymenu.cpp new file mode 100644 index 000000000..fd3cc99dc --- /dev/null +++ b/Descent3/buddymenu.cpp @@ -0,0 +1,503 @@ +/* +* $Logfile: /DescentIII/main/buddymenu.cpp $ +* $Revision: 31 $ +* $Date: 9/05/01 12:26p $ +* $Author: Matt $ +* +* Guidebot menu system. +* +* $Log: /DescentIII/main/buddymenu.cpp $ + * + * 31 9/05/01 12:26p Matt + * Flush low-level mouse click queue before bringing up buddy, save, and + * load menus since these menus would otherwise get clicks that occurred + * before they existed. + * + * 30 4/30/99 7:49p Jason + * setups urgent/non urgent reliable stuff better + * + * 29 4/30/99 1:29p Samir + * fixed buddy menu a-z selections. + * + * 28 4/27/99 1:56p Jeff + * audio taunts stuff in pilot menu, added stringtables + * + * 27 4/27/99 4:41a Jeff + * made multiplayer friendly + * + * 26 4/24/99 5:41p Samir + * keys should work properly for buddy menu. + * + * 25 4/22/99 10:25p Chris + * Added a comment block +* +* $NoKeywords: $ +*/ + +#include "help.h" +#include "mono.h" +#include "renderer.h" +#include "render.h" +#include "ddio.h" +#include "descent.h" +#include "game.h" +#include "CFILE.H" +#include "application.h" +#include +#include +#include +#include "newui.h" +#include "grtext.h" +#include "gamefont.h" +#include "AIMain.h" +#include "robot.h" +#include "hud.h" +#include "stringtable.h" +#include "textaux.h" +#include "multi.h" + +#include "osiris_share.h" +#include "multi.h" + +#define GB_MENU_REQ_TEXT 1 +#define GB_MENU_REQ_SELECT 2 + +#define IDH_QUIT 50 +#define WND_HELP_W 320 +#define WND_HELP_H 384 +#define WND_HELP_X (Game_window_w - WND_HELP_W)/2 +#define WND_HELP_Y (Game_window_h - WND_HELP_H)/2 +#define BUDDYMENU_ITEM_X 10 +#define BUDDYMENU_ITEM_W 226 +#define UID_BUDDYCMD 0x100 + +gb_menu Guidebot_menu_data; +int Guidebot_data_download_status; + +// downloads data for the buddybot +// on return: if 1, process data +// if 0, abort +// if -1, no guidebot +int BuddyBotDownloadData(void); +void BuddyProcessCommand(int res); + +void BuddyDisplay(void) +{ + newuiTiledWindow gbcommand_wnd; + newuiSheet *sheet; + bool exit_menu = false; + int strs_to_print, index, height; + + if (Game_mode & GM_MULTI && (!(Netgame.flags&NF_ALLOWGUIDEBOT)) ) { + //only in single player games, and multiplayer games that allow guidebot + return; + } + + strs_to_print = 0; + + + if(BuddyBotDownloadData()!=1) + return; + + gbcommand_wnd.Create(Guidebot_menu_data.title, 0, 0, WND_HELP_W, WND_HELP_H); + sheet = gbcommand_wnd.GetSheet(); + + // add commands + sheet->NewGroup(NULL, 10,10); + gbcommand_wnd.AddAcceleratorKey(KEY_F4, UID_CANCEL); + + for(index=0;indexAddHotspot(buf,BUDDYMENU_ITEM_W, height, index+UID_BUDDYCMD, (index==0||index==(Guidebot_menu_data.num_commands-1)) ? true : false); + if (index < (N_WINDOW_ACCELS-1)) { + if (index < 9) { + int key = ddio_AsciiToKey('1'+index); + gbcommand_wnd.AddAcceleratorKey(key, index+UID_BUDDYCMD); + } + else { + int key = ddio_AsciiToKey('a'+(index-9)); + gbcommand_wnd.AddAcceleratorKey(key, index+UID_BUDDYCMD); + } + } + } + + // Double space + sheet->NewGroup(NULL, WND_HELP_W-240,WND_HELP_H-96, NEWUI_ALIGN_HORIZ); + sheet->AddLongButton(TXT_PRESSESCRET, UID_CANCEL); + gbcommand_wnd.AddAcceleratorKey(KEY_ESC, UID_OK); + + + gbcommand_wnd.Open(); + + //Mouse clicks from gameplay will be read by the dialog without this flush + ddio_MouseQueueFlush(); + + while (!exit_menu) + { + int res = gbcommand_wnd.DoUI(); + + // handle all UI results. + switch (res) + { + case UID_OK: + case UID_CANCEL: + //TODO: Save menu options to variables + exit_menu = true; + break; + case NEWUIRES_FORCEQUIT: + exit_menu = true; + break; + default: + BuddyProcessCommand(res); + exit_menu = true; + break; + } + } + + gbcommand_wnd.Close(); + gbcommand_wnd.Destroy(); +} + + +void MultiAskforGuidebotMenu() +{ + int size_offset; + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + //Only process this if we are the client + if (Netgame.local_role!=LR_CLIENT) + { + Int3();//We shouldn't be here... get Kevin + return; + } + size_offset=START_DATA (MP_GUIDEBOTMENU_REQUEST,data,&count); + MultiAddByte(GB_MENU_REQ_TEXT,data,&count); + END_DATA (count,data,size_offset); + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + + +void MultiStuffGuidebotMenuData(ubyte *data,int *count,gb_menu *menu) +{ + int i; + //Get the menu data and stick it here + //length of the title + int slen = strlen(menu->title)+1; + MultiAddShort(slen,data,count); + memcpy(data+*count,menu->title,slen); + *count += slen; + + MultiAddShort(menu->num_commands,data,count); + for(i=0;inum_commands;i++) + { + slen = strlen(menu->command_text[i])+1; + MultiAddShort(slen,data,count); + memcpy(data+*count,menu->command_text[i],slen); + *count += slen; + + MultiAddByte(menu->command_id[i],data,count); + + MultiAddByte(menu->command_type[i],data,count); + + slen = strlen(menu->dialog_text[i])+1; + MultiAddShort(slen,data,count); + memcpy(data+*count,menu->dialog_text[i],slen); + *count += slen; + } +} + +//TODO -- Call this function when the client selects a menu item +void MultiSendGuidebotMenuSelection(gb_com *command) +{ + int size_offset; + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + + //Only process this if we are the client + if (Netgame.local_role!=LR_CLIENT) + { + Int3();//We shouldn't be here... get Kevin + return; + } + + size_offset=START_DATA (MP_GUIDEBOTMENU_REQUEST,data,&count); + MultiAddByte(GB_MENU_REQ_SELECT,data,&count); + + MultiAddInt(command->action,data,&count); + MultiAddInt(command->index,data,&count); + + if(command->ptr) + { + int len = strlen((char *)command->ptr) + 1; + MultiAddShort(len,data,&count); + memcpy(data+count,command->ptr,len); + count += len; + }else + { + MultiAddShort(0,data,&count); + } + + + END_DATA (count,data,size_offset); + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + + + +void MultiSendGuidebotMenuText(gb_menu *menu, int slot) +{ + int size_offset; + int count=0; + + ubyte data[MAX_GAME_DATA_SIZE]; + //Only process this if we are the server + if (Netgame.local_role!=LR_SERVER) + { + Int3();//We shouldn't be here... get Kevin + return; + } + size_offset=START_DATA (MP_GUIDEBOTMENU_DATA,data,&count); + + MultiStuffGuidebotMenuData(data,&count,menu); + + END_DATA (count,data,size_offset); + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count,false); +} + + + +void MultiReadGuidebotMenuData(ubyte *data,int *count,gb_menu *menu) +{ + + int i; + //Get the menu data and stick it here + //length of the title + int slen = MultiGetShort(data,count); + memcpy(menu->title,data+*count,slen); + *count += slen; + + menu->num_commands = MultiGetShort(data,count); + + for(i=0;inum_commands;i++) + { + slen = MultiGetShort(data,count); + memcpy(menu->command_text[i],data+*count,slen); + *count += slen; + + menu->command_id[i] = MultiGetByte(data,count); + + menu->command_type[i] = MultiGetByte(data,count); + + slen = MultiGetShort(data,count); + memcpy(menu->dialog_text[i],data+*count,slen); + *count += slen; + } + +} + + +void MultiDoGuidebotMenuRequest(ubyte *data,int slot) +{ + + int count = 0; + SKIP_HEADER (data,&count); + //Only process this if we are the server + if (Netgame.local_role==LR_CLIENT) + { + Int3();//We shouldn't be here... get Kevin + return; + } + + int command = MultiGetByte(data,&count); + + switch(command) + { + case GB_MENU_REQ_TEXT: + { + //Client wants text to put in his menu + //Generate a menu struct and send it off + object *b_obj = ObjGet(Buddy_handle[slot]); + ASSERT(b_obj); + + gb_com command; + gb_menu menu; + + command.action = COM_GET_MENU; + command.ptr = (void *) &menu; + + AINotify(b_obj, AIN_USER_DEFINED, (void *)&command); + + MultiSendGuidebotMenuText(&menu,slot); + } + break; + case GB_MENU_REQ_SELECT: + { + gb_com command; + object *b_obj = ObjGet(Buddy_handle[slot]); + ASSERT(b_obj); + + command.action = MultiGetInt(data,&count); + command.index = MultiGetInt(data,&count); + + int len = MultiGetShort(data,&count); + + if(len>0) + { + command.ptr = data+count; + count += len; + }else + { + command.ptr = NULL; + } + + AINotify(b_obj, AIN_USER_DEFINED, (void *)&command); + } + break; + default: + Int3();//We shouldn't be here either...get Kevin + } +} + + + +void MultiDoGuidebotMenuData(ubyte *data) +{ + int count = 0; + SKIP_HEADER (data,&count); + if (Netgame.local_role!=LR_CLIENT) + { + Int3(); + return; + } + //Server is telling us about the menu -- we should display it now + MultiReadGuidebotMenuData(data,&count,&Guidebot_menu_data); + + Guidebot_data_download_status = 1; + Multi_bail_ui_menu = true; + mprintf((0,"Recieved Guidebot data from server\n")); +} + +// downloads data for the buddybot +// on return: if 1, process data +// if 0, abort +// if -1, no guidebot +int BuddyBotDownloadData(void) +{ + if( (!(Game_mode&GM_MULTI)) || (Netgame.local_role!=LR_CLIENT) ) + { + //no need to download data, we already have access to it + //fill in Guidebot_menu_data and leave + object *b_obj = ObjGet(Buddy_handle[Player_object->id]); + ASSERT(b_obj); + + gb_com command; + command.action = COM_GET_MENU; + command.ptr = (void *) &Guidebot_menu_data; + + AINotify(b_obj, AIN_USER_DEFINED, (void *)&command); + + if(Guidebot_menu_data.num_commands == 0) + { + AddHUDMessage(TXT_NOGUIDEBOT); + return -1; + } + + return 1; + } + + //we are a client in a multiplayer game, we must request from the server + Guidebot_data_download_status = 0; //processesing + MultiAskforGuidebotMenu(); + + //now we must 'process' until Guidebot_data_download_status no longer is 0 + newuiTiledWindow hwnd; + newuiSheet *sheet; + hwnd.Create(TXT_GUIDEBOT, 0, 0, 288, 128); + sheet = hwnd.GetSheet(); + sheet->NewGroup(NULL, 5,10); + sheet->AddText(TXT_ACCESSGUIDEBOT); + sheet->NewGroup(NULL, 40,30); + sheet->AddText(TXT_ESCTOCANCEL); + hwnd.AddAcceleratorKey(KEY_ESC, UID_CANCEL); + + hwnd.Open(); + while(Guidebot_data_download_status==0) + { + int res = hwnd.DoUI(); + + switch (res) + { + case UID_OK: + case UID_CANCEL: + case NEWUIRES_FORCEQUIT: + if(Guidebot_data_download_status==1) + { + //we recieved data + Multi_bail_ui_menu = false; + }else + { + Guidebot_data_download_status = -1; + } + break; + default: + break; + } + } + hwnd.Close(); + hwnd.Destroy(); + + if(Guidebot_data_download_status==1) + { + if(Guidebot_menu_data.num_commands==0) + { + AddHUDMessage(TXT_NOGUIDEBOT); + return -1; + } + return 1; + } + return 0; +} + +void BuddyProcessCommand(int res) +{ + gb_com command; + char answer[8]; + + int index = res - UID_BUDDYCMD; + + command.action = COM_DO_ACTION; + command.index = Guidebot_menu_data.command_id[index]; + if(Guidebot_menu_data.command_type[index] == GBCT_STRING_DIALOG) + { + answer[0] = '\0'; + + DoEditDialog(Guidebot_menu_data.dialog_text[index], answer, sizeof(answer)-1); + command.ptr = (void *) answer; + }else + { + command.ptr = NULL; + } + + if( (!(Game_mode&GM_MULTI)) || (Netgame.local_role!=LR_CLIENT) ) + { + object *b_obj = ObjGet(Buddy_handle[Player_object->id]); + ASSERT(b_obj); + + AINotify(b_obj, AIN_USER_DEFINED, (void *)&command); + }else + { + MultiSendGuidebotMenuSelection(&command); + } +} diff --git a/Descent3/buddymenu.h b/Descent3/buddymenu.h new file mode 100644 index 000000000..11abbd9e9 --- /dev/null +++ b/Descent3/buddymenu.h @@ -0,0 +1,7 @@ +#ifndef __BUDDYMENU_H_ +#define __BUDDYMENU_H_ + +void BuddyDisplay(void); + + +#endif \ No newline at end of file diff --git a/Descent3/buildno.h b/Descent3/buildno.h new file mode 100644 index 000000000..25961ee5d --- /dev/null +++ b/Descent3/buildno.h @@ -0,0 +1,3 @@ +//Descent 3 release build number definition. This file is generated by code, so don't edit it!!!! + +#define D3_RELEASE_BUILD_NO 604 \ No newline at end of file diff --git a/Descent3/cinematics.cpp b/Descent3/cinematics.cpp new file mode 100644 index 000000000..52e0e6f42 --- /dev/null +++ b/Descent3/cinematics.cpp @@ -0,0 +1,209 @@ +#include "cinematics.h" +#include "movie.h" +#include "game.h" +#include "ddio.h" +#include "descent.h" +#include "gamefont.h" +#include "hlsoundlib.h" +#include "subtitles.h" +#include "dedicated_server.h" +#include "appdatabase.h" +#include "bitmap.h" + +#include +#include + +#ifdef DEBUG +static struct +{ + float last_frame_time; + float frame_interval_time; + float fps; +}MovieCallbackData; +#endif + +static bool Cinematic_lib_init = false; +static int Cinematic_x = 0, Cinematic_y = 0, Cinematic_w = 0, Cinematic_h = 0; + +void CinematicCallback( int mve_x, int mve_y, int frame_num ); + + +/////////////////////////////////////////////////////////////////////////////// +// functions + +bool InitCinematics() +{ + if (Dedicated_server) + { + Cinematic_lib_init = false; + return true; + } + + char path[_MAX_PATH]; + ddio_MakePath(path, Base_directory, "movies", NULL); + + if( mve_Init(path, Sound_card_name) != MVELIB_NOERROR ) + return false; + + mve_SetCallback( CinematicCallback ); + + Cinematic_lib_init = true; + + return true; +} + +void SetMovieProperties(int x, int y, int w, int h, renderer_type type) +{ + const bool kHiColorEnabled = true; + Cinematic_x = x; + Cinematic_y = y; + Cinematic_w = w; + Cinematic_h = h; + mve_SetRenderProperties( x, y, w, h, type, kHiColorEnabled ); +} + +bool PlayMovie(const char *moviename) +{ + if( !Cinematic_lib_init ) + return false; + + // get in the right directory + char filename[PSPATHNAME_LEN]; + strncpy( filename, moviename, sizeof(filename) - 1 ); + filename[ sizeof(filename) - 1 ] = 0; + + // check extension + const char* extension = strrchr( filename, '.' ); + if( extension == NULL || ( stricmp( extension, ".mve" ) != 0 && stricmp( extension, ".mv8" ) != 0 ) ) + { + // we need an extension + strncat( filename, ".mve", sizeof( filename ) - 1 ); + filename[ sizeof(filename) - 1 ] = 0; + } + + // shutdown sound. + bool sound_sys_active = Sound_system.IsActive(); + if (sound_sys_active) + { + Sound_system.KillSoundLib( false ); + } + + // start movie. +#ifdef DEBUG + MovieCallbackData.last_frame_time = timer_GetTime(); + MovieCallbackData.frame_interval_time = 1.0f; + MovieCallbackData.fps = 0.0f; +#endif + + // Initializes the subtitles for a given movie file + SubtInitSubtitles( moviename ); + + SetMovieProperties( 0, 0, Max_window_w, Max_window_h, Renderer_type ); + + int mveerr = mve_PlayMovie( filename, Descent ); + + // Shutdown the subtitle system + SubtCloseSubtitles(); + + bool retval = true; + if( mveerr != MVELIB_NOERROR ) + { + mprintf((1, "Movie error %d.\n", mveerr)); + retval = false; + } + + // startup D3 sound. + if( sound_sys_active ) + { + Sound_system.InitSoundLib( Descent, Sound_mixer, Sound_quality, false ); + } + + return retval; +} + +tCinematic *StartMovie( const char *moviename, bool looping ) +{ + if( !Cinematic_lib_init ) + return NULL; + + // get in the right directory + char filename[_MAX_PATH]; + strncpy( filename, moviename, sizeof(filename) - 1 ); + filename[ sizeof(filename) - 1 ] = 0; + + // check extension + const char* extension = strrchr( filename, '.' ); + if( extension == NULL || ( stricmp( extension, ".mve" ) != 0 && stricmp( extension, ".mv8" ) != 0 ) ) + { + // we need an extension + strncat( filename, ".mve", sizeof( filename ) - 1 ); + filename[ sizeof(filename) - 1 ] = 0; + } + + SetMovieProperties( 0, 0, Max_window_w, Max_window_h, Renderer_type ); + + int filehandle; + unsigned int hMovie = mve_SequenceStart( filename, &filehandle, Descent, looping ); + if( hMovie == 0 ) + return NULL; + + tCinematic *mve = new tCinematic; + mve->mvehandle = hMovie; + mve->filehandle = filehandle; + mve->frame_chunk.bm_array = NULL; + return mve; +} + +bool FrameMovie( tCinematic *mve, int x, int y, bool sequence ) +{ + if( !Cinematic_lib_init || mve == NULL ) + return false; + + int bm_handle; + unsigned int hNewMovie = mve_SequenceFrame( mve->mvehandle, mve->filehandle, sequence, &bm_handle ); + if( hNewMovie != (unsigned int)(-1) ) + { + // we must have looped, we have a new handle + mve->mvehandle = hNewMovie; + } + + // do chunk conversion and blt if absolutely necessary. + if( bm_handle > -1) + { + if (x == -1) + { + x = ( Cinematic_w - bm_w(bm_handle,0) ) / 2; + } + + if (y == -1) + { + y = ( Cinematic_h - bm_h(bm_handle,0) ) / 2; + } + + rend_SetAlphaType(AT_ALWAYS); + rend_CopyBitmapToFramebuffer( bm_handle, x, y ); + } + + return ( hNewMovie != (unsigned int)(-1) ); +} + + +void EndMovie( tCinematic *mve ) +{ + if( mve == NULL || !Cinematic_lib_init ) + return; + + mve_SequenceClose( mve->mvehandle, mve->filehandle ); + + if( mve->frame_chunk.bm_array ) + { + bm_DestroyChunkedBitmap( &mve->frame_chunk ); + } + + delete mve; +} + +void CinematicCallback( int mveX, int mveY, int frameNum ) +{ + SubtDrawSubtitles( frameNum ); +} diff --git a/Descent3/cinematics.h b/Descent3/cinematics.h new file mode 100644 index 000000000..80954751e --- /dev/null +++ b/Descent3/cinematics.h @@ -0,0 +1,55 @@ +/* + * $Logfile: /DescentIII/main/cinematics.h $ + * $Revision: 7 $ + * $Date: 3/31/99 11:53a $ + * $Author: Samir $ + * + * D3 Cinematics interface with movie player + * + * $Log: /DescentIII/main/cinematics.h $ + * + * 7 3/31/99 11:53a Samir + * slow opengl code for movies. + * + * 6 3/17/99 4:18p Samir + * redid movie code. + * + * 5 2/04/98 12:12p Samir + * The movie frame interface now has an option to not advance to next + * frame. + * + * 4 2/03/98 6:42p Samir + * Movies in 3dfx mode. + * + * 3 9/19/97 2:34p Samir + * Added movie frame management support. + * + * 2 9/12/97 4:12p Samir + * first movie library + * + * $NoKeywords: $ + */ + + +#ifndef CINEMATICS_H +#define CINEMATICS_H + +#include "renderer.h" +#include "bitmap.h" + +// Movie Cinematic +typedef struct tCinematic +{ + unsigned mvehandle; + int filehandle; + chunked_bitmap frame_chunk; // used internally, don't access. +}tCinematic; + +bool InitCinematics(); +void SetMovieProperties(int x, int y, int w, int h, renderer_type type); +bool PlayMovie(const char *moviename); +tCinematic *StartMovie(const char *moviename, bool looping=false); +bool FrameMovie(tCinematic *mve, int x, int y, bool sequence=true); +void EndMovie(tCinematic *mve); + +#endif \ No newline at end of file diff --git a/Descent3/cockpit.cpp b/Descent3/cockpit.cpp new file mode 100644 index 000000000..edf5f1ea9 --- /dev/null +++ b/Descent3/cockpit.cpp @@ -0,0 +1,629 @@ +/* + * $Logfile: /DescentIII/main/cockpit.cpp $ + * $Revision: 50 $ + * $Date: 8/10/99 5:10p $ + * $Author: Jeff $ + * + * Cockpit code + * + * $Log: /DescentIII/main/cockpit.cpp $ + * + * 50 8/10/99 5:10p Jeff + * added missing call to FreeReticle() + * + * 49 7/28/99 4:21p Kevin + * Mac! + * + * 48 5/10/99 10:17p Ardussi + * changes to compile on Mac + * + * 47 5/05/99 5:48p Samir + * initialize reticle in InitCockpit. + * + * 46 4/21/99 2:15p Samir + * table file filter fixes. + * + * 45 4/17/99 5:43a Jeff + * Changes to get it to compile in Linux + * + * 44 4/06/99 1:50p Jason + * added new mass driver effect + * + * 43 4/01/99 4:50p Matt + * Took out Warning() function, chaning those calls to mprintf() + * + * 42 3/30/99 2:39p Samir + * added custom config file for fullscreen hud and hacked mass driver + * reticle in special reticle code. + * + * 41 3/23/99 10:25a Samir + * shake cockpit a little more. + * + * 40 2/11/99 8:28p Matt + * Fixed stupid bugs + * + * 39 2/11/99 4:02p Matt + * Flaged a bunch of files for TableParse + * + * 38 2/05/99 7:03p Jeff + * table file parsing macros put in + * + * 37 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 36 10/20/98 12:42p Matt + * Made the small views work on the cockpit. + * + * 35 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 34 9/03/98 4:26p Samir + * gauges are off this time until cockpit is in functional mode. + * + * 33 8/28/98 11:54a Samir + * turn on monitors only if the cockpit is on. + * + * 32 8/19/98 5:47p Samir + * increased cockpit spee.d + * + * 31 8/18/98 1:11a Samir + * fixed some stuff for multi cockpit configs. + * + * 30 7/28/98 5:39p Samir + * basic cockpit sounds. need work on sync. + * + * 29 6/24/98 1:11p Samir + * dont draw monitors + * + * 28 6/19/98 4:14p Jason + * made multicolor lighting work with volume lights + * + * 27 6/15/98 4:00p Jason + * replaced monochromatic polymodel lighting with rgb lighting + * + * 26 6/12/98 5:13p Jason + * made cockpit work with dynamic lighting system + * + * 25 5/26/98 10:47p Samir + * cockpit shake a little better. + * + * 24 5/26/98 5:05p Samir + * cockpit and hud config file now 'unified'. cockpit adds to hudconfig + * load's funtionality, so cockpit info files can contain same syntax as + * hud file. + * + * 23 5/22/98 6:26p Samir + * cockpit lighting primatives. + * + * 22 5/18/98 5:29p Samir + * don't move cockpit while moving. + * + * 21 5/18/98 4:52p Samir + * refined cockpit motion. + * + * 20 5/15/98 12:01p Samir + * cockpit buffet more jarring. fixed red crap bug. + * + * 19 5/14/98 7:40p Samir + * shaking cockpit. + * + * 18 5/14/98 12:07p Sean + * Removed an annoying mprintf + * + * 17 5/13/98 4:01p Samir + * cockpit closing and opening secure. + * + * 16 5/05/98 6:27p Samir + * function that tells the cockpit system that it was resized + * + * 15 5/01/98 4:24p Samir + * added function to quickly open the cockpit. + * + * 14 4/24/98 8:02a Samir + * simpler cockpit animation. + * + * 13 4/23/98 4:13a Samir + * new hud system. + * + * 12 4/06/98 7:35p Samir + * added glare over monitor effect. + * + * 11 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 10 3/27/98 11:59a Samir + * moved weapon hud text from hud to weapon monitor gauges. + * + * 9 3/25/98 11:59a Samir + * implemented energy analog fully. + * + * 8 3/24/98 4:28p Samir + * fixed bug when calling OpenCockpit immediately after calling + * CloseCockpit. + * + * 7 3/23/98 11:14a Samir + * maybe better cockpit lighting. + * + * 6 3/22/98 5:03p Samir + * updated cockpit. + * + * 5 3/20/98 8:23p Samir + * new hud and cockpit customization system. + * + * 4 3/18/98 6:24p Samir + * Better view eye positioning for cockpit. + * + * 3 3/17/98 2:40p Samir + * reorg of cockpit/gauges and hud systems. + * + * 2 3/16/98 3:30p Samir + * incremental checkin. + * + * 1 3/13/98 11:22a Samir + * initial revision. + * + * $NoKeywords: $ + */ +// D3 Cockpit system +// needs a model with textures. +// major functions: +// activate cockpit +// render cockpit +// deactivate cockpit. +#include "cockpit.h" +#include "game.h" +#include "polymodel.h" +#include "hud.h" +#include "gauges.h" +#include "ship.h" +#include "player.h" +#include "room.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "sounds.h" +#ifdef __LINUX__ +#define min(a,b) ((a +#define COCKPIT_ANIM_TIME 2.0f +#define COCKPIT_DORMANT_FRAME 0.0 +#define COCKPIT_START_FRAME 3.0 +#define COCKPIT_MIDWAY_FRAME 7.0 +#define COCKPIT_COMPLETE_FRAME ((float)Cockpit_info.model->frame_max) +// cockpit FX modifiers +#define MAX_BUFFET_STRENGTH 0.75f +#define BUFFET_PERIOD 0.25f +#define COCKPIT_SHIFT_DELTA 0.02f +typedef struct tCockpitCfgInfo +{ + char modelname[PSFILENAME_LEN]; + char shieldrings[NUM_SHIELD_GAUGE_FRAMES][PSFILENAME_LEN]; + char shipimg[PSFILENAME_LEN]; + char burnimg[PSFILENAME_LEN]; + char energyimg[PSFILENAME_LEN]; + char invpulseimg[PSFILENAME_LEN]; +} +tCockpitCfgInfo; +typedef struct tCockpitInfo +{ + int state; // current state of cockpit on screen. + int ship_index; // index into ships page. + int model_num; // this should be retreived from the ship info. + int snd_open, snd_close; // sound open and close. + float frame_time; // current time of animation. + float next_keyframe, this_keyframe; // current animation of cockpit. + poly_model *model; // model of cockpit. + unsigned nonlayered_mask; // non layered submodel mask + unsigned layered_mask; // layered submodel mask + bool animating; // is cockpit moving? + bool resized; // if cockpit is being resized.... + vector buffet_vec; // buffet direction + float buffet_amp; // used to calculate real magnitude + float buffet_wave; // current sin angle of buffet + float buffet_time; // current position in buffet wave along time axis. + + matrix orient; // orientation of cockpit +} +tCockpitInfo; +static tCockpitInfo Cockpit_info; +float KeyframeAnimateCockpit(); +// loads cockpit. model_name = NULL, then will not load in model name. +void LoadCockpitInfo(const char *ckt_file, tCockpitCfgInfo *info); +////////////////////////////////////////////////////////////////////////////// +// Initializes the cockpit by loading it in and initializing all it's gauges. +// initialization of cockpit. +void InitCockpit(int ship_index) +{ + extern void FreeReticle(); + tCockpitCfgInfo cfginfo; + int i; + mprintf((0, "Initializing cockpit.\n")); + LoadCockpitInfo(Ships[ship_index].cockpit_name, &cfginfo); +// initialize special hud/cockpit images unique to this ship + for (i = 0; i < NUM_SHIELD_GAUGE_FRAMES; i++) + { + if (cfginfo.shieldrings[i][0]) + HUD_resources.shield_bmp[i] = bm_AllocLoadFileBitmap(IGNORE_TABLE(cfginfo.shieldrings[i]), 0); + } + HUD_resources.ship_bmp = bm_AllocLoadFileBitmap(IGNORE_TABLE(cfginfo.shipimg), 0); + HUD_resources.energy_bmp = bm_AllocLoadFileBitmap(IGNORE_TABLE(cfginfo.energyimg), 0); + HUD_resources.afterburn_bmp = bm_AllocLoadFileBitmap(IGNORE_TABLE(cfginfo.burnimg), 0); + HUD_resources.invpulse_bmp = bm_AllocLoadFileBitmap(IGNORE_TABLE(cfginfo.invpulseimg), 0); + if (cfginfo.modelname[0] == 0) { + Cockpit_info.model_num = -1; + mprintf((0,"No cockpit found for ship.\n")); + return; + } +// initialize cockpit. + Cockpit_info.ship_index = ship_index; + Cockpit_info.model_num = LoadPolyModel(IGNORE_TABLE(cfginfo.modelname),0); + Cockpit_info.model = GetPolymodelPointer (Cockpit_info.model_num); + Cockpit_info.frame_time = 0.0f; + Cockpit_info.state = COCKPIT_STATE_DORMANT; + Cockpit_info.this_keyframe = COCKPIT_DORMANT_FRAME; + Cockpit_info.next_keyframe = COCKPIT_DORMANT_FRAME; + Cockpit_info.resized = false; + Cockpit_info.buffet_amp = 0.0f; + Cockpit_info.snd_open = SOUND_COCKPIT; + vm_MakeZero(&Cockpit_info.buffet_vec); +// find layered submodel mask + Cockpit_info.layered_mask = 0x00000000; + Cockpit_info.nonlayered_mask = 0x00000000; + for (i = 0; i < Cockpit_info.model->n_models; i++) + { + if ((Cockpit_info.model->submodel[i].flags & SOF_VIEWER) || (Cockpit_info.model->submodel[i].flags & SOF_MONITOR_MASK)) + continue; + if (Cockpit_info.model->submodel[i].flags & SOF_LAYER) + Cockpit_info.layered_mask |= (1<< i); + else + Cockpit_info.nonlayered_mask |= (1< -1) { + FreePolyModel(Cockpit_info.model_num); + Cockpit_info.model = NULL; + Cockpit_info.model_num = -1; + } + Cockpit_info.ship_index = -1; + Cockpit_info.frame_time = 0.0f; + Cockpit_info.state = COCKPIT_STATE_DORMANT; +// free ship specific stuff for hud-cockpit shared. + bm_FreeBitmap(HUD_resources.invpulse_bmp); + bm_FreeBitmap(HUD_resources.afterburn_bmp); + bm_FreeBitmap(HUD_resources.energy_bmp); + for (i = 0; i < NUM_SHIELD_GAUGE_FRAMES; i++) + bm_FreeBitmap(HUD_resources.shield_bmp[i]); + bm_FreeBitmap(HUD_resources.ship_bmp); +} +// check if cockpit exists +bool IsValidCockpit() +{ + return (Cockpit_info.model_num > -1) ? true: false; +} +bool CockpitFileParse(const char *command, const char *operand, void *data) +{ + tCockpitCfgInfo *cfginf = (tCockpitCfgInfo *)data; + if (!strcmp(command, "ckptmodel")) { + // cockpit model name + if (cfginf) + strcpy(cfginf->modelname, operand); + } + else if (!strncmp(command, "shieldimg", strlen("shieldimg"))) { + // cockpit shield ring names + char buf[16]; + int i; + for (i = 0; i < NUM_SHIELD_GAUGE_FRAMES; i++) + { + sprintf(buf, "shieldimg%d", i); + if (!strcmpi(command, buf)) { + if (cfginf) + strcpy(cfginf->shieldrings[i], operand); + break; + } + } + } + else if (!strcmp(command, "shipimg")) { + // ship image name + if (cfginf) + strcpy(cfginf->shipimg, operand); + } + else if (!strcmp(command, "afterburnimg")) { + // ship image name + if (cfginf) + strcpy(cfginf->burnimg, operand); + } + else if (!strcmp(command, "energyimg")) { + // ship image name + if (cfginf) + strcpy(cfginf->energyimg, operand); + } + else if (!strcmp(command, "invpulseimg")) { + // ship image name + if (cfginf) + strcpy(cfginf->invpulseimg, operand); + } + else if (!strcmp(command, "fullhudinf")) { + if (cfginf) + strcpy(HUD_resources.hud_inf_name, operand); + } + else { + return false; + } + return true; +} +// loads pertinent information about cockpit. +void LoadCockpitInfo(const char *ckt_file, tCockpitCfgInfo *cfginfo) +{ +// clear out return values. + if (cfginfo) { + memset(cfginfo, 0, sizeof(tCockpitCfgInfo)); + ASSERT(NUM_SHIELD_GAUGE_FRAMES == 5); + strcpy(cfginfo->shieldrings[0], TBL_GAMEFILE("shieldring01.ogf")); + strcpy(cfginfo->shieldrings[1], TBL_GAMEFILE("shieldring02.ogf")); + strcpy(cfginfo->shieldrings[2], TBL_GAMEFILE("shieldring03.ogf")); + strcpy(cfginfo->shieldrings[3], TBL_GAMEFILE("shieldring04.ogf")); + strcpy(cfginfo->shieldrings[4], TBL_GAMEFILE("shieldring05.ogf")); + strcpy(cfginfo->shipimg, TBL_GAMEFILE("hudship.ogf")); + strcpy(cfginfo->burnimg, TBL_GAMEFILE("hudburn.ogf")); + strcpy(cfginfo->energyimg, TBL_GAMEFILE("hudenergy.ogf")); + strcpy(cfginfo->invpulseimg, TBL_GAMEFILE("shieldinv.ogf")); + } + if (ckt_file[0] == 0) + return; + + LoadHUDConfig(ckt_file, CockpitFileParse, cfginfo); +} +// forces opening of cockpit. +void OpenCockpit() +{ +// this should allow for opening cockpit while in closing mode. +// do this to insure that an immediate call to CloseCockpit will force it to close again. + if (Cockpit_info.this_keyframe <= COCKPIT_COMPLETE_FRAME) { + Cockpit_info.state = COCKPIT_STATE_QUASI; + Cockpit_info.this_keyframe = COCKPIT_DORMANT_FRAME; + Cockpit_info.next_keyframe = COCKPIT_COMPLETE_FRAME; + if (Cockpit_info.frame_time > 0.0f) + Cockpit_info.frame_time = COCKPIT_ANIM_TIME - Cockpit_info.frame_time; + } + Sound_system.Play2dSound(Cockpit_info.snd_open); +// load hud information for cockpit. + LoadCockpitInfo(Ships[Cockpit_info.ship_index].cockpit_name, NULL); +} +// forces complete closing of cockpit +void CloseCockpit() +{ +// this should allow for closing cockpit while in opening mode. +// do this to insure that an immediate call to OpenCockpit will force it to open again. + if (Cockpit_info.this_keyframe >= COCKPIT_DORMANT_FRAME) { + Cockpit_info.next_keyframe = COCKPIT_DORMANT_FRAME; + Cockpit_info.this_keyframe = COCKPIT_COMPLETE_FRAME; + if (Cockpit_info.frame_time > 0.0f) + Cockpit_info.frame_time = COCKPIT_ANIM_TIME - Cockpit_info.frame_time; + } + FlagGaugesNonfunctional(STAT_ALL); + Sound_system.Play2dSound(Cockpit_info.snd_open); +} +// forces quick opening of cockpit +void QuickOpenCockpit() +{ + Cockpit_info.frame_time = 0.0f; + Cockpit_info.state = COCKPIT_STATE_FUNCTIONAL; + Cockpit_info.this_keyframe = Cockpit_info.next_keyframe = COCKPIT_COMPLETE_FRAME; + FlagGaugesFunctional(STAT_ALL); +// load hud information for cockpit. + LoadCockpitInfo(Ships[Cockpit_info.ship_index].cockpit_name, NULL); +} +// forces quick closing of cockpit +void QuickCloseCockpit() +{ + Cockpit_info.frame_time = 0.0f; + Cockpit_info.state = COCKPIT_STATE_DORMANT; + Cockpit_info.this_keyframe = Cockpit_info.next_keyframe = COCKPIT_DORMANT_FRAME; + FlagGaugesNonfunctional(STAT_ALL); +} +// resizes cockpit. +void ResizeCockpit() +{ + Cockpit_info.resized = true; +} +// cockpit orientation. +void StartCockpitShake(float mag, vector *vec) +{ + ASSERT(vec); + if (mag > MAX_BUFFET_STRENGTH) + mag = MAX_BUFFET_STRENGTH; + Cockpit_info.buffet_amp = mag; + Cockpit_info.buffet_vec = (*vec); + Cockpit_info.buffet_wave = FixSin(0); + Cockpit_info.buffet_time = 0.0f; +} +////////////////////////////////////////////////////////////////////////////// +// renders the cockpit. +extern float GetTerrainDynamicScalar (vector *pos,int seg); +extern void GetRoomDynamicScalar (vector *pos,room *rp,float *r,float *g,float *b); +void RenderCockpit() +{ + object *player_obj = &Objects[Players[Player_num].objnum]; + physics_info *player_phys = &player_obj->mtype.phys_info; + vector view_pos, light_vec; + matrix view_tmat; + float view_z, view_y, view_x, keyframe; + float light_scalar_r,light_scalar_g,light_scalar_b; + float normalized_time[MAX_SUBOBJECTS]; + bool gauge_reset=false; +// draw cockpit depending on current state + if (Cockpit_info.state == COCKPIT_STATE_DORMANT || Cockpit_info.model_num == -1) + return; +// position cockpit correctly. + bsp_info *viewer_subobj = CockpitGetMonitorSubmodel(SOF_VIEWER); + if (!viewer_subobj) { + mprintf((0, "Cockpit missing viewer!\n")); + return; + } + view_z = viewer_subobj->offset.z - Cockpit_info.buffet_vec.z*Cockpit_info.buffet_amp*Cockpit_info.buffet_wave*1.1f; + view_y = viewer_subobj->offset.y + Cockpit_info.buffet_vec.y*Cockpit_info.buffet_amp*Cockpit_info.buffet_wave; + view_x = viewer_subobj->offset.x + Cockpit_info.buffet_vec.x*Cockpit_info.buffet_amp*Cockpit_info.buffet_wave; +//@@ if (player_phys->rotthrust.x !=0.0f || player_phys->rotthrust.y != 0.0f) { +//@@ gauge_reset = true; +//@@ view_x += (player_phys->rotthrust.y*COCKPIT_SHIFT_DELTA/player_phys->full_rotthrust); +//@@ view_y -= (player_phys->rotthrust.x*COCKPIT_SHIFT_DELTA/player_phys->full_rotthrust); +//@@ } +// create new orientation, so the cockpit should be facing the viewer + view_tmat = Viewer_object->orient; + view_pos = (view_tmat.fvec * view_z) + (view_tmat.uvec * view_y) + (view_tmat.rvec * view_x) + Viewer_object->pos; + view_tmat.fvec = -view_tmat.fvec; + view_tmat.rvec = -view_tmat.rvec; +// lighting. + light_vec = -Viewer_object->orient.uvec; + if (OBJECT_OUTSIDE(player_obj)) + { + float light_scalar = GetTerrainDynamicScalar(&player_obj->pos ,CELLNUM(player_obj->roomnum)); + light_scalar_r=light_scalar; + light_scalar_g=light_scalar; + light_scalar_b=light_scalar; + } + else + { + GetRoomDynamicScalar(&player_obj->pos, &Rooms[player_obj->roomnum],&light_scalar_r,&light_scalar_g,&light_scalar_b); + } + if (light_scalar_r < 0.1f) + light_scalar_r = 0.1f; + if (light_scalar_g < 0.1f) + light_scalar_g = 0.1f; + if (light_scalar_b < 0.1f) + light_scalar_b = 0.1f; + // Samir, I moved this from the DrawPolygonModel call below -JL + light_scalar_r*=.8f; + light_scalar_g*=.8f; + light_scalar_b*=.8f; + + if (player_obj->effect_info) + { + light_scalar_r=min(1.0,light_scalar_r+(player_obj->effect_info->dynamic_red)); + light_scalar_g=min(1.0,light_scalar_g+(player_obj->effect_info->dynamic_green)); + light_scalar_b=min(1.0,light_scalar_b+(player_obj->effect_info->dynamic_blue)); + } + if (Players[player_obj->id].flags & PLAYER_FLAGS_HEADLIGHT) + { + light_scalar_r=1.0; + light_scalar_g=1.0; + light_scalar_b=1.0; + } + +// animate and draw + keyframe = KeyframeAnimateCockpit(); + SetNormalizedTimeAnim(keyframe, normalized_time, Cockpit_info.model); +// must put after animation. + if (Cockpit_info.buffet_amp > 0.04f) { + angle buffet_angle; + Cockpit_info.buffet_time += Frametime; + if (Cockpit_info.buffet_time > BUFFET_PERIOD) { + Cockpit_info.buffet_time = 0.0f; + Cockpit_info.buffet_amp *= 0.5f; + } + buffet_angle = (angle)(65536.0*Cockpit_info.buffet_time/(BUFFET_PERIOD-((BUFFET_PERIOD-Cockpit_info.buffet_time)*0.5f))); + Cockpit_info.buffet_wave = FixSin(buffet_angle); + if (Cockpit_info.buffet_wave > 0.5f) + Cockpit_info.buffet_wave = 1.0f; + else if (Cockpit_info.buffet_wave < -0.5f) + Cockpit_info.buffet_wave = -1.0f; + else + Cockpit_info.buffet_wave = 0.0f; + Cockpit_info.animating = true; + } + else if (Cockpit_info.buffet_amp > 0.0f) { + Cockpit_info.animating = true; + Cockpit_info.buffet_amp = 0.0f; + } +// draws lower z cockpit, and monitor glares after gauge renderering + rend_SetZBufferState(0); + DrawPolygonModel(&view_pos, &view_tmat, Cockpit_info.model_num, normalized_time,0, &light_vec, light_scalar_r,light_scalar_g,light_scalar_b, Cockpit_info.nonlayered_mask, 0,1 ); + RenderGauges(&view_pos, &view_tmat, normalized_time, (Cockpit_info.animating || Cockpit_info.resized), gauge_reset); + rend_SetZBufferState(0); + DrawPolygonModel(&view_pos, &view_tmat, Cockpit_info.model_num, normalized_time,0, &light_vec, light_scalar_r,light_scalar_g,light_scalar_b, Cockpit_info.layered_mask,0,1); + + Cockpit_info.resized = false; +} +////////////////////////////////////////////////////////////////////////////// +// +// adjusts current keyframe and dormancy +float KeyframeAnimateCockpit() +{ + float newkeyframe; + + newkeyframe = Cockpit_info.this_keyframe + (Cockpit_info.next_keyframe-Cockpit_info.this_keyframe)*(Cockpit_info.frame_time/COCKPIT_ANIM_TIME); +// mprintf((0, "this=%.1f next=%.1f ft=%.1f\n", Cockpit_info.this_keyframe, Cockpit_info.next_keyframe, Cockpit_info.frame_time)); +// going up in keyframes + if (Cockpit_info.this_keyframe < Cockpit_info.next_keyframe) { + if (newkeyframe >= Cockpit_info.next_keyframe) { + Cockpit_info.frame_time = 0.0f; + Cockpit_info.this_keyframe = Cockpit_info.next_keyframe; + } + } +// going down in keyframes + else if (Cockpit_info.this_keyframe > Cockpit_info.next_keyframe) { + if (newkeyframe <= Cockpit_info.next_keyframe) { + Cockpit_info.frame_time = 0.0f; + Cockpit_info.this_keyframe = Cockpit_info.next_keyframe; + } + } + else { + Cockpit_info.animating = false; + return newkeyframe; + } + +// decide if we can process cockpit + Cockpit_info.animating = true; + if (Cockpit_info.this_keyframe != Cockpit_info.next_keyframe) { + Cockpit_info.frame_time += Frametime; + } + if (Cockpit_info.this_keyframe == COCKPIT_COMPLETE_FRAME && Cockpit_info.next_keyframe == Cockpit_info.this_keyframe) { + FlagGaugesFunctional(STAT_ALL); + Cockpit_info.state = COCKPIT_STATE_FUNCTIONAL; + } + else if (Cockpit_info.this_keyframe == COCKPIT_DORMANT_FRAME && Cockpit_info.next_keyframe == COCKPIT_DORMANT_FRAME) { + Cockpit_info.state = COCKPIT_STATE_DORMANT; + } + else { + Cockpit_info.state = COCKPIT_STATE_QUASI; + } + return newkeyframe; +} +////////////////////////////////////////////////////////////////////////////// +// +// returns the submodel number of the monitor requested +bsp_info *CockpitGetMonitorSubmodel(ushort monitor_flag) +{ + int i; + ASSERT(Cockpit_info.model_num > -1); + for (i = 0; i < Poly_models[Cockpit_info.model_num].n_models; i++) + { + if (Poly_models[Cockpit_info.model_num].submodel[i].flags & monitor_flag) + return &Poly_models[Cockpit_info.model_num].submodel[i]; + } + return NULL; +} +// returns the polymodel for the hud +poly_model *CockpitGetPolyModel() +{ + ASSERT(Cockpit_info.model_num > -1); + + return &Poly_models[Cockpit_info.model_num]; +} +//Tell whether the cockpit is animating +int CockpitState() +{ + return Cockpit_info.state; +} diff --git a/Descent3/cockpit.h b/Descent3/cockpit.h new file mode 100644 index 000000000..e9805d187 --- /dev/null +++ b/Descent3/cockpit.h @@ -0,0 +1,98 @@ +/* + * $Logfile: /DescentIII/main/cockpit.h $ + * $Revision: 10 $ + * $Date: 10/20/98 12:42p $ + * $Author: Matt $ + * + * cockpit code + * + * $Log: /DescentIII/main/cockpit.h $ + * + * 10 10/20/98 12:42p Matt + * Made the small views work on the cockpit. + * + * 9 8/18/98 1:11a Samir + * fixed some stuff for multi cockpit configs. + * + * 8 5/26/98 5:05p Samir + * cockpit and hud config file now 'unified'. cockpit adds to hudconfig + * load's funtionality, so cockpit info files can contain same syntax as + * hud file. + * + * 7 5/14/98 7:40p Samir + * shaking cockpit. + * + * 6 5/05/98 6:27p Samir + * function that tells the cockpit system that it was resized + * + * 5 5/01/98 4:24p Samir + * added function to quickly open the cockpit. + * + * 4 4/24/98 8:01a Samir + * don't pass zoom argument to Render functions. + * + * 3 3/17/98 2:37p Samir + * reorg of hud/gauge/cockpit dependencies. + * + * 2 3/16/98 3:30p Samir + * incremental checkin. + * + * 1 3/13/98 11:22a Samir + * initial revision. + * + * $NoKeywords: $ + */ + +#ifndef COCKPIT_H +#define COCKPIT_H + +#include "pstypes.h" +#include "vecmat.h" + +struct poly_model; +struct bsp_info; + +// renders the cockpit. +void RenderCockpit(); + +// initialization of cockpit information. +void InitCockpit(int ship_index); + +// Forces freeing of cockpit information +void FreeCockpit(); + +// check if cockpit exists +bool IsValidCockpit(); + +// forces opening of cockpit on hud +void OpenCockpit(); + +// forces closing of cockpit on hud. +void CloseCockpit(); + +// resizes cockpit. +void ResizeCockpit(); + +// forces quick open of cockpit +void QuickOpenCockpit(); + +// forces quick closing of cockpit +void QuickCloseCockpit(); + +// returns the submodel number of the monitor requested +bsp_info *CockpitGetMonitorSubmodel(ushort monitor_flag); + +// returns the polymodel for the hud +poly_model *CockpitGetPolyModel(); + +// cockpit effects. +void StartCockpitShake(float mag, vector *vec); + +#define COCKPIT_STATE_DORMANT 0 +#define COCKPIT_STATE_QUASI 1 // state changing +#define COCKPIT_STATE_FUNCTIONAL 2 + +//Tell whether the cockpit is animating +int CockpitState(); + +#endif diff --git a/Descent3/config.cpp b/Descent3/config.cpp new file mode 100644 index 000000000..1e3eb55f0 --- /dev/null +++ b/Descent3/config.cpp @@ -0,0 +1,1636 @@ +/* + * $Logfile: /DescentIII/Main/config.cpp $ + * $Revision: 178 $ + * $Date: 9/14/01 4:47p $ + * $Author: Matt $ + * + * source to configuration functions (video,sound,etc) + * + * $Log: /DescentIII/Main/config.cpp $ + * + * 178 9/14/01 4:47p Matt + * Cleaned up problems when screen bit depth set to 32. + * + * 177 9/14/01 12:47p Matt + * Took out center window missile view option since it didn't really work + * (& wasn't supposed to be in the game). + * + * 176 9/14/01 11:06a Matt + * Removed the mission goals item from the HUD config, since there's no + * mission goal HUD item to be controlled. + * + * 175 4/25/00 3:15p Matt + * Fixed math error (incorrect literal constant) in terrain detail + * computation. + * + * 174 4/19/00 5:10p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * Mac pragmas + * + * 173 3/20/00 12:02p Matt + * Merge of Duane's post-1.3 changes. + * Made some text localizable and changed default level settings (Mac + * only) + * + * 172 10/29/99 5:25p Jeff + * fixed for Linux (no gamma or 32/16 bit options) + * + * 171 10/25/99 11:56a Jeff + * ifdef'd some Macintosh pragmas + * + * 170 10/22/99 10:42a Matt + * Mac merge + * + * 169 5/22/99 1:16a Jason + * change terrain detail level settings + * + * 168 5/11/99 4:37p Jason + * made opengl work with 32 bit + * + * 167 5/11/99 1:44p Jason + * fixed config screen bug + * + * 166 5/06/99 1:40a Samir + * adjusted some text. + * + * 165 5/01/99 9:41a Kevin + * FIxed compiler error + * + * 164 5/01/99 1:57a Samir + * small change to where STAT_SCORE gets set. + * + * 163 5/01/99 1:56a Samir + * STAT_SCORE for hud is always set now. + * + * 162 4/30/99 12:12p Samir + * added strings for key ramping, other buttons in general config menu. + * + * 161 4/24/99 8:39p Samir + * added ship sounds toggle. + * + * 160 4/21/99 3:46p Jason + * made opengl only work with 16bit + * + * 159 4/20/99 11:44a Samir + * brightness menu has default focus on the slider. + * + * 158 4/17/99 8:25p Samir + * took out graphical option for inventory display. no time. + * + * 157 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 156 4/13/99 11:15a Samir + * + * 155 4/13/99 10:28a Samir + * don't call SetLLSoundPlayed if value doesn't change. + * + * 154 4/12/99 7:15p Samir + * added ability to configure number of sounds played at one time. + * + * 153 3/29/99 7:29p Jason + * made renderer handle default resolution more gracefully + * + * 152 3/23/99 9:03p Samir + * fixed a bunch of sound mixer problems when configing mixer to none. + * + * 151 3/22/99 4:26p Samir + * added toggles for guided missile view and reticle. + * + * 150 3/17/99 4:19p Samir + * change music volume on the fly. + * + * 149 3/08/99 9:05p Samir + * dont allow user to swtich sound quality while in game. + * + * 148 3/05/99 2:40p Samir + * fixed potential bug + * + * 147 3/05/99 12:25p Jason + * changed maximum terrain detail + * + * 146 3/04/99 6:08p Samir + * added wait message for config screen. + * + * 145 3/03/99 5:07p Samir + * music volume will shutoff music if 0, restart if greater only after a + * change in vollume. + * + * 144 3/02/99 1:14p Samir + * added joystick/mouse enable and gamma help. + * + * 143 3/02/99 12:27p Jason + * changed terrain distance settings for the different details + * + * 142 3/01/99 8:11p Samir + * adjust current stream volume with sound slider. + * + * 141 3/01/99 5:52p Samir + * fixed slider bars and added render distance. + * + * 140 3/01/99 5:01a Samir + * made sure NEWUIRES_FORCEQUIT was added to all new interfaces. + * + * 139 2/28/99 3:22a Samir + * reformatted video menu. + * + * 138 2/26/99 2:09a Samir + * gamma menu revised. + * + * 137 2/25/99 11:18a Jason + * fixed gamma zbuffer problem + * + * 136 2/24/99 6:23p Samir + * forgot to save default detail level var. + * + * 135 2/22/99 9:08p Samir + * added AddSimpleOption to replace NULL sheet options. + * + * 134 2/22/99 7:12p Doug + * temporarily removed txt_hudwarnings and replaced with a literal string + * until Samir can make the fix needed (Jeff) + * + * 133 2/22/99 5:52p Samir + * fixed another weird thing. + * + * 132 2/22/99 4:24p Samir + * took out remaining powerupvoices code. + * + * 131 2/20/99 7:08p Matt + * Removed a few items, and change the resolution strings to not be + * localizable + * + * 130 2/19/99 10:31p Samir + * added music volume. + * + * 129 2/19/99 6:56p Samir + * fixed bug in video menu in options screen (resolution switch + * sequencing.) + * + * 128 2/17/99 3:56p Jason + * FROM SAMIR: Upped video optios gadget count + * + * 127 2/16/99 12:05p Samir + * revamped ui for config menus. + * + * 126 2/15/99 7:47p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 125 2/15/99 12:03p Jason + * changed word "Gamma" to "Brightness" + * + * 124 2/07/99 1:17a Jeff + * peppered UI dialogs that were missing NEWUIRES_FORCEQUIT to handle it + * + * 123 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 122 1/28/99 3:58p Jeff + * localization update + * + * 121 1/27/99 2:58p Jason + * fixed center small view + * + * 120 1/24/99 3:13a Jason + * changed max terrain detail + * + * 119 1/21/99 10:46a Jason + * changed "weapon corona" text to "weapon effects" + * + * 118 1/12/99 5:29p Jason + * another pass at getting this gamma stuff done + * + * 117 1/12/99 12:55p Jason + * added gamma contrast boxes + * + * 116 1/11/99 5:52p Samir + * added Creative EAX option. + * + * 115 1/11/99 11:18a Jason + * first pass at autogamma + * + * 114 12/23/98 11:48a Samir + * changed name of aureal mixer. + * + * 113 12/10/98 3:41p Jeff + * implemented settings for 16 or 32 bit depth (in config) + * + * 112 12/09/98 6:54p Jason + * changed default config settings + * + * 111 12/09/98 6:46p Jeff + * added scorch marks and weapon coronas to detail menu + * + * 110 12/03/98 11:06a Samir + * added axis sensitivity + * + * 109 12/02/98 11:36a Samir + * readded joystick sensitivity. + * + * 108 11/30/98 6:00p Jeff + * max terrain detail setting is now 26 (to the user...6 internally) + * + * 107 11/30/98 3:25p Jeff + * fixed bug where game will crash if they enter graphics config with a + * hacked resolution + * + * 106 11/30/98 3:12p Jeff + * gamma goes 1->3 now + * + * 105 11/23/98 12:32p Jeff + * no decimal on the gain slider of forcefeedback + * + * 104 11/10/98 5:14p Jeff + * added Force Feedback configuration + * + * 103 10/22/98 4:37p Jeff + * removed missile view for demo + * + * 102 10/22/98 2:40p Samir + * took out joystick sensitivity for demo. + * + * 101 10/21/98 11:55p Samir + * added sensitivity slider functionality. + * + * 100 10/21/98 4:48p Jeff + * changed slider menu click sound + * +* +* $NoKeywords: $ +*/ + +#include "ConfigItem.h" +#include "player.h" +#include "config.h" +#include "ddio.h" +#include "newui.h" +#include "3d.h" +#include "polymodel.h" +#include "application.h" +#include "descent.h" +#include "mono.h" +#include "Mission.h" +#include "ddio.h" +#include "gamefont.h" +#include "multi_ui.h" +#include "cinematics.h" +#include "hlsoundlib.h" +#include "terrain.h" +#include "CFILE.H" +#include "mem.h" +#include "lighting.h" +#include "PHYSICS.H" +#include "pilot.h" +#include "hud.h" +#include "voice.h" +#include "bitmap.h" +#include "game.h" +#include "render.h" +#include "stringtable.h" +#include "SmallViews.h" +#include "D3ForceFeedback.h" +#include "descent.h" +#include "appdatabase.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "sounds.h" +#include "ctlconfig.h" +#include "d3music.h" + +#include +#include +#include + +#define STAT_SCORE STAT_TIMER + +#ifdef MACINTOSH +#include "macdisplays.h" + +tVideoResolution Video_res_list[N_SUPPORTED_VIDRES] = { + #ifdef USE_OPENGL + {640,480}, + {800,600}, + {1024,768}, +// {1152,870}, +// {1280,960} + #else +// {512,384}, + {640,480}, + {800,600}, + {960,720}, +// {1024,768} + #endif + }; +#else +tVideoResolution Video_res_list[N_SUPPORTED_VIDRES] = { + {512,384}, + {640,480}, + {800,600}, + {960,720}, + {1024,768}, + {1280,960}, + {1600,1200}, + {640,480} // custom +}; +#endif + +int Game_video_resolution = 1; + +tDetailSettings Detail_settings; +int Default_detail_level = DETAIL_LEVEL_MED; + +tGameToggles Game_toggles = { // toggles specified in general settings. + true, + false, + true +}; + +#define IDV_VCONFIG 12 //video config +#define IDV_GCONFIG 13 //general config +#define IDV_SCONFIG 14 //audio config +#define IDV_DCONFIG 15 //detail level config +#define IDV_HCONFIG 16 //hud config +#define IDV_CCONFIG 17 //controller config +#define IDV_QUIT 0xff + +#define UID_GAMMASLIDER 0x1000 + + +///////////////////////////////////////////////////////////////////////////// +// Defines +#define IDV_OK 1 +#define IDV_CANCEL 2 + +//video defines +#define VID_D3D 0 +#define VID_GLIDE 1 +#define VID_OPENGL 2 + +//small view settings +#define SMVS_NONE 0 +#define SMVS_MISSLE 1 + +//these are the default settings for each detail level +#define DL_LOW_TERRAIN_DISTANCE (MINIMUM_RENDER_DIST*TERRAIN_SIZE) +#ifndef MACINTOSH +#define DL_LOW_PIXEL_ERROR 25.0 +#else +#define DL_LOW_PIXEL_ERROR MAXIMUM_TERRAIN_DETAIL +#endif +#define DL_LOW_SPECULAR_LIGHT false +#define DL_LOW_DYNAMIC_LIGHTING false +#define DL_LOW_FAST_HEADLIGHT true +#define DL_LOW_MIRRORED_SURFACES false +#define DL_LOW_FOG_ENABLED false +#define DL_LOW_CORONAS_ENABLES false +#define DL_LOW_PROCEDURALS false +#define DL_LOW_POWERUP_HALOS false +#define DL_LOW_SCORCH_MARKS false +#define DL_LOW_WEAPON_CORONAS false +#define DL_LOW_SPEC_MAPPING_TYPE 1 +#define DL_LOW_OBJECT_COMPLEXITY 0 + +#ifndef MACINTOSH +#define DL_MED_TERRAIN_DISTANCE (90*TERRAIN_SIZE) +#else +#define DL_MED_TERRAIN_DISTANCE (((MAXIMUM_RENDER_DIST-MINIMUM_RENDER_DIST)*0.2+MINIMUM_RENDER_DIST)*TERRAIN_SIZE) +#endif +#define DL_MED_PIXEL_ERROR 18.0 +#define DL_MED_SPECULAR_LIGHT false +#define DL_MED_DYNAMIC_LIGHTING false +#ifndef MACINTOSH +#define DL_MED_FAST_HEADLIGHT true +#else +#define DL_MED_FAST_HEADLIGHT false +#endif +#define DL_MED_MIRRORED_SURFACES false +#define DL_MED_FOG_ENABLED true +#define DL_MED_CORONAS_ENABLES true +#define DL_MED_PROCEDURALS false +#define DL_MED_POWERUP_HALOS true +#define DL_MED_SCORCH_MARKS true +#define DL_MED_WEAPON_CORONAS false +#define DL_MED_SPEC_MAPPING_TYPE 1 +#define DL_MED_OBJECT_COMPLEXITY 1 + +#ifndef MACINTOSH +#define DL_HIGH_TERRAIN_DISTANCE (100*TERRAIN_SIZE) +#else +#define DL_HIGH_TERRAIN_DISTANCE (((MAXIMUM_RENDER_DIST-MINIMUM_RENDER_DIST)*0.4+MINIMUM_RENDER_DIST)*TERRAIN_SIZE) +#endif +#define DL_HIGH_PIXEL_ERROR 12.0 +#define DL_HIGH_SPECULAR_LIGHT false +#define DL_HIGH_DYNAMIC_LIGHTING true +#ifndef MACINTOSH +#define DL_HIGH_FAST_HEADLIGHT true +#else +#define DL_HIGH_FAST_HEADLIGHT false +#endif +#define DL_HIGH_MIRRORED_SURFACES true +#define DL_HIGH_FOG_ENABLED true +#define DL_HIGH_CORONAS_ENABLES true +#define DL_HIGH_PROCEDURALS true +#define DL_HIGH_POWERUP_HALOS true +#define DL_HIGH_SCORCH_MARKS true +#define DL_HIGH_WEAPON_CORONAS true +#define DL_HIGH_SPEC_MAPPING_TYPE 1 +#define DL_HIGH_OBJECT_COMPLEXITY 2 + +#ifndef MACINTOSH +#define DL_VHI_TERRAIN_DISTANCE (120.0*TERRAIN_SIZE) +#define DL_VHI_PIXEL_ERROR 10.0 +#else +#define DL_VHI_TERRAIN_DISTANCE (MAXIMUM_RENDER_DIST*TERRAIN_SIZE) +#define DL_VHI_PIXEL_ERROR MINIMUM_TERRAIN_DETAIL +#endif +#define DL_VHI_SPECULAR_LIGHT true +#define DL_VHI_DYNAMIC_LIGHTING true +#ifndef MACINTOSH +#define DL_VHI_FAST_HEADLIGHT true +#else +#define DL_VHI_FAST_HEADLIGHT false +#endif +#define DL_VHI_MIRRORED_SURFACES true +#define DL_VHI_FOG_ENABLED true +#define DL_VHI_CORONAS_ENABLES true +#define DL_VHI_PROCEDURALS true +#define DL_VHI_POWERUP_HALOS true +#define DL_VHI_SCORCH_MARKS true +#define DL_VHI_WEAPON_CORONAS true +#define DL_VHI_SPEC_MAPPING_TYPE 1 +#define DL_VHI_OBJECT_COMPLEXITY 2 + +#define MINIMUM_TERRAIN_DETAIL 4 +#define MAXIMUM_TERRAIN_DETAIL 28 +#ifdef MACINTOSH +#define MINIMUM_RENDER_DIST 40 +#define MAXIMUM_RENDER_DIST 200 +#else +#define MINIMUM_RENDER_DIST 80 +#define MAXIMUM_RENDER_DIST 200 +#endif + +tDetailSettings DetailPresetLow = { + DL_LOW_TERRAIN_DISTANCE, + DL_LOW_PIXEL_ERROR, + DL_LOW_SPECULAR_LIGHT, + DL_LOW_DYNAMIC_LIGHTING, + DL_LOW_FAST_HEADLIGHT, + DL_LOW_MIRRORED_SURFACES, + DL_LOW_FOG_ENABLED, + DL_LOW_CORONAS_ENABLES, + DL_LOW_PROCEDURALS, + DL_LOW_POWERUP_HALOS, + DL_LOW_SCORCH_MARKS, + DL_LOW_WEAPON_CORONAS, + DL_LOW_SPEC_MAPPING_TYPE, + DL_LOW_OBJECT_COMPLEXITY}; +tDetailSettings DetailPresetMed = { + DL_MED_TERRAIN_DISTANCE, + DL_MED_PIXEL_ERROR, + DL_MED_SPECULAR_LIGHT, + DL_MED_DYNAMIC_LIGHTING, + DL_MED_FAST_HEADLIGHT, + DL_MED_MIRRORED_SURFACES, + DL_MED_FOG_ENABLED, + DL_MED_CORONAS_ENABLES, + DL_MED_PROCEDURALS, + DL_MED_POWERUP_HALOS, + DL_MED_SCORCH_MARKS, + DL_MED_WEAPON_CORONAS, + DL_MED_SPEC_MAPPING_TYPE, + DL_MED_OBJECT_COMPLEXITY}; +tDetailSettings DetailPresetHigh = { + DL_HIGH_TERRAIN_DISTANCE, + DL_HIGH_PIXEL_ERROR, + DL_HIGH_SPECULAR_LIGHT, + DL_HIGH_DYNAMIC_LIGHTING, + DL_HIGH_FAST_HEADLIGHT, + DL_HIGH_MIRRORED_SURFACES, + DL_HIGH_FOG_ENABLED, + DL_HIGH_CORONAS_ENABLES, + DL_HIGH_PROCEDURALS, + DL_HIGH_POWERUP_HALOS, + DL_HIGH_SCORCH_MARKS, + DL_HIGH_WEAPON_CORONAS, + DL_HIGH_SPEC_MAPPING_TYPE, + DL_HIGH_OBJECT_COMPLEXITY}; +tDetailSettings DetailPresetVHi = { + DL_VHI_TERRAIN_DISTANCE, + DL_VHI_PIXEL_ERROR, + DL_VHI_SPECULAR_LIGHT, + DL_VHI_DYNAMIC_LIGHTING, + DL_VHI_FAST_HEADLIGHT, + DL_VHI_MIRRORED_SURFACES, + DL_VHI_FOG_ENABLED, + DL_VHI_CORONAS_ENABLES, + DL_VHI_PROCEDURALS, + DL_VHI_POWERUP_HALOS, + DL_VHI_SCORCH_MARKS, + DL_VHI_WEAPON_CORONAS, + DL_VHI_SPEC_MAPPING_TYPE, + DL_VHI_OBJECT_COMPLEXITY}; + +void ConfigSetDetailLevel(int level) +{ + switch(level){ + case DETAIL_LEVEL_LOW: + memcpy(&Detail_settings,&DetailPresetLow,sizeof(tDetailSettings)); + break; + case DETAIL_LEVEL_MED: + memcpy(&Detail_settings,&DetailPresetMed,sizeof(tDetailSettings)); + break; + case DETAIL_LEVEL_HIGH: + memcpy(&Detail_settings,&DetailPresetHigh,sizeof(tDetailSettings)); + break; + case DETAIL_LEVEL_VERY_HIGH: + memcpy(&Detail_settings,&DetailPresetVHi,sizeof(tDetailSettings)); + break; + }; + + Default_detail_level = level; +} + + +////////////////////////////////////////////////////////////////////////////// +// Gamma settings + +//gamma configuration, window placement +#define GAMMA_MENU_H 320 +#define GAMMA_MENU_W 416 + +#define GAMMA_SLICES 32 +#define GAMMA_SLICE_WIDTH 256 +#define GAMMA_SLICE_HEIGHT 128 +#define GAMMA_SLICE_X ((GAMMA_MENU_W - GAMMA_SLICE_WIDTH)/2); +#define GAMMA_SLICE_Y 64 +#define GAMMA_SLIDER_UNITS 100 + +#define IDV_GAMMAAPPLY 5 +#define IDV_AUTOGAMMA 6 + +#if !(defined( MACINTOSH ) && defined (USE_OPENGL)) + +void gamma_callback(newuiTiledWindow *wnd, void *data) +{ + int bm_handle = *((int *)data); + + g3Point pnts[4],*pntlist[4]; + rend_SetColorModel(CM_RGB); + rend_SetAlphaType (AT_ALWAYS); + rend_SetTextureType (TT_LINEAR); + rend_SetLighting (LS_NONE); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_WRAP); + rend_SetZBufferState (0); + + // First draw checkboard + int startx=GAMMA_SLICE_X; + + for (int i=0;i<4;i++) + { + pntlist[i]=&pnts[i]; + pnts[i].p3_z=0; + pnts[i].p3_r=1; + pnts[i].p3_g=1; + pnts[i].p3_b=1; + pnts[i].p3_flags = PF_PROJECTED; + } + + pnts[0].p3_sx=startx; + pnts[0].p3_sy=GAMMA_SLICE_Y; + pnts[0].p3_u=0; + pnts[0].p3_v=0; + + pnts[1].p3_sx=startx+GAMMA_SLICE_WIDTH; + pnts[1].p3_sy=GAMMA_SLICE_Y; + pnts[1].p3_u=2; + pnts[1].p3_v=0; + + pnts[2].p3_sx=startx+GAMMA_SLICE_WIDTH; + pnts[2].p3_sy=GAMMA_SLICE_Y+GAMMA_SLICE_HEIGHT; + pnts[2].p3_u=2; + pnts[2].p3_v=1; + + pnts[3].p3_sx=startx; + pnts[3].p3_sy=GAMMA_SLICE_Y+GAMMA_SLICE_HEIGHT; + pnts[3].p3_u=0; + pnts[3].p3_v=1; + + rend_DrawPolygon2D( bm_handle, pntlist, 4 ); + + // Now draw grey in the center + int int_val=8; + rend_SetFlatColor (GR_RGB(int_val,int_val,int_val)); + rend_SetTextureType (TT_FLAT); + + pnts[0].p3_sx+=64; + pnts[0].p3_sy+=32; + + pnts[1].p3_sx-=64; + pnts[1].p3_sy+=32; + + pnts[2].p3_sx-=64; + pnts[2].p3_sy-=32; + + pnts[3].p3_sx+=64; + pnts[3].p3_sy-=32; + + rend_DrawPolygon2D( 0, pntlist, 4 ); + + rend_SetWrapType (WT_CLAMP); + rend_SetZBufferState (1); +} + + +void config_gamma() +{ + newuiTiledWindow gamma_wnd; + newuiSheet *sheet; + tSliderSettings slider_set; + short *gamma_slider; + short curpos; + int res, gamma_bitmap=-1; + float init_gamma; + +// Make gamma bitmap + gamma_bitmap=bm_AllocBitmap (128,128,0); + if (gamma_bitmap<=0) { + gamma_bitmap=0; + } + else { + ushort *dest_data=(ushort *)bm_data(gamma_bitmap,0); + int addval=0; + int i,t; + + // This loop makes the checkerboard + for (i=0;i<128;i++) + { + addval = (i&1) ? 1 : 0; + + for (t=0;t<128;t++) + { + if (((t+addval)%2)==0) { + dest_data[i*128+t]=OPAQUE_FLAG|(1<<10)|(1<<5)|(1); + } + else { + dest_data[i*128+t]=OPAQUE_FLAG; + } + } + } + } + +// create ui. + gamma_wnd.Create(TXT_AUTO_GAMMA, 0, 0, GAMMA_MENU_W, GAMMA_MENU_H); + gamma_wnd.SetData(&gamma_bitmap); + gamma_wnd.SetOnDrawCB(gamma_callback); + + sheet = gamma_wnd.GetSheet(); + +// ok, cancel buttons. + sheet->NewGroup(NULL, 130, 224, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_APPLY, IDV_GAMMAAPPLY); + sheet->AddText(" "); + sheet->AddButton(TXT_OK, UID_OK); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); + + sheet->NewGroup(NULL, 0, 145); + sheet->AddText(TXT_GAMMAADJUSTA); + sheet->AddText(TXT_GAMMAADJUSTB); + +// add slider! + sheet->NewGroup(NULL, 0, 175); + slider_set.min_val.f = 1.0f; + slider_set.max_val.f = 3.0f; + slider_set.type = SLIDER_UNITS_FLOAT; + + init_gamma = Render_preferred_state.gamma; + curpos = CALC_SLIDER_POS_FLOAT(init_gamma, &slider_set, GAMMA_SLIDER_UNITS); + gamma_slider = sheet->AddSlider(TXT_GAMMA,100, curpos, &slider_set, UID_GAMMASLIDER); + sheet->SetInitialFocusedGadget(UID_GAMMASLIDER); + +// do ui. + gamma_wnd.Open(); + + do + { + res = gamma_wnd.DoUI(); + if (res == NEWUIRES_FORCEQUIT) { + break; + } + if (res == IDV_GAMMAAPPLY) { + //get & set the gamma + Render_preferred_state.gamma = CALC_SLIDER_FLOAT_VALUE(*gamma_slider, slider_set.min_val.f, slider_set.max_val.f, GAMMA_SLIDER_UNITS); + rend_SetPreferredState(&Render_preferred_state); + } + } + while (res != UID_OK && res != UID_CANCEL); + +// handle results. + if (res == UID_OK) { + Render_preferred_state.gamma = CALC_SLIDER_FLOAT_VALUE(*gamma_slider, slider_set.min_val.f, slider_set.max_val.f, GAMMA_SLIDER_UNITS); + } + else { + Render_preferred_state.gamma = init_gamma; + } + + rend_SetPreferredState(&Render_preferred_state); + +// cleanup + gamma_wnd.Close(); + gamma_wnd.Destroy(); + + if (gamma_bitmap != 0) { + bm_FreeBitmap(gamma_bitmap); + } +} +#endif + +#ifdef MACINTOSH +#pragma mark video -- +#endif + +////////////////////////////////////////////////////////////////// +// VIDEO MENU +// +struct video_menu +{ + newuiSheet *sheet; + + bool *filtering; // settings + bool *mipmapping; + bool *vsync; + + int *bitdepth; // bitdepths + int *resolution; // all resolutions + +// sets the menu up. + newuiSheet *setup(newuiMenu *menu) + { + int iTemp; + sheet = menu->AddOption(IDV_VCONFIG, TXT_OPTVIDEO, NEWUIMENU_MEDIUM); + + // video resolution + iTemp = Game_video_resolution; + sheet->NewGroup(TXT_RESOLUTION, 0, 0); +#ifdef MACINTOSH + #ifdef USE_OPENGL + resolution = sheet->AddFirstLongRadioButton("640x480"); + if(gDSpContext[DSP_800x600]) + sheet->AddLongRadioButton("800x600"); + if(gDSpContext[DSP_1024x768]) + sheet->AddLongRadioButton("1024x768"); +// if(gDSpContext[DSP_1152x870]) +// sheet->AddLongRadioButton("1152x870"); +// if(gDSpContext[DSP_1280x960]) +// sheet->AddLongRadioButton("1280x960"); + #else +// resolution = sheet->AddFirstLongRadioButton("512x384"); +// sheet->AddLongRadioButton("640x480"); + resolution = sheet->AddFirstLongRadioButton("640x480"); + extern int Glide_num_texelfx; + if(Glide_num_texelfx > 1) { + sheet->AddLongRadioButton("800x600"); + sheet->AddLongRadioButton("960x720"); + } +// sheet->AddLongRadioButton("1024x768"); + #endif +#else + resolution = sheet->AddFirstLongRadioButton("512x384"); + sheet->AddLongRadioButton("640x480"); + sheet->AddLongRadioButton("800x600"); + sheet->AddLongRadioButton("960x720"); + sheet->AddLongRadioButton("1024x768"); + sheet->AddLongRadioButton("1280x960"); + sheet->AddLongRadioButton("1600x1200"); +#endif + *resolution = iTemp; + +#if !(defined(MACINTOSH)||defined(__LINUX__)) + // renderer bit depth + switch(Render_preferred_bitdepth){ + case 16: iTemp = 0; break; + case 32: iTemp = 1; break; + default: iTemp = -1; + } + + if ((PreferredRenderer==RENDERER_DIRECT3D || PreferredRenderer==RENDERER_OPENGL) && iTemp != -1) { + sheet->NewGroup(TXT_CFG_BITDEPTH, 200, 0); + bitdepth = sheet->AddFirstRadioButton(TXT_CFG_SIXTEENBIT); + sheet->AddRadioButton(TXT_CFG_THIRTYTWOBIT); + *bitdepth = iTemp; + } + else { + bitdepth = NULL; + } +#endif + // video settings + sheet->NewGroup(TXT_TOGGLES, 0,120); + filtering = sheet->AddLongCheckBox(TXT_BILINEAR, (Render_preferred_state.filtering!=0)); + mipmapping = sheet->AddLongCheckBox(TXT_MIPMAPPING, (Render_preferred_state.mipping!=0)); + + sheet->NewGroup(TXT_MONITOR, 0, 180); + vsync = sheet->AddLongCheckBox(TXT_CFG_VSYNCENABLED, (Render_preferred_state.vsync_on!=0)); +#ifndef __LINUX__ +#if !(defined( MACINTOSH ) && defined (USE_OPENGL)) + sheet->AddText(""); + sheet->AddLongButton(TXT_AUTO_GAMMA, IDV_AUTOGAMMA); +#endif +#endif + return sheet; + }; + +// retreive values from property sheet here. + void finish() + { + if (filtering) + Render_preferred_state.filtering = (*filtering) ? 1 : 0; + if (mipmapping) + Render_preferred_state.mipping = (*mipmapping) ? 1 : 0; + if (vsync) + Render_preferred_state.vsync_on = (*vsync) ? 1 : 0; +#if !(defined(MACINTOSH)||defined(__LINUX__)) + if (bitdepth) + Render_preferred_bitdepth = (*bitdepth)==1 ? 32 : 16; +#endif + if (GetScreenMode()==SM_GAME) { + Render_preferred_state.bit_depth = Render_preferred_bitdepth; + rend_SetPreferredState(&Render_preferred_state); + } + + if ((*resolution) != Game_video_resolution) { + // if in game, do resolution change. + int temp_w, temp_h; + int old_sm = GetScreenMode(); + + Game_video_resolution = *resolution; + + if (old_sm == SM_GAME) { + SetScreenMode(SM_NULL); + SetScreenMode(old_sm, true); + } + temp_w = Video_res_list[Game_video_resolution].width; + temp_h = Video_res_list[Game_video_resolution].height; + Current_pilot.set_hud_data(NULL, NULL, NULL, &temp_w, &temp_h); + } + + sheet = NULL; + }; + +// process + void process(int res) + { +#ifndef __LINUX__ +#if !(defined( MACINTOSH ) && defined (USE_OPENGL)) + switch (res) + { + case IDV_AUTOGAMMA: + config_gamma(); + break; + } +#endif +#endif + }; +}; + +#ifdef MACINTOSH +#pragma mark sound -- +#endif + + +////////////////////////////////////////////////////////////////// +// SOUND MENU +// +struct sound_menu +{ + newuiSheet *sheet; + newuiMenu *parent_menu; + int ls_sound_id, sound_id; + + short *fxvolume, *musicvolume; // volume sliders + short *fxquantity; // sound fx quantity limit + int *fxquality; // sfx quality low/high + + short old_fxquantity; + +#ifdef _DEBUG + int *sndmixer; +#endif + +// sets the menu up. + newuiSheet *setup(newuiMenu *menu) + { + tSliderSettings slider_set; + + sheet = menu->AddOption(IDV_SCONFIG, TXT_OPTSOUND, NEWUIMENU_MEDIUM); + parent_menu = menu; + ls_sound_id = -1; + sound_id = FindSoundName("Menu Slider Click"); + ASSERT(sound_id != -1); //DAJ -1FIX + + // volume sliders + sheet->NewGroup(NULL, 0,0); + + slider_set.type = SLIDER_UNITS_PERCENT; + fxvolume = sheet->AddSlider(TXT_SOUNDVOL, 10, (short)(Sound_system.GetMasterVolume()*10), &slider_set); + + slider_set.type = SLIDER_UNITS_PERCENT; + musicvolume = sheet->AddSlider(TXT_SNDMUSVOL, 10, (short)(D3MusicGetVolume()*10), &slider_set); + + // sound fx quality radio list. + if (GetFunctionMode() != GAME_MODE && GetFunctionMode() != EDITOR_GAME_MODE) { + sheet->NewGroup(TXT_SNDQUALITY, 0, 95); +#ifdef MACINTOSH + fxquality = sheet->AddFirstRadioButton(TXT_LOW); + sheet->AddRadioButton(TXT_CFG_MEDIUM); + sheet->AddRadioButton(TXT_CFG_HIGH); + *fxquality = Sound_system.GetSoundQuality(); +#else + fxquality = sheet->AddFirstRadioButton(TXT_SNDNORMAL); + sheet->AddRadioButton(TXT_SNDHIGH); + *fxquality = Sound_system.GetSoundQuality()==SQT_HIGH ? 1 : 0; +#endif + slider_set.min_val.i = MIN_SOUNDS_MIXED; + slider_set.max_val.i = MAX_SOUNDS_MIXED; + slider_set.type = SLIDER_UNITS_INT; + fxquantity = sheet->AddSlider(TXT_SNDCFG_SFXQUANTITY, (slider_set.max_val.i-slider_set.min_val.i), + Sound_system.GetLLSoundQuantity()-MIN_SOUNDS_MIXED, &slider_set); + old_fxquantity = (Sound_system.GetLLSoundQuantity()-MIN_SOUNDS_MIXED); + + } + else { + fxquality = NULL; + fxquantity = NULL; + } + + #if defined( _DEBUG ) && !defined ( MACINTOSH ) + int iTemp; + // add sound stats group + sheet->NewGroup("MIXER SETTINGS", 180,85); + switch (Sound_system.GetSoundMixer()) + { + case SOUND_MIXER_NONE:iTemp = 0; break; + case SOUND_MIXER_SOFTWARE_16: iTemp = 1; break; + case SOUND_MIXER_DS_8: iTemp = 2; break; + case SOUND_MIXER_DS_16: iTemp = 3; break; + case SOUND_MIXER_DS3D_16: iTemp = 4; break; + case SOUND_MIXER_AUREAL: iTemp = 5; break; + case SOUND_MIXER_CREATIVE_EAX: iTemp = 6; break; + default: Int3(); //-wtf? + } + sndmixer = sheet->AddFirstRadioButton("NONE"); + sheet->AddRadioButton("SOFTWARE"); + sheet->AddRadioButton("DS 8"); + sheet->AddRadioButton("DS 16"); + sheet->AddRadioButton("DS 3D"); + sheet->AddRadioButton("A3D 2"); + sheet->AddRadioButton("EAX"); + *sndmixer = iTemp; + #endif + + return sheet; + }; + +// retreive values from property sheet here. + void finish() + { + int iTemp; + char mixer_type=SOUND_MIXER_NONE; + + #if defined( _DEBUG ) && !defined ( MACINTOSH ) + iTemp = *sndmixer; + + switch (iTemp) + { + case 0: mixer_type = SOUND_MIXER_NONE; break; + case 1: mixer_type = SOUND_MIXER_SOFTWARE_16; break; + case 2: mixer_type = SOUND_MIXER_DS_8; break; + case 3: mixer_type = SOUND_MIXER_DS_16; break; + case 4: mixer_type = SOUND_MIXER_DS3D_16; break; + case 5: mixer_type = SOUND_MIXER_AUREAL; break; + case 6: mixer_type = SOUND_MIXER_CREATIVE_EAX; break; + default: Int3(); // -wtf? + } + + if (Sound_system.GetSoundMixer() ==SOUND_MIXER_NONE) { + Sound_system.InitSoundLib(Descent, mixer_type, Sound_quality, false); + } + else { + Sound_system.SetSoundMixer(mixer_type); + } + #endif + + Sound_system.SetMasterVolume((*fxvolume)/10.0f); + D3MusicSetVolume((*musicvolume)/10.0f); + + if (fxquantity) { + mprintf((1, "oldquant %d newquant %d\n", old_fxquantity, *fxquantity)); + if (old_fxquantity != (*fxquantity)) { + Sound_system.SetLLSoundQuantity((*fxquantity)+MIN_SOUNDS_MIXED); + } + } + + if (fxquality) { +#ifdef MACINTOSH + Sound_system.SetSoundQuality(*fxquality); +#else + Sound_system.SetSoundQuality((*fxquality==1) ? SQT_HIGH : SQT_NORMAL); +#endif + } + +//@@ iTemp = (*powerupvoices); +//@@ if (iTemp == 2) { +//@@ PlayVoices = true; +//@@ PlayPowerupVoice = true; +//@@ } +//@@ else if (iTemp == 1) { +//@@ PlayPowerupVoice = false; +//@@ PlayVoices = true; +//@@ } +//@@ else { +//@@ PlayVoices = false; +//@@ PlayPowerupVoice = false; +//@@ } + }; + +// process output and do stuff accordintly + void process(int res) + { + if (sheet->HasChanged(fxvolume)) { + Sound_system.SetMasterVolume((*fxvolume)/10.0f); + Sound_system.BeginSoundFrame(false); + Sound_system.StopSoundImmediate(ls_sound_id); + ls_sound_id = Sound_system.Play2dSound(sound_id); + Sound_system.EndSoundFrame(); + } + if (sheet->HasChanged(musicvolume)) { + D3MusicSetVolume((*musicvolume)/10.0f); + } + }; +}; + + +#ifdef MACINTOSH +#pragma mark toggles -- +#endif + +////////////////////////////////////////////////////////////////// +// GENERAL SETTINGS (TOGGLES) MENU +// + +#define UID_SHORTCUT_JOYSETTINGS 0x1000 +#define UID_SHORTCUT_KEYSETTINGS 0x1001 +#define UID_SHORTCUT_FORCEFEED 0x1002 + +struct toggles_menu +{ + newuiSheet *sheet; + newuiMenu *parent_menu; + + int *terrain_autolevel; // auto leveling radios + int *mine_autolevel; + int *missile_view; // missile view radio + bool *joy_enabled, *mse_enabled; + bool *reticle_toggle, *guided_toggle; + bool *shipsnd_toggle; + +// sets the menu up. + newuiSheet *setup(newuiMenu *menu) + { + sheet = menu->AddOption(IDV_GCONFIG, TXT_OPTGENERAL, NEWUIMENU_MEDIUM); + parent_menu = menu; + bool ff_found = false; + + sheet->NewGroup(TXT_TERRAUTOLEV, 0, 0); + terrain_autolevel = sheet->AddFirstRadioButton(TXT_NONE); + sheet->AddRadioButton(TXT_CFG_MEDIUM); + sheet->AddRadioButton(TXT_CFG_HIGH); + *terrain_autolevel = Default_player_terrain_leveling; + + sheet->NewGroup(TXT_MINEAUTOLEV, 0,70); + mine_autolevel = sheet->AddFirstRadioButton(TXT_NONE); + sheet->AddRadioButton(TXT_CFG_MEDIUM); + sheet->AddRadioButton(TXT_CFG_HIGH); + *mine_autolevel = Default_player_room_leveling; + + sheet->NewGroup(TXT_MISSILEVIEW, 0, 140); + missile_view = sheet->AddFirstRadioButton(TXT_NONE); + sheet->AddRadioButton(TXT_LEFT); +//@@ sheet->AddRadioButton(TXT_CENTER); + sheet->AddRadioButton(TXT_RIGHT); +//@@ *missile_view = (Missile_camera_window==SVW_LEFT) ? 1 : (Missile_camera_window==SVW_CENTER) ? 2 : (Missile_camera_window==SVW_RIGHT) ? 3 : 0; + *missile_view = (Missile_camera_window==SVW_LEFT) ? 1 : (Missile_camera_window==SVW_RIGHT) ? 2 : 0; + +#ifndef MACINTOSH + sheet->NewGroup(TXT_CONTROL_TOGGLES, 110, 0); + joy_enabled = sheet->AddLongCheckBox(TXT_JOYENABLED); + mse_enabled = sheet->AddLongCheckBox(TXT_CFG_MOUSEENABLED); + *joy_enabled = CHECK_FLAG(Current_pilot.read_controller, READF_JOY) ? true : false; + *mse_enabled = CHECK_FLAG(Current_pilot.read_controller, READF_MOUSE) ? true : false; + + sheet->NewGroup(TXT_TOGGLES, 110, 70); + reticle_toggle = sheet->AddLongCheckBox(TXT_TOG_SHOWRETICLE); + guided_toggle = sheet->AddLongCheckBox(TXT_TOG_GUIDEDMISSILE); + shipsnd_toggle = sheet->AddLongCheckBox(TXT_TOG_SHIPSOUNDS); + *reticle_toggle = Game_toggles.show_reticle; + *guided_toggle = Game_toggles.guided_mainview; + *shipsnd_toggle = Game_toggles.ship_noises; + + ddio_ff_GetInfo(&ff_found, NULL); + sheet->NewGroup(TXT_ADJUSTCONTROLSETTINGS, 110, 140); + sheet->AddLongButton(TXT_JOYSENSBTN, UID_SHORTCUT_JOYSETTINGS); + sheet->AddLongButton(TXT_KEYRAMPING, UID_SHORTCUT_KEYSETTINGS); + + if (ff_found) { + sheet->AddLongButton(TXT_CFG_CONFIGFORCEFEEDBACK, UID_SHORTCUT_FORCEFEED); + } +#else + sheet->NewGroup(TXT_TOGGLES, 110, 40); + reticle_toggle = sheet->AddLongCheckBox(TXT_TOG_SHOWRETICLE); + guided_toggle = sheet->AddLongCheckBox(TXT_TOG_GUIDEDMISSILE); + shipsnd_toggle = sheet->AddLongCheckBox(TXT_TOG_SHIPSOUNDS); + *reticle_toggle = Game_toggles.show_reticle; + *guided_toggle = Game_toggles.guided_mainview; + *shipsnd_toggle = Game_toggles.ship_noises; +#endif + return sheet; + }; + +// retreive values from property sheet here. + void finish() + { + int iTemp; + + Default_player_terrain_leveling = *terrain_autolevel; + Default_player_room_leveling = *mine_autolevel; + + iTemp =(*missile_view); +//@@ Missile_camera_window = (iTemp==1) ? SVW_LEFT : (iTemp==2) ? SVW_CENTER : (iTemp==3) ? SVW_RIGHT : -1; + Missile_camera_window = (iTemp==1) ? SVW_LEFT : (iTemp==2) ? SVW_RIGHT : -1; +#ifndef MACINTOSH + Current_pilot.read_controller = (*joy_enabled) ? READF_JOY : 0; + Current_pilot.read_controller |= (*mse_enabled) ? READF_MOUSE : 0; +#endif + Game_toggles.show_reticle = (*reticle_toggle); + Game_toggles.guided_mainview = (*guided_toggle); + Game_toggles.ship_noises = (*shipsnd_toggle); + }; + +// process + void process(int res) + { + switch (res) + { + case UID_SHORTCUT_JOYSETTINGS: + case UID_SHORTCUT_FORCEFEED: + LoadControlConfig(); + joystick_settings_dialog(); + SaveControlConfig(); + break; + case UID_SHORTCUT_KEYSETTINGS: + LoadControlConfig(); + key_settings_dialog(); + SaveControlConfig(); + break; + } + }; +}; + +#ifdef MACINTOSH +#pragma mark HUD -- +#endif + + +////////////////////////////////////////////////////////////////// +// HUD CONFIG MENU +// +struct hud_menu +{ + newuiSheet *sheet; + newuiMenu *parent_menu; + + int *ship_status; + int *shield_energy; + int *weapon_loads; + int *afterburner; + int *inventory; + int *warnings; + int *countermeasures; +// int *goals; + + int add_hud_option(const char *title, int **ptr, int y, int sel, bool graphical) + { + const int y_line = 29; + + sheet->NewGroup(title,0,y,NEWUI_ALIGN_HORIZ); + *ptr = sheet->AddFirstRadioButton(TXT_OFF); + if (graphical) { + sheet->AddRadioButton(TXT_TEXT); + sheet->AddRadioButton(TXT_GRAPHICAL); + } + else { + sheet->AddRadioButton(TXT_ON); + } + *(*ptr) = sel; + return y+y_line; + }; + +// sets the menu up. + newuiSheet *setup(newuiMenu *menu) + { + int y, sel; + ushort stat,grstat; + + sheet = menu->AddOption(IDV_HCONFIG, TXT_OPTHUD, NEWUIMENU_MEDIUM); + parent_menu = menu; + + Current_pilot.get_hud_data(NULL, &stat, &grstat); + + sel = (stat & STAT_SHIP) ? 1 : (grstat & STAT_SHIP) ? 2 : 0; + y = add_hud_option(TXT_HUDSHIPSTATUS,&ship_status,0,sel,true); + + sel = (stat & (STAT_SHIELDS+STAT_ENERGY)) ? 1 : (grstat & (STAT_SHIELDS+STAT_ENERGY)) ? 2 : 0; + y = add_hud_option(TXT_HUDSHIELDENERGY,&shield_energy, y, sel, true); + + sel = (stat & (STAT_PRIMARYLOAD+STAT_SECONDARYLOAD)) ? 1 : (grstat & (STAT_PRIMARYLOAD+STAT_SECONDARYLOAD)) ? 2 : 0; + y = add_hud_option(TXT_HUDWEAPONS, &weapon_loads, y, sel, true); + + sel = (stat & STAT_AFTERBURN) ? 1 : (grstat & STAT_AFTERBURN) ? 2 : 0; + y = add_hud_option(TXT_HUDAFTERBURN, &afterburner, y, sel, true); + + sel = (stat & STAT_WARNING) ? 1 : (grstat & STAT_WARNING) ? 2 : 0; + y = add_hud_option(TXT_HUDWARNINGS, &warnings, y, sel, true); + + sel = (stat & STAT_CNTRMEASURE) ? 1 : (grstat & STAT_CNTRMEASURE) ? 2 : 0; + y = add_hud_option(TXT_HUDCNTRMEASURE, &countermeasures, y, sel, true); + +// sel = (stat & STAT_GOALS) ? 1 : 0; +// y = add_hud_option(TXT_HUDMISSIONSTATUS, &goals, y, sel, false); + + sel = (stat & STAT_INVENTORY) ? 1 : 0; + y = add_hud_option(TXT_HUDINVENTORY, &inventory, y, sel, false); + + return sheet; + }; + +// retreive values from property sheet here. + void finish() + { + int sel; + ushort hud_new_stat = STAT_MESSAGES+STAT_CUSTOM; + ushort hud_new_grstat = 0; + + sel = *ship_status; + if (sel==1) hud_new_stat |= STAT_SHIP; + else if (sel==2) hud_new_grstat |= STAT_SHIP; + sel = *shield_energy; + if (sel==1) hud_new_stat |= (STAT_SHIELDS+STAT_ENERGY); + else if (sel==2) hud_new_grstat |= (STAT_SHIELDS+STAT_ENERGY); + sel = *weapon_loads; + if (sel==1) hud_new_stat |= (STAT_PRIMARYLOAD+STAT_SECONDARYLOAD); + else if (sel==2) hud_new_grstat |= (STAT_PRIMARYLOAD+STAT_SECONDARYLOAD); + sel = *afterburner; + if (sel==1) hud_new_stat |= STAT_AFTERBURN; + else if (sel==2) hud_new_grstat |= STAT_AFTERBURN; + sel = *warnings; + if (sel==1) hud_new_stat |= STAT_WARNING; + else if (sel==2) hud_new_grstat |= STAT_WARNING; + sel = *countermeasures; + if (sel==1) hud_new_stat |= STAT_CNTRMEASURE; + else if (sel==2) hud_new_grstat |= STAT_CNTRMEASURE; +// sel = *goals; +// if (sel==1) hud_new_stat |= STAT_GOALS; + sel = *inventory; + if (sel==1) hud_new_stat |= STAT_INVENTORY; + + Current_pilot.set_hud_data(NULL, &hud_new_stat, &hud_new_grstat); + + //mprintf((0, "pilot hud stat=%x\n", Current_pilot.hud_stat)); + //mprintf((0, "pilot hud grstat=%x\n", Current_pilot.hud_graphical_stat)); + + // modify current hud stats if in game. + if ((GetFunctionMode()==EDITOR_GAME_MODE || GetFunctionMode()==GAME_MODE) && GetHUDMode()==HUD_FULLSCREEN) { + SetHUDState(hud_new_stat | STAT_SCORE | (Hud_stat_mask & STAT_FPS), hud_new_grstat); + } + }; +}; + +#ifdef MACINTOSH +#pragma mark details -- +#endif + + +////////////////////////////////////////////////////////////////// +// DETAILS MENU +// +struct details_menu +{ + newuiSheet *sheet; + newuiMenu *parent_menu; + + int *detail_level; // detail level radio + int *objcomp; // object complexity radio + bool *specmap, *headlight, *mirror, // check boxes + *dynamic, *fog, *coronas, *procedurals, + *powerup_halo, *scorches, *weapon_coronas; + short *pixel_err, // 0-27 (1-28) + *rend_dist; // 0-120 (80-200) + + int *texture_quality; + +// sets the menu up. + newuiSheet *setup(newuiMenu *menu) + { + int iTemp; + sheet = menu->AddOption(IDV_DCONFIG, TXT_OPTDETAIL, NEWUIMENU_MEDIUM); + parent_menu = menu; + + // detail level radio + Database->read_int("PredefDetailSetting",&Default_detail_level); + iTemp = Default_detail_level; + sheet->NewGroup(TXT_CFG_PRESETDETAILS, 0,0); + detail_level = sheet->AddFirstRadioButton(TXT_LOW); + sheet->AddRadioButton(TXT_CFG_MEDIUM); + sheet->AddRadioButton(TXT_CFG_HIGH); + sheet->AddRadioButton(TXT_CFG_VERYHIGH); + sheet->AddRadioButton(TXT_CFG_CUSTOM); + *detail_level = iTemp; + + // toggles + sheet->NewGroup(TXT_TOGGLES,0,87); + specmap = sheet->AddLongCheckBox(TXT_SPECMAPPING, Detail_settings.Specular_lighting); + headlight = sheet->AddLongCheckBox(TXT_FASTHEADLIGHT, Detail_settings.Fast_headlight_on); + mirror = sheet->AddLongCheckBox(TXT_MIRRORSURF, Detail_settings.Mirrored_surfaces); + dynamic = sheet->AddLongCheckBox(TXT_DYNLIGHTING, Detail_settings.Dynamic_lighting); + fog = sheet->AddLongCheckBox(TXT_CFG_ENABLEFOG, Detail_settings.Fog_enabled); + coronas = sheet->AddLongCheckBox(TXT_CFG_ENABLELIGHTCORONA, Detail_settings.Coronas_enabled); + procedurals = sheet->AddLongCheckBox(TXT_CFG_PROCEDURALS, Detail_settings.Procedurals_enabled); + powerup_halo = sheet->AddLongCheckBox(TXT_CFG_POWERUPHALOS, Detail_settings.Powerup_halos); + scorches = sheet->AddLongCheckBox(TXT_CFG_SCORCHMARKS, Detail_settings.Scorches_enabled); + weapon_coronas = sheet->AddLongCheckBox(TXT_CFG_WEAPONEFFECTS, Detail_settings.Weapon_coronas_enabled); + + // sliders + tSliderSettings slider_set; + sheet->NewGroup(TXT_GEOMETRY, 90,0); + iTemp = MAXIMUM_TERRAIN_DETAIL - Detail_settings.Pixel_error - MINIMUM_TERRAIN_DETAIL; + if(iTemp<0) iTemp = 0; + slider_set.min_val.i = MINIMUM_TERRAIN_DETAIL; + slider_set.max_val.i = MAXIMUM_TERRAIN_DETAIL; + slider_set.type = SLIDER_UNITS_INT; + pixel_err = sheet->AddSlider(TXT_TERRDETAIL, MAXIMUM_TERRAIN_DETAIL-MINIMUM_TERRAIN_DETAIL, iTemp, &slider_set); + + slider_set.min_val.i = MINIMUM_RENDER_DIST/2; + slider_set.max_val.i = MAXIMUM_RENDER_DIST/2; + slider_set.type = SLIDER_UNITS_INT; + iTemp = (int)(Detail_settings.Terrain_render_distance/((float)TERRAIN_SIZE)) - MINIMUM_RENDER_DIST; + if (iTemp < 0) iTemp = 0; + rend_dist = sheet->AddSlider(TXT_RENDDIST, (MAXIMUM_RENDER_DIST-MINIMUM_RENDER_DIST)/2, iTemp/2, &slider_set); + + // object complexity radio + sheet->NewGroup(TXT_CFG_OBJECTCOMPLEXITY, 174, 87); + objcomp = sheet->AddFirstRadioButton(TXT_LOW); + sheet->AddRadioButton(TXT_CFG_MEDIUM); + sheet->AddRadioButton(TXT_CFG_HIGH); + *objcomp = Detail_settings.Object_complexity; + +#ifdef MACINTOSH + if (GetFunctionMode() != GAME_MODE && GetFunctionMode() != EDITOR_GAME_MODE) { + sheet->NewGroup(TXT_TEXTURE_QUALITY, 174,152); + Database->read_int("RS_texture_quality",&Render_state.cur_texture_quality); + iTemp = Render_state.cur_texture_quality; + texture_quality = sheet->AddFirstRadioButton(TXT_LOW); + sheet->AddRadioButton(TXT_CFG_MEDIUM); + sheet->AddRadioButton(TXT_CFG_HIGH); + *texture_quality = Render_state.cur_texture_quality; + } +#endif + return sheet; + }; + +// retreive values from property sheet here. + void finish() + { + Detail_settings.Coronas_enabled = *coronas; + Detail_settings.Dynamic_lighting = *dynamic; + Detail_settings.Fast_headlight_on = *headlight; + Detail_settings.Fog_enabled = *fog; + Detail_settings.Mirrored_surfaces = *mirror; + Detail_settings.Object_complexity = *objcomp; + Detail_settings.Pixel_error = MAXIMUM_TERRAIN_DETAIL - ((*pixel_err)+MINIMUM_TERRAIN_DETAIL); + Detail_settings.Powerup_halos = *powerup_halo; + Detail_settings.Procedurals_enabled = *procedurals; + Detail_settings.Scorches_enabled = *scorches; + Detail_settings.Specular_lighting = *specmap; + Detail_settings.Terrain_render_distance = (((*rend_dist)*2)+MINIMUM_RENDER_DIST)*((float)TERRAIN_SIZE); + Detail_settings.Weapon_coronas_enabled = *weapon_coronas; + + Default_detail_level = *detail_level; + Database->write("PredefDetailSetting",Default_detail_level); + +#ifdef MACINTOSH + if (GetFunctionMode() != GAME_MODE && GetFunctionMode() != EDITOR_GAME_MODE) { + Render_state.cur_texture_quality = *texture_quality; + Database->write("RS_texture_quality",Render_state.cur_texture_quality); + + if(Render_state.cur_texture_quality == 0) { + Mem_low_memory_mode = true; + Mem_superlow_memory_mode = true; + } else if(Render_state.cur_texture_quality == 1) { + Mem_low_memory_mode = true; + Mem_superlow_memory_mode = false; + } else if(Render_state.cur_texture_quality == 2) { + Mem_low_memory_mode = false; + Mem_superlow_memory_mode = false; + } + } + InitVisEffects(); //reset max_vis_effects l +#endif + sheet = NULL; + }; + +// process output and do stuff accordintly + void process(int res) + { + bool changed; + + // check here if the detail level currently set should be custom + changed = sheet->HasChanged(specmap) || + sheet->HasChanged(headlight) || + sheet->HasChanged(mirror) || + sheet->HasChanged(dynamic) || + sheet->HasChanged(fog) || + sheet->HasChanged(coronas) || + sheet->HasChanged(procedurals) || + sheet->HasChanged(powerup_halo) || + sheet->HasChanged(scorches) || + sheet->HasChanged(weapon_coronas) || + sheet->HasChanged(objcomp) || + sheet->HasChanged(pixel_err) || + sheet->HasChanged(rend_dist); + + if (changed) { + // enable custom radio button + *detail_level = DETAIL_LEVEL_CUSTOM; + } + else { + // check if any preset detail has been selected. + if (sheet->HasChanged(detail_level)) { + set_preset_details(*detail_level); + } + } + + }; + +// sets detail presets + void set_preset_details(int setting); +}; + + +// sets detail presets +void details_menu::set_preset_details(int setting) +{ + tDetailSettings ds; + + switch(setting) + { + case DETAIL_LEVEL_LOW: memcpy(&ds,&DetailPresetLow,sizeof(tDetailSettings)); break; + case DETAIL_LEVEL_MED: memcpy(&ds,&DetailPresetMed,sizeof(tDetailSettings)); break; + case DETAIL_LEVEL_HIGH: memcpy(&ds,&DetailPresetHigh,sizeof(tDetailSettings)); break; + case DETAIL_LEVEL_VERY_HIGH: memcpy(&ds,&DetailPresetVHi,sizeof(tDetailSettings)); break; + default: + return; + }; + +//now go through all the config items and set to the new values + int iTemp; + + iTemp = MAXIMUM_TERRAIN_DETAIL - ds.Pixel_error - MINIMUM_TERRAIN_DETAIL; + if(iTemp<0) iTemp = 0; + *pixel_err = (short)(iTemp); + iTemp = (int)((ds.Terrain_render_distance/((float)TERRAIN_SIZE)) - MINIMUM_RENDER_DIST); + if(iTemp < 0) iTemp = 0; + iTemp = iTemp/2; + *rend_dist = (short)(iTemp); + *objcomp = ds.Object_complexity; + *specmap = ds.Specular_lighting; + *headlight = ds.Fast_headlight_on; + *mirror = ds.Mirrored_surfaces; + *dynamic = ds.Dynamic_lighting; + *fog = ds.Fog_enabled; + *coronas = ds.Coronas_enabled; + *procedurals = ds.Procedurals_enabled; + *powerup_halo = ds.Powerup_halos; + *scorches = ds.Scorches_enabled; + *weapon_coronas = ds.Weapon_coronas_enabled; +} + + + +////////////////////////////////////////////////////////////////// +// new Gamma menu +// + + + +////////////////////////////////////////////////////////////////// +// new Options menu +// + + +// externed from init.cpp +extern void SaveGameSettings(); + + +void OptionsMenu() +{ + newuiMenu menu; + video_menu video; + details_menu details; + sound_menu sound; + toggles_menu toggles; + hud_menu hud; + + int res=-1, state=0; // state = 0, options menu, 1 = controller config, 2 = quitting. + + while (state != 2) + { + if (state == 1) { + // enter controller config menu + mprintf((0, "CONTROLLER CONFIG MENU HERE!\n")); + CtlConfig(CTLCONFIG_KEYBOARD); + state = 0; // goto options menu. + } + else { + // open menu + + DoWaitMessage(true); + + menu.Create(); + menu.Open(); + + video.setup(&menu); // setup video menu IDV_VCONFIG + details.setup(&menu); // setup details menu. IDV_DCONFIG + sound.setup(&menu); // setup sound menu. IDV_SCONFIG + toggles.setup(&menu); // setup general menu. IDV_GCONFIG + hud.setup(&menu); // setup hud menu. IDV_HCONFIG + + menu.AddSimpleOption(IDV_CCONFIG,TXT_OPTCONFIG); + menu.AddSimpleOption(UID_CANCEL, TXT_DONE); + + menu.SetCurrentOption(IDV_VCONFIG); + + DoWaitMessage(false); + + // run menu + do + { + res = menu.DoUI(); + + // immediate checking of any option ids. + if (res == UID_CANCEL || res == NEWUIRES_FORCEQUIT) { + state = 2; break; // next pass will quit + } + else if (res == IDV_CCONFIG) { + state = 1; break; // next pass will enter controller menu + } + + // handle any processing needed by current option. + switch (menu.GetCurrentOption()) + { + case IDV_DCONFIG: + details.process(res); + break; + case IDV_SCONFIG: + sound.process(res); + break; + case IDV_VCONFIG: + video.process(res); + break; + case IDV_GCONFIG: + toggles.process(res); + break; + } + } + while (1); + + // get settings + hud.finish(); + toggles.finish(); + sound.finish(); + details.finish(); + video.finish(); + + // write them out and close. + PltWriteFile(&Current_pilot); + + menu.Close(); + menu.Destroy(); + } + } + + SaveGameSettings(); +} + + diff --git a/Descent3/config.h b/Descent3/config.h new file mode 100644 index 000000000..9798ac6d0 --- /dev/null +++ b/Descent3/config.h @@ -0,0 +1,192 @@ +/* +* $Logfile: /DescentIII/Main/config.h $ +* $Revision: 28 $ +* $Date: 10/22/99 10:42a $ +* $Author: Matt $ +* +* Header file for configuration functions (video, sound, etc) +* +* $Log: /DescentIII/Main/config.h $ + * + * 28 10/22/99 10:42a Matt + * Mac merge + * + * 27 6/08/99 1:00p Jason + * changes for bumpmapping + * + * 26 4/24/99 8:39p Samir + * added ship sounds toggle. + * + * 25 3/22/99 4:26p Samir + * added toggles for guided missile view and reticle. + * + * 24 2/28/99 3:22a Samir + * reformatted video menu. + * + * 23 2/16/99 12:05p Samir + * revamped ui for config menus. + * + * 22 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 21 12/09/98 5:28p Jason + * added more options + * + * 20 11/10/98 5:14p Jeff + * added Force Feedback configuration + * + * 19 10/19/98 7:08p Jeff + * moved preset detail defines to config.cpp and updated them + * + * 18 10/19/98 6:30p Jeff + * changes made for detail variables. Put in preset values. Preset + * options. Removed terrain_cast from detail. Put new callbacks in + * UIListBox and UISlider + * + * 17 10/15/98 1:33p Jeff + * removed amount of debris detail level. Added powerup halo detail + * level. Added predef detail level settings + * + * 16 10/12/98 3:01p Jeff + * added vsync and more detail level settings + * + * 15 8/27/98 6:23p Jeff + * changed autoleveling in config so it is a slider...had to convert + * global from bool->ubyte. Added fast headlight and mirrored surfaces to + * config menu + * + * 14 8/19/98 2:19p Jeff + * moved detail globals to a struct + * + * 13 6/19/98 5:40p Samir + * added hud configuration. + * + * 12 5/23/98 6:34p Jeff + * All options menus are up-to-date, added Detail Level menu + * + * 11 5/06/98 4:32p Samir + * screen res mode changes. + * + * 10 5/06/98 3:29p Jeff + * changed whats needed for screen resolution change + * + * 9 4/06/98 4:57p Jeff + * Added a dynamic lighting box in video config + * + * 8 3/05/98 11:32a Jeff + * + * 7 3/02/98 7:19p Jeff + * Added Gamma slider support + * + * 6 3/02/98 3:29p Jeff + * Convert d3.cfg from a binary to a text file + * + * 5 2/27/98 5:35p Jeff + * Added in loading and saving of a game configuration file + * + * 4 2/24/98 3:44p Jeff + * Basic structure, including some components added + * + * 3 2/23/98 5:35p Jeff + * General shell created + * + * 2 2/23/98 5:16p Jeff + * Initial creation +* +* $NoKeywords: $ +*/ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +//Main menu configuration functions +// ------------------------------------------------------ +// ConfigForceFeedback +// Purpose: +// Configures your Force Feedback device on your computer +// ------------------------------------------------------ +void ConfigForceFeedback(void); + + +// General option toggles +typedef struct tGameToggles +{ + bool show_reticle; + bool guided_mainview; + bool ship_noises; +} +tGameToggles; + +extern tGameToggles Game_toggles; + +// this list should match the list in config.cpp to work. +#ifdef MACINTOSH + #define N_SUPPORTED_VIDRES 3 + + #define RES_640X480 0 + #define RES_800X600 1 + #define RES_960X720 2 + #define RES_1024X768 3 +#else + #define N_SUPPORTED_VIDRES 8 + + #define RES_512X384 0 + #define RES_640X480 1 + #define RES_800X600 2 + #define RES_960X720 3 + #define RES_1024X768 4 + #define RES_1280X960 5 + #define RES_1600X1200 6 +#endif +// stored resolution list and desired game resolution +typedef struct tVideoResolution +{ + ushort width; + ushort height; +} +tVideoResolution; + +extern tVideoResolution Video_res_list[]; +extern int Game_video_resolution; + + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +//KEEP THESE MEMBERS IN THE SAME ORDER, IF YOU ADD,REMOVE, OR CHANGE ANYTHING IN THIS STRUCT, MAKE SURE YOU +//UPDATE DetailPresetLow,DetailPresetMed,DetailPresetHigh AND DetailPresetVHi IN CONFIG.CPP +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +typedef struct tDetailSettings { + float Terrain_render_distance; //VisibleTerrainDistance + float Pixel_error; //PixelErrorTolerance + bool Specular_lighting; //DoSpecularPass + bool Dynamic_lighting; //Enable_dynamic_lighting + bool Fast_headlight_on; + bool Mirrored_surfaces; + bool Fog_enabled; + bool Coronas_enabled; + bool Procedurals_enabled; + bool Powerup_halos; + bool Scorches_enabled; + bool Weapon_coronas_enabled; + bool Bumpmapping_enabled; + ubyte Specular_mapping_type; + ubyte Object_complexity; //0 = low, 1 = medium, 2=high +} tDetailSettings; + + +// Call this with one of the above defines to set the detail level to a predefined set (custom level is ignored) +void ConfigSetDetailLevel(int level); +// returns the current detail level that the given tDetailSettings is at +int ConfigGetDetailLevel(tDetailSettings *ds); + +#define DETAIL_LEVEL_LOW 0 +#define DETAIL_LEVEL_MED 1 +#define DETAIL_LEVEL_HIGH 2 +#define DETAIL_LEVEL_VERY_HIGH 3 +#define DETAIL_LEVEL_CUSTOM 4 + +extern tDetailSettings Detail_settings; +extern int Default_detail_level; + +// displays new options menu +extern void OptionsMenu(); +#endif \ No newline at end of file diff --git a/Descent3/controls.h b/Descent3/controls.h new file mode 100644 index 000000000..10ec4d72f --- /dev/null +++ b/Descent3/controls.h @@ -0,0 +1,326 @@ +/* + * $Logfile: /DescentIII/Main/controls.h $ + * $Revision: 36 $ + * $Date: 10/21/99 6:04p $ + * $Author: Matt $ + * + * Game controls header + * + * $Log: /DescentIII/Main/controls.h $ + * + * 36 10/21/99 6:04p Matt + * Mac merge + * + * 35 7/15/99 5:39p Samir + * upped max joystick sensitivity to 4.0 from 2.0. + * + * 34 4/30/99 10:52p Samir + * fixed energy-to-shield usage and made Load and Save COnfig take a + * pilot. + * + * 33 4/24/99 6:29p Jason + * changed mouse sensitivity + * + * 32 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 31 2/26/99 2:13a Samir + * added audio taunt keys. + * + * 30 2/16/99 12:07p Samir + * redid controller config with new ui. + * + * 29 1/13/99 6:37a Jeff + * fixed object.h. There were numerous struct declarations that were the + * same name as the instance of the struct (gcc doesn't like this). + * Changed the struct name. Also added some #ifdef's for linux build, + * along with fixing case-sensitive includes + * + * 28 12/09/98 1:09p Jason + * second draft of automap + * + * 27 12/02/98 11:39a Samir + * added better code to handle changes in controller function list for + * pilot files (needed to have a constant giving number of controller + * functions in demo 1.0) + * + * 26 11/30/98 4:54p Samir + * added rear view config item. + * + * 25 11/30/98 1:09p Samir + * added rear view toggle. + * + * 24 10/21/98 10:36a Samir + * added code to turn on or off joystick or mouse. + * + * 23 10/08/98 10:52a Samir + * added countermeasure and weapon cycling. + * + * 22 9/17/98 3:24p Samir + * added headlight configuration. + * + * 21 8/31/98 6:49p Jeff + * made inventory and countermeasure keys customizable + * + * 20 8/31/98 12:39p Samir + * added code to resume controllers when restoring game mode. + * + * 19 6/18/98 4:44p Samir + * added changes for multiple configs for joystick controls. + * + * 18 5/19/98 3:36p Samir + * added automap key. + * + * 17 5/12/98 3:04p Samir + * added button control for pbh. + * + * 16 4/27/98 7:24p Samir + * poll controller at specified rate. + * + * 15 4/09/98 2:23p Jason + * added guided/afterburner stuff + * + * 14 4/08/98 3:44p Samir + * added toggle bank and configuration for afterburner + * + * 13 4/02/98 7:58p Samir + * Fixed up control setting saving and restoring. + * + * 12 3/13/98 8:55p Jeff + * Various changes to move control configuration into Pilot file + * + * 11 2/16/98 7:15p Matt + * Added fire_secondary_down_state + * + * 10 2/16/98 3:01p Samir + * Added fire_primary_down_state and changed some configuration. + * + * 9 2/15/98 7:06p Samir + * Added functions to save controller state. + * + * 8 2/12/98 8:48p Matt + * Changed controls system to keep the reading of the controls seperate + * from using the results. Got rid of the Controls global. + * + * 7 1/12/98 6:28p Samir + * New keys for sliding and use RCTRL for firing too. + * + * 6 12/05/97 10:59a Samir + * Somewhat revised controls for keyboard. Joystick now has variable + * sensitivity. + * + * 5 12/03/97 7:32p Samir + * More D2 like keyboard movement. + * + * 4 11/05/97 3:45p Samir + * InitControls will close the previous call to InitControls. + * + * 3 10/30/97 4:02p Matt + * Added the flare + * + * 2 8/29/97 1:49p Samir + * Added toggliing slide. + * + * 8 4/24/97 12:10p Samir + * Added weapon firing for primary and secondary weapons. + * + * 7 4/23/97 1:06p Samir + * Implemented Suspend and Resume Controls as well as newer control system + * + * 6 4/17/97 2:48p Samir + * ReadFlyingControls renamed from read_flying_controls to match naming + * convention rules. + * + * 5 4/16/97 1:03p Samir + * Added fire primary button information + * + * 4 4/14/97 3:29p Samir + * Implemented game controls structure. + * + * 3 4/14/97 12:55p Samir + * Added primary fire button + * + * 2 4/11/97 2:15p Samir + * game controls functions defined for movement. + * + * $NoKeywords: $ + */ + +#ifndef CONTROLS_H +#define CONTROLS_H + +#include "object.h" +#include "Controller.h" + +class gameController; +class pilot; + +#define READF_MOUSE 0x2 +#define READF_JOY 0x1 +#ifdef MACINTOSH +#define JOY_AXIS_SENS_RANGE 2.0f +#define MSE_AXIS_SENS_RANGE 2.0f +#else +#define JOY_AXIS_SENS_RANGE 4.0f +#define MSE_AXIS_SENS_RANGE 4.0f +#endif + +// used to set or get axis sensitivity +#define HEADING_AXIS 0 +#define PITCH_AXIS 1 +#define BANK_AXIS 2 +#define HORIZONTAL_AXIS 3 +#define VERTICAL_AXIS 4 +#define THROTTLE_AXIS 5 + + +// Controller functions +const int NUM_CTLFUNCS_DEMOv1_0 = 63; +const int NUM_CONTROLLER_FUNCTIONS = 73; + +const int ctfFORWARD_THRUSTAXIS = 0, + ctfFORWARD_THRUSTKEY = 1, + ctfREVERSE_THRUSTKEY = 2, + ctfUP_THRUSTAXIS = 3, + ctfUP_THRUSTKEY = 4, + ctfDOWN_THRUSTKEY = 5, + ctfRIGHT_THRUSTAXIS = 6, + ctfRIGHT_THRUSTKEY = 7, + ctfLEFT_THRUSTKEY = 8, + ctfPITCH_DOWNAXIS = 9, + ctfPITCH_DOWNKEY = 10, + ctfPITCH_UPKEY = 11, + ctfBANK_RIGHTAXIS = 12, + ctfBANK_RIGHTKEY = 13, + ctfBANK_LEFTKEY = 14, + ctfHEADING_RIGHTAXIS = 15, + ctfHEADING_RIGHTKEY = 16, + ctfHEADING_LEFTKEY = 17, + ctfFIREPRIMARY_BUTTON = 18, + ctfFIREPRIMARY_KEY = 19, + ctfFIREPRIMARY_KEY2 = 20, + ctfFIRESECONDARY_BUTTON = 21, + ctfFIRESECONDARY_KEY = 22, + ctfTOGGLE_SLIDEKEY = 23, + ctfTOGGLE_SLIDEBUTTON = 24, + ctfFIREFLARE_KEY = 25, + ctfFIREFLARE_BUTTON = 26, + ctfUP_BUTTON = 27, + ctfDOWN_BUTTON = 28, + ctfLEFT_BUTTON = 29, + ctfRIGHT_BUTTON = 30, + ctfAFTERBURN_KEY = 31, + ctfAFTERBURN_BUTTON = 32, + ctfFORWARD_BUTTON = 33, + ctfREVERSE_BUTTON = 34, + ctfTOGGLE_BANKKEY = 35, + ctfTOGGLE_BANKBUTTON = 36, + ctfHEADING_LEFTBUTTON = 37, + ctfHEADING_RIGHTBUTTON = 38, + ctfPITCH_UPBUTTON = 39, + ctfPITCH_DOWNBUTTON = 40, + ctfBANK_LEFTBUTTON = 41, + ctfBANK_RIGHTBUTTON = 42, + ctfAUTOMAP_KEY = 43, + ctfPREV_INVKEY = 44, + ctfNEXT_INVKEY = 45, + ctfINV_USEKEY = 46, + ctfPREV_CNTMSKEY = 47, + ctfNEXT_CNTMSKEY = 48, + ctfCNTMS_USEKEY = 49, + ctfHEADLIGHT_KEY = 50, + ctfHEADLIGHT_BUTTON = 51, + ctfAUTOMAP_BUTTON = 52, + ctfPREV_INVBTN = 53, + ctfNEXT_INVBTN = 54, + ctfINV_USEBTN = 55, + ctfPREV_CNTMSBTN = 56, + ctfNEXT_CNTMSBTN = 57, + ctfCNTMS_USEBTN = 58, + ctfWPNSEL_PCYCLEKEY = 59, + ctfWPNSEL_PCYCLEBTN = 60, + ctfWPNSEL_SCYCLEKEY = 61, + ctfWPNSEL_SCYCLEBTN = 62, + ctfREARVIEW_KEY = 63, + ctfREARVIEW_BTN = 64, + ctfAUDIOTAUNT1_KEY = 65, + ctfAUDIOTAUNT1_BTN = 66, + ctfAUDIOTAUNT2_KEY = 67, + ctfAUDIOTAUNT2_BTN = 68, + ctfAUDIOTAUNT3_KEY = 69, + ctfAUDIOTAUNT3_BTN = 70, + ctfAUDIOTAUNT4_KEY = 71, + ctfAUDIOTAUNT4_BTN = 72; + + +typedef struct game_controls { +// movement values +// these values are from -1.0 to 1.0.- + float pitch_thrust; + float heading_thrust; + float bank_thrust; + float vertical_thrust; + float sideways_thrust; + float forward_thrust; + float afterburn_thrust; + +// these values modify thrust + bool toggle_slide; + bool toggle_bank; + +// this is for weapon control + int fire_primary_down_count; + bool fire_primary_down_state; + float fire_primary_down_time; + int fire_secondary_down_count; + bool fire_secondary_down_state; + float fire_secondary_down_time; + + //The flare + int fire_flare_down_count; + int rearview_down_count; + bool rearview_down_state; +} game_controls; + + +// This value should be set at initialization time. Use for remote controlling. +extern char *Controller_ip; + +// Controller object. +extern gameController *Controller; + +// determines if controller is being polled. +extern bool Control_poll_flag; + +// +extern ct_function Controller_needs[NUM_CONTROLLER_FUNCTIONS]; + +// initializes control system +void InitControls(); +void CloseControls(); + +// polls controller at a certain framerate. +void PollControls(); + +// suspends and or resumes the control system. this deactivates or reactivates any +// preemptive controller polling we setup. +void SuspendControls(); +void ResumeControls(); + +//Read the keyboard & other controllers. Fills in the specified structure. +void ReadPlayerControls(game_controls *controls); + +// reinits the controller, hence restoring default controller configurations +void RestoreDefaultControls(); + +// load in controller configurations +void LoadControlConfig(pilot *plt=NULL); + +// save controller configurations. +void SaveControlConfig(pilot *plt=NULL); + +// Reads movement only +void DoMovement(game_controls *controls); + +#endif \ No newline at end of file diff --git a/Descent3/credits.cpp b/Descent3/credits.cpp new file mode 100644 index 000000000..22e2569b4 --- /dev/null +++ b/Descent3/credits.cpp @@ -0,0 +1,526 @@ +/* +* $Logfile: /DescentIII/main/credits.cpp $ +* $Revision: 17 $ +* $Date: 10/05/99 3:00p $ +* $Author: Kevin $ +* +* Credits system +* +* $Log: /DescentIII/main/credits.cpp $ + * + * 17 10/05/99 3:00p Kevin + * No longer display static credits for demo + * + * 16 5/13/99 6:41p Samir + * don't reset mouse when ending credits. causes problems. + * + * 15 5/08/99 1:43p Jason + * made credits scroll a little faster + * + * 14 5/05/99 7:38p Jason + * added scrolling credits system + * + * 13 5/02/99 12:08a Jason + * changes to credits + * + * 12 4/30/99 5:42p Jason + * more updates to credits system + * + * 11 4/29/99 11:57a Samir + * added music. + * + * 10 4/19/99 12:46p Jason + * fixed credits + * + * 9 4/17/99 3:29p Jason + * first pass at credit system + * + * 8 4/15/99 2:32p Kevin + * Added some code for the Demo + * + * 7 4/14/99 4:19a Jeff + * more case mismatch fixes in #includes + * + * 6 4/13/99 12:24p Jason + * made credits warp faster + * + * 5 4/02/99 5:47p Jeff + * text effect in + * + * 4 4/02/99 6:03a Jeff + * testing a new text effect...not done yet + * + * 3 3/31/99 3:04p Jeff + * added _real_ temporary credits system... + * + * 2 3/31/99 12:11p Jeff + * added empty credits file +* +* $NoKeywords: $ +*/ + +#include "mono.h" +#include "pstypes.h" +#include "ddio.h" +#include "grtext.h" +#include "renderer.h" +#include "gamefont.h" +#include "game.h" +#include "bitmap.h" +#include "descent.h" +#include "mem.h" +#include "3d.h" +#include "d3music.h" +#include "hlsoundlib.h" +#include +#include +#include "psrand.h" + +#if defined(WIN32) +#include +#elif defined(__LINUX__) +#include "linux/linux_fix.h" +#endif + + +/* +$$TABLE_GAMEFILE "GameCredits.txt" +$$TABLE_GAMEFILE "credits.omf" +*/ + +// music regions for credits +#define CREDITS1_MUSIC_REGION 0 + +// other stuff +#define CLTYPE_HEADING 0 +#define CLTYPE_TEXT 1 +#define CLTYPE_END 2 +#define CLTYPE_BLANK 3 + +typedef struct creditline +{ + ddgr_color color; + ubyte type; + char *text; + creditline *next; + ushort startx,starty,endx,endy; + float displaytime; +} creditline; + +void Credits_Render(creditline *,float); +creditline Creditlines; + +ddgr_color CreditTextColor,CreditHeadingColor; +int CreditStartX,CreditStartY,CreditEndX,CreditEndY; +float CreditDisplayTime; +chunked_bitmap Credits_bm; + +#define MAX_CREDIT_LEN 200 + +bool Credits_IsKeyPressed(void) +{ + if(ddio_KeyInKey()) + return true; + + return false; +} + +// Parses a credit line +// Returns 1 if new text was read, else 0 +int Credits_ParseLine (char *line,creditline *credit) +{ + int line_len=strlen (line); + int new_text=0; + + if (line_len<1) + { + ASSERT (credit->next==NULL); + ASSERT (credit->text==NULL); + + credit->text=NULL; + + credit->type=CLTYPE_BLANK; + new_text=1; + + } + else if (line[0]==';') // Comment + return 0; + else if (line[0]=='*') // Movement data + { + int x1,x2,y1,y2; + float timedelay; + + int num_found = sscanf(line,"*%d %d %d %d %f", &x1,&y1,&x2,&y2,&timedelay); + if (num_found!=5) + { + mprintf ((0,"Bad movement data in credit text (%s)\n",line)); + return 0; + } + else + { + CreditStartX=x1; + CreditStartY=y1; + CreditEndX=x2; + CreditEndY=y2; + CreditDisplayTime=timedelay; + } + + } + else if (line[0]=='!')// Heading + { + ASSERT (credit->next==NULL); + ASSERT (credit->text==NULL); + + credit->text=(char *)mem_malloc (line_len+1); + ASSERT (credit->text); + + strcpy (credit->text,&line[1]); + credit->startx=CreditStartX; + credit->starty=CreditStartY; + credit->endx=CreditEndX; + credit->endy=CreditEndY; + credit->displaytime=CreditDisplayTime; + mprintf ((0,"Read header %s\n",line)); + + credit->color=CreditHeadingColor; + credit->type=CLTYPE_HEADING; + new_text=1; + + } + else if (line[0]=='$') // text color + { + int r,g,b; + int num_found = sscanf(line,"$%d %d %d", &r,&g,&b); + if (num_found!=3) + { + mprintf ((0,"Bad color in credit text (%s)\n",line)); + return 0; + } + else + { + CreditTextColor=GR_RGB(r,g,b); + } + + } + else if (line[0]=='^') // heading color + { + int r,g,b; + int num_found = sscanf(line,"^%d %d %d", &r,&g,&b); + if (num_found!=3) + { + mprintf ((0,"Bad color in credit text (%s)\n",line)); + return 0; + } + else + { + CreditHeadingColor=GR_RGB(r,g,b); + } + + } + else + { + ASSERT (credit->next==NULL); + ASSERT (credit->text==NULL); + + if (!strnicmp ("Jason Leighton",line,14) && (ps_rand()%100)==0) + { + strcat (line," (Hi mom!)"); + line_len+=10; + } + + credit->text=(char *)mem_malloc (line_len+1); + ASSERT (credit->text); + + strcpy (credit->text,line); + credit->color=CreditTextColor; + credit->type=CLTYPE_TEXT; + //mprintf ((0,"Read textline %s\n",line)); + new_text=1; + } + + return new_text; +} + +// Given a filename, attempts to load that filename as credit text +bool Credits_LoadCredits (char *filename) +{ + CFILE *infile; + char curline[MAX_CREDIT_LEN]; + + // Reset our data + CreditTextColor=GR_RGB(0,128,255); + CreditHeadingColor=GR_RGB(255,255,255); + CreditStartX=CreditEndX=320; + CreditStartY=CreditEndY=50; + CreditDisplayTime=10; + + creditline *cur_credit=&Creditlines; + Creditlines.next=NULL; + Creditlines.text=NULL; + + + // Open the file, bail on failure + infile=cfopen (filename,"rt"); + + if (!infile) + { + mprintf ((0,"Couldn't open credit file to read credits!\n")); + return false; + } + + for (int i=0;i<27;i++) + { + // Generate blank lines for the start + cur_credit->text=NULL; + cur_credit->type=CLTYPE_BLANK; + cur_credit->next=(creditline *)mem_malloc (sizeof(creditline)); + ASSERT (cur_credit->next); + cur_credit=cur_credit->next; + cur_credit->next=NULL; + cur_credit->text=NULL; + } + + // Read in each line of the credit file, allocing memory and putting it into a correct category + int done=0; + while (!done) + { + if (cfeof(infile)) + { + done=1; + continue; + } + + // Read a line and parse it + cf_ReadString (curline,MAX_CREDIT_LEN,infile); + //mprintf ((0,"Read string %s\n",curline)); + + if (!strnicmp ("END",curline,3)) + { + done=1; + continue; + } + + + if (Credits_ParseLine (curline,cur_credit)) + { + // Make a new line + cur_credit->next=(creditline *)mem_malloc (sizeof(creditline)); + ASSERT (cur_credit->next); + cur_credit=cur_credit->next; + cur_credit->next=NULL; + cur_credit->text=NULL; + } + } + + cur_credit->type=CLTYPE_END; + + cfclose (infile); + return true; +} + +void ShowStaticScreen(char *bitmap_filename,bool timed=false,float delay_time=0.0f); + +#define CREDIT_PIXELS_PER_SECOND 22 + +void Credits_Display(void) +{ +#ifdef DEMO + //ShowStaticScreen("democredits1.ogf"); + //ShowStaticScreen("democredits2.ogf"); + //return; +#else + // Load background image + int bm_handle = bm_AllocLoadFileBitmap("CreditsBackground.tga",0); + + if (bm_handle<0) + { + mprintf ((0,"Failed to load background credit screen!\n")); + return; + } + + if (!bm_CreateChunkedBitmap(bm_handle, &Credits_bm)) + { + Error("Failed to slice up credits screen!"); + } + + bm_FreeBitmap(bm_handle); + +// start up the music + D3MusicStart("credits.omf"); + D3MusicSetRegion(CREDITS1_MUSIC_REGION); + + mprintf((0,"Chillin in credits\n")); + + + // Load our credits + if (!Credits_LoadCredits ("GameCredits.txt")) + { + mprintf ((0,"There was an error loading game credits!\n")); + } + else + { + // First count how many headers there are so we know how many to allocate + int count=0; + int done=0; + creditline *credit=&Creditlines; + + while (!done) + { + if (credit->type!=CLTYPE_END) + count++; + credit=credit->next; + if (!credit || credit->type==CLTYPE_END) + done=1; + } + + int font_height=grfont_GetHeight(BIG_BRIEFING_FONT); + int total_pixel_height=font_height*count; + + int cur_line=0; + float total_pixel_count=0; + float cur_pixel_count=0; + float last_time=timer_GetTime(); + + // Now show these guys + done=0; + credit=&Creditlines; + while (cur_line=font_height) + { + credit=credit->next; + cur_line++; + if (credit==NULL || credit->type==CLTYPE_END) + { + cur_pixel_count=-1; + cur_line=count; + } + else + { + cur_pixel_count-=font_height; + } + } + + // Player music for this frame + tMusicSeqInfo music_info; + Sound_system.BeginSoundFrame(false); + music_info.frametime = frametime; + music_info.player_dead = false; + music_info.started_level = false; + D3MusicDoFrame(&music_info); + Sound_system.EndSoundFrame(); + + // Check for keys + Descent->defer(); + if(Credits_IsKeyPressed()) + { + cur_line=count; + } + } + + } + + // Free credit text memory + creditline *cur_credit=&Creditlines; + while (cur_credit) + { + if (cur_credit->text) + mem_free (cur_credit->text); + + creditline *next_credit=cur_credit->next; + + if (cur_credit!=&Creditlines) + { + mem_free (cur_credit); + } + + cur_credit=next_credit; + } + +// Free bitmaps + bm_DestroyChunkedBitmap(&Credits_bm); + +// end the music. + D3MusicStop(); + ddio_KeyFlush(); +// ddio_MouseReset(); // -mouse shouldn't be reset. ui system gets hosed unfortunately under NT. +#endif +} + +void Credits_Render(creditline *header,float pixels_in) +{ + bool done = false; + creditline *credit=header; + + grtext_Reset(); + grtext_SetFont(BIG_BRIEFING_FONT); + grtext_SetFontScale(1.0); + int text_height=grfont_GetHeight(BIG_BRIEFING_FONT); + + float pixels_this_frame=0; + int cur_pixel=-pixels_in; + int cur_line=0; + while((pixels_this_frame<480+text_height) && !done) + { + if (credit->type==CLTYPE_BLANK) + { + credit=credit->next; + cur_line++; + + pixels_this_frame+=text_height; + cur_pixel+=text_height; + + if (credit==NULL) + done=1; + else if (credit->type==CLTYPE_END) + done=1; + + continue; + } + grtext_SetColor (credit->color); + + // Figure out alpha + if (cur_pixel<0) + { + float norm=1.0-((-cur_pixel)/(float)text_height); + grtext_SetAlpha(norm*255); + } + else if ((cur_pixel+text_height)>480) + { + float norm=1.0-(((cur_pixel+text_height)-480)/(float)text_height); + grtext_SetAlpha(norm*255); + } + else + grtext_SetAlpha(255); + + // Figure out text placement + int texty=cur_pixel; + + grtext_CenteredPrintf(0, texty, "%s",credit->text); + + credit=credit->next; + + if (credit==NULL) + done=1; + else if (credit->type==CLTYPE_END) + done=1; + + cur_line++; + pixels_this_frame+=text_height; + cur_pixel+=text_height; + } + + grtext_Flush(); +} + diff --git a/Descent3/credits.h b/Descent3/credits.h new file mode 100644 index 000000000..17308ba4d --- /dev/null +++ b/Descent3/credits.h @@ -0,0 +1,18 @@ +/* +* $Logfile: /DescentIII/Main/credits.h $ +* $Revision: 2 $ +* $Date: 3/31/99 12:11p $ +* $Author: Jeff $ +* +* Credits system +* +* $Log: /DescentIII/Main/credits.h $ + * + * 2 3/31/99 12:11p Jeff + * added empty credits file +* +* $NoKeywords: $ +*/ + + +void Credits_Display(void); \ No newline at end of file diff --git a/Descent3/ctlconfig.cpp b/Descent3/ctlconfig.cpp new file mode 100644 index 000000000..032329e60 --- /dev/null +++ b/Descent3/ctlconfig.cpp @@ -0,0 +1,1546 @@ +/* + * $Logfile: /DescentIII/main/ctlconfig.cpp $ + * $Revision: 78 $ + * $Date: 4/19/00 5:25p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/main/ctlconfig.cpp $ + * + * 78 4/19/00 5:25p Matt + * From Duane for 1.4 + * Several Mac config changes + * Fixed a bug that caused the deadzone value to be decreased each time + * you the config dialog was used. + * + * 77 3/20/00 12:03p Matt + * Merge of Duane's post-1.3 changes. + * Mac deadzone stuff (Mac only) + * + * 76 10/22/99 3:40p Kevin + * Mac merge fixes + * + * 75 10/22/99 10:50a Matt + * Mac merge + * + * 74 7/28/99 4:18p Kevin + * Macintosh! + * + * 73 7/16/99 11:16a Samir + * multiple hat support. + * + * 72 6/10/99 9:07p Samir + * fixed weapon string name localization issues. + * + * 71 5/12/99 1:18p Samir + * play music during game too when in menus. + * + * 70 5/06/99 1:40a Samir + * adjusted some text. + * + * 69 5/02/99 2:19a Samir + * new weapon autoselect interface (a little more like D2) + * + * 68 4/30/99 12:12p Samir + * added strings for key ramping, other buttons in general config menu. + * + * 67 4/29/99 2:59p Samir + * added help and made CTRL-C clear for controller screens. + * + * 66 4/29/99 2:18a Samir + * updated art for options style menu. + * + * 65 4/22/99 3:43p Kevin + * Training missions show controls on screen + * + * 64 4/19/99 5:09p Samir + * ugh, forgot to destroy revert gadget when unrealizing ui page. + * + * 63 4/18/99 4:21p Samir + * added restore settings for either joystick or keyboard settings. + * + * 62 4/15/99 2:55p Samir + * added UI for mouselook. + * + * 61 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 60 3/17/99 4:18p Samir + * added button to calibrate joystick. + * + * 59 3/05/99 2:44p Samir + * needs to be cleaned up later, but mouse and joystick sensitivities are + * read in always, and set by the controller system. A cancel method + * needs to be implemented for these functions. + * + * 58 3/05/99 12:24p Samir + * weapon select screen has more help to determine what's high and what's + * low. + * + * 57 3/03/99 3:41a Samir + * moved ok and cancel buttons in weapon_select_dialog. + * + * 56 3/02/99 1:14p Samir + * added joystick/mouse enable and gamma help. + * + * 55 3/01/99 8:51p Samir + * fixed potential crash with no forcefeedback. + * + * 54 3/01/99 8:12p Samir + * don;t display force feedback menu if no stick. + * + * 53 3/01/99 5:01a Samir + * made sure NEWUIRES_FORCEQUIT was added to all new interfaces. + * + * 52 2/28/99 3:22a Samir + * added rearview text. + * + * 51 2/26/99 2:13a Samir + * massive reorg of weapon select prec + * + * 50 2/24/99 6:21p Samir + * moved sensitivity sliders to config key/controller and cleaned up + * weapon select menu. + * + * 49 2/24/99 12:41p Samir + * put in weapon selection. + * + * 48 2/23/99 2:00p Samir + * added keyboard ramping slider. + * + * 47 2/19/99 11:07p Samir + * don't verify control changes if new element has a NULL controller + * instead of a null binding (hats can have a 'null binding' and be valid. + * + * 46 2/16/99 12:07p Samir + * redid controller config with new ui. + * + * 45 2/10/99 3:39p Samir + * table file parsing stuff + * + * 44 1/28/99 3:58p Jeff + * localization update + * + * 43 1/06/99 6:33p Samir + * more hacks for demo. + * + * 42 1/06/99 6:28p Samir + * demo hack for controller checkbox code not in sync with UI checkbox + * code. + * + * 41 12/17/98 5:59p Samir + * moved mouse enabled and joy enabled to config menu. + * + * 40 11/30/98 4:54p Samir + * added rear view config item. + * + * 39 10/22/98 10:54p Samir + * put mouse or joystick disabled messages if pilot disabled them, to help + * those configuring. + * + * 38 10/21/98 11:14a Samir + * added generic code to skip rendering while in game controller config or + * telcom. + * + * 37 10/18/98 7:24p Samir + * added a reset to defaults options for key and joystick config. + * + * 36 10/18/98 1:07p Samir + * tweaked user interface for controller config. + * + * 35 10/17/98 7:33p Samir + * fixed bug when initializing elements list per panel. + * + * 34 10/13/98 6:33p Samir + * poll mulitplayer every frame if needed in controller config screen. + * + * 33 10/07/98 6:29p Samir + * bail if multiplayer game ended in ui. + * + * 32 10/02/98 4:15p Samir + * added fancy artwork for config screens. + * + * 31 10/01/98 12:57p Samir + * basic weapon autoselect config working. + * + * 30 9/30/98 4:35p Samir + * revamped control configuration ui, + * + * 29 9/23/98 6:19p Jeff + * finished up (hopefully) updating the config/ui dialogs to meet our + * standard. Keyboard/joystick config still needs some work + * + * 28 9/17/98 3:24p Samir + * added headlight configuration. + * + * 27 9/10/98 12:39p Samir + * added senstivity issures for controller. + * + * 26 8/31/98 6:49p Jeff + * made inventory and countermeasure keys customizable + * + * 25 6/29/98 6:42p Samir + * Properly handle controller pausing and resumption. + * + * 24 6/18/98 5:49p Samir + * finished controller string localization minus the binding stuff. + * + * 23 6/18/98 4:48p Samir + * added changes for multiple configs for joystick controls. + * + * 22 6/17/98 3:27p Jeff + * Changes made for localization + * + * 21 6/16/98 10:38a Jeff + * localization, strings pulled out to stringtable.h and d3.str + * + * 20 6/05/98 5:34p Samir + * new config system. + * + * 19 5/19/98 3:36p Samir + * added automap key. + * + * 18 5/15/98 3:13p Samir + * added 2 key support for controller functions. + * + * 17 5/12/98 3:04p Samir + * added button control for pbh. + * + * 16 5/11/98 4:53p Samir + * added axis sliding, config. + * + * 15 5/05/98 5:15p Samir + * adjusted menus to work in 512x384 as well as 640x480. + * + * 14 4/24/98 2:43a Samir + * added ability to turn off controller. + * + * 13 4/13/98 7:01p Samir + * added snazzy listbox and edit box art. + * + * 12 4/08/98 3:44p Samir + * added toggle bank and configuration for afterburner + * + * 11 3/20/98 1:19p Jeff + * Changes made to use Default_pilot string for pilot filename to use. + * + * 10 3/17/98 2:36p Samir + * Added Macros.h + * + * 9 3/05/98 6:38p Samir + * Use UI_FONT now. + * + * 8 2/16/98 9:27p Samir + * Simple joystick configuration working. + * + * 7 2/16/98 3:02p Samir + * Joystick config sort of working. + * + * 6 2/15/98 7:06p Samir + * Keyboard configuration system done. Not joystick. + * + * 5 2/13/98 7:10p Samir + * Fixed prob when pressing ALT (since it set a bit in the return key + * value that's greater that 256) + * + * 4 2/13/98 6:37p Samir + * Simple keyboard configuration. + * + * 3 2/10/98 4:55p Samir + * Added rudimentary items for controller config. + * + * 2 2/10/98 3:45p Samir + * Added list of configuration items. + * + * 1 2/10/98 11:49a Samir + * Initial revision. + * + * $NoKeywords: $ + */ +#include "ctlconfig.h" +#include "CtlCfgElem.h" +#include "ctlconfigtxt.h" +#include "game.h" +#include "weapon.h" +#include "pilot.h" +#include "bitmap.h" +#include "multi.h" +#include "gameloop.h" +#include "gamefont.h" +#include "D3ForceFeedback.h" +#include "hlsoundlib.h" +#include "ddio.h" +#ifdef MACINTOSH +#include "insprocket.h" //DAJ +#include "maccontroller.h" +#endif +#include +////////////////////////////////////////////////////////////////////////////// +#define IDV_KCONFIG 10 +#define IDV_CCONFIG 11 +#define IDV_WPNSEL 17 +#define IDV_OPTIONS 18 + +// used for adjusting settings. +#define CFG_AXIS_SENS_RANGE 20 +const short UID_JOYCFG = 0x1000; + +#ifdef MACINTOSH +short *joy_sens[N_JOY_AXIS] = {0, 0, 0, 0, 0, 0}; +float old_sens[N_JOY_AXIS]; +short *deadzone = 0; +#endif + +// Setup of config screens. +#define CCITEM_WPN_X 0 +#define CCITEM_WPN_Y 20 +#define CCITEM_AUX_X 0 +#define CCITEM_AUX_Y 160 +#define CCITEM_THRUST_X 295 +#define CCITEM_THRUST_Y 20 +#define CCITEM_ROT_X 295 +#define CCITEM_ROT_Y 160 +t_cfg_element Cfg_key_elements[] = +{ +// column1 -indices 0-15 + { -1, CtlText_WeaponGroup, CCITEM_WPN_X, CCITEM_WPN_Y }, + { ctfFIREPRIMARY_KEY, CtlText_FirePrimary, 0, 0 }, + { ctfFIRESECONDARY_KEY, CtlText_FireSecondary, 0, 0 }, + { ctfFIREFLARE_KEY, CtlText_FireFlare, 0, 0 }, + { ctfWPNSEL_PCYCLEKEY, CtlText_WpnCycP, 0, 0 }, + { ctfWPNSEL_SCYCLEKEY, CtlText_WpnCycS, 0, 0 }, + { -1, CtlText_MiscGroup, CCITEM_AUX_X, CCITEM_AUX_Y }, + { ctfHEADLIGHT_KEY, CtlText_Headlight, 0, 0 }, + { ctfREARVIEW_KEY, CtlText_Rearview, 0, 0 }, + { ctfAUTOMAP_KEY, CtlText_Automap, 0, 0 }, + { ctfPREV_INVKEY, CtlText_PrevInv, 0, 0 }, + { ctfNEXT_INVKEY, CtlText_NextInv, 0, 0 }, + { ctfINV_USEKEY, CtlText_InvUse, 0, 0 }, + { ctfPREV_CNTMSKEY, CtlText_PrevCntMs, 0, 0 }, + { ctfNEXT_CNTMSKEY, CtlText_NextCntMs, 0, 0 }, + { ctfCNTMS_USEKEY, CtlText_CntMsUse, 0, 0 }, + { ctfAUDIOTAUNT1_KEY, CtlText_AudioTaunt1, 0, 0 }, + { ctfAUDIOTAUNT2_KEY, CtlText_AudioTaunt2, 0, 0 }, + { ctfAUDIOTAUNT3_KEY, CtlText_AudioTaunt3, 0, 0 }, + { ctfAUDIOTAUNT4_KEY, CtlText_AudioTaunt4, 0, 0 }, + { -1, CtlText_ThrustGroup, CCITEM_THRUST_X, CCITEM_THRUST_Y }, + { ctfFORWARD_THRUSTKEY, CtlText_Accelerate, 0, 0 }, + { ctfREVERSE_THRUSTKEY, CtlText_Reverse, 0, 0 }, + { ctfAFTERBURN_KEY, CtlText_Afterburn, 0, 0 }, + { ctfTOGGLE_SLIDEKEY, CtlText_ToggleSlide, 0, 0 }, + { ctfLEFT_THRUSTKEY, CtlText_SlideLeft, 0, 0 }, + { ctfRIGHT_THRUSTKEY, CtlText_SlideRight, 0, 0 }, + { ctfUP_THRUSTKEY, CtlText_SlideUp, 0, 0 }, + { ctfDOWN_THRUSTKEY, CtlText_SlideDown, 0, 0 }, + { -1, CtlText_TurningGroup, CCITEM_ROT_X, CCITEM_ROT_Y }, + { ctfPITCH_UPKEY, CtlText_PitchUp, 0, 0 }, + { ctfPITCH_DOWNKEY, CtlText_PitchDown, 0, 0 }, + { ctfHEADING_LEFTKEY, CtlText_TurnLeft, 0, 0 }, + { ctfHEADING_RIGHTKEY, CtlText_TurnRight, 0, 0 }, + { ctfTOGGLE_BANKKEY, CtlText_ToggleBank, 0, 0 }, + { ctfBANK_LEFTKEY, CtlText_BankLeft, 0, 0 }, + { ctfBANK_RIGHTKEY, CtlText_BankRight, 0, 0 } +}; +#define CCITEM_WPN_X2 0 +#define CCITEM_WPN_Y2 20 +#define CCITEM_AUX_X2 0 +#define CCITEM_AUX_Y2 160 +#define CCITEM_THRUST_X2 295 +#define CCITEM_THRUST_Y2 20 +#define CCITEM_ROT_X2 295 +#define CCITEM_ROT_Y2 200 +t_cfg_element Cfg_joy_elements[] = +{ + { -1, CtlText_WeaponGroup, CCITEM_WPN_X2, CCITEM_WPN_Y2 }, + { ctfFIREPRIMARY_BUTTON, CtlText_FirePrimary, 0, 0 }, + { ctfFIRESECONDARY_BUTTON, CtlText_FireSecondary, 0, 0 }, + { ctfFIREFLARE_BUTTON, CtlText_FireFlare, 0, 0 }, + { ctfWPNSEL_PCYCLEBTN, CtlText_WpnCycP, 0, 0 }, + { ctfWPNSEL_SCYCLEBTN, CtlText_WpnCycS, 0, 0 }, + { -1, CtlText_MiscGroup, CCITEM_AUX_X2, CCITEM_AUX_Y2 }, + { ctfHEADLIGHT_BUTTON, CtlText_Headlight, 0, 0 }, + { ctfREARVIEW_BTN, CtlText_Rearview, 0, 0 }, + { ctfAUTOMAP_BUTTON, CtlText_Automap, 0, 0 }, + { ctfPREV_INVBTN, CtlText_PrevInv, 0, 0 }, + { ctfNEXT_INVBTN, CtlText_NextInv, 0, 0 }, + { ctfINV_USEBTN, CtlText_InvUse, 0, 0 }, + { ctfPREV_CNTMSBTN, CtlText_PrevCntMs, 0, 0 }, + { ctfNEXT_CNTMSBTN, CtlText_NextCntMs, 0, 0 }, + { ctfCNTMS_USEBTN, CtlText_CntMsUse, 0, 0 }, + { ctfAUDIOTAUNT1_BTN, CtlText_AudioTaunt1, 0, 0 }, + { ctfAUDIOTAUNT2_BTN, CtlText_AudioTaunt2, 0, 0 }, + { ctfAUDIOTAUNT3_BTN, CtlText_AudioTaunt3, 0, 0 }, + { ctfAUDIOTAUNT4_BTN, CtlText_AudioTaunt4, 0, 0 }, + { -1, CtlText_ThrustGroup, CCITEM_THRUST_X2, CCITEM_THRUST_Y2 }, + { ctfFORWARD_THRUSTAXIS, CtlText_Forward, 0, 0 }, + { ctfFORWARD_BUTTON, CtlText_Accelerate, 0, 0 }, + { ctfREVERSE_BUTTON, CtlText_Reverse, 0, 0 }, + { ctfAFTERBURN_BUTTON, CtlText_Afterburn, 0, 0 }, + { ctfUP_THRUSTAXIS, CtlText_SlideVert, 0, 0 }, + { ctfRIGHT_THRUSTAXIS, CtlText_SlideHoriz, 0, 0 }, + { ctfTOGGLE_SLIDEBUTTON, CtlText_ToggleSlide, 0, 0 }, + { ctfUP_BUTTON, CtlText_SlideUp, 0, 0 }, + { ctfDOWN_BUTTON, CtlText_SlideDown, 0, 0 }, + { ctfLEFT_BUTTON, CtlText_SlideLeft, 0, 0 }, + { ctfRIGHT_BUTTON, CtlText_SlideRight, 0, 0 }, + { -1, CtlText_TurningGroup, CCITEM_ROT_X2, CCITEM_ROT_Y2 }, + { ctfPITCH_DOWNAXIS, CtlText_Pitch, 0, 0 }, + { ctfPITCH_UPBUTTON, CtlText_PitchUp, 0, 0 }, + { ctfPITCH_DOWNBUTTON, CtlText_PitchDown, 0, 0 }, + { ctfHEADING_RIGHTAXIS, CtlText_Heading, 0, 0 }, + { ctfHEADING_LEFTBUTTON, CtlText_TurnLeft, 0, 0 }, + { ctfHEADING_RIGHTBUTTON, CtlText_TurnRight, 0, 0 }, + { ctfBANK_RIGHTAXIS, CtlText_Bank, 0, 0 }, + { ctfTOGGLE_BANKBUTTON, CtlText_ToggleBank, 0, 0 }, + { ctfBANK_LEFTBUTTON, CtlText_BankLeft, 0, 0 }, + { ctfBANK_RIGHTBUTTON, CtlText_BankRight, 0, 0 } +}; +#define N_JOY_CFG_FN (sizeof(Cfg_joy_elements)/sizeof(t_cfg_element)) +#define N_KEY_CFG_FN (sizeof(Cfg_key_elements)/sizeof(t_cfg_element)) +void ctl_cfg_set_and_verify_changes(short fnid, ct_type elem_type, ubyte controller, ubyte elem, sbyte slot); +void ctl_cfg_element_options_dialog(short fnid); +// used for adjusting settings. +int weapon_select_dialog(int wpn, bool is_secondary); +#define UID_KEYCFG_ID 0x1000 +#define UID_JOYCFG_ID 0x1100 +#define UID_PRIMARY_WPN 0x1200 +#define UID_PRIMARY_DIS 0x1220 +#define UID_SECONDARY_WPN 0x1300 +#define UID_SECONDARY_DIS 0x1320 +#define UID_WPNLIST 0x1400 +#define UID_RESETDEFAULTS 0x2000 +#define UID_CFGSETTINGS 0x2001 +#define UID_CLEAR_SLOT 0x2002 +#define UID_REVERT 0x2003 +////////////////////////////////////////////////////////////////////////////// +// +class cfg_screen +{ +protected: + newuiSheet *m_sheet; + newuiLargeMenu *m_menu; +public: + virtual void setup(newuiLargeMenu *menu) = 0; + virtual void finish() = 0; + virtual void realize() = 0; + virtual void unrealize() = 0; + virtual void process(int res) {}; +}; +////////////////////////////////////////////////////////////////////////////// +// KEY CONFIG +#define KEYCFG_EXTRAS_X 0 +#define KEYCFG_EXTRAS_Y 4 +#define KEYCFG_EXTRAS_W 160 +class key_cfg_screen: public cfg_screen +{ + cfg_element m_elem[N_KEY_CFG_FN]; + newuiButton m_reset_btn; + newuiButton m_key_settings; + newuiButton m_revert_btn; + UIText m_help1_txt; + UIText m_help2_txt; + cntrldata m_old_controls[N_KEY_CFG_FN]; +public: + virtual void setup(newuiLargeMenu *menu); + virtual void finish(); + virtual void process(int res); + virtual void realize(); + virtual void unrealize(); +}; +void key_cfg_screen::setup(newuiLargeMenu *menu) +{ + int i; + m_menu = menu; + m_sheet = menu->AddOption(IDV_KCONFIG,TXT_OPTCUSTKEYB); +// save original settings. + for (i = 0; i < N_KEY_CFG_FN; i++) + { + Controller->get_controller_function(Cfg_key_elements[i].fn_id, m_old_controls[i].type, + &m_old_controls[i].value, m_old_controls[i].flags); + } +} +void key_cfg_screen::finish() +{ +} +void key_cfg_screen::process(int res) +{ + int i; + for (i = 0; i < N_KEY_CFG_FN; i++) + { + if (m_elem[i].GetID() != -1 && m_elem[i].GetID() == res) { + // we chose a slot to configure. + ubyte elem, controller; + sbyte slot; + ct_type elem_type; + if (m_elem[i].GetActiveSlot() == CFGELEM_SLOT_CLEAR) { + ctl_cfg_element_options_dialog((short)(res-UID_KEYCFG_ID)); + } + else if (m_elem[i].Configure(&elem_type, &controller, &elem, &slot)) { + ctl_cfg_set_and_verify_changes((short)(res-UID_KEYCFG_ID), elem_type, controller, elem, slot); + } + break; + } + } + switch (res) + { + case UID_RESETDEFAULTS: + for (i = 0; i < NUM_CONTROLLER_FUNCTIONS; i++) + { + if (Controller_needs[i].ctype[0] == ctKey) { + ASSERT(Controller_needs[i].ctype[1] == ctKey); + Controller->assign_function(&Controller_needs[i]); + } + } + break; + case UID_CFGSETTINGS: + key_settings_dialog(); + break; + case UID_REVERT: + // restore original settings. + for (i = 0; i < N_KEY_CFG_FN; i++) + { + Controller->set_controller_function(Cfg_key_elements[i].fn_id, m_old_controls[i].type, + m_old_controls[i].value, m_old_controls[i].flags); + } + break; + } +} +void key_cfg_screen::realize() +{ +// create controls + int i, x, y; + t_cfg_element *cfg_elem = &Cfg_key_elements[0]; + m_reset_btn.Create(m_menu, UID_RESETDEFAULTS, TXT_RESETTODEFAULT, KEYCFG_EXTRAS_X+m_sheet->X(),KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); + m_key_settings.Create(m_menu, UID_CFGSETTINGS, TXT_ADJUSTSETTINGS, KEYCFG_EXTRAS_X+m_sheet->X()+KEYCFG_EXTRAS_W, KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); + m_revert_btn.Create(m_menu, UID_REVERT, TXT_REVERTCONTROLS, KEYCFG_EXTRAS_X+m_sheet->X()+KEYCFG_EXTRAS_W*2, KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); + m_help1_txt.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_KEYCFGSCREENHELP1, NEWUI_MONITORFONT_COLOR), 280, 30); + m_help2_txt.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_CFGSCREENHELP, NEWUI_MONITORFONT_COLOR), 280, 40); + for (i = 0; i < N_KEY_CFG_FN; i++) + { + if (cfg_elem->fn_id == -1) { + x = cfg_elem->x + m_sheet->X(); + y = cfg_elem->y + m_sheet->Y(); + } + m_elem[i].Create(m_menu, cfg_elem->text, x, y, cfg_elem->fn_id, (cfg_elem->fn_id == -1) ? -1 : UID_KEYCFG_ID+cfg_elem->fn_id); + y += m_elem[i].H()+2; + cfg_elem++; + } +} +void key_cfg_screen::unrealize() +{ +// create controls + int i; + t_cfg_element *cfg_elem = &Cfg_key_elements[0]; + m_help2_txt.Destroy(); + m_help1_txt.Destroy(); + m_key_settings.Destroy(); + m_reset_btn.Destroy(); + m_revert_btn.Destroy(); + for (i = 0; i < N_KEY_CFG_FN; i++) + { + m_elem[i].Destroy(); + cfg_elem++; + } +} +////////////////////////////////////////////////////////////////////////////// +// JOY CONFIG +class joy_cfg_screen: public cfg_screen +{ + cfg_element m_elem[N_JOY_CFG_FN]; + newuiButton m_reset_btn; + newuiButton m_controller_settings; + newuiButton m_revert_btn; + cntrldata m_old_controls[N_JOY_CFG_FN]; + UIText m_help1_txt; + UIText m_help2_txt; +public: + virtual void setup(newuiLargeMenu *menu); + virtual void finish(); + virtual void process(int res); + virtual void realize(); + virtual void unrealize(); +}; +// setup user interfaces for each screen. +void joy_cfg_screen::setup(newuiLargeMenu *menu) +{ + int i; + m_menu = menu; + m_sheet = menu->AddOption(IDV_CCONFIG,TXT_OPTCUSTCONT); + +#ifdef MACINTOSH + short curpos; + tSliderSettings slider_set; + int axis_str[N_JOY_AXIS] = { TXI_KB_HEADING, TXI_KB_PITCH, TXI_KB_BANK, TXI_KB_SLIDEHORIZ,TXI_KB_SLIDEVERT, TXI_KB_FORWARD }; + + for (i = 0; i < N_JOY_AXIS; i++) + { + old_sens[i] = Controller->get_axis_sensitivity(ctAxis, i+1); + } + m_controller_settings.Create(m_menu, UID_CFGSETTINGS, TXT_SPROCKETS_CONFIG, KEYCFG_EXTRAS_X+m_sheet->X()+KEYCFG_EXTRAS_W, KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); +// sensitivity sliders for joystick + m_sheet->NewGroup(TXT_SENSITIVITY, 80, 60); + for (i = 0; i < N_JOY_AXIS; i++) + { + char str[16]; + float val = Controller->get_axis_sensitivity(ctAxis, i+1); + slider_set.min_val.f = 0.0f; + slider_set.max_val.f = JOY_AXIS_SENS_RANGE; + slider_set.type = SLIDER_UNITS_FLOAT; + curpos = CALC_SLIDER_POS_FLOAT(val, &slider_set, CFG_AXIS_SENS_RANGE); + sprintf(str, "%s", TXT(axis_str[i])); + joy_sens[i] = m_sheet->AddSlider(str, CFG_AXIS_SENS_RANGE, curpos, &slider_set); + } + m_sheet->NewGroup(TXT_JOYSTICK_DEADZONE, 320, 60); + int val = Controller->get_controller_deadzone(MC_JOY); + slider_set.min_val.i = 0; + slider_set.max_val.i = 25; + slider_set.type = SLIDER_UNITS_INT; + curpos = CALC_SLIDER_POS_INT(val, &slider_set, 25); + deadzone = m_sheet->AddSlider("", 25, curpos, &slider_set); +#else +// save original settings. + for (i = 0; i < N_JOY_CFG_FN; i++) + { + Controller->get_controller_function(Cfg_joy_elements[i].fn_id, m_old_controls[i].type, + &m_old_controls[i].value, m_old_controls[i].flags); + } +#endif +} +void joy_cfg_screen::finish() +{ +} +void joy_cfg_screen::process(int res) +{ + int i; + +#ifdef MACINTOSH + + float uval = CALC_SLIDER_FLOAT_VALUE(*deadzone, 0.0, 25, 25); + Controller->set_controller_deadzone(MC_JOY, uval); + for (i = 0; i < N_JOY_AXIS; i++) + { + float val = CALC_SLIDER_FLOAT_VALUE(*joy_sens[i], 0.0f, JOY_AXIS_SENS_RANGE, CFG_AXIS_SENS_RANGE); + Controller->set_axis_sensitivity(ctAxis, i+1, val); + } + switch (res) { + case UID_RESETDEFAULTS: + for (i = 0; i < N_JOY_AXIS; i++) + Controller->set_axis_sensitivity(ctAxis, i+1, 1.0); + break; + case UID_CFGSETTINGS: + inSprocket_Configure(); + break; + case UID_REVERT: + for (i = 0; i < N_JOY_AXIS; i++) + Controller->set_axis_sensitivity(ctAxis, i+1, old_sens[i]); + break; + } + +#else + for (i = 0; i < N_JOY_CFG_FN; i++) + { + if (m_elem[i].GetID() != -1 && m_elem[i].GetID() == res) { + // we chose a slot to configure. + ubyte elem, controller; + sbyte slot; + ct_type elem_type; + if (m_elem[i].GetActiveSlot() == CFGELEM_SLOT_CLEAR) { + ctl_cfg_element_options_dialog((short)(res-UID_JOYCFG_ID)); + } + else if (m_elem[i].Configure(&elem_type, &controller, &elem, &slot)) { + ctl_cfg_set_and_verify_changes((short)(res-UID_JOYCFG_ID), elem_type, controller, elem, slot); + } + break; + } + } + switch (res) + { + case UID_RESETDEFAULTS: + for (i = 0; i < NUM_CONTROLLER_FUNCTIONS; i++) + { + if (Controller_needs[i].ctype[0] != ctKey) { + ASSERT(Controller_needs[i].ctype[1] != ctKey); + Controller->assign_function(&Controller_needs[i]); + } + } + break; + case UID_CFGSETTINGS: + joystick_settings_dialog(); + break; + case UID_REVERT: + // restore original settings. + for (i = 0; i < N_KEY_CFG_FN; i++) + { + Controller->set_controller_function(Cfg_joy_elements[i].fn_id, m_old_controls[i].type, + m_old_controls[i].value, m_old_controls[i].flags); + } + break; + } +#endif +} + +void joy_cfg_screen::realize() +{ + int i, x, y; + t_cfg_element *cfg_elem = &Cfg_joy_elements[0]; +#ifdef MACINTOSH +#else + m_reset_btn.Create(m_menu, UID_RESETDEFAULTS, TXT_RESETTODEFAULT, KEYCFG_EXTRAS_X+m_sheet->X(),KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); + m_controller_settings.Create(m_menu, UID_CFGSETTINGS, TXT_ADJUSTSETTINGS, KEYCFG_EXTRAS_X+m_sheet->X()+KEYCFG_EXTRAS_W, KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); + m_revert_btn.Create(m_menu, UID_REVERT, TXT_REVERTCONTROLS, KEYCFG_EXTRAS_X+m_sheet->X()+KEYCFG_EXTRAS_W*2, KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); + m_help1_txt.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_JOYCFGSCREENHELP1, NEWUI_MONITORFONT_COLOR), 280, 30); + m_help2_txt.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_CFGSCREENHELP, NEWUI_MONITORFONT_COLOR), 280, 40); + for (i = 0; i < N_JOY_CFG_FN; i++) + { + if (cfg_elem->fn_id == -1) { + x = cfg_elem->x + m_sheet->X(); + y = cfg_elem->y + m_sheet->Y(); + } + m_elem[i].Create(m_menu, cfg_elem->text, x, y, cfg_elem->fn_id, (cfg_elem->fn_id == -1) ? -1 : UID_JOYCFG_ID+cfg_elem->fn_id); + y += m_elem[i].H()+2; + cfg_elem++; + } +#endif +} +void joy_cfg_screen::unrealize() +{ +// create controls + int i; + +#ifdef MACINTOSH +#else + m_controller_settings.Destroy(); + t_cfg_element *cfg_elem = &Cfg_joy_elements[0]; + for (i = 0; i < N_JOY_CFG_FN; i++) + { + m_elem[i].Destroy(); + cfg_elem++; + } + m_help2_txt.Destroy(); + m_help1_txt.Destroy(); + m_reset_btn.Destroy(); + m_revert_btn.Destroy(); +#endif +} +////////////////////////////////////////////////////////////////////////////// +// WEAPON SEL CONFIG +#define WPNSEL_PRIMARY_X 70 +#define WPNSEL_PRIMARY_Y 170 +#define WPNSEL_SECONDARY_X 340 +#define WPNSEL_SECONDARY_Y 170 +extern float Key_ramp_speed; +class wpnsel_cfg_screen: public cfg_screen +{ + UIText m_primary_title; + UIText m_primary_low; + UIText m_primary_high; + newuiTinyButton m_primary_disables[MAX_PRIMARY_WEAPONS]; + newuiButton m_primary_buttons[MAX_PRIMARY_WEAPONS]; + UIText m_secondary_title; + UIText m_secondary_low; + UIText m_secondary_high; + newuiTinyButton m_secondary_disables[MAX_SECONDARY_WEAPONS]; + newuiButton m_secondary_buttons[MAX_SECONDARY_WEAPONS]; + UIText m_weapon_choose; + newuiButton m_reset_btn; + int m_selection_status; // -1 = no selection, 0 = primary, 1 = secondary + int m_switch_slot; +public: + virtual void setup(newuiLargeMenu *menu); + virtual void finish(); + virtual void process(int res); + virtual void realize(); + virtual void unrealize(); +}; +void wpnsel_cfg_screen::setup(newuiLargeMenu *menu) +{ + m_menu = menu; + m_sheet = menu->AddOption(IDV_WPNSEL,TXT_WPNSELPREC); + m_sheet->NewGroup(NULL, 0,20); + m_sheet->AddText(TXT_WPNCFGHELP0); + m_sheet->AddText(TXT_WPNCFGHELP1); + m_selection_status = -1; + m_switch_slot = -1; +} +void wpnsel_cfg_screen::finish() +{ +} +void wpnsel_cfg_screen::process(int res) +{ + int slot, i; + bool is_secondary = false, do_swap_ui = false, do_disable_ui = false, update_buttons = false; + mprintf((0, "res=%d\n", res)); + if (m_selection_status == -1) { + if (res == UID_RESETDEFAULTS) { + for (i = 0; i < MAX_PRIMARY_WEAPONS; i++) + SetAutoSelectPrimaryWpnIdx(i, DefaultPrimarySelectList[i]); + for (i = 0; i < MAX_SECONDARY_WEAPONS; i++) + SetAutoSelectSecondaryWpnIdx(i, DefaultSecondarySelectList[i]); + update_buttons = true; + } + else if (res == UID_CLEAR_SLOT) { + // figure out id of current gadget highlighted. + UIGadget *gadget = m_menu->GetFocus(); + if (gadget) { + // below code will then determine the correct information. + res = gadget->GetID(); + do_disable_ui = true; + } + } + } + slot = -1; + if (res >= UID_PRIMARY_WPN && res < (UID_PRIMARY_WPN+MAX_PRIMARY_WEAPONS) && m_selection_status != 1) { + slot = res - UID_PRIMARY_WPN; + is_secondary = false; + if (!do_disable_ui) do_swap_ui = true; + } + else if (res >= UID_SECONDARY_WPN && res < (UID_SECONDARY_WPN+MAX_SECONDARY_WEAPONS) && m_selection_status != 0) { + slot = res - UID_SECONDARY_WPN; + is_secondary = true; + if (!do_disable_ui) do_swap_ui = true; + } + else if (res >= UID_PRIMARY_DIS && res < (UID_PRIMARY_DIS+MAX_PRIMARY_WEAPONS) && m_selection_status == -1) { + slot = res - UID_PRIMARY_DIS; + is_secondary = false; + do_disable_ui = true; + } + else if (res >= UID_SECONDARY_DIS && res < (UID_SECONDARY_DIS+MAX_SECONDARY_WEAPONS) && m_selection_status == -1) { + slot = res - UID_SECONDARY_DIS; + is_secondary = true; + do_disable_ui = true; + } + mprintf((0, "slot=%d\n", slot)); + +// do different uis + if (do_disable_ui) { + ASSERT(m_selection_status == -1 && slot != -1); + ushort wpnidx = is_secondary ? GetAutoSelectSecondaryWpnIdx(slot) : GetAutoSelectPrimaryWpnIdx(slot); + wpnidx = wpnidx ^ WPNSEL_SKIP; + if (is_secondary) { + SetAutoSelectSecondaryWpnIdx(slot, wpnidx); + } + else { + SetAutoSelectPrimaryWpnIdx(slot, wpnidx); + } + update_buttons = true; + } +// here we see if we are selecting a weapon to switch with another, if one is already selected, we +// see if the user has selected another (in the same category) +// + else if (do_swap_ui) { + ushort wpnidx; + if (slot != -1) { + if (m_selection_status == -1) { + m_switch_slot = slot; + m_selection_status = is_secondary ? 1 : 0; + // disable proper controls here. + for (i = 0; i < MAX_PRIMARY_WEAPONS; i++) + { + if (m_selection_status == 1) { + m_primary_disables[i].Disable(); + m_primary_buttons[i].Disable(); + } + else { + m_secondary_disables[i].Disable(); + m_secondary_buttons[i].Disable(); + } + } + if (!m_weapon_choose.IsCreated()) { + UIGadget *focused_gadget = m_menu->GetFocus(); + m_weapon_choose.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_WPNCFGHELP2, GR_RGB(240,240,0)), WPNSEL_PRIMARY_X, WPNSEL_PRIMARY_Y- 48); + if (focused_gadget) m_menu->SetFocusOnGadget(focused_gadget); + } + } + else { + int slot_idx = m_switch_slot; + ushort new_wpnidx = is_secondary ? GetAutoSelectSecondaryWpnIdx(m_switch_slot) : GetAutoSelectPrimaryWpnIdx(m_switch_slot); + + ASSERT(m_switch_slot != -1); + if (slot > m_switch_slot) { + new_wpnidx = is_secondary ? GetAutoSelectSecondaryWpnIdx(m_switch_slot) : GetAutoSelectPrimaryWpnIdx(m_switch_slot); + while (slot_idx < slot) + { + if (is_secondary) { + wpnidx = GetAutoSelectSecondaryWpnIdx(slot_idx+1); + SetAutoSelectSecondaryWpnIdx(slot_idx, wpnidx); + } + else { + wpnidx = GetAutoSelectPrimaryWpnIdx(slot_idx+1); + SetAutoSelectPrimaryWpnIdx(slot_idx, wpnidx); + } + slot_idx++; + } + update_buttons = true; + } + else if (slot < m_switch_slot) { + while (slot_idx > slot) + { + if (is_secondary) { + wpnidx = GetAutoSelectSecondaryWpnIdx(slot_idx-1); + SetAutoSelectSecondaryWpnIdx(slot_idx, wpnidx); + } + else { + wpnidx = GetAutoSelectPrimaryWpnIdx(slot_idx-1); + SetAutoSelectPrimaryWpnIdx(slot_idx, wpnidx); + } + slot_idx--; + } + update_buttons = true; + } + if (is_secondary) { + SetAutoSelectSecondaryWpnIdx(slot, new_wpnidx); + } + else { + SetAutoSelectPrimaryWpnIdx(slot, new_wpnidx); + } + m_selection_status = -1; + m_switch_slot = -1; + if (m_weapon_choose.IsCreated()) { + m_weapon_choose.Destroy(); + } + } + } + } +// if above actions modified buttons, refresh em. + if (update_buttons) { + wpnsel_cfg_screen::unrealize(); + wpnsel_cfg_screen::realize(); + } +} +void wpnsel_cfg_screen::realize() +{ + int i; +// make sure and reset these here. + m_selection_status = -1; + m_switch_slot = -1; + + m_reset_btn.Create(m_menu, UID_RESETDEFAULTS, TXT_RESETTODEFAULT, KEYCFG_EXTRAS_X+m_sheet->X(),KEYCFG_EXTRAS_Y+m_sheet->Y(), NEWUI_BTNF_LONG); + m_primary_title.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_PRIMARY), WPNSEL_PRIMARY_X, WPNSEL_PRIMARY_Y - 28); + m_secondary_title.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_SECONDARY), WPNSEL_SECONDARY_X, WPNSEL_SECONDARY_Y - 28); + m_primary_low.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_LOWPRIORITY), WPNSEL_PRIMARY_X, WPNSEL_SECONDARY_Y - 11); + m_secondary_low.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_LOWPRIORITY), WPNSEL_SECONDARY_X, WPNSEL_SECONDARY_Y - 11); + for (i = 0; i < MAX_PRIMARY_WEAPONS; i++) + { + int wpnidx; + char str[64]; + wpnidx = GetAutoSelectPrimaryWpnIdx(i); + if (wpnidx != WPNSEL_INVALID) { + if (wpnidx & WPNSEL_SKIP) { + wpnidx &= (~WPNSEL_SKIP); + sprintf(str, TXT_WPNSELBTN, TXT(Static_weapon_names_msg[wpnidx])); + } + else { + sprintf(str, "%s", TXT(Static_weapon_names_msg[wpnidx])); + } + m_primary_disables[i].Create(m_menu, UID_PRIMARY_DIS+i, "X", WPNSEL_PRIMARY_X, WPNSEL_PRIMARY_Y+i*12); + m_primary_buttons[i].Create(m_menu, UID_PRIMARY_WPN+i, str, WPNSEL_PRIMARY_X + 28, WPNSEL_PRIMARY_Y+i*12, NEWUI_BTNF_LONG); + } + wpnidx = GetAutoSelectSecondaryWpnIdx(i); + if (wpnidx != WPNSEL_INVALID) { + if (wpnidx & WPNSEL_SKIP) { + wpnidx &= (~WPNSEL_SKIP); + sprintf(str, TXT_WPNSELBTN, TXT(Static_weapon_names_msg[wpnidx])); + } + else { + sprintf(str, "%s", TXT(Static_weapon_names_msg[wpnidx])); + } + m_secondary_disables[i].Create(m_menu, UID_SECONDARY_DIS+i, "X", WPNSEL_SECONDARY_X, WPNSEL_SECONDARY_Y+i*12); + m_secondary_buttons[i].Create(m_menu, UID_SECONDARY_WPN+i, str, WPNSEL_SECONDARY_X+28, WPNSEL_SECONDARY_Y+i*12, NEWUI_BTNF_LONG); + } + } + m_primary_high.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_HIGHPRIORITY), WPNSEL_PRIMARY_X, WPNSEL_PRIMARY_Y+i*12+2); + m_secondary_high.Create(m_menu, &UITextItem(MONITOR9_NEWUI_FONT, TXT_HIGHPRIORITY), WPNSEL_SECONDARY_X, WPNSEL_SECONDARY_Y+i*12+2); +// add accelerator keys + m_menu->AddAcceleratorKey(KEY_CTRLED+KEY_C, UID_CLEAR_SLOT); +} +void wpnsel_cfg_screen::unrealize() +{ + int i; + m_menu->ResetAcceleratorKey(); + if (m_weapon_choose.IsCreated()) { + m_weapon_choose.Destroy(); + } + for (i = 0; i < MAX_PRIMARY_WEAPONS; i++) + { + m_primary_buttons[i].Destroy(); + m_primary_disables[i].Destroy(); + m_secondary_buttons[i].Destroy(); + m_secondary_disables[i].Destroy(); + } + m_secondary_high.Destroy(); + m_primary_high.Destroy(); + + m_secondary_low.Destroy(); + m_primary_low.Destroy(); + m_secondary_title.Destroy(); + m_primary_title.Destroy(); + + m_reset_btn.Destroy(); +} +////////////////////////////////////////////////////////////////////////////// +// this will take out any repeats of element and slot in function id. +void ctl_cfg_set_and_verify_changes(short fnid, ct_type elem_type, ubyte ctrl, ubyte elem, sbyte slot) +{ + ct_type ctype_fn[CTLBINDS_PER_FUNC]; + ubyte cfgflags_fn[CTLBINDS_PER_FUNC]; + ct_config_data ccfgdata_fn; + t_cfg_element *fn_list; + tCfgDataParts cfgparts; + int i, n_fn; + if (fnid == -1) { + Int3(); // get samir + return; + } + ASSERT(slot < N_CFGELEM_SLOTS && slot >= 0); + if (ctrl == NULL_CONTROLLER) { + return; + } +// if element is repeated anywhere in function list other than 'fnid'[slot] of elem_type, then remove that +// binding. + if (elem_type == ctNone) { + Int3(); + return; + } + else if (elem_type == ctKey) { + n_fn = N_KEY_CFG_FN; + fn_list = Cfg_key_elements; + } + else { + n_fn = N_JOY_CFG_FN; + fn_list = Cfg_joy_elements; + } + for (i = 0; i < n_fn; i++) + { + // get current function. if element in any slot matches elem_type, and elem, then remove it. + bool match_found = false; + Controller->get_controller_function(fn_list[i].fn_id, ctype_fn, &ccfgdata_fn, cfgflags_fn); + + parse_config_data(&cfgparts, ctype_fn[0], ctype_fn[1], ccfgdata_fn); + if (elem_type == ctype_fn[0] && elem == cfgparts.bind_0 && cfgparts.ctrl_0 == ctrl) { + // match found! clear this part out. + cfgparts.bind_0 = NULL_BINDING; + cfgparts.ctrl_0 = NULL_CONTROLLER; + match_found = true; + } + if (elem_type == ctype_fn[1] && elem == cfgparts.bind_1 && cfgparts.ctrl_1 == ctrl) { + // match found! clear this part out. + cfgparts.bind_1 = NULL_BINDING; + cfgparts.ctrl_1 = NULL_CONTROLLER; + match_found = true; + } + if (match_found) { + // this will effectively clear any matches found. + ccfgdata_fn = unify_config_data(&cfgparts); + Controller->set_controller_function(fn_list[i].fn_id, ctype_fn, ccfgdata_fn, cfgflags_fn); + } + } + Controller->get_controller_function(fnid, ctype_fn, &ccfgdata_fn, cfgflags_fn); + + parse_config_data(&cfgparts, ctype_fn[0], ctype_fn[1], ccfgdata_fn); + + ctype_fn[slot] = elem_type; + cfgflags_fn[slot] = 0; + if (slot == 1) { + cfgparts.bind_1 = elem; cfgparts.ctrl_1 = ctrl; + } + else { + cfgparts.bind_0 = elem; cfgparts.ctrl_0 = ctrl; + } + ccfgdata_fn = unify_config_data(&cfgparts); + Controller->set_controller_function(fnid, ctype_fn, ccfgdata_fn, cfgflags_fn); +} +// used as a help/options dialog for each controller config element +extern const char *cfg_binding_text(ct_type ctype, ubyte ctrl, ubyte binding); +void ctl_cfg_element_options_dialog(short fnid) +{ + newuiTiledWindow wnd; + newuiSheet *sheet; + int *inv_binding[CTLBINDS_PER_FUNC]; + bool *clear_binding[CTLBINDS_PER_FUNC]; + ct_type ctype_fn[CTLBINDS_PER_FUNC]; + ubyte cfgflags_fn[CTLBINDS_PER_FUNC]; + ct_config_data ccfgdata_fn; + + tCfgDataParts cfgparts; + int res, y; + char str[64]; + + if (fnid == -1) { + Int3(); // get samir + return; + } +// determine which configuration pipe to go down + Controller->get_controller_function(fnid, ctype_fn, &ccfgdata_fn, cfgflags_fn); + parse_config_data(&cfgparts, ctype_fn[0], ctype_fn[1], ccfgdata_fn); + switch (ctype_fn[0]) + { + case ctKey: strcpy(str, TXT_CFGHELP_KEYB); break; + case ctAxis: + case ctMouseAxis: strcpy(str, TXT_CFGHELP_JOY); break; + case ctButton: + case ctMouseButton: + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: strcpy(str, TXT_CFGHELP_BTNS); break; + default: + str[0] = 0; + } +// open a small window + wnd.Create(NULL, 0, 0, 320, 192); + sheet = wnd.GetSheet(); +// the standard ok and cancel buttons + sheet->NewGroup(NULL, 116, 110, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK, UID_OK); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); + sheet->NewGroup(NULL, 0, 0); + sheet->AddText(TXT_CFGHELP_TEXT); + sheet->AddText(str); + y = 40; +// check if there's a binding, if so, then check if we should put up invert axis option. + clear_binding[0] = NULL; + inv_binding[0] = NULL; + if (cfgparts.ctrl_0 != NULL_CONTROLLER) { + sprintf(str, TXT_CFG_BIND_1, cfg_binding_text(ctype_fn[0], cfgparts.ctrl_0,cfgparts.bind_0)); + sheet->NewGroup(str, 0, y, NEWUI_ALIGN_HORIZ); + clear_binding[0] = sheet->AddCheckBox(TXT_CFG_CLEAR); + if (ctype_fn[0] == ctAxis || ctype_fn[0] == ctMouseAxis) { + sheet->AddText(TXT_CFG_INVERT); + inv_binding[0] = sheet->AddFirstRadioButton(TXT_NO); + sheet->AddRadioButton(TXT_YES); + *inv_binding[0] = CHECK_FLAG(cfgflags_fn[0], CTFNF_INVERT) ? 1 : 0; + } + y+= 30; + } +// check if there's a binding, if so, then check if we should put up invert axis option. + clear_binding[1] = NULL; + inv_binding[1] = NULL; + if (cfgparts.ctrl_1 != NULL_CONTROLLER) { + sprintf(str, TXT_CFG_BIND_2, cfg_binding_text(ctype_fn[1], cfgparts.ctrl_1,cfgparts.bind_1)); + sheet->NewGroup(str, 0, y, NEWUI_ALIGN_HORIZ); + clear_binding[1] = sheet->AddCheckBox(TXT_CFG_CLEAR); + if (ctype_fn[1] == ctAxis || ctype_fn[1] == ctMouseAxis) { + sheet->AddText(TXT_CFG_INVERT); + inv_binding[1] = sheet->AddFirstRadioButton(TXT_NO); + sheet->AddRadioButton(TXT_YES); + *inv_binding[1] = CHECK_FLAG(cfgflags_fn[1], CTFNF_INVERT) ? 1 : 0; + } + y+= 30; + } + wnd.Open(); + do + { + res = wnd.DoUI(); + if (res == NEWUIRES_FORCEQUIT) { + break; + } + } + while (res != UID_OK && res != UID_CANCEL); + if (res == UID_OK) { + if (clear_binding[0] && (*clear_binding[0])) { + cfgparts.bind_0 = NULL_BINDING; + cfgparts.ctrl_0 = NULL_CONTROLLER; + } + if (clear_binding[1] && (*clear_binding[1])) { + cfgparts.bind_1 = NULL_BINDING; + cfgparts.ctrl_1 = NULL_CONTROLLER; + } + if (inv_binding[0]) { + cfgflags_fn[0] = (*inv_binding[0]) ? (cfgflags_fn[0] | CTFNF_INVERT) : (cfgflags_fn[0] & (~CTFNF_INVERT)); + } + if (inv_binding[1]) { + cfgflags_fn[1] = (*inv_binding[1]) ? (cfgflags_fn[1] | CTFNF_INVERT) : (cfgflags_fn[1] & (~CTFNF_INVERT)); + } + + ccfgdata_fn = unify_config_data(&cfgparts); + Controller->set_controller_function(fnid, ctype_fn, ccfgdata_fn, cfgflags_fn); + } + wnd.Close(); + wnd.Destroy(); +} +// used in weapon selection: returns new slot. +int weapon_select_dialog(int wpn, bool is_secondary) +{ + newuiTiledWindow wnd; + newuiListBox *lbox; + newuiSheet *sheet; + int res, i; + ushort retval; + wpn &= (~WPNSEL_SKIP); + wnd.Create(NULL, 0, 0, 320,288); + sheet = wnd.GetSheet(); + sheet->NewGroup(NULL, 116, 210, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK, UID_OK); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); + sheet->NewGroup(NULL, 0, 0); + sheet->AddText(TXT_WPNCFGHELP3); + sheet->AddText(TXT(Static_weapon_names_msg[wpn])); + sheet->NewGroup(NULL, 0, 40); + lbox = sheet->AddListBox(192, 160, UID_WPNLIST, UILB_NOSORT); +// add primaries or secondaries. + for (i = 0; i < 10; i++) + { + int wpnidx; + char str[64]; + wpnidx = is_secondary ? GetAutoSelectSecondaryWpnIdx(i) : GetAutoSelectPrimaryWpnIdx(i); + if (wpnidx != WPNSEL_INVALID) { + if (wpnidx & WPNSEL_SKIP) { + wpnidx &= (~WPNSEL_SKIP); + sprintf(str, TXT_WPNSELBTN, TXT(Static_weapon_names_msg[wpnidx])); + } + else { + sprintf(str, "%s", TXT(Static_weapon_names_msg[wpnidx])); + } + lbox->AddItem(str); + } + else { + lbox->AddItem("ERR-WPN"); + } + } + wnd.Open(); + res = wnd.DoUI(); + wnd.Close(); + switch (res) + { + case UID_WPNLIST: + case UID_OK: + retval = lbox->GetCurrentIndex(); + break; + default: + retval = -1; + } + wnd.Destroy(); + + return retval; +} +void joystick_calibration() +{ +#if defined(WIN32) + extern bool Win32JoystickCalibrate(); + if (!Win32JoystickCalibrate()) + { + DoMessageBox(TXT_ERROR, TXT_CALIBJOYSTICKFAIL, MB_OK); + } +#endif +} +void joystick_settings_dialog() +{ + newuiTiledWindow wnd; + newuiSheet *sheet; + int res, i,y; + short curpos; + tSliderSettings slider_set; + char axis_str[N_JOY_AXIS] = { 'X','Y','Z','R','U','V' }; + short *joy_sens[N_JOY_AXIS]; + short *mse_sens[N_MOUSE_AXIS]; + short *ff_gain =NULL; + bool *ff_enabled =NULL; + bool *ff_auto_center =NULL; + bool ff_auto_center_support = false; + bool ff_found = false; + bool *joy_enabled; + bool *mse_enabled; + int *msectl; + + wnd.Create(TXT_JOYMOUSESETTINGS, 0,0,480,384); + sheet = wnd.GetSheet(); +// standard ui + sheet->NewGroup(NULL, 270, 288, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK, UID_OK); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); +// sensitivity sliders for joystick + sheet->NewGroup(TXT_SENSITIVITY, 0, 0); + for (i = 0; i < N_JOY_AXIS; i++) + { + char str[16]; + float val = Controller->get_axis_sensitivity(ctAxis, i+1); + slider_set.min_val.f = 0.0f; + slider_set.max_val.f = JOY_AXIS_SENS_RANGE; + slider_set.type = SLIDER_UNITS_FLOAT; + curpos = CALC_SLIDER_POS_FLOAT(val, &slider_set, CFG_AXIS_SENS_RANGE); + sprintf(str, TXT_CONTAXIS, axis_str[i]); + joy_sens[i] = sheet->AddSlider(str, CFG_AXIS_SENS_RANGE, curpos, &slider_set); + } +#ifndef MACINTOSH //DAJ + sheet->NewGroup(TXT_MSESENS, 0, 220); + + for (i = 0; i < N_MOUSE_AXIS; i++) + { + char str[16]; + float val = Controller->get_axis_sensitivity(ctMouseAxis, i+1); + slider_set.min_val.f = 0.0f; + slider_set.max_val.f = MSE_AXIS_SENS_RANGE; + slider_set.type = SLIDER_UNITS_FLOAT; + curpos = CALC_SLIDER_POS_FLOAT(val, &slider_set, CFG_AXIS_SENS_RANGE); + sprintf(str, TXT_CONTAXIS, axis_str[i]); + mse_sens[i] = sheet->AddSlider(str, CFG_AXIS_SENS_RANGE, curpos, &slider_set); + } +// joystick and mouse enabled + y = 0; + sheet->NewGroup(TXT_CONTROL_TOGGLES, 210, y); + joy_enabled = sheet->AddLongCheckBox(TXT_JOYENABLED); + mse_enabled = sheet->AddLongCheckBox(TXT_CFG_MOUSEENABLED); + *joy_enabled = CHECK_FLAG(Current_pilot.read_controller, READF_JOY) ? true : false; + *mse_enabled = CHECK_FLAG(Current_pilot.read_controller, READF_MOUSE) ? true : false; + y += 50; + sheet->NewGroup(TXT_MOUSECONTROL, 210, y); + msectl = sheet->AddFirstLongRadioButton(TXT_MOUSEFLIGHTSIM); + sheet->AddLongRadioButton(TXT_MOUSELOOK); + *msectl = Current_pilot.mouselook_control ? 1 : 0; + y += 50; + sheet->NewGroup(NULL, 210, y); + sheet->AddLongButton(TXT_CALIBJOYSTICK, UID_JOYCFG); +// force feedback stuff + y += 30; + ddio_ff_GetInfo(&ff_found, NULL); + if (ff_found) { + sheet->NewGroup(TXT_TITLE_FORCEFEEDBACK, 210, y); + ff_enabled = sheet->AddLongCheckBox(TXT_CFG_FFENABLED, ForceIsEnabled()); + ff_auto_center_support = ddio_ffjoy_SupportAutoCenter(kJoy1); + if(ff_auto_center_support){ + // force feedback joystick supports autocentering + // ---------------------------------------------- + ff_auto_center = sheet->AddLongCheckBox(TXT_CFG_FFAUTOCENTER, ForceIsAutoCenter()); + } + curpos = (short)(ForceGetGain()*50.0f); + slider_set.type = SLIDER_UNITS_PERCENT; + ff_gain = sheet->AddSlider(TXT_CFG_FORCEGAIN,50,curpos,&slider_set); + } +#else + sheet->NewGroup(NULL, 210, 20); + sheet->AddLongButton("Input Sprocket", UID_JOYCFG); +#endif + wnd.Open(); + do + { + res = wnd.DoUI(); + if (res == NEWUIRES_FORCEQUIT) { + break; + } + if (res == UID_JOYCFG) { +#ifdef MACINTOSH + inSprocket_Configure(); +#else + joystick_calibration(); +#endif + } + } + while (res != UID_OK && res != UID_CANCEL); + if (res == UID_OK) { + float f_temp; + + Current_pilot.read_controller = (*joy_enabled) ? READF_JOY : 0; + for (i = 0; i < N_JOY_AXIS; i++) + { + float val = CALC_SLIDER_FLOAT_VALUE(*joy_sens[i], 0.0f, JOY_AXIS_SENS_RANGE, CFG_AXIS_SENS_RANGE); + Controller->set_axis_sensitivity(ctAxis, i+1, val); + } +#ifndef MACINTOSH + Current_pilot.read_controller |= (*mse_enabled) ? READF_MOUSE : 0; + for (i = 0; i < N_MOUSE_AXIS; i++) + { + float val = CALC_SLIDER_FLOAT_VALUE(*mse_sens[i], 0.0f, MSE_AXIS_SENS_RANGE, CFG_AXIS_SENS_RANGE); + Controller->set_axis_sensitivity(ctMouseAxis, i+1, val); + } + Current_pilot.mouselook_control = (*msectl) ? true : false; + // force feedback stuff + if (ff_enabled) { + if (*ff_enabled) { + ForceEnable(); + } + else { + ForceDisable(); + } + } + if (ff_auto_center_support) { + if (*ff_auto_center) { + ForceEnableAutoCenter(); + } + else { + ForceDisableAutoCenter(); + } + } + if (ff_gain) { + f_temp = (*ff_gain)*0.5f; + ForceSetGain(f_temp); + } +#endif + } + wnd.Close(); + wnd.Destroy(); +} +#ifdef MACINTOSH +#define CFG_KEY_RAMP_MAX 2.0f +#else +#define CFG_KEY_RAMP_MAX 1.0f +#endif +#define CFG_KEY_RAMP_RANGE 20 +void key_settings_dialog() +{ + newuiTiledWindow wnd; + newuiSheet *sheet; + int res; + short curpos; + tSliderSettings slider_set; + short *key_ramp_speed; + wnd.Create(TXT_KEYSETTINGS, 0,0,384,256); + sheet = wnd.GetSheet(); + sheet->NewGroup(NULL, 0, 30); + slider_set.min_val.f = 0.0f; + slider_set.max_val.f = CFG_KEY_RAMP_MAX; + slider_set.type = SLIDER_UNITS_FLOAT; + curpos = CALC_SLIDER_POS_FLOAT(Key_ramp_speed, &slider_set, CFG_KEY_RAMP_RANGE); + key_ramp_speed = sheet->AddSlider(TXT_KEYRAMPINGTIME, CFG_KEY_RAMP_RANGE, curpos, &slider_set); + sheet->NewGroup(NULL, 180, 160, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK, UID_OK); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); + wnd.Open(); + do + { + res = wnd.DoUI(); + if (res == NEWUIRES_FORCEQUIT) { + break; + } + } + while (res != UID_OK && res != UID_CANCEL); + if (res == UID_OK) { + Key_ramp_speed = CALC_SLIDER_FLOAT_VALUE(*key_ramp_speed, 0.0f, CFG_KEY_RAMP_MAX, CFG_KEY_RAMP_RANGE); + } + wnd.Close(); + wnd.Destroy(); +} +////////////////////////////////////////////////////////////////////////////// +// configure controller new way +typedef struct t_ctlcfgswitchcb_data +{ + joy_cfg_screen *joycfg; + key_cfg_screen *keycfg; + wpnsel_cfg_screen *wpncfg; + cfg_screen *curcfg; +} t_ctlcfgswitchcb_data; +// called when we switch menus +void CtlConfigSwitchCB(newuiMenu *menu, short old_option_id, short new_option_id, void *data) +{ + t_ctlcfgswitchcb_data *cfgdata = (t_ctlcfgswitchcb_data *)data; +// performs custom gadget deinitialization and reinitilization + if (cfgdata->curcfg) { + cfgdata->curcfg->unrealize(); + } + + switch (new_option_id) + { + case IDV_KCONFIG: cfgdata->curcfg = cfgdata->keycfg; break; + case IDV_CCONFIG: cfgdata->curcfg = cfgdata->joycfg; break; + case IDV_WPNSEL: cfgdata->curcfg = cfgdata->wpncfg; break; + default: cfgdata->curcfg = NULL; + } + if (cfgdata->curcfg) { + cfgdata->curcfg->realize(); + } +} +// keep multiplayer going! +void CtlConfigUICB() +{ + if (Game_mode & (GM_MULTI)) { + Skip_render_game_frame = true; + GameFrame(); + Skip_render_game_frame = false; + } + else if (GetFunctionMode() == GAME_MODE) { + extern void GameProcessMusic(); + float saved_frame_time = Frametime; + Frametime = UIFrameTime; + Sound_system.BeginSoundFrame(true); + GameProcessMusic(); + Sound_system.EndSoundFrame(); + Frametime = saved_frame_time; + } +} +// the main controller config menu loop +void CtlConfig(int mode) +{ + newuiLargeMenu menu; + joy_cfg_screen joycfg; + key_cfg_screen keycfg; + wpnsel_cfg_screen wpncfg; + t_ctlcfgswitchcb_data cfg_cb_data; + int res; + void (*old_callback)() = GetUICallback(); + int old_mode = GetScreenMode(); +// must switch to standard resolution for large window + SetScreenMode(SM_MENU); + LoadControlConfig(); +// open menu + menu.Create(); + menu.Open(); + joycfg.setup(&menu); + keycfg.setup(&menu); + wpncfg.setup(&menu); + cfg_cb_data.joycfg = &joycfg; + cfg_cb_data.keycfg = &keycfg; + cfg_cb_data.wpncfg = &wpncfg; + cfg_cb_data.curcfg = NULL; + menu.AddSimpleOption(UID_CANCEL, TXT_GEN_EXIT); + menu.SetCurrentOption((mode==CTLCONFIG_WPNSEL) ? IDV_WPNSEL : (mode==CTLCONFIG_CONTROLLER) ? IDV_CCONFIG : IDV_KCONFIG); + menu.SetOnOptionSwitchCB(CtlConfigSwitchCB, (void *)&cfg_cb_data); + +// if(mode==CTLCONFIG_CONTROLLER) { +// inSprocket_Configure(); +// } +// make sure game is running! + SetUICallback(CtlConfigUICB); +// run menu + do + { + res = menu.DoUI(); + // immediate checking of any option ids. + if (res == UID_CANCEL || res == NEWUIRES_FORCEQUIT) { + break; // next pass will quit + } +// if(res == IDV_CCONFIG) { +// inSprocket_Configure(); +// } + switch (menu.GetCurrentOption()) + { + case IDV_KCONFIG: keycfg.process(res); break; + case IDV_CCONFIG: joycfg.process(res); break; + case IDV_WPNSEL: wpncfg.process(res); break; + }; + } + while (1); +// write them out and close. + wpncfg.finish(); + keycfg.finish(); + joycfg.finish(); + SaveControlConfig(); // write out config to pilot when destroying window + PltWriteFile(&Current_pilot); + menu.Close(); + menu.Destroy(); + SetScreenMode(old_mode); + SetUICallback(old_callback); + Skip_render_game_frame = false; +} +int CtlFindBinding(int controlid,bool keyboard) +{ + int i=0; + + if(keyboard) + { + for(i=0;i + * + * $Log: /DescentIII/main/ctlconfig.h $ + * + * 7 4/30/99 12:12p Samir + * added strings for key ramping, other buttons in general config menu. + * + * 6 4/22/99 3:43p Kevin + * Training missions show controls on screen + * + * 5 2/16/99 12:07p Samir + * redid controller config with new ui. + * + * 4 10/18/98 7:24p Samir + * added a reset to defaults options for key and joystick config. + * + * 3 2/15/98 7:06p Samir + * Keyboard configuration system done. Not joystick. + * + * 2 2/10/98 3:45p Samir + * Added list of configuration items. + * + * 1 2/10/98 11:49a Samir + * Initial revision. + * + * $NoKeywords: $ + */ + +#ifndef CTLCONFIG_H +#define CTLCONFIG_H + +#define CTLCONFIG_KEYBOARD 0 +#define CTLCONFIG_CONTROLLER 1 +#define CTLCONFIG_WPNSEL 2 + +typedef struct t_cfg_element +{ + short fn_id; // -1 = group start + short text; // text string id. + short x; + short y; // location (for groups only) +} +t_cfg_element; + +extern t_cfg_element Cfg_key_elements[]; +extern t_cfg_element Cfg_joy_elements[]; + +int CtlFindBinding(int controlid,bool keyboard); +// configures controllers. +void CtlConfig(int mode); + +// opens the settings dialogs. +void joystick_settings_dialog(); +void key_settings_dialog(); + +#endif \ No newline at end of file diff --git a/Descent3/ctlconfigtxt.h b/Descent3/ctlconfigtxt.h new file mode 100644 index 000000000..9f15f4e20 --- /dev/null +++ b/Descent3/ctlconfigtxt.h @@ -0,0 +1,169 @@ +/* + * $Logfile: /DescentIII/Main/ctlconfigtxt.h $ + * $Revision: 26 $ + * $Date: 3/23/99 4:26p $ + * $Author: Jeff $ + * + * Control Config text items + * + * $Log: /DescentIII/Main/ctlconfigtxt.h $ + * + * 26 3/23/99 4:26p Jeff + * fixed text for audio taunt 3 & 4 + * + * 25 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 24 2/28/99 3:22a Samir + * added rearview text. + * + * 23 2/26/99 2:13a Samir + * massive reorg of weapon select prec + * + * 22 2/16/99 12:07p Samir + * redid controller config with new ui. + * + * 21 11/30/98 5:22p Samir + * you can no longer bind keys to number keys. + * + * 20 10/21/98 12:00p Samir + * oops. + * + * 19 10/21/98 12:00p Samir + * don't allow player to configure ESC and function keys. + * + * 18 10/17/98 7:32p Samir + * made controller config text lowercase to save space. + * + * 17 10/08/98 10:52a Samir + * added countermeasure and weapon cycling. + * + * 16 9/17/98 3:24p Samir + * added headlight configuration. + * + * 15 8/31/98 6:50p Jeff + * made inventory and countermeasure keys customizable + * + * 14 6/18/98 5:49p Samir + * finished controller string localization minus the binding stuff. + * + * 13 6/18/98 4:48p Samir + * added changes for multiple configs for joystick controls. + * + * 12 6/17/98 3:27p Jeff + * Changes made for localization + * + * 11 6/05/98 5:34p Samir + * new config system. + * + * 10 5/19/98 3:36p Samir + * added automap key. + * + * 9 5/15/98 3:13p Samir + * formatted text better. + * + * 8 5/11/98 4:53p Samir + * added axis sliding, config. + * + * 7 4/08/98 3:44p Samir + * added toggle bank and configuration for afterburner + * + * 6 2/16/98 9:27p Samir + * Simple joystick configuration working. + * + * 5 2/16/98 3:02p Samir + * Joystick config sort of working. + * + * 4 2/15/98 7:06p Samir + * Keyboard configuration system done. Not joystick. + * + * 3 2/13/98 6:37p Samir + * Simple keyboard configuration. + * + * 2 2/10/98 4:55p Samir + * Added rudimentary items for controller config. + * + * 1 2/10/98 3:47p Samir + * + * $NoKeywords: $ + */ +#include "stringtable.h" + + +#define CtlText_FirePrimary TXI_KB_FIREPRIMARY +#define CtlText_FireSecondary TXI_KB_FIRESECONDARY +#define CtlText_Accelerate TXI_KB_ACCELERATE +#define CtlText_Reverse TXI_KB_REVERSE +#define CtlText_SlideHoriz TXI_KB_SLIDEHORIZ +#define CtlText_SlideLeft TXI_KB_SLIDELEFT +#define CtlText_SlideRight TXI_KB_SLIDERIGHT +#define CtlText_SlideVert TXI_KB_SLIDEVERT +#define CtlText_SlideUp TXI_KB_SLIDEUP +#define CtlText_SlideDown TXI_KB_SLIDEDOWN +#define CtlText_BankLeft TXI_KB_BANKLEFT +#define CtlText_BankRight TXI_KB_BANKRIGHT +#define CtlText_PitchUp TXI_KB_PITCHUP +#define CtlText_PitchDown TXI_KB_PITCHDOWN +#define CtlText_TurnLeft TXI_KB_TURNLEFT +#define CtlText_TurnRight TXI_KB_TURNRIGHT +#define CtlText_FireFlare TXI_KB_FIREFLARE +#define CtlText_ToggleSlide TXI_KB_TOGGLESLIDE +#define CtlText_ToggleBank TXI_KB_TOGGLEBANK +#define CtlText_Heading TXI_KB_HEADING +#define CtlText_Pitch TXI_KB_PITCH +#define CtlText_Throttle TXI_KB_THROTTLE +#define CtlText_Forward TXI_KB_FORWARD +#define CtlText_Bank TXI_KB_BANK +#define CtlText_Afterburn TXI_KB_AFTERBURN +#define CtlText_Automap TXI_KB_AUTOMAP +#define CtlText_PrevInv TXI_KB_PREVINV +#define CtlText_NextInv TXI_KB_NEXTINV +#define CtlText_InvUse TXI_KB_INVUSE +#define CtlText_PrevCntMs TXI_KB_PREVCNTMS +#define CtlText_NextCntMs TXI_KB_NEXTCNTMS +#define CtlText_CntMsUse TXI_KB_CNTMSUSE +#define CtlText_Headlight TXI_KB_HEADLIGHT +#define CtlText_WpnCycP TXI_KB_WPNPCYCLE +#define CtlText_WpnCycS TXI_KB_WPNSCYCLE +#define CtlText_AudioTaunt1 TXI_KB_AUDIOTAUNT1 +#define CtlText_AudioTaunt2 TXI_KB_AUDIOTAUNT2 +#define CtlText_Rearview TXI_KB_REARVIEW +#define CtlText_AudioTaunt3 TXI_KB_AUDIOTAUNT3 +#define CtlText_AudioTaunt4 TXI_KB_AUDIOTAUNT4 + +#define CtlText_WeaponGroup TXI_KB_WPNGRP +#define CtlText_MiscGroup TXI_KB_MISCGRP +#define CtlText_ThrustGroup TXI_KB_THRUSTGRP +#define CtlText_TurningGroup TXI_KB_TURNINGGRP + +/* +static char *CtlText_FirePrimary ="Fire Primary\t\t"; +static char *CtlText_FireSecondary ="Fire Secondary\t\t"; +static char *CtlText_Accelerate ="Accelerate\t\t\t"; +static char *CtlText_Reverse ="Reverse\t\t\t"; +static char *CtlText_SlideHoriz ="Slide Horizontal\t\t"; +static char *CtlText_SlideLeft ="Slide Left\t\t\t"; +static char *CtlText_SlideRight ="Slide Right\t\t\t"; +static char *CtlText_SlideVert ="Slide Vertical\t\t"; +static char *CtlText_SlideUp ="Slide Up\t\t\t"; +static char *CtlText_SlideDown ="Slide Down\t\t\t"; +static char *CtlText_BankLeft ="Bank Left\t\t\t"; +static char *CtlText_BankRight ="Bank Right\t\t\t"; +static char *CtlText_PitchUp ="Pitch Up\t\t\t"; +static char *CtlText_PitchDown ="Pitch Down\t\t\t"; +static char *CtlText_TurnLeft ="Turn Left\t\t\t"; +static char *CtlText_TurnRight ="Turn Right\t\t\t"; +static char *CtlText_FireFlare ="Fire Flare\t\t\t"; +static char *CtlText_ToggleSlide ="Toggle Slide\t\t"; +static char *CtlText_ToggleBank ="Toggle Bank\t\t\t"; +static char *CtlText_Heading ="Heading\t\t\t"; +static char *CtlText_Pitch ="Pitch\t\t\t\t"; +static char *CtlText_Throttle ="Throttle\t\t\t"; +static char *CtlText_Forward ="Forward\t\t\t"; +static char *CtlText_Bank ="Bank\t\t\t\t"; +static char *CtlText_Afterburn ="Afterburner\t\t\t"; +static char *CtlText_Automap ="Automap\t\t\t"; +*/ + + diff --git a/Descent3/d3serial.cpp b/Descent3/d3serial.cpp new file mode 100644 index 000000000..eaa5c37da --- /dev/null +++ b/Descent3/d3serial.cpp @@ -0,0 +1,220 @@ +/* +* $Logfile: /DescentIII/main/d3serial.cpp $ +* $Revision: 15 $ +* $Date: 5/13/99 11:18a $ +* $Author: Matt $ +* +* Functions to check the copy of the executable for expiration, registered name +* +* $Log: /DescentIII/main/d3serial.cpp $ + * + * 15 5/13/99 11:18a Matt + * Added some text. + * + * 14 4/16/99 11:59a Matt + * Took out include of io.h, because it wasn't needed. + * + * 13 4/16/99 12:39a Matt + * Took out Linux ifdef around include of io.h, since it's a system header + * file and there's no harm in including it in the Windows version. + * + * 12 4/15/99 1:38a Jeff + * changes for linux compile + * + * 11 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 10 2/17/99 3:06p Matt + * Updated copyrights. + * + * 9 10/18/98 9:12p Matt + * Use new symbolic constant for the program name. + * + * 8 10/13/98 2:30p Jeff + * changed serialization msg for demo + * + * 7 10/11/98 2:58a Jeff + * fixed serialization code + * + * 6 7/13/98 12:41p Jeff + * added serial number support + * + * 5 6/08/98 3:16p Matt + * Mucked with formatting & content of serialization message. + * + * 4 6/04/98 11:32a Jeff + * Better 'warning' box for the serialization + * + * 3 2/10/98 10:43a Jeff + * fixed it so a nonregistered version will run (to make development + * easier) + * + * 2 2/07/98 6:33p Jeff + * Initial Creation (Based on D2 serialization) +* +* $NoKeywords: $ +*/ + +#include +#include +#include + +#include "d3serial.h" +#include "game.h" +#include "debug.h" +#include "descent.h" +#include +#include "mono.h" +#include "CFILE.H" +#include "program.h" + +#include + + +#define SERIAL_NO_ERR 0 +#define SERIAL_EXPIRED 1 +#define SERIAL_BAD_CHECKSUM 2 +#define SERIAL_UNREGISTERED 3 + +static char name_copy[DESC_ID_LEN]; + +unsigned long d3_serialnum = 100000; + +//checks the exectuable (serialization) +int SerialCheck(void) +{ + char name2[] = DESC_ID_TAG "0000000000000000000000000000000000000000"; + char time_str[] = DESC_DEAD_TIME_TAG "00000000"; + int i, found; + unsigned long *checksum, test_checksum; + unsigned long *serialnum; + time_t current_time, saved_time; + +#ifdef DEMO + char regstr[] = "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTICE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + "\nThis Descent 3 Demo %s has been specially prepared for\n" + "\n" + "\t\t%s (Serial Num: %d)\n" + "\n" + " and is the Confidential Property of Outrage Entertainment, Inc.\n" + "\n" + "\n" + "\t\tDISTRIBUTION IS PROHIBITED\n" + "\n" + "\n" + " Descent 3 is Copyright (c) 1998,1999 Outrage Entertainment, Inc.\n" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; +#else + char regstr[] = "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTICE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + "\nThis pre-release version of Descent 3 has been specially prepared for\n" + "\n" + "\t\t%s (Serial Num: %d)\n" + "\n" + " and is the Confidential Property of Outrage Entertainment, Inc.\n" + "\n" + "\n" + "\t\tDISTRIBUTION IS PROHIBITED\n" + "\n" + "This version of Descent 3 should be used for evaluation and testing only.\n" + "Because the game is not yet complete, items may be missing the bugs may be\n" + "encountered.\n" + "\n" + " Descent 3 is Copyright (c) 1998,1999 Outrage Entertainment, Inc.\n" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; +#endif + + //get the expiration time of the executable + char *start_time = &time_str[DESC_DEAD_TIME_TAG_LEN]; + saved_time = (time_t)strtol(start_time,NULL, 16); + if (saved_time == (time_t)0) + //this guy has never been registered with the serialization program + return SERIAL_NO_ERR; + + //adjust pointer, so it points to the registered name + char *name = name2 + DESC_ID_TAG_LEN; + + //save for later use + strcpy(name_copy,name); + + //get current date from the system + current_time = time(NULL); + + //check to see if executable has expired + if (current_time >= saved_time) + { + return SERIAL_EXPIRED; + } + + + //check the checksum created by the registered name + test_checksum = 0; + for (i = 0; i < (signed)strlen(name); i++) { + found = 0; + test_checksum += name[i]; + if (((test_checksum / 2) * 2) != test_checksum) + found = 1; + test_checksum = test_checksum >> 1; + if (found) + test_checksum |= 0x80000000; + } + static char desc_id_checksum_str[] = DESC_CHKSUM_TAG "0000"; //4-byte checksum + char *checksum_ptr = desc_id_checksum_str + DESC_CHKSUM_TAG_LEN; + + //compare generated checksum with that in the executable + checksum = (unsigned long *)&(checksum_ptr[0]); + if (test_checksum != *checksum) + { + return SERIAL_BAD_CHECKSUM; + } + + static char desc_id_serialnum_str[] = DESC_SERIALNUM_TAG "0000"; //4-byte serialnum + char *serialnum_ptr = desc_id_serialnum_str + DESC_SERIALNUM_TAG_LEN; + + serialnum = (unsigned long *)&(serialnum_ptr[0]); + d3_serialnum = *serialnum; + + //this guy is ok, we can exit clean now + char buffer[400]; + + + +#ifdef DEMO + char ver[10]; + sprintf(ver,"Beta %d.%d.%d",D3_MAJORVER,D3_MINORVER,D3_BUILD); + sprintf(buffer,regstr,ver,name,d3_serialnum); +#else + sprintf(buffer,regstr,name,d3_serialnum); +#endif + + Debug_MessageBox(OSMBOX_OK,PRODUCT_NAME,buffer); + + return SERIAL_NO_ERR; +} + +//displays an error box displaying what went wrong with serialization checking +void SerialError(int error) +{ + switch(error) + { + case SERIAL_EXPIRED: + { + Debug_MessageBox(OSMBOX_OK,PRODUCT_NAME,"Executable has expired"); + break; + } + case SERIAL_BAD_CHECKSUM: + { + Debug_MessageBox(OSMBOX_OK,PRODUCT_NAME,"Bad Checksum"); + break; + } + case SERIAL_UNREGISTERED: + { + Debug_MessageBox(OSMBOX_OK,PRODUCT_NAME,"This is unregistered, please serialize"); + break; + } + } +} + +unsigned long SerialGetSerialNum(void) +{ + return d3_serialnum; +} diff --git a/Descent3/d3serial.h b/Descent3/d3serial.h new file mode 100644 index 000000000..768ab2462 --- /dev/null +++ b/Descent3/d3serial.h @@ -0,0 +1,67 @@ +/* +* $Logfile: /DescentIII/main/d3serial.h $ +* $Revision: 5 $ +* $Date: 10/11/98 2:58a $ +* $Author: Jeff $ +* +* file header for serialization +* +* $Log: /DescentIII/main/d3serial.h $ + * + * 5 10/11/98 2:58a Jeff + * fixed serialization code + * + * 4 9/29/98 3:04p Jeff + * + * 3 7/13/98 12:41p Jeff + * added serial number support + * + * 2 2/07/98 6:35p Jeff +* +* $NoKeywords: $ +*/ + + +#ifndef __SERIALIZE_H_ +#define __SERIALIZE_H_ + + +#include + +#define DESC_ID_LEN 40 //how long the id string can be +#define DESC_CHECKSUM_LEN 4 //checksum is 4 bytes +#define DESC_SERIALNUM_LEN 4 //serialnum is 4 bytes +#define DESC_DEAD_TIME_LEN 8 //dead time is 8 bytes + +#define DESC_ID_TAG "Midway in our life's journey, I went astray" +#define DESC_ID_TAG_LEN 43 + +#define DESC_CHKSUM_TAG "alone in a dark wood." +#define DESC_CHKSUM_TAG_LEN 21 + +#define DESC_SERIALNUM_TAG "You've entered Lord Spam's Pleasure Dome" +#define DESC_SERIALNUM_TAG_LEN 40 + +#define DESC_DEAD_TIME_TAG "from the straight road and woke to find myself" +#define DESC_DEAD_TIME_TAG_LEN 46 + + +//checks exectuable (returns !0 on error) +int SerialCheck(void); + +//given return value from SerialCheck() it reports the error +void SerialError(int error); + +//returns the serialnumber of the user +unsigned long SerialGetSerialNum(void); + +///////////////////////////////////////////////////////////////////////////// +// These are the functions used for serialization +// if this function returns: +// 1 : than it's a serialized exe, num will be given the serial num +// 0 : than it isn't a serialized exe +// -1 : detected a hacked exe +char GetInternalSerializationNumber(int *num); + + +#endif \ No newline at end of file diff --git a/Descent3/damage.cpp b/Descent3/damage.cpp new file mode 100644 index 000000000..86513a64b --- /dev/null +++ b/Descent3/damage.cpp @@ -0,0 +1,1661 @@ +/* + * $Logfile: /DescentIII/main/damage.cpp $ + * $Revision: 164 $ + * $Date: 3/22/00 12:13p $ + * $Author: Matt $ + * + * Applies and handles damage to game entities. + * + * $Log: /DescentIII/main/damage.cpp $ + * + * 164 3/22/00 12:13p Matt + * Added some if statements to not damage & kill objects when playing back + * a demo. + * + * 163 1/26/00 9:19p Jeff + * added support for IntelliVIBE DLL + * + * 162 10/21/99 2:44p Matt + * Mac merge + * + * 161 10/08/99 4:54p Nate + * Fixed bug that prevented custom damage modification from working + * + * 160 10/08/99 4:28p Chris + * Added the forcefield and glass breaking override options + * + * 159 9/22/99 11:58a Jeff + * always call the multiplayer event when player shields change and then + * check to see if the player should be killed + * + * 158 7/26/99 11:07a Jason + * fixed broke glass bug + * + * 157 5/22/99 10:27p Jason + * changes for multiplayer and buildings, ai + * + * 156 5/21/99 4:30a Matt + * Added commented-out code to play sound for sparking death, in case we + * ever get a sound we like. + * + * 155 5/17/99 9:23p Matt + * When applying damage to player, only play invulnerability sound if + * would otherwise be playing a sound. + * + * 154 5/09/99 10:50p Jason + * made napalm rocket burn people longer + * + * 153 5/04/99 6:53p Jeff + * added event for when a player dies + * + * 152 4/30/99 2:32p Matt + * Doubled the velocity of straight-up flying deaths to make Josh's pop + * machines cooler. + * + * 151 4/30/99 3:28a Chris + * Flares disappear when they hit bad surfaces (volatile and lava) + * + * 150 4/29/99 6:48p Matt + * Made volatile surfaces apply damage and lava surfaces catch the player + * on fire. Both now generate the steam puff effect. + * + * 149 4/28/99 4:48a Chris + * Added the no-scale damage by diff checkbox + * + * 148 4/27/99 12:12a Matt + * Added option to make object not tumble in flying death + * + * 147 4/26/99 7:07p Jason + * fixed multiplayer bug with destroy object + * + * 146 4/26/99 6:23p Jeff + * don't apply damage to OBJ_DUMMYs! (It cause problems in multiplayer + * with the guidebot) + * + * 145 4/25/99 10:38p Matt + * Made the Osiris kill object function work on players. + * + * 144 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 143 4/24/99 11:38p Matt + * Set the death delay for electrical deaths on default-death objects. + * This has apparently been broken since I rewrote the death system ages + * ago, but no one reported it. + * + * 142 4/24/99 4:32p Jason + * made fusion damage sound not be irritating + * + * 141 4/23/99 12:32a Matt + * Added a death info option to play the explosion sound at the start of + * the delay, instead of at the end when the object actually dies. + * + * 140 4/22/99 4:15p Matt + * Don't bash the delay time when setting the flying physics for dying + * objects. For default deaths, this was already set to the same values + * when setting up the death, and for explicit deaths, use the value from + * the page. + * + * 139 4/21/99 3:01p Matt + * Added a new type for dying objects that have AI, instead of keeping a + * flag in the dying info. + * + * 138 4/21/99 12:40p Jason + * made explosion system framerate independant + * + * 137 4/21/99 12:22p Matt + * Added extra time to delay-from-anim time to allow object to finish its + * current anim before starting the death anim. + * + * 136 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 135 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 134 4/12/99 6:15p Samir + * Sound priorities pass 1 + * + * 133 4/10/99 6:32p Matt + * Don't allow glass to be broken twice. + * + * 132 4/06/99 6:24p Jason + * various fixes for multiplayer + * + * 131 4/06/99 6:02p Matt + * Added score system + * + * 130 4/02/99 11:23a Matt + * Made KillObject not take a death_info struct, but rather the death info + * as individual parameters. Moved death_info into objinfo.h, since it's + * only used for generic objects. Took out fade-away death hack, now that + * fade-away deaths can be explicitely set. + * + * 129 4/01/99 4:28p Matt + * Cleaned up handling of dying flag to prevent infinite recursion. Jeff + * will test. + * + * 128 3/29/99 11:20a Matt + * Added more flexibility to death delays, and added fade away deaths. + * + * 127 3/22/99 1:47p Matt + * Moved breaking glass code from collide.cpp to damage.cpp + * + * 126 3/08/99 5:10p Chris + * Make upsidedown robots fall faster (use the loss anti-grav flag) + * + * 125 3/05/99 6:41p Matt + * In default death, instead of adding 3 second to delay time, calculate + * the time remaining the robot's current animation and add that. + * + * 124 3/05/99 3:32p Matt + * Added hack for making nomads fade out + * + * 123 3/05/99 11:30a Kevin + * Fixed really stupid bug + * + * 122 3/03/99 5:41p Matt + * Don't scale applied damage if damage type is GD_SCRIPTED + * + * 121 2/28/99 6:20p Matt + * Added the ability to set death types for generic objects. + * + * 120 2/26/99 2:03p Matt + * Got blastable doors working, and cleaned up concussive force code. + * + * 119 2/25/99 10:58a Matt + * Added new explosion system. + * + * 118 2/22/99 2:03p Jason + * added different damages for players and generics + * + * 117 2/13/99 6:16p Jeff + * fixed ApplyDamageTo functions so they only damage what objects they are + * supposed to + * + * 116 2/13/99 12:29a Jeff + * don't apply damage to OBJ_DUMMYs + * + * 115 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 114 2/08/99 5:17p Jason + * took out EMD effect and added more weapon options + * + * 113 2/07/99 1:19a Jeff + * added new multiplayer game events EVT_GAMEOBJKILLED and + * EVT_GAMEOBJDESTROYED + * + * 112 2/04/99 2:05p Matt + * Added blastable doors + * + * 111 2/04/99 11:36a Jason + * fixed some multi bugs + * + * 110 2/03/99 12:15p Jason + * added multiplayer vis optimizations + * + * 109 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 108 2/01/99 4:25p Jeff + * fixed player invulnerabilty after cinematic sequence and death bug + * + * 107 2/01/99 12:55p Jeff + * don't damage player if OF_DESTROYABLE is not set + * + * 106 1/28/99 12:09p Jeff + * added force feedback to player shake...fixed spelling error in define + * for forcefeedback + * + * 105 1/20/99 12:31p Chris + * Include the weapon_handle in evt_damage + * + * 104 1/20/99 2:13a Chris + * It is now possible for robots to have special immunities, resistances, + * and vunerabilities + * + * 103 1/18/99 2:46p Matt + * Combined flags & flags2 fields in object struct + * + * 102 1/17/99 7:20p Jeff + * doh! put back in call to multiplayer event on object damage + * + * 101 1/15/99 3:49a Jeff + * removed calls to multiplayer dll events (EVT_DAMAGED) + * + * 100 1/13/99 2:42p Jason + * added damage event + * + * 99 1/13/99 2:28a Chris + * Massive AI, OSIRIS update + * + * 98 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 97 1/06/99 3:07p Jason + * toned down redness + * + * 96 12/13/98 7:47p Chris + * Make walkers die slowly more often + * + * 95 12/07/98 2:26p Jason + * fixed a bug with peer to peer damage on the client end + * + * 94 12/04/98 3:03p Jason + * + * 93 12/04/98 1:41p Jason + * made napalm do even less damage + * + * 92 12/03/98 3:39p Jason + * added peer 2 peer style damage + * + * 91 12/02/98 10:30a Jason + * added additional damage types for client-side multiplayer + * + * 90 12/01/98 10:57a Jason + * fixed compiler warning + * + * 89 11/19/98 5:40p Kevin + * Demo system + * + * 88 11/19/98 12:22p Jason + * optimizations + * + * 87 11/13/98 2:25p Samir + * modify game music info when damaged or killed robot + * + * 86 11/13/98 12:29p Jason + * changes for weapons + * + * 85 11/11/98 2:46p Kevin + * Demo recording system work + * + * 84 11/11/98 11:41a Jason + * added sunlight damage + * + * 83 11/10/98 3:12p Chris + * Got rid of a const double to float warning + * + * 82 10/30/98 4:24p Jason + * changes for multiplayer + * + * 81 10/22/98 2:58p Chris + * Difficulty levels are in beta + * + * 80 10/22/98 10:50a Samir + * only register kill for robots so accuracy rating can be closer. + * + * 79 10/19/98 7:17p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 78 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 77 10/06/98 11:27a Jason + * added new death type for robots + * + * 76 9/30/98 5:35p Jason + * added multiplayer menu bailing for samir + * + * 75 9/25/98 5:02p Jason + * fixed parenting problems + * + * 74 9/17/98 6:08p Jason + * more tweaks for effects + * + * 73 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 72 9/14/98 4:17p Jason + * added friendly fire damage + * + * 71 8/14/98 6:26p Kevin + * for chris: dealt with killer==NULL in ExplodeObject + * + * 70 8/14/98 4:58p Jeff + * only if damage>0 does it play sound + * + * 69 8/14/98 12:49p Chris + * Working on attach system + * + * 68 8/12/98 12:04p Chris + * Attach system version .5 (still needs more work to be multiplayer + * friendly) + * + * 67 8/10/98 5:24p Jason + * made robots have a little variation when dying + * + * 66 8/03/98 5:56p Jason + * got fusion cannon working correctly + * + * 65 7/31/98 5:23p Jason + * added ship armor scalars + * + * 64 7/30/98 11:09a Jason + * added weapons that freeze and deform terrain + * + * 63 7/21/98 12:13p Jason + * Told DLL about which weapon was used to kill a player + * + * 62 7/15/98 2:33p Jason + * added scalar variables for various player skills + * + * 61 7/15/98 12:48p Jeff + * put in calls to handle new multiplayer event...when an object's shields + * change + * + * 60 6/30/98 11:39a Jason + * added AdditionalDamage packet type for multiplayer + * + * 59 6/29/98 12:12p Kevin + * Added robot damage and death packets + * + * 58 6/26/98 7:26p Jason + * checked in so kevin can work on coop + * + * 57 6/26/98 2:14p Jason + * fixed InitiatePlayerDeath bug with dead robots + * + * 56 6/02/98 6:04p Jason + * undid pre-e3 invul change + * + * 55 5/25/98 6:38p Matt + * Added needed include. + * + * 54 5/25/98 4:16p Jason + * made invulnerability different for E3 demo + * + * 53 5/22/98 6:22p Chris + * Improved Dynamic path allocation + * + * 52 5/19/98 2:41a Chris + * Added shockwave's -- enjoy. :) + * + * 51 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 50 5/04/98 12:55p Jason + * fixed NULL pointer bug + * + * 49 5/01/98 6:50p Samir + * call initiate player death with melee if killed by robot. + * + * 48 5/01/98 3:41p Chris + * + * 47 5/31/98 3:07p Chris + * Robots die slowly more often. Made robots die slowly when the killer + * object is a + * player or robot. (Melee attacks and bumping damage deaths) + * + * 46 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 45 4/17/98 3:57p Jason + * added microwave effect + * + * 44 4/17/98 1:59p Jason + * added cool object effects + * + * 43 4/10/98 3:28p Jason + * Turn off guided missile when you get hit + * + * 42 4/09/98 5:17p Jason + * got guided working in multiplayer + * + * 41 4/06/98 4:05p Jason + * made suicides work correctly + * + * 40 4/06/98 2:54p Jason + * yet more multiplayer changes + * + * 39 4/06/98 12:14p Jason + * changes to multiplayer + * + * 38 3/31/98 12:16p Jason + * some small changes for explosions + * + * 37 3/30/98 5:11p Jason + * more changes for game/dll integration + * + * 36 3/23/98 4:51p Jason + * incremental checkin for multiplay + * + * 35 3/23/98 4:36p Jason + * make powerups non-destroyable in multiplay + * + * 34 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 33 2/25/98 4:31p Jason + * changes for explosions + * + * 32 2/18/98 4:12a Jason + * took out ridiculous explosions + * + * 31 2/16/98 11:19p Chris + * Added support for melee robots + * + * 30 2/16/98 4:55p Chris + * Added default sounds for explosions + * + * 29 2/10/98 11:32a Samir + * Added D3X support when a player gets killed to the gamemode script. + * + * 28 2/09/98 5:38p Jeff + * changed EVT_PLAYERKILLED to EVT_GAMEPLAYERKILLED for osiris + * + * 27 2/05/98 2:54p Jason + * changes for explosions + * + * 26 2/04/98 9:28p Jason + * added some new weapons effects + * + * 25 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 24 2/03/98 4:04p Jeff + * added OSIRIS events for player killed and exploded + * + * 23 1/28/98 1:11p Jason + * took shields field out of Player struct + * + * 22 1/28/98 12:00p Jason + * more changes for multiplayer + * + * 21 1/21/98 6:09p Jason + * Got player deaths working in multiplayer + * + * 20 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 19 1/20/98 6:01p Jason + * first pass at getting multiplayer deaths working + * + * 18 1/19/98 6:23p Chris + * Fixed a crash when the killer is no longer alive. + * + * 17 1/19/98 10:04a Matt + * Added new object handle system + * + * 16 1/15/98 6:49p Jason + * incremental checkin for multiplayer + * + * 15 1/05/98 3:54p Chris + * Added ambient and explosion sounds + * + * 14 1/05/98 11:02a Jason + * made shake magnitude proportional to the distance from the explosion + * + * + * 13 12/12/97 6:13p Jason + * added sparks + * + * 12 12/08/97 5:21p Jason + * added code for destroyable buildings that leave their base object + * around + * + * 11 12/08/97 3:28p Jason + * tweaked explosions + * + * 10 12/03/97 5:53p Jason + * worked on cooler explosion effects + * + * 9 12/01/97 11:02a Jason + * made camera shake from explosions + * + * 8 12/01/97 9:50a Chris + * Added support for concussive forces, generalized robot collisions to + * generic collisions + * + * 7 11/18/97 4:37p Jason + * added visible damage system + * + * 6 11/04/97 5:50p Jason + * made #define for strange number (.6) + * + * 5 10/24/97 2:47p Jason + * added blast rings to robots + * + * 4 10/20/97 4:46p Jason + * changes for explosions + * + * 3 10/08/97 6:35p Samir + * Dying player ships get marked as destroyed. When death sequence is + * over, they are marked as dead. This is needed to better control + * movement of the dying ship. + * + * 2 10/03/97 11:58a Samir + * Added mission name + * + * 1 10/01/97 4:50p Samir + * Damage for players and robots. + * + * $NoKeywords: $ + */ + +#include +#include +#include "damage.h" +#include "object.h" +#include "objinfo.h" +#include "player.h" +#include "fireball.h" +#include "gameevent.h" +#include "hlsoundlib.h" +#include "multi.h" +#include "game.h" +#include "sounds.h" +#include "soundload.h" +#include "game2dll.h" +#include "weapon.h" +#include "ship.h" +#include "attach.h" +#include "difficulty.h" +#include "demofile.h" +#include "d3music.h" +#include "osiris_dll.h" +#include "D3ForceFeedback.h" +#include "multi_server.h" +#include "doorway.h" +#include "DeathInfo.h" +#include "AIGoal.h" +#include "viseffect.h" +#include "psrand.h" +#include "vibeinterface.h" + +// Shake variables +static matrix Old_player_orient; +float Shake_magnitude=0.0; + +// Causes an object to deform +void SetDeformDamageEffect (object *obj) +{ + if (obj->effect_info) + { + obj->effect_info->type_flags|=EF_DEFORM; + obj->effect_info->deform_time=1.0; + obj->effect_info->deform_range=.3f; + } +} + +#define MAX_NAPALM_DAMAGE_TIME 10.0f +// Causes an object to have napalm attached to it +void SetNapalmDamageEffect (object *obj,object *killer,int weapon_id) +{ + if (obj->effect_info) + { + obj->effect_info->type_flags|=EF_NAPALMED; + + if (Weapons[weapon_id].flags & WF_INVISIBLE) + obj->effect_info->damage_time+=(MAX_NAPALM_DAMAGE_TIME/5.0f); + else + obj->effect_info->damage_time+=(MAX_NAPALM_DAMAGE_TIME/1.5f); + + obj->effect_info->damage_time=min(MAX_NAPALM_DAMAGE_TIME,obj->effect_info->damage_time); + + if (obj->type==OBJ_PLAYER) + obj->effect_info->damage_per_second=Weapons[weapon_id].player_damage; + else + obj->effect_info->damage_per_second=Weapons[weapon_id].generic_damage; + + + if (Gametime-obj->effect_info->last_damage_time>1.0f) + obj->effect_info->last_damage_time=0; + + + if (killer!=NULL) + obj->effect_info->damage_handle=killer->handle; + else + obj->effect_info->damage_handle=OBJECT_HANDLE_NONE; + + if (obj->effect_info->sound_handle == SOUND_NONE_INDEX) + obj->effect_info->sound_handle = Sound_system.Play3dSound(SOUND_PLAYER_BURNING, SND_PRIORITY_HIGHEST, obj); + } +} + +// Causes an object to have napalm attached to it +void ApplyFreezeDamageEffect (object *obj) +{ + if (obj->effect_info) + { + if (!(obj->effect_info->type_flags & EF_FREEZE)) + obj->effect_info->freeze_scalar=1.0; + + obj->effect_info->type_flags|=EF_FREEZE; + obj->effect_info->freeze_scalar-=.2f; + + if (obj->effect_info->freeze_scalar<.3) + obj->effect_info->freeze_scalar=.3f; + } +} + +// Event to handle damage effect +void DoDamageEffect (int eventnum,void *data) +{ + float damage_norm=Players[Player_num].damage_magnitude/MAX_DAMAGE_MAG; + + if (damage_norm>1.0) + damage_norm=1.0; + + DrawAlphaBlendedScreen (1.0,0,0,.4*damage_norm); + + // If we're all done, just stop. Else, make another event! + if (Players[Player_num].damage_magnitude<.0001f) + Players[Player_num].damage_magnitude=0.0; + else + CreateNewEvent (RENDER_EVENT,DAMAGE_EFFECT,0,NULL,0,DoDamageEffect); +} + +// Decreases a players shields +void DecreasePlayerShields (int slot,float damage) +{ + object *playerobj=&Objects[Players[slot].objnum]; + playerobj->shields -= damage; + Players[slot].damage_magnitude+=damage; + + if (slot==Player_num) + { + if ((FindEventID(DAMAGE_EFFECT))==-1) + CreateNewEvent (RENDER_EVENT,DAMAGE_EFFECT,0,NULL,0,DoDamageEffect); + + if (Game_mode & GM_MULTI) + Multi_bail_ui_menu=true; + + Game_music_info.player_damaged = true; + + // update IntelliVIBE + if(damage > 0) + { + VIBE_DoPlayerDamage(damage); + } + } +} + +// Event to handle damage effect +void DoEDrainEffect (int eventnum,void *data) +{ + float edrain_norm=Players[Player_num].edrain_magnitude/MAX_EDRAIN_MAG; + + if (edrain_norm>1.0) + edrain_norm=1.0; + + DrawAlphaBlendedScreen (0,0,1.0,.6*edrain_norm); + + // If we're all done, just stop. Else, make another event! + if (Players[Player_num].edrain_magnitude<.0001f) + Players[Player_num].edrain_magnitude=0.0; + else + CreateNewEvent (RENDER_EVENT,EDRAIN_EFFECT,0,NULL,0,DoEDrainEffect); +} + +// Decreases a players shields +void DecreasePlayerEnergy (int slot,float energy) +{ + Players[slot].energy -= energy; + Players[slot].edrain_magnitude+=energy; + + if (slot==Player_num) + { + if ((FindEventID(EDRAIN_EFFECT))==-1) + CreateNewEvent (RENDER_EVENT,EDRAIN_EFFECT,0,NULL,0,DoEDrainEffect); + } +} + +//Plays the sound for a player being damaged +void PlayPlayerDamageSound(object *playerobj,int damage_type) +{ + int sound_handle; + + switch (damage_type) { + case PD_ENERGY_WEAPON: sound_handle = SOUND_HIT_BY_ENERGY_WEAPON; break; + case PD_MATTER_WEAPON: sound_handle = SOUND_HIT_BY_MATTER_WEAPON; break; + case PD_CONCUSSIVE_FORCE: sound_handle = SOUND_HIT_BY_CONCUSSIVE_FORCE; break; + case PD_WALL_HIT: sound_handle = SOUND_PLAYER_HIT_WALL; break; + case PD_VOLATILE_HISS: sound_handle = SOUND_VOLATILE_HISS; break; + + default: + Int3(); + case PD_MELEE_ATTACK: //melee sounds handled in aimain.cpp for now + case PD_NONE: + return; + } + + Sound_system.Play3dSound(sound_handle, SND_PRIORITY_HIGHEST, playerobj); +} + +//Plays the sound for a player being damaged +void PlayPlayerInvulnerabilitySound(object *playerobj) +{ + Sound_system.Play3dSound(SOUND_METALLIC_DOOR_HIT, SND_PRIORITY_HIGHEST, playerobj); +} + +//Kills the player +//weapon_id can be -1 for no weapon +void KillPlayer(object *playerobj,object *killer,float damage_amount,int weapon_id) +{ + // tell IntelliVIBE that the player is dying + if(playerobj->id==Player_num) + { + VIBE_PlayerDeath(); + } + + //Save the killer + Players[playerobj->id].killer_objnum = killer ? OBJNUM(killer) : 0; + + //Set some stuff + Players[playerobj->id].damage_magnitude=0; + Players[playerobj->id].edrain_magnitude=0; + + tOSIRISEventInfo ei; + ei.evt_player_dies.it_handle = playerobj->handle; + Osiris_CallLevelEvent(EVT_PLAYER_DIES,&ei); + + //If single-player, record & initiate death + if (!(Game_mode & GM_MULTI)) { + + if(Demo_flags == DF_RECORDING) + DemoWritePlayerDeath(playerobj, (killer && IS_ROBOT(killer))); + + if(Demo_flags != DF_PLAYBACK) + InitiatePlayerDeath(playerobj, (killer && IS_ROBOT(killer))); + } + else if (Netgame.local_role == LR_SERVER) { //Multiplayer & server + + // Call DLL stuff + DLLInfo.me_handle=playerobj->handle; + + if (killer!=NULL) + DLLInfo.it_handle=killer->handle; + else + DLLInfo.it_handle=OBJECT_HANDLE_NONE; + + DLLInfo.iParam=weapon_id; + + CallGameDLL (EVT_GAMEPLAYERKILLED,&DLLInfo); + + // pick a fate for this guy + int fate=PlayerChooseDeathFate (playerobj->id,damage_amount,false); + + // Send it to everyone + MultiSendPlayerDead (playerobj->id,fate); + + // Figure out new rankings + GetNewRankings (playerobj,killer); + } +} + +// Applies damage to a player object, returns false if damage wasn't applied due to things like +// invunerability +bool ApplyDamageToPlayer(object *playerobj, object *killer, int damage_type, float damage_amount,int server_says,int weapon_id,bool playsound) +{ + if( !playerobj || playerobj->type!=OBJ_PLAYER ) + { + //bail it's not a player (no damage should be applied to ghosts or observer's either + return false; + } + +// apply damage to the current player! + object *weapon_obj=NULL; + + if (killer!=NULL && killer->type==OBJ_WEAPON) + { + weapon_obj=killer; + killer = ObjGetUltimateParent (killer); + + if (!(Game_mode & GM_MULTI) || ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER)) + weapon_id=weapon_obj->id; + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_CLIENT && !server_says && (Netgame.flags & NF_PEER_PEER)) + weapon_id=weapon_obj->id; + } + + if ((Players[playerobj->id].flags & PLAYER_FLAGS_DYING) || (Players[playerobj->id].flags & PLAYER_FLAGS_DEAD)) + { + PlayPlayerDamageSound(playerobj,damage_type); + return false; + } + + if ( (Players[playerobj->id].flags & PLAYER_FLAGS_INVULNERABLE) || ((Player_object->flags & OF_DESTROYABLE)==0) ) + { + if (damage_type != PD_NONE) + PlayPlayerInvulnerabilitySound(playerobj); + + if (weapon_obj!=NULL) + { + Players[playerobj->id].invul_magnitude=1; + Players[playerobj->id].invul_vector=weapon_obj->pos-playerobj->pos; + vm_NormalizeVectorFast (&Players[playerobj->id].invul_vector); + } + return false; + } + else + { + if (!(Game_mode & GM_MULTI)) + { + damage_amount*=Players[playerobj->id].armor_scalar; + damage_amount*=Ships[Players[playerobj->id].ship_index].armor_scalar; + + DecreasePlayerShields (playerobj->id,damage_amount); + + if(damage_amount>0 && playsound) + PlayPlayerDamageSound(playerobj,damage_type); + + if (Players[playerobj->id].guided_obj!=NULL) + ReleaseGuidedMissile (playerobj->id); + + if (Players[playerobj->id].user_timeout_obj!=NULL) + ReleaseUserTimeoutMissile (playerobj->id); + + if (weapon_id!=255) + { + if (Weapons[weapon_id].flags & WF_MICROWAVE) + SetDeformDamageEffect (playerobj); + if (Weapons[weapon_id].flags & WF_FREEZE) + ApplyFreezeDamageEffect (playerobj); + if (Weapons[weapon_id].flags & WF_NAPALM) + SetNapalmDamageEffect (playerobj,killer,weapon_id); + } + + if (playerobj->shields < 0) + { + KillPlayer(playerobj,killer,damage_amount,weapon_id); + } + } + else + { + // If this is a peer to peer game, and its me taking damage, then ask the server to damage me! + if (Netgame.local_role==LR_CLIENT && (Netgame.flags & NF_PEER_PEER) && !server_says) + { + if (playerobj->id==Player_num) + { + MultiSendRequestPeerDamage (killer,weapon_id,damage_type,damage_amount); + return true; + } + } + + if ((Netgame.flags & NF_PEER_PEER) && Netgame.local_role==LR_SERVER && !server_says) + { + if (playerobj->id!=Player_num) + return true; + } + + if (Netgame.local_role==LR_SERVER || server_says) + { + if (Netgame.local_role==LR_SERVER) + { + // Check to see if we're on the same team + if (killer!=NULL && killer->type==OBJ_PLAYER) + { + if (killer->id!=playerobj->id) + { + if (Players[killer->id].team==Players[playerobj->id].team && !(Netgame.flags & NF_DAMAGE_FRIENDLY)) + return false; // don't hurt players on your own team + } + + damage_amount*=Players[killer->id].damage_scalar; + } + + damage_amount*=Players[playerobj->id].armor_scalar; + damage_amount*=Ships[Players[playerobj->id].ship_index].armor_scalar; + } + + DecreasePlayerShields (playerobj->id,damage_amount); + if(damage_amount>0 && playsound) + PlayPlayerDamageSound(playerobj,damage_type); + if (playerobj->id==Player_num && Players[playerobj->id].guided_obj!=NULL) + ReleaseGuidedMissile (playerobj->id); + + if (playerobj->id==Player_num && Players[playerobj->id].user_timeout_obj!=NULL) + ReleaseUserTimeoutMissile (playerobj->id); + + if (weapon_id!=255) + { + if (Weapons[weapon_id].flags & WF_MICROWAVE) + SetDeformDamageEffect (playerobj); + if (Weapons[weapon_id].flags & WF_NAPALM) + SetNapalmDamageEffect (playerobj,killer,weapon_id); + if (Weapons[weapon_id].flags & WF_FREEZE) + ApplyFreezeDamageEffect (playerobj); + } + + // Tell others about this tragic event + if (Netgame.local_role==LR_SERVER) + { + if (weapon_id!=255) + { + // If this is an electrical weapon then we should special case the damage + if (Weapons[weapon_id].flags & WF_ELECTRICAL) + { + Multi_additional_damage[playerobj->id]+=damage_amount; + Multi_additional_damage_type[playerobj->id]=PD_ENERGY_WEAPON; + } + else + MultiSendDamagePlayer (playerobj->id,weapon_id,damage_type,damage_amount); + } + else + { + if (weapon_obj!=NULL) + MultiSendDamagePlayer (playerobj->id,255,damage_type,damage_amount); + else + { + Multi_additional_damage[playerobj->id]+=damage_amount; + Multi_additional_damage_type[playerobj->id]=damage_type; + } + } + } + } + + if (Netgame.local_role==LR_SERVER) + { + // Call DLL stuff + DLLInfo.me_handle=playerobj->handle; + if (killer!=NULL) + DLLInfo.it_handle=killer->handle; + else + DLLInfo.it_handle=OBJECT_HANDLE_NONE; + DLLInfo.fParam=damage_amount; + CallGameDLL (EVT_GAMEOBJECTSHIELDSCHANGED,&DLLInfo); + + if (playerobj->shields < 0) + { + KillPlayer(playerobj,killer,damage_amount,weapon_id); + } + } + } + } + + return true; +} + +//Fills in death info to roughly appoximate the deaths from the old explosion system +void GenerateDefaultDeath(object *obj,int *death_flags,float *delay_time) +{ + //Set some defaults + *death_flags = 0; + *delay_time = 0.0; + + if ((obj->type != OBJ_ROBOT && !(obj->type == OBJ_BUILDING && obj->ai_info)) || + (obj->movement_type != MT_WALKING && ((ps_rand()%3)!=0)) || + (obj->movement_type == MT_WALKING && ((ps_rand()%10) > 5))) { + + //Quick death + *death_flags = 0; + *delay_time = 0.0; + + //Maybe set blast ring + if ((ps_rand()%3)==0) + *death_flags |= DF_BLAST_RING; + } + else { //slow death + + int alternate_death=0; + bool f_upsidedown_walker = (obj->movement_type == MT_WALKING) && (obj->orient.uvec.y < 0.0f); + + if(!f_upsidedown_walker && (ps_rand()%2)==1) { + *death_flags = DF_DELAY_SPARKS; + alternate_death = 1; + } + + if (obj->type == OBJ_ROBOT || (obj->type == OBJ_BUILDING && obj->ai_info)) { + if (alternate_death || (obj->movement_type != MT_PHYSICS && obj->movement_type != MT_WALKING) + || (obj->movement_type == MT_WALKING && !f_upsidedown_walker)) { + *death_flags = DF_DELAY_SPARKS; + *delay_time = 3.0f; + alternate_death = 1; + } + else { + if(obj->movement_type != MT_WALKING) + *death_flags = 0; + else + *death_flags = DF_DELAY_LOSES_ANTIGRAV; + } + + // Alternate death must last 3 seconds to correspond with sound effect + // If you change this assumption, tell Jerry + if (!alternate_death && Object_info[obj->id].anim) { + if(obj->control_type == CT_AI && Object_info[obj->id].anim[obj->ai_info->movement_type].elem[AS_DEATH].to != 0.0f) { + //compute time remaining in current animation + float extra_time = obj->rtype.pobj_info.anim_time * (obj->rtype.pobj_info.anim_end_frame - obj->rtype.pobj_info.anim_frame) / + (obj->rtype.pobj_info.anim_end_frame - obj->rtype.pobj_info.anim_start_frame); + extra_time = min(extra_time,3.0); //limit extra time to 3 seconds + *delay_time = Object_info[obj->id].anim[obj->ai_info->movement_type].elem[AS_DEATH].spc + 0.25 + extra_time; + // Walkers last a little longer + if(obj->movement_type == MT_WALKING) + *delay_time += 2.0f; + } + else + *delay_time = 2.0f; + } + } + else { // If building, make it shoot up + *death_flags |= DF_DELAY_FLYING; + if(obj->orient.uvec.y == 1.0f) + *delay_time = 2.0f; + else + *delay_time = 0.7f; + } + + //Some deaths get blast ring + if ((ps_rand()%8)==0) + *death_flags |= DF_BLAST_RING; + } + + //Set flags for all deaths + *death_flags |= DF_FIREBALL+DF_BREAKS_APART+ + DF_CONTACT_FIREBALL+DF_CONTACT_BREAKS_APART+DF_DEBRIS_SMOKES+DF_DEBRIS_FIREBALL; +} + +//Applies a default death to an object +//Parameters: objp - the object to destroy +// killer - the object who is killing it, or NULL +// damage - how much damage was applied in the death blow? +void KillObject(object *objp,object *killer,float damage) +{ + int death_flags=-1; + float delay_time; + + //Make sure a valid type + ASSERT(IS_GENERIC(objp->type) || (objp->type == OBJ_DOOR)); + + //Bail if already dying or exploding + if ((objp->flags & OF_DYING) || (objp->flags & OF_DEAD)) + return; + + //Get death info from the object page + if (IS_GENERIC(objp->type)) { + + //Get random probability + int r = (ps_rand() * 100 / (RAND_MAX+1)) + 1; //in range 1 to 100 + + //Loop through death types looking for chosen one + for (int i=0;iid].death_probabilities[i]; + if (r <= p) { + float delay_min,delay_max; + + death_flags = Object_info[objp->id].death_types[i].flags; + + delay_min = Object_info[objp->id].death_types[i].delay_min; + delay_max = Object_info[objp->id].death_types[i].delay_max; + + delay_time = delay_min + (delay_max - delay_min) * ps_rand() / RAND_MAX; + + mprintf((0,"Using %d\n",i)); + break; + } + r -= p; + } + } + + //If no death from page, generate default + if (death_flags == -1) + GenerateDefaultDeath(objp,&death_flags,&delay_time); + + //Now kill the object + KillObject(objp,killer,damage,death_flags,delay_time); +} + +//Sets the physics parameters for a falling object +void SetFallingPhysics(object *objp) +{ + //Set gravity & other parms + objp->mtype.phys_info.flags = PF_GRAVITY | PF_BOUNCE; + objp->mtype.phys_info.num_bounces=0; + objp->mtype.phys_info.coeff_restitution=.03f; + if(objp->mtype.phys_info.mass < 20.0) + objp->mtype.phys_info.mass = 20.0f; + objp->mtype.phys_info.drag=.00001f; + objp->mtype.phys_info.rotdrag=.00001f; + + //Special stuff for walkers + if (objp->movement_type == MT_WALKING) { + objp->mtype.phys_info.rotvel = Zero_vector; + objp->mtype.phys_info.flags |= PF_POINT_COLLIDE_WALLS; + float proj = objp->mtype.phys_info.velocity * objp->orient.uvec; + if(proj < 0.0f) + objp->mtype.phys_info.velocity -= proj * objp->orient.uvec; + + objp->mtype.phys_info.velocity += objp->orient.uvec * (3.0f + ((float)ps_rand()/RAND_MAX)*5.0); + objp->movement_type = MT_PHYSICS; + } + else { //not a walker + + //If not spinning much, give the object a good spin + if(vm_GetMagnitude(&objp->mtype.phys_info.rotvel) < 4000.0f) + { + objp->mtype.phys_info.rotvel.x = (float)((60000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + objp->mtype.phys_info.rotvel.y = (float)((60000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + objp->mtype.phys_info.rotvel.z = (float)((60000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + } + } +} + +//Sets the physics parameters for an object the flies in the air when dies +void SetFlyingPhysics(object *objp,bool tumbles) +{ + //Set gravity & other parms + objp->mtype.phys_info.flags = PF_GRAVITY|PF_NO_COLLIDE; + objp->mtype.phys_info.coeff_restitution=.03f; + objp->mtype.phys_info.mass=50000.0f; + objp->mtype.phys_info.drag=.01f; + objp->mtype.phys_info.rotdrag=.01f; + + objp->movement_type=MT_PHYSICS; + + if (tumbles) { + // Make y spin a little bit more that x or z + objp->mtype.phys_info.rotvel.x = (float)((40000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + objp->mtype.phys_info.rotvel.y = (float)((60000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + objp->mtype.phys_info.rotvel.z = (float)((40000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + } + + if(objp->orient.uvec.y == 1.0f) + { + objp->mtype.phys_info.velocity.y = (float)(3*(ps_rand()%15))+20.0; + objp->mtype.phys_info.velocity.x=0; + objp->mtype.phys_info.velocity.z=0; + + //Doubled the velocity on 4/30/99 to make Josh's pop machines cooler. -MT + objp->mtype.phys_info.velocity.y *= 2.0; + } + else + { + objp->mtype.phys_info.velocity = objp->orient.uvec * ((float)(2.0f*(ps_rand()%15))+7.0f); + } +} + +//Figure out how long it will take the dying anim to play +float GetDeathAnimTime(object *objp) +{ + float death_time = 0.0; + + if (objp->control_type == CT_AI) { + + ASSERT(IS_GENERIC(objp->type)); + + if (Object_info[objp->id].anim[objp->ai_info->movement_type].elem[AS_DEATH].to != 0) { + + //calculate how long for the object to finish its current animation before starting death anim + float extra_time = objp->rtype.pobj_info.anim_time * (objp->rtype.pobj_info.anim_end_frame - objp->rtype.pobj_info.anim_frame) / + (objp->rtype.pobj_info.anim_end_frame - objp->rtype.pobj_info.anim_start_frame); + extra_time = min(extra_time,3.0); //limit extra time to 3 seconds + mprintf((0,"extra_time = %2f\n",extra_time)); + + death_time = Object_info[objp->id].anim[objp->ai_info->movement_type].elem[AS_DEATH].spc + 0.25 + extra_time; + } + } + + return death_time; +} + +//Kill an object +//The caller can specify death information; if none is specified, a default death will be used. +//Parameters: objp - the object to destroy +// killer - the object who is killing it, or NULL +// damage - how much damage was applied in the death blow +// death_flags - how the object dies +// delay_time - how long to delay, if from anim flag not set +void KillObject(object *objp,object *killer,float damage,int death_flags,float delay_time) +{ + //Make sure a valid type + ASSERT(IS_GENERIC(objp->type) || (objp->type == OBJ_DOOR)); + + //Bail if already dying or exploding + if ((objp->flags & OF_DYING) || (objp->flags & OF_DEAD)) + return; + + //Send out multiplayer info + if ((Game_mode & GM_MULTI) && (Netgame.local_role==LR_SERVER)) { + int seed = ps_rand(); + MultiSendKillObject(objp,killer,damage,death_flags,delay_time,seed); + ps_srand(seed); + } + + //Record demo + if (Demo_flags == DF_RECORDING) { + int seed = ps_rand(); + DemoWriteKillObject(objp,killer,damage,death_flags,delay_time,seed); + ps_srand(seed); + } + + //Say this this object is dying + objp->flags |= OF_DYING; + + //If object is a door, let the doorway system know + if (objp->type == OBJ_DOOR) + DoorwayDestroy(objp); + + //Set the delay if from anim + if (death_flags & DF_DELAY_FROM_ANIM) + delay_time = GetDeathAnimTime(objp); + + //Check for delay + if (delay_time == 0.0) { //No delay. Dies now + + //If blast rings set, create 0-2 delayed blast rings + if (death_flags & DF_BLAST_RING) { + float ring_size = OBJECT_OUTSIDE(objp) ? (objp->size*3) : objp->size; + int extras = ps_rand() % 3; + for (int i=0;itype==OBJ_PLAYER) && IS_GENERIC(objp->type)) + PlayerScoreAdd(killer->id,Object_info[objp->id].score); + + //Destroy the object now + DestroyObject(objp,damage * 3.0,death_flags); + } + else { //Some sort of delayed death + + //For upside-down walkers, force simple delay + bool f_upsidedown_walker = (objp->movement_type == MT_WALKING) && (objp->orient.uvec.y < 0.0f); + if (f_upsidedown_walker) + death_flags |= DF_DELAY_LOSES_ANTIGRAV; + + //Set dying control + int old_control_type = objp->control_type; + SetObjectControlType(objp,CT_DYING); + + //Set dying flags + objp->ctype.dying_info.death_flags = death_flags; + objp->ctype.dying_info.last_smoke_time=-1; + objp->ctype.dying_info.last_spark_time=-1; + objp->ctype.dying_info.last_fireball_time=-1; + + //If the killer is a player, save so can add score when the object dies + if (killer && (killer->type==OBJ_PLAYER) && IS_GENERIC(objp->type)) + objp->ctype.dying_info.killer_playernum = killer->id; + else + objp->ctype.dying_info.killer_playernum = -1; + + //Set the timer + objp->ctype.dying_info.delay_time = delay_time; + + //If loses anti-grav, set physics so object falls + if (death_flags & DF_DELAY_LOSES_ANTIGRAV) + SetFallingPhysics(objp); + + //Deal with some goal stuff. Ask Chris about this + if ((objp->type == OBJ_ROBOT) || ((objp->type == OBJ_BUILDING) && objp->ai_info)) { + if ((old_control_type == CT_AI) && (Object_info[objp->id].anim[objp->ai_info->movement_type].elem[AS_DEATH].to != 0.0f)) { + SetObjectControlType(objp,CT_DYING_AND_AI); + int next_anim = AS_DEATH; + GoalAddGoal(objp, AIG_SET_ANIM, (void *)&next_anim , ACTIVATION_BLEND_LEVEL); + mprintf((0,"Start dying anim ")); + } + } + + //Deal with objects that fly + if (death_flags & DF_DELAY_FLYING) { + SetFlyingPhysics(objp,!(death_flags & DF_DELAY_NO_TUMBLE_FLY)); + + //Create a fireball + if (death_flags & DF_FIREBALL) + CreateObjectFireball(objp); + } + + //Deal with objects that fade out + if (death_flags & DF_DELAY_FADE_AWAY) { + ASSERT(objp->effect_info != NULL); + objp->effect_info->type_flags |= EF_FADING_OUT; + objp->effect_info->fade_time = objp->effect_info->fade_max_time = objp->ctype.dying_info.delay_time; + } + + //Play the sound now if flagged to do so + if (death_flags & DF_DELAY_SOUND) + PlayObjectExplosionSound(objp); + //else if (death_flags & DF_DELAY_SPARKS) //If no special sound & doing sparks, play sparks sound + // Sound_system.Play3dSound(SOUND_ELECTRICAL_DEATH, SND_PRIORITY_NORMAL, objp); + } +} + +// Applies damage to robot objects. Handled differently than damage to players for obvious reasons. +bool ApplyDamageToGeneric(object *hit_obj, object *killer, int damage_type, float damage,int server_says,int weapon_id) +{ + ASSERT(hit_obj != NULL); + + if(hit_obj->type==OBJ_DUMMY) + { + //as per ChrisP, dummies should be ignored here + return false; + } + + if(hit_obj->flags & OF_AI_DEATH) + { + return false; + } + + //I don't think there's any problem with calling this function on an object that's not a generic + //or a door, but I don't see why you would. + ASSERT(IS_GENERIC(hit_obj->type) || (hit_obj->type == OBJ_DOOR)); + + object *weapon_obj=killer; + + if (damage_type != GD_SCRIPTED && !(IS_GENERIC(hit_obj->type) && (Object_info[hit_obj->id].flags & OIF_NO_DIFF_SCALE_DAMAGE))) + damage *= Diff_robot_damage[DIFF_LEVEL]; + + if (killer!=NULL && killer->type==OBJ_WEAPON) + { + killer = ObjGetUltimateParent (killer); + + if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI && Netgame.local_role==LR_SERVER)) + weapon_id=weapon_obj->id; + } + + if(hit_obj->flags & OF_DESTROYABLE && (!(hit_obj->flags & OF_USE_DESTROYED_POLYMODEL))) + { + if ((Game_mode & GM_MULTI) && (hit_obj->flags & OF_DESTROYED)) + { + return false; // This object has already been sent to the server to be blown up! + } + + if (hit_obj->type==OBJ_POWERUP && (Game_mode & GM_MULTI)) + return false; + + if ((!(Game_mode & GM_MULTI))||server_says||((Netgame.local_role==LR_SERVER)&&(Game_mode & GM_MULTI))) + { + tOSIRISEventInfo ei; + if (killer!=NULL) + ei.evt_damaged.it_handle = killer->handle; + else + ei.evt_damaged.it_handle = OBJECT_HANDLE_NONE; + + if(weapon_obj) + ei.evt_damaged.weapon_handle = weapon_obj->handle; + else + ei.evt_damaged.weapon_handle = OBJECT_HANDLE_NONE; + + ei.evt_damaged.damage_type = damage_type; + ei.evt_damaged.damage=damage; + Osiris_CallEvent(hit_obj,EVT_DAMAGED,&ei); + + // This statement allows a robot to have special resistances or vulerabilites to damage types + damage = ei.evt_damaged.damage; + if(damage == 0.0f) + { + return false; + } + + hit_obj->shields -= damage; + + if (Game_mode & GM_MULTI) + {//only the server will be calling this + if (killer!=NULL) + DLLInfo.it_handle=killer->handle; + else + DLLInfo.it_handle=OBJECT_HANDLE_NONE; + + DLLInfo.me_handle=hit_obj->handle; + DLLInfo.fParam = damage; + CallGameDLL (EVT_GAMEOBJECTSHIELDSCHANGED,&DLLInfo); + } + } + if((Game_mode & GM_MULTI)&&(Netgame.local_role==LR_SERVER)) + { + //Let everyone know about the damage + MultiSendDamageObject (hit_obj,killer,damage,weapon_id); + } + + if((hit_obj->shields < 0) && (hit_obj->flags & OF_AI_DO_DEATH)) + { + hit_obj->shields = 0; + hit_obj->flags |= OF_AI_DEATH; + } + + if ((hit_obj->shields < 0) && !(hit_obj->flags & OF_DYING)) + { + // CURRRENTLY - atttached objects remain attached when dying (it looks better) -- chrishack +// if (!((Game_mode & GM_MULTI) && (Netgame.local_role == LR_CLIENT))) +// { +// // Attached objects have a 50% chance of falling off their parent when they die +// if(ps_rand() >= (RAND_MAX >> 1)) +// { +// UnattachFromParent(hit_obj); +// } +// } + + if (Game_mode & GM_MULTI) + { + + DLLInfo.me_handle=hit_obj->handle; + DLLInfo.it_handle=(killer)?killer->handle:OBJECT_HANDLE_NONE; + CallGameDLL (EVT_GAMEOBJKILLED,&DLLInfo); + + if (Netgame.local_role==LR_SERVER) + KillObject(hit_obj,killer,damage); + } + else + { + // music hook + if (killer && killer->type==OBJ_PLAYER && hit_obj->ai_info) { + if(hit_obj->ai_info->awareness > AWARE_BARELY && hit_obj->ai_info->target_handle == killer->handle) + Game_music_info.n_hostiles_player_killed++; + } + + if (Demo_flags != DF_PLAYBACK) + KillObject(hit_obj,killer,damage); + } + + } + + if ((!(Game_mode & GM_MULTI))||server_says||((Netgame.local_role==LR_SERVER)&&(Game_mode & GM_MULTI))) + { + if (weapon_id!=255) + { + if (Weapons[weapon_id].flags & WF_MICROWAVE) + SetDeformDamageEffect (hit_obj); + if (Weapons[weapon_id].flags & WF_NAPALM) + SetNapalmDamageEffect (hit_obj,killer,weapon_id); + if (Weapons[weapon_id].flags & WF_FREEZE) + ApplyFreezeDamageEffect (hit_obj); + } + return true; + } + return false; + } + else return false; +} + + + +// Adds a bit of shake to the camera +void AddToShakeMagnitude (float delta) +{ + + ASSERT (delta>=0.0); + Shake_magnitude+=delta; +} + +// Sets a definite shake to the camera +void SetShakeMagnitude (float delta) +{ + + ASSERT (delta>=0.0); + Shake_magnitude=delta; +} + + + +#define MAX_SHAKE_MAGNITUDE 120 + +// Shakes player by some random amount +void ShakePlayer () +{ + if (Shake_magnitude>MAX_SHAKE_MAGNITUDE) + Shake_magnitude=MAX_SHAKE_MAGNITUDE; + + float mag=Shake_magnitude; + + ushort pitch_adjust=((ps_rand()%5)-2)*mag; + ushort bank_adjust=((ps_rand()%5)-2)*mag; + ushort heading_adjust=((ps_rand()%5)-2)*mag; + matrix m,tempm; + + Old_player_orient=Player_object->orient; + + if (Shake_magnitude<.00001) + return; + + vm_AnglesToMatrix (&m,pitch_adjust,heading_adjust,bank_adjust); + + DoForceForShake(Shake_magnitude); + Shake_magnitude-=(Frametime*(MAX_SHAKE_MAGNITUDE/3)); + + if (Shake_magnitude<0) + Shake_magnitude=0.0; + + tempm=m * Player_object->orient; + + ObjSetOrient(Player_object, &tempm); +} + +// Restores the player orientation matrix after shaking +void UnshakePlayer () +{ + ObjSetOrient(Player_object, &Old_player_orient); +} + +#include "terrain.h" + +void ComputeCenterPointOnFace(vector *vp,room *rp,int facenum); +void FindHitpointUV(float *u,float *v,vector *point,room *rp,int facenum); + +#define SHARD_MAX_EDGE_LEN 3.0f + +//Break the specified (glass) face into shards +//Parameters: rp, facenum - the face to break. The face must be a portal face. +// hitpnt - the point on the face where the face shatters. If NULL, uses center point of face +// hitvec - the direction in which the thing that's breaking the glass is moving. If NULL, +// uses the negative of the surface normal +void BreakGlassFace(room *rp,int facenum,vector *hitpnt,vector *hitvec) +{ + int roomnum; + vector t_hitpnt,t_hitvec; + face *fp = &rp->faces[facenum]; + + ASSERT(fp->portal_num != -1); + portal *pp0 = &rp->portals[fp->portal_num]; + portal *pp1 = &Rooms[pp0->croom].portals[pp0->cportal]; + + //If face already broken, bail + if (! (pp0->flags & PF_RENDER_FACES)) + return; + + //Set hitpnt if not specified + if (hitpnt == NULL) { + ComputeCenterPointOnFace(&t_hitpnt,rp,facenum); + hitpnt = &t_hitpnt; + } + + //Set hitvec if not specified + if (hitvec == NULL) { + t_hitvec = -fp->normal; + hitvec = &t_hitvec; + } + + //Get roomnum + if (rp->flags & RF_EXTERNAL) + roomnum = MAKE_ROOMNUM(GetTerrainCellFromPos(hitpnt)); + else + roomnum = ROOMNUM(rp); + + //Play sound + pos_state pos; + pos.position = hitpnt; + pos.orient = (matrix *) &Identity_matrix; + pos.roomnum = roomnum; + + if(sound_override_glass_breaking == -1) + Sound_system.Play3dSound(SOUND_BREAKING_GLASS,SND_PRIORITY_HIGH, &pos); + else + Sound_system.Play3dSound(sound_override_glass_breaking,SND_PRIORITY_HIGH, &pos); + + //Clear render flags + pp0->flags &= ~PF_RENDER_FACES; + pp1->flags &= ~PF_RENDER_FACES; + + //Get the UV for the hit point + float hitpnt_u,hitpnt_v; + FindHitpointUV(&hitpnt_u,&hitpnt_v,hitpnt,rp,facenum); + + //Calculate shard movement vector + vector shardvec = *hitvec; + vm_NormalizeVectorFast(&shardvec); + + //Create shards + vector prevpoint = rp->verts[fp->face_verts[fp->num_verts-1]]; + float prev_u = fp->face_uvls[fp->num_verts-1].u,prev_v = fp->face_uvls[fp->num_verts-1].v; + for (int vertnum=0;vertnumnum_verts;) { + int objnum; + vector curpoint = rp->verts[fp->face_verts[vertnum]],center; + float cur_u = fp->face_uvls[vertnum].u,cur_v = fp->face_uvls[vertnum].v; + + //Check if should split edge + if (vm_VectorDistanceQuick(&prevpoint,&curpoint) > SHARD_MAX_EDGE_LEN) { + curpoint = (curpoint + prevpoint) / 2; + cur_u = (cur_u + prev_u) / 2; + cur_v = (cur_v + prev_v) / 2; + } + else //no split + vertnum++; + + //Calculate the center point + center = (curpoint + prevpoint + *hitpnt) / 3.0; + + //Create the object + objnum = ObjCreate(OBJ_SHARD,0,roomnum,¢er,NULL); + + //Setup shard info + if (objnum != -1) { + object *objp = &Objects[objnum]; + shard_info_s *si = &objp->rtype.shard_info; + + si->points[0] = prevpoint - objp->pos; + si->points[1] = curpoint - objp->pos; + si->points[2] = *hitpnt - objp->pos; + + si->u[0] = prev_u; + si->v[0] = prev_v; + si->u[1] = cur_u; + si->v[1] = cur_v; + si->u[2] = hitpnt_u; + si->v[2] = hitpnt_v; + + si->normal = fp->normal; + si->tmap = fp->tmap; + + //Set velocity + objp->mtype.phys_info.velocity = shardvec * (50.0 + 100.0 * (objnum % 3)); + + //Set rotational velocity + objp->mtype.phys_info.rotvel.x = 25000.0 * (objnum % 5); + objp->mtype.phys_info.rotvel.y = 25000.0 * (objnum % 2); + objp->mtype.phys_info.rotvel.z = 25000.0 * (objnum % 3); + + prevpoint = curpoint; + prev_u = cur_u; + prev_v = cur_v; + } + } + + // Do multiplayer stuff + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER) + { + MultiSendBreakGlass (rp,facenum); + + if (Num_broke_glass!=MAX_BROKE_GLASS) + { + Broke_glass_rooms[Num_broke_glass]=rp-Rooms; + Broke_glass_faces[Num_broke_glass]=facenum; + Num_broke_glass++; + } + } +} diff --git a/Descent3/damage.h b/Descent3/damage.h new file mode 100644 index 000000000..c3ddc570b --- /dev/null +++ b/Descent3/damage.h @@ -0,0 +1,141 @@ +/* + * $Logfile: /DescentIII/Main/damage.h $ + * $Revision: 20 $ + * $Date: 4/25/99 10:38p $ + * $Author: Matt $ + * + * Applies and handles damage to game entities. + * + * $Log: /DescentIII/Main/damage.h $ + * + * 20 4/25/99 10:38p Matt + * Made the Osiris kill object function work on players. + * + * 19 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 18 4/24/99 4:32p Jason + * made fusion damage sound not be irritating + * + * 17 4/02/99 11:23a Matt + * Made KillObject not take a death_info struct, but rather the death info + * as individual parameters. Moved death_info into objinfo.h, since it's + * only used for generic objects. Took out fade-away death hack, now that + * fade-away deaths can be explicitely set. + * + * 16 3/22/99 2:00p Matt + * Moved breaking glass code from collide.cpp to damage.cpp + * + * 15 2/25/99 11:01a Matt + * Added new explosion system. + * + * 14 1/20/99 2:13a Chris + * It is now possible for robots to have special immunities, resistances, + * and vunerabilities + * + * 13 11/28/98 3:11p Jason + * made damage glow much more apparent + * + * 12 11/19/98 5:40p Kevin + * Demo system + * + * 11 10/19/98 11:20p Matt + * Changed player damage types to start at 0 instead of -1 to avoid -1 -> + * 255 problem in multiplayer. + * + * 10 10/19/98 7:19p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 9 5/04/98 4:12p Chris + * + * 8 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 7 4/17/98 1:59p Jason + * added cool object effects + * + * 6 2/18/98 3:43a Mark + * + * 5 1/20/98 6:01p Jason + * first pass at getting multiplayer deaths working + * + * 4 1/15/98 6:49p Jason + * incremental checkin for multiplayer + * + * 3 12/01/97 11:02a Jason + * made camera shake from explosions + * + * 2 12/01/97 9:54a Chris + * Added support for concussive forces, generalized robot collisions to + * generic collisions + * + * 1 10/01/97 4:50p Samir + * Damage for players and robots. + * + * $NoKeywords: $ + */ + +#ifndef DAMAGE_H +#define DAMAGE_H + +#include "damage_external.h" +#include "vecmat.h" +#include "DeathInfo.h" + +// Maximum damage magnitude +#define MAX_DAMAGE_MAG 20.0f +#define MAX_EDRAIN_MAG 18.0f + +struct object; +struct room; + +// Applies damage to a player object, returns true if damage is applied. +bool ApplyDamageToPlayer(object *playerobj, object *killer, int damage_type,float damage_amount,int server_says=0,int weapon_id=255,bool playsound=1); + +// Applies damage to a robot object, returns true if damage is applied. +bool ApplyDamageToGeneric(object *robotobj, object *killer, int damage_type, float damage, int server_says=0,int weapon_id=255); + +//Starts on object on fire +void SetNapalmDamageEffect (object *obj,object *killer,int weapon_id); + +// Chrishack -- milestone +void DecreasePlayerEnergy (int slot,float energy); + +// Adds a bit of shake to the camera +void AddToShakeMagnitude (float delta); + +//This function sortof replaces ExplodeObject() +//Parameters: objp - the object to destroy +// killer - the object who is killing it, or NULL +// damage - how much damage was applied in the death blow? +// death_flags - how the object dies +// delay_time - how long to delay, if a timed delay +void KillObject(object *objp,object *killer,float damage,int death_flags,float delay_time); + +//Applies a default death to an object +//Figures out what sort of death to do, then calls the other KillObject() +//Parameters: objp - the object to destroy +// killer - the object who is killing it, or NULL +// damage - how much damage was applied in the death blow? +void KillObject(object *objp,object *killer,float damage); + +//Kills the player +//weapon_id can be -1 for no weapon +void KillPlayer(object *playerobj,object *killer,float damage_amount,int weapon_id); + +// Shakes player by some random amount +void ShakePlayer (); + +// Restores the player orientation matrix after shaking +void UnshakePlayer (); + +//Break the specified (glass) face into shards +//Parameters: rp, facenum - the face to break +// hitpnt - the point on the face where the face shatters. If NULL, uses center point of face +// hitvec - the direction in which the thing that's breaking the glass is moving. If NULL, +// uses the negative of the surface normal +void BreakGlassFace(room *rp,int facenum,vector *hitpnt=NULL,vector *hitvec=NULL); + +#endif \ No newline at end of file diff --git a/Descent3/damage_external.h b/Descent3/damage_external.h new file mode 100644 index 000000000..8ed80572d --- /dev/null +++ b/Descent3/damage_external.h @@ -0,0 +1,24 @@ +#ifndef DAMAGE_EXTERNAL_H_ +#define DAMAGE_EXTERNAL_H_ + +//Player Damage types. Used only to make a sound. +#define PD_NONE 0 //Make no sound +#define PD_ENERGY_WEAPON 1 //Hit by laser, etc. +#define PD_MATTER_WEAPON 2 //Hit by missile, etc. +#define PD_MELEE_ATTACK 3 //Whacked by robot +#define PD_CONCUSSIVE_FORCE 4 //Hit by shockwave +#define PD_WALL_HIT 5 //Crashed into a wall +#define PD_VOLATILE_HISS 6 //Touched a volatile substance (such as acid) + +//Generic damage types +#define GD_SCRIPTED 0 // Script is saying to do the damage +#define GD_ELECTRIC 1 // Electrical weapons +#define GD_CONCUSSIVE 2 // Concussive damage +#define GD_FIRE 3 // Fire and napalm like stuff +#define GD_MATTER 4 // Matter weapons +#define GD_ENERGY 5 // Energy weapons and fields +#define GD_PHYSICS 6 // Bumping into a wall or player too hard +#define GD_MELEE_ATTACK 7 // From a melee robot attack +#define GD_VOLATILE_HISS 8 // Touched a volatile substance (such as acid) + +#endif \ No newline at end of file diff --git a/Descent3/deathinfo_external.h b/Descent3/deathinfo_external.h new file mode 100644 index 000000000..da864a1ae --- /dev/null +++ b/Descent3/deathinfo_external.h @@ -0,0 +1,89 @@ +/* + * $Logfile: /DescentIII/main/deathinfo_external.h $ + * $Revision: 10 $ + * $Date: 4/26/99 11:39p $ + * $Author: Matt $ + * + * I don't know why we need this file, but here it is. + * + * $Log: /DescentIII/main/deathinfo_external.h $ + * + * 10 4/26/99 11:39p Matt + * Added a flag to make fly-in-air deaths no tumble + * + * 9 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 8 4/23/99 12:32a Matt + * Added a death info option to play the explosion sound at the start of + * the delay, instead of at the end when the object actually dies. + * + * 7 4/21/99 3:01p Matt + * Added a new type for dying objects that have AI, instead of keeping a + * flag in the dying info. + * + * 6 4/02/99 2:46p Matt + * Moved flags from deathinfo to deathinfo_external, because the arhive + * builder only copies the latter into the archive. + * + * 5 4/02/99 11:23a Matt + * Made KillObject not take a death_info struct, but rather the death info + * as individual parameters. Moved death_info into objinfo.h, since it's + * only used for generic objects. Took out fade-away death hack, now that + * fade-away deaths can be explicitely set. + * + */ + +// +// Object death info flags +// + +//Not a bit flag, but an override value +#define DF_DEFAULT -1 //use the default death for this object + +//Unused flag +#define DF_UNUSED 0x0000001 + +//Delay options +#define DF_DELAY_FROM_ANIM 0x0000002 //delay time from death animation +#define DF_DELAY_SPARKS 0x0000004 //delay with sparks +#define DF_DELAY_LOSES_ANTIGRAV 0x0000008 //object gets gravity during delay +#define DF_DELAY_SMOKES 0x0000010 //delay with smoke +#define DF_DELAY_FLYING 0x0100000 //delay with object flying up into the air +#define DF_DELAY_FIREBALL 0x0200000 //delay with fireballs +#define DF_DELAY_FADE_AWAY 0x0400000 //fade away +#define DF_DELAY_NO_TUMBLE_FLY 0x2000000 //don't tumble while flying up in the air + +//Options for what happens on death +#define DF_FIREBALL 0x0000020 //there are fireballs when the object dies +#define DF_BREAKS_APART 0x0000040 //the object breaks into pieces when it dies +#define DF_BLAST_RING 0x0000080 //a blast ring is created when the object dies +#define DF_REMAINS 0x0000100 //the object does not go away when it does +#define DF_LOSES_ANTIGRAV 0x0000200 //object gets gravity on death +#define DF_FADE_AWAY 0x0800000 //fades away + +//Explosion size if there is a death fireball +#define DF_EXPL_SMALL 0x0000000 //use a small explosion +#define DF_EXPL_MEDIUM 0x0000400 //use a medium explosion +#define DF_EXPL_LARGE 0x0000800 //use a large explosion +#define DF_EXPL_SIZE_MASK 0x0000c00 +#define DF_EXPL_SIZE_SHIFT 10 + +//What happens when this object hits something during delay +#define DF_CONTACT_FIREBALL 0x0001000 //creates fireballs +#define DF_CONTACT_BREAKS_APART 0x0002000 //break apart +#define DF_CONTACT_BLAST_RING 0x0004000 //blast ring +#define DF_CONTACT_REMAINS 0x0008000 //stays around + +//Whether the debris puts off smoke +#define DF_DEBRIS_SMOKES 0x0010000 //the debris that's created smokes + +//What happens to the debris when it times out or hits something +#define DF_DEBRIS_FIREBALL 0x0020000 //creates fireballs +#define DF_DEBRIS_BLAST_RING 0x0040000 //blast ring +#define DF_DEBRIS_REMAINS 0x0080000 //stays around + +//Sound option +#define DF_DELAY_SOUND 0x1000000 //play sound at start of fade + diff --git a/Descent3/debuggraph.cpp b/Descent3/debuggraph.cpp new file mode 100644 index 000000000..2fd7872ab --- /dev/null +++ b/Descent3/debuggraph.cpp @@ -0,0 +1,619 @@ +/* +* $Logfile: /DescentIII/main/debuggraph.cpp $ +* $Revision: 4 $ +* $Date: 9/23/99 12:02p $ +* $Author: Jeff $ +* +* Debug graph visual log +* +* $Log: /DescentIII/main/debuggraph.cpp $ + * + * 4 9/23/99 12:02p Jeff + * include stdlib for atexit + * + * 3 5/10/99 10:21p Ardussi + * changes to compile on Mac + * + * 2 4/04/99 8:14p Jeff + * added debug graph stuff +* +* $NoKeywords: $ +*/ + + +#include +#include "debuggraph.h" +#include "grdefs.h" +#include "mono.h" +#include +#include "pserror.h" +#include "bitmap.h" +#include "renderer.h" +#include "pstypes.h" +#include "stringtable.h" +#include "newui.h" +#include "newui_core.h" +#include "mem.h" +#include +#include "game.h" + +#define DATA_TYPE_INT 0 +#define DATA_TYPE_FLOAT 1 +#define NUM_FRAMES_TO_DISPLAY 128 +#define MAX_GRAPH_NODES 16 +#define MAX_NAME 38 + +int graph_bmp = -1; +int graph_mask = 0; +int graph_num_nodes = 0; + +typedef struct +{ + char *color_name; + ddgr_color color; +}tGraphColor; + +tGraphColor GraphColors[MAX_GRAPH_NODES] = +{ + {"Green", GR_RGB(40,255,40)}, + {"Red", GR_RGB(255,40,40)}, + {"Blue", GR_RGB(40,40,255)}, + {"Yellow", GR_RGB(255,255,40)}, + {"Purple", GR_RGB(255,40,255)}, + {"White", GR_RGB(255,255,255)}, + {"Lt.Grey", GR_RGB(180,180,180)}, + {"Dk.Grey", GR_RGB(80,80,80)}, + {"Orange", GR_RGB(255,140,5)}, + {"Pink", GR_RGB(250,147,147)}, + {"Brown", GR_RGB(119,68,2)}, + {"Dk.Blue", GR_RGB(3,20,124)}, + {"Dk.Red", GR_RGB(100,2,2)}, + {"Dk.Green",GR_RGB(2,100,9)}, + {"Turquoise",GR_RGB(14,229,202)}, + {"Dk.Purple",GR_RGB(100,4,104)} +}; + +typedef struct +{ + ubyte data_input; + int flags; + + union + { + int i_min_value; + float f_min_value; + }; + union + { + int i_max_value; + float f_max_value; + }; + +}tAddDebugGraph; + +class tGraphNode +{ +public: + tGraphNode() {} + ~tGraphNode() {} + + ddgr_color color; + + void Initialize(tAddDebugGraph *data); + void Update(int value); + void Update(float value); + void Render(void); + + char name[MAX_NAME]; + tAddDebugGraph init_data; +private: + + + int cur_write_pos; + int first_pos; + int num_items_stored; + + union + { + int i_data[NUM_FRAMES_TO_DISPLAY]; + float f_data[NUM_FRAMES_TO_DISPLAY]; + }; +}; + + +typedef struct tDebugGraphNode +{ + tGraphNode *node; + tDebugGraphNode *next; +}tDebugGraphNode; +tDebugGraphNode *DebugGraphNode_root = NULL; + +tGraphNode *DebugGraph_AddNode(void) +{ + if(graph_num_nodes==MAX_GRAPH_NODES) + return NULL; + + tDebugGraphNode *curr = DebugGraphNode_root; + + if(!curr) + { + curr = DebugGraphNode_root = (tDebugGraphNode *)mem_malloc(sizeof(tDebugGraphNode)); + }else + { + while(curr->next) + { + curr = curr->next; + } + curr->next = (tDebugGraphNode *)mem_malloc(sizeof(tDebugGraphNode)); + curr = curr->next; + } + + if(!curr) + { + Error("Out of Memory"); + } + + graph_num_nodes++; + curr->next = NULL; + curr->node = new tGraphNode; + + if(!curr->node) + { + Error("Out of Memory"); + } + + + return curr->node; +} + +tGraphNode *DebugGraph_FindNode(int index) +{ + if(index>=graph_num_nodes) + return NULL; + tDebugGraphNode *curr = DebugGraphNode_root; + int ci = 0; + while(curr) + { + if(ci==index) + { + return curr->node; + } + curr = curr->next; + ci++; + } + + return NULL; +} + +void DebugGraph_Free(void) +{ + tDebugGraphNode *curr,*next; + curr = DebugGraphNode_root; + + while(curr) + { + delete curr->node; + next = curr->next; + mem_free(curr); + curr = next; + } + + DebugGraphNode_root = NULL; + + if(graph_bmp>BAD_BITMAP_HANDLE) + { + bm_FreeBitmap(graph_bmp); + } + graph_bmp = -1; + graph_num_nodes = 0; +} + +void DebugGraph_Initialize(void) +{ + atexit(DebugGraph_Free); + DebugGraphNode_root = NULL; + graph_bmp = -1; + graph_mask = 0; + graph_num_nodes = 0; +} + +int DebugGraph_Add(int min,int max,char *name,int flags) +{ + tAddDebugGraph data; + tGraphNode *node = DebugGraph_AddNode(); + if(!node) + return -1; + + int index = graph_num_nodes-1; + data.data_input = DATA_TYPE_INT; + data.i_max_value = max; + data.i_min_value = min; + data.flags = flags; + node->Initialize(&data); + node->color = GraphColors[index].color; + strncpy(node->name,name,MAX_NAME-1); + node->name[MAX_NAME-1] = '\0'; + return index; +} + +int DebugGraph_Add(float min,float max,char *name,int flags) +{ + tAddDebugGraph data; + tGraphNode *node = DebugGraph_AddNode(); + if(!node) + return -1; + + int index = graph_num_nodes-1; + data.data_input = DATA_TYPE_FLOAT; + data.f_max_value = max; + data.f_min_value = min; + data.flags = flags; + node->Initialize(&data); + node->color = GraphColors[index].color; + strncpy(node->name,name,MAX_NAME-1); + node->name[MAX_NAME-1] = '\0'; + return index; +} + +void DebugGraph_Enable(int id) +{ + int bit = 0x01<=graph_num_nodes) + return; + + tGraphNode *node = DebugGraph_FindNode(id); + if(!node) + return; + + node->Update(value); +} + +void DebugGraph_Update(int id,float value) +{ + if(id<0 || id>=graph_num_nodes) + return; + + tGraphNode *node = DebugGraph_FindNode(id); + if(!node) + return; + + node->Update(value); +} + +#define TOP_L 10 +#define TOP_T 50 + +void DebugGraph_Render(void) +{ + if(graph_bmp==-1) + { + //create the bitmap + graph_bmp = bm_AllocBitmap(64,64,0); + if(graph_bmp>BAD_BITMAP_HANDLE) + { + ushort *data = bm_data(graph_bmp,0); + for(int i=0;i<64*64;i++) + { + data[i] = GR_RGB16(0,0,0)|OPAQUE_FLAG; + } + } + } + if(graph_bmp<=BAD_BITMAP_HANDLE) + return; + + if(graph_mask==0) + return; + + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (240); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + + rend_DrawScaledBitmap(TOP_L,TOP_T,TOP_L+256,TOP_T+64,graph_bmp,0,0,1,1); + + int bit = 0x01; + tGraphNode *node; + bool multi_game = (bool)((Game_mode&GM_MULTI)!=0); + bool ok_to_render; + + for(int i=0;iinit_data.flags&DGF_MULTIPLAYER)) + ok_to_render = false; + }else + { + if(!(node->init_data.flags&DGF_SINGLEPLAYER)) + ok_to_render = false; + } + + if(ok_to_render) + node->Render(); + } + bit = bit<<1; + } + + rend_SetFiltering (1); +} + +//////////////////////////////////////////// +void tGraphNode::Initialize(tAddDebugGraph *data) +{ + if(!data) + return; + + memcpy(&init_data,data,sizeof(tAddDebugGraph)); + cur_write_pos = 0; + first_pos = 0; + num_items_stored = 0; + + color = GR_RGB(255,40,40); + memset(i_data,0,sizeof(int)*NUM_FRAMES_TO_DISPLAY); +} + +void tGraphNode::Update(int value) +{ + ASSERT(init_data.data_input==DATA_TYPE_INT); + + bool multi_game = (bool)((Game_mode&GM_MULTI)!=0); + if(multi_game) + { + if(!(init_data.flags&DGF_MULTIPLAYER)) + return; + }else + { + if(!(init_data.flags&DGF_SINGLEPLAYER)) + return; + } + + if(cur_write_pos!=0 && (cur_write_pos%NUM_FRAMES_TO_DISPLAY)==0) + { + cur_write_pos = 0; + } + + if(num_items_stored==NUM_FRAMES_TO_DISPLAY) + { + first_pos++; + if(first_pos!=0 && (first_pos%NUM_FRAMES_TO_DISPLAY)==0) + { + first_pos = 0; + } + }else + { + num_items_stored++; + } + + if(value < init_data.i_min_value) + value = init_data.i_min_value; + if(value > init_data.i_max_value) + value = init_data.i_max_value; + + i_data[cur_write_pos] = value; + cur_write_pos++; +} + +void tGraphNode::Update(float value) +{ + ASSERT(init_data.data_input==DATA_TYPE_FLOAT); + + bool multi_game = (bool)((Game_mode&GM_MULTI)!=0); + if(multi_game) + { + if(!(init_data.flags&DGF_MULTIPLAYER)) + return; + }else + { + if(!(init_data.flags&DGF_SINGLEPLAYER)) + return; + } + + if(cur_write_pos!=0 && (cur_write_pos%NUM_FRAMES_TO_DISPLAY)==0) + { + cur_write_pos = 0; + } + + if(num_items_stored==NUM_FRAMES_TO_DISPLAY) + { + first_pos++; + if(first_pos!=0 && (first_pos%NUM_FRAMES_TO_DISPLAY)==0) + { + first_pos = 0; + } + }else + { + num_items_stored++; + } + + if(value < init_data.f_min_value) + value = init_data.f_min_value; + if(value > init_data.f_max_value) + value = init_data.f_max_value; + + f_data[cur_write_pos] = value; + + cur_write_pos++; +} + +void GetGraphY(int *y,int value,int max,int min) +{ + float range; + range = float(value-min)/float(max-min); + + *y = TOP_T + 64 - int(range*64.0); +} + +void GetGraphY(int *y,float value,float max,float min) +{ + float range; + range = (value-min)/(max-min); + + *y = TOP_T + 64 - int(range*64.0); +} + +void tGraphNode::Render(void) +{ + if(num_items_stored<2) + return; + rend_SetFlatColor(color); + + int last_x,last_y,y; + int index = first_pos; + int count = num_items_stored; + count--; + + switch(init_data.data_input) + { + case DATA_TYPE_FLOAT: + GetGraphY(&last_y,f_data[index],init_data.f_max_value,init_data.f_min_value); + break; + case DATA_TYPE_INT: + GetGraphY(&last_y,i_data[index],init_data.i_max_value,init_data.i_min_value); + break; + }; + index++; + last_x = TOP_L; + + while(count > 0 ) + { + if(index!=0 && (index%NUM_FRAMES_TO_DISPLAY)==0) + { + index = 0; + } + + switch(init_data.data_input) + { + case DATA_TYPE_FLOAT: + GetGraphY(&y,f_data[index],init_data.f_max_value,init_data.f_min_value); + break; + case DATA_TYPE_INT: + GetGraphY(&y,i_data[index],init_data.i_max_value,init_data.i_min_value); + break; + }; + + rend_DrawLine(last_x,last_y,last_x+1,y); + count--; + last_x+=2; + last_y = y; + index++; + } +} + +/* +*********************************************************************** +*/ + +void DebugGraph_DisplayOptions(void) +{ + if(graph_num_nodes==0) + return; + + newuiTiledWindow window; + newuiSheet *sheet; + + bool **bool_values = NULL; + bool exit_menu = false; + tGraphNode *node; + + bool_values = (bool **)mem_malloc(sizeof(bool *)*graph_num_nodes); + if(!bool_values) + return; + + window.Create("Debug Graph",0,0,256,384); + sheet = window.GetSheet(); + + sheet->NewGroup(NULL,0,0); + char buffer[256]; + int bit = 0x01; + + bool multi_game = (bool)((Game_mode&GM_MULTI)!=0); + + for(int i=0;iinit_data.flags&DGF_MULTIPLAYER)) + { + bit = bit<<1; + continue; + } + }else + { + if(!(node->init_data.flags&DGF_SINGLEPLAYER)) + { + bit = bit<<1; + continue; + } + } + + sprintf(buffer,"%s (%s)",node->name,GraphColors[i].color_name); + bool_values[i] = sheet->AddLongCheckBox(buffer); + if(bool_values[i]) + { + *bool_values[i] = (bool)((graph_mask&bit)!=0); + } + + bit = bit<<1; + } + + sheet->NewGroup(NULL,50,200); + sheet->AddButton(TXT_OK,UID_OK); + + //process + window.Open(); + + while (!exit_menu) + { + int res = window.DoUI(); + + // handle all UI results. + switch (res) + { + case UID_OK: + { + for(int x=0;x +#include + +#ifndef __LINUX__ +typedef int socklen_t; +#endif + +#include "pstypes.h" +#include "pserror.h" +#include "pstring.h" +#include "CFILE.H" +#include "InfFile.h" +#include "dedicated_server.h" +#include "multi.h" +#include "args.h" +#include "AppConsole.h" +#include "ddio.h" +#include "newui.h" +#include "ui.h" +#include "multi_dll_mgr.h" +#include "multi_ui.h" +#include "Mission.h" +#include "multi_server.h" +#include "Macros.h" +#include "game.h" +#include "mem.h" +#include "stringtable.h" +#include "multi_save_settings.h" +#include "objinfo.h" +#include "rtperformance.h" +#include "player.h" +#include "stringtable.h" +#include "init.h" +#include "ship.h" +#include "hud.h" + +#ifdef MACINTOSH +#include "macsock.h" +#endif + +bool Dedicated_server=false; + +int Dedicated_start_level = 1; +int Dummy_dedicated_var; +char Dummy_dedicated_string[_MAX_PATH]; + +//If true, allow remote connections +int Dedicated_allow_remote = 0; +ushort Dedicated_listen_port = 2092; +char dedicated_telnet_password[65]; +int Dedicated_num_teams = 1; + + +int CheckMissionForScript(char *mission,char *script,int dedicated_server_num_teams); + +extern char Multi_message_of_the_day[]; +extern char PXO_hosted_lobby_name[]; +// These define the types of variables that can be set in the code through +// the dedicated server + +cvar_entry CVars[]={ +{"PPS",CVAR_TYPE_INT,&Netgame.packets_per_second,2,20,CVAR_GAMEINIT}, // 0 +{"TimeLimit",CVAR_TYPE_INT,&Netgame.timelimit,0,10000,CVAR_GAMEINIT}, +{"KillGoal",CVAR_TYPE_INT,&Netgame.killgoal,0,10000,CVAR_GAMEINIT}, +{"RespawnTime",CVAR_TYPE_INT,&Netgame.respawn_time,0,10000,CVAR_GAMEINIT}, +{"GameName",CVAR_TYPE_STRING,&Netgame.name,-1,NETGAME_NAME_LEN,CVAR_GAMEINIT}, // 4 +{"MissionName",CVAR_TYPE_STRING,&Netgame.mission,-1,MSN_NAMELEN,CVAR_GAMEINIT}, +{"Scriptname",CVAR_TYPE_STRING,&Netgame.scriptname,-1,NETGAME_SCRIPT_LEN,CVAR_GAMEINIT}, +{"ConnectionName",CVAR_TYPE_STRING,&Netgame.connection_name,-1,PAGENAME_LEN,CVAR_GAMEINIT}, +{"Quit",CVAR_TYPE_NONE,NULL,-1,-1,CVAR_GAMEPLAY}, //8 +{"EndLevel",CVAR_TYPE_NONE,NULL,-1,-1,CVAR_GAMEPLAY}, +#if 0 //def DEMO +{"MaxPlayers",CVAR_TYPE_INT,&Netgame.max_players,2,16,CVAR_GAMEINIT}, //10 +#else +{"MaxPlayers",CVAR_TYPE_INT,&Netgame.max_players,2,MAX_PLAYERS,CVAR_GAMEINIT}, //10 +#endif +{"Say",CVAR_TYPE_STRING,NULL,-1,200,CVAR_GAMEPLAY}, //11 +{"UseSmoothing",CVAR_TYPE_INT,&Dummy_dedicated_var,0,1,CVAR_GAMEINIT}, //12 +{"SendRotVel",CVAR_TYPE_INT,&Dummy_dedicated_var,0,1,CVAR_GAMEINIT}, //13 +{"MultiSettingsFile",CVAR_TYPE_STRING,&Dummy_dedicated_string,-1,40,CVAR_GAMEINIT}, //14 +{"DisallowPowerup",CVAR_TYPE_STRING,&Dummy_dedicated_string,-1,40,CVAR_GAMEINIT|CVAR_GAMEPLAY}, //15 +{"AllowPowerup",CVAR_TYPE_STRING,&Dummy_dedicated_string,-1,40,CVAR_GAMEINIT|CVAR_GAMEPLAY}, //16 +{"StartRtLog",CVAR_TYPE_NONE,NULL,-1,-1,CVAR_GAMEPLAY}, //17 +{"StopRtLog",CVAR_TYPE_NONE,NULL,-1,-1,CVAR_GAMEPLAY}, //18 +{"PXOUsername",CVAR_TYPE_STRING,&Auto_login_name,-1,50,CVAR_GAMEINIT}, //19 +{"PXOPassword",CVAR_TYPE_STRING,&Auto_login_pass,-1,50,CVAR_GAMEINIT}, //20 +{"BrightPlayers",CVAR_TYPE_INT,&Dummy_dedicated_var,-1,-1,CVAR_GAMEINIT}, //21 +{"Peer2Peer",CVAR_TYPE_INT,&Dummy_dedicated_var,-1,-1,CVAR_GAMEINIT}, //22 +{"AccurateCollisions",CVAR_TYPE_INT,&Dummy_dedicated_var,-1,-1,CVAR_GAMEINIT}, //23 +{"ConsolePassword",CVAR_TYPE_STRING,&dedicated_telnet_password,-1,65,CVAR_GAMEINIT|CVAR_GAMEPLAY},//24 +{"AllowRemoteConsole",CVAR_TYPE_INT,&Dedicated_allow_remote,0,1,CVAR_GAMEINIT},//25 +{"RemoteConsolePort",CVAR_TYPE_INT,&Dedicated_listen_port,1,65535,CVAR_GAMEINIT},//26 +{"Permissable",CVAR_TYPE_INT,&Dummy_dedicated_var,-1,-1,CVAR_GAMEINIT},//27 +{"NumTeams",CVAR_TYPE_INT,&Dedicated_num_teams,2,4,CVAR_GAMEINIT},//28 +{"PXOHostingLobby",CVAR_TYPE_STRING,&PXO_hosted_lobby_name,-1,100,CVAR_GAMEINIT|CVAR_GAMEPLAY},//29 +{"RandomizeRespawn",CVAR_TYPE_INT,&Dummy_dedicated_var,-1,-1,CVAR_GAMEINIT},//30 +{"AllowMouselook",CVAR_TYPE_INT,&Dummy_dedicated_var,-1,-1,CVAR_GAMEINIT},//31 +{"AudioTauntDelay",CVAR_TYPE_FLOAT,NULL,-1,-1,CVAR_GAMEINIT},//32 +{"SetLevel",CVAR_TYPE_INT,NULL,-1,-1,CVAR_GAMEINIT|CVAR_GAMEPLAY},//33 +{"SetDifficulty",CVAR_TYPE_INT,NULL,0,4,CVAR_GAMEINIT},//34 +{"MOTD",CVAR_TYPE_STRING,&Multi_message_of_the_day,-1,HUD_MESSAGE_LENGTH*2,CVAR_GAMEINIT},//35 +}; + +#define CVAR_TIMELIMIT 1 +#define CVAR_KILLGOAL 2 +#define CVAR_QUIT 8 +#define CVAR_ENDLEVEL 9 +#define CVAR_MAXPLAYERS 10 +#define CVAR_MESSAGE 11 +#define CVAR_USESMOOTHING 12 +#define CVAR_SENDROTVEL 13 +#define CVAR_SETTINGS 14 +#define CVAR_DISALLOW 15 +#define CVAR_ALLOW 16 +#define CVAR_STARTLOG 17 +#define CVAR_STOPLOG 18 +#define CVAR_BRIGHTPLAYERS 21 +#define CVAR_PEER2PEER 22 +#define CVAR_ACCURATE_COLL 23 +#define CVAR_PERMISSABLE 27 +#define CVAR_RANDOMIZERESPAWN 30 +#define CVAR_ALLOWMOUSELOOKERS 31 +#define CVAR_AUDIOTAUNTDELAY 32 +#define CVAR_SETLEVEL 33 +#define CVAR_SETDIFF 34 +#define CVAR_MOTD 35 + +#define MAX_CVARS (sizeof(CVars)/sizeof(cvar_entry)) + +// Takes the data that the config file has an runs a server based on that data +// Returns true if ok, false if something is wrong +int RunServerConfigs () +{ + // Load the connection DLL + if(LoadMultiDLL(Netgame.connection_name)) + { + CallMultiDLL(MT_AUTO_START); + if (!MultiDLLGameStarting) + { + PrintDedicatedMessage (TXT_DS_COULDNTINIT,Netgame.connection_name); + PrintDedicatedMessage("\n"); + return 0; + } + else + { + PrintDedicatedMessage (TXT_DS_DLLINIT); + PrintDedicatedMessage("\n"); + } + } + else + { + PrintDedicatedMessage (TXT_DS_CONNECTLOADERR,Netgame.connection_name); + PrintDedicatedMessage("\n"); + return 0; + } + + //Put in the correct mission names for the oem builds +#if ( defined(OEM) || defined(DEMO) ) + if(strcmpi("polaris.d3l",Netgame.mission)==0) + { + strcpy(Netgame.mission_name,"Polaris"); + } + else if(strcmpi("taurus.d3l",Netgame.mission)==0) + { + strcpy(Netgame.mission_name,"Taurus"); + } + else if(strcmpi("thecore.d3l",Netgame.mission)==0) + { + strcpy(Netgame.mission_name,"The Core"); + } +#else + char * p = GetMissionName(Netgame.mission); + strcpy(Netgame.mission_name,p); +#endif + + // Load the mission + if (!LoadMission (Netgame.mission)) + { + PrintDedicatedMessage (TXT_DS_LOADMISSIONERR,Netgame.mission); + PrintDedicatedMessage("\n"); + return 0; + } + else + { + PrintDedicatedMessage (TXT_DS_MISSIONLOADED,Netgame.mission); + PrintDedicatedMessage("\n"); + } + + if(Current_mission.num_levels>=Dedicated_start_level) + { + Current_mission.cur_level = Dedicated_start_level; + } + else + { + Current_mission.cur_level = 1; + } + // Start the actual server + + int teams = CheckMissionForScript(Netgame.mission,Netgame.scriptname,Dedicated_num_teams); + + if(teams==-1) + { + PrintDedicatedMessage (TXT_MSNNOTCOMPATIBLE); + PrintDedicatedMessage("\n"); + return 0; + } + else + { + MultiStartServer (0,Netgame.scriptname,teams); + strcpy (Players[0].callsign,TXT_DS_SERVERNAME); + } + + InitDedicatedSocket(Dedicated_listen_port); + + return 1; +} + +int DedicatedServerLex(const char *command) +{ + for (int i = 0; i < MAX_CVARS; i++) + if (stricmp(CVars[i].varname, command) == 0) + return i; + + return INFFILE_ERROR; +} + + +// Reads in the server config file for a dedicated server +// Returns true if everything is ok +int LoadServerConfigFile () +{ + InfFile inf; + char operand[INFFILE_LINELEN]; // operand + int t=FindArgChar("-dedicated", 'd'); + +// int t=FindArg ("-dedicated"); + if (!t) + { + PrintDedicatedMessage (TXT_DS_BADCOMMANDLINE); + PrintDedicatedMessage("\n"); + return 0; + } + + // Set our defaults + MultiResetSettings(); + + // Make all ships available + for(int i=0;i INFFILE_ERROR) + { + SetCVar (CVars[cmd].varname,operand,true); + } + } + + inf.Close(); + + if (!RunServerConfigs ()) + return 0; + + return 1; +} + + +// Starts a dedicated server and loads the server config file +void StartDedicatedServer () +{ + int t=FindArgChar("-dedicated", 'd'); + //int t=FindArg ("-dedicated"); + if (!t) + return; + + Dedicated_server=true; +} + +// Sets the value for a cvar NONE type +void SetCVarNone (int index) +{ + if (CVars[index].type!=CVAR_TYPE_NONE) + return; // Only handles ints + + // Do command specific stuff here + if (index==CVAR_QUIT) + { + if (NetPlayers[Player_num].sequence==NETSEQ_PLAYING) + { + MultiLeaveGame(); + SetFunctionMode (QUIT_MODE); + } + } + + if (index==CVAR_ENDLEVEL) + { + if (NetPlayers[Player_num].sequence==NETSEQ_PLAYING) + { + MultiEndLevel (); + } + } + + if (index==CVAR_STARTLOG) + rtp_StartLog(); + + if (index==CVAR_STOPLOG) + rtp_StopLog(); + +} + +// Sets the value for a cvar INT type +void SetCVarInt (int index,int val) +{ + void *dest_variable; + + if (CVars[index].type!=CVAR_TYPE_INT) + return; // Only handles ints + + + dest_variable=CVars[index].dest_variable; + + if(!(CVars[index].var_min==-1 && CVars[index].var_max==-1)) + { + if (valCVars[index].var_max) + val=(int)CVars[index].var_max; + } + + // Do command specific stuff here + switch( index ) + { + case CVAR_TIMELIMIT: + { + if (val>0) + Netgame.flags|=NF_TIMER; + else + Netgame.flags&=~NF_TIMER; + }break; + + case CVAR_KILLGOAL: + { + if (val>0) + Netgame.flags|=NF_KILLGOAL; + else + Netgame.flags&=~NF_KILLGOAL; + }break; + + case CVAR_MAXPLAYERS: + { + if (val>MAX_PLAYERS) + val=MAX_PLAYERS; + else if (val<2) + val=2; + + }break; + + case CVAR_BRIGHTPLAYERS: + { + if (val) + Netgame.flags|=NF_BRIGHT_PLAYERS; + else + Netgame.flags&=~NF_BRIGHT_PLAYERS; + }break; + + case CVAR_USESMOOTHING: + { + if (val) + Netgame.flags|=NF_USE_SMOOTHING; + else + Netgame.flags&=~NF_USE_SMOOTHING; + }break; + + case CVAR_PEER2PEER: + { + if (val) + { + Netgame.flags|=NF_PEER_PEER; + Netgame.flags&=~NF_PERMISSABLE; + } + else + Netgame.flags&=~NF_PEER_PEER; + }break; + + case CVAR_PERMISSABLE: + { + if (val) + { + Netgame.flags|=NF_PERMISSABLE; + Netgame.flags&=~NF_PEER_PEER; + } + else + Netgame.flags&=~NF_PERMISSABLE; + }break; + + case CVAR_RANDOMIZERESPAWN: + { + if (val) + { + Netgame.flags|=NF_RANDOMIZE_RESPAWN; + } + else + Netgame.flags&=~NF_RANDOMIZE_RESPAWN; + }break; + + case CVAR_ACCURATE_COLL: + { + if (val) + Netgame.flags|=NF_USE_ACC_WEAP; + else + Netgame.flags&=~NF_USE_ACC_WEAP; + }break; + + case CVAR_SENDROTVEL: + { + if (val) + Netgame.flags|=NF_SENDROTVEL; + else + Netgame.flags&=~NF_SENDROTVEL; + }break; + case CVAR_SETLEVEL: + { + if (NetPlayers[Player_num].sequence==NETSEQ_PLAYING) + { + if (val>=1 && val<=Current_mission.num_levels) + { + Multi_next_level=val; + MultiEndLevel (); + } + } + else + { + if(val>=1) + { + Dedicated_start_level = val; + } + } + }break; + case CVAR_ALLOWMOUSELOOKERS: + { + if (val) + Netgame.flags|=NF_ALLOW_MLOOK; + else + Netgame.flags&=~NF_ALLOW_MLOOK; + }break; + case CVAR_SETDIFF: + { + Netgame.difficulty = val; + } + break; + } + + + if(dest_variable) + *((int *)dest_variable)=val; +} + +// Sets the value for a cvar FLOAT type +void SetCVarFloat (int index,float val) +{ + void *dest_variable; + + if (CVars[index].type!=CVAR_TYPE_FLOAT) + return; // Only handles ints + + dest_variable=CVars[index].dest_variable; + + if(!(CVars[index].var_min==-1 && CVars[index].var_max==-1)) + { + if (valCVars[index].var_max) + val=(float)CVars[index].var_max; + } + + if(dest_variable) + *((float *)dest_variable)=val; + + // Do command specific stuff here + switch( index ) + { + case CVAR_AUDIOTAUNTDELAY: + MultiSetAudioTauntTime(val); + break; + } +} +// Sets the value for a cvar string type +void SetCVarString (int index,char *val) +{ + void *dest_variable; + + if (CVars[index].type!=CVAR_TYPE_STRING) + return; // Only handles ints + + if(index==CVAR_MESSAGE) + { + //say the message to everyone + char str[255]; + + int to_whom; + char *msg = GetMessageDestination(val,&to_whom); + + if(to_whom != MULTI_SEND_MESSAGE_ALL) + val = msg; + + Psprintf (str,sizeof(str),TXT_HUDSAY,Players[Player_num].callsign,val); + + MultiSendMessageFromServer (GR_RGB(0,128,255),str,to_whom); + + return; + } + + if (index==CVAR_SETTINGS) + { + if (MultiLoadSettings (val)) + { + PrintDedicatedMessage (TXT_DS_SETTINGSLOADED); + PrintDedicatedMessage("\n"); + } + else + { + PrintDedicatedMessage (TXT_DS_SETTINGSERR); + PrintDedicatedMessage("\n"); + } + } + + if (index==CVAR_DISALLOW) + { + int objid = FindObjectIDName(IGNORE_TABLE(val)); + if(objid!=-1) + { + PrintDedicatedMessage (TXT_DS_DISALLOWOBJECT,Object_info[objid].name); + PrintDedicatedMessage("\n"); + Object_info[objid].multi_allowed=0; + } + } + + if (index==CVAR_ALLOW) + { + int objid = FindObjectIDName(IGNORE_TABLE(val)); + if(objid!=-1) + { + PrintDedicatedMessage (TXT_DS_ALLOWOBJECTS,Object_info[objid].name); + PrintDedicatedMessage("\n"); + Object_info[objid].multi_allowed=1; + } + } + + dest_variable=CVars[index].dest_variable; + + if(dest_variable) + { + strncpy ((char *)dest_variable,val,CVars[index].var_max); + } +} + + +// The accessor function that sets the value of a cvar +void SetCVar (char *cvar_string,char *cvar_argument,bool game_init) +{ + int i; + int done=0; + + for (i=0;iServerTimeout) + { + int quitno = DedicatedServerLex ("Quit"); + if(quitno>=0) + SetCVar (CVars[quitno].varname,"",false); + } + + } + + bool new_command=con_Input (str,255); + + if (!new_command) + return; + + if (str[0]=='$') + { + DLLInfo.input_string=str; + CallGameDLL (EVT_CLIENT_INPUT_STRING,&DLLInfo); + return; + } + + ParseLine (str,command,operand,255,255); + + if (!command[0]) + return; + + int index=DedicatedServerLex (command); + + if (index<0) + { + PrintDedicatedMessage (TXT_DS_BADCOMMAND); + PrintDedicatedMessage("\n"); + return; + } + + // Everything ok, set command + SetCVar (CVars[index].varname,operand,false); + +} + +// Prints a message to the console if the dedicated server is active +void PrintDedicatedMessage(const char *fmt, ...) +{ + if (!Dedicated_server) + return; + + char buf[CON_MAX_STRINGLEN]; + va_list args; + + va_start(args, fmt ); + Pvsprintf(buf,CON_MAX_STRINGLEN,fmt,args); + + con_Printf (buf); + DedicatedSocketputs(buf); +} + +#ifdef __LINUX__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/linux_fix.h" +#include "errno.h" +#define BOOL bool +#define SOCKET unsigned int +#define SOCKADDR_IN sockaddr_in +#define SOCKADDR sockaddr +#define INVALID_SOCKET -1 + +//Winsock = sockets error translation + +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINVAL EINVAL +#define WSAENOPROTOOPT ENOPROTOOPT + + +#define WSAGetLastError(a) errno + +#define SOCKET_ERROR -1 + + +#endif + + +typedef struct dedicated_socket +{ + SOCKET sock; + SOCKADDR_IN addr; + char input[255]; + bool validated; + dedicated_socket *prev; + dedicated_socket *next; +}dedicated_socket; + + +dedicated_socket *Head_sock = NULL; + +SOCKET dedicated_listen_socket = INVALID_SOCKET; + + + +#define DEDICATED_LOGIN_PROMPT TXT_DS_ENTERPASS + +void InitDedicatedSocket(ushort port) +{ + SOCKADDR_IN sock_addr; + + memset(&sock_addr,0,sizeof(SOCKADDR_IN)); + sock_addr.sin_family = AF_INET; + unsigned int my_ip; + my_ip = nw_GetThisIP(); + memcpy(&sock_addr.sin_addr.s_addr,&my_ip,sizeof(uint)); + sock_addr.sin_port = htons( port ); + + dedicated_listen_socket = socket(AF_INET,SOCK_STREAM,0); + + if(INVALID_SOCKET==dedicated_listen_socket) + { + mprintf((0,"Unable to create listen socket for dedicated server!\n")); + return; + } + if (SOCKET_ERROR==bind(dedicated_listen_socket, (SOCKADDR*)&sock_addr, sizeof (sock_addr))) + { + mprintf((0,"Unable to bind listen socket for dedicated server!\n")); + return; + } + if(listen(dedicated_listen_socket,1)) + { + mprintf((0,"Unable to listen on dedicated server socket!\n")); + return; + } + //Make the socket non-blocking + unsigned long argp = 1; +#if defined(WIN32) + ioctlsocket(dedicated_listen_socket,FIONBIO,&argp); +#elif defined(__LINUX__) + ioctl(dedicated_listen_socket,FIONBIO,&argp); +#endif + +} + +void ListenDedicatedSocket (void) +{ + SOCKET incoming_socket; + SOCKADDR_IN conn_addr; +// int addrlen = sizeof(conn_addr); + socklen_t addrlen = sizeof(conn_addr); + incoming_socket = accept(dedicated_listen_socket,(SOCKADDR *)&conn_addr,&addrlen); + if(INVALID_SOCKET!=incoming_socket) + { + //Make the socket non-blocking + unsigned long argp = 1; + #if defined(WIN32) + ioctlsocket(incoming_socket,FIONBIO,&argp); + #elif defined(__LINUX__) + ioctl(incoming_socket,FIONBIO,&argp); + #endif + if(!Dedicated_allow_remote) + { + //Check to see if this came in from the local address + unsigned long localhost = inet_addr("127.0.0.1"); + if(memcmp(&localhost,&conn_addr.sin_addr,sizeof(localhost))!=0) + { + mprintf((0,"Rejecting connection from remote host!\n")); + PrintDedicatedMessage(TXT_DS_REJECTREMOTE,inet_ntoa(conn_addr.sin_addr)); + PrintDedicatedMessage("\n"); + shutdown(incoming_socket,2); + #if defined(WIN32) + closesocket(incoming_socket); + #elif defined(__LINUX__) + close(incoming_socket); + #endif + return; + } + } + //At this point it's considered a valid connection + PrintDedicatedMessage(TXT_DS_NEWCONNECT,inet_ntoa(conn_addr.sin_addr)); + PrintDedicatedMessage("\n"); + dedicated_socket *new_socket; + new_socket = (dedicated_socket *)mem_malloc(sizeof(dedicated_socket)); + if(Head_sock) + Head_sock->prev = new_socket; + new_socket->next = Head_sock; + new_socket->prev = NULL; + Head_sock = new_socket; + memcpy(&new_socket->addr,&conn_addr,sizeof(new_socket->addr)); + new_socket->input[0] = NULL; + new_socket->sock = incoming_socket; + new_socket->validated = false; + //Give the new connection the login prompt + send(new_socket->sock,DEDICATED_LOGIN_PROMPT,strlen(DEDICATED_LOGIN_PROMPT)+1,0); + } + + + + +} + +void DedicatedSocketputs(char *str) +{ + char newstr[300]; + char buf[2]; + + memset(newstr,0,300); + int len = strlen(str); + for(int i=0;ivalidated) + { + send(conn->sock,newstr,strlen(newstr)+1,0); + } + conn = conn->next; + } +} + +dedicated_socket *DedicatedLogoutTelnet(dedicated_socket *conn) +{ + //user is no longer valid (so we don't attempt to send them a message + conn->validated = false; + + PrintDedicatedMessage(TXT_DS_REMOTECLOSE,inet_ntoa(conn->addr.sin_addr)); + PrintDedicatedMessage("\n"); + + shutdown(conn->sock,2); + #if defined(WIN32) + closesocket(conn->sock); + #elif defined(__LINUX__) + close(conn->sock); + #endif + + dedicated_socket *prev; + prev = conn->prev; + if(prev) + { + prev->next = conn->next; + } + else + { + Head_sock = conn->next; + if(Head_sock) + Head_sock->prev = NULL; + } + + mem_free(conn); + return prev; +} + +void DedicatedReadTelnet(void) +{ + dedicated_socket *conn; + char buf[5]; + conn = Head_sock; + + while(conn) + { + int bytesread = 1; + while(bytesread>=0 && conn) + { + //Read one byte at a time, looking for a CR + bytesread = recv(conn->sock,buf,1,0); + if(bytesread==0) + { + //Connection closed. Remove it from the list, and inform the other(?) users. + conn = DedicatedLogoutTelnet(conn); + break; + } + else if(bytesread>0) + { + buf[1] = NULL; + //When we see a CR, process the line + if((buf[0]==0x0a)||(buf[0]==0x0d)) + { + if(conn->input[0]==0) + { + continue; + } + send(conn->sock,"\r\n",strlen("\r\n"),0); + if(conn->validated) + { + char command[255]; // operand + char operand[255]; // operand + + if(!stricmp(conn->input,"logout")) + { + //log the connection out + conn = DedicatedLogoutTelnet(conn); + }else + { + //Process the string + PrintDedicatedMessage("[%s] %s\n",inet_ntoa(conn->addr.sin_addr),conn->input); + if (conn->input[0]=='$') + { + DLLInfo.input_string=conn->input; + CallGameDLL (EVT_CLIENT_INPUT_STRING,&DLLInfo); + conn->input[0] = NULL; + return; + } + + ParseLine (conn->input,command,operand,255,255); + conn->input[0] = NULL; + if (!command[0]) + return; + + int index=DedicatedServerLex (command); + + if (index<0) + { + PrintDedicatedMessage (TXT_DS_BADCOMMAND); + PrintDedicatedMessage("\n"); + return; + } + + // Everything ok, set command + SetCVar (CVars[index].varname,operand,false); + } + } + else + { + //All we want to see from them is the correct password + if(strcmp(conn->input,dedicated_telnet_password)==0) + { + //They logged in ok! + conn->validated = true; + send(conn->sock,"\r\n",strlen("\r\n")+1,0); + PrintDedicatedMessage(TXT_DS_REMOTELOGGEDIN,inet_ntoa(conn->addr.sin_addr)); + PrintDedicatedMessage("\n"); + conn->input[0] = NULL; + } + else + { + PrintDedicatedMessage(TXT_DS_BADPASS,inet_ntoa(conn->addr.sin_addr)); + PrintDedicatedMessage("\n"); + shutdown(conn->sock,2); + #if defined(WIN32) + closesocket(conn->sock); + #elif defined(__LINUX__) + close(conn->sock); + #endif + } + } + } + else + { + if(buf[0]==8) + { + //handle backspace + char delstr[5]; + delstr[0] = 8; + delstr[1] = ' '; + delstr[2] = 8; + delstr[3] = NULL; + conn->input[strlen(conn->input)-1] = NULL; + buf[1] = NULL; + send(conn->sock,delstr,strlen(delstr),0); + } + else + { + if (strlen(conn->input)+bytesread >= sizeof(conn->input)) + { + conn->input[0] = 0; + char *s = "\n\rCommand line too long\n\r"; + send(conn->sock,s,strlen(s),0); + } + else + { + strcat(conn->input,buf); + send(conn->sock,buf,1,0); + } + } + + } + } + } + if(conn) + conn = conn->next; + } +} diff --git a/Descent3/demofile.cpp b/Descent3/demofile.cpp new file mode 100644 index 000000000..f29017a3b --- /dev/null +++ b/Descent3/demofile.cpp @@ -0,0 +1,2096 @@ +/* + * $Logfile: /DescentIII/Main/demofile.cpp $ + * $Revision: 74 $ + * $Date: 10/21/01 7:15p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/Main/demofile.cpp $ + * + * 74 10/21/01 7:15p Matt + * Hack to set the ship_index field for each Player[] so the LOD models + * work right. + * + * 73 10/04/01 9:49a Matt + * Added missing semicolon. + * + * 72 10/03/01 11:39p Kevin + * init hud on demo playback + * + * 71 10/03/01 11:35p Kevin + * multiplayer stripes appear in demo playback + * + * 70 9/11/01 11:25a Matt + * Fixed rear-view camera problem when a demo using a player other than 0 + * started and the rear-view was already on. + * + * 69 9/10/01 2:30p Matt + * Added code to clear the 'press space to continue...' message after + * player respawn. + * + * 68 9/10/01 2:07p Matt + * Made add-on data load before level is loaded so texture are mapped + * right. + * + * 67 4/21/00 2:50p Matt + * Added check for killing an invalid object type. + * + * 66 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 65 3/23/00 2:35p Matt + * Added generic object id translastion when creating objects during demo + * playback. + * + * 64 10/22/99 2:38p Jay + * Update for the FinalBoss Laser Effect + * + * 63 7/28/99 9:39a Kevin + * + * 62 7/27/99 12:58p Kevin + * fixed napalm, omega & vauss rendering for other players in a + * multiplayer game + * + * 61 7/21/99 7:17p Chris + * Fixed an attach related crash in the load/save game code + * + * 60 7/21/99 2:32p Kevin + * currupt demo will no longer let you keep playing when the demo ends... + * + * 59 7/21/99 12:03p Kevin + * Changed version numbers around + * + * 58 7/14/99 4:59p Kevin + * fixed multiplayer weapons sitting still bug + * + * 57 6/10/99 9:07p Samir + * fixed weapon string name localization issues. + * + * 56 5/24/99 1:26a Matt + * Correctly deal with hit_info == NULL being passed to collision + * routines. + * + * 55 5/12/99 6:01p Jeff + * flip order of saving/loading objects and players + * + * 54 5/06/99 1:19a Samir + * shortened filename length to 128 because of problems with fopen and + * huge filenames. + * + * 53 5/06/99 12:34a Samir + * made the editbox take up to _MAX_FNAME characters. The cfile code has + * been changed to handle larger paths too, but just to keep some + * consistancy. + * + * 52 4/27/99 9:59p Kevin + * CD not requred for dedicated server. Also improved demo playback + * + * 51 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 50 4/23/99 11:39p Kevin + * doh! + * + * 49 4/23/99 10:28p Kevin + * fixed problems with minimal install and demo/savegames + * + * 48 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 47 4/16/99 6:00p Kevin + * Bunches of Demo stuff + * + * 46 4/15/99 1:38a Jeff + * changes for linux compile + * + * 45 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 44 3/16/99 6:43p Kevin + * OEM Split fixes + * + * 43 3/12/99 7:53p Jeff + * save player rotating balls info, handle obj_observer with obj_player, + * handle objects that use life left, handle 2d sound playing + * + * 42 3/11/99 11:40a Kevin + * Added SetObjDead code for multiplayer stuff + * + * 41 3/10/99 6:20p Jeff + * many fixes to demo system. Fixed IGC so cameras move...fixed osiris to + * be restored correctly, and it handles errors on restore + * + * 40 3/10/99 2:25p Kevin + * Save/Load and Demo file fixes + * + * 39 2/25/99 10:58a Matt + * Added new explosion system. + * + * 38 2/23/99 10:45a Kevin + * + * 37 2/23/99 10:37a Kevin + * Added camera to list of objects that are saved when changed + * + * 36 2/23/99 12:44a Jeff + * added support for in-game-cinematics in demo system + * + * 35 2/22/99 9:22p Kevin + * Added DT_CINEMATICS + * + * 34 2/17/99 2:44p Kevin + * Adde movie making capabilities to the demo playback system + * + * 33 2/15/99 11:48p Matt + * Fixed compile warnings + * + * 32 2/15/99 1:22p Kevin + * Changes for GameGauge + * + * 31 2/08/99 8:59p Kevin + * Fixed powerups + * + * 30 2/08/99 7:05p Kevin + * Trying to get demo system working with powerup scripts + * + * 29 2/07/99 1:17a Jeff + * peppered UI dialogs that were missing NEWUIRES_FORCEQUIT to handle it + * + * 28 2/02/99 1:20p Kevin + * Added script saving + * + * 27 2/02/99 12:43p Kevin + * Fixes to the Save/Load game system, and the demo system to work with + * the new OSIRIS + * + * 26 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 25 1/31/99 3:44p Matt + * Streamlined game sequencing + * + * 24 1/29/99 2:08p Jeff + * localization + * + * 23 1/29/99 12:47p Matt + * Rewrote the doorway system + * + * 22 1/23/99 10:10p Kevin + * Added the start of osiris support into the demo system + * + * 21 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 20 1/15/99 7:52p Chris + * Updated ObjSetPos() to include a f_update_attach_children flag + * + * 19 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 18 11/24/98 3:57p Kevin + * Demo system immprovements + * + * 17 11/24/98 12:08p Kevin + * + * 16 11/24/98 10:41a Kevin + * Demo system + * + * 15 11/23/98 4:52p Kevin + * Demo system enhancments + * + * 14 11/23/98 3:11p Kevin + * Demo system + * + * 13 11/19/98 5:53p Kevin + * + * 12 11/19/98 5:40p Kevin + * Demo system + * + * 11 11/17/98 4:16p Kevin + * Demo recording system + * + * 10 11/11/98 2:46p Kevin + * Demo recording system work + * + * 9 11/09/98 4:12p Kevin + * + * 8 11/05/98 5:54p Kevin + * Demo system work + * + * 7 10/15/98 12:22a Matt + * Fixed compile warnings + * + * 6 10/12/98 1:46p Samir + * new Program_version parameters. + * + * 5 10/08/98 12:00p Kevin + * Demo system work + * + * 4 10/06/98 5:45p Kevin + * Added new configuration for demo + * + * 3 10/05/98 12:09p Kevin + * Converted projects to VC6 and demo file stuff added + * + * 2 10/05/98 10:32a Kevin + * Initial version + * + * 1 10/05/98 10:22a Kevin + * + * $NoKeywords: $ + */ + +#include +#include "CFILE.H" +#include "objinfo.h" +#include "ship.h" +#include "ui.h" +#include "newui.h" +#include "hud.h" +#include "stringtable.h" +#include "program.h" +#include "Mission.h" +#include "game.h" +#include "gamesequence.h" +#include "weapon.h" +#include "damage.h" +#include "mem.h" +#include "ObjScript.h" +#include "hlsoundlib.h" +#include "viseffect.h" +#include "collide.h" +#include "sounds.h" +#include "fireball.h" +#include "attach.h" +#include "gameloop.h" +#include "multi.h" +#include "multisafe.h" +#include "osiris_dll.h" +#include "args.h" +#include "gamecinematics.h" +#include "psrand.h" +#include "cockpit.h" +//We borrow a lot of code from the savegame system +#include "gamesave.h" +#include "demofile.h" + + +extern bool is_multi_demo; +CFILE * Demo_cfp = NULL; +char Demo_fname[_MAX_PATH*2]; +char Old_demo_fname[_MAX_PATH*2]; +float Demo_next_frame = 0; +float Demo_frame_time = 0; +float Demo_last_pinfo; +float Demo_frame_ofs; +int Demo_auto_idx=0; +unsigned int Demo_flags = 0; +unsigned short Demo_obj_map[MAX_OBJECTS]; +bool Demo_turretchanged[MAX_OBJECTS]; +bool Demo_looping = false; +bool Demo_paused = false; +bool Demo_do_one_frame = false; +bool Demo_restart = false; +bool Demo_auto_play=false; +bool Demo_first_frame=true; +bool Demo_make_movie = false; + +#define DEMO_PINFO_UPDATE .1 +#define MAX_COOP_TURRETS 400 +extern float turret_holder[MAX_COOP_TURRETS]; + +extern gs_tables *gs_Xlates; + +extern float Min_frametime; +extern float Max_frametime; +extern float Avg_frametime; +extern unsigned int Frames_counted; +extern bool Game_paused; + +extern bool Game_gauge_do_time_test; + +bool Demo_play_fast = false; + +void PageInAllData (void); + +//Prompts user for filename and starts recording if successfull +void DemoToggleRecording() +{ + char szfile[_MAX_PATH*2]; + szfile[0] = NULL; + if(Demo_flags==DF_RECORDING) + { + //Stop recording and close the file + cfclose(Demo_cfp); + Demo_flags = DF_NONE; + AddBlinkingHUDMessage(TXT_DEMOSAVED); + + Demo_fname[0] = NULL; + return; + } + else if(Demo_flags==DF_PLAYBACK) + { + // We can't record a demo while we are playing back a demo + return; + } + +// hand coded 128 because long filenames were failing in cfopen(which called fopen, which failed on very +// long filenames. instead of printing a message out to the user, just don't allow filenames that long) + if(DoEditDialog(TXT_DEMOFILENAME,szfile,128)) + { + if(strcmpi(szfile+(strlen(szfile)-4),".dem")!=0) + { + strcat(szfile,".dem"); + } + ddio_MakePath(Demo_fname,Base_directory,"demo",szfile,NULL); + mprintf((0,"Saving demo to file: %s\n",Demo_fname)); + //Try to create the file + Demo_cfp = cfopen(Demo_fname,"wb"); + if(Demo_cfp) + { + //Setup the demo variables + if(!(Game_mode & GM_MULTI)) + { + MultiBuildMatchTables(); + } + //Male sure we write the player info the first frame + Demo_last_pinfo = timer_GetTime()-(DEMO_PINFO_UPDATE*2); + Demo_flags = DF_RECORDING; + //Write the header + DemoWriteHeader(); + DemoStartNewFrame(); + } + else + { + //cfopen failed + AddBlinkingHUDMessage(TXT_DEMOCANTCREATE); + Demo_fname[0] = NULL; + return; + } + } + + +} + +void DemoWriteHeader() +{ + char szsig[10]; + strcpy(szsig,D3_DEMO_SIG_NEW); + ASSERT(Demo_flags==DF_RECORDING); + + //Start off with the signature + cf_WriteString(Demo_cfp,(const char *)szsig); + //Next is the version + cf_WriteShort(Demo_cfp,GAMESAVE_VERSION); + //Write the mission filename + if(Current_mission.filename && (strcmpi("d3_2.mn3",Current_mission.filename)==0) ) + { + cf_WriteString(Demo_cfp, "d3.mn3"); + } + else + { + cf_WriteString(Demo_cfp, Current_mission.filename ? Current_mission.filename : ""); + } + + //Level number + cf_WriteInt(Demo_cfp,Current_mission.cur_level); + //Gametime + cf_WriteFloat(Demo_cfp,Gametime); + + //Frame count + cf_WriteInt(Demo_cfp, FrameCount); + + //Now store the world state (borrowing save game code) + + // Load translation tables + SGSXlateTables(Demo_cfp); + + // save out room information. + SGSRooms(Demo_cfp); + + // save out triggers + SGSTriggers(Demo_cfp); + + // save out object information. + SGSObjects(Demo_cfp); + + // players + SGSPlayers(Demo_cfp); + + // save out viseffects + SGSVisEffects(Demo_cfp); + + // save out spew + SGSSpew(Demo_cfp); + + Osiris_SaveSystemState(Demo_cfp); + + cf_WriteShort(Demo_cfp,Player_num); + +} + +void DemoStartNewFrame() +{ + if(Demo_flags!=DF_RECORDING) + { + return; + } + + + //Start with the gametime of this frame + cf_WriteByte(Demo_cfp, DT_NEW_FRAME); + cf_WriteFloat(Demo_cfp,Gametime); + cf_WriteFloat(Demo_cfp,Frametime); + + if((timer_GetTime()-Demo_last_pinfo)>=DEMO_PINFO_UPDATE) + { + DemoWritePlayerInfo(); + Demo_last_pinfo = timer_GetTime(); + } + +} + +//Store object num, position, orientation +//WB info, Anim states +void DemoWriteChangedObj(object *op) +{ + ASSERT(op); + if(Demo_flags==DF_RECORDING) + { + gs_WriteByte(Demo_cfp, DT_OBJ); + cf_WriteShort(Demo_cfp,OBJNUM(op)); + // positional information + gs_WriteInt(Demo_cfp, op->roomnum); + gs_WriteVector(Demo_cfp, op->pos); + gs_WriteMatrix(Demo_cfp, op->orient); + if(op->type==OBJ_PLAYER || op->type==OBJ_OBSERVER) + { + gs_WriteInt(Demo_cfp, Players[op->id].flags); + } + } +} + +void DemoWriteWeaponFire(unsigned short objectnum,vector *pos,vector *dir,unsigned short weaponnum,unsigned short weapobjnum,short gunnum) +{ + uint uniqueid = MultiGetMatchChecksum (OBJ_WEAPON,weaponnum); + if(weapobjnum==-1) + return; + if(!Weapons[weaponnum].used) + { + Int3(); + } + if(Demo_flags==DF_RECORDING) + { + gs_WriteByte(Demo_cfp, DT_WEAPON_FIRE); + cf_WriteFloat(Demo_cfp,Gametime); + cf_WriteShort(Demo_cfp,objectnum); + cf_WriteInt(Demo_cfp,uniqueid); + gs_WriteVector(Demo_cfp, *pos); + gs_WriteVector(Demo_cfp, *dir); + gs_WriteShort(Demo_cfp,weapobjnum); + cf_WriteShort(Demo_cfp,gunnum); + } +} + +void DemoWriteHudMessage(unsigned int color,bool blink,char *msg) +{ + if(Demo_flags==DF_RECORDING) + { + cf_WriteByte(Demo_cfp, DT_HUD_MESSAGE); + cf_WriteInt(Demo_cfp, color); + cf_WriteByte(Demo_cfp, blink?1:0); + cf_WriteString(Demo_cfp,msg); + } +} + +void DemoWriteChangedObjects() +{ + int i; + //int num_changed = 0; + if(Demo_flags==DF_RECORDING) + { + for (i = 0; i <= Highest_object_index; i++) + { + + if((Objects[i].type==OBJ_PLAYER)||(Objects[i].type==OBJ_OBSERVER)||(Objects[i].type==OBJ_ROBOT)||(Objects[i].type==OBJ_POWERUP)||(Objects[i].type==OBJ_CLUTTER)||(Objects[i].type==OBJ_BUILDING)||(Objects[i].type==OBJ_CAMERA)) + { + if((Objects[i].flags & OF_MOVED_THIS_FRAME))//||(Objects[i].mtype.phys_info.velocity!=Zero_vector)) + { + //mprintf((0,"Object moved this frame! type = %d\n",Objects[i].type)); + DemoWriteChangedObj(&Objects[i]); + //num_changed++; + } + } + } + // if(num_changed) + // mprintf((0,"%d Objects moved this frame!\n",num_changed)); + } +} + +void DemoWriteObjCreate(ubyte type, ushort id, int roomnum, vector *pos, const matrix *orient, int parent_handle,object * obj) +{ + + if(Demo_flags==DF_RECORDING) + { + + if((type==OBJ_ROBOT)||(type==OBJ_POWERUP)||(type==OBJ_CLUTTER)||(type==OBJ_BUILDING)||(type==OBJ_CAMERA)) + { + cf_WriteByte(Demo_cfp, DT_OBJ_CREATE); + //cf_WriteFloat(Demo_cfp,Gametime); + cf_WriteByte(Demo_cfp, type); + cf_WriteByte(Demo_cfp,orient?1:0); + cf_WriteShort(Demo_cfp,id); + cf_WriteInt(Demo_cfp, roomnum); + gs_WriteVector(Demo_cfp, *pos); + cf_WriteInt(Demo_cfp, parent_handle); + if(orient) + gs_WriteMatrix(Demo_cfp, *orient); + + // Add the script name if there is one + // Start with the name len (minus the null) + + cf_WriteShort(Demo_cfp,OBJNUM(obj)); + } + } +} + +#define MAX_COOP_TURRETS 400 +extern float turret_holder[MAX_COOP_TURRETS]; + +void DemoWriteTurretChanged(unsigned short objnum) +{ + Demo_turretchanged[objnum] = true; +} + +void DemoReadTurretChanged(void) +{ + multi_turret multi_turret_info; + int objnum; + ushort num_turrets; + float turr_time; + int count = 0; + float do_time; + + do_time = cf_ReadFloat(Demo_cfp); + short old_objnum = cf_ReadShort(Demo_cfp); + objnum = Demo_obj_map[old_objnum]; + + turr_time = cf_ReadFloat(Demo_cfp); + num_turrets = cf_ReadShort(Demo_cfp); + multi_turret_info.keyframes = (float *)&turret_holder; + multi_turret_info.num_turrets = num_turrets; + for(int i = 0; i < num_turrets; i++) + { + if(MAX_COOP_TURRETS>i) + multi_turret_info.keyframes[i] = cf_ReadFloat(Demo_cfp); + } + + if(Objects[objnum].type!=OBJ_NONE) + { + //mprintf((0,"Updating turret %d!\n",objnum)); + ObjSetTurretUpdate(objnum, &multi_turret_info); + } +} + + +void DemoWriteObjAnimChanged(unsigned short objnum) +{ + + custom_anim multi_anim_info; + if(ObjGetAnimUpdate(objnum, &multi_anim_info)) // Checks if obj is still alive and all + { + cf_WriteByte(Demo_cfp, DT_OBJ_ANIM); + cf_WriteFloat(Demo_cfp,Gametime); + cf_WriteShort(Demo_cfp,objnum); + + cf_WriteFloat(Demo_cfp,multi_anim_info.server_time); + cf_WriteShort(Demo_cfp,multi_anim_info.server_anim_frame); + cf_WriteByte(Demo_cfp, multi_anim_info.anim_start_frame); + cf_WriteByte(Demo_cfp, multi_anim_info.anim_end_frame); + + cf_WriteFloat(Demo_cfp,multi_anim_info.anim_time); + cf_WriteFloat(Demo_cfp,multi_anim_info.max_speed); + + cf_WriteByte(Demo_cfp, multi_anim_info.flags); + cf_WriteShort(Demo_cfp,multi_anim_info.anim_sound_index); + } +} + +void DemoReadObjAnimChanged(void) +{ + custom_anim multi_anim_info; + int objnum; + int count = 0; + float changetime; + changetime = cf_ReadFloat(Demo_cfp); + objnum = cf_ReadShort(Demo_cfp); + + multi_anim_info.server_time = cf_ReadFloat(Demo_cfp); + multi_anim_info.server_anim_frame = cf_ReadShort(Demo_cfp); + multi_anim_info.anim_start_frame = cf_ReadByte(Demo_cfp); + multi_anim_info.anim_end_frame = cf_ReadByte(Demo_cfp); + multi_anim_info.anim_time = cf_ReadFloat(Demo_cfp); + multi_anim_info.max_speed = cf_ReadFloat(Demo_cfp); + multi_anim_info.flags = cf_ReadByte(Demo_cfp); + multi_anim_info.anim_sound_index = cf_ReadShort(Demo_cfp); + + if(Objects[objnum].type!=OBJ_NONE) + { + ObjSetAnimUpdate(objnum, &multi_anim_info); + } + +} + +void DemoWriteKillObject(object *hit_obj,object *killer,float damage,int death_flags,float delay,int seed) +{ + cf_WriteByte(Demo_cfp,DT_OBJ_EXPLODE); + cf_WriteShort(Demo_cfp,OBJNUM(hit_obj)); + cf_WriteShort(Demo_cfp,killer?OBJNUM(killer):65535); + cf_WriteFloat(Demo_cfp,damage); + cf_WriteInt(Demo_cfp,death_flags); + cf_WriteFloat(Demo_cfp,delay); + cf_WriteShort(Demo_cfp,seed); +} + +void DemoReadKillObject(void) +{ + short hit_objnum = cf_ReadShort(Demo_cfp); + short killer = cf_ReadShort(Demo_cfp); + float damage = cf_ReadFloat(Demo_cfp); + int death_flags = cf_ReadInt(Demo_cfp); + float delay = cf_ReadFloat(Demo_cfp); + short seed = cf_ReadShort(Demo_cfp); + + if (! (IS_GENERIC(Objects[hit_objnum].type) || (Objects[hit_objnum].type == OBJ_DOOR))) + return; //bail if invalid object type + + ps_srand(seed); + KillObject(&Objects[hit_objnum],(killer!=65535)?&Objects[killer]:NULL,damage,death_flags,delay); +} + +void DemoWritePlayerDeath(object *player,bool melee,int fate) +{ + cf_WriteByte(Demo_cfp,DT_PLAYER_DEATH); + cf_WriteShort(Demo_cfp,OBJNUM(player)); + cf_WriteByte(Demo_cfp,melee?1:0); + cf_WriteInt(Demo_cfp, fate); +} + +void DemoReadPlayerDeath(void) +{ + short playernum = cf_ReadShort(Demo_cfp); + ubyte melee = cf_ReadByte(Demo_cfp); + int fate = cf_ReadInt(Demo_cfp); + InitiatePlayerDeath(&Objects[playernum],melee?true:false,fate); +} + +void DemoWrite2DSound(short soundidx,float volume) +{ + cf_WriteByte(Demo_cfp,DT_2D_SOUND); + cf_WriteShort(Demo_cfp,soundidx); + cf_WriteFloat(Demo_cfp,volume); +} + +void DemoRead2DSound(void) +{ + int soundidx = cf_ReadShort(Demo_cfp); + float volume = cf_ReadFloat(Demo_cfp); + + Sound_system.Play2dSound(soundidx,volume); +} + +void DemoWrite3DSound(short soundidx,ushort objnum,int priority,float volume) +{ + cf_WriteByte(Demo_cfp,DT_3D_SOUND); + cf_WriteShort(Demo_cfp,objnum); + cf_WriteShort(Demo_cfp,soundidx); + cf_WriteFloat(Demo_cfp,volume); + +} + +void DemoRead3DSound(void) +{ + int objnum; + short soundidx; + float volume; + + objnum = cf_ReadShort(Demo_cfp); + + soundidx = cf_ReadShort(Demo_cfp); + volume = cf_ReadFloat(Demo_cfp); + + Sound_system.Play3dSound(soundidx,&Objects[objnum],volume); +} + + + +void DemoWriteScriptEvent() +{ + +} + +int FrameDemoDelta=0; +int DemoFrameCount = 0; + +int DemoPlaybackFile(char *filename) +{ + is_multi_demo = false; + mprintf((0,"Playing back demo file!\n")); + MultiBuildMatchTables(); + if(FindArg("-makemovie")) + { + Demo_make_movie = true; + } + + if(FindArg("-fastdemo")) + { + Demo_play_fast = true; + } + Demo_first_frame=true; + for(int i=0;iGAMESAVE_VERSION) + { + //This file version is too new! + //return 0; + } + + cf_ReadString(demo_mission,_MAX_PATH,Demo_cfp); + level_num = cf_ReadInt(Demo_cfp); + demo_gametime = cf_ReadFloat(Demo_cfp); + frame_count = cf_ReadInt(Demo_cfp); + + //Now load the mission + Osiris_DisableCreateEvents(); + IsRestoredGame = true; + if (LoadMission((const char *)demo_mission)) + { + mng_LoadAddonPages (); + + SetCurrentLevel(level_num); + if (!LoadAndStartCurrentLevel()) + { + mprintf((0,"Couldn't start new level to play back the demo!\n")); + Osiris_EnableCreateEvents(); + return 0; + } + } + else + { + mprintf((0,"Couldn't load the level to play back the demo!\n")); + Osiris_EnableCreateEvents(); + return 0; + } + Osiris_EnableCreateEvents(); + + FrameCount = frame_count; + Demo_next_frame = demo_gametime; + + if (gs_Xlates) + mem_free(gs_Xlates); + gs_Xlates = (struct gs_tables *)mem_malloc(sizeof(gs_tables)); + + try + { + LGSXlateTables(Demo_cfp); + + LGSRooms(Demo_cfp); + + LGSTriggers(Demo_cfp); + + for(int j=0;j<=Highest_object_index;j++) + { + if( (Objects[j].type==OBJ_PLAYER) && (Objects[j].id!=Player_num) ) + { + object *objp = &Objects[j]; + objp->type=OBJ_GHOST; + objp->movement_type=MT_NONE; + objp->render_type=RT_NONE; + SetObjectControlType(objp, CT_NONE); + } + } + + LGSObjects(Demo_cfp, ver); + //Fix up the object list for use in MSAFE + for(int a=0;atype!=OBJ_NONE); + + int roomnum = cf_ReadInt(Demo_cfp); + gs_ReadVector(Demo_cfp,pos); + gs_ReadMatrix(Demo_cfp,orient); + if(obj->type==OBJ_PLAYER||obj->type==OBJ_OBSERVER) + { + if(obj->id!=Player_num) + is_multi_demo = true; + Players[obj->id].flags = cf_ReadInt(Demo_cfp); + + if (! (Players[obj->id].flags & PLAYER_FLAGS_DEAD)) + ResetPersistentHUDMessage(); + } + //if((!((obj->flags&OF_DYING)||(obj->flags&OF_EXPLODING)||(obj->flags&OF_DEAD)))&&obj->type!=255) + + if(obj->type!=OBJ_NONE) + { + obj->mtype.phys_info.velocity = Zero_vector; + obj->mtype.phys_info.rotvel = Zero_vector; + ObjSetPos (obj,&pos,roomnum,&orient,true); + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + if( (!(obj->flags&OF_ATTACHED)) && (pm->n_attach) ) + AttachUpdateSubObjects(obj); + } +} + +void DemoReadHudMessage() +{ + char msg[HUD_MESSAGE_LENGTH]; + int color = cf_ReadInt(Demo_cfp); + ubyte blink = cf_ReadByte(Demo_cfp); + cf_ReadString(msg,HUD_MESSAGE_LENGTH,Demo_cfp); + + if(color) + { + AddColoredHUDMessage (color,msg); + } + else if(blink) + { + AddBlinkingHUDMessage(msg); + } + else + { + AddHUDMessage(msg); + } +} + + + +void DemoReadWeaponFire() +{ + vector pos,dir; + float gametime; + uint uniqueid; + object *obj; + short weaponnum,objnum,weapobjnum; + vector laser_pos,laser_dir; + + // Mass driver is hack + static int mass_driver_id=-2; + static int mass_driver_ring=-2; + static int omega_id = -2; + static int vauss_id = -2; + static int napalm_id = -2; + + if (mass_driver_id==-2) + { + mass_driver_id=FindWeaponName ("Mass Driver"); + ASSERT(mass_driver_id != -1); //DAJ -1FIX + mass_driver_ring=FindTextureName ("CloakRing.tga1"); + ASSERT(mass_driver_ring != -1); //DAJ -1FIX + omega_id = FindWeaponName ("Omega"); + ASSERT(omega_id != -1); //DAJ -1FIX + vauss_id = FindWeaponName ("Vauss"); + ASSERT(vauss_id != -1); //DAJ -1FIX + napalm_id = FindWeaponName ("Napalm"); + ASSERT(napalm_id != -1); //DAJ -1FIX + } + + gametime = cf_ReadFloat(Demo_cfp); + short old_objnum = cf_ReadShort(Demo_cfp); + objnum = Demo_obj_map[old_objnum]; + obj = &Objects[objnum]; + ASSERT(Objects[objnum].type!=OBJ_NONE); + + uniqueid = cf_ReadInt(Demo_cfp); + gs_ReadVector(Demo_cfp,pos); + gs_ReadVector(Demo_cfp,dir); + laser_pos = pos; + laser_dir = dir; + weaponnum = MultiMatchWeapon(uniqueid); + weapobjnum = cf_ReadShort(Demo_cfp); + short gunnum = cf_ReadShort(Demo_cfp); + ASSERT(uniqueid != 0xffffffff); + ASSERT(dir != Zero_vector); + + + //This is a hack for the napalm, omega & vauss to prevent making files incompatible + if( (obj->type==OBJ_PLAYER) && (obj->id!=Player_num) ) + { + if(omega_id==weaponnum) + { + Players[obj->id].weapon[PW_PRIMARY].index = OMEGA_INDEX; + } + else if(vauss_id==weaponnum) + { + Players[obj->id].weapon[PW_PRIMARY].index = VAUSS_INDEX; + } + else if(napalm_id==weaponnum) + { + Players[obj->id].weapon[PW_PRIMARY].index = NAPALM_INDEX; + } + + } + + unsigned short new_weap_objnum = CreateAndFireWeapon( &pos, &dir, &Objects[objnum], weaponnum); + if(0xffff==new_weap_objnum) + { + Int3(); + return; + } + Objects[new_weap_objnum].ctype.laser_info.src_gun_num=gunnum; + + mprintf((0,"Player %d Firing weapon (%d) -- using objnum %d (old num = %d)\n",obj->id,weaponnum,new_weap_objnum,weapobjnum)); + + Demo_obj_map[weapobjnum] = new_weap_objnum; + short weapon_num = weaponnum; + + if (Weapons[weapon_num].sounds[WSI_FLYING]!=SOUND_NONE_INDEX) + Sound_system.Play3dSound(Weapons[weapon_num].sounds[WSI_FLYING], &Objects[objnum]); + + // Do a muzzle flash from this gun + if (Weapons[weapon_num].flags & WF_MUZZLE) + { + int visnum; + vector newpos; + + if (obj==&Objects[Players[Player_num].objnum]) + { + newpos=laser_pos+(laser_dir); + visnum=VisEffectCreate (VIS_FIREBALL,MUZZLE_FLASH_INDEX,obj->roomnum,&newpos); + } + else + { + newpos=laser_pos+(laser_dir/2); + visnum=VisEffectCreate (VIS_FIREBALL,MUZZLE_FLASH_INDEX,obj->roomnum,&newpos); + } + + // Make this guy live for a very short time + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + + vis->lifetime=.08f; + vis->lifeleft=.08f; + vis->size=1.0f; + + vis->movement_type=MT_PHYSICS; + vis->velocity=obj->mtype.phys_info.velocity; + + // Make some smoke! + visnum=VisEffectCreate (VIS_FIREBALL,MED_SMOKE_INDEX,obj->roomnum,&newpos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + + vis->lifetime=.3f; + vis->lifeleft=.3f; + + vis->movement_type=MT_PHYSICS; + vis->velocity=obj->mtype.phys_info.velocity; + vis->velocity.y+=10; + } + + } + } + + // Check for mass driver + if (weaponnum==mass_driver_id) + { + // Create a fire trail for this weapon + // find out where this weapon hits + fvi_query fq; + fvi_info hit_data; + + vector dest_vec=laser_pos+(laser_dir*5000); + + fq.p0 = &laser_pos; + fq.startroom = obj->roomnum; + fq.p1 = &dest_vec; + fq.rad = 0; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS|FQ_IGNORE_POWERUPS|FQ_IGNORE_WEAPONS; + + fvi_FindIntersection(&fq, &hit_data); + + float mag=vm_VectorDistanceQuick (&hit_data.hit_pnt,&obj->pos); + + int visnum; + + if(obj == NULL || obj->type != OBJ_BUILDING || stricmp(Object_info[obj->id].name, "FinalbossLITTLETIT") != 0) + visnum=VisEffectCreate (VIS_FIREBALL,MASSDRIVER_EFFECT_INDEX,obj->roomnum,&laser_pos); + else + visnum=VisEffectCreate (VIS_FIREBALL,MERCBOSS_MASSDRIVER_EFFECT_INDEX,obj->roomnum,&laser_pos); + + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->custom_handle=mass_driver_ring; + vis->lifeleft=1.5; + vis->lifetime=1.5; + + vis->end_pos=hit_data.hit_pnt; + vis->billboard_info.width=1; + if(obj == NULL || obj->type != OBJ_BUILDING || stricmp(Object_info[obj->id].name, "FinalbossLITTLETIT") != 0) + vis->lighting_color=GR_RGB16(100,100,170); + else + vis->lighting_color=GR_RGB16(170,100,100); + vis->flags|=VF_LINK_TO_VIEWER|VF_EXPAND; + vis->size=mag; + } + } + +} + +void DemoReadObjCreate() +{ + //float gametime; + ubyte type; + ubyte use_orient; + matrix orient; + vector pos; + int roomnum; + short id; + int parent_handle; + object *obj; + + + type = cf_ReadByte(Demo_cfp); + use_orient = cf_ReadByte(Demo_cfp); + id = cf_ReadShort(Demo_cfp); + roomnum = cf_ReadInt(Demo_cfp); + gs_ReadVector(Demo_cfp,pos); + parent_handle = cf_ReadInt(Demo_cfp); + if(use_orient) + gs_ReadMatrix(Demo_cfp,orient); + //mprintf((0,"Creating object ype: %d id: %d room: %d\n",type,id,roomnum)); + + // xlate id to new id. + switch (type) + { + case OBJ_ROBOT: + case OBJ_POWERUP: + case OBJ_BUILDING: + case OBJ_CLUTTER: + id = gs_Xlates->obji_indices[id]; + break; + case OBJ_CAMERA: //Don't xlate camera IDs + break; + case OBJ_DOOR: + case OBJ_WEAPON: + case OBJ_PLAYER: + Int3(); //Shouldn't be creating these things + break; + default: + Int3(); //What is this? + } + + short new_objnum = ObjCreate(type,id,roomnum,&pos,use_orient?&orient:NULL,parent_handle); + if(new_objnum > -1) { //DAJ -1FIX + obj = &Objects[new_objnum]; + + short oldobjnum = cf_ReadShort(Demo_cfp); + Demo_obj_map[oldobjnum] = new_objnum; + //MSAFE needs this list too + Server_object_list[oldobjnum] = new_objnum; + InitObjectScripts (obj); + obj->flags |= OF_SERVER_OBJECT; + } +} + + +//FrameDemoDelta = FrameCount +//DemoFrameCount = 0; + +ubyte DemoLastOpcode=0; + +void DemoFrame() +{ + ubyte opcode; + if(Demo_flags!=DF_PLAYBACK) + return; + if(!Demo_first_frame) + { + Demo_frame_ofs=0; + + if(Demo_make_movie) + { + DoScreenshot(); + } + //This code slows down demo playback + if( (!Game_gauge_do_time_test) && (!Demo_play_fast) ) + { + + float tdelta = timer_GetTime(); + //if(Gametimertype.pobj_info; + int i; + + if((a >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && + (obj->flags & OF_POLYGON_OBJECT) && (p_info->multi_turret_info.num_turrets)) + { + mprintf((0,"Turret %d updated!\n",a)); + multi_turret_info.keyframes = (float *)&turret_holder; + ObjGetTurretUpdate(a,&multi_turret_info); + + cf_WriteByte(Demo_cfp, DT_OBJ_TURRET); + cf_WriteFloat(Demo_cfp,Gametime); + cf_WriteShort(Demo_cfp,a);//objnum + cf_WriteFloat(Demo_cfp,multi_turret_info.time); + cf_WriteShort(Demo_cfp,multi_turret_info.num_turrets); + + for(i = 0; i < multi_turret_info.num_turrets; i++) + { + cf_WriteFloat(Demo_cfp,multi_turret_info.keyframes[i]); + } + } + } + } + for(i=0;i0) + { + cf_WriteFloat(Demo_cfp,Players[pnum].ballspeed); + + for (int i=0;i=0 && num_balls<=4); + + if(num_balls>0) + { + speed = cf_ReadFloat(Demo_cfp); + + for (int i=0;iflags&OF_USES_LIFELEFT) + { + cf_WriteByte(Demo_cfp,1); + cf_WriteFloat(Demo_cfp,obj->lifeleft); + }else + { + cf_WriteByte(Demo_cfp,0); + } + } +} + +void DemoReadObjLifeLeft(void) +{ + short oldobjnum = cf_ReadShort(Demo_cfp); + short local_objnum = Demo_obj_map[oldobjnum] ; + + if(cf_ReadByte(Demo_cfp)) + { + Objects[local_objnum].flags |= OF_USES_LIFELEFT; + Objects[local_objnum].lifeleft = cf_ReadFloat(Demo_cfp); + }else + { + Objects[local_objnum].flags &= ~OF_USES_LIFELEFT; + Objects[local_objnum].lifeleft = 0; + } +} diff --git a/Descent3/descent.cpp b/Descent3/descent.cpp new file mode 100644 index 000000000..9f30b93fe --- /dev/null +++ b/Descent3/descent.cpp @@ -0,0 +1,1037 @@ +/* + * $Logfile: /DescentIII/main/descent.cpp $ + * $Revision: 106 $ + * $Date: 3/20/00 12:05p $ + * $Author: Matt $ + * + * The Descent III main loop + * + * $Log: /DescentIII/main/descent.cpp $ + * + * 106 3/20/00 12:05p Matt + * Merge of Duane's post-1.3 changes. + * Misc Mac stuff, esp. GameRanger (Mac only) + * + * 105 11/04/99 12:35a Chris + * Added support for Merc + * + * 104 10/27/99 10:26a Matt + * Fixed bug from Mac merge + * + * 103 10/22/99 10:40p Matt + * Fixed editing error + * + * 102 10/22/99 1:21p Matt + * Mac merge + * + * 101 10/12/99 11:00a Jeff + * added -intro command line parameter to override intro movie filename + * + * 100 8/23/99 5:12p Kevin + * Proxy support + * + * 99 7/27/99 9:50a Kevin + * More DVD goodies + * + * 98 7/20/99 12:59p Jason + * added auto katmai support + * + * 97 7/12/99 6:43p Jeff + * delete lock file in temp directory after when exiting + * + * 96 6/03/99 8:48a Kevin + * fixes for new OEM version.... + * + * 95 5/21/99 10:05a Kevin + * Added stuff for a future DVD version + * + * 94 5/13/99 9:46a Kevin + * duh... not .cpp, .h! + * + * 93 5/13/99 9:34a Kevin + * call FreeMultiDLL() on quit to delete temp dll files. + * + * 92 5/12/99 2:23p Jeff + * Descent3 now has a setable temp directory for all temp files + * + * 91 5/10/99 10:21p Ardussi + * changes to compile on Mac + * + * 90 5/02/99 7:27p Samir + * a debug option, make it so -moviedir works for the dolby and intro too. + * + * 89 5/01/99 8:17p Jeff + * ifdef some windows specific code for Linux + * + * 88 4/27/99 9:28p Jason + * FROM SAMIR: Undid some stuff I broke in the last rev + * + * 87 4/27/99 6:30p Samir + * play dolby and intro in progression, changed some of the code a bit, it + * doesn't seem to break anything. + * + * 86 4/27/99 11:38a Kevin + * added code to play dolby1.mve + * + * 85 4/26/99 11:45a Kevin + * render a blank screen if prompting for a CD and there is no callback + * currently + * + * 84 4/25/99 6:14p Kevin + * added "-timetest file.dem" to behave like gamegauge does + * + * 83 4/25/99 2:19p Kevin + * main mission voice moved + * + * 82 4/21/99 2:15p Samir + * table file filter fixes. + * + * 81 4/18/99 4:29p Jeff + * fixed comparison of string with '\0' instead of NULL + * + * 80 4/18/99 1:56p Kevin + * Movie player fixes to avoid crashes running under the editor + * + * 79 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 78 4/17/99 3:44p Kevin + * Demo2 changes & fixes + * + * 77 4/15/99 9:25a Kevin + * CD check fixes and updates + * + * 76 4/14/99 3:07p Kevin + * Force main mission to list even on small installations + * + * 75 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 74 4/13/99 3:30p Samir + * changed call SetScreenMode(SM_CINEMATIC) to SM_MENU so UI works when + * the game starts. + * + * 73 4/09/99 2:38p Kevin + * Changed where on the CD D3 will look for files + * + * 72 4/02/99 7:04p Kevin + * Added splash screens do release builds + * + * 71 4/02/99 5:08p Matt + * Added intro movie. + * + * 70 3/31/99 3:04p Jeff + * added _real_ temporary credits system... + * + * 69 3/29/99 7:29p Jason + * made renderer handle default resolution more gracefully + * + * 68 3/22/99 6:26p Matt + * Cleaned up error handling in cfile and editor level loads. + * + * 67 3/19/99 4:08p Kevin + * Multiple CD installation support + * + * 66 3/18/99 2:33p Jeff + * fixed bug clearing the Current pilot stuff before we were done using it + * + * 65 3/15/99 4:32p Jeff + * fixed 'fake' memory leak with Current pilot + * + * 64 3/11/99 5:02p Samir + * set ui screen mode after showing static screen. + * + * 63 3/08/99 3:59p Kevin + * Idle processing of network packets fixed + * + * 62 3/01/99 9:51p Jeff + * fixed bad prototype + * + * 61 3/01/99 9:03p Kevin + * OEM Beta 4 + * + * 60 2/26/99 2:11a Samir + * do render GameFrame with app not active. + * + * 59 2/25/99 8:54p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/ms, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 58 2/21/99 6:38p Samir + * mouse and key input better. buffered mouse. + * + * 57 2/20/99 2:31p Kevin + * Made multiplayer DLLs return to the game list after a game. + * + * 56 2/13/99 12:32a Jeff + * inventory reset takes a different parameter + * + * 55 2/10/99 4:38p Matt + * Misc. changes for table file parsing + * + * 54 2/09/99 1:29a Jeff + * added code to let D3 process multiplayer games in the background if + * game is not in focus. + * + * 53 2/05/99 7:23p Kevin + * OEM Changes + * + * 52 2/04/99 9:46a Kevin + * OEM version changes + * + * 51 1/15/99 7:16p Kevin + * Added GameGauge Configuration & code + * + * 50 12/03/98 10:23a Jason + * fixed black background problem with D3D + * + * 49 10/28/98 4:48p Kevin + * Cleaned up inventory usage + * + * 48 10/28/98 12:27p Kevin + * Shutdown sound system when quiting + * + * 47 10/24/98 2:16p Samir + * moved pollcontrols to controls.cpp, readplayercontrols. + * + * 46 10/23/98 1:09p Samir + * Demo Release 1.0 + * + * 45 10/20/98 6:34p Jeff + * changes made to get dedicated server working + * + * 44 10/18/98 11:40p Matt + * Generalized the function that puts up the upsell screen so the credits + * could use it. + * + * 43 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 42 10/16/98 12:17p Samir + * took out ddio_KeyFrame. + * + * 41 10/14/98 4:37p Matt + * Made InitD3System() exit with error if there's a problem instead of + * returning a status value. Also moved some editor-specific code from + * init.cpp to mainfrm.cpp, and cleaned up some other initialization and + * error-handling code. + * + * 40 10/12/98 4:40p Samir + * mouse click to leave upsell screen. + * + * 39 10/12/98 1:46p Samir + * new Program_version parameters. + * + * 38 10/08/98 7:52p Samir + * added upsell screen. + * + * 37 10/08/98 7:46p Samir + * added demo upsell screen. + * + * 36 10/08/98 7:26p Samir + * changed the prototype for the defer handler callback. + * + * 35 10/08/98 12:00p Kevin + * Demo system work + * + * 34 10/02/98 12:30p Samir + * added version information for beta/demo. + * + * 33 9/18/98 1:27p Jason + * cleaned up renderer initting + * + * 32 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 31 9/15/98 12:42p Jason + * got dedicated server actually working + * + * 30 9/14/98 6:28p Jason + * first pass at getting dedicated server working + * + * 29 8/20/98 10:51a Samir + * added the RESTORE_GAME_MODE function mode,. + * + * 28 7/21/98 6:50p Jeff + * don't initialize or close renderer if set screen mode hasn't been + * called yet in debug handlers. + * + * 27 7/01/98 4:56p Samir + * redid init code. + * + * 26 6/16/98 10:38a Jeff + * localization, strings pulled out to stringtable.h and d3.str + * + * 25 4/27/98 7:24p Samir + * poll controller at specified rate. + * + * 24 3/23/98 8:03p Samir + * A bunch of changes to allow for ALT-TAB to work. + * + * 23 3/19/98 11:27a Samir + * Better error checking. + * + * 22 3/10/98 5:16p Samir + * Got debug callbacks working when you hit an Int3. + * + * 21 2/26/98 1:00p Samir + * input only when app is active. + * + * 20 2/23/98 4:46p Jeff + * Moved serialization check to Winmain + * + * 19 2/15/98 7:44p Matt + * Added groovy try/catch/throw error checking for cfile functions + * + * 18 2/08/98 5:01p Samir + * New game sequencing changes. + * + * 17 2/07/98 6:33p Jeff + * Added in serialization check + * + * 16 2/02/98 7:35p Samir + * Call QuickPlayGame to play game using quick mission system. + * + * 15 1/10/98 2:55p Jason + * changed some things for multiplayer + * + * 14 1/07/98 3:49p Jason + * only call StartNewGame if not in multiplayer + * + * 13 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * 12 12/03/97 7:34p Samir + * Run controller poll in idle loop. + * + * 11 10/27/97 4:30p Samir + * Set screen mode to SM_NULL when done with game. + * + * 10 10/16/97 2:30p Samir + * Added defer handler. + * + * 9 10/02/97 12:36p Samir + * Redid game sequencing flow. + * + * 8 9/17/97 11:16a Samir + * BIG SEGMENT RIPOUT. + * + * 7 9/12/97 4:08p Samir + * Made sure main game program version is DEVELOPMENT, not FULL + * DEVELOPMENT. + * + * 6 8/28/97 6:13p Matt + * Set the room engine to be always on + * + * 5 8/12/97 3:21p Samir + * When entering game from editor, call the QuickStart functions to load + * scripts, etc. + * + * 4 7/24/97 2:55p Matt + * Save game settings to registry on game exit + * + * 10 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 9 5/15/97 2:04p Samir + * Created functions to create game viewport. + * + * 8 3/10/97 11:30a Samir + * Took out osDebug global + * + * 7 3/03/97 5:23p Samir + * Added OS_database, and OS_debug objects pointers. + * + * 6 2/26/97 6:10 PM Jeremy + * removed #include of + * + * 5 2/04/97 5:28p Samir + * Added EDITOR_GAME_MODE mode + * + * 4 2/04/97 2:32p Samir + * Added a menu mode + * + * 3 1/30/97 6:20p Samir + * Mainloop was altered a little, and some changes reflecting osObject + * fix. + * + * $NoKeywords: $ + */ +#include + +#include "pserror.h" +#include "grdefs.h" +#include "mono.h" +#include "CFILE.H" + +#include "init.h" +#include "game.h" +#include "program.h" +#include "descent.h" +#include "menu.h" +#include "Mission.h" +#include "ddio.h" +#include "controls.h" +#include "Controller.h" +#include "gamesequence.h" +#include "stringtable.h" +#include "dedicated_server.h" +#include "networking.h" +#include "hlsoundlib.h" +#include "player.h" +#include "pilot.h" +#include "newui.h" +#include "credits.h" +#include "cinematics.h" +#include "args.h" +#include "multi_dll_mgr.h" +#include "localization.h" +#include "mem.h" +#if defined(MACINTOSH) && defined (GAMERANGER) +#include "GameRanger.h" +#endif +// --------------------------------------------------------------------------- +// Variables +// --------------------------------------------------------------------------- +#ifdef EDITOR +static function_mode Function_mode = EDITOR_MODE; // Game function mode +#else +static function_mode Function_mode = INIT_MODE; // Game function mode +#endif +static function_mode Last_function_mode = INIT_MODE; + +grScreen *Game_screen = NULL; // The one and only video screen +oeApplication *Descent = NULL; // The Main application +oeAppDatabase *Database = NULL; // Application database. + +bool Descent_overrided_intro = false; + +extern bool Game_gauge_do_time_test; +bool Katmai=true; + +char Descent3_temp_directory[_MAX_PATH]; //temp directory to put temp files +// --------------------------------------------------------------------------- +// Descent3: Choke 1 +// Initializes game elements and invokes the MainLoop +// --------------------------------------------------------------------------- +//#define BETA + +#if (defined(OEM) || defined(DEMO)) +void ShowStaticScreen(char *bitmap_filename,bool timed=false,float delay_time=0.0f); +#endif + +extern int CD_inserted; + +char Proxy_server[200] = ""; +short Proxy_port=80; + +void Descent3() +{ + int type; + +#ifdef _DEBUG + type = DEVELOPMENT_VERSION; +#else + type = RELEASE_VERSION; +#endif +#ifdef BETA + type |= BETA_VERSION; +#endif +#ifdef DEMO + type |= DEMO_VERSION; +#endif + + ProgramVersion(type, D3_MAJORVER, D3_MINORVER, D3_BUILD); + + //Catch cfile errors + try + { + //Init a bunch of stuff + InitD3Systems1(false); + + int proxyarg = FindArg("-httpproxy"); + if(proxyarg) + { + strcpy(Proxy_server,GameArgs[proxyarg+1]); + + char *port = strchr(Proxy_server,':'); + if(port) + { + //terminate the hostname + *port = 0; + //Increment to the first character of the port name + port++; + //get the port number + Proxy_port = atoi(port); + } + + } + //Show intro & loading screens if not dedicated server + if (!Dedicated_server) + { + SetScreenMode(SM_CINEMATIC); + + //Show the intro movie + if (! FindArg("-nointro")) { + char intropath[_MAX_PATH*2]; + bool remote_path = (CD_inserted==1) ? true : false; + + #ifdef _DEBUG + if (FindArg("-moviedir")) { + remote_path = true; + } + #endif + + ddio_MakePath(intropath,Base_directory,"movies","dolby1.mv8",NULL); + + if(remote_path || (cfexist(intropath))) + { + char *t = GetMultiCDPath("dolby1.mv8"); + if (t) + PlayMovie(t); + } + + int intro_movie_arg; + char base_intro_movie_name[256]; + char *intro_movie_name; + + intro_movie_arg = FindArg("-intro"); + if(intro_movie_arg>0) + { + // we have an override for the intro movie + // we have to split path because the stupid args system of D3 + // capitalizes everything and the PlayMovie function expects + // a lowercase extension + char extension[16],*p; + p = extension; + ddio_SplitPath(GameArgs[intro_movie_arg+1],NULL,base_intro_movie_name,extension); + while(*p) { *p = tolower(*p); p++;} + strcat(base_intro_movie_name,extension); + + Descent_overrided_intro = true; + + intro_movie_name = base_intro_movie_name; + }else + { + strcpy(base_intro_movie_name,"intro.mve"); + intro_movie_name = base_intro_movie_name; + } + + ddio_MakePath(intropath,Base_directory,"movies",intro_movie_name,NULL); + + if(remote_path || (cfexist(intropath))) + { + char *t = GetMultiCDPath(intro_movie_name); + if (t) + PlayMovie(t); + } + } + + SetScreenMode(SM_MENU); + //Show the intro screen + IntroScreen(); + } + + //Init a bunch more stuff + InitD3Systems2(false); + + #ifdef GAMEGAUGE + if(0) + #else + if(!Game_gauge_do_time_test) + #endif + { + SetFunctionMode(MENU_MODE); + } + else + { + SetFunctionMode(GAMEGAUGE_MODE); + } + +#if defined(MACINTOSH) && defined (GAMERANGER) + if(GRCheckFileForCmd()) { + SetFunctionMode (GAME_MODE); + GRGetWaitingCmd(); + } +#endif + MainLoop(); + + //delete the lock file in the temp directory (as long as it belongs to us) + ddio_DeleteLockFile(Descent3_temp_directory); + + //Save settings to registry + SaveGameSettings(); + } + catch(cfile_error *cfe) { + Error(TXT_D3ERROR1,(cfe->read_write==CFE_READING)?TXT_READING:TXT_WRITING,cfe->file->name,cfe->msg); + } +} + +// --------------------------------------------------------------------------- +// Main Loop: Choke 1 from editor Choke 2 from Game +// If called from the editor, this is the best way to start up the game. +// Otherwise this should be called from the main function. +// --------------------------------------------------------------------------- + +//#ifdef GAMEGAUGE +extern int frames_one_second; +extern int min_one_second; +extern int max_one_second; +extern float gamegauge_start_time; +extern int gamegauge_total_frames; +extern float gamegauge_total_time; +extern short gamegauge_fpslog[GAMEGAUGE_MAX_LOG]; + +//#endif + +void MainLoop() +{ + int exit_game = 0; + + while (!exit_game) + { + if (Dedicated_server && !(Function_mode==GAME_MODE || Function_mode==QUIT_MODE)) + SetFunctionMode (GAME_MODE); + + switch(Function_mode) + { + case QUIT_MODE: + exit_game=1; + break; + case MENU_MODE: + exit_game = MainMenu(); + break; + case RESTORE_GAME_MODE: // do special sequencing for load games. + SetGameState(GAMESTATE_LOADGAME); + PlayGame(); + break; + + case GAME_MODE: + SetGameState(GAMESTATE_NEW); + PlayGame(); // Does what is says. + break; + case LOADDEMO_MODE: + SetGameState(GAMESTATE_LOADDEMO); + PlayGame(); + break; + case CREDITS_MODE: + Credits_Display(); + Function_mode = MENU_MODE; + break; +//#ifdef GAMEGAUGE + case GAMEGAUGE_MODE: + { + int c; + for(c=0;c -1) { + if (!bm_CreateChunkedBitmap(bm_handle, &splash_bm)) + Error("Failed to slice up %s.",bitmap_filename); + + bm_FreeBitmap(bm_handle); + float start_time = timer_GetTime(); + while (1) + { + StartFrame(); + + rend_DrawChunkedBitmap(&splash_bm, 0,0,255); + rend_Flip(); + EndFrame(); + + + Descent->defer(); + + if(!timed) + { + int key = ddio_KeyInKey(); + if (key == KEY_ENTER || key == KEY_SPACEBAR || key == KEY_ESC) + break; + + int x,y,dx,dy; + if (ddio_MouseGetState(&x,&y,&dx,&dy)) + break; + } + else + { + if((timer_GetTime()-start_time)>delay_time) + { + break; + } + + } + } + + bm_DestroyChunkedBitmap(&splash_bm); + } + else { + mprintf((0, "Couldn't load %s.\n",bitmap_filename)); + } + + ui_SetScreenMode(Max_window_w, Max_window_h); +} +#endif + + + +// --------------------------------------------------------------------------- +// Accessor functions +// --------------------------------------------------------------------------- + +void SetFunctionMode(function_mode mode) +{ + Last_function_mode = Function_mode; + Function_mode = mode; +} + + +function_mode GetFunctionMode() +{ + return Function_mode; +} + + +// --------------------------------------------------------------------------- +// The game's defer handler (For Win32, it happens during idle processing) +// --------------------------------------------------------------------------- +extern bool Skip_render_game_frame; +void GameFrame(void); +void D3DeferHandler(bool is_active) +{ + if (is_active) { + // perform any needed io system processing in the background + ddio_Frame(); + } + else { + //JEFF - If the game is in idle loop and we are in multiplayer game + //then process the game frame so we stay alive in the game. + if(Game_mode&GM_MULTI) { + Skip_render_game_frame = true; + GameFrame(); + Skip_render_game_frame = false; + } + } + + //JEFF - Commented out due to new idle processing + nw_DoNetworkIdle(); +} + + +// --------------------------------------------------------------------------- +// this is called when you hit a debug break! +// --------------------------------------------------------------------------- +#ifndef RELEASE +#include "debug.h" +#include "renderer.h" +extern int rend_initted; // from game.cpp + +void D3DebugStopHandler() +{ +// close off all systems for debugging. + if (rend_initted) { + rend_Close(); + } +} + + +void D3DebugResumeHandler() +{ +// reopen all systems for gameplay. + if (rend_initted) + rend_initted = rend_Init (PreferredRenderer, Descent,&Render_preferred_state); + + if (rend_initted!=0) + rend_initted=1; +} + +#endif + +void RenderBlankScreen(void); + +char * GetCDVolume(int cd_num) +{ + char *p = NULL; + +#if defined (MACINTOSH) + char volume_labels[3][_MAX_PATH] = {"","Descent3","Descent3"}; +#elif !defined (OEM) + char volume_labels[3][_MAX_PATH] = {"","D3_1","D3_2"}; +#else + char volume_labels[3][_MAX_PATH] = {"","D3OEM_1",""}; +#endif + + p = ddio_GetCDDrive("D3_DVD"); + + if(!p) + p = ddio_GetCDDrive(volume_labels[cd_num]); + + if(p) + { + //We've got the disk already in the drive! + return p; + } + else + { + //Don't prompt for CD if not a release build + #ifndef RELEASE + //return NULL; + #endif + + //prompt them to enter the disk... + do + { + char message_txt[50]; +#ifdef MACINTOSH + strcpy(message_txt,TXT_CDPROMPT); + message_txt[strlen(message_txt)-2] = '\0'; +#else + sprintf(message_txt,TXT_CDPROMPT,cd_num); +#endif + //We need a background drawn! +#if defined(LINUX) + void (*ui_cb)() = GetUICallback(); +#else + void *ui_cb = GetUICallback(); +#endif + if(ui_cb==NULL) + SetUICallback(RenderBlankScreen); + int res = DoMessageBox(PRODUCT_NAME,message_txt,MSGBOX_OKCANCEL,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + SetUICallback((void (*)(void))ui_cb); + // + if(res == 0) + { + return NULL; + } + p = ddio_GetCDDrive(volume_labels[cd_num]); + if(p && *p) + return p; + }while(!(p && *p)); + + return NULL; + } + +} + +typedef struct file_vols +{ + char file[_MAX_PATH]; + char localpath[_MAX_PATH*2]; + int volume; + bool localized; +}file_vols; + +extern int CD_inserted; + +//Localization_GetLanguage(); + + +char Oem_language_dirs[5][10] = +{ + "English", + "German", + "Spanish", + "Italian", + "French" +}; + + +file_vols file_volumes[] = +{ + //Filename, directory it might be installed on the hard drive, CD number to look for it + {"d3.mn3","missions",1,false}, + {"d3_2.mn3","missions",2,false}, + {"level1.mve","movies",1,true}, + {"level5.mve","movies",2,true}, + {"end.mve","movies",2,true}, + {"intro.mve","movies",1,true}, +#ifdef MACINTOSH + {"ppics.hog","missions",1,true}, + {"training.mn3","missions",1,true}, +#else + {"dolby1.mv8","movies",1,true}, +#endif + {"d3voice1.hog","missions",1,true}, + {"d3voice2.hog","missions",2,true} +}; + +int num_cd_files = sizeof(file_volumes)/sizeof(file_vols); + +//This function figures out whether or not a file needs to be loaded off of +//CD or off of the local drive. If it needs to come from a CD, it figures out +//which CD and prompts the user to enter that CD. If they hit cancel, it +//returns NULL. +char * GetMultiCDPath(char *file) +{ + static char filepath[_MAX_PATH*2]; + static char fullpath[_MAX_PATH*2]; + int volume = 0; + int i; + + if(file==NULL) + return NULL; + if(*file=='\0') + return NULL; + + // see if there is a command line override for the name of the intro movie + int intro_movie_arg = FindArg("-intro"); + char temp_filename[256]; + + //Clear out any old path + //memset(filepath,0,_MAX_PATH*2); + filepath[0] = NULL; + + for(i=0;i0 && !stricmp(vol_filename,"intro.mve")) + { + // we have to override this intro movie + char extension[16],*p; + p = extension; + ddio_SplitPath(GameArgs[intro_movie_arg+1],NULL,temp_filename,extension); + while(*p) { *p = tolower(*p); p++;} + strcat(temp_filename,extension); + vol_filename = temp_filename; + } + + if(strcmpi(vol_filename,file)==0) + { + volume = file_volumes[i].volume; + ddio_MakePath(fullpath,LocalD3Dir,file_volumes[i].localpath,file,NULL); + //See if the file is in the local dir already. + if(cfexist(fullpath)) + { + return fullpath; + } + #ifdef _DEBUG + else if (strcmpi(file_volumes[i].localpath, "movies")==0) { + // if one specified a directory where the movies are located. + int arg = FindArg("-moviedir"); + if (arg) { + ddio_MakePath(fullpath, GameArgs[arg+1], file, NULL); + return fullpath; + } + } + #endif + break; + } + } + //This is a file we don't know about + if(i==num_cd_files) + return file; + + if(volume) + { + char *p = GetCDVolume(volume); + if(p) + { + //If it's DVD, we need to get the proper files for the language + if( (CD_inserted==3)&&(file_volumes[i].localized) ) + { + ddio_MakePath(filepath,p,file_volumes[i].localpath,Oem_language_dirs[Localization_GetLanguage()],file,NULL); + } + else + { + ddio_MakePath(filepath,p,file_volumes[i].localpath,file,NULL); + } + //strcpy(filepath,p); + //strcat(filepath,file); + return filepath; + } + else + { + return NULL; + } + } + + return NULL; +} diff --git a/Descent3/descent.h b/Descent3/descent.h new file mode 100644 index 000000000..fad43e0e4 --- /dev/null +++ b/Descent3/descent.h @@ -0,0 +1,219 @@ +/* + * $Logfile: /DescentIII/main/descent.h $ + * $Revision: 33 $ + * $Date: 11/04/99 12:35a $ + * $Author: Chris $ + * + * Descent 3 header file + * + * $Log: /DescentIII/main/descent.h $ + * + * 33 11/04/99 12:35a Chris + * Added support for Merc + * + * 32 10/21/99 3:27p Matt + * Mac merge + * + * 31 7/20/99 12:59p Jason + * added auto katmai support + * + * 30 5/12/99 2:24p Jeff + * Descent3 now has a setable temp directory for all temp files + * + * 29 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 28 3/31/99 3:04p Jeff + * added _real_ temporary credits system... + * + * 27 3/19/99 4:30p Kevin + * multi-cd support + * + * 26 2/16/99 12:36a Kevin + * Fixes for release builds of OEM V3 and KAtmai + * + * 25 2/03/99 4:40p Kevin + * Added new port number for OEM + * + * 24 1/15/99 7:16p Kevin + * Added GameGauge Configuration & code + * + * 23 12/23/98 6:38p Kevin + * All UDP data (except gamespy) now uses one (registered) port number + * + * 22 10/18/98 8:55p Matt + * Added a constant with the name of the program. + * + * 21 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 20 10/08/98 7:26p Samir + * changed the prototype for the defer handler callback. + * + * 19 10/08/98 12:00p Kevin + * Demo system work + * + * 18 9/22/98 3:54p Samir + * no include slew in release version. + * + * 17 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 16 8/20/98 10:51a Samir + * added the RESTORE_GAME_MODE function mode,. + * + * 15 8/15/98 5:16p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 14 7/02/98 12:57p Jason + * upped FOV + * + * 13 4/22/98 3:24p Jason + * made FOV be 65 degrees by default + * + * 12 4/14/98 7:56p Matt + * Moved MSN_NAMELEN from mission.h to descent,h, so multi.h didn't need + * to include mission.h. + * + * 11 3/23/98 8:06p Samir + * change return type of D3DeferHandler to match changes in library. + * + * 10 3/19/98 11:27a Samir + * Better error checking. + * + * 9 3/10/98 5:16p Samir + * Got debug callbacks working when you hit an Int3. + * + * 8 2/25/98 2:05p Jason + * did FOV and object visibility changes + * + * 7 10/16/97 2:30p Samir + * Added defer handler. + * + * 6 9/17/97 10:56a Matt + * Deleted Room_engine variable + * + * 5 9/16/97 1:04p Samir + * Added delay function. + * + * 4 7/24/97 6:35p Matt + * Created symbolic constant for default zoom, and used it every place + * that specifies zoom + * + * 3 7/24/97 2:53p Matt + * Added SourceSafe header and missing include + * + * $NoKeywords: $ + */ + +#ifndef _DESCENT_H +#define _DESCENT_H + +#include +#include "application.h" + +//The name of this product +#ifdef DEMO +#define PRODUCT_NAME "Descent 3 Demo" +#else +#define PRODUCT_NAME "Descent 3" +#endif + +//This is the port number assigned to "descent3" by the IANA (Internet Assigned Numbers Authority) +//Don't arbitrarily change this number! +#ifdef OEM +//The port used for the OEM version isn't a legitimate IANA port number +#define D3_DEFAULT_PORT 2092 +#else +#define D3_DEFAULT_PORT 2092 +#endif + +class grScreen; +class oeApplication; +class oeAppDatabase; +class grViewport; + +// --------------------------------------------------------------------------- +// Constants and Types + +typedef enum function_mode { + INIT_MODE, GAME_MODE, RESTORE_GAME_MODE, EDITOR_MODE, EDITOR_GAME_MODE, MENU_MODE,QUIT_MODE,LOADDEMO_MODE, GAMEGAUGE_MODE, CREDITS_MODE +} function_mode; + +extern bool Descent_overrided_intro; + +//Maximum samples to save for gamegauge graph info +#define GAMEGAUGE_MAX_LOG 1000 + +// This is the default FOV +#define D3_DEFAULT_FOV 72.0 +//This is the default zoom factor to be used for the game 3D view. +#define D3_DEFAULT_ZOOM 0.726f + +//How long the a mission name can be +#define MSN_NAMELEN 32 + +// The "root" directory of the D3 file tree +extern char Base_directory[]; + + +// --------------------------------------------------------------------------- +// Globals + +extern grScreen *Game_screen; // The Descent 3 screen. +extern oeApplication *Descent; // The Descent object +extern oeAppDatabase *Database; // The Database +extern char Descent3_temp_directory[_MAX_PATH]; //temp directory to put temp files +extern bool Katmai; // whether or not katmai is detected +// --------------------------------------------------------------------------- +// Functions + +#ifdef EDITOR +void WinMainInitEditor(unsigned hwnd, unsigned hinst); +#endif + +// Runs Descent III +void Descent3(); + +// Runs the game, or editor +void MainLoop(); + +// the defer handler +void D3DeferHandler(bool is_active); + +// Set and retrieve the current function mode of Descent 3 +void SetFunctionMode(function_mode mode); +function_mode GetFunctionMode(); + +// these functions create viewports from the game screen in a 'nice' C way +void CreateGameViewport(grViewport **vp); +void DestroyGameViewport(grViewport *vp); + +//This function figures out whether or not a file needs to be loaded off of +//CD or off of the local drive. If it needs to come from a CD, it figures out +//which CD and prompts the user to enter that CD. If they hit cancel, it +//returns NULL. +char * GetMultiCDPath(char *file); +char * GetCDVolume(int cd_num); + +inline void CREATE_VIEWPORT(grViewport **vp) { + CreateGameViewport(vp); +} + +inline void DESTROY_VIEWPORT(grViewport *vp) { + DestroyGameViewport(vp); +} + +inline void DELAY(float secs) { + Descent->delay(secs); +} + +#ifndef RELEASE +// this is called when you hit a debug break! +void D3DebugStopHandler(); +void D3DebugResumeHandler(); +#endif + + +#endif diff --git a/Descent3/difficulty.cpp b/Descent3/difficulty.cpp new file mode 100644 index 000000000..da389de8a --- /dev/null +++ b/Descent3/difficulty.cpp @@ -0,0 +1,22 @@ +#include "difficulty.h" + +// Notes: +// +// 1. Don't mess with target leading (other than algorithm type) as it will make robots +// turn strangely (or at least never scale it up beyond 1.0) + +float Diff_ai_dodge_percent[5] = {0.04f, 0.10f, 1.00f, 1.00f, 1.50f}; +float Diff_ai_dodge_speed[5] = {0.20f, 0.30f, 1.00f, 1.25f, 1.50f}; +float Diff_ai_speed[5] = {0.70f, 0.80f, 1.00f, 1.10f, 1.20f}; +float Diff_ai_rotspeed[5] = {0.70f, 0.80f, 1.00f, 1.10f, 1.20f}; +float Diff_ai_circle_dist[5] = {1.10f, 1.00f, 1.00f, 1.00f, 1.00f}; +float Diff_ai_vis_dist[5] = {0.80f, 0.90f, 1.00f, 1.10f, 1.20f}; +float Diff_player_damage[5] = {0.30f, 0.60f, 1.00f, 1.50f, 2.00f}; +float Diff_ai_weapon_speed[5] = {0.50f, 0.75f, 1.00f, 1.20f, 1.40f}; +float Diff_homing_strength[5] = {0.20f, 0.70f, 1.00f, 1.20f, 1.40f}; +float Diff_robot_damage[5] = {2.75f, 1.50f, 1.00f, 0.80f, 0.60f}; +float Diff_general_scalar[5] = {2.50f, 1.75f, 1.00f, 0.75f, 0.50f}; +float Diff_general_inv_scalar[5] = {0.50f, 0.75f, 1.00f, 1.75f, 2.50f}; +float Diff_shield_energy_scalar[5] = {2.25f, 1.5f, 1.0f, 0.75f, 0.5f}; +float Diff_ai_turret_speed[5] = {0.6f, 0.7f, 1.0f, 1.0f, 1.0f}; +float Diff_ai_min_fire_spread[5] = {.30f, .15f, 0.0f, 0.0f, 0.0f}; \ No newline at end of file diff --git a/Descent3/difficulty.h b/Descent3/difficulty.h new file mode 100644 index 000000000..f33d59da6 --- /dev/null +++ b/Descent3/difficulty.h @@ -0,0 +1,29 @@ +#ifndef _DIFFICULTY_H_ +#define _DIFFICULTY_H_ + +#include "game.h" +#include "pilot.h" +#include "difficulty_external.h" + +extern ubyte ingame_difficulty; + +//DAJ static global instead of subroutine call for speed +#define DIFF_LEVEL (((Game_mode & GM_MULTI))?Netgame.difficulty:ingame_difficulty) +//#define DIFF_LEVEL (((Game_mode & GM_MULTI))?Netgame.difficulty:dCurrentPilotDifficulty()) + +extern float Diff_ai_dodge_percent[5]; // +extern float Diff_ai_dodge_speed[5]; // +extern float Diff_ai_speed[5]; // +extern float Diff_ai_rotspeed[5]; // +extern float Diff_ai_circle_dist[5]; // +extern float Diff_ai_vis_dist[5]; // +extern float Diff_player_damage[5]; // +extern float Diff_ai_weapon_speed[5]; // +extern float Diff_homing_strength[5]; // +extern float Diff_robot_damage[5]; // +extern float Diff_general_scalar[5]; // Trainee = 2.50f HotShot = 1.0f +extern float Diff_general_inv_scalar[5];// Trainee = 0.50f HotShot = 1.0f +extern float Diff_shield_energy_scalar[5]; +extern float Diff_ai_turret_speed[5]; +extern float Diff_ai_min_fire_spread[5]; +#endif \ No newline at end of file diff --git a/Descent3/difficulty_external.h b/Descent3/difficulty_external.h new file mode 100644 index 000000000..28b454e3a --- /dev/null +++ b/Descent3/difficulty_external.h @@ -0,0 +1,13 @@ +#ifndef DIFFICULTY_EXTERNAL_H_ +#define DIFFICULTY_EXTERNAL_H_ + +#define MAX_DIFFICULTY_LEVELS 5 // Number of difficulty levels. + +//skill difficulties +#define DIFFICULTY_TRAINEE 0 +#define DIFFICULTY_ROOKIE 1 +#define DIFFICULTY_HOTSHOT 2 +#define DIFFICULTY_ACE 3 +#define DIFFICULTY_INSANE 4 + +#endif \ No newline at end of file diff --git a/Descent3/door.cpp b/Descent3/door.cpp new file mode 100644 index 000000000..b7d74c67a --- /dev/null +++ b/Descent3/door.cpp @@ -0,0 +1,263 @@ +/* + * $Logfile: /DescentIII/main/door.cpp $ + * $Revision: 12 $ + * $Date: 2/04/99 2:05p $ + * $Author: Matt $ + * + * doorway and side info. + * + * $Log: /DescentIII/main/door.cpp $ + * + * 12 2/04/99 2:05p Matt + * Added blastable doors + * + * 11 1/29/99 12:47p Matt + * Rewrote the doorway system + * + * 10 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 9 4/06/98 4:53p Jason + * got pageable polymodels working correctly with editor + * + * 8 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 7 2/02/98 7:07p Matt + * Added support for doors that can be seen through even when closed + * + * 6 10/06/97 1:01p Jason + * made doors work with scripts (sort of) + * + * 5 9/17/97 11:16a Matt + * Ripped out segment code + * + * 4 9/12/97 5:38p Jason + * got doors working + * + * 3 9/09/97 7:02p Jason + * don't reload the models during the remap phase + * + * 2 8/07/97 12:42p Jason + * fixed potential memory problem + * + * 18 5/23/97 5:46p Matt + * Added SetMatrixBasedOnSide() functionality to ExtractMatrixFromSeg(). + * + * 17 4/02/97 5:21p Jason + * added the ability for pages to free the models that they point to + * + * 16 3/18/97 5:22p Jason + * took out dumb mprintf + * + * 15 3/14/97 5:54p Jason + * made positional interpolation work with 3d doors + * + * 14 3/13/97 6:23p Jason + * fixed a door bug + * + * 13 3/13/97 6:13p Jason + * got poly doors working + * + * 12 3/12/97 2:15p Samir + * Removed global references to doorways. + * + * 11 3/12/97 12:40p Samir + * Adding doorway open code + * + * 10 3/10/97 4:07p Samir + * We now almost nevert render the texture in a door segment + * + * 9 3/07/97 2:19p Jason + * added DrawDoorSegment function + * + * 8 3/05/97 3:10p Jason + * added more door functionality + * + * 7 3/05/97 3:03p Jason + * added blinking messages + * + * 6 3/05/97 2:27p Samir + * + * 5 3/05/97 12:16p Jason + * added code to support our new 3d doors + * + * 4 2/27/97 5:09p Samir + * Key flags defined in door code. + * + * 3 2/26/97 4:25p Samir + * Call GetDoorFlags to get door info. + * + * 2 2/25/97 4:35p Samir + * Added SIDE_FLAGS macro. + * + * $NoKeywords: $ + */ + +#include + +#include "door.h" +#include "pserror.h" +#include "polymodel.h" +#include "game.h" +#include "doorway.h" + + +// --------------------------------------------------------------------------- +// Globals + +door Doors[MAX_DOORS]; // door info. +int Num_doors=0; + + +// --------------------------------------------------------------------------- +// Prototypes + + +// --------------------------------------------------------------------------- +// Functions + +// Sets all doors to unused +void InitDoors() +{ + for (int i=0;i0); + + Doors[n].used=0; + Doors[n].name[0]=0; + Num_doors--; +} + +// Gets next door from n that has actually been alloced +int GetNextDoor (int n) +{ + int i; + + ASSERT (n>=0 && n=0 && n=0;i--) + { + if (Doors[i].used) + return i; + } + for (i=MAX_DOORS-1;i>n;i--) + { + if (Doors[i].used) + return i; + } + + // this is the only one + return n; + +} +// Searches thru all doors for a specific name, returns -1 if not found +// or index of door with name +int FindDoorName (char *name) +{ + int i; + + ASSERT (name!=NULL); + + for (i=0;i0); + + return (Doors[handle].model_handle); +} + +void RemapDoors () +{ + // Now, if any doors are polygon models and those models don't have correct + // textures, attempt to reload the model texture list + + for (int i=0;iflags & RF_DOOR); + ASSERT(rp->doorway_data != NULL); + + rp->doorway_data->activenum = -1; + + for (int i = adway; i < (Num_active_doorways-1);i++) + { + Active_doorways[i] = Active_doorways[i+1]; + Rooms[Active_doorways[i]].doorway_data->activenum--; + } + + Num_active_doorways--; + + mprintf((0,"ActiveDoorways = %d\n",Num_active_doorways)); +} + +void AddActiveDoorway(int roomnum) +{ + room *rp = &Rooms[roomnum]; + ASSERT(rp->flags & RF_DOOR); + + doorway *dp = rp->doorway_data; + ASSERT(dp != NULL); + + if (dp->activenum != -1) { //already active + ASSERT(dp->activenum < Num_active_doorways); + ASSERT(Active_doorways[dp->activenum] == roomnum); + } + else { //make new active + ASSERT(Num_active_doorways < MAX_ACTIVE_DOORWAYS); + Active_doorways[Num_active_doorways] = roomnum; + dp->activenum = Num_active_doorways++; + } + + mprintf((0,"ActiveDoorways = %d\n",Num_active_doorways)); +} + +// Given an object handle, returns the doorway number +doorway *GetDoorwayFromObject(int door_obj_handle) +{ + object *objp = ObjGet(door_obj_handle); + + if (! objp) + return NULL; + + ASSERT(objp->type == OBJ_DOOR); + + room *rp = &Rooms[objp->roomnum]; + + ASSERT(rp->flags & RF_DOOR); + ASSERT(rp->doorway_data != NULL); + + return rp->doorway_data; +} + +//Plays a sound for this door +void DoorwayPlaySound(object *objp) +{ + room *rp = &Rooms[objp->roomnum]; + ASSERT(rp->flags & RF_DOOR); + + doorway *dp = rp->doorway_data; + ASSERT(dp != NULL); + + door *door = &Doors[dp->doornum]; + + //Stop sound if one playing + if (dp->sound_handle != -1) { + Sound_system.StopSoundImmediate(dp->sound_handle); + dp->sound_handle = -1; + } + + //Play new sound, if this door has one + if ((dp->state == DOORWAY_OPENING) || (dp->state == DOORWAY_OPENING_AUTO)) { + if (door->open_sound != -1) { + float offset = dp->position * door->total_open_time; + dp->sound_handle = Sound_system.Play3dSound(Doors[dp->doornum].open_sound,SND_PRIORITY_HIGH,objp,1.0,0,offset); + } + } + else { + ASSERT(dp->state == DOORWAY_CLOSING); + if (door->close_sound != -1) { + float offset = (1.0 - dp->position) * door->total_close_time; + dp->sound_handle = Sound_system.Play3dSound(Doors[dp->doornum].close_sound,SND_PRIORITY_HIGH,objp,1.0,0,offset); + } + } +} + +//Opens a door and, if it has the auto flag set, closes it +//Does not check to see if the door is openable -- the caller must do that. +void DoorwayActivate(int door_obj_handle) +{ + object *objp; + doorway *dp; + door *door; + + objp = ObjGet(door_obj_handle); + + if (! objp) + return; + + ASSERT(objp->type == OBJ_DOOR); + + dp = GetDoorwayFromObject(door_obj_handle); + if (! dp) + return; + + //If already blasted, bail + if (dp->flags & DF_BLASTED) + return; + + //Get pointer to door + door = &Doors[dp->doornum]; + + //If already open or opening or waiting, do nothing + if ((dp->state == DOORWAY_OPENING) || (dp->state == DOORWAY_WAITING) || (dp->state == DOORWAY_OPENING_AUTO)) + return; + + //Store new desired position + dp->dest_pos = 1.0; + + //If not already active, create active entry + AddActiveDoorway(objp->roomnum); + + //Set new state + dp->state = (dp->flags & DF_AUTO) ? DOORWAY_OPENING_AUTO : DOORWAY_OPENING; + + //Play sound + DoorwayPlaySound(objp); + + //Send notification event + tOSIRISEventInfo ei; + Osiris_CallEvent(objp,EVT_DOOR_ACTIVATE,&ei); +} + +//Sets the current position of the door. 0.0 = totally closed, 1.0 = totally open +//Does not check to see if the door is openable -- the caller must do that. +void DoorwaySetPosition(int door_obj_handle,float pos) +{ + object *objp; + doorway *dp; + door *door; + + objp = ObjGet(door_obj_handle); + + if (! objp) + return; + + ASSERT(objp->type == OBJ_DOOR); + + //Get pointer to doorway + dp = GetDoorwayFromObject(door_obj_handle); + if (! dp) + return; + + //Get pointer to door + door = &Doors[dp->doornum]; + + //Set new dest position + dp->dest_pos = pos; + + //Check if already there + if (dp->position == pos) + return; + + //If not already active, create active entry + AddActiveDoorway(objp->roomnum); + + //Set new state & play sound + if (dp->dest_pos > dp->position) + dp->state = DOORWAY_OPENING; + else + dp->state = DOORWAY_CLOSING; + + //Play sound + DoorwayPlaySound(objp); +} + +void DoorwayStop(int door_obj_handle) +{ + //Get pointer to doorway + doorway *dp = GetDoorwayFromObject(door_obj_handle); + if (! dp) + return; + + //Set the door as stopped + dp->dest_pos = dp->position; + dp->state = DOORWAY_STOPPED; + + //Stop the sound if one is playing + if (dp->sound_handle != -1) { + Sound_system.StopSoundImmediate(dp->sound_handle); + dp->sound_handle = -1; + } + + //Remove from the active doorway list + if (dp->activenum != -1) + RemoveActiveDoorway(dp->activenum); +} + +//Called when a door is blown up +void DoorwayDestroy(object *objp) +{ + room *rp = &Rooms[objp->roomnum]; + doorway *dp = rp->doorway_data; + + ASSERT(rp->flags & RF_DOOR); + ASSERT(dp); + + dp->flags |= DF_BLASTED; + dp->position = 1.0; + dp->state = DOORWAY_STOPPED; + + //Remove from the active doorway list + if (dp->activenum != -1) + RemoveActiveDoorway(dp->activenum); +} + + +//Stop all doors +void DoorwayDeactivateAll() +{ + int r; + room *rp; + + //Go through all rooms and deactivate doors + for (r=0,rp=Rooms;r<=Highest_room_index;r++) { + if (rp->used && (rp->flags & RF_DOOR)) { + if (rp->doorway_data->state != DOORWAY_STOPPED) { + doorway *dp = rp->doorway_data; + ASSERT(dp != NULL); + dp->position = dp->dest_pos; + dp->activenum = -1; + dp->state = DOORWAY_STOPPED; + if (dp->sound_handle != -1) { + Sound_system.StopSoundImmediate(dp->sound_handle); + dp->sound_handle = -1; + } + DoorwayUpdateAnimation(rp); + } + } + } + + Num_active_doorways = 0; +} + +// Sets the corresponding door objects animation frame +void DoorwayUpdateAnimation(room *rp) +{ + doorway *dway; + poly_model *pm; + int doornum; + float norm; + + dway = rp->doorway_data; + + object *objp = GetDoorObject(rp); + if (!objp) + return; + + doornum = objp->id; + pm=&Poly_models[GetDoorImage(doornum)]; + + norm = DoorwayPosition(rp); + + if (pm->flags & PMF_TIMED) + objp->rtype.pobj_info.anim_frame=pm->frame_max*norm; + else + objp->rtype.pobj_info.anim_frame=pm->max_keys*norm; +} + +// Update all doorways currently active in the mine +void DoorwayDoFrame() +{ + int i_doorway; + + for (i_doorway = 0; i_doorway < Num_active_doorways; i_doorway++) + { + int roomnum = Active_doorways[i_doorway]; + room *rp = &Rooms[roomnum]; + ASSERT(rp->flags & RF_DOOR); + doorway *dway = rp->doorway_data; + ASSERT(dway != NULL); + door *door = &Doors[dway->doornum]; + float delta; //movement delta + + switch (dway->state) + { + case DOORWAY_OPENING: + case DOORWAY_OPENING_AUTO: // doorway is opening + delta = Frametime / door->total_open_time; + + dway->position += delta; + + if (dway->position >= dway->dest_pos) { + dway->position = dway->dest_pos; + + if (dway->state == DOORWAY_OPENING_AUTO) { + dway->state = DOORWAY_WAITING; + dway->dest_pos = door->total_time_open; + } + else { // non automatic doors will stay open, hence are no longer active + dway->state = DOORWAY_STOPPED; + RemoveActiveDoorway(i_doorway); + } + } + break; + + case DOORWAY_CLOSING: // doorway is closing + delta = Frametime / door->total_close_time; + + dway->position -= delta; + + if (dway->position <= dway->dest_pos) { + dway->position = dway->dest_pos; + + dway->state = DOORWAY_STOPPED; + RemoveActiveDoorway(i_doorway); + + //Send notification event + object *objp = GetDoorObject(rp); + ASSERT(objp != NULL); + tOSIRISEventInfo ei; + Osiris_CallEvent(objp,EVT_DOOR_CLOSE,&ei); + } + break; + + case DOORWAY_WAITING: // doorway is in wait state + + dway->dest_pos -= Frametime; + + if (dway->dest_pos <= 0.0) { + + object *door_objp = GetDoorObject(rp); + ASSERT(door_objp != NULL); + + //Time to start closing. See if there's anything in the way + if (! ((door_objp->next != -1) || (door_objp->prev != -1))) { + + dway->dest_pos = 0.0; + dway->state = DOORWAY_CLOSING; + + //Play sound + DoorwayPlaySound(door_objp); + } + } + break; + + default: + Int3(); // these states shouldn't happen if door is active + } + + //Update the animation state + DoorwayUpdateAnimation(rp); + } +} + +//returns a pointer to the door object for the specified doorway +object *GetDoorObject(room *rp) +{ + ASSERT(rp->flags & RF_DOOR); + + for (int objnum=rp->objects;(objnum!=-1);objnum=Objects[objnum].next) + if (Objects[objnum].type==OBJ_DOOR) + return &Objects[objnum]; + + return NULL; +} + +// Returns true if the doorway is locked, else false +bool DoorwayLocked(int door_obj_handle) +{ + doorway *dp = GetDoorwayFromObject(door_obj_handle); + + if (!dp) + return 0; + + return ((dp->flags & DF_LOCKED) != 0); +} + +// Returns true if the doorway is locked, else false +bool DoorwayLocked(room *rp) +{ + ASSERT(rp->flags & RF_DOOR); + + doorway *dp = rp->doorway_data; + ASSERT(dp != NULL); + + return ((dp->flags & DF_LOCKED) != 0); +} + +// Returns true if the doorway is openable by the specified object, else false +bool DoorwayOpenable(int door_obj_handle,int opener_handle) +{ + int keys; + + //Get pointer to doorway + doorway *dp = GetDoorwayFromObject(door_obj_handle); + if (!dp) + return 0; + + //If locked, no one can open it + if (dp->flags & DF_LOCKED) + return 0; + + //If no keys needed, anyone can open + if (dp->keys_needed == 0) + return 1; + + object *opener = ObjGet(opener_handle); + + //If invalid object, can't open + if (! opener) + return 0; + + //If a weapon, get the parent + if (opener->type == OBJ_WEAPON) { + opener = ObjGetUltimateParent(opener); + ASSERT(opener && (opener->type != OBJ_WEAPON)); + } + + //Get the opener's keys + if (opener->type == OBJ_PLAYER) + keys = Players[opener->id].keys; + else if ((opener->type == OBJ_ROBOT) || ((opener->type == OBJ_BUILDING) && opener->ai_info)) + keys = Global_keys; + else + return 0; //If not a robot or a player, cannot open keyed doors + + //Door is openenable if have proper keys + if (dp->flags & DF_KEY_ONLY_ONE) + return ((keys & dp->keys_needed) != 0); + else + return ((keys & dp->keys_needed) == dp->keys_needed); +} + + +//Returns the current state of the specified door +int DoorwayState(int door_obj_handle) +{ + doorway *dp = GetDoorwayFromObject(door_obj_handle); + + if (!dp) + return DOORWAY_STOPPED; + + return dp->state; +} + +// Locks or unlocks a given doorway +void DoorwayLockUnlock(int door_obj_handle,bool state) +{ + doorway *dp = GetDoorwayFromObject(door_obj_handle); + + if (! dp) + return; + + if (state) + dp->flags |= DF_LOCKED; + else + dp->flags &= ~DF_LOCKED; +} + +//Returns the current position of the door. 0.0 = totally closed, 1.0 = totally open +float DoorwayPosition(room *rp) +{ + ASSERT(rp->flags & RF_DOOR); + ASSERT(rp->doorway_data != NULL); + + return rp->doorway_data->position; +} + +//Returns the current position of the door. 0.0 = totally closed, 1.0 = totally open +float DoorwayPosition(int door_obj_handle) +{ + doorway *dp = GetDoorwayFromObject(door_obj_handle); + + if (!dp) + return 0.0; + + return dp->position; +} + +//Called after loading a saved game to rebuild the active list +void DoorwayRebuildActiveList() +{ + int r; + room *rp; + + //Reset active doorways count + Num_active_doorways = 0; + + //Go through all rooms and look for active doors + for (r=0,rp=Rooms;r<=Highest_room_index;r++) { + if (rp->used && (rp->flags & RF_DOOR)) { + ASSERT(rp->doorway_data != NULL); + if (rp->doorway_data->state != DOORWAY_STOPPED) + AddActiveDoorway(r); + } + } +} + +// Adds a doorway to the specified room +// Returns a pointer to the doorway struct +doorway *DoorwayAdd(room *rp,int doornum) +{ + ASSERT(rp->doorway_data == NULL); + + rp->flags |= RF_DOOR; + + doorway *dp = rp->doorway_data = (doorway *) mem_malloc(sizeof(*rp->doorway_data)); + + //Initialize + dp->doornum = doornum; + dp->dest_pos = dp->position = 0.0; + dp->state = DOORWAY_STOPPED; + dp->flags = DF_AUTO; + dp->keys_needed = 0; + dp->activenum = -1; + dp->sound_handle = -1; + + return dp; +} diff --git a/Descent3/doorway.h b/Descent3/doorway.h new file mode 100644 index 000000000..71001d8cb --- /dev/null +++ b/Descent3/doorway.h @@ -0,0 +1,199 @@ +/* + * $Logfile: /DescentIII/main/doorway.h $ + * $Revision: 15 $ + * $Date: 5/05/99 12:58a $ + * $Author: Matt $ + * + * Doorway structures + * + * $Log: /DescentIII/main/doorway.h $ + * + * 15 5/05/99 12:58a Matt + * Added doorway flag to tell the Guide-Bot to ignore the locked state of + * the door. + * + * 14 4/23/99 8:28p Kevin + * trying to get doors working in save/load games + * + * 13 4/20/99 8:35p Matt + * Changed ActivateDoorway() to not exit if a door is locked. It's the + * caller's responsibility to check if the door is openable (if he cares) + * before activating. + * + * 12 4/20/99 6:55p Matt + * Added code to keep a bitmask of all keys held by all players, and to + * allow a robot to open a door if any player has the key(s) for that + * door. + * + * 11 2/06/99 10:03p Matt + * Added keys system + * + * 10 2/04/99 2:05p Matt + * Added blastable doors + * + * 9 1/29/99 12:47p Matt + * Rewrote the doorway system + * + * 8 9/08/98 12:06p Jason + * added automatic reinitting of doors when rooms get deleted + * + * 7 10/06/97 1:03p Samir + * Took out script name. Doorways are objects, and if there is a custom + * script for a doorway, we look at it's object. + * + * 6 10/06/97 1:01p Jason + * made doors work with scripts (sort of) + * + * 5 10/03/97 12:51p Jason + * added GetDoorwayNumFromObject function + * + * 4 10/03/97 12:24p Jason + * added new keys for doorways + * + * 3 9/29/97 12:09p Jason + * added functionality to doorway system + * + * 2 9/12/97 5:38p Jason + * got doors working + * + * 3 3/14/97 12:51p Samir + * Added code to clear active state of doorways. + * + * 2 3/13/97 11:58a Samir + * Added stuff for active doorway and opening/closing + * + * 1 3/12/97 2:08p Samir + * Moved structures and prototypes from door.h + * + * $NoKeywords: $ + */ + +#ifndef DOORWAY_H +#define DOORWAY_H + +#include "pstypes.h" + +// IMPORTANT!!!!!!!!!!! +// "Doors" refers to a predefined door that is in memory +// "Doorways" are specific doors that are in the mine +// So, there can be several Doorways that all point to the same Door +// Get it? If not, talk to Samir or Jason + +// doorway state +#define DOORWAY_STOPPED 0 // door is not moving +#define DOORWAY_OPENING 1 // door is opening +#define DOORWAY_CLOSING 2 // door is closing +#define DOORWAY_WAITING 3 // door is waiting to be closed +#define DOORWAY_OPENING_AUTO 4 // door is opening and will automatically close + +// doorway flags +#define DF_BLASTED 1 // it's been blasted away +#define DF_AUTO 2 // doorway closes after time. +#define DF_LOCKED 4 // doorway can't open for now +#define DF_KEY_ONLY_ONE 8 // only one key is needed to open (not all keys) +#define DF_GB_IGNORE_LOCKED 16 // the Guide-bot ignores the locked state of this door + +// keymasks +#define KF_KEY1 1 // Each key is a bit in the key_mask set in the door/object +#define KF_KEY2 2 +#define KF_KEY3 4 +#define KF_KEY4 8 + +#define MAX_ACTIVE_DOORWAYS 30 + +extern int Num_active_doorways; // number of active doors in game +extern int Active_doorways[MAX_ACTIVE_DOORWAYS]; + +//A doorway (room) in the mine +typedef struct doorway { + int doornum; // door type of this doorway + ubyte state; // current state of doorway + ubyte flags; // flags associated with a doorway + ubyte keys_needed; // used by trigger system. these bits need to be set to activate door + sbyte activenum; // index into active doorways array, or -1 if not active + float position; // current position of door + float dest_pos; // destination position + int sound_handle; // handle of last sound played +} doorway; + +// Macros + +//returns the bitflag for the given key number +#define KEY_FLAG(keynum) (1 << (keynum-1)) + +// Variables + +//This is a mask of all keys held by all players. Robots use this to determine if a door is openable. +extern int Global_keys; + +// +// Editor Functions +// + +//Define a couple types +struct room; +struct object; + +// Adds a doorway to the specified room +// Returns a pointer to the doorway struct +doorway *DoorwayAdd(room *rp,int doornum); + +//Updates the animation when the door position is changed +void DoorwayUpdateAnimation(room *rp); + +// +// General Functions +// + +// executes all active doorways in the game +void DoorwayDoFrame(); + +// clears the active state of all doorways that are active back to closed +void DoorwayDeactivateAll(); + +//Opens a door and, if it has the auto flag set, closes it +//Does not check to see if the door is openable -- the caller must do that. +void DoorwayActivate(int door_obj_handle); + +//Stops the doorway if it's moving +void DoorwayStop(int door_obj_handle); + +// Locks a given doorway +void DoorwayLockUnlock(int door_obj_handle,bool state); + +//Sets the current position of the door. 0.0 = totally closed, 1.0 = totally open +//Does not check to see if the door is openable -- the caller must do that. +void DoorwaySetPosition(int door_obj_handle,float pos); + +//Called after loading a saved game to rebuild the active list +void DoorwayRebuildActiveList(); + +//Called when a door is blown up +void DoorwayDestroy(object *objp); + +// +// Query Functions +// + +// Returns true if the doorway is locked, else false +bool DoorwayLocked(int door_obj_handle); + +// Returns true if the doorway is locked, else false +bool DoorwayLocked(room *rp); + +// Returns true if the doorway is openable by the specified player, else false +bool DoorwayOpenable(int door_obj_handle,int opener_handle); + +//Returns the current state of the specified door +int DoorwayState(int door_obj_handle); + +//Returns the current position of the door. 0.0 = totally closed, 1.0 = totally open +float DoorwayPosition(room *rp); + +//Returns the current position of the door. 0.0 = totally closed, 1.0 = totally open +float DoorwayPosition(int door_obj_handle); + +//Make old name work +#define DoorwayGetPosition DoorwayPosition + +#endif diff --git a/Descent3/fireball.cpp b/Descent3/fireball.cpp new file mode 100644 index 000000000..d63213742 --- /dev/null +++ b/Descent3/fireball.cpp @@ -0,0 +1,2650 @@ +/* + * $Logfile: /DescentIII/main/fireball.cpp $ + * $Revision: 241 $ + * $Date: 4/19/00 5:07p $ + * $Author: Matt $ + * + * Fireball code + * + * $Log: /DescentIII/main/fireball.cpp $ + * + * 241 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 240 3/22/00 12:13p Matt + * Added some if statements to not damage & kill objects when playing back + * a demo. + * + * 239 3/20/00 12:07p Matt + * Merge of Duane's post-1.3 changes. + * Check for no available vis effects. + * + * 238 10/24/99 1:30a 3dsmax + * if submodel face has 0 vertices, bail out nicely when creating visfx + * electric bolts. + * + * 237 10/12/99 11:01a Jeff + * added grey lightning + * + * 236 9/21/99 2:54p Jeff + * added grey spark fireball (for multicolored sparks) + * + * 235 7/28/99 4:15p Kevin + * Macintosh + * + * 234 5/22/99 10:27p Jason + * changes for multiplayer and buildings, ai + * + * 233 5/22/99 12:40a Matt + * Don't create spliters for non-renderable submodels. (This fixed the + * error texture that was sometimes visible when a blastable grate door + * blew up.) + * + * 232 5/21/99 7:53p Jason + * fixed bug where powerups in multiplayer would get out of sync due to + * blackshark + * + * 231 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 230 5/10/99 8:28p Jason + * made blackshark more interesting indoors + * + * 229 5/10/99 1:53a Jason + * add blackshark explosion + * + * 228 5/07/99 7:30p Chris + * Check for backfaces with concussive forces + * + * 227 5/06/99 7:18p Matt + * Fixed turret object names. + * + * 226 5/06/99 5:44p Matt + * Added three more dead turrets + * + * 225 5/06/99 3:16a Matt + * Added semi-hacked dead model for the Swatter turret. + * + * 224 5/05/99 2:33p Jason + * fixed some multiplayer issues with client objects + * + * 223 5/02/99 7:42p Jason + * added fireball error checking + * + * 222 5/01/99 4:59p Jason + * change blue blast ring + * + * 221 4/30/99 5:42p Jason + * changes for blast rings + * + * 220 4/29/99 2:16a Samir + * adjusted some sound priority stuff + * + * 219 4/23/99 12:32a Matt + * Deleted cut-and-paste-error comments. + * + * 218 4/23/99 12:27a Matt + * Moved the code that plays an object's explosion sound into a function. + * + * 217 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 216 4/21/99 5:31p Jason + * changed smoke chances + * + * 215 4/21/99 3:01p Matt + * Added a new type for dying objects that have AI, instead of keeping a + * flag in the dying info. + * + * 214 4/21/99 12:41p Jason + * make explosion system framerate independant + * + * 213 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 212 4/19/99 3:45a Jeff + * fixed min/max problem + * + * 211 4/18/99 10:55p Chris + * Added ignore own concussive blasts + * + * 210 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 209 4/10/99 5:09p Samir + * prioritize sounds, pass 1 + * + * 208 4/06/99 6:53p Jason + * fixed source safe bug + * + * 207 4/06/99 6:02p Matt + * Added score system + * + * 205 4/05/99 4:39p Jason + * added groovy new smoke trails + * + * 204 4/02/99 3:57p Jason + * sped up vis effect stuff + * + * 203 4/01/99 10:10p Matt + * Re-added my death system changes from version 197 that Jason so kindly + * blew away with version 198. + * + * 202 4/01/99 5:39p Matt + * Fixed (hopefully) bug introduced in last version. + * + * 201 4/01/99 4:28p Matt + * Cleaned up handling of dying flag to prevent infinite recursion. Jeff + * will test. + * + * 200 4/01/99 1:10p Jason + * made vis effects better for the lighting system + * + * 199 3/31/99 11:40a Jason + * added support for attached thick lightning + * + * 198 3/29/99 7:30p Jason + * added cool new energy effect + * + * 196 3/27/99 2:15p Jason + * added better thick lightining + * + * 195 3/25/99 3:29p Jason + * added option to randomize powerup respawn points + * + * 194 3/24/99 5:54p Jason + * fixed size for thick lightning + * + * 193 3/24/99 12:34p Jason + * fixed concussive damage bailing out too early + * + * 192 3/09/99 1:35p Jason + * made blackshark not suck in energy weapons + * + * 191 3/05/99 6:42p Matt + * On death, only do shockwave and shake if an object breaks apart or has + * fireballs. Also, check for no sound before calling play sound. + * + * 190 3/05/99 3:32p Matt + * Made 'remains' work for object deaths. + * + * 189 3/02/99 4:18p Jason + * fixed black smoke bug + * + * 188 3/01/99 11:28a Matt + * Made debris radius half the size of the polymodel, so it sits closer to + * the floor when it stops bouncing. + * + * 187 2/28/99 5:31p Matt + * Removed mprintf() + * + * 186 2/26/99 3:52p Jason + * fixed mprintf + * + * 185 2/26/99 2:03p Matt + * Got blastable doors working, and cleaned up concussive force code. + * + * 184 2/25/99 10:58a Matt + * Added new explosion system. + * + * 183 2/23/99 10:50a Matt + * Check for spew type of -1 is DoDeathSpew() + * + * 182 2/22/99 2:03p Jason + * added different damages for players and generics + * + * 181 2/13/99 12:30a Jeff + * an OBJ_DUMMY shouldn't be blown up...Int3 put in to catch this + * + * 180 2/10/99 3:38p Jason + * table file filter fixups + * + * 179 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 178 2/09/99 2:38p Jason + * added destroyable faces + * + * 177 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 176 2/06/99 10:30p Jason + * fixed some blackshark stuff + * + * 175 2/05/99 1:38p Jason + * dropped splinter count + * + * 174 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 173 2/01/99 10:16a Chris + * Fixed mass prop. bug and accounted for the fact that we hack in a new + * mass for debris + * + * 172 1/29/99 5:09p Chris + * Made changes for ROCKS + * + * 171 1/26/99 6:39p Jason + * added wall effects code + * + * 170 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 169 1/20/99 2:13a Chris + * It is now possible for robots to have special immunities, resistances, + * and vunerabilities + * + * 168 1/18/99 2:46p Matt + * Combined flags & flags2 fields in object struct + * + * 167 12/13/98 9:18p Chris + * Improved influence values for in-code goals (10000 to 1.0). Added + * GF_ORIENT stuff. :) + * + * 166 12/13/98 7:17p Chris + * Improved walker death code + * + * 165 12/11/98 5:08p Jason + * changed mass driver effect + * + * 164 11/30/98 6:02p Jay + * Fixed a bug with concussive forces being applied to room objects + * + * 163 11/19/98 6:37p Jason + * made a better black shark effect + * + * 162 11/19/98 4:35p Chris + * tweaked it + * + * 161 11/19/98 4:21p Chris + * Got rid of some wierd rotation cases with slow deaths from being killed + * with the mass driver + * + * 160 11/19/98 12:22p Jason + * optimizations + * + * 159 11/18/98 2:53p Jason + * added snow effect + * + * 158 11/16/98 6:03p Jason + * took out some comments + * + * 157 11/11/98 2:46p Kevin + * Demo recording system work + * + * 156 10/29/98 6:01p Jason + * added multiple coronas + * + * 155 10/28/98 11:51a Jason + * fixed some multiplayer problems, plus redid coop a bit to make it + * cleaner + * + * 154 10/22/98 5:40p Matt + * If no viewer object, bail from several fireball funtions that use that + * variable. + * + * 153 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 152 10/17/98 3:14p Jason + * made electrical death less deforming + * + * 150 10/16/98 1:41p Jason + * changes for the demo + * + * 149 10/15/98 11:09a Chris + * Added visibility checks for concussive blasts + * + * 148 10/12/98 9:28a Jason + * fixed some rendering bugs + * + * 147 10/07/98 6:55p Jason + * added better lightning death + * + * 146 10/07/98 5:17p Jason + * added new invul hit effect + * + * 145 10/06/98 11:41a Jason + * tweaked electrical death + * + * 144 10/06/98 11:27a Jason + * added new death type for robots + * + * 143 9/29/98 7:15p Jason + * added axis billboards + * + * 142 9/29/98 12:49p Jason + * worked on matcen effects and lightning + * + * 141 9/18/98 8:23p Jason + * fixed insidious vis effect errors + * + * 140 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 139 9/11/98 6:40p Jason + * added smoke puffs + * + * 138 9/09/98 4:49p Jason + * tweaked black shark lightning effect + * + * 137 9/09/98 4:28p Jason + * added better weapon effects + * + * 136 8/31/98 5:29p Jason + * made explosions better with addition of sound effects + * + * 135 8/21/98 12:29p Jason + * made smoke puffs draw more inside the mine + * + * 134 8/20/98 4:59p Nate + * Limitted the velocity of created debris to +-100000.0f + * + * 133 8/19/98 2:49p Jason + * Fixed black smoked to be more noticable + * + * 132 8/17/98 10:48a Jason + * added more lightning code + * + * 131 8/17/98 1:32a Chris + * Fixed a type cast bug the resulted in rand_vec.y always equaling .4 + * and then zero after normalization (as the others ranged from RAND_MAX/2 + * to -RAND_MAX/2) + * + * 130 8/17/98 1:15a Chris + * By default, obj_debris don't make a sound when they die + * + * 129 8/16/98 6:17p Chris + * Added generic object spewing code + * + * 128 8/10/98 5:24p Jason + * don't do blast flash if you can't see the blast + * + * 127 8/03/98 11:12a Jason + * fixed some zero foward vector errors + * + * 126 7/24/98 6:08p Jason + * added some more procedurals + * + * 125 7/24/98 4:45p Jason + * added DrawSphere + * + * 124 6/29/98 5:06p Jason + * made a somewhat better gravity explosion + * + * + * 123 6/25/98 5:17p Jason + * checked in for mark to check out warp effect + * + * 122 6/25/98 12:19p Jason + * made gravity effect do different damage in single vs multiplayer + * + * 121 6/23/98 3:34p Jason + * added cool lighting effect to gravity field + * + * 120 6/22/98 6:52p Jason + * changed gravity field effect somewhat + * + * 119 6/22/98 6:26p Jason + * added gravity field effect for weapons + * + * 118 6/15/98 7:45a Chris + * FIxed a bug with making shockwaves + * + * 117 5/27/98 5:53p Chris + * No more flying turrets + * + * 116 5/26/98 6:54p Jason + * viseffect optimizations + * + * 115 5/26/98 3:36p Chris + * Tweaked explosions + * + * 114 5/26/98 3:24p Chris + * Oops. I was corrupting the orientation + * + * 113 5/19/98 4:42a Chris + * Added shockwave's -- enjoy. :) + * + * 112 5/15/98 4:21p Chris + * Oops + * + * 111 5/15/98 3:53p Keneta + * Make objects on ceiling not fly upward + * + * 110 5/14/98 7:40p Samir + * shaking cockpit. + * + * 109 5/14/98 2:17p Jason + * more changes to be memory friendly + * + * 108 5/11/98 4:40p Chris + * AI info is now a malloc'ed pointer + * + * 107 5/01/98 6:23p Jason + * made better puddle rain + * + * 106 5/01/98 3:41p Chris + * + * 105 5/31/98 3:12p Chris + * Made objects die with death anims. + * + * 104 5/31/98 11:01a Chris + * Made debris less heavy + * + * 103 4/30/98 6:45p Jason + * more changes for weather + * + * 102 4/30/98 3:39p Jason + * don't make non-billowingfireballs rotate + * + * 101 4/30/98 12:22p Jason + * did some lo-res model optimizations + * + * 100 4/29/98 11:38a Jason + * added some weather effects (first pass) + * + * 99 4/22/98 3:24p Jason + * fixed colored disk problem + * + * 98 4/17/98 12:45p Jason + * various changes for multiplayer + * + * 97 4/15/98 3:28p Jason + * changed glow stuff to work with new system + * + * 96 4/15/98 2:33p Jason + * changed some smoke trail stuff + * + * 95 4/13/98 4:19p Chris + * Added PF_IGNORE_CONCUSSIVE_FORCES + * + * 94 4/10/98 2:17p Jason + * added afterburner fireball + * + * 93 4/09/98 5:17p Jason + * got guided working in multiplayer + * + * 92 4/07/98 3:31p Jason + * got particle effects working with weapons + * + * 91 4/06/98 1:35p Chris + * Fixed a multiplayer bug + * + * 90 4/02/98 6:42p Jason + * added blue blast ring effect + * + * 89 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 88 3/31/98 12:16p Jason + * some small changes for explosions + * + * 87 3/27/98 4:46p Jason + * changes for voodoo2 + * + * 86 3/23/98 4:54p Chris + * Fixed problem with powerups getting concussive force + * + * 85 3/20/98 9:35p Jason + * fixed stupid error with last rev + * + * 83 3/20/98 5:51p Jason + * more changes for multiplayer + * + * 82 3/19/98 3:44p Chris + * + * 81 3/19/98 3:44p Chris + * Multiplayer enchancement for collisions + * + * 80 3/19/98 1:25p Chris + * Improved size stuff used for concussive effects + * + * 79 3/19/98 1:17p Chris + * Concussive blasts effect weapons and not powerups + * + * 78 3/13/98 4:17p Jason + * took out mprintf + * + * 77 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 76 3/12/98 7:14p Jason + * fixed FOV bug + * + * 75 3/09/98 5:58p Jason + * draw powerups with saturated alpha rings + * + * 74 3/06/98 4:09p Jason + * don't do glows if not in hardware + * + * 73 2/27/98 11:56a Chris + * Improved the distance formula for concussive blasts + * + * 72 2/25/98 4:59p Jason + * got dynamic explosion lighting working with vis-effects + * + * 71 2/25/98 4:31p Jason + * changes for explosions + * + * 70 2/19/98 6:20p Chris + * FindPointRoom() call is not for in-game. + * + * 69 2/19/98 1:10p Jason + * toned down building explosions a bit + * + * 68 2/16/98 2:24p Jason + * fixed bad indexing into subobjects problem + * + * 67 2/10/98 6:03p Jason + * took out ugly blast sphere graphic + * + * 66 2/05/98 2:54p Jason + * changes for explosions + * + * 65 2/04/98 9:28p Jason + * added some new weapons effects + * + * 64 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + */ +#include +#include "pstypes.h" +#include "pserror.h" +#include "fireball.h" +#include "vclip.h" +#include "object.h" +#include "cockpit.h" +#include "game.h" +#include "3d.h" +#include "mono.h" +#include "room.h" +#include "polymodel.h" +#include "objinfo.h" +#include "gametexture.h" +#include "splinter.h" +#include "PHYSICS.H" +#include "damage.h" +#include "gameevent.h" +#include "weapon.h" +#include "viseffect.h" +#include "spew.h" +#include "hlsoundlib.h" +#include "sounds.h" +#include "gameloop.h" +#include "multi.h" +#include "AIGoal.h" +#include "AIMain.h" +#include "ship.h" +#include "BOA.h" +#include "demofile.h" +#include "ObjScript.h" +#include +#include +#include "psrand.h" +#ifdef __LINUX__ +#define min(a,b) ((a=0); + Fireballs[i].bm_handle=bm_handle; + } + + // convert the grey spark into grayscale + ushort *data; + int size; + float recip32 = 1.0f/32.0f; + data = bm_data(Fireballs[GRAY_SPARK_INDEX].bm_handle,0); + size = bm_w(Fireballs[GRAY_SPARK_INDEX].bm_handle,0) * bm_h(Fireballs[GRAY_SPARK_INDEX].bm_handle,0); + for(i=0;i> 11); + ubyte g = (ubyte)((col565 & 0x07c0) >> 6); + ubyte b = (ubyte)(col565 & 0x001f); + float brightness = ((r * 0.30f) + (g * 0.59f) + (b * 0.11f)) * recip32; + ubyte elem = (ubyte)(255.0f*brightness); + if (brightness > 1.0f) + elem = 255; + data[i] = GR_RGB16(elem,elem, elem) | OPAQUE_FLAG; + } + } + GameBitmaps[Fireballs[GRAY_SPARK_INDEX].bm_handle].flags |= BF_CHANGED; +} +void DrawSmolderingObject (object *obj); +// Given an object, renders the representation of this fireball +void DrawFireballObject (object *obj) +{ + ASSERT (obj->type==OBJ_FIREBALL); + + // First check to see if these are special types + if (obj->id==BLAST_RING_INDEX) + { + DrawBlastRingObject (obj); + return; + } + if (obj->id==GRAVITY_FIELD_INDEX) + { + // Draw two blast rings + matrix tempm; + vm_MakeIdentity (&tempm); + ObjSetOrient(obj, &tempm); + DrawBlastRingObject (obj); + return; + } + if (obj->id==SMOLDERING_INDEX) + { + DrawSmolderingObject (obj); + return; + } + float norm_time; + float time_live=Gametime-obj->creation_time; + float size=obj->size; + + int objnum=obj-Objects; + int rot_angle; + int bm_handle; + if (Fireballs[obj->id].type==FT_BILLOW) + rot_angle=((objnum*5000)+(FrameCount*160))%65536; + else + rot_angle=((objnum*5000)+(FrameCount*30))%65536; + + norm_time=time_live/obj->lifetime; + if (norm_time>=1) + norm_time=.99999f; // don't go over! + if (obj->id==SMOKE_TRAIL_INDEX) // If its a smoke trail, get image from texture + { + int texnum=obj->ctype.blast_info.bm_handle; + if (GameTextures[texnum].flags & TF_ANIMATED) + { + PageInVClip (GameTextures[texnum].bm_handle); + vclip *vc=&GameVClips[GameTextures[texnum].bm_handle]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + } + else + bm_handle=GameTextures[texnum].bm_handle; + } + else if (obj->id==CUSTOM_EXPLOSION_INDEX) // Do custom + { + bm_handle=GetTextureBitmap (obj->ctype.blast_info.bm_handle,0); + if (!(GameTextures[obj->ctype.blast_info.bm_handle].flags & TF_ANIMATED)) + { + // If this is just a single bitmap, scale it based on time + if (norm_time<.5) // Ramp up quickly + { + float temp_norm=norm_time/.2; + temp_norm=min(1.0,temp_norm); + size*=temp_norm; + } + else // ramp down slowly + { + float temp_norm=((norm_time-.5)/.5); + size*=(1.0-temp_norm); + } + + } + } + else if (Fireballs[obj->id].type==FT_SPARK) // Do spark + { + bm_handle=Fireballs[obj->id].bm_handle; + size*=(1.0-norm_time); + + } + else + { + vclip *vc=&GameVClips[Fireballs[obj->id].bm_handle]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + } + if (Fireballs[obj->id].type==FT_SMOKE) + { + if (norm_time>.3) + { + float temp_time=(norm_time-.3); + temp_time/=.7f; + size*=(1+(temp_time*2.3)); + } + } + // Set some alpha + if (Fireballs[obj->id].type==FT_SMOKE || obj->id==CUSTOM_EXPLOSION_INDEX) + rend_SetAlphaType (AT_CONSTANT+AT_TEXTURE); + else + rend_SetAlphaType (AT_SATURATE_TEXTURE); + float val; + if (norm_time>.5) + val=1.0-((norm_time-.5)/.5); + else + val=1.0; + if (Fireballs[obj->id].type==FT_SMOKE) + rend_SetAlphaValue (val*SMOKE_ALPHA*255); + else + rend_SetAlphaValue (val*FIREBALL_ALPHA*255); + rend_SetOverlayType (OT_NONE); + + rend_SetZBias (-2.0f); + rend_SetZBufferWriteMask (0); + rend_SetWrapType (WT_CLAMP); + rend_SetLighting (LS_NONE); + // Cap size + if (size>MAX_FIREBALL_SIZE) + size=MAX_FIREBALL_SIZE; + + // Draw!! + g3_DrawRotatedBitmap (&obj->pos,rot_angle,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + rend_SetZBias (0); + rend_SetZBufferWriteMask (1); +} +// Creates a gravity field that sucks objects into it +int CreateGravityField (vector *pos,int roomnum,float size,float time,int parent_handle) +{ + int index=CreateFireball (pos,GRAVITY_FIELD_INDEX,roomnum,REAL_FIREBALL); + if (index<0) + return -1; + object *obj=&Objects[index]; + + obj->lifetime=time; + obj->lifeleft=time; + obj->size=1; + obj->ctype.blast_info.max_size=size; + obj->ctype.blast_info.bm_handle=Fireballs[BLUE_BLAST_RING_INDEX].bm_handle; + obj->parent_handle=parent_handle; + return index; +} +// Creates a fireball and then sets its size +int CreateFireball(vector *pos,int fireball_num,int roomnum,int realtype) +{ + int objnum; + + ASSERT (roomnum != -1); + if (realtype==VISUAL_FIREBALL) // If this is a visual effect then create it instead! + return (VisEffectCreate (VIS_FIREBALL,fireball_num,roomnum,pos)); + else + { + objnum=ObjCreate (OBJ_FIREBALL,fireball_num,roomnum,pos,NULL); + if (objnum<0) + { + mprintf ((0,"Couldn't create fireball object!\n")); + return -1; + } + Objects[objnum].size=Fireballs[fireball_num].size; + Objects[objnum].flags|=OF_USES_LIFELEFT; + + Objects[objnum].lifeleft=Fireballs[fireball_num].total_life; + Objects[objnum].lifetime=Objects[objnum].lifeleft; + } + + return objnum; +} +// Creates a fireball object with a custom texture/vclip +int CreateCustomFireballObject (vector *pos,int fireball_num,int tex_handle,int roomnum) +{ + int objnum; + ASSERT (GameTextures[tex_handle].used>=1); + + if (roomnum==-1) + return -1; + objnum=ObjCreate (OBJ_FIREBALL,fireball_num,roomnum,pos,NULL); + if (objnum<0) + { + mprintf ((0,"Couldn't create fireball object!\n")); + return -1; + } + Objects[objnum].size=Fireballs[fireball_num].size; + Objects[objnum].flags|=OF_USES_LIFELEFT; + float lifetime; + // If this is an animated texture then use the life of the vclip + if (GameTextures[tex_handle].flags & TF_ANIMATED) + { + PageInVClip (GameTextures[tex_handle].bm_handle); + vclip *vc=&GameVClips[GameTextures[tex_handle].bm_handle]; + lifetime=vc->frame_time*vc->num_frames; + } + else // Just set it to half a second + lifetime=.5; + Objects[objnum].lifeleft=lifetime; + Objects[objnum].lifetime=lifetime; + Objects[objnum].ctype.blast_info.bm_handle=tex_handle; + + return objnum; +} +//---------------------------------- +// Putting in some explosion stuff +//---------------------------------- +// Creates a debris piece that goes off in a given direction, with a given magnitude +object *CreateSubobjectDebrisDirected(object *parent, int subobj_num,vector *dir,float explosion_mag,int death_flags) +{ + int objnum; + object *obj; + ASSERT(parent->flags & OF_POLYGON_OBJECT); + objnum = ObjCreate(OBJ_DEBRIS, 0,parent->roomnum,&parent->pos, &parent->orient); + + if(objnum < 0 || objnum > Highest_object_index) + { + mprintf((0, "WARNING: Debris not created!\n")); + return NULL; + } + obj = &Objects[objnum]; + + ASSERT(subobj_num < MAX_SUBOBJECTS); + int model_num=parent->rtype.pobj_info.model_num; + + //Set polygon-object-specific data + obj->rtype.pobj_info.model_num = model_num; + obj->rtype.pobj_info.subobj_flags = 0x00000001 << subobj_num; + obj->rtype.pobj_info.tmap_override = parent->rtype.pobj_info.tmap_override; + //Set physics data for this object + ASSERT(obj->movement_type == MT_PHYSICS); + obj->mtype.phys_info.velocity = *dir; + obj->mtype.phys_info.velocity *= explosion_mag; + //obj->mtype.phys_info.velocity += parent->mtype.phys_info.velocity; + if(obj->mtype.phys_info.velocity.x > 100000.0f) obj->mtype.phys_info.velocity.x = 100000.0f; + if(obj->mtype.phys_info.velocity.y > 100000.0f) obj->mtype.phys_info.velocity.y = 100000.0f; + if(obj->mtype.phys_info.velocity.z > 100000.0f) obj->mtype.phys_info.velocity.z = 100000.0f; + if(obj->mtype.phys_info.velocity.x < -100000.0f) obj->mtype.phys_info.velocity.x = -100000.0f; + if(obj->mtype.phys_info.velocity.y < -100000.0f) obj->mtype.phys_info.velocity.y = -100000.0f; + if(obj->mtype.phys_info.velocity.z < -100000.0f) obj->mtype.phys_info.velocity.z = -100000.0f; + obj->mtype.phys_info.rotvel = parent->mtype.phys_info.rotvel; + vm_MakeZero(&obj->mtype.phys_info.rotthrust); + obj->size = Poly_models[obj->rtype.pobj_info.model_num].submodel[subobj_num].rad * 0.5; + obj->mtype.phys_info.mass = 40.0; + obj->mtype.phys_info.drag=.001f; + + obj->mtype.phys_info.flags = (PF_GRAVITY | PF_BOUNCE | PF_FIXED_ROT_VELOCITY); + obj->mtype.phys_info.coeff_restitution=.2f; + obj->mtype.phys_info.rotvel.x = (float)((60000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + obj->mtype.phys_info.rotvel.y = (float)((60000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + obj->mtype.phys_info.rotvel.z = (float)((60000.0f * (float)(RAND_MAX/2 - ps_rand())) / (float)(RAND_MAX/2)); + if (death_flags & DF_DEBRIS_REMAINS) { + obj->mtype.phys_info.num_bounces = 8; + obj->mtype.phys_info.flags |= PF_STICK; + obj->flags &= ~OF_USES_LIFELEFT; + //ChrisP needs to add some code here to make the debris behave resonably + } + else { + obj->mtype.phys_info.num_bounces = 2; + obj->lifeleft = 2.0+((ps_rand()%50)*.05); + obj->flags |= OF_USES_LIFELEFT; + } + ASSERT(obj->control_type == CT_DEBRIS); + //Copy death flags debris + obj->ctype.debris_info.death_flags = death_flags; + obj->ctype.debris_info.last_smoke_time=-1; + //Done! + return obj; +} +// Creates a subobject debris piece that goes off in a random direction +object *CreateSubobjectDebris(object *parent, int subobj_num,float explosion_mag,int death_flags) +{ + vector rand_vec; + //Set physics data for this object + rand_vec.x= (float)(RAND_MAX/2 - ps_rand()); + rand_vec.y=(float)(RAND_MAX/2 - ps_rand())+.2f * RAND_MAX; // a habit of moving upwards + rand_vec.z = (float)(RAND_MAX/2 - ps_rand()); + + vm_NormalizeVectorFast(&rand_vec); + explosion_mag *= 1.0f + ((float)(RAND_MAX/2 - ps_rand())/(float)(RAND_MAX/2) * 0.05); // +5/-5 percent + return CreateSubobjectDebrisDirected(parent, subobj_num,&rand_vec,explosion_mag,death_flags); +} +//Create extra fireballs for an exploding object +void CreateExtraFireballs(object *obj,float size_scale) +{ + // Create some extra explosions to start later + float expl_size = obj->size * size_scale; + int size_add = expl_size/EXTRA_EXPLOSION_THRESHOLD; + int extras = size_add + (ps_rand()%4); + int i; + if (obj->type==OBJ_BUILDING && OBJECT_OUTSIDE(obj)) + extras+=4; + // Cap at 12 + extras=min(12,extras); + //mprintf ((0,"Creating %d extra explosions\n",extras)); + CreateRandomSparks (extras,&obj->pos,obj->roomnum); + for (i=0;ipos; + float vals[10],add_time; + float upwards; + // Make this a billowing explosion + if (obj->type==OBJ_BUILDING && OBJECT_OUTSIDE(obj)) + { + float increment=(obj->size*1.5)/extras; + pos.y+=(increment*i); + add_time=(1.0/extras)*i; + upwards=60; + } + else + { + pos.x+=( ( (ps_rand()%100) / 50.0)-1.0)*(obj->size/2); + pos.y+=( ( (ps_rand()%100) / 50.0)-1.0)*(obj->size/2); + pos.z+=( ( (ps_rand()%100) / 50.0)-1.0)*(obj->size/2); + add_time=.3+((ps_rand()%100)/200.0); + upwards=0; + } + + vals[0]=pos.x; + vals[1]=pos.y; + vals[2]=pos.z; + if (size_add>0) + { + if (ps_rand()%2) + vals[3]=BIG_EXPLOSION_INDEX; + else + vals[3]=GetRandomMediumExplosion(); + } + else + vals[3]=GetRandomMediumExplosion(); + vals[4]=obj->roomnum; + vals[5]=expl_size; + vals[6]=upwards; + // Bash some values for billowing explosions + if ((obj->type==OBJ_BUILDING) && (OBJECT_OUTSIDE(obj))) + { + float norm=((float)(i+1))/extras; + //norm=1.0-norm; + vals[3] = GetRandomBillowingExplosion(); + vals[5] *= (.5+(.5*norm)); + } + + CreateNewEvent (0,0,add_time,vals,sizeof(float)*7,DoExplosionEvent); + } +} +//Break apart a polygon model into pieces of debris +void BreakApartModel(object *obj,float explosion_mag,int death_flags) +{ + poly_model *pm=GetPolymodelPointer (obj->rtype.pobj_info.model_num); + if (pm->n_models > 1) + { + int i; + + // Create debris pieces from subobjects + for (i=1;in_models;i++) + CreateSubobjectDebris(obj,i,explosion_mag,death_flags); + // Change size for collision purposes + obj->size = Poly_models[obj->rtype.pobj_info.model_num].submodel[0].rad; + } +} +// Splinter stuff +#define MAX_SPLINTERS_PER_OBJECT 10 +// Creates individual face particles from the main body of an object +void CreateSplintersFromBody (object *obj,float explosion_mag,float lifetime) +{ + ASSERT (obj->flags & OF_POLYGON_OBJECT); + int facenums[MAX_SPLINTERS_PER_OBJECT]; + vector rot_vecs[MAX_SUBOBJECTS]; + int num_splinters=0; + int i,t; + int parent_objnum=obj-Objects; + matrix m; + m = obj->orient; + vm_TransposeMatrix(&m); + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + //If submodel 0 not renderable, bail + if (IsNonRenderableSubmodel(pm,0)) + return; + bsp_info *sm=&pm->submodel[0]; + if (sm->num_facesnum_faces;i++) + { + facenums[i]=i; + num_splinters++; + } + } + else + { + int limit=min(sm->num_faces,MAX_SPLINTERS_PER_OBJECT); + for (i=0;inum_faces; + num_splinters++; + } + } + // Now, create those splinters! + for (i=0;ifaces[facenum].nverts,MAX_VERTS_PER_SPLINTER); + + vm_MakeZero (&dest); + vm_MakeZero (¢er); + // First get the center of this particular face + for (t=0;tverts[sm->faces[facenum].vertnums[t]]; + vector temp_vec; + + temp_vec = pnt * m; + rot_vecs[t]=temp_vec; + center+=temp_vec; + } + + center/=num_verts; + dest=center+obj->pos; + + // Now create splinter with that faces center position + int s_objnum=ObjCreate (OBJ_SPLINTER,pm-Poly_models,obj->roomnum,&dest,NULL); + if (s_objnum<0) + { + mprintf ((0,"Couldn't create splinter object!\n")); + return; + } + else + { + // Fill in relevant info + + object *splinter_obj=&Objects[s_objnum]; + splinter_obj->ctype.splinter_info.subobj_num=0; + splinter_obj->ctype.splinter_info.facenum=facenum; + for (t=0;tctype.splinter_info.verts[t]=(rot_vecs[t])-center; + splinter_obj->ctype.splinter_info.center=center; + vector subvec=dest-obj->pos; + vm_NormalizeVector (&subvec); + ObjSetOrient(splinter_obj, &Identity_matrix); + splinter_obj->mtype.phys_info.velocity = (obj->mtype.phys_info.velocity)+(((subvec*obj->size)/lifetime)*4); + splinter_obj->mtype.phys_info.flags |= PF_FIXED_ROT_VELOCITY; + splinter_obj->mtype.phys_info.rotvel=splinter_obj->mtype.phys_info.velocity*400; + // Randomize by 20 percent plus or minus + splinter_obj->mtype.phys_info.velocity*=1.0+((((ps_rand()%100)-50)/50.0)*.2); + splinter_obj->mtype.phys_info.rotvel*=1.0+((((ps_rand()%100)-50)/50.0)*.2); + + vm_MakeZero(&splinter_obj->mtype.phys_info.rotthrust); + splinter_obj->size = .5f; + splinter_obj->mtype.phys_info.mass = 2500.0f; + splinter_obj->mtype.phys_info.drag = .01f; + + splinter_obj->mtype.phys_info.flags |= (PF_GRAVITY)|(PF_BOUNCE); + + splinter_obj->mtype.phys_info.num_bounces = 0; + splinter_obj->movement_type = MT_PHYSICS; + SetObjectControlType(splinter_obj, CT_SPLINTER); + splinter_obj->render_type=RT_SPLINTER; + // Randomize the lifetime a little + float mod_lifetime=lifetime; + mod_lifetime+=(((ps_rand()%9)-4)*.1); + if (((splinter_obj-Objects)%14)==0) + mod_lifetime+=((ps_rand()%3)*.5); + splinter_obj->lifeleft = mod_lifetime; + splinter_obj->lifetime=mod_lifetime; + splinter_obj->flags |= OF_USES_LIFELEFT; + } + } +} +void DoDeathSpew (object *parent) +{ + ASSERT(IS_GENERIC(parent->type)); + int i,j; + int num_last_type = 0; + unsigned char f_dspew = Object_info[parent->id].f_dspew; + + if (!parent) + return; + for(i = 0; i < MAX_DSPEW_TYPES; i++) + { + int type; + int id; + id = Object_info[parent->id].dspew[i]; + if (id == -1) + continue; //spew type set to none + type = Object_info[id].type; + int max_spewed = Object_info[parent->id].dspew_number[i]; + int rand_val = (int)((float)RAND_MAX * Object_info[parent->id].dspew_percent[i]); + // Skip it if the player has that weapon battery enabled + // If multiplayer, it will spew type 2 if no type 1 is spewed + if(i == 0 && (f_dspew & DSF_ONLY_IF_PLAYER_HAS_OBJ_1)) + { + bool f_skip = false; + + if(Game_mode & GM_MULTI) + { + f_dspew &= ~DSF_ONLY_IF_PLAYER_HAS_OBJ_1; + f_dspew |= DSF_ONLY_IF_NO_1; + } + else if(type == OBJ_POWERUP) + { + player *playp=&Players[0]; + for(j = 0; j < MAX_PLAYER_WEAPONS; j++) + { + if (playp->weapon_flags & HAS_FLAG(j)) + { + int p_id = Ships[playp->ship_index].spew_powerup[j]; + if(p_id == id) + { + f_skip = true; + break; + } + } + } + if(f_skip) + continue; + } + } + + if(i == 1 && (f_dspew & DSF_ONLY_IF_NO_1) && num_last_type != 0) + continue; + num_last_type = 0; + for(j = 0; j < max_spewed; j++) + { + if(ps_rand() <= rand_val) + { + int objnum=ObjCreate (type,id,parent->roomnum,&parent->pos,NULL); + num_last_type++; + if (objnum<0) + { + mprintf ((0,"Couldn't spew object!\n")); + return; + } + object *obj=&Objects[objnum]; + + //Set random velocity for powerups + obj->mtype.phys_info.velocity.x = ((ps_rand() / (float)RAND_MAX) - .5f) * 35.0; + obj->mtype.phys_info.velocity.z = ((ps_rand() / (float)RAND_MAX) - .5f) * 35.0; + obj->mtype.phys_info.velocity.y = ((ps_rand() / (float)RAND_MAX) - .5f) * 35.0; + InitObjectScripts (obj); + //Send object to other players + if (Game_mode & GM_MULTI) + { + if (Netgame.local_role==LR_SERVER) + { + MultiSendObject (obj,0); + } + } + } + } + } +} +int GetRandomExplosion(float size); +//Creates a fireball vis effect for the specified object +//The explosion size is based on the object size times the size_scale +//The fireball type will be randomly selected based on the explosion size +//Returns the visnum of the fireball +int CreateObjectFireball(object *objp,float size_scale) +{ + int expl_type = GetRandomExplosion(objp->size * size_scale); + float expl_size = objp->size * size_scale * 2.0; + int fireball_visnum = CreateFireball(&objp->pos,expl_type,objp->roomnum,VISUAL_FIREBALL); + if (fireball_visnum>=0) + VisEffects[fireball_visnum].size = expl_size; + return fireball_visnum; +} +// ------------------------------------------------------------------------------------------------------- +//do whatever needs to be done for this piece of debris for this frame +void DoDebrisFrame(object *obj) +{ + ASSERT(obj->control_type == CT_DEBRIS); + //Get death flags + int death_flags = obj->ctype.debris_info.death_flags; + //See if we're dead + if ((obj->lifeleft <= 0.0) || (obj->mtype.phys_info.num_bounces < 1)) + { + //If explodes, create fireball & sound + if (death_flags & DF_DEBRIS_FIREBALL) { + Sound_system.Play3dSound(SOUND_DEBRIS_EXPLODE, SND_PRIORITY_NORMAL, obj); + CreateObjectFireball(obj); + } + //If debris has a blast ring, create it + if (death_flags & DF_DEBRIS_BLAST_RING) + CreateObjectBlastRing(obj); + //Make debris go away, or make it inactive + if (! (death_flags & DF_DEBRIS_REMAINS)) + SetObjectDeadFlag (obj); + else + SetObjectControlType(obj, CT_NONE); + //Done with this debris + return; + } + //Create smoke, if this debris smokes + if (death_flags & DF_DEBRIS_SMOKES) + { + if (Gametime-obj->ctype.debris_info.last_smoke_time>.015 && (rand()%2)) + { + // Create a small flame puff every now and then + int visnum=CreateFireball (&obj->pos,BLACK_SMOKE_INDEX,obj->roomnum,VISUAL_FIREBALL); + + if (visnum>=0) + VisEffects[visnum].size=obj->size/2; // Make small! + obj->ctype.debris_info.last_smoke_time=Gametime; + } + } +} +//Create electrical bolts on an object +void CreateElectricalBolts(object *objp,int num_bolts) +{ + for (int i=0;irtype.pobj_info.model_num]; + int subnum=ps_rand()%pm->n_models; + bsp_info *sm=&pm->submodel[subnum]; + if (sm->nverts > 0) { + int visnum=VisEffectCreate (VIS_FIREBALL,LIGHTNING_BOLT_INDEX,objp->roomnum,&objp->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=1.0; + vis->lifetime=1.0; + vis->end_pos=objp->pos; + vis->velocity.x=.15f; + vis->velocity.y=3; + vis->attach_info.obj_handle=objp->handle; + vis->attach_info.subnum=subnum; + vis->attach_info.vertnum=ps_rand()%sm->nverts; + vis->attach_info.end_vertnum=ps_rand()%sm->nverts; + vis->attach_info.modelnum=objp->rtype.pobj_info.model_num; + vis->flags=VF_USES_LIFELEFT|VF_EXPAND|VF_ATTACHED; + } + } + else { + mprintf((0, "VIS: Submodel %s has 0 vertices!!!\n", sm->name)); + } + } +} +//Create a smoke spewer for the specified object +void CreateObjectSmokeSpewer(object *objp) +{ + spewinfo myspew; + myspew.random=0; + myspew.use_gunpoint=false; + myspew.real_obj=false; + myspew.pt.origin=objp->pos; + myspew.pt.normal.x=0; + myspew.pt.normal.y=1.0; + myspew.pt.normal.z=0; + myspew.pt.room_num=objp->roomnum; + myspew.effect_type=BLACK_SMOKE_INDEX; + myspew.drag=.01f; + myspew.mass=10; + myspew.time_int=.25; + myspew.longevity=30; + myspew.lifetime=2.0; + myspew.size=10.0; + myspew.speed=50; + SpewCreate (&myspew); +} +//Play the explosion sound for this object +void PlayObjectExplosionSound(object *objp) +{ + int sound; + if (objp->type == OBJ_DOOR) { + sound = SOUND_ROBOT_EXPLODE_1; + } + else { + ASSERT(IS_GENERIC(objp->type)); + sound = Object_info[objp->id].sounds[GSI_EXPLODE]; + if (sound == SOUND_NONE_INDEX) { //check for default + if (objp->type == OBJ_BUILDING) + sound = SOUND_BUILDING_EXPLODE; + else + sound = (ps_rand() > RAND_MAX/2) ? SOUND_ROBOT_EXPLODE_1 : SOUND_ROBOT_EXPLODE_2; + } + } + if (sound != -1) + Sound_system.Play3dSound(sound, SND_PRIORITY_HIGH, objp); +} +char *dead_object_types[] = { "swatter", "swatter(deadmodel)", + "Hangturret", "Securityturret(DEAD)", + "Lance", "Lance(DEAD)", + "L10 swatter", "swatter(deadmodel)", + }; +#define N_DEAD_OBJECT_TYPES (sizeof(dead_object_types) / sizeof(*dead_object_types) / 2) +#include "osiris_predefs.h" +//Creates a dead version of the given object +void CreateDeadObject(object *objp) +{ + if (objp->type == OBJ_ROBOT) { + for (int i=0;iid].name,dead_object_types[i*2]) == 0) { + int id = FindObjectIDName(dead_object_types[i*2 + 1]); + if (id != -1) { + vector old_point,pos; + PhysCalcGround(&old_point, NULL, objp, 0); //Old ground point + poly_model *pm = GetPolymodelPointer(Object_info[id].render_handle); + pos = old_point - pm->ground_slots[0].pnt * ~objp->orient; + osipf_ObjCreate(Object_info[id].type,id,objp->roomnum,&pos,&objp->orient); + } + return; //done + } + } + } +} +#define FADE_TIME 2.0 +//Destroy an object immediately +void DestroyObject(object *objp,float explosion_mag,int death_flags) +{ + ASSERT(IS_GENERIC(objp->type) || (objp->type == OBJ_DOOR)); + //Say this this object is dying (if not already set) + objp->flags |= OF_DYING; + //Create blast ring if enabled for this object + if (death_flags & DF_BLAST_RING) + CreateObjectBlastRing(objp); + //Do a shake & shockwave only if the object either breaks apart or has fireballs + if ((death_flags & DF_FIREBALL) || (death_flags & DF_BREAKS_APART)) { + //Shake the player. The odd magnitude is from Jason's old code. + //maybe it would make more sense to base the shake on the explosion size + float dist = vm_VectorDistanceQuick(&Player_object->pos,&objp->pos); + if (dist / objp->size < 30.0) + AddToShakeMagnitude(objp->size * 5.0 - dist / 6.0); + //Do a shockwave + MakeShockwave(objp, objp->handle); + } + //Play sound if not flagged to play at start of delay + if (! (death_flags & DF_DELAY_SOUND)) + PlayObjectExplosionSound(objp); + //Do death spew if generic object + if (IS_GENERIC(objp->type)) { + if(!((Game_mode & GM_MULTI) && (Netgame.local_role!=LR_SERVER))) + if(Demo_flags != DF_PLAYBACK) + DoDeathSpew(objp); + } + ASSERT(objp->flags & OF_POLYGON_OBJECT); + ASSERT(objp->rtype.pobj_info.subobj_flags == 0xFFFFFFFF); + //Create fireballs + int fireball_visnum = -1; + if (death_flags & DF_FIREBALL) { + //Determine size of explosion + float size_scale; + switch (DEATH_EXPL_SIZE(death_flags)) { + case DF_EXPL_SMALL: size_scale = 0.5f; break; + case DF_EXPL_MEDIUM: size_scale = 1.0f; break; + case DF_EXPL_LARGE: size_scale = 1.6f; break; + default: Int3(); + } + //Create big fireball + fireball_visnum = CreateObjectFireball(objp,size_scale); + //Create extra fireballs + CreateExtraFireballs(objp,size_scale); + } + // Switch to dying model + if (objp->rtype.pobj_info.dying_model_num != -1) + objp->rtype.pobj_info.model_num = objp->rtype.pobj_info.dying_model_num; + //Create debris pieces + if (death_flags & DF_BREAKS_APART) + BreakApartModel(objp,explosion_mag,death_flags); + //Create splinters. Base life of the life of the fireball created earlier + if (death_flags & DF_FIREBALL) + { + float fireball_lifeleft = (fireball_visnum != -1) ? VisEffects[fireball_visnum].lifeleft : 2.0; + CreateSplintersFromBody(objp,explosion_mag,OBJECT_OUTSIDE(objp) ? fireball_lifeleft : (fireball_lifeleft/2.0)); + } + // Make sure its ok to delete if this is a netplayer game + if ((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT) && (objp->flags & OF_SERVER_OBJECT)) + ASSERT (objp->flags & OF_SERVER_SAYS_DELETE); + //Make object go away or become inert + if (death_flags & DF_REMAINS) + { //Make object do nothing + SetObjectControlType(objp,CT_NONE); + objp->type = OBJ_DEBRIS; //do it won't do idle animation + objp->movement_type = MT_NONE; + } + else if (death_flags & DF_FADE_AWAY) + { + ASSERT(objp->effect_info != NULL); + objp->effect_info->type_flags |= EF_FADING_OUT; + objp->effect_info->fade_time = objp->effect_info->fade_max_time = FADE_TIME; + objp->lifeleft = FADE_TIME; + objp->flags |= OF_USES_LIFELEFT; + objp->control_type = CT_NONE; + } + else + { //Mark the object to die + CreateDeadObject(objp); + SetObjectDeadFlag(objp); + } + +} +#define ELECTRICAL_DEATH_TIME 3.0 +//Process a dying object for one frame +void DoDyingFrame(object *objp) +{ + //Make sure types & flags are right + ASSERT(objp->flags & OF_DYING); + ASSERT((objp->control_type == CT_DYING) || (objp->control_type == CT_DYING_AND_AI)); + //Get death flags + int death_flags = objp->ctype.dying_info.death_flags; + //Update delay time + objp->ctype.dying_info.delay_time -= Frametime; + //Done dying? + if (objp->ctype.dying_info.delay_time < 0) { + //Kill the object for real + DestroyObject(objp,30,death_flags); + //Add to score + if ((objp->ctype.dying_info.killer_playernum != -1) && IS_GENERIC(objp->type)) + PlayerScoreAdd(objp->ctype.dying_info.killer_playernum,Object_info[objp->id].score); + //We're done + return; + } + //If sparking death, do sparks + if (death_flags & DF_DELAY_SPARKS) { + float dying_norm=objp->ctype.dying_info.delay_time/ELECTRICAL_DEATH_TIME; + dying_norm=min(dying_norm,1.0); + //Do deform stuff + objp->effect_info->type_flags|=EF_DEFORM; + objp->effect_info->deform_time=1.0; + objp->effect_info->deform_range=(1.0-dying_norm)*.2f; + int dying_chance=(dying_norm*10)+1; + //Create random electrical effect + if (Gametime-objp->ctype.dying_info.last_spark_time>.01) + { + if ((ps_rand() % dying_chance)==0) { + int num_bolts=((1.0-dying_norm)*5.0)+3; + CreateElectricalBolts(objp,num_bolts); + } + objp->ctype.dying_info.last_spark_time=Gametime; + } + } + //If fireball death, do fireballs + if (death_flags & DF_DELAY_FIREBALL) { + vector velocity_norm=objp->mtype.phys_info.velocity; + vm_NormalizeVector (&velocity_norm); + vector pos=objp->pos-(velocity_norm*(objp->size/2)); + if (OBJECT_OUTSIDE(objp)) + CreateFireball (&pos,BLACK_SMOKE_INDEX,objp->roomnum,VISUAL_FIREBALL); + // Create an explosion that follows every now and then + if ((Gametime-objp->ctype.dying_info.last_fireball_time>.01) && (ps_rand()%3)==0) + { + if (!(objp->flags & OF_POLYGON_OBJECT)) + return; + objp->ctype.dying_info.last_fireball_time=Gametime; + vector dest; + poly_model *pm=&Poly_models[objp->rtype.pobj_info.model_num]; + bsp_info *sm=&pm->submodel[0]; + int vertnum=ps_rand()%sm->nverts; + GetPolyModelPointInWorld (&dest,&Poly_models[objp->rtype.pobj_info.model_num], &objp->pos, &objp->orient, 0, &sm->verts[vertnum] ); + int visnum=CreateFireball (&dest,GetRandomSmallExplosion(),objp->roomnum,VISUAL_FIREBALL); + if(visnum >= 0) //DAJ this pervents a -1 array index + { + VisEffects[visnum].size+=((ps_rand()%20)/20.0)*3.0; + if ((ps_rand()%2)) + { + VisEffects[visnum].movement_type=MT_PHYSICS; + VisEffects[visnum].velocity=objp->mtype.phys_info.velocity; + VisEffects[visnum].mass=objp->mtype.phys_info.mass; + VisEffects[visnum].drag=objp->mtype.phys_info.drag; + } + } + } + } + //If smoke death, do smoke + if (death_flags & DF_DELAY_SMOKES) { + // see if we should draw some smoke + if ((Gametime-objp->ctype.dying_info.last_smoke_time>.01) && (ps_rand()%3)==0) { // don't draw most of the time + objp->ctype.dying_info.last_smoke_time=Gametime; + + //Make even less likely inside + if (OBJECT_OUTSIDE(objp) || ((ps_rand()%2)==0)) + CreateFireball(&objp->pos,BLACK_SMOKE_INDEX,objp->roomnum,VISUAL_FIREBALL); + } + } +} +extern void SetShakeMagnitude (float delta); +// Pulls every object near the gravity field into its center +void DoGravityFieldEffect (object *obj) +{ + float max_size=obj->ctype.blast_info.max_size; + float lifenorm=(obj->lifetime-obj->lifeleft)/obj->lifetime; + float dist, force; + vector vforce; + int i; + object *hit_obj_ptr; + float time_norm=1.0-(obj->lifeleft/obj->lifetime); + obj->size=max_size; + float sphere_norm=1.0-((lifenorm-.1)*1.5); + if (sphere_norm<0) + sphere_norm=0; + SetShakeMagnitude (25); + float vdist=vm_VectorDistanceQuick (&Viewer_object->pos,&obj->pos); + CreateRandomSparks (3,&obj->pos,obj->roomnum,-1,2); + + if (!(obj->flags & OF_SERVER_SAYS_DELETE)) + { + obj->flags|=OF_SERVER_SAYS_DELETE; + int visnum=VisEffectCreate (VIS_FIREBALL,SUN_CORONA_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->size=max_size/4; + vis->lifeleft=obj->lifetime; + vis->lifetime=obj->lifetime; + vis->flags=VF_USES_LIFELEFT|VF_WINDSHIELD_EFFECT; + } + // Create lightning from the heavens + if (OBJECT_OUTSIDE (obj) && vdist>5) + { + int visnum=VisEffectCreate (VIS_FIREBALL,THICK_LIGHTNING_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=obj->lifetime; + vis->lifetime=obj->lifetime; + vis->end_pos=obj->pos; + + vis->end_pos.y=obj->pos.y+(MAX_TERRAIN_HEIGHT*2); + vis->velocity.x=2; + vis->velocity.y=1; + vis->velocity.z=3; + vis->custom_handle=FindTextureName("ThickLineLightning"); + vis->billboard_info.width=70; + vis->billboard_info.texture=0; + + vis->lighting_color=OPAQUE_FLAG|GR_RGB16(100,150,255); + vis->flags=VF_USES_LIFELEFT|VF_WINDSHIELD_EFFECT; + vis->size=50; + + + } + } + // Create lightning from the heavens + if (!OBJECT_OUTSIDE (obj)) + { + ps_srand(Gametime*100); + int count=0; + // Cast 3 rays out from the center + for (i=0;i<3;i++) + { + again: + vector rand_vector,test_vector; + rand_vector.x=((ps_rand()%100-50)/50.0)+.1; // So we don't get a zero vector + rand_vector.y=((ps_rand()%100)-50)/50.0; + rand_vector.z=((ps_rand()%100)-50)/50.0; + vm_NormalizeVector (&rand_vector); + test_vector=obj->pos+(rand_vector*1000); + vector dest_pos; + int dest_room; + fvi_query fq; + fvi_info hit_info; + int fate; + fq.p0 = &obj->pos; + fq.p1 = &test_vector; + fq.startroom = obj->roomnum; + + fq.rad = 0.0f; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_NON_LIGHTMAP_OBJECTS; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + fate = fvi_FindIntersection(&fq, &hit_info); + dest_pos=hit_info.hit_pnt; + dest_room=hit_info.hit_room; + if (count<5) + { + if (vm_VectorDistance(&obj->pos,&dest_pos)<2) + { + count++; + goto again; + } + else + count=0; + } + int visnum=VisEffectCreate (VIS_FIREBALL,THICK_LIGHTNING_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=obj->lifetime; + vis->lifetime=obj->lifetime; + + vis->end_pos=dest_pos; + + vis->custom_handle=FindTextureName("ThickLineLightning"); + vis->billboard_info.width=25; + vis->billboard_info.texture=1; + vis->velocity.x=2; + vis->velocity.y=1; + vis->velocity.z=.5; + + vis->lighting_color=GR_RGB16(100,150,255); + vis->flags=VF_USES_LIFELEFT|VF_WINDSHIELD_EFFECT|VF_LINK_TO_VIEWER; + vis->size=50; + } + } + } + } + // Create random strands of lightning + for (i=0;i<3 && vdist>5;i++) + { + int visnum=VisEffectCreate (VIS_FIREBALL,LIGHTNING_BOLT_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=Frametime; + vis->lifetime=Frametime; + vis->end_pos=obj->pos; + vis->end_pos.x+=(((float)(ps_rand()%100)-50)/50.0)*sphere_norm*max_size; + vis->end_pos.y+=(((float)(ps_rand()%100)-50)/50.0)*sphere_norm*max_size; + vis->end_pos.z+=(((float)(ps_rand()%100)-50)/50.0)*sphere_norm*max_size; + vis->custom_handle=visnum; + vis->velocity.x=2; + vis->velocity.y=.25; + vis->flags=VF_USES_LIFELEFT; + vis->size=100; + } + } + + for (i=0; i<=Highest_object_index; i++ ) + { + hit_obj_ptr = &Objects[i]; + if (!(hit_obj_ptr->type==OBJ_PLAYER || hit_obj_ptr->type==OBJ_ROBOT || hit_obj_ptr->type==OBJ_WEAPON || hit_obj_ptr->type==OBJ_POWERUP || hit_obj_ptr->type == OBJ_CLUTTER || (hit_obj_ptr->type == OBJ_BUILDING && hit_obj_ptr->ai_info))) + continue; + if (hit_obj_ptr->type==OBJ_WEAPON && !(Weapons[hit_obj_ptr->id].flags & WF_MATTER_WEAPON)) + continue; + if (obj==hit_obj_ptr) + continue; + if (hit_obj_ptr->flags & OF_DEAD) + continue; + if(hit_obj_ptr->movement_type != MT_PHYSICS) + continue; + // The distance is actually the objects' centers minus some of the hit object's radius (I set it to 80%) + if((hit_obj_ptr->flags & OF_POLYGON_OBJECT) && hit_obj_ptr->type != OBJ_WEAPON && hit_obj_ptr->type != OBJ_POWERUP && hit_obj_ptr->type != OBJ_DEBRIS) + { + vector pos; + pos = hit_obj_ptr->pos + hit_obj_ptr->wall_sphere_offset; + dist = vm_VectorDistanceQuick( &pos, &obj->pos ) - Poly_models[hit_obj_ptr->rtype.pobj_info.model_num].wall_size; + } + else + { + dist = vm_VectorDistanceQuick( &hit_obj_ptr->pos, &obj->pos ) - hit_obj_ptr->size; + } + + if(dist < 0.0f) + dist = 0.0f; + if ( dist <= (obj->ctype.blast_info.max_size*2) ) + { + + float dist_norm=1.0-(dist/(obj->ctype.blast_info.max_size*2)); + force=dist_norm*time_norm*30; + //Cap it + if (force>500) + force=500; + + // Find the force vector on the object + vm_GetNormalizedDirFast( &vforce, &hit_obj_ptr->pos, &obj->pos ); + vforce *= (-force); // Sucking vector + if (hit_obj_ptr->mtype.phys_info.mass>1) + vforce*=(hit_obj_ptr->mtype.phys_info.mass*5); + // Don't affect powerups in a multiplayer game + if (!((Game_mode & GM_MULTI) && hit_obj_ptr->type==OBJ_POWERUP)) + phys_apply_force(hit_obj_ptr,&vforce); + if (hit_obj_ptr!=Viewer_object) + { + // Create strands of lightning + int visnum=VisEffectCreate (VIS_FIREBALL,LIGHTNING_BOLT_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=Frametime; + vis->lifetime=Frametime; + vis->end_pos=hit_obj_ptr->pos; + vis->custom_handle=hit_obj_ptr-Objects; + vis->flags=VF_USES_LIFELEFT|VF_NO_Z_ADJUST; + vis->size=dist; + vis->velocity.x=1; + vis->velocity.y=.25; + } + if ((ps_rand()%8)==0) + CreateRandomSparks (ps_rand()%3,&hit_obj_ptr->pos,hit_obj_ptr->roomnum,-1,(ps_rand()%2)+2); + + } + + // Peg the object to the gravity field center if it is close + hit_obj_ptr->mtype.phys_info.flags|=PF_DESTINATION_POS; + hit_obj_ptr->mtype.phys_info.dest_pos=obj->pos; + } + } +} +//do whatever needs to be done for this explosion for this frame +void DoExplosionFrame(object *obj) +{ + ASSERT(obj->control_type == CT_EXPLOSION); + ASSERT (obj->type!=OBJ_PLAYER); + if (obj->id==GRAVITY_FIELD_INDEX) + { + DoGravityFieldEffect(obj); + } + + //See if we should die of old age + if (obj->lifeleft <= 0.0 ) + { + if (obj->id==GRAVITY_FIELD_INDEX) + { + // Create a big ass explosion + if (OBJECT_OUTSIDE (obj)) + CreateBlastRing (&obj->pos,BLAST_RING_INDEX,DAMAGE_RING_TIME,obj->ctype.blast_info.max_size*3,obj->roomnum,1); + else + CreateBlastRing (&obj->pos,BLAST_RING_INDEX,DAMAGE_RING_TIME,obj->ctype.blast_info.max_size,obj->roomnum,1); + + // Blow the damn thing up and register a kill. + + float mag=vm_VectorDistanceQuick(&obj->pos,&Player_object->pos); + if (mag>obj->ctype.blast_info.max_size) + mag=0; + else + mag=1.0-(mag/obj->ctype.blast_info.max_size); + + if (OBJECT_OUTSIDE (Viewer_object)) + AddToShakeMagnitude (mag*obj->ctype.blast_info.max_size*5); + else + AddToShakeMagnitude (mag*obj->ctype.blast_info.max_size*2); + obj->impact_time=0; + obj->impact_size=obj->ctype.blast_info.max_size; + obj->impact_force=obj->impact_size*2; + obj->impact_player_damage=obj->impact_size*3; + obj->impact_generic_damage=obj->impact_size*3; + + if (Game_mode & GM_MULTI) // Do different damage to players if in multiplayer + DoConcussiveForce (obj,obj->parent_handle,1.0f/5.0f); + else + DoConcussiveForce (obj,obj->parent_handle,1.0); + CreateRandomSparks (200,&obj->pos,obj->roomnum,-1,(ps_rand()%2)+2); + + int explode_visnum=CreateFireball (&obj->pos,BLUE_EXPLOSION_INDEX,obj->roomnum,VISUAL_FIREBALL); + if (explode_visnum>=0) + VisEffects[explode_visnum].size=obj->ctype.blast_info.max_size; + } + + // We died of old age + SetObjectDeadFlag (obj); + obj->lifeleft = 0.0; + } +} +void MakeShockwave(object *explode_obj_ptr, int parent_handle) +{ + if(explode_obj_ptr->impact_time > 0.0) + { + object *p = ObjGet(parent_handle); + object *top_parent = p; + int handle; + ASSERT(explode_obj_ptr); + while (p) + { + p = ObjGet(p->parent_handle); + if (p) + { + top_parent = p; + } + } + if(top_parent) + { + handle = top_parent->handle; + } + else + { + handle = OBJECT_HANDLE_NONE; + } + int objnum = ObjCreate(OBJ_SHOCKWAVE, 0, explode_obj_ptr->roomnum, &explode_obj_ptr->pos, NULL, handle); + if(objnum >= 0) + { + Objects[objnum].control_type = CT_NONE; + Objects[objnum].movement_type = MT_SHOCKWAVE; + Objects[objnum].render_type = RT_NONE; + Objects[objnum].lifeleft = explode_obj_ptr->impact_time; + Objects[objnum].impact_size = explode_obj_ptr->impact_size; + Objects[objnum].impact_force = explode_obj_ptr->impact_force; + Objects[objnum].impact_player_damage = explode_obj_ptr->impact_player_damage; + Objects[objnum].impact_generic_damage = explode_obj_ptr->impact_generic_damage; + Objects[objnum].impact_time = explode_obj_ptr->impact_time; + Objects[objnum].flags |= OF_USES_LIFELEFT; + } + } + else + { + DoConcussiveForce(explode_obj_ptr, parent_handle); + } +} +//object *object_create_explosion_sub(object *explode_obj_ptr, short segnum, vms_vector * position, fix size, int vclip_type, fix maxdamage, fix maxdistance, fix maxforce, int parent ) +void DoConcussiveForce(object *explode_obj_ptr, int parent_handle,float player_scalar) +{ + int objnum; + float max_player_damage = explode_obj_ptr->impact_player_damage; + float max_generic_damage = explode_obj_ptr->impact_generic_damage; + float maxforce = explode_obj_ptr->impact_force; + float maxdistance = explode_obj_ptr->impact_size; + float effect_distance; + short parent; + object *parent_obj; + if (explode_obj_ptr->type == OBJ_SHOCKWAVE) + { + effect_distance = explode_obj_ptr->impact_size * ((Gametime - explode_obj_ptr->creation_time) / explode_obj_ptr->impact_time); + if(effect_distance > explode_obj_ptr->impact_size) + effect_distance = explode_obj_ptr->impact_size; + } + else + { + effect_distance = explode_obj_ptr->impact_size; + } + parent_obj = ObjGet(parent_handle); + if (parent_obj != NULL) + parent = OBJNUM(parent_obj); + objnum = OBJNUM(explode_obj_ptr); + if(explode_obj_ptr->impact_size <= 0.0) + return; + { + float dist, force; + vector vforce; + float damage; + int i; + object *hit_obj_ptr; + + for (i=0; i<=Highest_object_index; i++ ) + { + // Weapons used to be affected by badass explosions, but this introduces serious problems. + // When a smart bomb blows up, if one of its children goes right towards a nearby wall, it will + // blow up, blowing up all the children. So I remove it. MK, 09/11/94 +// if ( (hit_obj_ptr!=explode_obj_ptr) && !(hit_obj_ptr->flags&OF_DEAD) && ((hit_obj_ptr->type==OBJ_WEAPON /*&& (hit_obj_ptr->id==PROXIMITY_ID || hit_obj_ptr->id==SUPERPROX_ID || hit_obj_ptr->id==PMINE_ID)*/) || (hit_obj_ptr->type == OBJ_CNTRLCEN) || (hit_obj_ptr->type==OBJ_PLAYER) || ((hit_obj_ptr->type==OBJ_ROBOT) && ((Objects[parent].type != OBJ_ROBOT) || (Objects[parent].id != hit_obj_ptr->id))))) { + hit_obj_ptr = &Objects[i]; + //Check for types that get concussive force + if (!((hit_obj_ptr->type == OBJ_WEAPON) || + (hit_obj_ptr->type == OBJ_ROBOT) || + (hit_obj_ptr->type == OBJ_BUILDING) || + (hit_obj_ptr->type == OBJ_PLAYER) || + (hit_obj_ptr->type == OBJ_DOOR) || + (hit_obj_ptr->type == OBJ_CLUTTER))) + continue; + if((hit_obj_ptr!=explode_obj_ptr) && !(hit_obj_ptr->flags&OF_DEAD)) + { + if(hit_obj_ptr->movement_type == MT_PHYSICS || hit_obj_ptr->movement_type == MT_WALKING) + { + if(hit_obj_ptr->mtype.phys_info.flags & PF_IGNORE_CONCUSSIVE_FORCES) + continue; + } + if(hit_obj_ptr->movement_type == MT_PHYSICS || hit_obj_ptr->movement_type == MT_WALKING) + { + if((hit_obj_ptr->mtype.phys_info.flags & PF_IGNORE_OWN_CONC_FORCES) && ObjGetUltimateParent(hit_obj_ptr) == ObjGetUltimateParent(explode_obj_ptr)) + continue; + } + // The distance is actually the objects' centers minus some of the hit object's radius (I set it to 80%) + if((hit_obj_ptr->flags & OF_POLYGON_OBJECT) && + hit_obj_ptr->type != OBJ_WEAPON && + hit_obj_ptr->type != OBJ_POWERUP && + hit_obj_ptr->type != OBJ_DEBRIS) + { + vector pos; + pos = hit_obj_ptr->pos + hit_obj_ptr->wall_sphere_offset; + dist = vm_VectorDistanceQuick( &pos, &explode_obj_ptr->pos ) - Poly_models[hit_obj_ptr->rtype.pobj_info.model_num].wall_size; + } + else + { + dist = vm_VectorDistanceQuick( &hit_obj_ptr->pos, &explode_obj_ptr->pos ) - hit_obj_ptr->size; + } + + if(dist < 0.0f) + dist = 0.0f; + // Make damage be from 'maxdamage' to 0.0, where 0.0 is 'maxdistance' away; + if ( dist <= effect_distance ) + { + //mprintf((0, "Distance = %f\n", dist)); + if (1/*object_to_object_visibility(obj, hit_obj_ptr, FQ_TRANSWALL)*/) + { + fvi_query fq; + fvi_info hit_info; + int fate; + if (explode_obj_ptr->type == OBJ_SHOCKWAVE) + { + int index = i >> 5; + int bit = i % 32; + if(explode_obj_ptr->mtype.shock_info.damaged_list[index] & (0x00000001 << bit)) + { + continue; + } + else + { + explode_obj_ptr->mtype.shock_info.damaged_list[index] |= (0x00000001 << bit); + } + } + // This section excludes objects that are not visible at shockwave passby time + { + if(!BOA_IsVisible(hit_obj_ptr->roomnum, explode_obj_ptr->roomnum)) + { + continue; + } + vector subvec=hit_obj_ptr->pos-explode_obj_ptr->pos; + vm_NormalizeVectorFast (&subvec); + vector hitvec=(explode_obj_ptr->pos+(subvec*.05f)); + fq.p0 = &hitvec; + fq.p1 = &hit_obj_ptr->pos; + fq.startroom = explode_obj_ptr->roomnum; + + fq.rad = 0.0f; + fq.flags = FQ_CHECK_OBJS | FQ_ONLY_DOOR_OBJ | FQ_BACKFACE | FQ_NO_RELINK; + fq.thisobjnum = OBJNUM(explode_obj_ptr); + fq.ignore_obj_list = NULL; + fate = fvi_FindIntersection(&fq, &hit_info); + if(fate != HIT_NONE) + { + continue; + } + } + if (hit_obj_ptr->type==OBJ_PLAYER) + damage = max_player_damage - (dist/maxdistance)*max_player_damage; + else + damage = max_generic_damage - (dist/maxdistance)*max_generic_damage; + + force = maxforce - (dist/maxdistance)*maxforce; + //mprintf((0, "f %f d %f\n", force, damage)); + // Find the force vector on the object + vm_GetNormalizedDirFast( &vforce, &hit_obj_ptr->pos, &explode_obj_ptr->pos ); + vforce *= force; + + if(hit_obj_ptr->type == OBJ_CLUTTER) // Reduces the ping pong effect + vforce *= .5f; + if(hit_obj_ptr->movement_type == MT_PHYSICS) + phys_apply_force(hit_obj_ptr,&vforce); + //Apply damage to players, generics, and doors + if (damage) { + + if (hit_obj_ptr->type == OBJ_PLAYER) { + if(Demo_flags != DF_PLAYBACK) { + ApplyDamageToPlayer(hit_obj_ptr, parent_obj, PD_CONCUSSIVE_FORCE, damage*player_scalar); + mprintf ((0,"Applying %f damage to player from force.\n",damage)); + } + + // shake player cockpit if damage is pretty bad. + if (hit_obj_ptr->id == Player_num) + { + float mag = damage/200.0f; + vector vec = vforce; + vm_NormalizeVector(&vec); + //mprintf((0, "force: %.1f,%.1f,%.1f mag=%.2f\n", vec.x,vec.y,vec.z,mag)); + StartCockpitShake(mag, &vec); + } + } + else if (IS_GENERIC(hit_obj_ptr->type) || (hit_obj_ptr->type == OBJ_DOOR)) { + if(Demo_flags != DF_PLAYBACK) + ApplyDamageToGeneric(hit_obj_ptr, parent_obj, GD_CONCUSSIVE, damage); + } + } + } + else + { + ; // mprintf((0, "No badass: robot=%2i, dist=%7.3f, maxdistance=%7.3f .\n", i, f2fl(dist), f2fl(maxdistance))); + } // end if (object_to_object_visibility... + } // end if (dist < maxdistance) + } // end for + } + } // end if (maxdamage... +// return obj; +} +void CreateBlueBlastRing(vector *pos,int index,float lifetime,float max_size,int roomnum,int objnum,int force_up) +{ + if (! Viewer_object) + return; + if (objnum==-1) // Create a new object + { + objnum=ObjCreate (OBJ_FIREBALL,index,roomnum,pos,NULL); + if (objnum<0) //DAJ -1FIX + { + mprintf ((0,"Couldn't create blast object!\n")); + return; + } + Objects[objnum].size=1.0; + Objects[objnum].flags|=OF_USES_LIFELEFT; + Objects[objnum].ctype.blast_info.max_size=max_size; + } + matrix tempm; + // Point straight up + + if (ps_rand()%2 || force_up) + { + memset (&tempm,0,sizeof(matrix)); + tempm.rvec.x=1.0; + tempm.uvec.z=-1.0; + tempm.fvec.y=1.0; + } + else + { + // Face the viewer + vector fvec=Viewer_object->pos-*pos; + vm_NormalizeVectorFast (&fvec); + if (vm_GetMagnitudeFast (&fvec)<.5) + return; + vm_VectorToMatrix (&tempm,&fvec,NULL,NULL); + } + ObjSetOrient(&Objects[objnum], &tempm); + Objects[objnum].lifeleft=lifetime/2; + Objects[objnum].lifetime=lifetime/2; + Objects[objnum].ctype.blast_info.bm_handle=Fireballs[BLUE_BLAST_RING_INDEX].bm_handle; +} +// Creates a blast ring to be drawn +int CreateBlastRing (vector *pos,int index,float lifetime,float max_size,int roomnum,int force_blue) +{ + int objnum; + float vals[4]; + fvi_info hit_info; + fvi_query fq; + if (! Viewer_object) + return -1; + // First create a flash if the viewer can see it + fq.p0=&Viewer_object->pos; + fq.p1=pos; + fq.startroom=Viewer_object->roomnum; + + fq.rad=0.01f; + fq.flags=FQ_NO_RELINK; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + int fate = fvi_FindIntersection(&fq,&hit_info); + if (fate==HIT_NONE) + { + vals[0]=1.0; + vals[1]=1.0; + vals[2]=1.0; + vals[3]=.8f; + CreateNewEvent (RENDER_EVENT,0,0,&vals,sizeof(float)*4,DrawAlphaEvent); + } + + objnum=ObjCreate (OBJ_FIREBALL,index,roomnum,pos,NULL); + if (objnum<0) + { + mprintf ((0,"Couldn't create blast object!\n")); + return -1; + } + Objects[objnum].size=max_size; + Objects[objnum].flags|=OF_USES_LIFELEFT; + Objects[objnum].lifeleft=lifetime; + Objects[objnum].lifetime=lifetime; + Objects[objnum].ctype.blast_info.max_size=max_size; + Objects[objnum].ctype.blast_info.bm_handle=Fireballs[index].bm_handle; + if ((ps_rand()%2)) + { + ObjSetOrient(&Objects[objnum], &Identity_matrix); + if ((ps_rand()%3)==0 || force_blue) + CreateBlueBlastRing (pos,index,lifetime,max_size,roomnum,-1,force_blue); + } + else + CreateBlueBlastRing (pos,index,lifetime,max_size,roomnum,objnum,force_blue); + + return objnum; +} +// Creates a standard blast ring for an object +int CreateObjectBlastRing(object *objp) +{ + float ring_size = OBJECT_OUTSIDE(objp) ? (objp->size*3) : objp->size; + return CreateBlastRing(&objp->pos,BLAST_RING_INDEX,DAMAGE_RING_TIME,ring_size,objp->roomnum); +} +// Creates a smolding smoke to be drawn +int CreateSmolderingObject (vector *pos,int index,float lifetime,float max_size,int roomnum) +{ + int objnum; + vector new_pos=*pos; + new_pos.y=GetTerrainGroundPoint (&new_pos); + + objnum=ObjCreate (OBJ_FIREBALL,index,roomnum,&new_pos,NULL); + if (objnum<0) + { + mprintf ((0,"Couldn't create blast object!\n")); + return -1; + } + Objects[objnum].size=1.0; + Objects[objnum].flags|=OF_USES_LIFELEFT; + Objects[objnum].lifeleft=lifetime; + Objects[objnum].lifetime=lifetime; + Objects[objnum].ctype.blast_info.max_size=max_size; + return objnum; +} +// Draws a colored alpha disk...useful for cool lighting effects +void DrawColoredDisk (vector *pos,float r,float g,float b,float inner_alpha,float outer_alpha,float size,ubyte saturate,ubyte lod) +{ + rend_SetZBufferWriteMask (0); + DrawColoredRing (pos,r,g,b,inner_alpha,outer_alpha,size,0,saturate,lod); + rend_SetZBufferWriteMask (1); +} +// Draws a glowing cone of light using a bitmap +void DrawColoredGlow (vector *pos,float r,float g,float b,float size) +{ + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetLighting (LS_GOURAUD); + rend_SetColorModel (CM_RGB); + ddgr_color color=GR_RGB(r*255,g*255,b*255); + int bm_handle=Fireballs[GRADIENT_BALL_INDEX].bm_handle; + g3_DrawBitmap (pos,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color); +} + +// Draws a colored alpha ring...useful for cool lighting effects +void DrawColoredRing(vector *pos,float r,float g,float b,float inner_alpha,float outer_alpha,float size,float inner_ring_ratio,ubyte saturate,ubyte lod) +{ + static vector circleVecs[3][32]; + static int lodSegments[3] = { 32, 16, 8 }; + static bool firstCall = true; + + if( firstCall ) + { + // initialize the circleVecs + firstCall = false; + int lodi; + for( lodi = 0; lodi < 3; ++lodi ) + { + int numSegments = lodSegments[ lodi ]; + int ringIncrement = 65536 / numSegments; + int ringAngle = 0; + int i; + for( i = 0; i < numSegments; ++i, ringAngle += ringIncrement ) + { + float ringSin = FixSin( ringAngle ); + float ringCos = FixCos( ringAngle ); + circleVecs[lodi][i].x = ringCos; + circleVecs[lodi][i].y = ringSin; + circleVecs[lodi][i].z = 0.0f; + } + } + } + + // setup renderer + rend_SetLighting( LS_GOURAUD ); + rend_SetTextureType( TT_FLAT ); + rend_SetOverlayType( OT_NONE ); + rend_SetColorModel( CM_RGB ); + rend_SetFlatColor( GR_RGB( r*255, g*255, b*255 ) ); + rend_SetAlphaType( (saturate) ? AT_SATURATE_VERTEX : AT_VERTEX ); + + // check LOD level + ASSERT( lod >= 0 && lod <= 2 ); + int i, numSegments = lodSegments[ lod ]; + + // get the view matrix + matrix viewOrient; + g3_GetUnscaledMatrix( &viewOrient ); + viewOrient = ~viewOrient; + + // setup the points + g3Point innerPoints[32], outerPoints[32]; + float innerRingSize = size * inner_ring_ratio; + float outerRingSize = size; + for( i = 0; i < numSegments; ++i ) + { + vector worldSpaceCircleVec = circleVecs[lod][i] * viewOrient; + vector innerPos = (*pos) + (worldSpaceCircleVec * innerRingSize); + vector outerPos = (*pos) + (worldSpaceCircleVec * outerRingSize); + + g3_RotatePoint( &innerPoints[i], &innerPos ); + innerPoints[i].p3_flags |= PF_RGBA; + innerPoints[i].p3_a = inner_alpha; + innerPoints[i].p3_r = r; + innerPoints[i].p3_g = g; + innerPoints[i].p3_b = b; + + g3_RotatePoint( &outerPoints[i], &outerPos ); + outerPoints[i].p3_flags |= PF_RGBA; + outerPoints[i].p3_a = outer_alpha; + outerPoints[i].p3_r = r; + outerPoints[i].p3_g = g; + outerPoints[i].p3_b = b; + } + + // and draw... + g3Point *pntList[4]; + for( i = 0; i < numSegments; ++i ) + { + int next = ( i + 1 ) % numSegments; + pntList[0] = &outerPoints[i]; + pntList[1] = &outerPoints[next]; + pntList[2] = &innerPoints[next]; + pntList[3] = &innerPoints[i]; + g3_DrawPoly( 4, pntList, 0 ); + } +} + +// Draws a sphere with the appropriate texture. If texture=-1, then uses rgb as colors +void DrawSphere (vector *pos,float r,float g,float b,float alpha,float size,int texture,ubyte saturate) +{ + static vector sphere_vecs[16][16]; + static int first=1; + g3Point sphere_points[16][16],*pntlist[4],draw_points[4]; + int bm_handle=-1; + ddgr_color color=GR_RGB(r*255,g*255,b*255); + + int i,t; + + if (size<0) + return; + if (first) + { + first=0; + // Create a unit sphere + int increment=65536/16; + for (i=0;i<16;i++) + { + for (t=0;t<16;t++) + { + matrix tempm; + angle pitch,heading; + pitch=i*increment; + heading=t*increment; + + vm_AnglesToMatrix (&tempm,pitch,heading,0); + sphere_vecs[i][t]=tempm.fvec; + } + } + } + rend_SetZBufferWriteMask (0); + draw_points[0].p3_u=0; + draw_points[0].p3_v=0; + draw_points[1].p3_u=1.0+((ps_rand()%100)/500.0); + draw_points[1].p3_v=0; + draw_points[2].p3_u=1.0+((ps_rand()%100)/500.0); + draw_points[2].p3_v=1.0+((ps_rand()%100)/500.0); + draw_points[3].p3_u=0; + draw_points[3].p3_v=1.0+((ps_rand()%100)/500.0); + + if (texture==-1) + { + rend_SetTextureType (TT_FLAT); + rend_SetOverlayType (OT_NONE); + rend_SetColorModel (CM_RGB); + rend_SetLighting (LS_NONE); + rend_SetFlatColor (color); + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (alpha*255.0); + } + else + { + rend_SetTextureType (TT_LINEAR); + rend_SetOverlayType (OT_NONE); + rend_SetColorModel (CM_MONO); + rend_SetLighting (LS_NONE); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (alpha*255.0); + bm_handle=GetTextureBitmap (texture,0); + } + + for (i=0;i<16;i++) + { + for (t=0;t<16;t++) + { + vector temp_vec=*pos+(sphere_vecs[i][t]*size); + g3_RotatePoint (&sphere_points[i][t],&temp_vec); + } + } + for (i=0;i<16;i++) + { + int next_i=(i+1)%16; + for (t=0;t<16;t++) + { + int next_t=(t+1)%16; + draw_points[0] = sphere_points[i][t]; + draw_points[1] = sphere_points[i][next_t]; + draw_points[2] = sphere_points[next_i][next_t]; + draw_points[3] = sphere_points[next_i][t]; + + for (int k=0;k<4;k++) + { + draw_points[k].p3_flags |= PF_UV|PF_RGBA; + g3_CodePoint( &draw_points[k] ); + pntlist[k]=&draw_points[k]; + } + g3_CheckAndDrawPoly(4,pntlist,bm_handle,NULL,&sphere_vecs[0][0]); + } + + } + rend_SetZBufferWriteMask (1); +} +#define BLAST_RING_ALPHA 1.0f +// Draws a blast ring +void DrawBlastRingObject (object *obj) +{ + vector inner_vecs[30],outer_vecs[30]; + g3Point inner_points[30],outer_points[30]; + float lifenorm=(obj->lifetime-obj->lifeleft)/obj->lifetime; + float cur_size=lifenorm*obj->ctype.blast_info.max_size; + int i; + g3Point *pntlist[4]; + static int grav_first=1; + static int grav_texture; + + if (obj->id==GRAVITY_FIELD_INDEX) + cur_size=(1-lifenorm)*obj->ctype.blast_info.max_size; + + int bm_handle=obj->ctype.blast_info.bm_handle; + int num_segments=20; + int ring_increment=65536/num_segments; + int ring_angle=0; + + if (lifenorm>1.0) + lifenorm=1.0; + // First draw a blast sphere + float sphere_norm; + int rot_angle; + // Scale lifetime to make it look like a lensflare + if (lifenorm<.1) + { + sphere_norm=lifenorm*10; + rot_angle=(FrameCount*20)%65536; + } + else + { + sphere_norm=1.0-((lifenorm-.1)*1.5); + rot_angle=(FrameCount*550)%65536; + if (sphere_norm<0) + sphere_norm=0; + } + float sphere_alpha=sphere_norm; + int disk_handle; + float disk_size=sphere_norm*obj->ctype.blast_info.max_size; + if (obj->id==GRAVITY_FIELD_INDEX) + { + rend_SetZBias (-(cur_size/2)); + DrawColoredDisk (&obj->pos,.3f,.5f,1,.9f,0,disk_size,1,0); + // Set states for the ring + rend_SetOverlayType (OT_NONE); + rend_SetLighting(LS_NONE); + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (sphere_alpha*255.0); + rend_SetZBufferWriteMask (0); + + } + else + { + disk_handle=Fireballs[SHRINKING_BLAST_INDEX].bm_handle; + + rend_SetOverlayType (OT_NONE); + rend_SetLighting(LS_NONE); + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (sphere_alpha*255.0); + rend_SetZBufferWriteMask (0); + rend_SetZBias (-(cur_size/2)); + if (sphere_norm>=0.0) + g3_DrawRotatedBitmap (&obj->pos,rot_angle,disk_size,(disk_size*bm_h(disk_handle,0))/bm_w(disk_handle,0),disk_handle); + } + // Now draw a blast ring + if (lifenorm<.2) + { + lifenorm=lifenorm*3; + } + else + lifenorm=.6+(((lifenorm-.2)/.8)*.4); + if (lifenorm>1) + lifenorm=1; + + rend_SetAlphaType (AT_SATURATE_TEXTURE_VERTEX); + rend_SetAlphaValue (255); + ring_angle=0; + cur_size*=2; + float alpha=1.0; + if (lifenorm>.5) + alpha=1.0-((lifenorm-.5)/.5); + for (i=0;iorient.rvec*(ring_cos*cur_size*.50); + inner_vecs[i]+=obj->orient.fvec*(ring_sin*cur_size*.50); + inner_vecs[i]+=obj->pos; + outer_vecs[i]=obj->orient.rvec*(ring_cos*cur_size); + outer_vecs[i]+=obj->orient.fvec*(ring_sin*cur_size); + outer_vecs[i]+=obj->pos; + + g3_RotatePoint (&inner_points[i],&inner_vecs[i]); + g3_RotatePoint (&outer_points[i],&outer_vecs[i]); + outer_points[i].p3_flags |= PF_UV|PF_RGBA; + inner_points[i].p3_flags |= PF_UV|PF_RGBA; + outer_points[i].p3_a=1.0-lifenorm; + inner_points[i].p3_a=0; + } + for (i=0;i=0) + { + vector zero_vec={0,0,0}; + matrix id_mat=Identity_matrix; + + sound_pos.position=&pos; + sound_pos.roomnum=roomnum; + sound_pos.orient=&id_mat; + sound_pos.velocity=&zero_vec; + + if(ps_rand() > RAND_MAX/2) + Sound_system.Play3dSound(SOUND_ROBOT_EXPLODE_1, SND_PRIORITY_HIGH, &sound_pos); + else + Sound_system.Play3dSound(SOUND_ROBOT_EXPLODE_2, SND_PRIORITY_HIGH, &sound_pos); + VisEffects[visnum].size=size; + if (upwards_velocity>.001) + { + VisEffects[visnum].movement_type=MT_PHYSICS; + VisEffects[visnum].phys_flags=PF_FIXED_VELOCITY; + VisEffects[visnum].velocity.x=0; + VisEffects[visnum].velocity.z=0; + VisEffects[visnum].velocity.y=upwards_velocity; + } + } +} +// An event handler that simply draws an alpha blended poly on top of the screen +// Takes a 4 element array of floats int r,g,b,a format +void DrawAlphaEvent (int eventnum,void *data) +{ + float *vals=(float *)data; + + DrawAlphaBlendedScreen (vals[0],vals[1],vals[2],vals[3]); +} +//Returns a random explosion based on the explosion size +int GetRandomExplosion(float size) +{ + if (size > EXTRA_EXPLOSION_THRESHOLD) + return BIG_EXPLOSION_INDEX; + else + return GetRandomMediumExplosion(); +} +// Returns a random medium sized explosion +int GetRandomMediumExplosion () +{ + int choices[]={MED_EXPLOSION_INDEX,MED_EXPLOSION_INDEX2,MED_EXPLOSION_INDEX3}; + int pick=ps_rand()%3; + return (choices[pick]); +} +int GetRandomSmallExplosion () +{ + int choices[]={SMALL_EXPLOSION_INDEX,SMALL_EXPLOSION_INDEX2}; + int pick=ps_rand()%2; + return (choices[pick]); +} +int GetRandomBillowingExplosion () +{ + int choices[]={BILLOWING_INDEX,MED_EXPLOSION_INDEX2}; + int pick=ps_rand()%2; + return (choices[pick]); +} +void DrawSmolderingObject (object *obj) +{ + vector veca; + angvec angs; + vector world_verts[4]; + g3Point pnts[4],*pntlist[4]; + if (! Viewer_object) + return; + vm_ExtractAnglesFromMatrix (&angs,&Viewer_object->orient); + veca.x=FixCos(angs.h); + veca.z=FixSin(angs.h); + veca.y=0; + world_verts[0]=obj->pos-(veca*10); + world_verts[1]=obj->pos+(veca*10); + world_verts[2]=obj->pos+(veca*10); + world_verts[3]=obj->pos-(veca*10); + world_verts[0].y+=obj->ctype.blast_info.max_size; + world_verts[1].y+=obj->ctype.blast_info.max_size; + for (int i=0;i<4;i++) + { + g3_RotatePoint(&pnts[i],&world_verts[i]); + pnts[i].p3_flags |= PF_UV; + pntlist[i]=&pnts[i]; + } + pnts[0].p3_u=0; + pnts[0].p3_v=0; + pnts[1].p3_u=1; + pnts[1].p3_v=0; + pnts[2].p3_u=1; + pnts[2].p3_v=1; + pnts[3].p3_u=0; + pnts[3].p3_v=1; + rend_SetTextureType (TT_LINEAR); + rend_SetLighting (LS_NONE); + rend_SetOverlayType (OT_NONE); + rend_SetAlphaType (AT_CONSTANT+AT_TEXTURE); + rend_SetAlphaValue (.4*255); + + vclip *vc=&GameVClips[Fireballs[obj->id].bm_handle]; + int bm_handle=vc->frames[3]; + g3_DrawPoly (4,pntlist,bm_handle); +} +// Creates end points that simulate lightning +void CreateLightningRodPositions (vector *src,vector *dest,vector *world_vecs,int num_segments,float rand_mag,bool do_flat) +{ + vector delta_vec=*dest-*src; + matrix mat; + + // Setup some interpolants + float mag=vm_GetMagnitudeFast (&delta_vec); + float delta_dist=mag/num_segments; + delta_vec/=mag; + // Create matrix for randomization + vm_VectorToMatrix (&mat,&delta_vec,NULL,NULL); + + vector cur_pos=*src; + + vector from=cur_pos; + world_vecs[0]=from; + world_vecs[num_segments-1]=*dest; + // Create the points + for (int i=1;i +#include "osiris_share.h" +#include "demofile.h" + +/////////////////////////////////////////////////////////////////////////////// +// Variables + +//Vars for game 3D window +int Game_window_x,Game_window_y,Game_window_w,Game_window_h; +int Max_window_w,Max_window_h; + +// The game mode we're in (ie multiplayer vs. single, etc) +int Game_mode=0; + +int sound_override_force_field = -1; +int sound_override_glass_breaking = -1; + +int force_field_bounce_texture[MAX_FORCE_FIELD_BOUNCE_TEXTURES] = {-1, -1, -1}; +float force_field_bounce_multiplier[MAX_FORCE_FIELD_BOUNCE_TEXTURES] = {1.0f, 1.0f, 1.0f}; + +bool Level_powerups_ignore_wind = false; + +//what renderer? +renderer_type PreferredRenderer=RENDERER_OPENGL; + +// Rendering options +rendering_state Render_state; +renderer_preferred_state Render_preferred_state; +int Render_preferred_bitdepth; + +// How hard is this game? +int Difficulty_level=0; + +#ifdef _DEBUG +int Game_show_sphere = 0; +int Game_show_portal_vis_pnts = 0; +int Game_update_attach = 1; +int Game_do_walking_sim = 1; +int Game_do_vis_sim = 1; +int Game_do_flying_sim = 1; +int Game_do_ai_movement = 1; +int Game_do_ai_vis = 1; +#endif + +// How much of the mine has been explored? +int Num_rooms_explored = 0; + +// Save and restores per level +int Num_player_saves = 0; +int Num_player_restores = 0; + +//Missile camera +int Missile_camera_window = SVW_LEFT; //will default to -1 when interface is in + +// contains all relevent information for gamemode pertaining to d3x system. +gamemode Gamemode_info; + + +/////////////////////////////////////////////////////////////////////////////// +// Game setup +/////////////////////////////////////////////////////////////////////////////// + +bool InitGameScript(); +void CloseGameScript(); + +float GetFPS () +{ + if (Frametime == 0.0f) { Frametime = 0.1f; } + return 1.0 / Frametime; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Game Execution +/////////////////////////////////////////////////////////////////////////////// + + +//Setup the game screen +void InitGameScreen(int w, int h) +{ + if (w > Max_window_w) w = Max_window_w; + if (h > Max_window_h) h = Max_window_h; + + Game_window_w = w; + Game_window_h = h; + + Game_window_x = (Max_window_w - Game_window_w) / 2; + Game_window_y = (Max_window_h - Game_window_h) / 2; +} + +//Setup whatever needs to be setup for game mode +bool InitGame() +{ + #ifdef _DEBUG + //Put player in flying mode + SlewStop(Player_object); + #endif + + InitHUD(); + + if(!InitGameScript()) // initializes game script + return false; + + Frametime=0.1f; + Skip_render_game_frame = false; + +// reset controllers. + Controller->mask_controllers((Current_pilot.read_controller&READF_JOY)?true:false, + (Current_pilot.read_controller&READF_MOUSE)?true:false); + + return true; +} + +#ifdef EDITOR +bool Game_being_played_from_quick_play = false; +void QuickPlayGame() +{ + if(InitGame()){ + Game_being_played_from_quick_play = true; + QuickStartMission(); + + // Run the game (note, if this call returns false, we couldn't play a level. Display an error maybe? + GameSequencer(); + QuickEndMission(); + }else{ + + } + Game_being_played_from_quick_play = false; + +// Close down some game stuff +// close down any systems not needed outside game. + CloseGameScript(); + CloseHUD(); + DoorwayDeactivateAll(); // deactivate doorways + ui_RemoveAllWindows(); // remove any ui windows left open. + SuspendControls(); +} +#endif + +void PlayGame() +{ +// Initialize misc game + if(InitGame()){ + // Run the game (note, if this call returns false, we couldn't play a level. Display an error maybe? + GameSequencer(); + }else{ + SetFunctionMode(MENU_MODE); + + //if they were going into a multiplayer game than we need to handle cleaning all that up + if(Game_mode&GM_MULTI){ + SetGameMode(GM_NORMAL); + for(int i=0;ihardware, etc.) +// make sure renderer is initialized +// also set any preferred renderer states. + if (sm == SM_NULL) { // || (sm == SM_CINEMATIC && Renderer_type == RENDERER_OPENGL)) { + if (rend_initted) { + rend_Close(); + rend_initted = 0; + } + } + else if (sm == SM_CINEMATIC) {// && (Renderer_type == RENDERER_OPENGL || Renderer_type == RENDERER_DIRECT3D)) { + if (rend_initted) { + rend_Close(); + rend_initted = 0; + } + } +//#ifdef RELEASE +// else if (sm == SM_CINEMATIC && (Renderer_type == RENDERER_GLIDE) ) { +// if (rend_initted) { +// rend_Close(); +// rend_initted = 0; +// } +// } +//#endif + else { + int scr_width, scr_height, scr_bitdepth; + + if (sm == SM_GAME) + { + scr_width = Video_res_list[Game_video_resolution].width; + scr_height = Video_res_list[Game_video_resolution].height; + scr_bitdepth = Render_preferred_bitdepth; +#ifdef MACINTOSH + SwitchDSpContex(Game_video_resolution); +#endif + } + else + { + scr_width = FIXED_SCREEN_WIDTH; + scr_height = FIXED_SCREEN_HEIGHT; + scr_bitdepth=16; +#ifdef MACINTOSH + SwitchDSpContex(0); +#endif + } + + if (!rend_initted) { + + Render_preferred_state.width=scr_width; + Render_preferred_state.height=scr_height; + Render_preferred_state.bit_depth=scr_bitdepth; + + rend_initted = rend_Init (PreferredRenderer, Descent,&Render_preferred_state); + rend_width = rend_height = 0; + } + else { + + //If bitdepth changed but not initting, switch bitdepth + if (Render_preferred_state.bit_depth != scr_bitdepth) { + Render_preferred_state.bit_depth = scr_bitdepth; + rend_SetPreferredState(&Render_preferred_state); + } + } + + if (!rend_initted) { + Error(rend_GetErrorMessage()); + } + else { + int t=FindArg ("-ForceStateLimited"); + if (t) { + StateLimited = (atoi((const char *)GameArgs[t+1]) != 0); + } + + + if (rend_initted==-1) + { + // We're using the default, so change some values for the menus + rend_initted=1; + mprintf ((0,"Changing menu settings to default!\n")); + Game_video_resolution = RES_640X480; + Render_preferred_state.bit_depth=16; + scr_width=640; + scr_height=480; + } + + if (rend_width != scr_width || rend_height != scr_height) + { + Render_preferred_state.width=scr_width; + Render_preferred_state.height=scr_height; + Render_preferred_state.bit_depth=scr_bitdepth; + + mprintf ((0,"Setting rend_width=%d height=%d\n",scr_width,scr_height)); + int retval=rend_SetPreferredState (&Render_preferred_state); + + if (retval==-1) + { + // We're using the default, so change some values for the menus + rend_initted=1; + mprintf ((0,"Changing menu settings to default!\n")); + Game_video_resolution = RES_640X480; + Render_preferred_state.bit_depth=16; + scr_width=640; + scr_height=480; + Render_preferred_state.width=scr_width; + Render_preferred_state.height=scr_height; + } + } + } + } + + if (rend_initted) { + // Get the amount of video memory + Low_vidmem=rend_LowVidMem (); + + if (FindArg ("-hividmem")) + Low_vidmem=0; + + // get current render width and height. + rend_GetRenderState (&rs); + rend_width=rs.screen_width; + rend_height=rs.screen_height; + + // sets up the screen resolution for the system + if (!UseHardware) { + ddvid_SetVideoMode(rend_width,rend_height,BPP_16, true); + } + else { + if (PreferredRenderer==RENDERER_OPENGL) { + ddvid_SetVideoMode(rend_width,rend_height,BPP_16, false); + } + } + + // chose font. + SelectHUDFont(rend_width); + + //Setup the screen + Max_window_w = rend_width; + Max_window_h = rend_height; + + InitGameScreen(Max_window_w, Max_window_h); + + // initialize ui system again + ui_SetScreenMode(Max_window_w, Max_window_h); + + mprintf ((0,"rend_width=%d height=%d\n",Max_window_w,Max_window_h)); + } + +// assign current screen mode + Screen_mode = sm; + old_sm = sm; + +// Adjust mouse mapping to current screen +// do screen mode stuff + switch (sm) + { + case SM_GAME: + { + ui_HideCursor(); + SetUICallback(NULL); + int gw,gh; + Current_pilot.get_hud_data(NULL,NULL,NULL,&gw,&gh); + if (force_res_change) { + gw = Max_window_w; + gh = Max_window_h; + } + InitGameScreen(gw, gh); + // need to do this since the pilot w,h could change. + Current_pilot.set_hud_data(NULL,NULL,NULL,&Game_window_w,&Game_window_h); + break; + } + + case SM_MENU: + { + // sets up the menu screen + SetUICallback(DEFAULT_UICALLBACK); + ui_ShowCursor(); + break; + } + + case SM_CINEMATIC: + { + SetMovieProperties(0,0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT, (rend_initted) ? Renderer_type : RENDERER_NONE); + break; + } + + case SM_NULL: + { + // cleans up + return; + } + } + + mprintf ((0,"NEW rend_width=%d height=%d\n",Max_window_w,Max_window_h)); + +// mark res change as false. + +#ifdef EDITOR + extern unsigned hGameWnd; +// HACK!!! In editor, to get things working fine, reassert window handle attached to game screen +// is the topmost window, since in the editor, if we're fullscreen the parent window is still +// the editor window, the screen would belong to the editor window. + tWin32AppInfo appinfo; + Descent->get_info(&appinfo); + ddvid_SetVideoHandle(hGameWnd); +#endif +} + + +// These functions are called to start and end a rendering frame +typedef struct tFrameStackFrame +{ + int x1,x2,y1,y2; + bool clear; + tFrameStackFrame *next; + tFrameStackFrame *prev; +}tFrameStackFrame; +tFrameStackFrame *FrameStackRoot = NULL; +tFrameStackFrame *FrameStackPtr = NULL; +tFrameStackFrame FrameStack[8]; +int FrameStackDepth = 0; + +void FramePush(int x1,int y1,int x2,int y2,bool clear) +{ + tFrameStackFrame *curr = FrameStackPtr; + + if(!curr){ + ASSERT( !FrameStackRoot ); + + //we need to allocate for the root +// curr = FrameStackRoot = FrameStackPtr = (tFrameStackFrame *)mem_malloc(sizeof(tFrameStackFrame)); + curr = FrameStackRoot = FrameStackPtr = &FrameStack[0]; + if(!curr){ + Error("Out of memory\n"); + } + + curr->prev = NULL; + curr->next = NULL; + }else{ + //add on to the end of the list + curr->next = FrameStackPtr = &FrameStack[FrameStackDepth]; +// curr->next = FrameStackPtr = (tFrameStackFrame *)mem_malloc(sizeof(tFrameStackFrame)); + if(!curr->next){ + Error("Out of memory\n"); + } + curr->next->prev = curr; //setup previous frame + curr = curr->next; + curr->next = NULL; + } + + //at this point curr should be a valid frame, with prev and next set + curr->x1 = x1; + curr->x2 = x2; + curr->y1 = y1; + curr->y2 = y2; + curr->clear = clear; + FrameStackDepth++; + //DAJ + if(FrameStackDepth > 7) { + mprintf((2, "FrameStack Overflow\n")); + Int3(); + } +} + +void FramePop(int *x1,int *y1,int *x2,int *y2,bool *clear) +{ + if(!FrameStackRoot || !FrameStackPtr){ + mprintf((0,"StartFrame/EndFrame mismatch\n")); + Int3(); + *clear = true; + *x1 = Game_window_x; + *y1 = Game_window_y; + *x2 = *x1 + Game_window_w; + *y2 = *y1 + Game_window_h; + return; + } + + tFrameStackFrame *frame = FrameStackPtr; + + *x1 = FrameStackPtr->x1; + *x2 = FrameStackPtr->x2; + *y1 = FrameStackPtr->y1; + *y2 = FrameStackPtr->y2; + *clear = FrameStackPtr->clear; + + if(frame==FrameStackRoot){ + //we're popping off the root +//DAJ mem_free(FrameStackRoot); + FrameStackRoot = NULL; + FrameStackPtr = NULL; + }else{ + //we're just going back a frame, but still have a stack + FrameStackPtr = FrameStackPtr->prev; //pop back a frame + FrameStackPtr->next = NULL; +//DAJ mem_free(frame); + } + FrameStackDepth--; +} + +//peeks at the current frame +// returns false if there is no current frame +bool FramePeek(int *x1,int *y1,int *x2,int *y2,bool *clear) +{ + if(!FrameStackPtr) + return false; + + *x1 = FrameStackPtr->x1; + *x2 = FrameStackPtr->x2; + *y1 = FrameStackPtr->y1; + *y2 = FrameStackPtr->y2; + *clear = FrameStackPtr->clear; + return true; +} + +void StartFrame(bool clear) +{ + StartFrame(Game_window_x, Game_window_y, Game_window_x+Game_window_w,Game_window_y+Game_window_h, clear); +} + +void StartFrame(int x, int y, int x2, int y2, bool clear,bool push_on_stack) +{ + static float last_fov=-1; + // Check to see if our FOV has changed since last frame + if (last_fov!=Render_FOV) + { + // Figure out new zoom factor + float num=(Render_FOV/2); + num=(3.14*(float)num/180.0); + Render_zoom=tan(num); + + last_fov=Render_FOV; + } + +// for software renderers perform frame buffer lock. + if (Renderer_type == RENDERER_SOFTWARE_16BIT) { + int w, h, color_depth, pitch; + ubyte *data; + + ddvid_GetVideoProperties(&w, &h, &color_depth); + ddvid_LockFrameBuffer(&data, &pitch); + rend_SetSoftwareParameters(ddvid_GetAspectRatio(), w, h, pitch, data); + } + + if(push_on_stack) + { + //push this frame onto the stack + FramePush(x,y,x2,y2,clear); + } + + rend_StartFrame (x,y,x2,y2); + if (Renderer_type == RENDERER_SOFTWARE_16BIT && clear) { + rend_FillRect(GR_RGB(0,0,0), x,y,x2,y2); + } + grtext_SetParameters(0,0,(x2-x),(y2-y)); +} + +// retrives the settings of the last call to StartFrame +bool GetFrameParameters(int *x1,int *y1,int *x2,int *y2) +{ + return false; + /* + if(!Frame_inside) + return false; + *x1 = Frame_x1; + *y1 = Frame_y1; + *x2 = Frame_x2; + *y2 = Frame_y2; + return true; + */ +} + + +void EndFrame() +{ + //@@Frame_inside = false; + rend_EndFrame(); + +// for software renderers perform unlock on frame buffer. + if (Renderer_type == RENDERER_SOFTWARE_16BIT) { + ddvid_UnlockFrameBuffer(); + } + + //pop off frame + int x1,x2,y1,y2; + bool clear; + + FramePop(&x1,&y1,&x2,&y2,&clear); //pop off frame just ending + + //see if there is a frame on the stack...if so, restore it's settings + if(FramePeek(&x1,&y1,&x2,&y2,&clear)) + { + //restore this frame + StartFrame(x1,y1,x2,y2,clear,false); + } +} + +// Does a screenshot and tells the bitmap lib to save out the picture as a tga +void DoScreenshot () +{ + int bm_handle; + int count; + char str[255],filename[255]; + CFILE *infile; + int done=0; + int width=640,height=480; + + if (UseHardware) + { + rendering_state rs; + rend_GetRenderState (&rs); + width=rs.screen_width; + height=rs.screen_height; + } + + + bm_handle=bm_AllocBitmap (width,height,0); + if (bm_handle<0) + { + AddHUDMessage(TXT_ERRSCRNSHT); + return; + } + + StopTime(); + + // Tell our renderer lib to take a screen shot + rend_Screenshot (bm_handle); + + // Find a valid filename + count=1; + while (!done) + { + sprintf (str,"Screenshot%.3d.tga",count); + ddio_MakePath (filename,Base_directory,str,NULL); + infile=(CFILE *)cfopen (filename,"rb"); + if (infile==NULL) + { + done=1; + continue; + } + else + cfclose (infile); + + count++; + if(count>999) + break; + } + + strcpy (GameBitmaps[bm_handle].name,str); + + // Now save it + bm_SaveBitmapTGA (filename,bm_handle); + if(Demo_flags != DF_PLAYBACK) + { + AddHUDMessage (TXT_SCRNSHT,filename); + } + + // Free memory + bm_FreeBitmap (bm_handle); + + StartTime(); +} + diff --git a/Descent3/game.h b/Descent3/game.h new file mode 100644 index 000000000..91206dd7b --- /dev/null +++ b/Descent3/game.h @@ -0,0 +1,344 @@ +/* + * $Logfile: /DescentIII/Main/game.h $ + * $Revision: 55 $ + * $Date: 9/14/01 4:47p $ + * $Author: Matt $ + * + * Game management + * + * $Log: /DescentIII/Main/game.h $ + * + * 55 9/14/01 4:47p Matt + * Cleaned up problems when screen bit depth set to 32. + * + * 54 10/14/99 1:08p Chris + * Added another custom bounce force field texture slot + * + * 53 10/13/99 2:19p Chris + * Added the ability to disable wind for powerups on a level + * + * 52 10/13/99 10:41a Chris + * Added 'special forcefields' that have custom bounce factors + * + * 51 10/08/99 4:29p Chris + * Added the forcefield and glass breaking override options + * + * 50 4/15/99 1:38a Jeff + * changes for linux compile + * + * 49 4/14/99 10:33p Jeff + * fixed missing extern + * + * 48 3/19/99 12:54p Jeff + * base support for requesting the number of teams for a multiplayer game + * + * 47 2/21/99 12:28p Matt + * Added terrain sound system + * + * 46 2/16/99 12:00p Samir + * added new video resolution swtich test. + * + * 45 2/10/99 2:41p Chris + * Added debug info + * + * 44 2/09/99 9:59a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 43 2/08/99 7:50p Jeff + * made StartFrame/EndFrame stack based...no longer have to worry about + * preserving current frame settings to restore + * + * 42 2/08/99 12:04a Jeff + * added a function to get the current StartFrame parameters (from last + * call to StartFrame) + * + * 39 1/10/99 10:23p Jeff + * added initial start/end boss intro function + * + * 38 10/22/98 2:58p Chris + * Difficulty levels are in beta + * + * 37 10/15/98 8:55a Matt + * Took out Endlevel() declaration, since no one should be calling this + * function except the gamesequence code. + * + * 36 10/14/98 7:30p Matt + * Changed StartEndlevelSeqence to take a time parameter for how long to + * delay before ending the level. + * + * 35 10/14/98 12:44a Matt + * Cleaned up endlevel sequence system (though it still doesn't work -- + * ChrisP is going to look at the path-following). + * + * 34 10/05/98 11:06a Chris + * Level goals version .9 + * + * 33 9/01/98 4:11p Samir + * moved screenshot code from gameloop to game.cpp + * + * 32 6/22/98 12:00p Chris + * Working on sound system to make it in a nice shape. + * + * 31 4/14/98 9:18p Samir + * redid post level results. + * + * 30 3/19/98 9:19p Samir + * removed dependency of hud.h on game.h. + * + * 29 3/17/98 2:37p Samir + * reorg of hud/gauge/cockpit dependencies. + * + * 28 3/13/98 6:55p Craig + * + * 27 3/11/98 4:58p Chris + * Changed Show_game_sphere to 3 types + * + * 26 3/10/98 5:15p Chris + * DEL+B shows the bounding-sphere of an object + * + * 25 2/14/98 10:48p Jason + * got preferred rendering working + * + * 24 2/13/98 10:57a Samir + * Changed some gamescript initialization. + * + * 23 2/12/98 5:08p Matt + * Reset cockpit mode when starting a level. Unfortunately, this involved + * some semi-major mucking with game sequencing. + * + * 22 2/12/98 4:46p Matt + * Added needed include + * + * 21 2/12/98 4:28p Matt + * Added function to start endlevel in-game "cutscene" + * + * 20 2/11/98 4:35p Samir + * SetGameMode fully implented takes a script. + * + * 19 2/10/98 11:28a Samir + * Added gamemode info support + * + * 18 2/09/98 3:20p Matt + * Added missile camera system + * + * 17 2/08/98 5:01p Samir + * New game sequencing changes. + * + * 16 2/02/98 7:35p Samir + * Call QuickPlayGame to play game using quick mission system. + * + * 15 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 14 1/13/98 4:27p Samir + * Fixed clear screen strangeness. + * + * 13 1/05/98 10:51a Samir + * Changed number of parameters in StartFrame + * + * 12 12/29/97 5:46p Samir + * Added new frame start and end functions. + * + * 11 11/10/97 12:35p Samir + * Added SM_CINEMATIC screen mode, and make sure we don't reinit renderer + * for hardware rendering between two screen modes that use hardware. + * + * 10 10/28/97 6:36p Samir + * Moved SetCockpitMode to gauges.cpp + * + * 9 10/28/97 12:46p Samir + * Moved SetCockpitMode to new gauges.cpp + * + * 8 10/16/97 2:32p Samir + * Fixed up some timing problems. + * + * 7 10/10/97 3:56p Samir + * Added a cockpit mode selection function. + * + * 6 10/02/97 12:36p Samir + * Redid game sequencing flow. + * + * $NoKeywords: $ + */ + + +#ifndef _GAME_H +#define _GAME_H + +#include "pserror.h" +#include "renderer.h" +#include "object.h" + +// return 0 if we wan't to return to the menu, or return 1 if everything +// is okay. starts a new game based off the current mission. +void PlayGame(void); + +// meant for 'instant' action (usually run from editor, but...) +void QuickPlayGame(); + +float GetFPS (); + +//Stop the Frame_time clock +void StopTime(void); + +//Restart the Frame_time clock +void StartTime(void); + +//Compute how long last frame took +void CalcFrameTime(void); + +// Initialize frame timer +void InitFrameTime(void); + + +//Sets screen mode +const int SM_NULL = 0, + SM_GAME = 1, + SM_MENU = 2, + SM_CINEMATIC = 3; + +void SetScreenMode(int sm, bool force_res_change=false); +int GetScreenMode(); + +// ALWAYS CALL THESE TO START AND END RENDERING +void StartFrame(bool clear=true); +void StartFrame(int x, int y, int x2, int y2, bool clear=true,bool push_on_stack=true); +void EndFrame(); + +// retrives the settings of the last call to StartFrame +// returns false if it's not currently in between a StartFrame/EndFrame block +bool GetFrameParameters(int *x1,int *y1,int *x2,int *y2); + +// call this to set the game mode +void SetGameMode(int mode); + +//How long (in seconds) the last frame took +extern float Frametime; + +//How long (in seconds) since the game started +extern float Gametime; + +// set this to clear the screen X number of times. +extern int Clear_screen; + +//How many frames have been renered. +//NOTE: this is a count of 3d frames, not game frames +extern int FrameCount; + +//Vars for game 3D window +extern int Game_window_x,Game_window_y,Game_window_w,Game_window_h; +extern int Max_window_w,Max_window_h; + +extern int Game_mode; +extern int Difficulty_level; + +extern int sound_override_force_field; +extern int sound_override_glass_breaking; + +#define MAX_FORCE_FIELD_BOUNCE_TEXTURES 3 +extern int force_field_bounce_texture[MAX_FORCE_FIELD_BOUNCE_TEXTURES]; +extern float force_field_bounce_multiplier[MAX_FORCE_FIELD_BOUNCE_TEXTURES]; + +extern bool Level_powerups_ignore_wind; + +#ifdef _DEBUG +extern int Game_show_sphere; +extern int Game_show_portal_vis_pnts; +extern int Game_update_attach; +extern int Game_do_walking_sim; +extern int Game_do_vis_sim; +extern int Game_do_flying_sim; +extern int Game_do_ai_movement; +extern int Game_do_ai_vis; +#endif + +extern bool UseHardware; +extern renderer_type PreferredRenderer; +// State variables for our renderer +extern rendering_state Render_state; +extern renderer_preferred_state Render_preferred_state; +extern int Render_preferred_bitdepth; + +#define GM_SINGLE 1 // Single player game. +//#define GM_SERIAL 2 // You are in serial mode (Jason doesn't like this.) +#define GM_NETWORK 4 // You are in network mode +#define GM_MODEM 32 // You are in a modem (serial) game + +#define GM_GAME_OVER 128 // Game has been finished + +#define GM_NONE 0 // You are not in any mode, kind of dangerous... +#define GM_NORMAL 1 // You are in normal play mode, no multiplayer stuff +#define GM_MULTI (GM_NETWORK + GM_MODEM) // You are in some type of multiplayer game + +/* +struct trigger; +struct tD3XThread; +struct tD3XProgram; +*/ + +typedef struct gamemode { + char scriptname[64]; + int requested_num_teams; +/* + tD3XProgram *d3xmod; + tD3XThread *d3xthread; + int objmehandle; + int objithandle; + trigger *trigme; + trigger *trigit; +*/ +} +gamemode; + +//Structure for a terrain sound "band" +typedef struct { + int sound_index; //the sound to play + ubyte low_alt,high_alt; //top & bottom of range of sound + float low_volume,high_volume; //volume at top & bottom of range +} terrain_sound_band; + +//How many terrain sound bands we have +#define NUM_TERRAIN_SOUND_BANDS 5 + +//The terrain sound bands for the current level +extern terrain_sound_band Terrain_sound_bands[]; + +//Clear out all the terrain sound bands +void ClearTerrainSound(); + +//Starts the sound on the terrain +void StartTerrainSound(); + +//Missile camera. If disabled, this is -1. Otherwise, it's a window number (see SmallViews.h). +extern int Missile_camera_window; + +// contains all relevent information for gamemode pertaining to d3x system. +extern gamemode Gamemode_info; + +//Starts the game-engine "cutscene" +//Puts the player in AI mode, sets the view to an external camera, switches to letterbox, & puts the player on a path +//Parameters: camera - the camera for the view +// pathnum - the path the player should follow +// time - if > 0.0, how long the sequence plays before the level ends +void StartEndlevelSequence(object *camera,int pathnum,float time); + + +inline void ResetGamemode() +{ + Gamemode_info.scriptname[0] = 0; + Gamemode_info.requested_num_teams = 1; + /* + Gamemode_info.objmehandle = 0; + Gamemode_info.objithandle = 0; + Gamemode_info.trigme = 0; + Gamemode_info.trigit = 0; + */ +} + +void SetGamemodeScript(const char *scrfilename,int num_requested_teams=-1); + +// Does a screenshot and tells the bitmap lib to save out the picture as a tga +void DoScreenshot (); + +#endif diff --git a/Descent3/gamecinematics.cpp b/Descent3/gamecinematics.cpp new file mode 100644 index 000000000..f22587867 --- /dev/null +++ b/Descent3/gamecinematics.cpp @@ -0,0 +1,2624 @@ +/* +* $Logfile: /DescentIII/main/gamecinematics.cpp $ +* $Revision: 49 $ +* $Date: 4/06/00 9:25a $ +* $Author: Matt $ +* +* In-Game Cinematics system +* +* $Log: /DescentIII/main/gamecinematics.cpp $ + * + * 49 4/06/00 9:25a Matt + * Fixed a screen clear problem on at ATI Rage Fury Maxx in dual-chip mode + * by forcing the screen to clear four times (instead of three). + * + * 48 10/08/99 4:14p 3dsmax + * complex cinematic paths with only one node issue fixed. + * + * 47 5/22/99 12:07a Jeff + * properly set the target handle when faking a canned cinematic + * + * 46 5/19/99 12:52p Samir + * clear player weapon firing when entering cinematics, + * + * 45 5/07/99 7:48p Jeff + * fixed demo recording during cinematics + * + * 44 5/07/99 1:44p Chris + * Made in-game cinematics use the ORIENT_PATH_NODE for player ships + * + * 43 5/04/99 6:52p Jeff + * added new canned cinematic to fade screen to white and endlevel. Fixed + * crash bug with invalid player path for canned cine with player paths + * + * 42 4/28/99 3:32a Jeff + * created cinematic level reset function + * + * 41 4/27/99 5:58p Jeff + * fixed for coop + * + * 40 4/27/99 5:41p Jeff + * hopefully fixed coop/multiplayer issues + * + * 39 4/24/99 2:20a Chris + * Added the Neutral_till_hit flag + * + * 38 4/15/99 11:40a Jeff + * changes layout of igc intro + * + * 37 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 36 4/06/99 4:34p Jeff + * fixed screenmode bug during endlevel + * + * 35 4/06/99 2:54p Jeff + * fixed 1 node end level sequence path bug + * + * 34 4/02/99 11:33p Jeff + * able to turn off IGC in debug mode. Better handling of cinematics + * quick exit if playing via alt-p + * + * 33 3/31/99 8:34p Jeff + * different bitmap for trans + * + * 32 3/30/99 4:47p Jeff + * added level events for when IGC occurs for a player + * + * 31 3/27/99 7:22p Jeff + * fixed cinematics when going from one cut to another immediatly. Added + * start transition + * + * 30 3/19/99 9:39a Nate + * Jeff - fixed crash bug + * + * 29 3/17/99 11:48a Jeff + * intro cams are letterbox + * + * 28 3/10/99 6:44p Jeff + * fixed exported game cinematic function for canned cinematics, so it's + * prototype doesn't change (oem patch friendly) + * + * 27 3/10/99 6:20p Jeff + * many fixes to demo system. Fixed IGC so cameras move...fixed osiris to + * be restored correctly, and it handles errors on restore + * + * 26 3/10/99 11:27a Jeff + * fixed saving cinematic data when writing cinematics that use paths + * + * 25 3/08/99 3:24p Jeff + * fixed end level sequencing where if the level ended while the player + * was dead. + * + * 24 3/08/99 12:04p Jeff + * fixed ship jitter in in-game cinematics (intro/end level) + * + * 23 3/04/99 3:57p Jeff + * make player's burners on in intro/end level seqeunces if on a path + * + * 22 3/03/99 1:18p Jeff + * turn off afterburner + * + * 21 3/01/99 5:35p Jeff + * didn't change much + * + * 20 2/28/99 8:31p Jeff + * added fade and move player dallas action. Fixed the end-level sequence + * changing view back to player for split second. + * + * 19 2/28/99 6:55p Jeff + * orient the player correctly when 1 node path for level intro + * + * 18 2/26/99 6:38p Mark + * fixed screen clearing problem + * + * 17 2/23/99 12:44a Jeff + * added support for in-game-cinematics in demo system + * + * 16 2/22/99 1:20a Jeff + * added support for inventory (simple) in dallas. Moved end-level + * sequence to use IGC. Add position clipboard stuff for dallas. Fixed + * some inventory bug with storing object handles + * + * 15 2/21/99 8:35p Jeff + * misc changes to handle new matcen and path types of dallas + * + * 14 2/20/99 4:15p Jeff + * add cinematic text to game messages + * + * 13 2/18/99 6:47p Jeff + * added call to start/stop special cinematics music + * + * 12 2/18/99 3:00p Jeff + * smoother rotation/autolevel + * + * 11 2/16/99 9:36p Jeff + * new add gamefile dialog + * + * 10 2/16/99 6:15p Jeff + * pause hud messages when in cinematics + * + * 9 2/14/99 1:16a Jeff + * added canned cinematic function/structures. Added a flag to push + * target to end of path if on a path (quick exit). Added canned intro + * cine. Determine correct velocity on player ship for intro cine. + * + * 8 2/12/99 2:45a Jeff + * added end-cinematic transitions + * + * 7 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 6 2/04/99 3:56p Jeff + * no cinematics in multiplayer + * + * 5 2/02/99 3:58p Jeff + * started to implement level intro cinematic (need ai functions first). + * No longer need camera object to do cinematic (auto-created)...path + * cameras use speed based on distance needed to travel. + * + * 4 2/02/99 12:32p Jeff + * added ai notify events for movie start and stop + * + * 3 2/01/99 12:55p Jeff + * restore correct hud mode, added flag to stop cinematics if target dies + * + * 2 1/31/99 8:48p Jeff + * new in game cinematics system finished +* +* $NoKeywords: $ +*/ + +#include "pstypes.h" +#include "pserror.h" +#include "grtext.h" +#include "renderer.h" +#include "gamecinematics.h" +#include "vecmat.h" +#include "mem.h" +#include "gamepath.h" +#include +#include +#include +#include "game.h" +#include "AIGoal.h" +#include "aipath.h" +#include "objinfo.h" +#include "player.h" +#include "ddio.h" +#include "gamefont.h" +#include "AIMain.h" +#include "controls.h" +#include "d3music.h" +#include "gamesequence.h" +#include "demofile.h" +#include "osiris_dll.h" +#include "multi.h" +#include "weapon.h" + +#ifdef _DEBUG +bool Cinematics_enabled = true; +extern int DoAI; +#ifdef EDITOR +extern bool Game_being_played_from_quick_play; +#endif +#endif +extern unsigned short Demo_obj_map[MAX_OBJECTS]; + +struct{ + // some important, pre-computed times + float text_display_start,text_display_stop; + float track_start,track_stop; + float player_start,player_stop; + float camera_start,camera_stop; + float exit_start,exit_end; + float end_time; + + void (*callback)(int type); + + int pathnum; + int room; + vector position; + + int target_objhandle; + int camera_objhandle; + + int bmp_handle; + int longest_line; + + uint flags; + + tHUDMode old_hudmode; + + int num_lines; + int end_transition_type; //what kind of ending transition should the cinematic have + int start_transition_type; //what kind of starting transition should the cinematic have + char *text; + bool target_triggered; //whether the notify event has been sent to the object yet + + bool doing_end_transition; //we're processing an end transition still + float end_transition_start_time; //Gametime that the end transition started + float end_transition_end_time; //Gametime that the end transition should end + + float start_transition_start_time; + float start_transition_end_time; + + //some transition data + float early_trans_end_start_time; + + bool controls_suspended; +}GameCinema; + +#define CDI_NOT_CANNED 0 +#define CDI_CANNED 1 + +typedef struct +{ + ubyte cinematic_type; //canned/not canned + int camera_objhandle; //object handle for camera to use + + char *text_string; //not canned text string + + union + { + tCannedCinematicInfo *canned_data; //data to start a canned cinematic + tGameCinematic *cinematic_data; //data to start a regular cinematic + }; + +}tCinematicDemoInfo; + +void Cinematic_WriteDemoFileData(tCinematicDemoInfo *info); +bool Cinematic_StartCine(tGameCinematic *info,char *text_string,int camera_objhandle); + +void Cinematic_DrawText(void); +void Cinematic_DoEndTransition(void); +void Cinematic_DoStartTransition(void); + +bool Cinematic_inuse; +tGameCinematic Cinematic_fake_info; +bool Cinematic_fake_queued = false; + +int gc_fade_bmp_handle = BAD_BITMAP_HANDLE; +int gc_fadewhite_bmp_handle = BAD_BITMAP_HANDLE; +int gc_wacky_bmp_handle = BAD_BITMAP_HANDLE; +int gc_temp_bmp_handle = BAD_BITMAP_HANDLE; + +#define END_TRANSITION_TIME 1.0f + +float Cine_GetPathTravelSpeed(int pathnum,float time); + +// Returns the hud mode before cinematics +tHUDMode Cinematic_GetOldHudMode(void) +{ + return GameCinema.old_hudmode; +} + +// Cinematic_Close +// +// Closes the in-game cinematics +void Cinematic_Close(void) +{ + if(Cinematic_inuse){ + Cinematic_Stop(); + } + + if(gc_fade_bmp_handle>BAD_BITMAP_HANDLE) + { + bm_FreeBitmap(gc_fade_bmp_handle); + gc_fade_bmp_handle = BAD_BITMAP_HANDLE; + } + + if(gc_fadewhite_bmp_handle>BAD_BITMAP_HANDLE) + { + bm_FreeBitmap(gc_fadewhite_bmp_handle); + gc_fadewhite_bmp_handle = BAD_BITMAP_HANDLE; + } + + if(gc_temp_bmp_handle>BAD_BITMAP_HANDLE) + { + bm_FreeBitmap(gc_temp_bmp_handle); + gc_temp_bmp_handle = BAD_BITMAP_HANDLE; + } + + if(gc_wacky_bmp_handle>BAD_BITMAP_HANDLE) + { + bm_FreeBitmap(gc_wacky_bmp_handle); + gc_wacky_bmp_handle = BAD_BITMAP_HANDLE; + } + + GameCinema.doing_end_transition = false; +} + +// Cinematic_Init +// +// Initializes the in-game cinematics +void Cinematic_Init(void) +{ + static bool called_once = false; + Cinematic_inuse = false; + + memset(&GameCinema,0,sizeof(GameCinema)); + + GameCinema.doing_end_transition = false; + gc_fade_bmp_handle = BAD_BITMAP_HANDLE; + + gc_fade_bmp_handle = bm_AllocBitmap(32,32,0); + if(gc_fade_bmp_handle>BAD_BITMAP_HANDLE) + { + ushort *data = bm_data(gc_fade_bmp_handle,0); + int i,size = 32*32; + + for(i=0;iBAD_BITMAP_HANDLE) + { + ushort *data = bm_data(gc_fadewhite_bmp_handle,0); + int i,size = 32*32; + + for(i=0;imin<0.0f) range->min = 0.0f; + if(range->min>1.0f) range->min = 1.0f; + if(range->max<0.0f) range->max = 0.0f; + if(range->max>1.0f) range->max = 1.0f; + + if( range->max < range->min ){ + float temp = range->max; + range->max = range->min; + range->min = temp; + } +} + + +// Cinematic_PerformFake +// +// Performs a fake cinematic...just calls certain functions that may need to be +// called for certain things to happen. For instance, some bosses are triggered when +// their boss intro cam is started, but in multiplayer there is no cinematics, so calling +// this would do what's needed. +void Cinematic_PerformFake(void) +{ + Cinematic_fake_queued = false; + mprintf((0,"Cinematics: Faking Cinematic\n")); + object *target = ObjGet(Cinematic_fake_info.target_objhandle); + if(!target) + { + mprintf((0,"Cinematics: Invalid target...can't fake\n")); + return; + } + + //tell the target it's the focus of a cinematic + AINotify(target, AIN_MOVIE_START, NULL); + AINotify(target, AIN_MOVIE_END, NULL); + + if(target->type==OBJ_PLAYER) + { + tOSIRISEventInfo ei; + + Osiris_CallLevelEvent(EVT_PLAYER_MOVIE_START,&ei); + Osiris_CallLevelEvent(EVT_PLAYER_MOVIE_END,&ei); + } +} + +void Cinematic_SetForFakeCinematic(tGameCinematic *info) +{ + ASSERT(Cinematic_fake_queued==false); //ack trying to play 2 cinematics in one frame!? + + memcpy(&Cinematic_fake_info,info,sizeof(tGameCinematic)); + Cinematic_fake_queued = true; +} + +inline int Cinematics_CreateCamera(void) +{ + int objnum = ObjCreate(OBJ_CAMERA,0,Player_object->roomnum,&Player_object->pos,NULL); + if(objnum==-1) + return OBJECT_HANDLE_NONE; + + if(Demo_flags==DF_RECORDING) + { + DemoWriteObjCreate(OBJ_CAMERA,0,Player_object->roomnum,&Player_object->pos,NULL,OBJECT_HANDLE_NONE,&Objects[objnum]); + } + + return Objects[objnum].handle; +} + +// Cinematic_Start +// +// Starts an in-game cinematic sequence. text_string is the text to be displayed +// use pipes (|) to seperate lines. (calls demo routines to record) +bool Cinematic_Start(tGameCinematic *info,char *text_string) +{ + if(Demo_flags == DF_PLAYBACK) + return true; //demo system calls Cinematic_StartCine itself + + ASSERT(info!=NULL); + ASSERT(text_string!=NULL); + if( !info || !text_string ) + return false; + + if(Cinematic_inuse) + { + Cinematic_SetForFakeCinematic(info); + return false; + } + + if(Game_mode&GM_MULTI) + { + Cinematic_SetForFakeCinematic(info); + return false; //no cinematics in multiplayer + } + + if((info->flags&GCF_CAMERAPLACEMENT)==GCF_USEPATH) + { + info->orient = NULL; //ensure that this is NULL + } + + int camera_handle = Cinematics_CreateCamera(); + + if( Demo_flags == DF_RECORDING) + { + tCinematicDemoInfo demo_data; + demo_data.cinematic_type = CDI_NOT_CANNED; + demo_data.text_string = text_string; + demo_data.cinematic_data = info; + demo_data.camera_objhandle = camera_handle; + Cinematic_WriteDemoFileData(&demo_data); + } + + return Cinematic_StartCine(info,text_string,camera_handle); +} + +inline void Cinematic_DeleteCamera(int objhandle) +{ + object *obj = ObjGet(objhandle); + if(obj) + { + SetObjectDeadFlag(obj); + } +} + + +// Cinematic_StartCine +// +// Starts an in-game cinematic sequence. text_string is the text to be displayed +// use pipes (|) to seperate lines. (does not call demo routines) +bool Cinematic_StartCine(tGameCinematic *info,char *text_string,int camera_objhandle) +{ + ASSERT(info!=NULL); + ASSERT(text_string!=NULL); + if( !info || !text_string ) + { + Cinematic_DeleteCamera(camera_objhandle); + return false; + } + + if(Cinematic_inuse) + { + Cinematic_SetForFakeCinematic(info); + Cinematic_DeleteCamera(camera_objhandle); + return false; + } + + if(Game_mode&GM_MULTI) + { + Cinematic_SetForFakeCinematic(info); + Cinematic_DeleteCamera(camera_objhandle); + return false; //no cinematics in multiplayer + } + +#ifdef _DEBUG + if(!Cinematics_enabled) + { + Cinematic_SetForFakeCinematic(info); + Cinematic_DeleteCamera(camera_objhandle); + AddBlinkingHUDMessage("Cinematic: Not Displayed, they are disabled (DEL-SHIFT-C)"); + return true; + } + + if(!DoAI) + { + Cinematic_SetForFakeCinematic(info); + Cinematic_DeleteCamera(camera_objhandle); + AddBlinkingHUDMessage("Cinematic: Not Displayed, AI is not turned on (DEL-A)"); + return true; + } +#endif + + + //take the given values and setup the struct + memset(&GameCinema,0,sizeof(GameCinema)); + + //verify passed in values + verify_percentranage(&info->text_display); + verify_percentranage(&info->track_target); + verify_percentranage(&info->player_disabled); + verify_percentranage(&info->in_camera_view); + verify_percentranage(&info->quick_exit); + + GameCinema.flags = info->flags; + GameCinema.target_objhandle = info->target_objhandle; + GameCinema.callback = info->callback; + GameCinema.old_hudmode = GetHUDMode(); + + int objnum; + object *c = ObjGet(camera_objhandle); + if(c) + { + objnum = OBJNUM(c); + }else + { + mprintf((0,"Unable to create camera\n")); + return false; + } + + + if((info->flags&GCF_CAMERAPLACEMENT)==GCF_USEPOINT){ + GameCinema.room = info->room; + GameCinema.position = info->position; + //objnum = ObjCreate(OBJ_CAMERA,0,info->room,&info->position,info->orient); + ObjSetPos(c,&info->position,info->room,info->orient,false); + + }else{ + GameCinema.pathnum = info->pathid; + if(GameCinema.pathnum==-1){ + mprintf((0,"Invalid Path given\n")); + return false; + } + //Abort if not a good path + if(((info->flags&GCF_CAMERAPLACEMENT)==GCF_USEPOINT) ){ + if (GamePaths[GameCinema.pathnum].num_nodes < 2) { + mprintf((0,"Not a good path passed to Cinematic_Start\n")); + return false; + } + } + + //objnum = ObjCreate(OBJ_CAMERA,0,GamePaths[GameCinema.pathnum].pathnodes[0].roomnum,&GamePaths[GameCinema.pathnum].pathnodes[0].pos,NULL); + ObjSetPos(c,&GamePaths[GameCinema.pathnum].pathnodes[0].pos,GamePaths[GameCinema.pathnum].pathnodes[0].roomnum,NULL,false); + } + + /* + if(objnum==-1){ + //couldn't create camera + mprintf((0,"Unable to create camera\n")); + return false; + } + */ + + GameCinema.camera_objhandle = Objects[objnum].handle; + GameCinema.target_triggered = false; + object *camera = &Objects[objnum]; + + // parse the text line and extract the lines + GameCinema.num_lines = 0; + GameCinema.text = mem_strdup(text_string); + char *ptr = GameCinema.text; + if ( !ptr ){ + Error("Out of Memory\n"); + } + while( *ptr ) { + if( *ptr == '|' ){ + *ptr = '\0'; + GameCinema.num_lines++; + } + ptr++; + } + GameCinema.num_lines++; + + float start_time = Gametime; + GameCinema.end_time = start_time + info->max_time_play; + GameCinema.text_display_start = start_time + (info->max_time_play*info->text_display.min); + GameCinema.text_display_stop = start_time + (info->max_time_play*info->text_display.max); + GameCinema.track_start = start_time + (info->max_time_play*info->track_target.min); + GameCinema.track_stop = start_time + (info->max_time_play*info->track_target.max); + GameCinema.player_start = start_time + (info->max_time_play*info->player_disabled.min); + GameCinema.player_stop = start_time + (info->max_time_play*info->player_disabled.max); + GameCinema.camera_start = start_time + (info->max_time_play*info->in_camera_view.min); + GameCinema.camera_stop = start_time + (info->max_time_play*info->in_camera_view.max); + GameCinema.exit_start = start_time + (info->max_time_play*info->quick_exit.min); + GameCinema.exit_end = start_time + (info->max_time_play*info->quick_exit.max); + GameCinema.start_transition_type= info->start_transition; + GameCinema.end_transition_type = info->end_transition; + GameCinema.doing_end_transition = false; //stop all pending end transitions + GameCinema.controls_suspended = false; + + if(GameCinema.end_transition_type==GCTT_WACKY || GameCinema.end_transition_type==GCTT_FADEINOUT) + { + GameCinema.early_trans_end_start_time = start_time + info->max_time_play - (END_TRANSITION_TIME/2.0f); + } + + if(GameCinema.end_transition_type==GCTT_FADEWHITE) + { + GameCinema.early_trans_end_start_time = start_time; + } + + if(GameCinema.start_transition_type==GCTT_NONE) + { + GameCinema.start_transition_start_time = start_time; + GameCinema.start_transition_end_time = start_time; + }else + { + GameCinema.start_transition_start_time = GameCinema.start_transition_end_time = -1.0f; + } + + + // The structs are setup, time to setup the camera and objects + + mprintf((0,"Cinematic_Start\n")); + + Player_object->flags &= ~OF_DESTROYABLE; + Players[Player_num].flags &= ~PLAYER_FLAGS_AFTERBURN_ON; + camera->flags &= ~OF_DESTROYABLE; + + //setup camera physics + camera->movement_type = MT_PHYSICS; + camera->mtype.phys_info.flags &= ~PF_USES_THRUST; + //camera->mtype.phys_info.flags |= PF_LEVELING; + camera->mtype.phys_info.flags &= ~PF_LEVELING; + camera->mtype.phys_info.drag = 0.1f; + camera->mtype.phys_info.mass = 2.0f; + camera->mtype.phys_info.rotdrag = 0.1f; + + vm_MakeZero(&camera->mtype.phys_info.thrust); + vm_MakeZero(&camera->mtype.phys_info.rotthrust); + vm_MakeZero(&camera->mtype.phys_info.rotvel); + vm_MakeZero(&camera->mtype.phys_info.velocity); + + //setup camera AI + SetObjectControlType(camera, CT_AI); + memset(camera->ai_info, 0, sizeof(ai_frame)); + + camera->ai_info->ai_class = AIC_STATIC; + camera->ai_info->ai_type = AIT_AIS; + + GoalInitTypeGoals(camera, AIT_AIS); + + float velocity_to_use = 75.0f; + + if((info->flags&GCF_CAMERAPLACEMENT)==GCF_USEPATH){ + //determine velocity based on distance to travel on path + velocity_to_use = Cine_GetPathTravelSpeed(GameCinema.pathnum,info->max_time_play); + } + + camera->ai_info->flags = AIF_PERSISTANT | AIF_DISABLE_FIRING | AIF_DISABLE_MELEE | AIF_FORCE_AWARENESS | AIF_DETERMINE_TARGET; + camera->ai_info->max_velocity = velocity_to_use; // Slightly faster than the real ship + camera->ai_info->max_delta_velocity = 40.0f; + camera->ai_info->max_turn_rate = 14000; + camera->ai_info->awareness = AWARE_MOSTLY; + camera->ai_info->movement_type = MC_FLYING; + camera->ai_info->next_movement = AI_INVALID_INDEX; + camera->ai_info->anim_sound_handle = 0; + camera->ai_info->status_reg = 0; + camera->ai_info->last_played_sound_index = -1; + camera->ai_info->weapon_speed = 0.0f; + camera->ai_info->animation_type = AS_ALERT; + camera->ai_info->next_melee_time = Gametime; + camera->ai_info->next_flinch_time = Gametime; + camera->ai_info->target_handle = OBJECT_HANDLE_NONE; + camera->ai_info->next_check_see_target_time = Gametime; + camera->ai_info->last_see_target_time = Gametime; + camera->ai_info->last_render_time = -1.0f; + camera->ai_info->next_target_update_time = Gametime; + camera->ai_info->notify_flags |= AI_NOTIFIES_ALWAYS_ON; + camera->ai_info->last_see_target_pos = Zero_vector; + camera->ai_info->dodge_vel_percent = 1.0f; + camera->ai_info->attack_vel_percent = 1.0f; + camera->ai_info->fight_same = 0.0f; + camera->ai_info->agression = 0.0f; + camera->ai_info->avoid_friends_distance = 0.0f; + camera->ai_info->biased_flight_importance = 0.0f; + camera->ai_info->circle_distance = 0.0f; + camera->ai_info->dodge_percent = 0.0f; + camera->ai_info->fight_team = 0.0; + + AIPathInitPath(&camera->ai_info->path); + + //Move camera to start position + vector c_pos; + int c_room; + matrix c_orient; + + if((info->flags&GCF_CAMERAPLACEMENT)==GCF_USEPOINT){ + c_room = GameCinema.room; + c_pos = GameCinema.position; + if(info->orient) + c_orient = *info->orient; + else + vm_MakeIdentity(&c_orient); + }else{ + //Set up path info + path_information pi; + + pi.path_id = GameCinema.pathnum; + pi.start_node = 0; + pi.end_node = GamePaths[GameCinema.pathnum].num_nodes-1; + + if (GamePaths[GameCinema.pathnum].num_nodes == 1) { + vm_VectorToMatrix(&c_orient, &GamePaths[GameCinema.pathnum].pathnodes[0].fvec, + &GamePaths[GameCinema.pathnum].pathnodes[0].uvec, NULL); + pi.next_node = 0; + } + else { + vector fvec = GamePaths[GameCinema.pathnum].pathnodes[1].pos - GamePaths[GameCinema.pathnum].pathnodes[0].pos; + vm_VectorToMatrix(&c_orient,&fvec,NULL,NULL); + pi.next_node = 1; + } + + c_pos = GamePaths[GameCinema.pathnum].pathnodes[0].pos; + c_room = GamePaths[GameCinema.pathnum].pathnodes[0].roomnum; + + //Make the player follow the path + GoalAddGoal(camera, AIG_FOLLOW_PATH, (void *) &pi, 0, 10.0f, GF_ORIENT_TARGET | GF_FORCE_AWARENESS); + } + + ObjSetPos(camera,&c_pos,c_room,&c_orient,true); + + //Switch to correct hud mode + switch(GameCinema.flags&GCF_SCREENFORMAT){ + case GCF_LETTERBOX: + SetHUDMode(HUD_LETTERBOX); + break; + case GCF_FULLSCREEN: + SetHUDMode(HUD_OBSERVER); + break; + } + + GameCinema.bmp_handle = -1; + GameCinema.longest_line = 0; + + if((GameCinema.flags&GCF_TEXT_MASK)==GCF_TEXT_WIPEIN){ + + //create overlay bitmap + GameCinema.bmp_handle = bm_AllocBitmap(128,128,0); + if(GameCinema.bmp_handle>BAD_BITMAP_HANDLE){ + ushort *data = bm_data(GameCinema.bmp_handle,0); + for(int j=0;j<128*128;j++){ + data[j] = GR_RGB16(0,0,0)|OPAQUE_FLAG; + } + } + } + + //determine longest line and add text to game messages + int old_font = grtext_GetFont(); + grtext_SetFont(BIG_BRIEFING_FONT); + int pos = 0,len; + char *str_ptr; + + for(int i=0;ipos - camera->pos; + vm_NormalizeVector(&turn_to); + + if(turn_to == Zero_vector ){ + mprintf((0,"Cine: No turn_to or less than 1 degree off goal\n")); + goto continue_start; + } + + goal_angle = vm_DeltaAngVecNorm(&camera->orient.fvec, &turn_to, &u_axis); + if(goal_angle == 0){ + mprintf((0,"Cine: Goal angle is zero\n")); + goto continue_start; + } + + camera->orient.fvec = turn_to; + + camera->orient.uvec.x = camera->orient.uvec.z = 0.0f; + camera->orient.uvec.y = 1.0f; + + vm_Orthogonalize(&camera->orient); + ObjSetOrient(camera, &camera->orient); + + }else if((GameCinema.flags&GCF_CAMERAPLACEMENT)==GCF_USEPATH) + { + //adjust to path node orientation (if we are on a path) + int cur_path = camera->ai_info->path.cur_path; + int cur_node = camera->ai_info->path.cur_node; + int end_node = camera->ai_info->path.path_end_node[cur_path]; + int next_node = min(cur_node+1,end_node); + int path_num = camera->ai_info->path.path_id[cur_path]; + + if(cur_node!=next_node){ + vector fvec = GamePaths[path_num].pathnodes[next_node].pos - GamePaths[path_num].pathnodes[cur_node].pos; + vm_VectorToMatrix(&camera->orient,&fvec,NULL,NULL); + vm_Orthogonalize(&camera->orient); + ObjSetOrient(camera,&camera->orient); + } + } + +continue_start:; + + } + + if(GameCinema.callback) + (*GameCinema.callback)(GCCT_START); + + return true; +} + +inline bool Cinematic_IsPlayerDead(void) +{ + if(!Player_object || (Player_object->type==OBJ_GHOST) || (Player_object->type==OBJ_PLAYER && Players[Player_object->id].flags&(PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD) ) ){ + return true; + } + return false; +} + +// Cinematic_Stop +// +// Stops and clears up a in-game cinematic. +void Cinematic_Stop(void) +{ + if(!Cinematic_inuse) + return; + + if(GameCinema.text){ + mem_free(GameCinema.text); + GameCinema.text = NULL; + } + + //tell the target object the cinematic is over + object *target = ObjGet(GameCinema.target_objhandle); + + if(GameCinema.callback) + (*GameCinema.callback)(GCCT_STOP); + + //reset things back to normal, anything that might be messed up + bool dont_restore_viewer = false; + if(GameCinema.flags&GCF_DONTRESTOREVIEWER) + { + mprintf((0,"********NOT RESTORING VIEW TO PLAYER*************\n")); + dont_restore_viewer = true; + } + + if(!dont_restore_viewer) + { + Viewer_object = Player_object; + } + Players[Player_object->id].controller_bitflags = 0xFFFFFFFF; + Player_object->flags |= OF_DESTROYABLE; + ResumeControls(); + + //go back to full screen + if(!Cinematic_IsPlayerDead() && GetScreenMode() == SM_GAME) //player death cam has started + SetHUDMode(Cinematic_GetOldHudMode()); + + if(GameCinema.bmp_handle>BAD_BITMAP_HANDLE){ + bm_FreeBitmap(GameCinema.bmp_handle); + } + + //delete the camera + if(!dont_restore_viewer) + { + object *cam = ObjGet(GameCinema.camera_objhandle); + if(cam){ + SetObjectDeadFlag(cam); + } + } + + Cinematic_inuse = false; + HUDUnpauseMessages(); + D3MusicStopCinematic(); + + //Start fade out transition + if(GameCinema.end_transition_type==GCTT_FADE) + { + GameCinema.doing_end_transition = true; + GameCinema.end_transition_start_time = Gametime; + GameCinema.end_transition_end_time = Gametime + END_TRANSITION_TIME; + }else if(GameCinema.end_transition_type==GCTT_WACKY) + { + //only start the wacky transition if it already hasn't (early break) + if(!GameCinema.doing_end_transition) + { + GameCinema.doing_end_transition = true; + GameCinema.end_transition_start_time = Gametime - (END_TRANSITION_TIME/2); + GameCinema.end_transition_end_time = Gametime + (END_TRANSITION_TIME/2); + } + }else if(GameCinema.end_transition_type==GCTT_FADEINOUT) + { + //only start if it already hasn't + if(!GameCinema.doing_end_transition) + { + GameCinema.doing_end_transition = true; + GameCinema.end_transition_start_time = Gametime - (END_TRANSITION_TIME/2); + GameCinema.end_transition_end_time = Gametime + (END_TRANSITION_TIME/2); + } + }else if(GameCinema.end_transition_type==GCTT_FADEWHITE) + { + if(!GameCinema.doing_end_transition) + { + GameCinema.doing_end_transition = true; + GameCinema.end_transition_start_time = GameCinema.early_trans_end_start_time; + GameCinema.end_transition_end_time = GameCinema.end_time; + } + } + + if(target) + { + AINotify(target, AIN_MOVIE_END, NULL); + + if(target->type==OBJ_PLAYER) + { + tOSIRISEventInfo ei; + Osiris_CallLevelEvent(EVT_PLAYER_MOVIE_END,&ei); + } + } +} + +bool Cinematic_IsKeyPressed(void) +{ + for(int i=0;iGameCinema.end_time){ + Cinematic_Stop(); + return; + } + + //check for keyboard quick exit + if(Gametime>=GameCinema.exit_start && Gametime<=GameCinema.exit_end){ + if(Cinematic_IsKeyPressed()){ + //a key has been pressed, quick exit! + ddio_KeyFlush(); + + if(GameCinema.flags&GCF_FORCETARGETTOEND){ + //move object to end of path + object *target = ObjGet(GameCinema.target_objhandle); + if(target && target->ai_info && target->ai_info->path.num_paths>0){ + bool shouldmove = true; + +#if defined(_DEBUG) && defined(EDITOR) + extern bool Game_being_played_from_quick_play; + + if(Game_being_played_from_quick_play && target->type==OBJ_PLAYER) + { + shouldmove = false; + } +#endif + + //its a target on a path + matrix orient; + vector pos; + int room; + + int currpath = target->ai_info->path.path_id[target->ai_info->path.cur_path]; + int endnode = GamePaths[currpath].num_nodes-1; + + vm_VectorToMatrix(&orient,&GamePaths[currpath].pathnodes[endnode].fvec,&GamePaths[currpath].pathnodes[endnode].uvec,NULL); + + pos = GamePaths[currpath].pathnodes[endnode].pos; + room = GamePaths[currpath].pathnodes[endnode].roomnum; + + if(shouldmove) + { + ObjSetPos(target,&pos,room,&orient,true); + } + } + } + Cinematic_Stop(); + return; + } + } + + object *camera = ObjGet(GameCinema.camera_objhandle); + object *target = ObjGet(GameCinema.target_objhandle); + + if( !target && GameCinema.flags&GCF_STOPIFTAGETDEAD ){ + Cinematic_Stop(); + return; + } + + //tell the target it's the focus of a cinematic + if( target && !GameCinema.target_triggered){ + GameCinema.target_triggered = true; + AINotify(target, AIN_MOVIE_START, NULL); + + if(target->type==OBJ_PLAYER) + { + tOSIRISEventInfo ei; + Osiris_CallLevelEvent(EVT_PLAYER_MOVIE_START,&ei); + } + } + + //check to see if we should be looking through the camera + if(Gametime>=GameCinema.camera_start && Gametime<=GameCinema.camera_stop && camera!=NULL){ + //camera should be the viewer + Viewer_object = camera; + }else{ + Viewer_object = Player_object; + } + + //check to see if we should be disabling player controls + if(Gametime>=GameCinema.player_start && Gametime<=GameCinema.player_stop ){ + //disable controls + Players[Player_object->id].controller_bitflags = 0; + + //make sure play controls are suspended + if(!GameCinema.controls_suspended) + { + SuspendControls(); + GameCinema.controls_suspended = true; + } + }else{ + //make sure controls are enabled + Players[Player_object->id].controller_bitflags = 0xFFFFFFFF; + + //make sure play controls are enabled + if(GameCinema.controls_suspended) + { + ResumeControls(); + GameCinema.controls_suspended = false; + } + } + + //adjust view to target + if(Gametime>=GameCinema.track_start && Gametime<=GameCinema.track_stop && target ){ + //track target + + + vector u_axis; + angle goal_angle; + + vector turn_to = target->pos - camera->pos; + vm_NormalizeVector(&turn_to); + + if(turn_to == Zero_vector ){ + mprintf((0,"Cine: No turn_to or less than 1 degree off goal\n")); + goto continue_frame; + } + + goal_angle = vm_DeltaAngVecNorm(&camera->orient.fvec, &turn_to, &u_axis); + if(goal_angle == 0){ + mprintf((0,"Cine: Goal angle is zero\n")); + goto continue_frame; + } + + camera->orient.fvec = turn_to; + + camera->orient.uvec.x = camera->orient.uvec.z = 0.0f; + camera->orient.uvec.y = 1.0f; + + vm_Orthogonalize(&camera->orient); + ObjSetOrient(camera, &camera->orient); + + + }else if((GameCinema.flags&GCF_CAMERAPLACEMENT)==GCF_USEPATH){ + //adjust to path node orientation (if we are on a path) + int cur_path = camera->ai_info->path.cur_path; + int cur_node = camera->ai_info->path.cur_node; + int end_node = camera->ai_info->path.path_end_node[cur_path]; + int next_node = min(cur_node+1,end_node); + int path_num = camera->ai_info->path.path_id[cur_path]; + + if(cur_node!=next_node){ + vector fvec = GamePaths[path_num].pathnodes[next_node].pos - GamePaths[path_num].pathnodes[cur_node].pos; + vm_VectorToMatrix(&camera->orient,&fvec,NULL,NULL); + vm_Orthogonalize(&camera->orient); + ObjSetOrient(camera,&camera->orient); + } + }else{ + //don't adjust at all + } + +continue_frame: + + if(GameCinema.end_transition_type==GCTT_WACKY || GameCinema.end_transition_type==GCTT_FADEINOUT) + { + if(Gametime>GameCinema.early_trans_end_start_time && !GameCinema.doing_end_transition) + { + //start the transition early + GameCinema.doing_end_transition = true; + GameCinema.end_transition_start_time = Gametime; + GameCinema.end_transition_end_time = Gametime + END_TRANSITION_TIME; + } + } + + if(GameCinema.end_transition_type==GCTT_FADEWHITE) + { + if(Gametime>GameCinema.early_trans_end_start_time && !GameCinema.doing_end_transition) + { + //start the transition early + GameCinema.doing_end_transition = true; + GameCinema.end_transition_start_time = GameCinema.early_trans_end_start_time; + GameCinema.end_transition_end_time = GameCinema.end_time; + } + } +} + +// Renders anything that needs to be rendered for the cinematic frame +void Cinematic_RenderFrame(void) +{ + if(!Cinematic_inuse){ + if(GameCinema.doing_end_transition) + Cinematic_DoEndTransition(); + return; + } + + if(GetHUDMode()==HUD_LETTERBOX){ + //clear letterbox areas + Clear_screen=4; + } + + if(GameCinema.start_transition_type!=GCTT_NONE) + { + Cinematic_DoStartTransition(); + } + + if(GameCinema.flags&GCF_DOTRANSBEFORETEXT) + { + //some end transitions start early...process now + if(GameCinema.doing_end_transition) + Cinematic_DoEndTransition(); + + //see if we should be displaying text + if(Gametime>=GameCinema.text_display_start && Gametime=GameCinema.text_display_start && Gametime= 0 );//too many lines of text! + + }break; + case GCF_LAYOUT_BOTTOM: + { + end_y = Max_window_h; + start_y = end_y - (GameCinema.num_lines*font_height); + + ASSERT( start_y >= 0 ); //too many lines of text! + }break; + case GCF_LAYOUT_TOP: + { + start_y = 2; + end_y = (GameCinema.num_lines*font_height) + start_y; + + ASSERT( end_y < Max_window_h); //too many lines of text! + }break; + case GCF_LAYOUT_MIDDLE: + { + int total_height = GameCinema.num_lines*font_height; + start_y = (Max_window_h/2) - (total_height/2); + end_y = start_y + total_height; + + ASSERT( start_y>=0 && end_y < Max_window_h); //too many lines of text + + }break; + } + + if((GameCinema.flags&GCF_TEXT_MASK)==GCF_TEXT_WIPEIN){ + //see if this mode is allowed (only if letterbox and not in middle layout) + if((GameCinema.flags&GCF_SCREENFORMAT)!=GCF_LETTERBOX){ + GameCinema.flags &= ~GCF_TEXT_WIPEIN; + GameCinema.flags |= GCF_TEXT_FADEINOUT; + }else{ + //it's letter box, make sure it's not middle + if((GameCinema.flags&GCF_LAYOUT_MASK)==GCF_LAYOUT_MIDDLE){ + GameCinema.flags &= ~GCF_TEXT_WIPEIN; + GameCinema.flags |= GCF_TEXT_FADEINOUT; + } + if((GameCinema.flags&GCF_LAYOUT_MASK)==GCF_LAYOUT_LOW){ + GameCinema.flags &= ~GCF_TEXT_WIPEIN; + GameCinema.flags |= GCF_TEXT_FADEINOUT; + } + } + } + + + float total_text_time = GameCinema.text_display_stop - GameCinema.text_display_start; + if(total_text_time<=0.0f){ + grtext_SetFont(old_font); + return; + } + + float time_in = Gametime - GameCinema.text_display_start; + float perc = time_in / total_text_time; + if( perc < 0.0f ) perc = 0.0f; + if( perc > 1.0f ) perc = 1.0f; + + switch(GameCinema.flags&GCF_TEXT_MASK){ + case GCF_TEXT_WIPEIN: + { + //first 1/3 is alpha in + //last 2/3 is solid + ubyte alpha; + + if( perc > 0.33f){ + //middle 1/3 + alpha = 255; + }else if (perc <= 0.33f ){ + //first 1/3 + float new_p = perc/0.33f; + alpha = (ubyte) (new_p * 255.0f); + } + grtext_SetAlpha(alpha); + + int len,pos = 0; + int y = start_y; + for(int i=0;iBAD_BITMAP_HANDLE){ + float alphas[4] = {0.0f,1.0f,1.0f,0.0f}; + float p = (perc>0.18f)?1.0f:(perc/0.18f); + + int gl,gr,gt,gb; + grtext_GetParameters(&gl,>,&gr,&gb,NULL); + + int width = (1.0f - p)*GameCinema.longest_line; + int rx = gl + (gr - gl)/2 + (GameCinema.longest_line/2); + int lx = gl + rx - width; + + rend_SetAlphaType (AT_VERTEX); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetFiltering (0); + rend_SetWrapType(WT_CLAMP); + rend_SetZBufferState(0); + + int t_y,b_y; + if((GameCinema.flags&GCF_LAYOUT_MASK)==GCF_LAYOUT_TOP){ + t_y = 0; + b_y = Game_window_y; + }else{ + t_y = Game_window_y+Game_window_h; + b_y = Max_window_h; + } + + rend_DrawScaledBitmap (lx,t_y,rx,b_y,GameCinema.bmp_handle,0,0,1,1,-1,alphas); + + rend_SetFiltering(1); + rend_SetZBufferState(1); + } + }break; + case GCF_TEXT_FADEINOUT: + { + //first 1/3 is alpha in + //middle 1/3 is solid + //last 1/3 is alpha out + ubyte alpha; + + if( perc > 0.33f && perc <= 0.66f ){ + //middle 1/3 + alpha = 255; + }else if (perc <= 0.33f ){ + //first 1/3 + float new_p = perc/0.33f; + alpha = (ubyte) (new_p * 255.0f); + }else{ + //last 1/3 + float new_p = (perc - 0.66f)/0.33f; + if( new_p < 0.0f ) new_p = 0.0f; + if( new_p > 1.0f ) new_p = 1.0f; + alpha = (ubyte)(255.0f - (new_p * 255.0f)); + } + grtext_SetAlpha(alpha); + + int len,pos = 0; + int y = start_y; + for(int i=0;itotal_time) + return; + + float perc = time_in/total_time; + + if( perc < 0.0f ) perc = 0.0f; + if( perc > 1.0f ) perc = 1.0f; + + if(gc_fade_bmp_handle<=BAD_BITMAP_HANDLE || gc_fadewhite_bmp_handle<=BAD_BITMAP_HANDLE){ + GameCinema.start_transition_type=GCTT_NONE; + return; + } + + float alpha = 1.0f - perc; + + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (alpha*255.0f); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + rend_SetZBufferState(0); + + rend_DrawScaledBitmap(0,0,Max_window_w,Max_window_h,(GameCinema.start_transition_type==GCTT_FADEWHITE)?gc_fadewhite_bmp_handle:gc_fade_bmp_handle,0,0,1,1); + + rend_SetFiltering (1); + rend_SetZBufferState(1); +} + +void Cinematic_DoEndTransition(void) +{ + if(!GameCinema.doing_end_transition) + return; + + if(Gametime>GameCinema.end_transition_end_time) + { + GameCinema.doing_end_transition = false; + return; + } + + if(Gametime 1.0f ) perc = 1.0f; + + switch(GameCinema.end_transition_type) + { + case GCTT_WACKY: + { + if(gc_wacky_bmp_handle<=BAD_BITMAP_HANDLE){ + GameCinema.doing_end_transition = false; + return; + } + int w,h; + w = bm_w(gc_wacky_bmp_handle,0); + h = bm_h(gc_wacky_bmp_handle,0); + + float wscale,hscale; + wscale = (float)(Max_window_w)/(float)(w); + hscale = (float)(Max_window_h)/(float)(h); + + if(wscalelarge + ratio = perc/0.5f; + }else{ + //large->small + ratio = 1.0f - ((perc-0.5f)/0.5f); + } + + w *= ratio; + h *= ratio; + + int x,y; + + x = (Max_window_w/2) - (w/2); + y = (Max_window_h/2) - (h/2); + + rend_SetAlphaType (AT_TEXTURE); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + rend_SetZBufferState(0); + + rend_DrawScaledBitmap(x,y,x+w,y+h,gc_wacky_bmp_handle,0,0,1,1); + + rend_SetFiltering (1); + rend_SetZBufferState(1); + + }break; + + case GCTT_FADE: + { + if(gc_fade_bmp_handle<=BAD_BITMAP_HANDLE){ + GameCinema.doing_end_transition = false; + return; + } + + float alpha = 1.0f - perc; + + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (alpha*255.0f); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + rend_SetZBufferState(0); + + rend_DrawScaledBitmap(0,0,Max_window_w,Max_window_h,gc_fade_bmp_handle,0,0,1,1); + + rend_SetFiltering (1); + rend_SetZBufferState(1); + + }break; + + case GCTT_FADEINOUT: + { + if(gc_fade_bmp_handle<=BAD_BITMAP_HANDLE){ + GameCinema.doing_end_transition = false; + return; + } + + float alpha = 0.0f; + + if( perc < 0.5f ) + { + //transparent->opaque + alpha = perc/0.5f; + }else{ + //opaque->transparent + alpha = 1.0f - ((perc-0.5f)/0.5f); + } + + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (alpha*255.0f); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + rend_SetZBufferState(0); + + rend_DrawScaledBitmap(0,0,Max_window_w,Max_window_h,gc_fade_bmp_handle,0,0,1,1); + + rend_SetFiltering (1); + rend_SetZBufferState(1); + + }break; + case GCTT_FADEWHITE: + { + if(gc_fade_bmp_handle<=BAD_BITMAP_HANDLE){ + GameCinema.doing_end_transition = false; + return; + } + +#define CUTOFF 0.8f + + float alpha; + bool use_white; + if( perc < CUTOFF ) + { + //to white + alpha = perc/CUTOFF; + use_white = true; + }else{ + //to black + alpha = 1.0f; + use_white = false; + } + + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (alpha*255.0f); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetFiltering (0); + rend_SetZBufferState(0); + + if(use_white) + { + rend_DrawScaledBitmap(0,0,Max_window_w,Max_window_h,gc_fadewhite_bmp_handle,0,0,1,1); + }else + { + if(gc_temp_bmp_handle>BAD_BITMAP_HANDLE) + { + float fade_out_perc = 1.0f - ((perc-CUTOFF)/(1.0f-CUTOFF)); + int grey = 255.0f*fade_out_perc; + ushort *data = bm_data(gc_temp_bmp_handle,0); + int i,size = 32*32; + + for(i=0;i0) + velocity_to_use = distance/time; + + return velocity_to_use; +} + + +/* +*********************************************************************** + Intro Movie Canned Cinematic +*********************************************************************** +*/ + + +struct{ + int player_path; + float cinematic_time; + bool should_thrust; +}CannedCinematicIntro; + +void CannedCinematicIntroCallback(int type) +{ + switch(type){ + case GCCT_START: + { + //start the player on the path + mprintf((0,"START PLAYER ON PATH\n")); + + //determine how fast they need to travel + int pathnum = CannedCinematicIntro.player_path; + float velocity = Cine_GetPathTravelSpeed(pathnum,CannedCinematicIntro.cinematic_time); + + SuspendControls(); + + if (Players[Player_num].objnum > -1) { + object *playerobj = &Objects[Players[Player_num].objnum]; + ClearPlayerFiring(playerobj, PW_PRIMARY); + ClearPlayerFiring(playerobj, PW_SECONDARY); + } + + PlayerSetControlToAI(Player_num,velocity); + + //move the target (player) to the first node of the path + //this makes it multiplayer friendly since this will only be called + //in a single player game + vector pos; + matrix orient; + int roomnum; + int next_node; + + if(pathnum==-1) + { + mprintf((0,"ILLEGAL PATH FOR PLAYER\n")); + break; + } + + if(GamePaths[pathnum].num_nodes>1) + { + next_node = 1; + vector fvec = GamePaths[pathnum].pathnodes[1].pos - GamePaths[pathnum].pathnodes[0].pos; + vm_VectorToMatrix(&orient,&fvec,NULL,NULL); + + //see if it is a hacked 2 node path (i.e. its such a small distance he should move + if(GamePaths[pathnum].num_nodes==2) + { + if(fabs(vm_VectorDistance(&GamePaths[pathnum].pathnodes[1].pos,&GamePaths[pathnum].pathnodes[0].pos))>30.0f) + { + CannedCinematicIntro.should_thrust = true; + mprintf((0,"Player should thrust\n")); + }else + { + CannedCinematicIntro.should_thrust = false; + mprintf((0,"Player should NOT thrust\n")); + } + }else + { + CannedCinematicIntro.should_thrust = true; + mprintf((0,"Player should thrust\n")); + } + }else + { + next_node = 0; + CannedCinematicIntro.should_thrust = false; + mprintf((0,"Player should NOT thrust\n")); + vm_VectorToMatrix(&orient,&GamePaths[pathnum].pathnodes[0].fvec,&GamePaths[pathnum].pathnodes[0].uvec,NULL); + } + pos = GamePaths[pathnum].pathnodes[0].pos; + roomnum = GamePaths[pathnum].pathnodes[0].roomnum; + + ObjSetPos(Player_object,&pos,roomnum,&orient,true); + + if(CannedCinematicIntro.should_thrust) + { + //Set up path info + path_information pi; + pi.path_id = pathnum; + pi.start_node = 0; + pi.next_node = next_node; + pi.end_node = GamePaths[pathnum].num_nodes-1; + + //Make the player follow the path + GoalAddGoal(Player_object, AIG_FOLLOW_PATH, (void *) &pi, 0, 1.0f, GF_ORIENT_PATH_NODE | GF_FORCE_AWARENESS); + } + }break; + case GCCT_STOP: + { + //stop the player on the path + mprintf((0,"STOP PLAYER ON PATH\n")); + ResetPlayerControlType (Player_num); + ResumeControls(); + + vm_MakeZero(&Player_object->mtype.phys_info.velocity); + + Players[Player_num].flags &= ~PLAYER_FLAGS_THRUSTED; + + }break; + case GCCT_FRAME: + { + if(CannedCinematicIntro.should_thrust) + Players[Player_num].flags |= PLAYER_FLAGS_THRUSTED; + else + Players[Player_num].flags &= ~PLAYER_FLAGS_THRUSTED; + }break; + } +} + + +void CannedCinematic_Intro(int PathID,char *Text,int PlayerPath,float Seconds,int camera_handle) +{ + CannedCinematicIntro.player_path = PlayerPath; + CannedCinematicIntro.cinematic_time = Seconds; + + tGameCinematic info; + info.flags = 0; + info.flags = (GCF_LETTERBOX|GCF_USEPATH|GCF_TEXT_FADEINOUT|GCF_LAYOUT_BOTTOM|GCF_FORCETARGETTOEND); + info.target_objhandle = Player_object->handle; + info.max_time_play = Seconds; + info.callback = CannedCinematicIntroCallback; + info.text_display.min = 0.40f; + info.text_display.max = 0.80f; + info.track_target.min = 0.0f; + info.track_target.max = 1.0f; + info.player_disabled.min= 0.0f; + info.player_disabled.max= 1.0f; + info.in_camera_view.min = 0.0f; + info.in_camera_view.max = 1.0f; + info.quick_exit.min = 0.25f; + info.quick_exit.max = 1.0f; + + info.end_transition = GCTT_FADEINOUT; + info.start_transition = GCTT_FADE; + info.pathid = PathID; + + Cinematic_StartCine(&info,Text,camera_handle); +} + + +struct{ + int player_path; + float cinematic_time; + bool should_thrust; +}CannedCinematicEndLevel; +bool EndLevel(); + +void CannedCinematicEndLevelCallback(int type) +{ + switch(type){ + case GCCT_START: + { + //if the player is dead, bring them back from the dead + if((Players[Player_num].flags & PLAYER_FLAGS_DYING) || (Players[Player_num].flags & PLAYER_FLAGS_DEAD)) + { + //the player is currently dead + mprintf((0,"Bringing player back from the dead\n")); + Players[Player_num].flags &= ~(PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD); + Player_object->shields = 1.0f; + } + + //Make player invulnerable + MakePlayerInvulnerable(Player_num,CannedCinematicEndLevel.cinematic_time,false); + + //start the player on the path + mprintf((0,"START PLAYER ON PATH\n")); + + //determine how fast they need to travel + int pathnum = CannedCinematicEndLevel.player_path; + + if(pathnum==-1) + { + mprintf((0,"ILLEGAL PATH FOR PLAYER\n")); + break; + } + + float velocity = Cine_GetPathTravelSpeed(pathnum,CannedCinematicEndLevel.cinematic_time); + + SuspendControls(); + if (Players[Player_num].objnum > -1) { + object *playerobj = &Objects[Players[Player_num].objnum]; + ClearPlayerFiring(playerobj, PW_PRIMARY); + ClearPlayerFiring(playerobj, PW_SECONDARY); + } + + PlayerSetControlToAI(Player_num,velocity); + + //move the target (player) to the first node of the path + //this makes it multiplayer friendly since this will only be called + //in a single player game + vector pos; + matrix orient; + int roomnum; + int next_node; + + if(GamePaths[pathnum].num_nodes>1) + { + next_node = 1; + vector fvec = GamePaths[pathnum].pathnodes[1].pos - GamePaths[pathnum].pathnodes[0].pos; + vm_VectorToMatrix(&orient,&fvec,NULL,NULL); + + if(GamePaths[pathnum].num_nodes==2) + { + if(fabs(vm_VectorDistance(&GamePaths[pathnum].pathnodes[1].pos,&GamePaths[pathnum].pathnodes[0].pos))>30.0f) + { + CannedCinematicEndLevel.should_thrust = true; + }else + { + CannedCinematicEndLevel.should_thrust = false; + } + }else + { + CannedCinematicEndLevel.should_thrust = true; + } + }else + { + next_node = 0; + vm_VectorToMatrix(&orient,&GamePaths[pathnum].pathnodes[0].fvec,&GamePaths[pathnum].pathnodes[0].uvec,NULL); + CannedCinematicEndLevel.should_thrust = false; + } + pos = GamePaths[pathnum].pathnodes[0].pos; + roomnum = GamePaths[pathnum].pathnodes[0].roomnum; + + ObjSetPos(Player_object,&pos,roomnum,&orient,true); + + if(CannedCinematicEndLevel.should_thrust) + { + + //Set up path info + path_information pi; + pi.path_id = pathnum; + pi.start_node = 0; + pi.next_node = next_node; + pi.end_node = GamePaths[pathnum].num_nodes-1; + + //Make the player follow the path + GoalAddGoal(Player_object, AIG_FOLLOW_PATH, (void *) &pi, 0, 1.0f, GF_ORIENT_PATH_NODE | GF_FORCE_AWARENESS); + } + }break; + case GCCT_STOP: + { + //stop the player on the path + mprintf((0,"STOP PLAYER ON PATH\n")); + ResetPlayerControlType (Player_num); + ResumeControls(); + + vm_MakeZero(&Player_object->mtype.phys_info.velocity); + + SetGameState(GAMESTATE_LVLEND); + }break; + case GCCT_FRAME: + { + if(CannedCinematicEndLevel.should_thrust) + Players[Player_num].flags |= PLAYER_FLAGS_THRUSTED; + else + Players[Player_num].flags &= ~PLAYER_FLAGS_THRUSTED; + }break; + } +} + +void CannedCinematic_EndLevelPath(int PathID,char *Text,int PlayerPath,float Seconds,int camera_handle) +{ + CannedCinematicEndLevel.player_path = PlayerPath; + CannedCinematicEndLevel.cinematic_time = Seconds; + + tGameCinematic info; + info.flags = 0; + info.flags = (GCF_LETTERBOX|GCF_USEPATH|GCF_TEXT_WIPEIN|GCF_LAYOUT_BOTTOM|GCF_FORCETARGETTOEND|GCF_DONTRESTOREVIEWER); + info.target_objhandle = Player_object->handle; + info.max_time_play = Seconds; + info.callback = CannedCinematicEndLevelCallback; + info.text_display.min = 0.40f; + info.text_display.max = 0.80f; + info.track_target.min = 0.0f; + info.track_target.max = 1.0f; + info.player_disabled.min= 0.0f; + info.player_disabled.max= 1.0f; + info.in_camera_view.min = 0.0f; + info.in_camera_view.max = 1.0f; + info.quick_exit.min = 1.0f; + info.quick_exit.max = 1.0f; + + info.end_transition = GCTT_FADEINOUT; + info.start_transition = GCTT_FADE; + info.pathid = PathID; + + Cinematic_StartCine(&info,Text,camera_handle); +} + +void CannedCinematic_EndLevelPoint(vector *pos,int room,char *Text,int PlayerPath,float Seconds,int camera_handle) +{ + CannedCinematicEndLevel.player_path = PlayerPath; + CannedCinematicEndLevel.cinematic_time = Seconds; + + tGameCinematic info; + info.flags = 0; + info.flags = (GCF_LETTERBOX|GCF_USEPOINT|GCF_TEXT_WIPEIN|GCF_LAYOUT_BOTTOM|GCF_FORCETARGETTOEND|GCF_DONTRESTOREVIEWER); + info.target_objhandle = Player_object->handle; + info.max_time_play = Seconds; + info.callback = CannedCinematicEndLevelCallback; + info.text_display.min = 0.40f; + info.text_display.max = 0.80f; + info.track_target.min = 0.0f; + info.track_target.max = 1.0f; + info.player_disabled.min= 0.0f; + info.player_disabled.max= 1.0f; + info.in_camera_view.min = 0.0f; + info.in_camera_view.max = 1.0f; + info.quick_exit.min = 1.0f; + info.quick_exit.max = 1.0f; + info.end_transition = GCTT_FADEINOUT; + info.start_transition = GCTT_FADE; + + info.position = *pos; + info.room = room; + info.orient = NULL; + + Cinematic_StartCine(&info,Text,camera_handle); +} + +struct +{ + object *player; + int room; + vector pos; + matrix orient; +}CannedCinematicMovePlayerFade; + +void CannedCinematicMovePlayerFadeCallback(int type) +{ + switch(type) + { + case GCCT_FRAME: + object *player = CannedCinematicMovePlayerFade.player; + ObjSetPos(player,&CannedCinematicMovePlayerFade.pos,CannedCinematicMovePlayerFade.room,&CannedCinematicMovePlayerFade.orient,true); + break; + } +} + +void CannedCinematic_MovePlayerFade(object *player,int room,vector *pos,matrix *orient,int camera_handle) +{ + CannedCinematicMovePlayerFade.player = player; + CannedCinematicMovePlayerFade.room = room; + CannedCinematicMovePlayerFade.pos = *pos; + CannedCinematicMovePlayerFade.orient = *orient; + + tGameCinematic info; + info.flags = 0; + info.flags = (GCF_FULLSCREEN|GCF_USEPOINT|GCF_TEXT_NOEFFECT|GCF_LAYOUT_BOTTOM); + info.target_objhandle = OBJECT_HANDLE_NONE; + info.max_time_play = 1.0f; + info.callback = CannedCinematicMovePlayerFadeCallback; + info.text_display.min = 1.0f; + info.text_display.max = 1.0f; + info.track_target.min = 1.0f; + info.track_target.max = 1.0f; + info.player_disabled.min= 1.0f; + info.player_disabled.max= 1.0f; + info.in_camera_view.min = 0.0f; + info.in_camera_view.max = 1.0f; + info.quick_exit.min = 1.0f; + info.quick_exit.max = 1.0f; + info.end_transition = GCTT_FADEINOUT; + info.start_transition = GCTT_NONE; + + info.position = player->pos; + info.room = player->roomnum; + info.orient = &player->orient; + + Cinematic_StartCine(&info,"",camera_handle); +} + +struct +{ + float start_time; + float max_time; +}CannedCinematicLevelEndFadeWhite; + +void CannedCinematicLevelEndFadeWhiteCallback(int type) +{ + switch(type) + { + case GCCT_START: + break; + case GCCT_FRAME: + break; + case GCCT_STOP: + SetGameState(GAMESTATE_LVLEND); + break; + } +} + +void CannedCinematic_LevelEndFadeWhite(int camera_handle,float time,char *text_to_display) +{ + CannedCinematicLevelEndFadeWhite.start_time = Gametime; + CannedCinematicLevelEndFadeWhite.max_time = time; + + tGameCinematic info; + info.flags = (GCF_LETTERBOX|GCF_USEPOINT|GCF_TEXT_NOEFFECT|GCF_LAYOUT_MIDDLE|GCF_DONTRESTOREVIEWER|GCF_IGNOREPLAYERDEAD|GCF_DOTRANSBEFORETEXT); + info.target_objhandle = OBJECT_HANDLE_NONE; + info.max_time_play = time; + info.callback = CannedCinematicLevelEndFadeWhiteCallback; + info.text_display.min = 0.0f; + info.text_display.max = 1.0f; + info.track_target.min = 1.0f; + info.track_target.max = 1.0f; + info.player_disabled.min= 1.0f; + info.player_disabled.max= 1.0f; + info.in_camera_view.min = 1.0f; + info.in_camera_view.max = 1.0f; + info.quick_exit.min = 1.0f; + info.quick_exit.max = 1.0f; + info.end_transition = GCTT_FADEWHITE; + info.start_transition = GCTT_NONE; + + info.position = Player_object->pos; + info.room = Player_object->roomnum; + info.orient = &Player_object->orient; + + Cinematic_StartCine(&info,(text_to_display)?text_to_display:(char *)"",camera_handle); +} + +//============================================================================== +// Canned Cinematic interface + +void Cinematic_DoFakeCannedCinematics(tCannedCinematicInfo *info) +{ + switch(info->type) + { + case CANNED_LEVEL_INTRO: + break; + case CANNED_LEVEL_END_PATH: + case CANNED_LEVEL_END_POINT: + case CANNED_LEVEL_END_FADE_WHITE: + if(Game_mode&GM_MULTI) + { + MultiEndLevel(); + }else + { + SetGameState(GAMESTATE_LVLEND); + } + break; + case CANNED_MOVE_PLAYER_FADE: + break; + } +} + +void Cinematic_StartCannedScript(tCannedCinematicInfo *info) +{ + if(Demo_flags == DF_PLAYBACK) + return; //demo system calls Cinematic_StartCanned itself + + Cinematic_StartCanned(info); +} + +void Cinematic_StartCanned(tCannedCinematicInfo *info,int camera_handle) +{ + if(Cinematic_inuse) + { + tGameCinematic gc; + memset(&gc,0,sizeof(tGameCinematic)); + switch(info->type) + { + case CANNED_LEVEL_INTRO: + case CANNED_LEVEL_END_PATH: + case CANNED_LEVEL_END_POINT: + case CANNED_MOVE_PLAYER_FADE: + case CANNED_LEVEL_END_FADE_WHITE: + gc.target_objhandle = Player_object->handle; + break; + default: + { + gc.target_objhandle = OBJECT_HANDLE_NONE; + }break; + } + + Cinematic_SetForFakeCinematic(&gc); + Cinematic_DoFakeCannedCinematics(info); + if(camera_handle!=-1) + Cinematic_DeleteCamera(camera_handle); + return; + } + + if(Game_mode&GM_MULTI) + { + tGameCinematic gc; + memset(&gc,0,sizeof(tGameCinematic)); + switch(info->type) + { + case CANNED_LEVEL_INTRO: + case CANNED_LEVEL_END_PATH: + case CANNED_LEVEL_END_POINT: + case CANNED_MOVE_PLAYER_FADE: + case CANNED_LEVEL_END_FADE_WHITE: + gc.target_objhandle = Player_object->handle; + break; + default: + { + gc.target_objhandle = OBJECT_HANDLE_NONE; + }break; + } + + Cinematic_SetForFakeCinematic(&gc); + Cinematic_DoFakeCannedCinematics(info); + if(camera_handle!=-1) + Cinematic_DeleteCamera(camera_handle); + return; //no cinematics in multiplayer + } + + if(camera_handle==-1) + { + //when playing back a demo, this should have a value + ASSERT(Demo_flags != DF_PLAYBACK); + camera_handle = Cinematics_CreateCamera(); + } + + if( Demo_flags == DF_RECORDING) + { + tCinematicDemoInfo demo_data; + demo_data.cinematic_type = CDI_CANNED; + demo_data.text_string = NULL; + demo_data.canned_data = info; + demo_data.camera_objhandle = camera_handle; + Cinematic_WriteDemoFileData(&demo_data); + } + + switch(info->type) + { + case CANNED_LEVEL_INTRO: + CannedCinematic_Intro(info->camera_pathid,info->text_to_display,info->target_pathid,info->time,camera_handle); + break; + case CANNED_LEVEL_END_PATH: + CannedCinematic_EndLevelPath(info->camera_pathid,info->text_to_display,info->target_pathid,info->time,camera_handle); + break; + case CANNED_LEVEL_END_POINT: + { + object *cam = ObjGet(info->object_to_use_for_point); + if(cam) + { + CannedCinematic_EndLevelPoint(&cam->pos,cam->roomnum,info->text_to_display,info->target_pathid,info->time,camera_handle); + }else + { + mprintf((0,"Passed in object handle for canned cinematic (level end) for camera position does not exist\n")); + Cinematic_DeleteCamera(camera_handle); + SetGameState(GAMESTATE_LVLEND); + } + }break; + case CANNED_MOVE_PLAYER_FADE: + { object *player = ObjGet(info->target_objhandle); + if(!player || player->type!=OBJ_PLAYER) + { + mprintf((0,"Invalid player passed to CANNED_MOVE_PLAYER_FADE\n")); + Cinematic_DeleteCamera(camera_handle); + return; + } + + CannedCinematic_MovePlayerFade(player,info->room,&info->pos,&info->orient,camera_handle); + + }break; + case CANNED_LEVEL_END_FADE_WHITE: + { + CannedCinematic_LevelEndFadeWhite(camera_handle,info->time,info->text_to_display); + }break; + default: + { + Cinematic_DeleteCamera(camera_handle); + }break; + } +} + + +//================================================== +// Demo file support +//================================================== +void mf_WriteInt(ubyte *buffer,int *pointer,int data); +void mf_WriteShort(ubyte *buffer,int *pointer,short data); +void mf_WriteByte(ubyte *buffer,int *pointer,ubyte data); +void mf_WriteFloat(ubyte *buffer,int *pointer,float data); +void mf_WriteBytes(ubyte *buffer,int *pointer,ubyte *data,int len); +void mf_WriteString(ubyte *buffer,int *pointer,char *string); +int mf_ReadInt(ubyte *buffer,int *pointer); +short mf_ReadShort(ubyte *buffer,int *pointer); +ubyte mf_ReadByte(ubyte *buffer,int *pointer); +float mf_ReadFloat(ubyte *buffer,int *pointer); +void mf_ReadBytes(ubyte *buffer,int *pointer,ubyte *data,int len); +void mf_ReadString(ubyte *buffer,int *pointer,char *string); + +void Cinematic_DoDemoFileData(ubyte *buffer) +{ + int count = 0; + char text_string[512]; + tCannedCinematicInfo canned_data; + tGameCinematic cinematic_data; + int old_camera_objnum,camera_objnum,camera_handle; + + //see what kind of cinematic we are loading + switch(mf_ReadByte(buffer,&count)) + { + case CDI_NOT_CANNED: + { + matrix fake_orient; + vm_MakeIdentity(&fake_orient); + + cinematic_data.orient = &fake_orient; + cinematic_data.callback = NULL; + cinematic_data.flags = mf_ReadInt(buffer,&count); + cinematic_data.target_objhandle = mf_ReadInt(buffer,&count); + cinematic_data.end_transition = mf_ReadInt(buffer,&count); + cinematic_data.start_transition = mf_ReadInt(buffer,&count); + cinematic_data.pathid = mf_ReadInt(buffer,&count); + cinematic_data.position.x = mf_ReadFloat(buffer,&count); + cinematic_data.position.y = mf_ReadFloat(buffer,&count); + cinematic_data.position.z = mf_ReadFloat(buffer,&count); + cinematic_data.room = mf_ReadInt(buffer,&count); + if(mf_ReadByte(buffer,&count)) + { + fake_orient.fvec.x = mf_ReadFloat(buffer,&count); + fake_orient.fvec.y = mf_ReadFloat(buffer,&count); + fake_orient.fvec.z = mf_ReadFloat(buffer,&count); + fake_orient.uvec.x = mf_ReadFloat(buffer,&count); + fake_orient.uvec.y = mf_ReadFloat(buffer,&count); + fake_orient.uvec.z = mf_ReadFloat(buffer,&count); + fake_orient.rvec.x = mf_ReadFloat(buffer,&count); + fake_orient.rvec.y = mf_ReadFloat(buffer,&count); + fake_orient.rvec.z = mf_ReadFloat(buffer,&count); + } + + cinematic_data.max_time_play = mf_ReadFloat(buffer,&count); + cinematic_data.text_display.max = mf_ReadFloat(buffer,&count); + cinematic_data.text_display.min = mf_ReadFloat(buffer,&count); + cinematic_data.track_target.max = mf_ReadFloat(buffer,&count); + cinematic_data.track_target.min = mf_ReadFloat(buffer,&count); + cinematic_data.player_disabled.max = mf_ReadFloat(buffer,&count); + cinematic_data.player_disabled.min = mf_ReadFloat(buffer,&count); + cinematic_data.in_camera_view.max = mf_ReadFloat(buffer,&count); + cinematic_data.in_camera_view.min = mf_ReadFloat(buffer,&count); + cinematic_data.quick_exit.max = mf_ReadFloat(buffer,&count); + cinematic_data.quick_exit.min = mf_ReadFloat(buffer,&count); + mf_ReadString(buffer,&count,text_string); + + old_camera_objnum = mf_ReadInt(buffer,&count); + ASSERT(old_camera_objnum>=0 && old_camera_objnum=0 && camera_objnum=0 && old_camera_objnum=0 && camera_objnumcinematic_type); + + switch(info->cinematic_type) + { + case CDI_NOT_CANNED: + { + + mf_WriteInt(buffer,&count,info->cinematic_data->flags); + mf_WriteInt(buffer,&count,info->cinematic_data->target_objhandle); + mf_WriteInt(buffer,&count,info->cinematic_data->end_transition); + mf_WriteInt(buffer,&count,info->cinematic_data->start_transition); + mf_WriteInt(buffer,&count,info->cinematic_data->pathid); + mf_WriteFloat(buffer,&count,info->cinematic_data->position.x); + mf_WriteFloat(buffer,&count,info->cinematic_data->position.y); + mf_WriteFloat(buffer,&count,info->cinematic_data->position.z); + mf_WriteInt(buffer,&count,info->cinematic_data->room); + + if(info->cinematic_data->orient) + { + mf_WriteByte(buffer,&count,1); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->fvec.x); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->fvec.y); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->fvec.z); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->uvec.x); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->uvec.y); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->uvec.z); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->rvec.x); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->rvec.y); + mf_WriteFloat(buffer,&count,info->cinematic_data->orient->rvec.z); + + }else + { + mf_WriteByte(buffer,&count,0); + } + + mf_WriteFloat(buffer,&count,info->cinematic_data->max_time_play); + mf_WriteFloat(buffer,&count,info->cinematic_data->text_display.max); + mf_WriteFloat(buffer,&count,info->cinematic_data->text_display.min); + mf_WriteFloat(buffer,&count,info->cinematic_data->track_target.max); + mf_WriteFloat(buffer,&count,info->cinematic_data->track_target.min); + mf_WriteFloat(buffer,&count,info->cinematic_data->player_disabled.max); + mf_WriteFloat(buffer,&count,info->cinematic_data->player_disabled.min); + mf_WriteFloat(buffer,&count,info->cinematic_data->in_camera_view.max); + mf_WriteFloat(buffer,&count,info->cinematic_data->in_camera_view.min); + mf_WriteFloat(buffer,&count,info->cinematic_data->quick_exit.max); + mf_WriteFloat(buffer,&count,info->cinematic_data->quick_exit.min); + + if(info->text_string) + mf_WriteString(buffer,&count,info->text_string); + else + mf_WriteByte(buffer,&count,0); + + object *obj = ObjGet(info->camera_objhandle); + ASSERT(obj && obj->type==OBJ_CAMERA); + mf_WriteInt(buffer,&count,OBJNUM(obj)); + + }break; + case CDI_CANNED: + { + + mf_WriteInt(buffer,&count,info->canned_data->type); + mf_WriteInt(buffer,&count,info->canned_data->camera_pathid); + mf_WriteInt(buffer,&count,info->canned_data->target_pathid); + mf_WriteInt(buffer,&count,info->canned_data->target_objhandle); + mf_WriteFloat(buffer,&count,info->canned_data->time); + mf_WriteInt(buffer,&count,info->canned_data->object_to_use_for_point); + mf_WriteInt(buffer,&count,info->canned_data->room); + mf_WriteFloat(buffer,&count,info->canned_data->pos.x); + mf_WriteFloat(buffer,&count,info->canned_data->pos.y); + mf_WriteFloat(buffer,&count,info->canned_data->pos.z); + mf_WriteFloat(buffer,&count,info->canned_data->orient.fvec.x); + mf_WriteFloat(buffer,&count,info->canned_data->orient.fvec.y); + mf_WriteFloat(buffer,&count,info->canned_data->orient.fvec.z); + mf_WriteFloat(buffer,&count,info->canned_data->orient.uvec.x); + mf_WriteFloat(buffer,&count,info->canned_data->orient.uvec.y); + mf_WriteFloat(buffer,&count,info->canned_data->orient.uvec.z); + mf_WriteFloat(buffer,&count,info->canned_data->orient.rvec.x); + mf_WriteFloat(buffer,&count,info->canned_data->orient.rvec.y); + mf_WriteFloat(buffer,&count,info->canned_data->orient.rvec.z); + + if(info->canned_data->text_to_display) + mf_WriteString(buffer,&count,info->canned_data->text_to_display); + else + mf_WriteByte(buffer,&count,0); + + object *obj = ObjGet(info->camera_objhandle); + ASSERT(obj && obj->type==OBJ_CAMERA); + mf_WriteInt(buffer,&count,OBJNUM(obj)); + + }break; + } + + DemoWriteCinematics(buffer,count); +} + + +void mf_WriteInt(ubyte *buffer,int *pointer,int data) +{ + memcpy(&buffer[(*pointer)],&data,sizeof(int)); + (*pointer)+=sizeof(int); +} + +void mf_WriteShort(ubyte *buffer,int *pointer,short data) +{ + memcpy(&buffer[(*pointer)],&data,sizeof(short)); + (*pointer)+=sizeof(short); +} + +void mf_WriteByte(ubyte *buffer,int *pointer,ubyte data) +{ + memcpy(&buffer[(*pointer)],&data,sizeof(ubyte)); + (*pointer)+=sizeof(ubyte); +} + +void mf_WriteFloat(ubyte *buffer,int *pointer,float data) +{ + memcpy(&buffer[(*pointer)],&data,sizeof(float)); + (*pointer)+=sizeof(float); +} + +void mf_WriteBytes(ubyte *buffer,int *pointer,ubyte *data,int len) +{ + memcpy(&buffer[(*pointer)],data,len); + (*pointer)+=len; +} + +void mf_WriteString(ubyte *buffer,int *pointer,char *string) +{ + while(*string) + { + mf_WriteByte(buffer,pointer,(*string)); + string++; + } + mf_WriteByte(buffer,pointer,(*string)); +} + +int mf_ReadInt(ubyte *buffer,int *pointer) +{ + int value; + memcpy(&value,&buffer[(*pointer)],sizeof(value)); + (*pointer)+=sizeof(value); + return value; +} + +short mf_ReadShort(ubyte *buffer,int *pointer) +{ + short value; + memcpy(&value,&buffer[(*pointer)],sizeof(value)); + (*pointer)+=sizeof(value); + return value; +} + +ubyte mf_ReadByte(ubyte *buffer,int *pointer) +{ + ubyte value; + memcpy(&value,&buffer[(*pointer)],sizeof(value)); + (*pointer)+=sizeof(value); + return value; +} + +float mf_ReadFloat(ubyte *buffer,int *pointer) +{ + float value; + memcpy(&value,&buffer[(*pointer)],sizeof(value)); + (*pointer)+=sizeof(value); + return value; +} + +void mf_ReadBytes(ubyte *buffer,int *pointer,ubyte *data,int len) +{ + memcpy(data,&buffer[(*pointer)],len); + (*pointer)+=len; +} + +void mf_ReadString(ubyte *buffer,int *pointer,char *string) +{ + ubyte data; + data = mf_ReadByte(buffer,pointer); + while(data) + { + *string = data; + string++; + data = mf_ReadByte(buffer,pointer); + } + *string = '\0'; +} diff --git a/Descent3/gamecinematics.h b/Descent3/gamecinematics.h new file mode 100644 index 000000000..4ba3acca3 --- /dev/null +++ b/Descent3/gamecinematics.h @@ -0,0 +1,89 @@ +/* +* $Logfile: /DescentIII/main/gamecinematics.h $ +* $Revision: 8 $ +* $Date: 4/28/99 3:32a $ +* $Author: Jeff $ +* +* In-Game Cinematics system +* +* $Log: /DescentIII/main/gamecinematics.h $ + * + * 8 4/28/99 3:32a Jeff + * created cinematic level reset function + * + * 7 4/27/99 8:31p Jason + * fixed cinematic bug + * + * 6 3/10/99 6:20p Jeff + * many fixes to demo system. Fixed IGC so cameras move...fixed osiris to + * be restored correctly, and it handles errors on restore + * + * 5 2/23/99 12:44a Jeff + * added support for in-game-cinematics in demo system + * + * 4 2/14/99 1:16a Jeff + * added canned cinematic function/structures. Added a flag to push + * target to end of path if on a path (quick exit). Added canned intro + * cine. Determine correct velocity on player ship for intro cine. + * + * 3 2/01/99 12:55p Jeff + * restore correct hud mode, added flag to stop cinematics if target dies + * + * 2 1/31/99 8:48p Jeff + * new in game cinematics system finished +* +* $NoKeywords: $ +*/ + +#ifndef __INGAME_CINEMATICS_H_ +#define __INGAME_CINEMATICS_H_ + +#include "vecmat_external.h" +#include "gamecinematics_external.h" +#include "hud.h" + +// Cinematic_Init +// +// Initializes the in-game cinematics +void Cinematic_Init(void); + +// Cinematic_Close +// +// Closes the in-game cinematics +void Cinematic_Close(void); + +// Cinematic_Start +// +// Starts an in-game cinematic sequence. text_string is the text to be displayed +// use pipes (|) to seperate lines. +bool Cinematic_Start(tGameCinematic *info,char *text_string); + +// Cinematic_Stop +// +// Stops and clears up a in-game cinematic. +void Cinematic_Stop(void); + +// Cinematic_Frame +// +// Processes a frame for the Cinematics +void Cinematic_Frame(void); + +// Renders anything that needs to be rendered for the cinematic frame +void Cinematic_RenderFrame(void); + +void Cinematic_LevelInit(void); + +extern bool Cinematic_inuse; +extern bool Cinematic_fake_queued; + +// Returns the hud mode before cinematics +tHUDMode Cinematic_GetOldHudMode(void); + +// Starts a canned cinematic sequence +// Only the demo system passing in a camera_handle, so it should never be explicitly passed by you +void Cinematic_StartCanned(tCannedCinematicInfo *info,int camera_handle=-1); + + +void Cinematic_DoDemoFileData(ubyte *data); + +#endif diff --git a/Descent3/gamecinematics_external.h b/Descent3/gamecinematics_external.h new file mode 100644 index 000000000..38c43d4b2 --- /dev/null +++ b/Descent3/gamecinematics_external.h @@ -0,0 +1,211 @@ +/* +* $Logfile: /DescentIII/Main/gamecinematics_external.h $ +* $Revision: 13 $ +* $Date: 5/10/99 10:22p $ +* $Author: Ardussi $ +* +* Defines and structs external for DLLs +* +* $Log: /DescentIII/Main/gamecinematics_external.h $ + * + * 13 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 12 5/04/99 6:53p Jeff + * added new canned cinematic to fade screen to white and endlevel. Fixed + * crash bug with invalid player path for canned cine with player paths + * + * 11 4/04/99 8:21p Matt + * Fixed include problems with osiris vector stuff. Osiris headers now + * include vecmat_external.h, instead of defining the typedefs, structs, + * and inlines on the Osiris side. + * + * 10 3/27/99 7:22p Jeff + * fixed cinematics when going from one cut to another immediatly. Added + * start transition + * + * 9 2/28/99 8:32p Jeff + * added fade and move player dallas action. Fixed the end-level sequence + * changing view back to player for split second. + * + * 8 2/22/99 1:20a Jeff + * added support for inventory (simple) in dallas. Moved end-level + * sequence to use IGC. Add position clipboard stuff for dallas. Fixed + * some inventory bug with storing object handles + * + * 7 2/21/99 8:36p Jeff + * misc changes to handle new matcen and path types of dallas + * + * 6 2/16/99 9:36p Jeff + * added low text layout for cinematics + * + * 5 2/14/99 1:16a Jeff + * added canned cinematic function/structures. Added a flag to push + * target to end of path if on a path (quick exit). Added canned intro + * cine. Determine correct velocity on player ship for intro cine. + * + * 4 2/12/99 2:45a Jeff + * added end-cinematic transitions + * + * 3 2/02/99 3:58p Jeff + * started to implement level intro cinematic (need ai functions first). + * No longer need camera object to do cinematic (auto-created)...path + * cameras use speed based on distance needed to travel. + * + * 2 2/01/99 12:55p Jeff + * restore correct hud mode, added flag to stop cinematics if target dies + * + * 1 1/31/99 10:28p Jeff +* +* $NoKeywords: $ +*/ + + +#ifndef __GAMECINEMATICS_EXTERNAL_H_ +#define __GAMECINEMATICS_EXTERNAL_H_ + +#include "vecmat_external.h" +#include "manage_external.h" + +/* +========================================================== + Canned Cinematics +*/ +#define CANNED_LEVEL_INTRO 1 +#define CANNED_LEVEL_END_PATH 2 //end level with camera on path +#define CANNED_LEVEL_END_POINT 3 //end level with camera on point +#define CANNED_MOVE_PLAYER_FADE 4 //fade the screen out and move the player to a new position +#define CANNED_LEVEL_END_FADE_WHITE 5 //fade the screen to white and end level + +typedef struct +{ + int type; + /* + CANNED_LEVEL_INTRO: + camera_pathid + target_pathid + text_to_display + time + */ + /* + CANNED_LEVEL_END_PATH + camera_pathid + target_pathid + text_to_display + time + */ + /* + CANNED_LEVEL_END_POINT + object_to_use_for_point + target_pathid + text_to_display + time + */ + /* + CANNED_MOVE_PLAYER_FADE + room //destination room + pos //destination pos + orient //destination orient + target_objhandle //the player + */ + /* + CANNED_LEVEL_END_FADE_WHITE + time + text_to_display + */ + + int camera_pathid; + int target_pathid; + char *text_to_display; + + int target_objhandle; + + int room; + + float time; + + int object_to_use_for_point; + + vector pos; + matrix orient; + +}tCannedCinematicInfo ; + +//======================================================== + + +// flags for game cinematic struct +#define GCF_LETTERBOX 0x00000000 +#define GCF_FULLSCREEN 0x00000001 +#define GCF_SCREENFORMAT 0x00000001 //mask for screen format to use + +#define GCF_USEPATH 0x00000000 +#define GCF_USEPOINT 0x00000002 +#define GCF_CAMERAPLACEMENT 0x00000002 //mask for camera placement (use path or point) + +#define GCF_TEXT_WIPEIN 0x00000004 +#define GCF_TEXT_FADEINOUT 0x00000008 +#define GCF_TEXT_NOEFFECT 0x00000000 +#define GCF_TEXT_MASK 0x0000000C //mask for text modes + +#define GCF_LAYOUT_BOTTOM 0x00000000 +#define GCF_LAYOUT_TOP 0x00000010 +#define GCF_LAYOUT_MIDDLE 0x00000020 +#define GCF_LAYOUT_LOW 0x00000040 +#define GCF_LAYOUT_MASK 0x00000070 //mask for text layout to use + +#define GCF_STOPIFTAGETDEAD 0x00000100 //stop the cinematic if the target dies +#define GCF_FORCETARGETTOEND 0x00000200 //if there is a quick exit (by keypress) force target position to the end of the path +#define GCF_DONTRESTOREVIEWER 0x00000400 //don't restore the view back to the player (this is really an internal flag and shouldn't be used) +#define GCF_IGNOREPLAYERDEAD 0x00000800 //don't stop cinematic if player dies +#define GCF_DOTRANSBEFORETEXT 0x00001000 //process end transitions before drawing text + +// callback types +#define GCCT_START 0x00000001 //first frame of the movie +#define GCCT_STOP 0x00000002 //last frame of the movie +#define GCCT_FRAME 0x00000003 //frame of the movie + +// Transition types +#define GCTT_NONE 0 +#define GCTT_WACKY 1 +#define GCTT_FADE 2 +#define GCTT_FADEINOUT 3 +#define GCTT_FADEWHITE 4 + +typedef struct +{ + float min,max; //0.0f->1.0f +}PercentageRange; + +typedef struct +{ + unsigned int flags; + + int target_objhandle; + + int end_transition; + int start_transition; + + // the name of the path or the position that the camera should be at + int pathid; + vector position; + matrix *orient; + int room; + + // the total time that the cinematic should play + float max_time_play; + + //called every frame while the cinematic is processing + void (*callback)(int type); + + //ranges (based on max_time_play) + PercentageRange text_display; // range that the text should be displayed + PercentageRange track_target; // range that the camera should track the target + PercentageRange player_disabled; // range that the player can't move/fire/etc. + PercentageRange in_camera_view; // range that the view is from the camera + PercentageRange quick_exit; // range where a key-press will quick exit cinematic + +}tGameCinematic; + + +#endif \ No newline at end of file diff --git a/Descent3/gameevent.cpp b/Descent3/gameevent.cpp new file mode 100644 index 000000000..44706b1c3 --- /dev/null +++ b/Descent3/gameevent.cpp @@ -0,0 +1,264 @@ +/* + * $Logfile: /DescentIII/main/gameevent.cpp $ + * $Revision: 12 $ + * $Date: 2/10/99 1:47p $ + * $Author: Matt $ + * + * Code for event system + * + * $Log: /DescentIII/main/gameevent.cpp $ + * + * 12 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 11 1/06/99 1:46p Jeff + * added object detonator code (auto cancel game event on object death) + * + * 10 10/14/98 5:14p Jason + * added better comments + * + * 9 10/13/98 12:03p Kevin + * Changed use of preprocessors for debug, etc. + * + * 8 5/26/98 2:49p Jason + * fixed FOV bug + * + * 7 3/31/98 3:49p Jason + * added memory lib + * + * 6 1/23/98 12:08p Jeff + * Added spew effect system + * + * 5 12/01/97 9:56a Chris + * Added some warnings for overflow of events + * + * 4 11/18/97 4:37p Jason + * implemented some more changes to event system + * + * 3 11/12/97 1:14p Jason + * redid the game event system to be much more flexible + * + * 2 9/22/97 6:18p Matt + * Removed include of stub.h + * + * $NoKeywords: $ + */ + +#include "object.h" +#include "game.h" +#include "gameevent.h" +#include "descent.h" +#include "gameloop.h" +#include "spew.h" +#include +//#include +#include "mem.h" +#include "player.h" + + +int Num_events=0; +game_event GameEvent[MAX_EVENTS]; + + +// Inits the event system +void InitEvents() +{ + int i; + for (i=0;itype==OBJ_GHOST) || (obj->type==OBJ_PLAYER && Players[obj->id].flags&(PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD) ) ){ + //the object has died, kill the event + FreeEvent (i); + skip_event = true; + mprintf((0,"Game Event %d cancelled\n",i)); + } + } + + if (GameEvent[i].type!=RENDER_EVENT && GameEvent[i].end_time<=Gametime && !skip_event) + { + HandleEvent (&GameEvent[i]); + FreeEvent (i); + } + + } + } + + SpewEmitAll(); +} + + +// Goes through all events and returns the first index of the id matching the argument passed. +// If not match found, returns -1 +int FindEventID (int id) +{ + int i; + + for (i=0;itype=type; + ge->id=id; + ge->start_time=Gametime; + ge->end_time=Gametime+length; + ge->frame_born=FrameCount; + ge->objhandle_detonator = objhandle_detonate; + + if (size) + { + ge->data=(void *)mem_malloc (size); + ASSERT (ge->data); + memcpy (ge->data,data,size); + } + + ge->subfunction=subfunction; + + return num; +} + +// Does a specific action according to an event +void HandleEvent (game_event *ge) +{ + switch (ge->type) + { + case RENDER_EVENT: + ge->subfunction(ge-GameEvent,ge->data); + break; + default: + ge->subfunction(ge-GameEvent,ge->data); + break; + } +} + + + + + diff --git a/Descent3/gameevent.h b/Descent3/gameevent.h new file mode 100644 index 000000000..91261fdfd --- /dev/null +++ b/Descent3/gameevent.h @@ -0,0 +1,65 @@ +#ifndef GAMEEVENT_H +#define GAMEEVENT_H + +#include "game.h" +#include "object.h" + + +#define MAX_EVENTS 500 + +// Game event types +#define OBJECT_EVENT 1 +#define RENDER_EVENT 2 + +// IDs +#define UNKNOWN_EVENT 0 +#define FUSION_EFFECT 1 +#define DAMAGE_EFFECT 2 +#define SCREEN_BLEND 3 +#define BLAST_RING_EVENT 4 +#define FOV_CHANGE_EVENT 5 +#define EDRAIN_EFFECT 6 + +#define D3X_TIMED_EVENT 256 + + +typedef struct game_event +{ + int type; + int id; + int objhandle_detonator; //watch this object, if it dies/gets killed than cancel this game event + ubyte used; + float start_time,end_time; + int frame_born; + + void *data; + + void(*subfunction)(int,void *); +} game_event; + + +// Adds and event to the list. The event will trigger at Gametime+length +int CreateNewEvent (int type,int id,float length,void *data,int size,void(*subfunction)(int eventnum,void *data),int objhandle_detonator=OBJECT_HANDLE_NONE); + +// Processes all pending events, removing the ones that are expired +void ProcessNormalEvents (); +void ProcessRenderEvents(); + +// Does a specific action according to an event +void HandleEvent (game_event *); + +// Clears the event list +void ClearAllEvents(); + +// Returns -1 if can't find event, else index of event int GameEvent array +int FindEventID (int id); + + +// Frees an event for use by others +void FreeEvent (int index); + +extern game_event GameEvent[MAX_EVENTS]; +extern int Num_events; + + +#endif \ No newline at end of file diff --git a/Descent3/gamefile.cpp b/Descent3/gamefile.cpp new file mode 100644 index 000000000..e2d51bf5d --- /dev/null +++ b/Descent3/gamefile.cpp @@ -0,0 +1,116 @@ +#include "pstypes.h" +#include "pserror.h" + +#include +#include +#include +#include +#include "game.h" +#include "gamefile.h" + + +gamefile Gamefiles[MAX_GAMEFILES]; +int Num_gamefiles=0; + +// Sets all gamefiles to unused +void InitGamefiles() +{ + for (int i=0;i0); + + Gamefiles[n].used=0; + Gamefiles[n].name[0]=0; + Num_gamefiles--; +} + +// Gets next gamefile from n that has actually been alloced +int GetNextGamefile (int n) +{ + int i; + + ASSERT (n>=0 && n=0 && n=0;i--) + { + if (Gamefiles[i].used) + return i; + } + for (i=MAX_GAMEFILES-1;i>n;i--) + { + if (Gamefiles[i].used) + return i; + } + + // this is the only one + return n; + +} +// Searches thru all gamefiles for a specific name, returns -1 if not found +// or index of gamefile with name +int FindGamefileName (char *name) +{ + int i; + + ASSERT (name!=NULL); + + for (i=0;i +#include "gamefont.h" +#include "game.h" +#include "grtext.h" +#include "pserror.h" +#include "stringtable.h" + +int Game_fonts[NUM_FONTS] = {0,0,0,0,0,0,}; + +//HUD font sizes +#define HUD_FONT_LOWRES 0 +#define HUD_FONT_HIGHRES 1 + +char *HUD_font_files[] = {"lohud.fnt","hihud.fnt"}; + +#define HUD_FONT_FILE HUD_font_files[HUD_font_resolution] + +static int HUD_font_resolution = HUD_FONT_LOWRES; +static bool Hud_font_template_init = false; +tFontTemplate Hud_font_template; // retain hud font template + +void FreeAuxFontData() +{ + if (Hud_font_template_init) { + Hud_font_template_init = false; + grfont_FreeTemplate(&Hud_font_template); + } +} + +//Loads the font and returns the handle. Exits with an error if the font can't be found. +int LoadFont(char *font_name) +{ + int handle = grfont_Load(font_name); + if (handle == -1) + Error(TXT_ERRNOFONT,font_name); + + mprintf((0,"Font <%s> height = %d\n",font_name,grfont_GetHeight(handle))); + + return handle; +} + +// loads all game fonts. +void LoadAllFonts() +{ + grfont_Reset(); + + atexit(FreeAuxFontData); + + if (Hud_font_template_init) { + grfont_FreeTemplate(&Hud_font_template); + } + + //Load the fonts (except HUD) + SMALL_FONT = LoadFont("briefing.fnt"); + BIG_FONT = LoadFont("bbriefing.fnt"); + MENU_FONT = LoadFont("newmenu.fnt"); + SMALL_UI_FONT = LoadFont("smallui.fnt"); + BIG_UI_FONT = LoadFont("largeui.fnt"); + + //Load the HUD font + HUD_font_resolution = HUD_FONT_LOWRES; + HUD_FONT = LoadFont(HUD_FONT_FILE); + if (!grfont_LoadTemplate(HUD_FONT_FILE, &Hud_font_template)) + Error(TXT_ERRNOFONT, HUD_FONT_FILE); + Hud_font_template_init = true; +} + +//If screen width greater than this, use the superhires fonts +#define SUPERHIRES_THRESHOLD_W 1024 + +//Set the HUD font resolution based on the render width +void SelectHUDFont(int rend_width) +{ + int fontres = (rend_width >= SUPERHIRES_THRESHOLD_W) ? HUD_FONT_HIGHRES : HUD_FONT_LOWRES; + + if (fontres == HUD_font_resolution) + return; + + HUD_font_resolution = fontres; + + grfont_Free(HUD_FONT); + + HUD_FONT = LoadFont(HUD_FONT_FILE); +} + diff --git a/Descent3/gamefont.h b/Descent3/gamefont.h new file mode 100644 index 000000000..f26a562fd --- /dev/null +++ b/Descent3/gamefont.h @@ -0,0 +1,109 @@ +/* + * $Logfile: /DescentIII/Main/gamefont.h $ + * $Revision: 17 $ + * $Date: 5/02/99 11:04p $ + * $Author: Matt $ + * + * Game fonts + * + * $Log: /DescentIII/Main/gamefont.h $ + * + * 17 5/02/99 11:04p Matt + * Cleaned up HUD font code, now that we have only two sizes of HUD fonts. + * + * 16 4/27/99 2:13p Matt + * Replaced the two (new) new UI fonts with one new font from Doug. + * + * 15 4/26/99 10:34p Matt + * Deleted now-unused UI font. + * + * 14 4/25/99 5:19p Matt + * Revised font definition & loading + * + * 13 4/23/99 9:25p Matt + * Killed 'SMALL_FONT', since it was never being used. + * + * 12 4/16/99 5:06p Matt + * Deleted big UI font, which was unused. + * + * 11 3/02/99 6:26p Samir + * hires font madness. + * + * 10 2/28/99 3:23a Samir + * added superhires fonts. + * + * 9 2/16/99 12:08p Samir + * added new fonts. + * + * 8 10/20/98 3:20p Samir + * added big ui font. + * + * 7 5/15/98 5:35p Samir + * allow usage of hires and lores fonts. + * + * 6 4/16/98 6:51a Samir + * added big briefing font. + * + * 5 3/05/98 6:40p Samir + * New UI Font. + * + * 4 1/12/98 5:23p Samir + * New fonts. + * + * 3 12/29/97 5:46p Samir + * Modified gamefont system. + * + * 2 11/10/97 12:56p Samir + * Macros for font rendering using the renderer. + * + * 1 11/04/97 2:03p Samir + * Game font manager. + * + * $NoKeywords: $ + */ + + +#ifndef GAMEFONT_H +#define GAMEFONT_H + +#include "grtext.h" + +extern int Game_fonts[]; // D3 font handles + +//Font defines + +#define NUM_FONTS 6 //How many fonts we have + +//Indices for the font handles +#define SMALL_FONT_INDEX 0 //Basic small font, used everywhere. Originally designed for briefing +#define BIG_FONT_INDEX 1 //Basic big Font, used for titles. +#define HUD_FONT_INDEX 2 //Dropshadowed font so can be seen over variable background +#define MENU_FONT_INDEX 3 //Main menu font, also used for some multiplayer messages +#define SMALL_UI_FONT_INDEX 4 //For the new UI +#define BIG_UI_FONT_INDEX 5 //For the new UI + +//Handles to the fonts +#define SMALL_FONT Game_fonts[SMALL_FONT_INDEX] +#define BIG_FONT Game_fonts[BIG_FONT_INDEX] +#define HUD_FONT Game_fonts[HUD_FONT_INDEX] +#define MENU_FONT Game_fonts[MENU_FONT_INDEX] +#define SMALL_UI_FONT Game_fonts[SMALL_UI_FONT_INDEX] +#define BIG_UI_FONT Game_fonts[BIG_UI_FONT_INDEX] + +//These are equivalencies for the base fonts +#define BRIEFING_FONT SMALL_FONT +#define BIG_BRIEFING_FONT BIG_FONT +#define BRIEF_FONT_INDEX SMALL_FONT_INDEX +#define BBRIEF_FONT_INDEX BIG_FONT_INDEX +#define MONITOR9_NEWUI_FONT SMALL_UI_FONT +#define MONITOR15_NEWUI_FONT BIG_UI_FONT +#define GADGET9_NEWUI_FONT SMALL_UI_FONT +#define GADGET15_NEWUI_FONT BIG_UI_FONT + +// loads all game fonts. +void LoadAllFonts(); + +//Set the HUD font resolution based on the render width +void SelectHUDFont(int rend_with); + +#endif \ No newline at end of file diff --git a/Descent3/gameloop.h b/Descent3/gameloop.h new file mode 100644 index 000000000..923fc428c --- /dev/null +++ b/Descent3/gameloop.h @@ -0,0 +1,117 @@ +/* + * $Logfile: /DescentIII/main/gameloop.h $ + * $Revision: 16 $ + * $Date: 4/30/99 12:02p $ + * $Author: Matt $ + * + * Header for gameloop.c + * + * $Log: /DescentIII/main/gameloop.h $ + * + * 16 4/30/99 12:02p Matt + * Keep the rear view up between levels, but close the other camera views. + * + * 15 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 14 10/21/98 11:14a Samir + * added generic code to skip rendering while in game controller config or + * telcom. + * + * 13 8/24/98 10:40a Jason + * fixed some rendering problems + * + * 12 3/03/98 12:10p Samir + * Added simple pausing. + * + * 11 2/25/98 2:05p Jason + * did FOV and object visibility changes + * + * 10 2/24/98 2:31p Jason + * added FOV warp effect for when coming back to life + * + * 9 2/18/98 3:25p Samir + * Added test systems code. + * + * 8 2/12/98 6:51p Matt + * Renamed GameLoop() to be GameFrame(), cleaned up the StartTime/StopTime + * system, and moved the interval script call to GameFrame(). + * + * 7 2/09/98 3:54p Matt + * Added prototype + * + * 6 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 5 2/04/98 12:50a Matt + * Added ability to view from a gun point in small views. + * Made pop-up view separate from and bigger than other small views. + * Added real keys to set view mode in the three small views. + * Changed rendering functions a bit to accommodate smallview changes. + * + * 4 1/30/98 2:56p Matt + * Added support for small views. Made R toggle rear view, and Delete-F8 + * show a view from object 2. Also changed main window render flow so + * could share code with small views. + * + * 3 1/06/98 1:28p Matt + * Cleaned up interfaces to rendering routines, deleted unused code, etc. + * + * 2 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * 3 5/05/97 4:47p Jason + * made terrain and mine rendering work without explicit calls to + * g3_Startframe in their main function calls + * This allows us to integrate mine/terrain engines nicely + * + * 2 2/03/97 8:14p Matt + * Added gameloop.cpp & gameloop.h + * + * 1 2/03/97 6:30p Matt + * + * $NoKeywords: $ + */ + + +#include "grdefs.h" +#include "object.h" + +//Do all game functions for one frame: render, move objects, etc. +void GameFrame(void); + +//Render the world into a game window +//Parameters: viewer - if not null, this object disabled from rendering. Not used otherwise. +// viewer_eye - where we're rendering from +// viewer_roomnum - the roomnum viewer_eye is in +// viewer_orient - the oriention for this view +// zoom - the zoom for this view +// rear_view - if true, we're looking out the rear of this object +void GameRenderWorld(object *viewer,vector *viewer_eye,int viewer_roomnum,matrix *viewer_orient,float zoom,bool rear_view); + +extern float Render_zoom; +extern float Render_FOV; + +extern bool Game_paused; // determines if game is paused. + +extern bool Rendering_main_view; // determines if we're rendering the main view +extern bool Skip_render_game_frame; // skips rendering the game frame if set. + + +//Turn off all camera views +//If total reset is true, set all views to none, otherwise kill object view but keep rear views. +void InitCameraViews(bool total_reset); + +//Pauses game +void PauseGame(); + +// resumes game operation. +void ResumeGame(); + +#ifdef _DEBUG +// initializes test systems. +void InitTestSystems(); +#endif + + diff --git a/Descent3/gamepath.cpp b/Descent3/gamepath.cpp new file mode 100644 index 000000000..64c954695 --- /dev/null +++ b/Descent3/gamepath.cpp @@ -0,0 +1,133 @@ +/* + * $Logfile: /DescentIII/main/gamepath.cpp $ + * $Revision: 15 $ + * $Date: 10/08/98 4:23p $ + * $Author: Kevin $ + * + * Game automated path routines. + * + * $Log: /DescentIII/main/gamepath.cpp $ + * + * 15 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 14 2/10/98 10:48a Matt + * Moved editor code from gamepath.cpp to epath.cpp + * + * 13 2/05/98 4:25p Chris + * Fixed a few bugs in the path system, improved the insert node function, + * and made the fvec and uvec of the node work (is even loaded and saved) + * correctly. + * + * 12 2/05/98 1:42p Chris + * Fixed the problems with DrawNumber. It use to only be able to draw + * numbers between 0 and 9. + * + * 11 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 10 2/03/98 5:01p Chris + * UPdated the path system + * + * 9 2/02/98 8:16p Chris + * FIxed some problems and added some functionality + * + * 8 1/27/98 12:01p Chris + * Game path system now works with new, load, and save in the editor. + * Also, the game path system is further bug proved. + * + * 7 1/26/98 6:32p Chris + * Incremental fixes to the path system. + * + * 6 12/23/97 6:18p Samir + * Moved all gr.h references to EDITOR block. + * + * 5 9/17/97 1:22p Samir + * BIG SEGMENT RIPOUT + * + * $NoKeywords: $ + */ + +#include "gamepath.h" +#include +#include +#include "pserror.h" +#include "pstypes.h" + +game_path GamePaths[MAX_GAME_PATHS]; +int Num_game_paths = 0; + +// Frees gamepath n for future use +void FreeGamePath (int n) +{ + if(!GamePaths[n].used) return; + + mem_free(GamePaths[n].pathnodes); + mprintf((0, "Path %d lost some\n", n)); + + GamePaths[n].num_nodes = 0; + GamePaths[n].used=0; + Num_game_paths--; +} + +void InitGamePaths() +{ + static bool f_game_paths_init = false; + int i; + + if(f_game_paths_init) + { + // Clear out the current path info + for(i = 0; i < MAX_GAME_PATHS; i++) + { + FreeGamePath(i); + } + } + + f_game_paths_init = true; + + + for(i = 0; i < MAX_GAME_PATHS; i++) + { + GamePaths[i].num_nodes = 0; + GamePaths[i].used=0; + } + + Num_game_paths = 0; +} + +// searches through GamePath index and returns index of path matching name +// returns -1 if not found +int FindGamePathName (char *name) +{ + int i; + + for (i=0;i +#include "matcen.h" +#include "hud.h" +#include "marker.h" +#include "d3music.h" +#include "weather.h" + +#ifdef MACINTOSH +#include "ddio_mac.h" +#endif +// function prototypes. + +void SGSSnapshot(CFILE *fp); + + +#define SAVE_HOTSPOT_ID 0x1000 +#define N_SAVE_SLOTS 8 +#define GAMESAVE_WND_W 448 +#define GAMESAVE_WND_H 384 +#define GAMESAVE_SLOT_W 336 +#define GAMESAVE_SLOT_H 18 +#define GAMESAVE_SLOT_H2 22 +#define GAMESAVE_HELP_Y 2 +#define GAMESAVE_HELP_X 12 +#define GAMESAVE_SLOT_Y 140 +#define GAMESAVE_SLOT_Y2 110 +#define GAMESAVE_SLOT_X 12 + + +extern int Times_game_restored; +// available for all. +int Quicksave_game_slot = -1; + +void QuickSaveGame() +{ + if(Game_mode & GM_MULTI) + { + DoMessageBox(TXT_ERROR, TXT_CANT_SAVE_MULTI, MSGBOX_OK); + return; + } + +#ifdef _DEBUG + if (GetFunctionMode() == EDITOR_GAME_MODE || !Current_mission.filename) { + DoMessageBox(TXT_ERROR, "You need to be playing a mission.", MSGBOX_OK); + return; + } +#endif + + if (Quicksave_game_slot == -1) { + SaveGameDialog(); + } + else { + // verify savegame still exists in the appropriate slot, if not just run dialog, if so then save + char filename[PSFILENAME_LEN]; + char pathname[PSPATHNAME_LEN]; + char desc[GAMESAVE_DESCLEN+1]; + FILE *fp; + int i; + + i = Quicksave_game_slot; + + sprintf(filename, "saveg00%d", i); + ddio_MakePath(pathname, Base_directory, "savegame", filename, NULL); + +#ifdef MACINTOSH + fp = mac_fopen(pathname, "rb"); +#else + fp = fopen(pathname, "rb"); +#endif + if (fp) { + // slot valid, save here. + fclose(fp); + if (GetGameStateInfo(pathname, desc)) { + if (SaveGameState(pathname, desc)) { + AddHUDMessage(TXT_QUICKSAVE); + return; // we're okay. game saved. put up hud message? + } + DoMessageBox(TXT_ERROR, TXT_SAVEGAMEFAILED, MSGBOX_OK); + } + } + Quicksave_game_slot = -1; + SaveGameDialog(); + } +} + + +void SaveGameDialog() +{ + newuiTiledWindow wnd; + newuiSheet *sheet; + int i, res; + + char savegame_dir[PSPATHNAME_LEN+1]; + char pathname[PSPATHNAME_LEN+1]; + char filename[PSFILENAME_LEN+1]; + char desc[GAMESAVE_DESCLEN+1]; + bool occupied_slot[N_SAVE_SLOTS]; + + if(Game_mode & GM_MULTI) + { + DoMessageBox(TXT_ERROR, TXT_CANT_SAVE_MULTI, MSGBOX_OK); + return; + } + +#ifdef _DEBUG + if (!Current_mission.filename) { + DoMessageBox(TXT_ERROR, "You need to be playing a mission.", MSGBOX_OK); + return; + } +#endif + +// setup paths. + ddio_MakePath(savegame_dir, Base_directory, "savegame", NULL); +// ddio_MakePath(pathname, savegame_dir, "*.sav", NULL); -unused + +// create savegame directory if it didn't exist before. + if (!ddio_DirExists(savegame_dir)) { + if (!ddio_CreateDir(savegame_dir)) { + DoMessageBox(TXT_ERROR, TXT_ERRCREATEDIR, MSGBOX_OK); + return; + } + } + +// open window + wnd.Create(TXT_SAVEGAME, 0, 0, GAMESAVE_WND_W,GAMESAVE_WND_H); + wnd.Open(); + sheet = wnd.GetSheet(); + + sheet->NewGroup(NULL, GAMESAVE_HELP_X, GAMESAVE_HELP_Y); + sheet->AddText(TXT_SAVEGAMEHELP); + + sheet->NewGroup(NULL, GAMESAVE_SLOT_X, GAMESAVE_SLOT_Y2); +// generate save slots. + for (i =0; i < N_SAVE_SLOTS; i++) + { + FILE *fp; + bool ingroup = (i==0 || i == (N_SAVE_SLOTS-1)) ? true: false; + + sprintf(filename, "saveg00%d", i); + ddio_MakePath(pathname, savegame_dir, filename, NULL); + + occupied_slot[i] = false; + +#ifdef MACINTOSH + fp = mac_fopen(pathname, "rb"); +#else + fp = fopen(pathname, "rb"); +#endif + if (fp) { + fclose(fp); + + if (GetGameStateInfo(pathname, desc)) { + sheet->AddHotspot(desc, GAMESAVE_SLOT_W, GAMESAVE_SLOT_H2, SAVE_HOTSPOT_ID+i, ingroup); + occupied_slot[i] = true; + } + else { + sheet->AddHotspot(TXT_ILLEGALSAVEGAME, GAMESAVE_SLOT_W, GAMESAVE_SLOT_H2, SAVE_HOTSPOT_ID+i, ingroup); + } + } + else { + sheet->AddHotspot(TXT_EMPTY, GAMESAVE_SLOT_W, GAMESAVE_SLOT_H2, SAVE_HOTSPOT_ID+i, ingroup); + } + } + + sheet->NewGroup(NULL, GAMESAVE_WND_W - 148, GAMESAVE_WND_H - 100); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); + + //Mouse clicks from gameplay will be read by the dialog without this flush + ddio_MouseQueueFlush(); + +// do ui. + do + { + res = wnd.DoUI(); + if (res == NEWUIRES_FORCEQUIT) { + break; + } + else if (res >= SAVE_HOTSPOT_ID && res < (SAVE_HOTSPOT_ID+N_SAVE_SLOTS)) { + newuiHotspot *hot; + const char *hot_desc; + int slot = res - SAVE_HOTSPOT_ID; + bool do_save = false; + + // fill edit box with current description. + hot = (newuiHotspot *)sheet->GetGadget(res); + ASSERT(hot); + hot_desc = hot->GetTitle(); + + if (occupied_slot[slot]) { + strcpy(desc, hot_desc); + } + else { + desc[0] = 0; + } + + reenter_save: + if (DoEditDialog(TXT_DESCRIPTION, desc, sizeof(desc)-1)) { + // perform check for duplicate names + // do not allow for empty or space only descriptions + if (strlen(desc) == 0) { + DoMessageBox("", TXT_SAVEGAMENAME, MSGBOX_OK); + goto reenter_save; + } + for (i = 0; i < N_SAVE_SLOTS; i++) + { + hot = (newuiHotspot *)sheet->GetGadget(SAVE_HOTSPOT_ID+i); + hot_desc = hot->GetTitle(); + + if (occupied_slot[i] && strcmpi(hot_desc, desc) == 0 && slot != i) { + DoMessageBox("", TXT_SAVEGAMEDUP, MSGBOX_OK); + goto reenter_save; + } + } + + do_save = occupied_slot[slot] ? (DoMessageBox(TXT_WARNING, TXT_OVERWRITESAVE, MSGBOX_YESNO)?true:false) : true; + + if (do_save) { + sprintf(filename, "saveg00%d", slot); + ddio_MakePath(pathname, savegame_dir, filename, NULL); + if (!SaveGameState(pathname, desc)) { + DoMessageBox("", TXT_SAVEGAMEFAILED, MSGBOX_OK); + } + else { + AddHUDMessage(TXT_QUICKSAVE); + res = UID_CANCEL; + Quicksave_game_slot = slot; + } + } + } + } + } + while (res != UID_CANCEL); + + wnd.Close(); + wnd.Destroy(); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +typedef struct tLoadGameDialogData +{ + int cur_slot; + chunked_bitmap chunk; +} +tLoadGameDialogData; + +#if defined(LINUX) +void LoadGameDialogCB(newuiTiledWindow *wnd, void *data) +#else +void __cdecl LoadGameDialogCB(newuiTiledWindow *wnd, void *data) +#endif +{ + tLoadGameDialogData *cb_data = (tLoadGameDialogData *)data; + UIGadget *cur_gadget; + int id; + + cur_gadget = wnd->GetFocus(); + + for (id = SAVE_HOTSPOT_ID; id < (SAVE_HOTSPOT_ID+N_SAVE_SLOTS); id++) + { + if (cur_gadget->GetID() == id) { + break; + } + } + + if (id < (SAVE_HOTSPOT_ID+N_SAVE_SLOTS) && id != cb_data->cur_slot) { + // new bitmap to be displayed! + char filename[PSFILENAME_LEN+1]; + char pathname[_MAX_PATH]; + char savegame_dir[_MAX_PATH]; + char desc[GAMESAVE_DESCLEN+1]; + int bm_handle; + + if (cb_data->chunk.bm_array) { + bm_DestroyChunkedBitmap(&cb_data->chunk); + } + + mprintf((0, "savegame slot=%d\n", id-SAVE_HOTSPOT_ID)); + + ddio_MakePath(savegame_dir, Base_directory, "savegame", NULL); + sprintf(filename, "saveg00%d", (id-SAVE_HOTSPOT_ID)); + ddio_MakePath(pathname, savegame_dir, filename, NULL); + + if (GetGameStateInfo(pathname, desc, &bm_handle)) { + if (bm_handle > 0) { + bm_CreateChunkedBitmap(bm_handle, &cb_data->chunk); + bm_FreeBitmap(bm_handle); + } + } + cb_data->cur_slot = id; + } + +// draw bitmap if there is one + if (cb_data->chunk.bm_array) { + UIBitmapItem bm_item; + int x,y; + bm_item.set_chunked_bitmap(&cb_data->chunk); + x = (wnd->W()-bm_item.width())/2; + y = (wnd->H()-bm_item.height())/4; + bm_item.draw(x,y); + } +} + + + +bool LoadGameDialog() +{ + tLoadGameDialogData lgd_data; + newuiTiledWindow wnd; + newuiSheet *sheet; + int i, res; + bool retval=true; + + char savegame_dir[PSPATHNAME_LEN+1]; + char pathname[PSPATHNAME_LEN+1]; + char filename[PSFILENAME_LEN+1]; + char desc[GAMESAVE_DESCLEN+1]; + bool occupied_slot[N_SAVE_SLOTS], loadgames_avail=false; + + if(Game_mode & GM_MULTI) + { + DoMessageBox(TXT_ERROR, TXT_CANT_LOAD_MULTI, MSGBOX_OK); + return false; + } + +// setup paths. + ddio_MakePath(savegame_dir, Base_directory, "savegame", NULL); + ddio_MakePath(pathname, savegame_dir, "*.sav", NULL); + +// create savegame directory if it didn't exist before. + if (!ddio_DirExists(savegame_dir)) { + DoMessageBox(TXT_ERROR, TXT_ERRNOSAVEGAMES, MSGBOX_OK); + return false; + } + +// open window + wnd.Create(TXT_LOADGAME, 0, 0, GAMESAVE_WND_W,GAMESAVE_WND_H); + wnd.Open(); + sheet = wnd.GetSheet(); + + sheet->NewGroup(NULL, GAMESAVE_HELP_X, GAMESAVE_HELP_Y); + sheet->AddText(TXT_LOADGAMEHELP); + + sheet->NewGroup(NULL, GAMESAVE_SLOT_X, GAMESAVE_SLOT_Y); + +// generate save slots. + lgd_data.cur_slot = SAVE_HOTSPOT_ID; + lgd_data.chunk.bm_array = NULL; + + for (i =0; i < N_SAVE_SLOTS; i++) + { + FILE *fp; + bool ingroup = (i==0 || i == (N_SAVE_SLOTS-1)) ? true: false; + + sprintf(filename, "saveg00%d", i); + ddio_MakePath(pathname, savegame_dir, filename, NULL); + + occupied_slot[i] = false; + +#ifdef MACINTOSH + fp = mac_fopen(pathname, "rb"); +#else + fp = fopen(pathname, "rb"); +#endif + if (fp) { + int bm_handle; + int *pbm_handle; + fclose(fp); + + if (lgd_data.cur_slot == (SAVE_HOTSPOT_ID+i)) { + pbm_handle = &bm_handle; + } + else { + pbm_handle = NULL; + } + + if (GetGameStateInfo(pathname, desc, pbm_handle)) { + sheet->AddHotspot(desc, GAMESAVE_SLOT_W, GAMESAVE_SLOT_H, SAVE_HOTSPOT_ID+i, ingroup); + occupied_slot[i] = true; + loadgames_avail = true; + + // create chunk + if (pbm_handle && bm_handle > 0) { + if (lgd_data.chunk.bm_array) { + bm_DestroyChunkedBitmap(&lgd_data.chunk); + } + bm_CreateChunkedBitmap(bm_handle, &lgd_data.chunk); + bm_FreeBitmap(bm_handle); + } + } + else { + sheet->AddHotspot(TXT_ILLEGALSAVEGAME, GAMESAVE_SLOT_W, GAMESAVE_SLOT_H, SAVE_HOTSPOT_ID+i, ingroup); + } + } + else { + sheet->AddHotspot(TXT_EMPTY, GAMESAVE_SLOT_W, GAMESAVE_SLOT_H, SAVE_HOTSPOT_ID+i, ingroup); + } + } + + if (!loadgames_avail) { + wnd.Close(); + DoMessageBox("", TXT_ERRNOSAVEGAMES, MSGBOX_OK); + wnd.Open(); + retval = false; + goto loadgame_fail; + } + + sheet->NewGroup(NULL, GAMESAVE_WND_W - 148, GAMESAVE_WND_H - 100); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); + + wnd.SetData(&lgd_data); + wnd.SetOnDrawCB(LoadGameDialogCB); + + //Mouse clicks from gameplay will be read by the dialog without this flush + ddio_MouseQueueFlush(); + +// do ui. + do + { + res = wnd.DoUI(); + if (res == NEWUIRES_FORCEQUIT) { + retval = false; + break; + } + else if (res >= SAVE_HOTSPOT_ID && res < (SAVE_HOTSPOT_ID+N_SAVE_SLOTS)) { + int slot = res - SAVE_HOTSPOT_ID; + + if (occupied_slot[slot]) { + sprintf(filename, "saveg00%d", slot); + ddio_MakePath(pathname, savegame_dir, filename, NULL); + strcpy(LGS_Path, pathname); + SetGameState(GAMESTATE_LOADGAME); + res = UID_CANCEL; + } + } + else if (res == UID_CANCEL) { + retval = false; + } + } + while (res != UID_CANCEL); + +loadgame_fail: + if (lgd_data.chunk.bm_array) { + bm_DestroyChunkedBitmap(&lgd_data.chunk); + } + + wnd.Close(); + wnd.Destroy(); + + return retval; +} + + +////////////////////////////////////////////////////////////////////////////// + + +// loads savegame as specified from LoadGameDialog. +bool LoadCurrentSaveGame() +{ + int retval = LoadGameState(LGS_Path); + if (retval != LGS_OK) { + Int3(); + DoMessageBox(TXT_ERROR, TXT_LOADGAMEFAILED, MSGBOX_OK); + return false; + } + AddHUDMessage(TXT_GAMERESTORED); + return true; +} + + +////////////////////////////////////////////////////////////////////////////// + +// give a description and slot number (0 to GAMESAVE_SLOTS-1) +bool SaveGameState(const char *pathname, const char *description) +{ + CFILE *fp; + char buf[GAMESAVE_DESCLEN+1]; + short pending_music_region; + + fp = cfopen(pathname, "wb"); + if (!fp) + return false; + + //Delete the old games restored count. + char countpath[_MAX_PATH*2]; + strcpy(countpath,pathname); + strcat(countpath,".cnt"); + CFILE *countfp; + countfp = cfopen(countpath,"wb"); + if(countfp) + { + cf_WriteInt(countfp,Times_game_restored); + cfclose(countfp); + } + +// save out header +START_VERIFY_SAVEFILE(fp); + ASSERT(strlen(description) < sizeof(buf)); + strcpy(buf, description); + cf_WriteBytes((ubyte *)buf, sizeof(buf), fp); + cf_WriteShort(fp, GAMESAVE_VERSION); + + SGSSnapshot(fp); //Save snapshot? MUST KEEP THIS HERE. + +// write out translation tables + SGSXlateTables(fp); + +// write out gamemode information + +// write out mission level information + cf_WriteShort(fp, (short)Current_mission.cur_level); + + if(Current_mission.filename && (strcmpi("d3_2.mn3",Current_mission.filename)==0) ) + { + cf_WriteString(fp, "d3.mn3"); + } + else + { + cf_WriteString(fp, Current_mission.filename ? Current_mission.filename : ""); + } + + + cf_WriteInt(fp, Current_mission.game_state_flags); + + cf_WriteFloat(fp, Gametime); + cf_WriteInt(fp, FrameCount); + cf_WriteInt(fp,Current_waypoint); + + pending_music_region = D3MusicGetPendingRegion(); + if (pending_music_region < 0) { + pending_music_region = D3MusicGetRegion(); + } + cf_WriteShort(fp, pending_music_region); + + //cf_WriteInt(fp,Times_game_restored); + //Save weather + cf_WriteInt(fp,sizeof(Weather)); + cf_WriteBytes((ubyte *)&Weather,sizeof(Weather),fp); + + //Save active doorways + cf_WriteInt(fp,MAX_ACTIVE_DOORWAYS); + cf_WriteInt(fp,Num_active_doorways); + + for(int d=0;d -1) { + gs_WriteShort(fp, Objects[i].ctype.blast_info.bm_handle); + cf_WriteString(fp, GameBitmaps[Objects[i].ctype.blast_info.bm_handle].name); + } + } + gs_WriteShort(fp, -1); // terminate bitmap list + cf_WriteString(fp, ""); +END_VERIFY_SAVEFILE(fp, "Xlate save"); +} + +extern ubyte AutomapVisMap[MAX_ROOMS]; +// initializes rooms +void SGSRooms(CFILE *fp) +{ + int i,f,p; + + gs_WriteShort(fp, (short)Highest_room_index); + + gs_WriteShort(fp,MAX_ROOMS); + + for (i = 0; i < MAX_ROOMS; i++) + { + gs_WriteByte(fp, AutomapVisMap[i]); + } + + for (i = 0; i <= Highest_room_index; i++) + { + gs_WriteByte(fp, Rooms[i].used); + if (Rooms[i].used) { + // we need to save some room info out. + gs_WriteInt(fp,Rooms[i].flags); + gs_WriteByte(fp,Rooms[i].pulse_time); + gs_WriteByte(fp,Rooms[i].pulse_offset); + gs_WriteVector(fp,Rooms[i].wind); + gs_WriteFloat(fp, Rooms[i].last_render_time); + gs_WriteFloat(fp, Rooms[i].fog_depth); + gs_WriteFloat(fp, Rooms[i].fog_r); + gs_WriteFloat(fp, Rooms[i].fog_g); + gs_WriteFloat(fp, Rooms[i].fog_b); + gs_WriteFloat(fp, Rooms[i].damage); + + + //?? gs_WriteFloat(fp, Rooms[i].ambient_sound); // need to save an index of sounds. + + // save additional face information here. + // save texture changes + int num_changed=0; + for (f = 0; f < Rooms[i].num_faces; f++) + { + if (Rooms[i].faces[f].flags & FF_TEXTURE_CHANGED) + num_changed++; + } + cf_WriteShort (fp,num_changed); + for (f=0;fstate); + gs_WriteByte(fp, dp->flags); + gs_WriteByte(fp, dp->keys_needed); + gs_WriteFloat(fp, dp->position); + gs_WriteFloat(fp, dp->dest_pos); + gs_WriteInt(fp, dp->sound_handle); + gs_WriteInt(fp, dp->activenum); + gs_WriteInt(fp, dp->doornum); + } + } + } +} + + +// saves out events +void SGSEvents(CFILE *fp) +{ +} + + +// saves out triggers +void SGSTriggers(CFILE *fp) +{ + int i; + + gs_WriteShort(fp, (short)Num_triggers); + + for (i = 0; i < Num_triggers; i++) + { + gs_WriteShort(fp, Triggers[i].flags); + gs_WriteShort(fp, Triggers[i].activator); + + // write script info +//@@ SGSScript(fp, &Triggers[i].script); + } +} + + +// players +void SGSPlayers(CFILE *fp) +{ +// player struct needs savin + player *plr = &Players[0]; + + gs_WriteShort(fp, sizeof(player)); + cf_WriteBytes((ubyte *)plr, sizeof(player), fp); + if (plr->guided_obj) + gs_WriteInt(fp, plr->guided_obj->handle); + + // save inventory and countermeasures + plr->inventory.SaveInventory(fp); + plr->counter_measures.SaveInventory(fp); +} + + +// save viseffects +void SGSVisEffects(CFILE *fp) +{ + int i, count=0; + +// count up all viseffects to write out. + for (i = 0; i <= Highest_vis_effect_index; i++) + if (VisEffects[i].type != VIS_NONE) + count++; + + gs_WriteShort(fp, (short)count); + + for (i = 0; i <= Highest_vis_effect_index; i++) + { + if (VisEffects[i].type != VIS_NONE) + cf_WriteBytes((ubyte *)&VisEffects[i], sizeof(vis_effect), fp); + } +} + +extern int Physics_NumLinked; +extern int PhysicsLinkList[MAX_OBJECTS]; +extern char MarkerMessages[MAX_PLAYERS*2][MAX_MARKER_MESSAGE_LENGTH]; +extern int Marker_message; +void InsureSaveGame(CFILE *fp) +{ + cf_WriteInt(fp,0xF00D4B0B); + +} + +#define INSURE_SAVEFILE +//InsureSaveGame(fp) + +// saves out objects +void SGSObjects(CFILE *fp) +{ + int i, j; + +START_VERIFY_SAVEFILE(fp); + + //Save marker info (text) + cf_WriteInt(fp,Marker_message); + cf_WriteShort(fp, (short)MAX_PLAYERS*2); + for(i=0;irtype.pobj_info.model_num]; + + + gs_WriteInt(fp,0xBADB0B); + // we don't save deleted objects or room objects since they're reconstructed on loadlevel + gs_WriteByte(fp, (sbyte)op->type); + + if (op->type == OBJ_NONE) + continue; + gs_WriteByte(fp, (sbyte)op->lighting_render_type); + + //Store whether or not we have a pointer to lighting_info + gs_WriteByte(fp,op->lighting_info?1:0); + if(op->lighting_info) + { + cf_WriteBytes((ubyte *)op->lighting_info,sizeof(*op->lighting_info),fp); + } + + + // these objects FOR NOW won't be saved + gs_WriteInt(fp, op->handle); + ASSERT((op->handle & HANDLE_OBJNUM_MASK) == i); + + // type and handle info. + gs_WriteByte(fp, (sbyte)op->dummy_type); + // positional information + gs_WriteInt(fp, op->roomnum); + gs_WriteVector(fp, op->pos); + gs_WriteVector(fp, op->last_pos); + gs_WriteMatrix(fp, op->orient); + + // write out object name + int ii; + ii = (op->name)?strlen(op->name):0; + gs_WriteByte(fp, ii); + if(ii>0) + cf_WriteBytes((ubyte *)op->name,ii,fp); + + // data universal to all objects that need to be saved. + gs_WriteShort(fp, (short)op->id); + gs_WriteInt(fp, (long)op->flags); + gs_WriteByte(fp, (sbyte)op->control_type); + gs_WriteByte(fp, (sbyte)op->movement_type); + gs_WriteByte(fp, (sbyte)op->render_type); + + gs_WriteShort(fp, (short)op->renderframe); + gs_WriteFloat(fp, op->size); + gs_WriteFloat(fp, op->shields); + gs_WriteByte(fp, op->contains_type); + gs_WriteByte(fp, op->contains_id); + gs_WriteByte(fp, op->contains_count); + gs_WriteFloat(fp, op->creation_time); + gs_WriteFloat(fp, op->lifeleft); + gs_WriteFloat(fp, op->lifetime); + gs_WriteInt(fp, op->parent_handle); + + // attachment info. + gs_WriteInt(fp, op->attach_ultimate_handle); + gs_WriteInt(fp, op->attach_parent_handle); + if((op->attach_ultimate_handle)&&(OBJECT_HANDLE_NONE!=op->attach_ultimate_handle)) + { + mprintf((0,"Object %d has an ultimate parent of %d (%d)\n",i,OBJNUM(ObjGet(op->attach_ultimate_handle)),op->attach_parent_handle )); + } + if((op->attach_ultimate_handle)&&(OBJECT_HANDLE_NONE!=op->attach_parent_handle)) + { + mprintf((0,"Object %d has a parent of %d (%d)\n",i,OBJNUM(ObjGet(op->attach_parent_handle)),op->attach_parent_handle )); + } + + gs_WriteInt(fp, pm->n_attach); + if(pm->n_attach) + { + mprintf((0,"Object %d has %d attach points.\n",i,pm->n_attach)); + + if(op->attach_children) + { + gs_WriteInt(fp, 1); + + for (j = 0; j < pm->n_attach; j++) + gs_WriteInt(fp, op->attach_children[j]); + } + else + { + gs_WriteInt(fp, 0); + } + } + + INSURE_SAVEFILE; + + gs_WriteByte(fp, op->attach_type); + gs_WriteShort(fp, op->attach_index); + gs_WriteFloat(fp, op->attach_dist); + gs_WriteVector(fp, op->min_xyz); + gs_WriteVector(fp, op->max_xyz); + gs_WriteFloat(fp, op->impact_size); + gs_WriteFloat(fp, op->impact_time); + gs_WriteFloat(fp, op->impact_player_damage); + gs_WriteFloat(fp, op->impact_generic_damage); + gs_WriteFloat(fp, op->impact_force); + + // write out custom default script info + ii = (op->custom_default_script_name)?strlen(op->custom_default_script_name):0; + gs_WriteByte(fp, ii); + if(ii>0) + cf_WriteBytes((ubyte *)op->custom_default_script_name,ii,fp); + + ii = (op->custom_default_module_name)?strlen(op->custom_default_module_name):0; + gs_WriteByte(fp, ii); + if(ii>0) + cf_WriteBytes((ubyte *)op->custom_default_module_name,ii,fp); + + INSURE_SAVEFILE; + + gs_WriteShort(fp, (short)op->position_counter); + + INSURE_SAVEFILE; + +// write out all structures here. + // movement info. + gs_WriteShort(fp, sizeof(op->mtype)); + cf_WriteBytes((ubyte *)&op->mtype, sizeof(op->mtype), fp); + + INSURE_SAVEFILE; + + //Control info, determined by CONTROL_TYPE + gs_WriteShort(fp, sizeof(op->ctype)); + cf_WriteBytes((ubyte *)&op->ctype, sizeof(op->ctype), fp); + + INSURE_SAVEFILE; + + // save ai information. + SGSObjAI(fp, op->ai_info); + + INSURE_SAVEFILE; + // save out rendering information + gs_WriteShort(fp, sizeof(op->rtype)); + cf_WriteBytes((ubyte *)&op->rtype, sizeof(op->rtype), fp); + + cf_WriteFloat(fp,op->size); + if(op->render_type==RT_POLYOBJ) + { + //Do Animation stuff + custom_anim multi_anim_info; + ObjGetAnimUpdate(i, &multi_anim_info); + cf_WriteBytes((ubyte *)&multi_anim_info,sizeof(multi_anim_info),fp); + } + + INSURE_SAVEFILE; + + // dynamic weapon battery info!! + SGSObjWB(fp, op, (op->type==OBJ_PLAYER) ? MAX_WBS_PER_OBJ : pm->num_wbs); + + + INSURE_SAVEFILE; + + // save effect info! + SGSObjEffects(fp, op); + + INSURE_SAVEFILE; + + //save script stuff. +//@@ SGSScript(fp, &op->script); + + // special things local to object + SGSObjSpecial(fp, op); + } + mprintf((0, "highest obj index = %d, ", Highest_object_index)); +END_VERIFY_SAVEFILE(fp, "Objects save"); +} + + +// saves ai +void SGSObjAI(CFILE *fp, const ai_frame *ai) +{ + gs_WriteByte(fp, (ai?1:0)); + if (!ai) + return; + + gs_WriteShort(fp, sizeof(ai_frame)); + cf_WriteBytes((ubyte *)ai, sizeof(ai_frame), fp); +} + + +// saves script +//@@void SGSScript(CFILE *fp, const script_info *script) +//@@{ +//@@ int i,j; +//@@ +//@@// ugh, write out script info. +//@@ gs_WriteByte(fp, (script->thread ? 1 : 0)); +//@@ +//@@ if (script->name) +//@@ cf_WriteString(fp, script->name); +//@@ else +//@@ cf_WriteString(fp, ""); +//@@ +//@@ gs_WriteShort(fp, script->num_parms); +//@@ gs_WriteShort(fp, script->is_custom); +//@@ for (i = 0; i < script->num_parms; i++) +//@@ { +//@@ gs_WriteByte(fp, script->parms[i].type); +//@@ gs_WriteFloat(fp, script->parms[i].val.x); +//@@ gs_WriteFloat(fp, script->parms[i].val.y); +//@@ gs_WriteFloat(fp, script->parms[i].val.z); +//@@ } +//@@ +//@@// write out thread data if necessary +//@@ if (script->thread) { +//@@ const vector *mem = D3XGetThreadMem(script->thread->mem_handle); +//@@ ushort mem_size = script->thread->prog->map[script->thread->prog_idx].mem; +//@@ +//@@ gs_WriteShort(fp, mem_size); +//@@ if (mem) { +//@@ for (j = 0; j < mem_size; j++) +//@@ gs_WriteVector(fp, mem[j]); +//@@ } +//@@ } +//@@} + + +// saves fx +void SGSObjEffects(CFILE *fp, const object *op) +{ + effect_info_s *ei = op->effect_info; + + gs_WriteByte(fp, (ei ? 1 : 0)); + if (ei) { + gs_WriteShort(fp, sizeof(effect_info_s)); + cf_WriteBytes((ubyte *)ei, sizeof(effect_info_s), fp); + } +} + + +// saves wb +void SGSObjWB(CFILE *fp, object *op, int num_wbs) +{ + int i; + + if (op->dynamic_wb) { + gs_WriteByte(fp, (sbyte)num_wbs); + for (i =0; idynamic_wb[i]; + cf_WriteBytes((ubyte *)dwb, sizeof(dynamic_wb_info), fp); + } + } + else { + gs_WriteByte(fp, 0); + } +} + + +// saves special object info +void SGSObjSpecial(CFILE *fp, const object *op) +{ +} + + +// load spew +void SGSSpew(CFILE *fp) +{ + int i; + + gs_WriteShort(fp, (short)spew_count); + for (i = 0; i < MAX_SPEW_EFFECTS; i++) + { + gs_WriteByte(fp, SpewEffects[i].inuse ? true : false); + if (SpewEffects[i].inuse) + cf_WriteBytes((ubyte *)&SpewEffects[i], sizeof(spewinfo), fp); + } +} + +// save matcens +void SGSMatcens(CFILE *fp) +{ + cf_WriteInt(fp,Num_matcens); + + for(int i = 0; i < Num_matcens; i++) + { + ASSERT(Matcen[i]); + + Matcen[i]->SaveData(fp); + } +} + + +#define HUD_RENDER_ZOOM 0.56f +void SGSSnapshot(CFILE *fp) +{ + extern void ResetFacings(); // render.cpp + + const int SGSSNAP_WIDTH = 160, + SGSSNAP_HEIGHT = 120; + int oldfilepos, bm_handle; + + oldfilepos = cftell(fp); + + //Set up for rendering + StartFrame(0,0,Max_window_w, Max_window_h); + g3_StartFrame(&Viewer_object->pos,&Viewer_object->orient,HUD_RENDER_ZOOM); + + // Reset facings for mine stuff + ResetFacings(); + + //Render the world + GameRenderWorld(Viewer_object,&Viewer_object->pos,Viewer_object->roomnum,&Viewer_object->orient,Render_zoom,false); + + //Done rendering + g3_EndFrame(); + EndFrame(); + rend_Flip(); +#ifdef MACINTOSH //DAJ do it again to insure dialog is not snaped + //Render the world + GameRenderWorld(Viewer_object,&Viewer_object->pos,Viewer_object->roomnum,&Viewer_object->orient,Render_zoom,false); + + //Done rendering + g3_EndFrame(); + EndFrame(); + rend_Flip(); +#endif + bm_handle=bm_AllocBitmap (Max_window_w,Max_window_h,0); + + cf_WriteByte(fp, (bm_handle>0) ? 1 : 0); + + if (bm_handle > 0) + { + // Tell our renderer lib to take a screen shot + rend_Screenshot (bm_handle); + bm_ChangeSize(bm_handle, SGSSNAP_WIDTH, SGSSNAP_HEIGHT); + + try + { + if (bm_SaveBitmap(fp, bm_handle) < 0) { + cfseek(fp, oldfilepos, SEEK_SET); + cf_WriteByte(fp, 0); + } + } + catch(cfile_error) + { + cfseek(fp, oldfilepos, SEEK_SET); + cf_WriteByte(fp, 0); + } + + bm_FreeBitmap(bm_handle); + } +} + + +//@@ switch (op->control_type) +//@@ { +//@@ case CT_NONE: +//@@ case CT_AI: +//@@ case CT_FLYING: //the player is flying +//@@ case CT_FLYTHROUGH: //the flythrough system +//@@ case CT_SLEW: //slewing +//@@ case CT_PARTICLE: //Particle +//@@ break; +//@@ case CT_EXPLOSION: //explosion sequencer +//@@ gs_WriteFloat(fp, op_expl->spawn_time); +//@@ gs_WriteFloat(fp, op_expl->delete_time); +//@@ gs_WriteShort(fp, op_expl->delete_objnum); +//@@ gs_WriteShort(fp, op_expl->attach_parent); +//@@ gs_WriteShort(fp, op_expl->prev_attach); +//@@ gs_WriteShort(fp, op_expl->next_attach); +//@@ break; +//@@ case CT_DEBRIS: //this is a piece of debris +//@@ gs_WriteFloat(fp, op->ctype.dying_info.delay_time); +//@@ gs_WriteBool(fp, op->ctype.dying_info.f_death_anim); +//@@ break; +//@@ case CT_POWERUP: //animating powerup blob +//@@ gs_WriteInt(fp, op->ctype.powerup_info.count); +//@@ gs_WriteInt(fp, op->ctype.powerup_info.flags); +//@@ break; +//@@ case CT_SPLINTER: //Splinter +//@@ gs_WriteByte(fp, (sbyte)op_splint->subobj_num); +//@@ gs_WriteShort(fp, op_splint->facenum); +//@@ for (j = 0; j < MAX_VERTS_PER_SPLINTER; j++) +//@@ gs_WriteVector(fp, op_splint->verts[j]); +//@@ gs_WriteVector(fp, op_splint->center); +//@@ break; +//@@ case CT_WEAPON: //laser, etc. +//@@ gs_WriteShort(fp, op_wpn->parent_type); +//@@ gs_WriteShort(fp, op_wpn->src_gun_num); +//@@ gs_WriteInt(fp, op_wpn->last_hit_handle); +//@@ gs_WriteInt(fp, op_wpn->track_handle); +//@@ gs_WriteInt(fp, op_wpn->hit_status); +//@@ gs_WriteVector(fp, op_wpn->hit_pnt); +//@@ gs_WriteVector(fp, op_wpn->hit_wall_pnt); +//@@ gs_WriteVector(fp, op_wpn->hit_wall_normal); +//@@ gs_WriteInt(fp, op_wpn->hit_room); +//@@ gs_WriteInt(fp, op_wpn->hit_pnt_room); +//@@ gs_WriteShort(fp, op_wpn->hit_face); +//@@ gs_WriteFloat(fp, op_wpn->multiplier); +//@@ gs_WriteFloat(fp, op_wpn->thrust_left); +//@@ gs_WriteFloat(fp, op_wpn->last_drop_time); +//@@ break; +//@@ +//@@ default: +//@@ Int3(); +//@@ } + +//@@ if (op->type == OBJ_FIREBALL) { +//@@ gs_WriteFloat(fp, op->ctype.blast_info.max_size); +//@@ gs_WriteInt(fp, op->ctype.blast_info.bm_handle); +//@@ } + + +//@@ const int N_SHARD_VERTS = 3; +//@@ switch (op->render_type) +//@@ { +//@@ case RT_NONE: +//@@ case RT_EDITOR_SPHERE: +//@@ case RT_FIREBALL: +//@@ case RT_LINE: +//@@ case RT_PARTICLE: +//@@ case RT_SPLINTER: +//@@ case RT_ROOM: +//@@ break; +//@@ case RT_WEAPON: +//@@ if (!(op->flags & OF_POLYGON_OBJECT)) +//@@ break; +//@@ case RT_POLYOBJ: +//@@ gs_WriteShort(fp, pobji->model_num); +//@@ gs_WriteShort(fp, pobji->dying_model_num); +//@@ gs_WriteFloat(fp, pobji->anim_start_frame); +//@@ gs_WriteFloat(fp, pobji->anim_frame); +//@@ gs_WriteFloat(fp, pobji->anim_end_frame); +//@@ gs_WriteFloat(fp, pobji->anim_time); +//@@ gs_WriteInt(fp, (int)pobji->anim_flags); +//@@ gs_WriteFloat(fp, pobji->max_speed); +//@@ gs_WriteInt(fp, (int)pobji->subobj_flags); +//@@ gs_WriteInt(fp, pobji->tmap_override); +//@@ //!! multi_anim_save here +//@@ break; +//@@ +//@@ case RT_SHARD: +//@@ for (j = 0; j < N_SHARD_VERTS; j++) +//@@ gs_WriteVector(fp, op->rtype.shard_info.points[j]); +//@@ for (j = 0; j < N_SHARD_VERTS; j++) +//@@ gs_WriteFloat(fp, op->rtype.shard_info.u[j]); +//@@ for (j = 0; j < N_SHARD_VERTS; j++) +//@@ gs_WriteFloat(fp, op->rtype.shard_info.v[j]); +//@@ gs_WriteVector(fp, op->rtype.shard_info.normal); +//@@ gs_WriteShort(fp, op->rtype.shard_info.tmap); +//@@ break; +//@@ +//@@ default: +//@@ Int3(); +//@@ } + +// AI WRITES +//@@// the FUN begins. +//@@ gs_WriteByte(fp, ai->ai_class); +//@@ gs_WriteByte(fp, ai->ai_type); +//@@ +//@@// write path info +//@@ const ai_path_info *path = &ai->path; +//@@ gs_WriteInt(fp, path->flags); +//@@ gs_WriteShort(fp, path->cur_path); +//@@ gs_WriteShort(fp, path->cur_node); +//@@ gs_WriteShort(fp, path->num_paths); +//@@ gs_WriteShort(fp, path->goal_index); +//@@ +//@@ for (i = 0; i < MAX_JOINED_PATHS; i++) +//@@ { +//@@ gs_WriteByte(fp, path->path_id[i]); +//@@ gs_WriteByte(fp, path->path_type[i]); +//@@ gs_WriteShort(fp, path->path_start_node[i]); +//@@ gs_WriteShort(fp, path->path_end_node[i]); +//@@ gs_WriteShort(fp, path->path_flags[i]); +//@@ } +//@@ +//@@// continue +//@@ gs_WriteFloat(fp, ai->max_velocity); +//@@ gs_WriteFloat(fp, ai->max_delta_velocity); +//@@ gs_WriteFloat(fp, ai->max_turn_rate); +//@@ gs_WriteFloat(fp, ai->max_delta_turn_rate); +//@@ gs_WriteFloat(fp, ai->attack_vel_percent); +//@@ gs_WriteFloat(fp, ai->flee_vel_percent); +//@@ gs_WriteFloat(fp, ai->dodge_vel_percent); +//@@ gs_WriteFloat(fp, ai->circle_distance); +//@@ gs_WriteFloat(fp, ai->dodge_percent); +//@@ +//@@ for (i = 0; i < 2; i++) +//@@ { +//@@ gs_WriteFloat(fp, ai->melee_damage[i]); +//@@ gs_WriteFloat(fp, ai->melee_latency[i]); +//@@ } +//@@ +//@@ for (i = 0; i < MAX_AI_SOUNDS; i++) +//@@ { +//@@ gs_WriteInt(fp, ai->sound[i]); +//@@ gs_WriteFloat(fp, ai->last_sound_time[i]); +//@@ } +//@@ gs_WriteShort(fp, ai->last_played_sound_index); +//@@ +//@@ gs_WriteByte(fp, ai->movement_type); +//@@ gs_WriteByte(fp, ai->movement_subtype); +//@@ gs_WriteByte(fp, ai->animation_type); +//@@ gs_WriteByte(fp, ai->next_animation_type); +//@@ gs_WriteByte(fp, ai->next_movement); +//@@ gs_WriteByte(fp, ai->current_wb_firing); +//@@ gs_WriteByte(fp, ai->last_wb_firing); +//@@ +//@@// goals +//@@ for (i = 0;i < MAX_GOALS; i++) +//@@ SGSObjAIGoal(fp, &ai->goals[i]); +//@@ +//@@// continue +//@@ gs_WriteInt(fp, ai->target_handle); +//@@ gs_WriteFloat(fp, ai->next_target_update_time); +//@@ gs_WriteFloat(fp, ai->dist_to_target); +//@@ gs_WriteVector(fp, ai->vec_to_target); +//@@ gs_WriteFloat(fp, ai->last_check_see_target_time); +//@@ gs_WriteVector(fp, ai->last_see_target_pos); +//@@ gs_WriteFloat(fp, ai->last_see_target_time); +//@@ gs_WriteFloat(fp, ai->weapon_speed); +//@@ gs_WriteFloat(fp, ai->next_melee_time); +//@@ gs_WriteFloat(fp, ai->last_render_time); +//@@ gs_WriteFloat(fp, ai->next_flinch_time); +//@@ gs_WriteInt(fp, ai->status_reg); +//@@ gs_WriteInt(fp, ai->flags); +//@@ gs_WriteInt(fp, ai->notify_flags); +//@@ +//@@// notify events (MUST BE WRITTEN OUT AT A LATER DATE) +//@@ +//@@// Normalized movement and facing information +//@@ gs_WriteVector(fp, ai->movement_dir); +//@@ gs_WriteVector(fp, ai->rot_thrust_vector); +//@@ gs_WriteFloat(fp, ai->fov); +//@@ gs_WriteInt(fp, ai->anim_sound_handle); +//@@ gs_WriteFloat(fp, ai->frustration); +//@@ gs_WriteFloat(fp, ai->curiousity); +//@@ gs_WriteFloat(fp, ai->fire_spread); +//@@ gs_WriteFloat(fp, ai->agression); +//@@ gs_WriteFloat(fp, ai->night_vision); +//@@ gs_WriteFloat(fp, ai->fog_vision); +//@@ gs_WriteFloat(fp, ai->lead_accuracy); +//@@ gs_WriteFloat(fp, ai->lead_varience); +//@@ gs_WriteFloat(fp, ai->fight_team); +//@@ gs_WriteFloat(fp, ai->fight_same); +//@@ gs_WriteFloat(fp, ai->hearing); +//@@ gs_WriteFloat(fp, ai->roaming); +//@@ gs_WriteFloat(fp, ai->life_preservation); +//@@ +//@@ gs_WriteShort(fp, ai->awareness); + +//@@void SGSObjAIGoal(CFILE *fp, const goal *g) +//@@{ +//@@ int i; +//@@ +//@@ gs_WriteByte(fp, (g->used ? 1 : 0)); +//@@ if (!g->used) +//@@ return; +//@@ +//@@ gs_WriteInt(fp, g->type); +//@@ gs_WriteByte(fp, g->activation_level); +//@@ gs_WriteShort(fp, g->influence); +//@@ +//@@// write goal info +//@@ switch (g->type) +//@@ { +//@@ case AIG_HIDE_FROM_OBJ: +//@@ gs_WriteInt(fp, g->g_info.handle); +//@@ gs_WriteFloat(fp, g->g_info.time); +//@@ break; +//@@ +//@@ case AIG_WANDER_AROUND: +//@@ gs_WriteInt(fp, g->g_info.roomnum); +//@@ break; +//@@ +//@@ case AIG_DODGE_OBJ: +//@@ case AIG_MOVE_RELATIVE_OBJ: +//@@ gs_WriteInt(fp, g->g_info.handle); +//@@ break; +//@@ +//@@ case AIG_MOVE_RELATIVE_OBJ_VEC: +//@@ gs_WriteInt(fp, g->g_info.handle); +//@@ gs_WriteInt(fp, g->g_info.subtype); +//@@ break; +//@@ +//@@ case AIG_GUARD_AREA: +//@@ gs_WriteVector(fp, g->g_info.pos); +//@@ break; +//@@ +//@@ case AIG_GET_TO_OBJ: +//@@ gs_WriteInt(fp, g->g_info.handle); +//@@ gs_WriteVector(fp, g->g_info.pos); +//@@ break; +//@@ +//@@ case AIG_GET_TO_POS: +//@@ gs_WriteInt(fp, g->g_info.roomnum); +//@@ gs_WriteVector(fp, g->g_info.pos); +//@@ break; +//@@ +//@@ case AIG_FOLLOW_PATH: // This must be fixed -- chrishack +//@@ gs_WriteInt(fp, g->g_info.id); +//@@ break; +//@@ } +//@@ +//@@// write goal enabler +//@@ gs_WriteByte(fp, g->num_enablers); +//@@ for (i = 0; i < g->num_enablers; i++) +//@@ { +//@@ gs_WriteByte(fp, g->enabler[i].enabler_type); +//@@ +//@@ switch (g->enabler[i].enabler_type) +//@@ { +//@@ case AIE_AI_STATUS_FLAG: +//@@ gs_WriteInt(fp, g->enabler[i].flags); +//@@ break; +//@@ } +//@@ +//@@ gs_WriteFloat(fp, g->enabler[i].percent_enable); +//@@ gs_WriteFloat(fp, g->enabler[i].check_interval); +//@@ gs_WriteFloat(fp, g->enabler[i].last_check_time); +//@@ gs_WriteByte(fp, g->enabler[i].bool_next_enabler_op); +//@@ } +//@@ +//@@// continue +//@@ gs_WriteFloat(fp, g->circle_distance); +//@@ gs_WriteInt(fp, g->status_reg); +//@@ gs_WriteFloat(fp, g->start_time); +//@@ gs_WriteFloat(fp, g->next_path_time); +//@@ gs_WriteFloat(fp, g->dist_to_goal); +//@@ gs_WriteVector(fp, g->vec_to_target); +//@@ gs_WriteFloat(fp, g->last_check_see_target_time); +//@@ gs_WriteVector(fp, g->last_see_target_pos); +//@@ gs_WriteFloat(fp, g->last_see_target_time); +//@@ gs_WriteFloat(fp, g->next_target_update_time); +//@@ gs_WriteShort(fp, g->flags); +//@@} +//@@ + + +// EFFECTS INFO STRUCTURE +//@@ gs_WriteInt(fp, ei->type_flags); +//@@ gs_WriteFloat(fp, ei->alpha); +//@@ +//@@// if (op->type == OBJ_POWERUP) { +//@@ gs_WriteFloat(fp, ei->last_object_hit_time); +//@@ gs_WriteInt(fp, ei->last_object_hit); +//@@// } +//@@// if (ei->type_flags & EF_DEFORM) { +//@@ gs_WriteFloat(fp, ei->deform_range); +//@@ gs_WriteFloat(fp, ei->deform_time); +//@@// } +//@@// if (ei->type_flags & EF_VOLUME_CHANGING) { +//@@ gs_WriteFloat(fp, ei->volume_change_time); +//@@ gs_WriteVector(fp, ei->volume_old_pos); +//@@ gs_WriteFloat(fp, ei->volume_old_room); +//@@// } +//@@// if (ei->type_flags & EF_VOLUME_LIT) { +//@@ gs_WriteByte(fp, (sbyte)ei->dynamic_this_frame); +//@@ gs_WriteFloat(fp, ei->dynamic_red); +//@@ gs_WriteFloat(fp, ei->dynamic_green); +//@@ gs_WriteFloat(fp, ei->dynamic_blue); +//@@// } +//@@// if (ei->type_flags & EF_CLOAKED) { +//@@ gs_WriteFloat(fp, ei->cloak_time); +//@@// } +//@@// if (ei->type_flags & EF_COLORED) { +//@@ gs_WriteFloat(fp, ei->color_time); +//@@ gs_WriteFloat(fp, ei->r); +//@@ gs_WriteFloat(fp, ei->g); +//@@ gs_WriteFloat(fp, ei->b); +//@@// } +//@@// if (ei->type_flags & EF_NAPALMED) { +//@@ gs_WriteFloat(fp, ei->damage_time); +//@@ gs_WriteFloat(fp, ei->damage_per_second); +//@@ gs_WriteFloat(fp, ei->last_damage_time); +//@@ gs_WriteInt(fp, ei->damage_handle); +//@@// } +//@@// if (ei->type_flags & EF_FREEZE) { +//@@ gs_WriteFloat(fp, ei->freeze_scalar); +//@@// } +//@@// if (ei->type_flags & EF_LINE_ATTACH) { +//@@ gs_WriteInt(fp, ei->attach_line_handle); +//@@// } + +// Weapon Battery Info +//@@ gs_WriteFloat(fp, dwb->last_fire_time); +//@@ gs_WriteByte(fp, dwb->cur_firing_mask); +//@@ +//@@ for (j = 0; j < MAX_WB_TURRETS; j++) +//@@ { +//@@ gs_WriteFloat(fp, dwb->norm_turret_angle[j]); +//@@ gs_WriteFloat(fp, dwb->turret_next_think_time[j]); +//@@ gs_WriteByte(fp, dwb->turret_direction[j]); +//@@ } +//@@ +//@@ gs_WriteByte(fp, dwb->wb_anim_mask); +//@@ gs_WriteFloat(fp, dwb->wb_anim_frame); +//@@ gs_WriteVector(fp, dwb->cur_target); +//@@ gs_WriteByte(fp, dwb->upgrade_level); +//@@ gs_WriteInt(fp, dwb->flags); diff --git a/Descent3/gamesave.h b/Descent3/gamesave.h new file mode 100644 index 000000000..a9f87ef63 --- /dev/null +++ b/Descent3/gamesave.h @@ -0,0 +1,282 @@ +/* + * $Logfile: /DescentIII/main/gamesave.h $ + * $Revision: 16 $ + * $Date: 7/21/99 7:17p $ + * $Author: Chris $ + * + * + * + * $Log: /DescentIII/main/gamesave.h $ + * + * 16 7/21/99 7:17p Chris + * Fixed an attach related crash in the load/save game code + * + * 15 4/20/99 11:45a Samir + * added autosave + * + * 14 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 13 3/22/99 5:12p Samir + * added snapshot to savegame. + * + * 12 3/08/99 11:04a Jason + * added versioning for texture changes + * + * 11 3/04/99 6:30p Jeff + * saves out current waypoint and matcens + * + * 10 1/29/99 12:47p Matt + * Rewrote the doorway system + * + * 9 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 8 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 7 10/08/98 12:00p Kevin + * Demo system work + * + * 6 10/06/98 5:45p Kevin + * Added new configuration for demo + * + * 5 8/20/98 10:52a Samir + * fixed some ui probs + * + * 4 8/19/98 5:35p Samir + * saved triggers, spew, viseffects, weapons, room data xcept texture per + * face changes, one real strange bug where robots in the same room as you + * don't appear but fire at you. + * + * 3 8/18/98 3:05p Samir + * save game with no weapons or fireballs. + * + * 2 8/18/98 1:12a Samir + * rudimentary savegame. pretty damn buggy when loading. + * + * $NoKeywords: $ + */ + + +#ifndef GAMESAVE_H +#define GAMESAVE_H + +#include "pstypes.h" +#include "CFILE.H" +#include "object.h" +#include "objinfo.h" + +#include "gametexture.h" +#include "bitmap.h" +#include "ddio.h" +#include "door.h" +#include "doorway.h" +#include "ship.h" +#include "weapon.h" +#include "polymodel.h" + +#define GAMESAVE_SLOTS 8 // maximum number of savegames +#define GAMESAVE_DESCLEN 31 // gamesave description maximum length. + +typedef struct gs_tables { + short model_handles[MAX_POLY_MODELS]; + short obji_indices[MAX_OBJECT_IDS]; + short bm_handles[MAX_BITMAPS]; + short tex_handles[MAX_TEXTURES]; + short door_handles[MAX_DOORS]; + short ship_handles[MAX_SHIPS]; + short wpn_handles[MAX_WEAPONS]; +} gs_tables; + +// savegame version info. +// this should be handled like level file version, as to prevent invalidating old savegames +// please record a table of changes here. + +// 0 samir-initial version +// 1 Added saving/loading of changed textures +// 2 Added correct saving and restoring of attach points + +#define GAMESAVE_VERSION 2 +#define GAMESAVE_OLDVER 0 // any version before this value is obsolete. + +void SaveGameDialog(); +bool LoadGameDialog(); // returns true if ok, false if canceled. +void QuickSaveGame(); + +bool LoadCurrentSaveGame(); // loads savegame as specified from LoadGameDialog (false fails) + +extern int Quicksave_game_slot; // externed so gamesequencing can reset this value starting new game. + + +// internal. + +// loads a game from a given slot. returns a code below. +#define LGS_OK 0 +#define LGS_FILENOTFOUND 1 +#define LGS_OUTDATEDVER 2 +#define LGS_STARTLVLFAILED 3 // level failed to load or startup +#define LGS_MISSIONFAILED 4 // mission failed to load. +#define LGS_OBJECTSCORRUPT 5 // object list is corrupt (or out of date with level) +#define LGS_CORRUPTLEVEL 6 // either level is out of date, or list is corrupted. + +int LoadGameState(const char *pathname); + +// Easy IO routines for repetitive tasks. + +#define gs_WriteVector(_f,_v) do {cf_WriteFloat((_f),(_v).x); cf_WriteFloat((_f),(_v).y); cf_WriteFloat((_f),(_v).z); } while (0) +#define gs_WriteMatrix(_f,_m) do {gs_WriteVector((_f),(_m).rvec); gs_WriteVector((_f),(_m).uvec); gs_WriteVector((_f),(_m).fvec); } while (0) +#define gs_WriteAngle(_f,_a) cf_WriteShort(_f,(short)(_a)) +#define gs_WriteByte(_f,_b) cf_WriteByte(_f,_b) +#define gs_WriteShort(_f,_s) cf_WriteShort(_f,_s) +#define gs_WriteInt(_f,_i) cf_WriteInt(_f,_i) +#define gs_WriteFloat(_f,_fl) cf_WriteFloat(_f,_fl) +#define gs_WriteBool(_f,_b) cf_WriteByte(_f, _b) + +#define START_VERIFY_SAVEFILE(f) int cur_file_pos = cftell(f) +#define END_VERIFY_SAVEFILE(f,s) mprintf((0, "%s =%d bytes\n", s, cftell(f)-cur_file_pos)) + +#define gs_ReadVector(_f,_v) do {(_v).x=cf_ReadFloat(_f); (_v).y=cf_ReadFloat(_f); (_v).z=cf_ReadFloat(_f); } while (0) +#define gs_ReadMatrix(_f,_m) do {gs_ReadVector((_f),(_m).rvec); gs_ReadVector((_f),(_m).uvec); gs_ReadVector((_f),(_m).fvec); } while (0) +#define gs_ReadAngle(_f,_a) ((_a) = (angle)cf_ReadShort(_f)) +#define gs_ReadBool(_f,_b) ((_b) = (bool)cf_ReadByte(_f)) +#define gs_ReadByte(_f,_b) ((_b) = cf_ReadByte(_f)) +#define gs_ReadShort(_f,_s) ((_s) = cf_ReadShort(_f)) +#define gs_ReadInt(_f,_i) ((_i) = cf_ReadInt(_f)) +#define gs_ReadFloat(_f, _fl) ((_fl) = cf_ReadFloat(_f)) + +// we need this directory to load the savegame from +static char LGS_Path[PSPATHNAME_LEN]; + +// writes out translation tables. +void SGSXlateTables(CFILE *fp); + +// initializes rooms +void SGSRooms(CFILE *fp); + +// saves out events +void SGSEvents(CFILE *fp); + +// saves out triggers +void SGSTriggers(CFILE *fp); + +// save viseffects +void SGSVisEffects(CFILE *fp); + +// players +void SGSPlayers(CFILE *fp); + +// saves out objects +void SGSObjects(CFILE *fp); + +// saves ai +void SGSObjAI(CFILE *fp, const ai_frame *ai); + +// saves fx +void SGSObjEffects(CFILE *fp, const object *op); + +// saves script +//@@void SGSScript(CFILE *fp, const script_info *script); + +// saves wb +void SGSObjWB(CFILE *fp, object *op, int num_wbs); + +// saves special object info +void SGSObjSpecial(CFILE *fp, const object *op); + +// load spew +void SGSSpew(CFILE *fp); + +//@@// saves ai goals +//@@void SGSObjAIGoal(CFILE *fp, const goal *g); + +// load matcens +void SGSMatcens(CFILE *fp); + + +// give a description and slot number (0 to GAMESAVE_SLOTS-1) +bool SaveGameState(const char *pathname, const char *description); + + +// retreive gamesave file header info. description must be a buffer of length GAMESAVE_DESCLEN+1 +// returns true if it's a valid savegame file. false if corrupted somehow +// pointer to bm_handle will return a bitmap handle to the snapshot for game. (*bm_handle) can be invalid. +bool GetGameStateInfo(const char *pathname, char *description, int *bm_handle=NULL); + + + +/////////////////////////////////////////////////////////////////////////////// +// reads in translation tables +int LGSXlateTables(CFILE *fp); + +// loads in level's mission and level. +int LGSMission(const char *msnname, int level); + +// initializes rooms +int LGSRooms(CFILE *fp); + +// loads in and sets these events +int LGSEvents(CFILE *fp); + +// loads in and sets these triggers +int LGSTriggers(CFILE *fp); + +// players +int LGSPlayers(CFILE *fp); + +// loads in and sets these objects +int LGSObjects(CFILE *fp, int version); + +// load viseffects +int LGSVisEffects(CFILE *fp); + +// load spew +int LGSSpew(CFILE *fp); + +// loads ai +int LGSObjAI(CFILE *fp, ai_frame **pai); + +// saves ai goals +int LGSObjAIGoal(CFILE *fp, goal *g); + +// loads fx +int LGSObjEffects(CFILE *fp, object *op); + +// loads weapon batter info +int LGSObjWB(CFILE *fp, object *op); + +// loads script +//@@vector *LGSScript(CFILE *fp, script_info *script, ubyte *is_scripted, int *memsize); + +// loads special object info +int LGSObjSpecial(CFILE *fp, object *op); + +// load matcens +int LGSMatcens(CFILE *fp); + + +////////////////////////////////////////////////////////////////////////////// +// Game Save/Load User interface + +#define SAVEGAMEDLG_WIDTH (512) +#define SAVEGAMEDLG_HEIGHT (384) +#define SAVEGAMEDLG_X ((Max_window_w - SAVEGAMEDLG_WIDTH)/2) +#define SAVEGAMEDLG_Y ((Max_window_h - SAVEGAMEDLG_HEIGHT)/2) +#define SAVEGAMELB_WIDTH (32*14) +#define SAVEGAMELB_HEIGHT (32*6) +#define SAVEGAMELB_X ((SAVEGAMEDLG_WIDTH - SAVEGAMELB_WIDTH)/2) +#define SAVEGAMELB_Y ((SAVEGAMEDLG_HEIGHT - SAVEGAMELB_HEIGHT + 32)/2) +#define SAVEGAMEBTN_W 96 +#define SAVEGAMEBTN_X ((SAVEGAMEDLG_WIDTH/4) - (SAVEGAMEBTN_W/2)) +#define SAVEGAMEBTN_X2 ((3*SAVEGAMEDLG_WIDTH/4) - (SAVEGAMEBTN_W/2)) +#define SAVEGAMEBTN_Y (SAVEGAMEDLG_HEIGHT - 64) + +#define UID_SAVELB 100 +#define UID_LOADLB 100 + +#endif diff --git a/Descent3/gamesequence.cpp b/Descent3/gamesequence.cpp new file mode 100644 index 000000000..67c631aa5 --- /dev/null +++ b/Descent3/gamesequence.cpp @@ -0,0 +1,2863 @@ +/* + * $Logfile: /DescentIII/main/gamesequence.cpp $ + * $Revision: 313 $ + * $Date: 8/30/01 12:20p $ + * $Author: Matt $ + * + * Game Sequencer + * + * $Log: /DescentIII/main/gamesequence.cpp $ + * + * 313 8/30/01 12:20p Matt + * Removed Int3 + * + * 312 7/09/01 4:22p Matt + * Fixed code that was supposed to find an allowed ship in a netgame but + * did so using the AllowedShips[] array instead of the Ships[] array, so + * it would never find the add-on ships. + * + * 311 4/20/00 12:29p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * Free the BSP tree when freeing a level + * On Mac, free all sounds when flushing the data cache + * Mac-only controller changes + * + * 310 4/06/00 9:25a Matt + * Fixed a screen clear problem on at ATI Rage Fury Maxx in dual-chip mode + * by forcing the screen to clear four times (instead of three). + * + * 309 3/20/00 12:09p Matt + * Merge of Duane's post-1.3 changes. + * Mac controller stuff + * Fixed mem leaks with game paths + * Message for PageDown pause + * Removed dynamic lightmap info system + * + * 308 1/26/00 9:20p Jeff + * added support for IntelliVIBE DLL + * + * 307 10/29/99 5:01p Jeff + * fixed bug where ending a cinematic on FreeThisLevel() hosed + * resume/suspend controls + * + * 306 10/22/99 10:51p Matt + * Mac merge + * + * 305 10/22/99 3:40p Kevin + * Mac merge fixes + * + * 304 7/20/99 1:18p Samir + * save state of rearviews between game through the pilot file. + * + * 303 6/14/99 4:04p Matt + * Fixed problem where goal couldn't be found by the Dallas init function + * because it was searching for the English name and the goals had been + * translated. Now we load the English text, init the scripts, and then + * load the correct language. + * + * A simpler way of doing this would have been to *not* load the localized + * version when the level is loaded, and just loaded it after the script + * had been initialized. + * + * 302 6/08/99 1:00p Jason + * changes for bumpmapping + * + * 301 5/21/99 7:07p Jason + * fixed some weird multiplayer rejoin anomalies + * + * 300 5/20/99 3:40p Jason + * made mission looping work correctly in multiplayer + * + * 299 5/20/99 12:17a Jason + * automatically delete all ambient objects if in multiplayer + * + * 298 5/19/99 5:39p Jason + * made level failing work in coop + * + * 297 5/19/99 11:46a Kevin + * fixed problem with level not being loaded and client not able to join + * further games. + * + * 296 5/18/99 7:02p Jeff + * created new callback for UI so that game states get set properly (only + * really affects music, but fixes a problem for multiplayer games with + * music.) + * + * 295 5/17/99 10:15p Matt + * Added system for having ambient objects that get deleted at low detail + * levels. + * + * 294 5/17/99 2:39p Kevin + * fixed bug with current_mission.filename and not using strdup to + * allocate enough room for the mn3 + * + * 293 5/12/99 1:57p Jason + * fixed yet more buggy/ugly code + * + * 292 5/12/99 1:18p Samir + * play music during game too when in menus. + * + * 291 5/11/99 10:59a Kevin + * ship allow/disallow in multi works now + * + * 290 5/10/99 9:25p Jeff + * first phase of Rock 'n' Ride support added + * + * 289 5/10/99 10:29a Samir + * moved StopAllSounds in EndLevel so that both single and multiplayer + * stop sounds at the end of a level. (interplay bug). + * + * 288 5/10/99 3:37a Matt + * When starting a level, delete all the viewer objects in the level. + * + * 287 5/09/99 2:34p Jason + * fixed hanging of clients when dedicated server quits + * + * 286 5/08/99 4:30a Jeff + * fixed sequencing bug where clients never got a level end event for the + * multiplayer games + * + * 285 5/07/99 5:39p Samir + * better error checking for CheckAndForceDataAlloc for PageInSound. + * + * 284 5/07/99 5:06p Jason + * some dedicated server memory optimizations + * + * 283 5/07/99 2:50p Jason + * fixed a bunch of endlevel multiplayer issues + * + * 282 5/06/99 4:12p Jason + * fixed automap getting cleared after loading a savegame + * + * 281 5/05/99 6:48p Samir + * made pause key take down the pause window as well. + * + * 280 5/05/99 12:23p Jason + * fixed some problems with ending levels + * + * 279 5/05/99 12:02p Kevin + * fixed bug with branching missions and scripts + * + * 278 5/05/99 1:32a Jeff + * save/restore player energy also + * + * 277 5/05/99 1:28a Jeff + * save/restore player's shields in single player game if they are over + * 100 + * + * 276 5/04/99 10:47p Samir + * added secret level screen. + * + * 275 5/04/99 4:34p Jason + * changes for bumpmapping + * + * 274 5/03/99 3:35p Kevin + * bug fixes + * + * 273 5/01/99 5:52p Samir + * removed RenderHudMessages, and redid RenderHUDMessages so it did what + * RenderHudMessages did, resets on screen hud messages. The HUD message + * console is resetted in ResetGameMessages. + * + * 272 4/30/99 12:02p Matt + * Keep the rear view up between levels, but close the other camera views. + * + * 271 4/29/99 11:46p Jason + * added ability to set the next level in a multiplayer game + * + * 270 4/29/99 4:59p Jason + * fixed some preuploaded texture problems + * + * 269 4/28/99 3:33a Jeff + * reset IGC system on level load, just for sanity + * + * 268 4/27/99 6:09p Samir + * SetCurrentLevel called for secret levels too. + * + * 267 4/25/99 6:14p Kevin + * added "-timetest file.dem" to behave like gamegauge does + * + * 266 4/25/99 3:35a Jeff + * added a please wait popup dialog when entering telcom + * + * 265 4/23/99 10:34p Kevin + * fixed bad things with multiple CD code + * + * 264 4/23/99 8:26p Samir + * fixed stupid clearscreen bug in endlevel + * + * 263 4/22/99 9:53a Kevin + * Fixed some dedicated server crashes + * + * 262 4/20/99 6:55p Matt + * Added code to keep a bitmask of all keys held by all players, and to + * allow a robot to open a door if any player has the key(s) for that + * door. + * + * 261 4/20/99 3:32p Kevin + * new prepare for descent behaivor + * + * 260 4/20/99 2:59p Jeff + * fixed SimpleStartLevel + * + * 259 4/20/99 11:45a Samir + * added autosave + * + * 258 4/17/99 3:44p Kevin + * Demo2 changes & fixes + * + * 257 4/16/99 4:30p Kevin + * New Training mission exit code + * + * 256 4/16/99 12:33a Matt + * Disable Soar on non-Windows systems. + * + * 255 4/15/99 3:26p Samir + * stop all sounds when quitting game, so that resume game doesn't + * playback any sounds. + * + * 254 4/15/99 2:47p Kevin + * doh! I forgot args.h + * + * 253 4/15/99 2:43p Kevin + * Added some things that I put into the demo for HEAT.NET + * + * 252 4/15/99 2:37p Kevin + * Fixed multi-cd problem going from cd 2 - 1 + * + * 251 4/15/99 1:39a Jeff + * changes for linux compile + * + * 250 4/14/99 6:55p Matt + * Fixed secret level sequencing bug + * + * 249 4/14/99 3:07p Kevin + * Fixed some multiple CD bugs + * + * 248 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 247 4/12/99 7:15p Samir + * prioritization pass 1 + * + * 246 4/12/99 5:57p Kevin + * Fixed a sequencing bug with branching levels + * + * 245 4/08/99 4:21p Samir + * clear screen before ending level. + * + * 244 4/08/99 3:12p Matt + * Finished cleaning up level sequencing code. Got rid of all the "level + * minus one" stuff. + * + * 243 4/07/99 3:40p Kevin + * Fixes for Beta 1 + * + * 242 4/07/99 12:30p Matt + * Added code for failed missions. + * + * 241 4/06/99 10:44p Samir + * Always stop music system when loading new level. + * + * 240 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 239 4/04/99 8:15p Jeff + * added debug graph stuff + * + * 238 4/03/99 5:05p Samir + * added ENDMISSION keyword to end a mission after a certain level. + * + * 237 3/25/99 4:57p Jason + * fixed resetplayerobject bug + * + * 236 3/25/99 11:57a Jason + * changed some sequencing bugs + * + * 235 3/24/99 11:55a Jason + * added S3 texture compression + * + * 234 3/24/99 10:54a Kevin + * Fixed some problems related to splitting up the main d3.mn3 file across + * 2 CDs + * + * 233 3/23/99 11:52a Jason + * added preliminary S3 texture compression support + * + * 232 3/19/99 4:08p Kevin + * Multiple CD installation support + * + * 231 3/11/99 12:06p Jason + * fixed triple buffering problem + * + * 230 3/10/99 7:12p Jason + * added smooth specular shading for curved surfaces + * + * 229 3/08/99 4:31p Kevin + * Fixed interlevel sequencing problem with branching multiplayer + * missions. + * + * 228 3/08/99 3:24p Jeff + * fixed end level sequencing where if the level ended while the player + * was dead. + * + * 227 3/05/99 7:42p Kevin + * Doh! we weren't resetting timers!! + * + * 226 3/03/99 2:35a Jeff + * clear hud messages at the end of level + * + * 225 3/03/99 12:56a Kevin + * removed assert that was causing problems + * + * 224 2/28/99 3:24a Samir + * call OptionsMenu. + * + * 223 2/25/99 8:54p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 222 2/24/99 3:15p Kevin + * OEM menu changes, and bug fixes for the save/load system + * + * 221 2/24/99 9:57a Kevin + * Added error messages if you try to save a game while in multi + * + * 220 2/21/99 12:18p Chris + * Improving the level goal system... Not done. + * + * 219 2/21/99 4:20p Matt + * Added SoundSource objects (and reformatted parts of the object header + * files). + * + * 218 2/21/99 12:28p Matt + * Added terrain sound system + * + * 217 2/20/99 9:22p Jeff + * finished telcom level goals screen. Made it so if you go into the + * telcom from the game it goes to main menu instead of briefings. + * + * 216 2/20/99 4:11p Jeff + * fixed cockpit selection bug again (which cockpit to use depending on + * multi or single) + * + * 215 2/20/99 2:31p Kevin + * Made multiplayer DLLs return to the game list after a game. + * + * 214 2/20/99 2:17p Matt + * Added texture sounds + * + * 213 2/19/99 12:01p Jason + * took out sky band + * + * 211 2/16/99 12:38p Kevin + * Improved paging data progress indicator + * + * 210 2/16/99 11:13a Jason + * fixed bug in cockpit initialization code + * + * 209 2/15/99 7:49p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 208 2/14/99 8:06p Jeff + * start of new pilot read/write...not working yet, still needs work + * + * 207 2/12/99 3:54a Jeff + * added function to restart a level, and a cheat key to do so (del-alt-e) + * + * 205 2/10/99 4:56p Kevin + * Better progress indicator & prepare for Descent message + * + * 204 2/10/99 3:47p Jason + * table filter changes + * + * 203 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 202 2/10/99 10:58a Kevin + * doh! + * + * 201 2/10/99 10:56a Kevin + * Took out loadlevelcb calls for now + * + * 200 2/09/99 7:01p Kevin + * First work for new and improved progress screen while loading a level. + * Note that this is a hack at this point, while I get the details worked + * out, then I'll make it cleaner. + * + * 199 2/05/99 7:23p Kevin + * OEM Changes + * + * 198 2/04/99 12:35p Jeff + * free in-game cinematics when level ends + * + * 197 2/01/99 6:14p Kevin + * Moved InitLevelScripts to later in the sequence + * + * 196 2/01/99 3:57p Samir + * fixed bug in secret levels. + * + * 195 2/01/99 12:44p Jason + * added another bumpmapping pass + * + * 194 2/01/99 10:35a Matt + * Fixed the initial player position in the game when playing from the + * editor. + * + * 193 1/31/99 7:25p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 192 1/31/99 3:44p Matt + * Streamlined game sequencing + * + * 191 1/29/99 6:29p Jason + * first pass at adding bumpmaps + * + * 190 1/29/99 2:08p Jeff + * localization + * + * 189 1/29/99 12:48p Matt + * Rewrote the doorway system + * + * 188 1/28/99 11:32a Jason + * added marker cameras + * + * 187 1/22/99 3:59p Jason + * added 256x256 textures to help with terrain skies + * + * 186 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 185 1/20/99 4:20p Samir + * finished secret level implementation. + * + * 184 1/20/99 10:50a Jason + * added new terrain + * + * 183 1/19/99 11:25a Jason + * added room (fog and wind) changing functions + * + * 182 1/15/99 7:16p Kevin + * Added GameGauge Configuration & code + * + * 181 1/14/99 3:08p Jason + * more cache changes + * + * 180 1/14/99 3:05p Jason + * fixed stupid texture data bug + * + * 179 1/14/99 2:26p Jason + * made data tracking more reliable + * + * 178 1/13/99 2:28a Chris + * Massive AI, OSIRIS update + * + * 177 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 176 1/04/99 6:45p Jason + * took out paging return + * + * 175 12/29/98 4:30p Jason + * added add-on data functionality + * + * 174 12/28/98 5:56p Jason + * don't page in data if editor + * + * 173 12/23/98 1:25p Chris + * Got the FIRST AI working!!!!!!!! I need to rearrange the sequencing so + * that the AIInitAll wouldn't reset objects AFTER there EVT_AI_INIT was + * called. + * + * 172 12/21/98 3:33p Samir + * level warp fully implemented. + * + * 171 12/21/98 9:44a Chris + * Ambient life stuff is all tied in to game sequencing + * + * 170 12/18/98 5:40p Chris + * Ambient life is now all sequenced up + * + * 169 12/15/98 4:28p Jeff + * added mission data information to the pilot files to save what the + * highest level they achieved on a mission is. Added level select dialog + * (not hooked up) and level warp cheat. + * + * 168 12/14/98 11:57a Jason + * changes for visibile automap + * + * 167 12/03/98 12:50p Samir + * music file loaded at beginning of level. + * + * 166 11/19/98 5:40p Kevin + * Demo system + * + * 165 11/16/98 12:19p Samir + * pause and resume sounds until first frame of level. + * + * 164 11/13/98 2:28p Samir + * new music system. + * + * 163 11/09/98 3:08p Kevin + * Added demo code + * + * 162 11/05/98 7:36p Samir + * rearranged some sequencing so that common editor/game code gets called, + * and moved redundant game screen init code to StartNewLevel. + * + * 161 11/05/98 5:54p Kevin + * Demo system work + * + * 160 10/30/98 3:44p Samir + * reset multi_ui_bail. + * + * 159 10/23/98 12:51p Samir + * added call to SetHudMode so that level sequencing maintains the hud. + * + * 158 10/22/98 8:31p Chris + * Sounds use GlobalAlloc and GlobalFree + * + * 157 10/22/98 5:46p Jason + * took care of remaining page-in issues + * + * 156 10/22/98 4:11p Samir + * took out ifdef DEMO for inventory. + * + * 155 10/22/98 3:30p Samir + * fixed for editor the hud. + * + * 154 10/22/98 2:40p Samir + * redid HUD sequencing so multiplayer hud stuff works. + * + * 153 10/22/98 1:30a Jeff + * (Samir) ifdefed out guidebot interface for demo + * + * 152 10/21/98 9:28p Jason + * Made no lightmaps work globally + * + * 151 10/21/98 7:04p Samir + * moved init hud and cockpit to init level + * + * 150 10/21/98 6:53p Jason + * changes for shitty direct3d cards + * + * 149 10/21/98 4:52p Jeff + * disallow Automap for Demo + * + * 148 10/21/98 1:37p Jason + * fixed naming bug + * + * 147 10/21/98 12:05p Jason + * changes for data paging + * + * 146 10/21/98 10:35a Samir + * cleaned up GameInterface code. + * + * 145 10/20/98 8:48p Sean + * Don't call ResetPlayerObject() when playing from the editor, since that + * caused the player to move to the start position. (MattT on Sean's + * machine.) + * + * 144 10/20/98 6:50p Matt + * Fixed small bug paging in object animation sounds. + * + * 143 10/20/98 2:53a Kevin + * gunboy crap + * + * 142 10/20/98 12:11a Chris + * + * 141 10/19/98 11:22p Samir + * clear screen after UI. + * + * 140 10/19/98 11:06p Kevin + * + * 139 10/19/98 8:51p Matt + * Added missing period in pause message. + * + * 138 10/19/98 7:45p Matt + * + * 137 10/19/98 5:40p Samir + * added loading level callback. + * + * 136 10/19/98 12:12p Samir + * call ResetPlayerObject from StartNewLevel. + * + * 135 10/19/98 11:57a Chris + * Update the sound system to use the import volume + * + * 134 10/18/98 11:11p Matt + * Added call to ClearTransientObjects() and cleaned up some sequencing + * stuff. + * + * 133 10/18/98 7:27p Samir + * flush controller at start of every level. + * + * 132 10/16/98 4:03p Matt + * Fixed run-on in pause message. + * + * 131 10/15/98 8:32p Matt + * Fixed bug with starting abient sounds that caused rooms & other + * non-generic objects to have ambient sounds. + * + * 130 10/14/98 6:40p Samir + * write pilot files out at quitting game. + * + * 129 10/12/98 3:02p Jeff + * moved DEFAULT_SHIP #define into ship.h + * + * 128 10/12/98 11:15a Samir + * changed some sequencing stuff in GAMESTATE_LVLSTART. + * + * 127 10/09/98 4:13p Matt + * Use different default ship for the demo + * + * 126 10/08/98 8:01p Nate + * got rid of warning. + * + * 125 10/08/98 7:29p Samir + * revamped sequencing. + * + * 124 10/08/98 2:33p Kevin + * + * 123 10/08/98 2:29p Kevin + * fixed bad if statement + * + * 122 10/08/98 12:00p Kevin + * Demo system work + * + * 121 10/07/98 12:22p Jason + * + * 120 10/06/98 5:45p Kevin + * Added new configuration for demo + * + * 119 10/06/98 2:38p Jeff + * fixed paused message + * + * 118 10/05/98 12:09p Kevin + * Converted projects to VC6 and demo file stuff added + * + * 117 10/02/98 3:40p Chris + * Improved the level goal code + * + * 116 9/28/98 2:15p Kevin + * Changed calls to network idle function + * + * 115 9/28/98 11:02a Kevin + * added Networking defer, and fixed some UI issues + * + * 114 9/28/98 10:55a Jason + * fixed some bugs that VC6 caught + * + * 113 9/25/98 4:55p Jason + * flush all multiplayer buffers at the start of a new level + * + * 112 9/17/98 3:31p Samir + * if level is ending, freethislevel gets called twice (at EndLevel, and + * QuitToMenu), so make sure we don't freelevel in latter case. + * + * 111 9/16/98 5:10p Jason + * added first pass at thrid-person camera system + * + * 110 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 109 9/10/98 3:07p Chris + * Improved matcen sequencing code and test effect1 + * + * 108 9/10/98 12:55p Chris + * Added pre-level initialization for matcens :) + * + * 107 9/09/98 1:08p Jeff + * commented out call to FlushDataCache in FreeThisLevel + * + * 106 9/08/98 3:25p Matt + * Fixed, hopefully, problems with getting the the player position & + * orientation back to the editor when playing from the editor. + * + * 105 9/08/98 12:05p Jason + * moved doorway.h out of room.h + * + * 104 9/08/98 10:28a Samir + * added function to deinitialize a level. + * + * 103 9/03/98 12:11p Chris + * Adding matcen support + * + * 102 8/29/98 6:53p Jeff + * added single-player ship selection + * + * 101 8/28/98 1:34p Matt + * Added code to reset the waypoint when starting a new level, and while I + * was at it cleaned up the new level start sequencing. + * + * 100 8/27/98 2:51p Jeff + * made it so SHIFT-ESC exits the TelCom back to Main Menu + * + * 99 8/26/98 1:48a Chris + * Fixed odd non-stopping sounds when ESC out of the game - etc. + * + * 98 8/24/98 2:45p Jason + * tweaks to the data cache stuff + * + * 97 8/24/98 10:42a Jason + * more memory paging issues dealt with + * + * 96 8/21/98 8:28p Samir + * made FlushDataCache bail if in the editor + * + * 95 8/21/98 5:14p Jason + * made better memory use of primitives + * + * 94 8/21/98 3:02p Jason + * better paging in of weapons stuff + * + * 93 8/20/98 10:52a Samir + * added load game(restore game) functionality from main menu. + * + * 92 8/19/98 6:39p Samir + * took out null of Current_level. + * + * 91 8/19/98 5:36p Samir + * pause game when in load game state. + * + * 90 8/18/98 3:02p Samir + * new sequencing for LoadGameState and PLEASE define Current_level some + * other way (or at least keep the definition in StartNewLebvel) + * + * 89 8/18/98 1:50p Matt + * Added missing ambient sound init + * + * 88 8/18/98 1:05a Samir + * + * 87 8/17/98 6:40p Matt + * Added ambient sound system + * + * 86 8/16/98 5:56p Jeff + * added game interface for multiplayer dll ui stuff + * + * 85 8/10/98 6:19p Jason + * Reset all doorways at the beginning of a level + * + * 84 8/10/98 2:21p Jeff + * changes made due to adding flag for inventory reset + * + * 83 8/04/98 2:32p Chris + * Improved attach code added more fixes to the AABB partial computation + * patch + * + * 82 7/31/98 5:19p Samir + * mission filenames are dynamically allocated now to allow for pathnames + * (since we'd have too many 256 char arrays per level. + * + * 81 7/28/98 5:41p Samir + * music system hooks. + * + * 80 7/20/98 12:04p Jason + * added per level satellite lighting + * + * 79 6/29/98 6:42p Samir + * Properly handle controller pausing and resumption. + * + * 78 6/25/98 5:22p Kevin + * Req/Send gametime to clients + * + * 77 6/25/98 12:43p Jeff + * Added exit game confirmation + * + * 76 6/24/98 5:18p Matt + * Added code for user pausing + * + * 75 6/23/98 11:10p Matt + * Simplified some code + * + * 74 6/19/98 3:03p Chris + * Made CheckAndForceSoundDataAlloc a SoundSystem function - useful for + * multiple mixers. Added IsSoundPlaying to the high level sound lib. + * + * 73 6/16/98 10:38a Jeff + * localization, strings pulled out to stringtable.h and d3.str + * + * 72 6/15/98 10:54a Chris + * All sounds stop at the end of a level + * + * 71 5/27/98 12:36a Samir + * print out message saying demo will restart in x minutes for E3. + * + * 70 5/26/98 11:03p Chris + * + * 69 5/26/98 10:54p Chris + * Ooops again + * + * 68 5/26/98 10:49p Samir + * reset level timer limit for E3. + * + * 67 5/26/98 10:42p Chris + * Fixed door sound paging + * + * 66 5/26/98 10:21p Chris + * Added door sound paging + * + * 65 5/26/98 10:16p Jason + * Precache terrain lod bitmaps + * + * 64 5/26/98 5:04p Samir + * took out calls to SetHudMode (called in SetScreenMode) + * + * 63 5/26/98 2:49p Jason + * fixed FOV bug + * + * 62 5/25/98 8:29p Samir + * moved call to postlevel results to EndLevel function. + * + * 61 5/25/98 3:46p Jason + * added better light glows + * + * 60 5/24/98 2:55a Jeff + * added paramter to MenuOptions() + * + * 59 5/22/98 12:34p Matt + * Added scorch mark/bullet hole system. + * + * 58 5/21/98 2:32p Samir + * added full support for intra-mission level branching. + * + * 57 5/18/98 4:36p Jeff + * TelCom uses Game_interface_mode variable now + * + * 56 5/17/98 4:08p Chris + * Fixed bugs with initial sound loading. :) + * + * 55 5/15/98 5:18p Chris + * Working on sound paging + * + * 54 5/15/98 5:07p Chris + * Fixed some bugs in sound paging + * + * 53 5/14/98 3:18p Jason + * finished multiplayer sequencing issues + * + * 52 5/14/98 11:49a Chris + * Bettered the sound paging system + * + * 51 5/12/98 4:18p Jason + * added better level sequencing for multiplayer + * + * 50 5/12/98 12:33p Jason + * got level sequencing working in multiplayer + * + * 49 5/11/98 6:19p Samir + * hide cursor when entering game. + * + * 48 5/11/98 4:14p Samir + * set ui callback to NULL when just entering game.... + * + * 47 5/11/98 11:31a Jason + * added some events for level sequencing + * + * 46 5/05/98 5:16p Samir + * moved loading level message to this file. + * + * 45 5/01/98 4:24p Samir + * open cockpit fully if current hud mode is cockpit at start of level. + * + * 44 4/24/98 5:31p Samir + * reset reticle at game start + * + * 43 4/24/98 1:53a Samir + * took care of a lot of scripting memory leaks. + * + * 42 4/21/98 4:16a Samir + * clear out obj_text array in level node for simple start level. + * + * 41 4/20/98 10:02a Chris + * Working on fixing AI/OSIRIS integration bugs + * + * 40 4/16/98 2:56p Chris + * Updated buddy support and intergrated scripting and AI + * + * 39 4/16/98 6:52a Samir + * took out cockpit init.free code + * + * 38 4/14/98 9:18p Samir + * redid post level results. + * + * 37 4/10/98 2:58p Chris + * Redid where the buddy gets allocated, not all the way done yet. + * + * 36 4/09/98 7:02p Chris + * + * 35 4/09/98 6:57p Craig + * FIxed a sequencing bug + * + * 34 4/07/98 4:25p Chris + * Added support for buddy bot + * + * 33 4/06/98 5:13p Chris + * Sounds page in at the beginning of a level + * + * 32 4/06/98 12:14p Jason + * changes to multiplayer + * + * 31 4/03/98 3:36p Chris + * Added the start of the buddy menu + * + * 30 4/02/98 11:11a Samir + * Error checking for level load/misison init fail. + * + * 29 3/31/98 3:49p Jason + * added memory lib + * + * 28 3/27/98 5:06p Samir + * took out unnecessary mprintfs. + * + * 27 3/23/98 7:06p Chris + * + * 26 3/23/98 6:16p Chris + * added some soar_helpers and some debug mprintf's + * + * 25 3/17/98 2:40p Samir + * reorg of hud/gauge system. + * + * 24 3/13/98 12:37p Jeff + * start level at 0 for simplestartlevel. + * + * 23 3/13/98 12:08p Samir + * Added SimpleStartLevel. + * + * 22 3/05/98 2:51p Jeff + * Added Help interface + * + * 21 3/05/98 2:50p Chris + * A bunch of SOAR updates + * + * 20 3/03/98 12:10p Samir + * Added simple pausing. + * + * 19 3/02/98 5:52p Samir + * Added game interface system. + * + * 18 2/23/98 1:59p Jason + * touch terrain textures when initting a level + * + * 17 2/18/98 3:25p Samir + * Added test systems code. + * + * 16 2/18/98 1:19a Samir + * DoLevelIntro now bails at the right place if we're in multiplayer mode. + * + * 15 2/17/98 8:26p Chris + * + * 14 2/17/98 7:48p Chris + * + * 13 2/17/98 7:47p Chris + * + * 12 2/17/98 4:42p Samir + * Current_level is now defined after calling DoLevelIntro. + * + * 11 2/17/98 2:22p Jason + * fixed multiplayer sequencing with briefings + * + * 10 2/17/98 12:20p Chris + * Made sounds not get paged out at end/beginning of level + * + * 9 2/13/98 6:43p Samir + * ifdef out editor code iin GameSequencer. + * + * 8 2/13/98 12:40p Jason + * added functions to touch all data in a level when the level is loaded + * + * 7 2/13/98 10:57a Samir + * Changed some gamescript initialization. + * + * 6 2/12/98 6:51p Matt + * Renamed GameLoop() to be GameFrame(), cleaned up the StartTime/StopTime + * system, and moved the interval script call to GameFrame(). + * + * 5 2/12/98 5:08p Matt + * Reset cockpit mode when starting a level. Unfortunately, this involved + * some semi-major mucking with game sequencing. + * + * 4 2/11/98 4:36p Samir + * Call SetGameMode to modify game mode. + * + * 3 2/09/98 3:58p Matt + * Initialize small views and camera views at level start + * + * 2 2/08/98 5:01p Samir + * New game sequencing changes. + * + * 1 2/08/98 3:54p Samir + * Initial revision. + * + * $NoKeywords: $ + */ +#ifdef USE_PROFILER +#include +#endif + +#include "gamesequence.h" + +#include "game.h" +#include "gameloop.h" +#include "descent.h" +#include "player.h" +#include "Mission.h" +#include "BOA.h" +#include "gameevent.h" +#include "AIMain.h" + +#include "soar_helpers.h" + +#include "terrain.h" +#include "hlsoundlib.h" +#include "SmallViews.h" +#include "polymodel.h" +#include "gametexture.h" +#include "hud.h" +#include "findintersection.h" +#include "menu.h" +#include "newui.h" +#include "cockpit.h" +#include "help.h" +#include "buddymenu.h" +#include "mem.h" +#include "soundload.h" +#include "robot.h" +#include "screens.h" +#include "game2dll.h" +#include "ship.h" +#include "TelCom.h" +#include "scorch.h" +#include "render.h" +#include "stringtable.h" +#include "ddio_common.h" +#include "gamesave.h" +#include "sounds.h" +#include "ambient.h" +#include "vclip.h" +#include "pilot.h" +#include "doorway.h" +#include "matcen.h" +#include "dedicated_server.h" +#include "networking.h" +#include "levelgoal.h" +#include "demofile.h" +#include "lightmap_info.h" +#include "lightmap.h" +#include "fireball.h" +#include "d3music.h" +#include "TelComAutoMap.h" +#include "aiambient.h" +#include "ObjScript.h" +#include "marker.h" +#include "gamecinematics.h" +#include "osiris_dll.h" +#include "debuggraph.h" +#include "multi_dll_mgr.h" +#include "multi_ui.h" +#include "rocknride.h" +#include "gamepath.h" +#include "vclip.h" +#include "bsp.h" +#include "vibeinterface.h" + +#include "args.h" +void ResetHudMessages(void); + +// Variables +tGameState Game_state = GAMESTATE_IDLE; // current game state. +tGameState Last_game_state = GAMESTATE_IDLE; // previous frame game state. +int Game_interface_mode = GAME_INTERFACE; // game interface mode (options menu?) + +extern bool FirstGame; + +static bool Level_started = false; +static int Level_warp_next = 0; + +#ifdef E3_DEMO +extern float E3_TIME_LIMIT; +extern bool E3_enforce_level_restart; +#endif + +extern float Multi_Game_time_start; + +extern bool Demo_looping; +extern bool IsRestoredGame; + +// internal functions +bool StartNewGame(); // We start a game by using the current mission in memory. +bool DoLevelIntro(); // shows movie and briefing +void StartGameFromEditor(); // set up new game/new level stuff when playing from editor +void EndLevel(int state); // ends the current level. +void RestartLevel(); // restarts the current level. +void RunGameMenu(); // executes a game menu. +void FreeThisLevel(); // frees any data/esc that was created for that level. +void FlushDataCache(); // Clears out all the level specific stuff from memory +void SetNextLevel(); // advances to the next level +void CheckHogfile(); // make sure we have the right hogfile + +// Data paging functions +void PageInAllData (); +void PageInLevelTexture (int); +bool PageInSound (int); +void PageInDoor (int); +void PageInWeapon (int); +void PageInGeneric (int); +void PageInShip (int); + +// Data allocation arrays, for keeping track of what textures/sounds are level specific +ubyte Models_to_free[MAX_POLY_MODELS]; +ubyte Textures_to_free[MAX_TEXTURES]; +ubyte Sounds_to_free[MAX_TEXTURES]; + +#ifdef EDITOR +extern vector editor_player_pos; +extern matrix editor_player_orient; +extern int editor_player_roomnum; +#endif + +extern bool mn3_Open(const char *mn3file); +extern void mn3_Close(); + +extern bool Game_gauge_do_time_test; +extern char Game_gauge_usefile[_MAX_PATH]; + +/////////////////////////////////////////////////////////////////////////////// +// Sequences game events +/////////////////////////////////////////////////////////////////////////////// +bool GameSequencer() +{ + bool in_editor = (GetFunctionMode() == EDITOR_GAME_MODE) ; + tGameState old_game_state; + +// interpret current function mode. + switch (GetFunctionMode()) + { + case RESTORE_GAME_MODE: + case LOADDEMO_MODE: + case GAMEGAUGE_MODE: + SetFunctionMode(GAME_MODE); // need to do so sequencer thiks we're in game. + break; + } + + +// The main game sequencer + while ((GetFunctionMode() == GAME_MODE) || (GetFunctionMode() == EDITOR_GAME_MODE)) + { + old_game_state = Game_state; + + switch (Game_state) + { + case GAMESTATE_NEW: + StartNewGame(); + SetGameState(GAMESTATE_LVLSTART); + break; + + case GAMESTATE_LVLSTART: + CheckHogfile(); //make sure we have the right hogfile + + if (in_editor) { //start in editor + #ifdef EDITOR + StartGameFromEditor(); + #endif + } + else { //start in game + if (!DoLevelIntro() || !LoadAndStartCurrentLevel()) + { + if(Game_mode & GM_MULTI) + MultiLeaveGame(); + MultiDLLGameStarting=0; + Multi_bail_ui_menu=false; + SetFunctionMode(MENU_MODE); // return to main menu + break; + } + } + + SetGameState(GAMESTATE_LVLPLAYING); + break; + + case GAMESTATE_LVLPLAYING: + // must do to display main menu interfaces + if (Last_game_state == GAMESTATE_LVLSTART) { + // 1st frame resume sounds. + Sound_system.ResumeSounds(); + } + + if (Game_interface_mode != GAME_INTERFACE) { + RunGameMenu(); + } + else { + Descent->defer(); + GameFrame(); + } + break; + + case GAMESTATE_LOADDEMO: + if(DemoPlaybackFile(Demo_fname)) { + SetGameState(GAMESTATE_LVLPLAYING); + } + else { + SetFunctionMode(MENU_MODE); + } + break; + case GAMESTATE_GAMEGAUGEDEMO: + { + char ggdemopath[_MAX_PATH*2]; + ddio_MakePath(ggdemopath,Base_directory,"demo",Game_gauge_usefile,NULL); + if(DemoPlaybackFile(ggdemopath)) { + SetGameState(GAMESTATE_LVLPLAYING); + } + else { + SetFunctionMode(MENU_MODE); + } + } + break; + case GAMESTATE_LOADGAME: + if(Game_mode & GM_MULTI) + { + DoMessageBox(TXT_ERROR, TXT_CANT_LOAD_MULTI, MSGBOX_OK); + break; + } + D3MusicStop(); // make sure all music streams are stopped before restoring game. + PauseGame(); + if (!LoadCurrentSaveGame()) { + SetFunctionMode(MENU_MODE); + } + else { + SetGameState(GAMESTATE_LVLPLAYING); + SetScreenMode(SM_GAME); + SetHUDMode(GetHUDMode()); + } + ResumeGame(); + break; + + case GAMESTATE_LVLEND: + SuspendControls(); + EndLevel(1); + SetGameState(GAMESTATE_LVLNEXT); + + + //This is for HEAT.NET and their tournement mode stuff + //if(FindArg("-doonelevel")) + // SetGameState(GAMESTATE_LVLSTART); + if(FindArg("-doonelevel")) + { + SetFunctionMode(QUIT_MODE); + } + + break; + + case GAMESTATE_LVLNEXT: + SetNextLevel(); + SetGameState(GAMESTATE_LVLSTART); + break; + + case GAMESTATE_LVLWARP: + SuspendControls(); + EndLevel(-1); + SetCurrentLevel(Level_warp_next); + SetGameState(GAMESTATE_LVLSTART); + break; + + case GAMESTATE_LVLFAILED: + SuspendControls(); + EndLevel(0); + SetGameState(GAMESTATE_LVLSTART); //restart same level + break; + } + Last_game_state = old_game_state; + } + + #ifdef EDITOR //Ugly hack to get play position back to the editor + if (Player_object && (Player_object->type == OBJ_PLAYER)) { + editor_player_pos = Player_object->pos; + editor_player_orient = Player_object->orient; + editor_player_roomnum = Player_object->roomnum; + } + else + editor_player_roomnum = -1; + #endif + +// free up scripts. + PltWriteFile(&Current_pilot); + FreeThisLevel(); + + SetGameMode(GM_NONE); + + return true; +} + +//Make sure we have the correct hogfile +void CheckHogfile() +{ + char hogpath[_MAX_PATH*2]; + mprintf((0,"Checking to see if we need to open another hog off of disk or CDROM\n")); + + if(Current_mission.filename && (strcmpi(Current_mission.filename,"d3.mn3")==0) && (Current_mission.cur_level > 4) ) + { + //close the mission hog file and open d3_2.mn3 + mn3_Close(); + char *hogp = GetMultiCDPath("d3_2.mn3"); + if(hogp) + { + strcpy(hogpath,hogp); + mn3_Open(hogpath); + mem_free(Current_mission.filename); + Current_mission.filename = mem_strdup("d3_2.mn3"); + } + else + { + SetFunctionMode(MENU_MODE); + } + } + else if(Current_mission.filename && (strcmpi(Current_mission.filename,"d3_2.mn3")==0) && (Current_mission.cur_level <= 4) ) + { + //Part 2 of the mission is d3_2.mn3 + //close the mission hog file and open d3.mn3 + mn3_Close(); + char *hogp = GetMultiCDPath("d3.mn3"); + if(hogp) + { + strcpy(hogpath,hogp); + mn3_Open(hogpath); + mem_free(Current_mission.filename); + Current_mission.filename = mem_strdup("d3.mn3"); + } + else + { + SetFunctionMode(MENU_MODE); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// quickly sets up a mission of 1 level, and doesn't do any +// fancy intro stuff +bool SimpleStartLevel(char *level_name) +{ +// this initializes a mini one level mission with no frills. + + Current_mission.cur_level = 1; + Current_mission.num_levels = 1; + Current_mission.levels = (tLevelNode *)mem_malloc(sizeof(tLevelNode)); + memset(Current_mission.levels, 0, sizeof(tLevelNode)); + + Current_level = NULL; + Current_mission.levels[0].filename = mem_strdup(level_name); + InitMissionScript(); + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// We start a game by using the current mission in memory. +// First we display the mission briefing (the intro movie for Descent 3, if not an add on) +// Then we start level +/////////////////////////////////////////////////////////////////////////////// + +bool StartNewGame() +{ + SetCurrentLevel(Current_mission.cur_level); + + ResetGamemode(); + + if (!(Game_mode & GM_MULTI)) { + Player_num=0; // Reset player num + Players[Player_num].ship_index = FindShipName(DEFAULT_SHIP); + ASSERT(Players[Player_num].ship_index != -1); + } + + if (Game_mode & GM_MULTI) + CallGameDLL (EVT_GAMECREATED,&DLLInfo); + + // Load any addon data + mng_LoadAddonPages(); + + InitPlayerNewShip(Player_num,INVRESET_ALL); + InitPlayerNewGame (Player_num); + + InitCameraViews(1); //Turn off all camera views, including rear views + + Quicksave_game_slot = -1; // reset so that quicksave key will always go to savegame menu first + + return true; +} + + +//Shows the intro movie and briefing for a level +bool DoLevelIntro() +{ + tLevelNode *lvl = Current_level; + + mprintf((0,"In DoLevelIntro()\n")); + + // multiplayer stuff (we skip the movie and briefings) + if (Game_mode & GM_MULTI) { + if (Netgame.local_role==LR_CLIENT) { + // Clear out residual junk + MultiFlushAllIncomingBuffers(); + + if (! MultiPollForLevelInfo ()) { + return false; + } + } + return true; + } + +#ifdef STEALTH //DAJ just get started + return true; +#endif + // if this level has a movie: + if (lvl->flags & LVLFLAG_STARTMOVIE) { + DoMissionMovie(Current_mission.levels[Current_mission.cur_level-1].moviename); + } + + // if this level has a briefing: + if (lvl->flags & LVLFLAG_BRIEFING) { + if(!DoMissionBriefing(Current_mission.cur_level-1)) { + //quit exit to main menu + return false; + } + } + return true; +} + +//Sets the current level for subsequent level loads, movies, or briefings +void SetCurrentLevel(int level) +{ + ASSERT(level>0 && level<=Current_mission.num_levels); + + //Set this level as the mission's current level + Current_mission.cur_level = level; + + //Set pointer to this level + Current_level = &Current_mission.levels[level-1]; +} + +void StartLevelSounds(); + +//#ifdef GAMEGAUGE +extern float gamegauge_start_time; +//#endif + +//Get rid of any viewer objects in the level +void ClearViewerObjects() +{ + int i; + object *objp; + + for (i=0,objp=Objects;i<=Highest_object_index;i++,objp++) + if (objp->type == OBJ_VIEWER) { + mprintf((0,"Deleting viewer object %d\n",i)); + ObjDelete(i); + } +} + +//If low or medium detail, delete all or some ambient objects +void DeleteAmbientObjects() +{ + int i,count; + object *objp; + + if (!(Game_mode & GM_MULTI) && Detail_settings.Object_complexity == 2) //high + return; + + for (i=0,objp=Objects,count=0;i<=Highest_object_index;i++,objp++) { + if (IS_GENERIC(objp->type) && (Object_info[objp->id].flags & OIF_AMBIENT_OBJECT)) { + if ((Detail_settings.Object_complexity == 0) || (count & 1) || (Game_mode & GM_MULTI)) + ObjDelete(i); + count++; + } + } +} + +void Localization_SetLanguage(int type); +int Localization_GetLanguage(void); +void LoadLevelText(char *level_filename); + +//Starts the level, which has already been loaded +void StartLevel() +{ + extern void RestoreCameraRearviews(); // gameloop.cpp + + //Init time + Gametime = 0.0f; + + //Make sure all sounds have stopped + Sound_system.StopAllSounds(); + + //Get the data for this level + PageInAllData(); + + // TEMP HACK + if (rend_SupportsBumpmapping()) + Detail_settings.Bumpmapping_enabled=1; + else + Detail_settings.Bumpmapping_enabled=0; + + + + //Initialize a bunch of stuff for this level + MakeBOA(); +#ifndef MACINTOSH + ComputeAABB(true); +#endif + + //Clear/reset objects & events + ClearAllEvents(); + ClearRoomChanges(); + ResetMarkers(); + ResetScorches(); + ResetWaypoint(); + ResetLightGlows(); + DoorwayDeactivateAll(); + ClearViewerObjects(); + DeleteAmbientObjects(); + + //AI, scripting, & Soar + AIInitAll(); + + //Set up the player object + ResetPlayerObject(Player_num); + + DSSoarInit(); + + InitMatcensForLevel(); + + //What is this? + a_life.InitForLevel(); + + //Start sound & music + StartLevelSounds(); + Sound_system.PauseSounds(); // HACK!! pause sounds started up to now. + D3MusicStart(Current_level->score); + + //Get the list of waypoints from the waypoint objects + MakeAtuoWaypointList(); + + //Test stuff + #ifdef _DEBUG + InitTestSystems(); + #endif + + //Set screen mode + SetScreenMode(SM_GAME); + + //Init HUD and cockpit + int ship_index; + + if(Game_mode&GM_MULTI) + { + char ship_model[PAGENAME_LEN]; + Current_pilot.get_ship(ship_model); + ship_index = FindShipName(ship_model); + ASSERT(ship_index != -1); //DAJ -1FIX + if(Netgame.local_role==LR_SERVER) + { + if(PlayerIsShipAllowed(Player_num,ship_model)) + { + Players[Player_num].ship_index=FindShipName (ship_model); + ASSERT(Players[Player_num].ship_index != -1); //DAJ -1FIX + } + else + { + mprintf((0,"Player %d wanted to use a ship (%s) which wasn't allowed.\n",Player_num,ship_model)); + int i; + bool found_one=false; + + //Loop through ships, looking for one that's allowed + for (i=0;iflush(); +#endif + + Level_goals.InitLevel(); + + //Do multiplayer & player stuff + if (Game_mode & GM_MULTI) { + MultiStartNewLevel(Current_mission.cur_level); + } + else { //Init player for single-player + DeleteMultiplayerObjects(); + InitPlayerNewLevel (Player_num); + } + + //Set flags + Level_started = true; + Multi_bail_ui_menu = false; + + //Clear robot keys + Global_keys = 0; + + // Clear OSIRIS stuff + Osiris_ResetAllTimers(); + + // Initialize IGC + Cinematic_LevelInit(); + + //Bash the language to english + int save_lang = Localization_GetLanguage(); + Localization_SetLanguage(LANGUAGE_ENGLISH); + + //Load the English text so the goal stuff will initialize correctly + LoadLevelText(Current_mission.levels[Current_mission.cur_level-1].filename); + + //Restore the correct language + Localization_SetLanguage(save_lang); + + //Init the level scripts + InitLevelScript(); + + //Load the localized text + LoadLevelText(Current_mission.levels[Current_mission.cur_level-1].filename); + + Gametime = 0.0f; + //Start the clock + InitFrameTime(); + //#ifdef GAMEGAUGE + gamegauge_start_time = timer_GetTime(); + //#endif + LoadLevelProgress(LOAD_PROGRESS_DONE,0); + +#ifdef MACINTOSH + // flush controller system. + ResumeControls(); + Controller->flush(); + #ifdef USE_OPENGL + extern void opengl_ResetContext(void); + opengl_ResetContext(); + #endif +#ifdef USE_PROFILER + ProfilerSetStatus(1); +#endif +#endif +} + +//Loads a level and starts everything up +bool LoadAndStartCurrentLevel() +{ + char hogpath[_MAX_PATH*2]; + //This is a bit redundant because we just did it in most cases, but we need to be sure that it always happens, + //and this code is here for weird systems, like save/load and demo, etc. + if(Current_mission.filename && (strcmpi(Current_mission.filename,"d3.mn3")==0) && (Current_mission.cur_level > 4) ) + { + //close the mission hog file and open d3_2.mn3 + mn3_Close(); + char *hogp = GetMultiCDPath("d3_2.mn3"); + if(hogp) + { + strcpy(hogpath,hogp); + mn3_Open(hogpath); + mem_free(Current_mission.filename); + Current_mission.filename = mem_strdup("d3_2.mn3"); + } + else + { + SetFunctionMode(MENU_MODE); + } + } + else if(Current_mission.filename && (strcmpi(Current_mission.filename,"d3_2.mn3")==0) && (Current_mission.cur_level <= 4) ) + { + //Part 2 of the mission is d3_2.mn3 + //close the mission hog file and open d3.mn3 + mn3_Close(); + char *hogp = GetMultiCDPath("d3.mn3"); + if(hogp) + { + strcpy(hogpath,hogp); + mn3_Open(hogpath); + mem_free(Current_mission.filename); + Current_mission.filename = mem_strdup("d3.mn3"); + } + else + { + SetFunctionMode(MENU_MODE); + } + } + + // load the level. if fails, then bail out + //ShowProgressScreen (TXT_LOADINGLEVEL); + if (!LoadMissionLevel(Current_mission.cur_level)) + return false; + + AutomapClearVisMap(); + + //Now start the level + StartLevel(); + + //Done! + return true; +} + +#ifdef EDITOR +//Called to do game initialization stuff when playing from the editor +//Mostly, this will be the same stuff as StartNewLevel() +void StartGameFromEditor() +{ + //Set up the player ship + InitPlayerNewShip(Player_num,INVRESET_ALL); + + //Start the level + StartLevel(); + + //Move the player to the editor viewer position + ObjSetPos(Player_object,&editor_player_pos,editor_player_roomnum,&editor_player_orient,false); +} +#endif + +void ComputeCenterPointOnFace(vector *vp,room *rp,int facenum); + +// Start object ambient sounds +void StartObjectSounds() +{ + for(int i = 0; i < MAX_OBJECTS; i++) + { + object *objp = &Objects[i]; + + //If generic object, check for ambient sound + switch (objp->type) { + + case OBJ_CLUTTER: + case OBJ_BUILDING: + case OBJ_ROBOT: + case OBJ_POWERUP: { + int ambient_sound = Object_info[objp->id].sounds[GSI_AMBIENT]; + if (ambient_sound != SOUND_NONE_INDEX) + Sound_system.Play3dSound(ambient_sound, SND_PRIORITY_LOWEST, objp); + break; + } + + case OBJ_SOUNDSOURCE: + ASSERT(objp->control_type == CT_SOUNDSOURCE); + if (objp->name) //if has name, attach sound to object + Sound_system.Play3dSound(objp->ctype.soundsource_info.sound_index, SND_PRIORITY_NORMAL, objp, objp->ctype.soundsource_info.volume); + else { //no name, so attach to 3D position & delete object + pos_state ps; + ps.position = &objp->pos; + ps.roomnum = objp->roomnum; + ps.orient = (matrix *) &Identity_matrix; + Sound_system.Play3dSound(objp->ctype.soundsource_info.sound_index,SND_PRIORITY_NORMAL, &ps,objp->ctype.soundsource_info.volume); + ObjDelete(i); + } + break; + } + } +} + +//Go through the level and start and sounds associated with textures +void StartTextureSounds() +{ + int r,f; + room *rp; + face *fp; + + for (r=0,rp=Rooms;r<=Highest_room_index;r++,rp++) { + if (rp->used) { + for (f=0,fp=rp->faces;fnum_faces;f++,fp++) { + int sound = GameTextures[fp->tmap].sound; + if ((sound != -1) && (sound != SOUND_NONE_INDEX)) { + vector pos; + pos_state ps; + ComputeCenterPointOnFace(&pos,rp,f); + ps.position = &pos; + ps.roomnum = r; + ps.orient = (matrix *) &Identity_matrix; + Sound_system.Play3dSound(sound,SND_PRIORITY_LOWEST, &ps); + } + } + } + } +} + +//Starts all the sounds on this level +void StartLevelSounds() +{ + StartObjectSounds(); + StartTextureSounds(); + StartTerrainSound(); + InitAmbientSounds(); +} + +// frees any data/esc that was created for that level +void FreeThisLevel() +{ + if (!Level_started) + return; + + bool original_controls = Control_poll_flag; + + Cinematic_Stop(); + + // make sure our controls are correct (this is needed for failed missions) + if(Control_poll_flag!=original_controls) + { + if(Control_poll_flag) + SuspendControls(); + else + ResumeControls(); + } + + Multi_bail_ui_menu = false; + + DemoAbort(); + +// closes hud. + FreeCockpit(); + CloseShipHUD(); + +// end music sequencer's run. + D3MusicStop(); + + FreeScriptsForLevel(); + ClearAllEvents (); + + // Resets the ambient life controller + a_life.ALReset(); + + DestroyAllMatcens(); + Level_goals.CleanupAfterLevel(); + + Sound_system.StopAllSounds(); + +// Clear out all memory that was used for this past level + FlushDataCache (); + + InitGamePaths(); //DAJ LEAKFIX + + DSSoarEnd(); + + // Reset the camera if need be + if (Player_camera_objnum!=-1) + { + SetObjectDeadFlag (&Objects[Player_camera_objnum]); + Player_camera_objnum=-1; + Viewer_object=&Objects[Players[Player_num].objnum]; + } + + ResetHUDMessages(); + + DestroyDefaultBSPTree(); + + Level_started = false; + IsRestoredGame = false; +} + + +extern void FreeTextureBumpmaps(int); +// Clears out all the level specific stuff from memory +void FlushDataCache () +{ + int i; + + // This must be done before we free polymodels + FreeAllObjects(); + + #ifdef EDITOR + if (Network_up) + return; + #endif + + int texfreed=0; + int modelsfreed=0; + int soundsfreed=0; + + for (i=0;iflags & LVLFLAG_ENDMOVIE) && !(Game_mode & GM_MULTI)) { + DoMissionMovie(lvl->endmovie); + } + +// display postlevel results. + if ((state != -1) && (GetFunctionMode() != EDITOR_GAME_MODE)) + PostLevelResults(state == 1); + + //check for dead players + if (Game_mode&GM_MULTI) + { + //in multiplayer check all players + for(int i=0;i= NETSEQ_PLAYING)) + { + //check to see if player is dying + if ((Players[i].flags & PLAYER_FLAGS_DYING) || (Players[i].flags & PLAYER_FLAGS_DEAD)) + { + mprintf((0,"Prematurely ending death for player %d\n",i)); + EndPlayerDeath (i); + } + } + } + + }else + { + //in single player, check Player_num + if ((Players[Player_num].flags & PLAYER_FLAGS_DYING) || (Players[Player_num].flags & PLAYER_FLAGS_DEAD)) + { + mprintf((0,"Prematurely ending death for player\n")); + EndPlayerDeath (Player_num); + } + } + +// Free any game objects/etc that needs to be done when ending a level here. + FreeThisLevel(); + + //This option is used for HEAT.NET. They have their dedicated server shut down after the + //kill goal or time limit is reached. + if(Dedicated_server) + { + if(FindArg("-quitongoal")) + { + SetFunctionMode (QUIT_MODE); + } + } +} + + +//Advances to the next level. If we were on the last level, end the game +void SetNextLevel() +{ + ASSERT(Current_mission.cur_level > 0); + + tLevelNode *lvl = &Current_mission.levels[Current_mission.cur_level-1]; + + if (Game_mode & GM_MULTI && Multi_next_level!=-1) + { + SetCurrentLevel(Multi_next_level); + Multi_next_level=-1; + } + else if (lvl->flags & LVLFLAG_BRANCH) { + // jump to brached level + mprintf((0,"Branching...\n")); + Current_mission.cur_level = lvl->lvlbranch0; + SetCurrentLevel(Current_mission.cur_level); + } + else if ((Current_mission.cur_level == Current_mission.num_levels) || (lvl->flags & LVLFLAG_FINAL)) { + // in this case we are done with the mission! + DoEndMission(); + + if (!(Game_mode & GM_MULTI)) // Multiplayer loops its levels and never ends + { + SetFunctionMode(MENU_MODE); + } + } + else if (Current_mission.game_state_flags & MSN_STATE_SECRET_LEVEL) { + if (lvl->flags & LVLFLAG_SPAWNSECRET) { + // display secret level screen? + ShowProgressScreen(TXT_ENTERSECRETLVL,NULL,true); + Descent->delay(1.0f); + Current_mission.game_state_flags &= (~MSN_STATE_SECRET_LEVEL); + SetCurrentLevel(lvl->secretlvl); + } + else { + Int3(); //Game says go to secret level, but there's none to go to. + SetCurrentLevel(Current_mission.cur_level+1); + } + } + else { + //nothing special + SetCurrentLevel(Current_mission.cur_level+1); + } +} + + +void GameFrameUI() +{ + GameFrame(); + Last_game_state = Game_state; +} + + +void RunGameMenu() +{ + bool pause_game = false; + + if (Game_interface_mode != GAME_INTERFACE) { + NewUIWindow_alpha = 226; + SetUICallback(GameFrameUI); + SuspendControls(); + RNR_UpdateGameStatus(RNRGSC_INMENU); + + // reset bail flag. + Multi_bail_ui_menu = false; + + if (!(Game_mode & GM_MULTI)) { + pause_game = true; +#ifndef MACINTOSH + PauseGame(); + D3MusicResume(); +#else + StopTime(); //DAJ just the time man + Game_paused = true; +#endif + } + + switch(Game_interface_mode) + { + case GAME_OPTIONS_INTERFACE: + { + extern void OptionsMenu(); + ui_ShowCursor(); + OptionsMenu(); + ui_HideCursor(); + } + break; + case GAME_HELP_INTERFACE: + { + ui_ShowCursor(); + HelpDisplay(); + ui_HideCursor(); + } + break; + case GAME_TOGGLE_DEMO: + { + ui_ShowCursor(); + DemoToggleRecording(); + ui_HideCursor(); + } + break; + case GAME_BUDDY_INTERFACE: + { +// #ifdef DEMO +// DoMessageBox(TXT_ERROR, TXT_WRONGVERSION, MSGBOX_OK); +// #else + ui_ShowCursor(); + BuddyDisplay(); + ui_HideCursor(); +// #endif + } + break; + case GAME_TELCOM_CARGO: + { + //#ifndef DEMO + DoWaitPopup(true,TXT_TELCOMLOAD); + ui_ShowCursor(); + TelComShow(TS_CARGO); + ui_HideCursor(); + DoWaitPopup(false); + //#else + // DoMessageBox(TXT_ERROR, TXT_WRONGVERSION, MSGBOX_OK); + //#endif + } + break; + case GAME_TELCOM_BRIEFINGS: + { + DoWaitPopup(true,TXT_TELCOMLOAD); + ui_ShowCursor(); + TelComShow(TS_MAINMENU); + ui_HideCursor(); + DoWaitPopup(false); + } + break; + case GAME_TELCOM_AUTOMAP: + { + //#ifndef DEMO + DoWaitPopup(true,TXT_TELCOMLOAD); + ui_ShowCursor(); + TelComShow(TS_MAP); + ui_HideCursor(); + DoWaitPopup(false); + //#else + // DoMessageBox(TXT_ERROR, TXT_WRONGVERSION, MSGBOX_OK); + //#endif + } + break; + case GAME_PAUSE_INTERFACE: + if (Game_mode & GM_MULTI) + AddHUDMessage(TXT_NOPAUSEINMULTI); + else { + #ifdef MACINTOSH + DoMessageBoxAdvanced(TXT(TXI_HLPPAUSE), TXT_PRESSOKTOCONT, TXT_OK, KEY_PAGEDOWN, NULL); + #else + DoMessageBoxAdvanced(TXT(TXI_HLPPAUSE), TXT_PRESSOKTOCONT, TXT_OK, KEY_PAUSE, NULL); + #endif + //DoMessageBox(TXT(TXI_HLPPAUSE),TXT_PRESSOKTOCONT, MSGBOX_OK); + } + break; + case GAME_LEVEL_WARP: + Level_warp_next = DisplayLevelWarpDlg(-1); + if (Level_warp_next != -1) + SetGameState(GAMESTATE_LVLWARP); + break; + case GAME_EXIT_CONFIRM: + { + int ret = 0; + ui_ShowCursor(); + //Weird code for the training mission and the first time you play... + if(Current_mission.filename && (strcmpi(Current_mission.filename,"training.mn3")==0) && (FirstGame) ) + { + ret = DoMessageBoxAdvanced(TXT_TRAININGABORTTITLE, TXT_TRAININGABORTTEXT, TXT_SKIP, KEY_S, TXT_ABORT, KEY_A, TXT_CANCEL, KEY_ESC, NULL); + if(ret==2) + { + //Cancel + ret = 0; + } + else if(ret==1) + { + //Abort -- back to the main menu + FirstGame = false; + } + else if(ret==0) + { + //Skip + ret = 1; + } + + } + else + { + ret = DoMessageBox(TXT_CONFIRM,TXT_CONFIRMEXIT,MSGBOX_YESNO); + } + if(ret){ + Demo_looping = false; + if ((Players[Player_num].flags & PLAYER_FLAGS_DYING) || (Players[Player_num].flags & PLAYER_FLAGS_DEAD)) + { + EndPlayerDeath(Player_num); + if (Game_mode & GM_MULTI) + { + MultiSendEndPlayerDeath (); + MultiLeaveGame(); + } + SetFunctionMode(MENU_MODE); + }else{ + if (Game_mode & GM_MULTI) + MultiLeaveGame(); + SetFunctionMode(MENU_MODE); + } + Sound_system.StopAllSounds(); + } + }break; + case GAME_DLL_INTERFACE: + { + DLLInfo.me_handle = DLLInfo.it_handle = OBJECT_HANDLE_NONE; + ui_ShowCursor(); + CallGameDLL (EVT_CLIENT_SHOWUI,&DLLInfo); + ui_HideCursor(); + }break; + + case GAME_DEBUGGRAPH_INTERFACE: + { + DebugGraph_DisplayOptions(); + }break; + + case GAME_SAVE_INTERFACE: + ui_ShowCursor(); +#ifdef EDITOR + if (GetFunctionMode() == EDITOR_GAME_MODE) + DoMessageBox("Silly", "You can't save while playing from the editor!", MSGBOX_OK); + else +#endif + SaveGameDialog(); // link to above endif! + ui_HideCursor(); + break; + + case GAME_LOAD_INTERFACE: + ui_ShowCursor(); +#ifdef EDITOR + if (GetFunctionMode() == EDITOR_GAME_MODE) + DoMessageBox("Silly", "You can't load while playing from the editor!", MSGBOX_OK); + else +#endif + LoadGameDialog(); + ui_HideCursor(); + break; + + case GAME_POST_DEMO: +#ifdef GAMEGAUGE + if(0) +#else + if(!Game_gauge_do_time_test) +#endif + { + DemoPostPlaybackMenu(); + } + SetFunctionMode(MENU_MODE); + break; + case GAME_DEMO_LOOP: + SetFunctionMode(MENU_MODE); + break; + default: + Int3(); //unhandled game interface mode + } + + if (pause_game) { +#ifndef MACINTOSH + ResumeGame(); +#else + StartTime(); //DAJ + Game_paused = false; +#endif + pause_game = false; + } + + ResumeControls(); + SetUICallback(NULL); + Game_interface_mode = GAME_INTERFACE; + NewUIWindow_alpha = 192; + Clear_screen = 4; + } +} + +void PageInLevelTexture (int id) +{ + if (id==-1 || id==0) + return; + + if (Dedicated_server) + return; + + TouchTexture (id); + + // Upload all these textures to the card + if (GameTextures[id].flags & TF_ANIMATED) + { + vclip *vc=&GameVClips[GameTextures[id].bm_handle]; + for (int i=0;inum_frames;i++) + { + if (!(GameTextures[id].flags & TF_PROCEDURAL) && !(GameTextures[id].flags & TF_SPECULAR)) + GameBitmaps[vc->frames[i]].flags|=BF_COMPRESSABLE; + else + GameBitmaps[vc->frames[i]].flags&=~BF_COMPRESSABLE; + + if (!Dedicated_server && GameBitmaps[vc->frames[i]].cache_slot==-1) + rend_PreUploadTextureToCard (vc->frames[i],MAP_TYPE_BITMAP); + } + } + else + { + if (!(GameTextures[id].flags & TF_PROCEDURAL) && !(GameTextures[id].flags & TF_SPECULAR)) + GameBitmaps[GameTextures[id].bm_handle].flags|=BF_COMPRESSABLE; + else + GameBitmaps[GameTextures[id].bm_handle].flags&=~BF_COMPRESSABLE; + + if (!Dedicated_server && GameBitmaps[GameTextures[id].bm_handle].cache_slot==-1) + rend_PreUploadTextureToCard (GameTextures[id].bm_handle,MAP_TYPE_BITMAP); + } + + + if (GameTextures[id].flags & TF_DESTROYABLE && GameTextures[id].destroy_handle!=-1) + PageInLevelTexture (GameTextures[id].destroy_handle); + + Textures_to_free[id]=1; +} + +bool PageInSound (int id) +{ + if (id==-1) + return false; + + if (Dedicated_server) + return false; + +// sometimes, id passed was 0xffff which seems like a short -1. The if statement +// ensures that the array Sounds_to_free is dealt with properly. + if (Sound_system.CheckAndForceSoundDataAlloc(id)) { + Sounds_to_free[id]=1; + return true; + } + + return false; +} + +void PageInDoor (int id) +{ + //Set sounds + door *doorpointer=&Doors[id]; + + PageInPolymodel (doorpointer->model_handle); + Models_to_free[doorpointer->model_handle]=1; + + poly_model *pm=&Poly_models[doorpointer->model_handle]; + for (int t=0;tn_textures;t++) + PageInLevelTexture (pm->textures[t]); + + + if (doorpointer->open_sound!=-1 && doorpointer->open_sound!=SOUND_NONE_INDEX) + PageInSound (doorpointer->open_sound); + if (doorpointer->close_sound!=-1 && doorpointer->close_sound!=SOUND_NONE_INDEX) + PageInSound (doorpointer->close_sound); +} + +void PageInWeapon (int id) +{ + weapon *weaponpointer=&Weapons[id]; + + if (id==-1) + return; + + int i; + + if (!(weaponpointer->flags & (WF_IMAGE_BITMAP|WF_IMAGE_VCLIP))) + { + PageInPolymodel (weaponpointer->fire_image_handle); + Models_to_free[weaponpointer->fire_image_handle]=1; + + poly_model *pm=&Poly_models[weaponpointer->fire_image_handle]; + for (int t=0;tn_textures;t++) + PageInLevelTexture (pm->textures[t]); + } + + // Load the various textures associated with this weapon + if (weaponpointer->explode_image_handle!=-1) + { + PageInLevelTexture (weaponpointer->explode_image_handle); + } + + if (weaponpointer->particle_handle!=-1) + { + PageInLevelTexture (weaponpointer->particle_handle); + } + + if (weaponpointer->smoke_handle!=-1) + { + PageInLevelTexture(weaponpointer->smoke_handle); + } + + if (weaponpointer->scorch_handle!=-1) + { + PageInLevelTexture (weaponpointer->scorch_handle); + } + + if (weaponpointer->icon_handle!=-1) + { + PageInLevelTexture (weaponpointer->icon_handle); + } + + // Try to load spawn weapons + if (weaponpointer->spawn_handle!=-1 && weaponpointer->spawn_count>0 && weaponpointer->spawn_handle!=id) + { + PageInWeapon (weaponpointer->spawn_handle); + } + + if (weaponpointer->alternate_spawn_handle!=-1 && weaponpointer->spawn_count>0 && weaponpointer->alternate_spawn_handle!=id) + { + PageInWeapon (weaponpointer->alternate_spawn_handle); + } + + if (weaponpointer->robot_spawn_handle!=-1) + { + PageInGeneric (weaponpointer->robot_spawn_handle); + } + + // Try and load the various sounds + for (i=0;isounds[i]!=SOUND_NONE_INDEX) + { + PageInSound (weaponpointer->sounds[i]); + } + } +} +int paged_in_count = 0; +extern int need_to_page_in; +extern int need_to_page_num; +int paged_in_num = 0; + +//#define PAGED_IN_CALC paged_in_count ? (float)paged_in_count/(float)need_to_page_in : 0.0f +#define PAGED_IN_CALC paged_in_num ? (float)paged_in_num/(float)need_to_page_num : 0.0f +void PageInShip (int id) +{ + int i,t; + + ship *shippointer=&Ships[id]; + + // Page in all textures for this object + + PageInPolymodel (shippointer->model_handle); + Models_to_free[shippointer->model_handle]=1; + + + poly_model *pm=&Poly_models[shippointer->model_handle]; + + for (t=0;tn_textures;t++) + { + PageInLevelTexture (pm->textures[t]); + // Create bumps if neccessary + if (rend_SupportsBumpmapping()) + { + if (GameTextures[pm->textures[t]].bumpmap==-1) + { + mprintf ((0,"Trying to make bumpmap!\n")); + BuildTextureBumpmaps (pm->textures[t]); + } + } + } + + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + if (shippointer->med_render_handle!=-1) + { + PageInPolymodel (shippointer->med_render_handle); + Models_to_free[shippointer->med_render_handle]=1; + + pm=&Poly_models[shippointer->med_render_handle]; + for (t=0;tn_textures;t++) + PageInLevelTexture (pm->textures[t]); + } + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + if (shippointer->lo_render_handle!=-1) + { + PageInPolymodel (shippointer->lo_render_handle); + Models_to_free[shippointer->lo_render_handle]=1; + + pm=&Poly_models[shippointer->lo_render_handle]; + for (t=0;tn_textures;t++) + PageInLevelTexture (pm->textures[t]); + } + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + if (shippointer->dying_model_handle!=-1) + { + PageInPolymodel (shippointer->dying_model_handle); + Models_to_free[shippointer->dying_model_handle]=1; + + pm=&Poly_models[shippointer->dying_model_handle]; + for (t=0;tn_textures;t++) + PageInLevelTexture(pm->textures[t]); + } + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + // Try and load the various weapons + int j; + if(shippointer->static_wb) { + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if (shippointer->static_wb[i].gp_weapon_index[j] != LASER_INDEX) + { + PageInWeapon (shippointer->static_wb[i].gp_weapon_index[j]); + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + } + } + } + } + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + // Try and load the various weapons + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if((j%5)==0) + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + if (shippointer->static_wb[i].fm_fire_sound_index[j] != SOUND_NONE_INDEX) + PageInSound (shippointer->static_wb[i].fm_fire_sound_index[j]); + } + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + if((i%5)==0) + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + if (shippointer->firing_sound[i]!=-1) + PageInSound (shippointer->firing_sound[i]); + + if(shippointer->firing_release_sound[i] != -1) + PageInSound (shippointer->firing_release_sound[i]); + + if (shippointer->spew_powerup[i]!=-1) + PageInGeneric (shippointer->spew_powerup[i]); + + } +} + +void PageInGeneric (int id) +{ + int i,t; + + if (id==-1) + return; + + object_info *objinfopointer=&Object_info[id]; + + // Page in all textures for this object + + PageInPolymodel (objinfopointer->render_handle); + Models_to_free[objinfopointer->render_handle]=1; + + poly_model *pm=&Poly_models[objinfopointer->render_handle]; + + for (t=0;tn_textures;t++) + { + PageInLevelTexture(pm->textures[t]); + + // Create bumps if neccessary + if (objinfopointer->type==OBJ_ROBOT && rend_SupportsBumpmapping()) + { + if (GameTextures[pm->textures[t]].bumpmap==-1) + BuildTextureBumpmaps (pm->textures[t]); + } + } + + if (objinfopointer->med_render_handle!=-1) + { + PageInPolymodel (objinfopointer->med_render_handle); + Models_to_free[objinfopointer->med_render_handle]=1; + + + pm=&Poly_models[objinfopointer->med_render_handle]; + for (t=0;tn_textures;t++) + PageInLevelTexture (pm->textures[t]); + } + + if (objinfopointer->lo_render_handle!=-1) + { + PageInPolymodel (objinfopointer->lo_render_handle); + Models_to_free[objinfopointer->lo_render_handle]=1; + + pm=&Poly_models[objinfopointer->lo_render_handle]; + for (t=0;tn_textures;t++) + PageInLevelTexture (pm->textures[t]); + } + + // Process all sounds for this object + for (i=0;isounds[i]!=SOUND_NONE_INDEX) + { + PageInSound (objinfopointer->sounds[i]); + } + } + + if(objinfopointer->ai_info) { + for (i=0;iai_info->sound[i]!=SOUND_NONE_INDEX) + { + PageInSound (objinfopointer->ai_info->sound[i]); + } + } + } + // Try and load the various wb sounds + int j; + if(objinfopointer->static_wb) { + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if(objinfopointer->static_wb[i].fm_fire_sound_index[j]!=SOUND_NONE_INDEX) + { + PageInSound (objinfopointer->static_wb[i].fm_fire_sound_index[j]); + } + } + } + } + // Try and load the various wb sounds + if(objinfopointer->anim) { + for(i = 0; i < NUM_MOVEMENT_CLASSES; i++) + { + for(j = 0; j < NUM_ANIMS_PER_CLASS; j++) + { + if(objinfopointer->anim[i].elem[j].anim_sound_index!=SOUND_NONE_INDEX) + { + PageInSound (objinfopointer->anim[i].elem[j].anim_sound_index); + } + } + } + } + + // Load the spew types + for(i=0;idspew_number[i]>0 && objinfopointer->dspew[i]!=0 && objinfopointer->dspew[i]!=id) + { + PageInGeneric (objinfopointer->dspew[i]); + } + } + + // Try and load the various weapons + + // Automatically include laser + PageInWeapon (LASER_INDEX); + + if(objinfopointer->static_wb) { + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if (objinfopointer->static_wb[i].gp_weapon_index[j]!=LASER_INDEX) + { + PageInWeapon (objinfopointer->static_wb[i].gp_weapon_index[j]); + } + } + } + } +} + +extern char *Static_sound_names[]; + + + +void PageInAllData () +{ + int i; + paged_in_count = 0; + paged_in_num = 0; + memset (Textures_to_free,0,MAX_TEXTURES); + memset (Sounds_to_free,0,MAX_SOUNDS); + memset (Models_to_free,0,MAX_POLY_MODELS); + + PageInShip (Players[Player_num].ship_index); + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + /* + $$TABLE_TEXTURE "LightFlareStare" + $$TABLE_TEXTURE "LightFlare" + */ + PageInLevelTexture (FindTextureName(IGNORE_TABLE("LightFlareStar"))); + PageInLevelTexture (FindTextureName(IGNORE_TABLE("LightFlare"))); + + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + + if (PreferredRenderer==RENDERER_DIRECT3D) + { + if(LightmapInfo && !Dedicated_server) + { + if (!NoLightmaps) + { + for (i=0;inum_faces;t++) + { + PageInLevelTexture(rp->faces[t].tmap); + } + } + LoadLevelProgress(LOAD_PROGRESS_PAGING_DATA,PAGED_IN_CALC); + + // Touch all terrain textures + for (i=0;itype==OBJ_POWERUP || obj->type==OBJ_ROBOT || obj->type==OBJ_CLUTTER || obj->type==OBJ_BUILDING) + { + PageInGeneric (obj->id); + continue; + } + + if (obj->type==OBJ_DOOR) + { + PageInDoor (obj->id); + continue; + } + } + LoadLevelProgress(LOAD_PROGRESS_PREPARE,0); +} diff --git a/Descent3/gamesequence.h b/Descent3/gamesequence.h new file mode 100644 index 000000000..3f448aca7 --- /dev/null +++ b/Descent3/gamesequence.h @@ -0,0 +1,161 @@ +/* + * $Logfile: /DescentIII/main/gamesequence.h $ + * $Revision: 24 $ + * $Date: 4/08/99 3:12p $ + * $Author: Matt $ + * + * Game Sequencer + * + * $Log: /DescentIII/main/gamesequence.h $ + * + * 24 4/08/99 3:12p Matt + * Finished cleaning up level sequencing code. Got rid of all the "level + * minus one" stuff. + * + * 23 4/07/99 12:30p Matt + * Added code for failed missions. + * + * 22 4/04/99 8:15p Jeff + * added debug graph stuff + * + * 21 2/12/99 3:54a Jeff + * added function to restart a level, and a cheat key to do so (del-alt-e) + * + * 20 1/31/99 3:44p Matt + * Streamlined game sequencing + * + * 19 1/15/99 7:16p Kevin + * Added GameGauge Configuration & code + * + * 18 12/15/98 4:28p Jeff + * added mission data information to the pilot files to save what the + * highest level they achieved on a mission is. Added level select dialog + * (not hooked up) and level warp cheat. + * + * 17 11/19/98 5:40p Kevin + * Demo system + * + * 16 10/08/98 7:29p Samir + * revamped sequencing. + * + * 15 10/08/98 12:00p Kevin + * Demo system work + * + * 14 10/05/98 12:09p Kevin + * Converted projects to VC6 and demo file stuff added + * + * 13 8/18/98 3:02p Samir + * new sequencing for LoadGameState and PLEASE define Current_level some + * other way (or at least keep the definition in StartNewLebvel) + * + * 12 8/18/98 1:05a Samir + * added save and load interfaces. + * + * 11 8/16/98 5:56p Jeff + * added game interface for multiplayer dll ui stuff + * + * 10 6/25/98 12:43p Jeff + * Added exit game confirmation + * + * 9 5/18/98 4:36p Jeff + * TelCom uses Game_interface_mode variable now + * + * 8 4/03/98 3:36p Chris + * Added the start of the buddy menu + * + * 7 3/13/98 12:08p Samir + * Added SimpleStartLevel. + * + * 6 3/05/98 2:51p Jeff + * Added Help interface + * + * 5 3/03/98 12:10p Samir + * Added simple pausing. + * + * 4 3/02/98 5:52p Samir + * Added game interface system. + * + * 3 2/12/98 5:08p Matt + * Reset cockpit mode when starting a level. Unfortunately, this involved + * some semi-major mucking with game sequencing. + * + * 2 2/08/98 5:01p Samir + * New game sequencing changes. + * + * 1 2/08/98 3:54p Samir + * Initial revision. + * + * $NoKeywords: $ + */ + +#ifndef GAMESEQUENCE_H +#define GAMESEQUENCE_H + + +// gamesequencer states: +// game states + +typedef enum tGameState +{ + GAMESTATE_IDLE, // no state + GAMESTATE_NEW, // starts a new game (with current mission.) + GAMESTATE_LVLNEXT, // go to the next level + GAMESTATE_LVLSTART, // happens when a level starts, game time starts here. + GAMESTATE_LVLPLAYING, // calls gameloop, polls for input. + GAMESTATE_LOADGAME, // a load game request has been issued. + GAMESTATE_LVLEND, // calls endlevel and any other endlevel stuff. + GAMESTATE_LVLFAILED, // a level was unsuccessfully ended + GAMESTATE_LVLWARP, // warp to a new level + GAMESTATE_LOADDEMO, // Load whatever demo was chosen in the UI + GAMESTATE_GAMEGAUGEDEMO // Play a gamegauge demo and exit +} +tGameState; + + +// top level interfaces for game. +#define GAME_INTERFACE 0 +#define GAME_OPTIONS_INTERFACE 1 +#define GAME_PAUSE_INTERFACE 2 +#define GAME_HELP_INTERFACE 3 +#define GAME_BUDDY_INTERFACE 4 +#define GAME_TELCOM_BRIEFINGS 5 +#define GAME_TELCOM_AUTOMAP 6 +#define GAME_TELCOM_CARGO 7 +#define GAME_EXIT_CONFIRM 8 +#define GAME_DLL_INTERFACE 9 +#define GAME_SAVE_INTERFACE 10 +#define GAME_LOAD_INTERFACE 11 +#define GAME_TOGGLE_DEMO 12 +#define GAME_POST_DEMO 13 +#define GAME_DEMO_LOOP 14 +#define GAME_LEVEL_WARP 15 +#define GAME_DEBUGGRAPH_INTERFACE 16 + +// variables +extern tGameState Game_state; +extern int Game_interface_mode; // current interface mode of game (menu, game?) + + +// functions +// main sequencing code for game. run this to execute a game. +// before calling this function make sure that a +// mission has been loaded/initialized. +bool GameSequencer(); + +//Sets the current level for subsequent level loads, movies, or briefings +void SetCurrentLevel(int level); + +// loads and starts the specified leve. starts a new level (usually called internally) +bool LoadAndStartCurrentLevel(); + +// creates a simple mission to play one level. +bool SimpleStartLevel(char *level_name); + +// sets and retrieves the current gamestate +inline void SetGameState(tGameState state) + { Game_state = state; } + +inline tGameState GetGameState() + { return Game_state; } + +#endif \ No newline at end of file diff --git a/Descent3/gametexture.cpp b/Descent3/gametexture.cpp new file mode 100644 index 000000000..1e4723ab7 --- /dev/null +++ b/Descent3/gametexture.cpp @@ -0,0 +1,952 @@ +/* + * $Logfile: /DescentIII/Main/gametexture.cpp $ + * $Revision: 57 $ + * $Date: 4/19/00 5:07p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/Main/gametexture.cpp $ + * + * 57 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 56 10/21/99 6:34p Matt + * Mac merge + * + * 55 5/23/99 7:43p Jason + * fixed briefing problem with lowmem textures + * + * 54 5/07/99 7:22p Jason + * changes for cards with low videomemory + * + * 53 4/30/99 5:06p Kevin + * misc dedicated server, networking and low memory enhancements + * + * 52 4/22/99 3:23p Jason + * + * 51 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 50 4/13/99 12:03p Jason + * more memory savings for dedicated server + * + * 49 4/12/99 3:04p Jason + * changes for low memory + * + * 48 4/09/99 7:04p Jason + * changed some texture defines + * + * 47 4/01/99 1:21p Jason + * don't resize huge textures + * + * 46 3/30/99 6:01p Jason + * added new procedural effect + * + * 45 2/12/99 3:37p Jason + * added client-side interpolation...its not fully debugged though. + * + * 44 2/10/99 3:38p Jason + * table file filter fixups + * + * 43 1/29/99 6:29p Jason + * first pass at adding bumpmaps + * + * 42 1/22/99 3:59p Jason + * added 256x256 textures to help with terrain skies + * + * 41 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 40 1/18/99 11:11a Jason + * made vclips be specular + * + * 39 11/30/98 5:50p Jason + * added 4444 bitmap support + * + * 38 10/21/98 9:47a Kevin + * + * 37 10/20/98 6:22p Jason + * added superlowmem mode + * + * 36 10/14/98 5:50p Jason + * added lowmem modes for quartering textures + * + * 35 10/13/98 1:26p Jason + * made new detail level settings work + * + * 34 10/08/98 2:27p Jason + * sped up table file loading + * + * 33 10/07/98 5:03p Jason + * more options for textures + * + * 32 10/01/98 2:47p Jason + * added timed procedurals + * + * 31 8/13/98 11:57a Jason + * added water procedurals + * + * 30 7/31/98 10:50a Jason + * fixed memory leak + * + * 29 7/16/98 12:41p Jason + * fixed dumb bug + * + * 28 7/14/98 6:50p Jason + * made procedurals save out to the table file + * + * 27 7/09/98 7:30p Jason + * more procedural stuff + * + * 26 7/07/98 4:12p Jason + * checkin for procedurals + * + * 25 7/06/98 12:55p Jason + * added first pass at procedurals + * + * 24 6/10/98 12:21p Matt + * Revamped system that controls what textures are displayed on the + * texture tab. Should work correctly now, and will now save the settings + * to the registry. Also, textures now default to no type set, not all + * types set. + * + * 23 5/20/98 5:44p Jason + * incremental checkin for bumpmapping + * + * 22 5/20/98 1:08p Jason + * more stuff for adding bumpmaps + * + * 21 5/14/98 2:38p Jason + * made temp solution for vclip residency problem + * + * 20 5/14/98 2:17p Jason + * more changes to be memory friendly + * + * 19 3/17/98 4:52p Jason + * fixed compiler warning + * + * 17 3/17/98 4:33p Jason + * Added size changing to bitmaps/textures + * + * 16 2/13/98 12:40p Jason + * added functions to touch all data in a level when the level is loaded + * + * 15 2/11/98 4:58p Jason + * fixed some memory problems with resizing and mipping + * + * 14 2/10/98 11:38a Matt + * Ripped out the obsolete merge stuff + * + * 13 12/19/97 6:07p Jason + * sped up bitmap loading + * + * 12 12/19/97 2:46p Jason + * implemented bitmap paging routines + * + * 11 10/30/97 12:02p Jason + * fixed an error checking bug + * + * 10 10/21/97 12:06p Jason + * added mprintf warning to resizing of textures + * + * 9 10/20/97 6:20p Mark + * FROM JASON: Fixed dumb no-name bug + * + * 8 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 7 10/13/97 3:56p Jason + * made a better 3d bitmap system + * + * 6 9/12/97 4:00p Matt + * If a game texture is not used, return the handle for the error bitmap + * + * 5 8/19/97 1:20p Jason + * set reflectivity to .5 by default + * + * 4 8/08/97 12:58p Jason + * got texture sliding and pingponging working + * + * 3 8/04/97 3:28p Jason + * added alpha blending per texture + * + * 21 6/17/97 2:53p Matt + * Allow 128x128 tmap2s over 64x64 tmap1s by scaling up the bottom + * texture. This is only temporary, because I think we'll be totally + * revising the tmap2 system. + * + * 20 6/06/97 3:57p Jason + * implemented changes for small textures and automatic tmap2 recognition + * + * 19 6/03/97 3:28p Jason + * fixed texturing problems + * + * 18 5/27/97 4:39p Jason + * changes to support OpenGL + * + * 17 5/23/97 7:07p Matt + * Added code, data, & dialogs to keep track of what a texture is used for + * (mine, terrain, object) so we can display in the texture dialog only + * the ones we're interested in. + * + * 16 5/19/97 5:10p Jason + * changes for our new abstracted renderer code + * + * 15 5/13/97 2:24p Jason + * fixed a couple of animating texture bugs + * + * 14 5/13/97 12:45p Matt + * Fixed bug in rotated cases of 16-bit texmerge + * + * 13 5/12/97 11:41a Jason + * made game work (default) to 16bit no mip maps mode + * Saves us alot of memory + * + * 12 5/12/97 11:19a Matt + * Worked on 16-bit merge code, which is still broken + * + * 11 5/02/97 7:28p Matt + * Got tmap2 system working, though I think the merge for 16-bit textures + * probably doesn't work + * + * 10 4/28/97 6:36p Jason + * fixed a bug with vclips + * + * 9 4/25/97 3:59p Jason + * fixed a bug that my last checkin caused + * + * 8 4/25/97 3:31p Jason + * implemented better memory management for vclips and bitmaps + * + * 7 4/21/97 5:29p Jason + * got animating textures to work on polygonal objects + * + * 6 2/19/97 3:23p Jason + * added hooks to import animated iffs + * + * 5 2/19/97 11:37a Jason + * take gametime into consideration when animating a vclip + * + * 4 2/11/97 3:21p Jason + * added MakeTexture8Bit function + * + * 3 1/25/97 5:55p Jason + * changes to support new 8bit palettized textures and vclips + * + * 2 1/16/97 3:01p Jason + * moved all Gametexture management stuff to the game level instead of in + * the texmap libarry. + * + * $NoKeywords: $ + */ + + +#include "grdefs.h" +#include "pstypes.h" +#include "gametexture.h" +#include "mono.h" +#include "bitmap.h" +#include "pserror.h" +#include "vclip.h" +#include "texture.h" +#include "game.h" +#include "bumpmap.h" +#include +#include +#include "procedurals.h" +#include "ddio.h" +#include "config.h" +#include "args.h" +#include "mem.h" + +int Num_textures=0; +texture GameTextures[MAX_TEXTURES]; + +extern bool Mem_superlow_memory_mode; + +int Total_memory_saved=0; + +void FreeAllTextures () +{ + for (int i=0;iused); + + int t; + int retval=-1; + + for (t=0;tnum_frames && retval==-1;t++) + if ((!stricmp(GameBitmaps[vc->frames[t]].name,name))) + retval=i; + + /*if (not_res) + { + FreeVClipResidency (GameTextures[i].bm_handle); + }*/ + + if (retval!=-1) + return retval; + } + else + { + if ((!stricmp(GameBitmaps[GameTextures[i].bm_handle].name,name))) + return i; + } + } + } + + return -1; +} + +// Allocates memory for a procedural that is attached to a texture +int AllocateProceduralForTexture (int handle) +{ + int w,h; + + if (GameTextures[handle].flags & TF_TEXTURE_64) + { + w=h=64; + } + else if (GameTextures[handle].flags & TF_TEXTURE_32) + { + w=h=32; + } + else + { + w=h=128; + } + + GameTextures[handle].procedural=(proc_struct *)mem_malloc (sizeof(proc_struct)); + ASSERT (GameTextures[handle].procedural); + + GameTextures[handle].procedural->proc1=NULL; + GameTextures[handle].procedural->proc2=NULL; + + GameTextures[handle].procedural->last_evaluation_time=0; + GameTextures[handle].procedural->osc_time=0; + GameTextures[handle].procedural->osc_value=8; + + GameTextures[handle].procedural->procedural_bitmap=bm_AllocBitmap (w,h,0); + ASSERT (GameTextures[handle].procedural->procedural_bitmap>=0); + + GameTextures[handle].procedural->memory_type=PROC_MEMORY_TYPE_NONE; + + GameTextures[handle].procedural->palette=(ushort *)mem_malloc(256*2); + + GameTextures[handle].procedural->last_procedural_frame=-1; + GameTextures[handle].procedural->heat=128; + + GameTextures[handle].procedural->dynamic_proc_elements=-1; + GameTextures[handle].procedural->static_proc_elements=NULL; + + GameTextures[handle].procedural->num_static_elements=0; + + GameTextures[handle].procedural->light=1; + GameTextures[handle].procedural->thickness=4; + + memcpy (GameTextures[handle].procedural->palette,DefaultProcPalette,256*2); + + return 1; + +} + +// Allocates the memory needed for static elements for a procedural texture +void AllocateStaticProceduralsForTexture (int handle,int num_elements) +{ + GameTextures[handle].procedural->static_proc_elements=(static_proc_element *)mem_malloc (sizeof(static_proc_element)*num_elements); + ASSERT (GameTextures[handle].procedural->static_proc_elements); +} + +// Frees the memory used by static procedural elements +void FreeStaticProceduralsForTexture (int handle) +{ + if (GameTextures[handle].procedural->static_proc_elements) + mem_free (GameTextures[handle].procedural->static_proc_elements); + GameTextures[handle].procedural->static_proc_elements=NULL; +} + + + +// Given a texture handle, returns that textures bitmap +// If the texture is animated, returns framenum mod num_of_frames in the animation +// Force is to force the evaluation of a procedural +// Also figures in gametime +int GetTextureBitmap (int handle,int framenum,bool force) +{ + int src_bitmap; + + if (! GameTextures[handle].used) + return 0; + + if (GameTextures[handle].flags & TF_ANIMATED) + { + float cur_frametime; + int int_frame; + texture *tex=&GameTextures[handle]; + PageInVClip(GameTextures[handle].bm_handle); + + vclip *vc=&GameVClips[GameTextures[handle].bm_handle]; + ASSERT (vc->used>=1); + + if (GameTextures[handle].flags & TF_PING_PONG) + { + // Ping pong this texture + + float frametime=tex->speed/vc->num_frames; + cur_frametime=Gametime/frametime; + int_frame=cur_frametime; + int_frame+=framenum; + + int_frame%=(vc->num_frames*2); + if (int_frame>=vc->num_frames) + int_frame=(vc->num_frames-1)-(int_frame%vc->num_frames); + else + int_frame%=vc->num_frames; + src_bitmap=vc->frames[int_frame]; + } + else + { + float frametime=tex->speed/vc->num_frames; + cur_frametime=Gametime/frametime; + int_frame=cur_frametime; + int_frame+=framenum; + src_bitmap=vc->frames[int_frame % vc->num_frames]; + } + } + else + { + src_bitmap=GameTextures[handle].bm_handle; + } + + if (GameTextures[handle].flags & TF_PROCEDURAL) // Do a procedural + { + int do_eval=1; + + if (GameTextures[handle].procedural==NULL) + AllocateProceduralForTexture (handle); + + if (GameTextures[handle].procedural->last_procedural_frame==FrameCount) + do_eval=0; + if (timer_GetTime()last_evaluation_time+GameTextures[handle].procedural->evaluation_time) + do_eval=0; + + if (!force && !Detail_settings.Procedurals_enabled) + { + if (timer_GetTime()last_evaluation_time+10.0) + do_eval=0; + } + + if (do_eval) + { + EvaluateProcedural (handle); + GameTextures[handle].procedural->last_procedural_frame=FrameCount; + GameTextures[handle].procedural->last_evaluation_time=timer_GetTime(); + src_bitmap=GameTextures[handle].procedural->procedural_bitmap; + GameBitmaps[src_bitmap].flags|=BF_CHANGED; + } + else + src_bitmap=GameTextures[handle].procedural->procedural_bitmap; + } + + return src_bitmap; + +} + +// Given a filename, loads either the bitmap or vclip found in that file. If type +// is not NULL, sets it to 1 if file is animation, otherwise sets it to zero +int LoadTextureImage (char *filename,int *type,int texture_size,int mipped,int pageable,int format) +{ + int anim=0,bm_handle; + char extension[10]; + int w,h; + + int len=strlen (filename); + + if (len<4) + return -1; + + strncpy (extension,&filename[len-3],5); + + + if ((!strnicmp ("oaf",extension,3)) || (!strnicmp ("ifl",extension,3)) || (!strnicmp ("abm",extension,3))) + anim=1; + + if (type!=NULL) + *type=anim; + + if (anim) + bm_handle=AllocLoadVClip (IGNORE_TABLE(filename),texture_size,mipped,pageable,format); + else + { + if (pageable) + bm_handle=bm_AllocLoadFileNoMemBitmap (IGNORE_TABLE(filename),mipped,format); + else + bm_handle=bm_AllocLoadFileBitmap (IGNORE_TABLE(filename),mipped,format); + + if (bm_handle<1) + return -1; + + if (texture_size==NORMAL_TEXTURE) + { + w=TEXTURE_WIDTH; + h=TEXTURE_HEIGHT; + } + else if (texture_size==SMALL_TEXTURE) + { + // Make small textures a quarter of the size of normal textures + w=TEXTURE_WIDTH/2; + h=TEXTURE_HEIGHT/2; + } + else if (texture_size==TINY_TEXTURE) + { + // Make these tinys an eigth of the size of normal textures + w=TEXTURE_WIDTH/4; + h=TEXTURE_HEIGHT/4; + } + else if (texture_size==HUGE_TEXTURE) + { + // Make these tinys an eigth of the size of normal textures + w=TEXTURE_WIDTH*2; + h=TEXTURE_HEIGHT*2; + } + else + return bm_handle; + + // If differing size, resize! + if (!pageable && (w!=bm_w(bm_handle,0) || h!=bm_h(bm_handle,0))) + { + int dest_bm; + + mprintf ((0,"WARNING: Resizing bitmap %s from %d x %d to %d x %d!\n",GameBitmaps[bm_handle].name,bm_w(bm_handle,0),bm_h(bm_handle,0),w,h)); + + dest_bm=bm_AllocBitmap (w,h,mipped*((w*h*2)/3)); + ASSERT (dest_bm>=0); + + if (mipped) + GameBitmaps[dest_bm].flags |=BF_MIPMAPPED; + GameBitmaps[dest_bm].format=format; + + bm_ScaleBitmapToBitmap (dest_bm,bm_handle); + strcpy (GameBitmaps[dest_bm].name,GameBitmaps[bm_handle].name); + bm_FreeBitmap (bm_handle); + + bm_handle=dest_bm; + + } + } + + return bm_handle; +} + +// Goes through and marks a texture as a tmap2 if its bitmap(s) have transparency +bool CheckIfTextureIsTmap2 (int texnum) +{ + if (GameTextures[texnum].flags & TF_ANIMATED) + { + vclip *vc=&GameVClips[GameTextures[texnum].bm_handle]; + PageInVClip (GameTextures[texnum].bm_handle); + + ASSERT (vc->used); + for (int i=0;inum_frames;i++) + { + if (bm_SetBitmapIfTransparent (vc->frames[i])) + { + GameTextures[texnum].flags |=TF_TMAP2; + return 1; + } + } + } + else + { + if (bm_SetBitmapIfTransparent (GameTextures[texnum].bm_handle)) + { + GameTextures[texnum].flags |=TF_TMAP2; + return 1; + } + } + + return 0; +} + + +extern int Low_vidmem; +// Pages a texture into memory, optionally resizing it if in low mem mode +// Doesn't resize if resize is false +void PageInTexture (int n,bool resize) +{ + static int super_low_mem=-1; + +#ifndef MACINTOSH + if (super_low_mem==-1) + { + if (Mem_superlow_memory_mode || FindArg ("-dedicated")) + super_low_mem=1; + else + super_low_mem=0; + + } +#endif + if (GameTextures[n].used==0) + return; + + texture *texp=&GameTextures[n]; + + bm_data (texp->bm_handle,0); + + #ifndef EDITOR +#ifdef MACINTOSH + if (resize==true && (Render_state.cur_texture_quality <= 1 || Low_vidmem)) +#else + if (resize==true && (Mem_low_memory_mode || Low_vidmem)) +#endif + { + if (!(texp->flags & (TF_TEXTURE_32|TF_TEXTURE_256)) && !(texp->flags & TF_PROCEDURAL)) + { + int bm_handle=texp->bm_handle; + int w=TEXTURE_WIDTH,h=TEXTURE_HEIGHT; + + if (texp->flags & TF_TEXTURE_64) + { + w/=2; + h/=2; + } + + w/=2; + h/=2; + +#ifdef MACINTOSH + if(Render_state.cur_texture_quality == 0) +#else + if (super_low_mem) +#endif + { + if (w!=32 || h!=32) + { + w=32; + h=32; + } + } + + if ( (w!=bm_w(bm_handle,0) || h!=bm_h(bm_handle,0))) + { + int dest_bm; + int mipped=0; + int saved=0; + + if (texp->flags & TF_TEXTURE_64) + saved=6144; + else + saved=24576; + + + if (bm_mipped(bm_handle)) + mipped=1; + + if (mipped) + saved+=(saved/3); + + Total_memory_saved+=saved; + + mprintf ((0,"Low mem: Resizing bitmap %s to %d x %d!\n",GameBitmaps[bm_handle].name,w,h)); + mprintf ((0,"Total memory saved=%d\n",Total_memory_saved)); + + dest_bm=bm_AllocBitmap (w,h,mipped*((w*h*2)/3)); + ASSERT (dest_bm>=0); + if (mipped) + GameBitmaps[dest_bm].flags |=BF_MIPMAPPED; + + GameBitmaps[dest_bm].format=GameBitmaps[bm_handle].format; + + bm_ScaleBitmapToBitmap (dest_bm,bm_handle); + strcpy (GameBitmaps[dest_bm].name,GameBitmaps[bm_handle].name); + bm_FreeBitmap (bm_handle); + + texp->bm_handle=dest_bm; + } + } + } + #endif +} + +// Pages in this particular texture +void TouchTexture (int n) +{ + if (GameTextures[n].flags & TF_ANIMATED) + { + PageInVClip (GameTextures[n].bm_handle); + } + else + PageInTexture (n,true); +} + +// Frees all the bumpmaps associated with a texture +void FreeTextureBumpmaps (int tex_handle) +{ + ASSERT (GameTextures[tex_handle].used>0); + + if (GameTextures[tex_handle].bumpmap!=-1) + { + bump_FreeBumpmap (GameTextures[tex_handle].bumpmap); + GameTextures[tex_handle].bumpmap=-1; + } +} + +// Builds the bumpmaps for the past in texture +void BuildTextureBumpmaps (int texhandle) +{ + int i,t; + sbyte *buffer; + + if (GameTextures[texhandle].flags & TF_ANIMATED) + return; //No bumps for animated textures + + mprintf ((0,"Calculating bumpmap for texture named %s.\n",GameTextures[texhandle].name)); + + // Make sure there is no bump already + ASSERT (GameTextures[texhandle].bumpmap==-1); + + // Get width and height + int w=bm_w(GameTextures[texhandle].bm_handle,0); + int h=bm_h(GameTextures[texhandle].bm_handle,0); + + + // Allocate a bumpmap + int bump=bump_AllocBumpmap(w,h); + ASSERT (bump!=-1); + GameTextures[texhandle].bumpmap=bump; + + // allocate memory for our buffer + buffer=(sbyte *)mem_malloc (w*h); + ASSERT (buffer); + + ushort *src_data=(ushort *)bm_data(GameTextures[texhandle].bm_handle,0); + + // create the grayscale + for (i = 0; i < h; i++) + { + for (t = 0; t < w; t++) + { + ushort color = src_data[i*w+t]; + + int red=((color >> 10) & 0x1f)<<3; + int green=((color >> 5) & 0x1f)<<3; + int blue=((color) & 0x1f)<<3; + + buffer[i*w+t] = (sbyte) (0.29 * red + 0.60 * green + 0.11 * blue); + } + } + + ushort *dest_data=(ushort *)bump_data(bump); + + for(i = 0; i < h; i++ ) + { + sbyte *pDst = (sbyte *)(dest_data + i*w); + for(t = 0; t < w; t++ ) + { + int v00, v01, v10; + + v00 = buffer[(i*w+t)]; // Get current pixel. *3 for 24 bits src + + if( t == w-1 ) // Special case for last column + v01 = buffer[(i*w+t)]; // Get pixel to the right + else + v01 = buffer[(i*h+t+1)]; // Get pixel to the right + + if( i == h-1 ) // Special case for last row + v10 = buffer[i*w+t]; // Get pixel one line below + else + v10 = buffer[((i+1)*w)+t]; // Get pixel one line below + + int iDu = v00 - v01; // The delta-u bump value + int iDv = v00 - v10; // The delta-v bump value + *pDst++ = (sbyte)iDu; + *pDst++ = (sbyte)iDv; + } + } + + mem_free(buffer); +} + + + +// Frees all the procedural memory for a texture +void FreeProceduralForTexture (int n) +{ + if (GameTextures[n].procedural!=NULL) + { + FreeStaticProceduralsForTexture (n); + mem_free (GameTextures[n].procedural->proc1); + mem_free (GameTextures[n].procedural->proc2); + bm_FreeBitmap (GameTextures[n].procedural->procedural_bitmap); + mem_free (GameTextures[n].procedural->palette); + mem_free (GameTextures[n].procedural); + + GameTextures[n].procedural=NULL; + } +} + + +// Frees a texture that is being used and +// Decrements number of textures in use +void FreeTexture (int n) +{ + ASSERT (GameTextures[n].used); + + FreeTextureBumpmaps (n); + + GameTextures[n].used=0; + GameTextures[n].name[0]=0; + Num_textures--; + + FreeProceduralForTexture (n); +} + +// Given current index, gets index of next texture in use +int GetNextTexture (int n) +{ + int i; + + if (Num_textures==0) + return -1; + + for (i=n+1;i=0;i--) + { + if (GameTextures[i].used) + return i; + } + for (i=MAX_TEXTURES-1;i>n;i--) + { + if (GameTextures[i].used) + return i; + } + + return n; +} diff --git a/Descent3/gametexture.h b/Descent3/gametexture.h new file mode 100644 index 000000000..5aea1c37d --- /dev/null +++ b/Descent3/gametexture.h @@ -0,0 +1,359 @@ +/* + * $Logfile: /DescentIII/Main/gametexture.h $ + * $Revision: 47 $ + * $Date: 9/13/01 2:30p $ + * $Author: Matt $ + * + * Header for gametexture.cpp + * + * $Log: /DescentIII/Main/gametexture.h $ + * + * 47 9/13/01 2:30p Matt + * Increased MAX_TEXTURES from 2800 to 3100. + * + * 46 3/21/00 1:17p Matt + * Increased MAX_TEXTURES to 2800 for PC and Mac, so add-on developers + * could add more of their own textures. + * + * 45 3/20/00 12:09p Matt + * Merge of Duane's post-1.3 changes. + * Lower MAX_TEXTURES for Mac (Mac only) + * + * 44 10/17/99 6:06p Chris + * We ran out of texture handles... + * + * 43 5/05/99 5:03a Gwar + * renamed ned_GameTextures array to GameTextures in new editor to make + * game code happy; 3D texture view still does not display textures + * + * 42 4/30/99 12:56p Kevin + * Lowered values for MAX_SOUNDS, MAX_ROOMS, MAX_TEXTURES and MAX_OBJIDS. + * Talk to me before changing any of these again. + * + * 41 4/20/99 9:26p Matt + * Added breakable flag for glass + * + * 40 4/09/99 7:04p Jason + * changed some texture defines + * + * 39 3/30/99 6:06p Jason + * added new procedural effect + * + * 38 3/10/99 7:12p Jason + * added smooth specular shading for curved surfaces + * + * 37 2/20/99 2:17p Matt + * Added texture sounds + * + * 36 1/29/99 6:29p Jason + * first pass at adding bumpmaps + * + * 35 1/26/99 4:04p Jason + * added some more texture flags + * + * 34 1/22/99 3:59p Jason + * added 256x256 textures to help with terrain skies + * + * 33 12/23/98 4:59p Jason + * some new texture features added + * + * 32 11/30/98 5:50p Jason + * added 4444 bitmap support + * + * 31 10/07/98 5:03p Jason + * more options for textures + * + * 30 10/01/98 2:47p Jason + * added timed procedurals + * + * 29 8/31/98 1:35p Keneta + * Made some use count unsigned shorts + * + * 28 8/13/98 11:57a Jason + * added water procedurals + * + * 27 7/14/98 6:50p Jason + * made procedurals save out to the table file + * + * 26 7/09/98 7:30p Jason + * more procedural stuff + * + * 25 7/07/98 4:12p Jason + * checkin for procedurals + * + * 24 7/06/98 12:55p Jason + * added first pass at procedurals + * + * 23 6/10/98 12:21p Matt + * Revamped system that controls what textures are displayed on the + * texture tab. Should work correctly now, and will now save the settings + * to the registry. Also, textures now default to no type set, not all + * types set. + * + * 22 6/02/98 6:03p Jason + * added specular lightmaps + * + * 21 5/20/98 5:44p Jason + * incremental checkin for bumpmapping + * + * 20 5/20/98 1:08p Jason + * more stuff for adding bumpmaps + * + * 19 5/19/98 3:19p Jason + * cleaned up some texture flags + * + * 18 5/03/98 10:24p Matt + * Renamed some unused constants + * + * 17 4/15/98 12:22p Jason + * lots of miscellaneous stuff pertaining to lighting and vis effects + * + * 16 4/06/98 4:41p Jason + * added texture alpha change + * + * 15 3/17/98 4:33p Jason + * Added size changing to bitmaps/textures + * + * 14 2/13/98 12:40p Jason + * added functions to touch all data in a level when the level is loaded + * + * 13 2/10/98 11:38a Matt + * Ripped out the obsolete merge stuff + * + * 12 2/03/98 3:15p Jason + * added the ability to specify saturation textures for models + * + * 11 1/13/98 3:09p Jason + * added glow effect for engines + * + * 10 1/06/98 2:58p Mark + * upped texture limit to 3000. + * + * 9 12/19/97 2:46p Jason + * implemented bitmap paging routines + * + * 8 11/21/97 2:02p Jason + * made light textures on model render that model face fully bright + * + * 7 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 6 8/19/97 1:57a Jason + * made rgb lighting take floats + * + * 5 8/12/97 1:10a Jason + * added code to support radiosity lighting + * + * 4 8/08/97 12:59p Jason + * got texture sliding and pingponging working + * + * 3 8/05/97 4:27p Chris + * Added two flags for fly thru and fly by + * + * 2 8/04/97 3:28p Jason + * added alpha blending per texture + * + * 11 6/24/97 12:45p Jason + * checked in for safety + * + * 9 6/06/97 3:57p Jason + * implemented changes for small textures and automatic tmap2 recognition + * + * 8 5/30/97 5:46p Jason + * added colored light functionality on a vertex level + * + * 7 5/23/97 7:07p Matt + * Added code, data, & dialogs to keep track of what a texture is used for + * (mine, terrain, object) so we can display in the texture dialog only + * the ones we're interested in. + * + * $NoKeywords: $ + */ + +#ifndef GAMETEXTURE_H +#define GAMETEXTURE_H + +#ifdef NEWEDITOR /* only include tablefile header (manage stuff for NEWEDITOR) */ +#include "..\neweditor\ned_TableFile.h" +#include "..\neweditor\ned_GameTexture.h" +#else + +#include "manage.h" + +#define TF_VOLATILE 1 +#define TF_WATER (1<<1) +#define TF_METAL (1<<2) // Shines like metal +#define TF_MARBLE (1<<3) // Shines like marble +#define TF_PLASTIC (1<<4) // Shines like plastic +#define TF_FORCEFIELD (1<<5) +#define TF_ANIMATED (1<<6) +#define TF_DESTROYABLE (1<<7) +#define TF_EFFECT (1<<8) +#define TF_HUD_COCKPIT (1<<9) +#define TF_MINE (1<<10) +#define TF_TERRAIN (1<<11) +#define TF_OBJECT (1<<12) +#define TF_TEXTURE_64 (1<<13) +#define TF_TMAP2 (1<<14) +#define TF_TEXTURE_32 (1<<15) +#define TF_FLY_THRU (1<<16) +#define TF_PASS_THRU (1<<17) +#define TF_PING_PONG (1<<18) +#define TF_LIGHT (1<<19) +#define TF_BREAKABLE (1<<20) // Breakable (as in glass) +#define TF_SATURATE (1<<21) +#define TF_ALPHA (1<<22) +#define TF_DONTUSE (1<<23) +#define TF_PROCEDURAL (1<<24) +#define TF_WATER_PROCEDURAL (1<<25) +#define TF_FORCE_LIGHTMAP (1<<26) +#define TF_SATURATE_LIGHTMAP (1<<27) +#define TF_TEXTURE_256 (1<<28) +#define TF_LAVA (1<<29) +#define TF_RUBBLE (1<<30) +#define TF_SMOOTH_SPECULAR (1<<31) + +#define TF_TEXTURE_TYPES (TF_MINE+TF_TERRAIN+TF_OBJECT+TF_EFFECT+TF_HUD_COCKPIT+TF_LIGHT) + +#define TF_SPECULAR (TF_METAL+TF_MARBLE|TF_PLASTIC) + + +#define NOT_TEXTURE 0 +#define NORMAL_TEXTURE 1 // a normal size texture +#define SMALL_TEXTURE 2 // 1/4 of a normal texture +#define TINY_TEXTURE 3 // 1/8 of a normal texture +#define HUGE_TEXTURE 4 // Double the size of a normal texture + +#define MAX_TEXTURES 3100 + +#define PROC_MEMORY_TYPE_NONE 0 +#define PROC_MEMORY_TYPE_FIRE 1 +#define PROC_MEMORY_TYPE_WATER 2 + +typedef struct +{ + ubyte type; + + ubyte frequency; + ubyte speed; + ubyte color; + ubyte size; + + ubyte x1,y1,x2,y2; + +} static_proc_element; + + + + +typedef struct +{ + short dynamic_proc_elements; // list of dynamic procedural texture elements + void *proc1; // pointer for procedural page + void *proc2; // back page of procedural + short procedural_bitmap; // handle to the bitmap holding the finished procedural + + ushort *palette; + static_proc_element *static_proc_elements; + ushort num_static_elements; + + ubyte memory_type; + + ubyte heat; + ubyte thickness; + ubyte light; + + float last_evaluation_time; + float evaluation_time; + float osc_time; + ubyte osc_value; + + int last_procedural_frame; // last frame a procedural was calculated for this texture +} proc_struct; + +typedef struct +{ + char name[PAGENAME_LEN]; // this textures name + int flags; // values defined above + int bm_handle; // handle which shows what this texture looks like + int destroy_handle; // handle which denotes the destroyed image + + int damage; + float reflectivity; + float r,g,b; // colored lighting (0 to 100%) + + float slide_u,slide_v; // How many times this texture slides during a second + float alpha; // alpha value (from 0 to 1) + float speed; // how fast this texture animates + + proc_struct *procedural; + + int sound; // The sound this texture makes + float sound_volume; // The volume for this texture's sound + + short bumpmap; // The bumpmap for this texture, or -1 if there is none + ubyte corona_type; // what type of corona this thing uses + ubyte used; // is this texture free to be allocated? + +} texture; + + +extern texture GameTextures[MAX_TEXTURES]; +extern int Num_textures; + +// Inits the texture system, returning 1 if successful +int InitTextures(); +void ShutdownTextures(); + +// Set aside a texture for use +int AllocTexture (void); + +// Frees a texture for future use +void FreeTexture (int); + +// Given current index, gets index of next texture in use +int GetNextTexture (int n); + +// Given current index, gets index of prev texture in use +int GetPreviousTexture (int n); + +// Searches thru all textures for a specific name, returns -1 if not found +// or index of texture with name +int FindTextureName (char *name); + +// Searches thru all textures for a bitmap of a specific name, returns -1 if not found +// or index of texture with name +int FindTextureBitmapName (char *name); + +// Given a texture handle, returns that textures bitmap +// If the texture is animated, returns framenum mod num_of_frames in the animation +int GetTextureBitmap (int handle,int framenum,bool force=false); + +// Given a filename, loads either the bitmap or vclip found in that file. If type +// is not NULL, sets it to 1 if file is animation, otherwise sets it to zero +int LoadTextureImage (char *filename,int *type,int texture_size,int mipped,int pageable=0,int format=0); + +// Goes through and marks a texture as a tmap2 if its bitmap(s) have transparency +bool CheckIfTextureIsTmap2 (int texnum); + +// Touches a texture, makes sure its in memory +void TouchTexture (int n); + +// Builds the bumpmaps for the texture +void BuildTextureBumpmaps (int texhandle); + +// Allocates the memory needed for static elements for a procedural texture +void AllocateStaticProceduralsForTexture (int handle,int num_elements); + +// Frees the memory used by static procedural elements +void FreeStaticProceduralsForTexture (int handle); + +// Frees all the procedural memory for a texture +void FreeProceduralForTexture (int n); + +// Allocates memory for a procedural that is attached to a texture +int AllocateProceduralForTexture (int handle); + +#endif + +#endif \ No newline at end of file diff --git a/Descent3/gauges.cpp b/Descent3/gauges.cpp new file mode 100644 index 000000000..cfb34267d --- /dev/null +++ b/Descent3/gauges.cpp @@ -0,0 +1,1028 @@ +/* + * $Logfile: /DescentIII/main/gauges.cpp $ + * $Revision: 47 $ + * $Date: 4/19/00 5:13p $ + * $Author: Matt $ + * + * Gauge rendering + * + * $Log: /DescentIII/main/gauges.cpp $ + * + * 47 4/19/00 5:13p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * Removed Mac OpenGL hack + * + * 46 10/21/99 6:37p Matt + * Mac merge + * This change is mostly setting & clearing the variable + * use_opengl_1555_format around some blocks of code. Duane says this + * won't affect the Windows version. (BTW, I think it would have been + * cleaner to use a rend_set() function to do this.) + * + * 45 4/24/99 10:38p Samir + * cleaned up hud text problems in 'small mode' + * + * 44 4/20/99 11:47a Samir + * shrinking hud fixes. + * + * 43 3/04/99 8:07p Samir + * render gauge text at correct scale. + * + * 42 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 41 10/23/98 12:52p Samir + * fixed bug in secondary monitors, projecting wrong point. + * + * 40 10/20/98 12:42p Matt + * Made the small views work on the cockpit. + * + * 39 10/13/98 12:56p Matt + * Finished (hopefully) with the ammo system. Added support for napalm + * fuel. + * + * 38 8/25/98 7:07p Samir + * display ammo in primary monitor. + * + * 37 8/25/98 12:22p Jason + * more changes for cockpits + * + * 36 8/24/98 3:25p Samir + * took out reordering. + * + * 35 8/24/98 3:04p Samir + * added reordering of monitor verts. + * + * 34 6/24/98 7:37p Samir + * fixed energy/shield gauges (must still do invulnerability!) + * + * 33 6/16/98 10:38a Jeff + * localization, strings pulled out to stringtable.h and d3.str + * + * 32 5/26/98 5:05p Samir + * shield frames now contained in hud structure and share common graphics + * between gauges and hud. + * + * 31 5/25/98 8:30p Samir + * fixed bug when weapon gauges drawn in multiplayer. + * + * 30 5/25/98 6:38p Matt + * Added needed include. + * + * 29 5/22/98 6:26p Samir + * moved text over a bit on left monitor. + * + * 28 5/18/98 4:52p Samir + * fixed hud image for weapons. + * + * 27 5/15/98 5:36p Samir + * use correct font scale for lores screens. + * + * 26 5/05/98 6:28p Samir + * gauges should render properly when cockpit is resized. + * + * 25 4/24/98 8:03a Samir + * draw weapon names using short names, and pass alpha array to + * drawgaugemonitor. + * + * 24 3/27/98 11:59a Samir + * moved weapon hud text from hud to weapon monitor gauges. + * + * 23 3/22/98 5:03p Samir + * updated cockpit. + * + * 22 3/20/98 8:23p Samir + * Started implementing images. + * + * 21 3/18/98 6:24p Samir + * Implemented monitor system. + * + * 20 3/17/98 2:40p Samir + * reorg of cockpit/gauges and hud systems. + * + * 19 3/16/98 3:30p Samir + * incremental checkin. + * + * 18 2/17/98 3:46p Matt + * Revamped weapon system and got sounds for spray and fusion weapons + * working. Still need to implements getting continuous & cutoff sounds + * from player ship. + * + * 17 2/11/98 4:54p Jeff + * Moved the inventory into the Player struct + * + * 16 2/09/98 4:58p Jeff + * Connected inventory to hud + * + * 15 2/03/98 12:13p Samir + * Font shadowing support in grtext. + * + * 14 1/28/98 1:11p Jason + * took shields field out of Player struct + * + * 13 1/26/98 2:38p Samir + * Assert alpha is opaque for hud font. + * + * 12 1/23/98 12:07p Jason + * made framerate display toggleable + * + * 11 1/12/98 5:40p Samir + * Gauges placed differently. + * + * 10 1/12/98 5:23p Samir + * Use new hud font for gauges. + * + * 9 12/29/97 5:47p Samir + * Uses new text system and took out references to Game_viewport. + * + * 8 11/16/97 6:55p Samir + * Initiated 'init' flag for gauge structure. + * + * 7 11/14/97 5:29p Samir + * Now we pass four world coordinates of the 4 vertex monitor to a gauge + * rendering function. + * + * 6 11/11/97 1:25p Samir + * Added modified flag to tGauge and cleaned up gauge renderering a + * little. + * + * 5 11/04/97 6:23p Samir + * New Gauge interface. + * + * 4 10/29/97 12:33p Samir + * Framework for RenderHudGauges + * + * 3 10/28/97 6:36p Samir + * Gauge system for text controls functioning. + * + * 2 10/28/97 12:47p Samir + * Gauges are drawn once per frame from GameLoop to a call to gauges.cpp + * + * 1 10/28/97 12:27p Samir + * Initial revision + * + * $NoKeywords: $ + */ + + +#include "gauges.h" +#include "gamefont.h" +#include "hud.h" +#include "game.h" +#include "renderer.h" +#include "render.h" +#include "player.h" +#include "polymodel.h" +#include "gametexture.h" +#include "bitmap.h" +#include "vclip.h" +#include "Inventory.h" +#include "cockpit.h" +#include "player.h" +#include "ship.h" +#include "3d.h" +#include "weapon.h" +#include "stringtable.h" + +////////////////////////////////////////////////////////////////////////////// + +typedef struct tGauge +{ + poly_model *cockpit; // cockpit model of gauge + bsp_info *monitor; // monitor in cockpit. + int first_vert; + bool functional; // is this gauge working? + bool just_init; // gauge just initialized? + ushort mask; // mask of stat for gauge + int state; // gauge dependent state data. + union { // data for gauge. + int i; + float f; + } + data; + g3Point pts[4]; // points of monitor in view coordinates. (may be cached from last frame) +} +tGauge; + +// which monitors correspond to what faces on the cockpit model. +#define PRIMARY_MONITOR SOF_MONITOR1 +#define SHIP_MONITOR SOF_MONITOR2 +#define SECONDARY_MONITOR SOF_MONITOR3 +#define ENERGY1_MONITOR SOF_MONITOR4 +#define ENERGY2_MONITOR SOF_MONITOR5 +#define SHIELD_MONITOR SOF_MONITOR6 +#define AFTERBURN_MONITOR SOF_MONITOR7 + +//Hack vars for turning off the monitors +bool Disable_primary_monitor=0,Disable_secondary_monitor=0; + +////////////////////////////////////////////////////////////////////////////// + +// Available gauges to be rendered. +static tStatMask Gauge_mask; + +// sets for next frame which gauges have been modified. +static tStatMask Gauge_mask_modified; + +// sets for next frame the functional state of each gauge (on/off). +static tGauge Gauge_list[NUM_GAUGES]; + +// position of cockpit +static vector *Render_gauge_pos; + +// matrix of cockpit +static matrix *Render_gauge_matrix; + +// normalized times of cockpit +static float *Render_normalized_times; + +// if gauges are moving in cockpit. +static bool Render_gauge_moving; // gauge is moving, but temporarily off. +static bool Render_gauge_reset; // set this if gauges will be moving, but are still active + + +// loads all shield bitmaps into memory +void FreeShieldFrames(); + +// loads all ship bitmaps into memory +void FreeShipFrames(); + +// projects monitor coordinates to screen coordinates +void RotateMonitorPosition(tGauge *gauge); + +//renders the current inventory item name at the x,y position +void InventoryRenderGauge(int x,int y); + +// renders the primary monitor gauge +void RenderPrimaryMonitor(tGauge *gauge, bool modified); + +// renders the secondary monitor gauge +void RenderSecondaryMonitor(tGauge *gauge, bool modified); + +// renders the shield gauge monitor +void RenderShieldMonitor(tGauge *gauge, bool modified); + +// renders the ship gauge monitor +void RenderShipMonitor(tGauge *gauge, bool modified); + +// renders the ship gauge monitor +void RenderEnergyMonitor(tGauge *gauge, int orient, bool modified); + +// renders the ship gauge monitor +void RenderAfterburnMonitor(tGauge *gauge, bool modified); + +// renders a monitor style quad +void DrawGaugeMonitor(g3Point *pts, int bm, float brightness, float *alphas); + +// renders a square texture onto the screen. +void DrawGaugeQuad(g3Point *pts, int bm, float u0,float v0, float u1,float v1, ubyte alpha, bool saturate); + +// renders a square texture onto the screen. +void DrawGaugeQuad(g3Point *pts, int bm, ubyte alpha=255, bool saturate=false); + +// renders a flat poly onto the screen with given color +void DrawGaugeQuadFlat(g3Point *pts, float r, float g, float b, ubyte alpha); + +// renders a flat poly onto the screen with 4 colors (for each vertex) +void DrawGaugeQuadFlat(g3Point *pts, float *r, float *g, float *b, ubyte alpha); + +// correctly orders monitor vertices based off of UVs +int GetFirstVert (bsp_info *sm); + +// tells what gauge index is the gauge stat item. +inline int GAUGE_INDEX(tStatMask mask) +{ + int i; + + for (i = 0; i < NUM_GAUGES; i++) + if (Gauge_list[i].mask == mask) + return i; + + return -1; +} + + +////////////////////////////////////////////////////////////////////////////// +// Initialization routines + +// initializes cockpit gauges +void InitGauges(ushort gauge_mask) +{ + Gauge_mask = gauge_mask; + Gauge_mask_modified = Gauge_mask; + int gauge=0; + + for (int i = 0; i < NUM_GAUGES; i++) + { + ushort mask = Gauge_mask & (1 << i); + + if (mask == STAT_SHIELDS) { + Gauge_list[gauge].monitor = CockpitGetMonitorSubmodel(SHIELD_MONITOR); + } + if (mask == STAT_PRIMARYLOAD) + Gauge_list[gauge].monitor = CockpitGetMonitorSubmodel(PRIMARY_MONITOR); + else if (mask == STAT_SECONDARYLOAD) + Gauge_list[gauge].monitor = CockpitGetMonitorSubmodel(SECONDARY_MONITOR); + else if (mask == STAT_SHIP) { + Gauge_list[gauge].monitor = CockpitGetMonitorSubmodel(SHIP_MONITOR); + } + else if (mask == STAT_ENERGY) { + // initialize two gauges. one for left, and one for right energy monitor. + Gauge_list[gauge].monitor = CockpitGetMonitorSubmodel(ENERGY1_MONITOR); + Gauge_list[gauge].functional = false; + Gauge_list[gauge].state = 0; + Gauge_list[gauge].cockpit = CockpitGetPolyModel(); + Gauge_list[gauge].just_init = true; + Gauge_list[gauge].mask = mask; + Gauge_list[gauge].first_vert=GetFirstVert (Gauge_list[gauge].monitor); + gauge++; + Gauge_list[gauge].monitor = CockpitGetMonitorSubmodel(ENERGY2_MONITOR); + } + else if (mask == STAT_AFTERBURN) { + Gauge_list[gauge].monitor = CockpitGetMonitorSubmodel(AFTERBURN_MONITOR); + } + + if (mask) { + Gauge_list[gauge].functional = false; + Gauge_list[gauge].state = 0; + Gauge_list[gauge].cockpit = CockpitGetPolyModel(); + Gauge_list[gauge].just_init = true; + Gauge_list[gauge].mask = mask; + Gauge_list[gauge].first_vert=GetFirstVert (Gauge_list[gauge].monitor); + + gauge++; + } + } +} + + +// deinitializes cockpit gauges +void CloseGauges() +{ + for (int i = 0; i < NUM_GAUGES; i++) + { + ushort mask = Gauge_mask & (1 << i); + Gauge_list[i].monitor = NULL; + } + Gauge_mask = 0; + Gauge_mask_modified = 0; +} + + +// renders gauges +void RenderGauges(vector *cockpit_pos, matrix *cockpit_mat, float *normalized_time, bool moving, bool reset) +{ + tGauge *gauge; + float font_aspect_x; + float font_aspect_y; + + Render_gauge_pos = cockpit_pos; + Render_gauge_matrix = cockpit_mat; + Render_normalized_times = normalized_time; + Render_gauge_moving = moving; + Render_gauge_reset = reset; + + grtext_Reset(); + grtext_SetFont(HUD_FONT); +// for lores screens, we use different fonts, so DONT SCALE. + font_aspect_x = (float)Game_window_w/Max_window_w; + font_aspect_y = (float)Game_window_h/Max_window_h; + + if (font_aspect_x <= 0.60) { + grtext_SetFontScale(0.60f); + } + else if (font_aspect_x <= 0.80) { + grtext_SetFontScale(0.80f); + } + else { + grtext_SetFontScale(1.0f); + } + + if (Gauge_mask & STAT_PRIMARYLOAD) { + ASSERT(GAUGE_INDEX(STAT_PRIMARYLOAD) != -1); + gauge = &Gauge_list[GAUGE_INDEX(STAT_PRIMARYLOAD)]; + if (gauge->monitor) { + RotateMonitorPosition(gauge); + RenderPrimaryMonitor(gauge, (Gauge_mask_modified & STAT_PRIMARYLOAD) ? true : false); + } + gauge->just_init = false; + } + if (Gauge_mask & STAT_SECONDARYLOAD) { + ASSERT(GAUGE_INDEX(STAT_SECONDARYLOAD) != -1); + gauge = &Gauge_list[GAUGE_INDEX(STAT_SECONDARYLOAD)]; + if (gauge->monitor) { + RotateMonitorPosition(gauge); + RenderSecondaryMonitor(gauge, (Gauge_mask_modified & STAT_SECONDARYLOAD) ? true : false); + } + gauge->just_init = false; + } + if (Gauge_mask & STAT_SHIELDS) { + ASSERT(GAUGE_INDEX(STAT_SHIELDS) != -1); + gauge = &Gauge_list[GAUGE_INDEX(STAT_SHIELDS)]; + if (gauge->monitor) { + RotateMonitorPosition(gauge); + RenderShieldMonitor(gauge, (Gauge_mask_modified & STAT_SHIELDS) ? true : false); + } + gauge->just_init = false; + } + if (Gauge_mask & STAT_SHIP) { + ASSERT(GAUGE_INDEX(STAT_SHIP) != -1); + gauge = &Gauge_list[GAUGE_INDEX(STAT_SHIP)]; + if (gauge->monitor) { + RotateMonitorPosition(gauge); + RenderShipMonitor(gauge, (Gauge_mask_modified & STAT_SHIP) ? true : false); + } + gauge->just_init = false; + } + if (Gauge_mask & STAT_ENERGY) { + ASSERT(GAUGE_INDEX(STAT_ENERGY) != -1); + gauge = &Gauge_list[GAUGE_INDEX(STAT_ENERGY)]; + if (gauge->monitor) { + RotateMonitorPosition(gauge); + RenderEnergyMonitor(gauge, 0,(Gauge_mask_modified & STAT_ENERGY) ? true : false); + } + gauge->just_init = false; + gauge = &Gauge_list[GAUGE_INDEX(STAT_ENERGY)+1]; + if (gauge->monitor) { + RotateMonitorPosition(gauge); + RenderEnergyMonitor(gauge, 1,(Gauge_mask_modified & STAT_ENERGY) ? true : false); + } + gauge->just_init = false; + } + if (Gauge_mask & STAT_AFTERBURN) { + ASSERT(GAUGE_INDEX(STAT_AFTERBURN) != -1); + gauge = &Gauge_list[GAUGE_INDEX(STAT_AFTERBURN)]; + if (gauge->monitor) { + RotateMonitorPosition(gauge); + RenderAfterburnMonitor(gauge, (Gauge_mask_modified & STAT_ENERGY) ? true : false); + } + gauge->just_init = false; + } + +// render all text + grtext_Flush(); + + Gauge_mask_modified = 0; +} + + +// flags certain gauges to be modified next frame. +void FlagGaugesModified(tStatMask mask_modified) +{ + Gauge_mask_modified |= mask_modified; +} + + +// sets whether the gauges are functional +void FlagGaugesFunctional(tStatMask mask) +{ + ushort i = 0x8000, j = NUM_GAUGES; + + while (i) + { + if (mask & i) + Gauge_list[j-1].functional = true; + i = i >> 1; + j--; + } +} + + +// sets whether the gauges are functional +void FlagGaugesNonfunctional(tStatMask mask) +{ + ushort i = 0x8000, j = NUM_GAUGES; + + while (i) + { + if (mask & i) + Gauge_list[j-1].functional = false; + i = i >> 1; + j--; + } +} + + +// renders the primary monitor gauge +// this will normally render the primary weapon currently selected +// can render another view though. +// renders the primary monitor gauge + +inline int get_weapon_hud_image(int player, int type) +{ + player_weapon *pw = &Players[player].weapon[type]; + weapon *wpn = GetWeaponFromIndex(player, pw->index); + + return (wpn ? wpn->hud_image_handle : BAD_BITMAP_HANDLE); +} + +void RenderPrimaryMonitor(tGauge *gauge, bool modified) +{ + if (Disable_primary_monitor) + return; + + if (gauge->functional) { + float alphas[4]; + alphas[0] = 1.0f; + alphas[1] = 1.0f; + alphas[2] = 1.0f; + alphas[3] = 1.0f; + + int hud_image = get_weapon_hud_image(Player_num, PW_PRIMARY); + + DrawGaugeMonitor(gauge->pts, hud_image, 0.5f, alphas); + + if (!Render_gauge_moving) { + int x1, y; + int index = Players[Player_num].weapon[PW_PRIMARY].index; + ship *ship = &Ships[Players[Player_num].ship_index]; + otype_wb_info *wb = &ship->static_wb[index]; + + gauge->pts[0].p3_flags &= (~PF_PROJECTED); + g3_ProjectPoint(&gauge->pts[0]); + x1 = gauge->pts[0].p3_sx; + y = gauge->pts[0].p3_sy; + + grtext_SetFlags(0); + grtext_SetFancyColor(GR_RGB(0,180,0), GR_RGB(0,180,0), GR_RGB(0,180,0), GR_RGB(0,180,0)); + grtext_Printf(x1+HUD_X(30), y+HUD_Y(10), "%s", TXT(Static_weapon_ckpt_names[index][0])); + grtext_SetFlags(GRTEXTFLAG_SATURATE); + grtext_Printf(x1+HUD_X(30), y+HUD_Y(10), "%s", TXT(Static_weapon_ckpt_names[index][0])); + + if (strlen(TXT(Static_weapon_ckpt_names[Players[Player_num].weapon[PW_PRIMARY].index][1]))) { + grtext_SetFlags(0); + grtext_Printf(x1+HUD_X(35), y+HUD_Y(20), "%s", TXT(Static_weapon_ckpt_names[index][1])); + grtext_SetFlags(GRTEXTFLAG_SATURATE); + grtext_Printf(x1+HUD_X(35), y+HUD_Y(20), "%s", TXT(Static_weapon_ckpt_names[index][1])); + y = y + HUD_Y(10); + } + + if (wb && wb->ammo_usage) { + int ammo = Players[Player_num].weapon_ammo[index]; + grtext_SetFlags(0); + if (ship->fire_flags[index] & SFF_TENTHS) { + grtext_Printf(x1+HUD_X(40), y+HUD_Y(20), "%d.%d", ammo/10, ammo%10); + grtext_SetFlags(GRTEXTFLAG_SATURATE); + grtext_Printf(x1+HUD_X(40), y+HUD_Y(20), "%d.%d", ammo/10, ammo%10); + } + else { + grtext_Printf(x1+HUD_X(40), y+HUD_Y(20), "%d", ammo); + grtext_SetFlags(GRTEXTFLAG_SATURATE); + grtext_Printf(x1+HUD_X(40), y+HUD_Y(20), "%d", ammo); + } + } + } + } +} + + +// renders the secondary monitor gauge +void RenderSecondaryMonitor(tGauge *gauge, bool modified) +{ + if (Disable_secondary_monitor) + return; + + if (gauge->functional) { + float alphas[4]; + + alphas[0] = 1.0f; + alphas[1] = 1.0f; + alphas[2] = 1.0f; + alphas[3] = 1.0f; + + int hud_image = get_weapon_hud_image(Player_num, PW_SECONDARY); + + DrawGaugeMonitor(gauge->pts, hud_image, 0.5f, alphas); + + if (!Render_gauge_moving) { + int x1, y; + + gauge->pts[0].p3_flags &= (~PF_PROJECTED); + g3_ProjectPoint(&gauge->pts[0]); + x1 = gauge->pts[0].p3_sx; + y = gauge->pts[0].p3_sy; + + grtext_SetFancyColor(GR_RGB(0,180,0), GR_RGB(0,180,0), GR_RGB(0,180,0), GR_RGB(0,180,0)); + grtext_SetFlags(0); + grtext_Printf(x1+HUD_X(5), y+HUD_Y(10), "%s", TXT(Static_weapon_ckpt_names[Players[Player_num].weapon[PW_SECONDARY].index][0])); + grtext_SetFlags(GRTEXTFLAG_SATURATE); + grtext_Printf(x1+HUD_X(5), y+HUD_Y(10), "%s", TXT(Static_weapon_ckpt_names[Players[Player_num].weapon[PW_SECONDARY].index][0])); + if (strlen(TXT(Static_weapon_ckpt_names[Players[Player_num].weapon[PW_SECONDARY].index][1]))) { + grtext_SetFlags(0); + grtext_Printf(x1+HUD_X(5), y+HUD_Y(20), "%s", TXT(Static_weapon_ckpt_names[Players[Player_num].weapon[PW_SECONDARY].index][1])); + grtext_SetFlags(GRTEXTFLAG_SATURATE); + grtext_Printf(x1+HUD_X(5), y+HUD_Y(20), "%s", TXT(Static_weapon_ckpt_names[Players[Player_num].weapon[PW_SECONDARY].index][1])); + y = y + HUD_Y(10); + } + grtext_SetFlags(0); + grtext_Printf(x1+HUD_X(15), y+HUD_Y(20), "%d", Players[Player_num].weapon_ammo[Players[Player_num].weapon[PW_SECONDARY].index]); + grtext_SetFlags(GRTEXTFLAG_SATURATE); + grtext_Printf(x1+HUD_X(15), y+HUD_Y(20), "%d", Players[Player_num].weapon_ammo[Players[Player_num].weapon[PW_SECONDARY].index]); + } + } +} + + +// renders the shield gauge monitor +void RenderShieldMonitor(tGauge *gauge, bool modified) +{ + float alpha_mod = (Objects[Players[Player_num].objnum].shields)/(float)INITIAL_SHIELDS; + + if (alpha_mod > 1.0f) + alpha_mod = 1.0f; + + int img = (int)ceil((1.0f-alpha_mod-0.1f) * NUM_SHIELD_GAUGE_FRAMES); + if (img < NUM_SHIELD_GAUGE_FRAMES) { + if (img < 0) + img = 0; + img = HUD_resources.shield_bmp[img]; + + if (gauge->functional) + DrawGaugeQuad(gauge->pts, img, 255); + } +} + + +// renders the ship gauge monitor +void RenderEnergyMonitor(tGauge *gauge, int orient, bool modified) +{ + if (gauge->functional) { + float img_h = bm_h(HUD_resources.energy_bmp,0); + float normalized_energy = (float)Players[Player_num].energy/(float)INITIAL_ENERGY; + g3Point used_pts[4], left_pts[4]; + float img_energy_h; + float hgt_scalar; + int i; + + if (normalized_energy > 1.0f) + normalized_energy = 1.0f; + + img_energy_h = (normalized_energy*img_h); + + for (i = 0; i < 4; i++) + { + used_pts[i] = gauge->pts[i]; + left_pts[i] = gauge->pts[i]; + } + + // draw energy gauge, showing how much energy below 100% you have. draw energy spent faded. + hgt_scalar = img_energy_h/img_h; + used_pts[3].p3_vec = used_pts[3].p3_vec - (used_pts[3].p3_vec - used_pts[0].p3_vec) * hgt_scalar; + used_pts[2].p3_vec = used_pts[2].p3_vec - (used_pts[2].p3_vec - used_pts[1].p3_vec) * hgt_scalar; + used_pts[3].p3_vecPreRot = used_pts[3].p3_vecPreRot - (used_pts[3].p3_vecPreRot - used_pts[0].p3_vecPreRot) * hgt_scalar; + used_pts[2].p3_vecPreRot = used_pts[2].p3_vecPreRot - (used_pts[2].p3_vecPreRot - used_pts[1].p3_vecPreRot) * hgt_scalar; + + if (!orient) + DrawGaugeQuad(used_pts, HUD_resources.energy_bmp, 0,0,1,1.0f-normalized_energy, 64, 0); + else + DrawGaugeQuad(used_pts, HUD_resources.energy_bmp, 1,0,0,1.0f-normalized_energy, 64, 0); + + left_pts[0].p3_vec = left_pts[0].p3_vec + (left_pts[3].p3_vec - left_pts[0].p3_vec) * (1.0f - hgt_scalar); + left_pts[1].p3_vec = left_pts[1].p3_vec + (left_pts[2].p3_vec - left_pts[1].p3_vec) * (1.0f - hgt_scalar); + left_pts[0].p3_vecPreRot = left_pts[0].p3_vecPreRot + (left_pts[3].p3_vecPreRot - left_pts[0].p3_vecPreRot) * (1.0f - hgt_scalar); + left_pts[1].p3_vecPreRot = left_pts[1].p3_vecPreRot + (left_pts[2].p3_vecPreRot - left_pts[1].p3_vecPreRot) * (1.0f - hgt_scalar); + + if (!orient) + DrawGaugeQuad(left_pts, HUD_resources.energy_bmp, 0,1.0f-normalized_energy,1,1, 255, 0); + else + DrawGaugeQuad(left_pts, HUD_resources.energy_bmp, 1,1.0f-normalized_energy,0,1, 255, 0); + } +} + + + +// renders the ship gauge monitor +void RenderAfterburnMonitor(tGauge *gauge, bool modified) +{ + g3Point used_pts[4], left_pts[4]; + + if (gauge->functional) { + float img_w = bm_w(HUD_resources.afterburn_bmp, 0); + float val= (Players[Player_num].afterburn_time_left/AFTERBURN_TIME); + float img_burn_w = (val*img_w); + float w_scalar; + int i; + + for (i = 0; i < 4; i++) + { + used_pts[i] = gauge->pts[i]; + left_pts[i] = gauge->pts[i]; + } + + // draw energy gauge, + // showing how much energy below 100% you have. draw energy spent faded. + w_scalar = img_burn_w/img_w; + + used_pts[3].p3_vec = used_pts[3].p3_vec + (used_pts[2].p3_vec-used_pts[3].p3_vec) * w_scalar; + used_pts[0].p3_vec = used_pts[0].p3_vec + (used_pts[1].p3_vec-used_pts[0].p3_vec) * w_scalar; + used_pts[3].p3_vecPreRot = used_pts[3].p3_vecPreRot + (used_pts[2].p3_vecPreRot-used_pts[3].p3_vecPreRot) * w_scalar; + used_pts[0].p3_vecPreRot = used_pts[0].p3_vecPreRot + (used_pts[1].p3_vecPreRot-used_pts[0].p3_vecPreRot) * w_scalar; + DrawGaugeQuad(used_pts, HUD_resources.afterburn_bmp, val,0,1,1, 64, 0); + + left_pts[1].p3_vec = left_pts[1].p3_vec - (left_pts[1].p3_vec-left_pts[0].p3_vec) * (1.0f-w_scalar); + left_pts[2].p3_vec = left_pts[2].p3_vec - (left_pts[2].p3_vec-left_pts[3].p3_vec) * (1.0f-w_scalar); + left_pts[1].p3_vecPreRot = left_pts[1].p3_vecPreRot - (left_pts[1].p3_vecPreRot-left_pts[0].p3_vecPreRot) * (1.0f-w_scalar); + left_pts[2].p3_vecPreRot = left_pts[2].p3_vecPreRot - (left_pts[2].p3_vecPreRot-left_pts[3].p3_vecPreRot) * (1.0f-w_scalar); + DrawGaugeQuad(left_pts, HUD_resources.afterburn_bmp, 0,0,val,1, 255, 0); + } +} + + + +// renders the ship gauge monitor +#define GAUGE_INVSHIPRING_DELTA 0.06f +void RenderShipMonitor(tGauge *gauge, bool modified) +{ + if( !gauge->functional ) + return; + + ubyte alpha = 255; + if( Objects[Players[Player_num].objnum].effect_info->type_flags & EF_CLOAKED ) + { + float time_frame = Objects[Players[Player_num].objnum].effect_info->cloak_time; + if( time_frame < HUD_CLOAKEND_TIME ) + { + alpha = 128 - 127*FixCos(65536.0f * (time_frame-(int)time_frame)); + } + else + { + alpha = 0; + } + } + + DrawGaugeQuad(gauge->pts, HUD_resources.ship_bmp, alpha); + DrawGaugeQuad(gauge->pts, HUD_resources.ship_bmp, alpha, true); + + if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) + { + // get to the view matrix so we have direction vectors to move along + matrix view_matrix; + g3_GetUnscaledMatrix( &view_matrix ); + + float inv_time_frame = (Gametime - (int)Gametime); + float inv_alpha = (ubyte)(255*(1.0f-(inv_time_frame/2.0f))); + g3Point invpts[4]; + int i; + + // do invulnerability animation. + for (i = 0; i < 4; i++) + { + invpts[i] = gauge->pts[i]; + } + + float amount = (inv_time_frame*GAUGE_INVSHIPRING_DELTA); + invpts[0].p3_x -= amount; + invpts[0].p3_y += amount; + invpts[1].p3_x += amount; + invpts[1].p3_y += amount; + invpts[2].p3_x += amount; + invpts[2].p3_y -= amount; + invpts[3].p3_x -= amount; + invpts[3].p3_y -= amount; + + invpts[0].p3_vecPreRot = invpts[0].p3_vecPreRot - ( view_matrix.rvec * amount ) + ( view_matrix.uvec * amount ); + invpts[1].p3_vecPreRot = invpts[1].p3_vecPreRot + ( view_matrix.rvec * amount ) + ( view_matrix.uvec * amount ); + invpts[2].p3_vecPreRot = invpts[2].p3_vecPreRot + ( view_matrix.rvec * amount ) - ( view_matrix.uvec * amount ); + invpts[3].p3_vecPreRot = invpts[3].p3_vecPreRot - ( view_matrix.rvec * amount ) - ( view_matrix.uvec * amount ); + + DrawGaugeQuad(invpts, HUD_resources.invpulse_bmp, inv_alpha); + DrawGaugeQuad(invpts, HUD_resources.invpulse_bmp, inv_alpha, true); + } +} + + +// projects monitor coordinates to screen coordinates +void RotateMonitorPosition(tGauge *gauge) +{ + ASSERT(gauge->monitor); + ASSERT(gauge->monitor->num_faces == 1); + int subnum = gauge->monitor - gauge->cockpit->submodel; + + if (gauge->monitor->faces[0].nverts != 4) + Int3(); + + for (int j = 0; j < gauge->monitor->faces[0].nverts; j++) + { + // saved the view coordinates of the monitor if the gauge is moving and/or was just initialized. + // if gauge is not moving, then just use these saved points and make sure to mark them as NOT PROJECTED. + vector vpt; + vector vert; + vpt = gauge->monitor->verts[gauge->monitor->faces[0].vertnums[(gauge->first_vert+j)%4]]; + // JEFFNOTE: Since we are using hardware transforms, we want to update the points all the time + //if (Render_gauge_moving || gauge->just_init || Render_gauge_reset) + { + GetPolyModelPointInWorld(&vert, gauge->cockpit, Render_gauge_pos, Render_gauge_matrix, subnum, Render_normalized_times, &vpt); + g3_RotatePoint(&gauge->pts[j], &vert); + } + } +} + + +////////////////////////////////////////////////////////////////////////////// +// Graphic primatives for hud + + +// renders a monitor style quad +void DrawGaugeMonitor(g3Point *pts, int bm, float brightness, float *alphas) +{ + g3Point *pntlist[4]; + g3Point pnts[4]; + + if (bm == -1) + bm = BAD_BITMAP_HANDLE; + + for (int i=0;i<4;i++) + { + pnts[i] = pts[i]; + pnts[i].p3_flags = PF_UV | PF_RGBA | (pts[i].p3_flags&PF_ORIGPOINT); + pntlist[i]=&pnts[i]; + } + + pnts[0].p3_u=0; + pnts[0].p3_v=0; + pnts[0].p3_a=alphas[0]; + + pnts[1].p3_u=1; + pnts[1].p3_v=0; + pnts[1].p3_a=alphas[1]; + + pnts[2].p3_u=1; + pnts[2].p3_v=1; + pnts[2].p3_a=alphas[2]; + + pnts[3].p3_u=0; + pnts[3].p3_v=1; + pnts[3].p3_a=alphas[3]; + + rend_SetZBufferState(0); + rend_SetTextureType (TT_LINEAR); + rend_SetLighting(LS_NONE); + rend_SetAlphaValue (brightness*255); + + rend_SetAlphaType (AT_CONSTANT_VERTEX); + + g3_DrawPoly (4,pntlist,bm); +} + + +// renders a square texture onto the screen. +void DrawGaugeQuad(g3Point *pts, int bm, ubyte alpha, bool saturate) +{ + DrawGaugeQuad(pts, bm, 0,0,1,1,alpha,saturate); +} + + +// renders a square texture onto the screen. +void DrawGaugeQuad(g3Point *pts, int bm, float u0,float v0, float u1,float v1, ubyte alpha, bool saturate) +{ + g3Point *pntlist[4]; + g3Point pnts[4]; + + if (bm == -1) + bm = BAD_BITMAP_HANDLE; + + for (int i=0;i<4;i++) + { + pnts[i] = pts[i]; + pnts[i].p3_flags = PF_UV | ( pts[i].p3_flags&PF_ORIGPOINT ); + pntlist[i]=&pnts[i]; + } + + pnts[0].p3_u=u0; + pnts[0].p3_v=v0; + + pnts[1].p3_u=u1; + pnts[1].p3_v=v0; + + pnts[2].p3_u=u1; + pnts[2].p3_v=v1; + + pnts[3].p3_u=u0; + pnts[3].p3_v=v1; + + + rend_SetZBufferState(0); + rend_SetTextureType(TT_LINEAR); + rend_SetLighting(LS_NONE); + rend_SetAlphaValue(alpha); + + if (!saturate) + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + else + rend_SetAlphaType (AT_SATURATE_TEXTURE); + + g3_DrawPoly (4,pntlist,bm); +} + + +// renders a flat poly onto the screen with given color +void DrawGaugeQuadFlat(g3Point *pts, float r, float g, float b, ubyte alpha) +{ + float ar[4] = {r,r,r,r}, ag[4]= {g,g,g,g}, ab[4] = {b,b,b,b}; + + DrawGaugeQuadFlat(pts, ar,ag,ab, alpha); +} + + +// renders a flat poly onto the screen with 4 colors (for each vertex) +void DrawGaugeQuadFlat(g3Point *pts, float *r, float *g, float *b, ubyte alpha) +{ + g3Point *pntlist[4]; + g3Point pnts[4]; + + for (int i=0;i<4;i++) + { + pnts[i] = pts[i]; + pnts[i].p3_flags = PF_RGBA | (pts[i].p3_flags&PF_ORIGPOINT); + pntlist[i]=&pnts[i]; + } + + pnts[0].p3_r=r[0]; + pnts[0].p3_g=g[0]; + pnts[0].p3_b=b[0]; + + pnts[1].p3_r=r[1]; + pnts[1].p3_g=g[1]; + pnts[1].p3_b=b[1]; + + pnts[2].p3_r=r[2]; + pnts[2].p3_g=g[2]; + pnts[2].p3_b=b[2]; + + pnts[3].p3_r=r[3]; + pnts[3].p3_g=g[3]; + pnts[3].p3_b=b[3]; + + + rend_SetZBufferState(0); + rend_SetLighting(LS_GOURAUD); + rend_SetTextureType (TT_FLAT); + rend_SetColorModel (CM_RGB); + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (alpha); + + g3_DrawPoly (4,pntlist,0); +} + + +// Returns the vertex index that corresponds to the upper-left vertex +int GetFirstVert(bsp_info *sm) +{ + ASSERT(sm->num_faces == 1 && sm->faces[0].nverts == 4); + int i; + polyface *face = &sm->faces[0]; + int first_vert=-1; + + for (i=0;i<4 && first_vert==-1;i++) + { + if (sm->alpha[face->vertnums[i]]<.5) + first_vert=i; + } + + if (first_vert<0) + first_vert=0; // If no alpha info, just set to first vertex + + return first_vert; +} + +//Returns the coordinates of the specified cockpit monitor +//Parameter: window - 0 means primary monitor, 1 means secondary +// x0,y0,x1,y1 - these are filled in with the coordinates of the montiro +//Returns: true if got coords, false if the monitor was animating +bool GetCockpitWindowCoords(int window,int *left,int *top,int *right,int *bot) +{ + if (Render_gauge_moving) + return 0; + + tGauge *gauge; + + gauge = &Gauge_list[GAUGE_INDEX(window?STAT_SECONDARYLOAD:STAT_PRIMARYLOAD)]; + + if (gauge->monitor) { + + #define HUD_RENDER_ZOOM 0.56f + + StartFrame(false); + g3_StartFrame(&Viewer_object->pos,&Viewer_object->orient,HUD_RENDER_ZOOM); + + for (int i=0;i<2;i++) { + gauge->pts[i*2].p3_flags &= (~PF_PROJECTED); + g3_ProjectPoint(&gauge->pts[i*2]); + } + + *left = gauge->pts[0].p3_sx; + *top = gauge->pts[0].p3_sy; + *right = gauge->pts[2].p3_sx+2; + *bot = gauge->pts[2].p3_sy+2; + + g3_EndFrame(); + EndFrame(); + + return 1; + } + + return 0; +} + + diff --git a/Descent3/gauges.h b/Descent3/gauges.h new file mode 100644 index 000000000..3358c3247 --- /dev/null +++ b/Descent3/gauges.h @@ -0,0 +1,105 @@ +/* + * $Logfile: /DescentIII/main/gauges.h $ + * $Revision: 16 $ + * $Date: 10/20/98 12:42p $ + * $Author: Matt $ + * + * Gauge interface + * + * $Log: /DescentIII/main/gauges.h $ + * + * 16 10/20/98 12:42p Matt + * Made the small views work on the cockpit. + * + * 15 5/26/98 5:05p Samir + * shield frames now contained in hud structure and share common graphics + * between gauges and hud. + * + * 14 5/18/98 4:52p Samir + * fixed hud image for weapons. + * + * 13 3/20/98 8:23p Samir + * Started implementing images. + * + * 12 3/18/98 6:24p Samir + * Implemented monitor system. + * + * 11 3/17/98 2:37p Samir + * reorg of hud/gauge/cockpit dependencies. + * + * 10 1/23/98 12:07p Jason + * made framerate display toggleable + * + * 9 12/29/97 5:47p Samir + * Uses new text system and took out references to Game_viewport. + * + * 8 11/16/97 6:55p Samir + * Added init flag for gauge structure. + * + * 7 11/14/97 5:29p Samir + * Now we pass four world coordinates of the 4 vertex monitor to a gauge + * rendering function. + * + * 6 11/11/97 1:25p Samir + * Added modified flag to tGauge and cleaned up gauge renderering a + * little. + * + * 5 11/04/97 6:23p Samir + * New Gauge interface. + * + * 4 10/29/97 12:33p Samir + * Framework for RenderHudGauges + * + * 3 10/28/97 6:36p Samir + * Gauge system for text controls functioning. + * + * 2 10/28/97 12:47p Samir + * Gauges are drawn once per frame from GameLoop to a call to gauges.cpp + * + * 1 10/28/97 12:27p Samir + * Initial revision + * + * $NoKeywords: $ + */ + + + +#ifndef GAUGES_H +#define GAUGES_H + +#include "pstypes.h" +#include "vecmat.h" +#include "hud.h" + +//Hack vars for turning off the monitors +extern bool Disable_primary_monitor,Disable_secondary_monitor; + +// number of gauges +#define NUM_GAUGES 16 + +// initializes cockpit gauges +void InitGauges(tStatMask gauge_mask); + +// deinitializes cockpit gauges +void CloseGauges(); + +// renders gauges +void RenderGauges(vector *cockpit_pos, matrix *cockpit_mat, float *normalized_times, bool moving,bool reset=false); + +// flags certain gauges to be modified next frame. +void FlagGaugesModified(tStatMask mask_modified); + +// sets gauges as functional +void FlagGaugesFunctional(tStatMask mask); + +// sets gauges as gauges nonfunctional +void FlagGaugesNonfunctional(tStatMask mask); + +//Returns the coordinates of the specified cockpit monitor +//Parameter: window - 0 means primary monitor, 1 means secondary +// x0,y0,x1,y1 - these are filled in with the coordinates of the montiro +//Returns: true if got coords, false if the monitor was animating +bool GetCockpitWindowCoords(int window,int *left,int *top,int *right,int *bot); + +#endif + diff --git a/Descent3/help.cpp b/Descent3/help.cpp new file mode 100644 index 000000000..a9a4e9996 --- /dev/null +++ b/Descent3/help.cpp @@ -0,0 +1,229 @@ +/* +* $Logfile: /DescentIII/main/help.cpp $ +* $Revision: 32 $ +* $Date: 3/20/00 12:10p $ +* $Author: Matt $ +* +* Help functions (when user presses F1 in the game) +* +* $Log: /DescentIII/main/help.cpp $ + * + * 32 3/20/00 12:10p Matt + * Merge of Duane's post-1.3 changes. + * Added help for PageDown pause key. + * + * 31 5/21/99 11:14p Samir + * changed some text in help dialog. (SHIFT-TAB) + * + * 30 5/21/99 12:45p Samir + * added help strings for F8 and F9 keys. + * + * 29 4/16/99 12:17p Samir + * new help menu with new ui. + * + * 28 4/16/99 4:25a Jeff + * removed ifdef of linux since new compiler compiles it + * + * 27 4/15/99 1:39a Jeff + * changes for linux compile + * + * 26 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 25 4/06/99 12:11p Doug + * Made ESC key work (Samir) + * + * 24 4/03/99 9:26p Jeff + * changed dialogs that weren't using UID_OK and UID_CANCEL to use and + * handle them properly + * + * 23 3/04/99 11:41a Jeff + * fixed text going off window + * + * 22 3/03/99 7:21p Jeff + * adjusted help file window text to make room for new text + * + * 21 3/03/99 1:18p Jeff + * added telcom main menu item + * + * 20 2/28/99 9:19p Jeff + * added some strings to help window + * + * 19 10/20/98 12:15p Jeff + * removed SHIFT-F2 Center Missile view + * + * 18 10/19/98 7:08p Jeff + * added "F6 Multiplayer On-Screen Menu" + * + * 17 10/08/98 7:27p Samir + * made multiplayer friendly. + * + * 16 10/07/98 2:54p Jeff + * General UI fixes and additions + * + * 15 10/06/98 5:34p Jeff + * various UI changes/improvements + * + * 14 9/21/98 4:34p Jeff + * updates to Help menu + * + * 13 9/02/98 2:54p Jeff + * added defines for text colors to be used throughout the game...fixed up + * buddy menu too + * + * 12 8/15/98 2:49p Matt + * Took out unneeded include + * + * 11 7/09/98 12:33p Jeff + * f1 closes help screen + * + * 10 7/08/98 4:58p Jeff + * a bit more updated + * + * 9 6/17/98 3:27p Jeff + * Changes made for localization + * + * 8 6/16/98 10:38a Jeff + * localization, strings pulled out to stringtable.h and d3.str + * + * 7 5/05/98 6:26p Samir + * adjusted to work in 512 384 + * + * 6 4/24/98 3:27p Jeff + * removed briefing.h dependency + * + * 5 3/05/98 2:51p Jeff + * Made help system use UI + * + * 4 3/04/98 3:09p Jeff + * Now gives key descriptions + * + * 3 3/03/98 4:01p Jeff + * Got it displaying a "cheap" help screen + * + * 2 3/02/98 5:14p Jeff + * initial help system created +* +* $NoKeywords: $ +*/ + +#include "help.h" +#include "mono.h" +#include "renderer.h" +#include "render.h" +#include "ddio.h" +#include "descent.h" +#include "game.h" +#include "CFILE.H" +#include "application.h" +#include +#include +#include +#include "newui.h" +#include "grtext.h" +#include "gamefont.h" +#include "stringtable.h" + +#define TITLETEXT TXT_HELP +int HelpText[] = { TXI_ESC , TXI_HLPQUIT, +#ifndef DEMO //Do Not include in the PC Gamer Demo + TXI_HLPALTF2 , TXI_HLPSAVEGAME, + TXI_HLPALTF3 , TXI_HLPLOADGAME, +#endif + TXI_F2 , TXI_HLPCONFIG, + TXI_F3 , TXI_HLPCOCKPIT, + TXI_HLPF4 , TXI_HLPGUIDEBOT, + TXI_F5 , TXI_TOGGLEDEMO, + TXI_F6 , TXI_MLTMENU, + TXI_F8 , TXI_HLP_MULTIMSG, + TXI_F9 , TXI_HLP_QUIKSAVE, + TXI_F12 , TXI_DROPSMARKER, + TXI_SHFTTAB , TXI_TCMM, + #ifdef MACINTOSH + TXI_PAGE_DOWN , TXI_HLPPAUSEDESC, + #else + TXI_HLPPAUSE , TXI_HLPPAUSEDESC, + #endif + TXI_PLUSMINUS , TXI_HLPSCRNSIZE, + TXI_HLPPRNTSCRN , TXI_HLPTAKESCRNSHT, + TXI_HLP1_5 , TXI_HLPSELPRIM, + TXI_HLP6_0 , TXI_HLPSELSECN, + TXI_SF1 , TXI_HLPREARLEFT, + TXI_SF2 , TXI_HLPREARRIGHT, + TXI_SHFTF8 , TXI_DISPLAYGAMEMSGCONSOLE, + TXI_SHFTF9 , TXI_DISPLAYHUDMSGCONSOLE, + 0}; + + +#define HELP_X_KEY_POS 60 +#define HELP_X_DESC_POS 160 +#define HELP_Y 32 + + +#define IDH_QUIT UID_CANCEL +#define WND_HELP_W 448 +#define WND_HELP_H 384 +#define WND_HELP_X (Game_window_w - WND_HELP_W)/2 +#define WND_HELP_Y (Game_window_h - WND_HELP_H)/2 + +void HelpDisplay(void) +{ + newuiTiledWindow help_wnd; + newuiSheet *sheet; + int strs_to_print = 0; + int index, res; + bool exit_menu = false; + + help_wnd.Create(TITLETEXT, 0, 0, WND_HELP_W, WND_HELP_H); + sheet = help_wnd.GetSheet(); + +// add commands + help_wnd.AddAcceleratorKey(KEY_F1, UID_CANCEL); + + strs_to_print = 0; + + //find out how many strings to print out + while(HelpText[strs_to_print]>0) strs_to_print++; + + sheet->NewGroup(NULL, 30,10); + for(index=0;indexAddText(TXT(HelpText[index])); + } + } + + + sheet->NewGroup(NULL, 130,10); + for(index=1;indexAddText(TXT(HelpText[index])); + } + } + + sheet->NewGroup(NULL, WND_HELP_W-160, WND_HELP_H-96, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK, UID_CANCEL); + +// quit_hot.Create(&help_wnd, UID_CANCEL, 0, &UITextItem(TXT_PRESSESCRET, UICOL_HOTSPOT_LO,UIALPHA_HOTSPOT_LO), +// &UITextItem(TXT_PRESSESCRET,UICOL_HOTSPOT_HI,UIALPHA_HOTSPOT_HI), +// HELP_X_KEY_POS, WND_HELP_H - OKCANCEL_YOFFSET, 0,0,UIF_FIT|UIF_CENTER); + help_wnd.Open(); + + while (!exit_menu) + { + res = help_wnd.DoUI(); + + // handle all UI results. + switch (res) + { + case UID_CANCEL: + case NEWUIRES_FORCEQUIT: + exit_menu = true; + break; + } + } + + help_wnd.Close(); + help_wnd.Destroy(); +} diff --git a/Descent3/help.h b/Descent3/help.h new file mode 100644 index 000000000..126b74450 --- /dev/null +++ b/Descent3/help.h @@ -0,0 +1,23 @@ +/* +* $Logfile: /DescentIII/main/help.h $ +* $Revision: 2 $ +* $Date: 3/02/98 5:14p $ +* $Author: Jeff $ +* +* Help functions (when user presses F1 in game) +* +* $Log: /DescentIII/main/help.h $ + * + * 2 3/02/98 5:14p Jeff + * initial help system created +* +* $NoKeywords: $ +*/ + +#ifndef __D3HELP_H_ +#define __D3HELP_H_ + +void HelpDisplay(void); + + +#endif \ No newline at end of file diff --git a/Descent3/hotspotmap.cpp b/Descent3/hotspotmap.cpp new file mode 100644 index 000000000..47d33dc3e --- /dev/null +++ b/Descent3/hotspotmap.cpp @@ -0,0 +1,1110 @@ +/* +* $Logfile: /DescentIII/main/hotspotmap.cpp $ +* $Revision: 19 $ +* $Date: 4/19/00 5:07p $ +* $Author: Matt $ +* +* Contains the code for the hotspotmap functions (used in TelCom) +* +* $Log: /DescentIII/main/hotspotmap.cpp $ + * + * 19 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 18 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 17 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 16 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 15 10/15/98 1:36p Jeff + * fixed bug vc6 caught + * + * 14 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 13 8/15/98 2:49p Matt + * Took out unneeded include + * + * 12 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 11 3/23/98 9:55a Jeff + * Made changes to remove old telcom + * + * 10 3/18/98 8:00p Jeff + * Added some functionality for text and using the monitor borders + * correctly in the new Telcom + * + * 9 3/12/98 3:32p Jeff + * Initial changes started for New TelCom + * + * 8 2/08/98 5:02p Samir + * Commented out use of Game_viewport code. + * + * 7 1/15/98 11:13a Jeff + * Got Telcom working again, switched from 2d to 3d + * + * 6 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 5 8/15/97 12:24p Jeff + * Changed things around a bit, added code to handle the round corners + * + * 4 8/13/97 3:13p Jeff + * optimized for memory usage (removed the second background from being + * stored in memory) + * + * 3 8/04/97 7:23p Jeff + * + * 2 8/04/97 6:23p Jeff + * + * 1 8/04/97 6:23p Jeff + * + * 2 8/01/97 2:36p Jeff + * + * 1 8/01/97 2:35p Jeff + * + * 2 8/01/97 2:35p Jeff +* +* $NoKeywords: $ +*/ + +#include "mono.h" +#include "renderer.h" +#include "render.h" +#include "grdefs.h" +#include "ddio.h" +#include "descent.h" +#include "game.h" +#include "CFILE.H" +#include "application.h" +#include +#include +#include "hotspotmap.h" +#include "mem.h" +#include "bitmap.h" +#include "manage.h" + +void makecorner(int corner_bmp,int back_bmp,char *tmap,int l,int t,int r,int b); + +//just like the old bm_tga_translate_pixel function, but it stores the alpha in the alpha_value parameter +ushort menutga_translate_pixel (int pixel,char *alpha_value) +{ + int red=((pixel>>16) & 0xFF); + int green=((pixel>>8) & 0xFF); + int blue=((pixel) & 0xFF); + int alpha=((pixel>>24) & 0xFF); + + *alpha_value=alpha; + + int newred=red>>3; + int newgreen=green>>3; + int newblue=blue>>3; + ushort newpix=OPAQUE_FLAG | (newred<<10) | (newgreen << 5) | (newblue); + if (alpha==0) + newpix=NEW_TRANSPARENT_COLOR; + + return newpix; +} + +//This takes a char array filled with alphavalues from the TGA and fills in a hotspotmap_t struct with the +//scanline info +//returns the number of windows it found while making the hotspot map +#define HOTSPOT_THERE 1 +#define WINDOW_THERE 2 +#define NOTHING_THERE 0 + +int CreateHotSpotMap(char *map,int width,int height,hotspotmap_t *hsmap) +{ + //make sure we have a clean struct to work with + if(hsmap->hs) + { + FreeHotSpotMapInternals(hsmap); + } + + int curr_sl,x,y,count,num_hs=0; + unsigned char alpha; + char whats_there[256]; + int window_count; + + for(count=0;count<256;count++) + whats_there[count]=NOTHING_THERE; + + //Get total number of hotspots (based on highest hotspot value) + for(y=0;y=num_hs) + num_hs=alpha+1; + } + else + { + whats_there[alpha]=WINDOW_THERE; + } + } + } + } + + hsmap->num_of_hotspots=num_hs; + if(!num_hs) return -1; + + hsmap->hs=(hotspot *)mem_malloc(sizeof(hotspot)*num_hs); + ASSERT(hsmap->hs); + for(count=0;counths[count].starting_y=MAX_MAP_HEIGHT; + hsmap->hs[count].scanlines=0; + } + else + { + hsmap->hs[count].starting_y=0; + hsmap->hs[count].scanlines=0; + } + } + + scanline sl_x[MAX_MAP_HEIGHT]; + int sl_count,last_y=-1; + + //this is to set the values of start and end initially + for(count=0;counths[count].starting_y>y) + hsmap->hs[count].starting_y=y; + if(sl_x[y].start>x) sl_x[y].start=x; + if(sl_x[y].endhs[count].scanlines=sl_count; + if(sl_count) + { + hsmap->hs[count].x=(scanline *)mem_malloc(sizeof(scanline)*sl_count); + ASSERT(hsmap->hs[count].x); + memset(hsmap->hs[count].x,0,sizeof(scanline)*sl_count); + y=hsmap->hs[count].starting_y; + for(curr_sl=0;curr_slhs[count].x[curr_sl].start=sl_x[y].start; + hsmap->hs[count].x[curr_sl].end=sl_x[y].end; + sl_x[y].start=MAX_MAP_WIDTH; + sl_x[y].end=0; + y++; + }//end for (curr_sl) + }//end if (sl_count) + } + else + { + hsmap->hs[count].scanlines=0; + hsmap->hs[count].starting_y=0; + hsmap->hs[count].x=NULL; + }//end ifelse (hotspot_there) + }//end for (count) + + mprintf((0,"Hunting down empty hotspots....\n")); + for(count=0;counths[count].scanlines=0; + hsmap->hs[count].starting_y=0; + if(hsmap->hs[count].x) + { + mem_free(hsmap->hs[count].x); + } + hsmap->hs[count].x=NULL; + } + } + + mprintf((0,"Finding window count...")); + window_count=0; + for(count=0;count<256;count++) + { + if(whats_there[count]==WINDOW_THERE) + window_count++; + + } + mprintf((0,"%d windows found\n",window_count)); + return window_count; +} + + +void CreateWindowMap(char *map,int width,int height,windowmap_t *wndmap) +{ + mprintf((0,"Processing %d windows\n",wndmap->num_of_windows)); + int x,y,count; + unsigned char alpha; + bool newline=true; + + wndmap->wm=(window_box *)mem_malloc(sizeof(window_box)*wndmap->num_of_windows); + ASSERT(wndmap->wm); + + for(int index=0;indexnum_of_windows;index++) + { + wndmap->wm[index].lt = NULL; + wndmap->wm[index].rt = NULL; + wndmap->wm[index].lb = NULL; + wndmap->wm[index].rb = NULL; + } + + for(count=0;countnum_of_windows;count++) + { + wndmap->wm[count].x=wndmap->wm[count].y= + wndmap->wm[count].width=wndmap->wm[count].height=-1; + wndmap->wm[count].t_top_y=wndmap->wm[count].b_top_y=-1; + wndmap->wm[count].t_bottom_y=wndmap->wm[count].b_bottom_y=0; + wndmap->wm[count].l_start_x=wndmap->wm[count].r_start_x=MAX_MAP_WIDTH; + wndmap->wm[count].l_end_x=wndmap->wm[count].r_end_x=0; + wndmap->wm[count].on_left=wndmap->wm[count].on_top=true; + } + + int working_window=-1; + + for(y=0;y=130) + alpha = alpha; + } + if( (alpha>=MAX_HOTSPOTS) && (alpha!=NO_ALPHA) ) + { + //set the working_window value (needed so we know which window to work with for the writeable + if(alpha!=WRITEABLE_ALPHA) + { + working_window=alpha-MAX_HOTSPOTS; + } + + if(newline) + { + int count; + for(count=0;countnum_of_windows;count++) + wndmap->wm[count].on_left=true; + newline=false; + } + + //Set an initial value for x and y of a window + if( (wndmap->wm[working_window].x==-1) && (alpha!=WRITEABLE_ALPHA)) + { + wndmap->wm[working_window].x=x; + wndmap->wm[working_window].y=y; + } + + //Set an initial value for the write_x and write_y for a window + if( (alpha==WRITEABLE_ALPHA) && (working_window>=0) ) + { + if(wndmap->wm[working_window].t_top_y!=-1) + { + if(wndmap->wm[working_window].l_end_xwm[working_window].on_left=false; + } + else + { + wndmap->wm[working_window].on_left=true; + } + + if(wndmap->wm[working_window].t_bottom_ywm[working_window].on_top=false; + } + else + { + wndmap->wm[working_window].on_top=true; + } + } + else + { + wndmap->wm[working_window].on_left=true; + wndmap->wm[working_window].on_top=true; + } + + + //first time init + if((wndmap->wm[working_window].t_top_y==-1) && (wndmap->wm[working_window].on_top) ) + { + wndmap->wm[working_window].t_top_y=y; + wndmap->wm[working_window].t_bottom_y=y; + } + if((wndmap->wm[working_window].b_top_y==-1) && (!wndmap->wm[working_window].on_top) ) + { + wndmap->wm[working_window].b_top_y=y; + wndmap->wm[working_window].b_bottom_y=y; + } + + //adjust bottom + if(wndmap->wm[working_window].on_top) + { + if(wndmap->wm[working_window].t_bottom_ywm[working_window].t_bottom_y=y; + } + } + else + { + if(wndmap->wm[working_window].b_bottom_ywm[working_window].b_bottom_y=y; + } + } + + //adjust edges + if(wndmap->wm[working_window].on_left) + { + if(wndmap->wm[working_window].l_start_x>x) + { + wndmap->wm[working_window].l_start_x=x; + } + if(wndmap->wm[working_window].l_end_xwm[working_window].l_end_x=x; + } + } + else + { + if(wndmap->wm[working_window].r_start_x>x) + { + wndmap->wm[working_window].r_start_x=x; + } + if(wndmap->wm[working_window].r_end_xwm[working_window].r_end_x=x; + } + } + } + + if(alpha!=WRITEABLE_ALPHA) + { + //adjust width and height + if( (wndmap->wm[working_window].x!=-1)&&(x>wndmap->wm[working_window].x)&& + (x-wndmap->wm[working_window].x>wndmap->wm[working_window].width) ) + { + wndmap->wm[working_window].width=x-wndmap->wm[working_window].x; + } + + if( (y-wndmap->wm[working_window].y!=-1)&&(y>y-wndmap->wm[working_window].y) && + (y-wndmap->wm[working_window].y>wndmap->wm[working_window].height)) + { + wndmap->wm[working_window].height=y-wndmap->wm[working_window].y; + } + } + }//end if + }//end for x + }//end for y + + for(count=0;countnum_of_windows;count++) + { + int h,w; + int x,y,real_x,real_y; + int *left_x,*right_x,*top_y,*bottom_y; + char *array; + unsigned char alpha; + + //left top + left_x=&wndmap->wm[count].l_start_x; + right_x=&wndmap->wm[count].l_end_x; + top_y=&wndmap->wm[count].t_top_y; + bottom_y=&wndmap->wm[count].t_bottom_y; + + if(*top_y!=-1) + { + w=*right_x-*left_x; + h=*bottom_y-*top_y; + wndmap->wm[count].lt=(char *)mem_malloc(w*h); + array=wndmap->wm[count].lt; + for(real_y=*top_y,y=0;real_y<*bottom_y;real_y++,y++) + { + for(real_x=*left_x,x=0;real_x<*right_x;real_x++,x++) + { + alpha=(unsigned)map[real_y*width+real_x]; + if(alpha==WRITEABLE_ALPHA) + { + array[y*w+x]=true; + } + else + { + array[y*w+x]=false; + } + } + } + + //right top + left_x=&wndmap->wm[count].r_start_x; + right_x=&wndmap->wm[count].r_end_x; + top_y=&wndmap->wm[count].t_top_y; + bottom_y=&wndmap->wm[count].t_bottom_y; + + w=*right_x-*left_x; + h=*bottom_y-*top_y; + wndmap->wm[count].rt=(char *)mem_malloc(w*h); + + array=wndmap->wm[count].rt; + for(real_y=*top_y,y=0;real_y<*bottom_y;real_y++,y++) + { + for(real_x=*left_x,x=0;real_x<*right_x;real_x++,x++) + { + alpha=(unsigned)map[real_y*width+real_x]; + if(alpha==WRITEABLE_ALPHA) + { + array[y*w+x]=true; + } + else + { + array[y*w+x]=false; + } + } + } + } + + //left bottom + left_x=&wndmap->wm[count].l_start_x; + right_x=&wndmap->wm[count].l_end_x; + top_y=&wndmap->wm[count].b_top_y; + bottom_y=&wndmap->wm[count].b_bottom_y; + + if(*top_y!=-1) + { + w=*right_x-*left_x; + h=*bottom_y-*top_y; + wndmap->wm[count].lb=(char *)mem_malloc(w*h); + array=wndmap->wm[count].lb; + + for(real_y=*top_y,y=0;real_y<*bottom_y;real_y++,y++) + { + for(real_x=*left_x,x=0;real_x<*right_x;real_x++,x++) + { + alpha=(unsigned)map[real_y*width+real_x]; + if(alpha==WRITEABLE_ALPHA) + { + array[y*w+x]=true; + } + else + { + array[y*w+x]=false; + } + } + } + + //right bottom + left_x=&wndmap->wm[count].r_start_x; + right_x=&wndmap->wm[count].r_end_x; + top_y=&wndmap->wm[count].b_top_y; + bottom_y=&wndmap->wm[count].b_bottom_y; + + w=*right_x-*left_x; + h=*bottom_y-*top_y; + wndmap->wm[count].rb=(char *)mem_malloc(w*h); + array=wndmap->wm[count].rb; + for(real_y=*top_y,y=0;real_y<*bottom_y;real_y++,y++) + { + for(real_x=*left_x,x=0;real_x<*right_x;real_x++,x++) + { + alpha=(unsigned)map[real_y*width+real_x]; + if(alpha==WRITEABLE_ALPHA) + { + array[y*w+x]=true; + } + else + { + array[y*w+x]=false; + } + } + } + } + + } + + for(count=0;countnum_of_windows;count++) + { + mprintf((0,"Window #%d: Left/Top=(%d,%d) Width=%d Height=%d\n",count, + wndmap->wm[count].x, + wndmap->wm[count].y, + wndmap->wm[count].width, + wndmap->wm[count].height)); + mprintf((0,"---L.T. (%d,%d)->(%d,%d)\n", + wndmap->wm[count].l_start_x, + wndmap->wm[count].t_top_y, + wndmap->wm[count].l_end_x, + wndmap->wm[count].t_bottom_y)); + mprintf((0,"---R.T. (%d,%d)->(%d,%d)\n", + wndmap->wm[count].r_start_x, + wndmap->wm[count].t_top_y, + wndmap->wm[count].r_end_x, + wndmap->wm[count].t_bottom_y)); + mprintf((0,"---L.B. (%d,%d)->(%d,%d)\n", + wndmap->wm[count].l_start_x, + wndmap->wm[count].b_top_y, + wndmap->wm[count].l_end_x, + wndmap->wm[count].b_bottom_y)); + mprintf((0,"---R.B. (%d,%d)->(%d,%d)\n", + wndmap->wm[count].r_start_x, + wndmap->wm[count].b_top_y, + wndmap->wm[count].r_end_x, + wndmap->wm[count].b_bottom_y)); + } +} + +// Loads a tga or ogf file into a bitmap...returns handle to bm or -1 on error, and fills in the alphamap +int menutga_alloc_file (char *name,char *hsmap[1],int *w,int *h) +{ + ubyte image_id_len,color_map_type,image_type,pixsize,descriptor; + ubyte upside_down=0; + ushort width,height; + unsigned int pixel; + int i,t,n; + char alphavalue; + CFILE *infile; + + if(!cfexist(name)) return -1; + + infile=(CFILE *)cfopen (name,"rb"); + if (!infile) + { + Int3(); // get jeff! + } + + image_id_len=cf_ReadByte (infile); + color_map_type=cf_ReadByte (infile); + image_type=cf_ReadByte (infile); + + if (color_map_type!=0 || (image_type!=2)) + { + mprintf ((0,"menutga: Can't read this type of TGA.\n")); + return -1; + } + + for (i=0;i<9;i++) // ingore next 9 bytes + cf_ReadByte (infile); + + *w=width=cf_ReadShort (infile); + *h=height=cf_ReadShort (infile); + pixsize=cf_ReadByte (infile); + + hsmap[0]=(char *)mem_malloc(width*height); + + ASSERT(hsmap); + + if (pixsize!=32) + { + mprintf ((0,"menutga: This file has a pixsize of field of %d, it should be 32. ",pixsize)); + return -1; + } + + descriptor=cf_ReadByte(infile); + if ((descriptor & 0x0F)!=8) + { + mprintf ((0,"menutga: Descriptor field & 0x0F must be 8, but this is %d.",descriptor & 0x0F)); + return -1; + } + + for (i=0;i>5; + upside_down=1-upside_down; + + // Load the actual bitmap data in, converting it from 32 bit to 16 bit, and replacing + // that pesky transparency color without our replacement + + + for (i=0;inum_of_hotspots)); + cf_WriteByte(file,hsmap->num_of_hotspots); + + for(curr_hs=0;curr_hsnum_of_hotspots;curr_hs++) + { + cf_WriteInt(file,hsmap->hs[curr_hs].starting_y); + cf_WriteInt(file,hsmap->hs[curr_hs].scanlines); + for(curr_sl=0;curr_slhs[curr_hs].scanlines;curr_sl++) + { + cf_WriteInt(file,hsmap->hs[curr_hs].x[curr_sl].start); + cf_WriteInt(file,hsmap->hs[curr_hs].x[curr_sl].end); + } + } + + int count; + int index,size; + + cf_WriteInt(file,wndmap->num_of_windows); + for(count=0;countnum_of_windows;count++) + { + cf_WriteInt(file,wndmap->wm[count].x); + cf_WriteInt(file,wndmap->wm[count].y); + cf_WriteInt(file,wndmap->wm[count].width); + cf_WriteInt(file,wndmap->wm[count].height); + cf_WriteInt(file,wndmap->wm[count].l_start_x); + cf_WriteInt(file,wndmap->wm[count].l_end_x); + cf_WriteInt(file,wndmap->wm[count].r_start_x); + cf_WriteInt(file,wndmap->wm[count].r_end_x); + cf_WriteInt(file,wndmap->wm[count].t_top_y); + cf_WriteInt(file,wndmap->wm[count].t_bottom_y); + cf_WriteInt(file,wndmap->wm[count].b_top_y); + cf_WriteInt(file,wndmap->wm[count].b_bottom_y); + + if(wndmap->wm[count].t_top_y!=-1) + { + //left top + size=((wndmap->wm[count].l_end_x)-(wndmap->wm[count].l_start_x))*((wndmap->wm[count].t_bottom_y)-(wndmap->wm[count].t_top_y)); + for(index=0;indexwm[count].lt[index]); + } + //right top + size=((wndmap->wm[count].r_end_x)-(wndmap->wm[count].r_start_x))*((wndmap->wm[count].t_bottom_y)-(wndmap->wm[count].t_top_y)); + for(index=0;indexwm[count].rt[index]); + } + } + if(wndmap->wm[count].b_top_y!=-1) + { + //left bottom + size=((wndmap->wm[count].l_end_x)-(wndmap->wm[count].l_start_x))*((wndmap->wm[count].b_bottom_y)-(wndmap->wm[count].b_top_y)); + for(index=0;indexwm[count].lb[index]); + } + //right bottom + size=((wndmap->wm[count].r_end_x)-(wndmap->wm[count].r_start_x))*((wndmap->wm[count].b_bottom_y)-(wndmap->wm[count].b_top_y)); + for(index=0;indexwm[count].rb[index]); + } + } + } + cfclose(file); +} + +//Given a filename and a hotspotmap structure, it loads the hotspot map (.HSM) +void menutga_LoadHotSpotMap(int back_bmp, char *filename,hotspotmap_t *hsmap,windowmap_t *wndmap) +{ + //start with a clean struct + if(hsmap->hs) + { + FreeHotSpotMapInternals(hsmap); + } + + mprintf((0,"Loading hotspotmap %s ",filename)); + + CFILE *infile; + infile=(CFILE *)cfopen(filename,"rb"); + if(!infile) + { + Int3(); //get Jeff + return; + } + + hsmap->num_of_hotspots=cf_ReadByte(infile); + + mprintf((0,"Contains: (%d hotspots) ",hsmap->num_of_hotspots)); + + int curr_hs,curr_sl,num_sl; + + + hsmap->hs=(hotspot *)mem_malloc(sizeof(hotspot)*hsmap->num_of_hotspots); + memset(hsmap->hs,0,sizeof(hotspot)*hsmap->num_of_hotspots); + for(curr_hs=0;curr_hsnum_of_hotspots;curr_hs++) + { + hsmap->hs[curr_hs].starting_y=cf_ReadInt(infile); + num_sl=hsmap->hs[curr_hs].scanlines=cf_ReadInt(infile); + if(num_sl) + { + hsmap->hs[curr_hs].x=(scanline *)mem_malloc(sizeof(scanline)*hsmap->hs[curr_hs].scanlines); + memset(hsmap->hs[curr_hs].x,0,sizeof(scanline)*hsmap->hs[curr_hs].scanlines); + for(curr_sl=0;curr_slhs[curr_hs].scanlines;curr_sl++) + { + hsmap->hs[curr_hs].x[curr_sl].start=cf_ReadInt(infile); + hsmap->hs[curr_hs].x[curr_sl].end=cf_ReadInt(infile); + } + } + else + { + hsmap->hs[curr_hs].x=NULL; + } + } + + int count,index,size; + wndmap->num_of_windows=cf_ReadInt(infile); + mprintf((0,"(%d Windows)\n",wndmap->num_of_windows)); + wndmap->wm=(window_box *)mem_malloc(sizeof(window_box)*wndmap->num_of_windows); + for(count=0;countnum_of_windows;count++) + { + wndmap->wm[count].x=cf_ReadInt(infile); + wndmap->wm[count].y=cf_ReadInt(infile); + wndmap->wm[count].width=cf_ReadInt(infile); + wndmap->wm[count].height=cf_ReadInt(infile); + wndmap->wm[count].l_start_x=cf_ReadInt(infile); + wndmap->wm[count].l_end_x=cf_ReadInt(infile); + wndmap->wm[count].r_start_x=cf_ReadInt(infile); + wndmap->wm[count].r_end_x=cf_ReadInt(infile); + wndmap->wm[count].t_top_y=cf_ReadInt(infile); + wndmap->wm[count].t_bottom_y=cf_ReadInt(infile); + wndmap->wm[count].b_top_y=cf_ReadInt(infile); + wndmap->wm[count].b_bottom_y=cf_ReadInt(infile); + + if(wndmap->wm[count].t_top_y!=-1) + { + //left top + size=((wndmap->wm[count].l_end_x)-(wndmap->wm[count].l_start_x))*((wndmap->wm[count].t_bottom_y)-(wndmap->wm[count].t_top_y)); + wndmap->wm[count].lt=(char *)mem_malloc(size); + for(index=0;indexwm[count].lt[index]=cf_ReadByte(infile); + } + + //right top + size=((wndmap->wm[count].r_end_x)-(wndmap->wm[count].r_start_x))*((wndmap->wm[count].t_bottom_y)-(wndmap->wm[count].t_top_y)); + wndmap->wm[count].rt=(char *)mem_malloc(size); + for(index=0;indexwm[count].rt[index]=cf_ReadByte(infile); + } + } + + if(wndmap->wm[count].b_top_y!=-1) + { + //left bottom + size=((wndmap->wm[count].l_end_x)-(wndmap->wm[count].l_start_x))*((wndmap->wm[count].b_bottom_y)-(wndmap->wm[count].b_top_y)); + wndmap->wm[count].lb=(char *)mem_malloc(size); + for(index=0;indexwm[count].lb[index]=cf_ReadByte(infile); + } + //right bottom + size=((wndmap->wm[count].r_end_x)-(wndmap->wm[count].r_start_x))*((wndmap->wm[count].b_bottom_y)-(wndmap->wm[count].b_top_y)); + wndmap->wm[count].rb=(char *)mem_malloc(size); + for(index=0;indexwm[count].rb[index]=cf_ReadByte(infile); + } + } + } + cfclose(infile); + + //make the corner bitmaps, with transparency + for(count=0;countnum_of_windows;count++) + { + if(wndmap->wm[count].t_top_y!=-1) + { + //left top + wndmap->wm[count].lt_bmp= bm_AllocBitmap(wndmap->wm[count].l_end_x-wndmap->wm[count].l_start_x, + wndmap->wm[count].t_bottom_y-wndmap->wm[count].t_top_y,0); + makecorner(wndmap->wm[count].lt_bmp,back_bmp,wndmap->wm[count].lt,wndmap->wm[count].l_start_x,wndmap->wm[count].t_top_y, + wndmap->wm[count].l_end_x,wndmap->wm[count].t_bottom_y); + + //right top + wndmap->wm[count].rt_bmp=bm_AllocBitmap(wndmap->wm[count].r_end_x-wndmap->wm[count].r_start_x, + wndmap->wm[count].t_bottom_y-wndmap->wm[count].t_top_y,0); + makecorner(wndmap->wm[count].rt_bmp,back_bmp,wndmap->wm[count].rt,wndmap->wm[count].r_start_x,wndmap->wm[count].t_top_y, + wndmap->wm[count].r_end_x,wndmap->wm[count].t_bottom_y); + if(wndmap->wm[count].lt) mem_free(wndmap->wm[count].lt); + if(wndmap->wm[count].rt) mem_free(wndmap->wm[count].rt); + } + + if(wndmap->wm[count].b_top_y!=-1) + { + //left bottom + wndmap->wm[count].lb_bmp=bm_AllocBitmap(wndmap->wm[count].l_end_x-wndmap->wm[count].l_start_x, + wndmap->wm[count].b_bottom_y-wndmap->wm[count].b_top_y,0); + makecorner(wndmap->wm[count].lb_bmp,back_bmp,wndmap->wm[count].lb,wndmap->wm[count].l_start_x,wndmap->wm[count].b_top_y, + wndmap->wm[count].l_end_x,wndmap->wm[count].b_bottom_y); + + //right bottom + wndmap->wm[count].rb_bmp=bm_AllocBitmap(wndmap->wm[count].r_end_x-wndmap->wm[count].r_start_x, + wndmap->wm[count].b_bottom_y-wndmap->wm[count].b_top_y,0); + makecorner(wndmap->wm[count].rb_bmp,back_bmp,wndmap->wm[count].rb,wndmap->wm[count].r_start_x,wndmap->wm[count].b_top_y, + wndmap->wm[count].r_end_x,wndmap->wm[count].b_bottom_y); + if(wndmap->wm[count].lb) mem_free(wndmap->wm[count].lb); + if(wndmap->wm[count].rb) mem_free(wndmap->wm[count].rb); + } + } +} + +void makecorner(int corner_bmp,int back_bmp,char *tmap,int l,int t,int r,int b) +{ + int real_x,real_y,awidth,ax,ay; + short *backdata,*cornerdata; + int back_rowsize,corner_rowsize; + + awidth=r-l; + bm_ClearBitmap(corner_bmp); + backdata=(short *)bm_data(back_bmp, 0); + back_rowsize = bm_rowsize(back_bmp, 0); + cornerdata=(short *)bm_data(corner_bmp, 0); + corner_rowsize = bm_rowsize(corner_bmp, 0); + + backdata += (t * (back_rowsize/2)); + + for(real_y=t,ay=0;real_yhs) return; + + int curr_hs; + int num_hs; + + num_hs=hsmap->num_of_hotspots; + for(curr_hs=0;curr_hshs[curr_hs].scanlines) + { + if(hsmap->hs[curr_hs].x) + { + mem_free(hsmap->hs[curr_hs].x); + hsmap->hs[curr_hs].x=NULL; + } + } + } + if(hsmap->hs) + { + mem_free(hsmap->hs); + hsmap->hs=NULL; + } +} + + + +//This function (given a filename) loads a TGA file, extracts a hotspot map, and saves the hotspot map +bool menutga_ConvertTGAtoHSM(char *fpath) +{ + char path[255],filename[255],ext[8]; + ddio_SplitPath(fpath,path,filename,ext); + mprintf((0,"Extracting hotspots from %s\n",filename)); + + //strip file name + int index,width=0,height=0; + char *menu_filename; + hotspotmap_t hsmap; + windowmap_t wndmap; + + wndmap.num_of_windows=0; + wndmap.wm=NULL; + hsmap.hs=NULL; + hsmap.num_of_hotspots=0; + + for(index=(signed)strlen(filename);index>=0;index--) + { + if(filename[index]=='.') + { + filename[index]='\0'; + index=-1; + } + } + + int size=strlen(filename)+5; + menu_filename=(char *)mem_malloc(size); + strcpy(menu_filename,filename); + strcat(menu_filename,".HSM"); //Hot Spot Map + mprintf((0,"HSM=%s\n",menu_filename)); + + ddio_MakePath(path,LocalManageGraphicsDir,menu_filename,NULL); + + //now load up the tga and alloc it, then save it with the alpha hotspots + int bm_handle; + char *map[1]; + bm_handle=menutga_alloc_file(fpath,map,&width,&height); + if(bm_handle==-1) return false; + wndmap.num_of_windows=CreateHotSpotMap(map[0],width,height,&hsmap); + if(wndmap.num_of_windows==-1) return false; //DAJ -1FIX + CreateWindowMap(map[0],width,height,&wndmap); + menutga_SaveHotSpotMap(path,&hsmap,&wndmap); + ExportHotSpot("C:\\hotspot.txt",&hsmap); + mem_free(menu_filename); + mem_free(map[0]); + FreeHotSpotMapInternals(&hsmap); + for(index=0;indexlock(); +//@@ for(count=0;countnum_of_hotspots;count++) +//@@ { +//@@ curr_hotspot=&hsmap->hs[count]; +//@@ curr_y=curr_hotspot->starting_y; +//@@ for(scanline_count=0;scanline_countscanlines;scanline_count++) +//@@ { +//@@ Game_viewport->hline(GR_RGB(0,0,255),curr_hotspot->x[scanline_count].start, +//@@ curr_hotspot->x[scanline_count].end,curr_y+scanline_count); +//@@ } +//@@ } +//@@ +//@@ for(count=0;countnum_of_windows;count++) +//@@ { +//@@ for(scanline_count=0;scanline_countscanlines;scanline_count++) +//@@ { +//@@ Game_viewport->fillrect(GR_RGB(255,0,0), wndmap->wm[count].x,wndmap->wm[count].y, +//@@ wndmap->wm[count].x+wndmap->wm[count].width-1,wndmap->wm[count].y+wndmap->wm[count].height-1); +//@@ //if there is a writeable area, display it +//@@ if(wndmap->wm[count].t_top_y!=-1) +//@@ { +//@@ Game_viewport->fillrect(GR_RGB(128,128,128), wndmap->wm[count].l_start_x,wndmap->wm[count].t_top_y, +//@@ wndmap->wm[count].l_end_x,wndmap->wm[count].t_bottom_y); +//@@ Game_viewport->fillrect(GR_RGB(128,128,128), wndmap->wm[count].r_start_x,wndmap->wm[count].t_top_y, +//@@ wndmap->wm[count].r_end_x,wndmap->wm[count].t_bottom_y); +//@@ } +//@@ if(wndmap->wm[count].b_top_y!=-1) +//@@ { +//@@ Game_viewport->fillrect(GR_RGB(128,128,128), wndmap->wm[count].l_start_x,wndmap->wm[count].b_top_y, +//@@ wndmap->wm[count].l_end_x,wndmap->wm[count].b_bottom_y); +//@@ Game_viewport->fillrect(GR_RGB(128,128,128), wndmap->wm[count].r_start_x,wndmap->wm[count].b_top_y, +//@@ wndmap->wm[count].r_end_x,wndmap->wm[count].b_bottom_y); +//@@ } +//@@ +//@@ } +//@@ } +//@@ +//@@ Game_viewport->unlock(); +//@@ Game_screen->flip(); +} + +//Writes a hotspotmap_t struct to a text file +void ExportHotSpot(char *filename,hotspotmap_t *hsmap) +{ + CFILE *file; + file=(CFILE *)cfopen(filename,"wt"); + ASSERT(file); + char buffer[256]; + + int i,j; + sprintf(buffer,"Number of HotSpots = %d",hsmap->num_of_hotspots); + cf_WriteString(file,buffer); + for(i=0;inum_of_hotspots;i++) + { + sprintf(buffer,"\n-%d-Scanlines = %d",i,hsmap->hs[i].scanlines); + cf_WriteString(file,buffer); + for(j=0;jhs[i].scanlines;j++) + { + sprintf(buffer,"----Y=%d StartX=%d EndX=%d",hsmap->hs[i].starting_y+j,hsmap->hs[i].x[j].start,hsmap->hs[i].x[j].end); + cf_WriteString(file,buffer); + } + } + + cfclose(file); +} diff --git a/Descent3/hotspotmap.h b/Descent3/hotspotmap.h new file mode 100644 index 000000000..5f86bbae5 --- /dev/null +++ b/Descent3/hotspotmap.h @@ -0,0 +1,104 @@ +/* +* $Logfile: /DescentIII/main/hotspotmap.h $ +* $Revision: 10 $ +* $Date: 4/17/99 6:15p $ +* $Author: Samir $ +* +* Hotspot stuff for TelCom +* +* $Log: /DescentIII/main/hotspotmap.h $ + * + * 10 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 9 3/23/98 2:18p Jeff + * Moved structures from telcom.h to here to make less dependent + * + * 8 3/23/98 9:55a Jeff + * Made changes to remove old telcom + * + * 7 3/18/98 8:00p Jeff + * Added some functionality for text and using the monitor borders + * correctly in the new Telcom + * + * 6 3/12/98 3:56p Jeff + * Added header comments to file +* +* $NoKeywords: $ +*/ + + + +#ifndef __HOTSPOTMAP_H_ +#define __HOTSPOTMAP_H_ + +#include "grdefs.h" + +//for the next two defines this is how they work when converting the alpha values into hotspots/windows +//0 to MAX_HOTSPOTS-1 = HotSpot (button) +//MAX_HOTSPOTS to 255 = Window Display +//The NO_ALPHA value is which value (0-255) means it's neither HotSpot or Window +#define MAX_HOTSPOTS 128 +#define NO_ALPHA 255 +#define WRITEABLE_ALPHA 254 +#define MAX_MAP_WIDTH 640 //Max hotspot map width +#define MAX_MAP_HEIGHT 480 //Max hotspot map height +#define TELCOM_MAX_ALPHA 130 + + +//structure for a hotspot scanline +typedef struct scanline +{ + int start,end; +}scanline; + +//structure for 1 hotspot +typedef struct hotspot +{ + int starting_y; + int scanlines; + scanline *x; +}hotspot; + +//structure for all hotspots +typedef struct hotspotmap_t +{ + char num_of_hotspots; + hotspot *hs; +}hotspotmap_t; + +//structure for 1 window +typedef struct window_box +{ + int x,y; //leftmost x, topmost y + int width,height; + + int l_start_x,l_end_x,r_start_x,r_end_x,t_top_y,t_bottom_y,b_top_y,b_bottom_y; + bool on_left,on_top;//used for creating window + char *lt,*rt,*lb,*rb;//used for holding the transparent information of the corners + int lt_bmp, rt_bmp, lb_bmp, rb_bmp; +}window_box; + +//structure for all the windows +typedef struct windowmap_t +{ + int num_of_windows; + window_box *wm; +}windowmap_t; + +// Loads a tga or ogf file into a bitmap...returns handle to bm or -1 on error, and fills in the alphamap +int menutga_alloc_file (char *name,char *hsmap[],int *w,int *h); +//Given a filename and a HotSpotMap structure, it saves it to disk (.HSM) +void menutga_SaveHotSpotMap(char *filename,hotspotmap_t *hsmap,windowmap_t *wndmap); +//Given a filename and a HotSpotMap structure, it loads the hotspot map (.HSM) +void menutga_LoadHotSpotMap(int back_bmp, char *filename,hotspotmap_t *hsmap,windowmap_t *wndmap); +//This function (given a filename) loads a TGA file, extracts a hotspot map, and saves the hotspot map +bool menutga_ConvertTGAtoHSM(char *filename); + +void ExportHotSpot(char *filename,hotspotmap_t *hsmap); //Exports a hotspotmap to an ASCII file, nice and readable +void DisplayHotSpots(hotspotmap_t *hsmap,windowmap_t *wndmap); //Displays the hotspots of the given hotspot map to the screen (in blue) +void FreeHotSpotMapInternals(hotspotmap_t *hsmap); //Deletes allocated memory within a hotspotmap struct +ushort menutga_translate_pixel (int pixel,char *alpha_value); + + +#endif \ No newline at end of file diff --git a/Descent3/hud.cpp b/Descent3/hud.cpp new file mode 100644 index 000000000..edc49069c --- /dev/null +++ b/Descent3/hud.cpp @@ -0,0 +1,2434 @@ +/* + * $Logfile: /DescentIII/main/hud.cpp $ + * $Revision: 143 $ + * $Date: 4/19/00 5:07p $ + * $Author: Matt $ + * + * $Log: /DescentIII/main/hud.cpp $ + * + * 143 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 142 4/06/00 9:25a Matt + * Fixed a screen clear problem on at ATI Rage Fury Maxx in dual-chip mode + * by forcing the screen to clear four times (instead of three). + * + * 141 3/20/00 12:10p Matt + * Merge of Duane's post-1.3 changes. + * Endian/enum fix. + * + * 140 10/21/99 4:28p Matt + * Mac merge + * + * 139 7/08/99 6:20p Jason + * fixed reticle bug + * + * 138 5/23/99 9:07p Matt + * Changed the customtext2 multisafe functions to create the item when + * it's updated, instead of having a separate create and update functions. + * This deals with players who join a netgame after the level has started + * (i.e., everyone but the server). + * + * 137 5/21/99 11:15p Samir + * savegames save hud timer and special text states. restore too. + * + * 136 5/21/99 4:06p Matt + * For customtext2 HUD items, store the buffer length in the HUD item. + * + * 135 5/21/99 3:06p Matt + * Changed customtext2 HUD items so that the HUD system keeps the text + * buffer an no one else references it directly. + * + * 134 5/20/99 5:48p Matt + * Added a HUD item flag, for use by Dallas-created items, that makes a + * HUD item persisitent for the duration of one level, but get cleared + * between levels. + * + * 133 5/20/99 2:12a Matt + * Don't blow away custom HUD items when resetting the HUD. This fixes + * one probem, but means that custom HUD items will never go away. I'll + * deal with that tomorrow. + * + * 132 5/19/99 11:25a Matt + * Added multisafe functions & Dallas actions for showing a timer on the + * screen and adding custom HUD messages. + * + * 131 5/17/99 6:04p Kevin + * new training mission localized text + * + * 130 5/17/99 5:27p Kevin + * changed enabled controls display for training mission + * + * 129 5/14/99 10:30a Kevin + * fixed misplaced #endif + * + * 128 5/13/99 12:24p Kevin + * added code for "Beta version" + * + * 127 5/05/99 5:45p Samir + * initialize reticle in cockpit code now. + * + * 126 5/01/99 5:52p Samir + * removed RenderHudMessages, and redid RenderHUDMessages so it did what + * RenderHudMessages did, resets on screen hud messages. The HUD message + * console is resetted in ResetGameMessages. + * + * 125 5/01/99 1:56a Samir + * timer always in debug builds, SCORE is off when dead. + * + * 124 4/26/99 5:51p Jason + * fixed guided bug + * + * 123 4/24/99 10:38p Samir + * cleaned up hud text problems in 'small mode' + * + * 122 4/24/99 8:43p Samir + * when shrinking screen hud messages get rendered in black region. + * + * 121 4/22/99 3:43p Kevin + * Training missions show controls on screen + * + * 120 4/21/99 9:29p Samir + * don't draw score in multiplayer. + * + * 119 4/21/99 4:17p Matt + * Added comments for table file filter + * + * 118 4/20/99 11:47a Samir + * shrinking hud fixes. + * + * 117 4/08/99 4:08p Samir + * moved timer below score and don't compile timer in release builds. + * + * 116 4/06/99 6:02p Matt + * Added score system + * + * 115 4/06/99 2:00p Samir + * added function to initialize hud items that don't go through config + * file. + * + * 114 4/06/99 11:39a Samir + * added more formatting options for hud items (added two other full + * screen hud infs for different ships) + * + * 113 4/01/99 4:50p Matt + * Took out Warning() function, chaning those calls to mprintf() + * + * 112 3/30/99 5:26p Samir + * fixed quad weapon reticle problem. + * + * 111 3/30/99 2:39p Samir + * added custom config file for fullscreen hud and hacked mass driver + * reticle in special reticle code. + * + * 110 3/29/99 10:50a Samir + * added command to move reticle in hud inf file. + * + * 109 3/26/99 12:46p Samir + * configurable reticle. + * + * 108 3/22/99 4:26p Samir + * added toggles for guided missile view and reticle. + * + * 107 3/09/99 12:20p Samir + * do quick close of cockpit if entering letterbox or observer mode from + * cockpit. + * + * 106 3/05/99 3:01p Jeff + * fixed crash when a persistant message is displayed during a IGC + * + * 105 3/04/99 8:11p Samir + * Don't assert if screen mode != SM_GAME in SetHUDMode, just Int3 and + * allow user to return. + * + * 104 3/03/99 5:34p Matt + * Added fade-out for goal complete messages + * + * 103 3/03/99 3:43a Samir + * fixed font aspect ratio problem in hires screens. + * + * 102 3/02/99 6:26p Samir + * hires font madness. + * + * 101 2/28/99 3:25a Samir + * stream name debug out. + * + * 100 2/27/99 5:07p Jason + * clear screen 3 times for triple buffer + * + * 99 2/24/99 12:25p Jeff + * fixed guided missile/mass driver reticle/view problems when going into + * a cinematic + * + * 98 2/18/99 4:02p Jeff + * only save hud mode to pilot file if fullscreen->cockpit or vice-versa + * + * 97 2/17/99 2:45p Kevin + * Adde movie making capabilities to the demo playback system + * + * 96 2/15/99 7:49p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 95 2/10/99 3:03p Samir + * table file parse changes. + * + * 94 1/31/99 9:05p Jeff + * handle HUD_OBSERVER in render + * + * 93 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 92 1/29/99 6:29p Samir + * implemented hud scrollback for hud messages. + * + * 91 1/29/99 2:08p Jeff + * localization + * + * 90 1/28/99 2:22p Samir + * simplified music system for D3. + * + * 89 1/27/99 6:05p Samir + * added scrollback for game messages on HUD. + * + * 88 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 87 12/16/98 1:57p Samir + * Replaced CleanupString2 with CleanupStr + * + * 86 12/03/98 12:52p Samir + * added hud music debug info timers. + * + * 85 12/01/98 3:34p Matt + * Got rear view working. + * + * 84 11/23/98 4:52p Kevin + * Demo system enhancments + * + * 83 11/19/98 5:40p Kevin + * Demo system + * + * 82 11/13/98 4:25p Jason + * changes for better weapon effects + * + * 81 11/13/98 2:28p Samir + * added debug code for music. + * + * 80 10/30/98 1:13p Samir + * dont' do SetHUDMode if in dedicated server mode. + * + * 79 10/22/98 2:40p Samir + * redid HUD sequencing so multiplayer hud stuff works. + * + * 78 10/20/98 1:46p Samir + * don't close cockpit if it's already open. + * + * 77 10/19/98 11:22p Samir + * added hud observer mode and fixed problems with hud switching and + * screen size. + * + * 76 10/14/98 4:27p Samir + * improved HUD_ITEM_CUSTOMTEXT + * + * 75 10/07/98 5:52p Samir + * fixed small bug in hud stat masks. + * + * 74 10/02/98 5:47p Samir + * moved reticle down a bit. + * + * 73 9/28/98 11:00a Samir + * fixed stupid bug. + * + * 72 9/18/98 4:33p Samir + * added dummy code for New Inf format hud files. + * + * 71 8/31/98 3:39p Samir + * if no energy or ammo, reticle will reflect this. + * + * 70 8/25/98 3:07p Samir + * when cockpit fails redo hud switch to fullscreen. + * + * 69 8/18/98 1:11a Samir + * fixed some stuff for multi cockpit configs. + * + * 68 8/15/98 10:51p Matt + * Added function ToggleHUDMode(), and made StartHudInputMessage() public. + * + * 67 7/06/98 7:34p Samir + * added countermeasures. + * + * 66 6/25/98 12:52p Samir + * draw afterburner hud in cockpit (special version) + * + * 65 6/24/98 7:38p Samir + * redid graphical/text/cockpit hud item management. + * + * 64 6/19/98 5:39p Samir + * use pilot information to get hud layout. + * + * 63 6/17/98 6:31p Samir + * Added anti-grav warning when dying. + * + * 62 6/15/98 6:54p Samir + * added invulnerability and cloak effect. + * + * 61 6/05/98 5:34p Samir + * if cockpit already up, when setting cockpit mode, do quick open. + * + * 60 5/26/98 10:49p Samir + * default to cockpit. + * + * 59 5/26/98 5:05p Samir + * cockpit and hud config file now 'unified'. cockpit adds to hudconfig + * load's funtionality, so cockpit info files can contain same syntax as + * hud file. + * + * 58 5/25/98 8:30p Samir + * guided missile reticle and added a hud timer. + * + * 57 5/22/98 1:36p Samir + * implemented a lot of hud items. + * + * 56 5/15/98 5:36p Samir + * use correct font scale for lores screens. + * + * 55 5/15/98 3:39p Samir + * reticle alphaed. + * + * 54 5/07/98 6:01p Samir + * allow for certain hud items to not be resettable. + * + * 53 5/07/98 2:51p Samir + * took out extern functions. + * + * 52 5/05/98 6:27p Samir + * added hud aspect ratio values and reticle is scaled correctly. + * + * 51 4/28/98 4:25p Samir + * scale font with hud shrink. + * + * 50 4/27/98 1:14p Jason + * cleaned up afterburner stuff + * + * 49 4/24/98 5:32p Samir + * new reticle implemented. + * + * 48 4/24/98 8:01a Samir + * don't pass zoom argument to Render functions. + * + * 47 4/23/98 4:13a Samir + * new hud system. + * + * 46 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 45 4/17/98 1:59p Jason + * added cool object effects + * + * 44 4/16/98 6:52a Samir + * initialize bitmap handle to -1 always when initializing hud item. + * + * 43 4/14/98 9:19p Samir + * made hacked good looking reticle for mag demo. + * + * 42 4/13/98 7:02p Samir + * beginning reticle code. + * + * 41 4/01/98 6:23p Jason + * added a slew of stuff for multiplayer + * + * 40 3/25/98 11:59a Samir + * implemented energy analog fully. + * + * 39 3/24/98 4:27p Samir + * modified energy gauges. + * + * 38 3/22/98 5:03p Samir + * updated cockpit. + * + * 37 3/20/98 8:23p Samir + * new hud and cockpit customization system. + * + * 36 3/19/98 9:19p Samir + * removed dependency of hud.h on game.h. + * + * 35 3/18/98 6:25p Samir + * Added new STAT_ constants/ + * + * 34 3/17/98 2:40p Samir + * reorg of cockpit/gauges and hud systems. + * + * 33 3/16/98 3:30p Samir + * incremental checkin. + * + * 32 3/13/98 12:09p Samir + * made changes to reflect new cockpit.cpp and cockpit.h + * + * 31 2/17/98 2:22p Jason + * fixed some potential z sorting problems + * + * 30 2/14/98 10:48p Jason + * got preferred rendering working + * + * 29 2/12/98 4:20p Matt + * Made letterbox window shorter. + * + * 28 1/28/98 10:34a Samir + * Reticle. + * + * 27 1/13/98 4:27p Samir + * Fixed clear screen strangeness. + * + * 26 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * + * 1 5/14/97 11:12a Jason + * file rendering stuff on the hud + * + * $NoKeywords: $ + */ + + +#include "hud.h" +#include "gauges.h" +#include "grdefs.h" +#include "game.h" +#include "ddio.h" +#include "player.h" +#include "renderer.h" +#include "descent.h" +#include "object.h" +#include "gamefont.h" +#include "polymodel.h" +#include "cockpit.h" +#include "game2dll.h" +#include "ship.h" +#include "pilot.h" +#include "mem.h" +#include "d3music.h" +#include "demofile.h" +#include "stringtable.h" +#include "pstring.h" +#include "config.h" + +#include +#include +#include +#include +#include "gamecinematics.h" +#include "CtlCfgElem.h" +#include "ctlconfig.h" + +////////////////////////////////////////////////////////////////////////////// +// constants + +#define FRAMERATE_TIME_DELAY 0.25 //update every quarter second + + +////////////////////////////////////////////////////////////////////////////// + +// PLAYER SAVEGAME PREFERENCES START +// determines what's displayable on the hud +#ifdef E3_DEMO +tStatMask Hud_stat_mask = STAT_TIMER; +#else +tStatMask Hud_stat_mask = 0; +#endif + +#define STAT_SCORE STAT_TIMER + +// PLAYER SAVEGAME PREFERENCES END + +// hud aspect +float Hud_aspect_x = 1.0f; +float Hud_aspect_y = 1.0f; +bool Small_hud_flag = false; // changes how hud items are drawn in small huds. + +// used by reticle system. +static short Ret_x_off, Ret_y_off; +static char Reticle_prefix[PSFILENAME_LEN+1]; + +// Hud mode of display +static tHUDMode Hud_mode = HUD_COCKPIT; + +// hud item array. +static tHUDItem HUD_array[MAX_HUD_ITEMS]; + +#define HUDITEMUSED(_c) (HUD_array[_c].stat ? true : false) + +// hud resource structure +struct sHUDResources HUD_resources; + +// initializes non configurable hud items. +void InitDefaultHUDItems(); + +// initializes items based off their type (information, etc.) +void InitHUDItem(int new_item, tHUDItem *item); + +// iterate through entire hud item list to draw. +void RenderHUDItems(tStatMask stat_mask); + +// frees up reticle bitmaps +void FreeReticle(); + +// renders the reticle +void RenderReticle(); + +// renders missile reticle +void RenderMissileReticle(); + +// renders zoom reticle +void RenderZoomReticle(); + +// hack! + +// functions in HudDisplay.cpp +extern void HudDisplayRouter(tHUDItem *item); + +bool Hud_show_controls = false; + + +////////////////////////////////////////////////////////////////////////////// + +// initializes hud. +void InitHUD() +{ + int i; + extern bool HUD_disabled; // from gameloop.cpp + + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + HUD_array[i].stat = 0; + HUD_array[i].flags = 0; + HUD_array[i].id = HUD_INVALID_ID; + } + +// load static hud image resources to be used by HudDisplay.cpp + HUD_resources.arrow_bmp = -1; + HUD_resources.goal_bmp = -1; + HUD_resources.goal_complete_bmp = -1; + HUD_resources.lock_bmp[0] = -1; + HUD_resources.lock_bmp[1] = -1; + HUD_resources.wpn_bmp = -1; + HUD_resources.dot_bmp = -1; + HUD_resources.antigrav_bmp[0] = -1; + HUD_resources.antigrav_bmp[1] = -1; + strcpy(HUD_resources.hud_inf_name, "hud.inf"); + + strcpy(Reticle_prefix, "ret"); + Ret_x_off = 0; + Ret_y_off = 0; + + for (i = 0; i < NUM_SHIELD_GAUGE_FRAMES; i++) + HUD_resources.shield_bmp[i] = -1; + HUD_resources.energy_bmp = -1; + HUD_resources.afterburn_bmp = -1; + HUD_resources.invpulse_bmp = -1; + +// this call may be unnecessary since it's called again in InitCockpit. +// InitReticle(-1,-1); // initialize reticle system + +// scrollback windows + ResetGameMessages(); // resets game message rollback screen. + ResetHUDMessages(); + HUD_disabled = 0; +} + + +// closes hud. +void CloseHUD() +{ + int i; + + CloseHUDMessageConsole(); // closes down rollback console + CloseGameMessageConsole(); // closes game journal rollback + ResetGameMessages(); // reset game message system. + ResetHUDMessages(); + +// free hud items. + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if (HUD_array[i].stat) { + FreeHUDItem(i); + } + } +} + +tHUDItem *GetHUDItem(int id) +{ + return &HUD_array[id]; +} + +// initializes hud. +void InitShipHUD(int ship) +{ + int i; + +// load static hud image resources to be used by HudDisplay.cpp + HUD_resources.arrow_bmp = bm_AllocLoadFileBitmap("hudarrow.ogf",0); + HUD_resources.goal_bmp = bm_AllocLoadFileBitmap("hudgoal.ogf",0); + HUD_resources.goal_complete_bmp = bm_AllocLoadFileBitmap("hudgoalon.ogf",0); + HUD_resources.lock_bmp[0] = bm_AllocLoadFileBitmap("hudlockl.ogf",0); + HUD_resources.lock_bmp[1] = bm_AllocLoadFileBitmap("hudlockr.ogf",0); + HUD_resources.wpn_bmp = bm_AllocLoadFileBitmap("hudicon.ogf", 0); + HUD_resources.dot_bmp = bm_AllocLoadFileBitmap("huddot.ogf", 0); + HUD_resources.antigrav_bmp[0] = bm_AllocLoadFileBitmap("hudantigravl.ogf",0); + HUD_resources.antigrav_bmp[1] = bm_AllocLoadFileBitmap("hudantigravr.ogf",0); + + for (i = 0; i < NUM_SHIELD_GAUGE_FRAMES; i++) + HUD_resources.shield_bmp[i] = -1; + HUD_resources.energy_bmp = -1; + HUD_resources.afterburn_bmp = -1; + HUD_resources.invpulse_bmp = -1; + +// sets current hud mode static global +//DAJ Current_pilot.get_hud_data((ubyte *)&Hud_mode); + ubyte hud_tmp; + Current_pilot.get_hud_data(&hud_tmp); + Hud_mode = (tHUDMode)hud_tmp; +} + + +// closes hud for current ship +void CloseShipHUD() +{ + FreeReticle(); + +// free hud resources + bm_FreeBitmap(HUD_resources.antigrav_bmp[1]); + bm_FreeBitmap(HUD_resources.antigrav_bmp[0]); + bm_FreeBitmap(HUD_resources.dot_bmp); + bm_FreeBitmap(HUD_resources.wpn_bmp); + bm_FreeBitmap(HUD_resources.lock_bmp[1]); + bm_FreeBitmap(HUD_resources.lock_bmp[0]); + bm_FreeBitmap(HUD_resources.goal_complete_bmp); + bm_FreeBitmap(HUD_resources.goal_bmp); + bm_FreeBitmap(HUD_resources.arrow_bmp); +} + + +// places an item on the hud +void AddHUDItem(tHUDItem *item) +{ +// find free hud slot. + int i; + + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if (!HUD_array[i].stat) { + // we will initialize the hud item. + InitHUDItem(i, item); + break; + } + } + + if (i == MAX_HUD_ITEMS) + { + mprintf((0,"Unable to add hud item (type=%d).\n", item->type)); + } +} + + +// resets hud +void ResetHUD() +{ + int i; + + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if (HUD_array[i].stat && !(HUD_array[i].flags & HUD_FLAG_LEVEL) && !(HUD_array[i].flags & HUD_FLAG_PERSISTANT)) { + FreeHUDItem(i); + } + } +} + +//clears out HUD level items +void ResetHUDLevelItems() +{ + int i; + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if (HUD_array[i].stat && (HUD_array[i].flags & HUD_FLAG_LEVEL)) { + FreeHUDItem(i); + } + } +} + +// from game.cpp +extern void InitGameScreen(int,int); +extern bool Dedicated_server; + +// manually sets the cockpit display. +void SetHUDMode(tHUDMode mode) +{ + static int saved_window_w = 0, saved_window_h = 0; + + if (Dedicated_server) + return; + + if (GetScreenMode() != SM_GAME) { + #ifdef _DEBUG + Int3(); + #endif + return; + } + +// clear the screen. + Clear_screen = 4; + +// free poly model of cockpit if we're not switching to a cockpit mode. + if (Hud_mode == HUD_COCKPIT && IsValidCockpit()) { + if (mode == HUD_LETTERBOX || mode == HUD_OBSERVER) { + QuickCloseCockpit(); + } + else if (mode != HUD_COCKPIT) { + CloseCockpit(); + } + } + +// do cockpit specific thing. +redo_hud_switch: + switch (mode) + { + ushort cp_hud_stat,cp_hud_graphical_stat; + + case HUD_LETTERBOX: + // our objective here is to display a letterbox screen for the cockpit. + // we save the old width and height of the windows. + saved_window_w = Game_window_w; + saved_window_h = Game_window_h; + + Game_window_w = Max_window_w; + Game_window_h = Max_window_h - (Max_window_h/3); + + if (Game_window_h > Max_window_h) + Game_window_h = Max_window_h; + + if (Game_window_w > Max_window_w) + Game_window_w = Max_window_w; + + Game_window_x = (Max_window_w - Game_window_w)/2; + Game_window_y = (Max_window_h - Game_window_h)/2; + + SetHUDState(STAT_MESSAGES | (Hud_stat_mask & STAT_FPS), 0); + break; + + case HUD_FULLSCREEN: + // our objective here is to display a fullscreen box that is resizable. + LoadHUDConfig(HUD_resources.hud_inf_name); + + InitGameScreen(saved_window_w ? saved_window_w : Game_window_w, saved_window_h ? saved_window_h : Game_window_h); + saved_window_w = 0; + saved_window_h = 0; + + Current_pilot.get_hud_data(NULL,&cp_hud_stat,&cp_hud_graphical_stat); + + SetHUDState(cp_hud_stat | STAT_SCORE | (Hud_stat_mask&STAT_FPS), cp_hud_graphical_stat); + + break; + + case HUD_COCKPIT: + if (!IsValidCockpit()) { + mode = HUD_FULLSCREEN; + AddHUDMessage(TXT_NOCOCKPIT); + goto redo_hud_switch; + } + if (Hud_mode == HUD_COCKPIT || Hud_mode == HUD_LETTERBOX || Hud_mode == HUD_OBSERVER) + QuickOpenCockpit(); + else + OpenCockpit(); + + InitGameScreen(saved_window_w ? saved_window_w : Game_window_w, saved_window_h ? saved_window_h : Game_window_h); + + saved_window_w = 0; + saved_window_h = 0; + + Current_pilot.get_hud_data(NULL,&cp_hud_stat,&cp_hud_graphical_stat); + + SetHUDState(STAT_MESSAGES | STAT_SHIELDS | STAT_ENERGY | STAT_GOALS | STAT_CUSTOM | STAT_AFTERBURN | STAT_INVENTORY | STAT_CNTRMEASURE | STAT_SCORE | + (Hud_stat_mask & STAT_WARNING) | + (Hud_stat_mask & STAT_FPS) | + (Hud_stat_mask & STAT_SCORE), + (cp_hud_graphical_stat & STAT_WARNING)); + break; + + case HUD_OBSERVER: + InitGameScreen(saved_window_w ? saved_window_w : Game_window_w, saved_window_h ? saved_window_h : Game_window_h); + saved_window_w = 0; + saved_window_h = 0; + + SetHUDState( STAT_MESSAGES | (Hud_stat_mask & STAT_FPS), 0); + break; + + default: + Int3(); + + } + + // save out current hud mode + //JEFF: (only if going from HUD_FULLSCREEN->HUD_COCKPIT or vice-versa) + if ( (Hud_mode==HUD_FULLSCREEN && mode==HUD_COCKPIT) || + (Hud_mode==HUD_COCKPIT && mode==HUD_FULLSCREEN) ) + { + ubyte bmode = mode; //DAJ MAC using enums always int + Current_pilot.set_hud_data((ubyte *)&bmode); + mprintf((0,"Saving new hud mode to pilot\n")); + PltWriteFile(&Current_pilot); + } + + Hud_mode = mode; +} + + +// the current cockpit mode; +tHUDMode GetHUDMode() +{ + return Hud_mode; +} + + +// toggle the hud between cockput & fullscreen modes +void ToggleHUDMode() +{ + if (GetHUDMode() == HUD_COCKPIT) + SetHUDMode(HUD_FULLSCREEN); + else if (GetHUDMode() == HUD_FULLSCREEN) + SetHUDMode(HUD_COCKPIT); +} + +// sets the hud item state(what's visible, how it's drawn, etc) +void SetHUDState(ushort hud_mask, ushort hud_gr_mask) +{ + int i; + + Hud_stat_mask = (hud_mask | hud_gr_mask); + +// now go through each hud item, check if it's hud stat mask includes the requested hud_gr_mask +// if it does, then set hud mask to graphical + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + HUD_array[i].stat &= ~(STAT_GRAPHICAL); + if (HUD_array[i].stat & hud_gr_mask) + HUD_array[i].stat |= STAT_GRAPHICAL; + } +} + + +// initializes items based off their type (information, etc.) +void InitHUDItem(int new_item, tHUDItem *item) +{ + ASSERT(new_item < MAX_HUD_ITEMS); + ushort stat = 0x0000; + + item->id = new_item; + + switch (item->type) + { + case HUD_ITEM_PRIMARY: + stat = STAT_PRIMARYLOAD; + break; + + case HUD_ITEM_SECONDARY: + stat = STAT_SECONDARYLOAD; + break; + + case HUD_ITEM_SHIELD: + stat = STAT_SHIELDS; + break; + + case HUD_ITEM_ENERGY: + stat = STAT_ENERGY; + break; + + case HUD_ITEM_AFTERBURNER: + stat = STAT_AFTERBURN; + break; + + case HUD_ITEM_INVENTORY: + stat = STAT_INVENTORY; + break; + + case HUD_ITEM_SHIPSTATUS: + stat = STAT_SHIP; + break; + + case HUD_ITEM_CNTRMEASURE: + stat = STAT_CNTRMEASURE; + break; + + case HUD_ITEM_CUSTOMTEXT: + if (item->data.text) { + char *text = mem_strdup(item->data.text); + HUD_array[new_item].data.text = text; + } + stat = STAT_CUSTOM; + break; + + case HUD_ITEM_CUSTOMTEXT2: //malloc buffer to be updated later + HUD_array[new_item].data.text = (char *) mem_malloc(item->buffer_size); + HUD_array[new_item].data.text[0] = 0; + HUD_array[new_item].buffer_size = item->buffer_size; + stat = STAT_CUSTOM; + break; + + case HUD_ITEM_CUSTOMIMAGE: + stat = STAT_CUSTOM; + break; + + case HUD_ITEM_WARNINGS: + stat = STAT_WARNING; + break; + + case HUD_ITEM_GOALS: + case HUD_ITEM_GOALSTATES: + stat = STAT_GOALS; + break; + + case HUD_ITEM_SCORE: + stat = STAT_SCORE; + break; + + case HUD_ITEM_TIMER: + HUD_array[new_item].data.timer_handle = item->data.timer_handle; + stat = STAT_CUSTOM; + break; + + default: + Int3(); + } + +// copy hud item structure. + HUD_array[new_item].x = item->x; + HUD_array[new_item].y = item->y; + HUD_array[new_item].xa = item->xa; + HUD_array[new_item].ya = item->ya; + HUD_array[new_item].xb = item->xb; + HUD_array[new_item].yb = item->yb; + HUD_array[new_item].tx = item->tx; + HUD_array[new_item].ty = item->ty; + HUD_array[new_item].grscalex = item->grscalex; + HUD_array[new_item].grscaley = item->grscaley; + HUD_array[new_item].type = item->type; + HUD_array[new_item].stat = item->stat | stat; + HUD_array[new_item].flags = item->flags; + HUD_array[new_item].alpha = item->alpha; + HUD_array[new_item].color = item->color; + HUD_array[new_item].tcolor = item->tcolor; + HUD_array[new_item].saturation_count = item->saturation_count; + HUD_array[new_item].render_fn = item->render_fn; + HUD_array[new_item].id = item->id; + HUD_array[new_item].dirty_rect.reset(); +} + +//Returns the item number if there's a customtext2 item, else -1 +int FindCustomtext2HUDItem() +{ + int i; + for (i = 0; i < MAX_HUD_ITEMS; i++) { + if (HUD_array[i].stat && (HUD_array[i].type == HUD_ITEM_CUSTOMTEXT2)) + return i; + } + + return -1; +} + + + +//Updates the customtext2 item, if there is one +void UpdateCustomtext2HUDItem(char *text) +{ + int i; + +#ifdef MACINTOSH + char *p = text; + char ch; + while(ch = *p) { + if(ch == '\r') + *p = '\n'; + p++; + } +#endif + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if (HUD_array[i].stat && (HUD_array[i].type == HUD_ITEM_CUSTOMTEXT2)) { + ASSERT(HUD_array[i].data.text != NULL); + strncpy(HUD_array[i].data.text,text,HUD_array[i].buffer_size); + HUD_array[i].data.text[HUD_array[i].buffer_size-1]; + break; + } + } +} + + +// savegame system hooks +void SGSHudState(CFILE *fp) +{ + int i; + tHUDItem *huditem; + + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + // save custom text2 and timer hud items + huditem = &HUD_array[i]; + if (huditem->stat) { + if (huditem->type == HUD_ITEM_CUSTOMTEXT2) { + cf_WriteShort(fp, (short)huditem->stat); + cf_WriteByte(fp, (sbyte)huditem->type); + cf_WriteShort(fp, huditem->x); + cf_WriteShort(fp, huditem->y); + cf_WriteInt(fp, huditem->color); + cf_WriteShort(fp, (short)huditem->flags); + cf_WriteByte(fp, (sbyte)huditem->alpha); + + cf_WriteShort(fp, (short)huditem->buffer_size); + cf_WriteString(fp, huditem->data.text); + mprintf((0, "sg: saved customtext2 (%x,%x,bufsize=%d)\n", huditem->x, huditem->y, huditem->buffer_size)); + } + else if (huditem->type == HUD_ITEM_TIMER) { + cf_WriteShort(fp, (short)huditem->stat); + cf_WriteByte(fp, (sbyte)huditem->type); + cf_WriteShort(fp, huditem->x); + cf_WriteShort(fp, huditem->y); + cf_WriteInt(fp, huditem->color); + cf_WriteShort(fp, (short)huditem->flags); + cf_WriteByte(fp, (sbyte)huditem->alpha); + + cf_WriteInt(fp, huditem->data.timer_handle); + mprintf((0, "sg: restored timer (%x,%x,timer_hndl=%d)\n", huditem->x, huditem->y, huditem->data.timer_handle)); + } + else if (huditem->type == HUD_ITEM_CUSTOMTEXT) { + // commented out because persistent hud messages are custom text, and its a mess to save the current + // state of hud persistent messages. + // cf_WriteShort(fp, (short)huditem->stat); + // cf_WriteByte(fp, (sbyte)huditem->type); + // cf_WriteShort(fp, huditem->x); + // cf_WriteShort(fp, huditem->y); + // cf_WriteInt(fp, huditem->color); + // cf_WriteShort(fp, (short)huditem->flags); + // cf_WriteByte(fp, (sbyte)huditem->alpha); + // + // cf_WriteString(fp, huditem->data.timer_handle); + } + } + } + + cf_WriteShort(fp, 0); // mark end of saved hud items. + +} + + +bool LGSHudState(CFILE *fp) +{ +// restore hud items + ushort stat_mask = 0; + + while ((stat_mask = (ushort)cf_ReadShort(fp)) != 0) + { + tHUDItem huditem; + ubyte item_type = (ubyte)cf_ReadByte(fp); + + memset(&huditem, 0, sizeof(huditem)); + huditem.type = item_type; + huditem.stat = stat_mask; + + switch (item_type) + { + char *buffer; + extern void RenderHUDTimer(tHUDItem *item); + + case HUD_ITEM_CUSTOMTEXT2: + huditem.x = cf_ReadShort(fp); + huditem.y = cf_ReadShort(fp); + huditem.color = (ddgr_color)cf_ReadInt(fp); + huditem.flags = (ushort)cf_ReadShort(fp); + huditem.alpha = (ubyte)cf_ReadByte(fp); + + huditem.buffer_size = (int)cf_ReadShort(fp); + huditem.render_fn = NULL; // use pointer to function void (*fn)(struct tHUDItem *) + AddHUDItem(&huditem); + + buffer = (char *)mem_malloc(huditem.buffer_size); + cf_ReadString(buffer, huditem.buffer_size, fp); + UpdateCustomtext2HUDItem(buffer); + mem_free(buffer); + mprintf((0, "lg: restored customtext2 (%x,%x,bufsize=%d)\n", huditem.x, huditem.y, huditem.buffer_size)); + break; + + case HUD_ITEM_TIMER: + huditem.x = cf_ReadShort(fp); + huditem.y = cf_ReadShort(fp); + huditem.color = (ddgr_color)cf_ReadInt(fp); + huditem.flags = (ushort)cf_ReadShort(fp); + huditem.alpha = (ubyte)cf_ReadByte(fp); + + huditem.data.timer_handle = cf_ReadInt(fp); + huditem.render_fn = RenderHUDTimer; // use pointer to function void (*fn)(struct tHUDItem *) + AddHUDItem(&huditem); + mprintf((0, "lg: restored timer (%x,%x,timer_hndl=%d)\n", huditem.x, huditem.y, huditem.data.timer_handle)); + break; + + //case HUD_ITEM_CUSTOMTEXT: + // break; + default: + return false; + } + } + + return true; +} + + + +// frees hud items based off their type class. +void FreeHUDItem(int item) +{ + ASSERT(item < MAX_HUD_ITEMS); + + HUD_array[item].stat = 0; + HUD_array[item].flags = 0; + HUD_array[item].id = HUD_INVALID_ID; + if (((HUD_array[item].type == HUD_ITEM_CUSTOMTEXT) || (HUD_array[item].type == HUD_ITEM_CUSTOMTEXT2)) && HUD_array[item].data.text) { + mem_free(HUD_array[item].data.text); + HUD_array[item].data.text = NULL; + } +} + +extern void RenderHUDScore(tHUDItem *item); + +// initializes non configurable hud items. +void InitDefaultHUDItems() +{ + tHUDItem huditem; + + memset(&huditem, 0, sizeof(huditem)); + huditem.type = HUD_ITEM_SCORE; // KEEP THIS + huditem.stat = STAT_SCORE; // This is bound to timer. in release builds, no timer code but just score + huditem.x = 640; // position on HUD in 640,480 coordinates + huditem.y = 5; + huditem.color = HUD_COLOR; // you can ignore this if you dont use this value passed into your render function + huditem.render_fn = RenderHUDScore; // use pointer to function void (*fn)(struct tHUDItem *) + AddHUDItem(&huditem); +} + + +// loads in hud configuration file, adds hud items. +void LoadHUDConfig(const char *filename, bool (*fn)(const char *,const char *, void*), void *ext_data) +{ + CFILE *fp; + char srcline[128]; // One line of mission source + bool text_pos=false; // text position defined? + +// start over. + ResetHUD(); + InitDefaultHUDItems(); + +// open file + fp = cfopen(filename, "rt"); + if (!fp) { + mprintf((0, "Unable to find hud.inf file.\n")); + return; + } + +// check if valid cockpit file + cf_ReadString(srcline, sizeof(srcline), fp); + + if (strcmpi(srcline, "[hud file]") == 0) { + tHUDItem hud_item; + + memset(&hud_item, 0, sizeof(hud_item)); + hud_item.alpha = HUD_ALPHA; + hud_item.color = HUD_COLOR; + hud_item.tcolor = HUD_COLOR; + + while (!cfeof(fp)) + { + char command[32]; // Command read from line. + char operand[96]; // operand read from line + char *keyword; // parsed keyword + int readcount; // read-in count + + readcount = cf_ReadString(srcline, sizeof(srcline), fp); + if (readcount) { + // we have a line of source. parse out primary keyword + // then parse out remainder. + keyword = strtok(srcline, " \t="); + CleanupStr(command, srcline, sizeof(command)); + CleanupStr(operand, srcline+strlen(command)+1, sizeof(operand)); + + // skip comments + if (command[0] == '@') + continue; + + // find command, + if (!strcmpi(command, "type")) { + // get operand. + hud_item.type = atoi(operand); + } + else if (!strcmpi(command, "pos")) { + // get two numbers from line + int ix,iy; + sscanf(operand, "%d,%d", &ix, &iy); + hud_item.x = ix; + hud_item.y = iy; + } + else if (!strcmpi(command, "posA")) { + // get two numbers from line + int ix,iy; + sscanf(operand, "%d,%d", &ix, &iy); + hud_item.xa = ix; + hud_item.ya = iy; + } + else if (!strcmpi(command, "posB")) { + // get two numbers from line + int ix,iy; + sscanf(operand, "%d,%d", &ix, &iy); + hud_item.xb = ix; + hud_item.yb = iy; + } + else if (!strcmpi(command, "grscale")) { + sscanf(operand, "%f,%f", &hud_item.grscalex,&hud_item.grscaley); + } + else if (!strcmpi(command, "textpos")) { + // get two numbers from line + int ix,iy; + sscanf(operand, "%d,%d", &ix, &iy); + hud_item.tx = ix; + hud_item.ty = iy; + text_pos = true; + } + else if (!strcmpi(command, "alpha")) { + // get alpha value. + hud_item.alpha = atoi(operand); + } + else if (!strcmpi(command, "sat")) { + // saturation count + hud_item.saturation_count = atoi(operand); + } + else if (!strcmpi(command, "rgb")) { + // saturation count + int r,g,b; + sscanf(operand, "%d,%d,%d", &r, &g, &b); + hud_item.color = GR_RGB(r,g,b); + } + else if (!strcmpi(command, "textrgb")) { + int r,g,b; + sscanf(operand, "%d,%d,%d", &r, &g, &b); + hud_item.tcolor = GR_RGB(r,g,b); + } + else if (!strcmpi(command, "special")) { + hud_item.stat |= STAT_SPECIAL; + } + else if (!strcmpi(command, "create")) { + // create hud item. + if (!text_pos) { + hud_item.tx = hud_item.x; + hud_item.ty = hud_item.y; + } + hud_item.render_fn = HudDisplayRouter; + AddHUDItem(&hud_item); + + // reset hud item. + memset(&hud_item, 0, sizeof(hud_item)); + hud_item.alpha = HUD_ALPHA; + hud_item.color = HUD_COLOR; + hud_item.tcolor = HUD_COLOR; + text_pos = false; + } + else if (!strcmpi(command, "reticleprefix")) { + // copy prefix of reticle bitmaps. + strcpy(Reticle_prefix, operand); + } + else if (!strcmpi(command, "reticleoffset")) { + int x, y; + sscanf(operand, "%d,%d", &x, &y); + Ret_x_off = (short)x; + Ret_y_off = (short)y; + } + else if (fn && (*fn)(command, operand, ext_data)) { + continue; + } + else { + mprintf((0,"Error reading hud file.\n")); + Int3(); // contact samir. + break; + } + } + } + } + else { + mprintf((0,"Not a valid hud file.\n")); + } + +// use any reticle specified. + FreeReticle(); + InitReticle(0,0); + + cfclose(fp); +} + + + +////////////////////////////////////////////////////////////////////////////// +// render cockpit and gauges frame +// checks to see what cockpit mode we are in. +// draws stuff accordingly. +// render internal gauges too. + +void RenderHUDFrame() +{ + extern bool Guided_missile_smallview; // from smallviews.cpp + + rend_SetOverlayType(OT_NONE); + +// determine hud rendering properties. + Hud_aspect_x = (float)Game_window_w/DEFAULT_HUD_WIDTH; + Hud_aspect_y = (float)Game_window_h/DEFAULT_HUD_HEIGHT; + Small_hud_flag = (((float)Game_window_w/(float)Max_window_w) <= 0.80f) ? true : false; + +// render special missile hud if available + if (Players[Player_num].guided_obj && !Guided_missile_smallview) { + if(!Cinematic_inuse) + RenderMissileReticle(); + } + else if (Players[Player_num].flags & PLAYER_FLAGS_ZOOMED) { + if(!Cinematic_inuse) + RenderZoomReticle(); + } + else if (! (Players[Player_num].flags & PLAYER_FLAGS_REARVIEW)) { + switch (GetHUDMode()) + { + case HUD_FULLSCREEN: + RenderHUDItems(Hud_stat_mask); + RenderCockpit(); // needed to render animated deactivation sequence and should be dormant + if (Game_toggles.show_reticle) { + RenderReticle(); + } + break; + + case HUD_COCKPIT: + RenderHUDItems(Hud_stat_mask); + RenderCockpit(); // called when cockpit is activating and functioning. + if (Game_toggles.show_reticle) { + RenderReticle(); + } + break; + + case HUD_LETTERBOX: + if(!Cinematic_inuse) + RenderHUDItems(Hud_stat_mask); + break; + + case HUD_OBSERVER: + if(!Cinematic_inuse) + RenderHUDItems(Hud_stat_mask); + break; + + default: + Int3(); + } + } + + // Do dll stuff + CallGameDLL (EVT_CLIENT_HUD_INTERVAL,&DLLInfo); + + rend_SetZBufferState (1); + mprintf_at((2,0,0,"FPS: %f",GetFPS())); +} + + +// renders hud frame before any graphics are drawn +void RenderPreHUDFrame() +{ + extern void RenderHUDMsgDirtyRects(); + +// render any dirty rectangles if small hud flag is set + if (Small_hud_flag) { + int i; + + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if ((Hud_stat_mask & HUD_array[i].stat) && (HUD_array[i].flags & HUD_FLAG_SMALL)) { + HUD_array[i].dirty_rect.fill(GR_BLACK); + } + } + + // messages. + if ((Hud_stat_mask & STAT_MESSAGES)) { + RenderHUDMsgDirtyRects(); + } + } +} + + + +extern MsgListConsole Game_msg_con; +extern MsgListConsole HUD_msg_con; + +// render auxillary hud +void RenderAuxHUDFrame() +{ +// take care of any 'small hud' stuff +// render hud array list for items drawn differently on a smaller hud. + if (Small_hud_flag) { + int cur_game_win_w = Game_window_w; + int cur_game_win_h = Game_window_h; + int cur_game_win_x = Game_window_x; + int cur_game_win_y = Game_window_y; + int i; + ushort stat_mask = Hud_stat_mask; + + // emulating hud that takes up entire screen + Game_window_w = Max_window_w; + Game_window_h = Max_window_h; + Game_window_x = 0; + Game_window_y = 0; + + // determine hud rendering properties. + Hud_aspect_x = (float)Game_window_w/DEFAULT_HUD_WIDTH; + Hud_aspect_y = (float)Game_window_h/DEFAULT_HUD_HEIGHT; + + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if ((stat_mask & HUD_array[i].stat) && (HUD_array[i].flags & HUD_FLAG_SMALL)) { + if (!HUD_array[i].render_fn) { + HudDisplayRouter(&HUD_array[i]); + } + else { + (*HUD_array[i].render_fn)(&HUD_array[i]); + } + } + } + + // messages. + if ((stat_mask & STAT_MESSAGES)) { + RenderHUDMessages(); + } + + grtext_Flush(); + + Game_window_w = cur_game_win_w; + Game_window_h = cur_game_win_h; + Game_window_x = cur_game_win_x; + Game_window_y = cur_game_win_y; + } + +// render game and hud message consoles. + Game_msg_con.DoInput(); + Game_msg_con.Draw(); + HUD_msg_con.DoInput(); + HUD_msg_con.Draw(); +} + +extern const char *cfg_binding_text(ct_type ctype, ubyte ctrl, ubyte binding); + +char * GetControllerBindingText(int fidcont) +{ + static char *cont_bind_txt; + ct_type ctype[CTLBINDS_PER_FUNC]; + ubyte cfgflags[CTLBINDS_PER_FUNC]; + ct_config_data cfgdata; + tCfgDataParts cfgparts; + if(-1==fidcont) + return NULL; + cont_bind_txt = NULL; + fidcont = CtlFindBinding(fidcont,false); + if(fidcont==-1) //DAJ -1FIX + return NULL; + + Controller->get_controller_function(Cfg_joy_elements[fidcont].fn_id, ctype, &cfgdata, cfgflags); + parse_config_data(&cfgparts, ctype[0], ctype[1], cfgdata); + ubyte one_binding = cfgparts.bind_0; + ubyte one_ctrlbind = cfgparts.ctrl_0; + + cont_bind_txt = (char *)cfg_binding_text(ctype[0], one_ctrlbind, one_binding); + return cont_bind_txt; +} + +char * GetKeyBindingText(int fidkey) +{ + static char *key_bind_txt; + ct_type ctype[CTLBINDS_PER_FUNC]; + ubyte cfgflags[CTLBINDS_PER_FUNC]; + ct_config_data cfgdata; + tCfgDataParts cfgparts; + if(-1==fidkey) + return NULL; + key_bind_txt = NULL; + fidkey = CtlFindBinding(fidkey,true); + if(fidkey==-1) //DAJ -1FIX + return NULL; + + Controller->get_controller_function(Cfg_key_elements[fidkey].fn_id, ctype, &cfgdata, cfgflags); + parse_config_data(&cfgparts, ctype[0], ctype[1], cfgdata); + ubyte one_binding = cfgparts.bind_0; + ubyte one_ctrlbind = cfgparts.ctrl_0; + + key_bind_txt = (char *)cfg_binding_text(ctype[0], one_ctrlbind, one_binding); + return key_bind_txt; +} + +void DoEnabledControlsLine(char *controlp,char *keyp,char *label,int y,char *axis = NULL) +{ + char control_text[200] = ""; + + strcpy(control_text,label); + strcat(control_text," : "); + if(keyp) + strcat(control_text,keyp); + if( keyp && (controlp || axis) ) + strcat(control_text," / "); + if(controlp) + strcat(control_text,controlp); + if(controlp && axis) + strcat(control_text," / "); + if(axis) + strcat(control_text,axis); + + if(keyp || controlp || axis) + RenderHUDTextFlags(0, GR_GREEN, HUD_ALPHA, 0, 30, y, control_text); + +} + +extern bool Demo_make_movie; + +#define HUD_KEYS_NEXT_LINE hudconty+=14 + +// iterate through entire hud item list to draw. +void RenderHUDItems(tStatMask stat_mask) +{ + static float framerate_timer = 0; + static float last_fps; + float font_aspect_x, font_aspect_y; + int i; + + grtext_Reset(); + grtext_SetFont(HUD_FONT); + +// for lores screens, we use different fonts, so DONT SCALE. + font_aspect_x = (float)Game_window_w/Max_window_w; + font_aspect_y = (float)Game_window_h/Max_window_h; + + if (font_aspect_x <= 0.60f) { + grtext_SetFontScale(0.60f); + } + else if (font_aspect_x <= 0.80f) { + grtext_SetFontScale(0.80f); + } + else { + grtext_SetFontScale(1.0f); + } + +// do framerate calculations + framerate_timer -= Frametime; + while (framerate_timer < 0) + { + framerate_timer += FRAMERATE_TIME_DELAY; + last_fps = GetFPS(); + } + +// show framerate text gauge + if (stat_mask & STAT_FPS) { + RenderHUDText(HUD_COLOR, HUD_ALPHA, 0, 10, 10, "FPS: %.1f",last_fps); + } + +// show music spew + +#ifdef _DEBUG + if (Music_debug_verbose) { + int min, sec; + RenderHUDText(HUD_COLOR, HUD_ALPHA, 0, 10, 60, "Music: %s", IsD3MusicOn() ? "ON" : "OFF"); + RenderHUDText(HUD_COLOR, HUD_ALPHA, 0, 10, 72, "Region: %d", D3MusicGetRegion()); + RenderHUDText(HUD_COLOR, HUD_ALPHA, 0, 10, 84, "Song: %s", Music_type_names[Game_music_info.cur_song]); + min = (int)(Game_music_info.peace_timer/60); + sec = (int)((Game_music_info.peace_timer/60.0f - (float)min)*60.0f); + RenderHUDText(HUD_COLOR, HUD_ALPHA, 0, 10, 96, "Peace time: %d:%.2d", min, sec); + + if (Game_music_info.cur_loop_name) { + RenderHUDText(HUD_COLOR, HUD_ALPHA, 0, 10, 108, "Loop: %s", Game_music_info.cur_loop_name); + } + } + +// show timer + if (1) { + // timer always displayed in debug builds. + int min, sec, y; + + min = (int)(Gametime/60); + sec = (int)((Gametime/60.0f - (float)min)*60.0f); + + y = (Game_mode & GM_MULTI) ? 10 : 40; + + RenderHUDText(HUD_COLOR, HUD_ALPHA, 0, 605,y, "%d:%.2d", min, sec); + } +#endif + +// render hud array list. + for (i = 0; i < MAX_HUD_ITEMS; i++) + { + if ((HUD_array[i].flags & HUD_FLAG_SMALL) && Small_hud_flag) { + continue; // skip items renderered differently on a small hud if we are in a small hud (see RenderAUXHUDFrame) + } + if ((stat_mask & HUD_array[i].stat)) { + if (!HUD_array[i].render_fn) { + HudDisplayRouter(&HUD_array[i]); + } + else { + (*HUD_array[i].render_fn)(&HUD_array[i]); + } + } + } + if(Demo_flags == DF_RECORDING) + { + RenderHUDTextFlags(HUDTEXT_CENTERED, GR_BLUE, HUD_ALPHA, 0, 10, 300, TXT_RECORDINGDEMO); + } + else if(Demo_flags == DF_PLAYBACK) + { + if(!Demo_make_movie) + { + if(Demo_paused) + { + RenderHUDTextFlags(HUDTEXT_CENTERED, GR_BLUE, HUD_ALPHA, 0, 10, 300, TXT_DEMOPAUSED); + } + else + { + RenderHUDTextFlags(HUDTEXT_CENTERED, GR_BLUE, HUD_ALPHA, 0, 10, 300, TXT_PLAYINGDEMO); + } + } + } + + //This is a big pain in the butt. It's for the training mission. + if(Hud_show_controls && (Players[Player_num].controller_bitflags!=0xffffffff)) + { + char *controlp = NULL; + char *keyp = NULL; + char * axis = NULL; + player *pp = &Players[Player_num]; + int fidkey = -1,fidcont = -1; + int hudconty = 130; + + RenderHUDTextFlags(0, GR_GREEN, HUD_ALPHA, 0, 15, hudconty, TXT_ENABLED_CONTROLS); + HUD_KEYS_NEXT_LINE; + + if(pp->controller_bitflags & PCBF_FORWARD) + { + keyp = GetKeyBindingText(ctfFORWARD_THRUSTKEY); + controlp = GetControllerBindingText(ctfFORWARD_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_FORWARD,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_REVERSE) + { + keyp = GetKeyBindingText(ctfREVERSE_THRUSTKEY); + + controlp = GetControllerBindingText(ctfREVERSE_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_REVERSE,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_LEFT) + { + keyp = GetKeyBindingText(ctfLEFT_THRUSTKEY); + controlp = GetControllerBindingText(ctfLEFT_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_SLIDELEFT,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_RIGHT) + { + keyp = GetKeyBindingText(ctfRIGHT_THRUSTKEY); + controlp = GetControllerBindingText(ctfRIGHT_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_SLIDERIGHT,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_UP) + { + keyp = GetKeyBindingText(ctfUP_THRUSTKEY); + controlp = GetControllerBindingText(ctfUP_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_SLIDEUP,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_DOWN) + { + keyp = GetKeyBindingText(ctfDOWN_THRUSTKEY); + controlp = GetControllerBindingText(ctfDOWN_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_SLIDEDOWN,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_PITCHUP) + { + axis = NULL; + if((pp->controller_bitflags & PCBF_PITCHUP) && (pp->controller_bitflags & PCBF_PITCHDOWN)) + { + axis = GetControllerBindingText(ctfPITCH_DOWNAXIS); + } + keyp = GetKeyBindingText(ctfPITCH_UPKEY); + controlp = GetControllerBindingText(ctfPITCH_UPBUTTON); + if(keyp || controlp || axis) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_PITCHUP,hudconty,axis); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_PITCHDOWN) + { + axis = NULL; + if((pp->controller_bitflags & PCBF_PITCHUP) && (pp->controller_bitflags & PCBF_PITCHDOWN)) + { + axis = GetControllerBindingText(ctfPITCH_DOWNAXIS); + } + keyp = GetKeyBindingText(ctfPITCH_DOWNKEY); + controlp = GetControllerBindingText(ctfPITCH_DOWNBUTTON); + if(keyp || controlp || axis) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_PITCHDOWN,hudconty,axis); + HUD_KEYS_NEXT_LINE; + } + } + /* + else if((pp->controller_bitflags & PCBF_HEADINGLEFT) && (pp->controller_bitflags & PCBF_HEADINGRIGHT)) + { + axis = GetControllerBindingText(ctfHEADING_RIGHTAXIS); + DoEnabledControlsLine(NULL,NULL,"Pitch",hudconty); + HUD_KEYS_NEXT_LINE; + + } + */ + if(pp->controller_bitflags & PCBF_HEADINGLEFT) + { + axis = NULL; + if((pp->controller_bitflags & PCBF_HEADINGLEFT) && (pp->controller_bitflags & PCBF_HEADINGRIGHT)) + { + axis = GetControllerBindingText(ctfHEADING_RIGHTAXIS); + } + keyp = GetKeyBindingText(ctfHEADING_LEFTKEY); + controlp = GetControllerBindingText(ctfHEADING_LEFTBUTTON); + if(keyp || controlp || axis) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_HEADLEFT,hudconty,axis); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_HEADINGRIGHT) + { + axis = NULL; + if((pp->controller_bitflags & PCBF_HEADINGLEFT) && (pp->controller_bitflags & PCBF_HEADINGRIGHT)) + { + axis = GetControllerBindingText(ctfHEADING_RIGHTAXIS); + } + keyp = GetKeyBindingText(ctfHEADING_RIGHTKEY); + controlp = GetControllerBindingText(ctfHEADING_RIGHTBUTTON); + if(keyp || controlp || axis) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_HEADRIGHT,hudconty,axis); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_BANKLEFT) + { + axis = NULL; + if((pp->controller_bitflags & PCBF_BANKLEFT) && (pp->controller_bitflags & PCBF_BANKLEFT)) + { + axis = GetControllerBindingText(ctfBANK_RIGHTAXIS); + } + keyp = GetKeyBindingText(ctfBANK_LEFTKEY); + controlp = GetControllerBindingText(ctfBANK_LEFTBUTTON); + if(keyp || controlp || axis) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_BANKLEFT,hudconty,axis); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_BANKRIGHT) + { + axis = NULL; + if((pp->controller_bitflags & PCBF_BANKLEFT) && (pp->controller_bitflags & PCBF_BANKLEFT)) + { + axis = GetControllerBindingText(ctfBANK_RIGHTAXIS); + } + keyp = GetKeyBindingText(ctfBANK_RIGHTKEY); + controlp = GetControllerBindingText(ctfBANK_RIGHTBUTTON); + if(keyp || controlp || axis) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_BANKRIGHT,hudconty,axis); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_PRIMARY) + { + keyp = GetKeyBindingText(ctfFIREPRIMARY_KEY); + controlp = GetControllerBindingText(ctfFIREPRIMARY_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_PRIMWEAP,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_SECONDARY) + { + keyp = GetKeyBindingText(ctfFIRESECONDARY_KEY); + controlp = GetControllerBindingText(ctfFIRESECONDARY_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_SECWEAP,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + if(pp->controller_bitflags & PCBF_AFTERBURNER) + { + keyp = GetKeyBindingText(ctfAFTERBURN_KEY); + controlp = GetControllerBindingText(ctfAFTERBURN_BUTTON); + if(keyp || controlp) + { + DoEnabledControlsLine(controlp,keyp,TXT_ENABLED_CONT_AFTERBURN,hudconty); + HUD_KEYS_NEXT_LINE; + } + } + } + +// render hud messages. if in a small hud, don't do it now but inside RenderAuxHUDFrame + if ((stat_mask & STAT_MESSAGES) && !Small_hud_flag) { + RenderHUDMessages(); + } +#if 0 + grtext_SetColor(GR_GREEN); + grtext_SetAlpha(128); + grtext_SetFont(BIG_BRIEFING_FONT); + grtext_CenteredPrintf(0,300,"Beta Version"); +#endif + grtext_Flush(); + +} + + + +////////////////////////////////////////////////////////////////////////////// +// RETICLE SYSTEM + +#include "robotfire.h" + +#define RET_IMAGE_WIDTH 32 +#define RET_IMAGE_HEIGHT 32 + +#define MAX_RETICLE_ELEMS 18 +#define RET_CPRIMARY 0 +#define RET_L1PRIMARY 1 +#define RET_R1PRIMARY 2 +#define RET_L2PRIMARY 3 +#define RET_R2PRIMARY 4 +#define RET_CPRIMARY2 5 +#define RET_CSECONDARY 8 +#define RET_L1SECONDARY 9 +#define RET_R1SECONDARY 10 +#define RET_L2SECONDARY 11 +#define RET_R2SECONDARY 12 +#define RET_LGUNSIGHT 16 +#define RET_RGUNSIGHT 17 + +/* +$$TABLE_GAMEFILE "ret_pr0a.ogf" +$$TABLE_GAMEFILE "ret_pr0b.ogf" +$$TABLE_GAMEFILE "ret_pr1b.ogf" +$$TABLE_GAMEFILE "ret_pr2b.ogf" +$$TABLE_GAMEFILE "ret_pr3b.ogf" +$$TABLE_GAMEFILE "ret_pr4b.ogf" +$$TABLE_GAMEFILE "ret_pr13a.ogf" +$$TABLE_GAMEFILE "ret_pr24a.ogf" +$$TABLE_GAMEFILE "ret_se0a.ogf" +$$TABLE_GAMEFILE "ret_se1a.ogf" +$$TABLE_GAMEFILE "ret_se2a.ogf" +$$TABLE_GAMEFILE "ret_se3a.ogf" +$$TABLE_GAMEFILE "ret_se4a.ogf" +$$TABLE_GAMEFILE "ret_se0b.ogf" +$$TABLE_GAMEFILE "ret_se1b.ogf" +$$TABLE_GAMEFILE "ret_se2b.ogf" +$$TABLE_GAMEFILE "ret_se3b.ogf" +$$TABLE_GAMEFILE "ret_se4b.ogf" +$$TABLE_GAMEFILE "ret_lguna.ogf" +$$TABLE_GAMEFILE "ret_rguna.ogf" +$$TABLE_GAMEFILE "ret_pr5a.ogf" +$$TABLE_GAMEFILE "ret_pr5b.ogf" + +$$TABLE_GAMEFILE "phx_pr0a.ogf" +$$TABLE_GAMEFILE "phx_pr0b.ogf" +$$TABLE_GAMEFILE "phx_pr1b.ogf" +$$TABLE_GAMEFILE "phx_pr2b.ogf" +$$TABLE_GAMEFILE "phx_pr3b.ogf" +$$TABLE_GAMEFILE "phx_pr4b.ogf" +$$TABLE_GAMEFILE "phx_pr13a.ogf" +$$TABLE_GAMEFILE "phx_pr24a.ogf" +$$TABLE_GAMEFILE "phx_se0a.ogf" +$$TABLE_GAMEFILE "phx_se1a.ogf" +$$TABLE_GAMEFILE "phx_se2a.ogf" +$$TABLE_GAMEFILE "phx_se3a.ogf" +$$TABLE_GAMEFILE "phx_se4a.ogf" +$$TABLE_GAMEFILE "phx_se0b.ogf" +$$TABLE_GAMEFILE "phx_se1b.ogf" +$$TABLE_GAMEFILE "phx_se2b.ogf" +$$TABLE_GAMEFILE "phx_se3b.ogf" +$$TABLE_GAMEFILE "phx_se4b.ogf" +$$TABLE_GAMEFILE "phx_lguna.ogf" +$$TABLE_GAMEFILE "phx_rguna.ogf" +$$TABLE_GAMEFILE "phx_pr5a.ogf" +$$TABLE_GAMEFILE "phx_pr5b.ogf" + +$$TABLE_GAMEFILE "mag_pr0a.ogf" +$$TABLE_GAMEFILE "mag_pr0b.ogf" +$$TABLE_GAMEFILE "mag_pr1b.ogf" +$$TABLE_GAMEFILE "mag_pr2b.ogf" +$$TABLE_GAMEFILE "mag_pr3b.ogf" +$$TABLE_GAMEFILE "mag_pr4b.ogf" +$$TABLE_GAMEFILE "mag_pr13a.ogf" +$$TABLE_GAMEFILE "mag_pr24a.ogf" +$$TABLE_GAMEFILE "mag_se0a.ogf" +$$TABLE_GAMEFILE "mag_se1a.ogf" +$$TABLE_GAMEFILE "mag_se2a.ogf" +$$TABLE_GAMEFILE "mag_se3a.ogf" +$$TABLE_GAMEFILE "mag_se4a.ogf" +$$TABLE_GAMEFILE "mag_se0b.ogf" +$$TABLE_GAMEFILE "mag_se1b.ogf" +$$TABLE_GAMEFILE "mag_se2b.ogf" +$$TABLE_GAMEFILE "mag_se3b.ogf" +$$TABLE_GAMEFILE "mag_se4b.ogf" +$$TABLE_GAMEFILE "mag_lguna.ogf" +$$TABLE_GAMEFILE "mag_rguna.ogf" +$$TABLE_GAMEFILE "mag_pr5a.ogf" +$$TABLE_GAMEFILE "mag_pr5b.ogf" +*/ + + + +// this should map to the reticle element defines +// column 0 = off bitmap +// column 1 = on bitmap (if any). +// column 2 = left, center or right aligned bitmap. +const char *Reticle_image_names[MAX_RETICLE_ELEMS][3] = { + { "_pr0a.ogf", "_pr0b.ogf", "c" }, + { "_pr13a.ogf", "_pr1b.ogf", "l" }, // gp1 + { "_pr24a.ogf", "_pr2b.ogf", "r" }, // gp2 + { "_pr13a.ogf", "_pr3b.ogf", "l" }, // since off version is same as gp1 + { "_pr24a.ogf", "_pr4b.ogf", "r" }, // since off version is same as gp2 + { "_pr5a.ogf", "_pr5b.ogf", "c"}, // center gunpoint version of gp0 (ammo based, single gp) + { NULL, NULL, "l"}, + { NULL, NULL, "l"}, + { "_se0a.ogf", "_se0b.ogf", "c" }, + { "_se1a.ogf", "_se1b.ogf", "l" }, + { "_se2a.ogf", "_se2b.ogf", "r" }, + { "_se3a.ogf", "_se3b.ogf", "l" }, + { "_se4a.ogf", "_se4b.ogf", "r" }, + { NULL, NULL, "l"}, + { NULL, NULL, "l"}, + { NULL, NULL, "l"}, + { "_lguna.ogf", NULL, "l" }, + { "_rguna.ogf", NULL, "r" } +}; + + + +// reticle slot element array +static struct +{ + int bmp_off; + int bmp_on; + char align; // 'l', 'r', or 'c' (clever, huh?) +} +Reticle_elem_array[MAX_RETICLE_ELEMS]; + + +const int Ret_prim_wb[MAX_WB_GUNPOINTS+1] = { + RET_CPRIMARY, // reserved for NON mass driver + RET_R1PRIMARY, + RET_L1PRIMARY, + RET_R2PRIMARY, + RET_L2PRIMARY, + -1, + -1, + -1, + RET_CPRIMARY2 // reserved for mass driver slot weapons. +}; + +const int Ret_sec_wb[MAX_WB_GUNPOINTS+1] = { + RET_CSECONDARY, + RET_R1SECONDARY, + RET_L1SECONDARY, + RET_R2SECONDARY, + RET_L2SECONDARY, + -1, + -1, + -1, + -1 +}; + +#define RETMASK_FLAG_AUXPRIMARY0 (1<<8) // matches 9th bit (bit 8) of reticle mask + +static ushort Ret_prim_mask = 0; +static ushort Ret_sec_mask = 0; + + + +// Initializes Reticle on Hud. Usually called when weapon changes. +void InitReticle(int primary_slots, int secondary_slots) +{ + int i; + + for (i = 0; i < MAX_RETICLE_ELEMS; i++) + { + char filename[PSFILENAME_LEN+1]; + + if (Reticle_image_names[i][0] && primary_slots >= 0) { + sprintf(filename,"%s%s", Reticle_prefix, Reticle_image_names[i][0]); + Reticle_elem_array[i].bmp_off = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename), 0); + if (Reticle_elem_array[i].bmp_off <= BAD_BITMAP_HANDLE) { + Reticle_elem_array[i].bmp_off = -1; + mprintf((0,"Unable to load %s reticle image.\n", filename)); + } + } + else { + Reticle_elem_array[i].bmp_off = -1; + } + + if (Reticle_image_names[i][1] && primary_slots >= 0) { + sprintf(filename,"%s%s", Reticle_prefix, Reticle_image_names[i][1]); + Reticle_elem_array[i].bmp_on = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename), 0); + if (Reticle_elem_array[i].bmp_on <= BAD_BITMAP_HANDLE) { + mprintf((0,"Unable to load %s reticle image.\n", filename)); + Reticle_elem_array[i].bmp_on = -1; + } + } + else { + Reticle_elem_array[i].bmp_on = -1; + } + + Reticle_elem_array[i].align = Reticle_image_names[i][2][0]; + } +} + + +// frees up bitmaps associated with reticle. +void FreeReticle() +{ + int i; + + for (i = 0; i < MAX_RETICLE_ELEMS; i++) + { + if (Reticle_elem_array[i].bmp_off > BAD_BITMAP_HANDLE) + bm_FreeBitmap(Reticle_elem_array[i].bmp_off); + Reticle_elem_array[i].bmp_off = -1; + if (Reticle_elem_array[i].bmp_on > BAD_BITMAP_HANDLE) + bm_FreeBitmap(Reticle_elem_array[i].bmp_on); + Reticle_elem_array[i].bmp_on = -1; + } +} + + +// resets reticle based off current player object. +// and the ship's weapon configuration +void ResetReticle() +{ + player *player = &Players[Player_num]; + object *pobj = &Objects[player->objnum]; + + ASSERT(player->objnum >= 0); + + // Make sure we're not resetting a non-existent object + if (!pobj || (pobj->type!=OBJ_PLAYER && pobj->type!=OBJ_GHOST && pobj->type!=OBJ_OBSERVER)) + return; + + poly_model *pm= &Poly_models[Objects[player->objnum].rtype.pobj_info.model_num]; + otype_wb_info *prim_wb = &Ships[player->ship_index].static_wb[player->weapon[PW_PRIMARY].index]; + otype_wb_info *sec_wb = &Ships[player->ship_index].static_wb[player->weapon[PW_SECONDARY].index]; + dynamic_wb_info *prim_dyn_wb = &pobj->dynamic_wb[player->weapon[PW_PRIMARY].index]; + + int i, j; + +// assign reticle elements to the Ret_prim_xx array. +// create battery mask + Ret_prim_mask = 0; + +// iterate through all battery masks to determine which gun points occupy the primary weapon +// on the player ship! + for (j = 0; j < prim_wb->num_masks; j++) + { + for(i = 0; i < pm->poly_wb[0].num_gps; i++) + { + if(prim_wb->gp_fire_masks[j] & (1 << i)) { + Ret_prim_mask |= (1<flags & DWBF_QUAD) && (prim_wb->gp_quad_fire_mask & (1<weapon[PW_PRIMARY].index == MASSDRIVER_INDEX && (Ret_prim_mask & (1<<0))) { + // special hack for mass driver weapons + Ret_prim_mask &= (~(1<<0)); + Ret_prim_mask |= RETMASK_FLAG_AUXPRIMARY0; + } + +// iterate through all battery masks to determine which gun points occupy the secondary weapon +// on the player ship! + Ret_sec_mask = 0; + + for (j = 0; j < sec_wb->num_masks; j++) + { + for(i = 0; i < pm->poly_wb[0].num_gps; i++) + { + if(sec_wb->gp_fire_masks[j] & (1 << i)) + Ret_sec_mask |= (1 << i); + } + } +} + + +// creates the reticle display bitmap mask to be used by the reticle renderer. +inline ushort reticle_mask(object *pobj, otype_wb_info *static_wb, int wb_index) +{ + poly_model *pm= &Poly_models[pobj->rtype.pobj_info.model_num]; + dynamic_wb_info *dyn_wb = &pobj->dynamic_wb[wb_index]; + unsigned mask=0; + int i; + +// determine if weapon battery is charged, if not, then return 0, indicating no 'on' weapon baterries + if (!WBIsBatteryReady(pobj, static_wb, wb_index)) + return 0; + + if (static_wb->ammo_usage) { + if (Players[pobj->id].weapon_ammo[wb_index] == 0) + return 0; + } + else if (static_wb->energy_usage) { + if (Players[pobj->id].energy < static_wb->energy_usage) + return 0; + } + +// create battery mask + for(i = 0; i < pm->poly_wb[0].num_gps; i++) + { + if(static_wb->gp_fire_masks[dyn_wb->cur_firing_mask] & (1 << i)) { + mask |= (1 << i); + } + else if ((dyn_wb->flags & DWBF_QUAD) && (static_wb->gp_quad_fire_mask & (1< -1); + if (on_mask & (1 < -1) { + // draw on image based off of alignment to cx,cy and 'align' + switch (align) + { + case 'l': + x = cx - rw; + y = cy - rh/2; + break; + case 'r': + x = cx; + y = cy - rh/2; + break; + case 'c': + x = cx - rw/2; + y = cy - rh/2; + break; + default: + x = cx; + y = cy; + Int3(); + } + + RenderHUDQuad(x, y, rw, rh, 0,0,1,1,bmp_handle, 192); + } + } + } +} + + +// renders the reticle +void RenderReticle() +{ + static ushort primary_index_last_frame = 0xffff; + static bool quad_primary_last_frame = false; + player *player = &Players[Player_num]; + object *pobj = &Objects[player->objnum]; + ship *ship = &Ships[player->ship_index]; + player_weapon *prim_pw = &player->weapon[PW_PRIMARY]; + player_weapon *sec_pw = &player->weapon[PW_SECONDARY]; + int prim_wb_index = prim_pw->index; + int sec_wb_index = sec_pw->index; + otype_wb_info *prim_wb = &ship->static_wb[prim_wb_index]; + otype_wb_info *sec_wb = &ship->static_wb[sec_wb_index]; + dynamic_wb_info *prim_dyn_wb = &pobj->dynamic_wb[prim_wb_index]; + + int cx = Ret_x_off + (FIXED_SCREEN_WIDTH >> 1); + int cy = Ret_y_off + (FIXED_SCREEN_HEIGHT >> 1) + 6; + int rw = RET_IMAGE_WIDTH; + int rh = RET_IMAGE_HEIGHT; + +// quad weapon check hack (any weapon states that change should be noted here.) + if (prim_dyn_wb->flags & DWBF_QUAD) { + if (!quad_primary_last_frame || primary_index_last_frame != prim_wb_index) { + ResetReticle(); + } + quad_primary_last_frame = true; + } + else { + if (quad_primary_last_frame) { + ResetReticle(); + } + quad_primary_last_frame = false; + } + + primary_index_last_frame = prim_wb_index; + +// determine which primary batteries are open. + draw_reticle_sub(cx,cy,rw,rh,reticle_mask(pobj, prim_wb, prim_wb_index), Ret_prim_mask, Ret_prim_wb); + draw_reticle_sub(cx,cy,rw,rh,reticle_mask(pobj, sec_wb, sec_wb_index), Ret_sec_mask, Ret_sec_wb); + + if (Reticle_elem_array[RET_LGUNSIGHT].bmp_off > -1) + RenderHUDQuad(cx - rw, cy - rh/2, rw, rh, 0,0,1,1,Reticle_elem_array[RET_LGUNSIGHT].bmp_off, 192); + + if (Reticle_elem_array[RET_RGUNSIGHT].bmp_off > -1) + RenderHUDQuad(cx, cy - rh/2, rw, rh, 0,0,1,1,Reticle_elem_array[RET_RGUNSIGHT].bmp_off, 192); +} + + +// renders missile reticle +void RenderMissileReticle() +{ +// Crosshair reticle + int cx = Game_window_w/2; + int cy = Game_window_h/2; + + RenderHUDTextFlags(HUDTEXT_CENTERED, GR_RED, HUD_ALPHA, 0, 10, cy-50, TXT_HUD_GUIDED); + grtext_Flush(); + + rend_SetZBufferState (0); + rend_SetFlatColor(GR_GREEN); + + rend_DrawLine(cx - 6, cy, cx+6,cy); + rend_DrawLine(cx, cy-6, cx, cy+6); + rend_SetZBufferState (1); +} + +// renders missile reticle +void RenderZoomReticle() +{ +// Crosshair reticle + int cx = Game_window_w/2; + int cy = Game_window_h/2; + int text_height=grfont_GetHeight(HUD_FONT); + char str[255]; + + RenderHUDTextFlags(HUDTEXT_CENTERED, GR_RED, HUD_ALPHA, 0, 10, cy-50, TXT_HUD_ZOOM); + + sprintf (str,TXT_HUD_ZOOM_UNITS,Players[Player_num].zoom_distance); + + RenderHUDTextFlags(HUDTEXT_CENTERED, GR_RED, HUD_ALPHA, 0, 10, cy-50+text_height, str); + grtext_Flush(); + + rend_SetZBufferState (0); + + if (Players[Player_num].flags & PLAYER_FLAGS_BULLSEYE) + rend_SetFlatColor(GR_RED); + else + rend_SetFlatColor(GR_GREEN); + + rend_DrawLine(cx - 8, cy, cx+8,cy); + rend_DrawLine(cx, cy-8, cx, cy+8); +} + + +/* +// HUD FILE FUNCTIONS +#define HUDCMD_NUM 22 +#define HUDCMD_TYPE 0 +#define HUDCMD_RED 1 +#define HUDCMD_GREEN 2 +#define HUDCMD_BLUE 3 +#define HUDCMD_X 4 +#define HUDCMD_Y 5 +#define HUDCMD_TX 6 +#define HUDCMD_TY 7 +#define HUDCMD_CREATE 8 +#define HUDCMD_SAT 9 +#define HUDCMD_SPECIAL 10 +#define HUDCMD_CKPTMODEL 11 +#define HUDCMD_AFTERBURN 12 +#define HUDCMD_ENERGY 13 +#define HUDCMD_INVPULSE 14 +#define HUDCMD_SHIP 15 +#define HUDCMD_SHIELDIMG0 16 +#define HUDCMD_SHIELDIMG1 17 +#define HUDCMD_SHIELDIMG2 18 +#define HUDCMD_SHIELDIMG3 19 +#define HUDCMD_SHIELDIMG4 20 +#define HUDCMD_ALPHA 21 + +const char *HUDCommands[HUDCMD_NUM] = { + "type", + "r", + "g", + "b", + "x", + "y", + "tx", + "ty", + "create", + "sat", + "special", + "ckptmodel", + "afterburnimg", + "energyimg", + "invpulseimg", + "shipimg", + "shieldimg0", + "shieldimg1", + "shieldimg2", + "shieldimg3", + "shieldimg4", + "alpha" +}; + + +int HUDLex(const char *command) +{ + for (int i = 0; i < HUDCMD_NUM; i++) + if (strcmp(HUDCommands[i], command) == 0) + return i; + + return INFFILE_ERROR; +} + + +// takes a filename containing oms data. +void LoadHudConfig(const char *filename, bool (*fn)(const char*, const char *, void*), void *ext_data) +{ + InfFile inf; + tHUDItem hud_item; + char operand[INFFILE_LINELEN]; // operand + bool txpos = false, typos = false; + ubyte r=GR_COLOR_RED(HUD_COLOR); + ubyte g=GR_COLOR_GREEN(HUD_COLOR); + ubyte b=GR_COLOR_BLUE(HUD_COLOR); + +// start over. + ResetHud(); + + memset(&hud_item, 0, sizeof(hud_item)); + hud_item.alpha = HUD_ALPHA; + +// open file + if (!inf.Open(file, "[hud file]", HUDLex)) { + Warning("Unable to find requested theme %s or bad file.\n", file); + return false; + } + +// check if valid cockpit file + while (inf.ReadLine()) + { + int cmd; + + while ((cmd = inf.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) + { + switch (cmd) + { + case HUDCMD_TYPE: hud_item.type = atoi(operand); break; + case HUDCMD_RED: r=(ubyte)atoi(operand); break; + case HUDCMD_GREEN: g=(ubyte)atoi(operand); break; + case HUDCMD_BLUE: b=(ubyte)atoi(operand); break; + case HUDCMD_X: hud_item.x = atoi(operand); break; + case HUDCMD_Y: hud_item.y = atoi(operand); break; + case HUDCMD_TX: hud_item.tx = atoi(operand); txpos = true; break; + case HUDCMD_TY: hud_item.ty = atoi(operand); typos = true; break; + case HUDCMD_SAT: hud_item.saturation_count = atoi(operand); break; + case HUDCMD_SPECIAL: hud_item.stat |= STAT_SPECIAL; break; + case HUDCMD_ALPHA: hud_item.alpha = atoi(operand); break; + case HUDCMD_CREATE: + hud_item.color = GR_RGB(r,g,b); + if (!txpos) hud_item.tx = hud_item.x; + if (!typos) hud_item.ty = hud_item.y; + hud_item.render_fn = HudDisplayRouter; + AddHudItem(&hud_item); + + memset(&hud_item, 0, sizeof(hud_item)); + hud_item.alpha = HUD_ALPHA; + r=GR_COLOR_RED(HUD_COLOR); + g=GR_COLOR_GREEN(HUD_COLOR); + b=GR_COLOR_BLUE(HUD_COLOR); + txpos = false; + typos = false; + break; + case HUDCMD_AFTERBURN: + case HUDCMD_ENERGY: + case HUDCMD_INVPULSE: + case HUDCMD_SHIP: + case HUDCMD_SHIELDIMG0: + case HUDCMD_SHIELDIMG1: + case HUDCMD_SHIELDIMG2: + case HUDCMD_SHIELDIMG3: + case HUDCMD_SHIELDIMG4: + case HUDCMD_CKPTMODEL: + default: + if (fn && !(*fn)(HUDCommands[cmd], operand, ext_data)) { + goto force_error; + } + } + } + +force_error: + if (cmd == INFFILE_ERROR) + Warning("Error in hud file %s line %d.\n", file, inf.line()); + } + + inf.Close(); + + return true; +} + +*/ \ No newline at end of file diff --git a/Descent3/hud.h b/Descent3/hud.h new file mode 100644 index 000000000..585e02c5e --- /dev/null +++ b/Descent3/hud.h @@ -0,0 +1,671 @@ +/* + * $Logfile: /DescentIII/Main/hud.h $ + * $Revision: 67 $ + * $Date: 7/13/99 12:22a $ + * $Author: Jeff $ + * + * + * + * $Log: /DescentIII/Main/hud.h $ + * + * 67 7/13/99 12:22a Jeff + * correctly externed HudInputMessage, increased size for new tokenized + * text macros + * + * 66 5/21/99 11:15p Samir + * savegames save hud timer and special text states. restore too. + * + * 65 5/21/99 4:06p Matt + * For customtext2 HUD items, store the buffer length in the HUD item. + * + * 64 5/21/99 2:58p Matt + * Fixed mis-defined flag that caused the multiplayer menus to not render. + * + * 63 5/20/99 5:48p Matt + * Added a HUD item flag, for use by Dallas-created items, that makes a + * HUD item persisitent for the duration of one level, but get cleared + * between levels. + * + * 62 5/19/99 11:25a Matt + * Added multisafe functions & Dallas actions for showing a timer on the + * screen and adding custom HUD messages. + * + * 61 5/17/99 6:22p Jeff + * added filtered HUD messages + * + * 60 5/08/99 1:04a Samir + * added timers for key presses in message list boxes. + * + * 59 5/07/99 10:48p Matt + * Made add-HUD functions return true if the message was added, and false + * if it wasn't (because the previous message was the same). + * + * 58 4/28/99 2:14a Samir + * save and load Game messages in savegame. + * + * 57 4/24/99 8:43p Samir + * when shrinking screen hud messages get rendered in black region. + * + * 56 4/06/99 11:39a Samir + * added more formatting options for hud items (added two other full + * screen hud infs for different ships) + * + * 55 3/30/99 2:39p Samir + * added custom config file for fullscreen hud and hacked mass driver + * reticle in special reticle code. + * + * 54 3/26/99 12:46p Samir + * configurable reticle. + * + * 53 3/04/99 7:39p Matt + * Added sound effects to FreeSpace-style persistent HUD messages. + * + * 52 3/03/99 5:34p Matt + * Added fade-out for goal complete messages + * + * 51 2/19/99 2:18p Samir + * AddGameMessage implicitly figures out time and level of message. + * + * 50 2/18/99 5:27p Matt + * Added color parm to AddPersistentHUDMessage() and fixed the timeout. + * + * 49 2/16/99 6:15p Jeff + * pause hud messages when in cinematics + * + * 48 2/06/99 6:59p Jeff + * created RenderHUDGetTextLineWidth and RenderHUDGetTextHeight + * + * 47 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 46 1/29/99 6:29p Samir + * implemented hud scrollback for hud messages. + * + * 45 1/27/99 6:05p Samir + * added scrollback for game messages on HUD. + * + * 44 1/23/99 2:33p Kevin + * Increased hud message length, because we clip to the real width of the + * hud. Also fixed up the multiline code a bit + * + * 43 1/22/99 4:06p Jeff + * added hud messages that can be sent to just teammates or individual + * people + * + * 42 1/11/99 4:08p Jason + * added multiplayer taunt macros + * + * 41 10/22/98 2:40p Samir + * redid HUD sequencing so multiplayer hud stuff works. + * + * 40 10/19/98 11:22p Samir + * added hud observer mode and fixed problems with hud switching and + * screen size. + * + * 39 10/14/98 4:26p Samir + * added persistent hud messages. + * + * 38 10/05/98 11:08a Jason + * implemented player message log + * + * 37 9/08/98 10:28a Samir + * added function to reset hud messages. + * + * 36 8/15/98 10:51p Matt + * Added function ToggleHUDMode(), and made StartHudInputMessage() public. + * + * 35 7/28/98 3:17p Jason + * fixed buffer overrun problem + * + * 34 7/06/98 7:34p Samir + * added countermeasures. + * + * 33 6/24/98 7:38p Samir + * redid graphical/text/cockpit hud item management. + * + * 32 6/17/98 6:31p Samir + * Added anti-grav warning when dying. + * + * 31 6/15/98 6:54p Samir + * added invulnerability and cloak effect. + * + * 30 6/15/98 2:14p Samir + * afterburner hud image scale modified. + * + * 29 5/26/98 5:05p Samir + * cockpit and hud config file now 'unified'. cockpit adds to hudconfig + * load's funtionality, so cockpit info files can contain same syntax as + * hud file. + * + * 28 5/25/98 8:30p Samir + * guided missile reticle and added a hud timer. + * + * 27 5/22/98 6:25p Samir + * implemented a lot of hud items. + * + * 26 5/07/98 6:01p Samir + * allow for certain hud items to not be resettable. + * + * 25 5/07/98 12:23p Samir + * RenderHudText functions added to hud.h + * + * 24 5/06/98 4:33p Samir + * added fixed screen width and height to grdefs.h (default res.) + * + * 23 5/05/98 6:27p Samir + * added hud aspect ratio values and reticle is scaled correctly. + * + * 22 4/28/98 4:26p Samir + * brightened hud., + * + * 21 4/24/98 5:32p Samir + * added reset reticle function. + * + * 20 4/24/98 8:01a Samir + * don't pass zoom argument to Render functions. + * + * 19 4/23/98 4:13a Samir + * new hud system. + * + * 18 4/14/98 9:19p Samir + * made hacked good looking reticle for mag demo. + * + * 17 4/13/98 7:02p Samir + * beginning reticle code. + * + * 16 3/25/98 11:59a Samir + * implemented energy analog fully. + * + * 15 3/23/98 5:36p Jason + * changed number of hud messages displayed at once + * + * 14 3/23/98 4:51p Jason + * incremental checkin for multiplay + * + * 13 3/20/98 8:23p Samir + * new hud and cockpit customization system. + * + * 12 3/19/98 9:19p Samir + * created a hud item type. + * + * 11 3/18/98 6:25p Samir + * Added new STAT_ constants/ + * + * 10 3/17/98 2:37p Samir + * reorg of hud/gauge/cockpit dependencies. + * + * 9 3/16/98 3:53p Jason + * added hud input message stuff + * + * 8 3/16/98 3:30p Samir + * incremental checkin. + * + * 7 3/13/98 12:09p Samir + * made changes to reflect new cockpit.cpp and cockpit.h + * + * 6 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * 5 11/11/97 1:26p Samir + * Added function to modify a hud gauge and initialized some gauges for + * first cockpit. + * + * 4 11/04/97 6:23p Samir + * Lighting cockpit, initializes gauges. + * + * 3 10/28/97 6:37p Samir + * 3d Cockpit draws, bad lighting and no gauges. + * + * 5 5/14/97 11:29a Jason + * added energy and shield gauges to hud + * + * 4 3/05/97 3:03p Jason + * added blinking messages + * + * 3 3/04/97 12:58p Jason + * made hud messages scroll and fade + * + * 2 2/13/97 4:12p Jason + * added hud message test +* +* $NoKeywords: $ +*/ + + + +#ifndef HUD_H +#define HUD_H + + +#include "pstypes.h" +#include "grdefs.h" + +#define MAX_HUD_MESSAGES 3 +#define HUD_MESSAGE_LENGTH 200 +#define HUD_TEXT_LENGTH 32 +#define DEFAULT_HUD_WIDTH (float)FIXED_SCREEN_WIDTH +#define DEFAULT_HUD_HEIGHT (float)FIXED_SCREEN_HEIGHT + +struct object; +struct poly_model; +struct bsp_info; +struct g3Point; + +// type to pass to functions that draw stats +typedef ushort tStatMask; + +struct CFILE; + +// hud (or gauge) stat flag. +#define STAT_FPS 0x0001 +#define STAT_SHIELDS 0x0002 +#define STAT_ENERGY 0x0004 +#define STAT_PRIMARYLOAD 0x0008 +#define STAT_SECONDARYLOAD 0x0010 +#define STAT_MESSAGES 0x0020 +#define STAT_INVENTORY 0x0040 +#define STAT_SHIP 0x0080 +#define STAT_AFTERBURN 0x0100 +#define STAT_WARNING 0x0200 +#define STAT_GOALS 0x0400 +#define STAT_TIMER 0x0800 +#define STAT_CUSTOM 0x1000 +#define STAT_CNTRMEASURE 0x2000 +#define STAT_STANDARD 0x3ffe +#define STAT_ALL 0x3fff + +// is this the special version? (defined by huddisplay.cpp or user equiv.) +#define STAT_SPECIAL 0x4000 +// is this the graphical version? +#define STAT_GRAPHICAL 0x8000 + +// hud modes +typedef enum tHUDMode { + + HUD_FULLSCREEN, + HUD_LETTERBOX, + HUD_COCKPIT, + HUD_OBSERVER +} +tHUDMode; + +#define MAX_HUD_ITEMS 32 +#define HUD_COLOR GR_RGB(0,255,0) +#define HUD_ALPHA 192 +#define HUD_ZOOM 0.56f + +#define HUD_MSG_PERSISTENT_INFINITE 10000.0f +#define HUD_MSG_PERSISTENT_CENTER -1 + +///////////////////////////////////////////////////////////////////////////////// + +// Max characters in a hud input message +#define MAX_HUD_INPUT_LEN 80 + +extern char HudInputMessage[MAX_HUD_INPUT_LEN]; +extern int HudInputMessageLen; + +extern char Hud_messages[MAX_HUD_MESSAGES][HUD_MESSAGE_LENGTH]; +extern int Num_hud_messages; + +// Whether or not we're in a input message frame +extern int Doing_input_message; + +// determines what's displayable on the hud +extern tStatMask Hud_stat_mask; + +// hud aspect ratio +extern float Hud_aspect_x; +extern float Hud_aspect_y; + +// normalize hud coordinates +#define HUD_X(_x) ((float)(_x)*Hud_aspect_x) +#define HUD_Y(_y) ((float)(_y)*Hud_aspect_y) + +// number of shield frames in gauge +#define NUM_SHIELD_GAUGE_FRAMES 5 +#define SHIELD_GAUGE_FRAME_FULL 0 +#define SHIELD_GAUGE_FRAME_PARTIAL 1 +#define SHIELD_GAUGE_FRAME_HALF 2 +#define SHIELD_GAUGE_FRAME_SERIOUS 3 +#define SHIELD_GAUGE_FRAME_CRITICAL 4 + + +///////////////////////////////////////////////////////////////////////////////// + +// Adds a message to the HUD message list. If the list is already full, punt the +// top one and move the others up +//Returns true if message added, or false if message not (because the previous message was the same) +bool AddHUDMessage (char *format, ... ); +bool AddBlinkingHUDMessage (char *format, ... ); + +// Adds a HUD message (similar to AddHUDMessage), however can be filtered out by +// a "-playermessages" command line. +bool AddFilteredHUDMessage (char *format, ... ); + +// initializes other hud stuff +void SetHUDEnergyImage(const char *energy_img); +void SetHUDAfterburnImage(const char *afterburn_img); + +// Renders all the messages we have in the message list. If HUD_MESSAGE_TIME has +// elapsed, then punt the oldest message and move the others up one +void RenderHUDMessages (); + +// renders ship stats +void RenderHUDStats(tStatMask stat_mask); + +// Handles all incoming keys for an inputted hud message +void DoHUDInputMessageKey (int key); + +//Called when the player hit the Multiplayer message key. +//Puts the player in input mode, or if already inputting resets the hud input message. +void StartHUDInputMessage(); + +//Called when the player hits the Multiplayer team-message key. +//Puts the player in input mode, or if already inputting resets the hud input message. +void StartTeamHUDInputMessage(); + +// Adds a colored message to the hud +//Returns true if message added, or false if message not (because the previous message was the same) +bool AddColoredHUDMessage (ddgr_color color,char *format,...); + +// Adds a HUD message (similar to AddColoredHUDMessage), however can be filtered out by +// a "-playermessages" command line. +bool AddFilteredColoredHUDMessage (ddgr_color color,char *format,...); + +// Initializes Reticle on Hud. Usually called when weapon changes. +void InitReticle(int primary_slots, int secondary_slots); + +// resets reticle to current weapon. +void ResetReticle(); + +//Flags for persistent HUD messages +#define HPF_FADEOUT 1 // message fades out when done +#define HPF_FREESPACE_DRAW 2 // message draws with the FreeSpace briefing effect + +// adds a persistent hud message that is timed, or infinite until removed +// for infinite, time = HUD_MSG_PERSISTENT_INFINITE +// for centering on an axis,set either x or y to HUD_MSG_PERSISTENT_CENTER +void AddPersistentHUDMessage(ddgr_color color,int x, int y, float time, int flags, int sound_index, const char *fmt, ...); + +// removes persistent hud message +void ResetPersistentHUDMessage(); + +// Given a hud message, it will determine who should get this message. +// "name: message" = the player with the callsign name should get the message +// "team: message" = the Player_num's team should get the message +// "0-32: message" = the player with player num of 0-32 should get the message +// all other messages are for everyone else +// +// returns the starting position of the real message (past the :) +// destination will receive one of the following values: +// MULTI_SEND_MESSAGE_ALL = everyone should get this message +// MULTI_SEND_MESSAGE_RED_TEAM = only red team (0) should get this message +// MULTI_SEND_MESSAGE_BLUE_TEAM = only blue team (1) should get this message +// MULTI_SEND_MESSAGE_GREEN_TEAM = only green team (2) should get this message +// MULTI_SEND_MESSAGE_YELLOW_TEAM = only yellow team (3) should get this message +// 0-32 = player num of the player to get the message +char *GetMessageDestination(char *message,int *destination); + + +// HUD System + +// predefined types (tHUDItem::type) +#define HUD_ITEM_PRIMARY 1 +#define HUD_ITEM_SECONDARY 2 +#define HUD_ITEM_SHIELD 3 +#define HUD_ITEM_ENERGY 4 +#define HUD_ITEM_AFTERBURNER 5 +#define HUD_ITEM_INVENTORY 6 +#define HUD_ITEM_SHIPSTATUS 7 +#define HUD_ITEM_WARNINGS 8 +#define HUD_ITEM_GOALS 9 +#define HUD_ITEM_GOALSTATES 10 +#define HUD_ITEM_CNTRMEASURE 11 +#define HUD_ITEM_SCORE 12 +#define HUD_ITEM_CUSTOMTEXT 20 +#define HUD_ITEM_CUSTOMIMAGE 21 +#define HUD_ITEM_TIMER 22 +#define HUD_ITEM_CUSTOMTEXT2 23 // like custom text, keeps string pointer passed in + +#define HUD_FLAG_PERSISTANT 1 // this hud item will not be resetted. +#define HUD_FLAG_SMALL 2 // when small version of hud is rendered, this will render in the AUX frame +#define HUD_FLAG_LEVEL 4 // persistent for the current level, but cleared between levels + +#define HUD_INVALID_ID 255 // hud invalid id constant. + +typedef struct t_dirty_rect +{ + struct + { + short l,t,r,b; + } + r[3]; // three rectangles for each frame buffer (3 max) + + void set(short l0, short t0, short r0, short b0) { r[0].l = l0; r[0].t = t0; r[0].r = r0; r[0].b = b0; }; + void reset(); + void fill(ddgr_color col); +} +tDirtyRect; // dirty rectangle for hud item (small hud version) + + +typedef struct tHUDItem +{ + short x, y; + short xa, ya; // auxillary points + short xb, yb; + short tx, ty; // text version x and y. + float grscalex,grscaley; // used to scale graphics. + + ubyte id; // id number. + ubyte type; // custom of predefined hud item type. + ushort stat; // stat mask (what class of hud items does this one belong to) + + ushort flags; // more flags. + ubyte alpha; // alpha of hud item + ubyte saturation_count; // how saturated is this hud item (how bright). + + ddgr_color color; // color of hud item. + ddgr_color tcolor; // text color of item. + + void (*render_fn)(struct tHUDItem *); // called by hud system to draw the hud item. + + int buffer_size; // for customtext2 item + +// data is initialized depending on 'type'. +// custom types must have either element of data already initialized. +// all predefined types can ignore this data field externally. + union + { + int bm_handle; // custom images + int timer_handle; // timer + char *text; // custom text. + } + data; + + tDirtyRect dirty_rect; // used in small version of hud to clear only 'dirty' area +} +tHUDItem; + +// hud resources +struct sHUDResources +{ + char hud_inf_name[PSFILENAME_LEN+1]; + int arrow_bmp; + int goal_complete_bmp; + int goal_bmp; + int lock_bmp[2]; + int wpn_bmp; + int ship_bmp; + int energy_bmp; + int shield_bmp[NUM_SHIELD_GAUGE_FRAMES]; + int invpulse_bmp; + int dot_bmp; + int afterburn_bmp; + int antigrav_bmp[2]; +}; + +#define HUD_ARROW_SCALE 0.20f +#define HUD_WPN_SCALE 0.75f +#define HUD_SHIP_SCALE 0.33f +#define HUD_ENERGY_SCALE 0.50f +#define HUD_SHIELD_SCALE 0.625f +#define HUD_DOT_SCALE 0.20f +#define HUD_LOCK_SCALE 0.5f +#define HUD_BURN_SCALE 0.5f + +#define HUD_CLOAKEND_TIME 3.0f + +extern struct sHUDResources HUD_resources; +extern bool Small_hud_flag; + +// initializes hud system. +void InitHUD(); + +// initializes hud for ship. +void InitShipHUD(int ship); + +// closes hud. +void CloseHUD(); + +// closes hud for current ship +void CloseShipHUD(); + +// manually sets the cockpit display. +void SetHUDMode(tHUDMode mode); + +// toggle the hud between cockput & fullscreen modes +void ToggleHUDMode(); + +// sets the hud item state(what's visible, how it's drawn, etc) +void SetHUDState(ushort hud_mask, ushort hud_gr_mask); + +// the current cockpit mode; +tHUDMode GetHUDMode(); + +// places an item on the hud +void AddHUDItem(tHUDItem *item); + +// frees hud items based off their type class. +void FreeHUDItem(int item); + +//Updates the customtext2 item, if there is one +void UpdateCustomtext2HUDItem(char *text); + +// resets hud +void ResetHUD(); + +// loads in hud configuration file, adds hud items. +void LoadHUDConfig(const char *filename, bool (*fn)(const char *,const char *, void *) = NULL, void *data=NULL); + +// render cockpit and gauges frame +void RenderHUDFrame(); + +// renders hud frame before any graphics are drawn +void RenderPreHUDFrame(); + +// render auxillary hud +void RenderAuxHUDFrame(); + +// savegame system hooks +void SGSHudState(CFILE *fp); +bool LGSHudState(CFILE *fp); + +// returns scaled line width +int RenderHUDGetTextLineWidth(char *string); + +// returns scaled text height +int RenderHUDGetTextHeight(char *string); + +// renders a bitmap onto the hud +void RenderHUDQuad(int x, int y, int w, int h, float u0, float v0, float u1, float v1, int bm, ubyte alpha, int sat_count=0); + +// renders text, scaled, alphaed, saturated, +void RenderHUDText(ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *fmt, ...); + +// flags for RenderHudText. +#define HUDTEXT_CENTERED 1 + +// renders text, scaled, alphaed, saturated, +void RenderHUDTextFlags(int flags, ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *fmt, ...); + +// reset hud messages. +void ResetHUDMessages(); + +// Sends off the input message the player was typing +void SendOffHUDInputMessage (); + +// reset hud message list. +void ResetHUDMessages(); + +// opens a hud rollback console. +void OpenHUDMessageConsole(); + +// closes hud rollback console +void CloseHUDMessageConsole(); + +// toggles hud rollback console +void ToggleHUDMessageConsole(); + +// resets game message list to no messages +void ResetGameMessages(); +void AddGameMessage(const char *msg); +void SGSGameMessages(CFILE *fp); +void LGSGameMessages(CFILE *fp); + +// toggles game message console (you usually call this.) +void ToggleGameMessageConsole(); + +// game message console +void OpenGameMessageConsole(); +void CloseGameMessageConsole(); + +// pause/unpause hud message rendering +void HUDPauseMessages(void); +void HUDUnpauseMessages(void); + + +///////////////////////////////////////////////////////////////////////// +// Message Console System +///////////////////////////////////////////////////////////////////////// + +struct tMsgList +{ + int m_nmsg, m_limit; + char **m_msg; + + tMsgList(); + void set_limit(int limit) { m_limit = limit; }; + bool add(const char *msg, ubyte lvl=0, ubyte hr=0, ubyte min=0, ubyte sec=0); + const char *get(int i); + void reset(); +}; + + +////////////////////////////////////////////////////////////////////////////// + +class MsgListConsole +{ + tMsgList *m_list; + int m_x, m_y, m_w, m_h; + int m_bufline, m_buflen, m_curmsgs, m_numlines; + bool m_opened; + char *m_buffer; + char m_title[32]; + char **m_conlines; + int n_conlines; + int m_keydownstate; // -1 for up key, 1 for down key, 0 for none. + float m_keydowntime; + +public: + MsgListConsole(); + ~MsgListConsole(); + bool Open(const char *title, int x, int y, int w, int h); // registers dimensions of box + void Close(); + void AttachMsgList(tMsgList *msglist); + void Draw(); + void DoInput(); // do scrolling ui on this console +}; + + +#endif diff --git a/Descent3/huddisplay.cpp b/Descent3/huddisplay.cpp new file mode 100644 index 000000000..92fbd0a15 --- /dev/null +++ b/Descent3/huddisplay.cpp @@ -0,0 +1,1113 @@ +/* + * $Logfile: /DescentIII/main/huddisplay.cpp $ + * $Revision: 59 $ + * $Date: 4/19/00 5:16p $ + * $Author: Matt $ + * + * Hud message and stat display file. + * + * $Log: /DescentIII/main/huddisplay.cpp $ + * + * 59 4/19/00 5:16p Matt + * From Duane for 1.4 + * Removed Mac OpenGL hack + * + * 58 10/21/99 6:39p Matt + * Mac merge + * This change is mostly setting & clearing the variable + * use_opengl_1555_format around some blocks of code. Duane says this + * won't affect the Windows version. + * + * 57 7/28/99 4:11p Kevin + * Mac + * + * 56 6/10/99 9:07p Samir + * fixed weapon string name localization issues. + * + * 55 5/20/99 12:38p Matt + * Fixed centering of the timer HUD display. + * + * 54 5/20/99 2:26a Matt + * Don't use sprintf() for custom_text2 type items, so imbedded percents + * work. + * + * 53 5/19/99 1:23p Matt + * Moved the timer display down a bit, and made the time round up. + * + * 52 5/19/99 11:25a Matt + * Added multisafe functions & Dallas actions for showing a timer on the + * screen and adding custom HUD messages. + * + * 51 5/13/99 7:00p Samir + * print score in co-op or robo-anarchy + * + * 50 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 49 5/09/99 6:21a Jeff + * added missing restore of ZBuffer state + * + * 48 5/05/99 5:46p Samir + * fixed RenderHUDGetTextWidth. Hopefully didn't break for higher + * resolutions. + * + * 47 4/29/99 5:08p Jason + * fixed wrapping bug with RenderHudQuad + * + * 46 4/28/99 5:23a Jeff + * use new inventory function that gets a list of inventory items to + * display inventory and countermeasure on the hud (fixes weird selection + * bug) + * + * 45 4/24/99 10:38p Samir + * cleaned up hud text problems in 'small mode' + * + * 44 4/24/99 8:43p Samir + * when shrinking screen hud messages get rendered in black region. + * + * 43 4/22/99 11:19a Samir + * render hud text functions shouldn't nest formatted text. + * + * 42 4/21/99 9:29p Samir + * don't draw score in multiplayer. + * + * 41 4/20/99 3:06p Jeff + * fixed inventory displaying of non-usable items + * + * 40 4/20/99 1:14p Samir + * non usable hud inventory. + * + * 39 4/20/99 11:47a Samir + * shrinking hud fixes. + * + * 38 4/16/99 10:54p Jeff + * fixed min() for Linux + * + * 37 4/06/99 6:02p Matt + * Added score system + * + * 36 4/06/99 11:39a Samir + * added more formatting options for hud items (added two other full + * screen hud infs for different ships) + * + * 35 3/02/99 6:26p Samir + * hires font madness. + * + * 34 3/01/99 12:55a Matt + * Deleted some obsolete strings from the string table, and moved some + * formatting info from the table to the code. + * + * 33 2/06/99 6:59p Jeff + * created RenderHUDGetTextLineWidth and RenderHUDGetTextHeight + * + * 32 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 31 1/29/99 2:08p Jeff + * localization + * + * 30 11/01/98 1:57a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 29 10/22/98 11:30p Matt + * Centered the names of non-ammo weapon names with respect to the weapon + * icon. + * + * 28 10/20/98 1:46p Samir + * made text version of inventory. + * + * 27 10/14/98 4:27p Samir + * improved HUD_ITEM_CUSTOMTEXT + * + * 26 10/13/98 4:16p Matt + * Fixed stupid bug + * + * 25 10/13/98 12:56p Matt + * Finished (hopefully) with the ammo system. Added support for napalm + * fuel. + * + * 24 9/30/98 4:34p Samir + * hack to prevent display of lock warning at Gametime = 0. + * + * 23 8/25/98 7:08p Samir + * hud primary weapon ammo. + * + * 22 8/07/98 12:54p Jeff + * removed () in countermeasures :) + * + * 21 8/07/98 12:54p Jeff + * inventory doesn't show count if count==1 countermeasure have () around + * count + * + * 20 7/06/98 7:34p Samir + * added countermeasures. + * + * 19 6/24/98 7:38p Samir + * redid graphical/text/cockpit hud item management. + * + * 18 6/17/98 6:31p Samir + * Added anti-grav warning when dying. + * + * 17 6/16/98 11:10a Jeff + * fixed merges goofs + * + * 16 6/16/98 10:54a Jeff + * + * 15 6/15/98 6:54p Samir + * added invulnerability and cloak effect. + * + * 14 6/15/98 2:14p Samir + * afterburner hud image scale modified. + * + * 13 6/10/98 4:03p Samir + * restored old hud config. + * + * 12 5/26/98 8:01p Samir + * don't display placeholder small icon for weapon. + * + * 11 5/26/98 5:06p Samir + * repositioned shield and energy sub hud items due to new design. + * + * 10 5/25/98 8:31p Samir + * added call to render hud text with cerntering options. + * + * 9 5/25/98 6:38p Matt + * Added needed include. + * + * 8 5/22/98 6:25p Samir + * implemented a lot of hud items. + * + * 7 5/07/98 12:23p Samir + * RenderHUDText functions added to hud.h + * + * 6 5/05/98 6:28p Samir + * moved hud_x and hud_y macros to hud.h. + * + * 5 4/27/98 1:14p Jason + * cleaned up afterburner stuff + * + * 4 4/24/98 5:32p Samir + * added saturation option to DrawHudQuad. + * + * 3 4/24/98 8:02a Samir + * display weapon names properly. + * + * 2 4/23/98 4:13a Samir + * new hud system. + * + * 1 4/23/98 4:05a Samir + * initial revision. + * + * $NoKeywords: $ + */ + +#include "pstring.h" +#include "hud.h" +#include "grtext.h" +#include "gamefont.h" +#include "renderer.h" +#include "pserror.h" +#include "player.h" +#include "game.h" +#include "weapon.h" +#include "gametexture.h" +#include "stringtable.h" +#include "ship.h" +#include "config.h" +#include "multi.h" +#include "render.h" + +#include +#include + +#ifdef __LINUX__ +#define min(a,b) ((aindex); + int bmp; + + if (wpn) { + bmp = GetTextureBitmap(wpn->icon_handle, 0); + if (bmp > 0) + return bmp; + } + + return HUD_resources.wpn_bmp; +} + + +void RenderHUDTextFlagsNoFormat(int flags, ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *str); + +////////////////////////////////////////////////////////////////////////////// +// Hud item display routines. + +void HudDisplayRouter(tHUDItem *item) +{ + grtext_SetAlpha(192); + grtext_SetFlags(0); + + switch (item->type) + { + case HUD_ITEM_PRIMARY: + RenderHUDPrimary(item); + break; + + case HUD_ITEM_SECONDARY: + RenderHUDSecondary(item); + break; + + case HUD_ITEM_SHIELD: + RenderHUDShieldValue(item); + break; + + case HUD_ITEM_ENERGY: + RenderHUDEnergyValue(item); + break; + + case HUD_ITEM_AFTERBURNER: + RenderHUDAfterburner(item); + break; + + case HUD_ITEM_INVENTORY: + RenderHUDInventory(item); + break; + + case HUD_ITEM_SHIPSTATUS: + RenderHUDShipStatus(item); + break; + + case HUD_ITEM_WARNINGS: + RenderHUDWarnings(item); + break; + + case HUD_ITEM_CNTRMEASURE: + RenderHUDCountermeasures(item); + break; + + case HUD_ITEM_CUSTOMTEXT: + if (item->data.text) { + RenderHUDText(item->color,item->alpha,item->saturation_count,item->x, item->y,item->data.text); + } + break; + + case HUD_ITEM_CUSTOMTEXT2: + if (item->data.text) { + RenderHUDTextFlagsNoFormat(0,item->color,item->alpha,item->saturation_count,2,Game_window_h/2,item->data.text); + } + break; + + case HUD_ITEM_CUSTOMIMAGE: + break; + + default: + Int3(); + } +} + + +////////////////////////////////////////////////////////////////////////////// +// Hud item functions. + +//renders the description for the current inventory item +void RenderHUDInventory(tHUDItem *item) +{ + tInvenList ilist[MAX_UNIQUE_INVEN_ITEMS]; + int cur_sel; + int count = Players[Player_num].inventory.GetInventoryItemList(ilist,MAX_UNIQUE_INVEN_ITEMS,&cur_sel); + if(!count) + return; + + int y; + float img_w; + + if(cur_sel!=-1) + { + ASSERT(cur_selx, item->y+2, img_w, bm_h(HUD_resources.arrow_bmp,0)*HUD_ARROW_SCALE, 0,0,1,1,HUD_resources.arrow_bmp, item->alpha, item->saturation_count); + + if(ilist[cur_sel].amount>1) + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, item->y, "%s (%d)", ilist[cur_sel].hud_name,ilist[cur_sel].amount); + else + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, item->y, "%s", ilist[cur_sel].hud_name); + } + } + + // render non usable list. + if(cur_sel!=-1 && ilist[cur_sel].hud_name) + { + y = item->y + RenderHUDGetTextHeight(ilist[cur_sel].hud_name) + 6; + } + else { + y = item->y + 16; + } + + for(int i=0;i1) + { + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, y, "%s (%d)", ilist[i].hud_name,ilist[i].amount); + }else + { + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, y, "%s", ilist[i].hud_name); + } + + y+= RenderHUDGetTextHeight(ilist[i].hud_name); + } + + + /* + Inventory *inventory = &Players[Player_num].inventory; + + if(inventory->Size()) //make sure there is at least one item in the inventory + { + int size, y; + int current_inv_pos = inventory->GetPos(); + float img_w = bm_w(HUD_resources.arrow_bmp,0)*HUD_ARROW_SCALE; + char *name = inventory->GetPosName(); + + if(name && inventory->IsUsable()) { + RenderHUDQuad(item->x, item->y+2, img_w, bm_h(HUD_resources.arrow_bmp,0)*HUD_ARROW_SCALE, 0,0,1,1,HUD_resources.arrow_bmp, + item->alpha, item->saturation_count); + int count = inventory->GetPosCount(); + if(count>1) + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, item->y, "%s (%d)", name,count); + else + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, item->y, "%s", name); + } + + + // render non usable list. + inventory->ResetPos(); + size = inventory->Size(); + if (name) { + y = item->y + RenderHUDGetTextHeight(name) + 6; + } + else { + y = item->y + 16; + } + while (size) + { + if (!inventory->IsUsable()) { + name = inventory->GetPosName(); + if(name) { + int count = inventory->GetPosCount(); + if(count>1) { + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, y, "%s (%d)", name,count); + } + else { + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w, y, "%s", name); + } + y+= RenderHUDGetTextHeight(name); + } + } + inventory->NextPos(true); + size--; + } + + // restore inventory position + inventory->GotoPos(current_inv_pos); + } + */ +} + + +//renders the shield rating for the ship +void RenderHUDShieldValue(tHUDItem *item) +{ + const int SHIELD_NUM_X = 86, SHIELD_NUM_Y = 14; + const int SHIELD_IMG_X = 21, SHIELD_IMG_Y = 57; + const int WARNING_IMG_X = 110, WARNING_IMG_Y = 8; + float alpha_mod = (Objects[Players[Player_num].objnum].shields)/(float)INITIAL_SHIELDS; + + if (alpha_mod > 1.0f) + alpha_mod = 1.0f; + + if (item->stat & STAT_GRAPHICAL) { + // GRAPHICAL VERSION + // display text and warning dot + int img = (int)ceil((1.0f-alpha_mod-0.1f) * NUM_SHIELD_GAUGE_FRAMES); + float grscalex = (item->grscalex != 0.0f) ? item->grscalex : HUD_SHIELD_SCALE; + float grscaley = (item->grscaley != 0.0f) ? item->grscaley : HUD_SHIELD_SCALE; + + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+SHIELD_NUM_X, item->y+SHIELD_NUM_Y, + "%03d",(int)Objects[Players[Player_num].objnum].shields); + + if (alpha_mod <= 0.20f) { + RenderHUDQuad(item->x+WARNING_IMG_X, item->y+WARNING_IMG_Y, + bm_w(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, bm_h(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, + 0,0,1,1, + HUD_resources.dot_bmp, item->alpha, 1); + } + + if (img < NUM_SHIELD_GAUGE_FRAMES) { + if (img < 0) + img = 0; + img = HUD_resources.shield_bmp[img]; + RenderHUDQuad(item->x+SHIELD_IMG_X, item->y+SHIELD_IMG_Y, + bm_w(img,0)*grscalex, bm_h(img,0)*grscaley, + 0,0,1,1,img, + item->alpha, item->saturation_count); + } + } + else if (item->stat & STAT_SPECIAL) { + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x, item->y, + "%03d",(int)Objects[Players[Player_num].objnum].shields); + + if (alpha_mod <= 0.20f) { + RenderHUDQuad(item->x+30, item->y-10, + bm_w(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, bm_h(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, + 0,0,1,1, + HUD_resources.dot_bmp, item->alpha, 1); + } + } + else { + // TEXT VERSION + RenderHUDText(item->tcolor, item->alpha, (alpha_mod<=.20f) ? item->saturation_count+1 : item->saturation_count, + item->tx, item->ty, "%s: %03d", TXT_HUD_SHIELDS, (int)Objects[Players[Player_num].objnum].shields); + } +} + + +//renders the energy rating for the ship +void RenderHUDEnergyValue(tHUDItem *item) +{ + float normalized_energy = (float)Players[Player_num].energy/(float)INITIAL_ENERGY; + +// cap off energy to 1.0 normalized + if (normalized_energy > 1.0f) + normalized_energy = 1.0f; + +// display text and warning dot + if (item->stat & STAT_GRAPHICAL) { + const int ENERGY_NUM_X = 15, ENERGY_NUM_Y = 14; + // int ENERGY_IMG_X = 3, ENERGY_IMG_Y = 74; + // int ENERGY_IMG_X2 = 119; + const int WARNING_IMG_X = 6, WARNING_IMG_Y = 8; + float grscalex = (item->grscalex != 0.0f) ? item->grscalex : HUD_ENERGY_SCALE; + float grscaley = (item->grscaley != 0.0f) ? item->grscaley : HUD_ENERGY_SCALE; + + float img_w = bm_w(HUD_resources.energy_bmp,0)*grscalex; + float img_h = bm_h(HUD_resources.energy_bmp,0)*grscaley; + int img_energy_h; + + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+ENERGY_NUM_X, item->y+ENERGY_NUM_Y, + "%03d", (int)Players[Player_num].energy); + + if (normalized_energy <= 0.20f) { + RenderHUDQuad(item->x+WARNING_IMG_X, item->y+WARNING_IMG_Y, + bm_w(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, bm_h(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, + 0,0,1,1, + HUD_resources.dot_bmp, item->alpha, 1); + } + + img_energy_h = (int)floor((normalized_energy*img_h)+0.5f); + + // draw energy gauge, showing how much energy below 100% you have. draw energy spent faded. + RenderHUDQuad(item->x+item->xa,item->y+item->ya, + img_w, img_h-img_energy_h, 0, 0, 1.0, 1.0f-normalized_energy, + HUD_resources.energy_bmp, item->alpha/4, 0); + + RenderHUDQuad(item->x+item->xb-img_w,item->y+item->yb, + img_w, img_h-img_energy_h, 1.0, 0, 0, 1.0f-normalized_energy, + HUD_resources.energy_bmp, item->alpha/4, 0); + + RenderHUDQuad(item->x+item->xa, item->y+item->ya+(int)(img_h-img_energy_h), + img_w, img_energy_h, + 0,1.0f-normalized_energy,1,1, + HUD_resources.energy_bmp, item->alpha, item->saturation_count); + + RenderHUDQuad(item->x+item->xb-img_w, item->y+item->yb+(int)(img_h-img_energy_h), + img_w, img_energy_h, + 1,1.0f-normalized_energy,0,1, + HUD_resources.energy_bmp, item->alpha, item->saturation_count); + } + else if (item->stat & STAT_SPECIAL) { + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x, item->y, + "%03d", (int)Players[Player_num].energy); + + if (normalized_energy <= 0.20f) { + RenderHUDQuad(item->x-10, item->y-10, + bm_w(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, bm_h(HUD_resources.dot_bmp,0)*HUD_DOT_SCALE, + 0,0,1,1, + HUD_resources.dot_bmp, item->alpha, 1); + } + } + else { + // TEXT VERSION + RenderHUDText(item->tcolor, item->alpha, (normalized_energy<=.20f) ? item->saturation_count+1 : item->saturation_count, + item->tx, item->ty, "%s: %03d", TXT_HUD_ENERGY, (int)Players[Player_num].energy); + } +} + + +// draws the afterburner hud gauge. +void RenderHUDAfterburner(tHUDItem *item) +{ + float val= (Players[Player_num].afterburn_time_left/AFTERBURN_TIME); + char str[8]; + + if (item->stat & STAT_GRAPHICAL) { + const int BURNER_NUM_X = 24; + float grscalex = (item->grscalex != 0.0f) ? item->grscalex : HUD_BURN_SCALE; + float grscaley = (item->grscaley != 0.0f) ? item->grscaley : HUD_BURN_SCALE; + float img_w = bm_w(HUD_resources.afterburn_bmp, 0)*grscalex; + float img_h = bm_h(HUD_resources.afterburn_bmp, 0)*grscaley; + int img_burn_w = (int)floor((val*img_w)+0.5f); + + // draw afterburn spent. + RenderHUDQuad(item->x+item->xa+img_burn_w, item->y+item->ya, + img_w - img_burn_w, img_h, val,0,1, 1, + HUD_resources.afterburn_bmp, item->alpha/4, 0); + + // draw afterburn left + RenderHUDQuad(item->x+item->xa, item->y+item->ya, + img_burn_w, img_h, 0,0,val, 1, + HUD_resources.afterburn_bmp, item->alpha, item->saturation_count); + + sprintf(str, "%d%%", (int)(val*100.0f)); + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+BURNER_NUM_X, item->y, str); + } + else if (item->stat & STAT_SPECIAL) { + sprintf(str, "%d%%", (int)(val*100.0f)); + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x, item->y, str); + } + else { + // TEXT VERSION + RenderHUDText(item->tcolor, item->alpha, (val<=.30f) ? item->saturation_count+1 : item->saturation_count, + item->tx, item->ty, "%s: %d%%", TXT_HUD_AFTERBURNER, (int)(val*100.0f)); + } +} + + +// draws the primary weapon current in. +void RenderHUDPrimary(tHUDItem *item) +{ + int index = Players[Player_num].weapon[PW_PRIMARY].index; + ship *ship = &Ships[Players[Player_num].ship_index]; + otype_wb_info *wb = &ship->static_wb[index]; + const char *text = TXT(Static_weapon_names_msg[index]); + float txt_w = (int)(grtext_GetLineWidth(text)/Hud_aspect_x)+4; + char ammo_string[10]; + + if (!wb) + return; + + if (wb->ammo_usage) { + int ammo = Players[Player_num].weapon_ammo[index]; + if (ship->fire_flags[index] & SFF_TENTHS) + sprintf(ammo_string,"%d.%d",ammo/10,ammo%10); + else + sprintf(ammo_string,"%d",ammo); + } + + if (item->stat & STAT_GRAPHICAL) { + const int WPN_INFO_W = 100; + float grscalex = (item->grscalex != 0.0f) ? item->grscalex : HUD_WPN_SCALE; + float grscaley = (item->grscaley != 0.0f) ? item->grscaley : HUD_WPN_SCALE; + + float img_w = bm_w(HUD_resources.wpn_bmp,0) * grscalex; + int icon; + + int y = item->y; + + if (wb->ammo_usage) { + int w2 = (int)(grtext_GetLineWidth("00000")/Hud_aspect_x)+2; + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+WPN_INFO_W-img_w-w2, y+13, + ammo_string); + } + else + y += 13/2; //If just one line, move it down + + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+WPN_INFO_W-img_w-txt_w, y, + "%s", text); + + icon = get_weapon_icon(Player_num, PW_PRIMARY); + + if (icon != HUD_resources.wpn_bmp) { + RenderHUDQuad(item->x+WPN_INFO_W-img_w, item->y, img_w, bm_h(icon,0)*grscaley,0,0,1,1, + icon, item->alpha, item->saturation_count); + } + } + else if (item->stat & STAT_SPECIAL) { + + } + else { + // TEXT VERSION + if (wb->ammo_usage) { + RenderHUDText(item->tcolor, item->alpha, item->saturation_count, item->tx, item->ty, + "%s %s", text, ammo_string); + } + else { + RenderHUDText(item->tcolor, item->alpha, item->saturation_count, item->tx, item->ty, + "%s", text); + } + } +} + + +// draw secondary weapon current in. +void RenderHUDSecondary(tHUDItem *item) +{ + int index = Players[Player_num].weapon[PW_SECONDARY].index; + ship *ship = &Ships[Players[Player_num].ship_index]; + otype_wb_info *wb = &ship->static_wb[index]; + weapon *wpn = GetWeaponFromIndex(Player_num, index); + const char *text = TXT(Static_weapon_names_msg[index]); + float txt_w = (int)(grtext_GetLineWidth(text)/Hud_aspect_x)+2; + + if (item->stat & STAT_GRAPHICAL) { + int icon; + float grscalex = (item->grscalex != 0.0f) ? item->grscalex : HUD_WPN_SCALE; + float grscaley = (item->grscaley != 0.0f) ? item->grscaley : HUD_WPN_SCALE; + + float img_w = bm_w(HUD_resources.wpn_bmp,0) * grscalex; + + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w+2, item->y, + "%s", text); + if (wb->ammo_usage) { + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+img_w+16, item->y+13, + "%d", Players[Player_num].weapon_ammo[Players[Player_num].weapon[PW_SECONDARY].index]); + } + + icon = get_weapon_icon(Player_num, PW_SECONDARY); + + if (icon != HUD_resources.wpn_bmp) { + RenderHUDQuad(item->x, item->y, img_w, bm_h(icon,0)*grscaley,0,0,1,1, + icon, item->alpha, item->saturation_count); + } + } + else if (item->stat & STAT_SPECIAL) { + + } + else { + // TEXT VERSION + RenderHUDText(item->tcolor, item->alpha, item->saturation_count, item->tx, item->ty, + "%s %d", text, Players[Player_num].weapon_ammo[Players[Player_num].weapon[PW_SECONDARY].index]); + } +} + + +void RenderHUDShipStatus(tHUDItem *item) +{ + float clk_time_frame, inv_time_frame; + ubyte clk_alpha, inv_alpha; + +// render text status + if (Objects[Players[Player_num].objnum].effect_info->type_flags & EF_CLOAKED) { + clk_time_frame = Objects[Players[Player_num].objnum].effect_info->cloak_time; + if (clk_time_frame < HUD_CLOAKEND_TIME) + clk_alpha = 128 - 127*FixCos(65536.0f * (clk_time_frame-(int)clk_time_frame)); + else + clk_alpha = 0; + } + if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) { + // do invulnerablity animation. + inv_time_frame = (Gametime - (int)Gametime); + inv_alpha = (255*inv_time_frame); + } + + + if (item->stat & STAT_GRAPHICAL) { + const int STATUS_IMG_X = 0, STATUS_IMG_Y = 68; + const int STATUS_TXT_X = 8; + const int STATUS_TXT_Y = -1, STATUS_TXT_Y2 = 9; + float grscalex = (item->grscalex != 0.0f) ? item->grscalex : HUD_SHIP_SCALE; + float grscaley = (item->grscaley != 0.0f) ? item->grscaley : HUD_SHIP_SCALE; + + int x = item->x+STATUS_IMG_X; + int y = item->y+STATUS_IMG_Y; + int w = bm_w(HUD_resources.ship_bmp,0)*grscalex; + int h = bm_w(HUD_resources.ship_bmp,0)*grscaley; + + if (Objects[Players[Player_num].objnum].effect_info->type_flags & EF_CLOAKED) { + RenderHUDQuad(x, y, w, h, 0,0,1,1, HUD_resources.ship_bmp, clk_alpha, item->saturation_count); + RenderHUDText(item->color, 255-clk_alpha, item->saturation_count, item->x+STATUS_TXT_X, item->y+STATUS_TXT_Y2, TXT_CLK); + } + else { + // non cloaked ship + RenderHUDQuad(x, y, w, h, 0,0,1,1, HUD_resources.ship_bmp, item->alpha, item->saturation_count); + } + + if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) { + x = x - (w*inv_time_frame*0.5f); + y = y - (h*inv_time_frame*0.5f); + + RenderHUDQuad(x, y, (1.0f+inv_time_frame)*w, (1.0f+inv_time_frame)*h, 0,0,1,1, + HUD_resources.invpulse_bmp, inv_alpha, item->saturation_count); + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x+STATUS_TXT_X+2, item->y+STATUS_TXT_Y, + TXT_INV); + } + } + else if (item->stat & STAT_SPECIAL) { + + } + else { + if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) + RenderHUDText(item->tcolor, 255-clk_alpha, item->saturation_count, item->tx, item->ty, TXT_HUD_INVULN); + if (Objects[Players[Player_num].objnum].effect_info->type_flags & EF_CLOAKED) + RenderHUDText(item->tcolor, 255-clk_alpha, item->saturation_count, item->tx, item->ty+12, TXT_HUD_CLOAKED); + } +} + + +// renders warnings like system failures or missile locks. +void RenderHUDWarnings(tHUDItem *item) +{ + object *playerobj = &Objects[Players[Player_num].objnum]; + + if (item->stat & STAT_GRAPHICAL) { + const int LOCK_IMG_X = 8, LOCK_IMG_Y = 2; + const int ANTIGRAV_X = 8, ANTIGRAV_Y = 64; + + float img_w, img_h; + float grscalex = (item->grscalex != 0.0f) ? item->grscalex : HUD_LOCK_SCALE; + float grscaley = (item->grscaley != 0.0f) ? item->grscaley : HUD_LOCK_SCALE; + + + if (Players[Player_num].last_homing_warning_sound_time == Gametime && Gametime > 0.0f) { + // draw graphical rep of missile lock warning. + img_w = bm_w(HUD_resources.lock_bmp[0],0)*grscalex; + img_h = bm_h(HUD_resources.lock_bmp[1],0)*grscaley; + + RenderHUDQuad(item->x+LOCK_IMG_X, item->y+LOCK_IMG_Y, + img_w, img_h, 0,0,1,1, HUD_resources.lock_bmp[0], item->alpha, item->saturation_count); + RenderHUDQuad(item->x+LOCK_IMG_X+img_w, item->y+LOCK_IMG_Y, + img_w, img_h, 0,0,1,1, HUD_resources.lock_bmp[1], item->alpha, item->saturation_count); + } + + if (playerobj->mtype.phys_info.flags & PF_GRAVITY) { + // draw graphical rep of missile lock warning. + img_w = bm_w(HUD_resources.antigrav_bmp[0],0)*grscalex; + img_h = bm_h(HUD_resources.antigrav_bmp[1],0)*grscaley; + + RenderHUDQuad(item->x+ANTIGRAV_X, item->y+ANTIGRAV_Y, + img_w, img_h, 0,0,1,1, HUD_resources.antigrav_bmp[0], item->alpha, item->saturation_count); + RenderHUDQuad(item->x+ANTIGRAV_X+img_w, item->y+ANTIGRAV_Y, + img_w, img_h, 0,0,1,1, HUD_resources.antigrav_bmp[1], item->alpha, item->saturation_count); + } + } + else { + if (Players[Player_num].last_homing_warning_sound_time == Gametime) + RenderHUDText(item->tcolor, item->alpha, item->saturation_count, item->tx, item->ty, TXT_HUDITM_LOCK); + if (playerobj->mtype.phys_info.flags & PF_GRAVITY) + RenderHUDText(item->tcolor, item->alpha, item->saturation_count, item->tx, item->ty+12, TXT_HUDITM_ANTIGRAV); + } +} + + +// render hud countermeasures +void RenderHUDCountermeasures(tHUDItem *item) +{ + tInvenList ilist[MAX_UNIQUE_INVEN_ITEMS]; + int cur_sel; + int count = Players[Player_num].counter_measures.GetInventoryItemList(ilist,MAX_UNIQUE_INVEN_ITEMS,&cur_sel); + if(!count) + return; + + if(cur_sel!=-1) + { + ASSERT(cur_selstat & STAT_GRAPHICAL) + RenderHUDText(item->color, item->alpha, item->saturation_count, item->x, item->y, "%s %d", ilist[cur_sel].hud_name,ilist[cur_sel].amount); + else + RenderHUDText(item->color, item->alpha, item->saturation_count, item->tx, item->ty, "%s %d", ilist[cur_sel].hud_name, ilist[cur_sel].amount); + } + } +} + + +////////////////////////////////////////////////////////////////////////////// +// Hud rendering functions + +extern tFontTemplate Hud_font_template; // retain hud font template + +// returns scaled line width +int RenderHUDGetTextLineWidth(char *string) +{ + float aspect_x; + int str_width_curfont = grtext_GetLineWidth(string); + + if (grtext_GetFont() == HUD_FONT) { + aspect_x = ((float)grtext_GetLineWidthTemplate(&Hud_font_template, string))/((float)str_width_curfont); + } + else { + aspect_x = 1.0f; + } + + if (aspect_x == 1.0f) { + aspect_x = aspect_x*DEFAULT_HUD_WIDTH/Max_window_w; + } + + return (int)((str_width_curfont*aspect_x)); +} + +// returns scaled text height +int RenderHUDGetTextHeight(char *string) +{ + float aspect_y; + int str_height_curfont = grtext_GetHeight(string); + + if (grtext_GetFont() == HUD_FONT) { + aspect_y = ((float)grtext_GetHeightTemplate(&Hud_font_template, string))/((float)str_height_curfont); + } + else { + aspect_y = 1.0f; + } + + return (int)((str_height_curfont*aspect_y)); +} + +void RenderHUDQuad(int x, int y, int w, int h, float u0, float v0, float u1, float v1, int bm, ubyte alpha, int sat_count) +{ + int i; + + rend_SetZBufferState (0); + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaValue (alpha); + rend_SetLighting (LS_NONE); + rend_SetWrapType (WT_CLAMP); + + if (sat_count) + rend_SetAlphaType (AT_SATURATE_TEXTURE); + else + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + + + x = HUD_X(x); + y = HUD_Y(y); + w = HUD_X(w); + h = HUD_Y(h); + + for (i = 0; i < sat_count+1; i++) + { + rend_DrawScaledBitmap (x,y,x+w,y+h,bm,u0,v0,u1,v1); + } + + rend_SetZBufferState (1); +} + + +// renders text, scaled, alphaed, saturated, +void RenderHUDText(ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *fmt, ...) +{ + va_list arglist; + char buf[128]; + + va_start(arglist,fmt); + Pvsprintf(buf,128,fmt,arglist); + va_end(arglist); + + RenderHUDTextFlagsNoFormat(0, col, alpha, sat_count, x, y, buf); +} + + +// renders text, scaled, alphaed, saturated, +void RenderHUDTextFlagsNoFormat(int flags, ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *str) +{ + int i; + + grtext_SetAlpha(alpha); + + if (sat_count) + grtext_SetFlags(GRTEXTFLAG_SATURATE); + else + grtext_SetFlags(0); + + grtext_SetColor(col); + + x = HUD_X(x); + y = HUD_Y(y); + + for (i = 0; i < sat_count+1; i++) + { + if (flags & HUDTEXT_CENTERED) + grtext_CenteredPrintf(0,y,str); + else + grtext_Puts(x,y,str); + } +} + + +// renders text, scaled, alphaed, saturated, +void RenderHUDTextFlags(int flags, ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *fmt, ...) +{ + va_list arglist; + char buf[128]; + int i; + + va_start(arglist,fmt); + Pvsprintf(buf,128,fmt,arglist); + va_end(arglist); + + grtext_SetAlpha(alpha); + + if (sat_count) + grtext_SetFlags(GRTEXTFLAG_SATURATE); + else + grtext_SetFlags(0); + + grtext_SetColor(col); + + x = HUD_X(x); + y = HUD_Y(y); + + for (i = 0; i < sat_count+1; i++) + { + if (flags & HUDTEXT_CENTERED) + grtext_CenteredPrintf(0,y,buf); + else + grtext_Puts(x,y,buf); + } +} + +//Show the score +void RenderHUDScore(tHUDItem *item) +{ + char buf[100]; + int win_w; + + if (Game_mode & GM_MULTI) { + if (!(Netgame.flags & NF_USE_ROBOTS)) { + return; + } + } + + sprintf(buf,"%s: %d ",TXT_SCORE,Players[Player_num].score); + + win_w = (Max_window_w-Game_window_w)*(Hud_aspect_x); +// if (Game_video_resolution==RES_512X384) { win_w = win_w + 10; } + + + int w = RenderHUDGetTextLineWidth(buf);// * win_w)/(Game_window_w); + RenderHUDText(item->color, HUD_ALPHA, 0, item->x-w-win_w, item->y,buf); + + if (Score_added_timer > 0.0) { + int text_height=grfont_GetHeight(HUD_FONT); + sprintf(buf,"%d ",Score_added); + w = RenderHUDGetTextLineWidth(buf);// * win_w/Game_window_w; + ubyte alpha = min(HUD_ALPHA,HUD_ALPHA * 4 * Score_added_timer / SCORE_ADDED_TIME); + RenderHUDText(item->color,alpha,0,item->x-w-win_w,item->y+text_height,buf); + Score_added_timer -= Frametime; + } +} + +extern float Osiris_TimerTimeRemaining(int handle); + +//Show the timer +void RenderHUDTimer(tHUDItem *item) +{ + float time; + int min,secs; + char buf[100]; + + time = Osiris_TimerTimeRemaining(item->data.timer_handle); + + //If no more time, kill item + if (time < 0) { + item->stat = 0; + return; + } + + //round up + time = ceil(time); + + min = time / 60; + secs = time - (60 * min); + + sprintf(buf,"T-%d:%02d",min,secs); + + int h = grfont_GetHeight(HUD_FONT); + RenderHUDTextFlagsNoFormat(HUDTEXT_CENTERED, item->color, HUD_ALPHA, 0, 0, h*4, buf); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +void t_dirty_rect::fill(ddgr_color col) +{ + int i; + for (i = 2; i >= 0; i--) + { + rend_SetAlphaValue(255); + if (r[i].l >= 0 && r[i].r >= 0) { + rend_FillRect(col, r[i].l, r[i].t, r[i].r, r[i].b); + } + if (i > 0) { + r[i].l = r[i-1].l; + r[i].t = r[i-1].t; + r[i].r = r[i-1].r; + r[i].b = r[i-1].b; + } + } + r[0].l = -1; + r[0].t = -1; + r[0].r = -1; + r[0].b = -1; +} + + +void t_dirty_rect::reset() +{ + int i; + for (i = 0;i < 3; i++) + { + r[i].l = -1; r[i].t = -1; r[i].b = -1; r[i].r = -1; + } +} + + diff --git a/Descent3/hudmessage.cpp b/Descent3/hudmessage.cpp new file mode 100644 index 000000000..84f42b9de --- /dev/null +++ b/Descent3/hudmessage.cpp @@ -0,0 +1,1989 @@ +/* +* $Logfile: /DescentIII/Main/hudmessage.cpp $ +* $Revision: 89 $ +* $Date: 9/22/01 7:14p $ +* $Author: Kevin $ +* +* +* +* $Log: /DescentIII/Main/hudmessage.cpp $ + * + * 89 9/22/01 7:14p Kevin + * Print confirmation messages when sending a private message + * + * 88 4/19/00 5:31p Matt + * From Duane for 1.4 + * Set m_limit class member variable if it had not been properly + * initialized + * + * 87 7/16/99 11:17a Samir + * dirty rects for multi line persistent hud messages. + * + * 86 7/13/99 12:22a Jeff + * correctly externed HudInputMessage, increased size for new tokenized + * text macros + * + * 85 7/09/99 3:49p Jason + * fixed bugs/issues for patch + * + * 84 7/07/99 12:50a Jeff + * if a hud message gets broken up while typing, the state is saved and + * continued on the next line + * + * 83 5/20/99 5:48p Matt + * Added a HUD item flag, for use by Dallas-created items, that makes a + * HUD item persisitent for the duration of one level, but get cleared + * between levels. + * + * 82 5/17/99 6:29p Jeff + * only filter hud messages in multiplayer + * + * 81 5/17/99 6:22p Jeff + * added filtered HUD messages + * + * 80 5/17/99 5:48p Samir + * base scrolling off of time key is down. + * + * 79 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 78 5/08/99 12:54p Matt + * When a HUD message is split over multiple lines, copy the color + * information to subsequent lines. + * + * 77 5/08/99 1:06a Samir + * create dirty rect when clearing persistent hud messages. + * + * 76 5/07/99 10:48p Matt + * Made add-HUD functions return true if the message was added, and false + * if it wasn't (because the previous message was the same). + * + * 75 5/05/99 6:36p Matt + * Added queue system for persistent HUD messages. + * + * 74 5/01/99 5:52p Samir + * removed RenderHudMessages, and redid RenderHUDMessages so it did what + * RenderHudMessages did, resets on screen hud messages. The HUD message + * console is resetted in ResetGameMessages. + * + * 73 4/28/99 2:14a Samir + * save and load Game messages in savegame. + * + * 72 4/27/99 12:32p Samir + * maybe fixed crash when resetting message lists. + * + * 71 4/24/99 10:38p Samir + * cleaned up hud text problems in 'small mode' + * + * 70 4/24/99 8:43p Samir + * when shrinking screen hud messages get rendered in black region. + * + * 69 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 68 4/14/99 12:35p Samir + * localization issues. + * + * 67 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 66 4/09/99 3:04p Kevin + * Set hud message length back to 40 -- I think I meant to change + * something else when I changed this. + * + * 65 3/10/99 2:25p Kevin + * Save/Load and Demo file fixes + * + * 64 3/05/99 3:01p Jeff + * fixed crash when a persistant message is displayed during a IGC + * + * 63 3/05/99 12:46p Matt + * Set text flags & alpha before drawing scrolling text and input message. + * + * 62 3/04/99 8:09p Samir + * when inputting text, don't use formatted print function to output it to + * screen. There's still a problem on the end though when printing out the + * message. + * + * 61 3/04/99 7:39p Matt + * Added sound effects to FreeSpace-style persistent HUD messages. + * + * 60 3/04/99 6:15p Matt + * Added groovy FreeSpace briefing-style text effect to the persistent HUD + * message. + * + * 59 3/03/99 5:34p Matt + * Added fade-out for goal complete messages + * + * 58 3/02/99 11:41p Kevin + * Fixed dedicated server + * + * 57 3/02/99 3:30p Samir + * took out ddio_KeyFlush which caused shift characters to be silenced + * when inputting message. This did not affect other key things as far as + * I know. + * + * 56 2/19/99 2:18p Samir + * AddGameMessage implicitly figures out time and level of message. + * + * 55 2/18/99 5:27p Matt + * Added color parm to AddPersistentHUDMessage() and fixed the timeout. + * + * 54 2/16/99 6:16p Jeff + * pause hud messages when in cinematics + * + * 53 2/16/99 3:52p Kevin + * Hud messages wrap on 70% of the screen width instead of 100% + * + * 52 2/09/99 6:52p Jeff + * implemented 'typing inidcator' in multiplayer...players that are typing + * messages have an icon on them + * + * 51 2/04/99 7:17p Jeff + * put in sounds for hud messages + * + * 50 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 49 1/29/99 6:34p Samir + * fixed booboo. + * + * 48 1/29/99 6:32p Samir + * implemented hud scrollback for hud messages. + * + * 47 1/29/99 2:08p Jeff + * localization + * + * 46 1/28/99 6:17p Jason + * added markers + * + * 45 1/27/99 6:05p Samir + * added scrollback for game messages on HUD. + * + * 44 1/23/99 2:33p Kevin + * Increased hud message length, because we clip to the real width of the + * hud. Also fixed up the multiline code a bit + * + * 43 1/22/99 4:19p Kevin + * Fixed some multiline hud stuff + * + * 42 1/22/99 4:06p Jeff + * added hud messages that can be sent to just teammates or individual + * people + * + * 41 1/21/99 11:43p Kevin + * Hud messages will now span multiple lines if needed (except for + * blinking) + * + * 40 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 39 12/14/98 11:06a Jason + * changes for 1.1 + * + * 38 12/10/98 4:58p Jason + * fixed off-by-one hudmessage problem + * + * 37 11/01/98 1:57a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 36 10/29/98 11:59a Jason + * fixed hud message issues + * + * 35 10/21/98 5:02p Jeff + * removed player from HUD if in observer nide + * + * 34 10/19/98 11:22p Samir + * made AddPersistentMessage screen res friendly. + * + * 33 10/17/98 2:46p Jason + * automatically start new line when eol is reaching in DoInputMessage + * + * 32 10/14/98 4:26p Samir + * added persistent hud messages. + * + * 31 10/13/98 8:44p Samir + * move outgoing message prompt down. + * + * 30 10/07/98 6:55p Jason + * made message rollback work + * + * 29 10/07/98 10:58a Jason + * flushed the key buffer when pressing escape while entering hud message + * + * 28 10/06/98 5:45p Kevin + * Added new configuration for demo + * + * 27 10/05/98 11:08a Jason + * implemented player message log + * + * 26 9/30/98 4:50p Jeff + * fixed mprintf if a hud message is too long + * + * 25 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 24 9/14/98 6:28p Jason + * first pass at getting dedicated server working + * + * 23 9/08/98 10:28a Samir + * added function to reset hud messages. + * + * 22 8/17/98 10:39a Jeff + * fixed bug with buffer length for hud messages + * + * 21 8/15/98 10:50p Matt + * Changed comment + * + * 20 8/13/98 3:10p Jeff + * key flush when done typing a hud message + * + * 19 7/28/98 3:17p Jason + * fixed buffer overrun problem + * + * 18 6/16/98 10:53a Jeff + * + * 17 5/05/98 5:35p Jason + * took off stupid drop shadow + * + * 16 4/23/98 4:13a Samir + * new hud system. + * + * 15 4/06/98 2:54p Jason + * yet more multiplayer changes + * + * 14 3/23/98 7:37p Jason + * added colored hud messages + * + * 13 3/23/98 4:51p Jason + * incremental checkin for multiplay + * + * 12 3/16/98 6:41p Jason + * added user definable messages to multiplayer + * + * 11 3/16/98 3:53p Jason + * added hud input message stuff + * + * 10 2/06/98 5:45p Samir + * Use hud font now. + * + * 9 1/19/98 4:37p Jason + * made hud message time longer + * + * 8 1/12/98 7:07p Samir + * Hud messages are in the briefing font for now. + * + * 7 1/12/98 5:23p Samir + * New hud font for messages. + * + * 6 12/29/97 5:47p Samir + * New text system and took out refs to Game_viewport. + * + * 5 12/16/97 4:27p Jason + * fixed time wrapping problem + * + * 4 8/29/97 5:42p Jason + * fixed hud rendering problems + * + * 6 4/25/97 10:49a Jason + * made timer stuff work with Gametime instead of timer_GetTime + * + * 5 3/05/97 3:03p Jason + * added blinking messages + * + * 4 3/04/97 12:58p Jason + * made hud messages scroll and fade + * + * 3 3/04/97 11:58a Jason + * checked in for Samir to debug + * + * 2 2/13/97 4:12p Jason + * added hud message test +* +* $NoKeywords: $ +*/ + + + +#include +#include +#include +#include +#include +#include "pstring.h" +#include "grdefs.h" +#include "hud.h" +#include "game.h" +#include "ddio.h" +#include "gamefont.h" +#include "newui.h" +#include "multi.h" +#include "player.h" +#include "game2dll.h" +#include "stringtable.h" +#include "dedicated_server.h" +#include "AppConsole.h" +#include "demofile.h" +#include "mem.h" +#include "textaux.h" +#include "3d.h" +#include "marker.h" +#include "controls.h" +#include "Mission.h" +#include "sounds.h" +#include "hlsoundlib.h" +#include "args.h" + +#define HUD_MESSAGE_NORMAL 0 +#define HUD_MESSAGE_BLINKING 1 + +#define HUD_MESSAGE_NONE 0 +#define HUD_MESSAGE_GENERAL 1 +#define HUD_MESSAGE_TEAM 2 + + +// How long the message stays up for (in seconds) +#define HUD_MESSAGE_TIME 5 +// How long the message scrolls for (in seconds) +#define HUD_SCROLL_TIME .5 + +char HudInputMessage[MAX_HUD_INPUT_LEN]; +int Doing_input_message=HUD_MESSAGE_NONE; +int HudInputMessageLen=0; +static tDirtyRect HUD_inmsg_dirty_rect; + +char HUD_messages[MAX_HUD_MESSAGES][HUD_MESSAGE_LENGTH]; +int HUD_message_type[MAX_HUD_MESSAGES]; +ddgr_color HUD_message_color[MAX_HUD_MESSAGES]; +int Num_hud_messages=0; +int Hud_scroll_offset=0; +static tDirtyRect HUD_msg_dirty_rect; + +float Hud_timer=0.0f; + +static ubyte Hud_persistent_msg_id = HUD_INVALID_ID; +static float Hud_persistent_msg_timer = 0.0f; +static int Hud_persistent_msg_flags = 0; +static int Hud_persistent_msg_current_len; +static float Hud_persistent_msg_char_timer; +static float Hud_persistent_msg_id2 = HUD_INVALID_ID; +static int Hud_persistent_msg_sound_handle = SOUND_NONE_INDEX; +static tDirtyRect HUD_persist_dirty_rect[2]; + +tHUDItem *GetHUDItem(int id); + +// game message console +MsgListConsole Game_msg_con; +static tMsgList Game_msg_list; +static bool Game_msg_con_vis = false; + +// hud message console +MsgListConsole HUD_msg_con; +static tMsgList HUD_msg_list; +static bool HUD_msg_con_vis = false; +static bool Hud_messages_paused = false; + +#define GAME_MSGCON_W 416 +#define GAME_MSGCON_H 256 +#define GAME_MSGCON_X ((Max_window_w-GAME_MSGCON_W)/2) +#define GAME_MSGCON_Y ((Max_window_h-GAME_MSGCON_H)/2) + + +// This function takes a HUD messages, ensures it isn't too long, and if it is, it correctly fixes it +void CorrectHudMessage(char *str); + +/////////////////////////////////////////////////////////////////////////////// +// Functions + + +// prints a string onto the debug consle +void AddMessageToRollback(char *msg) +{ + HUD_msg_list.add(msg); +} + + +// Adds a single line to the hud message list +//Returns true if message added, or false if message not (because the previous message was the same) +bool AddLineToHUDMessages (char *temp_message,ddgr_color color=-1); +bool AddLineToHUDMessages (char *temp_message,ddgr_color color) +{ + if (Num_hud_messages>0 && !strcmp (temp_message,HUD_messages[Num_hud_messages-1])) + return 0; // this is the same message as before, don't print it twice! + + if (Dedicated_server) + { + PrintDedicatedMessage ("*%s\n",temp_message); + return 1; + } + + AddMessageToRollback (temp_message); + + + if (color==-1) + color=GR_RGB(0,255,0); + + if (Num_hud_messages==MAX_HUD_MESSAGES) + { + // Get rid of top message + + for (int i=1;i(Game_window_w*.7)) + { + if(thisline[strlen(thisline)-1] == ' ') + thisline[strlen(thisline)-1] = NULL; + added |= AddLineToHUDMessages (thisline,color); + + //Scan for color information in the string we just added + char *c=NULL,*c2; + c2 = strchr(thisline,'\1'); + while (c2) { + c = c2; + c2 = strchr(c2+4,'\1'); + } + if (c) { //found one + for (int i=0;i<4;i++) + thisline[i] = c[i]; + thisline[4] = 0; + } + else + thisline[0] = NULL; + } + p = nextword; + } + } + if(thisline[strlen(thisline)-1] == ' ') + thisline[strlen(thisline)-1] = NULL; + if(thisline[0]) + added |= AddLineToHUDMessages (thisline,color); + + return added; +} + +// adds a colored hud message to the list +//Returns true if message added, or false if message not (because the previous message was the same) +bool AddColoredHUDMessage (ddgr_color color,char *format,...) +{ + va_list args; + char *message = NULL; + char *last_message=NULL; + char temp_message[HUD_MESSAGE_LENGTH*2]; + + va_start(args, format ); + Pvsprintf(temp_message,HUD_MESSAGE_LENGTH*2,format,args); + va_end(args); + + if(Demo_flags==DF_RECORDING) + { + DemoWriteHudMessage(color,false,temp_message); + } + + if(Dedicated_server) + { + return AddLineToHUDMessages (temp_message,color); + } + + return AddMultipleLinesToHUDMessages (temp_message,color); +} + +// Adds a HUD message (similar to AddColoredHUDMessage), however can be filtered out by +// a "-playermessages" command line. +bool AddFilteredColoredHUDMessage (ddgr_color color,char *format,...) +{ + static signed char checked_command_line = -1; + + if(checked_command_line==-1) + { + if(FindArg("-playermessages")!=0) + { + checked_command_line = 1; + }else + { + checked_command_line = 0; + } + } + + if(Game_mode&GM_MULTI && checked_command_line) + return false; //filter this message + + va_list args; + char *message = NULL; + char *last_message=NULL; + char temp_message[HUD_MESSAGE_LENGTH*2]; + + va_start(args, format ); + Pvsprintf(temp_message,HUD_MESSAGE_LENGTH*2,format,args); + va_end(args); + + if(Demo_flags==DF_RECORDING) + { + DemoWriteHudMessage(color,false,temp_message); + } + + if(Dedicated_server) + { + return AddLineToHUDMessages (temp_message,color); + } + + return AddMultipleLinesToHUDMessages (temp_message,color); +} + + +// Adds a HUD message (similar to AddHUDMessage), however can be filtered out by +// a "-playermessages" command line. +bool AddFilteredHUDMessage (char *format, ... ) +{ + static signed char checked_command_line = -1; + + if(checked_command_line==-1) + { + if(FindArg("-playermessages")!=0) + { + checked_command_line = 1; + }else + { + checked_command_line = 0; + } + } + + if(Game_mode&GM_MULTI && checked_command_line) + return false; //filter this message + + va_list args; + char *message = NULL; + char *last_message=NULL; + char temp_message[HUD_MESSAGE_LENGTH*2]; + + va_start(args, format ); + Pvsprintf(temp_message,HUD_MESSAGE_LENGTH*2,format,args); + va_end(args); + + if(Demo_flags==DF_RECORDING) + { + DemoWriteHudMessage(0,false,temp_message); + } + + if(Dedicated_server) + { + return AddLineToHUDMessages (temp_message); + } + + return AddMultipleLinesToHUDMessages (temp_message); +} + + +// Adds a message to the HUD message list. If the list is already full, punt the +// top one and move the others up +//Returns true if message added, or false if message not (because the previous message was the same) +bool AddHUDMessage (char *format, ... ) +{ + va_list args; + char *message = NULL; + char *last_message=NULL; + char temp_message[HUD_MESSAGE_LENGTH*2]; + + va_start(args, format ); + Pvsprintf(temp_message,HUD_MESSAGE_LENGTH*2,format,args); + va_end(args); + + if(Demo_flags==DF_RECORDING) + { + DemoWriteHudMessage(0,false,temp_message); + } + + if(Dedicated_server) + { + return AddLineToHUDMessages (temp_message); + } + + return AddMultipleLinesToHUDMessages (temp_message); +} + + + +// Adds a blinking message to the HUD message list. If the list is already full, punt the +// top one and move the others up +//Returns true if message added, or false if message not (because the previous message was the same) +bool AddBlinkingHUDMessage (char *format, ... ) +{ + va_list args; + char *message = NULL; + char *last_message=NULL; + char temp_message[HUD_MESSAGE_LENGTH*2]; + + va_start(args, format ); + Pvsprintf(temp_message,HUD_MESSAGE_LENGTH*2,format,args); + va_end(args); + + if(Demo_flags==DF_RECORDING) + { + DemoWriteHudMessage(0,true,temp_message); + } + + CorrectHudMessage(temp_message); + + if (Num_hud_messages>0 && !strcmp (temp_message,HUD_messages[Num_hud_messages-1])) + return 0; // this is the same message as before, don't print it twice! + + if (Num_hud_messages==MAX_HUD_MESSAGES) + { + // Get rid of top message + + for (int i=1;i=(HUD_MESSAGE_LENGTH-4); i-- ) + { + if(str[i]==0x01) + { + //we found a color start + str[i] = '\0'; + } + } +} + +//Called when the player hit the Multiplayer message key. +//Puts the player in input mode, or if already inputting resets the hud input message. +void StartHUDInputMessage() +{ + HudInputMessage[0]=0; + Doing_input_message=HUD_MESSAGE_GENERAL; + HudInputMessageLen=0; + SuspendControls(); + MultiSendRequestTypeIcon(true); +} + +//Called when the player hits the Multiplayer team-message key. +//Puts the player in input mode, or if already inputting resets the hud input message. +void StartTeamHUDInputMessage() +{ + //make sure that HUD_MESSAGE_TEAM is only set for team games + if(!Team_game ){ + StartHUDInputMessage(); + return; + } + + HudInputMessage[0]=0; + Doing_input_message=HUD_MESSAGE_TEAM; + HudInputMessageLen=0; + SuspendControls(); +} + + +// Given a hud message, it will determine who should get this message. +// "name: message" = the player with the callsign name should get the message +// "team: message" = the Player_num's team should get the message +// "0-32: message" = the player with player num of 0-32 should get the message +// all other messages are for everyone else +// +// returns the starting position of the real message (past the :) +// destination will receive one of the following values: +// MULTI_SEND_MESSAGE_ALL = everyone should get this message +// MULTI_SEND_MESSAGE_RED_TEAM = only red team (0) should get this message +// MULTI_SEND_MESSAGE_BLUE_TEAM = only blue team (1) should get this message +// MULTI_SEND_MESSAGE_GREEN_TEAM = only green team (2) should get this message +// MULTI_SEND_MESSAGE_YELLOW_TEAM = only yellow team (3) should get this message +// 0-32 = player num of the player to get the message +char *GetMessageDestination(char *message,int *destination) +{ + int to_who = MULTI_SEND_MESSAGE_ALL; + char *ret = message; + + //see if there is a colon in the string, and match that up with a name + char *colon_pos = NULL; + colon_pos = strchr(message,':'); + if(colon_pos>message){ + //this message might be for one person in particular + char buffer[256]; + memcpy(buffer,message,(colon_pos-message)); + buffer[colon_pos-message] = '\0'; + + //see if buffer is a number, in which case just send to that pnum + char *p = buffer; + bool is_num = true; + while( *p ) { if(!isdigit(*p)) {is_num=false;break;} p++; } + + if(is_num){ + to_who = atoi(buffer); + if(to_who<0 || to_who>=MAX_PLAYERS) to_who = MULTI_SEND_MESSAGE_ALL; + }else{ + int possible_match = -1; + int name_len = strlen(buffer); + int call_len; + + //it's not a number, see if it matches any of the player names + for(int pn=0;pnname_len){ + //try to match a partial name + if(!strnicmp(Players[pn].callsign,buffer,name_len)){ + //possible match + possible_match = pn; + } + }else if(call_len==name_len){ + if(!stricmp(Players[pn].callsign,buffer) ){ + //match! + to_who = pn; + break; + } + } + }//end if + }//end for + + if(to_who<0 && possible_match!=-1){ + to_who = possible_match; + } + } + + if(to_who==MULTI_SEND_MESSAGE_ALL){ + //see if it is for the team + if(!stricmp(buffer,"team")){ + switch(Players[Player_num].team){ + case 0: to_who = MULTI_SEND_MESSAGE_RED_TEAM; break; + case 1: to_who = MULTI_SEND_MESSAGE_BLUE_TEAM; break; + case 2: to_who = MULTI_SEND_MESSAGE_GREEN_TEAM; break; + case 3: to_who = MULTI_SEND_MESSAGE_YELLOW_TEAM; break; + } + } + } + + ret = colon_pos + 1; + } + + *destination = to_who; + return ret; +} + + +// Sends off the input message the player was typing +void SendOffHUDInputMessage () +{ + if(Doing_input_message==HUD_MESSAGE_NONE) + return; + + if (Game_mode & GM_MULTI) + { + if (HudInputMessage[0]=='$') // special command + { + DLLInfo.input_string=HudInputMessage; + CallGameDLL (EVT_CLIENT_INPUT_STRING,&DLLInfo); + } + else + { + char str[255]; + sprintf (str,TXT_HUDSAY,Players[Player_num].callsign,HudInputMessage); + + switch(Doing_input_message){ + case HUD_MESSAGE_GENERAL: + { + int to_who; + + char *colon_pos = GetMessageDestination(HudInputMessage,&to_who); + + if(to_who!=MULTI_SEND_MESSAGE_ALL){ + if(to_who<0){ + //to a team + sprintf (str,"[%s]: %s",Players[Player_num].callsign,colon_pos); + }else{ + //to a player + //cut out what's after the colon + sprintf (str,"<%s>:%s",Players[Player_num].callsign,colon_pos); + AddHUDMessage("Sent private message to %s",Players[to_who].callsign); + } + } + + if (Netgame.local_role==LR_SERVER) + MultiSendMessageFromServer (GR_RGB(0,128,255),str,to_who); + else + MultiSendMessageToServer (0,str,to_who); + }break; + case HUD_MESSAGE_TEAM: + { + int team = MULTI_SEND_MESSAGE_ALL; + switch(Players[Player_num].team){ + case 0: team = MULTI_SEND_MESSAGE_RED_TEAM; break; + case 1: team = MULTI_SEND_MESSAGE_BLUE_TEAM; break; + case 2: team = MULTI_SEND_MESSAGE_GREEN_TEAM; break; + case 3: team = MULTI_SEND_MESSAGE_YELLOW_TEAM; break; + } + + if(team!=MULTI_SEND_MESSAGE_ALL){ + sprintf (str,"[%s]: %s",Players[Player_num].callsign,HudInputMessage); + } + + if (Netgame.local_role==LR_SERVER) + MultiSendMessageFromServer (GR_RGB(0,128,255),str,team); + else + MultiSendMessageToServer (0,str,team); + }break; + } + } + } + HudInputMessage[0]=0; + HudInputMessageLen=0; + + ddio_KeyFlush(); + ResumeControls(); + Doing_input_message=HUD_MESSAGE_NONE; + MultiSendRequestTypeIcon(false); +} + +// Breaks up the input message so that if we overflow it starts on the next line nicely +// Returns the number of characters in the next line +int BreakupHUDInputMessage (char *str) +{ + int cur=0; + int last_space=-1; + int len=strlen (HudInputMessage); + + for (int i=0;i0) + { + int to_who; + char *colon_pos = GetMessageDestination(HudInputMessage,&to_who); + + if(to_who!=MULTI_SEND_MESSAGE_ALL) + { + char to_who_text[64]; + int to_who_size; + + to_who_size = colon_pos-HudInputMessage - 1; + strncpy(to_who_text,HudInputMessage,to_who_size); + to_who_text[to_who_size] = '\0'; + + sprintf (str,"%s:%s",to_who_text,&HudInputMessage[last_space+1]); + leftover_size = strlen(str); + }else + { + strcpy (str,&HudInputMessage[last_space+1]); + } + } + + HudInputMessage[last_space]=0; + + return leftover_size; +} + +// Handles all incoming keys for an inputted hud message +void DoHUDInputMessageKey (int key) +{ + if (key==KEY_F8) + { + StartHUDInputMessage(); + return; + } + + if (Doing_input_message==HUD_MESSAGE_NONE) + return; + +// ddio_KeyFlush(); // took this out. screwed up shift-keys. don't need it anyway. + + if (key==KEY_ESC) + { + Doing_input_message=HUD_MESSAGE_NONE; + MultiSendRequestTypeIcon(false); + Marker_message=0; + HudInputMessage[0]=0; + HudInputMessageLen=0; + ResumeControls(); + } + else if (key==KEY_BACKSP || key==KEY_DELETE) + { + if (HudInputMessageLen>0) + HudInputMessageLen--; + + HudInputMessage[HudInputMessageLen]=0; + } + else if (key==KEY_ENTER) + { + if (Marker_message) + { + DropMarker(HudInputMessage); + Doing_input_message=HUD_MESSAGE_NONE; + MultiSendRequestTypeIcon(false); + Marker_message=0; + HudInputMessage[0]=0; + HudInputMessageLen=0; + ResumeControls(); + } + else + SendOffHUDInputMessage(); + } + else + { + int ascii=ddio_KeyToAscii (key); + if (isdigit(ascii) || isalpha(ascii) || ispunct(ascii) || isspace (ascii)) + { + if (Marker_message) + { + if (HudInputMessageLen>=(MAX_MARKER_MESSAGE_LENGTH-2)) + return; + } + + HudInputMessage[HudInputMessageLen++]=ascii; + HudInputMessage[HudInputMessageLen]=0; + + if (!Marker_message && HudInputMessageLen>=(MAX_HUD_INPUT_LEN-1)) + { + // Break up our outgoing message onto the next line if need be + char str[MAX_HUD_INPUT_LEN]; + int str_len; + int old_style = Doing_input_message; + + str_len=BreakupHUDInputMessage (str); + SendOffHUDInputMessage (); + + switch(old_style) + { + case HUD_MESSAGE_TEAM: + StartTeamHUDInputMessage(); + break; + case HUD_MESSAGE_GENERAL: + StartHUDInputMessage(); + break; + } + + if (str_len>0) + { + strcpy (HudInputMessage,str); + HudInputMessageLen=str_len; + } + } + } + } +} + +// Simply renders the hud input message if there is one +void RenderHUDInputMessage () +{ + char message[255]; + int y,x,lw; + + ddgr_color text_color=HUD_COLOR; + grtext_SetColor(text_color); + + if(Doing_input_message==HUD_MESSAGE_TEAM){ + sprintf (message,TXT_HUD_TEAMSAY,HudInputMessage); + }else{ + if (Marker_message) + sprintf (message,TXT_HUD_MARKER,HudInputMessage); + else + sprintf (message,TXT_MESSAGE,HudInputMessage); + } + + grtext_SetAlpha(HUD_ALPHA); + grtext_SetFlags(0); + +// RenderHUDTextNoFormatFlags(HUDTEXT_CENTERED, HUD_COLOR, HUD_ALPHA, 1, 0, y, message); + lw = grtext_GetTextLineWidth(message); + x = (Game_window_w-lw)/2; + if (Small_hud_flag) { + y = Max_window_h - (int)(40*((float)Max_window_h/(float)DEFAULT_HUD_HEIGHT)); + HUD_inmsg_dirty_rect.set(x,y,x+lw,y+grfont_GetHeight(HUD_FONT)); + } + else { + y = (Game_window_h*3/5)+Game_window_y; + } + grtext_Puts(x, y, message); +} + +int testcolor=200; + + +float Hud_messages_paused_saved_Gametime; + +void HUDPauseMessages(void) +{ + if(Hud_messages_paused) + return; + + Hud_messages_paused = true; + Hud_messages_paused_saved_Gametime = Gametime; +} + +void HUDUnpauseMessages(void) +{ + if(!Hud_messages_paused) + return; + + Hud_messages_paused = false; + float time_paused = Gametime - Hud_messages_paused_saved_Gametime; + + Hud_timer += time_paused; +} + + +// Renders all the messages we have in the message list. If HUD_MESSAGE_TIME has +// elapsed, then punt the oldest message and move the others up one +void RenderScrollingHUDMessages () +{ + if(Hud_messages_paused) + return; + + int shade; + int text_height; + short l,t,r,b; + int i; + + // Check for wraps + if (GametimeHUD_MESSAGE_NONE) + RenderHUDInputMessage (); + + if (Num_hud_messages==0) + return; + + l = (Small_hud_flag) ? (Max_window_w/2) : (Game_window_w/2); + r = l; + t = 0; + b = 0; + for (i=0;i r) r = x+text_width; + b += text_height; + } + else { + x = (Game_window_w-text_width)/2; + } + grtext_Puts(x, y, message); + } + if (Small_hud_flag && (i>0)) { + HUD_msg_dirty_rect.set(l, t,r, b); + } + + // Now see if the topmost message is old + + float new_time=Gametime; + + if ((new_time-Hud_timer)>HUD_MESSAGE_TIME) + { + for (int i=1;i(HUD_MESSAGE_TIME-HUD_SCROLL_TIME)) + { + // scroll + + float cur_scroll_time=HUD_MESSAGE_TIME-HUD_SCROLL_TIME; + float cur_hud_time=(new_time-Hud_timer)-cur_scroll_time; + float fscroll; + + cur_hud_time/=HUD_SCROLL_TIME; + // cur_hud_time is now normalized from 0 to 1 + // 0 represents not scrolled, 1 represents a total scroll + + ASSERT (cur_hud_time>=0 && cur_hud_time<=1); + + fscroll=text_height*cur_hud_time; + + Hud_scroll_offset=-fscroll; + } +} + +#define FADEOUT_TIME 0.5f //fades out over this amount of time +#define CHAR_DELAY 0.03f //delay between character + +// Renders all the messages we have in the message list. If HUD_MESSAGE_TIME has +// elapsed, then punt the oldest message and move the others up one +void RenderHUDMessages () +{ + if(Hud_messages_paused) + return; + + //Render & update the scrolling message list + RenderScrollingHUDMessages(); + + // do persistent hud message system + if (Hud_persistent_msg_id != HUD_INVALID_ID) { + if (Hud_persistent_msg_timer != HUD_MSG_PERSISTENT_INFINITE) { + tHUDItem *item = GetHUDItem(Hud_persistent_msg_id); + + //Update time + Hud_persistent_msg_timer -= Frametime; + + //Do fadeout + if (Hud_persistent_msg_flags & HPF_FADEOUT) { + if (Hud_persistent_msg_timer < FADEOUT_TIME) { + item->alpha = HUD_ALPHA * Hud_persistent_msg_timer / FADEOUT_TIME; + } + } + + if (item && Small_hud_flag) { + grtext_SetFont(HUD_FONT); + short l = item->x*Max_window_w/DEFAULT_HUD_WIDTH; + short t = item->y*Max_window_h/DEFAULT_HUD_HEIGHT; + short r = l + grtext_GetTextLineWidth(item->data.text)+10; + short b = t + grtext_GetTextHeight(item->data.text); + item->dirty_rect.set(l,t,r,b); + } + + //Do FreeSpace printing effect + if (Hud_persistent_msg_id2 != HUD_INVALID_ID) { + Hud_persistent_msg_char_timer -= Frametime; + while (Hud_persistent_msg_char_timer < 0) { + tHUDItem *item = GetHUDItem(Hud_persistent_msg_id); + tHUDItem *item2 = GetHUDItem(Hud_persistent_msg_id2); + + item->data.text[Hud_persistent_msg_current_len] = item2->data.text[0]; + Hud_persistent_msg_current_len++; + + //Check for more chars + if (! item->data.text[Hud_persistent_msg_current_len]) { + FreeHUDItem(Hud_persistent_msg_id2); + Hud_persistent_msg_id2 = HUD_INVALID_ID; + Sound_system.StopSoundLooping(Hud_persistent_msg_sound_handle); + Hud_persistent_msg_sound_handle = SOUND_NONE_INDEX; + break; //stop printing chars + } + else { //dp next char + item2->data.text[0] = item->data.text[Hud_persistent_msg_current_len]; + item->data.text[Hud_persistent_msg_current_len] = 0; + item2->x = item->x + RenderHUDGetTextLineWidth(item->data.text); + Hud_persistent_msg_char_timer += CHAR_DELAY; + } + } + } + + //If time out, clear message + if (Hud_persistent_msg_timer <= 0.0f) { + void ClearPersistentHUDMessage(); + ClearPersistentHUDMessage(); + } + } + } +} + + +void RenderHUDMsgDirtyRects() +{ +// clean hud message dirty rects + HUD_msg_dirty_rect.fill(GR_BLACK); + +// clean input message dirty rects + HUD_inmsg_dirty_rect.fill(GR_BLACK); + +// clean remnant persistant messages + HUD_persist_dirty_rect[0].fill(GR_BLACK); + HUD_persist_dirty_rect[1].fill(GR_BLACK); +} + + +// reset hud messages. +void ResetHUDMessages() +{ + Hud_timer = 0.0f; + Num_hud_messages = 0; + Hud_scroll_offset = 0; + ResetPersistentHUDMessage(); + HUDUnpauseMessages(); + + HUD_inmsg_dirty_rect.reset(); + HUD_msg_dirty_rect.reset(); + HUD_persist_dirty_rect[0].reset(); + HUD_persist_dirty_rect[1].reset(); + + void ResetHUDLevelItems(); + ResetHUDLevelItems(); +} + +typedef struct { + char message[HUD_MESSAGE_LENGTH*2]; + ddgr_color color; + int x,y; + float time; + int flags; + int sound_index; +} phud_message; + +#define PHUD_QUEUE_SIZE 3 + +phud_message PHUD_message_queue[PHUD_QUEUE_SIZE]; + +int Num_queued_PHUD_messages = 0; + +//adds a message to the persistant message queue +void QueuePersistentHUDMessage(ddgr_color color,int x, int y, float time, int flags, int sound_index, char *message) +{ + if (Num_queued_PHUD_messages == PHUD_QUEUE_SIZE) + return; + + phud_message *pp = &PHUD_message_queue[Num_queued_PHUD_messages++]; + + pp->color = color; + pp->x = x; + pp->y = y; + pp->time = time; + pp->flags = flags; + pp->sound_index = sound_index; + strcpy(pp->message,message); +} + +//Start playing a persistent HUD message +void StartPersistentHUDMessage(ddgr_color color,int x, int y, float time, int flags, int sound_index, char *message) +{ + if(Demo_flags==DF_RECORDING) + { + DemoWritePersistantHUDMessage(color,x,y,time,flags,sound_index,message); + } + +// set x and y if special formatting. + grtext_SetFont(HUD_FONT); + if (x == HUD_MSG_PERSISTENT_CENTER) { + x = Game_window_x + (Game_window_w - grtext_GetLineWidth(message))/2; + } + if (y == HUD_MSG_PERSISTENT_CENTER) { + y = Game_window_y + (Game_window_h - grfont_GetHeight(HUD_FONT))/2; + } + +// add custom text item + tHUDItem huditem; + + memset(&huditem,0, sizeof(huditem)); + + huditem.alpha = HUD_ALPHA; + huditem.color = color; + huditem.type = HUD_ITEM_CUSTOMTEXT; + huditem.x = x*DEFAULT_HUD_WIDTH/Max_window_w; + huditem.y = y*DEFAULT_HUD_HEIGHT/Max_window_h; + huditem.data.text = message; + huditem.stat = STAT_MESSAGES; + huditem.flags = HUD_FLAG_PERSISTANT | HUD_FLAG_SMALL; // when hud is shrunk, this will draw outside of normal view + + AddHUDItem(&huditem); + + Hud_persistent_msg_id = huditem.id; + Hud_persistent_msg_timer = time; + Hud_persistent_msg_flags = flags; + + //Set up for FreeSpace-style effect + if (flags & HPF_FREESPACE_DRAW) { + + //Create additional hud item for leading character + huditem.alpha = 255; + huditem.data.text = "a"; //single-character string + huditem.saturation_count = 2; + AddHUDItem(&huditem); + Hud_persistent_msg_id2 = huditem.id; + + tHUDItem *item = GetHUDItem(Hud_persistent_msg_id); + tHUDItem *item2 = GetHUDItem(Hud_persistent_msg_id2); + + item2->data.text[0] = item->data.text[0]; + item->data.text[0] = 0; + + Hud_persistent_msg_current_len = 0; + Hud_persistent_msg_char_timer = CHAR_DELAY; + + if (sound_index != SOUND_NONE_INDEX) + Hud_persistent_msg_sound_handle = Sound_system.Play2dSound(sound_index); + } +} + +// adds a persistent hud message that is timed, or infinite until removed +// for infinite, time = -1.0f +// for centering on an axis, set either x or y to -1. +void AddPersistentHUDMessage(ddgr_color color,int x, int y, float time, int flags, int sound_index, const char *fmt, ...) +{ + va_list args; + char temp_message[HUD_MESSAGE_LENGTH*2]; + +// start new message + va_start(args, fmt ); + Pvsprintf(temp_message,HUD_MESSAGE_LENGTH*2,fmt,args); + va_end(args); + + if (Hud_persistent_msg_id != HUD_INVALID_ID) //already one active, so queue the new one + QueuePersistentHUDMessage(color,x,y,time,flags,sound_index,temp_message); + else + StartPersistentHUDMessage(color,x,y,time,flags,sound_index,temp_message); +} + +//Clears the current message, and plays the next in the queue +void ClearPersistentHUDMessage() +{ +// free persistent message currently visible + if (Hud_persistent_msg_id != HUD_INVALID_ID) { + tHUDItem *item = GetHUDItem(Hud_persistent_msg_id); + if (item) { + t_dirty_rect *r = &item->dirty_rect; + HUD_persist_dirty_rect[0].set(r->r[0].l, r->r[0].t, r->r[0].r, r->r[0].b); + } + FreeHUDItem(Hud_persistent_msg_id); + Hud_persistent_msg_id = HUD_INVALID_ID; + } + if (Hud_persistent_msg_id2 != HUD_INVALID_ID) { + tHUDItem *item = GetHUDItem(Hud_persistent_msg_id2); + if (item) { + t_dirty_rect *r = &item->dirty_rect; + HUD_persist_dirty_rect[1].set(r->r[0].l, r->r[0].t, r->r[0].r, r->r[0].b); + } + FreeHUDItem(Hud_persistent_msg_id2); + Hud_persistent_msg_id2 = HUD_INVALID_ID; + } + + if (Hud_persistent_msg_sound_handle != SOUND_NONE_INDEX) { + Sound_system.StopSoundLooping(Hud_persistent_msg_sound_handle); + Hud_persistent_msg_sound_handle = SOUND_NONE_INDEX; + } + + //Check for queued message + if (Num_queued_PHUD_messages) { + phud_message *pp = &PHUD_message_queue[0]; + StartPersistentHUDMessage(pp->color,pp->x,pp->y,pp->time,pp->flags,pp->sound_index,pp->message); + *pp = PHUD_message_queue[1]; + Num_queued_PHUD_messages--; + } + +} + +//Clears all the persistent hud messages +void ResetPersistentHUDMessage() +{ + Num_queued_PHUD_messages = 0; + ClearPersistentHUDMessage(); +} + + +///////////////////////////////////////////////////////////////////////// +// HUD Message System +///////////////////////////////////////////////////////////////////////// + + +// opens a hud rollback console. +void OpenHUDMessageConsole() +{ + if (Game_msg_con_vis) { + CloseGameMessageConsole(); + } + + HUD_msg_con_vis = HUD_msg_con.Open(TXT_HUDMSGPOPUP_TITLE, GAME_MSGCON_X, GAME_MSGCON_Y, GAME_MSGCON_W, GAME_MSGCON_H); +} + + +// closes hud rollback console +void CloseHUDMessageConsole() +{ + if (HUD_msg_con_vis) { + HUD_msg_con.Close(); + HUD_msg_con_vis = false; + } +} + + +// toggles hud rollback console +void ToggleHUDMessageConsole() +{ + if (HUD_msg_con_vis) CloseHUDMessageConsole(); + else OpenHUDMessageConsole(); +} + + +///////////////////////////////////////////////////////////////////////// +// Game Message System +///////////////////////////////////////////////////////////////////////// +// resets game message list to no messages +void ResetGameMessages() +{ + Game_msg_list.reset(); + Game_msg_con.AttachMsgList(&Game_msg_list); + HUD_msg_list.reset(); + HUD_msg_con.AttachMsgList(&HUD_msg_list); +} + + +void AddGameMessage(const char *msg) +{ + int secs = (int)Gametime; + + Game_msg_list.add(msg, (ubyte)Current_mission.cur_level, (secs/3600), (secs/60), secs % 60); +} + + +void SGSGameMessages(CFILE *fp) +{ + int i=0; + const char *msg; + + cf_WriteShort(fp, (short)Game_msg_list.m_nmsg); + + while ((msg =Game_msg_list.get(i++)) != NULL) + { + cf_WriteString(fp, msg); + } +} + + +void LGSGameMessages(CFILE *fp) +{ + int n; + char buf[512]; // some level designers can be nuts. + + n = (int)cf_ReadShort(fp); + + Game_msg_list.reset(); + while(n) + { + n--; + cf_ReadString(buf, sizeof(buf), fp); + buf[511]=0; + Game_msg_list.add(buf); + } +} + + +// toggles game message console +void ToggleGameMessageConsole() +{ + if (Game_msg_con_vis) CloseGameMessageConsole(); + else OpenGameMessageConsole(); +} + + +// game message console +void OpenGameMessageConsole() +{ + if (HUD_msg_con_vis) { + CloseHUDMessageConsole(); + } + Game_msg_con_vis = Game_msg_con.Open(TXT_HUD_GAMEMESSAGES, GAME_MSGCON_X, GAME_MSGCON_Y, GAME_MSGCON_W, GAME_MSGCON_H); +} + + +void CloseGameMessageConsole() +{ + Game_msg_con.Close(); + Game_msg_con_vis = false; +} + + + +////////////////////////////////////////////////////////////////////////////// + +#define MSGL_BORDER_THICKNESS 4 + + +tMsgList::tMsgList() +{ + m_nmsg = 0; + m_limit = 64; + m_msg = NULL; +} + + +bool tMsgList::add(const char *msg, ubyte lvl, ubyte hr, ubyte min, ubyte sec) +{ + char buf[2048]; + + if(!m_limit) + m_limit = 64; + + if (m_msg == NULL) { + m_msg = (char **)mem_malloc(sizeof(char*)*m_limit); + m_nmsg = 0; + } + + if (m_nmsg == m_limit && m_nmsg > 0) { + int i; + mem_free(m_msg[0]); + for (i = 1; i < m_nmsg; i++) + m_msg[i-1] = m_msg[i]; + m_nmsg--; + } + + if (lvl > 0) { + sprintf(buf, "[%d.%d.%d.%d] %s", lvl,hr,min,sec,msg); + m_msg[m_nmsg++] = mem_strdup(buf); + } + else { + m_msg[m_nmsg++] = mem_strdup(msg); + } + + return true; +} + + +void tMsgList::reset() +{ + if (m_msg) { + while (m_nmsg) + { + m_nmsg--; + if (m_msg[m_nmsg]) { + mem_free(m_msg[m_nmsg]); + } + } + mem_free(m_msg); + m_msg = NULL; + } + m_nmsg = 0; +} + + +const char *tMsgList::get(int i) +{ + if (m_msg) { + if (i < m_nmsg) { + return m_msg[i]; + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// + +MsgListConsole::MsgListConsole() +{ + m_opened = false; + m_list = NULL; + m_buffer = NULL; + m_bufline = 0; + m_conlines = NULL; + n_conlines = 0; +} + + +MsgListConsole::~MsgListConsole() +{ +} + + +// registers dimensions of box +bool MsgListConsole::Open(const char *title, int x, int y, int w, int h) +{ + m_x = x; + m_y = y; + m_w = w; + m_h = h; + + if (!m_list) return false; + +// determine text buffer dimensions + m_buflen = 2048; + +redo_copy: + m_buffer = (char *)mem_malloc(m_buflen); + if (m_buffer) { + m_buffer[0] = 0; + m_opened = true; + } + else { + m_opened = false; + return false; + } + +// generate text buffer + int i, len, j; + char *bufptr = m_buffer; + + for (i = 0; i < m_list->m_nmsg; i++) + { + const char *msg = m_list->m_msg[i]; + if ((strlen(m_buffer)+strlen(msg)+2) >= (unsigned)(m_buflen)) { + // reallocate buffer + mem_free(m_buffer); + m_buflen = m_buflen * 2; + goto redo_copy; + } + + textaux_WordWrap(msg, bufptr, m_w - MSGL_BORDER_THICKNESS*2, HUD_FONT); + bufptr += strlen(bufptr); + + // append newline character if there are more messages. + if (i < (m_list->m_nmsg-1)) { + *bufptr = '\n'; + bufptr++; + } + } + +// allocate console line pointers + int c = 0; + int y2 = grfont_GetHeight(HUD_FONT) + MSGL_BORDER_THICKNESS + 3; + while (y2 < (m_h-MSGL_BORDER_THICKNESS-grfont_GetHeight(HUD_FONT))) + { + y2 += (grfont_GetHeight(HUD_FONT) + 1); + c++; + } + + ASSERT(c > 0); + m_conlines = (char **)mem_malloc(sizeof(char*)*c); + n_conlines = c; + memset(m_conlines, 0, sizeof(char*)*c); + +// get pointer to first visible line. + c = 0; + m_bufline = 0; + len = strlen(m_buffer); + bufptr = m_buffer; + for (i = 0; i <=len; i++) + { + if (m_buffer[i] == '\n' || m_buffer[i] == 0) { + if (c == n_conlines) { + // scroll pointers up, store new one. + for (j = 1; j < n_conlines; j++) + m_conlines[j-1] = m_conlines[j]; + c--; + } + m_conlines[c] = bufptr; + bufptr = &m_buffer[i+1]; + m_bufline++; + c++; + } + } + + m_numlines = m_bufline; + m_keydowntime = 0.0f; + + strcpy(m_title, title); + m_curmsgs = m_list->m_nmsg; + + SuspendControls(); + + return true; +} + + +void MsgListConsole::Close() +{ + if (m_opened) { + if (m_conlines) mem_free(m_conlines); + if (m_buffer) mem_free(m_buffer); + m_opened = false; + ResumeControls(); + } +} + + +void MsgListConsole::AttachMsgList(tMsgList *msglist) +{ + m_list = msglist; + m_bufline = 0; +} + + +void MsgListConsole::Draw() +{ + g3Point *pntlist[4],points[4]; + int y, i; + + if (!m_opened || !m_list) return; + +// change in buffer, do 'slow' update + if (m_curmsgs != m_list->m_nmsg) { + char buf[32]; + strcpy(buf,m_title); + this->Close(); + this->Open(buf, m_x,m_y,m_w,m_h); + } + +// draw background window. + points[0].p3_sx=m_x; + points[0].p3_sy=m_y; + points[1].p3_sx=m_x+m_w; + points[1].p3_sy=m_y; + points[2].p3_sx=m_x+m_w; + points[2].p3_sy=m_y+m_h; + points[3].p3_sx=m_x; + points[3].p3_sy=m_y+m_h; + + for (i=0;i<4;i++) + { + points[i].p3_z=0; + points[i].p3_flags=PF_PROJECTED; + pntlist[i]=&points[i]; + } + + rend_SetZBufferState(0); + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetLighting (LS_NONE); + rend_SetFlatColor (GR_BLACK); + rend_SetAlphaValue(192); + rend_DrawPolygon2D( 0, pntlist, 4 ); + +// set up text rendering. + grtext_Reset(); + grtext_SetParameters(m_x+MSGL_BORDER_THICKNESS,m_y+MSGL_BORDER_THICKNESS-1,m_x+m_w - MSGL_BORDER_THICKNESS,m_y+m_h-MSGL_BORDER_THICKNESS); + grtext_SetFont(HUD_FONT); + grtext_SetColor(GR_RGB(0,255,0)); + grtext_Puts(m_x+MSGL_BORDER_THICKNESS+4, m_y+MSGL_BORDER_THICKNESS-1, m_title); + + y = m_y + grfont_GetHeight(HUD_FONT) + MSGL_BORDER_THICKNESS + 1; + rend_SetFlatColor(GR_RGB(0,255,0)); + rend_DrawLine(m_x+MSGL_BORDER_THICKNESS,y,m_x+m_w-MSGL_BORDER_THICKNESS,y); + +// draw all lines until bottom + y += 2; + for (i = 0; i < n_conlines; i++) + { + char *sptr = NULL; + if (i < (n_conlines-1)) { + sptr = m_conlines[i+1]; + } + if (sptr) { + sptr[-1] = 0; + } + grtext_Puts(m_x+MSGL_BORDER_THICKNESS,y,m_conlines[i]); + if (sptr) { + sptr[-1] = '\n'; + } + y += (grfont_GetHeight(HUD_FONT) + 1); + if (!sptr) { + break; + } + } + + grtext_Flush(); +} + + +void MsgListConsole::DoInput() +{ + if (m_opened) { + int i, offset_count = 0; // = ddio_KeyDownCount(KEY_DOWN) - ddio_KeyDownCount(KEY_UP); + float key_time = ddio_KeyDownTime(KEY_DOWN) - ddio_KeyDownTime(KEY_UP); + char *lineptr; + + if (m_numlines <= n_conlines) { + offset_count = 0; // no need to offset if no need to scroll + } + + if (key_time == 0.0f) { + m_keydowntime = 0.0f; + } + else { + m_keydowntime += key_time; + } + if (m_keydowntime < -0.1f) { + offset_count = -1; + m_keydowntime = 0.0f; + } + else if (m_keydowntime > 0.1f) { + offset_count = 1; + m_keydowntime = 0.0f; + } + + // scroll offset_count amount. + if (offset_count < 0) { + // look back from m_conlines[0] + if ((m_bufline + offset_count - n_conlines) < 0) offset_count = -(m_bufline-n_conlines); + + m_bufline = m_bufline + offset_count; + + if (offset_count < 0) { + mprintf((0, "bufline=%d\n", m_bufline)); + } + + while (offset_count < 0) + { + offset_count++; + for (i = n_conlines-1; i > 0; i--) + m_conlines[i] = m_conlines[i-1]; + + // go back until hit previous newline of beginning of buffer. + for (lineptr = m_conlines[0]-1; lineptr != m_buffer && lineptr[-1] != '\n'; lineptr--); + m_conlines[0] = lineptr; + } + } + else if (offset_count > 0) { + // look forward from m_conlines[n_conlines-1] + if ((m_bufline + offset_count) > m_numlines) offset_count = m_numlines - m_bufline; + + m_bufline = m_bufline + offset_count; + + if (offset_count > 0) { + mprintf((0, "bufline=%d\n", m_bufline)); + } + + while (offset_count > 0) + { + offset_count--; + for (i = 1; i < n_conlines; i++) + m_conlines[i-1] = m_conlines[i]; + + // go forward until we hit a newline or end of buffer (if end, then error!) + for (lineptr = m_conlines[n_conlines-1]; lineptr[0] != '\n'; lineptr++); + { + if (lineptr[0] == 0) { + Int3(); + offset_count = 0; + lineptr = m_conlines[n_conlines-1]; + break; + } + } + if (lineptr[0]) lineptr++; + m_conlines[n_conlines-1] = lineptr; + } + } + + } +} diff --git a/Descent3/init.cpp b/Descent3/init.cpp new file mode 100644 index 000000000..a4a0de255 --- /dev/null +++ b/Descent3/init.cpp @@ -0,0 +1,2908 @@ +/* + * $Logfile: /DescentIII/Main/init.cpp $ + * $Revision: 323 $ + * $Date: 10/22/01 12:42p $ + * $Author: Matt $ + * + * Initialization of D3 systems + * + * $Log: /DescentIII/Main/init.cpp $ + * + * 323 10/22/01 12:42p Matt + * Added MercInstalled() function. + * + * 322 10/03/01 12:45a Kevin + * Smaller version of pxo packets + * + * 321 9/15/01 1:46p Kevin + * Added -nocrashbox to suppress crash dialog box + * + * 320 9/14/01 4:47p Matt + * Cleaned up problems when screen bit depth set to 32. + * + * 319 9/14/01 12:50p Matt + * Took out code that bashed the missile view ot left if it was set to + * center. It's now not possible to set the view to center in the config + * menu. + * + * 318 4/19/00 5:36p Matt + * Added command-line option to adjust mouse sensitivity + * + * 317 3/20/00 12:11p Matt + * Merge of Duane's post-1.3 changes. + * Misc Mac stuff: movie, int'l keyboards, loading screens (Mac only) + * + * 316 1/26/00 9:20p Jeff + * added support for IntelliVIBE DLL + * + * 315 12/01/99 3:58p Jeff + * one last time...linux pregamecdcheck bug fix + * + * 314 12/01/99 3:30p Jeff + * set CD_inserted to 1 for Linux + * + * 313 11/30/99 5:07p Jeff + * no pregame cd check for Linux (for now) + * + * 312 10/26/99 3:30p Jeff + * handle extra.gam addon tablefile + * + * 311 10/25/99 3:09p Kevin + * added -mlooksens + * + * 310 10/25/99 9:48a Matt + * Mac code that should enable the dynamic allocation of lightmap_info + * structures. This should be disabled for editor builds. + * + * 309 10/25/99 12:47a Chris + * Fixed a long overdue bug with zero rad wall collison objects and + * colliding + * + * 308 10/22/99 10:52p Matt + * Mac merge + * + * 307 10/22/99 10:43p Jeff + * put in a variable to detect whether we are running the editor (needed + * in manage system, which can't use EDITOR define) + * + * 306 10/19/99 4:55p Jeff + * removed compiler warning + * + * 305 10/19/99 12:44p Chris + * Fixed a small bug with the registery check + * + * 304 10/18/99 1:59p Chris + * Added the ability to load the merc. hog + * + * 303 10/17/99 3:55p Jeff + * Linux movie stuff + * + * 302 10/12/99 11:03a Jeff + * if there is a -mission command line option, open that mn3 right after + * d3.hog for any overridables + * + * 301 10/06/99 2:04p Kevin + * copy protection fix to match the 1.2 full cd + * + * 300 10/05/99 10:11a Kevin + * Don't show tantrum logo on macintosh demo (per GraphSim's Request) + * + * 299 10/05/99 9:46a Kevin + * copy protection can now handle US/AUS & ROW no LL versions in one exe + * + * 298 9/29/99 8:05a Kevin + * Changes to copy protection to allow for us and ROW-NONLL versions to + * use the same exe + * + * 297 9/24/99 12:01p Samir + * use directinput when -alternatejoy is selected (like 1.0) + * + * 296 9/23/99 2:59p Kevin + * mac dll hog file + * + * 295 9/20/99 5:35p Jeff + * added -nosparkles command line option to turn off powerup sparkles + * + * 294 9/18/99 9:21p Jeff + * Init motion blur if we are a Katmai + * + * 293 8/20/99 9:45a Kevin + * Rankings changes! + * + * 292 7/30/99 5:24p Samir + * use no joystick emulation by default. + * + * 291 7/29/99 12:27p Chris + * FIxed a mismatched paren + * + * 290 7/28/99 5:11p Kevin + * Mac merges + * + * 289 7/26/99 5:19p Kevin + * fixed copy protection for DVD drives + * + * 288 7/12/99 6:44p Jeff + * create a lock file in the temp directory, if one already exists (for a + * program in use), then don't allow the use of the temp directory + * + * 287 7/08/99 5:44p Jason + * added default framecap of 60 to deal with stuttering issues + * + * 286 7/06/99 5:54p Kevin + * more foriegn versions.... + * + * 285 6/24/99 8:10p Jeff + * added support for OpenGL renderer + * + * 284 6/24/99 3:15p Jeff + * don't check renderer in Linux if dedicated + * + * 283 6/22/99 7:03p Jeff + * added command line arguments to set renderer for Linux (Glide only + * right now) + * + * 282 6/21/99 2:12p Kevin + * South american version changes + * + * 281 6/17/99 11:34a Kevin + * + * 280 6/16/99 12:04p Kevin + * Warning!!! This is a laserlocked version! + * + * 279 6/11/99 1:15a Samir + * localization issues. + * + * 278 6/08/99 1:00p Jason + * changes for bumpmapping + * + * 277 6/03/99 8:48a Kevin + * fixes for new OEM version.... + * + * 276 5/24/99 6:20a Kevin + * fixed dedicated server & copy protection + * + * 275 5/24/99 2:13a Matt + * Fixed -superlowmem switch. + * + * 274 5/23/99 3:03p Kevin + * real dist. checksums + * + * 273 5/23/99 2:38a Kevin + * Lame ass(tm) copy protection + * + * 272 5/21/99 10:05a Kevin + * Added stuff for a future DVD version + * + * 271 5/19/99 11:47a Kevin + * made the parent window of the message box actually be Descent 3. + * + * 270 5/18/99 10:31a Kevin + * made the us version only work with US english and US canadian languages + * + * 269 5/12/99 2:24p Jeff + * Descent3 now has a setable temp directory for all temp files + * + * 268 5/12/99 1:18p Samir + * play music during game too when in menus. + * + * 267 5/11/99 1:19a Samir + * upped music volume, set default to 0.5 + * + * 266 5/10/99 9:25p Jeff + * first phase of Rock 'n' Ride support added + * + * 265 5/10/99 8:49p Jason + * added vsync command line switch + * + * 264 5/10/99 5:35p Kevin + * New command line options for heat and scoring API enhancements + * + * 263 5/07/99 6:54p Jason + * fixed some gamegauge issues + * + * 262 5/07/99 12:04p Samir + * added support for foreign keyboards and error text. + * + * 261 5/06/99 6:11p Kevin + * fixes for save/load game syste. Also require CD again and added some + * heat.net stuff + * + * 260 5/05/99 11:16p Kevin + * heat.net stuff among other things + * + * 259 5/05/99 5:46p Samir + * took out call to ResumeSounds when restoring from an Alt-tab. + * + * 258 5/02/99 9:13a Jeff + * added system specific hog files (d3-linux.hog for Linux) + * + * 257 5/01/99 4:24p Jeff + * fixed some windows specific code for Linux (*grrrr*) + * + * 256 4/30/99 10:53p Samir + * store pilot settings in a temporary buffer when multitasking. + * + * 255 4/30/99 5:06p Kevin + * misc dedicated server, networking and low memory enhancements + * + * 254 4/29/99 10:01p Samir + * use emulation mode of joystick first. + * + * 253 4/29/99 5:53p Kevin + * Made it so Japanese people can't play D3... well that is only if their + * computers have the Japanese language installed. I'm not prejudice or + * anything....It's just that Interplay doesn't want the english version + * selling in Japan or something like that. :) + * + * 252 4/29/99 2:28p Kevin + * Laserlock implementation + * + * 251 4/27/99 9:59p Kevin + * CD not requred for dedicated server. Also improved demo playback + * + * 250 4/27/99 2:10p Samir + * added code to set the desired sound card given the descriptive name of + * a sound card. + * + * 249 4/26/99 9:11p Matt + * Use small font instead of old UI font. + * + * 248 4/26/99 11:34a Samir + * don't resume sounds if game was paused when alt-tabbing back + * (ResumeGame will do the trick.) + * + * 247 4/25/99 6:14p Kevin + * added "-timetest file.dem" to behave like gamegauge does + * + * 246 4/25/99 4:15p Matt + * Added code to manually allow the user to specifiy the screen aspect + * ratio, to make the game work with wide-screen TVs. + * + * 245 4/25/99 1:27p Matt + * Fixed small bug + * + * 244 4/22/99 9:53a Kevin + * Fixed some dedicated server crashes + * + * 243 4/20/99 11:44a Jeff + * get the language type from the registry + * + * 242 4/17/99 7:42p Samir + * Don't save out renderer and sound mixer in release version since the + * launcher should only do this. + * + * 241 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 240 4/16/99 12:03a Jeff + * removed cd check for Linux + * + * 239 4/15/99 3:48p Samir + * set mouse mode when ddio is initialized. + * + * 238 4/15/99 2:32p Kevin + * Added some code for the Demo + * + * 237 4/15/99 10:57a Kevin + * cd check only in release builds + * + * 236 4/15/99 9:25a Kevin + * CD check fixes and updates + * + * 235 4/14/99 3:07p Kevin + * Fixed some multiple CD bugs + * + * 234 4/14/99 12:35p Samir + * localization issues. + * + * 233 4/14/99 11:48a Matt + * Don't set messagebox title for release builds + * + * 232 4/14/99 2:50a Jeff + * fixed some case mismatched #includes + * + * 231 4/12/99 7:15p Samir + * added ability to configure number of sounds played at one time. + * + * 230 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 229 4/06/99 4:15p Nate + * Fixed UpdateInitMessage in editor (samir) + * + * 228 4/06/99 1:53p Samir + * added improved progress bar. + * + * 227 4/05/99 9:53a Kevin + * Took out the splash screens Craig made me add while he was smoking + * crack. + * + * 226 4/04/99 8:15p Jeff + * added debug graph stuff + * + * 225 4/03/99 1:24p Jason + * changed some wording for loading screen + * + * 224 4/02/99 7:04p Kevin + * Added splash screens do release builds + * + * 223 4/02/99 5:08p Matt + * Added intro movie. + * + * 222 4/02/99 3:49p Kevin + * Added profanity filter code + * + * 221 4/02/99 1:26p Matt + * Changes I made for the OEM version but forgot to check in. Oops. + * + * 220 3/19/99 4:08p Kevin + * Multiple CD installation support + * + * 219 3/09/99 12:34p Jeff + * extract scripts to custom\cache + * + * 218 3/08/99 3:29p Samir + * RestartD3 ddio_Init key emulation = true. + * + * 217 3/05/99 5:23p Jeff + * extract scripts from extra.hog before d3.hog + * + * 216 3/04/99 1:15p Jeff + * erase temporary files that may have been left over from last run + * + * 215 3/02/99 5:21p Jason + * fixed some flip bugs + * + * 214 3/02/99 2:21p Samir + * windows 95 and NT both use same keyboard handler due to flaky + * directinput. + * + * 213 3/02/99 1:49a Mark + * put ifdefs around showstaticscreen + * + * 212 3/02/99 12:16a Jeff + * + * 211 3/02/99 12:06a Kevin + * + * 210 3/01/99 11:48p Jeff + * fixed prototype placement + * + * 209 3/01/99 11:47p Kevin + * + * 208 3/01/99 11:38p Kevin + * + * 207 3/01/99 9:03p Kevin + * OEM Beta 4 + * + * 206 2/19/99 10:31p Samir + * added music volume. + * + * 205 2/17/99 12:38p Kevin + * Added -framecap to limit frame and fixed timer bug and old + * framerate limiter code. + * + * 204 2/16/99 12:00p Samir + * added new video resolution swtich test. + * + * 203 2/16/99 12:36a Kevin + * Fixes for release builds of OEM V3 and KAtmai + * + * 202 2/15/99 3:24p Kevin + * + * 201 2/15/99 3:23p Kevin + * Disabled procedurals for gamegauge + * + * 200 2/15/99 3:20p Kevin + * Fixed bug with dlls in a hog + * + * 199 2/15/99 1:22p Kevin + * Changes for GameGauge + * + * 198 2/11/99 10:35p Jeff + * added support for multiple languages in string table files + * + * 197 2/10/99 3:29p Jeff + * extracted dll manager knows difference between game hogs and miission + * hogs + * + * 196 2/09/99 1:29a Jeff + * added code to let D3 process multiplayer games in the background if + * game is not in focus. + * + * 195 2/08/99 2:24p Kevin + * Display bundler.mve and bundler.tga if they are included in the OEM + * package. + * + * 194 2/05/99 7:23p Kevin + * OEM Changes + * + * 193 2/04/99 9:46a Kevin + * OEM version changes + * + * 192 1/31/99 8:51p Jeff + * new in game cinematics system finished + * + * 191 1/29/99 5:22p Jeff + * localization + * + * 190 1/27/99 6:08p Jason + * first pass at markers + * + * 189 1/27/99 2:58p Jason + * fixed center small view + * + * 188 1/25/99 6:47p Samir + * allow slow keyboard + * + * 187 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 186 1/25/99 12:33a Jeff + * save out scorch marks and weapon corona detail settings + * + * 185 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 184 1/17/99 10:41p Kevin + * Added command line options for setting the renderer in GAMEGAUGE mode. + * + * 183 1/13/99 3:25p Jason + * fixed vsync problem + * + * 182 1/08/99 2:58p Kevin + * Added TCP mprintf support so you can log to a remote machine. + * + * 181 1/08/99 2:55p Samir + * Ripped out OSIRIS1. + * + * 180 1/07/99 12:28p Samir + * Call to InitD3Music added parameter. + * + * 179 1/05/99 3:59p Kevin + * + * 178 1/05/99 3:55p Kevin + * Added structured exception handling + * + * 177 1/04/99 5:43p Kevin + * new command line args + * + * 176 12/23/98 6:38p Kevin + * All UDP data (except gamespy) now uses one (registered) port number + * + * 175 12/16/98 12:04p Kevin + * GameSpy! + * + * 174 12/14/98 4:48p Jeff + * call to intialize Osiris module manager + * + * 173 12/10/98 3:41p Jeff + * implemented settings for 16 or 32 bit depth (in config) + * + * 172 12/07/98 11:30a Kevin + * Added some features from the 1.1 demo patch + * + * 171 12/02/98 1:07p Jason + * made vsync work correctly, finally + * + * 170 11/24/98 5:17p Jeff + * initialize Pilot Pics + * + * 169 11/23/98 12:31p Jeff + * save out force feedback stuff (bad merge, we lost it earlier) + * + * 168 11/13/98 2:29p Samir + * new music code. + * + * 166 11/09/98 2:59p Kevin + * Added command line option for disabling multiplayer bitmap exchange + * + * 165 11/03/98 6:43p Jeff + * new low-level & high level Force Feedback system implemented, handles + * window losing focus, etc. + * + * 164 10/24/98 2:18p Samir + * don't do mouse stuff for dedicated server. + * + * 163 10/22/98 2:42p Samir + * fixed ALT-TAB pause issue. + * + * 162 10/22/98 1:58p Kevin + * Fixed the working directory (-setdir) command + * + * 161 10/22/98 1:39p Jeff + * autolevel is default full + * + * 160 10/20/98 6:24p Jason + * added super low memory mode + * + * 159 10/20/98 10:56a Sean + * fixed #ifdef editor thingy + * + * 158 10/20/98 12:58a Jeff + * added a way to force a lo-resolution timer + * + * 157 10/20/98 12:10a Samir + * this should fix the problem of the mouse not working properly in game. + * + * 156 10/19/98 6:30p Jeff + * changes made for detail variables. Put in preset values. Preset + * options. Removed terrain_cast from detail. Put new callbacks in + * UIListBox and UISlider + * + * 153 10/18/98 9:12p Matt + * Use new symbolic constant for the program name. + * + * 152 10/18/98 8:51p Matt + * Revamped debug/error system. + * + * 151 10/18/98 3:36p Jeff + * if _DEBUG isn't defined than the rtp_enable/disableflags call gets + * compiled out + * + * 150 10/18/98 2:50p Kevin + * Use low memory mode in dedicated server mode. + * + * 149 10/17/98 5:50p Jeff + * hooked in rtperformance (run time performance) library + * + * 148 10/17/98 12:46p Kevin + * Beta 4 fixes + * + * 147 10/16/98 2:44p Kevin + * working on getting demo compiling + * + * 146 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 145 10/15/98 1:33p Jeff + * removed amount of debris detail level. Added powerup halo detail + * level. Added predef detail level settings + * + * 144 10/15/98 11:58a Kevin + * checked for low mem + * + * 143 10/14/98 4:37p Matt + * Made InitD3System() exit with error if there's a problem instead of + * returning a status value. Also moved some editor-specific code from + * init.cpp to mainfrm.cpp, and cleaned up some other initialization and + * error-handling code. + * + * 142 10/13/98 3:47p Samir + * added function to check if a cursor is visible + * + * 141 10/13/98 3:41p Kevin + * bug fixes + * + * 140 10/13/98 2:49p Matt + * Open "extra.hog" (if it exists" at startup so we can put any extra data + * in there. + * + * 139 10/12/98 3:01p Jeff + * added vsync and more detail level settings + * + * 138 10/12/98 2:51p Samir + * use main menu background for loading data screen. + * + * 137 10/09/98 8:47p Samir + * don't kill sound library when shutting down game. + * + * 136 10/08/98 7:27p Samir + * don't change defer handlers when shuting down or restarting. + * + * 135 10/08/98 5:55p Matt + * Set main hogfile. + * + * 134 10/08/98 3:36p Jason + * fixes for the demo + * + * 133 10/08/98 1:23p Samir + * shutdown and reinit now do so with ddio systems. + * + * 132 10/07/98 2:54p Jeff + * General UI fixes and additions + * + * 131 10/07/98 12:55p Jason + * fixed some defaults + * + * 130 10/03/98 11:21p Matt + * Added system to seperately control outline mode for mine, terrain, sky, + * & objects + * + * 129 10/01/98 11:57a Matt + * Added some mprintf()s + * + * 128 9/30/98 10:36a Kevin + * Added command line launching of URLs and command line directory + * specification + * + * 127 9/25/98 2:53p Jason + * added progress bar + * + * 126 9/25/98 12:31p Samir + * in main game, hide cursor when starting. + * + * 125 9/23/98 3:08p Jeff + * + * 124 9/22/98 6:58p Samir + * Render_floating_triggers is _DEBUG code, so make it so. + * + * 123 9/22/98 3:55p Samir + * ifdef out stuff for non-debug version. + * + * 122 9/22/98 10:32a Jason + * made preferred renderer default to OpenGL + * + * 121 9/21/98 11:10a Jeff + * Calls to init/close forcefeedback + * + * 120 9/18/98 7:38p Jeff + * creation of low-level forcefeedback and beginning of high-level + * forcefeedback + * + * 119 9/18/98 1:27p Jason + * cleaned up renderer initting + * + * 118 9/16/98 6:07p Jason + * made dedicated server use standard mouse mode + * + * 117 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 116 9/14/98 6:28p Jason + * first pass at getting dedicated server working + * + * 115 9/03/98 12:11p Chris + * Adding matcen support + * + * 114 8/31/98 12:39p Samir + * added code to resume controllers when restoring game mode. + * + * 113 8/27/98 6:23p Jeff + * changed autoleveling in config so it is a slider...had to convert + * global from bool->ubyte. Added fast headlight and mirrored surfaces to + * config menu + * + * 112 8/19/98 2:19p Jeff + * moved detail globals to a struct + * + * 111 8/17/98 6:40p Matt + * Added ambient sound system + * + * 110 8/15/98 5:16p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 109 8/10/98 5:52p Samir + * took out briefings search path. + * + * 108 7/31/98 11:51a Jason + * added player ship choosing to multiplayer games + * + * 107 7/24/98 5:32p Samir + * added initializer for D3 Music. + * + * 106 7/13/98 12:42p Samir + * don't do initmessage if graphics system not initialized. + * + * 105 7/08/98 6:27p Samir + * stream library integrated with highlevel sound system. + * + * 104 7/01/98 4:56p Samir + * redid init code. + * + * 103 6/24/98 11:14a Chris + * Added more support and bug fixes + * + * 102 6/23/98 6:00p Chris + * DirectSound 16 mixer is active with 8 bit samples + * + * 101 6/22/98 12:00p Chris + * Working on sound system to make it in a nice shape. + * + * 100 6/19/98 6:42p Jason + * made specular mapping a config detail item + * + * 99 6/19/98 6:04p Jeff + * variables to turn on/off voices + * + * 98 6/18/98 5:22p Jeff + * initialize streaming audio system + * + * 97 6/17/98 3:58p Jeff + * put in PilotInit(). String table loaded near begining for Main exe, + * after tables are loaded for editor + * + * 96 6/16/98 3:48p Chris + * Updated the sound system and added the start of sound streaming + * + * 95 6/16/98 11:49a Jeff + * string table loaded after manage system + * + * 94 6/16/98 10:54a Jeff + * + * 93 6/12/98 5:56p Jeff + * Load string tables during init + * + * 92 6/08/98 3:56p Jeff + * added initializing functions for compressed audio and voice systems + * + * 91 6/02/98 6:03p Jason + * added specular lightmaps + * + * 90 6/02/98 4:37p Samir + * multiple joysticks supported. + * + * 89 6/01/98 11:37a Chris + * + * 88 5/19/98 3:43p Samir + * must move mem_Init after error_Init. + * + * 87 5/19/98 3:37p Samir + * added mem_Init + * + * 86 5/14/98 11:49a Chris + * Bettered the sound paging system + * + * 85 5/12/98 2:05p Craig + * errror + * + * 84 5/12/98 11:45a Samir + * added logfile init. + * + * 83 5/06/98 6:34p Jeff + * Added Terrain casting to config + * + * 82 5/06/98 4:32p Samir + * moved ui init back to it's proper place. + * + * 81 5/06/98 12:44p Jeff + * added saving/restoring video resolution + * + * 80 5/05/98 5:16p Samir + * took out calls to init UI system (that's done now in SetScreenMode) + * + * 79 5/05/98 3:02p Jason + * attempting to add different screen resolutions + * + * 78 5/04/98 3:42p Matt + * Page in all sounds at init time, so you don't have to wait when you're + * starting a game + * + * 77 5/01/98 4:48p Jeff + * Added autoleveling into the configuration + * + * 76 4/28/98 6:42p Matt + * If registry says renderer is software, force it to be glide. + * + * 75 4/28/98 11:56a Jeff + * moved things from config (d3.cfg) into the registry + * + * 74 4/24/98 1:53a Samir + * added trigger init and free. + * + * 73 4/16/98 6:53a Samir + * added default script warning system. + * + * 72 4/16/98 12:23a Samir + * fixed bug when checking if script compiled in editor. + * + * 71 4/14/98 7:31p Matt + * Changed code to use ddio_MakePath() instead of sprintf() to create file + * spec + * + * 70 4/10/98 7:48p Samir + * make sure to set default values for outlining, etc. + * + * 69 4/09/98 5:30p Samir + * new main menu art. + * + * 68 4/08/98 7:19p Samir + * Added runtime debugging option. + * + * 67 4/07/98 9:20p Samir + * Changes to debug stuff. + * + * 66 4/06/98 5:13p Chris + * Sounds page in at the beginning of a level + * + * 65 4/02/98 7:58p Samir + * make sure to pause game and close/init control system when application + * looses focus. + * + * 64 3/27/98 5:18p Samir + * initialize debug break handlers differently for editor. + * + * 63 3/27/98 3:29p Brent + * if joy init fails, don't try initializing joystick. (samir) + * + * 62 3/27/98 2:44p Samir + * Restore joystick initialization. + * + * 61 3/23/98 8:03p Samir + * A bunch of changes to allow for ALT-TAB to work. + * + * 60 3/20/98 5:51p Jason + * more changes for multiplayer + * + * 59 3/20/98 1:19p Jeff + * Changes made to use Default_pilot string for pilot filename to use. + * + * 58 3/19/98 11:27a Samir + * Better error checking. + * + * 57 3/13/98 1:22p Jason + * Moved UseHardware flag to the renderer lib where it belongs + * + * 56 3/10/98 5:16p Samir + * Got debug callbacks working when you hit an Int3. + * + * 55 3/05/98 6:39p Samir + * Initialize ui system with UI_FONT + * + * 54 3/04/98 12:00p Jason + * added default gamma settings + * + * 53 2/27/98 5:35p Jeff + * Added in loading and saving of a game configuration file + * + * 52 2/18/98 1:33a Jason + * don't do mip mapping by default + * + * 51 2/18/98 1:11a Samir + * Some game window mess. + * + * 50 2/17/98 2:21p Samir + * Added d3xdebug.h + * + * 49 2/14/98 10:48p Jason + * got preferred rendering working + * + * 46 2/11/98 5:55p Samir + * Now InitD3XDebug and CloseD3XDebug are called when you enter and leave + * the game. This as well as ui_Init and ui_Close + * + * 45 2/02/98 7:34p Samir + * Moved D3X Initialization to PlayGame. + * + * 44 1/29/98 12:24p Samir + * Added logfile support. + * + * 43 1/27/98 1:20p Samir + * Fixed initialization for main game. + * + * 42 1/13/98 12:31p Samir + * only register small font for editor. + * + * 41 1/02/98 5:32p Chris + * More radical changes to the sound system + * + * 40 12/31/97 1:01a Chris + * Removed old dead code + * + * + * + * $NoKeywords: $ + */ + +// Initialization routines for Descent3/Editor + +#include + +#include "mono.h" +#include "application.h" +#include "gametexture.h" +#include "object.h" +#include "vecmat.h" +#include "init.h" +#include "config.h" +#include "3d.h" +#include "hlsoundlib.h" +#include "manage.h" +#include "bitmap.h" +#include "ddio.h" +#include "joystick.h" +#include "render.h" +#include "descent.h" +#include "renderer.h" +#include "vclip.h" +#include "grdefs.h" +#include "pserror.h" +#include "lighting.h" +#include "program.h" +#include "polymodel.h" +#include "door.h" +#include "terrain.h" +#include "soundload.h" +#include "ship.h" +#include "controls.h" +#include "texture.h" +#include "Mission.h" +#include "findintersection.h" +#include "appdatabase.h" +#include "AppConsole.h" +#include "room.h" +#include "game.h" +#include "render.h" +#include "gamefile.h" +#include "TelCom.h" +#include "objinfo.h" +#include "ObjScript.h" +#include "cinematics.h" +#include "lightmap_info.h" +#include "fireball.h" +#include "networking.h" +#include "args.h" +#include "pilot.h" +#include "d3serial.h" +#include "gameloop.h" +#include "trigger.h" +#include "PHYSICS.H" +#include "special_face.h" +#include "streamaudio.h" +#include "voice.h" +#include "localization.h" +#include "stringtable.h" +#include "hlsoundlib.h" +#include "player.h" +#include "ambient.h" +#include "matcen.h" +#include "dedicated_server.h" +#include "D3ForceFeedback.h" +#include "newui.h" +#include "SmallViews.h" +#include "uisys.h" +#include "rtperformance.h" +#include "d3music.h" +#include "PilotPicsAPI.h" +#include "osiris_dll.h" +//#include "gamespy.h" +#include "mem.h" +#include "multi.h" +#include "marker.h" +#include "gamecinematics.h" +#include "debuggraph.h" +#include "rocknride.h" +#include "vibeinterface.h" + +//Uncomment this for all non-US versions!! +//#define LASERLOCK + +//Uncomment this to allow all languages +#define ALLOW_ALL_LANG 1 + +#if defined(WIN32) && defined(LASERLOCK) +#include "laserlock.h" +#endif + +#ifdef EDITOR + #include "editor\HFile.h" + #include "editor\d3edit.h" + #include "slew.h" + #include "gr.h" + #define INIT_MESSAGE(c) SplashMessage c +#else + void IntroScreen(); + #define INIT_MESSAGE(c) InitMessage(c) +#endif + +#if defined(EDITOR)||defined(NEWEDITOR) +bool Running_editor = true;//didn't we have a variable like this somewhere +#else +bool Running_editor = false;//didn't we have a variable like this somewhere +#endif + +static bool Init_in_editor = false; + +// used to update load bar. +void SetInitMessageLength(char *c, float amount); // portion of total bar to fill (0 to 1) +void UpdateInitMessage(float amount); // amount is 0 to 1 +void SetupTempDirectory(void); +void DeleteTempFiles(void); + + +#define TEMPBUFFERSIZE 256 + +//If there's a joystick, this is the stick number. Else, this is -1 +char App_ddvid_subsystem[8]; + +// other info. +static chunked_bitmap Title_bitmap; +static bool Init_systems_init = false; +static bool Graphics_init = false; +static bool Title_bitmap_init = false; +ubyte Use_motion_blur = 0; + +// The "root" directory of the D3 file tree +char Base_directory[_MAX_PATH]; + +extern int Min_allowed_frametime; + +extern bool Mem_low_memory_mode; +extern bool Render_powerup_sparkles; +extern int Use_file_xfer; + +extern bool Mem_superlow_memory_mode; + +const float kDefaultMouselookSensitivity = 9.102f; +const float kAnglesPerDegree = 65536.0f / 360.0f; +int CD_inserted = 0; +float Mouselook_sensitivity = kAnglesPerDegree * kDefaultMouselookSensitivity; +float Mouse_sensitivity = 1.0f; + +int IsLocalOk(void) +{ +#ifdef WIN32 +#ifdef ALLOW_ALL_LANG + return 1; +#endif + + switch (PRIMARYLANGID(GetSystemDefaultLangID())) + { + case LANG_JAPANESE: + /* Japanese */ + return 0; +#ifndef LASERLOCK + //If no laserlock that means this is the US distribution + case LANG_ENGLISH: + if( (SUBLANG_ENGLISH_US!=SUBLANGID(GetSystemDefaultLangID())) && (SUBLANG_ENGLISH_CAN!=SUBLANGID(GetSystemDefaultLangID())) ) + return 0; + else + return 1; + break; + default: + //Non-japanese or english version + return 0; +#endif + } +#endif + return 1; +} + + + +void PreGameCdCheck() +{ + CD_inserted = 0; + do + { + char *p = NULL; +#if defined (MACINTOSH) + p = ddio_GetCDDrive("Descent3"); + if(p && *p) + { + CD_inserted = 1; + break; + } +#elif defined (OEM) + p = ddio_GetCDDrive("D3OEM_1"); + if(p && *p) + { + CD_inserted = 1; + break; + } +#else + p = ddio_GetCDDrive("D3_DVD"); + if(p && *p) + { + CD_inserted = 3; + break; + } + p = ddio_GetCDDrive("D3_1"); + if(p && *p) + { + CD_inserted = 1; + break; + } + p = ddio_GetCDDrive("D3_2"); + if(p && *p) + { + CD_inserted = 2; + break; + } +#endif + + if(!CD_inserted) + { +#if defined(WIN32) + char message_txt[50]; + sprintf(message_txt,TXT_CDPROMPT,1); + ShowCursor(true); + HWND dwnd = FindWindow(NULL,PRODUCT_NAME); + if(MessageBox(dwnd,message_txt,PRODUCT_NAME,MB_OKCANCEL)==IDCANCEL) + { + exit(0); + } + ShowCursor(false); + +#elif defined(MACINTOSH) + ::ShowCursor(); + short action = ::Alert(130, NULL); + if(action == 1) + ::ExitToShell(); +#elif defined(__LINUX__) + // ummm what should we do in Linux? + // right now I'm just going to return this hopefully will be safe, as I think + // this function is only really used for a copy protection check of sorts, if they + // don't have the CD in on start, then they don't get the intro movie + return; +#endif + } + + }while(!CD_inserted); +} + + +/* + Initializes all the subsystems that need to be initialized BEFORE application creation. + Returns 1 if all is good, 0 if something is wrong +*/ +void PreInitD3Systems() +{ +// initialize error system + bool debugging = false, console_output = false; + + #ifndef RELEASE + + debugging = (FindArg("-debug") != 0); + + #ifdef MONO + console_output = true; + #endif + #ifndef MACINTOSH + if (FindArg("-logfile")) + Debug_Logfile("d3.log"); + #endif + + #endif + + +#ifdef DAJ_DEBUG + debugging = true; +#endif + + error_Init(debugging, console_output, PRODUCT_NAME); + + #ifndef EDITOR + { + int serial_error = SerialCheck(); //determine if its ok to run based on serialization + + if(serial_error) { + SerialError(serial_error); + exit(1); + } + } +#endif + if(FindArg("-lowmem")) + Mem_low_memory_mode = true; + if(FindArg("-superlowmem")) + Mem_low_memory_mode = Mem_superlow_memory_mode = true; + if(FindArg("-dedicated")) + Mem_low_memory_mode = true; +#ifndef MACINTOSH + mem_Init(); +#endif + if(FindArg("-himem")) + { + Mem_low_memory_mode = false; + Mem_superlow_memory_mode = false; + } + //For the client and the server, this turns off the bitmap exchange system. + if(FindArg("-nomultibmp")) + Use_file_xfer = 0; + + int iframelmtarg = FindArg("-limitframe"); + if(iframelmtarg) + { + Min_allowed_frametime = atoi(GameArgs[iframelmtarg+1]); + mprintf((0,"Using %d as a minimum frametime\n",Min_allowed_frametime)); + } + else + { + if(FindArg("-dedicated")) + Min_allowed_frametime=30; + else + Min_allowed_frametime=0; + } + iframelmtarg = FindArg("-framecap"); + if(iframelmtarg) + { + Min_allowed_frametime = ((float)1.0/(float)atoi(GameArgs[iframelmtarg+1]))*1000; + mprintf((0,"Using %d as a minimum frametime\n",Min_allowed_frametime)); + } + else + { + // Default to a framecap of 60 + Min_allowed_frametime = (1.0/60.0)*1000; + mprintf ((0,"Using default framecap of 60\n")); + } + + //Mouselook sensitivity! + int msensearg = FindArg("-mlooksens"); + if(msensearg) + { + Mouselook_sensitivity = kAnglesPerDegree * atof( GameArgs[msensearg+1] ); + mprintf((0,"Using mouselook sensitivity of %f\n",Mouselook_sensitivity)); + } + + //Mouse sensitivity (non-mouselook) + msensearg = FindArg("-mousesens"); + if(msensearg) + { + Mouse_sensitivity = atof(GameArgs[msensearg+1]); + mprintf((0,"Using mouse sensitivity of %f\n",Mouse_sensitivity)); + } + + grtext_Init(); + + #ifndef RELEASE + SetMessageBoxTitle(PRODUCT_NAME " Message"); + #endif +} + + +/* + Save game variables to the registry +*/ +void SaveGameSettings() +{ + char tempbuffer[TEMPBUFFERSIZE]; + int tempint; + + sprintf(tempbuffer,"%f",Render_preferred_state.gamma); + Database->write("RS_gamma",tempbuffer,strlen(tempbuffer)+1); + + sprintf(tempbuffer,"%f",Sound_system.GetMasterVolume()); + Database->write("SND_mastervol",tempbuffer,strlen(tempbuffer)+1); + + sprintf(tempbuffer,"%f",D3MusicGetVolume()); + Database->write("MUS_mastervol",tempbuffer,strlen(tempbuffer)+1); + + sprintf(tempbuffer,"%f",Detail_settings.Pixel_error); + Database->write("RS_pixelerror",tempbuffer,strlen(tempbuffer)+1); + + sprintf(tempbuffer,"%f",Detail_settings.Terrain_render_distance/((float)TERRAIN_SIZE)); + Database->write("RS_terraindist",tempbuffer,strlen(tempbuffer)+1); + + Database->write("Dynamic_Lighting",Detail_settings.Dynamic_lighting); + +#ifdef _DEBUG + Database->write("Outline_mode",Outline_mode); + Database->write("Lighting_on",Lighting_on); + Database->write("Render_floating_triggers",Render_floating_triggers); +#endif + + Database->write("TerrLeveling",Default_player_terrain_leveling); + Database->write("RoomLeveling",Default_player_room_leveling); + //Database->write("Terrain_casting",Detail_settings.Terrain_casting); + Database->write("Specmapping",Detail_settings.Specular_lighting); + Database->write("FastHeadlight",Detail_settings.Fast_headlight_on); + Database->write("MirrorSurfaces",Detail_settings.Mirrored_surfaces); + Database->write("MissileView",Missile_camera_window); +#ifndef GAMEGAUGE + Database->write("RS_vsync",Render_preferred_state.vsync_on); +#else + Render_preferred_state.vsync_on = 0; +#endif + Database->write("DetailScorchMarks",Detail_settings.Scorches_enabled); + Database->write("DetailWeaponCoronas",Detail_settings.Weapon_coronas_enabled); + Database->write("DetailFog",Detail_settings.Fog_enabled); + Database->write("DetailCoronas",Detail_settings.Coronas_enabled); + Database->write("DetailProcedurals",Detail_settings.Procedurals_enabled); + Database->write("DetailObjectComp",Detail_settings.Object_complexity); + Database->write("DetailPowerupHalos",Detail_settings.Powerup_halos); + + Database->write("RS_resolution", Game_video_resolution); + + Database->write("RS_bitdepth",Render_preferred_bitdepth); + Database->write("RS_bilear",Render_preferred_state.filtering); + Database->write("RS_mipping",Render_preferred_state.mipping); + Database->write("RS_color_model",Render_state.cur_color_model); + Database->write("RS_light",Render_state.cur_light_state); + Database->write("RS_texture_quality",Render_state.cur_texture_quality); + + Database->write("VoicePowerup",PlayPowerupVoice); + Database->write("VoiceAll",PlayVoices); + + // Write out force feedback +#ifndef MACINTOSH + Database->write("EnableJoystickFF",D3Use_force_feedback); + Database->write("ForceFeedbackAutoCenter",D3Force_auto_center); + ubyte force_gain; + if(D3Force_gain<0.0f) D3Force_gain = 0.0f; + if(D3Force_gain>1.0f) D3Force_gain = 1.0f; + force_gain = (ubyte)((100.0f * D3Force_gain)+0.5f); + Database->write("ForceFeedbackGain",force_gain); +#endif + +#ifndef RELEASE // never save this value out in release. + Database->write("SoundMixer", Sound_mixer); + Database->write("PreferredRenderer",PreferredRenderer); +#endif + + tempint = Sound_quality; + Database->write("SoundQuality", tempint); + + Database->write("SoundQuantity",Sound_system.GetLLSoundQuantity()); + + if(Default_pilot[0]!='\0') + Database->write("Default_pilot",Default_pilot,strlen(Default_pilot)+1); + else + Database->write("Default_pilot"," ",2); +} + +extern bool Game_gauge_do_time_test; +extern char Game_gauge_usefile[_MAX_PATH]; + +/* + Read game variables from the registry +*/ +void LoadGameSettings() +{ + char tempbuffer[TEMPBUFFERSIZE],*stoptemp; + int templen = TEMPBUFFERSIZE; + int tempint; + + //set defaults +#ifdef _DEBUG + Outline_mode = OM_ALL; + Lighting_on = true; +#endif + + int tt_arg = FindArg("-timetest"); + if(tt_arg) + { + Game_gauge_do_time_test = true; + strcpy(Game_gauge_usefile,GameArgs[tt_arg+1]); + } + + Detail_settings.Specular_lighting = false; + Detail_settings.Dynamic_lighting = true; + Detail_settings.Fast_headlight_on = true; + Detail_settings.Mirrored_surfaces = true; + Detail_settings.Scorches_enabled = true; + Detail_settings.Weapon_coronas_enabled = true; + Render_preferred_state.mipping = true; + Render_preferred_state.filtering = true; + Render_preferred_state.bit_depth = 16; + Render_preferred_bitdepth = 16; + Default_player_terrain_leveling = 2; + Default_player_room_leveling = 2; + Render_preferred_state.gamma = 1.5; + PreferredRenderer = RENDERER_NONE; + +#ifdef MACINTOSH + +//DAJ render switch +#if defined(USE_OPENGL) + PreferredRenderer = RENDERER_OPENGL; +#elif defined(USE_GLIDE) + PreferredRenderer = RENDERER_GLIDE; +#elif defined(USE_SOFTWARE) + PreferredRenderer = RENDERER_SOFTWARE_16BIT; +#else + PreferredRenderer = RENDERER_NONE; +#endif + +#endif + + Sound_system.SetLLSoundQuantity(MIN_SOUNDS_MIXED+ (MAX_SOUNDS_MIXED-MIN_SOUNDS_MIXED)/2); + D3MusicSetVolume(0.5f); + Detail_settings.Pixel_error = 8.0; +#ifdef MACINTOSH + Sound_system.SetMasterVolume(0.7); + Detail_settings.Terrain_render_distance = 10.0 * TERRAIN_SIZE; //DAJ +#else + Sound_system.SetMasterVolume(1.0); + Detail_settings.Terrain_render_distance = 70.0 * TERRAIN_SIZE; + D3Use_force_feedback = true; + D3Force_gain = 1.0f; + D3Force_auto_center = true; +#endif + Game_video_resolution = RES_640X480; + PlayPowerupVoice = true; + PlayVoices = true; + Sound_mixer = SOUND_MIXER_SOFTWARE_16; +#ifdef MACINTOSH + Sound_quality = SQT_LOW; +#else + Sound_quality = SQT_NORMAL; +#endif + Missile_camera_window = SVW_LEFT; + Render_preferred_state.vsync_on = true; + Detail_settings.Fog_enabled = true; + Detail_settings.Coronas_enabled = true; + Detail_settings.Procedurals_enabled = true; + Detail_settings.Object_complexity = 1; + Detail_settings.Powerup_halos = true; + + ddio_SetKeyboardLanguage(KBLANG_AMERICAN); + + if (Database->read("KeyboardType", tempbuffer, &templen)) + { + if (strcmpi(tempbuffer, "French") == 0) { + ddio_SetKeyboardLanguage(KBLANG_FRENCH); + } + else if (strcmpi(tempbuffer, "German") == 0) { + ddio_SetKeyboardLanguage(KBLANG_GERMAN); + } + } + + templen = TEMPBUFFERSIZE; + if(Database->read("RS_gamma",tempbuffer,&templen)) + { + Render_preferred_state.gamma = strtod(tempbuffer,&stoptemp); + } + templen = TEMPBUFFERSIZE; + if(Database->read("SND_mastervol",tempbuffer,&templen)) + { + Sound_system.SetMasterVolume(strtod(tempbuffer,&stoptemp)); + } + templen = TEMPBUFFERSIZE; + if(Database->read("MUS_mastervol",tempbuffer,&templen)) + { + D3MusicSetVolume(strtod(tempbuffer,&stoptemp)); + } + templen = TEMPBUFFERSIZE; + if(Database->read("RS_pixelerror",tempbuffer,&templen)) + { + Detail_settings.Pixel_error = strtod(tempbuffer,&stoptemp); + } + templen = TEMPBUFFERSIZE; + if(Database->read("RS_terraindist",tempbuffer,&templen)) + { + Detail_settings.Terrain_render_distance = strtod(tempbuffer,&stoptemp) * TERRAIN_SIZE; + } + templen = TEMPBUFFERSIZE; + Database->read_int("Dynamic_Lighting",&Detail_settings.Dynamic_lighting); + +#ifdef _DEBUG + Database->read_int("Outline_mode",&Outline_mode); + Database->read("Lighting_on",&Lighting_on); + Database->read("Render_floating_triggers",&Render_floating_triggers); +#endif + + Database->read_int("TerrLeveling",&Default_player_terrain_leveling); + Database->read_int("RoomLeveling",&Default_player_room_leveling); + //Database->read("Terrain_casting",&Detail_settings.Terrain_casting); + Database->read("Specmapping",&Detail_settings.Specular_lighting); + Database->read("RS_bitdepth",&Render_preferred_bitdepth,sizeof(Render_preferred_bitdepth)); + Database->read_int("RS_resolution", &Game_video_resolution); + Database->read_int("RS_bilear",&Render_preferred_state.filtering); + Database->read_int("RS_mipping",&Render_preferred_state.mipping); + Database->read_int("RS_color_model",&Render_state.cur_color_model); + Database->read_int("RS_light",&Render_state.cur_light_state); + Database->read_int("RS_texture_quality",&Render_state.cur_texture_quality); +#ifdef MACINTOSH + if(Render_state.cur_texture_quality == 0) { + Mem_low_memory_mode = true; + Mem_superlow_memory_mode = true; + } else if(Render_state.cur_texture_quality == 1) { + Mem_low_memory_mode = true; + Mem_superlow_memory_mode = false; + } else if(Render_state.cur_texture_quality == 2) { + Mem_low_memory_mode = false; + Mem_superlow_memory_mode = false; + } +#else + // force feedback stuff + Database->read("EnableJoystickFF",&D3Use_force_feedback); + Database->read("ForceFeedbackAutoCenter",&D3Force_auto_center); + ubyte force_gain; + Database->read("ForceFeedbackGain",&force_gain,sizeof(force_gain)); + if(force_gain>100) force_gain = 100; + D3Force_gain = ((float)force_gain)/100.0f; +#endif + Database->read_int("PreferredRenderer",&PreferredRenderer); + Database->read_int("MissileView",&Missile_camera_window); + Database->read("FastHeadlight",&Detail_settings.Fast_headlight_on); + Database->read("MirrorSurfaces",&Detail_settings.Mirrored_surfaces); + Database->read_int("RS_vsync",&Render_preferred_state.vsync_on); + + if (FindArg ("-vsync")) + Render_preferred_state.vsync_on=true; + + +//@@ // Base missile camera if in wrong window +//@@ if (Missile_camera_window==SVW_CENTER) +//@@ Missile_camera_window=SVW_LEFT; + + Database->read("VoicePowerup",&PlayPowerupVoice); + Database->read("VoiceAll",&PlayVoices); + + Database->read("DetailScorchMarks",&Detail_settings.Scorches_enabled); + Database->read("DetailWeaponCoronas",&Detail_settings.Weapon_coronas_enabled); + Database->read("DetailFog",&Detail_settings.Fog_enabled); + Database->read("DetailCoronas",&Detail_settings.Coronas_enabled); + Database->read("DetailProcedurals",&Detail_settings.Procedurals_enabled); + Database->read_int("DetailObjectComp",&tempint); + Detail_settings.Object_complexity = tempint; + if(Detail_settings.Object_complexity<0 || Detail_settings.Object_complexity>2) + Detail_settings.Object_complexity = 1; + + Database->read("DetailPowerupHalos",&Detail_settings.Powerup_halos); + + if(Database->read_int("SoundMixer",&tempint)) + Sound_mixer = tempint; + + if(Database->read_int("SoundQuality",&tempint)) + Sound_quality = tempint; + + if(Database->read_int("SoundQuantity",&tempint)) { + Sound_system.SetLLSoundQuantity(tempint); + } + + Sound_card_name[0] = 0; + templen = TEMPBUFFERSIZE; + if (Database->read("SoundcardName",tempbuffer, &templen)) { + strcpy(Sound_card_name, tempbuffer); + } + + int len = _MAX_PATH; + Database->read("Default_pilot",Default_pilot,&len); + + //If preferred renderer set to software, force it to be glide + if ((PreferredRenderer == RENDERER_SOFTWARE_8BIT) || (PreferredRenderer == RENDERER_SOFTWARE_16BIT)) { + Int3(); //Warning: rederer was set to Software. Ok to ignore this. + PreferredRenderer = RENDERER_OPENGL; + } + + //Now that we have read in all the data, set the detail level if it is a predef setting (custom is ignored in function) + + int level; +#ifdef MACINTOSH + #ifdef USE_OPENGL + level = DETAIL_LEVEL_LOW; + #else + level = DETAIL_LEVEL_HIGH; + #endif +#else + level = DETAIL_LEVEL_MED; +#endif + + Database->read_int("PredefDetailSetting",&level); + ConfigSetDetailLevel(level); + int widtharg = FindArg("-Width"); + int heightarg = FindArg("-Height"); + if(widtharg) + { + Video_res_list[N_SUPPORTED_VIDRES-1].width = atoi(GameArgs[widtharg+1]); + Game_video_resolution = N_SUPPORTED_VIDRES-1; + } + if(heightarg) + { + Video_res_list[N_SUPPORTED_VIDRES-1].height = atoi(GameArgs[heightarg+1]); + Game_video_resolution = N_SUPPORTED_VIDRES-1; + } + + // Motion blur + Use_motion_blur = 0; + if(Katmai || FindArg("-motionblur")) + { + if(!FindArg("-nomotionblur")) + { + Use_motion_blur = 1; + } + } + + Render_powerup_sparkles = false; + if(Katmai && !FindArg("-nosparkles")) + { + Render_powerup_sparkles = true; + } + +#ifdef GAMEGAUGE + // Setup some default params for gamegauge + Detail_settings.Scorches_enabled=1; + Detail_settings.Weapon_coronas_enabled=1; + Detail_settings.Fog_enabled=1; + Detail_settings.Coronas_enabled=1; + Detail_settings.Procedurals_enabled=1; + Detail_settings.Powerup_halos=1; + Detail_settings.Mirrored_surfaces=1; + Detail_settings.Pixel_error = 8; + Detail_settings.Terrain_render_distance = 120 * TERRAIN_SIZE; + Detail_settings.Specular_lighting=1; + Use_motion_blur = 0; + + if(1) +#else + if(Game_gauge_do_time_test) +#endif + { + Detail_settings.Procedurals_enabled = 0; + } + + // We only support OpenGL now... + PreferredRenderer = RENDERER_OPENGL; +} + + +typedef struct +{ + char *wildcard; +}tTempFileInfo; +tTempFileInfo temp_file_wildcards[] = +{ + {"d3s*.tmp"}, + {"d3m*.tmp"}, + {"d3o*.tmp"}, + {"d3c*.tmp"}, + {"d3t*.tmp"}, + {"d3i*.tmp"} +}; +int num_temp_file_wildcards = sizeof(temp_file_wildcards)/sizeof(tTempFileInfo); + +//Returns true if Mercenary is installed +bool MercInstalled() +{ +#if defined(LINUX) + return false; // TODO: check for mercenary +#else + HKEY key; + DWORD lType; + LONG error; + + #define BUFLEN 2000 + char dir[BUFLEN]; + DWORD dir_len = BUFLEN; + + error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Descent3 Mercenary", 0, KEY_ALL_ACCESS, &key); + + if ( (error == ERROR_SUCCESS) ) + { + lType = REG_EXPAND_SZ; + + unsigned long len = BUFLEN; + error = RegQueryValueEx(key, "UninstallString", NULL, &lType, (unsigned char*)dir, &len); + + if (error == ERROR_SUCCESS) + { + // Mercenary is installed + return true; + } + } + + //Mercenary not installed + return false; +#endif +} + + +/* + I/O systems initialization +*/ +void InitIOSystems(bool editor) +{ + ddio_init_info io_info; + int dirlen = _MAX_PATH; + + //Set the base directory + int dirarg = FindArg("-setdir"); + int exedirarg = FindArg("-useexedir"); + if(dirarg) + { + strcpy(Base_directory,GameArgs[dirarg+1]); + } + else if(exedirarg) + { + strcpy(Base_directory,GameArgs[0]); + int len = strlen(Base_directory); + for(int i=(len-1);i>=0;i--) + { + if(i=='\\') + { + Base_directory[i] = NULL; + } + } + mprintf((0,"Using working directory of %s\n",Base_directory)); + } + else + { + ddio_GetWorkingDir(Base_directory,sizeof(Base_directory)); + } + + ddio_SetWorkingDir(Base_directory); + + Descent->set_defer_handler(D3DeferHandler); + +// do io init stuff + io_info.obj = Descent; + io_info.use_lo_res_time = (bool)(FindArg("-lorestimer")!=0); + io_info.joy_emulation = (bool)((FindArg("-alternatejoy")==0) && (FindArg("-directinput")==0)); + io_info.key_emulation = true; //(bool)(FindArg("-slowkey")!=0); WIN95: DirectInput is flaky for some keys. + if (!ddio_Init(&io_info)) { + Error("I/O initialization failed."); + } + + if( !editor && !FindArg("-windowed") ) + { + if (Dedicated_server) + { + ddio_MouseMode(MOUSE_STANDARD_MODE); + } + else + { + ddio_MouseMode(MOUSE_EXCLUSIVE_MODE); + } + } + + int rocknride_arg = FindArg("-rocknride"); + if(rocknride_arg) + { + int comm_port = atoi(GameArgs[rocknride_arg+1]); + if(!RNR_Initialize(comm_port)) + { + mprintf((0,"Rock'n'Ride Init failed!\n")); + }else + { + mprintf((0,"Rock'n'Ride Init success!\n")); + } + } + + rtp_Init(); + RTP_ENABLEFLAGS(RTI_FRAMETIME|RTI_RENDERFRAMETIME|RTI_MULTIFRAMETIME|RTI_MUSICFRAMETIME|RTI_AMBSOUNDFRAMETIME); + RTP_ENABLEFLAGS(RTI_WEATHERFRAMETIME|RTI_PLAYERFRAMETIME|RTI_DOORFRAMETIME|RTI_LEVELGOALTIME|RTI_MATCENFRAMETIME); + RTP_ENABLEFLAGS(RTI_OBJFRAMETIME|RTI_AIFRAMETIME|RTI_PROCESSKEYTIME); + +// Read in stuff from the registry + LoadGameSettings(); + + // Setup temp directory + SetupTempDirectory(); + +// Initialize file system + INIT_MESSAGE(("Managing file system.")); + + //delete any leftover temp files + mprintf((0,"Removing any temp files left over from last execution\n")); + DeleteTempFiles(); + +// create directory system. + if (!mng_InitTableFiles()) + { + #ifdef EDITOR + Error("Couldn't successfully initialize the table files. I'm shutting down!"); + #else + Error(TXT_INITTABLEERR); + #endif + } + + //Init hogfiles + int d3_hid=-1,extra_hid=-1,merc_hid=-1,sys_hid=-1,extra13_hid=-1; + char fullname[_MAX_PATH]; + + #ifdef DEMO +//DAJ d3_hid = cf_OpenLibrary("d3demo.hog"); + ddio_MakePath(fullname, LocalD3Dir, "d3demo.hog", NULL); + #else + ddio_MakePath(fullname, LocalD3Dir, "d3.hog", NULL); + #endif + d3_hid = cf_OpenLibrary(fullname); + + #ifdef __LINUX__ +//DAJ sys_hid = cf_OpenLibrary("d3-linux.hog"); + ddio_MakePath(fullname, LocalD3Dir, "d3-linux.hog", NULL); + sys_hid = cf_OpenLibrary(fullname); + #endif + #ifdef MACOSX + ddio_MakePath(fullname, LocalD3Dir, "d3-osx.hog", NULL); + sys_hid = cf_OpenLibrary(fullname); + #endif + + + //Open this file if it's present for stuff we might add later + ddio_MakePath(fullname, LocalD3Dir, "extra.hog", NULL); + extra_hid = cf_OpenLibrary(fullname); + + // Opens the mercenary hog if it exists and the registry entery is present + // Win32 only. Mac and Linux users are SOL for now +#ifdef WIN32 + HKEY key; + DWORD lType; + LONG error; + + #define BUFLEN 2000 + char dir[BUFLEN]; + DWORD dir_len = BUFLEN; + + error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Descent3 Mercenary", 0, KEY_ALL_ACCESS, &key); + + if ( (error == ERROR_SUCCESS) ) + { + lType = REG_EXPAND_SZ; + + unsigned long len = BUFLEN; + error = RegQueryValueEx(key, "UninstallString", NULL, &lType, (unsigned char*)dir, &len); + + if (error == ERROR_SUCCESS) + { + merc_hid = cf_OpenLibrary("merc.hog"); + } + } +#else + // non-windows platforms always get merc! + merc_hid = cf_OpenLibrary("merc.hog"); +#endif + // Open this for extra 1.3 code (Black Pyro, etc) + ddio_MakePath(fullname, LocalD3Dir, "extra13.hog", NULL); + extra13_hid = cf_OpenLibrary(fullname); + + //Check to see if there is a -mission command line option + //if there is, attempt to open that hog/mn3 so it can override such + //things as the mainmenu movie, or loading screen + int mission_arg = FindArg("-mission"); + if(mission_arg>0) + { + char path_to_mission[_MAX_PATH]; + char filename[256]; + + // get the true filename + ddio_SplitPath(GameArgs[mission_arg+1],NULL,filename,NULL); + strcat(filename,".mn3"); + + // make the full path (it is forced to be on the harddrive since it contains + // textures and stuff). + ddio_MakePath(path_to_mission,LocalD3Dir,"missions",filename,NULL); + if(cfexist(path_to_mission)) + { + cf_OpenLibrary(path_to_mission); + }else + { + Int3();//mission not found + } + } + + // Initialize debug graph early incase any system uses it in it's init + DebugGraph_Initialize(); + + // initialize all the OSIRIS systems + // extract from extra.hog first, so it's dll files are listed ahead of d3.hog's + Osiris_InitModuleLoader(); + if(extra13_hid!=-1) + Osiris_ExtractScriptsFromHog(extra13_hid,false); + if(extra_hid!=-1) + Osiris_ExtractScriptsFromHog(extra_hid,false); + if(merc_hid!=-1) + Osiris_ExtractScriptsFromHog(merc_hid,false); + if(sys_hid!=-1) + Osiris_ExtractScriptsFromHog(sys_hid,false); + Osiris_ExtractScriptsFromHog(d3_hid,false); +} + +extern int Num_languages; +void InitStringTable() +{ + +#if defined (MACINTOSH) && !defined (DAJ_DEBUG) + if(cfopen("german.lan", "rt")) { + Localization_SetLanguage(LANGUAGE_GERMAN); + ddio_SetKeyboardLanguage(KBLANG_GERMAN); + } else if(cfopen("spanish.lan", "rt")) { + Localization_SetLanguage(LANGUAGE_SPANISH); + ddio_SetKeyboardLanguage(KBLANG_AMERICAN); + } else if(cfopen("italian.lan", "rt")) { + Localization_SetLanguage(LANGUAGE_ITALIAN); + ddio_SetKeyboardLanguage(KBLANG_AMERICAN); + } else if(cfopen("french.lan", "rt")) { + Localization_SetLanguage(LANGUAGE_FRENCH); + ddio_SetKeyboardLanguage(KBLANG_FRENCH); + } else { + Localization_SetLanguage(LANGUAGE_ENGLISH); + ddio_SetKeyboardLanguage(KBLANG_AMERICAN); + } +#else + int language = LANGUAGE_ENGLISH; + Database->read("LanguageType",&language,sizeof(language)); + + if(language<0 || language>=Num_languages) + { + Int3(); + language = LANGUAGE_ENGLISH; + } + Localization_SetLanguage(language); + +#endif + int string_count = LoadStringTables(); + + if(string_count==0) + Error("Couldn't find the string table."); + else + mprintf((0,"%d strings loaded from the string tables\n",string_count)); +} + + +void InitGraphics(bool editor) +{ +// Init our bitmaps, must be called before InitTextures + bm_InitBitmaps(); + +// Init our textures + if (!InitTextures()) + Error("Failed to initialize texture system."); + +#ifdef EDITOR + char *driver = "GDIX"; + + if (!ddgr_Init(Descent, driver, editor ? false: true)) + Error("DDGR graphic system init failed."); + +// Init our renderer + grSurface::init_system(); + rend_Init (RENDERER_SOFTWARE_16BIT, Descent,NULL); + Desktop_surf = new grSurface(0,0,0, SURFTYPE_VIDEOSCREEN, 0); +#else + strcpy(App_ddvid_subsystem, "GDIX"); + + if (!Dedicated_server) + { + if (!ddvid_Init( Descent, App_ddvid_subsystem)) + Error("Graphics initialization failed.\n"); + } + + INIT_MESSAGE("Loading fonts."); + LoadAllFonts(); +#endif + Graphics_init = true; +} + + +void InitGameSystems(bool editor) +{ +// initialize possible remote controller. + int adr = FindArg("-rjoy"); + if (adr) + Controller_ip = &GameArgs[adr+1][1]; + +// do other joint editor/game initialization. + SetInitMessageLength(TXT_INITCOLLATING, 0.4f); + TelComInit(); + InitFrameTime(); + + //Check for aspect ratio override + int t = FindArg("-aspect"); + if (t) { + extern void g3_SetAspectRatio(float); + float aspect = atof(GameArgs[t+1]); + if (aspect > 0.0) + g3_SetAspectRatio(aspect); + } + + //Initialize force feedback effects (if we can) + ForceInit(); + + if (!editor) { + tUIInitInfo uiinit; + uiinit.window_font = SMALL_FONT; + uiinit.w = 640; + uiinit.h = 480; + ui_Init(Descent, &uiinit); + ui_UseCursor("StdCursor.ogf"); + + NewUIInit(); + InitControls(); + + atexit(CloseControls); + atexit(ui_Close); + } +} + + +////////////////////////////////////////////////////////////////////////////// +static float Init_messagebar_portion = 0.0f, Init_messagebar_offset=0.0f; +static char *Init_messagebar_text = NULL; + +// portion of total bar to fill (0 to 1) +void SetInitMessageLength(char *c, float amount) +{ + Init_messagebar_text = c; + Init_messagebar_offset += Init_messagebar_portion; + Init_messagebar_portion = amount; +} + + +// amount is 0 to 1 +void UpdateInitMessage(float amount) +{ + if (Init_in_editor) return; + InitMessage(Init_messagebar_text, (amount*Init_messagebar_portion)+Init_messagebar_offset); +// mprintf((0, "amt=%.2f, portion=%.2f offs=%.2f, prog=%.2f\n", amount, Init_messagebar_portion, Init_messagebar_offset, (amount*Init_messagebar_portion)+Init_messagebar_offset)); +} + + +void InitMessage(char *c,float progress) +{ + int x = Game_window_w/2 - Title_bitmap.pw/2; + int y = Game_window_h/2 - Title_bitmap.ph/2; + int i, rx, ry, rw ,rh; + + if (Dedicated_server) + { + PrintDedicatedMessage("%s\n",c); + return; + } + + if (!Graphics_init) + return; + + StartFrame(); + + if (Title_bitmap_init) { + rend_ClearScreen(GR_BLACK); + rend_DrawChunkedBitmap(&Title_bitmap, x,y,255); + } + + if (c) { + g3Point *pntlist[4],points[4]; + // Set our four corners to cover the screen + grtext_SetFont(MONITOR9_NEWUI_FONT); + rw = Game_window_w; + rh = grfont_GetHeight(MONITOR9_NEWUI_FONT) + 8; + rx = 0; + ry = Game_window_h - rh; + points[0].p3_sx=rx; + points[0].p3_sy=ry; + points[1].p3_sx=rx+rw; + points[1].p3_sy=ry; + points[2].p3_sx=rx+rw; + points[2].p3_sy=ry+rh; + points[3].p3_sx=rx; + points[3].p3_sy=ry+rh; + + for (i=0;i<4;i++) + { + points[i].p3_z=0; + points[i].p3_flags=PF_PROJECTED; + pntlist[i]=&points[i]; + } + + rend_SetZBufferState(0); + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetLighting (LS_NONE); + rend_SetFlatColor (GR_BLACK); + rend_SetAlphaValue(230); + rend_DrawPolygon2D( 0, pntlist, 4 ); + + if (progress>=0) + { + points[0].p3_sx=rx; + points[0].p3_sy=ry+rh-(rh/2)+2; + points[1].p3_sx=rx+((float)rw*progress); + points[1].p3_sy=ry+rh-(rh/2)+2; + points[2].p3_sx=rx+((float)rw*progress); + points[2].p3_sy=ry+rh; + points[3].p3_sx=rx; + points[3].p3_sy=ry+rh; + + rend_SetFlatColor (GR_RGB(255,0,0)); + rend_SetAlphaValue(230); + rend_DrawPolygon2D( 0, pntlist, 4 ); + } + + grtext_SetAlpha(255); + grtext_SetColor(NEWUI_MONITORFONT_COLOR); +// grtext_CenteredPrintf(0, ry, c); + grtext_Puts(0, ry, c); + grtext_Flush(); + } + + EndFrame(); + rend_Flip(); +} + + +////////////////////////////////////////////////////////////////////////////// +#if (defined(OEM) || defined(DEMO) || defined(RELEASE)) +void ShowStaticScreen(char *bitmap_filename,bool timed=false,float delay_time=0.0f); +#endif + +void IntroScreen() +{ +//#if (defined(OEM) || defined(DEMO) ) +#ifdef DEMO + #ifdef MACINTOSH + ShowStaticScreen("graphsim.ogf",true,3.0); + #else + ShowStaticScreen("tantrum.ogf",true,3.0); + #endif + ShowStaticScreen("outrage.ogf",true,3.0); +#else + #ifdef MACINTOSH + if(cfopen("publisher.ogf", "rb")) + ShowStaticScreen("publisher.ogf",true,3.0); + + if(cfopen("graphsim.ogf", "rb")) + ShowStaticScreen("graphsim.ogf",true,3.0); + #endif +#endif + +#ifdef DEMO + int bm_handle = bm_AllocLoadFileBitmap ("demomenu.ogf",0); +#else + int bm_handle = bm_AllocLoadFileBitmap ("oemmenu.ogf",0); +#endif + mprintf((0, "Intro screen!.\n")); + + if (bm_handle > -1) { + if (!bm_CreateChunkedBitmap(bm_handle, &Title_bitmap)) + Error("Failed to slice up d3.ogf!"); + + bm_FreeBitmap(bm_handle); + + Title_bitmap_init = true; + InitMessage(NULL); + } + else { + mprintf((1, "Unable to find d3.tga.\n")); + } +} + +void InitDedicatedServer() +{ + Game_mode|=GM_MULTI; + Netgame.local_role=LR_SERVER; + + int ok=LoadServerConfigFile (); + + if (!ok) { + PrintDedicatedMessage (TXT_SHUTTINGDOWN); + Error("Cannot load Dedicated Server config file."); +#ifdef WIN32 + exit(0); +#endif + } +} + +/* + The D3 Initialization Mess + + This initialization sequence will occur after the application and debug systems + are set up. + + From here, we initialize all systems. + + Initializes all the subsystems that D3/Editor needs to run. + Returns 1 if all is good, 0 if something is wrong +*/ +int ServerTimeout = 0; +float LastPacketReceived; + +ushort Gameport = D3_DEFAULT_PORT; +ushort PXOPort = 0; +//Initialiaze everything before data load +void InitD3Systems1(bool editor) +{ +#if defined (RELEASE) || defined (MACINTOSH) + SetDebugBreakHandlers(NULL, NULL); +#else + SetDebugBreakHandlers(D3DebugStopHandler, D3DebugResumeHandler); +#endif + + Init_in_editor = editor; + + //Gamespy command to specify the port to listen on + int gameportarg = FindArg("+host"); + if(!gameportarg) + { + gameportarg = FindArg("-useport"); + } + if(gameportarg) + { + Gameport = atoi(GameArgs[gameportarg+1]); + } + + int pxoportarg = FindArg("-pxoport"); + if(pxoportarg) + PXOPort = atoi(GameArgs[pxoportarg+1]); + +// perform user i/o system initialization + INIT_MESSAGE(("Initializing I/O system.")); + InitIOSystems(editor); + +// load the string table + InitStringTable(); + + if(!IsLocalOk()) + { +#ifdef WIN32 + MessageBox(NULL,"Sorry, your computer has a language not supported by this version of Descent 3.",PRODUCT_NAME,MB_OK); +#else + printf("Sorry, your computer has a language not supported by this version of Descent 3."); +#endif + exit(0); + } + +// With 1.5 no more copy protection! +#if 0 + //CD Check goes here +//#if ( defined(RELEASE) && (!defined(DEMO)) && (!defined(GAMEGAUGE)) ) + if( (!FindArg("-dedicated")) ) + PreGameCdCheck(); +#endif + +#ifdef LASERLOCK + //At this point the laser locked CD MUST be inserted or the + //game and possibly the entire OS will crash because laserlock + //will assume it's a hacked CD. + if(!ll_Init()) + exit(0); + if(!ll_Verify()) + exit(0); +#else +bool CheckCdForValidity(int cd); + if( (!FindArg("-dedicated"))) + { + if(!CheckCdForValidity(CD_inserted)) + { +#ifdef WIN32 + ShowCursor(true); + MessageBox(NULL,"Invalid CDROM!",PRODUCT_NAME,MB_OK); + ShowCursor(false); +#endif + exit(0); + } + } +#endif + INIT_MESSAGE(("Initializing GFX")); + InitGraphics(editor); + +// initialize data structures + InitObjectInfo(); + InitVClips(); + InitRooms(); + +// initialize lighting systems + InitLightmapInfo(); + InitSpecialFaces(); + InitDynamicLighting(); + +// Initialize missions + InitMission(); + +// Initializes the ship structure + InitShips(); + +// Initializes the fvi structures for quicker operation + InitFVI(); + +// Initializes the matcens + InitMatcens(); + +// This function needs be called before ANY 3d stuff can get done. I mean it. + InitMathTables(); + +// This function has to be done before any sound stuff is called + InitSounds(); + +// Allocate memory and stuff for our terrain engine, objects, etc. + InitTerrain(); + + InitModels(); + InitDoors(); + InitGamefiles(); + +// network initialization + if(!FindArg("-nonetwork")) + { + nw_InitNetworking(); + nw_InitSockets(Gameport); + + int tcplogarg; + tcplogarg = FindArg("-tcplog"); + if(tcplogarg) + { + char ipparse[50]; + char *pport; + int port = 9999; + strcpy(ipparse,GameArgs[tcplogarg+1]); + pport = strchr(ipparse,':'); + if(pport) + { + *pport = NULL; + pport++; + port = atoi(pport); + } +#if !defined(RELEASE) && !defined(MACINTOSH) + nw_InitTCPLogging(ipparse,port); +#endif + } + } + + int timeoutarg = FindArg("-timeout"); + if(timeoutarg) + { + ServerTimeout = atoi(GameArgs[timeoutarg+1]); + LastPacketReceived = timer_GetTime(); + } + +//Init gamespy +// gspy_Init(); + +// Sound initialization + int soundres = Sound_system.InitSoundLib(Descent, Sound_mixer, Sound_quality, false); +#ifdef OEM_AUREAL + if(!soundres) + { + Error("Unable to initialize Aureal audio. This version of Descent 3 requires Aureal audio"); + } +#endif + + // Initialize Cinematics system + InitCinematics(); + + // Initialize IntelliVIBE (if available) + VIBE_Init(Descent); +} + +//Initialize rest of stuff +void InitD3Systems2(bool editor) +{ + // Initialize In-Game Cinematics system + Cinematic_Init(); + +// initialize slewing +#ifdef EDITOR + SlewControlInit(); +#endif + + int tables_loaded=0; + +// load in all data headers. + INIT_MESSAGE((TXT_INITDATA)); + tables_loaded=mng_LoadTableFiles(! editor); + + if (!tables_loaded) + Error("Cannot load table file."); + + INIT_MESSAGE((TXT_INITCOLLATING)); + +//Initialize the pilot system + PilotInit(); + +// Setup our object system. By object I mean stuff in our world, not those silly C++ things. +// This call must come after InitTableFiles(), so we have a player ship for the user + InitObjects(); + +// Fireball initting must come after LoadTableFiles + SetInitMessageLength(TXT_INITCOLLATING, 0.1f); + InitFireballs(); + UpdateInitMessage(1.0f); + +// initializes triggers + InitTriggers(); + +// the remaining sound system + InitVoices(); + InitD3Music(FindArg("-nomusic") ? false : true); + InitAmbientSoundSystem(); + + InitGameSystems(editor); + + InitPlayers(); + + SetInitMessageLength(TXT_INITCOLLATING, 0.5f); + InitMarkers(); + UpdateInitMessage(1.0f); + + PPic_InitDatabase(); + +// Set next logical function for game. + if (editor) + SetFunctionMode(EDITOR_MODE); + else + SetFunctionMode(MENU_MODE); + + if (Dedicated_server) + InitDedicatedServer(); + + Init_systems_init = true; + +// free title screen bitmap. + if (Title_bitmap_init) { + bm_DestroyChunkedBitmap(&Title_bitmap); + Title_bitmap_init = false; + } + +// initialize localized text for controller system. + extern void Localize_ctl_bindings(); + Localize_ctl_bindings(); + +#ifdef OEM + if(cfexist("bundler.mve")) + { + PlayMovie("bundler.mve"); + } + + if(cfexist("bundler.tga")) + { + ShowStaticScreen("bundler.tga"); + } +#endif +} + +void SetupTempDirectory(void) +{ + //NOTE: No string tables are available at this point + //-------------------------------------------------- + + mprintf((0,"Setting up temp directory\n")); + + int t_arg = FindArg("-tempdir"); + if(t_arg) + { + strcpy(Descent3_temp_directory,GameArgs[t_arg+1]); + }else + { + //initialize it to custom/cache + ddio_MakePath(Descent3_temp_directory,Base_directory,"custom","cache",NULL); + } + + //verify that temp directory exists + if(!ddio_SetWorkingDir(Descent3_temp_directory)) + { + Error("Unable to set temporary directory to: \"%s\"",Descent3_temp_directory); + exit(1); + } + + char tempfilename[_MAX_PATH]; + + //verify that we can write to the temp directory + if(!ddio_GetTempFileName(Descent3_temp_directory,"d3t",tempfilename)) + { + mprintf((0,"Unable to get temp file name\n")); + Error("Unable to set temporary directory to: \"%s\"",Descent3_temp_directory); + exit(1); + } + + //try to create a file in temp directory + CFILE *file = cfopen(tempfilename,"wb"); + if(!file) + { + //unable to open file for writing + mprintf((0,"Unable to open temp file name for writing\n")); + Error("Unable to set temporary directory to: \"%s\"",Descent3_temp_directory); + exit(1); + } + + cf_WriteInt(file,0x56); + cfclose(file); + + //now open up the file and make sure it is correct + file = cfopen(tempfilename,"rb"); + if(!file) + { + //unable to open file for reading + mprintf((0,"Unable to open temp file name for reading\n")); + ddio_DeleteFile(tempfilename); + Error("Unable to set temporary directory to: \"%s\"",Descent3_temp_directory); + exit(1); + } + + if(cf_ReadInt(file)!=0x56) + { + //verify failed + mprintf((0,"Temp file verify failed\n")); + cfclose(file); + ddio_DeleteFile(tempfilename); + Error("Unable to set temporary directory to: \"%s\"",Descent3_temp_directory); + exit(1); + } + + cfclose(file); + + //temp directory is valid! + ddio_DeleteFile(tempfilename); + + mprintf((0,"Temp Directory Set To: \"%s\"\n",Descent3_temp_directory)); + +#ifndef MACINTOSH + // Lock the directory + int lock_res = ddio_CreateLockFile(Descent3_temp_directory); + switch(lock_res) + { + case 1: + mprintf((0,"Lock file created in temp dir\n")); + break; + case 2: + mprintf((0,"Lock file created in temp dir (deleted dead lock)\n")); + break; + case 3: + mprintf((0,"Lock file created in temp dir (lock already exists)\n")); + break; + case 0: + mprintf((0,"Lock file NOT created in temp dir\n")); + Error("Unable to set temporary directory to: \"%s\"\nThe directory is in use, please use -tempdir to set a different temp directory",Descent3_temp_directory); + exit(1); + break; + case -1: + mprintf((0,"Illegal directory for Lock file\n")); + Error("Unable to set temporary directory to: \"%s\"\nIllegal directory for lock file",Descent3_temp_directory); + exit(1); + break; + case -2: + mprintf((0,"Illegal Lock file, unable to create\n")); + Error("Unable to set temporary directory to: \"%s\"\nInvalid lock file located in directory",Descent3_temp_directory); + exit(1); + break; + case -3: + mprintf((0,"Error creating Lock file\n")); + Error("Unable to set temporary directory to: \"%s\"\nUnable to create lock file",Descent3_temp_directory); + exit(1); + break; + } +#endif + //restore working dir + ddio_SetWorkingDir(Base_directory); +} + +void DeleteTempFiles(void) +{ + char filename[_MAX_PATH]; + + //delete the d3 temp files in the temp directory + if(ddio_SetWorkingDir(Descent3_temp_directory)) + { + int i; + for(i=0;i= 0) + mfiles += nfiles; + else + { + mfiles -= nfiles; + return(-mfiles); // error return + } + } + } + }while (!_findnext(isearchhandle2,&fileinfo)); + } + } + + return mfiles; +} + + + +#endif diff --git a/Descent3/init.h b/Descent3/init.h new file mode 100644 index 000000000..2086376c1 --- /dev/null +++ b/Descent3/init.h @@ -0,0 +1,86 @@ +/* + * $Logfile: /DescentIII/main/init.h $ + * $Revision: 8 $ + * $Date: 5/12/99 1:57p $ + * $Author: Jason $ + * + * Initialization code + * + * $Log: /DescentIII/main/init.h $ + * + * 8 5/12/99 1:57p Jason + * fixed yet more buggy/ugly code + * + * 7 4/02/99 5:08p Matt + * Added intro movie. + * + * 6 10/14/98 4:37p Matt + * Made InitD3System() exit with error if there's a problem instead of + * returning a status value. Also moved some editor-specific code from + * init.cpp to mainfrm.cpp, and cleaned up some other initialization and + * error-handling code. + * + * 5 9/25/98 2:53p Jason + * added progress bar + * + * 4 7/01/98 4:56p Samir + * redid init code. + * + * 3 3/23/98 8:03p Samir + * A bunch of changes to allow for ALT-TAB to work. + * + * 2 7/24/97 3:04p Matt + * Added functions to load/save game variables from/to the registry + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 8 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 7 2/04/97 2:32p Samir + * Added separate editor initialization + * + * $NoKeywords: $ + */ + +#ifndef INIT_H +#define INIT_H + +// Universal globals for game +// --------------------------------------------------------------------------- + +// name of ddvideo subsystem to use for game. +extern char App_ddvid_subsystem[]; + +extern int ServerTimeout; +extern float LastPacketReceived; + + +// --------------------------------------------------------------------------- + +// Initializes all the subsystems that need to be initialized BEFORE application creation. +void PreInitD3Systems(); + +// Initializes all the subsystems that D3/Editor needs to run. +// Returns 1 if all is good, 0 if something is wrong +void InitD3Systems1(bool editor=false); +void InitD3Systems2(bool editor=false); + +//Show the intro screen, or something +void IntroScreen(); + +//Save game variables to the registry +void SaveGameSettings(); + +// This function shutdowns all game systems +void ShutdownD3(); + +// This function restarts all game systems +void RestartD3(); + +void InitMessage(char *c,float progress=-1); + +#endif + + diff --git a/Descent3/intellivibe.cpp b/Descent3/intellivibe.cpp new file mode 100644 index 000000000..4fe582745 --- /dev/null +++ b/Descent3/intellivibe.cpp @@ -0,0 +1,211 @@ +/* +* $Logfile: /DescentIII/Main/intellivibe.cpp $ +* $Revision: 5 $ +* $Date: 4/28/00 6:49p $ +* $Author: Jeff $ +* +* IntelliVIBE interface +* +* $Log: /DescentIII/Main/intellivibe.cpp $ + * + * 5 4/28/00 6:49p Jeff + * created stub functions for non-win32 + * + * 4 3/30/00 6:00p Jeff + * changed name of ivibe dll + * + * 3 1/27/00 12:04p Jeff + * updated IntelliVIBE to match new specs + * + * 2 1/26/00 9:20p Jeff + * added support for IntelliVIBE DLL +* +* $NoKeywords: $ +*/ + +#define DD_ACCESS_RING +#include "vibeinterface.h" + +#ifndef WIN32 +void VIBE_Init(oeApplication *app){} +void VIBE_Close(void){} +void VIBE_DoFrame(void){} +void VIBE_WeaponFire(int weapon_index){} +void VIBE_DoControls(game_controls *controls){} +void VIBE_PlayerRespawn(void){} +void VIBE_PlayerDeath(void){} +void VIBE_DoForce(vector *force_vec,int weapon_index){} +void VIBE_DoPlayerDamage(float damage_amount){} +void VIBE_DoQuaterFrame(bool){} +void VIBE_DoForce(vector *){} +void VIBE_DoLevelEnd(void){} +#else + +#include "intellivibe.h" +#include "module.h" +#include "mono.h" +#include "object_external_struct.h" + +static module IntelliVIBE_module = { NULL }; +static d3_intellivibe IntelliVIBE_state;// our current state +static IntelliVIBE_Initialize_fp IVIBE_Initialize = NULL; +static IntelliVIBE_Shutdown_fp IVIBE_Shutdown = NULL; +static IntelliVIBE_DoQuaterFrame_fp IVIBE_DoQuaterFrame = NULL; + +#define TEST_MODULE(x) { if(!(x)){ mprintf((0,"IntelliVIBE unable to find: %s\n",#x)); mod_FreeModule(&IntelliVIBE_module); IntelliVIBE_module.handle = NULL; return;} } +void VIBE_Init(oeApplication *app) +{ + if(!mod_LoadModule(&IntelliVIBE_module,"ivibe_D3.dll")) + { + mprintf((0,"Unable to load IntelliVIBE DLL\n")); + IntelliVIBE_module.handle=NULL; + return; + } + + // resolve our functions + IVIBE_Initialize = (IntelliVIBE_Initialize_fp)mod_GetSymbol(&IntelliVIBE_module,"IntelliVIBE_Initialize",4); + IVIBE_Shutdown = (IntelliVIBE_Shutdown_fp)mod_GetSymbol(&IntelliVIBE_module,"IntelliVIBE_Shutdown",0); + IVIBE_DoQuaterFrame = (IntelliVIBE_DoQuaterFrame_fp)mod_GetSymbol(&IntelliVIBE_module,"IntelliVIBE_DoQuaterFrame",4); + + // make sure everything went ok + TEST_MODULE(IVIBE_Initialize); + TEST_MODULE(IVIBE_Shutdown); + TEST_MODULE(IVIBE_DoQuaterFrame); + + d3_init_info info; + info.hinst = (HINSTANCE)((oeWin32Application *)app)->m_hInstance; + info.hwnd = (HWND)((oeWin32Application *)app)->m_hWnd; + + ASSERT(IVIBE_Initialize!=NULL); + if(!IVIBE_Initialize(&info)) + { + mprintf((0,"IntelliVIBE_Initialize failed\n")); + mod_FreeModule(&IntelliVIBE_module); + IntelliVIBE_module.handle = NULL; + return; + } + + // Initialize our current state + memset(&IntelliVIBE_state,0,sizeof(IntelliVIBE_state)); + + atexit(VIBE_Close); +} + +void VIBE_Close(void) +{ + if(IntelliVIBE_module.handle==NULL) + return; + ASSERT(IVIBE_Shutdown!=NULL); + + IVIBE_Shutdown(); + mod_FreeModule(&IntelliVIBE_module); + IntelliVIBE_module.handle = NULL; +} + +extern bool Game_paused; +extern float Frametime; +extern float Gametime; +extern float Shake_magnitude; +extern object *Player_object; + +// Informs us of a quater-frame tick +void VIBE_DoQuaterFrame(bool first_frame) +{ + if(IntelliVIBE_module.handle==NULL) + return; + ASSERT(IVIBE_DoQuaterFrame!=NULL); + + static char quater_frame_count = 0; + + if(first_frame) + { + // reset the flags + IntelliVIBE_state.flags = 0; + IntelliVIBE_state.damage_info.damage_amount = 0; + IntelliVIBE_state.force_info.force_vector.x = IntelliVIBE_state.force_info.force_vector.y = IntelliVIBE_state.force_info.force_vector.z = 0; + quater_frame_count = 0; + }else + { + quater_frame_count++; + } + + // set our frametime and stuff + IntelliVIBE_state.frame_info.frametime = Frametime; + IntelliVIBE_state.frame_info.gametime = Gametime; + IntelliVIBE_state.frame_info.game_paused = (Game_paused)?1:0; + + // reset the quaterframe flags + IntelliVIBE_state.flags &= ~(VIBEFLAG_QUATERFRAME_0|VIBEFLAG_QUATERFRAME_1|VIBEFLAG_QUATERFRAME_2|VIBEFLAG_QUATERFRAME_3); + + // set the flag for which quaterframe we are on + switch(quater_frame_count) + { + case 0: IntelliVIBE_state.flags |= VIBEFLAG_QUATERFRAME_0; break; + case 1: IntelliVIBE_state.flags |= VIBEFLAG_QUATERFRAME_1; break; + case 2: IntelliVIBE_state.flags |= VIBEFLAG_QUATERFRAME_2; break; + case 3: IntelliVIBE_state.flags |= VIBEFLAG_QUATERFRAME_3; break; + } + + // send a quaterframe tick to IntelliVIBE + IVIBE_DoQuaterFrame(&IntelliVIBE_state); +} + +void VIBE_WeaponFire(int weapon_index) +{ + IntelliVIBE_state.fire_info.weapon_index = weapon_index; + IntelliVIBE_state.flags |= VIBEFLAG_WEAPON_FIRED; +} + +void VIBE_DoControls(game_controls *controls) +{ + IntelliVIBE_state.controls_info.pitch_thrust = controls->pitch_thrust; + IntelliVIBE_state.controls_info.heading_thrust = controls->heading_thrust; + IntelliVIBE_state.controls_info.bank_thrust = controls->bank_thrust; + IntelliVIBE_state.controls_info.vertical_thrust = controls->vertical_thrust; + IntelliVIBE_state.controls_info.sideways_thrust = controls->sideways_thrust; + IntelliVIBE_state.controls_info.forward_thrust = controls->forward_thrust; + IntelliVIBE_state.controls_info.afterburn_thrust= controls->afterburn_thrust; + IntelliVIBE_state.controls_info.shake_magnitude = Shake_magnitude; + IntelliVIBE_state.controls_info.current_velocity.x = Player_object->mtype.phys_info.velocity.x; + IntelliVIBE_state.controls_info.current_velocity.y = Player_object->mtype.phys_info.velocity.y; + IntelliVIBE_state.controls_info.current_velocity.z = Player_object->mtype.phys_info.velocity.z; +} + +void VIBE_PlayerRespawn(void) +{ + IntelliVIBE_state.flags |= VIBEFLAG_PLAYER_RESPAWN; +} + +void VIBE_PlayerDeath(void) +{ + IntelliVIBE_state.flags |= VIBEFLAG_PLAYER_DEAD; +} + +void VIBE_DoForce(vector *force_vec) +{ + vector curr_force; + memcpy(&curr_force,&IntelliVIBE_state.force_info.force_vector,sizeof(vector)); + + // add them up...we can have more than one a frame + curr_force += *force_vec; + memcpy(&IntelliVIBE_state.force_info.force_vector,&curr_force,sizeof(vector)); + + IntelliVIBE_state.flags |= VIBEFLAG_FORCE_APPLIED; +} + +void VIBE_DoPlayerDamage(float damage_amount) +{ + IntelliVIBE_state.damage_info.damage_amount += damage_amount; + IntelliVIBE_state.flags |= VIBEFLAG_PLAYER_DAMAGED; +} + +void VIBE_DoLevelEnd(void) +{ + IntelliVIBE_state.flags |= VIBEFLAG_LEVEL_END; + + // force a frame + VIBE_DoQuaterFrame(false); +} + +#endif + diff --git a/Descent3/levelgoal.cpp b/Descent3/levelgoal.cpp new file mode 100644 index 000000000..cbc833dd0 --- /dev/null +++ b/Descent3/levelgoal.cpp @@ -0,0 +1,1472 @@ +/* + * $Logfile: /DescentIII/Main/levelgoal.cpp $ + * $Revision: 50 $ + * $Date: 6/23/99 5:34p $ + * $Author: Jeff $ + * + * Level goal stuff, I guess. + * + * $Log: /DescentIII/Main/levelgoal.cpp $ + * + * 50 6/23/99 5:34p Jeff + * For some reason Linux started complaining about Status function, + * renaming it to GetStatus made it quiet. + * + * 49 5/23/99 8:39p Chris + * Now multiple equal priority (primary and secondary) goals are shown on + * the level goal tab + * + * 48 5/18/99 7:33p Jeff + * fixed game message log...using out-dated variable that is no longer + * used...switched to real buffer. We should remove the out-dated + * variable, but I don't want to break any code at this point. + * + * 47 5/12/99 6:02p Jeff + * flip order of saving/loading objects and players + * + * 46 5/09/99 10:49p Jason + * fixed some strings being truncuated in multiplayer games + * + * 45 5/08/99 4:55p Chris + * Only add HUD completion message if the lgoal is enabled + * + * 44 5/07/99 7:57p Jeff + * handle the multisafe condition of clearing the completed flag + * + * 43 5/02/99 5:09a Jeff + * send levelgoal state to joining clients + * + * 42 5/01/99 1:41a Jeff + * made levelgoals multi friendly + * + * 41 4/14/99 11:49a Matt + * Made localizable the goal completed HUD message + * + * 40 4/05/99 1:07p Chris + * Added a goal failed state + * + * 39 3/16/99 4:17p Matt + * Changed goal complete message from ": Completed!" to "Completed: + * " + * + * 38 3/10/99 2:25p Kevin + * Save/Load and Demo file fixes + * + * 37 3/04/99 7:39p Matt + * Added sound effects to FreeSpace-style persistent HUD messages. + * + * 36 3/04/99 6:12p Matt + * Changed color of goal complete message + * + * 35 3/03/99 5:34p Matt + * Added fade-out for goal complete messages + * + * 34 3/03/99 3:35a Chris + * Fixed. + * + * 33 3/02/99 6:39p Jeff + * level goals working in save/load game + * + * 32 3/02/99 5:44p Chris + * Fixed the save game problem with levelgoals + * + * 31 2/26/99 8:33a Chris + * : Completed! was added + * + * 30 2/23/99 1:03p Luke + * Fixed a bug where all goals where completing automatically + * + * 29 2/22/99 8:11p Chris + * Fixed a bug with saving goals + * + * 28 2/22/99 3:05p Chris + * Complete - minus coop + * + * 27 2/22/99 1:43a Chris + * Version 4 is near complete + * + * 26 2/21/99 7:49p Chris + * + * 25 2/21/99 7:47p Chris + * Finished up with the anywhere on terrain and anywhere on mine items :) + * + * 24 2/21/99 7:16p Chris + * Added the EVT_LEVEL_GOAL_ITEM_COMPLETE event + * + * 23 2/21/99 6:37p Chris + * + * 22 2/21/99 5:44p Nate + * Fixed a bug with GoalComplete being called directly from the + * Level_goals class. Oops. + * + * 21 2/21/99 4:47p Chris + * More updates + * + * 20 2/21/99 4:31p Chris + * Improving the level goal system... Not done. + * + * 19 2/20/99 6:19p Chris + * + * 18 2/20/99 6:18p Chris + * Improved levelgoals - not done + * + * 17 2/20/99 4:29p Chris + * SOme updates to the system, not done + * + * 16 2/20/99 2:59p Matt + * Made ShowGoalMessage() add the message to the game message buffer. + * + * 15 2/19/99 11:32a Chris + * Added GoalFindId + * + * 14 2/18/99 5:29p Matt + * Added ShowGoalMessage() + * + * 13 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 12 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 11 1/29/99 5:22p Jeff + * localization + * + * 10 10/15/98 8:54a Matt + * Don't call Endlevel() directly, but instead change the game state to + * end-of-level + * + */ + +#include "levelgoal.h" +#include "mem.h" +#include "string.h" +#include "hud.h" +#include "game.h" +#include "gamesequence.h" +#include "stringtable.h" +#include +#include "pstring.h" +#include "hlsoundlib.h" +#include "sounds.h" +#include "osiris_dll.h" +#include "room.h" +#include "object.h" +#include "trigger.h" +#include "player.h" +#include "multi.h" +#include "demofile.h" +#include "osiris_share.h" +#include "multisafe.h" +#include "multi.h" +#include "multi_world_state.h" + +#define GOAL_MESSAGE_TIME 10.0 +#define GOAL_MESSAGE_COLOR GR_RGB(0,242,148) + +levelgoals Level_goals; + +// This is the load/save version of the level goals +#define LEVEL_GOAL_VERSION 4 + +void lgoal::GoalComplete(int handle,bool announce) +{ + m_goal_completed = true; + + tOSIRISEventInfo ei; + ei.evt_level_goal_complete.level_goal_index = handle; + + Osiris_CallLevelEvent(EVT_LEVEL_GOAL_COMPLETE, &ei); + + if(m_name && announce && (this->m_flags & LGF_ENABLED)) + { + //Draw on the HUD + int y = Game_window_h / 4; +// AddPersistentHUDMessage(GOAL_MESSAGE_COLOR,HUD_MSG_PERSISTENT_CENTER,y,GOAL_MESSAGE_TIME,m_completion_message); + char message[200]; + sprintf(message,"%s %s",TXT_COMPLETED_HUD,m_name); + if(Demo_flags==DF_RECORDING) + { + DemoWritePersistantHUDMessage(GOAL_MESSAGE_COLOR,HUD_MSG_PERSISTENT_CENTER,GOAL_MESSAGE_TIME,GOAL_MESSAGE_TIME,HPF_FADEOUT+HPF_FREESPACE_DRAW,SOUND_GOAL_COMPLETE,message); + } + if(Demo_flags!=DF_PLAYBACK) + { + AddPersistentHUDMessage(GOAL_MESSAGE_COLOR,HUD_MSG_PERSISTENT_CENTER,y,GOAL_MESSAGE_TIME,HPF_FADEOUT+HPF_FREESPACE_DRAW,SOUND_GOAL_COMPLETE,message); + } + + //Add to the game message buffer + AddGameMessage(message); + } +} + +int lgoal::AddItem() +{ + if(m_num_items < MAX_GOAL_ITEMS) + { + if(m_num_items < 1) + { + m_item[m_num_items].m_type = LIT_OBJECT; + m_item[m_num_items].m_handle = OBJECT_HANDLE_NONE; + m_item[m_num_items].m_f_done = false; + } + else + { + m_item[m_num_items].m_type = m_item[0].m_type; + m_item[m_num_items].m_handle = -1; + m_item[m_num_items].m_f_done = false; + } + + m_num_items++; + return m_num_items - 1; + } + else + { + return -1; + } +} + +bool lgoal::DeleteItem(int index) +{ + int i; + + if(index < 0 || index >= m_num_items) + return false; + + for(i = index; i < m_num_items - 1; i++) + { + m_item[i] = m_item[i + 1]; + } + + m_num_items--; + + return true; +} + +bool lgoal::ItemInfo(int index, char operation, char *type, int *handle, bool *done) +{ + if(index < 0 || index >= m_num_items) + return false; + + switch(operation) + { + case LO_SET_SPECIFIED: + if(type) + { + m_item[index].m_type = *type; + } + if(handle) + { + m_item[index].m_handle = *handle; + } + if(done) + { + m_item[index].m_f_done = *done; + } + return true; + break; + + case LO_GET_SPECIFIED: + if(type) + { + *type = m_item[index].m_type; + } + if(handle) + { + *handle = m_item[index].m_handle; + } + if(done) + { + *done = m_item[index].m_f_done; + } + return true; + break; + } + + return false; +} + +bool lgoal::SetName(int handle,char *name) +{ + if(!name) + return false; + + if(m_name) + mem_free(m_name); + + m_name = (char *) mem_malloc(strlen(name) + 1); + strcpy(m_name, name); + m_modified = 1; + + if (Game_mode & GM_MULTI && Netgame.local_role!=LR_CLIENT) + { + //send it off to the clients + SendMultiUpdate(handle); + } + + + return true; +} + +void lgoal::SendMultiUpdate(int handle) +{ + ASSERT(Game_mode&GM_MULTI && Netgame.local_role==LR_SERVER); + + msafe_struct mstruct; + + mstruct.index = handle; + GetName(mstruct.message,MSAFE_MESSAGE_LENGTH); + mstruct.type = m_flags; + mstruct.count = m_priority; + + msafe_CallFunction (MSAFE_MISC_LEVELGOAL,&mstruct); +} + +bool lgoal::SetCompletionMessage(char *message) +{ + if(!message) + return false; + + if(m_completion_message) + mem_free(m_completion_message); + + m_completion_message = (char *) mem_malloc(strlen(message) + 1); + strcpy(m_completion_message, message); + + return true; +} + +bool lgoal::SetItemName(char *iname) +{ + if(!iname) + return false; + + if(m_item_name) + mem_free(m_item_name); + + m_item_name = (char *) mem_malloc(strlen(iname) + 1); + strcpy(m_item_name, iname); + + return true; +} + +bool lgoal::Priority(int handle,char operation, int *value) +{ + if(!value) + return false; + + switch(operation) + { + case LO_SET_SPECIFIED: + m_priority = *value; + m_modified = 1; + if (Game_mode & GM_MULTI && Netgame.local_role!=LR_CLIENT) + { + //send it off to the clients + SendMultiUpdate(handle); + } + return true; + break; + case LO_GET_SPECIFIED: + *value = m_priority; + return true; + break; + } + + return false; +} + +bool lgoal::GoalList(char operation, char *value) +{ + if(!value) + return false; + + switch(operation) + { + case LO_SET_SPECIFIED: + if(*value < 0) + *value = 0; + else if(*value >= MAX_GOAL_LISTS) + *value = MAX_GOAL_LISTS - 1; + + m_g_list = *value; + return true; + break; + case LO_GET_SPECIFIED: + *value = m_g_list; + return true; + break; + } + + return false; +} + +bool lgoal::GetStatus(int handle, char operation, int *value, bool f_save_load,bool announce) +{ + if(!value) + return false; + + switch(operation) + { + case LO_SET_SPECIFIED: + m_flags |= *value; + m_modified = 1; + + if(((*value) & LGF_COMPLETED) && !m_goal_completed) + { + if(!f_save_load) + GoalComplete(handle,announce); + m_goal_completed = true; + } + + if (Game_mode & GM_MULTI && Netgame.local_role!=LR_CLIENT) + { + //send it off to the clients + SendMultiUpdate(handle); + } + return true; + break; + case LO_GET_SPECIFIED: + *value = m_flags; + return true; + break; + case LO_CLEAR_SPECIFIED: + //multisafe will pass in 0xFFFFFFFF on a clear before actually setting flags, + //so we need to ignore that case + if((*value)!=0xFFFFFFFF && m_goal_completed) + { + //check to see if we are marking this goal as "Not completed" + if((*value)&LGF_COMPLETED) + { + //unmark this m_goal_completed variable + m_goal_completed = false; + } + } + + m_flags &= ~(*value); + m_modified = 1; + + if (Game_mode & GM_MULTI && Netgame.local_role!=LR_CLIENT) + { + //send it off to the clients + SendMultiUpdate(handle); + } + return true; + break; + } + + return false; +} + +bool lgoal::SetDesc(char *desc) +{ + if(!desc) + return false; + + if(m_desc) + mem_free(m_desc); + + m_desc = (char *) mem_malloc(strlen(desc) + 1); + strcpy(m_desc, desc); + + return true; +} + +int lgoal::GetName(char *name, int buffer_size) +{ + if(!name) + return 0; + + if(buffer_size <= 0) + return strlen(m_name); + + if(m_name == NULL) + { + name[0] = '\0'; + return 0; + } + + strncpy(name, m_name, buffer_size); + name[buffer_size - 1] = '\0'; + + return strlen(m_name); +} + +int lgoal::GetCompletionMessage(char *message, int buffer_size) +{ + if(!message) + return 0; + + if(buffer_size <= 0) + return strlen(m_completion_message); + + if(m_completion_message == NULL) + { + message[0] = '\0'; + return 0; + } + + strncpy(message, m_completion_message, buffer_size); + message[buffer_size - 1] = '\0'; + + return strlen(m_completion_message); +} + +int lgoal::GetItemName(char *iname, int buffer_size) +{ + if(!iname) + return 0; + + if(buffer_size <= 0) + return strlen(m_item_name); + + if(m_item_name == NULL) + { + iname[0] = '\0'; + return 0; + } + + strncpy(iname, m_item_name, buffer_size); + iname[buffer_size - 1] = '\0'; + + return strlen(m_item_name); +} + +int lgoal::GetDesc(char *desc, int buffer_size) +{ + if(!desc) + return 0; + + if(buffer_size <= 0) + return strlen(m_desc); + + if(m_desc == NULL) + { + desc[0] = '\0'; + return 0; + } + + strncpy(desc, m_desc, buffer_size); + desc[buffer_size - 1] = '\0'; + + return strlen(m_desc); +} + +int lgoal::GetNumItems() +{ + return m_num_items; +} + +void lgoal::Reset(bool f_from_editor) +{ + if(m_name) + mem_free(m_name); + + if(m_item_name) + mem_free(m_item_name); + + if(m_desc) + mem_free(m_desc); + + m_name = NULL; + m_item_name = NULL; + m_desc = NULL; + m_num_items = 0; + m_priority = 0; + m_g_list = 0; + m_goal_completed = false; + + if(f_from_editor) + m_flags = LGF_ENABLED | LGF_TELCOM_LISTS | LGF_COMP_DALLAS; + else + m_flags = 0; +} + +void lgoal::SendStateToPlayer(int index,int pnum) +{ + if(!(Game_mode&GM_MULTI)) + return; + + if(m_modified==0) + return; + + ASSERT(Netgame.local_role==LR_SERVER); + ASSERT(pnum>=0 && pnum= m_num_goals) + return false; + + return m_goal[index].GetStatus(index, operation, flags,false,announce); +} + +bool levelgoals::GoalPriority(int index, char operation, int *flags) +{ + if(index < 0 || index >= m_num_goals) + return false; + + return m_goal[index].Priority(index,operation, flags); +} + +bool levelgoals::GoalGoalList(int index, char operation, char *value) +{ + if(index < 0 || index >= m_num_goals) + return false; + + return m_goal[index].GoalList(operation, value); +} + +int levelgoals::AddGoal(bool f_from_editor) +{ + if(m_num_goals < MAX_LEVEL_GOALS) + { + m_goal[m_num_goals].Reset(f_from_editor); + m_num_goals++; + return m_num_goals - 1; + } + else + { + return -1; + } +} + +bool levelgoals::DeleteGoal(int index) +{ + int i; + + if(index < 0 || index >= m_num_goals) + return false; + + m_goal[index].Reset(false); + + for(i = index; i < m_num_goals - 1; i++) + { + m_goal[i] = m_goal[i + 1]; + } + + m_num_goals--; + + return true; +} + +int levelgoals::GoalAddItem(int goal_index) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return -1; + + return m_goal[goal_index].AddItem(); +} + +bool levelgoals::GoalDeleteItem(int goal_index, int item_index) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return false; + + return m_goal[goal_index].DeleteItem(item_index); +} + +bool levelgoals::GoalItemInfo(int goal_index, int index, char operation, char *type, int *handle, bool *done) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return false; + + return m_goal[goal_index].ItemInfo(index, operation, type, handle, done); +} + +bool levelgoals::GoalSetName(int goal_index, char *name) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return false; + + return m_goal[goal_index].SetName(goal_index,name); +} + +bool levelgoals::GoalSetItemName(int goal_index, char *iname) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return false; + + return m_goal[goal_index].SetItemName(iname); +} + +bool levelgoals::GoalSetDesc(int goal_index, char *desc) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return false; + + return m_goal[goal_index].SetDesc(desc); +} + +bool levelgoals::GoalSetCompletionMessage(int goal_index, char *message) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return false; + + return m_goal[goal_index].SetCompletionMessage(message); +} + +int levelgoals::GoalGetName(int goal_index, char *name, int buffer_size) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return -1; + + return m_goal[goal_index].GetName(name, buffer_size); +} + +int levelgoals::GoalGetCompletionMessage(int goal_index, char *message, int buffer_size) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return -1; + + return m_goal[goal_index].GetCompletionMessage(message, buffer_size); +} + +int levelgoals::GoalGetItemName(int goal_index, char *iname, int buffer_size) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return -1; + + return m_goal[goal_index].GetItemName(iname, buffer_size); +} + +int levelgoals::GoalGetDesc(int goal_index, char *desc, int buffer_size) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return -1; + + return m_goal[goal_index].GetDesc(desc, buffer_size); +} + +int levelgoals::GoalGetNumItems(int goal_index) +{ + if(goal_index < 0 || goal_index >= m_num_goals) + return -1; + + return m_goal[goal_index].GetNumItems(); +} + +int levelgoals::GetNumGoals() +{ + return m_num_goals; +} + +bool levelgoals::SaveLevelGoalInfo(CFILE *fptr) +{ + int i; + cf_WriteShort(fptr, LEVEL_GOAL_VERSION); + + cf_WriteShort(fptr, m_num_goals); + for(i = 0; i < m_num_goals; i++) + { + char name[256]; + char iname[256]; + char desc[2560]; + char message[2560]; + int len; + int num_items; + int j; + int priority; + char g_list; + int status; + + Level_goals.GoalStatus(i, LO_GET_SPECIFIED, &status); + cf_WriteInt(fptr, status); + + Level_goals.GoalPriority(i, LO_GET_SPECIFIED, &priority); + cf_WriteInt(fptr, priority); + + Level_goals.GoalGoalList(i, LO_GET_SPECIFIED, &g_list); + cf_WriteByte(fptr, g_list); + + Level_goals.GoalGetName(i, name, 256); + Level_goals.GoalGetItemName(i, iname, 256); + Level_goals.GoalGetDesc(i, desc, 2560); + Level_goals.GoalGetCompletionMessage(i, message, 2560); + + len = strlen(name); + cf_WriteShort(fptr, len); + if(len) + { + cf_WriteBytes((unsigned char *)name, len, fptr); + } + + len = strlen(iname); + cf_WriteShort(fptr, len); + if(len) + { + cf_WriteBytes((unsigned char *)iname, len, fptr); + } + + len = strlen(desc); + cf_WriteShort(fptr, len); + if(len) + { + cf_WriteBytes((unsigned char *)desc, len, fptr); + } + + len = strlen(message); + cf_WriteShort(fptr, len); + if(len) + { + cf_WriteBytes((unsigned char *)message, len, fptr); + } + + num_items = Level_goals.GoalGetNumItems(i); + cf_WriteShort(fptr, num_items); + for(j = 0; j < num_items; j++) + { + char type; + int handle; + bool f_done; + + Level_goals.GoalItemInfo(i, j, LO_GET_SPECIFIED, &type, &handle, &f_done); + cf_WriteByte(fptr, type); + cf_WriteInt(fptr, handle); + cf_WriteByte(fptr, (char)f_done); + } + } + + cf_WriteInt(fptr, m_flags); + + return true; +} + +bool levelgoals::LGStatus(char operation, int *value) +{ + if(!value) + return false; + + switch(operation) + { + case LO_SET_SPECIFIED: + m_flags |= *value; + return true; + break; + case LO_GET_SPECIFIED: + *value = m_flags; + return true; + break; + case LO_CLEAR_SPECIFIED: + m_flags &= ~(*value); + return true; + break; + } + + return false; +} + +bool levelgoals::LoadLevelGoalInfo(CFILE *fptr) +{ + CleanupAfterLevel(); + + int i; + short version = cf_ReadShort(fptr); + int num_goals = cf_ReadShort(fptr); + + for(i = 0; i < num_goals; i++) + { + char name[256]; + char iname[256]; + char desc[2560]; + char message[2560]; + int len; + int num_items; + int j; + int priority; + char g_list; + int status; + + int gi = AddGoal(false); + ASSERT(gi == i); + + status = cf_ReadInt(fptr); + m_goal[i].GetStatus(i, LO_SET_SPECIFIED, &status, true); + + priority = cf_ReadInt(fptr); + Level_goals.GoalPriority(i, LO_SET_SPECIFIED, &priority); + + if(version < 4) + { + g_list = 0; + } + else + { + g_list = cf_ReadByte(fptr); + } + Level_goals.GoalGoalList(i, LO_SET_SPECIFIED, &g_list); + + len = cf_ReadShort(fptr); + if(len) + { + cf_ReadBytes((unsigned char *)name, len, fptr); + } + name[len] = '\0'; + + len = cf_ReadShort(fptr); + if(len) + { + cf_ReadBytes((unsigned char *)iname, len, fptr); + } + iname[len] = '\0'; + + len = cf_ReadShort(fptr); + if(len) + { + cf_ReadBytes((unsigned char *)desc, len, fptr); + } + desc[len] = '\0'; + + if(version < 3) + { + message[0] = '\0'; + } + else + { + len = cf_ReadShort(fptr); + if(len) + { + cf_ReadBytes((unsigned char *)message, len, fptr); + } + message[len] = '\0'; + } + + Level_goals.GoalSetName(i, name); + Level_goals.GoalSetItemName(i, iname); + Level_goals.GoalSetDesc(i, desc); + Level_goals.GoalSetCompletionMessage(i, message); + + num_items = cf_ReadShort(fptr); + for(j = 0; j < num_items; j++) + { + char type; + int handle; + bool f_done; + + int item_slot = GoalAddItem(i); + ASSERT(item_slot == j); + + type = cf_ReadByte(fptr); + handle = cf_ReadInt(fptr); + f_done = cf_ReadByte(fptr) != 0; + Level_goals.GoalItemInfo(i, j, LO_SET_SPECIFIED, &type, &handle, &f_done); + } + } + + if(version >= 2) + m_flags = cf_ReadInt(fptr); + else + m_flags = 0; + + ResetModifiedFlags(); + + return true; +} +/* +int levelgoals::GetCurrentPrimaryGoalIndex() +{ + int i; + int cp; + int bestp = -1; + int bestg = -1; + + for(i = 0; i < m_num_goals; i++) + { + int flags; + m_goal[i].GetStatus(i, LO_GET_SPECIFIED, &flags); + + if(!(flags & (LGF_SECONDARY_GOAL | LGF_COMPLETED))) + { + if(flags & LGF_ENABLED) + { + m_goal[i].Priority(i,LO_GET_SPECIFIED, &cp); + + if(cp > bestp) + { + bestp = cp; + bestg = i; + } + } + } + } + + return bestg; +}*/ + +//int m_num_active_primaries = 0; +//int m_active_primaries[MAX_LEVEL_GOALS]; +//int m_num_active_secondaries = 0; +//int m_active_secondaries[MAX_LEVEL_GOALS]; + +int levelgoals::GetNumActivePrimaryGoals() +{ + int x; + m_num_active_primaries = 0; + + int i; + int bestp[4]; + int cp; // Current priority + char cl; // Current list + int temp_p[MAX_GOAL_LISTS][MAX_LEVEL_GOALS]; + char temp_g[MAX_GOAL_LISTS][MAX_LEVEL_GOALS]; + + int temp_count[MAX_GOAL_LISTS]; + + for(i = 0; i < MAX_GOAL_LISTS; i++) + { + bestp[i] = -1; + temp_count[i] = 0; + } + + // Determine the active number for the current list + for(i = 0; i < m_num_goals; i++) + { + int flags; + m_goal[i].GetStatus(i, LO_GET_SPECIFIED, &flags); + + if(!(flags & (LGF_COMPLETED | LGF_FAILED | LGF_SECONDARY_GOAL))) + { + if(flags & LGF_ENABLED) + { + m_goal[i].GoalList(LO_GET_SPECIFIED, &cl); + m_goal[i].Priority(i,LO_GET_SPECIFIED, &cp); + + if(bestp[cl] == -1 || cp <= bestp[cl]) + { + bestp[cl] = cp; + temp_p[cl][temp_count[cl]] = cp; + temp_g[cl][(temp_count[cl])++] = i; + } + } + } + } + + for(x = 0; x < MAX_GOAL_LISTS; x++) + { + for(i = 0; i < temp_count[x]; i++) + { + if(temp_p[x][i] == bestp[x]) + { + m_active_primaries[m_num_active_primaries++] = temp_g[x][i]; + } + } + } + + return m_num_active_primaries; +} + +int levelgoals::GetActivePrimaryGoal(int p_index) +{ + if(p_index < 0 || p_index >= m_num_active_primaries) + return -1; + + return m_active_primaries[p_index]; +} + +int levelgoals::GetNumActiveSecondaryGoals() +{ + int x; + m_num_active_secondaries = 0; + + int i; + int bestp[4]; + int cp; // Current priority + char cl; // Current list + int temp_p[MAX_GOAL_LISTS][MAX_LEVEL_GOALS]; + char temp_g[MAX_GOAL_LISTS][MAX_LEVEL_GOALS]; + + int temp_count[MAX_GOAL_LISTS]; + + for(i = 0; i < MAX_GOAL_LISTS; i++) + { + bestp[i] = -1; + temp_count[i] = 0; + } + + // Determine the active number for the current list + for(i = 0; i < m_num_goals; i++) + { + int flags; + m_goal[i].GetStatus(i, LO_GET_SPECIFIED, &flags); + + if(!(flags & (LGF_COMPLETED | LGF_FAILED))) + { + if(flags & LGF_ENABLED) + { + if(flags & LGF_SECONDARY_GOAL) + { + m_goal[i].GoalList(LO_GET_SPECIFIED, &cl); + m_goal[i].Priority(i,LO_GET_SPECIFIED, &cp); + + if(bestp[cl] == -1 || cp <= bestp[cl]) + { + bestp[cl] = cp; + temp_p[cl][temp_count[cl]] = cp; + temp_g[cl][(temp_count[cl])++] = i; + } + } + } + } + } + + for(x = 0; x < MAX_GOAL_LISTS; x++) + { + for(i = 0; i < temp_count[x]; i++) + { + if(temp_p[x][i] == bestp[x]) + { + m_active_secondaries[m_num_active_secondaries++] = temp_g[x][i]; + } + } + } + + return m_num_active_secondaries; +} + +int levelgoals::GetActiveSecondaryGoal(int s_index) +{ + if(s_index < 0 || s_index >= m_num_active_secondaries) + return -1; + + return m_active_secondaries[s_index]; +} + +void levelgoals::DoFrame() +{ + int i; + + // Check for auto-completion + for(i = 0; i < m_num_goals; i++) + { + int flags; + m_goal[i].GetStatus(i, LO_GET_SPECIFIED, &flags); + + if(!(flags & (LGF_COMPLETED | LGF_FAILED))) + { + if(flags & LGF_ENABLED) + { + char ctype; + int chandle; + bool cdone; + bool f_done = true; + + m_goal[i].ItemInfo(0, LO_GET_SPECIFIED, &ctype, &chandle, &cdone); + + if(ctype == LIT_TERRAIN_CELL) + { + if(Game_mode & GM_MULTI) + { + int j; + + for(j = 0; j < MAX_PLAYERS; j++) + { + if((NetPlayers[j].flags & NPF_CONNECTED) && (NetPlayers[j].sequence >= NETSEQ_PLAYING)) + { + object *player = &Objects[Players[j].objnum]; + if(!ROOMNUM_OUTSIDE(player->roomnum)) + { + f_done = false; + } + } + } + } + else + { + if(!ROOMNUM_OUTSIDE(Player_object->roomnum)) + { + f_done = false; + } + } + } + else if(ctype == LIT_ANY_MINE) + { + if(Game_mode & GM_MULTI) + { + int j; + + for(j = 0; j < MAX_PLAYERS; j++) + { + if((NetPlayers[j].flags & NPF_CONNECTED) && (NetPlayers[j].sequence >= NETSEQ_PLAYING)) + { + object *player = &Objects[Players[j].objnum]; + if(ROOMNUM_OUTSIDE(player->roomnum)) + { + f_done = false; + } + } + } + } + else + { + if(ROOMNUM_OUTSIDE(Player_object->roomnum)) + { + f_done = false; + } + } + } + else + { + continue; + } + + if(f_done) + { + int flags = LGF_COMPLETED; + m_goal[i].GetStatus(i, LO_SET_SPECIFIED, &flags); + } + } + } + } + + if(GetNumActivePrimaryGoals() == 0 && !(m_flags & LF_ALL_PRIMARIES_DONE)) + { + m_flags |= LF_ALL_PRIMARIES_DONE; + + tOSIRISEventInfo ei; + Osiris_CallLevelEvent(EVT_ALL_LEVEL_GOALS_COMPLETE, &ei); + } +} + +void levelgoals::CleanupAfterLevel() +{ + int i; + + for(i = m_num_goals; i >= 0; i--) + { + DeleteGoal(i); + } +} + +void levelgoals::InitLevel() +{ + int i, j; + + for(i = 0; i <= Highest_object_index; i++) + { + if(Objects[i].type != OBJ_NONE) + { + Objects[i].flags &= (~(OF_INFORM_PLAYER_COLLIDE_TO_LG|OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG|OF_INFORM_DESTROY_TO_LG)); + } + } + + for(i = 0; i <= Highest_room_index; i++) + { + if(Rooms[i].used) + { + Rooms[i].flags &= (~(RF_INFORM_RELINK_TO_LG)); + } + } + + for(i = 0; i < Num_triggers; i++) + { + Triggers[i].flags &= (~(TF_INFORM_ACTIVATE_TO_LG)); + } + + for(i = 0; i < m_num_goals; i++) + { + int flags; + m_goal[i].GetStatus(i, LO_GET_SPECIFIED, &flags); + flags &= LGF_COMP_MASK; + + for(j = 0; j < m_goal[i].GetNumItems(); j++) + { + char type; + int handle; + bool done; + + m_goal[i].ItemInfo(j, LO_GET_SPECIFIED, &type, &handle, &done); + + switch(type) + { + case LIT_TERRAIN_CELL: + break; + case LIT_INTERNAL_ROOM: + if(flags == LGF_COMP_ENTER) + { + if(handle >= 0 && handle <= Highest_room_index && Rooms[handle].used) + { + Rooms[handle].flags |= RF_INFORM_RELINK_TO_LG; + } + } + break; + case LIT_OBJECT: + if(flags == LGF_COMP_PLAYER) + { + object *obj = ObjGet(handle); + if(obj) + { + obj->flags |= OF_INFORM_PLAYER_COLLIDE_TO_LG; + } + } + else if(flags == LGF_COMP_PLAYER_WEAPON) + { + object *obj = ObjGet(handle); + if(obj) + { + obj->flags |= OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG; + } + } + else if(flags == LGF_COMP_DESTROY) + { + object *obj = ObjGet(handle); + if(obj) + { + obj->flags |= OF_INFORM_DESTROY_TO_LG; + } + } + break; + case LIT_TRIGGER: + if(handle >= 0 && handle < Num_triggers) + { + if(flags == LGF_COMP_ACTIVATE) + { + Triggers[handle].flags |= TF_INFORM_ACTIVATE_TO_LG; + } + } + break; + case LIT_ANY_MINE: + break; + } + } + } +} + +void levelgoals::Inform(char type, int comp_type, int handle) +{ + int i, j; + + for(i = 0; i < m_num_goals; i++) + { + int flags; + m_goal[i].GetStatus(i, LO_GET_SPECIFIED, &flags); + + if(!(flags & LGF_ENABLED) && ( + comp_type == LGF_COMP_ACTIVATE || + comp_type == LGF_COMP_ENTER || + comp_type == LGF_COMP_PLAYER_WEAPON || + comp_type == LGF_COMP_PLAYER)) + { + continue; + } + + int cur_comp_type = flags & LGF_COMP_MASK; + int num_items = m_goal[i].GetNumItems(); + + if(flags != LGF_COMP_DALLAS && cur_comp_type != comp_type) + { + continue; + } + + for(j = 0; j < num_items; j++) + { + char ctype; + int chandle; + bool cdone; + + m_goal[i].ItemInfo(j, LO_GET_SPECIFIED, &ctype, &chandle, &cdone); + + if(cdone || type != ctype || chandle != handle) + { + continue; + } + + bool done = true; + m_goal[i].ItemInfo(j, LO_SET_SPECIFIED, NULL, NULL, &done); + + int k; + bool f_done = true; + + for(k = 0; k < num_items; k++) + { + m_goal[i].ItemInfo(j, LO_SET_SPECIFIED, NULL, NULL, &done); + if(!done) + { + f_done = false; + break; + } + } + + if(f_done) + { + int flags = LGF_COMPLETED; + m_goal[i].GetStatus(i, LO_SET_SPECIFIED, &flags); + } + + // Make this after the goal complete is done so that designers can say, if this and not + // goal complete + tOSIRISEventInfo ei; + ei.evt_level_goal_item_complete.level_goal_index = i; + + Osiris_CallLevelEvent(EVT_LEVEL_GOAL_ITEM_COMPLETE, &ei); + } + } +} + +int levelgoals::GoalFindId(char *goal_name) +{ + int i; + char cur_name[200]; + + for(i = 0; i < m_num_goals; i++) + { + GoalGetName(i, cur_name, 200); + if(stricmp(cur_name, goal_name) == 0) + { + return i; + } + } + + return -1; +} + +void levelgoals::ResetModifiedFlags(void) +{ + for(int i = 0; i < m_num_goals; i++) + { + m_goal[i].ResetModified(); + } +} + +void levelgoals::MultiSendChangedGoals(int pnum) +{ + for(int i = 0; i < m_num_goals; i++) + { + m_goal[i].SendStateToPlayer(i,pnum); + } +} diff --git a/Descent3/levelgoal.h b/Descent3/levelgoal.h new file mode 100644 index 000000000..c1f7c6d7a --- /dev/null +++ b/Descent3/levelgoal.h @@ -0,0 +1,142 @@ +#ifndef _LEVELGOAL_H_ +#define _LEVELGOAL_H_ + +#include "object.h" +#include "CFILE.H" +#if defined(MACOSX) +#include +#else +#include +#endif +#include "mem.h" +#include "levelgoal_external.h" + +class litem +{ + public: + litem(){m_type = LIT_OBJECT; m_handle = OBJECT_HANDLE_NONE; m_f_done = false;}; + + char m_type; + int m_handle; + bool m_f_done; +}; + +class lgoal +{ + private: + char *m_name; + char *m_item_name; + char *m_desc; + char *m_completion_message; + + int m_num_items; + litem m_item[MAX_GOAL_ITEMS]; + + char m_g_list; + char m_modified; + bool m_goal_completed; //since flags in a multiplayer game (client) get set to 0, then + //reset, we need to use this to store whether the goal has + //been completed, else we *possibly* can complete the goal more than + //once. + int m_priority; + unsigned int m_flags; + + void GoalComplete(int handle,bool announce); + void SendMultiUpdate(int handle); + + public: + lgoal(){m_name = NULL; m_item_name = NULL; m_desc = NULL; m_num_items = 0; m_completion_message = NULL; m_priority = 0; m_g_list = 0; m_flags = LGF_ENABLED | LGF_TELCOM_LISTS; m_modified = 0; m_goal_completed = false;}; + ~lgoal(){if(m_name)mem_free(m_name); if(m_item_name)mem_free(m_item_name); if(m_desc)mem_free(m_desc);}; + + int AddItem(); + bool DeleteItem(int index); + bool ItemInfo(int index, char operation, char *type, int *handle, bool *done); + + void Reset(bool f_from_editor); + + bool SetName(int handle,char *name); + bool SetItemName(char *iname); + bool SetDesc(char *desc); + bool SetCompletionMessage(char *message); + + int GetName(char *name, int buffer_size); + int GetItemName(char *iname, int buffer_size); + int GetDesc(char *desc, int buffer_size); + int GetCompletionMessage(char *message, int buffer_size); + + bool Priority(int handle,char operation, int *value); + bool GoalList(char operation, char *value); + bool GetStatus(int handle, char operation, int *value, bool f_save_load = false,bool announce=true); + + void SendStateToPlayer(int index,int pnum); + void ResetModified(void); + + int GetNumItems(); +}; + +// Goals are sorted by priority and completion +class levelgoals +{ + private: + int m_num_goals; + lgoal m_goal[MAX_LEVEL_GOALS]; + int m_flags; + + int m_num_active_primaries; + int m_active_primaries[MAX_LEVEL_GOALS]; + int m_num_active_secondaries; + int m_active_secondaries[MAX_LEVEL_GOALS]; + + public: + levelgoals(){m_num_goals = 0; m_num_active_secondaries = 0; m_num_active_primaries = 0;}; + + int AddGoal(bool f_from_editor); + bool DeleteGoal(int index); + + int GoalAddItem(int goal_index); + bool GoalDeleteItem(int goal_index, int item_index); + bool GoalItemInfo(int goal_index, int index, char operation, char *type, int *handle, bool *done); + + bool GoalSetName(int goal_index, char *name); + bool GoalSetItemName(int goal_index, char *iname); + bool GoalSetDesc(int goal_index, char *desc); + bool GoalSetCompletionMessage(int goal_index, char *message); + + int GoalGetName(int goal_index, char *name, int buffer_size); + int GoalGetItemName(int goal_index, char *iname, int buffer_size); + int GoalGetDesc(int goal_index, char *desc, int buffer_size); + int GoalGetCompletionMessage(int goal_index, char *message, int buffer_size); + + bool GoalPriority(int goal_index, char operation, int *value); + bool GoalGoalList(int goal_index, char operation, char *value); + bool GoalStatus(int goal_index, char operation, int *value,bool announce=true); + + int GoalFindId(char *goal_name); + + int GoalGetNumItems(int goal_index); + + int GetNumGoals(); + + bool SaveLevelGoalInfo(CFILE *fptr); + bool LoadLevelGoalInfo(CFILE *fptr); + + bool LGStatus(char operation, int *value); + + int GetNumActivePrimaryGoals(); + int GetActivePrimaryGoal(int p_index); + + int GetNumActiveSecondaryGoals(); + int GetActiveSecondaryGoal(int s_index); + + void DoFrame(); + void CleanupAfterLevel(); + void InitLevel(); + void Inform(char type, int comp_type, int handle); + + void ResetModifiedFlags(void); + void MultiSendChangedGoals(int pnum); +}; + +extern levelgoals Level_goals; + +#endif \ No newline at end of file diff --git a/Descent3/levelgoal_external.h b/Descent3/levelgoal_external.h new file mode 100644 index 000000000..7e8697f5b --- /dev/null +++ b/Descent3/levelgoal_external.h @@ -0,0 +1,49 @@ +#ifndef LEVELGOALEXTERNAL_H_ +#define LEVELGOALEXTERNAL_H_ + +#define LF_AUTO_END_LEVEL 0x01 +#define LF_ALL_PRIMARIES_DONE 0x02 + +// Level Item Types +#define LIT_TERRAIN_CELL 0 +#define LIT_INTERNAL_ROOM 1 +#define LIT_OBJECT 2 +#define LIT_TRIGGER 3 +#define LIT_ANY_MINE 4 + +// Level Item Operations +#define LO_SET_SPECIFIED 0 +#define LO_GET_SPECIFIED 1 +#define LO_CLEAR_SPECIFIED 2 + +// Level Goal Flags +#define LGF_BLANK1 0x00000001 +#define LGF_SECONDARY_GOAL 0x00000002 +#define LGF_ENABLED 0x00000004 +#define LGF_COMPLETED 0x00000008 +#define LGF_TELCOM_LISTS 0x00000010 +#define LGF_GB_DOESNT_KNOW_LOC 0x00000020 +#define LGF_NOT_LOC_BASED 0x00000040 +#define LGF_FAILED 0x00000080 +#define LGF_COMP_ACTIVATE 0x00000100 +#define LGF_COMP_ENTER 0x00000200 +#define LGF_COMP_DESTROY 0x00000400 +#define LGF_COMP_PLAYER_WEAPON 0x00000800 +#define LGF_COMP_PLAYER 0x00001000 +#define LGF_COMP_DALLAS 0x00002000 + +#define LGF_COMP_MASK (LGF_COMP_ACTIVATE|LGF_COMP_ENTER|LGF_COMP_DESTROY|LGF_COMP_PLAYER_WEAPON|LGF_COMP_PLAYER|LGF_COMP_DALLAS) + +#define LGF_COMP_TERRAIN_MASK (LGF_COMP_ENTER|LGF_COMP_DALLAS) +#define LGF_COMP_ROOM_MASK (LGF_COMP_ENTER|LGF_COMP_DALLAS) +#define LGF_COMP_OBJECT_MASK (LGF_COMP_DESTROY|LGF_COMP_PLAYER_WEAPON|LGF_COMP_PLAYER|LGF_COMP_DALLAS) +#define LGF_COMP_TRIGGER_MASK (LGF_COMP_ACTIVATE|LGF_COMP_DALLAS) +#define LGF_COMP_MINE_MASK (LGF_COMP_ENTER|LGF_COMP_DALLAS) + + +#define MAX_GOAL_ITEMS 12 +#define MAX_LEVEL_GOALS 32 + +#define MAX_GOAL_LISTS 4 + +#endif \ No newline at end of file diff --git a/Descent3/lighting.cpp b/Descent3/lighting.cpp new file mode 100644 index 000000000..44d73517a --- /dev/null +++ b/Descent3/lighting.cpp @@ -0,0 +1,2406 @@ +/* + * $Logfile: /DescentIII/Main/lighting.cpp $ + * $Revision: 109 $ + * $Date: 10/02/01 12:34p $ + * $Author: Matt $ + * $NoKeywords: $ + */ + +#include "3d.h" +#include "texture.h" +#include "gametexture.h" +#include "lighting.h" +#include "lightmap.h" +#include "descent.h" +#include "game.h" +#include "room.h" +#include +#include +#include "findintersection.h" +#include "lightmap_info.h" +#include "polymodel.h" +#include "special_face.h" +#include "mem.h" +#include "config.h" +#include "dedicated_server.h" +#include "objinfo.h" +#include "Macros.h" + +#define NUM_DYNAMIC_CLASSES 7 +#define MAX_DYNAMIC_FACES 2000 +#define MAX_DYNAMIC_CELLS 1500 +#define MAX_VOLUME_OBJECTS 500 + + +// How much memory set aside for dynamic lightmaps +// Currently 3 megs + +int DYNAMIC_LIGHTMAP_MEMORY=1000000; +#define MAX_DYNAMIC_LIGHTMAPS 2000 + +typedef struct +{ + int objnum; + int handle; +} volume_object; + +float Specular_tables[3][MAX_SPECULAR_INCREMENTS]; + +ushort *Dynamic_lightmap_memory=NULL; +float Light_component_scalar[32]; +float Ubyte_to_float[256]; + +static ubyte Lmi_spoken_for[MAX_LIGHTMAP_INFOS/8]; + +dynamic_lightmap *Dynamic_lightmaps; +dynamic_face Dynamic_face_list[MAX_DYNAMIC_FACES]; +ushort Specular_face_list[MAX_DYNAMIC_FACES]; +volume_object Dynamic_volume_object_list[MAX_VOLUME_OBJECTS]; +dynamic_cell Dynamic_cell_list[MAX_DYNAMIC_CELLS]; +int Specular_maps[NUM_DYNAMIC_CLASSES]; + +ushort Edges_to_blend[MAX_DYNAMIC_LIGHTMAPS]; +int Num_edges_to_blend=0; + + +int Num_specular_faces=0; +int Num_dynamic_faces=0; +int Num_dynamic_lightmaps=0; +int Cur_dynamic_mem_ptr=0; +int Num_volume_objects=0; +int Num_dynamic_cells=0; +int Num_destroyed_lights_this_frame=0; + +#define MAX_DESTROYED_LIGHTS_PER_FRAME 20 +int Destroyed_light_rooms_this_frame[MAX_DESTROYED_LIGHTS_PER_FRAME]; +int Destroyed_light_faces_this_frame[MAX_DESTROYED_LIGHTS_PER_FRAME]; + +// Frees memory used by dynamic light structures +void FreeLighting () +{ + if (Dynamic_lightmap_memory) + mem_free (Dynamic_lightmap_memory); + if (Dynamic_lightmaps) + mem_free (Dynamic_lightmaps); +} + +// Sets up our dynamic lighting maps +// and tables +void InitDynamicLighting () +{ + int i,cl,size; + + mprintf ((0,"Initting dynamic lighting.\n")); + + memset (Lmi_spoken_for,0,MAX_LIGHTMAP_INFOS/8); + + for (i=0;i<16;i++) + Light_component_scalar[i]=i/15.0; + for (i=16;i<32;i++) + Light_component_scalar[i]=1.0; + + for (cl=0,size=128;size>=2;size>>=1,cl++) + { + Specular_maps[cl]=lm_AllocLightmap (size,size); + ASSERT (Specular_maps[cl]!=BAD_LM_INDEX); + } + + + // Init our dynamic memory + if (Mem_low_memory_mode) + { + // Just use 1 meg if in low mem mode + DYNAMIC_LIGHTMAP_MEMORY=500000; + } + + Dynamic_lightmap_memory=(ushort *)mem_malloc (DYNAMIC_LIGHTMAP_MEMORY); + + + // Init our records list + Dynamic_lightmaps=(dynamic_lightmap *)mem_malloc (sizeof(dynamic_lightmap)*MAX_DYNAMIC_LIGHTMAPS); + ASSERT (Dynamic_lightmaps); + + for (i=0;i1.0) + { + mprintf ((0,"ERROR: Trying to get value in Float_to_ubyte that is %f!\n",fnum)); + return 0; + } + + for (i=0;i<256;i++) + { + if (fnumDYNAMIC_LIGHTMAP_MEMORY) + { + mprintf ((0,"Ran out of lightmap memory (%d)\n",DYNAMIC_LIGHTMAP_MEMORY)); + return -1; + } + + int n=Num_dynamic_lightmaps++; + ASSERT (Dynamic_lightmaps[n].used==0); + + Dynamic_lightmaps[n].used=1; + Dynamic_lightmaps[n].mem_ptr=&Dynamic_lightmap_memory[Cur_dynamic_mem_ptr/2]; + ASSERT (Dynamic_lightmaps[n].mem_ptr); + + Cur_dynamic_mem_ptr+=total; + + return n; +} + + +// Makes all the edges of dynamic lighting blend into the body of the lightmap +void BlendLightingEdges (lightmap_info *lmi_ptr) +{ + int lmi_handle=lmi_ptr-LightmapInfo; + int h=lmi_h (lmi_handle); + int w=lmi_w (lmi_handle); + int lm_handle=lmi_ptr->lm_handle; + ushort *dest_data=(ushort *)lm_data(lm_handle); + ushort *src_data=dest_data; + int dest_x=lmi_ptr->x1-1; + int dest_y=lmi_ptr->y1-1; + int i; + + ASSERT (dest_data); + if (!dest_data) + return; + + ASSERT (dest_x>=0 && dest_y>=0); + ASSERT (dest_x+w<127); + ASSERT (dest_y+h<127); + + for (i=0;inum_faces;i++) + { + face *fp=&rp->faces[i]; + + // check to see if this face is within reach + if(Light_max_xyz.y < fp->min_xyz.y || + fp->max_xyz.y < Light_min_xyz.y || + Light_max_xyz.x < fp->min_xyz.x || + fp->max_xyz.x < Light_min_xyz.x || + Light_max_xyz.z < fp->min_xyz.z || + fp->max_xyz.z < Light_min_xyz.z) continue; + + // Make sure face was rendered + if (fp->renderframe!=((FrameCount-1)%256)) + continue; + + if (Num_dynamic_faces>=MAX_DYNAMIC_FACES) + { + mprintf ((0,"Too many dynamic faces!\n")); + return; + } + + // Make sure there already is a lightmap for this face + if (!(fp->flags & FF_LIGHTMAP)) + continue; + + if (Lmi_spoken_for[fp->lmi_handle/8] & (1<<(fp->lmi_handle%8))) + continue; + + int xres=lmi_w(fp->lmi_handle); + int yres=lmi_h(fp->lmi_handle); + + lightmap_info *lmi_ptr=&LightmapInfo[fp->lmi_handle]; + // Lightmap width and height + int lmw=lm_w(lmi_ptr->lm_handle); + + // Check for backfaces + vector subvec=*pos-lmi_ptr->upper_left; + float dist_from_plane=fabs(vm_DotProduct(&subvec,&lmi_ptr->normal)); + + if (dist_from_plane>light_dist) + continue; + + if (light_direction) + { + // If this is a directional light, check for backfaces + vector norm_vec=*pos-rp->verts[fp->face_verts[0]]; + + if ((norm_vec * lmi_ptr->normal)<0) + { + // Backface, skip this light + continue; + } + // Test to see if all vertices are behind the light object, if so then bail + int in_front=0; // + for (t=0;tnum_verts && in_front==0;t++) + { + norm_vec=rp->verts[fp->face_verts[t]]-*pos; + if (norm_vec * *light_direction>0) + in_front=1; + } + + if (!in_front) // This face is completely behind, so bail! + continue; + } + + // Compute face matrix + matrix facematrix; + vector fvec=-lmi_ptr->normal; + vm_VectorToMatrix(&facematrix,&fvec,NULL,NULL); + + // Get upper left vector of face + vector base_vector=lmi_ptr->upper_left; + + // Get the area of effect for this light + float area_of_effect=(1.0-(dist_from_plane/light_dist))*light_dist; + vector touch_vector=*pos-(lmi_ptr->normal*dist_from_plane); + + subvec=touch_vector-base_vector; + float rdist=vm_DotProduct (&subvec,&facematrix.rvec); + float udist=-vm_DotProduct (&subvec,&facematrix.uvec); + int x_add=1+((area_of_effect+(lmi_ptr->xspacing/2))/lmi_ptr->xspacing); + int y_add=1+((area_of_effect+(lmi_ptr->yspacing/2))/lmi_ptr->yspacing); + + // Get the rectangle of the lightmap that we want to light + int start_x=(rdist/lmi_ptr->xspacing)-x_add; + int start_y=(udist/lmi_ptr->yspacing)-y_add; + int end_x=(rdist/lmi_ptr->xspacing)+x_add+.5; + int end_y=(udist/lmi_ptr->yspacing)+y_add+.5; + + if (end_x<0 || end_y<0 || start_x>=(xres) || start_y>=(yres)) + continue; + + if (start_x<0) + start_x=0; + if (start_y<0) + start_y=0; + if (end_x>xres) + end_x=xres; + if (end_y>yres) + end_y=yres; + + int width=end_x-start_x; + int height=end_y-start_y; + + if (width==0) + width++; + if (height==0) + height++; + + ASSERT (width>0); + ASSERT (height>0); + + if (lmi_ptr->dynamic!=BAD_LM_INDEX) // already lit, so just adjust, not start over + { + lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + dest_data=(ushort *)lm_data (lm_handle); + + if (start_x+lmi_ptr->x1x1; + if (start_x+lmi_ptr->x1+width>GameLightmaps[lm_handle].cx2) + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + + if (start_y+lmi_ptr->y1y1; + if (lmi_ptr->y1+start_y+height>GameLightmaps[lm_handle].cy2) + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + + GameLightmaps[lm_handle].flags|=(LF_CHANGED|LF_LIMITS); + + if (!(Lmi_spoken_for[fp->lmi_handle/8] & (1<<(fp->lmi_handle%8)))) + { + lmilist[num_spoken_for]=fp->lmi_handle; + Lmi_spoken_for[fp->lmi_handle/8]|=(1<<(fp->lmi_handle % 8)); + num_spoken_for++; + } + } + else // Start a new dynamic lightmap + { + // First find a suitable dynamic lightmap to work with + int dynamic_handle=GetFreeDynamicLightmap (xres,yres); + + if (dynamic_handle<0) + { + mprintf ((0,"No free dynamic maps!\n")); + return; // None free! + } + + + // Now copy our source data to our dest data so we have a base to work with + ushort *src_data; + + src_data=(ushort *)lm_data (LightmapInfo[fp->lmi_handle].lm_handle); + dest_data=Dynamic_lightmaps[dynamic_handle].mem_ptr; + + for (int y=0,index=0;yy1+y)*lmw)+lmi_ptr->x1+x]; + } + + } + + dest_data=src_data; + lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + + // Mark it as changed + lmi_ptr->dynamic=dynamic_handle; + + if (!(GameLightmaps[lm_handle].flags & LF_LIMITS)) + { + GameLightmaps[lm_handle].cx1=start_x+lmi_ptr->x1; + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + GameLightmaps[lm_handle].cy1=start_y+lmi_ptr->y1; + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + else + { + if (start_x+lmi_ptr->x1x1; + if (start_x+lmi_ptr->x1+width>GameLightmaps[lm_handle].cx2) + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + + if (start_y+lmi_ptr->y1y1; + if (lmi_ptr->y1+start_y+height>GameLightmaps[lm_handle].cy2) + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + + GameLightmaps[lm_handle].flags|=(LF_LIMITS|LF_CHANGED); + + Dynamic_face_list[Num_dynamic_faces].lmi_handle=fp->lmi_handle; + Num_dynamic_faces++; + + lmilist[num_spoken_for]=fp->lmi_handle; + Lmi_spoken_for[fp->lmi_handle/8]|=(1<<(fp->lmi_handle % 8)); + num_spoken_for++; + + // Setup edge blending + Edges_to_blend[Num_edges_to_blend++]=fp->lmi_handle; + } + + vector element_vec; + + base_vector-=(start_y*(facematrix.uvec*lmi_ptr->yspacing)); + base_vector+=(start_x*(facematrix.rvec*lmi_ptr->xspacing)); + + base_vector-=((facematrix.uvec/2)*lmi_ptr->yspacing); + base_vector+=((facematrix.rvec/2)*lmi_ptr->xspacing); + + // Go through and change each element of this lightmap + // SLOW! + + int texel_num=((start_y+lmi_ptr->y1)*lmw)+start_x+lmi_ptr->x1; + for (int y=0;yyspacing),texel_num+=lmw) + { + element_vec=base_vector; + + for (int x=0;xxspacing)) + { + int lightmap_texel_num=texel_num+x; + + ushort lightmap_texel=dest_data[lightmap_texel_num]; + + if (!(lightmap_texel & OPAQUE_FLAG)) + continue; + + float dist=vm_VectorDistanceQuick (&element_vec,pos); + float scalar=1.0-(dist/light_dist); + + if (light_direction) + { + vector lsubvec=element_vec-*pos; + vm_NormalizeVectorFast (&lsubvec); + float dp=vm_DotProduct (&lsubvec,light_direction); + if (dp < dot_range) + { + continue; + } + else + { + + float add_scale=(dp-dot_range)/(1.0-dot_range); + scalar*=add_scale; + } + } + + if (scalar<=0) + continue; + + int r=(lightmap_texel>>10)& 0x1f; + int g=(lightmap_texel>>5) & 0x1f; + int b=lightmap_texel & 0x1f; + + if(red_scale<0) + { + // we are subtracting light + r = max(0,r+(scalar*red_scale*31)); + }else + { + if (r= 0); + + + Light_position = Light_instance_stack[Light_instance_depth].p; + if (Use_light_direction) + Light_direction = Light_instance_stack[Light_instance_depth].dir; +} + +// Applies lighting to a submodel and all its children +void ApplyLightingToSubmodel (object *obj,poly_model *pm,bsp_info *sm,float light_dist,float red_scale,float green_scale,float blue_scale,float dot_range) +{ + matrix mat; + vector Light_min_xyz; + vector Light_max_xyz; + vector rad; + int i,t; + int subnum=sm-pm->submodel; + int lm_handle; + ushort *dest_data; + ushort lmilist[MAX_DYNAMIC_FACES]; + int num_spoken_for=0; + + int red_limit=31; + int green_limit=31; + int blue_limit=31; + + if (IsNonRenderableSubmodel(pm,subnum)) + return; // Don't do shells, frontfaces, etc + + // Start instance stuff + vector temp_vec=sm->mod_pos+sm->offset; + vm_AnglesToMatrix(&mat,sm->angs.p,sm->angs.h,sm->angs.b); + StartLightingInstance(&temp_vec,&mat ); + + rad.x=light_dist; + rad.y=light_dist; + rad.z=light_dist; + + vector light_pos= Light_position; + vector light_dir= Light_direction; + + Light_min_xyz=light_pos-rad; + Light_max_xyz=light_pos+rad; + + // Now the Light_position is in the submodels frame of reference + for (i=0;inum_faces;i++) + { + // check to see if this face is within reach + if(Light_max_xyz.y < sm->face_min[i].y || + sm->face_max[i].y < Light_min_xyz.y || + Light_max_xyz.x < sm->face_min[i].x || + sm->face_max[i].x < Light_min_xyz.x || + Light_max_xyz.z < sm->face_min[i].z || + sm->face_max[i].z < Light_min_xyz.z) continue; + + + + // Ok, now we know that this light touches this face + if (Num_dynamic_faces>=MAX_DYNAMIC_FACES) + { + mprintf ((0,"Too many dynamic faces!\n")); + DoneLightingInstance(); + return; + } + + lightmap_object_face *fp=&obj->lm_object.lightmap_faces[subnum][i]; + + if (Lmi_spoken_for[fp->lmi_handle/8] & (1<<(fp->lmi_handle%8))) + continue; + + int xres=lmi_w(fp->lmi_handle); + int yres=lmi_h(fp->lmi_handle); + + lightmap_info *lmi_ptr=&LightmapInfo[fp->lmi_handle]; + // Lightmap width and height + int lmw=lm_w(lmi_ptr->lm_handle); + + // Check for backfaces + // Get upper left vector of face + vector base_vector=lmi_ptr->upper_left; + vector subvec=light_pos-lmi_ptr->upper_left; + float dist_from_plane=fabs(vm_DotProduct(&subvec,&lmi_ptr->normal)); + + if (dist_from_plane>light_dist) + continue; + + if (Use_light_direction) + { + // If this is a directional light, check for backfaces + vector norm_vec=light_pos-base_vector; + + if ((norm_vec * lmi_ptr->normal)<0) + { + // Backface, skip this face + continue; + } + + // Test to see if all vertices are behind the light object, if so then bail + polyface *poly_fp=&sm->faces[i]; + int in_front=0; // + for (t=0;tnum_verts && in_front==0;t++) + { + norm_vec=sm->verts[poly_fp->vertnums[t]]-light_pos; + if (norm_vec * light_dir>0) + in_front=1; + } + + if (!in_front) // This face is completely behind, so bail! + continue; + } + + // Compute face matrix + matrix facematrix; + vector fvec=-lmi_ptr->normal; + vm_VectorToMatrix(&facematrix,&fvec,NULL,NULL); + + // Get the area of effect for this light + float area_of_effect=(1.0-(dist_from_plane/light_dist))*light_dist; + vector touch_vector=light_pos-(lmi_ptr->normal*dist_from_plane); + + subvec=touch_vector-base_vector; + float rdist=vm_DotProduct (&subvec,&facematrix.rvec); + float udist=-vm_DotProduct (&subvec,&facematrix.uvec); + int x_add=1+((area_of_effect+(lmi_ptr->xspacing/2))/lmi_ptr->xspacing); + int y_add=1+((area_of_effect+(lmi_ptr->yspacing/2))/lmi_ptr->yspacing); + + // Get the rectangle of the lightmap that we want to light + int start_x=(rdist/lmi_ptr->xspacing)-x_add; + int start_y=(udist/lmi_ptr->yspacing)-y_add; + int end_x=(rdist/lmi_ptr->xspacing)+x_add+.5; + int end_y=(udist/lmi_ptr->yspacing)+y_add+.5; + + if (end_x<0 || end_y<0 || start_x>=(xres) || start_y>=(yres)) + continue; + + if (start_x<0) + start_x=0; + if (start_y<0) + start_y=0; + if (end_x>xres) + end_x=xres; + if (end_y>yres) + end_y=yres; + + int width=end_x-start_x; + int height=end_y-start_y; + + if (width==0) + width++; + if (height==0) + height++; + + ASSERT (width>0); + ASSERT (height>0); + + if (lmi_ptr->dynamic!=BAD_LM_INDEX) // already lit, so just adjust, not start over + { + lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + dest_data=(ushort *)lm_data (lm_handle); + + if (start_x+lmi_ptr->x1x1; + if (start_x+lmi_ptr->x1+width>GameLightmaps[lm_handle].cx2) + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + + if (start_y+lmi_ptr->y1y1; + if (lmi_ptr->y1+start_y+height>GameLightmaps[lm_handle].cy2) + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + + GameLightmaps[lm_handle].flags|=(LF_CHANGED|LF_LIMITS); + + if (!(Lmi_spoken_for[fp->lmi_handle/8] & (1<<(fp->lmi_handle%8)))) + { + lmilist[num_spoken_for]=fp->lmi_handle; + Lmi_spoken_for[fp->lmi_handle/8]|=(1<<(fp->lmi_handle % 8)); + num_spoken_for++; + } + } + else // Start a new dynamic lightmap + { + // First find a suitable dynamic lightmap to work with + int dynamic_handle=GetFreeDynamicLightmap (xres,yres); + + if (dynamic_handle<0) + { + mprintf ((0,"No free dynamic maps!\n")); + DoneLightingInstance(); + + return; // None free! + } + + + // Now copy our source data to our dest data so we have a base to work with + ushort *src_data; + + src_data=(ushort *)lm_data (LightmapInfo[fp->lmi_handle].lm_handle); + dest_data=Dynamic_lightmaps[dynamic_handle].mem_ptr; + + for (int y=0,index=0;yy1+y)*lmw)+lmi_ptr->x1+x]; + } + + } + + dest_data=src_data; + lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + + // Mark it as changed + lmi_ptr->dynamic=dynamic_handle; + + if (!(GameLightmaps[lm_handle].flags & LF_LIMITS)) + { + GameLightmaps[lm_handle].cx1=start_x+lmi_ptr->x1; + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + GameLightmaps[lm_handle].cy1=start_y+lmi_ptr->y1; + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + else + { + if (start_x+lmi_ptr->x1x1; + if (start_x+lmi_ptr->x1+width>GameLightmaps[lm_handle].cx2) + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + + if (start_y+lmi_ptr->y1y1; + if (lmi_ptr->y1+start_y+height>GameLightmaps[lm_handle].cy2) + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + + GameLightmaps[lm_handle].flags|=(LF_LIMITS|LF_CHANGED); + + Dynamic_face_list[Num_dynamic_faces].lmi_handle=fp->lmi_handle; + Num_dynamic_faces++; + + lmilist[num_spoken_for]=fp->lmi_handle; + Lmi_spoken_for[fp->lmi_handle/8]|=(1<<(fp->lmi_handle % 8)); + num_spoken_for++; + + // Setup edge blending + Edges_to_blend[Num_edges_to_blend++]=fp->lmi_handle; + } + + vector element_vec; + + base_vector-=(start_y*(facematrix.uvec*lmi_ptr->yspacing)); + base_vector+=(start_x*(facematrix.rvec*lmi_ptr->xspacing)); + + base_vector-=((facematrix.uvec/2)*lmi_ptr->yspacing); + base_vector+=((facematrix.rvec/2)*lmi_ptr->xspacing); + + // Go through and change each element of this lightmap + // SLOW! + + int texel_num=((start_y+lmi_ptr->y1)*lmw)+start_x+lmi_ptr->x1; + for (int y=0;yyspacing),texel_num+=lmw) + { + element_vec=base_vector; + + for (int x=0;xxspacing)) + { + int lightmap_texel_num=texel_num+x; + + ushort lightmap_texel=dest_data[lightmap_texel_num]; + + if (!(lightmap_texel & OPAQUE_FLAG)) + continue; + + float dist=vm_VectorDistanceQuick (&element_vec,&light_pos); + float scalar=1.0-(dist/light_dist); + + if (Use_light_direction) + { + vector lsubvec=element_vec-light_pos; + vm_NormalizeVectorFast (&lsubvec); + float dp=vm_DotProduct (&lsubvec,&light_dir); + if (dp < dot_range) + { + continue; + } + else + { + + float add_scale=(dp-dot_range)/(1.0-dot_range); + scalar*=add_scale; + } + } + + if (scalar<=0) + continue; + + int r=(lightmap_texel>>10)& 0x1f; + int g=(lightmap_texel>>5) & 0x1f; + int b=lightmap_texel & 0x1f; + + if(red_scale<0) + { + r=max(0,r+(scalar*red_scale*31)); + }else + { + if (rnum_children;i++) + ApplyLightingToSubmodel(obj,pm,&pm->submodel[sm->children[i]], light_dist,red_scale,green_scale,blue_scale,dot_range); + + DoneLightingInstance(); + +} +/* +// Test to see if this wall should show specular lighting from this light source +void ApplySpecularLightingToWall (room *rp,face *fp,vector *pos,float light_dist,float dist_from_plane,float red_scale,float green_scale,float blue_scale) +{ + special_face *sf=&SpecialFaces[fp->special_handle]; + + if (!(sf->flags)) + { + // Reset this face + Specular_face_list[Num_specular_faces++]=fp->special_handle; + sf->flags=1; + sf->strength=0; + } + + float norm=min(1.0,(light_dist/dist_from_plane)*2.0); + float strength=((red_scale*.33)+(green_scale*.33)+(blue_scale*.33))*light_dist; + + if (strength>sf->strength) + { + sf->strength=strength; + sf->lightdist=light_dist; + sf->lightpos=*pos; + sf->color=GR_RGB16(norm*red_scale*255,norm*green_scale*255,norm*blue_scale*255); + } + +}*/ + +// Applys dynamic volumetric lighting to an object +void ApplyVolumeLightToObject (vector *pos,object *obj,float light_dist,float red_scale,float green_scale,float blue_scale,vector *light_direction,float dot_range) +{ + vector subvec=*pos-obj->pos; + float mag=vm_GetMagnitudeFast (&subvec); + + float scalar=mag/light_dist; + if (scalar>1) + scalar=1; + + scalar=1-scalar; + + if (light_direction) + { + // Do a directional light + vector dir_vec=obj->pos-*pos; + float mag=vm_NormalizeVectorFast (&dir_vec); + float dp=dir_vec * *light_direction; + + if (mag>1.0) + { + if (dpeffect_info!=NULL); + + if (!obj->effect_info->dynamic_this_frame) + { + if (Num_volume_objects < MAX_VOLUME_OBJECTS) { + Dynamic_volume_object_list[Num_volume_objects].handle=obj->handle; + Dynamic_volume_object_list[Num_volume_objects++].objnum=obj-Objects; + obj->effect_info->spec_mag=-100000; + + obj->effect_info->dynamic_this_frame=1; + } + } + + // See if this specular light source is greater than our current one + if ((light_dist-mag)>obj->effect_info->spec_mag && Detail_settings.Specular_lighting && !(Object_info[obj->id].lighting_info.flags & OLF_NO_SPECULARITY)) + { + //mprintf ((0,"Setting specular!\n")); + obj->effect_info->type_flags|=EF_SPECULAR; + obj->effect_info->spec_mag=light_dist-mag; + obj->effect_info->spec_pos=*pos; + obj->effect_info->spec_r=red_scale; + obj->effect_info->spec_g=green_scale; + obj->effect_info->spec_b=blue_scale; + } + + if (obj->effect_info->type_flags & EF_VOLUME_LIT) + { + obj->effect_info->dynamic_red=min(1,obj->effect_info->dynamic_red+(scalar*red_scale)); + obj->effect_info->dynamic_green=min(1,obj->effect_info->dynamic_green+(scalar*green_scale)); + obj->effect_info->dynamic_blue=min(1,obj->effect_info->dynamic_blue+(scalar*blue_scale)); + } +} + +// Applies lighting to all objects in a certain distance +void ApplyLightingToObjects(vector *pos,int roomnum,float light_dist,float red_scale,float green_scale,float blue_scale,vector *light_direction,float dot_range) +{ + short objlist[MAX_DYNAMIC_FACES]; + int num_objects,i; + float normalized_time[MAX_SUBOBJECTS]; + + num_objects=fvi_QuickDistObjectList (pos,roomnum,light_dist,objlist,MAX_DYNAMIC_FACES,false,false,true); + + for (i=0;itype==OBJ_ROOM) + { + ApplyLightingToExternalRoom (pos,obj->id,light_dist,red_scale,green_scale,blue_scale,light_direction,dot_range); + continue; + } + + if (obj->renderframe!=((FrameCount-1)% 65536)) + { + if (obj!=Viewer_object) + continue; + } + + if (obj->lm_object.used==0) + { + if (obj->effect_info && ((obj->effect_info->type_flags & EF_VOLUME_LIT) || obj->lighting_render_type==LRT_GOURAUD || obj->type==OBJ_POWERUP) ) + { + ApplyVolumeLightToObject (pos,obj,light_dist,red_scale,green_scale,blue_scale,light_direction,dot_range); + continue; + } + else + continue; + } + + int model_num=obj->rtype.pobj_info.model_num; + poly_model *pm=&Poly_models[model_num]; + + // Set our light position + Light_position=*pos; + + // Set up light direction if this is a directional light + if (light_direction) + { + Light_direction=*light_direction; + Use_light_direction=1; + } + else + Use_light_direction=0; + + StartLightingInstance (&obj->pos,&obj->orient); + + // Now, get the lights position in the objects submodel space + SetNormalizedTimeObj(obj, normalized_time); + SetModelAnglesAndPos (pm,normalized_time); + + for (int t=0;tn_models;t++) + { + bsp_info *sm=&pm->submodel[t]; + if (sm->parent==-1) + ApplyLightingToSubmodel (obj,pm,sm,light_dist,red_scale,green_scale,blue_scale,dot_range); + } + + DoneLightingInstance(); + ASSERT (Light_instance_depth==0); + + } +} + +// Applys dynamic lightmap changes to rooms and room objects. If light direction is non-null, we are applying a directional light +void ApplyLightingToRooms (vector *pos,int roomnum,float light_dist,float red_scale,float green_scale,float blue_scale,vector *light_direction,float dot_range) +{ + fvi_face_room_list facelist[MAX_DYNAMIC_FACES]; + ushort lmilist[MAX_DYNAMIC_FACES]; + int num_spoken_for=0; + + int num_faces,i,t,lm_handle; + ushort *dest_data; + int faces_misreported=0; + + if (Dedicated_server) + return; + + ApplyLightingToObjects (pos,roomnum,light_dist,red_scale,green_scale,blue_scale,light_direction,dot_range); + + num_faces=fvi_QuickDistFaceList (roomnum,pos,light_dist,facelist,MAX_DYNAMIC_FACES); + + #ifdef _DEBUG + if(num_faces == MAX_DYNAMIC_FACES) + { + mprintf((0, "Dynamic light from 1 object is touching %d faces! dist=%f\n", MAX_DYNAMIC_FACES,light_dist)); + } + #endif + + if (num_faces<1) + return; + + int red_limit=31; + int green_limit=31; + int blue_limit=31; + + for (i=0;ifaces[facelist[i].face_index]; + + ASSERT (Rooms[facelist[i].room_index].used); + ASSERT (facelist[i].face_indexrenderframe!=((FrameCount-1)%256)) + continue; + + if (Num_dynamic_faces>=MAX_DYNAMIC_FACES) + { + mprintf ((0,"Too many dynamic faces!\n")); + return; + } + + // Make sure there already is a lightmap for this face + if (!(fp->flags & FF_LIGHTMAP)) + continue; + + if (Lmi_spoken_for[fp->lmi_handle/8] & (1<<(fp->lmi_handle%8))) + continue; + + int xres=lmi_w(fp->lmi_handle); + int yres=lmi_h(fp->lmi_handle); + + lightmap_info *lmi_ptr=&LightmapInfo[fp->lmi_handle]; + // Lightmap width and height + int lmw=lm_w(lmi_ptr->lm_handle); + + // Check for backfaces + vector subvec=*pos-lmi_ptr->upper_left; + float dist_from_plane=fabs(vm_DotProduct(&subvec,&lmi_ptr->normal)); + + if (dist_from_plane>light_dist) + continue; + + if (light_direction) + { + // If this is a directional light, check for backfaces + vector norm_vec=*pos-rp->verts[fp->face_verts[0]]; + + if ((norm_vec * lmi_ptr->normal)<0) + { + // Backface, skip this light + continue; + } + // Test to see if all vertices are behind the light object, if so then bail + int in_front=0; // + for (t=0;tnum_verts && in_front==0;t++) + { + norm_vec=rp->verts[fp->face_verts[t]]-*pos; + if (norm_vec * *light_direction>0) + in_front=1; + } + + if (!in_front) // This face is completely behind, so bail! + continue; + } + + /*// Check for specular lighting changes on this face + if (Detail_settings.Specular_lighting && fp->special_handle!=BAD_SPECIAL_FACE_INDEX) + { + ApplySpecularLightingToWall(rp,fp,pos,light_dist,dist_from_plane,red_scale,green_scale,blue_scale); + }*/ + + // Compute face matrix + matrix facematrix; + vector fvec=-lmi_ptr->normal; + vm_VectorToMatrix(&facematrix,&fvec,NULL,NULL); + + // Get upper left vector of face + vector base_vector=lmi_ptr->upper_left; + + // Get the area of effect for this light + float area_of_effect=(1.0-(dist_from_plane/light_dist))*light_dist; + vector touch_vector=*pos-(lmi_ptr->normal*dist_from_plane); + + subvec=touch_vector-base_vector; + float rdist=vm_DotProduct (&subvec,&facematrix.rvec); + float udist=-vm_DotProduct (&subvec,&facematrix.uvec); + int x_add=1+((area_of_effect+(lmi_ptr->xspacing/2))/lmi_ptr->xspacing); + int y_add=1+((area_of_effect+(lmi_ptr->yspacing/2))/lmi_ptr->yspacing); + + // Get the rectangle of the lightmap that we want to light + int start_x=(rdist/lmi_ptr->xspacing)-x_add; + int start_y=(udist/lmi_ptr->yspacing)-y_add; + int end_x=(rdist/lmi_ptr->xspacing)+x_add+.5; + int end_y=(udist/lmi_ptr->yspacing)+y_add+.5; + + if (end_x<0 || end_y<0 || start_x>=(xres) || start_y>=(yres)) + continue; + + if (start_x<0) + start_x=0; + if (start_y<0) + start_y=0; + if (end_x>xres) + end_x=xres; + if (end_y>yres) + end_y=yres; + + int width=end_x-start_x; + int height=end_y-start_y; + + if (width==0) + width++; + if (height==0) + height++; + + ASSERT (width>0); + ASSERT (height>0); + + if (lmi_ptr->dynamic!=BAD_LM_INDEX) // already lit, so just adjust, not start over + { + lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + dest_data=(ushort *)lm_data (lm_handle); + + if (start_x+lmi_ptr->x1x1; + if (start_x+lmi_ptr->x1+width>GameLightmaps[lm_handle].cx2) + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + + if (start_y+lmi_ptr->y1y1; + if (lmi_ptr->y1+start_y+height>GameLightmaps[lm_handle].cy2) + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + + GameLightmaps[lm_handle].flags|=(LF_CHANGED|LF_LIMITS); + + if (!(Lmi_spoken_for[fp->lmi_handle/8] & (1<<(fp->lmi_handle%8)))) + { + lmilist[num_spoken_for]=fp->lmi_handle; + Lmi_spoken_for[fp->lmi_handle/8]|=(1<<(fp->lmi_handle % 8)); + num_spoken_for++; + } + } + else // Start a new dynamic lightmap + { + // First find a suitable dynamic lightmap to work with + int dynamic_handle=GetFreeDynamicLightmap (xres,yres); + + if (dynamic_handle<0) + { + mprintf ((0,"No free dynamic maps!\n")); + return; // None free! + } + + + // Now copy our source data to our dest data so we have a base to work with + ushort *src_data; + + src_data=(ushort *)lm_data (LightmapInfo[fp->lmi_handle].lm_handle); + dest_data=Dynamic_lightmaps[dynamic_handle].mem_ptr; + + for (int y=0,index=0;yy1+y)*lmw)+lmi_ptr->x1+x]; + } + + } + + dest_data=src_data; + lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + + // Mark it as changed + lmi_ptr->dynamic=dynamic_handle; + + if (!(GameLightmaps[lm_handle].flags & LF_LIMITS)) + { + GameLightmaps[lm_handle].cx1=start_x+lmi_ptr->x1; + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + GameLightmaps[lm_handle].cy1=start_y+lmi_ptr->y1; + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + else + { + if (start_x+lmi_ptr->x1x1; + if (start_x+lmi_ptr->x1+width>GameLightmaps[lm_handle].cx2) + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + + if (start_y+lmi_ptr->y1y1; + if (lmi_ptr->y1+start_y+height>GameLightmaps[lm_handle].cy2) + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + + GameLightmaps[lm_handle].flags|=(LF_LIMITS|LF_CHANGED); + + Dynamic_face_list[Num_dynamic_faces].lmi_handle=fp->lmi_handle; + Num_dynamic_faces++; + + + + lmilist[num_spoken_for]=fp->lmi_handle; + Lmi_spoken_for[fp->lmi_handle/8]|=(1<<(fp->lmi_handle % 8)); + num_spoken_for++; + + // Setup edge blending + Edges_to_blend[Num_edges_to_blend++]=fp->lmi_handle; + } + + vector element_vec; + + base_vector-=(start_y*(facematrix.uvec*lmi_ptr->yspacing)); + base_vector+=(start_x*(facematrix.rvec*lmi_ptr->xspacing)); + + base_vector-=((facematrix.uvec/2)*lmi_ptr->yspacing); + base_vector+=((facematrix.rvec/2)*lmi_ptr->xspacing); + + // Go through and change each element of this lightmap + // SLOW! + + int texel_num=((start_y+lmi_ptr->y1)*lmw)+start_x+lmi_ptr->x1; + for (int y=0;yyspacing),texel_num+=lmw) + { + element_vec=base_vector; + + for (int x=0;xxspacing)) + { + int lightmap_texel_num=texel_num+x; + + ushort lightmap_texel=dest_data[lightmap_texel_num]; + + if (!(lightmap_texel & OPAQUE_FLAG)) + continue; + + float dist=vm_VectorDistanceQuick (&element_vec,pos); + float scalar=1.0-(dist/light_dist); + + if (light_direction) + { + vector lsubvec=element_vec-*pos; + vm_NormalizeVectorFast (&lsubvec); + float dp=vm_DotProduct (&lsubvec,light_direction); + if (dp < dot_range) + { + continue; + } + else + { + + float add_scale=(dp-dot_range)/(1.0-dot_range); + scalar*=add_scale; + } + } + + if (scalar<=0) + continue; + + int r=(lightmap_texel>>10)& 0x1f; + int g=(lightmap_texel>>5) & 0x1f; + int b=lightmap_texel & 0x1f; + + if(red_scale < 0) + { + // we are subtracting light + r = max(0,r+(scalar*red_scale*31)); + }else + { + // we are adding light + if (rtype==OBJ_NONE || obj->handle!=Dynamic_volume_object_list[i].handle) + continue; // object was destroyed this frame + + obj->effect_info->type_flags&=~EF_SPECULAR; + obj->effect_info->dynamic_this_frame=0; + obj->effect_info->dynamic_red=0; + obj->effect_info->dynamic_green=0; + obj->effect_info->dynamic_blue=0; + } + + Num_volume_objects=0; + + // Reset specular faces + for (i=0;iy1*lmw); + + for (int y=0;yheight;y++,dest_data+=lmw) + { + for (int x=0;xwidth;x++) + { + dest_data[lmi_ptr->x1+x]=src_data[y*lmi_ptr->width+x]; + } + } + + GameLightmaps[lm_handle].flags |=(LF_LIMITS|LF_CHANGED); + LightmapInfo[lmi_handle].dynamic=BAD_LMI_INDEX; + } + + + Num_dynamic_faces=0; + + // Now do terrain + + for (i=0;irenderframe!=((FrameCount-1)%256)) + // continue; + + if (Num_dynamic_cells>=MAX_DYNAMIC_CELLS) + { + mprintf ((0,"Too many dynamic cells!\n")); + return; + } + + // Check for backfaces + vector tpos; + + tpos.x=(seg_x*TERRAIN_SIZE)+(TERRAIN_SIZE/2); + tpos.z=(seg_z*TERRAIN_SIZE)+(TERRAIN_SIZE/2); + tpos.y=tseg->y; + + vector subvec=*pos-tpos; + + float dist=vm_GetMagnitudeFast (&subvec); + float scalar=1.0-(dist/light_dist); + + if (light_direction) + { + vector lsubvec=-subvec; + lsubvec/=dist; + float dp=vm_DotProduct (&lsubvec,light_direction); + if (dp < dot_range) + { + continue; + } + else + { + float add_scale=(dp-dot_range)/(1.0-dot_range); + scalar*=add_scale; + } + } + + + if (scalar<=0) + continue; + + + // Add a new face to our list + if (!(tseg->flags & TF_DYNAMIC)) + { + Dynamic_cell_list[Num_dynamic_cells].cellnum=cellnum; + Dynamic_cell_list[Num_dynamic_cells].r=tseg->r; + Dynamic_cell_list[Num_dynamic_cells].g=tseg->g; + Dynamic_cell_list[Num_dynamic_cells].b=tseg->b; + tseg->flags|=TF_DYNAMIC; + Num_dynamic_cells++; + + if (GameLightmaps[whichmap].flags & LF_LIMITS) + { + if (subxGameLightmaps[whichmap].cx2) + GameLightmaps[whichmap].cx2=subx; + + if (subzGameLightmaps[whichmap].cy2) + GameLightmaps[whichmap].cy2=subz; + + } + else + { + GameLightmaps[whichmap].cx1=subx; + GameLightmaps[whichmap].cx2=subx+1; + GameLightmaps[whichmap].cy1=subz; + GameLightmaps[whichmap].cy2=subz+1; + GameLightmaps[whichmap].flags|=(LF_LIMITS|LF_CHANGED); + } + + } + + int r=tseg->r; + int g=tseg->g; + int b=tseg->b; + + if(red_scale<0) + { + // we are subtracting light + tseg->r=max(0,r+(scalar*red_scale*255)); + }else + { + if (rr=min(red_limit,r+(scalar*red_scale*255)); + } + + if(green_scale<0) + { + // we are subtracting light + tseg->g=max(0,g+(scalar*green_scale*255)); + }else + { + if (gg=min(green_limit,g+(scalar*green_scale*255)); + } + + if(blue_scale<0) + { + // we are subtracting light + tseg->b = max(0,b+(scalar*blue_scale*255)); + }else + { + if (bb=min(blue_limit,b+(scalar*blue_scale*255)); + } + + ushort color=OPAQUE_FLAG | GR_RGB16(tseg->r,tseg->g,tseg->b); + ushort *data=lm_data (whichmap); + + ASSERT(data); + + data[subz*128+subx]=color; + } +} + + +// Sets pulse parameters for an entire room +void SetRoomPulse (room *rp,ubyte pulse_time,ubyte pulse_offset) +{ + ASSERT (rp->used); + + rp->pulse_time=pulse_time; + rp->pulse_offset=pulse_offset; + +} + +// Returns the total number of bytes needed for volume lighting in this room +int GetVolumeSizeOfRoom (room *rp,int *w,int *h,int *d) +{ + int width=((rp->max_xyz.x-rp->min_xyz.x)/VOLUME_SPACING)+1; + int height=((rp->max_xyz.y-rp->min_xyz.y)/VOLUME_SPACING)+1; + int depth=((rp->max_xyz.z-rp->min_xyz.z)/VOLUME_SPACING)+1; + + if (w) + *w=width; + if (h) + *h=height; + if (d) + *d=depth; + + + return (width*height*depth); + +} + +/* +// Returns a lightmap that can be applied for specular lighting +int GetSpecularLightmapForFace (vector *pos,room *rp,face *fp) +{ + int xres=lmi_w(fp->lmi_handle); + int yres=lmi_h(fp->lmi_handle); + lightmap_info *lmi_ptr=&LightmapInfo[fp->lmi_handle]; + + matrix facematrix; + vector fvec=-lmi_ptr->normal; + vm_VectorToMatrix(&facematrix,&fvec,NULL,NULL); + + // Get upper left vector of face + vector base_vector=lmi_ptr->upper_left; + + int square_res=GameLightmaps[lmi_ptr->lm_handle].square_res; + + //if (square_res>=128) + //return -1; + + //square_res*=2; + //xres*=2; + //yres*=2; + float xspacing=(float)lmi_ptr->xspacing; + float yspacing=(float)lmi_ptr->yspacing; + + int cl=GetLightmapClass (square_res); + int lm_handle; + + ushort *dest_data; + ushort *src_data=(ushort *)lm_data(lmi_ptr->lm_handle); + + if (lmi_ptr->spec_map==-1) + { + // Get a new specular map and clear it + int dynamic_handle=FindFreeDynamicLightmap (cl); + + if (dynamic_handle<0) + { + mprintf ((0,"No free specular maps for class %d!\n",cl)); + return-1; // None free! + } + + Dynamic_lightmaps[cl][dynamic_handle].used=1; + int dynamic_lmi_handle=Dynamic_lightmaps[cl][dynamic_handle].lmi_handle; + lm_handle=LightmapInfo[dynamic_lmi_handle].lm_handle; + + lmi_ptr->spec_map=lm_handle; + dest_data=(ushort *)lm_data(lm_handle); + + memset (dest_data,0,xres*yres*2); + + Specular_face_list[Num_specular_faces]=fp->lmi_handle; + Num_specular_faces++; + } + else + { + return 1;// already done for this face + } + + GameLightmaps[lm_handle].flags|=LF_CHANGED; + + vector element_vec; + + // Choose material + int material_type=0; + + if (GameTextures[fp->tmap].flags & TF_PLASTIC) + material_type=1; + else if (GameTextures[fp->tmap].flags & TF_MARBLE) + material_type=2; + + // Go through and change each element of this lightmap + // SLOW! + + int texel_num=0; + + int num=SpecialFaces[fp->special_handle].num; + + for (int y=0;y>10 & 0x1f)]; + gscale=Light_component_scalar [(lightmap_texel>>5 & 0x1f)]; + bscale=Light_component_scalar [(lightmap_texel & 0x1f)]; + + for (int i=0;ispecial_handle].spec_instance[i].bright_color==0) + continue; + + lightmap_texel=dest_data[lightmap_texel_num]; + + int old_r=lightmap_texel>>10 & 0x1f; + int old_g=lightmap_texel>>5 & 0x1f; + int old_b=lightmap_texel & 0x1f; + + vector subvec=*pos-element_vec; + vm_NormalizeVectorFast (&subvec); + + vector incident_norm=element_vec-SpecialFaces[fp->special_handle].spec_instance[i].bright_center; + vm_NormalizeVectorFast (&incident_norm); + + float d=incident_norm * fp->normal; + vector upvec=d * fp->normal; + incident_norm-=(2*upvec); + + float dotp=subvec * incident_norm; + + if (dotp<0) + continue; + if (dotp>1) + dotp=1; + + if (dotp>0) + { + int index=((float)(MAX_SPECULAR_INCREMENTS-1)*dotp); + float val=Specular_tables[material_type][index]; + + ushort color=SpecialFaces[fp->special_handle].spec_instance[i].bright_color; + int r=color >> 10 & 0x1f; + int g=color >> 5 & 0x1f; + int b=color & 0x1f; + + r*=val*rscale; + g*=val*gscale; + b*=val*bscale; + + r=min(31,old_r+r); + g=min(31,old_g+g); + b=min(31,old_b+b); + + + dest_data[lightmap_texel_num]=OPAQUE_FLAG|(r<<10)|(g<<5)|(b); + } + } + } + } + + return lm_handle; +}*/ + +/* +// Returns a lightmap that can be applied for specular lighting +int GetSpecularLightmapForFace (vector *pos,room *rp,face *fp) +{ + + int xres=lmi_w(fp->lmi_handle); + int yres=lmi_h(fp->lmi_handle); + lightmap_info *lmi_ptr=&LightmapInfo[fp->lmi_handle]; + vector center=SpecialFaces[fp->special_handle].center; + + matrix facematrix; + vector fvec=-lmi_ptr->normal; + vm_VectorToMatrix(&facematrix,&fvec,NULL,NULL); + + // Get upper left vector of face + vector base_vector=lmi_ptr->upper_left; + + int start_x=fp->x1; + int start_y=fp->y1; + int end_x=fp->x1+fp->lw; + int end_y=fp->y1+fp->lh; + + int width=end_x-start_x; + int height=end_y-start_y; + + if (width==0) + width++; + if (height==0) + height++; + + ASSERT (width>0); + ASSERT (height>0); + + int square_res=GameLightmaps[lmi_ptr->lm_handle].square_res; + int cl=GetLightmapClass (square_res); + int lm_handle; + + ushort *dest_data; + ushort *src_data=(ushort *)lm_data(lmi_ptr->lm_handle); + + if (lmi_ptr->spec_map==-1) + { + // Get a new specular map and clear it + int dynamic_handle=FindFreeDynamicLightmap (cl); + + if (dynamic_handle<0) + { + mprintf ((0,"No free specular maps for class %d!\n",cl)); + return-1; // None free! + } + + Dynamic_lightmaps[cl][dynamic_handle].used=1; + int dynamic_lmi_handle=Dynamic_lightmaps[cl][dynamic_handle].lmi_handle; + lm_handle=LightmapInfo[dynamic_lmi_handle].lm_handle; + + lmi_ptr->spec_map=lm_handle; + dest_data=(ushort *)lm_data(lm_handle); + + memset (dest_data,0,xres*yres*2); + + Specular_face_list[Num_specular_faces]=fp->lmi_handle; + Num_specular_faces++; + } + else + { + lm_handle=lmi_ptr->spec_map; + dest_data=(ushort *)lm_data(lm_handle); + } + + + GameLightmaps[lm_handle].flags|=LF_CHANGED; + + // Rotate the base_vector, the eyepoint, face normal and the lightsource into the Z plane + vector tempvec,eye_pos,light_pos[4]; + vector norm; + + base_vector-=center; + + vm_MatrixMulVector (&tempvec,&base_vector,&facematrix); + base_vector=tempvec; + + tempvec=*pos-center; + vm_MatrixMulVector (&eye_pos,&tempvec,&facematrix); + + for (int i=0;i<4;i++) + { + if (SpecialFaces[fp->special_handle].spec_instance[i].bright_color!=0) + { + tempvec=SpecialFaces[fp->special_handle].spec_instance[i].bright_center-center; + vm_MatrixMulVector (&light_pos[i],&tempvec,&facematrix); + } + } + + vm_MatrixMulVector (&norm,&fp->normal,&facematrix); + + // Move to offset + vector element_vec; + + base_vector.y-=(start_y*lmi_ptr->yspacing); + base_vector.x+=(start_x*lmi_ptr->xspacing); + + // Move to center of texel + base_vector.y-=(lmi_ptr->yspacing/2); + base_vector.x+=(lmi_ptr->xspacing/2); + + // Choose material + int material_type=0; + + if (GameTextures[fp->tmap].flags & TF_PLASTIC) + material_type=1; + else if (GameTextures[fp->tmap].flags & TF_MARBLE) + material_type=2; + + // Go through and change each element of this lightmap + // SLOW! + + int texel_num=start_y*xres+start_x; + int num=SpecialFaces[fp->special_handle].num; + + for (int y=0;yyspacing,texel_num+=xres) + { + element_vec=base_vector; + + for (int x=0;xxspacing) + { + int lightmap_texel_num=texel_num+x; + + if (SpecialFaces[fp->special_handle].normal_map[lightmap_texel_num*3+2]==128) + continue; + else + { + norm.x=Normal_table[SpecialFaces[fp->special_handle].normal_map[lightmap_texel_num*3+0]]; + norm.y=Normal_table[SpecialFaces[fp->special_handle].normal_map[lightmap_texel_num*3+1]]; + norm.z=Normal_table[SpecialFaces[fp->special_handle].normal_map[lightmap_texel_num*3+2]]; + } + + ushort lightmap_texel=src_data[lightmap_texel_num]; + + if (! (lightmap_texel & OPAQUE_FLAG)) + continue; + + float base_r=(lightmap_texel>>10 & 0x1f)>15?1:0; + float base_g=(lightmap_texel>>5 & 0x1f)>15?1:0; + float base_b=(lightmap_texel & 0x1f)>15?1:0; + + + for (int i=0;ispecial_handle].spec_instance[i].bright_color==0) + continue; + + lightmap_texel=dest_data[lightmap_texel_num]; + + int old_r=lightmap_texel>>10 & 0x1f; + int old_g=lightmap_texel>>5 & 0x1f; + int old_b=lightmap_texel & 0x1f; + + vector subvec=eye_pos-element_vec; + vm_NormalizeVectorFast (&subvec); + + vector incident_norm=element_vec-light_pos[i]; + vm_NormalizeVectorFast (&incident_norm); + + float d=incident_norm * norm; + vector upvec=d * norm; + incident_norm-=(2*upvec); + + float dotp=subvec * incident_norm; + +// if (rp-Rooms==0 && fp-rp->faces==5 && (x==width/2) && (y==height/2)) +// mprintf ((0,"dotp=%f\n",dotp)); + + + if (dotp<0) + continue; + if (dotp>1) + dotp=1; + + if (dotp>0) + { + int index=((float)(MAX_SPECULAR_INCREMENTS-1)*dotp); + float val=Specular_tables[material_type][index]; + + ushort color=SpecialFaces[fp->special_handle].spec_instance[i].bright_color; + int r=color >> 10 & 0x1f; + int g=color >> 5 & 0x1f; + int b=color & 0x1f; + + r*=val*base_r; + g*=val*base_g; + b*=val*base_b; + + r=min(31,old_r+r); + g=min(31,old_g+g); + b=min(31,old_b+b); + + + dest_data[lightmap_texel_num]=OPAQUE_FLAG|(r<<10)|(g<<5)|(b); + } + } + } + } + + return lm_handle; +}*/ + +#define DESTROYABLE_IGNORE_LIMIT .2f + +// Kills the lighting that a face casts and dampens all the faces that light influences +void DestroyLight (int roomnum,int facenum) +{ + vector vecs[MAX_VERTS_PER_FACE],center; + room *destroy_rp=&Rooms[roomnum]; + face *destroy_fp=&destroy_rp->faces[facenum]; + float r,g,b; + fvi_face_room_list facelist[MAX_DYNAMIC_FACES]; + int num_faces,i; + ushort lmilist[MAX_DYNAMIC_FACES]; + int num_spoken_for=0; + + if (Dedicated_server) + return; + + float mul=((float)destroy_fp->light_multiple)/4.0; + + r=GameTextures[destroy_fp->tmap].r*mul; + g=GameTextures[destroy_fp->tmap].g*mul; + b=GameTextures[destroy_fp->tmap].b*mul; + + // Get highest component + float rmax=max(r,g); + rmax=max(rmax,b); + + // Get the normalized color that this face emits + float red_scale=r/rmax; + float green_scale=g/rmax; + float blue_scale=b/rmax; + + //red_scale/=30; + //green_scale/=30; + //blue_scale/=30; + + mprintf ((0,"r=%f g=%f b=%f\n",red_scale,green_scale,blue_scale)); + + // Get center and area of light face + for (i=0;inum_verts;i++) + vecs[i]=destroy_rp->verts[destroy_fp->face_verts[i]]; + + float area=vm_GetCentroid (¢er,vecs,destroy_fp->num_verts); + + // Get the sphere of influence of this light + float power=rmax*area; + float temp_ignore=DESTROYABLE_IGNORE_LIMIT/power; + float express=1.0/temp_ignore; + express/=3.14f; + express-=area; + float sphere_dist=sqrt (express); + +// ApplyLightingToObjects (pos,roomnum,light_dist,red_scale,green_scale,blue_scale,light_direction,dot_range); + + num_faces=fvi_QuickDistFaceList (roomnum,¢er,sphere_dist,facelist,MAX_DYNAMIC_FACES); + + for (i=0;ifaces[facelist[i].face_index]; + + ASSERT (Rooms[facelist[i].room_index].used); + ASSERT (facelist[i].face_indexflags & FF_LIGHTMAP)) + continue; + + if (GameTextures[fp->tmap].flags & TF_LIGHT) + continue; + + if (Lmi_spoken_for[fp->lmi_handle/8] & (1<<(fp->lmi_handle%8))) + continue; + + int xres=lmi_w(fp->lmi_handle); + int yres=lmi_h(fp->lmi_handle); + + lightmap_info *lmi_ptr=&LightmapInfo[fp->lmi_handle]; + + int lmw=lm_w(lmi_ptr->lm_handle); + + + + vector subvec=center-lmi_ptr->upper_left; + float dist_from_plane=vm_DotProduct(&subvec,&lmi_ptr->normal); + + // Check for backfaces + if (dist_from_plane<0) + continue; + + dist_from_plane=fabs (dist_from_plane); + + if (dist_from_plane>sphere_dist) + continue; + + // Compute face matrix + matrix facematrix; + vector fvec=-lmi_ptr->normal; + vm_VectorToMatrix(&facematrix,&fvec,NULL,NULL); + + // Get upper left vector of face + vector base_vector=lmi_ptr->upper_left; + + // Get the area of effect for this light + float area_of_effect=(1.0-(dist_from_plane/sphere_dist))*sphere_dist; + vector touch_vector=center-(lmi_ptr->normal*dist_from_plane); + + subvec=touch_vector-base_vector; + float rdist=vm_DotProduct (&subvec,&facematrix.rvec); + float udist=-vm_DotProduct (&subvec,&facematrix.uvec); + int x_add=1+((area_of_effect+(lmi_ptr->xspacing/2))/lmi_ptr->xspacing); + int y_add=1+((area_of_effect+(lmi_ptr->yspacing/2))/lmi_ptr->yspacing); + + // Get the rectangle of the lightmap that we want to light + int start_x=(rdist/lmi_ptr->xspacing)-x_add; + int start_y=(udist/lmi_ptr->yspacing)-y_add; + int end_x=(rdist/lmi_ptr->xspacing)+x_add+.5; + int end_y=(udist/lmi_ptr->yspacing)+y_add+.5; + + if (end_x<0 || end_y<0 || start_x>=(xres) || start_y>=(yres)) + continue; + + if (start_x<0) + start_x=0; + if (start_y<0) + start_y=0; + if (end_x>xres) + end_x=xres; + if (end_y>yres) + end_y=yres; + + end_x++; + end_y++; + + if (end_x>xres) + end_x=xres; + if (end_y>yres) + end_y=yres; + + int width=end_x-start_x; + int height=end_y-start_y; + + if (width==0) + width++; + if (height==0) + height++; + + ASSERT (width>0); + ASSERT (height>0); + + // Now copy our source data to our dest data so we have a base to work with + ushort *dest_data; + + dest_data=(ushort *)lm_data (LightmapInfo[fp->lmi_handle].lm_handle); + int lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + + if (!(GameLightmaps[lm_handle].flags & LF_LIMITS)) + { + GameLightmaps[lm_handle].cx1=start_x+lmi_ptr->x1; + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + GameLightmaps[lm_handle].cy1=start_y+lmi_ptr->y1; + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + else + { + if (start_x+lmi_ptr->x1x1; + if (start_x+lmi_ptr->x1+width>GameLightmaps[lm_handle].cx2) + GameLightmaps[lm_handle].cx2=start_x+width+lmi_ptr->x1; + + if (start_y+lmi_ptr->y1y1; + if (lmi_ptr->y1+start_y+height>GameLightmaps[lm_handle].cy2) + GameLightmaps[lm_handle].cy2=start_y+height+lmi_ptr->y1; + } + + + GameLightmaps[lm_handle].flags|=(LF_LIMITS|LF_CHANGED); + + lmilist[num_spoken_for]=fp->lmi_handle; + Lmi_spoken_for[fp->lmi_handle/8]|=(1<<(fp->lmi_handle % 8)); + num_spoken_for++; + + vector element_vec; + + base_vector-=(start_y*(facematrix.uvec*lmi_ptr->yspacing)); + base_vector+=(start_x*(facematrix.rvec*lmi_ptr->xspacing)); + + base_vector-=((facematrix.uvec/2)*lmi_ptr->yspacing); + base_vector+=((facematrix.rvec/2)*lmi_ptr->xspacing); + + // Go through and change each element of this lightmap + // SLOW! + + int texel_num=((start_y+lmi_ptr->y1)*lmw)+start_x+lmi_ptr->x1; + for (int y=0;yyspacing),texel_num+=lmw) + { + element_vec=base_vector; + + for (int x=0;xxspacing)) + { + int lightmap_texel_num=texel_num+x; + + ushort lightmap_texel=dest_data[lightmap_texel_num]; + + if (!(lightmap_texel & OPAQUE_FLAG)) + continue; + + float dist=vm_VectorDistanceQuick (&element_vec,¢er); + float scalar=1.0-(dist/sphere_dist); + + if (scalar<=0) + continue; + + int r=(lightmap_texel>>10)& 0x1f; + int g=(lightmap_texel>>5) & 0x1f; + int b=lightmap_texel & 0x1f; + + if (r>0) + r=max(0,r-(scalar*red_scale*31)); + + if (g>0) + g=max(0,g-(scalar*green_scale*31)); + + if (b>0) + b=max(0,b-(scalar*blue_scale*31)); + + lightmap_texel=OPAQUE_FLAG|(r<<10)|(g<<5)|b; + + dest_data[lightmap_texel_num]=lightmap_texel; + } + } + + BlendLightingEdges(lmi_ptr); + } + + for (i=0;i=MAX_DESTROYED_LIGHTS_PER_FRAME) + { + mprintf ((0,"Ran out of destroyable light slots!\n")); + return; + } + + mprintf ((0,"Destroying light. Room=%d face=%d\n",roomnum,facenum)); + + Destroyed_light_rooms_this_frame[Num_destroyed_lights_this_frame]=roomnum; + Destroyed_light_faces_this_frame[Num_destroyed_lights_this_frame++]=facenum; + +} + +// Goes through our destroyable light list and actually kills all the lights +void DoDestroyedLightsForFrame () +{ + int i; + for (i=0;i +#include +#include "mem.h" +#include "dedicated_server.h" + +int Num_of_lightmap_info=0; +lightmap_info *LightmapInfo = NULL; + +static ushort *Free_lmi_list = NULL; + +void CloseLightmapInfos () +{ + bool final_lightmap = true; + + if(LightmapInfo) + mem_free (LightmapInfo); + + if(Free_lmi_list) + mem_free (Free_lmi_list); + + LightmapInfo = NULL; + Free_lmi_list = NULL; +} + +// Sets all the lightmaps to unused +void InitLightmapInfo(int nummaps) +{ + int i; + + if (Dedicated_server) + return; + + if(nummaps == 0) { + LightmapInfo=(lightmap_info *)mem_malloc (MAX_LIGHTMAP_INFOS*sizeof(lightmap_info)); + ASSERT (LightmapInfo); + Free_lmi_list=(ushort *)mem_malloc (MAX_LIGHTMAP_INFOS*sizeof(ushort)); + ASSERT (Free_lmi_list); + + + for (i=0;i MAX_LIGHTMAP_INFOS) + Int3(); // Get Jason, ran out of lightmaps! + + n = Free_lmi_list[Num_of_lightmap_info++]; + ASSERT (LightmapInfo[n].used==0); + + ASSERT (n>=0 && n=2 && h>=2); + + if (alloc_lightmap) + { + LightmapInfo[n].lm_handle=lm_AllocLightmap(w,h); + ASSERT (LightmapInfo[n].lm_handle!=BAD_LM_INDEX); // Make sure we have a valid lightmap + + ushort *dest_data=lm_data(LightmapInfo[n].lm_handle); + + // Set the lightmap to be transparent + for (i=0;i=0 && handle<=MAX_LIGHTMAP_INFOS); + + if (Dedicated_server) + return; + + //when we free up our rooms, which frees up lightmaps, the lightmap array + //may already be freed + if (!LightmapInfo) + return; + + if (LightmapInfo[handle].used<1) + return; + + LightmapInfo[handle].used--; + + if (LightmapInfo[handle].used==0) + { + if (!Dedicated_server) + rend_FreePreUploadedTexture (LightmapInfo[handle].lm_handle,MAP_TYPE_LIGHTMAP); + lm_FreeLightmap (LightmapInfo[handle].lm_handle); + + Free_lmi_list[--Num_of_lightmap_info] = handle; + } +} + +// Gets the width of this lightmap_info handle +int lmi_w(int handle) +{ + if (!LightmapInfo[handle].used) + { + Int3(); // Get Jason + return 0; + } + + return (LightmapInfo[handle].width); +} + +// Gets the height of this lightmap_info handle +int lmi_h(int handle) +{ + if (!LightmapInfo[handle].used) + { + Int3(); // Get Jason + return 0; + } + + return (LightmapInfo[handle].height); +} + + +// Softens the edges of lightmaps so there are fewer artifaces +void ShadeLightmapInfoEdges (int type) +{ + int i; + + for (i=0;i0) + { + r/=num; + g/=num; + b/=num; + color16=GR_RGB16(r,g,b); + dest_data[y*w+x]=OPAQUE_FLAG|color16; + } + } + } + } + mem_free (src_data); + } + } +} + +// Blurs the lightmaps so a dithering pattern is less noticeable +void BlurLightmapInfos (int type) +{ + int i; + + for (i=0;i0) + { + r/=num; + g/=num; + b/=num; + color16=GR_RGB16(r,g,b); + dest_data[y*w+x]=OPAQUE_FLAG|color16; + } + } + } + } + mem_free (src_data); + } + } +} + diff --git a/Descent3/lightmap_info.h b/Descent3/lightmap_info.h new file mode 100644 index 000000000..074b240d8 --- /dev/null +++ b/Descent3/lightmap_info.h @@ -0,0 +1,85 @@ +/* + * $Logfile: /DescentIII/Main/lightmap_info.h $ + * $Revision: 25 $ + * $Date: 3/20/00 12:12p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/Main/lightmap_info.h $ + * + * 25 3/20/00 12:12p Matt + * Merge of Duane's post-1.3 changes. + * Lower MAX_LIGHT_INFOS for Mac (Mac only) + * + * 24 10/21/99 6:42p Matt + * Mac merge + * + */ + +#ifndef LIGHTMAP_INFO_H +#define LIGHTMAP_INFO_H +// Lightmap info header + +#include "pstypes.h" +#include "pserror.h" +#include "vecmat.h" + +#define BAD_LMI_INDEX 65535 + +// What this lightmap is used for: +#define LMI_ROOM 0 +#define LMI_ROOM_OBJECT 1 +#define LMI_TERRAIN 2 +#define LMI_TERRAIN_OBJECT 3 +#define LMI_DYNAMIC 4 +#define LMI_EXTERNAL_ROOM 5 +#define LMI_EXTERNAL_ROOM_OBJECT 6 + +typedef struct +{ + ubyte xspacing,yspacing; + ushort lm_handle; + vector upper_left,normal; + ubyte width,height,x1,y1; + ubyte used; + + + ushort dynamic; + short spec_map; + + ubyte type; // see LMI_types above +} lightmap_info; + +extern lightmap_info *LightmapInfo; +extern int Num_of_lightmap_info; +extern int Num_lightmap_infos_read; + +#ifdef MACINTOSH +#define MAX_LIGHTMAP_INFOS (60000) +#else +#define MAX_LIGHTMAP_INFOS (65534) +#endif + +// Sets all the lightmaps to unused +void InitLightmapInfo(int nummaps = 0); + +// Allocs a lightmap of w x h size +// Returns lightmap handle if successful, -1 if otherwise +int AllocLightmapInfo (int w,int h,int type,bool alloc_lightmap=true); + +// Given a handle, frees the lightmap memory and flags this lightmap as unused +void FreeLightmapInfo (int handle); + +// Gets the width of this lightmap_info handle +int lmi_w(int handle); + +// Gets the height of this lightmap_info handle +int lmi_h(int handle); + +// Softens the edges of lightmaps so there are fewer artifaces +void ShadeLightmapInfoEdges (int type); +void BlurLightmapInfos (int type); + +#endif + diff --git a/Descent3/list.cpp b/Descent3/list.cpp new file mode 100644 index 000000000..27d592960 --- /dev/null +++ b/Descent3/list.cpp @@ -0,0 +1,169 @@ +#include "list.h" +//#include +#include "mem.h" + + +// Allocates space for a new list node, returning the pointer to it +listnode *NewListNode(void) +{ + listnode *node; + + node = (listnode *) mem_malloc(sizeof(listnode)); + if(node == NULL) + { + mprintf ((0,"Not enough memory for a new listnode!\n")); + Int3(); + return NULL; + } + + + node->data = NULL; + node->next = NULL; + node->prev = NULL; + + return node; +} + + +// Adds an item to a list +// Returns 1 if everything is ok, else 0 +int AddListItem(list **listp, void *item) +{ + listnode *newnode, *curr; + + newnode = NewListNode(); + + if(newnode == NULL) + { + mprintf ((0,"There was a problem mallocing list node memory!\n")); + Int3(); + return 0; + } + + + if(*listp == NULL) // If this list is empty, construct a new head + { + newnode->data = item; + *listp = newnode; + return 1; + } + else + { + // Go through until the end of the list and add the new item there + for(curr=*listp; curr->next!=NULL; curr=curr->next) + { + if(curr->data == item) + { + Int3(); + return 0; + } + } + + newnode->data = item; + curr->next = newnode; + newnode->next = NULL; + newnode->prev = curr; + } + + return 1; +} + +// Removes an item from a list +int RemoveListItem(list **listp, void *item) +{ + listnode *curr, *node; + int inlist = 0; + + for(curr=*listp; curr!=NULL; curr=curr->next) + { + if(curr->data == item) + { + inlist = 1; + node = curr; + break; + } + } + + if(!inlist) + { + mprintf((0,"RemoveItem: Warning, item not found in list\n")); + return 0; + } + + // Head of list is a special case + if(node == *listp) + { + *listp = node->next; + if(*listp != NULL) + (*listp)->prev = NULL; + mem_free(node); + return 1; + } + else + { + if(node->prev != NULL) + node->prev->next = node->next; + if(node->next != NULL) + node->next->prev = node->prev; + mem_free(node); + } + return 1; +} + + +// Destroys all the nodes in a list +// The items must be freed in another routine +void DestroyList(list **listp) +{ + listnode *node, *next; + + for(node=*listp; node!=NULL; node=next) + { + next = node->next; + mem_free(node); + } +} + +// Returns the number of items in a list +int CountListItems(list **listp) +{ + listnode *node; + int count = 0; + + for(node=*listp; node!=NULL; node=node->next) + count++; + + return count; +} + +// returns a pointer the given item index in a list +// Returns NULL if point not found +void *GetListItem(list **listp, int index) +{ + listnode *node; + + for(node=*listp; node!=NULL; node=node->next) + { + if(index-- == 0) + return node->data; + } + + return NULL; +} + +// Returns how far from the head of the list a given item is +// Returns -1 if not found +int GetListItemIndex(list **listp, void *item) +{ + listnode *node; + int index = 0; + + for(node=*listp; node!=NULL; node=node->next) + { + if(node->data == item) + return index; + index++; + } + + return -1; +} diff --git a/Descent3/list.h b/Descent3/list.h new file mode 100644 index 000000000..d1047074d --- /dev/null +++ b/Descent3/list.h @@ -0,0 +1,39 @@ +#ifndef LIST_H +#define LIST_H + +#include "pstypes.h" +#include "pserror.h" + +typedef struct listnode +{ + void *data; + struct listnode *next; + struct listnode *prev; +} listnode, list; + + +// Allocates space for a new list node +listnode *NewListNode(void); + +// Adds an item to a list +int AddListItem(list **listp, void *item); + +// Removes an item from a list +int RemoveListItem(list **listp, void *item); + +// Destroys all the nodes in a list +// The items must be freed in another routine +void DestroyList(list **listp); + +// Returns the number of items in a list +int CountListItems(list **listp); + +// returns a pointer the given item index in a list +// Returns NULL if point not found +void *GetListItem(list **listp, int index); + +// Returns how far from the head of the list a given item is +// Returns -1 if not found +int GetListItemIndex(list **listp, void *item); + +#endif \ No newline at end of file diff --git a/Descent3/lnxfuncs.cpp b/Descent3/lnxfuncs.cpp new file mode 100644 index 000000000..f48d6bd51 --- /dev/null +++ b/Descent3/lnxfuncs.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +/* +// Create an audio decoder +// You supply a function for reading bytes from the compressed data via a +// void *data handle, and the handle itself (typically a FILE *). +// Create_AudioDecoder returns a new AudioDecoder which can be used to +// read uncompressed decoded data from the compressed stream, +// and also returns the number of channels (1 or 2), the sample rate +// (e.g. 22050), and the number of samples contained in the compressed file +// (in case you want to pre-allocate a buffer to load them all into memory). +typedef unsigned ReadFunction(void *data, void *buf, unsigned qty); +typedef struct {bool empty;} AudioDecoder; +AudioDecoder *Create_AudioDecoder(ReadFunction *reader, void *data,unsigned *pChannels, unsigned *pSampleRate,long *pSampleCount) +{ + return malloc(sizeof(AudioDecoder)); +} + +// Read from audio decoder at most the specified qty of bytes +// (each sample takes two bytes). +// Returns zero when the end of file is reached. +unsigned AudioDecoder_Read(AudioDecoder *ad, void *buf, unsigned qty) +{ +} + +// Close audio decoder +void AudioDecoder_Close(AudioDecoder *ad) +{ + if(ad) free(ad); +} + +// Optional interface for supplying your own malloc and free functions +// Default is to use standard malloc and free. +typedef void *(*ad_malloc)(unsigned size); +typedef void (*ad_free)(void *p); +void AudioDecoder_MallocFree(ad_malloc *fn_malloc, ad_free *fn_free) +{ +} +*/ \ No newline at end of file diff --git a/Descent3/lnxmain.cpp b/Descent3/lnxmain.cpp new file mode 100644 index 000000000..887750a90 --- /dev/null +++ b/Descent3/lnxmain.cpp @@ -0,0 +1,815 @@ +/* + * + * Linux Main and any Linux specific code that MUST be in the main application + * goes here. + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __PERMIT_MAKEHOG +#include +#define _GNU_SOURCE +#include +#endif + +#include "SDL.h" +#include "program.h" +#include "mono.h" +#include "descent.h" +#include "application.h" +#include "appdatabase.h" +#include "pserror.h" +#include "args.h" +#include "init.h" +#include "renderer.h" + +#include "ddio.h" +#include "ddvid.h" +#include "osiris_dll.h" +#include "loki_utils.h" + +#if defined(MACOSX) +#include +#endif + +extern bool ddio_mouseGrabbed; +int no_debug_dialog=0; +char *DMFCGetString(int d); +//void *x = (void *) DMFCGetString; // just force a reference to dmfc.so ... + +char *__orig_pwd = NULL; + +bool linux_permit_gamma = false; + +typedef struct +{ + char *lng; + char sht; + char *comment; +} cmdLineArg; + +static cmdLineArg d3ArgTable[] = +{ + #ifdef __PERMIT_LINUX_GLIDE + {"rend_opengl", 'G', "Use OpenGL for 3D rendering."}, + {"rend_glide", 'L', "Use Glide for 3D rendering."}, + #endif + + #ifdef __PERMIT_PLAYMVE + {"playmve", 'p', "Play a specified movie."}, + #endif + + #ifdef __PERMIT_MAKEHOG + {"makehog", 'a', "Make a HOG archive from a list of names."}, + {"dumphog", 'A', "Dump a HOG archive to a directory."}, + #endif + + {"glidelibrary", 'l', "Select Glide rendering library."}, + {"gllibrary", 'g', "Select OpenGL rendering library."}, + {"rocknride", 'r', "Enable Rock'n'Ride (http://www.rocknride.com)."}, + {"cobra", 'R', "Enable Cobra chair support."}, + + #if (!defined(DEMO)) + {"dedicated", 'd', "Run as a dedicated netgame server."}, + {"nointro", 'n', "Skip intro movie."}, + #endif + + {"joystick", 'j', "Specify a joystick (number)."}, + {"nomousegrab", 'm', "Don't grab the mouse."}, + {"cdrom", 'C', "Specify a path to check for CD-ROM based files."}, + + {"deadzone0", 'D', "Specify a joystick deadzone (0.0 to 1.0)"}, + + {"gspyfile", 'S', "Specify a GameSpy config file."}, + {"timetest", 'T', "Run a demo benchmark."}, + {"fastdemo", 'Q', "Run demos as fast as possible."}, + {"framecap", 'F', "Specify a framecap (for dedicated server)."}, + + {"tempdir", 'P', "Specify directory for temporary files."}, + + #if (defined(_USE_OGL_LISTS_OPTIONAL)) + {"gllists", '\0', "Use OpenGL lists."}, + #endif + + #ifdef __PERMIT_GL_LOGGING + {"gllogging", '\0', "to be removed."}, + #endif + + {"nomultitexture", 't', "Disable multitexturing."}, + {"nopackedpixels", 'x', "Disable packed pixels."}, + {"glfog", 'o', "Enable OpenGL fog."}, + {"nogamma", 'M', "Disable gamma support."}, + {"glinfo", 'I', "Display info about OpenGL library."} + + #ifdef __CHECK_FOR_TOO_SLOW_RENDERING__ + {"nofpscheck", 'H', "Disable FPS checking."}, + #endif +}; + +static volatile char already_tried_signal_cleanup = 0; + + +#if (defined DEMO) +#define GAME_NAME_EXT "_demo" +#define GAME_VERS_EXT " Demo" +#elif (defined OEM) +#define GAME_NAME_EXT "_limited" +#define GAME_VERS_EXT " Limited Edition" +#else +#define GAME_NAME_EXT "" +#define GAME_VERS_EXT "" +#endif + + +//#define DEDICATED +namespace +{ + extern "C" + { + char game_version_buffer[150]; + char *game_version = game_version_buffer; + } +} + +// Given a device/drive, this marks it as the default CD-ROM drive +void ddio_MarkDefaultCDDrive(char *drive); +void ddio_InternalClose(); // needed for emergency cleanup. +void cdrom_system_shutdown(void); // needed for emergency cleanup. + +#ifdef __PERMIT_LINUX_GLIDE +void glide_Close(void); +#endif + +void just_exit(void) +{ + ddio_InternalClose(); // try to reset serial port. + cdrom_system_shutdown(); + + #ifdef __PERMIT_LINUX_GLIDE + if (Renderer_type == RENDERER_GLIDE) + glide_Close(); + #endif + + SDL_Quit(); + sync(); // just in case. + _exit(0); +} + + +void fatal_signal_handler(int signum) +{ + switch(signum) + { + case SIGHUP: + case SIGTRAP: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGILL: + case SIGQUIT: + case SIGSEGV: + case SIGTERM: + case SIGVTALRM: + case SIGINT: + if (already_tried_signal_cleanup) + fprintf(stderr, "Recursive signal cleanup! Hard exit! AHHGGGG!\n"); + else + { + already_tried_signal_cleanup = 1; + fprintf(stderr,"SIGNAL %d caught, aborting\n",signum); + just_exit(); + } // else + break; + case SIGXCPU: + case SIGXFSZ: + break; + } + + sync(); // just in case. + _exit(-10); +} + +void safe_signal_handler(int signum) +{ +} + +void install_signal_handlers(void) +{ + struct sigaction sact,fact; + + memset(&sact,0,sizeof(sact)); + memset(&fact,0,sizeof(fact)); + sact.sa_handler = safe_signal_handler; + fact.sa_handler = fatal_signal_handler; + + if(sigaction(SIGHUP,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGHUP\n"); + if(sigaction(SIGABRT,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGABRT\n"); + if(sigaction(SIGINT,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGINT\n"); + if(sigaction(SIGBUS,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGBUS\n"); + if(sigaction(SIGFPE,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGFPE\n"); + if(sigaction(SIGILL,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGILL\n"); + if(sigaction(SIGQUIT,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGQUIT\n"); + if(sigaction(SIGSEGV,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGSEGV\n"); + if(sigaction(SIGTERM,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGTERM\n"); + if(sigaction(SIGXCPU,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGXCPU\n"); + if(sigaction(SIGXFSZ,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGXFSZ\n"); + if(sigaction(SIGVTALRM,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGVTALRM\n"); + if(sigaction(SIGTRAP,&fact,NULL)) fprintf(stderr,"SIG: Unable to install SIGTRAP\n"); +} +// --------------------------------------------------------------------------- +// Define our operating system specific extensions to the gameos system +// --------------------------------------------------------------------------- + +class oeD3LnxApp: public oeLnxApplication +{ + bool shutdown, final_shutdown; + int old_screen_mode; + +public: + oeD3LnxApp(unsigned flags); + virtual ~oeD3LnxApp() + { + final_shutdown = true; + }; + + void run() + { + Descent3(); + }; +}; + +oeD3LnxApp::oeD3LnxApp(unsigned flags) : oeLnxApplication(flags) +{ + Descent = this; + shutdown = false; + final_shutdown = false; +} + + +class oeD3LnxDatabase: public oeLnxAppDatabase +{ +public: + oeD3LnxDatabase(); +}; + + + +// --------------------------------------------------------------------------- +// D3LnxDatabase operating system specific initialization +oeD3LnxDatabase::oeD3LnxDatabase():oeLnxAppDatabase() +{ + char path[_MAX_PATH]; + char netpath[_MAX_PATH]; + +//put directories into database + create_record("Descent3"); + + char *dir = getenv("D3_LOCAL"); + char *netdir=getenv("D3_DIR"); + + if (!dir) strcpy(path, loki_getdatapath()); //"/usr/local/games/descent3"); + else strcpy(path, dir); + + if (!netdir) strcpy(netpath, ""); + else strcpy(netpath, netdir); + + write("local directory", path, strlen(path)+1); + write("net directory",netpath,strlen(netpath)+1); + Database = this; +} + + +static void register_d3_args(void) +{ + loki_register_stdoptions(); + + for (int i = 0; i < sizeof (d3ArgTable) / sizeof (d3ArgTable[0]); i++) + { + loki_registeroption(d3ArgTable[i].lng, + d3ArgTable[i].sht, + d3ArgTable[i].comment); + } // for +} // register_d3_args + + +int sdlKeyFilter(const SDL_Event *event); +int sdlMouseButtonUpFilter(const SDL_Event *event); +int sdlMouseButtonDownFilter(const SDL_Event *event); +int sdlMouseMotionFilter(const SDL_Event *event); + +int d3SDLEventFilter(const SDL_Event *event) +{ + switch (event->type) + { + case SDL_KEYUP: + case SDL_KEYDOWN: + return(sdlKeyFilter(event)); + + case SDL_JOYBALLMOTION: + case SDL_MOUSEMOTION: + return(sdlMouseMotionFilter(event)); + case SDL_MOUSEBUTTONUP: + return(sdlMouseButtonUpFilter(event)); + case SDL_MOUSEBUTTONDOWN: + return(sdlMouseButtonDownFilter(event)); + } // switch + + return(1); +} + + +void StartDedicatedServer(); + +#ifdef __PERMIT_MAKEHOG + +static void hogfileRefresh(const char *x) +{ + printf(" - %s\n", x); +} // hogfileRefresh + +int CreateNewHogFile(const char *hogname, int nfiles, const char **filenames, + void(* UpdateFunction)(char *)); + +// hack of the century. +static void buildNewHogFromFileList(char *fileName) +{ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + if (fileName == NULL) + { + printf("\n\nno filename specified.\n\n"); + return; + } // if + + char **files = (char **) malloc(sizeof (char *) * 8000); + int fCount = 0; + + FILE *f = fopen(fileName, "rb"); + if (f == NULL) + { + printf("\n\ncan't open [%s].\n\n", fileName); + return; + } // if + + int alreadyHere = 0; + int i = 0; + int j = 0; + + do + { + int ch = 0; + files[i] = (char *) malloc(300); + files[i][0] = '\0'; + for (j = 0; (ch != '\n') && (ch != EOF); j++) + { + ch = fgetc(f); + files[i][j] = (char) ch; + } + + files[i][j-1] = '\0'; + + // !!! trim spaces... + + if (files[i][0] == '\0') + alreadyHere = 1; + else + { + alreadyHere = 0; + + for (int n = 0; n < i; n++) + { + if (strcasecmp(files[n], files[i]) == 0) + alreadyHere = 1; + } // for + + if ((!alreadyHere) && (access(files[i], R_OK) != 0)) + { + alreadyHere = 1; + DIR *d = opendir("."); + struct dirent *ent = readdir(d); + while (ent != NULL) + { + if (fnmatch(files[i], ent->d_name, FNM_CASEFOLD) == 0) + { + rename(ent->d_name, files[i]); + ent = NULL; + alreadyHere = 0; // ok, so this variable is misnamed... + } // if + else + ent = readdir(d); + } // while + closedir(d); + + if (alreadyHere) + printf("File [%s] is missing.\n", files[i]); + } // if + } // if + + if (alreadyHere == 0) + { + printf(" - %s\n", files[i]); + i++; + fCount++; + } + else + free(files[i]); + } while (!feof(f)); + + fclose(f); + + int swapped; + do + { + swapped = 0; + + for (int y = 0; y < fCount - 1; y++) + { + if (strcasecmp(files[y], files[y + 1]) > 0) + { + char *tmp = files[y]; + files[y] = files[y + 1]; + files[y + 1] = tmp; + swapped = 1; + } // if + } // for + } while (swapped); + + CreateNewHogFile("new.hog", i, (const char **) files, (void (*) (char *))hogfileRefresh); +} // buildNewHogFileFromList + +#endif + + +#ifdef BETAEXPIRE +static void check_beta() +{ + fprintf(stderr, "\n\n\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + + if ( time(NULL) > (BETAEXPIRE + 30 * 24 * 60 * 60) ) + { + fprintf(stderr, + "Thanks for participating in the Descent 3 beta test!\n" + "This beta copy has now expired.\n" + "Please visit http://www.lokigames.com/ for a non-beta release.\n"); + _exit(0); + } // if + else + { + fprintf(stderr, + "Warning: This is a beta version of DESCENT 3.\n" + "Please report any bugs in fenris: http://fenris.lokigames.com/\n"); + } // else + + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "\n\n\n"); +} // check_beta +#endif + + +// --------------------------------------------------------------------------- +// Main +// creates all the OS objects and then runs Descent 3. +// this is all this function should do. +// --------------------------------------------------------------------------- +int main(int argc,char *argv[]) +{ + __orig_pwd = getcwd(NULL, 0); + + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + #ifdef BETAEXPIRE + // IMPORTANT - TAKE ME OUT - FIXME ------------------------------- + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "**** Please remove -DBETAEXPIRE from the Makefile! ******" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + check_beta(); + #endif + + #ifdef __DUMP_MVE_TO_DISK + // IMPORTANT - TAKE ME OUT - FIXME ------------------------------- + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "** Please remove -D__DUMP_MVE_TO_DISK from the Makefile! **" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + #warning "***********************************************************" + + fprintf(stderr, "\n\n\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, " Warning, this binary dumps movies to disk. This is BAD.\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "***************************************************************************\n"); + fprintf(stderr, "\n\n\n"); + #endif + +/* + if ( (argv[1] != NULL) && (strcasecmp(argv[1], "--nettest")) ) + _exit(nettest_Main(argc, argv)); +*/ + + //rcg01152000 need this for mpeg playback currently. + // rcg02232004 probably don't need this anymore. + //if (getenv("SDL_VIDEO_YUV_HWACCEL") == NULL) + // putenv("SDL_VIDEO_YUV_HWACCEL=0"); + + GatherArgs(argv); + + snprintf(game_version_buffer, sizeof (game_version_buffer), + "%d.%d.%d%s%s", D3_MAJORVER, D3_MINORVER, D3_BUILD, LOKI_VERSION, + GAME_VERS_EXT); + + loki_setgamename("descent3" GAME_NAME_EXT,game_version_buffer,"Descent 3"); + + snprintf(game_version_buffer, sizeof (game_version_buffer), + "\n\n" + "Descent 3 Linux %s v%d.%d.%d%s\n" + "Copyright (C) 1999 Outrage Entertainment, Inc.\n", + #ifdef DEDICATED + "Dedicated Server", + #elif DEMO + "Demo", + #else + "Client", + #endif + D3_MAJORVER, D3_MINORVER, D3_BUILD, LOKI_VERSION); + + game_version += 2; // skip those first newlines for loki_initialize. + + + register_d3_args(); + loki_initialize(argc, argv, game_version_buffer); + + int x; + + #ifdef __PERMIT_MAKEHOG + x = FindArgChar("-dumphog", 'A'); + if (x) + { + if (x != 1) + printf(" --dumphog must be first command if you use it.\n"); + else + dump_hog_to_directory(argv[x + 1]); + _exit(0); + } // if + + x = FindArgChar("-makehog", 'a'); + if (x) + { + if (x != 1) + printf(" --makehog must be first command if you use it.\n"); + else + buildNewHogFromFileList(argv[x + 1]); + _exit(0); + } // if + #endif + +/* + x = FindArg("-nettest"); + if (x) + { + if (x != 1) + printf(" --nettest must be first command if you use it.\n"); + _exit(0); + } // if +*/ + + SDL_ClearError(); + int rc = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CDROM); + if (rc != 0) + { + fprintf(stderr, "SDL: SDL_Init() failed! rc == (%d).\n", rc); + fprintf(stderr, "SDL_GetError() reports \"%s\".\n", SDL_GetError()); + return(0); + } // if + +// atexit(SDL_Quit); + + // !!! FIXME: Don't use an event filter! + SDL_SetEventFilter(d3SDLEventFilter); + install_signal_handlers(); + + //build the command line as one long string, seperated by spaces... +/* + char commandline[256]; + strcpy(commandline,""); + for(int i=0;i1) + { + fprintf(stderr,"Error: Too many renderer's defined, use only one\n"); + return 0; + } + + /* + //print out renderer + if(glide_rend) + { + fprintf(stderr,"Renderer: GLiDE\n"); + } + if(opengl_rend) + { + fprintf(stderr,"Renderer: OpenGL\n"); + } + */ + #endif + + if(FindArgChar("-nomousegrab", 'm')) + { + flags |= APPFLAG_NOMOUSECAPTURE; + } + + ddio_mouseGrabbed = (FindArgChar("-nomousegrab", 'm') == 0); + + if(!FindArg("-sharedmemory")) + { + flags |= APPFLAG_NOSHAREDMEMORY; + } + + #ifdef __PERMIT_LINUX_GLIDE + if(FindArgChar("+rend_opengl", 'G')) + { + flags |= APPFLAG_WINDOWEDMODE; + } + #else + flags |= APPFLAG_WINDOWEDMODE; + #endif + + if(!FindArg("-nodgamouse")) + { + flags |= APPFLAG_DGAMOUSE; + } + #else + fprintf(stderr,"Error: \"--dedicated\" or \"-d\" flag required\n"); + return 0; + #endif + + }else + { + // Dedicated Server Mode + flags |= OEAPP_CONSOLE; + + //service flag overrides others here in the group + if(FindArg("-service")) + { + flags |= APPFLAG_USESERVICE; + }else + { + if(FindArg("-svgalib")) + { + flags |= APPFLAG_USESVGA; + } + } + } + + bool run_d3 = true; + if(flags&APPFLAG_USESERVICE) + { + run_d3 = false; + pid_t np = fork(); + if(np==-1) + { + fprintf(stderr,"Unable to fork process\n"); + } + if(np==0) + { + run_d3 = true; + np = setsid(); + pid_t pp = getpid(); + printf("Successfully forked process [new sid=%d pid=%d]\n",np,pp); + } + } + + if(run_d3) + { + int cdrom_arg; + cdrom_arg = FindArgChar("-cdrom", 'C'); + if(cdrom_arg>0) + { + ddio_MarkDefaultCDDrive(GameArgs[cdrom_arg+1]); + } + + oeD3LnxApp d3(flags); + oeD3LnxDatabase dbase; + StartDedicatedServer(); + PreInitD3Systems(); + + d3.init(); + d3.run(); + } + } + + just_exit(); + return 0; +} diff --git a/Descent3/loadstate.cpp b/Descent3/loadstate.cpp new file mode 100644 index 000000000..ecec13870 --- /dev/null +++ b/Descent3/loadstate.cpp @@ -0,0 +1,2097 @@ +/* + * $Logfile: /DescentIII/Main/loadstate.cpp $ + * $Revision: 74 $ + * $Date: 9/05/01 12:19p $ + * $Author: Matt $ + * + * Load a saved game + * + * $Log: /DescentIII/Main/loadstate.cpp $ + * + * 74 9/05/01 12:19p Matt + * When loading a saved game, load the add-on data before the level so the + * textures get mapped properly. + * + * 73 4/21/00 3:23p Matt + * Check for bad vis effect structure on load, which probably means a game + * saved with a prior to version 1.3. If found, convert old struct to new + * struct. + * + * 72 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 71 4/07/00 5:51p Matt + * Do id translation on dummy objects that are really generics, etc. + * + * 70 3/20/00 12:13p Matt + * Merge of Duane's post-1.3 changes. + * Fixed mem leak. + * Added check for object type. + * + * 69 9/18/99 9:23p Jeff + * re initialize objects' position history (for motion blur) + * + * 68 7/22/99 11:47a Chris + * + * 67 7/21/99 7:17p Chris + * Fixed an attach related crash in the load/save game code + * + * 66 7/08/99 10:30a Kevin + * when loading a save game >level 4 from a minimal install, don't ask for + * CD1 + * + * 65 5/21/99 11:15p Samir + * savegames save hud timer and special text states. restore too. + * + * 64 5/20/99 2:48a Matt + * Auto-waypoints now stored & used per player. Manual waypoints will all + * players, once Jason makes it work. + * + * 63 5/17/99 10:38a Samir + * reset player ship and reset camera views before starting new level. may + * fix some sequencing problems with ship reticle and inventory too. + * + * 62 5/12/99 6:02p Jeff + * flip order of saving/loading objects and players + * + * 61 5/12/99 12:25p Kevin + * save & restore Current_mission.game_state_flags + * + * 60 5/12/99 11:40a Matt + * Changed some arrays to be malloc'd instead of declared locally on the + * stack, because the Mac limits local data to 32K. + * + * 59 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 58 5/06/99 6:11p Kevin + * fixes for save/load game syste. Also require CD again and added some + * heat.net stuff + * + * 57 4/30/99 3:03p Kevin + * improved times restored count + * + * 56 4/29/99 11:25a Kevin + * Added room damage to save game + * + * 55 4/28/99 2:14a Samir + * save and load Game messages in savegame. + * + * 54 4/23/99 10:23p Kevin + * Fixed savegame doorways bug! + * + * 53 4/23/99 8:28p Kevin + * trying to get doors working in save/load games + * + * 52 4/23/99 7:33p Kevin + * save weather effects and reset the HUD to show the correct ship + * + * 51 4/23/99 4:50p Jeff + * fixed page in polymodel problem + * + * 50 4/22/99 4:13p Matt + * Deleted sounds array from the object struct, since it was never used. + * + * 49 4/16/99 12:33a Matt + * Disable Soar on non-Windows systems. + * + * 48 4/15/99 5:41p Samir + * save current music region and restore it. + * + * 47 4/15/99 1:40a Jeff + * changes for linux compile + * + * 46 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 45 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 44 3/23/99 6:09p Kevin + * Marker text saved + * + * 43 3/23/99 12:33p Kevin + * More post level results improvements + * + * 42 3/22/99 5:12p Samir + * added snapshot to savegame. + * + * 41 3/11/99 9:09p Jeff + * fixed remaining known bugs in demo system (DemoWriteObjCreate being + * called twice and +#include "levelgoal.h" +#include "aistruct.h" +#include "matcen.h" +#include "pilot.h" +#include "marker.h" +#include "d3music.h" +#include "weather.h" +#include "cockpit.h" +#include "hud.h" + +void PageInAllData (); + +// dynamically allocated to be efficient (only needed during save/load) + +int LGSSnapshot(CFILE *fp); + + + +int Times_game_restored = 0; +//static gs_tables *gs_Xlates = NULL; + +gs_tables *gs_Xlates = NULL; +//int Gamesave_read_version=0; + + +void IncreaseRestoreCount(const char *file) +{ + //Open the file up in read more, read the current count, then incease it + //and write the increased value back out. + + char countpath[_MAX_PATH*2]; + CFILE *cfp; + + strcpy(countpath,file); + strcat(countpath,".cnt"); + + cfp = cfopen(countpath,"rb"); + if(cfp) + { + Times_game_restored = cf_ReadInt(cfp); + cfclose(cfp); + } + else + { + Times_game_restored = 0; + } + Times_game_restored++; + + cfp = cfopen(countpath,"wb"); + if(cfp) + { + cf_WriteInt(cfp,Times_game_restored); + cfclose(cfp); + } +} + + + +extern bool IsRestoredGame; +/////////////////////////////////////////////////////////////////////////////// +// loads a game from a given slot. +int LoadGameState(const char *pathname) +{ + CFILE *fp; + int retval = LGS_OK; + char desc[GAMESAVE_DESCLEN+1]; + char path[PSPATHNAME_LEN]; + ushort version; + ushort curlevel; + short pending_music_region; + IsRestoredGame = true; +// load in stuff + fp = cfopen(pathname, "rb"); + if (!fp) { + Int3(); + return LGS_FILENOTFOUND; + } + + +START_VERIFY_SAVEFILE(fp); + gs_Xlates = new gs_tables; + +// read in header and do version check. + cf_ReadBytes((ubyte *)desc, sizeof(desc), fp); + version = (ushort)cf_ReadShort(fp); + if (version < GAMESAVE_OLDVER) { + Int3(); + retval = LGS_OUTDATEDVER; + goto loadsg_error; + } + + retval = LGSSnapshot(fp); // read and clear snapshot. + if (retval > 0) { + bm_FreeBitmap(retval); + } + + //Gamesave_read_version=version; + +// read translation tables + retval = LGSXlateTables(fp); + if (retval != LGS_OK) + goto loadsg_error; +// read in gamemode info. + +// read in mission level info. + curlevel = (ushort)cf_ReadShort(fp); + cf_ReadString(path, sizeof(path), fp); + + if( (curlevel > 4) && (strcmpi(path,"d3.mn3")==0) ) + { + strcpy(path,"d3_2.mn3"); + } + +// must load mission and initialize level before reading in other data. + retval = LGSMission(path, curlevel); + if (retval != LGS_OK) goto loadsg_error; + + Current_mission.game_state_flags = cf_ReadInt(fp); + //Increase count for how many times this file was restored + IncreaseRestoreCount(pathname); + +// read in information after level's been loaded. + Gametime = cf_ReadFloat(fp); + FrameCount = cf_ReadInt(fp); + Current_waypoint = cf_ReadInt(fp); + pending_music_region = cf_ReadShort(fp); + D3MusicSetRegion(pending_music_region); + + //Times_game_restored = cf_ReadInt(fp); + //Read Weather + { + int weather_size = cf_ReadInt(fp); + if(weather_size != sizeof(Weather)) + { + Int3(); + retval = LGS_OUTDATEDVER; + goto loadsg_error; + } + } + cf_ReadBytes((ubyte *)&Weather,sizeof(Weather),fp); + + //Restore active doorways + { + int num_active_dw = cf_ReadInt(fp); + Num_active_doorways = cf_ReadInt(fp); + + for(int d=0;dobji_indices, MAX_OBJECT_IDS, FindObjectIDName); + +// load polymodel translation list. + BUILD_XLATE_TABLE(gs_Xlates->model_handles, MAX_POLY_MODELS, FindPolyModelName); + +// load doar translation list. + BUILD_XLATE_TABLE(gs_Xlates->door_handles, MAX_DOORS, FindDoorName); + +// load ship translation list. + BUILD_XLATE_TABLE(gs_Xlates->ship_handles, MAX_SHIPS, FindShipName); + +// build weapon translation list + BUILD_XLATE_TABLE(gs_Xlates->wpn_handles, MAX_WEAPONS, FindWeaponName); + +// read in limited texture name list. + BUILD_XLATE_TABLE(gs_Xlates->tex_handles, MAX_TEXTURES, FindTextureName); + +// read in limited bitmap name list. this is a slightly diff format than above to save space + BUILD_MINI_XLATE_TABLE(gs_Xlates->bm_handles, bm_FindBitmapName); + +END_VERIFY_SAVEFILE(fp, "Xlate load"); + return retval; +} + + +// loads in level's mission and level. +int LGSMission(const char *msnname, int level) +{ +// we will free the mission. +// Free any game objects/etc that needs to be done when ending a level here. + FreeScriptsForLevel(); + + Osiris_DisableCreateEvents(); + if (LoadMission((const char *)msnname)) { + SetCurrentLevel(level); + Player_num=0; // Reset player num + Players[Player_num].ship_index = FindShipName(DEFAULT_SHIP); + ASSERT(Players[Player_num].ship_index != -1); + + // Load any addon data + mng_LoadAddonPages(); + + InitPlayerNewShip(Player_num,INVRESET_ALL); + InitCameraViews(1); //Turn off all camera views, including rear views + + if (!LoadAndStartCurrentLevel()) { + Int3(); + Osiris_EnableCreateEvents(); + return LGS_STARTLVLFAILED; + } + } + else { + Int3(); + Osiris_EnableCreateEvents(); + return LGS_MISSIONFAILED; + } + Osiris_EnableCreateEvents(); + //mng_LoadAddonPages (); + return LGS_OK; +} + +extern ubyte AutomapVisMap[MAX_ROOMS]; + +// initializes rooms +int LGSRooms(CFILE *fp) +{ + int retval = LGS_OK; + short i,p,highest_index; + + gs_ReadShort(fp, highest_index); + if (highest_index != (short)Highest_room_index) { + Int3(); + return LGS_CORRUPTLEVEL; + } + + short num_rooms; + gs_ReadShort(fp,num_rooms); + + for (i = 0; i < num_rooms; i++) + { + gs_ReadByte(fp, AutomapVisMap[i]); + } + + for (i = 0; i <= highest_index; i++) + { + ubyte used; + gs_ReadByte(fp, used); + if (used) { + // reset lists + Rooms[i].objects = -1; + Rooms[i].vis_effects = -1; + + // we need to save some room info out. + gs_ReadInt(fp, Rooms[i].flags); + gs_ReadByte(fp,Rooms[i].pulse_time); + gs_ReadByte(fp,Rooms[i].pulse_offset); + gs_ReadVector(fp,Rooms[i].wind); + gs_ReadFloat(fp, Rooms[i].last_render_time); + gs_ReadFloat(fp, Rooms[i].fog_depth); + gs_ReadFloat(fp, Rooms[i].fog_r); + gs_ReadFloat(fp, Rooms[i].fog_g); + gs_ReadFloat(fp, Rooms[i].fog_b); + gs_ReadFloat(fp, Rooms[i].damage); + // gs_ReadInt(fp, Rooms[i].objects); + //?? gs_WriteFloat(fp, Rooms[i].ambient_sound); // need to save an index of sounds. + + // Find out about texture changes + //if (Gamesave_read_version>=1) + //{ + int num_changed=cf_ReadShort (fp); + + for (int t=0;ttex_handles[cf_ReadShort(fp)]; + Rooms[i].faces[facenum].flags |= FF_TEXTURE_CHANGED; + } + //} + for(p = 0;pstate); + gs_ReadByte(fp, dp->flags); + gs_ReadByte(fp, dp->keys_needed); + gs_ReadFloat(fp, dp->position); + gs_ReadFloat(fp, dp->dest_pos); + gs_ReadInt(fp, dp->sound_handle); + gs_ReadInt(fp, dp->activenum); + gs_ReadInt(fp, dp->doornum); + } + + } + } + + //Rebuild the active doorway list + //DoorwayRebuildActiveList(); + + return retval; +} + + +// loads in and sets these events +int LGSEvents(CFILE *fp) +{ + int retval = LGS_OK; + +// clear events + ClearAllEvents(); + + + return retval; +} + + +// loads in and sets these triggers +int LGSTriggers(CFILE *fp) +{ + short i, n_trigs=0; + + gs_ReadShort(fp, n_trigs); + if (n_trigs != (short)Num_triggers) { + Int3(); + return LGS_CORRUPTLEVEL; + } + + for (i = 0; i < n_trigs; i++) + { + + // must free current trigger script if there's one allocated. + FreeTriggerScript(&Triggers[i]); + + gs_ReadShort(fp, Triggers[i].flags); + gs_ReadShort(fp, Triggers[i].activator); + + // read script info +//@@ script_info script; +//@@ +//@@ scrmem = LGSScript(fp, &script, &is_scripted, &scrmemsize); +//@@ if (is_scripted) { +//@@ ReinitTriggerScripts(&Triggers[i], &script, scrmemsize, scrmem); +//@@ } +//@@ if (scrmem) +//@@ delete[] scrmem; +//@@ script.set_name(NULL); +//@@ script.set_parms(0, NULL); + } + + return LGS_OK; +} + + +typedef struct +{ + int obj_handle,dest_objhandle; + ubyte subnum,subnum2; + + ushort modelnum; + ushort vertnum,end_vertnum; +} old_vis_attach_info; + +typedef struct +{ + ubyte type; + ubyte id; + + vector pos; + + vector velocity; + float mass; + float drag; + float size; + float lifeleft; + float lifetime; + float creation_time; + + int roomnum; + ushort flags; + int phys_flags; + ubyte movement_type; + short custom_handle; + ushort lighting_color; + + vis_attach_info attach_info; + axis_billboard_info billboard_info; + + vector end_pos; + + short next; + short prev; +} old_vis_effect; + +void CopyVisStruct(vis_effect *vis,old_vis_effect *old_vis) +{ + //Copy fields over + vis->type = old_vis->type; + vis->id = old_vis->id; + vis->pos = old_vis->pos; + + vis->velocity = old_vis->velocity; + vis->mass = old_vis->mass; + vis->drag = old_vis->drag; + vis->size = old_vis->size; + vis->lifeleft = old_vis->lifeleft; + vis->lifetime = old_vis->lifetime; + vis->creation_time = old_vis->creation_time; + + vis->roomnum = old_vis->roomnum; + vis->flags = old_vis->flags; + vis->phys_flags = old_vis->phys_flags; + vis->movement_type = old_vis->movement_type; + vis->custom_handle = old_vis->custom_handle; + vis->lighting_color = old_vis->lighting_color; + + vis->attach_info.obj_handle = old_vis->attach_info.obj_handle; + vis->attach_info.dest_objhandle = old_vis->attach_info.dest_objhandle; + vis->attach_info.subnum = old_vis->attach_info.subnum; + vis->attach_info.subnum2 = old_vis->attach_info.subnum2; + vis->attach_info.modelnum = old_vis->attach_info.modelnum; + vis->attach_info.vertnum = old_vis->attach_info.vertnum; + vis->attach_info.end_vertnum = old_vis->attach_info.end_vertnum; + + vis->billboard_info = old_vis->billboard_info; + vis->end_pos = old_vis->end_pos; +} + +// save viseffects +int LGSVisEffects(CFILE *fp) +{ + short i, count=0; + + gs_ReadShort(fp, count); + +// reassemble viseffect list. + for (i = 0; i < count; i++) + { + int roomnum, v; + vis_effect vis; + + cf_ReadBytes((ubyte *)&vis, sizeof(vis_effect), fp); + roomnum = vis.roomnum; + + //Check for old struct, and if found, fix it + if ((vis.type != VIS_FIREBALL) || (roomnum == -1) || (!ROOMNUM_OUTSIDE(roomnum) && ((roomnum > Highest_room_index) || !Rooms[roomnum].used)) || ((ROOMNUM_OUTSIDE(roomnum) && (CELLNUM(roomnum) > 65535)))) { + old_vis_effect old_vis; + + //Copy new into old + memcpy((ubyte *) &old_vis, (ubyte *) &vis, sizeof(old_vis_effect)); + + //Read extra data from old + cf_ReadBytes(((ubyte *)&old_vis) + sizeof(vis_effect), sizeof(old_vis_effect)-sizeof(vis_effect), fp); + + //Copy from old to new struct + CopyVisStruct(&vis,&old_vis); + + //Reset new room number + roomnum = vis.roomnum; + } + + vis.roomnum = vis.prev = vis.next = -1; + v = VisEffectAllocate(); + if(v >= 0) { //DAJ -1FIX + memcpy(&VisEffects[v], &vis, sizeof(vis_effect)); + VisEffectLink(v, roomnum); + } + } + + return LGS_OK; +} + +// players +int LGSPlayers(CFILE *fp) +{ + player *plr = &Players[0]; + short size; + + +// must do this if we read player struct as whole. + plr->inventory.Reset(false,INVRESET_ALL); + plr->counter_measures.Reset(false,INVRESET_ALL); + + gs_ReadShort(fp, size); + if (size != sizeof(player)) { + Int3(); + return LGS_OUTDATEDVER; + } + else { + int guided_handle; + cf_ReadBytes((ubyte *)plr, sizeof(player), fp); + if (plr->guided_obj) { + gs_ReadInt(fp, guided_handle); + plr->guided_obj = &Objects[guided_handle&HANDLE_OBJNUM_MASK]; + } + // save inventory and countermeasures + // must do this if we read player struct as whole. + plr->inventory.ReadInventory(fp); + plr->counter_measures.ReadInventory(fp); + } + + int ship_index = Players[Player_num].ship_index; + + if (ship_index<0) + ship_index=0; + + FreeCockpit(); + CloseShipHUD(); + InitShipHUD(ship_index); + InitCockpit(ship_index); + + return LGS_OK; +} + +extern int Physics_NumLinked; +extern int PhysicsLinkList[MAX_OBJECTS]; + +int inreadobj=0; + +void VerifySaveGame(CFILE *fp) +{ + int testint; + testint=cf_ReadInt(fp); + ASSERT(testint==0xF00D4B0B); + +} + +#define VERIFY_SAVEFILE +//VerifySaveGame(fp) +extern char MarkerMessages[MAX_PLAYERS*2][MAX_MARKER_MESSAGE_LENGTH]; +extern int Marker_message; +// loads in and sets these objects +int LGSObjects(CFILE *fp, int version) +{ + + inreadobj = 1; + int retval = LGS_OK; + vector pos, last_pos; + int roomnum; + int i, j, highest_index; + int max_terr; + + matrix *objmat = (matrix *) mem_malloc(sizeof(*objmat) * MAX_OBJECTS); + + Osiris_DisableCreateEvents(); +// we must reset some data before continuing. + InitBigObjects(); + +START_VERIFY_SAVEFILE(fp); + Marker_message = cf_ReadInt(fp); + int num_marker_msgs = cf_ReadShort(fp); + for(i=0;ilighting_render_type = l_rend_type; + + + //Store whether or not we have a pointer to lighting_info + ubyte has_lightinfo; + gs_ReadByte(fp,has_lightinfo); + if(has_lightinfo) + { + if(!op->lighting_info) + { + op->lighting_info = (light_info *) mem_malloc(sizeof(*op->lighting_info)); + } + cf_ReadBytes((ubyte *)op->lighting_info,sizeof(*op->lighting_info),fp); + } + + + // validate handle. + gs_ReadInt(fp, handle); + if ((handle & HANDLE_OBJNUM_MASK) != i) { + Int3(); + retval = LGS_OBJECTSCORRUPT; + goto done; + } + + // read in the rest. + gs_ReadByte(fp, dummy_type); + + // positional information (not assigned to object 'cause of Unlink) + gs_ReadInt(fp, roomnum); + gs_ReadVector(fp, pos); + gs_ReadVector(fp, last_pos); + gs_ReadMatrix(fp, objmat[i]); + + // object name + gs_ReadByte(fp,j); + if(j>0){ + if(op->name) + mem_free(op->name); + op->name = (char *)mem_malloc(j+1); + if(!op->name) + Error("Out of memory"); + + cf_ReadBytes((ubyte *)op->name,j,fp); + op->name[j] = '\0'; + }else{ + op->name = NULL; + } + +// Kill any script thread associated with this object. + + + if (op->type != OBJ_NONE) + { + if(Objects[i].lighting_render_type!=LRT_LIGHTMAPS) + FreeObjectScripts(op,false); + + // Free up effects memory + if (op->effect_info) + { + mem_free (op->effect_info); + op->effect_info=NULL; + } + if (op->ai_info != NULL) + { + AIDestroyObj(op); + mem_free(op->ai_info); + op->ai_info = NULL; + } + if (op->dynamic_wb != NULL) + { + mem_free(op->dynamic_wb); + op->dynamic_wb = NULL; + } + if (op->attach_children != NULL) + { + mem_free(op->attach_children); + op->attach_children = NULL; + } + + } + op->type = type; + op->handle = handle; + op->dummy_type = dummy_type; + + op->roomnum = roomnum; + op->pos = pos; + op->last_pos = last_pos; + + // data universal to all objects + gs_ReadShort(fp, op->id); + + //Get type, and xlate if dummy + if (type == OBJ_DUMMY) + type = op->dummy_type; + + // xlate id to new id. + switch (type) + { + case OBJ_ROBOT: + case OBJ_POWERUP: + case OBJ_BUILDING: + case OBJ_CLUTTER: + op->id = gs_Xlates->obji_indices[op->id]; + obji = &Object_info[op->id]; + break; + case OBJ_DOOR: + op->id = gs_Xlates->door_handles[op->id]; + door = &Doors[op->id]; + break; + case OBJ_WEAPON: + op->id = gs_Xlates->wpn_handles[op->id]; + wpn = &Weapons[op->id]; + break; + case OBJ_PLAYER: + shp = &Ships[Players[op->id].ship_index]; + break; + } + + gs_ReadInt(fp, op->flags); + + op->flags|=OF_SERVER_OBJECT; + + gs_ReadByte(fp, op->control_type); + gs_ReadByte(fp, op->movement_type); + gs_ReadByte(fp, op->render_type); + + gs_ReadShort(fp, op->renderframe); + gs_ReadFloat(fp, op->size); + gs_ReadFloat(fp, op->shields); + gs_ReadByte(fp, op->contains_type); + gs_ReadByte(fp, op->contains_id); + gs_ReadByte(fp, op->contains_count); + gs_ReadFloat(fp, op->creation_time); + gs_ReadFloat(fp, op->lifeleft); + gs_ReadFloat(fp, op->lifetime); + gs_ReadInt(fp, op->parent_handle); + + // attachment info. free old info and load in new. + gs_ReadInt(fp, op->attach_ultimate_handle); + gs_ReadInt(fp, op->attach_parent_handle); + + gs_ReadInt(fp, nattach); + if (nattach) + { + int f_allocated; + + if(version >= 2) + gs_ReadInt(fp, f_allocated); + else + f_allocated = 1; + + if(f_allocated) + { + //mprintf((0,"Object %d has %d attach points.\n",i,nattach)); + op->attach_children = (int *)mem_malloc(sizeof(int)*nattach); + for (j = 0; j < nattach; j++) + gs_ReadInt(fp, op->attach_children[j]); + } + } + + VERIFY_SAVEFILE; + + gs_ReadByte(fp, op->attach_type); + gs_ReadShort(fp, op->attach_index); + gs_ReadFloat(fp, op->attach_dist); + gs_ReadVector(fp, op->min_xyz); + gs_ReadVector(fp, op->max_xyz); + gs_ReadFloat(fp, op->impact_size); + gs_ReadFloat(fp, op->impact_time); + gs_ReadFloat(fp, op->impact_player_damage); + gs_ReadFloat(fp, op->impact_generic_damage); + gs_ReadFloat(fp, op->impact_force); + + // custom default script info + gs_ReadByte(fp,j); + if(j>0){ + op->custom_default_script_name = (char *)mem_malloc(j+1); + if(!op->custom_default_script_name) + Error("Out of memory"); + + cf_ReadBytes((ubyte *)op->custom_default_script_name,j,fp); + op->custom_default_script_name[j] = '\0'; + }else{ + op->custom_default_script_name = NULL; + } + + gs_ReadByte(fp,j); + if(j>0){ + op->custom_default_module_name = (char *)mem_malloc(j+1); + if(!op->custom_default_module_name) + Error("Out of memory"); + + cf_ReadBytes((ubyte *)op->custom_default_module_name,j,fp); + op->custom_default_module_name[j] = '\0'; + }else{ + op->custom_default_module_name = NULL; + } + + VERIFY_SAVEFILE; + + gs_ReadShort(fp, op->position_counter); + + VERIFY_SAVEFILE; + +// write out all structures here. + // movement info. + gs_ReadShort(fp, size); + if (size != sizeof(op->mtype)) { + Int3(); + retval = LGS_OUTDATEDVER; + goto done; + } + cf_ReadBytes((ubyte *)&op->mtype, size, fp); + + VERIFY_SAVEFILE; + //Control info, determined by CONTROL_TYPE + gs_ReadShort(fp, size); + if (size != sizeof(op->ctype)) { + Int3(); + retval = LGS_OUTDATEDVER; + goto done; + } + cf_ReadBytes((ubyte *)&op->ctype, size, fp); + + VERIFY_SAVEFILE; + // remap bitmap handle if this is a fireball! + if (type == OBJ_FIREBALL) { + index = op->ctype.blast_info.bm_handle; + op->ctype.blast_info.bm_handle = (index > -1) ? gs_Xlates->bm_handles[index] : -1; + } + + + // save ai information. + retval = LGSObjAI(fp, &op->ai_info); + if (retval != LGS_OK) + goto lgsobjs_fail; + + + VERIFY_SAVEFILE; + + + // save out rendering information + gs_ReadShort(fp, size); + if (size != sizeof(op->rtype)) { + Int3(); + retval = LGS_OUTDATEDVER; + goto done; + } + cf_ReadBytes((ubyte *)&op->rtype, size, fp); + + op->size = cf_ReadFloat(fp); + + // page in correct graphical data now. + switch (op->render_type) + { + case RT_NONE: + case RT_EDITOR_SPHERE: + case RT_FIREBALL: + case RT_LINE: + case RT_PARTICLE: + case RT_SPLINTER: + case RT_ROOM: + break; + case RT_WEAPON: + if (!(op->flags & OF_POLYGON_OBJECT)) { + if (!(Weapons[op->id].flags&WF_IMAGE_BITMAP)) + { + PageInVClip (Weapons[op->id].fire_image_handle); + } + break; + } + break; + case RT_POLYOBJ: // be sure to use translated handles for polyobjs and textures + { + // the paging mess. must update size of object accordingly. + sindex = (short)op->rtype.pobj_info.model_num; + new_model = (sindex > -1) ? gs_Xlates->model_handles[sindex] : -1; + if( (new_model != op->rtype.pobj_info.model_num) || (Poly_models[new_model].flags & PMF_NOT_RESIDENT)) + { + switch (type) + { + case OBJ_DOOR: + PageInPolymodel(new_model); + ComputeDefaultSize(OBJ_DOOR, new_model, &op->size); + break; + case OBJ_ROBOT: + case OBJ_POWERUP: + case OBJ_BUILDING: + case OBJ_CLUTTER: PageInPolymodel(new_model, type, &obji->size); op->size = obji->size; break; + case OBJ_WEAPON: PageInPolymodel(new_model, OBJ_WEAPON, &wpn->size); op->size = wpn->size; break; + case OBJ_PLAYER: PageInPolymodel (new_model, OBJ_PLAYER, &shp->size); op->size = shp->size; break; + case OBJ_ROOM: op->size = ComputeRoomBoundingSphere(&pos,&Rooms[op->id]); + break; + + default: PageInPolymodel(new_model); + } + } + op->rtype.pobj_info.model_num = new_model; + + sindex = (short)op->rtype.pobj_info.dying_model_num; + new_model = (sindex > -1) ? gs_Xlates->model_handles[sindex] : -1; + if (new_model != op->rtype.pobj_info.dying_model_num) { + switch (type) + { + case OBJ_PLAYER: PageInPolymodel (new_model, OBJ_PLAYER, &shp->size); op->size = shp->size; break; + } + } + op->rtype.pobj_info.dying_model_num = new_model; + + index = op->rtype.pobj_info.tmap_override; + op->rtype.pobj_info.tmap_override = (index>-1) ? gs_Xlates->tex_handles[index] : -1; + + memset(&op->rtype.pobj_info.multi_turret_info, 0, sizeof(multi_turret)); + poly_model *pm=&Poly_models[op->rtype.pobj_info.model_num]; + + if(pm->n_attach) + { + mprintf((0,"*Object %d has %d attach points.\n",i,pm->n_attach)); + } + + polyobj_info *p_info = &op->rtype.pobj_info; + int num_wbs = pm->num_wbs; + int count = 0; + for(int j = 0; j < num_wbs; j++) + { + ASSERT(pm->poly_wb[j].num_turrets >= 0 && pm->poly_wb[j].num_turrets <= 6400); + count += pm->poly_wb[j].num_turrets; + } + + p_info->multi_turret_info.num_turrets = count; + + if((count > 0) && (p_info->multi_turret_info.keyframes == NULL)) + { + int cur = 0; + + p_info->multi_turret_info.time = 0; + p_info->multi_turret_info.keyframes = (float *) mem_malloc(sizeof(float) * count); + p_info->multi_turret_info.last_keyframes = (float *) mem_malloc(sizeof(float) * count); + p_info->multi_turret_info.flags = 0; + } + //Do Animation stuff + custom_anim multi_anim_info; + cf_ReadBytes((ubyte *)&multi_anim_info,sizeof(multi_anim_info),fp); + ObjSetAnimUpdate(i, &multi_anim_info); + break; + } + + case RT_SHARD: + sindex = (short)op->rtype.shard_info.tmap; + op->rtype.shard_info.tmap = (sindex>-1) ? gs_Xlates->tex_handles[sindex] : -1; + break; + + default: + Int3(); + } + + VERIFY_SAVEFILE; + + //!! dynamic weapon battery info!! + retval = LGSObjWB(fp, op); + if (retval != LGS_OK) + goto lgsobjs_fail; + + VERIFY_SAVEFILE; + + // save effect info! + retval = LGSObjEffects(fp, op); + if (retval != LGS_OK) + goto lgsobjs_fail; + + VERIFY_SAVEFILE; + + //save script stuff. + if(Objects[i].lighting_render_type!=LRT_LIGHTMAPS) + { + //Don't init the scripts for an object with lightmaps, because it was never destroyed! + InitObjectScripts (op); + } + + // special case saves + retval = LGSObjSpecial(fp, op); + if (retval != LGS_OK) + goto lgsobjs_fail; + + // link into mine. + // turn off big object flags if its a big object. (ObjLink will take care of this.) + if (op->flags & OF_BIG_OBJECT) + op->flags &= (~OF_BIG_OBJECT); + + op->roomnum = roomnum;//-1; + //ObjLink(OBJNUM(op), roomnum); + //ObjSetOrient(op, &objmat); + + } + for (;i < MAX_OBJECTS; i++) + { + if (Objects[i].type != OBJ_NONE) + { + Osiris_DisableEvents(OEM_OBJECTS|OEM_TRIGGERS|OEM_LEVELS); + ObjDelete(i); + Osiris_EnableEvents(OEM_OBJECTS|OEM_TRIGGERS|OEM_LEVELS); + } + } + + for (i = 0; i < MAX_OBJECTS; i++) + { + Objects[i].next = -1; + Objects[i].prev = -1; + } + for(i=0;iroomnum; + op->roomnum=-1; + ObjLink(OBJNUM(op), newroom); + ObjSetOrient(op, &objmat[i]); + if(op->type==OBJ_ROOM) + { + mprintf((0,"Object %d is a room and Is%s a big object. Size=%f\n",i,(op->flags&OF_BIG_OBJECT)?"":"n't",op->size)); + if((op->size >= ((TERRAIN_SIZE*(float)1))) && !(op->flags & OF_BIG_OBJECT)) + { + BigObjAdd(i); + } + + ObjSetAABB(op); + } + + } + /* + if((op->attach_ultimate_handle)&&(OBJECT_HANDLE_NONE!=op->attach_ultimate_handle)) + { + mprintf((0,"Object %d has an ultimate parent of %d (%d)\n",i,OBJNUM(ObjGet(op->attach_ultimate_handle)),op->attach_parent_handle )); + ASSERT(op->flags&OF_ATTACHED); + } + if((op->attach_ultimate_handle)&&(OBJECT_HANDLE_NONE!=op->attach_parent_handle)) + { + mprintf((0,"Object %d has a parent of %d (%d)\n",i,OBJNUM(ObjGet(op->attach_parent_handle)),op->attach_parent_handle )); + ASSERT(op->flags&OF_ATTACHED); + } + */ + + } + mprintf((0,"Objects[121].prev=%d\n",Objects[121].prev)); + ResetFreeObjects(); + mprintf((0, "highest obj index = %d, ", Highest_object_index)); + ObjReInitPositionHistory(); + + +END_VERIFY_SAVEFILE(fp, "Objects load"); +lgsobjs_fail: + + Osiris_EnableCreateEvents(); + +done:; + + mem_free(objmat); + + return retval; +} + + +// loads ai +int LGSObjAI(CFILE *fp, ai_frame **pai) +{ + ai_frame *ai; + short size; + sbyte read_ai; + + *pai = NULL; + + gs_ReadByte(fp, read_ai); + if (!read_ai) + return LGS_OK; + + gs_ReadShort(fp, size); + if (size != sizeof(ai_frame)) + return LGS_OUTDATEDVER; + + *pai = (ai_frame *)mem_malloc(size); + ai= *pai; + + cf_ReadBytes((ubyte *)ai, size, fp); + + return LGS_OK; +} + + + +// loads fx +int LGSObjEffects(CFILE *fp, object *op) +{ + short size; + sbyte do_read; + + op->effect_info = NULL; + + gs_ReadByte(fp,do_read); + if (do_read) { + gs_ReadShort(fp, size); + if (size != sizeof(effect_info_s)) + return LGS_OUTDATEDVER; + + op->effect_info = (effect_info_s *)mem_malloc(size); + effect_info_s *ei = op->effect_info; + + cf_ReadBytes((ubyte *)ei, size, fp); + } + + return LGS_OK; +} + +//@@// loads script +//@@vector *LGSScript(CFILE *fp, script_info *script, ubyte *is_scripted, int *memsize) +//@@{ +//@@ int i; +//@@ char name[64]; +//@@ ushort nparms; +//@@ tScriptParm *parms = NULL; +//@@ +//@@// ugh, write out script info. +//@@ gs_ReadByte(fp, (*is_scripted)); +//@@ +//@@ cf_ReadString(name, sizeof(name), fp); +//@@ if (name[0]) +//@@ script->set_name(name); +//@@ +//@@ gs_ReadShort(fp, nparms); +//@@ gs_ReadShort(fp, script->is_custom); +//@@ if (nparms) +//@@ parms = new tScriptParm[nparms]; +//@@ +//@@ for (i = 0; i < nparms; i++) +//@@ { +//@@ gs_ReadByte(fp, parms[i].type); +//@@ gs_ReadFloat(fp, parms[i].val.x); +//@@ gs_ReadFloat(fp, parms[i].val.y); +//@@ gs_ReadFloat(fp, parms[i].val.z); +//@@ } +//@@ +//@@ script->set_parms(nparms, parms); +//@@ if (parms) +//@@ delete[] parms; +//@@ +//@@// write out thread data if necessary +//@@ vector *mem = NULL; +//@@ *memsize = 0; +//@@ if (*is_scripted) { +//@@ int mem_size=0,j; +//@@ +//@@ gs_ReadShort(fp, mem_size); +//@@ *memsize = mem_size; +//@@ +//@@ if (mem_size) { +//@@ mem = new vector[mem_size]; +//@@ for (j = 0; j < mem_size; j++) +//@@ gs_ReadVector(fp, mem[j]); +//@@ } +//@@ } +//@@ +//@@ return mem; +//@@} + + +// loads fx +int LGSObjWB(CFILE *fp, object *op) +{ + dynamic_wb_info *dwba = NULL; + int i; + sbyte num_wbs; + + gs_ReadByte(fp, num_wbs); + if (!num_wbs) + return LGS_OK; + + dwba = (dynamic_wb_info *)mem_malloc(sizeof(dynamic_wb_info)*num_wbs); + + for (i = 0; i< num_wbs; i++) + { + dynamic_wb_info *dwb = &dwba[i]; + cf_ReadBytes((ubyte *)dwb, sizeof(dynamic_wb_info), fp); + } + op->dynamic_wb = dwba; + + return LGS_OK; +} + + +// loads special object info +int LGSObjSpecial(CFILE *fp, object *op) +{ + int retval = LGS_OK; + + return retval; +} + + +// load spew +int LGSSpew(CFILE *fp) +{ + int i, count=0; + +// read GLOBAL value + gs_ReadShort(fp, spew_count); + + for (i = 0; i < MAX_SPEW_EFFECTS; i++) + { + ubyte used; + gs_ReadByte(fp, used); + if (used) + cf_ReadBytes((ubyte *)&SpewEffects[i], sizeof(spewinfo), fp); + } + + return LGS_OK; +} + +int LGSMatcens(CFILE *fp) +{ + int num_matcens = cf_ReadInt(fp); + + for(int i = 0; i < num_matcens; i++) + { + Matcen[i]->LoadData(fp); + } + + return LGS_OK; +} + + +int LGSSnapshot(CFILE *fp) +{ + int bm_handle = -1; + sbyte valid_snapshot=0; + +// get snapshot byte + valid_snapshot = cf_ReadByte(fp); + +// if valid, read it in, otherwise just return + if (valid_snapshot) { + bm_handle = bm_AllocLoadBitmap(fp, 0); + } + + return bm_handle; +} + + + +//@@ // movement info. +//@@ switch (op->movement_type) +//@@ { +//@@ case MT_SHOCKWAVE: +//@@ { +//@@ short bound; +//@@ gs_ReadShort(fp, bound); +//@@ if (bound < (sizeof(op_wave->damaged_list)/sizeof(uint))) { +//@@ Int3(); +//@@ retval = LGS_OUTDATEDVER; +//@@ goto lgsobjs_fail; +//@@ } +//@@ for (j = 0; j < sizeof(op_wave->damaged_list)/sizeof(uint); j++) +//@@ gs_ReadInt(fp, (uint)op_wave->damaged_list[j]); +//@@ } +//@@ break; +//@@ default: +//@@ gs_ReadVector(fp, op_phys->velocity); +//@@ gs_ReadVector(fp, op_phys->thrust); +//@@ gs_ReadVector(fp, op_phys->rotvel); +//@@ gs_ReadVector(fp, op_phys->rotthrust); +//@@ gs_ReadAngle(fp, op_phys->turnroll); +//@@ gs_ReadFloat(fp, op_phys->last_still_time); +//@@ gs_ReadInt(fp, op_phys->num_bounces); +//@@ gs_ReadFloat(fp, op_phys->coeff_restitution); +//@@ gs_ReadFloat(fp, op_phys->mass); +//@@ gs_ReadFloat(fp, op_phys->drag); +//@@ gs_ReadFloat(fp, op_phys->rotdrag); +//@@ gs_ReadFloat(fp, op_phys->full_thrust); +//@@ gs_ReadFloat(fp, op_phys->full_rotthrust); +//@@ gs_ReadFloat(fp, op_phys->max_turnroll_rate); +//@@ gs_ReadFloat(fp, op_phys->turnroll_ratio); +//@@ gs_ReadFloat(fp, op_phys->wiggle_amplitude); +//@@ gs_ReadFloat(fp, op_phys->wiggles_per_sec); +//@@ gs_ReadVector(fp, op_phys->dest_pos); +//@@ gs_ReadFloat(fp, op_phys->hit_die_dot); +//@@ gs_ReadFloat(fp, op_phys->max_speed_time); +//@@ gs_ReadInt(fp, op_phys->flags); +//@@ } +//@@ +//@@ // don't need to load lightmap infomation. should be in object already at loadtime +//@@ +//@@ //Control info, determined by CONTROL_TYPE +//@@ switch (op->control_type) +//@@ { +//@@ case CT_NONE: +//@@ case CT_AI: +//@@ case CT_FLYING: //the player is flying +//@@ case CT_FLYTHROUGH: //the flythrough system +//@@ case CT_SLEW: //slewing +//@@ case CT_PARTICLE: //Particle +//@@ break; +//@@ case CT_EXPLOSION: //explosion sequencer +//@@ gs_ReadFloat(fp, op_expl->spawn_time); +//@@ gs_ReadFloat(fp, op_expl->delete_time); +//@@ gs_ReadShort(fp, op_expl->delete_objnum); +//@@ gs_ReadShort(fp, op_expl->attach_parent); +//@@ gs_ReadShort(fp, op_expl->prev_attach); +//@@ gs_ReadShort(fp, op_expl->next_attach); +//@@ break; +//@@ case CT_DEBRIS: //this is a piece of debris +//@@ gs_ReadFloat(fp, op->ctype.dying_info.delay_time); +//@@ gs_ReadBool(fp, op->ctype.dying_info.f_death_anim); +//@@ break; +//@@ case CT_POWERUP: //animating powerup blob +//@@ gs_ReadInt(fp, op->ctype.powerup_info.count); +//@@ gs_ReadInt(fp, op->ctype.powerup_info.flags); +//@@ break; +//@@ case CT_SPLINTER: //Splinter +//@@ gs_ReadByte(fp, op_splint->subobj_num); +//@@ gs_ReadShort(fp, op_splint->facenum); +//@@ for (j = 0; j < MAX_VERTS_PER_SPLINTER; j++) +//@@ gs_ReadVector(fp, op_splint->verts[j]); +//@@ gs_ReadVector(fp, op_splint->center); +//@@ break; +//@@ case CT_WEAPON: //laser, etc. +//@@ gs_ReadShort(fp, op_wpn->parent_type); +//@@ gs_ReadShort(fp, op_wpn->src_gun_num); +//@@ gs_ReadInt(fp, op_wpn->last_hit_handle); +//@@ gs_ReadInt(fp, op_wpn->track_handle); +//@@ gs_ReadInt(fp, op_wpn->hit_status); +//@@ gs_ReadVector(fp, op_wpn->hit_pnt); +//@@ gs_ReadVector(fp, op_wpn->hit_wall_pnt); +//@@ gs_ReadVector(fp, op_wpn->hit_wall_normal); +//@@ gs_ReadInt(fp, op_wpn->hit_room); +//@@ gs_ReadInt(fp, op_wpn->hit_pnt_room); +//@@ gs_ReadShort(fp, op_wpn->hit_face); +//@@ gs_ReadFloat(fp, op_wpn->multiplier); +//@@ gs_ReadFloat(fp, op_wpn->thrust_left); +//@@ gs_ReadFloat(fp, op_wpn->last_drop_time); +//@@ break; +//@@ +//@@ default: +//@@ Int3(); +//@@ } +//@@ +//@@ if (op->type == OBJ_FIREBALL) { +//@@ gs_ReadFloat(fp,op->ctype.blast_info.max_size); +//@@ gs_ReadInt(fp,index); +//@@ op->ctype.blast_info.bm_handle = (index > -1) ? gs_Xlates->bm_handles[index] : -1; +//@@ } + +// RENDER +//@@ const int N_SHARD_VERTS = 3; +//@@ switch (op->render_type) +//@@ { +//@@ case RT_NONE: +//@@ case RT_EDITOR_SPHERE: +//@@ case RT_FIREBALL: +//@@ case RT_LINE: +//@@ case RT_PARTICLE: +//@@ case RT_SPLINTER: +//@@ case RT_ROOM: +//@@ break; +//@@ case RT_WEAPON: +//@@ if (!(op->flags & OF_POLYGON_OBJECT)) { +//@@ PageInVClip (Weapons[op->id].fire_image_handle); +//@@ break; +//@@ } +//@@ case RT_POLYOBJ: // be sure to use translated handles for polyobjs and textures +//@@ gs_ReadShort(fp, sindex); +//@@ new_model = (sindex > -1) ? gs_Xlates->model_handles[sindex] : -1; +//@@ if (new_model != pobji->model_num) +//@@ PageInPolymodel (new_model, op->type, &op->size); +//@@ pobji->model_num = new_model; +//@@ +//@@ gs_ReadShort(fp, sindex); +//@@ new_model = (sindex > -1) ? gs_Xlates->model_handles[sindex] : -1; +//@@ if (new_model != pobji->dying_model_num) +//@@ PageInPolymodel (new_model, op->type, &op->size); +//@@ pobji->dying_model_num = new_model; +//@@ +//@@ gs_ReadFloat(fp, pobji->anim_start_frame); +//@@ gs_ReadFloat(fp, pobji->anim_frame); +//@@ gs_ReadFloat(fp, pobji->anim_end_frame); +//@@ gs_ReadFloat(fp, pobji->anim_time); +//@@ gs_ReadInt(fp, pobji->anim_flags); +//@@ gs_ReadFloat(fp, pobji->max_speed); +//@@ gs_ReadInt(fp, pobji->subobj_flags); +//@@ +//@@ gs_ReadInt(fp, index); +//@@ pobji->tmap_override = (index>-1) ? gs_Xlates->tex_handles[index] : -1; +//@@ +//@@ //!! multi_anim_save here +//@@ // !@$()*# SAVE MULTI ANIM TURRET INFORMATION HERE $*#)(*)(! +//@@ break; +//@@ +//@@ case RT_SHARD: +//@@ for (j = 0; j < N_SHARD_VERTS; j++) +//@@ gs_ReadVector(fp, op->rtype.shard_info.points[j]); +//@@ for (j = 0; j < N_SHARD_VERTS; j++) +//@@ gs_ReadFloat(fp, op->rtype.shard_info.u[j]); +//@@ for (j = 0; j < N_SHARD_VERTS; j++) +//@@ gs_ReadFloat(fp, op->rtype.shard_info.v[j]); +//@@ gs_ReadVector(fp, op->rtype.shard_info.normal); +//@@ gs_ReadShort(fp, sindex); +//@@ op->rtype.shard_info.tmap = (sindex>-1) ? gs_Xlates->tex_handles[sindex] : -1; +//@@ break; +//@@ +//@@ default: +//@@ Int3(); +//@@ } +//@@ + +// AI +//@@// the FUN begins. +//@@ gs_ReadByte(fp, ai->ai_class); +//@@ gs_ReadByte(fp, ai->ai_type); +//@@ +//@@// write path info +//@@ ai_path_info *path = &ai->path; +//@@ gs_ReadInt(fp, path->flags); +//@@ gs_ReadShort(fp, path->cur_path); +//@@ gs_ReadShort(fp, path->cur_node); +//@@ gs_ReadShort(fp, path->num_paths); +//@@ gs_ReadShort(fp, path->goal_index); +//@@ +//@@ for (i = 0; i < MAX_JOINED_PATHS; i++) +//@@ { +//@@ gs_ReadByte(fp, path->path_id[i]); +//@@ gs_ReadByte(fp, path->path_type[i]); +//@@ gs_ReadShort(fp, path->path_start_node[i]); +//@@ gs_ReadShort(fp, path->path_end_node[i]); +//@@ gs_ReadShort(fp, path->path_flags[i]); +//@@ } +//@@ +//@@// continue +//@@ gs_ReadFloat(fp, ai->max_velocity); +//@@ gs_ReadFloat(fp, ai->max_delta_velocity); +//@@ gs_ReadFloat(fp, ai->max_turn_rate); +//@@ gs_ReadFloat(fp, ai->max_delta_turn_rate); +//@@ gs_ReadFloat(fp, ai->attack_vel_percent); +//@@ gs_ReadFloat(fp, ai->flee_vel_percent); +//@@ gs_ReadFloat(fp, ai->dodge_vel_percent); +//@@ gs_ReadFloat(fp, ai->circle_distance); +//@@ gs_ReadFloat(fp, ai->dodge_percent); +//@@ +//@@ for (i = 0; i < 2; i++) +//@@ { +//@@ gs_ReadFloat(fp, ai->melee_damage[i]); +//@@ gs_ReadFloat(fp, ai->melee_latency[i]); +//@@ } +//@@ +//@@ for (i = 0; i < MAX_AI_SOUNDS; i++) +//@@ { +//@@ gs_ReadInt(fp, ai->sound[i]); +//@@ gs_ReadFloat(fp, ai->last_sound_time[i]); +//@@ } +//@@ gs_ReadShort(fp, ai->last_played_sound_index); +//@@ +//@@ gs_ReadByte(fp, ai->movement_type); +//@@ gs_ReadByte(fp, ai->movement_subtype); +//@@ gs_ReadByte(fp, ai->animation_type); +//@@ gs_ReadByte(fp, ai->next_animation_type); +//@@ gs_ReadByte(fp, ai->next_movement); +//@@ gs_ReadByte(fp, ai->current_wb_firing); +//@@ gs_ReadByte(fp, ai->last_wb_firing); +//@@ +//@@// goals +//@@ for (i = 0;i < MAX_GOALS; i++) +//@@ { +//@@ retval = LGSObjAIGoal(fp, &ai->goals[i]); +//@@ if (retval != LGS_OK) goto lgsaigoal_fail; +//@@ } +//@@ +//@@// continue +//@@ gs_ReadInt(fp, ai->target_handle); +//@@ gs_ReadFloat(fp, ai->next_target_update_time); +//@@ gs_ReadFloat(fp, ai->dist_to_target); +//@@ gs_ReadVector(fp, ai->vec_to_target); +//@@ gs_ReadFloat(fp, ai->last_check_see_target_time); +//@@ gs_ReadVector(fp, ai->last_see_target_pos); +//@@ gs_ReadFloat(fp, ai->last_see_target_time); +//@@ gs_ReadFloat(fp, ai->weapon_speed); +//@@ gs_ReadFloat(fp, ai->next_melee_time); +//@@ gs_ReadFloat(fp, ai->last_render_time); +//@@ gs_ReadFloat(fp, ai->next_flinch_time); +//@@ gs_ReadInt(fp, ai->status_reg); +//@@ gs_ReadInt(fp, ai->flags); +//@@ gs_ReadInt(fp, ai->notify_flags); +//@@ +//@@// notify events (MUST BE WRITTEN OUT AT A LATER DATE) +//@@ +//@@// Normalized movement and facing information +//@@ gs_ReadVector(fp, ai->movement_dir); +//@@ gs_ReadVector(fp, ai->rot_thrust_vector); +//@@ gs_ReadFloat(fp, ai->fov); +//@@ gs_ReadInt(fp, ai->anim_sound_handle); +//@@ gs_ReadFloat(fp, ai->frustration); +//@@ gs_ReadFloat(fp, ai->curiousity); +//@@ gs_ReadFloat(fp, ai->fire_spread); +//@@ gs_ReadFloat(fp, ai->agression); +//@@ gs_ReadFloat(fp, ai->night_vision); +//@@ gs_ReadFloat(fp, ai->fog_vision); +//@@ gs_ReadFloat(fp, ai->lead_accuracy); +//@@ gs_ReadFloat(fp, ai->lead_varience); +//@@ gs_ReadFloat(fp, ai->fight_team); +//@@ gs_ReadFloat(fp, ai->fight_same); +//@@ gs_ReadFloat(fp, ai->hearing); +//@@ gs_ReadFloat(fp, ai->roaming); +//@@ gs_ReadFloat(fp, ai->life_preservation); +//@@ gs_ReadShort(fp, ai->awareness); +//@@ + +// saves ai goals +//@@int LGSObjAIGoal(CFILE *fp, goal *g) +//@@{ +//@@ int i; +//@@ sbyte used; +//@@ +//@@ gs_ReadByte(fp, used); +//@@ g->used = used ? true : false; +//@@ if (!g->used) +//@@ return LGS_OK; +//@@ +//@@ gs_ReadInt(fp, g->type); +//@@ gs_ReadByte(fp, g->activation_level); +//@@ gs_ReadShort(fp, g->influence); +//@@ +//@@// write goal info +//@@ switch (g->type) +//@@ { +//@@ case AIG_HIDE_FROM_OBJ: +//@@ gs_ReadInt(fp, g->g_info.handle); +//@@ gs_ReadFloat(fp, g->g_info.time); +//@@ break; +//@@ +//@@ case AIG_WANDER_AROUND: +//@@ gs_ReadInt(fp, g->g_info.roomnum); +//@@ break; +//@@ +//@@ case AIG_DODGE_OBJ: +//@@ case AIG_MOVE_RELATIVE_OBJ: +//@@ gs_ReadInt(fp, g->g_info.handle); +//@@ break; +//@@ +//@@ case AIG_MOVE_RELATIVE_OBJ_VEC: +//@@ gs_ReadInt(fp, g->g_info.handle); +//@@ gs_ReadInt(fp, g->g_info.subtype); +//@@ break; +//@@ +//@@ case AIG_GUARD_AREA: +//@@ gs_ReadVector(fp, g->g_info.pos); +//@@ break; +//@@ +//@@ case AIG_GET_TO_OBJ: +//@@ gs_ReadInt(fp, g->g_info.handle); +//@@ gs_ReadVector(fp, g->g_info.pos); +//@@ break; +//@@ +//@@ case AIG_GET_TO_POS: +//@@ gs_ReadInt(fp, g->g_info.roomnum); +//@@ gs_ReadVector(fp, g->g_info.pos); +//@@ break; +//@@ +//@@ case AIG_FOLLOW_PATH: // This must be fixed -- chrishack +//@@ gs_ReadInt(fp, g->g_info.id); +//@@ break; +//@@ } +//@@ +//@@// write goal enabler +//@@ gs_ReadByte(fp, g->num_enablers); +//@@ for (i = 0; i < g->num_enablers; i++) +//@@ { +//@@ gs_ReadByte(fp, g->enabler[i].enabler_type); +//@@ +//@@ switch (g->enabler[i].enabler_type) +//@@ { +//@@ case AIE_AI_STATUS_FLAG: +//@@ gs_ReadInt(fp, g->enabler[i].flags); +//@@ break; +//@@ } +//@@ +//@@ gs_ReadFloat(fp, g->enabler[i].percent_enable); +//@@ gs_ReadFloat(fp, g->enabler[i].check_interval); +//@@ gs_ReadFloat(fp, g->enabler[i].last_check_time); +//@@ gs_ReadByte(fp, g->enabler[i].bool_next_enabler_op); +//@@ } +//@@ +//@@// continue +//@@ gs_ReadFloat(fp, g->circle_distance); +//@@ gs_ReadInt(fp, g->status_reg); +//@@ gs_ReadFloat(fp, g->start_time); +//@@ gs_ReadFloat(fp, g->next_path_time); +//@@ gs_ReadFloat(fp, g->dist_to_goal); +//@@ gs_ReadVector(fp, g->vec_to_target); +//@@ gs_ReadFloat(fp, g->last_check_see_target_time); +//@@ gs_ReadVector(fp, g->last_see_target_pos); +//@@ gs_ReadFloat(fp, g->last_see_target_time); +//@@ gs_ReadFloat(fp, g->next_target_update_time); +//@@ gs_ReadShort(fp, g->flags); +//@@ +//@@ return LGS_OK; +//@@} + +// Object effect_info +//@@ gs_ReadInt(fp, ei->type_flags); +//@@ gs_ReadFloat(fp, ei->alpha); +//@@ +//@@// if (op->type == OBJ_POWERUP) { +//@@ gs_ReadFloat(fp, ei->last_object_hit_time); +//@@ gs_ReadInt(fp, ei->last_object_hit); +//@@// } +//@@// if (ei->type_flags & EF_DEFORM) { +//@@ gs_ReadFloat(fp, ei->deform_range); +//@@ gs_ReadFloat(fp, ei->deform_time); +//@@// } +//@@// if (ei->type_flags & EF_VOLUME_CHANGING) { +//@@ gs_ReadFloat(fp, ei->volume_change_time); +//@@ gs_ReadVector(fp, ei->volume_old_pos); +//@@ gs_ReadFloat(fp, ei->volume_old_room); +//@@// } +//@@// if (ei->type_flags & EF_VOLUME_LIT) { +//@@ gs_ReadByte(fp, ei->dynamic_this_frame); +//@@ gs_ReadFloat(fp, ei->dynamic_red); +//@@ gs_ReadFloat(fp, ei->dynamic_green); +//@@ gs_ReadFloat(fp, ei->dynamic_blue); +//@@// } +//@@// if (ei->type_flags & EF_CLOAKED) { +//@@ gs_ReadFloat(fp, ei->cloak_time); +//@@// } +//@@// if (ei->type_flags & EF_COLORED) { +//@@ gs_ReadFloat(fp, ei->color_time); +//@@ gs_ReadFloat(fp, ei->r); +//@@ gs_ReadFloat(fp, ei->g); +//@@ gs_ReadFloat(fp, ei->b); +//@@// } +//@@// if (ei->type_flags & EF_NAPALMED) { +//@@ gs_ReadFloat(fp, ei->damage_time); +//@@ gs_ReadFloat(fp, ei->damage_per_second); +//@@ gs_ReadFloat(fp, ei->last_damage_time); +//@@ gs_ReadInt(fp, ei->damage_handle); +//@@// } +//@@// if (ei->type_flags & EF_FREEZE) { +//@@ gs_ReadFloat(fp, ei->freeze_scalar); +//@@// } +//@@// if (ei->type_flags & EF_LINE_ATTACH) { +//@@ gs_ReadInt(fp, ei->attach_line_handle); +//@@// } +//@@ } + +// Object WBs +//@@ gs_ReadFloat(fp, dwb->last_fire_time); +//@@ gs_ReadByte(fp, dwb->cur_firing_mask); +//@@ +//@@ for (j = 0; j < MAX_WB_TURRETS;j++) +//@@ { +//@@ gs_ReadFloat(fp, dwb->norm_turret_angle[j]); +//@@ gs_ReadFloat(fp, dwb->turret_next_think_time[j]); +//@@ gs_ReadByte(fp, dwb->turret_direction[j]); +//@@ } +//@@ +//@@ gs_ReadByte(fp, dwb->wb_anim_mask); +//@@ gs_ReadFloat(fp, dwb->wb_anim_frame); +//@@ gs_ReadVector(fp, dwb->cur_target); +//@@ gs_ReadByte(fp, dwb->upgrade_level); +//@@ gs_ReadInt(fp, dwb->flags); + diff --git a/Descent3/localization.cpp b/Descent3/localization.cpp new file mode 100644 index 000000000..4697ee776 --- /dev/null +++ b/Descent3/localization.cpp @@ -0,0 +1,807 @@ +/* +* $Logfile: /DescentIII/main/localization.cpp $ +* $Revision: 18 $ +* $Date: 11/05/99 4:05p $ +* $Author: Jeff $ +* +* Localization implementation +* +* $Log: /DescentIII/main/localization.cpp $ + * + * 18 11/05/99 4:05p Jeff + * fixed bug with 'retry english' closing the file and then trying to read + * it + * + * 17 10/12/99 11:04a Jeff + * load english string table if foreign strings are not found + * + * 16 5/05/99 4:33p Jeff + * fixed Linux filename fix when opening file for second time + * + * 15 5/04/99 8:50p Jeff + * safety precautions + * + * 14 4/26/99 5:17p Jeff + * loading string tables is filename friendly (for Linux)...filename must + * be all lowercase + * + * 13 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 12 2/22/99 3:19p Jeff + * added function to get current language + * + * 11 2/11/99 11:02p Jeff + * fixed bug with aux string table loading + * + * 10 2/11/99 10:35p Jeff + * added support for multiple languages in string table files + * + * 9 1/19/99 1:26p Jeff + * handled \r + * + * 8 1/18/99 12:03p Jeff + * return true if 0 strings are loaded from a correct string table, not + * false + * + * 7 1/13/99 4:05p Jeff + * when loading a script dll, a string table is looked for and + * automatically loaded and passed to the newly loaded dll on init for + * use. + * + * 6 6/26/98 4:52p Jeff + * fixed stupid + * + * 5 6/26/98 4:00p Jeff + * added parsing of escape characters for CreateStringTable() + * + * 4 6/17/98 3:28p Jeff + * + * 3 6/16/98 10:37a Jeff + * fixed bug in growstring class, parse escape chars + * + * 2 6/12/98 5:55p Jeff + * initial creation +* +* $NoKeywords: $ +*/ + +#include +#include +#include +#include +#include "game.h" +#include "descent.h" +#include "mono.h" +#include "CFILE.H" +#include "localization.h" +#include "mem.h" +#include "ddio.h" + +typedef struct +{ + char *tag; + int length; +}tLangTag; + +tLangTag Language_tags[] = { + {"!=!",-1}, //English + {"!G!",-1}, //German + {"!S!",-1}, //Spanish + {"!I!",-1}, //Italian + {"!F!",-1}, //French +}; +int Num_languages = sizeof(Language_tags)/sizeof(tLangTag); + +// The following data, in the anonymous namespace, are static to this file +namespace +{ + int Localization_language = -1; + + int String_table_size = 0; + char **String_table = NULL; + + //list of the string table files, they will be loaded in the order they are listed + char *String_table_list[] = {"D3.STR",NULL}; + + const char *_Error_string = "!!ERROR MISSING STRING!!"; + const char *_Empty_string = "\0"; +} + +void Localization_SetLanguage(int type) +{ + ASSERT(type>=0 && type Num_languages means it's the start of a string that begins with that language + +#define MAX_LINE_LENGTH 1024 +#define MAX_STRING_LENGTH 8*MAX_LINE_LENGTH +#define MAX_TAG_LENGTH 3 + +int GetTotalStringCount(void); +int LoadStringFile(char *filename,int starting_offset); +sbyte _parse_line_information(char *line); +char *_parse_string_tag(char *buffer); +char *_parse_escape_chars(char *buffer); + +//Call this to load up the string tables into memory +//Returns the number of strings loaded, if this is 0, then the program MUST not continue +int LoadStringTables(void) +{ + static bool called = false; + int old_language; + + if(called){ + //Only call this guy once + Int3(); + return 0; + } + called = true; + + old_language = Localization_language; + + int string_count = GetTotalStringCount(); + if(string_count==0) + { + // attempt to load english version of language, so there is something + if(Localization_language!=LANGUAGE_ENGLISH) + { + Localization_language = LANGUAGE_ENGLISH; + string_count = GetTotalStringCount(); + } + + if(string_count==0) + { + return 0; + } + } + + String_table_size = 0; + + //malloc our array of char * + String_table = (char **)mem_malloc(sizeof(char *)*string_count); + if(!String_table) + { + Localization_language = old_language; + return 0; + } + + for(int tcount=0;tcount string_count) + { + Localization_language = old_language; + return 0; + } + runcount += temp; + index++; + } + + if(runcount==0) + { + Localization_language = old_language; + return 0; + } + + String_table_size = runcount; + Localization_language = old_language; + + atexit(FreeStringTables); + return String_table_size; +} + +//Deallocates all the memory used for the string tables +void FreeStringTables(void) +{ + DestroyStringTable(String_table,String_table_size); + String_table = NULL; +} + +char *GetStringFromTable(int index) +{ + if( (index<0) || (index>=String_table_size) ) + return const_cast( _Error_string ); + + if(!String_table[index]) + return const_cast( _Empty_string ); + + return String_table[index]; +} + +void FixFilenameCase(char *filename,char *newfile) +{ + char path[_MAX_PATH],file[_MAX_FNAME],ext[256]; + ddio_SplitPath(filename,path,file,ext); + + char *p; + + p = file; + while( *p ) { *p = tolower(*p); p++; }; + p = ext; + while( *p ) { *p = tolower(*p); p++; }; + + strcat(file,ext); + + if(strlen(path)>0) + ddio_MakePath(newfile,path,file,NULL); + else + strcpy(newfile,file); +} + +//Given a filename, pointer to a char * array and a pointer to an int, +//it will load the string table and fill in the information +//returns true on success +bool CreateStringTable(char *filename,char ***table,int *size) +{ + ASSERT(filename); + ASSERT(Localization_language!=-1); + if(!filename) + { + if(table) + *table = NULL; + if(size) + *size = 0; + return false; + } + CFILE *file; + + char fname[_MAX_PATH]; + FixFilenameCase(filename,fname); + file = cfopen(fname,"rt"); + if(!file) + { + if(table) + *table = NULL; + if(size) + *size = 0; + return false; + } + + int old_language,scount; + char tempbuffer[MAX_STRING_LENGTH]; + + old_language = Localization_language; + +try_english: + + //first found out how many strings total in the file + scount = 0; + + while(!cfeof(file)){ + cf_ReadString(tempbuffer,MAX_LINE_LENGTH+1,file); + if(_parse_line_information(tempbuffer)==Localization_language) + scount++; + } + cfclose(file); + + if(scount==0) + { + // no strings found, if we are searching a non-English version, then + // load english versions + if(Localization_language!=LANGUAGE_ENGLISH) + { + Localization_language = LANGUAGE_ENGLISH; + // open the file again + file = cfopen(fname,"rt"); + goto try_english; + } + + // no strings found + Localization_language = old_language; + mprintf((0,"Localization: Warning, 0 strings found in %s\n",filename)); + *table = NULL; + *size = 0; + return true; + } + + *size = scount; + char **strtable; + + //malloc our array of char * + *table = (char **)mem_malloc(sizeof(char *)*scount); + if(!*table) + { + if(table) + *table = NULL; + if(size) + *size = 0; + Localization_language = old_language; + return false; + } + + strtable = *table; + for(int tcount=0;tcount=198) + scount = scount; + + line_info = _parse_line_information(tempbuffer); + + switch(line_info) + { + case STAG_CONTINUE: + if(reading_string){ + //we need to add on to the working buffer + string += _parse_escape_chars(tempbuffer); + } + break; + case STAG_EMPTY: + //empty line !ACK! What should I do...do a new line char or skip?...skip for now + break; + case STAG_COMMENT: + //ignore this line + if(reading_string){ + //ok we're done with the string, finish it and put it in the string table + string.GetString(&strtable[scount]); + scount++; + string.Destroy(); + } + reading_string = false; + break; + default: + { + if(line_info>=0 && line_info0) && (table) ){ + for(int i=0;i=198) + scount = scount; + + line_info = _parse_line_information(buffer); + + switch(line_info) + { + case STAG_CONTINUE: + if(reading_string){ + //we need to add on to the working buffer + string += _parse_escape_chars(buffer); + } + break; + case STAG_EMPTY: + //empty line !ACK! What should I do...do a new line char or skip?...skip for now + break; + case STAG_COMMENT: + //ignore this line + if(reading_string){ + //ok we're done with the string, finish it and put it in the string table + string.GetString(&String_table[scount+starting_offset]); + scount++; + string.Destroy(); + } + reading_string = false; + break; + default: + { + if(line_info>=0 && line_info=0 && i='0') && (buffer[b_index]<='9') ){ + //we have a number, so parse the value + ubyte value = 0; + while( (buffer[b_index]>='0') && (buffer[b_index]<='9') ){ + value = (value*10) + buffer[b_index]-'0'; + b_index++; + } + //adjust b_index + b_index--; + tempbuffer[t_index] = value; + } + break; + } + b_index++; + t_index++; + } + } + tempbuffer[t_index] = '\0'; + strcpy(buffer,tempbuffer); + return buffer; +} + + +GrowString::GrowString() +{ + root.string_data = NULL; + root.next = NULL; + curr = &root; +} +GrowString::~GrowString() +{ + Destroy(); +} + +void GrowString::operator += (char *str) +{ + if(!str) + return; + if(root.string_data){ + tbufferinfo *node; + node = (tbufferinfo *)mem_malloc(sizeof(tbufferinfo)); + if(!node) + return; + node->string_data = (char *)mem_malloc(strlen(str)+2); + if(!node->string_data){ + mem_free(node); + return; + } + sprintf(node->string_data,"\n%s",str); + curr->next = node; + node->next = NULL; + curr = node; + }else{ + root.string_data = (char *)mem_malloc(strlen(str)+1); + if(!root.string_data) + return; + strcpy(root.string_data,str); + root.next = NULL; + } +} + +void GrowString::Destroy(void) +{ + if(root.string_data) + mem_free(root.string_data); + root.string_data = NULL; + + tbufferinfo *c,*next; + c = next = root.next; + while(c) + { + next = c->next; + if(c->string_data) + mem_free(c->string_data); + mem_free(c); + c = next; + } + root.next = NULL; + root.string_data = NULL; + curr = &root; +} + +GrowString GrowString::operator + (char *str) +{ + *this += str; + return *this; +} + +GrowString GrowString::operator + (GrowString &gs) +{ + char *str = NULL; + gs.GetString(&str); + *this += str; + if(str) + mem_free(str); + return *this; +} + +void GrowString::operator += (GrowString &gs) +{ + char *str = NULL; + gs.GetString(&str); + *this += str; + if(str) + mem_free(str); +} + +void GrowString::operator = (char *str) +{ + Destroy(); + *this += str; +} + +void GrowString::operator = (GrowString &gs) +{ + char *str = NULL; + gs.GetString(&str); + *this = str; + if(str) + mem_free(str); +} + +void GrowString::GetString(char **str) +{ + *str = NULL; + int size = Length(); + if(size==-1) + return; + + tbufferinfo *next; + next = root.next; + + *str = (char *)mem_malloc(size+1); + + strcpy(*str,root.string_data); + next = root.next; + while(next){ + if(next->string_data){ + strcat(*str,next->string_data); + } + next = next->next; + } +} + +int GrowString::Length(void) +{ + if(!root.string_data) + return -1; + + tbufferinfo *next; + next = root.next; + int size = strlen(root.string_data); + while(next){ + if(next->string_data) + size += strlen(next->string_data); + next = next->next; + } + return size; +} \ No newline at end of file diff --git a/Descent3/localization.h b/Descent3/localization.h new file mode 100644 index 000000000..1b1d6d193 --- /dev/null +++ b/Descent3/localization.h @@ -0,0 +1,78 @@ +/* +* $Logfile: /DescentIII/main/localization.h $ +* $Revision: 4 $ +* $Date: 2/22/99 3:19p $ +* $Author: Jeff $ +* +* Localization system header +* +* $Log: /DescentIII/main/localization.h $ + * + * 4 2/22/99 3:19p Jeff + * added function to get current language + * + * 3 2/11/99 10:35p Jeff + * added support for multiple languages in string table files + * + * 2 6/12/98 5:55p Jeff + * initial creation +* +* $NoKeywords: $ +*/ + +#ifndef __LOCALIZATION_H__ +#define __LOCALIZATION_H__ + +#define LANGUAGE_ENGLISH 0 +#define LANGUAGE_GERMAN 1 +#define LANGUAGE_SPANISH 2 +#define LANGUAGE_ITALIAN 3 +#define LANGUAGE_FRENCH 4 +void Localization_SetLanguage(int type); +int Localization_GetLanguage(void); + +//Call this to load up the string tables into memory +//Returns the number of strings loaded, if this is 0, then the program MUST not continue +int LoadStringTables(void); + +//Deallocates all the memory used for the string tables +void FreeStringTables(void); + +//Returns a pointer to the string at the index location from the string table +//if it is a bad index given, then the pointer to the error string "ERROR MISSING STRING" is given +char *GetStringFromTable(int index); + +//Given a filename, pointer to a char * array and a pointer to an int, +//it will load the string table and fill in the information +//returns true on success +bool CreateStringTable(char *filename,char ***table,int *size); +//Given a string table and it's count of strings, it will free up it's memory +void DestroyStringTable(char **table,int size); + +//GrowString class +//handles a string of increasing size (using +,=,+=) +typedef struct tbufferinfo{ + char *string_data; + tbufferinfo *next; +}tbufferinfo; + +class GrowString +{ +public: + GrowString(); + ~GrowString(); + void Destroy(void); + void operator += (char *str); + GrowString operator + (char *str); + GrowString operator + (GrowString &gs); + void operator += (GrowString &gs); + void operator = (char *str); + void operator = (GrowString &gs); + void GetString(char **str); + int Length(void); +private: + tbufferinfo root; + tbufferinfo *curr; +}; + +#endif \ No newline at end of file diff --git a/Descent3/loki_utils.c b/Descent3/loki_utils.c new file mode 100644 index 000000000..7b6fd1830 --- /dev/null +++ b/Descent3/loki_utils.c @@ -0,0 +1,523 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "loki_utils.h" + +#define BAIL_IF_MACRO(x, y, z) if (x) return (z); +#define BAIL_MACRO(x, z) return (z); + +#if MACOSX + +#include +#include +#include +#include +#include +#include + +/* + * Code based on sample from Apple Developer Connection: + * http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm + */ + +static int darwinIsWholeMedia(io_service_t service) +{ + int retval = 0; + CFTypeRef wholeMedia; + + if (!IOObjectConformsTo(service, kIOMediaClass)) + return(0); + + wholeMedia = IORegistryEntryCreateCFProperty(service, + CFSTR(kIOMediaWholeKey), + kCFAllocatorDefault, 0); + if (wholeMedia == NULL) + return(0); + + retval = CFBooleanGetValue(wholeMedia); + CFRelease(wholeMedia); + + return retval; +} /* darwinIsWholeMedia */ + + +static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort) +{ + int retval = 0; + CFMutableDictionaryRef matchingDict; + kern_return_t rc; + io_iterator_t iter; + io_service_t service; + + if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL) + return(0); + + rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter); + if ((rc != KERN_SUCCESS) || (!iter)) + return(0); + + service = IOIteratorNext(iter); + IOObjectRelease(iter); + if (!service) + return(0); + + rc = IORegistryEntryCreateIterator(service, kIOServicePlane, + kIORegistryIterateRecursively | kIORegistryIterateParents, &iter); + + if (!iter) + return(0); + + if (rc != KERN_SUCCESS) + { + IOObjectRelease(iter); + return(0); + } /* if */ + + IOObjectRetain(service); /* add an extra object reference... */ + + do + { + if (darwinIsWholeMedia(service)) + { + if ( (IOObjectConformsTo(service, kIOCDMediaClass)) || + (IOObjectConformsTo(service, kIODVDMediaClass)) ) + { + retval = 1; + } /* if */ + } /* if */ + IOObjectRelease(service); + } while ((service = IOIteratorNext(iter)) && (!retval)); + + IOObjectRelease(iter); + IOObjectRelease(service); + + return(retval); +} /* darwinIsMountedDisc */ + +int loki_getmountpoint(const char *device, char *mountpoint, int max_size) +{ + const char *devPrefix = "/dev/"; + int prefixLen = strlen(devPrefix); + const char *dev = device + prefixLen; + mach_port_t masterPort = 0; + struct statfs *mntbufp; + int i, mounts; + + if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS) + return(0); + + mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */ + for (i = 0; i < mounts; i++) + { + char *dev = mntbufp[i].f_mntfromname; + char *mnt = mntbufp[i].f_mntonname; + + if (strcmp(dev, device) != 0) + continue; + + dev += prefixLen; + if (darwinIsMountedDisc(dev, masterPort)) + { + strncpy(mountpoint, mnt, max_size); + mountpoint[max_size-1] = '\0'; + return 1; + } /* if */ + } /* for */ + + return 0; +} /* loki_getmountpoint */ + +#else // not MACOSX + +#include + +int loki_getmountpoint(const char *device, char *mountpoint, int max_size) +{ + FILE *mounts = NULL; + struct mntent *ent = NULL; + int retval = 0; + + mounts = setmntent("/etc/mtab", "r"); + if (mounts == NULL) + return 0; + + while ( (ent = getmntent(mounts)) != NULL ) + { + if (strcmp(ent->mnt_dir, device) == 0) + { + strncpy(mountpoint, ent->mnt_fsname, max_size); + mountpoint[max_size-1] = '\0'; + retval = 1; + break; + } + } /* while */ + + endmntent(mounts); + + return(retval); +} +#endif + +char *__PHYSFS_platformCurrentDir(void) +{ + int allocSize = 0; + char *retval = NULL; + char *ptr; + + do + { + allocSize += 100; + ptr = (char *) realloc(retval, allocSize); + if (ptr == NULL) + { + if (retval != NULL) + free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval = ptr; + ptr = getcwd(retval, allocSize); + } while (ptr == NULL && errno == ERANGE); + + if (ptr == NULL && errno) + { + /* + * getcwd() failed for some reason, for example current + * directory not existing. + */ + if (retval != NULL) + free(retval); + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); + } /* if */ + + return(retval); +} /* __PHYSFS_platformCurrentDir */ + + +/* + * See where program (bin) resides in the $PATH specified by (envr). + * returns a copy of the first element in envr that contains it, or NULL + * if it doesn't exist or there were other problems. PHYSFS_SetError() is + * called if we have a problem. + * + * (envr) will be scribbled over, and you are expected to free() the + * return value when you're done with it. + */ +static char *findBinaryInPath(const char *bin, char *envr) +{ + size_t alloc_size = 0; + char *exe = NULL; + char *start = envr; + char *ptr; + + BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL); + BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL); + + do + { + size_t size; + ptr = strchr(start, ':'); /* find next $PATH separator. */ + if (ptr) + *ptr = '\0'; + + size = strlen(start) + strlen(bin) + 2; + if (size > alloc_size) + { + char *x = (char *) realloc(exe, size); + if (x == NULL) + { + if (exe != NULL) + free(exe); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + alloc_size = size; + exe = x; + } /* if */ + + /* build full binary path... */ + strcpy(exe, start); + if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/')) + strcat(exe, "/"); + strcat(exe, bin); + + if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */ + { + strcpy(exe, start); /* i'm lazy. piss off. */ + return(exe); + } /* if */ + + start = ptr + 1; /* start points to beginning of next element. */ + } while (ptr != NULL); + + if (exe != NULL) + free(exe); + + return(NULL); /* doesn't exist in path. */ +} /* findBinaryInPath */ + + +char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname) +{ + const char *envr = getenv(varname); + char *retval = NULL; + + if (envr != NULL) + { + retval = (char *) malloc(strlen(envr) + 1); + if (retval != NULL) + strcpy(retval, envr); + } /* if */ + + return(retval); +} /* __PHYSFS_platformCopyEnvironmentVariable */ + + +static char *getUserNameByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_name != NULL)) + { + retval = (char *) malloc(strlen(pw->pw_name) + 1); + if (retval != NULL) + strcpy(retval, pw->pw_name); + } /* if */ + + return(retval); +} /* getUserNameByUID */ + + +static char *getUserDirByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_dir != NULL)) + { + retval = (char *) malloc(strlen(pw->pw_dir) + 1); + if (retval != NULL) + strcpy(retval, pw->pw_dir); + } /* if */ + + return(retval); +} /* getUserDirByUID */ + + +char *__PHYSFS_platformGetUserName(void) +{ + char *retval = getUserNameByUID(); + if (retval == NULL) + retval = __PHYSFS_platformCopyEnvironmentVariable("USER"); + return(retval); +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME"); + if (retval == NULL) + retval = getUserDirByUID(); + return(retval); +} /* __PHYSFS_platformGetUserDir */ + + +static int appendDirSep(char **dir) +{ + const char *dirsep = "/"; + char *ptr; + + if (strcmp((*dir + strlen(*dir)) - strlen(dirsep), dirsep) == 0) + return(1); + + ptr = (char *)realloc(*dir, strlen(*dir) + strlen(dirsep) + 1); + if (!ptr) + { + free(*dir); + return(0); + } /* if */ + + strcat(ptr, dirsep); + *dir = ptr; + return(1); +} /* appendDirSep */ + + +static char *unixCalcBaseDir(const char *argv0) +{ + /* If there isn't a path on argv0, then look through the $PATH for it. */ + + char *retval; + char *envr; + + if (strchr(argv0, '/') != NULL) /* default behaviour can handle this. */ + return(NULL); + + envr = __PHYSFS_platformCopyEnvironmentVariable("PATH"); + BAIL_IF_MACRO(!envr, NULL, NULL); + retval = findBinaryInPath(argv0, envr); + free(envr); + return(retval); +} + + +static char *calcBaseDir(const char *argv0) +{ + const char *dirsep = "/"; + char *retval; + char *ptr; + + /* + * See if the platform driver wants to handle this for us... + */ + retval = unixCalcBaseDir(argv0); + if (retval != NULL) + return(retval); + + /* + * Determine if there's a path on argv0. If there is, that's the base dir. + */ + ptr = strstr(argv0, dirsep); + if (ptr != NULL) + { + char *p = ptr; + size_t size; + while (p != NULL) + { + ptr = p; + p = strstr(p + 1, dirsep); + } /* while */ + + size = (size_t) (ptr - argv0); + retval = (char *) malloc(size + 1); + if (retval == NULL) return NULL; + memcpy(retval, argv0, size); + retval[size] = '\0'; + return(retval); + } /* if */ + + /* + * Last ditch effort: it's the current working directory. (*shrug*) + */ + retval = __PHYSFS_platformCurrentDir(); + if(retval != NULL) { + return(retval); + } + + /* + * Ok, current directory doesn't exist, use the root directory. + * Not a good alternative, but it only happens if the current + * directory was deleted from under the program. + */ + retval = (char *) malloc(strlen(dirsep) + 1); + strcpy(retval, dirsep); + return(retval); +} + + +static char *basepath = NULL; +static char *prefpath = NULL; + +const char *loki_getdatapath(void) +{ + return basepath; +} + +const char *loki_getprefpath(void) +{ + return prefpath; +} + +void loki_initialize(int argc, char **argv, char *desc) +{ + char resolved_path[MAXPATHLEN]; + char *ptr = __PHYSFS_platformGetUserDir(); + if (!ptr) + { + fprintf(stderr, "ERROR: environment variable HOME not set?!"); + exit(42); + } + + #if MACOSX + const char *extra = "Library/Application Support/Descent 3"; + #else + const char *extra = ".loki/descent3"; + #endif + + prefpath = (char *) malloc(strlen(ptr) + strlen(extra) + 3); + strcpy(prefpath, ptr); + if (prefpath[strlen(prefpath)-1] != '/') + strcat(prefpath, "/"); + + free(ptr); + + strcat(prefpath, extra); + ptr = prefpath; + while ((ptr = strchr(ptr, '/')) != NULL) + { + *ptr = '\0'; + mkdir(prefpath, S_IRWXU); + *ptr = '/'; + ptr++; + } + mkdir(prefpath, S_IRWXU); + + basepath = calcBaseDir(argv[0]); + if (basepath == NULL) + { + fprintf(stderr, "ERROR: Couldn't find game directory!\n"); + exit(43); + } + + if (realpath(prefpath, resolved_path)) + { + ptr = (char *) realloc(prefpath, strlen(resolved_path) + 1); + if (ptr) + { + strcpy(ptr, resolved_path); + prefpath = ptr; + } + } + + if (realpath(basepath, resolved_path)) + { + ptr = (char *) realloc(basepath, strlen(resolved_path) + 1); + if (ptr) + { + strcpy(ptr, resolved_path); + basepath = ptr; + } + } + + // Strip out Application Bundle... + #if MACOSX + { + // !!! FIXME + } + #endif + + //printf("base path: %s\n", basepath); + //printf("pref path: %s\n", prefpath); + + printf("%s\n", desc); +} /* loki_initialize */ + +/* end of loki_utils.c ... */ + diff --git a/Descent3/loki_utils.h b/Descent3/loki_utils.h new file mode 100644 index 000000000..a527d774a --- /dev/null +++ b/Descent3/loki_utils.h @@ -0,0 +1,25 @@ +/* stubs for the little bits of loki_utils we actually need. */ + +#ifndef _INCL_LOKI_UTILS_H_ +#define _INCL_LOKI_UTILS_H_ + +// never liked these register option thingys... +#define loki_register_stdoptions() +#define loki_registeroption(x, y, z) +#define loki_setgamename(x, y, z) + +#ifdef __cplusplus +extern "C" { +#endif + +void loki_initialize(int argc, char **argv, char *desc); +int loki_getmountpoint(const char *device, char *mountpoint, int max_size); +const char *loki_getdatapath(void); +const char *loki_getprefpath(void); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Descent3/macmain.cpp b/Descent3/macmain.cpp new file mode 100644 index 000000000..b2516b008 --- /dev/null +++ b/Descent3/macmain.cpp @@ -0,0 +1,391 @@ +/* + * $Logfile: /DescentIII/Main/macmain.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:30 $ + * $Author: kevinb $ + * + * Macintosh Main and any macintosh specific code that MUST be in the main application + * goes here. + * + * $Log: macmain.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:30 kevinb + * initial 1.5 import + * + * + * 6 4/12/00 6:41p Matt + * From Duane for 1.4 + * + * 5 3/21/00 11:56a Matt + * Merge of Duane's post-1.3 changes. + * + * 3 10/22/99 11:13a Matt + * Mac merge + * + * 4 5/18/97 6:08 PM Jeremy + * changed compiler directive use for profiler, apparently __profile__ is + * always defined for some reason under codewarrior (regardless of project + * preferences setting) + * + * 3 5/17/97 6:51 PM Jeremy + * implemented default values for database entries of net_directory, + * local_directory, and user_name, and put support for code profiling into + * place. + * + * 2 5/15/97 1:42 AM Jeremy + * some initialization mprintf's and moved the keyboard handler into the + * osd3macobject from the osmacobject + * + * 1 5/14/97 11:26 PM Jeremy + * initial checkin + * + * $NoKeywords: $ + */ + +// ========================= +// ANSI Headers +// ========================= +#include +#include + +// ========================= +// Metrowerks Headers +// ========================= + +#ifdef USE_PROFILER +#include +//#include "SpotlightAPI.h" +#endif +#ifdef NEEDED +// ========================= +// Macintosh System Headers +// ========================= +#include +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include "agl.h" +#include "glide.h" +#include + +#include + +// ========================= +// Descent 3 Headers +// ========================= +#include "descent.h" +#include "gameos.h" +#include "pserror.h" +#include "mem.h" +#include "mono.h" +#include "ddio_mac.h" +#include "ddio.h" +#include "maccontroller.h" + +#include "descent.h" +#include "gameos.h" +#include "texture.h" +#include "application.h" +#include "appdatabase.h" +#include "pserror.h" +#include "init.h" +#include "dedicated_server.h" +#include "args.h" +#include "game.h" + +const char *English_strings[] = { + "Descent 3 under Windows NT requires version 4.0 or greater of NT to run.", + "Descent 3 requires Windows 9x, NT 4.0 or greater to run.", + "", + "You must install DirectX through the Descent 3 Launcher before continuing.", + "You must install at least Service Pack 3 to run Descent 3 under Windows NT 4.0.", + "Failed to retrieve DirectX version.", + "Descent 3 requires DirectX 3 or greater on this machine.", + "This version of Windows NT doesn't have DirectX 3 or greater installed.", + "Your processor and system must support Katmai to run this game." +}; + +const char *French_strings[] = { + "L'exTcution de Descent 3 sous Windows NT nTcessite la version 4.0 ou ultTrieure de NT.", + "L'exTcution de Descent 3 nTcessite Windows 9x, NT 4.0 ou ultTrieur.", + "", + "Vous devez installer DirectX a partir de l'installateur Descent 3 avant de continuer.", + "Vous devez installer au moins Service Pack 3 pour exTcuter Descent 3 sous Windows NT 4.0.", + "+chec de la rTcupTration de DirectX.", + "Descent 3 nTcessite DirectX 3 ou ultTrieur sur ce systFme.", + "Cette version de Windows NT n'est pas munie de DirectX 3 ou ultTrieur.", + "Votre processeur et votre systFme doivent prendre en charge Katmai pour exTcuter ce jeu." +}; + +const char *German_strings[] = { + "Descent3 unter Windows NT ben÷tigt fnr die Ausfnhrung die NT-Version 4.0 oder h÷her", + "Descent 3 ben÷tigt fnr die Ausfnhrung Windows 9x, NT 4.0 oder h÷her.", + "", + "Sie mnssen DirectX nber den Descent 3 Starter installieren, bevor Sie fortsetzen.", + "Sie mnssen mindestens Service Paket 3 installieren, um Descent 3 unter Windows NT 4.0 ausfnhren zu k÷nnen.", + "Die DirectX Version konnte nicht abgerufen werden.", + "Descent 3 ben÷tigt DirectX 3 oder h÷her auf diesem Computer.", + "In dieser Windows NT Version ist DirectX 3 oder h÷her nicht installiert.", + "Ihr Prozessor und System mu¯ Katmai unterstntzen, um dieses Spiel auszufnhren." +}; + +const char *Italian_strings[] = { + "Descent 3 per Windows NT richiede la versione NT 4.0 o superiore.", + "Descent 3 funziona solo con Windows 9x, NT 4.0 o superiore.", + "", + "Prima di prosegure installare DirectX per mezzo di Descent 3 Launcher.", + "Per utilizzare Descent 3 sotto Windows NT 4.0 occorre installare Service Pack 3 o sup.", + "Versione di DirectX non trovata.", + "Descent 3 richiede l'installazione di DirectX 3 o superiore.", + "DirectX 3 o superiore non trovato in questa versione di Windows NT.", + "Per questo gioco occorrono un processore e un sistema che supportino Katmai." +}; + +const char *Spanish_strings[] = { + "Descent 3 bajo Windows NT requiere version 4.0 o mejor para correr.", + "Descent 3 requiere Windows 9x, NT 4.0 o mejor para correr.", + "", + "Debe instalar DirectX desde el lanzador de Descent 3 antes de continuar.", + "Debe instalar por lo menos Service Pack 3 para correr Descent 3 bajo Windows NT 4.0.", + "Falla en la detección de la versión de DirectX.", + "Descent 3 requiere DirectX 3 o mejor en el ordenador.", + "Esta versión de Windows NT no tiene DirectX 3 o mejor instalado.", + "Vuestro procesador y ordenador deben soportar Katmai para correr este juego." +}; + +// --------------------------------------------------------------------------- +// Define our operating system specific extensions to the gameos system +// --------------------------------------------------------------------------- + +class oeD3MacApp: public oeMacApplication +{ + bool shutdown, final_shutdown; + int old_screen_mode; + +public: + oeD3MacApp(const char *name, unsigned flags):oeMacApplication(name, flags) {}; + + oeD3MacApp(void); + + virtual void os_init(); + virtual unsigned defer(); + + virtual ~oeD3MacApp() + { + final_shutdown = true; + }; + + void run() + { + Descent3(); + }; +}; + +class oeD3MacDatabase: public oeMacAppDatabase +{ +public: + oeD3MacDatabase():oeMacAppDatabase() + { + Database = this; + }; +}; + +// --------------------------------------------------------------------------- +// D3MacObject operating system specific initialization + +oeD3MacApp::oeD3MacApp(void) +{ + os_ConsoleInit(); + mprintf((0, "\n\n\n\n\n")); + mprintf((0, "==========================\n")); + mprintf((0, " A NEW D3 DAY HAS BEGUN!\n")); + mprintf((0, "==========================\n\n\n\n")); + + mprintf((0,"Created D3 MacOS Object (Macintosh Toolbox Managers initialized).\n")); +} + +void +oeD3MacApp::os_init() +{ + oeMacApplication::init(); +} + +unsigned +oeD3MacApp::defer() +{ + unsigned packet; + + packet = oeMacApplication::defer(); + +#ifdef DAJ_DEBUG + MacKeyboardHandler(); + MacInSprocketHandler(); +#endif + return packet; +} + +void D3End() +{ +// if (Descent) { +// delete Descent; +// } +} + +void TestForExtensions(void) +{ + if((ProcPtr)ISpInit == (ProcPtr)kUnresolvedCFragSymbolAddress) { + ::ShowCursor(); + ::Alert(150, NULL); + ::ExitToShell(); + } +// NumVersion ISpVer = ISpGetVersion(); + + if((ProcPtr)DSpStartup == (ProcPtr)kUnresolvedCFragSymbolAddress) { + ::ShowCursor(); + ::Alert(151, NULL); + ::ExitToShell(); + } +// NumVersion DSpVer = DSpGetVersion(); + + if((ProcPtr)SSpListener_New == (ProcPtr)kUnresolvedCFragSymbolAddress) { + ::ShowCursor(); + ::Alert(152, NULL); + ::ExitToShell(); + } +// NumVersion SMVer = SndSoundManagerVersion(); + +#ifdef USE_OPENGL + if((ProcPtr)aglChoosePixelFormat == (ProcPtr)kUnresolvedCFragSymbolAddress) { + ::ShowCursor(); + ::Alert(153, NULL); + ::ExitToShell(); + } +#endif +#ifdef USE_GLIDE + if((ProcPtr)grGlideInit == (ProcPtr)kUnresolvedCFragSymbolAddress) { + ::ShowCursor(); + ::Alert(154, NULL); + ::ExitToShell(); + } +#endif + if((ProcPtr)InitOpenTransport == (ProcPtr)kUnresolvedCFragSymbolAddress) { + ::ShowCursor(); + ::Alert(155, NULL); + ::ExitToShell(); + } +/* + if((ProcPtr)OpenMovieFile == (ProcPtr)kUnresolvedCFragSymbolAddress) { + ::ShowCursor(); + ::Alert(156, NULL); + ::ExitToShell(); + } +*/ +} + +// --------------------------------------------------------------------------- +// MacMain +// creates all the OS objects and then runs Descent 3. +// this is all this function should do. +// --------------------------------------------------------------------------- +int sioux_inited = false; +int +main(int argc, char *argv[]) +{ + EventRecord event; + int found; + + MaxApplZone(); + + FlushEvents(everyEvent, 0); + InitGraf(&qd.thePort); + InitFonts(); + InitWindows(); + InitMenus(); + TEInit(); + InitDialogs(0); + + InitCursor(); + + setup_sioux(); + mprintf((0, "debug started\n")); + sioux_inited = true; + + TestForExtensions(); + +// GetNextEvent(0, &event); // brings mac app to the front +#if 0 +//#ifdef DAJ_DEBUG + //DAJ spocket test + GetOSEvent(everyEvent - diskMask, &event); + if (event.modifiers & controlKey) { + argc = ccommand(&argv); + for(int i=1;iinit(); + d3->run(); + + + //¥ Close the Metrowerks Code Profiler + #ifdef USE_PROFILER + if (profilerInitted) + { + mprintf((2, "Dumping profiler results and closing profiler\n")); + ProfilerDump("\p::Z_D3_0"); + ProfilerTerm(); + } + #endif + + delete d3; //DAJ LEAKFIX + + interrupt_Close(); + FlushEvents(everyEvent, 0); + + return 0; +} diff --git a/Descent3/marker.cpp b/Descent3/marker.cpp new file mode 100644 index 000000000..62eabb758 --- /dev/null +++ b/Descent3/marker.cpp @@ -0,0 +1,93 @@ +#include "object.h" +#include "marker.h" +#include "polymodel.h" +#include "player.h" +#include "multi.h" +#include "game.h" +#ifdef _DEBUG +#include "AIMain.h" +#endif + +int Marker_polynum; +int Marker_message=0; + +char MarkerMessages[MAX_PLAYERS*2][MAX_MARKER_MESSAGE_LENGTH]; + +// Drops a market at the players current position +void DropMarker(char *message) +{ + int cur_marker_num; + + #ifdef _DEBUG + char teststring[80]; + strcpy(teststring, message); + teststring[8] = '\0'; + + if(strcmp("ai debug", teststring) == 0) + { + sscanf(message, "ai debug %d", &AI_debug_robot_index); + mprintf((0, "Debug robot is object index %d\n", AI_debug_robot_index)); + } + #endif + + if (Player_object->type!=OBJ_PLAYER) + return; + + if (Game_mode & GM_MULTI) + { + MultiSendRequestForMarker(message); + return; + } + + int limit=8; + + if (Players[Player_num].num_markers>=limit) + { + // Delete the oldest marker + int found=-1; + float low_time=999999999.0f; + for (int i=0;i<=Highest_object_index;i++) + { + if (Objects[i].type==OBJ_MARKER && Objects[i].parent_handle==Player_object->handle && Objects[i].creation_timeroomnum,&Player_object->pos,&Player_object->orient,Player_object->handle); + if (objnum>=0) + { + mprintf ((0,"Marker %d created!\n",cur_marker_num)); + strcpy (MarkerMessages[(Player_num*2)+cur_marker_num],message); + } + else + mprintf ((0,"Marker NOT created!\n")); + +} + +// Resets markers before a level stars +void ResetMarkers () +{ + Marker_message=0; + +} + +// Inits markers at game load +void InitMarkers () +{ + Marker_polynum=LoadPolyModel("marker.oof",0); + ASSERT (Marker_polynum>=0); +} diff --git a/Descent3/marker.h b/Descent3/marker.h new file mode 100644 index 000000000..a4f7fdc7c --- /dev/null +++ b/Descent3/marker.h @@ -0,0 +1,23 @@ +#ifndef MARKER_H +#define MARKER_H + +#include "player.h" + +#define MAX_MARKER_MESSAGE_LENGTH 40 + +// Drops a marker at the players current position +void DropMarker(char *message); + +// Resets markers before a level stars +void ResetMarkers (); + +// Inits markers at game load +void InitMarkers (); + +extern int Marker_polynum; +extern int Marker_message; + +extern char MarkerMessages[MAX_PLAYERS*2][MAX_MARKER_MESSAGE_LENGTH]; + + +#endif \ No newline at end of file diff --git a/Descent3/matcen.cpp b/Descent3/matcen.cpp new file mode 100644 index 000000000..815227d98 --- /dev/null +++ b/Descent3/matcen.cpp @@ -0,0 +1,2309 @@ +/* + * $Logfile: /DescentIII/main/matcen.cpp $ + * $Revision: 71 $ + * $Date: 4/19/00 5:35p $ + * $Author: Matt $ + * + * Matcen code + * + * $Log: /DescentIII/main/matcen.cpp $ + * + * 71 4/19/00 5:35p Matt + * From Duane for 1.4 + * Added include + * + * 70 3/20/00 12:14p Matt + * Merge of Duane's post-1.3 changes. + * Check for bad array index. This change duplicates Jason's fix of the + * same bug, but Duane's code looks a little more thorough. + * + * 69 1/24/00 11:49p Jason + * Fixed array indexing error with matcens + * + * 68 10/21/99 4:50p Matt + * Mac merge + * + * 67 8/15/99 12:22p Gwar + * more matcen support in NEWEDITOR (almost done) + * + * 66 8/12/99 7:55p Gwar + * fixed a compiler warning + * + * 65 8/11/99 9:15p Gwar + * matcen support for NEWEDITOR + * + * 64 7/28/99 4:05p Kevin + * Mac + * + * 63 5/19/99 3:24p Jason + * fixed wrong ordering of InitObjectScripts and MultiSendObject + * + * 62 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 61 5/09/99 1:15a Jason + * got rid of weird post production effect + * + * 60 5/05/99 4:51p Jason + * don't tile matcens so much + * + * 59 5/04/99 8:51p Chris + * Fixed homing missile problem (a PTMC matcen created robot would have a + * parent of a non-AI object). Homers use the ultimate parent. Fun bug. + * :) + * + * 58 5/03/99 1:15p Jason + * fixed matcen procedural effect + * + * 57 5/02/99 10:26p Matt + * Fixed silly little bug + * + * 56 5/02/99 7:30p Jason + * added "none" type procedural creation effect + * + * 55 5/02/99 6:55p Jeff + * fixed bug where a sound based off an object would not play because the + * object wasn't created yet (Jason) + * + * 54 4/29/99 4:50p Chris + * Fixed matcen and Osiris created objects so thier ambient sounds play + * + * 53 4/25/99 3:16p Chris + * Fixed a coop bug with matcen functioning on the server + * + * 52 4/23/99 11:28a Jason + * fixed matcen viseffect problem + * + * 51 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 50 4/19/99 3:45a Jeff + * fixed min/max problem + * + * 49 4/18/99 10:55p Chris + * matcens now have defualts sounds + * + * 48 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 47 4/12/99 6:15p Samir + * Sound priorities pass 1 + * + * 46 4/02/99 3:58p Jason + * sped up vis effect stuff + * + * 45 3/22/99 11:24a Chris + * Matcen generates an osiris call even if no object is produced + * + * 44 3/05/99 9:57a Chris + * Fixed bugs with walkers + * + * 43 3/04/99 3:24p Chris + * FIxed matcens and walkers + * + * 42 2/24/99 10:58a Dan + * CHRIS - Fixed the external room matcen problem + * + * 41 2/19/99 4:49p Dan + * Matcens *should* be able to be placed on external rooms + * + * 40 2/19/99 4:41p Dan + * External rooms can now be matcens... (Objects are created in the + * connection room) + * + * 39 2/13/99 4:24p Chris + * Fixed an off by one bug in num_prod_type + * + * 38 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 37 2/09/99 1:23p Matt + * Added object-generated notification event + * + */ + +#ifdef NEWEDITOR +#include "globals.h" +#endif + +#include "matcen.h" +#include "game.h" +#include "sounds.h" +#include "ssl_lib.h" +#include "string.h" +#include "soundload.h" +#include "room.h" +#include "object.h" +#include "objinfo.h" +#include "findintersection.h" +#ifndef NEWEDITOR +#include "multi.h" +#endif +#include +#include "fireball.h" +#include "weapon.h" +#include "polymodel.h" +#include "hlsoundlib.h" +#include "viseffect.h" +#include "viseffect_external.h" +#include "damage.h" +#include "PHYSICS.H" +#include "mem.h" +#include "ObjScript.h" +#ifndef NEWEDITOR +#include "player.h" +#include "osiris_dll.h" +#endif +#include "psrand.h" +#include "demofile.h" + +#ifdef __LINUX__ +#define max(a,b) ((a>b)?a:b) +#elif defined(MACINTOSH) +#include "Macros.h" +#endif + +// Beginning of the real file +#define LEVEL_DATA_MATCEN_VERSION 1 +#define GAME_DATA_MATCEN_VERSION 1 + +#if (defined(EDITOR) || defined(NEWEDITOR)) + +char *MatcenEffectStrings[NUM_MATCEN_EFFECTS] = +{ +"Line lightning", +"Line sinewave", +"Procedural lightning", +"None" +}; + +#endif + +int Num_matcens = 0; +matcen *Matcen[MAX_MATCENS]; +bool Matcen_created = false; + +matcen::matcen() +{ + int i; + + m_prod_mode = MMODE_NOTPROD; + m_cur_saturation_count=0; + + m_num_prod_types = 0; + m_control_type = MPC_SCRIPT; + m_type = MT_UNASSIGNED; + m_creation_effect = MEFFECT_LINE_LIGHTNING; + + // Get lightning handle in case we need it + m_creation_texture=FindTextureName ("Matcen Lightning"); + if (m_creation_texture==-1) + m_creation_texture=0; + + m_num_spawn_pnts = 0; + + m_roomnum = MATCEN_ERROR; + + for(i = 0; i < MAX_SPAWN_PNTS; i++) + { + m_spawn_pnt[i] = MATCEN_ERROR; + } + + m_max_prod = 0; + + for(i = 0; i < MAX_PROD_TYPES; i++) + { + m_prod_type[i] = MATCEN_ERROR; + m_prod_time[i] = 1.0f; + m_prod_priority[i] = 100; + m_max_prod_type[i] = 3; + } + + m_sounds[MATCEN_ACTIVE_SOUND] = FindSoundName("AmbMatCenRun"); + m_sounds[MATCEN_DISABLE_SOUND] = -1; + m_sounds[MATCEN_PROD_SOUND] = FindSoundName("AmbMatCenProduce"); + + m_speed_multi = 1.0f; + + m_max_alive_children = -1; + m_num_alive = 0; + m_alive_list = NULL; + + m_preprod_time = 1.5f; + m_postprod_time = 1.0f; + + m_status = 0; + + m_create_pnt = Zero_vector; + m_create_room = MATCEN_ERROR; + + m_cached_prod_index = -1; + m_cached_prod_time = 0.0f; + + Reset(); +} + +matcen::~matcen() +{ + if(m_alive_list) + { + mem_free(m_alive_list); + } +} + +bool matcen::SetMaxProd(int max_p) +{ + if(max_p < -1) + { + return false; + } + + m_max_prod = max_p; + + ComputeNextProdInfo(); + + return true; +} + +int matcen::GetMaxProd() +{ + return m_max_prod; +} + +#ifndef NEWEDITOR + +bool matcen::StartObjProd() +{ + if(m_cached_prod_index < 0 || m_max_prod_type[m_cached_prod_index] >= m_num_prod_type[m_cached_prod_index]) + ComputeNextProdInfo(); + + if(m_status & (MSTAT_DONE_PROD | MSTAT_ACTIVE_PAUSE | MSTAT_DISABLED)) + return false; + + if(m_prod_mode != MMODE_NOTPROD) + return false; + + if(!(m_status & (MSTAT_ACTIVE))) + return false; + + if(m_max_prod <= m_num_prod && m_max_prod != -1) + { + mprintf((0, "MATCEN: Done\n")); + m_status |= MSTAT_DONE_PROD; + return false; + } + + if(m_cached_prod_index < 0) + return false; + + // Guarentees against designer error + pos_state pos; + matrix idmat = Identity_matrix; + + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + pos.position = &m_create_pnt; + pos.orient = &idmat; + pos.roomnum = croom; + + Sound_system.StopSoundLooping(Sound_system.Play3dSound(m_sounds[MATCEN_PROD_SOUND],SND_PRIORITY_HIGH, &pos)); + + m_status &= ~MSTAT_NEVER_PROD; + + m_prod_mode_time = 0.0f; + m_prod_mode = MMODE_PREPROD; + m_cur_saturation_count=0; + + return true; +} + +bool matcen::DoObjProd() +{ + int objnum; + int p_handle = OBJECT_HANDLE_NONE; + bool script_initted=false; + + ASSERT(m_prod_mode == MMODE_PREPROD); + ASSERT(m_type == MT_ROOM || m_type == MT_OBJECT); + + if(m_type == MT_OBJECT) + { + object *parent = ObjGet(m_objref); + ASSERT(parent); + + if(parent->control_type == CT_AI || parent->type == OBJ_PLAYER) + { + p_handle = parent->handle; + } + } + + ComputeCreatePnt(); + matrix orient; + vector fvec = Player_object->pos - m_create_pnt; + vm_NormalizeVector(&fvec); + + vm_VectorToMatrix(&orient, &fvec, NULL, NULL); + + if(m_prod_type[m_cached_prod_index] >= 0) + { + int croom = m_create_room; + Matcen_created = true; + + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + objnum = ObjCreate(Object_info[m_prod_type[m_cached_prod_index]].type, m_prod_type[m_cached_prod_index], croom, &m_create_pnt, &orient, p_handle); + Matcen_created = false; + + if(objnum >= 0 && (Object_info[m_prod_type[m_cached_prod_index]].flags & OIF_CONTROL_AI) && (Object_info[m_prod_type[m_cached_prod_index]].ai_info->movement_type != MC_FLYING)) + { + vector gp; + object *obj = &Objects[objnum]; + + if(PhysCalcGround(&gp, NULL, obj, 0)) + { + fvi_info hit_info; + fvi_query fq; + int fate; + vector start = obj->pos; + vector end = obj->pos - 2000.0f * obj->orient.uvec; + + fq.p0 = &start; + fq.p1 = &end; + fq.startroom = obj->roomnum; + + fq.rad = 0.0f; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS; + fq.thisobjnum = OBJNUM(obj); + fq.ignore_obj_list = NULL; + + fate = fvi_FindIntersection(&fq, &hit_info); + + if(fate != HIT_NONE) + { + float ps; + float pr; + float diff; + + ps = (gp - obj->pos) * obj->orient.uvec; + pr = (hit_info.hit_pnt - obj->pos) * obj->orient.uvec; + + if(ps != pr) + { + diff = ps - pr; + obj->pos -= diff * obj->orient.uvec; + ObjSetPos(obj, &obj->pos, obj->roomnum, NULL,false); + obj->flags |= OF_MOVED_THIS_FRAME; + + m_create_pnt = obj->pos; + } + } + } + } + + if(objnum >= 0) + { + // Make it fade in for a second + Objects[objnum].effect_info->type_flags|=EF_FADING_IN; + Objects[objnum].effect_info->fade_time=1.0; + Objects[objnum].effect_info->fade_max_time=1.0; + + object *obj = &Objects[objnum]; + + if (Game_mode & GM_MULTI) + { + //ASSERT (Netgame.local_role==LR_SERVER); + if((Netgame.local_role==LR_SERVER)) + { + MultiSendObject (&Objects[objnum], 0); + } + } + + InitObjectScripts (obj); + script_initted=true; + + + if(IS_GENERIC(obj->type)) + { + int ambient_sound = Object_info[obj->id].sounds[GSI_AMBIENT]; + if (ambient_sound != SOUND_NONE_INDEX) + { + Sound_system.Play3dSound(ambient_sound, SND_PRIORITY_LOWEST, obj); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ambient_sound, objnum, SND_PRIORITY_LOW); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ambient_sound, objnum, SND_PRIORITY_LOW); + } + } + } + } + else + { + objnum = -1; + } + + //Send notification event + tOSIRISEventInfo ei; + if(objnum >= 0) + ei.evt_matcen_create.it_handle = Objects[objnum].handle; + else + ei.evt_matcen_create.it_handle = OBJECT_HANDLE_NONE; + ei.evt_matcen_create.id = FindMatcenIndex(m_name); + Osiris_CallLevelEvent(EVT_MATCEN_CREATE,&ei); + + + if(objnum >= 0) + { + if (!script_initted) + InitObjectScripts (&Objects[objnum]); + AddToAliveList(Objects[objnum].handle); + + m_last_prod_objref = Objects[objnum].handle; + } + else + { + m_last_prod_objref = OBJECT_HANDLE_NONE; + } + + m_status |= MSTAT_CREATE_OBJ_FRAME; + + m_last_prod_type_index = m_cached_prod_index; + m_num_prod_type[m_cached_prod_index]++; + m_num_prod++; + + m_prod_mode_time = 0.0f; + m_prod_mode = MMODE_POSTPROD; + m_cur_saturation_count=0; + + return true; +} + +bool matcen::DoAliveListFrame() +{ + int i, j; + + for(i = 0; i < m_num_alive; i++) + { + if(!ObjGet(m_alive_list[i])) + { + for(j = i; j < m_num_alive - 1; j++) + { + m_alive_list[j] = m_alive_list[j + 1]; + } + + m_num_alive--; + i--; // Example - 6 is now 5 (after 5 was deleted); so we need to check 5 again... + } + } + + return true; +} + +bool matcen::AddToAliveList(int objref) +{ + if(m_max_alive_children > 0 && m_max_alive_children > m_num_alive) + { + m_alive_list[m_num_alive++] = objref; + } + + return false; +} + +bool matcen::FinishObjProd() +{ + ASSERT(m_prod_mode == MMODE_POSTPROD); + + m_prod_mode_time = 0.0f; + m_prod_mode = MMODE_NOTPROD; + m_cur_saturation_count=0; + + ComputeNextProdInfo(); + + return false; +} + +#endif // NEWEDITOR + +bool matcen::SetAttach(int attach) +{ + m_roomnum = attach; + ComputeCreatePnt(); + + return true; +} + +int matcen::GetAttach() +{ + return m_roomnum; +} + +char matcen::GetAttachType() +{ + return m_type; +} + +bool matcen::SetAttachType(char type) +{ + if(type == MT_OBJECT || type == MT_ROOM || type == MT_UNASSIGNED) + { + m_type = type; + return true; + } + + return false; +} + +char matcen::GetControlType() +{ + return m_control_type; +} + +bool matcen::SetControlType(char type) +{ + if(type >= 0 && type < MAX_MATCEN_CONTROL_TYPES) + { + m_control_type = type; + return true; + } + + return false; +} + + +char matcen::GetNumSpawnPnts() +{ + return m_num_spawn_pnts; +} + +bool matcen::SetNumSpawnPnts(char num_s) +{ + if(num_s >= 0 && num_s <= MAX_SPAWN_PNTS) + { + m_num_spawn_pnts = num_s; + ComputeCreatePnt(); + return true; + } + + return false; +} + +bool matcen::ComputeCreatePnt() +{ + if(m_status & MSTAT_MANUAL_UPDATE_CREATE_PNT) + { + return true; + } + else if(m_type != MT_UNASSIGNED) + { + float spawn_dist; + + if(m_cached_prod_index >= 0 && m_cached_prod_index < m_num_prod_types && m_prod_type[m_cached_prod_index] >= 0 && m_prod_type[m_cached_prod_index] <= MAX_OBJECT_TYPES && Object_info[m_prod_type[m_cached_prod_index]].type != OBJ_NONE) + { + spawn_dist = Object_info[m_prod_type[m_cached_prod_index]].size + 1.0f; + } + else + { + spawn_dist = 8.0f; + } + + if(m_type == MT_ROOM) + { + bool f_computed = false; + + if(!(m_roomnum >= 0 && m_roomnum <= Highest_room_index && Rooms[m_roomnum].used)) + goto error; + + if(m_num_spawn_pnts) + { + int i; + int num_valid_faces = 0; + room *rp = &Rooms[m_roomnum]; + + m_create_pnt = Zero_vector; + + for(i = 0; i < m_num_spawn_pnts; i++) + { + if(m_spawn_pnt[i] >= 0 && m_spawn_pnt[i] < rp->num_faces) + { + face *fp = &rp->faces[m_spawn_pnt[i]]; + int y; + + m_spawn_vec[num_valid_faces].x = m_spawn_vec[num_valid_faces].y = m_spawn_vec[num_valid_faces].z = 0; + + for (y=0; y < fp->num_verts; y++) + m_spawn_vec[num_valid_faces] += rp->verts[fp->face_verts[y]]; + + m_spawn_vec[num_valid_faces] /= fp->num_verts; + m_create_pnt += m_spawn_vec[num_valid_faces]; + + m_spawn_normal[num_valid_faces] = fp->normal; + + num_valid_faces++; + } + } + + if(num_valid_faces) + { + m_create_pnt /= num_valid_faces; + + for(i = 0; i < num_valid_faces; i++) + { + //Compute the vector to the m_create_pnt + vector to_dist = m_create_pnt - m_spawn_vec[i]; + float dot = m_spawn_normal[i] * to_dist; + + if(dot < spawn_dist) + { + float add_dist = spawn_dist - dot; + m_create_pnt += (add_dist * m_spawn_normal[i]); + } + } + + f_computed = true; + } + } + + if(!f_computed) + { + m_create_pnt = Rooms[m_roomnum].path_pnt; + } + + m_create_room = m_roomnum; + } + else + { + ASSERT(m_type == MT_OBJECT); + object *master_obj = ObjGet(m_objref); + bool f_computed = false; + + if(!master_obj) + goto error; + + if(m_num_spawn_pnts) + { + int i; + int num_valid_gps = 0; + + m_create_pnt = Zero_vector; + + for(i = 0; i < m_num_spawn_pnts; i++) + { + if(WeaponCalcGun(&m_spawn_vec[num_valid_gps], &m_spawn_normal[num_valid_gps], master_obj, m_spawn_pnt[i])) + { + m_create_pnt += m_spawn_vec[num_valid_gps]; + num_valid_gps++; + } + } + + if(num_valid_gps) + { + m_create_pnt /= num_valid_gps; + + for(i = 0; i < num_valid_gps; i++) + { + //Compute the vector to the m_create_pnt + vector to_dist = m_create_pnt - m_spawn_vec[i]; + float dot = m_spawn_normal[i] * to_dist; + + if(dot < spawn_dist) + { + float add_dist = spawn_dist - dot; + m_create_pnt += (add_dist * m_spawn_normal[i]); + } + } + + f_computed = true; + } + } + + if(!f_computed) + { + m_create_pnt = master_obj->pos; + } + + // Determine the correct creation room + fvi_query fq; + fvi_info hit_info; + + fq.p0 = &master_obj->pos; + fq.startroom = master_obj->roomnum; + fq.p1 = &m_create_pnt; + fq.rad = 0.0f; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_IGNORE_WALLS; + + fvi_FindIntersection(&fq,&hit_info); + m_create_room = hit_info.hit_room; + } + } + + error: + return false; +} + +bool matcen::GetCreatePnt(vector *pnt) +{ + ComputeCreatePnt(); + *pnt = m_create_pnt; + return true; +} + +bool matcen::SetCreatePnt(vector *pnt) +{ + if(m_status & MSTAT_MANUAL_UPDATE_CREATE_PNT) + { + m_create_pnt = *pnt; + return true; + } + + return false; +} + +int matcen::GetCreateRoom() +{ + ComputeCreatePnt(); + + return m_create_room; +} + +bool matcen::SetCreateRoom(int room) +{ + if((m_status & MSTAT_MANUAL_UPDATE_CREATE_PNT)) // chrishack -- add room validation + { + m_create_room = room; + return true; + } + + return false; +} + +int matcen::GetSpawnPnt(char s_index) +{ + if(s_index >= 0 && s_index < MAX_SPAWN_PNTS) + return m_spawn_pnt[s_index]; + + return MATCEN_ERROR; +} + +bool matcen::SetSpawnPnt(char s_index, int s_value) +{ + if(s_index >= 0 && s_index < MAX_SPAWN_PNTS) + { + m_spawn_pnt[s_index] = s_value; + ComputeCreatePnt(); + return true; + } + + return false; +} + +bool matcen::SetName(char *name) +{ + strcpy(m_name, name); + + return true; +} + +void matcen::GetName(char *name) +{ + strcpy(name, m_name); +} + +void matcen::SaveData(CFILE *fp) +{ + int i, j; + int len; + + cf_WriteInt(fp, MATCEN_LOADSAVE_VERSION); + + cf_WriteShort(fp, MAX_SPAWN_PNTS); + cf_WriteShort(fp, MAX_PROD_TYPES); + cf_WriteShort(fp, MAX_MATCEN_SOUNDS); + + len = strlen(m_name) + 1; // Accounts for NULL charactor + cf_WriteShort(fp, len); + for(i = 0; i < len; i++) + { + cf_WriteByte(fp, m_name[i]); + } + + cf_WriteByte(fp, m_num_prod_types); + cf_WriteByte(fp, m_control_type); + cf_WriteByte(fp, m_type); + cf_WriteByte(fp, m_creation_effect); + cf_WriteShort (fp,m_creation_texture); + + cf_WriteInt(fp, m_num_spawn_pnts); + + cf_WriteInt(fp, m_roomnum); + + cf_WriteFloat(fp, m_create_pnt.x); + cf_WriteFloat(fp, m_create_pnt.y); + cf_WriteFloat(fp, m_create_pnt.z); + + cf_WriteInt(fp, m_create_room); + + for(i = 0; i < MAX_SPAWN_PNTS; i++) + { + cf_WriteInt(fp, m_spawn_pnt[i]); + + cf_WriteFloat(fp, m_spawn_vec[i].x); + cf_WriteFloat(fp, m_spawn_vec[i].y); + cf_WriteFloat(fp, m_spawn_vec[i].x); + + cf_WriteFloat(fp, m_spawn_normal[i].x); + cf_WriteFloat(fp, m_spawn_normal[i].y); + cf_WriteFloat(fp, m_spawn_normal[i].x); + } + + cf_WriteInt(fp, m_max_prod); + + for(i = 0; i < MAX_PROD_TYPES; i++) + { + int type = m_prod_type[i]; + + if(type >= 0) + { + len = strlen(Object_info[type].name) + 1; // Accounts for NULL charactor + cf_WriteShort(fp, len); + for(j = 0; j < len; j++) + { + cf_WriteByte(fp, Object_info[type].name[j]); + } + } + else + { + cf_WriteShort(fp, 1); + cf_WriteByte(fp, '\0'); + } + + cf_WriteFloat(fp, m_prod_time[i]); + cf_WriteInt(fp, m_prod_priority[i]); + cf_WriteInt(fp, m_max_prod_type[i]); + } + + cf_WriteShort(fp, m_max_alive_children); + if(m_max_alive_children > 0) + { + cf_WriteShort(fp, m_num_alive); + for(i = 0; i < m_num_alive; i++) + { + cf_WriteInt(fp, m_alive_list[i]); + } + } + + cf_WriteFloat(fp, m_preprod_time); + cf_WriteFloat(fp, m_postprod_time); + + // Convert these to names + for(i = 0; i < MAX_MATCEN_SOUNDS; i++) + { + len = strlen(Sounds[m_sounds[i]].name) + 1; // Accounts for NULL charactor + cf_WriteShort(fp, len); + for(j = 0; j < len; j++) + { + cf_WriteByte(fp, Sounds[m_sounds[i]].name[j]); + } + } + + cf_WriteFloat(fp, m_speed_multi); + + cf_WriteByte(fp, m_prod_mode); + cf_WriteFloat(fp, m_prod_mode_time); + + cf_WriteInt(fp, m_status); + + cf_WriteInt(fp, m_num_prod); + cf_WriteInt(fp, m_last_prod_type_index); + cf_WriteFloat(fp, m_last_prod_finish_time); + + cf_WriteInt(fp, m_cached_prod_index); + cf_WriteFloat(fp, m_cached_prod_time); + + cf_WriteFloat(fp, m_next_active_check_time); + cf_WriteByte(fp, m_last_active_check_result); + + cf_WriteInt(fp, m_last_prod_objref); + + for(i = 0; i < MAX_PROD_TYPES; i++) + { + cf_WriteInt(fp, m_num_prod_type[i]); + } + + cf_WriteInt(fp, m_sound_active_handle); +} + +extern short texture_xlate[]; +void matcen::LoadData(CFILE *fp) +{ + int i, j; + int len; + + short max_spawn_pnts; + short max_prod_types; + short max_matcen_sounds; + + int version = cf_ReadInt(fp); + + max_spawn_pnts = cf_ReadShort(fp); + ASSERT(max_spawn_pnts <= MAX_SPAWN_PNTS); + max_prod_types = cf_ReadShort(fp); + ASSERT(max_prod_types <= MAX_PROD_TYPES); + max_matcen_sounds = cf_ReadShort(fp); + ASSERT(max_matcen_sounds <= MAX_MATCEN_SOUNDS); + + len = cf_ReadShort(fp); + ASSERT(len <= MAX_MATCEN_NAME_LEN); + for(i = 0; i < len; i++) + { + m_name[i] = cf_ReadByte(fp); + } + + m_num_prod_types = cf_ReadByte(fp); + m_control_type = cf_ReadByte(fp); + m_type = cf_ReadByte(fp); + m_creation_effect = cf_ReadByte(fp); + + if (version>=2) + m_creation_texture = texture_xlate[cf_ReadShort (fp)]; + + m_num_spawn_pnts = cf_ReadInt(fp); + + m_roomnum = cf_ReadInt(fp); + + m_create_pnt.x = cf_ReadFloat(fp); + m_create_pnt.y = cf_ReadFloat(fp); + m_create_pnt.z = cf_ReadFloat(fp); + + m_create_room = cf_ReadInt(fp); + + for(i = 0; i < max_spawn_pnts; i++) + { + m_spawn_pnt[i] = cf_ReadInt(fp); + + m_spawn_vec[i].x = cf_ReadFloat(fp); + m_spawn_vec[i].y = cf_ReadFloat(fp); + m_spawn_vec[i].z = cf_ReadFloat(fp); + + m_spawn_normal[i].x = cf_ReadFloat(fp); + m_spawn_normal[i].y = cf_ReadFloat(fp); + m_spawn_normal[i].z = cf_ReadFloat(fp); + } + + m_max_prod = cf_ReadInt(fp); + + char temp_name[256]; + for(i = 0; i < max_prod_types; i++) + { + len = cf_ReadShort(fp); + for(j = 0; j < len; j++) + { + { + temp_name[j] = cf_ReadByte(fp); + } + } + + m_prod_type[i] = FindObjectIDName(temp_name); + + m_prod_time[i] = cf_ReadFloat(fp); + m_prod_priority[i] = cf_ReadInt(fp); + m_max_prod_type[i] = cf_ReadInt(fp); + } + + m_max_alive_children = cf_ReadShort(fp); + if(m_max_alive_children > 0) + { + m_num_alive = cf_ReadShort(fp); + m_alive_list = (int *) mem_malloc(sizeof(int) * m_max_alive_children); + + for(i = 0; i < m_num_alive; i++) + { + m_alive_list[i] = cf_ReadInt(fp); + } + } + else + { + m_num_alive = 0; + m_alive_list = NULL; + } + + m_preprod_time = cf_ReadFloat(fp); + m_postprod_time = cf_ReadFloat(fp); + + // Convert these to names + for(i = 0; i < max_matcen_sounds; i++) + { + char sound_name[256]; + + len = cf_ReadShort(fp); + for(j = 0; j < len; j++) + { + sound_name[j] = cf_ReadByte(fp); + } + + m_sounds[i] = FindSoundName(sound_name); + } + + m_speed_multi = cf_ReadFloat(fp); + + m_prod_mode = cf_ReadByte(fp); + m_prod_mode_time = cf_ReadFloat(fp); + + m_status = cf_ReadInt(fp); + + m_num_prod = cf_ReadInt(fp); + m_last_prod_type_index = cf_ReadInt(fp); + m_last_prod_finish_time = cf_ReadFloat(fp); + + m_cached_prod_index = cf_ReadInt(fp); + m_cached_prod_time = cf_ReadFloat(fp); + + m_next_active_check_time = cf_ReadFloat(fp); + m_last_active_check_result = (cf_ReadByte(fp) != 0); + + m_last_prod_objref = cf_ReadInt(fp); + + for(i = 0; i < max_prod_types; i++) + { + m_num_prod_type[i] = cf_ReadInt(fp); + } + + if(version >= 3) + m_sound_active_handle = cf_ReadInt(fp); +} + +char matcen::GetNumProdTypes() +{ + return m_num_prod_types; +} + +bool matcen::SetNumProdTypes(char num_prod_types) +{ + if(num_prod_types >= 0 && num_prod_types <= MAX_PROD_TYPES) + { + if((m_num_prod_types < num_prod_types || num_prod_types == -1) && (m_status & MSTAT_DONE_PROD)) + { + ComputeNextProdInfo(); + } + + m_num_prod_types = num_prod_types; + return true; + } + + return false; +} + +bool matcen::GetProdInfo(char index, int *type_id, int *priority, float *time, int *max_prod) +{ + if(index >= 0 && index < MAX_PROD_TYPES) + { + if(type_id) + *type_id = m_prod_type[index]; + + if(priority) + *priority = m_prod_priority[index]; + + if(time) + *time = m_prod_time[index]; + + if(max_prod) + *max_prod = m_max_prod_type[index]; + + return true; + } + + return false; +} + +bool matcen::SetProdInfo(char index, int *type_id, int *priority, float *time, int *max_prod) +{ + if(index >= 0 && index < MAX_PROD_TYPES) + { + if(type_id && (*type_id >= -1)) + m_prod_type[index] = *type_id; + + if(priority && (*priority >= 0)) + m_prod_priority[index] = *priority; + + if(time && (*time >= 0.0f)) + m_prod_time[index] = *time; + + if(max_prod && (*max_prod >= -1)) + { + if(*max_prod == -1 && m_max_prod_type[index] < *max_prod && (m_status & MSTAT_DONE_PROD)) + { + ComputeNextProdInfo(); + } + + m_max_prod_type[index] = *max_prod; + } + + return true; + } + + return false; +} + +float matcen::GetProdMultiplier() +{ + return m_speed_multi; +} + +bool matcen::SetProdMultiplier(float multi) +{ + if(multi >= 0.0f) + { + m_speed_multi = multi; + return true; + } + + return false; +} + +int matcen::GetStatus() +{ + return m_status; +} + +bool matcen::ComputeNextProdInfo() +{ + int i; + + if(m_status & (MSTAT_DISABLED)) + return false; + + m_status &= ~MSTAT_DONE_PROD; + + if(m_max_prod <= m_num_prod && m_max_prod != -1) + { + mprintf((0, "MATCEN: Done\n")); + m_status |= MSTAT_DONE_PROD; + return false; + } + + ASSERT(m_prod_mode == MMODE_NOTPROD); + + int cur_index; + bool f_do = true; + + if(m_status & MSTAT_RANDOM_PROD_ORDER) + { + int total_priorities = 0; + + for(i = 0; i < m_num_prod_types; i++) + { + if((m_max_prod_type[i] == -1 || m_max_prod_type[i] > m_num_prod_type[i]) && m_prod_type[i] >= -1) + { + total_priorities += m_prod_priority[i]; + } + } + + if(total_priorities <= 0) + { + f_do = false; + } + else + { + int r_val = ps_rand()%total_priorities; + bool f_looking = true; + + i = 0; + + do + { + ASSERT(i < m_num_prod_types); + + if((m_max_prod_type[i] == -1 || m_max_prod_type[i] > m_num_prod_type[i]) && m_prod_type[i] >= -1) + { + r_val -= m_prod_priority[i]; + + if(r_val <= 0) + { + cur_index = i; + f_looking = false; + } + } + + i++; + } + while(f_looking); + } + } + else + { + cur_index = -1; + int best_priority = -1; + + for(i = 0; i < m_num_prod_types; i++) + { + if((m_max_prod_type[i] == -1 || m_max_prod_type[i] > m_num_prod_type[i]) && m_prod_type[i] >= -1 && + m_prod_priority[i] > best_priority) + { + cur_index = i; + best_priority = m_prod_priority[i]; + } + } + + if(cur_index == -1) + { + f_do = false; + } + } + + if(f_do) + { + m_cached_prod_index = cur_index; + m_cached_prod_time = Gametime + m_prod_time[cur_index]; + + return true; + } + + m_cached_prod_index = -1; + m_status |= MSTAT_DONE_PROD; + return false; +} + +bool matcen::SetStatus(int status, bool f_enable) // Not all flags are settable +{ + status &= ~(MSTAT_DONE_PROD | MSTAT_NEVER_PROD); + m_last_active_check_result = (m_status & MSTAT_ACTIVE) != 0; + + if(f_enable) + { + bool f_recompute = false; + + if((m_prod_mode == MMODE_NOTPROD) && (status & MSTAT_ACTIVE) && !(m_status & MSTAT_ACTIVE)) + { + f_recompute = true; + } + + m_status |= status; + + if(f_recompute) + { + ComputeNextProdInfo(); + } + } + else + { + m_status &= ~status; + } + +#ifdef NEWEDITOR + + ASSERT(!(m_status & MSTAT_ACTIVE)); + +#else + + bool cur_active = (m_status & MSTAT_ACTIVE) != 0; + + if(cur_active && !m_last_active_check_result) + { + if(m_type == MT_OBJECT) + { + object *obj = ObjGet(m_objref); + + if(obj) + m_sound_active_handle = Sound_system.Play3dSound(m_sounds[MATCEN_ACTIVE_SOUND], SND_PRIORITY_NORMAL, obj); + } + else + { + pos_state pos; + matrix idmat = Identity_matrix; + + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + pos.position = &m_create_pnt; + pos.orient = &idmat; + pos.roomnum = croom; + + m_sound_active_handle = Sound_system.Play3dSound(m_sounds[MATCEN_ACTIVE_SOUND], SND_PRIORITY_NORMAL, &pos); + } + } + else if(!cur_active && m_last_active_check_result) + { + Sound_system.StopSoundLooping(m_sound_active_handle); + } + +#endif // NEWEDITOR + + return true; +} + +#ifndef NEWEDITOR + +void matcen::CheckActivateStatus() +{ + m_next_active_check_time = Gametime + MATCEN_ACTIVE_CHECK_RATE + (MATCEN_ACTIVE_CHECK_VARIENCE * (((float)ps_rand())/((float)RAND_MAX) - .5f)); + m_last_active_check_result = (m_status & MSTAT_ACTIVE) != 0; + + //Determine if active + if(m_status & (MSTAT_PROD_TILL_DONE)) + { + m_status |= MSTAT_ACTIVE; + } + else + { + switch(m_control_type) + { + case MPC_SCRIPT: + break; + + case MPC_WHILE_PLAYER_VISIBLE: + case MPC_AFTER_PLAYER_VISIBLE: + { + m_status &= ~MSTAT_ACTIVE; + + // Determine the correct creation room + fvi_query fq; + fvi_info hit_info; + int fate; + + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + fq.p0 = &m_create_pnt; + fq.startroom = croom; + fq.p1 = &Player_object->pos; + fq.rad = 0.0f; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_NO_RELINK | FQ_CHECK_OBJS | FQ_ONLY_DOOR_OBJ; + + fate = fvi_FindIntersection(&fq,&hit_info); + + if(fate == HIT_NONE) + m_status |= MSTAT_ACTIVE; + } + break; + + case MPC_WHILE_PLAYER_NEAR: + case MPC_AFTER_PLAYER_NEAR: + { + m_status &= ~MSTAT_ACTIVE; + + if(m_create_room == -1) + { + } + else if(ROOMNUM_OUTSIDE(m_create_room) || (m_create_room <= Highest_room_index && Rooms[m_create_room].used && (Rooms[m_create_room].flags & RF_EXTERNAL))) + { + if(vm_VectorDistance(&Player_object->pos, &m_create_pnt) <= MATCEN_OUTSIDE_NEAR_DIST) + { + m_status |= MSTAT_ACTIVE; + } + } + else if(m_create_room >= 0 && m_create_room <= Highest_room_index && Rooms[m_create_room].used) + { + room *rp = &Rooms[m_create_room]; + + if(m_create_room == Player_object->roomnum) + { + m_status |= MSTAT_ACTIVE; + } + else + { + int x; + + for(x = 0; x < rp->num_portals; x++) + { + int c_room = rp->portals[x].croom; + if(c_room < 0 && (vm_VectorDistance(&Player_object->pos, &m_create_pnt) <= MATCEN_OUTSIDE_NEAR_DIST)) + { + m_status |= MSTAT_ACTIVE; + } + else + { + if(c_room == Player_object->roomnum) + { + m_status |= MSTAT_ACTIVE; + } + } + + // Already active; so stop checking + if(m_status & MSTAT_ACTIVE) + { + break; + } + } + } + } + } + break; + + default: + Int3(); // Get Chris immediately + break; + } + } + + bool cur_active = (m_status & MSTAT_ACTIVE) != 0; + + if(cur_active && !m_last_active_check_result) + { + if(m_type == MT_OBJECT) + { + object *obj = ObjGet(m_objref); + + if(obj) + m_sound_active_handle = Sound_system.Play3dSound(m_sounds[MATCEN_ACTIVE_SOUND], SND_PRIORITY_NORMAL, obj); + } + else + { + pos_state pos; + matrix idmat = Identity_matrix; + + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + pos.position = &m_create_pnt; + pos.orient = &idmat; + pos.roomnum = croom; + + m_sound_active_handle = Sound_system.Play3dSound(m_sounds[MATCEN_ACTIVE_SOUND], SND_PRIORITY_NORMAL, &pos); + } + } + else if(!cur_active && m_last_active_check_result) + { + Sound_system.StopSoundLooping(m_sound_active_handle); + } + + if(cur_active && (m_control_type == MPC_AFTER_PLAYER_NEAR || m_control_type == MPC_AFTER_PLAYER_VISIBLE)) + m_status |= MSTAT_PROD_TILL_DONE; +} + +#define MATCEN_DAMAGE_PER_SECOND 20.0f +#define MATCEN_DAMAGE_DIST 10.0f +#define MATCEN_FORCE 60000.0f + +void matcen::DoThinkFrame() +{ + if((Game_mode & GM_MULTI) && Netgame.local_role!=LR_SERVER) + return; + + if(m_type == MT_UNASSIGNED) + return; + + // If disabled and not finishing up a production... + if((m_prod_mode == MMODE_NOTPROD) && (m_status & MSTAT_DISABLED)) + return; + + if(m_type == MT_OBJECT) + { + object *parent = ObjGet(m_objref); + + if(!parent) + { + m_prod_mode_time = 0.0f; + m_prod_mode = MMODE_NOTPROD; + m_cur_saturation_count=0; + + m_type = MT_UNASSIGNED; + return; + } + + if((m_prod_mode != MMODE_NOTPROD && (parent->movement_type == MT_PHYSICS || parent->movement_type == MT_WALKING)) || + (m_status & MSTAT_COMPUTE_CREATE_PNT_EVERY_FRAME)) + { + ComputeCreatePnt(); + } + } + + // Increment time + m_prod_mode_time += Frametime; + + // Do alive children frame + if(m_max_alive_children > 0) + { + DoAliveListFrame(); + } + + //Determine if it is time to start a production + switch(m_prod_mode) + { + case MMODE_NOTPROD: + { + if(!(m_status & (MSTAT_DONE_PROD | MSTAT_ACTIVE_PAUSE)) && (m_num_alive != m_max_alive_children)) + { + + if(Gametime >= m_next_active_check_time) + { + CheckActivateStatus(); + } + + if((m_status & MSTAT_ACTIVE) && (Gametime >= m_cached_prod_time) && (m_cached_prod_index >= 0)) + { + StartObjProd(); + } + } + else if((m_status & MSTAT_DONE_PROD) && (m_status & MSTAT_ACTIVE)) + { + Sound_system.StopSoundLooping(m_sound_active_handle); + m_status &= ~MSTAT_ACTIVE; + } + } + break; + + case MMODE_PREPROD: + { + if(m_prod_mode_time >= m_preprod_time) + DoObjProd(); + } + break; + + case MMODE_POSTPROD: + { + m_status &= ~MSTAT_CREATE_OBJ_FRAME; + + if(m_prod_mode_time >= m_postprod_time) + FinishObjProd(); + } + break; + + default: + Int3(); // get chris immediately + break; + } + + if( (!(m_status & MSTAT_NOT_HURT_PLAYER)) && (m_prod_mode == MMODE_PREPROD || m_prod_mode == MMODE_POSTPROD) ) + { + float damage = MATCEN_DAMAGE_PER_SECOND * Frametime; + if(Game_mode & GM_MULTI) + { + // Multiplayer targetting (Major difference is that robot will ignore players while infighting in single player) + for( int i = 0; i < MAX_PLAYERS; ++i ) + { + if(! ( (NetPlayers[i].flags & NPF_CONNECTED) && (NetPlayers[i].sequence >= NETSEQ_PLAYING) ) ) + continue; + + object *p = &Objects[Players[i].objnum]; + if(p->type == OBJ_GHOST) + continue; + + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + if(croom != p->roomnum) + continue; + + vector dir = p->pos - m_create_pnt; + float dist = vm_NormalizeVector(&dir) - p->size; + if(dist >= MATCEN_DAMAGE_DIST) + continue; + + if(Netgame.local_role==LR_SERVER) + { + ApplyDamageToPlayer (p, p, PD_ENERGY_WEAPON, damage); + } + + if(p == Player_object) + { + dir *= MATCEN_FORCE; + + vector movement_pos, movement_vec, pos; + PhysicsDoSimLinear( *p, pos, dir, p->mtype.phys_info.velocity, movement_vec, movement_pos, Frametime, 1 ); + } + } + } + else + { + object *p = Player_object; + if(p->type != OBJ_GHOST) + { + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + if(croom == p->roomnum) + { + vector dir = p->pos - m_create_pnt; + + float dist = vm_NormalizeVector(&dir) - p->size; + if(dist < MATCEN_DAMAGE_DIST) + { + ApplyDamageToPlayer(p, p, PD_ENERGY_WEAPON, damage); + + dir *= MATCEN_FORCE; + + vector movement_pos, movement_vec, pos; + PhysicsDoSimLinear( *p, pos, dir, p->mtype.phys_info.velocity, movement_vec, movement_pos, Frametime, 1 ); + } + } + } + } + } +} + +void matcen::DoRenderFrame() +{ + // MATCEN NOTE: The objref of the created object is valid from the creation time to the + // creation of the next object + + // Useful member variables: + // m_prod_mode -- See below + // m_last_prod_objref + // m_prod_mode_time -- Zero the first frame of the current production mode + // m_preprod_time + // m_postprod_time + + // Useful status members: + // MSTAT_CREATE_OBJ_FRAME + + //DAJ FIXES YET ANOTHER BUG WHERE THE ARRAY INDEX IS NOT CHECKED BEFORE USE (m_prod_type[m_cached_prod_index] == -1) + if (!(m_cached_prod_index >= 0 && m_cached_prod_index < m_num_prod_types && m_prod_type[m_cached_prod_index] >= 0 && m_prod_type[m_cached_prod_index] <= MAX_OBJECT_TYPES && Object_info[m_prod_type[m_cached_prod_index]].type != OBJ_NONE)) + return; + + // MATCEN NOTE: If an effect does something graphical, add it here + switch(m_prod_mode) + { + case MMODE_NOTPROD: + { + switch(m_creation_effect) + { + case MEFFECT_LINE_LIGHTNING: + default: + return; + } + } + break; + + case MMODE_PREPROD: + { + switch(m_creation_effect) + { + case MEFFECT_LINE_LIGHTNING: + { + int i; + + int oi_index=m_prod_type[m_cached_prod_index]; + + if (oi_index<0 || oi_index>=MAX_POLY_MODELS) + break; + + PageInPolymodel (Object_info[oi_index].render_handle,Object_info[oi_index].type,&Object_info[oi_index].size); + float size; + + if(oi_index >= 0) + size = Object_info[oi_index].size*1.5; + else + size = 0.0f; + + if (m_cur_saturation_count<1) + { + for(i = 0; i < m_num_spawn_pnts; i++) + { + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + m_spawn_vis_effects[m_cur_saturation_count][i]=-1; + + int visnum=VisEffectCreate (VIS_FIREBALL,LIGHTNING_BOLT_INDEX,croom,&m_create_pnt); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=.8f; + vis->lifetime=.8f; + vis->end_pos=m_spawn_vec[i]; + vis->flags=VF_USES_LIFELEFT|VF_EXPAND; + vis->velocity.x=.8f; + vis->velocity.y=1; + m_spawn_vis_effects[m_cur_saturation_count][i]=visnum; + } + + } + m_cur_saturation_count++; + } + + // Move the viseffects if needed + for (i=0;i=0 && visnumpos=m_create_pnt; + vis->end_pos=m_spawn_vec[t]; + } + + } + + + vector center = m_create_pnt; + size = (m_prod_mode_time/m_preprod_time) * size; + + if(size > 0.0) + DrawColoredDisk (¢er, 0.3f, 0.5f, 1.0f, .7f, 0, size, 1); + + } + break; + + case MEFFECT_LINE_SINE_WAVE: + { + int i; + + int oi_index=m_prod_type[m_cached_prod_index]; + + if (oi_index<0 || oi_index>=MAX_POLY_MODELS) + break; + + PageInPolymodel (Object_info[oi_index].render_handle,Object_info[oi_index].type,&Object_info[oi_index].size); + float size; + + if(oi_index >= 0) + size = Object_info[oi_index].size*1.5; + else + size = 0.0f; + + if (m_cur_saturation_count<1) + { + for(i = 0; i < m_num_spawn_pnts; i++) + { + int croom = m_create_room; + if(!ROOMNUM_OUTSIDE(m_create_room) && (Rooms[m_create_room].flags & RF_EXTERNAL)) + { + croom = GetTerrainRoomFromPos(&m_create_pnt); + } + + m_spawn_vis_effects[m_cur_saturation_count][i]=-1; + + + int visnum=VisEffectCreate (VIS_FIREBALL,SINE_WAVE_INDEX,croom,&m_create_pnt); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=1; + vis->lifetime=1; + vis->end_pos=m_spawn_vec[i]; + vis->flags=VF_USES_LIFELEFT|VF_EXPAND; + vis->velocity.x=1; + vis->velocity.y=1; + m_spawn_vis_effects[m_cur_saturation_count][i]=visnum; + } + } + + m_cur_saturation_count++; + } + + + // Move the viseffects if needed + for (i=0;i=0 && visnumpos=m_create_pnt; + vis->end_pos=m_spawn_vec[t]; + } + } + + + vector center = m_create_pnt; + size = (m_prod_mode_time/m_preprod_time) * size; + + + if(size > 0.0) + DrawColoredDisk (¢er, 0.6f, 0.4f, 1.0f, .7f, 0, size, 1); + } + break; + + case MEFFECT_PROCEDURAL_LIGHTNING: + { + int i; + + int oi_index=m_prod_type[m_cached_prod_index]; + PageInPolymodel (Object_info[oi_index].render_handle,Object_info[oi_index].type,&Object_info[oi_index].size); + float size; + + if(oi_index >= 0) + size = Object_info[oi_index].size*1.5; + else + size = 0.0f; + + if (m_cur_saturation_count=0) + { + vis_effect *vis=&VisEffects[visnum]; + + vis->end_pos=m_spawn_vec[i]; + vis->lifeleft=1; + vis->lifetime=1; + vis->flags=VF_USES_LIFELEFT|VF_EXPAND|VF_LINK_TO_VIEWER; + vis->billboard_info.width=max (1,size/2); + vis->billboard_info.texture=1; + vis->velocity.x=1; + vis->velocity.y=1; + vis->velocity.z=.5; + vis->lighting_color=GR_RGB16(255,255,255); + + vis->custom_handle=m_creation_texture; + vis->size=vm_VectorDistanceQuick(&vis->pos,&vis->end_pos); + m_spawn_vis_effects[m_cur_saturation_count][i]=visnum; + + } + } + m_cur_saturation_count++; + } + + // Move the viseffects if needed + for (i=0;i=0 && visnumpos=m_create_pnt; + vis->end_pos=m_spawn_vec[t]; + + } + } + + vector center = m_create_pnt; + size = (m_prod_mode_time/m_preprod_time) * size; + + if(size > 0.0) + DrawColoredDisk (¢er, 0.3f, 0.5f, 1.0f, .7f, 0, size, 1); + + } + break; + + default: + return; + } + } + break; + + case MMODE_POSTPROD: + { + + /*switch(m_creation_effect) + { + + + }*/ + + return; + + } + break; + + default: + Int3(); // Get chris immediately + } +} + +#endif // NEWEDITOR + +char matcen::GetCreationEffect() +{ + return m_creation_effect; +} + +bool matcen::SetCreationEffect(char effect_index) +{ + if(effect_index < 0 || effect_index >= NUM_MATCEN_EFFECTS) + return false; + + m_creation_effect = effect_index; + + + // MATCEN NOTE: Set default preprod and postprod stuff here + + // MATCEN NOTE: In addition, status flags can be set by effect type. See Chris if + // this is done as there are default functionality and script interaction issues + switch(m_creation_effect) + { + case MEFFECT_LINE_LIGHTNING: + m_preprod_time = 1.5f; + m_postprod_time = 1.0f; + break; + case MEFFECT_LINE_SINE_WAVE: + m_preprod_time = 0.25f; + m_postprod_time = 0.0f; + break; + default: + m_preprod_time = 1.5f; + m_postprod_time = 1.0f; + break; + } + + return true; +} + +void matcen::SetCreationTexture(short texnum) +{ + m_creation_texture=texnum; +} + +short matcen::GetCreationTexture() +{ + return m_creation_texture; +} + +int matcen::GetSound(char sound_type) +{ + if(sound_type >= 0 && sound_type < MAX_MATCEN_SOUNDS) + { + return m_sounds[sound_type]; + } + + return MATCEN_ERROR; +} + +bool matcen::SetSound(char sound_type, int sound_index) +{ + if(sound_type >= 0 && sound_type < MAX_MATCEN_SOUNDS) + { + m_sounds[sound_type] = sound_index; + } + return false; +} + +void matcen::Reset() +{ + int i; + + // Resets the a matcen + m_status &= ~(MSTAT_DONE_PROD | MSTAT_ACTIVE | MSTAT_ACTIVE_PAUSE | MSTAT_PROD_TILL_DONE | MSTAT_PROD_ONE_PAUSE | MSTAT_PROD_ONE_DISABLE); + + m_num_prod = 0; + m_last_prod_type_index = MATCEN_ERROR; + m_last_prod_finish_time = 0.0; + + m_last_active_check_result = false; + m_next_active_check_time = 0.0; + + m_prod_mode_time = 0.0f; + + for(i = 0; i < MAX_PROD_TYPES; i++) + { + m_num_prod_type[i] = 0; + } + + m_last_prod_objref = OBJECT_HANDLE_NONE; + + ComputeCreatePnt(); + + if(m_prod_mode == MMODE_NOTPROD) + { + m_cached_prod_index = -1; + m_cached_prod_time = 0.0f; + + ComputeNextProdInfo(); + } +} + +int matcen::GetMaxAliveChildren() +{ + return m_max_alive_children; +} + +bool matcen::SetMaxAliveChildren(int max_alive) +{ + // If the max_alive value is valid... + if(max_alive >= -1 && max_alive != 0 && max_alive <= MAX_MATCEN_ALIVE_CHILDREN) + { + int *temp; + int i; + + // Allocate the alive children list + if(max_alive > 0) + { + temp = (int *) mem_malloc(sizeof(int) * max_alive); + } + else + { + temp = NULL; + } + + // Determine the current number of alive (and tracked) children + if(m_alive_list && temp) + { + m_num_alive = (max_alive < m_num_alive)?max_alive:m_num_alive; + + for(i = 0; i < m_num_alive; i++) + { + temp[i] = m_alive_list[i]; + } + } + else + { + m_num_alive = 0; + } + + // Cleanup the old list + if(m_alive_list) + { + mem_free(m_alive_list); + } + + // Setup the new list pointer and max alive children value + m_alive_list = temp; + m_max_alive_children = max_alive; + + return true; + } + + return false; +} + +float matcen::GetPreProdTime() +{ + return m_preprod_time; +} + +bool matcen::SetPreProdTime(float time) +{ + float upper_bound; + + // MATCEN NOTE: Check if valid for effect here + switch(m_creation_effect) + { + case MEFFECT_LINE_LIGHTNING: + upper_bound = 2.0f; + break; + case MEFFECT_LINE_SINE_WAVE: + upper_bound = 2.0f; + break; + default: + upper_bound = 2.0f; + break; + } + + if(time >= 0.0f && time <= upper_bound) + { + m_preprod_time = time; + return true; + } + + return false; +} + +float matcen::GetPostProdTime() +{ + return m_postprod_time; +} + +bool matcen::SetPostProdTime(float time) +{ + float upper_bound; + + // MATCEN NOTE: Check if valid for effect here + switch(m_creation_effect) + { + case MEFFECT_LINE_LIGHTNING: + upper_bound = 2.0f; + break; + case MEFFECT_LINE_SINE_WAVE: + upper_bound = 2.0f; + break; + default: + upper_bound = 2.0f; + break; + } + + if(time >= 0.0f && time <= upper_bound) + { + m_postprod_time = time; + return true; + } + + return false; +} + +int FindMatcenIndex(char *name) +{ + int i; + char temp[MAX_MATCEN_NAME_LEN]; + + for(i = 0; i < Num_matcens; i++) + { + Matcen[i]->GetName(temp); + + if(!(strcmpi(temp, name))) + { + return i; + } + } + return MATCEN_ERROR; +} + +int CreateMatcen(char *name, bool *f_name_changed) +{ + if(Num_matcens < MAX_MATCENS) + { + Matcen[Num_matcens] = new matcen; + Matcen[Num_matcens++]->SetName(name); + + *f_name_changed = false; + + return Num_matcens - 1; + } + + return MATCEN_ERROR; +} + +void InitMatcens() +{ + int i; + + Num_matcens = 0; + + for(i = 0; i < MAX_MATCENS; i++) + { + Matcen[i] = NULL; + } + + atexit(DestroyAllMatcens); +} + +#if defined(LINUX) +void DestroyAllMatcens() +#else +void __cdecl DestroyAllMatcens() +#endif +{ + int i; + + for(i = 0; i < Num_matcens; i++) + { + if(Matcen[i]) + { + delete Matcen[i]; + } + } + + Num_matcens = 0; +} + +#ifndef NEWEDITOR + +void DoMatcensFrame() +{ + int i; + + for(i = 0; i < Num_matcens; i++) + { + if(Matcen[i]) + { + Matcen[i]->DoThinkFrame(); + } + } +} + +#endif // NEWEDITOR + +void InitMatcensForLevel() +{ + int i; + + for(i = 0; i < Num_matcens; i++) + { + if(Matcen[i]) + { + Matcen[i]->Reset(); + } + } +} + +#ifndef NEWEDITOR + +void DoMatcensRenderFrame() +{ + int i; + + for(i = 0; i < Num_matcens; i++) + { + if(Matcen[i]) + { + Matcen[i]->DoRenderFrame(); + } + } +} + +#endif // NEWEDITOR + +void DestroyMatcen(int id, bool f_resort = false) +{ +#if (!defined(EDITOR) && !defined(NEWEDITOR)) + f_resort = false; +#endif + + if(MatcenValid(id)) + { + delete Matcen[id]; + Matcen[id] = NULL; + + Num_matcens--; + + if(f_resort) + { + while(id < Num_matcens) + { + Matcen[id] = Matcen[id + 1]; + id++; + } + } + } +} + +bool MatcenValid(int id) +{ + return (id >= 0 && id < Num_matcens && Matcen[id]); +} diff --git a/Descent3/matcen.h b/Descent3/matcen.h new file mode 100644 index 000000000..433fd8550 --- /dev/null +++ b/Descent3/matcen.h @@ -0,0 +1,202 @@ +#ifndef _MATCEN_H_ +#define _MATCEN_H_ + +#include "CFILE.H" +#include "vecmat.h" +#include "matcen_external.h" + +#define MAX_MATCENS 60 +#define MAX_MATCEN_NAME_LEN 32 + +#define MATCEN_LOADSAVE_VERSION 3 + +#define MAX_MATCEN_ALIVE_CHILDREN 32 + +extern int Num_matcens; +extern bool Matcen_created; + +#define MATCEN_OUTSIDE_NEAR_DIST 150.0f + +#define MATCEN_ACTIVE_CHECK_RATE 4.0f +#define MATCEN_ACTIVE_CHECK_VARIENCE 1.0f + +#define CHECK_ACTIVE_RATE 3.0f +#define CHECK_ACTIVE_VARIENCE 1.0f + +#define MAX_MATCEN_EFFECT_SATURATION 2 + +#ifdef EDITOR +extern char *MatcenEffectStrings[NUM_MATCEN_EFFECTS]; +#endif + +// Versions +// 1 - Initial +// 2 - Added matcen effects +// 3 - Added matcen active_sound_handle + +class matcen +{ + private: + // Static data -- only changes by OSIRIS + char m_name[MAX_MATCEN_NAME_LEN]; + + char m_num_prod_types; + char m_control_type; + char m_type; + char m_creation_effect; + short m_creation_texture; + ubyte m_cur_saturation_count; + + int m_num_spawn_pnts; + + union + { + int m_roomnum; + int m_objref; + }; + + vector m_create_pnt; + int m_create_room; + + int m_spawn_pnt[MAX_SPAWN_PNTS]; + vector m_spawn_vec[MAX_SPAWN_PNTS]; + vector m_spawn_normal[MAX_SPAWN_PNTS]; + short m_spawn_vis_effects[MAX_MATCEN_EFFECT_SATURATION][MAX_SPAWN_PNTS]; + + int m_max_prod; + + int m_prod_type[MAX_PROD_TYPES]; + float m_prod_time[MAX_PROD_TYPES]; + int m_prod_priority[MAX_PROD_TYPES]; + int m_max_prod_type[MAX_PROD_TYPES]; + + short m_max_alive_children; + short m_num_alive; + int *m_alive_list; // list of alive children + + float m_preprod_time; + float m_postprod_time; + + int m_sounds[MAX_MATCEN_SOUNDS]; + + float m_speed_multi; + + // Dynamic values that change without scripting + char m_prod_mode; + float m_prod_mode_time; + + int m_status; + + int m_num_prod; + int m_last_prod_type_index; + float m_last_prod_finish_time; + + int m_cached_prod_index; + float m_cached_prod_time; + + int m_sound_active_handle; + + float m_next_active_check_time; + bool m_last_active_check_result; + + int m_last_prod_objref; + + int m_num_prod_type[MAX_PROD_TYPES]; + + // Private functions that are not available outside of the matcen internals + bool StartObjProd(); + bool DoObjProd(); + bool FinishObjProd(); + + bool ComputeNextProdInfo(); + bool ComputeCreatePnt(); + + void CheckActivateStatus(); + bool DoAliveListFrame(); + bool AddToAliveList(int objref); + + public: + matcen(); + ~matcen(); + + void SetCreationTexture(short texnum); + short GetCreationTexture(); + + char GetAttachType(); + bool SetAttachType(char type); + + char GetControlType(); + bool SetControlType(char type); + + int GetAttach(); + bool SetAttach(int attach); + + bool GetCreatePnt(vector *pnt); + bool SetCreatePnt(vector *pnt); + int GetCreateRoom(); + bool SetCreateRoom(int room); + + char GetNumSpawnPnts(); + bool SetNumSpawnPnts(char num_s); + + int GetSpawnPnt(char s_index); + bool SetSpawnPnt(char s_index, int s_value); + + void SaveData(CFILE *fptr); + void LoadData(CFILE *fptr); + + void GetName(char *); + bool SetName(char *); + + int GetMaxProd(); + bool SetMaxProd(int max_p); + + char GetNumProdTypes(); + bool SetNumProdTypes(char num_prod_types); + + bool GetProdInfo(char index, int *type_id, int *priority, float *time, int *max_prod); + bool SetProdInfo(char index, int *type_id, int *priority, float *time, int *max_prod); + + float GetProdMultiplier(); + bool SetProdMultiplier(float multi); + + int GetStatus(); + bool SetStatus(int status, bool f_enable); // Not all flags are settable + + void DoThinkFrame(); + void DoRenderFrame(); + + char GetCreationEffect(); + bool SetCreationEffect(char effect_index); + + int GetMaxAliveChildren(); + bool SetMaxAliveChildren(int max_alive); + + float GetPreProdTime(); + bool SetPreProdTime(float time); + + float GetPostProdTime(); + bool SetPostProdTime(float time); + + int GetSound(char sound_type); + bool SetSound(char sound_type, int sound_index); + + void Reset(); +}; + +extern matcen *Matcen[MAX_MATCENS]; +int FindMatcenIndex(char *name); +int CreateMatcen(char *name, bool *f_name_changed); +void InitMatcens(); + +void DestroyAllMatcens(); + +void DestroyMatcen(int id, bool f_resort); + +bool MatcenValid(int id); + +void DoMatcensFrame(); +void DoMatcensRenderFrame(); +void InitMatcensForLevel(); + +#endif \ No newline at end of file diff --git a/Descent3/matcen_external.h b/Descent3/matcen_external.h new file mode 100644 index 000000000..81bae0672 --- /dev/null +++ b/Descent3/matcen_external.h @@ -0,0 +1,51 @@ +#ifndef MATCEN_EXTERNAL_H_ +#define MATCEN_EXTERNAL_H_ + +#define MAX_PROD_TYPES 8 +#define MAX_SPAWN_PNTS 4 + +#define MATCEN_ERROR -1 + +#define MSTAT_DISABLED 1 +#define MSTAT_ACTIVE 2 +#define MSTAT_ACTIVE_PAUSE 4 +#define MSTAT_CREATE_OBJ_FRAME 8 +#define MSTAT_NEVER_PROD 16 +#define MSTAT_DONE_PROD 32 +#define MSTAT_RANDOM_PROD_ORDER 64 +#define MSTAT_PROD_TILL_DONE 128 +#define MSTAT_PROD_ONE_PAUSE 256 +#define MSTAT_PROD_ONE_DISABLE 512 +#define MSTAT_MANUAL_UPDATE_CREATE_PNT 1024 +#define MSTAT_COMPUTE_CREATE_PNT_EVERY_FRAME 2048 +#define MSTAT_NOT_HURT_PLAYER 4096 + +#define MMODE_NOTPROD 0 +#define MMODE_PREPROD 1 +#define MMODE_POSTPROD 2 + +// MATCEN NOTE: Make sure to add the name of the effect to the list +// in matcen.cpp +#define NUM_MATCEN_EFFECTS 4 +#define MEFFECT_LINE_LIGHTNING 0 +#define MEFFECT_LINE_SINE_WAVE 1 +#define MEFFECT_PROCEDURAL_LIGHTNING 2 +#define MEFFECT_NONE + +#define MAX_MATCEN_CONTROL_TYPES 5 +#define MPC_SCRIPT 0 +#define MPC_WHILE_PLAYER_NEAR 1 +#define MPC_AFTER_PLAYER_NEAR 2 +#define MPC_WHILE_PLAYER_VISIBLE 3 +#define MPC_AFTER_PLAYER_VISIBLE 4 + +#define MAX_MATCEN_SOUNDS 3 +#define MATCEN_ACTIVE_SOUND 0 +#define MATCEN_DISABLE_SOUND 1 +#define MATCEN_PROD_SOUND 2 + +#define MT_OBJECT 0 +#define MT_ROOM 1 +#define MT_UNASSIGNED 2 + +#endif \ No newline at end of file diff --git a/Descent3/megacell.cpp b/Descent3/megacell.cpp new file mode 100644 index 000000000..2a9cbe6d6 --- /dev/null +++ b/Descent3/megacell.cpp @@ -0,0 +1,142 @@ +/* +* $Logfile: /descent3/main/megacell.cpp $ +* $Revision: 3 $ +* $Date: 6/06/97 5:25p $ +* $Author: Mark $ +* +* $Log: /descent3/main/megacell.cpp $ + * + * 3 6/06/97 5:25p Mark + * FROM JASON:Fixed megacell bug + * + * 2 6/05/97 2:52p Jason + * added megacell functions + * + * 1 6/05/97 10:56a Jason + * + +* +* $NoKeywords: $ +*/ + + +#include "pstypes.h" +#include "pserror.h" +#include "terrain.h" +#include "texture.h" +#include "gametexture.h" +#include "megacell.h" +#include "bitmap.h" +#include +#include +#include +#include + +megacell Megacells[MAX_MEGACELLS]; +int Num_megacells=0; + +// Sets all megacells to unused +void InitMegacells () +{ + for (int i=0;i0); + + Megacells[n].used=0; + Megacells[n].name[0]=0; + Num_megacells--; +} + +// Gets next megacell from n that has actually been alloced +int GetNextMegacell (int n) +{ + int i; + + ASSERT (n>=0 && n=0 && n=0;i--) + { + if (Megacells[i].used) + return i; + } + for (i=MAX_MEGACELLS-1;i>n;i--) + { + if (Megacells[i].used) + return i; + } + + // this is the only one + return n; + +} +// Searches thru all megacells for a specific name, returns -1 if not found +// or index of megacell with name +int FindMegacellName (char *name) +{ + int i; + + ASSERT (name!=NULL); + + for (i=0;i +#define IDV_QUIT 0xff +// Menu Item Defines +#define IDV_NEWGAME 10 +#define IDV_MULTIPLAYER 11 +#define IDV_OPTIONS 12 +#define IDV_PILOT 13 +#define IDV_LOADGAME 14 +#define IDV_PLAYDEMO 15 +#define IDV_CREDITS 16 +#ifdef _DEBUG +#define IDV_LOADLEVEL 20 +#define IDV_OK 1 +#define IDV_CANCEL 2 +bool MenuLoadLevel(void); +#endif +// for command line joining of games +bool Auto_connected=false; +// externed from init.cpp +extern void SaveGameSettings(); +// runs command line options. +bool ProcessCommandLine(); +// new game selection +bool MenuNewGame(); +extern bool Mem_quick_exit; +bool IsRestoredGame = false; +////////////////////////////////////////////////////////////////////////////// +extern bool IsCheater; +extern bool Demo_looping; +extern bool Game_gauge_do_time_test; +extern char Game_gauge_usefile[_MAX_PATH]; +bool FirstGame = false; + +int MainMenu() +{ + extern void ShowStaticScreen(char *bitmap_filename,bool timed=false,float delay_time=0.0f); + mmInterface main_menu; + bool exit_game = false; + bool exit_menu = false; + +#ifdef GAMEGAUGE + if(1) +#else + if(Game_gauge_do_time_test) +#endif + { + Mem_quick_exit = 1; + return 1; + } +// okay over here, we'll decide whether we've finished the training mission and are going into game. + if (!Demo_looping && !Demo_restart && !MultiDLLGameStarting) { + if (FirstGame) { + if (MenuNewGame()) { + return 0; + } + } + } + +// setup screen + SetScreenMode(SM_MENU); +// create interface + main_menu.Create(); + main_menu.AddItem(IDV_NEWGAME,KEY_N,TXT_MENUNEWGAME, MM_STARTMENU_TYPE); + main_menu.AddItem(IDV_LOADGAME, KEY_L, TXT_LOADGAME); +//#ifndef DEMO + main_menu.AddItem(IDV_PLAYDEMO, KEY_D, TXT_VIEWDEMO); +//#endif + main_menu.AddItem(IDV_OPTIONS, KEY_O, TXT_MENUOPTIONS); + main_menu.AddItem(IDV_PILOT, KEY_P, TXT_MENUPILOTS); + main_menu.AddItem(IDV_MULTIPLAYER, KEY_M, TXT_MENUMULTIPLAYER); + main_menu.AddItem(IDV_CREDITS, KEY_C, TXT_MENUCREDITS); + main_menu.AddItem(IDV_QUIT, KEY_Q, TXT_MENUQUIT, MM_ENDMENU_TYPE); +#ifdef _DEBUG + main_menu.AddItem(0,0,NULL); + main_menu.AddItem(IDV_LOADLEVEL, 0, "Load Level"); +#endif +// page in ui data. + newuiCore_PageInBitmaps(); +// do special junk. + + //Only check for pilot arg first time, not every tme + static bool first_time = true; + if (first_time) { + int pilotarg = FindArg("+name"); + if(!pilotarg) + { + pilotarg = FindArg("-pilot"); + } + if(pilotarg) + { + char pfilename[_MAX_FNAME]; + strcpy(pfilename,GameArgs[pilotarg+1]); + strcat(pfilename,".plt"); + Current_pilot.set_filename(pfilename); + PltReadFile(&Current_pilot,true); + } + first_time = false; + } + char pfilename[_MAX_FNAME]; + Current_pilot.get_filename(pfilename); + + if( (pfilename[0]=='\0') || (strlen(pfilename)==0) || (!strcmp(pfilename," ")) ) + PilotSelect(); + +// always enforce that in main menu we are in normal game mode. + SetGameMode(GM_NONE); +// open main menu + main_menu.Open(); + exit_menu = ProcessCommandLine(); +// Main Menu Code Here + while (!exit_menu) + { + int res; + // handle all UI results. + if((Demo_looping)||(Demo_restart)) + { + Demo_restart = false; + SetGameMode(GM_NORMAL); + SetFunctionMode(LOADDEMO_MODE); + exit_menu = 1; + continue; + } + else if(MultiDLLGameStarting) + { + //Go back into the multiplayer DLL @ the game list + + mprintf ((0,"Returning to Multiplayer!\n")); + + if (ReturnMultiplayerGameMenu()) { + exit_menu=1; + SetFunctionMode(GAME_MODE); + continue; + } + } + /* + else + { + if(FirstGame) + { + //MenuScene(); + //ui_ShowCursor(); + //Descent->defer(); + //DoUIFrame(); + //rend_Flip(); + //GetUIFrameResult(); + res = IDV_NEWGAME; + } + else { + main_menu.SetMusicRegion(MM_MUSIC_REGION); + res = main_menu.DoUI(); + } + } + if (FirstGame) + res = IDV_NEWGAME; + */ + res = FirstGame ? IDV_NEWGAME : -1; + if (res == -1) { + main_menu.SetMusicRegion(MM_MUSIC_REGION); + res = main_menu.DoUI(); + } + switch (res) + { + case IDV_NEWGAME: + main_menu.SetMusicRegion(NEWGAME_MUSIC_REGION); + DoWaitMessage(true); + IsCheater = false; + IsRestoredGame = false; + //make only the default ships available (we may need to move this depending on load a saved game) + PlayerResetShipPermissions(-1,true); + if (MenuNewGame()) { + exit_menu = 1; + MenuScene(); + rend_Flip(); + } + break; + case IDV_QUIT: + if (DoMessageBox(TXT_MENUQUIT, TXT_QUITMESSAGE, MSGBOX_YESNO)) { + exit_game = 1; + exit_menu = 1; + Mem_quick_exit = 1; //tell the mem library to not free up each chunk individually + } + break; + case IDV_LOADGAME: + SetGameMode(GM_NONE); + if (LoadGameDialog()) { + SetGameMode(GM_NORMAL); + SetFunctionMode(RESTORE_GAME_MODE); + exit_menu = 1; + } + break; + case IDV_OPTIONS: + main_menu.SetMusicRegion(OPTIONS_MUSIC_REGION); + OptionsMenu(); + break; + case IDV_PILOT: + main_menu.SetMusicRegion(OPTIONS_MUSIC_REGION); + DoWaitMessage(true); + PilotSelect(); + break; + case IDV_MULTIPLAYER: + { + IsCheater = false; + main_menu.SetMusicRegion(MULTI_MUSIC_REGION); + mprintf ((0,"Multiplayer!\n")); + //make all ships available + mprintf((0,"Making all ships available\n")); + for(int i=0;i c) { + ddio_MakePath(fullpath, pathname, filename, NULL); + if(strcmpi("d3_2.mn3",filename)==0) + continue; + if (GetMissionInfo(filename, &msninfo) && msninfo.name[0] && msninfo.single) { + //if (!msninfo.training || (msninfo.training && Current_pilot.find_mission_data(TRAINING_MISSION_NAME)!= -1)) { + filelist[c] = mem_strdup(filename); + lb->AddItem(msninfo.name); + filename[0] = 0; + c++; + if (!(c %2)) DoWaitMessage(true); + //} + } + } + } + while (ddio_FindNextFile(filename)); + ddio_FindFileClose(); + + } + return c; +} +extern bool Skip_next_movie; +#define OEM_TRAINING_FILE "training.mn3" +#define OEM_MISSION_FILE "d3oem.mn3" +bool MenuNewGame() +{ + newuiTiledWindow menu; + newuiSheet *select_sheet; + newuiListBox *msn_lb; + char **filelist = NULL; + int n_missions,i,res; //,k + bool found = false; + bool do_menu = true, load_mission = false, retval = true; +#ifdef DEMO + if(LoadMission("d3demo.mn3")) + { + CurrentPilotUpdateMissionStatus(true); + // go into game mode. + SetGameMode(GM_NORMAL); + SetFunctionMode(GAME_MODE); + return true; + } + else + { + DoMessageBox(TXT_ERROR, TXT_ERRLOADMSN, MSGBOX_OK); + return false; + } +#else +#ifdef RELEASE + if( (!FindArg("-mission")) && (!FirstGame) && (-1==Current_pilot.find_mission_data(TRAINING_MISSION_NAME)) ) + { + + FirstGame=true; + + char temppath[PSFILENAME_LEN*2]; + char *moviepath; + moviepath = GetMultiCDPath("level1.mve"); + if(moviepath) + { + strcpy(temppath,moviepath); + PlayMovie(temppath); + } + Skip_next_movie = true; + + if(LoadMission("training.mn3")) + { + CurrentPilotUpdateMissionStatus(true); + // go into game mode. + SetGameMode(GM_NORMAL); + SetFunctionMode(GAME_MODE); + return true; + } + else + { + DoMessageBox(TXT_ERROR, TXT_ERRLOADMSN, MSGBOX_OK); + return false; + } + } + else if(FirstGame) + { + FirstGame=false; +#ifdef OEM + if(LoadMission(OEM_MISSION_FILE)) +#else + if(LoadMission("d3.mn3")) +#endif + { + CurrentPilotUpdateMissionStatus(true); + // go into game mode. + SetGameMode(GM_NORMAL); + SetFunctionMode(GAME_MODE); + return true; + } + else + { + DoMessageBox(TXT_ERROR, TXT_ERRLOADMSN, MSGBOX_OK); + return false; + } + } +#endif +// create menu. + menu.Create(TXT_MENUNEWGAME, 0, 0, 448, 384); + + select_sheet = menu.GetSheet(); + select_sheet->NewGroup(NULL, 10, 0); + msn_lb = select_sheet->AddListBox(352,256, UID_MSNLB); + select_sheet->NewGroup(NULL, 160, 280, NEWUI_ALIGN_HORIZ); + select_sheet->AddButton(TXT_OK, UID_OK); + select_sheet->AddButton(TXT_CANCEL, UID_CANCEL); +#ifndef OEM + select_sheet->AddButton(TXT_MSNINFO, UID_MSNINFO); +#endif +#ifndef OEM +// add mission names to listbox +// count valid mission files. +// add a please wait dialog here. + n_missions = 0; +#ifndef RELEASE + n_missions = count_missions(LocalLevelsDir, "*.msn"); +#endif + n_missions += count_missions(D3MissionsDir, "*.mn3"); +#ifdef MACINTOSH + char cdpath[_MAX_PATH]; + char *cdvol = GetCDVolume(1); + if(cdvol) { + ddio_MakePath(cdpath, cdvol,"missions",NULL); + n_missions += count_missions(cdpath, "*.mn3"); + } + mem_free(cdvol); +#endif + if (n_missions) { + // allocate extra mission slot because of check below which adds a name to the filelist. + filelist = (char **)mem_malloc(sizeof(char *)*(n_missions+1)); + for (i = 0; i < (n_missions+1); i++) + filelist[i] = NULL; + } + else { + DoMessageBox(TXT_ERROR, TXT_NOMISSIONS, MSGBOX_OK); + retval = false; + DoWaitMessage(false); + goto missions_fail; + } +// generate real listbox now. + i = 0; +#ifndef RELEASE + i = generate_mission_listbox(msn_lb, n_missions, filelist, LocalLevelsDir, "*.msn"); +#endif + i += generate_mission_listbox(msn_lb, n_missions - i, filelist+i, D3MissionsDir, "*.mn3"); +//#ifdef RELEASE + int k; + for(k=0;kAddItem(TXT_MAINMISSION); + n_missions++; + } +//#endif +#else +#define OEM_MISSION_NAME "Descent 3: Sol Ascent" +#define OEM_TRAINING_NAME "Pilot Training " + n_missions=2; + filelist = (char **)mem_malloc(sizeof(char *)*2); + filelist[0] = mem_strdup(OEM_MISSION_FILE);; + msn_lb->AddItem(OEM_MISSION_NAME); + filelist[1] = mem_strdup(OEM_TRAINING_FILE); + msn_lb->AddItem(OEM_TRAINING_NAME); +#endif + DoWaitMessage(false); +redo_newgame_menu: +// run menu + menu.Open(); + + do + { + res = menu.DoUI(); +#ifndef OEM + if (res == UID_MSNINFO) { + tMissionInfo msninfo; + int index = msn_lb->GetCurrentIndex(); + if (index >= 0 && index < n_missions) { + if (GetMissionInfo(filelist[index], &msninfo)) { + if (msninfo.name[0]) { + newuiTiledWindow infownd; + newuiSheet *sheet; + infownd.Create(NULL, 0, 0, 384, 192); + infownd.Open(); + sheet = infownd.GetSheet(); + sheet->NewGroup(TXT_MSNNAME, 0, 0); + sheet->AddText(msninfo.name); + sheet->NewGroup(TXT_MSNAUTHOR, 0, 32); + if (msninfo.author[0]) sheet->AddText(msninfo.author); + else sheet->AddText(TXT_NONE); + sheet->NewGroup(TXT_MSNNOTES, 0, 64); + if (msninfo.desc[0]) sheet->AddText(msninfo.desc); + else sheet->AddText(TXT_NONE); + sheet->NewGroup(NULL, 240, 118); + sheet->AddButton(TXT_OK, UID_OK); + infownd.DoUI(); + infownd.Close(); + infownd.Destroy(); + } + } + } + } +#endif + } + while (res != UID_OK && res != UID_CANCEL && res != UID_MSNLB); + menu.Close(); +// check stuff + if (res == UID_CANCEL) { + retval = false; + } + else if (res == UID_OK || res == UID_MSNLB) { + int index = msn_lb->GetCurrentIndex(); + char *nameptr=NULL; + if (index >=0 && index < n_missions) { + nameptr = filelist[index]; + } +#ifndef OEM + if (!nameptr || !LoadMission(nameptr)) { +#else + if (!LoadMission(nameptr)) { +#endif + DoMessageBox(TXT_ERROR, TXT_ERRLOADMSN, MSGBOX_OK); + retval = false; + } + else { + // if we didn't escape out of any part of new game start, then go to game. + int highest; + CurrentPilotUpdateMissionStatus(true); + // gets highest level flown for mission + #if defined(_DEBUG) || defined(DAJ_DEBUG) + highest = Current_mission.num_levels; + #else + highest = PilotGetHighestLevelAchieved(&Current_pilot,Current_mission.name); + highest = min(highest+1,Current_mission.num_levels); + #endif + if(highest>1){ + int start_level; + start_level = DisplayLevelWarpDlg(highest); + if (start_level == -1) { + goto redo_newgame_menu; + } + else { + Current_mission.cur_level = start_level; + //pull out the ship permssions and use them + Players[0].ship_permissions = GetPilotShipPermissions(&Current_pilot,Current_mission.name); + } + } + // go into game mode. + SetGameMode(GM_NORMAL); + SetFunctionMode(GAME_MODE); + retval = true; + } + } + menu.Destroy(); +missions_fail: +// free all memory + for (i = 0; i < n_missions; i++) + { + if(filelist[i]) { + mem_free(filelist[i]); + } + } + if (filelist) { + mem_free(filelist); + } + return retval; +#endif +} +// DisplayLevelWarpDlg +// pass in the max level allowed to be chosen, if -1, than all levels are allowed (a.k.a level warp cheat) +int DisplayLevelWarpDlg(int max_level) +{ + newuiMessageBox hwnd; + newuiSheet *sheet; + int chosen_level = 1,res; + int highest_allowed; + char buffer[128]; + char *input_text; +// creates a sheet + sheet = hwnd.GetSheet(); + if(max_level!=-1){ + hwnd.Create(TXT_LEVELSELECT, MSGBOX_OKCANCEL); + highest_allowed = max_level; + sprintf(buffer,TXT_LEVELSELECTB,highest_allowed); + }else{ + //level warp + hwnd.Create(TXT_LEVELWARP, MSGBOX_OKCANCEL); + highest_allowed = Current_mission.num_levels; + sprintf(buffer,TXT_LEVELWARPB,Current_mission.num_levels); + } + sheet->NewGroup(buffer, 0, 0); + input_text = sheet->AddEditBox(NULL, 4, 64, IDV_QUIT, UIED_NUMBERS); + itoa(chosen_level, input_text, 10); +redo_level_choose: + hwnd.Open(); + res = hwnd.DoUI(); + hwnd.Close(); + if (res == UID_OK || res == IDV_QUIT) { + chosen_level = atoi(input_text); + if(chosen_level<1 || chosen_level>highest_allowed){ + sprintf(buffer,TXT_CHOOSELEVEL,highest_allowed); + DoMessageBox(TXT_ERROR,buffer,MSGBOX_OK); + goto redo_level_choose; + } + } + else { + chosen_level = -1; + } + hwnd.Destroy(); + return chosen_level; +} +#ifdef _DEBUG +//Loads a level and starts the game +bool MenuLoadLevel(void) +{ + char buffer[_MAX_PATH]; +#ifdef MACINTOSH + ddio_MakePath(buffer,Base_directory,"data",NULL); +#else + buffer[0] = '\0'; +#endif + if(DoPathFileDialog(false,buffer,"Load Level","*.d3l",PFDF_FILEMUSTEXIST)){ + SimpleStartLevel(buffer); + SetFunctionMode(GAME_MODE); + return true; + } + return false; +} +#endif diff --git a/Descent3/menu.h b/Descent3/menu.h new file mode 100644 index 000000000..02fa2e22b --- /dev/null +++ b/Descent3/menu.h @@ -0,0 +1,100 @@ +/* + * $Logfile: /DescentIII/main/menu.h $ + * $Revision: 5 $ + * $Date: 4/17/99 6:15p $ + * $Author: Samir $ + * + * menu header + * + * $Log: /DescentIII/main/menu.h $ + * + * 5 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 4 12/15/98 4:28p Jeff + * added mission data information to the pilot files to save what the + * highest level they achieved on a mission is. Added level select dialog + * (not hooked up) and level warp cheat. + * + * 3 5/24/98 2:58a Jeff + * Options menu changes. MenuOptions now takes a parameter, whether its + * being called from the game or not + * + * 2 3/02/98 5:53p Samir + * Added MenuOptions prototype. + * + * 3 4/29/97 5:29p Samir + * New game calls mission functions. + * + * 2 2/10/97 11:49a Jason + * checked in for samir + * + * 1 2/04/97 2:50p Samir + * + * $NoKeywords: $ + */ + + +#ifndef MENU_H +#define MENU_H + +#include "pstypes.h" +#include "grdefs.h" + +#define MAX_MENUS 5 + +#define MAX_MENU_ITEMS 50 +#define MENU_STRING_LEN 70 + +#define MF_NORMAL_ANIMATED 1 +#define MF_SELECT_ANIMATED 2 + +#define MAX_MENU_TYPES 5 + +typedef enum +{ + MENU_TYPE_BUTTON, + MENU_TYPE_TEXT, + MENU_TYPE_RADIOBUTTON, + MENU_TYPE_CHECKBOX, + MENU_TYPE_TEXTBOX +} menu_type; + +typedef struct +{ + menu_type mtype; + char name[MENU_STRING_LEN]; + + int normal_handle; // The anim/bitmap associated with this unhighlighted item + int select_handle; // The anim/bitmap associated with the selected menu + + int flags; // see MF types above + + int x,y; + + ddgr_color normal_color,selected_color; + + +} menu_item; + +typedef struct +{ + int num_items; + + menu_item items[MAX_MENU_ITEMS]; + ubyte used; +} menu; + +// returns whether we decided to quit the game or not +// this displays the main menu options and runs the menu system +int MainMenu(); + +// opens options menu. +// ingame = true (if calling while playing the game) +void MenuOptions(bool ingame); + +// DisplayLevelWarpDlg +// pass in the max level allowed to be chosen, if -1, than all levels are allowed (a.k.a level warp cheat) +int DisplayLevelWarpDlg(int max_level); + +#endif \ No newline at end of file diff --git a/Descent3/mission_download.cpp b/Descent3/mission_download.cpp new file mode 100644 index 000000000..fb8d1d1b5 --- /dev/null +++ b/Descent3/mission_download.cpp @@ -0,0 +1,1187 @@ +/* + * $Logfile: /DescentIII/Main/mission_download.cpp $ + * $Revision: 29 $ + * $Date: 10/21/01 6:34p $ + * $Author: Kevin $ + * + * Mission file downloading system + * + * + * $Log: /DescentIII/Main/mission_download.cpp $ + * + * 29 10/21/01 6:34p Kevin + * minor fix to prevent a buffer overflow (not a reported bug, just + * something I noticed and thought was a good idea to avoid). + * + * 28 8/29/01 4:04p Matt + * Changed download directory from pxo.net to outrage.com + * + * 27 4/28/00 6:48p Jeff + * created _strlwr for linux + * + * 26 4/19/00 5:18p Matt + * From Duane for 1.4 + * ifdef-out mod download code for Mac + * + * 25 3/26/00 10:29p Kevin + * MOD Downloader for 1.4 patch. + * + * 24 11/04/99 12:35a Chris + * Added support for Merc + * + * 23 10/22/99 1:36p Matt + * Mac merge + * + * 22 10/18/99 1:27p Kevin + * Added cf_IsFileInHog + * + * 21 8/23/99 5:12p Kevin + * Proxy support + * + * 20 8/15/99 8:07p Jeff + * display interface when extracting files from a zip + * + * 19 8/15/99 4:27p Kevin + * possibly fixed mission download bug + * + * 18 8/13/99 8:00p Jeff + * handle zip files with new unzip class + * + * 17 7/21/99 3:48p Kevin + * changed left border. + * + * 16 5/18/99 10:31p Kevin + * fixed problem with joining d3.mn3 games and using the minimum install + * level + * + * 15 4/23/99 6:02p Kevin + * fixed bug when you can't download a mission + * + * 14 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 13 4/07/99 9:38a Kevin + * Fixed bug when there were no URLs in the mission file + * + * 12 4/03/99 9:26p Jeff + * changed dialogs that weren't using UID_OK and UID_CANCEL to use and + * handle them properly + * + * 11 3/23/99 8:57a Kevin + * Fixed a bug if the user hits cancel + * + * 10 3/03/99 2:32p Kevin + * + * 9 3/03/99 12:33a Kevin + * Minor OEM changes + * + * 8 2/07/99 1:17a Jeff + * peppered UI dialogs that were missing NEWUIRES_FORCEQUIT to handle it + * + * 7 2/03/99 4:20p Kevin + * Got multiplayer working with .mn3 files, and setup autodownloading + * + * 6 1/29/99 5:22p Jeff + * localization + * + * 5 12/30/98 12:42p Kevin + * Mission downloading localization + * + * 4 12/30/98 12:19p Kevin + * + * 3 12/30/98 12:15p Kevin + * Auto Mission Download system + * + * 2 12/28/98 2:22p Kevin + * Initial mission downloading system + * + * 1 12/28/98 11:42a Kevin + * + * + */ + +#include + +#include "pstypes.h" +#include "mem.h" +#include "args.h" +#include "descent.h" +#include "networking.h" +#include "multi.h" +#include "ui.h" +#include "newui.h" +#include "ddio.h" +#include "stringtable.h" +#include "multi_dll_mgr.h" +//#include "inetgetfile.h" +#include "grtext.h" +#include "Mission.h" +#include "mission_download.h" +#include "renderer.h" + +#ifndef MACINTOSH +#include "unzip.h" +#endif + +int Got_url; +msn_urls msn_URL = {"",{"","","","",""}}; +msn_urls Net_msn_URLs; + +extern char Proxy_server[200]; +extern short Proxy_port; + +int msn_ExtractZipFile(char *zipfilename,char *mn3name); + + +//Request a URL structure from a server containing a list of download locations +//For the current mission being played +msn_urls * msn_GetURL(network_address *net_addr) +{ + + int count=0; + int size; + int tries=0; + ubyte data[MAX_GAME_DATA_SIZE]; + float start_time; + network_address from_addr; + + size=START_DATA (MP_ASK_FOR_URL,data,&count); + END_DATA(count,data,size); + + Got_url=-1; + + while (tries<5 && Got_url==-1) + { + // Send the request + ASSERT (size>0); + nw_Send (net_addr,data,count,0); + tries++; + + start_time=timer_GetTime(); + while ((timer_GetTime()-start_time 0) && Got_url==-1) + { + MultiProcessBigData(Multi_receive_buffer,packsize,&from_addr); + } + } + } + + if (tries>=5 || Got_url!=1) + return NULL; + + return &msn_URL; + + + +} + + +//Get the list of URLs in the msn file +msn_urls * msn_GetURL(char * msnfile) +{ + //static msn_urls url; + //strcpy(url.msnname,msnfile); + return &Net_msn_URLs; +} + +#define MSN_DWNLD_CHOICE_H 256 +#define MSN_DWNLD_CHOICE_W 512 + +//Present the user with a list of URLs to download from. Return the 0 based selection, or -1 if cancel is hit. +int msn_ShowDownloadChoices(msn_urls *urls) +{ + UITextItem title_text(TXT_DOWNLOADPROMPT,UICOL_WINDOW_TITLE); + UITextItem title1_text(TXT_DONTHAVEMSN,UICOL_WINDOW_TITLE); + UITextItem header_text(TXT_DOWNLOADMSN,UICOL_TEXT_NORMAL); + UIHotspot ok_hot; + UIHotspot cancel_hot; + NewUIListBox choices_list; + + NewUIGameWindow menu_wnd; + + UIText texts[10]; + + int exit_menu=0; + int ret=0; + + menu_wnd.Create(10,10,MSN_DWNLD_CHOICE_W,MSN_DWNLD_CHOICE_H,UIF_PROCESS_ALL | UIF_CENTER); + texts[0].Create (&menu_wnd,&header_text,0,8,UIF_CENTER); + texts[1].Create (&menu_wnd,&title_text,0,55,UIF_CENTER); + texts[2].Create (&menu_wnd,&title1_text,0,35,UIF_CENTER); + + UITextItem ok_off(TXT_OK,UICOL_HOTSPOT_LO,UIALPHA_HOTSPOT_LO); + UITextItem ok_on(TXT_OK,UICOL_HOTSPOT_HI,UIALPHA_HOTSPOT_HI); + UITextItem cancel_off(TXT_CANCEL,UICOL_HOTSPOT_LO,UIALPHA_HOTSPOT_LO); + UITextItem cancel_on(TXT_CANCEL,UICOL_HOTSPOT_HI,UIALPHA_HOTSPOT_HI); + + int ok_x,cancel_x; + GetCenteredTextPos(menu_wnd.W(),20,ok_on.width(),cancel_on.width(),&ok_x,&cancel_x); + + ok_hot.Create(&menu_wnd, UID_OK, KEY_ENTER, &ok_off, &ok_on, + ok_x, MSN_DWNLD_CHOICE_H - OKCANCEL_YOFFSET, 0,0,UIF_FIT); + cancel_hot.Create(&menu_wnd,UID_CANCEL, KEY_ESC, &cancel_off,&cancel_on, + cancel_x, MSN_DWNLD_CHOICE_H - OKCANCEL_YOFFSET, 0,0,UIF_FIT); + + choices_list.Create(&menu_wnd,1,0,70,480,128,UIF_CENTER|UILB_NOSORT); + choices_list.SetSelectedColor(UICOL_LISTBOX_HI); + choices_list.SetHiliteColor(UICOL_LISTBOX_HI); + choices_list.RemoveAll(); + + UITextItem urlti[MAX_MISSION_URL_COUNT]; + //urls + int i; + int urlcount = 0; + for(i=0;iURL[i][0]) + { + urlti[i] = UITextItem(urls->URL[i],UICOL_LISTBOX_LO); + choices_list.AddItem(&urlti[i]); + urlcount++; + } + } + if(urlcount==0) + { + //Message that the mission can't be downloaded + DoMessageBox(TXT_ERROR,TXT_FMTCANTDNLD,MSGBOX_OK); + menu_wnd.Destroy(); + return -1; + } + menu_wnd.Open(); + + while (!exit_menu) + { + int res; + + res = DoUI(); + switch(res) + { + case NEWUIRES_FORCEQUIT: + case UID_CANCEL: + ret = -1; + menu_wnd.Close(); + exit_menu = 1; + break; + case UID_OK: + ret = choices_list.GetSelectedIndex(); + menu_wnd.Close(); + exit_menu = 1; + break; + } + } + menu_wnd.Destroy(); + return ret; +} + +#define MSN_REFRESH_INTERVAL .5 + +#define MSN_DWNLD_STATUS_H 256 +#define MSN_DWNLD_STATUS_W 512 + +#define MSN_BORDER_W 30 +#define MSN_BORDER_H 40 + +#define MSN_COL_1 MSN_BORDER_W +#define MSN_COL_2 ((MSN_DWNLD_STATUS_W/2)+(MSN_BORDER_W/2)) + +#define MSN_ROW_1 MSN_BORDER_H +#define MSN_ROW_2 (MSN_BORDER_H+30) +#define MSN_ROW_3 (MSN_BORDER_H+60) +#define MSN_ROW_4 (MSN_BORDER_H+90) +#define MSN_ROW_5 (MSN_BORDER_H+120) + +#define DOWNLOAD_STATUS_URL_TEXT TXT_FMTDOWNLOADING +#define DOWNLOAD_STATUS_RCVD_TEXT TXT_FMTRECEIVED +#define DOWNLOAD_STATUS_TOTAL_TEXT TXT_FMTTOTAL +#define DOWNLOAD_STATUS_ELAPS_TEXT TXT_FMTTIMEELAPSED +#define DOWNLOAD_STATUS_TIME_R_TEXT TXT_FMTTIMELEFT +#define DOWNLOAD_STATUS_XFERRATE_TEXT TXT_FMTXFERRATE + +#define MSN_MAX_STRING_LEN 100 + +//Start downloading the file at the url specifies, showing a status screen +//Return codes: +//0 Failed or cancelled +//1 Success +int msn_DownloadWithStatus(char *url,char *filename) +{ + return 0; + /* + char qualfile[_MAX_PATH*2]; + float last_refresh; + int total_bytes = 0; + int received_bytes = 0; + int time_elapsed = 0; + int time_remain = 0; + int xfer_rate = 0; + int starttime = timer_GetTime(); + bool file_is_zip = false; + + //check to see if we are downloading a zip file + char *url_ptr; + url_ptr = url + strlen(url); + while(url_ptr > url && *url_ptr!='.') url_ptr--; + if(*url_ptr=='.') + { + //now see if the rest of the extension is ZIP + if(!stricmp(url_ptr,".ZIP")) + { + mprintf((0,"We're downloading a zip file!!!\n")); + file_is_zip = true; + } + } + + char fmturl[MSN_MAX_STRING_LEN]; + char fmtrcvd[MSN_MAX_STRING_LEN]; + char fmttotal[MSN_MAX_STRING_LEN]; + char fmtelaps[MSN_MAX_STRING_LEN]; + char fmttimer[MSN_MAX_STRING_LEN]; + char fmtrate[MSN_MAX_STRING_LEN]; + + sprintf(fmturl,DOWNLOAD_STATUS_URL_TEXT,url); + msn_ClipURLToWidth(MSN_DWNLD_STATUS_W-(MSN_COL_1+MSN_BORDER_W),fmturl); + sprintf(fmtrcvd,DOWNLOAD_STATUS_RCVD_TEXT,received_bytes); + sprintf(fmttotal,DOWNLOAD_STATUS_TOTAL_TEXT,total_bytes); + sprintf(fmtelaps,DOWNLOAD_STATUS_ELAPS_TEXT,msn_SecondsToString(time_elapsed)); + sprintf(fmttimer,DOWNLOAD_STATUS_TIME_R_TEXT,msn_SecondsToString(time_remain)); + sprintf(fmtrate,DOWNLOAD_STATUS_XFERRATE_TEXT,xfer_rate); + + if(file_is_zip) + { + char fname[_MAX_FNAME]; + char *s_ptr,*d_ptr; + s_ptr = filename; + d_ptr = fname; + while(*s_ptr && *s_ptr!='.'){ *d_ptr = *s_ptr; s_ptr++; d_ptr++;} + *d_ptr = '\0'; + strcat(fname,".zip"); + ddio_MakePath(qualfile,D3MissionsDir,fname,NULL); + }else + { + ddio_MakePath(qualfile,D3MissionsDir,filename,NULL); + } + InetGetFile *getmsnfile; + if(Proxy_server[0]) + { + getmsnfile = new InetGetFile(url,qualfile,Proxy_server,Proxy_port); + } + else + { + getmsnfile = new InetGetFile(url,qualfile); + } + + UITextItem title_text(TXT_MD_DOWNLOADSTATUS,UICOL_TEXT_NORMAL); + + UIHotspot cancel_hot; + + UITextItem download_text(fmturl,UICOL_TEXT_NORMAL);; + UITextItem rcvd_text(fmtrcvd,UICOL_TEXT_NORMAL); + UITextItem total_text(fmttotal,UICOL_TEXT_NORMAL); + UITextItem elaps_text(fmtelaps,UICOL_TEXT_NORMAL); + UITextItem time_r_text(fmttimer,UICOL_TEXT_NORMAL); + UITextItem rate_text(fmtrate,UICOL_TEXT_NORMAL); + + NewUIGameWindow menu_wnd; + + UIText texts[10]; + UIProgress progress; + + int exit_menu=0; + int ret=0; + + menu_wnd.Create(10,10,MSN_DWNLD_STATUS_W,MSN_DWNLD_STATUS_H,UIF_PROCESS_ALL | UIF_CENTER); + texts[0].Create (&menu_wnd,&title_text,0,8,UIF_CENTER); + texts[1].Create (&menu_wnd,&download_text,MSN_COL_1,MSN_ROW_1,0); + texts[2].Create (&menu_wnd,&rcvd_text,MSN_COL_1,MSN_ROW_2,0); + texts[3].Create (&menu_wnd,&total_text,MSN_COL_2,MSN_ROW_2,0); + texts[4].Create (&menu_wnd,&elaps_text,MSN_COL_1,MSN_ROW_3,0); + texts[5].Create (&menu_wnd,&time_r_text,MSN_COL_2,MSN_ROW_3,0); + texts[6].Create (&menu_wnd,&rate_text,MSN_COL_1,MSN_ROW_4,0); + + UITextItem cancel_off(TXT_CANCEL,UICOL_HOTSPOT_LO,UIALPHA_HOTSPOT_LO); + UITextItem cancel_on(TXT_CANCEL,UICOL_HOTSPOT_HI,UIALPHA_HOTSPOT_HI); + + progress.Create(&menu_wnd,MSN_COL_1,MSN_ROW_5,MSN_DWNLD_STATUS_W-(MSN_BORDER_W*2),35,0); + + int cancel_x = 0; + + cancel_hot.Create(&menu_wnd, UID_CANCEL, KEY_ESC, &cancel_off,&cancel_on, + cancel_x, MSN_DWNLD_STATUS_H - OKCANCEL_YOFFSET, 0,0,UIF_FIT|UIF_CENTER); + menu_wnd.Open(); + + last_refresh = timer_GetTime()-MSN_REFRESH_INTERVAL; + + while (!exit_menu) + { + int res; + + if((timer_GetTime()-last_refresh)>MSN_REFRESH_INTERVAL) + { + + //Update the dialog + //mprintf((0,"!")); + + if(getmsnfile->IsFileReceived()) + { + //File transfer successful! + mprintf((0,"Succesfully received the file!\n")); + exit_menu = 1; + + if(file_is_zip) + { + // now we gotta handle the zip file + ret = msn_ExtractZipFile(qualfile,filename); + }else + { + ret = 1; + } + } + + if(getmsnfile->IsFileError()) + { + //File transfer Error! + DoMessageBox(TXT_ERROR,TXT_FMTCANTDNLD,MSGBOX_OK); + //Delete the file that didn't finish! + ddio_DeleteFile(qualfile); + mprintf((0,"Couldn't download the file! Error: %d\n",getmsnfile->GetErrorCode())); + exit_menu = 1; + ret = 0; + } + + last_refresh = timer_GetTime(); + received_bytes = getmsnfile->GetBytesIn(); + total_bytes = getmsnfile->GetTotalBytes(); + + time_elapsed = timer_GetTime()-starttime; + + if(total_bytes) + { + time_remain = ((float)(total_bytes-received_bytes))/((float)(received_bytes/time_elapsed)); + } + if(time_elapsed&&received_bytes) + { + xfer_rate = ((float)(received_bytes/time_elapsed)); + } + texts[1].Destroy(); + texts[2].Destroy(); + texts[3].Destroy(); + texts[4].Destroy(); + texts[5].Destroy(); + texts[6].Destroy(); + + sprintf(fmturl,DOWNLOAD_STATUS_URL_TEXT,url); + msn_ClipURLToWidth(MSN_DWNLD_STATUS_W-(MSN_COL_1+MSN_BORDER_W),fmturl); + sprintf(fmtrcvd,DOWNLOAD_STATUS_RCVD_TEXT,received_bytes); + sprintf(fmttotal,DOWNLOAD_STATUS_TOTAL_TEXT,total_bytes); + sprintf(fmtelaps,DOWNLOAD_STATUS_ELAPS_TEXT,msn_SecondsToString(time_elapsed)); + sprintf(fmttimer,DOWNLOAD_STATUS_TIME_R_TEXT,msn_SecondsToString(time_remain)); + msn_ClipURLToWidth(MSN_DWNLD_STATUS_W-(MSN_COL_2+MSN_BORDER_W),fmttimer); + sprintf(fmtrate,DOWNLOAD_STATUS_XFERRATE_TEXT,xfer_rate); + + download_text = UITextItem (fmturl,UICOL_TEXT_NORMAL);; + rcvd_text = UITextItem (fmtrcvd,UICOL_TEXT_NORMAL); + total_text = UITextItem (fmttotal,UICOL_TEXT_NORMAL); + elaps_text = UITextItem (fmtelaps,UICOL_TEXT_NORMAL); + time_r_text = UITextItem (fmttimer,UICOL_TEXT_NORMAL); + rate_text = UITextItem (fmtrate,UICOL_TEXT_NORMAL); + + texts[1].Create (&menu_wnd,&download_text,MSN_COL_1,MSN_ROW_1,0); + texts[2].Create (&menu_wnd,&rcvd_text,MSN_COL_1,MSN_ROW_2,0); + texts[3].Create (&menu_wnd,&total_text,MSN_COL_2,MSN_ROW_2,0); + texts[4].Create (&menu_wnd,&elaps_text,MSN_COL_1,MSN_ROW_3,0); + texts[5].Create (&menu_wnd,&time_r_text,MSN_COL_2,MSN_ROW_3,0); + texts[6].Create (&menu_wnd,&rate_text,MSN_COL_1,MSN_ROW_4,0); + + progress.Update(((float)((float)received_bytes)/((float)total_bytes))); + //mprintf((0,"@")); + } + //mprintf((0,"-In")); + res = PollUI(); + //mprintf((0,"-Out")); + switch(res) + { + case UID_CANCEL: + getmsnfile->AbortGet(); + ddio_DeleteFile(qualfile); + exit_menu = 1; + ret = 0; + break; + } + } + menu_wnd.Close(); + menu_wnd.Destroy(); + delete getmsnfile; + return ret; + */ +} + + + +void msn_DoAskForURL(ubyte *indata,network_address *net_addr) +{ + static msn_urls *url; + int count=0; + int size; + int i; + ubyte data[MAX_GAME_DATA_SIZE]; + int num_urls=0; + + if(Netgame.local_role==LR_SERVER) + { + size=START_DATA (MP_CUR_MSN_URLS,data,&count); + + url = msn_GetURL(Netgame.mission); + if(url) + { + for(i=0;iURL[0]) + { + num_urls++; + } + } + } + //length of the msn + int msnlen = strlen(Netgame.mission)+1; + MultiAddByte(msnlen,data,&count); + //Copy the mission name + memcpy(data+count,url->URL[i],msnlen); + count += msnlen; + + //Silly copy protection. Don't download the mn3 if + //"clang.wav" is in it. + if(cf_IsFileInHog(Netgame.mission,"clang.wav")) + { + num_urls = 0; + return; + } + + //Number of URLs + MultiAddByte (num_urls,data,&count); + for(i=0;iURL[i])+1; + if((count+urllen)>=MAX_GAME_DATA_SIZE) { + // if for some reason the URLS exceed what a packet can send + // rather than overflow the memory buffer, just don't send out the packet. + // I would just fit any URLS that I can but this is safer to put in a patch. + return; + } + + //Write the lenght of the url + MultiAddUshort(urllen,data,&count); + //Now write the url + memcpy(data+count,url->URL[i],urllen); + count += urllen; + } + END_DATA(count,data,size); + nw_Send (net_addr,data,count,0); + } + +} + +void msn_DoCurrMsnURLs(ubyte *data,network_address *net_addr) +{ + int count = 0; + int num_urls=0; + int i; + + memset(&msn_URL,0,sizeof(msn_URL)); + + SKIP_HEADER (data,&count); + //Get the mission name + int msnlen = MultiGetByte(data,&count); + memcpy(&msn_URL.msnname,data+count,msnlen); + count += msnlen; + num_urls = MultiGetByte(data,&count); + + for(i=0;iURL[sel])); + if(msn_DownloadWithStatus(murls->URL[sel],filename)) + { + return 1; + } + } + } + else + { + DoMessageBox(TXT_ERROR,TXT_FMTCANTDNLD,MSGBOX_OK); + } + return 0; +#endif +} + +void msn_ClipURLToWidth(int width,char *string) +{ + if(!string) + return; + + int string_length = strlen(string); + + int size = 0; + char save = string[0]; + string[0] = NULL; + while(size=width) + { + return; + } + //replace the char and move to the next + string[size] = save; + size++; + save = string[size]; + string[size] = NULL; + } +} + + +char * msn_SecondsToString(int time_sec) +{ + static char fmttime[100]; + char fmttemp[100] = ""; + int hours; + int minutes; + int seconds; + int i; + fmttime[0] = NULL; + hours = time_sec/3600; + i = time_sec%3600; + minutes = i/60; + seconds = time_sec%60; + if(hours) + { + sprintf(fmttemp,TXT_FMTTIMEHOUR,hours); + strcat(fmttime," "); + strcat(fmttime,fmttemp); + } + if(minutes) + { + sprintf(fmttemp,TXT_FMTTIMEMIN,minutes); + strcat(fmttime," "); + strcat(fmttime,fmttemp); + } + if(seconds) + { + sprintf(fmttemp,TXT_FMTTIMESEC,seconds); + strcat(fmttime,fmttemp); + } + return fmttime; +} + +void _get_zipfilename(char *output,char *directory,char *zipfilename) +{ + char* s = strrchr(zipfilename,'/'); + if(s) + s++; + else + s = zipfilename; + + ddio_MakePath(output,directory,s,NULL); +} + +// return 0 on failure +// return 1 on success +int msn_ExtractZipFile(char *zipfilename,char *mn3name) +{ +#ifndef MACINTOSH + + mprintf((0,"Extracting ZIP File (%s) to missions directory\n",zipfilename)); + if(!cfexist(zipfilename)) + { + mprintf((0,"Zip file doesn't exist\n")); + return 0; + } + + char mission_directory[_MAX_PATH]; + char output_filename[_MAX_PATH]; + char buffer[256]; + + ddio_MakePath(mission_directory,LocalD3Dir,"missions",NULL); + + // now go through the zip file and extract all the files out that we can + ZIP zfile; + zipentry *ze; + + if(!zfile.OpenZip(zipfilename)) + { + mprintf((0,"Unable to open zip file\n")); + return 0; + } + + NewUIGameWindow window; + UIConsoleGadget console; + + //Create all the UI Items + window.Create(0,0,384,288); + console.Create(&window,0x99,25,25,0,35,18,0); + window.Open(); + + bool found_mn3 = false; + bool process_file; + + while( (ze = zfile.ReadNextZipEntry()) != NULL) + { + if(ze->crc32==0) + continue; //skip directories + + Descent->defer(); + process_file = true; + + mprintf((0,"Processesing: %s\n",ze->name)); + + if(ze->compression_method==0x0000 || ze->compression_method==0x0008) + { + char* rfile = strrchr(ze->name,'/'); + if(rfile) + rfile++; + else + rfile = ze->name; + + sprintf(buffer,"%s %s...",(ze->compression_method==0x0000)?"Extracting":"Inflating",rfile); + console.puts(GR_GREEN,buffer); + + // create the filename for this file + _get_zipfilename(output_filename,mission_directory,ze->name); + + if(cfexist(output_filename)) + { + sprintf(buffer,"%s already exists. Overwrite?",output_filename); + if(DoMessageBox("Confirm",buffer,MSGBOX_YESNO,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL)) + { + //delete the file + mprintf((0,"Deleting %s\n",zipfilename)); + if(!ddio_DeleteFile(output_filename)) + { + process_file = false; + console.puts(GR_GREEN,"[Unable to Write] "); + }else + { + console.puts(GR_GREEN,"[Overwriting] "); + } + }else + { + process_file = false; + console.puts(GR_GREEN,"[Skipped] "); + } + } + + // extract this file + if(process_file) + { + //update the screen + DoUIFrame(); + rend_Flip(); + + int ret = zfile.ExtractFile(ze,output_filename); + if(ret<0) + { + if(ret==-9) + { + mprintf((0," Error writing to file\n")); + sprintf(buffer,"\nError writing to file (Out of space?)"); + }else + { + mprintf((0," Error %d extracting file\n",ret)); + sprintf(buffer,"\nError %d extracting file",ret); + } + console.puts(GR_GREEN,buffer); + if(cfexist(output_filename)) + { + ddio_DeleteFile(output_filename); + } + }else + { + //check the CRC + unsigned int crc = cf_GetfileCRC(output_filename); + if(crc==ze->crc32) + { + console.puts(GR_GREEN,"CRC OK"); + + //check to see if we extracted our mn3 + if(CompareZipFileName(ze->name,mn3name)) + { + found_mn3 = true; + } + }else + { + console.puts(GR_GREEN,"CRC FAIL!"); + ddio_DeleteFile(output_filename); + } + } + } + + }else + { + mprintf((0,"Unsupported compression for file (%s)\n",ze->name)); + console.puts(GR_GREEN,"Unsupported compression!!"); + } + + console.puts(GR_GREEN,"\n"); + DoUIFrame(); + rend_Flip(); + } + zfile.CloseZip(); + + if(DoMessageBox("Confirm","Do you want to delete the zip file? It is no longer needed.",MSGBOX_YESNO,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL)) + { + //delete the file + mprintf((0,"Deleting %s\n",zipfilename)); + ddio_DeleteFile(zipfilename); + } + + window.Close(); + window.Destroy(); + + if(!found_mn3) + return 0; + + return 1; +#else + return 0; +#endif +} + +#ifdef __LINUX__ +char *_strlwr(char *string) +{ + char *ptr=string; + while(*ptr) + { + *ptr = tolower(*ptr); + ptr++; + } + return string; +} +#endif + +#define MOD_URL_BASEPATH "http://www.descent3.com/mods/" //WAS: "http://www.pxo.net/descent3/mods/" + +int CheckGetD3M(char *d3m) +{ +#if !(defined(OEM) || defined(MACINTOSH)) + + char modurl[MAX_MISSION_URL_LEN+1]; + char *lowurl; + char pathname[PSFILENAME_LEN*2]; + char *fixedd3m = NULL; + + + ddio_MakePath(pathname, LocalD3Dir, "Netgames", d3m, NULL); + if(cfexist(d3m)||cfexist(pathname)) + { + return 1; + } + + //Now we need to replace evil spaces with underscores, so the downloader will work + fixedd3m = mem_strdup(d3m); + char *p = fixedd3m; + while(*p) + { + if(*p==' ') + { + *p = '_'; + } + p++; + } + // This should be the url we can download from. + int iurlbase = FindArg("-d3mbaseurl"); + if(!iurlbase) + { + strcpy(modurl,MOD_URL_BASEPATH); + } + else + { + strcpy(modurl,GameArgs[iurlbase+1]); + } + + strcat(modurl,fixedd3m); + + lowurl = mem_strdup(_strlwr(modurl)); + mprintf((0,"Downloading mod file from %s\n",modurl)); + + + if(ModDownloadWithStatus(modurl,d3m)) + { + mem_free(fixedd3m); + return 1; + } + + mem_free(lowurl); + mem_free(fixedd3m); + return 0; +#endif + +} + + + + + + +//Start downloading the file at the url specifies, showing a status screen +//Return codes: +//0 Failed or cancelled +//1 Success +int ModDownloadWithStatus(char *url,char *filename) +{ + return 0; + /* + char qualfile[_MAX_PATH*2]; + float last_refresh; + int total_bytes = 0; + int received_bytes = 0; + int time_elapsed = 0; + int time_remain = 0; + int xfer_rate = 0; + int starttime = timer_GetTime(); + bool file_is_zip = false; + + //check to see if we are downloading a zip file + char *url_ptr; + url_ptr = url + strlen(url); + while(url_ptr > url && *url_ptr!='.') url_ptr--; + if(*url_ptr=='.') + { + //now see if the rest of the extension is ZIP + if(!stricmp(url_ptr,".ZIP")) + { + mprintf((0,"We're downloading a zip file!!!\n")); + file_is_zip = true; + } + } + + char fmturl[MSN_MAX_STRING_LEN]; + char fmtrcvd[MSN_MAX_STRING_LEN]; + char fmttotal[MSN_MAX_STRING_LEN]; + char fmtelaps[MSN_MAX_STRING_LEN]; + char fmttimer[MSN_MAX_STRING_LEN]; + char fmtrate[MSN_MAX_STRING_LEN]; + + sprintf(fmturl,DOWNLOAD_STATUS_URL_TEXT,url); + msn_ClipURLToWidth(MSN_DWNLD_STATUS_W-(MSN_COL_1+MSN_BORDER_W),fmturl); + sprintf(fmtrcvd,DOWNLOAD_STATUS_RCVD_TEXT,received_bytes); + sprintf(fmttotal,DOWNLOAD_STATUS_TOTAL_TEXT,total_bytes); + sprintf(fmtelaps,DOWNLOAD_STATUS_ELAPS_TEXT,msn_SecondsToString(time_elapsed)); + sprintf(fmttimer,DOWNLOAD_STATUS_TIME_R_TEXT,msn_SecondsToString(time_remain)); + sprintf(fmtrate,DOWNLOAD_STATUS_XFERRATE_TEXT,xfer_rate); + + if(file_is_zip) + { + char fname[_MAX_FNAME]; + char *s_ptr,*d_ptr; + s_ptr = filename; + d_ptr = fname; + while(*s_ptr && *s_ptr!='.'){ *d_ptr = *s_ptr; s_ptr++; d_ptr++;} + *d_ptr = '\0'; + strcat(fname,".zip"); + ddio_MakePath(qualfile, LocalD3Dir, "Netgames", fname, NULL); + //ddio_MakePath(qualfile,D3MissionsDir,fname,NULL); + }else + { + //ddio_MakePath(qualfile,D3MissionsDir,filename,NULL); + ddio_MakePath(qualfile, LocalD3Dir, "Netgames", filename, NULL); + } + InetGetFile *getmsnfile; + if(Proxy_server[0]) + { + getmsnfile = new InetGetFile(url,qualfile,Proxy_server,Proxy_port); + } + else + { + getmsnfile = new InetGetFile(url,qualfile); + } + //InetGetFile getmsnfile(url,qualfile); + + UITextItem title_text(TXT_MD_DOWNLOADSTATUS,UICOL_TEXT_NORMAL); + + UIHotspot cancel_hot; + + UITextItem download_text(fmturl,UICOL_TEXT_NORMAL);; + UITextItem rcvd_text(fmtrcvd,UICOL_TEXT_NORMAL); + UITextItem total_text(fmttotal,UICOL_TEXT_NORMAL); + UITextItem elaps_text(fmtelaps,UICOL_TEXT_NORMAL); + UITextItem time_r_text(fmttimer,UICOL_TEXT_NORMAL); + UITextItem rate_text(fmtrate,UICOL_TEXT_NORMAL); + + NewUIGameWindow menu_wnd; + + UIText texts[10]; + UIProgress progress; + + int exit_menu=0; + int ret=0; + + menu_wnd.Create(10,10,MSN_DWNLD_STATUS_W,MSN_DWNLD_STATUS_H,UIF_PROCESS_ALL | UIF_CENTER); + texts[0].Create (&menu_wnd,&title_text,0,8,UIF_CENTER); + texts[1].Create (&menu_wnd,&download_text,MSN_COL_1,MSN_ROW_1,0); + texts[2].Create (&menu_wnd,&rcvd_text,MSN_COL_1,MSN_ROW_2,0); + texts[3].Create (&menu_wnd,&total_text,MSN_COL_2,MSN_ROW_2,0); + texts[4].Create (&menu_wnd,&elaps_text,MSN_COL_1,MSN_ROW_3,0); + texts[5].Create (&menu_wnd,&time_r_text,MSN_COL_2,MSN_ROW_3,0); + texts[6].Create (&menu_wnd,&rate_text,MSN_COL_1,MSN_ROW_4,0); + + UITextItem cancel_off(TXT_CANCEL,UICOL_HOTSPOT_LO,UIALPHA_HOTSPOT_LO); + UITextItem cancel_on(TXT_CANCEL,UICOL_HOTSPOT_HI,UIALPHA_HOTSPOT_HI); + + progress.Create(&menu_wnd,MSN_COL_1,MSN_ROW_5,MSN_DWNLD_STATUS_W-(MSN_BORDER_W*2),35,0); + + int cancel_x = 0; + + cancel_hot.Create(&menu_wnd, UID_CANCEL, KEY_ESC, &cancel_off,&cancel_on, + cancel_x, MSN_DWNLD_STATUS_H - OKCANCEL_YOFFSET, 0,0,UIF_FIT|UIF_CENTER); + menu_wnd.Open(); + + last_refresh = timer_GetTime()-MSN_REFRESH_INTERVAL; + + while (!exit_menu) + { + int res; + + if((timer_GetTime()-last_refresh)>MSN_REFRESH_INTERVAL) + { + + //Update the dialog + //mprintf((0,"!")); + + if(getmsnfile->IsFileReceived()) + { + //File transfer successful! + mprintf((0,"Succesfully received the file!\n")); + exit_menu = 1; + + if(file_is_zip) + { + // now we gotta handle the zip file + ret = msn_ExtractZipFile(qualfile,filename); + }else + { + ret = 1; + } + } + + if(getmsnfile->IsFileError()) + { + //File transfer Error! + DoMessageBox(TXT_ERROR,TXT_FMTCANTDNLD,MSGBOX_OK); + //Delete the file that didn't finish! + ddio_DeleteFile(qualfile); + mprintf((0,"Couldn't download the file! Error: %d\n",getmsnfile->GetErrorCode())); + exit_menu = 1; + ret = 0; + } + + last_refresh = timer_GetTime(); + received_bytes = getmsnfile->GetBytesIn(); + total_bytes = getmsnfile->GetTotalBytes(); + + time_elapsed = timer_GetTime()-starttime; + + if(total_bytes) + { + time_remain = ((float)(total_bytes-received_bytes))/((float)(received_bytes/time_elapsed)); + } + if(time_elapsed&&received_bytes) + { + xfer_rate = ((float)(received_bytes/time_elapsed)); + } + texts[1].Destroy(); + texts[2].Destroy(); + texts[3].Destroy(); + texts[4].Destroy(); + texts[5].Destroy(); + texts[6].Destroy(); + + sprintf(fmturl,DOWNLOAD_STATUS_URL_TEXT,url); + msn_ClipURLToWidth(MSN_DWNLD_STATUS_W-(MSN_COL_1+MSN_BORDER_W),fmturl); + sprintf(fmtrcvd,DOWNLOAD_STATUS_RCVD_TEXT,received_bytes); + sprintf(fmttotal,DOWNLOAD_STATUS_TOTAL_TEXT,total_bytes); + sprintf(fmtelaps,DOWNLOAD_STATUS_ELAPS_TEXT,msn_SecondsToString(time_elapsed)); + sprintf(fmttimer,DOWNLOAD_STATUS_TIME_R_TEXT,msn_SecondsToString(time_remain)); + msn_ClipURLToWidth(MSN_DWNLD_STATUS_W-(MSN_COL_2+MSN_BORDER_W),fmttimer); + sprintf(fmtrate,DOWNLOAD_STATUS_XFERRATE_TEXT,xfer_rate); + + download_text = UITextItem (fmturl,UICOL_TEXT_NORMAL);; + rcvd_text = UITextItem (fmtrcvd,UICOL_TEXT_NORMAL); + total_text = UITextItem (fmttotal,UICOL_TEXT_NORMAL); + elaps_text = UITextItem (fmtelaps,UICOL_TEXT_NORMAL); + time_r_text = UITextItem (fmttimer,UICOL_TEXT_NORMAL); + rate_text = UITextItem (fmtrate,UICOL_TEXT_NORMAL); + + texts[1].Create (&menu_wnd,&download_text,MSN_COL_1,MSN_ROW_1,0); + texts[2].Create (&menu_wnd,&rcvd_text,MSN_COL_1,MSN_ROW_2,0); + texts[3].Create (&menu_wnd,&total_text,MSN_COL_2,MSN_ROW_2,0); + texts[4].Create (&menu_wnd,&elaps_text,MSN_COL_1,MSN_ROW_3,0); + texts[5].Create (&menu_wnd,&time_r_text,MSN_COL_2,MSN_ROW_3,0); + texts[6].Create (&menu_wnd,&rate_text,MSN_COL_1,MSN_ROW_4,0); + + progress.Update(((float)((float)received_bytes)/((float)total_bytes))); + //mprintf((0,"@")); + } + //mprintf((0,"-In")); + res = PollUI(); + //mprintf((0,"-Out")); + switch(res) + { + case UID_CANCEL: + getmsnfile->AbortGet(); + ddio_DeleteFile(qualfile); + exit_menu = 1; + ret = 0; + break; + } + } + menu_wnd.Close(); + menu_wnd.Destroy(); + delete getmsnfile; + return ret; + */ +} diff --git a/Descent3/mission_download.h b/Descent3/mission_download.h new file mode 100644 index 000000000..6b168a604 --- /dev/null +++ b/Descent3/mission_download.h @@ -0,0 +1,55 @@ +/* + * $Logfile: /DescentIII/Main/mission_download.h $ + * $Revision: 5 $ + * $Date: 3/26/00 10:29p $ + * $Author: Kevin $ + * + * Mission file downloading system + * + * + * $Log: /DescentIII/Main/mission_download.h $ + * + * 5 3/26/00 10:29p Kevin + * MOD Downloader for 1.4 patch. + * + * 4 4/15/99 1:40a Jeff + * changes for linux compile + * + * 3 12/30/98 12:15p Kevin + * Auto Mission Download system + * + * 2 12/28/98 2:22p Kevin + * Initial mission downloading system + * + * 1 12/28/98 11:42a Kevin + * + * + */ + +#define MAX_MISSION_URL_LEN 300 +#define MAX_MISSION_URL_COUNT 5 + +#define URL_ASK_POLL_TIME 2 + + +extern int Got_url; + +typedef struct +{ + char msnname[_MAX_PATH]; + char URL[MAX_MISSION_URL_COUNT][MAX_MISSION_URL_LEN]; + //Possibly some quality of service flags +}msn_urls; + + +//Function prototypes +void msn_DoAskForURL(ubyte *indata,network_address *net_addr); +void msn_DoCurrMsnURLs(ubyte *data,network_address *net_addr); +int msn_CheckGetMission(network_address *net_addr,char *filename); +int msn_ShowDownloadChoices(msn_urls *urls); +int msn_DownloadWithStatus(char *url,char *filename); +void msn_ClipURLToWidth(int width,char *string); +char * msn_SecondsToString(int time_sec); + +int ModDownloadWithStatus(char *url,char *filename); +int CheckGetD3M(char *d3m); \ No newline at end of file diff --git a/Descent3/mmItem.cpp b/Descent3/mmItem.cpp new file mode 100644 index 000000000..cb0c4aee0 --- /dev/null +++ b/Descent3/mmItem.cpp @@ -0,0 +1,565 @@ +/* + * $Source: $ + * $Revision: 27 $ + * $Author: Matt $ + * $Date: 12/06/01 9:26a $ + * + * main menu interface + * + * $Log: /DescentIII/Main/mmItem.cpp $ + * + * 27 12/06/01 9:26a Matt + * Added code to override main menu bitmap. + * + * 26 3/20/00 12:17p Matt + * Merge of Duane's post-1.3 changes. + * Don't show build number in release versions (Mac only) + * + * 25 8/02/99 9:01a Duane + * turned off menusound for mac + * + * 24 4/29/99 3:21a Samir + * reorganized main menu music to work in config, multiplayer, whereever. + * + * 23 4/21/99 4:10p Samir + * changed sound names for table file. + * + * 22 4/20/99 1:58a Matt + * Changed version number text from old UI font to briefing font. + * + * 21 4/18/99 8:35p Samir + * replaced placeholder sounds with newer placeholder sounds. + * + * 20 4/15/99 2:32p Kevin + * Added some code for the Demo + * + * 19 4/14/99 11:48a Matt + * Moved the main menu movie to the movies directory + * + * 18 4/02/99 3:03p Samir + * opengl doesn't use main menu movie. + * + * 17 3/31/99 11:53a Samir + * slow opengl code for movies. + * + * 16 3/23/99 9:06p Samir + * properly sync wait dialog and sound system calls. + * + * 15 3/22/99 10:14a Kevin + * Added a build number to the release builds of main. Also put in code to + * increment the build number each time a release build is done. + * + * 14 3/18/99 10:13a Samir + * new main menu interface. + * + * 13 3/02/99 11:52a Kevin + * Fixes for OEM Beta 4.1 + * + * 12 2/26/99 5:34p Samir + * oem screen. + * + * 11 2/20/99 12:33p Kevin + * Added some OEM stuff + * + * 10 10/23/98 7:05p Samir + * fixed keyboard select prob. + * + * 9 10/22/98 5:23p Matt + * Moved the version number and made it not print the build number if the + * build number was zero. + * + * 8 10/20/98 3:52p Samir + * tweaked main menu look. + * + * 7 10/13/98 6:06p Samir + * fixed booboo. + * + * 6 10/13/98 5:51p Samir + * moved version number + * + * 5 10/13/98 3:16p Samir + * enhanced gadgets some more with new effects (added Credits option). + * + * 4 10/13/98 12:03p Kevin + * Changed use of preprocessors for debug, etc. + * + * 3 10/12/98 4:40p Samir + * fancy effect for hotspots in main menu. + * + * 2 10/12/98 1:46p Samir + * initial new main menu + * + */ + + +#include "mmItem.h" +#include "game.h" +#include "program.h" +#include "descent.h" +#include "cinematics.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "d3music.h" + +#include "ddio.h" +//#include +#include "mem.h" + +#include + +// externed from newui.cpp +extern int UI_frame_result; + +static mmInterface *MM_object = NULL; +// attached window. +mmInterface *mmItem::m_window = NULL; + + +char *MMSoundFiles[N_MM_SOUNDS] = {"MenuBeepEnter","MenuBeepSelect"}; +void PlayMenuSound(int sound_index, bool wait_till_done=false); + +/* +$$TABLE_SOUND "MenuBeepEnter" +$$TABLE_SOUND "MenuBeepSelect" +*/ + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +mmItem::mmItem() +{ + m_text = NULL; +} + + +mmItem::~mmItem() +{ + if (m_text) + mem_free(m_text); +} + + +// attaches a window to all mmitems. +void mmItem::AttachInterface(mmInterface *wnd) +{ + mmItem::m_window = wnd; +} + + +void mmItem::Create(int id, int key, int x, int y, const char *text, int flags, tmmItemFX init_fx) +{ + m_alpha = 0; + m_satcount = 0; + m_fxqueue.flush(); + + m_text = text ? mem_strdup(text) : NULL; + m_fxqueue.send(init_fx); + m_curfx = mmItemFXNull; + m_process_speed = 0.0f; + +// set colors + m_colors[0] = MMITEM_COLOR; + m_last_frametime = UI_TIME(); + + UIGadget::Create(m_window, id, x, y, 0, 0, UIF_FIT | flags); + UIGadget::SetHotkey(key); +} + + +void mmItem::AddEffect(tmmItemFX fx) +{ + m_fxqueue.send(fx); +} + + +void mmItem::ClearEffects() +{ + m_fxqueue.flush(); +} + + +// override: called when resized or before drawing. +void mmItem::OnFormat() +{ + if (m_Flags & UIF_FIT) { + ui_DrawSetFont(MMITEM_FONT); + + m_W = (m_text ? ui_GetTextWidth(m_text) : 0); + m_H = (m_text ? ui_GetTextHeight(m_text) : 0); + } + + UIGadget::OnFormat(); +} + + +// override: behavior when gadget is destroyed. +void mmItem::OnDestroy() +{ + if (m_text) { + mem_free(m_text); + m_text = NULL; + } +} + + +void mmItem::OnSelect() +{ +#ifdef MACINTOSH + PlayMenuSound(m_window->SoundHandles[MM_SELECT_SOUND], false); //why wait? +#else + PlayMenuSound(m_window->SoundHandles[MM_SELECT_SOUND], true); +#endif + UIGadget::OnSelect(); +} + + +// override: behavior when mouse button is pressed. +void mmItem::OnMouseBtnDown(int btn) +{ + if (btn == UILMSEBTN) { + LOCK_FOCUS(this); + } +} + + +// override: behavior when mouse button is released. +void mmItem::OnMouseBtnUp(int btn) +{ + if (btn == UILMSEBTN) { + if (HasFocus()) { + if (PT_IN_GADGET(UIGadget::m_Wnd, this, UI_input.mx, UI_input.my)) { + OnSelect(); + } + } + UNLOCK_FOCUS(this); + } +} + + +// override: behavior when gadget loses input focus. +void mmItem::OnLostFocus() +{ + AddEffect(mmItemFXFadeOutToNormal); +} + + +// override: behavior when gadget gains input focus. +void mmItem::OnGainFocus() +{ + AddEffect(mmItemFXFadeInToBright); + PlayMenuSound(m_window->SoundHandles[MM_FOCUS_SOUND]); +} + + +// override: behavior when gadget is processed +void mmItem::OnUserProcess() +{ +// if (HasFocus() && !PT_IN_GADGET(m_Wnd, this, UI_input.mx, UI_input.my)) { +// LostFocus(); +// } +} + + +// override: behavior when gadget is being drawn. +void mmItem::OnDraw() +{ +// if not disabled, do fancy effects. + if (m_text) { + int i; + + ui_DrawSetFont(MMITEM_FONT); + ui_DrawSetTextType(0); + ui_SetCharAlpha(255); + ui_DrawString(m_colors[0], 0, 0, m_text); + ui_DrawSetTextType(UI_TEXTTYPE_SATURATE); + ui_SetCharAlpha((ubyte)m_alpha); + for (i=0; i < m_satcount; i++) + ui_DrawString(m_colors[0], 0, 0, m_text); + } + + if ((UI_TIME() - m_last_frametime) >= m_process_speed) { + // process special fx. + switch (m_curfx) + { + case mmItemFXNormal: + m_alpha = MMITEM_ALPHA; + m_satcount = MMITEM_SAT; + m_process_speed = 0.0f; + m_curfx = mmItemFXNull; + break; + + case mmItemFXFadeOutToNormal: + m_process_speed = 0.075f; + m_satcount = (m_satcount < MMITEM_SAT) ? MMITEM_SAT : (m_satcount-1); + if (m_satcount == MMITEM_SAT) { + m_curfx = mmItemFXNull; + } + break; + + case mmItemFXFadeInToBright: + m_process_speed = 0.075f; + m_satcount = (m_satcount > 2) ? 2 : (m_satcount+1); + if (m_satcount == 2) { + m_curfx = mmItemFXNull; + } + break; + } + + // grab new fx. + if (m_curfx == mmItemFXNull) { + tmmItemFX newfx; + if (m_fxqueue.recv(&newfx)) { + m_process_speed = 0.0f; + m_curfx = newfx; + } + } + + m_last_frametime = UI_TIME(); + } +} + + + +////////////////////////////////////////////////////////////////////////////// +// MAIN MENU INTERFACE CODE + +#include "stdlib.h" //need this to get _MAX_PATH, which really ought to be in ddio.h + +bool static_menu_background = false; + +void mmInterface::Create() +{ + int i; + + MM_object = this; + + UIWindow::Create(0,0,Max_window_w,Max_window_h); + mmItem::AttachInterface(this); + +#ifndef MOVIE_MENU +#if defined(OEM) + if (!LoadLargeBitmap("oemmenu.ogf", &m_art)) { + Error("Unable to load main menu art oemmenu.ogf."); + } +#elif defined(DEMO) + if (!LoadLargeBitmap("demomenu.ogf", &m_art)) { + Error("Unable to load main menu art demomenu.ogf."); + } +#else + if (!LoadLargeBitmap("mainmenu.ogf", &m_art)) { + Error("Unable to load main menu art mainmenu.ogf."); + } +#endif +#else + if (cfexist("mainmenuoverride.ogf")) + { + if (!LoadLargeBitmap("mainmenuoverride.ogf", &m_art)) + { + Error("Unable to load main menu art mainmenuoverride.ogf."); + } + m_movie = NULL; + static_menu_background = true; + } + else + { + char filename[_MAX_PATH]; + ddio_MakePath(filename,Base_directory,"movies","mainmenu",NULL); + m_movie = StartMovie(filename, true); + } +#endif + +// load sounds and music + for (i = 0; i < N_MM_SOUNDS; i++) + { + SoundHandles[i] = FindSoundName(IGNORE_TABLE(MMSoundFiles[i])); + } + + D3MusicStart("mainmenu.omf"); + SetMusicRegion(MM_MUSIC_REGION); + + SetUICallback(MenuScene); + m_nmenu_items = 0; +} + + +// does user interface. +int mmInterface::DoUI() +{ + UI_frame_result = -1; + ui_ShowCursor(); + + SetUICallback(MenuScene); + + while (UI_frame_result == -1) + { + Descent->defer(); + DoUIFrame(); + rend_Flip(); + } + + ui_HideCursor(); + ui_Flush(); + + return UI_frame_result; +} + + +// add item. +bool mmInterface::AddItem(int id, int key, const char *text, int type) +{ + if (m_nmenu_items == N_MMENU_ITEMS) { + Int3(); // N_MMENU_ITEMS needs to be upped! (Samir) + return false; + } + + m_menuitems[m_nmenu_items].Create(id, key, MMITEM_X, MMITEM_Y+(m_nmenu_items*20), text, (type==1) ? UIF_GROUP_START : (type==2) ? UIF_GROUP_END : (type==3) ? (UIF_GROUP_START+UIF_GROUP_END) : 0 ); + m_nmenu_items++; + + return true; +} + + +// When interface is being nuked. +void mmInterface::OnDestroy() +{ + UIWindow::OnDestroy(); + + D3MusicStop(); + Sound_system.StopAllSounds(); + + + if (static_menu_background) { + FreeLargeBitmap(&m_art); + } + else { + #ifdef MOVIE_MENU + if (m_movie) { + EndMovie(m_movie); + m_movie = NULL; + } + #else + FreeLargeBitmap(&m_art); + #endif + } + + SetUICallback(DEFAULT_UICALLBACK); + MM_object = NULL; +} + + +// displays the copyright text +void mmInterface::CopyrightText() +{ + int i; + char type[16]; + type[0] = 0; +#ifdef DEMO + strcpy(type, "Demo "); +#endif + + if (PROGRAM(beta)) + strcpy(type, "Beta "); + +#ifdef OEM + strcat(type, "OEM "); +#endif + + if (Program_version.version_type == DEVELOPMENT_VERSION) { + strcat(type, "Dev"); + } + else if (Program_version.version_type == RELEASE_VERSION) { + strcat(type, "Ver"); + } +#ifndef RELEASE + int x = Max_window_w - 128,y = Max_window_h-29; //was -128 and -16 +#else + int x = Max_window_w - 164,y = Max_window_h-29; //was -128 and -16 +#endif +// attempt to print text nicely. + grtext_SetFont(BRIEFING_FONT); + grtext_SetAlpha(192); + grtext_SetColor(GR_RGB(255,32,32)); +#if ( defined(MACINTOSH) || (!defined(RELEASE)) || defined(DEMO) ) + grtext_Printf(x,y, Program_version.build ? "%s %d.%d.%d" : "%s v%d.%d", type, Program_version.major, Program_version.minor, Program_version.build); +#else + grtext_Printf(x,y, Program_version.build ? "%s %d.%d.%d Build %d" : "%s v%d.%d", type, Program_version.major, Program_version.minor, Program_version.build,D3_RELEASE_BUILD_NO); +#endif +// grtext_CenteredPrintf(0, Max_window_h-16, "(c) 1998 Outrage Entertainment, Inc."); + + grtext_SetFlags(GRTEXTFLAG_SATURATE); + + for (i = 0; i < 1; i++) + { + grtext_Printf(x,y, Program_version.build ? "%s %d.%d.%d" : "%s v%d.%d", type, Program_version.major, Program_version.minor, Program_version.build); +// grtext_CenteredPrintf(0, Max_window_h-16, "(c) 1998 Outrage Entertainment, Inc."); + } + + grtext_Flush(); +} + + +void mmInterface::SetMusicRegion(int region) +{ + if (region == -1) { + D3MusicStop(); + D3MusicStart("mainmenu.omf"); + SetMusicRegion(MM_MUSIC_REGION); + } + else { + D3MusicSetRegion(region ,true); + } +} + + +// display menu scene +void MenuScene() +{ + extern bool NewUI_wait_dialog; // is wait dialog up? + + if (MM_object) + { + StartFrame(); + + if (static_menu_background) + { + DrawLargeBitmap (&MM_object->m_art, 0, 0, 1.0f ); + } + else + { + #ifdef MOVIE_MENU + FrameMovie( MM_object->m_movie, -1, -1, true ); + #else + DrawLargeBitmap(&MM_object->m_art, 0, 0, 1.0f); + #endif + } + + MM_object->CopyrightText(); + + EndFrame(); + } +} + + +#define N_MENU_SOUND_SLOTS 4 + +void PlayMenuSound(int sound_index, bool wait_till_done) +{ + int sound_uid; + + Sound_system.BeginSoundFrame(false); + sound_uid = Sound_system.Play2dSound(sound_index, 1.0f); + Sound_system.EndSoundFrame(); + + if (wait_till_done) + { + float timer = timer_GetTime(); + while (Sound_system.IsSoundPlaying(sound_uid) && ((timer+5.0f)> timer_GetTime())) + { + Sound_system.BeginSoundFrame(false); + Descent->defer(); + Sound_system.EndSoundFrame(); + } + } +} \ No newline at end of file diff --git a/Descent3/mmItem.h b/Descent3/mmItem.h new file mode 100644 index 000000000..4d7c410da --- /dev/null +++ b/Descent3/mmItem.h @@ -0,0 +1,185 @@ +/* + * $Source: $ + * $Revision: 14 $ + * $Author: Kevin $ + * $Date: 7/28/99 4:01p $ + * + * main menu user interface items + * + * $Log: /DescentIII/main/mmItem.h $ + * + * 14 7/28/99 4:01p Kevin + * Mac + * + * 13 4/29/99 3:21a Samir + * reorganized main menu music to work in config, multiplayer, whereever. + * + * 12 4/15/99 2:32p Kevin + * Added some code for the Demo + * + * 11 4/15/99 1:40a Jeff + * changes for linux compile + * + * 10 4/02/99 3:03p Samir + * opengl doesn't use main menu movie. + * + * 9 3/18/99 10:13a Samir + * new main menu interface. + * + * 8 2/26/99 5:34p Samir + * oem screen. + * + * 7 10/22/98 12:46a Matt + * Made MenuScene() public. + * + * 6 10/20/98 6:24p Samir + * moved menu down. + * + * 5 10/20/98 3:52p Samir + * tweaked main menu look. + * + * 4 10/13/98 3:16p Samir + * enhanced gadgets some more with new effects (added Credits option). + * + * 3 10/12/98 4:40p Samir + * fancy effect for hotspots in main menu. + * + * 2 10/12/98 1:46p Samir + * initial new main menu + * + */ +#ifndef MMITEM_H +#define MMITEM_H +#include "newui.h" +#include "psclass.h" +#include "gamefont.h" +#if ( (!defined(OEM)) && (!defined(DEMO)) ) +#define MOVIE_MENU +#endif +#define MMITEM_ALPHA 192 // default alpha +#define MMITEM_SAT 0 // default saturation +#define MMITEM_FONT MENU_FONT // default font +#define MMITEM_COLOR GR_RGB(255,255,255) // default color of main menu +#define MMITEM_X (Max_window_w*3/5) // x position of menu text +#define MMITEM_Y 175 // y position of menu text start +#define N_MMENU_ITEMS 10 // modify this value to set the maximum main menu items avail. +#define MM_STARTMENU_TYPE 1 // start menu group (used in mmInterface::AddITem) +#define MM_ENDMENU_TYPE 2 // end menu group +// main menu sounds +#define N_MM_SOUNDS 2 +#define MM_SELECT_SOUND 0 +#define MM_FOCUS_SOUND 1 +// main menu music +#define MM_MUSIC_REGION 0 +#define NEWGAME_MUSIC_REGION 1 +#define OPTIONS_MUSIC_REGION 2 +#define MULTI_MUSIC_REGION 3 +// mmItem FX list. +typedef enum tmmItemFX +{ + mmItemFXNull, + mmItemFXNormal, // displays item with normal alpha, color, saturation. + mmItemFXFadeInToBright, + mmItemFXFadeOutToNormal // fade in and out to and from brighten +} +tmmItemFX; + +void MenuScene(); // display menu scene +///////////////////////////////////////////////////////////////////// +//gcc doesn't like the tQueue template in psclass.h +#ifdef __LINUX__ +// tQueue +// a circular queue implementation +class tmmItemQueue +{ +#define tmmItemSIZE 8 + tmmItemFX m_items[tmmItemSIZE]; + short m_head, m_tail; +public: + tmmItemQueue() { m_head = m_tail = 0; }; + ~tmmItemQueue() { }; + void send(tmmItemFX& item) { // sends an item onto the queue + short temp = m_tail+1; + if (temp == tmmItemSIZE) + temp = 0; + if (temp != m_head) { + m_items[m_tail] = item; + m_tail = temp; + } + }; + bool recv(tmmItemFX* item) { // returns an item from the queue, false if no item. + if (m_head == m_tail) + return false; + *item = m_items[m_head++]; + if (m_head == tmmItemSIZE) + m_head = 0; + return true; + }; + void flush() { // flush queue entries. + m_head = m_tail = 0; + }; +}; +#endif +////////////////////////////////////////////////////////////////////// +class mmInterface; +// class mmItem +class mmItem : public UIGadget +{ + char *m_text; // text for item + short m_alpha; // alpha for text item + short m_satcount; + tmmItemFX m_curfx; // current effect +#ifndef __LINUX__ + tQueue m_fxqueue; // special effects queue +#else + tmmItemQueue m_fxqueue; +#endif + ddgr_color m_colors[6]; // colors for menu (4 corners and 2 mid points) + float m_process_speed; // speed gadget is processed (state changes, etc every x seconds) + float m_last_frametime; // last frame's time. + static mmInterface *m_window; // attached window +public: + mmItem(); + virtual ~mmItem(); +public: +// attaches a window to all mmitems. + static void AttachInterface(mmInterface *wnd); + void Create(int id, int key, int x, int y, const char *text, int flags, tmmItemFX init_fx=mmItemFXNormal); + void AddEffect(tmmItemFX fx); + void ClearEffects(); +protected: + virtual void OnMouseBtnDown(int btn); // override: behavior when mouse button is pressed. + virtual void OnMouseBtnUp(int btn); // override: behavior when mouse button is released. + virtual void OnFormat(); // override: called when resized or before drawing. + virtual void OnDraw(); // override: behavior when gadget is being drawn. + virtual void OnLostFocus(); // override: behavior when gadget loses input focus. + virtual void OnGainFocus(); // override: behavior when gadget gains input focus. + virtual void OnDestroy(); // override: behavior when gadget is destroyed. + virtual void OnUserProcess(); // override: behavior when gadget is processed + virtual void OnSelect(); +}; +// Main Menu Interface Object +struct tCinematic; +class mmInterface: public UIWindow +{ + int m_nmenu_items; // number of menu items available. + mmItem m_menuitems[N_MMENU_ITEMS]; // main menu items +#ifdef MOVIE_MENU + tCinematic *m_movie; // menu movie handle +#endif + tLargeBitmap m_art; // artwork for the bitmap +public: +// type MM_STARTMENU_TYPE = start, type MM_ENDMENU_TYPE = end, (1+2) = start and end, type 0 = normal + bool AddItem(int id, int key, const char *text, int type =0); + void Create(); + int DoUI(); // does user interface. +public: + int SoundHandles[N_MM_SOUNDS]; + void SetMusicRegion(int region); + friend void MenuScene(); // display menu scene +protected: + virtual void OnDestroy(); // destroys window. +private: + void CopyrightText(); // displays the copyright text +}; +#endif diff --git a/Descent3/multi.cpp b/Descent3/multi.cpp new file mode 100644 index 000000000..b443a30f0 --- /dev/null +++ b/Descent3/multi.cpp @@ -0,0 +1,10955 @@ +/* + * $Logfile: /DescentIII/Main/multi.cpp $ + * $Revision: 530 $ + * $Date: 10/22/01 12:42p $ + * $Author: Matt $ + * + * Multiplayer code + * + * $Log: /DescentIII/Main/multi.cpp $ + * + * 530 10/22/01 12:42p Matt + * Changed previous fix to use spiffy new function to test for Mercenary + * installed. + * + * 529 10/22/01 11:12a Matt + * Put up HUD message when server tells us to switch ships. + * If told to switch to Black Pyro but don't have Merc, bail. + * + * 528 9/29/01 5:34p Kevin + * hack to prevent the observermode powerup stealing cheat/bug + * + * 527 9/13/01 2:32p Matt + * When recording a demo, save the new player position when a player + * respawns. + * + * 526 7/09/01 4:21p Matt + * Fixed code that used AllowedShips[] instead of Ships[] to find a valid + * ship. + * + * 525 4/20/00 6:23p Matt + * Fixed a bug that caused the difficulty for games in the games list to + * be always set to zero or one. + * + * 524 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 523 3/20/00 12:17p Matt + * Merge of Duane's post-1.3 changes. + * Check for callsign too long. + * Byteswap fix. + * + * 522 2/01/00 3:47a Jason + * made usesmoothing be a client side option only + * + * 521 1/30/00 2:11p Jason + * Added -usesmoothing option back in + * + * 520 1/22/00 10:43p Jason + * Fixed phantom napalm and vauss bugs + * + * 519 1/22/00 5:36p Jason + * Took out reliable secondaries. Define RELIABLE_SECONDARIES to reenable + * this behavior (not recommended) + * + * 518 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 517 10/16/99 9:56p Jeff + * added a way to strip all players of all weapons and energy + * + * 516 10/16/99 8:45p Jeff + * created multi functions to strip a player of his weapons and energy + * + * 515 9/17/99 2:43p Kevin + * fixed bug where pxo stats weren't written if the game quit + * + * 514 9/09/99 12:24p Kevin + * Fixed a bug that was causing problems in the Mac version + * (Game_is_master_tracker_game was defined as a ubyte in the game, but an + * int in the dll) + * + * 513 9/02/99 3:34p Jason + * send secondary fire reliable in a C/S game + * + * 512 9/01/99 6:56p Jason + * fixed guided missiles and timeout missiles so they work in multiplayer + * + * 511 9/01/99 4:12p Kevin + * Byte ordering fixes for the macintosh + * + * 510 8/23/99 5:14p Kevin + * Macintosh fixes + * + * 509 7/28/99 4:05p Kevin + * Mac + * + * 508 7/28/99 3:58p Kevin + * mac + * + * 507 7/20/99 12:57p Jason + * added MOTD to server + * + * 506 7/12/99 4:15p Kevin + * Changed the way we determine if we should report stats or not in PXO + * + * 505 7/12/99 1:56p Jason + * fixed dumb ammo bug + * + * 504 7/08/99 5:44p Jason + * Fixed some cheating holes for the patch + * + * 503 7/06/99 5:52p Kevin + * PXO & multiplayer fixes for the patch + * + * 502 5/24/99 5:42p Jason + * Fixed off by one observer crash + * + * 501 5/23/99 3:04a Jason + * fixed bug with player rankings not being updated correctly + * + * 500 5/22/99 10:27p Jason + * changes for multiplayer and buildings, ai + * + * 499 5/22/99 12:33a Jason + * added sending of physics flags upon joining + * + * 498 5/21/99 7:07p Jason + * fixed some weird multiplayer rejoin anomalies + * + * 497 5/20/99 4:55p Jason + * added heartbeats to server + * + * 496 5/20/99 4:10p Jason + * added heartbeat to multiplayer so clients wouldn't time out, also + * various multiplayer fixes + * + * 495 5/20/99 2:52p Jason + * made autowaypoints work in coop + * + * 494 5/20/99 11:57a Jason + * took out bogus int3 + * + * 493 5/20/99 2:48a Matt + * Auto-waypoints now stored & used per player. Manual waypoints will all + * players, once Jason makes it work. + * + * 492 5/19/99 9:58p Jason + * no collide with ghosted ships + * + * 491 5/19/99 5:39p Jason + * made level failing work in coop + * + * 490 5/19/99 4:58p Jason + * changes for ranking system + * + * 489 5/19/99 4:10p Jason + * fixed wacky killer problem + * + * 488 5/19/99 3:24p Jason + * fixed wrong ordering of InitObjectScripts and MultiSendObject + * + * 487 5/19/99 12:11p Jason + * fixed some issues with multiplayer + * + * 486 5/19/99 12:03p Kevin + * added mprintf + * + * 485 5/18/99 10:15p Kevin + * made it so a client who is disconnected but the server still hasn't + * timed him out will not see the game until the server times him out, and + * he won't stay connected while in the join games screen + * + * 484 5/13/99 7:19p Jason + * made dying objects be able to be updated + * + * 483 5/13/99 7:18p Jeff + * safety check in MultiDoUnattach + * + * 482 5/13/99 1:07p Jason + * added ASSERT for Jeff + * + * 481 5/12/99 1:57p Jason + * fixed yet more buggy/ugly code + * + * 480 5/11/99 9:42p Jeff + * send tracker id of players to other players + * + * 479 5/11/99 10:59a Kevin + * Ship allow/dissalow works now! + * + * 478 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 477 5/10/99 5:35p Kevin + * New command line options for heat and scoring API enhancements + * + * 476 5/10/99 1:45a Jason + * took out turret and robot position counters + * + * 475 5/10/99 1:22a Jason + * took out object error call + * + * 474 5/09/99 1:34p Kevin + * Added diffuculty level system to multiplayer + * + * 473 5/08/99 5:22p Jason + * fix audio taunt crash in single player + * + * 472 5/08/99 4:31a Jeff + * fixed sequencing bug where clients never got a level end event for the + * multiplayer games + * + * 471 5/07/99 3:16p Jason + * fixed some more multiplayer issues + * + * 470 5/07/99 2:50p Jason + * fixed a bunch of endlevel multiplayer issues + * + * 469 5/06/99 1:04a Kevin + * scoring API stuff + * + * 468 5/05/99 5:41p Jason + * fixed various multiplayer issues, including sequencing bugs and cheat + * prevention + * + * 467 5/05/99 12:23p Jason + * fixed some problems with ending levels + * + * 466 5/05/99 10:27a Kevin + * fixed some more player count bugs. + * + * 465 5/05/99 10:00a Kevin + * fixed dedicated server 'current number of players' count + * + * 464 5/03/99 2:35p Jason + * change for multiplayer games + * + * 463 5/03/99 1:17p Jason + * hack for disappearing powerup bug + * + * 462 5/03/99 8:38a Jeff + * handle world states for room's with goal/special flag change + * + * 461 5/02/99 5:09a Jeff + * send levelgoal state to joining clients + * + * 460 4/30/99 7:49p Jason + * setups urgent/non urgent reliable stuff better + * + * 459 4/30/99 5:43p Jason + * took out some errors + * + * 458 4/30/99 5:15p Kevin + * possibly fixed bug related to dedicated server player count + * + * 457 4/30/99 1:10p Jason + * fixed sounds being sent reliably by the server + * + * 456 4/30/99 12:58p Jeff + * fixed inventory removed bug in multiplayer + * + * 455 4/30/99 12:01a Jason + * added ability to set the next level in a multiplayer game + * + * 454 4/29/99 11:02p Jeff + * added the ability for the server to set audio taunt delay time via + * command line option and/or dedicated server console + * + * 453 4/29/99 4:59p Jason + * fixed bug with world states and refueling centers + * + * 452 4/29/99 3:07p Jason + * added more cheat protection + * + * 451 4/29/99 2:09a Jason + * added more safechecking for powerup bug tracking + * + * 450 4/29/99 2:00a Chris + * Added the portal blockage support + * + * 449 4/28/99 9:16p Jeff + * fixed inventory use bug...was using Server_object_list instead of + * Local_object_list + * + * 448 4/28/99 7:37p Jason + * fixed killer objnum bug + * + * 447 4/28/99 5:13p Jeff + * fixed(?) clients using inventory items in coop + * + * 446 4/28/99 3:33a Jeff + * removed useless IGC variable reset (wasn't needed, although previously + * thought it was needed) + * + * 445 4/28/99 2:28a Jeff + * finished (hopefully) making guidebot multiplayer friendly + * + * 444 4/27/99 8:32p Jason + * fixed cinematic bug + * + * 443 4/27/99 3:46p Jeff + * increased audio taunt delay to 5 seconds + * + * 442 4/27/99 4:42a Jeff + * pass guidebot name on MyInfo + * + * 441 4/26/99 11:48p Jason + * removed mprintfs + * + * 440 4/26/99 10:14p Kevin + * Stop playing the napalm sound when a player dies in multi + * + * 439 4/26/99 7:08p Jason + * fixed doorway join bug + * + * 438 4/26/99 11:59a Jeff + * removed stupid test code that wasn't tested and broke everything + * + * 437 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 436 4/25/99 5:20p Jason + * fixed some more coop bugs + * + * 435 4/25/99 5:02p Kevin + * Bunches of multiplayer UI improvements + * + * 434 4/25/99 12:19p Kevin + * fixed max players for dedicated server + * + * 433 4/25/99 8:57a Kevin + * fixes for server info dialog + * + * 432 4/24/99 11:58p Kevin + * Game info list (hit I in the pxo game list) + * + * 431 4/24/99 6:44p Jeff + * added functions for theif so he can steal things other than weapons + * + * 430 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 429 4/22/99 5:46p Jason + * made weapons work in multiplayer + * + * 428 4/22/99 3:24p Jason + * fixed some multisafe problems + * + * 427 4/21/99 12:05p Jason + * fixed sound priority bug + * + * 426 4/21/99 11:42a Kevin + * fixed compiler warning + * + * 425 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 424 4/19/99 3:46a Jeff + * fixed min/max problem + * + * 423 4/17/99 4:33p Jason + * changes for robot/turret tracking + * + * 422 4/16/99 11:23p Jeff + * added some includes to make linux happy + * + * 421 4/16/99 11:54a Matt + * Took out include of directplay.h, which wasn't needed. + * + * 420 4/16/99 11:21a Kevin + * Added netgame flags to game info packet + * + * 419 4/15/99 2:39p Kevin + * Increased the amount of time the dedicated server will wait before + * going on to the next level (to make sure the reliable code has time to + * catch up on missed packets) + * + * 418 4/15/99 1:40a Jeff + * changes for linux compile + * + * 417 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 416 4/11/99 1:57p Kevin + * Made some static memory usage reductions + * + * 415 4/08/99 3:13p Matt + * Finished cleaning up level sequencing code. Got rid of all the "level + * minus one" stuff. + * + * 414 4/07/99 12:30p Matt + * Added code for failed missions. + * + * 413 4/06/99 6:24p Jason + * various fixes for multiplayer + * + * 412 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 411 4/04/99 8:15p Jeff + * added debug graph stuff + * + * 410 4/02/99 6:30p Jason + * fixed multiplayer powerup/player start bugs + * + * 409 4/01/99 5:48p Kevin + * Put in code to make it easy to add the guidebot to multiplayer (coop) + * games + * + * 408 3/30/99 5:36p Matt + * Fixed compile warnings + * + * 407 3/29/99 7:31p Jason + * added better handling of non vis generic objects + * + * 406 3/27/99 2:19p Jason + * transmit rankings when a player joins + * + * 405 3/25/99 1:41p Jason + * took out debug mprintfs + * + * 404 3/25/99 1:41p Jeff + * I'm a moron...forgot to send over player logo stuff on client join... + * + * 403 3/25/99 11:18a Jason + * fixed fog state bug + * + * 402 3/22/99 6:21p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 401 3/17/99 4:08p Kevin + * Changed the way games appear and timeout in the game list. + * + * 400 3/15/99 3:24p Kevin + * Fixed some ingame file xfer stuff + * + * 399 3/12/99 7:49p Jeff + * save player type changes and objects set to use life left + * + * 398 3/11/99 9:10p Jeff + * fixed remaining known bugs in demo system (DemoWriteObjCreate being + * called twice and 50 shields + * + * 274 10/21/98 3:04p Kevin + * + * 273 10/21/98 9:48a Kevin + * took out some debugging code and mprintfs + * + * 272 10/20/98 5:46p Kevin + * Gunboy and other fixes + * + * 271 10/20/98 1:48p Jason + * fixed some reliable/unreliable problems + * + * 270 10/20/98 1:38p Kevin + * added mprintf's for debugging + * + * 269 10/20/98 2:53a Kevin + * gunboy crap + * + * 268 10/20/98 12:39a Jason + * took out erroneous checks + * + * 267 10/20/98 12:12a Jason + * fixed stupid bug + * + * 266 10/19/98 11:46p Jason + * changes for multiplayer debug layer + * + * 265 10/19/98 11:20p Matt + * Changed some bytes type ubytes, since bytes are unsigned anyway. + * + * 264 10/19/98 10:37p Jeff + * + * 263 10/19/98 7:51p Kevin + * performance testing + * + * 262 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 261 10/19/98 5:17p Jason + * fixed some vis issues (hopefully for good) + * + * 260 10/19/98 5:07p Jason + * put vis stuff back in + * + * 259 10/18/98 7:59p Jeff + * the function used to see if a player is banned has new prototype + * + * 258 10/18/98 2:59p Jason + * fixes for beta4 + * + * 257 10/17/98 12:46p Kevin + * Beta 4 fixes + * + * 256 10/16/98 3:19p Jason + * now checks to see if persistant data is same on client/server + * + * 255 10/16/98 2:52p Jason + * trying to debug world states + * + * 254 10/16/98 2:24p Jason + * changes for the demo + * + * 253 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 252 10/16/98 11:55a Kevin + * Made dlls loadable in a hog + * + * 251 10/14/98 11:23p Jeff + * removed hud messages on player join + * + * 250 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 249 10/14/98 11:46a Jason + * made camera scripts work correctly + * + * 248 10/13/98 9:26p Jason + * trying to track down wind problems + * + * 247 10/13/98 2:16a Jeff + * added call to EVT_GAME_DISCONNECTED in MultiLeaveGame() + * + * 246 10/11/98 2:57a Jeff + * added new multiplayer event EVT_GAME_INTERVAL, which is to be called on + * interval, and EVT_HUD_INTERVAL is only to be called when the hud is to + * be rendered + * + * 245 10/08/98 7:30p Samir + * game sequencing changes. + * + * 244 10/08/98 3:38p Jeff + * removed time_left from Netgame + * + * 243 10/07/98 5:05p Jason + * added quaded weapons + * + * 242 10/07/98 1:06p Jason + * added more safety precautions + * + * 241 10/07/98 10:25a Jason + * Now send damage reliably + * + * 240 10/06/98 6:07p Jason + * added protective layer for reliable packets + * + * 239 10/06/98 4:00p Jason + * Reset a players ship at the beginning of a new level + * + * 238 10/05/98 7:23p Jason + * added protective layer onto multiplayer + * + * 237 10/05/98 11:08a Jason + * fixed some potential gap problems in the multiplayer sequencing code + * + * 236 10/02/98 3:26p Chris + * Removed the bug ridden interpolation code + * + * 235 10/01/98 4:58p Kevin + * fixed debug code + * + * 234 10/01/98 2:48p Kevin + * put in debug info for removing a powerup + * + * 233 10/01/98 2:09p Jason + * more fixes to object creation + * + * 232 10/01/98 2:07p Jason + * possible fix for script problems + * + * 231 10/01/98 12:41p Jason + * more world state stuff + * + * 230 10/01/98 12:16p Kevin + * Fixed Ping for Peer-Peer games + * + * 229 10/01/98 12:02p Kevin + * Fixed PXO ping time + * + * 228 10/01/98 11:36a Kevin + * UI fixes and stuff + * + * 227 10/01/98 11:29a Jason + * changes for world states in multiplayer games + * + * 226 10/01/98 10:21a Jason + * added some testing mprintfs + * + * 225 9/30/98 6:56p Jason + * more changes for DLLS + * + * 224 9/30/98 6:25p Jason + * debugging info for remove object + * + * 223 9/30/98 5:35p Jason + * added multiplayer menu bailing for samir + * + * 222 9/30/98 3:14p Jason + * fixed some possible bugs with -1 objnums and scripts/dlls + * + * + * 219 9/29/98 11:18a Jeff + * fixed OBJ_DUMMY problems on client join objects + * + * 218 9/28/98 6:23p Chris + * Changed multi_anim to custom_anim + * + * 217 9/28/98 6:23p Jason + * added persistant data + * + * 216 9/28/98 12:56p Jason + * multiplayer bug fixes + * + * 215 9/28/98 11:02a Kevin + * added Networking defer, and fixed some UI issues + * + * 214 9/28/98 10:33a Jason + * changed all asserts to BailOnMultiplayer for clean exits + * + * 213 9/28/98 10:06a Jason + * fixed some multiplayer bugs + * + * 212 9/28/98 9:53a Kevin + * Fixing misc UI problems, and fixed some bugs that VC 6 found + * + * 211 9/25/98 9:25p Jason + * turned vis stuff back on + * + * 210 9/25/98 5:33p Jason + * fixed parenting again + * + * 209 9/25/98 4:21p Jason + * more multiplayer bug checking + * + * 208 9/25/98 3:01p Kevin + * fixxed ping code + * + * 207 9/25/98 12:06a Jason + * fixed some multiplayer scripting issues + * + * 206 9/24/98 4:59p Jason + * fixed script bug + * + * 205 9/24/98 4:41p Jason + * handled a multiplayer script problem + * + * 204 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 203 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 202 9/15/98 2:44p Jason + * fixed bug with my last rev + * + * 201 9/15/98 12:47p Jason + * made spewed countermeasures work correctly + * + * 200 9/11/98 12:25p Jason + * got energy to shield and fusion damage working in multiplayer + * + * 199 9/10/98 12:18p Jason + * more changes to afterburner/thrust effect + * + * 198 9/09/98 7:09p Jason + * changed afterburner effect for ships + * + * 197 9/04/98 3:45p Kevin + * Added ping_time to Netplayers struct. It's updated in peer-peer and + * client server + * + * 196 9/02/98 6:54p Kevin + * Fixed general directplay support up, and got modem-modem working + * + * 195 9/01/98 11:19a Matt + * Re-added wrongly-deleted ResetPlayerObject() call in + * MultiStartNewLevel() + * + * 194 8/31/98 10:14a Kevin + * Misc. multi-UI fixes + * + * 193 8/28/98 1:34p Matt + * Added code to reset the waypoint when starting a new level, and while I + * was at it cleaned up the new level start sequencing. + * + * 192 8/26/98 4:30p Jason + * added directional lights (headlight for player) + * + * 191 8/24/98 2:45p Jason + * added team start position stuff + * + * 190 8/19/98 11:49a Kevin + * Got DirectPlay IPX working, and localized connection DLLs + * + * 189 8/15/98 5:16p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 188 8/13/98 6:32p Kevin + * Initial implementation of directplay API + * + * 187 8/13/98 6:09p Jason + * addendum to my last rev....made it work with REMOVE_OBJECT packet + * + * 186 8/13/98 6:08p Jason + * Fixed blowing up of robots + * + * 185 8/12/98 6:36p Jeff + * converted the send link/unlink functions to handle ghosted objects. + * Updated MultiSendObject to handle dummy objects + * + * 184 8/12/98 11:54a Jeff + * added link/unlink object packets + * + * 183 8/10/98 5:27p Kevin + * fixed coop bug with tubs + * + * 182 8/10/98 4:58p Kevin + * Send robot orientation as 3 angles + * + * 181 8/10/98 2:21p Jeff + * changes made due to adding flag for inventory reset + * + * 180 8/07/98 6:03p Jason + * took out magnitude shift for player positions + * + * 179 8/07/98 12:27p Kevin + * Added network stuff for energy/shield conversion + * + * 178 8/05/98 6:59p Kevin + * + * 177 8/05/98 2:25p Kevin + * improved network address reporting + * + * 176 8/05/98 11:48a Kevin + * + * 175 8/05/98 10:46a Kevin + * sound & bmp exchange fixes + * + * 174 8/04/98 6:13p Kevin + * fixes for sound & bmp exchange + * + * 173 8/04/98 3:07p Kevin + * sound & bmp exchange fixes for > 2 players + * + * 172 8/04/98 10:26a Kevin + * Custom sound and bmp exchange system + * + * 171 8/03/98 5:56p Jason + * got fusion cannon working correctly + * + * 170 8/03/98 4:28p Jason + * added first pass of spline movement trick + * + * 169 7/31/98 11:51a Jason + * added player ship choosing to multiplayer games + * + * 168 7/30/98 11:09a Jason + * took out warp effect + * + * 167 7/29/98 6:40p Jason + * fixed on/off problem + * + * 166 7/29/98 5:47p Jason + * added warp effect again + * + * 165 7/29/98 5:39p Kevin + * sound/bitmap exchange + * + * 164 7/29/98 2:29p Jason + * got rid of some compiler warnings + * + * 163 7/29/98 2:02p Jason + * implemented multiplayer positional flags to help trim bandwidth + * consumption + * + * 162 7/29/98 12:40p Jason + * more multiplayer testing for occlusion and packet sequencing + * + * 161 7/28/98 3:42p Jason + * added positional sequencing back in + * + * 160 7/28/98 12:43p Jason + * trying a multiplayer experiment with a compressed orientation matrix + * + * + * 159 7/28/98 12:29p Jason + * added rotational velocity to multiplayer position packets + * + * 158 7/27/98 5:59p Jason + * added piggyback mode plus multiplayer colors + * + * 157 7/27/98 5:31p Kevin + * Sound/Bitmap exchange system + * + * 156 7/24/98 4:44p Jason + * added temporary multi_occluded variable + * + * 155 7/23/98 12:38p Jason + * more tweaks for multiplayer vis stuff + * + * 154 7/22/98 3:16p Jason + * added observer mode + * + * 153 7/21/98 2:59p Kevin + * peer-peer fire packets + * + * 152 7/21/98 1:49p Kevin + * IPX support and peer-peer option for multi + * + * 151 7/21/98 12:07p Jason + * made multiplayer work with BOA vis system + * + * 150 7/21/98 10:27a Kevin + * peer-peer fixes + * + * 149 7/20/98 7:23p Jeff + * fixed peer-peer/client server booboo + * + * 148 7/20/98 6:39p Jason + * first attempt at getting player visibilty stuff working in multiplayer + * + * 147 7/20/98 6:20p Kevin + * peer-peer stuff + * + * 146 7/17/98 5:57p Jason + * misc multiplayer changes + * + * 145 7/17/98 2:50p Jason + * a couple fixes for multiplayer bugs + * + * 144 7/17/98 1:22p Kevin + * Dynamic retransmission of reliable packets and stuff + * + * 143 7/15/98 1:22p Kevin + * Updated player network address for clients + * + * 142 7/15/98 12:48p Jeff + * sending client execute DLLs now allows a dllinfo struct to be passed + * + * 141 7/15/98 12:27p Kevin + * Fixed bug in execscriptwithparms and references + * + * 140 7/15/98 11:21a Kevin + * Added doors to the server object list + * + * 139 7/15/98 10:38a Kevin + * Fixes erroneous debug warning in multidomsgwithparms + * + * 138 7/14/98 5:52p Kevin + * Packet loss measurements and auto pps adjusting + * + * 137 7/13/98 11:52a Kevin + * Added Callscriptwithparms + * + * 136 7/10/98 10:29a Kevin + * Added parent handle to sendobj + * + * 135 7/09/98 4:50p Kevin + * added render type to multisendobj + * + * 134 7/09/98 3:26p Jason + * fixed scripting name bug + * + * 133 7/08/98 6:01p Jeff + * added packet to remove an item from inventory + * + * 132 7/08/98 5:31p Kevin + * Coop fixes + * + * 131 7/08/98 11:35a Kevin + * weapon battery info + * + * 130 7/08/98 11:27a Jeff + * removed yesterdays inventory functions, since inventory.cpp has been + * rolled back...its outdated now + * + * 129 7/07/98 7:33p Jeff + * changes made for inventory use + * + * 128 7/07/98 6:31p Jason + * fixed jeffery thang + * + * 127 7/07/98 3:46p Kevin + * Turrett info + * + * 126 7/07/98 3:26p Chris + * Added changes for turret updates + * + * 125 7/07/98 3:16p Kevin + * Added inventory parms + * + * 124 7/07/98 2:48p Kevin + * added support for custom scripts in multisendobject + * + * 123 7/07/98 11:19a Kevin + * Added inventory use message + * + * 122 7/07/98 10:10a Kevin + * Added basic turret support for coop + * + * 121 7/06/98 6:46p Kevin + * vector parameter passing in scripts + * + * 120 7/06/98 5:36p Kevin + * Variable parameter passing + * + * 119 7/06/98 11:51a Jason + * added accessor function for countermeasures + * + * 118 7/06/98 9:34a Kevin + * coop + * + * 117 7/02/98 4:52p Kevin + * coop fixes + * + * 116 7/02/98 12:57p Jason + * various changes for multiplayer positional sequencing + * + * 115 7/02/98 12:19p Kevin + * misc network fixes + * + * 114 7/02/98 10:39a Kevin + * More coop stuff + * + * 113 7/01/98 6:30p Kevin + * More coop + * + * 112 7/01/98 2:52p Kevin + * coop -- adding sound index + * + * 111 7/01/98 12:55p Jason + * more changes for countermeasures + * + * 110 7/01/98 12:46p Kevin + * Coop fixes + * + * 109 7/01/98 12:11p Jason + * added countermeasures + * + * 108 6/30/98 7:17p Kevin + * more animation stuff + * + * 107 6/30/98 5:54p Kevin + * Animation frame stuff + * + * 106 6/30/98 5:08p Kevin + * coop animation frame stuff + * + * 105 6/30/98 4:28p Chris + * Checked in some missing commas + * + * 104 6/30/98 3:55p Chris + * + * 103 6/30/98 3:22p Kevin + * Improved game ping time + * + * 102 6/30/98 11:39a Jason + * added AdditionalDamage packet type for multiplayer + * + * 101 6/30/98 10:10a Kevin + * Fix to prevent your old games from showing up in game list + * + * 100 6/29/98 5:53p Kevin + * Coop + * + * 99 6/29/98 3:08p Jason + * added on/off weapons + * + * 98 6/29/98 12:49p Jason + * temp checkin for on/off weapons + * + * 97 6/29/98 12:12p Kevin + * Added robot damage and death packets + * + * 96 6/26/98 6:53p Kevin + * Coop mode + * + * 95 6/26/98 6:20p Jason + * changes for coop + * + * 94 6/25/98 5:22p Kevin + * Req/Send gametime to clients + * + * 93 6/25/98 12:32p Jason + * added new multiplayer functionality + * + * 92 6/24/98 3:24p Kevin + * Updated PXO screens with chat, etc. + * + * 91 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 90 6/16/98 10:54a Jeff + * + * 89 6/11/98 12:48p Jason + * added better spewing weapons + * + * 88 6/10/98 7:32p Kevin + * nw_CloseSocket wasn't being called in MultiDoLeaveGame + * + * 87 6/09/98 6:18p Jeff + * added a call to the multiplayer DLL when level ends and when a player + * leaves the game + * + * 86 6/04/98 4:53p Jeff + * Online time is kept whether or not it's a mastertracker game now + * + * 85 6/01/98 10:09a Kevin + * Added DLL connection interface and auto update DLL + * + * 84 5/25/98 6:38p Matt + * Added needed include. + * + * 83 5/22/98 11:59a Chris + * Fixed improper uses of FindSoundName and made the proper static sounds + * + * 82 5/19/98 6:27p Jason + * put in urgent packets + * + * 81 5/14/98 6:17p Jason + * more sequencing bugs squashed + * + * 80 5/14/98 5:27p Jason + * fixed some more sequencing bugs + * + * 79 5/14/98 11:07a Kevin + * Made gameover packet to the tracker reliable + * + * 78 5/13/98 4:06p Kevin + * Added more mastertracker info + * + * 77 5/12/98 6:11p Kevin + * Added mastertracker values + * + * 76 5/12/98 5:49p Jason + * fixed some multiplayer bugs + * + * 75 5/12/98 5:12p Jason + * added dll callable level ending + * + * 74 5/12/98 4:18p Jason + * added better level sequencing for multiplayer + * + * 73 5/12/98 12:33p Jason + * got level sequencing working in multiplayer + * + * 72 5/11/98 4:40p Chris + * AI info is now a malloc'ed pointer + * + * 71 5/11/98 11:31a Jason + * added some events for level sequencing + * + * 70 5/08/98 6:23p Jason + * tweaked player positioning + * + * 69 5/07/98 5:08p Jason + * added better movement interpolation + * + * 68 5/05/98 3:02p Jason + * attempting to add different screen resolutions + * + * 67 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 66 5/04/98 12:48p Jason + * fixed goalface problem + * + * 65 5/04/98 10:55a Kevin + * Mastertracker fixes/enhancements + * + * 64 5/02/98 5:14p Kevin + * Made mastertracker games not show up on the lan search + * + * 63 4/30/98 3:49p Kevin + * Mastertracker pilot stats + * + * 62 4/27/98 1:14p Jason + * cleaned up afterburner stuff + * + * 61 4/24/98 3:50p Kevin + * Added mastertracker game tracking support + * + * 60 4/22/98 3:39p Jason + * trimmed some fat off player packets + * + * 59 4/22/98 3:29p Jason + * reset player ship when entering a game + * + * 58 4/22/98 3:25p Jason + * changes for multiplayer debugging + * + * 57 4/20/98 12:46p Jason + * added level checksumming for multiplayer games + * + * 56 4/20/98 11:30a Jason + * Added ShowProgressScreen function + * + * 55 4/19/98 3:21p Jason + * added the transmission of teams when a player connects + * + * 54 4/17/98 1:59p Jason + * added cool object effects + * + * 53 4/17/98 12:45p Jason + * various changes for multiplayer + * + * 52 4/14/98 7:56p Matt + * Moved MSN_NAMELEN from mission.h to descent,h, so multi.h didn't need + * to include mission.h. + * + * 51 4/10/98 3:28p Jason + * fixed afterburner bug + * + * 50 4/10/98 2:58p Chris + * Redid where the buddy gets allocated, not all the way done yet. + * + * 49 4/10/98 2:16p Jason + * fixed guided missile problems + * + * 48 4/09/98 5:17p Jason + * got guided working in multiplayer + * + * 47 4/09/98 2:23p Jason + * added guided/afterburner stuff + * + * 46 4/07/98 4:25p Chris + * Added support for buddy bot + * + * 45 4/07/98 12:54p Jason + * changes for viseffects and multiplayer dll + * + * 44 4/06/98 5:56p Jason + * got multiplayer options working + * + * 43 4/06/98 2:54p Jason + * yet more multiplayer changes + * + * 42 4/06/98 12:14p Jason + * changes to multiplayer + * + * 41 4/03/98 4:08p Jason + * fixed checksum bugs + * + * 40 4/03/98 12:28p Jason + * fixed confusion with clients changing rooms in multiplayer + * + * 39 4/03/98 12:22p Jeff + * fixed bug in change room event + * + * 38 4/03/98 11:04a Jason + * added a multplayer dll hook for when a player changes a room + * + * 37 4/02/98 12:43p Jason + * added functions to remove an object + * + * 36 4/01/98 6:23p Jason + * added a slew of stuff for multiplayer + * + * 35 4/01/98 12:02p Jason + * incremental checkin for rendering changes + * + * 34 3/30/98 5:11p Jason + * more changes for game/dll integration + * + * 33 3/23/98 7:37p Jason + * added colored hud messages + * + * 32 3/23/98 4:51p Jason + * incremental checkin for multiplay + * + * 31 3/20/98 5:51p Jason + * more changes for multiplayer + * + * 30 3/20/98 2:18p Jason + * changes for multiplayer + * + * 29 3/19/98 7:14p Jason + * more changes for multiplayer + * + * 28 3/18/98 5:45p Jason + * more multiplayer script integration + * + * 27 3/18/98 11:08a Jason + * more changes for script and multiplayer + * + * 26 3/17/98 6:17p Jason + * added gamemode collide stuff + * + * 25 3/17/98 6:00p Jason + * changes for gamemode scripts + * + * 24 3/16/98 6:41p Jason + * added user definable messages to multiplayer + * + * 23 3/16/98 11:19a Jason + * got scripts working with multiplayer + * + * 22 2/26/98 4:18p Jason + * fixed player death problem for incoming players + * + * 21 2/18/98 1:52p Jason + * made velocity a 6 byte packet + * + * 20 2/17/98 3:46p Matt + * Revamped weapon system and got sounds for spray and fusion weapons + * working. Still need to implements getting continuous & cutoff sounds + * from player ship. + * + * 19 2/08/98 5:01p Samir + * New game sequencing changes. + * + * 18 2/05/98 2:59p Matt + * Got rid of MultiObjSetPos(). Now just using ObjSetPos() + * + * 17 2/05/98 11:47a Jason + * changes for new roomnum system + * + * 16 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + */ + +#include "pstypes.h" +#include "pserror.h" +#include "player.h" +#include "game.h" +#include "multi.h" +#include "multi_client.h" +#include "multi_server.h" +#include "ddio.h" +#include "hud.h" +#include "robotfire.h" +#include "ship.h" +#include "descent.h" +#include "damage.h" +#include "gamesequence.h" +#include "objinfo.h" +#include "gametexture.h" +#include "room.h" +#include "game2dll.h" +#include "viseffect.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "fireball.h" +#include "Mission.h" +#include "LoadLevel.h" +#include "gamecinematics.h" +#include "init.h" + +#include "sounds.h" +#include "weapon.h" +#include "stringtable.h" + +#include "dedicated_server.h" +#include "demofile.h" +#include "args.h" + +#include "ui.h" +#include "newui.h" +#include "multi_dll_mgr.h" +#include "BOA.h" +#include "attach.h" +#include "mission_download.h" +//#include "gamespy.h" +#include "multi_world_state.h" +#include "ObjScript.h" +#include "audiotaunts.h" +#include "marker.h" +#include "weather.h" +#include "doorway.h" +#include "object_lighting.h" +#include "spew.h" +#include "PHYSICS.H" +#include "SmallViews.h" +#include "demofile.h" +#include "debuggraph.h" +#include "levelgoal.h" +#include "osiris_share.h" +#include "cockpit.h" +#include "hud.h" + + +#include +#include +#include + +#include "psrand.h" + +#include "../md5/md5.h" +void MultiProcessShipChecksum (MD5 *md5, int ship_index); + +#ifdef __LINUX__ +#define min(a,b) ((ax*16.0),data,count); + MultiAddUshort ((pos->z*16.0),data,count); + MultiAddFloat (pos->y,data,count); +} +void MultiExtractPositionData (vector *vec,ubyte *data,int *count) +{ + vec->x=((float)MultiGetUshort (data,count))/16.0; + vec->z=((float)MultiGetUshort (data,count))/16.0; + vec->y=MultiGetFloat (data,count); +} + +void MultiSendBadChecksum(int slot) +{ + ubyte data[MAX_GAME_DATA_SIZE]; + int size; + int count=0; + size=START_DATA (MP_REJECTED_CHECKSUM,data,&count); + END_DATA (count,data,size); + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count); +} + +// Puts player "slot" position info into the passed in buffer +// Returns the number of bytes used +int MultiStuffPosition (int slot,ubyte *data) +{ + int size; + int count=0; + ubyte flags=0; + + object *obj=&Objects[Players[slot].objnum]; + + size=START_DATA (MP_PLAYER_POS,data,&count); + MultiAddByte (slot,data,&count); + + if (slot==Player_num) + { + // tell the reciever to expect fire information, only if + // we fired this frame and it isn't going to be a seperate packet + // (like secondaries in a non-peer to peer, are sent reliably) + if (Player_fire_packet[slot].fired_on_this_frame==PFP_FIRED) + flags|=MPF_FIRED; + } + + // Send timestamp + if (slot==Player_num) + MultiAddFloat (Gametime,data,&count); + else + MultiAddFloat (NetPlayers[slot].packet_time,data,&count); + + // Do position + MultiAddPositionData (&obj->pos,data,&count); + + // Do orientation + angvec angs; + vm_ExtractAnglesFromMatrix (&angs,&obj->orient); + + MultiAddShort (angs.p,data,&count); + MultiAddShort (angs.h,data,&count); + MultiAddShort (angs.b,data,&count); + + // Do roomnumber and terrain flag + + MultiAddShort (CELLNUM (obj->roomnum),data,&count); + + + // Fill flags + if (OBJECT_OUTSIDE(obj)) + flags |=MPF_OUTSIDE; + if (Players[slot].flags & PLAYER_FLAGS_AFTERBURN_ON) + flags |=MPF_AFTERBURNER; + if (Players[slot].flags & PLAYER_FLAGS_THRUSTED) + flags |=MPF_THRUSTED; + if (Objects[Players[slot].objnum].weapon_fire_flags & WFF_SPRAY) + flags |=MPF_SPRAY; + if (Objects[Players[slot].objnum].weapon_fire_flags & WFF_ON_OFF) + flags |=MPF_ON_OFF; + if (Players[slot].flags & PLAYER_FLAGS_DEAD)// || Players[slot].flags & PLAYER_FLAGS_DYING) + flags |=MPF_DEAD; + if (Players[slot].flags & PLAYER_FLAGS_HEADLIGHT) + flags |=MPF_HEADLIGHT; + + // Do velocity + vector *vel=&obj->mtype.phys_info.velocity; + vector *rotvel=&obj->mtype.phys_info.rotvel; + +// mprintf ((0,"Outvel x=%f y=%f z=%f\n",vel->x,vel->y,vel->z)); + + MultiAddShort ((vel->x*128.0),data,&count); + MultiAddShort ((vel->y*128.0),data,&count); + MultiAddShort ((vel->z*128.0),data,&count); + + if (Netgame.flags & NF_SENDROTVEL) + { + MultiAddShort (rotvel->x/4,data,&count); + MultiAddShort (rotvel->y/4,data,&count); + MultiAddShort (rotvel->z/4,data,&count); + + MultiAddShort (obj->mtype.phys_info.turnroll,data,&count); + } + + int weapon_indices=((Players[slot].weapon[PW_SECONDARY].index-10)<<4)|Players[slot].weapon[PW_PRIMARY].index; + MultiAddByte (weapon_indices,data,&count); + + MultiAddUbyte (Players[slot].energy,data,&count); + MultiAddByte (flags,data,&count); + + + END_DATA (count,data,size); + + return count; +} + +void DoNextPlayerFile(int playernum) +{ + NetPlayers[playernum].file_xfer_flags = NETFILE_NONE; + NetPlayers[playernum].custom_file_seq++; + if(NetPlayers[playernum].custom_file_seq > NETFILE_ID_LAST_FILE) + { + NetPlayers[playernum].custom_file_seq = NETFILE_ID_DONE; + mprintf((0,"Setting %s's custom ship logo to %s\n",Players[playernum].callsign,NetPlayers[playernum].ship_logo)); + PlayerSetCustomTexture(playernum,NetPlayers[playernum].ship_logo); + //If we are the server, we need to tell everyone about this guy's custom data + if((Netgame.local_role==LR_SERVER)&&(Use_file_xfer)) + { + for (int i=0;iflags & OF_CLIENT_KNOWS); + + size=START_DATA (MP_ROBOT_POS,data,&count); + MultiAddUshort (objectnum,data,&count); + + // Do position + MultiAddPositionData (&obj->pos,data,&count); + + // Do orientation + + // Do orientation + angvec angs; + vm_ExtractAnglesFromMatrix (&angs,&obj->orient); + + MultiAddShort (angs.p,data,&count); + MultiAddShort (angs.h,data,&count); + MultiAddShort (angs.b,data,&count); + + // Do roomnumber and terrain flag + + MultiAddShort (CELLNUM (obj->roomnum),data,&count); + + if (OBJECT_OUTSIDE(obj)) + MultiAddByte (1,data,&count); + else + MultiAddByte (0,data,&count); + + vector *vel=&obj->mtype.phys_info.velocity; + vector *rotvel=&obj->mtype.phys_info.rotvel; + + MultiAddShort ((vel->x*128.0),data,&count); + MultiAddShort ((vel->y*128.0),data,&count); + MultiAddShort ((vel->z*128.0),data,&count); + + END_DATA (count,data,size); + return count; +} + +// Puts player "slot" position info into the passed in buffer +// Returns the number of bytes used +int MultiSendRobotFireWeapon (unsigned short objectnum,vector *pos,vector *dir,unsigned short weaponnum) +{ + int size; + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + //mprintf((0,"Sending Robot %d fired.\n",objectnum)); + // Check to make sure we're the server + if (Netgame.local_role!=LR_SERVER) + { + BailOnMultiplayer(NULL); + return 0; + } + + //mprintf((0,"@"));//KBTEST + object *obj=&Objects[objectnum]; + ASSERT (obj->flags & OF_CLIENT_KNOWS); + + size=START_DATA (MP_ROBOT_FIRE,data,&count); + MultiAddUshort (objectnum,data,&count); + + // Do position + //memcpy (&data[count],pos,sizeof(vector)); + //count+=sizeof(vector); + MultiAddVector(*pos,data,&count); + + // Do orientation + //memcpy (&data[count],dir,sizeof(vector)); + //count+=sizeof(vector); + MultiAddVector(*dir,data,&count); + + uint index=MultiGetMatchChecksum (OBJ_WEAPON,weaponnum); + MultiAddUint (index,data,&count); + + END_DATA (count,data,size); + //Add it to the outgoing queue. The MultiSendRobotFireSound() function will actually send it + for (int i=0;i=MAX_GAME_DATA_SIZE) + MultiSendFullPacket (i,0); + + memcpy (&Multi_send_buffer[i][Multi_send_size[i]],data,count); + Multi_send_size[i]+=count; + //Don't send because we are waiting for the robot fire sound to send + } + } + return count; +} + +void MultiDoRobotFire(ubyte *data) +{ + int count = 0; + unsigned short obj_num; + vector weapon_pos; + vector weapon_dir; + unsigned int weapon_num; + uint uniqueid; + + if (Netgame.local_role==LR_SERVER) + { + Int3(); // Get Jason, a server got the multi do fire packet + return; + } + + //mprintf((0,"*")); + SKIP_HEADER (data,&count); + int serv_objnum=MultiGetUshort (data,&count); + obj_num = Server_object_list[serv_objnum]; + + if (obj_num==65535 || !(Objects[obj_num].flags & OF_SERVER_OBJECT)) + { + //Int3(); // Get Jason, invalid robot firing! + return; + } + + //memcpy (&weapon_pos,&data[count],sizeof(vector)); + //count+=sizeof(vector); + weapon_pos = MultiGetVector(data,&count); + //memcpy (&weapon_dir,&data[count],sizeof(vector)); + //count+=sizeof(vector); + weapon_dir = MultiGetVector(data,&count); + + weapon_num = MultiGetUint(data,&count); + + if(Objects[obj_num].type==255) + return; + uniqueid = MultiMatchWeapon(weapon_num); + + CreateAndFireWeapon( &weapon_pos, &weapon_dir, &Objects[obj_num], uniqueid); + +} + +// Does a cool vis effect to announce a player or powerup +void MultiAnnounceEffect(object *obj,float size,float time) +{ + int visnum=VisEffectCreate (VIS_FIREBALL,BLAST_RING_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vector norm={0,1,0}; + vis_effect *vis=&VisEffects[visnum]; + vis->size=size; + vis->lifetime=time; + vis->lifeleft=time; + vis->custom_handle=Fireballs[BLUE_BLAST_RING_INDEX].bm_handle; + vis->flags|=VF_PLANAR|VF_REVERSE; + vis->end_pos=norm; + } +} + +// MultiProcessIncoming reads incoming data off the unreliable and reliable ports and sends +// the data to process_big_data +void MultiProcessIncoming() +{ + int size, i; + ubyte *data; + network_address from_addr; + + data = &(Multi_receive_buffer[0]); + + // get the other net players data + while( (size = nw_Receive(data, &from_addr))>0 ) + { + MultiProcessBigData(data, size, &from_addr); + if(ServerTimeout) + { + LastPacketReceived = timer_GetTime(); + } + } // end while + + // read reliable sockets for data + data = &(Multi_receive_buffer[0]); + if(Netgame.local_role==LR_SERVER) + { + for (i=0;i 0) + { + MultiProcessBigData(data,size,&NetPlayers[i].addr); + } + } + } + } + else + { + // if I'm not the master of the game, read reliable data from my connection with the server + if((NetPlayers[Player_num].reliable_socket != INVALID_SOCKET) && (NetPlayers[Player_num].reliable_socket != 0)) + { + while( (size = nw_ReceiveReliable(NetPlayers[Player_num].reliable_socket,data, MAX_RECEIVE_SIZE)) > 0) + { + MultiProcessBigData(data,size,&Netgame.server_address); + } + } + } +} + +// Starts a packet of data +int START_DATA (int type,ubyte *data,int *count,ubyte reliable) +{ + int size_offset; + + MultiAddByte (type,data,count); + + // Do size + size_offset=*count; + MultiAddShort (0,data,count); + +/* + #ifndef RELEASE + + if (Netgame.local_role==LR_SERVER) + { + MultiAddByte (reliable,data,count); + if (reliable) + { + MultiAddInt (Reliable_count,data,count); + Reliable_count++; + } + } + else + MultiAddByte (0,data,count); + + #endif +*/ + return size_offset; +} + + +// End a pakcet of data +void END_DATA (int count,ubyte *data,int offset) +{ + MultiAddShort (count,data,&offset); +} + +// Skips the header stuff at the beginning of a packet +void SKIP_HEADER (ubyte *data,int *count) +{ + MultiGetByte (data,count); + MultiGetShort (data,count); +} + +// called once a frame on client and server. NOTE: only servers (game masters) check for +// listen connections. +void MultiDoFrame() +{ + if (!(Game_mode & GM_MULTI)) + return; + + ScoreAPIFrameInterval(); + + static int debug_id = -2; + static float last_game_time = 99999999.9f; + + CallGameDLL (EVT_GAME_INTERVAL,&DLLInfo); + + if (Netgame.local_role==LR_SERVER) + MultiDoServerFrame (); + else + { + if(debug_id==-2) + { + debug_id = DebugGraph_Add(0.0f,10000.0f,"Ping",DGF_MULTIPLAYER); + } + if(debug_id>=0) + { + if(last_game_time>Gametime) + { + //reset last_game_time, we must have started a new level + last_game_time = Gametime; + } + + float next_time = last_game_time + 0.3f; //update every 1/3 second + if(Gametime>=next_time) + { + last_game_time = Gametime; + if(NetPlayers[Player_num].flags&NPF_CONNECTED && NetPlayers[Player_num].sequence==NETSEQ_PLAYING) + { + DebugGraph_Update(debug_id,NetPlayers[Player_num].ping_time*1000.0f); + } + } + } + + MultiDoClientFrame(); + // Verify object list + } +} + +// Returns to the slot number of the player that the passed in address belongs to +int MultiMatchPlayerToAddress (network_address *from_addr) +{ + int i; + for (i=0;i CALLSIGN_LEN) + length = CALLSIGN_LEN; + else + length = len; + + memcpy (Players[slot].callsign,&data[count],length); + count+=len; + + // Copy the ship name out + len=MultiGetByte (data,&count); + memcpy (ship_name,&data[count],len); + count+=len; + + int ship_index=FindShipName (ship_name); + if (ship_index<0) + ship_index=0; + + if(!PlayerIsShipAllowed(slot,ship_name)) + { + bool found_one=false; + int i; + + //Loop through ships, looking for one that's allowed + for (i=0;iNetgame.packets_per_second) + NetPlayers[slot].pps=Netgame.packets_per_second; + + //pilot picture id + NetPlayers[slot].pilot_pic_id = MultiGetUshort(data,&count); + + // Copy the guidebot name out + char guidebot_name[32]; + memset(guidebot_name,0,32); + len=MultiGetByte (data,&count); + ASSERT(len<32); + memcpy (guidebot_name,&data[count],len); + count+=len; + + //Set the guidebot name + if(Netgame.flags & NF_ALLOWGUIDEBOT) + { + gb_com command; + command.action = COM_DO_ACTION; + command.index = 44;//GBC_RENAME_SILENT + command.ptr = (void *) guidebot_name; + object *b_obj = ObjGet(Buddy_handle[slot]); + ASSERT(b_obj); + AINotify(b_obj, AIN_USER_DEFINED, (void *)&command); + } + + NetPlayers[slot].sequence=NETSEQ_NEED_GAMETIME; + + mprintf ((0,"Got a myinfo packet from %s len=%d!\n",Players[slot].callsign,len)); +} + +// Tell a client about the players connected +// Server only +void MultiDoRequestPlayers (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + if (!(NetPlayers[slot].flags & NPF_CONNECTED)) + { + mprintf ((1,"ERROR! We got a MY_INFO packet from a disconnected player!\n")); + Int3(); // Get Jason + return; + } + + NetPlayers[slot].sequence=NETSEQ_REQUEST_PLAYERS; +} + +// Tell a client about the buildings +// Server only +void MultiDoRequestBuildings (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + if (!(NetPlayers[slot].flags & NPF_CONNECTED)) + { + mprintf ((1,"ERROR! We got a request buildings packet from a disconnected player!\n")); + Int3(); // Get Jason + return; + } + + NetPlayers[slot].sequence=NETSEQ_REQUEST_BUILDINGS; +} + + +// Tell a client about the objects +// Server only +void MultiDoRequestObjects (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + if (!(NetPlayers[slot].flags & NPF_CONNECTED)) + { + mprintf ((1,"ERROR! We got a request object packet from a disconnected player!\n")); + Int3(); // Get Jason + return; + } + + NetPlayers[slot].sequence=NETSEQ_REQUEST_OBJECTS; +} + +// Tell a client about the objects +// Server only +void MultiDoRequestWorldStates (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + ubyte slot=MultiGetByte (data,&count); + + ubyte rxdigest[16]; + for(int i=0;i<16;i++) + { + rxdigest[i] = MultiGetByte (data,&count); + } + + char dbgmsg[1024]; + char expectedstr[50] = ""; + char gotstr[50] = ""; + for(int j=0;j<16;j++) + { + char bstr[10]; + sprintf(bstr,"%.2x",rxdigest[j] & 0xff); + strcat(gotstr,bstr); + sprintf(bstr,"%.2x",NetPlayers[slot].digest[j] & 0xff); + strcat(expectedstr,bstr); + } + sprintf(dbgmsg,"Expected %s for the digest, got %s for player %d\n",expectedstr,gotstr,slot); + + for(int i=0;i<16;i++) + { + // We got the digest from the player previously. Now see if it matches. + if(rxdigest[i]!=NetPlayers[slot].digest[i]) + { + // send 'bad checksum message.' + MultiSendBadChecksum(slot); + + // Bad checksum, kick the player out. + NetPlayers[slot].sequence=NETSEQ_PREGAME; + NetPlayers[slot].flags |= NPF_CONNECTED; + return; + } + } + if (!(NetPlayers[slot].flags & NPF_CONNECTED)) + { + mprintf ((1,"ERROR! We got a request world states packet from a disconnected player!\n")); + Int3(); // Get Jason + return; + } + + NetPlayers[slot].sequence=NETSEQ_REQUEST_WORLD; +} + +// The server is telling me about a player in the game +// Client only +void MultiDoPlayer (ubyte *data) +{ + int count=0; + char ship_name[PAGENAME_LEN]; + + mprintf ((0,"Got player packet!\n")); + + // Skip header stuff + SKIP_HEADER (data,&count); + + // Get slotnumber + ubyte slot=MultiGetByte (data,&count); + + // Reset some local stuff for this player + if (slot!=Player_num) + { + InitPlayerNewShip (slot,INVRESET_ALL); + InitPlayerNewGame (slot); + PlayerStopSounds(slot); + ResetPlayerObject (slot); + InitPlayerNewLevel (slot); + } + + // Get name + ubyte len=MultiGetByte (data,&count); + memcpy (Players[slot].callsign,&data[count],len); + count+=len; + + // Get ship name + len=MultiGetByte (data,&count); + memcpy (ship_name,&data[count],len); + count+=len; + + int ship_index=FindShipName (ship_name); + if (ship_index<0) + { + ship_index=0; // Should we bail out here? + } + + PlayerChangeShip (slot,ship_index); + + // Get various flags + if (slot!=Player_num) + { + Players[slot].flags=MultiGetInt (data,&count); + Players[slot].weapon_flags=MultiGetInt (data,&count); + } + else + { + // Skip my own data + MultiGetInt (data,&count); + MultiGetInt (data,&count); + } + + NetPlayers[slot].flags=MultiGetInt (data,&count); + float shields=MultiGetFloat (data,&count); + + // Get object number, just to verify + int objnum=MultiGetShort (data,&count); + + // Make sure we have the right object number + if (objnum!=Players[slot].objnum) + { + BailOnMultiplayer (NULL); + return; + } + + // Get team + ubyte temp_team = MultiGetByte (data,&count); + Players[slot].team = (temp_team==255)?-1:temp_team; + + // Get team start + if (slot==Player_num) + { + Players[slot].start_index=MultiGetShort (data,&count); + PlayerMoveToStartPos (slot,Players[slot].start_index); + } + + // Get observer mode + ubyte observing=MultiGetByte (data,&count); + + //Put in address + memcpy(&NetPlayers[slot].addr,data+count,sizeof(network_address)); +#ifdef MACINTOSH //DAJ if the addr is comming from a PC swap it + if(NetPlayers[slot].addr.connection_type > NP_DIRECTPLAY) { + NetPlayers[slot].addr.port = SWAPSHORT(NetPlayers[slot].addr.port); + NetPlayers[slot].addr.connection_type = (network_protocol)SWAPINT(NetPlayers[slot].addr.connection_type); + } +#endif + char dbg_output[50]; + nw_GetNumbersFromHostAddress(&NetPlayers[slot].addr,dbg_output); + mprintf((0,"Got player address of: %s\n", dbg_output)); + + count += sizeof(network_address); + + // PPS + NetPlayers[slot].pps=MultiGetByte (data,&count); + + //pilot picture id + NetPlayers[slot].pilot_pic_id = MultiGetUshort(data,&count); + + // Get ranking + Players[slot].rank=MultiGetFloat (data,&count); + + // Get tracker id + if(Game_is_master_tracker_game) + { + int len=MultiGetByte (data,&count); + memcpy (Players[slot].tracker_id,&data[count],len); + count+=len; + } + + object *obj=&Objects[objnum]; + obj->id=slot; + + MultiMakePlayerReal (slot); + + if (Player_num!=slot) + { + NetPlayers[slot].sequence=NETSEQ_PLAYING; + NetPlayers[slot].flags |= NPF_CONNECTED; + } + + if (slot!=Player_num) + { + obj->shields=shields; + if (Players[slot].flags & PLAYER_FLAGS_DEAD) + { + mprintf ((0,"Incoming %d player is dead!\n",slot)); + MultiMakePlayerGhost (slot); + } + else if (Players[slot].flags & PLAYER_FLAGS_DYING) + { + mprintf ((0,"Incoming %d player is dying!\n",slot)); + int save_flags=Players[slot].flags; + Players[slot].flags &=~(PLAYER_FLAGS_DEAD|PLAYER_FLAGS_DYING); + InitiatePlayerDeath (&Objects[Players[slot].objnum],false,0); + Players[slot].flags=save_flags; + } + } + + obj->flags|=OF_SERVER_OBJECT; + + if (observing) + { + obj->render_type=RT_NONE; + obj->type=OBJ_OBSERVER; + } + + if (slot==0) + { + memcpy(&NetPlayers[slot].addr,&Netgame.server_address,sizeof(network_address)); + + } + +} + +// Tells all our clients about a new player entering the game +void MultiSendNewPlayer (int slot) +{ + +} + +void MultiDoPlayerEnteredGame (ubyte *data) +{ + int count=0; + char ship_name[PAGENAME_LEN]; + int length; + + mprintf ((0,"Got player entered game packet!\n")); + + // Skip header stuff + SKIP_HEADER (data,&count); + + // Get slot number for this player + ubyte slot=MultiGetByte (data,&count); + + if(slot > MAX_PLAYERS) + return; + + ScoreAPIPlayerJoin(slot); + // get name + ubyte len=MultiGetByte (data,&count); + + if (len > CALLSIGN_LEN) + length = CALLSIGN_LEN; + else + length = len; + + memcpy (Players[slot].callsign,&data[count],length); + count+=len; + + // get ship name + len=MultiGetByte (data,&count); + memcpy (ship_name,&data[count],len); + count+=len; + + int ship_index=FindShipName (ship_name); + if (ship_index<0) + ship_index=0; + + // Get various flags + Players[slot].flags=MultiGetInt (data,&count); + NetPlayers[slot].flags=MultiGetInt (data,&count); + + // Verify object number + int objnum=MultiGetShort (data,&count); + + MULTI_ASSERT (objnum==Players[slot].objnum,NULL); + MULTI_ASSERT (Objects[objnum].id==slot,NULL); + + //Get the player's network address + memcpy(&NetPlayers[slot].addr,data+count,sizeof(network_address)); +#ifdef MACINTOSH //DAJ if the addr is comming from a PC swap it + if(NetPlayers[slot].addr.connection_type > NP_DIRECTPLAY) { + NetPlayers[slot].addr.port = SWAPSHORT(NetPlayers[slot].addr.port); + NetPlayers[slot].addr.connection_type = (network_protocol)SWAPINT(NetPlayers[slot].addr.connection_type); + } +#endif + count += sizeof(network_address); + + // Get PPS + NetPlayers[slot].pps=MultiGetByte (data,&count); + + //pilot picture id + NetPlayers[slot].pilot_pic_id = MultiGetUshort(data,&count); + + Players[slot].start_index=MultiGetShort (data,&count); + + // Get ranking + Players[slot].rank=MultiGetFloat (data,&count); + + // Get tracker id + if(Game_is_master_tracker_game) + { + int len=MultiGetByte (data,&count); + memcpy (Players[slot].tracker_id,&data[count],len); + count+=len; + + NetPlayers[slot].flags&=~NPF_WROTE_RANK; + } + + if (Player_num!=slot && NetPlayers[Player_num].sequence==NETSEQ_PLAYING) + { + NetPlayers[slot].sequence=NETSEQ_PLAYING; + NetPlayers[slot].flags |= NPF_CONNECTED; + } + + InitPlayerNewShip (slot,INVRESET_ALL); + InitPlayerNewGame (slot); + PlayerStopSounds(slot); + ResetPlayerObject (slot); + InitPlayerNewLevel (slot); + PlayerChangeShip (slot,ship_index); + PlayerMoveToStartPos (slot,Players[slot].start_index); + MultiMakePlayerReal (slot); + Objects[objnum].flags|=OF_SERVER_OBJECT; + + MultiAnnounceEffect (&Objects[objnum],Objects[objnum].size*5,1.0); + NetPlayers[slot].total_bytes_sent = 0; + NetPlayers[slot].total_bytes_rcvd = 0; + NetPlayers[slot].sequence=NETSEQ_PLAYING; +} + +// Sends a new player to existing players +// Sends to "slot" and describes player "which" +// Server only +void MultiSendPlayerEnteredGame (int which) +{ + MULTI_ASSERT (Netgame.local_role==LR_SERVER,NULL); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + size_offset=START_DATA(MP_PLAYER_ENTERED_GAME,data,&count,1); + + MultiAddByte (which,data,&count); + + // Add name + int len=strlen(Players[which].callsign)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Players[which].callsign,len); + count+=len; + + // Add ship name + len=strlen(Ships[Players[which].ship_index].name)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Ships[Players[which].ship_index].name,len); + count+=len; + + // Add flags + MultiAddInt (Players[which].flags,data,&count); + MultiAddInt (NetPlayers[which].flags,data,&count); + + // Just for safety sake send the object number + MultiAddShort (Players[which].objnum,data,&count); + + //Add the player's network address. + memcpy(data+count,&NetPlayers[which].addr,sizeof(network_address)); + count += sizeof(network_address); + + // Add pps + MultiAddByte (NetPlayers[which].pps,data,&count); + + //pilot picture id + MultiAddUshort (NetPlayers[which].pilot_pic_id,data,&count); + + + // Add start position + MultiAddShort (Players[which].start_index,data,&count); + + // Send ranking + MultiAddFloat (Players[which].rank,data,&count); + + // Send tracker id + if(Game_is_master_tracker_game) + { + int len = strlen(Players[which].tracker_id)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Players[which].tracker_id,len); + count+=len; + } + + + END_DATA (count,data,size_offset); + + // Send it out + + MultiSendReliablyToAllExcept (which,data,count,NETSEQ_REQUEST_BUILDINGS); + MultiDoPlayerEnteredGame (data); +} + + +// Client is saying that he's entering the game +// Server only +void MultiDoEnteringGame (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + + if (!(NetPlayers[slot].flags & NPF_CONNECTED)) + { + Int3(); // Get Jason + return; + } + + mprintf ((0,"Client %d (%s) entering game.\n",slot,Players[slot].callsign)); + + MultiSendPlayerEnteredGame (slot); + + //send the player the audio taunt delay time + MultiSetAudioTauntTime(taunt_DelayTime(),slot); + + Level_goals.MultiSendChangedGoals(slot); + + //add his guidebot (if it is a guidebot game) + //NOTE: It is possible that DMFC may InitPlayerNewShip after this + //so it is placed as a precaution in their also + if(Netgame.flags&NF_ALLOWGUIDEBOT) + { + if((!Players[slot].inventory.CheckItem(OBJ_ROBOT, ROBOT_GUIDEBOT)) && (ObjGet(Buddy_handle[slot])->type != OBJ_ROBOT)) + Players[slot].inventory.Add(OBJ_ROBOT, ROBOT_GUIDEBOT); + } + + DLLInfo.me_handle=Objects[Players[slot].objnum].handle; + DLLInfo.it_handle=Objects[Players[slot].objnum].handle; + CallGameDLL (EVT_GAMEPLAYERENTERSGAME,&DLLInfo); + + // Clear vis info + for (int i=0;iid==slot,NULL); // Get Jason + + obj->type=OBJ_GHOST; + obj->movement_type=MT_NONE; + obj->render_type=RT_NONE; + obj->mtype.phys_info.flags|=PF_NO_COLLIDE; + SetObjectControlType(obj, CT_NONE); + + if(Demo_flags==DF_RECORDING) + { + DemoWritePlayerTypeChange(slot); + } + + // Stop sounds for this player + PlayerStopSounds (slot); + +} + +// Makes the passed in player real (non-ghost) +void MultiMakePlayerReal (int slot) +{ + object *obj=&Objects[Players[slot].objnum]; + MULTI_ASSERT (obj->id==slot,NULL); // Get Jason + + obj->type=OBJ_PLAYER; + + if(Demo_flags==DF_RECORDING) + { + DemoWritePlayerTypeChange(slot); + } + + obj->render_type=RT_POLYOBJ; + obj->mtype.phys_info.flags&=~PF_NO_COLLIDE; + + if (slot==Player_num) + { + SetObjectControlType(obj, CT_FLYING); + obj->movement_type=MT_PHYSICS; + Viewer_object=Player_object=obj; + } + else + { + obj->movement_type=MT_PHYSICS; + obj->mtype.phys_info.flags=PF_FIXED_VELOCITY; + SetObjectControlType(obj, CT_NONE); + } + + +} + +// Server is telling us that its done sending players +void MultiDoDonePlayers (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte num=MultiGetByte (data,&count); + if (MultiCountPlayers() !=num) + { + mprintf ((1,"ERROR! We don't have the correct number of players!")); + Int3(); // Get Jason + return; + } + + NetPlayers[Player_num].sequence=NETSEQ_REQUEST_BUILDINGS; +} + + +// Server is telling us that its done sending buildings +void MultiDoDoneBuildings (ubyte *data) +{ + MULTI_ASSERT (Netgame.local_role==LR_CLIENT,NULL); + + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + NetPlayers[Player_num].sequence=NETSEQ_REQUEST_OBJECTS; +} + + +// Server is telling us that its done sending objects +void MultiDoDoneObjects (ubyte *data) +{ + MULTI_ASSERT (Netgame.local_role==LR_CLIENT,NULL); + + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + // Get the salt. + MD5 *playermd5 = Level_md5->Clone(); + // Get the salt value from the server. + int salt = MultiGetInt (data,&count); + // process the salt through the md5 + playermd5->MD5Update(salt); + // process the ships through the md5 + for (int i=0;iMD5Final(NetPlayers[Player_num].digest); + MD5::Destroy(playermd5); + + NetPlayers[Player_num].sequence=NETSEQ_REQUEST_WORLD; + + // Clear out all the lightmapped objects that weren't sent + for (int i=0;imulti_matrix[0]=(src->rvec.x*32767.0); + dest->multi_matrix[1]=(src->rvec.y*32767.0); + dest->multi_matrix[2]=(src->rvec.z*32767.0); + + dest->multi_matrix[3]=(src->uvec.x*32767.0); + dest->multi_matrix[4]=(src->uvec.y*32767.0); + dest->multi_matrix[5]=(src->uvec.z*32767.0); + + dest->multi_matrix[6]=(src->fvec.x*32767.0); + dest->multi_matrix[7]=(src->fvec.y*32767.0); + dest->multi_matrix[8]=(src->fvec.z*32767.0); +} + +// Extracts a matrix from an abbreviated matrix +void MultiExtractMatrix (matrix *dest,multi_orientation *src) +{ + dest->rvec.x=(float)src->multi_matrix[0]/32767.0; + dest->rvec.y=(float)src->multi_matrix[1]/32767.0; + dest->rvec.z=(float)src->multi_matrix[2]/32767.0; + + dest->uvec.x=(float)src->multi_matrix[3]/32767.0; + dest->uvec.y=(float)src->multi_matrix[4]/32767.0; + dest->uvec.z=(float)src->multi_matrix[5]/32767.0; + + dest->fvec.x=(float)src->multi_matrix[6]/32767.0; + dest->fvec.y=(float)src->multi_matrix[7]/32767.0; + dest->fvec.z=(float)src->multi_matrix[8]/32767.0; +} + +void MultiDoPlayerPos (ubyte *data) +{ + int use_smoothing=(Netgame.flags & NF_USE_SMOOTHING); + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + + + // Make sure its not out of order + float packet_time=MultiGetFloat (data,&count); + if (packet_timepos); + + // Get velocity + vel.x=((float)MultiGetShort (data,&count))/128.0; + vel.y=((float)MultiGetShort (data,&count))/128.0; + vel.z=((float)MultiGetShort (data,&count))/128.0; + + //mprintf ((0,"INCOMING x=%f y=%f z=%f dist=%f\n",vel.x,vel.y,vel.z,dist)); + + // Get rotational velocity + if (Netgame.flags & NF_SENDROTVEL) + { + rotvel.x=MultiGetShort (data,&count)*4; + rotvel.y=MultiGetShort (data,&count)*4; + rotvel.z=MultiGetShort (data,&count)*4; + + turnroll=MultiGetShort (data,&count); + + obj->mtype.phys_info.rotvel=rotvel; + obj->mtype.phys_info.turnroll=turnroll; + } + +// obj->mtype.phys_info.velocity=vel; + +// Get weapon states + ubyte windex=MultiGetByte (data,&count); + Players[slot].weapon[PW_PRIMARY].index=windex & 0x0F; + Players[slot].weapon[PW_SECONDARY].index=(windex >> 4) +10; + + // Get energy + Players[slot].energy=MultiGetUbyte (data,&count); + // Get flags + ubyte flags=MultiGetByte (data,&count); + + // Do special stuff for non-visible objects + bool visible=true; + if (Objects[Players[slot].objnum].render_type==RT_NONE) + visible=false; + + if (Netgame.local_role==LR_SERVER) + { + // Stop cheating with on/off weapons (can't fire things you shouldn't have!) + player *playp=&Players[slot]; + if (!(playp->weapon_flags & (1<Highest_room_index || roomnum<0) + return; + + if (Rooms[roomnum].used==0) + return; + + if (Rooms[roomnum].flags & RF_EXTERNAL) + return; + } + + if (Netgame.local_role==LR_CLIENT && use_smoothing && !(flags & MPF_FIRED)) + { + // Check to see if we need to correct this ship due to error + if (vm_VectorDistance(&pos,&obj->pos)<8) + { + int pps_player_num; + if (Netgame.local_role==LR_SERVER) + { + pps_player_num=slot; + } + else + { + pps_player_num=Player_num; + } + + vector dest_pos=pos+(vel*(1.0/NetPlayers[pps_player_num].pps)); + vel=(dest_pos-obj->pos)/(1.0/NetPlayers[pps_player_num].pps); + + pos=obj->pos; + } + } + + obj->mtype.phys_info.velocity=vel; + + if( (Netgame.local_role==LR_SERVER) && (Player_pos_fix[slot].active) && (roomnum!=Player_pos_fix[slot].room) ) { + //ignoring this position update....what should we do???? + Player_pos_fix[slot].ignored_pos++; + } + else { + if(Player_pos_fix[slot].active) { + Player_pos_fix[slot].active = false; + //print some info to the server console... + //PrintDedicatedMessage("Discarded %d position updates from %s\n",Player_pos_fix[slot].ignored_pos,Players[slot].callsign); + } + ObjSetPos (obj,&pos,roomnum,&orient,true); + } + + if (Netgame.local_role==LR_SERVER) + { + Players[slot].oldroom=roomnum; + } +} + +void MultiDoRobotPos (ubyte *data) +{ + int count=0; + //@@multi_orientation multi_mat; + // Skip header stuff + SKIP_HEADER (data,&count); + + ushort server_objnum=MultiGetUshort (data,&count); + ushort objectnum = Server_object_list[server_objnum]; + if(objectnum==65535 || !(Objects[objectnum].flags & OF_SERVER_OBJECT)) + { + mprintf((0,"Bad robotposition object number!\n")); + return; + } + object *obj=&Objects[objectnum]; + + vector pos; + matrix orient; + ushort short_roomnum; + int roomnum; + + // Get position + MultiExtractPositionData (&pos,data,&count); + + // Get orientation + ushort p=MultiGetShort (data,&count); + ushort h=MultiGetShort (data,&count); + ushort b=MultiGetShort (data,&count); + + vm_AnglesToMatrix (&orient,p,h,b); + + + // Get room and terrain flag + short_roomnum=MultiGetUshort (data,&count); + ubyte terrain=MultiGetByte (data,&count); + + roomnum = short_roomnum; + if (terrain) + roomnum = MAKE_ROOMNUM(roomnum); + vector vel={0,0,0}; + + // Get velocity + vel.x=((float)MultiGetShort (data,&count))/128.0; + vel.y=((float)MultiGetShort (data,&count))/128.0; + vel.z=((float)MultiGetShort (data,&count))/128.0; + obj->mtype.phys_info.velocity=vel; + + obj->mtype.phys_info.flags &=~PF_NO_COLLIDE; + obj->render_type=RT_POLYOBJ; + + if(!(obj->flags & (OF_DEAD)) && obj->type!=OBJ_NONE) + { + ObjSetPos (obj,&pos,roomnum,&orient,false); + + + } + +} + + +// Stuffs a players firing information into a packet +int MultiStuffPlayerFire (int slot,ubyte *data) +{ + int size_offset; + int count=0; + + size_offset=START_DATA (MP_PLAYER_FIRE,data,&count); + MultiAddByte (slot,data,&count); + MultiAddByte (Player_fire_packet[slot].wb_index,data,&count); + MultiAddByte (Player_fire_packet[slot].fire_mask,data,&count); + MultiAddByte (Player_fire_packet[slot].damage_scalar,data,&count); + MultiAddByte (Player_fire_packet[slot].reliable,data,&count); + + END_DATA (count,data,size_offset); + + return count; +} + +// Returns true if there is enough ammo to allow this player to fire, else false +bool MultiEnoughAmmoToFire (int slot,int wb_index) +{ + ship *ship = &Ships[Players[slot].ship_index]; + otype_wb_info *wb = &ship->static_wb[wb_index]; + + if (wb->ammo_usage>0 && Players[slot].weapon_ammo[wb_index]<=0) + return false; + + return true; +} + +void MultiSubtractAmmoToFire (int slot,int wb_index) +{ + ship *ship = &Ships[Players[slot].ship_index]; + otype_wb_info *wb = &ship->static_wb[wb_index]; + Players[slot].weapon_ammo[wb_index]-=wb->ammo_usage; + if (Players[slot].weapon_ammo[wb_index]<0) + Players[slot].weapon_ammo[wb_index]=0; +} + + +#define MPFF_QUADED 128 +// Sends a fire packet from a player +void MultiSendFirePlayerWB (int playernum,ubyte wb_index,ubyte fire_mask,ubyte reliable,float scalar) +{ + // Send quaded info if needed + ubyte index_to_send=wb_index; + + if (Objects[Players[playernum].objnum].dynamic_wb[wb_index].flags & DWBF_QUAD) + index_to_send|=MPFF_QUADED; + + ubyte damage_scalar=(scalar*64.0); + + Player_fire_packet[playernum].fire_mask=fire_mask; + Player_fire_packet[playernum].damage_scalar=damage_scalar; + Player_fire_packet[playernum].wb_index=index_to_send; + Player_fire_packet[playernum].reliable=reliable; + + // How should we send this information? + // secondary fire in a non-peer to peer game should get sent reliably + // except for a flage + #ifdef RELIABLE_SECONDARIES + if( (!(Netgame.flags&NF_PEER_PEER)) && (wb_index>=SECONDARY_INDEX) && (wb_index!=FLARE_INDEX)) + { + Player_fire_packet[playernum].fired_on_this_frame=PFP_FIRED_RELIABLE; + } + else + #endif + { + // send like normal fire + Player_fire_packet[playernum].fired_on_this_frame=PFP_FIRED; + } +} + +// Does player firing +void MultiDoFirePlayerWB (ubyte *data) +{ + int count=0,ok_to_fire=1; + + // Skip header stuff + SKIP_HEADER (data,&count); + + int pnum=MultiGetByte (data,&count); + Player_fire_packet[pnum].wb_index=MultiGetByte (data,&count); + Player_fire_packet[pnum].fire_mask=MultiGetByte (data,&count); + Player_fire_packet[pnum].damage_scalar=MultiGetByte (data,&count); + Player_fire_packet[pnum].reliable=MultiGetByte (data,&count); + + // Strip out quad data + int wb_index=Player_fire_packet[pnum].wb_index; + int quaded=wb_index & MPFF_QUADED; + wb_index&=~MPFF_QUADED; + + // Fire! + float scalar=(float)Player_fire_packet[pnum].damage_scalar/64.0; + int ship_index=Players[pnum].ship_index; + Objects[Players[pnum].objnum].dynamic_wb[wb_index].cur_firing_mask=Player_fire_packet[pnum].fire_mask; + + if (quaded) + Objects[Players[pnum].objnum].dynamic_wb[wb_index].flags |=DWBF_QUAD; + else + Objects[Players[pnum].objnum].dynamic_wb[wb_index].flags &=~DWBF_QUAD; + + if (Netgame.local_role==LR_SERVER) + { + // Check to see if this player is firing a weapon he doesn't have + player *playp=&Players[pnum]; + if (!(playp->weapon_flags & (1<weapon_flags,wb_index)); + ok_to_fire=0; + } + + if (!MultiEnoughAmmoToFire(pnum,wb_index)) + ok_to_fire=0; + } + + if (ok_to_fire) + { + WBFireBattery(&Objects[Players[pnum].objnum], &Ships[ship_index].static_wb[wb_index], 0, wb_index,scalar); + // Take off ammo + MultiSubtractAmmoToFire (pnum,wb_index); + + } + + + // Play cutoff sound if there is one + int cutoff_sound = Ships[ship_index].firing_release_sound[wb_index]; + if (cutoff_sound != -1) + Sound_system.Play3dSound(cutoff_sound,&Objects[Players[pnum].objnum]); + + if ( (Netgame.local_role==LR_SERVER && ok_to_fire) && ( (!(Netgame.flags & NF_PEER_PEER)) || ((Netgame.flags & NF_PEER_PEER) && Player_fire_packet[pnum].reliable) ) ) + { + #ifdef RELIABLE_SECONDARIES + // How should we send this information? + // secondary fire in a non-peer to peer game should get sent reliably + if( (!(Netgame.flags&NF_PEER_PEER)) && (wb_index>=SECONDARY_INDEX) && (wb_index!=FLARE_INDEX) ) + { + Player_fire_packet[pnum].fired_on_this_frame=PFP_FIRED_RELIABLE; + } + else + #endif + { + // send like normal fire + Player_fire_packet[pnum].fired_on_this_frame=PFP_FIRED; + } + } +} + +// Tell everyone I'm quitting +void MultiSendLeaveGame () +{ + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + mprintf ((0,"Sending leave game!\n")); + + if (Netgame.local_role==LR_SERVER) + { + size_offset=START_DATA(MP_SERVER_QUIT,data,&count,1); + + ShowProgressScreen (TXT_MLTSHUTDOWN); + + } + else + { + size_offset=START_DATA(MP_LEAVE_GAME,data,&count); + MultiAddByte (Player_num,data,&count); + } + + END_DATA(count,data,size_offset); + + if (Netgame.local_role==LR_SERVER) + { + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_REQUEST_BUILDINGS); + } + else + { + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); + } + + +} + +// Called whenever I want to leave the game +void MultiLeaveGame () +{ + mprintf ((0,"I'm leaving the netgame!\n")); + + CallGameDLL (EVT_GAME_DISCONNECTED,&DLLInfo); + + if ((Players[Player_num].flags & PLAYER_FLAGS_DYING) || (Players[Player_num].flags & PLAYER_FLAGS_DEAD)) + EndPlayerDeath (Player_num); + + //Cancel all incoming file transfers + + Multi_bail_ui_menu=true; + MultiSendLeaveGame (); + + CallMultiDLL(MT_EVT_GAME_OVER); + MultiCloseGame (); + + SetFunctionMode(MENU_MODE); + if(Netgame.local_role==LR_SERVER) + { +// gspy_EndGame(); + } + + + ScoreAPIGameOver(); + NetPlayers[Player_num].flags &=~NPF_CONNECTED; + +} + +// Releases a missile that belongs to a player +void MultiDoReleaseTimeoutMissile (ubyte *data) +{ + int count=0; + + SKIP_HEADER (data,&count); + + int pnum=MultiGetByte (data,&count); + + if (Players[pnum].user_timeout_obj!=NULL) + { + ReleaseUserTimeoutMissile (pnum); + } + + if (Netgame.local_role==LR_SERVER) + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYING); +} + +// Tell everyone I'm timingout my timeout weapon +void MultiSendReleaseTimeoutMissile () +{ + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + ASSERT (Players[Player_num].user_timeout_obj!=NULL); + + mprintf ((0,"Sending timeout weapon packet!\n")); + + size_offset=START_DATA(MP_TIMEOUT_WEAPON,data,&count); + MultiAddByte (Player_num,data,&count); + + END_DATA(count,data,size_offset); + + if (Netgame.local_role==LR_SERVER) + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_REQUEST_BUILDINGS); + else + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); + +} + +// Tells all the clients who are trying to join to piss off until the next level +void MultiSendConnectBail() +{ + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + bool wait_to_send=false; + int i; + + size_offset=START_DATA(MP_CONNECT_BAIL,data,&count); + END_DATA(count,data,size_offset); + + // Tell them to stop trying to join + for (i=1;iCurrent_mission.num_levels) + Multi_next_level=-1; + + mprintf ((0,"Doing level ended! Next level=%d\n",Multi_next_level)); + + if (success) + SetGameState(GAMESTATE_LVLEND); + else + SetGameState(GAMESTATE_LVLFAILED); + + + NetPlayers[Player_num].sequence=NETSEQ_LEVEL_END; + + Multi_bail_ui_menu=true; + + CallGameDLL (EVT_CLIENT_GAMELEVELEND,&DLLInfo); + CallMultiDLL(MT_EVT_GAME_OVER); + ScoreAPIGameOver(); +} + + +// Tells all the clients to end the level +void MultiSendLevelEnded (int success,int next_level) +{ + MULTI_ASSERT (Netgame.local_role==LR_SERVER,NULL); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + mprintf ((0,"Sending level ended!\n")); + + size_offset=START_DATA (MP_LEVEL_ENDED,data,&count,1); + MultiAddByte (success,data,&count); + MultiAddByte (next_level,data,&count); + END_DATA (count,data,size_offset); + + // First send out to all the people who are playing + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,true); + + // Now send a connect bail on all the ones who are connecting + MultiSendConnectBail(); + + NetPlayers[Player_num].sequence=NETSEQ_LEVEL_END; + + if (success) + SetGameState(GAMESTATE_LVLEND); + else + SetGameState(GAMESTATE_LVLFAILED); + + if (Netgame.local_role==LR_SERVER) + { + for (int i=0;iname); + cfclose(NetPlayers[slot].file_xfer_cfile); + NetPlayers[slot].file_xfer_cfile = NULL; + if(NetPlayers[slot].file_xfer_flags == NETFILE_RECEIVING) + ddio_DeleteFile(delfile); + } + NetPlayers[slot].file_xfer_flags = NETFILE_NONE; + NetPlayers[slot].custom_file_seq++; + NetPlayers[slot].custom_file_seq = NETFILE_ID_NOFILE; + } + + // If this is the server, do any cleanup on this slot + if (Netgame.local_role==LR_SERVER) + { + MultiClearGuidebot(slot); + MultiClearPlayerMarkers(slot); + DLLInfo.me_handle=DLLInfo.it_handle=Objects[Players[slot].objnum].handle; + CallGameDLL(EVT_GAMEPLAYERDISCONNECT,&DLLInfo); + + if (NetPlayers[slot].sequence==NETSEQ_PLAYING) + PlayerSpewInventory (&Objects[Players[slot].objnum],true,true); + MultiSendReliablyToAllExcept (slot,data,count,NETSEQ_REQUEST_BUILDINGS); + if(NetPlayers[slot].file_xfer_flags != NETFILE_NONE) + { + MultiCancelFile(slot,NetPlayers[slot].custom_file_seq,NetPlayers[slot].file_xfer_who); + } + nw_CloseSocket (&NetPlayers[slot].reliable_socket); + if(Game_is_master_tracker_game) + { + NetPlayers[slot].flags |= NPF_MT_WRITING_PILOT; + } + } + ScoreAPIPlayerLeft(slot); +} + +void MultiDoServerQuit (ubyte *data) +{ + ShowProgressScreen (TXT_MLTSERVERQUIT); + //Abort all file transfers + //ddio_DeleteFile(NetPlayers[filewho].file_xfer_cfile->name); + for (int i=0;iname); + cfclose(NetPlayers[i].file_xfer_cfile); + NetPlayers[i].file_xfer_cfile = NULL; + if(NetPlayers[i].file_xfer_flags == NETFILE_RECEIVING) + ddio_DeleteFile(delfile); + } + NetPlayers[i].file_xfer_flags = NETFILE_NONE; + NetPlayers[i].custom_file_seq++; + NetPlayers[i].custom_file_seq = NETFILE_ID_NOFILE; + } + } + mprintf ((0,"Server quitting!\n")); + MultiLeaveGame(); + Sleep(2000); +} + +void MultiDoDisconnect (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + + if (NetPlayers[slot].flags & NPF_CONNECTED) + { + if(NetPlayers[slot].file_xfer_flags != NETFILE_NONE) + { + if(NetPlayers[slot].file_xfer_cfile) + { + char delfile[_MAX_PATH*2]; + strcpy(delfile,NetPlayers[slot].file_xfer_cfile->name); + cfclose(NetPlayers[slot].file_xfer_cfile); + NetPlayers[slot].file_xfer_cfile = NULL; + if(NetPlayers[slot].file_xfer_flags == NETFILE_RECEIVING) + ddio_DeleteFile(delfile); + } + NetPlayers[slot].file_xfer_flags = NETFILE_NONE; + NetPlayers[slot].custom_file_seq++; + NetPlayers[slot].custom_file_seq = NETFILE_ID_NOFILE; + } + if (NetPlayers[slot].sequence==NETSEQ_PLAYING) + { + AddHUDMessage (TXT_MLTDISCONNECT,Players[slot].callsign); + MultiMakePlayerGhost (slot); + } + NetPlayers[slot].flags &=~NPF_CONNECTED; + NetPlayers[slot].secret_net_id = 0; + } + else + mprintf ((0,"Got a disconnect from a non-connected player!\n")); + + ScoreAPIPlayerLeft(slot); +} + + +void MultiDoServerRejectedChecksum(ubyte *data) +{ + int count=0; + + MULTI_ASSERT (Netgame.local_role==LR_CLIENT,NULL); + + SKIP_HEADER (data,&count); + ShowProgressScreen (TXT_MLTLEVELNOMATCH); + MultiLeaveGame(); + Sleep (2000); +} + +// Lets us know if the server says its ok to join +void MultiDoJoinResponse (ubyte *data) +{ + int count=0; + + MULTI_ASSERT (Netgame.local_role==LR_CLIENT,NULL); + + SKIP_HEADER (data,&count); + + Ok_to_join=MultiGetByte(data,&count); + +} + +// Returns an index of a unconnected slot +// Returns -1 if there are none +int MultiFindFreeSlot () +{ + if (Netgame.local_role!=LR_SERVER) + { + return -1; + } + + for (int i=0;i0,"FindFreeSlot returned -1!"); + + if (Players[slot].start_roomnum!=-1) + { + mprintf ((0,"Sending OK to join!\n")); + MultiAddByte (JOIN_ANSWER_OK,outdata,&count); + } + else + { + MultiAddByte (JOIN_ANSWER_NO_ROOM,outdata,&count); + } + } + else + { + MultiAddByte (JOIN_ANSWER_FULL,outdata,&count); + } + } + } + else + MultiAddByte (JOIN_ANSWER_NOT_SERVER,outdata,&count); + + END_DATA (count,outdata,size); + + nw_Send(from_addr,outdata,count,0); +} + +// Someone is asking about our game +void MultiDoGetGameInfo (ubyte *data,network_address *from_addr) +{ + ubyte outdata[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + network_address my_addr; + float ping_time; + + //mprintf ((0,"Got a request for knowledge about my game!\n")); + + //Get the time and stuff it back in the packet + SKIP_HEADER (data,&count); + ping_time=MultiGetFloat(data,&count); + int version=MultiGetInt (data,&count); + + if (version!=MULTI_VERSION) + return; // Versions don't match, so just return + + + count=0; + + if(Game_is_master_tracker_game) + return; + + if (!Multi_accept_state) + return; + + + if ((Netgame.local_role==LR_SERVER)&&(Game_mode & GM_MULTI)) + { + size_offset=START_DATA(MP_GAME_INFO,outdata,&count); + // Copies my address into the passed argument + nw_GetMyAddress (&my_addr); + memcpy (&outdata[count],&my_addr,sizeof(network_address)); + count+=sizeof(network_address); + + int len=strlen (Netgame.name)+1; + MultiAddByte (len,outdata,&count); + memcpy (&outdata[count],Netgame.name,len); + count+=len; + + len=strlen (Netgame.mission)+1; + + MultiAddByte (len,outdata,&count); + memcpy (&outdata[count],Netgame.mission,len); + count+=len; + + len=strlen (Netgame.mission_name)+1; + + MultiAddByte (len,outdata,&count); + memcpy (&outdata[count],Netgame.mission_name,len); + count+=len; + + len=strlen (Netgame.scriptname)+1; + + MultiAddByte (len,outdata,&count); + memcpy (&outdata[count],Netgame.scriptname,len); + count+=len; + + MultiAddShort(Current_mission.cur_level,outdata,&count); + unsigned short icurrplayers = 0; + for(int i=0;i=MAX_NETWORK_GAMES) + return; + + //mprintf ((0,"Got game info packet!!\n")); + + SKIP_HEADER (data,&count); + + //memcpy (&incoming_addr,&data[count],sizeof(network_address)); + //Get the network_address from the packet header, not the packet data + memcpy (&incoming_addr,from_addr,sizeof(network_address)); + count+=sizeof (network_address); + + + + len=MultiGetByte (data,&count); + + fixed_len = min(NETGAME_NAME_LEN,len); + + memcpy (name,&data[count],fixed_len); + name[fixed_len-1] = 0; + count+=len; + + mission_len=MultiGetByte (data,&count); + + fixed_len = min(MSN_NAMELEN,mission_len); + + memcpy (mission,&data[count],fixed_len); + mission[fixed_len-1] = 0; + count+=mission_len; + + mission_name_len=MultiGetByte (data,&count); + + fixed_len = min(MISSION_NAME_LEN,mission_name_len); + + memcpy (mission_name,&data[count],fixed_len); + mission_name[fixed_len-1] = 0; + count+=mission_name_len; + + script_len=MultiGetByte (data,&count); + + fixed_len = min(NETGAME_SCRIPT_LEN,script_len); + + memcpy (scriptname,&data[count],fixed_len); + scriptname[fixed_len-1] = 0; + count+=script_len; + + level = MultiGetShort(data,&count); + currplayers = MultiGetShort(data,&count); + maxplayers = MultiGetShort(data,&count); + + ping_time = MultiGetFloat(data,&count); + + unsigned int flags = MultiGetInt(data,&count); + bool dedicated = MultiGetByte (data,&count)?true:false; + + int diff = MultiGetByte (data,&count); + + // Now go through and see if this is game we don't already have in our list + for (i=0;iid].lo_render_handle!=-1) + { + obj->flags|=OF_USE_DESTROYED_POLYMODEL; + obj->lighting_render_type=LRT_GOURAUD; + + //make parent object only draw center part + obj->rtype.pobj_info.subobj_flags=1; + } + else + ObjDelete (objnum); + } + } + + Multi_num_buildings_changed=num; +} + +doorway *GetDoorwayFromObject(int door_obj_handle); +// Server is telling us the world state +void MultiDoWorldStates (ubyte *data) +{ + int count=0; + ubyte world_type; + + SKIP_HEADER (data,&count); + + mprintf ((0,"Got a world state packet!\n")); + + while ((world_type=MultiGetByte (data,&count))!=WS_END) + { + switch (world_type) + { + case WS_ROOM_WIND: + { + // Room wind + + short roomnum=MultiGetShort (data,&count); + Rooms[roomnum].wind.x=MultiGetFloat (data,&count); + Rooms[roomnum].wind.y=MultiGetFloat (data,&count); + Rooms[roomnum].wind.z=MultiGetFloat (data,&count); + + mprintf ((0,"Got room wind packet! Room=%d wind=%f %f %f\n",roomnum,Rooms[roomnum].wind.x,Rooms[roomnum].wind.y,Rooms[roomnum].wind.z)); + break; + + } + case WS_ROOM_FOG: + { + // Room wind + short roomnum=MultiGetShort (data,&count); + Rooms[roomnum].fog_depth=MultiGetFloat (data,&count); + Rooms[roomnum].fog_r=((float)MultiGetUbyte (data,&count))/255.0; + Rooms[roomnum].fog_g=((float)MultiGetUbyte (data,&count))/255.0; + Rooms[roomnum].fog_b=((float)MultiGetUbyte (data,&count))/255.0; + ubyte state=MultiGetUbyte (data,&count); + if (state) + Rooms[roomnum].flags|=RF_FOG; + else + Rooms[roomnum].flags&=~RF_FOG; + + break; + } + + case WS_ROOM_LIGHTING: + { + // Room lighting + ubyte state; + short roomnum=MultiGetShort (data,&count); + Rooms[roomnum].pulse_time=MultiGetUbyte (data,&count); + Rooms[roomnum].pulse_offset=MultiGetUbyte (data,&count); + state=MultiGetUbyte (data,&count); + if (state) + Rooms[roomnum].flags|=RF_FLICKER; + else + Rooms[roomnum].flags&=~RF_FLICKER; + + state=MultiGetUbyte (data,&count); + if (state) + Rooms[roomnum].flags|=RF_STROBE; + else + Rooms[roomnum].flags&=~RF_STROBE; + + + break; + } + case WS_ROOM_REFUEL: + { + // Room fueling + short roomnum=MultiGetShort (data,&count); + ubyte state=MultiGetUbyte (data,&count); + if (state) + Rooms[roomnum].flags|=RF_FUELCEN; + else + Rooms[roomnum].flags&=~RF_FUELCEN; + + break; + } + case WS_ROOM_TEXTURE: + { + short roomnum=MultiGetShort (data,&count); + short facenum=MultiGetShort (data,&count); + char str[255]; + MultiGetString (str,data,&count); + ChangeRoomFaceTexture (roomnum,facenum,FindTextureName(IGNORE_TABLE(str))); + break; + } + case WS_ROOM_GLASS: + { + short roomnum=MultiGetShort (data,&count); + short facenum=MultiGetShort (data,&count); + BreakGlassFace (&Rooms[roomnum],facenum); + break; + } + case WS_ROOM_PORTAL_RENDER: + { + short roomnum=MultiGetShort (data,&count); + short portalnum=MultiGetShort (data,&count); + ubyte flags=MultiGetByte (data,&count); + Rooms[roomnum].portals[portalnum].flags=flags; + break; + } + case WS_ROOM_PORTAL_BLOCK: + { + short roomnum=MultiGetShort (data,&count); + short portalnum=MultiGetShort (data,&count); + ubyte flags=MultiGetByte (data,&count); + Rooms[roomnum].portals[portalnum].flags=flags; + break; + } + case WS_ROOM_DAMAGE: + { + // Room wind + short roomnum=MultiGetShort (data,&count); + Rooms[roomnum].damage=MultiGetFloat (data,&count); + Rooms[roomnum].damage_type=MultiGetUbyte (data,&count); + break; + } + case WS_ROOM_GOALSPECFLAG: + { + //goals & special flags + short roomnum=MultiGetShort (data,&count); + int mask = (RF_SPECIAL1|RF_SPECIAL2|RF_SPECIAL3|RF_SPECIAL4|RF_SPECIAL5|RF_SPECIAL6|RF_GOAL1|RF_GOAL2|RF_GOAL3|RF_GOAL4); + int change_mask = MultiGetInt (data,&count); + + Rooms[roomnum].flags &= ~mask; + Rooms[roomnum].flags |= change_mask; + + break; + } + case WS_WAYPOINT: + { + Current_waypoint=MultiGetByte (data,&count); + break; + } + case WS_BUDDYBOTS: + { + int b_index = MultiGetByte(data,&count); + ushort serv_objnum = MultiGetUshort(data,&count); + int local_objnum = Server_object_list[serv_objnum]; + + ASSERT(Objects[local_objnum].type!=OBJ_NONE); + + Buddy_handle[b_index] = Objects[local_objnum].handle; + + //now add the guide to that player's inventory (if we need to) + if((!Players[b_index].inventory.CheckItem(OBJ_ROBOT, ROBOT_GUIDEBOT)) && (ObjGet(Buddy_handle[b_index])->type != OBJ_ROBOT)) + Players[b_index].inventory.Add(OBJ_ROBOT, ROBOT_GUIDEBOT); + break; + } + case WS_BUDDYBOTUPDATE: + { + int b_index = MultiGetByte(data,&count); + ushort serv_objnum = MultiGetUshort(data,&count); + int local_objnum = Server_object_list[serv_objnum]; + + ASSERT(Objects[local_objnum].type!=OBJ_NONE); + + Buddy_handle[b_index] = Objects[local_objnum].handle; + + break; + } + case WS_PLAYERBF: + { + Players_typing = MultiGetUint (data,&count); + break; + } + case WS_MOTD: + { + MultiGetString (Multi_message_of_the_day,data,&count); + AddPersistentHUDMessage(GR_RGB(255,255,255),HUD_MSG_PERSISTENT_CENTER,Game_window_y + (Game_window_h/2) - 20,10,HPF_FADEOUT+HPF_FREESPACE_DRAW,SOUND_GOAL_COMPLETE,Multi_message_of_the_day); + break; + } + case WS_WEATHER: + { + Weather.flags=MultiGetByte (data,&count); + Weather.rain_intensity_scalar=MultiGetFloat (data,&count); + Weather.snow_intensity_scalar=MultiGetFloat (data,&count); + Weather.lightning_interval_time=MultiGetFloat (data,&count); + Weather.lightning_rand_value=MultiGetShort (data,&count); + break; + } + case WS_DOOR: + { + doorway *dp; + int objnum=MultiGetShort(data,&count); + objnum=Server_object_list[objnum]; + ASSERT (Objects[objnum].type==OBJ_DOOR); + dp = GetDoorwayFromObject(Objects[objnum].handle); + + int state=MultiGetByte(data,&count); + ubyte locked=MultiGetByte (data,&count); + if (locked) + dp->flags|=DF_LOCKED; + else + dp->flags&=~DF_LOCKED; + + if (state== DOORWAY_OPENING || state == DOORWAY_WAITING || dp->state == DOORWAY_OPENING_AUTO) + DoorwayActivate (Objects[objnum].handle); + + DoorwaySetPosition (Objects[objnum].handle,MultiGetFloat (data,&count)); + + break; + } + case WS_LIGHT_DISTANCE: + { + int objnum=MultiGetShort(data,&count); + float val=MultiGetFloat (data,&count); + objnum=Server_object_list[objnum]; + + if (objnum==65535) + break; + + light_info *li = ObjGetLightInfo(&Objects[objnum]); + if (li) + li->light_distance=val; + + break; + } + case WS_LIGHT_COLOR: + { + int objnum=MultiGetShort(data,&count); + objnum=Server_object_list[objnum]; + + if (objnum==65535) + break; + + light_info *li = ObjGetLightInfo(&Objects[objnum]); + if (li) + { + li->red_light1=MultiGetFloat (data,&count); + li->green_light1=MultiGetFloat (data,&count); + li->blue_light1=MultiGetFloat (data,&count); + } + else + { + MultiGetFloat (data,&count); + MultiGetFloat (data,&count); + MultiGetFloat (data,&count); + } + + break; + } + case WS_LEVELGOAL: + { + char name[256]; + int goal_index = MultiGetUshort(data,&count); + int priority = MultiGetInt(data,&count); + + ubyte len=MultiGetByte (data,&count); + memcpy (name,&data[count],len); + count+=len; + + int flags = MultiGetInt(data,&count); + int temp = 0xFFFFFFFF; + + mprintf((0,"Recieved Level Goal World State: %d\n",goal_index)); + Level_goals.GoalSetName(goal_index,name); + Level_goals.GoalStatus(goal_index,LO_CLEAR_SPECIFIED,&temp); + Level_goals.GoalStatus(goal_index,LO_SET_SPECIFIED,&flags,false); + Level_goals.GoalPriority (goal_index,LO_SET_SPECIFIED,&priority); + + break; + } + case WS_SPEW: + { + spewinfo spew; + mprintf ((0,"Got a spew packet!\n")); + + ushort spewnum=MultiGetShort (data,&count); + + if (MultiGetByte (data,&count)) + spew.use_gunpoint=true; + else + spew.use_gunpoint=false; + + if (spew.use_gunpoint) + { + int objnum=MultiGetUshort (data,&count); + objnum=Server_object_list[objnum]; + ASSERT(objnum!=65535); + ASSERT (Objects[objnum].flags & OF_SERVER_OBJECT); + + spew.gp.obj_handle=Objects[objnum].handle; + spew.gp.gunpoint=MultiGetByte (data,&count); + } + else + { + //memcpy (&spew.pt.normal,&data[count],sizeof(vector)); + //count+=sizeof(vector); + spew.pt.normal = MultiGetVector(data,&count); + //memcpy (&spew.pt.origin,&data[count],sizeof(vector)); + //count+=sizeof(vector); + spew.pt.origin = MultiGetVector(data,&count); + + spew.pt.room_num=MultiGetInt (data,&count); + } + + spew.random=MultiGetByte (data,&count); + if (MultiGetByte (data,&count)) + spew.real_obj=true; + else + spew.real_obj=false; + + spew.effect_type=MultiGetByte (data,&count); + spew.phys_info=MultiGetByte (data,&count); + spew.drag=MultiGetFloat (data,&count); + spew.mass=MultiGetFloat (data,&count); + + spew.time_int=MultiGetFloat (data,&count); + spew.longevity=MultiGetFloat (data,&count); + spew.lifetime=MultiGetFloat (data,&count); + spew.size=MultiGetFloat (data,&count); + spew.speed=MultiGetFloat (data,&count); + + ushort local_spewnum = SpewCreate(&spew); + ASSERT(local_spewnum != -1); //DAJ -1FIX + local_spewnum&=0xFF; // Adjust for handle + Server_spew_list[spewnum]=local_spewnum; + mprintf ((0,"Got spew of type %d. Server=%d local=%d\n",spew.effect_type,spewnum,local_spewnum)); + + break; + } + case WS_NO_RENDER: + { + int objnum=MultiGetShort(data,&count); + objnum=Server_object_list[objnum]; + + if (objnum==65535) + break; + + Objects[objnum].render_type=RT_NONE; + break; + } + + case WS_OBJECT_PHYS_FLAGS: + { + int objnum=MultiGetShort(data,&count); + int flags=MultiGetInt (data,&count); + objnum=Server_object_list[objnum]; + + if (objnum==65535) + break; + Objects[objnum].mtype.phys_info.flags=flags; + break; + } + + case WS_OBJECT_ATTACH: + { + int parent_objnum=MultiGetUshort (data,&count); + int n_attach=MultiGetByte (data,&count); + + for (int i=0;i=0 && objnum=0 && local_objnumsound_handle != SOUND_NONE_INDEX) + { + Sound_system.StopSoundLooping(Objects[Players[slot].objnum].effect_info->sound_handle); + Objects[Players[slot].objnum].effect_info->sound_handle = SOUND_NONE_INDEX; + } +} + +// The server sends to everyone that the player is dead +void MultiSendPlayerDead (int slot,ubyte fate) +{ + MULTI_ASSERT (Netgame.local_role==LR_SERVER,"Client in SendPlayerDead!"); + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + size_offset=START_DATA(MP_PLAYER_DEAD,data,&count,1); + MultiAddByte (slot,data,&count); + MultiAddByte (fate,data,&count); + END_DATA (count,data,size_offset); + + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYERS); + MultiDoPlayerDead (data); + if(Game_is_master_tracker_game) + { + mprintf ((0,"Adding kill and death to player stats. Killer = %d Killee = %d\n",Objects[Players[slot].killer_objnum].id,slot)); + if(Objects[Players[slot].killer_objnum].id==slot) + { + Players[slot].suicides++; + } + else + { + Players[Objects[Players[slot].killer_objnum].id].kills++; + Players[slot].deaths++; + } + } + if(Objects[Players[slot].killer_objnum].id==slot) + { + Multi_deaths[slot]++; + } + else + { + Multi_kills[Objects[Players[slot].killer_objnum].id]++; + Multi_deaths[slot]++; + } + ScoreAPIPlayerKilled(Objects[Players[slot].killer_objnum].id,slot); + +} + +// A player is coming back from the dead...restore his ship! +void MultiDoRenewPlayer (ubyte *data) +{ + int count=0; + bool add_guidebot = false; + + SKIP_HEADER (data,&count); + ubyte slot=MultiGetByte (data,&count); + int start_slot=MultiGetShort (data,&count); + + if(MultiGetByte(data,&count)) + { + add_guidebot = true; + } + + EndPlayerDeath (slot); + + PlayerMoveToStartPos (slot,start_slot); + MultiAnnounceEffect (&Objects[Players[slot].objnum],Objects[Players[slot].objnum].size*5,1.0); + + // Make him invul for 2 seconds + MakePlayerInvulnerable(slot,2.0); + + //add guidebot if needed + if(add_guidebot) + { + mprintf((0,"MULTI: Adding guidebot to respawned player (%s)\n",Players[slot].callsign)); + if(!Players[slot].inventory.CheckItem(OBJ_ROBOT, ROBOT_GUIDEBOT)) + Players[slot].inventory.Add(OBJ_ROBOT, ROBOT_GUIDEBOT); + } + + //Update player position in demo + if(Demo_flags==DF_RECORDING) + { + DemoWriteChangedObj(&Objects[Players[slot].objnum]); + } +} + +// Tell everyone that a player is coming back from the dead +void MultiSendRenewPlayer (int slot) +{ + MULTI_ASSERT (Netgame.local_role==LR_SERVER,"Client in SendRenewPlayer!"); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + size_offset=START_DATA(MP_RENEW_PLAYER,data,&count,1); + MultiAddByte (slot,data,&count); + + // Get random start slot + int start_slot=PlayerGetRandomStartPosition (slot); + MultiAddShort (start_slot,data,&count); + + Player_pos_fix[slot].active = true; + Player_pos_fix[slot].expire_time = Gametime + PLAYER_POS_HACK_TIME; + Player_pos_fix[slot].room = Players[start_slot].start_roomnum; + Player_pos_fix[slot].ignored_pos = 0; + + //check to see if they need to add a guidebot to the player's inventory + if(Netgame.flags&NF_ALLOWGUIDEBOT) + { + if((!Players[slot].inventory.CheckItem(OBJ_ROBOT, ROBOT_GUIDEBOT)) && (ObjGet(Buddy_handle[slot])->type != OBJ_ROBOT)) + { + MultiAddByte(1,data,&count); + }else + { + MultiAddByte(0,data,&count); + } + }else + { + MultiAddByte (0,data,&count); + } + + END_DATA (count,data,size_offset); + + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYERS,false); + + MultiDoRenewPlayer (data); +} + +// This player says he's done dying +void MultiDoEndPlayerDeath (ubyte *data) +{ + MULTI_ASSERT (Netgame.local_role==LR_SERVER,"Client in DoEndPlayerDeath!"); + + int count=0; + + SKIP_HEADER (data,&count); + ubyte slot=MultiGetByte (data,&count); + + if (Players[slot].flags & PLAYER_FLAGS_DEAD || Players[slot].flags & PLAYER_FLAGS_DYING) + MultiSendRenewPlayer (slot); +} +void GetServerGameTime() +{ + MULTI_ASSERT (Netgame.local_role!=LR_SERVER,"Server in GetServerGameTime!"); + ubyte outdata[MAX_GAME_DATA_SIZE]; + int count=0; + int size; + mprintf((0,"Requesting gametime from server\n")); + Got_new_game_time=0; + size=START_DATA(MP_GET_GAMETIME,outdata,&count); + MultiAddFloat (timer_GetTime(),outdata,&count); + END_DATA (count,outdata,size); + nw_Send(&Netgame.server_address,outdata,count,0); +} +void MultiDoGameTimeReq(ubyte *data,network_address *from_addr) +{ + ubyte outdata[MAX_GAME_DATA_SIZE]; + int count=0; + int incount=0; + int size; + + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + mprintf((0,"Processing request for gametime\n")); + SKIP_HEADER (data,&incount); + float client_time=MultiGetFloat (data,&incount); + + size=START_DATA(MP_HERE_IS_GAMETIME,outdata,&count); + MultiAddFloat (client_time,outdata,&count); + MultiAddFloat (Gametime,outdata,&count); + + END_DATA (count,outdata,size); + + nw_Send(from_addr,outdata,count,0); + +} + +void MultiDoSetGameTime(ubyte *data) +{ + float req_time; + float server_latency; + float server_game_time; + int count=0; + mprintf((0,"Processing and setting new for gametime\n")); + MULTI_ASSERT_NOMESSAGE (Netgame.local_role!=LR_SERVER); + //Now get the latency. We calculate this by comparing the current timer to the time we got back + //Which was the time that we sent the request. + SKIP_HEADER (data,&count); + req_time = MultiGetFloat(data,&count); + server_game_time = MultiGetFloat(data,&count); + + //Half of the ping time is the latency + server_latency = (timer_GetTime()-req_time)/2; + mprintf((0,"Server Latency = %f\n",server_latency)); + + Gametime = (server_game_time+server_latency); + mprintf((0,"New gametime = %f\n",Gametime)); + Got_new_game_time = 1; +} + +// Tell the server that I'm done dying +void MultiSendEndPlayerDeath () +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + mprintf ((0,"Sending end player death packet!\n")); + + size_offset=START_DATA(MP_END_PLAYER_DEATH,data,&count); + + MultiAddByte (Player_num,data,&count); + END_DATA (count,data,size_offset); + + if (Netgame.local_role==LR_CLIENT) + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); + else + MultiDoEndPlayerDeath (data); +} + +// Prints out a message we got from the server +void MultiDoMessageFromServer (ubyte *data) +{ + static int sound_id = -2; + int count=0; + char message[255]; + + SKIP_HEADER (data,&count); + + + ddgr_color color=MultiGetInt (data,&count); + ubyte len=MultiGetByte (data,&count); + + memcpy (message,&data[count],len); + count+=len; + + //Play the player hud message sound + if(sound_id==-2){ + sound_id = FindSoundName("Hudmessage"); + } + + if(sound_id>-1){ + Sound_system.Play2dSound(sound_id); + } + + AddColoredHUDMessage (color,"%s",message); // added "%s" because message could have % signs, formatting issue. +} + +// Sends a message from the server to the client +void MultiSendMessageFromServer (int color,char *message,int to) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + size_offset=START_DATA (MP_MESSAGE_FROM_SERVER,data,&count,1); + MultiAddInt (color,data,&count); + + ubyte len=strlen(message)+1; + + MultiAddByte (len,data,&count); + + memcpy (&data[count],message,len); + + count+=len; + + END_DATA (count,data,size_offset); + + if (to==MULTI_SEND_MESSAGE_ALL) + { + MultiSendReliablyToAllExcept (Player_num,data,count,false); + MultiDoMessageFromServer (data); + } + else + { + int team = -1; + + switch(to){ + case MULTI_SEND_MESSAGE_RED_TEAM: team = 0; break; + case MULTI_SEND_MESSAGE_BLUE_TEAM: team = 1; break; + case MULTI_SEND_MESSAGE_GREEN_TEAM: team = 2; break; + case MULTI_SEND_MESSAGE_YELLOW_TEAM: team = 3; break; + } + + if(team==-1){ + //send it off to one person + if(to>=0 && to=0) + ObjGhostObject(local_objnum); + } + + if (local_objnum<0) + { + mprintf ((0,"Ran out of objects!\n")); + Int3(); // Get Jason + return; + } + + object *obj=&Objects[local_objnum]; + + if (obj->type!=OBJ_WEAPON) + obj->flags|=OF_SERVER_OBJECT; + obj->movement_type=mtype; + obj->mtype.phys_info.velocity=vel; + if (lifeleft!=255) + { + obj->flags|=OF_USES_LIFELEFT; + obj->lifeleft=lifeleft; + } + DemoWriteObjLifeLeft(obj); + + obj->render_type = MultiGetByte (data,&count); + + if (obj->type==OBJ_MARKER) + { + // Get message if marker + ubyte len=MultiGetByte (data,&count); + memcpy (MarkerMessages[obj->id],&data[count],len); + count+=len; + } + bool demo_record = false; + + if(Multi_Expect_demo_object_flags) + { + //if Multi_Expect_demo_object_flags is false that means that + //OEM 1.0 is the server and so it won't be packing this data at + //the end + demo_record = (MultiGetByte (data,&count)==0xBE)?true:false; + } + + // Put it in our lists + if (obj->type!=OBJ_WEAPON) + { + Local_object_list[local_objnum]=server_objnum; + Server_object_list[server_objnum]=local_objnum; + } + + if (announce) // Do effect to announce + { + Sound_system.Play3dSound(SOUND_WALL_FADE, obj); + CreateRandomSparks (20,&obj->pos,obj->roomnum); + + MultiAnnounceEffect (obj,obj->size*3,.7f); + } + + // Create the scripts for it + InitObjectScripts (&Objects[local_objnum]); + + if (type==OBJ_MARKER) + { + if (parent_handle==Objects[Players[Player_num].objnum].handle) + Players[Player_num].num_markers++; + } + + if(demo_record) + { + obj->flags |= OF_CLIENTDEMOOBJECT; + + if( Demo_flags==DF_RECORDING) + { + mprintf((0,"Recording object created on server\n")); + //DemoWriteObjCreate(obj->type,obj->id,obj->roomnum,&obj->pos,&obj->orient,obj->parent_handle,obj); + } + } + +// mprintf((0,"MultiDoObject() Local objnum = %d\n",Server_object_list[server_objnum])); +} + +// Sends an object from the server to the client +void MultiSendObject (object *obj,ubyte announce,ubyte demo_record) +{ + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + if (obj->type!=OBJ_WEAPON) + obj->flags|=OF_CLIENT_KNOWS; + + if(demo_record) + obj->flags|=OF_CLIENTDEMOOBJECT; + +// mprintf ((0,"Telling clients to CREATE object %d type=%d\n",obj-Objects,obj->type)); + + object *parent_obj=ObjGetUltimateParent(obj); + + size_offset=START_DATA(MP_OBJECT,data,&count,1); + + uint index=MultiGetMatchChecksum (obj->type,obj->id); + + // Send server object number + MultiAddByte (announce,data,&count); + MultiAddUshort (obj-Objects,data,&count); + + // Add parent + if (obj->parent_handle==OBJECT_HANDLE_NONE) + MultiAddUshort (65535,data,&count); + else + MultiAddUshort ((obj->parent_handle & HANDLE_OBJNUM_MASK),data,&count); // Add parent + + MultiAddByte (obj->type,data,&count); + MultiAddUint (index,data,&count); + //If it's a ghost object (type==OBJ_DUMMY) send it's old type + if(obj->type==OBJ_DUMMY) + MultiAddByte (obj->dummy_type,data,&count); + + // Send position + //memcpy (&data[count],&obj->pos,sizeof(vector)); + //count+=sizeof(vector); + MultiAddVector(obj->pos,data,&count); + + // Send velocity + //memcpy (&data[count],&obj->mtype.phys_info.velocity,sizeof(vector)); + //count+=sizeof(vector); + MultiAddVector(obj->mtype.phys_info.velocity,data,&count); + + if (obj->type!=OBJ_POWERUP && obj->type!=OBJ_DUMMY) + { + // Send over orientation + angvec angs; + vm_ExtractAnglesFromMatrix (&angs,&obj->orient); + + MultiAddShort (angs.p,data,&count); + MultiAddShort (angs.h,data,&count); + MultiAddShort (angs.b,data,&count); + } + + // Send movement type + MultiAddByte (obj->movement_type,data,&count); + + // Send room number and terrain flag + MultiAddUshort (CELLNUM (obj->roomnum),data,&count); + + if (OBJECT_OUTSIDE(obj)) + MultiAddByte (1,data,&count); + else + MultiAddByte (0,data,&count); + + if (obj->flags & OF_USES_LIFELEFT) + { + MULTI_ASSERT_NOMESSAGE (obj->lifeleft<255); + MultiAddUbyte (obj->lifeleft,data,&count); + } + else + MultiAddByte (255,data,&count); + + MultiAddByte (obj->render_type,data,&count); + + if (obj->type==OBJ_MARKER) + { + // Add marker message to the end of this + ubyte len = strlen(MarkerMessages[obj->id])+1; + MultiAddByte (len,data,&count); + memcpy (data+count,MarkerMessages[obj->id],len); + count+=len; + } + + MultiAddByte ((demo_record)?0xBE:0,data,&count); + + END_DATA (count,data,size_offset); + + if (announce) // Do effect to announce + { + Sound_system.Play3dSound(SOUND_WALL_FADE, obj); + CreateRandomSparks (20,&obj->pos,obj->roomnum); + + MultiAnnounceEffect (obj,obj->size*3,.7f); + } +// mprintf((0,"Sending object %d to clients.\n",OBJNUM(obj))); + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,true); +} + +void MultiDoGuidedInfo (ubyte *data) +{ + int count=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + ubyte release=MultiGetByte (data,&count); + + if (Players[slot].guided_obj==NULL) + return; + + object *obj=Players[slot].guided_obj; + + vector pos; + matrix orient; + ushort short_roomnum; + int roomnum; + + MultiExtractPositionData (&pos,data,&count); + + // Get orientation + ushort p=MultiGetShort (data,&count); + ushort h=MultiGetShort (data,&count); + ushort b=MultiGetShort (data,&count); + + vm_AnglesToMatrix (&orient,p,h,b); + + // Get room and terrain flag + short_roomnum=MultiGetUshort (data,&count); + ubyte terrain=MultiGetByte (data,&count); + + roomnum = short_roomnum; + if (terrain) + roomnum = MAKE_ROOMNUM(roomnum); + + // Get velocity + vector vel; + vel.x=((float)MultiGetShort (data,&count))/128.0; + vel.y=((float)MultiGetShort (data,&count))/128.0; + vel.z=((float)MultiGetShort (data,&count))/128.0; + + obj->mtype.phys_info.velocity=vel; + + ObjSetPos (obj,&pos,roomnum,&orient,false); + + if (release) + ReleaseGuidedMissile (slot); +} + +// Stuff info for a guided missile +int MultiStuffGuidedInfo (int slot,ubyte *data) +{ + int count=0; + int size_offset; + object *obj=Players[slot].guided_obj; + + if (Players[slot].guided_obj==NULL) + return 0; + + size_offset=START_DATA(MP_GUIDED_INFO,data,&count); + + MultiAddByte (slot,data,&count); + MultiAddByte (0,data,&count); + + // Do position + MultiAddPositionData (&obj->pos,data,&count); + + // Do orientation + angvec angs; + vm_ExtractAnglesFromMatrix (&angs,&obj->orient); + + MultiAddShort (angs.p,data,&count); + MultiAddShort (angs.h,data,&count); + MultiAddShort (angs.b,data,&count); + + // Do roomnumber and terrain flag + + MultiAddShort (CELLNUM (obj->roomnum),data,&count); + + if (OBJECT_OUTSIDE(obj)) + MultiAddByte (1,data,&count); + else + MultiAddByte (0,data,&count); + + // Do velocity + vector *vel=&obj->mtype.phys_info.velocity; + vector *rotvel=&obj->mtype.phys_info.rotvel; + + MultiAddShort (vel->x*128.0,data,&count); + MultiAddShort (vel->y*128.0,data,&count); + MultiAddShort (vel->z*128.0,data,&count); + + END_DATA (count,data,size_offset); + + return count; +} + +// Guided missile release +void MultiDoMissileRelease(int slot,ubyte *data) +{ + // if we are the server, we'll have to forward this packet to the clients + int count=0; + unsigned char pnum_release; + + SKIP_HEADER (data,&count); + + bool is_guided = false; + pnum_release = MultiGetUbyte(data,&count); + if(pnum_release&0x80) + { + pnum_release &= ~0x80; + is_guided = true; + } + + if(Netgame.local_role==LR_SERVER) + { + // verify that slot and player match up + if(pnum_release!=slot) + { + mprintf((0,"%s Release: Packet pnum does not match real pnum\n",(is_guided)?"Guided":"Timeout")); + return; + } + + // relay the packet to the clients + MultiSendMissileRelease(pnum_release,is_guided); + + // actually perform the guided release + if(is_guided) + ReleaseGuidedMissile (pnum_release); + else + ReleaseUserTimeoutMissile (pnum_release); + }else + { + // make sure we got this packet from the server + if(slot!=0) + { + mprintf((0,"%s Release: got packet from non-server!?!\n",(is_guided)?"Guided":"Timeout")); + return; + } + + // actually perform the guided release + if(is_guided) + ReleaseGuidedMissile (pnum_release); + else + ReleaseUserTimeoutMissile (pnum_release); + } +} + +void MultiSendMissileRelease(int slot,bool is_guided) +{ + ASSERT(slot>=0 && slottype,Object_info[obj->id].name)); + + ASSERT (obj->flags & OF_CLIENT_KNOWS); + + MultiAddUshort (obj-Objects,data,&count); + MultiAddByte (obj->type,data,&count); + MultiAddByte (playsound,data,&count); + + /*#ifndef RELEASE + ubyte len = strlen(Object_info[obj->id].name)+1; + MultiAddByte (len,data,&count); + memcpy (data+count,Object_info[obj->id].name,len); + count+=len; + #endif*/ + END_DATA (count,data,size_offset); + + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,false); +} + +// Repositions a powerup to be where it should be +void MultiDoPowerupReposition (ubyte *data) +{ + int count=0; + + SKIP_HEADER (data,&count); + + int server_objnum=MultiGetUshort (data,&count); + + int local_objnum=Server_object_list[server_objnum]; + if (local_objnum==65535) + { + return; // Powerup hasn't been born yet + } + MULTI_ASSERT_NOMESSAGE (Objects[local_objnum].type==OBJ_POWERUP); + object *obj=&Objects[local_objnum]; + vector pos; + + //memcpy (&pos,&data[count],sizeof(vector)); + //count+=sizeof(vector); + pos = MultiGetVector(data,&count); + + ushort short_roomnum=MultiGetUshort (data,&count); + ubyte terrain=MultiGetByte (data,&count); + + int roomnum = short_roomnum; + if (terrain) + roomnum = MAKE_ROOMNUM(roomnum); + + obj->mtype.phys_info.velocity=Zero_vector; + + ObjSetPos (obj,&pos,roomnum,&obj->orient,true); +} + + +// Client is telling us about weapons and energy he has +void MultiDoWeaponsLoad (ubyte *data) +{ + int count=0; + + SKIP_HEADER (data,&count); + + int slot=MultiGetByte(data,&count); + + for (int i=0;itype==OBJ_WEAPON) + MultiAddShort (-1,data,&count); + else + MultiAddShort (Local_object_list[GetSmallViewer(SVW_LEFT) & HANDLE_OBJNUM_MASK],data,&count); + + obj=ObjGet (GetSmallViewer(SVW_RIGHT)); + if (!obj || obj->type==OBJ_WEAPON) + MultiAddShort (-1,data,&count); + else + MultiAddShort (Local_object_list[GetSmallViewer(SVW_RIGHT) & HANDLE_OBJNUM_MASK],data,&count); + + if (Players[Player_num].small_dll_obj==-1) + MultiAddShort (-1,data,&count); + else + MultiAddShort (Local_object_list[Players[Player_num].small_dll_obj],data,&count); + + + END_DATA (count,data,size_offset); + + nw_Send(&Netgame.server_address,data,count,0); +} + +// Tells the other players that a slot is starting/stopping its on/off weapon +void MultiSendOnOff (object *obj,ubyte on,ubyte wb_index,ubyte fire_mask) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + int slot = obj->id; + + size_offset=START_DATA(MP_ON_OFF,data,&count); + + MultiAddByte (slot,data,&count); + MultiAddByte (on,data,&count); + MultiAddByte (wb_index,data,&count); + MultiAddByte (fire_mask,data,&count); + + END_DATA (count,data,size_offset); + + if (Netgame.local_role==LR_SERVER) + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYING,false); +} + +// Server is telling us to start/stop and on/off weapon +void MultiDoOnOff (ubyte *data) +{ + int count=0; + + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + ubyte on=MultiGetByte (data,&count); + ubyte wb_index=MultiGetByte (data,&count); + ubyte fire_mask=MultiGetByte (data,&count); + + if (on) + { + Objects[Players[slot].objnum].weapon_fire_flags|=WFF_ON_OFF; + + // Clear out firing mask + dynamic_wb_info *p_dwb = &Objects[Players[slot].objnum].dynamic_wb[wb_index]; + p_dwb->cur_firing_mask=0; + + Players[slot].weapon[PW_PRIMARY].index = wb_index; + } + else + Objects[Players[slot].objnum].weapon_fire_flags&=~WFF_ON_OFF; + + + if (Netgame.local_role==LR_SERVER) + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYING,false); + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(Players[slot].objnum); + } +} + +// Tells all the clients to apply damage to a player +void MultiSendAdditionalDamage (int slot,int type,float amount) +{ + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + size_offset=START_DATA(MP_ADDITIONAL_DAMAGE,data,&count,1); + + MultiAddByte (slot,data,&count); + MultiAddByte (type,data,&count); + MultiAddFloat (amount,data,&count); + + END_DATA (count,data,size_offset); + + mprintf ((0,"Sending additional damage packet of type=%d amount=%f!\n",type,amount)); + + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYING,false); +} + +// Server is telling us to apply damage to a player +void MultiDoAdditionalDamage (ubyte *data) +{ + int count=0; + + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_CLIENT); + + //mprintf ((0,"Got additional damage packet!\n")); + + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + ubyte type = MultiGetByte (data,&count); + float amount=MultiGetFloat (data,&count); + + if (amount<0) // add to shields + { + Objects[Players[slot].objnum].shields += (-amount); + if (Objects[Players[slot].objnum].shields > MAX_SHIELDS) + Objects[Players[slot].objnum].shields = MAX_SHIELDS; + } + else // take damage + { + ApplyDamageToPlayer (&Objects[Players[slot].objnum],NULL,type,amount,1); + } +} + +// Asks the server for shields based on frametime "amount" x the type of shields requested +void MultiSendRequestShields (int type,float amount) +{ + if (Netgame.local_role!=LR_CLIENT) + return; + + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + size_offset=START_DATA(MP_REQUEST_SHIELDS,data,&count); + + MultiAddByte (Player_num,data,&count); + MultiAddByte (type,data,&count); + MultiAddFloat (amount,data,&count); + + END_DATA (count,data,size_offset); + + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); +} + +// Someone wants us to give them shields +void MultiDoRequestShields (ubyte *data) +{ + if (Netgame.local_role!=LR_SERVER) + return; + + int count=0; + + SKIP_HEADER (data,&count); + + ubyte pnum=MultiGetByte (data,&count); + ubyte type=MultiGetByte (data,&count); + float amount=MultiGetFloat (data,&count); + + if (type==SHIELD_REQUEST_ENERGY_TO_SHIELD) + { + // This guys wants to use the energy to shield converter + + float amount_to_give; + + amount_to_give=(amount*CONVERTER_RATE)/CONVERTER_SCALE; + + if (Objects[Players[pnum].objnum].shields+amount_to_give>INITIAL_SHIELDS) + amount_to_give=INITIAL_SHIELDS-Objects[Players[pnum].objnum].shields; + + if (amount_to_give>0) + { + Multi_additional_damage[pnum]=-amount_to_give; + Multi_additional_damage_type[pnum]=PD_NONE; + } + + } +} + + +// We're asking the server to damage us +void MultiSendRequestDamage (int type,float amount) +{ + if (Netgame.local_role!=LR_CLIENT) + return; + + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + size_offset=START_DATA(MP_REQUEST_DAMAGE,data,&count); + + MultiAddByte (Player_num,data,&count); + MultiAddByte (type,data,&count); + MultiAddFloat (amount,data,&count); + + END_DATA (count,data,size_offset); + + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); +} + +// Someone wants us to damage them +void MultiDoRequestDamage (ubyte *data) +{ + if (Netgame.local_role!=LR_SERVER) + return; + + int count=0; + + SKIP_HEADER (data,&count); + + ubyte pnum=MultiGetByte (data,&count); + ubyte type = MultiGetByte (data,&count); + float amount=MultiGetFloat (data,&count); + + ApplyDamageToPlayer (&Objects[Players[pnum].objnum],&Objects[Players[pnum].objnum],type,amount); +} + +// Server is telling us to create a countermeasure +void MultiDoRequestCountermeasure (ubyte *data) +{ + int count=0; + + mprintf ((0,"Got request for countermeasure!\n")); + + SKIP_HEADER (data,&count); + + ushort parent_objnum=MultiGetShort (data,&count); + + if (Netgame.local_role!=LR_SERVER) + { + parent_objnum=Server_object_list[parent_objnum]; + MULTI_ASSERT_NOMESSAGE (parent_objnum!=65535); + } + + uint checksum=MultiGetUint (data,&count); + + int id=MultiMatchWeapon (checksum); + + if (id==-1) + { + mprintf ((0,"Server data doesn't match!\n")); + Int3(); + MultiMatchWeapon (checksum); + + return; + } + + int objnum=FireWeaponFromObject (&Objects[parent_objnum],id,5); + + if (objnum>=0) + { + if (Netgame.local_role==LR_SERVER) + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYERS,false); + } + +} + +// We're asking the server to create a countermeasure for us +void MultiSendRequestCountermeasure (short objnum,int weapon_index) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + size_offset=START_DATA(MP_REQUEST_COUNTERMEASURE,data,&count); + + MultiAddShort (objnum,data,&count); + + uint index=MultiGetMatchChecksum (OBJ_WEAPON,weapon_index); + MultiAddUint (index,data,&count); + + END_DATA (count,data,size_offset); + + if (Netgame.local_role==LR_CLIENT) + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); + else + MultiDoRequestCountermeasure (data); + + mprintf ((0,"Sending out request for countermeasure!\n")); +} + + +// Server is telling us about a player who is changing his observer mode +void MultiDoObserverChange (ubyte *data) +{ + int count=0; + int objnum=-1; + + SKIP_HEADER (data,&count); + + int slot=MultiGetByte (data,&count); + ubyte mode=MultiGetByte (data,&count); + ubyte on=MultiGetByte (data,&count); + int restart_slot=MultiGetShort (data,&count); + if (mode==OBSERVER_MODE_PIGGYBACK) + { + if (Netgame.local_role==LR_SERVER) + objnum=MultiGetInt (data,&count); + else + { + objnum=Server_object_list [MultiGetInt(data,&count)]; + MULTI_ASSERT_NOMESSAGE (objnum!=65535); + } + } + + if (on) + PlayerSwitchToObserver (slot,mode,objnum); + else + { + Player_pos_fix[slot].active = true; + Player_pos_fix[slot].expire_time = Gametime + PLAYER_POS_HACK_TIME; + Player_pos_fix[slot].room = Players[restart_slot].start_roomnum; + Player_pos_fix[slot].ignored_pos = 0; + PlayerStopObserving (slot); + PlayerMoveToStartPos (slot,restart_slot); + } +} + +// Someone is asking us for permission to enter observer mode +void MultiDoRequestToObserve (ubyte *data) +{ + int count=0; + int objnum; + + mprintf ((0,"Got request to observe!\n")); + + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + SKIP_HEADER (data,&count); + + ubyte slot=MultiGetByte (data,&count); + ubyte mode=MultiGetByte (data,&count); + ubyte on=MultiGetByte (data,&count); + + if (mode==OBSERVER_MODE_PIGGYBACK) + objnum=MultiGetInt (data,&count); + + if ((Objects[Players[slot].objnum].type==OBJ_PLAYER && on) || (Objects[Players[slot].objnum].type==OBJ_OBSERVER && !on)) + { + int newcount=0; + ubyte newdata[MAX_GAME_DATA_SIZE]; + int size_offset; + + size_offset=START_DATA(MP_OBSERVER_CHANGE,newdata,&newcount); + + MultiAddByte (slot,newdata,&newcount); + MultiAddByte (mode,newdata,&newcount); + MultiAddByte (on,newdata,&newcount); + + if (!on) + { + int start_slot=PlayerGetRandomStartPosition (slot); + MultiAddShort (start_slot,newdata,&newcount); + } + else + MultiAddShort (0,newdata,&newcount); + + if (mode==OBSERVER_MODE_PIGGYBACK) + { + MultiAddInt (objnum,newdata,&newcount); + } + + END_DATA (newcount,newdata,size_offset); + + MultiSendReliablyToAllExcept (Player_num,newdata,newcount,NETSEQ_PLAYERS,false); + MultiDoObserverChange (newdata); + } +} + +// We're asking to enter observer mode +void MultiSendRequestToObserve (int mode,int on,int objnum) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + // Check to see if the player has enough shields to enter + if (on && Objects[Players[Player_num].objnum].shields<(INITIAL_SHIELDS/2)) + { + AddHUDMessage (TXT_NOTENOUGHSHIELDSFOROBS); + return; + } + + size_offset=START_DATA(MP_REQUEST_TO_OBSERVE,data,&count); + + MultiAddByte (Player_num,data,&count); + MultiAddByte (mode,data,&count); + MultiAddByte (on,data,&count); + + if (mode==OBSERVER_MODE_PIGGYBACK) + { + if (Netgame.local_role==LR_SERVER) + MultiAddInt (objnum,data,&count); + else + MultiAddInt (Local_object_list[objnum],data,&count); + } + + END_DATA (count,data,size_offset); + + if (Netgame.local_role==LR_SERVER) + { + MultiDoRequestToObserve(data); + return; + } + else + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); +} + +// Server is telling us about players that we can see +void MultiDoVisiblePlayers (ubyte *data) +{ + int count=0; + int i; + + if (Netgame.local_role==LR_SERVER) + return; + + SKIP_HEADER (data,&count); + + int bitmask=MultiGetUint (data,&count); + + MULTI_ASSERT_NOMESSAGE (MAX_NET_PLAYERS<=32); + + for (i=0;itype != OBJ_ROBOT)) + Players[Player_num].inventory.Add(OBJ_ROBOT, ROBOT_GUIDEBOT); + } + + CallGameDLL (EVT_GAMELEVELSTART,&DLLInfo); + + return true; +} + +// Returns the number of players in a game +int MultiCountPlayers () +{ + int i; + int count=0; + + for (i=0;iflags & TF_ALPHA)) && texp->r<.01 && texp->g<.01 && texp->b<.01) + { + if (!goalfaces || (goalfaces && Rooms[roomnum].faces[t].flags & FF_GOALFACE)) + { + Rooms[roomnum].faces[t].tmap=tex; + Rooms[roomnum].faces[t].flags |= FF_TEXTURE_CHANGED; + + } + + } + } + } + } +} + +void MultiSendKillObject (object *hit_obj,object *killer,float damage,int death_flags,float delay,short seed) +{ + int size; + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + ushort hit_objnum,killer_objnum; + + //mprintf((0,"MultiSendExplodeObject Hit obj:%d Killer Obj: %d\n",OBJNUM(hit_obj),OBJNUM(killer))); + MULTI_ASSERT_NOMESSAGE(Netgame.local_role==LR_SERVER); + + ASSERT (hit_obj->flags & OF_CLIENT_KNOWS); + + size=START_DATA (MP_ROBOT_EXPLODE,data,&count,1); + //Get the dying objnum + hit_objnum = OBJNUM(hit_obj); + //Get the killer objnum + + if (killer!=NULL) + killer_objnum = OBJNUM(killer); + else + killer_objnum=65535; + + MultiAddUshort (hit_objnum,data,&count); + MultiAddUshort (killer_objnum,data,&count); + MultiAddFloat (damage,data,&count); + MultiAddUint( death_flags,data,&count); + MultiAddFloat (delay,data,&count); + MultiAddUshort (seed,data,&count); + + END_DATA (count,data,size); + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,false); +} + +void MultiDoRobotExplode(ubyte *data) +{ + int count = 0; + float damage,delay; + ushort hit_objnum,killer_objnum,seed; + int death_flags; + + MULTI_ASSERT_NOMESSAGE(Netgame.local_role!=LR_SERVER); + + SKIP_HEADER (data,&count); + hit_objnum = Server_object_list[MultiGetUshort (data,&count)]; + + if (hit_objnum==65535) + { + mprintf((0,"Bad hit_objnum(%d) MultiDoRobotExplode\n",hit_objnum)); + ASSERT (1); + return; + } + + killer_objnum = MultiGetUshort (data,&count); + + if (killer_objnum!=65535) + { + killer_objnum = Server_object_list[killer_objnum]; + + if (killer_objnum==65535) + { + mprintf((0,"Bad killer_objnum(%d) in MultiDoRobotExplode()\n",killer_objnum)); + } + } + + damage = MultiGetFloat (data,&count); + death_flags = MultiGetInt(data,&count); + delay = MultiGetFloat(data,&count); + seed = MultiGetUshort (data,&count); + if(killer_objnum!=65535) + { + if ((Objects[killer_objnum].type!=OBJ_NONE) && (Objects[hit_objnum].type!=OBJ_NONE)) + { + Objects[hit_objnum].flags|=OF_SERVER_SAYS_DELETE; + ps_srand(seed); + KillObject(&Objects[hit_objnum],&Objects[killer_objnum],damage,death_flags,delay); + } + } + else + { + if ((Objects[hit_objnum].type!=OBJ_NONE)) + { + Objects[hit_objnum].flags|=OF_SERVER_SAYS_DELETE; + ps_srand(seed); + KillObject(&Objects[hit_objnum],NULL,damage,death_flags,delay); + } + + } +} + +void MultiSendDamageObject (object *hit_obj,object *killer,float damage,int weaponid) +{ + int size; + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + ushort hit_objnum,killer_objnum; + + //mprintf((0,"MultiSendDamageObject Hit obj:%d Killer Obj: %d\n",OBJNUM(hit_obj),OBJNUM(killer))); + MULTI_ASSERT_NOMESSAGE(Netgame.local_role==LR_SERVER); + ASSERT (hit_obj->flags & OF_CLIENT_KNOWS); + //mprintf((0,"$"));//KBTEST + size=START_DATA (MP_ROBOT_DAMAGE,data,&count,1); + //Get the dying objnum + hit_objnum = OBJNUM(hit_obj); + //Get the killer objnum + killer_objnum = OBJNUM(killer); + MultiAddUshort (hit_objnum,data,&count); + MultiAddUshort (killer_objnum,data,&count); + MultiAddFloat (damage,data,&count); + MultiAddInt (weaponid,data,&count); + + END_DATA (count,data,size); + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYING,false); +} + +void MultiDoRobotDamage(ubyte *data) +{ + int count = 0; + float damage; + int weaponid; + ushort hit_objnum,killer_objnum; + + MULTI_ASSERT_NOMESSAGE(Netgame.local_role!=LR_SERVER); + SKIP_HEADER (data,&count); + hit_objnum = Server_object_list[MultiGetUshort (data,&count)]; + killer_objnum = Server_object_list[MultiGetUshort (data,&count)]; + + if (hit_objnum==65535 || killer_objnum==65535 || !(Objects[hit_objnum].flags & OF_SERVER_OBJECT) || !(Objects[killer_objnum].flags & OF_SERVER_OBJECT)) + { + mprintf((0,"Bad hit_objnum(%d) or killer_objnum(%d) in MultiDoRobotDamage()\n",hit_objnum,killer_objnum)); + //Int3(); // Get Jason, bad object number + return; + } + + damage = MultiGetFloat (data,&count); + weaponid = MultiGetInt (data,&count); + if((!((Objects[hit_objnum].flags&OF_DYING)||(Objects[hit_objnum].flags&OF_DEAD)))&&Objects[hit_objnum].type!=255) + if((!((Objects[killer_objnum].flags&OF_DYING)||(Objects[killer_objnum].flags&OF_DEAD)))&&Objects[killer_objnum].type!=255) + { + // The damage type doesn't matter on the client side (only server side) -- so, this one is bogus + ApplyDamageToGeneric(&Objects[hit_objnum],&Objects[killer_objnum],GD_PHYSICS,damage,1,weaponid); + } +} + +void MultiAddObjAnimUpdate(int objnum) +{ + MULTI_ASSERT_NOMESSAGE(Netgame.local_role==LR_SERVER); + ASSERT(Objects[objnum].flags & OF_CLIENT_KNOWS); + //mprintf((0,"D`")); + for (int i=0;i=MAX_GAME_DATA_SIZE) + MultiSendFullPacket (i,0); + + memcpy (&Multi_send_buffer[i][Multi_send_size[i]],data,count); + Multi_send_size[i]+=count; + MultiSendFullPacket (i,0); + } + } +} + +void MultiSendRobotFireSound(short soundidx,ushort objnum) +{ + int size; + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + //mprintf((0,"&"));//KBTEST + MULTI_ASSERT_NOMESSAGE(Netgame.local_role==LR_SERVER); + //mprintf((0,"Sending Robot %d fire sound.\n",objnum)); + object *obj=&Objects[objnum]; + + ASSERT (obj->flags & OF_CLIENT_KNOWS); + + size=START_DATA (MP_ROBOT_FIRE_SOUND,data,&count); + MultiAddUshort (objnum,data,&count); + + MultiAddShort (soundidx,data,&count); + + END_DATA (count,data,size); + + //Add it to the outgoing queue. The MultiSendRobotFireSound() function will actually send it + for (int i=0;i=MAX_GAME_DATA_SIZE) + MultiSendFullPacket (i,0); + + memcpy (&Multi_send_buffer[i][Multi_send_size[i]],data,count); + Multi_send_size[i]+=count; + MultiSendFullPacket (i,0); + } + } + +} + +void MultiDoRobotFireSound(ubyte *data) +{ + int objnum; + short soundidx; + int count=0; + MULTI_ASSERT_NOMESSAGE(Netgame.local_role!=LR_SERVER); + SKIP_HEADER (data,&count); + + int serverobjnum = MultiGetUshort (data,&count); + objnum = Server_object_list[serverobjnum]; + soundidx = MultiGetShort (data,&count); + + if (objnum==65535 || !(Objects[objnum].flags & OF_SERVER_OBJECT)) + { + mprintf((0,"Bad server objnum(%d) in MultiDoRobotFireSound().Objnum = %d\n",serverobjnum,objnum)); + //Int3(); // Get Jason, bad object number + return; + } + Sound_system.Play3dSound(soundidx,&Objects[objnum]); +} + +void MultiAddObjTurretUpdate(int objnum) +{ + + MULTI_ASSERT_NOMESSAGE(Netgame.local_role==LR_SERVER); + ASSERT (Objects[objnum].flags & OF_CLIENT_KNOWS); + + + for (int i=0;irtype.pobj_info; + if((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && + (obj->flags & OF_POLYGON_OBJECT) && (p_info->multi_turret_info.num_turrets)) + { + int i; + ASSERT (Objects[objnum].flags & OF_CLIENT_KNOWS); + size=START_DATA (MP_TURRET_UPDATE,data,&count); + MultiAddUshort (objnum,data,&count); + + multi_turret_info.keyframes = (float *)&turret_holder; + ObjGetTurretUpdate(objnum,&multi_turret_info); + + + MultiAddFloat (multi_turret_info.time,data,&count); + MultiAddUshort(multi_turret_info.num_turrets,data,&count); + + for(i = 0; i < multi_turret_info.num_turrets; i++) + { + if(MAX_COOP_TURRETS>i) + MultiAddFloat (multi_turret_info.keyframes[i],data,&count); + } + END_DATA (count,data,size); + + } + + return count; +} + +void MultiDoTurretUpdate(ubyte *data) +{ + + multi_turret multi_turret_info; + int objnum; + ushort num_turrets; + float turr_time; + int count = 0; + MULTI_ASSERT_NOMESSAGE(Netgame.local_role!=LR_SERVER); + + SKIP_HEADER (data,&count); + + int serverobjnum = MultiGetUshort (data,&count); + objnum = Server_object_list[serverobjnum]; + + if (objnum==65535 || !(Objects[objnum].flags & OF_SERVER_OBJECT)) + { + mprintf((0,"Bad server objnum(%d) in MultiDoTurretUpdate().\n",serverobjnum)); + //Int3(); // Get Jason , bad object number + return; + } + + turr_time = MultiGetFloat (data,&count); + num_turrets = MultiGetUshort (data,&count); + multi_turret_info.keyframes = (float *)&turret_holder; + multi_turret_info.num_turrets = num_turrets; + for(int i = 0; i < num_turrets; i++) + { + if(MAX_COOP_TURRETS>i) + multi_turret_info.keyframes[i] = MultiGetFloat (data,&count); + } + + if(Objects[objnum].type!=255) + { + ObjSetTurretUpdate(objnum, &multi_turret_info); + } + +} + +void MultiSendClientInventoryUseItem(int type,int id) +{ + if(id==-1) + { + mprintf((0,"Asking server to use objnum %d\n",OBJNUM(ObjGet(type)))); + }else + { + mprintf((0,"Asking server to use T=%d ID=%d\n",type,id)); + } + + int size=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int count = 0; + //Client sends this only + MULTI_ASSERT_NOMESSAGE(Netgame.local_role!=LR_SERVER); + + if(id!=-1) + { + //type/id use + size=START_DATA (MP_CLIENT_USE_INVENTORY_ITEM,data,&count,1); + MultiAddUshort(type,data,&count); + MultiAddUint (MultiGetMatchChecksum(type,id),data,&count); + END_DATA (count,data,size); + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); + + }else + { + //object use + int local_objhandle = type; + object *local_obj = ObjGet(local_objhandle); + ASSERT(local_obj); + ASSERT(local_obj->type!=OBJ_NONE); + + if(local_obj) + { + int server_objnum = Local_object_list[OBJNUM(local_obj)]; + ASSERT (server_objnum!=65535); + + mprintf((0,"Inven Use: Requesting to use server objnum %d. T=%d i=%d\n",server_objnum,local_obj->type,local_obj->id)); + + size=START_DATA (MP_CLIENT_USE_INVENTORY_ITEM,data,&count,1); + MultiAddUshort(server_objnum,data,&count); + MultiAddUint (0xFFFFFFFF,data,&count); + END_DATA (count,data,size); + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); + } + } + +} + +void MultiDoClientInventoryUseItem(int slot,ubyte *data) +{ + int count=0; + int type; + int id; + unsigned int hash; + + //Only the server receives this + MULTI_ASSERT_NOMESSAGE(Netgame.local_role==LR_SERVER); + SKIP_HEADER (data,&count); + + type = MultiGetUshort (data,&count); + hash = MultiGetUint(data,&count); + + bool is_counter_measure = false; + + if(hash==0xFFFFFFFF) + { + //object use + int server_objnum; + server_objnum = type; + + + mprintf((0,"Inven: use request objnum %d\n",server_objnum)); + + //the objnum coming in better be in our object list + ASSERT( server_objnum>=0 && server_objnum=0 && type=0 && to_who0) + { + drop_ratio = (float)NetPlayers[Player_num].total_bytes_rcvd/(float)server_sent; + float loss = 1-drop_ratio; + int pct_loss = loss*100; + NetPlayers[Player_num].percent_loss=pct_loss; + + if(pct_loss>15) + { + if(Bandwidth_throttle >= 0) + Bandwidth_throttle++; + else + Bandwidth_throttle = 0; + } + else if(pct_loss<5) + { + if(Bandwidth_throttle <= 0) + Bandwidth_throttle--; + else + Bandwidth_throttle = 0; + } + if(NetPlayers[Player_num].ping_time>=PING_FALLOFF) + { + Bandwidth_throttle--; + Bandwidth_throttle--; + } + //mprintf_at((2,1,0,"Packet Loss: %d%% ",pct_loss)); + } + else + { + //mprintf_at((2,1,0,"Packet Loss: 0%% ")); + NetPlayers[Player_num].percent_loss=0; + Bandwidth_throttle --; + } + if(Bandwidth_throttle>=3) + { + //Time to lower the pps setting! + if(NetPlayers[Player_num].pps>PPS_MIN) + { + NetPlayers[Player_num].pps--; + MultiSendPPSSet(NetPlayers[Player_num].pps); + Bandwidth_throttle = 0; + } + } + else if(Bandwidth_throttle<= -2) + { + //Time to up the pps setting! + if(NetPlayers[Player_num].ppssize; + NetPlayers[playernum].file_xfer_id = filenum; + mprintf((0,"File size = %d\n",cfp->size)); + SendDataChunk(playernum); + } + } + else + { + mprintf((0,"Got a file request while one was already in progress!\n")); + DenyFile(playernum,filenum,NetPlayers[playernum].file_xfer_who); + } +} + +void DenyFile(int playernum,int filenum,int file_who) +{ + int size=0; + ubyte outdata[MAX_GAME_DATA_SIZE]; + int count = 0; + size=START_DATA (MP_FILE_DENIED,outdata,&count); + MultiAddUshort (filenum,outdata,&count); + MultiAddUshort (Player_num,outdata,&count); + MultiAddUshort (file_who,outdata,&count); + END_DATA(count,outdata,size); + NetPlayers[playernum].file_xfer_cfile = NULL; + if(Netgame.local_role==LR_SERVER) + { + nw_SendReliable (NetPlayers[playernum].reliable_socket,outdata,count); + } + else + { + nw_SendReliable (NetPlayers[Player_num].reliable_socket,outdata,count); + } +} + +void MultiCancelFile(int playernum,int filenum,int file_who) +{ + int size=0; + ubyte outdata[MAX_GAME_DATA_SIZE]; + int count = 0; + // Send message to our peer and abort the xfer + if(NetPlayers[playernum].file_xfer_cfile) + { + cfclose(NetPlayers[playernum].file_xfer_cfile); + + } + + size=START_DATA (MP_FILE_CANCEL,outdata,&count); + MultiAddUshort (playernum,outdata,&count); + MultiAddUshort (filenum,outdata,&count); + MultiAddUshort (file_who,outdata,&count); + END_DATA(count,outdata,size); + NetPlayers[playernum].file_xfer_cfile = NULL; + NetPlayers[playernum].file_xfer_pos = 0; + NetPlayers[playernum].file_xfer_flags = NETFILE_NONE; + if(Netgame.local_role==LR_SERVER) + { + nw_SendReliable (NetPlayers[playernum].reliable_socket,outdata,count); + } + else + { + nw_SendReliable (NetPlayers[Player_num].reliable_socket,outdata,count); + } + +} +void MultiDoFileDenied(ubyte *data) +{ + //We asked for a file, but the request was denied for some reason + int count = 0; + SKIP_HEADER (data,&count); + ushort filenum = MultiGetUshort (data,&count); + ushort playernum = MultiGetUshort (data,&count); + ushort filewho = MultiGetUshort (data,&count); + mprintf((0,"Got a file denied packet from %d\n",playernum)); + + DoNextPlayerFile(filewho); +} + +void MultiDoFileData(ubyte *data) +{ + //File data. We asked for it, now the server is sending it to us. + unsigned int total_len; // Length of the entire file + unsigned int curr_len; // Length of file sent so far + ushort file_id; // Defines which file this is + ushort playernum; // Who is sending us the file + ushort data_len; // between 1-450 bytes + //ushort file_who; + int count = 0; + ubyte outdata[MAX_GAME_DATA_SIZE]; + int outcount = 0; + int size; + + SKIP_HEADER (data,&count); + total_len = MultiGetUint(data,&count); + curr_len = MultiGetUint(data,&count); //Length of the file after before this data chunk + file_id = MultiGetUshort (data,&count); + playernum = MultiGetUshort (data,&count); + //file_who = MultiGetUshort (data,&count); + data_len = MultiGetUshort (data,&count); + //mprintf((0,"Got a data packet from %d\n",playernum)); + if((NetPlayers[playernum].file_xfer_flags == NETFILE_RECEIVING)||(NetPlayers[playernum].file_xfer_flags == NETFILE_ASKING)) + { + + //Find out if this is the first packet of this file. if so, create and open the file + if(NetPlayers[playernum].file_xfer_pos == 0) + { + mprintf((0,"Creating file...\n")); + CFILE * cfp; + //char filename[_MAX_PATH]; + char filewithpath[_MAX_PATH*2]; + strcpy(filewithpath,GetFileNameFromPlayerAndID(playernum,file_id)); + //ddio_MakePath(filewithpath,LocalD3Dir,"custom","cache",filename,NULL); + + cfp = cfopen(filewithpath,"wb"); + if(!cfp) + { + mprintf((0,"Can't create file %s\n",filewithpath)); + // We couldn't create the file, so cancel the attempt to transfer it. + MultiCancelFile(playernum,file_id,NetPlayers[playernum].file_xfer_who); + return; + } + NetPlayers[playernum].file_xfer_cfile = cfp; + NetPlayers[playernum].file_xfer_flags = NETFILE_RECEIVING; + } + + MULTI_ASSERT_NOMESSAGE(NetPlayers[playernum].file_xfer_cfile); + //Write the data to the file + int data_wrote = cf_WriteBytes(data+count,data_len,NetPlayers[playernum].file_xfer_cfile); + if(data_wrote!=data_len) + { + mprintf((0,"cf_WriteBytes() should have written %d bytes, but only wrote %d!\n",data_len,data_wrote)); + Int3(); + } + + NetPlayers[playernum].file_xfer_pos+=data_len; + //See if this is the last data chunk + if(NetPlayers[playernum].file_xfer_pos>=total_len) + { + //We got the whole thing baby! + cfclose(NetPlayers[playernum].file_xfer_cfile); + NetPlayers[playernum].file_xfer_cfile = NULL; + NetPlayers[playernum].file_xfer_pos = 0; + DoNextPlayerFile(playernum); + mprintf((0,"Finished downloading file!\n")); + return; // Don't ack the last packet + } + //Ack the sender + size=START_DATA (MP_FILE_ACK,outdata,&outcount); + MultiAddUshort (Player_num,outdata,&outcount); + MultiAddUint (NetPlayers[playernum].file_xfer_pos,outdata,&outcount); + END_DATA(outcount,outdata,size); + if(Netgame.local_role==LR_SERVER) + { + nw_SendReliable (NetPlayers[playernum].reliable_socket,outdata,outcount,true); + } + else + { + nw_SendReliable (NetPlayers[Player_num].reliable_socket,outdata,outcount,true); + } + } + else + { + mprintf((0,"Received file transfer data from someone who we aren't expecting data from!\n")); + } +} + +void MultiDoFileAck(ubyte *data) +{ + //If we are transferring a file, and someone ACK's us, simply send them the next bit of data they are waiting for + ubyte playernum; // Who is acking us + uint len_recvd; // Total number of bytes received so far + int count = 0; + + SKIP_HEADER (data,&count); + playernum = MultiGetUshort (data,&count); + len_recvd = MultiGetUint(data,&count); + //mprintf((0,"Got a ACK packet from %d\n",playernum)); + if(NetPlayers[playernum].file_xfer_flags == NETFILE_SENDING) + { + if(NetPlayers[playernum].file_xfer_pos == len_recvd) + { + SendDataChunk(playernum); + } + else + { + Int3();//This shouldn't happen because the reliable network code should handle it + } + } + else + { + mprintf((0,"Received an ACK from someone we weren't sending a file to!\n")); + } +} + + +void SendDataChunk(int playernum) +{ + int outcount = 0; + static ubyte outdata[MAX_PACKET_SIZE]; + static ubyte readbuf[MAX_PACKET_SIZE]; + int size; + int dataread; + int done=0; + //mprintf((0,"Sending a data chunk to %d.\n",playernum)); + //Read the next chunk of the file and send it! + if((DATA_CHUNK_SIZE+NetPlayers[playernum].file_xfer_pos)>(unsigned int)NetPlayers[playernum].file_xfer_cfile->size) + { + dataread = NetPlayers[playernum].file_xfer_cfile->size-NetPlayers[playernum].file_xfer_pos; + //This is the end of the file + mprintf((0,"End of file detected!\n")); + done = 1; + } + else + { + dataread = DATA_CHUNK_SIZE; + } + + try + { + cf_ReadBytes(readbuf,dataread,NetPlayers[playernum].file_xfer_cfile); + } + catch(cfile_error *cfe) + { + int t=cfe->read_write; //this is is here to fix compile warning + //Woops, can't read the file, better error out! + MultiCancelFile(playernum,NetPlayers[playernum].file_xfer_id,NetPlayers[playernum].file_xfer_who); + Int3(); + return; + } + + if(done) + { + cfclose(NetPlayers[playernum].file_xfer_cfile); + NetPlayers[playernum].file_xfer_cfile = NULL; + NetPlayers[playernum].file_xfer_pos = 0; + NetPlayers[playernum].file_xfer_flags = NETFILE_NONE; + } + //Send them the next chunk + size=START_DATA (MP_FILE_DATA,outdata,&outcount); + MultiAddUint (NetPlayers[playernum].file_xfer_total_len,outdata,&outcount); + MultiAddUint (NetPlayers[playernum].file_xfer_pos,outdata,&outcount); + MultiAddUshort (NetPlayers[playernum].file_xfer_id,outdata,&outcount); + if(Netgame.local_role==LR_SERVER) + MultiAddUshort (NetPlayers[playernum].file_xfer_who,outdata,&outcount); + else + MultiAddUshort (Player_num,outdata,&outcount); + //MultiAddUshort (NetPlayers[playernum].file_xfer_who,outdata,&outcount); + MultiAddUshort (dataread,outdata,&outcount); + memcpy(outdata+outcount,readbuf,dataread); + outcount += dataread; + END_DATA(outcount,outdata,size); + if(Netgame.local_role==LR_SERVER) + { + nw_SendReliable (NetPlayers[playernum].reliable_socket,outdata,outcount,true); + } + else + { + nw_SendReliable (NetPlayers[Player_num].reliable_socket,outdata,outcount,true); + } + NetPlayers[playernum].file_xfer_pos += dataread; +} + +void MultiDoFileCancelled(ubyte *data) +{ + unsigned short playernum; // Who is telling us the file is cancelled + unsigned short filewho; // Who's file is being cancelled + int count = 0; + + SKIP_HEADER (data,&count); + playernum = MultiGetUshort (data,&count); + filewho = MultiGetUshort (data,&count); + mprintf((0,"Got a cancelled packet from %d for %d\n",playernum,filewho)); + if(NetPlayers[filewho].file_xfer_cfile) + { + char delfile[_MAX_PATH*2]; + strcpy(delfile,NetPlayers[filewho].file_xfer_cfile->name); + cfclose(NetPlayers[filewho].file_xfer_cfile); + NetPlayers[filewho].file_xfer_cfile = NULL; + ddio_DeleteFile(delfile); + } + NetPlayers[filewho].file_xfer_cfile = NULL; + NetPlayers[filewho].file_xfer_pos = 0; + DoNextPlayerFile(filewho); +} + +//ddio_splitpath +void MultiSendClientCustomData(int slot,int whoto) +{ + ubyte data[MAX_GAME_DATA_SIZE]; + char csum_filename[_MAX_PATH]; + char path[_MAX_PATH]; + char ext[_MAX_PATH]; + char file[_MAX_PATH]; + int count=0; + int size_offset; + + //@@mprintf((0,"Sending player %d's data to %d.\n",slot,whoto)); + + size_offset=START_DATA(MP_PLAYER_CUSTOM_DATA,data,&count); + //Who we are talking about + MultiAddShort (slot,data,&count); + + //Send custom logo filename (all 0's if none) + csum_filename[0]=NULL; + if(NetPlayers[slot].ship_logo[0]) + { + if(slot==Player_num) + { + char szcrc[_MAX_PATH]; + ddio_SplitPath(NetPlayers[slot].ship_logo, path, file, ext); + sprintf(szcrc,"_%.8x",cf_GetfileCRC(NetPlayers[slot].ship_logo)); + //See if the the CRC is already in the filename + if(strnicmp(szcrc,file+(strlen(file)-9),9)==0) + { + sprintf(csum_filename,"%s%s",file,ext); + } + else + { + sprintf(csum_filename,"%s_%.8x%s",file,cf_GetfileCRC(NetPlayers[slot].ship_logo),ext); + } + } + else + { + strcpy(csum_filename,NetPlayers[slot].ship_logo); + } + } + unsigned short logo_len = strlen(csum_filename)+1; + MultiAddUshort (logo_len,data,&count); + memcpy (data+count,csum_filename,logo_len); + count+=logo_len; + + for(int t=0;t<4;t++) + { + //Send custom voice taunts (all 0's if none) + csum_filename[0]='\0'; + char *filename; + + switch(t) + { + case 0: + filename = NetPlayers[slot].voice_taunt1; + break; + case 1: + filename = NetPlayers[slot].voice_taunt2; + break; + case 2: + filename = NetPlayers[slot].voice_taunt3; + break; + case 3: + filename = NetPlayers[slot].voice_taunt4; + break; + } + + if(filename[0]) + { + if(slot==Player_num) + { + char szcrc[_MAX_PATH]; + ddio_SplitPath(filename, path, file, ext); + sprintf(szcrc,"_%.8x",cf_GetfileCRC(filename)); + //See if the the CRC is already in the filename + if(strnicmp(szcrc,file+(strlen(file)-9),9)==0) + { + sprintf(csum_filename,"%s%s",file,ext); + } + else + { + sprintf(csum_filename,"%s_%.8x%s",file,cf_GetfileCRC(filename),ext); + } + } + else + { + strcpy(csum_filename,filename); + } + } + unsigned short vt_len = strlen(csum_filename)+1; + MultiAddUshort (vt_len,data,&count); + memcpy (data+count,csum_filename,vt_len); + count+=vt_len; + } + + END_DATA (count,data,size_offset); + + if(Netgame.local_role==LR_SERVER) + { + if(whoto==-1) + { + //Send to all clients + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYING); + } + else + { + nw_SendReliable (NetPlayers[whoto].reliable_socket,data,count,true); + } + } + else + { + //Send to the server + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,true); + } +} + + + +void MultiDoCustomPlayerData(ubyte *data) +{ + unsigned short playernum; // Who has data we are interested in + int count = 0; + + SKIP_HEADER (data,&count); + playernum = MultiGetUshort (data,&count); + if(playernum==Player_num) + return; + mprintf((0,"Got custom data in MultiDoCustomPlayerData()\n")); + NetPlayers[playernum].custom_file_seq = NETFILE_ID_SHIP_TEX;//First in the sequence of files we will request + short logo_len = MultiGetUshort (data,&count); + memcpy (NetPlayers[playernum].ship_logo,data+count,logo_len); + count+=logo_len; + mprintf((0,"%s uses custom ship logo %s\n",Players[playernum].callsign,NetPlayers[playernum].ship_logo)); + + for(int t=0;t<4;t++) + { + char *filename; + short vt_len; + + switch(t) + { + case 0: + filename = NetPlayers[playernum].voice_taunt1; + break; + case 1: + filename = NetPlayers[playernum].voice_taunt2; + break; + case 2: + filename = NetPlayers[playernum].voice_taunt3; + break; + case 3: + filename = NetPlayers[playernum].voice_taunt4; + break; + } + + vt_len = MultiGetUshort (data,&count); + memcpy (filename,data+count,vt_len); + count+=vt_len; + } +} + + +char * GetFileNameFromPlayerAndID(short playernum,short id) +{ + static char rval[_MAX_PATH*2]; + + rval[0]=NULL; + + if(playernum>=MAX_NET_PLAYERS) + { + mprintf((0,"Invalid playernum (%d) passed to GetFileNameFromPlayerAndID()\n",playernum)); + return rval; + } + if(id>NETFILE_ID_LAST_FILE) + { + mprintf((0,"Invalid file id (%d) passed to GetFileNameFromPlayerAndID()\n",id)); + return rval; + } + switch(id) + { + + case NETFILE_ID_NOFILE: + break; + case NETFILE_ID_SHIP_TEX: + if(NetPlayers[playernum].ship_logo[0]) + ddio_MakePath(rval,Base_directory,"custom","graphics",NetPlayers[playernum].ship_logo,NULL); + break; + case NETFILE_ID_VOICE_TAUNT1: + if(NetPlayers[playernum].voice_taunt1[0]) + ddio_MakePath(rval,Base_directory,"custom","sounds",NetPlayers[playernum].voice_taunt1,NULL); + break; + case NETFILE_ID_VOICE_TAUNT2: + if(NetPlayers[playernum].voice_taunt2[0]) + ddio_MakePath(rval,Base_directory,"custom","sounds",NetPlayers[playernum].voice_taunt2,NULL); + break; + case NETFILE_ID_VOICE_TAUNT3: + if(NetPlayers[playernum].voice_taunt3[0]) + ddio_MakePath(rval,Base_directory,"custom","sounds",NetPlayers[playernum].voice_taunt3,NULL); + break; + case NETFILE_ID_VOICE_TAUNT4: + if(NetPlayers[playernum].voice_taunt4[0]) + ddio_MakePath(rval,Base_directory,"custom","sounds",NetPlayers[playernum].voice_taunt4,NULL); + break; + default: + mprintf((0,"Unknown id (%d) passed to GetFileNameFromPlayerAndID()\n",id)); + Int3(); + break; + } + if(*rval) + { + CFILE *cfp; + cfp = cfopen(rval,"rb"); + if(!cfp) + { + mprintf((0,"Multiplayer file xfer File does not exist, not using file %d for player %d!\n",id,playernum)); + //rval[0] = NULL; + } + else if(327683) + return; + + if(!taunt_AreEnabled()) + return; + + //make sure an audio file exists there + char audio_file[_MAX_PATH]; + + switch(index) + { + case 0: + ddio_MakePath(audio_file,LocalCustomSoundsDir,NetPlayers[Player_num].voice_taunt1,NULL); + break; + case 1: + ddio_MakePath(audio_file,LocalCustomSoundsDir,NetPlayers[Player_num].voice_taunt2,NULL); + break; + case 2: + ddio_MakePath(audio_file,LocalCustomSoundsDir,NetPlayers[Player_num].voice_taunt3,NULL); + break; + case 3: + ddio_MakePath(audio_file,LocalCustomSoundsDir,NetPlayers[Player_num].voice_taunt4,NULL); + break; + } + + if(!cfexist(audio_file)){ + mprintf((0,"Ignoring request to play audio taunt...it does not exist\n")); + return; + } + + float t = Gametime; + if( (t - Time_last_taunt_request) < taunt_DelayTime() ){ + //too soon since last request + return; + } + Time_last_taunt_request = t; + + int count = 0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset = START_DATA(MP_CLIENT_PLAY_TAUNT,data,&count); + + MultiAddByte(Player_num,data,&count); + MultiAddByte(index,data,&count); + + END_DATA(count,data,size_offset); + + if (Netgame.local_role==LR_SERVER) + { + MultiDoRequestPlayTaunt (data); + } + else + { + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); + } +} + +// the server is telling us that about a player's message-type state (is [not] typing a message) +void MultiDoTypeIcon(ubyte *data) +{ + int count = 0; + SKIP_HEADER (data,&count); + int pnum = MultiGetByte(data,&count); + bool typing = (MultiGetByte(data,&count))?true:false; + + ASSERT( pnum >= 0 && pnumlast_special_wb_firing = wb_index; + Objects[obj_num].weapon_fire_flags = flags; + if(Demo_flags == DF_RECORDING) + { + DemoWriteObjWeapFireFlagChanged(obj_num); + } +} + + +void MultiSendAttach(object *parent, char parent_ap, object *child, char child_ap, bool f_aligned) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + size_offset=START_DATA(MP_ATTACH_OBJ,data,&count); + MultiAddUshort (OBJNUM(parent),data,&count); + MultiAddByte (parent_ap,data,&count); + MultiAddUshort (OBJNUM(child),data,&count); + MultiAddByte (child_ap,data,&count); + MultiAddByte (f_aligned?1:0,data,&count); + END_DATA (count,data,size_offset); + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_WORLD,false); + +} + +void MultiDoAttach(ubyte * data) +{ + + object *parent; + char parent_ap; + object *child; + char child_ap; + bool f_aligned; + + int count = 0; + SKIP_HEADER (data,&count); + + ushort parent_num = Server_object_list[MultiGetUshort(data,&count)]; + parent_ap = MultiGetByte(data,&count); + parent = &Objects[parent_num]; + ushort child_num = Server_object_list[MultiGetUshort(data,&count)]; + child_ap = MultiGetByte(data,&count); + child = &Objects[child_num]; + f_aligned = MultiGetByte(data,&count)?true:false; + + if(parent_num==65535) + { + mprintf((0,"Client/Server object lists don't match! (Server num %d)\n",parent_num)); + Int3(); + return; + } + if(child_num==65535) + { + mprintf((0,"Client/Server object lists don't match! (Server num %d)\n",child_num)); + Int3(); + return; + } + + ASSERT(child >= Objects && parent >= Objects); + ASSERT(child->type != OBJ_NONE && parent->type != OBJ_NONE); + + AttachObject(parent, parent_ap, child, child_ap, f_aligned); +} + +void MultiSendAttachRad(object *parent, char parent_ap, object *child, float rad) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + size_offset=START_DATA(MP_ATTACH_RAD_OBJ,data,&count); + MultiAddUshort (OBJNUM(parent),data,&count); + MultiAddByte (parent_ap,data,&count); + MultiAddUshort (OBJNUM(child),data,&count); + MultiAddFloat (rad,data,&count); + END_DATA (count,data,size_offset); + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_WORLD,false); + +} + +void MultiDoAttachRad(ubyte * data) +{ + + object *parent; + char parent_ap; + object *child; + float rad; + + int count = 0; + SKIP_HEADER (data,&count); + + ushort parent_num = Server_object_list[MultiGetUshort(data,&count)]; + parent_ap = MultiGetByte(data,&count); + parent = &Objects[parent_num]; + ushort child_num = Server_object_list[MultiGetUshort(data,&count)]; + rad = MultiGetFloat(data,&count); + child = &Objects[child_num]; + + if(parent_num==65535) + { + mprintf((0,"Client/Server object lists don't match! (Server num %d)\n",parent_num)); + Int3(); + return; + } + if(child_num==65535) + { + mprintf((0,"Client/Server object lists don't match! (Server num %d)\n",child_num)); + Int3(); + return; + } + + ASSERT(child >= Objects && parent >= Objects); + ASSERT(child->type != OBJ_NONE && parent->type != OBJ_NONE); + + AttachObject(parent, parent_ap, child, rad); +} + +void MultiSendUnattach(object *child) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + + ASSERT (Objects[OBJNUM(child)].flags & OF_CLIENT_KNOWS); + + size_offset=START_DATA(MP_UNATTACH_OBJ,data,&count); + MultiAddUshort (OBJNUM(child),data,&count); + END_DATA (count,data,size_offset); + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,false); +} + +void MultiDoUnattach(ubyte * data) +{ + object *child; + + int count = 0; + SKIP_HEADER (data,&count); + int server_objnum = MultiGetUshort(data,&count); + ASSERT(server_objnum>=0 && server_objnum=0 && playerdynamic_wb[wb_index].cur_firing_mask; + int save_flags=obj->dynamic_wb[wb_index].flags; + int save_roomnum=obj->roomnum; + matrix save_orient=obj->orient; + vector save_pos=obj->pos; + + obj->dynamic_wb[wb_index].cur_firing_mask=Player_fire_packet[pnum].fire_mask; + + if (quaded) + obj->dynamic_wb[wb_index].flags |=DWBF_QUAD; + else + obj->dynamic_wb[wb_index].flags &=~DWBF_QUAD; + + // Move the player into the temp position just for this firing + ObjSetPos (obj,&pos,roomnum,&orient,false); + + WBFireBattery(obj, &Ships[ship_index].static_wb[wb_index], 0, wb_index,scalar); + + obj->dynamic_wb[wb_index].cur_firing_mask=save_mask; + obj->dynamic_wb[wb_index].flags=save_flags; + + ObjSetPos (obj,&save_pos,save_roomnum,&save_orient,false); + + // Play cutoff sound if there is one + int cutoff_sound = Ships[ship_index].firing_release_sound[wb_index]; + if (cutoff_sound != -1) + Sound_system.Play3dSound(cutoff_sound,&Objects[Players[pnum].objnum]); +} + +void MultiSendPermissionToFire (int pnum) +{ + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + object *obj=&Objects[Players[pnum].objnum]; + + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + size_offset=START_DATA(MP_PERMISSION_TO_FIRE,data,&count); + MultiAddByte (pnum,data,&count); + MultiAddUbyte (Player_fire_packet[pnum].wb_index,data,&count); + MultiAddUbyte (Player_fire_packet[pnum].fire_mask,data,&count); + MultiAddUbyte (Player_fire_packet[pnum].damage_scalar,data,&count); + + // Do position + //memcpy (&data[count],&obj->pos,sizeof(vector)); + //count+=sizeof(vector); + MultiAddVector(obj->pos,data,&count); + + // Do orientation + angvec angs; + vm_ExtractAnglesFromMatrix (&angs,&obj->orient); + + MultiAddShort (angs.p,data,&count); + MultiAddShort (angs.h,data,&count); + MultiAddShort (angs.b,data,&count); + + // Do roomnumber and terrain flag + MultiAddShort (CELLNUM (obj->roomnum),data,&count); + + // Fill flags + if (OBJECT_OUTSIDE(obj)) + MultiAddByte (1,data,&count); + else + MultiAddByte (0,data,&count); + + END_DATA (count,data,size_offset); + + #ifdef RELIABLE_SECONDARIES + if( (Player_fire_packet[pnum].wb_index>=SECONDARY_INDEX) && (Player_fire_packet[pnum].wb_index!=FLARE_INDEX) ) + { + // send reliably + //mprintf((0,"PCS: SENDING RELIABLE FIRE FOR %d\n",pnum)); + MultiSendReliablyToAllExcept(Player_num,data,count,NETSEQ_PLAYING,true); + } + else + #endif + { + MultiSendToAllExcept (Player_num,data,count,NETSEQ_PLAYING); + } + MultiDoPermissionToFire (data); + +} + +// A client is asking for permission to fire +void MultiDoRequestToFire (ubyte *data) +{ + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + int count = 0; + SKIP_HEADER (data,&count); + + int pnum=MultiGetByte (data,&count); + Player_fire_packet[pnum].wb_index=MultiGetUbyte (data,&count); + Player_fire_packet[pnum].fire_mask=MultiGetUbyte (data,&count); + Player_fire_packet[pnum].damage_scalar=MultiGetUbyte (data,&count); + + // Check to see if this player is firing a weapon he doesn't have + player *playp=&Players[pnum]; + if ((playp->weapon_flags & (1<=SECONDARY_INDEX) && (wb_index!=FLARE_INDEX) ) + { + // send reliably + //mprintf((0,"PCS: Sending fire request reliably\n")); + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,true); + } + else + #endif + { + nw_Send(&Netgame.server_address,data,count,0); + } + } +} + +// Server is processing a request for a marker +void MultiDoRequestMarker (ubyte *data) +{ + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + int count = 0; + char message[100]; + SKIP_HEADER (data,&count); + + + int pnum=MultiGetByte (data,&count); + ubyte len=MultiGetByte (data,&count); + memcpy (message,&data[count],len); + count+=len; + + int limit=2; + int cur_marker_num; + + object *pobj=&Objects[Players[pnum].objnum]; + + if (Players[pnum].num_markers>=limit) + { + // Delete the oldest marker + int found=-1; + float low_time=999999999.0f; + for (int i=0;i<=Highest_object_index;i++) + { + if (Objects[i].type==OBJ_MARKER && Objects[i].parent_handle==pobj->handle && Objects[i].creation_timeroomnum,&pobj->pos,&pobj->orient,pobj->handle); + + if (objnum>=0) + { + strcpy (MarkerMessages[cur_marker_num],message); + MultiSendObject (&Objects[objnum],0); + } +} + +// Client is asking for a marker +void MultiSendRequestForMarker (char *message) +{ + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size_offset; + size_offset=START_DATA(MP_REQUEST_MARKER,data,&count); + + MultiAddByte (Player_num,data,&count); + + ubyte len = strlen(message)+1; + MultiAddByte (len,data,&count); + memcpy (data+count,message,len); + count+=len; + + END_DATA (count,data,size_offset); + + if (Netgame.local_role==LR_SERVER) + MultiDoRequestMarker (data); + else + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count,false); +} + +// The server is telling me to adjust my position +void MultiDoAdjustPosition (ubyte *data) +{ + int count = 0; + SKIP_HEADER (data,&count); + + float timestamp=MultiGetFloat (data,&count); + + vector newpos,newvel; + + //memcpy (&newpos,&data[count],sizeof(vector)); + //count+=sizeof(vector); + newpos = MultiGetVector(data,&count); + //memcpy (&newvel,&data[count],sizeof(vector)); + //count+=sizeof(vector); + newvel = MultiGetVector(data,&count); + + int roomnum=MultiGetInt (data,&count); + + object *obj=&Objects[Players[Player_num].objnum]; + + ObjSetPos (obj,&newpos,roomnum,&obj->orient,true); + obj->mtype.phys_info.velocity=newvel; + + float save_frametime=Frametime; + int start_move=-1,end_move=-1; + + // Now go through and adjust our position based on our saved moves + for (int i=0;i=timestamp) + start_move=i; + if (end_move==-1 && SavedMoves[i].timestamp>=timestamp && SavedMoves[next].timestampmtype.phys_info.thrust; + save_rotthrust=obj->mtype.phys_info.rotthrust; + + while (!done) + { + int next=cur_move+1; + int next_end=end_move+1; + + if (next==MAX_SAVED_MOVES) + next=0; + + if (next_end==MAX_SAVED_MOVES) + next_end=0; + + float delta; + + if (next==next_end) + delta=0.1f; + else + delta=SavedMoves[next].timestamp-SavedMoves[cur_move].timestamp; + + ASSERT (delta>=0); + + Frametime=delta; + obj->mtype.phys_info.thrust=SavedMoves[cur_move].thrust; + obj->mtype.phys_info.rotthrust=SavedMoves[cur_move].rotthrust; + + + do_physics_sim(obj); + + if (cur_move==end_move) + done=1; + cur_move=next; + } + + obj->mtype.phys_info.rotthrust=save_rotthrust; + obj->mtype.phys_info.thrust=save_thrust; + Frametime=save_frametime; + + +} + +// Server is telling the client to update his position +void MultiSendAdjustPosition (int slot,float timestamp) +{ + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + int size_offset; + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + + object *obj=&Objects[Players[slot].objnum]; + + size_offset=START_DATA (MP_ADJUST_POSITION,data,&count); + MultiAddFloat (timestamp,data,&count); + + MultiAddFloat (obj->pos.x,data,&count); + MultiAddFloat (obj->pos.y,data,&count); + MultiAddFloat (obj->pos.z,data,&count); + + MultiAddFloat (obj->mtype.phys_info.velocity.x,data,&count); + MultiAddFloat (obj->mtype.phys_info.velocity.y,data,&count); + MultiAddFloat (obj->mtype.phys_info.velocity.z,data,&count); + + MultiAddInt (obj->roomnum,data,&count); + + END_DATA (count,data,size_offset); + + nw_Send (&NetPlayers[slot].addr,data,count,0); +} + + +float Last_update_time[MAX_PLAYERS]; +// Client is asking permission to move +void MultiDoRequestToMove (ubyte *data) +{ + MULTI_ASSERT_NOMESSAGE (Netgame.local_role==LR_SERVER); + + int count = 0; + SKIP_HEADER (data,&count); + + vector thrust,rotthrust,pos; + matrix orient; + + int slot=MultiGetByte (data,&count); + object *obj=&Objects[Players[slot].objnum]; + + float timestamp=MultiGetFloat (data,&count); + + if (timestampmtype.phys_info.thrust=thrust; + obj->mtype.phys_info.rotthrust=rotthrust; + + // Perform autonomous movement + float save_frametime=Frametime; + + obj->movement_type=MT_PHYSICS; + obj->mtype.phys_info.flags |=PF_USES_THRUST; + obj->mtype.phys_info.flags &=~PF_FIXED_VELOCITY; + + obj->mtype.phys_info.thrust=thrust; + obj->mtype.phys_info.rotthrust=rotthrust; + Frametime=delta_time; + + do_physics_sim(obj); + + obj->mtype.phys_info.thrust=Zero_vector; + obj->mtype.phys_info.rotthrust=Zero_vector; + obj->orient=orient; + + + bool client_error=0; + if (vm_VectorDistance (&pos,&obj->pos)>5) + client_error=true; + + if (Gametime-Last_update_time[slot]>.15) + client_error=true; + + // If there is too much client error then adjust + if (client_error) + { + mprintf ((0,"Correcting, deltatime=%f dist=%f\n",delta_time,vm_VectorDistance (&pos,&obj->pos))); + MultiSendAdjustPosition (slot,timestamp); + Last_update_time[slot]=Gametime; + } + + Frametime=save_frametime; + +} + +// Sets up a packet so that we can request to move from server +int MultiStuffRequestToMove (ubyte *data) +{ + int size_offset; + int count=0; + + object *obj=&Objects[Players[Player_num].objnum]; + + size_offset=START_DATA (MP_REQUEST_TO_MOVE,data,&count); + MultiAddByte (Player_num,data,&count); + + // Add timestamp + MultiAddFloat (Gametime,data,&count); + + MultiAddFloat (obj->pos.x,data,&count); + MultiAddFloat (obj->pos.y,data,&count); + MultiAddFloat (obj->pos.z,data,&count); + + // Do acceleration + MultiAddFloat (obj->mtype.phys_info.thrust.x,data,&count); + MultiAddFloat (obj->mtype.phys_info.thrust.y,data,&count); + MultiAddFloat (obj->mtype.phys_info.thrust.z,data,&count); + + // Do rotational acceleration + MultiAddFloat (obj->mtype.phys_info.rotthrust.x,data,&count); + MultiAddFloat (obj->mtype.phys_info.rotthrust.y,data,&count); + MultiAddFloat (obj->mtype.phys_info.rotthrust.z,data,&count); + + // Do orientation + angvec angs; + vm_ExtractAnglesFromMatrix (&angs,&obj->orient); + + MultiAddShort (angs.p,data,&count); + MultiAddShort (angs.h,data,&count); + MultiAddShort (angs.b,data,&count); + + END_DATA (count,data,size_offset); + + return count; +} + +// Server is giving us a list of objects that aren't visible +void MultiDoGenericNonVis (ubyte *data) +{ + int count = 0; + SKIP_HEADER (data,&count); + + int num=MultiGetShort (data,&count); + for (int i=0;i0) + Sound_system.Play2dSound (soundnum); + } + else + { + int soundnum=FindSoundName ("CTFLostFlag1"); + if (soundnum>0) + Sound_system.Play2dSound (soundnum); + } + + Players[pnum].rank=newrank; +} + +// Server is telling me to strip bare! +void MultiDoStripPlayer(int slot,ubyte *data) +{ + if(slot!=0) + { + //server didnt send this to us?! + Int3(); + return; + } + + int count = 0; + SKIP_HEADER (data,&count); + slot = MultiGetByte(data,&count); + if(Game_mode&GM_MULTI && Netgame.local_role!=LR_SERVER && (slot!=Player_num) ) + { + Int3(); + return; + } + + mprintf((0,"I'm stripping %d bare! (of weapons)\n",slot)); + + object *pobj = &Objects[Players[slot].objnum]; + + // take quad fire + static int quad_id = -2; + if(pobj->dynamic_wb[LASER_INDEX].flags&DWBF_QUAD || + pobj->dynamic_wb[SUPER_LASER_INDEX].flags&DWBF_QUAD) + { + pobj->dynamic_wb[LASER_INDEX].flags &= ~DWBF_QUAD; + pobj->dynamic_wb[SUPER_LASER_INDEX].flags &= ~DWBF_QUAD; + + if(quad_id==-2) + quad_id = FindObjectIDName("QuadLaser"); + + if(quad_id>-1) + { + Players[slot].inventory.Remove(OBJ_POWERUP,quad_id); + } + } + + int i; + + // remove weapons + for (i=0; i Highest_object_index || Objects[Players[slot].objnum].type == OBJ_NONE) + { + parent_handle = OBJECT_HANDLE_NONE; + }else + { + parent_handle = Objects[Players[slot].objnum].handle; + } + + //BLACKPYRO + int objnum = ObjCreate(OBJ_ROBOT, ROBOT_GUIDEBOT, Player_object->roomnum, &Player_object->pos, NULL, parent_handle); + + ASSERT(objnum!=-1); + if(objnum==-1) + return; + + // tell the client about the new buddy + MultiSendObject(&Objects[objnum],0); + + InitObjectScripts(&Objects[objnum],true); + + Buddy_handle[slot] = Objects[objnum].handle; + ObjGhostObject(objnum); + + MultiSendGhostObject(&Objects[objnum],true); + + // now update the buddy handle list of the clients + int count=0; + int size_offset; + ubyte data[MAX_GAME_DATA_SIZE]; + + mprintf ((0,"Sending Buddy_handle update to clients for Buddy#%d\n",slot)); + + size_offset=START_DATA(MP_WORLD_STATES,data,&count); + + MultiAddByte (WS_BUDDYBOTUPDATE,data,&count); + MultiAddByte (slot,data,&count); + MultiAddUshort (Buddy_handle[slot]&HANDLE_OBJNUM_MASK,data,&count); + + MultiAddByte (WS_END,data,&count); + END_DATA (count,data,size_offset); + + // Send it out + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,false); +} + +void RequestPlayerList(network_address *addr) +{ + ubyte outdata[10]; + int count=0; + int size; + + size=START_DATA(MP_REQUEST_PLAYERLIST,outdata,&count); + END_DATA (count,outdata,size); + nw_Send(addr,outdata,count,0); +} + +// a client is requesting a list of players... so give it to em! +void DoReqPlayerList(network_address *addr) +{ + ubyte outdata[((CALLSIGN_LEN+1)*MAX_NET_PLAYERS)+1]; + int count=0; + int size; + + memset(outdata,0,sizeof(outdata)); + size=START_DATA(MP_PLAYERLIST_DATA,outdata,&count); + + unsigned short icurrplayers = 0; + int i=0; + if(Dedicated_server) + { + //Skip the server player + i=1; + } + + for(;i(HEARTBEAT_INTERVAL * time_scalar)) + { + ubyte outdata[100]; + int count=0; + int size; + + size=START_DATA(MP_HEARTBEAT,outdata,&count); + END_DATA (count,outdata,size); + + if (Netgame.local_role==LR_SERVER) + { + + mprintf ((0,"Server sending heartbeat.\n")); + MultiSendToAllExcept (Player_num,outdata,count,-1); + } + else + { + mprintf ((0,"Client sending heartbeat.\n")); + nw_Send(&Netgame.server_address,outdata,count,0); + } + + + + // Reset interval + last_heartbeat=timer_GetTime(); + } +} + +//----------------------------------------------- +void MultiDoMSafeFunction(ubyte *data); +void MultiDoMSafePowerup (ubyte *data); + +// This allows us to specify under what sequences certain packets are accepted +#define ACCEPT_CONDITION(s,e) {if (sequencee) return;} +//#define ACCEPT_CONDITION(s,e) + + +// Takes the individual packet types and passes their data to the appropriate routines +void MultiProcessData (ubyte *data,int len,int slot,network_address *from_addr) +{ + ubyte type=data[0]; + len=len; + int sequence=-1; + + // HEY!!!!! These packets are the only ones that are accepted by non-connected machines + // (ie people on the netgame join screen) + // This will go away once I fill out all the ACCEPT_CONDITION types + if (!(NetPlayers[Player_num].flags & NPF_CONNECTED)) + { + if (type!=MP_CONNECTION_ACCEPTED && type!=MP_JOIN_RESPONSE && type!=MP_GAME_INFO && type!=MP_PLAYERLIST_DATA && type!=MP_ASK_FOR_URL && type!=MP_CUR_MSN_URLS) + return; + } + else + { + // Setup our sequences for accept conditions + if (Netgame.local_role==LR_SERVER) + { + if (NetPlayers[Player_num].sequence!=NETSEQ_PLAYING) + return; + if (slot==-1) + sequence=-1; + else + sequence=NetPlayers[slot].sequence; + } + else + { + sequence=NetPlayers[Player_num].sequence; + } + } + + #ifndef RELEASE + Multi_packet_tracking[type]++; + #endif + + switch (type) + { + case MP_CONNECTION_ACCEPTED: + ACCEPT_CONDITION (-1,-1); + MultiDoConnectionAccepted(data); + break; + case MP_REQUEST_PLAYERS: + MultiDoRequestPlayers (data); + break; + case MP_REQUEST_BUILDINGS: + MultiDoRequestBuildings (data); + break; + case MP_REQUEST_OBJECTS: + MultiDoRequestObjects (data); + break; + case MP_REQUEST_WORLD_STATES: + MultiDoRequestWorldStates (data); + break; + case MP_PLAYER_POS: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + NetPlayers[Player_num].total_bytes_rcvd += len; + MultiDoPlayerPos(data); + break; + case MP_DONE_PLAYERS: + MultiDoDonePlayers (data); + break; + case MP_DONE_BUILDINGS: + MultiDoDoneBuildings (data); + break; + case MP_DONE_OBJECTS: + MultiDoDoneObjects (data); + break; + case MP_DONE_WORLD_STATES: + MultiDoDoneWorldStates (data); + break; + case MP_ENTERING_GAME: + MultiDoEnteringGame(data); + break; + case MP_PLAYER: + MultiDoPlayer (data); + break; + case MP_BUILDING: + MultiDoBuilding (data); + break; + case MP_WORLD_STATES: + MultiDoWorldStates (data); + break; + case MP_JOIN_OBJECTS: + MultiDoJoinObjects (data); + break; + case MP_SEND_DEMO_OBJECT_FLAGS: + MultiDoJoinDemoObjects (data); + break; + case MP_MY_INFO: + ACCEPT_CONDITION (NETSEQ_LEVEL_START,NETSEQ_LEVEL_START); + MultiDoMyInfo (data); + break; + case MP_DISCONNECT: + ACCEPT_CONDITION (-1,255); + MultiDoDisconnect (data); + break; + case MP_PLAYER_FIRE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoFirePlayerWB (data); + break; + case MP_ASK_TO_JOIN: + ACCEPT_CONDITION (-1,-1); + MultiDoAskToJoin (data,from_addr); + break; + case MP_JOIN_RESPONSE: + ACCEPT_CONDITION (-1,-1); + MultiDoJoinResponse (data); + break; + case MP_SERVER_QUIT: + ACCEPT_CONDITION (NETSEQ_WAITING_FOR_LEVEL,255); + MultiDoServerQuit(data); + break; + case MP_LEAVE_GAME: + MultiDoLeaveGame (data); + break; + case MP_BLOWUP_BUILDING: + ACCEPT_CONDITION (NETSEQ_REQUEST_OBJECTS,255); + MultiDoBlowupBuilding (data); + break; + case MP_PLAYER_DEAD: + ACCEPT_CONDITION (NETSEQ_PLAYERS,NETSEQ_PLAYING); + MultiDoPlayerDead (data); + break; + case MP_PLAYER_ENTERED_GAME: + ACCEPT_CONDITION (NETSEQ_REQUEST_BUILDINGS,255); + MultiDoPlayerEnteredGame (data); + break; + case MP_DAMAGE_PLAYER: + ACCEPT_CONDITION (NETSEQ_PLAYERS,NETSEQ_PLAYING); + MultiDoDamagePlayer (data); + break; + case MP_MESSAGE_FROM_SERVER: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoMessageFromServer (data); + break; + case MP_MESSAGE_TO_SERVER: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoMessageToServer (data); + break; + case MP_END_PLAYER_DEATH: + MultiDoEndPlayerDeath (data); + break; + case MP_RENEW_PLAYER: + ACCEPT_CONDITION (NETSEQ_PLAYERS,NETSEQ_PLAYING); + MultiDoRenewPlayer (data); + break; + case MP_GET_GAME_INFO: + ACCEPT_CONDITION (-1,-1); + MultiDoGetGameInfo (data,from_addr); + break; + case MP_GET_PXO_GAME_INFO: + ACCEPT_CONDITION (-1,-1); + MultiDoGetPXOGameInfo (data,from_addr); + break; + case MP_GAME_INFO: + ACCEPT_CONDITION (-1,-1); + MultiDoGameInfo (data,from_addr); + break; + case MP_OBJECT: + ACCEPT_CONDITION (NETSEQ_OBJECTS,NETSEQ_PLAYING); + MultiDoObject (data); + break; + case MP_SPECIAL_PACKET: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoSpecialPacket (data); + break; + case MP_EXECUTE_DLL: + MultiDoExecuteDLL (data); + break; + case MP_REMOVE_OBJECT: + MultiDoRemoveObject (data); + break; + case MP_GUIDED_INFO: + MultiDoGuidedInfo(data); + break; + case MP_LEVEL_INFO: + ACCEPT_CONDITION (NETSEQ_WAITING_FOR_LEVEL,NETSEQ_WAITING_FOR_LEVEL); + MultiDoLevelInfo(data); + break; + case MP_READY_FOR_LEVEL: + ACCEPT_CONDITION (NETSEQ_WAITING_FOR_LEVEL,NETSEQ_WAITING_FOR_LEVEL); + MultiDoReadyForLevel(data); + break; + case MP_LEVEL_ENDED: + MultiDoLevelEnded (data); + break; + case MP_GET_GAMETIME: + ACCEPT_CONDITION (NETSEQ_NEED_GAMETIME,NETSEQ_NEED_GAMETIME); + MultiDoGameTimeReq(data,from_addr); + break; + case MP_HERE_IS_GAMETIME: + ACCEPT_CONDITION (NETSEQ_NEED_GAMETIME,NETSEQ_WAIT_GAMETIME); + MultiDoSetGameTime(data); + break; + case MP_POWERUP_REPOSITION: + ACCEPT_CONDITION (NETSEQ_OBJECTS,NETSEQ_PLAYING); + MultiDoPowerupReposition (data); + break; + case MP_ROBOT_POS: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRobotPos(data); + break; + case MP_ROBOT_FIRE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRobotFire(data); + break; + case MP_ON_OFF: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoOnOff (data); + break; + case MP_ROBOT_DAMAGE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRobotDamage(data); + break; + case MP_ROBOT_EXPLODE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRobotExplode(data); + break; + case MP_ADDITIONAL_DAMAGE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoAdditionalDamage (data); + break; + case MP_ANIM_UPDATE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoObjAnimUpdate(data); + break; + case MP_PLAY_3D_SOUND_FROM_OBJ: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoPlay3dSound(data); + break; + case MP_ROBOT_FIRE_SOUND: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRobotFireSound(data); + break; + case MP_TURRET_UPDATE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoTurretUpdate(data); + break; + case MP_CLIENT_USE_INVENTORY_ITEM: + MultiDoClientInventoryUseItem(slot,data); + break; + case MP_REMOVE_INVENTORY_ITEM: + MultiDoClientInventoryRemoveItem(slot,data); + break; + case MP_REQUEST_COUNTERMEASURE: + MultiDoRequestCountermeasure (data); + break; + case MP_SERVER_SENT_COUNT: + MultiDoBytesSent(data); + break; + case MP_CLIENT_SET_PPS: + MultiDoPPSSet(data,slot); + break; + case MP_GREETINGS_FROM_CLIENT: + MultiDoGreetings(data,from_addr); + break; + case MP_REQUEST_TO_OBSERVE: + MultiDoRequestToObserve (data); + break; + case MP_OBSERVER_CHANGE: + MultiDoObserverChange (data); + break; + case MP_VISIBLE_PLAYERS: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoVisiblePlayers (data); + break; + case MP_FILE_REQ: + MultiDoFileReq(data); + break; + case MP_FILE_DENIED: + MultiDoFileDenied(data); + break; + case MP_FILE_DATA: + MultiDoFileData(data); + break; + case MP_FILE_ACK: + MultiDoFileAck(data); + break; + case MP_FILE_CANCEL: + MultiDoFileCancelled(data); + break; + case MP_PLAYER_CUSTOM_DATA: + MultiDoCustomPlayerData(data); + break; + case MP_GHOST_OBJECT: + MultiDoGhostObject(data); + break; + case MP_PLAYER_PING: + MultiDoPing(data,from_addr); + break; + case MP_PLAYER_PONG: + MultiDoPong(data); + break; + case MP_PLAYER_LAG_INFO: + MultiDoLagInfo(data); + break; + case MP_REQUEST_SHIELDS: + MultiDoRequestShields (data); + break; + case MP_REQUEST_DAMAGE: + MultiDoRequestDamage (data); + break; + case MP_ATTACH_OBJ: + MultiDoAttach(data); + break; + case MP_UNATTACH_OBJ: + MultiDoUnattach(data); + break; + case MP_AIWEAP_FLAGS: + MultiDoAiWeaponFlags(data); + break; + case MP_ATTACH_RAD_OBJ: + MultiDoAttachRad(data); + break; + case MP_TIMEOUT_WEAPON: + MultiDoReleaseTimeoutMissile (data); + break; + case MP_WEAPONS_LOAD: + MultiDoWeaponsLoad (data); + break; + case MP_REQUEST_PEER_DAMAGE: + MultiDoRequestPeerDamage (data,from_addr); + break; + case MP_MSAFE_FUNCTION: + MultiDoMSafeFunction (data); + break; + case MP_MSAFE_POWERUP: + MultiDoMSafePowerup (data); + break; + case MP_ASK_FOR_URL: + msn_DoAskForURL(data,from_addr); + break; + case MP_CUR_MSN_URLS: + msn_DoCurrMsnURLs(data,from_addr); + break; + case MP_REQUEST_TO_FIRE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRequestToFire (data); + break; + case MP_PERMISSION_TO_FIRE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoPermissionToFire(data); + break; + case MP_CONNECT_BAIL: + MultiDoConnectBail(); + break; + case MP_CLIENT_PLAY_TAUNT: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRequestPlayTaunt(data); + break; + case MP_SERVER_PLAY_TAUNT: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoPlayTaunt(data); + break; + case MP_REQUEST_MARKER: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRequestMarker(data); + break; + case MP_REQUEST_TYPE_ICON: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRequestTypeIcon(data); + break; + case MP_SEND_TYPE_ICON: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoTypeIcon(data); + break; + case MP_REQUEST_TO_MOVE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoRequestToMove (data); + break; + case MP_ADJUST_POSITION: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoAdjustPosition (data); + break; + case MP_GENERIC_NONVIS: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoGenericNonVis (data); + break; + case MP_GUIDEBOTMENU_REQUEST: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoGuidebotMenuRequest(data,slot); + break; + case MP_GUIDEBOTMENU_DATA: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoGuidebotMenuData(data); + break; + case MP_BREAK_GLASS: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoBreakGlass (data); + break; + case MP_THIEF_STEAL: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoThiefSteal(data); + break; + case MP_REQUEST_PLAYERLIST: + ACCEPT_CONDITION (-1,-1); + DoReqPlayerList(from_addr); + break; + case MP_PLAYERLIST_DATA: + ACCEPT_CONDITION (-1,-1); + DoPlayerListData(data,len); + break; + case MP_SERVER_AUDIOTAUNT_TIME: + MultiDoAudioTauntTime(data); + break; + case MP_CHANGE_RANK: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoChangeRank (data); + break; + case MP_BASHPLAYER_SHIP: + ACCEPT_CONDITION (NETSEQ_LEVEL_START,NETSEQ_PLAYING); + MultiDoBashPlayerShip(data); + break; + case MP_HEARTBEAT: + if (Netgame.local_role==LR_CLIENT) + { + Got_heartbeat=true; + mprintf ((0,"Got heartbeat from server.\n")); + } + else + mprintf ((0,"Got heartbeat from slot %d.\n",slot)); + break; + case MP_INITIAL_RANK: + MultiDoInitialRank(data); + break; + case MP_MISSILE_RELEASE: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoMissileRelease(slot,data); + break; + case MP_STRIP_PLAYER: + ACCEPT_CONDITION (NETSEQ_PLAYING,NETSEQ_PLAYING); + MultiDoStripPlayer(slot,data); + break; + case MP_REJECTED_CHECKSUM: + ACCEPT_CONDITION (-1,-1); + MultiDoServerRejectedChecksum(data); + default: + mprintf ((0,"Invalid packet type %d!\n",type)); + Int3(); // Invalid packet type!!!! + break; + } +} + + + +// Takes a bunch of messages, check them for validity, +// and pass them to multi_process_data. +void MultiProcessBigData(ubyte *buf, int len,network_address *from_addr) +{ + int type, bytes_processed = 0; + short sub_len; + int slot=0; + int last_type=-1,last_len=-1; + + // Update timer stuff so we don't disconnect due to timeout + if (Netgame.local_role==LR_CLIENT) + { + Netgame.last_server_time=timer_GetTime(); + } + else + { + slot=MultiMatchPlayerToAddress (from_addr); + if (slot>=0) + { + //This is to prevent clients who have disconnected on the client side + //but not the server side from being credited with a recent packet + //just because they are looking for a game + if( (buf[0]!=MP_GET_PXO_GAME_INFO) && (buf[0]!=MP_GET_GAME_INFO) ) + { + NetPlayers[slot].last_packet_time=timer_GetTime (); + } + else + { + //Someone who we think is still connected is looking for game info! + //We aren't going to process this packet because otherwise they + //might try to join which wouldn't work and they would be credited + //with sending a packet and the server wouldn't disconnect them. + mprintf((0,"Ignoring game info request from a currently connected user (%s)\n",Players[slot].callsign)); + return; + } + } + } + + while( bytes_processed < len ) + { + type = buf[bytes_processed]; + sub_len= INTEL_SHORT((*(short *)(buf+bytes_processed+1))); + + if (sub_len<3 || type==0 || (len-bytes_processed)<2) + { + mprintf ((0,"Got a corrupted packet!\n")); + Int3(); + return; // just throw the rest out + } + + if ( (bytes_processed+sub_len) > len ) + { + mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len )); + Int3(); + return; + } + + MultiProcessData (&buf[bytes_processed],sub_len,slot,from_addr); + bytes_processed += sub_len; + last_type=type; + last_len=sub_len; + } +} + diff --git a/Descent3/multi.h b/Descent3/multi.h new file mode 100644 index 000000000..a157d4dc6 --- /dev/null +++ b/Descent3/multi.h @@ -0,0 +1,1231 @@ +/* + * $Logfile: /DescentIII/Main/multi.h $ + * $Revision: 172 $ + * $Date: 7/09/01 2:06p $ + * $Author: Matt $ + * + * Header for multiplayer + * + * $Log: /DescentIII/Main/multi.h $ + * + * 172 7/09/01 2:06p Matt + * Channged multiplayer version to 10 + * + * 171 5/09/00 5:11p Jeff + * fixed struct packing bug + * + * 170 4/19/00 5:35p Matt + * Changed multiplayer version to 9 + * + * 169 10/25/99 5:22p Jeff + * upped multiplayer version for merc/1.3 + * + * 168 10/16/99 8:45p Jeff + * created multi functions to strip a player of his weapons and energy + * + * 167 10/01/99 1:39p Kevin + * new MULTI_VERSION for demo (macintosh) + * + * 166 9/09/99 12:24p Kevin + * Fixed a bug that was causing problems in the Mac version + * (Game_is_master_tracker_game was defined as a ubyte in the game, but an + * int in the dll) + * + * 165 9/02/99 3:34p Jason + * send secondary fire reliable in a C/S game + * + * 164 9/01/99 6:56p Jason + * fixed guided missiles and timeout missiles so they work in multiplayer + * + * 163 9/01/99 4:12p Kevin + * Byte ordering fixes for the macintosh + * + * 162 7/26/99 2:11p Kevin + * 1.1 patch multiplayer version change + * + * 161 6/03/99 8:48a Kevin + * fixes for new OEM version.... + * + * 160 5/23/99 3:04a Jason + * fixed bug with player rankings not being updated correctly + * + * 159 5/20/99 4:54p Jason + * added heartbeats to server + * + * 158 5/20/99 4:10p Jason + * added heartbeat to multiplayer so clients wouldn't time out, also + * various multiplayer fixes + * + * 157 5/19/99 5:39p Jason + * made level failing work in coop + * + * 156 5/11/99 10:59a Kevin + * Ship allow/dissalow works now! + * + * 155 5/03/99 2:36p Jason + * change for multiplayer games + * + * 154 5/02/99 5:01p Kevin + * changed MAX_RESPAWNS per Jason + * + * 153 4/29/99 11:46p Jason + * added ability to set the next level in a multiplayer game + * + * 152 4/29/99 11:02p Jeff + * added the ability for the server to set audio taunt delay time via + * command line option and/or dedicated server console + * + * 151 4/28/99 2:28a Jeff + * finished (hopefully) making guidebot multiplayer friendly + * + * 150 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 149 4/24/99 11:58p Kevin + * Game info list (hit I in the pxo game list) + * + * 148 4/24/99 6:44p Jeff + * added functions for theif so he can steal things other than weapons + * + * 147 4/17/99 4:24p Kevin + * Changed multiplayer version number for Demo & regular + * + * 146 4/16/99 12:03a Jeff + * removed #pragma's for Linux + * + * 145 4/06/99 6:24p Jason + * various fixes for multiplayer + * + * 144 4/03/99 2:06p Kevin + * Changed last file to audio taunt #4. + * + * 143 4/02/99 6:31p Jason + * fixed multiplayer powerup/player start bugs + * + * 142 4/01/99 5:48p Kevin + * Put in code to make it easy to add the guidebot to multiplayer (coop) + * games + * + * 141 3/17/99 4:08p Kevin + * Changed the way games appear and timeout in the game list. + * + * 140 3/11/99 6:30p Jeff + * numerous fixes to demo system in multiplayer games (when + * recording/playback a demo in a multiplayer game) + * + * 139 3/02/99 5:50p Kevin + * Ouch. Duplicate structures existed and were conflicting. + * + * 138 2/25/99 11:01a Matt + * Added new explosion system. + * + * 137 2/25/99 10:30a Jason + * added nonvis generic/robot system + * + * 136 2/13/99 12:35a Jeff + * fixed up packets to handle new inventory system (removed some compiler + * warnings) + * + * 135 2/12/99 3:38p Jason + * added client-side interpolation...its not fully debugged though. + * + * 134 2/09/99 6:52p Jeff + * implemented 'typing inidcator' in multiplayer...players that are typing + * messages have an icon on them + * + * 133 2/04/99 12:26p Jason + * added spew that was better for multiplayer + * + * 132 2/03/99 12:15p Jason + * added multiplayer vis optimizations + * + * 131 1/28/99 6:17p Jason + * added markers + * + * 130 1/27/99 5:47p Jeff + * audio taunts implemented! + * + * 129 1/22/99 4:06p Jeff + * added hud messages that can be sent to just teammates or individual + * people + * + * 128 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 127 1/13/99 2:53p Jason + * added CONNECT_BAIL packet type + * + * 126 1/13/99 6:38a Jeff + * fixed object.h. There were numerous struct declarations that were the + * same name as the instance of the struct (gcc doesn't like this). + * Changed the struct name. Also added some #ifdef's for linux build, + * along with fixing case-sensitive includes + * + * 125 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 124 1/05/99 5:09p Jason + * added permissable server networking (ala Quake/Unreal) to Descent3 + * + * 123 1/05/99 11:52a Jason + * added more msafe functionality + * + * 122 1/04/99 8:11p Jason + * fixed packet loss tracking problem + * + * 121 12/31/98 7:36p Jeff + * correctly hash type/ids for multisafe. Added some new inline functions + * to add/get type/id to a packet + * + * 120 12/28/98 2:22p Kevin + * Initial mission downloading system + * + * 119 12/21/98 4:04p Jason + * first pass at multisafe powerups + * + * 118 12/16/98 5:37p Jason + * added new multisafe architecture + * + * 117 12/15/98 4:20p Jason + * added triggers and door funtions to multisafe list + * + * 116 12/14/98 5:32p Jason + * added multisafe functions + * + * 115 12/14/98 10:53a Jason + * added bright player ships option + * + * 114 12/10/98 10:51a Jason + * added autolag + * + * 113 12/07/98 3:02p Jason + * added multi_logo_state + * + * 112 12/03/98 3:39p Jason + * added peer 2 peer style damage + * + * 111 12/02/98 10:30a Jason + * added additional damage types for client-side multiplayer + * + * 110 12/01/98 5:48p Jeff + * added pilot picture id to netplayer struct + * + * 109 12/01/98 12:47p Jason + * got rid of NF_DROPMISORDERED and added NF_USE_SMOOTHING + * + * 108 11/19/98 11:27a Jason + * added Multi_accept_state for Jeff + * + * 107 11/16/98 4:47p Jason + * changes for multiplayer (weapons load sent and deleting destroyed + * lightmapped objects) + * + * 106 11/13/98 4:25p Jason + * changes for better weapon effects + * + * 105 11/11/98 12:11p Chris + * The attach system and weapon firing (continous and spray) are now + * network friendly + * + * 104 11/10/98 4:29p Kevin + * Added attach code for chris + * + * 103 11/10/98 11:16a Jason + * fixed some multiplayer bugs with powerups disappearing + * + * + * 102 11/06/98 5:43p Jason + * made pings sent not reliably + * + * 101 11/03/98 6:14p Chris + * Starting to make on/off and spray weapons accessable to robots + * + * 100 10/28/98 11:51a Jason + * fixed some multiplayer problems, plus redid coop a bit to make it + * cleaner + * + * 99 10/27/98 10:19a Jason + * changes for new architecture + * + * 98 10/23/98 1:08p Kevin + * changed multi version + * + * 97 10/19/98 7:19p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 96 10/19/98 2:48p Kevin + * Added accurate weapon thingy for Chris + * + * 95 10/08/98 3:38p Jeff + * removed time_left from Netgame + * + * 94 10/07/98 1:07p Jason + * added more safety precautions + * + * 93 10/05/98 7:23p Jason + * added protective layer onto multiplayer + * + * 92 10/01/98 12:17p Kevin + * Fixed Ping for Peer-Peer games + * + * 91 10/01/98 11:29a Jason + * changes for world states in multiplayer games + * + * 90 9/30/98 5:35p Jason + * added multiplayer menu bailing for samir + * + * 89 9/15/98 12:42p Jason + * got dedicated server actually working + * + * 88 9/14/98 4:17p Jason + * added friendly fire damage + * + * 87 9/11/98 12:26p Jason + * got energy to shield and fusion damage working in multiplayer + * + * 86 9/04/98 3:45p Kevin + * Added ping_time to Netplayers struct. It's updated in peer-peer and + * client server + * + * 85 8/13/98 6:32p Kevin + * Initial implementation of directplay API + * + * 84 8/12/98 6:36p Jeff + * converted the send link/unlink functions to handle ghosted objects. + * Updated MultiSendObject to handle dummy objects + * + * 83 8/12/98 1:17p Jeff + * added link/unlink object packets + * + * 82 8/07/98 12:27p Kevin + * Added network stuff for energy/shield conversion + * + * 81 8/04/98 6:13p Kevin + * fixes for sound & bmp exchange + * + * 80 8/04/98 3:07p Kevin + * sound & bmp exchange fixes for > 2 players + * + * 79 8/04/98 10:26a Kevin + * Custom sound and bmp exchange system + * + * 78 8/03/98 5:56p Jason + * got fusion cannon working correctly + * + * 77 7/29/98 5:39p Kevin + * sound/bitmap exchange + * + * 76 7/29/98 12:40p Jason + * more multiplayer testing for occlusion and packet sequencing + * + * 75 7/27/98 5:31p Kevin + * Sound/Bitmap exchange system + * + * 74 7/24/98 9:37a Kevin + * Added some new MP_ types + * + * 73 7/23/98 12:38p Jason + * more tweaks for multiplayer vis stuff + * + * 72 7/22/98 3:16p Jason + * added observer mode + * + * 71 7/20/98 6:20p Kevin + * peer-peer stuff + * + * 70 7/17/98 1:22p Kevin + * Dynamic retransmission of reliable packets and stuff + * + * 69 7/16/98 12:51p Jason + * added a way for DLLs to bail out if something is wrong + * + * 68 7/14/98 5:52p Kevin + * Packet loss measurements and auto pps adjusting + * + * 67 7/13/98 11:52a Kevin + * Added Callscriptwithparms + * + * 66 7/08/98 6:01p Jeff + * added packet to remove an item from inventory + * + * 65 7/08/98 12:01p Kevin + * weapon battery info + * + * 64 7/07/98 7:33p Jeff + * changes made for inventory use + * + * 63 7/07/98 3:16p Kevin + * Added inventory parms + * + * 62 7/07/98 11:19a Kevin + * Added inventory use message + * + * 61 7/07/98 10:10a Kevin + * Added basic turret support for coop + * + * 60 7/06/98 6:46p Kevin + * vector parameter passing in scripts + * + * 59 7/06/98 5:36p Kevin + * Variable parameter passing + * + * 58 7/06/98 11:51a Jason + * added accessor function for countermeasures + * + * 57 7/02/98 12:57p Jason + * various changes for multiplayer positional sequencing + * + * 56 7/02/98 10:39a Kevin + * More coop stuff + * + * 55 7/01/98 6:30p Kevin + * More coop + * + * 54 7/01/98 12:55p Jason + * more changes for countermeasures + * + * 53 7/01/98 12:11p Jason + * added countermeasures + * + * 52 6/30/98 7:17p Kevin + * more animation stuff + * + * 51 6/30/98 5:08p Kevin + * Animation frame stuff + * + * 50 6/30/98 3:58p Chris + * + * 49 6/30/98 11:39a Jason + * added AdditionalDamage packet type for multiplayer + * + * 48 6/29/98 3:08p Jason + * added on/off weapons + * + * 47 6/29/98 12:49p Jason + * temp checkin for on/off weapons + * + * 46 6/29/98 12:12p Kevin + * Added robot damage and death packets + * + * 45 6/26/98 6:53p Kevin + * Coop mode + * + * 44 6/26/98 6:20p Jason + * changes for coop + * + * 43 6/25/98 5:22p Kevin + * Req/Send gametime to clients + * + * 42 6/25/98 12:32p Jason + * added new multiplayer functionality + * + * 41 6/24/98 3:24p Kevin + * Updated PXO screens with chat, etc. + * + * 40 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 39 5/19/98 6:27p Jason + * put in urgent packets + * + * 38 5/14/98 11:07a Kevin + * Made gameover packet to the tracker reliable + * + * 37 5/12/98 5:12p Jason + * added dll callable level ending + * + * 36 5/12/98 4:18p Jason + * added better level sequencing for multiplayer + * + * 35 5/12/98 12:33p Jason + * got level sequencing working in multiplayer + * + * 34 5/04/98 10:55a Kevin + * Mastertracker fixes/enhancements + * + * 33 4/30/98 3:50p Kevin + * Mastertracker pilot stats + * + * 32 4/28/98 2:58p Kevin + * Added mastertracker flags + * + * 31 4/24/98 3:50p Kevin + * Added mastertracker game tracking support + * + * 30 4/20/98 12:46p Jason + * added level checksumming for multiplayer games + * + * 29 4/17/98 1:59p Jason + * added cool object effects + * + * 28 4/14/98 7:56p Matt + * Moved MSN_NAMELEN from mission.h to descent,h, so multi.h didn't need + * to include mission.h. + * + */ + +#ifndef MULTI_H +#define MULTI_H + +#include "pstypes.h" +#include "vecmat_external.h" +#include "object_external_struct.h" +#include "object_external.h" +#include "player_external.h" + + +#if defined(__LINUX__) +#include "linux/linux_fix.h" +#endif + + +#include "multi_external.h" //defines and structs are in here + +extern bool Multi_bail_ui_menu; +#if defined(DEMO) +#define MULTI_VERSION 7 +#elif defined(OEM) +#define MULTI_VERSION 5 +#else +//#define MULTI_VERSION 4 +//Patch 1.1! +//#define MULTI_VERSION 6 +//Patch 1.3 +#define MULTI_VERSION 10 +#endif + +#define MULTI_PING_INTERVAL 3 + +// Multiplayer packets +#define MP_CONNECTION_ACCEPTED 1 // Server says we can join +#define MP_OBJECT 2 // Object packet from the server +#define MP_PLAYER 3 // Name packet from the server +#define MP_ALL_SET 4 // Client is ready to play! +#define MP_PLAYER_POS 5 // Player position packet +#define MP_REQUEST_PLAYERS 6 // Clients wants to know about players +#define MP_MY_INFO 7 // Stats for local client +#define MP_DONE_PLAYERS 8 // Done requesting players +#define MP_ENTERING_GAME 9 // I'm entering the game +#define MP_DISCONNECT 10 // A player has disconnected +#define MP_PLAYER_FIRE 11 // A player is firing +#define MP_ASK_TO_JOIN 12 // Asking if its ok to join +#define MP_JOIN_RESPONSE 13 // Answering MP_ASK_TO_JOIN +#define MP_SERVER_QUIT 14 // Server is quitting +#define MP_LEAVE_GAME 15 // A client is leaving the game +#define MP_BLOWUP_BUILDING 16 // A building is blowing up +#define MP_DONE_BUILDINGS 17 // Server is done sending buildings +#define MP_REQUEST_BUILDINGS 18 // Requesting buildings +#define MP_BUILDING 19 // Server is telling us about buildings +#define MP_DONE_OBJECTS 20 // Server is done sending buildings +#define MP_REQUEST_OBJECTS 21 // Requesting buildings +#define MP_JOIN_OBJECTS 22 // Server is telling us about buildings +#define MP_PLAYER_DEAD 23 // Server says someone is dead! +#define MP_PLAYER_RENEW 24 // A player is coming back from the dead! +#define MP_PLAYER_ENTERED_GAME 25 // A player is entering the game +#define MP_DAMAGE_PLAYER 26 // A player should take damage +#define MP_MESSAGE_FROM_SERVER 27 // A text message from the server +#define MP_END_PLAYER_DEATH 28 // A player is done dying +#define MP_RENEW_PLAYER 29 // Renew a player (a new ship!) +#define MP_GET_GAME_INFO 30 // Someone is asking about our game +#define MP_GAME_INFO 31 // Server is telling us about its game +//@@#define MP_EXECUTE_SCRIPT 32 // Server says to execute a script +#define MP_MESSAGE_TO_SERVER 33 // A message from the client to the server +#define MP_SPECIAL_PACKET 34 // a special data packet for scripts +#define MP_EXECUTE_DLL 35 // Server says to execute a dll +#define MP_REMOVE_OBJECT 36 // Server says to remove an object +#define MP_GUIDED_INFO 37 // Guided missile info from a player +#define MP_LEVEL_ENDED 38 // Server says the level has ended +#define MP_READY_FOR_LEVEL 39 // Client wants info about the level +#define MP_LEVEL_INFO 40 // Server is telling client about the level +#define MP_GET_PXO_GAME_INFO 41 // Same as MP_GET_GAME_INFO but for PXO games only +#define MP_POWERUP_REPOSITION 42 // The server is telling the client to move a powerup +#define MP_GET_GAMETIME 43 // Client equesting the server's gametime +#define MP_HERE_IS_GAMETIME 44 // Server's gametime response +#define MP_ROBOT_POS 45 // Robot position and orientation +#define MP_ROBOT_FIRE 46 // A robot is firing +#define MP_ROBOT_DAMAGE 47 // Apply damage to robot +#define MP_ROBOT_EXPLODE 48 // Blow up robot +#define MP_ON_OFF 49 // a player is starting or stopping an on/off weapon +#define MP_ADDITIONAL_DAMAGE 50 // Server is telling us to add or subtract shields +#define MP_ANIM_UPDATE 51 // Server is sending an animation update packet +#define MP_REQUEST_COUNTERMEASURE 52 // Client is asking the server to create a countermeasure +#define MP_CREATE_COUNTERMEASURE 53 // Server is telling us to create a countermeasure +#define MP_PLAY_3D_SOUND_FROM_OBJ 54 // Server sending a 3d sound based on an obj position +#define MP_PLAY_3D_SOUND_FROM_POS 55 // Server sending a 3d sound based on an arbitrary position +#define MP_ROBOT_FIRE_SOUND 56 // The packet type which makes the firing sound from the robot +//@@#define MP_EXECUTE_SCRIPT_WITH_PARMS 57 // Execute Script with parameters +#define MP_TURRET_UPDATE 58 // Update on turret info from the server +#define MP_CLIENT_USE_INVENTORY_ITEM 59 // Client is telling the server that he want's to use an item from his inventory +#define MP_REMOVE_INVENTORY_ITEM 60 // Server is telling the clients to remove an item from a player's inventory +#define MP_SERVER_SENT_COUNT 61 // The server is telling the client how much data he has sent +#define MP_CLIENT_SET_PPS 62 // The client is telling the server what pps to use +#define MP_GREETINGS_FROM_CLIENT 63 // The client is identifying himself so the server knows his ip +#define MP_REQUEST_TO_OBSERVE 64 // A player is requesting to enter observe mode +#define MP_OBSERVER_CHANGE 65 // The server is telling us about an observer change +#define MP_VISIBLE_PLAYERS 66 // Server is telling us what players are visible +#define MP_FILE_REQ 67 // Request a sound or bmp for a particular player slot +#define MP_FILE_DENIED 68 // The server isn't going to send you a file you asked for (no soup for you) +#define MP_FILE_DATA 69 // Data chunk, which is part of a file xfer +#define MP_FILE_ACK 70 // When you receive a chunk of data, reply with this ACK and the sender will send the next chunk +#define MP_SERVER_ECHO_REQ 71 // Special packet the server sends to an echo server (on the mastertracker) which responds with his IP address and port. It makes sure servers behind firewalls/NAT/proxy work +#define MP_SERVER_ECHO_RSP 72 // Response from Echo server with our real IP and port +#define MP_FILE_CANCEL 73 // Cancel an existing file transfer (client or server/Sender or receiver) +#define MP_INVISIBLE_PLAYER 74 // Just a packeting test +#define MP_PLAYER_CUSTOM_DATA 75 // This player has custom data. Here are the file names +#define MP_ABORT_JOIN_SERVER 76 // Stop trying to join a server because the level is ending or server quiting +#define MP_ENERGY_SHIELD_CONV_ON_OFF 77 // Client wants to do an energy to shield conversion +#define MP_GHOST_OBJECT 78 //Tells clients to ghost or unghost an object +#define MP_PLAYER_PING 79 // Ask for a ping +#define MP_PLAYER_PONG 80 // Response to a ping. +#define MP_PLAYER_LAG_INFO 81 // Tell clients in a client/server game what the ping of another player is. +#define MP_REQUEST_DAMAGE 82 // We're asking the server to damage us +#define MP_REQUEST_SHIELDS 83 // We're asking the server to give us shields +#define MP_REQUEST_WORLD_STATES 84 // Clients wants to know about the world state +#define MP_DONE_WORLD_STATES 85 // Server says we're done with world states +#define MP_WORLD_STATES 86 // Information about the world +#define MP_ATTACH_OBJ 87 // Attach an object with the attach system +#define MP_UNATTACH_OBJ 88 // UnAttach an object with the attach system +#define MP_AIWEAP_FLAGS 89 // Allows robots to have continous and spray weapons +#define MP_ATTACH_RAD_OBJ 90 // Attach by rad +#define MP_TIMEOUT_WEAPON 91 // Timeout weapon +#define MP_WEAPONS_LOAD 92 // Client is telling server about weapons load +#define MP_REQUEST_PEER_DAMAGE 93 // Client is asking another player to damage him +#define MP_MSAFE_FUNCTION 94 // Msafe function +#define MP_MSAFE_POWERUP 95 // Multisafe powerup +#define MP_ASK_FOR_URL 96 //Ask for a url list from the server +#define MP_CUR_MSN_URLS 97 //Response from the server listing the URLs for the current mission +#define MP_REQUEST_TO_FIRE 98 //Client is requesting to fire +#define MP_PERMISSION_TO_FIRE 99 //Permissable firing packet +#define MP_CONNECT_BAIL 100 // Server says to bail on connecting +#define MP_CLIENT_PLAY_TAUNT 101 // Client is requesting to play audio taunt +#define MP_SERVER_PLAY_TAUNT 102 // Server is telling clients to play a player's audio taunt +#define MP_REQUEST_MARKER 103 // Client is asking for a marker to be dropped +#define MP_REQUEST_TYPE_ICON 104 // Client is telling server that he is typing a message +#define MP_SEND_TYPE_ICON 105 // Server is telling clients that a player is typing a message (to display icon on hud) +#define MP_REQUEST_TO_MOVE 106 // Client is requesting to move +#define MP_ADJUST_POSITION 107 // Server is telling me my new position +#define MP_GENERIC_NONVIS 108 // Server says I can't see this generic object +#define MP_SEND_DEMO_OBJECT_FLAGS 109 // Server is sending what join objects have the OF_CLIENTDEMOOBJECT set +#define MP_GUIDEBOTMENU_REQUEST 110 //Client is requesting either the guidebot menu, or one of the items on the menu +#define MP_GUIDEBOTMENU_DATA 111 //Server is sending the guidebot text to display in the menu +#define MP_BREAK_GLASS 112 // Server is telling us to break some glass +#define MP_THIEF_STEAL 113 // Server is telling a client that the thief stole an item from him +#define MP_REQUEST_PLAYERLIST 114 // Client is requesting a list of players for this game +#define MP_PLAYERLIST_DATA 115 // Server is sending a list of players in this game +#define MP_SERVER_AUDIOTAUNT_TIME 116 +#define MP_CHANGE_RANK 117 // Server says a player has changed rank +#define MP_BASHPLAYER_SHIP 118 // Server says we need to use a different ship +#define MP_HEARTBEAT 119 // A blank packet that says we're still alive (for loading levels) +#define MP_INITIAL_RANK 120 // Server is telling me about rank +#define MP_MISSILE_RELEASE 121 // Informing about a guided missile being released from guided mode +#define MP_STRIP_PLAYER 122 // Strips player of all weapons (but laser) and reduces energy to 0 +#define MP_REJECTED_CHECKSUM 123 // The server rejected the client checksum. This lets the client know. + +// Shield request defines +#define MAX_SHIELD_REQUEST_TYPES 1 +#define SHIELD_REQUEST_ENERGY_TO_SHIELD 0 + + +#define SERVER_PLAYER 128 + +#define MASTER_TRACKER_SIG 0xf429b142 + +// values for fired_on_this_frame +#define PFP_NO_FIRED 0 // the player didn't fire at all this frame +#define PFP_FIRED 1 // the player fired this frame and the info should be packed in a player pos flag +#define PFP_FIRED_RELIABLE 2 // used for secondaries of a non-peer to peer game +typedef struct +{ + ubyte fired_on_this_frame; + ubyte wb_index; + ubyte fire_mask; + ubyte damage_scalar; + ubyte reliable; + int dest_roomnum; +} player_fire_packet; + +extern netgame_info Netgame; + + +extern ushort Local_object_list[]; +extern ushort Server_object_list[]; +extern ushort Server_spew_list[]; + +#define MAX_RECEIVE_SIZE 4096 +#define MAX_NETWORK_GAMES 100 + +// The size of the packet_number and guaranteed fields of game_packet_data: +#define HEADER_INFO_SIZE 5 + +#define NETFILE_NONE 0 // No file transfer in progress +#define NETFILE_SENDING 1 // Sending a file to someone +#define NETFILE_RECEIVING 2 // Receiving a file from someone +#define NETFILE_ASKING 3 // Waiting for a response as to if we can get a file + +#define NETFILE_ID_NOFILE 0 // No file at all (for sequencing) +#define NETFILE_ID_SHIP_TEX 1 // Custom ship texture +#define NETFILE_ID_VOICE_TAUNT1 2 // Voice taunt #1 +#define NETFILE_ID_VOICE_TAUNT2 3 // Voice taunt #2 +#define NETFILE_ID_VOICE_TAUNT3 4 // Voice taunt #3 +#define NETFILE_ID_VOICE_TAUNT4 5 // Voice taunt #4 +#define NETFILE_ID_DONE 99 // Done transferring files + +//This sets the last file we look for in the sequence above +#define NETFILE_ID_LAST_FILE NETFILE_ID_VOICE_TAUNT4 + +// A semi-compressed orientation matrix for multiplayer games +typedef struct +{ + short multi_matrix[9]; +} multi_orientation; + +inline void MultiMatrixMakeEndianFriendly(multi_orientation *mmat) +{ + for(int i=0;i<9;i++) + { + mmat->multi_matrix[i] = INTEL_SHORT(mmat->multi_matrix[i]); + } +} + +// For firing players +extern player_fire_packet Player_fire_packet[MAX_NET_PLAYERS]; + +// For powerup respawning +#define MAX_RESPAWNS 300 +#define RESPAWN_TIME 60 // seconds until a powerup respawns + +#define MULTI_SEND_MESSAGE_ALL -1 +#define MULTI_SEND_MESSAGE_RED_TEAM -2 +#define MULTI_SEND_MESSAGE_BLUE_TEAM -3 +#define MULTI_SEND_MESSAGE_GREEN_TEAM -4 +#define MULTI_SEND_MESSAGE_YELLOW_TEAM -5 + +typedef struct +{ + vector pos; + int objnum; + int roomnum; + ubyte used; + short original_id; +} powerup_respawn; + +typedef struct +{ + int id; + float respawn_time; +} powerup_timer; + +extern powerup_timer Powerup_timer[]; +extern powerup_respawn Powerup_respawn[]; +extern network_game Network_games[]; +extern netplayer NetPlayers[MAX_NET_PLAYERS]; +extern ubyte Multi_receive_buffer[MAX_RECEIVE_SIZE]; +extern int Ok_to_join; +extern int Num_powerup_respawn; +extern int Num_powerup_timer; +extern int Multi_next_level; + +// Heartbeat flag +extern bool Got_heartbeat; + + +// This is for breakable glass +#define MAX_BROKE_GLASS 100 +extern ushort Broke_glass_rooms[],Broke_glass_faces[]; +extern int Num_broke_glass; + +// For keeping track of damage and shields +extern float Multi_additional_damage[]; +extern int Multi_requested_damage_type; +extern float Multi_requested_damage_amount; +extern float Multi_additional_shields[]; + +extern ubyte Multi_send_buffer[MAX_NET_PLAYERS][MAX_GAME_DATA_SIZE]; +extern int Multi_send_size[MAX_NET_PLAYERS]; +extern float Multi_last_sent_time[MAX_NET_PLAYERS][MAX_NET_PLAYERS]; +extern int Multi_additional_damage_type[MAX_NET_PLAYERS]; + +extern ubyte Multi_reliable_urgent[MAX_NET_PLAYERS]; +extern ubyte Multi_reliable_send_buffer[MAX_NET_PLAYERS][MAX_GAME_DATA_SIZE]; +extern int Multi_reliable_send_size[MAX_NET_PLAYERS]; +extern float Multi_reliable_last_send_time[MAX_NET_PLAYERS]; +extern ubyte Multi_reliable_sent_position[MAX_NET_PLAYERS]; +extern uint Multi_visible_players[]; + +extern int Got_level_info; +extern int Got_new_game_time; +// For keeping track of buildings that have changed +extern ubyte Multi_building_states[]; +extern ushort Multi_num_buildings_changed; + +extern bool Multi_logo_state; + +// For searching out netgames +extern int Num_network_games_known; + +// Is this a master tracker game? +extern int Game_is_master_tracker_game; + +#define TRACKER_ID_LEN 10 //Don't change this! +extern char Tracker_id[TRACKER_ID_LEN]; + +extern ushort Turrett_position_counter[MAX_OBJECTS]; + + + +#define LOGIN_LEN 33 +#define REAL_NAME_LEN 66 +#define PASSWORD_LEN 17 +#define EMAIL_LEN 100 +#define TRACKER_ID_LEN 10 +#define PILOT_NAME_LEN 20 + +#if defined(WIN32) +#pragma pack(push,pxo) +#endif +#pragma pack(1) //Single byte alignment! +typedef struct vmt_descent3_struct { + char tracker_id[TRACKER_ID_LEN]; + char pilot_name[PILOT_NAME_LEN]; + int rank; + + int kills; + int deaths; + int suicides; + int online_time; + int games_played; + unsigned int security; + unsigned char virgin_pilot; //This pilot was just created if TRUE + unsigned int lateral_thrust; + unsigned int rotational_thrust; + unsigned int sliding_pct; //Percentage of the time you were sliding + unsigned long checksum; //This value needs to be equal to whatever the checksum is once the packet is decoded + unsigned long pad; //just to provide room for out 4 byte encryption boundry only needed on the client side for now +} vmt_descent3_struct; +#define DESCENT3_BLOCK_SIZE (sizeof(vmt_descent3_struct)-4) +#if defined(WIN32) +#pragma pack(pop,pxo) +#else +#pragma pack() +#endif + +extern vmt_descent3_struct MTPilotinfo[MAX_NET_PLAYERS]; + +//Display a menu based on what the server just told us about +void MultiDoGuidebotMenuData(ubyte *data); + +//The user either wants a menu to display or they took an action off of the menu +void MultiDoGuidebotMenuRequest(ubyte *data,int slot); + + + + +void MultiProcessBigData(ubyte *buf, int len,network_address *from_addr); + +// Points the server_address variable at a new location +void MultiSetServerAddress (network_address *addr); + +// Does multiplayer stuff for one frame +void MultiDoFrame(); + +// Returns 1 on success, 0 on fail +int TryToJoinServer (network_address *addr); + +// The server says we can join! +void MultiDoConnectionAccepted(ubyte *data); + +// Polls for a connection message so we can finally join this game +void MultiPollForConnectionAccepted (); + +// Gets a new connection set up +void MultiSendConnectionAccepted (int slotnum,SOCKET sock,network_address *addr); + +// Starts a new multiplayer game +void StartNewMultiplayerGame (); + +// Clears all connections +// Server and Client +void MultiCloseGame (); + +// The server sends to everyone that the player is dead +void MultiSendPlayerDead (int slot,ubyte fate); + +// Called when you want to leave a netgame +void MultiLeaveGame (); + + +// MultiProcessIncoming reads incoming data off the unreliable and reliable ports and sends +// the data to process_big_data +void MultiProcessIncoming(); + +// Starts a packet of data +int START_DATA (int type,ubyte *data,int *count,ubyte reliable=0); + +// End a pakcet of data +void END_DATA (int count,ubyte *data,int offset); + +// Skip the header stuff +void SKIP_HEADER (ubyte *data,int *count); + +// Starts a level for multiplayer +bool MultiStartNewLevel (int level); + +// Returns the number of players in a game +int MultiCountPlayers (); + +// Puts player "slot" position info into the passed in buffer +// Returns the number of bytes used +int MultiStuffPosition (int slot,ubyte *data); + +// Sends a full packet out the the server +// Resets the send_size variable +// If slot = -1, sends out to the server +void MultiSendFullPacket (int slot,int flags); + +void MultiSendFullReliablePacket (int slot,int flags); + +// Makes the passed in player a ghost +void MultiMakePlayerGhost (int slot); + + +// Makes the passed in player real (non-ghost) +void MultiMakePlayerReal (int slot); + +// Sends a fire packet from a player +void MultiSendFirePlayerWB (int playernum,ubyte wb_index,ubyte fire_mask,ubyte reliable=0,float scalar=1.0); + +void MultiMakeMatrix (multi_orientation *dest,matrix *src); + +// Extracts a matrix from an abbreviated matrix +void MultiExtractMatrix (matrix *dest,multi_orientation *src); + +void MultiSendBlowupBuilding (int,int,float); + +// Return index of powerup that has matching table entry +int MultiMatchPowerup (int unique_id); + +// Return index of robot that has matching table entry +int MultiMatchRobot (int unique_id); + +// Builds the tables for uniquely identifying powerups and robots +void MultiBuildMatchTables(); + +// Return index of generic that has matching table entry +int MultiMatchWeapon (uint unique_id); + +// Tell my clients about damage done to a player +void MultiSendDamagePlayer (int,int,int type,float amount); + +// Send a message! +void MultiSendMessageFromServer (int,char *,int to=MULTI_SEND_MESSAGE_ALL); + +// Tells the server that I'm done dying +void MultiSendEndPlayerDeath (); + +// Returns the unique id of a given object type/id +uint MultiGetMatchChecksum (int type,int id); + +// Return index of generic that has matching table entry +int MultiMatchGeneric (uint unique_id); + +// Sends a message from client to server +void MultiSendMessageToServer (int, char *,int to=MULTI_SEND_MESSAGE_ALL); + +// Sends an object from the server to the client +void MultiSendObject (object *obj,ubyte announce,ubyte demo_record=false); + +// Paints all the goal rooms in a level with their colors +void MultiPaintGoalRooms (int *texcolors=NULL); + +// Sends a special script packet to a player +void MultiSendSpecialPacket (int slot,ubyte *outdata,int size); + +// Flushes all receive sockets so that there is no data coming from them +void MultiFlushAllIncomingBuffers (); + +// Tells all clients to remove a specified object +void MultiSendRemoveObject (object *obj,ubyte playsound); + +// Sends the special script packet to the server +void MultiClientSendSpecialPacket (ubyte *outdata,int size); + +// Sends info out on my guided missile into a slot +// returns number of bytes in packet +int MultiStuffGuidedInfo (ubyte,ubyte *); + +// Polls for a connection message so we can finally join this game +// Client only +int MultiPollForLevelInfo (); + +// Server is telling us about the level +void MultiDoLevelInfo (ubyte *data); + +// Server is telling the client about the level currently playing +// Server only +void MultiSendLevelInfo (int slot); + +// Clients says he's ready for level info +// so send it to him +void MultiDoReadyForLevel (ubyte *data); + +// Client is telling the server that he is ready for a level +// Client only +void MultiSendReadyForLevel (); + +// Tells all the clients to end the level +void MultiSendLevelEnded (int success,int next_level); + +// Some DLL is telling us to end the level! +void MultiEndLevel (); + +//Request the server's gametime +void GetServerGameTime(); + +//Send robot info +int MultiStuffRobotPosition (unsigned short objectnum,ubyte *data); + +//Handle robot position +void MultiDoRobotPos (ubyte *data); + +//Handle robot (or any AI created) weapon fire +int MultiSendRobotFireWeapon (unsigned short objectnum,vector *pos,vector *dir,unsigned short weaponnum); + +//Send robot damage +void MultiSendKillObject (object *hit_obj,object *killer,float damage,int death_flags,float delay,short seed); + +//handle robot damage +void MultiDoRobotExplode(ubyte *data); + +// Peer to peer request for damage +void MultiSendRequestPeerDamage (object *,int,int,float); + +// Tell all the clients about this players rank +void MultiSendInitialRank (int pnum); + +// Tells the other players that a slot is starting/stopping its on/off weapon +void MultiSendOnOff (object *obj,ubyte on,ubyte wb_index,ubyte fire_mask); + +// Tells all the clients to apply damage to a player +void MultiSendAdditionalDamage (int slot,int type,float amount); + +// We're asking the server to create a countermeasure for us +void MultiSendRequestCountermeasure (short objnum,int weapon_index); + +// Tell the client that an object took damage +void MultiSendDamageObject (object *hit_obj,object *killer,float damage,int weaponid); + +// Handle message from server that robot/object took damage +void MultiDoRobotDamage(ubyte *data); + +// Add an object to the list of objects that need an animation update next player packet interval +void MultiAddObjAnimUpdate(int objnum); + +// Stuff an animation update into a packet +int MultiStuffObjAnimUpdate(unsigned short objnum, ubyte *data); + +// Handle an animation update +void MultiDoObjAnimUpdate(ubyte *data); + +// Play a 3d sound that the server told us about +void MultiDoPlay3dSound(ubyte *data); + +// Tell the clients to play a 3d sound +void MultiPlay3dSound(short soundidx,ushort objnum,int priority); + +// Tell the client to play a sound because a robot fired +void MultiSendRobotFireSound(short soundidx,ushort objnum); + +// Play the robot sound that the server told us about +void MultiDoRobotFireSound(ubyte *data); + +// Add a turret to the list of stuff to be updated +void MultiAddObjTurretUpdate(int objnum); + +// Stuff turret data into a packet +int MultiStuffTurretUpdate(unsigned short objnum, ubyte *data); + +// Handle a turret update from the server +void MultiDoTurretUpdate(ubyte *data); + +// Handle a client use inventory item packet +void MultiDoClientInventoryUseItem(int slot,ubyte *data); + +// Send a request to use an inventory item to the server +void MultiSendClientInventoryUseItem(int type,int id); + +// Handle a remove item from inventory +void MultiDoClientInventoryRemoveItem(int slot,ubyte *data); + +// Tell the clients to remove an item from a player's inventory +void MultiSendInventoryRemoveItem(int slot,int type,int id); + +void MultiAddObjWBAnimUpdate(int objnum); + +int MultiStuffObjWBAnimUpdate(unsigned short objnum, ubyte *data); + +void MultiDoObjWBAnimUpdate(ubyte *data); + +void MultiDoBytesSent(ubyte *data); + +void MultiSendBytesSent(int slot); + +void MultiSendPPSSet(int pps); + +void MultiDoPPSSet(ubyte *data,int slot); + +void MultiSendGreetings(unsigned int id); + +void MultiDoGreetings (ubyte *data,network_address *addr); + +// We're asking to enter observer mode +void MultiSendRequestToObserve (int mode,int on,int objnum); + +// Server is telling us about players that we can see +void MultiDoVisiblePlayers (ubyte *data); + +// Sends all the visible players to another player +void MultiSendVisiblePlayers (int to_slot); + +void MultiDoFileReq(ubyte *data); + +void MultiDoFileDenied(ubyte *data); + +void MultiDoFileData(ubyte *data); + +void MultiDoFileAck(ubyte *data); + +// Tells clients that a particular player's custom data is here and ready for downloading +void MultiSendClientCustomData(int slot,int whoto = -1); + +void MultiCancelFile(int playernum,int filenum,int file_who); + +void MultiAskForFile(ushort file_id,ushort file_who,ushort who); + +void DoNextPlayerFile(int playernum); + +// We're asking the server to damage us +void MultiSendRequestDamage (int type,float amount); + +// Asks the server for shields based on frametime "amount" x the type of shields requested +void MultiSendRequestShields (int type,float amount); + +// Tells the clients to ghost or unghost an object +void MultiSendGhostObject( object *obj, bool ghost); +void MultiDoGhostObject (ubyte *data); + +// Sends this nonreliable packet to everyone except the server and the named slot +void MultiSendToAllExcept (int except,ubyte *data,int size,int seq_threshold); + +// Tells the server about the weapons we're carrying +void MultiSendWeaponsLoad (); + +// Tell clients to break some glass +void MultiSendBreakGlass (room *rp,int facenum); + +// Sends a heartbeat to the server +void MultiSendHeartbeat (); + +//Ping functions to find the players latency +void MultiDoPong (ubyte *data); +void MultiDoPing (ubyte *data,network_address *addr); +void MultiSendPing( int slot); +void MultiDoLagInfo(ubyte *data); + +// Stuffs a players firing information into a packet +int MultiStuffPlayerFire (int slot,ubyte *data); + +// Stuffs request to move into a packet +int MultiStuffRequestToMove (ubyte *data); + +// Stuff info for a guided missile +int MultiStuffGuidedInfo (int slot,ubyte *data); + +// Tell everyone I'm timingout my timeout weapon +void MultiSendReleaseTimeoutMissile (); + +// We're asking the server for permission to fire! +void MultiSendRequestToFire (int,int,float scalar=1.0); + +// Client is asking the server to play an audio taunt +void MultiSendRequestPlayTaunt(int index); + +// Client is asking for a marker +void MultiSendRequestForMarker (char *message); + +// Client is telling the server that he is [not] typing a hud message +void MultiSendRequestTypeIcon(bool typing_message); + +// Sets whether or not the server answsers to a connection request +extern bool Multi_accept_state; +void MultiSetAcceptState (bool state); + +void MultiSendAiWeaponFlags(object *obj, int flags, int wb_index); +void MultiDoAiWeaponFlags(ubyte *data); +void MultiSendAttach(object *parent, char parent_ap, object *child, char child_ap, bool f_aligned); +void MultiDoAttach(ubyte * data); +void MultiSendAttachRad(object *parent, char parent_ap, object *child, float rad); +void MultiDoAttachRad(ubyte * data); +void MultiSendUnattach(object *child); +void MultiDoUnattach(ubyte * data); + +void MultiDoJoinDemoObjects (ubyte *data); + +// Rank stuff +void MultiDoChangeRank (ubyte *data); + +// Sets whether or not we want the logos to be displayed on ships +void MultiSetLogoState (bool state); + +void MultiSendThiefSteal(int player,int item); +void MultiDoThiefSteal(ubyte * data); + +void MultiSetAudioTauntTime(float time,int to_who=-1); +void MultiDoAudioTauntTime(ubyte *data); + +// Server only function to clear a Guidebot for a disconnected player +void MultiClearGuidebot(int slot); + +// Guided missile release +void MultiDoMissileRelease(int from_slot,ubyte *data); +void MultiSendMissileRelease(int slot,bool is_guided); + +//Server telling a client what ship to switch to +void MultiBashPlayerShip(int slot,char *ship); + +// Strips a player bare of weapons +void MultiSendStripPlayer(int slot); + +inline void MultiGetTypeID(ubyte *data,int *count,int *type,int *id) +{ + *id = -1; + *type = MultiGetByte(data,count); + + uint hash_value = MultiGetUint(data,count); + + if((*type)==OBJ_WEAPON) + *id = MultiMatchWeapon(hash_value); + else + *id = MultiMatchGeneric(hash_value); +} + +inline void MultiAddTypeID(int type,int id,ubyte *data,int *count) +{ + uint hash_value; + hash_value = MultiGetMatchChecksum(type,id); + + MultiAddByte(type,data,count); + MultiAddUint(hash_value,data,count); +} + +int MultiGetShipChecksum (int ship_index); + + +#endif + diff --git a/Descent3/multi_client.cpp b/Descent3/multi_client.cpp new file mode 100644 index 000000000..7842f89f5 --- /dev/null +++ b/Descent3/multi_client.cpp @@ -0,0 +1,563 @@ +/* + * $Logfile: /DescentIII/Main/multi_client.cpp $ + * $Revision: 57 $ + * $Date: 9/20/01 1:10p $ + * $Author: Matt $ + * + * Multiplayer client code + * + * $Log: /DescentIII/Main/multi_client.cpp $ + * + * 57 9/20/01 1:10p Matt + * Fix for client not taking fusion overchage damage in a peer-to-peer + * game. + * + * 56 9/02/99 3:34p Jason + * send secondary fire reliable in a C/S game + * + * 55 5/20/99 4:10p Jason + * added heartbeat to multiplayer so clients wouldn't time out, also + * various multiplayer fixes + * + * 54 5/17/99 6:26p Jason + * added message for when the reliable buffer overruns + * + * 53 5/13/99 6:20p Kevin + * fixed bug with client who's reliable connection was broken to the + * server not disconnecting + * + * 52 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 51 5/05/99 5:41p Jason + * fixed various multiplayer issues, including sequencing bugs and cheat + * prevention + * + * 50 4/30/99 5:06p Kevin + * misc dedicated server, networking and low memory enhancements + * + * 49 4/27/99 4:42a Jeff + * pass guidebot name on MyInfo + * + * 48 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 47 2/16/99 3:47p Jason + * added marker updates to multiplayer server stream + * + * 46 2/15/99 7:49p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 45 2/15/99 12:24p Jason + * set default pps setting if permissable netgame + * + * 44 2/15/99 11:21a Jason + * took out client side interpolation + * + * 43 2/12/99 5:58p Jason + * more permissable server stuff + * + * 42 2/12/99 3:38p Jason + * added client side interpolation + * + * 41 2/04/99 11:01a Jason + * added anti cheating to multiplayer + * + * 40 12/21/98 10:22a Jason + * added auto pps + * + * 39 12/08/98 4:52p Jeff + * changed the pilot pics packing to use Ushort instead of short just for + * my conscience...removed some annoying mprintf's too + * + * 38 12/01/98 5:48p Jeff + * added pilot picture id to netplayer struct + * + * 37 11/18/98 10:50a Jason + * fixed peer to peer with new architecture + * + * 36 11/16/98 4:47p Jason + * changes for multiplayer (weapons load sent and deleting destroyed + * lightmapped objects) + * + * 35 10/28/98 11:51a Jason + * fixed some multiplayer problems, plus redid coop a bit to make it + * cleaner + * + * 34 10/27/98 10:19a Jason + * changes for new arch + * + * 33 10/22/98 12:28p Kevin + * Fixed pings in peer-peer + * + * 32 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 31 10/01/98 4:03p Jason + * took out sequencing bug + * + * 30 10/01/98 11:36a Kevin + * UI fixes and stuff + * + * 29 10/01/98 11:29a Jason + * changes for world states in multiplayer games + * + * 28 9/11/98 12:26p Jason + * got energy to shield and fusion damage working in multiplayer + * + * 27 9/04/98 3:45p Kevin + * Added ping_time to Netplayers struct. It's updated in peer-peer and + * client server + * + * 26 8/05/98 10:46a Kevin + * sound & bmp exchange fixes + * +*/ + + +#include "multi.h" +#include "multi_client.h" +#include "game.h" +#include "player.h" +#include "ddio.h" +#include "pilot.h" +#include "Mission.h" +#include "stringtable.h" +#include "d3serial.h" +#include "ship.h" + +#define WEAPONS_LOAD_UPDATE_INTERVAL 2.0 + +float Last_weapons_load_update_time=0; + +// Setup saved moves +saved_move SavedMoves[MAX_SAVED_MOVES]; +int Current_saved_move=0; + +extern int Use_file_xfer; + +// Tell the server about my info, such as name, ship type, etc +void MultiSendMyInfo () +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending my info\n")); + char pshipmodel[PAGENAME_LEN]; + Current_pilot.get_ship(pshipmodel); + + int ship_index=FindShipName (pshipmodel); + if (ship_index<0) + ship_index=0; + + size=START_DATA (MP_MY_INFO,data,&count); + + // Do callsign name + MultiAddByte (Player_num,data,&count); + int len=strlen (Players[Player_num].callsign)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Players[Player_num].callsign,len); + count+=len; + + // Do ship name + len=strlen (Ships[ship_index].name)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Ships[ship_index].name,len); + count+=len; + + if(Game_is_master_tracker_game) + { + MultiAddUint(MASTER_TRACKER_SIG,data,&count); + strcpy(Players[Player_num].tracker_id,Tracker_id); + len = strlen(Players[Player_num].tracker_id)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Players[Player_num].tracker_id,len); + count+=len; + } + int ser = 0; + GetInternalSerializationNumber(&ser); + MultiAddInt (ser,data,&count); + + // Send packets per second + int pps=nw_ReccomendPPS(); + if ((Netgame.flags & NF_PERMISSABLE) && pps<8) + pps=8; // If permissable game, can't be lower than 8 + + MultiAddByte (pps,data,&count); + + //pilot picture id + ushort ppic_id; + Current_pilot.get_multiplayer_data(NULL,NULL,NULL,&ppic_id); + MultiAddUshort(ppic_id,data,&count); + + // Copy the guidebot name out + char guidebot_name[32]; + Current_pilot.get_guidebot_name(guidebot_name); + len=strlen (guidebot_name)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],guidebot_name,len); + count+=len; + + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + +// Ask the server to tell me about the players +void MultiSendRequestForPlayers () +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending request for players\n")); + + size=START_DATA (MP_REQUEST_PLAYERS,data,&count); + MultiAddByte (Player_num,data,&count); + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + +// Ask the server to tell me about the buildings +void MultiSendRequestForBuildings () +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending request for buildings\n")); + + size=START_DATA (MP_REQUEST_BUILDINGS,data,&count); + MultiAddByte (Player_num,data,&count); + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + +// Ask the server to tell me about the world +void MultiSendRequestForWorldStates () +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending request for world states\n")); + + size=START_DATA (MP_REQUEST_WORLD_STATES,data,&count); + MultiAddByte (Player_num,data,&count); + // Send the digest (checksum) for this level + our salt + the ships + // Do level checksum + for(int i=0;i<16;i++) + { + MultiAddByte(NetPlayers[Player_num].digest[i],data,&count); + } + + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + +// Ask the server to tell me about the objects +void MultiSendRequestForObjects () +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending request for objects\n")); + + size=START_DATA (MP_REQUEST_OBJECTS,data,&count); + MultiAddByte (Player_num,data,&count); + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + + + +#define SERVER_DISCONNECT_TIME 8.0 + +// Returns 1 if the server is gone! +int ServerTimedOut () +{ + // Don't check for a non-connected player + if (!(NetPlayers[Player_num].flags & NPF_CONNECTED)) + return 0; + + if(!nw_CheckReliableSocket(NetPlayers[Player_num].reliable_socket)) + { + mprintf((0,"Reliable connection to the server was broken. Disconnecting.\n")); + ShowProgressScreen (TXT_RELIABLE_OVERRUN); + Sleep (1000); + return 1; + } + if ((timer_GetTime()-Netgame.last_server_time)>SERVER_DISCONNECT_TIME) + return 1; + + return 0; + +} +#define NET_CLIENT_GAMETIME_REQ_TIMEOUT 10 +#define NET_CLIENT_GAMETIME_REQ_RETRY 1 +float First_gametime_req = 0; +float Last_gametime_req = 0; + +// Do client stuff for this frame +void MultiDoClientFrame () +{ + int i; + Multi_last_sent_time[Player_num][0]+=Frametime; + + // Get data from the server + MultiProcessIncoming(); + + if (Netgame.flags & NF_EXIT_NOW) + { + // DLL says we should bail! + Netgame.flags &=~NF_EXIT_NOW; + MultiLeaveGame(); + SetFunctionMode(MENU_MODE); + return; + } + + if (NetPlayers[Player_num].sequence==NETSEQ_LEVEL_START) + { + MultiSendMyInfo (); + NetPlayers[Player_num].sequence=NETSEQ_NEED_GAMETIME; + First_gametime_req = timer_GetTime(); + } + else if (NetPlayers[Player_num].sequence==NETSEQ_NEED_GAMETIME) + { + //Ask for gametime + GetServerGameTime(); + Last_gametime_req = timer_GetTime(); + NetPlayers[Player_num].sequence=NETSEQ_WAIT_GAMETIME; + } + else if (NetPlayers[Player_num].sequence==NETSEQ_WAIT_GAMETIME) + { + if(Got_new_game_time) + { + NetPlayers[Player_num].sequence=NETSEQ_REQUEST_PLAYERS; + } + else + { + //Wait for server response + if((timer_GetTime()-First_gametime_req)>NET_CLIENT_GAMETIME_REQ_TIMEOUT) + { + //Giving up, we waited too long. + mprintf ((0,"Server disconnected while waiting for gametime!\n")); + MultiLeaveGame(); + ShowProgressScreen (TXT_MLTDISCFRMSERV); + Sleep(2000); + } + else if((timer_GetTime()-Last_gametime_req)>NET_CLIENT_GAMETIME_REQ_RETRY) + { + NetPlayers[Player_num].sequence=NETSEQ_NEED_GAMETIME; + } + + } + + } + else if (NetPlayers[Player_num].sequence==NETSEQ_REQUEST_PLAYERS) + { + MultiSendRequestForPlayers(); + NetPlayers[Player_num].sequence=NETSEQ_PLAYERS; + } + else if (NetPlayers[Player_num].sequence==NETSEQ_REQUEST_BUILDINGS) + { + MultiSendRequestForBuildings(); + NetPlayers[Player_num].sequence=NETSEQ_BUILDINGS; + } + else if (NetPlayers[Player_num].sequence==NETSEQ_REQUEST_OBJECTS) + { + MultiSendRequestForObjects(); + NetPlayers[Player_num].sequence=NETSEQ_OBJECTS; + } + else if (NetPlayers[Player_num].sequence==NETSEQ_REQUEST_WORLD) + { + MultiSendRequestForWorldStates(); + NetPlayers[Player_num].sequence=NETSEQ_WORLD; + NetPlayers[Player_num].custom_file_seq = 0; + } + else if (NetPlayers[Player_num].sequence==NETSEQ_PLAYING) + { + if(NetPlayers[Player_num].custom_file_seq == 0) + { + //Tell the server about our custom data (once only) + if(NetPlayers[Player_num].ship_logo[0]) + PlayerSetCustomTexture(Player_num,NetPlayers[Player_num].ship_logo); + NetPlayers[Player_num].custom_file_seq = 0xff; + + // Make sure we as the client actually want to send our custom data + if(Use_file_xfer) + MultiSendClientCustomData(Player_num); + + } + + bool client_file_xfering = false; + for(i=0;iWEAPONS_LOAD_UPDATE_INTERVAL) + { + Last_weapons_load_update_time=0; + MultiSendWeaponsLoad (); + } + + + // Figure out if we need to send our movement + bool send_it=false; + + if (Player_fire_packet[Player_num].fired_on_this_frame!=PFP_NO_FIRED) + send_it=true; + + if (Multi_last_sent_time[Player_num][0]>(1.0/(float)NetPlayers[Player_num].pps)) + send_it=true; + + + if (send_it) + { + // Request damage if need be + if (Multi_requested_damage_amount!=0) + { + if ((Netgame.local_role==LR_CLIENT) && (Netgame.flags & NF_PEER_PEER)) + { + MultiSendRequestPeerDamage(&Objects[Players[Player_num].objnum],-1,Multi_requested_damage_type,Multi_requested_damage_amount); + } + else + { + MultiSendRequestDamage (Multi_requested_damage_type,Multi_requested_damage_amount); + } + Multi_requested_damage_amount=0; + } + + // Request shields if need be + for (i=0;iMULTI_PING_INTERVAL) + MultiSendPing(i); + + nw_Send (&NetPlayers[i].addr,data,count,0); + } + } + else + nw_Send(&Netgame.server_address,data,count,0); + + // TODO: SEND RELIABLE WEAPON FIRE + if (Player_fire_packet[Player_num].fired_on_this_frame==PFP_FIRED_RELIABLE) + { + //mprintf((0,"I NEED TO SEND RELIABLE FIRE\n")); + count = MultiStuffPlayerFire(Player_num,data); + nw_SendReliable(NetPlayers[Player_num].reliable_socket,data,count,true); + } + + // Clear our data + Player_fire_packet[Player_num].fired_on_this_frame=PFP_NO_FIRED; + Multi_last_sent_time[Player_num][0]=0; + Players[Player_num].flags &=~PLAYER_FLAGS_SEND_MOVEMENT; + } + + if (ServerTimedOut()) + { + mprintf ((0,"Server disconnected!\n")); + MultiLeaveGame(); + ShowProgressScreen (TXT_MLTDISCFRMSERV); + Sleep(2000); + } + } +} + +// Initializes some fields for a network client to play +// Client only +void MultiStartClient (char *scriptname) +{ + int i; + + for (i=0;i0); + nw_Send (addr,data,count,0); + tries++; + + start_time=timer_GetTime(); + while ((timer_GetTime()-start_time 0) && Ok_to_join==-1) + { + MultiProcessBigData(Multi_receive_buffer,packsize,&from_addr); + } + } + } + + if (tries>=5 || Ok_to_join!=JOIN_ANSWER_OK) + return 0; + + // Ok to join! + return 1; +} + +// Trys to join this client with the server address passed in +// Returns 1 on success, 0 on fail +// Client only +// For DirectPlay games, use the addr pointer as a pointer to the DPSESSIONDESC2 +int TryToJoinServer (network_address *addr) +{ + SOCKET sock; + + ShowProgressScreen(TXT_MULTI_CONNECTING); + //Store the server's address in the netplayer struct + memcpy(&NetPlayers[0].addr,addr,sizeof(network_address)); + + for (int i=0;i 0) + { + MultiProcessBigData(data,size,&Netgame.server_address); + + if (NetPlayers[Player_num].flags & NPF_CONNECTED) + { + connected=1; + } + } + } + + if (!connected) + { + mprintf ((0,"Couldn't get a connection_accepted packet for some reason!\n")); + } + +} + +// Gets a new connection to a new client set up +// This does not mean the client will enter the game, just that the server +// is successfully talking to the client +// Server only +void MultiSendConnectionAccepted (int slotnum,SOCKET sock,network_address *addr) +{ + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + mprintf ((0,"Sending connection accepted packet to slot %d!\n",slotnum)); + + NetPlayers[slotnum].reliable_socket=sock; + memcpy (&NetPlayers[slotnum].addr,addr,sizeof(network_address)); + NetPlayers[slotnum].flags =NPF_CONNECTED; + NetPlayers[slotnum].sequence=NETSEQ_WAITING_FOR_LEVEL; + Players[slotnum].flags=0; + Players[slotnum].rank=-1; + + // Adjust port + NetPlayers[slotnum].addr.port--; + + + NetPlayers[slotnum].last_packet_time=timer_GetTime(); + + // Tell the new guy about the game in progress + size_offset=START_DATA (MP_CONNECTION_ACCEPTED,data,&count); + + // Send server version + MultiAddShort (Netgame.server_version,data,&count); + + // Do mission name + int len=strlen(Netgame.mission)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Netgame.mission,len); + count+=len; + + mprintf ((0,"Sending netgame mission %s with length of %d!\n",Netgame.mission,len)); + + len = strlen(Netgame.name)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Netgame.name,len); + count+=len; + + // Do script name + len=strlen(Netgame.scriptname)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Netgame.scriptname,len); + count+=len; + + // Set Player number + MultiAddByte (slotnum,data,&count); + + // Send packets per second + MultiAddByte (Netgame.packets_per_second,data,&count); + + //Send secret code which the client will use to send a non reliable packet + NetPlayers[slotnum].secret_net_id = Secret_net_id++; + MultiAddUint (NetPlayers[slotnum].secret_net_id,data,&count); + + + // Send netplayer flags (obsoletes the above line of code, left for a while for compatability) + MultiAddInt (Netgame.flags,data,&count); + + END_DATA (count,data,size_offset); + + // Send it back to the player + nw_SendReliable (sock,data,count); + + ASSERT (count5.0) + { + ask_time=timer_GetTime(); + MultiSendReadyForLevel (); + } + + int size; + while( (size = nw_ReceiveReliable(NetPlayers[Player_num].reliable_socket,data, MAX_RECEIVE_SIZE)) > 0) + { + MultiProcessBigData(data,size,&Netgame.server_address); + } + + while( (size = nw_Receive(data, &from_addr))>0 ) + { + if (data[0]==MP_HEARTBEAT) + { + mprintf ((0,"Got a heart beat from the server.\n")); + Got_heartbeat=true; + } + } + + if (Got_heartbeat) + { + // Reset timer because we got a heartbeat + start_time=timer_GetTime(); + Got_heartbeat=false; + } + } + + if (!Got_level_info) + { + ShowProgressScreen (TXT_MLTNOLEVELINFO); + MultiLeaveGame(); + Sleep (2000); + } + else if (Got_level_info<0) + { + ShowProgressScreen (TXT(Join_response_strings[-Got_level_info])); + MultiLeaveGame(); + Sleep (2000); + } + else + { + Got_level_info=0; + return 1; + } + + return 0; +} + +#include "polymodel.h" + +// Returns a unique value for this ship +int MultiGetShipChecksum (int ship_index) +{ + ship *s=&Ships[ship_index]; + + int num=0; + + AppendToLevelChecksum(s->phys_info.full_thrust); + AppendToLevelChecksum(s->phys_info.full_rotthrust); + + AppendToLevelChecksum(s->phys_info.mass); + AppendToLevelChecksum(s->phys_info.drag); + AppendToLevelChecksum(s->phys_info.rotdrag); + + AppendToLevelChecksum(s->phys_info.flags); + poly_model *pm = GetPolymodelPointer(s->model_handle); + AppendToLevelChecksum(pm->rad); + + for (int w=0;wstatic_wb[w].gp_fire_wait[i]); + AppendToLevelChecksum(s->static_wb[w].gp_fire_masks[i]); + } + + AppendToLevelChecksum(s->static_wb[w].energy_usage); + AppendToLevelChecksum(s->static_wb[w].ammo_usage); + AppendToLevelChecksum(s->fire_flags[w]); + } + return num; +} + + +// Returns a unique value for this ship +void MultiProcessShipChecksum (MD5 *md5, int ship_index) +{ + ship *s=&Ships[ship_index]; + + md5->MD5Update(s->phys_info.full_thrust); + md5->MD5Update(s->phys_info.full_rotthrust); + + md5->MD5Update(s->phys_info.mass); + md5->MD5Update(s->phys_info.drag); + md5->MD5Update(s->phys_info.rotdrag); + + md5->MD5Update(s->phys_info.flags); + poly_model *pm = GetPolymodelPointer(s->model_handle); + md5->MD5Update(pm->rad); + + for (int w=0;wMD5Update(s->static_wb[w].gp_fire_wait[i]); + md5->MD5Update(s->static_wb[w].gp_fire_masks[i]); + } + + md5->MD5Update(s->static_wb[w].energy_usage); + md5->MD5Update(s->static_wb[w].ammo_usage); + md5->MD5Update(s->fire_flags[w]); + } +} + +// Server is telling us about the level +void MultiDoLevelInfo (ubyte *data) +{ + int count=0; + char pshipmodel[PAGENAME_LEN]; + Current_pilot.get_ship(pshipmodel); + + int ship_index=FindShipName (pshipmodel); + if (ship_index<0) + ship_index=0; + + // Skip header stuff + SKIP_HEADER (data,&count); + + int join_response=MultiGetByte (data,&count); + + // Get level number + Current_mission.cur_level=MultiGetByte (data,&count); + + Netgame.difficulty = MultiGetByte (data,&count); + + if( (Netgame.difficulty>4) || (Netgame.difficulty<0) ) + Netgame.difficulty = 2; + + if (join_response!=JOIN_ANSWER_OK) + Got_level_info=-join_response; + else + Got_level_info=1; +} + +// Server is telling the client about the level currently playing +// Server only +void MultiSendLevelInfo (int slot) +{ + ASSERT (Netgame.local_role==LR_SERVER); + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + size_offset=START_DATA(MP_LEVEL_INFO,data,&count); + + if (Netgame.local_role==LR_SERVER) + { + if (Players[slot].start_roomnum!=-1) + MultiAddByte (JOIN_ANSWER_OK,data,&count); + else + MultiAddByte (JOIN_ANSWER_NO_ROOM,data,&count); + } + else + MultiAddByte (JOIN_ANSWER_NOT_SERVER,data,&count); + + // Do level number (of the mission) + mprintf ((0,"Sending current mission level %d!\n",Current_mission.cur_level)); + MultiAddByte (Current_mission.cur_level,data,&count); + + //Send the difficulty + MultiAddByte (Netgame.difficulty,data,&count); + + END_DATA (count,data,size_offset); + + // Send it out + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count); +} + +// Clients says he's ready for level info +// so send it to him +void MultiDoReadyForLevel (ubyte *data) +{ + int count=0; + ubyte slot; + char ship_name[PAGENAME_LEN]; + + // Skip header stuff + SKIP_HEADER (data,&count); + + // Get slot number + slot=MultiGetByte (data,&count); + + // Copy the ship name out + int len=MultiGetByte (data,&count); + memcpy (ship_name,&data[count],len); + count+=len; + + int ship_index=FindShipName (ship_name); + if (ship_index<0) + ship_index=0; + + PlayerChangeShip (slot,ship_index); + + MultiSendLevelInfo (slot); + NetPlayers[slot].sequence=NETSEQ_LEVEL_START; +} + +// Client is telling the server that he is ready for a level +// Client only +void MultiSendReadyForLevel () +{ + ASSERT (Netgame.local_role!=LR_SERVER); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + mprintf ((0,"Sending ready for level!\n")); + + char pshipmodel[PAGENAME_LEN]; + Current_pilot.get_ship(pshipmodel); + + int ship_index=FindShipName (pshipmodel); + if (ship_index<0) + ship_index=0; + + size_offset=START_DATA(MP_READY_FOR_LEVEL,data,&count); + + MultiAddByte (Player_num,data,&count); + + // Send ship for anti-cheating purposes (CHEAP and EASILY HACKABLE!) + int len=strlen (Ships[ship_index].name)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Ships[ship_index].name,len); + count+=len; + + END_DATA (count,data,size_offset); + + // Send it out + nw_SendReliable (NetPlayers[Player_num].reliable_socket,data,count); +} + + +extern int Multi_packet_tracking[]; +// Clears all connections +// Server and Client +void MultiCloseGame () +{ + mprintf ((0,"Multi close game!\n")); + + if (!(Game_mode & GM_NETWORK)) + { + mprintf ((0,"Not network game!\n")); + return; + } + + #if !(defined (RELEASE) || defined(MACINTOSH)) + // Write our packet tracking + CFILE *outfile; + outfile=cfopen ("PacketTracking","wt"); + if (!outfile) + { + mprintf ((0,"Couldn't open packet tracking file!\n")); + } + else + { + for (int i=0;i<255;i++) + { + cfprintf (outfile,"Packet %d = %d\n",i,Multi_packet_tracking[i]); + } + cfclose (outfile); + } + #endif + + Sleep (1000); // Sleep for one second + + if (Netgame.local_role==LR_SERVER) + { + int i; + + for (i=0;i 0)) + { + MultiProcessBigData(Multi_receive_buffer,packsize,&from_addr); + } + return Num_network_games_known; + +} + +// Fills in the passed in buffers with the info of the games that are on this subnet +int SearchForGamesPXO (unsigned int ask,ushort port) +{ + int count=0; + int size; + int tries=0; + ubyte data[MAX_GAME_DATA_SIZE]; + network_address check_addr,from_addr; + check_addr.connection_type = NP_TCP; + if(ask) + { + memcpy (&check_addr.address,&ask,4); + check_addr.port=htons(port); + + size=START_DATA (MP_GET_PXO_GAME_INFO,data,&count); + MultiAddFloat(timer_GetTime(),data,&count); + MultiAddInt (MULTI_VERSION,data,&count); + END_DATA(count,data,size); + + nw_Send (&check_addr,data,count,0); + + //Num_network_games_known=0; + } + + int packsize; + while(((packsize = nw_Receive(Multi_receive_buffer, &from_addr)) > 0)) + { + MultiProcessBigData(Multi_receive_buffer,packsize,&from_addr); + } + return Num_network_games_known; + +} + +#define MULTI_SERVER_TIMEOUT_TIME 40 +extern bool Multi_Gamelist_changed; + +void UpdateAndPackGameList(void) +{ + float curtime = timer_GetTime(); + for (int i=0;i MULTI_SERVER_TIMEOUT_TIME) + { + memcpy(&Network_games[i],&Network_games[Num_network_games_known],sizeof(network_game)); + Num_network_games_known--; + i=0; + Multi_Gamelist_changed = true; + } + } + +} + +int SearchForLocalGamesIPX (network_address *check_addr) +{ + int count=0; + int size; + int tries=0; + ubyte data[MAX_GAME_DATA_SIZE]; + network_address from_addr; + + if(check_addr) + { + if(check_addr->connection_type != NP_IPX) + return 0; + + size=START_DATA (MP_GET_GAME_INFO,data,&count); + MultiAddFloat(timer_GetTime(),data,&count); + MultiAddInt (MULTI_VERSION,data,&count); + END_DATA(count,data,size); + + nw_Send (check_addr,data,count,0); + + //Num_network_games_known=0; + } + + int packsize; + while(((packsize = nw_Receive(Multi_receive_buffer, &from_addr)) > 0)) + { + MultiProcessBigData(Multi_receive_buffer,packsize,&from_addr); + } + return Num_network_games_known; + +} + +// Sets whether or not the server answsers to a connection request +void MultiSetAcceptState (bool state) +{ + mprintf ((0,"Setting multi_accept_state to %s.\n",state?"true":"false")); + Multi_accept_state=state; +} diff --git a/Descent3/multi_dll_mgr.cpp b/Descent3/multi_dll_mgr.cpp new file mode 100644 index 000000000..aeb8f0adb --- /dev/null +++ b/Descent3/multi_dll_mgr.cpp @@ -0,0 +1,1286 @@ +/* +* $Logfile: /DescentIII/Main/multi_dll_mgr.cpp $ +* $Revision: 82 $ +* $Date: 10/03/01 12:45a $ +* $Author: Kevin $ +* +* Multi player DLL interface manager +* +* $Log: /DescentIII/Main/multi_dll_mgr.cpp $ + * + * 82 10/03/01 12:45a Kevin + * Smaller version of pxo packets + * + * 81 3/26/00 10:29p Kevin + * MOD Downloader for 1.4 patch. + * + * 80 7/28/99 3:55p Kevin + * Mac! + * + * 79 7/12/99 4:15p Kevin + * Changed the way we determine if we should report stats or not in PXO + * + * 78 7/06/99 5:52p Kevin + * PXO & multiplayer fixes for the patch + * + * 77 5/12/99 2:24p Jeff + * Descent3 now has a setable temp directory for all temp files + * + * 76 5/10/99 10:22p Ardussi + * changes to compile on Mac + * + * 75 5/10/99 5:35p Kevin + * New command line options for heat and scoring API enhancements + * + * 74 5/08/99 11:25a Kevin + * + * 73 5/08/99 10:15a Kevin + * A bunch of heat scoring stuff + * + * 72 5/02/99 2:47p Jeff + * made linux friendly when extracting d3cs + * + * 71 4/28/99 2:24p Kevin + * added some heat scoring api code + * + * 70 4/25/99 5:02p Kevin + * Bunches of multiplayer UI improvements + * + * 69 4/25/99 12:18p Matt + * Use the briefing font instead of the HUD font for the PXO chat window. + * + * 68 4/24/99 11:58p Kevin + * Game info list (hit I in the pxo game list) + * + * 67 4/23/99 3:33p Kevin + * mission file/multiplayer mod keyword system + * + * 66 4/22/99 3:43p Kevin + * Training missions show controls on screen + * + * 65 4/19/99 10:23p Kevin + * A bunch of little things for the new demo + * + * 64 4/19/99 4:49p Jeff + * removed call to atexit to free DLL (Linux doesn't like that) + * + * 63 4/19/99 3:46a Jeff + * pass in variable for directplay in use for Linux + * + * 62 4/16/99 3:17p Kevin + * More mouselook support + * + * 61 4/16/99 11:56a Matt + * Changed directplay code to be "ifdef _WIN32" instead of "ifndef + * __LINUX__" so it will work on the Mac. + * + * 60 4/16/99 12:15a Jeff + * linux wants stdcall modifiers before parens, unlike windows + * + * 59 4/15/99 3:04a Jeff + * oops messed up some order of includes + * + * 58 4/15/99 1:41a Jeff + * changes for linux compile + * + * 57 4/14/99 4:19a Jeff + * more case mismatch fixes in #includes + * + * 56 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 55 4/08/99 4:54p Kevin + * Display level warp dialog for multiplayer + * + * 54 4/08/99 3:41p Kevin + * Added some stuff for the scoring API. Not functional yet. + * + * 53 3/25/99 3:26p Kevin + * Made PXO games be based on your chat channel + * + * 52 3/17/99 4:08p Kevin + * Changed the way games appear and timeout in the game list. + * + * 51 3/05/99 1:24p Kevin + * Fixed Stupid Direct serial bug where it would try to join the game + * again and crash + * + * 50 2/15/99 7:49p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 49 2/08/99 2:50p Kevin + * Locked PollUI to 30 FPS MAX + * + * 48 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 47 1/12/99 2:55p Jeff + * added/finished the waiting for player's to join dialog + * + * 46 1/11/99 12:29p Jeff + * changes made not to call the module library directly + * + * 45 1/07/99 11:51a Kevin + * Added support for joining servers on alternate ports and hosting behind + * a proxy/firewall + * + * 44 12/30/98 5:24p Kevin + * Added +name to specify a default pilot (for gamespy) fixed a gamespy + * bug, and improved the pxo dll game list screen + * + * 43 12/30/98 3:49p Kevin + * Moved multiplayer options out of DLL and into main app + * + * 42 12/30/98 12:15p Kevin + * Auto Mission Download system + * + * 41 12/23/98 6:38p Kevin + * All UDP data (except gamespy) now uses one (registered) port number + * + * 40 12/08/98 3:28p Jeff + * created and exported UI Checkbox wrappers + * + * 39 12/07/98 11:30a Kevin + * Added some features from the 1.1 demo patch + * + * 38 11/19/98 5:56p Jeff + * created Slider wrappers + * + * 37 10/29/98 7:00p Jeff + * create some new UI function wrappers + * + * 36 10/19/98 8:12p Jason + * flush all incoming buffers + * + * 35 10/16/98 11:55a Kevin + * Made dlls loadable in a hog + * + * 34 10/15/98 3:29p Kevin + * Fixed dlls to not require runtime debug libraries in release builds + * + * 33 10/13/98 5:19p Kevin + * + * 32 10/12/98 11:13a Kevin + * doh! changed mem_free to mem_free_sub + * + * 31 10/01/98 11:36a Kevin + * UI fixes and stuff + * + * 30 9/28/98 4:21p Kevin + * Redesigned game list menus + * + * 29 9/23/98 2:54p Kevin + * Saved multi config and changed UI to conform + * + * 28 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 27 9/21/98 11:19a Kevin + * check protocol before entering multiplayer screens + * + * 26 9/16/98 8:06p Jason + * got mastertracker working with the dedicated server + * + * 25 9/14/98 2:36p Kevin + * fixed some UI issues + * + * 24 9/09/98 12:41p Kevin + * Fixed up some UI issues + * + * 23 9/04/98 1:51p Kevin + * implemented asyncronous gethostbyname + * + * 22 9/02/98 6:54p Kevin + * Fixed general directplay support up, and got modem-modem working + * + * 21 8/31/98 10:14a Kevin + * Misc. multi-UI fixes + * + * 20 8/27/98 5:03p Kevin + * Prettied up multiplayer screens and fixed some bugs. + * + * 19 8/25/98 6:33p Kevin + * pxo + * + * 18 8/24/98 5:04p Kevin + * Made msn files have the option to not be playable in multiplayer + * + * 17 8/24/98 10:55a Kevin + * new directplay stuff + * + * 16 8/19/98 11:49a Kevin + * Got DirectPlay IPX working, and localized connection DLLs + * + * 15 8/15/98 5:16p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 14 8/07/98 12:40p Jeff + * added some functions + * + * 13 7/21/98 1:49p Kevin + * IPX support and peer-peer option for multi + * + * 12 7/20/98 2:34p Kevin + * Re-wrote the DLL wrapper, to allow for better expandability + * + * 9 6/24/98 6:40p Kevin + * Added help to main dll menu + * + * 8 6/24/98 3:24p Kevin + * Updated PXO screens with chat, etc. + * + * 7 6/18/98 5:37p Kevin + * + * 6 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 5 6/05/98 2:16p Jeff + * Changes made so Dynamic loading of DLLs done through module library + * + * 4 6/01/98 11:56a Kevin + * Fixed lame listbox bug + * + * 3 6/01/98 10:09a Kevin + * Added DLL connection interface and auto update DLL + * + * 2 5/18/98 12:16p Kevin + * Initial version + * + * 1 5/18/98 12:07p Kevin +* +* $NoKeywords: $ +*/ +#ifdef WIN32 +#include +#include "windows.h" +#endif +#include "ui.h" +#include "newui.h" +#include "game.h" +#include "gamefont.h" +#include "multi.h" +#include "multi_client.h" +#include "manage.h" +#include "Mission.h" +#include "pilot.h" +#include "ui.h" +#include "newui.h" +#include "pstypes.h" +#include "pserror.h" +#include "descent.h" +#include "game.h" +#include "room.h" +#include "object.h" +#include "terrain.h" +#include "player.h" +#include "mono.h" +#include "hud.h" +#include "Inventory.h" +#include "multi_server.h" +#include "multi_ui.h" +#include "ship.h" +#include "soundload.h" +#include "spew.h" +#include "DllWrappers.h" +#include "appdatabase.h" +#include "module.h" +#include "ship.h" +#include "localization.h" +#include "stringtable.h" +#include "dedicated_server.h" +#include "multi_save_settings.h" +#include "multi_dll_mgr.h" +#include "mission_download.h" +#include "module.h" +#include "mem.h" +#include "args.h" +//#define USE_DIRECTPLAY + +#ifdef USE_DIRECTPLAY +#include "directplay.h" +#endif + +void *callback = NULL; +module MultiDLLHandle={NULL}; +int SearchForLocalGamesTCP (unsigned int ask,ushort port); +int SearchForGamesPXO (unsigned int ask,ushort port); +int SearchForLocalGamesIPX (network_address *check_addr); +extern ubyte NewUIWindow_alpha; +extern void DoScreenshot(); +extern void UpdateAndPackGameList(void); +extern bool Multi_Gamelist_changed; +int CheckMissionForScript(char *mission,char *script,int dedicated_server_num_teams); +void ShowNetgameInfo(network_game *game); +// The exported DLL function call prototypes +#if defined(__LINUX__) +typedef void DLLFUNCCALL (*DLLMultiCall_fp )( int eventnum); +typedef void DLLFUNCCALL (*DLLMultiScoreCall_fp )( int eventnum,void *data); +typedef void DLLFUNCCALL (*DLLMultiInit_fp )(int *api_fp); +typedef void DLLFUNCCALL (*DLLMultiClose_fp )(); +#else +typedef void( DLLFUNCCALL *DLLMultiCall_fp )( int eventnum); +typedef void( DLLFUNCCALL *DLLMultiScoreCall_fp )( int eventnum,void *data); +typedef void( DLLFUNCCALL *DLLMultiInit_fp )(int *api_fp); +typedef void( DLLFUNCCALL *DLLMultiClose_fp )(); +#endif +DLLMultiScoreCall_fp DLLMultiScoreCall=NULL; +DLLMultiCall_fp DLLMultiCall=NULL; +DLLMultiInit_fp DLLMultiInit=NULL; +DLLMultiClose_fp DLLMultiClose=NULL; +//dllmultiiInfo DLLMultiInfo; +//The DLL needs these too. +#define MAXTEXTITEMS 100 +#define MAXNEWWINDOWS 5 +#define MAXNEWGAMEWINDOWS 5 +#define MAXUIBUTTONS 20 +#define MAXUITEXTS 20 +#define MAXEDITS 20 +#define MAXLISTS 20 +#define MAXHOTSPOTS 20 +#define MAXCONSOLES 5 +#define MAXPARENTS 5 +NewUIMessageBox messageb; +//Stuff for the splash +UITextItem ti_msg("",UICOL_TEXT_NORMAL); +UITextItem ti_tmp("",UICOL_TEXT_NORMAL); +UITextItem ti_cancel_on("",UICOL_HOTSPOT_HI); +UITextItem ti_cancel_off("",UICOL_HOTSPOT_LO); +UIHotspot uih; +UIHotspot uihcancel; +UIText ti; +extern char HelpText1[]; +extern char HelpText2[]; +extern char HelpText3[]; +extern char HelpText4[]; +char Auto_login_name[MAX_AUTO_LOGIN_STUFF_LEN]; +char Auto_login_pass[MAX_AUTO_LOGIN_STUFF_LEN]; +char Auto_login_addr[MAX_AUTO_LOGIN_STUFF_LEN]; +char Auto_login_port[MAX_AUTO_LOGIN_STUFF_LEN]; +char Multi_conn_dll_name[_MAX_PATH*2] = ""; +char PXO_hosted_lobby_name[100] = "global"; +bool Supports_score_api = false; +#ifdef USE_DIRECTPLAY +extern modem_list Modems_found[MAX_MODEMS]; +extern int Num_modems_found; +#endif +#define DLL_BRIEFING_FONT 1 +#define DLL_BIG_BRIEFING_FONT 2 +extern unsigned short nw_ListenPort; +extern ushort PXOPort; + + +void GetMultiAPI (multi_api *api) +{ + //make the compiler happy + bool (*fp_PlayerIsShipAllowed)(int,int) = PlayerIsShipAllowed; + api->objs=(int *)Objects; + api->rooms=(int *)Rooms; + api->terrain=(int *)Terrain_seg; + api->players=(int *)Players; + api->netgame=(int *)&Netgame; + api->netplayers=(int *)&NetPlayers; + api->ships = (int *)Ships; + // Fill in function pointers here. The order here must match the order on the + // DLL side + + api->fp[0]=(int *)SetUITextItemText; + api->fp[1]=(int *)NewUIWindowCreate; + api->fp[2]=(int *)NewUIWindowDestroy; + api->fp[3]=(int *)NewUIWindowOpen; + api->fp[4]=(int *)NewUIWindowClose; + api->fp[5]=(int *)TextCreate; + api->fp[6]=(int *)EditCreate; + api->fp[7]=(int *)ButtonCreate; + api->fp[8]=(int *)ListCreate; + api->fp[9]=(int *)ListRemoveAll; + api->fp[10]=(int *)ListAddItem; + api->fp[11]=(int *)ListRemoveItem; + api->fp[12]=(int *)ListSelectItem; + api->fp[13]=(int *)ListGetItem; + api->fp[14]=(int *)ListGetSelectedIndex; + api->fp[15]=(int *)EditSetText; + api->fp[16]=(int *)EditGetText; + api->fp[17]=(int *)DatabaseWrite; + api->fp[18]=(int *)DatabaseRead; + api->fp[19]=(int *)DoUI; + api->fp[20]=(int *)DoMessageBox; + api->fp[21]=(int *)DescentDefer; + api->fp[22]=(int *)MonoPrintf; + api->fp[23]=(int *)DestroyStringTable; + api->fp[24]=(int *)CreateStringTable; + api->fp[25]=(int *)NewUIGameWindowCreate; + api->fp[26]=(int *)NewUIGameWindowDestroy; + api->fp[27]=(int *)NewUIGameWindowOpen; + api->fp[28]=(int *)NewUIGameWindowClose; + api->fp[29]=(int *)SetScreenMode; + api->fp[30]=(int *)timer_GetTime; + api->fp[31]=(int *)TryToJoinServer; + api->fp[32]=(int *)MultiStartClient; + api->fp[33]=(int *)rend_GetRenderState; + api->fp[34]=(int *)LoadMission; + api->fp[35]=(int *)ddio_MakePath; + api->fp[36]=(int *)ddio_FindFileStart; + api->fp[37]=(int *)ddio_FindFileClose; + api->fp[38]=(int *)ddio_FindNextFile; + api->fp[39]=(int *)MultiStartServer; + api->fp[40]=(int *)ShowProgressScreen; + api->fp[41]=(int *)SearchForLocalGamesTCP; + api->fp[42]=(int *)nw_GetHostAddressFromNumbers; + api->fp[43]=(int *)NULL;//nw_GetProtocolType; + api->fp[44]=(int *)HotSpotCreate; + api->fp[45]=(int *)PollUI; + api->fp[46]=(int *)GetMissionName; + api->fp[47]=(int *)RemoveUITextItem; + api->fp[48]=(int *)CreateNewUITextItem; + api->fp[49]=(int *)mem_malloc_sub; + api->fp[50]=(int *)mem_free_sub; + api->fp[51]=(int *)CreateSplashScreen; + api->fp[52]=(int *)CloseSplashScreen; + api->fp[53]=(int *)UIConsoleGadgetCreate; + api->fp[54]=(int *)UIConsoleGadgetputs; + api->fp[55]=(int *)NewUIWindowSetFocusOnEditGadget; + api->fp[56]=(int *)OldEditCreate; + api->fp[57]=(int *)OldListCreate; + api->fp[58]=(int *)OldListRemoveAll; + api->fp[59]=(int *)OldListAddItem; + api->fp[60]=(int *)OldListRemoveItem; + api->fp[61]=(int *)OldListSelectItem; + api->fp[62]=(int *)OldListGetItem; + api->fp[63]=(int *)OldListGetSelectedIndex; + api->fp[64]=(int *)OldEditSetText; + api->fp[65]=(int *)OldEditGetText; + api->fp[66]=(int *)ToggleUICallback; + api->fp[67]=(int *)SearchForGamesPXO; + api->fp[68]=(int *)SetOldEditBufferLen; + api->fp[69]=(int *)NewUIWindowLoadBackgroundImage; + api->fp[70]=(int *)DeleteUIItem; + api->fp[71]=(int *)SearchForLocalGamesIPX; + api->fp[72]=(int *)HotSpotSetStates; + api->fp[73]=(int *)PlayerSetShipPermission; + api->fp[74]=(int *)fp_PlayerIsShipAllowed; +#ifdef USE_DIRECTPLAY + api->fp[75]=(int *)dp_InitDirectPlay; + api->fp[76]=(int *)dp_ListDirectPlayGames; + api->fp[77]=(int *)dp_GetModemChoices; +#else + api->fp[75]=(int *)NULL; + api->fp[76]=(int *)NULL; + api->fp[77]=(int *)NULL; +#endif + api->fp[78]=(int *)DatabaseReadInt; + api->fp[79]=(int *)DatabaseWriteInt; + api->fp[80]=(int *)DoScreenshot; + api->fp[81]=(int *)IsMissionMultiPlayable; + api->fp[82]=(int *)grtext_GetTextLineWidth; + api->fp[83]=(int *)GadgetDestroy; +#ifdef USE_DIRECTPLAY + api->fp[84]=(int *)dp_StartGame; + api->fp[85]=(int *)dp_EndGame; +#else + api->fp[84]=(int *)NULL; + api->fp[85]=(int *)NULL; +#endif + api->fp[86]=(int *)nw_Asyncgethostbyname; + api->fp[87]=(int *)PrintDedicatedMessage; + api->fp[88]=(int *)nw_ReccomendPPS; + api->fp[89]=(int *)DoMultiAllowed; + api->fp[90]=(int *)MultiDoConfigSave; + api->fp[91]=(int *)MultiDoConfigLoad; + api->fp[92]=(int *)MultiLoadSettings; + api->fp[93]=(int *)nw_DoReceiveCallbacks; + api->fp[94]=(int *)nw_SendWithID; + api->fp[95]=(int *)nw_UnRegisterCallback; + api->fp[96]=(int *)nw_RegisterCallback; + api->fp[97]=(int *)msn_CheckGetMission; + api->fp[98]=(int *)MultiGameOptionsMenu; + api->fp[99]=(int *)mod_FreeModule; + api->fp[100]=(int *)mod_GetLastError; + api->fp[101]=(int *)mod_GetSymbol; + api->fp[102]=(int *)mod_LoadModule; + api->fp[103]=(int *)dCurrentPilotName; + api->fp[104]=(int *)UpdateAndPackGameList; + api->fp[105]=(int *)MultiLevelSelection; + api->fp[106]=(int *)DoPlayerMouselookCheck; + api->fp[107]=(int *)CheckMissionForScript; + api->fp[108]=(int *)ShowNetgameInfo; + api->fp[109]=(int *)GetRankIndex; + api->fp[110]=(int *)CheckGetD3M; + + // Variable pointers + api->vp[0]=(int *)&Player_num; + api->vp[1]=(int *)Tracker_id; + api->vp[2]=(int *)&Game_is_master_tracker_game; + api->vp[3]=(int *)&Game_mode; + api->vp[4]=(int *)NULL;//Current_pilot; no longer a struct + api->vp[5]=(int *)Base_directory; + api->vp[6]=(int *)&MultiDLLGameStarting; + api->vp[7]=(int *)MTPilotinfo; + api->vp[8]=(int *)&Num_network_games_known; + api->vp[9]=(int *)&Network_games; + api->vp[10]=(int *)&NewUIWindow_alpha; + api->vp[11]=(int *)HelpText1; + api->vp[12]=(int *)HelpText2; + api->vp[13]=(int *)HelpText3; + api->vp[14]=(int *)HelpText4; + api->vp[15]=(int *)Auto_login_name; + api->vp[16]=(int *)Auto_login_pass; + api->vp[17]=(int *)Auto_login_addr; + api->vp[18]=(int *)Auto_login_port; +#ifdef USE_DIRECTPLAY + api->vp[19]=(int *)&Num_directplay_games; + api->vp[20]=(int *)Directplay_sessions; + api->vp[21]=(int *)&DP_active; + api->vp[22]=(int *)&Use_DirectPlay; + api->vp[23]=(int *)&Modems_found; + api->vp[24]=(int *)&Num_modems_found; +#else + static bool _dummyUse_DirectPlay = false; + api->vp[19]=(int *)NULL; + api->vp[20]=(int *)NULL; + api->vp[21]=(int *)NULL; + api->vp[22]=(int *)&_dummyUse_DirectPlay; + api->vp[23]=(int *)NULL; + api->vp[24]=(int *)NULL; +#endif + api->vp[25]=(int *)&Dedicated_server; + api->vp[26]=(int *)&TCP_active; + api->vp[27]=(int *)&IPX_active; + api->vp[28]=(int *)nw_ListenPort; + api->vp[29]=(int *)&Multi_Gamelist_changed; + api->vp[30]=(int *)PXO_hosted_lobby_name; + api->vp[31]=(int *)&Supports_score_api; + api->vp[32]=(int *)PXOPort; + //Jeff: Linux dies if you try to free a DLL/so on + //atexit, these should be freed during game sequencing + //anyway. + //atexit(FreeMultiDLL); + +} +// Frees the dll if its in memory +void FreeMultiDLL () +{ + if (!MultiDLLHandle.handle) + return; + if (DLLMultiClose) + DLLMultiClose (); + mod_FreeModule(&MultiDLLHandle); + //Try deleting the file now! + if(!ddio_DeleteFile(Multi_conn_dll_name)) + { + mprintf((0,"Couldn't delete the tmp dll")); + } + DLLMultiCall=NULL; + DLLMultiInit=NULL; + DLLMultiClose=NULL; +} +// Loads the Multi dll. Returns 1 on success, else 0 on failure +int LoadMultiDLL (char *name) +{ + static int first=1; + char lib_name[_MAX_PATH*2]; + char dll_name[_MAX_PATH*2]; + char tmp_dll_name[_MAX_PATH*2]; + char dll_path_name[_MAX_PATH*2]; + MultiFlushAllIncomingBuffers(); + + //Delete old dlls + if (MultiDLLHandle.handle) + FreeMultiDLL(); + + ddio_MakePath(dll_path_name,Base_directory,"online","*.tmp",NULL); + ddio_DeleteFile(dll_path_name); + //Make the hog filename + ddio_MakePath(lib_name,Base_directory,"online",name,NULL); + strcat(lib_name,".d3c"); + //Make the dll filename + #if defined (WIN32) + sprintf(dll_name,"%s.dll",name); + #elif defined (MACINTOSH) + sprintf(dll_name,"%s.msl",name); + #else + sprintf(dll_name,"%s.so",name); + #endif + + //Open the hog file + if(!cf_OpenLibrary(lib_name)) + { + ddio_MakePath(tmp_dll_name,Base_directory,"online",name,NULL); + strcat(tmp_dll_name,".d3c"); + Multi_conn_dll_name[0] = NULL; + goto loaddll; + } + //get a temp file name + if(!ddio_GetTempFileName(Descent3_temp_directory,"d3c",tmp_dll_name)) + { + return 0; + } + //Copy the DLL +// ddio_MakePath(dll_path_name,Base_directory,"online",tmp_dll_name,NULL); + if(!cf_CopyFile(tmp_dll_name,dll_name)) + { + mprintf((0,"DLL copy failed!\n")); + return 0; + } + strcpy(Multi_conn_dll_name,tmp_dll_name); +loaddll: + + if(!mod_LoadModule(&MultiDLLHandle,tmp_dll_name)) + { + int err = mod_GetLastError(); + mprintf ((0,"You are missing the DLL %s!\n",name)); + return 0; + } + + DLLMultiInit=(DLLMultiInit_fp)mod_GetSymbol(&MultiDLLHandle,"DLLMultiInit",4); + if (!DLLMultiInit) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLMultiInit!\n")); + Int3(); + FreeMultiDLL (); + return 0; + } + DLLMultiCall=(DLLMultiCall_fp)mod_GetSymbol(&MultiDLLHandle,"DLLMultiCall",4); + if (!DLLMultiCall) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLMultiCall!\n")); + Int3(); + FreeMultiDLL (); + return 0; + } + DLLMultiClose=(DLLMultiClose_fp)mod_GetSymbol(&MultiDLLHandle,"DLLMultiClose",0); + if (!DLLMultiClose) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLMultiClose!\n")); + Int3(); + FreeMultiDLL (); + return 0; + } + + if (first) + { + //Jeff: Linux dies if you try to free a DLL/so during atexit + //for some reason. The dll/so should be freed during + //game sequencing anyway. + //atexit (FreeMultiDLL); + first=0; + } + void *api_fp; + api_fp=(void *)GetMultiAPI; + + if(Auto_login_name[0]) + { + Database->write("TrackerLogin",Auto_login_name,strlen(Auto_login_name)+1); + } + if(Auto_login_pass[0]) + { + Database->write("TrackerPassword",Auto_login_pass,strlen(Auto_login_pass)+1); + } + if(!Dedicated_server) + { + strcpy(PXO_hosted_lobby_name,"global"); + } + DLLMultiInit((int *)api_fp); + + if(Supports_score_api) + { + DLLMultiScoreCall=(DLLMultiScoreCall_fp)mod_GetSymbol(&MultiDLLHandle,"DLLMultiScoreEvent",8); + + if (!DLLMultiScoreCall) + { + int err = mod_GetLastError(); + mprintf ((0,"Couldn't get a handle to the dll function DLLMultiScoreCall!\n")); + Int3(); + Supports_score_api = false; + } + } + return 1; +} +// The chokepoint function to call the dll function +void CallMultiDLL (int eventnum) +{ + if (MultiDLLHandle.handle && DLLMultiCall) + DLLMultiCall (eventnum); +} +void SetUITextItemText(UITextItem *uit,char *newtext,unsigned int color) +{ + //This function is currently broken! + strcpy(uit->m_Text,newtext); + uit->set_color(color); +} +void * NewUIWindowCreate(int x, int y, int w, int h, int flags) +{ + NewUIWindow *newwin; + newwin = new NewUIWindow; + newwin->Create(x,y,w,h,flags); + return newwin; +} +void NewUIWindowDestroy(NewUIWindow *deswin) +{ + deswin->Destroy(); +} +void NewUIWindowOpen(NewUIWindow *deswin) +{ + deswin->Open(); +} +void NewUIWindowClose(NewUIWindow *deswin) +{ + deswin->Close(); +} +void * TextCreate(UIWindow *parentwin,UITextItem * textitem, int x, int y, int flags) +{ + UIText *newtext; + newtext = new UIText; + newtext->Create(parentwin,textitem,x,y,flags); + return newtext; +} +void TextSetTitle(UIText *text,UITextItem *textitem) +{ + text->SetTitle(textitem); +} +void * EditCreate(UIWindow *parentwin, int id, int x, int y, int w, int h, int flags) +{ + NewUIEdit *newedit; + newedit = new NewUIEdit; + newedit->Create(parentwin,id,x,y,w,h,flags); + return newedit; +} +void EditSetText(NewUIEdit * item,char *newtext) +{ + item->SetText(newtext); +} +void EditGetText(NewUIEdit * item,char *buff,int len) +{ + item->GetText(buff,len); +} +void * ButtonCreate(UIWindow *parentwin, int id,UITextItem * titleitem, int x, int y, int w, int h, int flags) +{ + UIButton *newbutt; + newbutt = new UIButton; + newbutt->Create(parentwin,id,titleitem,x,y,w,h,flags); + return newbutt; +} +void *ListCreate(UIWindow *parentwin, int id, int x, int y, int w, int h, int flags) +{ + NewUIListBox *newlist; + newlist = new NewUIListBox; + newlist->Create(parentwin,id,x,y,w,h,flags); + newlist->SetSelectedColor(UICOL_LISTBOX_HI); + newlist->SetHiliteColor(UICOL_LISTBOX_HI); + return newlist; +} +void ListRemoveAll(UIListBox *item) +{ + item->RemoveAll(); +} +void ListAddItem(UIListBox *item,UITextItem * uitext) +{ + item->AddItem(uitext); +} +void ListRemoveItem(UIListBox * item,UITextItem * txtitem) +{ + item->RemoveItem(txtitem); +} +void ListSelectItem(UIListBox * item,UITextItem * txtitem) +{ + item->SelectItem(txtitem); +} +char *ListGetItem(UIListBox * item,int index) +{ + UITextItem *ui_item; + ui_item = (UITextItem *)item->GetItem(index); + if(ui_item) return (char *)ui_item->GetBuffer(); + else + { + mprintf((0,"No listbox item found for index %d\n",index)); + return ""; + } +} +int ListGetSelectedIndex(UIListBox * item) +{ + return item->GetSelectedIndex(); +} +void ListSetSelectedIndex(UIListBox *item,int index) +{ + item->SetSelectedIndex(index); +} +void DatabaseRead(const char *label, char *entry, int *entrylen) +{ + Database->read(label,entry,entrylen); +} +void DatabaseWrite(const char *label, const char *entry, int entrylen) +{ + Database->write(label,entry,entrylen); +} +void DatabaseReadInt(const char *label, int *val) +{ + Database->read_int(label,val); + mprintf((0,"Read int: %s:%d\n",label,*val)); +} +void DatabaseWriteInt(const char *label, int val) +{ + mprintf((0,"Writing int: %s:%d\n",label,val)); + Database->write(label,val); +} +void DescentDefer(void) +{ + Descent->defer(); +} +void * NewUIGameWindowCreate(int x, int y, int w, int h, int flags) +{ + NewUIGameWindow *newgamewin; + newgamewin = new NewUIGameWindow; + newgamewin->Create(x,y,w,h,flags); + return newgamewin; +} +void NewUIGameWindowDestroy(NewUIGameWindow * item) +{ + item->Destroy(); +} +void NewUIGameWindowOpen(NewUIGameWindow * item) +{ + item->Open(); +} +void NewUIGameWindowClose(NewUIGameWindow * item) +{ + item->Close(); +} +void * HotSpotCreate(UIWindow *parentwin, int id, int key, UIItem * txtitemoff, UIItem * txtitemon, int x, int y, int w, int h, int flags) +{ + UIHotspot *newhs; + newhs = new UIHotspot; + if(newhs) + newhs->Create(parentwin,id,key,txtitemoff,txtitemon,x,y,w,h,flags); + return newhs; +} +void HotSpotSetStates(UIHotspot *hs,UIItem *texton,UIItem *textoff) +{ + hs->SetStates(textoff,texton); +} +void *CheckBoxCreate(UIWindow *parent, int id, UIItem *title, int x, int y, int w, int h, int flags) +{ + UICheckBox *newcb; + newcb = new UICheckBox; + if(newcb) + newcb->Create(parent,id,title,x,y,w,h,flags); + return newcb; +} +void CheckBoxSetCheck(UICheckBox *cb,bool state) +{ + cb->SetCheck(state); +} +bool CheckBoxIsChecked(UICheckBox *cb) +{ + return cb->IsChecked(); +} +float UI_LastPoll = 0l; +#define MIN_FRAMETIME .033 +int PollUI(void) +{ + // this should poll UI_frame_result. + int result = -1; + + ui_ShowCursor(); + + //Limit this to a fixed framerate + if(UI_LastPoll) + { + if((timer_GetTime()-UI_LastPoll)defer(); + DoUIFrame(); + rend_Flip(); + result = GetUIFrameResult(); + if(result != -1) + { + ui_HideCursor(); + ui_Flush(); + ddio_KeyFlush(); + } + return result; +} +void * CreateNewUITextItem(char *newtext,unsigned int color,int font) +{ + UITextItem *new_text_item; + if(font==-1) + { + new_text_item = new UITextItem(newtext); + } + else + { + switch(font) + { + case DLL_BRIEFING_FONT: + new_text_item = new UITextItem(BRIEFING_FONT,newtext); + break; + case DLL_BIG_BRIEFING_FONT: + new_text_item = new UITextItem(BIG_BRIEFING_FONT,newtext); + break; + default: + new_text_item = new UITextItem(BRIEFING_FONT,newtext); + } + } + if(new_text_item) + new_text_item->set_color(color); + return new_text_item; +} +void RemoveUITextItem(void *item) +{ + UITextItem *old_text_item = (UITextItem *)item; + delete old_text_item; +} +void * CreateNewUIBmpItem(int handle,ubyte alpha) +{ + UIBitmapItem *new_bmp_item; + new_bmp_item = new UIBitmapItem(handle,alpha); + return new_bmp_item; +} +int GetUIItemWidth(void *item) +{ + UIItem *i = (UIItem *)item; + return i->width(); +} +int GetUIItemHeight(void *item) +{ + UIItem *i = (UIItem *)item; + return i->height(); +} +void RemoveUIBmpItem(void *item) +{ + UIBitmapItem *old_bmp_item = (UIBitmapItem *)item; + delete old_bmp_item; +} +void NewUIMessageBoxCreate(int x, int y, int w, int flags) +{ + messageb.Create(x,y,w,flags); +} +void NewUIMessageBoxDestroy() +{ + +} +void NewUIMessageBoxOpen() +{ + messageb.Open(); +} +void NewUIMessageBoxClose() +{ + +} +static bool splash_screen_up = false; +void CreateSplashScreen(char *msg,int usecancel) +{ + if(splash_screen_up) + return; + splash_screen_up = true; + ti_cancel_on = UITextItem(TXT_CANCEL,UICOL_HOTSPOT_HI); + ti_cancel_off = UITextItem(TXT_CANCEL,UICOL_HOTSPOT_LO); + ti_msg = UITextItem(msg); + messageb.Create(0,0,384,UIF_CENTER); + ti.Create(&messageb,&ti_msg,20,50,UIF_CENTER); + + if(usecancel) + { + uihcancel.Create(&messageb,99,KEY_ESC,&ti_cancel_off,&ti_cancel_on,1,75,100,15,UIF_CENTER); + } + messageb.Open(); +} +void CloseSplashScreen(void) +{ + if(splash_screen_up) + { + messageb.Close(); + messageb.Destroy(); + splash_screen_up = false; + } + else + return; +} +void * UIConsoleGadgetCreate(UIWindow * parentid, int id, int x, int y, int font, int cols, int rows, int flags) +{ + UIConsoleGadget *newconsole; + newconsole = new UIConsoleGadget; + newconsole->Create(parentid,id,x,y,BRIEFING_FONT,cols,rows,flags); + return newconsole; +} +void UIConsoleGadgetputs(UIConsoleGadget * item, const char *str) +{ + unsigned char r = 0; + unsigned char g = 230; + unsigned char b = 0; + if(*str==1) + { + str++; + r = *str; + str++; + g = *str; + str++; + b = *str; + str++; + } + item->puts(GR_RGB(r,g,b),str); +} +void NewUIWindowSetFocusOnEditGadget(UIEdit * item,UIWindow * parent) +{ + item->Activate(); +} +void * OldListCreate(UIWindow * parentitem, int id, int x, int y, int w, int h, int flags) +{ + UIListBox *newoldlist; + newoldlist = new UIListBox; + newoldlist->Create(parentitem,id,x,y,w,h,flags); + newoldlist->SetSelectedColor(UICOL_LISTBOX_HI); + newoldlist->SetHiliteColor(UICOL_LISTBOX_HI); + return newoldlist; +} +void OldListRemoveAll(UIListBox * item) +{ + item->RemoveAll(); +} +void OldListAddItem(UIListBox * item,UITextItem * uitext) +{ + item->AddItem(uitext); +} +void OldListRemoveItem(UIListBox * item,UITextItem * txtitem) +{ + item->RemoveItem(txtitem); +} +void OldListSelectItem(UIListBox * item,UITextItem * txtitem) +{ + item->SelectItem(txtitem); +} +char *OldListGetItem(UIListBox * item,int index) +{ + UITextItem * ti = (UITextItem *)item->GetItem(index); + if(ti) + return (char *)ti->GetBuffer(); + else + { + mprintf((0,"No listbox item found for index %d\n",index)); + return ""; + } +} +int OldListGetSelectedIndex(UIListBox * item) +{ + return item->GetSelectedIndex(); +} +void *OldEditCreate(UIWindow * parentitem, int id, int x, int y, int w, int h, int flags) +{ + UIEdit * newoldedit; + newoldedit = new UIEdit; + newoldedit->Create(parentitem,id,x,y,w,h,flags); + return newoldedit; +} +void OldEditSetText(UIEdit * item,char *newtext) +{ + item->SetText(newtext); +} +void OldEditGetText(UIEdit * item,char *buff,int len) +{ + item->GetText(buff,len); +} +static void (*DLLInit_old_ui_callback)() = NULL; +void ToggleUICallback(int state) +{ + + if(state==0) + { + if(DLLInit_old_ui_callback==NULL) + { + DLLInit_old_ui_callback = GetUICallback(); + SetUICallback(NULL); + } + } + else + { + if(DLLInit_old_ui_callback!=NULL) + { + SetUICallback(DLLInit_old_ui_callback); + DLLInit_old_ui_callback = NULL; + } + } +} +void SetOldEditBufferLen(UIEdit * item,int len) +{ + item->SetBufferLen(len); +} +void NewUIWindowLoadBackgroundImage(NewUIWindow * item,const char *image_name) +{ + item->LoadBackgroundImage(image_name); +} +void DeleteUIItem(void *delitem) +{ + + delete delitem; +} +void GadgetDestroy(UIGadget *item) +{ + item->Destroy(); +} +void *SliderCreate(UIWindow *parent, int id, int x, int y, int flags) +{ + NewUISlider *slid; + slid = new NewUISlider; + slid->Create(parent,id,x,y,flags); + return (void *)slid; +} +void SliderSetRange(UISlider *slider,int range) +{ + slider->SetRange(range); +} +int SliderGetRange(UISlider *slider) +{ + return slider->GetRange(); +} +void SliderSetPos(UISlider *slider,int pos) +{ + slider->SetPos(pos); +} +int SliderGetPos(UISlider *slider) +{ + return slider->GetPos(); +} +void SliderSetSelectChangeCallback(UISlider *slider,void (*fn)(int)) +{ + slider->SetSelectChangeCallback(fn); +} +void SliderSetSelectChangeCallbackWData(UISlider *slider,void (*fn)(int,void *),void *ptr) +{ + slider->SetSelectChangeCallback(fn,ptr); +} +void GetUIItemPosition(UIObject *item,int *x,int *y,int *w,int *h) +{ + if(x) + *x = item->X(); + if(y) + *y = item->Y(); + if(w) + *w = item->W(); + if(h) + *h = item->H(); +} +#define CDT_EVT_PLAYERKILLED 1 +#define CDT_EVT_CONNECT 2 +#define CDT_EVT_GAME_OVER 3 +#define CDT_EVT_GAME_STARTING 4 +#define CDT_EVT_FRAME 5 +#define CDT_EVT_PLAYER_DIED 6 +#define CDT_EVT_PLAYER_JOINS 7 +#define CDT_EVT_PLAYER_LEFT 8 +#define CDT_EVT_SET_SCORE_IP 9 +#define CDT_EVT_SET_GAMEID 10 +#define CALLSIGN_LEN 19 +typedef struct player_killed +{ + char killer_name[CALLSIGN_LEN+1]; + char killed_name[CALLSIGN_LEN+1]; +}player_killed; +typedef struct player_score_matrix +{ + int num_players; + char name[MAX_NET_PLAYERS][CALLSIGN_LEN+1]; + short deaths[MAX_NET_PLAYERS]; + short kills[MAX_NET_PLAYERS]; +}player_score_matrix; +// The chokepoint function to call the dll function +void CallMultiScoreDLL (int eventnum,void *data) +{ + if (MultiDLLHandle.handle && DLLMultiCall) + DLLMultiScoreCall (eventnum,data); +} +//Determine if the Score API functions should run +bool ScoreAPIShouldBail() +{ + if(!(Game_mode & GM_MULTI)) + return true; + if(Supports_score_api) + return false; + return true; +} +void ScoreAPIPlayerKilled(int killer,int killed) +{ + if(ScoreAPIShouldBail()) + return; + player_killed pk; + strcpy(pk.killed_name,Players[killed].callsign); + strcpy(pk.killer_name,Players[killer].callsign); + + CallMultiScoreDLL(CDT_EVT_PLAYERKILLED,&pk); +} +void ScoreAPIPlayerEntered(int playernum) +{ + if(ScoreAPIShouldBail()) + return; + CallMultiScoreDLL(CDT_EVT_CONNECT,Players[playernum].callsign); +} +void ScoreAPIPlayerLeft(int playernum) +{ + if(ScoreAPIShouldBail()) + return; + CallMultiScoreDLL(CDT_EVT_PLAYER_LEFT,Players[playernum].callsign); +} +void ScoreAPIPlayerJoin(int playernum) +{ + if(ScoreAPIShouldBail()) + return; + CallMultiScoreDLL(CDT_EVT_PLAYER_JOINS,Players[playernum].callsign); +} +void ScoreAPIGameOver() +{ + player_score_matrix sm; + + if(ScoreAPIShouldBail()) + return; + + + + int counted=0; + for (int i=0;i +#endif + +#include "pstypes.h" +#include "manage_external.h" +#include "CFILE.H" +#include "networking.h" +#include "descent.h" //for MSN_NAMELEN +#include "byteswap.h" + +#define NETGAME_NAME_LEN 32 +#define NETGAME_SCRIPT_LEN 32 + +#define LR_CLIENT 0 +#define LR_SERVER 1 + +// Max number of network players +#define MAX_NET_PLAYERS 32 + +#define MAX_GAME_DATA_SIZE (MAX_PACKET_SIZE-4) + +//Stuff for the connection DLLs +#define MT_EVT_LOGIN 1 +#define MT_EVT_FIRST_FRAME 2 +#define MT_EVT_FRAME 3 +#define MT_EVT_GAME_OVER 4 +#define MT_EVT_GET_HELP 5 +#define MT_AUTO_LOGIN 6 +#define MT_AUTO_START 7 +#define MT_RETURN_TO_GAME_LIST 8 + +// Net sequencing + +#define NETSEQ_PREGAME 0 // Not in a game +#define NETSEQ_WAITING_FOR_LEVEL 1 // Client - waiting for level name and checksum +#define NETSEQ_LEVEL_START 2 // Waiting for level object stuff +#define NETSEQ_NEED_GAMETIME 3 // Need to ask for gametime +#define NETSEQ_WAIT_GAMETIME 4 // Waiting for server response in gametime +#define NETSEQ_REQUEST_PLAYERS 5 // Requesting players +#define NETSEQ_PLAYERS 6 // Getting player stuff +#define NETSEQ_REQUEST_BUILDINGS 7 // Requesting buildings +#define NETSEQ_BUILDINGS 8 // Getting buildings +#define NETSEQ_REQUEST_OBJECTS 9 // Getting objects +#define NETSEQ_OBJECTS 10 // Got objects +#define NETSEQ_REQUEST_WORLD 11 // Getting world states +#define NETSEQ_WORLD 12 // Got World +#define NETSEQ_PLAYING 13 // Playing the game +#define NETSEQ_LEVEL_END 14 // At the post-level stage + + +// Netplayer flags +#define NPF_CONNECTED 1 // This player is connected +#define NPF_SERVER 2 // This player is the server +#define NPF_MASTER_TRACKER 4 // This is a master tracker game +#define NPF_MT_READING_PILOT 8 // Waiting for a response from the mastertracker about this pilot's stats +#define NPF_MT_WRITING_PILOT 16 // Waiting to finish updating the mastertracker with this pilot's stats +#define NPF_MT_HAS_PILOT_DATA 32 // We got data from the mastertracker +#define NPF_WROTE_RANK 64 // We told the clients about this clients rank + +typedef struct +{ + network_address addr; + int flags; + SOCKET reliable_socket; + float last_packet_time; + float packet_time; // for making sure we don't get position packets out of order + unsigned int total_bytes_sent; + unsigned int total_bytes_rcvd; + unsigned int secret_net_id; // We use this to determine who we are getting packets from + int file_xfer_flags; // Are we sending,receiving, or neither + unsigned int file_xfer_total_len; // Total length of the file we are receiving + unsigned int file_xfer_pos; // Position for sending and/or receiving + unsigned int file_xfer_id; // File id that we are sending + unsigned int file_xfer_who; // Who the file is for + CFILE * file_xfer_cfile; // File handle for src/target file + ushort position_counter; // for making sure we don't get position packets out of order + char ship_logo[_MAX_PATH]; + char voice_taunt1[_MAX_PATH]; + char voice_taunt2[_MAX_PATH]; + char voice_taunt3[_MAX_PATH]; + char voice_taunt4[_MAX_PATH]; + ubyte custom_file_seq; + ubyte sequence; // where we are in the sequence chain + ubyte pps; + HANDLE hPlayerEvent; // player event to use for directplay + unsigned long dpidPlayer; // directplay ID of player created + float ping_time; + float last_ping_time; + ushort pilot_pic_id; + float percent_loss; + unsigned char digest[16]; +} netplayer; + +#define MISSION_NAME_LEN 50 + +typedef struct +{ + network_address addr; + char name[NETGAME_NAME_LEN]; + char mission[MSN_NAMELEN]; + char mission_name[MISSION_NAME_LEN]; + char scriptname[NETGAME_SCRIPT_LEN]; + ushort level_num; + ushort curr_num_players; + ushort max_num_players; + float server_response_time; + unsigned int flags; + float last_update; + bool dedicated_server; + ubyte difficulty; // Game difficulty level + unsigned int handle; +} network_game; + +// netgame flags +#define NF_TIMER 0x01 // This level will end when the timer runs out +#define NF_KILLGOAL 0x02 // This level will end when the number of kills reaches a certain point +#define NF_USE_ROBOTS 0x04 // This game uses robots +#define NF_EXIT_NOW 0x08 // This game needs to bail right now +#define NF_PEER_PEER 0x10 // This game is a psuedo peer-peer game, so send position packets to all clients. +#define NF_SENDROTVEL 0x20 // Use low resolution packets +#define NF_ALLOWGUIDEBOT 0x40 // Whether the Guide bot is allowed in the game +#define NF_DIRECTPLAY 0x80 // This game is a directplay game +#define NF_ATTACK_FRIENDLY 0x100 // Homers and gunboys will attack friendlies +#define NF_DAMAGE_FRIENDLY 0x200 // Friendly fire will cause damage +#define NF_USE_ACC_WEAP 0x400 // Use big weapon spheres against player ships. +#define NF_USE_SMOOTHING 0x800 // Smooth out positional movement via curve interpolation +#define NF_BRIGHT_PLAYERS 0x1000 // Bright players in netgame +#define NF_PERMISSABLE 0x2000 // Clients need server permission to fire +#define NF_RESPAWN_WAYPOINT 0x4000 // Players should use waypoints to respawn +#define NF_RANDOMIZE_RESPAWN 0x8000 // Powerups should move around randomly when respawning +#define NF_ALLOW_MLOOK 0x10000 //Allow mouse lookers +#define NF_TRACK_RANK 0x20000 // Track rankings for PXO +#define NF_COOP 0x40000 // This game is a cooperative game + + +typedef struct +{ + ushort server_version; // This is so client and server code matches + char name[NETGAME_NAME_LEN]; + char mission[MSN_NAMELEN]; + char mission_name[MISSION_NAME_LEN]; + char scriptname[NETGAME_SCRIPT_LEN]; + char server_config_name[PAGENAME_LEN]; + char connection_name[PAGENAME_LEN]; + network_address server_address; // The address of the server that we're talking to - not used if we are the server + + ubyte local_role; + ubyte server_sequence; + float last_server_time; // last time we got a packet from the server + ubyte packets_per_second; // how many packets per second we'll send out + int flags; + int timelimit; // how many minutes to play this level + int killgoal; // kill goal for this level + int respawn_time; + int max_players; + ubyte difficulty; // Game difficulty level + u_char digest[16]; +} netgame_info; + + +// Inline functions for extracting/packing multiplayer data +inline void MultiAddUbyte (ubyte element,ubyte *data,int *count) +{ + data[*count]=element; + *count+=sizeof(ubyte); +} + +inline void MultiAddByte (ubyte element,ubyte *data,int *count) +{ + data[*count]=element; + *count+=sizeof(ubyte); +} + +inline void MultiAddSbyte (sbyte element,ubyte *data,int *count) +{ + data[*count]=element; + *count+=sizeof(sbyte); +} + + +inline void MultiAddShort (short element,ubyte *data,int *count) +{ + *(short *)(data+*count)=INTEL_SHORT(element); + *count+=sizeof(short); +} + +inline void MultiAddUshort (ushort element,ubyte *data,int *count) +{ + *(ushort *)(data+*count)=INTEL_SHORT(element); + *count+=sizeof(ushort); +} + +inline void MultiAddInt (int element,ubyte *data,int *count) +{ + *(int *)(data+*count)=INTEL_INT(element); + *count+=sizeof(int); +} + +inline void MultiAddUint (uint element,ubyte *data,int *count) +{ + *(uint *)(data+*count)=INTEL_INT(element); + *count+=sizeof(uint); +} + + +inline void MultiAddFloat (float element,ubyte *data,int *count) +{ + *(float *)(data+*count)=INTEL_FLOAT(element); + *count+=sizeof(float); +} + +inline void MultiAddString (char *str,ubyte *data,int *count) +{ + ubyte len=strlen(str)+1; + + MultiAddByte (len,data,count); + memcpy (&data[*count],str,len); + *count+=len; +} + + +inline ubyte MultiGetUbyte (ubyte *data,int *count) +{ + ubyte element=(*(ubyte *)(data+*count)); + (*count)+=sizeof(ubyte); + return element; +} + +inline ubyte MultiGetByte (ubyte *data,int *count) +{ + ubyte element=(*(ubyte *)(data+*count)); + (*count)+=sizeof(ubyte); + return element; +} + +inline sbyte MultiGetSbyte (ubyte *data,int *count) +{ + sbyte element=(*(sbyte *)(data+*count)); + (*count)+=sizeof(sbyte); + return element; +} + + + +inline short MultiGetShort (ubyte *data,int *count) +{ + short element=(*(short *)(data+*count)); + *count+=sizeof(short); + return INTEL_SHORT(element); +} + +inline ushort MultiGetUshort (ubyte *data,int *count) +{ + ushort element=(*(ushort *)(data+*count)); + *count+=sizeof(short); + return INTEL_SHORT(element); +} + +inline int MultiGetInt (ubyte *data,int *count) +{ + int element=(*(int *)(data+*count)); + *count+=sizeof(int); + return INTEL_INT(element); +} + +inline uint MultiGetUint (ubyte *data,int *count) +{ + uint element=(*(uint *)(data+*count)); + *count+=sizeof(int); + return INTEL_INT(element); +} + +inline float MultiGetFloat (ubyte *data,int *count) +{ + float element=(*(float *)(data+*count)); + *count+=sizeof(float); + return INTEL_FLOAT(element); +} + +inline void MultiGetString (char *str,ubyte *data,int *count) +{ + ubyte len=MultiGetByte (data,count); + memcpy (str,&data[*count],len); + *count+=len; +} + +inline void MultiAddVector(vector v,ubyte *data,int *count) +{ + MultiAddFloat(v.x,data,count); + MultiAddFloat(v.y,data,count); + MultiAddFloat(v.z,data,count); +} + +inline vector MultiGetVector(ubyte *data,int *count) +{ + vector v; + + v.x = MultiGetFloat(data,count); + v.y = MultiGetFloat(data,count); + v.z = MultiGetFloat(data,count); + return v; + +} +#endif diff --git a/Descent3/multi_save_setting.cpp b/Descent3/multi_save_setting.cpp new file mode 100644 index 000000000..008d678fa --- /dev/null +++ b/Descent3/multi_save_setting.cpp @@ -0,0 +1,306 @@ +/* + * $Logfile: /DescentIII/main/multi_save_setting.cpp $ + * $Revision: 12 $ + * $Date: 5/05/00 9:04a $ + * $Author: Matt $ + * + * Multiplayer settings save/load + * + * $Log: /DescentIII/main/multi_save_setting.cpp $ + * + * 12 5/05/00 9:04a Matt + * Allow ship names for the SHIPBAN command to have spaces, so the black + * pyro will work. + * + * 11 5/09/99 1:34p Kevin + * Added diffuculty level system to multiplayer + * + * 10 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 9 3/25/99 3:29p Jason + * added option to randomize powerup respawn points + * + * 8 2/05/99 1:17p Jason + * added some options + * + * 7 12/07/98 12:54p Kevin + * Added Accurate weapon collisions flag + * + * 6 12/01/98 12:47p Jason + * got rid of NF_DROPMISORDERED and added NF_USE_SMOOTHING + * + * 5 10/17/98 2:32p Kevin + * FIxed problem with banned users getting stuck on the ban message + * screen. + * + * 4 10/02/98 10:11a Kevin + * dealt with spaces in the name of values + * + * 3 9/23/98 6:33p Kevin + * Fixed load settings + * + * 2 9/23/98 2:54p Kevin + * Saved multi config and changed UI to conform + * + * 1 9/23/98 10:15a Kevin + * Initial Version + * + * $NoKeywords: $ + */ + +#include +#include "CFILE.H" +#include "multi.h" +#include "objinfo.h" +#include "ship.h" +#include "multi_save_settings.h" + +int MultiSaveSettings(char *filename) +{ + CFILE * cf; + char szoutput[MAX_MPS_LINE_LEN]; + int i; + cf = cfopen(filename,"wt"); + if(!cf) + return NULL; + sprintf(szoutput,"NAME\t%s",Netgame.name); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"MISSION\t%s",Netgame.mission); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"SCRIPT\t%s",Netgame.scriptname); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"PPS\t%d",Netgame.packets_per_second); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"PEERPEER\t%s",(Netgame.flags & NF_PEER_PEER)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"PERMISSABLE\t%s",(Netgame.flags & NF_PERMISSABLE)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"RANDOMIZERESPAWN\t%s",(Netgame.flags & NF_RANDOMIZE_RESPAWN)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"ROTVEL\t%s",(Netgame.flags & NF_SENDROTVEL)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"USESMOOTHING\t%s",(Netgame.flags & NF_USE_SMOOTHING)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"BRIGHTPLAYERS\t%s",(Netgame.flags & NF_BRIGHT_PLAYERS)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"MAXPLAYERS\t%d",Netgame.max_players); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"RESPAWNTIME\t%d",Netgame.respawn_time); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"KILLGOAL\t%d",Netgame.killgoal); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"TIMELIMIT\t%d",Netgame.timelimit); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"ACCWEAP\t%s",(Netgame.flags & NF_USE_ACC_WEAP)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"MLOOK\t%s",(Netgame.flags & NF_ALLOW_MLOOK)?"TRUE":"FALSE"); + cf_WriteString(cf,szoutput); + sprintf(szoutput,"DIFFICULTY\t%d",Netgame.difficulty); + cf_WriteString(cf,szoutput); + + for(i=0;i4) || (Netgame.difficulty<0) ) + Netgame.difficulty = 0; + } + else + { + mprintf((0,"Unknown line in multiplayer config file %s\t%s\n",toklabel,tokval)); + } + }; + return 1; + +} + diff --git a/Descent3/multi_save_settings.h b/Descent3/multi_save_settings.h new file mode 100644 index 000000000..2101a3e61 --- /dev/null +++ b/Descent3/multi_save_settings.h @@ -0,0 +1,47 @@ +/* + * $Logfile: /DescentIII/main/lib/multi_save_settings.h $ + * $Revision: 3 $ + * $Date: 9/23/98 6:33p $ + * $Author: Kevin $ + * + * Multiplayer settings save/load + * + * $Log: /DescentIII/main/lib/multi_save_settings.h $ + * + * 3 9/23/98 6:33p Kevin + * Fixed load settings + * + * 2 9/23/98 2:55p Kevin + * Saved multi config and changed UI to conform + * + * 1 9/23/98 10:42a Kevin + * + * + * Initial Version + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_SAVE_SETTINGS_HEADER +#define _MULTI_SAVE_SETTINGS_HEADER + +#define MAX_MPS_LINE_LEN 200 + + +int MultiSaveSettings(char *filename); +int MultiLoadSettings(char *filename); + + + + + + + + + + + + + + +#endif \ No newline at end of file diff --git a/Descent3/multi_server.cpp b/Descent3/multi_server.cpp new file mode 100644 index 000000000..5c9397ff1 --- /dev/null +++ b/Descent3/multi_server.cpp @@ -0,0 +1,3358 @@ +/* + * $Logfile: /DescentIII/Main/multi_server.cpp $ + * $Revision: 276 $ + * $Date: 9/19/01 7:26p $ + * $Author: Matt $ + * + * Multiplayer server code + * + * $Log: /DescentIII/Main/multi_server.cpp $ + * + * 276 9/19/01 7:26p Matt + * Fix for clients not hearing when the server player took omega damage. + * + * 275 9/10/01 3:39p Matt + * Always send the server position, because we always say that the server + * is visible. This should fix some or all ghost ship problems. + * + * 274 2/19/00 6:45p Jason + * fixed dumb -usesmoothing bug...server was turning it on when it + * shouldn't have + * + * 273 9/28/99 7:08a Kevin + * Fixed bug with MultiAddVector calls getting passes the wrong pointers + * + * 272 9/16/99 2:37p Kevin + * don't send server or observer position updates + * + * 271 9/02/99 3:35p Jason + * send secondary fire reliable in a C/S game + * + * 270 9/01/99 4:12p Kevin + * Byte ordering fixes for the macintosh + * + * 269 8/20/99 9:45a Kevin + * Rankings changes! + * + * 268 8/17/99 1:27p Kevin + * fixed wacky rank bug + * + * 267 8/02/99 12:25p Jason + * fixed bug in ranking system + * + * 266 7/20/99 12:57p Jason + * added MOTD to server + * + * 265 7/08/99 5:45p Jason + * don't track rank if flag isn't set + * + * 264 5/24/99 11:58p Matt + * Removed Jason's previous fix because he and I decided it was too risky + * to change the night before the (hopefully) final build. + * + * 263 5/24/99 10:19p Jason + * fixed objects disappearing over terrain + * + * 262 5/23/99 7:37p Jason + * changed rankins so it is a little more fair to players + * + * 261 5/23/99 3:05a Jason + * fixed bug with player rankings not being updated correctly + * + * 260 5/22/99 3:10a Jason + * fixed weird invisible powerup bug + * + * 259 5/22/99 12:33a Jason + * added sending of physics flags upon joining + * + * 258 5/21/99 7:08p Jason + * fixed some weird multiplayer rejoin anomalies + * + * 257 5/20/99 4:55p Jason + * added heartbeats to server + * + * 256 5/20/99 2:53p Jason + * made autowaypoints work in coop + * + * 255 5/20/99 2:48a Matt + * Auto-waypoints now stored & used per player. Manual waypoints will all + * players, once Jason makes it work. + * + * 254 5/19/99 4:58p Jason + * changes for ranking system + * + * 253 5/19/99 12:11p Jason + * fixed some issues with multiplayer + * + * 252 5/17/99 6:27p Jason + * added reliable overrun message + * + * 251 5/13/99 7:18p Jason + * upped the limit of players disconnecting while joining + * + * 250 5/12/99 7:45p Jason + * fixed boneheaded powerup repositioning bug...I should be shot. + * + * 249 5/12/99 4:19p Jason + * pick a random start position for server + * + * 248 5/12/99 2:42p Jason + * changed default netgame difficult to hotshot (1.0 scalar) + * + * 247 5/12/99 1:57p Jason + * fixed yet more buggy/ugly code + * + * 246 5/11/99 9:42p Jeff + * send tracker id of players to other players + * + * 245 5/11/99 10:59a Kevin + * Ship allow/dissalow works now! + * + * 244 5/10/99 10:23p Ardussi + * changes to compile on Mac + * + * 243 5/10/99 8:28p Jason + * adjusted blackshark popping bug + * + * 242 5/10/99 5:35p Kevin + * New command line options for heat and scoring API enhancements + * + * 241 5/10/99 3:13a Jeff + * put in missing prototype + * + * 240 5/10/99 2:54a Jason + * fixed player spawning in opposite team waypoints + * + * 239 5/10/99 1:45a Jason + * took out robot and turret position counters + * + * 238 5/09/99 1:34p Kevin + * Added diffuculty level system to multiplayer + * + * 237 5/08/99 12:10p Kevin + * + * 236 5/08/99 12:09p Kevin + * fixed dedicated server num team selection + * + * 235 5/07/99 6:59p Jason + * fixed marker/guided nonvis issues + * + * 234 5/07/99 2:51p Jason + * fixed a bunch of endlevel multiplayer issues + * + * 233 5/06/99 3:26a Kevin + * heat scoring stuff + * + * 232 5/05/99 5:42p Jason + * fixed various multiplayer issues, including sequencing bugs and cheat + * prevention + * + * 231 5/03/99 4:22p Jeff + * changed byte var to ubyte type for Linux + * + * 230 5/03/99 2:36p Jason + * changes for multiplayer games + * + * 229 5/03/99 8:38a Jeff + * handle world states for room's with goal/special flag change + * + * 228 5/02/99 2:32p Kevin + * fixed various dedicated server problems. + * + * 227 5/01/99 4:06p Jeff + * fixed #incldue for Linux + * + * 226 4/30/99 7:50p Jason + * setups urgent/non urgent reliable stuff better + * + * 225 4/30/99 5:06p Kevin + * misc dedicated server, networking and low memory enhancements + * + * 224 4/29/99 11:02p Jeff + * added the ability for the server to set audio taunt delay time via + * command line option and/or dedicated server console + * + * 223 4/29/99 12:23p Jason + * fixed blackshark related problem with powerups + * + * 222 4/29/99 2:00a Chris + * Added the portal blockage support + * + * 221 4/28/99 2:27a Jeff + * added buddybot world states + * + * 220 4/27/99 12:08p Kevin + * fixed team count dialog + * + * 219 4/25/99 5:20p Jason + * fixed some more coop bugs + * + * 218 4/23/99 5:32p Kevin + * Fixed a few mission bugs. + * + * 217 4/23/99 3:33p Kevin + * mission file/multiplayer mod keyword system + * + * 216 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 215 4/20/99 3:32p Kevin + * made it possible for an edit box to not have a cancel button... used it + * for choosing team count for server + * + * 214 4/17/99 4:34p Jason + * changes for robot/turret tracking + * + * 212 4/08/99 11:08a Jason + * cleaned up a variable naming problem + * + * 211 4/06/99 6:24p Jason + * various fixes for multiplayer + * + * 210 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 209 4/03/99 6:34p Jeff + * fixed bug with calling BOA_IsVisible, in which it was passing in an + * object number instead of a room number + * + * 208 4/02/99 6:31p Jason + * Fixed multiplayer powerup/player start bugs + * + * 207 3/29/99 7:31p Jason + * added better handling of non-vis generic objects + * + * 206 3/27/99 2:20p Jason + * transmit rankings when a player joins + * + * 205 3/25/99 3:29p Jason + * added option to randomize powerup respawn points + * + * 204 3/25/99 1:41p Jason + * took out mprintfs + * + * 203 3/25/99 11:20a Jason + * fixed fog state bug + * + * 202 3/24/99 1:41p Jeff + * some dedicated server fixups...ability to set number of teams + * + * 201 3/23/99 6:09p Kevin + * Added team count prompt for Jeff + * + * 200 3/22/99 6:22p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 199 3/16/99 12:06p Kevin + * Enabled rankings, but took out the hud message in the OEM build + * + * 198 3/11/99 9:10p Jeff + * fixed remaining known bugs in demo system (DemoWriteObjCreate being + * called twice and 0) + { + float t; + char adt_str[64],*p; + strcpy(adt_str,GameArgs[audiotauntdelayarg+1]); + p = adt_str; + while ( *p && (*p==' '||*p=='\t') ) p++; + t = atof(p); + if(t==0) + t = 5.0f; + + mprintf((0,"MULTI: Setting audio taunt delay time to %.2f seconds\n",t)); + taunt_SetDelayTime(t); + }else + { + taunt_SetDelayTime(5.0f); + } + + // Temporary name fix + Current_pilot.get_name(Players[Player_num].callsign); + + // setup the server's pilot id + Current_pilot.get_multiplayer_data(NULL,NULL,NULL,&NetPlayers[Player_num].pilot_pic_id); + + // Choose ship + char pshipmodel[PAGENAME_LEN]; + Current_pilot.get_ship(pshipmodel); + + Players[Player_num].ship_index=FindShipName (pshipmodel); + if (Players[Player_num].ship_index<0) + Players[Player_num].ship_index=0; + + SetGamemodeScript (scriptname,dedicated_server_num_teams); + + MultiFlushAllIncomingBuffers(); + #ifdef USE_DIRECTPLAY + dp_StartGame(Netgame.name); + #endif + + //gspy_StartGame(Netgame.name); + //We use this to identify clients.... + ps_srand(timer_GetTime()); + Secret_net_id = ps_rand()*ps_rand(); + LastPacketReceived = timer_GetTime(); + + // At this point we have a level checksum, so add the ships into the mix + for (int i=0;i=0 Number of teams supported for this mod & level + +#define SCRIPTBADFORMISSION -1 + +int CheckMissionForScript(char *mission,char *script,int dedicated_server_num_teams) +{ + char mod_keys[MAX_KEYWORDLEN]; + if(!GetDLLRequirements(script,mod_keys,MAX_KEYWORDLEN)) + { + return SCRIPTBADFORMISSION; + } + int teams = MissionGetKeywords(mission,mod_keys); + //Bad match! + if(teams==-1) + { + if(!Dedicated_server) + { + DoMessageBox(script,TXT_MSNNOTCOMPATIBLE,MSGBOX_OK); + return SCRIPTBADFORMISSION; + } + else + { + PrintDedicatedMessage(TXT_MSNNOTCOMPATIBLE); + PrintDedicatedMessage("\n"); + } + } + + + + //See how many teams this dll will allow. + int desired_teams = 1; + int max_teams,min_teams; + + GetDLLNumTeamInfo(script,&min_teams,&max_teams); + + if(min_teams > teams) + { + mprintf((0,"This multiplayer game requires more teams than the mission supports!\n")); + return SCRIPTBADFORMISSION; + } + //Use whatever is smaller, the dll's max teams, or what the missions supports + if(max_teams>teams) + max_teams = teams; + + if(Dedicated_server) + { + teams = min_teams; + + if(min_teams!=max_teams) + { + //they can set the number of teams for this game + //make sure it's valid + if(dedicated_server_num_teams>=min_teams && dedicated_server_num_teams<=max_teams) + { + teams = dedicated_server_num_teams; + }else + { + //invalid num teams + PrintDedicatedMessage(TXT_INVALIDNUMTEAMS,teams); + PrintDedicatedMessage("\n"); + } + } + + }else + { + if(min_teams!=max_teams) + { + char a_num_teams[5]; + char team_count_msg[300]; + sprintf(a_num_teams,"%d",min_teams); + sprintf(team_count_msg,"%s %s (%d - %d)",TXT_TEAMCOUNTPROMPT,script,min_teams,max_teams); +retry_team_count: + int res = DoEditDialog(team_count_msg,a_num_teams,4); + if(res) + { + desired_teams = atoi(a_num_teams); + if( (desired_teams > max_teams) || (desired_teams < min_teams) ) + { + DoMessageBox(script,TXT_INVALIDTEAMCOUNT,MSGBOX_OK); + goto retry_team_count; + } + else + { + teams = desired_teams; + } + } + else + { + return SCRIPTBADFORMISSION; + } + } + } + + return teams; +} + + + +// multi_check_listen() calls low level routines to see if we have a connection from a client we +// should accept. +void MultiCheckListen() +{ + int i; + network_address addr; + SOCKET sock = INVALID_SOCKET; + + // call routine which calls select to see if we need to check for a connect from a client + // by passing addr, we are telling check_for_listen to do the accept and return who it was from in + // addr. The + sock = nw_CheckListenSocket(&addr); + if ( sock != INVALID_SOCKET ) + { + // the connection was accepted. Find a free slot for this new player + for (i = 0; i < MAX_NET_PLAYERS; i++ ) + { + if (i==Player_num) + continue; + + if (!(NetPlayers[i].flags & NPF_CONNECTED)) + { + // This slot is free! + MultiSendConnectionAccepted (i,sock,&addr); + break; + } + } + + // if we didn't find a player, close the socket + if ( i == MAX_NET_PLAYERS ) + { + mprintf((0, "Got accept on my listen socket, but no free slots. Closing socket.\n")); + nw_CloseSocket(&sock); + } + } +} + +// Tells the client that we're done sending players +void MultiSendDonePlayers (int slot) +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending done players\n")); + + size=START_DATA (MP_DONE_PLAYERS,data,&count); + int num=MultiCountPlayers(); + MultiAddByte (num,data,&count); + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count); +} + +// Tells the client that we're done sending buildings +void MultiSendDoneBuildings (int slot) +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending done buildings\n")); + + size=START_DATA (MP_DONE_BUILDINGS,data,&count); + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count); +} + +// Tells the client that we're done sending objects +void MultiSendDoneObjects (int slot) +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending done objects\n")); + + // Take the level checksum and clone it. + MD5 *playermd5 = Level_md5->Clone(); + // Generate a random salt value and send it to the client. + int salt = ps_rand(); + // process the salt through the md5 + playermd5->MD5Update(salt); + // process the ships through the md5 + for (int i=0;iMD5Final(NetPlayers[slot].digest); + MD5::Destroy(playermd5); + + size=START_DATA (MP_DONE_OBJECTS,data,&count); + MultiAddInt(salt,data,&count); + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count); +} + +// Tells the client that we're done sending the world states +void MultiSendDoneWorldStates (int slot) +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending done world states\n")); + + size=START_DATA (MP_DONE_WORLD_STATES,data,&count); + END_DATA (count,data,size); + + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count); +} + +// Clears all the player markers belonging to a particular slot +void MultiClearPlayerMarkers (int slot) +{ + int m1=slot*2; + int m2=(slot*2)+1; + + for (int i=0;i<=Highest_object_index;i++) + { + if (Objects[i].type==OBJ_MARKER && (Objects[i].id==m1 || Objects[i].id==m2)) + { + SetObjectDeadFlag(&Objects[i],true,false); + } + } +} + +// Tells our clients that a player disconnected +void MultiSendPlayerDisconnect (int slot) +{ + int size,count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + + size=START_DATA (MP_DISCONNECT,data,&count); + + // Send player whose disconnected + MultiAddByte (slot,data,&count); + + END_DATA (count,data,size); + + if(Game_is_master_tracker_game) + { + NetPlayers[slot].flags |= NPF_MT_WRITING_PILOT; + } + for (int i=0;iNETSEQ_LEVEL_START && NetPlayers[i].sequence!=NETSEQ_LEVEL_END) + { + if (cur_time-NetPlayers[i].last_packet_time>DISCONNECT_TIME) + { + mprintf ((0,"8sec disconnecting player %d. Last packet time=%f Sequence=%d\n",i,cur_time-NetPlayers[i].last_packet_time,NetPlayers[i].sequence)); + MultiDisconnectPlayer (i); + } + } + else // If not playing (ie joining, give them longer) + { + if (cur_time-NetPlayers[i].last_packet_time>(DISCONNECT_TIME*15)) + { + mprintf ((0,"Too long...disconnecting player %d. Last packet time=%f Sequence=%d\n",i,cur_time-NetPlayers[i].last_packet_time,NetPlayers[i].sequence)); + MultiDisconnectPlayer (i); + } + } + } + } +} + +// Disconnect a player for whatever reason +// Server only +void MultiDisconnectPlayer (int slot) +{ + ASSERT (Netgame.local_role==LR_SERVER); + + DLLInfo.me_handle=DLLInfo.it_handle=Objects[Players[slot].objnum].handle; + CallGameDLL(EVT_GAMEPLAYERDISCONNECT,&DLLInfo); + + if (NetPlayers[slot].flags & NPF_CONNECTED) + { + mprintf ((0,"Disconnecting player %d (%s)...\n",slot,Players[slot].callsign)); + if(NetPlayers[slot].file_xfer_flags != NETFILE_NONE) + { + MultiCancelFile(slot,NetPlayers[slot].custom_file_seq,NetPlayers[slot].file_xfer_who); + } + nw_CloseSocket (&NetPlayers[slot].reliable_socket); + + if (NetPlayers[slot].sequence==NETSEQ_PLAYING) + { + PlayerSpewInventory (&Objects[Players[slot].objnum],true,true); + MultiMakePlayerGhost (slot); + AddHUDMessage (TXT_MLTDISCONNECTED,Players[slot].callsign); + } + + + NetPlayers[slot].flags &=~NPF_CONNECTED; + NetPlayers[slot].secret_net_id = 0; + MultiClearGuidebot(slot); + MultiClearPlayerMarkers(slot); + MultiSendPlayerDisconnect (slot); + Players[slot].flags=0; + + } + else + mprintf ((0,"Trying to disconnect a non-existant player!\n")); +} + + +// Sends existing players to a joining player +// Sends to "slot" and describes player "which" +// Server only +void MultiSendPlayer (int slot,int which) +{ + ASSERT (Netgame.local_role==LR_SERVER); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + mprintf ((0,"Sending MP_PLAYER packet to player %d!\n",slot)); + + size_offset=START_DATA(MP_PLAYER,data,&count); + + MultiAddByte (which,data,&count); + + // Add name + int len=strlen(Players[which].callsign)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Players[which].callsign,len); + count+=len; + + // Add name + len=strlen(Ships[Players[which].ship_index].name)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Ships[Players[which].ship_index].name,len); + count+=len; + + // Add flags, shields, inventory + MultiAddInt (Players[which].flags,data,&count); + MultiAddInt (Players[which].weapon_flags,data,&count); + MultiAddInt (NetPlayers[which].flags,data,&count); + MultiAddFloat (Objects[Players[which].objnum].shields,data,&count); + + // Add position, roomnum, and terrain flag + + // Just for safety sake add the object number + MultiAddShort (Players[which].objnum,data,&count); + + // If this is a team game, get the first start position + if (slot==which) + { + int myteam=GameDLLGetConnectingPlayersTeam(slot); + + ubyte temp_team = (myteam==-1)?255:myteam; + MultiAddByte (temp_team,data,&count); + + if (myteam>=0 && myteam<=4) + Players[slot].team=myteam; + + if (temp_team==255) + Players[slot].start_index=slot; + else + Players[slot].start_index=PlayerGetRandomStartPosition (slot); + + MultiAddShort (Players[slot].start_index,data,&count); + } + else + { + ubyte temp_team = (Players[which].team==-1)?255:Players[which].team; + MultiAddByte (temp_team,data,&count); + } + + // Tell them if this player is currently observing + if (Objects[Players[which].objnum].type==OBJ_OBSERVER) + MultiAddByte(1,data,&count); + else + MultiAddByte(0,data,&count); + + + //send the player's address + memcpy(data+count,&NetPlayers[which].addr,sizeof(network_address)); + count += sizeof(network_address); + + // PPS + MultiAddByte (NetPlayers[which].pps,data,&count); + + //pilot picture id + MultiAddUshort (NetPlayers[which].pilot_pic_id,data,&count); + + // Ranking + MultiAddFloat (Players[which].rank,data,&count); + + // Mastertracker id + if(Game_is_master_tracker_game) + { + int len = strlen(Players[which].tracker_id)+1; + MultiAddByte (len,data,&count); + memcpy (&data[count],Players[which].tracker_id,len); + count+=len; + } + + END_DATA (count,data,size_offset); + + // Send it out + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count); + +} + +// Sends blownup buildings to a joining player +// Server only +void MultiSendBuildings (int slot) +{ + ASSERT (Netgame.local_role==LR_SERVER); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + mprintf ((0,"Sending MP_BUILDING packet to player %d!\n",slot)); + + size_offset=START_DATA(MP_BUILDING,data,&count); + + MultiAddByte (Multi_num_buildings_changed,data,&count); + + for (int i=0;itype==OBJ_ROBOT || obj->type==OBJ_CLUTTER || obj->type==OBJ_BUILDING) + good=true; + + if (good==true) + { + if (obj->flags & (OF_DEAD)) + good=false; + if (!(obj->flags & OF_CLIENT_KNOWS)) + good=false; + + // Still going? Good! + if (good==true) + { + if (obj->movement_type == MT_WALKING || obj->movement_type == MT_PHYSICS) + return true; + } + } + + // Didn't pass all the tests else it wouldn't make it this fare + return false; +} + +void MultiSendJoinDemoObjects(int slot) +{ + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset,i; + + int num_demo_objects = 0; + for(i=0;itype,data,&count); + + // Send old object type if it's a dummy + if (obj->type==OBJ_DUMMY) + { + obj_is_dummy = true; + MultiAddByte (obj->dummy_type,data,&count); + ObjUnGhostObject(obj - Objects); + } + + + if (obj->type!=OBJ_CAMERA && obj->type!=OBJ_DOOR) + { + // Stuff the checksum + index=MultiGetMatchChecksum (obj->type,obj->id); + MultiAddUint (index,data,&count); + } + + if (obj->type!=OBJ_POWERUP && obj->type!=OBJ_DOOR) + { + multi_orientation mat; + MultiMakeMatrix (&mat,&obj->orient); + MultiMatrixMakeEndianFriendly(&mat); + memcpy (&data[count],&mat,sizeof(multi_orientation)); + count+=sizeof(multi_orientation); + } + + // Send position + if (obj->type!=OBJ_DOOR) + { + //memcpy (&data[count],&obj->pos,sizeof(vector)); + //count+=sizeof(vector); + MultiAddVector(obj->pos,data,&count); + + // Send room number and terrain flag + MultiAddUshort (CELLNUM (obj->roomnum),data,&count); + + if (OBJECT_OUTSIDE(obj)) + MultiAddByte (1,data,&count); + else + MultiAddByte (0,data,&count); + + if (obj->flags & OF_USES_LIFELEFT) + { + ASSERT (obj->lifeleft<255); + MultiAddUbyte (obj->lifeleft,data,&count); + } + else + MultiAddUbyte (255,data,&count); + } + + if (obj->type==OBJ_MARKER) + { + // Add marker message to the end of this + ubyte len = strlen(MarkerMessages[obj->id])+1; + MultiAddByte (len,data,&count); + memcpy (data+count,MarkerMessages[obj->id],len); + count+=len; + } + + ASSERT (countrtype.pobj_info; + if (p_info->multi_turret_info.num_turrets) + { + if (Num_changed_turret[slot]0) + { + int num_objects_this_packet=min (MAX_OBJECTS_PER_PACKET,objects_to_go); + int overflow=0; + + count=0; + size_offset=START_DATA(MP_JOIN_OBJECTS,data,&count); + int num_objs_offset=count; + MultiAddByte (0,data,&count); + + // Actually add the objects to our outgoing buffer + for (i=0;i=MAX_GAME_DATA_SIZE) + { + num_objects_this_packet=i; + data[num_objs_offset]=num_objects_this_packet; + END_DATA (count,data,size_offset); + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count,false); + overflow=1; + } + else + { + memcpy (&data[count],obj_data,num_bytes); + count+=num_bytes; + } + } + + if (!overflow) + { + data[num_objs_offset]=num_objects_this_packet; + END_DATA (count,data,size_offset); + nw_SendReliable (NetPlayers[slot].reliable_socket,data,count,false); + } + + cur_object+=num_objects_this_packet; + objects_to_go-=num_objects_this_packet; + } + + MultiSendJoinDemoObjects(slot); +} + +void MultiStoreWorldPacket (int slot,ubyte *big_data,int *big_count,ubyte *cur_data,int *cur_count,int *size_offset) +{ + if (*big_count+*cur_count>=(MAX_GAME_DATA_SIZE-3)) + { + mprintf ((0,"Starting another world packet!\n")); + MultiAddByte (WS_END,big_data,big_count); + END_DATA (*big_count,big_data,*size_offset); + + // Send it out + nw_SendReliable (NetPlayers[slot].reliable_socket,big_data,*big_count); + + // Restart another packet + *big_count=0; + *size_offset=START_DATA(MP_WORLD_STATES,big_data,big_count); + } + + memcpy (&big_data[*big_count],cur_data,*cur_count); + *big_count+=*cur_count; + *cur_count=0; + ASSERT (*big_count<(MAX_GAME_DATA_SIZE-3)); +} + +doorway *GetDoorwayFromObject(int door_obj_handle); +// Function that sends all the changed world states to an incoming player +void MultiSendWorldStates (int slot) +{ + int i; + ASSERT (Netgame.local_role==LR_SERVER); + + ubyte data[MAX_GAME_DATA_SIZE]; + ubyte cur_data[MAX_GAME_DATA_SIZE]; + int cur_count; + int count=0; + int size_offset; + + mprintf ((0,"Sending MP_WORLD_STATES packet to player %d!\n",slot)); + + size_offset=START_DATA(MP_WORLD_STATES,data,&count); + + // Send MOTD + if (Dedicated_server && Multi_message_of_the_day[0]!=0) + { + cur_count=0; + MultiAddByte (WS_MOTD,cur_data,&cur_count); + MultiAddString (Multi_message_of_the_day,cur_data,&cur_count); + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + + + // Send weather + cur_count=0; + MultiAddByte (WS_WEATHER,cur_data,&cur_count); + MultiAddByte (Weather.flags,cur_data,&cur_count); + MultiAddFloat (Weather.rain_intensity_scalar,cur_data,&cur_count); + MultiAddFloat (Weather.snow_intensity_scalar,cur_data,&cur_count); + MultiAddFloat (Weather.lightning_interval_time,cur_data,&cur_count); + MultiAddShort (Weather.lightning_rand_value,cur_data,&cur_count); + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + + + + // Send broke glass + if (Num_broke_glass>0) + { + for (i=0;itype==OBJ_NONE) || (obj->flags & OF_DEAD)) + continue; + + if (obj->type==OBJ_DOOR) + { + doorway *dp; + dp = GetDoorwayFromObject(Objects[i].handle); + cur_count=0; + MultiAddByte (WS_DOOR,cur_data,&cur_count); + MultiAddShort (i,cur_data,&cur_count); + MultiAddByte (dp->state,cur_data,&cur_count); + MultiAddByte ((dp->flags & DF_LOCKED)!=0,cur_data,&cur_count); + MultiAddFloat (dp->position,cur_data,&cur_count); + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + + if (obj->change_flags & OCF_LIGHT_DISTANCE) + { + cur_count=0; + MultiAddByte (WS_LIGHT_DISTANCE,cur_data,&cur_count); + MultiAddShort (i,cur_data,&cur_count); + + light_info *li = ObjGetLightInfo(obj); + if (li) + MultiAddFloat (li->light_distance,cur_data,&cur_count); + else + MultiAddFloat (0,cur_data,&cur_count); + + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + + if (obj->change_flags & OCF_LIGHT_COLOR) + { + cur_count=0; + MultiAddByte (WS_LIGHT_COLOR,cur_data,&cur_count); + MultiAddShort (i,cur_data,&cur_count); + + light_info *li = ObjGetLightInfo(obj); + if (li) + { + MultiAddFloat (li->red_light1,cur_data,&cur_count); + MultiAddFloat (li->green_light1,cur_data,&cur_count); + MultiAddFloat (li->blue_light1,cur_data,&cur_count); + } + else + { + MultiAddFloat (0,cur_data,&cur_count); + MultiAddFloat (0,cur_data,&cur_count); + MultiAddFloat (0,cur_data,&cur_count); + } + + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + + if (obj->change_flags & OCF_NO_RENDER) + { + cur_count=0; + MultiAddByte (WS_NO_RENDER,cur_data,&cur_count); + MultiAddShort (i,cur_data,&cur_count); + + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + + + if (obj->change_flags & OCF_PHYS_FLAGS) + { + cur_count=0; + MultiAddByte (WS_OBJECT_PHYS_FLAGS,cur_data,&cur_count); + MultiAddShort (i,cur_data,&cur_count); + MultiAddInt (Objects[i].mtype.phys_info.flags,cur_data,&cur_count); + + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + + + // Check to see if we have an attached object + if (obj->attach_children!=NULL) + { + cur_count=0; + + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + MultiAddByte (WS_OBJECT_ATTACH,cur_data,&cur_count); + MultiAddUshort (i,cur_data,&cur_count); + + MultiAddByte (pm->n_attach,cur_data,&cur_count); + + for (int t=0;tn_attach;t++) + { + object *child = ObjGet(obj->attach_children[t]); + if (!child) + MultiAddUshort (65535,cur_data,&cur_count); + else + { + ASSERT(child->flags & OF_ATTACHED); + MultiAddUshort (OBJNUM(child),cur_data,&cur_count); + MultiAddByte (child->attach_index,cur_data,&cur_count); + MultiAddByte (child->attach_type,cur_data,&cur_count); + + if (child->attach_type==AT_RAD) + MultiAddFloat (child->attach_dist/child->size,cur_data,&cur_count); + } + } + + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + } + + // Do spew stuff + int spewcount=0; + for(i = 0;i < MAX_SPEW_EFFECTS;i++) + { + + if(SpewEffects[i].inuse) + { + spewcount++; + cur_count=0; + spewinfo *sp=&SpewEffects[i]; + + MultiAddByte (WS_SPEW,cur_data,&cur_count); + MultiAddShort (i,cur_data,&cur_count); + + if (sp->use_gunpoint) + { + MultiAddByte (1,cur_data,&cur_count); + MultiAddUshort (sp->gp.obj_handle & HANDLE_OBJNUM_MASK,cur_data,&cur_count); + MultiAddByte (sp->gp.gunpoint,cur_data,&cur_count); + } + else + { + MultiAddByte (0,cur_data,&cur_count); + //memcpy (&cur_data[cur_count],&sp->pt.normal,sizeof(vector)); + //cur_count+=sizeof(vector); + MultiAddVector(sp->pt.normal,cur_data,&cur_count); + //memcpy (&cur_data[cur_count],&sp->pt.origin,sizeof(vector)); + //cur_count+=sizeof(vector); + MultiAddVector(sp->pt.origin,cur_data,&cur_count); + + MultiAddInt (sp->pt.room_num,cur_data,&cur_count); + } + + MultiAddByte (sp->random,cur_data,&cur_count); + MultiAddByte (sp->real_obj,cur_data,&cur_count); + MultiAddByte (sp->effect_type,cur_data,&cur_count); + MultiAddByte (sp->phys_info,cur_data,&cur_count); + MultiAddFloat (sp->drag,cur_data,&cur_count); + MultiAddFloat (sp->mass,cur_data,&cur_count); + MultiAddFloat (sp->time_int,cur_data,&cur_count); + MultiAddFloat (sp->longevity,cur_data,&cur_count); + MultiAddFloat (sp->lifetime,cur_data,&cur_count); + MultiAddFloat (sp->size,cur_data,&cur_count); + MultiAddFloat (sp->speed,cur_data,&cur_count); + + MultiStoreWorldPacket (slot,data,&count,cur_data,&cur_count,&size_offset); + } + } + mprintf ((0,"Send %d spew events\n",spewcount)); + + //Send buddybot handles if needed + if(Netgame.flags&NF_ALLOWGUIDEBOT) + { + for(int i=0;i=seq_threshold && NetPlayers[i].sequence!=NETSEQ_LEVEL_END) + nw_SendReliable (NetPlayers[i].reliable_socket,data,size,urgent); + } +} + +// Sends this nonreliable packet to everyone except the server and the named slot +void MultiSendToAllExcept (int except,ubyte *data,int size,int seq_threshold) +{ + + ASSERT (Netgame.local_role==LR_SERVER); + for (int i=0;i=seq_threshold && NetPlayers[i].sequence!=NETSEQ_LEVEL_END)) + nw_Send (&NetPlayers[i].addr,data,size,0); + + } +} + +// Flushes all receive sockets so that there is no data coming from them +void MultiFlushAllIncomingBuffers () +{ + ubyte *data; + network_address from_addr; + int size; + + data = &(Multi_receive_buffer[0]); + + // get all incoming data and throw it away + while( (size = nw_Receive(data, &from_addr))>0 ) + ; + + + if (Netgame.local_role==LR_SERVER) + { + // Flush reliable sockets + for (int i=0;i 0) + ; + + } + } + } + +} + +// Checks to see if its any powerups need repositioning on the client machine +void MultiCheckToRepositionPowerups () +{ + int i; + static int invis_id=-2; + + if (invis_id==-2) + { + invis_id=FindObjectIDName ("InvisiblePowerup"); + } + + int changed=0; + + for (i=0;i<=Highest_object_index;i++) + { + object *obj=&Objects[i]; + if (obj->type!=OBJ_POWERUP) + continue; + + if (obj->id==invis_id) + continue; + + if (obj->flags & OF_DEAD) + continue; + + if (obj->flags & OF_STOPPED_THIS_FRAME) + { + // Send out our positional change + int count=0; + ubyte data[MAX_GAME_DATA_SIZE]; + int size=START_DATA (MP_POWERUP_REPOSITION,data,&count); + changed++; + + MultiAddUshort (i,data,&count); + //memcpy (&data[count],&obj->pos,sizeof(vector)); + //count+=sizeof(vector); + MultiAddVector(obj->pos,data,&count); + + // Send room number and terrain flag + MultiAddUshort (CELLNUM (obj->roomnum),data,&count); + + if (OBJECT_OUTSIDE(obj)) + MultiAddByte (1,data,&count); + else + MultiAddByte (0,data,&count); + + END_DATA (count,data,size); + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,false); + } + } + + if (changed>0) + { + mprintf ((0,"Stopped=%d\n",changed)); + } +} + + +// Checks to see if its time to respawn any powerups +void MultiCheckToRespawnPowerups () +{ + int i,t; + + for (i=0;i-1) + MultiSendObject (&Objects[objnum],1); + + InitObjectScripts (&Objects[objnum]); + Powerup_respawn[t].objnum=objnum; + + // Now erase this powerup from our timer info + for (t=i;t=8) + { + vector subvec=Objects[Players[i].objnum].pos-Objects[Players[to_slot].objnum].pos; + float mag=vm_GetMagnitudeFast (&subvec); + subvec/=mag; + + float dp=vm_DotProduct (&subvec,&Objects[Players[to_slot].objnum].orient.fvec); + dp=fabs(dp); + + pps_check_time=((float)pps_check_time/2.0)+((dp*((float)pps_check_time/2.0))+(dp*.9)); + pps_check_time=max (pps_check_time,6.0); + pps_check_time=min (pps_check_time,20.0); + + int pps=pps_check_time; + pps_check_time=pps; + }*/ + + // Set the timer as popped if it actually did + if (Multi_last_sent_time[to_slot][i]>(1.0/pps_check_time)) + timer_popped=1; + + + //if (i==Player_num) // Always send server position + // send_position=1; + + int num_src_to_check=1; + int srcs[10]; + srcs[0]=Players[to_slot].objnum; + + // Check for guideds + if (Players[to_slot].guided_obj!=NULL) + srcs[num_src_to_check++]=Players[to_slot].guided_obj-Objects; + + if (Players[to_slot].small_dll_obj!=-1) + { + int objnum=Players[to_slot].small_dll_obj; + if (Objects[objnum].flags & OF_DEAD || Objects[objnum].type==OBJ_NONE) + { + Players[to_slot].small_dll_obj=-1; + } + else + { + srcs[num_src_to_check]=objnum; + num_src_to_check++; + } + } + + // See if there are any small views we need to send for + // Do left view + if (Players[to_slot].small_left_obj!=-1) + { + int objnum=Players[to_slot].small_left_obj; + if (Objects[objnum].flags & OF_DEAD || Objects[objnum].type==OBJ_NONE || Objects[objnum].type==OBJ_WEAPON) + { + Players[to_slot].small_left_obj=-1; + } + else + { + srcs[num_src_to_check]=objnum; + num_src_to_check++; + } + } + + // Do right view + if (Players[to_slot].small_right_obj!=-1) + { + int objnum=Players[to_slot].small_right_obj; + if ((Objects[objnum].flags & OF_DEAD) || (Objects[objnum].type==OBJ_NONE) || (Objects[objnum].type==OBJ_WEAPON)) + { + Players[to_slot].small_right_obj=-1; + } + else + { + srcs[num_src_to_check]=objnum; + num_src_to_check++; + } + } + + for (int t=0;t=SECONDARY_INDEX) + { + send_position=1; + } + else + { + if (BOA_IsVisible(Player_fire_packet[i].dest_roomnum,Objects[srcs[t]].roomnum)) + send_position=1; + + } + } + } + + //Always send the server position, because we always say that the server is visible. This + //should fix some or all ghost ship problems. + if (i == Player_num) + { + send_position = 1; + } + + if (timer_popped) + { + if (i!=Player_num) + Multi_last_sent_time[to_slot][i]=0; + + + if (send_position) + { + Multi_visible_players[to_slot]|=(1<type==OBJ_NONE) + continue; + + if (MultiIsValidMovedObject (obj)) + { + if((obj->flags & OF_MOVED_THIS_FRAME)) + { + skip_this_obj = false; + //mprintf ((0,"Object %d (type=%d) moved this frame!\n",a,obj->type)); + //Check to see if this robot is on the list already + for(int b=0;bhandle) + { + skip_this_obj = true; + } + } + + if(!skip_this_obj) + { + Moved_robots[slot][Num_moved_robots[slot]]= obj->handle; + ASSERT (Objects[a].flags & OF_CLIENT_KNOWS); + Num_moved_robots[slot]++; + } + } + } + } +} + +// Sets up our data structures so that the nonvisible robots will be sent when they are needed +void MultiSetupNonVisRobots (int slot,object *obj) +{ + obj->generic_nonvis_flags|=(1<type==OBJ_NONE) + continue; + + if (obj->generic_nonvis_flags & (1<generic_sent_nonvis & (1<flags & OF_CLIENT_KNOWS); + send_out[num_to_send++]=objnum; + obj->generic_sent_nonvis |=(1<handle) + { + skip_this_obj = true; + } + } + + if(!skip_this_obj) + { + if (Num_moved_robots[slot]handle; + ASSERT (Objects[i].flags & OF_CLIENT_KNOWS); + Num_moved_robots[slot]++; + } + } + + // Now do changed anim + skip_this_obj=false; + //Check to see if this robot is on the list already + for(b=0;bgeneric_nonvis_flags&=~(1<0) + { + int num_packets=num_to_send/100; + int num_left=num_to_send % 100; + + for (i=0;i0) + MultiSendGenericNonVis (slot,&send_out[num_packets*100],num_left); + } + + +} + +// Returns true or false based on whether or not the player can see this generic object in a multiplayer game +// This takes into account markers, dll objects, and other stuff +bool MultiIsGenericVisibleToPlayer (int test_objnum,int to_slot) +{ + int srcs[10],num_src_to_check=1; + + srcs[0]=Players[to_slot].objnum; + + if (Players[to_slot].guided_obj!=NULL) + srcs[num_src_to_check++]=Players[to_slot].guided_obj-Objects; + + if (Players[to_slot].small_dll_obj!=-1) + { + int objnum=Players[to_slot].small_dll_obj; + if (Objects[objnum].flags & OF_DEAD || Objects[objnum].type==OBJ_NONE) + { + Players[to_slot].small_dll_obj=-1; + } + else + { + srcs[num_src_to_check]=objnum; + num_src_to_check++; + } + } + // See if there are any small views we need to send for + // Do left view + if (Players[to_slot].small_left_obj!=-1) + { + int objnum=Players[to_slot].small_left_obj; + if (Objects[objnum].flags & OF_DEAD || Objects[objnum].type==OBJ_NONE || Objects[objnum].type==OBJ_WEAPON) + { + Players[to_slot].small_left_obj=-1; + } + else + { + srcs[num_src_to_check]=objnum; + num_src_to_check++; + } + } + + // Do right view + if (Players[to_slot].small_right_obj!=-1) + { + int objnum=Players[to_slot].small_right_obj; + if (Objects[objnum].flags & OF_DEAD || Objects[objnum].type==OBJ_NONE || Objects[objnum].type==OBJ_WEAPON) + { + Players[to_slot].small_right_obj=-1; + } + else + { + srcs[num_src_to_check]=objnum; + num_src_to_check++; + } + } + + for (int t=0;t0) + { + if (Multi_send_size[slot]+rcount>=MAX_GAME_DATA_SIZE) + MultiSendFullPacket (slot,0); + memcpy (&Multi_send_buffer[slot][Multi_send_size[slot]],rdata,rcount); + Multi_send_size[slot]+=rcount; + } + + Objects[objnum].generic_sent_nonvis&=~(1<handle) + { + mprintf ((0,"Caught anim handle objnum problem!\n")); + continue; + } + + if (MultiIsGenericVisibleToPlayer (objnum,slot)) + send_position=1; + + if (send_position) + { + rcount = MultiStuffObjAnimUpdate(objnum,rdata); + if (Multi_send_size[slot]+rcount>=MAX_GAME_DATA_SIZE) + MultiSendFullPacket (slot,0); + memcpy (&Multi_send_buffer[slot][Multi_send_size[slot]],rdata,rcount); + Multi_send_size[slot]+=rcount; + Objects[objnum].generic_sent_nonvis&=~(1<handle) + { + mprintf ((0,"Caught turret handle objnum problem!\n")); + continue; + } + + if (MultiIsGenericVisibleToPlayer (objnum,slot)) + send_position=1; + + if (send_position) + { + rcount = MultiStuffTurretUpdate(objnum,rdata); + if (Multi_send_size[slot]+rcount>=MAX_GAME_DATA_SIZE) + MultiSendFullPacket (slot,0); + memcpy (&Multi_send_buffer[slot][Multi_send_size[slot]],rdata,rcount); + Multi_send_size[slot]+=rcount; + Objects[objnum].generic_sent_nonvis&=~(1<handle) + { + mprintf ((0,"Caught wb anim handle objnum problem!\n")); + continue; + } + + + if (MultiIsGenericVisibleToPlayer (objnum,slot)) + send_position=1; + + if (send_position) + { + rcount = MultiStuffObjWBAnimUpdate(objnum,rdata); + if (Multi_send_size[slot]+rcount>=MAX_GAME_DATA_SIZE) + MultiSendFullPacket (slot,0); + memcpy (&Multi_send_buffer[slot][Multi_send_size[slot]],rdata,rcount); + Multi_send_size[slot]+=rcount; + Objects[objnum].generic_sent_nonvis&=~(1<Highest_room_index) + Player_fire_packet[i].dest_roomnum=Highest_room_index+1; + else + { + if (Rooms[hit_data.hit_room].flags & RF_EXTERNAL) + { + int roomnum=GetTerrainRoomFromPos (&hit_data.hit_pnt); + Player_fire_packet[i].dest_roomnum=roomnum; + } + else + Player_fire_packet[i].dest_roomnum=hit_data.hit_room; + } + } + } +} + + +int Multi_occluded=0; +#define MT_DATA_UPDATE_INTERVAL 300 //300 seconds +// Does whatever the server needs to do for this frame +void MultiDoServerFrame () +{ + int i; + + if (Netgame.flags & NF_EXIT_NOW) + { + // DLL says we should bail! + Netgame.flags &=~NF_EXIT_NOW; + MultiLeaveGame(); + SetFunctionMode(MENU_MODE); + return; + } + + if (NetPlayers[Player_num].sequence==NETSEQ_PREGAME) + { + MultiFlushAllIncomingBuffers(); + NetPlayers[Player_num].sequence=NETSEQ_PLAYING; + + Players[Player_num].time_in_game = timer_GetTime(); + + // Pick a start slot for me + Players[Player_num].start_index=PlayerGetRandomStartPosition (Player_num); + PlayerMoveToStartPos (Player_num,Players[Player_num].start_index); + + //Update the tracker if it's a master tracker game + CallMultiDLL(MT_EVT_FIRST_FRAME); + if(Game_is_master_tracker_game) + { + NetPlayers[Player_num].flags |= NPF_MT_READING_PILOT; + strcpy(MTPilotinfo[Player_num].tracker_id,Players[Player_num].tracker_id); + strcpy(MTPilotinfo[Player_num].pilot_name,Players[Player_num].callsign); + NetPlayers[Player_num].flags&=~NPF_WROTE_RANK; + } + + if (Dedicated_server) // Make this player an observer + PlayerSwitchToObserver (Player_num,OBSERVER_MODE_ROAM); + + for (int i=0;i(1.0/12.0)) //was: (float)NetPlayers[i].pps + { + // Check to see if there is additional damage or shields to be added to this guy + if (Multi_additional_damage[i]!=0) + { + if (! (Players[i].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD))) + MultiSendAdditionalDamage(i,Multi_additional_damage_type[i],Multi_additional_damage[i]); + Multi_additional_damage[i]=0; + Multi_additional_damage_type[i]=PD_NONE; + } + + Multi_last_sent_time[i][Player_num]=0; + } + //END: code to fix clients not hearing server omega damage + + continue; + } + + if (NetPlayers[i].flags & NPF_CONNECTED) + { + // Check to see if this guy as any special requests + if (NetPlayers[i].sequence==NETSEQ_REQUEST_PLAYERS) + { + for (int t=0;tMULTI_PING_INTERVAL)) + { + MultiSendPing(i); + } + //If we actually want to send custom data out.. + if(Use_file_xfer) + { + //Check to see if we need to request any files from this bozo + if((NetPlayers[i].custom_file_seq!=NETFILE_ID_NOFILE)&&(NetPlayers[i].custom_file_seq!=NETFILE_ID_DONE)) + { + //See if we are already sending or receiving a file from this player + if(NetPlayers[i].file_xfer_flags == NETFILE_NONE) + { + //Check to see if we have this file in our cache + //Time to ask for the file specified in the sequence + MultiAskForFile(NetPlayers[i].custom_file_seq,i,i); + } + } + } + + + //Send the client how much data we have sent him so far. + if((timer_GetTime() - last_sent_bytes[i])> 5) + { + MultiSendBytesSent(i); + last_sent_bytes[i] = timer_GetTime(); + } + + if ((timer_GetTime() - Multi_last_send_visible[i])>.5) + { + Multi_last_send_visible[i]=timer_GetTime(); + MultiSendVisiblePlayers(i); + } + + // Figure out which robots moved this frame + MultiUpdateRobotMovedList (i); + + Multi_last_sent_time[i][Player_num]+=Frametime; + float last_client_update=Multi_last_sent_time[i][Player_num]; + + // Send out all players movement + MultiSendPositionalUpdates (i); + + // Send other info if the timer has popped + if(last_client_update>(1.0/(float)NetPlayers[i].pps)) + { + // Send out robot updates if needed + MultiDoServerRobotFrame (i); + + // Check to see if there is additional damage or shields to be added to this guy + if (Multi_additional_damage[i]!=0) + { + if (! (Players[i].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD))) + MultiSendAdditionalDamage(i,Multi_additional_damage_type[i],Multi_additional_damage[i]); + Multi_additional_damage[i]=0; + Multi_additional_damage_type[i]=PD_NONE; + } + + Multi_last_sent_time[i][Player_num]=0; + } + } + } + } + + for (i=0;i=-1 && tofParam,data,&count); + MultiAddInt (info->iParam,data,&count); + + switch(eventnum){ + case EVT_CLIENT_GAMEWALLCOLLIDE: + case EVT_GAMEWALLCOLLIDE: + MultiAddFloat (info->collide_info.point.x,data,&count); + MultiAddFloat (info->collide_info.point.y,data,&count); + MultiAddFloat (info->collide_info.point.z,data,&count); + MultiAddFloat (info->collide_info.normal.x,data,&count); + MultiAddFloat (info->collide_info.normal.y,data,&count); + MultiAddFloat (info->collide_info.normal.z,data,&count); + MultiAddFloat (info->collide_info.hitspeed,data,&count); + MultiAddFloat (info->collide_info.hit_dot,data,&count); + MultiAddInt (info->collide_info.hitseg,data,&count); + MultiAddInt (info->collide_info.hitwall,data,&count); + break; + case EVT_CLIENT_GAMECOLLIDE: + case EVT_GAMECOLLIDE: + MultiAddFloat (info->collide_info.point.x,data,&count); + MultiAddFloat (info->collide_info.point.y,data,&count); + MultiAddFloat (info->collide_info.point.z,data,&count); + MultiAddFloat (info->collide_info.normal.x,data,&count); + MultiAddFloat (info->collide_info.normal.y,data,&count); + MultiAddFloat (info->collide_info.normal.z,data,&count); + break; + case EVT_GAMEOBJCHANGESEG: + case EVT_CLIENT_GAMEOBJCHANGESEG: + case EVT_GAMEPLAYERCHANGESEG: + case EVT_CLIENT_GAMEPLAYERCHANGESEG: + MultiAddInt (info->newseg,data,&count); + MultiAddInt (info->oldseg,data,&count); + break; + } + }else{ + MultiAddByte (0,data,&count); + } + + END_DATA (count,data,size_offset); + + // Send it out + if (to==-1) + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,false); + else + nw_SendReliable (NetPlayers[to].reliable_socket,data,count); +} + +// Resets the settings that a server uses +void MultiResetSettings () +{ + strcpy (Netgame.scriptname,"Anarchy.d3m"); + strcpy (Netgame.connection_name,"Direct TCP~IP"); + strcpy (Netgame.mission,"fury.mn3"); + strcpy (Netgame.name,"Generic D3 Game"); + + Netgame.packets_per_second=10; + Netgame.respawn_time=60; + Netgame.flags=NF_RANDOMIZE_RESPAWN; + Netgame.max_players=8; + Netgame.difficulty=2; + + // Clear MOTD + Multi_message_of_the_day[0]=0; + + +} + +#define LEVEL_SPAN 10.0f + +#define KILL_BASE 0.52f +#define DEATH_BASE 0.50f + +#define KILL_SCALAR (KILL_BASE/LEVEL_SPAN) +#define DEATH_SCALAR (DEATH_BASE/LEVEL_SPAN) + + +int GetRankLevel(int rank) +{ + if(rank<600) return 1; + if(rank<900) return 2; + if(rank<1200) return 3; + if(rank<1500) return 4; + if(rank<1800) return 5; + if(rank<2100) return 6; + if(rank<2400) return 7; + if(rank<2600) return 8; + if(rank<3000) return 9; + return 10; +} + +// Given two objects, calculates rank for them after a kill +float CalculateNewRanking (object *obj_a,object *obj_b,int won) +{ + ASSERT (obj_a->type==OBJ_PLAYER && obj_b->type==OBJ_PLAYER); + + float rank_a=Players[obj_a->id].rank; + float rank_b=Players[obj_b->id].rank; + + int level_a=GetRankLevel((int)rank_a); + int level_b=GetRankLevel((int)rank_b); + + if((obj_a==obj_b) && won) return rank_a; + + if(won) + { + rank_a+=(KILL_SCALAR*float(level_b-level_a) + KILL_BASE); + } + else + { + rank_a-=(DEATH_SCALAR*float(level_a-level_b) + DEATH_BASE); + } + float new_rank=rank_a; + + if (new_rank<0) + new_rank=0; + + return new_rank; +} + + +/* +// Given two objects, calculates rank for them after a kill +float CalculateNewRanking (object *obj_a,object *obj_b,int won) +{ + ASSERT (obj_a->type==OBJ_PLAYER && obj_b->type==OBJ_PLAYER); + + float rank_a=Players[obj_a->id].rank; + float rank_b=Players[obj_b->id].rank; + + float k_factor; + float g_factor=400; + float scalar; + int num_players=Player_count; + + if (num_players>8) + num_players=8; + if (num_players<2) + num_players=2; + + scalar=1.0-(((num_players-2)/6.0)*.5); + g_factor=400*(1.0+(((num_players-2)/6.0)*.4)); + + k_factor=16; + k_factor*=scalar; + + float delta_r=rank_b-rank_a; + float expected_wins = 1.0/((pow(10.0,delta_r / g_factor) + 1)); + float new_rank = (k_factor * (won - (expected_wins))); + + if (won && new_rank<.005f) + new_rank=.005f; + + + // This prevents players of vastly different rank from hurting each the better rank + if ((rank_a-rank_b)>400 && !won) + new_rank=0; + + new_rank=Players[obj_a->id].rank+new_rank; + + if (new_rank<0) + new_rank=0; + + mprintf ((0,"Player %s rating is now %f\n",Players[obj_a->id].callsign,new_rank)); + + return new_rank; +} +*/ + +// Returns a ranking index based on the player rating +// If rankbuf is non-null, fills in the string corresponding to that rank +// Returns -1 if not a pxo game (ie no rankings in this game) +int GetRankIndex (int pnum,char *rankbuf) +{ + if (!Game_is_master_tracker_game) + return -1; + + float ranking=Players[pnum].rank; + int val=0; + + if (ranking>=0 && ranking<600) + val=0; + else if (ranking>=600 && ranking<900) + val=1; + else if (ranking>=900 && ranking<1200) + val=2; + else if (ranking>=1200 && ranking<1500) + val=3; + else if (ranking>=1500 && ranking<1800) + val=4; + else if (ranking>=1800 && ranking<2100) + val=5; + else if (ranking>=2100 && ranking<2400) + val=6; + else if (ranking>=2400 && ranking<2600) + val=7; + else if (ranking>=2600 && ranking<3000) + val=8; + else if (ranking>=3000) + val=9; + + if (rankbuf) + sprintf (rankbuf,"%s",TXT(TXT_MULTI_RANKS+val)); + + return val; + +} + +// Tells the clients to change rank for a player +void MultiSendChangeRank (int pnum,char *str,ubyte goodnews) +{ + int size; + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + + mprintf ((0,"Sending change rank!\n")); + + size=START_DATA (MP_CHANGE_RANK,data,&count); + MultiAddByte (pnum,data,&count); + MultiAddString (str,data,&count); + MultiAddByte (goodnews,data,&count); + MultiAddFloat (Players[pnum].rank,data,&count); + END_DATA (count,data,size); + + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_PLAYING,false); + MultiDoChangeRank (data); +} + +// Does PXO stuff and sends out messages demoting/promoting the player +void ChangeRankIndex (int old_index,int pnum) +{ + char str[255]; + int new_index=GetRankIndex (pnum); + ubyte goodnews=1; + + if (old_indextype!=OBJ_PLAYER || killer->type!=OBJ_PLAYER) + return; + + int old_killed_index=GetRankIndex (killed->id); + int old_killer_index=GetRankIndex (killer->id); + + if (killed==killer) // Suicide, always subract .5 points + { + Players[killer->id].rank-=.5; + if (Players[killer->id].rank<0) + { + Players[killer->id].rank=0; + } + + if (GetRankIndex(killer->id)!=old_killer_index) + ChangeRankIndex (old_killer_index,killer->id); + } + else + { + killed_rank=CalculateNewRanking (killed,killer,0); + killer_rank=CalculateNewRanking (killer,killed,1); + + Players[killer->id].rank=killer_rank; + Players[killed->id].rank=killed_rank; + + if (GetRankIndex(killer->id)!=old_killer_index) + ChangeRankIndex (old_killer_index,killer->id); + + if (GetRankIndex(killed->id)!=old_killed_index) + ChangeRankIndex (old_killed_index,killed->id); + } +} diff --git a/Descent3/multi_server.h b/Descent3/multi_server.h new file mode 100644 index 000000000..2845760ac --- /dev/null +++ b/Descent3/multi_server.h @@ -0,0 +1,71 @@ +#ifndef MULTI_SERVER_H + +#include "pstypes.h" +#include "multi.h" +#include "game2dll.h" +//#include "gametrack.h" + +extern int Join_response_strings[]; + +//extern d3_net_game_data D3_tracker_info; + +#define MAX_CHANGED_OBJECTS 700 + + +extern int Changed_anim[MAX_CHANGED_OBJECTS][MAX_NET_PLAYERS]; +extern unsigned short Num_changed_anim[MAX_NET_PLAYERS]; + +extern int Changed_wb_anim[MAX_CHANGED_OBJECTS][MAX_NET_PLAYERS]; +extern unsigned short Num_changed_wb_anim[MAX_NET_PLAYERS]; + +extern int Changed_turret[MAX_CHANGED_OBJECTS][MAX_NET_PLAYERS]; +extern unsigned short Num_changed_turret[MAX_NET_PLAYERS]; + +extern unsigned int Secret_net_id; +#define JOIN_ANSWER_OK 0 +#define JOIN_ANSWER_NOT_SERVER 1 +#define JOIN_ANSWER_REJECTED 2 +#define JOIN_ANSWER_NO_ROOM 3 +#define JOIN_ANSWER_FULL 4 + + +// Does whatever the server needs to do for this frame +void MultiDoServerFrame (); + +// Set the local machine as a server +void MultiStartServer (int playing,char *scriptname,int dedicated_server_num_teams=-1); + +// Disconnects all players that haven't been heard from in a while +// Server only +void MultiDisconnectDeadPlayers (); + + +// Disconnect a player for whatever reason +// Server only +void MultiDisconnectPlayer (int slot); + + +// Sends existing players to a joining player +// Server only +void MultiSendPlayer (int slot,int which); + +// Sends this reliable packet to everyone except the server and the named slot +void MultiSendReliablyToAllExcept (int except,ubyte *data,int size,int seq_threshold=0,bool urgent=1); + +// Tells clients to execute dlls on their machines +void MultiSendClientExecuteDLL (int eventnum,int me_objnum,int it_objnum,int to,dllinfo *info=NULL); + +// Resets the settings that a server uses +void MultiResetSettings (); + +// Given a killer and killed player, calculates their new rankings +void GetNewRankings (object *killed,object *killer); + +// Returns a ranking index based on the player rating +// If rankbuf is non-null, fills in the string corresponding to that rank +// Returns -1 if not a pxo game (ie no rankings in this game) +int GetRankIndex (int pnum,char *rankbuf=NULL); + + + +#endif \ No newline at end of file diff --git a/Descent3/multi_ui.cpp b/Descent3/multi_ui.cpp new file mode 100644 index 000000000..51545feaf --- /dev/null +++ b/Descent3/multi_ui.cpp @@ -0,0 +1,2053 @@ +/* + * $Logfile: /DescentIII/main/multi_ui.cpp $ + * $Revision: 105 $ + * $Date: 7/09/01 3:34p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/main/multi_ui.cpp $ + * + * 105 7/09/01 3:34p Matt + * Require at least one allowed ship when starting a netgame. + * + * 104 4/20/00 6:25p Matt + * Netgame info dialog was looking at wrong variable for the difficulty + * level. + * + * 103 8/02/99 9:57a Kevin + * fixed port parameter with -pxo + * + * 102 5/19/99 1:10p Kevin + * set the default (or previously selected) difficulty level + * + * 101 5/09/99 4:55p Kevin + * Added some options for directplay games + * + * 100 5/09/99 1:34p Kevin + * Added diffuculty level system to multiplayer + * + * 99 5/08/99 3:58p Kevin + * made ESC clear the game info dialog + * + * 98 5/06/99 6:11p Kevin + * fixes for save/load game syste. Also require CD again and added some + * heat.net stuff + * + * 97 5/06/99 1:04a Kevin + * Fixed stupid default selection problem + * + * 96 5/05/99 11:59p Kevin + * fixed command line parms for auto connecting + * + * 95 5/05/99 11:16p Kevin + * heat.net stuff among other things + * + * 94 4/28/99 10:04p Kevin + * doh! forgot to return a value + * + * 93 4/28/99 6:39p Kevin + * Build 182 fixes + * + * 92 4/26/99 9:11p Matt + * Use small font instead of old UI font. + * + * 91 4/25/99 5:02p Kevin + * Bunches of multiplayer UI improvements + * + * 90 4/25/99 9:44a Kevin + * localized remaining game info strings + * + * 89 4/25/99 8:58a Kevin + * fixes for server info dialog + * + * 88 4/24/99 11:58p Kevin + * Game info list (hit I in the pxo game list) + * + * 87 4/23/99 5:32p Kevin + * Fixed a few mission bugs. + * + * 86 4/20/99 11:47a Samir + * converted protocol help to newui and fixed connection menu so enter + * worked on listbox. + * + * 85 4/17/99 3:44p Kevin + * Demo2 changes & fixes + * + * 84 4/16/99 6:00p Kevin + * Bunches of Demo stuff + * + * 83 4/16/99 3:17p Kevin + * More mouselook support + * + * 82 4/16/99 11:56a Matt + * Changed directplay code to be "ifdef _WIN32" instead of "ifndef + * __LINUX__" so it will work on the Mac. + * + * 81 4/15/99 3:36p Kevin + * Added mouselook UI stuff to the multiplayer options menu + * + * 80 4/15/99 1:41a Jeff + * changes for linux compile + * + * 79 4/14/99 4:19a Jeff + * more case mismatch fixes in #includes + * + * 78 4/14/99 2:51a Jeff + * fixed some case mismatched #includes + * + * 77 4/08/99 4:54p Kevin + * Display level warp dialog for multiplayer + * + * 76 4/03/99 9:26p Jeff + * changed dialogs that weren't using UID_OK and UID_CANCEL to use and + * handle them properly + * + * 75 3/25/99 3:29p Jason + * added option to randomize powerup respawn points + * + * 74 3/03/99 12:44p Jason + * made options mirror that of the 1.1 demo + * + * 73 3/01/99 12:55a Matt + * Deleted some obsolete strings from the string table, and moved some + * formatting info from the table to the code. + * + * 72 2/28/99 6:00p Jeff + * converted to new ui + * + * 71 2/24/99 4:00p Jason + * fixed flush problem when going to the join menu + * + * 70 2/20/99 2:31p Kevin + * Made multiplayer DLLs return to the game list after a game. + * + * 69 2/15/99 7:50p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 68 2/10/99 1:32p Kevin + * Added tablefile ignore thingy + * + * 67 2/08/99 2:39p Kevin + * Fixed command line joining & disabled gamespy launching in OEM builds + * + * 66 1/29/99 5:22p Jeff + * localization + * + * 65 1/23/99 3:21p Kevin + * Fixed default selection to work with new sorted listboxes + * + * 64 1/05/99 5:09p Jason + * added permissable server networking (ala Quake/Unreal) to Descent3 + * + * 63 1/04/99 6:56p Kevin + * Fixed unmatched if/else + * + * 62 12/30/98 3:49p Kevin + * Moved multiplayer options out of DLL and into main app + * + * 61 11/20/98 11:16a Kevin + * Fixed command line game launching + * + * 60 11/19/98 5:40p Kevin + * Demo system + * + * 59 10/24/98 10:49a Kevin + * Fixed Direct Serial Game + * + * 58 10/20/98 5:47p Kevin + * Gunboy and other fixes + * + * 57 10/19/98 11:54a Kevin + * + * 56 10/19/98 10:54a Kevin + * Fixed help option + * + * 55 10/16/98 11:55a Kevin + * Made dlls loadable in a hog + * + * 54 10/15/98 10:58a Kevin + * fixed ~ thingy for the first file in the dir + * + * 53 10/14/98 2:48p Kevin + * Changed memory code to comply with mem lib + * + * 52 10/09/98 12:59a Craig + * Fixed ship name for demo + * + * 51 10/06/98 5:34p Jeff + * various UI changes/improvements + * + * 50 10/01/98 11:45a Kevin + * fixed hotspot colors in allow/disallow screen + * + * 49 9/30/98 10:36a Kevin + * Added command line launching of URLs and command line directory + * specification + * + * 48 9/29/98 2:23p Kevin + * More UI tweaks + * + * 47 9/28/98 6:53p Kevin + * localized some multiplayer menus + * + * 46 9/28/98 4:35p Jeff + * general UI changes and improvements + * + * 45 9/28/98 4:21p Kevin + * Redesigned game list menus + * + * 44 9/28/98 9:53a Kevin + * Fixing misc UI problems, and fixed some bugs that VC 6 found + * + * 43 9/25/98 11:07a Kevin + * fixed columns to line up and cleaned up some PXO bugs + * + * 42 9/23/98 6:33p Kevin + * Fixed load settings + * + * 41 9/23/98 2:54p Kevin + * Saved multi config and changed UI to conform + * + * 40 9/22/98 3:55p Kevin + * Removed obsolete function + * + * 39 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 38 8/26/98 1:31p Jason + * Flush buffers before starting a game + * + * 37 8/17/98 11:00a Kevin + * Moved DLLs into subdirectories + * + * 36 8/15/98 5:16p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 35 7/10/98 10:47a Kevin + * Added command line connecting to games + * + * 34 7/01/98 2:52p Kevin + * coop -- adding sound index + * + * 33 6/24/98 6:40p Kevin + * Added help to main dll menu + * + * 32 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 31 6/16/98 10:54a Jeff + * + * 30 6/11/98 2:33p Kevin + * Updated dsps for new dll names + * + * 29 6/11/98 2:11p Kevin + * New Main multiplayer menu & dlls renamed to d3c + * + * 28 6/01/98 12:09p Kevin + * closed the menu before loading DLLs + * + * 27 6/01/98 10:09a Kevin + * Added DLL connection interface and auto update DLL + * + * 26 5/12/98 5:59p Jason + * fixed stupid timer bug + * + * 25 5/05/98 3:02p Jason + * attempting to add different screen resolutions + * + * 24 5/04/98 10:56a Kevin + * Mastertracker fixes/enhancements + * + * 23 5/01/98 12:28p Kevin + * Added new user registration button for MT + * + * 22 4/30/98 3:50p Kevin + * Mastertracker pilot stats + * + * 21 4/28/98 12:50p Kevin + * Master tracker login + * + * 20 4/27/98 7:12p Kevin + * Mastertracker pilot tracker + * + * 19 4/24/98 3:50p Kevin + * Added mastertracker game tracking support + * + * 18 4/23/98 4:49p Kevin + * Added Master tracker Game tracker support + * + * 17 4/23/98 2:12p Kevin + * Adding Mastertracker stuff + * + * 16 4/14/98 7:56p Matt + * Moved MSN_NAMELEN from mission.h to descent,h, so multi.h didn't need + * to include mission.h. + * Changed code to use ddio_MakePath() instead of sprintf() to make file + * spec. + * + */ + +#include "args.h" +#include "ui.h" +#include "newui.h" +#include "game.h" +#include "gamefont.h" +#include "descent.h" +#include "multi.h" +#include "multi_ui.h" +#include "multi_server.h" +#include "multi_client.h" +#include "networking.h" +#include "player.h" +#include "manage.h" +#include "pilot.h" +#include +#include "ddio.h" +#include "objinfo.h" +#include "stringtable.h" +#include "ConfigItem.h" +#include "appdatabase.h" + +//#define USE_DIRECTPLAY + +#ifdef USE_DIRECTPLAY +#include "directplay.h" +#endif + +#include "ship.h" + + +#include "multi_dll_mgr.h" +#include "Mission.h" +#include "menu.h" + + +#include "multi_save_settings.h" + +#define MAIN_MULTI_MENU_W 384 +#define MAIN_MULTI_MENU_H 256 +#define MAIN_MULTI_MENU_X 320 - (MAIN_MULTI_MENU_W/2) +#define MAIN_MULTI_MENU_Y 240 - (MAIN_MULTI_MENU_H/2) + +#define HELP_MULTI_MENU_W 512 +#define HELP_MULTI_MENU_H 192 +#define HELP_MULTI_MENU_X 320 - (MAIN_MULTI_MENU_W/2) +#define HELP_MULTI_MENU_Y 240 - (MAIN_MULTI_MENU_H/2) + +#define MAX_HELP_TEXT_LEN 1000 +char HelpText1[MAX_HELP_TEXT_LEN]; +char HelpText2[MAX_HELP_TEXT_LEN]; +char HelpText3[MAX_HELP_TEXT_LEN]; +char HelpText4[MAX_HELP_TEXT_LEN]; + +int MultiDLLGameStarting = 0; + +void DisplayNetDLLHelp(const char *topic); +// The first multiplayer menu that the user will see...all multiplayer stuff is +// reached from this menu +// Returns true if we're starting a multiplayer game +#define MAX_DLLS 10 +#define JEFF_RED GR_RGB(255,40,40) +#define MAX_NET_DLL_LEN 100 + +#define UID_MULTILB 0x1000 + + +#define HEAT_NAME "HEAT.NET" +#define PXO_NAME "Parallax Online" + + + + +int MainMultiplayerMenu () +{ + + + mprintf((0,"Entering MainMultiplayerMenu()\n")); + +#ifdef USE_DIRECTPLAY + Num_directplay_games = 0; +#endif + int exit_menu=0; + int ret=0; + char sznetgame[MAX_NET_DLL_LEN] = ""; + int netgamelen = MAX_NET_DLL_LEN; + int i; + unsigned int j; + + for(i=0;iread("DefaultNetConn",sznetgame,&netgamelen); + if(*sznetgame=='\0') + { + strcpy(sznetgame,"HEAT.NET"); + Database->write("DefaultNetConn",sznetgame,strlen(sznetgame)+1); + } + + newuiTiledWindow menu_wnd; + newuiListBox *lists; + newuiSheet *sheet; + + menu_wnd.Create(TXT_MULTIPLAYER,0,0,MAIN_MULTI_MENU_W,MAIN_MULTI_MENU_H); + sheet = menu_wnd.GetSheet(); + + sheet->NewGroup(TXT_MLTCHOOSETYPE,25,0); + lists = sheet->AddListBox(256,128,UID_MULTILB,UILB_NOSORT); + lists->RemoveAll(); + + sheet->NewGroup(NULL,75,145); + sheet->AddLongButton(TXT_SETITEMDEFAULT,0x30); + + sheet->NewGroup(NULL,55,160,NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK,UID_OK); + sheet->AddButton(TXT_REALHELP,0x20); + sheet->AddButton(TXT_CANCEL,UID_CANCEL); + + int dllcount=1; + char buffer[_MAX_PATH],fname[_MAX_PATH],fext[_MAX_PATH],fdir[_MAX_PATH]; + char search[256]; + + ddio_MakePath(search,Base_directory,"online","*.d3c",NULL); + + int dftidx = -1; + dllcount = 0; + + //Put the HEAT on top. + if(ddio_FindFileStart(search,buffer)) + { + + ddio_SplitPath(buffer,fdir,fname,fext); + + if(!stricmp(HEAT_NAME,fname)) + { + lists->AddItem(fname); + dllcount++; + } + if(!stricmp(sznetgame,fname)) + { + dftidx = 0; + } + + while( (ddio_FindNextFile(buffer)) && (dllcountAddItem(fname); + dllcount++; + } + if(!stricmp(sznetgame,fname)) + { + dftidx = 0; + } + } + } + + //Put the PXO next. + if(ddio_FindFileStart(search,buffer)) + { + + ddio_SplitPath(buffer,fdir,fname,fext); + + if(!stricmp(PXO_NAME,fname)) + { + lists->AddItem(fname); + dllcount++; + } + if(!stricmp(sznetgame,fname)) + { + dftidx = 1; + } + + while( (ddio_FindNextFile(buffer)) && (dllcountAddItem(fname); + dllcount++; + } + if(!stricmp(sznetgame,fname)) + { + dftidx = 1; + + } + } + } + + + + + + if(ddio_FindFileStart(search,buffer)) + { + + ddio_SplitPath(buffer,fdir,fname,fext); + + for(j=0;jAddItem(fname); + if(!stricmp(sznetgame,fname)) + { + dftidx = dllcount; + } + dllcount++; + } + + + unsigned int len; + while( (ddio_FindNextFile(buffer)) && (dllcountAddItem(fname); + if(!stricmp(sznetgame,fname)) + { + dftidx = dllcount; + } + dllcount++; + } + } + } + else + { + dllcount = 0; + } + ddio_FindFileClose(); + + if(dllcount==0) + { + DoMessageBox(TXT_MULTIPLAYER,TXT_MULTINOFILES,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + exit_menu = 1; + menu_wnd.Destroy(); + return 0; + } + + if(dftidx!=-1) + { + lists->SetCurrentItem(sznetgame);//>SetCurrentIndex(dftidx); + } + + MultiDLLGameStarting = 0; + menu_wnd.Open(); + + // Menu loop + char seldll[_MAX_PATH]; + + while (!exit_menu) + { + int res; + + res = menu_wnd.DoUI(); + + // handle all UI results. + switch(res) + { + case UID_CANCEL: + menu_wnd.Close(); + exit_menu = 1; + break; + + case UID_MULTILB: + case UID_OK: + { + menu_wnd.Close(); + lists->GetCurrentItem(seldll,_MAX_PATH); + unsigned int len = strlen(seldll); + + for(j=0;jGetCurrentItem(seldll,_MAX_PATH); + unsigned int len = strlen(seldll); + for(j=0;jGetCurrentItem(def,256); + + Database->write("DefaultNetConn",def,strlen(def)+1); + sprintf(fmtmsg,TXT_SETDEFAULT,def); + DoMessageBox(TXT_MENUMULTIPLAYER,fmtmsg,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + break; + } + } + + } + menu_wnd.Destroy(); + + return ret; + +} + +void DisplayNetDLLHelp(const char *topic) +{ +// samir- converted to newui. + newuiTiledWindow menu_wnd; + newuiSheet *sheet; + int exit_menu=0; + char str[128]; + + SetScreenMode(SM_MENU); + +// Create our buttons + sprintf(str, "%s: %s", TXT_REALHELP, topic); + + menu_wnd.Create(str, 0, 0, HELP_MULTI_MENU_W,HELP_MULTI_MENU_H); + sheet = menu_wnd.GetSheet(); + + sheet->NewGroup(NULL, 10,30); + sheet->AddText(HelpText1); + sheet->AddText(HelpText2); + sheet->AddText(HelpText3); + sheet->AddText(HelpText4); + + sheet->NewGroup(NULL, (HELP_MULTI_MENU_W-140), HELP_MULTI_MENU_H-95, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_CLOSE, UID_OK); + + menu_wnd.Open(); + + // Menu loop + while (!exit_menu) + { + int res; + + res = menu_wnd.DoUI(); + + // handle all UI results. + switch(res) + { + case UID_OK: + exit_menu = 1; + break; + + } + } + + menu_wnd.Close(); + menu_wnd.Destroy(); +} + + +int AutoConnectPXO() +{ + //char seldll[_MAX_PATH]; + int iparg; + int portarg; + MultiDLLGameStarting = 0; + iparg = FindArg("-pxo"); + if(!iparg) + { + return 0; + } + + strcpy(Auto_login_addr,GameArgs[iparg+1]); + + char *port = strchr(Auto_login_addr,':'); + if(port) + { + //terminate the hostname + *port = NULL; + //Increment to the first character of the port name + port++; + //get the port number + strcpy(Auto_login_port,port); + } + else + { + portarg = FindArg("port"); + if(portarg) + { + strcpy(Auto_login_port,GameArgs[portarg+1]); + } + else + { + Auto_login_port[0]=NULL; + } + } + + //ddio_MakePath(seldll,Base_directory,"online","parallax online.d3c",NULL); + if(LoadMultiDLL("parallax online")) + { + CallMultiDLL(MT_AUTO_LOGIN); + } + return MultiDLLGameStarting; +} + +int AutoConnectLANIP() +{ + //char seldll[_MAX_PATH]; + int iparg; + int portarg; + MultiDLLGameStarting = 0; + iparg = FindArg("ip"); + if(!iparg) + { + int connarg = FindArg("-directip"); + + if(!connarg) + return 0; + strcpy(Auto_login_addr,GameArgs[connarg+1]); + char *port = strchr(Auto_login_addr,':'); + if(port) + { + //terminate the hostname + *port = NULL; + //Increment to the first character of the port name + port++; + //get the port number + strcpy(Auto_login_port,port); + } + } + else + { + strcpy(Auto_login_addr,GameArgs[iparg+1]); + + portarg = FindArg("port"); + if(portarg) + { + strcpy(Auto_login_port,GameArgs[portarg+1]); + } + else + { + Auto_login_port[0]=NULL; + } + } + //ddio_MakePath(seldll,Base_directory,"online","Direct TCP~IP Game.d3c",NULL); + if(LoadMultiDLL("Direct TCP~IP")) + { + CallMultiDLL(MT_AUTO_LOGIN); + } + return MultiDLLGameStarting; +} + + +int AutoConnectHeat() +{ + //char seldll[_MAX_PATH]; + int iparg; + int portarg; + MultiDLLGameStarting = 0; + iparg = FindArg("ip"); + if(!iparg) + { + int connarg = FindArg("-heat"); + + if(!connarg) + return 0; + strcpy(Auto_login_addr,GameArgs[connarg+1]); + char *port = strchr(Auto_login_addr,':'); + if(port) + { + //terminate the hostname + *port = NULL; + //Increment to the first character of the port name + port++; + //get the port number + strcpy(Auto_login_port,port); + } + } + else + { + strcpy(Auto_login_addr,GameArgs[iparg+1]); + + portarg = FindArg("port"); + if(portarg) + { + strcpy(Auto_login_port,GameArgs[portarg+1]); + } + else + { + Auto_login_port[0]=NULL; + } + } + if(LoadMultiDLL("HEAT.NET")) + { + CallMultiDLL(MT_AUTO_LOGIN); + } + return MultiDLLGameStarting; +} + +#include "mem.h" + +void DoMultiAllowed(void) +{ + rendering_state rs; + rend_GetRenderState (&rs); + int cury = 30; + int i; + const char *p; + int objid; + int *index_to_id = NULL; + bool shipsallowed[MAX_SHIPS]; + bool objsallowed[MAX_OBJECTS]; + ConfigItem **ship_list = NULL; + char *str = (char *) mem_malloc(max(strlen(TXT_ALLOW),strlen(TXT_DISALLOW))+3); + + // Create Text Items and Window + UITextItem cancel_on_text (TXT_CANCEL, UICOL_HOTSPOT_HI); + UITextItem cancel_off_text (TXT_CANCEL, UICOL_HOTSPOT_LO); + + UITextItem exit_on_text(TXT_DONE, UICOL_HOTSPOT_HI); + UITextItem exit_off_text(TXT_DONE, UICOL_HOTSPOT_LO); + + sprintf(str,"\x18 %s",TXT_ALLOW); + UITextItem add_on(str, UICOL_HOTSPOT_HI); + UITextItem add_off(str, UICOL_HOTSPOT_LO); + + sprintf(str,"%s \x1a",TXT_DISALLOW); + UITextItem remove_on(str, UICOL_HOTSPOT_HI); + UITextItem remove_off(str, UICOL_HOTSPOT_LO); + mem_free(str); + + UITextItem allowed_ships(TXT_ALLOWEDSHIPS, UICOL_TEXT_NORMAL); + UITextItem banned_ships(TXT_DISALLOWDSHIPS, UICOL_TEXT_NORMAL); + + UITextItem allowed_gen(TXT_ALLOWEDITEMS, UICOL_TEXT_NORMAL); + UITextItem banned_gen(TXT_DISALLOWEDITEM, UICOL_TEXT_NORMAL); + + UITextItem obj_ti[MAX_OBJECT_IDS]; + UITextItem ship_ti[MAX_SHIPS]; + + + NewUIWindow menu_wnd; + menu_wnd.Create(0,0,rs.screen_width,rs.screen_height,UIF_PROCESS_ALL); + + int xcol1 = 110; + int yrow1 = 50; + int xcol2 = 374; + int yrow2 = 250; + int list_xsize = 160; + int list_ysize = 160; + + NewUIListBox lb_ships_allowed; + NewUIListBox lb_ships_banned; + + NewUIListBox lb_generic_allowed; + NewUIListBox lb_generic_banned; + UIHotspot exit_hs; + UIHotspot add_ship_hs; + UIHotspot remove_ship_hs; + UIHotspot add_gen_hs; + UIHotspot remove_gen_hs; + UIHotspot cancel_hs; + + UIText ship_allowed_txt; + UIText ship_banned_txt; + UIText gen_allowed_txt; + UIText gen_banned_txt; + + int textw; + int startx; + + grtext_SetFont(SMALL_FONT); + + add_ship_hs.Create(&menu_wnd,10,0,&add_off,&add_on,10,(yrow1+(list_ysize/2))-25,130, 30,UIF_FIT|UIF_CENTER); + remove_ship_hs.Create(&menu_wnd,11,0,&remove_off,&remove_on,10,(yrow1+(list_ysize/2))-5,130, 30,UIF_FIT|UIF_CENTER); + + add_gen_hs.Create(&menu_wnd,12,0,&add_off,&add_on,10,(yrow2+(list_ysize/2))-25,130, 30,UIF_FIT|UIF_CENTER); + remove_gen_hs.Create(&menu_wnd,13,0,&remove_off,&remove_on,10,(yrow2+(list_ysize/2))-5,130, 30,UIF_FIT|UIF_CENTER); + + textw = grtext_GetTextLineWidth(allowed_ships.GetBuffer()); + startx = ((list_xsize/2)+xcol1)-(textw/2); + + ship_allowed_txt.Create(&menu_wnd,&allowed_ships,startx,yrow1-15,0); + textw = grtext_GetTextLineWidth(banned_ships.GetBuffer()); + startx = ((list_xsize/2)+xcol2)-(textw/2); + + ship_banned_txt.Create(&menu_wnd,&banned_ships,startx,yrow1-15,0); + textw = grtext_GetTextLineWidth(allowed_gen.GetBuffer()); + startx = ((list_xsize/2)+xcol1)-(textw/2); + + gen_allowed_txt.Create(&menu_wnd,&allowed_gen,startx,yrow2-15,0); + textw = grtext_GetTextLineWidth(banned_gen.GetBuffer()); + startx = ((list_xsize/2)+xcol2)-(textw/2); + + gen_banned_txt.Create(&menu_wnd,&banned_gen,startx,yrow2-15,0); + + lb_ships_allowed.Create(&menu_wnd,2,xcol1,yrow1,list_xsize,list_ysize,0); + lb_ships_allowed.SetSelectedColor(UICOL_LISTBOX_HI); + lb_ships_allowed.SetHiliteColor(UICOL_LISTBOX_HI); + lb_ships_banned.Create(&menu_wnd,3,xcol2,yrow1,list_xsize,list_ysize,0); + lb_ships_banned.SetSelectedColor(UICOL_LISTBOX_HI); + lb_ships_banned.SetHiliteColor(UICOL_LISTBOX_HI); + lb_generic_allowed.Create(&menu_wnd,4,xcol1,yrow2,list_xsize,list_ysize,0); + lb_generic_allowed.SetSelectedColor(UICOL_LISTBOX_HI); + lb_generic_allowed.SetHiliteColor(UICOL_LISTBOX_HI); + lb_generic_banned.Create(&menu_wnd,5,xcol2,yrow2,list_xsize,list_ysize,0); + lb_generic_banned.SetSelectedColor(UICOL_LISTBOX_HI); + lb_generic_banned.SetHiliteColor(UICOL_LISTBOX_HI); + + // Exit out Hotspot + exit_hs.Create(&menu_wnd,UID_OK,KEY_ESC,&exit_off_text,&exit_on_text, 10,420, 180, 30,UIF_FIT|UIF_CENTER); cury+=20; + cancel_hs.Create(&menu_wnd,UID_CANCEL,0,&cancel_off_text,&cancel_on_text, 10,445, 180, 30,UIF_FIT|UIF_CENTER); cury+=20; + + NewUIWindowLoadBackgroundImage(&menu_wnd,"multimain.ogf"); + + //Object_info[MAX_OBJECT_IDS]; + // + //if(Object_info[i].type==OBJ_POWERUP) + for(i=0;iGetBuffer(); + PlayerSetShipPermission(-1,(char *)p,1); + lb_ships_banned.RemoveItem(selti); + lb_ships_allowed.AddItem(selti); + break; + } +#else + DoMessageBox(TXT_ERROR,TXT_WRONGVERSION,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + break; +#endif + case 11: +#ifndef DEMO + { + //Make ship not allowed + UITextItem * selti = (UITextItem *)lb_ships_allowed.GetItem(lb_ships_allowed.GetSelectedIndex()); + if(!selti) + break; + p = selti->GetBuffer(); + PlayerSetShipPermission(-1,(char *)p,0); + lb_ships_allowed.RemoveItem(selti); + lb_ships_banned.AddItem(selti); + break; + } +#else + DoMessageBox(TXT_ERROR,TXT_WRONGVERSION,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + break; +#endif + case 12: + { + //make obj allowed + UITextItem * selti = (UITextItem *)lb_generic_banned.GetItem(lb_generic_banned.GetSelectedIndex()); + if(!selti) + break; + p = selti->GetBuffer(); + objid = FindObjectIDName(IGNORE_TABLE((char *)p)); + if(objid!=-1) + { + ASSERT(Object_info[objid].type==OBJ_POWERUP); + ASSERT(Object_info[objid].multi_allowed==0); + Object_info[objid].multi_allowed = 1; + lb_generic_banned.RemoveItem(&obj_ti[objid]); + lb_generic_allowed.AddItem(&obj_ti[objid]); + } + break; + } + case 13: + { + //make obj not allowed + UITextItem * selti = (UITextItem *)lb_generic_allowed.GetItem(lb_generic_allowed.GetSelectedIndex()); + if(!selti) + break; + p = selti->GetBuffer(); + objid = FindObjectIDName(IGNORE_TABLE((char *)p)); + if(objid!=-1) + { + ASSERT(Object_info[objid].type==OBJ_POWERUP); + ASSERT(Object_info[objid].multi_allowed==1); + Object_info[objid].multi_allowed = 0; + lb_generic_allowed.RemoveItem(&obj_ti[objid]); + lb_generic_banned.AddItem(&obj_ti[objid]); + } + break; + } + default: + { + + } + } + } + // handle all UI results. + menu_wnd.Close(); + menu_wnd.Destroy(); +} + + +void MultiDoConfigSave(void) +{ + char file[_MAX_PATH*2]; + + ddio_MakePath(file,Base_directory,"custom","settings",NULL); + if(DoPathFileDialog(true,file,TXT_MULTISAVESET,"*.mps",0)) + { + if(strcmpi(file+(strlen(file)-4),".mps")!=0) + strcat(file,".mps"); + MultiSaveSettings(file); + } +} + +void MultiDoConfigLoad(void) +{ + char file[_MAX_PATH*2]; + + ddio_MakePath(file,Base_directory,"custom","settings",NULL); + if(DoPathFileDialog(false,file,TXT_MULTILOADSET,"*.mps",PFDF_FILEMUSTEXIST)) + MultiLoadSettings(file); +} + + +#define CHAR_LEFT_ARROW 24 +#define CHAR_UP_ARROW 25 +#define CHAR_RIGHT_ARROW 26 +#define CHAR_DOWN_ARROW 27 +#define CHAR_CHECKBOX_OFF 28 +#define CHAR_CHECKBOX_ON 29 +#define CHAR_RADIO_OFF 30 +#define CHAR_RADIO_ON 31 + + +#define CS_MODE_HS_ID 20 +#define PP_MODE_HS_ID 21 +#define ROT_VEL_HS_ID 22 +#define ACC_WEAP_HS_ID 24 +#define BRIGHT_SHIP_HS_ID 25 +#define RAND_POWERUP_ID 26 +#define PS_MODE_HS_ID 27 +#define MLOOK_HS_ID 28 +#define DIFF_LIST_ID 30 +#define SHIP_ALLOW_HS_ID 0xdd + +void MultiGameOptionsMenu (int alloptions) +{ + +#define MULTI_OPT_TITLE_Y 30 +#define MULTI_OPT_EDITS_Y 80 +#define MULTI_OPT_EDITS_X_LCOL1 40 +#define MULTI_OPT_EDITS_X_COL1 160 +#define MULTI_OPT_EDITS_X_LCOL2 300 +#define MULTI_OPT_EDITS_X_COL2 460 +#define MULTI_OPT_TOGGLES_Y 210 +#define MULTI_OPT_TOGGLES_X 40 +#define MULTI_OPT_TOGGLES_COL2_X 300 +#define MULTI_OPT_LABEL_OFS 10 +#define MULTI_OPT_HOTSPOT_X_MID 410 +#define MULTI_OPT_HOTSPOT_Y 360 + + UITextItem options_title_text(BIG_BRIEFING_FONT,TXT_GEN_MPLYROPTIONS,UICOL_WINDOW_TITLE); + UITextItem time_limit_text (TXT_GEN_TIMELIMIT,UICOL_TEXT_NORMAL); + UITextItem kill_goal_text (TXT_GEN_KILLGOAL,UICOL_TEXT_NORMAL); + UITextItem pps_text (TXT_GEN_PPS,UICOL_TEXT_NORMAL); + UITextItem respawn_rate_text (TXT_GEN_RESPAWNRATE,UICOL_TEXT_NORMAL); + UITextItem max_players_text (TXT_GEN_MAXPLAYERS,UICOL_TEXT_NORMAL); + + UITextItem exit_on_text (TXT_DONE, UICOL_HOTSPOT_HI); + UITextItem exit_off_text (TXT_DONE, UICOL_HOTSPOT_LO); + + UITextItem cancel_on_text (TXT_CANCEL, UICOL_HOTSPOT_HI); + UITextItem cancel_off_text (TXT_CANCEL, UICOL_HOTSPOT_LO); + + UITextItem ship_on_text (TXT_GEN_CFGALLOWEDSHIP, UICOL_HOTSPOT_HI); + UITextItem ship_off_text (TXT_GEN_CFGALLOWEDSHIP, UICOL_HOTSPOT_LO); + + UITextItem blank_text ("",UICOL_TEXT_NORMAL); + + char str[100]; + + + int cancel_id; + rendering_state rs; + rend_GetRenderState (&rs); + + + NewUIWindow main_wnd; + main_wnd.Create(0,0,rs.screen_width,rs.screen_height,UIF_PROCESS_ALL); + + int num_text=0,num_edit=0; + int return_id; + int id=0; + + UIText title_txt; + title_txt.Create(&main_wnd,&options_title_text,0,MULTI_OPT_TITLE_Y,UIF_CENTER); + int cury=MULTI_OPT_EDITS_Y; + // Time limit + UIText time_limit; + time_limit.Create(&main_wnd,&time_limit_text,MULTI_OPT_EDITS_X_LCOL1,cury+MULTI_OPT_LABEL_OFS,0); + NewUIEdit time_limit_edit; + time_limit_edit.Create(&main_wnd,id++,MULTI_OPT_EDITS_X_COL1,cury,100,15,0);cury+=20; + cury+=20; + + // Kill goal + UIText kill_goal; + kill_goal.Create(&main_wnd,&kill_goal_text,MULTI_OPT_EDITS_X_LCOL1,cury+MULTI_OPT_LABEL_OFS,0); + NewUIEdit kill_goal_edit; + kill_goal_edit.Create(&main_wnd,id++,MULTI_OPT_EDITS_X_COL1,cury,100,15,0); cury+=20; + cury+=20; + NewUIEdit max_players_edit; + UIText max_players; + if(alloptions) + { + // Max Players + max_players.Create(&main_wnd,&max_players_text,MULTI_OPT_EDITS_X_LCOL1,cury+MULTI_OPT_LABEL_OFS,0); + max_players_edit.Create(&main_wnd,id++,MULTI_OPT_EDITS_X_COL1,cury,100,15,0); cury+=20; + } + cury+=20; + + cury=MULTI_OPT_EDITS_Y; + + // Packets per second + UIText pps; + pps.Create(&main_wnd,&pps_text,MULTI_OPT_EDITS_X_LCOL2,cury+MULTI_OPT_LABEL_OFS,0); + NewUIEdit pps_edit; + pps_edit.Create(&main_wnd,id++,MULTI_OPT_EDITS_X_COL2,cury,100,15,0); cury+=20; + cury+=20; + + // Respawn rate + UIText respawn; + respawn.Create(&main_wnd,&respawn_rate_text,MULTI_OPT_EDITS_X_LCOL2,cury+MULTI_OPT_LABEL_OFS,0); + NewUIEdit respawn_edit; + respawn_edit.Create(&main_wnd,id++,MULTI_OPT_EDITS_X_COL2,cury,100,15,0); cury+=20; + + cury = MULTI_OPT_TOGGLES_Y; + UIHotspot cs_mode_hs; + UIHotspot pp_mode_hs; + UIHotspot pcs_mode_hs; + sprintf(str,"%c %s",CHAR_CHECKBOX_ON,TXT_GEN_USEROTVEL); + UITextItem rot_vel_sel_txt_on(str,UICOL_HOTSPOT_LO); + UITextItem rot_vel_sel_txt_off(str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_CHECKBOX_OFF,TXT_GEN_USEROTVEL); + UITextItem rot_vel_unsel_txt_on (str,UICOL_HOTSPOT_LO); + UITextItem rot_vel_unsel_txt_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_CHECKBOX_ON,TXT_GEN_BRIGHT_PLAYERS); + UITextItem bright_players_sel_on (str,UICOL_HOTSPOT_LO); + UITextItem bright_players_sel_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_CHECKBOX_OFF,TXT_GEN_BRIGHT_PLAYERS); + UITextItem bright_players_unsel_on (str,UICOL_HOTSPOT_LO); + UITextItem bright_players_unsel_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_RADIO_ON,TXT_GEN_CLIENTSERVER); + UITextItem cs_mode_sel_text_on (str,UICOL_HOTSPOT_HI); + UITextItem cs_mode_sel_text_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_RADIO_OFF,TXT_GEN_CLIENTSERVER); + UITextItem cs_mode_unsel_text_on (str,UICOL_HOTSPOT_HI); + UITextItem cs_mode_unsel_text_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_RADIO_ON,TXT_GEN_PEERPEER); + UITextItem pp_mode_sel_text_on (str,UICOL_HOTSPOT_HI); + UITextItem pp_mode_sel_text_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_RADIO_OFF,TXT_GEN_PEERPEER); + UITextItem pp_mode_unsel_text_on (str,UICOL_HOTSPOT_HI); + UITextItem pp_mode_unsel_text_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_RADIO_ON,TXT_GEN_PERMISSABLE_CS); + UITextItem pcs_mode_sel_text_on (str,UICOL_HOTSPOT_HI); + UITextItem pcs_mode_sel_text_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,"%c %s",CHAR_RADIO_OFF,TXT_GEN_PERMISSABLE_CS); + UITextItem pcs_mode_unsel_text_on (str,UICOL_HOTSPOT_HI); + UITextItem pcs_mode_unsel_text_off (str,UICOL_HOTSPOT_LO); + + sprintf(str,TXT_GEN_ACC_WEAP_COLL,CHAR_CHECKBOX_ON); + UITextItem acc_weap_coll_sel_off (str,UICOL_HOTSPOT_LO); + UITextItem acc_weap_coll_sel_on (str,UICOL_HOTSPOT_HI); + + sprintf(str,TXT_GEN_ACC_WEAP_COLL,CHAR_CHECKBOX_OFF); + UITextItem acc_weap_coll_unsel_off (str,UICOL_HOTSPOT_LO); + UITextItem acc_weap_coll_unsel_on (str,UICOL_HOTSPOT_HI); + + sprintf(str,"%c %s",CHAR_CHECKBOX_ON,TXT_RANDOMPOWERUPRESPAWN); + UITextItem powerup_respawn_sel_off (str,UICOL_HOTSPOT_LO); + UITextItem powerup_respawn_sel_on (str,UICOL_HOTSPOT_HI); + + sprintf(str,"%c %s",CHAR_CHECKBOX_OFF,TXT_RANDOMPOWERUPRESPAWN); + UITextItem powerup_respawn_unsel_off (str,UICOL_HOTSPOT_LO); + UITextItem powerup_respawn_unsel_on (str,UICOL_HOTSPOT_HI); + + sprintf(str,"%c %s",CHAR_CHECKBOX_ON,TXT_ALLOWMLOOK); + UITextItem mouselookers_sel_off (str,UICOL_HOTSPOT_LO); + UITextItem mouselookers_sel_on (str,UICOL_HOTSPOT_HI); + + sprintf(str,"%c %s",CHAR_CHECKBOX_OFF,TXT_ALLOWMLOOK); + UITextItem mouselookers_unsel_off (str,UICOL_HOTSPOT_LO); + UITextItem mouselookers_unsel_on (str,UICOL_HOTSPOT_HI); + + + int cs_y; + int pp_y; + int pcs_y; + UIHotspot rot_vel_hs; + UIHotspot acc_weap_hs; + UIHotspot powerup_hs; + UIHotspot mlook_hs; + UIHotspot bright_players_hs; + + int rotvely; + int acc_weap_y; + int powerup_y; + int mlook_y; + int bright_players_y; + int diff_y; + + cury -= 15; + diff_y = cury; + if(alloptions) + { + + cury +=15; + if(Netgame.flags & NF_PEER_PEER) + { + cs_mode_hs.Create(&main_wnd,CS_MODE_HS_ID,KEY_C,&cs_mode_unsel_text_off,&cs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + cs_y = cury; + cury+=20; + + pp_mode_hs.Create(&main_wnd,PP_MODE_HS_ID,KEY_C,&pp_mode_sel_text_off,&pp_mode_sel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + pp_y = cury; + cury+=20; + + pcs_mode_hs.Create(&main_wnd,PS_MODE_HS_ID,KEY_C,&pcs_mode_unsel_text_off,&pcs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + pcs_y = cury; + cury+=20; + } + else if (Netgame.flags & NF_PERMISSABLE) + { + //// + cs_mode_hs.Create(&main_wnd,CS_MODE_HS_ID,KEY_C,&cs_mode_unsel_text_off,&cs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + cs_y = cury; + cury+=20; + + pp_mode_hs.Create(&main_wnd,PP_MODE_HS_ID,KEY_C,&pp_mode_unsel_text_off,&pp_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + pp_y = cury; + cury+=20; + + pcs_mode_hs.Create(&main_wnd,PS_MODE_HS_ID,KEY_C,&pcs_mode_sel_text_off,&pcs_mode_sel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + pcs_y = cury; + cury+=20; + } + else + { + cs_mode_hs.Create(&main_wnd,CS_MODE_HS_ID,KEY_C,&cs_mode_sel_text_off,&cs_mode_sel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + cs_y = cury; + cury+=20; + pp_mode_hs.Create(&main_wnd,PP_MODE_HS_ID,KEY_C,&pp_mode_unsel_text_off,&pp_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + pp_y = cury; + cury+=20; + + pcs_mode_hs.Create(&main_wnd,PS_MODE_HS_ID,KEY_C,&pcs_mode_unsel_text_off,&pcs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cury,180,30,UIF_FIT); + pcs_y = cury; + cury+=20; + } + + cury += 10; + diff_y = cury; + + // Rotvel toggle + + cury = MULTI_OPT_TOGGLES_Y; + if(Netgame.flags & NF_SENDROTVEL) + { + rot_vel_hs.Create(&main_wnd,ROT_VEL_HS_ID,0,&rot_vel_sel_txt_off,&rot_vel_sel_txt_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + else + { + rot_vel_hs.Create(&main_wnd,ROT_VEL_HS_ID,0,&rot_vel_unsel_txt_off,&rot_vel_unsel_txt_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + rotvely = cury; + cury+=20; + } + // Accurate collisions toggle + if(Netgame.flags & NF_USE_ACC_WEAP) + { + acc_weap_hs.Create(&main_wnd,ACC_WEAP_HS_ID,KEY_A,&acc_weap_coll_sel_off,&acc_weap_coll_sel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + else + { + acc_weap_hs.Create(&main_wnd,ACC_WEAP_HS_ID,KEY_A,&acc_weap_coll_unsel_off,&acc_weap_coll_unsel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + acc_weap_y = cury; + cury+=20; + + // Bright players toggle + + if(Netgame.flags & NF_BRIGHT_PLAYERS) + { + bright_players_hs.Create(&main_wnd,BRIGHT_SHIP_HS_ID,KEY_B,&bright_players_sel_off,&bright_players_sel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + else + { + bright_players_hs.Create(&main_wnd,BRIGHT_SHIP_HS_ID,KEY_B,&bright_players_unsel_off,&bright_players_unsel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + bright_players_y = cury; + cury+=20; + + if(Netgame.flags & NF_RANDOMIZE_RESPAWN) + { + powerup_hs.Create(&main_wnd,RAND_POWERUP_ID,KEY_B,&powerup_respawn_sel_off,&powerup_respawn_sel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + else + { + powerup_hs.Create(&main_wnd,RAND_POWERUP_ID,KEY_B,&powerup_respawn_unsel_off,&powerup_respawn_unsel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + powerup_y = cury; + cury+=20; + + if(Netgame.flags & NF_ALLOW_MLOOK) + { + mlook_hs.Create(&main_wnd,MLOOK_HS_ID,KEY_B,&mouselookers_sel_off,&mouselookers_sel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + else + { + mlook_hs.Create(&main_wnd,MLOOK_HS_ID,KEY_B,&mouselookers_unsel_off,&mouselookers_unsel_on,MULTI_OPT_TOGGLES_COL2_X,cury,180,30,UIF_FIT); + } + mlook_y = cury; + cury+=20; + + //Difficulty Listbox & title + UITextItem diff_title_text(TXT_PLTDIFFICULT,UICOL_WINDOW_TITLE); + UITextItem trainee(TXT_TRAINEE,GR_LIGHTGRAY); + UITextItem rookie(TXT_ROOKIE,GR_LIGHTGRAY); + UITextItem hotshot(TXT_HOTSHOT,GR_LIGHTGRAY); + UITextItem ace(TXT_ACE,GR_LIGHTGRAY); + UITextItem insane(TXT_INSANE,GR_LIGHTGRAY); + + UIText diff_title; + + diff_title.Create(&main_wnd,&diff_title_text,MULTI_OPT_TOGGLES_X,diff_y,0);diff_y+=15; + NewUIListBox diff_list; + + diff_list.SetSelectedColor(UICOL_LISTBOX_HI); + diff_list.SetHiliteColor(UICOL_LISTBOX_HI); + diff_list.Create(&main_wnd,DIFF_LIST_ID,MULTI_OPT_TOGGLES_X,diff_y,160,96,UILB_NOSORT); + + diff_list.AddItem(&trainee); + diff_list.AddItem(&rookie); + diff_list.AddItem(&hotshot); + diff_list.AddItem(&ace); + diff_list.AddItem(&insane); + + switch(Netgame.difficulty) + { + case 0: + diff_list.SelectItem(&trainee); + break; + case 1: + diff_list.SelectItem(&rookie); + break; + case 2: + diff_list.SelectItem(&hotshot); + break; + case 3: + diff_list.SelectItem(&ace); + break; + case 4: + diff_list.SelectItem(&insane); + break; + } + + + + cury = MULTI_OPT_HOTSPOT_Y; + //int hsmid = MULTI_OPT_HOTSPOT_X_MID - (DLLgrtext_GetTextLineWidth(TXT_GEN_CFGALLOWEDSHIP)/2); + UIHotspot ship_hs; + ship_hs.Create(&main_wnd,SHIP_ALLOW_HS_ID,KEY_S,&ship_off_text,&ship_on_text,0,cury,180,30,UIF_FIT|UIF_CENTER); cury+=20; + return_id=UID_OK; + cury+=10; + //hsmid = MULTI_OPT_HOTSPOT_X_MID - (DLLgrtext_GetTextLineWidth(TXT_GEN_PREVMENU)/2); + UIHotspot exit_hs; + exit_hs.Create(&main_wnd,UID_OK,KEY_ESC,&exit_off_text,&exit_on_text, 0,cury, 180, 30,UIF_FIT|UIF_CENTER); cury+=20; + cancel_id = UID_CANCEL; +/// hsmid = MULTI_OPT_HOTSPOT_X_MID - (DLLgrtext_GetTextLineWidth(TXT_GEN_CANCEL)/2); + UIHotspot cancel_hs; + cancel_hs.Create(&main_wnd,UID_CANCEL,0,&cancel_off_text,&cancel_on_text, 0,cury, 180, 30,UIF_FIT|UIF_CENTER); cury+=20; + // Bash some values + + if (Netgame.flags & NF_TIMER) + sprintf (str,"%d",Netgame.timelimit); + else + sprintf (str,"%d",0); + + time_limit_edit.SetText(str); + + if (Netgame.flags & NF_KILLGOAL) + sprintf (str,"%d",Netgame.killgoal); + else + sprintf (str,"%d",0); + + kill_goal_edit.SetText(str); + + sprintf (str,"%d",Netgame.packets_per_second); + pps_edit.SetText(str); + + sprintf (str,"%d",Netgame.max_players); + if(alloptions) + { + max_players_edit.SetText(str); + } + + sprintf (str,"%d",Netgame.respawn_time); + respawn_edit.SetText(str); + + main_wnd.LoadBackgroundImage("multimain.ogf"); + main_wnd.Open(); + int exit_menu=0; + + while (!exit_menu) + { + int res; + + res = DoUI(); + if(res==cancel_id) + { + exit_menu = true; + break; + } + if(alloptions) + { + if(res==CS_MODE_HS_ID) + { + cs_mode_hs.Destroy(); + pp_mode_hs.Destroy(); + pcs_mode_hs.Destroy(); + + cs_mode_hs.Create(&main_wnd,CS_MODE_HS_ID,KEY_C,&cs_mode_sel_text_off,&cs_mode_sel_text_on,MULTI_OPT_TOGGLES_X,cs_y,180,30,UIF_FIT); + pp_mode_hs.Create(&main_wnd,PP_MODE_HS_ID,KEY_C,&pp_mode_unsel_text_off,&pp_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,pp_y,180,30,UIF_FIT); + pcs_mode_hs.Create(&main_wnd,PS_MODE_HS_ID,KEY_C,&pcs_mode_unsel_text_off,&pcs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,pcs_y,180,30,UIF_FIT); + Netgame.flags &= ~NF_PEER_PEER; + Netgame.flags &= ~NF_PERMISSABLE; + mprintf((0,"Using Client/Server model\n")); + } + else if(res==PP_MODE_HS_ID) + { + cs_mode_hs.Destroy(); + pp_mode_hs.Destroy(); + pcs_mode_hs.Destroy(); + + cs_mode_hs.Create(&main_wnd,CS_MODE_HS_ID,KEY_C,&cs_mode_unsel_text_off,&cs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cs_y,180,30,UIF_FIT); + pp_mode_hs.Create(&main_wnd,PP_MODE_HS_ID,KEY_C,&pp_mode_sel_text_off,&pp_mode_sel_text_on,MULTI_OPT_TOGGLES_X,pp_y,180,30,UIF_FIT); + pcs_mode_hs.Create(&main_wnd,PS_MODE_HS_ID,KEY_C,&pcs_mode_unsel_text_off,&pcs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,pcs_y,180,30,UIF_FIT); + Netgame.flags |= NF_PEER_PEER; + Netgame.flags &= ~NF_PERMISSABLE; + + mprintf((0,"Using Peer/Peer model\n")); + } + else if (res==PS_MODE_HS_ID) + { + cs_mode_hs.Destroy(); + pp_mode_hs.Destroy(); + pcs_mode_hs.Destroy(); + + cs_mode_hs.Create(&main_wnd,CS_MODE_HS_ID,KEY_C,&cs_mode_unsel_text_off,&cs_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,cs_y,180,30,UIF_FIT); + pp_mode_hs.Create(&main_wnd,PP_MODE_HS_ID,KEY_C,&pp_mode_unsel_text_off,&pp_mode_unsel_text_on,MULTI_OPT_TOGGLES_X,pp_y,180,30,UIF_FIT); + pcs_mode_hs.Create(&main_wnd,PS_MODE_HS_ID,KEY_C,&pcs_mode_sel_text_off,&pcs_mode_sel_text_on,MULTI_OPT_TOGGLES_X,pcs_y,180,30,UIF_FIT); + Netgame.flags |= NF_PERMISSABLE; + Netgame.flags &= ~NF_PEER_PEER; + + mprintf((0,"Using Permissable model\n")); + } + else if(res==ROT_VEL_HS_ID) + { + rot_vel_hs.Destroy(); + + if(Netgame.flags & NF_SENDROTVEL) + { + rot_vel_hs.Create(&main_wnd,ROT_VEL_HS_ID,0,&rot_vel_unsel_txt_off,&rot_vel_unsel_txt_on,MULTI_OPT_TOGGLES_COL2_X,rotvely,180,30,UIF_FIT); + Netgame.flags &= ~NF_SENDROTVEL; + } + else + { + rot_vel_hs.Create(&main_wnd,ROT_VEL_HS_ID,0,&rot_vel_sel_txt_off,&rot_vel_sel_txt_on,MULTI_OPT_TOGGLES_COL2_X,rotvely,180,30,UIF_FIT); + Netgame.flags |= NF_SENDROTVEL; + } + } + + } + + if (res==SHIP_ALLOW_HS_ID){ + //Ships allowed + main_wnd.Close(); + DoMultiAllowed(); + main_wnd.Open(); + } + else if (res==ACC_WEAP_HS_ID) + { + acc_weap_hs.Destroy(); + if(Netgame.flags & NF_USE_ACC_WEAP) + { + acc_weap_hs.Create(&main_wnd,ACC_WEAP_HS_ID,KEY_A,&acc_weap_coll_unsel_off,&acc_weap_coll_unsel_on,MULTI_OPT_TOGGLES_COL2_X,acc_weap_y,180,30,UIF_FIT); + Netgame.flags &= ~NF_USE_ACC_WEAP; + } + else + { + acc_weap_hs.Create(&main_wnd,ACC_WEAP_HS_ID,KEY_A,&acc_weap_coll_sel_off,&acc_weap_coll_sel_on,MULTI_OPT_TOGGLES_COL2_X,acc_weap_y,180,30,UIF_FIT); + Netgame.flags |= NF_USE_ACC_WEAP; + + } + + } + else if (res==BRIGHT_SHIP_HS_ID) + { + bright_players_hs.Destroy(); + + if(Netgame.flags & NF_BRIGHT_PLAYERS) + { + bright_players_hs.Create(&main_wnd,BRIGHT_SHIP_HS_ID,KEY_B,&bright_players_unsel_off,&bright_players_unsel_on,MULTI_OPT_TOGGLES_COL2_X,bright_players_y,180,30,UIF_FIT); + Netgame.flags &= ~NF_BRIGHT_PLAYERS; + } + else + { + bright_players_hs.Create(&main_wnd,BRIGHT_SHIP_HS_ID,KEY_B,&bright_players_sel_off,&bright_players_sel_on,MULTI_OPT_TOGGLES_COL2_X,bright_players_y,180,30,UIF_FIT); + Netgame.flags |= NF_BRIGHT_PLAYERS; + } + } + + else if (res==RAND_POWERUP_ID) + { + powerup_hs.Destroy(); + + if(Netgame.flags & NF_RANDOMIZE_RESPAWN) + { + powerup_hs.Create(&main_wnd,RAND_POWERUP_ID,KEY_B,&powerup_respawn_unsel_off,&powerup_respawn_unsel_on,MULTI_OPT_TOGGLES_COL2_X,powerup_y,180,30,UIF_FIT); + Netgame.flags &= ~NF_RANDOMIZE_RESPAWN; + } + else + { + powerup_hs.Create(&main_wnd,RAND_POWERUP_ID,KEY_B,&powerup_respawn_sel_off,&powerup_respawn_sel_on,MULTI_OPT_TOGGLES_COL2_X,powerup_y,180,30,UIF_FIT); + Netgame.flags |= NF_RANDOMIZE_RESPAWN; + } + } + + else if (res==MLOOK_HS_ID) + { + mlook_hs.Destroy(); + + if(Netgame.flags & NF_ALLOW_MLOOK) + { + mlook_hs.Create(&main_wnd,MLOOK_HS_ID,KEY_B,&mouselookers_unsel_off,&mouselookers_unsel_on,MULTI_OPT_TOGGLES_COL2_X,mlook_y,180,30,UIF_FIT); + Netgame.flags &= ~NF_ALLOW_MLOOK; + } + else + { + mlook_hs.Create(&main_wnd,MLOOK_HS_ID,KEY_B,&mouselookers_sel_off,&mouselookers_sel_on,MULTI_OPT_TOGGLES_COL2_X,mlook_y,180,30,UIF_FIT); + Netgame.flags |= NF_ALLOW_MLOOK; + } + } + else if (res==return_id) + { + int val; + + // Get kill goal + //edits[killgoal_edit].GetText (str,100); + kill_goal_edit.GetText(str,100); + val=atoi(str); + if (val>1) + { + if (val>10000) + val=10000; + Netgame.flags|=NF_KILLGOAL; + } + else + { + Netgame.flags&=~NF_KILLGOAL; + val=0; + } + + Netgame.killgoal=val; + + // Get timelimit + //edits[timer_edit].GetText (str,100); + time_limit_edit.GetText(str,100); + val=atoi(str); + if (val>0) + { + if (val>10000) + val=10000; + Netgame.flags|=NF_TIMER; + } + else + { + Netgame.flags&=~NF_TIMER; + val=0; + } + + Netgame.timelimit=val; + + // Get packets per second + pps_edit.GetText(str,100); + val=atoi(str); + Netgame.packets_per_second=val; + if (val>20) + val=20; + if (val<2) + val=2; + + if(alloptions) + { + // Get Max players + max_players_edit.GetText(str,100); + val=atoi(str); + Netgame.max_players = val; + if (val>MAX_NET_PLAYERS) + Netgame.max_players=MAX_NET_PLAYERS; + if (val<2) + val=2; + } + // Get respawn rate + //edits[respawn_edit].GetText (str,100); + respawn_edit.GetText(str,100); + val=atoi(str); + if (val<15) + val=15; + if (val>300) + val=300; + + Netgame.respawn_time=val; + + int difficulty = diff_list.GetSelectedIndex(); + + Netgame.difficulty = difficulty; + + exit_menu=1; + } + + } + + main_wnd.Close(); + main_wnd.Destroy(); + +} + + +int ReturnMultiplayerGameMenu(void) +{ + MultiFlushAllIncomingBuffers(); + CallMultiDLL(MT_RETURN_TO_GAME_LIST); + return MultiDLLGameStarting; +} + + +int MultiLevelSelection(void) +{ + if(Current_mission.num_levels==1) + return 1; + + Current_mission.cur_level = DisplayLevelWarpDlg(Current_mission.num_levels); + + if(Current_mission.cur_level < 1) + { + Current_mission.cur_level = 1; + return -1; + } + return Current_mission.cur_level; +} + +bool DoPlayerMouselookCheck(unsigned int flags) +{ + if(Current_pilot.mouselook_control) + { + if(flags & NF_ALLOW_MLOOK) + { + return true; + } + else + { + int ret = DoMessageBox(TXT_NOMOUSELOOK,TXT_MLOOK_JOINANYWAY,MSGBOX_YESNO); + return ret?true:false; + } + } + + return true; +} + +#define GAME_INFO_MENU_W 384 +#define GAME_INFO_MENU_H 384 +#define GAME_INFO_MENU_X 320 - (GAME_INFO_MENU_W/2) +#define GAME_INFO_MENU_Y 240 - (GAME_INFO_MENU_H/2) + +#define GAME_INFO_COL1 10 + +#define GAME_INFO_GOTO_NEXT_LINE cury+=13 + +#define UID_GAMEINFO_PLAYERS 1000 + +extern char *Multi_recieved_player_list; +extern bool Multi_got_player_list; + +void RequestPlayerList(network_address *addr); + +#define ASK_PLAYERS_RETRY_INTERVAL 2.0 +#define ASK_PLAYERS_RETRIES 4 + +//Display a dialog box showing the information relating to this particular net game +void ShowNetgameInfo(network_game *game) +{ + char str[200]; + int cury = 10; + int exit_menu = 0; + newuiTiledWindow menu_wnd; + newuiListBox *lists; + newuiSheet *sheet; + char *plyr; + + ASSERT(game); + + menu_wnd.Create(TXT_GAME_INFO,0,0,GAME_INFO_MENU_W,GAME_INFO_MENU_H); + sheet = menu_wnd.GetSheet(); + + sheet->NewGroup(game->name,GAME_INFO_COL1,cury); cury+=15; + sprintf(str,TXT_GINFO_MISSION,game->mission_name); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + sprintf(str,TXT_PLAYERS_X_OF_X,game->curr_num_players,game->max_num_players); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + char *szdiff = TXT_ERROR; + + switch(game->difficulty) + { + case 0: + szdiff = TXT_TRAINEE; + break; + case 1: + szdiff = TXT_ROOKIE; + break; + case 2: + szdiff = TXT_HOTSHOT; + break; + case 3: + szdiff = TXT_ACE; + break; + case 4: + szdiff = TXT_INSANE; + break; + default: + szdiff = TXT_ERROR; + } + + sprintf(str,"%s: %s",TXT_PLTDIFFICULT,szdiff); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + char *mode; + if((game->flags & NF_PEER_PEER)) + { + mode = TXT_GEN_PEERPEER; + } + else if((game->flags & NF_PERMISSABLE)) + { + mode = TXT_GEN_PERMISSABLE_CS; + } + else + { + mode = TXT_GEN_CLIENTSERVER; + } + + if(game->dedicated_server) + sprintf(str,TXT_DEDICATED_SERVER,mode); + else + sprintf(str,TXT_NONDEDICATED_SERVER,mode); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + sprintf(str,TXT_GINFO_ALLOWMOUSELOOK,(game->flags & NF_ALLOW_MLOOK)?TXT_DOES:TXT_DOESNOT); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + sprintf(str,"%s: %s",TXT_GEN_BRIGHT_PLAYERS,(game->flags & NF_BRIGHT_PLAYERS)?TXT_ON:TXT_OFF); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + sprintf(str,TXT_GINFO_ACC_WEAPCOLL,(game->flags & NF_USE_ACC_WEAP)?TXT_ON:TXT_OFF); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + sprintf(str,TXT_USE_ROTATIONAL_VEL,(game->flags & NF_SENDROTVEL)?TXT_ON:TXT_OFF); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + sprintf(str,TXT_RANDOMIZEPOWERUPSPAWN,(game->flags & NF_RANDOMIZE_RESPAWN)?TXT_ON:TXT_OFF); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + //GAME_INFO_GOTO_NEXT_LINE; + + sprintf(str,"%s:",TXT_GAMEINFOPLAYERSTITLE); + sheet->NewGroup(str,GAME_INFO_COL1,cury); GAME_INFO_GOTO_NEXT_LINE; + + lists = sheet->AddListBox(256,128,UID_GAMEINFO_PLAYERS); + lists->RemoveAll(); + + + sheet->NewGroup(NULL, (GAME_INFO_MENU_W-140), GAME_INFO_MENU_H-95, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_DONE,UID_CANCEL); + //Get players + Multi_recieved_player_list = (char *)mem_malloc(((CALLSIGN_LEN+1)*MAX_NET_PLAYERS)+1); + memset(Multi_recieved_player_list,0,((CALLSIGN_LEN+1)*MAX_NET_PLAYERS)+1); + + Multi_got_player_list = false; + + CreateSplashScreen(TXT_GETTINGPLAYERLIST,1); + + RequestPlayerList(&game->addr); + float last_asked_time = timer_GetTime(); + int retries = 0; + do + { + MultiProcessIncoming(); + int res = PollUI(); + //If the user hit cancel... + if(res==99) + { + CloseSplashScreen(); + goto no_players; + } + if((timer_GetTime()-last_asked_time)>ASK_PLAYERS_RETRY_INTERVAL) + { + last_asked_time = timer_GetTime(); + RequestPlayerList(&game->addr); + retries++; + } + if(retries >= ASK_PLAYERS_RETRIES) + { + DoMessageBox(TXT_ERROR,TXT_CANTGETPLAYERLIST,MSGBOX_OK); + CloseSplashScreen(); + goto no_players; + } + + }while(!Multi_got_player_list); + + CloseSplashScreen(); + + //Ok, now we've got the list of players.. let's parse it and add the players to the list + plyr = Multi_recieved_player_list; + + while(*plyr) + { + lists->AddItem(plyr); + //Move on to the next item + while(*plyr) + { + plyr++; + } + //Move past the NULL + plyr++; + } + + if(Multi_recieved_player_list) + { + mem_free(Multi_recieved_player_list); + Multi_recieved_player_list = NULL; + } + + + + + menu_wnd.Open(); + + while (!exit_menu) + { + int res; + + res = menu_wnd.DoUI(); + + // handle all UI results. + switch(res) + { + case UID_CANCEL: + menu_wnd.Close(); + exit_menu = 1; + break; + } + } + menu_wnd.Destroy(); + +no_players: + if(Multi_recieved_player_list) + { + mem_free(Multi_recieved_player_list); + Multi_recieved_player_list = NULL; + } +} \ No newline at end of file diff --git a/Descent3/multi_ui.h b/Descent3/multi_ui.h new file mode 100644 index 000000000..3fa6b4f2a --- /dev/null +++ b/Descent3/multi_ui.h @@ -0,0 +1,21 @@ +#ifndef MULTI_UI_H +#define MULTI_UI_H + +// The first multiplayer menu that the user will see...all multiplayer stuff is +// reached from this menu +// Returns true if we're starting a multiplayer game +int MainMultiplayerMenu (); +int SearchMasterTrackerGameMenu (); +int LoginMasterTrackerGameMenu (); +int AutoConnectPXO(); +int AutoConnectLANIP(); +int AutoConnectHeat(); +void DoMultiAllowed(void); +extern int MultiDLLGameStarting; +void MultiDoConfigLoad(void); +void MultiDoConfigSave(void); +void MultiGameOptionsMenu (int alloptions); +int ReturnMultiplayerGameMenu(void); +int MultiLevelSelection(void); +bool DoPlayerMouselookCheck(unsigned int flags); +#endif \ No newline at end of file diff --git a/Descent3/multi_world_state.h b/Descent3/multi_world_state.h new file mode 100644 index 000000000..3401d46c1 --- /dev/null +++ b/Descent3/multi_world_state.h @@ -0,0 +1,52 @@ +#ifndef MULTI_WORLD_STATE_H +#define MULTI_WORLD_STATE_H + +#define WS_ROOM_WIND 0 +#define WS_ROOM_FOG 1 +#define WS_ROOM_LIGHTING 2 +#define WS_ROOM_TEXTURE 3 +#define WS_ROOM_PORTAL_RENDER 4 +#define WS_WEATHER 5 +#define WS_WAYPOINT 6 +#define WS_DOOR 7 +#define WS_LIGHT_DISTANCE 8 +#define WS_SPEW 9 +#define WS_PLAYERBF 10 +#define WS_ROOM_DAMAGE 11 +#define WS_ROOM_REFUEL 12 +#define WS_LIGHT_COLOR 13 +#define WS_ROOM_GLASS 14 +#define WS_BUDDYBOTS 15 +#define WS_BUDDYBOTUPDATE 16 +#define WS_ROOM_PORTAL_BLOCK 17 +#define WS_LEVELGOAL 18 +#define WS_ROOM_GOALSPECFLAG 19 +#define WS_OBJECT_ATTACH 20 +#define WS_NO_RENDER 21 +#define WS_OBJECT_PHYS_FLAGS 22 +#define WS_MOTD 23 +#define WS_END 100 + +// WEATHER! +//WAYPOINT! + +// Room change flags +#define RCF_WIND 1 +#define RCF_FOG (1<<1) +#define RCF_AMBIENT_SOUND (1<<2) +#define RCF_LIGHTING (1<<3) +#define RCF_CHANGING_WIND_FOG (1<<4) +#define RCF_TEXTURE (1<<5) +#define RCF_PORTAL_RENDER (1<<6) +#define RCF_DAMAGE (1<<7) +#define RCF_REFUEL (1<<8) +#define RCF_PORTAL_BLOCK (1<<9) +#define RCF_GOALSPECIAL_FLAGS (1<<10) //Goal flags/special 1-6 flags have changed + +// Object change flags +#define OCF_NO_RENDER 1 +#define OCF_LIGHT_DISTANCE (1<<1) +#define OCF_LIGHT_COLOR (1<<2) +#define OCF_PHYS_FLAGS (1<<3) + +#endif \ No newline at end of file diff --git a/Descent3/multisafe.cpp b/Descent3/multisafe.cpp new file mode 100644 index 000000000..f10566759 --- /dev/null +++ b/Descent3/multisafe.cpp @@ -0,0 +1,3253 @@ +/* + * $Logfile: /DescentIII/Main/multisafe.cpp $ + * $Revision: 210 $ + * $Date: 9/13/01 11:38a $ + * $Author: Matt $ + * + * Multiplayer safe code + * + * $Log: /DescentIII/Main/multisafe.cpp $ + * + * 210 9/13/01 11:38a Matt + * Made the get-player-objhandle function not return the handle for + * players in observer mode. + * + * 209 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 208 11/03/99 8:47p Chris + * Improved the auto-select algorithm for dual fire secondary weapons + * + * 207 11/03/99 6:29p Chris + * Improved the auto-selection code for dual fire secondary weapons + * + * 206 10/20/99 5:41p Chris + * Added Red GB + * + * 205 10/20/99 4:08p 3dsmax + * don't delete invisible powerup when deleting powerups from room + * + * 204 10/16/99 8:45p Jeff + * multisafe function to remove all powerups from a room + * + * 203 7/28/99 3:54p Kevin + * MAC + * + * 202 5/26/99 2:05a Jason + * fixed world position bug + * + * 201 5/23/99 9:07p Matt + * Changed the customtext2 multisafe functions to create the item when + * it's updated, instead of having a separate create and update functions. + * This deals with players who join a netgame after the level has started + * (i.e., everyone but the server). + * + * 200 5/23/99 5:58p Jason + * don't send rotational drag to clients + * + * 199 5/23/99 5:50p Chris + * Fixed problems with msafe send on over non-changed information + * + * 198 5/23/99 12:33a Chris + * Cannot change the control type of dying objects + * + * 197 5/22/99 12:33a Jason + * added sending of physics flags upon joining + * + * 196 5/21/99 4:06p Matt + * For customtext2 HUD items, store the buffer length in the HUD item. + * + * 195 5/21/99 3:06p Matt + * Changed customtext2 HUD items so that the HUD system keeps the text + * buffer an no one else references it directly. + * + * 194 5/20/99 5:49p Matt + * Added a HUD item flag, for use by Dallas-created items, that makes a + * HUD item persisitent for the duration of one level, but get cleared + * between levels. + * + * 193 5/20/99 4:12p Jeff + * fixed up add to inventory + * + * 192 5/20/99 2:48a Matt + * Changed stat type for timer & custom HUD messages. + * + * 191 5/19/99 4:23p Chris + * Fixed level goal problems with keys and persistant inventory items + * + * 190 5/19/99 4:01p Jason + * handle robot firing + * + * 189 5/19/99 12:12p Jason + * fixed some issues with multiplayer + * + * 188 5/19/99 11:26a Matt + * Added multisafe functions & Dallas actions for showing a timer on the + * screen and adding custom HUD messages. + * + * 187 5/18/99 6:09p Kevin + * for Jeff. fixed crash in multiplayer + * + * 186 5/17/99 6:22p Jeff + * added filtered HUD messages + * + * 185 5/17/99 4:09p Jeff + * fixed secondary autoselect if the thief steals your currently selected + * secondary and you have no more + * + * 184 5/11/99 11:49a Matt + * Added the ability to specify a name when adding an item to the + * inventory. + * + * 183 5/10/99 10:23p Ardussi + * changes to compile on Mac + * + * 182 5/10/99 2:37a Jason + * fixed potential crash bug with lightning effects + * + * 181 5/09/99 10:49p Jason + * fix string chopping in multiplayer games + * + * 180 5/08/99 4:06p Matt + * Made Guidebot name translation work for game messages. + * + * 179 5/07/99 11:55p Matt + * Made player key action work if passed a player weapon or a player. + * + * 178 5/07/99 10:57p Chris + * Added a spew uvec dir + * + * 177 5/07/99 10:49p Matt + * For GB HUD messages, only make the sound if the message is actually + * added (i.e., if it isn't the same as the last message). + * + * 176 5/07/99 1:32p Jeff + * made osipf for sounds multisafe + * + * 175 5/06/99 12:52p Jason + * fixed set on fire bug + * + * 174 5/06/99 5:44a Chris + * Exclude GBs from the delete robot lists + * + * 173 5/06/99 4:11a Jeff + * created multisafe function to destroy robots in the level (helps boss + * fights) + * + * 172 5/05/99 12:55a Chris + * Setting a dead object's shields makes it no longer OF_AI_DEATH + * + * 171 5/04/99 12:13p Matt + * Renamed fire-flare symbol to fire-weapon. + * + * 170 5/04/99 1:04a Matt + * Turned fire flare function into fire weapon, though I haven't yet + * actually changed the name. + * + * 169 5/02/99 5:09a Jeff + * send levelgoal state to joining clients + * + * 168 5/02/99 1:36a Jason + * added moving object lighting viseffects + * + * 167 5/01/99 1:41a Jeff + * made levelgoals multi friendly + * + * 166 4/30/99 4:32p Matt + * Added a Dallas action to fire a flare from an object. + * + * 165 4/29/99 7:21p Jason + * fixed some wacky spew issues + * + * 164 4/29/99 1:28p Jeff + * fixed errant hud messages being displayed for wrong people + * + * 163 4/29/99 2:01a Chris + * Added the portal blockage support + * + * 162 4/28/99 9:49p Jeff + * handle player or player weapon types + * + * 161 4/27/99 9:20p Jason + * fixed key spewing + * + * 160 4/27/99 4:43a Jeff + * added multisafe function for setting/getting guidebot name, fixed msafe + * object flag set for clients + * + * 159 4/26/99 11:46p Matt + * Don't give the player a key if he's already got it. + * + * 158 4/26/99 8:16p Jason + * added sparks type + * + * 157 4/26/99 11:14a Jason + * more levelgoal work for Chris + * + * 156 4/26/99 4:27a Jeff + * added multisafe functions to check a player's weapons, and add/remove + * weapons from a player + * + * 155 4/25/99 10:13p Matt + * Added code to scan HUD messages for messages from the Guide-Bot, and + * print them using the GB message format. + * + * 154 4/24/99 6:45p Jeff + * added functions for theif so he can steal things other than weapons + * + * 153 4/23/99 11:00p Jeff + * added Afterburner HUD name + * + * 152 4/22/99 7:12p Jeff + * don't send object remove (it calls MultiSendRemoveObject) + * + * 151 4/22/99 4:44p Jason + * fixed level goal stuff for multiplayer + * + * 150 4/22/99 3:43p Kevin + * Training missions show controls on screen + * + * 149 4/22/99 3:24p Jason + * fixed some multisafe problems + * + * 148 4/20/99 7:29p Jeff + * difficulty based shields & energy + * + * 147 4/20/99 6:55p Matt + * Added code to keep a bitmask of all keys held by all players, and to + * allow a robot to open a door if any player has the key(s) for that + * door. + * + * 146 4/18/99 3:06p Jason + * fixed printing of hud message on the wrong players machine + * + * 145 4/17/99 3:45p Matt + * Add keys to the player's inventory. + * + * 144 4/14/99 11:54a Jason + * added message in multipay + * + * 143 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 142 4/12/99 6:15p Samir + * Sound priorities pass 1 + * + * 141 4/11/99 6:44p Matt + * Moved/renamed some text symbols that were in multisafe but belonged in + * stringtable + * + * 140 4/07/99 12:30p Matt + * Added code for failed missions. + * + * 139 4/06/99 11:44p Matt + * Added action to create a lighting bolt between two gunpoints on the + * same object, and queries to return an object's original shields and to + * multiply a percent times a float. + * + * 138 4/06/99 8:44p Matt + * Added a couple actions for Mark's energy/shield collector + * + * 137 4/06/99 6:24p Jason + * various fixes for multiplayer + * + * 136 4/04/99 6:20p Matt + * Added distance-based shake Dallas action. + * + * 135 4/03/99 5:23p Matt + * Removed placeholder code for Possession and Hint Reveal powerups. + * + * 134 4/03/99 1:13a Jeff + * added multisafe/dallas actions to set an object on fire + * + * 133 4/02/99 10:12p Jeff + * made ship permissions multi safe + * + * 132 4/02/99 9:33p Jeff + * added full map + * + * 131 3/31/99 11:41a Jason + * added support for attached thick lightning + * + * 130 3/30/99 7:26p Chris + * + * 128 3/29/99 7:30p Jason + * added cool new energy effect + * + * 127 3/28/99 5:56p Matt + * Added Dallas action to turn on object sparking + * + * 126 3/27/99 2:15p Jason + * added better thick lightining + * + * 125 3/26/99 3:25p Jeff + * option to display hud message when cloaking + * + * 124 3/26/99 12:38p Jeff + * added cloak predefs + * + * 123 3/25/99 3:30p Jason + * added option to randomize powerup respawn points + * + * 122 3/24/99 6:02p Jason + * fixed size for thick lightning + * + * 121 3/24/99 10:02a Matt + * Added Dallas action to set music region for all players + * + * 120 3/23/99 3:51p Jason + * added lightning bolt effect to msafe + * + * 119 3/22/99 1:59p Matt + * Added break glass Dallas action + * + * 118 3/22/99 11:26a Matt + * Changed energy powerup pickup behavior, and added energy full message. + * + * 117 3/22/99 10:59a Chris + * Awareness code was tweaked. Multisafe stuff added for objects. + * + * 116 3/16/99 6:43p Kevin + * OEM Split fixes + * + * 115 3/12/99 7:50p Jeff + * demo record a powerup being set dead if msafe says so + * + * 114 3/10/99 2:25p Kevin + * Save/Load and Demo file fixes + * + * 113 3/05/99 12:18p Matt + * Fixed, I hope, a bunch of ammo and weapon bugs + * + * 112 3/04/99 8:50p Samir + * AddColoredHUDMessage is formatted, so when mulitplayer messages went + * out, they would take the % signs and screw up the string. Now it + * doesn't. + * + * 111 3/04/99 7:39p Matt + * Added sound effects to FreeSpace-style persistent HUD messages. + * + * 110 3/04/99 7:01p Matt + * Fixed max ammo bug, and took out hack to set max ammo + * + * 109 3/04/99 6:30p Jason + * took out comments + * + * 108 3/04/99 6:29p Jason + * added more multiplayer functionality + * + * 107 3/04/99 6:04p Matt + * Made music region work for the specified object. + * Added sound effect & FreeSpace draw to game message. + * + * 106 3/04/99 3:46p Jason + * fixed some multisafe stuff + * + * 105 3/03/99 5:35p Matt + * Added fade-out for goal complete messages + * + * 104 3/03/99 5:08p Samir + * AddWeaponAmmoToPlayer added. + * + * 103 3/03/99 4:39p Matt + * Fixed some prematurely-check-in code. + * + * 102 3/03/99 3:02p Matt + * Added Game Message action in Dallas + * + * 101 3/02/99 1:31p Matt + * Added hack for secondary weapon ammo max not set + * + * 100 3/02/99 12:22p Matt + * Revisions to weapon & ammo powerups. Not fully tested. + * + * 99 3/01/99 1:09p Matt + * Changed validate functions to do an int3 & return nicely rather than + * error out + * + * 98 2/25/99 8:55p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 97 2/23/99 7:36p Jeff + * handle non-spewing inventory item add + * + * 96 2/22/99 1:20a Jeff + * added support for inventory (simple) in dallas. Moved end-level + * sequence to use IGC. Add position clipboard stuff for dallas. Fixed + * some inventory bug with storing object handles + * + * 95 2/21/99 8:36p Jeff + * misc changes to handle new matcen and path types of dallas + * + * 94 2/21/99 5:49p Matt + * Added Dallas action to set an object's volume + * + * 93 2/19/99 7:14p Luke + * ghost and unghost dont cause int3s + * + * 92 2/16/99 6:26p Matt + * Fixed bug in get player handle code & cleaned it up a bit besides + * + * 91 2/15/99 11:09a Matt + * Added function to count the number of a certain type of object in the + * level. (Also added HUD actions to print an int and float.) + * + * 90 2/11/99 1:44p Matt + * Fixed compile warning + * + * 89 2/11/99 1:10p Matt + * Added functions to get & set a triggers enabled/disabled state + * + * 88 2/11/99 2:56a Jeff + * added multisafe function to set an objects position in the world + * + * 87 2/10/99 7:14p Jeff + * 4 proxmines instead of 1 + * + * 86 2/10/99 6:55p Jeff + * added proxmines to the game + * + * 85 2/10/99 3:48p Jason + * table filter changes + * + * 84 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 83 2/09/99 12:43p Matt + * Added function to check for players in a room + * + * 82 2/09/99 11:57a Jeff + * quad fire now goes into your inventory so you spew it + * + * 81 2/08/99 7:05p Kevin + * Trying to get demo system working with powerup scripts + * + * 80 2/08/99 5:25p Jeff + * removed all calls to MultiSendRemoveObject, incorportated into + * SetObjectDeadFlag. Fixes sequencing issues in multiplayer + * + * 79 2/08/99 3:09a Jeff + * added a multisafe type to switch a player in and out of ai control mode + * (and to get whether a player is in ai mode or not) + * + * 78 2/06/99 10:03p Matt + * Added keys system + * + * 77 2/06/99 1:52a Matt + * Added a bunch of Dallas funcs, mostly for Sean + * + * 76 2/04/99 12:26p Jason + * added spew that was better for multiplayer + * + * 75 2/03/99 5:49p Matt + * Added room damage system + * + * 74 2/03/99 4:26p Jason + * made multiplayer coop actually work! + * + * 73 2/03/99 12:24a Matt + * Got HUD message, 2D sound, streaming sound, and player-is-visible all + * working correctly in multiplayer. + * + * 72 2/02/99 12:43p Kevin + * Fixes to the Save/Load game system, and the demo system to work with + * the new OSIRIS + * + * 71 2/01/99 4:17p Jason + * more changes for multisafe + * + * 70 2/01/99 3:47p Jason + * added more multisafe functionality + * + * 69 2/01/99 2:31p Jason + * made player handle grabbing more reliable + * + * 68 2/01/99 1:11p Matt + * Changed a bunch of stuff to work with object handles instead of player + * numbers. Also fixed the view cone action, which ignored distance. + * + * 67 1/31/99 11:05p Matt + * Added music region set + * + * 66 1/31/99 8:11p Matt + * Added player visible query, and made zoom work with popup cameras. + * + * 65 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 64 1/30/99 3:19p Matt + * Made Dallas spews work from object centers in addition to gunpoints + * + * 63 1/29/99 5:22p Jeff + * localization + * + * 62 1/29/99 3:14p Matt + * Deleted some obsolete Dallas actions, and made the player controls + * actions take player numbers instead of the player object. + * + * 61 1/29/99 12:48p Matt + * Rewrote the doorway system + * + * 60 1/26/99 5:16p Matt + * Added STOP_SOUND_OBJ + * + * 59 1/26/99 4:03p Jason + * fixed bad object invulnerable code that someone wrote + * + * 58 1/25/99 7:06p Matt + * Ripped out doorway index stuff, since doors are referred to by their + * door objects. + * + * 57 1/25/99 6:30p Matt + * Made sound functions take sound num instead of name, and door functions + * take object handle instead of doorway index. + * + * 56 1/23/99 10:10p Kevin + * Added the start of osiris support into the demo system + * + * 55 1/22/99 6:53p Chris + * Fixed LoadandBind Aux notify problems, fixed static path problems, + * fixed AIF_FORCE_AWARE Problems, improved path code + * + * 54 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 53 1/20/99 8:29p Matt + * Added a comment block listing what functions have been added or + * modified. + * + * 52 1/20/99 6:26p Matt + * Added several actions + * + * 51 1/20/99 4:33p Jason + * added changing wind/fog to msafe + * + * 50 1/20/99 2:13a Chris + * It is now possible for robots to have special immunities, resistances, + * and vunerabilities + * + * 49 1/19/99 10:27p Jeff + * in single player, primaries will give you energy if you already have + * the weapon + * + * 48 1/19/99 6:53p Matt + * Added some error checking + * + * 47 1/19/99 6:13p Matt + * Added an action & query for lighting distance + * + * 46 1/19/99 3:43p Kevin + * Added msafe functionality to set an object to render type RT_NONE + * + * 45 1/19/99 12:16p Matt + * Added start endlevel sequence action + * + * 44 1/18/99 7:31p Matt + * Added a bunch of Dallas actions + * + * 43 1/18/99 6:18p Kevin + * Added controller masking to DALLAS + * + * 42 1/18/99 2:46p Matt + * Combined flags & flags2 fields in object struct + * + * 41 1/15/99 10:44a Jason + * fixed fog bug + * + * 40 1/13/99 2:29p Jeff + * state is set of the mstruct for when getting a doorway index (if the + * door doesn't exist anymore...the object handle) + * + * 39 1/13/99 12:42p Matt + * Added code to close a popup view + * + * 38 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 37 1/11/99 10:43a Matt + * Made forcefield on/off/toggle optionally do both sides of a portal + * + * 36 1/08/99 4:33p Matt + * Got popup views working in Dallas + * + * 35 1/06/99 5:06p Chris + * Improving OSIRIS/game intergration - improved support for custom + * animations + * + * 34 1/06/99 4:23p Jeff + * added a couple of inventory and counter measure functions + * + * 33 1/06/99 1:46p Jeff + * cloak and invuln put in multisafe + * + * 32 1/05/99 3:41p Jeff + * added spew create and stop functions + * + * 31 1/05/99 12:42p Jason + * fixed missing break + * + * 30 1/05/99 11:52a Jason + * added more msafe functionality + * + * 29 1/04/99 12:24p Jeff + * added invulnerablilty and cloak. FInished up rest of powerups + * + * 28 12/31/98 7:36p Jeff + * correctly hash type/ids for multisafe. Added some new inline functions + * to add/get type/id to a packet + * + * 27 12/30/98 6:05p Jason + * added some inventory functions + * + * 26 12/30/98 2:17p Jason + * added MSAFE_DOOR_DOORWAY_INDEX + * + * 25 12/30/98 1:37p Jason + * added waypoint function + * + * 24 12/30/98 12:09p Jason + * added logfile +*/ +#include "multisafe.h" +#include "weather.h" +#include "room.h" +#include "game.h" +#include "multi.h" +#include "damage.h" +#include "hud.h" +#include "doorway.h" +#include "trigger.h" +#include "gamepath.h" +#include "AIGoal.h" +#include "weapon.h" +#include "spew.h" +#include "hlsoundlib.h" +#include "sounds.h" +#include "ship.h" +#include "player.h" +#include "object_lighting.h" +#include "soundload.h" +#include "streamaudio.h" +#include "gamesequence.h" +#include "gameevent.h" +#include "SmallViews.h" +#include "difficulty.h" +#include "door.h" +#include "demofile.h" +#include "stringtable.h" +#include "d3music.h" +#include "multi_world_state.h" +#include "osiris_predefs.h" +#include "viseffect.h" +#include "levelgoal.h" +#ifdef MACINTOSH +#include "osiris_common.h" //DAJ explicit include so it know its here +#endif +/* + The following functions have been added or modified by Matt and/or someone else other than Jason, + and thus Jason should check them out to make sure they're ok for multiplayer. + +*/ +// Returns true if the player has this weapon (this fuction should be moved) +bool PlayerHasWeapon (int slot,int weapon_index); +int AddWeaponAmmo (int slot,int weap_index,int ammo); +void CheckForWeaponSelect(int id,int weapon_index); +#define MOBJ (ObjGet(mstruct->objhandle)) +#define KOBJ (ObjGet(mstruct->killer_handle)) +#define IOBJ (ObjGet(mstruct->ithandle)) +//Returns true if the specified room is valid +bool VALIDATE_ROOM(int roomnum) { + + bool valid = ((roomnum >= 0) && (roomnum <= Highest_room_index) && Rooms[roomnum].used); + if (! valid) + Int3(); + return valid; +} +//Returns true if the specified room & face are valid +bool VALIDATE_ROOM_FACE(int roomnum,int facenum) { + + bool valid = ((roomnum >= 0) && (roomnum <= Highest_room_index) && (Rooms[roomnum].used) && + (facenum >= 0) && (facenum < Rooms[roomnum].num_faces)); + if (! valid) + Int3(); + return valid; +} +//Returns true if the specified room & portal are valid +bool VALIDATE_ROOM_PORTAL(int roomnum,int portalnum) { + + bool valid = ((roomnum >= 0) && (roomnum <= Highest_room_index) && (Rooms[roomnum].used) && + (portalnum >= 0) && (portalnum < Rooms[roomnum].num_portals)); + if (! valid) + Int3(); + return valid; +} +//Returns an object pointer from a handle. Really just ObjGet() with an mprintf() +object *VALIDATE_OBJECT (int handle) +{ + object *objp=ObjGet (handle); + if (!objp) + { + mprintf ((0,"Invalid object passed to multisafe.\n")); + } + return objp; +} +extern bool Hud_show_controls; +// Gets a value for the calling party +void msafe_GetValue (int type,msafe_struct *mstruct) +{ + object *objp; + switch (type) + { + case MSAFE_SHOW_ENABLED_CONTROLS: + objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) + mstruct->state = Hud_show_controls; + else + mstruct->state = false; + break; + break; + case MSAFE_OBJECT_PLAYER_CMASK: + objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) + mstruct->control_mask = Players[objp->id].controller_bitflags; + else + mstruct->control_mask = 0; + break; + case MSAFE_OBJECT_POS: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->pos = Zero_vector; + else + mstruct->pos = objp->pos; + break; + case MSAFE_OBJECT_ORIENT: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->orient = Identity_matrix; + else + mstruct->orient = objp->orient; + break; + case MSAFE_OBJECT_ROOMNUM: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->roomnum = -1; + else + mstruct->roomnum = objp->roomnum; + break; + case MSAFE_OBJECT_WORLD_POSITION: + objp = ObjGet(mstruct->objhandle); + if(!objp) + { + mstruct->pos = Zero_vector; + mstruct->orient = Identity_matrix; + mstruct->roomnum = -1; + } + else + { + mstruct->orient = objp->orient; + mstruct->roomnum = objp->roomnum; + mstruct->pos = objp->pos; + } + break; + case MSAFE_OBJECT_VELOCITY: + objp = ObjGet(mstruct->objhandle); + if(!objp || (objp->movement_type != MT_WALKING && objp->movement_type != MT_PHYSICS)) + mstruct->velocity = Zero_vector; + else + mstruct->velocity = objp->mtype.phys_info.velocity; + break; + case MSAFE_OBJECT_ROTVELOCITY: + objp = ObjGet(mstruct->objhandle); + if(!objp || (objp->movement_type != MT_WALKING && objp->movement_type != MT_PHYSICS)) + mstruct->rot_velocity = Zero_vector; + else + mstruct->rot_velocity = objp->mtype.phys_info.rotvel; + break; + case MSAFE_OBJECT_THRUST: + objp = ObjGet(mstruct->objhandle); + if(!objp || (objp->movement_type != MT_WALKING && objp->movement_type != MT_PHYSICS)) + mstruct->thrust = Zero_vector; + else + mstruct->thrust = objp->mtype.phys_info.thrust; + break; + case MSAFE_OBJECT_ROTTHRUST: + objp = ObjGet(mstruct->objhandle); + if(!objp || (objp->movement_type != MT_WALKING && objp->movement_type != MT_PHYSICS)) + mstruct->rot_thrust = Zero_vector; + else + mstruct->rot_thrust = objp->mtype.phys_info.rotthrust; + break; + case MSAFE_OBJECT_FLAGS: // Needs to be send + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->flags = 0; + else + mstruct->flags = objp->flags; + break; + case MSAFE_OBJECT_SIZE: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->size = 0.0f; + else + mstruct->size = objp->size; + break; + case MSAFE_OBJECT_CONTROL_TYPE: // Needs to be send + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->control_type = CT_NONE; + else + mstruct->control_type = objp->control_type; + break; + case MSAFE_OBJECT_MOVEMENT_TYPE: // Needs to be send + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->movement_type = MT_NONE; + else + mstruct->movement_type = objp->movement_type; + break; + case MSAFE_OBJECT_CREATION_TIME: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->creation_time = 0.0f; + else + mstruct->creation_time = objp->creation_time; + break; + case MSAFE_OBJECT_PHYSICS_FLAGS: // Needs to be send + objp = ObjGet(mstruct->objhandle); + if(!objp || (objp->movement_type != MT_WALKING && objp->movement_type != MT_PHYSICS)) + mstruct->physics_flags = 0; + else + mstruct->physics_flags = objp->mtype.phys_info.flags; + break; + case MSAFE_OBJECT_ROTDRAG: + objp = ObjGet(mstruct->objhandle); + if(!objp || (objp->movement_type != MT_WALKING && objp->movement_type != MT_PHYSICS)) + mstruct->rot_drag = 1.0f; + else + mstruct->rot_drag = objp->mtype.phys_info.rotdrag; + break; + case MSAFE_OBJECT_SHIELDS: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->shields = -1; + else + mstruct->shields = objp->shields; + break; + case MSAFE_OBJECT_SHIELDS_ORIGINAL: + objp = ObjGet(mstruct->objhandle); + if(!objp || !IS_GENERIC(objp->type)) + mstruct->shields = -1; + else + mstruct->shields = Object_info[objp->id].hit_points; + break; + case MSAFE_OBJECT_ENERGY: + ASSERT (MOBJ->type==OBJ_PLAYER); + if (!VALIDATE_OBJECT (mstruct->objhandle) || (MOBJ->type!=OBJ_PLAYER)) + mstruct->energy = 0; + else + mstruct->energy=Players[MOBJ->id].energy; + break; + case MSAFE_OBJECT_MOVEMENT_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle) || (MOBJ->type!=OBJ_PLAYER)) + mstruct->scalar = 0.0; + else + mstruct->scalar=Players[MOBJ->id].movement_scalar; + break; + case MSAFE_OBJECT_RECHARGE_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle) || (MOBJ->type!=OBJ_PLAYER)) + mstruct->scalar = 0.0; + else + mstruct->scalar=Players[MOBJ->id].weapon_recharge_scalar; + break; + case MSAFE_OBJECT_WSPEED_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle) || (MOBJ->type!=OBJ_PLAYER)) + mstruct->scalar = 0.0; + else + mstruct->scalar=Players[MOBJ->id].weapon_speed_scalar; + break; + case MSAFE_OBJECT_ARMOR_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle) || (MOBJ->type!=OBJ_PLAYER)) + mstruct->scalar = 0.0; + else + mstruct->scalar=Players[MOBJ->id].armor_scalar; + break; + case MSAFE_OBJECT_DAMAGE_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle) || (MOBJ->type!=OBJ_PLAYER)) + mstruct->scalar = 0.0; + else + mstruct->scalar=Players[MOBJ->id].damage_scalar; + break; + case MSAFE_OBJECT_TYPE: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->type = -1; + else + mstruct->type = objp->type; + break; + case MSAFE_OBJECT_ID: + objp = ObjGet(mstruct->objhandle); + if(!objp) + mstruct->id = -1; + else + mstruct->id = objp->id; + break; + case MSAFE_OBJECT_PARENT: + { + object *obj = ObjGet(mstruct->objhandle); + if (!obj) + return; + obj = ObjGetUltimateParent(obj); + mstruct->objhandle=obj->handle; + break; + } + case MSAFE_OBJECT_ENERGY_WEAPON: { + object *objp = ObjGet(mstruct->objhandle); + mstruct->state = (objp && (objp->type == OBJ_WEAPON) && !(Weapons[objp->id].flags & WF_MATTER_WEAPON)); + } + case MSAFE_OBJECT_DAMAGE_AMOUNT: { + mstruct->amount = 0; //default + object *objp = ObjGet(mstruct->objhandle); + if (!objp || (objp->type != OBJ_WEAPON)) + return; + mstruct->amount = Weapons[objp->id].generic_damage * objp->ctype.laser_info.multiplier; + break; + } + case MSAFE_OBJECT_COUNT_TYPE: { + int i,count=0; + for (i=0,objp=Objects;i<=Highest_object_index;i++,objp++) { + if ((objp->type == mstruct->type) && (objp->id == mstruct->id)) + count++; + } + mstruct->count = count; + break; + } +// case MSAFE_OBJECT_ANIM_FRAME: +// { +// objp = ObjGet(mstruct->objhandle); +// if(!objp || !(objp->flags & OF_POLYGON_OBJECT)) +// mstruct->anim_frame = 0.0f; +// else +// mstruct->anim_frame = objp->rtype.pobj_info.anim_frame; +// break; +// } + case MSAFE_OBJECT_INVULNERABLE: + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) { + mstruct->state = 0; + mstruct->lifetime = 0; + return; + } + + if (MOBJ->type==OBJ_PLAYER) + { + mstruct->state = (bool) ( (Players[MOBJ->id].flags&PLAYER_FLAGS_INVULNERABLE)!=0); + if(mstruct->state) + mstruct->lifetime = Players[MOBJ->id].invulnerable_time; + else + mstruct->lifetime = 0; + } + else + { + mstruct->state = (bool) ( (MOBJ->flags & OF_DESTROYABLE)==0); + mstruct->lifetime = 0; + } + + break; + } + case MSAFE_OBJECT_CLOAK: + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) { + mstruct->state = 0; + mstruct->lifetime = 0; + return; + } + if(!MOBJ->effect_info){ + mstruct->state = 0; + mstruct->lifetime = 0; + return; + } + mstruct->state = (bool) ( (MOBJ->effect_info->type_flags&EF_CLOAKED)!=0); + if(mstruct->state) + mstruct->lifetime = MOBJ->effect_info->cloak_time; + else + mstruct->lifetime = 0; + break; + } + case MSAFE_OBJECT_LIGHT_DIST: { + mstruct->light_distance = 0.0; //default + object *objp = ObjGet(mstruct->objhandle); + if (objp) { + light_info *li = ObjGetLightInfo(objp); + if (li) + mstruct->light_distance = li->light_distance; + } + break; + } + case MSAFE_OBJECT_PLAYER_HANDLE: + mstruct->objhandle = OBJECT_HANDLE_NONE; //default to none + if ((mstruct->slot >= 0) && (mstruct->slot < MAX_PLAYERS) && (Players[mstruct->slot].objnum != -1)) + { + if (Game_mode & GM_MULTI) + { + //Don't return the handle for observers + if (Objects[Players[mstruct->slot].objnum].type == OBJ_OBSERVER) + break; + + if (NetPlayers[mstruct->slot].flags & NPF_CONNECTED && NetPlayers[mstruct->slot].sequence==NETSEQ_PLAYING) + mstruct->objhandle = Objects[Players[mstruct->slot].objnum].handle; + } + else + { + if (mstruct->slot==0) + mstruct->objhandle = Objects[Players[mstruct->slot].objnum].handle; + } + } + break; + case MSAFE_OBJECT_PLAYER_CONTROLAI: + { + bool ok = false; + mstruct->state = false; + if ((mstruct->slot >= 0) && (mstruct->slot < MAX_PLAYERS) && (Players[mstruct->slot].objnum != -1)) + { + if (Game_mode & GM_MULTI) + { + if (NetPlayers[mstruct->slot].flags & NPF_CONNECTED && NetPlayers[mstruct->slot].sequence==NETSEQ_PLAYING) + ok = true; + } + else + { + if (mstruct->slot==0) + ok = true; + } + } + if( ok ) + { + if( Objects[Players[mstruct->slot].objnum].control_type==CT_AI ) + mstruct->state = true; + else + mstruct->state = false; + } + } + break; + case MSAFE_DOOR_LOCK_STATE: + mstruct->state = DoorwayLocked(mstruct->objhandle); + break; + case MSAFE_DOOR_OPENABLE: + mstruct->state = DoorwayOpenable(mstruct->objhandle,mstruct->ithandle); + break; + case MSAFE_DOOR_POSITION: + mstruct->scalar = DoorwayGetPosition(mstruct->objhandle); + break; + case MSAFE_TRIGGER_SET: + // Returns 1 if the trigger is enabled + mstruct->state = TriggerGetState(mstruct->trigger_num); + break; + case MSAFE_ROOM_HAS_PLAYER: + { + int objnum; + + if (! VALIDATE_ROOM (mstruct->roomnum)) { + mstruct->state = 0; + break; + } + for (objnum=Rooms[mstruct->roomnum].objects;objnum != -1;objnum = Objects[objnum].next) { + if (Objects[objnum].type == OBJ_PLAYER) + break; + } + mstruct->state = (objnum != -1); + break; + } + case MSAFE_ROOM_PORTAL_RENDER: + { + if (!VALIDATE_ROOM_PORTAL(mstruct->roomnum,mstruct->portalnum)) { + mstruct->state=0; + break; + } + + portal *pp = &Rooms[mstruct->roomnum].portals[mstruct->portalnum]; + + if (pp->flags & PF_RENDER_FACES) + mstruct->state=1; + else + mstruct->state=0; + break; + } + case MSAFE_ROOM_PORTAL_BLOCK: + { + if (!VALIDATE_ROOM_PORTAL(mstruct->roomnum,mstruct->portalnum)) { + mstruct->state=0; + break; + } + + portal *pp = &Rooms[mstruct->roomnum].portals[mstruct->portalnum]; + if (pp->flags & PF_BLOCK) + mstruct->state=1; + else + mstruct->state=0; + break; + } + case MSAFE_ROOM_DAMAGE: + if (!VALIDATE_ROOM (mstruct->roomnum)) { + mstruct->amount = 0.0; + mstruct->index = 0; + break; + } + mstruct->amount = Rooms[mstruct->roomnum].damage; + mstruct->index = Rooms[mstruct->roomnum].damage_type; + break; + case MSAFE_MISC_ENABLE_SHIP: + { + mstruct->state = PlayerIsShipAllowed(-1,mstruct->name); + }break; + case MSAFE_MISC_WAYPOINT: + mstruct->index=Current_waypoint; + break; + case MSAFE_MISC_GUIDEBOT_NAME: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp && objp->type==OBJ_PLAYER && objp->id==Player_num) + { + Current_pilot.get_guidebot_name(mstruct->name); + }else + { + strcpy(mstruct->name,"GB"); + } + }break; + case MSAFE_INVEN_CHECK: + case MSAFE_COUNTERMEASURE_CHECK: + { + object *objp = ObjGet(mstruct->objhandle); + int ret; + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + if(type==MSAFE_COUNTERMEASURE_CHECK) + mstruct->type = OBJ_WEAPON; + if(mstruct->type==OBJ_WEAPON) + ret = Players[slot].counter_measures.CheckItem(mstruct->type,mstruct->id); + else + ret = Players[slot].inventory.CheckItem(mstruct->type,mstruct->id); + if (ret) + mstruct->state=1; + else + mstruct->state=0; + } + break; + } + case MSAFE_INVEN_COUNT: { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + mstruct->count=Players[slot].inventory.GetTypeIDCount(mstruct->type,mstruct->id); + } + break; + } + case MSAFE_INVEN_SIZE: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + mstruct->size = Players[slot].inventory.Size(); + } + }break; + case MSAFE_INVEN_GET_TYPE_ID: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + int size = Players[slot].inventory.Size(); + if(mstruct->index<0 || mstruct->index>size){ + mstruct->count = 0; + mstruct->type = OBJ_NONE; + mstruct->id = 0; + return; + } + int savepos = Players[slot].inventory.GetPos(); + Players[slot].inventory.GotoPos(mstruct->index); + mstruct->count = Players[slot].inventory.GetPosCount(); + Players[slot].inventory.GetPosTypeID(mstruct->type,mstruct->id); + + Players[slot].inventory.GotoPos(savepos); + } + }break; + case MSAFE_INVEN_CHECK_OBJECT: + { + object *player = ObjGet(mstruct->objhandle); + object *object = ObjGet(mstruct->ithandle); + mstruct->state = false; + if( player && object && (player->type==OBJ_PLAYER)) + { + int slot = player->id; + mstruct->state = Players[slot].inventory.CheckItem(mstruct->ithandle,-1); + } + }break; + case MSAFE_COUNTERMEASURE_COUNT: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + int id = FindWeaponName(IGNORE_TABLE(mstruct->name)); + if(id==-1){ + mstruct->count = 0; + }else{ + mstruct->count=Players[slot].counter_measures.GetTypeIDCount(OBJ_WEAPON,id); + } + } + }break; + case MSAFE_COUNTERMEASURE_SIZE: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + mstruct->size = Players[slot].counter_measures.Size(); + } + }break; + case MSAFE_COUNTERMEASURE_GET: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + int size = Players[slot].counter_measures.Size(); + if(mstruct->index<0 || mstruct->index>size){ + mstruct->count = 0; + mstruct->type = OBJ_NONE; + mstruct->id = 0; + return; + } + int savepos = Players[slot].counter_measures.GetPos(); + Players[slot].counter_measures.GotoPos(mstruct->index); + mstruct->count = Players[slot].counter_measures.GetPosCount(); + Players[slot].counter_measures.GetPosTypeID(mstruct->type,mstruct->id); + Players[slot].counter_measures.GotoPos(savepos); + } + }break; + case MSAFE_ROOM_FOG_STATE: + if (!VALIDATE_ROOM (mstruct->roomnum)) + mstruct->state = 0; + else + mstruct->state = (Rooms[mstruct->roomnum].flags & RF_FOG) != 0; + break; + case MSAFE_WEAPON_CHECK: + { + //we want to check a player for a particular weapon + //objhandle: the player + //index: the weapon + //state: whether the player has the weapon + //count: ammo (if it's an ammo/secondary) of the weapon, else -1 + object *objp = ObjGet(mstruct->objhandle); + mstruct->state = 0; + ASSERT(mstruct->index>=0 && mstruct->indextype == OBJ_PLAYER)) + { + int pnum = objp->id; + if (Players[pnum].weapon_flags & HAS_FLAG(mstruct->index)) + { + mstruct->state = 1; + if (mstruct->index >= SECONDARY_INDEX) + { + mstruct->count = Players[pnum].weapon_ammo[mstruct->index]; + } + else + { + ship *ship = &Ships[Players[pnum].ship_index]; + otype_wb_info *wb = &ship->static_wb[mstruct->index]; + ASSERT(wb != NULL); + if(wb->ammo_usage) + { + //this guy is an ammo weapon! + mstruct->count = Players[pnum].weapon_ammo[mstruct->index]; + }else + { + //no ammo + mstruct->count = -1; + } + } + } + }else + { + Int3(); //uhhhh + } + }break; + case MSAFE_WEAPON_ADD: + Int3(); //huh? + break; + default: + Int3(); // Invalid type passed to function + break; + } +} +//Gets the player slot from the specified object, if the object is a player or player weapon +//Returns -1 if can't find player +int GetPlayerSlot(int objhandle) +{ + object *objp = ObjGet(objhandle); + if (!objp) + return -1; + if (objp->type == OBJ_WEAPON) + objp = ObjGetUltimateParent(objp); + if (objp->type == OBJ_PLAYER) + return objp->id; + else + return -1; +} +#define GB_TOKEN "GUIDEBOT:" +//If the src message is from the guidebot, xlates it into the proper format. If not a GB +//message, just copies the string over. +//Returns true if is a GB message +bool XlateGBMessage(char *dest,char *src) +{ + if (strnicmp(src,GB_TOKEN,sizeof(GB_TOKEN)-1) != 0) { + strcpy(dest,src); + return 0; + } + char *t = src + strlen(GB_TOKEN); //skip the first part of the string + while (*t == ' ') //skip spaces + t++; + if (*t == '"') //skip the leading quote + t++; + char gbname[100]; + Current_pilot.get_guidebot_name(gbname); + sprintf(dest, "\1\255\255\1%s:\1\1\255\1 %s", gbname, t); + while (dest[strlen(dest)-1] == ' ') //strip spaces at end + dest[strlen(dest)-1] = 0; + if (dest[strlen(dest)-1] == '"') //Remove the trailing quote + dest[strlen(dest)-1] = 0; + return 1; +} +char Custom_HUD_text[MSAFE_MESSAGE_LENGTH] = ""; +extern bool Demo_call_ok; +// The main entry point for all the multisafe functions +// Pass the type of function you want, and then fill in the relevant fields +// of the mstruct +void msafe_CallFunction (ubyte type,msafe_struct *mstruct) +{ + if((Demo_flags==DF_PLAYBACK)&&(!Demo_call_ok)) + return; + ubyte send_it=1; + switch (type) + { + case MSAFE_WEATHER_RAIN: + SetRainState (mstruct->state,mstruct->scalar); + break; + case MSAFE_WEATHER_SNOW: + SetSnowState (mstruct->state,mstruct->scalar); + break; + case MSAFE_WEATHER_LIGHTNING: + SetLightningState (mstruct->state,mstruct->scalar,mstruct->randval); + break; + case MSAFE_WEATHER_LIGHTNING_BOLT: + { + if (mstruct->texnum==-1) + { + mprintf ((0,"Failing bolt because texnum is -1\n")); + return; + } + + if (mstruct->flags) + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + } + else + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + if (!VALIDATE_OBJECT (mstruct->ithandle)) + return; + } + int visnum=VisEffectCreate (VIS_FIREBALL,THICK_LIGHTNING_INDEX,mstruct->roomnum,&mstruct->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=mstruct->lifetime; + vis->lifetime=mstruct->lifetime; + vis->end_pos=mstruct->pos2; + + vis->custom_handle=mstruct->texnum; + + vis->lighting_color=mstruct->color; + vis->billboard_info.width=mstruct->size; + vis->billboard_info.texture=mstruct->state; + vis->velocity.x=mstruct->count; + vis->velocity.y=mstruct->interval; + vis->velocity.z=mstruct->index; + + vis->flags=VF_USES_LIFELEFT|VF_WINDSHIELD_EFFECT|VF_LINK_TO_VIEWER; + vis->size=vm_VectorDistanceQuick(&vis->pos,&vis->end_pos); + if (mstruct->flags) // Do attached lightning effect + { + vis->flags|=VF_ATTACHED; + vis->attach_info.obj_handle=MOBJ->handle; + vis->attach_info.modelnum=MOBJ->rtype.pobj_info.model_num; + vis->attach_info.vertnum=mstruct->g1; + vis->attach_info.end_vertnum=mstruct->g2; + WeaponCalcGun (&vis->pos,NULL,MOBJ,vis->attach_info.vertnum); + WeaponCalcGun (&vis->end_pos,NULL,MOBJ,vis->attach_info.end_vertnum); + } + else + { + vis->flags|=VF_ATTACHED|VF_PLANAR; + vis->attach_info.obj_handle=MOBJ->handle; + vis->attach_info.dest_objhandle=IOBJ->handle; + vis->end_pos=IOBJ->pos; + } + } + break; + } + case MSAFE_ROOM_TEXTURE: + { + if (!VALIDATE_ROOM_FACE(mstruct->roomnum,mstruct->facenum)) + return; + ASSERT (Rooms[mstruct->roomnum].num_faces>mstruct->facenum); + ChangeRoomFaceTexture(mstruct->roomnum,mstruct->facenum,mstruct->index); + break; + } + case MSAFE_ROOM_WIND: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + Rooms[mstruct->roomnum].wind=mstruct->wind; + Rooms[mstruct->roomnum].room_change_flags|=RCF_WIND; + break; + case MSAFE_ROOM_FOG: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + Rooms[mstruct->roomnum].fog_r=mstruct->fog_r; + Rooms[mstruct->roomnum].fog_g=mstruct->fog_g; + Rooms[mstruct->roomnum].fog_b=mstruct->fog_b; + Rooms[mstruct->roomnum].fog_depth=mstruct->fog_depth; + Rooms[mstruct->roomnum].room_change_flags|=RCF_FOG; + Rooms[mstruct->roomnum].flags|=RF_FOG; + break; + case MSAFE_ROOM_FOG_STATE: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + if (mstruct->state) + Rooms[mstruct->roomnum].flags |= RF_FOG; + else + Rooms[mstruct->roomnum].flags &= ~RF_FOG; + Rooms[mstruct->roomnum].room_change_flags |= RCF_FOG; + break; + case MSAFE_ROOM_LIGHT_PULSE: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + Rooms[mstruct->roomnum].pulse_time=mstruct->pulse_time; + Rooms[mstruct->roomnum].pulse_offset=mstruct->pulse_offset; + Rooms[mstruct->roomnum].room_change_flags|=RCF_LIGHTING; + break; + case MSAFE_ROOM_LIGHT_FLICKER: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + if (mstruct->state) + Rooms[mstruct->roomnum].flags |= RF_FLICKER; + else + Rooms[mstruct->roomnum].flags &= ~RF_FLICKER; + Rooms[mstruct->roomnum].room_change_flags|=RCF_LIGHTING; + break; + case MSAFE_ROOM_LIGHT_STROBE: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + if (mstruct->state) + Rooms[mstruct->roomnum].flags |= RF_STROBE; + else + Rooms[mstruct->roomnum].flags &= ~RF_STROBE; + Rooms[mstruct->roomnum].room_change_flags|=RCF_LIGHTING; + break; + case MSAFE_ROOM_REFUEL: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + if (mstruct->state) + Rooms[mstruct->roomnum].flags |= RF_FUELCEN; + else + Rooms[mstruct->roomnum].flags &= ~RF_FUELCEN; + Rooms[mstruct->roomnum].room_change_flags|=RCF_REFUEL; + break; + case MSAFE_ROOM_CHANGING_FOG: { + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + vector fog_vec; + fog_vec.x = mstruct->fog_r; + fog_vec.y = mstruct->fog_g; + fog_vec.z = mstruct->fog_b; + SetRoomChangeOverTime(mstruct->roomnum,1,&fog_vec,mstruct->fog_depth,mstruct->interval); + break; + } + case MSAFE_ROOM_CHANGING_WIND: { + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + SetRoomChangeOverTime(mstruct->roomnum,0,&mstruct->wind,0,mstruct->interval); + break; + } + case MSAFE_ROOM_PORTAL_RENDER: + { + if (!VALIDATE_ROOM_PORTAL(mstruct->roomnum,mstruct->portalnum)) + return; + + portal *pp = &Rooms[mstruct->roomnum].portals[mstruct->portalnum]; + + if (mstruct->state) + pp->flags |= PF_RENDER_FACES; + else + pp->flags &= ~PF_RENDER_FACES; + Rooms[mstruct->roomnum].room_change_flags|=RCF_PORTAL_RENDER; + pp->flags|=PF_CHANGED; + + //check other side of portal + if (mstruct->flags) { + portal *pp2 = &Rooms[pp->croom].portals[pp->cportal]; + if (mstruct->state) + pp2->flags |= PF_RENDER_FACES; + else + pp2->flags &= ~PF_RENDER_FACES; + Rooms[pp->croom].room_change_flags|=RCF_PORTAL_RENDER; + pp2->flags|=PF_CHANGED; + } + break; + } + case MSAFE_ROOM_PORTAL_BLOCK: + { + if (!VALIDATE_ROOM_PORTAL(mstruct->roomnum,mstruct->portalnum)) + return; + + portal *pp = &Rooms[mstruct->roomnum].portals[mstruct->portalnum]; + if(!(pp->flags & (PF_BLOCK | PF_BLOCK_REMOVABLE))) + return; + if (mstruct->state) + pp->flags |= PF_BLOCK; + else + pp->flags &= ~PF_BLOCK; + Rooms[mstruct->roomnum].room_change_flags|=RCF_PORTAL_BLOCK; + pp->flags|=PF_CHANGED; + + //check other side of portal + portal *pp2 = &Rooms[pp->croom].portals[pp->cportal]; + if (mstruct->state) + pp2->flags |= PF_BLOCK; + else + pp2->flags &= ~PF_BLOCK; + Rooms[pp->croom].room_change_flags|=RCF_PORTAL_BLOCK; + pp2->flags|=PF_CHANGED; + break; + } + case MSAFE_ROOM_BREAK_GLASS: + { + void ComputeCenterPointOnFace(vector *vp,room *rp,int facenum); + if (!VALIDATE_ROOM_PORTAL(mstruct->roomnum,mstruct->portalnum)) + return; + send_it=0; // Handled in function below + + BreakGlassFace(&Rooms[mstruct->roomnum],Rooms[mstruct->roomnum].portals[mstruct->portalnum].portal_face); + break; + } + case MSAFE_ROOM_DAMAGE: + if (!VALIDATE_ROOM (mstruct->roomnum)) + return; + Rooms[mstruct->roomnum].damage = mstruct->amount; + Rooms[mstruct->roomnum].damage_type = mstruct->index; + Rooms[mstruct->roomnum].room_change_flags|=RCF_DAMAGE; + break; + case MSAFE_ROOM_REMOVE_ALL_POWERUPS: + { + if (VALIDATE_ROOM (mstruct->roomnum)) + { + int i,high_index; + int invis_id; + invis_id = FindObjectIDName("InvisiblePowerup"); + high_index = Highest_object_index+1; + for(i=0;iroomnum) + continue; + if(Objects[i].id==invis_id) + continue; + SetObjectDeadFlag(&Objects[i],true,false); + } + } + }break; + case MSAFE_OBJECT_FIRE_WEAPON: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (mstruct->index >= 0) && (mstruct->index < MAX_WEAPONS) && Weapons[mstruct->index].used) + FireWeaponFromObject(objp,mstruct->index,mstruct->gunpoint); + send_it=0; + break; + } + case MSAFE_OBJECT_SETONFIRE: + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + + object *obj = MOBJ; + float damage_time = mstruct->longevity; + float d_per_sec = mstruct->interval; + if (obj->effect_info) + { + if(damage_time>0) + { + obj->effect_info->type_flags|=EF_NAPALMED; + obj->effect_info->damage_time+=damage_time; + //NOTE: commented out because lava rocks need to go burning for awhile... + //so we trust that someone doesn't do something stupid + //obj->effect_info->damage_time=min(10.0f,obj->effect_info->damage_time); + obj->effect_info->damage_per_second=d_per_sec; + if (Gametime-obj->effect_info->last_damage_time>1.0f) + obj->effect_info->last_damage_time=0; + + obj->effect_info->damage_handle=mstruct->ithandle; + if (obj->effect_info->sound_handle == SOUND_NONE_INDEX) + obj->effect_info->sound_handle = Sound_system.Play3dSound(SOUND_PLAYER_BURNING, SND_PRIORITY_HIGHEST, obj); + } + else + { + obj->effect_info->type_flags &= ~EF_NAPALMED; + obj->effect_info->damage_time = 0; + } + } + } + break; + case MSAFE_OBJECT_SHIELDS: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + + MOBJ->shields=mstruct->shields; + MOBJ->flags &= ~(OF_AI_DEATH); + + break; + case MSAFE_OBJECT_ENERGY: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + ASSERT (MOBJ->type==OBJ_PLAYER); + Players[MOBJ->id].energy=mstruct->energy; + break; + case MSAFE_SHOW_ENABLED_CONTROLS: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) + { + Hud_show_controls = mstruct->state?true:false; + } + } + break; + case MSAFE_OBJECT_PLAYER_CMASK: { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + if(mstruct->control_val) + Players[objp->id].controller_bitflags |= mstruct->control_mask; + else + Players[objp->id].controller_bitflags &= ~mstruct->control_mask; + } + break; + } + case MSAFE_OBJECT_PLAYER_KEY: { + int slot = GetPlayerSlot(mstruct->ithandle); + if (slot == -1) + break; //couldn't find player object + if (Players[slot].keys & KEY_FLAG(mstruct->index)) + return; //if player already has key, do nothing + //We should really have a function to set the key flags + Players[slot].keys |= KEY_FLAG(mstruct->index); + Global_keys |= KEY_FLAG(mstruct->index); + if (mstruct->objhandle != OBJECT_HANDLE_NONE) { + object *keyobj = ObjGet(mstruct->objhandle); + if (keyobj) + { + mprintf ((0,"Adding key from multisafe to player %d\n",slot)); + Sound_system.Play3dSound(SOUND_POWERUP_PICKUP,SND_PRIORITY_HIGH,keyobj); + Players[slot].inventory.Add(keyobj->type,keyobj->id,NULL,-1,-1,INVAF_NOTSPEWABLE,mstruct->message); + + if (!(Game_mode & GM_MULTI)) //If not multiplayer, delete the key + { + SetObjectDeadFlag(keyobj); + } + else if((keyobj->flags & OF_INFORM_DESTROY_TO_LG)) + { + Level_goals.Inform(LIT_OBJECT, LGF_COMP_DESTROY, keyobj->handle); + } + } + } + send_it=0; // Handled by server + break; + } + case MSAFE_OBJECT_MOVEMENT_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + ASSERT (MOBJ->type==OBJ_PLAYER); + Players[MOBJ->id].movement_scalar=mstruct->scalar; + break; + case MSAFE_OBJECT_RECHARGE_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + ASSERT (MOBJ->type==OBJ_PLAYER); + Players[MOBJ->id].weapon_recharge_scalar=mstruct->scalar; + break; + case MSAFE_OBJECT_WSPEED_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + ASSERT (MOBJ->type==OBJ_PLAYER); + Players[MOBJ->id].weapon_speed_scalar=mstruct->scalar; + break; + case MSAFE_OBJECT_ARMOR_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + ASSERT (MOBJ->type==OBJ_PLAYER); + Players[MOBJ->id].armor_scalar=mstruct->scalar; + break; + case MSAFE_OBJECT_DAMAGE_SCALAR: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + ASSERT (MOBJ->type==OBJ_PLAYER); + Players[MOBJ->id].damage_scalar=mstruct->scalar; + break; + case MSAFE_OBJECT_ADD_WEAPON: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + ASSERT (MOBJ->type==OBJ_PLAYER); + AddWeaponToPlayer (MOBJ->id,mstruct->index,mstruct->ammo); + if (MOBJ->id==Player_num) + send_it=0; // Don't send because the server is picking it up + break; + case MSAFE_OBJECT_DAMAGE_OBJECT: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + send_it=0; // Don't need to send because ApplyDamage does the work + if (MOBJ->type==OBJ_PLAYER) + { + if (mstruct->killer_handle==OBJECT_HANDLE_NONE) + ApplyDamageToPlayer (MOBJ,NULL,mstruct->damage_type,mstruct->amount); + else + ApplyDamageToPlayer (MOBJ,KOBJ,mstruct->damage_type,mstruct->amount); + } + else + { + if (mstruct->killer_handle==OBJECT_HANDLE_NONE) + ApplyDamageToGeneric (MOBJ,NULL,mstruct->damage_type,mstruct->amount); + else + ApplyDamageToGeneric (MOBJ,KOBJ,mstruct->damage_type,mstruct->amount); + } + break; + case MSAFE_OBJECT_START_SPEW: + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + spewinfo spew; + if (mstruct->gunpoint == -2) + { //no gun + object *objp = ObjGet(mstruct->objhandle); + if (!objp) + break; + spew.use_gunpoint = false; + spew.pt.origin = objp->pos; + spew.pt.normal = objp->orient.uvec; + spew.pt.room_num = objp->roomnum; + } + else if (mstruct->gunpoint == -1) + { //no gun + object *objp = ObjGet(mstruct->objhandle); + if (!objp) + break; + spew.use_gunpoint = false; + spew.pt.origin = objp->pos; + spew.pt.normal = objp->orient.fvec; + spew.pt.room_num = objp->roomnum; + } + else + { + spew.use_gunpoint = true; + spew.gp.obj_handle = mstruct->objhandle; + spew.gp.gunpoint = mstruct->gunpoint; + } + spew.random = mstruct->random; + spew.real_obj = (bool)(mstruct->is_real!=0); + spew.effect_type = mstruct->effect_type; + spew.phys_info = mstruct->phys_info; + spew.drag = mstruct->drag; + spew.mass = mstruct->mass; + spew.time_int = mstruct->interval; + spew.longevity = mstruct->longevity; + spew.lifetime = mstruct->lifetime; + spew.size = mstruct->size; + spew.speed = mstruct->speed; + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_CLIENT) + { + int spewnum = SpewCreate(&spew); + ASSERT(spewnum != -1); //DAJ -1FIX + spewnum&=0xFF; + + Server_spew_list[mstruct->id]=spewnum; + } + else { + mstruct->id = SpewCreate(&spew); + ASSERT(mstruct->id != -1); //DAJ -1FIX + } + break; + } + case MSAFE_OBJECT_STOP_SPEW: + { + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_CLIENT) + { + SpewClearEvent(SpewEffects[mstruct->id].handle,true); + Server_spew_list[mstruct->id]=65535; + } + else + { + SpewClearEvent(mstruct->id,true); + } + break; + } + case MSAFE_OBJECT_NO_RENDER: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + MOBJ->render_type = RT_NONE; + MOBJ->change_flags|=OCF_NO_RENDER; + break; + case MSAFE_OBJECT_GHOST: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + if( ObjGet(mstruct->objhandle)->type==OBJ_NONE ) return; + if( ObjGet(mstruct->objhandle)->type==OBJ_DUMMY ) return; //Don't ghost a ghosted object!! + if( ObjGet(mstruct->objhandle)->type==OBJ_PLAYER ) return; + if( ObjGet(mstruct->objhandle)->type==OBJ_GHOST ) return; + ObjGhostObject(mstruct->objhandle & HANDLE_OBJNUM_MASK); + break; + case MSAFE_OBJECT_UNGHOST: + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + if( ObjGet(mstruct->objhandle)->type!=OBJ_DUMMY ) return; //Don't unghost an unghosted object!! + ObjUnGhostObject(mstruct->objhandle & HANDLE_OBJNUM_MASK); + break; + case MSAFE_OBJECT_REMOVE: + send_it=0; + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + + SetObjectDeadFlag (MOBJ,true,(bool)(mstruct->playsound!=0)); + if(mstruct->playsound) + Sound_system.Play3dSound(SOUND_POWERUP_PICKUP, SND_PRIORITY_HIGH, MOBJ); + break; + case MSAFE_OBJECT_INVULNERABLE: + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + + if (MOBJ->type==OBJ_PLAYER) + { + if(mstruct->state) + MakePlayerInvulnerable(MOBJ->id,mstruct->lifetime); + else + MakePlayerVulnerable(MOBJ->id); + } + else + { + if (mstruct->state) + MOBJ->flags &=~OF_DESTROYABLE; + else + MOBJ->flags |=OF_DESTROYABLE; + } + send_it = 1; + break; + } + case MSAFE_OBJECT_CLOAKALLPLAYERS: + { + if(mstruct->state) + { + if(Game_mode&GM_MULTI) + { + for(int i=0;ilifetime,1.0f,true); + } + } + }else + { + MakeObjectInvisible(Player_object,mstruct->lifetime,1.0f,true); + } + }else + { + if(Game_mode&GM_MULTI) + { + for(int i=0;iobjhandle)) + return; + if(mstruct->state){ + MakeObjectInvisible(MOBJ,mstruct->lifetime); + }else{ + MakeObjectVisible(MOBJ); + } + send_it = 1; + break; + } + case MSAFE_OBJECT_LIGHT_DIST: { + object *objp = ObjGet(mstruct->objhandle); + if (objp) { + ObjSetLocalLighting(objp); + if (mstruct->light_distance < 0) + mstruct->light_distance = 0; + objp->lighting_info->light_distance = mstruct->light_distance; + objp->change_flags|=OCF_LIGHT_DISTANCE; + } + break; + } + case MSAFE_OBJECT_LIGHT_COLOR: { + object *objp = ObjGet(mstruct->objhandle); + if (objp) { + ObjSetLocalLighting(objp); + objp->lighting_info->red_light1 = mstruct->r1; + objp->lighting_info->green_light1 = mstruct->g1; + objp->lighting_info->blue_light1 = mstruct->b1; + objp->change_flags|=OCF_LIGHT_COLOR; + } + break; + } + case MSAFE_OBJECT_DEFORM: { + object *objp = ObjGet(mstruct->objhandle); + if (objp && objp->effect_info) { + objp->effect_info->type_flags |= EF_DEFORM; + objp->effect_info->deform_range = mstruct->amount; + objp->effect_info->deform_time = mstruct->lifetime; + } + break; + } + case MSAFE_OBJECT_SPARKS: { + object *objp = ObjGet(mstruct->objhandle); + if (objp && objp->effect_info) { + objp->effect_info->type_flags |= EF_SPARKING; + objp->effect_info->spark_delay = 1.0 / mstruct->amount; + objp->effect_info->spark_timer = 0; + objp->effect_info->spark_time_left = mstruct->lifetime; + } + break; + } + case MSAFE_OBJECT_VIEWER_SHAKE: { + AddToShakeMagnitude(mstruct->amount); + break; + } + case MSAFE_OBJECT_SHAKE_AREA: { + object *objp = ObjGet(mstruct->objhandle); + if (objp) { + float dist = vm_VectorDistanceQuick(&Viewer_object->pos,&objp->pos); + if (dist < mstruct->scalar) + AddToShakeMagnitude(mstruct->amount * (1.0 - (dist / mstruct->scalar))); + } + break; + } + case MSAFE_OBJECT_WORLD_POSITION: + { + object *objp = ObjGet(mstruct->objhandle); + send_it = 0; + if(objp) + { + if (mstruct->roomnum!=-1) + { + ObjSetPos(objp,&mstruct->pos,mstruct->roomnum,&mstruct->orient,true); + // When DON'T we want to send this to the clients?? + send_it = 1; + } + } + }break; + case MSAFE_OBJECT_PLAYER_CONTROLAI: + { + bool ok = false; + send_it = 0; + if ((mstruct->slot >= 0) && (mstruct->slot < MAX_PLAYERS) && (Players[mstruct->slot].objnum != -1)) + { + if (Game_mode & GM_MULTI) + { + if (NetPlayers[mstruct->slot].flags & NPF_CONNECTED && NetPlayers[mstruct->slot].sequence==NETSEQ_PLAYING) + ok = true; + } + else + { + if (mstruct->slot==0) + ok = true; + } + } + if( ok ) + { + //if mstruct->state is true, then set to AI + //else set back to control mode + osipf_SetPlayerControlMode(mstruct->slot,(bool)(mstruct->state!=0)); + } + } + break; + case MSAFE_OBJECT_DESTROY_ROBOTS_EXCEPT: + { + //destroy all object (generics) except those in the list specified + tKillObjectItem *koilist = (tKillObjectItem *)mstruct->list; + int koisize = mstruct->count; + bool kill_it; + send_it = 0; + //go through all generic objects, kill those that don't match an exception + for(int i=0;i<=Highest_object_index;i++) + { + if(IS_ROBOT((&Objects[i])) && !(IS_GUIDEBOT(&Objects[i]))) + { + kill_it = true; + //see if it's on the exception list + for(int x=0;xstate) { //player specified + slot = GetPlayerSlot(mstruct->objhandle); + if (slot == -1) + break; //couldn't find player object + mstruct->objhandle = Objects[Players[slot].objnum].handle; + } + else + slot = -1; //all players + if ((slot == -1) || (slot==Player_num)) + mstruct->sound_handle=StreamPlay(mstruct->name,mstruct->volume,mstruct->flags); + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER && slot==Player_num) + send_it=0; + mstruct->slot = slot; //for send_to + break; + } + case MSAFE_SOUND_2D: + { + int slot; + if (mstruct->state) { //player specified + slot = GetPlayerSlot(mstruct->objhandle); + if (slot == -1) + break; //couldn't find player object + mstruct->objhandle = Objects[Players[slot].objnum].handle; + } + else + slot = -1; //all players + if ((slot == -1) || (slot==Player_num)) + mstruct->sound_handle = Sound_system.Play2dSound(mstruct->index,SND_PRIORITY_HIGHEST, mstruct->volume); + else + mstruct->sound_handle = -1; + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER && slot==Player_num) + send_it=0; + mstruct->slot = slot; //for send_to + break; + } + case MSAFE_SOUND_OBJECT: + { + if (!VALIDATE_OBJECT (mstruct->objhandle)) + return; + mstruct->sound_handle=Sound_system.Play3dSound(mstruct->index,SND_PRIORITY_HIGHEST, MOBJ); + break; + } + case MSAFE_SOUND_STOP: + Int3(); + send_it=0; + break; + case MSAFE_SOUND_STOP_OBJ: + Sound_system.StopObjectSound(mstruct->objhandle); + break; + case MSAFE_SOUND_VOLUME_OBJ: + Sound_system.SetVolumeObject(mstruct->objhandle,mstruct->volume); + break; + case MSAFE_MUSIC_REGION: { + int slot; + if (mstruct->state) { //player specified + slot = GetPlayerSlot(mstruct->objhandle); + if (slot == -1) + break; //couldn't find player object + mstruct->objhandle = Objects[Players[slot].objnum].handle; + } + else + slot = -1; //all players + if ((slot == -1) || (slot == Player_num)) + D3MusicSetRegion(mstruct->index); + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER && slot==Player_num) + send_it=0; + mstruct->slot = slot; //for send_to + break; + } + case MSAFE_MISC_LEVELGOAL: + if (Game_mode & GM_MULTI && Netgame.local_role==LR_CLIENT) + { + int v = 0xFFFFFFFF; + Level_goals.GoalSetName(mstruct->index,mstruct->message); + Level_goals.GoalStatus(mstruct->index,LO_CLEAR_SPECIFIED,&v); + Level_goals.GoalStatus(mstruct->index,LO_SET_SPECIFIED,&mstruct->type); + Level_goals.GoalPriority (mstruct->index,LO_SET_SPECIFIED,&mstruct->count); + } + break; + case MSAFE_MISC_WAYPOINT: + PlayerAddWaypoint (mstruct->index); + break; + case MSAFE_MISC_ENABLE_SHIP: + { + PlayerSetShipPermission(-1,mstruct->name,(bool)(mstruct->state!=0)); + }break; + case MSAFE_MISC_FILTERED_HUD_MESSAGE: + case MSAFE_MISC_HUD_MESSAGE: { + int slot; + if (mstruct->state) { //player specified + slot = GetPlayerSlot(mstruct->objhandle); + if (slot == -1) + break; //couldn't find player object + mstruct->objhandle = Objects[Players[slot].objnum].handle; + } + else + slot = -1; //all players + if ((slot == -1) || (slot==Player_num)) { + char message[256]; + bool gb = XlateGBMessage(message,mstruct->message); + bool added; + if(type==MSAFE_MISC_HUD_MESSAGE) + added = AddColoredHUDMessage(mstruct->color,"%s",message); // "%s" because message could contain % signs! + else + added = AddFilteredColoredHUDMessage(mstruct->color,"%s",message); // "%s" because message could contain % signs! + if (gb && added) //If was GB message & was added, play the GB sound + Sound_system.Play2dSound(FindSoundName("GBotGreetB1"),SND_PRIORITY_HIGHEST, 1.0); + } + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER && slot==Player_num) + send_it=0; + mstruct->slot = slot; //for send_to + break; + } + case MSAFE_MISC_GAME_MESSAGE: { + int slot; + if (mstruct->state) { //player specified + slot = GetPlayerSlot(mstruct->objhandle); + if (slot == -1) + break; //couldn't find player object + mstruct->objhandle = Objects[Players[slot].objnum].handle; + } + else + slot = -1; //all players + if ((slot == -1) || (slot==Player_num)) { + AddGameMessage(mstruct->message); + char message2[256]; + XlateGBMessage(message2,mstruct->message2); + int y = Game_window_h / 4 - 15; + if(Demo_flags==DF_RECORDING) + { + DemoWritePersistantHUDMessage(mstruct->color,HUD_MSG_PERSISTENT_CENTER,y,10.0,HPF_FADEOUT+HPF_FREESPACE_DRAW,SOUND_GAME_MESSAGE,message2); + } + if(Demo_flags!=DF_PLAYBACK) + { + AddPersistentHUDMessage(mstruct->color,HUD_MSG_PERSISTENT_CENTER,y,10.0,HPF_FADEOUT+HPF_FREESPACE_DRAW,SOUND_GAME_MESSAGE,message2); + } + } + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER && slot==Player_num) + send_it=0; + mstruct->slot = slot; //for send_to + break; + } + case MSAFE_MISC_END_LEVEL: + if(Game_mode&GM_MULTI) + MultiEndLevel(); + else + SetGameState(mstruct->state ? GAMESTATE_LVLEND : GAMESTATE_LVLFAILED); + send_it=0; + break; + case MSAFE_MISC_POPUP_CAMERA: { + CreateSmallView(SVW_LEFT,mstruct->objhandle,SVF_POPUP+SVF_BIGGER,mstruct->interval,D3_DEFAULT_ZOOM/mstruct->scalar,mstruct->gunpoint); + break; + } + case MSAFE_MISC_CLOSE_POPUP: + ClosePopupView(SVW_LEFT); + break; + case MSAFE_MISC_GUIDEBOT_NAME: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp && objp->type==OBJ_PLAYER && objp->id==Player_num) + { + Current_pilot.set_guidebot_name(mstruct->name); + } + }break; + case MSAFE_MISC_START_TIMER: { + extern void RenderHUDTimer(tHUDItem *item); + int Osiris_GetTimerHandle(int id); + int handle = Osiris_GetTimerHandle(mstruct->index); + if (handle == 0) + return; + tHUDItem huditem; + memset(&huditem,0, sizeof(huditem)); + huditem.alpha = HUD_ALPHA; + huditem.color = mstruct->color; + huditem.type = HUD_ITEM_TIMER; + huditem.x = 0; + huditem.y = 0; + huditem.data.timer_handle = handle; + huditem.stat = STAT_CUSTOM; + huditem.flags = HUD_FLAG_LEVEL; + huditem.render_fn = RenderHUDTimer; + AddHUDItem(&huditem); + send_it=0; + break; + } + case MSAFE_MISC_UPDATE_HUD_ITEM: + int FindCustomtext2HUDItem(); + //If item not added yet, add it now + if (FindCustomtext2HUDItem() == -1) { + tHUDItem huditem; + memset(&huditem,0, sizeof(huditem)); + huditem.alpha = HUD_ALPHA; + huditem.color = mstruct->color; + huditem.type = HUD_ITEM_CUSTOMTEXT2; + huditem.x = 0; + huditem.y = 0; + huditem.buffer_size = MSAFE_MESSAGE_LENGTH; + huditem.stat = STAT_CUSTOM; + huditem.flags = HUD_FLAG_LEVEL; + huditem.render_fn = NULL; + AddHUDItem(&huditem); + ASSERT(FindCustomtext2HUDItem() != -1); + } + UpdateCustomtext2HUDItem(mstruct->message); + break; + case MSAFE_DOOR_LOCK_STATE: + DoorwayLockUnlock(mstruct->objhandle,(mstruct->state != 0)); + break; + case MSAFE_DOOR_ACTIVATE: + ASSERT (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_DOOR); + DoorwayActivate(mstruct->objhandle); + break; + case MSAFE_DOOR_STOP: + DoorwayStop(mstruct->objhandle); + break; + case MSAFE_DOOR_POSITION: + DoorwaySetPosition(mstruct->objhandle,mstruct->scalar); + break; + case MSAFE_TRIGGER_SET: + TriggerSetState(mstruct->trigger_num,(mstruct->state != 0)); + send_it=0; + break; + case MSAFE_INVEN_ADD_OBJECT: + { + object *player = ObjGet(mstruct->objhandle); + object *item = ObjGet(mstruct->ithandle); + bool ret = false; + + send_it = 0; + if( player && item && (player->type==OBJ_PLAYER)) + { + int slot = player->id; + ret = Players[slot].inventory.AddObject(item->handle,mstruct->flags,(mstruct->message[0]!='\0')?mstruct->message:NULL); + if(ret) + send_it = 1; + } + }break; + case MSAFE_INVEN_REMOVE_OBJECT: + { + object *player = ObjGet(mstruct->objhandle); + object *item = ObjGet(mstruct->ithandle); + mstruct->state = false; + send_it = 0; + if( player && item && (player->type==OBJ_PLAYER)) + { + int slot = player->id; + mstruct->state = Players[slot].inventory.Remove(item->handle,-1); + if(mstruct->state) + send_it = 1; + } + }break; + case MSAFE_INVEN_ADD_TYPE_ID: { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + + if(mstruct->type==OBJ_WEAPON) + Players[slot].counter_measures.Add(mstruct->type,mstruct->id,NULL,-1,-1,mstruct->flags); + else + Players[slot].inventory.Add(mstruct->type,mstruct->id,NULL,-1,-1,mstruct->flags); + } + break; + } + case MSAFE_INVEN_REMOVE: + case MSAFE_COUNTERMEASURE_REMOVE: { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + if(type==MSAFE_COUNTERMEASURE_REMOVE) + mstruct->type = OBJ_WEAPON; + + if(mstruct->type==OBJ_WEAPON) + Players[slot].counter_measures.Remove(mstruct->type,mstruct->id); + else + Players[slot].inventory.Remove(mstruct->type,mstruct->id); + } + break; + } + case MSAFE_COUNTERMEASURE_ADD: + { + object *objp = ObjGet(mstruct->objhandle); + if (objp && (objp->type == OBJ_PLAYER)) { + int slot = objp->id; + int total = 0; + if(mstruct->count>0){ + int id = FindWeaponName(IGNORE_TABLE(mstruct->name)); + if(id!=-1){ + for(int i=0;icount;i++){ + if(Players[slot].counter_measures.Add(OBJ_WEAPON,id,objp,mstruct->aux_type,mstruct->aux_id)){ + total++; + } + } + } + } + mstruct->count = total; + if(total) + send_it = 1; + else + send_it = 0; + } + }break; + case MSAFE_OBJECT_ROTDRAG: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + objp->mtype.phys_info.rotdrag = mstruct->rot_drag; + send_it=false; + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_TYPE: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + objp->type = mstruct->type; + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_ID: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + objp->id = mstruct->id; + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_CONTROL_TYPE: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp && !(objp->flags & OF_DYING)) + { + objp->control_type = mstruct->control_type; + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_FLAGS: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + if(Game_mode&GM_MULTI && Netgame.local_role==LR_CLIENT) + { + ASSERT(objp->flags&OF_SERVER_OBJECT); + } + if(objp->flags != (unsigned int)mstruct->flags) + { + objp->flags = mstruct->flags; + } + else + { + send_it = false; + } + if(Game_mode&GM_MULTI && Netgame.local_role==LR_CLIENT) + { + objp->flags |= OF_SERVER_OBJECT; + } + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_MOVEMENT_TYPE: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + objp->movement_type = mstruct->movement_type; + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_CREATION_TIME: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + objp->creation_time = mstruct->creation_time; + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_PHYSICS_FLAGS: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + if(objp->mtype.phys_info.flags != (unsigned int)mstruct->physics_flags) + { + objp->mtype.phys_info.flags = mstruct->physics_flags; + objp->change_flags|=OCF_PHYS_FLAGS; + } + else + { + send_it = false; + } + } + else + { + return; + } + } + break; + case MSAFE_OBJECT_PARENT: + { + object *objp = ObjGet(mstruct->objhandle); + if(objp) + { + objp->parent_handle = mstruct->ithandle; + } + else + { + return; + } + } + break; + + case MSAFE_WEAPON_CHECK: + Int3(); //hmmmm + break; + case MSAFE_WEAPON_ADD: + { + //add/subtract ammo/weapons from a player + //if count>0 and the player doesn't have the weapon it will be added + //objhandle: the player + //index: the weapon + //state: if the weapon is a secondary: + // 1: remove weapon if ammo<=0 + // 0: do nothing to the weapon + // if the weapon is a primary + // 1: remove weapon if count==0 + // 0: do nothing to the weapon + //count: ammo (if it's an ammo/secondary) of the weapon + object *player = ObjGet(mstruct->objhandle); + ASSERT(mstruct->index>=0 && mstruct->indextype==OBJ_PLAYER) + { + ship *ship = &Ships[Players[player->id].ship_index]; + otype_wb_info *wb = &ship->static_wb[mstruct->index]; + ASSERT(wb != NULL); + // check if the player has the weapon + bool have_weapon = PlayerHasWeapon (player->id,mstruct->index); + if( !have_weapon && mstruct->count<=0 ) + { + //nothing to do + return; + } + if(mstruct->count>0) + { + bool is_ammo = false; + if((mstruct->index >= SECONDARY_INDEX) || wb->ammo_usage) + { + is_ammo = true; + } + //we're going to give the player the weapon and ammo (if needed) + if(!have_weapon) + { + AddWeaponToPlayer(player->id,mstruct->index,(is_ammo)?mstruct->count:0); + //thats it! + return; + } + //ok, the player already has this weapon, add some ammo if ammo weapon + int old_ammo = Players[player->id].weapon_ammo[mstruct->index]; + int added = 1; + //now add ammo if necessary + if(is_ammo) + { + added = AddWeaponAmmo (player->id,mstruct->index,mstruct->count); + } + if (added>0 && player->id == Player_num && old_ammo==0) + { + CheckForWeaponSelect(player->id,mstruct->index); + } + }else + { + bool weap_removed = false; + int type; + //we're going to remove the weapon/ammo + if(mstruct->index >= SECONDARY_INDEX) + { + type = PW_SECONDARY; + //remove ammo + Players[player->id].weapon_ammo[mstruct->index] += mstruct->count; + if(Players[player->id].weapon_ammo[mstruct->index]<0) + Players[player->id].weapon_ammo[mstruct->index] = 0; + if(Players[player->id].weapon_ammo[mstruct->index]==0) + { + weap_removed = true; + if(mstruct->state==1) + { + //remove weapon + Players[player->id].weapon_flags &= ~HAS_FLAG(mstruct->index); + } + } + }else + { + type = PW_PRIMARY; + //remove the weapon + if(mstruct->state==1) + { + Players[player->id].weapon_flags &= ~HAS_FLAG(mstruct->index); + weap_removed = true; + } + } + if(weap_removed) + { + int curr_weapon_index = Players[player->id].weapon[type].index; + if(curr_weapon_index==mstruct->index) + { + //check autoselect + if (player->id == Player_num) { + bool select_new; + + select_new = AutoSelectWeapon(type,-1); + if (!select_new) { + //AddHUDMessage ("%s",TXT(Static_weapon_names_msg[Players[player->id].weapon[type].index])); + } + } + } + } + } + }else + { + Int3(); //come on it isn't even a player! + } + }break; + default: + Int3(); // Illegal type passed to multisafe function + break; + } + if (((Demo_flags == DF_RECORDING) && send_it)||(send_it && (Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER)) + MultiSendMSafeFunction (type,mstruct); +} +#define SHIELD_BONUS 12.0f +#define ENERGY_BONUS 12.0f +// Returns true if the player has this weapon (this fuction should be moved) +bool PlayerHasWeapon (int slot,int weapon_index) +{ + if (Players[slot].weapon_flags & HAS_FLAG(weapon_index)) + if (weapon_index >= SECONDARY_INDEX) + return (Players[slot].weapon_ammo[weapon_index] > 0); //have secondary only if have ammo + else + return true; //primaries don't check for ammo + else + return false; +} +// Adds weapon ammo to a player, returns 0 if it couldn't +// This function should be moved! +int AddWeaponAmmo (int slot,int weap_index,int ammo) +{ + //Get pointer to the battery for this weapon + player *player = &Players[slot]; + ship *ship = &Ships[player->ship_index]; + otype_wb_info *wb = &ship->static_wb[weap_index]; + ASSERT(wb != NULL); + //if secondary or primary that uses ammo, then use the ammo + if((weap_index >= SECONDARY_INDEX) || wb->ammo_usage) + { + //figure out much ammo to add + int added = min(ship->max_ammo[weap_index] - player->weapon_ammo[weap_index],ammo); + //now add it + player->weapon_ammo[weap_index] += added; + return added; + } + else //not an ammo-using weapon + { + Int3(); //trying to add ammo to something that doesn't take it + return 0; + } +} +bool AddPowerupEnergyToPlayer(int id) +{ + if(Game_mode&GM_MULTI) + return false; + float curr_energy = Players[Player_num].energy; + if(Players[id].energy>=MAX_ENERGY) + return false; + float amount = 10.0f * Diff_general_scalar[DIFF_LEVEL]; + curr_energy = min(MAX_ENERGY,curr_energy+amount); + Players[id].energy = curr_energy; + return true; +} +// returns true if it was handled +bool HandleCommonPowerups(char *pname,msafe_struct *mstruct,ubyte *pickup); +bool HandleWeaponPowerups(char *pname,msafe_struct *mstruct,ubyte *pickup); +bool HandleCounterMeasurePowerups(char *pname,msafe_struct *mstruct,ubyte *pickup); +bool HandleInventoryPowerups(char *pname,msafe_struct *mstruct,ubyte *pickup); +// Does whatever magic needs to be done to get the default powerups to work +void msafe_DoPowerup (msafe_struct *mstruct) +{ + char pname[255]; + strcpy (pname,Object_info[MOBJ->id].name); + ASSERT (MOBJ->type==OBJ_POWERUP); + ASSERT (IOBJ->type==OBJ_PLAYER); + ubyte pickup=0; + // Now go through and do the magic for each powerup + bool handled = false; + handled = HandleCommonPowerups(pname,mstruct,&pickup); + if(!handled) + handled = HandleWeaponPowerups(pname,mstruct,&pickup); + if(!handled) + handled = HandleCounterMeasurePowerups(pname,mstruct,&pickup); + if(!handled) + handled = HandleInventoryPowerups(pname,mstruct,&pickup); + if(!handled) + return; + //if (Game_mode & GM_MULTI && Netgame.local_role==LR_SERVER) + //{ + MultiSendMSafePowerup (mstruct); + //} + + // Now remove the object from the world + if (pickup) + { + if (((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER) || !(Game_mode & GM_MULTI)) + { + if(Demo_flags==DF_RECORDING) + { + //DemoWriteSetObjDead(MOBJ); + } + SetObjectDeadFlag (MOBJ,true,(bool)(mstruct->playsound!=0)); + Sound_system.Play3dSound(SOUND_POWERUP_PICKUP, SND_PRIORITY_HIGH, MOBJ); + } + } +} +//Data for primary powerups +struct { + char *name; + int weapon_index; + int pickup_msg,already_have_msg,ammo_msg,full_msg; +} powerup_data_primary[] = { + {"Vauss",VAUSS_INDEX,TXI_MSG_VAUSS,TXI_MSG_HAVEVAUSS,TXI_MSG_VAUSSAMMO,TXI_MSG_VAUSSFULL}, + {"Napalm",NAPALM_INDEX,TXI_MSG_NAPALM,TXI_MSG_NAPALMHAVE,TXI_MSG_NAPALMFUEL,TXI_MSG_NAPALMFULL}, + {"EMDlauncher",EMD_INDEX,TXI_MSG_EMD,TXI_MSG_EMDALREADYHAVE,-1,-1}, + {"Microwave",MICROWAVE_INDEX,TXI_MSG_MICROWAVE,TXI_MSG_MICROWAVEHAVE,-1,-1}, + {"MassDriver",MASSDRIVER_INDEX,TXI_MSG_MASSDRIVER,TXI_MSG_MASSALREADYHAVE,TXI_MSG_MASSAMMO,TXI_MSG_MASSFULL}, + {"SuperLaser",SUPER_LASER_INDEX,TXI_MSG_SUPERLASER,TXI_MSG_SUPERLHAVE,-1,-1}, + {"Plasmacannon",PLASMA_INDEX,TXI_MSG_PLASMA,TXI_MSG_PLASMAHAVE,-1,-1}, + {"Fusioncannon",FUSION_INDEX,TXI_MSG_FUSION,TXI_MSG_FUSIONHAVE,-1,-1}, + {"Omegacannon",OMEGA_INDEX,TXI_MSG_OMEGA,TXI_MSG_OMEGAHAVE,-1,-1}, +}; +#define NUM_POWERUP_TYPES_PRIMARY (sizeof(powerup_data_primary) / sizeof(*powerup_data_primary)) +struct { + char *name; + int weapon_index; + int added_one_msg,added_multi_msg,full_msg; +} powerup_data_secondary[] = { + {"Frag",FRAG_INDEX,TXI_MSG_FRAG,-1,TXI_MSG_FRAGFULL}, + {"ImpactMortar",IMPACTMORTAR_INDEX,TXI_MSG_IMPACTM,-1,TXI_MSG_IMPACTMFULL}, + {"NapalmRocket",NAPALMROCKET_INDEX,TXI_MSG_NAPALMR,-1,TXI_MSG_NAPALMRFULL}, + {"Cyclone",CYCLONE_INDEX,TXI_MSG_CYCLONE,-1,TXI_MSG_CYCLONEFULL}, + {"BlackShark",BLACKSHARK_INDEX,TXI_MSG_BSHARK,-1,TXI_MSG_BSHARKFULL}, + {"Concussion",CONCUSSION_INDEX,TXI_MSG_CONC,-1,TXI_MSG_CONCFULL}, + {"Homing",HOMING_INDEX,TXI_MSG_HOMING,-1,TXI_MSG_HOMINGFULL}, + {"Smart",SMART_INDEX,TXI_MSG_SMART,-1,TXI_MSG_SMARTFULL}, + {"Mega",MEGA_INDEX,TXI_MSG_MEGA,-1,TXI_MSG_MEGAFULL}, + {"Guided",GUIDED_INDEX,TXI_MSG_GUIDED,-1,TXI_MSG_GUIDEDFULL}, + {"4PackHoming",HOMING_INDEX,TXI_MSG_HOMING,TXI_MSG_MULTI_HOMING,TXI_MSG_HOMINGFULL}, + {"4PackConc",CONCUSSION_INDEX,TXI_MSG_CONC,TXI_MSG_MULTI_CONC,TXI_MSG_CONCFULL}, + {"4PackFrag",FRAG_INDEX,TXI_MSG_FRAG,TXI_MSG_MULTI_FRAG,TXI_MSG_FRAGFULL}, + {"4PackGuided",GUIDED_INDEX,TXI_MSG_GUIDED,TXI_MSG_MULTI_GUIDED,TXI_MSG_GUIDEDFULL}, +}; +#define NUM_POWERUP_TYPES_SECONDARY (sizeof(powerup_data_secondary) / sizeof(*powerup_data_secondary)) +struct { + char *name; + int weapon_index; + int ammo_msg,full_msg; +} powerup_data_ammo[] = { + {"Vauss clip",VAUSS_INDEX,TXI_MSG_VAUSSAMMO,TXI_MSG_VAUSSFULL}, + {"MassDriverAmmo",MASSDRIVER_INDEX,TXI_MSG_MASSAMMO,TXI_MSG_MASSFULL}, + {"NapalmTank",NAPALM_INDEX,TXI_MSG_NAPALMFUEL,TXI_MSG_NAPALMFULL} +}; +#define NUM_POWERUP_TYPES_AMMO (sizeof(powerup_data_ammo) / sizeof(*powerup_data_ammo)) +void ShowAmmoAddedMessage(int weapon_index,int msg_index,int added) +{ + int added_frac = 0; + if (Ships[Players[Player_num].ship_index].fire_flags[weapon_index] & SFF_TENTHS) { + added_frac = added; + while(added_frac>=100) + added_frac -= 100; + while(added_frac>=10) + added_frac -= 10; + added /= 10; + } + AddFilteredHUDMessage(TXT(msg_index),added,added_frac); +} +//See if we should select the specified weapon +void CheckForWeaponSelect(int id,int weapon_index) +{ + if (id == Player_num) { + bool select_new; + if (weapon_index < SECONDARY_INDEX) + select_new = AutoSelectWeapon(PW_PRIMARY, weapon_index); + else + select_new = AutoSelectWeapon(PW_SECONDARY, weapon_index); + if (!select_new) { + AddFilteredHUDMessage ("%s",TXT(Static_weapon_names_msg[weapon_index])); + } + } +} +extern ubyte AutomapVisMap[MAX_ROOMS]; +//New spiffy table-based code +bool HandleWeaponPowerups(char *pname,msafe_struct *mstruct,ubyte *pickup) +{ + object *player = ObjGet(mstruct->ithandle); + object *powerup = ObjGet(mstruct->objhandle); + int p; + if (!player || !powerup) + return 0; + bool handled = false; + ASSERT(player->type == OBJ_PLAYER); + ASSERT(powerup->control_type == CT_POWERUP); + //Check the primaries + for (p=0;p < NUM_POWERUP_TYPES_PRIMARY;p++) { + if (!stricmp(powerup_data_primary[p].name,pname)) { + handled = true; + int weapon_index = powerup_data_primary[p].weapon_index; + if (!PlayerHasWeapon(player->id,weapon_index)) { + *pickup=1; + if (player->id == Player_num) + AddFilteredHUDMessage(TXT(powerup_data_primary[p].pickup_msg)); + AddWeaponToPlayer(player->id,weapon_index,powerup->ctype.powerup_info.count); + } + else { //we already have this weapon + //If multiplayer, don't do anything (except print the message), so only do stuff if not multiplayer + if (! (Game_mode & GM_MULTI)) { + //If this is an ammo weapon, give the player the ammo + if (powerup->ctype.powerup_info.count > 0) { + int old_ammo = Players[player->id].weapon_ammo[weapon_index]; + int added = AddWeaponAmmo(player->id,weapon_index,powerup->ctype.powerup_info.count); + if (added) { //got ammo, so remove powerup + *pickup = 1; + if (player->id == Player_num) { + ShowAmmoAddedMessage(weapon_index,powerup_data_primary[p].ammo_msg,added); + if (old_ammo == 0) + CheckForWeaponSelect(player->id,weapon_index); + } + } + else { //did not get ammo from powerup, so say full + if (player->id == Player_num) + AddFilteredHUDMessage(TXT(powerup_data_primary[p].full_msg)); + } + } + else { //not an ammo weapon (or has no ammo) + //Give the player energy & remove powerup, if energy not full, + if (AddPowerupEnergyToPlayer(player->id)) + *pickup = 1; + } + } + else + { + if (player->id==Player_num) + AddFilteredHUDMessage (TXT_ALREADY_HAVE_WEAPON); + } + } + } + } + //Check the secondaries + for (p=0;p < NUM_POWERUP_TYPES_SECONDARY;p++) { + if (!stricmp(powerup_data_secondary[p].name,pname)) { + handled = true; + int weapon_index = powerup_data_secondary[p].weapon_index; + //Does the player have any now? + bool has_weapon = PlayerHasWeapon(player->id,weapon_index); + //Determine how much to add + int added = AddWeaponAmmo(player->id,weapon_index,powerup->ctype.powerup_info.count); + //Pickup powerup if multiplayer or if got any ammo from it + *pickup = (Game_mode & GM_MULTI) || (added != 0); + + //Show message + if (player->id == Player_num) { + if (added > 1) { + ASSERT(powerup_data_secondary[p].added_multi_msg != -1); + AddFilteredHUDMessage(TXT(powerup_data_secondary[p].added_multi_msg),added); + } + else if (added == 1) + AddFilteredHUDMessage(TXT(powerup_data_secondary[p].added_one_msg)); + else + AddFilteredHUDMessage(TXT(powerup_data_secondary[p].full_msg)); + } + //Give the player the weapon if didn't already have it + + ship *ship = &Ships[Players[player->id].ship_index]; + otype_wb_info *wb = &ship->static_wb[weapon_index]; + + if (!has_weapon || (player->id==Player_num && wb->ammo_usage > 1 && wb->ammo_usage == Players[player->id].weapon_ammo[weapon_index])) + AddWeaponToPlayer(player->id,weapon_index,0); + } + } + //Check the ammo powerups + for (p=0;p < NUM_POWERUP_TYPES_AMMO;p++) { + if (!stricmp(pname,powerup_data_ammo[p].name)) { + handled = true; + int weapon_index = powerup_data_ammo[p].weapon_index; + ASSERT(weapon_index < SECONDARY_INDEX); + //Get the ammo from this weapon + int old_ammo = Players[player->id].weapon_ammo[weapon_index]; + int added = AddWeaponAmmo(player->id,weapon_index,powerup->ctype.powerup_info.count); + //Pickup powerup if multiplayer or if got any ammo from it + *pickup = (Game_mode & GM_MULTI) || (added != 0); + + if (player->id == Player_num) { + if (added > 0) + ShowAmmoAddedMessage(weapon_index,powerup_data_ammo[p].ammo_msg,added); + else + AddFilteredHUDMessage(TXT(powerup_data_ammo[p].full_msg)); + } + //Check to select the weapon + if (added && PlayerHasWeapon(player->id,weapon_index) && (old_ammo == 0)) + CheckForWeaponSelect(player->id,weapon_index); + } + } + return handled; +} +// returns true if it was handled +bool HandleCommonPowerups(char *pname,msafe_struct *mstruct,ubyte *pickup) +{ + int pnum = IOBJ->id; + bool handled = false; + // Shield bonus + if (!stricmp("Shield",pname)) + { + handled = true; + // If this guy is already at max shields, then don't do anything + if ((IOBJ->shields)>=MAX_SHIELDS) + { + if (pnum==Player_num) + AddFilteredHUDMessage (TXT_MAXSHIELDS); + } + else + { + // Pick it up + *pickup=1; + float addval=SHIELD_BONUS * Diff_shield_energy_scalar[DIFF_LEVEL]; + if (IOBJ->shields+addval>MAX_SHIELDS) + addval=MAX_SHIELDS-IOBJ->shields; + + IOBJ->shields+=addval; + if (pnum==Player_num) + AddFilteredHUDMessage (TXT_SHIELDBOOST,(int)IOBJ->shields); + } + } + // ENERGY POWERUP + else if (!stricmp("Energy",pname)) + { + handled = true; + float addval = ENERGY_BONUS * Diff_shield_energy_scalar[DIFF_LEVEL]; + addval = min(addval,MAX_ENERGY-Players[pnum].energy); + Players[pnum].energy += addval; + //Pick up if multiplayer or added + if ((Game_mode & GM_MULTI) || (addval > 0)) + *pickup = 1; + if (pnum==Player_num) + AddFilteredHUDMessage((addval > 0) ? TXT_MSG_ENERGY : TXT_MAXENERGY); + } + //Quad Laser + else if (!stricmp("QuadLaser",pname)) + { + handled = true; + object *pobj = &Objects[Players[pnum].objnum]; + if(!(pobj->dynamic_wb[LASER_INDEX].flags&DWBF_QUAD)){ + //player doesn't have Quad Lasers + + if(pnum==Player_num) + AddFilteredHUDMessage (TXT_QUADLASER); + *pickup = 1; + pobj->dynamic_wb[LASER_INDEX].flags |= DWBF_QUAD; + pobj->dynamic_wb[SUPER_LASER_INDEX].flags |= DWBF_QUAD; + //add them to the inventory so we can spew it + static int quad_id = -2; + if(quad_id==-2) + quad_id = FindObjectIDName(IGNORE_TABLE(pname)); + if(quad_id>-1) + { + Players[pnum].inventory.Add(OBJ_POWERUP,quad_id,NULL,-1,-1,INVAF_LEVELLAST|INVAF_TIMEOUTONSPEW); + } + }else if(pnum==Player_num) + AddFilteredHUDMessage (TXT_MSG_QUADHAVE); + } + //FullMap Powerup + else if (!stricmp("FullMap",pname)) + { + handled = true; + if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_FULLMAP); + + for (int i=0;ihandle,THIEFITEM_AUTOMAP); + *pickup = 1; + } + //Thief stolen headlight + else if (!stricmp("HeadlightPowerup",pname)) + { + handled = true; + if(!ThiefPlayerHasItem(IOBJ->handle,THIEFITEM_HEADLIGHT)) + { + if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_RETURNEDHEADLIGHT); + } + ThiefReturnItem(IOBJ->handle,THIEFITEM_HEADLIGHT); + *pickup = 1; + }else + { + if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_HAVEHEADLIGHT); + } + } + } + //Invulnerability + else if (!stricmp(pname,"Invulnerability")) + { + handled = true; + if(!(Players[pnum].flags&PLAYER_FLAGS_INVULNERABLE)){ + float time_period = 30.0f; //30 seconds + MakePlayerInvulnerable(pnum,time_period,true); + *pickup = 1; + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_INVULNALREADY); + } + } + //Cloak + else if (!stricmp(pname,"Cloak")) + { + handled = true; + if(IOBJ->effect_info){ + if(! ((IOBJ->effect_info->type_flags&EF_FADING_OUT)||(IOBJ->effect_info->type_flags&EF_CLOAKED))){ + float time_period = 30.0f; //30 seconds + MakeObjectInvisible(IOBJ,time_period); + *pickup = 1; + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_CLOAKALREADY); + } + } + } + return handled; +} +bool HandleCounterMeasurePowerups(char *pname,msafe_struct *mstruct,ubyte *pickup) +{ + bool handled = false; + // Chaff + if (!stricmp("Chaff",pname)) + { + handled = true; + int pnum = IOBJ->id; + int added_count = 0; + int weapon_id = FindWeaponName("Chaff"); + + if(weapon_id!=-1){ + int amount = 12 - Players[pnum].counter_measures.GetTypeIDCount(OBJ_WEAPON,weapon_id); + if( amount>4 ) + amount = 4; + if( amount>0 ){ + for(int c=0;ctype,MOBJ->id)){ + added_count++; + }else + break; + } + if(added_count){ + // Pick it up + *pickup=1; + } + if(pnum==Player_num){ + if(added_count==1){ + AddFilteredHUDMessage(TXT_CHAFF); + }else if(added_count>1){ + AddFilteredHUDMessage(TXT_MSG_MULTI_CHAFFS,added_count); + }else{ + AddFilteredHUDMessage(TXT_COUNTERMEASUREFULL); + } + } + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_CHAFFSFULL); + } + } + } + // Betty4pack + else if (!stricmp("Betty4Pack",pname)) + { + handled = true; + int pnum = IOBJ->id; + int added_count = 0; + int weapon_id = FindWeaponName("Betty"); + + if(weapon_id!=-1){ + int amount = 12 - Players[pnum].counter_measures.GetTypeIDCount(OBJ_WEAPON,weapon_id); + if( amount>4 ) + amount = 4; + if( amount>0 ){ + for(int c=0;ctype,MOBJ->id)){ + added_count++; + }else + break; + } + if(added_count){ + // Pick it up + *pickup=1; + } + if(pnum==Player_num){ + if(added_count==1){ + AddFilteredHUDMessage(TXT_BETTY); + }else if(added_count>1){ + AddFilteredHUDMessage(TXT_MSG_MULTI_BETTY,added_count); + }else{ + AddFilteredHUDMessage(TXT_COUNTERMEASUREFULL); + } + } + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_BETTYFULL); + } + } + } + // Seeker3pack + else if (!stricmp("Seeker3Pack",pname)) + { + handled = true; + int pnum = IOBJ->id; + int added_count = 0; + int weapon_id = FindWeaponName("SeekerMine"); + + if(weapon_id!=-1){ + int amount = 12 - Players[pnum].counter_measures.GetTypeIDCount(OBJ_WEAPON,weapon_id); + if( amount>3 ) + amount = 3; + if( amount>0 ){ + for(int c=0;ctype,MOBJ->id)){ + added_count++; + }else + break; + } + if(added_count){ + // Pick it up + *pickup=1; + } + if(pnum==Player_num){ + if(added_count==1){ + AddFilteredHUDMessage(TXT_SEEKERMINE); + }else if(added_count>1){ + AddFilteredHUDMessage(TXT_MSG_MULTI_SEEKERS,added_count); + }else{ + AddFilteredHUDMessage(TXT_COUNTERMEASUREFULL); + } + } + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_SEEKERFULL); + } + } + } + // Gunboy + else if (!stricmp("GunboyPowerup",pname)) + { + handled = true; + int pnum = IOBJ->id; + int added_count = 0; + int weapon_id = FindWeaponName("GunBoy"); + + if(weapon_id!=-1){ + int amount = 4 - Players[pnum].counter_measures.GetTypeIDCount(OBJ_WEAPON,weapon_id); + if( amount>1 ) + amount = 1; + if( amount>0 ){ + for(int c=0;ctype,MOBJ->id)){ + added_count++; + }else + break; + } + if(added_count){ + // Pick it up + *pickup=1; + } + if(pnum==Player_num){ + if(added_count==1){ + AddFilteredHUDMessage(TXT_GUNBOY); + }else if(added_count>1){ + AddFilteredHUDMessage(TXT_MSG_MULTI_GUNBOY,added_count); + }else{ + AddFilteredHUDMessage(TXT_COUNTERMEASUREFULL); + } + } + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_GUNBOYFULL); + } + } + } + // ProxMine + else if (!stricmp("ProxMinePowerup",pname)) + { + handled = true; + int pnum = IOBJ->id; + int added_count = 0; + int weapon_id = FindWeaponName("ProxMine"); + + if(weapon_id!=-1){ + int amount = 12 - Players[pnum].counter_measures.GetTypeIDCount(OBJ_WEAPON,weapon_id); + if( amount>4 ) + amount = 4; + if( amount>0 ){ + for(int c=0;ctype,MOBJ->id)){ + added_count++; + }else + break; + } + if(added_count){ + // Pick it up + *pickup=1; + } + if(pnum==Player_num){ + if(added_count==1){ + AddFilteredHUDMessage(TXT_PROXMINE); + }else if(added_count>1){ + AddFilteredHUDMessage(TXT_PROXMINECOUNT,added_count); + }else{ + AddFilteredHUDMessage(TXT_COUNTERMEASUREFULL); + } + } + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_PROXMINEFULL); + } + } + } + return handled; +} +bool HandleInventoryPowerups(char *pname,msafe_struct *mstruct,ubyte *pickup) +{ + bool handled = false; + int pnum = IOBJ->id; + //Afterburner cooler + if (!stricmp("Afterburner",pname)) + { + handled = true; + int ab_id = FindObjectIDName("Afterburner"); + if(ab_id!=-1){ + + int inv_count = Players[pnum].inventory.GetTypeIDCount(OBJ_POWERUP,ab_id); + if(inv_count==0){ + //pickit up + *pickup = 1; + Players[pnum].inventory.Add(OBJ_POWERUP,ab_id,NULL,-1,-1,INVAF_TIMEOUTONSPEW,TXT_ABCOOLER); + if(pnum==Player_num) + AddFilteredHUDMessage(TXT_AFTERBURNERCOOLER); + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_ABCOOLERHAVE); + } + } + } + //Energy->Shield Converter + else if (!stricmp("Converter",pname)) + { + handled = true; + int es_id = FindObjectIDName("Converter"); + if(es_id!=-1){ + int inv_count = Players[pnum].inventory.GetTypeIDCount(OBJ_POWERUP,es_id); + if(inv_count==0){ + //pick it up + *pickup = 1; + Players[pnum].inventory.Add(OBJ_POWERUP,es_id,NULL,-1,-1,INVAF_TIMEOUTONSPEW); + if(pnum==Player_num) + AddFilteredHUDMessage(TXT_ETOSCONVERTER); + }else if(pnum==Player_num){ + AddFilteredHUDMessage(TXT_ETOSCONVHAVE); + } + } + } + return handled; +} diff --git a/Descent3/multisafe.h b/Descent3/multisafe.h new file mode 100644 index 000000000..3b8f56b26 --- /dev/null +++ b/Descent3/multisafe.h @@ -0,0 +1,20 @@ +#ifndef MULTISAFE_H +#define MULTISAFE_H + +#include "vecmat.h" +#include "osiris_share.h" + +// LOCAL MULTISAFE FUNCTIONS +void msafe_CallFunction (ubyte type,msafe_struct *mstruct); +void msafe_DoPowerup (msafe_struct *mstruct); + +// Gets a value for the calling party +void msafe_GetValue (int type,msafe_struct *mstruct); +//---------------------------------------------------------------------- + +// MULTPLAYER SPECIFIC STUFF +// MULTISAFE FUNCTIONS +void MultiSendMSafeFunction (ubyte type,msafe_struct *mstruct); +void MultiSendMSafePowerup (msafe_struct *mstruct); + +#endif \ No newline at end of file diff --git a/Descent3/multisafe_server.cpp b/Descent3/multisafe_server.cpp new file mode 100644 index 000000000..c262e0f0a --- /dev/null +++ b/Descent3/multisafe_server.cpp @@ -0,0 +1,1193 @@ +#include "pstypes.h" +#include "pserror.h" +#include "game.h" +#include "multi.h" +#include "multisafe.h" +#include "multi_server.h" +#include "object.h" +#include "player.h" +#include "demofile.h" + +//------------------------ +// MULTISAFE FUNCTIONS +//------------------------ + +#define GET_AND_VERIFY_OBJECT(o) {o=VerifyMSafeObject(MultiGetUshort (data,&count)); if (o==OBJECT_HANDLE_NONE) return; } + +// Makes sure the sever objnum passed in is actually real on our machine +// Also turns it into and object handle +int VerifyMSafeObject (int objnum) +{ + int objectnum=Server_object_list[objnum]; + + if(objectnum==65535 || !(Objects[objectnum].flags & OF_SERVER_OBJECT)) + { + Int3(); // Get Jason, got a bad object with msafe + return OBJECT_HANDLE_NONE; + } + + return Objects[objectnum].handle; + +} + +//This is a bit ugly, but I didn't want to totally change the way the msafe calls work. +//this bool tells msafe if it's ok to process a call even though it's playing back a demo. +bool Demo_call_ok = false; +// Extracts the multisafe function to be executed on the client machine +void MultiDoMSafeFunction (ubyte *data) +{ + msafe_struct base_mstruct; + msafe_struct *mstruct=&base_mstruct; + int count = 0; + + SKIP_HEADER (data,&count); + ubyte type=MultiGetUbyte (data,&count); + switch (type) + { + case MSAFE_WEATHER_RAIN: + mstruct->state=MultiGetByte (data,&count); + mstruct->scalar=MultiGetFloat (data,&count); + break; + case MSAFE_WEATHER_SNOW: + mstruct->state=MultiGetByte (data,&count); + mstruct->scalar=MultiGetFloat (data,&count); + break; + case MSAFE_WEATHER_LIGHTNING: + mstruct->state=MultiGetByte (data,&count); + mstruct->randval=MultiGetUbyte (data,&count); + mstruct->scalar=MultiGetFloat (data,&count); + break; + case MSAFE_WEATHER_LIGHTNING_BOLT: + mstruct->roomnum=MultiGetInt (data,&count); + GET_AND_VERIFY_OBJECT (mstruct->objhandle); + + mstruct->pos.x=MultiGetFloat (data,&count); + mstruct->pos.y=MultiGetFloat (data,&count); + mstruct->pos.z=MultiGetFloat (data,&count); + + mstruct->lifetime=MultiGetFloat (data,&count); + + mstruct->pos2.x=MultiGetFloat (data,&count); + mstruct->pos2.y=MultiGetFloat (data,&count); + mstruct->pos2.z=MultiGetFloat (data,&count); + + char str[255]; + MultiGetString (str,data,&count); + mstruct->texnum=FindTextureName (str); + if (mstruct->texnum<0) + mstruct->texnum=0; + + mstruct->color=MultiGetUshort (data,&count); + mstruct->size=MultiGetByte (data,&count); + mstruct->state=MultiGetByte (data,&count); + mstruct->count=MultiGetByte (data,&count); + mstruct->interval=MultiGetFloat (data,&count); + mstruct->index=MultiGetByte (data,&count); + mstruct->flags=MultiGetByte (data,&count); + + if (mstruct->flags) + { + mstruct->g1=MultiGetUshort (data,&count); + mstruct->g2=MultiGetUshort (data,&count); + } + else + { + GET_AND_VERIFY_OBJECT (mstruct->ithandle); + } + break; + case MSAFE_ROOM_TEXTURE: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->facenum=MultiGetShort (data,&count); + MultiGetString (mstruct->name,data,&count); + mstruct->index=FindTextureName (mstruct->name); + if (mstruct->index==-1) + mstruct->index=0; + break; + case MSAFE_ROOM_WIND: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->wind.x=MultiGetFloat (data,&count); + mstruct->wind.y=MultiGetFloat (data,&count); + mstruct->wind.z=MultiGetFloat (data,&count); + break; + case MSAFE_ROOM_FOG: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->fog_r=(float)MultiGetUbyte (data,&count)/255.0; + mstruct->fog_g=(float)MultiGetUbyte (data,&count)/255.0; + mstruct->fog_b=(float)MultiGetUbyte (data,&count)/255.0; + mstruct->fog_depth=MultiGetFloat (data,&count); + break; + case MSAFE_ROOM_FOG_STATE: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->state=MultiGetUbyte (data,&count); + break; + case MSAFE_ROOM_CHANGING_FOG: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->fog_r=(float)MultiGetUbyte (data,&count)/255.0; + mstruct->fog_g=(float)MultiGetUbyte (data,&count)/255.0; + mstruct->fog_b=(float)MultiGetUbyte (data,&count)/255.0; + mstruct->fog_depth=MultiGetFloat (data,&count); + mstruct->interval=MultiGetFloat (data,&count); + break; + case MSAFE_ROOM_CHANGING_WIND: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->wind.x=MultiGetFloat (data,&count); + mstruct->wind.y=MultiGetFloat (data,&count); + mstruct->wind.z=MultiGetFloat (data,&count); + mstruct->interval=MultiGetFloat (data,&count); + break; + case MSAFE_ROOM_LIGHT_PULSE: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->pulse_time=MultiGetUbyte (data,&count); + mstruct->pulse_offset=MultiGetUbyte (data,&count); + break; + case MSAFE_ROOM_LIGHT_STROBE: + case MSAFE_ROOM_LIGHT_FLICKER: + case MSAFE_ROOM_REFUEL: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->state=MultiGetUbyte (data,&count); + break; + case MSAFE_ROOM_DAMAGE: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->amount=MultiGetFloat (data,&count); + mstruct->index=MultiGetUbyte (data,&count); + break; + case MSAFE_ROOM_PORTAL_RENDER: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->portalnum=MultiGetShort (data,&count); + mstruct->state=MultiGetUbyte (data,&count); + mstruct->flags=MultiGetUbyte (data,&count); + break; + case MSAFE_ROOM_PORTAL_BLOCK: + mstruct->roomnum=MultiGetShort (data,&count); + mstruct->portalnum=MultiGetShort (data,&count); + mstruct->state=MultiGetUbyte (data,&count); + mstruct->flags=MultiGetUbyte (data,&count); + break; + case MSAFE_OBJECT_SHAKE_AREA: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->scalar=MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_SETONFIRE: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + if(MultiGetByte(data,&count)) + { + GET_AND_VERIFY_OBJECT(mstruct->ithandle); + } + else + { + mstruct->ithandle = OBJECT_HANDLE_NONE; + } + mstruct->longevity=MultiGetFloat(data,&count); + mstruct->interval=MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_SHIELDS: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->shields=MultiGetShort(data,&count); + break; + case MSAFE_OBJECT_ENERGY: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->energy=MultiGetShort(data,&count); + break; +// case MSAFE_OBJECT_ANIMATE: +// mstruct->objhandle=VerifyMSafeObject(MultiGetUshort (data,&count)); +// mstruct->start_tick=MultiGetShort (data,&count); +// mstruct->end_tick=MultiGetShort (data,&count); +// mstruct->cycle_time=MultiGetFloat (data,&count); +// mstruct->flags=MultiGetShort (data,&count); +// break; + case MSAFE_OBJECT_MOVEMENT_SCALAR: + case MSAFE_OBJECT_RECHARGE_SCALAR: + case MSAFE_OBJECT_WSPEED_SCALAR: + case MSAFE_OBJECT_ARMOR_SCALAR: + case MSAFE_OBJECT_DAMAGE_SCALAR: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->scalar=MultiGetFloat (data,&count); + break; + case MSAFE_SHOW_ENABLED_CONTROLS: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->state = MultiGetByte (data,&count); + break; + case MSAFE_OBJECT_PLAYER_CMASK: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->control_mask = MultiGetUint (data,&count); + mstruct->control_val = MultiGetByte (data,&count); + break; + case MSAFE_OBJECT_ADD_WEAPON: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->index=MultiGetByte (data,&count); + mstruct->ammo=MultiGetFloat (data,&count); + break; + case MSAFE_OBJECT_START_SPEW: + mstruct->id=MultiGetUbyte (data,&count); + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->random=MultiGetByte (data,&count); + mstruct->is_real=MultiGetByte (data,&count); + mstruct->gunpoint=MultiGetByte (data,&count); + mstruct->effect_type=MultiGetByte (data,&count); + mstruct->phys_info=MultiGetByte (data,&count); + mstruct->drag=MultiGetFloat (data,&count); + mstruct->mass=MultiGetFloat (data,&count); + mstruct->interval=MultiGetFloat (data,&count); + mstruct->longevity=MultiGetFloat (data,&count); + mstruct->lifetime=MultiGetFloat (data,&count); + mstruct->size=MultiGetFloat (data,&count); + mstruct->speed=MultiGetFloat (data,&count); + mprintf ((0,"Got a START SPEW. Index=%d\n",mstruct->id)); + break; + case MSAFE_OBJECT_STOP_SPEW: + mstruct->id = MultiGetUbyte (data,&count); + mprintf ((0,"Got STOP SPEW. index=%d\n",mstruct->id)); + mstruct->id=Server_spew_list[mstruct->id]; + ASSERT (mstruct->id!=65535); + break; + case MSAFE_OBJECT_NO_RENDER: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + break; + case MSAFE_OBJECT_DEFORM: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->amount=MultiGetFloat (data,&count); + mstruct->lifetime=MultiGetFloat (data,&count); + break; + case MSAFE_OBJECT_VIEWER_SHAKE: + mstruct->amount=MultiGetFloat (data,&count); + break; + case MSAFE_OBJECT_GHOST: + case MSAFE_OBJECT_UNGHOST: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + break; + case MSAFE_OBJECT_INVULNERABLE: + case MSAFE_OBJECT_CLOAK: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->state = MultiGetByte(data,&count); + mstruct->lifetime = MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_CLOAKALLPLAYERS: + mstruct->state = MultiGetByte(data,&count); + mstruct->lifetime = MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_LIGHT_DIST: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->light_distance = MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_LIGHT_COLOR: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->r1 = MultiGetFloat(data,&count); + mstruct->g1 = MultiGetFloat(data,&count); + mstruct->b1 = MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_REMOVE: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->playsound = MultiGetByte(data,&count); + break; + case MSAFE_OBJECT_PLAYER_CONTROLAI: + mstruct->slot = MultiGetByte(data,&count); + mstruct->state = MultiGetByte(data,&count); + break; + + case MSAFE_OBJECT_WORLD_POSITION: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->roomnum = MultiGetInt(data,&count); + mstruct->pos.x = MultiGetFloat(data,&count); + mstruct->pos.y = MultiGetFloat(data,&count); + mstruct->pos.z = MultiGetFloat(data,&count); + mstruct->orient.fvec.x = MultiGetFloat(data,&count); + mstruct->orient.fvec.y = MultiGetFloat(data,&count); + mstruct->orient.fvec.z = MultiGetFloat(data,&count); + mstruct->orient.rvec.x = MultiGetFloat(data,&count); + mstruct->orient.rvec.y = MultiGetFloat(data,&count); + mstruct->orient.rvec.z = MultiGetFloat(data,&count); + mstruct->orient.uvec.x = MultiGetFloat(data,&count); + mstruct->orient.uvec.y = MultiGetFloat(data,&count); + mstruct->orient.uvec.z = MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_SPARKS: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->amount = MultiGetFloat(data,&count); + mstruct->lifetime = MultiGetFloat(data,&count); + break; + case MSAFE_SOUND_STREAMING: { + mstruct->state=MultiGetByte (data,&count); + int objnum = MultiGetUshort (data,&count); + if (mstruct->state) + mstruct->objhandle=VerifyMSafeObject(objnum); + + mstruct->volume=MultiGetFloat (data,&count); + MultiGetString (mstruct->name,data,&count); + break; + } + case MSAFE_SOUND_STOP_OBJ: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + + break; + case MSAFE_SOUND_VOLUME_OBJ: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->volume=MultiGetFloat (data,&count); + break; + case MSAFE_MUSIC_REGION: { + mstruct->state=MultiGetByte (data,&count); + int objnum = MultiGetUshort (data,&count); + if (mstruct->state) + mstruct->objhandle=VerifyMSafeObject(objnum); + mstruct->index=MultiGetInt (data,&count); + break; + } + case MSAFE_SOUND_2D: { + mstruct->state=MultiGetByte (data,&count); + int objnum = MultiGetUshort (data,&count); + if (mstruct->state) + mstruct->objhandle=VerifyMSafeObject(objnum); + mstruct->volume=MultiGetFloat (data,&count); + mstruct->index=MultiGetInt (data,&count); + break; + } + case MSAFE_SOUND_OBJECT: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->index=MultiGetInt (data,&count); + break; + case MSAFE_MISC_LEVELGOAL: + MultiGetString (mstruct->message,data,&count); + mstruct->index=MultiGetInt (data,&count); + mstruct->type=MultiGetInt (data,&count); + mstruct->count=MultiGetInt (data,&count); + break; + + case MSAFE_MISC_ENABLE_SHIP: + mstruct->state = (bool)(MultiGetByte (data,&count)!=0); + MultiGetString (mstruct->name,data,&count); + break; + case MSAFE_MISC_UPDATE_HUD_ITEM: + mstruct->color=MultiGetInt (data,&count); + MultiGetString (mstruct->message,data,&count); + break; + case MSAFE_MISC_FILTERED_HUD_MESSAGE: + case MSAFE_MISC_HUD_MESSAGE: + { + mstruct->state=MultiGetByte (data,&count); + int objnum = MultiGetUshort (data,&count); + if (mstruct->state) + mstruct->objhandle=VerifyMSafeObject(objnum); + mstruct->color=MultiGetInt (data,&count); + MultiGetString (mstruct->message,data,&count); + break; + } + + case MSAFE_MISC_GAME_MESSAGE: { + mstruct->state=MultiGetByte (data,&count); + int objnum = MultiGetUshort (data,&count); + if (mstruct->state) + mstruct->objhandle=VerifyMSafeObject(objnum); + mstruct->color=MultiGetInt (data,&count); + MultiGetString (mstruct->message,data,&count); + MultiGetString (mstruct->message2,data,&count); + break; + } + + case MSAFE_MISC_WAYPOINT: + mstruct->index=MultiGetByte (data,&count); + break; + case MSAFE_MISC_POPUP_CAMERA: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->interval=MultiGetFloat (data,&count); + mstruct->scalar=MultiGetFloat (data,&count); + mstruct->gunpoint=MultiGetByte (data,&count); + break; + case MSAFE_MISC_CLOSE_POPUP: + break; + case MSAFE_MISC_GUIDEBOT_NAME: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + MultiGetString (mstruct->name,data,&count); + break; + case MSAFE_DOOR_LOCK_STATE: + mstruct->state=MultiGetUbyte (data,&count); + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + break; + case MSAFE_DOOR_ACTIVATE: + case MSAFE_DOOR_STOP: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + break; + case MSAFE_DOOR_POSITION: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->scalar=MultiGetFloat (data,&count); + break; + case MSAFE_INVEN_ADD_OBJECT: + mstruct->flags=MultiGetInt(data,&count); + MultiGetString(mstruct->message,data,&count); + //note the fall through + case MSAFE_INVEN_REMOVE_OBJECT: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + GET_AND_VERIFY_OBJECT(mstruct->ithandle); + break; + case MSAFE_INVEN_ADD_TYPE_ID: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + MultiGetTypeID(data,&count,&mstruct->type,&mstruct->id); + mstruct->flags = MultiGetInt(data,&count); + break; + case MSAFE_INVEN_REMOVE: + case MSAFE_COUNTERMEASURE_REMOVE: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + MultiGetTypeID(data,&count,&mstruct->type,&mstruct->id); + break; + case MSAFE_COUNTERMEASURE_ADD: + mstruct->slot = MultiGetByte (data,&count); + MultiGetString (mstruct->name,data,&count); + mstruct->count = MultiGetByte(data,&count); + MultiGetTypeID(data,&count,&mstruct->type,&mstruct->id); + break; + + case MSAFE_OBJECT_ROTDRAG: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->rot_drag = MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_TYPE: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->type = MultiGetByte(data,&count); + break; + case MSAFE_OBJECT_ID: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->id = MultiGetUshort(data,&count); + break; + case MSAFE_OBJECT_CONTROL_TYPE: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->control_type = MultiGetByte(data,&count); + break; + case MSAFE_OBJECT_FLAGS: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->flags = MultiGetInt(data,&count); + break; + case MSAFE_OBJECT_MOVEMENT_TYPE: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->movement_type = MultiGetByte(data,&count); + break; + case MSAFE_OBJECT_CREATION_TIME: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->creation_time = MultiGetFloat(data,&count); + break; + case MSAFE_OBJECT_PHYSICS_FLAGS: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->physics_flags = MultiGetInt(data,&count); + break; + case MSAFE_OBJECT_PARENT: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->ithandle = MultiGetUshort (data,&count); + + if(mstruct->ithandle == HANDLE_OBJNUM_MASK) + mstruct->ithandle = OBJECT_HANDLE_NONE; + else + mstruct->ithandle = VerifyMSafeObject(mstruct->ithandle); + break; + case MSAFE_WEAPON_ADD: + GET_AND_VERIFY_OBJECT(mstruct->objhandle); + mstruct->index = MultiGetInt (data,&count); + mstruct->state = (bool)(MultiGetByte (data,&count)!=0); + mstruct->count = MultiGetInt (data,&count); + break; + default: + Int3(); // Illegal type passed to multisafe function + break; + } + if(Demo_flags==DF_PLAYBACK) + { + //Yes this is ugly, but it's better than the other alternatives at the moment + //(changing the prototype of the exported function and breaking all the dlls) + Demo_call_ok = true; + msafe_CallFunction (type,mstruct); + Demo_call_ok = false; + } + else + { + msafe_CallFunction (type,mstruct); + } + +} + +void MultiSendMSafeFunction (ubyte type,msafe_struct *mstruct) +{ + int sequence=-1; + int to_slot=-1; + uint to_bitmask=0xFFFFFFFF; + + ASSERT ((Demo_flags==DF_RECORDING)||(Netgame.local_role==LR_SERVER)); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + size_offset=START_DATA(MP_MSAFE_FUNCTION,data,&count); + + MultiAddUbyte (type,data,&count); + + switch (type) + { + case MSAFE_WEATHER_RAIN: + sequence=NETSEQ_WORLD; + MultiAddByte (mstruct->state,data,&count); + MultiAddFloat (mstruct->scalar,data,&count); + break; + case MSAFE_WEATHER_SNOW: + sequence=NETSEQ_WORLD; + MultiAddByte (mstruct->state,data,&count); + MultiAddFloat (mstruct->scalar,data,&count); + break; + case MSAFE_WEATHER_LIGHTNING: + sequence=NETSEQ_WORLD; + MultiAddByte (mstruct->state,data,&count); + MultiAddUbyte (mstruct->randval,data,&count); + MultiAddFloat (mstruct->scalar,data,&count); + break; + case MSAFE_WEATHER_LIGHTNING_BOLT: + sequence=NETSEQ_WORLD; + MultiAddInt (mstruct->roomnum,data,&count); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + + MultiAddFloat (mstruct->pos.x,data,&count); + MultiAddFloat (mstruct->pos.y,data,&count); + MultiAddFloat (mstruct->pos.z,data,&count); + + MultiAddFloat (mstruct->lifetime,data,&count); + + MultiAddFloat (mstruct->pos2.x,data,&count); + MultiAddFloat (mstruct->pos2.y,data,&count); + MultiAddFloat (mstruct->pos2.z,data,&count); + + MultiAddString (GameTextures[mstruct->texnum].name,data,&count); + MultiAddUshort (mstruct->color,data,&count); + MultiAddByte (mstruct->size,data,&count); + MultiAddByte (mstruct->state,data,&count); + MultiAddByte (mstruct->count,data,&count); + MultiAddFloat (mstruct->interval,data,&count); + MultiAddByte (mstruct->index,data,&count); + + if (mstruct->flags) // Do attached lightning effect + { + MultiAddByte (1,data,&count); + + MultiAddUshort (mstruct->g1,data,&count); + MultiAddUshort (mstruct->g2,data,&count); + } + else + { + MultiAddByte (0,data,&count); + MultiAddUshort (mstruct->ithandle & HANDLE_OBJNUM_MASK,data,&count); + } + + break; + case MSAFE_ROOM_TEXTURE: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddShort (mstruct->facenum,data,&count); + MultiAddString (GameTextures[mstruct->index].name,data,&count); + break; + case MSAFE_ROOM_WIND: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddFloat (mstruct->wind.x,data,&count); + MultiAddFloat (mstruct->wind.y,data,&count); + MultiAddFloat (mstruct->wind.z,data,&count); + break; + case MSAFE_ROOM_CHANGING_WIND: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddFloat (mstruct->wind.x,data,&count); + MultiAddFloat (mstruct->wind.y,data,&count); + MultiAddFloat (mstruct->wind.z,data,&count); + MultiAddFloat (mstruct->interval,data,&count); + break; + case MSAFE_ROOM_FOG: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddUbyte (mstruct->fog_r*255.0,data,&count); + MultiAddUbyte (mstruct->fog_g*255.0,data,&count); + MultiAddUbyte (mstruct->fog_b*255.0,data,&count); + MultiAddFloat (mstruct->fog_depth,data,&count); + break; + case MSAFE_ROOM_FOG_STATE: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddUbyte (mstruct->state,data,&count); + break; + case MSAFE_ROOM_CHANGING_FOG: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddUbyte (mstruct->fog_r*255.0,data,&count); + MultiAddUbyte (mstruct->fog_g*255.0,data,&count); + MultiAddUbyte (mstruct->fog_b*255.0,data,&count); + MultiAddFloat (mstruct->fog_depth,data,&count); + MultiAddFloat (mstruct->interval,data,&count); + break; + case MSAFE_ROOM_LIGHT_PULSE: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddUbyte (mstruct->pulse_time,data,&count); + MultiAddUbyte (mstruct->pulse_offset,data,&count); + break; + case MSAFE_ROOM_LIGHT_STROBE: + case MSAFE_ROOM_LIGHT_FLICKER: + case MSAFE_ROOM_REFUEL: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddUbyte (mstruct->state,data,&count); + break; + case MSAFE_ROOM_DAMAGE: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddFloat (mstruct->amount,data,&count); + MultiAddUbyte (mstruct->index,data,&count); + break; + case MSAFE_ROOM_PORTAL_RENDER: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddShort (mstruct->portalnum,data,&count); + MultiAddUbyte (mstruct->state,data,&count); + + if (mstruct->flags) + MultiAddUbyte (1,data,&count); + else + MultiAddUbyte (0,data,&count); + + break; + case MSAFE_ROOM_PORTAL_BLOCK: + sequence=NETSEQ_WORLD; + MultiAddShort (mstruct->roomnum,data,&count); + MultiAddShort (mstruct->portalnum,data,&count); + MultiAddUbyte (mstruct->state,data,&count); + + if (mstruct->flags) + MultiAddUbyte (1,data,&count); + else + MultiAddUbyte (0,data,&count); + + break; + + case MSAFE_OBJECT_SHAKE_AREA: + { + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->scalar,data,&count); + + to_bitmask=0; + + object *obj=ObjGet(mstruct->objhandle); + + for (int i=1;ipos); + if (distscalar) + to_bitmask|=(1<objhandle & HANDLE_OBJNUM_MASK,data,&count); + if(mstruct->ithandle==OBJECT_HANDLE_NONE) + { + MultiAddByte(0,data,&count); + }else + { + MultiAddByte(1,data,&count); + MultiAddUshort (mstruct->ithandle & HANDLE_OBJNUM_MASK,data,&count); + } + MultiAddFloat (mstruct->longevity,data,&count); + MultiAddFloat (mstruct->interval,data,&count); + break; + + case MSAFE_OBJECT_SHIELDS: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddShort (mstruct->shields,data,&count); + break; + + case MSAFE_OBJECT_ROTDRAG: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat(mstruct->rot_drag, data, &count); + break; + + case MSAFE_OBJECT_TYPE: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte(mstruct->type, data, &count); + break; + + case MSAFE_OBJECT_ID: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddUshort (mstruct->id, data, &count); + break; + + case MSAFE_OBJECT_CONTROL_TYPE: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte(mstruct->control_type,data,&count); + break; + + case MSAFE_OBJECT_FLAGS: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt (mstruct->flags, data, &count); + break; + + case MSAFE_OBJECT_MOVEMENT_TYPE: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte (mstruct->movement_type,data,&count); + break; + + case MSAFE_OBJECT_CREATION_TIME: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->creation_time,data,&count); + break; + + case MSAFE_OBJECT_PHYSICS_FLAGS: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt(mstruct->physics_flags,data,&count); + break; + + case MSAFE_OBJECT_PARENT: + if (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER) + sequence=NETSEQ_PLAYERS; + else + sequence=NETSEQ_OBJECTS; + + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + if(mstruct->ithandle != OBJECT_HANDLE_NONE && mstruct->ithandle != OBJECT_HANDLE_BAD) + MultiAddUshort (mstruct->ithandle & HANDLE_OBJNUM_MASK,data,&count); + else + MultiAddUshort (HANDLE_OBJNUM_MASK,data,&count); + break; + + case MSAFE_OBJECT_ENERGY: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddShort (mstruct->energy,data,&count); + break; +// case MSAFE_OBJECT_ANIMATE: +// sequence=NETSEQ_OBJECTS; +// MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); +// MultiAddShort (mstruct->start_tick,data,&count); +// MultiAddShort (mstruct->end_tick,data,&count); +// MultiAddFloat (mstruct->cycle_time,data,&count); +// MultiAddShort (mstruct->flags,data,&count); +// break; + case MSAFE_OBJECT_MOVEMENT_SCALAR: + case MSAFE_OBJECT_RECHARGE_SCALAR: + case MSAFE_OBJECT_WSPEED_SCALAR: + case MSAFE_OBJECT_ARMOR_SCALAR: + case MSAFE_OBJECT_DAMAGE_SCALAR: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->scalar,data,&count); + break; + case MSAFE_SHOW_ENABLED_CONTROLS: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte (mstruct->state,data,&count); + break; + case MSAFE_OBJECT_PLAYER_CMASK: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddUint (mstruct->control_mask,data,&count); + MultiAddByte (mstruct->control_val,data,&count); + break; + case MSAFE_OBJECT_ADD_WEAPON: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte (mstruct->index,data,&count); + MultiAddFloat (mstruct->ammo,data,&count); + + to_slot=Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].id; + break; + case MSAFE_OBJECT_START_SPEW: + sequence=NETSEQ_WORLD; + MultiAddUbyte ((mstruct->id & 0xFF),data,&count); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte (mstruct->random,data,&count); + MultiAddByte (mstruct->is_real,data,&count); + MultiAddByte (mstruct->gunpoint,data,&count); + MultiAddByte (mstruct->effect_type,data,&count); + MultiAddByte (mstruct->phys_info,data,&count); + MultiAddFloat (mstruct->drag,data,&count); + MultiAddFloat (mstruct->mass,data,&count); + MultiAddFloat (mstruct->interval,data,&count); + MultiAddFloat (mstruct->longevity,data,&count); + MultiAddFloat (mstruct->lifetime,data,&count); + MultiAddFloat (mstruct->size,data,&count); + MultiAddFloat (mstruct->speed,data,&count); + break; + case MSAFE_OBJECT_STOP_SPEW: + sequence=NETSEQ_WORLD; + MultiAddUbyte (mstruct->id & 0xFF,data,&count); + break; + case MSAFE_OBJECT_NO_RENDER: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + break; + case MSAFE_OBJECT_DEFORM: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->amount,data,&count); + MultiAddFloat (mstruct->lifetime,data,&count); + break; + case MSAFE_OBJECT_VIEWER_SHAKE: + sequence=NETSEQ_OBJECTS; + MultiAddFloat (mstruct->amount,data,&count); + break; + case MSAFE_OBJECT_GHOST: + case MSAFE_OBJECT_UNGHOST: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + break; + case MSAFE_OBJECT_INVULNERABLE: + case MSAFE_OBJECT_CLOAK: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte (mstruct->state,data,&count); + MultiAddFloat (mstruct->lifetime,data,&count); + break; + case MSAFE_OBJECT_CLOAKALLPLAYERS: + sequence=NETSEQ_OBJECTS; + MultiAddByte(mstruct->state,data,&count); + MultiAddFloat(mstruct->lifetime,data,&count); + break; + case MSAFE_OBJECT_LIGHT_DIST: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->light_distance,data,&count); + break; + case MSAFE_OBJECT_LIGHT_COLOR: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->r1,data,&count); + MultiAddFloat (mstruct->g1,data,&count); + MultiAddFloat (mstruct->b1,data,&count); + break; + case MSAFE_OBJECT_REMOVE: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddByte (mstruct->playsound,data,&count); + break; + case MSAFE_OBJECT_PLAYER_CONTROLAI: + sequence=NETSEQ_OBJECTS; + MultiAddByte(mstruct->slot,data,&count); + MultiAddByte(mstruct->state,data,&count); + break; + + case MSAFE_OBJECT_WORLD_POSITION: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt (mstruct->roomnum,data,&count); + MultiAddFloat(mstruct->pos.x,data,&count); + MultiAddFloat(mstruct->pos.y,data,&count); + MultiAddFloat(mstruct->pos.z,data,&count); + MultiAddFloat(mstruct->orient.fvec.x,data,&count); + MultiAddFloat(mstruct->orient.fvec.y,data,&count); + MultiAddFloat(mstruct->orient.fvec.z,data,&count); + MultiAddFloat(mstruct->orient.rvec.x,data,&count); + MultiAddFloat(mstruct->orient.rvec.y,data,&count); + MultiAddFloat(mstruct->orient.rvec.z,data,&count); + MultiAddFloat(mstruct->orient.uvec.x,data,&count); + MultiAddFloat(mstruct->orient.uvec.y,data,&count); + MultiAddFloat(mstruct->orient.uvec.z,data,&count); + break; + case MSAFE_OBJECT_SPARKS: + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat(mstruct->amount,data,&count); + MultiAddFloat(mstruct->lifetime,data,&count); + break; + case MSAFE_SOUND_STREAMING: + sequence=NETSEQ_PLAYING; + MultiAddByte (mstruct->state,data,&count); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->volume,data,&count); + MultiAddString (mstruct->name,data,&count); + to_slot=mstruct->slot; + break; + case MSAFE_SOUND_STOP_OBJ: + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + break; + case MSAFE_SOUND_VOLUME_OBJ: + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->volume,data,&count); + break; + case MSAFE_MUSIC_REGION: + sequence=NETSEQ_PLAYING; + MultiAddByte (mstruct->state,data,&count); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt (mstruct->index,data,&count); + to_slot=mstruct->slot; + break; + case MSAFE_SOUND_2D: + sequence=NETSEQ_PLAYING; + MultiAddByte (mstruct->state,data,&count); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->volume,data,&count); + MultiAddInt (mstruct->index,data,&count); + to_slot=mstruct->slot; + break; + case MSAFE_SOUND_OBJECT: + sequence=NETSEQ_OBJECTS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt (mstruct->index,data,&count); + break; + case MSAFE_MISC_LEVELGOAL: + sequence=NETSEQ_WORLD; + MultiAddString (mstruct->message,data,&count); + MultiAddInt (mstruct->index,data,&count); + MultiAddInt (mstruct->type,data,&count); + MultiAddInt (mstruct->count,data,&count); + break; + case MSAFE_MISC_ENABLE_SHIP: + sequence=NETSEQ_PLAYERS; + MultiAddByte (mstruct->state,data,&count); + MultiAddString (mstruct->name,data,&count); + break; + case MSAFE_MISC_UPDATE_HUD_ITEM: + sequence=NETSEQ_PLAYING; + MultiAddInt (mstruct->color,data,&count); + MultiAddString (mstruct->message,data,&count); + break; + case MSAFE_MISC_FILTERED_HUD_MESSAGE: + case MSAFE_MISC_HUD_MESSAGE: + sequence=NETSEQ_PLAYING; + MultiAddByte (mstruct->state,data,&count); + + if (mstruct->state) + { + ASSERT (Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_PLAYER || Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_OBSERVER || Objects[mstruct->objhandle & HANDLE_OBJNUM_MASK].type==OBJ_GHOST); + } + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt (mstruct->color,data,&count); + MultiAddString (mstruct->message,data,&count); + to_slot=mstruct->slot; + break; + case MSAFE_MISC_GAME_MESSAGE: + sequence=NETSEQ_PLAYING; + MultiAddByte (mstruct->state,data,&count); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt (mstruct->color,data,&count); + MultiAddString (mstruct->message,data,&count); + MultiAddString (mstruct->message2,data,&count); + to_slot=mstruct->slot; + break; + case MSAFE_MISC_WAYPOINT: + sequence=NETSEQ_WORLD; + MultiAddByte (mstruct->index,data,&count); + break; + case MSAFE_MISC_POPUP_CAMERA: + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->interval,data,&count); + MultiAddFloat (mstruct->scalar,data,&count); + MultiAddByte (mstruct->gunpoint,data,&count); + break; + case MSAFE_MISC_CLOSE_POPUP: + sequence=NETSEQ_PLAYING; + break; + case MSAFE_MISC_GUIDEBOT_NAME: + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddString (mstruct->name,data,&count); + break; + case MSAFE_DOOR_LOCK_STATE: + sequence=NETSEQ_WORLD; + MultiAddUbyte (mstruct->state,data,&count); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + break; + case MSAFE_DOOR_ACTIVATE: + case MSAFE_DOOR_STOP: + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + break; + case MSAFE_DOOR_POSITION: + sequence=NETSEQ_PLAYING; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddFloat (mstruct->scalar,data,&count); + break; + case MSAFE_INVEN_ADD_OBJECT: + MultiAddInt(mstruct->flags,data,&count); + MultiAddString(mstruct->message,data,&count); + //note the fall through + case MSAFE_INVEN_REMOVE_OBJECT: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddUshort (mstruct->ithandle & HANDLE_OBJNUM_MASK,data,&count); + break; + case MSAFE_INVEN_ADD_TYPE_ID: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddTypeID(mstruct->type,mstruct->id,data,&count); + MultiAddInt(mstruct->flags,data,&count); + break; + case MSAFE_INVEN_REMOVE: + case MSAFE_COUNTERMEASURE_REMOVE: + sequence=NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddTypeID(mstruct->type,mstruct->id,data,&count); + break; + case MSAFE_COUNTERMEASURE_ADD: + sequence = NETSEQ_PLAYERS; + + MultiAddByte (mstruct->slot,data,&count); + MultiAddString (mstruct->name,data,&count); + MultiAddByte(mstruct->count,data,&count); + MultiAddTypeID(mstruct->type,mstruct->id,data,&count); + break; + + case MSAFE_WEAPON_ADD: + sequence = NETSEQ_PLAYERS; + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddInt (mstruct->index,data,&count); + MultiAddByte (mstruct->state,data,&count); + MultiAddInt (mstruct->count,data,&count); + break; + + default: + mprintf ((0,"Type %d is not handled in multisafe server...fix me!!\n",type)); + Int3(); // Illegal type passed to multisafe function + break; + } + + + // Close the packet + END_DATA (count,data,size_offset); + + + // Make sure the sequence got set + ASSERT (sequence!=-1); + if (sequence==-1) + return; + + if(Demo_flags == DF_RECORDING) + { + DemoWriteMSafe(data,count); + } + + if( (Game_mode & GM_MULTI) && (Netgame.local_role==LR_SERVER) ) + { + // Ok, send this out to our clients + if (to_slot==-1) + { + if (to_bitmask==0xFFFFFFFF) + MultiSendReliablyToAllExcept (Player_num,data,count,sequence,false); + else + { + for (int i=1;i=sequence) + nw_SendReliable (NetPlayers[i].reliable_socket,data,count,false); + } + + } + } + else + { + if (NetPlayers[to_slot].sequence>=sequence) + nw_SendReliable (NetPlayers[to_slot].reliable_socket,data,count,false); + } + } +} + + +// Resolves a powerup that the server is telling us about +void MultiDoMSafePowerup (ubyte *data) +{ + int count = 0; + msafe_struct base_mstruct; + msafe_struct *mstruct=&base_mstruct; + + SKIP_HEADER (data,&count); + int objnum=MultiGetUshort (data,&count); + + mstruct->objhandle=VerifyMSafeObject(objnum); + + objnum=MultiGetUshort (data,&count); + mstruct->ithandle=VerifyMSafeObject(objnum); + + + ASSERT( mstruct->objhandle!=-1 && mstruct->ithandle!=-1); + if( mstruct->objhandle==-1 || mstruct->ithandle==-1) + return; + object *op = ObjGet(mstruct->objhandle); + ASSERT(op); + if(op && (op->type!=OBJ_NONE)) + msafe_DoPowerup(mstruct); +} + + +// Sends a powerup over to be evaluated on the client machine +void MultiSendMSafePowerup (msafe_struct *mstruct) +{ + int sequence=-1; + int to_slot=-1; + +// ASSERT (Netgame.local_role==LR_SERVER); + + ubyte data[MAX_GAME_DATA_SIZE]; + int count=0; + int size_offset; + + size_offset=START_DATA(MP_MSAFE_POWERUP,data,&count); + + mprintf ((0,"Sending powerup objnum of %d\n",mstruct->objhandle & HANDLE_OBJNUM_MASK)); + MultiAddUshort (mstruct->objhandle & HANDLE_OBJNUM_MASK,data,&count); + MultiAddUshort (mstruct->ithandle & HANDLE_OBJNUM_MASK,data,&count); + + // Close the packet + END_DATA (count,data,size_offset); + + if(Demo_flags == DF_RECORDING) + { + DemoWritePowerup(data,count); + } + + // Ok, send this out to our clients + if( (Game_mode & GM_MULTI) && (Netgame.local_role==LR_SERVER) ) + MultiSendReliablyToAllExcept (Player_num,data,count,NETSEQ_OBJECTS,false); +} + + + diff --git a/Descent3/newui.cpp b/Descent3/newui.cpp new file mode 100644 index 000000000..e8f194754 --- /dev/null +++ b/Descent3/newui.cpp @@ -0,0 +1,1459 @@ +/* + * $Logfile: /DescentIII/main/newui.cpp $ + * $Revision: 85 $ + * $Date: 4/29/99 3:20a $ + * $Author: Samir $ + * + * New UI Enhancements for D3 + * + * $Log: /DescentIII/main/newui.cpp $ + * + * 85 4/29/99 3:20a Samir + * took out Newui_wait_flag. Not needed. + * + * 84 4/28/99 1:55a Samir + * interface change for UIEdit. + * + * 83 4/25/99 3:35a Jeff + * added a please wait popup dialog when entering telcom + * + * 82 4/22/99 7:41p Matt + * Use new art for old UI. + * + * 81 4/21/99 2:15p Samir + * table file filter fixes. + * + * 80 4/20/99 3:32p Kevin + * made it possible for an edit box to not have a cancel button... used it + * for choosing team count for server + * + * 79 4/20/99 11:46a Samir + * numerous ui 'feel' fixes. + * + * 78 4/18/99 7:55p Samir + * new progress indicator for delays when loading data. + * + * 77 4/15/99 5:28p Samir + * added advanced messagebox + * + * 76 4/06/99 10:44p Samir + * Fixed residue from changes to UID_OK and UID_CANCEL. + * + * 75 3/23/99 9:05p Samir + * don't do sound system calls in wait dialog. time with main menu, since + * that's the place where we have to sync sound initialization with menu + * selection. + * + * 74 3/18/99 10:13a Samir + * wait dialog tied more to menu system. + * + * 73 3/04/99 6:09p Samir + * changed DoWaitMessage to properly center (works for single lines, not + * sure about multiple lines, but since most messages are single lined, it + * works for now.) + * + * 72 3/03/99 5:13a Jeff + * added DoWaitMessage() + * + * 71 3/03/99 4:16a Samir + * added fix for DoEditDialog, setting focus on edit gadget by default for + * all sheets with editboxes (1st one.) + * + * 70 3/01/99 6:55p Samir + * fixed saturation problems with old ui text. + * + * 69 2/27/99 8:34p Samir + * NewuiGameWindow inherits newuiTiledWindow. + * + * 68 2/27/99 4:17p Jeff + * crude fix for text wrapping in DoEdit + * + * 67 2/21/99 6:39p Samir + * new edit box. + * + * 66 2/16/99 12:06p Samir + * added new ui api. + * + * 65 2/07/99 1:17a Jeff + * peppered UI dialogs that were missing NEWUIRES_FORCEQUIT to handle it + * + * 64 1/28/99 6:49p Samir + * frame limit ui system. + * + * 63 12/09/98 4:17p Samir + * messageboxes use new art. + * + * 62 11/19/98 5:40p Kevin + * Demo system + * + * 61 11/02/98 6:00p Jeff + * fixed MessageBox to use new UI artwork...had to redo the way it + * calculates button centers + * + * 60 10/30/98 1:34p Samir + * better sizable UIwindow. + * + * 59 10/23/98 12:53p Samir + * bail from DoUIFrameWithoutInput for multiplayer too. + * + * 58 10/18/98 2:59p Jason + * fixes for beta4 + * + * 57 10/16/98 4:20p Samir + * hotspots replaced by keys Y or N for yesno messagebox. + * + * 56 10/14/98 11:24p Jeff + * fixed DoMessageBox (so keys work in it) + * + * 55 10/12/98 10:23a Samir + * made UI_frame_result non-static. + * + * 54 10/11/98 3:01a Jeff + * made some dialogs multiplayer friendly with multi_bail_ui_menu + * + * 53 10/08/98 7:31p Samir + * moved multi_bail_menu flag to gamesequence.cpp. + * + * 52 10/07/98 6:29p Samir + * bail if multiplayer game ended in ui. + * + * 51 10/07/98 11:24a Jeff + * fixed text centering function + * + * 50 10/06/98 5:34p Jeff + * various UI changes/improvements + * + * 49 9/23/98 6:19p Jeff + * finished up (hopefully) updating the config/ui dialogs to meet our + * standard. Keyboard/joystick config still needs some work + * + * 48 9/23/98 3:07p Jeff + * updated the colors and various other items of config and UI + * + * 47 9/22/98 3:55p Samir + * moved simple callback to NewUI system. + * + * 46 9/14/98 11:22a Samir + * enhanced editboxes + * + * 45 9/10/98 6:33p Samir + * messageboxes should use up to 3 lines of text without a problem. + * + * 44 9/02/98 6:50p Samir + * added new combo box. + * + * 43 9/01/98 4:12p Samir + * added screenshot caparbilities. + * + * 42 9/01/98 12:29p Jeff + * error handling in DoMessageBox (handles extra long strings) + * + * 41 8/31/98 12:38p Samir + * took out hotspots in messageboxes. functionality implented internally. + * + * 40 8/27/98 5:03p Kevin + * Prettied up multiplayer screens and fixed some bugs. + * + * 39 8/27/98 2:51p Samir + * you must specify UIF_BORDER to print the border for a NewUIWindow. + * + * 38 8/24/98 3:12p Samir + * fixed listbox text clipping. + * + * 37 8/20/98 6:46p Samir + * added DoEditDialog. + * + * 36 8/14/98 7:37p Matt + * Since PSFILENAME_LEN is now considered to be the max length of the + * string itself (not counting the terminating null), buffers for file + * name need to be PSFILENAME_LEN+1 bytes long. + * + * 35 7/22/98 2:38p Jeff + * added Y and N keys for the DoMessageBox + * + * 34 6/25/98 12:34p Jeff + * added enter and esc key to DoMessageBox + * + * 33 6/24/98 5:16p Matt + * Don't reset screen mode in DoMessageBox(), so we still see the game + * screen when the pause message is up. + * + * 32 6/22/98 2:34p Samir + * added ability to use a bitmap as background for NewUIWindows. + * + * 31 6/16/98 10:54a Jeff + * + * 30 6/01/98 10:57a Jeff + * (Samir) Fixed bug if a file couldn't be loaded + * + * 29 5/26/98 8:15p Samir + * implemented UIFileDialog's ONDestroy function. + * + * 28 5/25/98 8:18p Samir + * File dialogs implemented. + * + * 27 5/24/98 2:57a Jeff + * updated DoMessageBox to use NewUIButton + * + * 26 5/19/98 6:23p Samir + * added NewUIButton + * + * 25 5/08/98 12:55p Samir + * improved ui handling when switch screen modes. + * + * 24 4/29/98 12:55p Samir + * added titile bar to NewUIWindow. + * + * 23 4/18/98 2:09a Samir + * error checking. + * + * 22 4/13/98 7:01p Samir + * added snazzy listbox and edit box art. + * + * 21 4/10/98 7:51p Samir + * added ui_Flush + * + * 20 4/02/98 12:39p Samir + * don't clear screen in NULL callback. + * + * 19 4/02/98 11:12a Samir + * Always set screen mode to menu for message boxes, and clear screen if + * UICallback if 0. + * + * 18 3/24/98 10:45a Samir + * Added GetUICallback. + * + * 17 3/13/98 12:00p Jeff + * Fixed up DoMessageBox so the text position is correct, it prints a + * window title, and you can specify text color + * + * 16 3/11/98 2:35p Samir + * New message box implemented. + * + * 15 3/10/98 7:26p Samir + * Added new slider. + * + * 14 3/10/98 12:49p Samir + * Bitmapped dialog supported. + * + * 13 3/05/98 6:38p Samir + * Use UI_FONT now. + * + * 12 3/03/98 12:09p Samir + * Clear out keyboard buffer after UI is done. + * + * 11 3/02/98 5:53p Samir + * Default value for alpha of windows is set. + * + * 10 2/15/98 7:07p Samir + * Updated NewUIWindow to UIWindow's changes. + * + * 9 2/13/98 6:37p Samir + * Added DoUIFrameWithoutInput. + * + * 8 1/30/98 7:33p Samir + * Took out 'ok' button. + * + * 7 1/30/98 7:04p Samir + * Added the first NewUIWindow class. + * + * 6 1/26/98 2:15p Samir + * Message box params are char*. + * + * 5 1/18/98 4:22p Samir + * Implemented new UIItem system. + * + * 4 1/13/98 6:29p Samir + * Moved Large Bitmap code from Menu.cpp to newui.cpp. + * + * 3 1/08/98 12:18p Samir + * New menu interface. + * + * 2 1/05/98 10:56a Samir + * New menu stuff. + * + * 1 1/02/98 2:09p Samir + * Initial revision. + * + * + * $NoKeywords: $ + */ + + +#include "newui.h" +#include "game.h" +#include "descent.h" +#include "renderer.h" +#include "3d.h" +#include "gamefont.h" +#include "bitmap.h" +#include "ddio.h" +#include "stringtable.h" +#include "textaux.h" +#include "newui_core.h" +#include "hlsoundlib.h" +#include "dedicated_server.h" + +#include +#include +#include + +#define MSGBOX_HEIGHT msgbox.H() +#define BTN_WIDTH 96 +#define NEWUI_FRAMETIME (1.0f/25.0f) + + +////////////////////////////////////////////////////////////////////////////// +// Declarations + +static struct { + int bmp; + chunked_bitmap chunk; +} +NewUI_bitmap_list[NEWUIBMP_NUM]; + +static bool NewUI_init = false; + +// closes New UI system +void NewUIClose(); + + + +////////////////////////////////////////////////////////////////////////////// +// Functions + +// returns x coordinates to use to determine center position of text items for in a window/dialog +// width - total width of dialog/window +// gap - size (in pixels) of the gap you want between the left and right item +// left_item_width - the width (in pixels) of the left item +// right_item_width - the widht (in pixels) of the right item +// lx - will contain the left item's x coord +// rx - will contain the right item's x coord +void GetCenteredTextPos(int width,int gap,int left_item_width,int right_item_width,int *lx,int *rx) +{ + int center_x = width/2; + + //the left side of the left item should be same distance as the right side of the right item is from the middle + + int width_of_items = gap + left_item_width + right_item_width; + int center_of_items = width_of_items/2; + + int left_gap = 0,right_gap = 0; + + left_gap = center_of_items - left_item_width; + right_gap = center_of_items - right_item_width; + + *lx = center_x - (left_gap + left_item_width); + *rx = center_x + right_gap; +} + +// returns x coordinates to use to determine center position of text items for in a window/dialog +// width - total width of dialog/window +// gap - size (in pixels) of the gap you want between the left and right item +// left_item_width - the width (in pixels) of the left item +// middle_item_width - the width (in pixels) of the middle item +// right_item_width - the width (in pixels) of the right item +// lx - will contain the left item's x coord +// rx - will contain the right item's x coord +// mx - will contain the middle item's x coord +void GetCenteredTextPos(int width,int gap,int left_item_width,int middle_item_width,int right_item_width,int *lx,int *mx,int *rx) +{ + int center_x = width/2; + int width_of_items = 2*gap + left_item_width + middle_item_width + right_item_width; + int center_of_items = width_of_items/2; + + *lx = center_x - (width_of_items/2); + *mx = (*lx) + gap + left_item_width; + *rx = (*mx) + gap + middle_item_width; +} + + +// load in D3 user interface resources +void NewUIInit() +{ + int i; + + if (NewUI_init) { + NewUIClose(); + } + else { + NewUI_init = true; + atexit(NewUIClose); + } + + for (i = 0; i < NEWUIBMP_NUM; i++) + { + NewUI_bitmap_list[i].bmp = -3; + } + +// load in bitmap handles here. + int bm_handle; + + NewUI_bitmap_list[NEWUIBMP_DIALOG_CORNER1].bmp= bm_AllocLoadFileBitmap("d3dlg_nw.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_CORNER2].bmp= bm_AllocLoadFileBitmap("d3dlg_ne.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_CORNER3].bmp= bm_AllocLoadFileBitmap("d3dlg_se.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_CORNER4].bmp= bm_AllocLoadFileBitmap("d3dlg_sw.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_HORIZTOP].bmp= bm_AllocLoadFileBitmap("d3dlg_n.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_VERTRIGHT].bmp= bm_AllocLoadFileBitmap("d3dlg_e.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_HORIZBOT].bmp= bm_AllocLoadFileBitmap("d3dlg_s.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_VERTLEFT].bmp= bm_AllocLoadFileBitmap("d3dlg_w.ogf",0); + NewUI_bitmap_list[NEWUIBMP_DIALOG_BACK].bmp= bm_AllocLoadFileBitmap("d3dlg_bk.ogf",0); +// NewUI_bitmap_list[NEWUIBMP_DIALOG_PANEL].bmp= bm_AllocLoadFileBitmap("d3dlg_tb.ogf",0); + NewUI_bitmap_list[NEWUIBMP_MSGBOX_LEFT].bmp= bm_AllocLoadFileBitmap("d3mbox_l.ogf",0); + NewUI_bitmap_list[NEWUIBMP_MSGBOX_RIGHT].bmp= bm_AllocLoadFileBitmap("d3mbox_r.ogf",0); + NewUI_bitmap_list[NEWUIBMP_MSGBOX_CENTER].bmp= bm_AllocLoadFileBitmap("d3mbox_c.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_CORNER1].bmp= bm_AllocLoadFileBitmap("lbTopLeft32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_CORNER2].bmp= bm_AllocLoadFileBitmap("lbTopRight32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_CORNER3].bmp= bm_AllocLoadFileBitmap("lbBottomRight32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_CORNER4].bmp= bm_AllocLoadFileBitmap("lbBottomLeft32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_HORIZTOP].bmp= bm_AllocLoadFileBitmap("lbTop32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_VERTRIGHT].bmp= bm_AllocLoadFileBitmap("lbRight32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_HORIZBOT].bmp= bm_AllocLoadFileBitmap("lbBottom32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_VERTLEFT].bmp= bm_AllocLoadFileBitmap("lbLeft32.ogf",0); + NewUI_bitmap_list[NEWUIBMP_LIST_BACK].bmp= bm_AllocLoadFileBitmap("d3_lb_bk.ogf",0); + NewUI_bitmap_list[NEWUIBMP_EDIT_LEFT].bmp= bm_AllocLoadFileBitmap("tbLeft.ogf",0); + NewUI_bitmap_list[NEWUIBMP_EDIT_RIGHT].bmp= bm_AllocLoadFileBitmap("tbRight.ogf",0); + NewUI_bitmap_list[NEWUIBMP_EDIT_CENTER].bmp= bm_AllocLoadFileBitmap("tbCenter.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_UP].bmp= bm_AllocLoadFileBitmap("d3_btn_up.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_DOWN].bmp= bm_AllocLoadFileBitmap("d3_btn_down.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_HILITE].bmp= bm_AllocLoadFileBitmap("d3_btn_hilite.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_UP_L].bmp= bm_AllocLoadFileBitmap("d3_btn_up_l.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_DOWN_L].bmp= bm_AllocLoadFileBitmap("d3_btn_down_l.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_HILITE_L].bmp= bm_AllocLoadFileBitmap("d3_btn_hilite_l.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_UP_R].bmp= bm_AllocLoadFileBitmap("d3_btn_up_r.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_DOWN_R].bmp= bm_AllocLoadFileBitmap("d3_btn_down_r.ogf",0); + NewUI_bitmap_list[NEWUIBMP_BTN_HILITE_R].bmp= bm_AllocLoadFileBitmap("d3_btn_hilite_r.ogf",0); + + + bm_handle = bm_AllocLoadFileBitmap("d3slider.ogf",0); + if (bm_handle > 0) { + NewUI_bitmap_list[NEWUIBMP_SLIDER_BUTTON].bmp = -2; + bm_CreateChunkedBitmap(bm_handle, &NewUI_bitmap_list[NEWUIBMP_SLIDER_BUTTON].chunk); + bm_FreeBitmap(bm_handle); + } + else { + NewUI_bitmap_list[NEWUIBMP_SLIDER_BUTTON].bmp = -1; + } + bm_handle = bm_AllocLoadFileBitmap("d3sliderbar.ogf",0); + if (bm_handle > 0) { + NewUI_bitmap_list[NEWUIBMP_SLIDER_BAR].bmp = -2; + bm_CreateChunkedBitmap(bm_handle, &NewUI_bitmap_list[NEWUIBMP_SLIDER_BAR].chunk); + bm_FreeBitmap(bm_handle); + } + else { + NewUI_bitmap_list[NEWUIBMP_SLIDER_BAR].bmp = -1; + } + + for (i = 0; i < NEWUIBMP_NUM; i++) + { + if (NewUI_bitmap_list[i].bmp == 0 || NewUI_bitmap_list[i].bmp == -1) + Error("Unable to load new user interface bitmap #%d.\n", i); + } + + UITextItem::SetSaturationFactor(0); + + +// new ui init 2 + newuiCore_Init(); +} + + +// closes New UI system +void NewUIClose() +{ +// new ui init 2 + newuiCore_Close(); + + for (int i = 0; i < NEWUIBMP_NUM; i++) + { + if (NewUI_bitmap_list[i].bmp >= BAD_BITMAP_HANDLE) { + bm_FreeBitmap(NewUI_bitmap_list[i].bmp); + NewUI_bitmap_list[i].bmp = -1; + } + else if (NewUI_bitmap_list[i].bmp == -2) { + bm_DestroyChunkedBitmap(&NewUI_bitmap_list[i].chunk); + NewUI_bitmap_list[i].bmp = -1; + } + } +} + +// Displays a 'temporary' dialog with a message +// for example: +// ... +// DoWaitMessage(true,"Please Wait..."); +// ... //some code +// DoWaitMessage(false); +void DoWaitMessage(bool enable,char *message) +{ + static bool opened = false; + static ubyte cur_pos = 0; + float curpos_f; + +// if (1) return; + + if (Dedicated_server) return; + if (!enable && !opened) return; + if (GetScreenMode() != SM_MENU) { + opened = false; + return; + } + if (enable && !opened) { + opened = true; + cur_pos = -1; + } + if (!enable && opened) { + opened = false; + return; + } + +// render a ui frame + DoUIFrame(); + cur_pos++; + if (cur_pos > 10) cur_pos = 0; + curpos_f = (cur_pos/10.0f); + +// print our downloading data message. + g3Point *pntlist[4],points[4]; + int i, rx, ry, rw ,rh; + + StartFrame(0,0,Max_window_w, Max_window_h); + grtext_SetFont(MONITOR9_NEWUI_FONT); + rw = Max_window_w; + rh = grfont_GetHeight(MONITOR9_NEWUI_FONT) + 8; + rx = 0; + ry = Max_window_h - rh; + points[0].p3_sx=rx; points[0].p3_sy=ry; + points[1].p3_sx=rx+rw; points[1].p3_sy=ry; + points[2].p3_sx=rx+rw; points[2].p3_sy=ry+rh; + points[3].p3_sx=rx; points[3].p3_sy=ry+rh; + + for (i=0;i<4;i++) + { + points[i].p3_z=0; + points[i].p3_flags=PF_PROJECTED; + pntlist[i]=&points[i]; + } + + rend_SetZBufferState(0); + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetLighting (LS_NONE); + rend_SetFlatColor (GR_BLACK); + rend_SetAlphaValue(230); + rend_DrawPolygon2D( 0, pntlist, 4 ); + + if (cur_pos > 0) + { + points[0].p3_sx=rx; + points[0].p3_sy=ry+rh-(rh/2)+2; + points[1].p3_sx=rx+((float)rw*curpos_f); + points[1].p3_sy=ry+rh-(rh/2)+2; + points[2].p3_sx=rx+((float)rw*curpos_f); + points[2].p3_sy=ry+rh; + points[3].p3_sx=rx; + points[3].p3_sy=ry+rh; + + rend_SetFlatColor (GR_RGB(255,0,0)); + rend_SetAlphaValue(230); + rend_DrawPolygon2D( 0, pntlist, 4 ); + } + + grtext_SetAlpha(255); + grtext_SetColor(NEWUI_MONITORFONT_COLOR); + if (message) { + grtext_Puts(0, ry, message); + } + else { + grtext_Puts(0, ry, TXT_INITDATA); + } + grtext_Flush(); + + EndFrame(); + rend_Flip(); +} + + +void DoWaitPopup(bool enable,char *message) +{ +#define FDWM_HEIGHT 128 +#define FDWM_WIDTH 256 + + static newuiTiledWindow *msgbox = NULL; + static UIText text; + static bool opened = false; + newuiSheet *sheet; + + if(opened == enable){ + Int3(); + } + + opened = enable; + + if(enable){ + msgbox = new newuiTiledWindow; + + if(msgbox){ + + msgbox->Create(NULL, 0,0,FDWM_WIDTH,FDWM_HEIGHT); + + sheet = msgbox->GetSheet(); + if(message) + { + char msg[512]; + int w, h; + textaux_WordWrap(message, msg, FDWM_WIDTH, MONITOR9_NEWUI_FONT); + grtext_SetFont(MONITOR9_NEWUI_FONT); + w = grtext_GetTextLineWidth(msg); + h = grtext_GetTextHeight(msg); + text.Create(msgbox, &UITextItem(MONITOR9_NEWUI_FONT, msg, NEWUI_MONITORFONT_COLOR), (msgbox->W()-w)/2, (msgbox->H()-h)/2); + +// sheet->NewGroup(NULL, , 25); +// sheet->AddText(msg); + } + + msgbox->Open(); +// sheet->Realize(); + + //fake a DoUI + DoUIFrame(); + rend_Flip(); + } + }else{ + + if(msgbox){ + msgbox->Close(); + msgbox->Destroy(); + delete msgbox; + } + msgbox = NULL; + } +} + + +// puts up a message box with a title and message. +int DoMessageBox(const char *title, const char *msg, int type, ddgr_color title_color, ddgr_color text_color) +{ + newuiMessageBox mbox; + newuiSheet *sheet; + + int old_screen_mode, res; + char msgbuf[256]; + +// force menu mode + old_screen_mode = GetScreenMode(); + + textaux_WordWrap(msg, msgbuf, NEWUI_MSGBOX_SHEET_W, MONITOR9_NEWUI_FONT); + +// make message box + mbox.Create(title,type); + + sheet = mbox.GetSheet(); + sheet->NewGroup(NULL, 0,0); + sheet->AddText(msgbuf); + + mbox.Open(); + res = mbox.DoUI(); + mbox.Close(); + + mbox.Destroy(); + +// restore screen + SetScreenMode(old_screen_mode); + +// return correct value. + return ((res==UID_OK)?1:0); +} + + +// puts up a message box with a title and message. +// define text for buttons, btn0_title, btn1_title, ..., until NULL. +// also define hotkey for button immediately after title is defined. +// +// DoMessageBoxAdvanced("Title", "Hi", "Abort", KEY_A, "Retry", KEY_R, "Cancel", KEY_ESC, NULL) +// +// if button with btn0_title pressed, returns 0, btn1_title, returns 1, etc. +// safe for up to three buttons. +int DoMessageBoxAdvanced(const char *title, const char *msg, const char *btn0_title, int key0, ...) +{ + newuiMessageBox mbox; + newuiSheet *sheet; + va_list args; + const char *curbtntitle; + int i; + + int old_screen_mode, res; + char msgbuf[256]; + +// force menu mode + old_screen_mode = GetScreenMode(); + + textaux_WordWrap(msg, msgbuf, NEWUI_MSGBOX_SHEET_W, MONITOR9_NEWUI_FONT); + +// make message box + mbox.Create(title,MSGBOX_NULL); + i = 0; + if (btn0_title) { + mbox.AddButton(btn0_title, i, key0); + i++; + } + +// go through all titles. + va_start(args, key0); + while ((curbtntitle = va_arg(args, char*)) != NULL) + { + int key = va_arg(args, int); + + mbox.AddButton(curbtntitle, i, key); + i++; + if (i == NEWUI_MSGBOX_MAXBTNS) break; + } + + va_end(args); + + sheet = mbox.GetSheet(); + sheet->NewGroup(NULL, 0,0); + sheet->AddText(msgbuf); + + mbox.Open(); + res = mbox.DoUI(); + mbox.Close(); + + mbox.Destroy(); + +// restore screen + SetScreenMode(old_screen_mode); + +// return correct value. + return res; +} + + +bool DoEditDialog(const char *title, char *buffer, int buflen,bool showcancel) +{ + newuiMessageBox mbox; + newuiSheet *sheet; + + int old_screen_mode, res; + char msgbuf[256]; + char *edit; + +// force menu mode + old_screen_mode = GetScreenMode(); + + textaux_WordWrap(title, msgbuf, NEWUI_MSGBOX_SHEET_W, MONITOR9_NEWUI_FONT); + +// make message box + mbox.Create("",showcancel?MSGBOX_OKCANCEL:MSGBOX_OK); + + sheet = mbox.GetSheet(); + sheet->NewGroup(NULL, 0,25); + edit = sheet->AddEditBox( "", buflen, 0, DEFAULT_NEWUID, 0, true); + strcpy(edit, buffer); + + sheet->NewGroup(NULL, 0,0); + sheet->AddText(msgbuf); + + mbox.Open(); + res = mbox.DoUI(); + mbox.Close(); + + if (res != UID_CANCEL && res != NEWUIRES_FORCEQUIT) { + if (strcmp(edit, NEWUI_EDIT_CANCELED_STR) != 0) { + strcpy(buffer, edit); + mprintf((0, "editdialog=%s\n", buffer)); + } + else { + res = UID_CANCEL; + } + } + + mbox.Destroy(); + +// restore screen + SetScreenMode(old_screen_mode); + +// return correct value. + return ((res!=UID_CANCEL && res!=NEWUIRES_FORCEQUIT)?true:false); +} + + +// puts up a file selector box +//Parameters: max_filename_len - the max length for the filename. filebuf have a length of at least max_filename_len+1 +bool DoFileDialog(const char *title, const char *search_path, const char *ext, char *filebuf, unsigned max_filename_len) +{ + NewUIFileDialog dlg; + int w=256,h=256; + int x=Game_window_w/2 - w/2; + int y=Game_window_h/2 - h/2; + + dlg.Create(title, x,y,w,h, search_path, ext); + + if (dlg.DoModal()) { + if (strlen(dlg.GetFilename()) > max_filename_len) + Error("Runtime error. NewUI system DoFileDialog: (%d,%d)", strlen(dlg.GetFilename()), max_filename_len); + strcpy(filebuf, dlg.GetFilename()); + return true; + } + + filebuf[0] = 0; + return false; +} + + + +// shows or hides windows +void OpenUIWindow(UIWindow *wnd) +{ + ASSERT(wnd); + wnd->Open(); +} + + +void CloseUIWindow(UIWindow *wnd) +{ + ASSERT(wnd); + wnd->Close(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// large bitmap library + +int LoadLargeBitmap(char *filename, tLargeBitmap *bmp) +{ + chunked_bitmap chunk; + int bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename), 0); + if (bm_handle == -1) { + bmp->bm_array = NULL; + bmp->bmps_w = 0; + bmp->bmps_h = 0; + return 0; + } + + bm_CreateChunkedBitmap(bm_handle, &chunk); + + if (bm_handle > BAD_BITMAP_HANDLE) + bm_FreeBitmap(bm_handle); + + bmp->bmps_w = chunk.w; + bmp->bmps_h = chunk.h; + bmp->bm_array = chunk.bm_array; + + return 1; +} + + +void FreeLargeBitmap(tLargeBitmap *bmp) +{ + chunked_bitmap chunk; + + chunk.w = bmp->bmps_w; + chunk.h = bmp->bmps_h; + chunk.bm_array = bmp->bm_array; + bm_DestroyChunkedBitmap(&chunk); +} + + +void DrawLargeBitmap(tLargeBitmap *bmp, int x, int y, float alpha) +{ + chunked_bitmap chunk; + + chunk.w = bmp->bmps_w; + chunk.h = bmp->bmps_h; + chunk.bm_array = bmp->bm_array; + rend_DrawChunkedBitmap(&chunk, x, y, alpha*255); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Listbox procedure. + +ubyte NewUIWindow_alpha = 192; + +NewUIWindow::NewUIWindow() +:UIWindow() +{ +} + + +// initializes the window +void NewUIWindow::Create(int x, int y, int w, int h, int flags) +{ + UIWindow::Create(x,y,w,h, flags); + UIWindow::SetBackItem(&UIPrimativeItem(GR_BLACK, NewUIWindow_alpha)); + memset(&m_Chunk, 0,sizeof(m_Chunk)); +} + + +// settings and state info + + +// ui system overridables +// overridable draws the window background before gadgets +void NewUIWindow::OnDraw() +{ + UIWindow::OnDraw(); + + if (m_Flags & UIF_BORDER) { + ui_DrawBox(GR_DARKGRAY, 0,0,m_W, m_H); + ui_DrawBox(GR_LIGHTGRAY, 2,2, m_W-2, m_H-2); + } +} + + +void NewUIWindow::OnDestroy() +{ + bm_DestroyChunkedBitmap(&m_Chunk); +} + + +void NewUIWindow::LoadBackgroundImage(const char *image_name) +{ + UIBitmapItem back_bm; + + bm_DestroyChunkedBitmap(&m_Chunk); + memset(&m_Chunk, 0, sizeof(m_Chunk)); + + if (image_name) { + int bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(image_name), 0); + + if (bm_handle <= BAD_BITMAP_HANDLE) { + Error("Unable to load image %s for background.", image_name); + } + + // takes a large static bitmap and breaks it into smaller managable bitmaps + if (!bm_CreateChunkedBitmap(bm_handle, &m_Chunk)) { + Error("Failed to chunk background bitmap %s.", image_name); + } + + back_bm.set_chunked_bitmap(&m_Chunk); + UIWindow::SetBackItem(&back_bm); + bm_FreeBitmap(bm_handle); + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// NewUIGameWindow implementation. + +NewUIGameWindow::NewUIGameWindow() +{ +} + + +// initializes the window +void NewUIGameWindow::Create(int x, int y, int w, int h, int flags) +{ + newuiTiledWindow::Create(NULL, x, y, w, h, flags); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// NewUIMessageBox implementation. + +NewUIMessageBox::NewUIMessageBox() +:NewUIGameWindow() +{ +} + + +// initializes the window +void NewUIMessageBox::Create(int x, int y, int w, int flags) +{ + NewUIGameWindow::Create(x,y,w,128, flags | UIF_PROCESS_ALL); +} + + +// settings and state info + + + +/////////////////////////////////////////////////////////////////////////////// +// NewUISlider implementation. + +// settings + +void NewUISlider::Create(UIWindow *parent, int id, int x, int y,int flags) +{ + m_Slider.set_chunked_bitmap(&NewUI_bitmap_list[NEWUIBMP_SLIDER_BUTTON].chunk); + m_SliderBar.set_chunked_bitmap(&NewUI_bitmap_list[NEWUIBMP_SLIDER_BAR].chunk); + + UISlider::Create(parent, id, x, y, 5, 5, flags); + + UISlider::SetSliderSwitchItem(&m_Slider); + UISlider::SetSliderItem(&m_SliderBar); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// NewUIEdit implementation. + +// settings + +void NewUIEdit::Create(UIWindow *parent, int id, int x, int y, int w, int h, int flags) +{ + m_EditLeft.set_bitmap(NewUI_bitmap_list[NEWUIBMP_EDIT_LEFT].bmp); + m_EditCen.set_bitmap(NewUI_bitmap_list[NEWUIBMP_EDIT_CENTER].bmp); + m_EditRight.set_bitmap(NewUI_bitmap_list[NEWUIBMP_EDIT_RIGHT].bmp); + +// adjust w and height acoordingly with bitmap background width and height. + int bk_width = (w - m_EditLeft.width() - m_EditRight.width()) / m_EditCen.width(); + int bk_height = 1; + + if (bk_width < 0) + bk_width = 0; + else { + if ((w-m_EditLeft.width()-m_EditRight.width()) % m_EditCen.width()) + bk_width++; + } + + w = (bk_width * m_EditCen.width()) + m_EditLeft.width() + m_EditRight.width(); + h = (bk_height * m_EditLeft.height()); + m_TileWidth = bk_width; + + UIEdit::Create(parent, id, x, y, w, h, flags); + UIEdit::SetTextBounds(m_EditLeft.width()/2, m_W - m_EditRight.height()/2,0); +} + + +void NewUIEdit::OnDraw() +{ +// draw tiles. + int x, tx; + + tx = x = 0; + +// top row. + m_EditLeft.draw(x,0); + x+= m_EditRight.width(); + while (tx++ < m_TileWidth) + { + m_EditCen.draw(x,0); + x+= m_EditCen.width(); + } + m_EditRight.draw(x,0); + + DrawText(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// NewUIListBox Implementation + +void NewUIListBox::Create(UIWindow *parent, int id, int x, int y, int w, int h, int flags) +{ + m_BackNW.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_CORNER1].bmp); + m_BackN.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_HORIZTOP].bmp); + m_BackNE.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_CORNER2].bmp); + m_BackE.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_VERTRIGHT].bmp); + m_BackSE.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_CORNER3].bmp); + m_BackS.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_HORIZBOT].bmp); + m_BackSW.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_CORNER4].bmp); + m_BackW.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_VERTLEFT].bmp); + m_Back.set_bitmap(NewUI_bitmap_list[NEWUIBMP_LIST_BACK].bmp); + +// adjust w and height acoordingly with bitmap background width and height. + int bk_width = (w - m_BackNW.width() - m_BackNE.width()) / m_BackN.width(); + int bk_height = (h - m_BackNW.height() - m_BackSW.height()) / m_BackW.height(); + + if (bk_width < 0) + bk_width = 0; + else { + if ((w-m_BackNW.width()-m_BackNE.width()) % m_BackN.width()) + bk_width++; + } + + if (bk_height < 0) + bk_height = 0; + else { + if ((h-m_BackNW.height()-m_BackSW.height()) % m_BackW.height()) + bk_height++; + } + + w = (bk_width * m_BackN.width()) + m_BackNW.width() + m_BackNE.width(); + h = (bk_height * m_BackW.height()) + m_BackNW.height() + m_BackSW.height(); + m_TileWidth = bk_width; + m_TileHeight = bk_height; + + UIListBox::Create(parent, id, x, y, w, h, flags); + +// redefine special paramaeters for the new listbox + m_TextOffX = m_BackW.width()/2; + m_TextOffY = m_BackN.height()/2; + m_CX = m_TextOffX; + m_CY = m_TextOffY; + m_CX2 = m_W - m_TextOffX; + m_CY2 = m_H - m_TextOffY; +} + + +void NewUIListBox::OnDraw() +{ + int x, y, tx, ty; + +// draw tiles. + tx = x = 0; + ty = y = 0; + +// top row. + m_BackNW.draw(x,y); + x+= m_BackNW.width(); + while (tx++ < m_TileWidth) + { + m_BackN.draw(x,y); + x+= m_BackN.width(); + } + m_BackNE.draw(x,y); + + x = 0; + y += m_BackNW.height(); + +// mid rows. + while (ty++ < m_TileHeight) + { + m_BackW.draw(x,y); + x+= m_BackW.width(); + tx = 0; + while (tx++ < m_TileWidth) + { + m_Back.draw(x,y); + x+= m_Back.width(); + } + m_BackE.draw(x,y); + x = 0; + y += m_BackW.height(); + } + +// bottom row. + m_BackSW.draw(x,y); + x+= m_BackSW.width(); + tx = 0; + while (tx++ < m_TileWidth) + { + m_BackS.draw(x,y); + x+= m_BackS.width(); + } + m_BackSE.draw(x,y); + +// draw text! + UIListBox::OnDraw(); +} + + +// NewUIComboBox +// displays a listbox just one item. + +void NewUIComboBox::Create(UIWindow *parent, int id, int x, int y, int w, int flags) +{ + int h; + + m_CmbLeft.set_bitmap(NewUI_bitmap_list[NEWUIBMP_EDIT_LEFT].bmp); + m_CmbCen.set_bitmap(NewUI_bitmap_list[NEWUIBMP_EDIT_CENTER].bmp); + m_CmbRight.set_bitmap(NewUI_bitmap_list[NEWUIBMP_EDIT_RIGHT].bmp); + +// adjust w and height acoordingly with bitmap background width and height. + int bk_width = (w - m_CmbLeft.width() - m_CmbRight.width()) / m_CmbCen.width(); + int bk_height = 1; + + if (bk_width < 0) + bk_width = 0; + else { + if ((w-m_CmbLeft.width()-m_CmbRight.width()) % m_CmbCen.width()) + bk_width++; + } + + w = (bk_width * m_CmbCen.width()) + m_CmbLeft.width() + m_CmbRight.width(); + h = (bk_height * m_CmbLeft.height()); + m_TileWidth = bk_width; + + UIComboBox::Create(parent, id, x, y, w, h, flags); + m_CX = m_CmbLeft.width()/2; + m_CY = 1+m_CmbLeft.height()/4; + m_CX2 = m_W - m_CmbRight.width()/2; + m_CY2 = m_H - m_CmbRight.height()/4; +} + + +void NewUIComboBox::OnDraw() +{ +// draw tiles. + int x, tx; + + tx = x = 0; + +// top row. + m_CmbLeft.draw(x,0); + x+= m_CmbRight.width(); + while (tx++ < m_TileWidth) + { + m_CmbCen.draw(x,0); + x+= m_CmbCen.width(); + } + m_CmbRight.draw(x,0); + +// draw text! + UIComboBox::OnDraw(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// NewUIButton Implementation + +#define NEWBTN_HEIGHT 25 + +void NewUIButton::Create(UIWindow *parent, int id, UIItem *item, int x, int y, int w, int h, int flags) +{ + flags |= UIF_FIT; + + m_BtnUpC.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_UP].bmp); + m_BtnDownC.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_DOWN].bmp); + m_BtnHiliteC.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_HILITE].bmp); + m_BtnUpL.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_UP_L].bmp); + m_BtnDownL.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_DOWN_L].bmp); + m_BtnHiliteL.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_HILITE_L].bmp); + m_BtnUpR.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_UP_R].bmp); + m_BtnDownR.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_DOWN_R].bmp); + m_BtnHiliteR.set_bitmap(NewUI_bitmap_list[NEWUIBMP_BTN_HILITE_R].bmp); + +// adjust w and height acoordingly with bitmap background width and height. + UIButton::Create(parent, id, item, x, y, w, h, flags); + OnFormat(); +} + + +void NewUIButton::SetTitle(UIItem *item) +{ + UIButton::SetStateItem(UI_BTS_DISABLED, item); + UIButton::SetStateItem(UI_BTS_INACTIVE, item); + UIButton::SetStateItem(UI_BTS_HILITE, item); + UIButton::SetStateItem(UI_BTS_ACTIVATED, item); +} + + +// drawing behavior of the new bitmap. +void NewUIButton::OnDraw() +{ +// draw tiles. + int x, tx; + UIBitmapItem *ltile=NULL, *ctile=NULL, *rtile=NULL; + + tx = x = 0; + +// top row. + switch (m_State) + { + case UI_BTS_DISABLED: + case UI_BTS_INACTIVE: + ltile = &m_BtnUpL; rtile = &m_BtnUpR; ctile = &m_BtnUpC; + break; + + case UI_BTS_HILITE: + ltile = &m_BtnHiliteL; rtile = &m_BtnHiliteR; ctile = &m_BtnHiliteC; + break; + + case UI_BTS_ACTIVATED: + ltile = &m_BtnDownL; rtile = &m_BtnDownR; ctile = &m_BtnDownC; + break; + + default: + ASSERT(0); + } + + ltile->draw(x,0); + x+= ltile->width(); + while (tx++ < m_TileWidth) + { + ctile->draw(x,0); + x+= ctile->width(); + } + rtile->draw(x,0); + +// draw title + if (GetStateItem(m_State)) { + UIItem *item = GetStateItem(m_State); + int x = (m_W/2 - item->width()/2); + int y = (m_H/2 - item->height()/2); + item->draw(x,y); + } +} + + +// override: called when resized or before drawing. +void NewUIButton::OnFormat() +{ + int bk_width; + int bk_height; + +// check fit flag than resize according to bitmap aligned coords, then call UIGadget's one that does +// centering! + if ((m_Flags & UIF_FIT) && m_BtnTitle) { + m_W = m_BtnTitle->width()+10; + } + + bk_width = (m_W - m_BtnUpL.width() - m_BtnUpR.width()) / m_BtnUpC.width(); + bk_height = 1; + + if (bk_width < 0) + bk_width = 0; + else { + if ((m_W-m_BtnUpL.width()-m_BtnUpR.width()) % m_BtnUpC.width()) + bk_width++; + } + + m_W = (bk_width * m_BtnUpC.width()) + m_BtnUpL.width() + m_BtnUpR.width(); + m_H = NEWBTN_HEIGHT; + m_TileWidth = bk_width; + + UIGadget::OnFormat(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// NewUIFileDialog +// this draws a file lister. + +#define UID_FILELIST 0x100 + +void NewUIFileDialog::Create(const char *title, int x, int y, int w, int h, const char *path, const char *filecard) +{ + int list_y, list_w, list_h; + + NewUIGameWindow::Create(x,y,w,h); + + strcpy(m_SearchExt, filecard); + SetSearchPath(path); + + list_w = w - 48; + list_h = h - 64; + list_y = 24; + + m_TitleStr.Create(this, &UITextItem((char *)title), 0,5, UIF_CENTER); + m_ListBox.Create(this, UID_FILELIST, 0,list_y,list_w,list_h, UIF_CENTER); + m_Ok.Create(this, UID_OK, &UITextItem(TXT_OK), 32, m_H - 32, 0, 0, UIF_FIT); + m_Cancel.Create(this, UID_CANCEL, &UITextItem(TXT_CANCEL), m_W-m_Ok.W()-32, m_H - 32, 0, 0, UIF_FIT); + + m_FileItems = NULL; + + UpdateList(); +} + + +void NewUIFileDialog::SetSearchPath(const char *path) +{ + strcpy(m_SearchPath, path); +} + + + +const char *NewUIFileDialog::GetFilename() +{ + return m_NewPath; +} + + +void NewUIFileDialog::UpdateList() +{ + char search_str[PSPATHNAME_LEN]; + char filename[PSFILENAME_LEN+1]; + +// remove items from listbox, free them. + while (m_ListBox.GetNumItems()) + { + UIItem *item = m_ListBox.GetItem(0); + m_ListBox.RemoveItem(item); + delete item; + } + +// read in all files that have all search parameters met + ddio_MakePath(search_str, m_SearchPath, m_SearchExt, NULL); + + if (ddio_FindFileStart(search_str, filename)) { + UIItem *item = new UITextItem(filename); + m_ListBox.AddItem(item); + + while (ddio_FindNextFile(filename)) + { + item = new UITextItem(filename); + m_ListBox.AddItem(item); + } + + ddio_FindFileClose(); + } +} + + +bool NewUIFileDialog::DoModal() +{ + bool exit_menu=false, return_value=false; + + this->Open(); + +// update file list in listbox. + UpdateList(); + + while (!exit_menu) + { + const char *filename; + int index; + int result; + + result = DoUI(); + + switch (result) + { + case NEWUIRES_FORCEQUIT: + case UID_CANCEL: + exit_menu = true; + return_value = false; + break; + + case UID_OK: + case UID_FILELIST: + index = m_ListBox.GetSelectedIndex(); + filename = ((UITextItem *)m_ListBox.GetItem(index))->GetBuffer(); + ASSERT(strlen(filename) < (PSPATHNAME_LEN-1)); + strcpy(m_NewPath, filename); + return_value = true; + exit_menu = true; + break; + } + } + + this->Close(); + this->Destroy(); + + return return_value; +} + + +void NewUIFileDialog::OnDestroy() +{ +// remove items from listbox, free them. + while (m_ListBox.GetNumItems()) + { + UIItem *item = m_ListBox.GetItem(0); + m_ListBox.RemoveItem(item); + delete item; + } + + UIWindow::OnDestroy(); +} + +//@@class NewUIFileDialog: public NewUIGameWindow +//@@{ +//@@ NewUIListBox m_ListBox; +//@@ NewUIButton m_Ok; +//@@ +//@@ char m_SearchPath[PSPATHNAME_LEN]; +//@@ char m_SearchExt[PSFILENAME_LEN]; +//@@ char m_NewPath[PSPATHNAME_LEN]; +//@@ +//@@public: +//@@ void Create(int x, int y, int w, int h, const char *path, const char *ext); +//@@ +//@@}; + + diff --git a/Descent3/newui.h b/Descent3/newui.h new file mode 100644 index 000000000..8e35ed3d9 --- /dev/null +++ b/Descent3/newui.h @@ -0,0 +1,571 @@ +/* + * $Logfile: /DescentIII/Main/newui.h $ + * $Revision: 49 $ + * $Date: 4/27/99 8:51a $ + * $Author: Matt $ + * + * New UI Enhancements for D3 + * + * $Log: /DescentIII/Main/newui.h $ + * + * 49 4/27/99 8:51a Matt + * Added defines for dialog title sizes + * + * 48 4/26/99 9:12p Matt + * Made the hotspot color full bright for old newui to make up for the + * removal of saturation. + * + * 47 4/25/99 3:35a Jeff + * added a please wait popup dialog when entering telcom + * + * 46 4/24/99 11:13p Jeff + * added flags for creating a newui window for a title bar + * + * 45 4/20/99 3:32p Kevin + * made it possible for an edit box to not have a cancel button... used it + * for choosing team count for server + * + * 44 4/16/99 3:58a Jeff + * for linux builds include linux_fix.h to get rid of some errors for + * stricmp + * + * 43 4/15/99 5:28p Samir + * added advanced messagebox + * + * 42 3/05/99 11:38a Kevin + * new color scheme + * + * 41 3/03/99 5:14a Jeff + * added DoWaitMessage() + * + * 40 2/28/99 3:06a Jeff + * adjust ok/cancel y offset for new ui artwork + * + * 39 2/27/99 8:34p Samir + * NewuiGameWindow inherits newuiTiledWindow. + * + * 38 2/21/99 6:39p Samir + * new edit box. + * + * 37 2/16/99 12:06p Samir + * added new ui api. + * + * 36 12/09/98 4:17p Samir + * messageboxes use new art. + * + * 35 11/10/98 5:17p Jeff + * fixed define for OK_CANCEL for new ui artwork + * + * 34 10/07/98 6:27p Samir + * added code to quit menus if mutliplayer bailed. + * + * 33 10/06/98 5:34p Jeff + * various UI changes/improvements + * + * 32 9/28/98 4:35p Jeff + * general UI changes and improvements + * + * 31 9/23/98 3:08p Jeff + * updated the colors and various other items of config and UI + * + * 30 9/22/98 3:55p Samir + * moved simple callback to NewUI system. + * + * 29 9/02/98 6:50p Samir + * added new combo box. + * + * 28 9/02/98 2:54p Jeff + * added defines for text colors to be used throughout the game...fixed up + * buddy menu too + * + * 27 8/27/98 2:52p Samir + * changing window system to work in two modes. + * + * 26 8/20/98 6:46p Samir + * added DoEditDialog. + * + * 25 8/19/98 2:18p Jeff + * changed ddio_CleanPath + * + * 24 8/14/98 7:37p Matt + * Since PSFILENAME_LEN is now considered to be the max length of the + * string itself (not counting the terminating null), buffers for file + * name need to be PSFILENAME_LEN+1 bytes long. + * + * 23 7/29/98 5:39p Jeff + * added DoPathFileDialog + * + * 22 6/22/98 2:34p Samir + * added ability to use a bitmap as background for NewUIWindows. + * + * 21 6/05/98 5:35p Samir + * added Class functions to all newui classes. + * + * 20 5/26/98 8:16p Samir + * implemented UIFileDialog's ONDestroy function. + * + * 19 5/25/98 8:18p Samir + * File dialogs implemented. + * + * 18 5/19/98 6:23p Samir + * added NewUIButton + * + * 17 4/29/98 12:55p Samir + * added titile bar to NewUIWindow. + * + * 16 4/13/98 7:01p Samir + * added snazzy listbox and edit box art. + * + * 15 3/24/98 10:45a Samir + * Added GetUICallback. + * + * 14 3/13/98 12:00p Jeff + * Fixed up DoMessageBox so the text position is correct, it prints a + * window title, and you can specify text color + * + * 13 3/11/98 2:35p Samir + * New message box implemented. + * + * 12 3/10/98 7:26p Samir + * Added new slider. + * + * 11 3/10/98 12:49p Samir + * Bitmapped dialog supported. + * + * 10 3/02/98 5:53p Samir + * Default value for alpha of windows is set. + * + * 9 2/15/98 7:07p Samir + * Updated NewUIWindow to UIWindow's changes. + * + * 8 2/13/98 6:37p Samir + * Added DoUIFrameWithoutInput. + * + * 7 2/02/98 4:31p Samir + * Added prototype for DoUIFrame. + * + * 6 1/30/98 7:04p Samir + * Added the first NewUIWindow class. + * + * 5 1/26/98 2:15p Samir + * Message box params are char*. + * + * 4 1/13/98 6:29p Samir + * Moved Large Bitmap code from Menu.cpp to newui.cpp. + * + * 3 1/08/98 12:18p Samir + * New menu interface. + * + * 2 1/05/98 10:56a Samir + * New menu stuff. + * + * 1 1/02/98 2:09p Samir + * Initial revision + * + * + * $NoKeywords: $ + */ + +#ifndef NEWUI_H +#define NEWUI_H + +#if defined(__LINUX__) +#include "linux/linux_fix.h" //fix some of the stricmp's +#endif + +#include "newui_core.h" + +// flags for creating a newui window +#define NUWF_TITLENONE 0x00000000 //don't display a title bar +#define NUWF_TITLESMALL 0x00100000 //display a small title bar +#define NUWF_TITLEMED 0x00200000 //display a medium size title bar +#define NUWF_TITLELARGE 0x00300000 //display a large title bar +#define NUWF_TITLEBARMASK 0x00300000 //mask to use with flags to get title bar flags + +#define NEWUI_GETTITLEBAR(flags) (flags&NUWF_TITLEBARMASK) + +// the sizes of the various title bars +#define NUW_TITLESMALL_WIDTH 64 +#define NUW_TITLEMED_WIDTH 128 +#define NUW_TITLELARGE_WIDTH 196 + + +////////////////////////////////////////////////////////////////////////////// +// Definitions + +// The following defines are RGB definitions for colors that should be used throughout the UI for text +#define UICOL_HOTSPOT_LO GR_RGB(85,234,3) // Color for a hotspot when it isn't in focus//GR_RGB(50,50,255) // Color for a hotspot when it isn't in focus +#define UICOL_HOTSPOT_HI GR_WHITE // Color for a hotspot when it is in focus +#define UICOL_TEXT_NORMAL GR_RGB(206,254,241) // Color for text that is used for labels +#define UICOL_WINDOW_TITLE GR_RGB(207,248,105) // Color for window title text +#define UICOL_TEXT_AUX GR_RGB(200,200,200) // Color for auxillary text (text that isn't a label) +#define UICOL_LISTBOX_LO GR_RGB(85,234,3) // Color for listbox text that isn't in focus +#define UICOL_LISTBOX_HI GR_RGB(207,248,105) // Color for listbox text that is in focus +#define UICOL_BUTTON_LO GR_RGB(85,234,3) // Color for text on a button that isn't in focus +#define UICOL_BUTTON_HI GR_RGB(207,248,105) // Color for text on a button that is in focus +#define UIALPHA_HOTSPOT_LO 192 // Alpha value for hotspots not in focus +#define UIALPHA_HOTSPOT_HI 255 // Alpha value for hotspots in focus + +// returns x coordinates to use to determine center position of text items for in a window/dialog +// width - total width of dialog/window +// gap - size (in pixels) of the gap you want between the left and right item +// left_item_width - the width (in pixels) of the left item +// right_item_width - the widht (in pixels) of the right item +// lx - will contain the left item's x coord +// rx - will contain the right item's x coord +void GetCenteredTextPos(int width,int gap,int left_item_width,int right_item_width,int *lx,int *rx); +// returns x coordinates to use to determine center position of text items for in a window/dialog +// width - total width of dialog/window +// gap - size (in pixels) of the gap you want between the left and right item +// left_item_width - the width (in pixels) of the left item +// middle_item_width - the width (in pixels) of the middle item +// right_item_width - the width (in pixels) of the right item +// lx - will contain the left item's x coord +// rx - will contain the right item's x coord +// mx - will contain the middle item's x coord +void GetCenteredTextPos(int width,int gap,int left_item_width,int middle_item_width,int right_item_width,int *lx,int *mx,int *rx); + +// The following defines are y offset values to be used for "OK","Cancel" and "Done" (take the height of the window +// minus the offset to get the correct y) +#define OKCANCEL_YOFFSET 50 //subtract this from the height of the window + +#define UI_BORDERSIZE 20 + +#define NEWUIBMP_NUM 48 + +// bitmap ids for user interface bitmaps. +#define NEWUIBMP_DIALOG_CORNER1 0 +#define NEWUIBMP_DIALOG_CORNER2 1 +#define NEWUIBMP_DIALOG_CORNER3 2 +#define NEWUIBMP_DIALOG_CORNER4 3 +#define NEWUIBMP_DIALOG_HORIZTOP 4 +#define NEWUIBMP_DIALOG_VERTRIGHT 5 +#define NEWUIBMP_DIALOG_HORIZBOT 6 +#define NEWUIBMP_DIALOG_VERTLEFT 7 +#define NEWUIBMP_DIALOG_BACK 8 +#define NEWUIBMP_DIALOG_PANEL 9 +#define NEWUIBMP_MSGBOX_LEFT 10 +#define NEWUIBMP_MSGBOX_RIGHT 11 +#define NEWUIBMP_MSGBOX_CENTER 12 +#define NEWUIBMP_SLIDER_BAR 13 +#define NEWUIBMP_SLIDER_BUTTON 14 +#define NEWUIBMP_EDIT_LEFT 15 +#define NEWUIBMP_EDIT_CENTER 16 +#define NEWUIBMP_EDIT_RIGHT 17 +#define NEWUIBMP_LIST_CORNER1 18 +#define NEWUIBMP_LIST_CORNER2 19 +#define NEWUIBMP_LIST_CORNER3 20 +#define NEWUIBMP_LIST_CORNER4 21 +#define NEWUIBMP_LIST_HORIZTOP 22 +#define NEWUIBMP_LIST_VERTRIGHT 23 +#define NEWUIBMP_LIST_HORIZBOT 24 +#define NEWUIBMP_LIST_VERTLEFT 25 +#define NEWUIBMP_LIST_BACK 26 +#define NEWUIBMP_BTN_UP 27 +#define NEWUIBMP_BTN_DOWN 28 +#define NEWUIBMP_BTN_HILITE 29 +#define NEWUIBMP_BTN_UP_L 30 +#define NEWUIBMP_BTN_DOWN_L 31 +#define NEWUIBMP_BTN_HILITE_L 32 +#define NEWUIBMP_BTN_UP_R 33 +#define NEWUIBMP_BTN_DOWN_R 34 +#define NEWUIBMP_BTN_HILITE_R 35 + + +// Large Bitmap system +typedef struct tLargeBitmap +{ + int bmps_w; + int bmps_h; + int *bm_array; +} +tLargeBitmap; + +// alpha for all NewUIWindows. +extern ubyte NewUIWindow_alpha; + +////////////////////////////////////////////////////////////////////////////// +// Core interface with UI system + +// load in D3 user interface resources +void NewUIInit(); + +// closes New UI system +void NewUIClose(); + +// shows or hides windows +void OpenUIWindow(UIWindow *wnd); +void CloseUIWindow(UIWindow *wnd); + + +////////////////////////////////////////////////////////////////////////////// +// quick and dirty functions + +// Displays a 'temporary' dialog with a message +// for example: +// ... +// DoWaitMessage(true,"Please Wait..."); +// ... //some code +// DoWaitMessage(false); +void DoWaitMessage(bool enable,char *message=NULL); +void DoWaitPopup(bool enable,char *message=NULL); + +// puts up a message box with a title and message. +int DoMessageBox(const char *title, const char *msg, int type, ddgr_color title_color = UICOL_WINDOW_TITLE, ddgr_color text_color = UICOL_TEXT_NORMAL); + +// puts up a message box with a title and message. +// define text for buttons, btn0_title, btn1_title, ..., until NULL. +// also define hotkey for button immediately after title is defined. +// +// DoMessageBoxAdvanced("Title", "Hi", "Abort", KEY_A, "Retry", KEY_R, "Cancel", KEY_ESC, NULL) +// +// if button with btn0_title pressed, returns 0, btn1_title, returns 1, etc. +// safe for up to three buttons. +int DoMessageBoxAdvanced(const char *title, const char *msg, const char *btn0_title, int key0, ...); + +// edit dialog. +bool DoEditDialog(const char *title, char *buffer, int buflen,bool showcancel=true); + +// puts up a file selector box +//Parameters: max_filename_len - the max length for the filename. filebuf have a length of at least max_filename_len+1 +bool DoFileDialog(const char *title, const char *search_path, const char *ext, char *filebuf, unsigned max_filename_len); + +#define PFDF_FILEMUSTEXIST 0x0001 +// Displays a file dialog that is very much like a Windows file dialog (you can move around directories) +// save_dialog = is this dialog being used to save file, or load a file. If save, than pass true +// path = on entry is the initial path to start in (must be set...set to 0 length string to go to root directory) +// on exit it is the absolute path to selected file on return (if return is true) must be at least _MAX_PATH in size +// title = Title of the dialog +// wildc = semicolon seperated list of wildcards ("*.txt;*.doc;*.exe") +bool DoPathFileDialog(bool save_dialog,char *path,char *title,char *wildc,int flags); + +////////////////////////////////////////////////////////////////////////////// +// quick and dirty functions + +int LoadLargeBitmap(char *filename, tLargeBitmap *bmp); +void FreeLargeBitmap(tLargeBitmap *bmp); +void DrawLargeBitmap(tLargeBitmap *bmp, int x, int y, float alpha); + + +////////////////////////////////////////////////////////////////////////////// +// NewUI Classes + +const tUIClass newuiWindow = uiNewClass, + newuiGameWindow = uiNewClass+1; +// newuiMessageBox = uiNewClass+2, +// newuiSlider = uiNewClass+3, +// newuiEdit = uiNewClass+4, +// newuiListBox = uiNewClass+5, +// newuiButton = uiNewClass+6; + +const tUIClass newuiNewClass = 1000; + + +// NewUIGameWindow +// this draws a UI window, but with a not-so-cool faded background. + +class NewUIWindow: public UIWindow +{ + chunked_bitmap m_Chunk; + +public: + NewUIWindow(); + + void Create(int x, int y, int w, int h, int flags=UIF_PROCESS_MENU); + + virtual tUIClass Class() const { // Overide this function to name the class + return newuiWindow; + }; + +// specify filename of image to use as background. + void LoadBackgroundImage(const char *image_name); + +// ui system overridables +protected: + virtual void OnDraw(); // overridable draws the window background before gadgets + virtual void OnDestroy(); // when window is nuked. +}; + + +// NewUIGameWindow +// this draws a UI window, but with the cool standard game dialog background. + +class NewUIGameWindow: public newuiTiledWindow +{ +public: + NewUIGameWindow(); + + void Create(int x, int y, int w, int h, int flags=UIF_PROCESS_MENU); + + virtual tUIClass Class() const { // Overide this function to name the class + return newuiGameWindow; + }; +}; + + +// NewUIMessageBox +// this draws a UI window, but with the cool standard game dialog background. + +class NewUIMessageBox: public NewUIGameWindow +{ +public: + NewUIMessageBox(); + + void Create(int x, int y, int w, int flags=0); +}; + + + +// NewUISlider +// this draws a nicer loking slider. + +class NewUISlider: public UISlider +{ + UIBitmapItem m_SliderBar; + UIBitmapItem m_Slider; + +public: + void Create(UIWindow *parent, int id, int x, int y, int flags=0); + +}; + + + +// NewUIEdit +// this draws a nicer edit box + +class NewUIEdit: public UIEdit +{ + UIBitmapItem m_EditLeft; + UIBitmapItem m_EditCen; + UIBitmapItem m_EditRight; + + int m_TileWidth; + +public: + void Create(UIWindow *parent, int id, int x, int y, int w, int h, int flags=0); + +// ui system overridables +protected: + virtual void OnDraw(); // overridable draws the background first +}; + + + +// NewUIListBox +// this draws a nicer list box + +class NewUIListBox: public UIListBox +{ + UIBitmapItem m_BackNW; + UIBitmapItem m_BackN; + UIBitmapItem m_BackNE; + UIBitmapItem m_BackE; + UIBitmapItem m_BackSE; + UIBitmapItem m_BackS; + UIBitmapItem m_BackSW; + UIBitmapItem m_BackW; + UIBitmapItem m_Back; + + int m_TileWidth; + int m_TileHeight; + +public: + void Create(UIWindow *parent, int id, int x, int y, int w, int h, int flags=0); + +// ui system overridables +protected: + virtual void OnDraw(); // overridable draws the background first +}; + + +// NewUIComboBox +// displays a listbox just one item. + +class NewUIComboBox: public UIComboBox +{ + UIBitmapItem m_CmbLeft; + UIBitmapItem m_CmbCen; + UIBitmapItem m_CmbRight; + + int m_TileWidth; +public: + void Create(UIWindow *parent, int id, int x, int y, int w, int flags=0); + +// ui system overridables +protected: + virtual void OnDraw(); // overridable draws the background first +}; + + + +// NewUIButton +// a nicer fixed size button. + +class NewUIButton: public UIButton +{ + UIBitmapItem m_BtnUpL, m_BtnUpC, m_BtnUpR; + UIBitmapItem m_BtnDownL, m_BtnDownC, m_BtnDownR; + UIBitmapItem m_BtnHiliteL, m_BtnHiliteC, m_BtnHiliteR; + + UIItem *m_BtnTitle; + + int m_TileWidth; + +public: + NewUIButton() + { + m_BtnTitle = NULL; + }; + + virtual ~NewUIButton() + { + if (m_BtnTitle) + delete m_BtnTitle; + }; + + void Create(UIWindow *parent, int id, UIItem *item, int x, int y, int w, int h, int flags=0); + void SetTitle(UIItem *item); + +// ui system overridables +protected: + virtual void OnDraw(); // overridable draws the background first + virtual void OnFormat(); // override: called when resized or before drawing. +}; + + +// NewUIFileDialog +// this draws a file lister. + +class NewUIFileDialog: public NewUIGameWindow +{ + NewUIListBox m_ListBox; + NewUIButton m_Ok; + NewUIButton m_Cancel; + UIText m_TitleStr; + + UITextItem *m_FileItems; // file item array. + + char m_SearchPath[PSPATHNAME_LEN]; + char m_SearchExt[PSFILENAME_LEN+1]; + char m_NewPath[PSPATHNAME_LEN]; + +private: + void UpdateList(); + +public: + void Create(const char *title, int x, int y, int w, int h, const char *path, const char *filecard); + + void SetSearchPath(const char *path); + const char *GetFilename(); + +public: +// call this function to execute dialog. + virtual bool DoModal(); + +protected: + virtual void OnDestroy(); +}; + + +#endif \ No newline at end of file diff --git a/Descent3/newui_core.cpp b/Descent3/newui_core.cpp new file mode 100644 index 000000000..3bcb06401 --- /dev/null +++ b/Descent3/newui_core.cpp @@ -0,0 +1,4813 @@ +/* +* $Logfile: /DescentIII/Main/newui_core.cpp $ +* $Revision: 64 $ +* $Date: 10/21/99 5:05p $ +* $Author: Matt $ +* +* +* +* $Log: /DescentIII/Main/newui_core.cpp $ + * + * 64 10/21/99 5:05p Matt + * Mac merge + * + * 63 5/20/99 11:08p Samir + * better error checking when freeing resources, may do nothing, may fix a + * bug. + * + * 62 5/10/99 12:31a Samir + * listboxes don't quit when double clicked outside selected item. + * + * 61 5/08/99 11:20p Samir + * hotspots have different highlight effect. + * + * 60 5/08/99 1:06a Samir + * scrunch messagebox buttons closer together + * + * 59 5/06/99 1:24a Samir + * upped editbox maxlength to 512 + * + * 58 5/05/99 10:53a Samir + * allow loading of 'error texture' bitmaps to determine missing data (and + * not to error out.) + * + * 57 5/02/99 7:28p Samir + * fixed a bunch of option button bugs and allow music in + * UIFrameWithoutInput for the menu mode. + * + * 56 5/02/99 2:14a Samir + * fixed a few issues with option buttons. + * + * 55 5/01/99 7:46p Samir + * oops. really bad if statement fixed, could fix crash bug. + * + * 54 5/01/99 1:14a Samir + * fixed mouse sequencing probs with selections being canceled out by + * UISystem. + * + * 53 4/30/99 10:53p Samir + * only accept double clicks inside of listbox + * + * 52 4/29/99 10:02p Samir + * fixed percent display for slider. + * + * 51 4/29/99 3:21a Samir + * reorganized main menu music to work in config, multiplayer, whereever. + * + * 50 4/29/99 2:18a Samir + * updated art for options style menu. + * + * 49 4/28/99 1:54a Samir + * visual tweaks to text + * + * 48 4/27/99 9:24a Matt + * Added system for drawing title bars on dialogs. + * + * 47 4/26/99 7:46p Samir + * newuiTiledWindow flags passed to create is an int now. + * + * 46 4/21/99 2:15p Samir + * table file filter fixes. + * + * 45 4/21/99 12:42p Samir + * change some communication betwen UIEdit and child classes. + * + * 44 4/21/99 10:58a Samir + * added changable text. + * + * 43 4/20/99 11:46a Samir + * numerous ui 'feel' fixes. + * + * 42 4/18/99 7:55p Samir + * fixed listbox double click and added functions to load and release + * pertinent newui graphic data. + * + * 41 4/15/99 5:28p Samir + * added advanced messagebox + * + * 40 4/05/99 5:40p Samir + * double clicking on an empty listbox wont return anything. + * + * 39 4/05/99 5:13p Samir + * fixed combo box asthetics. + * + * 38 4/05/99 10:40a Samir + * upped count of gadgets in titled window. + * + * 37 3/23/99 9:05p Samir + * took out redundant ddio_KeyFlush + * + * 36 3/22/99 1:55p Jeff + * fixed more crashes + * + * 35 3/22/99 12:47p Jeff + * oops...fixed some listbox crashes + * + * 34 3/19/99 9:21p Jeff + * fixed some listbox sorting issues + * + * 33 3/19/99 9:15p Jeff + * fixed some combo box things (Samir). Fixed sorting issues with + * listboxes/comboboxes if an item is added/removed before the selected + * index (Jeff) + * + * 32 3/17/99 11:46a Jeff + * added callback to set focus when a sheet is realized (Samir) + * + * 31 3/10/99 3:49p Samir + * indent hotspots after newline. + * + * 30 3/09/99 11:58a Samir + * converted buddy menu to new ui. + * + * 29 3/03/99 4:16a Samir + * added fix for DoEditDialog, setting focus on edit gadget by default for + * all sheets with editboxes (1st one.) + * + * 28 3/03/99 3:38a Samir + * made listbox highlight a little more tolerable. + * + * 27 3/01/99 8:51p Samir + * upped count of ui bitmaps. + * + * 26 3/01/99 7:29p Samir + * fixed an issue with slaves getting freed after their master was + * destroyed for sliders. + * + * 25 3/01/99 6:55p Samir + * fixed saturation problems with old ui text. + * + * 24 3/01/99 5:52p Samir + * fixed slider bars and added render distance. + * + * 23 3/01/99 4:39p Samir + * added mono_state and dual_state menu option buttons. + * + * 22 3/01/99 4:59a Samir + * added hotspots, combo boxes. + * + * 21 2/28/99 3:26a Samir + * editbox enhancements. + * + * 20 2/28/99 2:23a Jeff + * fixed sorting problem when calling GetCurrentItem + * + * 19 2/28/99 1:21a Jeff + * fixed cut & paste bug using slider-> instead of edit-> + * + * 18 2/26/99 2:10a Samir + * fixed newuiTiledWindow so it realizes gadgets only when first started. + * + * 17 2/24/99 9:39p Jeff + * fixed listbox sorting +* +* $NoKeywords: $ +*/ + + +#include "newui_core.h" +#include "bitmap.h" +#include "mem.h" +#include "pserror.h" +#include "game.h" +#include "ddio.h" +#include "renderer.h" +#include "descent.h" +#include "application.h" +#include "stringtable.h" +#include "gamefont.h" +#include "pstring.h" +#include "textaux.h" +#include "d3music.h" +#include "hlsoundlib.h" + +#include +#include + +extern void ui_DoCursor(); + + + +// filenames of ui bitmaps +/* + $$TABLE_GAMEFILE "LRGButton.ogf" + $$TABLE_GAMEFILE "LRGButtonLit.ogf" + $$TABLE_GAMEFILE "LongButton.ogf" + $$TABLE_GAMEFILE "LongButtonLit.ogf" + $$TABLE_GAMEFILE "SmallButton.ogf" + $$TABLE_GAMEFILE "SmallButtonLit.ogf" + $$TABLE_GAMEFILE "SmallButtonFramed.ogf" + $$TABLE_GAMEFILE "SmallButtonFramedLit.ogf" + $$TABLE_GAMEFILE "LongButtonRed.ogf" + $$TABLE_GAMEFILE "LongButtonLitRed.ogf" + $$TABLE_GAMEFILE "SmallButton.ogf" + $$TABLE_GAMEFILE "SmallButtonLitRed.ogf" + $$TABLE_GAMEFILE "Bar.ogf" + $$TABLE_GAMEFILE "MinusButton.ogf" + $$TABLE_GAMEFILE "MinusButtonLit.ogf" + $$TABLE_GAMEFILE "PluseButton.ogf" + $$TABLE_GAMEFILE "PluseButtonLit.ogf" + $$TABLE_GAMEFILE "UpArrow.ogf" + $$TABLE_GAMEFILE "UpArrowLit.ogf" + $$TABLE_GAMEFILE "DownArrow.ogf" + $$TABLE_GAMEFILE "DownArrowLit.ogf" + $$TABLE_GAMEFILE "TinyButton.ogf" + $$TABLE_GAMEFILE "TinyButtonLit.ogf" + $$TABLE_GAMEFILE "SmallScreen.ogf" + $$TABLE_GAMEFILE "MediumScreen.ogf" + $$TABLE_GAMEFILE "FullScreen.ogf" + $$TABLE_GAMEFILE "lbTop32.ogf" + $$TABLE_GAMEFILE "lbTopRight32.ogf" + $$TABLE_GAMEFILE "lbRight32.ogf" + $$TABLE_GAMEFILE "lbBottomRight32.ogf" + $$TABLE_GAMEFILE "lbBottom32.ogf" + $$TABLE_GAMEFILE "lbBottomLeft32.ogf" + $$TABLE_GAMEFILE "lbLeft32.ogf" + $$TABLE_GAMEFILE "lbTopLeft32.ogf" + $$TABLE_GAMEFILE "winTop32.ogf" + $$TABLE_GAMEFILE "winTopRight64.ogf" + $$TABLE_GAMEFILE "winRight32.ogf" + $$TABLE_GAMEFILE "winBottomRight64.ogf" + $$TABLE_GAMEFILE "winBottom32.ogf" + $$TABLE_GAMEFILE "winBottomLeft64.ogf" + $$TABLE_GAMEFILE "winLeft32.ogf" + $$TABLE_GAMEFILE "winTopLeft64.ogf" + $$TABLE_GAMEFILE "winCenter32.ogf" + $$TABLE_GAMEFILE "winTitleLeft32.ogf" + $$TABLE_GAMEFILE "winTitleCenter32.ogf" + $$TABLE_GAMEFILE "winTitleRight32.ogf" + $$TABLE_GAMEFILE "ComboBox.ogf" + $$TABLE_GAMEFILE "AltArrowUp.ogf" + $$TABLE_GAMEFILE "AltArrowUpLit.ogf" + $$TABLE_GAMEFILE "AltArrowDown.ogf" + $$TABLE_GAMEFILE "AltArrowDownLit.ogf" +*/ + +#define N_NEWUI_BMPS_TOTAL 64 + +#define NEWUI_LRGBTN_FILE "LRGButton.ogf" +#define NEWUI_LRGBTNLIT_FILE "LRGButtonLit.ogf" +#define NEWUI_LBTN_FILE "LongButton.ogf" +#define NEWUI_LBTNLIT_FILE "LongButtonLit.ogf" +#define NEWUI_BTN_FILE "SmallButton.ogf" +#define NEWUI_BTNLIT_FILE "SmallButtonLit.ogf" +#define NEWUI_LBTNFRAMED_FILE "LongButton.ogf" +#define NEWUI_LBTNLITFRAMED_FILE "LongButtonLit.ogf" +#define NEWUI_BTNFRAMED_FILE "SmallButtonFramed.ogf" +#define NEWUI_BTNLITFRAMED_FILE "SmallButtonFramedLit.ogf" +#define NEWUI_LCHKBTN_FILE "LongButtonRed.ogf" +#define NEWUI_LCHKBTNLIT_FILE "LongButtonLitRed.ogf" +#define NEWUI_CHKBTN_FILE "SmallButton.ogf" +#define NEWUI_CHKBTNLIT_FILE "SmallButtonLitRed.ogf" +#define NEWUI_SLIDER_FILE "Bar.ogf" +#define NEWUI_LARR_FILE "MinusButton.ogf" +#define NEWUI_LARRLIT_FILE "MinusButtonLit.ogf" +#define NEWUI_RARR_FILE "PluseButton.ogf" +#define NEWUI_RARRLIT_FILE "PluseButtonLit.ogf" +#define NEWUI_UARR_FILE "UPArrow.ogf" +#define NEWUI_UARRLIT_FILE "UPArrowLit.ogf" +#define NEWUI_DARR_FILE "DownArrow.ogf" +#define NEWUI_DARRLIT_FILE "DownArrowLit.ogf" +#define NEWUI_UARRCB_FILE "AltArrowUp.ogf" +#define NEWUI_UARRLITCB_FILE "AltArrowUpLit.ogf" +#define NEWUI_DARRCB_FILE "AltArrowDown.ogf" +#define NEWUI_DARRLITCB_FILE "AltArrowDownLit.ogf" +#define NEWUI_TINYBTN_FILE "TinyButton.ogf" +#define NEWUI_TINYBTNLIT_FILE "TinyButtonLit.ogf" + +#define NEWUI_MSGBOX_FILE "SmallScreen.ogf" +#define NEWUI_MEDWIN_FILE "MediumScreen.ogf" +#define NEWUI_LRGWIN_FILE "FullScreen.ogf" + +#define NEWUI_LB_N_FILE "lbTop32.ogf" +#define NEWUI_LB_NE_FILE "lbTopRight32.ogf" +#define NEWUI_LB_E_FILE "lbRight32.ogf" +#define NEWUI_LB_SE_FILE "lbBottomRight32.ogf" +#define NEWUI_LB_S_FILE "lbBottom32.ogf" +#define NEWUI_LB_SW_FILE "lbBottomLeft32.ogf" +#define NEWUI_LB_W_FILE "lbLeft32.ogf" +#define NEWUI_LB_NW_FILE "lbTopLeft32.ogf" + +#define NEWUI_CB_FILE "ComboBox.ogf" + +#define NEWUI_WIN_N_FILE "winTop32.ogf" +#define NEWUI_WIN_NE_FILE "winTopRight64.ogf" +#define NEWUI_WIN_E_FILE "winRight32.ogf" +#define NEWUI_WIN_SE_FILE "winBottomRight64.ogf" +#define NEWUI_WIN_S_FILE "winBottom32.ogf" +#define NEWUI_WIN_SW_FILE "winBottomLeft64.ogf" +#define NEWUI_WIN_W_FILE "winLeft32.ogf" +#define NEWUI_WIN_NW_FILE "winTopLeft64.ogf" +#define NEWUI_WIN_C_FILE "winCenter32.ogf" + +#define NEWUI_WIN_TITLE_L_FILE "winTitleLeft32.ogf" +#define NEWUI_WIN_TITLE_C_FILE "winTitleCenter32.ogf" +#define NEWUI_WIN_TITLE_R_FILE "winTitleRight32.ogf" + + +// used to position gadgets in a small screen +#define NEWUI_MSGBOX_BUTTONS_X 242 +#define NEWUI_MSGBOX_BUTTONS_Y 24 +#define NEWUI_MSGBOX_BUTTONS_H 22 +#define NEWUI_MSGBOX_BUTTONS_Y2 16 +#define NEWUI_MSGBOX_BUTTONS_H2 20 +#define NEWUI_MSGBOX_SHEET_X 22 +#define NEWUI_MSGBOX_SHEET_Y 30 + +// used to position gadgets in a medium screen +#define NEWUI_MEDWIN_OPTIONS_X 314 +#define NEWUI_MEDWIN_OPTIONS_Y 60 +#define NEWUI_MEDWIN_OPTIONS_W 140 +#define NEWUI_MEDWIN_OPTIONS_H 27 +#define NEWUI_MEDWIN_SHEET_X 28 +#define NEWUI_MEDWIN_SHEET_Y 40 +#define NEWUI_MEDWIN_TITLE_X 90 +#define NEWUI_MEDWIN_TITLE_Y 13 +#define NEWUI_MEDWIN_TITLE_W 125 + +// used to position gadgets in a large screen +#define NEWUI_LRGWIN_OPTIONS_X 35 +#define NEWUI_LRGWIN_OPTIONS_Y 415 +#define NEWUI_LRGWIN_OPTIONS_W 132 +#define NEWUI_LRGWIN_OPTIONS_H 27 +#define NEWUI_LRGWIN_SHEET_X 40 +#define NEWUI_LRGWIN_SHEET_Y 50 +#define NEWUI_LRGWIN_TITLE_X 40 +#define NEWUI_LRGWIN_TITLE_Y 30 + +// used to define tiled window dims +#define NEWUI_TILED_SHEET_X 40 +#define NEWUI_TILED_SHEET_Y 50 +#define NEWUI_TILED_TITLE_X 40 +#define NEWUI_TILED_TITLE_Y 30 + +// newuiListbox constants. +#define LB_PIECE_WIDTH 32 +#define LB_PIECE_HEIGHT 32 + +// newuiTiledWindow constants. +#define TW_PIECE_WIDTH 32 +#define TW_PIECE_HEIGHT 32 +#define TW_CORNER_WIDTH 64 +#define TW_CORNER_HEIGHT 64 + +// newuiEdit constants +#define EDIT_GADGET_WIDTH 192 +#define EDIT_GADGET_HEIGHT 12 +#define EDIT_BUFLEN_MAX 1025 //DAJ utb 512 + + +// CLASS newui resource manager + +class newuiResources +{ + struct t_bmp_list + { + union + { + int bm_handle; + chunked_bitmap chunk; + }; + short chunked; // is it a bitmap handle or chunk + short n_count; + char *filename; // filename of bitmap. + } + m_list[N_NEWUI_BMPS_TOTAL]; // list of bitmaps. + +public: + newuiResources(); + + bool Init(); // initializes list + void Shutdown(); // frees memory + +// loads bitmap and returns the object + UIBitmapItem *Load(const char *filename); + +// frees memory for bitmap object. + void Free(UIBitmapItem *bmitem); +}; + + + +// CLASS creates an arrow button that is sensitive to touch (when down, always select) + +// CLASS a new check box: note that newuiButton and UICheckBox will share the same UIButton base +// since bott newuiButton and UICheckBox inherit UIButton virtually. + +class newuiCheckBox: public newuiButton, public UICheckBox +{ +public: + newuiCheckBox(); + + void Create(UIWindow *wnd, short id, const char *name, short x, short y, bool is_long=false); + +protected: + virtual void OnDraw(); // this will use the newuiButton drawing scheme + virtual void OnDestroy() { newuiButton::OnDestroy(); }; + virtual void OnFormat() { newuiButton::OnFormat(); }; + virtual void OnKeyDown(int key) { UICheckBox::OnKeyDown(key); }; + virtual void OnKeyUp(int key) { UICheckBox::OnKeyUp(key); }; + virtual void OnMouseBtnDown(int btn) { UICheckBox::OnMouseBtnDown(btn); }; + virtual void OnMouseBtnUp(int btn) { UICheckBox::OnMouseBtnUp(btn); }; + virtual void OnLostFocus() { newuiButton::OnLostFocus(); }; + virtual void OnGainFocus() { newuiButton::OnGainFocus(); }; +}; + + +class newuiRadioButton: public newuiButton, public UIRadioButton +{ +public: + newuiRadioButton(); + + void Create(UIWindow *wnd, UIRadioButton *prev_rb, short id, const char *name, short x, short y, bool is_long=false); + +protected: + virtual void OnDraw(); // this will use the newuiButton drawing scheme + virtual void OnDestroy() { newuiButton::OnDestroy(); }; + virtual void OnFormat() { newuiButton::OnFormat(); }; + virtual void OnKeyDown(int key) { UIRadioButton::OnKeyDown(key); }; + virtual void OnKeyUp(int key) { UIRadioButton::OnKeyUp(key); }; + virtual void OnMouseBtnDown(int btn) { UIRadioButton::OnMouseBtnDown(btn); }; + virtual void OnMouseBtnUp(int btn) { UIRadioButton::OnMouseBtnUp(btn); }; + virtual void OnLostFocus() { newuiButton::OnLostFocus(); }; + virtual void OnGainFocus() { newuiButton::OnGainFocus(); }; +}; + + + +// CLASS a new slider. + +#define SUBGADGET_LEFT 0x1 +#define SUBGADGET_RIGHT 0x2 + +class newuiSlider: public UIGadget +{ + short m_pos; + short m_unitrange; + UIBitmapItem *m_bar_bmp; + UISnazzyTextItem *m_title; + newuiArrowButton m_minus_btn; + newuiArrowButton m_plus_btn; + tSliderSettings m_unit_settings; + +public: + newuiSlider(); + + void Create(UIWindow *wnd, short id, const char *name, short x, short y, short range); + void Offset(short offs); + void SetPos(short pos); + void SetRange(short range); + short GetPos() const { return m_pos; }; + short GetRange() const { return m_unitrange; }; + void SetUnits(tSliderSettings *settings); + +protected: + virtual void OnFormat(); // override: called when resized or before drawing. + virtual void OnLostFocus(); // override: behavior when gadget loses input focus. + virtual void OnGainFocus(); // override: behavior when gadget gains input focus. + virtual void OnDraw(); // behavior when gadget is being drawn. + virtual void OnDestroy(); // behavior when gadget is being destroyed. + virtual void OnNotifySelect(UIGadget *g); // this function will handle when an arrow button was pressed + virtual void OnKeyDown(int btn); // override: behavior when key pressed. + virtual void OnKeyUp(int btn); // override: behavior when key released. + virtual void OnAttachToWindow(); // when gadget is added to a window (AddGadget is called) + virtual void OnDetachFromWindow(); // when gadget is detached from window +}; + + + +// CLASS newuiEditBox + +class newuiEditBox: public UIEdit +{ +public: + newuiEditBox(); + + void Create(UIWindow *wnd, short id, const char *name, short x, short y, short w, short flags); + void EnableOnQuickEscapeBehavior(bool do_it); + +protected: + virtual void OnDraw(); + virtual void OnDestroy(); + virtual void OnKeyDown(int key); + +private: + UISnazzyTextItem *m_title; + bool m_quick_escape_enable; +}; + + +inline UISnazzyTextItem *MonitorSmallText(const char *text) +{ + return new UISnazzyTextItem(0,MONITOR9_NEWUI_FONT, text, NEWUI_MONITORFONT_COLOR); +} + +inline UISnazzyTextItem *MonitorLargeText(const char *text) +{ + return new UISnazzyTextItem(0,MONITOR15_NEWUI_FONT, text, NEWUI_MONITORFONT_COLOR); +} + +inline UISnazzyTextItem *GadgetSmallText(const char *text) +{ + return new UISnazzyTextItem(0,GADGET9_NEWUI_FONT, text, NEWUI_GADGETFONT_COLOR); +} + +inline UISnazzyTextItem *GadgetLargeText(const char *text) +{ + return new UISnazzyTextItem(0,GADGET15_NEWUI_FONT, text, NEWUI_GADGETFONT_COLOR); +} + + + +////////////////////////////////////////////////////////////////////////////// +// DATA +static const char *Preloaded_bitmap_list[] = { + NEWUI_LRGBTN_FILE, // common buttons + NEWUI_LRGBTNLIT_FILE, + NEWUI_LBTN_FILE, + NEWUI_LBTNLIT_FILE, + NEWUI_BTN_FILE, + NEWUI_BTNLIT_FILE, + NEWUI_LCHKBTN_FILE, + NEWUI_LCHKBTNLIT_FILE, + NEWUI_CHKBTN_FILE, + NEWUI_CHKBTNLIT_FILE, + NEWUI_TINYBTN_FILE, + NEWUI_TINYBTNLIT_FILE, + NEWUI_MSGBOX_FILE, // window frames + NEWUI_MEDWIN_FILE, + NEWUI_LRGWIN_FILE, + NEWUI_LB_N_FILE, // listbox frame + NEWUI_LB_NE_FILE, + NEWUI_LB_E_FILE, + NEWUI_LB_SE_FILE, + NEWUI_LB_S_FILE, + NEWUI_LB_SW_FILE, + NEWUI_LB_W_FILE, + NEWUI_LB_NW_FILE, + NEWUI_WIN_N_FILE, // tiled window frames + NEWUI_WIN_NE_FILE, + NEWUI_WIN_E_FILE, + NEWUI_WIN_SE_FILE, + NEWUI_WIN_S_FILE, + NEWUI_WIN_SW_FILE, + NEWUI_WIN_W_FILE, + NEWUI_WIN_NW_FILE, + NEWUI_WIN_C_FILE, + NEWUI_WIN_TITLE_L_FILE, + NEWUI_WIN_TITLE_C_FILE, + NEWUI_WIN_TITLE_R_FILE, +}; + +#define N_UI_PRELOADED_BITMAPS (sizeof(Preloaded_bitmap_list)/sizeof(const char*)) + +int UI_frame_result = -1; +static newuiResources Newui_resources; +static void (*UI_callback)() = NULL; +static UIBitmapItem *Preloaded_bitmaps[N_UI_PRELOADED_BITMAPS] = { NULL, }; + +#ifndef MULTI_H +extern bool Multi_bail_ui_menu; +#endif + +void SimpleUICallback(); + + + +////////////////////////////////////////////////////////////////////////////// +// initializes the core system for the newui + +void newuiCore_Init() +{ + Newui_resources.Init(); +} + + +// initializes the core system for the newui +void newuiCore_Close() +{ + Newui_resources.Shutdown(); +} + + +// C interface to load and free bitmap resources +UIBitmapItem *newui_LoadBitmap(const char *filename) +{ + return Newui_resources.Load(filename); +} + + +// C interface to load and free bitmap resources +void newui_FreeBitmap(UIBitmapItem *bmitem) +{ + Newui_resources.Free(bmitem); +} + + +// touches all newui bitmaps, do before any newui menus open +void newuiCore_PageInBitmaps() +{ + int i; + + if (Preloaded_bitmaps[0]) { + Int3(); // we've already paged in stuff! + return; + } + + for (i = 0; i < N_UI_PRELOADED_BITMAPS; i++) + { + Preloaded_bitmaps[i] = newui_LoadBitmap(Preloaded_bitmap_list[i]); + } +} + + +// call when NO newui menus open +void newuiCore_ReleaseBitmaps() +{ + int i; + + if (!Preloaded_bitmaps[0]) { + Int3(); // we've already freed paged in stuff! + return; + } + + for (i = 0; i < N_UI_PRELOADED_BITMAPS; i++) + { + newui_FreeBitmap(Preloaded_bitmaps[i]); + Preloaded_bitmaps[i] = NULL; + } +} + + +// does a UI loop +// + +int DoUI() +{ +// this should poll UI_frame_result. + UI_frame_result = -1; + ui_ShowCursor(); + ui_Flush(); + + DebugBlockPrint("UJ"); + + while (UI_frame_result == -1) + { + Descent->defer(); + DoUIFrame(); + rend_Flip(); + } + + DebugBlockPrint("UO"); + + ui_HideCursor(); + ui_Flush(); + + return UI_frame_result; +} + + +// does one frame of ui. +void DoUIFrame() +{ + if (Multi_bail_ui_menu) + { + UI_frame_result = NEWUIRES_FORCEQUIT; + } + else { + DebugBlockPrint("UK"); + if (UI_callback) + (*UI_callback)(); + DebugBlockPrint("UL"); + + if (GetFunctionMode() == MENU_MODE) { + tMusicSeqInfo music_info; + + Sound_system.BeginSoundFrame(false); + + music_info.frametime = UIFrameTime; + music_info.player_dead = false; + music_info.started_level = false; + D3MusicDoFrame(&music_info); + + Sound_system.EndSoundFrame(); + } + + DebugBlockPrint("UM"); + UI_frame_result = ui_DoFrame(); + DebugBlockPrint("UN"); + } + + if (UI_input.printscreen) { + UI_input.printscreen = false; + DoScreenshot(); + } +} + + +// does one frame of ui. +void DoUIFrameWithoutInput() +{ + if (Multi_bail_ui_menu) + { + UI_frame_result = NEWUIRES_FORCEQUIT; + } + else { + if (UI_callback) + (*UI_callback)(); + + if (GetFunctionMode() == MENU_MODE) { + tMusicSeqInfo music_info; + + Sound_system.BeginSoundFrame(false); + + music_info.frametime = UIFrameTime; + music_info.player_dead = false; + music_info.started_level = false; + D3MusicDoFrame(&music_info); + + Sound_system.EndSoundFrame(); + } + + UI_frame_result = ui_DoFrame(false); + } +} + + +int GetUIFrameResult() +{ + return UI_frame_result; +} + + +// sets the callback for background rendering of desktop for UI +void SetUICallback(void (*fn)()) +{ + if (fn == DEFAULT_UICALLBACK) + fn = SimpleUICallback; + UI_callback = fn; +} + + +void (*GetUICallback())() +{ + return UI_callback; +} + + +void SimpleUICallback() +{ + StartFrame(0,0,Max_window_w, Max_window_h); + rend_ClearScreen(GR_BLACK); + EndFrame(); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS newui resource manager + +newuiResources::newuiResources() +{ +} + + +bool newuiResources::Init() +{ + int i; + for (i = 0; i < N_NEWUI_BMPS_TOTAL; i++) + { + m_list[i].n_count = 0; + } + + return true; +} + + +void newuiResources::Shutdown() +{ + int i; + + for (i = 0; i < N_NEWUI_BMPS_TOTAL; i++) + { + if (m_list[i].n_count > 0) { + if (m_list[i].chunked) { + bm_DestroyChunkedBitmap(&m_list[i].chunk); + } + else { + bm_FreeBitmap(m_list[i].bm_handle); + } + } + m_list[i].n_count = 0; + } +} + + + +// loads bitmap and returns the object +UIBitmapItem *newuiResources::Load(const char *filename) +{ + UIBitmapItem *bmitem; + int i, free_slot = -1; + +// do search for bitmap. + for (i = 0; i < N_NEWUI_BMPS_TOTAL; i++) + { + if (m_list[i].n_count == 0) { + if (free_slot == -1) { + free_slot = i; + } + } + else { + if (strcmpi(filename, m_list[i].filename) == 0) { + break; + } + } + } + if (i == N_NEWUI_BMPS_TOTAL) { + // perform load. + int bm, chw, chh; + + if (free_slot == -1) { + // reached limit. + Int3(); + return NULL; + } + i = free_slot; + + bm = bm_AllocLoadFileBitmap(IGNORE_TABLE(filename), 0); + if (bm < 0) { + Error("NewUI: Failed to load bitmap %s", filename); + } + + chw = bm_w(bm, 0); + chh = bm_h(bm, 0); + + if ((chw==32 && chh==32) || (chw==64 && chh==64) || (chw==128 && chh==128)) { + m_list[i].bm_handle = bm; + m_list[i].chunked = 0; + } + else { + // chunk it. + bm_CreateChunkedBitmap(bm, &m_list[i].chunk); + bm_FreeBitmap(bm); + m_list[i].chunked = 1; + } + + m_list[i].n_count = 0; + m_list[i].filename = mem_strdup(filename); + } + + m_list[i].n_count++; + + if (m_list[i].chunked) { + bmitem = new UIBitmapItem(&m_list[i].chunk); + } + else { + bmitem = new UIBitmapItem(m_list[i].bm_handle); + } + + return bmitem; +} + + +// frees memory for bitmap object. +void newuiResources::Free(UIBitmapItem *bmitem) +{ + int i; + + if (!bmitem) { + Int3(); // Get samir. + return; + } + + for (i = 0; i < N_NEWUI_BMPS_TOTAL; i++) + { + if (m_list[i].n_count > 0) { + if (m_list[i].chunked && bmitem->is_chunked()) { + if (bmitem->get_chunked_bitmap()->bm_array == m_list[i].chunk.bm_array) { + break; + } + } + else if (!m_list[i].chunked && !bmitem->is_chunked()) { + if (bmitem->get_bitmap() == m_list[i].bm_handle) { + break; + } + } + } + } + + if (i >= N_NEWUI_BMPS_TOTAL) { + Int3(); // maybe we should free this, well this really should never happen but if it does + return; // I can't be completely sure this is a valid pointer, so rather than risking a crash I'll have a leak. + } + +// ASSERT(i < N_NEWUI_BMPS_TOTAL); + +// decrement and free. + delete bmitem; + + m_list[i].n_count--; + if (m_list[i].n_count == 0) { + if (m_list[i].chunked) { + bm_DestroyChunkedBitmap(&m_list[i].chunk); + } + else { + bm_FreeBitmap(m_list[i].bm_handle); + } + mem_free(m_list[i].filename); + } +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS a menu with options and sheets + +newuiMenu::newuiMenu() +{ + m_nsheets = 0; + m_bkg = 0; + m_cursheet = NULL; + m_newoptionid = -1; + m_align = NEWUI_ALIGN_VERT; +} + + +// creates a menu +void newuiMenu::Create() +{ + m_nsheets = 0; + + if (!m_bkg) { + m_bkg = Newui_resources.Load(NEWUI_MEDWIN_FILE); + } + + m_sheetx = NEWUI_MEDWIN_SHEET_X; + m_sheety = NEWUI_MEDWIN_SHEET_Y; + m_optionsx = NEWUI_MEDWIN_OPTIONS_X; + m_optionsy = NEWUI_MEDWIN_OPTIONS_Y; + m_optionsw = NEWUI_MEDWIN_OPTIONS_W; + m_optionsh = NEWUI_MEDWIN_OPTIONS_H; + m_titlex = NEWUI_MEDWIN_TITLE_X; + m_titley = NEWUI_MEDWIN_TITLE_Y; + m_align = NEWUI_ALIGN_VERT; + + m_uiframe_cb = GetUICallback(); + m_activate_sheet_cb = NULL; + m_option_focus_cb = NULL; + + UIWindow::Create((Max_window_w-m_bkg->width())/2,(Max_window_h-m_bkg->height())/2, m_bkg->width(), m_bkg->height()); + m_newoptionid = -1; + m_cursheetidx = -1; +} + + +// sets current option to display. +void newuiMenu::SetCurrentOption(short id) +{ + m_newoptionid = id; +} + + +// returns current option +short newuiMenu::GetCurrentOption() const +{ + if (m_cursheetidx != -1) { + return m_sheetbtn[m_cursheetidx].GetID(); + } + + return -1; +} + + +newuiSheet *newuiMenu::GetCurrentSheet() const +{ + return m_cursheet; +} + + +// when a new option is ready. at this point the system sets the focus on this option +void newuiMenu::SetOnOptionFocusCB(void (*fn)(newuiMenu *,short, void *), void *data) +{ + m_option_focus_cb = fn; + m_option_focus_cb_data = data; +} + + +// processes a menu +int newuiMenu::DoUI() +{ + int res, i; + + if (m_newoptionid != -1) { + m_cursheetidx = -1; + for (i = 0; i < m_nsheets; i++) + { + if (m_sheetbtn[i].GetID() == (int)m_newoptionid) { + if (m_cursheet) { + m_cursheet->Unrealize(); + m_cursheet = NULL; + } + + if (m_hassheet[i]) { + if (m_activate_sheet_cb) { + (*m_activate_sheet_cb)(this, m_sheetbtn[i].GetID(), m_newoptionid, m_activate_sheet_cb_data); + } + + m_cursheet = &m_sheets[i]; + m_cursheet->Realize(); + } + + m_cursheetidx = i; + + SetFocusOnGadget(&m_sheetbtn[i]); + + if (m_option_focus_cb) { + (*m_option_focus_cb)(this, m_sheetbtn[i].GetID(), m_option_focus_cb_data); + } + + break; + } + } + if (i == m_nsheets) { // error! + Int3(); + if (m_cursheet) { + m_cursheet->Unrealize(); + m_cursheet = NULL; + } + } + m_newoptionid = -1; + } + +// refreshes gadgets on current sheet. + if (m_cursheet) { + m_cursheet->UpdateChanges(); + } + +// do ui and check if a new option was selected + res = ::DoUI(); + + for (i = 0; i < m_nsheets; i++) + { + if (i != m_cursheetidx) { + if (res == m_sheetbtn[i].GetID() && m_hassheet[i]) { + m_newoptionid = (short)res; + break; + } + } + } + +// updates return values + if (m_cursheet) { + m_cursheet->UpdateReturnValues(); + } + + return res; +} + + +// when a new sheet is realized, this function will be called. +// passed in: the menu object, the old option and the new option respectively +void newuiMenu::SetOnOptionSwitchCB(void (*fn)(newuiMenu *, short, short, void *), void *data) +{ + m_activate_sheet_cb = fn; + m_activate_sheet_cb_data = data; +} + + +// equivalent of SetUICallback, called before gadgets are drawn +void newuiMenu::SetOnUIFrameCB(void (*fn)()) +{ +// m_uiframe_cb = GetUICallback(); +// SetUICallback(fn); +} + + +// adds an option to a menu, returns a newuiSheet object to add user interface to. +void newuiMenu::AddSimpleOption(short id, const char *title, int yoff) +{ + newuiMenu::AddOption(id, title, 0, false, yoff); +} + + +newuiSheet *newuiMenu::AddOption(short id, const char *title, int size, bool has_sheet, int yoff) +{ + newuiMenuOptionButton *last_btn = NULL; + + if (m_nsheets == N_NEWUI_SHEETS) { + Int3(); + return NULL; + } + + if (has_sheet) { + m_sheets[m_nsheets].Create(this, title, size, m_sheetx, m_sheety); + } + + if (m_nsheets > 0) { + last_btn = &m_sheetbtn[m_nsheets-1]; + } + + switch (m_align) + { + case NEWUI_ALIGN_VERT: + m_sheetbtn[m_nsheets].Create(this, last_btn, id, title, m_optionsx, m_optionsy + m_nsheets* m_optionsh + yoff, has_sheet); + break; + case NEWUI_ALIGN_HORIZ: + m_sheetbtn[m_nsheets].Create(this, last_btn, id, title, m_optionsx+m_nsheets*m_optionsw+yoff, m_optionsy, has_sheet); + break; + default: + Int3(); + } + + m_hassheet[m_nsheets] = has_sheet; + m_newoptionid = id; + m_nsheets++; + + if (!has_sheet) { + return NULL; + } + + return &m_sheets[m_nsheets-1]; +} + + +// set master sheet,button locations +void newuiMenu::SetPlacements(int x, int y, int options_x, int options_y, int options_w, int options_h, + int title_x, int title_y, tAlignment align) +{ + m_sheetx = x; + m_sheety = y; + m_optionsx = options_x; + m_optionsy = options_y; + m_optionsw = options_w; + m_optionsh = options_h; + m_titlex = title_x; + m_titley = title_y; + m_align = align; +} + + +// overridable draws the window background before gadgets +void newuiMenu::OnDraw() +{ +// draw background + m_bkg->draw(0,0); + +// draw title + if (m_cursheetidx != -1 && m_hassheet[m_cursheetidx]) { + UITextItem *title = m_sheetbtn[m_cursheetidx].GetTitle(); + UITextItem m_texttitle(MONITOR15_NEWUI_FONT, title->GetBuffer(), NEWUI_MONITORFONT_COLOR); + + m_texttitle.draw(m_titlex-8+(NEWUI_MEDWIN_TITLE_W - m_texttitle.width())/2, m_titley); + } + +// draws gadgets. + UIWindow::OnDraw(); +} + + +// overridable: called in Destroy +void newuiMenu::OnDestroy() +{ + int i; + + if (m_bkg) { + Newui_resources.Free(m_bkg); + m_bkg = NULL; + } + + for (i = 0; i < m_nsheets; i++) + { + if (m_hassheet[i]) { + m_sheets[i].Destroy(); + m_sheetbtn[i].Destroy(); + m_hassheet[i] = false; + } + } + + m_nsheets = 0; + + SetUICallback(m_uiframe_cb); + + UIWindow::OnDestroy(); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS, large (fullscreen) menu + +// creates a menu +void newuiLargeMenu::Create() +{ + m_bkg = Newui_resources.Load(NEWUI_LRGWIN_FILE); + newuiMenu::Create(); + newuiMenu::SetPlacements(NEWUI_LRGWIN_SHEET_X, NEWUI_LRGWIN_SHEET_Y, NEWUI_LRGWIN_OPTIONS_X, NEWUI_LRGWIN_OPTIONS_Y, + NEWUI_LRGWIN_OPTIONS_W, NEWUI_LRGWIN_OPTIONS_H, NEWUI_LRGWIN_TITLE_X, NEWUI_LRGWIN_TITLE_Y, + NEWUI_ALIGN_HORIZ); +} + + +// adds an option to a menu, returns a newuiSheet object to add user interface to. +newuiSheet *newuiLargeMenu::AddOption(short id, const char *title, int size) +{ + return newuiMenu::AddOption(id, title, size); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS, small message box +#define MBTN_OK 0 +#define MBTN_CANCEL 1 + +newuiMessageBox::newuiMessageBox() +{ + m_bkg = NULL; +} + + +void newuiMessageBox::Create(const char *title, int type) +{ + ASSERT(title); + m_bkg = Newui_resources.Load(NEWUI_MSGBOX_FILE); + + UIWindow::Create((Max_window_w-m_bkg->width())/2,(Max_window_h-m_bkg->height())/2, m_bkg->width(), m_bkg->height()); + + m_sheet.Create(this,title,NEWUIMENU_SMALL, NEWUI_MSGBOX_SHEET_X, NEWUI_MSGBOX_SHEET_Y); + + if (type == MSGBOX_OK) { + m_btn[MBTN_OK].Create(this, UID_OK, TXT_OK, NEWUI_MSGBOX_BUTTONS_X, NEWUI_MSGBOX_BUTTONS_Y, NEWUI_BTNF_FRAMED); + AddAcceleratorKey(KEY_ESC, UID_OK); + } + else if (type == MSGBOX_OKCANCEL) { + m_btn[MBTN_OK].Create(this, UID_OK, TXT_OK, NEWUI_MSGBOX_BUTTONS_X, NEWUI_MSGBOX_BUTTONS_Y, NEWUI_BTNF_FRAMED); + m_btn[MBTN_CANCEL].Create(this, UID_CANCEL, TXT_CANCEL, NEWUI_MSGBOX_BUTTONS_X, NEWUI_MSGBOX_BUTTONS_Y+NEWUI_MSGBOX_BUTTONS_H, NEWUI_BTNF_FRAMED); + } + else if (type == MSGBOX_YESNO) { + m_btn[MBTN_OK].Create(this, UID_OK, TXT_YES, NEWUI_MSGBOX_BUTTONS_X, NEWUI_MSGBOX_BUTTONS_Y, NEWUI_BTNF_FRAMED); + m_btn[MBTN_CANCEL].Create(this, UID_CANCEL, TXT_NO, NEWUI_MSGBOX_BUTTONS_X, NEWUI_MSGBOX_BUTTONS_Y+NEWUI_MSGBOX_BUTTONS_H, NEWUI_BTNF_FRAMED); + m_btn[MBTN_OK].SetHotkey(KEY_Y); + m_btn[MBTN_CANCEL].SetHotkey(KEY_N); + } +} + + +void newuiMessageBox::AddButton(const char *title, int slot, int key) +{ + ASSERT(slot >= 0 && slot < NEWUI_MSGBOX_MAXBTNS); + + if (m_btn[slot].IsCreated()) { + m_btn[slot].Destroy(); + } + + m_btn[slot].Create( this, slot, title,NEWUI_MSGBOX_BUTTONS_X, NEWUI_MSGBOX_BUTTONS_Y2+NEWUI_MSGBOX_BUTTONS_H2*slot, NEWUI_BTNF_FRAMED); + if (key) { + m_btn[slot].SetHotkey(key); + } +} + + +// grab the message box's sheet. +newuiSheet *newuiMessageBox::GetSheet() +{ + return &m_sheet; +} + + +// do user interface, return UID_OK or UID_CANCEL or UID_??? (UID_OK and UID_CANCEL are predefined) +int newuiMessageBox::DoUI() +{ + int res; + m_sheet.Realize(); + res = ::DoUI(); + m_sheet.Unrealize(); + + return res; +} + + +void newuiMessageBox::OnDestroy() +{ + int i; + + if (m_bkg) { + Newui_resources.Free(m_bkg); + m_bkg = NULL; + } + + m_sheet.Destroy(); + + for (i=0;i < NEWUI_MSGBOX_MAXBTNS; i++) + { + if (m_btn[i].IsCreated()) { + m_btn[i].Destroy(); + } + } + + UIWindow::OnDestroy(); +} + + +// overridable draws the window background before gadgets +void newuiMessageBox::OnDraw() +{ +// draw background + m_bkg->draw(0,0); + + UITextItem title(MONITOR9_NEWUI_FONT, m_sheet.GetTitle(), NEWUI_MONITORFONT_COLOR); + title.draw(NEWUI_MSGBOX_SHEET_X, NEWUI_MSGBOX_SHEET_Y-16); + +// draws gadgets. + UIWindow::OnDraw(); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS contains gadgets + +// constants used in newuiSheet for gadget types. hidden from user. + +#define GADGET_GROUP 0 // repositions column, row alignment +#define GADGET_HGROUP 1 +#define GADGET_STATIC_TXT 2 // static text +#define GADGET_BUTTON 3 // small mono state button +#define GADGET_CHECKBOX 4 // small two state button +#define GADGET_RADIO 5 // small two state radio +#define GADGET_LBUTTON 6 // large equivalents +#define GADGET_LCHECKBOX 7 +#define GADGET_LRADIO 8 +#define GADGET_SLIDER 9 // slider +#define GADGET_EDITBOX 10 // edit control +#define GADGET_EDITBOXPASS 11 // edit control +#define GADGET_EDITBOXNUM 12 // edit control +#define GADGET_LISTBOX 13 // listbox +#define GADGET_STATIC_BMP 14 // static bitmap +#define GADGET_COMBOBOX 15 // combo box +#define GADGET_HOTSPOT 16 // hotspot +#define GADGET_CHANGEABLE_TXT 17 // changeable text + +newuiSheet::newuiSheet() +{ + m_gadgetlist = NULL; + m_gadgetlimit = 0; + m_ngadgets = 0; + m_realized = false; +} + + +void newuiSheet::Create(UIWindow *menu, const char *title, int n_items, int sx, int sy) +{ + int i; + + if (title) { + m_title = mem_strdup(title); + } + else { + m_title = NULL; + } + m_gadgetlimit = n_items; + m_ngadgets = 0; + if (n_items) { + m_gadgetlist = (newuiSheet::t_gadget_desc *)mem_malloc(n_items * sizeof(newuiSheet::t_gadget_desc)); + } + else { + m_gadgetlist = NULL; + } + + m_parent = menu; + m_sx = sx; + m_sy = sy; + m_realized = false; + m_initial_focus_id = -1; + + for (i = 0; i < m_gadgetlimit; i++) + { + m_gadgetlist[i].obj.gadget = NULL; + m_gadgetlist[i].title = NULL; + m_gadgetlist[i].id = -1; + m_gadgetlist[i].internal = NULL; + } +} + + +// deallocates memory +void newuiSheet::Destroy() +{ + Unrealize(); + + newuiSheet::Reset(); + + if (m_gadgetlist) mem_free(m_gadgetlist); + if (m_title) mem_free(m_title); + m_title= NULL; + m_gadgetlist = NULL; + m_ngadgets = 0; +} + + +void newuiSheet::Reset() +{ + int i; + + ASSERT(!m_realized); + + for (i = 0; i < m_ngadgets; i++) + { + if (m_gadgetlist[i].title) { + mem_free(m_gadgetlist[i].title); + } + if (m_gadgetlist[i].internal) { + if (m_gadgetlist[i].type == GADGET_LISTBOX) { + newuiListBox *lb = (newuiListBox *)m_gadgetlist[i].internal; + lb->Destroy(); + delete lb; + } + else if (m_gadgetlist[i].type == GADGET_SLIDER) { + mem_free(m_gadgetlist[i].internal); + } + else if (m_gadgetlist[i].type == GADGET_EDITBOX || m_gadgetlist[i].type == GADGET_EDITBOXNUM || m_gadgetlist[i].type == GADGET_EDITBOXPASS) { + mem_free(m_gadgetlist[i].internal); + } + else if (m_gadgetlist[i].type == GADGET_COMBOBOX) { + newuiComboBox *cb = (newuiComboBox *)m_gadgetlist[i].internal; + cb->Destroy(); + delete cb; + } + else if (m_gadgetlist[i].type == GADGET_CHANGEABLE_TXT) { + mem_free(m_gadgetlist[i].parm.p); + } + else if (m_gadgetlist[i].type != GADGET_HOTSPOT) { + Int3(); // get samir. + } + } + + } + + m_ngadgets = 0; + m_initial_focus_id =-1; +} + + +// set focus on this gadget specified by id upon realization. +void newuiSheet::SetInitialFocusedGadget(short id) +{ + m_initial_focus_id = id; +} + + +// call this to initialize gadgets specified above in parent window +void newuiSheet::Realize() +{ + int i, first_radio_index=-1, last_toggle_index = -1, gx=m_sx, gy=m_sy; + newuiRadioButton *prev_radio = NULL; + bool horizontal_align = false, hotspot_group=false, toggles_group=false; + newuiEditBox *focus_edit =NULL; + UIGadget *focus_gadget = NULL; + + if (m_realized) return; + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + newuiButton *btn; + newuiCheckBox *cbox; + newuiRadioButton *radio; + newuiSlider *slider; + newuiListBox *lb; + newuiEditBox *edit; + newuiComboBox *cb; + newuiHotspot *hot; + UIText *text; + UIStatic *bmp; + short flags; + bool bval; + + switch (desc->type) + { + case GADGET_HGROUP: + case GADGET_GROUP: + horizontal_align = (desc->type == GADGET_GROUP) ? false : true; + gx = desc->parm.s[0]+m_sx; + gy = desc->parm.s[1]+m_sy; + if (desc->title) { + text = new UIText; + text->Create(m_parent, &UITextItem(MONITOR9_NEWUI_FONT, desc->title, NEWUI_MONITORFONT_COLOR), gx, gy); + desc->obj.gadget = text; + + // determine pixel offset to first control if we stuffed the offset into desc->id (-1 = default) + if (desc->id != -1) { + gy += text->H()+desc->id; + } + else { + gy += text->H(); + } + } + else { + desc->obj.gadget = NULL; + } + + if (toggles_group && last_toggle_index != -1) { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[last_toggle_index]; + desc->obj.chbox->SetFlag(UIF_GROUP_END); + } + + prev_radio = NULL; + hotspot_group = false; + toggles_group = false; + first_radio_index = -1; + last_toggle_index = -1; + break; + + case GADGET_STATIC_TXT: + text = new UIText; + text->Create(m_parent, &UITextItem(MONITOR9_NEWUI_FONT, desc->title, NEWUI_MONITORFONT_COLOR),gx,gy); + if (horizontal_align) gx+= text->W()+2; + else gy+= text->H(); + desc->obj.gadget = text; + break; + + case GADGET_CHANGEABLE_TXT: + text = new UIText; + text->Create(m_parent, &UITextItem(MONITOR9_NEWUI_FONT, (const char *)desc->parm.p, NEWUI_MONITORFONT_COLOR), gx, gy); + if (horizontal_align) gx+= text->W()+2; + else gy+= text->H(); + desc->obj.text = text; + break; + + case GADGET_STATIC_BMP: + bmp = new UIStatic; + bmp->Create(m_parent, &UIBitmapItem(desc->parm.i), gx,gy,10,10); + if (horizontal_align) gx+= bmp->W()+2; + else gy+= bmp->H(); + desc->obj.gadget = bmp; + break; + + case GADGET_BUTTON: + btn = new newuiButton; + btn->Create(m_parent, desc->id, desc->title, gx, gy, desc->parm.i); + if (horizontal_align) gx+= btn->W()+2; + else gy += btn->H(); + desc->obj.button = btn; + break; + + case GADGET_CHECKBOX: + cbox = new newuiCheckBox; + cbox->Create(m_parent, desc->id, desc->title, gx, gy, false); + if (!toggles_group) { + toggles_group = true; + cbox->SetFlag(UIF_GROUP_START); + } + last_toggle_index = i; + if (desc->parm.b) cbox->SetCheck(true); + if (horizontal_align) gx+= cbox->W()+2; + else gy += cbox->H(); + desc->obj.chbox = cbox; + break; + + case GADGET_RADIO: + if (first_radio_index == -1) first_radio_index = i; + radio = new newuiRadioButton; + radio->Create(m_parent, prev_radio, desc->id, desc->title, gx, gy, false); + if (m_gadgetlist[first_radio_index].parm.i == (i-first_radio_index)) { + radio->Activate(); + } + if (horizontal_align) gx+= radio->W()+2; + else gy += radio->H(); + desc->obj.radio = radio; + prev_radio = radio; + break; + + case GADGET_LBUTTON: + btn = new newuiButton; + btn->Create(m_parent, desc->id, desc->title, gx, gy, desc->parm.i | NEWUI_BTNF_LONG); + if (horizontal_align) gx+= btn->W()+2; + else gy += btn->H(); + desc->obj.button = btn; + break; + + case GADGET_LCHECKBOX: + cbox = new newuiCheckBox; + cbox->Create(m_parent, desc->id, desc->title, gx, gy, true); + if (!toggles_group) { + toggles_group = true; + cbox->SetFlag(UIF_GROUP_START); + } + last_toggle_index = i; + if (desc->parm.b) cbox->SetCheck(true); + if (horizontal_align) gx+= cbox->W()+2; + else gy += cbox->H(); + desc->obj.chbox = cbox; + break; + + case GADGET_LRADIO: + if (first_radio_index == -1) first_radio_index = i; + radio = new newuiRadioButton; + radio->Create(m_parent, prev_radio, desc->id, desc->title, gx, gy, true); + if (m_gadgetlist[first_radio_index].parm.i == (i-first_radio_index)) { + radio->Activate(); + } + if (horizontal_align) gx+= radio->W()+2; + else gy += radio->H(); + desc->obj.radio = radio; + prev_radio = radio; + break; + + case GADGET_HOTSPOT: + hot = new newuiHotspot; + bval = desc->internal ? true : false; + hot->Create(m_parent, desc->id, desc->title, gx, gy, desc->parm.s[0], desc->parm.s[1], + (bval && !hotspot_group) ? UIF_GROUP_START : (bval && hotspot_group) ? UIF_GROUP_END : 0); + + if (bval && !hotspot_group) { + hotspot_group = true; + } + else if (bval && hotspot_group) { + hotspot_group = false; + } + + if (horizontal_align) gx += hot->W()+2; + else gy += hot->H(); + desc->obj.hot = hot; + break; + + case GADGET_SLIDER: + slider = new newuiSlider; + slider->Create(m_parent, desc->id, desc->title, gx, gy, desc->parm.s[1]); + slider->SetPos(desc->parm.s[0]); + if (desc->internal) { + slider->SetUnits((tSliderSettings *)desc->internal); + } + if (horizontal_align) gx += slider->W()+4; + else gy += slider->H() + 2; + desc->obj.slider = slider; + break; + + case GADGET_EDITBOX: + case GADGET_EDITBOXPASS: + case GADGET_EDITBOXNUM: + ASSERT(desc->internal); + edit = new newuiEditBox; + flags = (desc->type == GADGET_EDITBOXPASS) ? UIED_PASSWORD : (desc->type == GADGET_EDITBOXNUM) ? UIED_NUMBERS : 0; + + // awful hack, but no more room to store passed in parameters. note that we should never have an edit + // box with a width greater than 4096 pixels in the lifetime of this system. + edit->Create(m_parent, desc->id, desc->title, gx,gy, (desc->parm.s[0] & 0xfff), flags); + edit->SetBufferLen(desc->parm.s[1]); + edit->SetText((char *)desc->internal); + + if (desc->parm.s[0] & 0x1000) { + edit->EnableOnQuickEscapeBehavior(true); + } + + if (!focus_edit) { + focus_edit = edit; + } + + if (horizontal_align) gx += edit->W()+4; + else gy += edit->H() + 2; + desc->obj.edit = edit; + break; + + case GADGET_LISTBOX: + lb = (newuiListBox *)desc->internal; + ASSERT(lb); + lb->Move(gx, gy, lb->W(), lb->H()); + m_parent->AddGadget(lb); + if (horizontal_align) gx += lb->W()+4; + else gy += lb->H() + 2; + desc->obj.lb = lb; + break; + + case GADGET_COMBOBOX: + cb = (newuiComboBox *)desc->internal; + ASSERT(cb); + cb->Move(gx, gy, cb->W(), cb->H()); + m_parent->AddGadget(cb); + if (horizontal_align) gx += cb->W()+4; + else gy += cb->H() + 2; + desc->obj.cb = cb; + break; + + default: + Int3(); + } + desc->changed = false; + + if (desc->obj.gadget) { + if (desc->obj.gadget->GetID() > -1 && desc->obj.gadget->GetID() == m_initial_focus_id) { + focus_gadget = desc->obj.gadget; + } + } + } + + if (toggles_group) { + if (last_toggle_index != -1) { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[last_toggle_index]; + desc->obj.chbox->SetFlag(UIF_GROUP_END); + } + toggles_group = false; + } + + if (focus_gadget) { + m_parent->SetFocusOnGadget(focus_gadget); + } + else if (focus_edit) { + m_parent->SetFocusOnGadget(focus_edit); + focus_edit->Activate(); + } + + m_realized = true; +} + + +// call this to release gadgets specified above in parent window +void newuiSheet::Unrealize() +{ + int i, first_radio_index=-1; + + if (!m_realized) return; + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + + if (desc->obj.gadget) { + switch (desc->type) + { + case GADGET_HGROUP: + case GADGET_GROUP: + first_radio_index = -1; + if (desc->title && desc->obj.gadget) { + desc->obj.gadget->Destroy(); + delete desc->obj.gadget; + } + break; + case GADGET_LCHECKBOX: + case GADGET_CHECKBOX: + desc->parm.b = desc->obj.chbox->IsChecked(); + desc->obj.chbox->Destroy(); + delete desc->obj.chbox; + break; + case GADGET_RADIO: + case GADGET_LRADIO: + if (first_radio_index == -1) first_radio_index = i; + if (desc->obj.radio->IsActivated()) { + m_gadgetlist[first_radio_index].parm.i = i - first_radio_index; + } + desc->obj.radio->Destroy(); + delete desc->obj.radio; + break; + case GADGET_SLIDER: + desc->parm.s[0] = desc->obj.slider->GetPos(); + desc->obj.slider->Destroy(); + delete desc->obj.slider; + break; + + case GADGET_HOTSPOT: + desc->obj.hot->Destroy(); + delete desc->obj.hot; + break; + + case GADGET_BUTTON: + case GADGET_LBUTTON: + desc->obj.button->Destroy(); + delete desc->obj.button; + break; + + case GADGET_LISTBOX: + m_parent->RemoveGadget(desc->obj.lb); + break; + + case GADGET_COMBOBOX: + m_parent->RemoveGadget(desc->obj.cb); + break; + + case GADGET_EDITBOX: + case GADGET_EDITBOXPASS: + case GADGET_EDITBOXNUM: + desc->obj.edit->GetText((char *)desc->internal, (desc->parm.s[1])+1); + desc->obj.edit->Destroy(); + delete desc->obj.edit; + break; + + case GADGET_CHANGEABLE_TXT: + desc->obj.text->Destroy(); + delete desc->obj.gadget; + break; + + default: + desc->obj.gadget->Destroy(); + delete desc->obj.gadget; + } + + desc->obj.gadget = NULL; + } + } + + m_realized = false; +} + + +// refreshes gadget states with values passed to the pointers returned by the below functions. +void newuiSheet::UpdateChanges() +{ + int i, first_radio_index; + + ASSERT(m_realized); + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + bool bval; + short sval; + char sbuftest[EDIT_BUFLEN_MAX]; + + switch (desc->type) + { + case GADGET_HGROUP: + case GADGET_GROUP: + first_radio_index = -1; + break; + + case GADGET_LCHECKBOX: + case GADGET_CHECKBOX: + bval = desc->obj.chbox->IsChecked(); + if (desc->parm.b != bval) { + desc->obj.chbox->SetCheck(desc->parm.b); + } + break; + + case GADGET_LRADIO: + case GADGET_RADIO: + // check if new index is same as current index. + if (first_radio_index == -1) first_radio_index = i; + if (m_gadgetlist[first_radio_index].parm.i == (i-first_radio_index)) { + // possible request for change of radio button state here. + bval = desc->obj.radio->IsActivated(); + if (!bval) { + // wasn't active before so lets activate it now. + desc->obj.radio->Activate(); + } + } + break; + + case GADGET_SLIDER: + sval = desc->obj.slider->GetPos(); + if (desc->parm.s[0] != sval) { + desc->obj.slider->SetPos(desc->parm.s[0]); + desc->parm.s[0] = desc->obj.slider->GetPos(); + } + break; + + case GADGET_EDITBOX: + case GADGET_EDITBOXNUM: + case GADGET_EDITBOXPASS: + desc->obj.edit->GetText(sbuftest, EDIT_BUFLEN_MAX); + if (strcmp(sbuftest, (char *)desc->internal) != 0) { + desc->obj.edit->SetText((char *)desc->internal); + } + break; + + case GADGET_CHANGEABLE_TXT: + desc->obj.text->SetTitle(&UITextItem(MONITOR9_NEWUI_FONT, (const char *)desc->parm.p, NEWUI_MONITORFONT_COLOR)); + break; + } + desc->changed = false; + } +} + + +// refreshes return values of gadgets, so they are accessible by the pointers returned by below functions +void newuiSheet::UpdateReturnValues() +{ + int i, first_radio_index=-1; + + ASSERT(m_realized); + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + bool bval; + short sval; + char sbuftest[EDIT_BUFLEN_MAX]; + + if (desc->obj.gadget) { + switch (desc->type) + { + case GADGET_HGROUP: + case GADGET_GROUP: + first_radio_index = -1; + break; + case GADGET_LCHECKBOX: + case GADGET_CHECKBOX: + bval = desc->obj.chbox->IsChecked(); + if (desc->parm.b != bval) { + desc->parm.b = bval; + desc->changed = true; + } + break; + case GADGET_RADIO: + case GADGET_LRADIO: + if (first_radio_index == -1) first_radio_index = i; + bval = desc->obj.radio->IsActivated(); + if (bval && m_gadgetlist[first_radio_index].parm.i != (i-first_radio_index)) { + m_gadgetlist[first_radio_index].parm.i = (i-first_radio_index); + m_gadgetlist[first_radio_index].changed = true; + } + break; + case GADGET_SLIDER: + sval = desc->obj.slider->GetPos(); + if (desc->parm.s[0] != sval) { + desc->parm.s[0] = sval; + desc->changed = true; + } + break; + + case GADGET_EDITBOX: + case GADGET_EDITBOXNUM: + case GADGET_EDITBOXPASS: + desc->obj.edit->GetText(sbuftest, EDIT_BUFLEN_MAX); + if (strcmp(sbuftest, (char *)desc->internal) != 0) { + strcpy((char *)desc->internal, sbuftest); + desc->changed = true; + } + break; + + default: + desc->changed = false; + } + } + } +} + + +// call these functions to determine if a pointer's value has changed after call to UpdateReturnValues (internal) +bool newuiSheet::HasChanged(bool *bptr) +{ + int i; + + ASSERT(m_realized); + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + if (desc->obj.gadget) { + switch (desc->type) + { + case GADGET_LCHECKBOX: + case GADGET_CHECKBOX: + if ((&desc->parm.b) == bptr) { + return desc->changed; + } + break; + } + } + } + + return false; +} + + +bool newuiSheet::HasChanged(int *iptr) +{ + int i; + + ASSERT(m_realized); + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + if (desc->obj.gadget) { + switch (desc->type) + { + case GADGET_RADIO: + case GADGET_LRADIO: + if ((&desc->parm.i) == iptr) { + return desc->changed; + } + break; + } + } + } + + return false; +} + + +bool newuiSheet::HasChanged(short *sptr) +{ + int i; + + ASSERT(m_realized); + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + if (desc->obj.gadget) { + switch (desc->type) + { + case GADGET_SLIDER: + if ((&desc->parm.s[0]) == sptr) { + return desc->changed; + } + break; + } + } + } + + return false; +} + + +bool newuiSheet::HasChanged(char *cptr) +{ + int i; + + ASSERT(m_realized); + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + if (desc->obj.gadget) { + switch (desc->type) + { + case GADGET_EDITBOX: + case GADGET_EDITBOXNUM: + case GADGET_EDITBOXPASS: + if (desc->internal == cptr) { + return desc->changed; + } + break; + } + } + } + + return false; +} + + +UIGadget *newuiSheet::GetGadget(short id) +{ + int i; + + for (i = 0; i < m_ngadgets; i++) + { + newuiSheet::t_gadget_desc *desc = &m_gadgetlist[i]; + + if (desc->obj.gadget && id == (short)desc->obj.gadget->GetID()) { + switch (desc->type) + { + case GADGET_STATIC_TXT: return desc->obj.gadget; + case GADGET_STATIC_BMP: return desc->obj.gadget; + case GADGET_BUTTON: return desc->obj.button; + case GADGET_HOTSPOT: return desc->obj.hot; + case GADGET_CHECKBOX: return desc->obj.chbox; + case GADGET_RADIO: return desc->obj.radio; + case GADGET_LBUTTON: return desc->obj.button; + case GADGET_LCHECKBOX: return desc->obj.chbox; + case GADGET_LRADIO: return desc->obj.radio; + case GADGET_SLIDER: return desc->obj.slider; + case GADGET_EDITBOX: + case GADGET_EDITBOXPASS: + case GADGET_EDITBOXNUM: return desc->obj.edit; + case GADGET_LISTBOX: return desc->obj.lb; + case GADGET_COMBOBOX: return desc->obj.cb; + case GADGET_CHANGEABLE_TXT: return desc->obj.text; + + default: + return desc->obj.gadget; + } + } + } + + Int3(); + return NULL; +} + + +newuiSheet::t_gadget_desc *newuiSheet::AddGadget(short id, sbyte type, const char *title) +{ + int i = m_ngadgets; + + ASSERT(!m_realized); + ASSERT(m_gadgetlimit > m_ngadgets); + if (m_gadgetlimit <= m_ngadgets) { + Error("Internal error in newuiSheet::AddGadget (ngadgets=%d, id=%d, type=%d, title=%s)\n", m_ngadgets, (int)id, (int)type, title ? title : "NULL"); + return NULL; + } + + m_gadgetlist[i].id = id; + m_gadgetlist[i].type = type; + m_gadgetlist[i].title = title ? mem_strdup(title) : NULL; + m_gadgetlist[i].obj.gadget = NULL; + m_gadgetlist[i].parm.i = 0; + m_gadgetlist[i].changed = false; + m_gadgetlist[i].internal = NULL; + + m_ngadgets++; + + return &m_gadgetlist[i]; +} + + +// creates gadget list. +void newuiSheet::NewGroup(const char *title, short x, short y, tAlignment align, short pix_offset) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(-1,(align==NEWUI_ALIGN_HORIZ) ? GADGET_HGROUP : GADGET_GROUP, title); + gadget->parm.s[0] = x; + gadget->parm.s[1] = y; + gadget->id = pix_offset; +} + + +// adds standard button to current group. +void newuiSheet::AddButton(const char *title, short id, short flags) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_BUTTON, title); + gadget->parm.i = flags; +} + + +// adds standard long button to current group. +void newuiSheet::AddLongButton(const char *title, short id, short flags) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_LBUTTON, title); + gadget->parm.i = flags; +} + + +// adds checkbox to current group. initial state of checkbox can be set. +bool *newuiSheet::AddCheckBox(const char *title, bool init_state, short id) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_CHECKBOX, title); + gadget->parm.b = init_state; + return &gadget->parm.b; +} + + +// adds checkbox to current group. initial state of checkbox can be set. +bool *newuiSheet::AddLongCheckBox(const char *title, bool init_state, short id) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_LCHECKBOX, title); + gadget->parm.b = init_state; + return &gadget->parm.b; +} + + +// adds a radio button to current group. initial state of radio may be set +int *newuiSheet::AddFirstRadioButton(const char *title, short id) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_RADIO, title); + gadget->parm.i = 0; + return &gadget->parm.i; +} + + +// adds a radio button to current group. initial state of radio may be set +void newuiSheet::AddRadioButton(const char *title, short id) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_RADIO, title); + gadget->parm.i = -1; +} + + +// adds a radio button to current group. initial state of radio may be set +int *newuiSheet::AddFirstLongRadioButton(const char *title, short id) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_LRADIO, title); + gadget->parm.i = 0; + return &gadget->parm.i; +} + + +// adds a radio button to current group. initial state of radio may be set +void newuiSheet::AddLongRadioButton(const char *title, short id) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_LRADIO, title); + gadget->parm.i = -1; +} + + +// adds a slider, set the range for it too. +short *newuiSheet::AddSlider(const char *title, short range, short init_pos, tSliderSettings *settings, short id) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_SLIDER, title); + gadget->parm.s[0] = init_pos; + gadget->parm.s[1] = range; + + if (settings) { + gadget->internal = mem_malloc(sizeof(tSliderSettings)); + memcpy(gadget->internal, settings, sizeof(tSliderSettings)); + } + else { + gadget->internal = NULL; + } + + return &gadget->parm.s[0]; +} + + +// adds a static text item +void newuiSheet::AddText(const char *text, ...) +{ + va_list arglist; + char buf[512]; + int len; + + va_start(arglist,text); + len = Pvsprintf(buf,512,text,arglist); + va_end(arglist); + + newuiSheet::t_gadget_desc *gadget = AddGadget(-1, GADGET_STATIC_TXT, buf); + + gadget->parm.b = false; // means small text. +} + + +// adds a static bitmap +void newuiSheet::AddBitmap(int bm_handle) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(-1, GADGET_STATIC_BMP, NULL); + gadget->parm.i = bm_handle; +} + + +// adds a static text item +char *newuiSheet::AddChangeableText(int buflen) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(-1, GADGET_CHANGEABLE_TXT, NULL); + gadget->internal = (void *)buflen; + gadget->parm.p = mem_malloc(buflen); + return (char *)gadget->parm.p; +} + + +// adds a hotspot :(, should word wrap too. +void newuiSheet::AddHotspot(const char *title, short w, short h, short id, bool group) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_HOTSPOT, title); + gadget->parm.s[0] = w; + gadget->parm.s[1] = h; + gadget->internal = (void *)(group); +} + + +// adds a listbox +newuiListBox *newuiSheet::AddListBox(short w, short h, short id, ushort flags) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_LISTBOX, NULL); + newuiListBox *lb = new newuiListBox; + lb->Create(m_parent, id, 0, 0, w, h, flags); + m_parent->RemoveGadget(lb); + gadget->internal = lb; + return lb; +} + + +// adds a listbox +newuiComboBox *newuiSheet::AddComboBox(short id, ushort flags) +{ + newuiSheet::t_gadget_desc *gadget = AddGadget(id, GADGET_COMBOBOX, NULL); + newuiComboBox *cb = new newuiComboBox; + cb->Create(m_parent, id, 0, 0, flags); + m_parent->RemoveGadget(cb); + gadget->internal = cb; + return cb; +} + + +// adds an edit box +char *newuiSheet::AddEditBox(const char *title, short maxlen, short w, short id, short flags, bool on_escape_quit) +{ + sbyte type=GADGET_EDITBOX; + if (flags & UIED_PASSWORD) { + type = GADGET_EDITBOXPASS; + } + else if (flags & UIED_NUMBERS) { + type = GADGET_EDITBOXNUM; + } + + newuiSheet::t_gadget_desc *gadget = AddGadget(id, type, title); + + ASSERT(maxlen < EDIT_BUFLEN_MAX); + ASSERT(w <= 0xfff); + + char *strbuf = (char *)mem_malloc(maxlen+1); + strbuf[0] = 0; + gadget->internal = strbuf; + gadget->parm.s[0] = (w & 0xfff); + gadget->parm.s[1] = maxlen; + +// awful hack to store extra info into gadget structure. width should never exceed 4096 + if (on_escape_quit) { + gadget->parm.s[0] |= (0x1000); + } + + return (char *)gadget->internal; +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS simple buttons + +newuiButton::newuiButton() +{ + m_bkg = NULL; + m_litbkg = NULL; + m_text = NULL; +} + + +void newuiButton::Create(UIWindow *menu, short id, const char *name, short x, short y, short flags) +{ + UIButton::Create(menu, id, &UITextItem(""), x, y, 10,8, flags | UIF_FIT); + + if (flags & NEWUI_BTNF_FRAMED) { + ASSERT(!(flags & NEWUI_BTNF_LONG)); // remove this if art is added. + if (!m_bkg) { + m_bkg = Newui_resources.Load((flags & NEWUI_BTNF_LONG) ? NEWUI_LBTNFRAMED_FILE : NEWUI_BTNFRAMED_FILE); + } + if (!m_litbkg) { + m_litbkg = Newui_resources.Load((flags & NEWUI_BTNF_LONG) ? NEWUI_LBTNLITFRAMED_FILE : NEWUI_BTNLITFRAMED_FILE); + } + } + + newuiButton::InitStates(name, (flags&NEWUI_BTNF_LONG)!=0); +} + + +void newuiTinyButton::Create(UIWindow *wnd, short id, const char *name, short x, short y) +{ + m_bkg = Newui_resources.Load(NEWUI_TINYBTN_FILE); + m_litbkg = Newui_resources.Load(NEWUI_TINYBTNLIT_FILE); + newuiButton::Create(wnd, id, name, x, y); +} + + +// override: called when resized or before drawing. +void newuiButton::OnFormat() +{ + m_W = GetStateItem(m_State)->width(); + m_H = GetStateItem(m_State)->height(); + + UIGadget::OnFormat(); +} + + +void newuiButton::InitStates(const char *name, bool is_long, short flags) +{ + if (!m_bkg) { + m_bkg = Newui_resources.Load(is_long ? NEWUI_LBTN_FILE : NEWUI_BTN_FILE); + } + + if (!m_litbkg) { + m_litbkg = Newui_resources.Load(is_long ? NEWUI_LBTNLIT_FILE : NEWUI_BTNLIT_FILE); + } + + if (name && !m_text) { + m_text = GadgetSmallText(name); + } + + SetStateItem(UI_BTS_DISABLED, m_bkg); + SetStateItem(UI_BTS_INACTIVE, m_bkg); + SetStateItem(UI_BTS_HILITE, m_bkg); + SetStateItem(UI_BTS_ACTIVATED, m_litbkg); + + OnFormat(); +} + + +void newuiButton::OnDestroy() +{ + if (m_bkg) { + Newui_resources.Free(m_bkg); + m_bkg = NULL; + } + if (m_litbkg) { + Newui_resources.Free(m_litbkg); + m_litbkg = NULL; + } + if (m_text) { + delete m_text; + m_text = NULL; + } + + UIButton::OnDestroy(); +} + + +void newuiButton::OnDraw() +{ + ubyte alpha = IsDisabled() ? 128 : 255; + + if (GetStateItem(m_State)) { + GetStateItem(m_State)->set_alpha(alpha); + GetStateItem(m_State)->draw(0, 0); + } + + if (m_text) { + m_text->set_alpha(alpha); + m_text->draw((m_W - m_text->width())/2, (m_H - m_text->height())/2); + } +} + + +void newuiButton::OnLostFocus() +{ + if (m_text) { + m_text->set_flags(0); + m_text->set_color(NEWUI_GADGETFONT_COLOR); + } + UIButton::OnLostFocus(); +} + + +void newuiButton::OnGainFocus() +{ + if (m_text) { + m_text->set_color(NEWUI_GADGETFONT_HICOLOR); + m_text->set_flags(UISNAZZYTEXTF_BLINKING); + } + UIButton::OnGainFocus(); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS a large option button used in menus. + +void newuiMenuOptionButton::Create(newuiMenu *menu, newuiMenuOptionButton *last, short id, const char *name, short x, short y, bool mono_press) +{ + m_bkg = Newui_resources.Load(NEWUI_LRGBTN_FILE); + m_litbkg = Newui_resources.Load(NEWUI_LRGBTNLIT_FILE); + m_text = GadgetLargeText(name); + m_mono_press = mono_press; + m_prev_btn = last; + m_next_btn = NULL; + if (m_prev_btn) { + m_prev_btn->m_next_btn = this; + } + + newuiButton::Create(menu,id, name, x, y); +} + + +void newuiMenuOptionButton::OnMouseBtnDown(int btn) +{ + if (btn == UILMSEBTN) { + if ((m_State != UI_BTS_ACTIVATED) && PT_IN_GADGET(m_Wnd, this, UI_input.mx, UI_input.my)) { + m_State = UI_BTS_ACTIVATED; + LOCK_FOCUS(this); + // mprintf((0, "mouse down in button gadget (%d)\n", GetID())); + if (m_mono_press) { + // mprintf((0, "selected button gadget (%d)\n", GetID())); + OnSelect(); + m_State = UI_BTS_HILITE; + } + } + } +} + + +void newuiMenuOptionButton::OnMouseBtnUp(int btn) +{ + if (btn == UILMSEBTN) { + if (m_State == UI_BTS_ACTIVATED) { + if (PT_IN_GADGET(UIGadget::m_Wnd, this, UI_input.mx, UI_input.my)) { + m_State = UI_BTS_HILITE; + // if (!m_mono_press) { + OnSelect(); + // } + } + else { + m_State = UI_BTS_INACTIVE; + } + } + UNLOCK_FOCUS(this); + } +} + + +void newuiMenuOptionButton::OnKeyDown(int key) +{ + if (key == KEY_DOWN || key == KEY_RIGHT) { + if (m_next_btn) { + if (UIGadget::IsLocked()) { + UNLOCK_FOCUS(this); + } + m_Wnd->SetFocusOnGadget(m_next_btn, true); + } + } + else if (key == KEY_UP || key == KEY_LEFT) { + if (m_prev_btn) { + if (UIGadget::IsLocked()) { + UNLOCK_FOCUS(this); + } + m_Wnd->SetFocusOnGadget(m_prev_btn, true); + } + } +} + + +void newuiMenuOptionButton::OnKeyUp(int key) +{ +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS creates an arrow button that is sensitive to touch (when down, always select) + +void newuiArrowButton::Create(UIWindow *wnd, short id, short type, const char *name, short x, short y) +{ + switch (type) + { + case NEWUI_ARROW_LEFT: + m_bkg = Newui_resources.Load(NEWUI_LARR_FILE); + m_litbkg = Newui_resources.Load(NEWUI_LARRLIT_FILE); + break; + case NEWUI_ARROW_RIGHT: + m_bkg = Newui_resources.Load(NEWUI_RARR_FILE); + m_litbkg = Newui_resources.Load(NEWUI_RARRLIT_FILE); + break; + case NEWUI_ARROW_UP: + m_bkg = Newui_resources.Load(NEWUI_UARR_FILE); + m_litbkg = Newui_resources.Load(NEWUI_UARRLIT_FILE); + break; + case NEWUI_ARROW_DOWN: + m_bkg = Newui_resources.Load(NEWUI_DARR_FILE); + m_litbkg = Newui_resources.Load(NEWUI_DARRLIT_FILE); + break; + case NEWUI_ARROW_CBUP: + m_bkg = Newui_resources.Load(NEWUI_UARRCB_FILE); + m_litbkg = Newui_resources.Load(NEWUI_UARRLITCB_FILE); + break; + case NEWUI_ARROW_CBDOWN: + m_bkg = Newui_resources.Load(NEWUI_DARRCB_FILE); + m_litbkg = Newui_resources.Load(NEWUI_DARRLITCB_FILE); + break; + default: + Int3(); + } + + newuiButton::Create(wnd, id, name, x, y); + UIGadget::SetDatum(type); + + m_timer = 0.0f; + m_selecttimer = 0.0f; + m_hidden = false; +} + + +void newuiArrowButton::Show(bool show) +{ + m_hidden = !show; +} + + +void newuiArrowButton::OnMouseBtnDown(int btn) +{ + bool mouse_in_gadget = PT_IN_GADGET(m_Wnd, this, UI_input.mx, UI_input.my); //DAJ Added to keep stray clicks from changing values + + if (btn == UILMSEBTN && mouse_in_gadget) { + if (m_State == UI_BTS_HILITE) { + if (!m_hidden) { + m_State = UI_BTS_ACTIVATED; + LOCK_FOCUS(this); + m_timer = 0.0f; + } + } + if (m_State == UI_BTS_ACTIVATED) { + // this is so that select messages get set at correct times + if (m_hidden) { + m_State = UI_BTS_HILITE; + UNLOCK_FOCUS(this); + } + else { + if (m_timer >= m_selecttimer) { + if (m_selecttimer == 0.0f) { + m_selecttimer = 0.5f; + } + else if (m_selecttimer == 0.5f) { + m_selecttimer = 0.050f; + } + OnSelect(); //DAJ moved from above the m_selecttimer set since this is recursive + m_timer = 0.0f; + } + m_timer += UIFrameTime; + } + } + } +} + + +void newuiArrowButton::OnMouseBtnUp(int btn) +{ + if (btn == UILMSEBTN) { + if (m_State == UI_BTS_ACTIVATED) { + if (PT_IN_GADGET(UIGadget::m_Wnd, this, UI_input.mx, UI_input.my)) { + m_State = UI_BTS_HILITE; + } + else { + m_State = UI_BTS_INACTIVE; + } + } + UNLOCK_FOCUS(this); + + m_selecttimer = 0.0f; + m_timer = 0.0f; + } +} + + +void newuiArrowButton::OnDraw() +{ + if (m_hidden) return; + + newuiButton::OnDraw(); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS a new check box: note that newuiButton and UICheckBox will share the same UIButton base +// since bott newuiButton and UICheckBox inherit UIButton virtually. + +newuiCheckBox::newuiCheckBox() +{ +} + + +void newuiCheckBox::Create(UIWindow *wnd, short id, const char *name, short x, short y, bool is_long) +{ + m_bkg = Newui_resources.Load(is_long ? NEWUI_LCHKBTN_FILE : NEWUI_CHKBTN_FILE); + m_litbkg = Newui_resources.Load(is_long ? NEWUI_LCHKBTNLIT_FILE : NEWUI_CHKBTNLIT_FILE); + + UICheckBox::Create(wnd, id, &UITextItem(""), x, y, 10, 8, UIF_FIT); + newuiButton::InitStates(name, is_long); +} + + +// this will use the newuiButton drawing scheme +void newuiCheckBox::OnDraw() +{ + newuiButton::OnDraw(); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS a new radio button + +newuiRadioButton::newuiRadioButton() +{ +} + + +void newuiRadioButton::Create(UIWindow *wnd, UIRadioButton *prev_rb, short id, const char *name, short x, short y, bool is_long) +{ + m_bkg = Newui_resources.Load(is_long ? NEWUI_LBTN_FILE : NEWUI_BTN_FILE); + m_litbkg = Newui_resources.Load(is_long ? NEWUI_LCHKBTNLIT_FILE : NEWUI_CHKBTNLIT_FILE); + + UIRadioButton::Create(wnd, prev_rb, id, &UITextItem(""), x, y, 10, 8, UIF_FIT); + newuiButton::InitStates(name, is_long); +} + + +// this will use the newuiButton drawing scheme +void newuiRadioButton::OnDraw() +{ + newuiButton::OnDraw(); +} + + + +////////////////////////////////////////////////////////////////////////////// +// CLASS a new slider. + +newuiSlider::newuiSlider() +{ + m_pos = m_unitrange = 0; + m_bar_bmp = NULL; + m_title = NULL; +} + +void newuiSlider::Create(UIWindow *wnd, short id, const char *name, short x, short y, short range) +{ + ASSERT(range > 0); + + if (name) { + m_title = MonitorSmallText(name); + } + + m_pos = range; + m_unitrange = range; + m_bar_bmp = Newui_resources.Load(NEWUI_SLIDER_FILE); + + UIGadget::Create(wnd, id, x, y, 10, 10, 0); + + m_unit_settings.type = -1; +} + + +void newuiSlider::Offset(short offs) +{ + newuiSlider::SetPos(m_pos+offs); +} + + +void newuiSlider::SetPos(short pos) +{ + if (pos < 0) pos = 0; + if (pos > m_unitrange) pos = m_unitrange; + m_pos = pos; +} + + +void newuiSlider::SetRange(short range) +{ + m_unitrange = range; +} + + +// when gadget is added to a window (AddGadget is called) +void newuiSlider::OnAttachToWindow() +{ + short bx=m_X, by=m_Y; + + if (m_title) { + by += m_title->height() + 5; + bx += 2; + } + + m_minus_btn.Create(m_Wnd, -1, NEWUI_ARROW_LEFT, NULL, bx,by); + m_minus_btn.SetFlag(UIF_NOTIFYMASTERSEL); + AttachSlaveGadget(&m_minus_btn); + m_plus_btn.Create(m_Wnd, -1, NEWUI_ARROW_RIGHT, NULL, bx+m_bar_bmp->width()-24,by); + m_plus_btn.SetFlag(UIF_NOTIFYMASTERSEL); + AttachSlaveGadget(&m_plus_btn); +} + + +// when gadget is detached from window +void newuiSlider::OnDetachFromWindow() +{ + if (m_plus_btn.IsCreated()) { + DetachSlaveGadget(&m_plus_btn); + m_plus_btn.Destroy(); + } + if (m_minus_btn.IsCreated()) { + DetachSlaveGadget(&m_minus_btn); + m_minus_btn.Destroy(); + } +} + + +void newuiSlider::SetUnits(tSliderSettings *settings) +{ + if (settings == NULL) { + m_unit_settings.type = -1; + } + else { + memcpy(&m_unit_settings, settings, sizeof(m_unit_settings)); + } +} + + +void newuiSlider::OnFormat() +{ + if (m_bar_bmp) { + m_W = m_bar_bmp->width(); + m_H = m_bar_bmp->height(); + if (m_title) { + m_H += m_title->height(); + } + } +} + + +void newuiSlider::OnLostFocus() +{ + if (m_title) { + m_title->set_flags(0); + } +} + + +void newuiSlider::OnGainFocus() +{ + if (m_title) { + m_title->set_flags(UISNAZZYTEXTF_BLINKING); + } +} + + +// override: behavior when key pressed. +void newuiSlider::OnKeyDown(int key) +{ + if (key == KEY_LEFT) { + newuiSlider::Offset(-1); + UIGadget::OnSelect(); + } + if (key == KEY_RIGHT) { + newuiSlider::Offset(1); + UIGadget::OnSelect(); + } +} + + +// override: behavior when key released. +void newuiSlider::OnKeyUp(int key) +{ +} + + +void newuiSlider::OnDraw() +{ + int ox = 0, oy = 0; + int x, lx; + float percent_full, delta_g, green_intensity; + + percent_full = (float)m_pos/(float)m_unitrange; + +// draw units value if available + if (m_unit_settings.type > -1) { + char str[8]; + + switch (m_unit_settings.type) + { + case SLIDER_UNITS_INT: + sprintf(str, "%d", m_unit_settings.min_val.i+(int)floor((m_unit_settings.max_val.i-m_unit_settings.min_val.i)*percent_full+0.5f)); + break; + case SLIDER_UNITS_FLOAT: + sprintf(str, "%.2f", m_unit_settings.min_val.f+(m_unit_settings.max_val.f-m_unit_settings.min_val.f)*percent_full); + break; + case SLIDER_UNITS_PERCENT: + sprintf(str, "%d%%", (int)((percent_full*100.0f)+0.5f)); + break; + default: + Int3(); + str[0] = 0; + } + + UITextItem value_txt(MONITOR9_NEWUI_FONT, str, NEWUI_MONITORFONT_COLOR); + value_txt.draw(ox+m_W-value_txt.width()-1, oy+1); + } + +// draw graphic bar first + if (m_title) { + m_title->draw(ox, oy+1); + oy += m_title->height()+2; + } + +// draw bar frame + if (m_bar_bmp) { + m_bar_bmp->draw(ox, oy); + ox += 20; // move to where bar will be filled in. + oy += 2; + } + +// draw slider bar now. + int width = m_bar_bmp->width() - 42; + int height = m_bar_bmp->height() - 6; + + lx = (percent_full*width)+ox; + delta_g = (lx-ox) ? (percent_full * 255.0f)/(lx-ox) : 0.0f; + green_intensity = 0.0f; + + for (x = ox; x < lx; x++) + { + ui_DrawLine(GR_RGB(0, green_intensity, 0), x,oy+1,x,oy+height-1); + green_intensity += delta_g; + } +} + + +void newuiSlider::OnDestroy() +{ + if (m_bar_bmp) { + Newui_resources.Free(m_bar_bmp); + m_bar_bmp = NULL; + } + if (m_title) { + delete m_title; + m_title = NULL; + } + +} + + +// this function will handle when an arrow button was pressed +void newuiSlider::OnNotifySelect(UIGadget *g) +{ + switch (g->GetDatum()) + { + case NEWUI_ARROW_LEFT: + newuiSlider::Offset(-1); + break; + case NEWUI_ARROW_RIGHT: + newuiSlider::Offset(1); + break; + } + + UIGadget::OnSelect(); +} + + + +// CLASS a new listbox. uses less memory than the old listbox hopefully. + +newuiListBox::newuiListBox() +{ + m_ItemList = NULL; + m_Virt2Real = NULL; + m_Real2Virt = NULL; + selectchange_fn = NULL; + selectchange_id_fn = NULL; + m_callbackptr = NULL; + m_NumItems = 0; + m_SelectedIndex = -1; + m_Index = 0; + m_NumVisibleItems = 0; + m_mse_clicked = false; + m_nclicks = 0; + m_mse_x = 0; + m_mse_y = 0; + m_click_time = 0.0f; +} + + +void newuiListBox::Create(UIWindow *wnd, short id, short x, short y, short w, short h, ushort flags) +{ + m_ItemList = NULL; + m_Virt2Real = NULL; + m_Real2Virt = NULL; + selectchange_fn = NULL; + selectchange_id_fn = NULL; + m_callbackptr = NULL; + m_NumItems = 0; + m_SelectedIndex = -1; + m_Index = 0; + m_NumVisibleItems = 0; + m_boffs = 12; + m_nclicks = 0; + m_mse_clicked = false; + m_click_time = 0.0f; + + m_pieces[N_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_N_FILE); + m_pieces[NE_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_NE_FILE); + m_pieces[E_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_E_FILE); + m_pieces[SE_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_SE_FILE); + m_pieces[S_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_S_FILE); + m_pieces[SW_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_SW_FILE); + m_pieces[W_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_W_FILE); + m_pieces[NW_PIECE_IDX] = Newui_resources.Load(NEWUI_LB_NW_FILE); + +// determine true width and height (snap width and height to 32 pixel boundaries + short p_w = LB_PIECE_WIDTH, p_h = LB_PIECE_HEIGHT; + short t_width = (w/p_w); + short t_height = (h/p_h); + if (w % p_w) t_width++; + if (h % p_h) t_height++; + w = t_width*p_w; + h = t_height*p_h; + + UIGadget::Create(wnd, id, x, y, w, h, flags); +} + + +// functions to add or remove items, +void newuiListBox::AddItem(const char *name) +{ + char **temp; + int i; + int *tempvirt2real,*tempreal2virt; + int real_index = -1; + + if(m_Flags&UILB_NOSORT){ + //insert without sort + + if ((m_NumItems % LISTBOX_BUFFER_SIZE) == 0) { + // dynamically allocated memory for listbox, expand as needed. + temp = (char **)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE) * sizeof(char *)); + tempvirt2real = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + tempreal2virt = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + + if (m_ItemList) { + for (i = 0; i < m_NumItems; i++){ + temp[i] = m_ItemList[i]; + tempvirt2real[i] = m_Virt2Real[i]; + tempreal2virt[i] = m_Real2Virt[i]; + } + + mem_free(m_ItemList); + mem_free(m_Virt2Real); + mem_free(m_Real2Virt); + } + m_ItemList= temp; + m_Virt2Real = tempvirt2real; + m_Real2Virt = tempreal2virt; + } + + real_index = m_NumItems; + }else{ + //only text item listboxes! + + //insert with sorting + if ((m_NumItems % LISTBOX_BUFFER_SIZE) == 0) { + // dynamically allocated memory for listbox, expand as needed. + int oidx = 0,cidx = 0; + int pidx = 0; + int result; + + temp = (char **)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE) * sizeof(char *)); + tempvirt2real = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + tempreal2virt = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + + real_index = -1; + + if (m_ItemList) { + while(oidx<=m_NumItems){ + if(oidx==m_NumItems){ + if(real_index==-1){ + real_index = oidx; + }else{ + temp[cidx] = m_ItemList[pidx]; + tempreal2virt[cidx] = m_Real2Virt[pidx]; + } + oidx++; + continue; + } + + if(m_Flags&UILB_CASESENSITIVE) + result = strcmp(m_ItemList[cidx],name); + else + result = stricmp(m_ItemList[cidx],name); + if(result<0){ + //copy old + temp[cidx] = m_ItemList[pidx]; + tempreal2virt[cidx] = m_Real2Virt[pidx]; + cidx++; + pidx++; + }else if(result>=0){ + //see if we need to insert, if not just keep copying + if(real_index==-1){ + real_index = cidx; + cidx++; + }else{ + temp[cidx] = m_ItemList[pidx]; + tempreal2virt[cidx] = m_Real2Virt[pidx]; + cidx++; + pidx++; + } + } + oidx++; + } + ASSERT(real_index!=-1); + + //now we need to fix m_Virt2Real to represent the new structure, + //all values>=real_index must be incremented + for(oidx=0;oidx<=m_NumItems;oidx++){ + if(oidx=real_index) + tempvirt2real[oidx]++; + } + } + + mem_free(m_ItemList); + mem_free(m_Virt2Real); + mem_free(m_Real2Virt); + }else{ + real_index = 0; + } + + m_ItemList= temp; + m_Virt2Real = tempvirt2real; + m_Real2Virt = tempreal2virt; + }else{ + //find where to insert, without allocating memory + int oidx = 0,cidx = 0; + real_index = -1; + int result; + + int old_r2v; + char *old_item = NULL; + + while(oidx<=m_NumItems){ + + if(oidx==m_NumItems){ + //we're past the slot of the old buffer + if(real_index==-1){ + //we haven't set the real index yet, so it has to go here + real_index = m_NumItems; + }else{ + //the real index has been set, we're still pulling back + m_Real2Virt[cidx] = old_r2v; + m_ItemList[cidx] = old_item; + } + oidx++; + continue; + }else{ + if(m_Flags&UILB_CASESENSITIVE) + result = strcmp(m_ItemList[cidx],name); + else + result = stricmp(m_ItemList[cidx],name); + } + + if(result<0){ + //we haven't gotten to the string yet, keep as they are + cidx++; + }else if(result>=0){ + //see if we need to insert, if not just keep copying + if(real_index==-1){ + //we need to insert here + real_index = cidx; + old_item = m_ItemList[cidx]; + old_r2v = m_Real2Virt[cidx]; + cidx++; + }else{ + //old_item and old_r2v should be set! + ASSERT(old_item); + char *oi; + int or2v; + + or2v = m_Real2Virt[cidx]; + oi = m_ItemList[cidx]; + + m_Real2Virt[cidx] = old_r2v; + m_ItemList[cidx] = old_item; + old_r2v = or2v; + old_item = oi; + + cidx++; + } + } + + //continue on + oidx++; + } + + ASSERT(real_index!=-1); + + //now we need to fix m_Virt2Real to represent the new structure, + //all values>=real_index must be incremented + cidx = 0; + for(oidx=0;oidx<=m_NumItems;oidx++){ + if(oidx!=real_index){ + if(m_Virt2Real[cidx]>=real_index) + m_Virt2Real[cidx]++; + cidx++; + } + } + } + } + + // copy item passed to list. + m_ItemList[real_index] = mem_strdup(name); + m_Virt2Real[m_NumItems] = real_index; + m_Real2Virt[real_index] = m_NumItems; + m_NumItems++; + + if(real_index<=m_SelectedIndex) + { + //adjust selected index + if(m_SelectedIndex=real_index){ + m_ItemList[j] = m_ItemList[j+1]; + m_Real2Virt[j] = m_Real2Virt[j+1]; + } + + if(j>=virt_index){ + m_Virt2Real[j] = m_Virt2Real[j+1]; + } + + if(m_Real2Virt[j]>=real_thresh) + m_Real2Virt[j]--; + + if(m_Virt2Real[j]>=virt_thresh) + m_Virt2Real[j]--; + } + m_NumItems--; + + if(real_index<=m_SelectedIndex) + { + //adjust selected index + if(m_SelectedIndex>0) + m_SelectedIndex--; + } +} + + +void newuiListBox::RemoveAll() +{ + m_SelectedIndex = -1; + + if(m_ItemList) { + int i; + for (i = 0; i < m_NumItems; i++) + { + if (m_ItemList[i]) + mem_free(m_ItemList[i]); + } + mem_free(m_ItemList); + } + + if(m_Virt2Real) + mem_free(m_Virt2Real); + + if(m_Real2Virt) + mem_free(m_Real2Virt); + + m_ItemList = NULL; + m_Virt2Real = NULL; + m_Real2Virt = NULL; + m_Index = m_NumItems = 0; +} + + +// item information +int newuiListBox::GetCurrentIndex() +{ + if(m_NumItems==0) + return 0; + + if(m_SelectedIndex<0) + return m_SelectedIndex; + + if(!m_Real2Virt) + return 0; + + int virt_index = m_Real2Virt[m_SelectedIndex]; + ASSERT(virt_index>=0 && virt_index=0 && index < m_NumItems) { + int real_index = m_Virt2Real[index], old_index = m_SelectedIndex; + int i=0,y; + + ASSERT(real_index>=0 && real_index (m_H-m_boffs-item.height())) { + i++; + break; + } + } + + if (m_SelectedIndex < m_Index) { + m_Index = m_SelectedIndex; + } + if (m_SelectedIndex >= (m_Index+i)) { + m_Index = m_SelectedIndex - i + 1; + } + if (m_Index < 0) { + m_Index = 0; + } + + // call callbacks if selection really changed. + if (old_index != m_SelectedIndex) { + if (selectchange_fn) + (*selectchange_fn)(index); + if (selectchange_id_fn) + (*selectchange_id_fn)(GetID(),m_callbackptr); + } + } +} + + +void newuiListBox::SetInternalCurrentIndex(int index) +{ + +} + + +bool newuiListBox::GetCurrentItem(char *buffer, int buflen) +{ + return newuiListBox::GetItem(m_Real2Virt[m_SelectedIndex], buffer, buflen); +} + + +bool newuiListBox::GetItem(int index, char *buffer, int buflen) +{ + if(m_NumItems==0) { + return false; + *buffer = 0; + } + + if (index>=0 && index < m_NumItems) { + int real_index = m_Virt2Real[index]; + ASSERT(real_index>=0 && real_indexdraw(x,y); + tx++; + x+= LB_PIECE_WIDTH; + while (tx++ < (t_width-1)) + { + m_pieces[N_PIECE_IDX]->draw(x,y); + x+= LB_PIECE_WIDTH; + } + m_pieces[NE_PIECE_IDX]->draw(x,y); + tx++; + x = 0; + y += LB_PIECE_HEIGHT; + ty++; + +// mid rows. + while (ty++ < (t_height-1)) + { + m_pieces[W_PIECE_IDX]->draw(x,y); + x+= LB_PIECE_WIDTH; + tx = 1; + while (tx++ < (t_width-1)) + { + x+= LB_PIECE_WIDTH; + } + m_pieces[E_PIECE_IDX]->draw(x,y); + tx++; + x = 0; + y += LB_PIECE_HEIGHT; + } + +// bottom row. + m_pieces[SW_PIECE_IDX]->draw(x,y); + x+= LB_PIECE_WIDTH; + tx = 1; + while (tx++ < (t_width-1)) + { + m_pieces[S_PIECE_IDX]->draw(x,y); + x+= LB_PIECE_WIDTH; + } + m_pieces[SE_PIECE_IDX]->draw(x,y); + tx++; + ty++; + +// draw text? + int i; + ubyte alpha = 255; + bool auto_select = ((m_Flags&UILB_AUTOSELECT)>0); + bool use_scroll = true; + + ui_SetTextClip(m_boffs,m_boffs,m_W-m_boffs-m_up_btn.W(), m_H-m_boffs); + + for (i = m_Index, y = m_boffs; i < m_NumItems; i++) + { + UITextItem item(m_ItemList[i]); + item.set_font(MONITOR9_NEWUI_FONT); + + if (m_mse_clicked) { + if (m_mse_y >= y && m_mse_y <= (y+item.height()-1)) { + if (m_mse_x > m_boffs && m_mse_x < (m_W-m_boffs-12)) { + newuiListBox::SetCurrentIndex((m_Real2Virt)?m_Real2Virt[i]:0); + // item.set_font(GADGET9_NEWUI_FONT); + } + } + } + + y+= item.height(); + if (y > (m_H-m_boffs-item.height())) { + break; + } + } + + m_mse_clicked = false; + + if (m_SelectedIndex == -1 && m_NumItems>0) { + newuiListBox::SetCurrentIndex(m_Real2Virt[0]); + } + + for (i = m_Index, y = m_boffs; i < m_NumItems; i++) + { + UITextItem item(m_ItemList[i]); + + item.set_font(MONITOR9_NEWUI_FONT); + item.set_color(NEWUI_MONITORFONT_COLOR); + + if (m_SelectedIndex == i) { + // draw a bar? + ddgr_color bar_color; + // item.set_font(GADGET9_NEWUI_FONT); + // item.set_color(NEWUI_GADGETFONT_COLOR); + bar_color = m_infocus ? GR_RGB(0,64,0) : GR_RGB(0,32,0); + ui_DrawRect(bar_color, m_boffs,y,m_W-m_boffs-m_up_btn.W(),y+item.height()); + } + + if (!m_infocus) { + item.set_alpha(192); + } + + item.draw(m_boffs, y); + + y+= item.height(); + if (y > (m_H-m_boffs-item.height())) { + i++; + break; + } + } + + if(m_NumItems) { + if(use_scroll && auto_select ) { + if(m_SelectedIndex < m_Index) + newuiListBox::SetCurrentIndex(m_Real2Virt[m_Index]); + if(m_SelectedIndex > i) + newuiListBox::SetCurrentIndex(m_Real2Virt[i]); + } + } + + m_NumVisibleItems = (i - m_Index); + + ui_ResetTextClip(); +} + + +// behavior when key is pressed. +void newuiListBox::OnKeyDown(int key) +{ + if (key == KEY_SPACEBAR) { + + } + else { + int new_selected_index = m_SelectedIndex; + + if (key == KEY_UP) { + if (m_SelectedIndex > 0) { + new_selected_index = m_SelectedIndex-1; + } + } + else if (key == KEY_DOWN) { + if (m_SelectedIndex < (m_NumItems-1)) { + new_selected_index = m_SelectedIndex+1; + } + } + else if (key == KEY_PAGEUP) { + if (m_SelectedIndex != m_Index) { + new_selected_index = m_Index; + } + else { + new_selected_index = m_SelectedIndex - m_NumVisibleItems; + if (new_selected_index < 0) new_selected_index = 0; + } + } + else if (key == KEY_PAGEDOWN) { + if (m_SelectedIndex != (m_Index+m_NumVisibleItems-1)) { + new_selected_index = (m_Index+m_NumVisibleItems-1); + } + else { + new_selected_index = m_SelectedIndex+m_NumVisibleItems; + if (new_selected_index >= m_NumItems) new_selected_index = m_NumItems-1; + } + } + newuiListBox::SetCurrentIndex((m_Real2Virt)?m_Real2Virt[new_selected_index]:0); + } +} + + +// behavior when key is released. +void newuiListBox::OnKeyUp(int key) +{ + + +} + + +// behavior when mouse button is pressed. +void newuiListBox::OnMouseBtnDown(int btn) +{ + bool mouse_in_gadget = PT_IN_GADGET(m_Wnd, this, UI_input.mx, UI_input.my); + + if (btn == UILMSEBTN && mouse_in_gadget) { + float time_delta=0.0f, old_click_time; + m_mse_x = SCREEN_TO_GAD_X(this, UI_input.mx); + m_mse_y = SCREEN_TO_GAD_Y(this, UI_input.my); + m_mse_clicked = true; + + old_click_time= m_click_time; + m_click_time = UI_TIME(); + + time_delta = m_click_time - old_click_time; + if (time_delta > UI_DBLCLICK_DELAY) { + m_nclicks = 0; + } + + if (m_nclicks == 0) { + LOCK_FOCUS(this); + m_nclicks = 1; + // mprintf((0, "click 1\n")); + } + else if (m_nclicks == 2) { + // mprintf((0, "click 2\n")); + LOCK_FOCUS(this); + if (((m_click_time - old_click_time) < UI_DBLCLICK_DELAY)) { + if (abs(m_mse_x - m_last_mse_x) < UI_DBLCLICK_MSEDELTA && abs(m_mse_y - m_last_mse_y) < UI_DBLCLICK_MSEDELTA) { + if (m_NumItems > 0 && m_SelectedIndex > -1) { + int y, i; + for (i = m_Index, y = m_boffs; i < m_NumItems; i++) + { + UITextItem item(m_ItemList[i]); + item.set_font(MONITOR9_NEWUI_FONT); + + if (m_mse_y >= y && m_mse_y <= (y+item.height()-1)) { + if (m_mse_x > m_boffs && m_mse_x < (m_W-m_boffs-12)) { + OnSelect(); + // mprintf((0, "select!\n")); + m_nclicks = 0; + break; + } + } + y+= item.height(); + if (y > (m_H-m_boffs-item.height())) { + break; + } + } + UNLOCK_FOCUS(this); + } + } + } + if (m_nclicks == 2) m_nclicks = 3; + } + m_last_mse_x = m_mse_x; + m_last_mse_y = m_mse_y; + } + else if (btn == UILMSEBTN && !mouse_in_gadget) { + if (IsLocked()) UNLOCK_FOCUS(this); + m_nclicks = 0; + } + +} + + +// behavior when mouse button is released. +void newuiListBox::OnMouseBtnUp(int btn) +{ + bool mouse_in_gadget = PT_IN_GADGET(m_Wnd, this, UI_input.mx, UI_input.my); + + if (btn == UILMSEBTN && mouse_in_gadget) { + UNLOCK_FOCUS(this); + if (m_nclicks == 1) m_nclicks = 2; + else if (m_nclicks == 3) m_nclicks = 0; + m_last_selected_index = m_SelectedIndex; + } +} + + +// this function will handle when an arrow button was pressed +void newuiListBox::OnNotifySelect(UIGadget *g) +{ + switch (g->GetDatum()) + { + case NEWUI_ARROW_UP: + newuiListBox::Offset(-1); + break; + case NEWUI_ARROW_DOWN: + newuiListBox::Offset(1); + break; + } +} + + +// when a listbox item was double clicked (or entered) +void newuiListBox::OnSelect() +{ + UIGadget::OnSelect(); +} + +// called when destroyed. +void newuiListBox::OnDestroy() +{ + int i; + + if (m_up_btn.IsCreated()) + m_up_btn.Destroy(); + if (m_down_btn.IsCreated()) + m_down_btn.Destroy(); + + for (i = 0; i < m_NumItems; i++) + { + mem_free(m_ItemList[i]); + } + + if (m_ItemList) + mem_free(m_ItemList); + if (m_Real2Virt) + mem_free(m_Real2Virt); + if (m_Virt2Real) + mem_free(m_Virt2Real); + + m_ItemList = NULL; + m_Real2Virt = NULL; + m_Virt2Real = NULL; + + + Newui_resources.Free(m_pieces[N_PIECE_IDX]); + Newui_resources.Free(m_pieces[NE_PIECE_IDX]); + Newui_resources.Free(m_pieces[E_PIECE_IDX]); + Newui_resources.Free(m_pieces[SE_PIECE_IDX]); + Newui_resources.Free(m_pieces[S_PIECE_IDX]); + Newui_resources.Free(m_pieces[SW_PIECE_IDX]); + Newui_resources.Free(m_pieces[W_PIECE_IDX]); + Newui_resources.Free(m_pieces[NW_PIECE_IDX]); + + UIGadget::OnDestroy(); +} + + +// override: behavior when gadget is processed +void newuiListBox::OnUserProcess() +{ + +} + + +// when gadget is added to a window (AddGadget is called) +void newuiListBox::OnAttachToWindow() +{ + m_up_btn.Create(m_Wnd, -1, NEWUI_ARROW_UP, NULL, m_X+m_W-m_boffs-8, m_Y+m_boffs-1); + m_up_btn.SetFlag(UIF_NOTIFYMASTERSEL); + AttachSlaveGadget(&m_up_btn); + + m_down_btn.Create(m_Wnd, -1, NEWUI_ARROW_DOWN, NULL, m_X+m_W-m_boffs-8, m_Y+m_H-m_boffs-19); + m_down_btn.SetFlag(UIF_NOTIFYMASTERSEL); + AttachSlaveGadget(&m_down_btn); +} + + +void newuiListBox::OnDetachFromWindow() +{ + DetachSlaveGadget(&m_down_btn); + m_down_btn.Destroy(); + DetachSlaveGadget(&m_up_btn); + m_up_btn.Destroy(); +} + + +void newuiListBox::Offset(short offs) +{ + m_Index = m_Index + offs; + if (m_Index < 0) { + m_Index = 0; + } + else if (m_Index >= (m_NumItems-m_NumVisibleItems)) { + m_Index = m_NumItems-m_NumVisibleItems; + } +} + + + +// CLASS a new listbox. uses less memory than the old listbox hopefully. + +newuiComboBox::newuiComboBox() +{ + m_ItemList = NULL; + m_Virt2Real = NULL; + m_Real2Virt = NULL; + selectchange_fn = NULL; + m_NumItems = 0; + m_Index = 0; +} + + +void newuiComboBox::Create(UIWindow *wnd, short id, short x, short y, ushort flags) +{ + m_ItemList = NULL; + m_Virt2Real = NULL; + m_Real2Virt = NULL; + selectchange_fn = NULL; + m_NumItems = 0; + m_Index = 0; + m_showup = m_showdown = false; + + m_barbmp = Newui_resources.Load(NEWUI_CB_FILE); + m_boffs = 12; + +// determine true width and height (snap width and height to 32 pixel boundaries + UIGadget::Create(wnd, id, x, y, m_barbmp->width(), m_barbmp->height(), flags); +} + + +// functions to add or remove items, +void newuiComboBox::AddItem(const char *name) +{ + char **temp; + int i; + int *tempvirt2real,*tempreal2virt; + int real_index = -1; + + if(m_Flags&UILB_NOSORT){ + //insert without sort + + if ((m_NumItems % LISTBOX_BUFFER_SIZE) == 0) { + // dynamically allocated memory for listbox, expand as needed. + temp = (char **)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE) * sizeof(char *)); + tempvirt2real = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + tempreal2virt = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + + if (m_ItemList) { + for (i = 0; i < m_NumItems; i++){ + temp[i] = m_ItemList[i]; + tempvirt2real[i] = m_Virt2Real[i]; + tempreal2virt[i] = m_Real2Virt[i]; + } + + mem_free(m_ItemList); + mem_free(m_Virt2Real); + mem_free(m_Real2Virt); + } + m_ItemList= temp; + m_Virt2Real = tempvirt2real; + m_Real2Virt = tempreal2virt; + } + + real_index = m_NumItems; + }else{ + //only text item listboxes! + + //insert with sorting + if ((m_NumItems % LISTBOX_BUFFER_SIZE) == 0) { + // dynamically allocated memory for listbox, expand as needed. + int oidx = 0,cidx = 0; + int pidx = 0; + int result; + + temp = (char **)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE) * sizeof(char *)); + tempvirt2real = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + tempreal2virt = (int *)mem_malloc((m_NumItems + LISTBOX_BUFFER_SIZE)*sizeof(int)); + + real_index = -1; + + if (m_ItemList) { + while(oidx<=m_NumItems){ + if(oidx==m_NumItems){ + if(real_index==-1){ + real_index = oidx; + }else{ + temp[cidx] = m_ItemList[pidx]; + tempreal2virt[cidx] = m_Real2Virt[pidx]; + } + oidx++; + continue; + } + + if(m_Flags&UILB_CASESENSITIVE) + result = strcmp(m_ItemList[cidx],name); + else + result = stricmp(m_ItemList[cidx],name); + if(result<0){ + //copy old + temp[cidx] = m_ItemList[pidx]; + tempreal2virt[cidx] = m_Real2Virt[pidx]; + cidx++; + pidx++; + }else if(result>=0){ + //see if we need to insert, if not just keep copying + if(real_index==-1){ + real_index = cidx; + cidx++; + }else{ + temp[cidx] = m_ItemList[pidx]; + tempreal2virt[cidx] = m_Real2Virt[pidx]; + cidx++; + pidx++; + } + } + oidx++; + } + ASSERT(real_index!=-1); + + //now we need to fix m_Virt2Real to represent the new structure, + //all values>=real_index must be incremented + for(oidx=0;oidx<=m_NumItems;oidx++){ + if(oidx=real_index) + tempvirt2real[oidx]++; + } + } + + mem_free(m_ItemList); + mem_free(m_Virt2Real); + mem_free(m_Real2Virt); + }else{ + real_index = 0; + } + + m_ItemList= temp; + m_Virt2Real = tempvirt2real; + m_Real2Virt = tempreal2virt; + }else{ + //find where to insert, without allocating memory + int oidx = 0,cidx = 0; + real_index = -1; + int result; + + int old_r2v; + char *old_item = NULL; + + while(oidx<=m_NumItems){ + + if(oidx==m_NumItems){ + //we're past the slot of the old buffer + if(real_index==-1){ + //we haven't set the real index yet, so it has to go here + real_index = m_NumItems; + }else{ + //the real index has been set, we're still pulling back + m_Real2Virt[cidx] = old_r2v; + m_ItemList[cidx] = old_item; + } + oidx++; + continue; + }else{ + if(m_Flags&UILB_CASESENSITIVE) + result = strcmp(m_ItemList[cidx],name); + else + result = stricmp(m_ItemList[cidx],name); + } + + if(result<0){ + //we haven't gotten to the string yet, keep as they are + cidx++; + }else if(result>=0){ + //see if we need to insert, if not just keep copying + if(real_index==-1){ + //we need to insert here + real_index = cidx; + old_item = m_ItemList[cidx]; + old_r2v = m_Real2Virt[cidx]; + cidx++; + }else{ + //old_item and old_r2v should be set! + ASSERT(old_item); + char *oi; + int or2v; + + or2v = m_Real2Virt[cidx]; + oi = m_ItemList[cidx]; + + m_Real2Virt[cidx] = old_r2v; + m_ItemList[cidx] = old_item; + old_r2v = or2v; + old_item = oi; + + cidx++; + } + } + + //continue on + oidx++; + } + + ASSERT(real_index!=-1); + + //now we need to fix m_Virt2Real to represent the new structure, + //all values>=real_index must be incremented + cidx = 0; + for(oidx=0;oidx<=m_NumItems;oidx++){ + if(oidx!=real_index){ + if(m_Virt2Real[cidx]>=real_index) + m_Virt2Real[cidx]++; + cidx++; + } + } + } + } + + // copy item passed to list. + m_ItemList[real_index] = mem_strdup(name); + m_Virt2Real[m_NumItems] = real_index; + m_Real2Virt[real_index] = m_NumItems; + m_NumItems++; + + if(real_index<=m_Index) + { + //adjust selected index + if(m_Index=real_index){ + m_ItemList[j] = m_ItemList[j+1]; + m_Real2Virt[j] = m_Real2Virt[j+1]; + } + + if(j>=virt_index){ + m_Virt2Real[j] = m_Virt2Real[j+1]; + } + + if(m_Real2Virt[j]>=real_thresh) + m_Real2Virt[j]--; + + if(m_Virt2Real[j]>=virt_thresh) + m_Virt2Real[j]--; + } + m_NumItems--; + + if(real_index<=m_Index) + { + //adjust selected index + m_Index--; + } +} + + +void newuiComboBox::RemoveAll() +{ + if(m_ItemList) { + int i; + for (i = 0; i < m_NumItems; i++) + { + if (m_ItemList[i]) + mem_free(m_ItemList[i]); + } + mem_free(m_ItemList); + } + + if(m_Virt2Real) + mem_free(m_Virt2Real); + + if(m_Real2Virt) + mem_free(m_Real2Virt); + + m_ItemList = NULL; + m_Virt2Real = NULL; + m_Real2Virt = NULL; + m_Index = m_NumItems = 0; +} + + +// item information +int newuiComboBox::GetCurrentIndex() +{ + if(m_NumItems==0) + return 0; + + if(!m_Real2Virt) + return 0; + + int virt_index = m_Real2Virt[m_Index]; + ASSERT(virt_index>=0 && virt_index=0 && index < m_NumItems) { + int real_index = m_Virt2Real[index], old_index = m_Index; + + ASSERT(real_index>=0 && real_index=0 && index < m_NumItems) { + int real_index = m_Virt2Real[index]; + ASSERT(real_index>=0 && real_indexdraw(0,0); + bool auto_select = ((m_Flags&UILB_AUTOSELECT)>0); + bool use_scroll = true; + + ui_SetTextClip(m_boffs, 1,m_W-m_boffs-m_up_btn.W(), m_H-1); + +// decide whether to show arrows. , tricky code. + if (m_NumItems > 1) { + if (m_Index < (m_NumItems-1) && !m_showdown) { + m_showdown = true; + m_down_btn.Show(true); + } + if (m_Index > 0 && !m_showup) { + m_showup = true; + m_up_btn.Show(true); + } + } + + if (m_Index == (m_NumItems-1) && m_showdown) { + m_showdown = false; + m_down_btn.Show(false); + } + if (m_Index == 0 && m_showup) { + m_showup = false; + m_up_btn.Show(false); + } + +// use gamma 192 for non focus. + if (m_NumItems && m_Index < m_NumItems && m_Index >= 0) { + UITextItem text(MONITOR9_NEWUI_FONT, m_ItemList[m_Index], NEWUI_MONITORFONT_COLOR); + text.draw(m_boffs+8, 4); + } + + ui_ResetTextClip(); +} + + +// behavior when key is pressed. +void newuiComboBox::OnKeyDown(int key) +{ + int new_selected_index = m_Index; + + if (key == KEY_UP) { + if (m_Index > 0) { + new_selected_index = m_Index-1; + } + } + else if (key == KEY_DOWN) { + if (m_Index < (m_NumItems-1)) { + new_selected_index = m_Index+1; + } + } + newuiComboBox::SetCurrentIndex((m_Real2Virt)?m_Real2Virt[new_selected_index]:0); +} + + +// this function will handle when an arrow button was pressed +void newuiComboBox::OnNotifySelect(UIGadget *g) +{ + switch (g->GetDatum()) + { + case NEWUI_ARROW_CBUP: + newuiComboBox::OnKeyDown(KEY_UP); + break; + case NEWUI_ARROW_CBDOWN: + newuiComboBox::OnKeyDown(KEY_DOWN); + break; + } +} + + +// when a listbox item was double clicked (or entered) +void newuiComboBox::OnSelect() +{ + UIGadget::OnSelect(); +} + +// called when destroyed. +void newuiComboBox::OnDestroy() +{ + int i; + + if (m_up_btn.IsCreated()) + m_up_btn.Destroy(); + if (m_down_btn.IsCreated()) + m_down_btn.Destroy(); + + for (i = 0; i < m_NumItems; i++) + { + mem_free(m_ItemList[i]); + } + + if (m_ItemList) + mem_free(m_ItemList); + if (m_Real2Virt) + mem_free(m_Real2Virt); + if (m_Virt2Real) + mem_free(m_Virt2Real); + + m_ItemList = NULL; + m_Real2Virt = NULL; + m_Virt2Real = NULL; + + + Newui_resources.Free(m_barbmp); + + UIGadget::OnDestroy(); +} + + +// when gadget is added to a window (AddGadget is called) +void newuiComboBox::OnAttachToWindow() +{ + m_boffs = 16; + m_up_btn.Create(m_Wnd, -1, NEWUI_ARROW_CBUP, NULL, m_X+4, m_Y+3); + m_up_btn.SetFlag(UIF_NOTIFYMASTERSEL); + m_showup = true; + AttachSlaveGadget(&m_up_btn); +// m_showup = false; +// m_Wnd->RemoveGadget(&m_up_btn); + + m_down_btn.Create(m_Wnd, -1, NEWUI_ARROW_CBDOWN, NULL, m_X+m_W-m_boffs-8, m_Y+3); + m_down_btn.SetFlag(UIF_NOTIFYMASTERSEL); + AttachSlaveGadget(&m_down_btn); + m_showdown = true; +// m_showdown = false; +// m_Wnd->RemoveGadget(&m_down_btn); +} + + +void newuiComboBox::OnDetachFromWindow() +{ + if (m_showdown) { + DetachSlaveGadget(&m_down_btn); + m_showdown = false; + } + m_down_btn.Destroy(); + if (m_showup) { + DetachSlaveGadget(&m_up_btn); + m_showup = false; + } + m_up_btn.Destroy(); +} + + + +// CLASS newuiEditBox + +newuiEditBox::newuiEditBox() +{ + m_title = NULL; +} + + +void newuiEditBox::Create(UIWindow *wnd, short id, const char *name, short x, short y, short w, short flags) +{ +// if (name) { +// m_title = new UISnazzyTextItem(0, MONITOR9_NEWUI_FONT, name, NEWUI_MONITORFONT_COLOR); +// } + UIEdit::Create(wnd, (int)id, x, y, w ? w : EDIT_GADGET_WIDTH, EDIT_GADGET_HEIGHT, flags | UIF_BORDER); + +// if (m_title) { +// UIEdit::SetTextBounds(m_title->width()+12, m_W-1); +// } +// else { + UIEdit::SetTextBounds(1, m_W-1, -1); +// } + + UIEdit::SetColor(NEWUI_MONITORFONT_COLOR); + UIEdit::SetFont(MONITOR9_NEWUI_FONT); + + m_quick_escape_enable = false; +} + + +void newuiEditBox::EnableOnQuickEscapeBehavior(bool do_it) +{ + m_quick_escape_enable = do_it; +} + + +void newuiEditBox::OnDestroy() +{ + if (m_title) { + delete m_title; + m_title = NULL; + } + + UIEdit::OnDestroy(); +} + + +void newuiEditBox::OnDraw() +{ + ui_DrawBox(GR_RGB(128,128,128),0,0,m_W,m_H); + if (m_title) { + if (HasFocus()) { + m_title->set_flags(UISNAZZYTEXTF_BLINKING); + } + else { + m_title->set_flags(0); + } + m_title->draw(0,0); + } + + DrawText(); +} + + +void newuiEditBox::OnKeyDown(int key) +{ + UIEdit::OnKeyDown(key); + if (key == KEY_ESC && m_quick_escape_enable) { + UIEdit::SetText(NEWUI_EDIT_CANCELED_STR); + OnSelect(); + } +} + + + +// the new classic hotspot CLASS + +newuiHotspot::newuiHotspot() +{ + m_title = NULL; +} + + +void newuiHotspot::Create(UIWindow *wnd, short id, const char *title, short x, short y, short w, short h, short flags) +{ + if (title) { + m_title = mem_strdup(title); + } + else { + m_title = NULL; + } + + m_alpha = 0; + m_alpha_adjust = 0; + m_mse_timer = -1.0f; + + UIGadget::Create(wnd, id, x, y, w, h, flags); +} + + +void newuiHotspot::OnDraw() +{ + static char buf[256]; + char *strptr; + int y = 2, x= 2; + + textaux_WordWrap(m_title, buf, m_W-22, MONITOR9_NEWUI_FONT); + + strptr = strtok(buf, "\n"); + while (strptr) + { + UITextItem text(MONITOR9_NEWUI_FONT, strptr, NEWUI_MONITORFONT_COLOR); + ddgr_color col = (m_alpha_adjust!=0) ? GR_RGB(160+m_alpha,160+m_alpha,0+m_alpha) : NEWUI_MONITORFONT_COLOR; + text.set_color(col); + text.set_alpha((m_alpha_adjust!=0) ? 255 : 128); + text.draw(x, y); + y += text.height(); + x = 20; + + strptr = strtok(NULL, "\n"); + } + + if (m_alpha_adjust!=0) { + m_alpha += m_alpha_adjust; + if (m_alpha < 0) { + m_alpha = 0; + m_alpha_adjust = 6; + } + else if (m_alpha > 94) { + m_alpha = 94; + m_alpha_adjust = -6; + } + } + if (!m_infocus) { + m_alpha_adjust = 0; + m_alpha = 0; + } +} + + +void newuiHotspot::OnDestroy() +{ + if (m_title) { + mem_free(m_title); + m_title =NULL; + } + + UIGadget::OnDestroy(); +} + + +void newuiHotspot::OnMouseBtnDown(int btn) +{ + if (btn == UILMSEBTN) { + LOCK_FOCUS(this); + if (m_mse_timer > -1.0f) { + if ((UI_TIME() - m_mse_timer) < UI_DBLCLICK_DELAY) { + if (PT_IN_GADGET(UIGadget::m_Wnd, this, UI_input.mx, UI_input.my)) { + UNLOCK_FOCUS(this); + OnSelect(); + } + } + m_mse_timer = -1.0f; + } + } +} + + +void newuiHotspot::OnMouseBtnUp(int btn) +{ + if (btn == UILMSEBTN) { + UNLOCK_FOCUS(this); + + if (PT_IN_GADGET(UIGadget::m_Wnd, this, UI_input.mx, UI_input.my)) { + if (m_mse_timer == -1.0f) { + m_mse_timer = UI_TIME(); + } + } + } +} + + +void newuiHotspot::OnGainFocus() +{ + m_alpha = 0; + m_alpha_adjust = 6; +} + + +void newuiHotspot::OnLostFocus() +{ + m_alpha = 0; + m_alpha_adjust = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CLASS, sizable window + +newuiTiledWindow::newuiTiledWindow() +{ + m_startui_cb = NULL; + m_endui_cb = NULL; + m_draw_cb = NULL; + m_onframe_cb = NULL; + m_data = NULL; + m_title[0] = 0; + m_realized = false; +} + + +void newuiTiledWindow::Create(const char *title, short x, short y, short w, short h, int flags) +{ + if (title) { + ASSERT(strlen(title) < (sizeof(m_title)-1)); + } + +// adjust w and height acoordingly with bitmap background width and height. + int bk_width = (w / TW_PIECE_WIDTH) + ((w % TW_PIECE_WIDTH) ? 1 : 0); + int bk_height = (h / TW_PIECE_HEIGHT) + ((h % TW_PIECE_HEIGHT) ? 1 : 0); + + if (bk_width < 4) bk_width = 4; + if (bk_height < 4) bk_height = 4; + + w = bk_width * TW_PIECE_WIDTH; + h = bk_height * TW_PIECE_HEIGHT; + + UIWindow::Create(x, y, w, h, flags | UIF_CENTER); + + if (title) { + strcpy(m_title, title); + } + else { + m_title[0] = 0; + } + +// initialze stuff + m_startui_cb = NULL; + m_endui_cb = NULL; + m_draw_cb = NULL; + m_onframe_cb = NULL; + m_data = NULL; + m_realized = false; + + m_sheet.Create(this, NULL, 96, NEWUI_TILED_SHEET_X, title ? NEWUI_TILED_SHEET_Y : NEWUI_TILED_TITLE_Y); + +// graphics. + m_pieces[N_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_N_FILE); + m_pieces[NE_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_NE_FILE); + m_pieces[E_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_E_FILE); + m_pieces[SE_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_SE_FILE); + m_pieces[S_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_S_FILE); + m_pieces[SW_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_SW_FILE); + m_pieces[W_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_W_FILE); + m_pieces[NW_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_NW_FILE); + m_pieces[C_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_C_FILE); + + m_pieces[TL_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_TITLE_L_FILE); + m_pieces[TC_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_TITLE_C_FILE); + m_pieces[TR_PIECE_IDX] = Newui_resources.Load(NEWUI_WIN_TITLE_R_FILE); +} + + +// grab a newui interface from it. +newuiSheet *newuiTiledWindow::GetSheet() +{ + return &m_sheet; +} + + +// do user interface on it. +int newuiTiledWindow::DoUI() +{ +// start ui. + UI_frame_result = -1; + ui_ShowCursor(); + + if (m_startui_cb) { + (*m_startui_cb)(this, m_data); + } + + if (!m_realized) { + m_sheet.Realize(); + m_realized = true; + } + +// refreshes gadgets on current sheet. + m_sheet.UpdateChanges(); + +// this should poll UI_frame_result. + while (UI_frame_result == -1) + { + DebugBlockPrint("UA"); + + Descent->defer(); + DoUIFrame(); + if (m_onframe_cb) { + (*m_onframe_cb)(this, m_data); + } + if (m_draw_cb) { + ui_StartDraw(m_X, m_Y, m_X+m_W, m_Y+m_H); + (*m_draw_cb)(this, m_data); + ui_EndDraw(); + ui_DoCursor(); + } + DebugBlockPrint("UZ"); + rend_Flip(); + } + +// refreshes gadgets on current sheet. + m_sheet.UpdateReturnValues(); + + if (m_endui_cb) { + (*m_endui_cb)(this, m_data); + } + +// cleanup + ui_HideCursor(); + ui_Flush(); + + return UI_frame_result; +} + + +void newuiTiledWindow::SetData(void *data) +{ + m_data = data; +} + + +// set callbacks +// first called when DoUI is called. +void newuiTiledWindow::SetOnStartUICB(void (*fn)(newuiTiledWindow *, void *)) +{ + m_startui_cb = fn; +} + + +// called when DoUI returns +void newuiTiledWindow::SetOnEndUICB(void (*fn)(newuiTiledWindow *, void *)) +{ + m_endui_cb = fn; +} + + +// called before rend_Flip but after ui has drawn. (SetUICallback is before ui has been rendered) +void newuiTiledWindow::SetOnDrawCB(void (*fn)(newuiTiledWindow *, void *)) +{ + m_draw_cb = fn; +} + + +// called after DoUIFrame is called. for immediate results. +void newuiTiledWindow::SetOnUIFrameCB(void (*fn)(newuiTiledWindow *, void *)) +{ + m_onframe_cb = fn; +} + +#include "newui.h" + +//Returns the index of the tile bitmap to draw along the top of the window +int newuiTiledWindow::GetTopTileIndex(int tx,int window_width) +{ + int title_width,left,right; + + //Get size of the title + int flags = GetFlags(); + switch (NEWUI_GETTITLEBAR(flags)) { + case NUWF_TITLENONE: return N_PIECE_IDX; + case NUWF_TITLESMALL: title_width = 2; break; + case NUWF_TITLEMED: title_width = 4; break; + case NUWF_TITLELARGE: title_width = 6; break; + } + + //If one width is odd and other even, then add an extra tile to the title + if ((window_width ^ title_width) & 1) + title_width++; + + //Get positions of left & right edges + left = window_width/2 - title_width/2 - 1; + right = left+title_width+1; + + //Clip to edges + if (left < 0) + left = 0; + if (right >= window_width) + right = window_width-1; + + //Bail if no room for title + if (left >= right) + return N_PIECE_IDX; + + if (tx == left) + return TL_PIECE_IDX; + else if (tx == right) + return TR_PIECE_IDX; + else if ((tx > left) && (tx < right)) + return TC_PIECE_IDX; + else + return N_PIECE_IDX; +} + +void newuiTiledWindow::OnDraw() +{ +// draw tiles. + int tx, ty; + int bk_width = (m_W / TW_PIECE_WIDTH) + ((m_W % TW_PIECE_WIDTH) ? 1 : 0); + int bk_height = (m_H / TW_PIECE_HEIGHT) + ((m_H % TW_PIECE_HEIGHT) ? 1 : 0); + int corner_tw = TW_CORNER_WIDTH/TW_PIECE_WIDTH; + int corner_th = TW_CORNER_HEIGHT/TW_PIECE_HEIGHT; + +// fill background + for (ty = 1; ty < (bk_height-1); ty++) + { + for (tx = 1; tx < (bk_width-1); tx++) + { + m_pieces[C_PIECE_IDX]->draw(tx*TW_PIECE_WIDTH, ty*TW_PIECE_HEIGHT); + } + } + +// do sides + for (ty = corner_th; ty < (bk_height-corner_th); ty++) + { + m_pieces[W_PIECE_IDX]->draw(0, ty*TW_PIECE_HEIGHT); + m_pieces[E_PIECE_IDX]->draw((bk_width-1)*TW_PIECE_WIDTH, ty*TW_PIECE_HEIGHT); + } + + for (tx = corner_tw; tx < (bk_width-corner_tw); tx++) + { + + m_pieces[GetTopTileIndex(tx-corner_tw,bk_width-2*corner_tw)]->draw(tx*TW_PIECE_WIDTH, 0); + m_pieces[S_PIECE_IDX]->draw(tx*TW_PIECE_WIDTH, (bk_height-1)*TW_PIECE_HEIGHT); + } + +// do corners. + m_pieces[NW_PIECE_IDX]->draw(0,0); + m_pieces[NE_PIECE_IDX]->draw((bk_width-corner_tw)*TW_PIECE_WIDTH, 0); + m_pieces[SE_PIECE_IDX]->draw((bk_width-corner_tw)*TW_PIECE_WIDTH, (bk_height-corner_th)*TW_PIECE_HEIGHT); + m_pieces[SW_PIECE_IDX]->draw(0, (bk_height-corner_th)* TW_PIECE_HEIGHT); + + +// do title + UITextItem text(MONITOR15_NEWUI_FONT, m_title, NEWUI_MONITORFONT_COLOR); + text.draw(NEWUI_TILED_TITLE_X, NEWUI_TILED_TITLE_Y); +} + + +void newuiTiledWindow::OnDestroy() +{ + Newui_resources.Free(m_pieces[N_PIECE_IDX]); + Newui_resources.Free(m_pieces[NE_PIECE_IDX]); + Newui_resources.Free(m_pieces[E_PIECE_IDX]); + Newui_resources.Free(m_pieces[SE_PIECE_IDX]); + Newui_resources.Free(m_pieces[S_PIECE_IDX]); + Newui_resources.Free(m_pieces[SW_PIECE_IDX]); + Newui_resources.Free(m_pieces[W_PIECE_IDX]); + Newui_resources.Free(m_pieces[NW_PIECE_IDX]); + Newui_resources.Free(m_pieces[C_PIECE_IDX]); + + Newui_resources.Free(m_pieces[TL_PIECE_IDX]); + Newui_resources.Free(m_pieces[TC_PIECE_IDX]); + Newui_resources.Free(m_pieces[TR_PIECE_IDX]); + + m_startui_cb = NULL; + m_endui_cb = NULL; + m_draw_cb = NULL; + m_onframe_cb = NULL; + m_data = NULL; + + m_sheet.Destroy(); + + UIWindow::OnDestroy(); +} + + diff --git a/Descent3/newui_core.h b/Descent3/newui_core.h new file mode 100644 index 000000000..50faf7e2e --- /dev/null +++ b/Descent3/newui_core.h @@ -0,0 +1,785 @@ +/* + * $Source: $ + * $Revision: 27 $ + * $Author: Samir $ + * $Date: 4/29/99 2:18a $ + * + * new NewUI Header + * + * $Log: /DescentIII/main/newui_core.h $ + * + * 27 4/29/99 2:18a Samir + * updated art for options style menu. + * + * 26 4/28/99 1:54a Samir + * visual tweaks to text + * + * 25 4/27/99 2:13p Matt + * Replaced the two (new) new UI fonts with one new font from Doug. + * + * 24 4/27/99 9:24a Matt + * Added system for drawing title bars on dialogs. + * + * 23 4/26/99 7:46p Samir + * newuiTiledWindow flags passed to create is an int now. + * + * 22 4/21/99 10:58a Samir + * added changable text. + * + * 21 4/20/99 11:46a Samir + * numerous ui 'feel' fixes. + * + * 20 4/18/99 7:55p Samir + * fixed listbox double click and added functions to load and release + * pertinent newui graphic data. + * + * 19 4/15/99 5:28p Samir + * added advanced messagebox + * + * 18 4/05/99 5:13p Samir + * fixed combo box asthetics. + * + */ + +#ifndef NEWUI_CORE_H +#define NEWUI_CORE_H + +#include "ui.h" + +#define N_NEWUI_BMPS 32 // number of ui bitmaps. +#define N_NEWUI_SHEETS 8 // number of sheets allowed in a menu. + +#define NEWUI_MONITORFONT_COLOR GR_GREEN //GR_RGB(255,255,255) +#define NEWUI_GADGETFONT_COLOR GR_BLACK //GR_RGB(255,255,255) + +#define NEWUI_GADGETFONT_HICOLOR GR_RGB(192,192,0) + +#ifndef MSGBOX_OK +// types of message boxes +#define MSGBOX_NULL 0 +#define MSGBOX_OK 1 +#define MSGBOX_OKCANCEL 2 +#define MSGBOX_YESNO 3 +#endif + + +// used internally for ease of management +#define N_DIRECTIONS 12 +#define N_PIECE_IDX 0 +#define NE_PIECE_IDX 1 +#define E_PIECE_IDX 2 +#define SE_PIECE_IDX 3 +#define S_PIECE_IDX 4 +#define SW_PIECE_IDX 5 +#define W_PIECE_IDX 6 +#define NW_PIECE_IDX 7 +#define C_PIECE_IDX 8 +#define TL_PIECE_IDX 9 +#define TC_PIECE_IDX 10 +#define TR_PIECE_IDX 11 + +// types of 'arrow' buttons used in newuiArrowButton +#define NEWUI_ARROW_LEFT 0x1 +#define NEWUI_ARROW_RIGHT 0x2 +#define NEWUI_ARROW_UP 0x3 +#define NEWUI_ARROW_DOWN 0x4 +#define NEWUI_ARROW_CBUP 0x5 +#define NEWUI_ARROW_CBDOWN 0x6 + + + + +// initializes the core system for the newui +void newuiCore_Init(); + +// initializes the core system for the newui +void newuiCore_Close(); + +// touches all newui bitmaps, do before any newui menus open +void newuiCore_PageInBitmaps(); + +// call when NO newui menus open +void newuiCore_ReleaseBitmaps(); + +// C interface to load and free bitmap resources +UIBitmapItem *newui_LoadBitmap(const char *filename); + +// C interface to load and free bitmap resources +void newui_FreeBitmap(UIBitmapItem *bmitem); + +// sets the callback for background rendering of desktop for UI +#define DEFAULT_UICALLBACK ((void (*)())1) + +void SetUICallback(void (*fn)()); +void (*GetUICallback())(); + +// predefined return values for DoUI or UI_frame_result. +#define NEWUIRES_FORCEQUIT -2 + +// does a UI loop: returns a result value for the current window in focus. +int DoUI(); + +// does one frame of UI input (returns result.) +void DoUIFrame(); + +// does one frame of UI without polling input. +void DoUIFrameWithoutInput(); + +// gets frame result of ui input. +int GetUIFrameResult(); + + + +////////////////////////////////////////////////////////////////////////////// + +// CLASS simple buttons + +class newuiSlider; +class newuiCheckBox; +class newuiRadioButton; +class newuiButton; +class newuiMenu; +class newuiListBox; +class newuiEditBox; +class newuiComboBox; +class newuiHotspot; + +#define DEFAULT_NEWUID -5 + +typedef enum tAlignment +{ + NEWUI_ALIGN_VERT, + NEWUI_ALIGN_HORIZ +}; + +#define SLIDER_UNITS_INT 0 +#define SLIDER_UNITS_PERCENT 1 +#define SLIDER_UNITS_FLOAT 2 + +// used optionally for sliders. +typedef struct tSliderSettings +{ + union { + int i; + float f; + } + min_val; + union { + int i; + float f; + } + max_val; + int type; // enumerated above in SLIDER_UNITS +} +tSliderStruct; + + +#define NEWUI_EDIT_CANCELED_STR "\1" // if a newuiEditbox returns this, then we cancelled. invalid string + +#define NEWUI_EDIT_BUFLEN 63 + +#define NEWUI_BTNF_FRAMED UIF_CUSTOMF // when Adding a button, this button have 'framed' art. +#define NEWUI_BTNF_LONG (UIF_CUSTOMF << 1) // button will be a long version (with art) + +// CLASS contains gadgets + +class newuiSheet +{ + int m_sx, m_sy; // origin of sheet gadgets relative to parent. + short m_initial_focus_id; // gadget that will have focus upon realization. + +public: + newuiSheet(); + +// called usually by other classes, used to initialize the sheet + void Create(UIWindow *menu, const char *title, int n_items, int sx, int sy); + +// deallocates memory + void Destroy(); + +// get properties + int X() const { return m_sx; }; + int Y() const { return m_sy; }; + +// resets gadget list + void Reset(); + +// call this to initialize gadgets specified above in parent window + void Realize(); + +// call this to release gadgets specified above in parent window, retreives checkbox,radio,etc values too. + void Unrealize(); + +// ALL following functions that return a pointer, that pointer contains the value of the gadget added +// i.e. checkbox will have a true or false value. you can use this pointer to modify gadget values. +// to validate these modifications, call 'UpdateChanges'. (done internally most of the time) + +// call these functions to determine if a pointer's value has changed after call to UpdateReturnValues (internal) + bool HasChanged(bool *bptr); + bool HasChanged(short *sptr); + bool HasChanged(int *iptr); + bool HasChanged(char *cptr); + +// obtain's pointer to gadget of respective item. it is your responsibility to typecast the gadget +// only works if you specified an ID! Also the sheet must have been realized by now. + UIGadget *GetGadget(short id); + +// set focus on this gadget specified by id upon realization. + void SetInitialFocusedGadget(short id); + +// creates gadget list.pix_offset is the pixel offset from the group title to the first control. + void NewGroup(const char *title, short x, short y, tAlignment align=NEWUI_ALIGN_VERT, short pix_offset=-1); + +// adds standard button to current group. (flags see NEWUI_BTNF_xxxx) + void AddButton(const char *title, short id, short flags=0); + +// adds a long button (flags see NEWUI_BTNF_xxxx) + void AddLongButton(const char *title, short id, short flags=0); + +// adds checkbox to current group. initial state of checkbox can be set. + bool *AddCheckBox(const char *title, bool init_state=false, short id=DEFAULT_NEWUID); + +// adds long checkbox to current group. initial state of checkbox can be set. + bool *AddLongCheckBox(const char *title, bool init_state=false, short id=DEFAULT_NEWUID); + +// adds a radio button to current group. initial state of radio may be set +// pointer returned will return index of radio button in group currently selected. + int *AddFirstRadioButton(const char *title, short id=DEFAULT_NEWUID); + +// adds a radio button to current group. initial state of radio may be set + void AddRadioButton(const char *title, short id=DEFAULT_NEWUID); + +// adds a radio button to current group. initial state of radio may be set + int *AddFirstLongRadioButton(const char *title, short id=DEFAULT_NEWUID); + +// adds a radio button to current group. initial state of radio may be set + void AddLongRadioButton(const char *title, short id=DEFAULT_NEWUID); + +// adds a slider, set the range for it too., returns two values, short[0] = position, short[1] = range + short *AddSlider(const char *title, short range, short init_pos=0, tSliderSettings *settings=NULL, short id=DEFAULT_NEWUID); + +// adds a static text item + void AddText(const char *text, ...); + +// adds a static bitmap + void AddBitmap(int bm_handle); + +// adds a static text item + char *AddChangeableText(int buflen); + +// adds a listbox + newuiListBox *AddListBox(short w, short h, short id, ushort flags = 0); + +// adds a listbox + newuiComboBox *AddComboBox(short id, ushort flags = 0); + +// adds an edit box + char *AddEditBox(const char *title, short maxlen=NEWUI_EDIT_BUFLEN, short w=0, short id=DEFAULT_NEWUID, short flags=0, bool return_on_esc=false); + +// adds a hotspot :(, should word wrap too. + void AddHotspot(const char *title, short w, short h, short id, bool group=false); + + +// THESE FUNCTIONS ARE CALLED BY FRAMEWORK, BUT IF YOU NEED TO DO SOME CUSTOM UI HANDLING, THESE +// FUNCTIONS ARE MADE PUBLIC. +public: +// refreshes gadget states with values passed to the pointers returned by the below functions +// depends on no direct manipulation of gadgets outside framework. + void UpdateChanges(); + +// refreshes return values of gadgets, so they are accessible by the pointers returned by below functions +// if a gadget has changed since last call to DoUI, the user can tell this by calling HasChanged + void UpdateReturnValues(); + +private: + struct t_gadget_desc + { + sbyte type; // enumerated ui gadget type + bool changed; // parameters are different than defaults? + short id; // id value + char *title; // title of gadget + union + { + int i; + short s[2]; + bool b; + void *p; + } + parm; // parameter list. + union + { + UIGadget *gadget; + UIText *text; + newuiSlider *slider; + newuiCheckBox *chbox; + newuiRadioButton *radio; + newuiButton *button; + newuiListBox *lb; + newuiComboBox *cb; + newuiEditBox *edit; + newuiHotspot *hot; + } + obj; + void *internal; // internal info. + } + *m_gadgetlist; + + UIWindow *m_parent; + int m_ngadgets, m_gadgetlimit; + bool m_realized; + char *m_title; + + t_gadget_desc *AddGadget(short id, sbyte type, const char *title); + +public: + const char *GetTitle() { return m_title; }; + +}; + + + +// CLASS new button class. + +class newuiButton: virtual public UIButton +{ +public: + newuiButton(); + +// see (NEWUI_BTNF_xxx) flags + void Create(UIWindow *wnd, short id, const char *name, short x, short y, short flags=0); + +protected: + virtual void OnDraw(); // overridable draws the background first + virtual void OnDestroy(); // override: frees memory and such. + virtual void OnFormat(); // new formatting + virtual void OnLostFocus(); + virtual void OnGainFocus(); + + UIBitmapItem *m_bkg, *m_litbkg; // used to define visual characteristics of button + UISnazzyTextItem *m_text; + +// this is used in multiple inheritance cases.(flags are additional attributes usually passed when creating gadget) + void InitStates(const char *name, bool is_long, short flags=0); + +public: + UISnazzyTextItem *GetTitle() { return m_text; }; +}; + + +// CLASS a large option button used in menus. + +class newuiMenuOptionButton: public newuiButton +{ +public: + void Create(newuiMenu *menu, newuiMenuOptionButton *last, short id, const char *name, short x, short y, bool m_mono_press); + +protected: + void OnMouseBtnDown(int btn); + void OnMouseBtnUp(int btn); + virtual void OnKeyDown(int key); + virtual void OnKeyUp(int key); + +private: + bool m_mono_press; + newuiMenuOptionButton *m_prev_btn, *m_next_btn; +}; + + +class newuiTinyButton: public newuiButton +{ +public: + void Create(UIWindow *wnd, short id, const char *name, short x, short y); +}; + + + +// CLASS creates an arrow button that is sensitive to touch (when down, always select) + +class newuiArrowButton: public newuiButton +{ +public: + newuiArrowButton() {}; + + void Create(UIWindow *wnd, short id, short type, const char *name, short x, short y); + void Show(bool show=true); // this will activate or deactivate a button. + +protected: + virtual void OnMouseBtnDown(int btn); + virtual void OnMouseBtnUp(int btn); + virtual void OnDraw(); + +private: + float m_timer, m_selecttimer; + bool m_hidden; +}; + + + +// CLASS a new listbox. uses less memory than the old listbox hopefully. + +#define SUBGADGET_UP 0x3 +#define SUBGADGET_DOWN 0x4 + +class newuiListBox: public UIGadget +{ +public: + newuiListBox(); + +// creates listbox + void Create(UIWindow *wnd, short id, short x, short y, short w, short h, ushort flags); + +// functions to add or remove items, + void AddItem(const char *name); + void RemoveItem(const char *name); + void RemoveAll(); + +// item information + int GetCurrentIndex(); + void SetCurrentIndex(int index); + bool GetCurrentItem(char *buffer, int buflen); + bool GetItem(int index, char *buffer, int buflen); + bool SetCurrentItem(const char *name); + +// set callbacks + void SetSelectChangeCallback(void (*fn)(int)); + void SetSelectChangeCallback(void (*fn)(int,void *),void *ptr); + +protected: + virtual void OnLostFocus(); // override: behavior when gadget loses input focus. + virtual void OnGainFocus(); // override: behavior when gadget gains input focus. + virtual void OnDraw(); // behavior when gadget is being drawn. + virtual void OnKeyDown(int key); // behavior when key is pressed. + virtual void OnKeyUp(int key); // behavior when key is released. + virtual void OnMouseBtnDown(int btn); // behavior when mouse button is pressed. + virtual void OnMouseBtnUp(int btn); // behavior when mouse button is released. + virtual void OnNotifySelect(UIGadget *g); // this function will handle when an arrow button was pressed + virtual void OnSelect(); // when a listbox item was double clicked (or entered) + virtual void OnDestroy(); // called when destroyed. + virtual void OnUserProcess(); // override: behavior when gadget is processed + virtual void OnAttachToWindow(); // when gadget is added to a window (AddGadget is called) + virtual void OnDetachFromWindow(); // when gadget is detached from window + +private: + char **m_ItemList; // list of strings. + int *m_Virt2Real; //translates virtual(user) id to real index + int *m_Real2Virt; //translates real index into virtual(user) id + + void (*selectchange_fn)(int); // callback when selection changes in listbox. + void (*selectchange_id_fn)(int,void *); // callback when selection changes in listbox..also return the ID + void *m_callbackptr; // user defined callback pointer + + int m_NumItems; // number of items in list. + int m_SelectedIndex; // current selected index into listbox + int m_Index; // current index of visible items. + int m_NumVisibleItems; // number of items visible in listbox + int m_boffs; // offset from edge to text region (border width) + int m_mse_x, m_mse_y; // mouse x and y of last click. + int m_last_mse_x, m_last_mse_y; + int m_last_selected_index; + float m_click_time; // time in between clicks. + sbyte m_nclicks; + bool m_mse_clicked; + + + newuiArrowButton m_up_btn; // buttons to scroll. + newuiArrowButton m_down_btn; + + UIBitmapItem *m_pieces[N_DIRECTIONS]; // bitmap pieces + + void Offset(short offs); + void SetInternalCurrentIndex(int index); +}; + + + +// CLASS newuiComboBox + +class newuiComboBox: public UIGadget +{ +public: + newuiComboBox(); + +// creates listbox + void Create(UIWindow *wnd, short id, short x, short y, ushort flags); + +// functions to add or remove items, + void AddItem(const char *name); + void RemoveItem(const char *name); + void RemoveAll(); + +// item information + int GetCurrentIndex(); + void SetCurrentIndex(int index); + bool GetCurrentItem(char *buffer, int buflen); + bool GetItem(int index, char *buffer, int buflen); + bool SetCurrentItem(const char *name); + +// set callbacks + void SetSelectChangeCallback(void (*fn)(int)); + +protected: + virtual void OnDraw(); // behavior when gadget is being drawn. + virtual void OnKeyDown(int key); // behavior when key is pressed. + virtual void OnNotifySelect(UIGadget *g); // this function will handle when an arrow button was pressed + virtual void OnSelect(); // when a listbox item was double clicked (or entered) + virtual void OnDestroy(); // called when destroyed. + virtual void OnAttachToWindow(); // when gadget is added to a window (AddGadget is called) + virtual void OnDetachFromWindow(); // when gadget is detached from window + +private: + char **m_ItemList; // list of strings. + int *m_Virt2Real; //translates virtual(user) id to real index + int *m_Real2Virt; //translates real index into virtual(user) id + + void (*selectchange_fn)(int); // callback when selection changes in listbox. + + int m_NumItems; // number of items in list. + int m_Index; // current index of visible items. + int m_boffs; // offset from edge to text region (border width) + bool m_showup, m_showdown; + + newuiArrowButton m_up_btn; // buttons to scroll. + newuiArrowButton m_down_btn; + + UIBitmapItem *m_barbmp; // bitmap pieces +}; + + +// the new classic hotspot CLASS + +class newuiHotspot: public UIGadget +{ +private: + char *m_title; + short m_alpha, m_alpha_adjust; + float m_mse_timer; + +public: + newuiHotspot(); + + void Create(UIWindow *wnd, short id, const char *title, short x, short y, short w, short h, short flags=0); + const char *GetTitle() const { return m_title; }; + + +protected: + virtual void OnDraw(); // this will use the newuiButton drawing scheme + virtual void OnDestroy(); + virtual void OnMouseBtnDown(int btn); + virtual void OnMouseBtnUp(int btn); + virtual void OnGainFocus(); + virtual void OnLostFocus(); +}; + + +/////////////////////////////////////////////////////////////////////////////// + +// CLASS, contains options buttons and sheets of ui stuff + +#define NEWUIMENU_SMALL 0x10 +#define NEWUIMENU_MEDIUM 0x20 +#define NEWUIMENU_LARGE 0x40 + +class newuiMenu: public UIWindow +{ +public: + newuiMenu(); + +// creates a menu + void Create(); + +// adds an option to a menu, returns a newuiSheet object to add user interface to. + newuiSheet *AddOption(short id, const char *title, int size=NEWUIMENU_SMALL, bool has_sheet=true, int yoff=0); + void AddSimpleOption(short id, const char *title, int yoff=0); + +// sets current option to display. + void SetCurrentOption(short id); + +// returns current option + short GetCurrentOption() const; + +// returns current sheet + newuiSheet *GetCurrentSheet() const; + +// set master sheet,button locations + void SetPlacements(int x, int y, int options_x, int options_y, int options_w, int options_h, + int title_x, int title_y, tAlignment align=NEWUI_ALIGN_VERT); + +// processes a menu + int DoUI(); + +// THESE FUNCTIONS DEAL WITH CALLBACK PROCEDURES +public: +// when a new option is realized, this function will be called. +// passed in: the menu object, the old option and the new option and an optional pointer respectively + void SetOnOptionSwitchCB(void (*fn)(newuiMenu *, short,short, void*), void *data); + +// when a new option is ready. at this point the system sets the focus on this option + void SetOnOptionFocusCB(void (*fn)(newuiMenu *,short, void *), void *data); + +// equivalent of SetUICallback, called before gadgets are drawn + void SetOnUIFrameCB(void (*fn)()); + +protected: + virtual void OnDraw(); // overridable draws the window background before gadgets + virtual void OnDestroy(); // overridable: called in Destroy + + UIBitmapItem *m_bkg; // background + +private: + newuiMenuOptionButton m_sheetbtn[N_NEWUI_SHEETS]; + newuiSheet m_sheets[N_NEWUI_SHEETS]; + bool m_hassheet[N_NEWUI_SHEETS]; + newuiSheet *m_cursheet; // current sheet + int m_nsheets, m_sheetx, m_sheety; // sheet characteristics + int m_optionsw, m_optionsh; + int m_optionsx, m_optionsy; // option button placements + int m_titlex, m_titley; // title location. + tAlignment m_align; // alignment of text + short m_newoptionid; // tells that a new option has been selected. + short m_cursheetidx; + bool m_refreshgadgets; // refreshes gadgets on current sheet. + + void (*m_activate_sheet_cb)(newuiMenu *, short, short, void *); + void (*m_option_focus_cb)(newuiMenu *, short, void *); + void (*m_uiframe_cb)(); + + void *m_activate_sheet_cb_data; + void *m_option_focus_cb_data; +}; + + + +// CLASS, large (fullscreen) menu + +class newuiLargeMenu: public newuiMenu +{ +public: +// creates a menu + void Create(); + +// adds an option to a menu, returns a newuiSheet object to add user interface to. + newuiSheet *AddOption(short id, const char *title, int size=NEWUIMENU_MEDIUM); +}; + + + +// CLASS, small message box +#define NEWUI_MSGBOX_SHEET_W 200 +#define NEWUI_MSGBOX_MAXBTNS 4 + +class newuiMessageBox: public UIWindow +{ +public: + newuiMessageBox(); + + void Create(const char *title, int type=MSGBOX_OK); + + void AddButton(const char *title, int slot, int key=0); + +// grab the message box's sheet. + newuiSheet *GetSheet(); + +// do user interface, return UID_OK or UID_CANCEL or UID_??? (UID_OK and UID_CANCEL are predefined) + int DoUI(); + +protected: + virtual void OnDraw(); // overridable draws the window background before gadgets + virtual void OnDestroy(); + +private: + newuiSheet m_sheet; + newuiButton m_btn[NEWUI_MSGBOX_MAXBTNS]; + + UIBitmapItem *m_bkg; +}; + + +// CLASS, sizable window + +class newuiTiledWindow: public UIWindow +{ +public: + newuiTiledWindow(); + + void Create(const char *title, short x, short y, short w, short h, int flags=0); + +// grab a newui interface from it. + newuiSheet *GetSheet(); + +// do user interface on it. + int DoUI(); + +// set callbacks, SetData keeps a pointer that is passed to all callbacks + void SetData(void *data); + +// first called when DoUI is called. + void SetOnStartUICB(void (*fn)(newuiTiledWindow *, void *)); + +// called when DoUI returns + void SetOnEndUICB(void (*fn)(newuiTiledWindow *, void *)); + +// called before rend_Flip but after ui has drawn. (SetUICallback is before ui has been rendered) + void SetOnDrawCB(void (*fn)(newuiTiledWindow *, void *)); + +// called after DoUIFrame is called. for immediate results. + void SetOnUIFrameCB(void (*fn)(newuiTiledWindow *, void *)); + +protected: + virtual void OnDraw(); + virtual void OnDestroy(); + +private: + UIBitmapItem *m_pieces[N_DIRECTIONS]; // bitmap pieces + newuiSheet m_sheet; + char m_title[64]; + void *m_data; + bool m_realized; + + void (*m_startui_cb)(newuiTiledWindow *, void *); + void (*m_endui_cb)(newuiTiledWindow *, void *); + void (*m_draw_cb)(newuiTiledWindow *, void *); + void (*m_onframe_cb)(newuiTiledWindow *, void *); + + int GetTopTileIndex(int tx,int window_width); +}; + + +// inlines and macros + +#define F_APPROXIMATE(_f) ((_f)+0.000001f) + +inline short CALC_SLIDER_POS_FLOAT(float val, const tSliderSettings *settings, short range) +{ + short curpos; + curpos = (short)F_APPROXIMATE(((val - settings->min_val.f) * range)/(settings->max_val.f-settings->min_val.f)); + return curpos; +} + +inline short CALC_SLIDER_POS_INT(int val, const tSliderSettings *settings, short range) +{ + short curpos; + float num = (float)((val-settings->min_val.i)*range); + float dem = (float)((settings->max_val.i - settings->min_val.i)); + curpos = (short)F_APPROXIMATE(num/dem); + return curpos; +} + + +inline float CALC_SLIDER_FLOAT_VALUE(short val, float min, float max, short range) +{ + float retval; + retval = F_APPROXIMATE((max-min)*val/range) + min; + return retval; +} + + +inline int CALC_SLIDER_INT_VALUE(short val, int min, int max, short range) +{ + int retval; + float num = (float)((max-min)*val); + retval = (int)(F_APPROXIMATE(num/range)) + min; + return retval; +} + + +#endif \ No newline at end of file diff --git a/Descent3/newui_filedlg.cpp b/Descent3/newui_filedlg.cpp new file mode 100644 index 000000000..1f365c8f1 --- /dev/null +++ b/Descent3/newui_filedlg.cpp @@ -0,0 +1,830 @@ +/* +* $Logfile: /DescentIII/Main/newui_filedlg.cpp $ +* $Revision: 22 $ +* $Date: 10/25/99 10:46a $ +* $Author: Matt $ +* +* File Dialog using NewUI class +* +* $Log: /DescentIII/Main/newui_filedlg.cpp $ + * + * 22 10/25/99 10:46a Matt + * Code from Duane that allows users to double-click on a directory in the + * file dialog to open that folder. + * + * 21 9/07/99 4:33p Jeff + * fixed crash due to not setting a pointer to NULL + * + * 20 5/10/99 10:23p Ardussi + * changes to compile on Mac + * + * 19 5/10/99 9:21p Samir + * Ardussi pointed out this small but significant error when checking for + * valid path. + * + * 18 4/20/99 3:11p Jeff + * handle invalid initial directory + * + * 17 4/20/99 3:07p Jeff + * save last directory + * + * 16 4/20/99 11:46a Samir + * numerous ui 'feel' fixes. + * + * 15 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 14 2/28/99 6:14p Jeff + * use UID_OK and UID_CANCEL + * + * 13 2/28/99 2:57a Jeff + * got rid of useless hotspots + * + * 12 2/28/99 2:23a Jeff + * use new ui + * + * 11 1/29/99 5:22p Jeff + * localization + * + * 10 1/27/99 5:47p Jeff + * put up a message that files are being gathered...ease the fear of crash + * + * 9 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 8 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 7 9/28/98 4:35p Jeff + * general UI changes and improvements + * + * 6 9/23/98 6:19p Jeff + * finished up (hopefully) updating the config/ui dialogs to meet our + * standard. Keyboard/joystick config still needs some work + * + * 5 9/02/98 2:54p Jeff + * added defines for text colors to be used throughout the game...fixed up + * buddy menu too + * + * 4 8/19/98 2:18p Jeff + * changed ddio_CleanPath + * + * 3 7/30/98 12:31p Jeff + * + * 2 7/29/98 5:39p Jeff + * initial creation +* +* $NoKeywords: $ +*/ +#include "mono.h" +#include "renderer.h" +#include "render.h" +#include "ddio.h" +#include "descent.h" +#include "game.h" +#include "CFILE.H" +#include "application.h" +#include +#include +#include "newui.h" +#include "grtext.h" +#include "gamefont.h" +#include "stringtable.h" +#include "ConfigItem.h" //for colors +#include "mem.h" + +newuiListBox *path_list=NULL; +char *path_edit=NULL; +char *working_filename=NULL; +char *working_listpath=NULL; +newuiSheet *fdlg_working_sheet = NULL; + +char NewuiFileDlg_lastpath[_MAX_PATH] = {'\0'}; + + +//border size of the UI window +#define UI_BORDERSIZE 20 + +//returns the number of files in path p, that matches wildcard +//it fills in buffer (up to maxcount) with the files found +int GetFilesInPath(char **buffer,int maxcount,const char *p,const char *wildcard); + +//returns the number of directories in path p +//it fills in buffer (up to maxcount) with the directories found +int GetDirectoriesInPath(char **buffer,int maxcount,const char *p); + +//Updates the given listbox with the directories and files that match wildcards (each wildcard seperates by a ;) +void UpdateFileList(newuiListBox *lb,const char *path,char *wildcards); + +//Callback function for listbox +void FileSelectCallback(int index); + + +void FDlg_EnableWaitMessage(bool enable) +{ +#define FDWM_HEIGHT 128 +#define FDWM_WIDTH 256 + + static NewUIGameWindow *msgbox = NULL; + static bool opened = false; + newuiSheet *sheet; + + if(opened == enable){ + Int3(); + } + + opened = enable; + + if(enable){ + msgbox = new NewUIGameWindow; + if(msgbox){ + + msgbox->Create(0,0,FDWM_WIDTH,FDWM_HEIGHT,UIF_PROCESS_ALL|UIF_CENTER); + + sheet = msgbox->GetSheet(); + sheet->NewGroup(NULL, 45, 25); + sheet->AddText(TXT_GETTINGFILES); + + msgbox->Open(); + sheet->Realize(); + + //fake a DoUI + DoUIFrame(); + rend_Flip(); + } + }else{ + + if(msgbox){ + msgbox->Close(); + msgbox->Destroy(); + delete msgbox; + } + msgbox = NULL; + } +} + +bool IsDirectoryItem(char *item) +{ + if(*item!=' ') + return false; + if(*(item+1)!='[') + return false; + if(item[strlen(item)-1]==']') + return true; + return false; +} +//Callback function for listbox +void FileSelectCallback(int index) +{ + if( (!path_edit) || (!working_filename) || (!path_list) || (!working_listpath) ) + return; + + char file[_MAX_PATH]; + path_list->GetCurrentItem(file,_MAX_PATH); + if(IsDirectoryItem(file)) + return; //we don't care about directories + + strncpy(working_filename,file,_MAX_FNAME-1); + working_filename[_MAX_FNAME-1] = '\0'; + + ddio_MakePath(file,working_listpath,working_filename,NULL); + + strncpy(path_edit,file,_MAX_PATH-1); + path_edit[_MAX_PATH-1] = '\0'; + mprintf((0,"New Path: %s\n",path_edit)); + fdlg_working_sheet->UpdateChanges(); +} + +// Displays a file/directory dialog box +// path - Initial path, will contain the chosen file/path on exit with true, must be at least _MAX_PATH in size +// title - Title of the dialog +// wildc - semicolon (;) seperated list of wildcards to be shown +// flags - see header PFDF_ for flags +bool DoPathFileDialog(bool save_dialog,char *path,char *title,char *wildc,int flags) +{ +#define HS_CANCEL 0 +#define HS_OK 1 +#define HS_UPDIR 0 +#define ED_WILDCARD 0 +#define ED_FILENAME 1 +#define ID_LISTBOX 25 +#define ID_UPDIR 26 +#define ID_WILDCARD 27 +#define ID_FILENAME 28 +#define ID_CANCEL UID_CANCEL +#define ID_OK UID_OK + newuiTiledWindow window; + newuiSheet *sheet; + newuiListBox *listbox; + char *edits[2]; + + char wildcards[256]; + char working_path[_MAX_PATH]; + char working_file[_MAX_FNAME]; + + if(!path) + return false; + if(!title) + return false; + if(!wildc) + return false; + + strcpy(wildcards,wildc); + strcpy(working_file,""); + + //figure out the correct path + char oldpath[_MAX_PATH]; + ddio_GetWorkingDir(oldpath,_MAX_PATH); + if(!ddio_SetWorkingDir(path)) + { + //try to use last good path + if(!ddio_SetWorkingDir(NewuiFileDlg_lastpath)) + { + strcpy(path,LocalD3Dir); + }else + { + strcpy(path,NewuiFileDlg_lastpath); + } + } + ddio_SetWorkingDir(oldpath); + + //Create all the UI Items + window.Create(title,0,0,384,288); + fdlg_working_sheet = sheet = window.GetSheet(); + + sheet->NewGroup(NULL,10,0); + listbox = sheet->AddListBox(268,100,ID_LISTBOX); + + sheet->NewGroup(NULL,0,145); + edits[ED_FILENAME] = sheet->AddEditBox(NULL,_MAX_PATH,300,ID_FILENAME); + edits[ED_WILDCARD] = sheet->AddEditBox(NULL,256,150,ID_WILDCARD); + sheet->AddLongButton(TXT_UPTOPARENTDIR,ID_UPDIR); + + sheet->NewGroup(NULL,200,160); + sheet->AddButton((save_dialog)?TXT_SAVE:TXT_OPEN,ID_OK); + sheet->AddButton(TXT_CANCEL,ID_CANCEL); + + working_filename = working_file; + working_listpath = working_path; + path_edit = edits[ED_FILENAME]; + path_list = listbox; + + listbox->SetSelectChangeCallback(FileSelectCallback); + + ddio_CleanPath(working_path,path); + UpdateFileList(listbox,working_path,wildcards); + + strncpy(edits[ED_FILENAME],working_path,_MAX_PATH-1); + edits[ED_FILENAME][_MAX_PATH-1] = '\0'; + + strncpy(edits[ED_WILDCARD],wildcards,255); + edits[ED_WILDCARD][255] = '\0'; + + bool ret = false; + bool exit_menu = false; + + sheet->SetInitialFocusedGadget(ID_LISTBOX); + + window.Open(); + while (!exit_menu) + { + int res = window.DoUI(); + + // handle all UI results. + switch (res) + { + case ID_OK: + { + strcpy(path,edits[ED_FILENAME]); + + if(path[0]=='\0'){ + DoMessageBox(TXT_ERROR,TXT_ERRCHOOSEFILE,MSGBOX_OK); + }else{ + if(ddio_DirExists(path)){ + char newpath[_MAX_PATH]; + char file[_MAX_PATH]; + int selection = listbox->GetCurrentIndex(); + if( (selection>=0) && (listbox->GetCurrentItem(file,_MAX_PATH)) ){ + if(*file){ + strcpy(working_file,""); + + //ok the user double clicked on a directory + //convert file without the brackets + file[strlen(file)-1] = '\0'; + if(strlen(working_path)!=0) + ddio_MakePath(newpath,working_path,file+2,NULL); + else + strcpy(newpath,file+2); + + UpdateFileList(listbox,newpath,wildcards); + ddio_CleanPath(working_path,newpath); + + strncpy(edits[ED_FILENAME],working_path,_MAX_PATH-1); + edits[ED_FILENAME][_MAX_PATH-1] = '\0'; + } + } +// DoMessageBox(TXT_ERROR,TXT_ERRCHOOSEFILE,MSGBOX_OK); + }else{ + if(flags&PFDF_FILEMUSTEXIST){ + if(!cfexist(path)){ + DoMessageBox(TXT_ERROR,TXT_ERRFILENOTEXIST,MSGBOX_OK); + }else{ + ddio_SplitPath(path,NewuiFileDlg_lastpath,NULL,NULL); + ret = exit_menu = true; + } + }else + { + ddio_SplitPath(path,NewuiFileDlg_lastpath,NULL,NULL); + ret = exit_menu = true; + } + } + } + } + break; + case ID_CANCEL: + ret = false; + exit_menu = true; + break; + case ID_LISTBOX: + { + char newpath[_MAX_PATH]; + char file[_MAX_PATH]; + //check to see if the double clicked on a dir + int selection = listbox->GetCurrentIndex(); + + if( (selection>=0) && (listbox->GetCurrentItem(file,_MAX_PATH)) ){ + if(*file){ + if(IsDirectoryItem(file)){ + strcpy(working_file,""); + + //ok the user double clicked on a directory + //convert file without the brackets + file[strlen(file)-1] = '\0'; + if(strlen(working_path)!=0) + ddio_MakePath(newpath,working_path,file+2,NULL); + else + strcpy(newpath,file+2); + + UpdateFileList(listbox,newpath,wildcards); + ddio_CleanPath(working_path,newpath); + + strncpy(edits[ED_FILENAME],working_path,_MAX_PATH-1); + edits[ED_FILENAME][_MAX_PATH-1] = '\0'; + }else{ + //double click on a file + strcpy(working_file,file); + FileSelectCallback(selection); + strcpy(file,edits[ED_FILENAME]); + + //the user wants a file + if(flags&PFDF_FILEMUSTEXIST){ + if(cfexist(file)){ + exit_menu = true; + ret = true; + strcpy(path,file); + ddio_SplitPath(path,NewuiFileDlg_lastpath,NULL,NULL); + }else{ + //invalid file + DoMessageBox(TXT_ERROR,TXT_ERRCHOOSEFILE,MSGBOX_OK); + } + }else{ + strcpy(path,file); + exit_menu = true; + ret = true; + ddio_SplitPath(path,NewuiFileDlg_lastpath,NULL,NULL); + } + } + } + } + }break; + case ID_UPDIR: + { + char newpath[_MAX_PATH]; + strcpy(working_file,""); + ddio_GetParentPath(newpath,working_path); + strcpy(working_path,newpath); + + UpdateFileList(listbox,working_path,wildcards); + strncpy(edits[ED_FILENAME],working_path,_MAX_PATH-1); + edits[ED_FILENAME][_MAX_PATH-1] = '\0'; + } + break; + case ID_WILDCARD: + { + strcpy(wildcards,edits[ED_WILDCARD]); + UpdateFileList(listbox,working_path,wildcards); + } + break; + case ID_FILENAME: + { + char c[_MAX_PATH]; + strcpy(c,edits[ED_FILENAME]); + + if(strlen(c)>0){ + if(!ddio_DirExists(c)){ + //the user wants a file + if(flags&PFDF_FILEMUSTEXIST){ + if(cfexist(c)){ + strcpy(path,c); + exit_menu = true; + ret = true; + ddio_SplitPath(path,NewuiFileDlg_lastpath,NULL,NULL); + }else{ + //invalid file + DoMessageBox(TXT_ERROR,TXT_ERRFILENOTEXIST,MSGBOX_OK); + } + }else{ + strcpy(path,c); + ddio_SplitPath(path,NewuiFileDlg_lastpath,NULL,NULL); + exit_menu = true; + ret = true; + } + }else{ + //just change directories + strcpy(working_path,c); + UpdateFileList(listbox,working_path,wildcards); + } + } + } + break; + } + } + + if(ret){ + mprintf((0,"Selected Filename: %s\n",path)); + }else{ + mprintf((0,"Cancel!\n")); + } + + window.Close(); + window.Destroy(); + + working_filename = NULL; + working_listpath = NULL; + path_edit = NULL; + path_list = NULL; + + return ret; +} + + +//returns the number of files in path p, that matches wildcard +//it fills in buffer (up to maxcount) with the files found +int GetFilesInPath(char **buffer,int maxcount,const char *p,const char *wildcard) +{ + ASSERT(buffer); + ASSERT(p); + ASSERT(wildcard); + if(maxcount<=0) + return 0; + + char old_path[_MAX_PATH],path[_MAX_PATH]; + ddio_GetWorkingDir(old_path,_MAX_PATH); + if(!ddio_SetWorkingDir(p)){ + //directory doesn't exist + ddio_SetWorkingDir(old_path); + return -1; + } + + ddio_GetWorkingDir(path,_MAX_PATH); + if(stricmp(path,p)){ + //the working dir is not the same as the passed in path, so try to create a correct path and set it + ddio_MakePath(path,p,"",NULL); + if(!ddio_SetWorkingDir(path)){ + ddio_SetWorkingDir(old_path); + return -1; + } + } + + + int count = 0; + char tempbuffer[_MAX_PATH]; + + if(ddio_FindFileStart(wildcard,tempbuffer)){ + if(!ddio_DirExists(tempbuffer)){ + buffer[count] = mem_strdup(tempbuffer); + count++; + } + + bool done = false; + + while(!done){ + if(ddio_FindNextFile(tempbuffer)){ + if(!ddio_DirExists(tempbuffer)){ + if(countRemoveAll(); + + //if path is NULL or 0 length string then just list the directories + if( (!path) || (*path=='\0') ){ + char *roots[30]; + int rootcount = ddio_GetFileSysRoots((char **)&roots,30); + if(!rootcount){ + FDlg_EnableWaitMessage(false); + return; + } + char tempbuffer[5]; + for(int i=0;iAddItem(tempbuffer); + mem_free(roots[i]); + } + } + FDlg_EnableWaitMessage(false); + return; + } + + char **dirs = NULL; + char *tempdir[1]; + char fullpath[_MAX_PATH]; + ddio_GetRootFromPath(path,fullpath); + if(!stricmp(path,fullpath)){ + //we are are the root of the drive, make a dummy path + ddio_MakePath(fullpath,path,"",NULL); + }else{ + //just use this + strcpy(fullpath,path); + } + + int dircount = GetDirectoriesInPath((char **)&tempdir,1,fullpath); + if(dircount==-1){ + //invalid directory/path + DoMessageBox(TXT_ERROR,TXT_ERRPATHNOTVALID,MSGBOX_OK); + FDlg_EnableWaitMessage(false); + return; + } + + if(tempdir[0]) + mem_free(tempdir[0]); + if(dircount>0){ + dirs = (char **)mem_malloc(sizeof(char *)*dircount); + if(!dirs){ + //out of memory! + dircount = 0; + }else{ + GetDirectoriesInPath(dirs,dircount,fullpath); + } + }else{ + dircount = 0; + dirs = NULL; + } + + //figure out how many wildcards we have, and allocate accordingly + int wildcard_count = 0; + char *wc = NULL; + char **wc_strings = NULL; + int *wc_count = NULL; + char ***filelist = NULL; + int total_files = 0; + int wildcard_len = 0; + char *strptr = NULL; + + //save the wildcards + wc = (char *)mem_malloc(strlen(wildcards)+2); + if(!wc){ + FDlg_EnableWaitMessage(false); + return; + } + + //make sure there is a tailing ; + strcpy(wc,wildcards); + wildcard_len = strlen(wildcards); + if(wc[wildcard_len-1]!=';'){ + wc[wildcard_len] = ';'; + wc[wildcard_len+1] = '\0'; + } + + //break up wildcard string, counting the number of wildcards as we go + strptr = wc; + while(*strptr!='\0'){ + if(*strptr==';'){ + *strptr='\0'; + wildcard_count++; + } + strptr++; + } + + if(wildcard_count>0){ + wc_strings = (char **)mem_malloc(sizeof(char *)*wildcard_count); + wc_count = (int *)mem_malloc(sizeof(int)*wildcard_count); + + if((wc_strings)&&(wc_count)){ + int e; + + //get each individual string of the wildcard strings + strptr = wc; + for(e=0;e0){ + //now we need to get all the files for the wildcard + filelist[e] = (char **)mem_malloc(sizeof(char *)*count); + if(filelist[e]){ + GetFilesInPath(filelist[e],count,path,wc_strings[e]); + wc_count[e] = count; + }else{ + wc_count[e] = 0; + } + }else{ + filelist[e] = NULL; + wc_count[e] = 0; + } + }//end if(count==-1) + + //we don't need the wildcard anymore, we can free it up + if(wc_strings[e]) + mem_free(wc_strings[e]); + }//end for(e=0;eAddItem(tempbuffer); + mem_free(dirs[i]); + } + } + i = dircount; + if(total_files>0){ + for(int j=0;j0){ + wc_filelist = filelist[j]; + for(int k=0;kAddItem(wc_filelist[k]); + i++; + mem_free(wc_filelist[k]); + wc_filelist[k] = NULL; + } + } + if(wc_filelist){ + mem_free(wc_filelist); + wc_filelist = NULL; + } + } + } + } + + if(dirs) + mem_free(dirs); + if(filelist) + mem_free(filelist); + if(wc_count) + mem_free(wc_count); + FDlg_EnableWaitMessage(false); +} diff --git a/Descent3/object.cpp b/Descent3/object.cpp new file mode 100644 index 000000000..2d144cb20 --- /dev/null +++ b/Descent3/object.cpp @@ -0,0 +1,3742 @@ +/* + * $Logfile: /DescentIII/main/object.cpp $ + * $Revision: 315 $ + * $Date: 3/20/00 12:18p $ + * $Author: Matt $ + * + * Object management functions + * + * $Log: /DescentIII/main/object.cpp $ + * + * 315 3/20/00 12:18p Matt + * Merge of Duane's post-1.3 changes. + * Memset ai_frame to zeros after malloc. + * + * 314 1/26/00 9:20p Jeff + * added support for IntelliVIBE DLL + * + * 313 10/25/99 9:52a Matt + * Undid a Mac change (Duane says it's ok) + * + * 312 10/23/99 12:51a Matt + * Fixed compile warning + * + * 311 10/22/99 10:44p Jeff + * mac merge bug fix + * + * 310 10/22/99 3:43p Matt + * Mac merge + * + * 309 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 308 10/08/99 4:24p 3dsmax + * some ambient sound fix done by MattT + * + * 307 9/18/99 9:24p Jeff + * object position history code (needed for motion blur effect) + * + * 306 5/22/99 1:54a Matt + * Got rid of debug code mistakenly checked in. + * + * 305 5/21/99 3:43a Matt + * + * 304 5/20/99 9:03p Matt + * Changed trigger system so that collisions with rendered portals don't + * set off triggers on the portals. + * + * 303 5/20/99 3:00p Jason + * added support for negative respawn scalars + * + * 302 5/13/99 7:18p Jeff + * fixed unattach bug in delete dead + * + * 301 5/10/99 8:29p Jason + * robots no longer burn on terrain that causes damage + * + * 300 5/08/99 4:12p Chris + * Added AI hearing noises... version 1 + * + * 299 5/06/99 12:52p Jason + * fixed set on fire bug + * + * 298 5/05/99 5:18p Jason + * Invul generic objects don't burn on damaging terrain + * + * 297 5/05/99 5:10a Matt + * Fixed a bug with dying & animating objects. + * + * 296 4/29/99 4:50p Chris + * Fixed matcen and Osiris created objects so thier ambient sounds play + * + * 295 4/29/99 3:09p Jason + * fixed a potential bug with object deaths + * + * 294 4/27/99 10:25p Jason + * slowed down afterburner outside just a bit + * + * 293 4/27/99 9:13a Matt + * When clearing a room's secret flag, clear the flag from all the + * connected rooms. + * + * 292 4/26/99 2:30p Jason + * fixed bug with secret messages + * + * 291 4/26/99 2:28p Jason + * added persistant hud messages + * + * 290 4/25/99 5:20p Jason + * fixed some more coop bugs + * + * 289 4/24/99 2:00p Chris + * Removed the slow terrain cell verify check from ObjMoveOne when Del+Y + * is in effect + * + * 288 4/23/99 7:18p Jason + * fixed some multiplayer issues with observer mode + * + * 287 4/22/99 7:13p Jeff + * fixed bug deleting ghost object in multiplayer + * + * 286 4/22/99 4:19p Chris + * Checked in with the extended DEL+Y terrain node check stuff + * + * 285 4/21/99 3:01p Matt + * Added a new type for dying objects that have AI, instead of keeping a + * flag in the dying info. + * + * 284 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 283 4/20/99 12:24p Matt + * Re-revised the ObjInit() system. ObjInit() again does all the + * initialization stuff, but now it's passed a lot more information so + * those fields can be set before the rest of the initialization takes + * place. + * + * 282 4/18/99 8:13p Chris + * Fixed the floating flare problems (where windows where broken out and + * the flare remained) + * + * 281 4/18/99 5:33p Jason + * fixed object multiplayer problem + * + * 280 4/16/99 12:33a Matt + * Disable Soar on non-Windows systems. + * + * 279 4/15/99 1:41a Jeff + * changes for linux compile + * + * 278 4/14/99 3:56a Jeff + * fixed case mismatch in #includes + * + * 277 4/12/99 6:15p Samir + * Sound priorities pass 1 + * + * 276 4/12/99 8:18a Chris + * + * 275 4/12/99 8:12a Chris + * + * 274 4/12/99 8:06a Chris + * Added an ASSERT to check init code that doesn't call + * ComputeDefaultSize() for polygon objects + * + * 273 4/10/99 5:56p Matt + * Cleaned up object initialization code, including create object & load + * object. + * + * 272 4/09/99 12:06p Jason + * made model setup code faster + * + * 271 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 270 4/03/99 4:24p Jason + * sped up attached vis effects by a large amount + * + * 269 3/28/99 5:56p Matt + * Added sparking effect for objects + * + * 268 3/16/99 9:01a Kevin + * Fixed generic check to also check for doors. + * + * 267 3/12/99 7:47p Jeff + * only call applydamagetogeneric for generic objects + * + * 266 3/04/99 6:12p Jason + * fixed bug with terrain LOD + * + * 265 3/01/99 8:34p Matt + * Made room damage system set the player on fire instead of applying + * damage directly. This system still needs work, but is ok for now. + * + * 264 3/01/99 6:27p Matt + * Cleaned up handling of the outside-mine flag + * + * 263 3/01/99 3:15p Jason + * fixed multiplayer object deletion bug + * + * 262 2/28/99 6:36p Kevin + * More progress on load/save + * + * 261 2/27/99 5:40p Jason + * fixed bug where clients weren't getting remove messages for timed out + * powerups + * + * 260 2/25/99 11:01a Matt + * Added new explosion system. + * + * 259 2/25/99 10:29a Jason + * fixed powerup stalling bug in netgames + * + * 258 2/24/99 1:02p Jason + * various fixes for multiplayer + * + * 257 2/23/99 7:35p Jeff + * removed assert/return in unghost if the item is in a player's inventory + * (needs to be called when sending over objects in a multiplayer game, + * causes an assert) + * + * 256 2/23/99 5:13p Kevin + * Fixed it so objects with lightmaps will retain their lightmaps when a + * game is saved & loaded. + * + * 255 2/23/99 12:39p Jason + * added more options for generic objects + * + * 254 2/22/99 3:26a Matt + * Deleted obsolete code + * + * 253 2/22/99 1:20a Jeff + * added support for inventory (simple) in dallas. Moved end-level + * sequence to use IGC. Add position clipboard stuff for dallas. Fixed + * some inventory bug with storing object handles + * + * 252 2/21/99 4:31p Chris + * Improving the level goal system... Not done. + * + * 251 2/21/99 4:20p Matt + * Added SoundSource objects (and reformatted parts of the object header + * files). + * + * 250 2/19/99 5:16p Chris + * Added the child died event + * + * 249 2/15/99 11:22a Jason + * took out client-side prediction + * + * 248 2/13/99 12:36a Jeff + * new object flag. set for when an object is currently in a player's + * inventory + * + * 247 2/12/99 5:59p Jason + * more permissable server stuff + * + * 246 2/12/99 3:38p Jason + * added client side interpolation + * + * 245 2/10/99 2:49p Matt + * Renamed OBJECT_HANDLE_INVALID to OBJECT_HANDLE_BAD + * + * 244 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 243 2/08/99 5:25p Jeff + * removed all calls to MultiSendRemoveObject, incorportated into + * SetObjectDeadFlag. Fixes sequencing issues in multiplayer + * + * 242 2/07/99 1:20a Jeff + * added new multiplayer game events EVT_GAMEOBJKILLED and + * EVT_GAMEOBJDESTROYED + * + * 241 2/05/99 1:27p Matt + * Only do cycled animations for generic objects + * + * 240 2/03/99 6:32p Jason + * more changes for world states + * + * 239 2/03/99 5:49p Matt + * Added room damage system + * + * 238 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 237 2/01/99 4:17p Jason + * more changes for multisafe + * + * 236 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 235 1/29/99 5:22p Jeff + * localization + * + * 234 1/27/99 6:48p Jeff + * better error handling for ghost/unghost (release build) + * + * 233 1/24/99 1:24a Jason + * fixed chase camera being deleted wrong + * + * 232 1/23/99 2:49p Kevin + * Fixed player movement masking + * + * 231 1/22/99 8:53p Jeff + * added custom-default script overrides + * + * 230 1/22/99 7:12p Jason + * changed terrain speed + * + * 229 1/22/99 4:23p Jason + * sped up afterburner temporarily + * + * 228 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 227 1/21/99 6:50p Jason + * added test impact mortar + * + * 226 1/21/99 3:34p Jason + * added liquid code + * + * 225 1/20/99 8:06p Jeff + * added members into DLLinfo struct for game change segment events, pass + * them over on execute dll packets + * + * 224 1/20/99 2:13a Chris + * It is now possible for robots to have special immunities, resistances, + * and vunerabilities + * + * 223 1/19/99 4:22p Matt + * Added the ability for objects to have their own lighting info, + * different from the default lighting for that type of object. + * + * 222 1/19/99 9:20a Kevin + * Added afterburner masking + * + * 221 1/18/99 6:18p Kevin + * Added controller masking to DALLAS + * + * 220 1/18/99 2:46p Matt + * Combined flags & flags2 fields in object struct + * + * 219 1/18/99 12:01a Jeff + * fixed EVT_GAMEPLAYERCHANGESEG event (would only get called if the + * server changed seg) and added EVT_GAMEOBJCHANGESEG + * + * 218 1/15/99 7:52p Chris + * Updated ObjSetPos() to include a f_update_attach_children flag + * + * 217 1/15/99 2:53p Jason + * fixed multiplayer ping bug + * + * 216 1/13/99 6:31p Jason + * added punch to the afterburner + * + * 215 1/13/99 2:27p Jeff + * added an is_dying flag for evt_destroy to determine whether the event + * is due to level end or because it really is dying + * + * 214 1/12/99 11:44p Jason + * Fixed bug with accelerated ping objects + * + * 213 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 212 1/06/99 7:02p Jeff + * added a multiplayer event for game controls + * + * 211 1/05/99 5:09p Jason + * added permissable server networking (ala Quake/Unreal) to Descent3 + * + * 210 12/31/98 7:35p Jeff + * don't allow players to be Ghosted (turned into OBJ_DUMMY). Also, for + * ObjDelete[Dead] unghost a ghosted object before killing it + * + * 209 12/30/98 12:31p Jeff + * when freeing object scripts, the type is changed before it's freed in + * ObjDelete. Also, when destroyed, OBJ_ROBOT becomes OBJ_DEBRIS and + * Osiris was not handling this. + * + * 208 12/28/98 10:16a Jason + * fixed code that was causing some weapons to disappear + * + * 207 12/21/98 11:30p Matt + * Added names for objects + * + * 206 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 205 12/10/98 7:09p Jason + * added cloak fade + * + * 204 12/08/98 4:27p Chris + * Removed MT_WALKING hack + * + * 203 12/01/98 3:35p Matt + * Got rear view working. + * + * 202 12/01/98 3:15p Jason + * play refueling sound in 3d for multiplayer + * + * 201 11/17/98 4:16p Kevin + * Demo recording system + * + * 200 11/13/98 4:25p Jason + * changes for better weapon effects + * + * 199 11/13/98 2:27p Samir + * took out music code. + * + * 198 11/11/98 2:46p Kevin + * Demo recording system work + * + * 197 11/10/98 4:48p Jason + * sped up afterburner recharge time + * + * 196 11/10/98 11:16a Jason + * fixed some multiplayer bugs with powerups disappearing + * + * + * 195 11/06/98 11:05a Kevin + * Added code for demo play back + * + * 194 11/02/98 6:49p Jason + * fixed ifdef release problem + * + * 193 11/02/98 4:04p Luke + * Temp. disabled walkers inside + * + * 192 10/30/98 4:24p Jason + * changes for multiplayer + * + * 191 10/29/98 5:20p Chris + * Player ships collide smaller now + * + * 190 10/28/98 11:51a Jason + * fixed some multiplayer problems, plus redid coop a bit to make it + * cleaner + * + * 189 10/21/98 12:13p Jason + * took out guided in multi + * + * 188 10/20/98 5:47p Kevin + * Gunboy and other fixes + * + * 187 10/20/98 1:19p Jason + * fixed afterburner and Of2_SERVER_OBJECT bug + * + * 186 10/20/98 10:50a Sean + * + * 185 10/19/98 11:46p Jason + * changes for multiplayer debug layer + * + * 184 10/19/98 10:45p Jason + * toned down afterburner outside + * + * 183 10/19/98 7:51p Kevin + * performance testing + * + * 182 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 181 10/19/98 1:21p Jason + * made afterburner faster + * + * 180 10/18/98 11:07p Matt + * Made ClearTransientObjects() work. + * + * 179 10/17/98 5:50p Jeff + * hooked in rtperformance (run time performance) library + * + * 178 10/16/98 4:18p Chris + * Fixed the 'flare thing' + * + * 177 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 176 10/15/98 8:47a Matt + * Changed _DEBUG/RELEASE inconsistancy with slew + * + * 175 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 174 10/14/98 12:43a Matt + * Fixed error handling for when an object can't be initialized. + * + * 173 10/13/98 1:08p Chris + * Greatly improved the AI's use of paths. Improved visability checking + * algorithm. Probably needs a second pass for further cleanup. + * + * 172 10/11/98 10:53p Matt + * Fixed some zero-length malloc()s + * + * 171 10/11/98 2:39p Matt + * If ObjInit() fails, ObjCreate() now returns an error. + * + * 170 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 169 10/06/98 5:46p Kevin + * Added new configuration for demo + * + * 168 10/06/98 2:59p Jason + * added timedemo function + * + * 167 10/06/98 11:27a Jason + * added new death type for robots + * + * 166 10/03/98 8:05p Matt + * Added asserts + * + * 165 9/28/98 6:23p Chris + * Changed multi_anim to custom_anim + * + * 164 9/23/98 2:55p Chris + * Improved auto-leveling and added newbie leveling + * + * 163 9/22/98 6:48p Jason + * fixed dedicated server problems + * + * 162 9/22/98 6:04p Samir + * ifdef DoAI to 1 if in release version. + * + * 161 9/22/98 1:02p Matt + * Don't set previous room info for an object if the object was moved in + * the editor. + * + * 160 9/22/98 12:40p Matt + * Cleaned up the object per-frame processing code. + * + * 159 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 158 9/10/98 12:18p Jason + * more changes to afterburner/thrust effect + * + * 157 9/09/98 7:09p Jason + * changed afterburner effect for ships + * + * 156 9/09/98 4:30p Jason + * fixed a remaining (and insidious) bug with gunpoint instancing + * + * 155 8/31/98 6:12p Luke + * Moved the multi turret deletion code to somewhere where I can debug + * things + * + * 154 8/24/98 4:48p Jason + * undid my last rev + * + * 153 8/24/98 2:52p Jason + * fixed instance problem + * + * 152 8/19/98 11:30a Jason + * fixed afterburner problem with low framerates + * + * 151 8/17/98 12:10p Chris + * Fixed MAJOR bug in getting gunpoint positions + * + * 150 8/14/98 6:21p Chris + * Added another safety check for docycledanim + * + * 149 8/12/98 6:37p Jeff + * added functions to ghost an object (make it's type to OBJ_DUMMY) and + * unghost + * + * 148 8/12/98 12:04p Chris + * Attach system version .5 (still needs more work to be multiplayer + * friendly) + * + * 147 8/07/98 8:25p Chris + * Improved the attach system + * + * 146 8/07/98 6:54p Jeff + * afterburner powerup effect added + * + * 145 8/03/98 4:29p Chris + * Added additional support for the attach system + * + * 144 7/30/98 11:09a Jason + * added weapons that freeze and deform terrain + * + * 143 7/28/98 5:43p Samir + * added room change hook for music system, + * + * 142 7/22/98 3:16p Jason + * added observer mode + * + * 141 7/20/98 10:42a Jason + * added more player scalars, plus afterburner changes + * + * 140 7/16/98 12:51p Mark + * Fixed a potential bug + * + * 139 7/15/98 2:33p Jason + * added scalar variables for various player skills + * + * 138 7/15/98 12:48p Jeff + * put in calls to handle new multiplayer event...when an object's shields + * change + * + * 137 7/14/98 5:52p Kevin + * Packet loss measurements and auto pps adjusting + * + * 136 7/14/98 3:54p Sean + * CHRIS -- Fixed a bug in free'ing multi_turret_info on objects that are + * not polygon models (i.e. they don't have that info and it is union'ed + * to something else so it is not NULL) + * + * 135 7/09/98 7:50p Jeff + * added ObjGetUltimateParent to find out the original parent of an + * object, traces the family tree + * + * 134 7/09/98 3:30p Chris + * Fixed communication problem between systems + * + * 133 7/09/98 11:34a Chris + * Turrets are interpolated. + * + * 132 7/08/98 11:38a Chris + * Improved the turret info passing in multiplayer + * + * 131 7/07/98 3:26p Chris + * Added changes for turret updates + * + * 130 7/02/98 2:47p Chris + * Dynamic weapon info is now dynamically allocated + * + * 129 7/01/98 4:35p Chris + * More multiplayer sync issues + * + * 128 7/01/98 2:02p Chris + * Added the sound for animations + * + * 127 6/30/98 6:36p Chris + * Added rev .1 of multiplayer animations - BTW It is totally not done. + * + * 126 6/30/98 4:28p Chris + * Checked in some missing commas + * + * 125 5/26/98 7:07p Samir + * reset script.is_custom to 1 when calling ObjCreate. + * + * 124 5/22/98 4:45p Chris + * Powerups us the correct anim time + * + * 123 5/19/98 4:42a Chris + * Added shockwave's -- enjoy. :) + * + * 122 5/15/98 5:41p Jason + * implemented volume lighting system + * + * 121 5/11/98 4:40p Chris + * AI info is now a malloc'ed pointer + * + * 120 5/04/98 6:47p Craig + * Rooms cannot cycle through anims + * + * 119 5/04/98 12:30p Matt + * ObjCreate() now takes object id as a ushort + * + * 118 5/04/98 12:28p Matt + * Added shard objects + * + * 117 5/01/98 3:41p Chris + * + * 116 5/31/98 3:08p Chris + * Allowed for death animations + * + * 115 4/27/98 1:14p Jason + * cleaned up afterburner stuff + * + * 114 4/23/98 12:44p Jason + * made AI toggleable to test framerate - this is temporary + * + * 113 4/22/98 4:15p Chris + * Improved DebugBlockPrint + * + * 112 4/22/98 3:50p Chris + * Added DebugBlockPrint + * + * 111 4/22/98 2:10p Matt + * Made FreeAllObjects() free the player object (object 0), since objects + * now contain malloc'd data and must be freed. + * + * 110 4/20/98 6:24p Chris + * Bulletproofing collision code + * + * 109 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 108 4/17/98 1:59p Jason + * added cool object effects + * + * 107 4/16/98 2:56p Chris + * Updated buddy support and intergrated scripting and AI + * + * 106 4/15/98 8:38p Jason + * fixed some multiplayer issues + * + * 105 4/15/98 12:56p Chris + * Updated parent_handle support and added buddy bot support + * + * 104 4/13/98 2:21p Chris + * Fixed some collision problems dealing with AABBs and Polymodel paging + * in. + * + * 103 4/13/98 12:42p Jason + * changed afterburner effect + * + * 102 4/10/98 2:42p Jason + * fixed afterburner blobs to be a stream + * + * 101 4/10/98 2:16p Jason + * fixed guided missile problems + * + * 100 4/09/98 5:18p Jason + * got guided working in multiplayer + * + * 99 4/09/98 3:04p Jason + * temp fix for afterburner running out of fuel + * + * 98 4/09/98 2:23p Jason + * added guided/afterburner stuff + * + * 97 4/07/98 1:12p Jason + * make powerup respawn a variable in multiplayer games + * + * 96 4/03/98 11:04a Jason + * added a multplayer dll hook for when a player changes a room + * + * 95 4/01/98 6:23p Jason + * added a slew of stuff for multiplayer + * + * 94 3/31/98 5:56p Jason + * changes for multiplay + * + * 93 3/31/98 4:23p Chris + * Added a new AIpath system + * + * 92 3/20/98 9:34p Jason + * added SetObjectDeadFlag inlined function + * + * 91 3/13/98 5:55p Chris + * Added the new collision spheres + * + * 90 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 89 2/25/98 3:01p Jason + * added splinter objects to FreeObjects + * + * 88 2/25/98 2:05p Jason + * did FOV and object visibility changes + * + * 87 2/20/98 1:46p Chris + * JASON: Made dynamic lighting only occur if the object is rendered + * + * 86 2/19/98 11:17a Chris + * Tweaked the BIG_OBJECT system + * + * 85 2/17/98 3:46p Matt + * Revamped weapon system and got sounds for spray and fusion weapons + * working. Still need to implements getting continuous & cutoff sounds + * from player ship. + * + * 84 2/16/98 2:49p Chris + * Made the MAX_SUBOBJECTS determine the number of normalized_time values + * to be processed. No longer a 'literal' problem. + * + * 83 2/13/98 6:59p Chris + * FIxed the normalized_time array from being smaller than the number of + * subobjects. Also update newstyle_fi.cpp + * + * 82 2/12/98 8:48p Matt + * Changed controls system to keep the reading of the controls seperate + * from using the results. Got rid of the Controls global. + * + * 81 2/11/98 2:04p Jason + * got spawning weapons working + * + * 80 2/09/98 3:20p Matt + * Changed assert to allow OBJECT_HANDLE_INVALID to be pased to ObjGet() + * + * 79 2/05/98 3:00p Matt + * Made ObjSetPos() take optional orienation. Got rid of unlink & relink + * funcs from header. + * + * 78 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 77 2/03/98 1:06p Jason + * tidied up some multiplayer loose ends + * + * 76 1/30/98 5:26p Chris + * Allow all non-AI objects to animate + * + * 75 1/29/98 5:49p Matt + * Changed old camera object type to be viewer object (for editor), and + * now camera objects will just be for game cameras. + * + * 74 1/27/98 12:26p Samir + * Added GetObjectPointInWorld. + * + * 73 1/23/98 11:21a Jason + * incremental multiplayer checkin + * + * 72 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 71 1/20/98 6:01p Jason + * first pass at getting multiplayer deaths working + * + * 70 1/20/98 4:12p Samir + * New script housekeeping system. + * + * 69 1/20/98 12:27p Luke + * Added an assert to make sure that we are not linking to an external + * room + * + * 68 1/20/98 12:10p Jason + * implemented vis effect system + * + * 67 1/19/98 2:44p Samir + * Use one script per object and started parameter passing support. + * + * 66 1/19/98 12:38p Jason + * took out mprintf in ObjCreate + * + * 65 1/19/98 10:04a Matt + * Added new object handle system + * + * 64 1/15/98 6:49p Jason + * incremental checkin for multiplayer + * + * 63 12/30/97 5:45p Jason + * added toggleable fog + * + * 62 12/19/97 2:46p Jason + * implemented bitmap paging routines + * + * 61 12/12/97 3:42p Jason + * free all objects on shutdown + * + * 60 12/01/97 9:56a Chris + * Removed FLARE and CNTRL_CENTER types + * + * 59 11/10/97 12:27p Jason + * added objects live mprintf + * + * 58 10/28/97 12:22p Chris + * Added a movement type of walking + * + * 57 10/23/97 5:36p Jason + * added splinter objects (probably temporary) + * + * 56 10/22/97 5:14p Chris + * Allow for non-AI objects to animate + * + * 55 10/20/97 4:46p Jason + * changes for explosions + * + * 54 10/14/97 6:53p Chris + * Added the initial SOAR support + * + * 53 10/08/97 6:35p Samir + * Dying player ships get marked as destroyed. When death sequence is + * over, they are marked as dead. This is needed to better control + * movement of the dying ship. + * + * 52 10/03/97 5:18p Chris + * Added OBJ_LINE as a new type + * + * 51 10/02/97 1:04p Jason + * implemented FreeAllObjects + * + * 50 10/01/97 7:51p Matt + * Added code for external rooms + * + * 49 9/30/97 6:40p Jason + * got lightmap stuff sort of working with objects + * + * 48 9/22/97 6:20p Matt + * Killed include of stub.h, and removed references to stuff that had been + * in stub.h + * + * 47 9/19/97 6:58p Chris + * Fixed some bugs with the big object system and removed some annoying + * mprintf's + * + * 46 9/19/97 1:01p Chris + * Better big object support + * + * 45 9/18/97 1:25p Matt + * Cleaned up code, added the text name for door objects, and other small + * changes. + * + * 44 9/17/97 11:16a Matt + * Ripped out segment code + * + * 43 9/16/97 5:04p Matt + * Changed conditional for debug code + * + * 42 9/15/97 12:42p Samir + * Added include of stdlib. + * + * 41 9/15/97 5:21a Chris + * Added sphere2poly support + * + * 40 9/12/97 6:36p Chris + * Added collision terrain segment support + * Added some LARGE OBJECT collision support + * + * 39 9/12/97 10:12a Matt + * Added trigger check + * + * 38 9/05/97 3:22p Jason + * changes for lit objects + * + * 37 9/05/97 1:29p Jason + * revamped generic object lighting + * + * 36 9/05/97 12:26p Samir + * Player initiates death sequence if killed. + * + * 35 9/04/97 5:26p Jason + * got pulsing objects to work + * + * 34 9/04/97 12:11p Jason + * added flickering generic objects + * + * 33 9/03/97 6:17p Jason + * added code to support powerups/robots/etc that cast light + * + * 32 9/03/97 3:53p Jason + * got objects to cast light + * + * 31 8/25/97 7:32p Chris + * + * 30 8/25/97 6:18p Chris + * + * 29 8/25/97 5:14p Craig + * + * 28 8/25/97 12:41p Chris + * Made sure that all object movements and mine changes result in AABB + * updates. + * + * 27 8/22/97 10:51a Samir + * call destroy script if object is being killed. + * + * 26 8/21/97 7:47p Matt + * ObjSetPos() wasn't relinking the object when the room changed + * + * 25 8/21/97 5:57p Samir + * Added code that kills scripts when object is being deleted. + * + * 24 8/20/97 5:04p Samir + * Added frame interval scripting. + * + * 23 8/18/97 1:45a Chris + * Fix some bugs. + * + * 22 8/12/97 3:56p Matt + * Added new object type: OBJ_BUILDING + * + * 21 8/12/97 1:13p Chris + * Added AABBs. + * + * 20 8/11/97 6:48p Matt + * Rewrote static object remap system + * + * 19 8/11/97 1:53p Matt + * Ripped out robot & powerup pages, and added generic page + * + * 18 8/04/97 5:29p Matt + * Removed some not-to-be-used room code + * + * 17 8/04/97 4:08p Matt + * Added ObjSetPos() that takes the new room number + * + * 16 8/04/97 12:37p Chris + * We now support fvi in Rooms :) + * + * 15 7/29/97 10:27a Jason + * added RenderOBject abstraction layer + * + * 14 7/28/97 1:14p Chris + * Added support for sub-object visability. Plus, debris. + * + * 13 7/16/97 4:09p Jason + * ripped out temporary object lighting + * + * + * 12 7/16/97 3:43p Jason + * took off lighting of objects + * + * 11 7/15/97 5:32p Jason + * got simple static lighting working with objects + * + * 10 7/15/97 4:59p Chris + * + * 9 7/15/97 4:59p Chris + * added support for AI and animations + * + * 50 6/24/97 6:54p Mark + * + * 49 6/24/97 6:07p Matt + * Use DrawPolygonModel() directly, instead of DrawPolygonObject() + * + * 48 6/06/97 4:07p Jason + * verify tmap2 textures if in editor mode + * + * 47 5/16/97 2:17p Samir + * ResetPlayerObject added. + * + * 46 5/15/97 6:09p Chris + * + * 45 5/14/97 3:41p Chris + * + * 44 5/14/97 12:32p Matt + * Took out timer stuff for refuel sound; instead, we set the execlusive + * flag for the sound itself. + * + * 43 5/13/97 10:31p Matt + * Added code for refueling centers. + * Dead objects now get deleted right after the simulation is done. + * + * 42 4/30/97 5:43p Jason + * remap polymodels when pagefile is done loading + * + * 41 4/24/97 5:42p Jason + * got fireball vclips working + * + * 40 4/17/97 2:48p Samir + * ReadFlyingControls renamed from read_flying_controls to match naming + * convention rules. + * + * 39 4/14/97 4:23p Jason + * made objects work with DrawWeaponObject + * + * 38 4/14/97 3:48p Jason + * changed RT_LASER to RT_WEAPON + * + * 37 4/04/97 4:33p Chris + * + * 36 4/04/97 2:45p Matt + * Removed prototypes for deleted functions + * + * 35 4/02/97 3:44p Matt + * Find a valid Ships[] entry, instead of assuming that Ships[0] is valid. + * + * 34 4/02/97 2:26p Jason + * made RemapEverything actually call every remap function + * + * 33 4/01/97 10:49p Matt + * Get player ship model num from Ship page + * + * 32 3/27/97 10:45a Chris + * Set player mass and drag info + * + * 31 3/26/97 3:29p Jason + * made robots work/render + * + * 30 3/26/97 3:02p Chris + * Fixed a bug in ObjMoveAll... It only moved every other object. + * + * 29 3/24/97 4:11p Chris + * Fixed a bug by the TerrainMaster... It seems that !obj->flags & + * OF_OVER_TERRAIN is not the same as !(obj->flags & OF_OVER_TERRAIN) and + * it isn't and shouldn't be. :) + * + * 28 3/21/97 5:01p Jason + * incremental terrain improvments + * + * + * 27 3/20/97 5:45p Jason + * made object work with and render on terrain + * + * 26 3/17/97 12:11p Chris + * + * 25 3/17/97 11:56a Chris + * Forced player object to have valid mass and drag. Will fix this + * correctly at a later date. + * + * 24 3/14/97 12:18p Chris + * Tweak physics and remove braking for now. + * + * 23 3/12/97 6:31p Chris + * Added player flight controls while in game + * + * 22 3/05/97 3:09p Jason + * added RemapDoors to RemapEverything + * + * 21 3/5/97 1:40 PM Jeremy + * only #include editor headers when EDITOR is defined + * + * 20 2/28/97 2:37p Matt + * Draw wireframe box around editor's current object + * + * 19 2/27/97 5:10p Samir + * Added crude trigger checking while flying. + * + * 18 2/18/97 6:31p Matt + * Changed Frame_time to Frametime, & added Gametime + * + * 17 2/13/97 12:16p Jason + * changes for powerup remapping + * + * 16 2/12/97 5:37p Jason + * changed GamePowerups to Powerups to keep from + * getting lynched + * + * 15 2/11/97 6:51p Matt + * Ripped out some old stuff & renamed a function + * + * 14 2/10/97 5:39p Matt + * Fixed to compile with EDITOR defined + * + * 13 2/07/97 5:48p Matt + * Renamed vector.h to vecmat.h to fix DevStudio problem + * + * $NoKeywords: $ + */ + +#include +#include // for memset +#include + +#include "object.h" + +#include "pserror.h" +#include "vecmat.h" +#include "mono.h" + +#include "descent.h" +#include "player.h" +#include "slew.h" +#include "game.h" +#include "trigger.h" +#include "PHYSICS.H" +#include "collide.h" +#include "door.h" +#include "controls.h" +#include "terrain.h" +#include "polymodel.h" +#include "gametexture.h" +#include "ship.h" +#include "soundload.h" +#include "weapon.h" +#include "objinit.h" +#include "fireball.h" +#include "hlsoundlib.h" +#include "sounds.h" +#include "AIMain.h" +#include "room.h" +#include "objinfo.h" +#include "lighting.h" +#include "findintersection.h" +#include "lightmap_info.h" +#include "object_lighting.h" +#include "soar.h" +#include "splinter.h" +#include "ObjScript.h" +#include "viseffect.h" +#include "multi.h" +#include "game2dll.h" +#include "robot.h" +#include "damage.h" +#include "attach.h" +#include "dedicated_server.h" +#include "hud.h" +#include "demofile.h" +#include "rtperformance.h" +#include "osiris_dll.h" +#include "gameloop.h" +#include "mem.h" +#include "stringtable.h" +#include "levelgoal.h" +#include "psrand.h" +#include "vibeinterface.h" + +#ifdef EDITOR + #include "editor\d3edit.h" +#endif + +/* + * Global variables + */ + +object *Player_object = NULL; //the object that is the player +object *Viewer_object = NULL; //which object we are seeing from + +static short free_obj_list[MAX_OBJECTS]; + +//Data for objects + +// -- Object stuff + +//info on the various types of objects + +object Objects[MAX_OBJECTS]; + +tPosHistory Object_position_samples[MAX_OBJECT_POS_HISTORY]; +ubyte Object_position_head; +signed short Object_map_position_history[MAX_OBJECTS]; +short Object_map_position_free_slots[MAX_OBJECT_POS_HISTORY]; +unsigned short Num_free_object_position_history; + +int Num_objects=0; +int Highest_object_index=0; +int Highest_ever_object_index=0; + +int print_object_info = 0; + +#ifdef EDITOR +//This array matches the object types in object.h +char *Object_type_names[MAX_OBJECT_TYPES] = { + "WALL", //OBJ_WALL 0 + "FIREBALL", //OBJ_FIREBALL 1 + "ROBOT", //OBJ_ROBOT 2 + "SHARD", //OBJ_SHARD 3 + "PLAYER", //OBJ_PLAYER 4 + "WEAPON", //OBJ_WEAPON 5 + "VIEWER", //OBJ_VIEWER 6 + "POWERUP", //OBJ_POWERUP 7 + "DEBRIS", //OBJ_DEBRIS 8 + "CAMERA", //OBJ_CAMERA 9 + "SHOCKWV", //OBJ_SHOCKWAVE 10 + "CLUTTER", //OBJ_CLUTTER 11 + "GHOST", //OBJ_GHOST 12 + "LIGHT", //OBJ_LIGHT 13 + "COOP", //OBJ_COOP 14 + "UNUSED", //OBJ_MARKER 15 + "BUILDING", //OBJ_BUILDING 16 + "DOOR", //OBJ_DOOR 17 + "ROOM", //OBJ_ROOM 18 + "LINE", //OBJ_PARTICLE 19 + "SPLINTER", //OBJ_SPLINTER 20 + "DUMMY", //OBJ_DUMMY 21 + "OBSERVER", //OBJ_OBSERVER 22 + "DEBUG LINE", //OBJ_DEBUG_LINE 23 + "SOUNDSOURCE", //OBJ_SOUNDSOURCE 24 + "WAYPOINT", //OBJ_WAYPOINT 25 +}; +#endif + +int Num_big_objects = 0; +short BigObjectList[MAX_BIG_OBJECTS]; //DAJ_MR utb int + +/* + * Local Function Prototypes + */ + +//Do refueling centers & damage rooms +void DoSpecialPlayerStuff(object *obj); + + +/* + * Functions + */ + + +#ifndef RELEASE +//set viewer object to next object in array +void ObjGotoNextViewer() +{ + int i, start_obj = 0; + + start_obj = Viewer_object - Objects; //get viewer object number + + for (i=0;i<=Highest_object_index;i++) + { + + start_obj++; + if (start_obj > Highest_object_index ) start_obj = 0; + + if (Objects[start_obj].type != OBJ_NONE ) + { + Viewer_object = &Objects[start_obj]; + return; + } + } + + Error( "Couldn't find a viewer object!" ); + +} + +//set viewer object to next object in array +void obj_goto_prev_viewer() +{ + int i, start_obj = 0; + + start_obj = Viewer_object - Objects; //get viewer object number + + for (i=0; i<=Highest_object_index; i++) + { + + start_obj--; + if (start_obj < 0 ) start_obj = Highest_object_index; + + if (Objects[start_obj].type != OBJ_NONE ) + { + Viewer_object = &Objects[start_obj]; + return; + } + } + + Error( "Couldn't find a viewer object!" ); + +} +#endif + +object *obj_find_first_of_type (int type) +{ + int i; + + for (i=0;i<=Highest_object_index;i++) + if (Objects[i].type==type) + return (&Objects[i]); + + return NULL; +} + +int obj_return_num_of_type (int type) +{ + int i,count=0; + + for (i=0;i<=Highest_object_index;i++) + if (Objects[i].type==type) + count++; + + return (count); +} + +int obj_return_num_of_typeid (int type,int id) +{ + int i,count=0; + + for (i=0;i<=Highest_object_index;i++) + if (Objects[i].type==type && Objects[i].id==id) + count++; + + return (count); +} + +int ObjAllocate(void); + +//Creates the player object in the center of the given room +void CreatePlayerObject(int roomnum) +{ + int objnum; + vector pos; + + ComputeRoomCenter(&pos,&Rooms[roomnum]); + + objnum = ObjCreate(OBJ_PLAYER,0,roomnum,&pos,NULL); + + ASSERT(objnum == 0); //player must be object 0 + + Player_object = Viewer_object = &Objects[objnum]; +} + +void InitBigObjects() +{ + Num_big_objects = 0; +} + +void BigObjAdd(int objnum) +{ + ASSERT(Num_big_objects < MAX_BIG_OBJECTS); + if(Num_big_objects >= MAX_BIG_OBJECTS) + { + Int3(); + return; + } + + Objects[objnum].flags |= OF_BIG_OBJECT; + BigObjectList[Num_big_objects++] = objnum; +} + +void BigObjRemove(int objnum) +{ + int i; + + Objects[objnum].flags &= (~OF_BIG_OBJECT); + + for(i = 0; i < Num_big_objects; i++) + { + if(BigObjectList[i] == objnum) + { + Num_big_objects--; + break; + } + } + + while(i < Num_big_objects) + { + BigObjectList[i] = BigObjectList[i+1]; + i++; + } + + ASSERT(Num_big_objects < MAX_BIG_OBJECTS); + ASSERT(Num_big_objects >= 0); +} + + +//Resets the object list: sets all objects to unused, intializes handles, & sets roomnums to -1 +//Called by the editor to init a new level. +void ResetObjectList() +{ + int i; + + //Init data for each object + for (i=0;iroomnum == -1); + ASSERT(!(obj->flags & OF_BIG_OBJECT)); + + if((obj->size >= MIN_BIG_OBJ_RAD) && (!ROOMNUM_OUTSIDE(roomnum))) + { + BigObjAdd(objnum); + } + + obj->roomnum = roomnum; + + if (ROOMNUM_OUTSIDE(roomnum)) + { + int cellnum = CELLNUM(roomnum); + + obj->next = Terrain_seg[cellnum].objects; + Terrain_seg[cellnum].objects = objnum; + + ASSERT(obj->next != objnum); + } + else + { + ASSERT(roomnum>=0 && roomnum<=Highest_room_index); + ASSERT(!(Rooms[roomnum].flags & RF_EXTERNAL)); + + obj->next = Rooms[roomnum].objects; + Rooms[roomnum].objects = objnum; + ASSERT(obj->next != objnum); + } + + obj->prev = -1; + + if (obj->next != -1) Objects[obj->next].prev = objnum; + + ASSERT(Objects[0].next != 0); + if (Objects[0].next == 0) + Objects[0].next = -1; + + ASSERT(Objects[0].prev != 0); + if (Objects[0].prev == 0) + Objects[0].prev = -1; +} + +void ObjUnlink(int objnum) +{ + object *obj = &Objects[objnum]; + + ASSERT(objnum != -1); + + + if (obj->flags & OF_BIG_OBJECT) + { + BigObjRemove(objnum); + } + + if (OBJECT_OUTSIDE(obj)) + { + int cellnum = CELLNUM(obj->roomnum); + + terrain_segment *seg = &Terrain_seg[cellnum]; + + if (obj->prev == -1) + seg->objects = obj->next; + else + Objects[obj->prev].next = obj->next; + + if (obj->next != -1) Objects[obj->next].prev = obj->prev; + } + else { + room *rp = &Rooms[obj->roomnum]; + + if (obj->prev == -1) + rp->objects = obj->next; + else + Objects[obj->prev].next = obj->next; + + if (obj->next != -1) Objects[obj->next].prev = obj->prev; + + } + + //Mark as not linked + obj->roomnum = -1; + + ASSERT(Objects[0].next != 0); + ASSERT(Objects[0].prev != 0); +} + +//----------------------------------------------------------------------------- +// Scan the object list, freeing down to num_used objects +// Returns number of slots freed. +int FreeObjectSlots(int num_used) +{ + int i, olind; + int obj_list[MAX_OBJECTS]; + int num_already_free, num_to_free, original_num_to_free; + + olind = 0; + num_already_free = MAX_OBJECTS - Highest_object_index - 1; + + if (MAX_OBJECTS - num_already_free < num_used) + { + return 0; + } + + for (i=0; i<=Highest_object_index; i++) + { + if (Objects[i].flags & OF_DEAD) + { + num_already_free++; + if (MAX_OBJECTS - num_already_free < num_used) + return num_already_free; + } + else + { + switch (Objects[i].type) + { + case OBJ_NONE: + num_already_free++; + if (MAX_OBJECTS - num_already_free < num_used) + return 0; + break; + case OBJ_WALL: + Int3(); // This is curious. What is an object that is a wall? + break; + case OBJ_FIREBALL: + case OBJ_WEAPON: + case OBJ_DEBRIS: + case OBJ_SPLINTER: + obj_list[olind++] = i; + break; + default: + break; + } + } + } + + num_to_free = MAX_OBJECTS - num_used - num_already_free; + original_num_to_free = num_to_free; + + if (num_to_free > olind) { + mprintf((1, "Warning: Asked to free %i objects, but can only free %i.\n", num_to_free, olind)); + num_to_free = olind; + } + + for (i=0; i= MAX_OBJECTS-2 ) { + int num_freed; + + num_freed = FreeObjectSlots(MAX_OBJECTS-10); + mprintf((0, " *** Freed %i objects in frame %i\n", num_freed, FrameCount)); + } + + if ( Num_objects >= MAX_OBJECTS ) { + mprintf((1, "Object creation failed - too many objects!\n" )); + return -1; + } + + objnum = free_obj_list[Num_objects++]; + + if (objnum > Highest_object_index) { + Highest_object_index = objnum; + if (Highest_object_index > Highest_ever_object_index) { + Highest_ever_object_index = Highest_object_index; + mprintf((0, "Highest ever Object %d\n", Highest_ever_object_index)); + } + } + + return objnum; +} + +//frees up an object. Generally, ObjDelete() should be called to get +//rid of an object. This function deallocates the object entry after +//the object has been unlinked +void ObjFree(int objnum) +{ + ObjFreePositionHistory(&Objects[objnum]); + + free_obj_list[--Num_objects] = objnum; + ASSERT(Num_objects >= 0); + + if (objnum == Highest_object_index) + while (Objects[--Highest_object_index].type == OBJ_NONE); +} + +void ObjSetAABB(object *obj) +{ + vector object_rad; + + if(obj->type == OBJ_ROOM) + { + obj->min_xyz = Rooms[obj->id].min_xyz; + obj->max_xyz = Rooms[obj->id].max_xyz; + } + else if(obj->flags & OF_POLYGON_OBJECT && + obj->type != OBJ_WEAPON && + obj->type != OBJ_DEBRIS && + obj->type != OBJ_POWERUP && + obj->type != OBJ_PLAYER) + { + vector offset_pos; + + object_rad.x = object_rad.y = object_rad.z = Poly_models[obj->rtype.pobj_info.model_num].anim_size; + offset_pos = obj->pos + obj->anim_sphere_offset; + + obj->min_xyz = offset_pos - object_rad; + obj->max_xyz = offset_pos + object_rad; + } + else + { + object_rad.x = obj->size; + object_rad.y = obj->size; + object_rad.z = obj->size; + + obj->min_xyz = obj->pos - object_rad; + + obj->max_xyz = obj->pos + object_rad; + } +} + +//----------------------------------------------------------------------------- +//initialize a new object. adds to the list for the given room +//returns the object number +int ObjCreate(ubyte type, ushort id, int roomnum, vector *pos, const matrix *orient, int parent_handle) +{ + int objnum; + object *obj; + int handle; + + ASSERT(type != OBJ_NONE); + + if (ROOMNUM_OUTSIDE(roomnum)) + { + ASSERT(CELLNUM(roomnum) <= TERRAIN_WIDTH*TERRAIN_DEPTH); + ASSERT(CELLNUM(roomnum) >= 0); + + roomnum = GetTerrainRoomFromPos(pos); + + if (roomnum == -1) + return -1; + } + + //Get next free object + objnum = ObjAllocate(); + if (objnum == -1) //no free objects + return -1; + obj = &Objects[objnum]; + + //Make sure the object is ok + ASSERT(obj->type == OBJ_NONE); //make sure unused + ASSERT(obj->roomnum == -1); //make sure unlinked + + //Compute the new handle + handle = obj->handle + HANDLE_COUNT_INCREMENT; + + //Check for handle wrap + if ((handle & HANDLE_COUNT_MASK) == HANDLE_COUNT_MASK) //Going to wrap! + Int3(); //Show this to Matt, or email him if he's not here + + //Initialize the object + if (! ObjInit(obj,type,id,handle,pos,Gametime,parent_handle)) { //Couldn't init! + obj->type = OBJ_NONE; //mark as unused + ObjFree(objnum); //de-allocate object + return -1; + } + + #ifdef _DEBUG + if (print_object_info) + { + mprintf( (0, "Created object %d of type %d\n", objnum, obj->type )); + } + #endif + + //Set the object's orietation + // THIS MUST BE DONE AFTER ObjInit (as ObjInit could load in a polymodel and set the anim and wall offsets) + ObjSetOrient(obj, orient?orient:&Identity_matrix); + + //Link object into room or terrain cell + ObjLink(objnum,roomnum); + + // Type specific should have set up the size, so now we can compute the bounding box. + ObjSetAABB(obj); + + //Let the demo system know about this object + DemoWriteObjCreate(type,id,roomnum,pos,orient,parent_handle,obj); + + ObjInitPositionHistory(obj); + + // Check bad init code that doesn't call ComputeDefaultSize() + ASSERT(!(obj->type != OBJ_ROOM && obj->type != OBJ_DEBRIS && (obj->flags & OF_POLYGON_OBJECT) && !(Poly_models[obj->rtype.pobj_info.model_num].flags & PMF_SIZE_COMPUTED))); + + //Done! + return objnum; +} + +void ObjInitPositionHistory(object *obj) +{ + ASSERT(Object_map_position_history[OBJNUM(obj)]==-1); + + // If it is one of the types of object's that we have to sample positions for + // Initialize that + switch(obj->type) + { + case OBJ_ROBOT: + case OBJ_WEAPON: + case OBJ_DEBRIS: + if(Num_free_object_position_history>0) + { + // we have a free slot + Num_free_object_position_history--; + int free_slot = Object_map_position_free_slots[Num_free_object_position_history]; + Object_map_position_history[OBJNUM(obj)] = free_slot; + int i; + for(i=0;ipos; + } + }else + { + // out of slots! + Object_map_position_history[OBJNUM(obj)] = -1; + }break; + default: + // not saving positions + Object_map_position_history[OBJNUM(obj)] = -1; + break; + + } +} + +void ObjFreePositionHistory(object *obj) +{ + int objnum = OBJNUM(obj); + + if(Object_map_position_history[objnum]!=-1) + { + int slot_to_free = Object_map_position_history[objnum]; + Object_map_position_history[objnum] = -1; + + Object_map_position_free_slots[Num_free_object_position_history] = slot_to_free; + Num_free_object_position_history++; + ASSERT(Num_free_object_position_history<=MAX_OBJECT_POS_HISTORY); + } +} + +void ObjResetPositionHistory(void) +{ + Num_free_object_position_history = MAX_OBJECT_POS_HISTORY; + Object_position_head = 0; + + int i; + + for(i=0;itype==OBJ_DUMMY){ + //unghost the object before destroying it + ObjUnGhostObject(objnum); + } + + +/* if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_CLIENT && Objects[objnum].type==OBJ_POWERUP) + { + mprintf ((0,"Deleting object number %d type=%d id=%d\n",objnum,obj->type,obj->id)); + }*/ + + if ((Game_mode & GM_MULTI) && (obj->flags & OF_SERVER_OBJECT)) + { + if ( Netgame.local_role==LR_CLIENT && NetPlayers[Player_num].sequence>NETSEQ_OBJECTS && NetPlayers[Player_num].sequence!=NETSEQ_LEVEL_END ) + { + if (!(obj->flags & OF_SERVER_SAYS_DELETE)) + { + mprintf ((0,"Illegally deleting server object! Objnum=%d type=%d id=%d\n",objnum,obj->type,obj->id)); + Int3(); + return; + } + + } + } + + + if(obj->flags & OF_POLYGON_OBJECT) + { + polyobj_info *p_info = &obj->rtype.pobj_info; + if (p_info->multi_turret_info.keyframes != NULL) + { + mem_free(p_info->multi_turret_info.keyframes); + mem_free(p_info->multi_turret_info.last_keyframes); + + p_info->multi_turret_info.keyframes = NULL; + p_info->multi_turret_info.last_keyframes = NULL; + } + } + + ASSERT(objnum != -1); +// ASSERT(objnum != 0 ); + ASSERT(obj->type != OBJ_NONE); +// ASSERT(obj != Player_object); + +//?? if (obj->type==OBJ_WEAPON && obj->id==GUIDEDMISS_ID) { +//?? int pnum=Objects[obj->ctype.laser_info.parent_num].id; +//?? mprintf ((0,"Deleting a guided missile! Player %d\n\n",pnum)); +//?? +//?? if (pnum!=Player_num) { +//?? mprintf ((0,"deleting missile that belongs to %d (%s)!\n",pnum,Players[pnum].callsign)); +//?? Guided_missile[pnum]=NULL; +//?? } +//?? else if (Newdemo_state==ND_STATE_RECORDING) +//?? newdemo_record_guided_end(); +//?? +//?? } + + if (obj == Viewer_object) //deleting the viewer? + Viewer_object = Player_object; //..make the player the viewer + + // Delete this chase camera if needed + if (Player_camera_objnum==objnum) + Player_camera_objnum=-1; + +//?? if (obj->flags & OF_ATTACHED) //detach this from object +//?? obj_detach_one(obj); +//?? +//?? if (obj->attached_obj != -1) //detach all objects from this +//?? obj_detach_all(obj); + + #ifdef _DEBUG + if (print_object_info) + { + mprintf( (0, "Deleting object %d of type %d\n", objnum, Objects[objnum].type )); + } + #endif + + + if (obj->type==OBJ_WEAPON && obj->ctype.laser_info.parent_type==OBJ_PLAYER) + { + int pnum=Objects[(obj->parent_handle & HANDLE_OBJNUM_MASK)].id; + + if (Players[pnum].guided_obj==obj) + { + mprintf ((0,"Deleting a guided missile!")); + Players[pnum].guided_obj=NULL; + } + } + + if (obj->type==OBJ_WEAPON && obj->ctype.laser_info.parent_type==OBJ_PLAYER) + { + int pnum=Objects[(obj->parent_handle & HANDLE_OBJNUM_MASK)].id; + + if (Players[pnum].user_timeout_obj==obj) + { + mprintf ((0,"Deleting a timeout missile!")); + Players[pnum].user_timeout_obj=NULL; + } + } + + ObjUnlink(objnum); + + ASSERT(Objects[0].next != 0); + + // Update powerup respawn point + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_SERVER) + { + if (obj->type==OBJ_POWERUP && Object_info[obj->id].respawn_scalar>0) + { + for (int i=0;iid].respawn_scalar); + Powerup_timer[Num_powerup_timer].id=obj->id; + Num_powerup_timer++; + mprintf ((0,"Adding powerup id %d to respawn list! count=%d\n",obj->id,Num_powerup_timer)); + } + + } + } + + } + +// Kill any script thread associated with this object. + FreeObjectScripts(obj,false); + + if (obj->custom_default_script_name){ + mem_free(obj->custom_default_script_name); + obj->custom_default_script_name = NULL; + } + + if (obj->custom_default_module_name){ + mem_free(obj->custom_default_module_name); + obj->custom_default_module_name = NULL; + } + + obj->type = OBJ_NONE; //unused! + obj->roomnum=-1; // zero it! + + // Free lightmap memory + if (obj->lm_object.used) + ClearObjectLightmaps (obj); + + // Free up effects memory + if (obj->effect_info) + { + mem_free (obj->effect_info); + obj->effect_info=NULL; + } + + if (obj->ai_info != NULL) + { + AIDestroyObj(obj); + mem_free(obj->ai_info); + obj->ai_info = NULL; + } + + if (obj->dynamic_wb != NULL) + { + mem_free(obj->dynamic_wb); + obj->dynamic_wb = NULL; + } + + if (obj->attach_children != NULL) + { + mem_free(obj->attach_children); + obj->attach_children = NULL; + } + + if (obj->name) { + mem_free(obj->name); + obj->name = NULL; + } + + if (obj->lighting_info) { + mem_free(obj->lighting_info); + obj->lighting_info = NULL; + } + + ObjFree(objnum); +} + +// Frees all the objects that are currently in use +void FreeAllObjects () +{ + int objnum; + + for (objnum=0;objnum<=Highest_object_index;objnum++) + if (Objects[objnum].type != OBJ_NONE) + { + Objects[objnum].flags|=OF_SERVER_SAYS_DELETE; + Objects[objnum].flags &= ~OF_INPLAYERINVENTORY; + ObjDelete(objnum); + } + + FreeAllVisEffects(); +} + +// ------------------------------------------------------------------------------------------------------------------ +void ObjDeleteDead() +{ + int i; + object *objp; + int local_dead_player_object=-1; + + // Move all objects + objp = Objects; + + for (i=0;i<=Highest_object_index;i++) + { + if ((objp->type!=OBJ_NONE) && (objp->flags&OF_DEAD) ) + { + if(objp->flags & OF_INFORM_DESTROY_TO_LG) + { + Level_goals.Inform(LIT_OBJECT, LGF_COMP_DESTROY, objp->handle); + } + + if(objp->flags&OF_INPLAYERINVENTORY) + { + //this object is in a player inventory somewhere...remove it first! + InventoryRemoveObject(objp->handle); + } + + if(objp->type==OBJ_DUMMY){ + //unghost the object before deleting it + ObjUnGhostObject(i); + + //tell clients to unghost + if ( (Game_mode & GM_MULTI) && (Netgame.local_role!=LR_CLIENT)) + { + MultiSendGhostObject(&Objects[i],false); + } + } + + if(objp->flags & OF_POLYGON_OBJECT) + { + if(objp->flags & OF_ATTACHED) + { + tOSIRISEventInfo ei; + ei.evt_child_died.it_handle = objp->handle; + + Osiris_CallEvent(ObjGet(objp->attach_ultimate_handle),EVT_CHILD_DIED,&ei); + } + + UnattachFromParent(objp); + UnattachChildren(objp); + } + + // Execute script for death + tOSIRISEventInfo ei; + ei.evt_destroy.is_dying = 1; + Osiris_CallEvent(objp,EVT_DESTROY,&ei); + + if(Game_mode & GM_MULTI){ + DLLInfo.me_handle = DLLInfo.it_handle = objp->handle; + CallGameDLL (EVT_GAMEOBJDESTROYED,&DLLInfo); + } + + //detach any scripts attached + Osiris_DetachScriptsFromObject(objp); + + //if it's flag to tell the clients to remove is set, tell them now + if ( (Game_mode & GM_MULTI) && (Netgame.local_role!=LR_CLIENT)) + { + bool removed = false; + + if (objp->flags&OF_SEND_MULTI_REMOVE_ON_DEATH) + { + // Tell all clients to remove this object + MultiSendRemoveObject (objp,0); + removed = true; + } + if (!removed && objp->flags&OF_SEND_MULTI_REMOVE_ON_DEATHWS) + { + // Tell all clients to remove this object with sound + MultiSendRemoveObject (objp,1); + removed = true; + } + } + + if(IS_GUIDEBOT(objp)) + { + int i; + int handle = objp->handle; + + for(i = 0; i < MAX_PLAYERS; i++) + { + if(objp->handle == Buddy_handle[i]) + { + Buddy_handle[i] = OBJECT_HANDLE_NONE; + break; + } + } + } + + // check if player being killed. + if (objp->type!=OBJ_PLAYER) + { + ObjDelete(i); + } + } + objp++; + } + + // Delete our visual effects + VisEffectDeleteDead (); +} + +//when an object has moved into a new room, this function unlinks it +//from its old room and links it into the new room +void ObjRelink(int objnum,int newroomnum) +{ + ASSERT((objnum >= 0) && (objnum <= Highest_object_index)); + + + if (! ROOMNUM_OUTSIDE(newroomnum)) + { + ASSERT((newroomnum <= Highest_room_index) && (newroomnum >= 0)); + ASSERT (Rooms[newroomnum].used); + } + else + ASSERT (CELLNUM(newroomnum)>=0 && CELLNUM(newroomnum)<=(TERRAIN_WIDTH*TERRAIN_DEPTH)); + + ObjUnlink(objnum); + ObjLink(objnum,newroomnum); +} + + +void DoCycledAnim(object *obj) +{ + float from; + float to; + float delta; + float spc; + + float anim_time = Frametime; + + if(!(obj->flags & OF_POLYGON_OBJECT)) + return; + + from = Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].from; + to = Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].to; + spc = Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].spc; + + if(spc <= 0.0) return; + ASSERT(from <= to); + + if(obj->rtype.pobj_info.anim_frame < from || obj->rtype.pobj_info.anim_frame > to) + { + mprintf((0, "NonAI-Animation: Correcting for an incorrect frame number\n")); + obj->rtype.pobj_info.anim_frame = from; + } + + delta = to - from; + + ASSERT(delta >= 0.0f); + + if (delta > 0.0f) + { + // Frames per second will be on the animation page + float step = (1.0f / (spc/(delta)))*anim_time; + + while (step > delta) + { + step -= delta; + } + + ASSERT(step >= 0.0f); + + obj->rtype.pobj_info.anim_frame += step; + + if (obj->rtype.pobj_info.anim_frame >= to) + { + + // We are guarenteed that this is between 'to' and 'from' from the above asserts() :) + obj->rtype.pobj_info.anim_frame -= delta; + } + } + else + { + obj->rtype.pobj_info.anim_frame = from; + } + + ASSERT(obj->rtype.pobj_info.anim_frame >= from && obj->rtype.pobj_info.anim_frame <= to); +} + +void TimeoutObject (object *obj) +{ + switch (obj->type) + { + case OBJ_WEAPON: + TimeoutWeapon (obj); + break; + case OBJ_SHOCKWAVE: + DoConcussiveForce(obj, obj->parent_handle); + break; + default: + break; + } +} + +// Does afterburn effects to a player ship +void DoPlayerAfterburnControl (game_controls *controls,object *objp) +{ + static int afterburner_id = -1; + int slot=objp->id; + + if (controls->afterburn_thrust>0) + { + Players[slot].last_afterburner_time = Gametime; + + if (Players[slot].afterburn_time_left>0) + { + float punch_scalar=1.0; + + if (Players[slot].afterburn_time_left==(AFTERBURN_TIME)) + AddToShakeMagnitude(objp->mtype.phys_info.mass/2); + + if (Players[slot].afterburn_time_left>(AFTERBURN_TIME*.90)) + punch_scalar=1.8f; + else if (Players[slot].afterburn_time_left>(AFTERBURN_TIME*.80) && Players[slot].afterburn_time_left<(AFTERBURN_TIME*.90)) + { + float norm=Players[slot].afterburn_time_left-(AFTERBURN_TIME*.80); + norm/=(AFTERBURN_TIME*.1); + punch_scalar=1.0+(norm*.8); + } + + if (OBJECT_OUTSIDE(objp)) + controls->forward_thrust=controls->afterburn_thrust*1.6*punch_scalar; + else + controls->forward_thrust=controls->afterburn_thrust*1.6*punch_scalar; + + Players[slot].flags|=PLAYER_FLAGS_AFTERBURN_ON|PLAYER_FLAGS_THRUSTED; + Players[slot].afterburn_time_left-=Frametime; + if (Players[slot].afterburn_time_left<0) + Players[slot].afterburn_time_left=0; + } + else + { + Players[slot].afterburn_time_left=0; + Players[slot].flags&=~PLAYER_FLAGS_AFTERBURN_ON; + } + } + else + { + Players[slot].flags&=~PLAYER_FLAGS_AFTERBURN_ON; + + if (Players[slot].afterburn_time_left5.0) + { + float useage; + if(afterburner_id==-1) + afterburner_id = FindObjectIDName("Afterburner"); + + if( afterburner_id!=-1 && Players[slot].inventory.CheckItem(OBJ_POWERUP,afterburner_id)) + useage = Frametime*2; + else + useage = Frametime; + + Players[slot].afterburn_time_left+=useage; + Players[slot].afterburn_time_left=min (AFTERBURN_TIME,Players[slot].afterburn_time_left); + + Players[slot].energy-=(useage); + } + } + } +} + +//Deal with the player's rear view +void CheckRearView(int down_count,bool down_state) +{ + player *player = &Players[Player_num]; + + #define LEAVE_TIME (1.0 / 16) //How long until we decide key is down + + static bool leave_mode; + static float entry_time; + + if (down_count) { //key/button has gone down + + if (player->flags & PLAYER_FLAGS_REARVIEW) { //rear view was active, so turn it off + player->flags &= ~PLAYER_FLAGS_REARVIEW; + } + else { //rear view wasn't active, so turn it on + player->flags |= PLAYER_FLAGS_REARVIEW; + leave_mode = 0; //means wait for another key + entry_time = Gametime; + } + } + else { //key didn't go down this frame; check if it used to be down + + if (down_state) { //key is still down + + if (!leave_mode && ((Gametime - entry_time) > LEAVE_TIME)) + leave_mode = 1; + } + else { + + if (leave_mode && (player->flags & PLAYER_FLAGS_REARVIEW)) { + player->flags &= ~PLAYER_FLAGS_REARVIEW; + } + } + } +} + +extern int Timedemo_frame; +float Timedemo_timecount; + +//Do the controls for a player-type object +void DoFlyingControl(object *objp) +{ + game_controls controls; + + if (Dedicated_server) + return; + + ASSERT(objp->control_type == CT_FLYING); + + if ((objp->type!=OBJ_PLAYER && objp->type!=OBJ_OBSERVER) || objp->id!=Player_num) + return; + + if (Timedemo_frame!=-1) + { + matrix temp_mat,rot_mat; + + if (Timedemo_frame==0) + { + Timedemo_timecount=0; + } + else + { + Timedemo_timecount+=Frametime; + } + + vm_AnglesToMatrix (&temp_mat,0,65536/360,0); + + rot_mat=objp->orient * temp_mat; + + ObjSetOrient (objp,&rot_mat); + + Timedemo_frame++; + if (Timedemo_frame==360) + { + // Calculate timedemo framerate + + float fps=1.0/(Timedemo_timecount/360.0); + AddHUDMessage (TXT_TIMEDEMO,fps); + Timedemo_frame=-1; + } + + return; + } + if(Demo_flags==DF_PLAYBACK) + { + //If we are playing back a demo, bail early and don't process controls + return; + } + + //Read the controllers + ReadPlayerControls(&controls); + + + if((objp->type==OBJ_PLAYER)&&(Players[objp->id].controller_bitflags!=0xffffffff)) + { + player *pp = &Players[objp->id]; + if(!(pp->controller_bitflags & PCBF_FORWARD)) + { + if(controls.forward_thrust>0.0f) + controls.forward_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_REVERSE)) + { + if(controls.forward_thrust<0.0f) + controls.forward_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_LEFT)) + { + if(controls.sideways_thrust<0.0f) + controls.sideways_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_RIGHT)) + { + if(controls.sideways_thrust>0.0f) + controls.sideways_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_UP)) + { + if(controls.vertical_thrust>0.0f) + controls.vertical_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_DOWN)) + { + if(controls.vertical_thrust<0.0f) + controls.vertical_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_PITCHUP)) + { + if(controls.pitch_thrust>0.0f) + controls.pitch_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_PITCHDOWN)) + { + if(controls.pitch_thrust<0.0f) + controls.pitch_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_HEADINGLEFT)) + { + if(controls.heading_thrust<0.0f) + controls.heading_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_HEADINGRIGHT)) + { + if(controls.heading_thrust>0.0f) + controls.heading_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_BANKLEFT)) + { + if(controls.bank_thrust<0.0f) + controls.bank_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_BANKRIGHT)) + { + if(controls.bank_thrust>0.0f) + controls.bank_thrust = 0.0f; + } + if(!(pp->controller_bitflags & PCBF_PRIMARY)) + { + controls.fire_primary_down_count = 0; + controls.fire_primary_down_time = 0; + controls.fire_primary_down_state = false; + } + if(!(pp->controller_bitflags & PCBF_SECONDARY)) + { + controls.fire_secondary_down_count = 0; + controls.fire_secondary_down_time = 0; + controls.fire_secondary_down_state = false; + } + if(!(pp->controller_bitflags & PCBF_AFTERBURNER)) + { + controls.afterburn_thrust = 0.0f; + } + } + + // Update IntelliVIBE + if(objp==Player_object) + { + VIBE_DoControls(&controls); + } + + //Send an event to the Game DLLs so they can do any processing of game controls + if(Game_mode & GM_MULTI){ + DLLInfo.me_handle = DLLInfo.it_handle = objp->handle; + DLLInfo.special_data = (ubyte *)&controls; + CallGameDLL (EVT_GAMEDOCONTROLS,&DLLInfo); + } + + //Deal with afterburner + if (objp->type==OBJ_PLAYER) + { + ASSERT (Player_num==objp->id); + + // Reset thrusting + Players[objp->id].flags &=~PLAYER_FLAGS_THRUSTED; + + DoPlayerAfterburnControl (&controls,objp); + } + + //Update object's physics + if (objp->movement_type == MT_PHYSICS) + { + if (objp->type==OBJ_PLAYER && Players[objp->id].guided_obj!=NULL) + { + ASSERT (objp->id==Player_num); + object *g_obj=Players[objp->id].guided_obj; + + g_obj->mtype.phys_info.rotthrust.x = controls.pitch_thrust * g_obj->mtype.phys_info.full_rotthrust; + g_obj->mtype.phys_info.rotthrust.z = controls.bank_thrust * g_obj->mtype.phys_info.full_rotthrust; + g_obj->mtype.phys_info.rotthrust.y = controls.heading_thrust * g_obj->mtype.phys_info.full_rotthrust; + } + else + { + player *playp=&Players[objp->id]; + + objp->mtype.phys_info.rotthrust.x = controls.pitch_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar; + objp->mtype.phys_info.rotthrust.z = controls.bank_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar; + objp->mtype.phys_info.rotthrust.y = controls.heading_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar; + } + + float speed_scalar=1.0; + + // If thrusting foward, show glow + if (objp->type==OBJ_PLAYER && controls.forward_thrust>0) + Players[objp->id].flags |=PLAYER_FLAGS_THRUSTED; + + if (objp->effect_info && objp->effect_info->type_flags & EF_FREEZE) + speed_scalar=objp->effect_info->freeze_scalar; + + if (OBJECT_OUTSIDE (objp)) + speed_scalar*=1.3f; + + objp->mtype.phys_info.thrust = speed_scalar*Players[objp->id].movement_scalar * ((objp->orient.fvec * controls.forward_thrust * objp->mtype.phys_info.full_thrust) + + (objp->orient.uvec * controls.vertical_thrust * objp->mtype.phys_info.full_thrust) + + (objp->orient.rvec * controls.sideways_thrust * objp->mtype.phys_info.full_thrust)); + + } + + //Process player-specific stuff + if (objp->type==OBJ_PLAYER) + { + //Process weapon firing + FireWeaponFromPlayer(objp,PW_PRIMARY,controls.fire_primary_down_count,controls.fire_primary_down_state,controls.fire_primary_down_time); + FireWeaponFromPlayer(objp,PW_SECONDARY,controls.fire_secondary_down_count,controls.fire_secondary_down_state,controls.fire_secondary_down_time); + + //Deal with the flare + for (int i=0;ieffect_info) + return; + + static int napalm_id=-1; + + if (napalm_id==-1) + napalm_id=FindWeaponName ("Napalm"); + + if (obj->effect_info->type_flags & EF_VOLUME_CHANGING) + { + obj->effect_info->volume_change_time-=Frametime; + if (obj->effect_info->volume_change_time <= 0.0f) + { + obj->effect_info->type_flags &=~EF_VOLUME_CHANGING; + } + } + + if (obj->effect_info->type_flags & EF_FADING_IN) + { + obj->effect_info->fade_time -= Frametime; + if (obj->effect_info->fade_time <= 0.0f) + { + obj->effect_info->type_flags&=~EF_FADING_IN; + } + } + + + if (obj->effect_info->type_flags & EF_CLOAKED) + { + obj->effect_info->cloak_time -= Frametime; + if (obj->effect_info->cloak_time <= 0.0f) + { + // reappear the object + MakeObjectVisible(obj); + } + } + + if (obj->effect_info->type_flags & EF_FADING_OUT) + { + obj->effect_info->fade_time -= Frametime; + if (obj->effect_info->fade_time <= 0.0f) + { + obj->effect_info->type_flags&=~EF_FADING_OUT; + obj->effect_info->type_flags|=EF_CLOAKED; + } + } + + if (obj->effect_info->type_flags & EF_LIQUID) + { + obj->effect_info->liquid_time_left -= Frametime; + if (obj->effect_info->liquid_time_left <= 0.0f) + { + // Stop doing liquid effect + obj->effect_info->type_flags &=~EF_LIQUID; + if (obj==Viewer_object) + Render_FOV=D3_DEFAULT_FOV; + } + else + { + if (obj==Viewer_object) + { + int inttime=Gametime; + float normtime=Gametime-inttime; + + float scalar=FixSin(normtime*65535*4); + + scalar*=((float)obj->effect_info->liquid_mag); + if (obj->effect_info->liquid_time_left<1) + scalar*=(obj->effect_info->liquid_time_left); + + Render_FOV=D3_DEFAULT_FOV+scalar; + } + } + } + + + if (obj->effect_info->type_flags & EF_DEFORM) + { + obj->effect_info->deform_time -= Frametime; + if (obj->effect_info->deform_time <= 0.0f) + { + // Stop deforming + obj->effect_info->type_flags &=~EF_DEFORM; + } + } + + if (obj->effect_info->type_flags & EF_FREEZE) + { + obj->effect_info->freeze_scalar += (Frametime/3); + if (obj->effect_info->freeze_scalar >= 1.0f) + { + // Stop freezing + obj->effect_info->type_flags &=~EF_FREEZE; + } + } + + if (obj->effect_info->type_flags & EF_NAPALMED) + { + if ((Gametime-obj->effect_info->last_damage_time)>1.0) + { + obj->effect_info->last_damage_time=Gametime; + + // Apply damage to this napalmed object + object *killer; + + if (obj->effect_info->damage_handle!=OBJECT_HANDLE_NONE) + { + uint sig=obj->effect_info->damage_handle & HANDLE_COUNT_MASK; + int objnum=obj->effect_info->damage_handle & HANDLE_OBJNUM_MASK; + + if ((Objects[objnum].handle & HANDLE_COUNT_MASK) !=sig) + killer=NULL; + else + killer=&Objects[objnum]; + } + else + killer=NULL; + + if (obj->type==OBJ_PLAYER) + { + ApplyDamageToPlayer (obj,killer,PD_NONE,obj->effect_info->damage_per_second); + } + else if(IS_GENERIC(obj->type) || (obj->type == OBJ_DOOR)) + { + ApplyDamageToGeneric (obj,killer,GD_FIRE,obj->effect_info->damage_per_second); + } + + } + + obj->effect_info->damage_time-=Frametime; + if (obj->effect_info->damage_time<=0) + { + obj->effect_info->type_flags &=~EF_NAPALMED; + obj->effect_info->last_damage_time=0; + Sound_system.StopSoundLooping(obj->effect_info->sound_handle); + obj->effect_info->sound_handle = SOUND_NONE_INDEX; + } + + // Drop some smoke/fire and stuff + AttachRandomNapalmEffectsToObject (obj); + } + + //Do sparking stuff + if (obj->effect_info->type_flags & EF_SPARKING) { + int num_bolts = 0; + + //Create spark effects + obj->effect_info->spark_timer -= Frametime; + while (obj->effect_info->spark_timer < 0.0) { + obj->effect_info->spark_timer += obj->effect_info->spark_delay; + num_bolts++; + } + CreateElectricalBolts(obj,num_bolts); + + //Update timer + obj->effect_info->spark_time_left -= Frametime; + if (obj->effect_info->spark_time_left <= 0.0f) { + obj->effect_info->type_flags &= ~EF_SPARKING; + } + } + + +} + +//Check passed-through faces for triggers, etc. +void ObjCheckTriggers(object *objp) +{ + for (int i=0;i= 0 && j <= Highest_object_index); + j = Objects[j].next; + } + } +} +#endif + +#ifdef _DEBUG +extern int DoAI; +#else +#define DoAI 1 +#endif +//-------------------------------------------------------------------- +//Process an object for the current frame +void ObjDoFrame( object * obj ) +{ + int previous_room = obj->roomnum; + float save_frametime=Frametime; + + ASSERT(obj->type != OBJ_NONE); + + // Clear attached vis for this frame + obj->lowest_attached_vis=-1; + + // If this is a permissable netgame, accelerate this object if need by + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_CLIENT && (obj->flags & OF_PING_ACCELERATE)) + { + Frametime+=(NetPlayers[Player_num].ping_time/2.0); + obj->flags &= ~OF_PING_ACCELERATE; + } + //Update previous position before moving + obj->last_pos = obj->pos; + + //Inevitable countdown towards death + if (obj->flags & OF_USES_LIFELEFT) + obj->lifeleft -= Frametime; + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + //Process the control for this object + switch (obj->control_type) { + + case CT_POWERUP: + case CT_NONE: + break; + case CT_FLYING: { RTP_STARTINCTIME(ct_flying_time); DoFlyingControl(obj); RTP_ENDINCTIME(ct_flying_time); }break; + case CT_AI: if (DoAI){ RTP_STARTINCTIME(ct_aidoframe_time); AIDoFrame(obj); RTP_ENDINCTIME(ct_aidoframe_time); }break; + case CT_WEAPON: { RTP_STARTINCTIME(ct_weaponframe_time); WeaponDoFrame(obj); RTP_ENDINCTIME(ct_weaponframe_time); }break; + case CT_EXPLOSION: { RTP_STARTINCTIME(ct_explosionframe_time); DoExplosionFrame(obj); RTP_ENDINCTIME(ct_explosionframe_time); }break; + case CT_DEBRIS: { RTP_STARTINCTIME(ct_debrisframe_time); DoDebrisFrame(obj); RTP_ENDINCTIME(ct_debrisframe_time); }break; + case CT_SPLINTER: { RTP_STARTINCTIME(ct_splinterframe_time); DoSplinterFrame(obj); RTP_ENDINCTIME(ct_splinterframe_time); }break; + case CT_DYING: DoDyingFrame(obj); break; + case CT_DYING_AND_AI: + if (DoAI) + AIDoFrame(obj); + DoDyingFrame(obj); + break; + case CT_SOUNDSOURCE: break; //do nothing + case CT_SOAR: break; + + #ifdef _DEBUG + case CT_SLEW: SlewFrame(obj); break; + #endif + + default: + Error("Unknown control type %d in object %i, handle/type/id = %i/%i/%i",obj->control_type, obj-Objects, obj->handle, obj->type, obj->id); + break; + } + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + //Update cycled animation + if((obj->control_type != CT_AI) && (obj->control_type != CT_DYING_AND_AI) && (obj->control_type != CT_DEBRIS) && IS_GENERIC(obj->type) && (obj->type != OBJ_ROOM)) + { + if(Object_info[obj->id].anim && Object_info[obj->id].anim[MC_STANDING].elem[AS_ALERT].to) + { + RTP_STARTINCTIME(cycle_anim); + DoCycledAnim(obj); + RTP_ENDINCTIME(cycle_anim); + } + } + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + // Do freeze stuff on AI controlled robots + if ((obj->type==OBJ_ROBOT || obj->type == OBJ_BUILDING) && obj->control_type==CT_AI && obj->effect_info && (obj->effect_info->type_flags & EF_FREEZE)) + obj->mtype.phys_info.velocity*=obj->effect_info->freeze_scalar; + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + //Check for object dead of old age + if ((obj->flags & OF_USES_LIFELEFT) && (obj->lifeleft < 0)) + { + bool kill_it=true; + bool tell_clients=false; + + ASSERT (obj->type!=OBJ_PLAYER); + + if (Game_mode & GM_MULTI) + { + if (Netgame.local_role==LR_CLIENT) + { + if (obj->type==OBJ_POWERUP || obj->type==OBJ_ROBOT || obj->type==OBJ_CLUTTER || obj->type==OBJ_BUILDING) + { + ASSERT (obj->flags & OF_SERVER_OBJECT); + + if (!(obj->flags & OF_SERVER_SAYS_DELETE)) + { + kill_it=false; + obj->lifeleft=.001f; + } + } + } + else + { + if (obj->type==OBJ_POWERUP || obj->type==OBJ_ROBOT || obj->type==OBJ_CLUTTER || obj->type==OBJ_BUILDING) + tell_clients=true; + } + } + + if (kill_it) + { + TimeoutObject (obj); + SetObjectDeadFlag (obj,tell_clients); + } + } + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + + //If object is dead, don't do any more processing on it + if (obj->flags & OF_DEAD) + { + Frametime=save_frametime; + return; + } + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + //Do the movement for this object + switch (obj->movement_type) { + case MT_NONE: + if((obj->flags & OF_STUCK_ON_PORTAL) && !(Rooms[obj->mtype.phys_info.stuck_room].portals[obj->mtype.phys_info.stuck_portal].flags & PF_RENDER_FACES)) + { + if (IS_GENERIC(obj->type) && (obj->flags & OF_CLIENT_KNOWS)) + SetObjectDeadFlag(obj,true); + else + SetObjectDeadFlag(obj,false); + + } + break; //this doesn't move + + case MT_PHYSICS: + { + RTP_STARTINCTIME(mt_physicsframe_time); + + do_physics_sim(obj); + DebugBlockPrint("DP"); + ObjCheckTriggers(obj); + + RTP_ENDINCTIME(mt_physicsframe_time); + }break; + + case MT_WALKING: + { + RTP_STARTINCTIME(mt_walkingframe_time); + do_walking_sim(obj); + DebugBlockPrint("DW"); + ObjCheckTriggers(obj); + RTP_ENDINCTIME(mt_walkingframe_time); + }break; + + case MT_SHOCKWAVE: + { + RTP_STARTINCTIME(mt_shockwave_time); + DoConcussiveForce(obj, obj->parent_handle); + RTP_ENDINCTIME(mt_shockwave_time); + }break; + + case MT_OBJ_LINKED: + PhysicsLinkList[Physics_NumLinked++] = OBJNUM(obj); + break; + + default: + Int3(); //unknown movement type + } + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + // Do special effects stuff to object + if (obj->effect_info) + { + RTP_STARTINCTIME(obj_doeffect_time); + ObjDoEffects(obj); + RTP_ENDINCTIME(obj_doeffect_time); + } + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + //Deal with special player movement stuff + if (obj->type == OBJ_PLAYER) + { + RTP_STARTINCTIME(obj_move_player_time); + DoSpecialPlayerStuff(obj); + RTP_ENDINCTIME(obj_move_player_time); + } + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + // Handle interval event for script. + do{ + RTP_STARTINCTIME(obj_d3xint_time); + //@$-D3XExecScript(obj, EVT_INTERVAL, obj); + tOSIRISEventInfo ei; + ei.evt_interval.frame_time = Frametime; + ei.evt_interval.game_time = Gametime; + Osiris_CallEvent(obj,EVT_INTERVAL,&ei); + RTP_ENDINCTIME(obj_d3xint_time); + }while(0); //this do{}while(0) is here because the RTP_STARTINCTIME/RTP_ENDINCTIME must be in the same scope + //and not share scope with another RTP_STARTINCTIME + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + // Cast light + do{ + RTP_STARTINCTIME(obj_objlight_time); + DoObjectLight (obj); + RTP_ENDINCTIME(obj_objlight_time); + }while(0); //this do{}while(0) is here because the RTP_STARTINCTIME/RTP_ENDINCTIME must be in the same scope + //and not share scope with another RTP_STARTINCTIME + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + // if this object is walking/rolling on the terrain, make it not LOD at that point + if (obj->movement_type==MT_WALKING && (obj->flags & OF_RENDERED) && OBJECT_OUTSIDE(obj)) + TurnOffLODForCell(CELLNUM(obj->roomnum)); + +//#ifdef _DEBUG +// if(Physics_player_verbose) +// { +// debug_check_terrain_objects(); +// } +//#endif _DEBUG + + //Mark object as not rendered for this frame + obj->flags &= ~OF_RENDERED; + + Frametime=save_frametime; +} + +int Max_used_objects = MAX_OBJECTS - 20; +float Last_position_history_update[MAX_POSITION_HISTORY];//gametime of the last position history update of the object +float Last_position_history_update_time = 0.0f; + +//-------------------------------------------------------------------- +//Process all objects for the current frame +#define OBJ_POS_SAMPLE_TIME (1.0f/( ((float)MAX_POSITION_HISTORY)*20.0f) )//super sample at 20 fps +void ObjDoFrameAll() +{ + int i; + object *objp; + int objs_live=0; + bool update_position_history = false; + + if(Last_position_history_update_time>Gametime) + { + // the Gametime rolled over (new level) + update_position_history = true; + } + + if(Gametime>=Last_position_history_update_time+OBJ_POS_SAMPLE_TIME) + { + update_position_history = true; + } + + if(update_position_history) + { + Object_position_head--; + if(Object_position_head==255) + Object_position_head = MAX_POSITION_HISTORY-1; + + Last_position_history_update[Object_position_head] = Gametime; + Last_position_history_update_time = Gametime; + } + +//MT: seed code removed 9/20/99 because it breaks the randomness of the +//ambient sound patterns. +//IMPORTANT NOTE: Because this code could have far-reaching consequences, +//please DO NOT check this in until after the regular patches for D3 are +//done. This code should only go in for the Mercenary patch, which will +//get adequate testing to make sure nothing serious has broken. +//@@ // Seed timer for this frame +//@@ int seed=Gametime*1000; +//@@ ps_srand (seed); + + // Does control for soar objects + if (Soar_active) + SoarTick(Frametime); + + Physics_NumLinked = 0; + + //Process each object + for (i=0,objp=Objects;i<=Highest_object_index;i++,objp++) { + if ( (objp->type != OBJ_NONE) && (!(objp->flags&OF_DEAD)) ) { + RTP_STARTINCTIME(obj_do_frm); + ObjDoFrame(objp); + + if(update_position_history) + { + int slot = Object_map_position_history[OBJNUM(objp)]; + if(slot!=-1) + { + Object_position_samples[slot].pos[Object_position_head] = objp->pos; + } + } + + RTP_ENDINCTIME(obj_do_frm); + objs_live++; + } + } + + // Account for linked objects + for(i = 0; i < Physics_NumLinked; i++) + { + RTP_STARTINCTIME(phys_link); + DoPhysLinkedFrame(&Objects[PhysicsLinkList[i]]); + RTP_ENDINCTIME(phys_link); + } + + // Move vis effects + { + RTP_STARTINCTIME(vis_eff_move); + VisEffectMoveAll (); + RTP_ENDINCTIME(vis_eff_move); + } + + // Blend all lights that are needed + BlendAllLightingEdges(); + + mprintf_at ((1,5,40,"Objs=%d ",objs_live)); + + //Delete everything that died + ObjDeleteDead(); +} + +#define FUELCEN_SOUND_DELAY 0.25 //play every quarter second +#define FUELCEN_GIVE_RATE 25 //give 25 units per second + +//Give the player fuel. Called when the player is in an energy center +void RefuelPlayer(object *objp) +{ + float max,amount; + + ASSERT(objp->type == OBJ_PLAYER); + ASSERT(Rooms[objp->roomnum].flags & RF_FUELCEN); + + max = INITIAL_ENERGY - Players[objp->id].energy; + amount = FUELCEN_GIVE_RATE * Frametime; + + if (amount > max) + amount = max; + + if ((amount > 0)) { + Players[objp->id].energy += amount; + + Sound_system.Play3dSound(SOUND_REFUELING,SND_PRIORITY_HIGH,objp); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.01f; + hear.curiosity_level = 1.0f; + hear.max_dist = Sounds[SOUND_REFUELING].max_distance; + AINotify(objp, AIN_HEAR_NOISE, (void *)&hear); + + //if (Game_mode & GM_MULTI) + // multi_send_play_sound(SOUND_REFUEL_STATION_GIVING_FUEL, F1_0/2); + } +} + +//Clears the secret flag in the specified room, and recursively, all connected rooms +void ClearSecretFlags(room *rp) +{ + int p; + portal *pp; + + rp->flags &= ~RF_SECRET; + + for (p=0,pp=rp->portals;pnum_portals;p++,pp++) { + room *rp2 = &Rooms[pp->croom]; + if (rp2->flags & RF_SECRET) + ClearSecretFlags(rp2); + } + +} +// Prints out a message when you've found a secret +#define SECRET_COLOR GR_RGB(0,128,255) +void DoSecretForPlayer (room *rp,object *objp) +{ + int y = Game_window_h / 4; + AddPersistentHUDMessage(SECRET_COLOR,HUD_MSG_PERSISTENT_CENTER,y,5,HPF_FADEOUT+HPF_FREESPACE_DRAW,SOUND_GAME_MESSAGE,TXT_FOUND_SECRET); + + ClearSecretFlags(rp); +} + +// -------------------------------------------------------------------- + +//Do refueling centers & damage rooms +void DoSpecialPlayerStuff(object *objp) +{ + if (! OBJECT_OUTSIDE(objp)) { + room *rp = &Rooms[objp->roomnum]; + + //Check for refueling + if (rp->flags & RF_FUELCEN) + RefuelPlayer(objp); + + if (objp->id==Player_num && (rp->flags & RF_SECRET)) + DoSecretForPlayer (rp,objp); + + //Check for damage room + if (rp->damage > 0.0) { + if (! (objp->effect_info->type_flags & EF_NAPALMED)) { + + //Set the player on fire + ASSERT(objp->effect_info); + objp->effect_info->type_flags|=EF_NAPALMED; + objp->effect_info->damage_time = 1.0; + objp->effect_info->damage_per_second = rp->damage; + if (Gametime-objp->effect_info->last_damage_time>1.0f) + objp->effect_info->last_damage_time=0; + objp->effect_info->damage_handle=OBJECT_HANDLE_NONE; + if (objp->effect_info->sound_handle == SOUND_NONE_INDEX) + objp->effect_info->sound_handle = Sound_system.Play3dSound(SOUND_PLAYER_BURNING, SND_PRIORITY_HIGHEST, objp); + } + } + + //Check for & set waypoint + if (rp->flags & RF_WAYPOINT) + SetAutoWaypoint(objp); + } + else { //outside + + //Check for & set waypoint + //if (Terrain_seg[CELLNUM(objp->roomnum)].flags & TF_WAYPOINT) + // SetAutoWaypoint(objp); + } +} + +//Builds the free object list by scanning the list of free objects & adding unused ones to the list +//Also sets Highest_object_index +void ResetFreeObjects() +{ + int i; + + Highest_object_index = -1; + + for (i=Num_objects=MAX_OBJECTS;--i >= 0;) + if (Objects[i].type == OBJ_NONE) + free_obj_list[--Num_objects] = i; + else if (Highest_object_index == -1) + Highest_object_index = i; +} + +//sets the orientation of an object. This should be called to orient an object +void ObjSetOrient(object *obj,const matrix *orient) +{ + // Accounts for if the orientation was set and then this function is being used + // to update the other stuff + if(&obj->orient != orient) + obj->orient = *orient; + + // Recompute the orientation dependant information + if(obj->flags & OF_POLYGON_OBJECT) + { + if(obj->type != OBJ_WEAPON && + obj->type != OBJ_DEBRIS && + obj->type != OBJ_POWERUP && + obj->type != OBJ_ROOM) + { + matrix m; + + m = obj->orient; + vm_TransposeMatrix(&m); + + obj->wall_sphere_offset = Poly_models[obj->rtype.pobj_info.model_num].wall_size_offset * m; + obj->anim_sphere_offset = Poly_models[obj->rtype.pobj_info.model_num].anim_size_offset * m; + } + else + { + obj->wall_sphere_offset = Zero_vector; + obj->anim_sphere_offset = Zero_vector; + } + } +} + +//sets the position of an object. This should be called to move an object +void ObjSetPos(object *obj,vector *pos,int roomnum,matrix *orient,bool f_update_attached_children) +{ + int oldroomnum=obj->roomnum; + vector old_pos=obj->pos; + + //Reset the position & recalculate the AABB + obj->pos = *pos; + ObjSetAABB(obj); + + //Reset the orientation if changed + if (orient != NULL) + ObjSetOrient(obj, orient); + + //Clear the outside-mine flag + obj->flags &= ~OF_OUTSIDE_MINE; + + //If changed rooms, do a bunch of stuff + if (obj->roomnum != roomnum) { + + //Let the script know + tOSIRISEventInfo ei; + ei.evt_changeseg.room_num = roomnum; + Osiris_CallEvent(obj,EVT_CHANGESEG,&ei); + + if(obj->type == OBJ_PLAYER && !ROOMNUM_OUTSIDE(roomnum) && (Rooms[roomnum].flags & RF_INFORM_RELINK_TO_LG)) + { + Level_goals.Inform(LIT_INTERNAL_ROOM, LGF_COMP_ENTER, roomnum); + } + + //Relink the object + ObjRelink(OBJNUM(obj),roomnum); + + // Call DLL function if the server player changed rooms + if ((Game_mode & GM_MULTI) && (Netgame.local_role==LR_SERVER) ){ + DLLInfo.me_handle=obj->handle; + DLLInfo.it_handle=obj->handle; + DLLInfo.oldseg = oldroomnum; + DLLInfo.newseg = obj->roomnum; + + if(obj->type==OBJ_PLAYER){ + CallGameDLL (EVT_GAMEPLAYERCHANGESEG,&DLLInfo); + }else{ + CallGameDLL (EVT_GAMEOBJCHANGESEG,&DLLInfo); + } + } + + // Slowly change volume lighting if going between rooms, if not in the editor + if (GetFunctionMode() != EDITOR_MODE) { + if ((obj->effect_info!=NULL) && (obj->effect_info->type_flags & EF_VOLUME_LIT)) { + if (!ROOMNUM_OUTSIDE(oldroomnum) && !ROOMNUM_OUTSIDE(roomnum)) { + if (!(obj->effect_info->type_flags & EF_VOLUME_CHANGING)) + { + obj->effect_info->type_flags|=EF_VOLUME_CHANGING; + obj->effect_info->volume_change_time=1.0; + obj->effect_info->volume_old_room=oldroomnum; + obj->effect_info->volume_old_pos=old_pos; + } + } + else //either old or new room was outside, so don't do volume changing + obj->effect_info->type_flags&=~EF_VOLUME_CHANGING; + } + } + } +} + +//Delete objects, such as weapons & explosions, that shouldn't stay between levels +//if clear_all is set, clear even proximity bombs +void ClearTransientObjects(int clear_all) +{ + int objnum; + object *objp; + + for (objnum=0,objp=Objects;objnum<=Highest_object_index;objnum++,objp++) { + if (((objp->type == OBJ_WEAPON) && !(Weapons[objp->id].flags & WF_COUNTERMEASURE)) || + (objp->type == OBJ_FIREBALL) || + (objp->type == OBJ_DEBRIS) || + (objp->type == OBJ_SHARD) || + (objp->type == OBJ_SHOCKWAVE) || + (objp->type == OBJ_PARTICLE) || + (objp->type == OBJ_SPLINTER) || + ((objp->type != OBJ_NONE) && (objp->flags & OF_DYING))) { + mprintf((0,"Clearing object %d type = %d\n",objnum,objp->type)); + ObjDelete(objnum); + } + } +} + + +// Remaps all static powerups,sounds,etc to their appropriate indices +void RemapEverything() +{ + RemapStaticIDs(); + RemapDoors(); + RemapShips(); + RemapWeapons(); + RemapSounds(); + RemapPolyModels(); +} + + +//Retruns a pointer to an object given its handle. Returns NULL if object no longer exists. +object *ObjGet(int handle) +{ + if (handle == OBJECT_HANDLE_NONE) + return NULL; + + if (handle == OBJECT_HANDLE_BAD) { + Int3(); + return NULL; + } + + int objnum = handle & HANDLE_OBJNUM_MASK; + object *objp; + + ASSERT((handle & HANDLE_COUNT_MASK) != 0); //count == 0 means never-used object + ASSERT(objnum < MAX_OBJECTS); + + objp = &Objects[objnum]; + + if ((objp->type != OBJ_NONE) && (objp->handle == handle)) + return objp; + else + return NULL; +} + +void GetObjectPointInWorld (vector *dest,object *obj,int subnum,int vertnum) +{ + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + bsp_info *sm=&pm->submodel[subnum]; + float normalized_time[MAX_SUBOBJECTS]; + int i; + + if (!pm->new_style) + return; + + for (i=0;iverts[vertnum]; + int mn = subnum; + matrix m; + + // Instance up the tree for this gun + while (mn != -1) + { + vector tpnt; + + vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b); + vm_TransposeMatrix(&m); + + tpnt = pnt * m; + + pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos; + + mn = pm->submodel[mn].parent; + } + + //now instance for the entire object + m = obj->orient; + vm_TransposeMatrix(&m); + + *dest = pnt * m; + *dest += obj->pos; +} + +bool ObjGetAnimUpdate(unsigned short objnum, custom_anim *multi_anim_info) +{ + object *obj = &Objects[objnum]; + + if((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && + (obj->flags & OF_POLYGON_OBJECT)) + { + polyobj_info *pm = &obj->rtype.pobj_info; + ai_frame *ai_info = obj->ai_info; + + multi_anim_info->server_time = Gametime; + multi_anim_info->server_anim_frame = (ushort)(pm->anim_frame * 256.0f); + + multi_anim_info->anim_start_frame = pm->anim_start_frame; + multi_anim_info->anim_end_frame = pm->anim_end_frame; + multi_anim_info->anim_time = pm->anim_time; + multi_anim_info->max_speed = pm->max_speed; + + multi_anim_info->flags = 0; + + if(pm->anim_flags & AIAF_LOOPING) + multi_anim_info->flags |= FMA_LOOPING; + + if(obj->ai_info != NULL) + { + multi_anim_info->anim_sound_index = obj->ai_info->last_played_sound_index; + multi_anim_info->flags |= FMA_HAS_AI; + } + else + { + multi_anim_info->anim_sound_index = -1; + } + + return true; + } + + return false; +} + +void SetObjectControlType(object *obj, int control_type) +{ + ASSERT(obj); + ASSERT(OBJNUM(obj) >= 0 && OBJNUM(obj) < MAX_OBJECTS); + + if((control_type == CT_AI) && (obj->ai_info == NULL)) + { + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + polyobj_info *p_info = &obj->rtype.pobj_info; + int num_wbs = pm->num_wbs; + int count = 0; + int i; + + obj->ai_info = (ai_frame *) mem_malloc(sizeof(ai_frame)); + memset(obj->ai_info, 0x00, sizeof(ai_frame)); //DAJ clear the baby + + for(i = 0; i < num_wbs; i++) + { + ASSERT(pm->poly_wb[i].num_turrets >= 0 && pm->poly_wb[i].num_turrets <= 6400); + count += pm->poly_wb[i].num_turrets; + } + + p_info->multi_turret_info.num_turrets = count; + + if((count > 0) && (p_info->multi_turret_info.keyframes == NULL)) + { + int cur = 0; + + p_info->multi_turret_info.time = 0; + p_info->multi_turret_info.keyframes = (float *) mem_malloc(sizeof(float) * count); + p_info->multi_turret_info.last_keyframes = (float *) mem_malloc(sizeof(float) * count); + p_info->multi_turret_info.flags = 0; + } + } + + obj->control_type = control_type; + + if(obj->control_type == CT_AI || obj->type == OBJ_PLAYER) + { + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + int num_wbs = pm->num_wbs; + + if(obj->dynamic_wb == NULL) + { + if(obj->type == OBJ_PLAYER) + { + obj->dynamic_wb = (dynamic_wb_info *) mem_malloc(sizeof(dynamic_wb_info) * MAX_WBS_PER_OBJ); + } + else + { + if (num_wbs) + obj->dynamic_wb = (dynamic_wb_info *) mem_malloc(sizeof(dynamic_wb_info) * num_wbs); + } + } + } +} + +void ObjSetAnimUpdate(unsigned short objnum, custom_anim *multi_anim_info) +{ + object *obj = &Objects[objnum]; + polyobj_info *pm; + + if((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && + (obj->flags & OF_POLYGON_OBJECT)) + { + pm = &obj->rtype.pobj_info; + pm->multi_anim_info = *multi_anim_info; + + // Make sure we mark it as current and + pm->multi_anim_info.flags |= FMA_VALID; + pm->multi_anim_info.flags &= ~FMA_CURRENT; + } +} + +void ObjGetTurretUpdate(unsigned short objnum, multi_turret *multi_turret_info) +{ + object *obj = &Objects[objnum]; + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + polyobj_info *p_info = &obj->rtype.pobj_info; + + if((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && + (obj->flags & OF_POLYGON_OBJECT) && (p_info->multi_turret_info.num_turrets)) + { + int i; + int j; + int count = 0; + + multi_turret_info->time = Gametime; + multi_turret_info->num_turrets = p_info->multi_turret_info.num_turrets; + + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + multi_turret_info->keyframes[count++] = obj->dynamic_wb[i].norm_turret_angle[j]; + } + } + } +} + +void ObjSetTurretUpdate(unsigned short objnum, multi_turret *multi_turret_info) +{ + object *obj = &Objects[objnum]; + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + polyobj_info *p_info = &obj->rtype.pobj_info; + + if((objnum >= 0) && (obj->type != OBJ_NONE) && (obj->type != OBJ_WEAPON) && + (obj->flags & OF_POLYGON_OBJECT) && (p_info->multi_turret_info.num_turrets)) + { + int i; + + p_info->multi_turret_info.time = multi_turret_info->time; + + for(i = 0; i < p_info->multi_turret_info.num_turrets; i++) + { + p_info->multi_turret_info.keyframes[i] = multi_turret_info->keyframes[i]; + } + + p_info->multi_turret_info.flags = FMT_NEW_DATA; + } + else + { + mprintf((0,"Woops, no turret here to update!\n")); + } + +} + +//Returns the original parent for the given object. Returns self if it has no parent +object *ObjGetUltimateParent(object *child) +{ + ASSERT(child); + + object *ret = child; + int handle; + + handle = child->parent_handle; + + while(ObjGet(handle)){ + ret = ObjGet(handle); + handle = ret->parent_handle; + } + return ret; +} + +//Sets an object to a type OBJ_DUMMY (saves it's old type) so it won't be renderered, etc, but still alive +void ObjGhostObject(int objnum) +{ + ASSERT ( objnum>=0 && objnum=MAX_OBJECTS) + return; + + object *obj = &Objects[objnum]; + + ASSERT ( obj->type!=OBJ_NONE ); + ASSERT ( obj->type!=OBJ_DUMMY ); //Don't ghost a ghosted object!! + ASSERT ( obj->type!=OBJ_PLAYER ); + ASSERT ( obj->type!=OBJ_GHOST ); + + if( obj->type==OBJ_NONE || obj->type==OBJ_DUMMY || + obj->type==OBJ_PLAYER ||obj->type==OBJ_GHOST ) + return; + + obj->dummy_type = obj->type; + obj->type = OBJ_DUMMY; +} + +//Restores a ghosted object back to it's old type +void ObjUnGhostObject(int objnum) +{ + ASSERT ( objnum>=0 && objnum=MAX_OBJECTS) + return; + + object *obj = &Objects[objnum]; + + ASSERT ( obj->type==OBJ_DUMMY ); + + if(obj->type!=OBJ_DUMMY) + return; + + //@@ASSERT (!(obj->flags&OF_INPLAYERINVENTORY)); + //@@if(obj->flags&OF_INPLAYERINVENTORY) + //@@ return; + if(obj->flags&OF_INPLAYERINVENTORY) + { + mprintf((0,"UnGhosting Object in that is currently in a player's inventory!\n")); + } + + obj->type = obj->dummy_type; + obj->dummy_type = OBJ_NONE; +} diff --git a/Descent3/object.h b/Descent3/object.h new file mode 100644 index 000000000..aa8cde3c7 --- /dev/null +++ b/Descent3/object.h @@ -0,0 +1,824 @@ +/* + * $Logfile: /DescentIII/Main/object.h $ + * $Revision: 174 $ + * $Date: 10/22/99 2:06p $ + * $Author: Matt $ + * + * Header for object.c + * + * $Log: /DescentIII/Main/object.h $ + * + * 174 10/22/99 2:06p Matt + * Mac merge + * + * 173 9/18/99 9:24p Jeff + * object position history code (needed for motion blur effect) + * + * 172 5/22/99 1:54a Matt + * Got rid of debug code mistakenly checked in. + * + * 171 5/21/99 3:43a Matt + * + * 170 2/23/99 12:39p Jason + * added more options for generic objects + * + * 169 2/21/99 4:20p Matt + * Added SoundSource objects (and reformatted parts of the object header + * files). + * + * 168 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 167 2/08/99 5:26p Jeff + * removed all calls to MultiSendRemoveObject, incorportated into + * SetObjectDeadFlag. Fixes sequencing issues in multiplayer + * + * 166 2/05/99 3:00p Jason + * fixed some framerate problems + * + * 165 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 164 1/21/99 3:34p Jason + * added liquid code + * + * 163 1/19/99 4:22p Matt + * Added the ability for objects to have their own lighting info, + * different from the default lighting for that type of object. + * + * 162 1/18/99 2:46p Matt + * Combined flags & flags2 fields in object struct + * + * 161 1/15/99 7:52p Chris + * Updated ObjSetPos() to include a f_update_attach_children flag + * + * 160 1/13/99 6:38a Jeff + * fixed object.h. There were numerous struct declarations that were the + * same name as the instance of the struct (gcc doesn't like this). + * Changed the struct name. Also added some #ifdef's for linux build, + * along with fixing case-sensitive includes + * + * 159 1/13/99 3:25a Chris + * Added Obj_Burning and Obj_IsEffect to OSIRIS + * + * 158 1/13/99 12:12a Jeff + * changed the name of the obj_link_info struct to object_link_info + * (because the instance is named the same). Allows it to compile under + * GCC + * + * 157 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 156 1/08/99 12:02p Chris + * + * 155 1/06/99 5:06p Chris + * Improving OSIRIS/game intergration - improved support for custom + * animations + * + * 154 1/04/99 12:42p Chris + * Updated OSIRIS with "xxxx_external.h" files and Obj_Value, and + * Matcen_XXX functions + * + * 153 12/21/98 11:30p Matt + * Added names for objects + * + * 152 12/21/98 6:01p Matt + * Changed the object handle bit allocation to handle object numbers up to + * 2047. + * Increased MAX_OBJECTS to 1500. + * + * 151 12/18/98 6:00p Jeff + * added support for door scripts in new osiris + * + * 150 12/16/98 11:27a Jeff + * added new osiris fields + * + * 149 12/10/98 7:09p Jason + * added cloak fade + * + * 148 12/10/98 12:27p Jason + * added cooler specular mapping for objects + * + * 147 11/03/98 6:16p Chris + * Starting to make on/off and spray weapons accessable to robots + * + * 146 10/20/98 10:27p Chris + * + * 145 10/19/98 11:46p Jason + * changes for multiplayer debug layer + * + * 144 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 143 10/19/98 3:14p Chris + * Version .05 of locked axes + * + * 142 10/17/98 12:26p Chris + * Fixed attached flares + * + * 141 10/16/98 5:25p Chris + * Fixed sticky code + * + * 140 10/16/98 4:18p Chris + * Fixed the 'flare thing' + * + * 139 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 138 10/15/98 6:46p Chris + * Added custom size for weapons + * + * 137 10/15/98 10:46a Chris + * Made powerups fall to below the ceiling height + * + * 136 10/13/98 11:30a Matt + * Added parens around a macro value + * + * 135 10/12/98 7:09p Matt + * Removed some obsolete stuff + * + * 134 10/06/98 11:27a Jason + * added new death type for robots + * + * 133 9/28/98 6:23p Chris + * Changed multi_anim to custom_anim + * + * 132 9/22/98 12:40p Matt + * Cleaned up the object per-frame processing code. + * + * 131 8/18/98 12:57a Samir + * added ObjUnlink call. + * + * 130 8/12/98 6:37p Jeff + * added functions to ghost an object (make it's type to OBJ_DUMMY) and + * unghost + * + * 129 8/12/98 12:04p Chris + * Attach system version .5 (still needs more work to be multiplayer + * friendly) + * + * 128 8/07/98 8:25p Chris + * Improved the attach system + * + * 127 8/03/98 3:59p Chris + * Added support for FQ_IGNORE_WEAPONS, added .000001 attach code, fix a + * bug in polymodel collision detection + * + * 126 7/31/98 11:52a Chris + * Weapons can be persistent. Added ability for objects to be manually + * set for no object collisions. + * + * 125 7/30/98 11:09a Jason + * added weapons that freeze and deform terrain + * + * 124 7/22/98 3:16p Jason + * added observer mode + * + * 123 7/20/98 2:48p Chris + * Removed hit_time (not neccessary) + * + * 122 7/20/98 2:06p Chris + * Added the precomputation of some weapons (fixed speed weapons). This + * algorithm can be expanded to include some missiles with some minor + * work. + * + * 121 7/19/98 9:44p Chris + * Greatly improved homing performance + * + * 120 7/09/98 7:50p Jeff + * added ObjGetUltimateParent to find out the original parent of an + * object, traces the family tree + * + * 119 7/09/98 11:34a Chris + * Turrets are interpolated. + * + * 118 7/08/98 11:38a Chris + * Improved the turret info passing in multiplayer + * + * 117 7/07/98 7:34p Jeff + * created object type, OBJ_DUMMY + * + * 116 7/07/98 3:26p Chris + * Added changes for turret updates + * + * 115 7/06/98 5:36p Kevin + * Variable parameter passing + * + * 114 7/02/98 2:47p Chris + * Dynamic weapon info is now dynamically allocated + * + * 113 7/01/98 6:36p Chris + * Added more multiplayer support + * + * 112 7/01/98 4:35p Chris + * More multiplayer sync issues + * + * 111 7/01/98 2:02p Chris + * Added the sound for animations + * + * 110 6/30/98 4:28p Chris + * Checked in some missing commas + * + * 109 6/26/98 2:04p Jason + * fixed some graphical problems with the omega + * + * 108 6/12/98 4:07p Jason + * added dynamic volume lighting to objects + * + * 107 6/11/98 12:48p Jason + * added better spewing weapons + * + * 106 5/19/98 4:42a Chris + * Added shockwave's -- enjoy. :) + * + * 105 5/15/98 5:41p Jason + * implemented volume lighting system + * + * 104 5/14/98 12:56p Jason + * changes to help lower memory usage + * + * 103 5/11/98 4:40p Chris + * AI info is now a malloc'ed pointer + * + * 102 5/08/98 1:16p Jason + * added PF_DESTINATION_POS flag for interpolating multiplayer positions + * + * 101 5/07/98 1:41p Chris + * Added hit_death_dot + * + * 100 5/04/98 12:30p Matt + * ObjCreate() now takes object id as a ushort + * + * 99 5/04/98 12:28p Matt + * Added shard objects + * + * 98 5/01/98 3:41p Chris + * + * 97 5/31/98 3:08p Chris + * Allowed for death animations + * + * 96 5/30/98 10:16p Chris + * Fixed some bad spelling + * + * 95 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 94 4/17/98 1:59p Jason + * added cool object effects + * + * 93 4/15/98 12:56p Chris + * Updated parent_handle support and added buddy bot support + * + * 92 4/13/98 4:19p Chris + * Added PF_IGNORE_CONCUSSIVE_FORCES + * + * 91 4/10/98 12:41p Jason + * sped up lighting a little + * + * 90 4/09/98 2:23p Jason + * added guided/afterburner stuff + * + * 89 4/09/98 12:05p Chris + * Added parenting for all object types. :) + * + * 88 4/08/98 6:02p Luke + * + * 87 4/08/98 3:47p Chris + * Moved stuff around so that the object flags can stay 16 bits + * + * 86 4/08/98 2:53p Chris + * Added flags for move this frame and stop this frame + * + * 85 3/31/98 4:23p Chris + * Added a new AIpath system + * + * 84 3/20/98 9:34p Jason + * added SetObjectDeadFlag inlined function + * + * 83 3/16/98 3:47p Jason + * got rid of circular dependencies for objects and polymodels + * + * 82 3/13/98 5:55p Chris + * Added the new collision spheres + * + * 81 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 80 3/09/98 4:54p Jason + * don't render objects that can't been seen through a portal + * + * 79 3/09/98 8:12a Chris + * Added the start of the homing code + * + * 78 3/06/98 2:15p Chris + * Changed target_object to target_handle + * + * 77 2/20/98 1:46p Chris + * JASON: Made dynamic lighting only occur if the object is rendered + * + * 76 2/19/98 11:21a Chris + * Made ROOMNUM_OUTSIDE return a bool + * + * 75 2/18/98 1:39p Jason + * more changes for lighting + * + * 74 2/17/98 5:54p Luke + * + * 73 2/16/98 2:49p Chris + * Made the MAX_SUBOBJECTS determine the number of normalized_time values + * to be processed. No longer a 'literal' problem. + * + * 72 2/16/98 2:47a Chris + * Massive improvements to the animation system and the AI + * + * 71 2/11/98 6:58p Matt + * Changed the way cameras are rendered, so they show up on the terrain as + * well. + * + * 70 2/11/98 7:01p Chris + * Started to add the new anim stuff + * + * 69 2/09/98 7:28p Matt + * Added a rendering type for external room so we could check against + * render type == RT_NONE + * + * 68 2/06/98 2:57a Chris + * Added point_collide_with_walls and ignore_robot_collisions + * + * 67 2/05/98 3:00p Matt + * Made ObjSetPos() take optional orienation. Got rid of unlink & relink + * funcs from header. + * + * 66 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 65 2/03/98 11:17a Samir + * Added macro to get handle from an object. + * + * 64 1/29/98 5:49p Matt + * Changed old camera object type to be viewer object (for editor), and + * now camera objects will just be for game cameras. + * + * 63 1/29/98 10:07a Matt + * Added a constant that defines an invalid object handle + * + * 62 1/27/98 12:24p Samir + * Added GetObjectPointInWorld. + * + * 61 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 60 1/20/98 4:12p Samir + * New script housekeeping system. + * + * 59 1/19/98 2:44p Samir + * Use one script per object and started parameter passing support. + * + * 58 1/19/98 10:04a Matt + * Added new object handle system + * + * 57 1/07/98 3:32p Chris + * Ship's wiggle falloff is computed via last thrust time. + * + * 56 1/05/98 3:54p Chris + * Added ambient and explosion sounds + * + * 55 12/08/97 5:22p Jason + * added code for destroyable buildings that leave their base object + * around + * + * 54 12/03/97 5:53p Jason + * worked on cooler explosion effects + * + * 53 12/03/97 4:31p Chris + * Added the initial shockwave structure + * + * 52 12/01/97 9:54a Chris + * Added support for concussive forces, generalized robot collisions to + * generic collisions + * + * 51 11/12/97 5:47p Chris + * re-ordered the object structure so that Soar people can get to known + * data elements without having to worry about other stuff. + * + * 50 10/29/97 10:35a Chris + * Added the no collide/relink flag to physics -- Dangerous ask Chris how + * to use correctly + * + * 49 10/24/97 2:30p Jason + * more changes for explosions + * + * 48 10/23/97 6:14p Jason + * tweaks for splinter objects + * + * 47 10/23/97 5:36p Jason + * added splinter objects (probably temporary) + * + * 46 10/23/97 2:06p Chris + * Added physics structural support for walkers/flyers with reduced + * physics + * + * 45 10/22/97 4:19p Jason + * added lifetime field to object structure + * + * 44 10/20/97 5:54p Jason + * added polygon_object flag + * + * 43 10/20/97 4:46p Jason + * changes for explosions + * + * 42 10/15/97 5:07p Chris + * Added a gravity mask define + * + * 41 10/14/97 6:53p Chris + * Added the initial SOAR support + * + * 40 10/03/97 5:11p Chris + * Wrong MAX_OBJECT_TYPES fixed + * + * 39 10/03/97 4:43p Chris + * added debug fvi call and object type line + * + * 38 10/02/97 1:04p Jason + * implemented FreeAllObjects + * + * 37 10/01/97 7:00p Jason + * did more work on object lightmaps + * + * 36 10/01/97 6:15p Matt + * Added object type for rooms, and changed object id to a short + * + * 35 10/01/97 10:59a Chris + * + * 34 9/30/97 6:40p Jason + * got lightmap stuff sort of working with objects + * + * 33 9/24/97 6:18p Samir + * Use script names instead of script id values to identify scripts. + * + * 32 9/22/97 6:20p Matt + * Removed some obsolete render type constants + * + * 31 9/19/97 6:58p Chris + * Fixed some bugs with the big object system and removed some annoying + * mprintf's + * + * 30 9/19/97 1:01p Chris + * Better big object support + * + * 29 9/18/97 1:27p Matt + * Cleaned up object struct + * + * 28 9/17/97 12:02p Jason + * put back segnum field in object struct (for the terrain) + * + * 27 9/17/97 11:16a Matt + * Ripped out segment code + * + * 26 9/15/97 5:21a Chris + * Added sphere2poly support + * + * 25 9/12/97 6:36p Chris + * Added collision terrain segment support + * Added some LARGE OBJECT collision support + * + * 24 9/11/97 5:38p Jason + * initial door coding for room engine + * + * 23 9/10/97 5:17p Jason + * more lighting improvements + * + * 22 9/05/97 1:29p Jason + * revamped generic object lighting + * + * 21 9/04/97 3:21p Jason + * added pulse timing for lit objects + * + * 20 9/03/97 6:17p Jason + * added code to support powerups/robots/etc that cast light + * + * 19 9/03/97 3:54p Jason + * got objects to cast light + * + * 18 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 17 8/13/97 4:53p Matt + * Added OF_DESTROYABLE flag + * + * 16 8/12/97 3:56p Matt + * Added new object type: OBJ_BUILDING + * + * 15 8/12/97 1:13p Chris + * Added AABBs. + * + * 14 8/11/97 9:57p Samir + * Added scripts to objects. + * + * 13 8/07/97 4:44p Chris + * Massively expanded the weapon system. Not done yet. + * + * 12 8/06/97 4:32p Chris + * Expanded the weapons page + * + * 11 8/04/97 4:09p Matt + * Added ObjSetPos() that takes the new room number + * + * 10 7/29/97 10:27a Jason + * added RenderOBject abstraction layer + * + * 9 7/28/97 1:14p Chris + * Added support for sub-object visability. Plus, debris. + * + * 8 7/15/97 5:32p Jason + * got simple static lighting working with objects + * + * 7 7/15/97 4:59p Chris + * + * 6 7/15/97 4:59p Chris + * added support for AI and animations + * + * 23 6/30/97 5:03p Chris + * Added new AI stuff. + * + * 22 6/27/97 7:18p Matt + * Added OBJNUM() macro + * + * 21 5/22/97 5:15p Chris + * + * 20 5/16/97 2:17p Samir + * ResetPlayerObject added. + * + * 19 5/14/97 3:41p Chris + * + * 18 5/09/97 5:34p Samir + * Added object scripting to object structure. + * + * 17 4/24/97 5:42p Jason + * got fireball vclips working + * + * 16 4/14/97 3:48p Jason + * changed RT_LASER to RT_WEAPON + * + * 15 4/09/97 9:57p Chris + * Added more ability to customize ships/objects. + * + * 14 4/07/97 3:13p Chris + * + * 13 4/04/97 2:57p Matt + * Added code to initialize all the type-specific data for an object from + * the page for that object type. This means that we need to pass less + * info to ObjCreate(), and that we save less info in the level save file. + * It also makes it easy to reset all the objects when an object page has + * changed. + * + * 12 3/27/97 10:45a Chris + * Set player mass and drag info + * + * 11 3/21/97 5:02p Jason + * change short to int in segnum field of object struct + * + * 10 3/20/97 5:45p Jason + * made object work with and render on terrain + * + * 9 3/20/97 12:17p Jason + * added terrain flag to object flags + * + * 8 3/14/97 12:18p Chris + * Tweak physics and remove braking for now. + * + * 7 2/13/97 12:16p Jason + * changes for powerup remapping + * + * 6 2/11/97 6:51p Matt + * Ripped out some old stuff & renamed a function + * + * 5 2/07/97 5:48p Matt + * Renamed vector.h to vecmat.h to fix DevStudio problem + * + * $NoKeywords: $ + */ + +#ifndef _OBJECT_H +#define _OBJECT_H + +#include "pstypes.h" +#include "pserror.h" +#include "object_external_struct.h" +#include "object_external.h" + +/* + * CONSTANTS + */ + +//Object handle stuff. +//The handle is comprised of the object number in the low 10 bits, and a count in the high 22 bits. +#define HANDLE_OBJNUM_MASK 0x7ff //to mask off the object number part of the handle +#define HANDLE_COUNT_MASK 0xfffff800 //to maks off the count part of the handle +#define HANDLE_COUNT_INCREMENT 0x800 //what gets added to the handle to increment it + +// See object external for OBJ_ types + +// Lighting render types +#define LRT_STATIC 0 +#define LRT_GOURAUD 1 +#define LRT_LIGHTMAPS 2 + +extern char *Object_type_names[MAX_OBJECT_TYPES]; + + +//stuctures for different kinds of weapon simulation (for precompution) + +#define WPC_NOT_USED 0 +#define WPC_NO_COLLISIONS 1 +#define WPC_HIT_WALL 2 + + +#define FMA_VALID 1 +#define FMA_CURRENT 2 +#define FMA_LOOPING 4 +#define FMA_USE_SPEED 8 +#define FMA_HAS_AI 16 + + +#define FMT_NEW_DATA 1 +#define FMT_UPDATING 2 + +//object light info flags +#define OLF_FLICKERING 1 +#define OLF_TIMEBITS 2 +#define OLF_PULSE 4 +#define OLF_PULSE_TO_SECOND 8 +#define OLF_FLICKER_SLIGHTLY 16 +#define OLF_DIRECTIONAL 32 // Directional light - casts light in a cone +#define OLF_NO_SPECULARITY 64 // Object does not have specular light cast on it + +// OSIRIS defines +#define MAX_MODULENAME_LEN 32 + +//How long an object name can be +#define OBJ_NAME_LEN 19 //max length for object name + +/* + * VARIABLES + */ + +extern object Objects[]; +extern int Highest_object_index; //highest objnum + +extern object *Player_object; //the object that is the player +extern object *Viewer_object; //which object we are seeing from + +#define MAX_BIG_OBJECTS 350 +extern int Num_big_objects; +extern short BigObjectList[MAX_BIG_OBJECTS]; //DAJ_MR utb int + +//Compute the object number from an object pointer +#define OBJNUM(objp) (objp-Objects) +#define OBJHANDLE(objp) ((objp) ? (objp)->handle : 0) + + +/* + * FUNCTIONS + */ + + +// Set the dead flag for an object +void SetObjectDeadFlag (object *obj,bool tell_clients_to_remove = false,bool play_sound_on_clients=false); +inline void SetObjectDeadFlag (object *obj,bool tell_clients_to_remove,bool play_sound_on_clients) +{ + int objnum=OBJNUM(obj); + ASSERT(objnum != -1); + ASSERT(objnum != 0 ); + ASSERT(obj->type != OBJ_NONE); + ASSERT(obj != Player_object); + + obj->flags|=OF_DEAD; + + if(tell_clients_to_remove){ + if(play_sound_on_clients) + { + obj->flags|=OF_SEND_MULTI_REMOVE_ON_DEATHWS; + }else + { + obj->flags|=OF_SEND_MULTI_REMOVE_ON_DEATH; + } + } +} + +void SetObjectControlType(object *obj, int control_type); + +//do whatever setup needs to be done +void InitObjects(void); + +//links an object into a room's list of objects. +//takes object number and room number +void ObjLink(int objnum,int roomnum); + +// reverses ObjLink. +void ObjUnlink(int objnum); + +// Sets the AABB for the object +void ObjSetAABB(object *obj); + +//initialize a new object. adds to the list for the given room +//returns the object number +int ObjCreate(ubyte type,ushort id,int roomnum,vector *pos,const matrix *orient,int parent_handle = OBJECT_HANDLE_NONE); + +//remove object from the world +void ObjDelete(int objnum); + +//Resets the handles for all the objects. Called by the editor to init a new level. +void ResetObjectList(); + +//Builds the free object list by scanning the list of free objects & adding unused ones to the list +//Also sets Highest_object_index +void ResetFreeObjects(); + +// Frees all the objects that are currently in use +void FreeAllObjects(); + +//Deletes all objects that have been marked for death. +void ObjDeleteDead(); + +//Process all objects for the current frame +void ObjDoFrameAll(); + +//set viewer object to next object in array +void ObjGotoNextViewer(); + +//move an object for the current frame +void ObjMoveOne( object * obj ); + +//Sets the position of an object. This should be called whenever an object moves. +//Parameters: obj - the object being moved +// pos - the new position +// roomnum - the correct roomnum for pos. No error checking is done. +// orient - if this is not null, the object's orientation is set to this. +void ObjSetPos(object *obj,vector *pos,int roomnum,matrix *orient, bool f_update_attached_children); +void ObjSetOrient(object *obj,const matrix *orient); + +//delete objects, such as weapons & explosions, that shouldn't stay between levels +//if clear_all is set, clear even proximity bombs +void ClearTransientObjects(int clear_all); + +// Remaps all static powerups,sounds,etc to their appropriate indices +void RemapEverything(); + +void BigObjAdd(int objnum); +void InitBigObjects(void); + +//Creates the player object in the center of the given room +void CreatePlayerObject(int roomnum); + +//Retruns a pointer to an object given its handle. Returns NULL if object no longer exists. +object *ObjGet(int handle); + +// returns a vertex of an object in WORLD coordinates. +void GetObjectPointInWorld (vector *dest,object *obj,int subnum,int vertnum); + +// These functions are for setting and getting an objects animation information +// (used in multiplayer games and the like) +bool ObjGetAnimUpdate(unsigned short objnum, custom_anim *multi_anim_info); +void ObjSetAnimUpdate(unsigned short objnum, custom_anim *multi_anim_info); + +void ObjGetTurretUpdate(unsigned short objnum, multi_turret *multi_turret_info); +void ObjSetTurretUpdate(unsigned short objnum, multi_turret *multi_turret_info); + +//Returns the original parent for the given object. Returns self if it has no parent +object *ObjGetUltimateParent(object *child); + +//Sets an object to a type OBJ_DUMMY (saves its old type) so it won't be renderered, etc, but still alive +void ObjGhostObject(int objnum); + +//Restores a ghosted object back to it's old type +void ObjUnGhostObject(int objnum); + +///////////////////////////////// +// Position history information +// ---Not to be externalized--- +// Used for motion blur +///////////////////////////////// +#define MAX_OBJECT_POS_HISTORY (MAX_OBJECTS/2) +#define MAX_POSITION_HISTORY 3 +typedef struct +{ + vector pos[MAX_POSITION_HISTORY]; +}tPosHistory; +extern tPosHistory Object_position_samples[MAX_OBJECT_POS_HISTORY]; +extern ubyte Object_position_head; +extern signed short Object_map_position_history[MAX_OBJECTS]; +extern float Last_position_history_update[MAX_POSITION_HISTORY];// last gametime the positions were updated +void ObjInitPositionHistory(object *obj); +void ObjFreePositionHistory(object *obj); +void ObjResetPositionHistory(void); +void ObjReInitPositionHistory(void); +#endif diff --git a/Descent3/object_external.h b/Descent3/object_external.h new file mode 100644 index 000000000..f58ff5325 --- /dev/null +++ b/Descent3/object_external.h @@ -0,0 +1,336 @@ +/* + * $Logfile: /DescentIII/main/object_external.h $ + * $Revision: 37 $ + * $Date: 10/26/99 10:32a $ + * $Author: Jeff $ + * + * Object defines usable by both the main code & the DLLs + * + * $Log: /DescentIII/main/object_external.h $ + * + * 37 10/26/99 10:32a Jeff + * no red guidebot in non-Windows versions + * + * 36 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 35 10/12/99 11:06a Jeff + * added object effect flags for negative lighting and virus infection + * + * 34 6/08/99 1:01p Jason + * changes for bumpmapping + * + * 33 4/21/99 3:01p Matt + * Added a new type for dying objects that have AI, instead of keeping a + * flag in the dying info. + * + * 32 4/20/99 8:14p Chris + * Added support for object's that hit the ceiling and for making the + * level always check for the ceiling (inside and outside the mine) + * + * 31 4/20/99 5:39p Matt + * Added macro to check if an object is a robot. Does the + * type-is-robot-or-type-is-building-with-AI check. + * + * 30 4/18/99 10:55p Chris + * Added ignore own concussive blasts + * + * 29 4/18/99 8:13p Chris + * Fixed the floating flare problems (where windows where broken out and + * the flare remained) + * + * 28 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 27 3/31/99 3:59p Chris + * made the MC_ stuff externalized to OSIRIS. + * + * 26 3/28/99 5:56p Matt + * Added sparking effect for objects + * + * 25 3/26/99 3:26p Jeff + * option to display hud message when cloaking + * + * 24 3/11/99 6:31p Jeff + * numerous fixes to demo system in multiplayer games (when + * recording/playback a demo in a multiplayer game) + * + * 23 2/25/99 11:01a Matt + * Added new explosion system. + * + * 22 2/22/99 11:38p Matt + * Deleted static debris objects, since they were never used + * + * 21 2/21/99 4:35p Chris + * Improving the level goal system... Not done. + * + * 20 2/21/99 4:20p Matt + * Added SoundSource objects (and reformatted parts of the object header + * files). + * + * 19 2/13/99 12:36a Jeff + * new object flag. set for when an object is currently in a player's + * inventory + * + * 18 2/12/99 3:38p Jason + * added client side interpolation + * + * 17 2/11/99 6:25p Chris + * Added PF_NO_COLLIDE_DOORS + * + * 16 2/10/99 2:49p Matt + * Renamed OBJECT_HANDLE_INVALID to OBJECT_HANDLE_BAD + * + * 15 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 14 2/08/99 5:26p Jeff + * removed all calls to MultiSendRemoveObject, incorportated into + * SetObjectDeadFlag. Fixes sequencing issues in multiplayer + * + * 13 2/05/99 1:26p Matt + * Added a macro to check if a type is one of the generic types + * + * 12 1/27/99 6:08p Jason + * first pass at markers + * + * 11 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 10 1/21/99 3:35p Jason + * added liquid code + * + * 9 1/18/99 8:07p Chris + * Added the no-collide same flag (for flocks and nests) + * + * 8 1/18/99 2:46p Matt + * Combined flags & flags2 fields in object struct + * + * 7 1/13/99 3:25a Chris + * Added Obj_Burning and Obj_IsEffect to OSIRIS + * + * 6 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 5 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 4 1/05/99 5:09p Jason + * added permissable server networking (ala Quake/Unreal) to Descent3 + * + * 3 1/05/99 4:16p Matt + * Added SourceSafe header + * + */ + +#ifndef OBJECT_EXTERNAL_H_ +#define OBJECT_EXTERNAL_H_ + +//Use this handle when you want a handle that will never be a valid object +#define OBJECT_HANDLE_BAD 0 + +//Use this handle when you want a handle that will never be a valid object +#define OBJECT_HANDLE_NONE -1 + +//Object types +#define OBJ_NONE 255 //unused object +#define OBJ_WALL 0 //A wall... not really an object, but used for collisions +#define OBJ_FIREBALL 1 //a fireball, part of an explosion +#define OBJ_ROBOT 2 //an evil enemy +#define OBJ_SHARD 3 //a piece of glass +#define OBJ_PLAYER 4 //the player on the console +#define OBJ_WEAPON 5 //a laser, missile, etc +#define OBJ_VIEWER 6 //a viewed object in the editor +#define OBJ_POWERUP 7 //a powerup you can pick up +#define OBJ_DEBRIS 8 //a piece of robot +#define OBJ_CAMERA 9 //a camera object in the game +#define OBJ_SHOCKWAVE 10 //a shockwave +#define OBJ_CLUTTER 11 //misc objects +#define OBJ_GHOST 12 //what the player turns into when dead +#define OBJ_LIGHT 13 //a light source, & not much else +#define OBJ_COOP 14 //a cooperative player object. +#define OBJ_MARKER 15 //a map marker +#define OBJ_BUILDING 16 //a building +#define OBJ_DOOR 17 //a door +#define OBJ_ROOM 18 //a room, visible on the terrain +#define OBJ_PARTICLE 19 //a particle +#define OBJ_SPLINTER 20 //a splinter piece from an exploding object +#define OBJ_DUMMY 21 //a dummy object, ignored by everything +#define OBJ_OBSERVER 22 //an observer in a multiplayer game +#define OBJ_DEBUG_LINE 23 //something for debugging, I guess. I sure wish people would add comments. +#define OBJ_SOUNDSOURCE 24 //an object that makes a sound but does nothing else +#define OBJ_WAYPOINT 25 //a object that marks a waypoint +#define MAX_OBJECT_TYPES 26 //Update this when adding new types +//NOTE: if you add a type here, you must add the name to Object_type_names[] + +//Condition to check if the specified type in a generic type +#define IS_GENERIC(type) ((type == OBJ_CLUTTER) || (type == OBJ_BUILDING) || (type == OBJ_ROBOT) || (type == OBJ_POWERUP)) + +//Condition to check if the specified object is a robot (checks for buildings with AI) +#define IS_ROBOT(objp) ((objp->type == OBJ_ROBOT) || ((objp->type == OBJ_BUILDING) && objp->ai_info)) + +// Effect type flags +#define EF_CLOAKED 1 // This object is cloaked +#define EF_DEFORM 2 // This object is deforming +#define EF_COLORED 4 // This object is slowing fading from a color +#define EF_NAPALMED 8 // This object is on fire +#define EF_VOLUME_LIT 16 // This object has volumetric lighting on it +#define EF_VOLUME_CHANGING 32 // This object is morphing from one volume color to another +#define EF_FREEZE 64 // This object is being slowed down +#define EF_LINE_ATTACH 128 // This object has a grapple between it and another object +#define EF_SPECULAR 256 // This object has specular lighting +#define EF_FADING_IN 512 // Object is fading in +#define EF_FADING_OUT 1024 // Object is fading out +#define EF_LIQUID 2048 // Object has the FOV liquid effect +#define EF_CLOAK_WITH_MSG 4096 // This object (which is OBJ_PLAYER) needs to print a HUD message when cloak wears off +#define EF_SPARKING 8192 // This object is sparking +#define EF_BUMPMAPPED 16384 // This object is sparking +#define EF_NEGATIVE_LIGHT 32768 // This object subtracts light that it gives off +#define EF_VIRUS_INFECTED 65536 // This object shows signs of being infected + +//Control types - what tells this object what do do +#define CT_NONE 0 //doesn't move (or change movement) +#define CT_AI 1 //driven by AI +#define CT_EXPLOSION 2 //explosion sequencer +#define CT_FLYING 4 //the player is flying +#define CT_SLEW 5 //slewing +#define CT_FLYTHROUGH 6 //the flythrough system +#define CT_WEAPON 9 //laser, etc. +#define CT_DEBRIS 12 //this is a piece of debris +#define CT_POWERUP 13 //animating powerup blob +#define CT_SOAR 14 //Soar object +#define CT_PARTICLE 15 // Particle +#define CT_SPLINTER 16 // Splinter +#define CT_SOUNDSOURCE 17 // SoundSource +#define CT_DYING 18 // slowly dying +#define CT_DYING_AND_AI 19 // dying with AI + +//Movement types +#define MT_NONE 0 // Doesn't move +#define MT_PHYSICS 1 // Moves by physics +#define MT_WALKING 2 // Uses physics data structure, but uses a different physics code pipe +#define MT_AT_REST 3 +#define MT_SHOCKWAVE 4 // Moves like a shockwave + // (actually this is for space conservation - + // it could be more logically used as a + // control type) +#define MT_OBJ_LINKED 5 // Allows sticky objects to link to polymodel objects + +// Movement classes +#define MC_STANDING 0 +#define MC_FLYING 1 +#define MC_ROLLING 2 +#define MC_WALKING 3 +#define MC_JUMPING 4 + +// Attach types +#define AT_RAD 0 +#define AT_ALIGNED 1 +#define AT_UNALIGNED 2 + +//Render types +#define RT_NONE 0 //does not render +#define RT_POLYOBJ 1 //a polygon model +#define RT_FIREBALL 2 //a fireball +#define RT_WEAPON 3 //a non-polygonal weapon +#define RT_LINE 4 //a line +#define RT_PARTICLE 5 //render as particle type +#define RT_SPLINTER 6 //render as a splinter +#define RT_ROOM 7 //rendered as a room, not an object +#define RT_EDITOR_SPHERE 8 //renderd as a sphere in the editor, else not rendered +#define RT_SHARD 9 //bits of broken glass + +//misc object flags +#define OF_FORCE_CEILING_CHECK 0x00000001 //this object is exploding +#define OF_DEAD 0x00000002 //this object is dead, so next time we can, we should delete this object. +#define OF_DESTROYED 0x00000004 //this has been killed, and is showing the dead version +#define OF_STOPPED_THIS_FRAME 0x00000008 +#define OF_ATTACHED 0x00000010 //this object is a fireball attached to another object +#define OF_MOVED_THIS_FRAME 0x00000020 +#define OF_AI_DO_DEATH 0x00000040 //this is so objects can be vulerable, but the death is scripted out +#define OF_USES_LIFELEFT 0x00000080 //this object's lifeleft is valid +#define OF_SAFE_TO_RENDER 0x00000100 //this object can be seen this frame +#define OF_OUTSIDE_MINE 0x00000200 //this object has slewed outside of the mine (EDITOR ONLY) +#define OF_DESTROYABLE 0x00000400 //this object can be destroyed +#define OF_BIG_OBJECT 0x00000800 //this object is classified as being BIG +#define OF_POLYGON_OBJECT 0x00001000 //This object is a bonafide polygon object +#define OF_DYING 0x00002000 //This object is going through its death throes +#define OF_USE_DESTROYED_POLYMODEL 0x00004000 //This object should be drawn with its destroyed model (usual a lightmapped building) +#define OF_RENDERED 0x00008000 +#define OF_NO_OBJECT_COLLISIONS 0x00010000 +#define OF_STUCK_ON_PORTAL 0x00020000 +#define OF_TEMP_GRAVITY 0x00040000 +#define OF_CLIENT_KNOWS 0x00080000 //Client knows about this object +#define OF_SERVER_SAYS_DELETE 0x00100000 //Server tells me to delete this object +#define OF_SERVER_OBJECT 0x00200000 //Server told me to create this object +#define OF_PING_ACCELERATE 0x00400000 //Server is telling me to accelerate this object +#define OF_AI_DEATH 0x00800000 +#define OF_SEND_MULTI_REMOVE_ON_DEATH 0x01000000 //when the object is being deleted on the server, send a MultiSendRemoveObject call +#define OF_SEND_MULTI_REMOVE_ON_DEATHWS 0x02000000 //same as above, but a sound should be played on the clients along with the remove +#define OF_PREDICTED 0x04000000 // This object moves with client-side prediction +#define OF_INPLAYERINVENTORY 0x08000000 // This object is in a player's inventory +#define OF_INFORM_PLAYER_COLLIDE_TO_LG 0x10000000 +#define OF_INFORM_PLAYER_WEAPON_COLLIDE_TO_LG 0x20000000 +#define OF_INFORM_DESTROY_TO_LG 0x40000000 +#define OF_CLIENTDEMOOBJECT 0x80000000 // This object was sent to the client via + // MultiSendObject() and so it should + // recorded when writing a demo when it is + // created and destroyed + +///Which object flags get saved with the level +#define OBJECT_SAVE_LOAD_FLAGS (OF_OUTSIDE_MINE) + +//physics flags +#define PF_TURNROLL 0x01 // Roll when turning +#define PF_LEVELING 0x02 // Level object with closest side +#define PF_BOUNCE 0x04 // Bounce (not slide) when hit will +#define PF_WIGGLE 0x08 // Wiggle while flying +#define PF_STICK 0x10 // Object sticks (stops moving) when hits wall +#define PF_PERSISTENT 0x20 // Object keeps going even after it hits another object (eg, fusion cannon) +#define PF_USES_THRUST 0x40 // This object uses its thrust +#define PF_GRAVITY 0x80 // Effected by gravity +#define PF_IGNORE_OWN_CONC_FORCES 0x100 // Effected by magnetism +#define PF_WIND 0x200 // Effected by wind +#define PF_USES_PARENT_VELOCITY 0x400 +#define PF_FIXED_VELOCITY 0x800 +#define PF_FIXED_ROT_VELOCITY 0x1000 +#define PF_NO_COLLIDE_PARENT 0x2000 // this object cannot collide with its parent +#define PF_HITS_SIBLINGS 0x4000 // this object can collide with its siblings (like a bomb) // chrishack -- add flag to weapon page +#define PF_REVERSE_GRAVITY 0x8000 // this object flys upward with gravity +#define PF_GRAVITY_MASK (PF_REVERSE_GRAVITY|PF_GRAVITY) +#define PF_NO_COLLIDE 0x10000 // No collisions AND NO RELINKS -- DANGEROUS TO USE if not used correctly +#define PF_NO_ROBOT_COLLISIONS 0x20000 // No collisions occur with robots +#define PF_POINT_COLLIDE_WALLS 0x40000 // When colliding with walls, make our radius zero +#define PF_HOMING 0x80000 // This object (weapon) homes +#define PF_GUIDED 0x100000 // This object is guided +#define PF_IGNORE_CONCUSSIVE_FORCES 0x200000 +#define PF_DESTINATION_POS 0x400000 +#define PF_LOCK_X 0x800000 +#define PF_LOCK_Y 0x1000000 +#define PF_LOCK_Z 0x2000000 +#define PF_LOCK_P 0x4000000 +#define PF_LOCK_H 0x8000000 +#define PF_LOCK_B 0x10000000 +#define PF_LOCK_MASK (PF_LOCK_X | PF_LOCK_Y | PF_LOCK_Z | PF_LOCK_P | PF_LOCK_H | PF_LOCK_B) +#define PF_NEVER_USE_BIG_SPHERE 0x20000000 +#define PF_NO_SAME_COLLISIONS 0x40000000 +#define PF_NO_DOOR_COLLISIONS 0x80000000 + +// Generic Sound indices +#define GSI_AMBIENT 0 +#define GSI_EXPLODE 1 + +// Static Robot ids +#define ROBOT_GUIDEBOT 0 //NOTE: this must match GENOBJ_GUIDEBOT +#ifdef _WIN32 +#define ROBOT_GUIDEBOTRED 2 //NOTE: this must match GENOBJ_GUIDEBOTRED +#else +#define ROBOT_GUIDEBOTRED 0 //NOTE: this must match GENOBJ_GUIDEBOTRED +#endif + +#endif \ No newline at end of file diff --git a/Descent3/object_external_struct.h b/Descent3/object_external_struct.h new file mode 100644 index 000000000..959a22a67 --- /dev/null +++ b/Descent3/object_external_struct.h @@ -0,0 +1,524 @@ +/* + * $Logfile: /DescentIII/main/object_external_struct.h $ + * $Revision: 19 $ + * $Date: 4/28/99 11:28a $ + * $Author: Jason $ + * + * Contains the structures needed for object definition (can be exported if needed to DLLs) + * + * $Log: /DescentIII/main/object_external_struct.h $ + * + * 19 4/28/99 11:28a Jason + * made real viseffects not cast light + * + * 18 4/22/99 4:13p Matt + * Deleted sounds array from the object struct, since it was never used. + * + * 17 4/21/99 12:41p Jason + * make explosion system framerate independant + * + * 16 4/18/99 8:13p Chris + * Fixed the floating flare problems (where windows where broken out and + * the flare remained) + * + * 15 4/06/99 6:02p Matt + * Added score system + * + * 14 4/05/99 4:39p Jason + * added groovy new smoke trails + * + * 13 4/05/99 10:31a Matt + * Changed soundsource volume from int to float. Duh. + * + * 12 4/03/99 4:24p Jason + * sped up attached vis effects by a large amount + * + * 11 3/28/99 5:56p Matt + * Added sparking effect for objects + * + * 10 2/25/99 11:01a Matt + * Added new explosion system. + * + * 9 2/25/99 10:30a Jason + * added nonvis generic/robot system + * + * 8 2/22/99 2:04p Jason + * added different damages for players and generics + * + * 7 2/21/99 4:20p Matt + * Added SoundSource objects (and reformatted parts of the object header + * files). + * + * 6 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 5 2/03/99 12:43a Chris + * Added Obj_GetGroundPos + * + * 4 2/01/99 4:17p Jason + * more changes for multisafe + * + * 3 1/22/99 8:53p Jeff + * added custom-default script overrides + * + * 2 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h +* +* $NoKeywords: $ +*/ + +#include "pstypes.h" +#include "vecmat_external.h" +#include "robotfirestruct.h" + +#include "polymodel_external.h" + +#include "aistruct.h" //Not to be included for multiplayer game sdk + +#ifndef OBJECT_EXTERNAL_STRUCT_H +#define OBJECT_EXTERNAL_STRUCT_H + +//IMPORTANT: make sure MAX_OBJECTS meshes with handle bit allocation +#define MAX_OBJECTS 1500 //total number of objects in world + +// Splinter stuff +#define MAX_VERTS_PER_SPLINTER 4 + +/* + * MACROS + */ + +//Flags and masks for cell numbers +#define ROOMNUM_CELLNUM_FLAG 0x80000000 +#define ROOMNUM_CELLNUM_MASK 0x7fffffff + +//Get a cell number from a room number +#define CELLNUM(roomnum) ((roomnum) & ROOMNUM_CELLNUM_MASK) + +//Make a room number from a cell number +#define MAKE_ROOMNUM(cellnum) ((cellnum) | ROOMNUM_CELLNUM_FLAG) + +//Determine if a roomnum is really a cell number +#define ROOMNUM_OUTSIDE(roomnum) (((roomnum) & ROOMNUM_CELLNUM_FLAG) != 0) + +//Determine if an object is outside +#define OBJECT_OUTSIDE(objp) ROOMNUM_OUTSIDE((objp)->roomnum) + + +/* + * STRUCTURES + */ + +//lighting info +typedef struct +{ + int flags; // see above + float light_distance; + float red_light1,green_light1,blue_light1; + float red_light2,green_light2,blue_light2; + float time_interval; + float flicker_distance; + float directional_dot; + int timebits; + ubyte angle; + ubyte lighting_render_type; +} light_info; + +typedef struct +{ + int type_flags; // see EF_FLAGS above + + float alpha; // alpha value + float deform_range; // how many units to deform when drawing + float cloak_time; // how much time left cloaked + float deform_time; // how much time left deforming + float color_time; // how much time left colored + float r,g,b; + + // For fading in/out + float fade_time; + float fade_max_time; + + float damage_time; + float damage_per_second; // how much damage this object takes per second + float last_damage_time; // last time this object took damage + int damage_handle; // the object handle of the owner of the damage + + float volume_change_time; + vector volume_old_pos; + int volume_old_room; + + // For powerups only + float last_object_hit_time; + int last_object_hit; + + // For specular lighting + vector spec_pos; + float spec_mag; + float spec_r,spec_g,spec_b; + + // For dynamic volume lighting + ubyte dynamic_this_frame; + float dynamic_red,dynamic_green,dynamic_blue; + + // For liquid object + float liquid_time_left; + ubyte liquid_mag; + + // For freeze objects + float freeze_scalar; + + // For attach objects + int attach_line_handle; // handle to the object that this object is tethered to + + //Sound for special effects + int sound_handle; + + //For spark effect + float spark_delay; //delay between sparks + float spark_timer; //how long until next spark + float spark_time_left; //how long until sparking stops + +} effect_info_s; + +// Describes the next animation state for a robot +typedef struct custom_anim +{ + float server_time; + ushort server_anim_frame; + + unsigned short anim_start_frame; + unsigned short anim_end_frame; + float anim_time; + float max_speed; + + short anim_sound_index; + ubyte flags; + char next_anim_type; +} custom_anim; + +typedef struct multi_turret +{ + float time; + float last_time; + ubyte num_turrets; + float *last_keyframes; + float *keyframes; + ubyte flags; +} multi_turret; + +//Information specific to objects that render as a polygon model +typedef struct polyobj_info +{ + short model_num; // Which polygon model this object is + short dying_model_num; // The dying model for this object + + float anim_start_frame; + float anim_frame; // The model's current animation frame + float anim_end_frame; + float anim_time; + uint anim_flags; // Looping/notify at finish/pending + float max_speed; + + union + { + custom_anim multi_anim_info; // Multiplayer client info + custom_anim custom_anim_info; // Single-player/Server custom anim AIA_CUSTOM info + }; + multi_turret multi_turret_info; + + uint subobj_flags; // Mask of which subobjects to draw + int tmap_override; // If this is not -1, map all faces to this +} polyobj_info; + +//A shard of, presumably, glass +typedef struct shard_info_s { + vector points[3]; + float u[3],v[3]; + vector normal; + short tmap; +} shard_info_s; + +typedef struct line_info_s { + vector end_pos; // start pos is the object's .pos field +} line_info_s; + +typedef struct blast_info_s +{ + float max_size; + int bm_handle; +} blast_info_s; + +typedef struct dying_info_s { + int death_flags; // Info about the death + float delay_time; // How long until object dies + int killer_playernum; // The player who wille this object, or -1 if not a player + float last_spark_time; + float last_fireball_time; + float last_smoke_time; +} dying_info_s; + +typedef struct debris_info_s { + int death_flags; // a copy of the parent's death flags + float last_smoke_time; +} debris_info_s; + +typedef struct laser_info_s +{ + short parent_type; // The type of the parent of this object + short src_gun_num; // The src gunpoint that this object fired from + + int last_hit_handle; // For persistent weapons (survive object collision), object it most recently hit. + int track_handle; // Object this object is tracking. + float last_track_time; // Last track time (see if an object is visible) + + int hit_status; // Zero not used + vector hit_pnt; + vector hit_wall_pnt; + vector hit_wall_normal; + int hit_room; + int hit_pnt_room; + short hit_face; + + float multiplier; // Power if this is a fusion bolt (or other super weapon to be added). + float thrust_left; // How many seconds of thrust are left before the weapon stops thrusting + + float last_drop_time; // Last time a particle was dropped from this weapon + vector last_smoke_pos; // Last place smoke was dropped from this weapon + bool casts_light; // Whether or not this weapon casts light +} laser_info_s; + +typedef struct powerup_info_s +{ + int count; //how many/much we pick up (vulcan cannon only?) +} powerup_info_s; + +typedef struct splinter_info_s +{ + ubyte subobj_num; + short facenum; + vector verts[MAX_VERTS_PER_SPLINTER]; + vector center; +} splinter_info_s; + +//Data for sourcesource objects +typedef struct { + int sound_index; + float volume; +} soundsource_info_s; + +//information for physics sim for an object +// Some of this stuff is not object instance dependant -- so, it could be moved into +// a different struct to save space. (But, then we cannot change it -- i.e we might want a powerup +// to change the mass of an object...) Wait to move until we are optimizing -- see Chris if you move any fields +// out of the physics_info struct. Thanx! +typedef struct physics_info +{ + vector velocity; // Velocity vector of this object + vector thrust; // Constant force applied to this object + union + { + vector rotvel; // Rotational velecity (angles) + float turn_rate; + }; + union + { + vector rotthrust; // Rotational acceleration + }; + angle turnroll; // Rotation caused by turn banking + float last_still_time; // The current delta position a wiggle has caused. + int num_bounces; // Number of bounces before exploding (PHYSICS_UNLIMITED_BOUNCE is for unlimited bouncing) + + float coeff_restitution;// What percent of velocity is kept after a bounce + float mass; // The mass of this object -- what about moving into type info + float drag; // How fast this slows down -- what about moving into type info + float rotdrag; // How much resistance to a change in spin rate -- what about moving into type info + union + { + float full_thrust; // Maximum thrust magnitude -- what about moving into type info + float max_velocity; + }; + union + { + float full_rotthrust; // Maximum rotation thrust magnitude -- what about moving into type info + float max_turn_rate; + }; + float max_turnroll_rate;// How fast is the maximum turnroll rate -- what about moving into type info + float turnroll_ratio; // How much roll for a given turning rate -- what about moving into type info + float wiggle_amplitude; // The amplitude of an object's wiggle -- what about moving into type info + float wiggles_per_sec; // How fast something wiggles -- what about moving into type info + + vector dest_pos; // destination position for interpolating velocity (for multiplayer only) + + union + { + float hit_die_dot; + int stuck_room; + }; + + union + { + float max_speed_time; + int stuck_portal; + }; + + uint flags; // Misc physics flags +} physics_info; + +typedef struct shockwave_info +{ + uint damaged_list[(MAX_OBJECTS/32) + 1]; +} shockwave_info; + +typedef struct object_link_info +{ + int parent_handle; + int sobj_index; + vector fvec; + vector uvec; + vector pos; +} object_link_info; + +typedef struct{ + ushort DLLID; + ushort script_id; + void *script_instance; +}tOSIRISScriptNode; + +typedef struct{ + tOSIRISScriptNode custom_script; + tOSIRISScriptNode mission_script; + tOSIRISScriptNode level_script; + tOSIRISScriptNode default_script; +}tOSIRISScript; + +//The data for an object +typedef struct object { + ubyte type; // what type of object this is... robot, weapon, hostage, powerup, fireball + ubyte dummy_type; // stored type of an OBJ_DUMMY + ushort id; // which form of object...which powerup, robot, etc. + ulong flags; + + char *name; // the name of this object, or NULL + + int handle; // unique handle for this object. See defines above + short next,prev; // id of next and previous connected object in Objects, -1 = no connection + + ubyte control_type; // how this object is controlled + ubyte movement_type; // how this object moves + ubyte render_type; // how this object renders + ubyte lighting_render_type; // how this object is lit. See flags above + + int roomnum; // room number or terrain cell containing object + + vector pos; // absolute x,y,z coordinate of center of object + matrix orient; // orientation of object in world + vector last_pos; // where object was last frame + + ushort renderframe; // framenum this object was last rendered + + vector wall_sphere_offset; + vector anim_sphere_offset; + + float size; // 3d size of object - for collision detection + float shields; // Starts at maximum, when <0, object dies.. + + sbyte contains_type; // Type of object this object contains (eg, spider contains powerup) + sbyte contains_id; // ID of object this object contains (eg, id = blue type = key) + sbyte contains_count;// number of objects of type:id this object contains + sbyte pad3; // keep alignment + + float creation_time; // absolute time when this object was created + float lifeleft; // how long until goes away, if OF_USES_LIFELEFT flag is set + float lifetime; // How long this object stays alive (in seconds) + + int parent_handle; // The handle of this object's parent + + int attach_ultimate_handle; + int attach_parent_handle; + int *attach_children; // List of object handles for connected children + + ubyte weapon_fire_flags; // Used to indicate special weapon effects. See flags above. + + char attach_type; + short lowest_attached_vis; + union + { + float attach_dist; + short attach_index; + }; + + //Movement info, determined by MOVEMENT_TYPE + union + { + physics_info phys_info; //a physics object + shockwave_info shock_info; + object_link_info obj_link_info; + } mtype; + + //Collition detection stuff + vector min_xyz,max_xyz; // the current min & max extents of this object's sphere + + //Current weapon battery info for this object + dynamic_wb_info *dynamic_wb; + + //Explosion information + float impact_size; + float impact_time; + float impact_player_damage; + float impact_generic_damage; + float impact_force; + + // Object change information + int change_flags; + + // object generic vis flags + int generic_nonvis_flags; + int generic_sent_nonvis; + + lightmap_object lm_object; // The lightmap object for this object + + //Control info, determined by CONTROL_TYPE + union { + laser_info_s laser_info; + powerup_info_s powerup_info; + splinter_info_s splinter_info; + blast_info_s blast_info; + dying_info_s dying_info; + debris_info_s debris_info; + soundsource_info_s soundsource_info; + } ctype; + + ai_frame *ai_info; // AI information pointer + + //Render info, determined by RENDER_TYPE + union { + polyobj_info pobj_info; //polygon model + shard_info_s shard_info; //shard + #ifdef _DEBUG + line_info_s line_info; //line info + #endif + ddgr_color sphere_color; //for RT_EDITOR_SPHERE + } rtype; + + effect_info_s *effect_info; + light_info *lighting_info; //Pointer to lighting info, or NULL if inherits from type + + //Something to do with multiplayer, possibly, but it's hard to know for sure + //because some people are incapable of commented their code. + ushort position_counter; + + //OSIRIS Script Info (new OSIRIS) + tOSIRISScript *osiris_script; + + char *custom_default_script_name; + char *custom_default_module_name; +} object; + +#endif diff --git a/Descent3/object_lighting.cpp b/Descent3/object_lighting.cpp new file mode 100644 index 000000000..0422b496f --- /dev/null +++ b/Descent3/object_lighting.cpp @@ -0,0 +1,700 @@ +/* + * $Logfile: /DescentIII/main/object_lighting.cpp $ + * $Revision: 64 $ + * $Date: 3/20/00 12:58p $ + * $Author: Matt $ + * + * Code to deal with object lighting + * + * $Log: /DescentIII/main/object_lighting.cpp $ + * + * 64 3/20/00 12:58p Matt + * Merge of Duane's post-1.3 changes. + * Lots of lightmap stuff. Jason looked at this and said nothing jumps + * out at him as being wrong. + * + * 63 10/26/99 6:33p Chris + * Added the green flare + * + * 62 10/12/99 11:06a Jeff + * negative lighting effect + * + * 61 7/20/99 1:00p Jason + * added auto katmai support + * + * 60 4/28/99 11:28a Jason + * made real viseffects not cast light + * + * 59 4/26/99 4:28a Jeff + * reset cloak time to 0 on MakeObjectVisible() to prevent some weird + * anomalies (like the thief stealing cloak) + * + * 58 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 57 4/12/99 6:15p Samir + * Sound priorities pass 1 + * + * 56 4/03/99 2:20p Jason + * added better heat damage effect + * + * 55 3/26/99 3:26p Jeff + * option to display hud message when cloaking + * + * 54 3/22/99 11:16a Jason + * Katmai enhancements + * + * 53 3/05/99 12:03p Jason + * fixed lighting with popup views + * + * 52 2/25/99 8:55p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 51 2/15/99 4:10p Jason + * made non-vis objects kill all their attach sounds/objects + * + * 50 1/20/99 5:29p Jason + * took out thruster glow for players + * + * 49 1/19/99 4:22p Matt + * Added the ability for objects to have their own lighting info, + * different from the default lighting for that type of object. + * + */ + +#include "object_lighting.h" +#include "object.h" +#include "lighting.h" +#include "objinfo.h" +#include "weapon.h" +#include "descent.h" +#include "game.h" +#include "polymodel.h" +#include "renderobject.h" +#include +#include "lightmap_info.h" +#include "fireball.h" +#include "player.h" +#include "mem.h" +#include "config.h" +#include "findintersection.h" +#include "psrand.h" + +// How far the headlight casts light +#define HEADLIGHT_DISTANCE 150.0f +// Defines the headlight cone of light +#define HEADLIGHT_DOT .75f + +// How big the fast headlight is +#define FAST_HEADLIGHT_SIZE 20.0f + +// Defines the headlight cone of light +#define FAST_HEADLIGHT_DOT .80f + +// returns true if this light point is visible +bool IsLightPointVisible (vector *pos,float size,object *obj) +{ + if (obj!=NULL) + { + if ((obj->flags & OF_RENDERED) || IsPointVisible (pos,size,NULL)) + return true; + } + else + { + if (IsPointVisible (pos,size,NULL)) + return true; + } + + return false; +} + +// Fills in the color of the passed in players thrusters. For lighting +void FindPlayerThrusterColor (int slot,float *r,float *g,float *b) +{ + ASSERT (slot>=0 && slotrtype.pobj_info.model_num; + + *r=0; + *g=0; + *b=0; + + if (obj->render_type!=RT_POLYOBJ) + return; + + poly_model *pm=&Poly_models[model_num]; + + for (int i=0;in_models;i++) + { + if (pm->submodel[i].flags & SOF_GLOW) + { + *r=pm->submodel[i].glow_info->glow_r; + *g=pm->submodel[i].glow_info->glow_g; + *b=pm->submodel[i].glow_info->glow_b; + + return; + } + } + + Int3(); // No thruster glow? +} + +ubyte EasterEgg=0; +#define EASTER_EGG_TIMER (3600*4) + +void ReflectRay (vector *,vector *,vector *); +// Casts light from an object onto the rooms or terrain +void DoObjectLight(object *obj) +{ + light_info *li; + static int first=1,flare_id=-1,greenflare_id=-1; + + if (obj->flags & OF_DEAD) + return; + + if (obj->render_type==RT_NONE) + return; + + float negative_light = 1.0f; + if (obj->effect_info && obj->effect_info->type_flags & EF_NEGATIVE_LIGHT) + negative_light = -1.0f; + + // Temporary hack to make explosions light up + if (obj->type==OBJ_FIREBALL) + { + if (!Detail_settings.Dynamic_lighting) + return; + + if (obj->flags & OF_USES_LIFELEFT && Fireballs[obj->id].type==FT_EXPLOSION) + { + float scalar; + + scalar=(obj->lifetime-obj->lifeleft)/obj->lifetime; + + if (scalar>.5) + { + scalar-=.5; + + scalar=1.0-(scalar/.5); + } + else + { + scalar*=2; + } + + if (scalar>.05 ) + { + if (OBJECT_OUTSIDE(obj)) + ApplyLightingToTerrain (&obj->pos,CELLNUM(obj->roomnum),40*scalar,negative_light*1.0f,negative_light*0.5f,negative_light*0.0f); + else + ApplyLightingToRooms (&obj->pos,obj->roomnum,40*scalar,negative_light*1.0f,negative_light*0.5f,negative_light*0.0f); + } + + } + return; + } + + + switch (obj->type) + { + case OBJ_PLAYER: + { + if ((Players[obj->id].flags) & PLAYER_FLAGS_HEADLIGHT) + { + + if (Detail_settings.Fast_headlight_on) + { + // Do fast headlight + fvi_info hit_info; + fvi_query fq; + + // shoot a ray from the light position to the current vertex + vector end_pos=obj->pos+(obj->orient.fvec*HEADLIGHT_DISTANCE*2); + fq.p0=&obj->pos; + fq.p1=&end_pos; + + fq.startroom=obj->roomnum; + + fq.rad=0.0f; + fq.flags=FQ_CHECK_OBJS; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + + fvi_FindIntersection(&fq,&hit_info); + + hit_info.hit_pnt-=obj->orient.fvec/4; + + float dist=vm_VectorDistanceQuick(&hit_info.hit_pnt,&obj->pos); + + // Now light up the hit area + if (ROOMNUM_OUTSIDE(hit_info.hit_room)) + ApplyLightingToTerrain (&hit_info.hit_pnt,CELLNUM(hit_info.hit_room),FAST_HEADLIGHT_SIZE,negative_light*1.0,negative_light*1.0,negative_light*1.0); + else + { + ApplyLightingToRooms (&hit_info.hit_pnt,hit_info.hit_room,FAST_HEADLIGHT_SIZE,negative_light*1.0,negative_light*1.0,negative_light*1.0); + // Do stupid easter egg trick + face *fp=&Rooms[hit_info.hit_room].faces[hit_info.hit_face[0]]; + if (Gametime>EASTER_EGG_TIMER && GameTextures[fp->tmap].flags & (TF_PROCEDURAL|TF_WATER_PROCEDURAL)) + { + EasterEgg=1; + } + } + } + else + { + // Do slow headlight + + if (OBJECT_OUTSIDE(obj)) + ApplyLightingToTerrain (&obj->pos,CELLNUM(obj->roomnum),HEADLIGHT_DISTANCE,negative_light*1.0,negative_light*1.0,negative_light*1.0,&obj->orient.fvec,HEADLIGHT_DOT); + else + ApplyLightingToRooms (&obj->pos,obj->roomnum,HEADLIGHT_DISTANCE,negative_light*1.0,negative_light*1.0,negative_light*1.0,&obj->orient.fvec,HEADLIGHT_DOT); + } + + } + + + int slot=obj->id; + + /*// Do thruster glow + if (Players[slot].thrust_mag && Detail_settings.Dynamic_lighting) + { + float lightsize=Players[slot].thrust_mag*30.0; + lightsize+=Players[slot].afterburner_mag*40.0; + + float r,g,b; + vector negz=-obj->orient.fvec; + vector newpos=obj->pos-(obj->orient.fvec*(obj->size*.7)); + + if (IsLightPointVisible (&newpos,lightsize,NULL)) + { + FindPlayerThrusterColor (slot,&r,&g,&b); + + ASSERT (r>=0 && r<=1.0); + ASSERT (g>=0 && g<=1.0); + ASSERT (b>=0 && b<=1.0); + + if (r!=0 || g!=0 || b!=0) + { + if (OBJECT_OUTSIDE(obj)) + ApplyLightingToTerrain (&newpos,CELLNUM(obj->roomnum),lightsize,r,g,b,&negz,0); + else + ApplyLightingToRooms (&newpos,obj->roomnum,lightsize,r,g,b,&negz,0); + } + } + }*/ + + // If the player is glowing + if (Players[slot].light_dist>.5 && Detail_settings.Dynamic_lighting) + { + if (IsLightPointVisible (&obj->pos,Players[slot].light_dist,obj)) + { + if (OBJECT_OUTSIDE(obj)) + ApplyLightingToTerrain (&obj->pos,CELLNUM(obj->roomnum),Players[slot].light_dist,Players[slot].r*negative_light,Players[slot].g*negative_light,Players[slot].b*negative_light); + else + ApplyLightingToRooms (&obj->pos,obj->roomnum,Players[slot].light_dist,Players[slot].r*negative_light,Players[slot].g*negative_light,Players[slot].b*negative_light); + } + } + + // Do stupid ball glow + if (Players[slot].num_balls>0 && Detail_settings.Dynamic_lighting) + { + vector ballpos; + for (int i=0;iroomnum),light_size,Players[slot].ball_r[i]*negative_light,Players[slot].ball_g[i]*negative_light,Players[slot].ball_b[i]*negative_light); + else + ApplyLightingToRooms (&ballpos,obj->roomnum,light_size,Players[slot].ball_r[i]*negative_light,Players[slot].ball_g[i]*negative_light,Players[slot].ball_b[i]*negative_light); + + } + } + + return; + } + + case OBJ_WEAPON: + { + weapon *w=&Weapons[obj->id]; + li=&w->lighting_info; + + + // Check to see if we should have dynamic light + if (first) + { + flare_id=FindWeaponName ("Yellow flare"); + greenflare_id=FindWeaponName ("GreenFlare"); + first=0; + } + + if (!Detail_settings.Dynamic_lighting) + { + if (obj->id!=flare_id && obj->id!=greenflare_id) + return; + } + if (obj->ctype.laser_info.casts_light==false) + return; + + break; + } + case OBJ_POWERUP: + case OBJ_ROBOT: + case OBJ_CLUTTER: + case OBJ_BUILDING: + { + li = ObjGetLightInfo(obj); + if (!Detail_settings.Dynamic_lighting && (obj->type==OBJ_ROBOT || obj->type==OBJ_POWERUP)) + return; + break; + } + default: + return; + } + + float scalar=1.0; + float red,green,blue; + float light_distance=li->light_distance; + vector *direction=NULL; + + // Make katmai lights bigger + if (Katmai) + light_distance*=1.5; + + if (light_distance>0) + { + if (!IsLightPointVisible (&obj->pos,light_distance,obj)) + return; + + if (li->flags & OLF_DIRECTIONAL) + direction=&obj->orient.fvec; + + + if (li->flags & OLF_FLICKER_SLIGHTLY) + { + int factor=li->flicker_distance; + + if (factor==0) + { + mprintf ((0,"You have a flicker_slightly light that has a flicker factor of zero!\n")); + return; + } + + light_distance+=((ps_rand()%factor)-(factor/2)); + if (light_distance<0) + return; + } + + red=li->red_light1; + green=li->green_light1; + blue=li->blue_light1; + + if ((li->flags & OLF_PULSE) && li->time_interval>0) + { + float ptime=li->time_interval; + + int int_time=(int)(Gametime/(ptime*2)); + float left_time=Gametime-(int_time*ptime*2); + float norm_time=left_time/(ptime); + + if (norm_time>1) + scalar=1-(norm_time-1.0); + else + scalar=norm_time; + + scalar=li->flicker_distance+(scalar*(1.0-li->flicker_distance)); + } + + if (li->flags & OLF_PULSE_TO_SECOND && li->time_interval>0) + { + float ptime=li->time_interval; + + int int_time=(int)(Gametime/(ptime*2)); + float left_time=Gametime-(int_time*ptime*2); + float norm_time=left_time/(ptime); + + if (norm_time>1) + { + norm_time-=1.0; + + red=((1.0-norm_time)*li->red_light2)+(li->red_light1*norm_time); + green=((1.0-norm_time)*li->green_light2)+(li->green_light1*norm_time); + blue=((1.0-norm_time)*li->blue_light2)+(li->blue_light1*norm_time); + } + else + { + red=((1.0-norm_time)*li->red_light1)+(li->red_light2*norm_time); + green=((1.0-norm_time)*li->green_light1)+(li->green_light2*norm_time); + blue=((1.0-norm_time)*li->blue_light1)+(li->blue_light2*norm_time); + } + + } + + if ( scalar < 0.0f) + scalar = 0.0f; + + + // If this is object is flickering, just compare it with a random value + if (li->flags & OLF_FLICKERING) + { + if (ps_rand()%2==1) + return; + } + + // If this object is based on timebits, then return if the corresponding bit + // is zero for this gametime + if (li->flags & OLF_TIMEBITS) + { + int int_time=(Gametime/li->time_interval); + float left_time=(Gametime-(int_time*li->time_interval)); + float slice_time=li->time_interval/8; + + int timebit_num=left_time/slice_time; + + if (!(li->timebits & (1<pos,CELLNUM(obj->roomnum),light_distance*scalar,red*negative_light,green*negative_light,blue*negative_light,direction,li->directional_dot); + else + ApplyLightingToRooms (&obj->pos,obj->roomnum,light_distance*scalar,red*negative_light,green*negative_light,blue*negative_light,direction,li->directional_dot); + + } + +} + +// Frees all the memory associated with this objects lightmap +void ClearObjectLightmaps (object *obj) +{ + int Mnum,Fnum; + + if (obj->lm_object.used==1) + { + obj->lm_object.used=0; + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + ASSERT(pm->n_models < MAX_SUBOBJECTS); + + mprintf((1, "CLEAR %d %s", obj->handle, obj->name)); + + int faceCount = 0; + for (Mnum=0; Mnumn_models; Mnum++) { + if (!IsNonRenderableSubmodel (pm,Mnum)) { + mprintf((1, " %X\n", obj->lm_object.lightmap_faces[Mnum][0].u2)); + mem_free (obj->lm_object.lightmap_faces[Mnum][0].u2); + break; + } + } + for (Mnum=0; Mnumn_models; Mnum++) + { + if (IsNonRenderableSubmodel (pm,Mnum)) + continue; + + for (Fnum=0; Fnumsubmodel[Mnum].num_faces; Fnum++) + { + lightmap_object_face *lof=&obj->lm_object.lightmap_faces[Mnum][Fnum]; + + if (lof->lmi_handle!=BAD_LMI_INDEX) { + FreeLightmapInfo (lof->lmi_handle); + lof->lmi_handle=BAD_LMI_INDEX; + } + lof->num_verts = 0; + } + + if(obj->lm_object.num_faces[Mnum]) { + mem_free (obj->lm_object.lightmap_faces[Mnum]); + obj->lm_object.lightmap_faces[Mnum]=NULL; + } + obj->lm_object.num_faces[Mnum]=0; + } + } +} + +// Frees all the memory associated with lightmap objects +void ClearAllObjectLightmaps (int terrain) +{ + int i; + + for (i=0;i<=Highest_object_index;i++) + { + object *obj=&Objects[i]; + + if ((OBJECT_OUTSIDE(obj) != 0) != (terrain != 0)) + continue; + + if (obj->lm_object.used==1) + { + ClearObjectLightmaps (obj); + + } + } +} + + +// Sets up the memory to be used by an object for lightmaps +void SetupObjectLightmapMemory (object *obj) +{ + int Mnum,Fnum; + lightmap_object_face *lof; + + ASSERT (obj->lm_object.used==0); + obj->lm_object.used=1; + + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + ASSERT (pm->new_style); + + obj->lm_object.num_models=pm->n_models; + + ASSERT(pm->n_models < MAX_SUBOBJECTS); + int uv2size = 0; + + mprintf((1, "SETUP %d %s", obj->handle, obj->name)); + for (Mnum=0; Mnumn_models; Mnum++) + { + if (IsNonRenderableSubmodel (pm,Mnum)) + { + obj->lm_object.num_faces[Mnum]=0; + obj->lm_object.lightmap_faces[Mnum]=NULL; + continue; + } + + obj->lm_object.num_faces[Mnum]=pm->submodel[Mnum].num_faces; + obj->lm_object.lightmap_faces[Mnum]=(lightmap_object_face *)mem_malloc (obj->lm_object.num_faces[Mnum]*sizeof(lightmap_object_face)); + ASSERT(obj->lm_object.lightmap_faces[Mnum]); + + for (Fnum=0; Fnumsubmodel[Mnum].num_faces; Fnum++) + { + lof=&obj->lm_object.lightmap_faces[Mnum][Fnum]; + + lof->num_verts=pm->submodel[Mnum].faces[Fnum].nverts; + lof->lmi_handle=BAD_LMI_INDEX; + + uv2size += lof->num_verts; + } + } + if(uv2size) { + float *uvblock = (float *)mem_malloc(2*uv2size*sizeof(float)); + mprintf((1, " %X %d\n", uvblock, uv2size)); + + for (Mnum=0; Mnumn_models; Mnum++) + { + if (!IsNonRenderableSubmodel (pm,Mnum)) { + for (Fnum=0; Fnumsubmodel[Mnum].num_faces; Fnum++) + { + obj->lm_object.lightmap_faces[Mnum][Fnum].u2=uvblock; + uvblock += pm->submodel[Mnum].faces[Fnum].nverts; + obj->lm_object.lightmap_faces[Mnum][Fnum].v2=uvblock; + uvblock += pm->submodel[Mnum].faces[Fnum].nverts; + } + } + } + } +} + + +//SHOULDN'T THE FOLLOWING TWO FUNCTIONS REALLY BE IN OBJECT.CPP? +#include "hlsoundlib.h" +#include "soundload.h" +#include "hud.h" +#include "stringtable.h" +// makes the an object cloaked +void MakeObjectInvisible(object *obj,float time,float fade_time,bool no_hud_message) +{ + if (obj->effect_info) + { + obj->effect_info->cloak_time=time; + obj->effect_info->type_flags|=EF_FADING_OUT; + obj->effect_info->fade_time=fade_time; + obj->effect_info->fade_max_time=fade_time; + } + + if(obj->type==OBJ_PLAYER && !no_hud_message) + { + static int cloak_sound_on_id = -2; + if(cloak_sound_on_id==-2) + cloak_sound_on_id = FindSoundName("Cloak on"); + + if(cloak_sound_on_id!=-1) + Sound_system.Play3dSound(cloak_sound_on_id,SND_PRIORITY_HIGHEST,obj,MAX_GAME_VOLUME/2); + + if(obj->id==Player_num){ + AddHUDMessage(TXT_CLOAKON); + } + + if(obj->effect_info) + obj->effect_info->type_flags |= EF_CLOAK_WITH_MSG; + } +} + +// makes an object visbile +void MakeObjectVisible(object *obj) +{ + if (obj->effect_info) + { + // Play sound maybe. + obj->effect_info->type_flags&=~EF_CLOAKED; + obj->effect_info->type_flags|=EF_FADING_IN; + obj->effect_info->fade_time=obj->effect_info->fade_max_time; + obj->effect_info->cloak_time = 0; + } + + if(obj->type==OBJ_PLAYER && obj->effect_info && obj->effect_info->type_flags&EF_CLOAK_WITH_MSG) + { + static int cloak_sound_off_id = -2; + if(cloak_sound_off_id==-2) + cloak_sound_off_id = FindSoundName("Cloak off"); + + if(cloak_sound_off_id!=-1) + Sound_system.Play3dSound(cloak_sound_off_id,SND_PRIORITY_HIGHEST,obj,MAX_GAME_VOLUME/2); + + if(obj->id==Player_num){ + AddHUDMessage(TXT_MSG_CLOAKOFF); + } + } +} + +//Sets an object to have local lighting info +void ObjSetLocalLighting(object *objp) +{ + //If this object doesn't have its own lighting info, create it + if (objp->lighting_info == NULL) { + + //make sure this is a generic object + ASSERT((objp->type == OBJ_POWERUP) || (objp->type == OBJ_ROBOT) || (objp->type == OBJ_CLUTTER) || (objp->type == OBJ_BUILDING)); + + //allocate a light info for this object + objp->lighting_info = (light_info *) mem_malloc(sizeof(*objp->lighting_info)); + + //copy the lighting info from the type for this object + *objp->lighting_info = Object_info[objp->id].lighting_info; + } +} + +//Returns a pointer to the lighting info for the specified object +light_info *ObjGetLightInfo(object *objp) +{ + if (objp->lighting_info) + return objp->lighting_info; + + if ((objp->type == OBJ_POWERUP) || (objp->type == OBJ_ROBOT) || (objp->type == OBJ_CLUTTER) || (objp->type == OBJ_BUILDING)) + return &Object_info[objp->id].lighting_info; + + if (objp->type == OBJ_WEAPON) + return &Weapons[objp->id].lighting_info; + + Int3(); + + return NULL; +} + diff --git a/Descent3/object_lighting.h b/Descent3/object_lighting.h new file mode 100644 index 000000000..b6a0e88b9 --- /dev/null +++ b/Descent3/object_lighting.h @@ -0,0 +1,49 @@ +/* + * $Logfile: /DescentIII/Main/object_lighting.h $ + * $Revision: 7 $ + * $Date: 3/26/99 3:25p $ + * $Author: Jeff $ + * + * Header for object_lighting.cpp + * + * $Log: /DescentIII/Main/object_lighting.h $ + * + * 7 3/26/99 3:25p Jeff + * option to display hud message when cloaking + * + * 6 1/19/99 4:22p Matt + * Added the ability for objects to have their own lighting info, + * different from the default lighting for that type of object. + * + */ + +#ifndef OBJECT_LIGHTING_H +#define OBJECT_LIGHTING_H + +#include "object.h" + +// Casts light from an object onto the rooms or terrain +void DoObjectLight(object *obj); + +// Frees all the memory associated with lightmap objects +void ClearAllObjectLightmaps (int terrain); + +// Frees all the memory associated with this objects lightmap +void ClearObjectLightmaps (object *obj); + +// Sets up the memory to be used by an object for lightmaps +void SetupObjectLightmapMemory (object *obj); + +// makes the an object cloaked +void MakeObjectInvisible(object *obj,float time,float fade_time=1.0,bool no_hud_message=false); + +// makes the player visbile +void MakeObjectVisible(object *obj); + +//Returns a pointer to the lighting info for the specified object +light_info *ObjGetLightInfo(object *objp); + +//Sets an object to have local lighting info +void ObjSetLocalLighting(object *objp); + +#endif \ No newline at end of file diff --git a/Descent3/objinfo.cpp b/Descent3/objinfo.cpp new file mode 100644 index 000000000..2dab96fbe --- /dev/null +++ b/Descent3/objinfo.cpp @@ -0,0 +1,403 @@ +/* + * $Logfile: /DescentIII/main/objinfo.cpp $ + * $Revision: 64 $ + * $Date: 4/19/00 5:07p $ + * $Author: Matt $ + * + * Code to handle objects in the big list of robots,powerups,etc. + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "pserror.h" +#include "object.h" +#include "objinfo.h" +#include "3d.h" +#include +#include +#include +#include +#include "polymodel.h" +#include "robotfire.h" +#include "AIMain.h" +#include "sounds.h" +#include "stringtable.h" +#include "mem.h" + +//The array with information for robots, powerups, buildings, etc. +object_info Object_info[MAX_OBJECT_IDS]; + +//The number of ids of each type in the list +int Num_object_ids[MAX_OBJECT_TYPES]; + +//Objects that must be remapped so we can reference by index +//If you change this array then you must change the defines in the header file +//#ifdef _WIN32 +//char *Static_object_names[]={TBL_GENERIC("GuideBot"),TBL_GENERIC("ChaffChunk"),TBL_GENERIC("GuideBotRed")}; +//#else +char *Static_object_names[]={TBL_GENERIC("GuideBot"),TBL_GENERIC("ChaffChunk")}; +//#endif + +#define NUM_STATIC_OBJECTS (sizeof(Static_object_names) / sizeof(*Static_object_names)) + +#ifdef EDITOR +char *Movement_class_names[]={"Standing","Flying","Rolling","Walking","Jumping"}; +char *Anim_state_names[]={"Alert","Death","Fire Missile 1","Missile Recoil 1", + "Fire Missile 2","Missile Recoil 2","Melee 1","Melee Recoil 1", + "Melee 2","Melee Recoil 2","Idle","Quirk","Flinch","Taunt", + "To Standing Idle","To Flying Idle","To Rolling Idle",";To Walking Idle","To Jumping Idle", + "Goto standing","Goto flying","Goto rolling","Goto walking", + "Goto jumping"}; + +//extern char *Movement_class_names[]; +//extern char *Anim_state_names[]; +#endif + +// Sets all objects to unused +void InitObjectInfo () +{ + int i; + + for (i=0;i=0 && n=0 && n=0;i--) + { + if (Object_info[i].type == Object_info[n].type) + return i; + } + + for (i=MAX_OBJECT_IDS-1;i>=n;i--) + { + if (Object_info[i].type == Object_info[n].type) + return i; + } + + Int3(); //shouldn't get here + return n; + +} +#endif + +// Searches thru all object ids for a specific name +// Returns the found id, or -1 if not found +int FindObjectIDName(char *name) +{ + int i; + + ASSERT (name!=NULL); + + for (i=0;iid].type != OBJ_NONE); + + if (obj->render_type == RT_POLYOBJ) + DrawPolygonModel(&obj->pos,&obj->orient,Object_info[obj->id].render_handle,NULL,0,1.0,1.0,1.0, obj->rtype.pobj_info.subobj_flags); + else + Int3(); + +} + +//Go through the object list and update an ID which has changed +void RemapObjects(int old_id,int new_id) +{ + int i; + + for (i=0;i",Static_object_names[i]); + continue; + } + + if (cur_index == i) + continue; // hurray, we're already matched up! + + //If our slot is already used, move the ID somewhere else + if (Object_info[i].type != OBJ_NONE) { +// int new_index = AllocObjectID(Object_info[i].type, Object_info[i].flags); + if(Object_info[i].anim) f_anim = true; + if(Object_info[i].static_wb) f_weapons = true; + if(Object_info[i].ai_info) f_ai = true; + int new_index = AllocObjectID(Object_info[i].type, f_anim, f_weapons, f_ai); + if(new_index >= 0) { //DAJ -1FIX + Object_info[new_index] = Object_info[i]; + RemapObjects(i,new_index); + } + } + + //Copy static ID into new slot + Object_info[i] = Object_info[cur_index]; + + //Free up the old slot + Object_info[cur_index].type = OBJ_NONE; + + //Remap the objects that used this ID + RemapObjects(cur_index,i); + } +} + +// Changes all existing objects model_nums to a new model num +void ChangeOldModelsForObjects (int old_handle,int new_handle) +{ + int i; + + for (i=0;i<=Highest_object_index;i++) + { + if (Objects[i].render_type==RT_POLYOBJ && Objects[i].rtype.pobj_info.model_num==old_handle) + { + Objects[i].rtype.pobj_info.model_num=new_handle; + } + } +} + +void RemapObjects () +{ + RemapStaticIDs(); +}; + diff --git a/Descent3/objinfo.h b/Descent3/objinfo.h new file mode 100644 index 000000000..260cdfe0d --- /dev/null +++ b/Descent3/objinfo.h @@ -0,0 +1,552 @@ +/* + * $Logfile: /DescentIII/Main/objinfo.h $ + * $Revision: 83 $ + * $Date: 9/13/01 2:31p $ + * $Author: Matt $ + * + * Structure information about different kinds of objects + * + * $Log: /DescentIII/Main/objinfo.h $ + * + * 83 9/13/01 2:31p Matt + * Increased MAX_OBJECT_IDS from 810 to 910, and made the value the same + * for the PC and Mac. + * + * 82 3/20/00 12:18p Matt + * Merge of Duane's post-1.3 changes. + * Lower MAX_OBJECT_IDS for Mac (Mac only) + * + * 81 10/28/99 12:44p 3dsmax + * Increased the number of Object IDs + * + * 80 10/26/99 10:32a Jeff + * no red guidebot in non-Windows versions + * + * 79 10/22/99 11:30a Matt + * Mac merge + * + * 78 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 77 10/08/99 4:29p Chris + * + * 76 10/08/99 4:24p 3dsmax + * upped number of objids (don't know who did this) + * + * 75 5/17/99 10:15p Matt + * Added system for having ambient objects that get deleted at low detail + * levels. + * + * 74 5/09/99 1:13p Matt + * Made ChaffChunk a static object. + * + * 73 5/09/99 2:01a Matt + * Increased max object IDs from 600 to 650. + * + * 72 5/06/99 1:04a Gwar + * adding object support to new editor -- place objects with right-click + * in orthogonal views (still needs many bug fixes :) + * + * 71 5/01/99 3:49a Chris + * Added the "No scale movement properties by diff level" checkbox + * + * 70 4/30/99 11:54p Jeff + * put max_object_ids back up to 600 for now + * + * 69 4/30/99 12:56p Kevin + * Lowered values for MAX_SOUNDS, MAX_ROOMS, MAX_TEXTURES and MAX_OBJIDS. + * Talk to me before changing any of these again. + * + * 68 4/28/99 5:06a Chris + * Added the no-scale damage by diff checkbox + * + * 67 4/22/99 7:08p Matt + * Reduced the number of object sounds from 3 to 2, since we were only + * using two. + * + * 66 4/22/99 4:13p Matt + * Deleted sounds array from the object struct, since it was never used. + * + * 65 4/22/99 11:33a Chris + * Added the ability of some generic objects to be able to go through + * forcefields and glass + * + * 64 4/21/99 2:50p Matt + * Killed function LoadObjectImage(), since it was only used in one file + * and only did a call to LoadPolygonModel() + * + * 63 4/21/99 1:23p Chris + * Added the check ceiling checkbox for generic objects + * + * 62 4/18/99 4:50p Matt + * Took out code that hacked in ammo amounts for weapon powerups, and + * added code to set the counts on the page and read and write to the + * table file. + * + * 61 4/15/99 1:41a Jeff + * changes for linux compile + * + * 60 4/14/99 10:31p Jeff + * fixed name of ai_info struct to t_ai_info + * + * 59 4/10/99 6:39p Matt + * Only save the designer-editable AI data in the Object_info array, + * instead of the whole ai_frame structure. This saves 3200 bytes per + * Object_info entry, which is about 2 MB overall. + * + * 58 4/02/99 11:23a Matt + * Made KillObject not take a death_info struct, but rather the death info + * as individual parameters. Moved death_info into objinfo.h, since it's + * only used for generic objects. Took out fade-away death hack, now that + * fade-away deaths can be explicitely set. + * + * 57 3/31/99 3:59p Chris + * made the MC_ stuff externalized to OSIRIS. + * + * 56 3/10/99 7:14p Jason + * upped max object id limit to 600 + * + * 55 2/28/99 6:20p Matt + * Added the ability to set death types for generic objects. + * + * 54 2/23/99 12:39p Jason + * added more options for generic objects + * + * 53 2/22/99 11:24p Matt + * Deleted static debris objects, since they were never used + * + * 52 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 51 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 50 1/19/99 4:22p Matt + * Added the ability for objects to have their own lighting info, + * different from the default lighting for that type of object. + * + * 49 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 48 1/06/99 5:06p Chris + * Improving OSIRIS/game intergration - improved support for custom + * animations + * + * 47 12/30/98 5:17p Jeff + * changes made to handle script name override, to override the name of + * the script to use in a module. + * + * 46 12/18/98 6:00p Jeff + * added support for door scripts in new osiris + * + * 45 12/16/98 11:27a Jeff + * added new osiris fields + * + * 44 9/28/98 6:23p Chris + * Changed multi_anim to custom_anim + * + * 43 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 42 8/31/98 1:35p Keneta + * Made some use count unsigned shorts + * + * 41 8/31/98 10:41a Keneta + * increase max_object_ids + * + * 40 8/26/98 8:07p Jason + * added directional lights to objects + * + * 39 8/16/98 6:17p Chris + * Added generic object spewing code + * + * 38 8/13/98 11:55a Jeff + * added 2 flags for inventory information + * + * 37 7/10/98 2:12p Jeff + * added a flag for inventory information in objinfo + * + * 36 5/03/98 5:38p Chris + * Added sounds to anim page + * + * 35 4/30/98 12:22p Jason + * did some lo-res model optimizations + * + * 34 4/24/98 7:09p Jeff + * Added a flag for a non-useable inventory item + * + * 33 4/16/98 2:56p Chris + * Updated buddy support and intergrated scripting and AI + * + * 32 4/13/98 2:21p Chris + * Fixed some collision problems dealing with AABBs and Polymodel paging + * in. + * + * 31 4/06/98 4:53p Jason + * got pageable polymodels working correctly with editor + * + * 30 3/25/98 11:02a Chris + * version 1.0 of the new AI ranged firing code. + * + * 29 3/12/98 6:22p Jeff + * added an atexit call to free up any memory allocated in objinfos + * + * 28 2/20/98 5:50p Jeff + * Changed it so that whether an inventory item was selected was placed in + * the objinfo flags + * + * 27 2/20/98 4:55p Jeff + * Added inven_selectable field to determine if an object can be selected + * in the inventory + * + * 26 2/16/98 2:47a Chris + * Massive improvements to the animation system and the AI + * + * 25 2/06/98 6:18p Jason + * fixed model mixup bug + * + * 24 2/03/98 6:15p Jeff + * Added fields for inventory description and icon name + * + * 23 1/29/98 3:29p Chris + * Major update to the AI system. + * + * 22 1/05/98 3:54p Chris + * Added ambient and explosion sounds + * + * 21 12/01/97 9:54a Chris + * Added support for concussive forces, generalized robot collisions to + * generic collisions + * + * 20 11/25/97 12:28p Chris + * Starting to add support for concussive blast damage + * + * 19 11/25/97 12:29p Jason + * added LOD levels for certain objects + * + * 18 11/06/97 6:18p Jason + * added support for lights that flicker a little bit + * + * 17 10/29/97 12:36p Jason + * added some features to object lighting + * + * 16 10/28/97 12:22p Chris + * now use the ai_frame struct to store ai information in the generic + * objects + * + * 15 9/24/97 6:18p Samir + * Use script names instead of script id values to identify scripts. + * + * 14 9/10/97 5:17p Jason + * more lighting improvements + * + * 13 9/08/97 11:51a Chris + * Added support for entering seconds-per-cycle animation information + * + * 12 9/05/97 1:29p Jason + * revamped generic object lighting + * + * 11 9/04/97 3:21p Jason + * added pulse timing for lit objects + * + * 10 9/04/97 12:11p Jason + * added flickering generic objects + * + * 9 9/03/97 6:17p Jason + * added code to support powerups/robots/etc that cast light + * + * 8 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 7 8/22/97 3:03a Chris + * + * 6 8/13/97 4:54p Matt + * Added destroyable flag & hitpoints + * + * 5 8/11/97 6:48p Matt + * Rewrote static object remap system + * + * 4 8/11/97 1:53p Matt + * Ripped out robot & powerup pages, and added generic page + * + * 3 8/08/97 11:54a Matt + * Added constants from robot.h, and prototypes for the generic object + * functions. + * + * 2 8/06/97 2:21p Matt + * + * 1 8/06/97 2:20p Matt + * Generic object info for robots, powerups, etc. + * + * $NoKeywords: $ + */ + +#ifndef _OBJINFO_H +#define _OBJINFO_H + +#include "object.h" +#include "manage.h" +#include "DeathInfo.h" + +#ifdef NEWEDITOR +#include "..\neweditor\ned_Object.h" +#include "..\neweditor\ned_Tablefile.h" +#endif + +//max sizes for inventory information in the objinfo +#define MAX_INVEN_DESC_SIZE 180 +#define MAX_INVEN_ICON_SIZE 30 + +//How many object ids in the array +#define MAX_OBJECT_IDS 910 + +//How many different sounds each object can make +#define MAX_OBJ_SOUNDS 2 + +//Object info flags +#define OIF_CONTROL_AI 0x01 //this object uses AI +#define OIF_USES_PHYSICS 0x02 //this object uses physics +#define OIF_DESTROYABLE 0x04 //this object can be destroyed +#define OIF_INVEN_SELECTABLE 0x08 //this object can be selected in the inventory +#define OIF_INVEN_NONUSEABLE 0x10 //this object can not be used by pressing ENTER during the game +#define OIF_INVEN_TYPE_MISSION 0x20 //this object is for Mission objectives +#define OIF_INVEN_NOREMOVE 0x40 //this object should NOT be removed from the inventory when used +#define OIF_INVEN_VISWHENUSED 0x80 //this object will not have it contol type, movement type and render types +#define OIF_AI_SCRIPTED_DEATH 0x100 +#define OIF_DO_CEILING_CHECK 0x200 +#define OIF_IGNORE_FORCEFIELDS_AND_GLASS 0x400 +#define OIF_NO_DIFF_SCALE_DAMAGE 0x800 +#define OIF_NO_DIFF_SCALE_MOVE 0x1000 +#define OIF_AMBIENT_OBJECT (1<<13) //this object is just for show, & can be removed to improve performance + +// This next numbers define the distance at which LOD popping occurs in models +#define DEFAULT_MED_LOD_DISTANCE 75.0f +#define DEFAULT_LO_LOD_DISTANCE 120.0f + + +//The number of ids of each type in the list +extern int Num_object_ids[]; + +extern char *Movement_class_names[]; +extern char *Anim_state_names[]; + +// These defines must correspond to the Static_object_names array +#define GENOBJ_GUIDEBOT 0 //NOTE: This must match ROBOT_GUIDEBOT +#define GENOBJ_CHAFFCHUNK 1 +#ifdef _WIN32 +#define GENOBJ_GUIDEBOTRED 2 //NOTE: This must match ROBOT_GUIDEBOTRED +#else +#define GENOBJ_GUIDEBOTRED 0 //NOTE: This must match ROBOT_GUIDEBOTRED +#endif + +#define IS_GUIDEBOT(x) (((object *)x)->type==OBJ_ROBOT&&((((object *)x)->id==ROBOT_GUIDEBOTRED)||(((object *)x)->id==ROBOT_GUIDEBOT))) + +//Animation constants +#define NUM_MOVEMENT_CLASSES 5 +#define NUM_ANIMS_PER_CLASS 24 + +//Info for an animation state +typedef struct +{ + short from,to; + float spc; + int anim_sound_index; + ubyte used; +} anim_entry; + +typedef struct +{ + anim_entry elem[NUM_ANIMS_PER_CLASS]; +} anim_elem; + +#define MAX_DSPEW_TYPES 2 +#define DSF_ONLY_IF_PLAYER_HAS_OBJ_1 1 +#define DSF_ONLY_IF_NO_1 2 + +//How many different deaths each object can have +#define MAX_DEATH_TYPES 4 + +//Death info for an object type +typedef struct { + uint flags; //death flags + float delay_min; //if delay, min amount + float delay_max; //if delay, max amount +} death_info; + +//AI info for this object +//This is the subset of ai_frame data that the user can edit for an object type +typedef struct { + char ai_class; + char ai_type; + + float max_velocity; + float max_delta_velocity; + float max_turn_rate; + float max_delta_turn_rate; + + float attack_vel_percent; + float flee_vel_percent; + float dodge_vel_percent; + + float circle_distance; + float dodge_percent; + + float melee_damage[2]; + float melee_latency[2]; + + int sound[MAX_AI_SOUNDS]; + + char movement_type; + char movement_subtype; + + int flags; + int notify_flags; + + float fov; + + float avoid_friends_distance; + + float frustration; + float curiousity; + float life_preservation; + float agression; + + float fire_spread; + float night_vision; + float fog_vision; + float lead_accuracy; + float lead_varience; + float fight_team; + float fight_same; + float hearing; + float roaming; + + float biased_flight_importance; + float biased_flight_min; + float biased_flight_max; +} t_ai_info; + +#ifndef NEWEDITOR + +//Info for robots, powerups, debris, etc. +typedef struct { + char name[PAGENAME_LEN]; //the name on the page + + int type; //what type of object this is + float size; //size + int flags; //misc flags. See above. + +// int render_type; //set RT_ defines in object.h + int render_handle; //handle for bitmap/polygon model(hi-res) + int med_render_handle; //handle for med res version of this object + int lo_render_handle; //handle for lo res version of this object + + float med_lod_distance; // The distance at which the med-res model takes over + float lo_lod_distance; // The distance at which the lo-res model takes over + + + int score; //how many points you get for killing/picking up + + int hit_points; //if destroyable, the hit points + float damage; + float impact_size; + float impact_time; + + int ammo_count; //if a powerup, how much ammo it has + + char *description; //used for inventory + char icon_name[MAX_INVEN_ICON_SIZE]; //used for inventory + + short sounds[MAX_OBJ_SOUNDS]; //list of sound handles + short dspew[MAX_DSPEW_TYPES]; + float dspew_percent[MAX_DSPEW_TYPES]; + short dspew_number[MAX_DSPEW_TYPES]; + unsigned char f_dspew; + + //Valid for physics objects only + physics_info phys_info; //the physics data for this obj type + + // Valid for lighting of objects + light_info lighting_info; + + // for multiplayer respawning + float respawn_scalar; + + bool multi_allowed; + + // OSIRIS information + char module_name[MAX_MODULENAME_LEN]; + char script_name_override[PAGENAME_LEN]; + + //Death information + death_info death_types[MAX_DEATH_TYPES]; //the ways this object can die + ubyte death_probabilities[MAX_DEATH_TYPES]; //how likely each death is, from 0-100 (percent) + + //Valid for AI objects only + t_ai_info *ai_info; //the AI info for this obj type + + // Valid for polygon models with weapons + otype_wb_info *static_wb; +// otype_wb_info static_wb[MAX_WBS_PER_OBJ]; + + //Valid for polygon model objects only + anim_elem *anim; // which anim states are active +// anim_elem anim[NUM_MOVEMENT_CLASSES]; // which anim states are active + +} object_info; + +//The big array of object info +extern object_info Object_info[]; + +#endif + +// Sets all objects to unused +void InitObjectInfo (); + +// Frees up all objects (any memory allocated, and resets them) automatic atexit +void FreeObjectInfo(void); + +// Allocs a object for use, returns -1 if error, else index on success +//int AllocObjectID(int type, int flags); +int AllocObjectID(int type, bool f_anim, bool f_weapons, bool f_ai); + +// Frees object index n +void FreeObjectID(int n); + +//Find an object with the given type. Returns -1 if none found. +int GetObjectID(int type); + +// Gets next object from n of the same type as n +int GetNextObjectID(int n); + +// Gets previous object from n that has the same type +int GetPrevObjectID(int n); + +// Searches thru all object ids for a specific name +// Returns the found id, or -1 if not found +int FindObjectIDName(char *name); + +// Given an object handle, returns an index to that object's model +int GetObjectImage(int handle); + +// Given an object, renders the representation of this object +void DrawObject(object *obj); + +// Moves an object from a given index into a new one (above MAX_STATIC_IDS) +// returns new index +int MoveObjectFromIndex (int index); + +//Remap all the static object ids (objects that must have a specific index) into their assigned slots. +void RemapStaticIDs(); + +void ChangeOldModelsForObjects (int old_handle,int new_handle); + +#endif \ No newline at end of file diff --git a/Descent3/objinit.h b/Descent3/objinit.h new file mode 100644 index 000000000..33f5e22b5 --- /dev/null +++ b/Descent3/objinit.h @@ -0,0 +1,61 @@ +/* + * $Logfile: /DescentIII/main/objinit.h $ + * $Revision: 8 $ + * $Date: 4/20/99 12:24p $ + * $Author: Matt $ + * + * Header for ObjInit.cpp + * + * $Log: /DescentIII/main/objinit.h $ + * + * 8 4/20/99 12:24p Matt + * Re-revised the ObjInit() system. ObjInit() again does all the + * initialization stuff, but now it's passed a lot more information so + * those fields can be set before the rest of the initialization takes + * place. + * + * 7 4/10/99 5:56p Matt + * Cleaned up object initialization code, including create object & load + * object. + * + * 6 3/30/98 6:58p Matt + * When reinitializing an object, don't bash its custom script. + * + * 5 3/30/98 6:20p Matt + * Renamed ResetObject() to be ObjReInitAll() + * + * 4 1/18/98 9:06p Matt + * Changed a comment, and deleted some unused code + * + * 3 8/21/97 5:56p Samir + * Took out script specific stuff from ObjInit and moved to ObjScript + * + * 2 8/12/97 3:25p Samir + * Added prototype to initialize scripts for objects. + * + * 2 4/04/97 2:57p Matt + * Added code to initialize all the type-specific data for an object from + * the page for that object type. This means that we need to pass less + * info to ObjCreate(), and that we save less info in the level save file. + * It also makes it easy to reset all the objects when an object page has + * changed. + * + * 1 4/04/97 11:38a Matt + * + * $NoKeywords: $ + */ + +#ifndef _OBJINIT_H +#define _OBJINIT_H + +#include "object.h" + +//Initializes a new object. All fields not passed in set to defaults. +//Returns 1 if ok, 0 if error +int ObjInit(object *objp,int type,int id,int handle,vector *pos,float creation_time,int parent_handle=OBJECT_HANDLE_NONE); + +//Re-copies data to each object from the appropriate page for that object type. +//Called after an object page has changed. +void ObjReInitAll(void); + +#endif diff --git a/Descent3/osiris_dll.h b/Descent3/osiris_dll.h new file mode 100644 index 000000000..c2a60cdc8 --- /dev/null +++ b/Descent3/osiris_dll.h @@ -0,0 +1,325 @@ +/* +* $Logfile: /DescentIII/main/osiris_dll.h $ +* $Revision: 22 $ +* $Date: 4/14/99 3:57a $ +* $Author: Jeff $ +* +* OSIRIS system header +* +* $Log: /DescentIII/main/osiris_dll.h $ + * + * 22 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 21 3/10/99 6:21p Jeff + * many fixes to demo system. Fixed IGC so cameras move...fixed osiris to + * be restored correctly, and it handles errors on restore + * + * 20 3/05/99 5:25p Jeff + * fixed saving demos in multiplayer...required adding a parameter to + * Osiris_LoadMissionModule() to pass in the name of the d3m file (for + * storage) + * + * 19 2/28/99 8:52p Jeff + * added functions to enable/disable creation events + * + * 18 2/18/99 11:06a Jeff + * added event masks (so you can enable/disable object/trigger/level + * events) + * + * 17 2/17/99 3:27a Jeff + * added game checksum function to handle out-of-sync dlls + * + * 16 2/10/99 3:29p Jeff + * extracted dll manager knows the difference between game hogs and + * mission hogs + * + * 15 1/22/99 8:54p Jeff + * added custom-default script overrides + * + * 14 1/22/99 5:15p Jeff + * added a key to toggle osiris debug messages + * + * 13 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 12 1/16/99 10:39a Jeff + * added mission memory management to Osiris...only slightly tested. Need + * to solve game save/restore problem still + * + * 11 1/08/99 1:50p Jeff + * finished support on loading scripts from hogs + * + * 10 1/04/99 12:23p Jeff + * added to evt_use and support for mission module scripts + * + * 9 12/31/98 7:33p Jeff + * improved Osiris timers to have unique IDs, along with adding an + * EVT_TIMERCANCEL and a new flag to autodestruct the timer if a given + * object handle dies. + * + * 8 12/18/98 10:42a Jeff + * added timer support and auto-save memory manager for new osiris. New + * events evt_memrestore state added also + * + * 7 12/17/98 7:26p Jeff + * added memory manager for autosaving script data + * + * 6 12/17/98 5:43p Jeff + * created timer system for osiris and save restore events + * + * 5 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 4 12/16/98 10:16p Jeff + * added functions to call level and trigger events + * + * 3 12/16/98 10:58a Jeff + * checked in so Jason can use + * + * 2 12/14/98 11:32a Jeff + * started work on osiris load and bind functions + * + * 1 12/13/98 9:10p Jeff +* +* $NoKeywords: $ +*/ + + +#ifndef __OSIRIS_H_ +#define __OSIRIS_H_ + +#include "pstypes.h" +#include "object_external_struct.h" +#include "osiris_share.h" +#include "module.h" +#include "CFILE.H" + +extern uint Osiris_game_checksum; + +// Osiris_InitModuleLoader +// Purpose: +// Initializes the OSIRIS module loader and handling system +void Osiris_InitModuleLoader(void); + +// Osiris_FreeModule +// Purpose: +// Removes a currently loaded module from the OSIRIS system +void Osiris_FreeModule(int id); + +// Osiris_ShutdownModuleLoader +// Purpose: +// Closes down the OSIRIS module loader and handling system +void Osiris_ShutdownModuleLoader(void); + +// Osiris_FindLoadedModule +// Purpose: +// Given the name of a module, it returns the id of a loaded OSIRIS module. -1 if it isn't loaded. +int Osiris_FindLoadedModule(char *filename); + +// Osiris_LoadLevelModule +// Purpose: +// Given a module name, it will attempt to load the module as a level module. If it succeeds +// it will return the id of the module where it has been loaded. If the module was already loaded +// before calling this function, it will return the id to where the module is, and will not reload +// the module. Returns -1 if the module does not exist. Returns -2 if the module couldn't initialize. +// Returns -3 if the module is not a level module. Returns -4 if no module slots are available. +int Osiris_LoadLevelModule(char *module_name); + +// Osiris_UnloadLevelModule +// Purpose: +// Instead of saving the handle returned from Osiris_LoadLevelModule() and calling +// Osiris_UnloadModule() with that handle, you can just call this, instead of the call +// to Osiris_UnloadModule(). +void Osiris_UnloadLevelModule(void); + +// Osiris_LoadGameModule +// Purpose: +// Given a module name, it will attempt to load the module as a game module. If it succeeds +// it will return the id of the module where it has been loaded. If the module was already loaded +// before calling this function, it will return the id to where the module is, and will not reload +// the module. Returns -1 if the module does not exist. Returns -2 if the module couldn't initialize. +// Returns -3 if the module is not a game module. Returns -4 if no module slots are available. +int Osiris_LoadGameModule(char *module_name); + +// Osiris_UnloadModule +// Purpose: +// Given a module id, it will decrement the reference count to that module by one. If the reference +// count becomes zero, the module will be unloaded from memory. +void Osiris_UnloadModule(int module_id); + +// Osiris_LoadMissionModule +// Purpose: +// It will attempt to load the module as a game module. If it succeeds +// it will return the id of the module where it has been loaded. If the module was already loaded +// before calling this function, it will return the id to where the module is, and will not reload +// the module. Returns -1 if the module does not exist. Returns -2 if the module couldn't initialize. +// Returns -3 if the module is not a game module. Returns -4 if no module slots are available. +// This technically doesn't load a mission module, as it should already be loaded by +// Descent 3 prior. +int Osiris_LoadMissionModule(module *module_handle,char *filename); + +// Osiris_UnloadMissionModule +// Purpose: +// Instead of saving the handle returned from Osiris_LoadMissionModule() and calling +// Osiris_UnloadModule() with that handle, you can just call this, instead of the call +// to Osiris_UnloadModule(). +void Osiris_UnloadMissionModule(void); + +// Osiris_BindScriptsToObject +// Purpose: +// Call this function after an object has been created to bind all the scripts associated +// with it to the object. This function must be called near the end of it's initialization, +// to make sure that all fields have been filled in. This function does not call any events. +// This function will also load any dll's needed for it's game script. +// returns false if nothing was bound. +bool Osiris_BindScriptsToObject(object *obj); + +// Osiris_DetachScriptsFromObject +// Purpose: +// Call this function when an object is about to be destroyed. This will unbind and remove +// all scripts associated with that object. This function does not call any events. +void Osiris_DetachScriptsFromObject(object *obj); + +// Osiris_CallEvent +// Purpose: +// Triggers an event for an object. Pass in the event number and the associated +// structure of data. All events will be chained through the associated scripts of the +// object (as long as they are available) in the order: custom script, level script, +// mission script, and finally it's default script. The chain breaks if one of the scripts +// returns false on the call to their CallInstanceEvent(). +bool Osiris_CallEvent(object *obj, int event, tOSIRISEventInfo *data ); + +// Osiris_CallLevelEvent +// Purpose: +// Triggers an event for a level script. Returns true if the default action should continue +// to process. +bool Osiris_CallLevelEvent(int event, tOSIRISEventInfo *data ); + +// Osiris_CallTriggerEvent +// Purpose: +// Triggers an event for a trigger script. Returns true if the default action should continue +// to process. +bool Osiris_CallTriggerEvent(int trignum,int event,tOSIRISEventInfo *ei); + +// Osiris_ProcessTimers +// Purpose: +// This function checks all timers currently running, if any need to be signaled it signals them. +void Osiris_ProcessTimers(void); + +// Osiris_CreateTimer +// Pupose: +// Adds a timer to the list to be processed. You'll receive a EVT_TIMER when the timer is signaled. +// Returns an id to the timer, which can be used to cancel a timer. -1 on error. +int Osiris_CreateTimer(tOSIRISTIMER *ot); + +// Osiris_ResetAllTimers +// Purpose: +// Flushes all the timers +void Osiris_ResetAllTimers(void); + +// Osiris_CancelTimer +// Purpose: +// Cancels a timer thats in use, given it's ID +void Osiris_CancelTimer(int handle); + +// Osiris_TimerExists +// Purpose: +// Returns true if the timer is valid +ubyte Osiris_TimerExists(int handle); + +// Osiris_SaveSystemState +// Purpose: +// Saves the current state of the system (not the scripts!) to file +void Osiris_SaveSystemState(CFILE *file); + +// Osiris_RestoreSystemState +// Purpose: +// Restore the state of the system from file (does not restore scripts!) +bool Osiris_RestoreSystemState(CFILE *file); + +// Osiris_InitMemoryManager +// Purpose: +// Initializes the memory manager for the scripts, for buffers that the scripts want +// automatically restored/save. +void Osiris_InitMemoryManager(void); + +// Osiris_CloseMemoryManager +// Purpose: +// Shuts down the Osiris memory manager, freeing any unfreed memory +void Osiris_CloseMemoryManager(void); + +// Osiris_AllocateMemory +// Purpose: +// Allocates a chunk of memory to be associated with a script. It will automatically +// save this memory to disk on game save, and will pass the pointer to this memory on EVT_RESTORE +void *Osiris_AllocateMemory(tOSIRISMEMCHUNK *mc); + +// Osiris_FreeMemory +// Purpose: +// Frees a chunk of memory that was allocated by Osiris_AllocateMemory(). +void Osiris_FreeMemory(void *mem_ptr); + +// Osiris_FreeMemoryForScript +// Purpose: +// Frees all memory allocated for a given script +void Osiris_FreeMemoryForScript(tOSIRISSCRIPTID *sid); + +// Osiris_RestoreMemoryChunks +// Purpose: +// Restores the 'auto manage' from file, calls the EVT_MEMRESTORE +void Osiris_RestoreMemoryChunks(CFILE *file); + +// Osiris_SaveMemoryChunks +// Purpose: +// Saves out the 'auto manage' script memory to file +void Osiris_SaveMemoryChunks(CFILE *file); + +// Osiris_ExtractScriptsFromHog +// Given the handle of a loaded hog file, this extracts all the scripts out to a temp directory +// Pass false for the second parameter if it's a game hog (d3.hog for example) +int Osiris_ExtractScriptsFromHog(int library_handle,bool is_mission_hog=true); + +// Osiris_ClearExtractedScripts +// Deletes the temp files created when the scripts where extracted from the hog +// Pass false if you want it to remove _all_ extracted hogs...else only mission related ones +void Osiris_ClearExtractedScripts(bool misson_only=true); + +// Initializes the Osiris Mission Memory System +void Osiris_InitOMMS(void); +// Shutsdown the Osiris Mission Memory System (frees all memory associated, call at end of mission) +void Osiris_CloseOMMS(void); + +extern bool Show_osiris_debug; + +#define OEM_OBJECTS 0x01 +#define OEM_TRIGGERS 0x02 +#define OEM_LEVELS 0x04 +// Osiris_EnableEvents +// Purpose: +// Enables the passed in mask of event types to be called +void Osiris_EnableEvents(ubyte mask); +// Osiris_DisableEvents +// Purpose: +// Disables the passed in mask of event types +void Osiris_DisableEvents(ubyte mask); + +// Osiris_DisableCreateEvents +// Purpose: +// Disables any events involved when an object is created. This is to be used for +// Loading games/viewing demos, as so not to re-initialize good data. +void Osiris_DisableCreateEvents(void); +// Osiris_EnablesCreateEvents +// Purpose: +// Enables any events involved when an object is created. This is to be used for +// Loading games/viewing demos, as so not to re-initialize good data. (call when done with Osiris_DisableCreateEvents()) +void Osiris_EnableCreateEvents(void); + + +#endif \ No newline at end of file diff --git a/Descent3/osiris_predefs.cpp b/Descent3/osiris_predefs.cpp new file mode 100644 index 000000000..94be66a38 --- /dev/null +++ b/Descent3/osiris_predefs.cpp @@ -0,0 +1,4584 @@ +/* +* $Logfile: /DescentIII/main/osiris_predefs.cpp $ +* $Revision: 128 $ +* $Date: 4/13/00 2:31p $ +* $Author: Matt $ +* +* implementation code for osiris predefined functions +* +* $Log: /DescentIII/main/osiris_predefs.cpp $ + * + * 128 4/13/00 2:31p Matt + * Don't allow scripts to kill the guidebot. + * + * 127 11/05/99 4:28p Jay + * Made sure the wb is a valid index + * + * 126 11/04/99 3:08p Jeff + * fixed ushort check with -1 bug + * + * 125 11/03/99 10:20a Chris + * Added the ability to find out if a room is volatile + * + * 124 11/02/99 12:16p Chris + * More Merc code - added support for ignore_obj_lists for the Osiris + * RayCast function + * + * 123 10/27/99 4:19p 3dsmax + * added cloak flag for object value get + * + * 122 10/23/99 2:43a Chris + * Added the PutObjectOnObject AI Goal + * + * 121 10/22/99 11:31a Matt + * Mac merge + * + * 120 10/20/99 5:40p Chris + * Added the Red Guidebot + * + * 119 10/19/99 10:18p 3dsmax + * error check object name retreival. + * + * 118 10/16/99 9:56p Jeff + * added a way to strip all players of all weapons and energy + * + * 117 10/16/99 8:46p Jeff + * player value function to strip a player of weapons and energy + * + * 116 10/12/99 12:44p Jeff + * added obj_value things for negative light and virus infection + * + * 115 9/13/99 12:26p Samir + * added AIG_GUARD_AREA to add goal, since it exits in AIGoal.cpp already. + * + * 114 9/10/99 11:03a Samir + * added command to get object's name from Obj_Value. + * + * 113 5/23/99 5:50p Chris + * Fixed problems with msafe send on over non-changed information + * + * 112 5/23/99 12:33a Chris + * Cannot change the control type of dying objects + * + * 111 5/21/99 7:29a Chris + * More tweaking... + * + * 110 5/20/99 1:16a Chris + * Fixed the AIV_I_TARGET_HANDLE set function + * + * 109 5/19/99 3:25p Jason + * fixed wrong ordering of InitObjectScripts and MultiSendObject + * + * 108 5/12/99 8:03p Jason + * fixed bug where multiplayer and demo code was playing a sound before an + * object was even created + * + * 107 5/12/99 6:18a Chris + * GetNearby objects doesn't return dead or RT_NONE objects + * + * 106 5/10/99 8:21a Chris + * FindNearby... ignores dead objects (OF_DEAD) + * + * 105 5/10/99 12:23a Chris + * Fixed another hearing/seeing case. :) Buddy bot now is in the player + * ship at respawn + * + * 104 5/07/99 1:32p Jeff + * made osipf for sounds multisafe + * + * 103 5/06/99 4:54p Matt + * Fixed array bounds error. + * + * 102 5/05/99 4:56p Chris + * Added the RType powerup for the GB + * + * 101 5/04/99 2:51a Chris + * Fixed bug with fire sounds in Obj_WBValue + * + * 100 4/29/99 7:15p Jeff + * added initial velocity parameter to Obj_Create + * + * 99 4/29/99 4:50p Chris + * Fixed matcen and Osiris created objects so thier ambient sounds play + * + * 98 4/29/99 3:25p Chris + * Added support to find out if an object has wbs + * + * 97 4/28/99 2:26a Jeff + * fixed places in code where only checking OBJ_PLAYER, added OBJ_GHOST + * and OBJ_OBSERVER where applicable + * + * 96 4/27/99 4:43a Jeff + * changed to call msafe functions for guidebot name commands + * + * 95 4/26/99 5:53p Jason + * make sure no objects are being created from script that are flagged + * with lightmaps + * + * 94 4/26/99 4:28a Jeff + * added multisafe functions to check a player's weapons, and add/remove + * weapons from a player + * + * 93 4/25/99 10:38p Matt + * Made the Osiris kill object function work on players. + * + * 92 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 91 4/24/99 6:46p Jeff + * added functions for theif so he can steal things other than weapons + * + * 90 4/24/99 2:19a Chris + * Added another parameter to FollowPathSimple + * + * 89 4/24/99 12:09a Jeff + * added path value functions + * + * 88 4/22/99 10:25p Chris + * Corrected the mprintf for Player_Value + * + * 87 4/22/99 5:46p Jason + * made weapons work in multiplayer + * + * 86 4/21/99 3:01p Matt + * Added a new type for dying objects that have AI, instead of keeping a + * flag in the dying info. + * + * 85 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 84 4/20/99 8:56p Chris + * Fixed problem with robots not being able to open locked doors that a + * player has the key for. + * + * 83 4/20/99 7:29p Jeff + * handle set/get guidebot name + * + * 82 4/18/99 7:27a Chris + * Added AIAF_IMMEDIATE to force non-tiling and instant anim loop changes + * + * 81 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 80 4/12/99 6:15p Samir + * Sound priorities pass 1 + * + * 79 4/03/99 1:13a Jeff + * added multisafe/dallas actions to set an object on fire + * + * 78 4/02/99 10:13p Jeff + * made ship permissions multi safe + * + * 77 4/02/99 11:23a Matt + * Made KillObject not take a death_info struct, but rather the death info + * as individual parameters. Moved death_info into objinfo.h, since it's + * only used for generic objects. Took out fade-away death hack, now that + * fade-away deaths can be explicitely set. + * + * 76 4/02/99 10:18a Chris + * We can now mess with the Object_info anim stuff + * + * 75 3/30/99 7:25p Chris + * Fixed the bug where dead objects would get a valid type + * + * 74 3/30/99 4:32p Chris + * Moved subtype to the main goal sturct (from goal_info). Made move + * relative object vec finishable. (Like get behind player) + * + * 73 3/29/99 11:53a Chris + * Corrected a objnum to obj ref problem in RayCast() + * + * 72 3/27/99 12:53p Chris + * Added new enabler types + * + * 71 3/23/99 5:53p Matt + * Fixed bug in physics set flags code + * + * 70 3/22/99 10:59a Chris + * Awareness code was tweaked. Multisafe stuff added for objects. + * + * 69 3/17/99 11:47a Jeff + * exported function to get what language we are using + * + * 68 3/16/99 11:26a Chris + * Buddy is now smarter -- gets to trigger level goals + * + * 67 3/10/99 1:33p Chris + * Fixes for multi-line gb messages + * + * 66 3/03/99 3:01a Chris + * Exported the difficulty stuff to OSIRIS + * + * 65 3/03/99 12:49a Chris + * + * 64 3/03/99 12:13a Chris + * + * 63 3/03/99 12:12a Chris + * + * 62 3/03/99 12:11a Chris + * one more AI path check function + * + * 61 3/03/99 12:07a Chris + * Added the two new OSIRIS predefs for AI path finding + * + * 60 3/01/99 8:13p Chris + * major hack so that goal text can be displayed + * + * 59 3/01/99 8:05p Chris + * Fixed bugs with the Level goal value functions + * + * 58 3/01/99 7:20p Chris + * Fixed dist problems with finding nearby objects + * + * 57 2/28/99 11:30p Chris + * FindObjOfType and OSIRIS controllable deaths + * + * 56 2/25/99 5:43p Chris + * Massive improvement to BOA and AI (again) + * + * 55 2/22/99 8:21p Chris + * + * 54 2/22/99 5:59p Chris + * Level Goals are now in OSIRIS. :) + * + * 53 2/21/99 5:51p Chris + * FIxed a bug with OBJV_C_TYPE (It should have been OBJV_I_TYPE). Added + * Obj_FindType() + * + * 52 2/20/99 5:08p Chris + * + * 51 2/18/99 4:27p Jeff + * added lookup functions for matcen, paths, level goals + * + * 50 2/13/99 3:14p Dan + * Check for no AI info for object in osipf_ObjectCustomAnim() + * + * 49 2/12/99 6:56p Chris + * + * 48 2/12/99 6:44p Chris + * + * 47 2/11/99 2:56a Jeff + * improvements to introcam + * + * 45 2/10/99 4:45p Jeff + * table file parser stuff + * + * 44 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 43 2/09/99 9:58a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 42 2/08/99 3:06a Jeff + * created a function to switch a player between ai and flying control + * types (not exported to osiris...instead used in multisafe) + * + * 41 2/06/99 12:55p Chris + * Converted the path stuff to base-1 + * + * 40 2/03/99 1:51a Jeff + * added predefs for ship permissions + * + * 39 2/03/99 12:43a Chris + * Added Obj_GetGroundPos + * + * 38 1/29/99 5:10p Chris + * Added an optional parent handle check for FindObjOfType + * + * 37 1/26/99 2:51p Chris + * AIG_WANDER improvements + * + * 36 1/26/99 9:31a Chris + * Obj_Value now correctly return OBJ_NONE when doing a TYPE check on an + * invalid objref + * + * 35 1/25/99 6:13p Matt + * When doing a radius attach to a powerup, cut the radius ratio in half, + * since powerups use double their actual radius. + * + * 34 1/25/99 8:38a Chris + * The the GUID to AI_FollowPathSimple + * + * 33 1/25/99 7:43a Chris + * Added the GUID (Goal Unique Id) and added the ability for weapon + * batteries to always fire exactly forward. + * + * 32 1/24/99 11:39p Chris + * Added AIG_FOLLOW_PATH to AI_AddGoal for OSIRIS + * + * 31 1/24/99 8:17p Chris + * Updated AI and OSIRIS. Externalized fireball constants for spew and + * sparks. Added CreateRandomSparks to OSIRIS, renamed a bunch of AI + * Notify names to use underscores. Fixed a memory access leak in the + * matcen effect code. + * + * 30 1/24/99 6:36p Matt + * Made AIGoalFollowPathSimple() use slot 3 instead of slot 0 (with + * ChrisP) + * + * 29 1/23/99 4:48p Matt + * Fixed a pair of small bugs + * + * 28 1/22/99 6:53p Chris + * Fixed LoadandBind Aux notify problems, fixed static path problems, + * fixed AIF_FORCE_AWARE Problems, improved path code + * + * 27 1/22/99 4:47p Jeff + * + * 26 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 25 1/21/99 3:56p Nate + * Changed osipf_FindObjectName() so that it returns a handle + * + * 24 1/20/99 4:16p Jeff + * added ai notify child events and predefs to get ids of sound, room, + * trigger, object names + * + * 23 1/20/99 1:01a Chris + * Improved AI and OSIRIS intergration + * + * 22 1/18/99 2:46p Matt + * Combined flags & flags2 fields in object struct + * + * 21 1/18/99 9:05a Chris + * Improved OSIRIS, AI, and ATTACH system, changed wiggle code, changed + * attach code for rad attaches, and added the AIG_ATTACH_OBJ goal + * + * 20 1/15/99 5:59p Chris + * + * 19 1/13/99 5:20p Jeff + * added 4 new file operation predefs + * + * 18 1/13/99 3:29a Chris + * oops, forgot to include sounds.h + * + * 17 1/13/99 3:25a Chris + * Added Obj_Burning and Obj_IsEffect to OSIRIS + * + * 16 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 15 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 14 1/05/99 12:24p Chris + * More OSIRIS improvements + * + * 13 1/05/99 11:34a Jeff + * fixed bugs in mission_flagget/set (needed to decrement flag before + * using) + * + * 12 1/04/99 6:43p Jeff + * added support to get/set mission flag values + * + * 11 1/04/99 12:42p Chris + * Updated OSIRIS with "xxxx_external.h" files and Obj_Value, and + * Matcen_XXX functions + * + * 10 12/30/98 3:46p Chris + * Incremental AI changes + * + * 9 12/23/98 12:10p Chris + * Added Gametime and Frametime to OSIRIS + * + * 8 12/22/98 4:51p Chris + * Added a ObjCreate predefined function + * + * 7 12/22/98 2:46p Chris + * Added the AIValue predefined function + * + * 6 12/21/98 5:30p Samir + * predef for determining if object is being rendered. + * + * 5 12/18/98 4:01p Samir + * implemented controls functions. + * + * 4 12/17/98 6:44p Samir + * added control manipulation predefs. + * + * 3 12/17/98 5:43p Jeff + * created timer system for osiris and save restore events + * + * 2 12/16/98 10:14p Jeff + * created osiris predefined function file +* +* $NoKeywords: $ +*/ +#include +#include "osiris_predefs.h" +#include "object.h" +#include "mono.h" +#include "trigger.h" +#include "pstypes.h" +#include "pserror.h" +#include "hlsoundlib.h" +#include "gamepath.h" +#include "AIGoal.h" +#include "AIMain.h" +#include "soundload.h" +#include "room.h" +#include "objinfo.h" +#include "weapon.h" +#include "game.h" +#include "terrain.h" +#include "attach.h" +#include "matcen.h" +#include "findintersection.h" +#include "controls.h" +#include "Controller.h" +#include "ship.h" +#include "Mission.h" +#include "osiris_share.h" +#include "multisafe.h" +#include "sounds.h" +#include "polymodel.h" +#include "multi.h" +#include "viseffect.h" +#include "PHYSICS.H" +#include "levelgoal.h" +#include "BOA.h" +#include "marker.h" +#include "damage.h" +#include "aipath.h" +#include "difficulty.h" +#include "localization.h" +#include "psrand.h" +#include "demofile.h" + +int *hack_ilist = NULL; +int hack_list[100]; + +// osipf_CallObjectEvent +// Sends an event to an object. Returns true if the default action should +// continue to process. +bool osipf_CallObjectEvent(int objhandle,int event,tOSIRISEventInfo *ei) +{ + object *objp = ObjGet(objhandle); + if(!objp) + return false; + + return Osiris_CallEvent(objp,event,ei); +} + +// osipf_CallTriggerEvent +// Sends an event to a trigger. Returns true if the default action should +// continue to process. +bool osipf_CallTriggerEvent(int trignum,int event,tOSIRISEventInfo *ei) +{ + if(trignum<0 || trignum>=Num_triggers) + return false; + + return Osiris_CallTriggerEvent(trignum,event,ei); +} + +/* + +*Get/Set Object Position +*Kill object +*Get Object max shields +*Get Object energy max +*Check to see if player has weapon +*Add weapon to a player +*Add weapon ammo +*Get weapon ammo +*Get/Set Player flags +*Is doorway openable +*play sound 2d/3d +*stop sound +*adjust 2d sound +*pickup powerup? +*end level +*timer events +*explode object +*start/stop spew +*get object type +*get object radius +*is object alive +*create pop-up view +*start end level sequence +*add type/id to inventory +*remove from inventory +*get inventory count +*use inventory item +*check for inventory item +*get player room +*set/get lighting effect distance +*set/get room wind +*get object fvec,rvec,uvec +*get/set object velocity +*get/set object parent +*enable WB +*get object anim frame +*play voice/stream +*add counter measure +*get counter measure count +*set portal render +*change face texture +*set object lighting effect parms +*ghost/unghost object +*set/get quad firing +*add player way point +*/ + +// searches through GamePath index and returns index of path matching name +// returns -1 if not found +int osipf_AIGetPathID(char *string) +{ + // extract string reference + return FindGamePathName(string); +} + +int osipf_AIGoalFollowPathSimple(int objhandle,int path_id,int guid, int flags, int slot) +{ + object *obj = ObjGet(objhandle); + path_information path_info; + + if(!obj){ + mprintf((0, "Illegal object passed to AIGoalFollowPathSimple\n")); + return -1; + } + + if(path_id == -1) + { + mprintf((0, "Illegal Path Id Passed To AIGoalFollowPathSimple\n")); + Int3(); + return -1; + } + + if(obj->control_type != CT_AI) + { + mprintf((0,"Illegal Object CT Passed To AIGoalFollowPathSimple\n")); + Int3(); + return -1; + } + + path_info.path_id = path_id; + + if(flags & GF_PATH_MOVE_REVERSE_DIR) + path_info.next_node = GamePaths[path_id].num_nodes - 1; + else + path_info.next_node = 0; + + path_info.start_node = 0; + path_info.end_node = GamePaths[path_id].num_nodes - 1; + + return GoalAddGoal(obj, AIG_FOLLOW_PATH, (void *)&path_info, slot, 1.0f, flags, guid); +} + +int osipf_AIPowerSwitch(int objhandle,ubyte f_power_on) +{ + object *obj = ObjGet(objhandle); + if(!obj) + { + mprintf((0,"Illegal Object passed to AIPowerSwitch\n")); + return -1; + } + + if(f_power_on) + AIPowerSwitch(obj, true); + else + AIPowerSwitch(obj, false); + + return 0; +} + +// Touches a sound file so it loads into memory +void osipf_SoundTouch(char *str) +{ + int id = FindSoundName(IGNORE_TABLE(str)); + + if (id == -1) { + mprintf((0,"Sound %s was not found. Unable to touch.\n",str)); + Int3(); + } + else + Sound_system.CheckAndForceSoundDataAlloc(id); +} + +// Gets room values +void osipf_RoomValue(int roomnum, char op, char vtype, void *ptr, int index) +{ + if(roomnum < 0 || roomnum > Highest_room_index || !Rooms[roomnum].used) + { + if(vtype == RMV_C_USED) + *(char *)ptr = 0; + else + mprintf((0,"RoomValue: Illegal Room Passed\n")); + + return; + } + + room *rp = &Rooms[roomnum]; + + switch(vtype) + { + case RMV_I_FLAGS: + if(op == VF_GET) + *(int *)ptr = rp->flags; + else if(op == VF_SET) + rp->flags = *(int *)ptr; + else if(op == VF_SET_FLAGS) + rp->flags |= *(int *)ptr; + else if(op == VF_CLEAR_FLAGS) + rp->flags &= ~(*(int *)ptr); + break; + case RMV_V_WIND: + if(op == VF_SET) + rp->wind = *(vector *)ptr; + else if(op == VF_GET) + *(vector *)ptr = rp->wind; + break; + case RMV_C_USED: + if(op == VF_GET) + *(char *)ptr = (rp->used != 0); + break; + + case RMV_I_NUM_PATH_PNTS: + if(op == VF_GET) + *(int *)ptr = 1; // chrishack -- might change if the multi point intraroom points are added + break; + case RMSV_V_PATH_PNT: + if(op == VF_SET) + rp->path_pnt = *(vector *)ptr; + else if(op == VF_GET) + *(vector *)ptr = rp->path_pnt; + break; + + case RMV_I_NUM_FACES: + if(op == VF_GET) + *(int *)ptr = rp->num_faces; + break; + case RMSV_I_FACE_TEXTURE_ID: + if(op == VF_SET) + rp->faces[index].tmap = *(int *)ptr; + else if(op == VF_GET) + *(int *)ptr = rp->faces[index].tmap; + break; + case RMSV_I_FACE_FLAGS: + if(op == VF_GET) + *(int *)ptr = rp->faces[index].flags; + else if(op == VF_SET) + rp->faces[index].flags = *(int *)ptr; + else if(op == VF_SET_FLAGS) + rp->faces[index].flags |= *(int *)ptr; + else if(op == VF_CLEAR_FLAGS) + rp->faces[index].flags &= ~(*(int *)ptr); + break; + case RMSV_V_FACE_NORMAL: + if(op == VF_GET) + *(vector *)ptr = rp->faces[index].normal; + break; + case RMSV_V_FACE_CENTER_PNT: + if(op == VF_GET) + { + int i; + + if(index < 0 || index >= rp->num_faces || rp->faces[index].num_verts <= 0) + { + *(vector *)ptr = rp->path_pnt; + } + else + { + *(vector *)ptr = Zero_vector; + + for(i = 0; i < rp->faces[index].num_verts; i++) + { + *(vector *)ptr += rp->verts[rp->faces[index].face_verts[i]]; + } + + *(vector *)ptr /= rp->faces[index].num_verts; + } + } + break; + case RMV_I_NUM_PORTALS: + if(op == VF_GET) + *(int *)ptr = rp->num_portals; + break; + case RMSV_V_PORTAL_PATH_PNT: + if(op == VF_SET) + rp->portals[index].path_pnt = *(vector *)ptr; // chrishack -- make sure save game accounts for this + else if(op == VF_GET) + *(vector *)ptr = rp->portals[index].path_pnt; + break; + case RMSV_I_PORTAL_CONNECT_ROOM: + if(op == VF_GET) + *(int *)ptr = rp->portals[index].croom; + break; + case RMSV_I_PORTAL_CONNECT_PORTAL: + if(op == VF_GET) + *(int *)ptr = rp->portals[index].cportal; + break; + case RMSV_I_PORTAL_FACE: + if(op == VF_GET) + *(int *)ptr = rp->portals[index].portal_face; + break; + case RMV_F_DAMAGE: + if(op == VF_GET) + *(float *)ptr = rp->damage; + break; + } +} + +void osipf_PlayerValue(int obj_handle, char op, char vhandle, void *ptr, int index) +{ + object *objp; + int id; + bool extract_info = true; + + if(vhandle==PLYV_I_STRIP_WEAPONS) + { + // check to see if ptr is a -1, if so, don't extract info + if((*(int *)ptr)==-1) + extract_info = false; + } + + if(extract_info) + { + objp = ObjGet(obj_handle); + + if (!objp || (!(objp->type == OBJ_PLAYER || objp->type==OBJ_GHOST || objp->type==OBJ_OBSERVER)) ) + { + mprintf((0,"Player Value: Illegal Object Passed\n")); + return; + } + + id = objp->id; + } + + switch(vhandle) + { + case PLYV_I_FLAGS: + { + if(op == VF_GET) + { + *(int *)ptr = Players[id].flags; + } + } + break; + case PLYV_F_ENERGY: + if(op == VF_GET) + *(float *)ptr = Players[id].energy; + else if(op == VF_SET) + Players[id].energy = *(float *)ptr; + break; + case PLYV_F_MOVEMENT_SCALAR: + if(op == VF_GET) + *(float *)ptr = Players[id].movement_scalar; + else if(op == VF_SET) + Players[id].movement_scalar = *(float *)ptr; + break; + case PLYV_F_RECHARGE_SCALAR: + if(op == VF_GET) + *(float *)ptr = Players[id].weapon_recharge_scalar; + else if(op == VF_SET) + Players[id].weapon_recharge_scalar = *(float *)ptr; + break; + case PLYV_F_WSPEED_SCALAR: + if(op == VF_GET) + *(float *)ptr = Players[id].weapon_speed_scalar; + else if(op == VF_SET) + Players[id].weapon_speed_scalar = *(float *)ptr; + break; + case PLYV_F_DAMAGE_SCALAR: + if(op == VF_GET) + *(float *)ptr = Players[id].damage_scalar; + else if(op == VF_SET) + Players[id].damage_scalar = *(float *)ptr; + break; + case PLYV_F_ARMOR_SCALAR: + if(op == VF_GET) + *(float *)ptr = Players[id].armor_scalar; + else if(op == VF_SET) + Players[id].armor_scalar = *(float *)ptr; + break; + case PLYV_I_BUDDY_HANDLE: + if(op == VF_GET) + *(int *)ptr = Buddy_handle[id]; + break; + case PLYSV_I_WEAPON: + if(op == VF_GET) + { + msafe_struct mstruct; + mstruct.objhandle = obj_handle; + mstruct.index = index; + msafe_GetValue (MSAFE_WEAPON_CHECK,&mstruct); + + if(!mstruct.state) + { + *(int *)ptr = 0; + }else + { + if (index >= SECONDARY_INDEX) + *(int *)ptr = mstruct.count; + else + *(int *)ptr = 1; + } + } + else if(op == VF_SET) + { + int amount = *(int *)ptr; + msafe_struct mstruct; + mstruct.objhandle = obj_handle; + mstruct.index = index; + mstruct.state = (index >= SECONDARY_INDEX)?0:1; + mstruct.count = amount; + msafe_CallFunction (MSAFE_WEAPON_ADD,&mstruct); + }break; + + case PLYV_CS_GUIDEBOTNAME: + { + if(op == VF_GET) + { + char *dest = (char *)ptr; + ASSERT(dest); + msafe_struct mstruct; + mstruct.objhandle = obj_handle; + msafe_GetValue(MSAFE_MISC_GUIDEBOT_NAME,&mstruct); + strcpy(dest,mstruct.name); + }else + { + char *src = (char *)ptr; + ASSERT(src); + msafe_struct mstruct; + mstruct.objhandle = obj_handle; + strcpy(mstruct.name,src); + msafe_CallFunction(MSAFE_MISC_GUIDEBOT_NAME,&mstruct); + } + + }break; + + case PLYSV_US_WEAPON_POWERUP_ID: + { + if(op == VF_GET) + *(unsigned short *)ptr = Ships[Players[id].ship_index].spew_powerup[index]; + } + break; + case PLYSV_I_WEAPON_AMMO: + if(op == VF_GET) + if(index >= 0 && index < 10) + { + *(int *)ptr = Players[id].weapon_ammo[index]; + } + break; + case PLYV_B_THIEF_PLAYERHASITEM: + //note this is multiplayer friendly + if(op == VF_GET) + { + bool ret = ThiefPlayerHasItem(obj_handle,index); + *((bool *)ptr) = ret; + }else + { + Int3(); + } + break; + case PLYV_I_THIEF_STEALPLAYERITEM: + //note this is multiplayer friendly + if(op == VF_SET) + { + ThiefStealItem(obj_handle,index); + }else + { + Int3(); + } + break; + case PLYV_I_STRIP_WEAPONS: + if(op==VF_SET) + { + if((*(int *)ptr)==-1) + { + MultiSendStripPlayer(-1); + }else + { + MultiSendStripPlayer(id); + } + } + break; +// case PLYSV_F_ACCESSORY: +// if(op == VF_GET) +// *(float *)ptr = Players[id] +// else if(op == VF_SET) +// break; + } +} + +extern void AIUpdateAnim(object *obj); + +void osipf_ObjectCustomAnim(int handle, float start, float end, float time, char flags, int sound_handle, char next_anim_type) +{ + object *objp = ObjGet(handle); + + if(!objp) + { + mprintf((0,"AIValue: Illegal Object Passed\n")); + return; + } + + objp->rtype.pobj_info.anim_flags |= AIAF_NOTIFY; + + objp->rtype.pobj_info.custom_anim_info.anim_start_frame=start; + objp->rtype.pobj_info.custom_anim_info.anim_end_frame=end; + objp->rtype.pobj_info.custom_anim_info.anim_time=time; + objp->rtype.pobj_info.custom_anim_info.flags=flags; + objp->rtype.pobj_info.custom_anim_info.anim_sound_index = sound_handle; + objp->rtype.pobj_info.custom_anim_info.next_anim_type = next_anim_type; + + if (objp->ai_info) + objp->ai_info->next_animation_type = AS_CUSTOM; + else + mprintf((0,"ERROR: Changing animation for non-AI object.\n")); + + if(flags & AIAF_IMMEDIATE) + { + AIUpdateAnim(objp); + } +} + + +//searches for an object id given it's name +int osipf_ObjectFindID(char *name) +{ + return FindObjectIDName(IGNORE_TABLE(name)); +} + +//searches for an object id given it's name +int osipf_ObjectFindType(char *name) +{ + int id = FindObjectIDName(IGNORE_TABLE(name)); + + if(id >= 0) + { + return Object_info[id].type; + } + + return OBJ_NONE; +} + +//searches through the weapons for a name and returns the id +int osipf_WeaponFindID(char *name) +{ + return FindWeaponName(IGNORE_TABLE(name)); +} + +//returns how long an object has lived +float osipf_ObjectGetTimeLived(int objhandle) +{ + object *objp = ObjGet(objhandle); + + if(!objp) + { + mprintf((0,"Illegal object passed to ObjectGetTimeLived\n")); + return 0; + } + + return (Gametime - objp->creation_time); +} + +void osipf_AIValue(int objhandle, char op, char vtype, void *ptr) +{ + object *objp = ObjGet(objhandle); + + if(!objp) + { + mprintf((0,"AIValue: Illegal Object Passed\n")); + return; + } + + if((objp->control_type != CT_AI) && (objp->control_type != CT_DYING_AND_AI)) + { + mprintf((0,"AIValue: Illegal Object CT Passed\n")); + return; + } + + ai_frame *ai_info = objp->ai_info; + + switch(vtype) + { + case AIV_F_MAX_SPEED: + if(op == VF_SET) + { + ai_info->max_velocity = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->max_velocity; + } + break; + case AIV_F_MAX_DELTA_SPEED: + if(op == VF_SET) + { + ai_info->max_delta_velocity = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->max_delta_velocity; + } + break; + case AIV_F_MAX_TURN_RATE: + if(op == VF_SET) + { + ai_info->max_turn_rate = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->max_turn_rate; + } + break; + case AIV_F_MAX_DELTA_TURN_RATE: + if(op == VF_SET) + { + ai_info->max_delta_turn_rate = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->max_delta_turn_rate; + } + break; + case AIV_F_ATTACK_VEL_PERCENT: + if(op == VF_SET) + { + ai_info->attack_vel_percent = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->attack_vel_percent; + } + break; + case AIV_F_FLEE_VEL_PERCENT: + if(op == VF_SET) + { + ai_info->flee_vel_percent = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->flee_vel_percent; + } + break; + case AIV_F_DODGE_VEL_PERCENT: + if(op == VF_SET) + { + ai_info->dodge_vel_percent = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->dodge_vel_percent; + } + break; + case AIV_F_CIRCLE_DIST: + if(op == VF_SET) + { + ai_info->circle_distance = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->circle_distance; + } + break; + case AIV_F_DODGE_PERCENT: + if(op == VF_SET) + { + ai_info->dodge_percent = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->dodge_percent; + } + break; + case AIV_F_MELEE_DAMAGE1: + if(op == VF_SET) + { + ai_info->melee_damage[0] = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->melee_damage[0]; + } + break; + case AIV_F_MELEE_DAMAGE2: + if(op == VF_SET) + { + ai_info->melee_damage[1] = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->melee_damage[1]; + } + break; + case AIV_F_MELEE_LATENCY1: + if(op == VF_SET) + { + ai_info->melee_latency[0] = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->melee_latency[0]; + } + break; + case AIV_F_MELEE_LATENCY2: + if(op == VF_SET) + { + ai_info->melee_latency[1] = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->melee_latency[1]; + } + break; + case AIV_C_MOVEMENT_TYPE: + if(op == VF_SET) + { + ai_info->movement_type = *((char *)ptr); + } + else + { + *((char *)ptr) = ai_info->movement_type; + } + break; + case AIV_C_MOVEMENT_SUBTYPE: + if(op == VF_SET) + { + ai_info->movement_subtype = *((char *)ptr); + } + else + { + *((char *)ptr) = ai_info->movement_subtype; + } + break; + case AIV_C_ANIMATION_TYPE: + if(op == VF_SET) + { + ai_info->animation_type = *((char *)ptr); + } + else + { + *((char *)ptr) = ai_info->animation_type; + } + break; + case AIV_C_NEXT_ANIMATION_TYPE: + if(op == VF_SET) + { + ai_info->next_animation_type = *((char *)ptr); + } + else + { + *((char *)ptr) = ai_info->next_animation_type; + } + break; + case AIV_C_NEXT_MOVEMENT: + if(op == VF_SET) + { + ai_info->next_movement = *((char *)ptr); + } + else + { + *((char *)ptr) = ai_info->next_movement; + } + break; + case AIV_C_CURRENT_WB_FIRING: + if(op == VF_SET) + { + ai_info->current_wb_firing = *((char *)ptr); + } + else + { + *((char *)ptr) = ai_info->current_wb_firing; + } + break; + case AIV_I_TARGET_HANDLE: + if(op == VF_SET) + { + AISetTarget(objp, *((int *)ptr)); + } + else + { + *((int *)ptr) = ai_info->target_handle; + } + break; + case AIV_F_NEXT_TARGET_UPDATE_TIME: + if(op == VF_SET) + { + ai_info->next_target_update_time = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->next_target_update_time; + } + break; + case AIV_F_DIST_TO_TARGET: + if(op == VF_SET) + { + ai_info->dist_to_target_actual = ai_info->dist_to_target_perceived = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->dist_to_target_perceived; + } + break; + case AIV_V_VEC_TO_TARGET: + if(op == VF_SET) + { + ai_info->vec_to_target_actual = ai_info->vec_to_target_perceived = *((vector *)ptr); + } + else + { + *((vector *)ptr) = ai_info->vec_to_target_perceived; + } + break; + case AIV_F_NEXT_CHECK_SEE_TARGET_TIME: + if(op == VF_SET) + { + ai_info->next_check_see_target_time = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->next_check_see_target_time; + } + break; + case AIV_V_LAST_SEE_TARGET_POS: + if(op == VF_SET) + { + ai_info->last_see_target_pos = *((vector *)ptr); + } + else + { + *((vector *)ptr) = ai_info->last_see_target_pos; + } + break; + case AIV_F_LAST_SEE_TARGET_TIME: + if(op == VF_SET) + { + ai_info->last_see_target_time = *((float *)ptr); + if(ai_info->last_see_target_time == Gametime) + { + AINotify(objp, AIN_SEE_TARGET, ObjGet(ai_info->target_handle)); + } + } + else + { + *((float *)ptr) = ai_info->last_see_target_time; + } + break; + case AIV_F_LAST_HEAR_TARGET_TIME: + if(op == VF_SET) + { + ai_info->last_hear_target_time = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->last_hear_target_time; + } + break; + case AIV_F_WEAPON_SPEED: + if(op == VF_SET) + { + ai_info->weapon_speed = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->weapon_speed; + } + break; + case AIV_F_NEXT_MELEE_TIME: + if(op == VF_SET) + { + ai_info->next_melee_time = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->next_melee_time; + } + break; + case AIV_F_LAST_RENDER_TIME: + if(op == VF_SET) + { + ai_info->last_render_time = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->last_render_time; + } + break; + case AIV_F_NEXT_FLINCH_TIME: + if(op == VF_SET) + { + ai_info->next_flinch_time = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->next_flinch_time; + } + break; + case AIV_V_MOVEMENT_DIR: + if(op == VF_SET) + { + ai_info->movement_dir = *((vector *)ptr); + } + else + { + *((vector *)ptr) = ai_info->movement_dir; + } + break; + case AIV_V_ROT_THRUST_VECTOR: + if(op == VF_SET) + { + ai_info->rot_thrust_vector = *((vector *)ptr); + } + else + { + *((vector *)ptr) = ai_info->rot_thrust_vector; + } + break; + case AIV_F_FOV: + if(op == VF_SET) + { + ai_info->fov = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->fov; + } + break; + case AIV_F_AVOID_FRIENDS_DIST: + if(op == VF_SET) + { + ai_info->avoid_friends_distance = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->avoid_friends_distance; + } + break; + case AIV_F_FRUSTRATION: + if(op == VF_SET) + { + ai_info->frustration = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->frustration; + } + break; + case AIV_F_CURIOUSITY: + if(op == VF_SET) + { + ai_info->curiousity = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->curiousity; + } + break; + case AIV_F_FIRE_SPREAD: + if(op == VF_SET) + { + ai_info->fire_spread = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->fire_spread; + } + break; + case AIV_F_AGRESSION: + if(op == VF_SET) + { + ai_info->agression = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->agression; + } + break; + case AIV_F_NIGHT_VISION: + if(op == VF_SET) + { + ai_info->night_vision = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->night_vision; + } + break; + case AIV_F_FOG_VISION: + if(op == VF_SET) + { + ai_info->fog_vision = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->fog_vision; + } + break; + case AIV_F_LEAD_ACCURACY: + if(op == VF_SET) + { + ai_info->lead_accuracy = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->lead_accuracy; + } + break; + case AIV_F_LEAD_VARIENCE: + if(op == VF_SET) + { + ai_info->lead_varience = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->lead_varience; + } + break; + case AIV_F_FIGHT_TEAM: + if(op == VF_SET) + { + ai_info->fight_team = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->fight_team; + } + break; + case AIV_F_FIGHT_SAME: + if(op == VF_SET) + { + ai_info->fight_same = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->fight_same; + } + break; + case AIV_F_HEARING: + if(op == VF_SET) + { + ai_info->hearing = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->hearing; + } + break; + case AIV_F_ROAMING: + if(op == VF_SET) + { + ai_info->roaming = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->roaming; + } + break; + case AIV_F_LIFE_PRESERVATION: + if(op == VF_SET) + { + ai_info->life_preservation = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->life_preservation; + } + break; + case AIV_F_BIASED_FLIGHT_IMPORTANCE: + if(op == VF_SET) + { + ai_info->biased_flight_importance = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->biased_flight_importance; + } + break; + case AIV_F_BIASED_FLIGHT_MIN: + if(op == VF_SET) + { + ai_info->biased_flight_min = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->biased_flight_min; + } + break; + case AIV_F_BIASED_FLIGHT_MAX: + if(op == VF_SET) + { + ai_info->biased_flight_max = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->biased_flight_max; + } + break; + case AIV_F_AWARENESS: + if(op == VF_SET) + { + ai_info->awareness = *((float *)ptr); + } + else + { + *((float *)ptr) = ai_info->awareness; + } + break; + case AIV_I_FLAGS: + if(op == VF_GET) + { + *((int *)ptr) = ai_info->flags; + } + else if(op == VF_SET) + { + ai_info->flags = *((int *)ptr); + } + else if(op == VF_SET_FLAGS) + { + ai_info->flags |= *((int *)ptr); + } + else if(op == VF_CLEAR_FLAGS) + { + ai_info->flags &= ~(*((int *)ptr)); + } + break; + case AIV_I_STATUS_REG: + if(op == VF_GET) + { + *((int *)ptr) = ai_info->status_reg; + } + else if(op == VF_SET) + { + ai_info->status_reg = *((int *)ptr); + } + else if(op == VF_SET_FLAGS) + { + ai_info->status_reg |= *((int *)ptr); + } + else if(op == VF_CLEAR_FLAGS) + { + ai_info->status_reg &= ~(*((int *)ptr)); + } + break; + } +} + +void osipf_ObjectValue(int handle, char op, char var_handle, void *ptr, int index) +{ + object *obj = ObjGet(handle); + if(!obj) + { + if(op == VF_GET && var_handle == OBJV_I_TYPE) + *(int *)ptr = (int)OBJ_NONE; + + return; + } + + msafe_struct m; + m.objhandle = obj->handle; + + switch(var_handle) + { + case OBJV_PI_HACK_FVI_IGNORE_LIST: + { + if(op == VF_SET) + { + if(ptr != NULL) + { + int count = 0; + + do + { + hack_list[count] = ((int *)ptr)[count]; + count++; + } + while(hack_list[count - 1] != -1); + + hack_ilist = hack_list; + } + else + { + hack_ilist = NULL; + } + } + } + break; + case OBJV_F_ROTDRAG: + if(op == VF_SET) + { + m.rot_drag = *(float *)ptr; + msafe_CallFunction(MSAFE_OBJECT_ROTDRAG, &m); + } + else if(op == VF_GET) + *(float *)ptr = obj->mtype.phys_info.rotdrag; + break; + case OBJV_F_SHIELDS: + if(op == VF_SET) + { + m.shields = *(float *)ptr; + msafe_CallFunction(MSAFE_OBJECT_SHIELDS, &m); + } + else if(op == VF_GET) + *(float *)ptr = obj->shields; + break; + case OBJV_I_TYPE: + if(op == VF_SET) + { + m.type = *(int *)ptr; + msafe_CallFunction(MSAFE_OBJECT_TYPE, &m); + } + else if(op == VF_GET) + (*(int *)ptr) = obj->type; + break; + case OBJV_US_ID: + if(op == VF_SET) + { + m.id = *(ushort *)ptr; + msafe_CallFunction(MSAFE_OBJECT_ID, &m); + } + else if(op == VF_GET) + (*(ushort *)ptr) = obj->id; + break; + case OBJV_V_POS: + if(op == VF_SET) + { + ObjSetPos(obj, (vector *)ptr, obj->roomnum, NULL, true); + obj->flags |= OF_MOVED_THIS_FRAME; + obj->flags &= ~OF_STOPPED_THIS_FRAME; + } + else if(op == VF_GET) + *(vector *)ptr = obj->pos; + break; + case OBJV_M_ORIENT: + if(op == VF_SET) + { + ObjSetPos(obj, &obj->pos, obj->roomnum, (matrix *)ptr, true); + obj->flags |= OF_MOVED_THIS_FRAME; + obj->flags &= ~OF_STOPPED_THIS_FRAME; + } + else if(op == VF_GET) + *(matrix *)ptr = obj->orient; + break; + case OBJV_I_ROOMNUM: + if(op == VF_SET) + { + ObjSetPos(obj, &obj->pos, *(int *)ptr, NULL, false); + obj->flags |= OF_MOVED_THIS_FRAME; + obj->flags &= ~OF_STOPPED_THIS_FRAME; + } + else if(op == VF_GET) + *(int *)ptr = obj->roomnum; + break; + case OBJV_V_VELOCITY: + if(op == VF_SET) + obj->mtype.phys_info.velocity = *(vector *)ptr; + else if(op == VF_GET) + *(vector *)ptr = obj->mtype.phys_info.velocity; + break; + case OBJV_V_ROTVELOCITY: + if(op == VF_SET) + obj->mtype.phys_info.rotvel = *(vector *)ptr; + else if(op == VF_GET) + *(vector *)ptr = obj->mtype.phys_info.rotvel; + break; + break; + case OBJV_V_THRUST: + if(op == VF_SET) + obj->mtype.phys_info.thrust = *(vector *)ptr; + else if(op == VF_GET) + *(vector *)ptr = obj->mtype.phys_info.thrust; + break; + break; + case OBJV_V_ROTTHRUST: + if(op == VF_SET) + obj->mtype.phys_info.rotthrust = *(vector *)ptr; + else if(op == VF_GET) + (*(vector *)ptr) = obj->mtype.phys_info.rotthrust; + break; + break; + case OBJV_I_FLAGS: + { + int result = obj->flags; + + if(op == VF_GET) + (*(int *)ptr) = obj->flags; + else if(op == VF_SET) + result = (*(int *)ptr); + else if(op == VF_SET_FLAGS) + result |= (*(int *)ptr); + else if(op == VF_CLEAR_FLAGS) + result &= ~(*(int *)ptr); + + if(op != VF_GET) + { + m.flags = result; + msafe_CallFunction(MSAFE_OBJECT_FLAGS, &m); + } + } + break; + case OBJV_F_SIZE: + if(op == VF_GET) + *(float *)ptr = obj->size; + break; + case OBJV_S_NAME: + if (op == VF_GET) { + if (obj->name) { + strcpy((char *)ptr, obj->name); + } + else { + *((char *)ptr) = 0; + } + } + break; + case OBJV_C_CONTROL_TYPE: + if(op == VF_SET) + { + m.control_type = *(char *)ptr; + msafe_CallFunction(MSAFE_OBJECT_CONTROL_TYPE, &m); + } + else if(op == VF_GET) + *(char *)ptr = obj->control_type; + break; + case OBJV_C_MOVEMENT_TYPE: + if(op == VF_SET) + { + m.movement_type = *(char *)ptr; + msafe_CallFunction(MSAFE_OBJECT_MOVEMENT_TYPE, &m); + } + else if(op == VF_GET) + *(char *)ptr = obj->movement_type; + break; + case OBJV_F_CREATION_TIME: + if(op == VF_SET) + { + m.creation_time = *(float *)ptr; + msafe_CallFunction(MSAFE_OBJECT_CREATION_TIME, &m); + } + else if(op == VF_GET) + *(float *)ptr = obj->creation_time; + break; + case OBJV_I_PHYSICS_FLAGS: + { + int result = obj->mtype.phys_info.flags; + + if(op == VF_GET) + (*(int *)ptr) = obj->mtype.phys_info.flags; + else if(op == VF_SET) + result = (*(int *)ptr); + else if(op == VF_SET_FLAGS) + result |= (*(int *)ptr); + else if(op == VF_CLEAR_FLAGS) + result &= ~(*(int *)ptr); + + if(op != VF_GET) + { + m.physics_flags = result; + msafe_CallFunction(MSAFE_OBJECT_PHYSICS_FLAGS, &m); + } + } + break; + case OBJV_I_PARENT_HANDLE: + if(op == VF_SET) + { + m.ithandle = *(int *)ptr; + msafe_CallFunction(MSAFE_OBJECT_PARENT, &m); + } + else if(op == VF_GET) + (*(int *)ptr) = obj->parent_handle; + break; + case OBJV_F_ANIM_FRAME: + if(op == VF_GET) + *(float *)ptr = obj->rtype.pobj_info.anim_frame; + break; + case OBJV_F_MAX_SHIELDS: + if(op == VF_GET) + { + if(obj->type == OBJ_PLAYER || obj->type == OBJ_GHOST || obj->type == OBJ_OBSERVER) + *(float *)ptr = 200.0f; + else //chrishack -- make sure it is a generic object + *(float *)ptr = Object_info[obj->id].hit_points; + } + break; + case OBJV_PC_MARKER_MESSAGE: + if(op == VF_GET) + { + if(obj->type == OBJ_MARKER) + { + strcpy((char *)ptr, MarkerMessages[obj->id]); + } + else + { + ((char *)ptr)[0] = '\0'; + } + } + break; + case OBJSV_F_ANIM_START: + if(obj->control_type != CT_AI) + return; + + if(op == VF_GET) + (*(float *)ptr) = Object_info[obj->id].anim[obj->ai_info->movement_type].elem[index].from; + else if(op == VF_SET) + Object_info[obj->id].anim[obj->ai_info->movement_type].elem[index].from = (*(float *)ptr); + break; + case OBJSV_F_ANIM_END: + if(obj->control_type != CT_AI) + return; + + if(op == VF_GET) + (*(float *)ptr) = Object_info[obj->id].anim[obj->ai_info->movement_type].elem[index].to; + else if(op == VF_SET) + Object_info[obj->id].anim[obj->ai_info->movement_type].elem[index].to = (*(float *)ptr); + break; + case OBJSV_F_ANIM_TIME: + if(obj->control_type != CT_AI) + return; + + if(op == VF_GET) + (*(float *)ptr) = Object_info[obj->id].anim[obj->ai_info->movement_type].elem[index].spc; + else if(op == VF_SET) + Object_info[obj->id].anim[obj->ai_info->movement_type].elem[index].spc = (*(float *)ptr); + break; + + case OBJV_C_VIRUS_INFECTED: + { + // the object show signs of virus infection + if(!obj->effect_info) + return; + + if(op == VF_GET) + { + (*(char *)ptr) = (obj->effect_info->type_flags&EF_VIRUS_INFECTED)?1:0; + }else if(op==VF_SET) + { + bool enable = (*(char *)ptr)?true:false; + + if(enable) + { + obj->effect_info->type_flags |= EF_VIRUS_INFECTED; + }else + { + obj->effect_info->type_flags &= ~EF_VIRUS_INFECTED; + } + } + }break; + + case OBJV_C_IS_CLOAKED: + if (op == VF_GET) { + if (!obj->effect_info) { + (*(char *)ptr) = 0; + } + else { + (*(char *)ptr) = (obj->effect_info->type_flags & EF_CLOAKED) ? 1 : 0; + } + } + break; + + case OBJV_C_NEGATIVE_LIGHT: + { + // the object casts negative light + if(!obj->effect_info) + return; + + if(op == VF_GET) + { + (*(char *)ptr) = (obj->effect_info->type_flags&EF_NEGATIVE_LIGHT)?1:0; + }else if(op==VF_SET) + { + bool enable = (*(char *)ptr)?true:false; + + if(enable) + { + obj->effect_info->type_flags |= EF_NEGATIVE_LIGHT; + }else + { + obj->effect_info->type_flags &= ~EF_NEGATIVE_LIGHT; + } + } + + }break; + } +} + +ubyte osipf_AITurnTowardsVectors(int objhandle,vector *fvec,vector *uvec) +{ + object *objp = ObjGet(objhandle); + + if(!objp) + { + mprintf((0,"AITurnTowardsVectors: Illegal Object Passed\n")); + return 0; + } + + if(objp->control_type != CT_AI) + { + mprintf((0,"AITurnTowardsVectors: Illegal Object CT Passed\n")); + return 0; + } + + matrix g_orient; + vm_VectorToMatrix(&g_orient, fvec, uvec, NULL); + + return AITurnTowardsMatrix(objp, objp->ai_info->max_turn_rate, &g_orient); +} + +void osipf_AISetType(int objhandle,int type) +{ + object *objp = ObjGet(objhandle); + + if(!objp) + { + mprintf((0,"AISetType: Illegal Object Passed\n")); + return; + } + + if(objp->control_type != CT_AI) + { + mprintf((0,"AISetType: Illegal Object CT Passed\n")); + return; + } + + if(type < 0 || type >= MAX_AI_INIT_TYPES) + return; + + GoalInitTypeGoals(objp, type); +} + +vector osipf_AIFindHidePos(int hideobjhandle,int viewobjhandle,float time,int *hide_room) +{ + object *hide_obj = ObjGet(hideobjhandle); + object *view_obj = ObjGet(viewobjhandle); + + vector hpos; + int hroom; + + if(hide_obj==NULL) + { + mprintf((0,"Illegal Hide Object Passed To AIFindHidePos\n")); + *hide_room = -1; + return Zero_vector; + } + + if(hide_obj->control_type != CT_AI) + { + mprintf((0,"Illegal Object CT Passed To AIFindHidePos\n")); + *hide_room = -1; + return Zero_vector; + } + + if(view_obj==NULL) + { + mprintf((0,"Illegal View Object Passed To AIFindHidePos\n")); + *hide_room = hide_obj->roomnum; + return hide_obj->pos; + } + + if(time <= 0.0) + { + mprintf((0,"AIFindHidePos: Illegal Time Passed bashing to 1sec\n")); + time = 1.0f; + } + else if(time > 15.0) + { + mprintf((0,"AIFindHidePos: Illegal Time Passed bashing to 15sec\n")); + time = 15.0f; + } + + // Returns the hide object's pos/room if it cannot find a valid hide position + AIFindHidePos(hide_obj, view_obj, &hpos, &hroom, time); + + *hide_room = hroom; + return hpos; +} + +int osipf_AIGoalAddEnabler(int objhandle,int goal_index,int enabler_type,float percent,float interval, void *ptr) +{ + object *obj; + + obj = ObjGet(objhandle); + + if(obj == NULL || obj->ai_info == NULL){ + mprintf((0,"Illegal object passed to AIGoalAddEnabler\n")); + return -1; + } + + if(!ptr) + return -1; + + return GoalAddEnabler(obj, goal_index, enabler_type, ptr, percent, interval); +} + +int osipf_AIGoalAdd(int objhandle,int goal_type,int level,float influence,int guid,int flags, ... ) +{ + object *obj; + goal_info g_info; + va_list marker; + + obj = ObjGet(objhandle); + if(obj == NULL || obj->ai_info == NULL){ + mprintf((0,"Illegal object passed to AIGoalAdd\n")); + return -1; + } + + switch(goal_type) + { + case AIG_GET_AWAY_FROM_OBJ: + case AIG_GET_TO_OBJ: + case AIG_GUARD_OBJ: + case AIG_DODGE_OBJ: + case AIG_MOVE_AROUND_OBJ: + case AIG_MOVE_RELATIVE_OBJ: + case AIG_GET_AROUND_OBJ: + { + // Pop Goal Local Variables + va_start(marker,flags); + int objref = va_arg(marker,int); + va_end(marker); + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&objref, level, influence, flags, guid); + } + break; + + case AIG_FOLLOW_PATH: + { + path_information path_info; + + va_start(marker,flags); + path_info.path_id = va_arg(marker, int); + path_info.start_node = va_arg(marker, int)-1; + path_info.end_node = va_arg(marker, int)-1; + path_info.next_node = va_arg(marker, int)-1; + va_end(marker); + + if(path_info.path_id >= 0 && path_info.end_node < 0) + { + path_info.end_node = GamePaths[path_info.path_id].num_nodes - 1; + } + + return GoalAddGoal(obj, AIG_FOLLOW_PATH, (void *)&path_info, level, influence, flags, guid); + } + break; + + case AIG_ATTACH_TO_OBJ: + case AIG_PLACE_OBJ_ON_OBJ: + { + goal_info g_info; + g_info.attach_info.flags = 0; + + // Pop Goal Local Variables + va_start(marker,flags); + g_info.handle = va_arg(marker,int); + g_info.attach_info.parent_ap = va_arg(marker, int); + g_info.attach_info.child_ap = va_arg(marker, int); + g_info.attach_info.rad = va_arg(marker, double); + + object *objp = ObjGet(g_info.handle); + if (objp && (objp->type == OBJ_POWERUP)) + g_info.attach_info.rad /= 2; + + if(va_arg(marker, int) != 0) + g_info.attach_info.flags = GAF_ALIGNED; + if(va_arg(marker, int) != 0) + g_info.attach_info.flags |= GAF_SPHERE; + va_end(marker); + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&g_info, level, influence, flags, guid); + } + break; + + case AIG_FIRE_AT_OBJ: + { + // Pop Goal Local Variables + va_start(marker,flags); + int i = va_arg(marker,int); + va_end(marker); + + gi_fire attack_info; + + attack_info.cur_wb = i; + attack_info.cur_mask = obj->dynamic_wb[i].cur_firing_mask; + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&attack_info, level, influence, flags, guid); + } + break; + + case AIG_MOVE_RELATIVE_OBJ_VEC: + { + va_start(marker,flags); + // Pop Goal Local Variables + int objref = va_arg(marker,int); + int i_value = va_arg(marker,int); + va_end(marker); + + g_info.handle = objref; + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&g_info, level, influence, flags, guid, i_value); + } + break; + + case AIG_HIDE_FROM_OBJ: + { + // Pop Goal Local Variables + va_start(marker,flags); + int objref = va_arg(marker,int); + int f_value = va_arg(marker,int); + va_end(marker); + + g_info.handle = objref; + g_info.time = f_value; + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&g_info, level, influence, flags, guid); + } + break; + + case AIG_GUARD_AREA: + case AIG_GET_TO_POS: + { + // Pop Goal Local Variables + va_start(marker,flags); + vector *pos = va_arg(marker,vector *); + int roomnum = va_arg(marker,int); + va_end(marker); + + g_info.pos = (*pos); + g_info.roomnum = roomnum; + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&g_info, level, influence, flags, guid); + } + break; + + case AIG_MELEE_TARGET: + { + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)NULL, level, influence, flags, guid); + } + break; + + case AIG_SET_ANIM: // Animation index + case AIG_DO_MELEE_ANIM: // Which melee attack + case AIG_USE_MOVEMENT_TYPE: // MT_INDEX + case AIG_SCRIPTED: + { + // Pop Goal Local Variables + va_start(marker,flags); + int i_value = va_arg(marker,int); + va_end(marker); + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&i_value, level, influence, flags, guid); + } + break; + + case AIG_WANDER_AROUND: // Initial room + { + // Pop Goal Local Variables + va_start(marker,flags); + int i_value = va_arg(marker,int); + int j_value = va_arg(marker,int); + va_end(marker); + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)&i_value, level, influence, flags, guid); + } + break; + + case AIG_FACE_DIR: // MT_INDEX + { + // Pop Goal Local Variables + va_start(marker,flags); + vector *v_value = va_arg(marker,vector *); + va_end(marker); + + // Do the actual goal and return; + return GoalAddGoal(obj, goal_type, (void *)v_value, level, influence, flags, guid); + } + break; + + default: + mprintf((0, "AIGoalAdd Error: Get chris - goal %d is not available of scripting\n", goal_type)); + } + + return -1; +} + +void osipf_AIGoalClear(int objhandle,int goal_index) +{ + object *obj = ObjGet(objhandle); + + if(obj==NULL) + { + mprintf((0, "Invalid Object Passed To AIGoalClear\n")); + return; + } + + if(obj->control_type != CT_AI) + { + mprintf((0,"AIGoalClear: Illegal Object CT Passed\n")); + return; + } + + if(goal_index < 0) + { + GoalClearAll(obj); + } + else if(goal_index < MAX_GOALS) + { + GoalClearGoal(obj, &obj->ai_info->goals[goal_index]); + } +} + +int osipf_AIFindObjOfType(int objhandle, int type, int id, bool f_ignore_init_room, int parent_handle) +{ + object *obj = ObjGet(objhandle); + object *f_obj; + + if(obj==NULL) + { + mprintf((0,"Illegal Object Passed To AIFindObjOfType\n")); + return OBJECT_HANDLE_NONE; + } + + if(f_obj = AIFindObjOfType(obj, type, id, f_ignore_init_room, parent_handle)) + return f_obj->handle; + + return OBJECT_HANDLE_NONE; +} + +int osipf_ObjMakeListOfType(int objhandle, int type, int id, bool f_ignore_init_room, int parent_handle, int max_recorded, int *handles) +{ + object *obj = ObjGet(objhandle); + + int num_recorded = 0; + int i; + + if(obj==NULL) + { + mprintf((0,"Illegal Object Passed To Obj_MakeListOfType\n")); + return OBJECT_HANDLE_NONE; + } + + for(i = 0; i <= Highest_object_index; i++) + { + if(Objects[i].type == type) + { + if(Objects[i].type == OBJ_NONE) + continue; + + if(&Objects[i] == obj) + continue; + + if(f_ignore_init_room && Objects[i].roomnum == obj->roomnum) + continue; + + if(parent_handle != OBJECT_HANDLE_NONE && Objects[i].parent_handle != parent_handle) + continue; + + if(id != -1 && Objects[i].id != id) + continue; + + handles[num_recorded++] = Objects[i].handle; + + if(num_recorded >= max_recorded) + break; + } + } + + return num_recorded; +} + +vector osipf_AIGetRoomPathPoint(int roomnum) +{ + if(ROOMNUM_OUTSIDE(roomnum)) + { + int cell = CELLNUM(roomnum); + + if (cell >= TERRAIN_DEPTH * TERRAIN_WIDTH) + { + return Zero_vector; + } + else + { + vector pos; + + ComputeTerrainSegmentCenter (&pos,cell); + pos.y += 15.0f + ((float)ps_rand()/(float)RAND_MAX) * 20; // between 15 and 35 + + return pos; + } + } + else if(roomnum <= Highest_room_index && Rooms[roomnum].used) + { + return Rooms[roomnum].path_pnt; + } + + return Zero_vector; +} + +int osipf_AIFindEnergyCenter(int objhandle) +{ + object *obj = ObjGet(objhandle); + + if(obj==NULL) + { + mprintf((0,"Illegal Object Passed To AIFindEnergyCenter\n")); + return -1; + } + + // returns the roomnum of the closest room with that flag + return AIFindRoomWithFlag(obj, RF_FUELCEN); +} + +float osipf_AIGetDistToObj(int objhandle,int otherobjhandle) +{ + object *obj = ObjGet(objhandle); + object *fobj = ObjGet(otherobjhandle); + + float dist; + + if(obj==NULL) + { + mprintf((0,"Illegal Object Passed To AIGetDistToObj\n")); + return 0; + } + + if(fobj==NULL) + { + mprintf((0, "Illegal Find Object Passed To AIGetDistToObj\n")); + return 0; + } + + + BOA_ComputeMinDist(obj->roomnum, fobj->roomnum, 0.0f, &dist, NULL); + + return dist; +} + + +int osipf_AISetGoalFlags(int objhandle,int goal_handle,int flags,ubyte f_enable) +{ + object *obj = ObjGet(objhandle); + + if(obj==NULL) + { + mprintf((0, "Illegal Object Passed To AISetGoalFlags\n")); + return 0; + } + + if(obj->control_type != CT_AI) + { + mprintf((0, "Non-AI Object Passed To AISetGoalFlags\n")); + return 0; + } + + ai_frame *ai_info = obj->ai_info; + + if(goal_handle >= 0 && + goal_handle < MAX_GOALS && + ai_info->goals[goal_handle].used != 0) + { + if(f_enable) + ai_info->goals[goal_handle].flags |= flags; + else + ai_info->goals[goal_handle].flags &= ~flags; + } + + return (ai_info->goals[goal_handle].flags); +} + +void osipf_AISetGoalCircleDist(int objhandle,int goal_handle,float dist) +{ + object *obj = ObjGet(objhandle); + + if(obj==NULL) + { + mprintf((0, "Illegal Object Passed To AISetGoalCircleDist\n")); + return; + } + + if(obj->control_type != CT_AI) + { + mprintf((0, "Non-AI Object Passed To AISetGoalCircleDist\n")); + return; + } + + ai_frame *ai_info = obj->ai_info; + + if(goal_handle >= 0 && + goal_handle < MAX_GOALS && + ai_info->goals[goal_handle].used != 0) + { + ai_info->goals[goal_handle].circle_distance = dist; + } +} + +void osipf_GetGunPos(int objhandle,int gun_number,vector *gun_pnt,vector *gun_normal) +{ + object *obj = ObjGet(objhandle); + + if(obj==NULL) + { + mprintf((0, "Illegal Object Passed To AIGetGunPosition\n")); + *gun_pnt = Zero_vector; + *gun_normal = Zero_vector; + return; + } + + WeaponCalcGun(gun_pnt, gun_normal, obj, gun_number); +} + +void osipf_GetGroundPos(int objhandle,int ground_number,vector *ground_pnt,vector *ground_normal) +{ + object *obj = ObjGet(objhandle); + + if(obj==NULL) + { + mprintf((0, "Illegal Object Passed To Obj_GetGroundPos\n")); + *ground_pnt = Zero_vector; + *ground_normal = Zero_vector; + return; + } + + PhysCalcGround(ground_pnt, ground_normal, obj, ground_number); +} + +ubyte osipf_IsRoomValid(int roomnum) +{ + if(roomnum == -1.0) + { + return 0; + } + else if(ROOMNUM_OUTSIDE(roomnum)) + { + int cell = CELLNUM(roomnum); + + if((CELLNUM(cell) < 0) || (cell >= TERRAIN_WIDTH * TERRAIN_DEPTH)) + { + return 0; + } + else + { + return 1; + } + } + else + { + if(roomnum < 0 || roomnum > Highest_room_index || Rooms[roomnum].used == 0) + { + return 0; + } + else + { + return 2; + } + } + + return 0; +} + +int osipf_GetAttachParent(int childhandle) +{ + object *child = ObjGet(childhandle); + + if((child) && (child->flags & OF_ATTACHED)) + { + return (child->attach_parent_handle); + } + + return (OBJECT_HANDLE_NONE); +} + +int osipf_GetNumAttachSlots(int objhandle) +{ + object *parent = ObjGet(objhandle); + + if((parent) && (parent->flags & OF_POLYGON_OBJECT)) + { + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + return (parent_pm->n_attach); + } + + return 0; +} + +int osipf_GetAttachChildHandle(int objhandle,char attachpoint) +{ + object *parent= ObjGet(objhandle); + char parent_ap = attachpoint; + + if((parent) && (parent->flags & OF_POLYGON_OBJECT)) + { + poly_model *parent_pm = &Poly_models[parent->rtype.pobj_info.model_num]; + if(parent_ap >= 0 && parent_ap < parent_pm->n_attach) + { + return (parent->attach_children[parent_ap]); + } + } + + return (OBJECT_HANDLE_NONE); +} + +int osipf_AttachObjectAP(int parenthandle,char parent_ap,int childhandle,char child_ap,ubyte f_use_aligned) +{ + object *parent= ObjGet(parenthandle); + object *child = ObjGet(childhandle); + + if((parent) && (parent->flags & OF_POLYGON_OBJECT) && (child) && (child->flags & OF_POLYGON_OBJECT)) + { + return AttachObject(parent, parent_ap, child, child_ap, (bool)(f_use_aligned!=0)); + } + + return 0; +} + +int osipf_AttachObjectRad(int parenthandle,char parent_ap,int childhandle,float percent_rad) +{ + object *parent= ObjGet(parenthandle); + object *child = ObjGet(childhandle); + + if((parent) && (parent->flags & OF_POLYGON_OBJECT) && (child) && (child->flags & OF_POLYGON_OBJECT)) + { + return AttachObject(parent, parent_ap, child, percent_rad); + } + + return 0; +} + +void osipf_UnattachFromParent(int objhandle) +{ + object *child = ObjGet(objhandle); + + if((child) && (child->flags & OF_POLYGON_OBJECT)) + { + UnattachFromParent(child); + } +} + +void osipf_UnattachChild(int objhandle,char parent_ap) +{ + object *parent = ObjGet(objhandle); + + if((parent) && (parent->flags & OF_POLYGON_OBJECT)) + { + UnattachChild(parent, parent_ap); + } +} + +void osipf_UnattachChildren(int objhandle) +{ + object *parent = ObjGet(objhandle); + + if((parent) && (parent->flags & OF_POLYGON_OBJECT)) + { + UnattachChildren(parent); + } +} + +void osipf_MatcenValue(int matcen_id, char op, char var_handle, void *ptr, int index) +{ + if(matcen_id < 0 || matcen_id >= Num_matcens || !Matcen[matcen_id]) + return; + switch(var_handle) + { + case MTNV_C_ATTACH_TYPE: + if(op == VF_GET) + (*(char *)ptr) = Matcen[matcen_id]->GetAttachType(); + else if(op == VF_SET) + Matcen[matcen_id]->SetAttachType(*(char *)ptr); + break; + case MTNV_C_CONTROL_TYPE: + if(op == VF_GET) + (*(char *)ptr) = Matcen[matcen_id]->GetControlType(); + else if(op == VF_SET) + Matcen[matcen_id]->SetControlType(*(char *)ptr); + break; + case MTNV_I_ATTACH: + if(op == VF_GET) + (*(int *)ptr) = Matcen[matcen_id]->GetAttach(); + else if(op == VF_SET) + Matcen[matcen_id]->SetAttach(*(int *)ptr); + break; + case MTNV_V_CREATE_POINT: + if(op == VF_GET) + Matcen[matcen_id]->GetCreatePnt((vector *)ptr); + else if(op == VF_SET) + Matcen[matcen_id]->SetCreatePnt((vector *)ptr); + break; + case MTNV_I_CREATE_ROOM: + if(op == VF_GET) + (*(int *)ptr) = Matcen[matcen_id]->GetCreateRoom(); + else if(op == VF_SET) + Matcen[matcen_id]->SetCreateRoom(*(int *)ptr); + break; + case MTNV_PC_NAME: + if(op == VF_GET) + Matcen[matcen_id]->GetName((char *)ptr); + else if(op == VF_SET) + Matcen[matcen_id]->SetName((char *)ptr); + break; + case MTNV_I_MAX_PROD: + if(op == VF_GET) + (*(int *)ptr) = Matcen[matcen_id]->GetMaxProd(); + else if(op == VF_SET) + Matcen[matcen_id]->SetMaxProd(*(int *)ptr); + break; + case MTNV_F_PROD_MULTIPLIER: + if(op == VF_GET) + (*(float *)ptr) = Matcen[matcen_id]->GetProdMultiplier(); + else if(op == VF_SET) + Matcen[matcen_id]->SetProdMultiplier(*(float *)ptr); + break; + case MTNV_I_STATUS: + if(op == VF_GET) + (*(int *)ptr) = Matcen[matcen_id]->GetStatus(); + else if(op == VF_SET_FLAGS) + Matcen[matcen_id]->SetStatus(*(int *)ptr, true); + else if(op == VF_CLEAR_FLAGS) + Matcen[matcen_id]->SetStatus(*(int *)ptr, false); + else if(op == VF_SET) + { + int status = Matcen[matcen_id]->GetStatus(); + int diff_flags = (*(int *)ptr) ^ status; + int clear_flags = status & diff_flags; + int set_flags = (~(status)) & diff_flags; + + if(set_flags) + Matcen[matcen_id]->SetStatus(set_flags, true); + if(clear_flags) + Matcen[matcen_id]->SetStatus(clear_flags, false); + } + break; + case MTNV_C_CREATION_EFFECT: + if(op == VF_GET) + (*(char *)ptr) = Matcen[matcen_id]->GetCreationEffect(); + else if(op == VF_SET) + Matcen[matcen_id]->SetCreationEffect(*(char *)ptr); + break; + case MTNV_I_MAX_ALIVE_CHILDREN: + if(op == VF_GET) + (*(int *)ptr) = Matcen[matcen_id]->GetMaxAliveChildren(); + else if(op == VF_SET) + Matcen[matcen_id]->SetMaxAliveChildren(*(int *)ptr); + break; + case MTNV_F_PRE_PROD_TIME: + if(op == VF_GET) + (*(float *)ptr) = Matcen[matcen_id]->GetPreProdTime(); + else if(op == VF_SET) + Matcen[matcen_id]->SetPreProdTime(*(float *)ptr); + break; + case MTNV_F_POST_PROD_TIME: + if(op == VF_GET) + (*(float *)ptr) = Matcen[matcen_id]->GetPostProdTime(); + else if(op == VF_SET) + Matcen[matcen_id]->SetPostProdTime(*(float *)ptr); + break; + case MTNSV_I_SOUND: + if(op == VF_GET) + (*(int *)ptr) = Matcen[matcen_id]->GetSound(index); + else if(op == VF_SET) + Matcen[matcen_id]->SetSound(index, (*(int *)ptr)); + break; + case MTNV_S_CREATION_TEXTURE: + if(op == VF_GET) + (*(short *)ptr) = Matcen[matcen_id]->GetCreationTexture(); + else if(op == VF_SET) + Matcen[matcen_id]->SetCreationTexture(*(short *)ptr); + break; + + case MTNV_C_NUM_SPAWN_PTS: + if(op == VF_GET) + (*(char *)ptr) = Matcen[matcen_id]->GetNumSpawnPnts(); + else if(op == VF_SET) + Matcen[matcen_id]->SetNumSpawnPnts(*(char *)ptr); + break; + case MTNSV_I_SPAWN_POINT: + if(op == VF_GET) + Matcen[matcen_id]->SetSpawnPnt(index, *(int *)index); + else if(op == VF_SET) + (*(int *)ptr) = Matcen[matcen_id]->GetSpawnPnt(index); + break; + + case MTNV_C_NUM_PROD_TYPES: + if(op == VF_GET) + (*(char *)ptr) = Matcen[matcen_id]->GetNumProdTypes(); + else if(op == VF_SET) + Matcen[matcen_id]->SetNumSpawnPnts(*(char *)ptr); + break; + case MTNSV_I_PROD_ITEM_ID: + if(op == VF_GET) + Matcen[matcen_id]->GetProdInfo(index, (int *)ptr, NULL, NULL, NULL); + else if(op == VF_SET) + Matcen[matcen_id]->SetProdInfo(index, (int *)ptr, NULL, NULL, NULL); + break; + case MTNSV_I_PROD_ITEM_PRIORITY: + if(op == VF_GET) + Matcen[matcen_id]->GetProdInfo(index, NULL, (int *)ptr, NULL, NULL); + else if(op == VF_SET) + Matcen[matcen_id]->SetProdInfo(index, NULL, (int *)ptr, NULL, NULL); + break; + case MTNSV_F_PROD_ITEM_TIME: + if(op == VF_GET) + Matcen[matcen_id]->GetProdInfo(index, NULL, NULL, (float *)ptr, NULL); + else if(op == VF_SET) + Matcen[matcen_id]->SetProdInfo(index, NULL, NULL, (float *)ptr, NULL); + break; + case MTNSV_I_PROD_ITEM_MAX_PROD: + if(op == VF_GET) + Matcen[matcen_id]->GetProdInfo(index, NULL, NULL, NULL, (int *)ptr); + else if(op == VF_SET) + Matcen[matcen_id]->SetProdInfo(index, NULL, NULL, NULL, (int *)ptr); + break; + } +} + +void osipf_MatcenReset(int matcen_id) +{ + if(matcen_id >= 0 && matcen_id < Num_matcens && Matcen[matcen_id]) + { + Matcen[matcen_id]->Reset(); + } +} + +int osipf_MatcenCreate(char *str) +{ + bool name_changed; + + char name[MAX_MATCEN_NAME_LEN]; + strncpy(name, str, MAX_MATCEN_NAME_LEN-1); + name[MAX_MATCEN_NAME_LEN - 1] = '\0'; + + return CreateMatcen(name, &name_changed); +} + +void osipf_MatcenCopy(int md_id,int ms_id) +{ + if(md_id >= 0 && md_id < Num_matcens && Matcen[md_id]) + { + if(ms_id >= 0 && ms_id < Num_matcens && Matcen[ms_id]) + { + char name[MAX_MATCEN_NAME_LEN]; + Matcen[md_id]->GetName(name); + + *Matcen[md_id] = *Matcen[ms_id]; + Matcen[md_id]->SetName(name); + Matcen[md_id]->Reset(); + } + } +} + +int osipf_MatcenFindId(char *str) +{ + char name[MAX_MATCEN_NAME_LEN]; + strncpy(name, str, MAX_MATCEN_NAME_LEN-1); + name[MAX_MATCEN_NAME_LEN - 1] = '\0'; + + return FindMatcenIndex(name); +} + +int osipf_RayCast(int objhandle,vector *p0,vector *p1,int start_roomnum,float rad,int flags,ray_info *ri) +{ + int fate; + int ignore_obj_list[100]; + + int *ilist = hack_ilist; + object *obj = ObjGet(objhandle); + if(!obj && objhandle != OBJECT_HANDLE_NONE) + { + mprintf((0,"Invalid object passed to RayCast\n")); + return 0; + } + + fvi_info hit_info; + fvi_query fq; + + fq.p0 = p0; + fq.p1 = p1; + + fq.startroom = start_roomnum; + + fq.rad = rad; + fq.flags = flags; + + if(obj) + fq.thisobjnum = OBJNUM(obj); + else + fq.thisobjnum = -1; + + if(ilist) + { + int num_items = 0; + int count = 0; + + while(ilist[count] != -1) + { + object *iobj = ObjGet(ilist[count]); + if(iobj) + { + ignore_obj_list[num_items++] = OBJNUM(iobj); + } + + count++; + } + + ignore_obj_list[num_items] = -1; + fq.ignore_obj_list = ignore_obj_list; + } + else + { + fq.ignore_obj_list = NULL; + } + + fate = fvi_FindIntersection(&fq, &hit_info); + + ri->hit_point = hit_info.hit_pnt; + ri->hit_room = hit_info.hit_room; + ri->fate = fate; + + if(fate == HIT_SPHERE_2_POLY_OBJECT || fate == HIT_OBJECT) + { + ri->hit_object = Objects[hit_info.hit_object[0]].handle; + } + ri->hit_subobject = hit_info.hit_subobject[0]; + + ri->hit_face = hit_info.hit_face[0]; + ri->hit_face_pnt = hit_info.hit_face_pnt[0]; + ri->hit_face_room = hit_info.hit_face_room[0]; + ri->hit_wallnorm = hit_info.hit_wallnorm[0]; + + return fate; +} + +//Reads the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO READ STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes read. +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int osipf_CFReadBytes(ubyte *buffer, int count, CFILE *cfp) +{ + return cf_ReadBytes(buffer,count,cfp); +} + +// The following functions read numeric vales from a CFILE. All values are +// stored in the file in Intel (little-endian) format. These functions +// will convert to big-endian if required. +// These funtions will throw an exception of if the value cannot be read, +// so do not call these if you don't require the data to be present. + +//Read and return an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int osipf_CFReadInt(CFILE *cfp) +{ + return cf_ReadInt(cfp); +} + +//Read and return a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +short osipf_CFReadShort(CFILE *cfp) +{ + return cf_ReadShort(cfp); +} + +//Read and return a byte (8 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +sbyte osipf_CFReadByte(CFILE *cfp) +{ + return cf_ReadByte(cfp); +} + +//Read and return a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +float osipf_CFReadFloat(CFILE *cfp) +{ + return cf_ReadFloat(cfp); +} + +//Read and return a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +double osipf_CFReadDouble(CFILE *cfp) +{ + return cf_ReadDouble(cfp); +} + +//Reads a string from a CFILE. If the file is type binary, this +//function reads until a NULL or EOF is found. If the file is text, +//the function reads until a newline or EOF is found. The string is always +//written to the destination buffer null-terminated, without the newline. +//Parameters: buf - where the string is written +// n - the maximum string length, including the terminating 0 +// cfp - the CFILE pointer +//Returns the number of bytes in the string, before the terminator +//Does not generate an exception on EOF +int osipf_CFReadString(char *buf,size_t n,CFILE *cfp) +{ + return cf_ReadString(buf,n,cfp); +} + +//Writes the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO WRITE STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes written. +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int osipf_CFWriteBytes(const ubyte *buf,int count, CFILE *cfp) +{ + return cf_WriteBytes(buf,count,cfp); +} + +//Writes a null-terminated string to a file. If the file is type binary, +//the string is terminated in the file with a null. If the file is type +//text, the string is terminated with a newline. +//Parameters: buf - pointer to the string +// cfp = the CFILE pointer +//Returns the number of bytes written +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int osipf_CFWriteString(const char *buf,CFILE *cfp) +{ + return cf_WriteString(cfp,buf); +} + +// The following functions write numeric vales to a CFILE. All values are +// stored to the file in Intel (little-endian) format. +// All these throw an exception if there's an error on write. + + +//Write an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteInt(int i,CFILE *cfp) +{ + cf_WriteInt(cfp,i); +} + +//Write a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteShort(short s,CFILE *cfp) +{ + cf_WriteShort(cfp,s); +} + +//Write a byte (8 bits). If the byte is a newline & the file is a text file, writes a CR/LF pair. +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteByte(sbyte b,CFILE *cfp) +{ + cf_WriteByte(cfp,b); +} + +//Write a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteFloat(float f,CFILE *cfp) +{ + cf_WriteFloat(cfp,f); +} + +//Write a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteDouble(double d,CFILE *cfp) +{ + cf_WriteDouble(cfp,d); +} + + +// CONTROLLER predefs + +// enables or disables controls specified. +void osipf_SetAllControls(bool enabled) +{ + int i; + + for (i = 0; i < NUM_CONTROLLER_FUNCTIONS; i++) + { + Controller->enable_function(i, enabled); + } +} + +// enable or disable specified function +void osipf_SetControls(int fn, bool enabled) +{ + if (Controller) { + Controller->enable_function(fn, enabled); + } +} + + +// display messages using a primative dialog +void osipf_OpenMessageWindow(const char *title, ...) +{ + Int3(); //ummm, this is a blank function, should we ever be calling it? +} + +int osipf_ObjCreate(ubyte type,ushort id,int roomnum,vector *pos,const matrix *orient,int parent_handle,vector *initial_velocity) +{ + object *obj; + int objnum; + + if (id == 65535)//since it is a ushort, this is == -1 + return OBJECT_HANDLE_NONE; + + if(((roomnum >= 0) && (roomnum <= Highest_room_index) && (Rooms[roomnum].used)) || (ROOMNUM_OUTSIDE(roomnum))) + { + if (IS_GENERIC(type)) + { + // Make sure the scripts aren't creating objects that have lightmaps! + ASSERT (Object_info[id].lighting_info.lighting_render_type!=LRT_LIGHTMAPS); + } + + objnum = ObjCreate(type, id, roomnum, pos, orient, parent_handle); + + + + if (objnum == -1) + { + return OBJECT_HANDLE_NONE; + } + + obj = &Objects[objnum]; + + //if there was an initial velocity, set it + if(initial_velocity) + { + ASSERT(obj->movement_type==MT_PHYSICS); + obj->mtype.phys_info.velocity = *initial_velocity; + } + + if (Game_mode & GM_MULTI) + { + //ASSERT (Netgame.local_role==LR_SERVER); + if((Netgame.local_role==LR_SERVER)) + { + MultiSendObject (obj, 0); + } + } + + InitObjectScripts (obj); + + if(IS_GENERIC(obj->type)) + { + int ambient_sound = Object_info[obj->id].sounds[GSI_AMBIENT]; + if (ambient_sound != SOUND_NONE_INDEX) + { + Sound_system.Play3dSound(ambient_sound, SND_PRIORITY_LOWEST, obj); + if(Game_mode & GM_MULTI) + MultiPlay3dSound(ambient_sound, objnum, SND_PRIORITY_LOW); + if(Demo_flags == DF_RECORDING) + DemoWrite3DSound(ambient_sound, objnum, SND_PRIORITY_LOW); + } + } + } + else + { + return OBJECT_HANDLE_NONE; + } + + return obj->handle; +} + +// OBJECT Properties. +bool osipf_IsObjectVisible(object *obj) +{ + if (obj->renderframe!=((FrameCount-1)% 65536)) { + return false; + } + + return true; +} + +float osipf_GameTime(void) +{ + return Gametime; +} + +float osipf_FrameTime(void) +{ + return Frametime; +} + +void osipf_ObjWBValue(int obj_handle, char wb_index, char op, char vtype, void *ptr, char g_index) +{ + object *objp = ObjGet(obj_handle); + + if(!objp) + { + mprintf((0,"Obj_WBValue: Illegal Object Passed\n")); + return; + } + + if(objp->control_type != CT_AI && objp->type != OBJ_PLAYER && objp->type != OBJ_OBSERVER) + { + mprintf((0,"Obj_WBValue: Illegal Object CT Passed\n")); + return; + } + + dynamic_wb_info *p_dwb = &objp->dynamic_wb[wb_index]; + otype_wb_info *static_wb; + poly_model *pm= &Poly_models[objp->rtype.pobj_info.model_num]; + + if(objp->type == OBJ_PLAYER || objp->type == OBJ_OBSERVER ) + { + ship *ship = &Ships[Players[objp->id].ship_index]; + static_wb = &ship->static_wb[wb_index]; + } + else + { + object_info *obj_info = &Object_info[objp->id]; + static_wb = &obj_info->static_wb[wb_index]; + } + + if(objp->type == OBJ_BUILDING && (wb_index < 0 || wb_index > pm->num_wbs)) + { + return; + } + + switch(vtype) + { + case WBV_C_NUM_WBS: + { + if(op == VF_GET) + { + *(char *)ptr = pm->num_wbs; + } + } + break; + case WBV_F_ANIM_FRAME: + if(op == VF_GET) + { + int anim_type = static_wb->flags & WBF_ANIM_MASKS; + + if(anim_type == WBF_ANIM_LOCAL) + { + *((float *)ptr) = p_dwb->wb_anim_frame; + } + else + { + *((float *)ptr) = objp->rtype.pobj_info.anim_frame; + } + } + break; + + case WBV_F_LAST_FIRE_TIME: + if(op == VF_SET) + { + p_dwb->last_fire_time = *((float *)ptr); + } + else if(op == VF_GET) + { + *((float *)ptr) = p_dwb->last_fire_time; + } + break; + + case WBV_I_DYNAMIC_FLAGS: + if(op == VF_SET) + { + p_dwb->flags = *((int *)ptr); + } + else if(op == VF_GET) + { + *((int *)ptr) = p_dwb->flags; + } + else if(op == VF_SET_FLAGS) + { + p_dwb->flags |= *((int *)ptr); + } + else if(op == VF_CLEAR_FLAGS) + { + p_dwb->flags &= ~(*((int *)ptr)); + } + break; + + case WBV_C_NUM_GUNPTS: + if(op == VF_GET) + { + if(objp->type == OBJ_PLAYER || objp->type == OBJ_OBSERVER) + { + *((char *)ptr) = 8; + } + else + { + *((char *)ptr) = pm->poly_wb[wb_index].num_gps; + } + } + break; + case WBSV_US_GUNPT_WEAPON_ID: + if(op == VF_SET) + { + static_wb->gp_weapon_index[g_index] = *((unsigned short *)ptr); + } + else if(op == VF_GET) + { + *((unsigned short *)ptr) = static_wb->gp_weapon_index[g_index]; + } + break; + case WBSV_V_GUNPT_POS: + if(op == VF_GET) + { + if(objp->type == OBJ_PLAYER || objp->type == OBJ_OBSERVER) + WeaponCalcGun(((vector *)ptr), NULL, objp, pm->poly_wb[0].gp_index[g_index]); + else + WeaponCalcGun(((vector *)ptr), NULL, objp, pm->poly_wb[wb_index].gp_index[g_index]); + } + break; + + case WBV_C_NUM_MASKS: + if(op == VF_SET) + { + static_wb->num_masks = *((char *)ptr); + } + else if(op == VF_GET) + { + *((char *)ptr) = static_wb->num_masks; + } + break; + case WBSV_C_MASK: + if(op == VF_SET) + { + static_wb->gp_fire_masks[g_index] = *((char *)ptr); + } + else if(op == VF_GET) + { + *((char *)ptr) = static_wb->gp_fire_masks[g_index]; + } + else if(op == VF_SET_FLAGS) + { + static_wb->gp_fire_masks[g_index] |= *((char *)ptr); + } + else if(op == VF_CLEAR_FLAGS) + { + static_wb->gp_fire_masks[g_index] &= ~(*((char *)ptr)); + } + break; + case WBSV_I_FIRE_SOUND: + if(op == VF_SET) + { + static_wb->fm_fire_sound_index[g_index] = (*((int *)ptr)); + } + else if(op == VF_GET) + { + (*((int *)ptr)) = static_wb->fm_fire_sound_index[g_index]; + } + break; + case WBSV_F_LATENCY: + if(op == VF_SET) + { + static_wb->gp_fire_wait[g_index] = (*((float *)ptr)); + } + else if(op == VF_GET) + { + (*((float *)ptr)) = static_wb->gp_fire_wait[g_index]; + } + break; + case WBSV_F_ANIM_TIME: + if(op == VF_SET) + { + static_wb->anim_time[g_index] = (*((float *)ptr)); + } + else if(op == VF_GET) + { + (*((float *)ptr)) = static_wb->anim_time[g_index]; + } + break; + case WBSV_F_ANIM_START: + if(op == VF_SET) + { + static_wb->anim_start_frame[g_index] = (*((float *)ptr)); + } + else if(op == VF_GET) + { + (*((float *)ptr)) = static_wb->anim_start_frame[g_index]; + } + break; + case WBSV_F_ANIM_FIRE: + if(op == VF_SET) + { + static_wb->anim_fire_frame[g_index] = (*((float *)ptr)); + } + else if(op == VF_GET) + { + (*((float *)ptr)) = static_wb->anim_fire_frame[g_index]; + } + break; + case WBSV_F_ANIM_END: + if(op == VF_SET) + { + static_wb->anim_end_frame[g_index] = (*((float *)ptr)); + } + else if(op == VF_GET) + { + (*((float *)ptr)) = static_wb->anim_end_frame[g_index]; + } + break; + } +} + +// Sets/Clears mission flags +// flag is which mission flag to set/clear (1-32) +// value is 0 to clear, or 1 to set +void osipf_MissionFlagSet(int flag,ubyte value) +{ + if(flag<1 && flag>32){ + mprintf((0,"Invalid flag passed to osipf_MissionFlagSet(%d)\n",flag)); + return; + } + + flag--; + uint bit = 0x01; + bit = bit << flag; + + if(!value){ + Current_mission.game_state_flags &= ~bit; + }else{ + Current_mission.game_state_flags |= bit; + } +} + +// Gets a mission flag +// flag is what mission flag to get. Returns 1 if set, 0 if not. +int osipf_MissionFlagGet(int flag) +{ + if(flag<1 && flag>32){ + mprintf((0,"Invalid flag passed to osipf_MissionFlagGet(%d)\n",flag)); + return 0; + } + + flag--; + uint bit = 0x01; + bit = bit << flag; + + if(Current_mission.game_state_flags&bit) + return 1; + + return 0; +} + +void osipf_PlayerAddHudMessage(int handle, char *str) +{ + msafe_struct mo; + + mo.objhandle = mo.id = handle; + mo.color = GR_RGB(0,255,0); + strncpy(mo.message, str, MSAFE_MESSAGE_LENGTH - 1); + mo.message[MSAFE_MESSAGE_LENGTH - 1] = '\0'; + + msafe_CallFunction(MSAFE_MISC_HUD_MESSAGE,&mo); +} + +void osipf_ObjGhost(int handle, bool f_ghost) +{ + object *obj = ObjGet(handle); + if(obj) + { + //BLACKPYROHACK - Chrishack for Mercenary + if(!(Game_mode & GM_MULTI)) + { + if(obj->handle == Buddy_handle[0]) + { + if(stricmp(Ships[Players[Player_object->id].ship_index].name, "Black Pyro") == 0) + { + obj->id = ROBOT_GUIDEBOTRED; + PageInPolymodel (Object_info[ROBOT_GUIDEBOTRED].render_handle,Object_info[ROBOT_GUIDEBOTRED].type,&Object_info[ROBOT_GUIDEBOTRED].size); + obj->rtype.pobj_info.model_num = Object_info[ROBOT_GUIDEBOTRED].render_handle; + } + else + { + obj->id = ROBOT_GUIDEBOT; + obj->rtype.pobj_info.model_num = Object_info[ROBOT_GUIDEBOT].render_handle; + } + } + } + + msafe_struct mo; + mo.objhandle = obj->handle; + + if(f_ghost) + msafe_CallFunction(MSAFE_OBJECT_GHOST,&mo); + else + msafe_CallFunction(MSAFE_OBJECT_UNGHOST,&mo); + } +} + +void osipf_ObjBurning(int handle, float time, float damage_per_second) +{ + object *obj = ObjGet(handle); + if(obj && obj->effect_info) + { + if(time > 0.0) + { + obj->effect_info->type_flags|=EF_NAPALMED; + + obj->effect_info->damage_time = time; + obj->effect_info->damage_per_second = damage_per_second; + + obj->effect_info->last_damage_time = 0; + + obj->effect_info->damage_handle = obj->handle; + + if (obj->effect_info->sound_handle == SOUND_NONE_INDEX) + obj->effect_info->sound_handle = Sound_system.Play3dSound(SOUND_PLAYER_BURNING, SND_PRIORITY_HIGHEST, obj); + } + else + { + obj->effect_info->type_flags&=(~EF_NAPALMED); + obj->effect_info->last_damage_time=0; + Sound_system.StopSoundLooping(obj->effect_info->sound_handle); + obj->effect_info->sound_handle = SOUND_NONE_INDEX; + } + } +} + +bool osipf_ObjIsEffect(int handle, int type_flag) +{ + object *obj = ObjGet(handle); + return (obj && obj->effect_info && (obj->effect_info->type_flags & type_flag)); +} + +void *osipf_CFopen(const char *filename,const char *mode) +{ + return cfopen(filename,mode); +} + +void osipf_CFclose(CFILE *file) +{ + cfclose(file); +} + +int osipf_CFtell(CFILE *file) +{ + return cftell(file); +} + +ubyte osipf_CFeof(CFILE *file) +{ + return (cfeof(file)!=0)?1:0; +} + +void osipf_SoundStop(int s_handle, bool f_immediately) +{ + //JEFF - sorry, this can't be made multiplayer friendly + /* + if(f_immediately) + Sound_system.StopSoundImmediate(s_handle); + else + Sound_system.StopSoundLooping(s_handle); + */ +} + +int osipf_SoundPlay2d(int obj_handle, int s_id, float volume) +{ + msafe_struct mstruct; + mstruct.index = s_id; + + if(obj_handle!=OBJECT_HANDLE_NONE) + { + mstruct.state = 1; + mstruct.objhandle = obj_handle; + }else + { + mstruct.state = 0; + } + + mstruct.volume = volume; + + msafe_CallFunction(MSAFE_SOUND_2D,&mstruct); + return mstruct.sound_handle; + /* + //chrishack -- use handle for who hears the sound + return Sound_system.Play2dSound(s_id, SND_PRIORITY_HIGHEST, volume); + */ +} + +int osipf_SoundPlay3d(int obj_handle, int s_id, float volume) +{ + msafe_struct mstruct; + mstruct.objhandle = obj_handle; + mstruct.index = s_id; + + msafe_CallFunction(MSAFE_SOUND_OBJECT,&mstruct); + return mstruct.sound_handle; + /* + object *obj = ObjGet(obj_handle); + + if(obj) + { + return Sound_system.Play3dSound(s_id, SND_PRIORITY_HIGHEST,obj, volume); + } + + return -1; + */ +} + +int osipf_SoundFindId(char *s_name) +{ + return FindSoundName(IGNORE_TABLE(s_name)); +} + +bool osipf_AIIsObjFriend(int obj_handle, int it_handle) +{ + object *me = ObjGet(obj_handle); + object *it = ObjGet(it_handle); + + if(me && it) + return AIObjFriend(me, it); + + return false; +} + +bool osipf_AIIsObjEnemy(int obj_handle, int it_handle) +{ + object *me = ObjGet(obj_handle); + object *it = ObjGet(it_handle); + + if(me && it) + return AIObjEnemy(me, it); + + return false; +} + +void osipf_AIGoalValue(int obj_handle, char g_index, char op, char vtype, void *ptr, char index) +{ + object *obj = ObjGet(obj_handle); + if(!obj) + return; + + ai_frame *ai_info = obj->ai_info; + if(!ai_info) + return; + + if(g_index < 0 || g_index >= MAX_GOALS) + return; + + goal *g_ptr = &ai_info->goals[g_index]; + + if(!g_ptr->used || vtype == AIGV_B_USED) + { + if(vtype == AIGV_B_USED && op == VF_GET) + *(bool *)ptr = g_ptr->used; + + return; + } + + switch(vtype) + { + case AIGV_I_TYPE: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->type; + else if(op == VF_SET) + g_ptr->type = *(int *)ptr; + } + break; + case AIGV_C_ACTIVATION_LEVEL: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->activation_level; + else if(op == VF_SET) + g_ptr->activation_level = *(char *)ptr; + } + break; + case AIGV_F_MIN_INFLUENCE: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->min_influence; + else if(op == VF_SET) + g_ptr->min_influence = *(float *)ptr; + } + break; + case AIGV_F_MAX_INFLUENCE: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->max_influence; + else if(op == VF_SET) + g_ptr->max_influence = *(float *)ptr; + } + break; + + + case AIGSV_F_INFLUENCE_DIST: // 0 - 3 + { + if(op == VF_GET) + *(float *)ptr = g_ptr->ramp_influence_dists[index]; + else if(op == VF_SET) + g_ptr->ramp_influence_dists[index] = *(float *)ptr; + } + break; + + case AIGV_I_HANDLE: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->g_info.handle; + else if(op == VF_SET) + g_ptr->g_info.handle = *(int *)ptr; + } + break; + case AIGV_I_ROOMNUM: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->g_info.roomnum; + else if(op == VF_SET) + g_ptr->g_info.roomnum = *(int *)ptr; + } + break; + case AIGV_I_F_ACTIONS: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->g_info.f_actions; + else if(op == VF_SET) + g_ptr->g_info.f_actions = *(int *)ptr; + } + break; + case AIGV_I_ID: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->g_info.id; + else if(op == VF_SET) + g_ptr->g_info.id = *(int *)ptr; + } + break; + case AIGV_C_SUBTYPE: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->subtype; + else if(op == VF_SET) + g_ptr->subtype = *(char *)ptr; + } + break; + case AIGV_F_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->g_info.time; + else if(op == VF_SET) + g_ptr->g_info.time = *(float *)ptr; + } + break; + case AIGV_V_VEC: + { + if(op == VF_GET) + *(vector *)ptr = g_ptr->g_info.vec; + else if(op == VF_SET) + g_ptr->g_info.vec = *(vector *)ptr; + } + break; + case AIGV_V_POS: + { + if(op == VF_GET) + *(vector *)ptr = g_ptr->g_info.pos; + else if(op == VF_SET) + g_ptr->g_info.pos = *(vector *)ptr; + } + break; + case AIGV_F_STEER_MIN_DIST: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->g_info.dist_info.min_dist; + else if(op == VF_SET) + g_ptr->g_info.dist_info.min_dist = *(float *)ptr; + } + break; + case AIGV_F_STEER_MAX_DIST: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->g_info.dist_info.max_dist; + else if(op == VF_SET) + g_ptr->g_info.dist_info.max_dist = *(float *)ptr; + } + break; + case AIGV_F_STEER_MAX_STRENGTH: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->g_info.dist_info.max_strength; + else if(op == VF_SET) + g_ptr->g_info.dist_info.max_strength = *(float *)ptr; + } + break; + case AIGV_B_ATTACH_F_ALIGNED: + { + if(op == VF_GET) + *(bool *)ptr = (g_ptr->g_info.attach_info.flags & GAF_ALIGNED) != 0; + } + break; + case AIGV_B_ATTACH_F_SPHERE: + { + if(op == VF_GET) + *(bool *)ptr = (g_ptr->g_info.attach_info.flags & GAF_SPHERE) != 0; + } + break; + case AIGV_F_ATTACH_RAD: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->g_info.attach_info.rad; + } + break; + case AIGV_C_ATTACH_CHILD_AP: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->g_info.attach_info.child_ap; + } + break; + case AIGV_C_ATTACH_PARENT_AP: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->g_info.attach_info.parent_ap; + } + break; + case AIGV_I_WANDER_AVOID_HANDLE: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->g_info.wander_extra_info.avoid_handle; + else if(op == VF_SET) + g_ptr->g_info.wander_extra_info.avoid_handle = *(int *)ptr; + } + break; + case AIGV_C_WANDER_MIN_ROOMS: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->g_info.wander_extra_info.min_rooms; + else if(op == VF_SET) + g_ptr->g_info.wander_extra_info.min_rooms = *(char *)ptr; + } + break; + case AIGV_C_WANDER_MAX_ROOMS: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->g_info.wander_extra_info.max_rooms; + else if(op == VF_SET) + g_ptr->g_info.wander_extra_info.max_rooms = *(char *)ptr; + } + break; + case AIGV_C_WANDER_FLAGS: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->g_info.wander_extra_info.flags; + else if(op == VF_SET) + g_ptr->g_info.wander_extra_info.flags = *(char *)ptr; + else if(op == VF_SET_FLAGS) + g_ptr->g_info.wander_extra_info.flags |= *(char *)ptr; + else if(op == VF_CLEAR_FLAGS) + g_ptr->g_info.wander_extra_info.flags &= ~(*(char *)ptr); + } + break; + case AIGV_C_WANDER_MINE_INDEX: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->g_info.wander_extra_info.mine_index; + else if(op == VF_SET) + g_ptr->g_info.wander_extra_info.mine_index = *(char *)ptr; + } + break; + case AIGV_F_CIRCLE_DIST: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->circle_distance; + else if(op == VF_SET) + g_ptr->circle_distance = *(float *)ptr; + } + break; + case AIGV_I_STATUS_REG: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->status_reg; + else if(op == VF_SET) + g_ptr->status_reg = *(int *)ptr; + else if(op == VF_SET_FLAGS) + g_ptr->status_reg |= *(int *)ptr; + else if(op == VF_CLEAR_FLAGS) + g_ptr->status_reg &= ~(*(int *)ptr); + } + break; + case AIGV_F_START_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->start_time; + else if(op == VF_SET) + g_ptr->start_time = *(float *)ptr; + } + break; + case AIGV_F_NEXT_PATH_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->next_path_time; + else if(op == VF_SET) + g_ptr->next_path_time = *(float *)ptr; + } + break; + case AIGV_F_DIST_TO_GOAL: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->dist_to_goal; + else if(op == VF_SET) + g_ptr->dist_to_goal = *(float *)ptr; + } + break; + case AIGV_I_SCRIPTED_DATA_PTR: + { + if(op == VF_GET) + ptr = g_ptr->g_info.scripted_data_ptr; + else if(op == VF_SET) + g_ptr->g_info.scripted_data_ptr = (void *)(*(int *)ptr); + } + break; + case AIGV_V_VEC_TO_TARGET: + { + if(op == VF_GET) + *(vector *)ptr = g_ptr->vec_to_target; + else if(op == VF_SET) + g_ptr->vec_to_target = *(vector *)ptr; + } + break; + case AIGV_F_NEXT_CHECK_SEE_TARGET_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->next_check_see_target_time; + else if(op == VF_SET) + g_ptr->next_check_see_target_time = *(float *)ptr; + } + break; + case AIGV_V_LAST_SEE_TARGET_POS: + { + if(op == VF_GET) + *(vector *)ptr = g_ptr->last_see_target_pos; + else if(op == VF_SET) + g_ptr->last_see_target_pos = *(vector *)ptr; + } + break; + case AIGV_F_LAST_SEE_TARGET_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->last_see_target_time; + else if(op == VF_SET) + g_ptr->last_see_target_time = *(float *)ptr; + } + break; + case AIGV_F_NEXT_TARGET_UPDATE_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->next_target_update_time; + else if(op == VF_SET) + g_ptr->next_target_update_time = *(float *)ptr; + } + break; + case AIGV_I_FLAGS: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->flags; + else if(op == VF_SET) + g_ptr->flags = *(int *)ptr; + else if(op == VF_SET_FLAGS) + g_ptr->flags |= *(int *)ptr; + else if(op == VF_CLEAR_FLAGS) + g_ptr->flags &= ~(*(int *)ptr); + } + break; + + case AIGV_C_NUM_ENABLERS: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->num_enablers; + else if(op == VF_SET) + g_ptr->num_enablers = *(char *)ptr; + } + break; + case AIGV_V_ORIENT_FVEC: + { + if(op == VF_GET) + *(vector *)ptr = g_ptr->set_fvec; + else if(op == VF_SET) + g_ptr->set_fvec = *(vector *)ptr; + } + break; + case AIGV_V_ORIENT_UVEC: + { + if(op == VF_GET) + *(vector *)ptr = g_ptr->set_uvec; + else if(op == VF_SET) + g_ptr->set_uvec = *(vector *)ptr; + } + break; + case AIGSV_C_ENABLER_TYPE: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->enabler[index].enabler_type; + else if(op == VF_SET) + g_ptr->enabler[index].enabler_type = *(char *)ptr; + } + break; + case AIGSV_F_ENABLER_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->enabler[index].time; + else if(op == VF_SET) + g_ptr->enabler[index].time = *(float *)ptr; + } + break; + case AIGSV_C_ENABLER_MTYPE: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->enabler[index].movement_type; + else if(op == VF_SET) + g_ptr->enabler[index].movement_type = *(char *)ptr; + } + break; + case AIGSV_F_ENABLER_FLOAT_VALUE: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->enabler[index].float_value; + else if(op == VF_SET) + g_ptr->enabler[index].float_value = *(float *)ptr; + } + break; + case AIGSV_I_ENABLER_FLAGS: + { + if(op == VF_GET) + *(int *)ptr = g_ptr->enabler[index].flags; + else if(op == VF_SET) + g_ptr->enabler[index].flags = *(int *)ptr; + else if(op == VF_SET_FLAGS) + g_ptr->enabler[index].flags |= *(int *)ptr; + else if(op == VF_CLEAR_FLAGS) + g_ptr->enabler[index].flags &= ~(*(int *)ptr); + } + break; + case AIGSV_F_ENABLER_DIST: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->enabler[index].dist; + else if(op == VF_SET) + g_ptr->enabler[index].dist = *(float *)ptr; + } + break; + case AIGSV_F_ENABLER_PERCENT: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->enabler[index].percent_enable; + else if(op == VF_SET) + g_ptr->enabler[index].percent_enable = *(float *)ptr; + } + break; + case AIGSV_F_ENABLER_CHECK_INTERVAL: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->enabler[index].check_interval; + else if(op == VF_SET) + g_ptr->enabler[index].check_interval = *(float *)ptr; + } + break; + case AIGSV_F_ENABLER_LAST_CHECK_TIME: + { + if(op == VF_GET) + *(float *)ptr = g_ptr->enabler[index].last_check_time; + else if(op == VF_SET) + g_ptr->enabler[index].last_check_time = *(float *)ptr; + } + break; + case AIGSV_C_ENABLER_NEXT_ENABLER_OP: + { + if(op == VF_GET) + *(char *)ptr = g_ptr->enabler[index].bool_next_enabler_op; + else if(op == VF_SET) + g_ptr->enabler[index].bool_next_enabler_op = *(char *)ptr; + } + break; + } +} + +int osipf_AIGetNearbyObjs(vector *pos, int init_roomnum, float rad, int *object_handle_list, int max_elements, bool f_lightmap_only, bool f_only_players_and_ais, bool f_include_non_collide_objects, bool f_stop_at_closed_doors) +{ + short *s_list; + int num_close; + int i; + int count = 0; + + s_list = (short *)mem_malloc(sizeof(short) * max_elements); + + num_close = fvi_QuickDistObjectList(pos, init_roomnum, rad, s_list, max_elements, f_lightmap_only, f_only_players_and_ais, f_include_non_collide_objects, f_stop_at_closed_doors); + ASSERT(num_close <= max_elements); + for(i = 0; i < num_close; i++) + { + if(!(Objects[s_list[i]].flags & OF_DEAD) && Objects[s_list[i]].render_type != RT_NONE) + { + object_handle_list[count++] = Objects[s_list[i]].handle; + } + } + + mem_free(s_list); + + return count; +} + +char osipf_AIGetCurGoalIndex(int obj_handle) +{ + object *obj = ObjGet(obj_handle); + + if(obj && obj->ai_info) + { + goal *cur_goal = GoalGetCurrentGoal(obj); + if(cur_goal) + { + return (cur_goal - obj->ai_info->goals); + } + } + + return -1; +} + +int osipf_FindSoundName(char *name) +{ + return FindSoundName(IGNORE_TABLE(name)); +} + +int osipf_FindRoomName(char *name) +{ + for(int i=0;i<=Highest_room_index;i++){ + if(Rooms[i].used && Rooms[i].name){ + if(!stricmp(name,Rooms[i].name)) + return i; + } + } + return -1; +} + +int osipf_FindTriggerName(char *name) +{ + for(int i=0;i=Num_triggers) + return -1; + + return Triggers[trigger_id].roomnum; +} + +int osipf_GetTriggerFace(int trigger_id) +{ + if(trigger_id<0 || trigger_id>=Num_triggers) + return -1; + + return Triggers[trigger_id].facenum; +} + +int osipf_FindDoorName(char *name) +{ + for(int i=0;i<=MAX_OBJECTS;i++){ + if(Objects[i].type==OBJ_DOOR && Objects[i].name && !stricmp(Objects[i].name,name) ){ + return Objects[i].handle; + } + } + return OBJECT_HANDLE_NONE; +} + +int osipf_FindTextureName(char *name) +{ + return FindTextureName(IGNORE_TABLE(name)); +} + +int osipf_FindMatcenName(char *name) +{ + return FindMatcenIndex(name); +} + +int osipf_FindPathName(char *name) +{ + return FindGamePathName (name); +} + +int osipf_FindLevelGoalName(char *name) +{ + return Level_goals.GoalFindId(name); +} + +void osipf_CreateRandomSparks(int num_sparks,vector *pos,int roomnum,int which_index,float force_scalar) +{ + CreateRandomSparks (num_sparks, pos, roomnum, which_index, force_scalar); +} + +//disable/enable ship +void osipf_EnableShip(char *ship_name,bool enable) +{ + msafe_struct ms; + strcpy(ms.name,ship_name); + ms.state = enable; + msafe_CallFunction(MSAFE_MISC_ENABLE_SHIP,&ms); +} + +//is ship enabled +bool osipf_IsShipEnabled(char *ship_name) +{ + msafe_struct ms; + strcpy(ms.name,ship_name); + msafe_GetValue(MSAFE_MISC_ENABLE_SHIP,&ms); + return (bool)(ms.state!=0); +} + +//turns the given player into AI mode or back to regular control mode +void osipf_SetPlayerControlMode(int pnum,bool set_to_ai) +{ + ASSERT( pnum>=0 && pnum=MAX_PLAYERS) + return; + + if(set_to_ai) + { + if(pnum!=Player_num) + return; //only change it for ourselves + + PlayerSetControlToAI (pnum); + } + else + { + ResetPlayerControlType (pnum); + } +} + +//gets information about a path point +//pass NULL for parameters not needed. +// pathid: path number +// point: which path point +//returns true if operation was successful +bool osipf_PathGetInformation(int pathid,int point,vector *pos,int *room,matrix *orient) +{ + if(pathid<0 || pathid>=MAX_GAME_PATHS) + return false; + + if(!GamePaths[pathid].used) + return false; + + if(point<0 || point>=GamePaths[pathid].num_nodes) + return false; + + if(pos) + { + pos->x = GamePaths[pathid].pathnodes[point].pos.x; + pos->y = GamePaths[pathid].pathnodes[point].pos.y; + pos->z = GamePaths[pathid].pathnodes[point].pos.z; + } + + if(room) + { + *room = GamePaths[pathid].pathnodes[point].roomnum; + } + + if(orient) + { + vm_VectorToMatrix(orient,&GamePaths[pathid].pathnodes[point].fvec,&GamePaths[pathid].pathnodes[point].uvec,NULL); + } + + return true; +} + +void osipf_LGoalValue(char op, char vtype, void *ptr, int g_index, int i_index) +{ + switch(vtype) + { + case LGV_I_STATUS: + { + if(op == VF_GET) + Level_goals.LGStatus(LO_GET_SPECIFIED, (int *)ptr); + else if(op == VF_SET_FLAGS) + Level_goals.LGStatus(LO_SET_SPECIFIED, (int *)ptr); + else if(op == VF_CLEAR_FLAGS) + Level_goals.LGStatus(LO_CLEAR_SPECIFIED, (int *)ptr); + else if(op == VF_SET) + { + int status; + Level_goals.LGStatus(LO_GET_SPECIFIED, &status); + + int diff_flags = (*(int *)ptr) ^ status; + int clear_flags = status & diff_flags; + int set_flags = (~(status)) & diff_flags; + + if(clear_flags) + Level_goals.LGStatus(LO_CLEAR_SPECIFIED, &clear_flags); + if(set_flags) + Level_goals.LGStatus(LO_SET_SPECIFIED, &set_flags); + } + } + break; + case LGV_I_NUM_ACTIVE_PRIMARIES: + { + if(op == VF_GET) + *((int *)ptr) = Level_goals.GetNumActivePrimaryGoals(); + } + break; + case LGSV_I_ACTIVE_PRIMARY_GOAL: + { + if(op == VF_GET) + *((int *)ptr) = Level_goals.GetActivePrimaryGoal(g_index); + } + break; + case LGV_I_NUM_ACTIVE_SECONDARIES: + { + if(op == VF_GET) + *((int *)ptr) = Level_goals.GetNumActiveSecondaryGoals(); + } + break; + case LGSV_I_ACTIVE_SECONDARY_GOAL: + { + if(op == VF_GET) + *((int *)ptr) = Level_goals.GetActiveSecondaryGoal(g_index); + } + break; + case LGV_I_NUM_GOALS: + { + if(op == VF_GET) + *((int *)ptr) = Level_goals.GetNumGoals(); + } + break; + case LGSV_PC_GOAL_NAME: + { + if(op == VF_GET) + { + Level_goals.GoalGetName(g_index, (char *)ptr, 256); // chrishack - major demo hack + } + else if(op == VF_SET) + { + Level_goals.GoalSetName(g_index, (char *)ptr); + } + } + break; + case LGSV_PC_LOCATION_NAME: + { + if(op == VF_GET) + { + Level_goals.GoalGetItemName(g_index, (char *)ptr, 256); // chrishack - major demo hack + } + else if(op == VF_SET) + { + Level_goals.GoalSetItemName(g_index, (char *)ptr); + } + } + break; + case LGSV_PC_DESCRIPTION: + { + if(op == VF_GET) + { + Level_goals.GoalGetDesc(g_index, (char *)ptr, 256); // chrishack - major demo hack + } + else if(op == VF_SET) + { + Level_goals.GoalSetDesc(g_index, (char *)ptr); + } + } + break; + case LGSV_PC_COMPLETION_MESSAGE: + { + if(op == VF_GET) + { + Level_goals.GoalGetCompletionMessage(g_index, (char *)ptr, 256); + } + else if(op == VF_SET) + { + Level_goals.GoalSetCompletionMessage(g_index, (char *)ptr); + } + } + break; + case LGSV_I_PRIORITY: + { + if(op == VF_GET) + { + Level_goals.GoalPriority(g_index, LO_GET_SPECIFIED, (int *)&ptr); + } + else if(op == VF_SET) + { + Level_goals.GoalPriority(g_index, LO_SET_SPECIFIED, (int *)&ptr); + } + } + break; + case LGSV_C_GOAL_LIST: + { + if(op == VF_GET) + { + Level_goals.GoalGoalList(g_index, LO_GET_SPECIFIED, (char *)ptr); + } + else if(op == VF_SET) + { + Level_goals.GoalGoalList(g_index, LO_SET_SPECIFIED, (char *)ptr); + } + } + break; + case LGSV_I_STATUS: + { + if(op == VF_GET) + Level_goals.GoalStatus(g_index, LO_GET_SPECIFIED, (int *)ptr); + else if(op == VF_SET_FLAGS) + Level_goals.GoalStatus(g_index, LO_SET_SPECIFIED, (int *)ptr); + else if(op == VF_CLEAR_FLAGS) + Level_goals.GoalStatus(g_index, LO_CLEAR_SPECIFIED, (int *)ptr); + else if(op == VF_SET) + { + int status; + Level_goals.GoalStatus(g_index, LO_GET_SPECIFIED, &status); + + int diff_flags = (*(int *)ptr) ^ status; + int clear_flags = status & diff_flags; + int set_flags = (~(status)) & diff_flags; + + if(clear_flags) + Level_goals.GoalStatus(g_index, LO_CLEAR_SPECIFIED, &clear_flags); + if(set_flags) + Level_goals.GoalStatus(g_index, LO_SET_SPECIFIED, &set_flags); + } + } + break; + case LGSV_I_NUM_ITEMS: + { + if(op == VF_GET) + { + *((int *)ptr) = Level_goals.GoalGetNumItems(g_index); + } + } + break; + case LGSSV_C_ITEM_TYPE: + { + if(op == VF_GET) + { + Level_goals.GoalItemInfo(g_index, i_index, LO_GET_SPECIFIED, (char *)ptr, NULL, NULL); + } + else if(op == VF_SET) + { + Level_goals.GoalItemInfo(g_index, i_index, LO_SET_SPECIFIED, (char *)ptr, NULL, NULL); + } + } + break; + case LGSSV_I_ITEM_HANDLE: + { + if(op == VF_GET) + { + Level_goals.GoalItemInfo(g_index, i_index, LO_GET_SPECIFIED, NULL, (int *)ptr, NULL); + } + else if(op == VF_SET) + { + Level_goals.GoalItemInfo(g_index, i_index, LO_SET_SPECIFIED, NULL, (int *)ptr, NULL); + } + } + break; + case LGSSV_B_ITEM_DONE: + { + if(op == VF_GET) + { + Level_goals.GoalItemInfo(g_index, i_index, LO_GET_SPECIFIED, NULL, NULL, (bool *)ptr); + } + else if(op == VF_SET) + { + Level_goals.GoalItemInfo(g_index, i_index, LO_SET_SPECIFIED, NULL, NULL, (bool *)ptr); + } + } + break; + } +} + +void osipf_ObjKill(int handle, int killer_handle, float damage, int flags, float min_time, float max_time) +{ + object *obj = ObjGet(handle); + object *killer = ObjGet(killer_handle); + + if(obj == NULL) + return; + + if (obj->type == OBJ_PLAYER || obj->type == OBJ_GHOST || obj->type == OBJ_OBSERVER) { + if(obj->type!=OBJ_PLAYER) + { + Int3(); + } + + KillPlayer(obj,killer,damage,-1); + } + else if (IS_GENERIC(obj->type) || (obj->type == OBJ_DOOR)) { + + if (IS_GUIDEBOT(obj)) //Don't allow killing of the guidebot + return; + + if (flags == -1) //no flags, so use default death + KillObject(obj, killer, damage); + else { + float delay_time = min_time + (max_time - min_time) * ps_rand() / RAND_MAX; + KillObject(obj, killer, damage, flags, delay_time); + } + } +} + +bool osipf_AIIsDestReachable(int handle, int room) +{ + object *obj = ObjGet(handle); + + if(!obj) + return false; + + return AIFindAltPath(obj, obj->roomnum, room); +} + +bool osipf_AIIsObjReachable(int handle, int target_handle) +{ + object *obj = ObjGet(handle); + object *target = ObjGet(target_handle); + + if(!obj) + return false; + + if(!target) + return false; + + return AIFindAltPath(obj, obj->roomnum, target->roomnum); +} + +char osipf_GameGetDiffLevel(void) +{ + return DIFF_LEVEL; +} + +int osipf_GetLanguageSetting(void) +{ +/* +0:LANGUAGE_ENGLISH +1:LANGUAGE_GERMAN +2:LANGUAGE_SPANISH +3:LANGUAGE_ITALIAN +4:LANGUAGE_FRENCH +*/ + return Localization_GetLanguage(); +} + +// Sets/Gets information about a path. +// If you change is PV_ALL (or any of it's individual components), pass in a pointer to an +// osiris_path_node_info struct. For the others, you must pass in an appropriate pointer +// (i.e. an int* for PV_I_NUMNODES). You can only set PV_ALL components. +// for PV_I_NUMNODES, path_id MUST be specified, node_id is ignored +// for PV_I_NUMPATHS, path_id and node_id are ignored +// for PV_CS_NAME, path_id MUST be specified, node_id is ignored +// for PV_ALL components, path_id and node_id MUST be valid. +void osipf_PathValue(int path_id, int node_id, char op, int changes, void *ptr) +{ + if(changes&PV_I_NUMPATHS) + { + *((int *)ptr) = Num_game_paths; + return; + } + + if(path_id<0 || path_id>=Num_game_paths) + { + mprintf((0,"Invalid Path\n")); + Int3(); + return; + } + + game_path *cpath = &GamePaths[path_id]; + + ASSERT(cpath->used); + if(!cpath->used) + return; + + if(op==VF_GET) + { + if(changes&PV_ALL) + { + if(node_id<0 || node_id>=cpath->num_nodes) + { + mprintf((0,"Invalid node on path\n")); + Int3(); + return; + } + + node *cnode = &cpath->pathnodes[node_id]; + osiris_path_node_info *pni = (osiris_path_node_info *)ptr; + + if(changes&PV_POS) + { + pni->pos = cnode->pos; + } + if(changes&PV_ROOMNUM) + { + pni->roomnum = cnode->roomnum; + } + if(changes&PV_FLAGS) + { + pni->flags = cnode->flags; + } + if(changes&PV_FVEC) + { + pni->fvec = cnode->fvec; + } + if(changes&PV_UVEC) + { + pni->uvec = cnode->uvec; + } + + changes &= ~PV_ALL; + ASSERT(changes==0); //no other changes allowed!!!! + return; + } + + if(changes==PV_I_NUMNODES) + { + *((int *)ptr) = cpath->num_nodes; + return; + } + + if(changes==PV_CS_NAME) + { + strcpy((char *)ptr,cpath->name); + return; + } + + }else if(op==VF_SET) + { + if(changes&PV_ALL) + { + if(node_id<0 || node_id>=cpath->num_nodes) + { + mprintf((0,"Invalid node on path\n")); + Int3(); + return; + } + + node *cnode = &cpath->pathnodes[node_id]; + osiris_path_node_info *pni = (osiris_path_node_info *)ptr; + + if(changes&PV_POS) + { + cnode->pos = pni->pos; + } + if(changes&PV_ROOMNUM) + { + cnode->roomnum = pni->roomnum; + } + if(changes&PV_FLAGS) + { + cnode->flags = pni->flags; + } + if(changes&PV_FVEC) + { + cnode->fvec = pni->fvec; + } + if(changes&PV_UVEC) + { + cnode->uvec = pni->uvec; + } + + changes &= ~PV_ALL; + ASSERT(changes==0); //no other changes allowed!!!! + return; + } + + }else + { + Int3(); + } +} diff --git a/Descent3/osiris_predefs.h b/Descent3/osiris_predefs.h new file mode 100644 index 000000000..57b6029df --- /dev/null +++ b/Descent3/osiris_predefs.h @@ -0,0 +1,430 @@ +/* +* $Logfile: /DescentIII/main/osiris_predefs.h $ +* $Revision: 47 $ +* $Date: 5/05/99 4:56p $ +* $Author: Chris $ +* +* Header file for osiris predefined functions +* +* $Log: /DescentIII/main/osiris_predefs.h $ + * + * 47 5/05/99 4:56p Chris + * Added the RType powerup for the GB + * + * 46 4/29/99 7:15p Jeff + * added initial velocity parameter to Obj_Create + * + * 45 4/24/99 2:19a Chris + * Added another parameter to FollowPathSimple + * + * 44 4/24/99 12:09a Jeff + * added path value functions + * + * 43 4/20/99 8:56p Chris + * Fixed problem with robots not being able to open locked doors that a + * player has the key for. + * + * 42 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 41 4/02/99 10:18a Chris + * We can now mess with the Object_info anim stuff + * + * 40 3/17/99 11:47a Jeff + * exported function to get what language we are using + * + * 39 3/03/99 3:01a Chris + * Exported the difficulty stuff to OSIRIS + * + * 38 3/03/99 12:11a Chris + * one more AI path check function + * + * 37 3/03/99 12:07a Chris + * Added the two new OSIRIS predefs for AI path finding + * + * 36 3/01/99 7:20p Chris + * Fixed dist problems with finding nearby objects + * + * 35 2/28/99 11:30p Chris + * FindObjOfType and OSIRIS controllable deaths + * + * 34 2/22/99 8:22p Chris + * + * 33 2/22/99 8:17p Chris + * Added the LGoal_Value() function to OSIRIS + * + * 32 2/21/99 5:51p Chris + * FIxed a bug with OBJV_C_TYPE (It should have been OBJV_I_TYPE). Added + * Obj_FindType() + * + * 31 2/18/99 4:28p Jeff + * added lookup functions for matcen, paths, level goals + * + * 30 2/11/99 2:56a Jeff + * improvements to introcam + * + * 29 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 28 2/08/99 3:06a Jeff + * created a function to switch a player between ai and flying control + * types (not exported to osiris...instead used in multisafe) + * + * 26 2/03/99 12:43a Chris + * Added Obj_GetGroundPos + * + * 25 1/29/99 5:10p Chris + * Added an optional parent handle check for FindObjOfType + * + * 24 1/25/99 8:38a Chris + * The the GUID to AI_FollowPathSimple + * + * 23 1/25/99 7:43a Chris + * Added the GUID (Goal Unique Id) and added the ability for weapon + * batteries to always fire exactly forward. + * + * 22 1/24/99 8:17p Chris + * Updated AI and OSIRIS. Externalized fireball constants for spew and + * sparks. Added CreateRandomSparks to OSIRIS, renamed a bunch of AI + * Notify names to use underscores. Fixed a memory access leak in the + * matcen effect code. + * + * 21 1/22/99 4:47p Jeff + * + * 20 1/20/99 4:16p Jeff + * added ai notify child events and predefs to get ids of sound, room, + * trigger, object names + * + * 19 1/20/99 1:01a Chris + * Improved AI and OSIRIS intergration + * + * 18 1/15/99 5:59p Chris + * + * 17 1/13/99 5:20p Jeff + * added 4 new file operation predefs + * + * 16 1/13/99 3:25a Chris + * Added Obj_Burning and Obj_IsEffect to OSIRIS + * + * 15 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 14 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 13 1/05/99 12:24p Chris + * More OSIRIS improvements + * + * 12 1/04/99 6:43p Jeff + * added support to get/set mission flag values + * + * 11 1/04/99 12:42p Chris + * Updated OSIRIS with "xxxx_external.h" files and Obj_Value, and + * Matcen_XXX functions + * + * 10 12/30/98 3:46p Chris + * Incremental AI changes + * + * 9 12/23/98 12:10p Chris + * Added Gametime and Frametime to OSIRIS + * + * 8 12/22/98 4:52p Chris + * Added another defualt parameter to Objcreate + * + * 7 12/22/98 4:51p Chris + * Added a ObjCreate predefined function + * + * 6 12/22/98 2:46p Chris + * Added the AIValue predefined function + * + * 5 12/21/98 5:30p Samir + * predef for determining if object is being rendered. + * + * 4 12/17/98 6:42p Samir + * added functions to manipulate player controls + * + * 3 12/17/98 5:43p Jeff + * created timer system for osiris and save restore events + * + * 2 12/16/98 10:14p Jeff + * created osiris predefined function file +* +* $NoKeywords: $ +*/ + +#ifndef __OSIRIS_PREDEF_H_ +#define __OSIRIS_PREDEF_H_ + +#include "osiris_dll.h" +#include "CFILE.H" + +// osipf_CallObjectEvent +// Sends an event to an object. Returns true if the default action should +// continue to process. +bool osipf_CallObjectEvent(int objnum,int event,tOSIRISEventInfo *ei); + +// osipf_CallTriggerEvent +// Sends an event to a trigger. Returns true if the default action should +// continue to process. +bool osipf_CallTriggerEvent(int trignum,int event,tOSIRISEventInfo *ei); + +// Touches a sound file so it loads into memory +void osipf_SoundTouch(char *str); + +//searches for an object id given it's name +int osipf_ObjectFindID(char *name); + +int osipf_ObjectFindType(char *name); + +//searches through the weapons for a name and returns the id +int osipf_WeaponFindID(char *name); + +//returns how long an object has lived +float osipf_ObjectGetTimeLived(int objhandle); + +void osipf_GetGunPos(int objhandle,int gun_number,vector *gun_pnt,vector *gun_normal = NULL); + +// Returns room values (Index is optional and not always used) +void osipf_RoomValue(int roomnum, char op, char vhandle, void *ptr, int index = 0); +ubyte osipf_IsRoomValid(int roomnum); + +// Returns player only values (Index is optional and not always used) +void osipf_PlayerValue(int obj_handle, char op, char vhandle, void *ptr, int index = 0); +void osipf_ObjectCustomAnim(int handle, float start, float end, float time, char flags, int sound_handle = -1, char next_anim_type = -1); + +int osipf_GetAttachParent(int childhandle); +int osipf_GetNumAttachSlots(int objhandle); +int osipf_GetAttachChildHandle(int objhandle,char attachpoint); +int osipf_AttachObjectAP(int parenthandle,char parent_ap,int childhandle,char child_ap,ubyte f_use_aligned); +int osipf_AttachObjectRad(int parenthandle,char parent_ap,int childhandle,float percent_rad); +void osipf_UnattachFromParent(int objhandle); +void osipf_UnattachChild(int objhandle,char parent_ap); +void osipf_UnattachChildren(int objhandle); + +int osipf_RayCast(int objhandle,vector *p0,vector *p1,int start_roomnum,float rad,int flags,ray_info *ri); + +// searches through GamePath index and returns index of path matching name +// returns -1 if not found +int osipf_AIGetPathID(char *string); +vector osipf_AIFindHidePos(int hideobjhandle,int viewobjhandle,float time,int *hide_room); +int osipf_AIFindObjOfType(int objhandle,int type, int id, bool f_ignore_init_room, int parent_handle = OBJECT_HANDLE_NONE); +vector osipf_AIGetRoomPathPoint(int roomnum); +int osipf_AIFindEnergyCenter(int objhandle); +float osipf_AIGetDistToObj(int objhandle,int otherobjhandle); + +void osipf_AISetType(int objhandle,int type); +int osipf_AIPowerSwitch(int objhandle,ubyte f_power_on); + +int osipf_AIGoalAdd(int objhandle,int goal_type,int level,float influence,int guid,int flags, ... ); +int osipf_AIGoalAddEnabler(int objhandle,int goal_index,int enabler_type,float percent,float interval,void *ptr); +void osipf_AIGoalClear(int objhandle,int goal_index); +int osipf_AIGoalFollowPathSimple(int objhandle,int path_id,int guid, int flags, int slot = 3); +void osipf_AISetGoalCircleDist(int objhandle,int goal_handle,float dist); +int osipf_AISetGoalFlags(int objhandle,int goal_handle,int flags,ubyte f_enable); + +ubyte osipf_AITurnTowardsVectors(int objhandle,vector *fvec,vector *uvec); +ubyte osipf_AIMoveTowardsPosition(int objhandle, vector *pos, int *roomnum, float scalar, bool f_bline, bool f_bline_if_vis); +ubyte osipf_AIMoveTowardsDir(int objhandle, vector *dir, float scalar); + +void osipf_AIValue(int objhandle, char op, char vtype, void *ptr); + +//Reads the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO READ STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes read. +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int osipf_CFReadBytes(ubyte *buffer, int count, CFILE *cfp); + +// The following functions read numeric vales from a CFILE. All values are +// stored in the file in Intel (little-endian) format. These functions +// will convert to big-endian if required. +// These funtions will throw an exception of if the value cannot be read, +// so do not call these if you don't require the data to be present. + +//Read and return an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int osipf_CFReadInt(CFILE *cfp); + +//Read and return a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +short osipf_CFReadShort(CFILE *cfp); + +//Read and return a byte (8 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +sbyte osipf_CFReadByte(CFILE *cfp); + +//Read and return a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +float osipf_CFReadFloat(CFILE *cfp); + +//Read and return a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +double osipf_CFReadDouble(CFILE *cfp); + +//Reads a string from a CFILE. If the file is type binary, this +//function reads until a NULL or EOF is found. If the file is text, +//the function reads until a newline or EOF is found. The string is always +//written to the destination buffer null-terminated, without the newline. +//Parameters: buf - where the string is written +// n - the maximum string length, including the terminating 0 +// cfp - the CFILE pointer +//Returns the number of bytes in the string, before the terminator +//Does not generate an exception on EOF +int osipf_CFReadString(char *buf,size_t n,CFILE *cfp); + +//Writes the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO WRITE STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes written. +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int osipf_CFWriteBytes(const ubyte *buf,int count, CFILE *cfp); + +//Writes a null-terminated string to a file. If the file is type binary, +//the string is terminated in the file with a null. If the file is type +//text, the string is terminated with a newline. +//Parameters: buf - pointer to the string +// cfp = the CFILE pointer +//Returns the number of bytes written +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int osipf_CFWriteString(const char *buf,CFILE *cfp); + +// The following functions write numeric vales to a CFILE. All values are +// stored to the file in Intel (little-endian) format. +// All these throw an exception if there's an error on write. + + +//Write an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteInt(int i,CFILE *cfp); + +//Write a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteShort(short s,CFILE *cfp); + +//Write a byte (8 bits). If the byte is a newline & the file is a text file, writes a CR/LF pair. +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteByte(sbyte b,CFILE *cfp); + +//Write a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteFloat(float f,CFILE *cfp); + +//Write a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void osipf_CFWriteDouble(double d,CFILE *cfp); + + +// CONTROLLER predefs +// enables or disables controls specified. +void osipf_SetAllControls(bool enabled); + +// enable or disable specified function +void osipf_SetControls(int fn, bool enabled); + +// Creates an object +int osipf_ObjCreate(ubyte type,ushort id,int roomnum,vector *pos,const matrix *orient=NULL,int parent_handle = OBJECT_HANDLE_NONE,vector *velocity=NULL); + +// OBJECT Properties. +// is an object visible? (rendered current frame) +bool osipf_IsObjectVisible(object *obj); + +float osipf_GameTime(void); +float osipf_FrameTime(void); +void osipf_ObjWBValue(int obj_handle, char wb_index, char op, char vtype, void *ptr, char g_index = 0); + +void osipf_ObjectValue(int handle, char op, char vtype, void *ptr, int index = 0); +void osipf_MatcenValue(int handle, char op, char vtype, void *ptr, int index = 0); + +void osipf_MatcenReset(int handle); +void osipf_MatcenCopy(int dhandle, int shandle); +int osipf_MatcenCreate(char *name); +int osipf_MatcenFindId(char *str); + +// Sets/Clears mission flags +// flag is which mission flag to set/clear (1-32) +// value is 0 to clear, or 1 to set +void osipf_MissionFlagSet(int flag,ubyte value); + +// Gets a mission flag +// flag is what mission flag to get. Returns 1 if set, 0 if not. +int osipf_MissionFlagGet(int flag); + +void osipf_PlayerAddHudMessage(int handle, char *str); +void osipf_ObjGhost(int handle, bool f_ghost); +void osipf_ObjBurning(int handle, float time, float damage_per_second); +bool osipf_ObjIsEffect(int handle, int type_flag); + +// opens a cfile for reading/writing (works just like fopen()) +void *osipf_CFopen(const char *filename,const char *mode); + +// closes a cfile (like fclose()) +void osipf_CFclose(CFILE *file); + +// returns the current position of the file (like ftell()) +int osipf_CFtell(CFILE *file); + +// returns 1 if the file is at the EOF, else 0 (like feof()) +ubyte osipf_CFeof(CFILE *file); + +void osipf_SoundStop(int s_handle, bool f_immediately = false); +int osipf_SoundPlay2d(int obj_handle, int s_id, float volume = 1.0f); +int osipf_SoundPlay3d(int obj_handle, int s_id, float volume = 1.0f); +int osipf_SoundFindId(char *s_name); + +bool osipf_AIIsObjFriend(int obj_handle, int it_handle); +bool osipf_AIIsObjEnemy(int obj_handle, int it_handle); +void osipf_AIGoalValue(int obj_handle, char g_index, char op, char vtype, void *ptr, char index = 0); +int osipf_AIGetNearbyObjs(vector *pos, int init_roomnum, float rad, int *object_handle_list, int max_elements, bool f_lightmap_only, bool f_only_players_and_ais = true, bool f_include_non_collide_objects = false, bool f_stop_at_closed_doors = true); +char osipf_AIGetCurGoalIndex(int obj_handle); + +int osipf_FindSoundName(char *name); +int osipf_FindRoomName(char *name); +int osipf_FindTriggerName(char *name); +int osipf_FindObjectName(char *name); +int osipf_GetTriggerRoom(int trigger_id); +int osipf_GetTriggerFace(int trigger_id); +int osipf_FindDoorName(char *name); +int osipf_FindTextureName(char *name); +int osipf_FindMatcenName(char *name); +int osipf_FindPathName(char *name); +int osipf_FindLevelGoalName(char *name); + +void osipf_CreateRandomSparks(int num_sparks,vector *pos,int roomnum,int which_index,float force_scalar); + +void osipf_GetGroundPos(int objhandle,int ground_number,vector *ground_pnt,vector *ground_normal); + +//disable/enable ship +void osipf_EnableShip(char *ship_name,bool enable); +//is ship enabled +bool osipf_IsShipEnabled(char *ship_name); + +//turns the given player into AI mode or back to regular control mode +void osipf_SetPlayerControlMode(int pnum,bool set_to_ai); + +//gets information about a path point +//pass NULL for parameters not needed. +// pathid: path number +// point: which path point +//returns true if operation was successful +bool osipf_PathGetInformation(int pathid,int point,vector *pos,int *room,matrix *orient); + +void osipf_LGoalValue(char op, char vtype, void *ptr, int g_index = -1, int i_index = -1); +int osipf_ObjMakeListOfType(int objhandle, int type, int id, bool f_ignore_init_room, int parent_handle, int max_recorded, int *handles); + +void osipf_ObjKill(int handle, int killer_handle, float damage, int flags, float min_time, float max_time); +bool osipf_AIIsDestReachable(int handle, int room); +bool osipf_AIIsObjReachable(int handle, int target); +char osipf_GameGetDiffLevel(void); +int osipf_GetLanguageSetting(void); + +// Sets/Gets information about a path. +// If you change is PV_ALL (or any of it's individual components), pass in a pointer to an +// osiris_path_node_info struct. For the others, you must pass in an appropriate pointer +// (i.e. an int* for PV_I_NUMNODES). You can only set PV_ALL components. +// for PV_I_NUMNODES, path_id MUST be specified, node_id is ignored +// for PV_I_NUMPATHS, path_id and node_id are ignored +// for PV_CS_NAME, path_id MUST be specified, node_id is ignored +// for PV_ALL components, path_id and node_id MUST be valid. +void osipf_PathValue(int path_id, int node_id, char op, int changes, void *ptr); + +#endif diff --git a/Descent3/osiris_share.h b/Descent3/osiris_share.h new file mode 100644 index 000000000..ae00f192b --- /dev/null +++ b/Descent3/osiris_share.h @@ -0,0 +1,13 @@ +#ifndef __OSIRIS_SHARE_H_ +#define __OSIRIS_SHARE_H_ + +// include this file for access to the structs, types and defines shared by +// the osiris modules +#define INCLUDED_FROM_D3 +#ifdef MACINTOSH +#include "osiris_common.h" +#else +#include "../scripts/osiris_common.h" +#endif + +#endif \ No newline at end of file diff --git a/Descent3/pilot.cpp b/Descent3/pilot.cpp new file mode 100644 index 000000000..6ca225863 --- /dev/null +++ b/Descent3/pilot.cpp @@ -0,0 +1,4385 @@ +/* + * $Logfile: /DescentIII/main/pilot.cpp $ + * $Revision: 175 $ + * $Date: 3/20/00 12:07p $ + * $Author: Matt $ + * + * Player/Pilot configuration + * + * $Log: /DescentIII/main/pilot.cpp $ + * + * 175 3/20/00 12:07p Matt + * Merge of Duane's post-1.3 changes. + * Changed difficulty level to be a global variable instead of a function + * call + * + * 174 11/30/99 5:08p Jeff + * don't let the Black Pyro be selected in LInux + * + * 173 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 172 10/19/99 4:55p Jeff + * only let them select Black Pyro if they own Mercenary + * + * 171 10/04/99 9:57a Kevin + * #ifdef for demo, only pyro is available + * + * 170 9/12/99 12:09a Jeff + * fixed stupid axtoi bug + * + * 169 9/03/99 4:37p Jeff + * fixed bug related to highlighting an oaf if it is supposed to be + * selected in the pilot config dialog + * + * 168 7/23/99 2:36p Jeff + * put in warning message if imported oaf file is too large + * + * 167 7/06/99 11:47p Jeff + * Added delete buttons for audio taunts and logos + * + * 166 5/25/99 3:09a Jeff + * don't let a player start in secret levels if they beat the game + * + * 165 5/24/99 7:24p Samir + * use correct string length for DoEditDialog now. + * + * 164 5/23/99 2:22a Jeff + * don't update some mission stuff for secret levels. correctly update + * when finished the level + * + * 163 5/12/99 2:24p Jeff + * Descent3 now has a setable temp directory for all temp files + * + * 162 5/12/99 2:02p Samir + * don't open pilot interface until inputted name of new pilot. + * + * 161 5/10/99 10:23p Ardussi + * changes to compile on Mac + * + * 160 5/09/99 6:30a Jeff + * fixed copy controls bug when there was .pld's but only 1 plt + * + * 159 5/05/99 12:43a Jeff + * selected imported logo, don't stop sounds + * + * 158 5/03/99 8:38a Jeff + * fixed copy controls + * + * 157 5/02/99 12:55a Jeff + * save ship permissions at highest level achieved and use that on restore + * to a level previously played + * + * 156 5/01/99 12:17a Jeff + * adjusted config due to new artwork + * + * 155 4/29/99 5:52p Jeff + * if displaying 1 pilot pic, don't put index number at the end of name + * + * 154 4/29/99 2:19a Samir + * updated art for options style menu. + * + * 153 4/28/99 5:06p Jeff + * widened copy controls dialog + * + * 152 4/27/99 1:56p Jeff + * audio taunts stuff in pilot menu, added stringtables + * + * 151 4/26/99 2:13p Jeff + * go right into preset select on new pilot + * + * 150 4/25/99 4:53p Jeff + * fixed startframe/endframe bugs in D3D + * + * 149 4/25/99 2:32a Jeff + * fixed bug trying to read pilot files that are too new + * + * 148 4/23/99 9:52p Jeff + * fixed messed up string for ship selection + * + * 147 4/21/99 4:54p Jeff + * display name of pilot being configured + * + * 146 4/21/99 12:58p Samir + * progress bar for ship config. + * + * 145 4/21/99 12:43p Samir + * redid pilot taunt menu for newui. + * + * 144 4/20/99 7:28p Jeff + * added guidebot name + * + * 143 4/18/99 7:55p Samir + * new progress indicator for delays when loading data. + * + * 142 4/16/99 6:00p Kevin + * Bunches of Demo stuff + * + * 141 4/16/99 12:05p Matt + * Changed code to use cfile functions, & took out include of io.h. + * + * 140 4/16/99 12:39a Matt + * Took out Linux ifdef around include of io.h, since it's a system header + * file and there's no harm in including it in the Windows version. + * + * 139 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 138 4/09/99 7:04p Jason + * changed some texture defines + * + * 137 4/04/99 3:27p Jeff + * localized pilot.cpp + * + * 136 4/03/99 9:26p Jeff + * changed dialogs that weren't using UID_OK and UID_CANCEL to use and + * handle them properly + * + * 135 4/03/99 2:18a Jeff + * added profanity filter stuff + * + * 134 3/30/99 7:41p Jeff + * error handling...reset ppic if there isn't a valid one + * + * 133 3/30/99 5:30p Jeff + * fixed bug when canceling out of multiplayer ship config + * + * 132 3/24/99 1:41p Jeff + * some dedicated server fixups...ability to set number of teams + * + * 131 3/23/99 4:26p Jeff + * new pilot choose dialog + * + * 130 3/23/99 12:45p Jeff + * added preset control selection for pilot + * + * 129 3/22/99 6:22p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 128 3/19/99 9:14p Jeff + * converted multiplayer ship selection dialog + * + * 127 3/18/99 2:33p Jeff + * fixed bug clearing the Current pilot stuff before we were done using it + * + * 126 3/17/99 11:48a Jeff + * enter selects pilot on dialog display + * + * 125 3/15/99 9:24p Gwar + * + * 124 3/15/99 4:31p Jeff + * fixed some memory leaks + * + * 123 3/04/99 6:07p Samir + * fixed bug with entering pilot names for the first time (buffer sent to + * DoEditDialog was not null terminated.) Also added wait screen. + * + * 122 3/04/99 11:40a Jeff + * better error messages on pilot write error + * + * 121 3/03/99 5:09p Samir + * fixed audio taunt combobox. Passed incorrect flag values. + * + * 120 3/01/99 4:39p Samir + * made AddOption to AddSimpleOption for options without sheets. + * + * 119 2/28/99 6:05p Jeff + * use UID_OK and UID_CANCEL + * + * 118 2/28/99 3:17p Jeff + * multiplayer ship selection only has Pyro-GL + * + * 117 2/28/99 3:06a Jeff + * converted "select pilot pic" dialog + * + * 116 2/27/99 4:18p Jeff + * added support for .pld files (used to copy pilot default controls), + * audio taunt size import error fixed...removed dead code + * + * 115 2/25/99 4:30p Jeff + * mission data of pilot keeps track of all missions, not just after you + * beat a level + * + * 114 2/23/99 7:34p Jeff + * use new ui for add dialog + * + * 113 2/23/99 1:47a Jeff + * attempted to convert add new pilot + * + * 109 2/19/99 12:14a Jeff + * start of new ui conversion + * + * 108 2/15/99 7:50p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 107 2/10/99 4:45p Jeff + * table file parser stuff + * + * 106 1/29/99 5:22p Jeff + * localization + * + * 105 1/27/99 5:47p Jeff + * audio taunts implemented! + * + * 104 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 103 1/16/99 2:55p Jeff + * mission data doesn't get updated in a multiplayer game + * + * 102 1/11/99 4:08p Jason + * added multiplayer taunt macros + * + * 101 12/30/98 6:51p Matt + * Fixed compile warnings + * + * 100 12/17/98 5:59p Samir + * moved mouse enabled and joy enabled to config menu. + * + * 99 12/17/98 12:44p Samir + * fixed bugs in writing 0 length strings in pilot file! + * + * 98 12/16/98 1:57p Samir + * added finished field to mission data structure. + * + * 97 12/15/98 4:28p Jeff + * added mission data information to the pilot files to save what the + * highest level they achieved on a mission is. Added level select dialog + * (not hooked up) and level warp cheat. + * + * 96 12/03/98 11:06a Samir + * added axis sensitivity + * + * 95 12/02/98 11:43a Samir + * added better code to handle changes in controller function list for + * pilot files (needed to have a constant giving number of controller + * functions in demo 1.0) + * + * 94 12/01/98 5:47p Jeff + * created pilot picture selection dialog + * + * 93 11/30/98 11:56a Jeff + * fixed bug, allowing unlimited pilots + * + * 92 11/23/98 11:25a Jeff + * fixed import animated bitmap message box bug + * + * 91 10/27/98 2:31p Jeff + * adjusted play button for audio taunts on ship config dialog + * + * 90 10/23/98 2:58p Samir + * set defaults for controller sensitivities. + * + * 89 10/22/98 10:55p Jeff + * return error if reading a pilot file that is newer than we support + * + * 88 10/22/98 10:38p Jeff + * a decent pilot file version check for later pilot file versions + * + * 87 10/22/98 2:58p Chris + * Difficulty levels are in beta + * + * 86 10/22/98 2:41p Samir + * fixed autoselection for good. + * + * 85 10/22/98 2:25p Jeff + * fixed bug + * + * 84 10/22/98 1:35p Jeff + * + * 83 10/22/98 1:30p Jeff + * brought back difficulty + * + * 82 10/21/98 11:54p Samir + * fixed typos. + * + * 81 10/21/98 7:15p Samir + * added joy and mouse sensitivities for pilot. + * + * 80 10/21/98 10:36a Samir + * added code to turn on or off joystick or mouse. + * + * 79 10/20/98 1:41a Jeff + * a couple more improvements to ImportGraphic + * + * 78 10/20/98 12:59a Jeff + * fixed pilot import bitmap + * + * 77 10/19/98 10:41a Jeff + * moved pilot select window down to uncover "demo" + * + * 76 10/18/98 10:07p Jeff + * automatically chooses the last used pilot + * + * 75 10/17/98 7:31p Samir + * added invertible axes + * + * 74 10/15/98 1:36p Jeff + * allow cancel out of pilot select menu + * + * 73 10/15/98 11:48a Samir + * fixed pilot create so it initializes the proper defaults. + * + * 72 10/14/98 6:39p Samir + * save screen size for game. + * + * 71 10/14/98 2:48p Kevin + * Changed memory code to comply with mem lib + * + * 70 10/12/98 3:02p Jeff + * added a verify function, give warning when they go into Multiplayer + * ship customize with a bad ship + * + * 69 10/11/98 3:02a Jeff + * handle the case where a player has a ship selected in his pilot file, + * but the ship doesn't exist in the game + * + * 68 10/09/98 3:32p Kevin + * New memory library + * + * 67 10/09/98 3:36p Jeff + * attempted to fix Pyro-SE for demo again + * + * 66 10/09/98 3:06p Jeff + * fixed default_ship for demo + * + * 65 10/08/98 6:41p Jeff + * when creating your first pilot it immediatly returns you to main menu + * + * 64 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 63 10/06/98 5:34p Jeff + * various UI changes/improvements + * + * 62 10/01/98 2:19p Samir + * took out DEMO define. + * + * 61 10/01/98 12:58p Samir + * save autoselect ordering. + * + * 60 9/29/98 11:20a Jeff + * set buffer length on pilotcreate to correct size + * + * 59 9/28/98 4:35p Jeff + * general UI changes and improvements + * + * 58 9/24/98 10:45a Jeff + * keep PyroGL the only ship to be allowed to be selected + * + * 57 9/23/98 6:19p Jeff + * finished up (hopefully) updating the config/ui dialogs to meet our + * standard. Keyboard/joystick config still needs some work + * + * 56 9/23/98 3:07p Jeff + * updated the colors and various other items of config and UI + * + * 55 9/22/98 3:56p Samir + * special demo code doesn't allow pilot and mission stuff. + * + * 54 9/08/98 11:41a Jeff + * new pilot selection interface + * + * 53 9/04/98 3:52p Jeff + * changes made from UI meeting + * + * 52 9/04/98 1:20p Jeff + * updates to ship selection, now includes audio taunts, strips crc's from + * filenames when displaying + * + * 51 9/02/98 2:54p Jeff + * added defines for text colors to be used throughout the game...fixed up + * buddy menu too + * + * 50 8/31/98 5:20p Jeff + * put in callback for ship selection UI + * + * 49 8/31/98 12:38p Samir + * don't call setlistindex. + * + * 48 8/29/98 6:53p Jeff + * added single-player ship selection + * + * 47 8/15/98 5:16p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 46 8/14/98 2:24p Jeff + * give error message on error import + * + * 45 8/06/98 4:59p Jeff + * now imports graphic files to ogfs + * + * 44 8/04/98 5:41p Jeff + * fixed bug if user selected none for texture + * + * 43 8/03/98 12:20p Jeff + * fixed some more bugs in ship config + * + * 42 8/03/98 10:42a Jeff + * forgot to DrawPolymodel with effect on ship config + * + * 41 7/30/98 2:48p Jeff + * + * 40 7/30/98 12:32p Jeff + * everything working for ship customization, including importing of ifl + * + * 39 7/29/98 5:39p Jeff + * updated + * + * 38 7/28/98 4:16p Jeff + * ship dialog is in and working good + * + * 37 7/27/98 6:26p Jeff + * basic implementation of ship configurations...needs to be purtied up + * + * 36 6/22/98 7:31p Samir + * added UIEdit::Activate, which activates an edit box manually. + * + * 35 6/19/98 5:39p Samir + * save out hud mode too. + * + * 34 6/19/98 3:32p Samir + * initialize hud layout in PilotInit. + * + * 33 6/19/98 3:30p Samir + * added hud layout info in pilot file. + * + * 32 6/18/98 4:48p Samir + * added changes for multiple configs for joystick controls. + * + * 31 6/17/98 3:28p Jeff + * localization changes. made an init function + * + * 30 6/16/98 10:54a Jeff + * + * 29 6/12/98 5:56p Jeff + * localization test + * + * 28 6/01/98 10:57a Jeff + * Fixed Pilot read bug. Fixed name length on Pilot create dialog + * + * 27 5/24/98 2:56a Jeff + * Pilot dialogs up to date + * + * 26 4/23/98 11:14p Samir + * added read controller flag to pilot + * + * 25 4/14/98 7:31p Matt + * Changed code to use ddio_MakePath() instead of sprintf() to create file + * spec + * + * 24 4/13/98 7:01p Samir + * added snazzy listbox and edit box art. + * + * 23 4/02/98 7:58p Samir + * Fixed up control setting saving and restoring. + * + * 22 4/01/98 3:34p Jeff + * ship_model is now a string + * + * 21 3/23/98 11:09a Jeff + * Fixed up the "Choose A Pilot" window + * + * 20 3/20/98 5:34p Jeff + * Added Copy Controls from a pilot support + * + * 19 3/20/98 1:19p Jeff + * Changes made to use Default_pilot string for pilot filename to use. + * + * 18 3/19/98 6:57p Jeff + * Pilot stuff reads and writes to the correct directories + * + * 17 3/18/98 7:49p Samir + * Maybe fixed controller config init mess. + * + * 16 3/16/98 3:26p Samir + * Fixed controller need ID and index discrepancy. + * + * 15 3/13/98 8:55p Jeff + * Various changes to move control configuration into Pilot file + * + * 14 3/13/98 5:32p Jeff + * close the display window before displaying delete confirmation box + * + * 13 3/13/98 5:19p Jeff + * UIListBoxes have scroll button code now + * + * 12 3/12/98 7:10p Jeff + * double clicking on pilot list selects pilot + * + * 11 3/12/98 2:00p Jeff + * Various changes to improve pilot dialogs + * + * 10 3/11/98 5:38p Jeff + * Now use the NewUIMessageBox for small windows + * + * 9 3/10/98 7:08p Jeff + * Various changes due to new window class + * + * 8 3/10/98 11:57a Jeff + * Corrected some function comments and made '.' a valid filename + * character + * + * 7 3/10/98 11:50a Jeff + * Added filename field to pilot structure, which keeps track of the pilot + * filename...fixes any bugs that come with renaming a file. Made changes + * to take advantage of this. + * + * 6 3/10/98 11:12a Jeff + * Made various changes to accomodate Samir's Listbox callback paradigm + * + * 5 3/09/98 6:27p Jeff + * Cleaned up code, made file operations more robust, pretty sturdy now + * + * 4 3/09/98 4:00p Jeff + * Various improvements + * + * 3 3/06/98 6:32p Jeff + * Added Pilot files and major functionality + * + * 2 3/05/98 4:28p Jeff + * Initial creation +* +* $NoKeywords: $ +*/ + +#include +#include +#include + +#include "pilot.h" +#include "mono.h" +#include "renderer.h" +#include "render.h" +#include "ddio.h" +#include "descent.h" +#include "game.h" +#include "CFILE.H" +#include "application.h" +#include "manage.h" +#include "newui.h" +#include "grtext.h" +#include "gamefont.h" +#include "ConfigItem.h" +#include "ctlconfig.h" +#include "hud.h" +#include "stringtable.h" +#include "gametexture.h" +#include "vclip.h" +#include "hlsoundlib.h" +#include "weapon.h" +#include "config.h" +#include "difficulty.h" +#include "PilotPicsAPI.h" +#include "pstring.h" +#include "Mission.h" +#include "mem.h" +#include "polymodel.h" +#include "audiotaunts.h" +#include "streamaudio.h" +#include "ship.h" +#include "dedicated_server.h" + +//some general defines +#define IDP_SAVE 10 +#define IDP_CANCEL 11 +#define IDP_APPLY 12 + +//used for the Pilot file functions +#define PLTWILDCARD "*.plt" +#define PLTEXTENSION ".plt" +#define DPLTWILDCARD "*.pld" +#define PLTFILELEN 260 + +#define MAX_AUDIOTAUNTSIZE (32*1024) + +// !!!!!(PLEASE VERSION CHECK ON READ TO MAINTAIN PILOT COMPAT.)!!!! +// 0x20 NEW PILOT FILE READ->WRITE +// 0x12 ???? +// 0x11 added finished field to mission data -Samir +// 0x10 added mission data - Jeff +// 0xf write out multiple joystick and mouse sensitivities.-Samir +// 0xe write out number of controller functions in file-Samir +// 0xd added pilot picture id +// 0xc added mouse and joy sensitivities -Samir. +// 0xb added invertible axes - Samir +// 0xa Added game window size-Samir +// 0x9 Added weapon selection info -Samir +// 0x8 Added audio taunt files - Jeffrey +// 0x7 Added ship logo - Jeff +// 0x6 Added default hud mode- Samir +// 0x5 Added hud layout info- Samir +// 0x4 Added read controller flag- Samir. +#define PLTFILEVERSION 0x12 + +//border size of the UI window +#define UI_BORDERSIZE 20 + +/////////////////////////////////////////////// +// Externals (Globals for the game) + +//Game global for current pilot +pilot Current_pilot; +char Default_pilot[_MAX_PATH] = {" "}; +ubyte ingame_difficulty = 1; + + +/////////////////////////////////////////////// +// Internals (Globals for the file) + +bool DisplayFileDialog(char *path,char *title,char *wildcards,int flags); + +//internal function prototypes +bool PltDelete(pilot *Pilot); +void NewPltUpdate(newuiListBox *list,char **flist,int filecount,int selected,char *filename=NULL); +bool PilotChoose(pilot *Pilot,bool presets=false); +bool PltCopyKeyConfig(pilot *src,pilot *dest); +bool PltSelectShip(pilot *Pilot); +void ShipSelectCallBack(int c); +void CustomCallBack(int c); +void PilotCopyDefaultControls(pilot *Pilot); + +int Pilot_NewRead(pilot *Pilot,bool read_keyconfig,bool read_missiondata); +int Pilot_NewWrite(pilot *Pilot,bool newpilot); + +typedef struct{ + newuiListBox *custom_bitmap_list; + int needed_size; //size of allocated memory for files + char *files; //string list of file names + // Initializes the struct + void Init(){ + files = NULL; + custom_bitmap_list = NULL; + needed_size = 0; + } + // Frees and resets the struct + void Reset(){ + if(files){ + mem_free(files); + files = NULL; + } + custom_bitmap_list = NULL; + needed_size = 0; + } +}tCustomListInfo; + +typedef struct +{ + newuiComboBox *taunt_a,*taunt_b,*taunt_c,*taunt_d; +}tAudioTauntComboBoxes; + +// Deletes the currently selected audio taunt #4 +void ShipSelectDeleteTaunt(pilot *Pilot,tCustomListInfo *cust_snds,newuiComboBox *lb,tAudioTauntComboBoxes *taunt_boxex); + +// Deletes the currently selected ship logo +void ShipSelectDeleteLogo(tCustomListInfo *cust_bmps,newuiListBox *lb); + +// ------------------------------------------------------- +// ShowPilotPicDialog +// Purpose: +// Displays the dialog to choose a pilot picture for +// the given pilot. +// ------------------------------------------------------- +void ShowPilotPicDialog(pilot *Pilot); + + +UITextItem *pilot_items=NULL; //array of UITextItems for use in Pilot listbox +pilot temp; //pilot in use by the listbox +NewUIGameWindow *PilotDisplayWindow; //pointer to display_window (needed for listbox callback) +char **filelist; //list of pilot filenames +int filecount; //number of pilot filenames found +void PilotListSelectChangeCallback(int index); + +//////////////////////////////////////////////////////////////////////////// +// Dialog Functions +//////////////////////////////////////////////////////////////////////////// +void PilotInitData(pilot *plt); + +void PilotShutdown(void) +{ + Current_pilot.flush(false); + Current_pilot.clean(false); +} + +void PilotInit(void) +{ + PilotInitData(&Current_pilot); + atexit(PilotShutdown); +} + +void PilotInitData(pilot *plt) +{ + if(!plt) + { + Int3(); + }else + { + plt->clean(true); + } +} + + + +// VerifyPilotData +// +// Call this function to check the data that is in the given pilot struct...it will verify that all files +// listed are available, if they are not, then it will set them to defaults. Returns true if it had to +// fix the data (you may want to save the pilot immediatly) +bool VerifyPilotData(pilot *Pilot) +{ + if(!Pilot){ + Int3(); + return false; + } + Pilot->verify(); + return false; +} + +/* +*********************************************************** +*/ + +#define IDP_SELECT 0x50 +#define IDP_SEL_PILOTLIST 0x51 +#define IDP_DELETE 0x52 +#define IDP_ADD 0x53 +#define IDP_EDIT 0x54 +#define IDP_SHIPCONFIG 0x55 +#define IDP_CHOOSEPIC 0x56 +#define IDP_CONFIGCONT 0x57 +#define IDP_CONFIGKEYB 0x58 +#define IDP_COPYCONTROLS 0x59 + +struct pilot_select_menu +{ + newuiSheet *sheet; + + newuiListBox *pilot_list; + +// sets the menu up. + newuiSheet *setup(newuiMenu *menu) + { + sheet = menu->AddOption(IDP_SELECT, TXT_PILOTS, NEWUIMENU_MEDIUM); + + // Pilot selection list + sheet->NewGroup(NULL, 6, 18); + pilot_list = sheet->AddListBox(232,168,IDP_SEL_PILOTLIST); + pilot_list->SetSelectChangeCallback(PilotListSelectChangeCallback); + + return sheet; + }; + +// retreive values from property sheet here. + void finish() + { + sheet = NULL; + }; + +// process + void process(int res) + { + }; +}; + +struct pilot_edit_menu +{ + newuiSheet *sheet; + + int *difficulty; + bool *profanity; + bool *audiotaunts; + char *pilot_name; + + newuiSheet *setup(newuiMenu *menu) + { + sheet = menu->AddOption(IDP_EDIT, TXT_CONFIGURE, NEWUIMENU_MEDIUM); + + //pilot name + sheet->NewGroup(NULL,5,0); + pilot_name = sheet->AddChangeableText(64); + + // difficulty + sheet->NewGroup(TXT_PLTDIFFICULT,55, 12); +#if 1 //ndef DEMO + difficulty = sheet->AddFirstLongRadioButton(TXT_TRAINEE); + sheet->AddLongRadioButton(TXT_ROOKIE); + sheet->AddLongRadioButton(TXT_HOTSHOT); + sheet->AddLongRadioButton(TXT_ACE); + sheet->AddLongRadioButton(TXT_INSANE); + *difficulty = 0; +#else + difficulty = sheet->AddFirstLongRadioButton(TXT_TRAINEE); + sheet->AddLongRadioButton(TXT_HOTSHOT); + *difficulty = 0; +#endif + sheet->NewGroup(TXT_CONTROLSCONFIG, 55, 93); + sheet->AddLongButton(TXT_CPYKEYCONF, IDP_COPYCONTROLS); + sheet->AddLongButton(TXT_CUSTKEYB,IDP_CONFIGKEYB); + sheet->AddLongButton(TXT_CUSTGAMEC,IDP_CONFIGCONT); + + sheet->NewGroup(TXT_MULTIPLAYERCONFIG, 55, 150); +#if (!defined(OEM) && !defined(DEMO)) + sheet->AddLongButton(TXT_SELPILOTPIC, IDP_CHOOSEPIC); +#endif + sheet->AddLongButton(TXT_SHIPCUSTOMIZE,IDP_SHIPCONFIG); + + sheet->NewGroup(TXT_MISCELLANEOUS,55,195); + profanity = sheet->AddLongCheckBox(TXT_PROFFILTER); + audiotaunts = sheet->AddLongCheckBox(TXT_AUDIOTAUNTS); + + return sheet; + }; + + // retreive values from property sheet here. + void finish() + { + sheet = NULL; + }; + + // process + void process(int res) + { + }; +}; + +struct pilot_add_menu +{ + newuiSheet *sheet; +}; + +struct +{ + newuiMenu *menu; + pilot_select_menu *select; + pilot_edit_menu *edit; + + bool initial_call; + bool all_setup; +}PilotChooseDialogInfo; +pilot working_pilot; + +void PilotListSelectChangeCallback(int index) +{ + if(!filecount || !PilotChooseDialogInfo.all_setup) + return; + + pilot *Pilot = &working_pilot; + char name[PILOT_STRING_SIZE]; + ubyte difficulty; + bool profanity,audiotaunts; + bool in_edit = false; + + if(PilotChooseDialogInfo.menu->GetCurrentOption()==IDP_EDIT) + { + in_edit = true; + PilotChooseDialogInfo.edit->sheet->Realize(); + } + + //Pilot has changed...reset all data to new pilot selected + if(!PilotChooseDialogInfo.initial_call) + { + //save out old Pilot file so we can load up the new one + char filename[PAGENAME_LEN]; + working_pilot.get_filename(filename); + + if(cfexist(filename)) + { + if(in_edit) + PilotChooseDialogInfo.edit->sheet->UpdateReturnValues(); + + //only save if the file is already there + //which keeps us from bringing deleted pilots + //back from the dead. + difficulty = *PilotChooseDialogInfo.edit->difficulty; + profanity = *PilotChooseDialogInfo.edit->profanity; + audiotaunts = *PilotChooseDialogInfo.edit->audiotaunts; + + Pilot->set_profanity_filter(profanity); + Pilot->set_difficulty(difficulty); + Pilot->set_audiotaunts(audiotaunts); + PltWriteFile(&working_pilot); + mprintf((0,"Pilot saved\n")); + }else + { + mprintf((0,"Skipping pilot save...has the old pilot been deleted?\n")); + } + + working_pilot.clean(true); + } + + Pilot->set_filename(filelist[index]); + PltReadFile(Pilot); + + // Setup all values + /////////////////////////////// + Pilot->get_difficulty(&difficulty); + Pilot->get_profanity_filter(&profanity); + Pilot->get_audiotaunts(&audiotaunts); + *PilotChooseDialogInfo.edit->difficulty = difficulty; + *PilotChooseDialogInfo.edit->profanity = profanity; + *PilotChooseDialogInfo.edit->audiotaunts = audiotaunts; + + + if(in_edit) + PilotChooseDialogInfo.edit->sheet->UpdateChanges(); + Pilot->get_name(name); + mprintf((0,"Pilot has changed to: %s\n",name)); + + if(PilotChooseDialogInfo.edit->pilot_name){ + strncpy(PilotChooseDialogInfo.edit->pilot_name,name,63); + PilotChooseDialogInfo.edit->pilot_name[63] = '\0'; + } + + + PilotChooseDialogInfo.initial_call = false; +} + +void selectcb(newuiMenu *menu,short id, void *data) +{ + pilot_select_menu *select = (pilot_select_menu *)data; + if(id==IDP_SELECT) + { + menu->SetFocusOnGadget(select->pilot_list); + } +} + +void PilotSelect(void) +{ + newuiMenu menu; + pilot_select_menu select; + pilot_edit_menu edit; + + PilotChooseDialogInfo.menu = &menu; + PilotChooseDialogInfo.select = &select; + PilotChooseDialogInfo.edit = &edit; + PilotChooseDialogInfo.initial_call = true; + PilotChooseDialogInfo.all_setup = false; + filelist = NULL; + filecount = 0; + + int res=-1; + bool done = false; + + if(cfexist(Default_pilot)!=CF_NOT_FOUND){ + //ok so the default pilot file is around, mark this as the current pilot + Current_pilot.set_filename(Default_pilot); + PltReadFile(&Current_pilot); + } + + char pfilename[_MAX_FNAME]; + + // open menu + menu.Create(); + + select.setup(&menu); // setup pilot select menu IDP_SELECT + edit.setup(&menu); // edit pilot + menu.AddSimpleOption(IDP_ADD,TXT_ADD); // add + menu.AddSimpleOption(IDP_DELETE,TXT_DELETE); // delete currently selected pilot + menu.AddSimpleOption(UID_OK, TXT_OK); + menu.AddSimpleOption(UID_CANCEL,TXT_CANCEL); + menu.SetCurrentOption(IDP_SELECT); + + menu.SetOnOptionFocusCB(selectcb,&select); + + PilotChooseDialogInfo.all_setup = true; + filelist = PltGetPilots(&filecount); + + if(!filecount){ + pilot temp_pilot; + //if there are currently no pilots force player to create a new pilot + if(PilotCreate(&temp_pilot,true)){ + PltClearList(); + PltGetPilotsFree(); + filelist = PltGetPilots(&filecount); + NewPltUpdate(select.pilot_list,filelist,filecount,filecount-1); + + int index = select.pilot_list->GetCurrentIndex(); + + PilotListSelectChangeCallback(index); + + PilotCopyDefaultControls(&working_pilot); + + menu.SetCurrentOption(IDP_EDIT); + } + } + + Current_pilot.get_filename(pfilename); + NewPltUpdate(select.pilot_list,filelist,filecount,0,pfilename); + + //if we get here than there is at least one pilot already + char old_file[_MAX_FNAME]; + + //use this in case they cancel out + Current_pilot.get_filename(pfilename); + if(cfexist(pfilename)!=CF_NOT_FOUND){ + strcpy(old_file,pfilename); + }else{ + old_file[0] = '\0'; + } + + DoWaitMessage(false); + + menu.Open(); + + // run menu + do + { + res = menu.DoUI(); + + switch(res) + { + case IDP_EDIT: + { + if(!filecount) + { + menu.SetCurrentOption(IDP_SELECT); + } + }break; + + case IDP_SEL_PILOTLIST: + case UID_OK: + { + //Start Game + if(filecount){ + int index = select.pilot_list->GetCurrentIndex(); + + PilotListSelectChangeCallback(index); + + char filename[PAGENAME_LEN]; + working_pilot.get_filename(filename); + Current_pilot.set_filename(filename); + PltReadFile(&Current_pilot,true,true); + + char pname[PILOT_STRING_SIZE]; + Current_pilot.get_name(pname); + mprintf((0,"Pilot To Use: %s\n",pname)); + + if(VerifyPilotData(&Current_pilot)){ + //save out updated pilot since it had to be fixed + mprintf((0,"PILOT: Saving out Pilot info due to bad data in pilot file\n")); + PltWriteFile(&Current_pilot); + } + + Current_pilot.get_filename(Default_pilot); + + done = true; + + }else{ + DoMessageBox(TXT_PLTERROR,TXT_NEEDTOCREATE,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + menu.SetCurrentOption(IDP_SELECT); + } + }break; + + case UID_CANCEL: + { + // Cancel out + bool found_old = (cfexist(old_file)!=CF_NOT_FOUND); + bool display_error; + + if(filecount && found_old) + display_error = false; + else + display_error = true; + + if(filecount) + { + int index = select.pilot_list->GetCurrentIndex(); + PilotListSelectChangeCallback(index); + + if(found_old){ + Current_pilot.set_filename(old_file); + PltReadFile(&Current_pilot,true,true); + + char pname[PILOT_STRING_SIZE]; + Current_pilot.get_name(pname); + + mprintf((0,"Pilot To Use: %s\n",pname)); + if(VerifyPilotData(&Current_pilot)){ + //save out updated pilot since it had to be fixed + mprintf((0,"PILOT: Saving out Pilot info due to bad data in pilot file\n")); + PltWriteFile(&Current_pilot); + } + done = true; + } + } + + if(display_error) + { + if(filecount>0 && old_file[0]!='\0'){ + DoMessageBox(TXT_PLTERROR,TXT_OLDPILOTNOEXIST,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + }else{ + DoMessageBox(TXT_PLTERROR,TXT_NEEDTOCREATE,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + } + } + + }break; + + case IDP_DELETE: + //delete a pilot + if(filecount) + { + pilot temp_pilot; + char buff[200]; + int tindex=select.pilot_list->GetCurrentIndex(); + + PilotListSelectChangeCallback(tindex); + + temp_pilot.set_filename(filelist[tindex]); + PltReadFile(&temp_pilot); + + char pname[PILOT_STRING_SIZE]; + temp_pilot.get_name(pname); + + sprintf(buff,TXT_PLTOKDEL,pname); + if(DoMessageBox(TXT_PLTDELCONF,buff,MSGBOX_YESNO,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL)) { + PltDelete(&temp_pilot); + } + + PltClearList(); + PltGetPilotsFree(); + filelist = PltGetPilots(&filecount); + if(tindex>=filecount){ + tindex = filecount-1; + } + NewPltUpdate(select.pilot_list,filelist,filecount,tindex); + } + break; + + case IDP_ADD: + { + //Create New Player + bool go_into_edit = false; + int cpilotindex = select.pilot_list->GetCurrentIndex(); + pilot temp_pilot; + if(PilotCreate(&temp_pilot,!filecount)){ + PltClearList(); + PltGetPilotsFree(); + filelist = PltGetPilots(&filecount); + + char pfilename[_MAX_FNAME]; + temp_pilot.get_filename(pfilename); + NewPltUpdate(select.pilot_list,filelist,filecount,filecount-1,pfilename); + go_into_edit = true; + }else{ + if(filecount) + NewPltUpdate(select.pilot_list,filelist,filecount,cpilotindex); + } + + int index = select.pilot_list->GetCurrentIndex(); + PilotListSelectChangeCallback(index); + + if(go_into_edit) + { + PilotCopyDefaultControls(&working_pilot); + } + + if(go_into_edit) + menu.SetCurrentOption(IDP_EDIT); + else + menu.SetCurrentOption(IDP_SELECT); + }break; + + case IDP_SHIPCONFIG: + { + PltSelectShip(&working_pilot); + }break; + + case IDP_CHOOSEPIC: + { + char pname[PILOT_STRING_SIZE]; + working_pilot.get_name(pname); + + if(PPic_QueryPilot(pname)) + { + ShowPilotPicDialog(&working_pilot); + }else + { + ushort pid; + pid = PPIC_INVALID_ID; + working_pilot.set_multiplayer_data(NULL,NULL,NULL,&pid); + + DoMessageBox(TXT_PLTERROR,TXT_NOPICSAVAIL,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + } + + }break; + + case IDP_CONFIGCONT: + case IDP_CONFIGKEYB: + { + if(!filecount){ + break; + } + + int index = select.pilot_list->GetCurrentIndex(); + + //this saves out the pilot we're currently working on + PilotListSelectChangeCallback(index); + + //read the working pilot into the Current_pilot position + char pfilename[_MAX_FNAME]; + working_pilot.get_filename(pfilename); + + Current_pilot.set_filename(pfilename); + PltReadFile(&Current_pilot,true,true); + //configure the current pilot + if(res==IDP_CONFIGKEYB) + CtlConfig(CTLCONFIG_KEYBOARD); + else + CtlConfig(CTLCONFIG_CONTROLLER); + //now save the current_pilot out to disk + PltWriteFile(&Current_pilot,false); + + PltReadFile(&working_pilot,true,true); + }break; + + case IDP_COPYCONTROLS: + { + pilot s_pil; + char pfilename[_MAX_FNAME]; + working_pilot.get_filename(pfilename); + + // destroy the current list of pilots and recreate, but + // ignoring our current pilot + PltClearList(); + PltGetPilotsFree(); + filelist = PltGetPilots(&filecount,pfilename,1); + + if(filecount>=1) + { + if(PilotChoose(&s_pil)) + { + char spfilename[_MAX_FNAME]; + s_pil.get_filename(spfilename); + + if(strcmp(spfilename,pfilename)==0) + { + //user choose the same file as what he is configuring + DoMessageBox(TXT_COPYCONFERR,TXT_COPYCONFERR1,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + } + else + { + PltCopyKeyConfig(&s_pil,&working_pilot); + } + } + }else + { + DoMessageBox(TXT_PLTERROR,TXT_NOPILOTSTOCOPY,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + } + + // Restore the pilot list, ignoring none + PltClearList(); + PltGetPilotsFree(); + filelist = PltGetPilots(&filecount); + }break; + } + + }while(!done); + + PltClearList(); + PltGetPilotsFree(); + Current_pilot.get_difficulty(&ingame_difficulty); + + // get settings + select.finish(); + edit.finish(); + menu.Close(); + menu.Destroy(); +} + + +////////////////////////////////////////////////////////////// +//Brings up the Create A New Pilot Dialog +bool PilotCreate(pilot *Pilot,bool forceselection) +{ + bool done = false; + bool ret; + bool to_ret = false; + char pname[PILOT_STRING_SIZE]; + + while(!done) + { + pname[0] = 0; + ret = DoEditDialog(TXT_PLTENTERNAME1,pname,PILOT_STRING_SIZE-1); + + if(!ret) + { + if(!forceselection) + { + done = true; + to_ret = false; + } + }else + { + mprintf((0,"Creating Pilot!")); + + // call this to initialize pilot data for player. + PilotInitData(Pilot); + char pfilename[_MAX_FNAME]; + + //strip whitespace + char *ptr = pname + strlen(pname) - 1; + while( (ptr > pname) && (*ptr==' '||*ptr=='\t') ){ *ptr = '\0'; ptr--; } + + ptr = pname; + while( *ptr && (*ptr==' '||*ptr=='\t') ) ptr++; + + if(strlen(ptr)==0){ + DoMessageBox(TXT_PLTERROR,TXT_PLTERRORNAME,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + goto retry; + } + + Pilot->set_name(ptr); + strcpy(pfilename,ptr); + PltMakeFNValid(pfilename); + strcat(pfilename,PLTEXTENSION); + + Pilot->set_filename(pfilename); + + RestoreDefaultControls(); + + for(int id=0;idget_controller_function(Controller_needs[id].id, type, &value, flags); + Pilot->controls[id].id = Controller_needs[id].id; + Pilot->controls[id].type[0] = type[0]; + Pilot->controls[id].type[1] = type[1]; + Pilot->controls[id].value = value; + Pilot->controls[id].flags[0] = flags[0]; + Pilot->controls[id].flags[1] = flags[1]; + } + switch(PltWriteFile(Pilot,true)) + { + case PLTW_CFILE_FATAL: + { + //real bad error trying to open file to write to + DoMessageBox(TXT_FILEERROR,TXT_PLTCFILEERR,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + done = true; + to_ret = false; //returning true, that way if they are out of drive space + //they can't get to the pilot window, and delete some pilots + }break; + + case PLTW_UNKNOWN_FATAL: + { + //real bad error trying to open file to write to + DoMessageBox(TXT_FILEERROR,TXT_PLTUKNOWNERR,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + done = true; + to_ret = false; //returning true, that way if they are out of drive space + //they can't get to the pilot window, and delete some pilots + }break; + + case PLTW_FILE_CANTOPEN: + { + //real bad error trying to open file to write to + DoMessageBox(TXT_FILEERROR,TXT_FILEERRPLT1,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + done = true; + to_ret = false; //returning true, that way if they are out of drive space + //they can't get to the pilot window, and delete some pilots + }break; + + case PLTW_NO_FILENAME: + { + //No name was given + DoMessageBox(TXT_PLTERROR,TXT_PLTERRORNAME,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + }break; + + case PLTW_FILE_EXISTS: + { + //pilot already exists so bring up error window + DoMessageBox(TXT_PLTERROR,TXT_PLTERREXISTS,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + }break; + + case PLTW_NO_ERROR: + { + //File saved ok + done = true; + to_ret = true; + }break; + } + } +retry:; + + } + + return to_ret; +} + + +/////////////////////////////////////////////////////////////////////////////////// +// Helper funktions +/////////////////////////////////////////////////////////////////////////////////// +void PilotCopyDefaultControls(pilot *Pilot) +{ + PltClearList(); + PltGetPilotsFree(); + filelist = PltGetPilots(&filecount,NULL,2); + + //if(filecount>0 && DoMessageBox(TXT_PRESETCONTROLS,TXT_USEPRESETS,MSGBOX_YESNO)) + if(filecount>0) + { + pilot s_pil; + + if(PilotChoose(&s_pil,true)) + { + char spfilename[_MAX_FNAME]; + s_pil.get_filename(spfilename); + + if(cfexist(spfilename)) + { + PltCopyKeyConfig(&s_pil,Pilot); + }else + { + mprintf((0,"%s does not exist...not copying\n",spfilename)); + Int3(); + } + } + } + + // Restore the pilot list, ignoring none + PltClearList(); + PltGetPilotsFree(); + filelist = PltGetPilots(&filecount); +} + +/////////////////////////////////////////////////////////// +// Displays a window to select a pilot +bool PilotChoose(pilot *Pilot,bool presets) +{ +#define IDP_PCLIST 19 +#define PCHOOSE_WND_H 288 +#define PCHOOSE_WND_W 384 + + if(filecount==0) + return false; + + newuiTiledWindow window; + newuiSheet *sheet; + newuiListBox *pilot_list; + + bool exit_menu = false; + + if(presets) + { + window.Create(TXT_PRESETCONTROLS,0,0,PCHOOSE_WND_W,PCHOOSE_WND_H); + }else + { + window.Create(TXT_PLTCHOOSE,0,0,PCHOOSE_WND_W,PCHOOSE_WND_H); + } + sheet = window.GetSheet(); + + sheet->NewGroup(NULL,57,0); + sheet->AddText(TXT_PILOTPICTITLE); + sheet->AddText(TXT_COPYCONTB); + sheet->AddText(TXT_COPYCONTOLSC); + + sheet->NewGroup(NULL,7,40); + pilot_list = sheet->AddListBox(284,100,IDP_PCLIST); + pilot_list->SetCurrentIndex(0); + NewPltUpdate(pilot_list,filelist,filecount,0); + + sheet->NewGroup(NULL,80,180,NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK,UID_OK); + sheet->AddButton(TXT_CANCEL,UID_CANCEL); + + bool ret = false; + + window.Open(); + + while (!exit_menu) + { + int res = window.DoUI(); + + // handle all UI results. + switch (res) + { + case IDP_PCLIST: + case UID_OK: + { + int index = pilot_list->GetCurrentIndex(); + if(index>=0) + { + Pilot->set_filename(filelist[index]); + PltReadFile(Pilot,false); + ret = true; + exit_menu = true; + } + }break; + + case UID_CANCEL: + ret = false; + exit_menu = true; + break; + } + } + + window.Close(); + window.Destroy(); + + return ret; +} + +/////////////////////////////////////////////////////////// +//copies a pilot to another +/*************************************************** +bool PilotCopy(pilot *Src,pilot *Dest) +{ + char sname[PILOT_STRING_SIZE]; + char sship[PAGENAME_LEN]; + ubyte sdiff; + + Src->get_name(sname); + Src->get_ship(sship); + Src->get_difficulty(&sdiff); + + Dest->set_name(sname); + Dest->set_ship(sship); + Dest->set_difficulty(sdiff); + + return PltCopyKeyConfig(Src,Dest); +} +***************************************************/ +///////////////////////////////////////////////////// +// Updates the pilot listbox +void NewPltUpdate(newuiListBox *list,char **flist,int filecount,int selected,char *filename) +{ + int index; + pilot tPilot; + + list->RemoveAll(); + + if(pilot_items) + { + delete[] pilot_items; + pilot_items=NULL; + } + + if(filecount){ + //create UIItems + char pname[PILOT_STRING_SIZE]; + + for(index=0;indexAddItem(pname); + } + + list->SetCurrentIndex(selected); + + if(filename && (cfexist(filename)!=CF_NOT_FOUND)){ + //get the selected pilot from the filename + mprintf((0,"Looking for Pilot: %s\n",filename)); + for(int d=0;dSetCurrentIndex(d); + break; + } + } + } + } +} + +// updates the current pilot's information (level played, mission played, etc) +// call after every successful mission completion +extern bool IsCheater; +extern int Default_ship_permission; +void CurrentPilotUpdateMissionStatus(bool just_add_data) +{ + //Don't update if it's a multiplayer game + if(Game_mode&GM_MULTI) + return; + //look for the current mission in the player's data + int index; + tMissionData mission_to_use; + + index = Current_pilot.find_mission_data(Current_mission.name); + + if(index==-1){ + + //this mission doesn't exist for the pilot yet, so add the mission to the pilot + mprintf((0,"PILOT: New Mission being added to mission data (%s)\n",Current_mission.name)); + mission_to_use.highest_level = 0; + mission_to_use.finished = false; + mission_to_use.num_restores = 0; + mission_to_use.num_saves = 0; + mission_to_use.ship_permissions = Default_ship_permission; + strcpy(mission_to_use.mission_name,Current_mission.name); + + Current_pilot.add_mission_data(&mission_to_use); + + index = Current_pilot.find_mission_data(Current_mission.name); + + ASSERT(index!=-1); + + }else{ + //this pilot has flown this mission before, just update it + mprintf((0,"PILOT: Updating previously flown mission data (%s)\n",Current_mission.name)); + + Current_pilot.get_mission_data(index,&mission_to_use); + } + + if(!just_add_data) + { + // bad place to do this, but this code was added at the last minute. a long term fix requires more files to fix. + int max_level; + for (max_level=0; max_level < Current_mission.num_levels; max_level++) + { + if (Current_mission.levels[max_level].flags & LVLFLAG_FINAL) { + max_level++; + break; + } + } + + if( Current_mission.cur_level>mission_to_use.highest_level && !IsCheater && Current_mission.cur_level < max_level){//cheaters don't win + ASSERT(!(Game_mode&GM_MULTI)); + mission_to_use.highest_level = Current_mission.cur_level; + mission_to_use.ship_permissions = Players[0].ship_permissions; + } + + // if we've reached end of mission, we've finished it. + if (Current_mission.cur_level == max_level) { + mission_to_use.finished = true; + } + + Current_pilot.edit_mission_data(index,&mission_to_use); + } + + PltWriteFile(&Current_pilot,false); +} + +// gets highest level flown for mission +int PilotGetHighestLevelAchieved(pilot *Pilot,char *mission_name) +{ + ASSERT(Pilot); + if(!Pilot) + return 1; + + int index = Pilot->find_mission_data(mission_name); + if(index==-1) + return 0; + + tMissionData data; + Pilot->get_mission_data(index,&data); + + return data.highest_level; +} + +// just like it says +bool HasPilotFinishedMission(pilot* Pilot, const char *mission_name) +{ + ASSERT(Pilot); + if(!Pilot) + return false; + + int index = Pilot->find_mission_data((char *)mission_name); + if(index==-1) + return false; + + tMissionData data; + Pilot->get_mission_data(index,&data); + return data.finished; +} + +bool HasPilotFlownMission(pilot *Pilot,const char *mission_name) +{ + ASSERT(Pilot); + if(!Pilot) + return false; + + int index = Pilot->find_mission_data((char *)mission_name); + if(index==-1) + return false; + return true; +} + +int GetPilotNumSavedGamesForMission(pilot *Pilot,const char *mission_name) +{ + ASSERT(Pilot); + if(!Pilot) + return 0; + + int index = Pilot->find_mission_data((char *)mission_name); + if(index==-1) + return 0; + + tMissionData data; + Pilot->get_mission_data(index,&data); + return data.num_saves; +} + +int GetPilotNumRestoredGamesForMission(pilot *Pilot,const char *mission_name) +{ + ASSERT(Pilot); + if(!Pilot) + return 0; + + int index = Pilot->find_mission_data((char *)mission_name); + if(index==-1) + return 0; + + tMissionData data; + Pilot->get_mission_data(index,&data); + return data.num_restores; +} + +void IncrementPilotSavedGamesForMission(pilot *Pilot,const char *mission_name) +{ + ASSERT(Pilot); + if(!Pilot) + return; + + int index = Pilot->find_mission_data((char *)mission_name); + if(index==-1) + return; + + tMissionData data; + Pilot->get_mission_data(index,&data); + data.num_saves++; + Pilot->edit_mission_data(index,&data); + + PltWriteFile(Pilot,false); +} + +void IncrementPilotRestoredGamesForMission(pilot *Pilot,const char *mission_name) +{ + ASSERT(Pilot); + if(!Pilot) + return; + + int index = Pilot->find_mission_data((char *)mission_name); + if(index==-1) + return; + + tMissionData data; + Pilot->get_mission_data(index,&data); + data.num_restores++; + Pilot->edit_mission_data(index,&data); + + PltWriteFile(Pilot,false); +} + +int GetPilotShipPermissions(pilot *Pilot,const char *mission_name) +{ + ASSERT(!(Game_mode&GM_MULTI)); + ASSERT(Pilot); + if(!Pilot) + return 1; + + int index = Pilot->find_mission_data((char *)mission_name); + if(index==-1) + return 0; + + tMissionData data; + Pilot->get_mission_data(index,&data); + + return data.ship_permissions; +} + +//////////////////////////////////////////////////////////////////////// +// Pilot file functions +//////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////// +//deletes a pilot +bool PltDelete(pilot *Pilot) +{ + char filename[_MAX_PATH]; + char pfilename[_MAX_FNAME]; + char pname[PILOT_STRING_SIZE]; + + Pilot->get_filename(pfilename); + + if(pfilename[0]!=0) + { +#ifdef MACINTOSH + ddio_MakePath(filename,Base_directory,"pilots",pfilename,NULL); +#else + ddio_MakePath(filename,Base_directory,pfilename,NULL); +#endif + return (ddio_DeleteFile(pfilename)==1); + } + else + { + Int3(); //this is odd + + //it shouldn't get to here, but if for some reason filename didn't exist + //then create the filename from the player name and delete the file + Pilot->get_name(pname); + + if(pname[0]==0) + return false; + + PltMakeFNValid(pname); + + strcpy(pfilename,pname); + strcat(pfilename,PLTEXTENSION); +#ifdef MACINTOSH + ddio_MakePath(filename,Base_directory,"pilots",pfilename,NULL); +#else + ddio_MakePath(filename,Base_directory,pfilename,NULL); +#endif + return (ddio_DeleteFile(filename)==1); + } +} + +void PltCreateDedicatedServerPilot(pilot *Pilot) +{ + Pilot->clean(true); + Pilot->set_name("SERVER"); +} + +////////////////////////////////////////////// +//writes a pilot out to file +//make sure Pilot->filename and Pilot->name are filled in correctly!!!! +int PltWriteFile(pilot *Pilot,bool newpilot) +{ + if(Dedicated_server) + return PLTW_NO_ERROR; + + Pilot->write(); + return Pilot->flush(newpilot); +} + +//////////////////////////////////////////////////// +//reads in a pilot file (fill in pilot name to read in the struct before calling) +//Pilot->filename MUST be filled in (with .plt extension) +void _ReadOldPilotFile(pilot *Pilot,bool keyconfig,bool missiondata); + +void PltReadFile(pilot *Pilot,bool keyconfig,bool missiondata) +{ + if(Dedicated_server) + { + PltCreateDedicatedServerPilot(Pilot); + return; + } + + char filename[_MAX_PATH]; + char pfilename[_MAX_FNAME]; + CFILE *file; + int filever; + + Pilot->get_filename(pfilename); + if(pfilename[0]==0) + return; + + //open and process file +#ifdef MACINTOSH + ddio_MakePath(filename,Base_directory,"pilots",pfilename,NULL); +#else + ddio_MakePath(filename,Base_directory,pfilename,NULL); +#endif + try + { + file = cfopen(filename,"rb"); + if(!file) + return; + + filever = cf_ReadInt(file); + cfclose(file); + }catch(...) + { + mprintf((0,"File exception has occured\n")); + Int3(); + Error(TXT_MAJORPLTERROR); + return; + } + + if(filever>=0x20){ + //new pilot files! + int ret = Pilot->read(!keyconfig,!missiondata); + switch(ret) + { + case PLTR_TOO_NEW: + Error(TXT_PLTFILETOONEW,pfilename); + break; + } + + return; + }else{ + try + { + _ReadOldPilotFile(Pilot,keyconfig,missiondata); + }catch(...) + { + mprintf((0,"File exception has occured\n")); + Int3(); + Error(TXT_MAJORPLTERROR); + return; + } + } +} + +////////////////////////////////////////////////////////////// +//returns the filelist of pilots available +typedef struct tPGetPilotStruct{ + char *filename; + tPGetPilotStruct *next; +}tPGetPilotStruct; +static char **pltgetname_list = NULL; +static int pltgetname_count = 0; + +void PltGetPilotsFree(void) +{ + if(pltgetname_list){ + mem_free(pltgetname_list); + pltgetname_list = NULL; + } + pltgetname_count = 0; +} + +char **PltGetPilots(int *count,char *ignore_filename,int display_default_configs) +{ + static char **pname_list = NULL; + + char buffer[PLTFILELEN]; + char search[256]; + tPGetPilotStruct *root = NULL,*curr = NULL; + int loop = 1; + + if(display_default_configs==1) + loop = 2; + + //clear list + PltClearList(); + PltGetPilotsFree(); + (*count) = 0; + root = NULL; + + for(int loop_count=0;loop_countnext = NULL; + }else + { + curr->next = (tPGetPilotStruct *)mem_malloc(sizeof(tPGetPilotStruct)); + curr = curr->next; + curr->next = NULL; + } + + if(curr) + { + curr->filename = mem_strdup(buffer); + + if(curr->filename) + { + (*count)++; + mprintf((0,"Getting Pilots..found %s\n",buffer)); + }else + { + Error(TXT_OUTOFMEMORY); + } + } + + } + + while(ddio_FindNextFile(buffer)){ + if(ignore_filename && !stricmp(ignore_filename,buffer)) + { + mprintf((0,"Getting Pilots...found %s, but ignoring\n",buffer)); + }else + { + if(root==NULL) + { + root = curr = (tPGetPilotStruct *)mem_malloc(sizeof(tPGetPilotStruct)); + curr->next = NULL; + }else + { + curr->next = (tPGetPilotStruct *)mem_malloc(sizeof(tPGetPilotStruct)); + curr = curr->next; + curr->next = NULL; + } + + if(curr){ + curr->filename = (char *)mem_strdup(buffer); + + if(curr->filename){ + (*count)++; + mprintf((0,"Getting Pilots..found %s\n",buffer)); + }else{ + Error(TXT_OUTOFMEMORY); + } + } + } + } + + } + + ddio_FindFileClose(); + } + + //now allocate for the real list of char * and move the linked list to that array + if((*count)>0){ + pltgetname_list = (char **)mem_malloc((*count)*sizeof(char *)); + if(pltgetname_list){ + int end = (*count); + curr = root; + tPGetPilotStruct *next; + + for(int i=0;inext; + + if(curr){ + pltgetname_list[i] = curr->filename; + }else{ + pltgetname_list[i] = NULL; + } + + mem_free(curr); + curr = next; + } + + } + } + pltgetname_count = (*count); + + mprintf((0,"Found %d pilots\n",(*count))); + return pltgetname_list; +} + +/////////////////////////////////////////////////////////// +//clears the file list (MUST CALL TO FREE MEMORY!!!!!!!!!) +void PltClearList(void) +{ + if(!pltgetname_list) + return; + + int index; + + for(index=0;index': + case ':': + case '"': + name[whiteindex]='_'; + break; + default: + break; + } + } + + //remove leading whitespace + whiteindex=0; + while(name[whiteindex]==' ') whiteindex++; + + char temp[PLTFILELEN]; + strcpy(temp,&name[whiteindex]); + strcpy(name,temp); +} + +//////////////////////////////////////////////////////// +// Copies the key/joy config of a pilot to another (src must exist!) (Keep up to date with Read/Write file) +// this will also save out the dest, so make sure its filled in before calling function +bool PltCopyKeyConfig(pilot *src,pilot *dest) +{ + //check to make sure src exists + ASSERT(src); + ASSERT(dest); + + char pname[PILOT_STRING_SIZE]; + src->get_name(pname); + + if( (pname[0]!=0) && (strlen(pname)>0) && (strcmp(pname," ")!=0) ) + { + int i; + for(i=0;icontrols[i].id = src->controls[i].id; + dest->controls[i].type[0] = src->controls[i].type[0]; + dest->controls[i].type[1] = src->controls[i].type[1]; + dest->controls[i].value = src->controls[i].value; + dest->controls[i].flags[0] = src->controls[i].flags[0]; + dest->controls[i].flags[1] = src->controls[i].flags[1]; + } + for(i=0;imouse_sensitivity[i] = src->mouse_sensitivity[i]; + } + for(i=0;ijoy_sensitivity[i] = src->joy_sensitivity[i]; + } + dest->key_ramping = src->key_ramping; + dest->read_controller = src->read_controller; + dest->mouselook_control = src->mouselook_control; + + return true; + } + else + return false; +} + +/* + ************************************************************************ + * The following section is for the multiplayer ship selection * + * and configuration. * + ************************************************************************ + * +*/ + +// UI3DWindow class +// +// UI class for displaying a polymodel on the UIWindow, rotating 30 deg/sec +class UI3DWindow: public UIStatic +{ +public: + void OnDraw(void); +}; + +// UIBmpWindow class +// +// UI class for displaying a bitmap on a UIWindow +class UIBmpWindow: public UIStatic +{ +public: + UIBmpWindow(); + ~UIBmpWindow(); + void SetInfo(bool animated,int handle); + void OnDraw(void); +private: + void DrawBorder(void); + bool animated; + int bm_handle; + float start_time; + UIText text; + bool created; +}; + +// struct ship_pos +// +// contains information about the ship to be displayed in the ship configuration +typedef struct{ + matrix last_frame; + float cam_dist; + float last_time; + bool texture_changed; + int texture_type; + int texture_id; + int bm_handle; + void Init(){ + vm_AnglesToMatrix (&last_frame,0,(65535/2),0); + last_time = timer_GetTime(); + texture_id = Players[0].custom_texture_handle; + bm_handle = -1; + } +}tShipPos; + +tShipPos ship_pos; +int ship_model = -1; +char custom_texture[_MAX_PATH]; +UIBmpWindow *bmpwindow; + +typedef struct{ + int *idlist; //array of remapping indicies (list index->ship index) + void Init(){ + idlist = NULL; + } + void Reset(){ + if(idlist){ + mem_free(idlist); + idlist = NULL; + } + } +}tShipListInfo; + +tCustomListInfo *lp_cust_bmps = NULL; +tShipListInfo *lp_ship_info = NULL; + + +// axtoi +// +//Convert a string that represents a hex value into an int (like atoi) +int axtoi(char *p) +{ + int value = 0; + + while( (p) &&(*p)) + { + *p = toupper(*p); + if ( (*p>='0') && (*p<='9') ) + value = (value * 16) + ((*p)-'0'); + else if ( (*p>='A') && (*p<='F') ) + value = (value * 16) + (((*p)-'A')+10); + else + return 0; + p++; + } + return value; +} + +// StripCRCFileName +// +// Given a filename that has the _ in it at the end of the name, it will fill +// in dest with just the filename part (without the trailing _). +void StripCRCFileName(const char *src,char *dest) +{ + ASSERT(src); + ASSERT(dest); + + int full_strlen = strlen(src); + char ext[256]; + ddio_SplitPath(src,NULL,NULL,ext); + int extstrlen = strlen(ext); + int eon = full_strlen-9-extstrlen; + + if(eon<1){ + //this filename doesn't have a trailing CRC + strcpy(dest,src); + return; + } + + //check to make sure the [eon] is '_' + if(src[eon]!='_'){ + //it wasn't so it must not have a CRC at the end + strcpy(dest,src); + return; + } + + char hexstring[9]; + strncpy(hexstring,&src[eon+1],8); + hexstring[8] = '\0'; + unsigned int crc; + crc = axtoi(hexstring); + + if(crc==0){ + //crc can't be 0? + strcpy(dest,src); + return; + } + + //strip the filename + strncpy(dest,src,eon); + dest[eon]='\0'; + strcat(dest,ext); //put back the extension +} + + +// CreateCRCFileName +// +// Given a filename, this function will open the file, make a CRC of the data +// and then fill in dest buffer with _ where is the +// original filename and is the converted string of the CRC in hex. +// returns true on success. +// dest should be size of src + 9 (for the _ & CRC) +bool CreateCRCFileName(const char *src,char *dest) +{ + ASSERT(src); + ASSERT(dest); + + if(cfexist(src)!=CF_ON_DISK) + return false; + + unsigned int crc_value = cf_GetfileCRC((char *)src); + if(crc_value==0){ + mprintf((0,"CRC WARNING: A CRC of 0 HAS BEEN GENERATED!\n")); + } + char hex_string[10]; + sprintf(hex_string,"_%08X",crc_value); + + char ext[256]; + ddio_SplitPath(src,NULL,NULL,ext); + + //now create the full filename + //first strip any possible CRC on the file + StripCRCFileName(src,dest); + dest[strlen(dest)-strlen(ext)] = '\0'; //lose the extension + strcat(dest,hex_string); + strcat(dest,ext); //put extension back + + return true; +} + +// CreateCRCFileName +// +// Given a file, a new filename, it will take the src file, create a new filename, with base as the base +bool CreateCRCFileName(const char *src,char *base,char *newfilename) +{ + ASSERT(src); + ASSERT(base); + ASSERT(newfilename); + + if(cfexist(src)!=CF_ON_DISK) + return false; + + unsigned int crc_value = cf_GetfileCRC((char *)src); + if(crc_value==0){ + mprintf((0,"CRC WARNING: A CRC of 0 HAS BEEN GENERATED!\n")); + } + char hex_string[10]; + sprintf(hex_string,"_%08X",crc_value); + + char ext[256]; + ddio_SplitPath(base,NULL,NULL,ext); + + //now create the full filename + //first strip any possible CRC on the file + strcpy(newfilename,base); + newfilename[strlen(newfilename)-strlen(ext)] = '\0'; //lose the extension + strcat(newfilename,hex_string); + strcat(newfilename,ext); //put extension back + + return true; +} + + +// FindAllFiles +// +// Retrieves the files in the current directory that match the pattern given. Call FindAllFilesSize() to determine +// how much memory will be needed for the buffer. +// pattern = wildcard pattern to use to match +// list = buffer of memory that will be filled in with the filenames (each seperated by a \0) +// size = size of memory allocated for list parm +// filecount = filled in with the number of files it found +int FindAllFiles(const char *pattern,char *list,int size,int *filecount) +{ + int count = 0; + char filename[_MAX_PATH]; + *filecount = 0; + int memory = 0; + + if(size<=0) + return 0; + if(!list) + return 0; + if(!pattern) + return 0; + + if(ddio_FindFileStart(pattern,filename)){ + count++; + strcpy(&list[memory],filename); + memory += strlen(&list[memory]) + 1; + while( (countRemoveAll(); + + cust_bmps->Reset(); + cust_bmps->custom_bitmap_list = lb; + + //get a list of custom textures + int count = 0; + bool ok_to_get_files; + int total_files; + + //build list + char oldpath[_MAX_PATH]; + + ddio_GetWorkingDir(oldpath,_MAX_PATH); + + if(ddio_SetWorkingDir(LocalCustomGraphicsDir)) + ok_to_get_files = true; + else + ok_to_get_files = false; + + if(ok_to_get_files){ + //Get all the filenames + int totalsize,size; + int count,totalcount; + + //.oaf + //.ogf + count = totalcount = totalsize = total_files = 0; + + totalsize += FindAllFilesSize("*.oaf",&count); totalcount+=count; + totalsize += FindAllFilesSize("*.ogf",&count); totalcount+=count; + + if(totalsize){ + cust_bmps->needed_size = totalsize; + total_files = totalcount; + cust_bmps->files = (char *)mem_malloc(totalsize); + if(cust_bmps->files){ + size = 0; + count = 0; + + size += FindAllFiles("*.oaf",&cust_bmps->files[size],totalcount,&count); totalcount-=count; + size += FindAllFiles("*.ogf",&cust_bmps->files[size],totalcount,&count); totalcount-=count; + }else + mprintf((0,"Unable to allocate memory for %d bytes\n",totalsize)); + } + } + + count = total_files + 1; //make room for "None" + + char chosen_name[256],ext[256]; + if(selected_name) + { + ddio_SplitPath(selected_name,NULL,chosen_name,ext); + strcat(chosen_name,ext); + }else + { + *chosen_name = '\0'; + } + + //default "None" + lb->AddItem(TXT_LBNONE); + + int memory_used; + memory_used = 0; + for(int i=1;ifiles[memory_used],tempf); + lb->AddItem(tempf); + if(!stricmp(&cust_bmps->files[memory_used],chosen_name)) + { + lb->SetCurrentIndex(i); + CustomCallBack(i); + } + + memory_used += strlen(&cust_bmps->files[memory_used])+1; + } + + // If we are not forcing a selection, choose none + if(*chosen_name=='\0') + { + lb->SetCurrentIndex(0); + CustomCallBack(0); + } + + ddio_SetWorkingDir(oldpath); + return true; +} + +// GetCustomSoundFiles +// +// Looks in the custom/sound directory and retrieves all the wav files in there. Returns the size of the buffer +// needed to get all the needed files. The filenames are stored in the buffer, seperated by \0, use count to +// determine how many were placed in the buffer +// +// buffer = Your buffer passed in, if this is NULL then it just returns a buffer size that is needed +// size = Size of your buffer passed in +// *count = filled in with the number of files that were placed into your buffer +int GetCustomSoundFiles(char *buffer=NULL,int size=0,int *count=NULL); +int GetCustomSoundFiles(char *buffer,int size,int *count) +{ + int c = 0; + int isize = 0; + int len,overallsize=0; + char tempname[_MAX_PATH]; + char oldpath[_MAX_PATH]; + ddio_GetWorkingDir(oldpath,_MAX_PATH); + ddio_SetWorkingDir(LocalCustomSoundsDir); + + if(!buffer) + size = 0; + + if(count) + *count = 0; + + if(ddio_FindFileStart("*.osf",tempname)){ + len = strlen(tempname); + overallsize += len + 1; + if(isize+len\0\0...). Be VERY careful that +// the index you pass in is a valid string index +// index = Index of string you want +// list = string list (a bunch of strings, seperated by \0) +// maxsize = size of string list buffer +// returns NULL on error (if an error can be detected) +char *GetStringInList(int index,char *list,int maxsize) +{ + int count = 0; + int p = 0; + char *pp; + pp = list; + + while(1){ + if(count==index) + return pp; + + if(pRemoveAll(); + audio2_list->RemoveAll(); + audio3_list->RemoveAll(); + audio4_list->RemoveAll(); + + // free up allocated memory + cust_snds->Reset(); + + // Get all the audio files and put them into the lists + cust_snds->needed_size = GetCustomSoundFiles(); + if(cust_snds->needed_size) + cust_snds->files = (char *)mem_malloc(cust_snds->needed_size); + else + cust_snds->files = NULL; + + int audio_count; + + GetCustomSoundFiles(cust_snds->files,cust_snds->needed_size,&audio_count); + + int len; + + //Add to both boxes + audio1_list->AddItem(TXT_LBNONE); + audio2_list->AddItem(TXT_LBNONE); + audio3_list->AddItem(TXT_LBNONE); + audio4_list->AddItem(TXT_LBNONE); + + int count = 0; + + char paudio1[PAGENAME_LEN],paudio2[PAGENAME_LEN]; + char paudio3[PAGENAME_LEN],paudio4[PAGENAME_LEN]; + Pilot->get_multiplayer_data(NULL,paudio1,paudio2,NULL,paudio3,paudio4); + + + for(int i=0;ifiles[count]); + StripCRCFileName(&cust_snds->files[count],tfn); + + audio1_list->AddItem((char *)(tfn)); + audio2_list->AddItem((char *)(tfn)); + audio3_list->AddItem((char *)(tfn)); + audio4_list->AddItem((char *)(tfn)); + + if(!stricmp(paudio1,&cust_snds->files[count])){ + //set this as selected index for audio #1 + audio1_list->SetCurrentIndex(i+1); + } + if(!stricmp(paudio2,&cust_snds->files[count])){ + //set this as selected index for audio #2 + audio2_list->SetCurrentIndex(i+1); + } + if(!stricmp(paudio3,&cust_snds->files[count])){ + //set this as selected index for audio #1 + audio3_list->SetCurrentIndex(i+1); + } + if(!stricmp(paudio4,&cust_snds->files[count])){ + //set this as selected index for audio #2 + audio4_list->SetCurrentIndex(i+1); + } + count+=len+1; + } +} + +pilot *AudioTauntPilot; +tCustomListInfo *AudioTauntPilotSndInfo; +void audio1changecallback(int index) +{ + if(index==0){ + AudioTauntPilot->set_multiplayer_data(NULL,""); + return; + }else{ + index--; + char *p = GetStringInList(index,AudioTauntPilotSndInfo->files,AudioTauntPilotSndInfo->needed_size); + if(p){ + AudioTauntPilot->set_multiplayer_data(NULL,p); + } + } +} + +void audio2changecallback(int index) +{ + if(index==0){ + AudioTauntPilot->set_multiplayer_data(NULL,NULL,""); + return; + }else{ + index--; + char *p = GetStringInList(index,AudioTauntPilotSndInfo->files,AudioTauntPilotSndInfo->needed_size); + if(p){ + AudioTauntPilot->set_multiplayer_data(NULL,NULL,p); + } + } +} + +void audio3changecallback(int index) +{ + if(index==0){ + AudioTauntPilot->set_multiplayer_data(NULL,NULL,NULL,NULL,""); + return; + }else{ + index--; + char *p = GetStringInList(index,AudioTauntPilotSndInfo->files,AudioTauntPilotSndInfo->needed_size); + if(p){ + AudioTauntPilot->set_multiplayer_data(NULL,NULL,NULL,NULL,p); + } + } +} + +void audio4changecallback(int index) +{ + if(index==0){ + AudioTauntPilot->set_multiplayer_data(NULL,NULL,NULL,NULL,NULL,""); + return; + }else{ + index--; + char *p = GetStringInList(index,AudioTauntPilotSndInfo->files,AudioTauntPilotSndInfo->needed_size); + if(p){ + AudioTauntPilot->set_multiplayer_data(NULL,NULL,NULL,NULL,NULL,p); + } + } +} + +#define TAUNT_MENU_W 512 +#define TAUNT_MENU_H 384 + +#define MAX_MULTIPLAYER_TAUNTS 8 +#define TAUNT_EDIT_WIDTH 400 + + +void DoPilotTauntScreen(pilot *plt) +{ + newuiTiledWindow taunt_wnd; + newuiSheet *sheet; + char *taunt_edit[MAX_PILOT_TAUNTS]; + int cury = 10; + bool exit_menu=false; + int i; + + //create window + taunt_wnd.Create(TXT_MULTIPLAYER_TAUNTS, 0,0, TAUNT_MENU_W, TAUNT_MENU_H); + sheet = taunt_wnd.GetSheet(); + + //Ok/Cancel buttons + for (i=0;iNewGroup(str, 10,cury); + + taunt_edit[i] = sheet->AddEditBox(str, PILOT_TAUNT_SIZE, TAUNT_EDIT_WIDTH); + strcpy(taunt_edit[i], plt->taunts[i]); + cury+= 32; + } + + + sheet->NewGroup(NULL, TAUNT_MENU_W-210, TAUNT_MENU_H - 100, NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK, UID_OK); + sheet->AddButton(TXT_CANCEL, UID_CANCEL); + + taunt_wnd.Open(); + + while (!exit_menu) + { + int res = taunt_wnd.DoUI(); + + // handle all UI results. + switch (res) + { + case UID_OK: + for (i=0;itaunts[i], taunt_edit[i]); + // taunt_edit[i].GetText(plt->taunts[i],PILOT_TAUNT_SIZE); + } + exit_menu = true; + break; + case UID_CANCEL: + exit_menu = true; + break; + default: + break; + } + } + + taunt_wnd.Close(); + taunt_wnd.Destroy(); +} + +bool PltSelectShip(pilot *Pilot) +{ +#define IDP_SHPLIST 19 +#define ID_GETANIM 20 +#define ID_IMPORT 21 +#define ID_PLAY1 22 +#define ID_PLAY2 23 +#define ID_PLAY3 24 +#define ID_PLAY4 25 + +#define ID_IMPORTSOUND 26 +#define ID_TAUNTS 27 +#define ID_DEL_LOGO 28 +#define ID_DEL_TAUNTA 29 +#define ID_DEL_TAUNTB 30 +#define ID_DEL_TAUNTC 31 +#define ID_DEL_TAUNTD 32 +#define PSSELECT_WND_H 384 +#define PSSELECT_WND_W 512 + + // variable declarations + newuiTiledWindow window; + newuiSheet *sheet; + newuiListBox *custom_list; + newuiComboBox *ship_list; + + int count; + char oldt1[PILOT_TAUNT_SIZE],oldt2[PILOT_TAUNT_SIZE]; + char oldt3[PILOT_TAUNT_SIZE],oldt4[PILOT_TAUNT_SIZE]; + + DoWaitMessage(true); + + Pilot->get_multiplayer_data(NULL,oldt1,oldt2,NULL,oldt3,oldt4); + + tCustomListInfo cust_snds; + tAudioTauntComboBoxes taunts_lists; + AudioTauntPilot = Pilot; + AudioTauntPilotSndInfo = &cust_snds; + + UI3DWindow ship_win; + UIBmpWindow bmp_win; + tCustomListInfo cust_bmps; + tShipListInfo ship_info; + + int old_bmhandle; + int old_flags; + bool exit_menu = false; + bool ret; + + // pre-initialize all variables + cust_bmps.Init(); + cust_snds.Init(); + + lp_cust_bmps = &cust_bmps; + lp_ship_info = &ship_info; + + ship_pos.Init(); + ship_info.Init(); + + custom_texture[0]='\0'; + old_bmhandle = GameTextures[ship_pos.texture_id].bm_handle; + old_flags = GameTextures[ship_pos.texture_id].flags; + + // create UIWindow + window.Create(TXT_MULTISELECTSHIP,0,0,PSSELECT_WND_W,PSSELECT_WND_H); + sheet = window.GetSheet(); + + sheet->NewGroup(TXT_DEFAULTMSHIPS,0,0); + ship_list = sheet->AddComboBox(IDP_SHPLIST,0); + + sheet->NewGroup(TXT_CUSTOMTEXTURES,0,36); + cust_bmps.custom_bitmap_list = custom_list = sheet->AddListBox(200,96,IDP_SHPLIST,0); + + sheet->NewGroup(NULL,230,130); + sheet->AddButton(TXT_DELETE,ID_DEL_LOGO); + + sheet->NewGroup(TXT_AUDIOTAUNTA,0,155); + taunts_lists.taunt_a = sheet->AddComboBox(-5,0); + + sheet->NewGroup(TXT_AUDIOTAUNTB,0,192); + taunts_lists.taunt_b = sheet->AddComboBox(-5,0); + + sheet->NewGroup(TXT_AUDIOTAUNTC,0,229); + taunts_lists.taunt_c = sheet->AddComboBox(-5,0); + + sheet->NewGroup(TXT_AUDIOTAUNTD,0,266); + taunts_lists.taunt_d = sheet->AddComboBox(-5,0); + + //Ship window + ship_win.Create(&window,&UITextItem(""),290,50,180,140,0); + + //Bitmap display of selected logo + bmpwindow = &bmp_win; + bmp_win.Create(&window,&UITextItem(""),UI_BORDERSIZE+200,53,42,42,0); + + // Get all the audio files and put them into the lists + UpdateAudioTauntBoxes(&cust_snds,taunts_lists.taunt_a,taunts_lists.taunt_b,taunts_lists.taunt_c,taunts_lists.taunt_d,Pilot); + + DoWaitMessage(true); + + // Setup hotspots for audio taunt commands + sheet->NewGroup(NULL,170,163); + sheet->AddButton(TXT_PLAYSOUND,ID_PLAY1); + sheet->AddButton(TXT_DELETE,ID_DEL_TAUNTA); + + sheet->NewGroup(NULL,170,200); + sheet->AddButton(TXT_PLAYSOUND,ID_PLAY2); + sheet->AddButton(TXT_DELETE,ID_DEL_TAUNTB); + + sheet->NewGroup(NULL,170,237); + sheet->AddButton(TXT_PLAYSOUND,ID_PLAY3); + sheet->AddButton(TXT_DELETE,ID_DEL_TAUNTC); + + sheet->NewGroup(NULL,170,274); + sheet->AddButton(TXT_PLAYSOUND,ID_PLAY4); + sheet->AddButton(TXT_DELETE,ID_DEL_TAUNTD); + + //setup callback function + custom_list->SetSelectChangeCallback(CustomCallBack); + ship_list->SetSelectChangeCallback(ShipSelectCallBack); + taunts_lists.taunt_a->SetSelectChangeCallback(audio1changecallback); + taunts_lists.taunt_b->SetSelectChangeCallback(audio2changecallback); + taunts_lists.taunt_c->SetSelectChangeCallback(audio3changecallback); + taunts_lists.taunt_d->SetSelectChangeCallback(audio4changecallback); + + int i; + //get all the ship ids + count = 0; + for(i=0;iget_ship(pship); + + for(i=0;iAddItem(Ships[i].name); +#ifdef DEMO + } +#endif + + if(!stricmp(pship,Ships[i].name)){ + ship_list->SetCurrentIndex(count); + found_ship = true; + } + count++; + } + } + + //if we couldn't find the ship than that means that the ship in their pilot file no longer + //exists and so we must set it to the default ship + if(!found_ship){ + DoMessageBox(TXT_WARNING,TXT_SHIPSELECTERR,MSGBOX_OK); + if(count>0){ + //find the ship in the page + int index = FindShipName(DEFAULT_SHIP); + if(index==-1){ + mprintf((0,"WARNING: CAN'T FIND DEFAULT SHIP IN TABLE\n")); + }else{ + //go through all the id's of the ships we found and find the ship (if FindShipName found it, + //then we'll have it here somewhere. + for(int i=0;iSetCurrentIndex(i); + + }//endif + }//endfor + }//end else + }else{ + //NO SHIPS IN THE TABLE!!! + mprintf((0,"WARNING: NO SHIPS IN THE TABLE!?\n")); + } + } + + // get a list of custom textures + char pshiplogo[PAGENAME_LEN]; + Pilot->get_multiplayer_data(pshiplogo); + + DoWaitMessage(true); + + if(!UpdateGraphicsListbox(&cust_bmps,custom_list,pshiplogo)) + goto ship_id_err; + + i = ship_list->GetCurrentIndex(); + ShipSelectCallBack(i); + + // setup hotspots + sheet->NewGroup(NULL,250,170); + sheet->AddLongButton(TXT_IMPORTGRAPHIC,ID_IMPORT); + sheet->AddLongButton(TXT_IMPORTIFL,ID_GETANIM); + sheet->AddLongButton(TXT_MULTIPLAYER_TAUNTS,ID_TAUNTS); + sheet->AddLongButton(TXT_IMPORTSOUND,ID_IMPORTSOUND); + sheet->NewGroup(NULL,260,281,NEWUI_ALIGN_HORIZ); + sheet->AddButton(TXT_OK,UID_OK); + sheet->AddButton(TXT_CANCEL,UID_CANCEL); + + //Ok, all initial setup is finally complete, time to display the window and process input + ret = false; + + DoWaitMessage(false); + + window.Open(); + while (!exit_menu) + { + int res = window.DoUI(); + + // handle all UI results. + switch (res){ + case UID_OK: + { + char tbuf[64]; + ship_list->GetCurrentItem(tbuf,64); + + Pilot->set_ship(tbuf); + Pilot->set_multiplayer_data(custom_texture); + + //Retrieve Audio taunts + Pilot->set_multiplayer_data(NULL,"","",NULL,"",""); + + int index; + index = taunts_lists.taunt_a->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + Pilot->set_multiplayer_data(NULL,p); + } + } + index = taunts_lists.taunt_b->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + Pilot->set_multiplayer_data(NULL,NULL,p); + } + } + + index = taunts_lists.taunt_c->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + Pilot->set_multiplayer_data(NULL,NULL,NULL,NULL,p); + } + } + + index = taunts_lists.taunt_d->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + Pilot->set_multiplayer_data(NULL,NULL,NULL,NULL,NULL,p); + } + } + + char tempn[PAGENAME_LEN]; + + Pilot->get_multiplayer_data(NULL,tempn); + mprintf((0,"Audio #1: '%s'\n",tempn)); + + Pilot->get_multiplayer_data(NULL,NULL,tempn); + mprintf((0,"Audio #2: '%s'\n",tempn)); + + Pilot->get_multiplayer_data(NULL,NULL,NULL,NULL,tempn); + mprintf((0,"Audio #3: '%s'\n",tempn)); + + Pilot->get_multiplayer_data(NULL,NULL,NULL,NULL,NULL,tempn); + mprintf((0,"Audio #4: '%s'\n",tempn)); + + ret = true; + exit_menu = true; + }break; + case UID_CANCEL: + ret = false; + exit_menu = true; + GameTextures[ship_pos.texture_id].bm_handle = old_bmhandle; + GameTextures[ship_pos.texture_id].flags = old_flags; + Pilot->set_multiplayer_data(NULL,oldt1,oldt2,NULL,oldt3,oldt4); + break; + case ID_IMPORT: + { + char path[_MAX_PATH]; + char newf[_MAX_FNAME]; + char wildcards[100]; +#if defined (MACINTOSH) + ddio_MakePath(path,Base_directory,"custom","graphics",NULL); +#else + path[0] = '\0'; +#endif + strcpy(wildcards,"*.ogf;*.tga;*.pcx;*.iff"); + if(DoPathFileDialog(false,path,TXT_CHOOSE,wildcards,PFDF_FILEMUSTEXIST)){ + if(ImportGraphic(path,newf)){ + //update the listbox + if(!UpdateGraphicsListbox(&cust_bmps,custom_list,newf)) + goto ship_id_err; + }else{ + DoMessageBox(TXT_ERROR,TXT_ERRORIMPORT,MSGBOX_OK); + } + } + ddio_SetWorkingDir(path); + } + break; + case ID_GETANIM: + { + char path[_MAX_PATH]; + char opath[_MAX_PATH]; + char wildcards[100]; +#if defined (MACINTOSH) + ddio_MakePath(path,Base_directory,"custom","graphics",NULL); +#else + path[0] = '\0'; +#endif + strcpy(opath,path); + strcpy(wildcards,"*.ifl"); + if(DoPathFileDialog(false,path,TXT_CHOOSE,wildcards,PFDF_FILEMUSTEXIST)){ + int handle = AllocLoadIFLVClip (IGNORE_TABLE(path),SMALL_TEXTURE,1); + + if(handle!=-1){ + //change the file extension + char f[_MAX_FNAME],newf[_MAX_FNAME+_MAX_EXT],tempf[_MAX_PATH]; + if(!CreateCRCFileName(path,tempf)){ + FreeVClip(handle); + break; + } + ddio_SplitPath(tempf,NULL,f,NULL); + strcpy(newf,f); + strcat(newf,".oaf"); + ddio_MakePath(path,LocalCustomGraphicsDir,newf,NULL); + + if(SaveVClip (path,handle)==0){ + //error saving + DoMessageBox(TXT_ERROR,TXT_ERRORIMPORT,MSGBOX_OK); + }else{ + //all went ok!!! + DoMessageBox(TXT_SUCCESS,TXT_SUCCESSIMPORT,MSGBOX_OK); + } + FreeVClip(handle); + + //check file size...make sure it isn't too big + CFILE *file = cfopen(path,"rb"); + if(file) + { + if(cfilelength(file)>MAX_AUDIOTAUNTSIZE) + { + //file too big!!!!!! + char message[256]; + sprintf(message,TXT_COMPRESSTOOBIG,MAX_AUDIOTAUNTSIZE/1024); + DoMessageBox(TXT_WARNING,message,MSGBOX_OK); + } + cfclose(file); + } + + //update the listbox + if(!UpdateGraphicsListbox(&cust_bmps,custom_list,newf)) + goto ship_id_err; + } + } + ddio_SetWorkingDir(opath); + } + break; + case ID_PLAY1: + { + // Play audio taunt #1 if isn't selected + char path[_MAX_PATH]; + int index = taunts_lists.taunt_a->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + ddio_MakePath(path,LocalCustomSoundsDir,p,NULL); + mprintf((0,"Playing: %s\n",path)); + bool cenable = taunt_AreEnabled(); + taunt_Enable(true); + taunt_PlayTauntFile(path); + taunt_Enable(cenable); + } + } + }break; + case ID_PLAY2: + { + // Play audio taunt #2 if isn't selected + char path[_MAX_PATH]; + int index = taunts_lists.taunt_b->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + ddio_MakePath(path,LocalCustomSoundsDir,p,NULL); + mprintf((0,"Playing: %s\n",path)); + bool cenable = taunt_AreEnabled(); + taunt_Enable(true); + taunt_PlayTauntFile(path); + taunt_Enable(cenable); + } + } + }break; + case ID_PLAY3: + { + // Play audio taunt #3 if isn't selected + char path[_MAX_PATH]; + int index = taunts_lists.taunt_c->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + ddio_MakePath(path,LocalCustomSoundsDir,p,NULL); + mprintf((0,"Playing: %s\n",path)); + bool cenable = taunt_AreEnabled(); + taunt_Enable(true); + taunt_PlayTauntFile(path); + taunt_Enable(cenable); + } + } + }break; + + case ID_PLAY4: + { + // Play audio taunt #4 if isn't selected + char path[_MAX_PATH]; + int index = taunts_lists.taunt_d->GetCurrentIndex(); + if(index>0 && cust_snds.files){ + char *p = GetStringInList(index-1,cust_snds.files,cust_snds.needed_size); + if(p){ + ddio_MakePath(path,LocalCustomSoundsDir,p,NULL); + mprintf((0,"Playing: %s\n",path)); + bool cenable = taunt_AreEnabled(); + taunt_Enable(true); + taunt_PlayTauntFile(path); + taunt_Enable(cenable); + } + } + }break; + + case ID_TAUNTS: + { + DoPilotTauntScreen(Pilot); + break; + } + case ID_IMPORTSOUND: + { + //Import the sound, set it's sample to xx.xKhz and xbit depth, attach the CRC to the filename + //and place in custom/sounds. Then update the audio taunt combo boxes + char path[_MAX_PATH]; + char wildcards[100]; +#ifdef MACINTOSH + ddio_MakePath(path,Base_directory,"custom","sounds",NULL); +#else + path[0] = '\0'; +#endif + strcpy(wildcards,"*.wav"); + if(DoPathFileDialog(false,path,TXT_CHOOSE,wildcards,PFDF_FILEMUSTEXIST)) + { + char dpath[_MAX_PATH*2]; + char filename[_MAX_PATH]; + char tempfile[_MAX_PATH]; + + ddio_SplitPath(path,NULL,filename,NULL); + strcat(filename,".osf"); + ddio_MakePath(tempfile,LocalCustomSoundsDir,filename,NULL); + + //import the sound + mprintf((0,"Importing: '%s'->'%s'\n",path,tempfile)); + if(taunt_ImportWave(path,tempfile)){ + //success + + //check file size...make sure it isn't too big + CFILE *file = cfopen(tempfile,"rb"); + + if(file) { + + if(cfilelength(file)>MAX_AUDIOTAUNTSIZE) + { + //file too big!!!!!! + char message[256]; + sprintf(message,TXT_COMPRESSTOOBIG,MAX_AUDIOTAUNTSIZE/1024); + DoMessageBox(TXT_WARNING,message,MSGBOX_OK); + } + + cfclose(file); + + if(CreateCRCFileName(tempfile,dpath)){ + //dpath is destination file + //tempfile is source file + if(cf_CopyFile(dpath,tempfile,0)){ + //file copied... + DoMessageBox(TXT_SUCCESS,TXT_AUDIOIMPORTSUC,MSGBOX_OK); + //delete temp file + ddio_DeleteFile(tempfile); + //Put path in the custom\sounds as dpath (convert if needed) + UpdateAudioTauntBoxes(&cust_snds,taunts_lists.taunt_a,taunts_lists.taunt_b,taunts_lists.taunt_c,taunts_lists.taunt_d,Pilot); + }else{ + DoMessageBox(TXT_ERROR,TXT_COPYTEMPERR,MSGBOX_OK); + //delete temp file + ddio_DeleteFile(tempfile); + } + }else{ + //couldn't generate a crc filename + DoMessageBox(TXT_ERROR,TXT_CANTCREATECRC,MSGBOX_OK); + //delete temp file + ddio_DeleteFile(tempfile); + } + }//end else (file size) + }else{ + int imp_err = taunt_GetError(); + char *err = taunt_GetErrorString(imp_err); + //failure + DoMessageBox(TXT_ERROR,err,MSGBOX_OK); + } + } + ddio_SetWorkingDir(path); + }break; + + case ID_DEL_LOGO: + { + ShipSelectDeleteLogo(&cust_bmps,custom_list); + }break; + + case ID_DEL_TAUNTA: + { + ShipSelectDeleteTaunt(Pilot,&cust_snds,taunts_lists.taunt_a,&taunts_lists); + }break; + + case ID_DEL_TAUNTB: + { + ShipSelectDeleteTaunt(Pilot,&cust_snds,taunts_lists.taunt_b,&taunts_lists); + }break; + + case ID_DEL_TAUNTC: + { + ShipSelectDeleteTaunt(Pilot,&cust_snds,taunts_lists.taunt_c,&taunts_lists); + }break; + + case ID_DEL_TAUNTD: + { + ShipSelectDeleteTaunt(Pilot,&cust_snds,taunts_lists.taunt_d,&taunts_lists); + }break; + } + } + + +ship_id_err: + + //no use for this since stream files will stop + //Sound_system.StopAllSounds(); + + taunts_lists.taunt_a = NULL; + taunts_lists.taunt_b = NULL; + taunts_lists.taunt_c = NULL; + taunts_lists.taunt_d = NULL; + + cust_snds.Reset(); + + cust_bmps.Reset(); + ship_info.Reset(); + + if( (ret)&&(ship_pos.bm_handle<=BAD_BITMAP_HANDLE) ){ + GameTextures[ship_pos.texture_id].flags&=~TF_ANIMATED; + GameTextures[ship_pos.texture_id].flags&=~TF_TEXTURE_32; + GameTextures[ship_pos.texture_id].flags|=TF_TEXTURE_64; + GameTextures[ship_pos.texture_id].bm_handle=BAD_BITMAP_HANDLE; + } + + if(ship_pos.bm_handle>BAD_BITMAP_HANDLE){ + int handle; + bool anim; + if(ret){ + handle = old_bmhandle; + anim = (bool)((old_flags&TF_ANIMATED)!=0); + }else{ + handle = ship_pos.bm_handle; + anim = (bool)(ship_pos.texture_type>0); + } + if(handle>BAD_BITMAP_HANDLE){ + if(anim) + FreeVClip(handle); + else + bm_FreeBitmap(handle); + } + } + ship_pos.bm_handle = -1; + + window.Close(); + window.Destroy(); + + return ret; +} + + +void CustomCallBack(int c) +{ + char select_bitmap[_MAX_PATH]; + char *sbmp = GetStringInList(c-1,lp_cust_bmps->files,lp_cust_bmps->needed_size); + if(sbmp) + strcpy(select_bitmap,sbmp); + else + select_bitmap[0] = '\0'; + + if(!strcmp(custom_texture,select_bitmap)) + return; + + if(ship_pos.bm_handle>BAD_BITMAP_HANDLE){ + if(ship_pos.texture_type) + FreeVClip(ship_pos.bm_handle); + else + bm_FreeBitmap(ship_pos.bm_handle); + ship_pos.bm_handle = -1; + } + ship_pos.texture_changed = true; + + if(c==0){ + //None selected + custom_texture[0]='\0'; + mprintf((0,"None selected\n")); + bmpwindow->SetInfo(false,-1); + GameTextures[ship_pos.texture_id].flags&=~TF_ANIMATED; + GameTextures[ship_pos.texture_id].flags&=~TF_TEXTURE_32; + GameTextures[ship_pos.texture_id].flags|=TF_TEXTURE_64; + GameTextures[ship_pos.texture_id].bm_handle=BAD_BITMAP_HANDLE; + }else{ + //Get the filename + char *p = GetStringInList(c-1,lp_cust_bmps->files,lp_cust_bmps->needed_size); + if(p) + strcpy(custom_texture,p); + else + custom_texture[0]='\0'; + ship_pos.bm_handle = LoadTextureImage (IGNORE_TABLE(custom_texture),&ship_pos.texture_type,SMALL_TEXTURE,1); + if(ship_pos.bm_handle>BAD_BITMAP_HANDLE){ + if (ship_pos.texture_type) + GameTextures[ship_pos.texture_id].flags|=TF_ANIMATED; + else + GameTextures[ship_pos.texture_id].flags&=~TF_ANIMATED; + + GameTextures[ship_pos.texture_id].flags&=~TF_TEXTURE_32; + GameTextures[ship_pos.texture_id].flags|=TF_TEXTURE_64; + + GameTextures[ship_pos.texture_id].bm_handle=ship_pos.bm_handle; + + bmpwindow->SetInfo(ship_pos.texture_type?true:false,ship_pos.bm_handle); + + mprintf((0,"Loaded texture (%s). Type=%d, ID=%d\n",custom_texture,ship_pos.texture_type,ship_pos.texture_id)); + }else + goto load_texture_err; + } + + return; + +load_texture_err: + ship_pos.bm_handle = BAD_BITMAP_HANDLE; + GameTextures[ship_pos.texture_id].flags&=~TF_ANIMATED; + GameTextures[ship_pos.texture_id].flags&=~TF_TEXTURE_32; + GameTextures[ship_pos.texture_id].flags|=TF_TEXTURE_64; + GameTextures[ship_pos.texture_id].bm_handle=BAD_BITMAP_HANDLE; + strcpy(custom_texture,""); + mprintf((0,"Unable to load texture\n")); + bmpwindow->SetInfo(false,-1); +} + +extern float Render_zoom; +void ShipSelectCallBack(int c) +{ + if(!lp_ship_info->idlist) + return; + + float size; + ship_model = Ships[lp_ship_info->idlist[c]].model_handle; + if(ship_model==-1){ + mprintf((0,"ship_model is -1\n")); + Int3(); + } + + DoWaitMessage(true); + + PageInPolymodel (ship_model, OBJ_PLAYER, &size); + poly_model *pm = GetPolymodelPointer(ship_model); + + ship_pos.cam_dist = pm->anim_size / Render_zoom + 5; + + DoWaitMessage(false); +} + +extern char LocalCustomGraphicsDir[TABLE_NAME_LEN]; +extern char LocalCustomSoundsDir[TABLE_NAME_LEN]; + +// Deletes the currently selected ship logo +void ShipSelectDeleteLogo(tCustomListInfo *cust_bmps,newuiListBox *lb) +{ + ASSERT(lb); + ASSERT(cust_bmps); + + int selected_index = lb->GetCurrentIndex(); + char custom_filename[384]; + char custom_logoname[384]; + + //check for None selected + if(selected_index==0) + { + mprintf((0,"Listbox selected item is None\n")); + return; + } + + lb->GetItem(selected_index,custom_logoname,384); + + //Get the filename + char *p = GetStringInList(selected_index-1,cust_bmps->files,cust_bmps->needed_size); + if(p) + { + strncpy(custom_filename,p,383); + custom_filename[383] = '\0'; + }else + { + mprintf((0,"Listbox selected item not found\n")); + Int3(); + return; + } + + //delete custom_filename, we don't want it.... + char buffer[512]; + sprintf(buffer,TXT_PLTOKDEL,custom_logoname); + if(DoMessageBox(TXT_PLTDELCONF,buffer,MSGBOX_YESNO,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL)) + { + mprintf((0,"Deleting pilot logo %s (%s)\n",custom_logoname,custom_filename)); + + char olddir[_MAX_PATH]; + ddio_GetWorkingDir(olddir,_MAX_PATH); + ddio_SetWorkingDir(LocalCustomGraphicsDir); + if(ddio_DeleteFile(custom_filename)) + { + // Update the list box, select none + UpdateGraphicsListbox(cust_bmps,lb,NULL); + }else + { + mprintf((0,"Unable to delete file %s\n",custom_filename)); + Int3(); + } + ddio_SetWorkingDir(olddir); + } +} + +// Deletes the currently selected audio taunt +void ShipSelectDeleteTaunt(pilot *Pilot,tCustomListInfo *cust_snds,newuiComboBox *lb,tAudioTauntComboBoxes *taunt_boxes) +{ + ASSERT(Pilot); + ASSERT(cust_snds); + ASSERT(taunt_boxes); + + int selected_index = lb->GetCurrentIndex(); + char custom_filename[384]; + char custom_logoname[384]; + + //check for None selected + if(selected_index==0) + { + mprintf((0,"Listbox selected item is None\n")); + return; + } + + lb->GetItem(selected_index,custom_logoname,384); + + //Get the filename + char *p = GetStringInList(selected_index-1,cust_snds->files,cust_snds->needed_size); + if(p) + { + strncpy(custom_filename,p,383); + custom_filename[383] = '\0'; + }else + { + mprintf((0,"Listbox selected item not found\n")); + Int3(); + return; + } + + //delete custom_filename, we don't want it.... + char buffer[512]; + sprintf(buffer,TXT_PLTOKDEL,custom_logoname); + if(DoMessageBox(TXT_PLTDELCONF,buffer,MSGBOX_YESNO,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL)) + { + mprintf((0,"Deleting audio taunt %s (%s)\n",custom_logoname,custom_filename)); + + char olddir[_MAX_PATH]; + ddio_GetWorkingDir(olddir,_MAX_PATH); + ddio_SetWorkingDir(LocalCustomSoundsDir); + if(ddio_DeleteFile(custom_filename)) + { + // Update the list boxes, select none + UpdateAudioTauntBoxes(cust_snds,taunt_boxes->taunt_a,taunt_boxes->taunt_b,taunt_boxes->taunt_c,taunt_boxes->taunt_d,Pilot); + }else + { + mprintf((0,"Unable to delete file %s\n",custom_filename)); + Int3(); + } + ddio_SetWorkingDir(olddir); + } +} + +void UI3DWindow::OnDraw() +{ + AudioStream::Frame(); + + static polymodel_effect pefx; + + vector viewer_eye = {0,0,0}; + matrix viewer_orient = IDENTITY_MATRIX; + viewer_eye.z = -ship_pos.cam_dist; + +// 3d start frame + //@@@@@StartFrame(m_X,m_Y,m_X+m_W-1,m_Y+m_H-1); + g3_StartFrame(&viewer_eye,&viewer_orient,D3_DEFAULT_ZOOM); + rend_SetFlatColor (0); + + if(ship_model==-1){ + mprintf((0,"Shipmodel is -1\n")); + return; + } + + float normalized_time[MAX_SUBOBJECTS]; + float light_scalar,size; + PageInPolymodel (ship_model, OBJ_PLAYER, &size); + poly_model *pm = GetPolymodelPointer(ship_model); + vector view_pos; + vector light_vec; + matrix view_orient = IDENTITY_MATRIX; + matrix rot_mat = IDENTITY_MATRIX; + matrix final_mat = IDENTITY_MATRIX; + +// draw model. + SetNormalizedTimeAnim(0, normalized_time, pm); + + view_pos = pm->anim_size_offset; + + //move 30 degrees a sec + float new_time = timer_GetTime(); + + vm_AnglesToMatrix(&rot_mat,0,(new_time - ship_pos.last_time)*(65535/360)*30,0); + vm_MatrixMul (&view_orient,&rot_mat,&ship_pos.last_frame); + vm_Orthogonalize(&view_orient); + ship_pos.last_frame = view_orient; + + + light_vec.x = 0.0f; light_vec.y = -1.0f; light_vec.z = -1.0f; + light_scalar = 0.8f; + vm_NormalizeVector(&light_vec); + + rend_SetZBufferState(1); + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (255); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + + if(ship_pos.bm_handle>BAD_BITMAP_HANDLE){ + if(ship_pos.texture_changed){ + memset(&pefx,0,sizeof(pefx)); + pefx.custom_texture = ship_pos.texture_id; + pefx.type = PEF_CUSTOM_TEXTURE; + ship_pos.texture_changed = false; + } + + SetPolymodelEffect (&pefx); + + DrawPolygonModel(&view_pos, &view_orient, ship_model, normalized_time,0, &light_vec, light_scalar,light_scalar,light_scalar,0xFFFFFFFF,1); + }else{ + DrawPolygonModel(&view_pos, &view_orient, ship_model, normalized_time,0, &light_vec, light_scalar,light_scalar,light_scalar); + } + + g3_EndFrame(); + //@@@@@@@@@@@EndFrame(); + + UIStatic::OnDraw(); + + ship_pos.last_time = new_time; +} + +UIBmpWindow::UIBmpWindow() +{ + animated = false; + bm_handle = -1; + start_time = timer_GetTime(); + created = false; +} + +UIBmpWindow::~UIBmpWindow() +{ +} + +void UIBmpWindow::SetInfo(bool anim,int handle) +{ + animated = anim; + bm_handle = handle; + start_time = timer_GetTime(); +} + +void UIBmpWindow::DrawBorder(void) +{ + int minx,maxx,miny,maxy; + + minx = 0; + maxx = m_W-1; + miny = 0; + maxy = m_H-1; + + rend_SetZBufferState(0); + rend_SetFlatColor(GR_RGB(40,255,40)); + + rend_DrawLine(minx,miny,maxx,miny); + rend_DrawLine(minx,maxy,maxx,maxy); + rend_DrawLine(minx,miny,minx,maxy); + rend_DrawLine(maxx,miny,maxx,maxy); + + rend_SetZBufferState(1); +} + +void UIBmpWindow::OnDraw(void) +{ + int bmh = -1; + + //@@@@@StartFrame(m_Wnd->X(),m_Wnd->Y(),m_Wnd->X()+m_Wnd->W(),m_Wnd->Y()+m_Wnd->H()); + + if(bm_handle==-1){ + UIStatic::OnDraw(); + DrawBorder(); + //@@@@@@@@@@@EndFrame(); + return; + } + + if(animated){ + vclip *vc = &GameVClips[bm_handle]; + + float elapsed_time = timer_GetTime() - start_time; + float interval = 1.0f/10.0f; + int frames_elapsed = (int)(elapsed_time/interval); + + int frame = frames_elapsed % vc->num_frames; + bmh = vc->frames[frame]; + }else{ + bmh = bm_handle; + } + + //draw the bitmap + rend_SetAlphaType (AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (255); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetFiltering (0); + + int real_x,real_y; + + real_x = 5; + real_y = 5; + + rend_DrawScaledBitmap (real_x,real_y,real_x+31,real_y+31,bmh,0,0,1,1); + + rend_SetFiltering (1); + + UIStatic::OnDraw(); + + DrawBorder(); + //@@@@@@@EndFrame(); +} + + +struct{ + int blank_bmp; + int curr_bmp; + int curr_index; + ushort size; + ushort *id_list; + newuiListBox *listbox; + UIStatic *bmp_disp; +}PPicDlgInfo; + +void ShowPilotPicDialogListCallback(int index) +{ + if(index==PPicDlgInfo.curr_index) + return; + + int new_idx = index; + + if(PPicDlgInfo.blank_bmp!=PPicDlgInfo.curr_bmp){ + bm_FreeBitmap(PPicDlgInfo.curr_bmp); + PPicDlgInfo.curr_bmp = -1; + } + + if(index==0){ + // is selected + PPicDlgInfo.curr_bmp = PPicDlgInfo.blank_bmp; + }else{ + if(index<=PPicDlgInfo.size){ + int handle = PPic_GetBitmapHandle(PPicDlgInfo.id_list[index-1]); + if(handle<=BAD_BITMAP_HANDLE){ + mprintf((0,"Couldn't get ID#%d's bitmap\n",PPicDlgInfo.id_list[index-1])); + Int3(); + PPicDlgInfo.curr_bmp = PPicDlgInfo.blank_bmp; + new_idx = 0; + }else{ + PPicDlgInfo.curr_bmp = handle; + } + }else{ + mprintf((0,"Invalid index\n")); + Int3(); + PPicDlgInfo.curr_bmp = PPicDlgInfo.blank_bmp; + new_idx = 0; + } + } + + //change the bitmap + UIBitmapItem bmp_item(PPicDlgInfo.curr_bmp); + PPicDlgInfo.bmp_disp->SetTitle(&bmp_item); + + PPicDlgInfo.curr_index = new_idx; + PPicDlgInfo.listbox->SetCurrentIndex(new_idx); +} + +// ------------------------------------------------------- +// ShowPilotPicDialog +// Purpose: +// Displays the dialog to choose a pilot picture for +// the given pilot. +// ------------------------------------------------------- +void ShowPilotPicDialog(pilot *Pilot) +{ + ASSERT(Pilot); + if(!Pilot) return; + + char pname[PILOT_STRING_SIZE]; + + Pilot->get_name(pname); + + ushort num_pilots = PPic_QueryPilot(pname); + int blank_bmp_handle; + + //only display the dialog if there is a pilot to choose from + if(num_pilots==0){ + mprintf((0,"No Pilot Pics available for %s\n",pname)); + ushort pid; + pid = PPIC_INVALID_ID; + Pilot->set_multiplayer_data(NULL,NULL,NULL,&pid); + + DoMessageBox(TXT_ERROR,TXT_NOPICSAVAIL,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + return; + } + + blank_bmp_handle = bm_AllocBitmap(64,64,0); + if(blank_bmp_handle<=BAD_BITMAP_HANDLE){ + ushort pid; + pid = PPIC_INVALID_ID; + Pilot->set_multiplayer_data(NULL,NULL,NULL,&pid); + + mprintf((0,"Couldn't alloc bitmap\n")); + DoMessageBox(TXT_ERROR,TXT_ERRCREATINGDIALOG,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + return; + } + ushort *data = bm_data(blank_bmp_handle,0); + for(int i=0;i<64*64;i++){ + data[i] = GR_RGB16(0,0,0)|OPAQUE_FLAG; + } + + // create the dialog + // ----------------- +#define DLG_PPIC_W 320 +#define DLG_PPIC_H 256 + newuiTiledWindow hwnd; + newuiSheet *sheet; + newuiListBox *list; + + UIBitmapItem bmp_item(blank_bmp_handle); + UIStatic bmp_disp; + bool exit_menu = false; + + hwnd.Create(TXT_PILOTPICTURE,0,0,DLG_PPIC_W,DLG_PPIC_H); + sheet = hwnd.GetSheet(); + + sheet->NewGroup(NULL,0,0); + list = sheet->AddListBox(150,150,IDP_SAVE,UILB_NOSORT); + list->SetSelectChangeCallback(ShowPilotPicDialogListCallback); + + sheet->NewGroup(TXT_DISPLAY,175,0); + bmp_disp.Create(&hwnd,&bmp_item,215,65,bmp_item.width(),bmp_item.height(),UIF_FIT); + + sheet->NewGroup(NULL,170,145); + sheet->AddButton(TXT_OK,UID_OK); + + // Initialize PPicDlgInfo data + // --------------------------- + ushort *id_list; + id_list = (ushort *)mem_malloc(num_pilots*sizeof(ushort)); + + if(!id_list){ + //out of memory + mprintf((0,"Out of memory\n")); + goto clean_up; + } + + PPicDlgInfo.curr_bmp = PPicDlgInfo.blank_bmp = blank_bmp_handle; + PPicDlgInfo.curr_index = 0; + PPicDlgInfo.listbox = list; + PPicDlgInfo.bmp_disp = &bmp_disp; + PPicDlgInfo.size = num_pilots; + PPicDlgInfo.id_list = id_list; + + // fill in dialog data + // ------------------- + ushort temp_id; + int idx; + idx = 0; + + Pilot->get_name(pname); + + if(PPic_FindFirst(pname,&temp_id)){ + id_list[idx] = temp_id; + idx++; + while(PPic_FindNext(&temp_id)){ + id_list[idx] = temp_id; + idx++; + } + } + PPic_FindClose(); + + int selected_index; + selected_index = 0; + list->AddItem(TXT_NONE); + + char temp_buffer[PILOT_STRING_SIZE+6]; + ushort ppic_id; + Pilot->get_multiplayer_data(NULL,NULL,NULL,&ppic_id); + + Pilot->get_name(pname); + + if(num_pilots>1) + { + for(idx = 0;idxAddItem(temp_buffer); + } + }else + { + if(ppic_id==id_list[idx]) + selected_index = idx+1; + + Psprintf(temp_buffer,PILOT_STRING_SIZE+6,"%s",pname); + list->AddItem(temp_buffer); + } + + ShowPilotPicDialogListCallback(selected_index); + + // display dialog + // -------------- + hwnd.Open(); + while (!exit_menu){ + int res = hwnd.DoUI(); + + // handle all UI results. + switch (res){ + case UID_OK: + exit_menu = true; + break; + }; + } + + selected_index = list->GetCurrentIndex(); + ushort pid; + if(selected_index==0){ + pid = PPIC_INVALID_ID; + }else{ + pid = id_list[selected_index-1]; + } + Pilot->set_multiplayer_data(NULL,NULL,NULL,&pid); + + hwnd.Close(); + hwnd.Destroy(); + + + +clean_up: + // free data + // --------- + if( (PPicDlgInfo.curr_bmp != PPicDlgInfo.blank_bmp) && (PPicDlgInfo.curr_bmp>BAD_BITMAP_HANDLE)) + bm_FreeBitmap(PPicDlgInfo.curr_bmp); + + bm_FreeBitmap(blank_bmp_handle); + + if(id_list){ + mem_free(id_list); + } +} + + +// "Current Pilot" access functions +void dCurrentPilotName(char *buffer) +{ + Current_pilot.get_name(buffer); +} +ubyte dCurrentPilotDifficulty(void) +{ + ubyte d; + Current_pilot.get_difficulty(&d); + return d; +} + + +///////////////////////////////////////////////////////////////////// +void _ReadOldPilotFile(pilot *Pilot,bool keyconfig,bool missiondata) +{ + char filename[_MAX_PATH]; + char pfilename[_MAX_FNAME]; + char buffer[256]; + ubyte temp_b; + ushort temp_s; + int temp_i; + int filever,i,nctlfuncs; + + + Pilot->get_filename(pfilename); + + //open and process file +#ifdef MACINTOSH + ddio_MakePath(filename,Base_directory,"pilots",pfilename,NULL); +#else + ddio_MakePath(filename,Base_directory,pfilename,NULL); +#endif + CFILE *file = cfopen(filename,"rb"); + if(!file) + return; + + filever = cf_ReadInt(file); + + if(filever<0x12){ + cfclose(file); + Error(TXT_PLTTOOOLD); + return; + } + + if(filever>PLTFILEVERSION){ + //we're reading in a version that's newer than we have + cfclose(file); + Error(TXT_PLTTOONEW,pfilename); + return; + } + + Pilot->clean(true); + Pilot->set_filename(pfilename); + + cf_ReadString(buffer,PILOT_STRING_SIZE+1,file); + Pilot->set_name(buffer); + + cf_ReadInt(file); + cf_ReadInt(file); + + cf_ReadString(buffer,PAGENAME_LEN+1,file); + Pilot->set_ship(buffer); + + cf_ReadString(buffer,PAGENAME_LEN+1,file); + Pilot->set_multiplayer_data(buffer); + + temp_b = cf_ReadByte(file); + Pilot->set_difficulty(temp_b); + + // load hud info + temp_b = cf_ReadByte(file); + Pilot->set_hud_data(&temp_b); + + temp_s = cf_ReadShort(file); + Pilot->set_hud_data(NULL,&temp_s); + + temp_s = cf_ReadShort(file); + Pilot->set_hud_data(NULL,NULL,&temp_s); + + //read in audio taunts + cf_ReadString(buffer,PAGENAME_LEN+1,file); + Pilot->set_multiplayer_data(NULL,buffer); + + cf_ReadString(buffer,PAGENAME_LEN+1,file); + Pilot->set_multiplayer_data(NULL,NULL,buffer); + + // added in version 0x9 + int n; + ushort widx; + n = (int)cf_ReadByte(file); + for (i = 0; i < n; i++) + { + widx = (ushort)cf_ReadShort(file); + SetAutoSelectPrimaryWpnIdx(i, widx); + } + n = (int)cf_ReadByte(file); + for (i = 0; i < n; i++) + { + widx = (ushort)cf_ReadShort(file); + SetAutoSelectSecondaryWpnIdx(i, widx); + } + + temp_i = cf_ReadInt(file); + Pilot->set_hud_data(NULL,NULL,NULL,&temp_i); + temp_i = cf_ReadInt(file); + Pilot->set_hud_data(NULL,NULL,NULL,NULL,&temp_i); + + for (i = 0; i < N_MOUSE_AXIS; i++) + Pilot->mouse_sensitivity[i] = cf_ReadFloat(file); + for (i = 0; i < N_JOY_AXIS; i++) + Pilot->joy_sensitivity[i] = cf_ReadFloat(file); + + + temp_s = cf_ReadShort(file); + Pilot->set_multiplayer_data(NULL,NULL,NULL,&temp_s); + + //skip over mission data + int count = cf_ReadInt(file); + if(count>0){ + for( i = 0; i < count; i++){ + char dummy[256]; + int length; + cf_ReadByte(file); + if (filever >= 0x11) { + cf_ReadByte(file); + } + length = cf_ReadByte(file); + if (length) { + cf_ReadBytes((ubyte *)dummy,length,file); + } + } + } + + //key/joy config MUST stay at the end of the file + Pilot->read_controller = cf_ReadByte(file); + + // needed to properly map functions between pilot versions + nctlfuncs = (filever >=0xe) ? cf_ReadByte(file) : NUM_CTLFUNCS_DEMOv1_0; + + for (i = 0; i < nctlfuncs; i++) + { + int id = cf_ReadInt(file); + ct_type type[2]; + ct_config_data value; + int y; + + type[0] = (ct_type)cf_ReadInt(file); + type[1] = (ct_type)cf_ReadInt(file); + value = (ct_config_data)cf_ReadInt(file); + for (y=0; y < nctlfuncs; y++) + if (Controller_needs[y].id == id) { + if (type[0] == ctNone) // do this if there are new functions that don't have ctNone. + type[0] = Controller_needs[y].ctype[0]; + if (type[1] == ctNone) // do this if there are new functions that don't have ctNone. + type[1] = Controller_needs[y].ctype[1]; + break; + } + + Pilot->controls[y].id = id; + Pilot->controls[y].type[0] = type[0]; + Pilot->controls[y].type[1] = type[1]; + Pilot->controls[y].value = value; + + // new flags for each controller item. + if (filever >= 0xb) { + Pilot->controls[y].flags[0] = (ubyte)cf_ReadByte(file); + Pilot->controls[y].flags[1] = (ubyte)cf_ReadByte(file); + } + + if(keyconfig) + Controller->set_controller_function(Pilot->controls[y].id,Pilot->controls[y].type,Pilot->controls[y].value, Pilot->controls[y].flags); + } + +// fill in remainder of pilot controls array. + for (;iget_controller_function(Controller_needs[i].id,Pilot->controls[i].type, &Pilot->controls[i].value, Pilot->controls[i].flags); + } + + if (Controller) { + Controller->mask_controllers((Current_pilot.read_controller&READF_JOY)?true:false, + (Current_pilot.read_controller&READF_MOUSE)?true:false); + } + + // Read taunts if there + for (i=0;itaunts[i],PILOT_TAUNT_SIZE,file); + + cfclose(file); +} diff --git a/Descent3/pilot.h b/Descent3/pilot.h new file mode 100644 index 000000000..5da54cc2a --- /dev/null +++ b/Descent3/pilot.h @@ -0,0 +1,327 @@ +/* +* $Logfile: /DescentIII/main/pilot.h $ +* $Revision: 47 $ +* $Date: 5/02/99 12:55a $ +* $Author: Jeff $ +* +* Player/Pilot configuration +* +* $Log: /DescentIII/main/pilot.h $ + * + * 47 5/02/99 12:55a Jeff + * save ship permissions at highest level achieved and use that on restore + * to a level previously played + * + * 46 3/23/99 12:45p Jeff + * added preset control selection for pilot + * + * 45 3/22/99 6:22p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 44 3/15/99 9:25p Gwar + * + * 43 3/15/99 4:31p Jeff + * fixed some memory leaks + * + * 42 2/27/99 4:18p Jeff + * added support for .pld files (used to copy pilot default controls), + * audio taunt size import error fixed...removed dead code + * + * 41 2/25/99 4:30p Jeff + * mission data of pilot keeps track of all missions, not just after you + * beat a level + * + * 40 2/20/99 3:44a Jeff + * some more updates and bug fixes + * + * 39 2/15/99 7:50p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 38 1/11/99 4:08p Jason + * added multiplayer taunt macros + * + * 37 12/16/98 1:57p Samir + * added finished field to mission data structure. + * + * 36 12/15/98 4:28p Jeff + * added mission data information to the pilot files to save what the + * highest level they achieved on a mission is. Added level select dialog + * (not hooked up) and level warp cheat. + * + * 35 12/03/98 11:06a Samir + * added axis sensitivity + * + * 34 12/01/98 5:47p Jeff + * created pilot picture selection dialog + * + * 33 11/30/98 11:57a Jeff + * fixed bug, allowing unlimited pilots + * + * 32 10/22/98 2:58p Chris + * Difficulty levels are in beta + * + * 31 10/21/98 11:54p Samir + * fixed typos. + * + * 30 10/21/98 7:15p Samir + * added joy and mouse sensitivities for pilot. + * + * 29 10/17/98 7:31p Samir + * added invertible axes + * + * 28 10/15/98 1:36p Jeff + * allow cancel out of pilot select menu + * + * 27 10/14/98 6:39p Samir + * save screen size for game. + * + * 26 10/12/98 3:02p Jeff + * added a verify function, give warning when they go into Multiplayer + * ship customize with a bad ship + * + * 25 10/08/98 6:41p Jeff + * when creating your first pilot it immediatly returns you to main menu + * + * 24 9/08/98 11:41a Jeff + * new pilot selection interface + * + * 23 9/04/98 3:52p Jeff + * changes made from UI meeting + * + * 22 9/04/98 1:20p Jeff + * updates to ship selection, now includes audio taunts, strips crc's from + * filenames when displaying + * + * 21 8/29/98 6:53p Jeff + * added single-player ship selection + * + * 20 7/27/98 6:26p Jeff + * basic implementation of ship configurations...needs to be purtied up + * + * 19 6/24/98 7:36p Samir + * made pilot structure cleaner. + * + * 18 6/19/98 5:39p Samir + * save out hud mode too. + * + * 17 6/19/98 3:30p Samir + * added hud layout info in pilot file. + * + * 16 6/18/98 4:48p Samir + * added changes for multiple configs for joystick controls. + * + * 15 6/17/98 3:28p Jeff + * localization changes. made an init function + * + * 14 5/24/98 2:56a Jeff + * Pilot dialogs up to date + * + * 13 4/23/98 11:14p Samir + * added read_controller flag to pilot.cpp + * + * 12 4/01/98 3:34p Jeff + * ship_model is now a string + * + * 11 3/20/98 1:19p Jeff + * Changes made to use Default_pilot string for pilot filename to use. + * + * 10 3/19/98 6:58p Jeff + * Pilot stuff reads and writes to the correct directories + * + * 9 3/13/98 8:55p Jeff + * Various changes to move control configuration into Pilot file + * + * 8 3/12/98 2:00p Jeff + * Various changes to improve pilot dialogs + * + * 7 3/10/98 7:08p Jeff + * Various changes due to new window class + * + * 6 3/10/98 11:57a Jeff + * Corrected some function comments and made '.' a valid filename + * character + * + * 5 3/10/98 11:50a Jeff + * Added filename field to pilot structure, which keeps track of the pilot + * filename...fixes any bugs that come with renaming a file. Made changes + * to take advantage of this. + * + * 4 3/09/98 6:27p Jeff + * Cleaned up code, made file operations more robust, pretty sturdy now + * + * 3 3/06/98 6:32p Jeff + * Added Pilot files and major functionality + * + * 2 3/05/98 4:28p Jeff + * Initial creation +* +* $NoKeywords: $ +*/ + +#ifndef __PILOT_H_ +#define __PILOT_H_ + +#include +#include "pilot_class.h" + +/* +#include "controls.h" +#include "Controller.h" +#include "ship.h" + +#define PILOT_STRING_SIZE 20 +#define PPIC_INVALID_ID 65535 + +//errors reported by PltWriteFile() +#define PLT_FILE_FATAL -3 //can't open file, possibly out of hd space +#define PLT_FILE_CANT_CREATE -2 //bad filename, most likely 0 length string for filename +#define PLT_FILE_EXISTS -1 //file already exists (will only return this if newpilot=true on PltWriteFile +#define PLT_FILE_NOERR 0 //no errors, success + +// should be put somewhere else, but here for the demo. +#define N_MOUSE_AXIS 2 +#define N_JOY_AXIS 6 + +#define MAX_PILOT_TAUNTS 8 +#define PILOT_TAUNT_SIZE 60 + +typedef struct +{ + int id; + ct_type type[2]; + ct_config_data value; + ubyte flags[2]; +} cntrldata; + +typedef struct +{ + ubyte highest_level; + bool finished; // was mission finished? (different than highest level,btw) + char mission_name[MSN_NAMELEN]; +} tMissionData; + +typedef struct +{ + char filename[_MAX_FNAME]; //added because of some weird .plt renaming bugs that can happen + //not saved out + //Name of the Pilot + char name[PILOT_STRING_SIZE]; + //number of kills and number of times killed + int kills,deaths; + //name of ship model + char ship_model[PAGENAME_LEN]; + //filename of custom texture + char ship_logo[PAGENAME_LEN]; + //filename of audio taunt 1 (including CRC) + char audio1_file[PAGENAME_LEN]; + //filename of audio taunt 2 (including CRC) + char audio2_file[PAGENAME_LEN]; + //difficulty setting (DIFFICULTY_*) + char difficulty; + //controller settings + cntrldata controls[NUM_CONTROLLER_FUNCTIONS]; + // hud layout using the STAT mask + ushort hud_stat; + ushort hud_graphical_stat; + // hud display mode + ubyte hud_mode; + // do we read the controller port also (beyond keyboard/mouse) + char read_controller; + // axis sensitivities + float mouse_sensitivity[N_MOUSE_AXIS], joy_sensitivity[N_JOY_AXIS]; + + int game_window_w, game_window_h; + + //pilot picture image id + ushort picture_id; + + //number of mission's flown + int num_missions_flown; + tMissionData *mission_data; + + // taunt macros + char taunts[MAX_PILOT_TAUNTS][PILOT_TAUNT_SIZE]; + +}pilot; +*/ + +// PilotSelect +// +// Brings up a window where you can select a pilot to use +void PilotSelect(void); + +//Display the pilot display/selection screen +//void PilotDisplay(bool forceselection = false); + +//brings up the configuration screen for a pilot +// Returns: true on success +// Pilot: pointer to pilot to configure +// newpilot: true if this is a new pilot, false if an existing pilot +bool PilotConfig(pilot *Pilot,bool newpilot,bool forceok = false); + +//creates a new pilot (and pilot file) (if you call this you should call PilotConfig() after it) +// Returns: true on success +// Pilot: pointer to pilot structure to be filled in with name and filename (should then configure the rest) +bool PilotCreate(pilot *Pilot,bool dontallowcancel); + +//copies a pilot to another +bool PilotCopy(pilot *Src,pilot *Dest); + +//Write a Pilot out to file +// Pilot - Pilot to write (filename field is root of filename extension .plt) +// Make sure both filename and name are filled in before calling +// newpilot - Whether it is supposed to create a new file +// false = overwrite existing file +// true = don't overwrite any existing file, returns error if so +// Returns: PLT_FILE_* +int PltWriteFile(pilot *Pilot,bool newpilot=false); + +//Reads a Pilot from file +// Pilot - pointer to structure to fill in... +// MAKE SURE filename field is filled in with correct filename to read in before calling!! +// keyconfig - whether to set the controls on load +void PltReadFile(pilot *Pilot,bool keyconfig=false,bool missiondata=false); + +//Given a string it will make a valid filename out of it +void PltMakeFNValid(char *name); + +void PilotInit(void); + +void PltClearList(void); +char **PltGetPilots(int *count,char *ignore_filename=NULL,int display_default_configs=0); +void PltGetPilotsFree(void); + +// VerifyPilotData +// +// Call this function to check the data that is in the given pilot struct...it will verify that all files +// listed are available, if they are not, then it will set them to defaults. Returns true if it had to +// fix the data (you may want to save the pilot immediatly) +bool VerifyPilotData(pilot *Pilot); + +// updates the current pilot's information (level played, mission played, etc) +// call after every successful mission completion (by passing false) +// call when a mission is loaded (pass true) +void CurrentPilotUpdateMissionStatus(bool just_add_data=false); + +// gets highest level flown for mission +int PilotGetHighestLevelAchieved(pilot *Pilot,char *mission_name); +int GetPilotShipPermissions(pilot *Pilot,const char *mission_name); + +bool HasPilotFinishedMission(pilot* Pilot, const char *mission_name); +bool HasPilotFlownMission(pilot *Pilot,const char *mission_name); + +extern pilot Current_pilot; +extern char Default_pilot[_MAX_PATH]; + +// "Current Pilot" access functions +void dCurrentPilotName(char *buffer); +ubyte dCurrentPilotDifficulty(void); + +void IncrementPilotRestoredGamesForMission(pilot *Pilot,const char *mission_name); +void IncrementPilotSavedGamesForMission(pilot *Pilot,const char *mission_name); +int GetPilotNumRestoredGamesForMission(pilot *Pilot,const char *mission_name); +int GetPilotNumSavedGamesForMission(pilot *Pilot,const char *mission_name); + +#endif \ No newline at end of file diff --git a/Descent3/pilot_class.cpp b/Descent3/pilot_class.cpp new file mode 100644 index 000000000..8fa036050 --- /dev/null +++ b/Descent3/pilot_class.cpp @@ -0,0 +1,1837 @@ +/* +* $Logfile: /DescentIII/main/pilot_class.cpp $ +* $Revision: 30 $ +* $Date: 3/20/00 12:19p $ +* $Author: Matt $ +* +* Pilot class and access functions +* +* $Log: /DescentIII/main/pilot_class.cpp $ + * + * 30 3/20/00 12:19p Matt + * Merge of Duane's post-1.3 changes. + * Default to full-screen mode on Mac. + * Mouse sensitivity change (Mac only) + * + * 29 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 28 8/10/99 5:12p Jeff + * fixed compiler warnings + * + * 27 8/06/99 4:25p Samir + * set key ramping speed globally when loading pilot file. + * + * 26 7/20/99 1:18p Samir + * save state of rearviews between game through the pilot file. + * + * 25 5/21/99 10:42p Jeff + * init pilot ship to Pyro-GL for dedicated server needs + * + * 24 5/09/99 1:48a Jeff + * default rookie + * + * 23 5/02/99 12:56a Jeff + * save ship permissions at highest level achieved and use that on restore + * to a level previously played + * + * 22 5/01/99 1:36a Samir + * oops forgot to update pilot version number! + * + * 21 4/30/99 1:29p Samir + * added save for key ramping + * + * 20 4/27/99 4:18p Jeff + * fixed crash + * + * 19 4/27/99 3:58p Jeff + * added kiddie settings + * + * 18 4/27/99 1:56p Jeff + * audio taunts stuff in pilot menu, added stringtables + * + * 17 4/25/99 2:32a Jeff + * fixed bug trying to read pilot files that are too new + * + * 16 4/24/99 8:40p Samir + * added toggle to pilot for ship sounds. + * + * 15 4/20/99 7:28p Jeff + * added guidebot name + * + * 14 4/17/99 8:25p Samir + * took out graphical option for inventory display. no time. + * + * 13 4/15/99 2:55p Samir + * added UI for mouselook. + * + * 12 4/05/99 5:13p Samir + * added game toggles. + * + * 11 4/03/99 2:18a Jeff + * added profanity filter stuff + * + * 10 3/22/99 6:22p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 9 3/15/99 9:24p Gwar + * + * 8 3/05/99 2:44p Samir + * needs to be cleaned up later, but mouse and joystick sensitivities are + * read in always, and set by the controller system. A cancel method + * needs to be implemented for these functions. + * + * 7 3/03/99 5:09p Samir + * default to graphical hud! + * + * 6 2/25/99 8:55p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 5 2/24/99 12:55p Samir + * refresh weapon select info in pilot_class when writing out pilot file. + * + * 4 2/18/99 5:55p Jeff + * added weapon select data + * + * 3 2/16/99 12:00p Samir + * added new video resolution swtich test. + * + * 2 2/15/99 7:50p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 1 2/15/99 3:18a Jeff + * initial creation +* +* $NoKeywords: $ +*/ + +#include "pilot_class.h" +#include "ddio.h" +#include "hud.h" +#include "mem.h" +#include "pserror.h" +#include "mono.h" +#include "config.h" +#include "ship.h" +#include +#include + +#include "application.h" +#include "appdatabase.h" + +#include "stringtable.h" + +#define __PILOT_H_ //don't want to include pilot.h right now +#include "difficulty.h" + +void grtext_SetProfanityFilter(bool enabled); +void taunt_Enable(bool enable); + +extern float Key_ramp_speed; + + +#define PLT_FILE_VERSION 0x2B //pilot file version + + +//pilot file version history +#define PFV_REARVIEWINFO 0x2B // (SAMIR) save current state of rear small view. +#define PFV_SHIPPERMISSIONS 0x2A // (JEFF) save highest level ship permission in mission data +#define PFV_KEYRAMPING 0x29 // (SAMIR) added save of keyboard ramping value. +#define PFV_AUDIOTAUNTS 0x28 // (JEFF) added audiotaunts +#define PFV_GUIDEBOTNAME 0x26 // (JEFF) added guidebot name +#define PFV_MOUSELOOK 0x25 // (SAMIR) added mouselook option to pilot. +#define PFV_GAMETOGGLES 0x24 // (SAMIR) gameplay toggles +#define PFV_PROFANITY 0x23 // (JEFF) added profanity filter +#define PFV_AUDIOTAUNT3N4 0x22 // (JEFF) added saving of audio taunts 3 & 4 (and save/restore count for mission data) +#define PFV_WEAPONSELECT 0x21 // (JEFF) added saving weapon select + +pilot::~pilot() +{ + clean(false); +} + +pilot::pilot() +{ + write_pending = false; + initialize(); +} + +pilot::pilot(pilot *copy) +{ + write_pending = true; + initialize(); + +} + +pilot::pilot(char *fname) +{ + write_pending = true; + initialize(); +} + +//initializes all the data (for constructors) +void pilot::initialize(void) +{ + int i; + + filename = NULL; + name = NULL; + ship_model = mem_strdup("Pyro-GL"); + ship_logo = NULL; + audio1_file = NULL; + audio2_file = NULL; + audio3_file = NULL; + audio4_file = NULL; + guidebot_name = mem_strdup("GB"); + picture_id = PPIC_INVALID_ID; + difficulty = DIFFICULTY_ROOKIE; +#ifdef MACINTOSH + hud_mode = (ubyte)HUD_FULLSCREEN; +#else + hud_mode = (ubyte)HUD_COCKPIT; +#endif + hud_stat = 0; + hud_graphical_stat = STAT_STANDARD; + game_window_w = Video_res_list[Game_video_resolution].width; + game_window_h = Video_res_list[Game_video_resolution].height; + num_missions_flown = 0; + mission_data = NULL; + mouselook_control = false; + key_ramping = 0.35f; + lrearview_enabled = false; + rrearview_enabled = false; + + bool kiddie_settings = true; + + if(Database) + { + Database->read("ProfanityPrevention",&kiddie_settings); + } + + if(kiddie_settings) + { + profanity_filter_on = true; + audiotaunts = false; + }else + { + profanity_filter_on = false; + audiotaunts = true; + } + + gameplay_toggles.guided_mainview = false; + gameplay_toggles.show_reticle = true; + gameplay_toggles.ship_noises = true; + + // Copy taunts + for (i=0;iget_controller_function(Controller_needs[i].id,controls[i].type, &controls[i].value, controls[i].flags); + controls[i].id = Controller_needs[i].id; + } + }else + { + for(i=0;iship_model) ){ + found_model = true; + break; + } + } + + if(!found_model){ + //we couldn't find the ship model + had_to_change = true; + strcpy(Pilot->ship_model,DEFAULT_SHIP); + } + + //check the custom texture + if(Pilot->ship_logo[0]!='\0'){ + if(cfexist(Pilot->ship_logo)==CF_NOT_FOUND){ + //couldn't find the custom texture + Pilot->ship_logo[0] = '\0'; + had_to_change = true; + } + } + + //check audio files + if(Pilot->audio1_file[0]!='\0'){ + if(cfexist(Pilot->audio1_file)==CF_NOT_FOUND){ + //couldn't find the custom texture + Pilot->audio1_file[0] = '\0'; + had_to_change = true; + } + } + + if(Pilot->audio2_file[0]!='\0'){ + if(cfexist(Pilot->audio2_file)==CF_NOT_FOUND){ + //couldn't find the custom texture + Pilot->audio2_file[0] = '\0'; + had_to_change = true; + } + } + + char temp_buffer[PILOT_STRING_SIZE]; + if(Pilot->picture_id!=PPIC_INVALID_ID && !PPic_GetPilot(Pilot->picture_id,temp_buffer,PILOT_STRING_SIZE)){ + //the pilot picture id is invalid + Pilot->picture_id = PPIC_INVALID_ID; + had_to_change = true; + } + + return had_to_change; + */ +} + +// This function makes the pilot file so it's write pending, meaning that +// on the next call to flush, it will actually write out the data. There is +// no need to constantly do file access unless it's really needed +void pilot::write(void) +{ + write_pending = true; +} + +// This function makes certain that the pilot data is up to date with the +// actual pilot file, writing if needed. +int pilot::flush(bool new_file) +{ + if(!write_pending) + return PLTW_NO_ERROR; //no need to write, data hasn't changed + + if(!filename) + { + Int3(); + return PLTW_NO_FILENAME; //no filename was given + } + + + CFILE *file; + char real_filename[_MAX_PATH]; + + //open and process file +#ifdef MACINTOSH + ddio_MakePath(real_filename,Base_directory,"pilots",filename,NULL); +#else + ddio_MakePath(real_filename,Base_directory,filename,NULL); +#endif + + if(new_file && cfexist(real_filename)) + { + //the file already exists, we can't write out + mprintf((0,"PLTW: File (%s) exists, can't create\n",real_filename)); + return PLTW_FILE_EXISTS; + } + + try + { + verify(); + + file = cfopen(real_filename,"wb"); + if(!file) + { + mprintf((0,"PLTW: File (%s) can't be opened\n",real_filename)); + return PLTW_FILE_CANTOPEN; + } + + // Write out our fileversion + file_version = PLT_FILE_VERSION; + cf_WriteInt(file,file_version); + + write_name(file); + write_ship_info(file); + write_custom_multiplayer_data(file); + write_difficulty(file); + write_profanity_filter(file); + write_audiotaunts(file); + write_hud_data(file); + write_mission_data(file); + write_taunts(file); + write_weapon_select(file); + write_gameplay_toggles(file); // version 0x24 PFV_GAMETOGGLES + write_guidebot_name(file); + write_controls(file); + + cfclose(file); + + } + catch(cfile_error) + { + //catch and handle CFILE errors + mprintf((0,"PLTW: CFILE Exception writing data\n")); + Int3(); + try{ + cfclose(file); + }catch(...) + { + mprintf((0,"PLTW: Unable to close file due to exception\n")); + } + return PLTW_CFILE_FATAL; + } + catch(...) + { + //catch all errors + mprintf((0,"PLTW: Unknown exception writing data\n")); + Int3(); + try{ + cfclose(file); + }catch(...) + { + mprintf((0,"PLTW: Unable to close file due to exception\n")); + } + return PLTW_UNKNOWN_FATAL; + } + + write_pending = false; + + return PLTW_NO_ERROR; +} + +// This function sets the filename that is associated with this pilot +void pilot::set_filename(char *fname) +{ + if(filename) + { + mem_free(filename); + filename = NULL; + } + + filename = mem_strdup(fname); +} + +void pilot::get_filename(char *fname) +{ + if(filename) + { + strcpy(fname,filename); + }else + { + *fname = '\0'; + } +} + +// This function reads in the data from file (from the filename associated) +// into the pilot data. +int pilot::read(bool skip_config,bool skip_mission_data) +{ + if(!filename) + { + Int3(); + return PLTR_NO_FILENAME; //no filename was given + } + + CFILE *file; + char real_filename[_MAX_PATH]; + + //open and process file +#ifdef MACINTOSH + ddio_MakePath(real_filename,Base_directory,"pilots",filename,NULL); +#else + ddio_MakePath(real_filename,Base_directory,filename,NULL); +#endif + + if(!cfexist(real_filename)) + { + //the file already exists, we can't write out + mprintf((0,"PLTR: File (%s) does not exist\n",real_filename)); + return PLTR_FILE_NOEXIST; + } + + try + { + file = cfopen(real_filename,"rb"); + if(!file) + { + mprintf((0,"PLTR: File (%s) can't be opened\n",real_filename)); + return PLTR_FILE_CANTOPEN; + } + + // Write out our fileversion + file_version = cf_ReadInt(file); + + if(file_version>PLT_FILE_VERSION) + { + //too new!! + cfclose(file); + return PLTR_TOO_NEW; + } + + ////////////////////////////////////////////// + read_name(file,false); + read_ship_info(file,false); + read_custom_multiplayer_data(file,false); + read_difficulty(file,false); + if(file_version>=PFV_PROFANITY) + { + read_profanity_filter(file,false); + } + if(file_version>=PFV_AUDIOTAUNTS) + { + read_audiotaunts(file,false); + } + + read_hud_data(file,false); + read_mission_data(file,skip_mission_data); + read_taunts(file,false); + + if(file_version>=PFV_WEAPONSELECT) + { + read_weapon_select(file); + } + + if (file_version>=PFV_GAMETOGGLES) { + read_gameplay_toggles(file, false); + } + + if (file_version>=PFV_GUIDEBOTNAME){ + read_guidebot_name(file, false); + } + + read_controls(file,skip_config); + + ////////////////////////////////////////////// + int wpn; + for(wpn=0;wpn=PAGENAME_LEN) + { + ship_logo[PAGENAME_LEN-1] = '\0'; + } + } + write_pending = true; + } + //////////// + if(audio1) + { + if(audio1_file) + { + mem_free(audio1_file); + audio1_file = NULL; + } + audio1_file = mem_strdup(audio1); + if(audio1_file) + { + int len = strlen(audio1_file); + if(len>=PAGENAME_LEN) + { + audio1_file[PAGENAME_LEN-1] = '\0'; + } + } + write_pending = true; + } + //////////// + if(audio2) + { + if(audio2_file) + { + mem_free(audio2_file); + audio2_file = NULL; + } + audio2_file = mem_strdup(audio2); + if(audio2_file) + { + int len = strlen(audio2_file); + if(len>=PAGENAME_LEN) + { + audio2_file[PAGENAME_LEN-1] = '\0'; + } + } + write_pending = true; + } + //////////// + if(ppic) + { + picture_id = *ppic; + write_pending = true; + } + //////////// + if(audio3) + { + if(audio3_file) + { + mem_free(audio3_file); + audio3_file = NULL; + } + audio3_file = mem_strdup(audio3); + if(audio3_file) + { + int len = strlen(audio3_file); + if(len>=PAGENAME_LEN) + { + audio3_file[PAGENAME_LEN-1] = '\0'; + } + } + write_pending = true; + } + //////////// + if(audio4) + { + if(audio4_file) + { + mem_free(audio4_file); + audio4_file = NULL; + } + audio4_file = mem_strdup(audio4); + if(audio4_file) + { + int len = strlen(audio4_file); + if(len>=PAGENAME_LEN) + { + audio4_file[PAGENAME_LEN-1] = '\0'; + } + } + write_pending = true; + } + +} +void pilot::get_multiplayer_data(char *logo,char *audio1,char *audio2,ushort *ppic,char *audio3,char *audio4) +{ + if(logo) + { + if(ship_logo) + { + strcpy(logo,ship_logo); + }else + { + *logo = '\0'; + } + } + + if(audio1) + { + if(audio1_file) + { + strcpy(audio1,audio1_file); + }else + { + *audio1 = '\0'; + } + } + + if(audio2) + { + if(audio2_file) + { + strcpy(audio2,audio2_file); + }else + { + *audio2 = '\0'; + } + } + + if(audio3) + { + if(audio3_file) + { + strcpy(audio3,audio3_file); + }else + { + *audio3 = '\0'; + } + } + + if(audio4) + { + if(audio4_file) + { + strcpy(audio4,audio4_file); + }else + { + *audio4 = '\0'; + } + } + + if(ppic) + { + *ppic = picture_id; + } +} + +void pilot::set_difficulty(ubyte diff) +{ + difficulty = diff; + write_pending = true; +} +void pilot::get_difficulty(ubyte *diff) +{ + if(diff) + { + *diff = difficulty; + } +} + +void pilot::set_profanity_filter(bool enable) +{ + profanity_filter_on = enable; + write_pending = true; + grtext_SetProfanityFilter(enable); +} + +void pilot::get_profanity_filter(bool *enabled) +{ + if(enabled) + { + *enabled = profanity_filter_on; + } +} + +void pilot::set_audiotaunts(bool enable) +{ + audiotaunts = enable; + write_pending = true; + taunt_Enable(enable); +} + +void pilot::get_audiotaunts(bool *enabled) +{ + if(enabled) + { + *enabled = audiotaunts; + } +} + +void pilot::set_guidebot_name(char *name) +{ + if(guidebot_name) + { + mem_free(guidebot_name); + guidebot_name = NULL; + } + + if(name) + { + guidebot_name = mem_strdup(name); + }else + { + guidebot_name = mem_strdup("GB"); + } +} + +void pilot::get_guidebot_name(char *name) +{ + if(guidebot_name) + { + strcpy(name,guidebot_name); + }else + { + strcpy(name,"GB"); + } +} + +void pilot::set_hud_data(ubyte *hmode,ushort *hstat,ushort *hgraphicalstat,int *gw_w,int *gw_h) +{ + if(hmode) + { + //should do checking here + switch( *hmode ) + { + case HUD_COCKPIT: + case HUD_FULLSCREEN: + hud_mode = *hmode; + write_pending = true; + break; + default: + mprintf((0,"PILOT: Trying to set hode mode to invalid mode (%d)\n",*hmode)); + } + } + + if(hstat) + { + hud_stat = *hstat; + write_pending = true; + } + + if(hgraphicalstat) + { + hud_graphical_stat = *hgraphicalstat; + write_pending = true; + } + + if(gw_w) + { + game_window_w = *gw_w; + write_pending = true; + } + + if(gw_h) + { + game_window_h = *gw_h; + write_pending = true; + } + +} +void pilot::get_hud_data(ubyte *hmode,ushort *hstat,ushort *hgraphicalstat,int *gw_w,int *gw_h) +{ + if(hmode) + { + *hmode = hud_mode; + } + + if(hstat) + { + *hstat = hud_stat; + } + + if(hgraphicalstat) + { + *hgraphicalstat = hud_graphical_stat; + } + + if(gw_w) + { + *gw_w = game_window_w; + } + + if(gw_h) + { + *gw_h = game_window_h; + } +} + +void pilot::add_mission_data(tMissionData *mdata) +{ + if(!mdata) + { + Int3(); + return; + } + + if(find_mission_data(mdata->mission_name)!=-1) + { + Int3(); + mprintf((0,"Mission already exists\n")); + return; + } + + mprintf((0,"Adding new mission data for (%s)\n",mdata->mission_name)); + + tMissionData *new_data = (tMissionData *)mem_malloc((num_missions_flown+1)*sizeof(tMissionData)); + if(!new_data) + { + mprintf((0,"Out of memory\n")); + return; + } + + ASSERT(num_missions_flown>=0); + + if(mission_data && num_missions_flown>0) + { + memcpy(new_data,mission_data,sizeof(tMissionData)*num_missions_flown); + mem_free(mission_data); + } + + mission_data = new_data; + + memcpy(&mission_data[num_missions_flown],mdata,sizeof(tMissionData)); + + num_missions_flown++; +} +void pilot::edit_mission_data(int index,tMissionData *mdata) +{ + if(index<0 || index>=num_missions_flown) + { + mprintf((0,"Invalid mission index\n")); + Int3(); + return; + } + + if(!mission_data) + { + Int3(); + mprintf((0,"No mission data\n")); + return; + } + + if(!mdata) + { + Int3(); + return; + } + + memcpy(&mission_data[index],mdata,sizeof(tMissionData)); +} + +void pilot::get_mission_data(int index,tMissionData *mdata) +{ + if(index<0 || index>=num_missions_flown) + { + mprintf((0,"Invalid mission index\n")); + Int3(); + return; + } + + if(!mission_data) + { + Int3(); + mprintf((0,"No mission data\n")); + return; + } + + if(!mdata) + { + Int3(); + return; + } + + memcpy(mdata,&mission_data[index],sizeof(tMissionData)); +} + +int pilot::find_mission_data(char *mission_name) +{ + if(num_missions_flown<=0) + { + ASSERT(num_missions_flown==0); + return -1; + } + + ASSERT(mission_data); + if(!mission_data) + return -1; + + for( int i=0;i=PFV_AUDIOTAUNT3N4) + { + cf_ReadString(buffer,PAGENAME_LEN,file); + if(!skip) + { + if(audio3_file) + { + mem_free(audio3_file); + audio3_file = NULL; + } + audio3_file = mem_strdup(buffer); + } + + cf_ReadString(buffer,PAGENAME_LEN,file); + if(!skip) + { + if(audio4_file) + { + mem_free(audio4_file); + audio4_file = NULL; + } + audio4_file = mem_strdup(buffer); + } + } + + temp = cf_ReadShort(file); + if(!skip) + { + picture_id = temp; + } + +} + +void pilot::write_difficulty(CFILE *file) +{ + cf_WriteByte(file,difficulty); +} + +void pilot::read_difficulty(CFILE *file,bool skip) +{ + ubyte temp; + + temp = cf_ReadByte(file); + if(!skip) + { + difficulty = temp; + } +} + +void pilot::write_profanity_filter(CFILE *file) +{ + cf_WriteByte(file,(profanity_filter_on)?1:0); +} + +void pilot::read_profanity_filter(CFILE *file,bool skip) +{ + bool temp; + + temp = cf_ReadByte(file)?true:false; + if(!skip) + { + profanity_filter_on = temp; + grtext_SetProfanityFilter(profanity_filter_on); + } +} + +void pilot::write_audiotaunts(CFILE *file) +{ + cf_WriteByte(file,(audiotaunts)?1:0); +} + +void pilot::read_audiotaunts(CFILE *file,bool skip) +{ + bool temp; + + temp = cf_ReadByte(file)?true:false; + if(!skip) + { + audiotaunts = temp; + taunt_Enable(audiotaunts); + } +} + +void pilot::write_guidebot_name(CFILE *file) +{ + if(guidebot_name) + { + cf_WriteByte(file,1); + cf_WriteString(file,guidebot_name); + }else + { + cf_WriteByte(file,0); + } +} + +void pilot::read_guidebot_name(CFILE *file,bool skip) +{ + if(guidebot_name) + { + mem_free(guidebot_name); + guidebot_name = NULL; + } + + int len = cf_ReadByte(file); + char buffer[256]; + + if(len) + { + cf_ReadString(buffer,256,file); + guidebot_name = mem_strdup(buffer); + }else + { + guidebot_name = mem_strdup("GB"); + } +} + + +void pilot::write_hud_data(CFILE *file) +{ + cf_WriteByte(file,hud_mode); + cf_WriteShort(file,hud_stat); + cf_WriteShort(file,hud_graphical_stat); + cf_WriteInt(file,game_window_w); + cf_WriteInt(file,game_window_h); + +// PFV_REARVIEWINFO + cf_WriteByte(file, (sbyte)lrearview_enabled); + cf_WriteByte(file, (sbyte)rrearview_enabled); +} + + +void pilot::read_hud_data(CFILE *file,bool skip) +{ + ubyte temp_b; + ushort temp_s; + int temp_i; + + temp_b = cf_ReadByte(file); + if(!skip) + { + hud_mode = temp_b; + } + + temp_s = cf_ReadShort(file); + if(!skip) + { + hud_stat = temp_s; + } + + temp_s = cf_ReadShort(file); + if(!skip) + { + hud_graphical_stat = temp_s; + } + + temp_i = cf_ReadInt(file); + if(!skip) + { + game_window_w = temp_i; + } + + temp_i = cf_ReadInt(file); + if(!skip) + { + game_window_h = temp_i; + } + +// kill graphical stat for inventory and reset to text version + if (!skip) { + if (hud_graphical_stat & STAT_INVENTORY) { + hud_graphical_stat = hud_graphical_stat & (~STAT_INVENTORY); + hud_stat = hud_stat | STAT_INVENTORY; + } + } + +// read smallview state + if (file_version >= PFV_REARVIEWINFO) { + lrearview_enabled = (bool)(cf_ReadByte(file)!=0); + rrearview_enabled = (bool)(cf_ReadByte(file)!=0); + } + else { + lrearview_enabled = false; + rrearview_enabled = false; + } +} + +void pilot::write_mission_data(CFILE *file) +{ + int i; + cf_WriteInt(file,num_missions_flown); + + for(i=0;i=PFV_AUDIOTAUNT3N4) + { + temp_restores = cf_ReadInt(file); + temp_saves = cf_ReadInt(file); + }else + { + temp_restores = temp_saves = 0; + } + + if(file_version>=PFV_SHIPPERMISSIONS) + { + temp_perm = cf_ReadInt(file); + }else + { + temp_perm = Default_ship_permission; + } + + if(!skip) + { + mission_data[i].highest_level = temp_b1; + mission_data[i].ship_permissions = temp_perm; + mission_data[i].finished = (temp_b2)?true:false; + mission_data[i].num_restores = temp_restores; + mission_data[i].num_saves = temp_saves; + strcpy(mission_data[i].mission_name,temp_s); + } + } +} + +void pilot::write_taunts(CFILE *file) +{ + int i; + cf_WriteByte(file,MAX_PILOT_TAUNTS); + + for(i=0;iset_controller_function(controls[y].id,controls[y].type,controls[y].value, controls[y].flags); + } + + // fill in remainder of pilot controls array. + for (;iget_controller_function(Controller_needs[i].id,controls[i].type, &controls[i].value, controls[i].flags); + } + + // Set controller enabled masks + if (!skip && Controller) { + Controller->mask_controllers((read_controller&READF_JOY)?true:false,(read_controller&READF_MOUSE)?true:false); + } + + // mouse sensitivity + temp_b = cf_ReadByte(file); + for (i = 0; i= PFV_KEYRAMPING) { + temp_f = cf_ReadFloat(file); + key_ramping = temp_f; + } + if (file_version >= PFV_MOUSELOOK) { + temp_b = cf_ReadByte(file); + mouselook_control = temp_b ? true : false; + } +} + +void pilot::write_weapon_select(CFILE *file) +{ + int i; + + cf_WriteShort(file,MAX_PRIMARY_WEAPONS); + for(i=0;i 16) { Int3(); count = 16; } // bad, very bad. + + for (i = 0; i < count; i++) + { + toggles[i] = (bool)(cf_ReadByte(file) ? true : false); + } + +// define toggles. + if (!skip) { + gameplay_toggles.guided_mainview = toggles[0]; + gameplay_toggles.show_reticle = toggles[1]; + + // verify that we are setting values correctly, if new toggles are added to pilot. + gameplay_toggles.ship_noises = (count < 3) ? true : toggles[2]; + } +} + + +void pilot::write_gameplay_toggles(CFILE *file) +{ +// number of toggles to write out! + cf_WriteByte(file, 3); + + cf_WriteByte(file, (sbyte)gameplay_toggles.guided_mainview); + cf_WriteByte(file, (sbyte)gameplay_toggles.show_reticle); + cf_WriteByte(file, (sbyte)gameplay_toggles.ship_noises); +} + diff --git a/Descent3/pilot_class.h b/Descent3/pilot_class.h new file mode 100644 index 000000000..327f33205 --- /dev/null +++ b/Descent3/pilot_class.h @@ -0,0 +1,307 @@ +/* +* $Logfile: /DescentIII/main/pilot_class.h $ +* $Revision: 17 $ +* $Date: 3/20/00 12:07p $ +* $Author: Matt $ +* +* Pilot class and access functions +* +* $Log: /DescentIII/main/pilot_class.h $ + * + * 17 3/20/00 12:07p Matt + * Merge of Duane's post-1.3 changes. + * Changed difficulty level to be a global variable instead of a function + * call + * + * 16 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 15 7/20/99 1:18p Samir + * save state of rearviews between game through the pilot file. + * + * 14 5/02/99 12:56a Jeff + * save ship permissions at highest level achieved and use that on restore + * to a level previously played + * + * 13 4/30/99 1:29p Samir + * added save for key ramping + * + * 12 4/27/99 1:56p Jeff + * audio taunts stuff in pilot menu, added stringtables + * + * 11 4/25/99 2:33a Jeff + * fixed bug trying to read pilot files that are too new + * + * 10 4/20/99 7:29p Jeff + * added guidebot name + * + * 9 4/15/99 2:55p Samir + * added UI for mouselook. + * + * 8 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 7 4/05/99 5:13p Samir + * added game toggles. + * + * 6 4/03/99 2:19a Jeff + * added profanity filter stuff + * + * 5 3/22/99 6:22p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 4 3/15/99 9:24p Gwar + * + * 3 2/18/99 5:56p Jeff + * added weapon select data + * + * 2 2/15/99 7:50p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 1 2/15/99 3:18a Jeff + * initial creation +* +* $NoKeywords: $ +*/ + + +#ifndef __PILOT_CLASS_H_ +#define __PILOT_CLASS_H_ + +#include "pstypes.h" +#include "controls.h" +#include "Controller.h" +#include "CFILE.H" +#include "weapon.h" +#include "config.h" + +//YUCK! +#include "descent.h" //just for MSN_NAMELEN + + +/* +======================================================================= + +IIIIIIIII MM MM PPPP OOOO RRRR TTTTTTT AA NN NN TTTTTTT + II MM MM P P O O R R T AA NNN NN T + II M M M M P P O O R R T A A NN N NN T + II M M M M P P O O R R T A A NN N NN T + II M M M M PPP O O RRR T AAAAAA NN NNN T + II M M M P O O R R T A A NN NNN T +IIIIIIIII N N P OOOO R R T A A NN NN T + +======================================================================= + + If you add new data members to the pilot class, you _must_ do the following: + + 1) Add functionality to the pilot::initialize(); + 2) Add functionality to the pilot::clean(); + 3) Add functionality to the pilot::verify(); + 4) Create a pilot::write_ function + 5) Create a corresponding pilot::read_ function + 6) Add to calls to your write_ and read_ functions in pilot::flush() and pilot::read() +*/ + +// Maximum size of the pilot name +#define PILOT_STRING_SIZE 20 +// Invalid Pilot Pic ID +#define PPIC_INVALID_ID 65535 +// should be put somewhere else, but here for the demo. +#define N_MOUSE_AXIS 2 +#define N_JOY_AXIS 6 + +// Number of multiplayer taunts +#define MAX_PILOT_TAUNTS 8 +// Maximum string length of a taunt +#define PILOT_TAUNT_SIZE 60 + +//file error codes +#define PLTW_NO_ERROR 0 //there was no error +#define PLTW_NO_FILENAME 1 //no filename has been set +#define PLTW_FILE_EXISTS 2 //the file already exists +#define PLTW_FILE_CANTOPEN 3 //file couldn't be opened +#define PLTW_CFILE_FATAL 4 //a CFILE error had occurred +#define PLTW_UNKNOWN_FATAL 5 //an unknown exception occurred + +#define PLTR_NO_ERROR 0 //there was no error +#define PLTR_NO_FILENAME 1 //no filename has been set +#define PLTR_FILE_NOEXIST 2 //the file doesn't exist to read +#define PLTR_FILE_CANTOPEN 3 //file couldn't be opened +#define PLTR_CFILE_FATAL 4 //a CFILE error had occurred +#define PLTR_UNKNOWN_FATAL 5 //an uknown exception occurred +#define PLTR_TOO_NEW 6 //pilot file too new + +typedef struct +{ + ubyte highest_level; // highlest level completed in the mission + int ship_permissions; // Ship permissions at highest level achieved + bool finished; // was mission finished? (different than highest level,btw) + char mission_name[MSN_NAMELEN]; // name of the mission (from the mission file) + int num_restores; // number of game loads for this mission + int num_saves; // number of game saves for this mission +} tMissionData; + + +typedef struct +{ + int id; + ct_type type[2]; + ct_config_data value; + ubyte flags[2]; +} cntrldata; + +class pilot +{ + +public: + ~pilot(); + pilot(); + pilot(pilot *copy); + pilot(char *fname); + + // This function guts the data so it's virgin (fresh for reading) + // frees any memory that needs to be freed, etc. + // if reset is true, it resets all data to what it is at initialization + // else it is an unknown state + void clean(bool reset=true); + + // This function verifies all the pilot data, making sure nothing is out of whack + // It will correct any messed data. + void verify(void); + + // This function makes the pilot file so it's write pending, meaning that + // on the next call to flush, it will actually write out the data. There is + // no need to constantly do file access unless it's really needed + void write(void); + + // This function makes certain that the pilot data is up to date with the + // actual pilot file, writing if needed. + int flush(bool new_file); + + // This function sets the filename that is associated with this pilot + void set_filename(char *fname); + void get_filename(char *fname); + + // This function reads in the data from file (from the filename associated) + // into the pilot data. + int read(bool skip_control_set,bool skip_mission_data); + +public: + // data access functions + void set_name(char *name); + void get_name(char *name); + + void set_ship(char *ship); + void get_ship(char *ship); + + void set_multiplayer_data(char *logo=NULL,char *audio1=NULL,char *audio2=NULL,ushort *ppic=NULL,char *audio3=NULL,char *audio4=NULL); + void get_multiplayer_data(char *logo=NULL,char *audio1=NULL,char *audio2=NULL,ushort *ppic=NULL,char *audio3=NULL,char *audio4=NULL); + + void set_difficulty(ubyte diff); + void get_difficulty(ubyte *diff); + + void set_hud_data(ubyte *hmode=NULL,ushort *hstat=NULL,ushort *hgraphicalstat=NULL,int *gw_w=NULL,int *gw_h=NULL); + void get_hud_data(ubyte *hmode=NULL,ushort *hstat=NULL,ushort *hgraphicalstat=NULL,int *gw_w=NULL,int *gw_h=NULL); + + void set_profanity_filter(bool enable); + void get_profanity_filter(bool *enabled); + + void set_audiotaunts(bool enable); + void get_audiotaunts(bool *enabled); + + void set_guidebot_name(char *name); + void get_guidebot_name(char *name); + + void add_mission_data(tMissionData *data); + void edit_mission_data(int index,tMissionData *data); + void get_mission_data(int index,tMissionData *data); + int find_mission_data(char *mission_name); + +private: + void initialize(void); //initializes all the data (for constructors) + bool write_pending; //data has changed and pilot data is out of sync with file +private: + // internal file access functions + void write_name(CFILE *file); + void write_ship_info(CFILE *file); + void write_custom_multiplayer_data(CFILE *file); + void write_difficulty(CFILE *file); + void write_hud_data(CFILE *file); + void write_mission_data(CFILE *file); + void write_taunts(CFILE *file); + void write_weapon_select(CFILE *file); + void write_controls(CFILE *file); + void write_profanity_filter(CFILE *file); + void write_audiotaunts(CFILE *file); + void write_gameplay_toggles(CFILE *file); + void write_guidebot_name(CFILE *file); + + // for the read functions, skip is true if the data should actually + // just be skipped and not processed + int file_version; + void read_name(CFILE *file,bool skip); + void read_ship_info(CFILE *file,bool skip); + void read_custom_multiplayer_data(CFILE *file,bool skip); + void read_difficulty(CFILE *file,bool skip); + void read_hud_data(CFILE *file,bool skip); + void read_mission_data(CFILE *file,bool skip); + void read_taunts(CFILE *file,bool skip); + void read_weapon_select(CFILE *file); + void read_controls(CFILE *file,bool skip); + void read_profanity_filter(CFILE *file,bool skip); + void read_audiotaunts(CFILE *file,bool skip); + void read_gameplay_toggles(CFILE *file,bool skip); + void read_guidebot_name(CFILE *file,bool skip); + +private: + //--- Pilot data ---// + //--- Try to preserve alignment ---// + char *filename; //filename location of this pilot + char *name; //name of the pilot (used in the game) + char *ship_logo; //ship logo for multiplayer play (filename) + char *ship_model; //what ship does this pilot fly + char *audio1_file; //audio taunt #1 (filename) + char *audio2_file; //audio taunt #2 (filename) + char *audio3_file; //audio taunt #1 (filename) + char *audio4_file; //audio taunt #2 (filename) + char *guidebot_name; //guidebot name + + ushort picture_id; //pilot picture image id + ubyte difficulty; //difficulty setting for this pilot (DIFFICULTY_*) + ubyte hud_mode; // hud display mode + bool profanity_filter_on,audiotaunts; + + ushort hud_stat; // hud layout using the STAT mask + ushort hud_graphical_stat; + + int game_window_w, game_window_h; //game window size + + int num_missions_flown; //number of mission's flown + tMissionData *mission_data; //mission data + + ushort PrimarySelectList[MAX_PRIMARY_WEAPONS]; + ushort SecondarySelectList[MAX_SECONDARY_WEAPONS]; + + tGameToggles gameplay_toggles; // special options in config menu. + +public: + + char taunts[MAX_PILOT_TAUNTS][PILOT_TAUNT_SIZE];// taunt macros + + cntrldata controls[NUM_CONTROLLER_FUNCTIONS];//controller settings + float mouse_sensitivity[N_MOUSE_AXIS]; // axis sensitivities + float joy_sensitivity[N_JOY_AXIS]; // axis sensitivities + float key_ramping; + char read_controller; // do we read the controller port also (beyond keyboard/mouse) + bool mouselook_control; // mouselook control. + bool lrearview_enabled; + bool rrearview_enabled; // are these small views enabled? + + ubyte ingame_difficulty; //DAJ for optimization +}; + + + +#endif \ No newline at end of file diff --git a/Descent3/player.h b/Descent3/player.h new file mode 100644 index 000000000..82e1b9cb5 --- /dev/null +++ b/Descent3/player.h @@ -0,0 +1,582 @@ +/* + * $Logfile: /DescentIII/Main/player.h $ + * $Revision: 101 $ + * $Date: 9/29/01 5:34p $ + * $Author: Kevin $ + * + * Header for player.cpp + * + * $Log: /DescentIII/Main/player.h $ + * + * 101 9/29/01 5:34p Kevin + * hack to prevent the observermode powerup stealing cheat/bug + * + * 100 5/20/99 2:48a Matt + * Auto-waypoints now stored & used per player. Manual waypoints will all + * players, once Jason makes it work. + * + * 99 4/24/99 6:45p Jeff + * added functions for theif so he can steal things other than weapons + * + * 98 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 97 4/06/99 6:02p Matt + * Added score system + * + * 96 4/05/99 3:34p Matt + * Cleaned up player start flags + * + * 95 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 94 3/08/99 5:14p Jason + * added delay to player respawn after death + * + * 93 3/02/99 5:50p Kevin + * Ouch. Duplicate structures existed and were conflicting. + * + * 92 2/25/99 8:55p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 91 2/11/99 1:22a Jeff + * made function to switch a player into AI mode, converted code that did + * this to call this function. + * + * 90 2/09/99 6:53p Jeff + * implemented 'typing inidcator' in multiplayer...players that are typing + * messages have an icon on them + * + * 89 2/08/99 3:06a Jeff + * added a function to reset a player's control type + * + * 88 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 87 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 86 1/19/99 9:20a Kevin + * Added afterburner masking + * + * 85 1/18/99 6:18p Kevin + * Added controller masking to DALLAS + * + * 84 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 83 12/01/98 3:35p Matt + * Got rear view working. + * + * 82 11/23/98 1:50p Jason + * added thruster sounds + * + * 81 11/13/98 4:25p Jason + * changes for better weapon effects + * + * 80 11/13/98 12:30p Jason + * changes for weapons + * + * 79 11/11/98 7:18p Jeff + * changes made so that a dedicated server's team is always -1 (team game + * or not) + * + * 78 11/03/98 6:16p Chris + * Starting to make on/off and spray weapons accessable to robots + * + * 77 9/23/98 2:55p Chris + * Improved auto-leveling and added newbie leveling + * + * 76 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 75 9/16/98 5:10p Jason + * added first pass at thrid-person camera system + * + * 74 9/11/98 12:26p Jason + * got energy to shield and fusion damage working in multiplayer + * + * 73 9/10/98 12:18p Jason + * more changes to afterburner/thrust effect + * + * 72 8/28/98 1:34p Matt + * Added code to reset the waypoint when starting a new level, and while I + * was at it cleaned up the new level start sequencing. + * + * 71 8/24/98 2:45p Jason + * added team start position stuff + * + * 70 8/24/98 12:24p Jason + * added waypoints and player start position flags + * + * 69 8/19/98 6:27p Chris + * Added a last_fired_time for the player (used for AI and cloaking) + * + * 68 8/16/98 12:19a Jeff + * added energy->shields function + * + * 67 8/10/98 2:21p Jeff + * changes made due to adding flag for inventory reset + * + * 66 8/06/98 2:44p Jeff + * added ship permissions functions (first round) + * + * 65 8/03/98 5:56p Jason + * got fusion cannon working correctly + * + * 64 7/31/98 1:55p Jason + * added a function for custom textures + * + * 63 7/31/98 11:51a Jason + * added player ship choosing to multiplayer games + * + * 62 7/22/98 3:16p Jason + * added observer mode + * + * 61 7/20/98 6:39p Jason + * first attempt at getting player visibilty stuff working in multiplayer + * + * 60 7/20/98 10:42a Jason + * added more player scalars, plus afterburner changes + * + * 59 7/15/98 2:33p Jason + * added scalar variables for various player skills + * + * 58 7/03/98 3:09p Jeff + * added counter measures inventory + * + * 57 7/02/98 6:30p Jason + * added some multiplayer stuff for Jeff + * + * 56 6/29/98 3:08p Jason + * added on/off weapons + * + * 55 6/25/98 5:17p Jason + * added multiple colored balls for players + * + * 54 6/25/98 12:45p Jeff + * fixed callsign length, it was too short + * + * 53 5/26/98 7:49p Samir + * E3 version has a long afterburner. + * + * 52 5/25/98 8:17p Matt + * Moved MAX_PLAYER_WEAPONS fromw weapon.h to player.h, so that the latter + * doesn't need to include the former, drastically speeding build times + * when weapon.h is changed. + * + * 51 5/12/98 5:09p Kevin + * Added fields to player to keep more mastertracker stats + * + * 50 5/04/98 4:04p Chris + * Minestone checkin' -- energy effect and more AI functionality + * + * 49 5/01/98 6:50p Samir + * changed death sequence to fall on melee kill (single player) and death + * camera improvements. + * + * 48 4/30/98 2:53p Kevin + * added suicide to players + * + * 47 4/30/98 1:15p Kevin + * Added lifetime deaths and kills for MT + * + * 46 4/30/98 11:32a Chris + * ClearWB to WBClear + * + * 45 4/29/98 12:51p Kevin + * Added mastertracker values + * + * 44 4/27/98 1:14p Jason + * cleaned up afterburner stuff + * + * 43 4/17/98 1:59p Jason + * added cool object effects + * + * 42 4/13/98 12:42p Jason + * changed afterburner effect + * + * 41 4/09/98 5:18p Jason + * got guided working in multiplayer + * + * 40 4/09/98 2:23p Jason + * added guided/afterburner stuff + * + * 39 4/06/98 12:14p Jason + * changes to multiplayer + * + * 38 4/03/98 12:28p Jason + * fixed confusion with clients changing rooms in multiplayer + * + * 37 4/01/98 2:13p Jason + * added PlayerChangeShip + * + * 36 4/01/98 12:02p Jason + * incremental checkin for rendering changes + * + * 35 3/31/98 11:15a Jason + * added the ability for player objects to cast light + * + * 34 3/20/98 5:51p Jason + * more changes for multiplayer + * + * 33 3/20/98 3:54p Chris + * Working on adding sounds to the game. :) + * + * 32 3/20/98 2:59p Chris + * Added a wall hit sound for the player and added support for a base + * volume for 3d sounds + * + * 31 3/18/98 5:45p Jason + * more multiplayer script integration + * + * 30 3/17/98 3:25p Jason + * added number of team functions + * + * 29 3/17/98 11:22a Jason + * added GetGoalRoom functions + * + * 28 3/16/98 3:53p Jason + * added team field to player struct + * + * 27 2/17/98 3:41p Matt + * Revamped weapon system and got sounds for spray and fusion weapons + * working. Still need to implements getting continuous & cutoff sounds + * from player ship. + * + * 26 2/11/98 4:54p Jeff + * Moved the inventory into the Player struct + * + * 25 2/06/98 6:07p Jason + * made multiplayer objects get deleted in single-player mode + * + * 24 2/05/98 3:03p Matt + * Changed comment + * + * 23 1/28/98 1:12p Jason + * took shields field out of Player struct + * + * 22 1/28/98 12:00p Jason + * more changes for multiplayer + * + * 21 1/23/98 6:25p Jason + * Got spray weapons working + * + * 20 1/23/98 11:21a Jason + * incremental multiplayer checkin + * + * 19 1/21/98 6:09p Jason + * Got player deaths working in multiplayer + * + * 18 1/21/98 3:08p Samir + * Added slot to StartPlayerExplosion. + * + * 17 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 16 1/20/98 6:35p Samir + * Death sequence more multiplayer friendly? + * + * 15 1/20/98 6:01p Jason + * first pass at getting multiplayer deaths working + * + * 14 1/07/98 6:39p Jason + * Fixed player object number stuff + * + * 13 12/18/97 3:49p Jason + * a new first pass at networking! + * + * 12 11/14/97 2:59p Mark + * FROM JASON:changed the way paging weapons and player weapons work + * + * 11 11/04/97 6:18p Chris + * Added support so that the player ship uses the robot's firing dialog. + * + * 10 10/22/97 7:26p Samir + * Player death mostly working. Damn hangup still occurs. Freaky. + * + * 9 10/21/97 3:16p Samir + * Death with explosions (not customized yet.) + * + * 8 10/08/97 6:36p Samir + * Except for player explosions, player death cycle implemented. May + * change due to asthetics. + * + * 7 10/03/97 11:59a Samir + * Take care of setting player weapon usage stats. + * + * 6 10/01/97 4:51p Samir + * Moved ApplyDamagePlayer to damage.cpp + * + * 5 9/17/97 11:35a Samir + * BIG SEGMENT RIPOUT + * + * 4 9/12/97 4:16p Samir + * Added cloaking and invulnerability. + * + * 3 9/05/97 2:34p Samir + * Simple player death should work. + * + * 2 9/05/97 12:26p Samir + * Player takes damage, and dies. + * + * 7 5/06/97 12:53p Matt + * Save over terrain flag with start position in player struct + * + * 6 4/23/97 6:06p Jason + * made player ship and weapons work correctly together (first pass) + * + * 5 4/17/97 2:55p Jason + * added many D2 like fields to the player structure + * + * 4 4/02/97 4:00p Jason + * added ship_index field to player struct + * + * 3 4/01/97 10:49p Matt + * Deleted obsolete player_ship stuff, and added player stuff + * + * $NoKeywords: $ + */ + +#ifndef _PLAYER_H +#define _PLAYER_H + +#include "pstypes.h" +#include "Inventory.h" + +#include "robotfirestruct.h" +#include "object_external_struct.h" +#include "player_external_struct.h" +#include "player_external.h" + +#define MAX_WAYPOINTS 25 + +#define INITIAL_LIVES 3 //start off with 3 lives + +//For energy to shields conversion +#define CONVERTER_RATE 20.0f //10 units per second xfer rate +#define CONVERTER_SCALE 2.0f //2 units energy -> 1 unit shields +#define CONVERTER_SOUND_DELAY 0.5f //play every half second + +// How long afterburner lasts before it has to be recharged +#ifdef E3_DEMO +#define AFTERBURN_TIME 5000.0 +#else +#define AFTERBURN_TIME 5.0 +#endif + +// Player start position flags +#define PSPF_RED 1 +#define PSPF_BLUE 2 +#define PSPF_GREEN 4 +#define PSPF_YELLOW 8 + + +typedef struct +{ + int room; + float expire_time; + int ignored_pos; + bool active; +} player_pos_suppress; + + +#define PLAYER_POS_HACK_TIME 10 + +extern player_pos_suppress Player_pos_fix[MAX_PLAYERS]; + +typedef struct +{ + char name[CALLSIGN_LEN+1]; + int score; +} team; + +extern int Player_num; +extern int Default_ship_permission; + +//the object which is the person playing the game +extern object *Player_object; + +extern int Num_teams,Team_game; +extern player Players[]; +extern team Teams[]; +extern float HudNameTan; +extern int Current_waypoint; + +extern bool Player_has_camera; +extern int Player_camera_objnum; +extern uint Players_typing; //information about which players are typing messages (to display an icon) + +// How long a player must be dead before he can respawn +#define DEATH_RESPAWN_TIME 3.0f +extern float Total_time_dead; + +//Stuff for the new score info on the HUD +extern int Score_added; //the recently-added amount +extern float Score_added_timer; //how long the added value will be displayed +#define SCORE_ADDED_TIME 2.0 //how long new score info stays on the screen + +// Sets up players array +void InitPlayers(); + +//Look for player objects & set player starts +extern void FindPlayerStarts(); + +// Resets all the properties a player ship to the default values +// Pass in what kind of reset the inventory should do INVRESET_ +void InitPlayerNewShip (int slot,int inven_reset); + +// makes the player invulnerable +void MakePlayerInvulnerable(int slot,float time,bool play_sound_and_message=false); + +// makes the player invulnerable +void MakePlayerVulnerable(int slot); + +// Performs the player death sequence. +void InitiatePlayerDeath(object *playerobj,bool melee=false,int fate=-1); + +// Forces end to player death +void EndPlayerDeath(int slot); + +// Detaches a subobject from the player ship +void DeadPlayerShipHit(object *obj, vector *hitpt, float magnitude); + +// Do actions for the player each frame +void DoPlayerFrame(); + +// Force player to explode (this should be called to intercept the death sequencer's control of explosions +void StartPlayerExplosion(int slot); + +// Resets the player object in a mine to stop moving +void ResetPlayerObject(int slot, bool f_reset_pos = true); + +// Makes the player into an AI controlled physics object +void PlayerSetControlToAI(int slot,float velocity=75.0f); + +// Resets a player's control type back to it's default setting +void ResetPlayerControlType (int slot); + +void InitPlayerNewGame (int slot); + +// Called from a single player game to get rid of all multiplayer ships +void DeleteMultiplayerObjects (); + +// Sets the maximum number of teams in a game +void SetMaxTeams (int num); + +// Returns the goal room of the passed in team +int GetGoalRoomForTeam (int teamnum); + +// Returns the goal room of the passed in player +int GetGoalRoomForPlayer (int slot); + +// Moves a player to a specified start position +void PlayerMoveToStartPos (int slot,int start_slot); + +// Returns a random player starting position +int PlayerGetRandomStartPosition (int slot); + +// Increases the team score by an amount, returning the new total +int IncTeamScore (int,int); + +// Resets the scores for all the teams +void ResetTeamScores (); + +// Sets the lighting that a player will cast +void PlayerSetLighting (int slot,float dist,float r,float g,float b); + +// Sets a wacky rotating ball around the player ship +void PlayerSetRotatingBall (int slot,int num,float speed,float *r,float *g,float *b); + +// Gets the position of a given ball in world coords +void PlayerGetBallPosition (vector *dest,int slot,int num); + +// Spews the inventory of the passed in player object +void PlayerSpewInventory (object *obj,bool spew_energy_and_shield=true,bool spew_nonspewable=false); + +// Changes the ship a particular player is flying +void PlayerChangeShip (int slot,int ship_index); + +// Called when a player is entering a new level +void InitPlayerNewLevel (int slot); + +// Sets the FOV range at which the hud names will come on +void PlayerSetHUDNameFOV (int fov); + +// Switches a player object to observer mode +void PlayerSwitchToObserver (int slot,int observer_mode,int piggy_objnum=0); + +// Stops a player from observing +void PlayerStopObserving (int slot); + +// Sets the players custom texture. If filename=NULL then sets to no texture +// Returns 1 if filename was successfully loaded, else 0 +int PlayerSetCustomTexture (int slot,char *filename); + +// Chooses the style of death a player is going to use +int PlayerChooseDeathFate (int slot,float damage,bool melee); + +// Sets/Clears a permission for a ship on a given player +// if pnum is -1 then all players will be set, else player is the player number +// returns true on success +bool PlayerSetShipPermission(int pnum,char *ship_name,bool allowed); + +// Resets the ship permissions for a given player +// pass false for set_default if you don't want the default ship allowed +bool PlayerResetShipPermissions(int pnum,bool set_default); + +// Returns true if the given ship is allowed to be chosen for a pnum +bool PlayerIsShipAllowed(int pnum,char *ship_name); +bool PlayerIsShipAllowed(int pnum,int ship_index); + +// Performs the energy->shields tranfer (for the powerup) given the playernum, call this while player +// is holding down the e->s key. +void DoEnergyToShields(int pnum); + +// Stop sounds for this player +void PlayerStopSounds (int slot); + +// Sets the start position that the player will respawn at +void PlayerAddWaypoint (int index); + +//Resets the waypoints (for new level) +void ResetWaypoint(); + +//Find all the waypoint objects and add them to the list of waypoints +void MakeAtuoWaypointList(); + +//Sets the auto-waypoint in the object's room to be current +void SetAutoWaypoint(object *objp); + +//Returns the team (0 to 3) of the given player +inline int PlayerGetTeam(int pnum) +{ + if(Players[pnum].team==-1){ + //special "no-team" for Dedicated server + return 0; //fake a red team + } + + return Players[pnum].team; +} + +//Add the player's score +void PlayerScoreAdd(int playernum,int points); + +// steals an item from the given player +void ThiefStealItem(int player_object_handle,int item); +// returns a stolen item to a player +void ThiefReturnItem(int player_object_handle,int item); +// returns true if a player has the specified item to be stolen +bool ThiefPlayerHasItem(int player_object_handle,int item); +#endif diff --git a/Descent3/player_external.h b/Descent3/player_external.h new file mode 100644 index 000000000..caeed74fa --- /dev/null +++ b/Descent3/player_external.h @@ -0,0 +1,110 @@ +/* +* $Logfile: /DescentIII/main/player_external.h $ +* $Revision: 7 $ +* $Date: 5/23/99 3:06a $ +* $Author: Jason $ +* +* Includes the flags, defines, etc. for player struct and functions (for DLL export) +* +* $Log: /DescentIII/main/player_external.h $ + * + * 7 5/23/99 3:06a Jason + * fixed bug with player rankings not being updated correctly + * + * 6 5/10/99 12:23a Chris + * Fixed another hearing/seeing case. :) Buddy bot now is in the player + * ship at respawn + * + * 5 4/24/99 6:45p Jeff + * added functions for theif so he can steal things other than weapons + * + * 4 2/25/99 8:55p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 3 2/12/99 3:39p Jason + * added client side interpolation + * + * 2 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h +* +* $NoKeywords: $ +*/ + + +#ifndef __PLAYER_EXTERNAL_H_ +#define __PLAYER_EXTERNAL_H_ + +#define N_PLAYER_GUNS 8 + +// Initial player stat values +#define INITIAL_ENERGY 100 //100% energy to start +#define INITIAL_SHIELDS 100 //100% shields to start + +#define MAX_ENERGY 200 //go up to 200 +#define MAX_SHIELDS 200 + +// Observer modes +#define OBSERVER_MODE_ROAM 0 +#define OBSERVER_MODE_PIGGYBACK 1 + +// Values for special flags +#define PLAYER_FLAGS_INVULNERABLE 1 // Player is invincible +#define PLAYER_FLAGS_DYING 4 // Is this player in the middle of dying? +#define PLAYER_FLAGS_DEAD 8 // The player is just sitting there dead. +#define PLAYER_FLAGS_UNUSED 16 // ???? +#define PLAYER_FLAGS_UNUSED1 32 // ???? +#define PLAYER_FLAGS_UNUSED2 64 // ???? +#define PLAYER_FLAGS_UNUSED3 128 // ???? +#define PLAYER_FLAGS_UNUSED4 256 // ???? +#define PLAYER_FLAGS_UNUSED5 512 // ???? +#define PLAYER_FLAGS_UNUSED6 1024 // ???? +#define PLAYER_FLAGS_AFTERBURNER 4096 // Player has an afterburner +#define PLAYER_FLAGS_HEADLIGHT 8192 // Player has headlight boost +#define PLAYER_FLAGS_HEADLIGHT_STOLEN 16384 // is the headlight stolen? +#define PLAYER_FLAGS_AFTERBURN_ON 32768 // Player afterburner is engaged +#define PLAYER_FLAGS_CUSTOM_TEXTURE 65536 // Player has a custom texture +#define PLAYER_FLAGS_THRUSTED (1<<17) // Player has thrusted this frame +#define PLAYER_FLAGS_BULLSEYE (1<<18) // Bullseye reticle should light up +#define PLAYER_FLAGS_ZOOMED (1<<19) // Bullseye reticle should light up +#define PLAYER_FLAGS_REARVIEW (1<<20) // Play is using rearview +#define PLAYER_FLAGS_SEND_MOVEMENT (1<<21) // We need to tell the server about our movement +#define PLAYER_FLAGS_PLAYSOUNDMSGFORINVULN (1<<22) // A sound and hud message should be displayed when invulnerability wears off + +//Variables for weapon_fire_flags +#define WFF_SPRAY 1 //a spray weapon is firing +#define WFF_ON_OFF 2 //an on/off weapon is firing +#define WFF_FIRED 128 // fired this frame + +//Define the two player weapons +#define PW_PRIMARY 0 +#define PW_SECONDARY 1 + +//These are used to mask out various controls, put in initially for the training system +#define PCBF_FORWARD 1 +#define PCBF_REVERSE 2 +#define PCBF_LEFT 4 +#define PCBF_RIGHT 8 +#define PCBF_UP 16 +#define PCBF_DOWN 32 +#define PCBF_PITCHUP 64 +#define PCBF_PITCHDOWN 128 +#define PCBF_HEADINGLEFT 256 +#define PCBF_HEADINGRIGHT 512 +#define PCBF_BANKLEFT 1024 +#define PCBF_BANKRIGHT 2048 +#define PCBF_PRIMARY 4096 +#define PCBF_SECONDARY 8192 +#define PCBF_AFTERBURNER 16384 + +#endif \ No newline at end of file diff --git a/Descent3/player_external_struct.h b/Descent3/player_external_struct.h new file mode 100644 index 000000000..1e79258e3 --- /dev/null +++ b/Descent3/player_external_struct.h @@ -0,0 +1,230 @@ +/* +* $Logfile: /DescentIII/main/player_external_struct.h $ +* $Revision: 13 $ +* $Date: 5/20/99 2:48a $ +* $Author: Matt $ +* +* Contains the structs needed for player definition (for DLL export) +* +* $Log: /DescentIII/main/player_external_struct.h $ + * + * 13 5/20/99 2:48a Matt + * Auto-waypoints now stored & used per player. Manual waypoints will all + * players, once Jason makes it work. + * + * 12 5/10/99 12:23a Chris + * Fixed another hearing/seeing case. :) Buddy bot now is in the player + * ship at respawn + * + * 11 5/03/99 2:36p Jason + * added start index + * + * 10 4/23/99 1:56a Matt + * Added system for counting kills of friendly objects + * + * 9 4/06/99 6:02p Matt + * Added score system + * + * 8 4/05/99 3:34p Matt + * Cleaned up player start flags + * + * 7 3/23/99 12:33p Kevin + * More post level results improvements + * + * 6 2/16/99 3:48p Jason + * added marker updates to multiplayer server stream + * + * 5 2/06/99 10:03p Matt + * Added keys system + * + * 4 2/02/99 7:06p Jason + * added ranking system + * + * 3 1/28/99 6:17p Jason + * added markers + * + * 2 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h +* +* $NoKeywords: $ +*/ + + +#ifndef _PLAYER_EXTERNAL_STRUCT_H_ +#define _PLAYER_EXTERNAL_STRUCT_H_ + +#include "pstypes.h" +#include "vecmat_external.h" +#include "object_external_struct.h" +#include "multi_external.h" + +#define MAX_PLAYERS MAX_NET_PLAYERS +#define MAX_TEAMS 4 + +class Inventory; + +#define CALLSIGN_LEN 19 +// THIS CONSTANT MUST NEVER BE OVER 32!!! +#define MAX_PLAYER_WEAPONS MAX_WBS_PER_OBJ + +#define TRACKER_ID_LEN 10 //if this is changed, make sure it's changed in pilottrack.h + + +//Info on player weapon firing. +//There is one of these each for the primary & secondary weapons +typedef struct { + int index; //the index of the current primary or secondary weapon + float firing_time; //how long the current weapon has been firing + int sound_handle; //the handle for the sound the firing weapon is making +} player_weapon; + +//The structure for a player. Most of this data will be for multiplayer +typedef struct { + + // positional data for player starts + int start_index; + vector start_pos; //where the player starts + int start_roomnum; + matrix start_orient; + + int startpos_flags; // these flags apply to the start position (used for teams) + + int ship_index; // the index into the Ships array that this player is flying + + // Who am I data + char callsign[CALLSIGN_LEN+1]; // The callsign of this player, for net purposes. + + // Game data + uint flags; // Powerup flags, see above... + int score; // The player's current score + float damage_magnitude; // for shield effects + float edrain_magnitude; // for energy drain effects + float invul_magnitude; // for invulnerability effects + float energy; // Amount of energy remaining. + ubyte lives; // Lives remaining, 0 = game over. + sbyte level; // Current level player is playing. (must be signed for secret levels) + sbyte starting_level; // What level the player started on. + ubyte keys; // Which keys the player has + short killer_objnum; // Who killed me.... (-1 if no one) + float invulnerable_time; // Time left invulnerable + float last_hit_wall_sound_time; // Last time we played a hit wall sound + float last_homing_warning_sound_time; // Obvious :) + float last_thrust_time; // Last time the player thrusted + float last_afterburner_time; // Last time the player used the afterburner + short objnum; // The object number of this player + sbyte team; // The team number this guy is on + + //The current auto-waypoint for this player + int current_auto_waypoint_room; //the most recent auto-waypoint, or -1 + + // Statistics... + // int score; // Current score. + float time_level; // Level time played + float time_total; // Game time played (high word = seconds) + + int num_hits_level; // used for accuracy calculation. + int num_discharges_level; + + short num_kills_level; // Number of kills this level + short friendly_kills_level; // Number of friendly kills this level + short num_kills_total; // Number of kills total + + // Player weapon info + uint weapon_flags; // Mask of currently owned weapons + ushort weapon_ammo[MAX_PLAYER_WEAPONS]; // Ammo for each weapon + + //Weapons + player_weapon weapon[2]; // Info on the player weapons + ubyte laser_level; // Current level of the laser. + + // lighting + float light_dist; + float r,g,b; + + float ballspeed; + ubyte num_balls; + float ball_r[3],ball_g[3],ball_b[3]; + + // Room tracking + int oldroom; + + // Inventory + Inventory inventory; + + // CounterMeasures Inventory + Inventory counter_measures; + + // Last time the player fired a weapon + float last_fire_weapon_time; + + // Afterburner stuff + float afterburner_mag; // How big the thrust is for the afterburner + float thrust_mag; + int afterburner_sound_handle; + float afterburn_time_left; + + int thruster_sound_handle; + int thruster_sound_state; + + // For small views and external cameras + int small_left_obj,small_right_obj,small_dll_obj; + + + // Multiplayer stuff + ubyte multiplayer_flags; + ubyte last_multiplayer_flags; + float last_guided_time; + + char tracker_id[TRACKER_ID_LEN]; + int kills; + int deaths; + int suicides; + float rank; + float lateral_thrust; //total lateral movement over the whole game + float rotational_thrust; //total rotational movement over the whole game + unsigned int time_in_game; //seconds in game + object *guided_obj,*user_timeout_obj; + + float zoom_distance; + + + // Scalar values + float movement_scalar; + float damage_scalar; + float armor_scalar; + float turn_scalar; + float weapon_recharge_scalar; + float weapon_speed_scalar; + + // Observer stuff + int piggy_objnum; + int piggy_sig; + + // Custom texture stuff + int custom_texture_handle; + + // Ship permissions (1 bit per ship) + int ship_permissions; + + // For invul hit effect + vector invul_vector; + + //Used to disable various controller input from scripting + unsigned int controller_bitflags; + + // Marker stuff + short num_markers; + + short num_deaths_level; // Number of kills this level + short num_deaths_total; // Number of kills total + + +} player; + + +#endif \ No newline at end of file diff --git a/Descent3/postrender.cpp b/Descent3/postrender.cpp new file mode 100644 index 000000000..9bc133873 --- /dev/null +++ b/Descent3/postrender.cpp @@ -0,0 +1,268 @@ +/* + * $Logfile: /DescentIII/main/postrender.cpp $ + * $Revision: 15 $ + * $Date: 4/19/00 5:16p $ + * $Author: Matt $ + * + * program state info + * + * $Log: /DescentIII/main/postrender.cpp $ + * + * 15 4/19/00 5:16p Matt + * From Duane for 1.4 + * Removed Mac OpenGL hack + * + * 14 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 13 6/08/99 12:12p Jeff + * fixed warning messages when compiling new editor + * + * 12 5/12/99 4:55p Gwar + * added an include for the NEWEDITOR + * + * 11 3/22/99 5:51p Jason + * enhancements to mirrors + * + * 10 2/17/99 1:59p Jason + * fixed some postrender issues + * + * 9 2/16/99 5:25p Matt + * zero out Num_postrenders after rendering + * + * 8 2/10/99 1:34p Dan + * fixed another assert + * + * 7 2/10/99 11:41a Dan + * fixed assertion + * + * 6 2/09/99 1:12p Jason + * fixed sorting problem with postrender + * + * 5 2/09/99 12:59p Jason + * spedup indoor renderer + * + * 4 2/09/99 12:09p Jason + * rewriting indoor engine... + * + * 3 2/09/99 9:59a Chris + * Quick compile patch + * + * 2 2/08/99 6:39p Jason + * first pass at new indoor engine + * + * $NoKeywords: $ + */ + +#ifdef NEWEDITOR //include first to get rid of ugly warning message about macro redfinitions +#include "..\neweditor\globals.h" +#endif + +#include +#include "object.h" +#include "viseffect.h" +#include "render.h" +#include "renderobject.h" +#include "room.h" +#include "postrender.h" +#include "config.h" +#include "terrain.h" +#include "renderer.h" + + +postrender_struct Postrender_list[MAX_POSTRENDERS]; +int Num_postrenders=0; + +static vector Viewer_eye; +static matrix Viewer_orient; +static int Viewer_roomnum; + + +// Resets out postrender list for a new frame +void ResetPostrenderList () +{ + Num_postrenders=0; +} + +//Compare function for room face sort +static int Postrender_sort_func(const postrender_struct *a, const postrender_struct *b) +{ + if (a->z < b->z) + return -1; + else if (a->z > b->z) + return 1; + else + return 0; +} + + +#define STATE_PUSH(val) {state_stack[state_stack_counter]=val; state_stack_counter++; ASSERT (state_stack_counter<2000);} +#define STATE_POP() {state_stack_counter--; pop_val=state_stack[state_stack_counter];} +// Sorts our texture states using the quicksort algorithm +void SortPostrenders () +{ + postrender_struct v,t; + int pop_val; + int i,j; + int l,r; + l=0; + r=Num_postrenders-1; + + ushort state_stack_counter=0; + ushort state_stack[MAX_POSTRENDERS]; + + + while (1) + { + while (r>l) + { + i=l-1; + j=r; + v=Postrender_list[r]; + while (1) + { + while (Postrender_list[++i].z < v.z) + ; + + while (Postrender_list[--j].z > v.z) + ; + + if (i>=j) + break; + + t=Postrender_list[i]; + Postrender_list[i]=Postrender_list[j]; + Postrender_list[j]=t; + } + + t=Postrender_list[i]; + Postrender_list[i]=Postrender_list[r]; + Postrender_list[r]=t; + + if (i-l > r-i) + { + STATE_PUSH (l); + STATE_PUSH (i-1); + l=i+1; + } + else + { + STATE_PUSH (i+1); + STATE_PUSH (r); + r=i-1; + } + } + + if (!state_stack_counter) + break; + STATE_POP (); + r=pop_val; + STATE_POP (); + l=pop_val; + } +} + + +void SetupPostrenderRoom (room *rp) +{ + // Setup faces if this is a fogged room + if (rp->flags & RF_FOG) + SetupRoomFog (rp,&Viewer_eye,&Viewer_orient,Viewer_roomnum); + +} + + +// Rotates a face, and then renders it +void DrawPostrenderFace (int roomnum,int facenum,bool change_z) +{ + int i; + + // Always draw as non state limited + bool save_state=StateLimited; + StateLimited=false; + + ASSERT (roomnum>=0 && roomnum<(MAX_ROOMS+MAX_PALETTE_ROOMS)); + ASSERT (Rooms[roomnum].used); + + room *rp=&Rooms[roomnum]; + + ASSERT (facenum>=0 && facenumnum_faces); + + face *fp=&rp->faces[facenum]; + + // Rotate points + rp->wpb_index=0; + for (i=0;inum_verts;i++) + { + g3_RotatePoint( &World_point_buffer[fp->face_verts[i]],&rp->verts[fp->face_verts[i]] ); + g3_ProjectPoint( &World_point_buffer[fp->face_verts[i]] ); + } + + SetupPostrenderRoom (rp); + + // Render! + if (change_z) + rend_SetZBufferWriteMask (0); + RenderFace (rp,facenum); + + // Render any effects for this face + if (Num_specular_faces_to_render>0) + { + RenderSpecularFacesFlat(rp); + Num_specular_faces_to_render=0; + } + + if (Num_fog_faces_to_render>0) + { + RenderFogFaces(rp); + Num_fog_faces_to_render=0; + } + + // Restore statelimited setting + StateLimited=save_state; + + if (change_z) + rend_SetZBufferWriteMask (1); +} + + +// Renders all the objects/viseffects/walls we have in our postrender list +void PostRender(int roomnum) +{ + g3_GetViewPosition(&Viewer_eye); + g3_GetUnscaledMatrix (&Viewer_orient); + + Viewer_roomnum=roomnum; + + int i,index; + // Sort the objects + SortPostrenders(); + //qsort(Postrender_list,Num_postrenders,sizeof(*Postrender_list),(int (cdecl *)(const void*,const void*))Postrender_sort_func); + + for (i=Num_postrenders-1;i>=0;i--) + { + + if(Postrender_list[i].type==PRT_VISEFFECT) + { + index = Postrender_list[i].visnum; + DrawVisEffect (&VisEffects[index]); + } + else if (Postrender_list[i].type==PRT_OBJECT) + { + object *objp = &Objects[Postrender_list[i].objnum]; + + if (!OBJECT_OUTSIDE (&Objects[Postrender_list[i].objnum])) + SetupPostrenderRoom(&Rooms[Objects[Postrender_list[i].objnum].roomnum]); + RenderObject(objp); + } + else + { + // Do room face + DrawPostrenderFace (Postrender_list[i].roomnum,Postrender_list[i].facenum); + } + } + Num_postrenders=0; + rend_SetFogState (0); + RenderLightGlows (); + +} diff --git a/Descent3/postrender.h b/Descent3/postrender.h new file mode 100644 index 000000000..154117aab --- /dev/null +++ b/Descent3/postrender.h @@ -0,0 +1,36 @@ +#ifndef POSTRENDER_H +#define POSTRENDER_H + +#define MAX_POSTRENDERS 3000 + +#define PRT_OBJECT 0 +#define PRT_VISEFFECT 1 +#define PRT_WALL 2 + + +typedef struct +{ + ubyte type; // See types above + union + { + short objnum; + short visnum; + short facenum; + }; + + short roomnum; + float z; +} postrender_struct; + +extern int Num_postrenders; +extern postrender_struct Postrender_list[]; + +void ResetPostrenderList (); + +// Renders all the objects/viseffects/walls we have in our postrender list +void PostRender(int); + +void DrawPostrenderFace (int roomnum,int facenum,bool change_z=true); + + +#endif \ No newline at end of file diff --git a/Descent3/powerup.h b/Descent3/powerup.h new file mode 100644 index 000000000..17011bf72 --- /dev/null +++ b/Descent3/powerup.h @@ -0,0 +1,143 @@ +#ifndef POWERUP_H +#define POWERUP_H + +#include "pstypes.h" +#include "manage.h" +#include "object.h" + +#define MAX_POWERUPS 100 +#define MAX_STATIC_POWERUPS 50 + +#define PF_IMAGE_BITMAP 1 + +// These defines must correspond to the Static_powerup_names array +#define POW_SHIELD 0 +#define POW_ENERGY 1 + +#define POW_LASER 2 +#define POW_VULCAN_WEAPON 3 +#define POW_SPREADFIRE_WEAPON 4 +#define POW_PLASMA_WEAPON 5 +#define POW_FUSION_WEAPON 6 + +#define POW_SUPER_LASER 7 +#define POW_GAUSS_WEAPON 8 +#define POW_HELIX_WEAPON 9 +#define POW_PHOENIX_WEAPON 10 +#define POW_OMEGA_WEAPON 11 + +#define POW_MISSILE_1 12 +#define POW_MISSILE_4 13 //4-pack MUST follow single missile +#define POW_HOMING_MISSILE_1 14 +#define POW_HOMING_MISSILE_4 15 //4-pack MUST follow single missile +#define POW_PROXIMITY_WEAPON 16 +#define POW_SMART_MISSILE_WEAPON 17 +#define POW_MEGA_WEAPON 18 + +#define POW_FLASH_MISSILE_1 19 +#define POW_FLASH_MISSILE_4 20 //4-pack MUST follow single missile +#define POW_GUIDED_MISSILE_1 21 +#define POW_GUIDED_MISSILE_4 22 //4-pack MUST follow single missile +#define POW_SMART_MINE 23 +#define POW_MERCURY_MISSILE_1 24 +#define POW_MERCURY_MISSILE_4 25 //4-pack MUST follow single missile +#define POW_EARTHSHAKER_MISSILE 26 + +#define POW_EXTRA_LIFE 27 +#define POW_QUAD_FIRE 28 +#define POW_VULCAN_AMMO 29 +#define POW_CLOAK 30 +#define POW_TURBO 31 +#define POW_INVULNERABILITY 32 +#define POW_FULL_MAP 33 +#define POW_CONVERTER 34 +#define POW_AMMO_RACK 35 +#define POW_AFTERBURNER 36 +#define POW_HEADLIGHT 37 + +#define POW_FLAG_BLUE 38 +#define POW_FLAG_RED 39 +#define POW_HOARD_ORB 40 + +// sound stuff + +#define MAX_POWERUP_SOUNDS 7 + +#define PSI_PICKUP 0 + + +typedef struct +{ + char name[PAGENAME_LEN]; + float size; + int score; + int image_handle; // Either a vclip or a polygon model + char model_name[PAGENAME_LEN]; // used for remapping powerups which contain models + int flags; + unsigned short used; + + short sounds[MAX_POWERUP_SOUNDS]; + + // Default physics information for this powerup type + physics_info phys_info; //the physics data for this obj type. + +} powerup; + + +extern int Num_powerups; +extern powerup Powerups[MAX_POWERUPS]; +extern char *Static_powerup_names[]; + +// Sets all powerups to unused +void InitPowerups (); + +// Allocs a powerup for use, returns -1 if error, else index on success +int AllocPowerup (); + +// Frees powerup index n +void FreePowerup (int n); + +// Gets next powerup from n that has actually been alloced +int GetNextPowerup (int n); + +// Gets previous powerup from n that has actually been alloced +int GetPrevPowerup (int n); + +// Searches thru all powerups for a specific name, returns -1 if not found +// or index of powerup with name +int FindPowerupName (char *name); + +// Given a filename, loads either the model or vclip found in that file. If type +// is not NULL, sets it to 1 if file is model, otherwise sets it to zero +int LoadPowerupImage (char *filename,int *type); + +// Given a powerup handle, returns that powerups image for framenum +int GetPowerupImage (int handle,int framenum); + +// Given an object, renders the representation of this powerup +// Currently only handles bitmap types, not poly models +void DrawPowerupObject (object *obj); + +// Given a powerup name, assigns that powerup to a specific index into +// the Powerups array. Returns -1 if the named powerup is not found, 0 if the powerup +// is already in its place, or 1 if successfully moved +int MatchPowerupToIndex (char *name,int dest_index); + +// Moves a powerup from a given index into a new one (above MAX_STATIC_POWERUPS) +// returns new index +int MovePowerupFromIndex (int index); + +// This is a very confusing function. It takes all the powerups that we have loaded +// and remaps then into their proper places (if they are static). +void RemapPowerups (); + +// goes thru every entity that could possible have a powerup index (ie objects, robots, etc) +// and changes the old index to the new index +void RemapAllPowerupObjects (int old_index,int new_index); + +// Player activated this powerup +int DoPowerup(object *obj); + +#endif + + diff --git a/Descent3/procedurals.cpp b/Descent3/procedurals.cpp new file mode 100644 index 000000000..a2d2bd9fd --- /dev/null +++ b/Descent3/procedurals.cpp @@ -0,0 +1,1514 @@ +/* +* $Logfile: /DescentIII/main/procedurals.cpp $ +* $Revision: 34 $ +* $Date: 7/28/99 3:46p $ +* $Author: Kevin $ +* +* Procedurals +* +* $Log: /DescentIII/main/procedurals.cpp $ + * + * 34 7/28/99 3:46p Kevin + * Mac! + * + * 33 5/10/99 10:23p Ardussi + * changes to compile on Mac + * + * 32 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 31 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand +* +* $NoKeywords: $ +*/ +#ifdef MACINTOSH +#include +#include "Macros.h" +#endif +#include "procedurals.h" +#include "bitmap.h" +#include "gr.h" +#include "gametexture.h" +#include "vclip.h" +#include "game.h" +#include "texture.h" +#include "mem.h" +#include "ddio.h" +#include "config.h" +#include +#include +#include +#include "psrand.h" +#define BRIGHT_COLOR 254 +#define PROC_SIZE 128 +#define TABSIZE 256 +#define TABMASK (TABSIZE-1) +#define PERM(x) perm[(x)&TABMASK] +#define INDEX(ix,iy) PERM((ix)+PERM((iy))) +#define RANDNBR ((prand())/(float) RAND_MAX) +#define LERP(t,x0,x1) ((x0)+(t)*((x1)-(x0))) +#define SMOOTHSTEP(x) ((x)) +unsigned char perm[TABSIZE]; +static float Noise_table[TABSIZE*3]; +dynamic_proc_element DynamicProcElements[MAX_PROC_ELEMENTS]; +ushort DefaultProcPalette[256]; +int Num_proc_elements=0; +static short Proc_free_list[MAX_PROC_ELEMENTS]; +ushort ProcFadeTable[32768]; +#define NUM_WATER_SHADES 64 +ushort WaterProcTableHi[NUM_WATER_SHADES][256]; +ubyte WaterProcTableLo[NUM_WATER_SHADES][256]; +char *ProcNames[]={"None","Line Lightning","Sphere lightning","Straight","Rising Embers","Random Embers","Spinners","Roamers","Fountain","Cone","Fall Right","Fall Left","END"}; +char *WaterProcNames[]={"None","Height blob","Sine Blob","Random Raindrops","Random Blobdrops","END"}; +static ubyte *ProcDestData; +int pholdrand=1; +inline int prand () +{ + return(((pholdrand = pholdrand * 214013L + 2531011L) >> 16) & 0x7fff); +} +// Given an array of r,g,b values, generates a 16bit palette table for those colors +void GeneratePaletteForProcedural (ubyte *r,ubyte *g,ubyte *b,ushort *pal) +{ + int i; + float fr,fg,fb; + for (i=0;i<256;i++) + { + int ir=r[i]; + int ig=g[i]; + int ib=b[i]; + fr=(float)ir/255.0; + fg=(float)ig/255.0; + fb=(float)ib/255.0; + ir=(fr*31.0); + ig=(fg*31.0); + ib=(fb*31.0); + pal[i]=OPAQUE_FLAG|(ir<<10)|(ig<<5)|(ib); + } +} +// Inits the noise table for procedurals +void InitNoise() +{ + float *table = Noise_table; + float z, r, theta; + int i; + for(i = 0; i < TABSIZE; i++) + { + perm[i]=prand()%TABMASK; + z = 1.0f - 2.0f*RANDNBR; + + /* r is radius of x,y circle */ + r = sqrtf(1 - z*z); + /* theta is angle in (x,y) */ + theta = 2.0f * 3.14f * RANDNBR; + *table++ = r * cosf(theta); + *table++ = r * sinf(theta); + *table++ = z; + } +} +// Gets a lattice point for our noise +float GradLattice(int ix, int iy,float fx, float fy) +{ + float *g = &Noise_table[INDEX(ix,iy)*2]; + return g[0]*fx + g[1]*fy; +} +float GradNoise(float x, float y) +{ + int ix, iy; + float fx0, fx1, fy0, fy1; + float wx, wy; + float vx0, vx1, vy0, vy1; + ix = (int)floor(x); + fx0 = x - ix; + fx1 = fx0 - 1; + wx = SMOOTHSTEP(fx0); + iy = (int)floor(y); + fy0 = y - iy; + fy1 = fy0 - 1; + wy = SMOOTHSTEP(fy0); + vx0 = GradLattice(ix,iy,fx0,fy0); + vx1 = GradLattice(ix+1,iy,fx1,fy0); + vy0 = LERP(wx, vx0, vx1); + vx0 = GradLattice(ix,iy+1,fx0,fy1); + vx1 = GradLattice(ix+1,iy+1,fx1,fy1); + vy1 = LERP(wx, vx0, vx1); + + return LERP(wy, vy0, vy1); +} +extern ubyte EasterEgg; +int Easter_egg_handle=-1; +// Goes through our array and clears the slots out +void InitProcedurals() +{ + int t; + int i; + + // Load easter egg bitmap + Easter_egg_handle=bm_AllocLoadFileBitmap("FreakyEye.ogf",0); + if (Easter_egg_handle==-1) + { + mprintf ((0,"Failed to load easter egg!\n")); + } + for (i=0;i>10) & 0x1f; + int g=(i>>5) & 0x1f; + int b=(i) & 0x1f; + r=__max(0,r-1); + g=__max(0,g-1); + b=__max(0,b-1); + ProcFadeTable[i]=OPAQUE_FLAG|(r<<10)|(g<<5)|b; + } + // Init our palette + for (i=0;i<128;i++) + { + float fr=(float)i/127.0; + int ib=fr*31.0; + int ig=fr*16.0; + DefaultProcPalette[i]=OPAQUE_FLAG|(ig<<5)|(ib); + } + // Do lower part of table + for (i=0;i<(NUM_WATER_SHADES);i++) + { + float norm=((float)i/(float)(NUM_WATER_SHADES-1)); + float lo_norm=min((norm/.5)*1.0,1.0); + float hi_norm=max(((norm-.5)/.5)*1.0,0); + + for (int rcount=0;rcount<32;rcount++) + { + for (int gcount=0;gcount<4;gcount++) + { + int r=rcount; + + int index=(rcount*4)+gcount; + + float fr=r; + + r=min(fr*lo_norm+(31.0*hi_norm),31); + + WaterProcTableHi[i][index]=OPAQUE_FLAG|(r<<10); + } + } + for (int bcount=0;bcount<32;bcount++) + { + for (int gcount=0;gcount<8;gcount++) + { + int b=bcount; + int index=gcount*32+bcount; + + float fb=b; + b=min(fb*lo_norm+(31.0*hi_norm),31); + + WaterProcTableLo[i][index]=b; + } + } + + int gcount; + for (gcount=0;gcount<8;gcount++) + { + float fg=gcount; + int g=min((fg*lo_norm)+(7.0*hi_norm),7); + for (t=0;t<32;t++) + { + int index=gcount*32+t; + WaterProcTableLo[i][gcount*32+t]|=(g<<5); + } + } + for (gcount=0;gcount<4;gcount++) + { + float fg=gcount*8; + int g=min((fg*lo_norm)+(24.0*hi_norm),24); + + for (t=0;t<32;t++) + { + int index=t*4+gcount; + + WaterProcTableHi[i][t*4+gcount]|=(g<<5); + + } + } + } + /*for (i=0;i>10) & 0x1f; + int g=((WaterProcTableHi[i][127]>>5) & 0x1f); + g+=((WaterProcTableLo[i][255]>>5) & 0x1f); + int b=(WaterProcTableLo[i][255]) & 0x1f; + mprintf ((0,"index=%d r=%d g=%d b=%d\n",i,r,g,b)); + }*/ + // Init our default palette + for (i=0;i<128;i++) + { + float norm=(float)i/127.0; + int ir=(norm*31.0); + int ig=16+(norm*15.0); + DefaultProcPalette[i+128]=OPAQUE_FLAG|(ir<<10)|(ig<<5)|(0x1f); + } + InitNoise(); +} +// Returns the next free procelement +int ProcElementAllocate () +{ + if (Num_proc_elements==MAX_PROC_ELEMENTS) + { + //mprintf ((0,"Couldn't allocate proc element!\n")); + return -1; + } + + int n=Proc_free_list[Num_proc_elements++]; + ASSERT (DynamicProcElements[n].type==PROC_NONE); // Get Jason + + DynamicProcElements[n].next=-1; + DynamicProcElements[n].prev=-1; + + return n; +} +// Frees up a procelement for use +int ProcElementFree (int index) +{ + ASSERT (index>=0 && index<=MAX_PROC_ELEMENTS); + Proc_free_list[--Num_proc_elements]=index; + DynamicProcElements[index].type=PROC_NONE; + return 1; +} +// Draws a line into the passed in bitmap +void DrawProceduralLine (int x1, int y1, int x2, int y2,ubyte color) +{ + int DY, DX; + int i,x,y,yinc=1,xinc=1,error_term; + + ubyte *surface_ptr=ProcDestData; + + int xmask=PROC_SIZE-1; + int ymask=PROC_SIZE-1; + +// Check to see if our x coords are reversed + if (x1>x2) + { + SWAP (x1, x2); + SWAP (y1, y2); + } + DX=x2-x1; DY=y2-y1; + if (DX<0) + { + xinc=-1; + DX=-DX; + } + if (DY<0) + { + yinc=-1; + DY=-DY; + } + if (DX>=DY) // X is greater than y + { + error_term=0; + x=x1 & xmask; y=y1 & ymask; + surface_ptr += y*PROC_SIZE; + for (i=0;i=DX) + { + y+=yinc; + y&=ymask; + surface_ptr=ProcDestData+(y*PROC_SIZE); + error_term-=DX; + } + } + } + else + { + error_term=0; + x=x1 & xmask; y=y1 & ymask; + surface_ptr += y*PROC_SIZE; + + for (i=0;i=DY) + { + x+=xinc; + x&=xmask; + error_term-=DY; + } + } + } +} +// Fades an entire bitmap one step closer to black +void FadeProcTexture (int tex_handle) +{ + int total=PROC_SIZE*PROC_SIZE; + ubyte *src_data=ProcDestData; + int fadeval; + fadeval=255-GameTextures[tex_handle].procedural->heat; + fadeval>>=3; + fadeval++; + + for (int i=0;iheat; + val=255-heat; + + for (int i=0;i=val) + (*src_data)++; + } +} +// Fades and entire bitmap one step closer to black +void BlendProcTexture (int tex_handle) +{ + int total=PROC_SIZE*PROC_SIZE; + ubyte *src_data=(ubyte *)GameTextures[tex_handle].procedural->proc1; + ubyte *dest_data=(ubyte *)GameTextures[tex_handle].procedural->proc2; + ubyte *start_data=src_data; + for (int i=0;i>=2; + *dest_data=total; + } + } +} +// Draws lightning into a bitmap +void AddProcLightning (int x1,int y1,int x2,int y2,ubyte color,static_proc_element *proc) +{ + float dx=x2-x1; + float dy=y2-y1; + + float mag=sqrt ((dx*dx)+(dy*dy)); + if (mag<1) + return; + int num_segments=mag/8; + dx/=mag; + dy/=mag; + float cur_x=x1; + float cur_y=y1; + float rot_x=dy; + float rot_y=dx; + float from_x=cur_x; + float from_y=cur_y; + for (int i=0;ispeed)*(((prand()%200)-100.0)/18.0)); + to_y+=(rot_y)*((1+proc->speed)*(((prand()%200)-100.0)/18.0)); + } + + DrawProceduralLine (from_x,from_y,to_x,to_y,color); + + from_x=to_x; + from_y=to_y; + } +} +//link the procedural into the list for its texture +void ProcElementLink(int index,int texnum) +{ + dynamic_proc_element *proc = &DynamicProcElements[index]; + proc->next = GameTextures[texnum].procedural->dynamic_proc_elements; + GameTextures[texnum].procedural->dynamic_proc_elements=index; + ASSERT(proc->next != index); + + proc->prev = -1; + if (proc->next != -1) + DynamicProcElements[proc->next].prev = index; +} +// Unlinks a proc_element from a texture +void ProcElementUnlink(int index,int texnum) +{ + dynamic_proc_element *proc = &DynamicProcElements[index]; + + ASSERT(index != -1); + if (proc->prev == -1) + GameTextures[texnum].procedural->dynamic_proc_elements = proc->next; + else + DynamicProcElements[proc->prev].next = proc->next; + if (proc->next != -1) + DynamicProcElements[proc->next].prev = proc->prev; + ProcElementFree (index); +} +// Clears all the procedurals elements attached to this texture +void ClearAllProceduralsFromTexture (int texnum) +{ + int proc_num=GameTextures[texnum].procedural->dynamic_proc_elements; + + while (proc_num!=-1) + { + int temp=DynamicProcElements[proc_num].next; + ProcElementUnlink (proc_num,texnum); + proc_num=temp; + } + GameTextures[texnum].procedural->num_static_elements=0; +} +// Adds one point to a bitmap +void AddProcPoint (int x,int y,ubyte color) +{ + x&=(PROC_SIZE-1); + y&=(PROC_SIZE-1); + + int index=y*PROC_SIZE+x; + ProcDestData[index]=color; + +} +// Adds one point to a bitmap +inline void PlaceProcPoint (int x,int y,ubyte color) +{ + x&=(PROC_SIZE-1); + y&=(PROC_SIZE-1); + ProcDestData[y*PROC_SIZE+x]=color; +} +// Adds a sphere lightning bolt +void AddProcSphereLightning (int handle,static_proc_element *proc) +{ + int procnum=proc-GameTextures[handle].procedural->static_proc_elements; + + if (Detail_settings.Procedurals_enabled) + { + if (proc->frequency!=0 && ((FrameCount) % proc->frequency)!=0) + return; + } + float norm=((float)proc->size/255.0); + int len=norm*(float)PROC_SIZE; + len/=2; + int dir=prand()*2; + int dest_x=proc->x1+(FixCos(dir)*(float)len); + int dest_y=proc->y1+(FixSin(dir)*(float)len); + AddProcLightning (proc->x1,proc->y1,dest_x,dest_y, BRIGHT_COLOR,proc); +} +void AddProcRisingEmber (int handle,static_proc_element *proc) +{ + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + // Add a new fizzle + int num=prand()%7; + for (int i=0;i-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].x1=IntToFix ((int)proc->x1); + DynamicProcElements[index].y1=IntToFix ((int)proc->y1); + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].speed=proc->speed; + DynamicProcElements[index].frames_left=(prand()%10)+15; + } + } + } +} +void DoDynamicRisingEmber (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + int speed_adjust=1+((proc->speed/255.0)*2.0); + proc->dx=FloatToFix (((prand()%3)-1)*speed_adjust); + proc->dy=FloatToFix (((prand()%3)-2)*speed_adjust); + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void AddProcSpinners (int handle,static_proc_element *proc) +{ + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + + int proc_num=(proc-GameTextures[handle].procedural->static_proc_elements)*60; + int speed_adjust=1+((proc->speed/255.0)*5.0); + int cur_angle=(((FrameCount+proc_num)*speed_adjust) % 64)*1024; + + int index=ProcElementAllocate (); + if (index>-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].dx=FloatToFix(FixCos (cur_angle)); + DynamicProcElements[index].dy=FloatToFix(FixSin (cur_angle)); + DynamicProcElements[index].x1=FloatToFix(proc->x1+(DynamicProcElements[index].dx*FloatToFix(proc->size))); + DynamicProcElements[index].y1=FloatToFix(proc->y1+(DynamicProcElements[index].dy*FloatToFix(proc->size))); + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].speed=proc->speed; + DynamicProcElements[index].frames_left=(prand()%10)+15; + } + } +} +void DoDynamicSpinners (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void AddProcRandomEmber (int handle,static_proc_element *proc) +{ + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + // Add a new fizzle + int num=(prand()%4)+1; + for (int i=0;i-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].x1=IntToFix(proc->x1); + DynamicProcElements[index].y1=IntToFix(proc->y1); + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].speed=proc->speed; + DynamicProcElements[index].frames_left=(prand()%10)+15; + } + } + } +} +void DoDynamicRandomEmber (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + int speed_adjust=1+((proc->speed/255.0)*2.0); + proc->dx=FloatToFix(((prand()%3)-1)*speed_adjust); + proc->dy=FloatToFix(((prand()%3)-1)*speed_adjust); + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void AddProcRoamers (int handle,static_proc_element *proc) +{ + proc->x1+=(ps_rand()%5)-2; + proc->y1+=(ps_rand()%5)-2; + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + // Add a new fizzle + int num=(prand()%4)+1; + for (int i=0;i-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].x1=IntToFix(proc->x1); + DynamicProcElements[index].y1=IntToFix(proc->y1); + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].speed=proc->speed; + DynamicProcElements[index].frames_left=(prand()%10)+15; + } + } + } +} +void DoDynamicRoamers (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + int speed_adjust=1+((proc->speed/255.0)*2.0); + proc->dx=FloatToFix(((prand()%3)-1)*speed_adjust); + proc->dy=FloatToFix(((prand()%3)-1)*speed_adjust); + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void AddProcFountain (int handle,static_proc_element *proc) +{ + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + // Add a new fizzle + int num=(prand()%4)+1; + for (int i=0;i-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].x1=IntToFix(proc->x1); + DynamicProcElements[index].y1=IntToFix(proc->y1); + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].speed=proc->speed; + DynamicProcElements[index].dx=FloatToFix(((prand()%100)-50)/200.0); + if ((prand()%10)==0) + { + DynamicProcElements[index].dy=FloatToFix(-((prand()%100)/300.0)); + DynamicProcElements[index].frames_left=(prand()%6)+3; + } + else + { + DynamicProcElements[index].dy=FloatToFix((prand()%100)/50.0); + DynamicProcElements[index].frames_left=(prand()%10)+15; + } + } + } + } +} +void DoDynamicFountain (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void AddProcCone (int handle,static_proc_element *proc) +{ + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + // Add a new fizzle + int num=(prand()%4)+1; + for (int i=0;i-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].x1=IntToFix(proc->x1); + DynamicProcElements[index].y1=IntToFix(proc->y1); + + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].speed=proc->speed; + DynamicProcElements[index].dx=FloatToFix(((prand()%100)-50)/80.0); + if ((prand()%10)==0) + { + DynamicProcElements[index].dy=FloatToFix(-((prand()%100)/300.0)); + DynamicProcElements[index].frames_left=(prand()%6)+3; + } + else + { + DynamicProcElements[index].dy=IntToFix(1); + DynamicProcElements[index].frames_left=(prand()%10)+15; + } + } + } + } +} +void DoDynamicCone (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void AddProcFallRight (int handle,static_proc_element *proc) +{ + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + // Add a new fizzle + int num=(prand()%2)+1; + for (int i=0;i-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].x1=IntToFix(proc->x1+((ps_rand()%5)-2)); + DynamicProcElements[index].y1=IntToFix(proc->y1+((ps_rand()%5)-2)); + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].dx=IntToFix(1); + DynamicProcElements[index].dy=FloatToFix((-(prand()%100))/300.0); + DynamicProcElements[index].frames_left=(prand()%15)+25; + } + } + } +} +void DoDynamicFallRight (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + if (FixToInt(proc->dx)>0) + proc->dx-=FloatToFix(((ps_rand()%100)/2000.0)); + if (FixToInt(proc->dy)<2) + proc->dy+=FloatToFix(((ps_rand()%100)/1000.0)); + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void AddProcFallLeft (int handle,static_proc_element *proc) +{ + if (proc->frequency==0 || (FrameCount % proc->frequency)==0) + { + // Add a new fizzle + int num=(prand()%2)+1; + for (int i=0;i-1) + { + ProcElementLink (index,handle); + DynamicProcElements[index].type=proc->type; + + DynamicProcElements[index].x1=IntToFix(proc->x1+((ps_rand()%5)-2)); + DynamicProcElements[index].y1=IntToFix(proc->y1+((ps_rand()%5)-2)); + DynamicProcElements[index].color=BRIGHT_COLOR; + DynamicProcElements[index].dx=IntToFix(-1); + DynamicProcElements[index].dy=FloatToFix((-(prand()%100))/300.0); + DynamicProcElements[index].frames_left=(prand()%15)+25; + } + } + } +} +void DoDynamicFallLeft (int handle,dynamic_proc_element *proc) +{ + PlaceProcPoint (FixToInt(proc->x1),FixToInt(proc->y1),proc->color); + proc->frames_left--; + if (proc->frames_left<1) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + proc->color--; + if (proc->color==0) + ProcElementUnlink (proc-DynamicProcElements,handle); + else + { + if (FixToInt(proc->dx)<0) + proc->dx+=FloatToFix(((ps_rand()%100)/2000.0)); + if (FixToInt(proc->dy)<2) + proc->dy+=FloatToFix(((ps_rand()%100)/1000.0)); + proc->x1+=proc->dx; + proc->y1+=proc->dy; + } + } +} +void CalcWater2 (int handle,int density) +{ + int newh; + int count = PROC_SIZE + 1; + proc_struct *procedural=GameTextures[handle].procedural; + + short *newptr = (short *)procedural->proc2; + short *oldptr = (short *)procedural->proc1; + + int x, y; + + // Do main block + for (y = 1; y<(PROC_SIZE-1); y++, count += 2) + { + for (x = 1; x>2 ) + - newptr[count]; + newptr[count] = newh - (newh >> density); + } + } + int up,down,left,right; + count=0; + for (y=0;y>=2; + newh-=newptr[count]; + newptr[count] = newh - (newh >> density); + } + } +} +void CalcWater (int handle,int density) +{ + int newh; + int count = PROC_SIZE + 1; + proc_struct *procedural=GameTextures[handle].procedural; + + short *newptr = (short *)procedural->proc2; + short *oldptr = (short *)procedural->proc1; + + int x, y; + + // Do main block + for (y = 1; y<(PROC_SIZE-1); y++, count += 2) + { + for (x = 1; x>1 ) + - newptr[count]; + newptr[count] = newh - (newh >> density); + } + } + int up,down,left,right; + count=0; + for (y=0;y>=1; + newh-=newptr[count]; + newptr[count] = newh - (newh >> density); + } + } +} +void DrawWaterNoLight(int handle) +{ + int dx, dy; + int x, y; + int offset=0; + proc_struct *procedural=GameTextures[handle].procedural; + int dest_bitmap=procedural->procedural_bitmap; + ushort *dest_data=(ushort *)bm_data (dest_bitmap,0); + ushort *src_data=(ushort *)bm_data (GameTextures[handle].bm_handle,0); + short *ptr = (short *)procedural->proc1; + for (y = 0; y < PROC_SIZE; y++) + { + for (x = 0;x >3))%PROC_SIZE; + int xoffset=(x+(dx>>3))%PROC_SIZE; + dest_data[offset] = src_data[yoffset*PROC_SIZE+xoffset]; + + } + } +} +void DrawWaterWithLight(int handle,int lightval) +{ + int dx, dy; + int x, y; + ushort c; + int offset=0; + proc_struct *procedural=GameTextures[handle].procedural; + int dest_bitmap=procedural->procedural_bitmap; + ushort *dest_data=(ushort *)bm_data (dest_bitmap,0); + ushort *src_data=(ushort *)bm_data (GameTextures[handle].bm_handle,0); + short *ptr = (short *)procedural->proc1; + for (y = 0; y < PROC_SIZE; y++) + { + int ychange,ychange2; + if (y==PROC_SIZE-1) + { + ychange=PROC_SIZE; + ychange2=((PROC_SIZE-1)*PROC_SIZE); + } + else if (y==0) + { + ychange=-((PROC_SIZE-1)*PROC_SIZE); + ychange2=-PROC_SIZE; + } + else + { + ychange=PROC_SIZE; + ychange2=-PROC_SIZE; + } + + for (x = 0;x>3)); + int xoffset=(x+(dx>>3)); + yoffset &=(PROC_SIZE-1); + xoffset &=(PROC_SIZE-1); + ASSERT (yoffset>=0); + ASSERT (xoffset>=0); + c = src_data[yoffset*PROC_SIZE+xoffset]; + int l=(NUM_WATER_SHADES/2)-(dx>>lightval); + if (l>(NUM_WATER_SHADES-1)) + l=NUM_WATER_SHADES-1; + if (l<0) + l=0; + c&=~OPAQUE_FLAG; + + c=WaterProcTableHi[l][c>>8]+WaterProcTableLo[l][c & 0xFF]; + + dest_data[offset] = c; + } + } +} +void AddProcHeightBlob(static_proc_element *proc,int handle) +{ + int rquad; + int cx, cy, cyq; + int left, top, right, bottom; + short *dest_data=(short *)GameTextures[handle].procedural->proc1; + int x=proc->x1; + int y=proc->y1; + int procnum=proc-GameTextures[handle].procedural->static_proc_elements; + if (proc->frequency && ((FrameCount+procnum) % proc->frequency)) + return; + int radius=proc->size; + int height=proc->speed; + rquad = radius * radius; + + left=-radius; right = radius; + top=-radius; bottom = radius; + // Perform edge clipping... + if(x - radius < 1) left -= (x-radius-1); + if(y - radius < 1) top -= (y-radius-1); + if(x + radius > PROC_SIZE-1) right -= (x+radius-PROC_SIZE+1); + if(y + radius > PROC_SIZE-1) bottom-= (y+radius-PROC_SIZE+1); + for(cy = top; cy < bottom; cy++) + { + cyq = cy*cy; + for(cx = left; cx < right; cx++) + { + if(cx*cx + cyq < rquad) + dest_data[PROC_SIZE*(cy+y) + (cx+x)] += height; + } + } +} +void AddProcRaindrops (static_proc_element *proc,int handle) +{ + int procnum=proc-GameTextures[handle].procedural->static_proc_elements; + if (proc->frequency && ((FrameCount+procnum) % proc->frequency)) + return; + ubyte proc_frequency=proc->frequency; + ubyte proc_size=proc->size; + ubyte proc_speed=proc->speed; + ubyte x1=proc->x1; + ubyte y1=proc->y1; + proc->frequency=0; + proc->size=(ps_rand()%3)+1; + proc->speed=max (0,proc_speed+((ps_rand()%10)-5)); + proc->x1=x1+(ps_rand()%(proc_size*2))-(proc_size); + proc->y1=y1+(ps_rand()%(proc_size*2))-(proc_size); + AddProcHeightBlob(proc,handle); + proc->x1=x1; + proc->y1=y1; + proc->size=proc_size; + proc->speed=proc_speed; + proc->frequency=proc_frequency; +} +void AddProcBlobdrops (static_proc_element *proc,int handle) +{ + int procnum=proc-GameTextures[handle].procedural->static_proc_elements; + if (proc->frequency && ((FrameCount+procnum) % proc->frequency)) + return; + ubyte proc_frequency=proc->frequency; + ubyte proc_size=proc->size; + ubyte proc_speed=proc->speed; + ubyte x1=proc->x1; + ubyte y1=proc->y1; + proc->frequency=0; + proc->size=(ps_rand()%6)+4; + proc->speed=max (0,proc_speed+((ps_rand()%50)-25)); + proc->x1=x1+(ps_rand()%(proc_size*2))-(proc_size); + proc->y1=y1+(ps_rand()%(proc_size*2))-(proc_size); + AddProcHeightBlob(proc,handle); + proc->x1=x1; + proc->y1=y1; + proc->size=proc_size; + proc->speed=proc_speed; + proc->frequency=proc_frequency; +} +void AddProcSineBlob(static_proc_element *proc, int handle) +{ + int procnum=proc-GameTextures[handle].procedural->static_proc_elements; + if (proc->frequency && ((FrameCount+procnum) % proc->frequency)) + return; + int x=proc->x1; + int y=proc->y1; + int cx, cy; + int left,top,right,bottom; + int square, dist; + int radius=proc->size; + int radsquare = radius * radius; + float length = (1024.0/(float)radius)*(1024.0/(float)radius); + short *dest_data=(short *)GameTextures[handle].procedural->proc1; + int height=proc->speed; + radsquare = (radius*radius); + left=-radius; right = radius; + top=-radius; bottom = radius; + // Perform edge clipping... + if(x - radius < 1) left -= (x-radius-1); + if(y - radius < 1) top -= (y-radius-1); + if(x + radius > PROC_SIZE-1) right -= (x+radius-PROC_SIZE+1); + if(y + radius > PROC_SIZE-1) bottom-= (y+radius-PROC_SIZE+1); + for(cy = top; cy < bottom; cy++) + { + for(cx = left; cx < right; cx++) + { + square = cy*cy + cx*cx; + if(square < radsquare) + { + dist = sqrt(square*length); + int addval=(FixCos(dist%65536)*(float)height); + addval/=8; + dest_data[PROC_SIZE*(cy+y) + cx+x] += addval; + } + } + } +} +void AllocateMemoryForWaterProcedural (int handle) +{ + proc_struct *procedural=GameTextures[handle].procedural; + int w=PROC_SIZE; + int h=PROC_SIZE; + if (procedural->proc1) + mem_free (procedural->proc1); + if (procedural->proc2) + mem_free (procedural->proc2); + GameTextures[handle].procedural->proc1=(ushort *)mem_malloc (w*h*2); + ASSERT (GameTextures[handle].procedural->proc1); + memset (GameTextures[handle].procedural->proc1,0,w*h*2); + + GameTextures[handle].procedural->proc2=(ushort *)mem_malloc (w*h*2); + ASSERT (GameTextures[handle].procedural->proc2); + memset (GameTextures[handle].procedural->proc2,0,w*h*2); + procedural->memory_type=PROC_MEMORY_TYPE_WATER; +} +// Does a water effect for this texture +void EvaluateWaterProcedural (int handle) +{ + proc_struct *procedural=GameTextures[handle].procedural; + if (procedural->memory_type!=PROC_MEMORY_TYPE_WATER) + AllocateMemoryForWaterProcedural (handle); + + int dest_bitmap=procedural->procedural_bitmap; + for (int i=0;inum_static_elements;i++) + { + static_proc_element *proc=&procedural->static_proc_elements[i]; + switch (proc->type) + { + case PROC_WATER_HEIGHT_BLOB: + AddProcHeightBlob (proc,handle); + break; + case PROC_WATER_SINE_BLOB: + AddProcSineBlob (proc,handle); + break; + case PROC_WATER_RAINDROPS: + AddProcRaindrops (proc,handle); + break; + case PROC_WATER_BLOBDROPS: + AddProcBlobdrops (proc,handle); + break; + default: + break; + } + } + if (EasterEgg) + { + if (Easter_egg_handle!=-1) + { + ushort *src_data=bm_data(Easter_egg_handle,0); + short *dest_data=(short *)GameTextures[handle].procedural->proc1; + int sw=bm_w(Easter_egg_handle,0); + int sh=bm_w(Easter_egg_handle,0); + int x1=(PROC_SIZE/2)-(sw/2); + int y1=(PROC_SIZE/2)-(sh/2); + // Make sure size is valid + if (sw<=PROC_SIZE && sh<=PROC_SIZE) + { + int i,t; + for (i=0;ilight) + DrawWaterNoLight(handle); + else + DrawWaterWithLight(handle,procedural->light-1); + int thickness=procedural->thickness; + if (procedural->osc_time>0) + { + int start=procedural->osc_value; + int end=procedural->thickness; + if (start>end) + { + int t=start; + start=end; + end=t; + } + int diff=end-start; + if (diff>0) + { + float gametime=timer_GetTime(); + float frametime=procedural->osc_time/(diff); + float cur_frametime=gametime/frametime; + int int_frame=cur_frametime; + + int_frame%=((diff)*2); + if (int_frame>=diff) + int_frame=(diff-1)-(int_frame%diff); + else + int_frame%=diff; + + thickness=start+int_frame; + } + } + CalcWater (handle,thickness); + // Swap for next time + short *temp=(short *)procedural->proc1; + procedural->proc1=procedural->proc2; + procedural->proc2=temp; +} +void AllocateMemoryForFireProcedural (int handle) +{ + proc_struct *procedural=GameTextures[handle].procedural; + int w=PROC_SIZE; + int h=PROC_SIZE; + if (procedural->proc1) + mem_free (procedural->proc1); + if (procedural->proc2) + mem_free (procedural->proc2); + GameTextures[handle].procedural->proc1=(ubyte *)mem_malloc (w*h*1); + ASSERT (GameTextures[handle].procedural->proc1); + memset (GameTextures[handle].procedural->proc1,0,w*h*1); + + GameTextures[handle].procedural->proc2=(ubyte *)mem_malloc (w*h*1); + ASSERT (GameTextures[handle].procedural->proc2); + memset (GameTextures[handle].procedural->proc2,0,w*h*1); + procedural->memory_type=PROC_MEMORY_TYPE_FIRE; +} +void EvaluateFireProcedural (int handle) +{ + proc_struct *procedural=GameTextures[handle].procedural; + + int dest_bitmap=procedural->procedural_bitmap; + if (procedural->memory_type!=PROC_MEMORY_TYPE_FIRE) + { + AllocateMemoryForFireProcedural (handle); + } + ProcDestData=(ubyte *)procedural->proc1; + // fade and the current texture + FadeProcTexture (handle); + //HeatProcTexture (handle); + // Do the static procedurals first + int i; + for (i=0;inum_static_elements;i++) + { + static_proc_element *proc=&procedural->static_proc_elements[i]; + switch (proc->type) + { + case PROC_LINE_LIGHTNING: + AddProcLightning (proc->x1,proc->y1,proc->x2,proc->y2,BRIGHT_COLOR,proc); + break; + case PROC_SPHERE_LIGHTNING: + AddProcSphereLightning (handle,proc); + break; + case PROC_RISING_EMBER: + AddProcRisingEmber (handle,proc); + break; + case PROC_RANDOM_EMBERS: + AddProcRandomEmber (handle,proc); + break; + case PROC_SPINNERS: + AddProcSpinners (handle,proc); + break; + case PROC_ROAMERS: + AddProcRoamers (handle,proc); + break; + case PROC_FOUNTAIN: + AddProcFountain (handle,proc); + break; + case PROC_CONE: + AddProcCone (handle,proc); + break; + case PROC_FALL_RIGHT: + AddProcFallRight (handle,proc); + break; + case PROC_FALL_LEFT: + AddProcFallLeft (handle,proc); + break; + default: + break; + } + + } + int proc_num=procedural->dynamic_proc_elements; + while (proc_num!=-1) + { + dynamic_proc_element *proc=&DynamicProcElements[proc_num]; + + switch (proc->type) + { + case PROC_RISING_EMBER: + DoDynamicRisingEmber (handle,proc); + break; + case PROC_RANDOM_EMBERS: + DoDynamicRandomEmber (handle,proc); + break; + case PROC_SPINNERS: + DoDynamicSpinners (handle,proc); + break; + case PROC_ROAMERS: + DoDynamicRoamers (handle,proc); + break; + case PROC_FOUNTAIN: + DoDynamicFountain (handle,proc); + break; + case PROC_CONE: + DoDynamicCone (handle,proc); + break; + case PROC_FALL_RIGHT: + DoDynamicFallRight (handle,proc); + break; + case PROC_FALL_LEFT: + DoDynamicFallLeft (handle,proc); + break; + default: + break; + } + proc_num=DynamicProcElements[proc_num].next; + } + + // blend the current texture + BlendProcTexture (handle); + ProcDestData=(ubyte *)procedural->proc2; + int total=PROC_SIZE*PROC_SIZE; + ushort *data=bm_data (dest_bitmap,0); + ushort *pal=procedural->palette; + for (i=0;iproc1; + procedural->proc1=procedural->proc2; + procedural->proc2=temp; +} +// Does a procedural for this texture +void EvaluateProcedural (int handle) +{ + proc_struct *procedural=GameTextures[handle].procedural; + + int dest_bitmap=procedural->procedural_bitmap; + if (bm_w(dest_bitmap,0)!=PROC_SIZE) + { + mprintf ((0,"Couldn't evaluate procedural because its not %d x %d!\n",PROC_SIZE,PROC_SIZE)); + return; + } + if (GameTextures[handle].flags & TF_WATER_PROCEDURAL) + EvaluateWaterProcedural (handle); + else + EvaluateFireProcedural (handle); +} diff --git a/Descent3/procedurals.h b/Descent3/procedurals.h new file mode 100644 index 000000000..0bba4d4aa --- /dev/null +++ b/Descent3/procedurals.h @@ -0,0 +1,86 @@ +#ifndef PROCEDRUALS_H +#define PROCEDURALS_H + +#include "pstypes.h" +#include "pserror.h" +#include "fix.h" + +#define MAX_PROC_ELEMENTS 8000 + + +// Fire procedurals +#define PROC_NONE 0 +#define PROC_LINE_LIGHTNING 1 +#define PROC_SPHERE_LIGHTNING 2 +#define PROC_STRAIGHT 3 +#define PROC_RISING_EMBER 4 +#define PROC_RANDOM_EMBERS 5 +#define PROC_SPINNERS 6 +#define PROC_ROAMERS 7 +#define PROC_FOUNTAIN 8 +#define PROC_CONE 9 +#define PROC_FALL_RIGHT 10 +#define PROC_FALL_LEFT 11 + +#define PROC_WATER_NONE 0 +#define PROC_WATER_HEIGHT_BLOB 1 +#define PROC_WATER_SINE_BLOB 2 +#define PROC_WATER_RAINDROPS 3 +#define PROC_WATER_BLOBDROPS 4 + + +typedef struct +{ + ubyte type; + fix dx,dy; + ubyte frames_left; + ubyte speed; + ubyte color; + + ubyte size; + + fix x1,y1; + + short prev,next; +} dynamic_proc_element; + +extern dynamic_proc_element DynamicProcElements[]; +extern char *ProcNames[],*WaterProcNames[]; + +extern ushort DefaultProcPalette[]; + +// Goes through our array and clears the slots out +void InitProcedurals(); + +// Fades and entire texture one step closer to black +void FadeProcTexture (int tex_handle); + +// Draws lightning into a bitmap +void AddProcLightning (int x1,int y1,int x2,int y2); + +//link the procedural into the list for its texture +void ProcElementLink(int index,int texnum); + +// Unlinks a proc_element from a texture +void ProcElementUnlink(int index,int texnum); + + +// Clears all the procedurals elements attached to this texture +void ClearAllProceduralsFromTexture (int texnum); + + +// Does a procedural for this texture +void EvaluateProcedural (int texnum); + +// Returns the next free procelement +int ProcElementAllocate (); + +// Frees up a procelement for use +int ProcElementFree (int index); + +// Given an array of r,g,b values, generates a 16bit palette table for those colors +void GeneratePaletteForProcedural (ubyte *r,ubyte *g,ubyte *b,ushort *pal); + +#endif + + diff --git a/Descent3/program.cpp b/Descent3/program.cpp new file mode 100644 index 000000000..000a9a12e --- /dev/null +++ b/Descent3/program.cpp @@ -0,0 +1,110 @@ +/* + * $Logfile: /DescentIII/Main/program.cpp $ + * $Revision: 8 $ + * $Date: 5/13/99 3:41p $ + * $Author: Ardussi $ + * + * program state info + * + * $Log: /DescentIII/Main/program.cpp $ + * + * 8 5/13/99 3:41p Ardussi + * changes for compiling on the Mac + * + * 7 4/15/99 1:42a Jeff + * changes for linux compile + * + * 6 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 5 10/18/98 9:12p Matt + * Use new symbolic constant for the program name. + * + * 4 10/12/98 1:45p Samir + * added version key to database. + * + * 3 10/08/98 7:30p Samir + * changed version numbers. + * + * 2 10/02/98 12:30p Samir + * added version information for beta/demo. + * + * 3 2/04/97 5:07p Samir + * Added windowed state to determine if we run in a window + * + * 2 2/04/97 2:30p Samir + * Changed program_state to program_version. + * + * 1 2/03/97 4:42p Samir + * Program version control + * + * $NoKeywords: $ + */ + +#include "program.h" +#include "pserror.h" +#include "descent.h" + +#include "appdatabase.h" + + +program_version Program_version; + + +// Initializes the current program state + +void ProgramVersion(int version_type, ubyte major, ubyte minor, ubyte build) +{ +#if defined(WIN32) // I'm sorry. Samir + oeWin32AppDatabase dbase((oeWin32AppDatabase *)Database); +#elif defined(__LINUX__) + oeLnxAppDatabase dbase((oeLnxAppDatabase *)Database); +#elif defined(MACINTOSH) + oeMacAppDatabase dbase((oeMacAppDatabase *)Database); +#else + oeAppDatabase dbase(Database); // this will fail without an operating system equiv +#endif + + Program_version.version_type = version_type; + Program_version.major = major; + Program_version.minor = minor; + Program_version.build = build; + + PROGRAM(beta) = 0; + PROGRAM(demo) = 0; + + if (version_type & BETA_VERSION) + PROGRAM(beta) = 1; + if (version_type & DEMO_VERSION) + PROGRAM(demo) = 1; + + switch (version_type & (~0xf000)) + { + case DEVELOPMENT_VERSION: + PROGRAM(debug) = 1; + PROGRAM(editor) = 0; + PROGRAM(release) = 0; + PROGRAM(windowed) = 0; + break; + + case RELEASE_VERSION: + PROGRAM(debug) = 0; + PROGRAM(editor) = 0; + PROGRAM(release) = 0; + PROGRAM(windowed) = 0; + break; + + default: + Int3(); // NO NO NO + } + + if (dbase.lookup_record("Version")) { + dbase.write("Major",Program_version.major); + dbase.write("Minor",Program_version.minor); + dbase.write("Build",Program_version.build); + } + else { + Error("Unable to find version key for %s",PRODUCT_NAME); + } +} + \ No newline at end of file diff --git a/Descent3/program.h b/Descent3/program.h new file mode 100644 index 000000000..a45876747 --- /dev/null +++ b/Descent3/program.h @@ -0,0 +1,179 @@ +/* + * $Logfile: /DescentIII/main/program.h $ + * $Revision: 40 $ + * $Date: 8/30/01 4:31p $ + * $Author: Matt $ + * + * program state header + * + * $Log: /DescentIII/main/program.h $ + * + * 40 8/30/01 4:31p Matt + * Updated version number to 1.5 + * + * 39 4/19/00 5:37p Matt + * Changed version number to 1.4 (accidentally left it as 1.4.1 last time) + * + * 38 3/21/00 10:20a Matt + * Changed to version 1.4 + * + * 37 10/18/99 4:03p Chris + * Merc is 1.3 + * + * 35 10/18/99 1:27p Kevin + * Added cf_IsFileInHog + * + * 34 9/29/99 8:04a Kevin + * + * 33 7/06/99 5:55p Kevin + * 1.1 patch for the US version + * + * 32 6/16/99 12:03p Kevin + * version + * + * 31 6/10/99 4:12p Kevin + * Fixed SetLevel in dedicated server for HEAT.NET, and added + * level_names.str for level name localization. + * + * 30 5/03/99 5:24p Kevin + * Version number + * + * 29 5/01/99 10:41a Kevin + * beta 6 + * + * 28 4/28/99 4:14p Kevin + * + * 27 4/25/99 12:19p Kevin + * version change + * + * 26 4/21/99 9:47a Kevin + * beta 3 + * + * 25 4/18/99 1:18p Kevin + * Demo2 build changes + * + * 24 4/15/99 9:25a Kevin + * CD check fixes and updates + * + * 23 4/07/99 9:19a Kevin + * Changed version number for full version beta 1(?) + * + * 22 3/22/99 10:14a Kevin + * Added a build number to the release builds of main. Also put in code to + * increment the build number each time a release build is done. + * + * 21 3/19/99 4:36p Kevin + * + * 20 3/16/99 11:26a Kevin + * Changed version number for OEM build + * + * 19 3/03/99 7:21p Jeff + * updated program version for oem final + * + * 18 3/03/99 1:03a Kevin + * Version Change + * + * 17 3/02/99 8:50p Jeff + * + * 16 3/02/99 11:52a Kevin + * Fixes for OEM Beta 4.1 + * + * 15 3/01/99 9:03p Kevin + * OEM Beta 4 + * + * 14 2/26/99 6:48p Kevin + * Fixes to load/save game, and change minor version for OEM beta 3 + * + * 13 2/24/99 3:35p Kevin + * Changed OEM minor version to 2 + * + * 12 2/20/99 12:33p Kevin + * Added some OEM stuff + * + * 11 10/23/98 1:09p Samir + * Demo Release 1.0 + * + * 10 10/22/98 11:07a Samir + * Demo Beta 7! + * + * 9 10/20/98 3:52p Samir + * Demo Beta 6 + * + * 8 10/19/98 7:48p Samir + * beta 5 demo + * + * 7 10/16/98 1:11p Samir + * Beta 4 Demo + * + * 6 10/13/98 5:51p Samir + * beta 3 + * + * 5 10/12/98 1:45p Samir + * added version key to database. + * + * 4 10/08/98 10:48p Samir + * version 2.0 + * + * 3 10/08/98 7:30p Samir + * changed version numbers. + * + * 2 10/02/98 12:30p Samir + * added version information for beta/demo. + * + * 4 2/11/97 3:17p Samir + * Tookout editor defines. + * + * 3 2/06/97 4:04p Samir + * Added editor and beta defines + * + * 2 2/04/97 5:07p Samir + * Added windowed state to determine if we run in a window + * + * 1 2/03/97 5:07p Samir + * Program version info + * + * $NoKeywords: $ + */ + +#ifndef PROGRAM_H +#define PROGRAM_H + +#include "pstypes.h" + +#include "buildno.h" + +#define D3_MAJORVER 0x1 // DESCENT 3 VERSION NUMBER +#define D3_MINORVER 0x5 +#define D3_BUILD 0x0 + +#define DEVELOPMENT_VERSION 0x1 // without editor: with debug, no beta +#define RELEASE_VERSION 0x2 // final release candidate: no debug, beta, editor + +#define BETA_VERSION 0x1000 // beta modifier. +#define DEMO_VERSION 0x2000 // same as release, but its the demo. + + +typedef struct t_program_version { + int version_type; + ubyte major, minor, build; + bool debug: 1; // are we in debug mode + bool beta:1; // are we in beta testing mode + bool release: 1; // are we a final release candidate + bool editor: 1; // editor code? + bool windowed: 1; // runs in a window? + bool demo:1; // demo? +} program_version; + + +// Program state available to everyone +extern program_version Program_version; + + +// Useful macros +#define PROGRAM(_c) Program_version._c + +// functions +void ProgramVersion(int version_type, ubyte major, ubyte minor, ubyte build); + + +#endif \ No newline at end of file diff --git a/Descent3/render.cpp b/Descent3/render.cpp new file mode 100644 index 000000000..2505251c0 --- /dev/null +++ b/Descent3/render.cpp @@ -0,0 +1,4269 @@ +/* + * $Logfile: /DescentIII/Main/render.cpp $ + * $Revision: 325 $ + * $Date: 9/10/01 5:16p $ + * $Author: Matt $ + * + * Code to render the mine + * + * $Log: /DescentIII/Main/render.cpp $ + * + * $NoKeywords: $ + */ +#include "render.h" +#include +#include +#include "descent.h" +#include "3d.h" +#include "mono.h" +#include "gametexture.h" +#include "texture.h" +#include "vclip.h" +#include "program.h" +#include "game.h" +#include "renderobject.h" +#include "door.h" +#include "terrain.h" +#include "renderer.h" +#include "room.h" +#include "lighting.h" +#include "lightmap.h" +#include "limits.h" +#include "lightmap_info.h" +#include "viseffect.h" +#include "weapon.h" +#include "fireball.h" +#include "scorch.h" +#include "findintersection.h" +#include "special_face.h" +#include "BOA.h" +#include "config.h" +#include "gameloop.h" +#include "doorway.h" +#include "TelComAutoMap.h" +#include "postrender.h" +#include "mem.h" +#include "Macros.h" +#include "psrand.h" +#include "player.h" +#include "args.h" +#ifdef EDITOR + #include "editor\d3edit.h" +#endif + +//#define KATMAI + +//Katmai enhanced rotate only in a release build, because not +//everyone has the intel compiler! +#if ( defined(RELEASE) && defined(KATMAI) ) +// Katmai version -- Rotates all the points in a room +void RotateRoomPoints (room *rp,vector4 *world_vecs); +#endif + +#ifdef MACINTOSH +#include "Macros.h" +#endif + + +static int Faces_rendered=0; +extern float GetFPS(); +extern ubyte Outline_release_mode; +//3d point for each vertex for use during rendering a room +ubyte Room_clips[MAX_VERTS_PER_ROOM]; // used for face culling +//default face reflectivity +float Face_reflectivity = 0.5; +//the position of the viewer - valid while a frame is being rendered +static vector Viewer_eye; +static matrix Viewer_orient; +int Viewer_roomnum; +int Flag_automap,Called_from_terrain; +// Fog zone variables +float Fog_zone_start=FLT_MAX,Fog_zone_end=FLT_MAX; +int Must_render_terrain; +int Global_buffer_index; +int No_render_windows_hack=-1; +#define WALL_PULSE_INCREMENT .01 +//Variables for various debugging features +#ifndef _DEBUG +#define In_editor_mode 0 +#define Outline_lightmaps 0 +#define Outline_alpha 0 +#define Render_floating_triggers 0 +#define Use_software_zbuffer 0 +#define Render_all_external_rooms 0 +#define Render_portals 0 +#define Render_one_room_only 0 +#define Render_inside_only 0 +#define Shell_render_flag 0 +#else //ifdef _DEBUG +// If true, draw white outline for each polygon +int Render_portals=0; +ubyte Outline_mode=0; +ubyte Shell_render_flag=0; +bool Outline_alpha=0; +bool Outline_lightmaps=0; +bool Render_floating_triggers=0; +bool Use_software_zbuffer=0; +bool Lighting_on = 1; +bool Render_all_external_rooms=0; +bool In_editor_mode=0; +bool Render_one_room_only=0; +bool Render_inside_only=0; +#endif //ifdef _DEBUG +#ifndef RELEASE +int Mine_depth; +#endif +#ifndef EDITOR +#define Search_lightmaps 0 +#else //ifndef EDITOR +//Vars for find_seg_side_face() +static int Search_lightmaps=0; // true if searching for a lightmap +int found_lightmap; +#endif //ifndef EDITOR +bool Vsync_enabled = true; +// Prototypes +void RenderRoomObjects(room *rp); +//The current window width & height (valid while rendering) +static int Render_width,Render_height; +int Clear_window_color=-1; +int Clear_window=2; // 1 = Clear whole background window, 2 = clear view portals into rest of world, 0 = no clear +#define MAX_RENDER_ROOMS 100 +char Rooms_visited[MAX_ROOMS+MAX_PALETTE_ROOMS]; +int Facing_visited[MAX_ROOMS+MAX_PALETTE_ROOMS]; +// For keeping track of portal recursion +ubyte Room_depth_list[MAX_ROOMS+MAX_PALETTE_ROOMS]; +short Render_list[MAX_RENDER_ROOMS]; +short External_room_list[MAX_ROOMS]; +int N_external_rooms; +// For rendering specular faces +#define MAX_SPECULAR_FACES 2500 +short Specular_faces[MAX_SPECULAR_FACES]; +int Num_specular_faces_to_render=0; +int Num_real_specular_faces_to_render=0; // Non-invisible specular faces +typedef struct +{ + float r,g,b; + int used; +} smooth_spec_vert; +smooth_spec_vert Smooth_verts[MAX_VERTS_PER_ROOM]; +// For scorch rendering +ushort Scorches_to_render[MAX_FACES_PER_ROOM]; +int Num_scorches_to_render=0; +// For rendering volumetric fog +#define MAX_FOGGED_ROOMS_PER_FRAME 8 +fog_portal_data Fog_portal_data[MAX_FOGGED_ROOMS_PER_FRAME]; +int Num_fogged_rooms_this_frame=0; +float Room_light_val=0; +int Room_fog_plane_check=0; +float Room_fog_distance=0; +float Room_fog_eye_distance=0; +vector Room_fog_plane,Room_fog_portal_vert; +short Fog_faces[MAX_FACES_PER_ROOM]; +int Num_fog_faces_to_render=0; +#define MAX_EXTERNAL_ROOMS 100 +vector External_room_corners[MAX_EXTERNAL_ROOMS][8]; +ubyte External_room_codes[MAX_EXTERNAL_ROOMS]; +ubyte External_room_project_net[MAX_EXTERNAL_ROOMS]; +// For light glows +#define MAX_LIGHT_GLOWS 100 +#define LGF_USED 1 +#define LGF_INCREASING 2 +#define LGF_FAST 4 +typedef struct +{ + short roomnum; + short facenum; + float size; + vector center; + float scalar; + ubyte flags; +} light_glow; +light_glow LightGlows[MAX_LIGHT_GLOWS]; +light_glow LightGlowsThisFrame[MAX_LIGHT_GLOWS]; +int FastCoronas=0; +int Num_glows=0,Num_glows_this_frame=0; +// For sorting our textures in state limited environments +state_limited_element State_elements[MAX_STATE_ELEMENTS]; +// For terrain portals +int Terrain_portal_left,Terrain_portal_right,Terrain_portal_top,Terrain_portal_bottom; +// For deformation effect +vector Global_alter_vec={19,-19,19}; +// For detail stuff (mirrors, specular,etc) +bool Render_mirror_for_room=false; +int Mirror_room; +int Num_mirrored_rooms; +short Mirrored_room_list[MAX_ROOMS]; +ubyte Mirrored_room_checked[MAX_ROOMS]; +short Mirror_rooms[MAX_ROOMS]; +int Num_mirror_rooms=0; +// +// UTILITY FUNCS +// +//Determines if a face renders +//Parameters: rp - pointer to room that contains the face +// fp - pointer to the face in question +inline bool FaceIsRenderable(room *rp,face *fp) +{ + //Check for a floating trigger, which doesn't get rendered + if ((fp->flags & FF_FLOATING_TRIG) && (!In_editor_mode || !Render_floating_triggers)) + return 0; + //Check for face that's part of a portal + if (fp->portal_num != -1) + { + if (rp->portals[fp->portal_num].flags & PF_RENDER_FACES) + return 1; + if (rp->flags & RF_FOG && !In_editor_mode) + return 1; + return 0; + } + + //Nothing special, so face renders + return 1; +} +//Flags for GetFaceAlpha() +#define FA_CONSTANT 1 //face has a constant alpha for the whole face +#define FA_VERTEX 2 //face has different alpha per vertex +#define FA_TRANSPARENT 4 //face has transparency (i.e. per pixel 1-bit alpha) +//Determines if a face draws with alpha blending +//Parameters: fp - pointer to the face in question +// bm_handle - the handle for the bitmap for this frame, or -1 if don't care about transparence +//Returns: bitmask describing the alpha blending for the face +// the return bits are the ATF_ flags in renderer.h +inline int GetFaceAlpha(face *fp,int bm_handle) +{ + int ret = AT_ALWAYS; + if (GameTextures[fp->tmap].flags & TF_SATURATE) + { + ret=AT_SATURATE_TEXTURE; + } + else + { + //Check the face's texture for an alpha value + if (GameTextures[fp->tmap].alpha < 1.0) + ret |= ATF_CONSTANT; + + //Check for transparency + if (GameBitmaps[bm_handle].format!=BITMAP_FORMAT_4444 && GameTextures[fp->tmap].flags & TF_TMAP2) + ret |= ATF_TEXTURE; + } + return ret; +} +//Determine if you should render through a portal +//Parameters: rp - the room the portal is in +// pp - the portal we're checking +//Returns: true if you should render the room to which the portal connects +inline bool RenderPastPortal(room *rp,portal *pp) +{ + //If we don't render the portal's faces, then we see through it + if (! (pp->flags & PF_RENDER_FACES)) + return 1; + if (!UseHardware) // Don't render alpha stuff in software + return 0; + //Check if the face's texture has transparency + face *fp = &rp->faces[pp->portal_face]; + if (GameTextures[fp->tmap].flags & TF_PROCEDURAL) + return 1; + int bm_handle = GetTextureBitmap(fp->tmap,0); + if (GetFaceAlpha(fp,bm_handle)) + return 1; //Face has alpha or transparency, so we can see through it + else + return 0; //Not transparent, so no render past +} + +//used during rendering as count of items in render_list[] +int N_render_rooms; +int first_terminal_room; +#define round(a) (int((a) + 0.5f)) + +#ifdef EDITOR +#define CROSS_WIDTH 8.0 +#define CROSS_HEIGHT 8.0 +#define CURFACE_COLOR GR_RGB( 255, 255, 0) +#define CUREDGE_COLOR GR_RGB( 0, 255, 0) +#define MARKEDFACE_COLOR GR_RGB( 0, 255, 255) +#define MARKEDEDGE_COLOR GR_RGB( 0, 150, 150) +#define PLACED_COLOR GR_RGB( 255, 0, 255) +//Draw outline for current edge & vertex +void OutlineCurrentFace(room *rp,int facenum,int edgenum,int vertnum,ddgr_color face_color,ddgr_color edge_color) +{ + face *fp = &rp->faces[facenum]; + g3Point p0,p1; + ubyte c0,c1; + int v; + for (v=0;vnum_verts;v++) { + c0 = g3_RotatePoint(&p0,&rp->verts[fp->face_verts[v]]); + c1 = g3_RotatePoint(&p1,&rp->verts[fp->face_verts[(v+1)%fp->num_verts]]); + if (! (c0 & c1)) { //both not off screen? + //Draw current edge in green + g3_DrawLine((v==edgenum)?edge_color:face_color,&p0,&p1); + } + if ((v==vertnum) && (c0==0)) { + //Draw a little cross at the current vert + g3_ProjectPoint(&p0); //make sure projected + rend_SetFlatColor(edge_color); + rend_DrawLine(p0.p3_sx-CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy-CROSS_HEIGHT); + rend_DrawLine(p0.p3_sx,p0.p3_sy-CROSS_HEIGHT,p0.p3_sx+CROSS_WIDTH,p0.p3_sy); + rend_DrawLine(p0.p3_sx+CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy+CROSS_HEIGHT); + rend_DrawLine(p0.p3_sx,p0.p3_sy+CROSS_HEIGHT,p0.p3_sx-CROSS_WIDTH,p0.p3_sy); + } + } + // Draw upper left cross + if (Outline_lightmaps && (rp->faces[facenum].flags & FF_LIGHTMAP)) + { + ASSERT (rp->faces[facenum].lmi_handle!=BAD_LMI_INDEX); + + p0.p3_flags=0; + c0 = g3_RotatePoint(&p0,&LightmapInfo[rp->faces[facenum].lmi_handle].upper_left); + if (! c0) + { + //Draw a little cross at the current vert + g3_ProjectPoint(&p0); //make sure projected + rend_SetFlatColor(GR_RGB(255,0,0)); + rend_DrawLine(p0.p3_sx-CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy-CROSS_HEIGHT); + rend_DrawLine(p0.p3_sx,p0.p3_sy-CROSS_HEIGHT,p0.p3_sx+CROSS_WIDTH,p0.p3_sy); + rend_DrawLine(p0.p3_sx+CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy+CROSS_HEIGHT); + rend_DrawLine(p0.p3_sx,p0.p3_sy+CROSS_HEIGHT,p0.p3_sx-CROSS_WIDTH,p0.p3_sy); + } + } +} + +// Draw a room rotated and placed in space +static void DrawPlacedRoomFace(room *rp,vector *rotpoint,matrix *rotmat,vector *placepoint,int facenum,int color) +{ + face *fp = &rp->faces[facenum]; + + g3Point p0,p1; + ubyte c0,c1; + int v; + for (v=0;vnum_verts;v++) { + vector tv; + + tv = (rp->verts[fp->face_verts[v]] - *rotpoint) * *rotmat + *placepoint; + c0 = g3_RotatePoint(&p0,&tv); + + tv = (rp->verts[fp->face_verts[(v+1)%fp->num_verts]] - *rotpoint) * *rotmat + *placepoint; + c1 = g3_RotatePoint(&p1,&tv); + + if (! (c0 & c1)) //both not off screen? + g3_DrawLine(color,&p0,&p1); + } +} +#endif //ifdef EDITOR + +struct clip_wnd +{ + float left,top,right,bot; +}; + +inline int clip2d(g3Point *pnt,clip_wnd *wnd) +{ + int ret = 0; + if (pnt->p3_codes & CC_BEHIND) + return CC_BEHIND; + + if (pnt->p3_sx < wnd->left) + ret |= CC_OFF_LEFT; + if (pnt->p3_sx > wnd->right) + ret |= CC_OFF_RIGHT; + if (pnt->p3_sy < wnd->top) + ret |= CC_OFF_TOP; + if (pnt->p3_sy > wnd->bot) + ret |= CC_OFF_BOT; + return ret; +} + +// Returns true if a line intersects another line +inline bool LineIntersectsLine(g3Point *ls,g3Point *le,float x1,float y1,float x2,float y2) +{ + float num,denom; + num=((ls->p3_sy-y1)*(x2-x1))-((ls->p3_sx-x1)*(y2-y1)); + denom=((le->p3_sx-ls->p3_sx)*(y2-y1))-((le->p3_sy-ls->p3_sy)*(x2-x1)); + float r=num/denom; + if (r>=0.0 && r<=1.0) + return true; + num=((ls->p3_sy-y1)*(le->p3_sx-ls->p3_sx))-((ls->p3_sx-x1)*(le->p3_sy-ls->p3_sy)); + denom=((le->p3_sx-ls->p3_sx)*(y2-y1))-((le->p3_sy-ls->p3_sy)*(x2-x1)); + float s=num/denom; + if (s>=0.0 && s<=1.0) + return true; + return false; +} +// Returns true if a face intersects the passed in portal in any way +inline bool FaceIntersectsPortal(room *rp,face *fp,clip_wnd *wnd) +{ + g3Codes cc; + int i; + + cc.cc_or=0; + cc.cc_and=0xff; + for (i=0;inum_verts;i++) + { + cc.cc_or|=Room_clips[fp->face_verts[i]]; + cc.cc_and&=Room_clips[fp->face_verts[i]]; + } + if (cc.cc_and) + return false; // completely outside + if (!cc.cc_or) + return true; // completely inside + + // Now we must do a check + for (i=0;inum_verts;i++) + { + g3Point *p1= &World_point_buffer[rp->wpb_index+fp->face_verts[i]]; + g3Point *p2= &World_point_buffer[rp->wpb_index+fp->face_verts[(i+1)%fp->num_verts]]; + if (LineIntersectsLine (p1,p2,wnd->left,wnd->top,wnd->right,wnd->top)) + return true; + if (LineIntersectsLine (p1,p2,wnd->right,wnd->top,wnd->right,wnd->bot)) + return true; + if (LineIntersectsLine (p1,p2,wnd->right,wnd->bot,wnd->left,wnd->bot)) + return true; + if (LineIntersectsLine (p1,p2,wnd->left,wnd->bot,wnd->left,wnd->top)) + return true; + } + return false; +} +// Sets the status of a glow light +void SetGlowStatus (int roomnum,int facenum,vector *center,float size,int fast) +{ + int i; + int first=1; + int first_free=-1; + int done=0; + int count=0; + int found=0; + for (i=0;i=Num_glows) + { + if (first_free==-1) + first_free=i; + done=1; + continue; + } + if (LightGlows[i].flags & LGF_USED) + { + count++; + if (LightGlows[i].roomnum==roomnum && LightGlows[i].facenum==facenum) + { + found=1; + LightGlows[i].flags|=LGF_INCREASING; + done=1; + } + } + else + { + if (first) + { + first_free=i; + first=0; + } + } + } + if (!found) // couldn't find it - is it a new one? + { + if (first_free==-1) // no free slots + return; + LightGlows[first_free].flags=LGF_USED|LGF_INCREASING; + if (fast) + LightGlows[first_free].flags|=LGF_FAST; + LightGlows[first_free].roomnum=roomnum; + LightGlows[first_free].facenum=facenum; + LightGlows[first_free].scalar=0; + LightGlows[first_free].size=size; + LightGlows[first_free].center=*center; + Num_glows++; + } + +} +// Takes a min,max vector and makes a surrounding cube from it +void MakePointsFromMinMax(vector *corners,vector *minp,vector *maxp) +{ + corners[0].x=minp->x; + corners[0].y=maxp->y; + corners[0].z=minp->z; + corners[1].x=maxp->x; + corners[1].y=maxp->y; + corners[1].z=minp->z; + corners[2].x=maxp->x; + corners[2].y=minp->y; + corners[2].z=minp->z; + corners[3].x=minp->x; + corners[3].y=minp->y; + corners[3].z=minp->z; + corners[4].x=minp->x; + corners[4].y=maxp->y; + corners[4].z=maxp->z; + corners[5].x=maxp->x; + corners[5].y=maxp->y; + corners[5].z=maxp->z; + corners[6].x=maxp->x; + corners[6].y=minp->y; + corners[6].z=maxp->z; + corners[7].x=minp->x; + corners[7].y=minp->y; + corners[7].z=maxp->z; +} + + +// Rotates all the points in a room +void RotateRoomPoints(room *rp,vector *world_vecs) +{ + int i; + // Jig the vertices a bit if being deformed + if (Viewer_object->effect_info && (Viewer_object->effect_info->type_flags & EF_DEFORM)) + { + for (i=0;inum_verts;i++) + { + vector vec=world_vecs[i]; + float val=((ps_rand()%1000)-500.0)/500.0; + val*=Viewer_object->effect_info->deform_time; + vec+=Global_alter_vec*(Viewer_object->effect_info->deform_range*val); + + g3_RotatePoint( &World_point_buffer[rp->wpb_index+i], &vec ); + g3_ProjectPoint( &World_point_buffer[rp->wpb_index+i] ); + } + } + else + { + for (i=0;inum_verts;i++) + { + g3_RotatePoint( &World_point_buffer[rp->wpb_index+i], &world_vecs[i] ); + g3_ProjectPoint( &World_point_buffer[rp->wpb_index+i] ); + } + } +} + + + +// Given a vector, reflects that vector off of a mirror vector +// Useful for specular and other reflective surfaces +void ReflectRay (vector *dest,vector *src,vector *mirror_norm) +{ + *dest = *src; + float d = *dest * *mirror_norm; + vector upvec = d * *mirror_norm; + *dest -= (2.0f*upvec); +} + +// This is needed for small view cameras +// It clears the facing array so that it is recomputed +void ResetFacings() +{ + memset (Facing_visited,0,sizeof(int)*(Highest_room_index+1)); +} + +// Marks all the faces facing us as drawable +void MarkFacingFaces(int roomnum,vector *world_verts) +{ + room *rp=&Rooms[roomnum]; + face *fp; + vector tvec; + if (Facing_visited[roomnum]==FrameCount) + return; + Facing_visited[roomnum]=FrameCount; + + fp=&rp->faces[0]; + // Go through and mark all non facing faces + if (Render_mirror_for_room) + { + room *mirror_rp=&Rooms[Mirror_room]; + face *mirror_fp=&mirror_rp->faces[mirror_rp->mirror_face]; + for (int t=0;tnum_faces;t++,fp++) + { + vector incident_norm; + ReflectRay (&incident_norm,&fp->normal,&mirror_fp->normal); + + tvec = Viewer_eye - world_verts[fp->face_verts[0]]; + if ((tvec * incident_norm) <= 0) + fp->flags |=FF_NOT_FACING; + } + } + else + { + for (int i=0;inum_faces;i++,fp++) + { + tvec = Viewer_eye - world_verts[fp->face_verts[0]]; + if ((tvec * fp->normal) <= 0) + fp->flags |=FF_NOT_FACING; + } + } +} + +// Returns true if the external room is visible from the passed in portal +int ExternalRoomVisibleFromPortal(int index,clip_wnd *wnd) +{ + int i; + ubyte code=0xff; + g3Point pnt; + // This is a stupid hack to prevent really large buildings from popping in and out of view + if (External_room_project_net[index]) + return 1; + for (i=0;i<8;i++) + { + pnt.p3_sx=External_room_corners[index][i].x; + pnt.p3_sy=External_room_corners[index][i].y; + pnt.p3_codes=0; + code &= clip2d( &pnt, wnd ); + } + if (code) + return 0; // building can't be seen from this portal + return 1; +} + +// Checks to see what faces intersect the passed in portal +void MarkFacesForRendering(int roomnum,clip_wnd *wnd) +{ + room *rp=&Rooms[roomnum]; + int i; + MarkFacingFaces(roomnum,rp->verts); + + // Rotate all the points in this room + if (rp->wpb_index==-1) + { + rp->wpb_index=Global_buffer_index; + + //Katmai enhanced rotate only in a release build, because not + //everyone has the intel compiler! +#if ( defined(RELEASE) && defined(KATMAI) ) + if(Katmai) + RotateRoomPoints (rp,rp->verts4); + else +#endif + RotateRoomPoints (rp,rp->verts); + + Global_buffer_index+=rp->num_verts; + } + if (rp->flags & RF_DOOR) + { + for (i=0;inum_faces;i++) + rp->faces[i].flags|=FF_VISIBLE; + } + else + { + // If this room contains a mirror, just mark all faces as visible + // Else go through and figure out which ones are visible from the current portal + if (rp->mirror_face==-1 || !Detail_settings.Mirrored_surfaces) + { + // Do pointer dereferencing instead of array lookup for speed reasons + g3Point *pnt = &World_point_buffer[rp->wpb_index]; + for (i=0;inum_verts;i++,pnt++) + { + Room_clips[i]=clip2d (pnt,wnd); + } + face *fp=&rp->faces[0]; + for (i=0;inum_faces;i++,fp++) + { + if (fp->flags & (FF_NOT_FACING|FF_VISIBLE)) + continue; // this face is a backface + + if (FaceIntersectsPortal(rp,fp,wnd)) + fp->flags|=FF_VISIBLE; + } + } + else + { + if (rp->flags & RF_MIRROR_VISIBLE) // If this room is already mirror, just return + return; + g3Point *pnt = &World_point_buffer[rp->wpb_index]; + for (i=0;inum_verts;i++,pnt++) + { + Room_clips[i]=clip2d (pnt,wnd); + } + int done=0; + face *fp; + for (i=0;inum_mirror_faces && !done;i++) + { + fp=&rp->faces[rp->mirror_faces_list[i]]; + if (FaceIntersectsPortal(rp,fp,wnd)) + { + rp->flags|=RF_MIRROR_VISIBLE; + Mirror_rooms[Num_mirror_rooms++]=roomnum; + done=1; + } + } + + if (rp->flags & RF_MIRROR_VISIBLE) // Mirror is visible, just mark all faces as visible + { + fp=&rp->faces[0]; + for (i=0;inum_faces;i++,fp++) + { + fp->flags|=FF_VISIBLE; + } + } + else + { + fp=&rp->faces[0]; + for (i=0;inum_faces;i++,fp++) + { + if (fp->flags & (FF_NOT_FACING|FF_VISIBLE)) + continue; // this face is a backface + + if (FaceIntersectsPortal(rp,fp,wnd)) + fp->flags|=FF_VISIBLE; + } + } + } + } + + // Mark objects for rendering + int objnum; + for (objnum=rp->objects;(objnum!=-1);objnum=Objects[objnum].next) + { + object *obj=&Objects[objnum]; + ubyte anded=0xff; + g3Point pnts[8]; + vector vecs[8]; + ubyte code; + if (rp->flags & RF_DOOR) // Render all objects in a door room + { + obj->flags|=OF_SAFE_TO_RENDER; + continue; + } + if (rp->flags & RF_MIRROR_VISIBLE) // Render all objects if this mirror is visible + { + obj->flags|=OF_SAFE_TO_RENDER; + continue; + } + + MakePointsFromMinMax (vecs,&obj->min_xyz,&obj->max_xyz); + for (i=0;i<8;i++) + { + g3_RotatePoint (&pnts[i],&vecs[i]); + g3_ProjectPoint (&pnts[i]); + code=clip2d( &pnts[i], wnd ); + anded&=code; + if (pnts[i].p3_codes & CC_BEHIND) + anded=0; + } + + if (!anded) + { + // Object is visible + obj->flags|=OF_SAFE_TO_RENDER; + } + } + +} +// Prerotates all external room points and caches them +void RotateAllExternalRooms () +{ + // Build the external room list if needed + if (N_external_rooms==-1) + { + // Set up our z wall + float zclip=(Detail_settings.Terrain_render_distance)*Matrix_scale.z; + g3_SetFarClipZ (zclip); + N_external_rooms=0; + + int i; + for (i=0;i<=Highest_room_index;i++) + { + if ((Rooms[i].flags & RF_EXTERNAL) && Rooms[i].used) + { + External_room_list[N_external_rooms++]=i; + } + } + ASSERT (N_external_roomsmin_xyz,&rp->max_xyz); + + ubyte andbyte=0xff; + g3Point pnt; + External_room_codes[i]=0xff; + External_room_project_net[i]=0; + bool behind=0; + bool infront=0; + for (int t=0;t<8;t++) + { + g3_RotatePoint(&pnt,&corners[t]); + External_room_codes[i]&=pnt.p3_codes; + if (pnt.p3_codes & CC_BEHIND) + behind=true; + else + infront=true; + pnt.p3_codes &=~CC_BEHIND; + g3_ProjectPoint (&pnt); + External_room_corners[i][t].x=pnt.p3_sx; + External_room_corners[i][t].y=pnt.p3_sy; + } + if (infront && behind) + { + External_room_codes[i]=0; + External_room_project_net[i]=1; + } + else + { + if (behind) + { + External_room_codes[i]=CC_BEHIND; + } + } + } + } +} + +void CheckFogPortalExtents(int roomnum,int portalnum) +{ + room *rp = &Rooms[roomnum]; + ASSERT( rp->flags & RF_FOG ); + + int i, found_room = -1; + for( i = 0; i < Num_fogged_rooms_this_frame; ++i ) + { + if( Fog_portal_data[i].roomnum != roomnum ) + continue; + + found_room = i; + break; + } + + if( found_room == -1 ) + { + // Couldn't find this room in our list, so make a new one + if( Num_fogged_rooms_this_frame >= MAX_FOGGED_ROOMS_PER_FRAME ) + { + mprintf ((0,"Too many fogged rooms in view cone!\n")); + return; + } + + found_room = Num_fogged_rooms_this_frame++; + Fog_portal_data[ found_room ].close_face = NULL; + Fog_portal_data[ found_room ].close_dist = 10000000.0f; + Fog_portal_data[ found_room ].roomnum = roomnum; + } + + // get the portal face + int fn = rp->portals[portalnum].portal_face; + face *fp = &rp->faces[fn]; + + // calculate the plane equation for the portal + vector *vec = &rp->verts[ fp->face_verts[0] ]; + float distance = -vm_DotProduct( &fp->normal, vec ); + + // calculate the distance from the camera to the portal + distance = vm_DotProduct( &fp->normal, &Viewer_eye ) + distance; + if( distance < Fog_portal_data[found_room].close_dist ) + { + // this portal is closer to the camera than the previous + Fog_portal_data[found_room].close_dist = distance; + Fog_portal_data[found_room].close_face = fp; + } +} + +g3Point Combined_portal_points[MAX_VERTS_PER_FACE*5]; +void BuildRoomListSub(int start_room_num,clip_wnd *wnd,int depth) +{ + room *rp = &Rooms[start_room_num]; + int i,t; + if (Render_portals) + { + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (255); + rend_SetFlatColor (GR_RGB(255,255,255)); + + rend_DrawLine (wnd->left,wnd->top,wnd->right,wnd->top); + rend_DrawLine (wnd->right,wnd->top,wnd->right,wnd->bot); + rend_DrawLine (wnd->right,wnd->bot,wnd->left,wnd->bot); + rend_DrawLine (wnd->left,wnd->bot,wnd->left,wnd->top); + } + if (!Rooms_visited[start_room_num]) + { + Render_list[N_render_rooms++] = start_room_num; + } + + ASSERT(N_render_rooms < MAX_RENDER_ROOMS); +#ifdef _DEBUG + Mine_depth++; +#endif + Rooms_visited[start_room_num] = 1; + Room_depth_list[start_room_num]=depth; + //If this room is a closed (non-seethrough) door, don't check any of its portals, + //...UNLESS this is the first room we're looking at (meaning the viewer is in this room) + if ((rp->flags & RF_DOOR) && (DoorwayGetPosition(rp) == 0.0) && !(Doors[rp->doorway_data->doornum].flags & DF_SEETHROUGH)) + if (depth != 0) + return; + + //Check all the portals for this room + for (t=0;tnum_portals;t++) + { + portal *pp = &rp->portals[t]; + int croom = pp->croom; + ASSERT (croom>=0); + + // If we are an external room portalizing into another external room, then skip! + if ((rp->flags & RF_EXTERNAL) && (Rooms[croom].flags & RF_EXTERNAL)) + continue; + + //Check if we can see through this portal, and if not, skip it + if (! RenderPastPortal(rp,pp)) + continue; + + // If this portal has been visited, skip it + if (Room_depth_list[croom]flags & RF_EXTERNAL && Rooms[croom].flags & RF_DOOR) + external_door_hack=1; + + //Get pointer to this portal's face + face *fp = &rp->faces[pp->portal_face]; + + //See if portal is facing toward us + if( !external_door_hack && !(pp->flags & PF_COMBINED) ) + { + vector check_v = Viewer_eye - rp->verts[fp->face_verts[0]]; + if( check_v * fp->normal <= 0 ) + { + //not facing us + continue; + } + } + + g3Codes cc; + cc.cc_or = 0; cc.cc_and = 0xff; + int nv = fp->num_verts; + + //Code the face points + // If this is a combined portal, then do that + if ((pp->flags & PF_COMBINED) && !(Rooms[croom].flags & RF_FOG)) + { + // If this isn't the portal-combine master, then skip it + if (pp->combine_master!=t) + continue; + int num_points=0; + for (i=0;inum_portals;i++) + { + if ( ((rp->portals[i].flags & PF_COMBINED)==0) || rp->portals[i].combine_master!=t) + continue; + + int k; + + face *this_fp = &rp->faces[rp->portals[i].portal_face]; + vector check_v; + check_v = Viewer_eye - rp->verts[this_fp->face_verts[0]]; + if (check_v * this_fp->normal <= 0) //not facing us + continue; + + g3Codes combine_cc; + combine_cc.cc_or = 0; combine_cc.cc_and = 0xff; + ASSERT ((num_points+this_fp->num_verts)<(MAX_VERTS_PER_FACE*5)); + + // First we must rotate and clip this polygon + for (k=0;knum_verts;k++) + { + ubyte c=g3_RotatePoint(&Combined_portal_points[num_points+k],&rp->verts[this_fp->face_verts[k]]); + combine_cc.cc_or|=c; + combine_cc.cc_and&=c; + } + if (combine_cc.cc_and) + { + continue; // clipped away! + } + else if (combine_cc.cc_or) + { + g3Point *pointlist[MAX_VERTS_PER_FACE]; + for (k=0;knum_verts;k++) + { + pointlist[k] = &Combined_portal_points[num_points+k]; + ASSERT (!(pointlist[k]->p3_flags & PF_TEMP_POINT)); + } + + //If portal not all on screen, must clip it + int combine_nv=this_fp->num_verts; + g3Point **pl = g3_ClipPolygon(pointlist,&combine_nv,&combine_cc); + if (combine_cc.cc_and) + { + g3_FreeTempPoints(pl,combine_nv); + } + else + { + // save the clipped points + g3Point temp_points[MAX_VERTS_PER_FACE]; + for (k=0;knum_verts; + } + } + if (num_points==0) + continue; + + // Now, figure out a min/max for these points + g3Point four_points[4]; + for (i=0;i combine_wnd.right) + { + combine_wnd.right = x; + right=i; + } + if (y < combine_wnd.top) + { + combine_wnd.top = y; + top=i; + } + if (y > combine_wnd.bot) + { + combine_wnd.bot = y; + bottom=i; + } + } + // Now harvest these points + four_points[0]=Combined_portal_points[left]; + four_points[1]=Combined_portal_points[top]; + four_points[2]=Combined_portal_points[right]; + four_points[3]=Combined_portal_points[bottom]; + for (i=0;i<4;i++) + { + Combined_portal_points[i]=four_points[i]; + Combined_portal_points[i].p3_flags&=~(PF_PROJECTED|PF_TEMP_POINT); + ubyte c = Combined_portal_points[i].p3_codes; + cc.cc_and &= c; + cc.cc_or |= c; + } + nv=4; + } + else + { + for (i=0;iverts[fp->face_verts[i]]); + + ubyte c = Combined_portal_points[i].p3_codes; + cc.cc_and &= c; + cc.cc_or |= c; + } + } + //If points are on screen, see if they're in the clip window + if (cc.cc_and == 0 || external_door_hack) + { + bool clipped = 0; + g3Point *pointlist[MAX_VERTS_PER_FACE],**pl = pointlist; + for (i=0;ip3_sx, y = pl[i]->p3_sy; + if (x < new_wnd.left) + new_wnd.left = x; + if (x > new_wnd.right) + new_wnd.right = x; + if (y < new_wnd.top) + new_wnd.top = y; + if (y > new_wnd.bot) + new_wnd.bot = y; + } + // If this room is fogged, see if this portal is closest + if (Rooms[croom].flags & RF_FOG) + { + CheckFogPortalExtents (croom,pp->cportal); + } + //Combine the two windows + new_wnd.left = __max(wnd->left,new_wnd.left); + new_wnd.right = __min(wnd->right,new_wnd.right); + new_wnd.top = __max(wnd->top,new_wnd.top); + new_wnd.bot = __min(wnd->bot,new_wnd.bot); + if (clipped) + { //Free up temp points + g3_FreeTempPoints(pl,nv); + clipped = 0; + } + if (Rooms[croom].flags & RF_EXTERNAL) + { + if (!Called_from_terrain) + { + Must_render_terrain = 1; + RotateAllExternalRooms(); + // For this external portal, we must check to see what external + // rooms are visible from here + for (i=0;i bz) + return -1; + else + return 0; +} +//build a list of rooms to be rendered +//fills in Render_list & N_render_rooms +void BuildRoomList(int start_room_num) +{ + clip_wnd wnd; + room *rp=&Rooms[start_room_num]; + int i; + //For now, render all connected rooms + for (i=0;i<=Highest_room_index;i++) + { + Rooms_visited[i] = 0; + Room_depth_list[i]=255; + Rooms[i].wpb_index=-1; + } + #ifdef EDITOR + Rooms_visited[start_room_num] = 0; //take care of rooms in the room palette + Room_depth_list[start_room_num]=0; + #endif + N_external_rooms=-1; + N_render_rooms = 0; + Global_buffer_index=0; + // Mark all the faces in our start room as renderable + for (i=0;inum_faces;i++) + rp->faces[i].flags|=FF_VISIBLE; + + MarkFacingFaces (start_room_num,rp->verts); + // Enable mirror if there is one + if (rp->mirror_face!=-1 && Detail_settings.Mirrored_surfaces && !(rp->faces[rp->mirror_face].flags & FF_NOT_FACING)) + { + rp->flags|=RF_MIRROR_VISIBLE; + Mirror_rooms[Num_mirror_rooms++]=start_room_num; + } + + // Get our points rotated, and update the global point list + rp->wpb_index=Global_buffer_index; + + //Katmai enhanced rotate only in a release build, because not + //everyone has the intel compiler! +#if ( defined(RELEASE) && defined(KATMAI) ) + if(Katmai) + + RotateRoomPoints (rp,rp->verts4); + else +#endif + RotateRoomPoints (rp,rp->verts); + + + Global_buffer_index+=rp->num_verts; + + // Mark all objects in this room as visible + for (int objnum=rp->objects;(objnum!=-1);objnum=Objects[objnum].next) + Objects[objnum].flags|=OF_SAFE_TO_RENDER; + + + //Initial clip window is whole screen + wnd.left = wnd.top = 0.0; + wnd.right = Render_width; + wnd.bot = Render_height; + BuildRoomListSub(start_room_num,&wnd,0); + //mprintf((0,"N_render_rooms = %d ",N_render_rooms)); + #ifdef EDITOR + //Add all external rooms to render list if that flag set + if (Editor_view_mode==VM_MINE && In_editor_mode) + { + if (Render_all_external_rooms) { + int i; + room *rp; + for (i=0,rp=Rooms;i<=Highest_room_index;i++,rp++) { + if (rp->used && (rp->flags & RF_EXTERNAL)) + { + for (int t=0;tnum_faces;t++) + rp->faces[t].flags|=FF_VISIBLE; + MarkFacingFaces (i,rp->verts); + + if (!Rooms_visited[i]) + Render_list[N_render_rooms++] = i; + Rooms_visited[i]=1; + } + } + } + } + #endif +} +#ifdef EDITOR +// Returns 1 if x,y is inside the given polygon, else 0 +int point_in_poly(int nv, g3Point *p, float x, float y) +{ + int i, j, c = 0; + for (i = 0, j = nv-1; i < nv; j = i++) + { + if ((((p[i].p3_sy<=y) && (yverts[fp->face_verts[0]]; + right = rp->verts[fp->face_verts[1]]; + g3_RotatePoint(&p3,&left); + stepsize = STEPSIZE * p3.p3_z; + if (stepsize < STEPSIZE_MIN) + stepsize = STEPSIZE_MIN; + leftvec = rp->verts[fp->face_verts[3]] - rp->verts[fp->face_verts[0]]; + rightvec = rp->verts[fp->face_verts[2]] - rp->verts[fp->face_verts[1]]; + n_steps = (vm_GetMagnitude(&leftvec) / stepsize + 0.5) + 1; + left_step = leftvec / n_steps; + right_step = rightvec / n_steps; + for (i=0;i0); + int i,vn; + g3Point *pointlist[MAX_VERTS_PER_FACE]; + g3Point pointbuffer[MAX_VERTS_PER_FACE]; + rend_SetWrapType (WT_CLAMP); + rend_SetOverlayType (OT_NONE); + rend_SetTextureType (TT_PERSPECTIVE); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (255); + + for (i=0;ifaces[Specular_faces[i]]; + int lm_handle=LightmapInfo[fp->lmi_handle].spec_map; + ASSERT (lm_handle!=-1); + for (vn=0;vnnum_verts;vn++) + { + pointbuffer[vn] = World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[vn] = p; + p->p3_uvl.u = fp->face_uvls[vn].u2; + p->p3_uvl.v = fp->face_uvls[vn].v2; + + p->p3_flags |= PF_UV; + } + + int save_w=lm_w(lm_handle); + int save_h=lm_h(lm_handle); + GameLightmaps[lm_handle].width=lm_w (LightmapInfo[fp->lmi_handle].lm_handle); + GameLightmaps[lm_handle].height=lm_h (LightmapInfo[fp->lmi_handle].lm_handle); + g3_DrawPoly(fp->num_verts,pointlist,lm_handle,MAP_TYPE_LIGHTMAP); + GameLightmaps[lm_handle].width=save_w; + GameLightmaps[lm_handle].height=save_h; + } + rend_SetWrapType (WT_WRAP); +} + +float Specular_scalars[4][4]={{1.0f},{1.0f,.66f},{1.0f,.66f,.33f},{1.0f,.66f,.33f,.25f}}; +void RenderSpecularFacesFlat(room *rp) +{ + static int first=1; + static float lm_red[32],lm_green[32],lm_blue[32]; + int num_smooth_faces=0; + int num_smooth_used=0; + ushort smooth_faces[MAX_FACES_PER_ROOM]; + ushort smooth_used[MAX_VERTS_PER_ROOM]; + int i; + ASSERT (Num_specular_faces_to_render>0); + if (!UseHardware) + return; + if (Num_real_specular_faces_to_render==0) + { + for (i=0;ifaces[Specular_faces[i]]; + fp->flags&=~FF_SPEC_INVISIBLE; + } + return; + } + if (first) + { + first=0; + int i; + for (i=0;i<32;i++) + { + lm_red[i]=(float)i/31.0; + lm_green[i]=(float)i/31.0; + lm_blue[i]=(float)i/31.0; + } + for (i=0;ifaces[Specular_faces[i]]; + + int material_type=0; + if (GameTextures[fp->tmap].flags & TF_PLASTIC) + material_type=1; + else if (GameTextures[fp->tmap].flags & TF_MARBLE) + material_type=2; + int bm_handle=GetTextureBitmap(fp->tmap,0); + if ((fp->flags & FF_DESTROYED) && (GameTextures[fp->tmap].flags & TF_DESTROYABLE)) + bm_handle = GetTextureBitmap(GameTextures[fp->tmap].destroy_handle,0); + if (bm_format (bm_handle)!=BITMAP_FORMAT_4444) + continue; + + int lm_handle; + ushort *data; + int w,h; + + if(fp->lmi_handle==65535) + { + // this face shouldn't be rendered during the specular pass...skip it + continue; + } + + lm_handle = LightmapInfo[fp->lmi_handle].lm_handle; + data=(ushort *)lm_data(lm_handle); + w=lm_w(lm_handle); + h=lm_h(lm_handle); + + for (vn=0;vnnum_verts;vn++) + { + + float rv=0,gv=0,bv=0; + float scalar=0; + float u,v; + int int_u,int_v; + ushort texel; + int r,g,b; + float vr,vg,vb; + + u = fp->face_uvls[vn].u2*w; + v = fp->face_uvls[vn].v2*h; + int_u=u; + int_v=v; + texel=data[int_v*w+int_u]; + r=(texel>>10) & 0x1f; + g=(texel>>5) & 0x1f; + b=(texel) & 0x1f; + vr=lm_red[r]; + vg=lm_green[g]; + vb=lm_blue[b]; + + vector subvec=Viewer_eye-rp->verts[fp->face_verts[vn]]; + vm_NormalizeVectorFast (&subvec); + if (!(rp->flags & RF_EXTERNAL)) + { + int limit=SpecialFaces[fp->special_handle].num; + int spec_index=limit-1; + /*if (GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) + { + limit/=2; + }*/ + + for (t=0;tspecial_handle].spec_instance[t].bright_color; + if (color==0) + continue; + incident_norm=rp->verts[fp->face_verts[vn]]-SpecialFaces[fp->special_handle].spec_instance[t].bright_center; + spec_scalar=Specular_scalars[spec_index][t]; + + vm_NormalizeVectorFast (&incident_norm); + float d; + vector upvec; + if ((GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) && (SpecialFaces[fp->special_handle].flags & SFF_SPEC_SMOOTH)) + { + d=incident_norm * SpecialFaces[fp->special_handle].vertnorms[vn]; + upvec=d * SpecialFaces[fp->special_handle].vertnorms[vn]; + } + else + { + d=incident_norm * fp->normal; + upvec=d * fp->normal; + + } + incident_norm-=(2*upvec); + float dotp=subvec * incident_norm; + if (dotp>1) + dotp=1; + + if (dotp>0) + { + int index=((float)(MAX_SPECULAR_INCREMENTS-1)*dotp); + scalar=Specular_tables[material_type][index]*spec_scalar; + + float cr=(float)((color>>10) & 0x1f)/31.0; + float cg=(float)((color>>5) & 0x1f)/31.0; + float cb=(float)(color & 0x1f)/31.0; + rv=min(1.0,(rv+(scalar*cr))); + gv=min(1.0,(gv+(scalar*cg))); + bv=min(1.0,(bv+(scalar*cb))); + } + } + } + else + { + vector incident_norm=rp->verts[fp->face_verts[vn]]-Terrain_sky.satellite_vectors[0]; + vm_NormalizeVectorFast (&incident_norm); + + float d=incident_norm * fp->normal; + vector upvec=d * fp->normal; + incident_norm-=(2*upvec); + float dotp=subvec * incident_norm; + if (dotp>1) + dotp=1; + + if (dotp>0) + { + int index=((float)(MAX_SPECULAR_INCREMENTS-1)*dotp); + scalar=Specular_tables[material_type][index]; + rv=scalar; + gv=scalar; + bv=scalar; + } + } + // Finally, brighten these value up a bit + if (GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) + { + rv=rv*vr; + gv=gv*vg; + bv=bv*vb; + if (Smooth_verts[fp->face_verts[vn]].used==0) + { + Smooth_verts[fp->face_verts[vn]].used=1; + Smooth_verts[fp->face_verts[vn]].r=rv; + Smooth_verts[fp->face_verts[vn]].g=gv; + Smooth_verts[fp->face_verts[vn]].b=bv; + smooth_used[num_smooth_used++]=fp->face_verts[vn]; + } + else + { + Smooth_verts[fp->face_verts[vn]].r+=rv; + Smooth_verts[fp->face_verts[vn]].g+=gv; + Smooth_verts[fp->face_verts[vn]].b+=bv; + } + } + else + { + rv=min(1.0,rv*vr*4.0); + gv=min(1.0,gv*vg*4.0); + bv=min(1.0,bv*vb*4.0); + pointbuffer[vn]= World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[vn] = p; + p->p3_uvl.u = fp->face_uvls[vn].u; + p->p3_uvl.v = fp->face_uvls[vn].v; + p->p3_a=1.0; + p->p3_r=rv; + p->p3_g=gv; + p->p3_b=bv; + p->p3_flags |= PF_RGBA|PF_UV; + } + } + if (GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) + { + smooth_faces[num_smooth_faces]=fp-rp->faces; + num_smooth_faces++; + } + else + { + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(1); + + g3_DrawPoly(fp->num_verts,pointlist,bm_handle); + + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(0); + } + } + // Now draw smooth specular faces + for (i=0;ifaces[smooth_faces[i]]; + if (fp->flags & FF_SPEC_INVISIBLE) + { + fp->flags &=~(FF_SPEC_INVISIBLE); + continue; + } + int bm_handle=GetTextureBitmap(fp->tmap,0); + if ((fp->flags & FF_DESTROYED) && (GameTextures[fp->tmap].flags & TF_DESTROYABLE)) + bm_handle = GetTextureBitmap(GameTextures[fp->tmap].destroy_handle,0); + if (bm_format (bm_handle)!=BITMAP_FORMAT_4444) + continue; + float reflect=GameTextures[fp->tmap].reflectivity*1.5; + for (vn=0;vnnum_verts;vn++) + { + pointbuffer[vn]= World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[vn] = p; + p->p3_uvl.u = fp->face_uvls[vn].u; + p->p3_uvl.v = fp->face_uvls[vn].v; + p->p3_a=1.0; + p->p3_r=min(1.0,Smooth_verts[fp->face_verts[vn]].r*reflect); + p->p3_g=min(1.0,Smooth_verts[fp->face_verts[vn]].g*reflect); + p->p3_b=min(1.0,Smooth_verts[fp->face_verts[vn]].b*reflect); + p->p3_flags |= PF_RGBA|PF_UV; + } + + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(1); + + g3_DrawPoly(fp->num_verts,pointlist,bm_handle); + + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(0); + } + for (i=0;iflags & RF_EXTERNAL) + { + int handle=GetSpecularLightmapForFace (&Viewer_eye,rp,fp); + if (handle<0) + return; + }*/ + if (Renderer_type==RENDERER_OPENGL) + return; + int n=Num_specular_faces_to_render; + if (n>=MAX_SPECULAR_FACES) + return; + Specular_faces[n]=fp-rp->faces; + Num_specular_faces_to_render++; + if (!(fp->flags & FF_SPEC_INVISIBLE)) + Num_real_specular_faces_to_render++; +} + +#if _DEBUG +bool Fog_disabled = 0; +#else +#define Fog_disabled 0 +#endif + +// Adds a specular face to draw after the mine has been drawn +void UpdateFogFace(room *rp,face *fp) +{ + if( Fog_disabled || !Detail_settings.Fog_enabled ) + return; + Fog_faces[Num_fog_faces_to_render++] = fp-rp->faces; +} + +// Render a fog layer on top of a face +void RenderFogFaces (room *rp) +{ + int vn; + float eye_distance; + g3Point *pointlist[MAX_VERTS_PER_FACE]; + g3Point pointbuffer[MAX_VERTS_PER_FACE]; + rend_SetOverlayType (OT_NONE); + rend_SetTextureType (TT_FLAT); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetAlphaType (AT_VERTEX); + rend_SetAlphaValue (255); + rend_SetZBufferWriteMask (0); + rend_SetCoplanarPolygonOffset(1); + + rend_SetFlatColor (GR_RGB((int)(rp->fog_r*255.0),(int)(rp->fog_g*255.0),(int)(rp->fog_b*255.0))); + for (int i=0;ifaces[Fog_faces[i]]; + for (vn=0;vnnum_verts;vn++) + { + pointbuffer[vn]= World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[vn] = p; + + float mag; + + if (Room_fog_plane_check==0) + { + // Outside of the room + vector *vec=&rp->verts[fp->face_verts[vn]]; + // Now we must generate the split point. This is simply + // an equation in the form Origin + t*Direction + + float dist = (*vec*Room_fog_plane)+ Room_fog_distance; + + vector subvec=*vec-Viewer_eye; + float t = Room_fog_eye_distance / (Room_fog_eye_distance - dist); + vector portal_point=Viewer_eye+(t*subvec); + + eye_distance=-(vm_DotProduct (&Viewer_orient.fvec,&portal_point)); + mag = vm_DotProduct(&Viewer_orient.fvec,vec)+eye_distance; + } + else if (Room_fog_plane_check==1) + { + // In the room, distance from + vector *vec=&rp->verts[fp->face_verts[vn]]; + mag = vm_DotProduct(&Room_fog_plane,vec)+Room_fog_distance; + } + float scalar=mag/rp->fog_depth; + if (scalar>1) + scalar=1; + if (scalar<0) + scalar=0; + p->p3_a=scalar*Room_light_val; + + p->p3_flags |= PF_RGBA; + } + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(1); + g3_DrawPoly(fp->num_verts,pointlist,0); + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(0); + } + + rend_SetCoplanarPolygonOffset(0); + rend_SetZBufferWriteMask (1); +} +// MATT! Change this function to sort by state once you change the scorch system! +void RenderScorchesForRoom (room *rp) +{ + int i; + if (!Detail_settings.Scorches_enabled) + return; + //Set alpha, transparency, & lighting for this face + rend_SetAlphaType(AT_LIGHTMAP_BLEND); + rend_SetAlphaValue(255); + rend_SetLighting(LS_NONE); + rend_SetColorModel(CM_MONO); + rend_SetOverlayType(OT_NONE); + rend_SetZBias (-.5); + rend_SetZBufferWriteMask (0); + + //Select texture type + rend_SetTextureType(TT_LINEAR); + + for (i=0;ifaces[facenum]; + g3Point *pointlist[MAX_VERTS_PER_FACE]; + g3Point pointbuffer[MAX_VERTS_PER_FACE]; + ubyte face_code=255; + if (NoLightmaps) + return; + if (fp->lmi_handle==BAD_LMI_INDEX) + return; + if (!(fp->flags & FF_LIGHTMAP)) + return; + // check for render windows hack + if (No_render_windows_hack==1) + { + if (fp->portal_num!=-1) + return; + } + if (GameTextures[fp->tmap].flags & TF_SATURATE) + return; + #ifdef EDITOR + if (In_editor_mode && !Lighting_on) + return; + #endif + int lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + // Setup saturation if needed + if (GameTextures[fp->tmap].flags & TF_SATURATE_LIGHTMAP) + rend_SetAlphaType (AT_LIGHTMAP_BLEND_SATURATE); + // Our lightmaps aren't square, but our destination texture surfaces are + // Use these scalars to get them into the correct coordinates + float xscalar=(float)GameLightmaps[lm_handle].width/(float)GameLightmaps[lm_handle].square_res; + float yscalar=(float)GameLightmaps[lm_handle].height/(float)GameLightmaps[lm_handle].square_res; + if (!Render_mirror_for_room) + { + for (vn=0;vnnum_verts;vn++) + { + pointbuffer[vn]= World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[vn] = p; + p->p3_uvl.u = fp->face_uvls[vn].u2*xscalar; + p->p3_uvl.v = fp->face_uvls[vn].v2*yscalar; + + p->p3_flags |= PF_UV + PF_L + PF_RGBA; //has uv and l set + p->p3_uvl.l=1.0; + + face_code&=p->p3_codes; + } + } + else + { + for (vn=0;vnnum_verts;vn++) + { + pointbuffer[vn]= World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[(fp->num_verts-1)-vn] = p; + p->p3_uvl.u = fp->face_uvls[vn].u2*xscalar; + p->p3_uvl.v = fp->face_uvls[vn].v2*yscalar; + + p->p3_flags |= PF_UV + PF_L + PF_RGBA; //has uv and l set + p->p3_uvl.l=1.0; + + face_code&=p->p3_codes; + } + } + + if (face_code) // This entire face is off the scren + return; + rend_SetAlphaValue (GameTextures[fp->tmap].alpha*255); + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(1); + //Draw the damn thing + drawn=g3_DrawPoly(fp->num_verts,pointlist,lm_handle,MAP_TYPE_LIGHTMAP); + if (fp->flags & FF_TRIANGULATED) + g3_SetTriangulationTest(0); + // Restore if using saturated blending + if (GameTextures[fp->tmap].flags & TF_SATURATE_LIGHTMAP) + rend_SetAlphaType (AT_LIGHTMAP_BLEND); +} +//Draw the specified face +//Parameters: rp - pointer to the room the face is un +// facenum - which face in the specified room +void RenderFace(room *rp,int facenum) +{ + int vn,drawn=0; + face *fp = &rp->faces[facenum]; + g3Point *pointlist[MAX_VERTS_PER_FACE]; + g3Point pointbuffer[MAX_VERTS_PER_FACE]; + int bm_handle; + float uchange=0,vchange=0; + texture_type tt; + ubyte do_triangle_test=0; + g3Codes face_cc; + static int first=1; + static float lm_red[32],lm_green[32],lm_blue[32]; + bool spec_face=0; + face_cc.cc_and=0xff; + face_cc.cc_or=0; + #ifdef EDITOR + if (fp->flags & FF_FLOATING_TRIG) { + RenderFloatingTrig(rp,fp); + return; + } + #endif + // Clear triangulation flag + fp->flags&=~FF_TRIANGULATED; + // check for render windows hack + if (No_render_windows_hack==1) + { + if (fp->portal_num!=-1) + return; + } + if (rp->flags & RF_TRIANGULATE) + do_triangle_test=1; + if (!Render_mirror_for_room && Detail_settings.Specular_lighting && (GameTextures[fp->tmap].flags & TF_SPECULAR) && + ((fp->special_handle!=BAD_SPECIAL_FACE_INDEX) || (rp->flags & RF_EXTERNAL))) + spec_face=1; +#ifdef MACINTOSH + //DAJ check for off screen early! + for (vn=0;vnnum_verts;vn++) + { + face_cc.cc_and &= World_point_buffer[rp->wpb_index+fp->face_verts[vn]].p3_codes; + } + if (face_cc.cc_and) // This entire face is off the screen + { + if (spec_face && GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) + { + fp->flags|=FF_SPEC_INVISIBLE; + UpdateSpecularFace (rp,fp); + } + return; + } +#endif + // Figure out if there is any texture sliding + if (GameTextures[fp->tmap].slide_u!=0) + { + int int_time=Gametime/GameTextures[fp->tmap].slide_u; + float norm_time=Gametime-(int_time*GameTextures[fp->tmap].slide_u); + norm_time/=GameTextures[fp->tmap].slide_u; + uchange=norm_time; + } + if (GameTextures[fp->tmap].slide_v!=0) + { + int int_time=Gametime/GameTextures[fp->tmap].slide_v; + float norm_time=Gametime-(int_time*GameTextures[fp->tmap].slide_v); + norm_time/=GameTextures[fp->tmap].slide_v; + vchange=norm_time; + } + //Build list of points and UVLs for this face + if (Render_mirror_for_room) // If mirror room, order the vertices counter clockwise + { + for (vn=0;vnnum_verts;vn++) + { + pointbuffer[vn]= World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[(fp->num_verts-1)-vn] = p; + p->p3_uvl.u = fp->face_uvls[vn].u; + p->p3_uvl.v = fp->face_uvls[vn].v; + p->p3_uvl.u2 = fp->face_uvls[vn].u2; + p->p3_uvl.v2 = fp->face_uvls[vn].v2; + + p->p3_flags |= PF_UV + PF_L + PF_UV2; //has uv and l set + #ifndef RELEASE + if ((fp->flags & FF_LIGHTMAP) && UseHardware) + p->p3_uvl.l=Room_light_val; + else + p->p3_uvl.l=1.0; + #else + p->p3_uvl.l=Room_light_val; + #endif + + // do texture sliding + p->p3_uvl.u+=uchange; + p->p3_uvl.v+=vchange; + face_cc.cc_and&=p->p3_codes; + face_cc.cc_or|=p->p3_codes; + + } + } + else + { + + for (vn=0;vnnum_verts;vn++) + { + pointbuffer[vn]= World_point_buffer[rp->wpb_index+fp->face_verts[vn]]; + g3Point *p = &pointbuffer[vn]; + pointlist[vn] = p; + p->p3_uvl.u = fp->face_uvls[vn].u; + p->p3_uvl.v = fp->face_uvls[vn].v; + p->p3_uvl.u2 = fp->face_uvls[vn].u2; + p->p3_uvl.v2 = fp->face_uvls[vn].v2; + + p->p3_flags |= PF_UV + PF_L + PF_UV2; //has uv and l set + #ifndef RELEASE + if ((fp->flags & FF_LIGHTMAP) && UseHardware) + p->p3_uvl.l=Room_light_val; + else + p->p3_uvl.l=1.0; + #else + p->p3_uvl.l=Room_light_val; + #endif + + // do texture sliding + p->p3_uvl.u+=uchange; + p->p3_uvl.v+=vchange; + face_cc.cc_and&=p->p3_codes; + face_cc.cc_or|=p->p3_codes; + + } + } +#ifndef MACINTOSH + if (face_cc.cc_and) // This entire face is off the screen + { + if (spec_face && GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) + { + fp->flags|=FF_SPEC_INVISIBLE; + UpdateSpecularFace (rp,fp); + } + return; + } +#endif + // Do stupid gouraud shading for lightmap + if (NoLightmaps) + { + if (first) + { + first=0; + for (int i=0;i<32;i++) + { + lm_red[i]=(float)i/31.0; + lm_green[i]=(float)i/31.0; + lm_blue[i]=(float)i/31.0; + } + } + + if (fp->flags & FF_LIGHTMAP) + { + int lm_handle=LightmapInfo[fp->lmi_handle].lm_handle; + ushort *data=(ushort *)lm_data(lm_handle); + int w=lm_w(lm_handle); + int h=lm_h(lm_handle); + + for (int i=0;inum_verts;i++) + { + float u=fp->face_uvls[i].u2*(w-1); + float v=fp->face_uvls[i].v2*(h-1); + g3Point *p = &pointbuffer[i]; + int int_u=u; + int int_v=v; + ushort texel=data[int_v*w+int_u]; + int r=(texel>>10) & 0x1f; + int g=(texel>>5) & 0x1f; + int b=(texel) & 0x1f; + p->p3_r=p->p3_l*lm_red[r]; + p->p3_g=p->p3_l*lm_green[g]; + p->p3_b=p->p3_l*lm_blue[b]; + p->p3_flags|=PF_RGBA; + } + } + else + { + for (int i=0;inum_verts;i++) + { + g3Point *p = &pointbuffer[i]; + p->p3_r=p->p3_l; + p->p3_g=p->p3_l; + p->p3_b=p->p3_l; + p->p3_flags|=PF_RGBA; + } + } + } + //Get bitmap handle + if ((fp->flags & FF_DESTROYED) && (GameTextures[fp->tmap].flags & TF_DESTROYABLE)) { + bm_handle = GetTextureBitmap(GameTextures[fp->tmap].destroy_handle,0); + ASSERT(bm_handle != -1); + } + else + bm_handle = GetTextureBitmap(fp->tmap,0); + //If searching, + #ifdef EDITOR + ddgr_color oldcolor; + if (TSearch_on) + { + rend_SetPixel (GR_RGB(16,255,16),TSearch_x,TSearch_y); + oldcolor=rend_GetPixel (TSearch_x,TSearch_y); + } + #endif + //Set alpha, transparency, & lighting for this face + rend_SetAlphaType(GetFaceAlpha(fp,bm_handle)); + + // If this is a mirror face, and mirrors are turned off, then just make opaque + if (!Detail_settings.Mirrored_surfaces && rp->mirror_face!=-1 && rp->faces[rp->mirror_face].tmap==fp->tmap) + rend_SetAlphaValue (255); + else if (Render_mirror_for_room && rp->mirror_face!=-1 && fp->tmap==rp->faces[rp->mirror_face].tmap && rp!=&Rooms[Mirror_room]) + rend_SetAlphaValue (255); // This prevents mirrors from rendering each other + else + rend_SetAlphaValue (GameTextures[fp->tmap].alpha*255); + if (!UseHardware) + rend_SetLighting (Lighting_on?LS_GOURAUD:LS_NONE); + else + rend_SetLighting (LS_GOURAUD); + if (!NoLightmaps) + rend_SetColorModel (CM_MONO); + else + rend_SetColorModel (CM_RGB); + // Set lighting map + if ((fp->flags & FF_LIGHTMAP) && (!StateLimited || UseMultitexture)) + { + if (GameTextures[fp->tmap].flags & TF_SATURATE) + rend_SetOverlayType (OT_NONE); + else + rend_SetOverlayType (OT_BLEND); + rend_SetOverlayMap (LightmapInfo[fp->lmi_handle].lm_handle); + } + else + rend_SetOverlayType (OT_NONE); + //Select texture type + if (!UseHardware) + { + tt = TT_LINEAR; //default to linear + for (vn=0;vnnum_verts;vn++) //select perspective if close + if (pointlist[vn]->p3_vec.z < 35) { + tt = TT_PERSPECTIVE; + break; + } + rend_SetTextureType(tt); + } + else + rend_SetTextureType(TT_PERSPECTIVE); + if (face_cc.cc_or) // Possible triangulate this face cuz its off screen somewhat + { + if (Room_light_val<1.0) + do_triangle_test=1; + if (Detail_settings.Fog_enabled && (rp->flags & RF_FOG)) + do_triangle_test=1; + if (spec_face) + do_triangle_test=1; + } + if (do_triangle_test) + { + fp->flags|=FF_TRIANGULATED; + g3_SetTriangulationTest (1); + } + // Do special fog stuff for portal faces + if (rp->flags & RF_FOG && !In_editor_mode) + { + if (fp->portal_num!=-1 && !(rp->portals[fp->portal_num].flags & PF_RENDER_FACES)) + { + drawn=1; + goto draw_fog; + } + } + //Draw the damn thing + drawn=g3_DrawPoly(fp->num_verts,pointlist,bm_handle,MAP_TYPE_BITMAP,&face_cc); + #ifdef EDITOR + if (TSearch_on) + { + if (rend_GetPixel (TSearch_x,TSearch_y)!=oldcolor) + { + TSearch_found_type=TSEARCH_FOUND_MINE; + TSearch_seg=rp-Rooms; + TSearch_face=facenum; + } + } + #endif + + // Do light saturation + if (!Render_mirror_for_room && Rendering_main_view && drawn && fp->portal_num ==-1 && ((fp->flags & FF_CORONA) || FastCoronas) && (fp->flags & FF_LIGHTMAP) && UseHardware && (GameTextures[fp->tmap].flags & TF_LIGHT)) + { + if (Num_glows_this_frametmap].flags & TF_SMOOTH_SPECULAR) + { + fp->flags|=FF_SPEC_INVISIBLE; + UpdateSpecularFace (rp,fp); + } + } + } + + //Draw scorches, if any + if (drawn && fp->flags & FF_SCORCHED && !Render_mirror_for_room) + { + if (!StateLimited) + DrawScorches(ROOMNUM(rp),facenum); + else + { + Scorches_to_render[Num_scorches_to_render]=facenum; + Num_scorches_to_render++; + } + } + + draw_fog: + if (!Render_mirror_for_room && !In_editor_mode && drawn && (rp->flags & RF_FOG) && UseHardware) + { + UpdateFogFace(rp,fp); + } + + if (do_triangle_test) + g3_SetTriangulationTest (0); + // Mark it as rendered + if (drawn) + fp->renderframe = FrameCount % 256; + //if (Render_mirror_for_room && (GameTextures[fp->tmap].flags & TF_ALPHA)) + //rend_SetZBufferWriteMask (1); + #ifdef EDITOR + if (OUTLINE_ON(OM_MINE)) //Outline the face + { + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_ALWAYS); + rend_SetFlatColor (GR_RGB(255,255,255)); + + if (UseHardware) + { + rend_SetZBias (-.1f); + for (int i=0;inum_verts;i++) + { + g3_DrawSpecialLine(pointlist[i],pointlist[(i+1)%fp->num_verts]); + + } + rend_SetZBias (0); + } + else + { + for (int i=0;inum_verts;i++) + { + g3_DrawLine(GR_RGB(255,255,255),pointlist[i],pointlist[(i+1)%fp->num_verts]); + + } + } + if ((fp->flags & FF_HAS_TRIGGER) && (fp->num_verts > 3)) { + g3_DrawLine(CUREDGE_COLOR,pointlist[0],pointlist[2]); + g3_DrawLine(CUREDGE_COLOR,pointlist[1],pointlist[3]); + } + if (fp->special_handle!=BAD_SPECIAL_FACE_INDEX) + { + g3Point p1,p2; + vector verts[MAX_VERTS_PER_FACE]; + vector center,end; + for (int t=0;tnum_verts;t++) + verts[t]=rp->verts[fp->face_verts[t]]; + vm_GetCentroid (¢er,verts,fp->num_verts); + vector subvec=SpecialFaces[fp->special_handle].spec_instance[0].bright_center-center; + vm_NormalizeVectorFast (&subvec); + end=center+subvec; + g3_RotatePoint (&p1,¢er); + g3_RotatePoint (&p2,&end); + g3_DrawLine(GR_RGB(255,255,255),&p1,&p2); + /*for (t=0;tnum_verts;t++) + { + end=rp->verts[fp->face_verts[t]]+SpecialFaces[fp->special_handle].vertnorms[t]; + g3_RotatePoint (&p1,&rp->verts[fp->face_verts[t]]); + g3_RotatePoint (&p2,&end); + g3_DrawLine(GR_RGB(255,255,255),&p1,&p2); + }*/ + } + } + if (Outline_lightmaps) + { + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_ALWAYS); + if (fp==&Curroomp->faces[Curface] && (fp->flags & FF_LIGHTMAP)) + { + ASSERT (fp->lmi_handle!=BAD_LMI_INDEX); + + lightmap_info *lmi=&LightmapInfo[fp->lmi_handle]; + ushort *src_data=(ushort *)lm_data(lmi->lm_handle); + matrix facematrix; + vector fvec=-lmi->normal; + vm_VectorToMatrix(&facematrix,&fvec,NULL,NULL); + vector rvec=facematrix.rvec*lmi->xspacing; + vector uvec=facematrix.uvec*lmi->yspacing; + vm_TransposeMatrix (&facematrix); + int w=lm_w (lmi->lm_handle); + int h=lm_h (lmi->lm_handle); + for (int i=0;iupper_left-(y*uvec)+(x*rvec); + g3_RotatePoint(&epoints[0],&evec[0]); + pointlist[0] = &epoints[0]; + evec[1]=lmi->upper_left-(y*uvec)+((x+1)*rvec); + g3_RotatePoint(&epoints[1],&evec[1]); + pointlist[1] = &epoints[1]; + evec[2]=lmi->upper_left-((y+1)*uvec)+((x+1)*rvec); + g3_RotatePoint(&epoints[2],&evec[2]); + pointlist[2] = &epoints[2]; + evec[3]=lmi->upper_left-((y+1)*uvec)+(x*rvec); + g3_RotatePoint(&epoints[3],&evec[3]); + pointlist[3] = &epoints[3]; + + if (!(src_data[y*w+x] & OPAQUE_FLAG)) + { + for (t=0;t<4;t++) + g3_DrawLine(GR_RGB(255,0,255),pointlist[t],pointlist[(t+1)%4]); + } + else + { + for (t=0;t<4;t++) + g3_DrawLine(GR_RGB(255,255,255),pointlist[t],pointlist[(t+1)%4]); + } + // Draw interpolated normals + /*if (fp->special_handle!=BAD_SPECIAL_FACE_INDEX && SpecialFaces[fp->special_handle].normal_map!=NULL) + { + vector norm,from,to,temp; + g3Point p1,p2; + vm_MakeZero (&from); + norm.x=Normal_table[SpecialFaces[fp->special_handle].normal_map[i*3+0]]; + norm.y=Normal_table[SpecialFaces[fp->special_handle].normal_map[i*3+1]]; + norm.z=Normal_table[SpecialFaces[fp->special_handle].normal_map[i*3+2]]; + vm_MatrixMulVector (&temp,&norm,&facematrix); + norm=temp; + for (t=0;t<4;t++) + from+=evec[t]; + from/=4; + to=from+norm; + g3_RotatePoint (&p1,&from); + g3_RotatePoint (&p2,&to); + g3_DrawLine(GR_RGB(255,255,255),&p1,&p2); + }*/ + + if (Search_lightmaps) + { + for (t=0;t<4;t++) + g3_ProjectPoint (&epoints[t]); + + if (point_in_poly(4,epoints,TSearch_x,TSearch_y)) + { + found_lightmap=i; + TSearch_found_type=TSEARCH_FOUND_MINE; + TSearch_seg = ROOMNUM(rp); + TSearch_face = facenum; + } + + } + } + } + } + #endif +} +static float face_depth[MAX_FACES_PER_ROOM]; +//compare function for room face sort +static int room_face_sort_func(const short *a, const short *b) +{ + float az,bz; + az = face_depth[*a]; + bz = face_depth[*b]; + if (az < bz) + return -1; + else if (az > bz) + return 1; + else + return 0; +} +//Sorts the faces of a room before rendering. Used for rendering in the editor. +//Parameters: rp - pointer to the room to be rendered +void RenderRoomSorted(room *rp) +{ + int vn,fn,i,rcount; + int render_order[MAX_FACES_PER_ROOM]; + ASSERT(rp->num_faces <= MAX_FACES_PER_ROOM); + //Rotate all the points + if (rp->wpb_index==-1) + { + rp->wpb_index=Global_buffer_index; + //Katmai enhanced rotate only in a release build, because not + //everyone has the intel compiler! +#if ( defined(RELEASE) && defined(KATMAI) ) + if(Katmai) + RotateRoomPoints (rp,rp->verts4); + else +#endif + RotateRoomPoints (rp,rp->verts); + + + Global_buffer_index+=rp->num_verts; + } + //Build list of visible (non-backfacing) faces, & compute average face depths + for (fn=rcount=0;fnnum_faces;fn++) { + face *fp = &rp->faces[fn]; + + if ((!(fp->flags & FF_VISIBLE)) || ((fp->flags & FF_NOT_FACING))) + { + fp->flags &=~(FF_NOT_FACING|FF_VISIBLE); + continue; // this guy shouldn't be rendered + } + // Clear visibility flags + fp->flags &=~(FF_VISIBLE|FF_NOT_FACING); + + if (! FaceIsRenderable(rp,fp)) + continue; //skip this face + #ifdef EDITOR + if (In_editor_mode) { + if ((Shell_render_flag & SRF_NO_NON_SHELL) && (fp->flags & FF_NOT_SHELL)) + continue; + if ((Shell_render_flag & SRF_NO_SHELL) && !(fp->flags & FF_NOT_SHELL)) + continue; + } + #endif + face_depth[fn] = 0; + for (vn=0;vnnum_verts;vn++) + face_depth[fn] += World_point_buffer[rp->wpb_index+fp->face_verts[vn]].p3_z; + face_depth[fn] /= fp->num_verts; + //initialize order list + render_order[rcount] = fn; + rcount++; + + } + //Sort the faces + qsort(render_order,rcount,sizeof(*render_order),(int (*)(const void *,const void *)) room_face_sort_func); + //Render the faces + for (i=rcount-1;i>=0;i--) + RenderFace(rp,render_order[i]); +} + +// Sets up fog if this room is fogged +void SetupRoomFog(room *rp,vector *eye,matrix *orient,int viewer_room) +{ + if( (rp->flags & RF_FOG) == 0 ) + return; + + if( !Detail_settings.Fog_enabled ) + { + // fog is disabled + Room_fog_plane_check = -1; + return; + } + + if( viewer_room == (rp-Rooms) ) + { + // viewer is in the room + vector *vec = eye; + Room_fog_plane_check = 1; + Room_fog_distance = -vm_DotProduct( &orient->fvec, vec ); + Room_fog_plane = orient->fvec; + return; + } + + // find the 'fogroom' number (we should have put it in here if we will render the room) + int found_room = -1; + for(int i = 0; i < Num_fogged_rooms_this_frame && found_room==-1; i++ ) + { + if (Fog_portal_data[i].roomnum==rp-Rooms) + { + found_room=i; + break; + } + } + + if( found_room == -1 || Fog_portal_data[found_room].close_face==NULL ) + { + // we won't be rendering this room + Room_fog_plane_check = -1; + return; + } + + // Use the closest face + face *close_face = Fog_portal_data[found_room].close_face; + Room_fog_plane_check = 0; + Room_fog_plane = close_face->normal; + Room_fog_portal_vert = rp->verts[close_face->face_verts[0]]; + Room_fog_distance = -vm_DotProduct(&Room_fog_plane,&Room_fog_portal_vert); + Room_fog_eye_distance = ( *eye * Room_fog_plane ) + Room_fog_distance; +} + +//Renders the faces in a room without worrying about sorting. Used in the game when Z-buffering is active +void RenderRoomUnsorted(room *rp) +{ + int fn; + int rcount=0; + ASSERT(rp->num_faces <= MAX_FACES_PER_ROOM); + + // Rotate points in this room if need be + if (rp->wpb_index==-1) + { + rp->wpb_index=Global_buffer_index; + + //Katmai enhanced rotate only in a release build, because not + //everyone has the intel compiler! + #if ( defined(RELEASE) && defined(KATMAI) ) + if(Katmai) + { + RotateRoomPoints (rp,rp->verts4); + } + else + #endif + { + RotateRoomPoints( rp, rp->verts ); + } + + Global_buffer_index += rp->num_verts; + } + + // Sort portal faces if this is a fogged room + if( rp->flags & RF_FOG ) + { + SetupRoomFog( rp, &Viewer_eye, &Viewer_orient, Viewer_roomnum ); + } + + //Check for visible (non-backfacing) faces, & render + for( fn = 0; fn < rp->num_faces; fn++ ) + { + face *fp = &rp->faces[fn]; + int fogged_portal=0; + + if (!(fp->flags & FF_VISIBLE) || (fp->flags & FF_NOT_FACING)) + { + if (GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR) + { + if (!Render_mirror_for_room && Detail_settings.Specular_lighting && (GameTextures[fp->tmap].flags & TF_SPECULAR) && + ((fp->special_handle!=BAD_SPECIAL_FACE_INDEX) || (rp->flags & RF_EXTERNAL))) + { + fp->flags|=FF_SPEC_INVISIBLE; + UpdateSpecularFace(rp,fp); + } + + } + fp->flags &=~(FF_NOT_FACING|FF_VISIBLE); + continue; // this guy shouldn't be rendered + } + + // Clear visibility flags + if (Render_mirror_for_room==false) + { + fp->flags &=~(FF_VISIBLE|FF_NOT_FACING); + } + else + { + if (rp==&Rooms[Mirror_room]) + { + if (rp->faces[fn].tmap==rp->faces[rp->mirror_face].tmap) + continue; // Don't render the mirror face if rendering the mirror + } + } + + if (! FaceIsRenderable(rp,fp)) + continue; //skip this face + + #ifdef EDITOR + if (In_editor_mode) + { + if ((Shell_render_flag & SRF_NO_NON_SHELL) && (fp->flags & FF_NOT_SHELL)) + continue; + if ((Shell_render_flag & SRF_NO_SHELL) && !(fp->flags & FF_NOT_SHELL)) + continue; + } + #endif + + if( fp->portal_num!=-1 && !(rp->portals[fp->portal_num].flags & PF_RENDER_FACES) && (rp->flags & RF_FOG)) + { + fogged_portal=1; + } + + if (Render_mirror_for_room==false && (fogged_portal || (GetFaceAlpha(fp,-1) & (ATF_CONSTANT+ATF_VERTEX)))) + { + // Place alpha faces into our postrender list + if (Num_postrenders < MAX_POSTRENDERS) + { + face_depth[fn] = 0.0f; + for(int vn=0;vnnum_verts;vn++) + { + face_depth[fn] += World_point_buffer[rp->wpb_index+fp->face_verts[vn]].p3_z; + } + Postrender_list[Num_postrenders].type=PRT_WALL; + Postrender_list[Num_postrenders].roomnum=rp-Rooms; + Postrender_list[Num_postrenders].facenum=fn; + Postrender_list[Num_postrenders++].z=face_depth[fn] /= fp->num_verts;; + } + } + else + { + if( !StateLimited ) + { + RenderFace(rp,fn); + } + else + { + //setup order list + State_elements[rcount].facenum = fn; + if (fp->flags & FF_LIGHTMAP) + State_elements[rcount].sort_key = (LightmapInfo[fp->lmi_handle].lm_handle*MAX_TEXTURES)+fp->tmap; + else + State_elements[rcount].sort_key=fp->tmap; + rcount++; + } + } + } + + if( StateLimited ) + { + //Sort the faces + SortStates( State_elements, rcount ); + + //Render the faces + int i; + for( i = rcount-1; i >= 0; i-- ) + { + RenderFace(rp,State_elements[i].facenum); + } + + if( !UseMultitexture ) + { + // Since we're state limited, we have to render lightmap faces completely separate + // Now render lightmap faces + rend_SetAlphaType(AT_LIGHTMAP_BLEND); + rend_SetLighting (LS_GOURAUD); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetTextureType(TT_PERSPECTIVE); + rend_SetWrapType (WT_CLAMP); + rend_SetMipState (0); + for (i=rcount-1;i>=0;i--) + { + RenderLightmapFace(rp,State_elements[i].facenum); + } + rend_SetWrapType (WT_WRAP); + rend_SetMipState (1); + } + } +} + +// Figures out a scalar value to apply to all vertices in the room +void ComputeRoomPulseLight (room *rp) +{ + if (rp->pulse_time==0 || In_editor_mode) + Room_light_val=1.0; + else + { + float ptime=rp->pulse_time*WALL_PULSE_INCREMENT; + float add_time=rp->pulse_offset*WALL_PULSE_INCREMENT; + + int int_time=(int)((Gametime+add_time)/(ptime*2)); + float left_time=(Gametime+add_time)-(int_time*ptime*2); + float norm_time=left_time/(ptime); + if (norm_time>1) + Room_light_val=1-(norm_time-1.0); + else + Room_light_val=norm_time; + } + if (!In_editor_mode) + { + if (rp->flags & RF_STROBE) + { + int val=(Gametime * 10)+(rp-Rooms); + if (val % 2) + Room_light_val=0; + } + if (rp->flags & RF_FLICKER) + { + ps_srand ((Gametime*1000)+(rp-Rooms)); + if (ps_rand()%2) + Room_light_val=0; + } + } +} + +#ifdef MACINTOSH +//what is this?? -Jeff +#pragma mark --- +#endif + +#define CORONA_DIST_CUTOFF 5.0f +//Draws a glow around a light +void RenderSingleLightGlow (int index) +{ + int bm_handle; + room *rp=&Rooms[LightGlows[index].roomnum]; + face *fp=&rp->faces[LightGlows[index].facenum]; + texture *texp=&GameTextures[fp->tmap]; + bm_handle=Fireballs[DEFAULT_CORONA_INDEX+texp->corona_type].bm_handle; + + // Get size of light + float size=LightGlows[index].size; + vector center=LightGlows[index].center; + + // Get alpha of light + vector tvec = Viewer_eye - rp->verts[fp->face_verts[0]]; + vm_NormalizeVectorFast (&tvec); + float facing_scalar=(tvec * fp->normal)*2; + if (facing_scalar<0) + return; + tvec=center-Viewer_eye; + + float dist=vm_GetMagnitudeFast (&tvec); + if (dist<(size*CORONA_DIST_CUTOFF)) + return; + if (dist<(size*(CORONA_DIST_CUTOFF+15))) + { + float dist_scalar=((dist-(size*CORONA_DIST_CUTOFF))/(size*15)); + facing_scalar*=dist_scalar; + } + + facing_scalar*=LightGlows[index].scalar; + facing_scalar=min (facing_scalar,1.0); + // Take into effect pulsing + ComputeRoomPulseLight(rp); + facing_scalar*=Room_light_val; + rend_SetAlphaValue (facing_scalar*.4*255); + + float maxc=max(texp->r,texp->g); + maxc=max(texp->b,maxc); + float r,g,b; + if (maxc>1.0) + { + r=texp->r/maxc; + g=texp->g/maxc; + b=texp->b/maxc; + } + else + { + r=texp->r; + g=texp->g; + b=texp->b; + } + if (LightGlows[index].flags & LGF_FAST) + { + rend_SetZBufferWriteMask (0); + rend_SetZBias ((-size/2)); + } + else + { + rend_SetZBufferState (0); + } + ddgr_color color=GR_RGB(r*255,g*255,b*255); + g3_DrawBitmap (¢er,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color); + if (LightGlows[index].flags & LGF_FAST) + { + rend_SetZBufferWriteMask (1); + rend_SetZBias (0); + } + else + { + rend_SetZBufferState(1); + } +} +//Draws a glow around a light +void RenderSingleLightGlow2 (int index) +{ + static int first=1; + static int normal_handle,star_handle; + int bm_handle; + room *rp=&Rooms[LightGlows[index].roomnum]; + face *fp=&rp->faces[LightGlows[index].facenum]; + texture *texp=&GameTextures[fp->tmap]; + + if (first) + { + int texhandle=FindTextureName("LongCorona"); + if (texhandle==-1) + star_handle=0; + else + star_handle=GetTextureBitmap (texhandle,0); + first=0; + } + rend_SetAlphaValue (.4*255); + float maxc=max(texp->r,texp->g); + maxc=max(texp->b,maxc); + float r,g,b; + if (maxc>1.0) + { + r=texp->r/maxc; + g=texp->g/maxc; + b=texp->b/maxc; + } + else + { + r=texp->r; + g=texp->g; + b=texp->b; + } + rend_SetLighting (LS_GOURAUD); + + // Get size of light + float size=LightGlows[index].size; + vector center=LightGlows[index].center; + vector corona_pos=center+(fp->normal*size); + bm_handle=star_handle; + matrix mat,rot_mat; + vector fvec,uvec,temp_vec,rvec; + temp_vec=-fp->normal; + + vm_VectorToMatrix(&mat,NULL,&temp_vec,NULL); + + // Rotate view vector into billboard space + fvec=Viewer_eye-corona_pos; + vm_NormalizeVectorFast (&fvec); + temp_vec=fvec*mat; + fvec=temp_vec; + vm_NormalizeVectorFast (&fvec); + rvec.x=fvec.z; + rvec.y=0; + rvec.z=-fvec.x; + uvec.y=1; + uvec.x=0; + uvec.z=0; + vm_VectorToMatrix (&rot_mat,NULL,&uvec,&rvec); + vm_TransposeMatrix (&mat); + temp_vec=rot_mat.rvec * mat; + rot_mat.rvec=temp_vec; + temp_vec=rot_mat.uvec * mat; + rot_mat.uvec=temp_vec; + temp_vec=rot_mat.fvec * mat; + rot_mat.fvec=temp_vec; + vector world_vecs[4]; + g3Point pnts[4],*pntlist[4]; + world_vecs[0]=corona_pos-(size*rot_mat.rvec); + world_vecs[0]+=(size*rot_mat.uvec); + world_vecs[1]=corona_pos+(size*rot_mat.rvec); + world_vecs[1]+=(size*rot_mat.uvec); + world_vecs[2]=corona_pos+(size*rot_mat.rvec); + world_vecs[2]-=(size*rot_mat.uvec); + world_vecs[3]=corona_pos-(size*rot_mat.rvec); + world_vecs[3]-=(size*rot_mat.uvec); + for (int i=0;i<4;i++) + { + g3_RotatePoint (&pnts[i],&world_vecs[i]); + pnts[i].p3_flags|=PF_UV|PF_RGBA; + pnts[i].p3_r=r; + pnts[i].p3_g=g; + pnts[i].p3_b=b; + pntlist[i]=&pnts[i]; + } + pnts[0].p3_u=0; + pnts[0].p3_v=0; + pnts[1].p3_u=1; + pnts[1].p3_v=0; + pnts[2].p3_u=1; + pnts[2].p3_v=1; + pnts[3].p3_u=0; + pnts[3].p3_v=1; + g3_DrawPoly (4,pntlist,bm_handle); + rend_SetZBufferWriteMask (1); +} +// Figures out if we can see the center of a light face and adds it to our globa list +void CheckLightGlowsForRoom (room *rp) +{ + int i; + for (i=0;ifaces[LightGlowsThisFrame[i].facenum]; + vector verts[MAX_VERTS_PER_FACE]; + vector center; + for (int t=0;tnum_verts;t++) + verts[t]=rp->verts[fp->face_verts[t]]; + float size=sqrt(vm_GetCentroid (¢er,verts,fp->num_verts)); + size*=2; + center+=(fp->normal/4); + if (vm_VectorDistanceQuick(¢er,&Viewer_eye) < (size*CORONA_DIST_CUTOFF)) + continue; + // Check if we can see this light + fvi_info hit_info; + fvi_query fq; + // shoot a ray from the light position to the current vertex + if (FastCoronas) + { + if (rp->flags & RF_EXTERNAL) + { + SetGlowStatus (rp-Rooms,LightGlowsThisFrame[i].facenum,¢er,size,FastCoronas); + continue; + } + vector subvec=Viewer_eye-center; + vm_NormalizeVectorFast (&subvec); + subvec*=size; + subvec+=center; + fq.p0=¢er; + fq.p1=&subvec; + fq.startroom=rp-Rooms; + } + else + { + fq.p0=&Viewer_eye; + fq.p1=¢er; + fq.startroom=Viewer_object->roomnum; + } + + fq.rad=0.0f; + fq.flags=FQ_CHECK_OBJS|FQ_NO_RELINK|FQ_IGNORE_WEAPONS|FQ_ROBOTS_AS_SPHERE|FQ_PLAYERS_AS_SPHERE; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + int fate = fvi_FindIntersection(&fq,&hit_info); + if (fate!=HIT_NONE) + continue; + SetGlowStatus (rp-Rooms,LightGlowsThisFrame[i].facenum,¢er,size,FastCoronas); + } +} +// Called before a frame starts to render - sets all of our light glows to decreasing +void PreUpdateAllLightGlows () +{ + int i,count; + for (i=0,count=0;i1) + LightGlows[i].scalar=1; + } + else + { + LightGlows[i].scalar-=(Frametime*4); + if (LightGlows[i].scalar<0) + { + LightGlows[i].scalar=0; + LightGlows[i].flags &=~LGF_USED; + Num_glows--; + count--; + } + } + } + } + + ASSERT (Num_glows>=0); +} +// Recursive function that mirrored rooms use +void BuildMirroredRoomListSub(int start_room_num,clip_wnd *wnd) +{ + room *rp = &Rooms[start_room_num]; + g3Point portal_points[MAX_VERTS_PER_FACE]; + int i,t; + if (!Mirrored_room_checked[start_room_num]) + { + Mirrored_room_list[Num_mirrored_rooms++] = start_room_num; + } + Mirrored_room_checked[start_room_num]=1; + + //If this room is a closed (non-seethrough) door, don't check any of its portals, + //...UNLESS this is the first room we're looking at (meaning the viewer is in this room) + if ((rp->flags & RF_DOOR) && (DoorwayGetPosition(rp) == 0.0) && !(Doors[rp->doorway_data->doornum].flags & DF_SEETHROUGH)) + return; + room *mirror_rp=&Rooms[Mirror_room]; + vector *mirror_vec=&mirror_rp->verts[mirror_rp->faces[mirror_rp->mirror_face].face_verts[0]]; + face *mirror_fp=&mirror_rp->faces[mirror_rp->mirror_face]; + vector *mirror_norm=&mirror_fp->normal; + // This is how far the mirror face is from the normalized plane + float mirror_dist=-(mirror_vec->x*mirror_norm->x+mirror_vec->y*mirror_norm->y+mirror_vec->z*mirror_norm->z); + //Check all the portals for this room + for (t=0;tnum_portals;t++) + { + portal *pp = &rp->portals[t]; + int croom = pp->croom; + ASSERT (croom>=0); + // If we are an external room portalizing into another external room, then skip! + if ((rp->flags & RF_EXTERNAL) && (Rooms[croom].flags & RF_EXTERNAL)) + continue; + //Check if we can see through this portal, and if not, skip it + if (! RenderPastPortal(rp,pp)) + continue; + // If this portal has been visited, skip it + if (Mirrored_room_checked[croom]) + continue; + //Deal with external portals differently + int external_door_hack=0; + if (rp->flags & RF_EXTERNAL && Rooms[croom].flags & RF_DOOR) + external_door_hack=1; + //Get pointer to this portal's face + face *fp = &rp->faces[pp->portal_face]; + //See if portal is facing toward us + if (! external_door_hack) + { + vector temp_vec; + vector *vec=&rp->verts[fp->face_verts[0]]; + float dist_from_mirror = vec->x*mirror_norm->x+vec->y*mirror_norm->y+vec->z*mirror_norm->z+mirror_dist; + // dest_vecs contains the point on the other side of the mirror (ie the reflected point) + temp_vec=*vec-(*mirror_norm*(dist_from_mirror*2)); + vector incident_norm; + ReflectRay (&incident_norm,&fp->normal,&mirror_fp->normal); + + vector tvec = Viewer_eye - temp_vec; + if ((tvec * incident_norm) <= 0) + continue; // not facing + } + + g3Codes cc; + cc.cc_or = 0; cc.cc_and = 0xff; + int nv = fp->num_verts; + //Code the face points + for (i=0;iverts[fp->face_verts[i]]; + float dist_from_mirror = vec->x*mirror_norm->x+vec->y*mirror_norm->y+vec->z*mirror_norm->z+mirror_dist; + // dest_vecs contains the point on the other side of the mirror (ie the reflected point) + temp_vec=*vec-(*mirror_norm*(dist_from_mirror*2)); + g3_RotatePoint (&portal_points[i],&temp_vec); + + ubyte c = portal_points[i].p3_codes; + cc.cc_and &= c; + cc.cc_or |= c; + } + //If points are on screen, see if they're in the clip window + if (cc.cc_and == 0 || external_door_hack) + { + bool clipped = 0; + g3Point *pointlist[MAX_VERTS_PER_FACE],**pl = pointlist; + for (i=0;ip3_sx, y = pl[i]->p3_sy; + if (x < new_wnd.left) + new_wnd.left = x; + if (x > new_wnd.right) + new_wnd.right = x; + if (y < new_wnd.top) + new_wnd.top = y; + if (y > new_wnd.bot) + new_wnd.bot = y; + } + + //Combine the two windows + new_wnd.left = __max(wnd->left,new_wnd.left); + new_wnd.right = __min(wnd->right,new_wnd.right); + new_wnd.top = __max(wnd->top,new_wnd.top); + new_wnd.bot = __min(wnd->bot,new_wnd.bot); + if (clipped) + { //Free up temp points + g3_FreeTempPoints(pl,nv); + clipped = 0; + } + BuildMirroredRoomListSub(croom,&new_wnd); + } + if (clipped) //Free up temp points + g3_FreeTempPoints(pl,nv); + } + } +} +// Goes through and builds a list of rooms that can be seen from a mirror +void BuildMirroredRoomList () +{ + room *rp=&Rooms[Mirror_room]; + clip_wnd wnd; + Num_mirrored_rooms=0; + memset (Mirrored_room_checked,0,MAX_ROOMS); + Mirrored_room_checked[Mirror_room]=1; + Mirrored_room_list[Num_mirrored_rooms++]=Mirror_room; + + //Initial clip window is whole screen + wnd.left = wnd.top = 0.0; + wnd.right = Render_width; + wnd.bot = Render_height; + g3Point portal_points[MAX_VERTS_PER_FACE],temp_points[MAX_VERTS_PER_FACE*2]; + g3Point *pointlist[MAX_VERTS_PER_FACE*2],**pl = pointlist; + room *mirror_rp=&Rooms[Mirror_room]; + vector *mirror_vec=&mirror_rp->verts[mirror_rp->faces[mirror_rp->mirror_face].face_verts[0]]; + face *mirror_fp=&mirror_rp->faces[mirror_rp->mirror_face]; + vector *mirror_norm=&mirror_fp->normal; + // This is how far the mirror face is from the normalized plane + float mirror_dist=-(mirror_vec->x*mirror_norm->x+mirror_vec->y*mirror_norm->y+mirror_vec->z*mirror_norm->z); + int total_points=0; + for (int t=0;tnum_mirror_faces;t++) + { + face *fp=&rp->faces[rp->mirror_faces_list[t]]; + int i; + ASSERT (total_points+fp->num_verts<=MAX_VERTS_PER_FACE*2); + g3Codes cc; + cc.cc_and=0xff; + cc.cc_or=0; + int nv=fp->num_verts; + int clipped=0; + + for (i=0;inum_verts;i++) + { + vector temp_vec; + vector *vec=&rp->verts[fp->face_verts[i]]; + float dist_from_mirror = vec->x*mirror_norm->x+vec->y*mirror_norm->y+vec->z*mirror_norm->z+mirror_dist; + // dest_vecs contains the point on the other side of the mirror (ie the reflected point) + temp_vec=*vec-(*mirror_norm*(dist_from_mirror*2)); + g3_RotatePoint (&portal_points[i],&temp_vec); + cc.cc_and&=portal_points[i].p3_codes; + cc.cc_or|=portal_points[i].p3_codes; + pointlist[i]=&portal_points[i]; + } + + // Clipped away + if (cc.cc_and) + continue; + if (cc.cc_or) + { + // Must clip + pl=g3_ClipPolygon (pl,&nv,&cc); + + if (cc.cc_and) + { + g3_FreeTempPoints (pl,nv); + continue; + } + else + { + for (i=0;i new_wnd.right) + new_wnd.right = x; + if (y < new_wnd.top) + new_wnd.top = y; + if (y > new_wnd.bot) + new_wnd.bot = y; + } + new_wnd.left = __max(wnd.left,new_wnd.left); + new_wnd.right = __min(wnd.right,new_wnd.right); + new_wnd.top = __max(wnd.top,new_wnd.top); + new_wnd.bot = __min(wnd.bot,new_wnd.bot); + /*rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (255); + rend_SetFlatColor (GR_RGB(255,255,255)); + + rend_DrawLine (new_wnd.left,new_wnd.top,new_wnd.right,new_wnd.top); + rend_DrawLine (new_wnd.right,new_wnd.top,new_wnd.right,new_wnd.bot); + rend_DrawLine (new_wnd.right,new_wnd.bot,new_wnd.left,new_wnd.bot); + rend_DrawLine (new_wnd.left,new_wnd.bot,new_wnd.left,new_wnd.top);*/ + + BuildMirroredRoomListSub(Mirror_room,&new_wnd); +} +vector mirror_dest_vecs[MAX_VERTS_PER_ROOM]; +g3Point mirror_save_points[MAX_VERTS_PER_ROOM]; +// Renders a mirror flipped about the mirrored plane +void RenderMirroredRoom (room *rp) +{ + int i; + #if ( defined(RELEASE) && defined(KATMAI) ) + vector4 kat_vecs[MAX_VERTS_PER_ROOM]; + #endif + ushort save_flags[MAX_FACES_PER_ROOM]; + bool restore_index=true; + int save_index=Global_buffer_index; + + // Save old rotated points + if (rp->wpb_index==-1) + { + restore_index=false; + rp->wpb_index=Global_buffer_index; + } + else + { + for (i=0;inum_verts;i++) + mirror_save_points[i]=World_point_buffer[rp->wpb_index+i]; + } + + // Find facing faces for this mirror + face *fp=&rp->faces[0]; + for (i=0;inum_faces;i++,fp++) + { + save_flags[i]=fp->flags; + fp->flags &=~FF_NOT_FACING; + fp->flags |=FF_VISIBLE; + } + room *mirror_rp=&Rooms[Mirror_room]; + vector *mirror_vec=&mirror_rp->verts[mirror_rp->faces[mirror_rp->mirror_face].face_verts[0]]; + face *mirror_fp=&mirror_rp->faces[mirror_rp->mirror_face]; + vector *norm=&mirror_fp->normal; + // This is how far the mirror face is from the normalized plane + float mirror_dist=-(mirror_vec->x*norm->x+mirror_vec->y*norm->y+mirror_vec->z*norm->z); + + for (i=0;inum_verts;i++) + { + vector *vec=&rp->verts[i]; + float dist_from_mirror = vec->x*norm->x+vec->y*norm->y+vec->z*norm->z+mirror_dist; + // dest_vecs contains the point on the other side of the mirror (ie the reflected point) + mirror_dest_vecs[i]=*vec-(*norm*(dist_from_mirror*2)); + #if ( defined(RELEASE) && defined(KATMAI) ) + if(Katmai) + { + kat_vecs[i].x=mirror_dest_vecs[i].x; + kat_vecs[i].y=mirror_dest_vecs[i].y; + kat_vecs[i].z=mirror_dest_vecs[i].z; + } + #endif + } + // Rotate our mirror points + vector revnorm=-*norm; + g3_SetCustomClipPlane (1,mirror_vec,&revnorm); + + + + //Katmai enhanced rotate only in a release build, because not + //everyone has the intel compiler! +#if ( defined(RELEASE) && defined(KATMAI) ) + if(Katmai) + RotateRoomPoints (rp,kat_vecs); + else +#endif + RotateRoomPoints (rp,mirror_dest_vecs); + + + + // Mark facing faces + int save_frame=Facing_visited[rp-Rooms]; + Facing_visited[rp-Rooms]=0; + + MarkFacingFaces (rp-Rooms,mirror_dest_vecs); + Facing_visited[rp-Rooms]=save_frame; + // Render the mirror room + rend_SetColorModel (CM_MONO); + rend_SetLighting (LS_GOURAUD); + rend_SetWrapType (WT_WRAP); + RenderRoomUnsorted (rp); + if (restore_index==false) + { + rp->wpb_index=-1; + } + else + { + for (i=0;inum_verts;i++) + World_point_buffer[rp->wpb_index+i]=mirror_save_points[i]; + } + fp=&rp->faces[0]; + for (i=0;inum_faces;i++,fp++) + fp->flags=save_flags[i]; + + RenderRoomObjects(rp); + rp->last_render_time=Gametime; + g3_SetCustomClipPlane (0,NULL,NULL); +} +// Renders a specific room. If pos_offset is not NULL, adds that offset to each of the +// rooms vertices +void RenderRoom(room *rp) +{ + Num_glows_this_frame=0; + + //Set up rendering states + rend_SetColorModel(CM_MONO); + rend_SetLighting(LS_GOURAUD); + rend_SetWrapType(WT_WRAP); + if( rp->used == 0 ) + { + Int3(); // Trying to draw a room that isn't in use! + return; + } + + // Figure out pulse lighting for room + ComputeRoomPulseLight(rp); + + // Mark it visible for automap + AutomapVisMap[rp-Rooms] = 1; + + #ifdef EDITOR + if (!UseHardware) + { + RenderRoomSorted(rp); + } + else + #endif + { + //NOTE LINK TO ABOVE ELSE + RenderRoomUnsorted(rp); + } + + rp->last_render_time = Gametime; + rp->flags &= ~RF_MIRROR_VISIBLE; + + CheckLightGlowsForRoom(rp); + + if( Num_scorches_to_render > 0 ) + { + RenderScorchesForRoom(rp); + Num_scorches_to_render=0; + } + + if( Num_specular_faces_to_render > 0 ) + { + RenderSpecularFacesFlat(rp); + Num_specular_faces_to_render=0; + Num_real_specular_faces_to_render=0; + } + + if( Num_fog_faces_to_render > 0 ) + { + RenderFogFaces(rp); + Num_fog_faces_to_render=0; + } +} + +#define MAX_OBJECTS_PER_ROOM 2000 +typedef struct +{ + int vis_effect; + int objnum; + float dist; +} obj_sort_item; +obj_sort_item obj_sort_list[MAX_OBJECTS_PER_ROOM]; +//Compare function for room face sort +static int obj_sort_func(const obj_sort_item *a, const obj_sort_item *b) +{ + if (a->dist < b->dist) + return -1; + else if (a->dist > b->dist) + return 1; + else + return 0; +} +inline void IsRoomDynamicValid (room *rp,int x,int y,int z,float *r,float *g,float *b) +{ + int w=rp->volume_width; + int h=rp->volume_height; + int d=rp->volume_depth; + + ubyte color=rp->volume_lights[(z*w*h)+(y*w)+x]; + + *r=(float)((color>>5)/7.0); + *g=(float)((color>>2) & 0x07)/7.0; + *b=(float)((float)(color & 0x03)/3.0); +} +// Gets the dynamic light value for this position +void GetRoomDynamicScalar (vector *pos,room *rp,float *r,float *g,float *b) +{ + float front_values_r[10]; + float back_values_r[10]; + float front_values_g[10]; + float back_values_g[10]; + float front_values_b[10]; + float back_values_b[10]; + if (!rp->volume_lights) + { + *r=1; + *g=1; + *b=1; + return; + } + float fl_x=(pos->x-rp->min_xyz.x)/VOLUME_SPACING; + float fl_y=(pos->y-rp->min_xyz.y)/VOLUME_SPACING; + float fl_z=(pos->z-rp->min_xyz.z)/VOLUME_SPACING; + fl_x=max(fl_x,0); + fl_y=max(fl_y,0); + fl_z=max(fl_z,0); + fl_x=min(fl_x,rp->volume_width-1); + fl_y=min(fl_y,rp->volume_height-1); + fl_z=min(fl_z,rp->volume_depth-1); + int int_x=fl_x; + int int_y=fl_y; + int int_z=fl_z; + fl_x-=int_x; + fl_y-=int_y; + fl_z-=int_z; + int next_x=int_x+1; + int next_y=int_y+1; + int next_z=int_z+1; + next_x=min(rp->volume_width-1,next_x); + next_y=min(rp->volume_height-1,next_y); + next_z=min(rp->volume_depth-1,next_z); + + float left_norm_r,left_norm_g,left_norm_b; + float right_norm_r,right_norm_g,right_norm_b; + float front_norm_r,front_norm_g,front_norm_b; + float back_norm_r,back_norm_g,back_norm_b; + + IsRoomDynamicValid(rp,int_x,int_y,int_z,&front_values_r[0],&front_values_g[0],&front_values_b[0]); + IsRoomDynamicValid(rp,int_x,next_y,int_z,&front_values_r[1],&front_values_g[1],&front_values_b[1]); + IsRoomDynamicValid(rp,next_x,next_y,int_z,&front_values_r[2],&front_values_g[2],&front_values_b[2]); + IsRoomDynamicValid(rp,next_x,int_y,int_z,&front_values_r[3],&front_values_g[3],&front_values_b[3]); + IsRoomDynamicValid(rp,int_x,int_y,next_z,&back_values_r[0],&back_values_g[0],&back_values_b[0]); + IsRoomDynamicValid(rp,int_x,next_y,next_z,&back_values_r[1],&back_values_g[1],&back_values_b[1]); + IsRoomDynamicValid(rp,next_x,next_y,next_z,&back_values_r[2],&back_values_g[2],&back_values_b[2]); + IsRoomDynamicValid(rp,next_x,int_y,next_z,&back_values_r[3],&back_values_g[3],&back_values_b[3]); + // Do front edge + int left_out=0; + int right_out=0; + // Left edge + left_norm_r=((1-fl_y)*front_values_r[0])+(fl_y*front_values_r[1]); + left_norm_g=((1-fl_y)*front_values_g[0])+(fl_y*front_values_g[1]); + left_norm_b=((1-fl_y)*front_values_b[0])+(fl_y*front_values_b[1]); + + // Right edge + right_norm_r=((1-fl_y)*front_values_r[3])+(fl_y*front_values_r[2]); + right_norm_g=((1-fl_y)*front_values_g[3])+(fl_y*front_values_g[2]); + right_norm_b=((1-fl_y)*front_values_b[3])+(fl_y*front_values_b[2]); + // Figure out front edge + front_norm_r=((1-fl_x)*left_norm_r)+(fl_x*right_norm_r); + front_norm_g=((1-fl_x)*left_norm_g)+(fl_x*right_norm_g); + front_norm_b=((1-fl_x)*left_norm_b)+(fl_x*right_norm_b); + + // Do back edge + left_norm_r=((1-fl_y)*back_values_r[0])+(fl_y*back_values_r[1]); + left_norm_g=((1-fl_y)*back_values_g[0])+(fl_y*back_values_g[1]); + left_norm_b=((1-fl_y)*back_values_b[0])+(fl_y*back_values_b[1]); + + right_norm_r=((1-fl_y)*back_values_r[3])+(fl_y*back_values_r[2]); + right_norm_g=((1-fl_y)*back_values_g[3])+(fl_y*back_values_g[2]); + right_norm_b=((1-fl_y)*back_values_b[3])+(fl_y*back_values_b[2]); + back_norm_r=((1-fl_x)*left_norm_r)+(fl_x*right_norm_r); + back_norm_g=((1-fl_x)*left_norm_g)+(fl_x*right_norm_g); + back_norm_b=((1-fl_x)*left_norm_b)+(fl_x*right_norm_b); + *r=((1-fl_z)*front_norm_r)+(fl_z*back_norm_r); + *g=((1-fl_z)*front_norm_g)+(fl_z*back_norm_g); + *b=((1-fl_z)*front_norm_b)+(fl_z*back_norm_b); + // Factor in flickering + ComputeRoomPulseLight(rp); + (*r)*=Room_light_val; + (*g)*=Room_light_val; + (*b)*=Room_light_val; +} +ubyte Trick_type=0; +//Render the objects and viseffects in a room. Do a simple sort +void RenderRoomObjects(room *rp) +{ + int n_objs=0,objnum,i,visnum; + float zdist; + if (!Render_mirror_for_room && UseHardware) + return; // This function only works for mirrors now + + //Add objects to sort list + for (objnum=rp->objects;(objnum!=-1) && (n_objs < MAX_OBJECTS_PER_ROOM);objnum=Objects[objnum].next) + { + ASSERT (objnum!=Objects[objnum].next); + object *obj = &Objects[objnum]; + + if (obj->render_type == RT_NONE) + continue; + if (obj==Viewer_object && !Render_mirror_for_room) + continue; + float size = obj->size; + // Special case weapons with streamers + if (obj->type==OBJ_WEAPON && (Weapons[obj->id].flags & WF_STREAMER)) + size=Weapons[obj->id].phys_info.velocity.z; + // Check if object is trivially rejected + bool isVisible = IsPointVisible( &obj->pos, size, &zdist ) ? true : false; + if (Render_mirror_for_room || (obj->type==OBJ_WEAPON && Weapons[obj->id].flags & WF_ELECTRICAL) || isVisible ) + { + obj_sort_list[n_objs].vis_effect=0; + obj_sort_list[n_objs].objnum = objnum; + obj_sort_list[n_objs].dist = zdist; + n_objs++; + } + } + //Add vis effects to sort list + for (visnum=rp->vis_effects;(visnum!=-1) && (n_objs < MAX_OBJECTS_PER_ROOM);visnum=VisEffects[visnum].next) + { + ASSERT (visnum!=VisEffects[visnum].next); + if (VisEffects[visnum].type==VIS_NONE || VisEffects[visnum].flags & VF_DEAD) + continue; + + bool pointIsVisible = IsPointVisible (&VisEffects[visnum].pos,VisEffects[visnum].size,&zdist) ? true : false; + if (Render_mirror_for_room || pointIsVisible ) + { + obj_sort_list[n_objs].vis_effect=1; + obj_sort_list[n_objs].objnum = visnum; + obj_sort_list[n_objs].dist = zdist; + n_objs++; + } + } + ASSERT(objnum == -1); //if not -1, ran out of space in render_order[] + ASSERT(visnum == -1); //if not -1, ran out of space in render_order[] + //Sort the objects + qsort(obj_sort_list,n_objs,sizeof(*obj_sort_list),(int (*)(const void*,const void*))obj_sort_func); + #ifdef _DEBUG + bool save_polymodel_outline_mode = Polymodel_outline_mode; + Polymodel_outline_mode = OUTLINE_ON(OM_OBJECTS); + #endif + //Render the objects + if (UseHardware && Render_mirror_for_room) + { + // Render the mirror stuff if present + room *mirror_rp=&Rooms[Mirror_room]; + vector *mirror_vec=&mirror_rp->verts[mirror_rp->faces[mirror_rp->mirror_face].face_verts[0]]; + face *mirror_fp=&mirror_rp->faces[mirror_rp->mirror_face]; + vector *norm=&mirror_fp->normal; + // This is how far the mirror face is from the normalized plane + float mirror_dist=-(mirror_vec->x*norm->x+mirror_vec->y*norm->y+mirror_vec->z*norm->z); + // Setup mirror matrix + + vector negz_vec={0,0,-1}; + + matrix mirror_matrix,inv_mirror_matrix,dest_matrix,negz_matrix; + vm_VectorToMatrix (&mirror_matrix,norm,NULL,NULL); + inv_mirror_matrix=mirror_matrix; + vm_TransposeMatrix (&inv_mirror_matrix); + vm_VectorToMatrix (&negz_matrix,&negz_vec,NULL,NULL); + negz_matrix.rvec*=-1; + + for (i=n_objs-1;i>=0;i--) + { + objnum = obj_sort_list[i].objnum; + if (obj_sort_list[i].vis_effect) + { + vis_effect *vis=&VisEffects[objnum]; + vector save_vec=vis->pos; + vector save_end_vec=vis->end_pos; + vector *vec=&vis->pos; + vector *end_vec=&vis->end_pos; + float dist_from_mirror = vec->x*norm->x+vec->y*norm->y+vec->z*norm->z+mirror_dist; + // dest_vecs contains the point on the other side of the mirror (ie the reflected point) + vis->pos=*vec-(*norm*(dist_from_mirror*2)); + dist_from_mirror = end_vec->x*norm->x+end_vec->y*norm->y+end_vec->z*norm->z+mirror_dist; + // dest_vecs contains the point on the other side of the mirror (ie the reflected point) + vis->end_pos=*end_vec-(*norm*(dist_from_mirror*2)); + + DrawVisEffect (vis); + vis->pos=save_vec; + vis->end_pos=save_end_vec; + } + else + { + object *objp = &Objects[objnum]; + + vector save_vec=objp->pos; + matrix save_orient=objp->orient; + matrix temp_mat; + + vector *vec=&objp->pos; + float dist_from_mirror = vec->x*norm->x+vec->y*norm->y+vec->z*norm->z+mirror_dist; + // dest_vecs contains the point on the other side of the mirror (ie the reflected point) + objp->pos=*vec-(*norm*(dist_from_mirror*2)); + // Check for rear view + if (objp==Viewer_object && Viewer_object == Player_object && (Players[Player_num].flags & PLAYER_FLAGS_REARVIEW)) + { + objp->orient.fvec = -objp->orient.fvec; + objp->orient.rvec = -objp->orient.rvec; + } + // Get new orientation + temp_mat=mirror_matrix * negz_matrix * inv_mirror_matrix; + dest_matrix.rvec=objp->orient.rvec*temp_mat; + dest_matrix.uvec=objp->orient.uvec*temp_mat; + dest_matrix.fvec=objp->orient.fvec*temp_mat; + objp->orient=dest_matrix; + bool save_render=false; + if (objp->flags & OF_SAFE_TO_RENDER) + save_render=true; + objp->flags|=OF_SAFE_TO_RENDER; + + + RenderObject(objp); + if (save_render) + objp->flags|=OF_SAFE_TO_RENDER; + else + objp->flags&=~OF_SAFE_TO_RENDER; + objp->pos=save_vec; + objp->orient=save_orient; + } + } + return; + } + for (i=n_objs-1;i>=0;i--) + { + objnum = obj_sort_list[i].objnum; + if (obj_sort_list[i].vis_effect) + { + DrawVisEffect (&VisEffects[objnum]); + } + else + { + object *objp = &Objects[objnum]; + if (objp==Viewer_object) + continue; + RenderObject(objp); + } + } + #ifdef _DEBUG + Polymodel_outline_mode = save_polymodel_outline_mode; + #endif +} +// Either renders the objects in a room, or stuffs them into our postrender list +void CheckToRenderMineObjects (int roomnum) +{ + + if (UseHardware) + { + int index; + float zdist; + if (Render_mirror_for_room) + return; + for (index=Rooms[roomnum].objects;index!=-1;index=Objects[index].next) + { + object *obj=&Objects[index]; + if (Objects[index].render_type == RT_NONE) + continue; + if (obj==Viewer_object) + continue; + // Don't draw piggybacked objects + if (Viewer_object->type==OBJ_OBSERVER && index==Players[Viewer_object->id].piggy_objnum) + continue; + float size = Objects[index].size; + // Special case weapons with streamers + if (Objects[index].type==OBJ_WEAPON && (Weapons[Objects[index].id].flags & WF_STREAMER)) + size=Weapons[Objects[index].id].phys_info.velocity.z; + // Check if object is trivially rejected + int visible = IsPointVisible (&obj->pos,size,&zdist); //calculate zdist + if ((obj->type==OBJ_WEAPON && Weapons[obj->id].flags & WF_ELECTRICAL) || visible) + { + if (Num_postrenders < MAX_POSTRENDERS) + { + Postrender_list[Num_postrenders].type=PRT_OBJECT; + Postrender_list[Num_postrenders].z=zdist; + Postrender_list[Num_postrenders++].objnum=index; + } + } + } + // Now do viseffects + for (index=Rooms[roomnum].vis_effects;index!=-1;index=VisEffects[index].next) + { + if (VisEffects[index].type==VIS_NONE || VisEffects[index].flags & VF_DEAD) + continue; + + if (IsPointVisible (&VisEffects[index].pos,VisEffects[index].size,&zdist)) + { + if (Num_postrenders < MAX_POSTRENDERS) + { + Postrender_list[Num_postrenders].type=PRT_VISEFFECT; + Postrender_list[Num_postrenders].z=zdist; + Postrender_list[Num_postrenders++].visnum=index; + } + } + } + } + else + RenderRoomObjects (&Rooms[roomnum]); +} +// Renders all the mirrored rooms for this frame +void RenderMirrorRooms () +{ + int i; + if (!UseHardware || !Detail_settings.Mirrored_surfaces) + return; + if (Num_mirror_rooms==0) + return; + for (i=0;iflags &=~RF_MIRROR_VISIBLE; + // Make sure its really ok to render this mirrored room + if (rp->mirror_face!=-1) + do_mirror_face=true; + if (rp->mirror_face>=rp->num_faces) + do_mirror_face=false; + if (do_mirror_face) + { + if (!(GameTextures[rp->faces[rp->mirror_face].tmap].flags & TF_ALPHA)) + do_mirror_face=false; + } + if (do_mirror_face) + { + int on_screen=0; + // See if any of the faces that share the mirror texture are on the screen + for (int k=0;knum_mirror_faces && on_screen==0;k++) + { + face *fp=&rp->faces[rp->mirror_faces_list[k]]; + + // See if this face is on screen + int anded=0xff; + g3Point pnt; + for (int t=0;tnum_verts;t++) + anded&=g3_RotatePoint(&pnt,&rp->verts[fp->face_verts[t]]); + if (!anded) + on_screen=1; + } + if (!on_screen) + do_mirror_face=false; + } + if (do_mirror_face) // This room has a mirror...render it first + { + Render_mirror_for_room=true; + Mirror_room=rp-Rooms; + + BuildMirroredRoomList (); + for (int t=Num_mirrored_rooms-1;t>=0;t--) + RenderMirroredRoom (&Rooms[Mirrored_room_list[t]]); + Render_mirror_for_room=false; + } + } + // Do z buffer trick + rend_ClearZBuffer (); + rend_SetZBufferWriteMask (1); + rend_SetZBufferState (1); + // Draw mirror faces now + for (i=0;ifaces[rp->mirror_face]; + g3Point save_points[MAX_VERTS_PER_FACE]; + int save_index; + + // Now do the same for all faces in this room that share that texture of the mirror + for (int k=0;knum_faces;k++) + { + face *this_fp=&rp->faces[k]; + if (this_fp->tmap==fp->tmap) + { + int t; + + save_index=rp->wpb_index; + for (t=0;tnum_verts;t++) + save_points[t]=World_point_buffer[fp->face_verts[t]]; + DrawPostrenderFace(Mirror_rooms[i],rp->mirror_face,false); + + rp->wpb_index=save_index; + for (t=0;tnum_verts;t++) + World_point_buffer[fp->face_verts[t]]=save_points[t]; + } + } + } + +} +// Renders a room in just outline form +void RenderRoomOutline(room *rp) +{ + int fn; + ddgr_color back_line_color,face_line_color; + back_line_color = GR_RGB(100,100,100); + face_line_color = GR_RGB(255,255,255); + for (fn=0;fnnum_faces;fn++) + { + face *fp = &rp->faces[fn]; + g3Point p0,p1; + ubyte c0,c1; + int v; + ddgr_color color; + for (v=0;vnum_verts;v++) + { + c0 = g3_RotatePoint(&p0,&rp->verts[fp->face_verts[v]]); + c1 = g3_RotatePoint(&p1,&rp->verts[fp->face_verts[(v+1)%fp->num_verts]]); + if ((!(fp->flags & FF_VISIBLE)) || ((fp->flags & FF_NOT_FACING))) + { + //wouldn't normally be rendered + color = back_line_color; + }else + { + color = face_line_color; + } + if (! (c0 & c1)) { //both not off screen? + //Draw current edge in green + g3_DrawLine(color,&p0,&p1); + } + } + } +} +//Draws the mine, starting at a the specified room +//The rendering surface must be set up, and g3_StartFrame() must have been called +//Parameters: viewer_roomnum - what room the viewer is in +// flag_automap - if true, flag segments as visited when rendered +// called_from_terrain - set if calling this routine from the terrain renderer +void RenderMine(int viewer_roomnum,int flag_automap,int called_from_terrain) +{ +#ifdef EDITOR + In_editor_mode = (GetFunctionMode() == EDITOR_MODE) ; +#endif + // check to see if we should render windows + if (No_render_windows_hack==-1) + { + if (FindArg ("-NoRenderWindows")) + No_render_windows_hack=1; + else + No_render_windows_hack=0; + } + //Get the viewer eye so functions down the line can look at it + g3_GetViewPosition(&Viewer_eye); + g3_GetUnscaledMatrix (&Viewer_orient); + //set these globals so functions down the line can look at them + Viewer_roomnum = viewer_roomnum; + Flag_automap = flag_automap; + Called_from_terrain = called_from_terrain; + //Assume no terrain + Must_render_terrain = 0; + + //Get the width & height of the render window + rend_GetProjectionParameters(&Render_width,&Render_height); + if (!Called_from_terrain) + { + Terrain_portal_top=Render_height; + Terrain_portal_bottom=0; + Terrain_portal_right=0; + Terrain_portal_left=Render_width; + } + //Build the list of visible rooms + BuildRoomList(viewer_roomnum); //fills in Render_list & N_render_segs + + //If we determined that the terrain is visible, render it + if (Must_render_terrain && !Called_from_terrain && !(In_editor_mode && Render_inside_only)) + { + RenderTerrain(1,Terrain_portal_left,Terrain_portal_top,Terrain_portal_right,Terrain_portal_bottom); + // Mark all room points to be rerotated due to terrain trashing our point list + for (int i=0;i<=Highest_room_index;i++) + { + Rooms[i].wpb_index=-1; + Global_buffer_index=0; + } + // Setup fog if needed + g3_SetFarClipZ (VisibleTerrainZ); + + if ((Terrain_sky.flags & TF_FOG) && (UseHardware || (!UseHardware && Lighting_on))) + { + rend_SetZValues(0,VisibleTerrainZ); + rend_SetFogState (1); + rend_SetFogBorders (VisibleTerrainZ*.85,VisibleTerrainZ); + rend_SetFogColor(Terrain_sky.fog_color); + } + else + rend_SetZValues(0,5000); + + } + + // First render mirrored rooms + RenderMirrorRooms (); + + Num_mirror_rooms=0; + + //Render the list of rooms + for (int nn=N_render_rooms-1;nn>=0;nn--) + { + int roomnum; + roomnum = Render_list[nn]; + #ifdef _DEBUG + if (In_editor_mode && Render_one_room_only && (roomnum != viewer_roomnum)) + continue; + #endif + if (roomnum!=-1) + { + ASSERT (Rooms_visited[roomnum]!=255); + if (Outline_release_mode&1) { + RenderRoomOutline(&Rooms[roomnum]); + } + RenderRoom(&Rooms[roomnum]); + Rooms_visited[roomnum] = (char) 255; + // Stuff objects into our postrender list + CheckToRenderMineObjects (roomnum); + } + } + rend_SetOverlayType(OT_NONE); // turn off lightmap blending + if (Must_render_terrain && !Called_from_terrain) + rend_SetFogState(0); + + #ifdef EDITOR + if (OUTLINE_ON(OM_MINE)) { + OutlineCurrentFace(Curroomp,Curface,Curedge,Curvert,CURFACE_COLOR,CUREDGE_COLOR); + if (Markedroomp) + OutlineCurrentFace(Markedroomp,Markedface,Markededge,Markedvert,MARKEDFACE_COLOR,MARKEDEDGE_COLOR); + if (Placed_room != -1) + DrawPlacedRoomFace(&Rooms[Placed_room],&Placed_room_origin,&Placed_room_rotmat,&Placed_room_attachpoint,Placed_room_face,PLACED_COLOR); + } + #endif +} +// Simply sets the number of glows to zero +void ResetLightGlows () +{ + Num_glows=0; + for (int i=0;ip3_flags=0; + z_on=on_pnt->p3_z; + z_off=off_pnt->p3_z; + k = 1.0-((z_off-zval) / (z_off-z_on)); + tmp->p3_z = on_pnt->p3_z + ((off_pnt->p3_z-on_pnt->p3_z) * k); + + tmp->p3_x = on_pnt->p3_x + ((off_pnt->p3_x-on_pnt->p3_x) * k); + tmp->p3_y = on_pnt->p3_y + ((off_pnt->p3_y-on_pnt->p3_y) * k); + if (on_pnt->p3_flags & PF_UV) { + tmp->p3_u = on_pnt->p3_u + ((off_pnt->p3_u-on_pnt->p3_u) * k); + tmp->p3_v = on_pnt->p3_v + ((off_pnt->p3_v-on_pnt->p3_v) * k); + tmp->p3_flags |= PF_UV; + } + if (on_pnt->p3_flags & PF_UV2) { + tmp->p3_u2 = on_pnt->p3_u2 + ((off_pnt->p3_u2-on_pnt->p3_u2) * k); + tmp->p3_v2 = on_pnt->p3_v2 + ((off_pnt->p3_v2-on_pnt->p3_v2) * k); + tmp->p3_flags |= PF_UV2; + } + if (on_pnt->p3_flags & PF_L) { + tmp->p3_l = on_pnt->p3_l + ((off_pnt->p3_l-on_pnt->p3_l) * k); + tmp->p3_flags |= PF_L; + } + if (on_pnt->p3_flags & PF_RGBA) { + tmp->p3_r = on_pnt->p3_r + ((off_pnt->p3_r-on_pnt->p3_r) * k); + tmp->p3_g = on_pnt->p3_g + ((off_pnt->p3_g-on_pnt->p3_g) * k); + tmp->p3_b = on_pnt->p3_b + ((off_pnt->p3_b-on_pnt->p3_b) * k); + tmp->p3_flags |= PF_RGBA; + } + if (ending) + tmp->p3_a = 0.0; + else + tmp->p3_a = 1.0; + g3_CodePoint(tmp); +} +*/ + +#define STATE_PUSH(val) {state_stack[state_stack_counter]=val; state_stack_counter++; ASSERT (state_stack_counter<2000);} +#define STATE_POP() {state_stack_counter--; pop_val=state_stack[state_stack_counter];} +// Sorts our texture states using the quicksort algorithm +void SortStates (state_limited_element *state_array,int cellcount) +{ + state_limited_element v,t; + int pop_val; + int i,j; + int l,r; + l=0; + r=cellcount-1; + ushort state_stack_counter=0; + ushort state_stack[2000]; + + while (1) + { + while (r>l) + { + i=l-1; + j=r; + v=state_array[r]; + while (1) + { + while (state_array[++i].sort_key < v.sort_key) + ; + while (state_array[--j].sort_key > v.sort_key) + ; + if (i>=j) + break; + t=state_array[i]; + state_array[i]=state_array[j]; + state_array[j]=t; + } + t=state_array[i]; + state_array[i]=state_array[r]; + state_array[r]=t; + + if (i-l > r-i) + { + STATE_PUSH (l); + STATE_PUSH (i-1); + l=i+1; + } + else + { + STATE_PUSH (i+1); + STATE_PUSH (r); + r=i-1; + } + } + if (!state_stack_counter) + break; + STATE_POP (); + r=pop_val; + STATE_POP (); + l=pop_val; + } +} +// Builds a list of mirror faces for each room and allocs memory accordingly +void ConsolidateMineMirrors() +{ + int i,t; + mprintf ((0,"Consolidating mine mirrors!\n")); + for (i=0;iused) + continue; + if (rp->mirror_faces_list) + { + mem_free(rp->mirror_faces_list); + rp->mirror_faces_list=NULL; + rp->num_mirror_faces=0; + } + if (rp->mirror_face==-1) + continue; + // Count the number of faces that have the same texture as the mirror face + int num_mirror_faces=0; + for (t=0;tnum_faces;t++) + { + face *fp=&rp->faces[t]; + if (fp->tmap==rp->faces[rp->mirror_face].tmap) + num_mirror_faces++; + } + if (num_mirror_faces==0) + { + // No faces found? Weird. + rp->mirror_face=0; + continue; + } + rp->mirror_faces_list=(ushort *)mem_malloc (num_mirror_faces*sizeof(ushort)); + ASSERT (rp->mirror_faces_list); + rp->num_mirror_faces=num_mirror_faces; + // Now go through and fill in our list + int count=0; + for (t=0;tnum_faces;t++) + { + face *fp=&rp->faces[t]; + if (fp->tmap==rp->faces[rp->mirror_face].tmap) + rp->mirror_faces_list[count++]=t; + } + } +} + +/* +// Takes a face and adds the appropriate vertices for drawing in the fog zone +// Returns number of points in new polygon +// New polygon points are in FogPoints array +int FogBlendFace (g3Point **src,int nv,int *num_solid,int *num_alpha) +{ + int alpha_nv=0,solid_nv=0,temp_nv=0; + g3Point temp_buf[128],temp_pnt; + float fog_zone_range=Fog_zone_end-Fog_zone_start; + int i; + + for (i=0;ip3_z>Fog_zone_end) + { + if (prevpnt->p3_zp3_zp3_z>Fog_zone_start) + { + if (prevpnt->p3_zp3_z-Fog_zone_start)/fog_zone_range); + if (alpha<0) + alpha=0; + ASSERT (alpha<=1.0); + AlphaFogPoints[alpha_nv]=*thispnt; + AlphaFogPoints[alpha_nv].p3_a=alpha; + alpha_nv++; + //------------------------------ + if (nextpnt->p3_z0); +} +*/ + +// RenderBlankScreen +// Renders a blank screen, to be used for UI callbacks to prevent Hall of mirrors with mouse cursor +void RenderBlankScreen(void) +{ + rend_ClearScreen(GR_BLACK); +} +#ifdef EDITOR +//Finds what room & face is visible at a given screen x & y +//Everything must be set up just like for RenderMineRoom(), and presumably is the same as +//for the last frame rendered (though it doesn't have to be) +//Parameters: x,y - the screen coordinates +// start_roomnum - where to start rendering +// roomnum,facenum - these are filled in with the found values +// if room<0, then an object was found, and the object number is -room-1 +//Returns: 1 if found a room, else 0 +/*int FindRoomFace(short x,short y,int start_roomnum,int *roomnum,int *facenum) +{ + //Init search mode + search_mode = -1; + search_x = x; search_y = y; + found_room = INT_MAX; + //Render and search + RenderMine(start_roomnum,0,0); + //Turn search off + search_mode = 0; + //Set return values + *roomnum = found_room; + *facenum = found_face; + return (found_room != INT_MAX); +}*/ +//finds what room,face,lumel is visible at a given screen x & y +//Everything must be set up just like for RenderMineRoom(), and presumably is the same as +//for the last frame rendered (though it doesn't have to be) +//Parameters: x,y - the screen coordinates +// start_roomnum - where to start rendering +// roomnum,facenum,lumel_num - these are filled in with the found values +//Returns: 1 if found a room, else 0 +/*int FindLightmapFace(short x,short y,int start_roomnum,int *roomnum,int *facenum,int *lumel_num) +{ + Search_lightmaps=1; + search_x = x; search_y = y; + found_room = INT_MAX; + //Get the width & height of the render window + rend_GetProjectionParameters(&Render_width,&Render_height); + RenderMine(start_roomnum,0,0); + Search_lightmaps=0; + *roomnum = found_room; + *facenum = found_face; + *lumel_num=found_lightmap; + return (found_room != INT_MAX); +}*/ +#endif diff --git a/Descent3/render.h b/Descent3/render.h new file mode 100644 index 000000000..be3d60f49 --- /dev/null +++ b/Descent3/render.h @@ -0,0 +1,304 @@ +/* + * $Logfile: /DescentIII/main/render.h $ + * $Revision: 38 $ + * $Date: 10/21/99 9:29p $ + * $Author: Jeff $ + * + * Header for render.c + * + * $Log: /DescentIII/main/render.h $ + * + * 38 10/21/99 9:29p Jeff + * B.A. Macintosh code merge + * + * 37 8/10/99 11:19p Gwar + * + * 36 4/18/99 5:42a Chris + * Added the FQ_IGNORE_RENDER_THROUGH_PORTALS flag + * + * 35 4/15/99 12:19p Jason + * made mirrors more robust and able to have multiple mirrored faces in + * the same room (as long as they are all on the same plane) + * + * 34 3/31/99 5:15p Matt + * Added checkboxes on the room tab to control rendering of faces based on + * shell flag. + * + * 33 2/17/99 1:05p Jason + * revamped object/face/terrain selection code + * + * 32 2/09/99 12:10p Jason + * rewriting indoor engine + * + * 31 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 30 10/12/98 3:01p Jeff + * added vsync_enable global + * + * 29 10/08/98 3:11p Matt + * Constant for all outline modes didn't include sky bit. + * + * 28 10/04/98 2:35p Matt + * Added debug options to limit rendering + * + * 27 10/03/98 11:21p Matt + * Added system to seperately control outline mode for mine, terrain, sky, + * & objects + * + * 26 9/24/98 12:57p Jason + * more state limited optimizations + * + * 25 9/22/98 3:55p Samir + * ifdef out stuff for non-debug version. + * + * 24 8/27/98 5:19p Jason + * added first rev of reflected surfaces + * + * 23 8/19/98 2:19p Jeff + * moved detail globals to a struct + * + * 22 8/18/98 11:38a Jason + * fixed polymodel fog lighting + * + * 21 8/13/98 6:56p Jason + * made objects foggable correctly + * + * 20 6/19/98 6:42p Jason + * made specular mapping a config detail item + * + * 19 5/25/98 3:46p Jason + * added better light glows + * + * 18 5/19/98 12:27p Jason + * cleaned up some 3d stuff + * + * 17 4/30/98 6:46p Jason + * more framerate testing + * + * 16 4/30/98 3:40p Jason + * framerate optimizations + * + * 15 2/03/98 11:43p Matt + * Added defaults for RenderMine() parameters + * + * 14 1/12/98 3:34p Jason + * sped up indoor rendering by clipping faces against portals + * + * 13 1/06/98 1:28p Matt + * Cleaned up interfaces to rendering routines, deleted unused code, etc. + * + * 12 1/02/98 6:40p Matt + * User renderer library (instead of viewport) functions to draw lines and + * to set and read pixels. Also made FindRoomFace() work without + * Render_viewport. + * + * 11 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * 10 12/01/97 4:20p Jason + * shadow and lighting changes + * + * 9 11/04/97 6:25p Matt + * Added code to render all external rooms for editing purposes + * + * 8 9/16/97 5:50p Matt + * Changed conditional for debug code + * + * 7 9/16/97 4:09p Jason + * implemented software zbuffer + * + * 6 9/11/97 3:14p Matt + * Added code to render floating triggers in the editor + * + * 5 9/04/97 5:23p Matt + * + * 4 9/04/97 12:12p Jason + * added lightmap visibility + * + * 3 7/23/97 6:36p Matt + * Changed Outline_mode & Lighting_on to be type bool + * + * 12 6/12/97 1:19p Matt + * Added version of rotate_list that takes list of int point numbers + * + * 11 5/27/97 3:30p Matt + * Added editor button to toggle lighting + * + * 10 5/08/97 7:46p Matt + * Added called_from_terrain parm for RenderMine() + * + * 9 5/07/97 1:30p Jason + * more changes for terrain/mine integration + * + * 8 5/05/97 4:47p Jason + * made terrain and mine rendering work without explicit calls to + * g3_Startframe in their main function calls + * This allows us to integrate mine/terrain engines nicely + * + * 7 4/29/97 5:15p Jason + * added mirrored textures...I don't know if we'll keep them in though + * + * 6 2/26/97 6:00p Matt + * Renamed 3d lib structs for D3 naming convention + * + * $NoKeywords: $ + */ +#ifndef RENDER_H +#define RENDER_H + +#include "3d.h" + +//Variables for debug/test +#if (defined(_DEBUG) || defined(NEWEDITOR)) + +#define SRF_NO_SHELL 1 //don't render the shell +#define SRF_NO_NON_SHELL 2 //don't render the non-shell + +extern int Render_portals; +extern bool Lighting_on; // If true, draw w/ normal lighting, else draw full brightness +extern ubyte Outline_mode; // Controls outline drawing. See constants below +extern ubyte Shell_render_flag; +extern bool Render_floating_triggers; // If true, render the floating triggers +extern bool Outline_lightmaps; +extern bool Use_software_zbuffer; +extern bool Render_all_external_rooms; // If true, draw all the outside rooms +extern bool Render_one_room_only; +extern bool Render_inside_only; + +#else +#define Lighting_on 1 +#define Outline_mode 0 +#endif +extern short use_opengl_1555_format; //DAJ + +#ifndef RELEASE +extern int Mine_depth; +#endif +//Macro for checking Outline mode +#define OUTLINE_ON(flag) ((Outline_mode & (flag + OM_ON)) == (flag + OM_ON)) + +//Constants for outline mode +#define OM_ON 1 +#define OM_MINE 2 +#define OM_TERRAIN 4 +#define OM_OBJECTS 8 +#define OM_SKY 16 +#define OM_ALL (OM_MINE + OM_TERRAIN + OM_OBJECTS + OM_SKY) + +extern float Fog_zone_start,Fog_zone_end; +extern bool DoBumpmaps; +extern bool Render_mirror_for_room; +extern bool Vsync_enabled; + +extern float Room_light_val; +extern int Room_fog_plane_check; +extern float Room_fog_distance; +extern float Room_fog_eye_distance; +extern vector Room_fog_plane,Room_fog_portal_vert; + +struct face; + +typedef struct +{ + short roomnum; + float close_dist; + face *close_face; +} fog_portal_data; + + +extern fog_portal_data Fog_portal_data[]; + +extern int Num_fogged_rooms_this_frame; + +// Sets fogzone start and end points +void SetFogZoneStart(float z); +void SetFogZoneEnd (float z); + +struct room; + +// For sorting our textures in state limited environments +typedef struct +{ + int facenum; + int sort_key; +} state_limited_element; + +#define MAX_STATE_ELEMENTS 8000 +extern state_limited_element State_elements[MAX_STATE_ELEMENTS]; + +extern g3Point SolidFogPoints[],AlphaFogPoints[]; + +// Takes a face and adds the appropriate vertices for drawing in the fog zone +// Returns number of points in new polygon +// New polygon points are in FogPoints array +int FogBlendFace (g3Point **src,int nv,int *num_solid,int *num_alpha); + +//Draws the mine, starting at a the specified room +//The rendering surface must be set up, and g3_StartFrame() must have been called +//Parameters: viewer_roomnum - what room the viewer is in +// flag_automap - if true, flag segments as visited when rendered +// called_from_terrain - set if calling this routine from the terrain renderer +void RenderMine(int viewer_roomnum,int flag_automap=0,int called_from_terrain=0); + +//Finds what room & face is visible at a given screen x & y +//Everything must be set up just like for RenderMineRoom(), and presumably is the same as +//for the last frame rendered (though it doesn't have to be) +//Parameters: x,y - the screen coordinates +// start_roomnum - where to start rendering +// roomnum,facenum - these are filled in with the found values +// if room<0, then an object was found, and the object number is -room-1 +//Returns: 1 if found a room, else 0 +int FindRoomFace(short x,short y,int start_roomnum,int *roomnum,int *facenum); + +//finds what room,face,lumel is visible at a given screen x & y +//Everything must be set up just like for RenderMineRoom(), and presumably is the same as +//for the last frame rendered (though it doesn't have to be) +//Parameters: x,y - the screen coordinates +// start_roomnum - where to start rendering +// roomnum,facenum,lumel_num - these are filled in with the found values +//Returns: 1 if found a room, else 0 +int FindLightmapFace(short x,short y,int start_roomnum,int *roomnum,int *facenum,int *lumel_num); + +// This is needed for small view cameras +// It clears the facing array so that it is recomputed +void ResetFacings(); + +// Renders all the lights glows for this frame +void RenderLightGlows (); + +// Called before a frame starts to render - sets all of our light glows to decreasing +void PreUpdateAllLightGlows (); + +// Called after a frame has been rendered - slowly morphs our light glows into nothing +void PostUpdateAllLightGlows (); + +// Resets our light glows to zero +void ResetLightGlows (); + +// Gets the dynamic light value for this position +void GetRoomDynamicScalar (vector *pos,room *rp,float *r,float *g,float *b); + +// Sorts our texture states using the quicksort algorithm +void SortStates (state_limited_element *state_array,int cellcount); + +// Sets up fog if this room is fogged +void SetupRoomFog (room *rp,vector *eye,matrix *orient,int viewer_room); + +//Draw the specified face +//Parameters: rp - pointer to the room the face is un +// facenum - which face in the specified room +void RenderFace(room *rp,int facenum); + +// Renders a specular face +void RenderSpecularFacesFlat(room *rp); + +// Renders fog faces for a room +void RenderFogFaces(room *rp); + +// Builds a list of mirror faces for each room and allocs memory accordingly +void ConsolidateMineMirrors(); + +extern int Num_specular_faces_to_render,Num_fog_faces_to_render; + +#endif \ No newline at end of file diff --git a/Descent3/renderobject.cpp b/Descent3/renderobject.cpp new file mode 100644 index 000000000..bb63bcf75 --- /dev/null +++ b/Descent3/renderobject.cpp @@ -0,0 +1,2519 @@ +/* + * $Logfile: /DescentIII/Main/renderobject.cpp $ + * $Revision: 182 $ + * $Date: 10/03/01 11:35p $ + * $Author: Kevin $ + * + * Code to render objects + * + * $Log: /DescentIII/Main/renderobject.cpp $ + * + * 182 10/03/01 11:35p Kevin + * multiplayer stripes appear in demo playback + * + * 181 10/12/99 11:07a Jeff + * created virus infected effect + * + * 180 9/21/99 2:55p Jeff + * use powerup color for spark colors + * + * 179 9/20/99 5:35p Jeff + * added -nosparkles command line option to turn off powerup sparkles + * + * 178 9/20/99 5:30p Jeff + * added pretty powerup particles for Katmai systems + * + * 177 9/18/99 9:28p Jeff + * motion blur robots and debris + * + * 176 8/09/99 3:27p Jeff + * fixed render bug with typing indicator + * + * 175 7/28/99 3:44p Kevin + * Mac! + * + * 174 7/20/99 1:00p Jason + * added auto katmai support + * + * 173 7/08/99 5:47p Jason + * changes for new bumpmapping system in 1.1 update patch + * + * 172 5/26/99 3:03a Jason + * fixed bug where flags attached to players would render incorrectly in + * multplayer (specifially ctf flags) + * + * 171 5/13/99 3:41p Ardussi + * changes for compiling on the Mac + * + * 170 5/12/99 3:25p Jason + * don't render powerup glows if their render type is RT_NONE + * + * 169 5/08/99 1:50p Jason + * fixed some player colors + * + * 168 5/03/99 9:27p Jason + * fixed one of Chris's bugs with the attach system + * + * 167 4/29/99 4:58p Jason + * fixed typing indicator problem + * + * 166 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 165 4/19/99 3:25p Jason + * + * 164 4/19/99 3:46a Jeff + * fixed min/max for Linux + * + * 163 4/17/99 4:51p Jason + * fixed stupid headlight issue for good + * + * 162 4/16/99 11:53a Jason + * fixed some lighting issues + * + * 161 4/14/99 7:21p Jason + * made ships more visible in multiplayer + * + * 160 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 159 4/09/99 7:04p Jason + * changed some texture defines + * + * 158 4/06/99 1:51p Jason + * added new mass driver effect + * + * 157 4/06/99 10:23a Chris + * More "render even though the parent is invisible" cases + * + * 156 4/03/99 4:56p Jason + * fixed deform effect just a little + * + * 155 4/03/99 2:20p Jason + * added better heat damage effect + * + * 154 3/29/99 4:02p Chris + * Patched/Hacked a solution to the invis. after landing problem. + * + * 153 3/22/99 11:16a Jason + * Katmai enhancements + * + * 152 3/04/99 11:44a Jeff + * made it so typing indicator is at max 64x64 when rendering + * + * 151 3/03/99 4:51p Jason + * fixed divide by zero problem + * + * 150 2/24/99 11:51p Jason + * fixed object bug for matt + * + * 149 2/23/99 12:40p Jason + * added more options for generic objects + * + * 148 2/22/99 3:37p Jason + * fixed LOD problems + * + * 147 2/21/99 4:20p Matt + * Added SoundSource objects (and reformatted parts of the object header + * files). + * + * 146 2/18/99 11:05a Jason + * fixed some cloak effects + * + * 145 2/17/99 1:05p Jason + * revamped object/face/terrain selection code + * + * 144 2/15/99 4:11p Jason + * made non-vis objects kill all their attach sounds/objects + * + * 143 2/10/99 12:47p Jeff + * fixed up typing indicator + * + * 142 2/09/99 6:53p Jeff + * implemented 'typing inidcator' in multiplayer...players that are typing + * messages have an icon on them + * + * 141 2/09/99 9:59a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 140 2/05/99 3:00p Jason + * fixed bug with my last rev + * + * 139 2/05/99 12:19p Jason + * fixed object selection + * + * 138 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 137 1/27/99 6:08p Jason + * first pass at markers + * + * 136 1/21/99 11:29p Jason + * took out misplaced no_glow flag + * + * 135 1/20/99 5:36p Jason + * added custom glows for teams + * + * 134 1/19/99 4:22p Matt + * Added the ability for objects to have their own lighting info, + * different from the default lighting for that type of object. + * + * 133 1/13/99 12:43p Jason + * added some more detail settings + * + * 132 1/13/99 6:38a Jeff + * fixed object.h. There were numerous struct declarations that were the + * same name as the instance of the struct (gcc doesn't like this). + * Changed the struct name. Also added some #ifdef's for linux build, + * along with fixing case-sensitive includes + * + * 131 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 130 12/14/98 10:53a Jason + * added bright player ships option + * + * 129 12/10/98 7:09p Jason + * added cloak fade + * + * 128 12/10/98 12:27p Jason + * added cooler specular mapping for objects + * + * 127 12/07/98 3:02p Jason + * added multi_logo_state + * + * 126 11/11/98 7:18p Jeff + * changes made so that a dedicated server's team is always -1 (team game + * or not) + * + * 125 11/11/98 12:33p Jason + * changed black player color + * + * 124 10/21/98 9:28p Jason + * Made no lightmaps work globally + * + * 123 10/21/98 7:55p Jason + * d3d changes + * + * 122 10/21/98 7:30p Jason + * more direct3d changes + * + * 121 10/19/98 3:16p Jason + * additional error checking for lightning + * + * 120 10/18/98 4:25p Jason + * fixes for fogged objects + * + * 119 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 118 10/16/98 2:38p Jason + * made powerup halo detail setting work + * + * 117 10/16/98 2:24p Jason + * changes for the demo + * + * 116 10/14/98 6:24p Jason + * made object complexity work + * + * 115 10/08/98 5:59p Jason + * fixed object and room popping with small views + * + * 114 10/07/98 5:04p Jason + * made spheres work in WindowGL mode + * + * 113 10/02/98 1:47p Jason + * added lod player ships + * + * 112 10/02/98 12:23p Jason + * took off sight vector + * + * 111 10/02/98 11:46a Jason + * checked in for samir + * + * 110 10/01/98 6:10p Jason + * added EVT_CLIENT_GETCOLOREDNAME + * + * 109 10/01/98 4:41p Jason + * fixed colors in team games + * + * 108 10/01/98 4:09p Jason + * added multicolor ship stuff + * + * 107 9/29/98 12:49p Jason + * worked on matcen effects and lightning + * + * 106 9/24/98 12:57p Jason + * more state limited optimizations + * + * 105 9/21/98 5:08p Jason + * made damage shield not rotate when you get hit + * + * 104 9/21/98 1:03p Jason + * make sparky damage lightning ramp up depending on amount of damage + * + * 103 9/18/98 8:23p Jason + * fixed insidious vis effect errors + * + * 102 9/17/98 6:08p Jason + * more tweaks for effects + * + * 101 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 100 9/10/98 12:18p Jason + * more changes to afterburner/thrust effect + * + * 99 9/09/98 7:09p Jason + * changed afterburner effect for ships + * + * 98 8/27/98 5:19p Jason + * added first rev of reflected surfaces + * + * 97 8/24/98 1:01p Jason + * made object specular faces selectable by texture + * + * 96 8/19/98 2:19p Jeff + * moved detail globals to a struct + * + * 95 8/18/98 11:38a Jason + * fixed polymodel fog lighting + * + * 94 8/14/98 4:00p Jason + * added specular objects outside + * + * 93 8/13/98 6:57p Jason + * made fogged objects render correctly + * + * 92 8/12/98 6:38p Jeff + * don't render ghosted objects (type==OBJ_DUMMY) + * + * 91 8/03/98 3:59p Chris + * Added support for FQ_IGNORE_WEAPONS, added .000001 attach code, fix a + * bug in polymodel collision detection + * + * 90 8/03/98 2:59p Jeff + * player names only check doors for visibility + * + * 89 8/03/98 11:06a Jason + * made custom textures work correctly + * + * 88 7/30/98 11:12a Jason + * fixed player names showing up when cloaked + * + * 87 7/28/98 3:29p Jason + * fixed z buffer bug with damage disk + * + * 86 7/27/98 5:59p Jason + * added piggyback mode plus multiplayer colors + * + * 85 7/02/98 6:30p Jason + * added some multiplayer stuff for Jeff + * + * 84 6/30/98 6:36p Chris + * Added rev .1 of multiplayer animations - BTW It is totally not done. + * + * 83 6/25/98 5:17p Jason + * added multiple colored balls for players + * + * 82 6/22/98 12:27p Jason + * fixed stupid bug with my last rev + * + * 81 6/22/98 12:15p Jason + * took out cheap LOD effect with robots + * + * 80 6/15/98 4:00p Jason + * replaced monochromatic polymodel lighting with rgb lighting + * + * 79 6/11/98 12:48p Jason + * added better spewing weapons + * + * 78 5/11/98 10:41a Jason + * made clutter object lodable + * + * 77 5/05/98 6:26p Chris + * Added BBoxs and spheres to external rooms (in mode 2) + * + * 76 5/04/98 7:26p Chris + * Allowed turning off of submodels on robots + * + * 75 5/04/98 3:51p Matt + * Finished (for now) with breaking glass. + * + * 74 5/04/98 12:28p Matt + * Added shard objects + * + * 73 5/03/98 8:42p Chris + * + * 72 5/03/98 8:36p Chris + * Additional debug info + * + * 71 4/30/98 12:22p Jason + * did some lo-res model optimizations + * + * 70 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 69 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 68 4/17/98 3:57p Jason + * added microwave effect + * + * 67 4/17/98 1:59p Jason + * added cool object effects + * + * 66 4/15/98 3:28p Jason + * changed glow stuff to work with new system + * + * 65 4/13/98 3:51p Chris + * Added sphere bubbles to weapons (when wanted) and fixed a small FVI + * bug + * + * 64 4/10/98 12:41p Jason + * sped up lighting a little + * + * 63 4/08/98 12:58p Jason + * fixed cloak effect in software + * + * 62 4/07/98 5:01p Jason + * more tweaks to cloak effect + * + * 61 4/07/98 4:49p Jason + * fixed cloak problem + * + * 60 4/06/98 2:54p Jason + * yet more multiplayer changes + * + * 59 4/02/98 12:24p Jason + * trimmed some fat from our structures + * + * 58 4/01/98 6:23p Jason + * added a slew of stuff for multiplayer + * + * 57 4/01/98 12:02p Jason + * incremental checkin for rendering changes + * + * 56 3/20/98 5:51p Jason + * more changes for multiplayer + * + * 55 3/20/98 2:18p Jason + * changes for multiplayer + * + * 54 3/19/98 7:15p Jason + * more changes for multiplayer + * + * 53 3/13/98 7:00p Chris + * EDITOR to _DEBUG + * + * 52 3/13/98 12:10p Jason + * draw a bit of a ring around polygon glows + * + * 51 3/11/98 4:59p Chris + * Added a few new sphere types (wall and anim) + * + * 50 3/10/98 5:15p Chris + * DEL+B shows the bounding-sphere of an object + * + * 49 3/09/98 5:58p Jason + * draw powerups with saturated alpha rings + * + * 48 3/09/98 4:54p Jason + * don't render objects that can't been seen through a portal + * + * 47 2/25/98 4:31p Jason + * changes for explosions + * + * 46 2/25/98 2:05p Jason + * did FOV and object visibility changes + * + * 45 2/20/98 1:46p Chris + * JASON: Made dynamic lighting only occur if the object is rendered + * + * 44 2/11/98 6:58p Matt + * Changed the way cameras are rendered, so they show up on the terrain as + * well. + * + * 43 2/11/98 6:38p Jason + * tweaking the glow effects for engines + * + * 42 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 41 2/04/98 2:52p Jason + * made length of glow dependant on whether or not the player is thrusting + * + * 40 1/30/98 6:07p Matt + * Added code and menu items to deal with camera objects + * + * 39 1/30/98 2:56p Matt + * Added support for small views. Made R toggle rear view, and Delete-F8 + * show a view from object 2. Also changed main window render flow so + * could share code with small views. + * + * 38 1/28/98 5:37p Jason + * added streamer weapons + * + * 37 1/23/98 11:21a Jason + * incremental multiplayer checkin + * + * 36 1/21/98 6:09p Jason + * Got player deaths working in multiplayer + * + * 35 1/21/98 1:11p Jason + * incremental checkin for multiplayer + * + * 34 1/20/98 2:17p Jason + * fixed weird sorting problem with objects and viseffects + * + * 33 1/20/98 12:10p Jason + * implemented vis effect system + * + * 32 1/13/98 4:04p Jason + * changes for engine glow + * + * 31 1/13/98 3:09p Jason + * added glow effect for engines + * + * 30 1/05/98 5:59p Jason + * tweaked z bias code + * + * 29 12/08/97 6:18p Jason + * more tweaks for destroyable buildings + * + * 28 12/08/97 5:22p Jason + * added code for destroyable buildings that leave their base object + * around + * + * 27 11/25/97 1:36p Jason + * adjusted LOD switch levels + * + * 26 11/25/97 1:17p Jason + * added lod system for certain objects + * + * 25 11/18/97 12:26a Sean + * I don't know (Chris) + * + * 24 11/11/97 8:40p Chris + * Added an assert. + * + * 23 11/05/97 6:03p Jason + * added disky colored lighting effects + * + * 22 11/05/97 12:20p Jason + * made powerups render with halos + * + * 21 10/28/97 11:34a Chris + * Fixed a bug in the animation code (if keyframe = 0 and the object + * animates, we need to set the angles) + * + * 20 10/23/97 11:34p Chris + * Fixed problems with idle animations + * + * 19 10/23/97 5:36p Jason + * added splinter objects (probably temporary) + * + * 18 10/20/97 4:46p Jason + * changes for explosions + * + * 17 10/03/97 5:57p Chris + * Added support for debug fvi_line + * + * 16 10/03/97 4:43p Chris + * added debug fvi call and object type line + * + * 15 10/01/97 7:00p Jason + * did more work on object lightmaps + * + * 14 10/01/97 11:35a Jason + * added IsObjectVisible function + * + * 13 9/30/97 5:08p Chris + * No changes + * + * 12 9/22/97 6:20p Matt + * Removed obsolete code + * + * 11 9/16/97 5:04p Matt + * Changed conditional for debug code + * + * 10 9/12/97 5:38p Jason + * got doors working + * + * 9 9/10/97 5:17p Jason + * more lighting improvements + * + * 8 9/09/97 6:15p Jason + * made dynamic lighting on objects more memory efficient + * + * 7 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 6 8/20/97 5:19p Matt + * Draw selected bracket instead of box for current object + * + * $NoKeywords: $ + */ +#include "object.h" +#include "object_lighting.h" +#include "3d.h" +#include "polymodel.h" +#include "renderer.h" +#include "weapon.h" +#include "fireball.h" +#include "descent.h" +#include "renderobject.h" +#include "AIMain.h" +#include "objinfo.h" +#include "splinter.h" +#include "fireball.h" +#include "descent.h" +#include "render.h" +#include "gametexture.h" +#include "game.h" +#include "player.h" +#include "damage.h" +#include "gameloop.h" +#include "findintersection.h" +#include "grtext.h" +#include "gamefont.h" +#include "config.h" +#include "viseffect.h" +#include "game2dll.h" +#include "marker.h" +#include "ship.h" +#include "psrand.h" + +#ifdef __LINUX__ +#define min(a,b) ((ab)?a:b) +#elif defined(MACINTOSH) +#include "Macros.h" +#endif +#include +#ifdef EDITOR + #include "editor\d3edit.h" +#endif +//what darkening level to use when cloaked +#define CLOAKED_FADE_LEVEL 28 +#define CLOAK_FADEIN_DURATION_PLAYER F2_0 +#define CLOAK_FADEOUT_DURATION_PLAYER F2_0 +#define CLOAK_FADEIN_DURATION_ROBOT F1_0 +#define CLOAK_FADEOUT_DURATION_ROBOT F1_0 +#define RO_STATIC 0 +#define RO_GOURAUD 1 +#define RO_LIGHTMAPS 2 +extern ubyte Use_motion_blur; +ubyte RenderObjectType=RO_STATIC; +float RenderObjectStaticRedValue=1.0f; +float RenderObjectStaticGreenValue=1.0f; +float RenderObjectStaticBlueValue=1.0f; +float RenderObjectStaticScalar=1.0f; +ubyte *RenderObjectGouraudValue=NULL; +lightmap_object *RenderObjectLightmapObject=NULL; +vector RenderObject_LightDirection; + +float Last_powerup_sparkle_time = 0.0f; +bool Render_powerup_sparkles = false; +//Do powerup sparkles and stuff +void DrawPowerupSparkles(object *obj); + +// Draws the glowing disk around a powerup +void DrawPowerupGlowDisk (object *obj); +// Draws a damage disk around a powerup +void DrawPlayerDamageDisk (object *obj); +void DrawPlayerInvulSphere (object *obj); +void DrawPlayerSightVector (object *obj); +// Draws this players name on the hud +void DrawPlayerNameOnHud (object *obj); +// Draws a rotating ball around the player +void DrawPlayerRotatingBall (object *obj); +// Creates lightning sparks on a damaged object +void DrawSparkyDamageLightning (object *obj); +// Creates Virus infected lightning on an object +void DrawVirusLightning (object *obj); + +#ifdef EDITOR +//Draws the little corner brackets around the selected object +//Actually, only draws those either in front or in back of the object, based on front_flag +void DrawObjectSelectionBrackets(object *obj,bool front_flag) +{ + vector viewvec; + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + float line_len; + //Get vector from object to viewer + g3_GetViewPosition(&viewvec); + viewvec -= obj->pos; + //Get length of line segments we're drawing + line_len = (pm->maxs.x - pm->mins.x) * 0.2f; + //Do each corner + for (int c=0;c<8;c++) { + vector corner; + //Get the corner relative to the object + corner = (obj->orient.rvec * ((c&1) ? pm->mins.x : pm->maxs.x)) + + (obj->orient.uvec * ((c&2) ? pm->mins.y : pm->maxs.y)) + + (obj->orient.fvec * ((c&4) ? pm->mins.z : pm->maxs.z)); + //See if this corner is in front or in back of the object, as specified + if (((corner * viewvec) > 0.0) != front_flag) + continue; + //Get absolute position in 3-space + corner += obj->pos; + //Draw line for each axis at this corner + for (int a=0;a<3;a++) { + g3Point pp0,pp1; + vector t; + //Grab the x,y, or z axis, and scale by the line segment length + t = corner + ((((vector *) &obj->orient)[a]) * ((c&(1< 10) + { + mprintf((0, "Cannot represent a number with over 10 digits\n")); + Int3(); + return; + } + + for(j = num_numbers - 1; j >= 0; j--) + { + num_array[j] = total / ((int)powf(10.0f,(float)j)); + total -= num_array[j] * (int)powf(10.0f,(float)j); + } + for(j = 0; j < num_numbers; j++) + { + vector cur_pos; + + if (num_numbers & 0x00000001) + cur_pos = pos + (2.1 * size * ((num_numbers >> 1) - j)) * Viewer_object->orient.rvec; + else + cur_pos = pos + (2.1 * size * ((num_numbers >> 1) - j) - size) * Viewer_object->orient.rvec; + g3_RotatePoint (&basepnt,&cur_pos); + for (i=0;imax_xyz.x < obj2->min_xyz.x || + obj2->max_xyz.x < obj1->min_xyz.x || + obj1->max_xyz.z < obj2->min_xyz.z || + obj2->max_xyz.z < obj1->min_xyz.z || + obj1->max_xyz.y < obj2->min_xyz.y || + obj2->max_xyz.y < obj1->min_xyz.y) overlap = false; + + return overlap; +} +// Given a face, computes the upper left corner of the face +void ComputeDebugVisFaceUpperLeft (room *rp,face *fp,vector *upper_left,float *xdiff,float *ydiff,vector *center) +{ + matrix face_matrix,trans_matrix; + vector fvec; + vector avg_vert; + vector verts[MAX_VERTS_PER_FACE]; + vector rot_vert; + int i; + + // find the center point of this face + vm_MakeZero (&avg_vert); + for (i=0;inum_verts;i++) + avg_vert+=rp->verts[fp->face_verts[i]]; + avg_vert/=fp->num_verts; + // Make the orientation matrix + // Reverse the normal because we're looking "at" the face, not from it + fvec=-fp->normal; + + vm_VectorToMatrix(&face_matrix,&fvec,NULL,NULL); + // Make the transformation matrix + + angvec avec; + vm_ExtractAnglesFromMatrix(&avec,&face_matrix); + vm_AnglesToMatrix (&trans_matrix,avec.p,avec.h,avec.b); + + // Rotate all the points + for (i=0;inum_verts;i++) + { + vector vert=rp->verts[fp->face_verts[i]]; + + vert-=avg_vert; + vm_MatrixMulVector (&rot_vert,&vert,&trans_matrix); + verts[i]=rot_vert; + } + // Find left most point + int leftmost_point=-1; + float leftmost_x=900000.00f; // a big number + for (i=0;inum_verts;i++) + { + if (verts[i].xnum_verts;i++) + { + if (verts[i].y>topmost_y) + { + topmost_point=i; + topmost_y=verts[i].y; + } + } + ASSERT (topmost_point!=-1); + // Find right most point + int rightmost_point=-1; + float rightmost_x=-900000.00f; // a big number + for (i=0;inum_verts;i++) + { + if (verts[i].x>rightmost_x) + { + rightmost_point=i; + rightmost_x=verts[i].x; + } + } + ASSERT (rightmost_point!=-1); + // Find bottom most point + int bottommost_point=-1; + float bottommost_y=900000.0f; // a big number + for (i=0;inum_verts;i++) + { + if (verts[i].yroomnum; + if((!ROOMNUM_OUTSIDE(roomnum)) && (roomnum != -1) && (Rooms[roomnum].used)) + { + room *rp = &Rooms[roomnum]; + int t; + int j; + for (t = 0; t < rp->num_portals; t++) + { + face *src_fp=&rp->faces[rp->portals[t].portal_face]; + float src_width,src_height; + vector src_upper_left,src_center; + matrix src_matrix; + vector src_verts[MAX_VERTS_PER_FACE],*src_vertp[MAX_VERTS_PER_FACE]; + for (j=0; j < src_fp->num_verts; j++) + { + src_verts[j]=rp->verts[src_fp->face_verts[j]]; + src_vertp[j]=&src_verts[j]; + } + vector fvec=-src_fp->normal; + vm_VectorToMatrix(&src_matrix,&fvec,NULL,NULL); + ComputeDebugVisFaceUpperLeft (rp,src_fp,&src_upper_left,&src_width,&src_height,&src_center); + + if (src_width>VIS_TABLE_RESOLUTION) + { + float num=src_width/VIS_TABLE_RESOLUTION; + src_width=VIS_TABLE_RESOLUTION; + src_upper_left+=(src_matrix.rvec*(num/2)); + src_matrix.rvec*=num; + } + if (src_height>VIS_TABLE_RESOLUTION) + { + float num=src_height/VIS_TABLE_RESOLUTION; + src_height=VIS_TABLE_RESOLUTION; + src_upper_left-=(src_matrix.uvec*(num/2)); + src_matrix.uvec*=num; + } + vector src_vector, src_ybase; + src_ybase=src_upper_left; + for (int sy = 0; sy < src_height; sy++, src_ybase -= src_matrix.uvec) + { + src_vector = src_ybase; + for (int sx = 0; sx < src_width; sx++, src_vector += src_matrix.rvec) + { + vector src2=src_vector; +// vector subvec=src_center-src_vector; +// float mag=vm_GetMagnitudeFast (&subvec); +// subvec/=(mag*4); +// src2+=subvec; + if ((check_point_to_face(&src2, &src_fp->normal,src_fp->num_verts,src_vertp))) + { + DrawColoredDisk(&src2, 1.0f, 0.0f, 0.0f, .1f, .7f, 0.25f, 1); + continue; + } + + src2 += 0.1f * rp->faces[rp->portals[t].portal_face].normal; + if(!fvi_QuickRoomCheck(&src2, rp, false) && !fvi_QuickRoomCheck(&src2, rp, true)) + { + DrawColoredDisk(&src2, 0.0f, 1.0f, 0.0f, .1f, .7f, 0.25f, 1); + continue; + } + DrawColoredDisk(&src2, 0.0f, 0.0f, 1.0f, .1f, .7f, 0.25f, 1); + } + } + } + } +} +void DrawDebugInfo(object *obj) +{ + matrix m; + m = obj->orient; + vm_TransposeMatrix(&m); + if(obj->type == OBJ_ROOM) + { + DrawColoredDisk (&obj->pos, 0.0f, 0.0f, 1.0f, .1f, .7f, obj->size, 1); + } + else + { + if(Game_show_sphere == 1) + { + vector center = obj->pos + Poly_models[obj->rtype.pobj_info.model_num].wall_size_offset * m; + DrawColoredDisk (¢er, 0.0f, 0.0f, 1.0f, .1f, .7f, Poly_models[obj->rtype.pobj_info.model_num].wall_size, 1); + } + else if(Game_show_sphere == 2) + { + vector center = obj->pos + Poly_models[obj->rtype.pobj_info.model_num].anim_size_offset * m; + DrawColoredDisk (¢er, 0.0f, 0.1f, .9f, .1f, .7f, Poly_models[obj->rtype.pobj_info.model_num].anim_size, 1); + } + else if(Game_show_sphere == 3) + { + DrawColoredDisk (&obj->pos, 0.1f, 0.2f, .9f, .1f, .7f, obj->size, 1); + } + } + g3Point g3p[8]; + memset(g3p, 0, 8 * sizeof(g3Point)); + vector pos[9]; + //g3p[0].p3_vec = obj->pos; + //g3p[1].p3_vec = obj->rtype.line_info.end_pos; + + ddgr_color c1; + if(!object_object_AABB(obj, Player_object)) + c1 = GR_RGB(255,0,0); + else + c1 = GR_RGB(255,255,255); + pos[0] = obj->max_xyz; + pos[1] = obj->max_xyz; + pos[1].z = obj->min_xyz.z; + pos[2] = obj->min_xyz; + pos[2].y = obj->max_xyz.y; + pos[3] = obj->max_xyz; + pos[3].x = obj->min_xyz.x; + pos[4] = obj->max_xyz; + pos[4].y = obj->min_xyz.y; + pos[5] = obj->min_xyz; + pos[5].x = obj->max_xyz.x; + pos[6] = obj->min_xyz; + pos[7] = obj->min_xyz; + pos[7].z = obj->max_xyz.z; + + pos[8] = obj->pos; + pos[8].y += .8f * obj->size; + g3_RotatePoint(&g3p[0], &pos[0]); + g3_RotatePoint(&g3p[1], &pos[1]); + g3_RotatePoint(&g3p[2], &pos[2]); + g3_RotatePoint(&g3p[3], &pos[3]); + g3_RotatePoint(&g3p[4], &pos[4]); + g3_RotatePoint(&g3p[5], &pos[5]); + g3_RotatePoint(&g3p[6], &pos[6]); + g3_RotatePoint(&g3p[7], &pos[7]); + g3_DrawLine(c1,&g3p[0],&g3p[1]); + g3_DrawLine(c1,&g3p[1],&g3p[2]); + g3_DrawLine(c1,&g3p[2],&g3p[3]); + g3_DrawLine(c1,&g3p[3],&g3p[0]); + g3_DrawLine(c1,&g3p[0],&g3p[4]); + g3_DrawLine(c1,&g3p[1],&g3p[5]); + g3_DrawLine(c1,&g3p[2],&g3p[6]); + g3_DrawLine(c1,&g3p[3],&g3p[7]); + g3_DrawLine(c1,&g3p[4],&g3p[5]); + g3_DrawLine(c1,&g3p[5],&g3p[6]); + g3_DrawLine(c1,&g3p[6],&g3p[7]); + g3_DrawLine(c1,&g3p[7],&g3p[4]); + DrawNumber (OBJNUM(obj), obj->pos, 1.0, GR_RGB(255,255,255)); + + if(!OBJECT_OUTSIDE(obj)) + { + if(obj->roomnum != Player_object->roomnum) + c1 = GR_RGB(255,0,0); + else + c1 = GR_RGB(255,255,255); + DrawNumber (obj->roomnum, obj->max_xyz, 0.50, c1); + } +} +#endif +//Draw a shard +void DrawShardObject(object *obj) +{ + shard_info_s *si = &obj->rtype.shard_info; + bool flip = 0; + g3Point rotated_points[3]; + g3Point *pointlist[3]; + ubyte codes_and = 0xff; + g3_StartInstanceMatrix(&obj->pos,&obj->orient); + //Build list of points and UVLs for this face + for (int i=0;i<3;i++) { + ubyte c; + g3Point *p = &rotated_points[i]; + c = g3_RotatePoint(p,&si->points[i]); + codes_and &= c; + p->p3_uvl.u = si->u[i]; + p->p3_uvl.v = si->v[i]; + p->p3_uvl.a = 1.0; //GameTextures[si->tmap].alpha; + p->p3_flags |= PF_UV; // + PF_L; // + PF_UV2 + PF_RGBA; //has uv and l set + p->p3_uvl.l=1.0; + pointlist[i] = &rotated_points[i]; + } + //Check for backside + if (! g3_CheckNormalFacing(&obj->pos,&si->normal)) { + g3Point *t = pointlist[1]; + pointlist[1] = pointlist[2]; + pointlist[2] = t; + } + //Done with 3D + g3_DoneInstance(); + //Check if face off screen + if (codes_and) + return; + //Get bitmap handle + int bm_handle = GetTextureBitmap(si->tmap,0); + ASSERT(bm_handle != -1); + float alpha = GameTextures[si->tmap].alpha; + alpha = 0.2 + alpha * 0.8; //Make less transparent + //Set alpha, transparency, & lighting for this face + rend_SetAlphaType(ATF_CONSTANT); + rend_SetAlphaValue(alpha*255); + rend_SetLighting(LS_GOURAUD); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + //Select texture type + rend_SetTextureType(TT_LINEAR); + //Draw the polygon + g3_DrawPoly(3,pointlist,bm_handle); +} +// Sets up the light states for an outdoor object to be rendered +bool SetupTerrainObject (object *obj) +{ + vector camlight=Terrain_sky.lightsource; + vm_NormalizeVector (&camlight); +#ifdef EDITOR + if(!Terrain_render_ext_room_objs) + return false; +#endif + + obj->flags|=OF_SAFE_TO_RENDER; + RenderObject_SetLightDirection (&camlight); + rend_SetColorModel (CM_MONO); + if (obj->render_type==RT_POLYOBJ || (obj->render_type==RT_WEAPON && !((Weapons[obj->id].flags & WF_IMAGE_BITMAP) || (Weapons[obj->id].flags & WF_IMAGE_VCLIP)))) + { + float scalar_r,scalar_g,scalar_b,scalar; + if (obj->type==OBJ_POWERUP) + { + scalar=1.0; + scalar_r=1.0; + scalar_g=1.0; + scalar_b=1.0; + } + else + { + scalar=GetTerrainDynamicScalar (&obj->pos,CELLNUM(obj->roomnum)); + if (obj->effect_info && (obj->effect_info->type_flags & EF_VOLUME_LIT)) + { + scalar_r=min(1,scalar+(obj->effect_info->dynamic_red)); + scalar_g=min(1,scalar+(obj->effect_info->dynamic_green)); + scalar_b=min(1,scalar+(obj->effect_info->dynamic_blue)); + // If this is a robot, make it at least 10% for each RGB component + if (obj->type==OBJ_ROBOT) + { + scalar_r=max(.1,scalar_r); + scalar_g=max(.1,scalar_g); + scalar_b=max(.1,scalar_b); + } + if (obj->type==OBJ_PLAYER && ((Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT) || ((Game_mode & GM_MULTI) && (Netgame.flags & NF_BRIGHT_PLAYERS)))) + { + scalar_r=1; + scalar_g=1; + scalar_b=1; + } + } + else + { + scalar_r=scalar; + scalar_g=scalar; + scalar_b=scalar; + } + } + + if (obj->lighting_render_type==LRT_STATIC || Poly_models[obj->rtype.pobj_info.model_num].new_style==0) + RenderObject_SetStatic (scalar_r,scalar_g,scalar_b); + else if (obj->lighting_render_type==LRT_GOURAUD || NoLightmaps) + { + vector lightdir={0,-1.0,0}; // straight down for now + RenderObject_SetGouraud (&lightdir,scalar_r,scalar_g,scalar_b,scalar); + } + else if (obj->lighting_render_type==LRT_LIGHTMAPS) + { + if (obj->lm_object.used==0) + RenderObject_SetStatic (scalar_r,scalar_g,scalar_b); + else + RenderObject_SetLightmaps (&obj->lm_object); + + } + } + else + { + RenderObject_SetStatic (1,1,1); + } + return true; +} +// Sets up the light states for an indoor object to be rendered +bool SetupMineObject(object *objp) +{ + if (objp->lighting_render_type==LRT_STATIC || Poly_models[objp->rtype.pobj_info.model_num].new_style==0) + { + RenderObject_SetStatic (1.0f,1.0f,1.0f); + } + else if (objp->lighting_render_type==LRT_GOURAUD || NoLightmaps) + { + float scalar_r=1.0,scalar_g=1.0,scalar_b=1.0; + + vector lightdir={0,-1.0,0}; // straight down for now + + // Get the volume light for this object + if (objp->effect_info && (objp->effect_info->type_flags & EF_VOLUME_LIT) && !(Rooms[objp->roomnum].flags & RF_EXTERNAL)) + { + vector vpos=objp->pos; + if (Render_mirror_for_room) + vpos=objp->last_pos; + if (objp->effect_info->type_flags & EF_VOLUME_CHANGING) + { + float old_r,old_g,old_b; + float new_r,new_g,new_b; + GetRoomDynamicScalar (&objp->effect_info->volume_old_pos,&Rooms[objp->effect_info->volume_old_room],&old_r,&old_g,&old_b); + GetRoomDynamicScalar (&vpos,&Rooms[objp->roomnum],&new_r,&new_g,&new_b); + scalar_r=(old_r*objp->effect_info->volume_change_time)+((1-objp->effect_info->volume_change_time)*new_r); + scalar_g=(old_g*objp->effect_info->volume_change_time)+((1-objp->effect_info->volume_change_time)*new_g); + scalar_b=(old_b*objp->effect_info->volume_change_time)+((1-objp->effect_info->volume_change_time)*new_b); + } + else + GetRoomDynamicScalar (&vpos,&Rooms[objp->roomnum],&scalar_r,&scalar_g,&scalar_b); + + scalar_r=min(1,scalar_r+(objp->effect_info->dynamic_red)); + scalar_g=min(1,scalar_g+(objp->effect_info->dynamic_green)); + scalar_b=min(1,scalar_b+(objp->effect_info->dynamic_blue)); + // If this is a robot, make it at least 10% for each RGB component + if (objp->type==OBJ_ROBOT) + { + scalar_r=max(.1,scalar_r); + scalar_g=max(.1,scalar_g); + scalar_b=max(.1,scalar_b); + } + + if (objp->type==OBJ_PLAYER && (Game_mode & GM_MULTI)) + { + if (Netgame.flags & NF_BRIGHT_PLAYERS) + { + scalar_r=1; + scalar_g=1; + scalar_b=1; + } + else + { + // Make this ship brighter based on its speed + float speed_norm; + speed_norm=min(vm_GetMagnitudeFast (&objp->mtype.phys_info.velocity)/20.0,1); + speed_norm*=1; + speed_norm+=1; + scalar_r=min(1,scalar_r*speed_norm); + scalar_g=min(1,scalar_g*speed_norm); + scalar_b=min(1,scalar_b*speed_norm); + } + } + if (objp->type==OBJ_PLAYER && (Players[objp->id].flags & PLAYER_FLAGS_HEADLIGHT)) + { + scalar_r=1; + scalar_g=1; + scalar_b=1; + + } + } + RenderObject_SetGouraud (&lightdir,scalar_r,scalar_g,scalar_b); + } + else if (objp->lighting_render_type==LRT_LIGHTMAPS) + { + if (objp->lm_object.used==0) + RenderObject_SetStatic (1.0f,1.0f,1.0f); + else + RenderObject_SetLightmaps (&objp->lm_object); + } + return true; +} + +bool GetLinearPosition(vector *points,float *times,int num_points,float t,vector *pos) +{ + // find between which points is the time + int min_point; + for(min_point=0;min_pointtype==OBJ_NONE ) { + mprintf( (1, "ERROR!!!! Bogus obj %d in room %d is rendering!\n", OBJNUM(obj), obj->roomnum )); + Int3(); + return; + } + if ( obj->type==OBJ_DUMMY ) + return; + if (obj->flags & OF_ATTACHED) + { + // See if we should be rendered, because our attach parent might be invisible + object *parent_obj=ObjGet (obj->attach_ultimate_handle); + if (!parent_obj) + return; + if (parent_obj->render_type==RT_NONE && parent_obj->type != OBJ_POWERUP && (parent_obj->type==OBJ_PLAYER || parent_obj->movement_type != MT_NONE)) + return; + } + if (OBJECT_OUTSIDE(obj)) + render_it=SetupTerrainObject (obj); + else + render_it=SetupMineObject (obj); + if (!render_it) + return; + if (!(obj->flags & OF_SAFE_TO_RENDER)) + return; + // Mark this a rendered this frame + obj->flags|=OF_RENDERED; + // If we're not rendering from a mirror, mark this object as rendered + if (Render_mirror_for_room==false) + obj->flags&=~OF_SAFE_TO_RENDER; + obj->renderframe=FrameCount % 65536; + if(obj->control_type == CT_AI && + (GetFunctionMode() == GAME_MODE || GetFunctionMode() == EDITOR_GAME_MODE)) + { + AI_RenderedList[AI_NumRendered] = OBJNUM(obj); + AI_NumRendered += 1; + } + #ifdef EDITOR + ddgr_color oldcolor; + if (TSearch_on && obj->type!=OBJ_ROOM) + { + rend_SetPixel(GR_RGB(16,255,64),TSearch_x,TSearch_y); + oldcolor = rend_GetPixel(TSearch_x,TSearch_y); //will be different in 15/16-bit color + } + #endif + switch (obj->render_type) { + case RT_NONE: + break; + case RT_EDITOR_SPHERE: //to render objects in editor mode + #ifdef EDITOR + if ((GetFunctionMode() == EDITOR_MODE)) { //only render if in editor mode + if (!UseHardware) + { + g3Point sphere_point; + g3_RotatePoint(&sphere_point,&obj->pos); + g3_DrawSphere(obj->rtype.sphere_color,&sphere_point,obj->size); + } + else + { + //Let me take this opportunity to say how much it pisses me off that + //the DrawColoredDisk() function takes r,g,b as floats, when the standard + //in our graphics system is to pass color as a ddgr_color + float r = (float) GR_COLOR_RED(obj->rtype.sphere_color) / 255.0, + g = (float) GR_COLOR_GREEN(obj->rtype.sphere_color) / 255.0, + b = (float) GR_COLOR_BLUE(obj->rtype.sphere_color) / 255.0; + DrawColoredDisk (&obj->pos,r,g,b,1,1,obj->size,0); + } + } + #endif + break; + case RT_POLYOBJ: + #ifdef EDITOR + if ((GetFunctionMode() == EDITOR_MODE) && (obj-Objects == Cur_object_index)) + DrawObjectSelectionBrackets(obj,0); //draw back brackets + #endif + if(obj->rtype.pobj_info.anim_frame || (Poly_models[obj->rtype.pobj_info.model_num].frame_max != Poly_models[obj->rtype.pobj_info.model_num].frame_min)) + { + SetNormalizedTimeObj(obj, normalized_time); + RenderObject_DrawPolymodel (obj,normalized_time); + } + else + { + RenderObject_DrawPolymodel (obj,NULL); + } + + + //////////////////////////////////////////// + /////////////MOTION BLUR//////////////////// + if(Use_motion_blur && (obj->type==OBJ_ROBOT || obj->type==OBJ_DEBRIS) && Object_map_position_history[OBJNUM(obj)]!=-1) + { + float vel_mag; //velocity magnitude + float sphere_size_perc = 0.20f; // percentage of object size + float AFT = 1.0f/20.0f; //Assumed frame time + int num_iterations; //number of iterations + + vel_mag = fabs(vm_GetMagnitude(&obj->mtype.phys_info.velocity)); + num_iterations = (vel_mag*AFT)/(sphere_size_perc*obj->size); + + if(num_iterations>12) + num_iterations = 12; + + if(num_iterations>=1) + { + vector saved_pos; + float saved_alpha_fac; + vector positions[MAX_POSITION_HISTORY+1]; + float times[MAX_POSITION_HISTORY+1]; + + // save the position of the object, because we'll have to restore it + saved_pos = obj->pos; + saved_alpha_fac = rend_GetAlphaFactor(); + + int pos_slot = Object_map_position_history[OBJNUM(obj)]; + int i,c_pos = Object_position_head; + + // fill in the positions, starting with our current one + for(i=0;ipos; + times[i] = 0; + }else + { + positions[i] = Object_position_samples[pos_slot].pos[c_pos]; + times[i] = Gametime - Last_position_history_update[c_pos]; + c_pos++; + c_pos = c_pos%MAX_POSITION_HISTORY; + } + } + + float t_interval; //time interval + float alpha_step; //opacity stepping + float total_time; //total time of samples + + t_interval = AFT/((float)num_iterations+1); + alpha_step = 1.0f/(num_iterations+1); + total_time = times[MAX_POSITION_HISTORY] - times[0]; + ASSERT(total_time>0); + + float curr_alpha = 1.0f - alpha_step; + float curr_t = t_interval; + float full_time = AFT/total_time; //how much of the interval we want to go across + + for(i=0;ipos) ) + break; + + rend_SetAlphaFactor(curr_alpha); + + // render the iteration + if(obj->rtype.pobj_info.anim_frame || (Poly_models[obj->rtype.pobj_info.model_num].frame_max != Poly_models[obj->rtype.pobj_info.model_num].frame_min)) + { + RenderObject_DrawPolymodel (obj,normalized_time); + } + else + { + RenderObject_DrawPolymodel (obj,NULL); + } + + // update + curr_alpha -= alpha_step; + curr_t += t_interval; + } + + obj->pos = saved_pos; + rend_SetAlphaFactor(saved_alpha_fac); + } + } + //////////////////////////////////////////// + + #ifdef _DEBUG + if(Game_show_sphere) + { + DrawDebugInfo(obj); + } + #endif + + // Render that powerup glow + if (obj->type==OBJ_POWERUP) + { + DrawPowerupGlowDisk (obj); + DrawPowerupSparkles (obj); + } + + if (obj->type==OBJ_PLAYER) + { + DrawPlayerDamageDisk (obj); + DrawPlayerRotatingBall (obj); + DrawPlayerNameOnHud (obj); + DrawPlayerTypingIndicator (obj); + DrawPlayerInvulSphere (obj); + } + if (obj->type==OBJ_PLAYER || obj->type==OBJ_ROBOT || (obj->type == OBJ_BUILDING && obj->ai_info)) + { + DrawSparkyDamageLightning (obj); + DrawVirusLightning (obj); + } + + break; + case RT_FIREBALL: + DrawFireballObject(obj); + break; + case RT_WEAPON: + DrawWeaponObject(obj); + + #ifdef _DEBUG + if(Game_show_sphere) + { + DrawDebugInfo(obj); + } + #endif + break; + case RT_SPLINTER: + DrawSplinterObject (obj); + break; + case RT_SHARD: + DrawShardObject(obj); + break; + #ifdef _DEBUG + case RT_LINE: + { + g3Point g3p[2]; + memset(g3p, 0, 2 * sizeof(g3Point)); + //g3p[0].p3_vec = obj->pos; + //g3p[1].p3_vec = obj->rtype.line_info.end_pos; + + g3_RotatePoint(&g3p[0], &obj->pos); + g3_RotatePoint(&g3p[1], &obj->rtype.line_info.end_pos); + g3_DrawLine(GR_RGB(255,255,255),&g3p[0],&g3p[1]); + break; + } + #endif + default: Error("Unknown render_type <%d>",obj->render_type); + } + #ifdef NEWDEMO + if ( obj->render_type != RT_NONE ) + if ( Newdemo_state == ND_STATE_RECORDING ) { + if (!WasRecorded[obj-Objects]) { + newdemo_record_RenderObject(obj); + WasRecorded[obj-Objects]=1; + } + } + #endif +//?? Max_linear_depth = mld_save; + //Mark selected objects + #ifdef EDITOR + if ((GetFunctionMode() == EDITOR_MODE) && (obj-Objects == Cur_object_index)) { + if (obj->render_type != RT_POLYOBJ) { + g3Point pnt; + g3_RotatePoint(&pnt,&obj->pos); + g3_DrawBox(GR_RGB(255,255,255),&pnt,obj->size); + } + else { //polygon model + DrawObjectSelectionBrackets(obj,1); //draw front brackets + } + } + #endif //ifdef EDITOR + #ifdef EDITOR + if (TSearch_on) + { + if (rend_GetPixel(TSearch_x,TSearch_y) != oldcolor) + { + TSearch_found_type=TSEARCH_FOUND_OBJECT; + TSearch_seg = obj-Objects; + mprintf ((0,"TR:objnum=%d\n",obj-Objects)); + } + } + #endif +} +// Sets the polygon render object type to static (one lightval for whole object) +void RenderObject_SetStatic (float r,float g,float b) +{ + RenderObjectType=RO_STATIC; + RenderObjectStaticRedValue=r; + RenderObjectStaticGreenValue=g; + RenderObjectStaticBlueValue=b; + RenderObjectStaticScalar=1.0; +} +// Sets the polygon render object type to gouraud (one lightval for each vertex) +void RenderObject_SetGouraud (vector *dir,float r,float g,float b,float scalar) +{ + RenderObject_LightDirection=*dir; + RenderObjectStaticRedValue=r; + RenderObjectStaticGreenValue=g; + RenderObjectStaticBlueValue=b; + RenderObjectStaticScalar=scalar; + RenderObjectType=RO_GOURAUD; +} +// Sets the object render to draw a polymodel with lightmaps applied +void RenderObject_SetLightmaps (lightmap_object *lmobject) +{ + ASSERT (lmobject->used); + RenderObjectType=RO_LIGHTMAPS; + RenderObjectLightmapObject=lmobject; +} + + +ddgr_color Player_colors[]={GR_RGB(255,0,0),GR_RGB(0,32,255),GR_RGB(0,255,0),GR_RGB(255,255,0),GR_RGB(32,64,96), +GR_RGB(255,0,255),GR_RGB(255,128,0),GR_RGB(128,128,0),GR_RGB(0,128,0),GR_RGB(0,32,128), +GR_RGB(0,128,128),GR_RGB(128,0,128),GR_RGB(128,0,0),GR_RGB(128,128,255),GR_RGB(255,128,255), +GR_RGB(255,128,128),GR_RGB(128,64,0),GR_RGB(128,0,255),GR_RGB(0,255,128),GR_RGB(255,64,0), +GR_RGB(128,255,0),GR_RGB(255,0,128),GR_RGB(64,64,128),GR_RGB(128,64,64),GR_RGB(255,192,128), +GR_RGB(255,255,128),GR_RGB(128,128,64),GR_RGB(64,128,64),GR_RGB(128,0,64),GR_RGB(255,192,0), +GR_RGB(255,255,255),GR_RGB(128,128,128)}; + +bool is_multi_demo = false; + + +// Actually draws a polygon model based on the light parameters set by the above +// functions +void RenderObject_DrawPolymodel (object *obj,float *normalized_times) +{ + int model_num; + int use_effect=0; + vector obj_pos=obj->pos; + polymodel_effect pe={0}; + + // Do cloak effect on player + if (UseHardware) + { + if (obj->effect_info && (obj->effect_info->type_flags & EF_FADING_OUT)) + { + pe.type=PEF_ALPHA; + pe.alpha=.08+(.92*(obj->effect_info->fade_time/obj->effect_info->fade_max_time)); + use_effect=1; + } + if (obj->effect_info && (obj->effect_info->type_flags & EF_FADING_IN)) + { + pe.type=PEF_ALPHA; + pe.alpha=.08+(.92*(1.0-(obj->effect_info->fade_time/obj->effect_info->fade_max_time))); + use_effect=1; + } + if (obj->effect_info && (obj->effect_info->type_flags & EF_CLOAKED)) + { + pe.type=PEF_ALPHA|PEF_DEFORM; + pe.alpha=.13f; + pe.deform_range=.1f; + + use_effect=1; + } + if (obj->type==OBJ_PLAYER) + { + // Draw thrust/afterburner cooler + pe.type|=PEF_GLOW_SCALAR; + pe.glow_length_scalar=(Players[obj->id].thrust_mag); + pe.glow_size_scalar=(Players[obj->id].thrust_mag); + + pe.glow_length_scalar+=(Players[obj->id].afterburner_mag*3); + pe.glow_size_scalar+=(Players[obj->id].afterburner_mag*.5); + float adjustment=(ps_rand()%100-50); + adjustment/=50.0; + pe.glow_length_scalar+=(.2*adjustment); + + use_effect=1; + // Make Katmai glows longer + if (Katmai) + { + pe.glow_length_scalar*=1.5; + } + //hack to make multiplayer demos work! + if(is_multi_demo||(Game_mode & GM_MULTI)) + { + if (Num_teams>1) + { + int teamnum=PlayerGetTeam(obj->id); + + pe.type|=PEF_CUSTOM_COLOR|PEF_CUSTOM_GLOW; + pe.custom_color=Player_colors[teamnum]; + pe.glow_r=GR_COLOR_RED(pe.custom_color)/255.0; + pe.glow_g=GR_COLOR_GREEN(pe.custom_color)/255.0; + pe.glow_b=GR_COLOR_BLUE(pe.custom_color)/255.0; + + use_effect=1; + } + else + { + pe.type|=PEF_CUSTOM_COLOR; + pe.custom_color=Player_colors[obj->id]; + + use_effect=1; + } + if (Multi_logo_state && GameTextures[Players[obj->id].custom_texture_handle].bm_handle!=0) + { + pe.type|=PEF_CUSTOM_TEXTURE; + + pe.custom_texture=Players[obj->id].custom_texture_handle; + + use_effect=1; + } + + } + + } + // Deform this object if needed + if (obj->effect_info && (obj->effect_info->type_flags & EF_DEFORM)) + { + pe.type|=PEF_DEFORM; + pe.deform_range=obj->effect_info->deform_range * obj->effect_info->deform_time; + float val=obj->effect_info->deform_time; + if (val>1) + val=1; + + pe.type|=PEF_COLOR; + pe.r=1.0; + pe.g=1-(val/2); + pe.b=1-(val/2); + + use_effect=1; + } + // If the viewer is deformed, warp his view somewhat + if (Viewer_object->effect_info && (Viewer_object->effect_info->type_flags & EF_DEFORM)) + { + pe.type|=PEF_DEFORM; + pe.deform_range=Viewer_object->effect_info->deform_range * Viewer_object->effect_info->deform_time; + float val=Viewer_object->effect_info->deform_time; + if (val>1) + val=1; + + + use_effect=1; + // switch object position a little + float moveval=10.0*val*Viewer_object->effect_info->deform_range; + obj_pos.x+=(((ps_rand()%1000)-500)/500.0)*moveval; + obj_pos.y+=(((ps_rand()%1000)-500)/500.0)*moveval; + obj_pos.z+=(((ps_rand()%1000)-500)/500.0)*moveval; + } + // If this is a powerup, fade it out near the end of its life + if (obj->type==OBJ_POWERUP && (obj->flags & OF_USES_LIFELEFT) && obj->lifeleft<5) + { + pe.type|=PEF_ALPHA|PEF_DEFORM; + pe.alpha=obj->lifeleft/5.0; + pe.deform_range=.2f*(1.0-(obj->lifeleft/5.0)); + + use_effect=1; + } + // Fog this object if needed + if (!OBJECT_OUTSIDE(obj) && (Rooms[obj->roomnum].flags & RF_FOG) && Room_fog_plane_check!=-1) + { + pe.type|=PEF_FOGGED_MODEL; + pe.fog_distance=Room_fog_distance; + pe.fog_eye_distance=Room_fog_eye_distance; + pe.fog_plane_check=Room_fog_plane_check; + pe.fog_portal_vert=Room_fog_portal_vert; + pe.fog_plane=Room_fog_plane; + pe.fog_depth=Rooms[obj->roomnum].fog_depth; + pe.fog_r=Rooms[obj->roomnum].fog_r; + pe.fog_g=Rooms[obj->roomnum].fog_g; + pe.fog_b=Rooms[obj->roomnum].fog_b; + + use_effect=1; + } + // Apply specularity from dynamic lights + if (obj->effect_info) + { + if ((obj->effect_info->type_flags & EF_SPECULAR)) + { + if (obj->type==OBJ_POWERUP) + pe.type|=PEF_SPECULAR_MODEL; + else + pe.type|=PEF_SPECULAR_FACES; + + pe.spec_light_pos=obj->effect_info->spec_pos; + pe.spec_r=obj->effect_info->spec_r; + pe.spec_g=obj->effect_info->spec_g; + pe.spec_b=obj->effect_info->spec_b; + pe.spec_scalar=1.0; + use_effect=1; + } + } + // Apply specularity from outdoor satellites + if (OBJECT_OUTSIDE(obj) && obj->lighting_render_type==LRT_GOURAUD && Detail_settings.Specular_lighting && !(Object_info[obj->id].lighting_info.flags & OLF_NO_SPECULARITY)) + { + if (obj->effect_info && !(obj->effect_info->type_flags & EF_SPECULAR)) + { + if (obj->type==OBJ_POWERUP) + pe.type|=PEF_SPECULAR_MODEL; + else + pe.type|=PEF_SPECULAR_FACES; + + pe.spec_light_pos=Terrain_sky.satellite_vectors[0]; + pe.spec_r=1.0; + pe.spec_g=1.0; + pe.spec_b=1.0; + pe.spec_scalar=RenderObjectStaticScalar; + use_effect=1; + } + } + if (Detail_settings.Bumpmapping_enabled && (obj->type==OBJ_ROBOT || obj->type==OBJ_PLAYER)) + { + pe.bump_light_pos=obj->pos; + pe.bump_light_pos.y+=100; + pe.bump_scalar=1; + pe.type|=PEF_BUMPMAPPED; + use_effect=1; + } + + } + // Pick a lod model to use if eligible + if (obj->lighting_render_type==LRT_STATIC || obj->lighting_render_type==LRT_GOURAUD) + { + if (obj->type==OBJ_POWERUP || obj->type==OBJ_ROBOT || obj->type==OBJ_CLUTTER) + { + g3Point pnt; + float detail_scalar=1.0; + g3_RotatePoint (&pnt,&obj->pos); + if (Detail_settings.Object_complexity==0) + detail_scalar=.6f; + else if (Detail_settings.Object_complexity==2) + detail_scalar=1.2f; + if (pnt.p3_z<(Object_info[obj->id].med_lod_distance*detail_scalar)) + model_num=obj->rtype.pobj_info.model_num; + else if (pnt.p3_z<(Object_info[obj->id].lo_lod_distance*detail_scalar)) + { + if (Object_info[obj->id].med_render_handle!=-1) + model_num=Object_info[obj->id].med_render_handle; + else + { + model_num=obj->rtype.pobj_info.model_num; + } + } + else + { + if (Object_info[obj->id].lo_render_handle!=-1) + model_num=Object_info[obj->id].lo_render_handle; + else + { + model_num=obj->rtype.pobj_info.model_num; + if (Object_info[obj->id].med_render_handle!=-1) + model_num=Object_info[obj->id].med_render_handle; + else + model_num=obj->rtype.pobj_info.model_num; + } + } + } + else if (obj->type==OBJ_MARKER) + { + model_num=Marker_polynum; + } + else if (obj->type==OBJ_PLAYER && !(Players[obj->id].flags & (PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD))) + { + g3Point pnt; + g3_RotatePoint (&pnt,&obj->pos); + int ship_num=Players[obj->id].ship_index; + float detail_scalar=1.0; + if (Detail_settings.Object_complexity==0) + detail_scalar=.6f; + else if (Detail_settings.Object_complexity==2) + detail_scalar=1.2f; + if (pnt.p3_z<(Ships[ship_num].med_lod_distance * detail_scalar)) + model_num=obj->rtype.pobj_info.model_num; + else if (pnt.p3_z<(Ships[ship_num].lo_lod_distance * detail_scalar)) + { + if (Ships[ship_num].med_render_handle!=-1) + model_num=Ships[ship_num].med_render_handle; + else + { + model_num=obj->rtype.pobj_info.model_num; + } + } + else + { + if (Ships[ship_num].lo_render_handle!=-1) + model_num=Ships[ship_num].lo_render_handle; + else + { + model_num=obj->rtype.pobj_info.model_num; + if (Ships[ship_num].med_render_handle!=-1) + model_num=Ships[ship_num].med_render_handle; + else + model_num=obj->rtype.pobj_info.model_num; + } + } + } + else + model_num=obj->rtype.pobj_info.model_num; + } + else + model_num=obj->rtype.pobj_info.model_num; + if (obj->type==OBJ_BUILDING && obj->flags & OF_USE_DESTROYED_POLYMODEL) + { + if (Object_info[obj->id].lo_render_handle!=-1) + model_num=Object_info[obj->id].lo_render_handle; + } + // Set effects + if (use_effect) + SetPolymodelEffect (&pe); + if (RenderObjectType==RO_STATIC) + { + // Draw this object with static light + int overlay = 0; + if((obj->type == OBJ_ROBOT || obj->type==OBJ_PLAYER) && obj->rtype.pobj_info.subobj_flags != 0xFFFFFFFF) + overlay = 1; + + DrawPolygonModel (&obj_pos,&obj->orient,model_num,normalized_times,0, + RenderObjectStaticRedValue, RenderObjectStaticGreenValue,RenderObjectStaticBlueValue, + obj->rtype.pobj_info.subobj_flags,use_effect, overlay); + } + else if (RenderObjectType==RO_GOURAUD || NoLightmaps) + { + // Draw this object with gouraud static light + int overlay = 0; + if((obj->type == OBJ_ROBOT || obj->type==OBJ_PLAYER) && obj->rtype.pobj_info.subobj_flags != 0xFFFFFFFF) + overlay = 1; + DrawPolygonModel (&obj_pos,&obj->orient,model_num,normalized_times,0, &RenderObject_LightDirection, + RenderObjectStaticRedValue, RenderObjectStaticGreenValue,RenderObjectStaticBlueValue, + obj->rtype.pobj_info.subobj_flags,use_effect,overlay); + } + else if (RenderObjectType==RO_LIGHTMAPS) + { + int overlay = 0; + if((obj->type == OBJ_ROBOT || obj->type==OBJ_PLAYER) && obj->rtype.pobj_info.subobj_flags != 0xFFFFFFFF) + overlay = 1; + // If this object is a destroyed building then do something different with it + DrawPolygonModel (&obj_pos,&obj->orient,model_num,normalized_times,0, RenderObjectLightmapObject, obj->rtype.pobj_info.subobj_flags,use_effect, overlay); + } + else + Int3(); // Get Jason +} +// Sets the direction of the lightsource to be used when calculating vertex lighting +// The light source vector should be in the models coordinate space +void RenderObject_SetLightDirection(vector *dir) +{ + RenderObject_LightDirection=*dir; +} +//draw an object that has one bitmap & doesn't rotate +void obj_draw_blob(object *obj,int bmnum) +{ +// int orientation=0; +// grs_bitmap * bm = &GameBitmaps[bmnum]; +// +// +// if (obj->type == OBJ_FIREBALL) +// orientation = (obj-Objects) & 7; +// +// orientation = global_orientation; +// +// PIGGY_PAGE_IN( bmi ); +// +// if (bm->bm_w > bm->bm_h) +// +// g3_draw_bitmap(&obj->pos,obj->size,fixmuldiv(obj->size,bm->bm_h,bm->bm_w),bm, orientation); +// +// else +// +// g3_draw_bitmap(&obj->pos,fixmuldiv(obj->size,bm->bm_w,bm->bm_h),obj->size,bm, orientation); +} +extern float Far_clip_z; +int Point_visible_last_frame=-1; +// Given a position in 3space and a size, returns whether or not that sized point is +// visible from the current view matrix +int IsPointVisible (vector *pos,float size,float *pointz) +{ + g3Point pnt; + ubyte ccode; + static float last_render_fov=-1; + static vector left_normal,right_normal,top_normal,bottom_normal,view_position; + static matrix unscaled_matrix; + + g3_RotatePoint(&pnt,pos); + ccode=g3_CodePoint (&pnt); + if (pointz!=NULL) + *pointz=pnt.p3_z; + if (ccode) // the center point is off, find out if the whole object is off + { + float dotp; + + if (pnt.p3_z<-size) + return 0; // behind plane! + if (pnt.p3_z>Far_clip_z+size) + return 0; // too far away! + if (Render_FOV!=last_render_fov) + { + last_render_fov=Render_FOV; + int angle_adjust=(Render_FOV/2)*(65536/360); + vector rvec={1,0,0}; + vector lvec={-1,0,0}; + vector tvec={0,1,0}; + vector bvec={0,-1,0}; + matrix temp_mat; + vm_AnglesToMatrix (&temp_mat,0,65536-angle_adjust,0); + right_normal=rvec*temp_mat; + vm_AnglesToMatrix (&temp_mat,0,angle_adjust,0); + left_normal=lvec*temp_mat; + vm_AnglesToMatrix (&temp_mat,65536-angle_adjust,0,0); + bottom_normal=bvec*temp_mat; + vm_AnglesToMatrix (&temp_mat,angle_adjust,0,0); + top_normal=tvec*temp_mat; + } + // Get unscaled matrix stuff + if (Point_visible_last_frame!=FrameCount) + { + Point_visible_last_frame=FrameCount; + g3_GetUnscaledMatrix (&unscaled_matrix); + g3_GetViewPosition (&view_position); + } + vector temp_vec = *pos - view_position; + pnt.p3_vec = temp_vec * unscaled_matrix; + if (ccode & CC_OFF_RIGHT) + { + dotp=vm_DotProduct (&right_normal,&pnt.p3_vec); + if (dotp>size) + return 0; + } + if (ccode & CC_OFF_LEFT) + { + dotp=vm_DotProduct (&left_normal,&pnt.p3_vec); + if (dotp>size) + return 0; + } + + if (ccode & CC_OFF_TOP) + { + dotp=vm_DotProduct (&top_normal,&pnt.p3_vec); + if (dotp>size) + return 0; + } + if (ccode & CC_OFF_BOT) + { + dotp=vm_DotProduct (&bottom_normal,&pnt.p3_vec); + if (dotp>size) + return 0; + } + return 1; + } + // Center point is on, must be visible + return 1; +} + +//Do powerup sparkles and stuff +void DrawPowerupSparkles(object *obj) +{ + extern bool Render_mirror_for_room; + if(!Render_powerup_sparkles) + return; + + if(Game_paused) + return; + + // we want to render if + // a) we are not rendering in a mirror + // b) we are rendering a mirror but the object is not visible from our position + if(Render_mirror_for_room && !IsPointVisible(&obj->pos,obj->size,NULL)) + return; + + if(obj->render_type != RT_POLYOBJ) + return; + + // check to see if it's time to update + if( Last_powerup_sparkle_time < POWERUP_SPARKLE_INTERVAL ) + return; + + int num_sparks = ps_rand() & 7; + float obj_size_delta = obj->size*0.3f; + + // Create some sparks + for( int i = 0; i < num_sparks; ++i ) + { + const int randnum = ps_rand() % 10; + + int index; + switch(randnum) + { + case 0: + index=HOT_SPARK_INDEX; + break; + case 1: + index=COOL_SPARK_INDEX; + break; + default: + index = GRAY_SPARK_INDEX; + break; + } + + vector pos_delta; + pos_delta.x = (ps_rand()%100) - 50; + pos_delta.y = (ps_rand()%100) - 80; + pos_delta.z = (ps_rand()%100) - 50; + vm_NormalizeVector( &pos_delta ); + pos_delta = obj->last_pos + ( obj_size_delta * pos_delta ); + + int sparknum = VisEffectCreate( VIS_FIREBALL, index, obj->roomnum, &pos_delta ); + if (sparknum>=0) + { + vis_effect *vis=&VisEffects[sparknum]; + + vis->end_pos = vis->pos; + vis->movement_type = MT_PHYSICS; + vis->mass = 1000.0f; + vis->drag = 0.2f; + vis->phys_flags |= PF_GRAVITY|PF_NO_COLLIDE; + + vis->velocity.x = (ps_rand()%50)-25; + vis->velocity.y = -10 - (ps_rand()%20); + vis->velocity.z = (ps_rand()%50)-25; + vm_NormalizeVector( &vis->velocity ); + vis->velocity *= 3.0f + (ps_rand()%4); + + vis->size = 0.05f+ ((ps_rand()%10)*0.008f); + vis->flags |= VF_USES_LIFELEFT; + + const float lifetime = 0.25f + ((ps_rand()%10)*0.05f); + vis->lifeleft = lifetime; + vis->lifetime = lifetime; + + light_info *li = ObjGetLightInfo(obj); + if(li) + { + vis->lighting_color = GR_RGB16(li->red_light1*255.0f,li->green_light1*255.0f,li->blue_light1*255.0f); + } + else + { + vis->lighting_color = GR_RGB16(255,255,255); + } + } + } +} + +#define POWERUP_HALO_ALPHA .3f +// Draws the glowing disk around a powerup +void DrawPowerupGlowDisk (object *obj) +{ + ASSERT (obj->type==OBJ_POWERUP); + if (!Detail_settings.Powerup_halos) + return; + if (obj->render_type==RT_NONE) + return; + if (Viewer_object->effect_info && Viewer_object->effect_info->type_flags & EF_DEFORM) + return; + light_info *li = ObjGetLightInfo(obj); + int objnum=obj-Objects; + float size_adjust=FixSin((objnum*5000)+(FrameCount*600)); + size_adjust+=1.0; + size_adjust/=2; // now in range 0 to 1.0 + size_adjust+=.5; + vector subvec=obj->pos-Viewer_object->pos; + float mag=vm_GetMagnitudeFast (&subvec); + if (mag<40) + return; + + float alpha_scalar=1; + if (obj->flags & OF_USES_LIFELEFT) + { + if (obj->lifeleft<5) + alpha_scalar=(obj->lifeleft/5.0); + } + if (mag<70) + { + alpha_scalar*=((mag-40)/30.0); + } + + // Draw lo-res disk + DrawColoredDisk (&obj->pos,li->red_light1,li->green_light1,li->blue_light1,POWERUP_HALO_ALPHA*alpha_scalar,0.0f,(obj->size/2)+size_adjust,0,2); +} +// Draws the glowing disk around a player indicating damage +void DrawPlayerDamageDisk (object *obj) +{ + //if (!(Game_mode & GM_MULTI)) + // return; // only in multiplayer + + ASSERT (obj->type==OBJ_PLAYER); + if (obj==Viewer_object) + return; // don't do me + float damage_norm=Players[obj->id].damage_magnitude/MAX_DAMAGE_MAG; + if (damage_norm>1.0) + damage_norm=1.0; + if (damage_norm<.001) + return; + rend_SetZBias (-obj->size); + int bm_handle; + int objnum=obj-Objects; + float rot_temp=.25; // Higher is faster + int int_game=Gametime/rot_temp; + float diff=Gametime-(int_game*rot_temp); + int rot_angle=diff*65536; + rot_angle=0; + bm_handle=Fireballs[SHIP_HIT_INDEX].bm_handle; + + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetZBufferWriteMask (0); + + rend_SetAlphaValue (.4*damage_norm*255); + rend_SetOverlayType (OT_NONE); + rend_SetLighting(LS_NONE); + g3_DrawRotatedBitmap (&obj->pos,rot_angle,obj->size,(obj->size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + rend_SetZBufferWriteMask (1); + rend_SetZBias (0); +} +// Draws the red sphere around a player indicating invulnerability +void DrawPlayerInvulSphere (object *obj) +{ + //if (!(Game_mode & GM_MULTI)) + // return; // only in multiplayer + + ASSERT (obj->type==OBJ_PLAYER); + if (Viewer_object==obj) + return; // don't do me + if (!(Players[obj->id].flags & PLAYER_FLAGS_INVULNERABLE)) + return; + if (Players[obj->id].invul_magnitude<.05) + return; + + int bm_handle=Fireballs[INVUL_HIT_INDEX].bm_handle; + // Figure out the position and orientation to draw this + vector hit_pos=obj->pos+(Players[obj->id].invul_vector*obj->size); + vector norm_vec=Players[obj->id].invul_vector; + // Check to see that we're ok with the length of the vector + float mag=vm_GetMagnitudeFast (&norm_vec); + if (mag<1) + return; + norm_vec/=mag; + + // Now draw! + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetZBufferWriteMask (0); + rend_SetAlphaValue (.6*Players[obj->id].invul_magnitude*255); + rend_SetOverlayType (OT_NONE); + rend_SetLighting(LS_NONE); + g3_DrawPlanarRotatedBitmap (&hit_pos,&norm_vec,0,3,3,bm_handle); + rend_SetZBufferWriteMask (1); +} +//Draws a rotating ball around the player +void DrawPlayerRotatingBall (object *obj) +{ + ASSERT (obj->type==OBJ_PLAYER); + if (Players[obj->id].num_balls<1) + return; + static int first=1; + static int bm_handle; + vector worldpos; + rend_SetColorModel (CM_RGB); + rend_SetLighting (LS_GOURAUD); + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (.3*255); + rend_SetZBufferWriteMask (0); + if (first) + { + int texhandle=FindTextureName("WhiteGlowingBall"); + if (texhandle==-1) + bm_handle=0; + else + bm_handle=GetTextureBitmap (texhandle,0); + first=0; + } + for (int i=0;iid].num_balls;i++) + { + PlayerGetBallPosition (&worldpos,obj->id,i); + // Get color of balls + ddgr_color color=GR_RGB(Players[obj->id].ball_r[i]*255,Players[obj->id].ball_g[i]*255,Players[obj->id].ball_b[i]*255); + g3_DrawBitmap (&worldpos,obj->size/4,((obj->size/4)*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color); + } + rend_SetZBufferWriteMask (1); +} +// Draws the 'typing message' icon indicator above an object +void DrawPlayerTypingIndicator (object *obj) +{ + if (!(Game_mode & GM_MULTI)) + return; + int slot=obj->id; + if (slot==Player_num) + return; + if (NetPlayers[slot].sequence!=NETSEQ_PLAYING) + return; + uint bit = (0x01<effect_info && obj->effect_info->type_flags & EF_CLOAKED) + return; + static int type_indicator_model = -2; + if(type_indicator_model==-2) + type_indicator_model = FindTextureName("TypingIndicator"); + if(type_indicator_model<0) + return; + int bm_handle=GetTextureBitmap(type_indicator_model,0); + float IndicatorTan; + float rad=(float)(3.14*(float)(10)/180.0); + IndicatorTan=tan(rad); + // See if its in our viewcone + vector subvec=obj->pos-Player_object->pos; + vm_NormalizeVectorFast (&subvec); + if ((vm_DotProduct (&subvec,&Player_object->orient.fvec))pos,&obj->pos)>800.0f)) + return; + //Find out if it is o.k. to draw here. + fvi_query fq; + fvi_info hit_data; + int fate; + fq.p0 = &Player_object->pos; + fq.startroom = Player_object->roomnum; + fq.p1 = &obj->pos; + fq.rad = 0; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS; + fate = fvi_FindIntersection(&fq, &hit_data); + if (fate==HIT_NONE || (fate == HIT_SPHERE_2_POLY_OBJECT && hit_data.hit_object[0]==(obj-Objects))) + { + // Draw this indicator on the hud + g3Point pnt; + int bmh,bmw; + g3Point *pntlist[32],points[4]; + g3Codes cc; + ubyte code; + + cc.cc_and = 0xFF; + cc.cc_or = 0; + + int b_w = min(bm_w(bm_handle,0),32); + int b_h = min(bm_h(bm_handle,0),32); + bmw = b_w/2; + bmh = b_h/2; + + g3_RotatePoint (&pnt,fq.p1); + g3_ProjectPoint (&pnt); + + points[0].p3_vec.x = pnt.p3_vec.x - bmw; + points[0].p3_vec.y = pnt.p3_vec.y + bmh + 5; + points[0].p3_vec.z = pnt.p3_vec.z; + points[0].p3_u=0; + points[0].p3_v=0; + + points[1].p3_vec.x = pnt.p3_vec.x + bmw; + points[1].p3_vec.y = pnt.p3_vec.y + bmh + 5; + points[1].p3_vec.z = pnt.p3_vec.z; + points[1].p3_u=1; + points[1].p3_v=0; + + points[2].p3_vec.x = pnt.p3_vec.x + bmw; + points[2].p3_vec.y = pnt.p3_vec.y - bmh + 5; + points[2].p3_vec.z = pnt.p3_vec.z; + points[2].p3_u=1; + points[2].p3_v=1; + + points[3].p3_vec.x = pnt.p3_vec.x - bmw; + points[3].p3_vec.y = pnt.p3_vec.y - bmh + 5; + points[3].p3_vec.z = pnt.p3_vec.z; + points[3].p3_u=0; + points[3].p3_v=1; + + for (int i=0;i<4;i++) + { + points[i].p3_flags|=PF_UV; + pntlist[i]=&points[i]; + code = g3_CodePoint(pntlist[i]); + g3_ProjectPoint(pntlist[i]); + + cc.cc_and &= code; + cc.cc_or |= code; + } + rend_SetAlphaType(AT_CONSTANT_TEXTURE); + rend_SetAlphaValue (200); + rend_SetWrapType (WT_CLAMP); + rend_SetLighting (LS_NONE); + rend_SetOverlayType (OT_NONE); + rend_SetColorModel (CM_MONO); + rend_SetTextureType (TT_LINEAR); + g3_DrawPoly(4,pntlist,bm_handle,0,&cc); + } +} +// Draws the player name on the hud +void DrawPlayerNameOnHud (object *obj) +{ + if (!(Game_mode & GM_MULTI)) + return; + int slot=obj->id; + int color; + if (slot==Player_num) + return; + if (NetPlayers[slot].sequence!=NETSEQ_PLAYING) + return; + if (HudNameTan<=0) + return; + if (obj->effect_info && obj->effect_info->type_flags & EF_CLOAKED) + return; + + // Get color to draw this name in + DLLInfo.me_handle=obj->handle; + CallGameDLL (EVT_CLIENT_GETCOLOREDNAME,&DLLInfo); + color=DLLInfo.iRet; + if (color<0) + return; + + // See if its in our viewcone + vector subvec=obj->pos-Player_object->pos; + vm_NormalizeVectorFast (&subvec); + if ((vm_DotProduct (&subvec,&Player_object->orient.fvec))pos; + fq.startroom = Player_object->roomnum; + fq.p1 = &obj->pos; + fq.rad = 0; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS; + fate = fvi_FindIntersection(&fq, &hit_data); + if (fate==HIT_NONE || (fate == HIT_SPHERE_2_POLY_OBJECT && hit_data.hit_object[0]==(obj-Objects))) + { + int half=Game_window_w/2; + // Draw this name on the hud + g3Point pnt; + g3_RotatePoint (&pnt,fq.p1); + g3_ProjectPoint (&pnt); + // put a centered name string in the text buffer. + + grtext_SetFont (HUD_FONT); + grtext_SetColor (color); + grtext_CenteredPrintf(pnt.p3_sx-half, pnt.p3_sy, Players[slot].callsign); + grtext_Flush(); + } +} +// Creates lightning sparks on a damaged object +void DrawSparkyDamageLightning (object *obj) +{ + float max_shields=100; + if (obj->type==OBJ_ROBOT || obj->type==OBJ_CLUTTER || obj->type==OBJ_BUILDING) + max_shields=Object_info[obj->id].hit_points; + if (obj->type==OBJ_PLAYER) + max_shields=INITIAL_SHIELDS; + // If less than 30% shields, make some lightning every now and then + if (max_shields>0 && (obj->shields/max_shields)<.3 && obj->shields>=0) + { + float shield_norm=(obj->shields/max_shields)/.3; + shield_norm=1-shield_norm; + ASSERT (shield_norm>=0 && shield_norm<=1); + int shield_modulo=20-(12*shield_norm); + if (shield_modulo<1) + shield_modulo=1; + if ((ps_rand()%shield_modulo)==0) + { + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + if (pm->n_models==0) + return; + int subnum=ps_rand()%pm->n_models; + if (IsNonRenderableSubmodel (pm,subnum)) + return; + + bsp_info *sm=&pm->submodel[subnum]; + if (sm->nverts==0) + return; + int visnum=VisEffectCreate (VIS_FIREBALL,LIGHTNING_BOLT_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=1.0; + vis->lifetime=1.0; + vis->end_pos=obj->pos; + vis->velocity.x=.15f; + vis->velocity.y=3; + vis->attach_info.obj_handle=obj->handle; + vis->attach_info.subnum=subnum; + vis->attach_info.vertnum=ps_rand()%sm->nverts; + vis->attach_info.end_vertnum=ps_rand()%sm->nverts; + vis->attach_info.modelnum=obj->rtype.pobj_info.model_num; + vis->flags=VF_USES_LIFELEFT|VF_EXPAND|VF_ATTACHED; + } + } + } +} + +// Creates Virus infected lightning on an object +void DrawVirusLightning (object *obj) +{ + if(!obj->effect_info) + return; + if(!(obj->effect_info->type_flags&EF_VIRUS_INFECTED)) + return; + + float shield_norm = (float)(ps_rand()%RAND_MAX)/(float)RAND_MAX; + shield_norm=1-shield_norm; + + ASSERT (shield_norm>=0 && shield_norm<=1); + int shield_modulo=20-(16*shield_norm); + if (shield_modulo<1) + shield_modulo=1; + if ((ps_rand()%shield_modulo)==0) + { + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + if (pm->n_models==0) + return; + int subnum=ps_rand()%pm->n_models; + if (IsNonRenderableSubmodel (pm,subnum)) + return; + + bsp_info *sm=&pm->submodel[subnum]; + if (sm->nverts==0) + return; + int visnum=VisEffectCreate (VIS_FIREBALL,GRAY_LIGHTNING_BOLT_INDEX,obj->roomnum,&obj->pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->lifeleft=1.0; + vis->lifetime=1.0; + vis->end_pos=obj->pos; + vis->velocity.x=.15f; + vis->velocity.y=3; + vis->attach_info.obj_handle=obj->handle; + vis->attach_info.subnum=subnum; + vis->attach_info.vertnum=ps_rand()%sm->nverts; + vis->attach_info.end_vertnum=ps_rand()%sm->nverts; + vis->attach_info.modelnum=obj->rtype.pobj_info.model_num; + vis->flags=VF_USES_LIFELEFT|VF_EXPAND|VF_ATTACHED; + vis->lighting_color = GR_RGB16(40,250,40); + } + } +} + +void DrawPlayerSightVector (object *obj) +{ + if (obj->type!=OBJ_PLAYER || obj->id!=Player_num) + return; + if (Player_has_camera==0 || Player_camera_objnum==-1) + return; + // Cast a ray to see where it ends up + fvi_query fq; + fvi_info hit_data; + int fate; + vector end_pos=obj->pos+(obj->orient.fvec*500); + + fq.p0 = &obj->pos; + fq.startroom = obj->roomnum; + fq.p1 = &end_pos; + fq.rad = 0; + fq.thisobjnum = -1; + fq.ignore_obj_list = NULL; + fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS; + fate = fvi_FindIntersection(&fq, &hit_data); + rend_SetAlphaType (AT_VERTEX); + rend_SetTextureType (TT_FLAT); + rend_SetLighting(LS_GOURAUD); + rend_SetColorModel (CM_RGB); + vector vecs[2]; + g3Point pnts[2]; + + vecs[0]=obj->pos; + vecs[1]=hit_data.hit_pnt; + for (int i=0;i<2;i++) + { + g3_RotatePoint (&pnts[i],&vecs[i]); + pnts[i].p3_flags|=PF_RGBA; + pnts[i].p3_r=1; + pnts[i].p3_g=0; + pnts[i].p3_b=0; + } + pnts[0].p3_a=.1f; + pnts[1].p3_a=.05f; + g3_DrawSpecialLine (&pnts[0],&pnts[1]); +} diff --git a/Descent3/renderobject.h b/Descent3/renderobject.h new file mode 100644 index 000000000..82bab4cf6 --- /dev/null +++ b/Descent3/renderobject.h @@ -0,0 +1,42 @@ +#ifndef RENDEROBJECT_H +#define RENDEROBJECT_H + +#include "object.h" +#include "polymodel.h" + +#ifdef _DEBUG +void DrawDebugInfo(object *obj); +void DrawRoomVisPnts(object *obj); +#endif + +// Render an object. Calls one of several routines based on type +void RenderObject(object *obj); + +// Sets the polygon render object type to static (one lightval for whole object) +void RenderObject_SetStatic (float r,float g,float b); + +// Sets the polygon render object type to gouraud +void RenderObject_SetGouraud (vector *dir,float r,float g,float b,float scalar=1.0); + +// Sets the object render to draw a polymodel with lightmaps applied +void RenderObject_SetLightmaps (lightmap_object *lmobject); + +// Actually draws a polygon model based on the light parameters set by the above +// functions +void RenderObject_DrawPolymodel (object *obj,float *normalized_times); + +// Sets the direction of the lightsource to be used when calculating vertex lighting +// The light source vector should be in the models coordinate space +void RenderObject_SetLightDirection(vector *dir); + +// Given a position in 3space and a size, returns whether or not that sized point is +// visible from the current view matrix +int IsPointVisible (vector *,float,float *pointz=NULL); + +// Draws the 'typing message' icon indicator above an object +void DrawPlayerTypingIndicator (object *obj); + +#define POWERUP_SPARKLE_INTERVAL 1.0f/30.0f +extern float Last_powerup_sparkle_time; + +#endif \ No newline at end of file diff --git a/Descent3/resource.h b/Descent3/resource.h new file mode 100644 index 000000000..6bd424a85 --- /dev/null +++ b/Descent3/resource.h @@ -0,0 +1,24 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by main.rc +// +#define ID_TXT_WINNT 1 +#define ID_TXT_WIN32 2 +#define ID_TXT_DX95 4 +#define ID_TXT_DXNT4 5 +#define ID_TXT_DXVER 6 +#define ID_TXT_DXERR 7 +#define ID_TXT_DXNTERR 8 +#define ID_TXT_NOKATMAI 9 +#define IDI_D3ICON 102 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Descent3/robot.h b/Descent3/robot.h new file mode 100644 index 000000000..59cdf30ef --- /dev/null +++ b/Descent3/robot.h @@ -0,0 +1,67 @@ +#ifndef ROBOT_H +#define ROBOT_H + +#include "pstypes.h" +#include "manage.h" +#include "object.h" +#include "objinfo.h" + +#define MAX_ROBOTS 100 + +extern int Num_robots; +extern object_info Robots[MAX_ROBOTS]; +extern char *Movement_class_names[]; +extern char *Anim_state_names[]; + +// Sets all robots to unused +void InitRobots (); + +// Allocs a robot for use, returns -1 if error, else index on success +int AllocRobot (); + +// Frees robot index n +void FreeRobot (int n); + +// Gets next robot from n that has actually been alloced +int GetNextRobot (int n); + +// Gets previous robot from n that has actually been alloced +int GetPrevRobot (int n); + +// Searches thru all robots for a specific name, returns -1 if not found +// or index of robot with name +int FindRobotName (char *name); + +// Given a filename, loads either the model or vclip found in that file. If type +// is not NULL, sets it to 1 if file is model, otherwise sets it to zero +int LoadRobotImage (char *filename); + +// Given a robot handle, returns that robots image for framenum +int GetRobotImage (int handle); + +// Given an object, renders the representation of this robot +// Currently only handles bitmap types, not poly models +void DrawRobotObject (object *obj); + +// Given a robot name, assigns that robot to a specific index into +// the Robots array. Returns -1 if the named robot is not found, 0 if the robot +// is already in its place, or 1 if successfully moved +int MatchRobotToIndex (char *name,int dest_index); + +// Moves a robot from a given index into a new one (above MAX_STATIC_POWERUPS) +// returns new index +int MoveRobotFromIndex (int index); + +// This is a very confusing function. It takes all the robots that we have loaded +// and remaps then into their proper places (if they are static). +void RemapRobots (); + +// goes thru every entity that could possible have a robot index (ie objects, robots, etc) +// and changes the old index to the new index +void RemapAllRobotObjects (int old_index,int new_index); + + + +#endif + + diff --git a/Descent3/robotfire.cpp b/Descent3/robotfire.cpp new file mode 100644 index 000000000..a99804e26 --- /dev/null +++ b/Descent3/robotfire.cpp @@ -0,0 +1,426 @@ +#include "robotfire.h" +#include "object.h" +#include "objinfo.h" + +#include "config.h" // for game toggles. +#include "objinfo.h" +#include "weapon.h" +#include "ship.h" +#include "game.h" +#include "polymodel.h" +#include "sounds.h" +#include "hlsoundlib.h" +#include "multi.h" +#include "player.h" +#include "demofile.h" +#include "SmallViews.h" +#include "PHYSICS.H" +#include "AIMain.h" + +#include + +#include "psrand.h" +// Fires a multiplayer and AI on/off weapon +void FireOnOffWeapon (object *objp) +{ + if(objp->type == OBJ_PLAYER) + { + int slot = objp->id; + + int ship_index=Players[slot].ship_index; + int wb_index=Players[slot].weapon[PW_PRIMARY].index; + + if (WBIsBatteryReady(objp,&Ships[ship_index].static_wb[wb_index],wb_index)) + { + WBFireBattery(objp, &Ships[ship_index].static_wb[wb_index], 0, wb_index); + } + } + else if(objp->ai_info) + { + char wb_index = objp->ai_info->last_special_wb_firing; + if(wb_index > MAX_WBS_PER_OBJ) { //DAJ + mprintf((2, "FireOnOffWeapon wb_index %d > MAX_WBS_PER_OBJ\n", wb_index)); + return; + } + + if (WBIsBatteryReady(objp, &Object_info[objp->id].static_wb[wb_index], wb_index)) + { + WBFireBattery(objp, &Object_info[objp->id].static_wb[wb_index], 0, wb_index); + } + } + +} + +void WBSetupFireAnim(object *obj, otype_wb_info *static_wb, int wb_index) +{ + dynamic_wb_info *p_dwb = &obj->dynamic_wb[wb_index]; + + p_dwb->wb_anim_mask = p_dwb->cur_firing_mask; + + if(static_wb->anim_start_frame != static_wb->anim_end_frame) + { + p_dwb->flags |= DWBF_ANIMATING; + p_dwb->wb_anim_frame = static_wb->anim_start_frame[p_dwb->cur_firing_mask]; + } + + if(static_wb->anim_start_frame[p_dwb->cur_firing_mask] == static_wb->anim_fire_frame[p_dwb->cur_firing_mask]) + { + WBFireBattery(obj, static_wb, wb_index); + p_dwb->flags |= DWBF_ANIM_FIRED; + } +} + +void WBFireAnimFrame(object *obj, otype_wb_info *static_wb, int wb_index) +{ + dynamic_wb_info *p_dwb = &obj->dynamic_wb[wb_index]; + const int cur_mask = p_dwb->wb_anim_mask; + + p_dwb->wb_anim_frame += Frametime * ((static_wb->anim_end_frame[cur_mask] - static_wb->anim_start_frame[cur_mask])/static_wb->anim_time[cur_mask]); + + if((p_dwb->flags & DWBF_ANIM_FIRED) == 0) + { + if(p_dwb->wb_anim_frame >= static_wb->anim_fire_frame[cur_mask]) + { + WBFireBattery(obj, static_wb, wb_index); + p_dwb->flags |= DWBF_ANIM_FIRED; + } + } + + if(p_dwb->wb_anim_frame >= static_wb->anim_end_frame[cur_mask]) + { + p_dwb->wb_anim_frame = 0.0; + p_dwb->flags &= ~(DWBF_ANIM_FIRED|DWBF_ANIMATING); + } +} + +bool WBIsBatteryReady(object *obj, otype_wb_info *static_wb, int wb_index) +{ + dynamic_wb_info *p_dwb = &obj->dynamic_wb[wb_index]; + float scalar=1.0; + float quad_time = 0.0f; + + if (obj->type==OBJ_PLAYER) + scalar=Players[obj->id].weapon_recharge_scalar; + + if(p_dwb->flags & DWBF_QUAD) + { + // Add 25% on the firing time of quaded weapons + quad_time = static_wb->gp_fire_wait[p_dwb->cur_firing_mask] * 0.25f; + } + + if(!(p_dwb->flags & DWBF_ANIMATING) && + (p_dwb->last_fire_time + (static_wb->gp_fire_wait[p_dwb->cur_firing_mask]*scalar) + quad_time) < Gametime) + { + return true; + } + + return false; +} + +#include "stringtable.h" + +// Allows for multiple configurations of a single wb +void WBFireBattery(object *obj, otype_wb_info *static_wb, int poly_wb_index, int dynamic_wb_index,float damage_scalar) +{ + int cur_m_bit; + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + vector ave_pos; + int num_fired = 0; + vm_MakeZero(&ave_pos); + pos_state cur_pos; + dynamic_wb_info *p_dwb = &obj->dynamic_wb[dynamic_wb_index]; + int weapon_obj; + int must_send=0; + int first=1; + int saved_weapon_id; + + unsigned char fire_mask; + + if(p_dwb->flags & DWBF_QUAD) + fire_mask = static_wb->gp_quad_fire_mask; + else + fire_mask = static_wb->gp_fire_masks[p_dwb->cur_firing_mask]; + + for(cur_m_bit = 0; cur_m_bit < pm->poly_wb[poly_wb_index].num_gps; cur_m_bit++) + { + if(fire_mask & (0x01 << cur_m_bit)) + { + saved_weapon_id = static_wb->gp_weapon_index[cur_m_bit]; + weapon_obj = FireWeaponFromObject(obj, static_wb->gp_weapon_index[cur_m_bit], pm->poly_wb[poly_wb_index].gp_index[cur_m_bit], (static_wb->flags & WBF_FIRE_FVEC) != 0, (static_wb->flags & WBF_FIRE_TARGET) != 0); + + if (first) + { + first=0; + if (obj->type==OBJ_PLAYER && obj->id==Player_num && Weapons[static_wb->gp_weapon_index[cur_m_bit]].recoil_force>0) + { + vector force_vec=obj->orient.fvec*-Weapons[static_wb->gp_weapon_index[cur_m_bit]].recoil_force; + phys_apply_force (obj,&force_vec); + } + } + + if(weapon_obj >= 0) + { + ave_pos += Objects[weapon_obj].pos; + Objects[weapon_obj].ctype.laser_info.multiplier=damage_scalar; + num_fired++; + + // If this is a permissable game, accelerate this object for one frame by the client ping time + if ((Game_mode & GM_MULTI) && Netgame.local_role==LR_CLIENT && (Netgame.flags & NF_PERMISSABLE)) + Objects[weapon_obj].flags|=OF_PING_ACCELERATE; + + if (Weapons[Objects[weapon_obj].id].flags & WF_COUNTERMEASURE) + must_send=1; + } + } + } + + if(num_fired) + { + ave_pos /= num_fired; + + cur_pos.roomnum = obj->roomnum; + cur_pos.position = &ave_pos; + cur_pos.orient = &obj->orient; // chrishack -- incorrect for directional sounds + // chrishack -- actually we only need an fvec (direction of fire -- NOT OBJECT) + + if (Game_mode & GM_MULTI) + { + if(Netgame.local_role==LR_SERVER) + { + if(obj->control_type==CT_AI) + { + //Stuff the sound + MultiSendRobotFireSound(static_wb->fm_fire_sound_index[p_dwb->cur_firing_mask], OBJNUM(obj)); + } + } + } + if(Demo_flags == DF_RECORDING) + { + DemoWrite3DSound(static_wb->fm_fire_sound_index[p_dwb->cur_firing_mask], OBJNUM(obj),SND_PRIORITY_HIGHEST); + } + if(obj == Viewer_object) + Sound_system.Play3dSound(static_wb->fm_fire_sound_index[p_dwb->cur_firing_mask], SND_PRIORITY_HIGHEST, &cur_pos, MAX_GAME_VOLUME, SIF_NO_3D_EFFECTS); + else + Sound_system.Play3dSound(static_wb->fm_fire_sound_index[p_dwb->cur_firing_mask], SND_PRIORITY_HIGHEST, &cur_pos); + + if(obj->type == OBJ_PLAYER) + { + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 1.0f; + hear.curiosity_level = 0.0f; + hear.max_dist = Sounds[static_wb->fm_fire_sound_index[p_dwb->cur_firing_mask]].max_distance; + AINotify(obj, AIN_HEAR_NOISE, (void *)&hear); + } + } + + if ((static_wb->flags & WBF_GUIDED) && obj->type==OBJ_PLAYER) + { + ASSERT (num_fired<=1); // Only one guided can be fired at a time + if (num_fired>0) + { + if (Players[obj->id].guided_obj!=NULL) // if there already is a guided, release it + { + ReleaseGuidedMissile (obj->id); + } + + Players[obj->id].guided_obj=&Objects[weapon_obj]; + Objects[weapon_obj].mtype.phys_info.flags |=(PF_HOMING|PF_GUIDED); + + // draw guided view in small window + if (!Game_toggles.guided_mainview && obj->id==Player_num) { + int window = (Missile_camera_window < 0) ? SVW_LEFT : Missile_camera_window; + CreateSmallView(window,Objects[weapon_obj].handle,SVF_POPUP+SVF_BIGGER+SVF_CROSSHAIRS,0.0,D3_DEFAULT_ZOOM,-1,TXT_HUD_GUIDED); + } + } + } + + if ((static_wb->flags & WBF_USER_TIMEOUT) && obj->type==OBJ_PLAYER) + { + ASSERT (num_fired<=1); // Only one guided can be fired at a time + if (num_fired>0) + { + if (Players[obj->id].user_timeout_obj!=NULL) // if there already is a guided, release it + { + ReleaseUserTimeoutMissile (obj->id); + } + + Players[obj->id].user_timeout_obj=&Objects[weapon_obj]; + + } + } + + if (num_fired>=1 && obj->type==OBJ_PLAYER && obj->id==Player_num && (Game_mode & GM_MULTI) && !(Netgame.flags & NF_PERMISSABLE)) + { + ship *ship = &Ships[Players[obj->id].ship_index]; + otype_wb_info *wb = &ship->static_wb[dynamic_wb_index]; + int on_off_weapon = (wb->flags & WBF_ON_OFF); + + if (!on_off_weapon) // Normal projectile weapon + { + MultiSendFirePlayerWB (Player_num,dynamic_wb_index,p_dwb->cur_firing_mask,must_send,damage_scalar); + } + } + + // Don't do any of this if its the local client and a permissable netgame + if (!((Game_mode & GM_MULTI) && obj->type==OBJ_PLAYER && obj->id==Player_num && Netgame.flags & NF_PERMISSABLE)) + { + p_dwb->last_fire_time = Gametime; + + if(num_fired>=1 && obj->type==OBJ_PLAYER) + { + Players[obj->id].last_fire_weapon_time = Gametime; + } + + if(static_wb->flags & WBF_RANDOM_FIRE_ORDER) + { + p_dwb->cur_firing_mask = ((float)ps_rand()/(float)RAND_MAX) * static_wb->num_masks; + } + else + { + p_dwb->cur_firing_mask++; + } + + if(p_dwb->cur_firing_mask >= static_wb->num_masks) + p_dwb->cur_firing_mask = 0; + } + + if(obj->ai_info) + { + for(cur_m_bit = 0; cur_m_bit < pm->poly_wb[poly_wb_index].num_gps; cur_m_bit++) + { + if(static_wb->gp_fire_masks[p_dwb->cur_firing_mask] & (0x01 << cur_m_bit)) + { + int weapon_id = static_wb->gp_weapon_index[cur_m_bit]; + + if(Weapons[weapon_id].phys_info.flags & PF_FIXED_VELOCITY) + obj->ai_info->weapon_speed = Weapons[weapon_id].phys_info.velocity.z; + else if(Weapons[weapon_id].phys_info.flags & PF_USES_THRUST) + obj->ai_info->weapon_speed = Weapons[weapon_id].phys_info.full_thrust/Weapons[weapon_id].phys_info.drag; + else + obj->ai_info->weapon_speed = 0.0; + break; + } + } + } + + // RType Code... + if (!((Game_mode & GM_MULTI) && (Netgame.local_role==LR_CLIENT))) + { + if(obj->type == OBJ_PLAYER && obj->id >= 0) + { + object *bobj = ObjGet(Buddy_handle[obj->id]); + if(bobj && bobj->type == OBJ_ROBOT && (bobj->ai_info->flags & AIF_GB_MIMIC_PLAYER_FIRING_HACK)) + { + if(dynamic_wb_index != OMEGA_INDEX && dynamic_wb_index != NAPALM_INDEX && dynamic_wb_index < 10) + { + FireWeaponFromObject(bobj, saved_weapon_id, 0, true, false); + } + } + } + } +} + +void WBFireBattery(object *obj, otype_wb_info *static_wb, int wb_index) +{ + WBFireBattery(obj, static_wb, wb_index, wb_index); +} + +void WBClearInfo(object *obj) +{ + int i, j; + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + int num_wbs = pm->num_wbs; + + if(obj->type == OBJ_PLAYER) + num_wbs = MAX_WBS_PER_OBJ; + + for(i = 0; i < num_wbs; i++) + { + obj->dynamic_wb[i].last_fire_time = -1.0f; + obj->dynamic_wb[i].cur_firing_mask = 0; + + for(j = 0; j < MAX_WB_TURRETS; j++) + { + obj->dynamic_wb[i].norm_turret_angle[j] = 0.0f; + obj->dynamic_wb[i].turret_direction[j] = WB_MOVE_STILL; + obj->dynamic_wb[i].turret_next_think_time[j] = 0.0f; + } + + obj->dynamic_wb[i].wb_anim_mask = 0; + obj->dynamic_wb[i].wb_anim_frame = 0.0f; + + vm_MakeZero(&obj->dynamic_wb[i].cur_target); + + obj->dynamic_wb[i].flags = DWBF_ENABLED | DWBF_AUTOMATIC; + } +} + +void WBClearInfo(otype_wb_info static_wb[]) +{ + int i; + int j; + + if(static_wb==NULL) + return; + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + static_wb[i].aiming_gp_index = 0; + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + static_wb[i].gp_weapon_index[j] = LASER_INDEX; + } + + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + static_wb[i].fm_fire_sound_index[j] = SOUND_NONE_INDEX; + static_wb[i].anim_start_frame[j] = 0.0; + static_wb[i].anim_fire_frame[j] = 0.0; + static_wb[i].anim_end_frame[j] = 0.0; + static_wb[i].anim_time[j] = 0.0; + } + + static_wb[i].num_masks = 1; + + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + static_wb[i].gp_fire_masks[j] = 0; + static_wb[i].gp_fire_wait[j] = 1.0f; + } + // Makes sure a robot with a weapon bank can fire it even if the artist forget to set the weapon info for + // this robot + static_wb[i].gp_fire_masks[0] = 1; + + static_wb[i].flags=0; + static_wb[i].aiming_flags = 0; + static_wb[i].aiming_3d_dot = 0.9f; + static_wb[i].aiming_3d_dist = 1000.0f; + static_wb[i].aiming_XZ_dot = 0.9f; + } +} + +// If the wb_index is negative, all wbs are enabled/disabled +void WBEnable(object *obj, int wb_index, bool f_enable) +{ + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + int i; + + if(wb_index < 0) + { + for(i = 0; i < pm->num_wbs; i++) + { + WBEnable(obj, i, f_enable); + } + } + else + { + if((obj->type != OBJ_PLAYER && wb_index < pm->num_wbs) || (obj->type == OBJ_PLAYER)) + { + if(f_enable) + obj->dynamic_wb[wb_index].flags |= DWBF_ENABLED; + else + obj->dynamic_wb[wb_index].flags &= ~DWBF_ENABLED; + } + } +} diff --git a/Descent3/robotfire.h b/Descent3/robotfire.h new file mode 100644 index 000000000..e9a2e7ccc --- /dev/null +++ b/Descent3/robotfire.h @@ -0,0 +1,23 @@ +#ifndef ROBOTFIRE_H_ +#define ROBOTFIRE_H_ + +#include "robotfirestruct.h" +#include "objinfo.h" +#include "object.h" +#include "ship.h" + +void FireOnOffWeapon (object *obj); + +void WBSetupFireAnim(object *obj, otype_wb_info *static_wb, int wb_index); +void WBFireAnimFrame(object *obj, otype_wb_info *static_wb, int wb_index); + +bool WBIsBatteryReady(object *obj, otype_wb_info *static_wb, int wb_index); +void WBFireBattery(object *obj, otype_wb_info *static_wb, int wb_index); +void WBFireBattery(object *obj, otype_wb_info *static_wb, int poly_wb_index, int dynamic_wb_index,float damage_scalar=1.0); + +void WBClearInfo(object *obj); +void WBClearInfo(otype_wb_info static_wb[]); + +void WBEnable(object *obj, int wb_index, bool f_enable); + +#endif diff --git a/Descent3/robotfirestruct.h b/Descent3/robotfirestruct.h new file mode 100644 index 000000000..6946742f4 --- /dev/null +++ b/Descent3/robotfirestruct.h @@ -0,0 +1,90 @@ +#ifndef ROBOTFIRESTRUCT_H_ +#define ROBOTFIRESTRUCT_H_ + +#include "pstypes.h" +#include "vecmat.h" +#include "robotfirestruct_external.h" + +// NOTE: CHANGE gunbattery.h (IN POFGEN PROJECT) if constants are changed +// NOTE: Robots are limited to the number of wb configurations of the player. This seem like an +// adequit number (currently 21) +// (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS + 1/*Flare*/) +// NOTE: Cannot include weapon.h because of circular dependances. +#define MAX_WBS_PER_OBJ 21 + +// Attach to the polymodel +typedef struct poly_wb_info +{ + // Static Data (Add to robot generic page) + unsigned short num_gps; + ubyte gp_index[MAX_WB_GUNPOINTS]; + + // Turrets are listed from most important (greatest mobility) to least important + ubyte num_turrets; + unsigned short turret_index[MAX_WB_TURRETS]; + +} poly_wb_info; + + + +//Next free WBF is 32 + +// Attach to a object type +typedef struct otype_wb_info +{ + unsigned short gp_weapon_index[MAX_WB_GUNPOINTS]; + unsigned short fm_fire_sound_index[MAX_WB_FIRING_MASKS]; + unsigned short aiming_gp_index; + + ubyte num_masks; + ubyte gp_fire_masks[MAX_WB_FIRING_MASKS]; + float gp_fire_wait[MAX_WB_FIRING_MASKS]; + + ubyte gp_quad_fire_mask; + + ubyte num_levels; + unsigned short gp_level_weapon_index[MAX_WB_UPGRADES]; + unsigned short gp_level_fire_sound_index[MAX_WB_UPGRADES]; + + ubyte aiming_flags; + float aiming_3d_dot; // These can be reused. + float aiming_3d_dist; + float aiming_XZ_dot; + + float anim_start_frame[MAX_WB_FIRING_MASKS]; + float anim_fire_frame[MAX_WB_FIRING_MASKS]; + float anim_end_frame[MAX_WB_FIRING_MASKS]; + float anim_time[MAX_WB_FIRING_MASKS]; + + ushort flags; + + float energy_usage,ammo_usage; +} otype_wb_info; + +#define WB_MOVE_STILL 0 +#define WB_MOVE_RIGHT 1 +#define WB_MOVE_LEFT 2 + +// Goes with an individual robot's instance +typedef struct dynamic_wb_info +{ + // Dynamic Data + float last_fire_time; + unsigned char cur_firing_mask; + + float norm_turret_angle[MAX_WB_TURRETS]; + float turret_next_think_time[MAX_WB_TURRETS]; + ubyte turret_direction[MAX_WB_TURRETS]; + + ubyte wb_anim_mask; + float wb_anim_frame; + + vector cur_target; + + char upgrade_level; // For multi-level weapons ( 0 to MAX_WB_UPGRADES-1) + + int flags; + +} dynamic_wb_info; + +#endif \ No newline at end of file diff --git a/Descent3/robotfirestruct_external.h b/Descent3/robotfirestruct_external.h new file mode 100644 index 000000000..db815082a --- /dev/null +++ b/Descent3/robotfirestruct_external.h @@ -0,0 +1,33 @@ +#ifndef ROBOTFIRESTRUCT_EXTERNAL_H_ +#define ROBOTFIRESTRUCT_EXTERNAL_H_ + +#define MAX_WB_GUNPOINTS 8 // Maximum gun points for a gun battery // Limitted by interface in Editor +#define MAX_WB_FIRING_MASKS 8 // Maximum firing masks (firing sequence) // Limitted by interface in Editor +#define MAX_WB_TURRETS 8 // Maximum number of turret// Arbitary + +// Object type flags +#define WBF_SPRAY 1 +#define WBF_ANIM_LOCAL 2 +#define WBF_ANIM_FULL 4 +#define WBF_ANIM_MASKS 6 +#define WBF_RANDOM_FIRE_ORDER 8 +#define WBF_GUIDED 16 +#define WBF_USE_CUSTOM_FOV 32 +#define WBF_ON_OFF 64 +#define WBF_USE_CUSTOM_MAX_DIST 128 +#define WBF_USER_TIMEOUT 256 +#define WBF_FIRE_FVEC 512 +#define WBF_AIM_FVEC 1024 +#define WBF_FIRE_TARGET 2048 + +#define MAX_WB_UPGRADES 5 + +#define DWBF_ENABLED 1 +#define DWBF_AUTOMATIC 2 +#define DWBF_ANIMATING 4 +#define DWBF_ANIM_FIRED 8 +#define DWBF_QUAD 16 +#define DWBF_UPGRADED 32 + + +#endif \ No newline at end of file diff --git a/Descent3/rocknride.cpp b/Descent3/rocknride.cpp new file mode 100644 index 000000000..d9b01c994 --- /dev/null +++ b/Descent3/rocknride.cpp @@ -0,0 +1,241 @@ +/* +* $Logfile: /DescentIII/main/rocknride.cpp $ +* $Revision: 5 $ +* $Date: 9/23/99 12:02p $ +* $Author: Jeff $ +* +* Rock 'n' Ride +* +* $Log: /DescentIII/main/rocknride.cpp $ + * + * 5 9/23/99 12:02p Jeff + * include stdlib for atexit + * + * 4 7/28/99 3:44p Kevin + * Mac! + * + * 3 5/11/99 11:18a Jeff + * finished rock 'n' ride + * + * 2 5/10/99 9:25p Jeff + * first phase of Rock 'n' Ride support added +* +* $NoKeywords: $ +*/ +#include "mono.h" +#include "ddio.h" +#include "debug.h" +#include "game.h" +#include "pserror.h" +#include "rocknride.h" +#include +#include +#include +bool RocknRide_enabled = false; +float RocknRide_lastcontroller_update = 0.0f; +float RocknRide_lastforce_update = 0.0f; +#define RNR_CONTROLLER_UPDATE 1.0f/20.0f +#define RNR_FORCE_UPDATE 1.0f/60.0f +tSerialPort RocknRide_serial_port; +// RNR_Shutdown +// +// Shutsdown the Rock 'n' Ride device +void RNR_Shutdown(void) +{ +#ifndef MACINTOSH + if(!RocknRide_enabled) + return; + if(RocknRide_serial_port) + ddio_SerialClosePort(RocknRide_serial_port); +#endif +} +// RNR_Initialize +// +// Initializes the Rock 'n' Ride device +// Pass in the COMM port +bool RNR_Initialize(int comm_port) +{ +#ifndef MACINTOSH + if(RocknRide_enabled) + return true; + static bool called = false; + RocknRide_serial_port = ddio_SerialOpenPort(comm_port,9600); + if(!RocknRide_serial_port) + { + mprintf((0,"Couldn't open serial port\n")); + return false; + } + RocknRide_enabled = true; + //only setup once + if(!called) + { + called = true; + atexit(RNR_Shutdown); + } +#endif + return true; +} +// RNR_SendPacket +// +// Sends off a packet to the Rock 'n' Ride device +void RNR_SendPacket(tRocknride_packet *packet) +{ +#ifndef MACINTOSH + if(!RocknRide_enabled) + return; + ASSERT(packet); + if(!packet) + return; + ubyte data[32]; + int size = -1; + switch(packet->packet_type) + { + case RNRP_POSITION: + size = 3; + data[0] = 'P'; + data[1] = packet->pos.x; + data[2] = packet->pos.y; + break; + case RNRP_HIT: + size = 3; + data[0] = 'H'; + data[1] = packet->pos.x; + data[2] = packet->pos.y; + break; + case RNRP_GAMESTATUS: + size = 2; + data[0] = 'G'; + data[1] = packet->status; + break; + } + if(size!=-1) + { + //send off the packet to the device + //@@char test[32]; + //@@memcpy(test,data,size); + //@@test[size] = '\0'; + //@@mprintf((0,"DATA: %s\n",test)); + for(int i=0;iGametime) + { + RocknRide_lastcontroller_update = 0.0f; //handle new game started + } + if(RocknRide_lastcontroller_update+RNR_CONTROLLER_UPDATE>Gametime) + return; //don't update yet + + RocknRide_lastcontroller_update = Gametime; + ubyte x_val,y_val; + x_val = 128 + (127.0f*controls->heading_thrust); + y_val = 128 + (127.0f*controls->pitch_thrust); + tRocknride_packet packet; + packet.packet_type = RNRP_POSITION; + packet.pos.x = x_val; + packet.pos.y = y_val; + RNR_SendPacket(&packet); +#endif +} +// RNR_UpdateForceFeedbackInfo +// +// Updates any Force Feedback effects +void RNR_UpdateForceFeedbackInfo(float magnitude,vector *direction) +{ +#ifndef MACINTOSH + if(!RocknRide_enabled) + return; + ASSERT(magnitude>=0 && magnitude<=1); + + if(RocknRide_lastforce_update>Gametime) + { + RocknRide_lastforce_update = 0.0f; //handle new game started + } + if(RocknRide_lastforce_update+RNR_FORCE_UPDATE>Gametime) + return; //don't update yet + + RocknRide_lastforce_update = Gametime; + matrix mat = Identity_matrix; + angvec ag; + vm_VectorToMatrix(&mat,direction); + vm_ExtractAnglesFromMatrix(&ag,&mat); + + float sh,ch; + vm_SinCos (ag.h,&sh,&ch); + + float x_val = magnitude*ch; + float y_val = magnitude*sh; + x_val *= 255.0f; + y_val *= 255.0f; + ubyte x_b = (ubyte)x_val; + ubyte y_b = (ubyte)y_val; + tRocknride_packet packet; + packet.packet_type = RNRP_HIT; + packet.pos.x = x_b; + packet.pos.y = y_b; + RNR_SendPacket(&packet); +#endif +} +// RNR_UpdateForceFeedbackInfo +// +// Updates any Force Feedback effects +void RNR_UpdateForceFeedbackInfo(float magnitude,int *direction) +{ +#ifndef MACINTOSH + if(!RocknRide_enabled) + return; + if(RocknRide_lastforce_update>Gametime) + { + RocknRide_lastforce_update = 0.0f; //handle new game started + } + if(RocknRide_lastforce_update+RNR_FORCE_UPDATE>Gametime) + return; //don't update yet + + RocknRide_lastforce_update = Gametime; + + Int3(); //I don't think we ever use FF with an int direction +#endif +} +// RNR_UpdateGameStatus +// +// Updates a game status to a Rock 'n' Ride chair +void RNR_UpdateGameStatus(ubyte status) +{ +#ifndef MACINTOSH + if(!RocknRide_enabled) + return; + tRocknride_packet packet; + packet.packet_type = RNRP_GAMESTATUS; + + bool send = true; + switch(status) + { + case RNRGSC_PLAYERDIES: + packet.status = 'D'; + break; + case RNRGSC_INMENU: + packet.status = 'P'; + break; + default: + send = false; + break; + } + if(send) + RNR_SendPacket(&packet); +#endif +} \ No newline at end of file diff --git a/Descent3/rocknride.h b/Descent3/rocknride.h new file mode 100644 index 000000000..c7c743124 --- /dev/null +++ b/Descent3/rocknride.h @@ -0,0 +1,78 @@ +/* +* $Logfile: /DescentIII/Main/rocknride.h $ +* $Revision: 2 $ +* $Date: 5/10/99 9:25p $ +* $Author: Jeff $ +* +* Rock 'n' Ride +* +* $Log: /DescentIII/Main/rocknride.h $ + * + * 2 5/10/99 9:25p Jeff + * first phase of Rock 'n' Ride support added +* +* $NoKeywords: $ +*/ + +#ifndef __ROCK_N_RIDE_H_ +#define __ROCK_N_RIDE_H_ + +#include "pstypes.h" +#include "controls.h" +#include "vecmat.h" + +extern bool RocknRide_enabled; + +#define RNRP_POSITION 0x00 //Rock 'n' Ride Position Packet +#define RNRP_HIT 0x01 //Rock 'n' Ride Hit Packet +#define RNRP_GAMESTATUS 0x02 //Rock 'n' Ride Game Status Packet +typedef struct +{ + ubyte packet_type; + union + { + struct + { + ubyte x,y; + }pos; + ubyte status; + }; +}tRocknride_packet; + +//Rock 'n' Ride Game Status codes +#define RNRGSC_PLAYERDIES 0 // The player has died +#define RNRGSC_INMENU 1 // The player is in a UI menu + +// RNR_Initialize +// +// Initializes the Rock 'n' Ride device +// Pass in the COMM port +bool RNR_Initialize(int comm_port); + +// RNR_SendPacket +// +// Sends off a packet to the Rock 'n' Ride device +void RNR_SendPacket(tRocknride_packet *packet); + +// RNR_UpdateControllerInfo +// +// Updates any Rock 'n' Ride controller data for the frame (if needed) +void RNR_UpdateControllerInfo(game_controls *controls); + +// RNR_UpdateForceFeedbackInfo +// +// Updates any Force Feedback effects +void RNR_UpdateForceFeedbackInfo(float magnitude,vector *direction); + +// RNR_UpdateForceFeedbackInfo +// +// Updates any Force Feedback effects +void RNR_UpdateForceFeedbackInfo(float magnitude,int *direction); + +// RNR_UpdateGameStatus +// +// Updates a game status to a Rock 'n' Ride chair +void RNR_UpdateGameStatus(ubyte status); + +#endif + diff --git a/Descent3/room.cpp b/Descent3/room.cpp new file mode 100644 index 000000000..42fff779f --- /dev/null +++ b/Descent3/room.cpp @@ -0,0 +1,1429 @@ +/* + * $Logfile: /DescentIII/main/room.cpp $ + * $Revision: 126 $ + * $Date: 4/19/00 5:07p $ + * $Author: Matt $ + * + * Functions and data for rooms + * + * $Log: /DescentIII/main/room.cpp $ + * + * 126 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 125 10/21/99 9:29p Jeff + * B.A. Macintosh code merge + * + * 124 10/12/99 10:19a Gwar + * adding terrain support to NEWEDITOR + * + * 123 8/30/99 1:06p Gwar + * added a check in ComputeRoomBoundingSphere for no verts in the room + * (for NEWEDITOR) + * + * 122 8/18/99 7:02a Gwar + * in NEWEDITOR, init light multiplier and ambience in InitRoom + * + * 121 8/12/99 12:11a Gwar + * minor NEWEDITOR stuff + * + * 120 7/20/99 1:00p Jason + * added auto katmai support + * + * 119 7/05/99 12:42p Gwar + * wrapped my last change with an #ifdef NEWEDITOR + * + * 118 7/05/99 12:10p Gwar + * fixed "bug" in ComputeRoomCenter, when there are no verts in the room. + * but if anyone minds, i can just test for no verts before calling the + * function + * + * 117 7/04/99 4:56p Gwar + * changes for texture management in NEWEDITOR + * + * 116 6/21/99 12:51p Gwar + * adding texture marking for NEWEDITOR ; kind of in a state of flux right + * now + * + * 115 5/17/99 11:49a Kevin + * Added the ability to save a level from the new editor + * + * 114 5/08/99 8:40a Gwar + * TONS of fixes + * + * 113 4/25/99 9:02p Chris + * Improving the Bnode system + * + * 112 4/23/99 9:44a Gwar + * had to add a NEWEDITOR #define...sorry. + * + * 111 4/18/99 4:35p Matt + * Changed BNode_FreeRoom() to take a room pointer instead of a room + * number, since it was being called for rooms in the scrap buffer, which + * weren't in the room list. Also added code to InitRoom() to init the + * bnode data. + * + * 110 4/18/99 5:39a Chris + * Vastly improved the path node system + * + * 109 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 108 4/15/99 5:49p Chris + * Fixed a bug with rendering the BOAPath nodes + * + * 107 4/15/99 12:19p Jason + * made mirrors more robust and able to have multiple mirrored faces in + * the same room (as long as they are all on the same plane) + * + * 106 4/07/99 4:38p Matt + * When initializing a new face, set its tmap to 0 + * + * 105 4/04/99 6:20p Kevin + * Put in an ifdef for now for the new editor + * + * 104 3/24/99 5:53p Jason + * added per room ambience settings + * + * 103 3/10/99 7:13p Jason + * added smooth specular shading for curved surfaces + * + * 102 2/24/99 5:31p Hayes + * Lowered MIN_NORMAL_MAG from 0.05 to 0.035 to allow for a small triangle + * in Hayes's geometry for level 3. (MattT on Hayes's machine) + * + * 101 2/19/99 4:28p Jason + * fixed katmai defines + * + * 100 2/19/99 4:26p Jason + * + * 99 2/19/99 1:24p Matt + * Moved ComputerCenterRoomOnFace() from editor to main + * + * 98 2/18/99 12:32p Jason + * added room multiplier + * + * 97 2/03/99 5:49p Matt + * Added room damage system + * + * 96 2/03/99 4:26p Jason + * made multiplayer coop actually work! + * + * 95 2/01/99 4:17p Jason + * more changes for multisafe + * + * 94 1/29/99 12:48p Matt + * Rewrote the doorway system + * + * 93 1/26/99 2:36p Jason + * clear out room change flags upon room initting + * + * 92 1/24/99 6:35p Matt + * Init a room's current face to 0 instead of -1 + * + * 91 1/20/99 6:11p Matt + * Fixed a couple bugs in the room wind/fog change system, and make the + * values change from current -> new instead of start -> end. + * + * 90 1/19/99 11:25a Jason + * added room (fog and wind) changing functions + * + * 89 1/08/99 5:37p Samir + * reverb values per room. + * + * 88 12/22/98 2:03p Matt + * Added room names, and made rooms not compress so that room numbers are + * suitable for persistant uses. + * + * 87 12/21/98 2:18p Jason + * made CheckTrasparency always return false (for now) + * + * 86 11/02/98 6:15p Chris + * Room AABBs get saved with the level and the sort_face and dec_sort_face + * list s have been removed + * + * 85 10/17/98 11:11p Matt + * Added room memory allocation system + * + * 84 10/16/98 2:24p Jason + * changes for the demo + * + * 83 10/14/98 2:48p Kevin + * Changed memory code to comply with mem lib + * + * 82 10/03/98 6:00p Matt + * Fixed bogus assert + * + * 81 9/11/98 4:45p Matt + * Changed minimum surface normal mag from 0.15 to 0.05, to accomodate + * some faces in Sean's level 15. + * + * 80 9/08/98 12:05p Jason + * moved doorway.h out of room.h + * + * 79 9/01/98 12:04p Matt + * Ripped out multi-face portal code + * + * 78 8/27/98 5:19p Jason + * added first rev of reflected surfaces + * + * 77 8/19/98 2:17p Jeff + * made a function to change the texture on a face + * + * 76 8/17/98 6:40p Matt + * Added ambient sound system + * + * 75 7/21/98 2:14p Chris + * Some FVI speedups - not done + * + * 74 7/17/98 9:56a Chris + * Intermediate checkin + * + * 73 7/16/98 8:29p Chris + * Partial implementation of the new collide code + * + * 72 6/05/98 5:22p Jason + * added volumetric fog + * + * 71 6/02/98 6:03p Jason + * added specular lightmaps + * + * 70 5/22/98 3:28p Jason + * added specular lighting + * + * 69 5/22/98 12:08p Matt + * Changed MIN_NORMAL_MAG to 0.15 to accomodate small face in Sean's cave + * tunnel. + * + * 68 5/15/98 5:41p Jason + * implemented volume lighting system + * + * 67 5/06/98 12:55p Jason + * did some vis effect optimizations + * + * 66 4/20/98 6:34p Chris + * Oops + * + * 65 4/20/98 6:24p Chris + * Bulletproofing collision code + * + * 64 4/13/98 2:53p Chris + * + * 63 4/13/98 2:21p Chris + * Fixed some collision problems dealing with AABBs and Polymodel paging + * in. + * + * 62 4/02/98 12:24p Jason + * trimmed some fat from our structures + * + * 61 3/31/98 3:49p Jason + * added memory lib + * + * 60 3/30/98 12:27a Jason + * fixed memory leaks as reported by BoundsChecker + * + * 59 3/18/98 4:31p Chris + * Speed up fvi and fixed some bugs + * + * 58 3/18/98 11:57a Sean + * Lowered MIN_NORMAL_MAG down to 0.18, since the last change only fixed + * two of the three small polygons that were giving us trouble. (MattT on + * Sean's machine) + * + * 57 3/18/98 11:49a Matt + * Lowered MIN_NORMAL_MAG from 0.24 to 0.20 to not generate warnings for + * small polygons in Sean's refueling dock antenna. + * + * 56 3/16/98 5:50p Chris + * Added sorted face lists for fvi + * + * 55 2/19/98 11:17a Chris + * Tweaked the BIG_OBJECT system + * + * 54 2/18/98 1:39p Jason + * more changes for lighting + * + * 53 2/15/98 3:58p Sean + * Lowered threshjhold for detecting low-precision normals. (MattT on + * Sean's machine) + * + * 52 2/13/98 12:57p Jason + * changes for adjusting light multiples + * + * 51 2/11/98 7:01p Chris + * Started to add wind + * + * 50 2/10/98 3:49p Jason + * added pulsing walls + * + * 49 2/04/98 8:25p Jason + * added light multiplier for faces + * + * 48 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 47 2/02/98 5:13p Matt + * Added more generic function to compute a surface normal for a face + * + * 46 1/20/98 6:36p Brent + * Lowered MIN_NORMAL_MAG (MattT on Brent's machine) + * + * 45 1/20/98 2:16p Chris + * Fixed an init bug + * + * 44 1/20/98 1:06p Jason + * fixed unitialized vis_effect field in room structure + * + * 43 1/16/98 11:53a Matt + * Relaxed low-precision normal check, and added mprintfs with info on + * problem normals + * + * 42 1/15/98 7:34p Matt + * Revamped error checking when computing face normals + * + * 41 1/15/98 4:26p Luke + * Re-added bad normal check. (MattT on Luke's machine) + * + * 40 12/23/97 1:35p Samir + * Took out unused variable Room_viewport. + * + * 39 11/24/97 1:30a Jason + * first attempt at adding shadows + * + * 38 11/14/97 11:55p Chris + * Added support so that external rooms can be BIG_OBJECTs + * + * 37 11/14/97 6:39p Jason + * added ability to do lighting on a single room + * + * 36 10/13/97 5:08p Matt + * Moved ComputeRoomBoundingSphere() & CreateRoomObjects() from editor to + * main + * + * + * 35 10/08/97 2:28p Jason + * got external rooms working with terrain lighting + * + * 34 10/03/97 3:33p Samir + * Took out Int3 in ComputeFaceNormal. + * + * 33 10/02/97 12:08p Chris + * Allow FindPointRoom to correctly handle external rooms (ignore them) + * + * 32 9/23/97 2:48p Matt + * Fixed ComputeFaceNormal() to find the best normal for the face + * + * 31 9/19/97 2:52p Jason + * changes to fix lightmap seam problem + * + * 30 9/16/97 4:26p Matt + * Got rid of static_light & changed fields in the room struct + * + * 29 9/12/97 6:37p Chris + * Fixed the FindPointRoom bug. + * + * 28 9/12/97 5:38p Jason + * got doors working + * + * 27 9/12/97 2:25p Jason + * fixed alpha problem + * + * 26 9/10/97 3:02p Matt + * Moved GetIJ() from erooms.cpp to room.cpp + * + * 25 9/03/97 2:13p Chris + * Cleaned a few lines of code + * + * 24 8/27/97 5:44p Matt + * If can't get a normal from the first three points on a face, rotate + * around and try again + * + * 23 8/19/97 1:43p Matt + * Added code to free room palette on exit + * + * 22 8/19/97 1:13p Jason + * added GetAreaForFace function + * + * 21 8/18/97 1:44a Chris + * Added the SOLID_PORTALS flag for better performance of FindPointRoom + * + * 20 8/13/97 11:53a Jason + * moved ClearAllRoomLightmaps into room.cpp where it belongs + * + * 19 8/12/97 3:51p Jason + * tweaked lightmaps with radiosity + * + * 18 8/12/97 1:10a Jason + * added code to support radiosity lighting + * + * 17 8/11/97 3:55p Chris + * Added a function to compute the rough center of a portal + * + * 16 8/04/97 6:55p Chris + * + * 15 8/01/97 10:12a Jason + * implemented FndRoomCenter + * + * 14 7/28/97 5:10p Matt + * Initialize room's current_face field to -1 + * + * 13 7/18/97 5:36p Jason + * save changed paletted rooms on exit + * + * 12 7/17/97 3:38p Matt + * Free all rooms at exit (doesn't include palette rooms) + * + * $NoKeywords: $ + */ + +#include "room.h" +#include "mono.h" +#include "vecmat.h" +#include "gametexture.h" +#include "manage.h" +#include "renderer.h" +#include "game.h" +#include "render.h" +#include "grdefs.h" +#include +#include +#include "terrain.h" +#include "findintersection.h" +#include "lightmap.h" +#include "lightmap_info.h" +#include "special_face.h" +#include "mem.h" +#include "doorway.h" +#include "multi_world_state.h" +#include "damage_external.h" +#include "descent.h" +#ifdef EDITOR + #include "editor\editor_lighting.h" +#endif +#ifdef NEWEDITOR + #include "neweditor\editor_lighting.h" +#endif +#include "bnode.h" + +//Global array of rooms +room Rooms[MAX_ROOMS+MAX_PALETTE_ROOMS]; +room_changes Room_changes[MAX_ROOM_CHANGES]; + +extern int Cur_selected_room,Cur_selected_face; + +int Highest_room_index = -1; + +void FreePaletteRooms(); + +// Zeroes out the rooms array +void InitRooms () +{ + int i; + for (i=0;inum_faces;f++) + n += rp->faces[f].num_verts; + + return n; +} + +#endif + +//Vars for the room memory system +ubyte *Room_mem_buf=NULL; //pointer to the rooms block of memory +ubyte *Room_mem_ptr=NULL; //pointer to free memory in the rooms block +int Room_mem_size; //How big our chunk is + +//Closes down the room memory system. +void RoomMemClose() +{ + if (Room_mem_buf) + mem_free(Room_mem_buf); + + Room_mem_buf = Room_mem_ptr = NULL; +} + +//Initialized the memory buffer for a room +//Parameters: size - the total amount of memory needed for the room +void RoomMemInit(int nverts,int nfaces,int nfaceverts,int nportals) +{ +#if (defined(EDITOR) || defined(NEWEDITOR)) + return; //This system is disabled in the editor +#endif + + if (nverts == 0) //We don't know how much mem the room will use, so do the old way + return; + + int size = (nfaces * (sizeof(*Rooms[0].faces))) + + (nverts * sizeof(*Rooms[0].verts)) + + (nportals * sizeof(*Rooms[0].portals)) + + (nfaceverts * (sizeof(*Rooms[0].faces[0].face_verts) + sizeof(*Rooms[0].faces[0].face_uvls))); + + if (Room_mem_buf) + mem_free(Room_mem_buf); + + Room_mem_buf = (ubyte *) mem_malloc(size); + Room_mem_size = size; + + Room_mem_ptr = Room_mem_buf; +} + + +//Allocates memory for a room or face +void *RoomMemAlloc(int size) +{ + if (Room_mem_buf) { + void *p = Room_mem_ptr; + Room_mem_ptr += size; + ASSERT(Room_mem_ptr <= (Room_mem_buf + Room_mem_size)); + return p; + } + else + return mem_malloc(size); +} + +//Frees memory in a room +//Doesn't actually do anything +void RoomMemFree(void *buf) +{ + if (!buf) + return; + + if (Room_mem_buf) { + ASSERT(((buf) >= Room_mem_buf) && ((buf) < (Room_mem_buf + Room_mem_size))); + } + else + mem_free(buf); +} + +//Initalize a room, allocating memory and filling in fields +//Parameters: rp - the room to be initialized +// nverts - how many vertices this room will have +// nfaces - how many faces this room wil have +// nportals - how many portals this room will have +void InitRoom(room *rp,int nverts,int nfaces,int nportals) +{ + //initialize room fields + rp->flags = 0; + rp->objects = -1; + rp->vis_effects=-1; + rp->volume_lights=NULL; + rp->mirror_face=-1; + rp->num_mirror_faces=0; + rp->mirror_faces_list=NULL; + rp->room_change_flags=0; + +#ifndef NEWEDITOR // the new editor must allow users to create a room from scratch + ASSERT(nverts > 0); + ASSERT(nfaces > 0); +#endif + + rp->wind = Zero_vector; + + rp->num_faces = nfaces; + rp->num_verts = nverts; + rp->num_portals = nportals; + rp->last_render_time=0; + rp->fog_depth=100.0; + rp->fog_r=1.0; + rp->fog_g=1.0; + rp->fog_b=1.0; + + rp->faces = (face *) RoomMemAlloc(nfaces * sizeof(*rp->faces)); ASSERT(rp->faces != NULL); + + rp->num_bbf_regions = 0; + + rp->verts = (vector *) RoomMemAlloc(nverts * sizeof(*rp->verts)); ASSERT(rp->verts != NULL); + + if (Katmai) + { + rp->verts4 = (vector4 *) mem_malloc(nverts * sizeof(*rp->verts4)); ASSERT(rp->verts4 != NULL); + } + + rp->pulse_time=0; + rp->pulse_offset=0; + + if (nportals) { + rp->portals = (portal *) RoomMemAlloc(nportals * sizeof(*rp->portals)); + ASSERT(rp->portals != NULL); + } else + rp->portals = NULL; + + //Default to no ambient sound + rp->ambient_sound = -1; + + rp->name = NULL; + rp->doorway_data = NULL; + + rp->env_reverb = 0; // reverb for sound system. + + rp->damage = 0.0; // room damage + rp->damage_type = PD_NONE; // room damage type + + rp->bn_info.num_nodes = 0; + rp->bn_info.nodes = NULL; + +#if ( defined(EDITOR) || defined(NEWEDITOR) ) + Room_multiplier[rp-Rooms]=1.0; + + Room_ambience_r[rp-Rooms]=0.0; + Room_ambience_g[rp-Rooms]=0.0; + Room_ambience_b[rp-Rooms]=0.0; +#endif + + rp->used = 1; //flag this room as used +} + +//Initialize a room face structure. +void InitRoomFace(face *fp,int nverts) +{ + fp->flags = 0; + fp->num_verts = nverts; + fp->portal_num = -1; + fp->tmap = 0; + +#ifdef NEWEDITOR + ned_MarkTextureInUse(0,true); +#endif + + fp->lmi_handle=BAD_LMI_INDEX; + fp->special_handle=BAD_SPECIAL_FACE_INDEX; + fp->light_multiple=4; + + fp->face_verts = (short *) RoomMemAlloc(nverts * sizeof(*fp->face_verts)); ASSERT(fp->face_verts != NULL); + fp->face_uvls = (roomUVL *) RoomMemAlloc(nverts * sizeof(*fp->face_uvls)); ASSERT(fp->face_uvls != NULL); + + ASSERT (fp->face_verts); + ASSERT (fp->face_uvls); + for (int i=0;iface_uvls[i].alpha=255; +} + +// Finds out if we are in a room or outside the mine (-1 if we are outside) +int FindPointRoom(vector *pnt) +{ + int i; + + ASSERT(pnt != NULL); + + for(i = 0; i <= Highest_room_index; i++) + { + if((Rooms[i].used) && !(Rooms[i].flags & RF_EXTERNAL)) + { + bool f_in_room; + + f_in_room = fvi_QuickRoomCheck(pnt, &Rooms[i]); + + if(f_in_room == true) return i; + } + } + + return -1; +} + +//Frees a room, deallocating its memory and marking it as unused +void FreeRoom(room *rp) +{ + int i; + int old_hri = Highest_room_index; + + ASSERT(rp->used != 0); //make sure room is un use + + //Free the faces + for (i=0;inum_faces;i++) + FreeRoomFace(&rp->faces[i]); + + //Free up mem alloced for this room + RoomMemFree(rp->faces); + RoomMemFree(rp->portals); + RoomMemFree(rp->verts); + + if (Katmai) + mem_free (rp->verts4); + + if(rp->num_bbf_regions) + { + for(i = 0; i < rp->num_bbf_regions; i++) + { + mem_free(rp->bbf_list[i]); + } + mem_free(rp->bbf_list); + mem_free(rp->num_bbf); + mem_free(rp->bbf_list_min_xyz); + mem_free(rp->bbf_list_max_xyz); + mem_free(rp->bbf_list_sector); + + rp->num_bbf_regions = 0; + } + + BNode_FreeRoom(rp); + + if (rp->volume_lights) + mem_free(rp->volume_lights); + + if (rp->name) + mem_free(rp->name); + + if (rp->doorway_data) + mem_free(rp->doorway_data); + + if (rp->mirror_faces_list) + mem_free (rp->mirror_faces_list); + + rp->used = 0; + + //Update Highest_room_index + if (ROOMNUM(rp) == Highest_room_index) + while ((Highest_room_index >= 0) && (! Rooms[Highest_room_index].used)) + Highest_room_index--; + + BNode_RemapTerrainRooms(old_hri, Highest_room_index); +} + + +//Frees all the rooms currently in use, deallocating their memory and marking them as unused +void FreeAllRooms() +{ + int rn; + room *rp; + mprintf((1,"Freeing rooms...Higest_room_index %d\n", Highest_room_index)); + for (rn=0,rp=Rooms;rn<=Highest_room_index;rn++,rp++) { + if (rp->used) { +// mprintf((2, "rn %d\n", rn)); + FreeRoom(rp); + } + } + + ASSERT(Highest_room_index == -1); + + RoomMemClose(); + +// mprintf((2,"Done\n")); +} + +#ifdef EDITOR +//Frees rooms that are in the room palette +void FreePaletteRooms() +{ + int rn; + room *rp; + + for (rn=MAX_ROOMS,rp=&Rooms[MAX_ROOMS];rnused) + FreeRoom(rp); +} +#endif + + +//Free the memory used by a room face structure +void FreeRoomFace(face *fp) +{ + if (fp->lmi_handle!=BAD_LMI_INDEX) + { + FreeLightmapInfo (fp->lmi_handle); + fp->lmi_handle=BAD_LMI_INDEX; + fp->flags &=~FF_LIGHTMAP; + } + + if (fp->special_handle!=BAD_SPECIAL_FACE_INDEX) + { + FreeSpecialFace (fp->special_handle); + fp->special_handle=BAD_SPECIAL_FACE_INDEX; + } + + RoomMemFree(fp->face_verts); + RoomMemFree(fp->face_uvls); +} + +//Finds the center point of a room +//Parameters: vp - filled in with the center point +// rp - the room whose center to find +void ComputeRoomCenter(vector *vp,room *rp) +{ + int i; + + vp->x = vp->y = vp->z = 0; + + for (i=0;inum_verts;i++) + *vp+=rp->verts[i]; + +#ifdef NEWEDITOR + if (rp->num_verts) +#endif + *vp/=rp->num_verts; + +} + +//Computes the center point on a face by averaging the points in the face +void ComputeCenterPointOnFace(vector *vp,room *rp,int facenum) +{ + face *fp = &rp->faces[facenum]; + int i; + + vp->x = vp->y = vp->z = 0; + + for (i=0;inum_verts;i++) + *vp += rp->verts[fp->face_verts[i]]; + + *vp /= fp->num_verts; +} + +//the minimum magnitude of a surface normal that we're willing to accept +#define MIN_NORMAL_MAG 0.035 + +//Computes (fills in) the surface normal of a face. +//Finds the best normal on this face by checking all sets of three vertices +//IMPORTANT: The caller should really check the return value of this function +//Parameters: rp,facenum - the room and face to calculate the normal for +//Returns: true if the normal is ok +// false if the normal has a very small (pre-normalization) magnitude +bool ComputeFaceNormal(room *rp,int facenum) +{ + face *fp = &rp->faces[facenum]; + bool ok; + + ok = ComputeNormal(&fp->normal,fp->num_verts,fp->face_verts,rp->verts); + + if (!ok) + { + mprintf((1,"Warning: Low precision normal for room:face = %d:%d\n",ROOMNUM(rp),facenum)); + } + + return ok; +} + +//Compute the surface normal from a list of vertices that determine a face +//Finds the best normal on this face by checking all sets of three vertices +//IMPORTANT: The caller should really check the return value of this function +//Parameters: normal - this is filled in with the normal +// num_verts - how many vertices in the face +// vertnum_list - a list of vertex numbers for this face. these index into verts +// verts - the array of vertices into which the elements of vertnum_list index +//Returns: true if the normal is ok +// false if the normal has a very small (pre-normalization) magnitude +bool ComputeNormal(vector *normal,int num_verts,short *vertnum_list,vector *verts) +{ + int i; + float largest_mag; + + i = 0; + largest_mag = 0.0; + + for (i=0;i largest_mag) { + *normal = tnormal; + largest_mag = mag; + } + } + + + if (largest_mag < MIN_NORMAL_MAG) { + mprintf((1,"Warning: Normal has low precision. mag = %f, norm = %f,%f,%f\n",largest_mag,normal->x,normal->y,normal->z)); + return 0; + } + else + return 1; + + +} + +//Computes the center point on a face by averaging the points in the portal +void ComputePortalCenter(vector *vp, room *rp, int portal_index) +{ + portal *pp = &rp->portals[portal_index]; + face *fp = &rp->faces[pp->portal_face]; + int i; + + vm_MakeZero(vp); + + for (i=0;inum_verts;i++) + *vp += rp->verts[fp->face_verts[i]]; + + *vp /= fp->num_verts; +} + +// Clears lightmaps for a single room +void ClearRoomLightmaps (int roomnum) +{ + int t; + + ASSERT (Rooms[roomnum].used); + + for (t=0;tused>0); + ASSERT (facenum>=0 && facenumnum_faces); + + face *fp=&rp->faces[facenum]; + int i; + vector normal; + float area=0; + + vm_GetPerp (&normal,&rp->verts[fp->face_verts[0]],&rp->verts[fp->face_verts[1]],&rp->verts[fp->face_verts[2]]); + area=(vm_GetMagnitude (&normal)/2); + + for (i=2;inum_verts-1;i++) + { + vm_GetPerp (&normal,&rp->verts[fp->face_verts[0]],&rp->verts[fp->face_verts[i]],&rp->verts[fp->face_verts[i+1]]); + area+=(vm_GetMagnitude (&normal)/2); + } + + return area; +} + +//Returns indeces of the two elements of points on a face to use as a 2d projection +//Parameters: normal - the surface normal of the face +// ii,jj - filled in with elements numbers (0,1, or 2) +void GetIJ(const vector *normal,int *ii,int *jj) +{ + + //To project onto 2d, find the largest element of the surface normal + if (fabs(normal->x) > fabs(normal->y)) + if (fabs(normal->x) > fabs(normal->z)) { + if (normal->x > 0) { + *ii = 2; *jj = 1; // x > y, x > z + } else { + *ii = 1; *jj = 2; + } + } + else { + if (normal->z > 0) { + *ii = 1; *jj = 0; // z > x > y + } else { + *ii = 0; *jj = 1; + } + } + else // y > x + if (fabs(normal->y) > fabs(normal->z)) { + if (normal->y > 0) { + *ii = 0; *jj = 2; // y > x, y > z + } else { + *ii = 2; *jj = 0; + } + } + else { + if (normal->z > 0) { + *ii = 1; *jj = 0; // z > y > x + } else { + *ii = 0; *jj = 1; + } + } +} + +//2d cross product +#define cross(v0,v1) (((v0)[ii] * (v1)[jj]) - ((v0)[jj] * (v1)[ii])) + +//Finds the uv coords of a given point on a room:face. Fills in u & v. +//Parameters: u,v - pointers to variables to be filled in +// pnt - the point we're checking +// rp - pointer to the room that pnt is in +// fp - pointer to the face that pnt is on +void FindPointUV(float *u,float *v,const vector *pnt,const room *rp,const face *fp) +{ + int roomnum = ROOMNUM(rp); + int ii,jj; + vector vec0,vec1; + float *p1,*checkp,*v0,*v1; + float k0,k1; + int t; + + //Make sure we have a valid room + ASSERT((roomnum >= 0) && (roomnum <= Highest_room_index)); + + //Find what plane to project this wall onto to make it a 2d case + GetIJ(&fp->normal,&ii,&jj); + + //Compute delta vectors + vec0 = rp->verts[fp->face_verts[0]] - rp->verts[fp->face_verts[1]]; //vec from 1 -> 0 + vec1 = rp->verts[fp->face_verts[2]] - rp->verts[fp->face_verts[1]]; //vec from 1 -> 0 + + //Get pointers to referece our vectors as arrays of floats + p1 = (float *) &rp->verts[fp->face_verts[1]]; + v0 = (float *) &vec0; + v1 = (float *) &vec1; + checkp = (float *) pnt; + + //Compute our clipping values along i & j axes + k1 = -(cross(checkp,v0) + cross(v0,p1)) / cross(v0,v1); + t = (fabs(v0[ii]) > fabs(v0[jj])) ? ii : jj; + k0 = ((-k1 * v1[t]) + checkp[t] - p1[t]) / v0[t]; + + //Compute u & v values + *u = fp->face_uvls[1].u + (k0 * (fp->face_uvls[0].u - fp->face_uvls[1].u)) + (k1 * (fp->face_uvls[2].u - fp->face_uvls[1].u)); + *v = fp->face_uvls[1].v + (k0 * (fp->face_uvls[0].v - fp->face_uvls[1].v)) + (k1 * (fp->face_uvls[2].v - fp->face_uvls[1].v)); +} + +//Check if a particular point on a wall is a transparent pixel +//Parameters: pnt - the point we're checking +// rp - pointer to the room that pnt is in +// facenum - the face that pnt is on +//Returns: true if can pass through the given point, else 0 +int CheckTransparentPoint(const vector *pnt,const room *rp,const int facenum) +{ + int bm_handle; + face *fp = &rp->faces[facenum]; + float u,v; + int w,h,x,y; + + return false; + + //Get the UV coordindates of the point we hit + FindPointUV(&u,&v,pnt,rp,fp); + + //Get pointer to the bitmap data + bm_handle = GetTextureBitmap(fp->tmap,0); + + //Get x & y coordindates (in bitmap) of check point + w = bm_w(bm_handle,0); h = bm_h(bm_handle,0); + x = ((int) (u * w)) % w; + y = ((int) (v * h)) % h; + + //Return true if the check point is transparent + return bm_pixel_transparent(bm_handle,x,y); +} + +//Computes a bounding sphere for the current room +//Parameters: center - filled in with the center point of the sphere +// rp - the room we’re bounding +//Returns: the radius of the bounding sphere +float ComputeRoomBoundingSphere(vector *center,room *rp) +{ + //This algorithm is from Graphics Gems I. There's a better algorithm in Graphics Gems III that + //we should probably implement sometime. + + vector *min_x,*max_x,*min_y,*max_y,*min_z,*max_z,*vp; + float dx,dy,dz; + float rad,rad2; + int i; + +#ifdef NEWEDITOR + if (!rp->num_verts) + { + center->x = 0.0f; center->y = 0.0f; center->z = 0.0f; + return 0.0f; + } +#endif + + //Initialize min, max vars + min_x = max_x = min_y = max_y = min_z = max_z = &rp->verts[0]; + + //First, find the points with the min & max x,y, & z coordinates + for (i=0,vp=rp->verts;inum_verts;i++,vp++) { + + if (vp->x < min_x->x) + min_x = vp; + + if (vp->x > max_x->x) + max_x = vp; + + if (vp->y < min_y->y) + min_y = vp; + + if (vp->y > max_y->y) + max_y = vp; + + if (vp->z < min_z->z) + min_z = vp; + + if (vp->z > max_z->z) + max_z = vp; + } + + //Calculate initial sphere + + dx = vm_VectorDistance(min_x,max_x); + dy = vm_VectorDistance(min_y,max_y); + dz = vm_VectorDistance(min_z,max_z); + + if (dx > dy) + if (dx > dz) { + *center = (*min_x + *max_x) / 2; rad = dx / 2; + } + else { + *center = (*min_z + *max_z) / 2; rad = dz / 2; + } + else + if (dy > dz) { + *center = (*min_y + *max_y) / 2; rad = dy / 2; + } + else { + *center = (*min_z + *max_z) / 2; rad = dz / 2; + } + + + //Go through all points and look for ones that don't fit + rad2 = rad * rad; + for (i=0,vp=rp->verts;inum_verts;i++,vp++) { + vector delta; + float t2; + + delta = *vp - *center; + t2 = delta.x * delta.x + delta.y * delta.y + delta.z * delta.z; + + //If point outside, make the sphere bigger + if (t2 > rad2) { + float t; + + t = sqrt(t2); + rad = (rad + t) / 2; + rad2 = rad * rad; + *center += delta * (t - rad) / t; + } + } + + //We're done + return rad; +} + +//Create objects for the external rooms +void CreateRoomObjects() +{ + int objnum,r; + room *rp; + + //First delete any old room objects + for (objnum=0;objnum<=Highest_object_index;objnum++) + if (Objects[objnum].type == OBJ_ROOM) + ObjDelete(objnum); + + //Now go through all rooms & create objects for external ones + for (r=0,rp=Rooms;r<=Highest_room_index;r++,rp++) + if (rp->used && (rp->flags & RF_EXTERNAL)) { + vector pos; + float rad; + int roomnum,objnum; + + rad = ComputeRoomBoundingSphere(&pos,rp); + roomnum = GetTerrainRoomFromPos(&pos); + + ASSERT(roomnum != -1); + + objnum = ObjCreate(OBJ_ROOM,r,roomnum,&pos,NULL); + ASSERT(objnum != -1); //DAJ -1FIX moved up + Objects[objnum].size = rad; + Objects[objnum].wall_sphere_offset = Zero_vector; + Objects[objnum].anim_sphere_offset = Zero_vector; + + if((rad >= MIN_BIG_OBJ_RAD) && !(Objects[objnum].flags & OF_BIG_OBJECT)) + { + BigObjAdd(objnum); + } + // Type specific should have set up the size, so now we can compute the bounding box. + ObjSetAABB(&Objects[objnum]); + } +} + + +// returns the index of the first room that is being used. Returns -1 if there are none +int FindFirstUsedRoom () +{ + int i; + + for (i=0;i<=Highest_room_index;i++) + { + if (Rooms[i].used) + { + return i; + } + } + + Int3(); // Get Jason or Matt, no rooms in use! + return -1; + +} + +// Changes a face's texture within a room +// returns true on successs +bool ChangeRoomFaceTexture(int room_num,int face_num,int texture) +{ + if ( (room_num<0) || (room_num>Highest_room_index) || ROOMNUM_OUTSIDE(room_num) || (!Rooms[room_num].used) ){ + mprintf((0,"Invalid room passed to ChangeRoomFaceTexture\n")); + Int3(); + return false; + } + + room *rp = &Rooms[room_num]; + + if ( face_num<0 || face_num>=rp->num_faces ){ + mprintf((0,"Invalid face number passed to ChangeRoomFaceTexture. Room=%d, you gave face #%d, there are only %d in the room\n",room_num,face_num,rp->num_faces)); + Int3(); + return false; + } + + if (texture==-1){ + mprintf((0,"not a valid texture, passed to ChangeRoomFaceTexture\n")); + Int3(); + return false; + } + + face *fp = &rp->faces[face_num]; + + fp->tmap = texture; + fp->flags |= FF_TEXTURE_CHANGED; + rp->room_change_flags|=RCF_TEXTURE; + return true; +} + +// Clears the data for room changes +void ClearRoomChanges () +{ + for (int i=0;i1) + norm=1.0; + + if (Room_changes[i].fog) + { + + vector scale_color=((Room_changes[i].end_vector-Room_changes[i].start_vector)*norm)+Room_changes[i].start_vector; + float scale_depth=((Room_changes[i].end_depth-Room_changes[i].start_depth)*norm)+Room_changes[i].start_depth; + + rp->flags|=RF_FOG; + rp->room_change_flags|=RCF_CHANGING_WIND_FOG; + + rp->fog_r=scale_color.x; + rp->fog_g=scale_color.y; + rp->fog_b=scale_color.z; + rp->fog_depth=scale_depth; + } + else + { + vector scale_wind=((Room_changes[i].end_vector-Room_changes[i].start_vector)*norm)+Room_changes[i].start_vector; + + rp->room_change_flags|=RCF_CHANGING_WIND_FOG; + rp->wind=scale_wind; + } + + // If this room is done changing, take it out of the list and mark it as changed + if (norm>=1.0) + { + Room_changes[i].used=0; + if (Room_changes[i].fog) + rp->room_change_flags|=RCF_FOG; + else + rp->room_change_flags|=RCF_WIND; + + rp->room_change_flags&=~RCF_CHANGING_WIND_FOG; + + continue; + + } + } + +} + +// Sets up a room to change its fog or wind over time +int SetRoomChangeOverTime (int roomnum,bool fog,vector *end,float depth_end,float time) +{ + room *rp = &Rooms[roomnum]; + int index,i; + + // First search to see if there is another with this same roomnum + + int found=0; + for (i=0;iroom_change_flags|=RCF_CHANGING_WIND_FOG; + + if (fog) { + Room_changes[index].start_depth = rp->fog_depth; + Room_changes[index].start_vector.x = rp->fog_r; + Room_changes[index].start_vector.y = rp->fog_g; + Room_changes[index].start_vector.z = rp->fog_b; + Room_changes[index].end_depth=depth_end; + } + else + Room_changes[index].start_vector = rp->wind; + + return index; +} diff --git a/Descent3/room.h b/Descent3/room.h new file mode 100644 index 000000000..9b40ed175 --- /dev/null +++ b/Descent3/room.h @@ -0,0 +1,589 @@ +/* + * $Logfile: /DescentIII/main/room.h $ + * $Revision: 104 $ + * $Date: 5/05/99 5:03a $ + * $Author: Gwar $ + * + * Room structures & functions + * + * $Log: /DescentIII/main/room.h $ + * + * 104 5/05/99 5:03a Gwar + * renamed ned_GameTextures array to GameTextures in new editor to make + * game code happy; 3D texture view still does not display textures + * + * 103 4/30/99 12:56p Kevin + * Lowered values for MAX_SOUNDS, MAX_ROOMS, MAX_TEXTURES and MAX_OBJIDS. + * Talk to me before changing any of these again. + * + * 102 4/21/99 5:33a Gwar + * added a NEWEDITOR #define + * + * 101 3/24/99 3:27p Matt + * Increased the max number of room changes from 20 to 100. + * + * 100 2/09/99 9:59a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 99 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 98 1/21/99 11:34a Matt + * Got rid of portal triggers. Since we don't have multi-face portals, a + * face trigger works fine for a portal. Also fixed a few editor/trigger + * bugs. + * + * 97 1/21/99 11:20a Jason + * added a two new flags: strobe and flicker + * + * 96 1/20/99 6:11p Matt + * Fixed a couple bugs in the room wind/fog change system, and make the + * values change from current -> new instead of start -> end. + * + * 95 1/19/99 11:25a Jason + * added room (fog and wind) changing functions + * + * 94 1/15/99 3:14p Jason + * added combinable portals + * + * 93 1/11/99 11:17a Jason + * made fog not have that stupid telltale z problem + * + * 92 1/08/99 5:37p Samir + * reverb values per room. + * + * 91 12/22/98 2:03p Matt + * Added room names, and made rooms not compress so that room numbers are + * suitable for persistant uses. + * + * 90 11/30/98 3:59p Jason + * changed dynamic lighting to be better + * + * 89 11/02/98 6:15p Chris + * Room AABBs get saved with the level and the sort_face and dec_sort_face + * list s have been removed + * + * 88 10/06/98 6:08p Jason + * added RF_TRIANGULATE so that special rooms (with non-planar UVS) can be + * drawn correctly + * + * 87 10/01/98 10:33a Jason + * added room_change_flags + * + * 86 9/22/98 6:58p Samir + * Render_floating_triggers is _DEBUG code, so make it so. + * + * 85 9/22/98 3:55p Samir + * ifdef out stuff for non-debug version. + * + * 84 9/08/98 12:05p Jason + * moved doorway.h out of room.h + * + * 83 9/01/98 12:04p Matt + * Ripped out multi-face portal code + * + * 82 8/28/98 4:44p Jason + * optimized mirror rendering + * + * 81 8/27/98 5:19p Jason + * added first rev of reflected surfaces + * + * 80 8/19/98 2:17p Jeff + * made a function to change the texture on a face + * + * 79 8/17/98 6:40p Matt + * Added ambient sound system + * + * 78 7/21/98 2:14p Chris + * Some FVI speedups - not done + * + * 77 7/17/98 9:56a Chris + * Intermediate checkin + * + * 76 7/16/98 8:29p Chris + * Partial implementation of the new collide code + * + * 75 7/16/98 12:06p Jason + * added special flags to room structure + * + * 74 6/08/98 12:28p Matt + * Removed unused face flag, and changed some commenting. + * + * 73 6/05/98 5:22p Jason + * added volumetric fog + * + * 72 6/02/98 6:03p Jason + * added specular lightmaps + * + * 71 5/26/98 5:56p Jason + * only render coronas which are flagged + * + * 70 5/25/98 3:46p Jason + * added better light glows + * + * 69 5/22/98 3:26p Jason + * added better memory management for specular lighting + * + * 68 5/22/98 12:34p Matt + * Added scorch mark/bullet hole system. + * + * 67 5/15/98 5:41p Jason + * implemented volume lighting system + * + * 66 5/06/98 12:55p Jason + * did some vis effect optimizations + * + * 65 4/30/98 5:50p Jason + * more framerate optimizations + * + * 64 4/30/98 12:37p Jason + * added FF_FACING for non-backfaces + * + * 63 4/22/98 12:38p Chris + * Added path points to portals and rooms. Improved BOA auto-making + * process. + * + * 62 4/02/98 12:24p Jason + * trimmed some fat from our structures + * + * 61 3/18/98 4:31p Chris + * Speed up fvi and fixed some bugs + * + * 60 3/16/98 6:41p Jason + * added goal room stuff + * + * 59 3/16/98 5:50p Chris + * Added sorted face lists for fvi + * + * 58 3/13/98 12:05p Matt + * Changed the way we determine whether to render past a closed door to + * fix a render bug when the viewer is in a door room. + * Moved FaceIsRenderabl(), GetFaceAlpha(), and RenderPastPortal() from + * room.h to render.cpp. + * + * 57 3/06/98 3:23p Jason + * added lighting from satellites to indoor rooms + * + * 56 2/23/98 6:50p Jason + * changes to help facilitate fast lighting with shadow volumes + * + * + * 55 2/18/98 1:21p Jason + * upped max lightmap count + * + * 54 2/11/98 7:01p Chris + * Started to add wind + * + * 53 2/11/98 11:48a Matt + * Fixed alignment in room struct. + * + * 52 2/10/98 7:45p Matt + * Added (probably temporary) flags for goals + * + * 51 2/10/98 3:49p Jason + * added pulsing walls + * + * 50 2/10/98 1:12p Jason + * added forcefields and saturating walls + * + * 49 2/04/98 8:25p Jason + * added light multiplier for faces + * + * 48 2/02/98 7:07p Matt + * Added support for doors that can be seen through even when closed + * + * 47 2/02/98 5:13p Matt + * Added more generic function to compute a surface normal for a face + * + * 46 1/20/98 12:10p Jason + * implemented vis effect system + * + * 45 1/15/98 7:34p Matt + * Revamped error checking when computing face normals + * + * 44 1/15/98 3:47p Jason + * sped up room rendering on the terrain + * + * 43 1/12/98 3:34p Jason + * sped up indoor rendering by clipping faces against portals + * + * 42 11/24/97 1:30a Jason + * first attempt at adding shadows + * + * 41 11/14/97 9:02p Mark + * Increased the number of rooms from 100 to 500 (Matt on Mark's machine) + * + * 40 11/14/97 6:39p Jason + * added ability to do lighting on a single room + * + * 39 10/13/97 5:08p Matt + * Moved ComputeRoomBoundingSphere() & CreateRoomObjects() from editor to + * main + * + * + * 38 10/10/97 11:38a Jason + * put in better volumetric support + * + * 37 10/08/97 2:28p Jason + * got external rooms working with terrain lighting + * + * 36 10/01/97 7:51p Matt + * Added code for external rooms + * + * 35 9/19/97 8:09p Jason + * optimizations for dynamic lighting + * + * 34 9/19/97 2:52p Jason + * changes to fix lightmap seam problem + * + * 33 9/17/97 6:14p Jason + * did some optimizations due to running vtune + * + * 32 9/16/97 4:27p Matt + * Got rid of static_light & changed fields in the room struct + * + * 31 9/16/97 1:07a Matt + * Fixed rendering past open doors + * + * 30 9/15/97 5:33p Jason + * made portals check for doors + * + * 29 9/12/97 5:38p Jason + * got doors working + * + * 28 9/12/97 2:35p Matt + * Changed face flags to a word, and added destroyed flag + * + * 27 9/11/97 3:13p Matt + * Several more-or-less small changes, mostly with portal triggers + * + * 26 9/10/97 5:13p Matt + * Changed some flags & added a few functions + * + * 25 9/09/97 12:21p Matt + * Added new flags, deleted old ones, renamed others + * Added new functions + * Cleaned up structs, fixing alignment + * + * 24 9/06/97 10:53p Matt + * Added portal and face flags + * + * 23 9/04/97 11:23a Jason + * sped up dynamic light computation a bit + * + * 22 9/02/97 5:17p Jason + * changes for dynamic lighting + * + * 21 9/02/97 12:55p Jason + * classify faces as alphaed or not + * + * 20 8/28/97 12:31p Jason + * added hi-res lightmaps for radiosity + * + * 19 8/19/97 1:13p Jason + * added GetAreaForFace function + * + * 18 8/13/97 11:53a Jason + * moved ClearAllRoomLightmaps into room.cpp where it belongs + * + * 17 8/12/97 3:51p Jason + * tweaked lightmaps with radiosity + * + * 16 8/12/97 1:13p Chris + * Added AABBs. + * + * 15 8/12/97 1:10a Jason + * added code to support radiosity lighting + * + * 14 8/11/97 3:55p Chris + * Added a function to compute the rough center of a portal + * + * 13 8/04/97 5:35p Chris + * + * 12 8/01/97 4:38p Chris + * + * 11 7/18/97 5:36p Jason + * save changed paletted rooms on exit + * + * 10 7/17/97 11:50a Jason + * Upped max_Verts_per_face to 64 + * + * 9 7/16/97 1:50p Sean + * from JASON: upped max face limit to 3000 + * + * 8 6/30/97 1:30p Jason + * added netherspace stuff + * + * 7 6/27/97 4:15p Jason + * added more room functions + * + * 6 6/27/97 3:04p Jason + * added cool room stuff + * + * 5 6/26/97 2:37p Jason + * added combine_faces function and texturing to rooms + * + * 4 6/25/97 5:29p Jason + * added/modified code to display a room + * + * 3 6/24/97 1:51p Jason + * + * 2 6/18/97 12:40p Jason + * added include files + * + * 1 6/18/97 12:39p Jason + * + * $NoKeywords: $ + */ + +#ifndef _ROOM_H +#define _ROOM_H + +#include "pstypes.h" +#include "vecmat_external.h" +#include "gametexture.h" + +#ifdef NEWEDITOR +#include "..\neweditor\ned_GameTexture.h" +#endif + +#include "room_external.h" + +//Sizes for some global arrays +#define MAX_ROOMS 400 //max number of rooms in the world + +//Constants for room palette (editor-specific) +#if (defined(EDITOR) || defined(NEWEDITOR)) +#define FIRST_PALETTE_ROOM MAX_ROOMS //start of rooms for palette +#define MAX_PALETTE_ROOMS 50 //max number of loaded rooms +#else +#define MAX_PALETTE_ROOMS 0 //max number of loaded rooms +#endif + + +// Room change stuff +#define MAX_ROOM_CHANGES 100 +typedef struct +{ + int roomnum; + bool fog; + vector start_vector,end_vector; + float start_depth,end_depth; + float start_time; + float total_time; + ubyte used; +} room_changes; + +// +// Globals +// + +extern room Rooms[]; //global sparse array of rooms +extern int Highest_room_index; //index of highest-numbered room + +// +// Macros +// + +//Handy macro to convert a room ptr to a room number +#define ROOMNUM(r) (r-Rooms) + +// See above from RF_MINE_MASK +#define MINE_INDEX(x) ((Rooms[x].flags&RFM_MINE)>>20) + +// +// Functions +// + +// Zeroes out the rooms array +void InitRooms (); + +#ifdef _DEBUG +// Allows a spew'er to find out if he is in a room or external to the mine +// NOTE: THIS FUNCTION IS NOT FOR IN GAME STUFF. It is REALLY SLOW and accurate. +// Talk to Chris if you need something like this function. +int FindPointRoom(vector *pnt); + +//Put this here so we don't need to include render.h +extern bool Render_floating_triggers; + +#endif + +//Initalize a room, allocating memory and filling in fields +//Parameters: rp - the room to be initialized +// nverts - how many vertices this room will have +// nfaces - how many faces this room wil have +// nfaces - how many portals this room wil have +void InitRoom(room *rp,int nverts,int nfaces,int nportals); + +//Initialize a room face structure, allocating memory for vertlist and uvls +void InitRoomFace(face *fp,int nverts); + +//Frees a room, deallocating its memory and marking it as unused +void FreeRoom(room *rp); + +//Frees all the rooms currently in use, deallocating their memory and marking them as unused +void FreeAllRooms(); + +//Finds the center point of a room +//Parameters: vp - filled in with the center point +// rp - the room whose center to find +void ComputeRoomCenter(vector *vp,room *rp); + +//Computes (fills in) the surface normal of a face. +//Finds the best normal on this face by checking all sets of three vertices +//IMPORTANT: The caller should really check the return value of this function +//Parameters: rp,facenum - the room and face to calculate the normal for +//Returns: true if the normal is ok +// false if the normal has a very small (pre-normalization) magnitude +bool ComputeFaceNormal(room *rp,int facenum); + +//Compute the surface normal from a list of vertices that determine a face +//Finds the best normal on this face by checking all sets of three vertices +//IMPORTANT: The caller should really check the return value of this function +//Parameters: normal - this is filled in with the normal +// num_verts - how many vertices in the face +// vertnum_list - a list of vertex numbers for this face. these index into verts +// verts - the array of vertices into which the elements of vertnum_list index +//Returns: true if the normal is ok +// false if the normal has a very small (pre-normalization) magnitude +bool ComputeNormal(vector *normal,int num_verts,short *vertnum_list,vector *verts); + +//Finds the center point of a portal by averaging the points in the portal +//Parameters: vp - filled in with the center point +// rp - the room +// portal_index - the index of the portal whose center to find +void ComputePortalCenter(vector *vp, room *rp, int portal_index); + +//Computes the center point on a face by averaging the points in the face +void ComputeCenterPointOnFace(vector *vp,room *rp,int facenum); + +//Free the memory used by a room face structure +void FreeRoomFace(face *fp); + +// Removes all room lightmaps from memory and sets indoor faces accordingly +void ClearAllRoomLightmaps (int external); + +// Removes all room volume lights from memory +void ClearAllVolumeLights (); + +// Returns the area taken up by a face +float GetAreaForFace (room *rp,int facenum); + +//Check if a particular point on a wall is a transparent pixel +//Parameters: pnt - the point we're checking +// rp - pointer to the room that pnt is in +// facenum - the face that pnt is on +//Returns: true if can pass through the given point, else 0 +int CheckTransparentPoint(const vector *pnt,const room *rp,const int facenum); + +//Face physics flags returned by GetFacePhysicsFlags() +//Note that: +// it is illegal for a face to have both SOLID and TRANSPARENT +// it is legal, but probably not of interest, for a face to have SOLID & PORTAL +#define FPF_SOLID 1 //nothing passes through this face +#define FPF_TRANSPARENT 2 //face has transparency, so some things may be able to fly through it +#define FPF_PORTAL 4 //this face is in a portal. +#define FPF_RECORD 8 //take note of when an object passes through this face + +//Face physics types. These are combinations of the above flags +#define FPT_IGNORE 0 //completey ignore this face + +//Figure out how the physics should deal with a given face +//Parameters: rp - pointer to the room the face is in +// fp - the face we're interested in +//Returns: bitmask of flags (see above). +inline int GetFacePhysicsFlags(const room *rp,const face *fp) +{ + int ret = 0; + + //If face is a trigger, must record + if (fp->flags & FF_HAS_TRIGGER) + ret |= FPF_RECORD; + + //If it's a floating trigger, then we're done + if (fp->flags & FF_FLOATING_TRIG) + return ret; + + if (fp->flags & FF_VOLUMETRIC) + return ret; + + //Deal with faces that are part of a portal + if (fp->portal_num != -1) { + portal *pp = &rp->portals[fp->portal_num]; + + //Mark as portal + ret |= FPF_PORTAL; + + //Face is flythrough if we don't render the portal faces, or it's marked rendered flythrough + if (!(pp->flags & PF_RENDER_FACES) || (pp->flags & PF_RENDERED_FLYTHROUGH)) + return ret; + } + + //If we're here, it's either a non-portal face, or portal face that gets rendered + + //Check if the face is marked fly-through + if (GameTextures[fp->tmap].flags & TF_FLY_THRU) + return ret; + + //Check if the face is solid or transparent + int bm_handle = GetTextureBitmap(fp->tmap,0); + if (GameBitmaps[bm_handle].flags & BF_TRANSPARENT) + ret |= FPF_TRANSPARENT; + else + ret |= FPF_SOLID; + + //We're done + return ret; + +} + +//Computes a bounding sphere for the current room +//Parameters: center - filled in with the center point of the sphere +// rp - the room we’re bounding +//Returns: the radius of the bounding sphere +float ComputeRoomBoundingSphere(vector *center,room *rp); + +//Create objects for the external rooms +void CreateRoomObjects(); + +// Clears lightmaps for a single room +void ClearRoomLightmaps (int roomnum); + + +// returns the index of the first room that is being used. Returns -1 if there are none +int FindFirstUsedRoom (); + +// Clears specmaps for a single room +void ClearRoomSpecmaps (int roomnum); + +// Removes all room specularity maps from memory and sets indoor faces accordingly +// External=1 means to perform the operation on external rooms only, 0 means indoor rooms only +void ClearAllRoomSpecmaps (int external); + +extern void GetIJ(const vector *normal,int *ii,int *jj); + +// Changes a face's texture within a room +// returns true on successs +bool ChangeRoomFaceTexture(int room_num,int face_num,int texture); + +// Clears the data for room changes +void ClearRoomChanges (); + +// Returns index of room change allocatd, else -1 on error +int AllocRoomChange (); + +// Does whatever fading/changing of room stuff that needs to be done this frame +void DoRoomChangeFrame (); + + +// Sets up a room to change its fog or wind over time +int SetRoomChangeOverTime (int roomnum,bool fog,vector *end,float depth_end,float time); + +#endif diff --git a/Descent3/room_external.h b/Descent3/room_external.h new file mode 100644 index 000000000..05440003a --- /dev/null +++ b/Descent3/room_external.h @@ -0,0 +1,254 @@ +/* +* $Logfile: /DescentIII/main/room_external.h $ +* $Revision: 21 $ +* $Date: 10/21/99 1:32p $ +* $Author: Jeff $ +* +* Defines and structs for room definitions (for DLL export) +* +* $Log: /DescentIII/main/room_external.h $ + * + * 21 10/21/99 1:32p Jeff + * added checkbox to prevent certain rooms from lighting + * + * 20 4/28/99 1:39p Chris + * Added the ability to block portals + * + * 19 4/26/99 11:11a Chris + * Updated Bnode system + * + * 18 4/18/99 5:39a Chris + * Vastly improved the path node system + * + * 17 4/15/99 5:49p Chris + * Fixed a bug with rendering the BOAPath nodes + * + * 16 4/15/99 12:20p Jason + * made mirrors more robust and able to have multiple mirrored faces in + * the same room (as long as they are all on the same plane) + * + * 15 4/14/99 3:13p Chris + * Beginning to add BoaNode stuff + * + * 14 4/14/99 11:40a Jason + * added secret flag + * + * 13 4/05/99 10:54a Matt + * Added auto-waypoint system + * + * 12 3/31/99 5:14p Matt + * Added shell flag for faces + * + * 11 3/11/99 1:08p Jason + * more fixes for smooth specular lighting + * + * 10 3/01/99 8:10p Matt + * Added flag for manually-set path points + * + * 9 2/21/99 4:35p Chris + * Improving the level goal system... Not done. + * + * 8 2/19/99 4:26p Jason + * more work on Katmai support + * + * 7 2/09/99 9:59a Chris + * Massive BOA update :) Terrain happy now. Vis happy now. Sound happy + * now. + * + * 6 2/03/99 5:49p Matt + * Added room damage system + * + * 5 2/03/99 4:26p Jason + * made multiplayer coop actually work! + * + * 4 2/01/99 4:17p Jason + * more changes for multisafe + * + * 3 1/29/99 12:48p Matt + * Rewrote the doorway system + * + * 2 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h +* +* $NoKeywords: $ +*/ + + +#ifndef _ROOM_EXTERNAL_H_ +#define _ROOM_EXTERNAL_H_ + +#include "pstypes.h" +#include "vecmat_external.h" +#include "bnode.h" + +#define MAX_FACES_PER_ROOM 3000 //max number of faces per room +#define MAX_VERTS_PER_ROOM 10000 //max vertices per room +#define MAX_VERTS_PER_FACE 64 //max vertices per face + +//Face flags +//NOTE: If you add a flag here, please check the function CopyFaceFlags() +#define FF_LIGHTMAP 0x0001 // Render this face with a lightmap on top +#define FF_VERTEX_ALPHA 0x0002 // This face has vertex alpha blending +#define FF_CORONA 0x0004 // This face has a lighting corona +#define FF_TEXTURE_CHANGED 0x0008 // The texture on this face has changed +#define FF_HAS_TRIGGER 0x0010 // This face has a trigger +#define FF_SPEC_INVISIBLE 0x0020 // This face needs to be not rendered during specularity pass +#define FF_FLOATING_TRIG 0x0040 // This face only exists as a floating trigger +#define FF_DESTROYED 0x0080 // This face has been blown up +#define FF_VOLUMETRIC 0x0100 // This face is a volumetric face +#define FF_TRIANGULATED 0x0200 // ?? +#define FF_VISIBLE 0x0400 // This face is visible this frame (Valid only during render) +#define FF_NOT_SHELL 0x0800 // This face is not part of the room shell +#define FF_TOUCHED 0x1000 // This face has been touched by fvi_QuickDistFaceList +#define FF_GOALFACE 0x2000 // This face is a goal texture face +#define FF_NOT_FACING 0x4000 // This face is not facing us this frame (Valid only during render) +#define FF_SCORCHED 0x8000 // This face has one or more scorch marks +//NOTE: If you add a flag here, please check the function CopyFaceFlags() + +//UVLs for room verts +typedef struct roomUVL +{ + float u,v; //texture coordinates + float u2,v2; + ubyte alpha; //alpha for this vertex +} roomUVL; + +//an n-sided polygon used as part of a room or portal +typedef struct face { + ushort flags; // flags for this face (see above) + ubyte num_verts; // how many vertices in this face + sbyte portal_num; // which portal this face is part of, or -1 if none + + short *face_verts; // index into list of vertices for this face + roomUVL *face_uvls; // index into list of uvls for this face + vector normal; // the surface normal of this face + short tmap; // texture numbers for this face + ushort lmi_handle; // the lightmap info number for this face + short special_handle; // the index into the special_faces array + ubyte renderframe; // what frame this face was last rendered (for lighting) + ubyte light_multiple; // what multiple to times by + vector min_xyz,max_xyz; // min & max extents of this face (for FVI) +} face; + + +//Portal flags +#define PF_RENDER_FACES 1 // render the face(s) in the portal +#define PF_RENDERED_FLYTHROUGH 2 // allow flythrough of rendered faces +#define PF_TOO_SMALL_FOR_ROBOT 4 // Too small for a robot to use for path following (like a small window) +#define PF_COMBINED 8 // This portal has been combined with another for rendering purposes +#define PF_CHANGED 16 // Used for multiplayer - this portal has been changed +#define PF_BLOCK 32 +#define PF_BLOCK_REMOVABLE 64 + + +//a connection between two rooms +typedef struct portal +{ + int flags; // flags for this portal + short portal_face; // the face for this portal + short croom; // the room this portal connects to + short cportal; // the portal in croom this portal connects to + short bnode_index; + int combine_master; // For rendering combined portals + vector path_pnt; // Point used by the path system +} portal; + +//Room flags +#define RF_FUELCEN 1 // room is a refueling center +#define RF_DOOR (1<<1) // a 3d door is here. +#define RF_EXTERNAL (1<<2) // this is an external room (i.e. a building) +#define RF_GOAL1 (1<<3) // this room is goal 1 +#define RF_GOAL2 (1<<4) // this room is goal 2 +#define RF_TOUCHES_TERRAIN (1<<5) // this room should recieve lighting from satellites +#define RF_SORTED_INC_Y (1<<6) // Faces are sorted with increasing y +#define RF_GOAL3 (1<<7) // this room is goal 3 +#define RF_GOAL4 (1<<8) // this room is goal 4 +#define RF_FOG (1<<9) // this room is fogged +#define RF_SPECIAL1 (1<<10) // This room is a special room +#define RF_SPECIAL2 (1<<11) // This room is a special room +#define RF_SPECIAL3 (1<<12) // This room is a special room +#define RF_SPECIAL4 (1<<13) // This room is a special room +#define RF_SPECIAL5 (1<<14) // This room is a special room +#define RF_SPECIAL6 (1<<15) // This room is a special room +#define RF_MIRROR_VISIBLE (1<<16) // The mirror is this room is visible +#define RF_TRIANGULATE (1<<17) // All the faces in this room should be drawn with triagulation on +#define RF_STROBE (1<<18) // This room strobes with pulse lighting +#define RF_FLICKER (1<<19) // This room flickers with pulse lighting +#define RFM_MINE ((1<<20) | (1<<21) | (1<<22) | (1<<23) | (1<<24)) // Mine index of this room (we support up to 32 individual mines without a problem) +#define RF_INFORM_RELINK_TO_LG (1<<25) // Informs the level goal system on player relinking to this room +#define RF_MANUAL_PATH_PNT (1<<26) //The room path_pnt has been set manually (i.e. by the designer) +#define RF_WAYPOINT (1<<27) //This room has a waypoint in it +#define RF_SECRET (1<<28) // This room is a secret room +#define RF_NO_LIGHT (1<<29) // This room does not get lit + +#define GOALROOM (RF_GOAL1|RF_GOAL2|RF_GOAL3|RF_GOAL4) + +#define ROOM_NAME_LEN 19 //how long a room name can be (not counting null terminator) + +struct doorway; + +//the basic building-block of a Descent 3 level +typedef struct room { + int flags; // various room flags + + int num_faces; // how many poygons in this room + int num_portals; // how many connections in this room + int num_verts; // how many verts in the room + face *faces; // pointer to list of faces + portal *portals; // pointer to list of portals + vector *verts; // array of vertices for this room + vector4 *verts4; // array of 16byte vertices for this room + + + doorway *doorway_data; // pointer to this room's doorway data, or NULL if not a doorway + char *name; // name of this room, or NULL + int objects; // index of first object in this room + vector max_xyz,min_xyz; // for external room visibility checking + + float last_render_time; // Last time we rendered this room + + //Hierarchical bounding boxes for this room + vector bbf_min_xyz; + vector bbf_max_xyz; + short num_bbf_regions; + short pad1; + short **bbf_list; + short *num_bbf; + vector *bbf_list_min_xyz; + vector *bbf_list_max_xyz; + ubyte *bbf_list_sector; + + bn_list bn_info; + + short wpb_index; // world point buffer index - where this room starts + ubyte pulse_time; // each room can has a pulse time + ubyte pulse_offset; // each room has a timer offset for which it pulses + vector wind; // Wind vector for the room + int ambient_sound; // Index of ambient sound pattern for this room, or -1 if none + short vis_effects; // index of first visual effect in this room + short mirror_face; // Index of face that this room is to be mirrored by + ubyte num_mirror_faces; // Number of faces in this room that have the same texture as the mirror + ushort *mirror_faces_list; // the list of faces in this room that have the same texture as the mirror + float damage; // The damage per second applied to players (& maybe others) in room + + vector path_pnt; // Point used by the path system + ubyte *volume_lights; // Pointer to memory for our volumetric lighting + short volume_width; // The dimensions of our volumetric room + short volume_height; + short volume_depth; + float fog_depth; // How far until fog is totally opaque + float fog_r,fog_g,fog_b; // Fog color + + ubyte env_reverb; // environmental reverb preset + ubyte room_change_flags; // For multiplayer, detects what characteristics have to be sent + ubyte damage_type; // What type of damage this rooms does (for sound) if damage > 0 + ubyte used; // is this room holding data? + +} room; + +#endif \ No newline at end of file diff --git a/Descent3/scorch.cpp b/Descent3/scorch.cpp new file mode 100644 index 000000000..1ebf76064 --- /dev/null +++ b/Descent3/scorch.cpp @@ -0,0 +1,390 @@ +/* + * $Logfile: /DescentIII/Main/scorch.cpp $ + * $Revision: 20 $ + * $Date: 5/13/99 3:42p $ + * $Author: Ardussi $ + * + * System for drawing scorch marks on walls + * + * $Log: /DescentIII/Main/scorch.cpp $ + * + * 20 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 19 4/30/99 12:10p Matt + * Don't add a scorch if it will overlap an edge of a face. + * + * 18 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 17 4/16/99 11:31p Jeff + * added max() for Linux + * + * 16 1/26/99 5:06p Jason + * no scorch marks on procedurals + * + * 15 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 14 12/10/98 12:27p Jason + * added cooler specular mapping for objects + * + * 13 12/03/98 12:26p Jason + * fixed some scorch slowdowns with direct3d + * + * 12 6/24/98 10:19p Matt + * When too many scorches per face, don't add new ones, rather than not + * drawing old ones. + * + * 11 5/26/98 6:15p Matt + * Cleaned up and finished scorch mark limiting code. + * + * 10 5/26/98 3:59p Jason + * added LIGHTMAP_BLEND_CONSTANT alpha type + * + * 9 5/26/98 2:38p Matt + * Added code (not really done yet) to fade out far-away marks, and to + * limit the number of marks drawn. + * + * 8 5/25/98 8:36p Matt + * Added code to set different sizes for different weapon scorch marks. + * Also, don't leave scorch marks on lights. + * + * 7 5/22/98 5:58p Matt + * Bail (for now) if try to make a scorch mark on the ground. + * + * 6 5/22/98 5:20p Matt + * Added randomized rotations for scorces + * + * 5 5/22/98 4:32p Jason + * fixed stupid z buffer write mask bug + * + * 4 5/22/98 3:57p Jason + * fixed a zbuffer problem with scorches + * + * 3 5/22/98 1:16p Matt + * Fixed stupid bug + * + * 2 5/22/98 12:34p Matt + * Added scorch mark/bullet hole system. + * + * 1 5/21/98 11:32p Matt + * + */ + +#include + +#include "pserror.h" +#include "renderer.h" +#include "3d.h" +#include "scorch.h" +#include "room.h" +#include "config.h" +#include "object_external_struct.h" //for ROOMNUM_OUTSIDE macro +#include "psrand.h" + +#ifdef __LINUX__ +#define max(a,b) ((a>b)?a:b) //where is linux max()?? +#endif + +//Structure for storing scorch marks +typedef struct { + int roomface; //room number & face number combined into an int + vector pos; //the position of the center of the scorch mark + ubyte handle_index; //which mark? + sbyte rx,ry,rz; //right vector + sbyte ux,uy,uz; //up vector + ubyte size; //floating-point size times 16 +} scorch; + +//How many scorch marks +#define MAX_SCORCHES 500 + +//Buffer of scorch marks +scorch Scorches[MAX_SCORCHES]; + +//Indices into scorch buffer. Start is oldest item, end is newest item +int Scorch_start,Scorch_end; + +//Bitmap handles for scorches +#define MAX_SCORCH_TEXTURES 10 +int Num_scorch_textures = 0; +int Scorch_texture_handles[MAX_SCORCH_TEXTURES]; + +//Pack & unpack roomnum & facenum into an int +#define ROOMFACE(r,f) ((r << 16) + f) +#define RF_ROOM(rf) (rf >> 16) +#define RF_FACE(rf) (rf & 0xffff) + +//Value used to limit the number of scorches drawn per face +#define MAX_VIS_COUNT 30 + +//Called when a new level is started to reset the scorch list +void ResetScorches() +{ + Scorch_start = Scorch_end = -1; +} + +//Delete the specified scorch mark +void DeleteScorch(int index) +{ + int roomface = Scorches[index].roomface; + scorch *sp; + int i; + + //mprintf((0,"Deleting scorch %d\n",index)); + + //Look through all the scorches to see if there are other scorches on the same face + for (i=Scorch_start,sp=&Scorches[Scorch_start];;) { + + if ((sp->roomface == roomface) && (i != index)) //Found another scorch + return; + + //Check for end of loop + if (i == Scorch_end) + break; + + //Increment & wrap + if (++i == MAX_SCORCHES) { + i = 0; + sp = Scorches; + } + else + sp++; + } + + //If we're here, there are no other scorches on the face, so clear the flag + Rooms[RF_ROOM(roomface)].faces[RF_FACE(roomface)].flags &= ~FF_SCORCHED; + //mprintf((0,"Clearing scorch flag from %d:%d\n",RF_ROOM(roomface),RF_FACE(roomface))); +} + +//Add a scorch mark +//Parameters: roomnum,facenum - the face that the scorch is on +// pos - where the scorch goes +// texture_handle - the texture for the scorch mark +// size - how big the scorch is +void AddScorch(int roomnum,int facenum,vector *pos,int texture_handle,float size) +{ + //Bail if on terrain. We should probably add support for this later. + if (ROOMNUM_OUTSIDE(roomnum)) + return; + + room *rp = &Rooms[roomnum]; + face *fp = &rp->faces[facenum]; + + //Check if face is a light, and if so don't add the scorch + if (GameTextures[fp->tmap].flags & (TF_LIGHT|TF_PROCEDURAL)) + return; + + int roomface = ROOMFACE(roomnum,facenum); + + //Count the number of scorches on this face, and bail if already at max + int count = 0; + int i; + scorch *sp; + for (i=Scorch_start,sp=&Scorches[Scorch_start];;) { + float size = (float) sp->size / 16.0; + + //Increment count, and stop drawing if hit limit + if (sp->roomface == roomface) { //Found one! + count += __max((int) size,1); //count large marks more + if (count >= MAX_VIS_COUNT) + return; + } + + //Check for end of loop + if (i == Scorch_end) + break; + + //Increment & wrap + if (++i == MAX_SCORCHES) { + i = 0; + sp = Scorches; + } + else + sp++; + } + + //Check for scorch going off the edge of the face, and bail if does + vector *v0,*v1=&rp->verts[fp->face_verts[fp->num_verts-1]]; + for (int v=0;vnum_verts;v++) { + vector edgevec,checkvec,checkp; + float dot,dist; + + v0 = v1; + v1 = &rp->verts[fp->face_verts[v]]; + vm_GetNormalizedDir(&edgevec,v1,v0); + + checkvec = *pos - *v0; + dot = checkvec * edgevec; + checkp = *v0 + edgevec * dot; + dist = vm_VectorDistance(&checkp,pos); + if (dist < size) + return; + } + + //Get next item in circular list, freeing oldest item if necessary + int new_end = Scorch_end+1; + if (new_end == MAX_SCORCHES) + new_end = 0; + if (new_end == Scorch_start) { + DeleteScorch(Scorch_start); + Scorch_start++; + if (Scorch_start == MAX_SCORCHES) + Scorch_start = 0; + } + Scorch_end = new_end; + if (Scorch_start == -1) + Scorch_start = 0; + + //Get a pointer to our struct + sp = &Scorches[Scorch_end]; + + //Fill in the data + sp->roomface = roomface; + sp->pos = *pos; + ASSERT((size >= 0.0) && (size < 15.0)); + sp->size = size * 16.0; + + int handle_index; + + //Get index for handle + for (handle_index=0;handle_indexhandle_index = handle_index; + + //Calculate the vectors + matrix m; + vm_VectorAngleToMatrix(&m,&fp->normal,ps_rand() * 2); + + //Store the vectors as signed 8-bit values + sp->rx = -m.rvec.x * 127; + sp->ry = -m.rvec.y * 127; + sp->rz = -m.rvec.z * 127; + + sp->ux = m.uvec.x * 127; + sp->uy = m.uvec.y * 127; + sp->uz = m.uvec.z * 127; + + //Flag this face as being scorched + fp->flags |= FF_SCORCHED; +} + +//The UVs for the blob bitmap +struct { + float u,v; +} scorch_uvs[4] = { {0.0,0.0}, {1.0,0.0}, {1.0,1.0}, {0.0,1.0} }; + +//Values used to get rid of far-away scorches +#define MAX_VIS_DISTANCE 200 //if mark farther than this, don't draw +#define FADE_START_DISTANCE 170 //when mark is this far away, start fading + +//Draw the scorch(es) for a given face +void DrawScorches(int roomnum,int facenum) +{ + int i; + scorch *sp; + + if (!Detail_settings.Scorches_enabled) + return; + + int roomface = ROOMFACE(roomnum,facenum); + + //Set alpha, transparency, & lighting for this face + if (!StateLimited) + { + rend_SetAlphaType(AT_LIGHTMAP_BLEND); + rend_SetAlphaValue(255); + rend_SetLighting(LS_NONE); + rend_SetColorModel(CM_MONO); + rend_SetOverlayType(OT_NONE); + rend_SetZBias (-.5); + rend_SetZBufferWriteMask (0); + + //Select texture type + rend_SetTextureType(TT_LINEAR); + } + + ASSERT(Scorch_end != -1); + + //Loop through all the scorches, and draw the ones for this face + for (i=Scorch_start,sp=&Scorches[Scorch_start];;) { + + if (sp->roomface == roomface) { //Found one! + vector right,up; + vector corners[4]; + g3Point points[4]; + g3Point *pointlist[4]; + float size = (float) sp->size / 16.0; + + //Don't draw mark if too far away, and make smaller those marks that are almost too far + float depth = g3_CalcPointDepth(&sp->pos); + if (depth > MAX_VIS_DISTANCE) + goto skip_draw; + if (depth > FADE_START_DISTANCE) + size *= 1.0 - ((depth - FADE_START_DISTANCE) / (MAX_VIS_DISTANCE - FADE_START_DISTANCE)); + + //Calculate vectors to corners + right.x = (float) sp->rx * (size / 127.0); + right.y = (float) sp->ry * (size / 127.0); + right.z = (float) sp->rz * (size / 127.0); + + up.x = (float) sp->ux * (size / 127.0); + up.y = (float) sp->uy * (size / 127.0); + up.z = (float) sp->uz * (size / 127.0); + + //Compute four corners + corners[0] = sp->pos - right + up; + corners[1] = sp->pos + right + up; + corners[2] = sp->pos + right - up; + corners[3] = sp->pos - right - up; + + //Rotate the points. Set uv values + for (int p=0;p<4;p++) { + g3_RotatePoint(&points[p],&corners[p]); + pointlist[p] = &points[p]; + points[p].p3_uvl.u = scorch_uvs[p].u; + points[p].p3_uvl.v = scorch_uvs[p].v; + points[p].p3_l=1.0; + points[p].p3_flags |= PF_UV|PF_L; + } + + //Get the bitmap handle + int bm_handle = GetTextureBitmap(Scorch_texture_handles[sp->handle_index],0); + + //Draw the polygon + g3_DrawPoly(4,pointlist,bm_handle); + } +skip_draw:; + + //Check for end of loop + if (i == Scorch_end) + break; + + //Increment & wrap + if (++i == MAX_SCORCHES) { + i = 0; + sp = Scorches; + } + else + sp++; + } + + //Reset rendering states + if (!StateLimited) + { + rend_SetZBias (0); + rend_SetZBufferWriteMask (1); + } +} diff --git a/Descent3/scorch.h b/Descent3/scorch.h new file mode 100644 index 000000000..21d890f5b --- /dev/null +++ b/Descent3/scorch.h @@ -0,0 +1,37 @@ +/* + * $Logfile: /DescentIII/main/scorch.h $ + * $Revision: 3 $ + * $Date: 5/25/98 8:36p $ + * $Author: Matt $ + * + * Header for scorch system + * + * $Log: /DescentIII/main/scorch.h $ + * + * 3 5/25/98 8:36p Matt + * Added code to set different sizes for different weapon scorch marks. + * Also, don't leave scorch marks on lights. + * + * 2 5/22/98 12:34p Matt + * Added scorch mark/bullet hole system. + * + * 1 5/21/98 11:32p Matt + * + */ + +#include "vecmat.h" + +//Called when a new level is started to reset the data +void ResetScorches(); + +//Add a scorch mark +//Parameters: roomnum,facenum - the face that the scorch is on +// pos - where the scorch goes +// texture_handle - the texture for the scorch mark +// size - how big the scorch is +void AddScorch(int roomnum,int facenum,vector *pos,int texture_handle,float size); + +//Draw the scorch(es) for a given face +void DrawScorches(int roomnum,int facenum); + + diff --git a/Descent3/screens.cpp b/Descent3/screens.cpp new file mode 100644 index 000000000..6e647de7a --- /dev/null +++ b/Descent3/screens.cpp @@ -0,0 +1,753 @@ +/* + * $Logfile: /DescentIII/main/screens.cpp $ + * $Revision: 52 $ + * $Date: 10/21/99 9:29p $ + * $Author: Jeff $ + * + * + * + * $Log: /DescentIII/main/screens.cpp $ + * + * 52 10/21/99 9:29p Jeff + * B.A. Macintosh code merge + * + * 51 5/24/99 4:16a Jeff + * fixed screenshot bug for multiplayer + * + * 50 5/23/99 7:22p Samir + * flush keyboard and mouse when entering screen! + * + * 49 5/23/99 3:53a Samir + * fixed printscreen. + * + * 47 5/20/99 6:14p Kevin + * fixed mouse button to close PLR + * + * 46 5/11/99 6:33p Kevin + * fixed problem with sound being played as 3d and view position not being + * equal to the player position! Doh + * + * 45 5/07/99 5:38p Jeff + * use new multiplayer plr screen + * + * 44 5/07/99 2:51p Jason + * fixed a bunch of endlevel multiplayer issues + * + * 43 5/05/99 7:46p Kevin + * Fixed some post level results things + * + * 42 5/02/99 5:30p Kevin + * fixed shield and energy ratings. + * + * 41 4/30/99 3:03p Kevin + * improved times restored count + * + * 40 4/26/99 11:34a Samir + * wrap startframe and endframe around plrresults UI callback. + * + * 39 4/23/99 1:56a Matt + * Added system for counting kills of friendly objects + * + * 38 4/21/99 4:49p Kevin + * + * 37 4/20/99 8:58p Jeff + * included joystick.h so it compiles in Linux + * + * 36 4/20/99 2:34p Kevin + * multiplayer post level results will automatically go to the next level + * after 30 seconds so the client doesn't timeout in the reliable code. + * This number should never be higher than the timeout for the reliable + * networking code in networking.cpp (currently 2 minutes) + * + * 35 4/20/99 2:00p Kevin + * Improved post level results screens + * + * 34 4/20/99 12:45a Matt + * Use briefing font instead of HUD font for post-level results screen. + * + * 33 4/18/99 2:48p Kevin + * lock PLR at 30 fps + * + * 32 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 31 4/07/99 12:41p Kevin + * New strings + * + * 30 4/07/99 12:31p Matt + * Added code for failed missions. + * + * 29 4/07/99 11:31a Kevin + * Added success/failure to Post level results + * + * 28 4/06/99 6:02p Matt + * Added score system + * + * 27 3/28/99 4:11p Kevin + * Fixed compiler warning + * + * 26 3/23/99 5:04p Kevin + * Post level results stuff. + * + * 25 3/23/99 12:33p Kevin + * More post level results improvements + * + * 24 3/22/99 7:02p Kevin + * New post level results screens.... not done. + * + * 23 3/09/99 6:36p Samir + * space bar to quit + * + * 22 2/15/99 9:17p Jeff + * table parse for plr screens + * + * 21 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 20 10/22/98 10:50a Samir + * only register kill for robots so accuracy rating can be closer. + * + * 19 10/18/98 12:15p Jeff + * set multi_bail_ui_menu to false when starting PLR screen, else it bails + * immediatly + * + * 18 10/17/98 7:26p Samir + * destroy window after closing. + * + * 17 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 16 10/07/98 12:08p Samir + * took out buttons. + * + * 15 10/02/98 5:46p Samir + * took out mission file objective text and replaced with level goals. + * + * 14 9/15/98 5:44p Jason + * more work on dedicated server + * + * 13 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 12 6/17/98 3:27p Jeff + * Changes made for localization + * + * 11 6/16/98 10:54a Jeff + * + * 10 6/11/98 1:56p Jeff + * use d3m files instead of dll + * + * 9 6/10/98 2:08p Samir + * multiplayer screens loaded. + * + * 8 5/25/98 8:19p Samir + * Added E3 speed demo to pass through screens. + * + * 7 5/15/98 2:22p Jeff + * Sets default colors for Post level results screen + * + * 6 5/14/98 5:27p Jason + * fixed some more sequencing bugs + * + * 5 5/14/98 3:18p Jason + * finished multiplayer sequencing issues + * + * 4 5/11/98 7:45p Samir + * hopefully fixed some flash in bugs. + * + * 3 4/18/98 7:18a Samir + * added level objectives text to result screen. + * + * 2 4/18/98 2:08a Samir + * update. + * + * 1 4/14/98 8:18p Samir + * initial revision. + * + * $NoKeywords: $ + */ + + +#include "screens.h" +#include "newui.h" +#include "game.h" +#include "ddio.h" +#include "descent.h" +#include "application.h" +#include "gamefont.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "player.h" +#include "Mission.h" +#include "game2dll.h" +#include "stringtable.h" +#include "dedicated_server.h" +#include "levelgoal.h" +#include "sounds.h" +#include "pilot.h" +#include "joystick.h" + +#define LVLRES_FADEIN_TIME 0.50f +#define TEXT_REDRAW_COUNT 6.0f +#define LVLRES_STATS 7 + +/* +$$TABLE_GAMEFILE "mplvlr1.ogf" +$$TABLE_GAMEFILE "lvlres0.ogf" +$$TABLE_GAMEFILE "mplvlr2.ogf" +$$TABLE_GAMEFILE "lvlres1.ogf" +$$TABLE_GAMEFILE "mplvlr3.ogf" +$$TABLE_GAMEFILE "lvlres2.ogf" +*/ +#define BACKGROUND_VIS 0 +#define OVERLAY_VIS 1 + +static int Stat_ratings[] = { + TXI_SCRNNONE, + TXI_SCRNFEEBLE, + TXI_SCRNPOOR, + TXI_SCRNAVG, + TXI_SCRNGOOD, + TXI_SCRNEXCELLENT, + TXI_SCRNSUPREME +}; + + +static struct +{ + int state; + float state_time, this_frame_time, last_frame_time; + + tLargeBitmap first_back; + tLargeBitmap glow_back; + tLargeBitmap shade_back; + + struct { + tLargeBitmap *bmp; + } + vis_list[2]; + + short time_min, time_sec; + float shield_rating, energy_rating; + float accuracy_rating; +} +PLR; + + +void PLResultsFrame(); +void PLResultsText(float draw_mag); +void PLResultsVisFrame(); +const char *GetNormalizedRating(float mag); +void PaintPLRSinglePlayerText(); +void SinglePlayerPostLevelResults(); + +static tLargeBitmap level_bmp; +bool background_loaded = false; +bool PLR_success = true; + + +void PLResultsFrame() +{ + StartFrame(0,0,Max_window_w, Max_window_h); + Sound_system.BeginSoundFrame(false); + //Right now we don't want you to do anything + if(background_loaded) + DrawLargeBitmap(&level_bmp,0,0, 1.0f); + //Call the DLL so it can do it's thing + if(Game_mode & GM_MULTI) + { + CallGameDLL (EVT_CLIENT_GAMEPOSTLEVELRESULTS,&DLLInfo); + } + else + { + //Draw the text here + PaintPLRSinglePlayerText(); + + } + grtext_Flush(); + Sound_system.EndSoundFrame(); + EndFrame(); +} + +float paint_start = 0.0f; +#define MAX_PLR_LINES 20 +bool val_printed[MAX_PLR_LINES]; + +int paint_in_sound_id = -1; +int paint_in_sound_handle = -1; +int chunk_sound_id = -1; + +#define MAX_PLR_FPS_TIME .033 +extern void DoScreenshot(); +bool PostLevelResults(bool success) +{ + PLR_success = success; + + if (Dedicated_server) + return true; + + paint_in_sound_id = FindSoundName("PPR paint in"); + chunk_sound_id = FindSoundName("PPR chunk"); + + float timelast = timer_GetTime(); + + int old_mode = GetScreenMode(); + SetScreenMode(SM_MENU); + SetUICallback(PLResultsFrame); + ui_HideCursor(); + ddio_KeyFlush(); + ddio_MouseQueueFlush(); + + //Do the multiplayer stuff + if(Game_mode & GM_MULTI) + { + //Draw the background + if (!LoadLargeBitmap("mplvlr.ogf", &level_bmp)) + Error("Unable to load %s.", "mplvlr.ogf"); + else + background_loaded = true; + + // Play a sound + Sound_system.Play2dSound(SOUND_LIGHTNING); + + float start_time=timer_GetTime(); + float time_to_wait; + + if (Netgame.local_role==LR_SERVER) + time_to_wait=10; + else + time_to_wait=15; + + while ((timer_GetTime()-start_time)defer(); + DoUIFrame(); + rend_Flip(); + GetUIFrameResult(); + } + + int keypressed = ddio_KeyInKey(); + if(keypressed==KEY_PRINT_SCREEN) + { + DoScreenshot(); + } + ddio_KeyFlush(); + } + else + { + //Do the single player stuff + if(!background_loaded) + { + if(LoadLargeBitmap("multimain.ogf", &level_bmp)) + { + background_loaded = true; + } + SinglePlayerPostLevelResults(); + float start_time=timer_GetTime(); + float time_to_wait = 2; + + while ((timer_GetTime()-start_time)delay(0); + } + timelast = timer_GetTime(); + + Descent->defer(); + DoUIFrame(); + rend_Flip(); + GetUIFrameResult(); + } + } + } + + bool rval = true; + float max_wait_time = 10; + float start_time=timer_GetTime(); + //Wait for input and move on + while(Netgame.local_role!=LR_SERVER) + { + if(Game_mode & GM_MULTI) + { + if((timer_GetTime()-start_time)>max_wait_time) + { + break; + } + } + while((timer_GetTime()-timelast)delay(0); + } + timelast = timer_GetTime(); + + Multi_bail_ui_menu = 0; + Descent->defer(); + int x,y,dx,dy; + if(ddio_MouseGetState(&x,&y,&dx,&dy)) + { + rval = true; + break; + } + + + + DoUIFrame(); + rend_Flip(); + GetUIFrameResult(); + + + int key = ddio_KeyInKey(); + +#ifndef MACINTOSH + tJoyPos jp; + joy_GetPos((tJoystick)JOYSTICK_1,&jp); + if(jp.buttons) + { + rval = true; + break; + } +#endif + + if( (key==KEY_ENTER) || (key==KEY_SPACEBAR) ) + { + rval = true; + break; + } + else if(key==KEY_ESC) + { + rval = false; + break; + } + else if (key==KEY_PRINT_SCREEN) { + DoScreenshot(); + } +#ifndef RELEASE + else if(key==KEY_R) + { + paint_start = timer_GetTime(); + for(int a=0;a 0) + PLR.accuracy_rating = (float)Players[Player_num].num_hits_level/(float)Players[Player_num].num_discharges_level; + else + PLR.accuracy_rating = 0.5f; + + //strcpy(PLRHeader,PLR_success?"Mission Successfull":"Mission Failed"); + strcpy(PLRHeader,PLR_success?TXT_PLR_MSN_SUCCESS:TXT_PLR_MSN_FAILED); + + sprintf(PLRLevel,"%s",Level_info.name); + sprintf(PLRText[curline],"%s",TXT_PLTDIFFICULT); + ubyte dif; + Current_pilot.get_difficulty(&dif); + sprintf(PLRVal[curline],"%s",difficulty_levels[dif]); + curline++; + curline++; + + sprintf(PLRText[curline],"%s",TXT_SCORE); + sprintf(PLRVal[curline],"%d",Players[Player_num].score); + curline++; + curline++; + + sprintf(PLRText[curline],"%s",TXT_PLRTIME); + sprintf(PLRVal[curline],"%d:%.2d",PLR.time_min,PLR.time_sec); + curline++; + curline++; + + sprintf(PLRText[curline],"%s",TXT_PLRENEMIESKILL); + sprintf(PLRVal[curline],"%d",Players[Player_num].num_kills_level); + curline++; + curline++; + + if (Players[Player_num].friendly_kills_level) { + sprintf(PLRText[curline],"%s",TXT_PLRFRIENDSKILL); + sprintf(PLRVal[curline],"%d",Players[Player_num].friendly_kills_level); + curline++; + curline++; + } + + sprintf(PLRText[curline],"%s",TXT_PLRSHIELD); + sprintf(PLRVal[curline],"%.0f",PLR.shield_rating); + curline++; + sprintf(PLRText[curline],"%s",TXT_PLRENERGY); + sprintf(PLRVal[curline],"%.0f",PLR.energy_rating); + curline++; + curline++; + + sprintf(PLRText[curline],"%s",TXT_NUMDEATHS); + sprintf(PLRVal[curline],"%d",Players[Player_num].num_deaths_level); + curline++; + sprintf(PLRText[curline],"%s",TXT_PLRSAVERESRAT); + sprintf(PLRVal[curline],"%d",Times_game_restored); + curline++; + curline++; + + bool has_objectives = false; + for (j = 0; (j < Level_goals.GetNumGoals()) && (curline < MAX_PLR_LINES); j++) + { + int goal_status; + + if (Level_goals.GoalStatus(j, LO_GET_SPECIFIED, &goal_status)) + { + if ( (goal_status & LGF_ENABLED) && (goal_status & LGF_TELCOM_LISTS) ) + { + has_objectives = true; + break; + } + } + } + + if(has_objectives) + { + sprintf(PLRText[curline],TXT_OBJECTIVES1); + curline++; + curline++; + } + + for (j = 0; (j < Level_goals.GetNumGoals()) && (curline < MAX_PLR_LINES); j++) + { + int goal_status; + + if (Level_goals.GoalStatus(j, LO_GET_SPECIFIED, &goal_status)) { + if ( (goal_status & LGF_ENABLED) && (goal_status & LGF_TELCOM_LISTS) ) + //if(goal_status & LGF_ENABLED) + { + char txt[64]; + Level_goals.GoalGetName(j, txt, sizeof(txt)); + if (goal_status & LGF_COMPLETED) + { + sprintf(PLRText[curline]," %s:",txt); + sprintf(PLRVal[curline],"%s",TXT_COMPLETED); + } + else + { + sprintf(PLRText[curline]," %s:",txt); + sprintf(PLRVal[curline],"%s",TXT_NOTCOMPLETEDYET); + } + curline++; + } + } + } + + +} + +#define PLR_PAINT_TIME .8 //Seconds it takes to paint the text across the screen +#define PLR_START_VAL_TIME 2.0 +#define PLR_VAL_INTERVAL .20 + +int paint_progress = 0; + +#define PLR_VALUE_TOP 125 + +int line_progress[MAX_PLR_LINES];//This is where we currently are +char line_replace[MAX_PLR_LINES];//The character we replaced with a NULL. Draw this bright + + +void PaintPLRSinglePlayerText() +{ + + const int TEXT_HEIGHT = 14; + const int LEFT_COL = 50; + const int RIGHT_COL = 450; + char hi[3] = "\0\0"; + +#define PLR_TXT_HEADER TXT_PLRTITLE + int i; + + //Put everything back where it was... + for(i=0;icurpos) + { + line_progress[i] = curpos; + line_replace[i] = PLRText[i][curpos]; + PLRText[i][curpos] = NULL; + all_done_painting = false; + } + + } + + if(all_done_painting) + { + if(paint_in_sound_handle!=-1) + { + mprintf((0,"Stopping paint in sound\n")); + Sound_system.StopSoundImmediate(paint_in_sound_handle); + paint_in_sound_handle = -1; + } + } + + grtext_SetAlpha(255); + + grtext_SetFlags(GRTEXTFLAG_SHADOW); + + grtext_SetColor(UICOL_TEXT_NORMAL); + grtext_SetFont(BIG_BRIEFING_FONT); + int centerx = (Max_window_w/2) - (grtext_GetTextLineWidth(PLR_TXT_HEADER)/2); + if(centerx<0) + centerx = 0; + grtext_Printf(centerx,30,PLR_TXT_HEADER); + + grtext_SetColor(UICOL_WINDOW_TITLE); + centerx = (Max_window_w/2) - (grtext_GetTextLineWidth(PLRLevel)/2); + if(centerx<0) + centerx = 0; + grtext_Printf(centerx,55,PLRLevel); + + centerx = (Max_window_w/2) - (grtext_GetTextLineWidth(PLRHeader)/2); + if(centerx<0) + centerx = 0; + grtext_Printf(centerx,80,PLRHeader); + + + grtext_SetFont(BRIEFING_FONT); + int adjusted_line_no = 0; + //depending on where we are paint part or all of the text. + for(i=0;i0)) + { + grtext_SetAlpha(64); + hi[0] = line_replace[i]; + grtext_Printf(LEFT_COL+grtext_GetTextLineWidth(PLRText[i]),(TEXT_HEIGHT*i)+PLR_VALUE_TOP,hi); + } + grtext_SetAlpha(255); + float now = timer_GetTime(); + if((now-paint_start)>(PLR_START_VAL_TIME+(adjusted_line_no*PLR_VAL_INTERVAL))) + { + if(!val_printed[i]) + { + if( (chunk_sound_id!=-1) && (*PLRVal[i]) ) + Sound_system.Play2dSound(chunk_sound_id,(float)1.0); + } + val_printed[i] = true; + grtext_SetColor(UICOL_WINDOW_TITLE); + grtext_Printf(RIGHT_COL,(TEXT_HEIGHT*i)+PLR_VALUE_TOP,PLRVal[i]); + } + } + + grtext_SetAlpha(255); + grtext_SetColor(UICOL_WINDOW_TITLE); + centerx = (Max_window_w/2) - (grtext_GetTextLineWidth(TXT_HITAKEYORBUTTON)/2); + if(centerx<0) + centerx = 0; + grtext_Printf(centerx,Max_window_h-(TEXT_HEIGHT*1.2),TXT_HITAKEYORBUTTON); + +} + + + +/* + +const char *GetNormalizedRating(float mag) +{ + int rating; + +// determine rating. + rating = (int)(mag*5.0f); + + if (rating < 0) + rating = 0; + else if (rating >= LVLRES_STATS) + rating = LVLRES_STATS-1; + + return TXT(Stat_ratings[rating]); +} + +*/ \ No newline at end of file diff --git a/Descent3/screens.h b/Descent3/screens.h new file mode 100644 index 000000000..56b3e7f81 --- /dev/null +++ b/Descent3/screens.h @@ -0,0 +1,33 @@ +/* + * $Logfile: /DescentIII/main/screens.h $ + * $Revision: 3 $ + * $Date: 4/07/99 12:31p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/main/screens.h $ + * + * 3 4/07/99 12:31p Matt + * Added code for failed missions. + * + * 2 4/18/98 2:08a Samir + * update. + * + * 1 4/14/98 8:19p Samir + * initial revision. + * + * $NoKeywords: $ + */ + + +#ifndef SCREENS_H +#define SCREENS_H + + +// returns true if contine, returns false if player escaped out. +//Parameter: success - true if the level was completed ok, false if failed +bool PostLevelResults(bool success); + + +#endif \ No newline at end of file diff --git a/Descent3/ship.cpp b/Descent3/ship.cpp new file mode 100644 index 000000000..b08a32e3e --- /dev/null +++ b/Descent3/ship.cpp @@ -0,0 +1,319 @@ +/* +* $Logfile: /DescentIII/Main/ship.cpp $ +* $Revision: 19 $ +* $Date: 5/13/99 3:42p $ +* $Author: Ardussi $ +* +* $Log: /DescentIII/Main/ship.cpp $ + * + * 19 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 18 5/12/99 1:57p Jason + * fixed yet more buggy/ugly code + * + * 17 4/11/99 4:46p Matt + * Small fixes + * + * 16 4/11/99 3:04p Matt + * Reduced the max number of player ships from 30 to 5. + * Re-wrote the static ship name code to be more space efficient. + * Took default ship name (the one used to find the ship) out of string + * table. + * + * 15 2/17/99 10:32a Matt + * Init physics hit_die_dot for new items + * + * 14 10/08/98 10:28p Jason + * more demo changes for ship + * + * 13 10/02/98 1:47p Jason + * added lod player ships + * + * 12 8/07/98 2:43p Jeff + * flags gets cleared and initialized correctly + * + * 11 7/31/98 5:23p Jason + * added ship armor scalars + * + * 10 6/15/98 4:00p Jason + * replaced monochromatic polymodel lighting with rgb lighting + * + * 9 4/30/98 11:32a Chris + * ClearWB to WBClear + * + * 8 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 7 2/17/98 8:12p Matt + * Added looping & release sounds for player weapons + * + * 6 11/04/97 6:18p Chris + * Added support so that the player ship uses the robot's firing dialog. + * + * 5 10/21/97 11:57a Jason + * added ability to set dying model for player ship + * + * 4 10/20/97 4:46p Jason + * changes for explosions + * + * 3 9/09/97 7:02p Jason + * don't reload the models during the remap phase + * + * 2 8/06/97 12:40p Jason + * fixed some potentially serious memory problems + * + * 6 4/07/97 3:17p Chris + * Incremental improvement + * + * 5 4/07/97 3:13p Chris + * + * 4 4/02/97 5:21p Jason + * added the ability for pages to free the models that they point to + * + * 3 4/02/97 4:04p Jason + * made ships remap properly + * + * 2 3/31/97 4:34p Jason + * added player ship page +* +* 1 3/31/97 12:21p Jason +* +* $NoKeywords: $ +*/ + + + +#include "ship.h" +#include "pstypes.h" +#include "pserror.h" +#include "object.h" +#include "3d.h" +#include +#include +#include +#include +#include "polymodel.h" +#include "player.h" +#include "robotfire.h" + +#define DEFAULT_SHIP_SIZE 4.0 + +ship Ships[MAX_SHIPS]; +int Num_ships=0; + +//There are no static ships +char *Static_ship_names[1]; +#define NUM_STATIC_SHIPS 0 + +#define SHIP_PYRO_ID 0 +#define SHIP_PHOENIX_ID 1 +#define SHIP_MAGNUM_ID 2 + +char *AllowedShips[MAX_SHIPS] = { "Pyro-GL", "Phoenix", "Magnum-AHT" }; + + +// Sets all ships to unused +void InitShips () +{ + for (int i=0;i0); + + Ships[n].used=0; + Ships[n].name[0]=0; + Num_ships--; +} + +// Gets next ship from n that has actually been alloced +int GetNextShip (int n) +{ + int i; + + if (Num_ships==0) + return -1; + + if ((n < 0) || (n >= MAX_SHIPS)) + n = -1; + + for (i=n+1;i= MAX_SHIPS)) + n = MAX_SHIPS; + + for (i=n-1;i>=0;i--) + { + if (Ships[i].used) + return i; + } + for (i=MAX_SHIPS-1;i>n;i--) + { + if (Ships[i].used) + return i; + } + + // this is the only one + return n; + +} +// Searches thru all ships for a specific name, returns -1 if not found +// or index of ship with name +int FindShipName (char *name) +{ + int i; + + ASSERT (name!=NULL); + + for (i=0;i0); + + return (Ships[handle].model_handle); +} + + +// This is a very confusing function. It takes all the ships that we have loaded +// and remaps then into their proper places (if they are static). +void RemapShips () +{ + int i; + + //Loop through the static ships and move them to the correct slots + for (i=0;i Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *size = Objects[n].size; + return true; +} + +bool DSGetObjMass(int n, float *mass) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *mass = Objects[n].mtype.phys_info.mass; + return true; +} + +bool DSGetObjShields(int n, float *shields) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *shields = Objects[n].shields; + return true; +} + +bool DSGetObjOrient(int n, matrix *m) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *m = Objects[n].orient; + return true; +} + +bool DSGetObjVelocity(int n, vector *vel) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *vel = Objects[n].mtype.phys_info.velocity; + return true; +} + +bool DSGetObjPos(int n, vector *pos) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *pos = Objects[n].pos; + return true; +} + +bool DSGetObjRoomNum(int n, int *roomnum) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *roomnum = Objects[n].roomnum; + return true; +} + +bool DSGetObjRotVelocity(int n, vector *rotvel) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *rotvel = Objects[n].mtype.phys_info.rotvel; + return true; +} + +bool DSGetObjType(int n, int *type) +{ + if(n < 0 || n > Highest_object_index) return false; + + *type = Objects[n].type; + return true; +} + +bool DSGetObjId(int n, int *id) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *id = Objects[n].id; + return true; +} + +bool DSGetNextObjIndex(int n, int *next) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *next = Objects[n].next; + return true; +} + +bool DSGetPrevObjIndex(int n, int *prev) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *prev = Objects[n].next; + return true; +} + +bool DSSetControllerInfo(int n, game_controls *controls) +{ + object *objp; + + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE || Objects[n].control_type != CT_SOAR) return false; + + objp = &Objects[n]; + + //Update object's physics + if (objp->movement_type == MT_PHYSICS) { + objp->mtype.phys_info.rotthrust.x = controls->pitch_thrust * objp->mtype.phys_info.full_rotthrust; + objp->mtype.phys_info.rotthrust.z = controls->bank_thrust * objp->mtype.phys_info.full_rotthrust; + objp->mtype.phys_info.rotthrust.y = controls->heading_thrust * objp->mtype.phys_info.full_rotthrust; + objp->mtype.phys_info.thrust = (objp->orient.fvec * controls->forward_thrust * objp->mtype.phys_info.full_thrust) + + (objp->orient.uvec * controls->vertical_thrust * objp->mtype.phys_info.full_thrust) + + (objp->orient.rvec * controls->sideways_thrust * objp->mtype.phys_info.full_thrust); + } + + //Process weapon firing + FireWeaponFromPlayer(objp,PW_PRIMARY,controls->fire_primary_down_count,controls->fire_primary_down_state,controls->fire_primary_down_time); + FireWeaponFromPlayer(objp,PW_SECONDARY,controls->fire_secondary_down_count,controls->fire_secondary_down_state,controls->fire_secondary_down_time); + + //Deal with the flare + for (int i=0;ifire_flare_down_count;i++) + FireFlareFromPlayer(objp); + + return true; +} + +bool DSGetFirstRoomObject(int n, int *first) +{ + if(!ROOMNUM_OUTSIDE(n)) + { + if(n < 0 || n > Highest_room_index || Rooms[n].used == 0) return false; + + *first = Rooms[n].objects; + } + else + { + int cell; + + cell = CELLNUM(n); + if(cell < 0 && cell >= TERRAIN_WIDTH * TERRAIN_DEPTH) return false; + + *first = Terrain_seg[n].objects; + } + + return true; +} + +bool DSPauseGame(void) +{ + PauseGame(); + + return true; +} + +bool DSResumeGame(void) +{ + ResumeGame(); + + return true; +} + +bool DSGetObjAABB(int n, vector *min_xyz, vector *max_xyz) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *min_xyz = Objects[n].min_xyz; + *max_xyz = Objects[n].max_xyz; + + return true; +} + +// CHRISHACK -- THIS WEAPON STUFF SUCKS. The current weapon slots are from 0 to 9. +// slots 0 - 4 are primaries (mapped twice for a total of 10 primaries (weapons 0 - 9)) +// Note: Weapon 0 and 5 use slot 0, weapon 1 and 6 use slot 1, etc. +// slots 5 - 9 are secondaries (mapped twice for a total of 10 secondaries (weapons 10 - 19)) +// if you currently are using the lower mapping, and you call the function with that slot +// number, you use the higher mapping. If available at that slot, it selects that one +bool DSSelectCurrentWeapon(int n) +{ + if(n < 0 || n > 9) return false; + + SelectWeapon(n); + return true; +} + +// These are from 0 to 9 +bool DSGetCurrentPrimary(int *n) +{ + *n = Players[Player_num].weapon[PW_PRIMARY].index; + return true; +} + +// These are from 10 to 19 +bool DSGetCurrentSecondary(int *n) +{ + *n = Players[Player_num].weapon[PW_SECONDARY].index; + return true; +} + +// Bit field using the first 20 bits. If set, that weapon is available +bool DSGetAvailableWeapons(int *flags) +{ + *flags = Players[Player_num].weapon_flags; + + return true; +} + +bool DSGetNumRoomPortals(int n, int *num_portals) +{ + if(n < 0 || n > Highest_room_index || Rooms[n].used == 0) return false; + + *num_portals = Rooms[n].num_portals; + + return true; +} + +bool DSGetRoomPortalConnectRoom(int n, int p, int *connect_room) +{ + if(n < 0 || n > Highest_room_index || Rooms[n].used == 0) return false; + if(p < 0 || p >= Rooms[n].num_portals) return false; + + *connect_room = Rooms[n].portals[p].croom; + + return true; +} + +bool DSIsObjDestroyable(int n, bool *f_is_destroyable) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type == OBJ_NONE) return false; + + *f_is_destroyable = (Objects[n].flags & OF_DESTROYABLE) != 0; + + return true; +} + +bool DSIsDoorOpenable(int n, int *is_openable) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type != OBJ_DOOR) return false; + *is_openable = !DoorwayLocked(Objects[n].handle); + return true; +} + +// doorway state +//#define DOORWAY_CLOSED 0 // door is closed +//#define DOORWAY_OPENING 1 // door is opening +//#define DOORWAY_WAITING 2 // door is waiting to be closed +//#define DOORWAY_CLOSING 3 // door is closing +//#define DOORWAY_OPEN 4 // door is open, that's it. + +bool DSGetDoorStatus(int n, int *status) +{ + if(n < 0 || n > Highest_object_index || Objects[n].type != OBJ_DOOR) return false; + *status = DoorwayState(Objects[n].handle); + return true; +} + +void DSSoarInit() +{ + void *dsfunc[27]; + + dsfunc[0] = (void *) DSGetHighestObjIndex; + dsfunc[1] = (void *) DSGetObjSize; + dsfunc[2] = (void *) DSGetObjMass; + dsfunc[3] = (void *) DSGetObjShields; + dsfunc[4] = (void *) DSGetObjPos; + dsfunc[5] = (void *) DSGetObjOrient; + dsfunc[6] = (void *) DSGetObjVelocity; + dsfunc[7] = (void *) DSGetObjRotVelocity; + dsfunc[8] = (void *) DSGetObjRoomNum; + dsfunc[9] = (void *) DSGetObjType; + dsfunc[10] = (void *) DSGetObjId; + dsfunc[11] = (void *) DSGetNextObjIndex; + dsfunc[12] = (void *) DSGetPrevObjIndex; + dsfunc[13] = (void *) DSSetControllerInfo; + dsfunc[14] = (void *) DSGetFirstRoomObject; + dsfunc[15] = (void *) DSPauseGame; + dsfunc[16] = (void *) DSResumeGame; + dsfunc[17] = (void *) DSGetObjAABB; + dsfunc[18] = (void *) DSSelectCurrentWeapon; + dsfunc[19] = (void *) DSGetCurrentPrimary; + dsfunc[20] = (void *) DSGetCurrentSecondary; + dsfunc[21] = (void *) DSGetAvailableWeapons; + dsfunc[22] = (void *) DSGetNumRoomPortals; + dsfunc[23] = (void *) DSGetRoomPortalConnectRoom; + dsfunc[24] = (void *) DSIsObjDestroyable; + dsfunc[25] = (void *) DSIsDoorOpenable; + dsfunc[26] = (void *) DSGetDoorStatus; + + SoarInit((void *)Terrain_seg, sizeof(Terrain_seg[0]), (void *)Rooms, sizeof(Rooms[0]), (void *)Objects, sizeof(Objects[0]), (void *)dsfunc); + + Soar_active = true; +} + +void DSSoarEnd() +{ + SoarEnd(); + Soar_active = false; +} + +#endif //ifdef SOAR_ENABLED diff --git a/Descent3/soar_helpers.h b/Descent3/soar_helpers.h new file mode 100644 index 000000000..e7d63f387 --- /dev/null +++ b/Descent3/soar_helpers.h @@ -0,0 +1,74 @@ +/* + * $Logfile: /DescentIII/main/soar_helpers.h $ + * $Revision: 4 $ + * $Date: 4/16/99 12:33a $ + * $Author: Matt $ + * + * Header for soar_helpers.cpp + * + * $Log: /DescentIII/main/soar_helpers.h $ + * + * 4 4/16/99 12:33a Matt + * Disable Soar on non-Windows systems. + * + */ + + +#ifndef SOAR_HELPERS_H_ +#define SOAR_HELPERS_H_ + +#include "soar.h" + +#ifdef SOAR_ENABLED + +#include "object.h" +#include "vecmat.h" +#include "controls.h" +#include "weapon.h" +#include "room.h" +#include "terrain.h" +#include "player.h" + +// Functions passed to Soar in this order + +bool DSGetHighestObjIndex(int &n); +bool DSGetObjSize(int n, float *size); +bool DSGetObjMass(int n, float *mass); +bool DSGetObjShields(int n, float *shields); +bool DSGetObjPos(int n, vector *pos); +bool DSGetObjOrient(int n, matrix *m); +bool DSGetObjVelocity(int n, vector *vel); +bool DSGetObjRotVelocity(int n, vector *rotvel); +bool DSGetObjRoomNum(int n, int *roomnum); +bool DSGetObjType(int n, int *type); +bool DSGetObjId(int n, int *id); +bool DSGetNextObjIndex(int n, int *next); +bool DSGetPrevObjIndex(int n, int *prev); +bool DSSetControllerInfo(int n, game_controls *controls); +bool DSGetFirstRoomObject(int n, int *first); +bool DSPauseGame(void); +bool DSResumeGame(void); +bool DSGetObjAABB(int n, vector *min_xyz, vector *max_xyz); +bool DSSelectCurrentWeapon(int n); +bool DSGetCurrentPrimary(int *n); +bool DSGetCurrentSecondary(int *n); +bool DSGetAvailableWeapons(int *flags); +bool DSGetNumRoomPortals(int n, int *num_portals); +bool DSGetRoomPortalConnectRoom(int n, int p, int *connect_room); +bool DSIsObjDestroyable(int n, bool *f_is_destroyable); +bool DSIsDoorOpenable(int n, int *is_openable); +bool DSGetDoorStatus(int n, int *status); + +// Not externalized to Soar +void DSSoarInit(); +void DSSoarEnd(); + +#else //ifdef SOAR_ENABLED + +#define DSSoarInit() do {} while (0) +#define DSSoarEnd() do {} while (0) + +#endif //ifdef SOAR_ENABLED + + +#endif \ No newline at end of file diff --git a/Descent3/sounds.h b/Descent3/sounds.h new file mode 100644 index 000000000..76b11ecb3 --- /dev/null +++ b/Descent3/sounds.h @@ -0,0 +1,137 @@ +/* + * $Logfile: /DescentIII/main/sounds.h $ + * $Revision: 22 $ + * $Date: 5/08/99 3:30p $ + * $Author: Matt $ + * + * Constants for sounds + * + * $Log: /DescentIII/main/sounds.h $ + * + * 22 5/08/99 3:30p Matt + * Added weapon-hit-water sound. + * + * 21 4/30/99 3:28p Matt + * Weapons now make a special sound when colliding with lava & volatile + * surfaces, instead of the standard hit-wall sound. + * + * 20 4/29/99 3:50p Matt + * Added hiss sound for volatile surface damage + * + * 19 4/29/99 1:10p Jeff + * added sounds for inventory and countermeasure switch + * + * 18 4/11/99 5:03p Matt + * Changed static sound remapping code to use simpler & more + * space-efficient scheme. Also deleted an unused entry in the static + * sound list. + * + * 17 3/04/99 10:57a Matt + * Added goal message notification sound + * + * 16 2/22/99 10:24p Matt + * Added a static sound for debris explosion + * + * 15 2/18/99 2:49p Matt + * Added goal complete sound + * + * 14 11/23/98 1:50p Jason + * added thruster sounds + * + * 13 10/21/98 4:53p Jeff + * added some sounds to sounds array + * + * 12 10/20/98 2:09p Jeff + * added 2 more sounds to static sounds + * + * 11 10/20/98 12:49p Matt + * Removed obsolete sounds from static sound list. + * + * 10 10/19/98 7:18p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 9 10/14/98 1:02p Jason + * fixed FindSoundName issues + * + * 8 9/21/98 8:19p Chris + * Improved volatile and forcefield hits + * + * 7 9/18/98 1:27p Matt + * Added afterburner release sound + * + * 6 5/22/98 11:59a Chris + * Fixed improper uses of FindSoundName and made the proper static sounds + * + * 5 5/03/98 10:22p Matt + * Added breaking glass sound + * + * 4 2/16/98 4:55p Chris + * Added default sounds for explosions + * + * 3 12/22/97 6:19p Chris + * Moved weapon battery firing sound off the projectile (weapon) and into + * the weapon battery. + * + * 2 7/15/97 7:29p Chris + * Added a sound for helicopter blades. + * + * 1 5/13/97 10:32p Matt + * Symbols for sounds referenced in code + * + * $NoKeywords: $ + */ + +//THESE SOUNDS MUST MATCH THE Static_sound_names[] array in SoundLoad.cpp + +#define SOUND_NONE_INDEX 0 //the not-defined sound +#define SOUND_REFUELING 1 +#define SOUND_ROBOT_EXPLODE_1 2 +#define SOUND_ROBOT_EXPLODE_2 3 +#define SOUND_BUILDING_EXPLODE 4 +#define SOUND_BREAKING_GLASS 5 +#define SOUND_MELEE_HIT_0 6 +#define SOUND_MELEE_HIT_1 7 +#define SOUND_AFTERBURNER 8 +#define SOUND_ENERGY_DRAIN 9 +#define SOUND_MISSLE_LOCK 10 +#define SOUND_POWERUP_PICKUP 11 +#define SOUND_DO_NOT_HAVE_IT 12 +#define SOUND_CHANGE_PRIMARY 13 +#define SOUND_CHANGE_SECONDARY 14 +#define SOUND_PLAYER_HIT_WALL 15 +#define SOUND_AFTERBURNER_TAIL 16 +#define SOUND_FORCEFIELD_BOUNCE 17 +#define SOUND_HEADLIGHT 18 +#define SOUND_CHEATER 19 +#define SOUND_ENERGY_CONVERTER 20 +#define SOUND_COCKPIT 21 +#define SOUND_METALLIC_DOOR_HIT 22 +#define SOUND_WALL_FADE 23 +#define SOUND_RAINDROP 24 +#define SOUND_LIGHTNING 25 +#define SOUND_HIT_BY_ENERGY_WEAPON 26 +#define SOUND_HIT_BY_MATTER_WEAPON 27 +#define SOUND_HIT_BY_CONCUSSIVE_FORCE 28 +#define SOUND_PLAYER_BURNING 29 +#define SOUND_MENU_SOUND_CLICK 30 +#define SOUND_HOSTAGE_PICKUP 31 +#define SOUND_BRIEF_STARTUP 32 +#define SOUND_BRIEF_STATIC 33 +#define SOUND_BRIEF_MONITOROFF 34 +#define SOUND_BRIEF_RUNNING 35 +#define SOUND_BRIEF_BULLB 36 +#define SOUND_BRIEF_TYPE 37 +#define SOUND_SHIP_IDLE 38 +#define SOUND_SHIP_FORWARD_THRUST 39 +#define SOUND_SHIP_FORWARD_RELEASE 40 +#define SOUND_GOAL_COMPLETE 41 +#define SOUND_DEBRIS_EXPLODE 42 +#define SOUND_GAME_MESSAGE 43 +#define SOUND_CHANGE_COUNTERMEASURE 44 +#define SOUND_CHANGE_INVENTORY 45 +#define SOUND_VOLATILE_HISS 46 +#define SOUND_WEAPON_HIT_LAVA 47 +#define SOUND_WEAPON_HIT_WATER 48 +#define NUM_STATIC_SOUNDS 49 //update this every time you add a new sound + diff --git a/Descent3/special_face.cpp b/Descent3/special_face.cpp new file mode 100644 index 000000000..81fc1cc85 --- /dev/null +++ b/Descent3/special_face.cpp @@ -0,0 +1,92 @@ +#include "pstypes.h" + +#include "special_face.h" +#include "mono.h" +#include +#include +#include "mem.h" + +int Num_of_special_faces=0; +special_face SpecialFaces[MAX_SPECIAL_FACES]; + +static ushort Free_special_face_list[MAX_SPECIAL_FACES]; + +// Sets all the special faces to unused +void InitSpecialFaces() +{ + int i; + + for (i=0;i=MAX_SPECIAL_FACES) + { + Int3(); // Get Jason, ran out of special faces! + return BAD_SPECIAL_FACE_INDEX; + } + + n = Free_special_face_list[Num_of_special_faces++]; + ASSERT (SpecialFaces[n].used==0); + + ASSERT (n>=0 && n=0 && handle<=MAX_SPECIAL_FACES); + + if (SpecialFaces[handle].used<1) + return; + + SpecialFaces[handle].used--; + + if (SpecialFaces[handle].used==0) + { + Free_special_face_list[--Num_of_special_faces] = handle; + if (SpecialFaces[handle].spec_instance) + { + mem_free (SpecialFaces[handle].spec_instance); + SpecialFaces[handle].spec_instance=NULL; + } + + + if (SpecialFaces[handle].vertnorms) + { + mem_free (SpecialFaces[handle].vertnorms); + SpecialFaces[handle].vertnorms=NULL; + } + } +} + + diff --git a/Descent3/special_face.h b/Descent3/special_face.h new file mode 100644 index 000000000..3fa2198c7 --- /dev/null +++ b/Descent3/special_face.h @@ -0,0 +1,54 @@ +#ifndef SPECIAL_FACE_H +#define SPECIAL_FACE_H + +#include "pstypes.h" +#include "pserror.h" +#include "vecmat.h" + +#define BAD_SPECIAL_FACE_INDEX -1 +#ifdef MACINTOSH +#define MAX_SPECIAL_FACES 5000 +#else +#define MAX_SPECIAL_FACES 13000 //made large enough for Josh's Mercenary level 3 +#endif + +// What this special face is used for: +#define SFT_SPECULAR 1 + +typedef struct +{ + vector bright_center; + ushort bright_color; +} specular_instance; + + +#define SFF_SPEC_OBJECT 1 +#define SFF_SPEC_SMOOTH 2 + +typedef struct +{ + ubyte type; // See types (above) + ubyte num; // Number of instances + ubyte used; + ubyte flags; + + specular_instance *spec_instance; + + vector *vertnorms; + +} special_face; + +extern special_face SpecialFaces[]; +extern int Num_of_special_faces; + +// Sets all the special faces to unused +void InitSpecialFaces(); + +// Returns an index into the special faces array +int AllocSpecialFace (int type,int num,bool vertnorms=false,int num_vertnorms=0); + +// Given a handle, frees the special face +void FreeSpecialFace (int handle); + + +#endif diff --git a/Descent3/spew.cpp b/Descent3/spew.cpp new file mode 100644 index 000000000..1b55fcdb5 --- /dev/null +++ b/Descent3/spew.cpp @@ -0,0 +1,518 @@ +/* +* $Logfile: /DescentIII/main/spew.cpp $ +* $Revision: 28 $ +* $Date: 10/31/99 5:41p $ +* $Author: Chris $ +* +* Source file for spew system +* +* $Log: /DescentIII/main/spew.cpp $ + * + * 28 10/31/99 5:41p Chris + * Fixed a problem with spewers on attached objects + * + * 27 5/02/99 2:52a Jeff + * fixed spewers for turrets/rotaters + * + * 26 4/30/99 8:32p Jeff + * more info on mprintf + * + * 25 4/29/99 7:50p Jason + * fixed spew errors + * + * 24 4/29/99 5:09p Jeff + * fixed spewers on animated objects + * + * 23 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 22 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 21 4/19/99 3:17p Jason + * made real vis effects work + * + * 20 4/15/99 1:42a Jeff + * changes for linux compile + * + * 19 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 18 1/13/99 2:32p Jeff + * added error checking on create...returns -1 on error + * + * 17 10/18/98 9:15p Jeff + * ok....it's sped up now + * + * 16 10/18/98 9:04p Jeff + * sped up spew by saving values? + * + * 15 8/19/98 5:36p Samir + * moved some stuff from source to header file. + * + * 14 7/27/98 6:26p Jeff + * added an mprintf for my sanity + * + * 13 2/16/98 2:33p Jeff + * Now passing real flag when creating a spew + * + * 12 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 11 2/02/98 5:05p Luke + * + * 10 2/02/98 4:38p Jeff + * fixed handle system (stupid bug) + * + * 9 2/02/98 3:10p Jeff + * new handle system, makes more reliable + * + * 8 1/27/98 10:49a Jeff + * commented out mprintfs + * + * 7 1/26/98 4:20p Jeff + * added some debugging mprintfs + * + * 6 1/26/98 11:01a Jeff + * changed spew struct to use object handles + * + * 5 1/23/98 7:00p Jeff + * checked object before using it bug fixed + * + * 4 1/23/98 5:18p Jeff + * Added in new timer and 3/5 of the random flags + * + * 3 1/23/98 3:03p Jeff + * Added mass into spew structure + * + * 2 1/23/98 12:14p Jeff + * Took out an mprintf + * + * 1 1/23/98 12:07p Jeff + * + * 2 1/23/98 11:50a Jeff + * Creation of Spew system +* +* $NoKeywords: $ +*/ + +// ancillary includes +#include "game.h" +#include "vecmat.h" +#include "ddio.h" +#include "pserror.h" +#include "object.h" +#include "damage.h" +#include "viseffect.h" +#include "fireball.h" +#include "spew.h" +#include "weapon.h" +#include "polymodel.h" + + +// ANSI C includes +#include +#include +#include + +#include "psrand.h" + +#define MAX_SPEWS_PER_FRAME 5 //maximum number of spews 1 can emit per frame + +inline int FORM_HANDLE(int counter, int slot) +{ + return (((counter&0xFF)<<16) + (slot&0xFF)); +} + +inline int GET_SLOT(int handle) +{ + return (handle&0xFF); +} + +spewinfo SpewEffects[MAX_SPEW_EFFECTS]; +int spew_count; + +bool SpewObjectNeedsEveryFrameUpdate(object *obj,int gunpoint); + + +//Initializes the Spew system +void SpewInit() +{ + mprintf((0,"Initializing Spew System\n")); + int count; + spew_count = 0; + + for(count=0;count Pointer to spewinfo structure (that is filled out)...may be deleted after function call +//returns a handle to the spew effect +int SpewCreate(spewinfo *spew) +{ + int count; + spewinfo *veffect; + + ASSERT(spew); + + if(spew->drag<0) spew->drag = 0; + if(spew->effect_type<0 || spew->effect_type>=NUM_FIREBALLS) return -1; + if(spew->lifetime<0) spew->lifetime = 0; + if(spew->longevity<0) spew->longevity = 0; + if(spew->mass<0) spew->mass = 0; + if(spew->size<1) spew->size = 1; + if(spew->speed<0) spew->speed = 0; + if(spew->time_int<=0) spew->time_int = 0.001f; + if(spew->use_gunpoint){ + //make sure all the object data is valid + object *obj = ObjGet(spew->gp.obj_handle); + if(!obj) + return -1; + + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + if(spew->gp.gunpoint<0 || spew->gp.gunpoint >= pm->n_guns) + return -1; + } + + + for(count = 0;countinuse = true; + veffect->random = spew->random; + veffect->use_gunpoint = spew->use_gunpoint; + veffect->real_obj = spew->real_obj; + veffect->flags = 0; + + if(veffect->use_gunpoint) + { + veffect->gp.obj_handle = spew->gp.obj_handle; + + //WHAT IS THIS? it used to be "if(veffect->gp.obj_handle==NULL)" + if(veffect->gp.obj_handle==OBJECT_HANDLE_NONE) + { + SpewClearEvent(count,false); + return -1; + } + veffect->gp.gunpoint = spew->gp.gunpoint; + veffect->flags |= SF_FORCEUPDATE; //force an update so it has some values + + + object *obj = ObjGet(spew->gp.obj_handle); + ASSERT(obj); + + if(obj) + { + if(SpewObjectNeedsEveryFrameUpdate(obj,spew->gp.gunpoint)) + { + veffect->flags |= SF_UPDATEEVERYFRAME; + } + } + } + else + { + veffect->pt.origin = spew->pt.origin; + veffect->pt.normal = spew->pt.normal; + veffect->pt.room_num = spew->pt.room_num; + } + + veffect->effect_type = spew->effect_type; + veffect->phys_info = spew->phys_info; + veffect->drag = spew->drag; + veffect->mass = spew->mass; + veffect->time_until_next_blob=veffect->time_int = spew->time_int; + veffect->longevity = spew->longevity; + veffect->lifetime = spew->lifetime; + veffect->size = spew->size; + veffect->speed = spew->speed; + + veffect->handle = FORM_HANDLE(spew_count,count); + + + veffect->start_time = Gametime; + + //mprintf((0,"Creating Spew Effect (%d)\n",veffect->handle)); + + return veffect->handle; //return the handle + } + + if(count == MAX_SPEW_EFFECTS-1) + return -1; //we didn't find any open handles + } + return -1; +} + +//Does a step in a spew system +void SpewEmitAll(void) +{ + spewinfo *spew; + int count; + + float size,speed,lifetime; + + for(count = 0;count < MAX_SPEW_EFFECTS;count++) + { + + if(SpewEffects[count].inuse) + { + spew = &SpewEffects[count]; + + spew->time_until_next_blob -= Frametime; //account for the current frame + + //no randomness if it's a real object! + if(spew->real_obj) + spew->random = 0; + + if (spew->time_until_next_blob >= 0 ){ + //ok, since we won't be in the following while loop at all + //this frame for the spew, we need to force an update on + //it's gunpoint (IF it's a gunpoint) that way we can be sure + //it has the correct position, in case we miss the obj->flags&OF_MOVED_THIS_FRAME + if(spew->use_gunpoint){ + object *obj = ObjGet(spew->gp.obj_handle); + if(obj){ + + if((obj->flags&OF_MOVED_THIS_FRAME) || ((obj->flags & OF_ATTACHED) && ObjGet(obj->attach_ultimate_handle) && ObjGet(obj->attach_ultimate_handle)->movement_type == MT_PHYSICS && (ObjGet(obj->attach_ultimate_handle)->flags & OF_MOVED_THIS_FRAME))) + spew->flags |= SF_FORCEUPDATE; + + //check to see if it is an animated polymodel, which means update everyframe + if(obj->flags&OF_POLYGON_OBJECT) + { + if(obj->rtype.pobj_info.anim_end_frame != obj->rtype.pobj_info.anim_start_frame) + { + spew->flags |= SF_FORCEUPDATE; + } + } + }else{ + //well, this spewer is dead, kill it + SpewClearEvent(spew->handle,true); + } + } + } + + if(spew->flags&SF_UPDATEEVERYFRAME) + spew->flags |= SF_FORCEUPDATE; + + //turn this off, we will turn this on the first time we calc + spew->flags &= ~SF_UPDATEDFORFRAME; + + //if time_until_next_blob is negative, it's time to spew + int num_spewed = MAX_SPEWS_PER_FRAME; + + while (spew->time_until_next_blob < 0.0 && (num_spewed--) ) { + + //do random computations + if((spew->random&SPEW_RAND_SIZE)!=0){ + size = spew->size + (((RAND_MAX>>1) - ps_rand())/(float)RAND_MAX)*spew->size*0.5f; + } + else + size=spew->size; + + if((spew->random&SPEW_RAND_SPEED)!=0){ + speed = spew->speed + (((RAND_MAX>>1) - ps_rand())/(float)RAND_MAX)*spew->speed*0.5f; + } + else + speed=spew->speed; + + if((spew->random&SPEW_RAND_LIFETIME)!=0){ + lifetime = spew->lifetime + (((RAND_MAX>>1) - ps_rand())/(float)RAND_MAX)*spew->lifetime*0.5f; + } + else + lifetime=spew->lifetime; + + //move this code to within the following if statement and else + if((spew->random&SPEW_RAND_WIGGLE)!=0){ + + } + else{ + + } + if((spew->random&SPEW_RAND_SLIDE)!=0){ + + } + else{ + + } + + + if(spew->use_gunpoint) + { + vector vel_vector; + object *obj; + + obj = ObjGet(spew->gp.obj_handle); + if(obj) + { + if(!(spew->flags&SF_UPDATEDFORFRAME)) + { + + if((obj->flags&OF_MOVED_THIS_FRAME) || ((obj->flags & OF_ATTACHED) && ObjGet(obj->attach_ultimate_handle) && ObjGet(obj->attach_ultimate_handle)->movement_type == MT_PHYSICS && (ObjGet(obj->attach_ultimate_handle)->flags & OF_MOVED_THIS_FRAME))) + { + //since the object moved this frame, force an update + spew->flags |= SF_FORCEUPDATE; + } + + //check to see if it is an animated polymodel, which means update everyframe + if(obj->flags&OF_POLYGON_OBJECT) + { + if(obj->rtype.pobj_info.anim_end_frame != obj->rtype.pobj_info.anim_start_frame) + { + spew->flags |= SF_FORCEUPDATE; + } + } + + if(spew->flags&SF_UPDATEEVERYFRAME) + spew->flags |= SF_FORCEUPDATE; + } + + //see if we need to recalc info + if((spew->flags&SF_FORCEUPDATE) && (!(spew->flags&SF_UPDATEDFORFRAME)) ){ + //recalculate + // Returns the position and the normal of a gun point + WeaponCalcGun(&spew->gun_point, &spew->gp_normal, obj, spew->gp.gunpoint); + spew->flags &= ~SF_FORCEUPDATE; + spew->flags |= SF_UPDATEDFORFRAME; + } + + //calc velocity vector + vel_vector = spew->gp_normal * speed; + + //Create the viseffect + VisEffectCreateControlled(VIS_FIREBALL,obj,(int)spew->effect_type,obj->roomnum,&spew->gun_point, + lifetime,&vel_vector,spew->phys_info,size,spew->drag,spew->mass,spew->real_obj); + } + else + { + //object no longer exists + SpewClearEvent(spew->handle,true); + } + } + else + { + vector vel_vector; + + //mprintf((0,"Emitting Point based Spew %d\n",count)); + + //calc velocity vector + vel_vector = spew->pt.normal * speed; + + //Create the viseffect + VisEffectCreateControlled(VIS_FIREBALL,NULL,(int)spew->effect_type,spew->pt.room_num,&spew->pt.origin, + lifetime,&vel_vector,spew->phys_info,size,spew->drag,spew->mass,spew->real_obj); + + }//end else + + spew->time_until_next_blob += spew->time_int; + }//end while + + if(num_spewed==0) + { + mprintf((0,"Max spews per frame hit! Handle=%d Interval=%f Lifetime=%f Longevity=%f\n",spew->handle,spew->time_int,spew->lifetime,spew->longevity)); + spew->time_until_next_blob = spew->time_int; + } + + //see if this effect is done + if(spew->longevity>0.0f) + if(Gametime - spew->start_time >= spew->longevity) + { + SpewClearEvent(spew->handle,true); + } + + }//end if + }//end for +} + + +//Clears a Spew Event given a handle to it +void SpewClearEvent(int handle,bool force) +{ + spewinfo *vis; + + int slot = GET_SLOT(handle); + + if((slot<0) || (slot>MAX_SPEW_EFFECTS)) + return; + + //mprintf((0,"Clearing spew event %d - %d....",handle,slot)); + + vis = &SpewEffects[slot]; + + if((!force) && (handle!=vis->handle)) + return; + + //mprintf((0,"handle OK\n")); + + vis->inuse = vis->random = vis->use_gunpoint = vis->real_obj = false; + vis->pt.origin.x = vis->pt.origin.y = vis->pt.origin.z = 0.0f; + vis->pt.room_num = 0; + vis->pt.normal.x = vis->pt.normal.y = vis->pt.normal.z = 0.0f; + vis->effect_type = MED_SMOKE_INDEX; + vis->phys_info = PF_FIXED_VELOCITY; + vis->drag = vis->mass = vis->time_int = vis->longevity = vis->lifetime = vis->size = vis->speed = 0.0f; + vis->time_until_next_blob = vis->start_time = 0.0f; + vis->handle = 0; + vis->flags = 0; +} + + +bool SpewObjectNeedsEveryFrameUpdate(object *obj,int gun_num) +{ + poly_model *pm; + vector pnt; + vector normal; + int mn; //submodel number + float normalized_time[MAX_SUBOBJECTS]; + + if (!(obj->flags & OF_POLYGON_OBJECT)) + { + return false; + } + + pm = &Poly_models[obj->rtype.pobj_info.model_num]; + + if(pm->n_guns == 0) + { + return false; + } + + SetNormalizedTimeObj(obj, normalized_time); + SetModelAnglesAndPos (pm,normalized_time); + + if (gun_num < 0 || gun_num >= pm->n_guns) + { + return false; + } + + pnt = pm->gun_slots[gun_num].pnt; + normal = pm->gun_slots[gun_num].norm; + mn = pm->gun_slots[gun_num].parent; + + // Instance up the tree for this gun + while (mn != -1) + { + if(pm->submodel[mn].flags&(SOF_ROTATE|SOF_TURRET)) + { + return true; + } + + mn = pm->submodel[mn].parent; + } + + return false; +} \ No newline at end of file diff --git a/Descent3/spew.h b/Descent3/spew.h new file mode 100644 index 000000000..6d19e83be --- /dev/null +++ b/Descent3/spew.h @@ -0,0 +1,138 @@ +/* +* $Logfile: /DescentIII/main/spew.h $ +* $Revision: 14 $ +* $Date: 5/02/99 2:52a $ +* $Author: Jeff $ +* +* header file for spew system +* +* $Log: /DescentIII/main/spew.h $ + * + * 14 5/02/99 2:52a Jeff + * fixed spewers for turrets/rotaters + * + * 13 4/29/99 7:50p Jason + * fixed spew errors + * + * 12 10/18/98 9:04p Jeff + * sped up spew by saving values? + * + * 11 8/19/98 5:36p Samir + * moved some stuff from source to header file. + * + * 10 8/19/98 1:44p Samir + * added array of spew extern + * + * 9 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 8 2/02/98 5:40p Luke + * + * 7 2/02/98 3:10p Jeff + * new handle system, makes more reliable + * + * 6 1/28/98 6:29p Jeff + * Added the "RISCy" layer between predef statements and the game + * + * 5 1/26/98 11:01a Jeff + * changed spew struct to use object handles + * + * 4 1/23/98 7:00p Jeff + * checked object before using it bug fixed + * + * 3 1/23/98 5:18p Jeff + * Added in new timer and 3/5 of the random flags + * + * 2 1/23/98 3:03p Jeff + * Added mass into spew structure + * + * 1 1/23/98 12:07p Jeff + * + * 2 1/23/98 11:50a Jeff + * Creation of Spew system +* +* $NoKeywords: $ +*/ + +#ifndef __SPEW_H_ +#define __SPEW_H_ + +#define SF_FORCEUPDATE 0x01 //this spew needs to do an update no matter what next time it spews (gunpoint) +#define SF_UPDATEDFORFRAME 0x02 //thie spew has already updated it's position for this frame, no need to do it again +#define SF_UPDATEEVERYFRAME 0x04 + +typedef struct gunpoint_info +{ + int obj_handle; //handle to object (needed if it's gunpoint based) + int gunpoint; //gunpoint number (needed if it's gunpoint based) +} guninfo; + +typedef struct point_info +{ + vector origin; //origin of the viseffect (needed if it's NOT gunpoint based) + vector normal; //normal of the point + int room_num; //room number of point of origin +}pointinfo; + +typedef struct spew_t +{ + ubyte flags; //flags + bool inuse; //if this slot is in use + bool use_gunpoint; //is this a gunpoint based object (yes=true) + bool real_obj; //if this object should be treated as a real object (can hurt you) (yes=true) + + union + { + pointinfo pt; //point info structure (needed if it's point based) + guninfo gp; //gunpoint info structure (needed if it's gunpoint based) + }; + + int effect_type; //viseffect type (see fireball.h) + int phys_info; //physics info type (see physics.h) + int random; //should the viseffect have some randomness to it (must be 0 if real) + int handle; //stored handle for speweffect + + float drag; //drag of viseffect + float mass; //mass of viseffect + float time_int; //how often it should be 'updated' + float longevity; //lifespan of the viseffect (i.e. how long should the puff of smoke last) + float lifetime; //how long viseffect should run (0=infinity, until asked to stop) + float size; //size of viseffect blob + float speed; //speed of viseffect blob + float time_until_next_blob; //per spewer + float start_time; //time viseffect started + + vector gp_normal,gun_point; //vectors saved to keep from recalculating +}spewinfo; + + +#define SPEW_RAND_WIGGLE 1 +#define SPEW_RAND_SPEED 2 +#define SPEW_RAND_SIZE 4 +#define SPEW_RAND_SLIDE 8 +#define SPEW_RAND_LIFETIME 16 + +#define MAX_SPEW_EFFECTS 50 //maximum viseffects to be handled by at once + +// spew array +extern spewinfo SpewEffects[]; + +// spew count value. +extern int spew_count; + +//Initializes the Spew system +void SpewInit(); + +//Creates a Spew effect +//spew -> Pointer to spewinfo structure (that is filled out)...may be deleted after function call +//returns a handle to the spew effect +int SpewCreate(spewinfo *spew); + +//Does a step in a spew system +void SpewEmitAll(void); + +//Clears a Spew Event given a handle to it +void SpewClearEvent(int handle,bool force=true); + +#endif \ No newline at end of file diff --git a/Descent3/splinter.cpp b/Descent3/splinter.cpp new file mode 100644 index 000000000..680bd9421 --- /dev/null +++ b/Descent3/splinter.cpp @@ -0,0 +1,79 @@ + +#include "fireball.h" +#include "object.h" +#include "splinter.h" +#include "polymodel.h" +#include "renderer.h" +#include "gametexture.h" + + +#include "Macros.h" +#include + +#include "psrand.h" +// Given an object, renders the representation of this splinter +void DrawSplinterObject (object *obj) +{ + ASSERT (obj->type==OBJ_SPLINTER); + int i; + poly_model *pm=&Poly_models[obj->id]; + bsp_info *sm=&pm->submodel[obj->ctype.splinter_info.subobj_num]; + int facenum=obj->ctype.splinter_info.facenum; + vector world_verts[MAX_VERTS_PER_SPLINTER]; + vector temp_vec; + g3Point pnts[MAX_VERTS_PER_SPLINTER]; + g3Point *pntlist[MAX_VERTS_PER_SPLINTER]; + + int limit=min(MAX_VERTS_PER_SPLINTER,sm->faces[facenum].nverts); + float lifenorm=1.0-((obj->lifetime-obj->lifeleft)/obj->lifetime); + + rend_SetLighting(LS_NONE); + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (255*lifenorm); + rend_SetTextureType (TT_LINEAR); + + lifenorm=1.0; + + for (i=0;ictype.splinter_info.verts[i]*lifenorm; + + vm_MatrixMulVector (&world_verts[i],&temp_vec,&obj->orient); + + world_verts[i]+=obj->pos; + g3_RotatePoint (&pnts[i],&world_verts[i]); + pntlist[i]=&pnts[i]; + pnts[i].p3_u=sm->faces[facenum].u[i]; + pnts[i].p3_v=sm->faces[facenum].v[i]; + + } + + int bm_handle=GetTextureBitmap (pm->textures[sm->faces[facenum].texnum],0); + g3_DrawPoly (limit,pntlist,bm_handle); + +} + +//do whatever needs to be done for this piece of splinter for this frame +void DoSplinterFrame(object *obj) +{ + ASSERT(obj->control_type == CT_SPLINTER); + + if ((obj->lifeleft<=0.0) && ((ps_rand()%2)==0)) + { + //Mark the object to die + SetObjectDeadFlag(obj); + } + else + { + //Create smoke for some splinters + if (((obj-Objects)%8)==0) + { + if (ps_rand()%4) + { + int type = ((obj-Objects)%2) ? GetRandomSmallExplosion() : BLACK_SMOKE_INDEX; + CreateFireball (&obj->pos,type,obj->roomnum,VISUAL_FIREBALL); + } + } + } +} diff --git a/Descent3/splinter.h b/Descent3/splinter.h new file mode 100644 index 000000000..616bd5b0b --- /dev/null +++ b/Descent3/splinter.h @@ -0,0 +1,12 @@ +#ifndef SPLINTER_H +#define SPLINTER_H + +#include "object.h" + +// Draws a splinter object +void DrawSplinterObject (object *obj); + +//do whatever needs to be done for this piece of splinter for this frame +void DoSplinterFrame(object *obj); + +#endif \ No newline at end of file diff --git a/Descent3/stringtable.h b/Descent3/stringtable.h new file mode 100644 index 000000000..eb9d9beb1 --- /dev/null +++ b/Descent3/stringtable.h @@ -0,0 +1,917 @@ +//Defines for indexes into the string table for in-code strings + +#ifndef __STRING_TABLE____ +#define __STRING_TABLE____ + +#define TXT(index) GetStringFromTable(index) + +//Returns a pointer to the string at the index location from the string table +//if it is a bad index given, then the pointer to the error string "ERROR MISSING STRING" is given + +char *GetStringFromTable(int index); + +#define TXT_NEW TXT(0) //"New" +#define TXT_DELETE TXT(1) //"Delete" +#define TXT_CONFIGURE TXT(2) //"Configure" +#define TXT_OK TXT(3) //"OK" +#define TXT_CANCEL TXT(4) //"Cancel" +#define TXT_DEFAULTMSHIPS TXT(5) //"Default Multiplayer Ship" +#define TXT_NUMKILLS TXT(6) //"Number of Kills:" +#define TXT_NUMDEATHS TXT(7) //"Number of Deaths:" +#define TXT_KIDERATIO TXT(8) //"Kill/Deaths Ratio:" +#define TXT_DIFFICULTYCOLON TXT(9) //"Difficulty:" +#define TXT_LEVELSTRING TXT(10) //"Level %d" +#define TXT_UNNAMED TXT(11) //"" +#define TXT_PRIMGOALS TXT(12) //"Primary Goals" +#define TXT_SECONDARYGOALS TXT(13) //"Secondary Goals" +#define TXT_SHIP_ TXT(14) //"Ship:" +#define TXT_NOPILOT TXT(15) //"No Pilot Selected" +#define TXT_PILOTS TXT(16) //"Pilots" +#define TXT_PLTERROR TXT(17) //"Pilot Error" +#define TXT_NEEDTOCREATE TXT(18) //"You need to create and select a pilot" +#define TXT_PLTOKDEL TXT(19) //"Are you sure you want to delete %s?" +#define TXT_PLTDELCONF TXT(20) //"Delete Confirmation" +#define TXT_PLTDIFFICULT TXT(21) //"Difficulty" +#define TXT_TRAINEE TXT(22) //"Trainee" +#define TXT_ROOKIE TXT(23) //"Rookie" +#define TXT_HOTSHOT TXT(24) //"Hotshot" +#define TXT_ACE TXT(25) //"Ace" +#define TXT_INSANE TXT(26) //"Insane!" +#define TXT_CPYKEYCONF TXT(27) //"COPY CONTROLS" +#define TXT_CUSTKEYB TXT(28) //"CONFIGURE KEYBOARD" +#define TXT_CUSTGAMEC TXT(29) //"CONFIGURE CONTROLLER" +#define TXT_COPYCONFERR TXT(30) //"Copy Config Error" +#define TXT_COPYCONFERR1 TXT(31) //"You Can't Copy the Configuration From The Same Pilot" +#define TXT_PLTENTERNAME TXT(32) //"Enter Pilot Name" +#define TXT_PLTENTERNAME1 TXT(33) //"Please enter a name for your pilot" +#define TXT_FILEERROR TXT(34) //"File Error" +#define TXT_FILEERRPLT1 TXT(35) //"Can't Create Pilot File (Out of Drive Space?)" +#define TXT_PLTERRORNAME TXT(36) //"You Need To Name Your Pilot" +#define TXT_PLTERREXISTS TXT(37) //"Pilot Already Exists, Please Rename" +#define TXT_PLTCHOOSE TXT(38) //"Choose Pilot" +#define TXT_SAVEGAMENAME TXT(39) // "You must specify a name." +#define TXT_LOADMESSAGE TXT(40) // "loading" +#define TXT_AUDIOTAUNTC TXT(41) //"Audio Taunt #3" +#define TXT_AUDIOTAUNTD TXT(42) //"Audio Taunt #4" +#define TXT_MAXENERGY TXT(43) // "Energy Full" +#define TXT_PRESETCONTROLS TXT(44) //"Preset Controls" +#define TXT_NOGOALSAVAIL TXT(45) //"There are no goals available" +#define TXT_USEPRESETS TXT(46) //"Would you like to use one of the preset configurations?" +#define TXT_NOGUIDEBOT TXT(47) //"One second... Waking up." +#define TXT_PRESSESCRET TXT(48) //"Press ESC to return" +#define TXT_VIDEO TXT(49) //"Graphics" +#define TXT_APPLY TXT(50) //"Apply" +#define TXT_GAMMA TXT(51) //"Gamma" +#define TXT_RENDERER TXT(52) //"Renderer" +#define TXT_DIRECT3D TXT(53) //"Direct 3D" +#define TXT_GLIDE TXT(54) //"Glide (3DFX)" +#define TXT_OPENGL TXT(55) //"OpenGL" +#define TXT_RESOLUTION TXT(56) //"Resolution" +#define TXT_COMPLETED TXT(57) //"Completed!" +#define TXT_NOTCOMPLETEDYET TXT(58) //"Not Completed Yet" +#define TXT_CANT_LOAD_MULTI TXT(59) //You cannot load a save game in multiplayer! +#define TXT_BILINEAR TXT(60) //"Bilinear Filtering" +#define TXT_MIPMAPPING TXT(61) //"Mipmapping" +#define TXT_DYNLIGHTING TXT(62) //"Dynamic Lighting" +#define TXT_GOALSTATUSHEADING TXT(63) //" Goal Status" +#define TXT_CANT_SAVE_MULTI TXT(64) //"You cannot save a game in multiplayer!" +#define TXT_SOUND TXT(65) //"Sound" +#define TXT_SOUNDVOL TXT(66) //"Sound Volume" +#define TXT_GENERAL TXT(67) //"General" +#define TXT_TERRAUTOLEV TXT(68) //"Terrain Autoleveling" +#define TXT_MINEAUTOLEV TXT(69) //"Mine Autoleveling" +#define TXT_LEFT TXT(70) //"Left" +#define TXT_NONE TXT(71) //"None" +#define TXT_MISSILE TXT(72) //"Missile" +#define TXT_RIGHT TXT(73) //"Right" +#define TXT_MISSILEVIEW TXT(74) //"Missile View" +#define TXT_DETAILS TXT(75) //"Details" +#define TXT_TERRDETAIL TXT(76) //"Terrain Detail" +#define TXT_RENDDIST TXT(77) //"Terrain Render Depth" +#define TXT_TERRCAST TXT(78) //"Terrain Casting" +#define TXT_OFF TXT(79) //"Off" +#define TXT_ON TXT(80) //"On" +#define TXT_NO TXT(81) //"No" +#define TXT_YES TXT(82) //"Yes" +#define TXT_ERRUNINITCNT TXT(83) //"Unable to initialize game control/joystick system." +#define TXT_CLICKXCLEAR TXT(84) //"Click on 'X' or CTRL-C to clear bindings for selection." +#define TXT_ACTIVE TXT(85) //"Active" +#define TXT_D3ERROR1 TXT(86) //"%s file <%s>: errno = %d\n" +#define TXT_READING TXT(87) //"Reading" +#define TXT_WRITING TXT(88) //"Writing" +#define TXT_ADJUSTSETTINGS TXT(89) //"Adjust settings" +#define TXT_ERRNORENDERER TXT(90) //"Unable to initialize renderer. Descent 3 requires a 3DFX for now." +#define TXT_ERRNOFONT TXT(91) //"Unable to load font %s." +#define TXT_ERRSCRNSHT TXT(92) //"Not enough memory to do screenshot!" +#define TXT_SCRNSHT TXT(93) //"Screenshot saved as '%s'" +#define TXT_CHEATER TXT(94) //"%s is cheating!" +#define TXT_HUDENABLED TXT(95) //"HUD Enabled" +#define TXT_RECEIVINGDATA TXT(96) //"Receiving data..." +#define TXT_LOADINGLEVEL TXT(97) //"Loading level..." +#define TXT_HELP TXT(98) //"Keys" +#define TXI_HLPPRNTSCRN 99 //"PrintScreen" +#define TXI_HLPTAKESCRNSHT 100 //"Save A Screenshot" +#define TXI_F3 101 //"F3" +#define TXI_HLPCOCKPIT 102 //"Toggle Cockpit" +#define TXI_F2 103 //"F2" +#define TXI_HLPCONFIG 104 //"Options Menu" +#define TXI_ESC 105 //"ESC" +#define TXI_HLPQUIT 106 //"Abort Game" +#define TXI_PLUSMINUS 107 //"+/-" +#define TXI_HLPSCRNSIZE 108 //"Change Screen Size" +#define TXI_HLPPAUSE 109 //"Pause" +#define TXI_HLPPAUSEDESC 110 //"Pause The Game" +#define TXI_SF1 111 //"SHIFT F1" +#define TXI_SF2 112 //"SHIFT F2" +#define TXI_SF3 113 //"SHIFT F3" +#define TXI_HLPREARLEFT 114 //"Show Rear View (Left Window)" +#define TXT_JOYMOUSESETTINGS TXT(115) //"Joystick-Mouse settings" +#define TXI_HLPREARRIGHT 116 //"Show Rear View (Right Window)" +#define TXT_HUDSAY TXT(117) //"%s says: %s" +#define TXT_MESSAGE TXT(118) //"Message: %s" +#define TXT_KEYSETTINGS TXT(119) //"" +#define TXT_INV TXT(120) //"inv" +#define TXT_CLK TXT(121) //"clk" +#define TXT_WPNSELBTN TXT(122) //"%s-disabled" +#define TXT_INITDATA TXT(123) //"Loading data..." +#define TXT_INITTABLEERR TXT(124) //"Couldn't successfully load the table files. I'm shutting down!" +#define TXT_INITCOLLATING TXT(125) //"Collating..." +#define TXT_LEVELERRNEW TXT(126) //"Don't know how to load level -- version number too new" +#define TXT_KEYCFGHELP TXT(127) //"Select slot to configure for keyboard." +#define TXT_MENUNEWGAME TXT(128) //"New Game" +#define TXT_MENUOPTIONS TXT(129) //"Options" +#define TXT_MENUPILOTS TXT(130) //"Pilots" +#define TXT_MENUMULTIPLAYER TXT(131) //"Multiplayer" +#define TXT_MENUQUIT TXT(132) //"Quit" +#define TXT_MENULOADMSN TXT(133) //"Load Mission" +#define TXT_ERROR TXT(134) //"Error" +#define TXT_ERRLOADMSN TXT(135) //"Failed to load mission." +#define TXT_OPTCUSTKEYB TXT(136) //"Customize Keyboard" +#define TXT_OPTCUSTCONT TXT(137) //"Customize Game Controller" +#define TXT_OPTVIDEO TXT(138) //"Video Settings" +#define TXT_OPTSOUND TXT(139) //"Sound Settings" +#define TXT_OPTGENERAL TXT(140) //"General Settings" +#define TXT_OPTDETAIL TXT(141) //"Detail Level Settings" +#define TXT_DONE TXT(142) //"Done" +#define TXT_CENTER TXT(143) //"Center" +#define TXT_MSNERROR TXT(144) //"Unable to find level file %s." +#define TXT_CFGHELP_KEYB TXT(145) //"Keyboard" +#define TXT_MLTSHUTDOWN TXT(146) //"Shutting down server!" +#define TXT_MLTLEAVEGAME TXT(147) //"%s has left the game!" +#define TXT_MLTSERVERQUIT TXT(148) //"The server quit!" +#define TXT_MLTDISCONNECT TXT(149) //"%s disconnected!" +#define TXT_MLTLEVELNOMATCH TXT(150) //"Levels don't match!" +#define TXT_MLTDISCFRMSERV TXT(151) //"Disconnected from server!" +#define TXT_MLTCANTCONNECT TXT(152) //"Can't connect. Reason: %s" +#define TXT_MLTNORESPONSE TXT(153) //"No response from server" +#define TXT_MLTWAITSERVER TXT(154) //"Waiting for server..." +#define TXT_MLTNOLEVELINFO TXT(155) //"Couldn't get level info from server!" +#define TXI_MLTOK 156 //"OK!" +#define TXI_MLTNOTSERV 157 //"Not server" +#define TXI_MLTDORK 158 //"You're a dork" +#define TXI_MLTNOTENOUGHSTR 159 //"Not enough start positions" +#define TXI_MLTGAMEFULL 160 //"All Full!" +#define TXT_MULTIPLAYER TXT(161) //"Multiplayer" +#define TXT_MLTCHOOSETYPE TXT(162) //"Choose Game Type" +#define TXT_MLTNOMULTI TXT(163) //"No Multiplayer Files Found!" +#define TXT_WPNSELPREC TXT(164) //"Weapon Selection Precedence" +#define TXT_SPACETOCONT TXT(165) //"Press space bar to continue..." +#define TXI_SCRNNONE 166 //"None" +#define TXI_SCRNFEEBLE 167 //"Feeble" +#define TXI_SCRNPOOR 168 //"Poor" +#define TXI_SCRNAVG 169 //"Average" +#define TXI_SCRNGOOD 170 //"Good" +#define TXI_SCRNEXCELLENT 171 //"Excellent" +#define TXI_SCRNSUPREME 172 //"Supreme" +#define TXT_TCPLEASEWAIT TXT(173) //"Please wait...Initializing" +#define TXT_TCMAINMENU TXT(174) //"Main Menu" +#define TXT_TCBRIEFINGS TXT(175) //"Briefings" +#define TXT_TCAUTOMAP TXT(176) //"AutoMap" +#define TXI_WPN_LASER 177 //"Laser" +#define TXI_WPN_VAUSS 178 //"Vauss" +#define TXI_WPN_MICROWAVE 179 //"Microwave" +#define TXI_WPN_PLASMA 180 //"Plasma" +#define TXI_WPN_FUSION 181 //"Fusion" +#define TXI_WPN_SUPLASER 182 //"Super Laser" +#define TXI_WPN_MASSDRIVER 183 //"Mass Driver" +#define TXI_WPN_NAPALM 184 //"Napalm" +#define TXI_WPN_EMDGUN 185 //"EMD Gun" +#define TXI_WPN_OMEGA 186 //"Omega" +#define TXI_WPN_CONCUSSION 187 //"Concussion" +#define TXI_WPN_HOMING 188 //"Homing" +#define TXI_WPN_IMPACT 189 //"Impact Mortar" +#define TXI_WPN_SMART 190 //"Smart" +#define TXI_WPN_MEGA 191 //"Mega" +#define TXI_WPN_FRAG 192 //"Frag" +#define TXI_WPN_GUIDED 193 //"Guided" +#define TXI_WPN_NAPALMR 194 //"Napalm Rocket" +#define TXI_WPN_CYCLONE 195 //"Cyclone" +#define TXI_WPN_BLACKSHARK 196 //"Black Shark" +#define TXI_WPN_YELL_FLARE 197 //"Yellow Flare" +#define TXI_WPNC_LASER_1 198 //"Laser" +#define TXI_WPNC_LASER_2 199 //"" +#define TXI_WPNC_VAUSS_1 200 //"Vauss" +#define TXI_WPNC_VAUSS_2 201 //"" +#define TXI_WPNC_MICRO_1 202 //"Micro-" +#define TXI_WPNC_MICRO_2 203 //"wave" +#define TXI_WPNC_PLASMA_1 204 //"Plasma" +#define TXI_WPNC_PLASMA_2 205 //"" +#define TXI_WPNC_FUSION_1 206 //"Fusion" +#define TXI_WPNC_FUSION_2 207 //"" +#define TXI_WPNC_SUPLAS_1 208 //"Super" +#define TXI_WPNC_SUPLAS_2 209 //"Laser" +#define TXI_WPNC_MASSD_1 210 //"Mass" +#define TXI_WPNC_MASSD_2 211 //"Driver" +#define TXI_WPNC_NAPALM_1 212 //"Napalm" +#define TXI_WPNC_NAPALM_2 213 //"" +#define TXI_WPNC_EMD_1 214 //"EMD" +#define TXI_WPNC_EMD_2 215 //"Gun" +#define TXI_WPNC_OMEGA_1 216 //"Omega" +#define TXI_WPNC_OMEGA_2 217 //"" +#define TXI_WPNC_CONC_1 218 //"Concussn" +#define TXI_WPNC_CONC_2 219 //"" +#define TXI_WPNC_HOMING_1 220 //"Homing" +#define TXI_WPNC_HOMING_2 221 //"" +#define TXI_WPNC_IMPACT_1 222 //"Impact" +#define TXI_WPNC_IMPACT_2 223 //"Mortar" +#define TXI_WPNC_SMART_1 224 //"Smart" +#define TXI_WPNC_SMART_2 225 //"" +#define TXI_WPNC_MEGA_1 226 //"Mega" +#define TXI_WPNC_MEGA_2 227 //"" +#define TXI_WPNC_FRAG_1 228 //"Frag" +#define TXI_WPNC_FRAG_2 229 //"" +#define TXI_WPNC_GUID_1 230 //"Guided" +#define TXI_WPNC_GUID_2 231 //"" +#define TXI_WPNC_NAPALMR_1 232 //"Napalm" +#define TXI_WPNC_NAPALMR_2 233 //"Rocket" +#define TXI_WPNC_CYCLONE_1 234 //"Cyclone" +#define TXI_WPNC_CYCLONE_2 235 //"" +#define TXI_WPNC_BLKSHRK_1 236 //"Black" +#define TXI_WPNC_BLKSHRK_2 237 //"Shark" +#define TXI_WPNC_YELFLARE_1 238 //"Yellow" +#define TXI_WPNC_YELFLARE_2 239 //"Flare" +#define TXT_WPNNOTAVAIL TXT(240) //"That weapon is not available." +#define TXT_OBJECTIVES1 TXT(241) //"Objectives" *not the one used in telcom* +#define TXT_WPNSELECT TXT(242) //"%s selected." +#define TXT_WPNNONRG TXT(243) //"Not enough energy available to fire this weapon!" +#define TXT_WPNNOAMMO TXT(244) //"Not enough ammo available to fire this weapon!" +#define TXT_WPNNOPROJ TXT(245) //"Not enough projectiles available!" +#define TXT_WPNFLARENONRG TXT(246) //"Not enough energy available to fire flare!" +#define TXT_MLTDISCONNECTED TXT(247) //""%s disconnected!" +#define TXT_PLRACCURACY TXT(248) //"Accuracy rating:" +#define TXT_PLRSHIELD TXT(249) //"Shield rating:" +#define TXT_PLRENERGY TXT(250) //"Energy rating" +#define TXT_PLRENEMIESKILL TXT(251) //"Enemies killed:" +#define TXT_PLRMINEEXPLORE TXT(252) //"Mine explored:" +#define TXT_PLRTIME TXT(253) //"Time played:" +#define TXT_PLRSAVERESRAT TXT(254) //"Save/Restore ratio:" +#define TXT_PLRTITLE TXT(255) //"Pilot Performance Review" +#define TXT_PLRSUCCESS TXT(256) //"Success" +#define TXT_PLRFAILED TXT(257) //"Failed" +#define TXI_KB_FIREPRIMARY 258 //"Fire Primary\t\t" +#define TXI_KB_FIRESECONDARY 259 //"Fire Secondary\t\t" +#define TXI_KB_ACCELERATE 260 //"Accelerate\t\t\t" +#define TXI_KB_REVERSE 261 //"Reverse\t\t\t" +#define TXI_KB_SLIDEHORIZ 262 //"Slide Horizontal\t\t" +#define TXI_KB_SLIDELEFT 263 //"Slide Left\t\t\t" +#define TXI_KB_SLIDERIGHT 264 //"Slide Right\t\t\t" +#define TXI_KB_SLIDEVERT 265 //"Slide Vertical\t\t" +#define TXI_KB_SLIDEUP 266 //"Slide Up\t\t\t" +#define TXI_KB_SLIDEDOWN 267 //"Slide Down\t\t\t" +#define TXI_KB_BANKLEFT 268 //"Bank Left\t\t\t" +#define TXI_KB_BANKRIGHT 269 //"Bank Right\t\t\t" +#define TXI_KB_PITCHUP 270 //"Pitch Up\t\t\t" +#define TXI_KB_PITCHDOWN 271 //"Pitch Down\t\t\t" +#define TXI_KB_TURNLEFT 272 //"Turn Left\t\t\t" +#define TXI_KB_TURNRIGHT 273 //"Turn Right\t\t\t" +#define TXI_KB_FIREFLARE 274 //"Fire Flare\t\t\t" +#define TXI_KB_TOGGLESLIDE 275 //"Toggle Slide\t\t" +#define TXI_KB_TOGGLEBANK 276 //"Toggle Bank\t\t\t" +#define TXI_KB_HEADING 277 //"Heading\t\t\t" +#define TXI_KB_PITCH 278 //"Pitch\t\t\t\t" +#define TXI_KB_THROTTLE 279 //"Throttle\t\t\t" +#define TXI_KB_FORWARD 280 //"Forward\t\t\t" +#define TXI_KB_BANK 281 //"Bank\t\t\t\t" +#define TXI_KB_AFTERBURN 282 //"Afterburner\t\t\t" +#define TXI_KB_AUTOMAP 283 //"Automap\t\t\t" +#define TXT_QUIT TXT(284) //"Quit" +#define TXT_COMPLETED_HUD TXT(285) //"Completed" +#define TXT_SNDMUSVOL TXT(286) //"Music Volume" +#define TXT_SNDMIXER TXT(287) //"Sound Mixer" +#define TXT_SNDM_SOFT TXT(288) //"Software" +#define TXT_SNDM_DSND8 TXT(289) //"DirectSound 8Bit" +#define TXT_SNDM_DS3D TXT(290) //"DS3D" +#define TXT_SNDM_AUREAL TXT(291) //"Aureal" +#define TXT_SNDQUALITY TXT(292) //"Sound Quality" +#define TXT_SNDHIGH TXT(293) //"High" +#define TXT_SNDNORMAL TXT(294) //"Normal" +#define TXT_OPTHUD TXT(295) //"Customize Hud" +#define TXT_CFGHELP_JOY TXT(296) //"Joystick Handle" +#define TXT_TEXT TXT(297) //"Text" +#define TXT_GRAPHICAL TXT(298) //"Graphical" +#define TXT_HUDSHIPSTATUS TXT(299) //"Ship Status" +#define TXT_HUDSHIELDENERGY TXT(300) //"Shield/Energy" +#define TXT_HUDWEAPONS TXT(301) //"Weapon Layout" +#define TXT_HUDAFTERBURN TXT(302) //"Afterburner" +#define TXT_HUDGOALSTOGGLE TXT(303) //"Goals" +#define TXT_HUDMISSIONSTATUS TXT(304) //"Mission Status" +#define TXT_HUDINVENTORY TXT(305) //"Inventory" +#define TXT_CFGHELP_BTNS TXT(306) //"Joystick, mouse buttons or hat." +#define TXT_SPECMAPPING TXT(307) //"Specular Mapping" +#define TXT_CFG_BIND_1 TXT(308) //"Bind 1: %s" +#define TXT_CFG_BIND_2 TXT(309) //"Bind 2: %s" +#define TXT_SNDM_DSND16 TXT(310) //"DirectSound 16Bit" +#define TXT_HUDCNTRMEASURE TXT(311) //"Countermeasures" +#define TXT_HUDITM_ANTIGRAV TXT(312) //"ANTIGRAV FAIL" +#define TXT_HUDITM_LOCK TXT(313) //"LOCK" +#define TXT_CFG_CLEAR TXT(314) //"Clear" +#define TXT_TCCARGO TXT(315) //"Cargo" +#define TXT_ALL TXT(316) //"All" +#define TXT_WARNINGS TXT(317) //"Warnings" +#define TXT_IMPORTIFL TXT(318) //"Import IFL File" +#define TXT_SELECTSHIP TXT(319) //"Select Ship" +#define TXT_SHIPDISPLAY TXT(320) //"Ship Display" +#define TXT_SHIPS TXT(321) //"Ships" +#define TXT_CUSTOMTEXTURES TXT(322) //"Custom Textures" +#define TXT_CHOOSE TXT(323) //"Choose" +#define TXT_SUCCESS TXT(324) //"Success" +#define TXT_ERRORIMPORT TXT(325) //"There was an error importing" +#define TXT_SUCCESSIMPORT TXT(326) //"Animated Bitmap Successfully Imported" +#define TXT_CUSTOMLOGO TXT(327) //"Custom Logo" +#define TXT_SHIPCUSTOMIZE TXT(328) //"SHIP CONFIGURATION" +#define TXT_UPTOPARENTDIR TXT(329) //"Up To Parent Directory" +#define TXT_ERRCHOOSEFILE TXT(330) //"You Must Select A Filename" +#define TXT_ERRFILENOTEXIST TXT(331) //"File Does Not Exist" +#define TXT_ERRPATHNOTVALID TXT(332) //"Invalid Path/Directory" +#define TXT_ILLEGALSAVEGAME TXT(333) //"Illegal savegame file" +#define TXT_SAVEGAMEFAILED TXT(334) //"Unable to save current game." +#define TXT_LOADGAMEFAILED TXT(335) //"Unable to load game." +#define TXT_EMPTY TXT(336) //"Empty" +#define TXT_ERRCREATEDIR TXT(337) //"Unable to create directory." +#define TXT_ERRNOSAVEGAMES TXT(338) //"There are no savegames to load." +#define TXT_NOMISSIONS TXT(339) //"There are no valid missions in the missions directory." +#define TXT_LOADGAME TXT(340) //"Load Game" +#define TXT_SAVEGAME TXT(341) //"Save Game" +#define TXT_DESCRIPTION TXT(342) //"Description" +#define TXT_FASTHEADLIGHT TXT(343) //"Fast Headlight" +#define TXT_MIRRORSURF TXT(344) //"Mirrored Surfaces" +#define TXI_KB_PREVINV 345 //"Prev Inven Item" +#define TXI_KB_NEXTINV 346 //"Next Inven Item" +#define TXI_KB_INVUSE 347 //"Use Inven Item" +#define TXI_KB_PREVCNTMS 348 //"Prev Counter Meas" +#define TXI_KB_NEXTCNTMS 349 //"Next Counter Meas" +#define TXI_KB_CNTMSUSE 350 //"Use Counter Meas" +#define TXT_SENSITIVITY TXT(351) //"Sensitivity" +#define TXT_MSESENS TXT(352) //"Mouse Sensitivity" +#define TXI_KB_HEADLIGHT 353 //"Headlight" +#define TXI_HLPALTF2 354 //"Alt-F2" +#define TXI_HLPSAVEGAME 355 //"Save Game" +#define TXI_HLPALTF3 356 //"Alt-F3" +#define TXI_HLPLOADGAME 357 //"Load Game" +#define TXI_HLPF4 358 //"F4" +#define TXI_HLPGUIDEBOT 359 //"GuideBot Menu" +#define TXI_HLP1_5 360 //"1-5" +#define TXI_HLPSELPRIM 361 //"Select Primary Weapon" +#define TXI_HLP6_0 362 //"6-0" +#define TXI_HLPSELSECN 363 //"Select Secondary Weapon" +#define TXT_WRONGVERSION TXT(364) //"Not available in this version." +#define TXT_SETDEFAULT TXT(365) //"%s Set as Default" +#define TXT_SETITEMDEFAULT TXT(366) //"Set Selected as Default" +#define TXT_REALHELP TXT(367) //"Help" +#define TXT_LBNONE TXT(368) //"[None]" +#define TXT_SAVE TXT(369) //"Save" +#define TXT_OPEN TXT(370) //"Open" +#define TXT_MULTINOFILES TXT(371) //"No Multiplayer Files Found!" +#define TXT_MULTISAVESET TXT(372) //"Save Multiplayer Settings" +#define TXT_MULTILOADSET TXT(373) //"Load Multiplayer Settings" +#define TXT_ALLOWEDITEMS TXT(374) //"Allowed items" +#define TXT_DISALLOWEDITEM TXT(375) //"Disallowed items" +#define TXT_ALLOWEDSHIPS TXT(376) //"Allowed Ships" +#define TXT_DISALLOWDSHIPS TXT(377) //"Disallowed Ships" +#define TXT_ALLOW TXT(378) //"Allow" +#define TXT_DISALLOW TXT(379) //"Disallow" +#define TXT_SHIPSALLOWED TXT(380) //"Ships Allowed:" +#define TXT_WPNCFGHELP0 TXT(381) //"Select weapon to switch that slot with another weapon." +#define TXT_WPNCFGHELP1 TXT(382) //"To disable a weapon, click on the 'X' button or use CTRL-C on highlighted weapon." +#define TXT_CFGHELP_TEXT TXT(383) //"%s may be configured using" +#define TXT_WPNCFGHELP3 TXT(384) //"Select weapon you want to switch precedence for:" +#define TXT_WPNSELDISABLE TXT(385) //"Disable/Enable Slot" +#define TXT_PRIMARY TXT(386) //"Primary" +#define TXT_SECONDARY TXT(387) //"Secondary" +#define TXT_LOWPRIORITY TXT(388) //"Low Priority" +#define TXT_HIGHPRIORITY TXT(389) //"High Priority" +#define TXI_F5 390 //"F5" +#define TXI_TOGGLEDEMO 391 //"Toggle Demo" +#define TXI_KB_WPNPCYCLE 392 //"Cycle Primary" +#define TXI_KB_WPNSCYCLE 393 //"Cycle Secondary" +#define TXT_MENUCREDITS TXT(394) //"Credits" +#define TXT_CFG_INVERT TXT(395) //"Invert" +#define TXT_RESETTODEFAULT TXT(396) //"Reset to Default Settings" +#define TXI_F6 397 //"F6" +#define TXI_MLTMENU 398 //"Multi On-Screen Menu" +#define TXT_VIEWDEMO TXT(399) //"View Demo (main menu)" +#define TXT_RECORDINGDEMO TXT(400) //"Recording Demo (hud)" +#define TXT_PLAYINGDEMO TXT(401) //"Playing Demo (hud)" +#define TXT_DEMOFILENAME TXT(402) //"Demo FileName" +#define TXT_CANTSAVEDEMO TXT(403) //"Unable to save demo file!" +#define TXT_DEMOSAVED TXT(404) //"Demo file saved!" +#define TXI_TCMM_GOALS 405 //"Goal Status" +#define TXT_DEMOCANTCREATE TXT(406) //"Unable to create demo file!" +#define TXT_CANTLOAD TXT(407) //"Unable to load demo file!" +#define TXT_BADDEMOFILE TXT(408) //"Bad Demo file, unable to read." +#define TXT_MINFPS TXT(409) //"Min. FPS: %.2f" +#define TXT_MAXFPS TXT(410) //"Max. FPS: %.2f" +#define TXT_AVGFSP TXT(411) //"Avg. FPS: %.2f" +#define TXT_PLAYLOOPING TXT(412) //"Play Looping" +#define TXT_DEMOPAUSED TXT(413) //"Demo Playback Paused" +#define TXT_DOWNLOADPROMPT TXT(414) //"Would you like to download it?" +#define TXT_DONTHAVEMSN TXT(415) //"You do not have this mission installed." +#define TXT_DOWNLOADMSN TXT(416) //"Download Mission" +#define TXT_FMTDOWNLOADING TXT(417) //"Downloading: %s" +#define TXT_FMTRECEIVED TXT(418) //"Received: %d" +#define TXT_FMTTOTAL TXT(419) //"Total: %d" +#define TXT_FMTTIMEELAPSED TXT(420) //"Time Elapsed: %s" +#define TXT_FMTTIMELEFT TXT(421) //"Time Left: %s" +#define TXT_FMTXFERRATE TXT(422) //"Transfer Rate: %d Bytes/Second" +#define TXT_FMTCANTDNLD TXT(423) //"Unable to download the selected mission!" +#define TXT_FMTTIMEHOUR TXT(424) //"%d hr. " +#define TXT_FMTTIMEMIN TXT(425) //"%d min. " +#define TXT_FMTTIMESEC TXT(426) //"%d sec. " +#define TXT_GEN_EXIT TXT(427) //"Exit" +#define TXT_GEN_MPLYROPTIONS TXT(428) //"Multiplayer Options" +#define TXT_GEN_TIMELIMIT TXT(429) //"Time Limit" +#define TXT_GEN_KILLGOAL TXT(430) //"Goal Limit" +#define TXT_GEN_PPS TXT(431) //"Packets per second" +#define TXT_GEN_CFGALLOWEDSHIP TXT(432) //"Configure Allowed Ships/Items" +#define TXT_GEN_SERVERMODE TXT(433) //"Server mode" +#define TXT_GEN_CLIENTSERVER TXT(434) //"Client/Server" +#define TXT_GEN_PEERPEER TXT(435) //"Peer-Peer" +#define TXT_GEN_RESPAWNRATE TXT(436) //"Respawn Rate" +#define TXT_GEN_USEROTVEL TXT(437) //"Use rotational velocity" +#define TXT_GEN_USESMOOTHING TXT(438) //"Movement averaging" +#define TXT_GEN_MAXPLAYERS TXT(439) //"Max. Players" +#define TXT_GEN_ACC_WEAP_COLL TXT(440) //"%c Use Accurate weapon collisions" +#define TXT_GEN_BRIGHT_PLAYERS TXT(441) //"Bright player ships" +#define TXT_MAX_TERRAIN_DIST TXT(442) //"Max terrain distance" +#define TXT_FIELD_OF_VIEW TXT(443) //"Field of view" +#define TXT_GEN_PERMISSABLE_CS TXT(444) //"Permissable client server" +#define TXT_AUTO_GAMMA TXT(445) //"Auto gamma" +#define TXT_MULTIPLAYER_TAUNTS TXT(446) //" Multiplayer taunt macros" +#define TXT_TAUNT_NUMBER TXT(447) //"" +#define TXT_TAUNT_TEXT 448 //" Note! This is not like the others!" +#define TXT_TAUNT_TEXTA 449 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_TAUNT_TEXTB 450 //" Note! This is not like the others!" +#define TXT_TAUNT_TEXTC 451 //" Note! This is not like the others!" +#define TXT_TAUNT_TEXTD 452 //" Note! This is not like the others!" +#define TXT_TAUNT_TEXTE 453 //" Note! This is not like the others!" +#define TXT_TAUNT_TEXTF 454 //" Note! This is not like the others!" +#define TXT_TAUNT_TEXTG 455 //" Note! This is not like the others!" +#define TXT_FILENOTFOUND TXT(456) //"File Not Found" +#define TXT_NOTRIFF TXT(457) //"File Not In RIFF Format" +#define TXT_NOTWAVE TXT(458) //"File Not A WAVE " +#define TXT_INVALIDFILE TXT(459) //"Invalid File" +#define TXT_FORMATNOTSUPPORTED TXT(460) //"File Format Not Supported" +#define TXT_MUSTBEMONO TXT(461) //"WAV Must Be In Mono Format" +#define TXT_BADSAMPLERATE TXT(462) //"WAV Must Have 22,050 Sample Rate" +#define TXT_BADBITDEPTH TXT(463) //"Invalid Bit Depth, Must Be 8 or 16" +#define TXT_NODATAINWAVE TXT(464) //"No Data Found In WAV" +#define TXT_OSFALREADYEXISTS TXT(465) //"OSF File Already Exists, Can't Overwrite" +#define TXT_INTERNALERROR TXT(466) //"Internal Error" +#define TXT_COMPRESSFAIL TXT(467) //"Compression Failure" +#define TXT_OUTOFMEMORY TXT(468) //"Out of Memory" +#define TXT_CANTOPENOSF TXT(469) //"Can't Open OSF For Writing" +#define TXT_NOERROR TXT(470) //"No Error" +#define TXT_GAMMAADJUSTA TXT(471) //"Adjust the gamma slider so that the dark" +#define TXT_GAMMAADJUSTB TXT(472) //"grey box above is barely visible inside the black box" +#define TXT_CFG_BITDEPTH TXT(473) //"Bit Depth" +#define TXT_CFG_SIXTEENBIT TXT(474) //"16 Bit" +#define TXT_CFG_THIRTYTWOBIT TXT(475) //"32 Bit" +#define TXT_CFG_VSYNCENABLED TXT(476) //"Vsync Enabled" +#define TXT_SNDM_CREATIVEEAX TXT(477) //"Creative EAX" +#define TXT_CFG_MEDIUM TXT(478) //"Medium" +#define TXT_CFG_MOUSEENABLED TXT(479) //"Mouse" +#define TXT_CFG_CONTROLLERENABLED TXT(480) //"Controller Enabled" +#define TXT_CFG_JOYSENSITIVITY TXT(481) //"Joystick %c Sensitivity" +#define TXT_CFG_MOUSESENSITIVITY TXT(482) //"Mouse %c Sensitivity" +#define TXT_CFG_CONFIGFORCEFEEDBACK TXT(483) //"Config ForceFeedback" +#define TXT_CFG_PRESETDETAILS TXT(484) //"Preset Details" +#define TXT_LOW TXT(485) //"Low" +#define TXT_CFG_VERYHIGH TXT(486) //"Very High" +#define TXT_CFG_CUSTOM TXT(487) //"Custom" +#define TXT_CFG_OBJECTCOMPLEXITY TXT(488) //"Object Complexity" +#define TXT_CFG_ENABLEFOG TXT(489) //"Enable Fog" +#define TXT_CFG_ENABLELIGHTCORONA TXT(490) //"Enable Light Coronas" +#define TXT_CFG_PROCEDURALS TXT(491) //"Use Procedural Textures" +#define TXT_CFG_POWERUPHALOS TXT(492) //"Powerup Halos" +#define TXT_CFG_SCORCHMARKS TXT(493) //"Scorch Marks" +#define TXT_CFG_WEAPONEFFECTS TXT(494) //"Weapon Effects" +#define TXT_TITLE_FORCEFEEDBACK TXT(495) //"Force Feedback" +#define TXT_CFG_FFENABLED TXT(496) //"Force Feedback Enabled" +#define TXT_CFG_FFAUTOCENTER TXT(497) //"Controller AutoCenter" +#define TXT_CFG_FORCEGAIN TXT(498) //"Force Gain" +#define TXT_CFG_HIGH TXT(499) //"High" +#define TXT_HEADLIGHTTURNED TXT(500) //"Headlight turned %s." +#define TXT_STRING_ON TXT(501) //"on" +#define TXT_STRING_OFF TXT(502) //"off" +#define TXT_REARVIEW TXT(503) //"rear view" +#define TXT_JOYENABLED TXT(504) //"Joystick" +#define TXT_DS_COULDNTINIT TXT(505) //"Couldn't initialize connection '%s' DLL.\n" +#define TXT_DS_DLLINIT TXT(506) //"Connection DLL initialized.\n" +#define TXT_DS_CONNECTLOADERR TXT(507) //"Error loading connection dll '%s'.\n" +#define TXT_DS_LOADMISSIONERR TXT(508) //"Couldn't load mission '%s' for dedicated server.\n" +#define TXT_DS_MISSIONLOADED TXT(509) //"Mission '%s' loaded successfully.\n" +#define TXT_DS_SERVERNAME TXT(510) //"-Server-" +#define TXT_DS_BADCOMMANDLINE TXT(511) //"Bad command line\n" +#define TXT_DS_MISSINGCONFIG TXT(512) //"Missing server config filename\n" +#define TXT_DS_BADCONFIG TXT(513) //"Unable to open server config '%s' or bad file.\n" +#define TXT_DS_SETTINGSLOADED TXT(514) //"Settings file loaded successfully.\n" +#define TXT_DS_SETTINGSERR TXT(515) //"Settings file failed to load.\n" +#define TXT_DS_DISALLOWOBJECT TXT(516) //"Disallowing objected named %s." +#define TXT_DS_ALLOWOBJECTS TXT(517) //"Allowing objected named %s." +#define TXT_DS_VARSET TXT(518) //"Setting '%s' variable to %s.\n" +#define TXT_DS_BADCOMMAND TXT(519) //"Unrecognized command or bad format.\n" +#define TXT_DS_ENTERPASS TXT(520) //"Enter Password:" +#define TXT_DS_REJECTREMOTE TXT(521) //"Rejecting connection from remote host! (%s)\n" +#define TXT_DS_NEWCONNECT TXT(522) //"New connection (%s)\n" +#define TXT_DS_REMOTECLOSE TXT(523) //"Remote host %s closed the connection.\n" +#define TXT_DS_REMOTELOGGEDIN TXT(524) //"Remote host %s logged in.\n" +#define TXT_DS_BADPASS TXT(525) //"Invalid login password from %s.\n" +#define TXT_DEMO_PLAY_OPTIONS TXT(526) //"Demo Playback options" +#define TXT_LOADMODULEERR TXT(527) //"Couldn't Load Module" +#define TXT_INITMODULEERR TXT(528) //"Couldn't Initialize Game Module" +#define TXT_CHEATATTEMPT TXT(529) //"%s is attempting to cheat!!!" +#define TXT_LAMER TXT(530) //"Lamer!" +#define TXT_FRAMETIMEMSG TXT(531) //"Frame time: %s" +#define TXT_ENABLED TXT(532) //"Enabled" +#define TXT_DISABLED TXT(533) //"Disabled" +#define TXT_COOLTEXTURES TXT(534) //"Cool Textures!!" +#define TXT_NORMALTEXTURES TXT(535) //"Textures Back To Normal" +#define TXT_INVULNCHEATON TXT(536) //"Your mother wont protect you now!" +#define TXT_INVULNCHEAT TXT(537) //"You're a wimp!" +#define TXT_CLOAKCHEATOFF TXT(538) //"You escape the darkness!" +#define TXT_CLOAKCHEATON TXT(539) //"You enter the darkness!" +#define TXT_ALLWEAPONS TXT(540) //"All weapons!" +#define TXT_ALLROBOTSDEAD TXT(541) //"All robots dead!" +#define TXT_NOPAUSEINMULTI TXT(542) //"You cannot pause in a multiplayer game" +#define TXT_PRESSOKTOCONT TXT(543) //"The game is paused. Press OK to resume." +#define TXT_CONFIRM TXT(544) //"Confirm" +#define TXT_CONFIRMEXIT TXT(545) //"Are You Sure You Want To Exit?" +#define TXT_NOCOCKPIT TXT(546) //"This ship does not have a cockpit!" +#define TXT_HUD_GUIDED TXT(547) //"guided" +#define TXT_HUD_ZOOM TXT(548) //"zoom view" +#define TXT_HUD_ZOOM_UNITS TXT(549) //"%.1f units" +#define TXT_HUD_SHIELDS TXT(550) //"Shields: %03d" +#define TXT_HUD_ENERGY TXT(551) //"Energy: %03d" +#define TXT_HUD_AFTERBURNER TXT(552) //"Afterburner: %d%%" +#define TXT_HUD_INVULN TXT(553) //"Invulnerable" +#define TXT_HUD_CLOAKED TXT(554) //"Cloaked" +#define TXT_HUD_TEAMSAY TXT(555) //"Team Say: %s" +#define TXT_HUD_MARKER TXT(556) //"Marker: %s" +#define TXT_HUD_GAMEMESSAGES TXT(557) //"Game Messages" +#define TXT_SHUTTINGDOWN TXT(558) //"Shutting down!" +#define TXT_INVALIDTEAMCOUNT TXT(559)//Invalid team count +#define TXT_DS_OPENLEVEL TXT(560) //"Opening level '%s'...\n" +#define TXT_LEVELSELECT TXT(561) //"Level Select" +#define TXT_LEVELSELECTB TXT(562) //"Please select a level to start at (1 to %d)" +#define TXT_LEVELWARP TXT(563) //"Level Warp" +#define TXT_LEVELWARPB TXT(564) //"Please select a level to warp to (1 to %d)" +#define TXT_SELECT TXT(565) //"Select" +#define TXT_CHOOSELEVEL TXT(566) //"Please choose a level between 1 and %d" +#define TXT_MISSIONNOTFOUND TXT(567) //"Mission file not found" +#define TXT_DS_LEVELLOADSTATUS TXT(568) //"%s %.0f Percent Complete\n" +#define TXT_LL_ROOMSLOADED TXT(569) //"All rooms loaded" +#define TXT_LL_OBJECTSLOADED TXT(570) //"Level objects loaded" +#define TXT_LL_PAGINGDATA TXT(571) //"Paging data..." +#define TXT_DS_MISSIONDONE TXT(572) //"Mission over. Looping back to first level in mission file.\n" +#define TXT_LL_SCRIPTLOADED TXT(573) //"Level script loaded" +#define TXT_LL_TERRAIN TXT(574) //"Terrain loaded" +#define TXT_MD_DOWNLOADSTATUS TXT(575) //"Download Status" +#define TXT_MULTI_DATACORRUPT TXT(576) //"Network data corruption detected!" +#define TXT_MULTI_SERVERCHANGEA TXT(577) //"Server changed levels" +#define TXT_MULTI_SERVERCHANGEB TXT(578) //"Please try rejoining" +#define TXT_DS_ENDINGLEVEL TXT(579) //"Ending level.\n" +#define TXT_MULTI_CORRUPTOBJ TXT(580) //"Corrupted object received!\n" +#define TXT_NOTENOUGHSHIELDSFOROBS TXT(581) //"Not enough shields to set observer!" +#define TXT_MULTI_CONNECTING TXT(582) //"Connecting..." +#define TXT_CLOSE TXT(583) //"Close" +#define TXI_MSG_HAVEVAUSS 584 //"You already have the Vauss" +#define TXI_MSG_VAUSS 585 //"Vauss!" +#define TXI_MSG_NAPALMHAVE 586 //"You already have the Napalm Gun" +#define TXI_MSG_NAPALM 587 //"Napalm Gun!" +#define TXI_MSG_EMDALREADYHAVE 588 //"You already have the EMD Launcher" +#define TXI_MSG_EMD 589 //"EMD Launcher!" +#define TXI_MSG_MICROWAVEHAVE 590 //"You already have the Microwave Cannon" +#define TXI_MSG_MICROWAVE 591 //"Microwave Cannon!" +#define TXI_MSG_MASSALREADYHAVE 592 //"You already have the Mass Driver" +#define TXI_MSG_MASSDRIVER 593 //"Mass Driver!" +#define TXI_MSG_SUPERLHAVE 594 //"You already have the Super Laser" +#define TXI_MSG_SUPERLASER 595 //"Super Laser!" +#define TXI_MSG_PLASMAHAVE 596 //"You already have the Plasma Cannon" +#define TXI_MSG_PLASMA 597 //"Plasma Cannon!" +#define TXI_MSG_FUSIONHAVE 598 //"You already have the Fusion Cannon" +#define TXI_MSG_FUSION 599 //"Fusion Cannon!" +#define TXI_MSG_OMEGAHAVE 600 //"You already have the Omega Cannon" +#define TXI_MSG_OMEGA 601 //"Omega Cannon!" +#define TXI_MSG_FRAG 602 //"Frag Missile!" +#define TXI_MSG_FRAGFULL 603 //"Frag Missiles Full" +#define TXI_MSG_IMPACTM 604 //"Impact Mortar!" +#define TXI_MSG_IMPACTMFULL 605 //"Impact Mortars Full" +#define TXI_MSG_NAPALMR 606 //"Napalm Rocket!" +#define TXI_MSG_NAPALMRFULL 607 //"Napalm Rockets Full" +#define TXI_MSG_CYCLONE 608 //"Cyclone Missile!" +#define TXI_MSG_CYCLONEFULL 609 //"Cyclone Missiles Full" +#define TXI_MSG_BSHARK 610 //"Black Shark Missile!" +#define TXI_MSG_BSHARKFULL 611 //"Black Shark Missiles Full" +#define TXI_MSG_CONC 612 //"Concussion Missile!" +#define TXI_MSG_CONCFULL 613 //"Concussion Missiles Full" +#define TXI_MSG_HOMING 614 //"Homing Missile!" +#define TXI_MSG_HOMINGFULL 615 //"Homing Missiles Full" +#define TXI_MSG_SMART 616 //"Smart Missile!" +#define TXI_MSG_SMARTFULL 617 //"Smart Missiles Full" +#define TXI_MSG_MEGA 618 //"Mega Missile!" +#define TXI_MSG_MEGAFULL 619 //"Mega Missiles Full" +#define TXI_MSG_GUIDED 620 //"Guided Missile!" +#define TXI_MSG_GUIDEDFULL 621 //"Guided Missiles Full" +#define TXI_MSG_MULTI_HOMING 622 //"%d Homing Missiles!" +#define TXI_MSG_MULTI_CONC 623 //"%d Concussion Missiles!" +#define TXI_MSG_MULTI_FRAG 624 //"%d Frag Missiles!" +#define TXI_MSG_MULTI_GUIDED 625 //"%d Guided Missiles!" +#define TXI_MSG_VAUSSAMMO 626 //"%d Vauss rounds" +#define TXI_MSG_VAUSSFULL 627 //"Vauss Cannon full" +#define TXI_MSG_MASSAMMO 628 //"%d Mass Driver rounds" +#define TXI_MSG_MASSFULL 629 //"Mass Driver full" +#define TXI_MSG_NAPALMFUEL 630 //"%d.%d liters of Napalm fuel" +#define TXI_MSG_NAPALMFULL 631 //"Napalm Cannon full" +#define TXT_INVULNOFF TXT(632) //"Invulnerability Off" +#define TXT_CLOAKOFF TXT(633) //"Cloak off" +#define TXT_MSG_CLOAKOFF TXT(634) //"Cloak Off" +#define TXT_MAXSHIELDS TXT(635) //"Shields Full" +#define TXT_SHIELDBOOST TXT(636) //"Shields boosted to %d!" +#define TXT_MSG_ENERGY TXT(637) //"Energy!" +#define TXT_QUADLASER TXT(638) //"Quad Lasers!" +#define TXT_MSG_QUADHAVE TXT(639) //"You already have Quad Lasers" +#define TXT_INVULNON TXT(640) //"Invulnerability On" +#define TXT_INVULNALREADY TXT(641) //"You're already invulnerable" +#define TXT_CLOAKON TXT(642) //"Cloak On" +#define TXT_CLOAKALREADY TXT(643) //"You already are cloaked" +#define TXT_CHAFF TXT(644) //"Chaff" +#define TXT_MSG_MULTI_CHAFFS TXT(645) //"%d Chaffs" +#define TXT_COUNTERMEASUREFULL TXT(646) //"Counter Measures Full" +#define TXT_CHAFFSFULL TXT(647) //"You Can't Carry Any More Chaffs" +#define TXT_BETTY TXT(648) //"Bouncing Betty" +#define TXT_MSG_MULTI_BETTY TXT(649) //"%d Bouncing Bettys" +#define TXT_BETTYFULL TXT(650) //"You Can't Carry Any More Bettys" +#define TXT_SEEKERMINE TXT(651) //"Seeker Mine" +#define TXT_MSG_MULTI_SEEKERS TXT(652) //"%d Seeker Mines" +#define TXT_SEEKERFULL TXT(653) //"You Can't Carry Any More Seeker Mines" +#define TXT_GUNBOY TXT(654) //"GunBoy" +#define TXT_MSG_MULTI_GUNBOY TXT(655) //"%d GunBoys" +#define TXT_GUNBOYFULL TXT(656) //"You Can't Carry Any More GunBoys" +#define TXT_AFTERBURNERCOOLER TXT(657) //"Afterburner Cooler!" +#define TXT_ABCOOLERHAVE TXT(658) //"You Already Have An Afterburner Cooler" +#define TXT_ETOSCONVERTER TXT(659) //"Energy-Shields Converter!" +#define TXT_ETOSCONVHAVE TXT(660) //"You Already Have A Energy-Shields Converter" +#define TXT_FULLMAP TXT(661) //"Full Map!" +#define TXT_GETTINGFILES TXT(662) //"Getting Files..." +#define TXT_TIMEDEMO TXT(663) //"Timedemo reports %.2f FPS" +#define TXT_ADD TXT(664) //"Add" +#define TXT_EDIT TXT(665) //"Edit" +#define TXT_OLDPILOTNOEXIST TXT(666) //"Old Pilot No Longer Exists, Please Select A New One" +#define TXT_SELPILOTPIC TXT(667) //"CHOOSE PILOT PICTURE" +#define TXT_PILOTPICTITLE TXT(668) //"Select a pilot from" +#define TXT_COPYCONTB TXT(669) //"which to copy the" +#define TXT_COPYCONTOLSC TXT(670) //"control settings" +#define TXT_IMPORTGRAPHIC TXT(671) //"Import Graphic" +#define TXT_PLAYSOUND TXT(672) //"Play" +#define TXT_IMPORTSOUND TXT(673) //"Import Sound" +#define TXT_AUDIOTAUNTA TXT(674) //"Audio Taunt #1" +#define TXT_AUDIOTAUNTB TXT(675) //"Audio Taunt #2" +#define TXT_WARNING TXT(676) //"Warning" +#define TXT_SHIPSELECTERR TXT(677) //"The ship you have selected wasn't found, using default" +#define TXT_COMPRESSTOOBIG TXT(678) //"Compressed File Can't Be larger than %dK" +#define TXT_AUDIOIMPORTSUC TXT(679) //"Audio File Imported" +#define TXT_COPYTEMPERR TXT(680) //"Unable to copy temp file" +#define TXT_CANTCREATECRC TXT(681) //"Unable to create CRC File" +#define TXT_NOPICSAVAIL TXT(682) //"There are no available\npictures for this pilot" +#define TXT_ERRCREATINGDIALOG TXT(683) //"An Error Occured Creating Dialog" +#define TXT_PILOTPICTURE TXT(684) //"Pilot Picture" +#define TXT_PICTURES TXT(685) //"Pictures" +#define TXT_DISPLAY TXT(686) //"Display" +#define TXT_ENTEROBS TXT(687) //"Entering observer mode." +#define TXT_PLYRENTEROBS TXT(688) //"%s starts observing." +#define TXT_LEAVEOBS TXT(689) //"Leaving observer mode." +#define TXT_PLYRLEAVEOBS TXT(690) //"%s stops observing." +#define TXT_NEEDMOREENERGY TXT(691) //"Need more than %d energy to enable transfer" +#define TXT_SHIELDSATMAX TXT(692) //"No transfer: Shields already at max" +#define TXI_TCMM_BRIEFINGS 693 //"Briefings" +#define TXI_TCMM_CARGO 694 //"Cargo" +#define TXI_TCMM_AUTOMAP 695 //"AutoMap" +#define TXT_TCINITIALIZING TXT(696) //"Please wait... Initializing" +#define TXT_TCSHUTDOWN TXT(697) //"Telcom Shutting Down" +#define TXT_TCAM_TERRAIN TXT(698) //"F1 - Turn terrain %s" +#define TXT_TCAM_INSTRUCTIONS TXT(699) //"F2 - Center on player F3 - Realign to gravity" +#define TXT_MULTI_RANKS 700 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSA 701 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSB 702 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSC 703 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSD 704 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSE 705 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSF 706 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSG 707 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSH 708 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_MULTI_RANKSI 709 //" Note! This is not like the others!put in to keep parser happy" +#define TXT_HAS_BEEN TXT(710) // "has been" +#define TXT_PROMOTED TXT(711) // "promoted to" +#define TXT_DEMOTED TXT(712) // "demoted to" +#define TXT_PROXMINE TXT(713) //"Prox Mine" +#define TXT_PROXMINECOUNT TXT(714) //"%d Prox Mines" +#define TXT_PROXMINEFULL TXT(715) //"You Can't Carry Any More Prox Mines" +#define TXT_PLAY TXT(716) //"Play" (play game) +#define TXT_OPTCONFIG TXT(717) //"Config" (options menu) +#define TXT_TOGGLES TXT(718) //"Toggles" +#define TXT_GEOMETRY TXT(719) // "Geometry" +#define TXT_MONITOR TXT(720) // "Monitor options" +#define TXT_CONTAXIS TXT(721) // "%c-axis" +#define TXI_KB_WPNGRP 722 // "Turning" +#define TXI_KB_MISCGRP 723 // "Miscellaneous" +#define TXI_KB_THRUSTGRP 724 // "Thrust" +#define TXI_KB_TURNINGGRP 725 // "Turning" +#define TXI_KB_AUDIOTAUNT1 726 // "Audio Taunt 1" +#define TXI_KB_AUDIOTAUNT2 727 // "Audio Taunt 2" +#define TXI_KB_REARVIEW 728 // "Rear view" +#define TXT_HUDWARNINGS TXT(729) // "HUD Warnings" +#define TXT_MSNINFO TXT(730) // "Info" +#define TXT_MSNNAME TXT(731) // "Name: %s" +#define TXT_MSNAUTHOR TXT(732) // "Author: %s" +#define TXT_MSNNOTES TXT(733) // "Notes: %s" +#define TXI_SHFTF9 734 // "Shift-F9" +#define TXI_DISPLAYHUDMSGCONSOLE 735 // "Displays HUD Message Console" +#define TXI_SHFTF8 736 // "Shift-F8" +#define TXI_DISPLAYGAMEMSGCONSOLE 737 // "Displays Game Message Console" +#define TXI_F12 738 // "F12" +#define TXI_DROPSMARKER 739 // "Drops a Marker" +#define TXT_SAVEGAMEHELP TXT(740) // "Choose a slot to save game." +#define TXT_LOADGAMEHELP TXT(741) // "Choose a slot to load a saved game." +#define TXT_SAVEGAMEDUP TXT(742) // "There is already a saved game of that name. Please choose another." +#define TXT_OVERWRITESAVE TXT(743) // "Overwrite current saved game?" +#define TXT_CONTROL_TOGGLES TXT(744) // "Input devices" +#define TXI_SHFTTAB 745 // "Shift-Tab" +#define TXI_TCMM 746 // "TelCom Main Menu" +#define TXT_PLTCFILEERR TXT(747) // "There was a file write error trying to create the Pilot file." +#define TXT_PLTUKNOWNERR TXT(748) // "There was an internal error trying to create the Pilot file." +#define TXI_KB_AUDIOTAUNT3 749 // "Audio Taunt 3" +#define TXI_KB_AUDIOTAUNT4 750 // "Audio Taunt 4" +#define TXT_TEAMCOUNTPROMPT TXT(751) //"Please enter the number of teams for" +#define TXT_DS_CANTUSEINIT TXT(752) //"This value can't be set during server initialization\n" +#define TXT_DS_CANTUSEPLAY TXT(753) //"This value can't be set during game play\n" +#define TXT_INVALIDNUMTEAMS TXT(754) //"Invalid Number of Teams Set, defaulting to %d\n" +#define TXT_MAINMISSION TXT(755) //"Descent 3: Retribution" +#define TXT_RANDOMPOWERUPRESPAWN TXT(756) //"Randomize powerup respawn" +#define TXT_CONTROLSCONFIG TXT(757) //"CONTROLS CONFIGURATION" +#define TXT_MULTIPLAYERCONFIG TXT(758) //"MULTIPLAYER CONFIGURATION" +#define TXT_MISCELLANEOUS TXT(759) //"MISCELLANEOUS" +#define TXT_PROFFILTER TXT(760) //"FILTER PROFANITY" +#define TXT_NOPILOTSTOCOPY TXT(761) //"There are no pilots to copy from" +#define TXT_MAJORPLTERROR TXT(762) //"An error has occurred reading your pilot file, please create a new one" +#define TXT_PLTTOOOLD TXT(763) //"Your pilot file is too old, delete and create a new one" +#define TXT_PLTTOONEW TXT(764) //"Pilot File '%s' is not compatible with this version." +#define TXT_FAILED TXT(765) //"Failed" +#define TXT_SCORE TXT(766) //"Score" +#define TXT_PLR_MSN_SUCCESS TXT(767) //Mission Successfull +#define TXT_PLR_MSN_FAILED TXT(768) //Mission Failed +#define TXT_VIEW_GUIDEBOT TXT(769) //"Guide-Bot" +#define TXT_VIEW_MARKER TXT(770) //"Marker" +#define TXT_VIEW_REAR TXT(771) //"Rear" +#define TXT_PREPARE_FOR_DESCENT TXT(772) //Prepare for Descent... +#define TXT_ALREADY_HAVE_WEAPON TXT(773) //Prepare for Descent... +#define TXT_CTLBINDHELP1 TXT(774) //Press key to bind control +#define TXT_CTLBINDHELP2_0 TXT(775) //Press a button on your mouse +#define TXT_CTLBINDHELP2_1 TXT(776) //or joystick (including hat). +#define TXT_CTLBINDHELP3_0 TXT(777) //Move your mouse or joystick +#define TXT_CTLBINDHELP3_1 TXT(778) //in the desirec direction. +#define TXT_HUDMSGPOPUP_TITLE TXT(779) //HUD Messages +#define TXT_MESSAGEBOXTITLE TXT(780) //Message +#define TXT_MSN_OPENMN3FAILED TXT(781) //Failed to open MN3 file. +#define TXT_MSN_MSNCOMMAND TXT(782) //Command specified is a mission command. +#define TXT_MSN_LVLCOMMAND TXT(783) //Command specified is not a level command. +#define TXT_MSN_NUMLVLSINVALID TXT(784) //NUMLEVELS doesn't match number of level descript. +#define TXT_MSN_LVLNUMINVALID TXT(785) //Level number is out of range. +#define TXT_MSN_ILLEGALCMD TXT(786) //Illegal command %s +#define TXT_MSN_FAILEDCREATEDIR TXT(787) //Failed to create missions directory. +#define TXT_CDPROMPT TXT(788) //Please insert Descent 3 CD %d +#define TXT_CALIBJOYSTICKFAIL TXT(789) //calibrate joystick failed. +#define TXT_CALIBJOYSTICK TXT(790) //calibrate joystick. +#define TXT_MOUSECONTROL TXT(791) //Mouse Control +#define TXT_MOUSEFLIGHTSIM TXT(792) //Flight Sim +#define TXT_MOUSELOOK TXT(793) //Mouselook +#define TXT_ALLOWMLOOK TXT(794) //Allow Mouselookers +#define TXT_NOMOUSELOOK TXT(795) //Mouselook not allowed +#define TXT_MLOOK_JOINANYWAY TXT(796) //This game does not allow mouselook. Do you want to join anyway? +#define TXT_TRAININGABORTTITLE TXT(797) //Abort Training System +#define TXT_TRAININGABORTTEXT TXT(798) //Do you want to Skip to the first level, or abort the Mission? +#define TXT_SKIP TXT(799) //Skip +#define TXT_ABORT TXT(800) //Abort +#define TXT_REVERTCONTROLS TXT(801) //Restore settings +#define TXT_QUICKSAVE TXT(802) //Game saved +#define TXT_HITAKEYORBUTTON TXT(803) //"Hit a button or the spacebar to continue" +#define TXT_ENABLED_KEY TXT(804) //"Enabled Key: %s" +#define TXT_ENABLED_CONTROL TXT(805) //"Enabled Control: %s" +#define TXT_PLRFRIENDSKILL TXT(806) //"Friendlies killed:" +#define TXT_MSNNOTCOMPATIBLE TXT(807) //"This mission is not compatible with the selected multiplayer mode." +#define TXT_MULTISELECTSHIP TXT(808) //"Multiplayer Select Ship" +#define TXT_ABCOOLER TXT(809) //"AB Cooler" +#define TXT_RETURNEDAUTOMAP TXT(810) //"Your automap has been retrieved" +#define TXT_RETURNEDHEADLIGHT TXT(811) //"Your headlight has been retrieved" +#define TXT_HAVEHEADLIGHT TXT(812) //"You already have a headlight" +#define TXT_DONTHAVEHEADLIGHT TXT(813) //"You don't have any headlight" +#define TXT_TOG_GUIDEDMISSILE TXT(814) //"Guided missile view" +#define TXT_TOG_SHOWRETICLE TXT(815) //"Show reticle" +#define TXT_TOG_SHIPSOUNDS TXT(816) //"Ship sounds" +#define TXT_SNDCFG_SFXQUANTITY TXT(817) //"SFX Quantity" +#define TXT_DEDICATED_SERVER TXT(818) //"Dedicated server: %s" +#define TXT_NONDEDICATED_SERVER TXT(819) //"Nondedicated server: %s" +#define TXT_GINFO_ALLOWMOUSELOOK TXT(820) //"%s allow Mouselook." +#define TXT_DOES TXT(821) //"Does" +#define TXT_DOESNOT TXT(822) //"Does not" +#define TXT_GINFO_ACC_WEAPCOLL TXT(823) //"Accurate weapon collisions: %s" +#define TXT_USE_ROTATIONAL_VEL TXT(824) //"Use rotational velocity: %s" +#define TXT_RANDOMIZEPOWERUPSPAWN TXT(825) //"Randomize powerup respawn: %s" +#define TXT_PLAYERS_X_OF_X TXT(826) //"Players: %d of %d" +#define TXT_GINFO_MISSION TXT(827) //"Mission: %s" +#define TXT_GAME_INFO TXT(828) //"Game information" +#define TXT_PLTFILETOONEW TXT(829) //"The selected pilot (%s) is too new, please delete and create a new one" +#define TXT_TELCOMLOAD TXT(830) //"Accessing TelCom...Please wait" +#define TXT_CANTGETPLAYERLIST TXT(831) //"Couldn't get player list from server!" +#define TXT_GAMEINFOPLAYERSTITLE TXT(832) //"Players" +#define TXT_GETTINGPLAYERLIST TXT(833) //"Getting Player list..." +#define TXT_FOUND_SECRET TXT(834) // "You have found a secret area..." +#define TXT_GAMERESTORED TXT(835) // "Game restored." +#define TXT_AUDIOTAUNTS TXT(836) // "Audio Taunts" +#define TXT_GUIDEBOT TXT(837) // "Guidebot" +#define TXT_ACCESSGUIDEBOT TXT(838) // "Please wait, accessing Guidebot" +#define TXT_ESCTOCANCEL TXT(839) // "Press ESC to Cancel" +#define TXT_QUITMESSAGE TXT(840) // "Are you sure you want to exit?" +#define TXT_CFGSCREENHELP TXT(841) // "Use CTRL-C to clear currently highlighted slot." +#define TXT_KEYCFGSCREENHELP1 TXT(842) // "Press '?' for help" +#define TXT_JOYCFGSCREENHELP1 TXT(843) // "Press '?' for help, or to invert axis" +#define TXT_KEYRAMPINGTIME TXT(844) // "Key Ramping Time (s)" +#define TXT_ADJUSTCONTROLSETTINGS TXT(845) // "Controller settings" +#define TXT_JOYSENSBTN TXT(846) // "Sensitivity" +#define TXT_KEYRAMPING TXT(847) // "Keyboard Ramping" +#define TXT_WPNCFGHELP2 TXT(848) // "Select weapon slot to place \1\140\140\1%s" +#define TXT_ENTERSECRETLVL TXT(849) // "Entering Secret Level" +#define TXI_SS_PY_SPEED 850//"" +#define TXI_SS_PY_MANEUVERABILITY 851//"" +#define TXI_SS_PY_SHIELDS 852//"" +#define TXI_SS_PY_ORDANCE 853//"" +#define TXI_SS_PY_WINGSPAN 854//"" +#define TXI_SS_PY_LENGTH 855//"" +#define TXI_SS_PY_HEIGHT 856//"" +#define TXI_SS_PY_WEIGHT 857//"" +#define TXI_SS_PH_SPEED 858//"" +#define TXI_SS_PH_MANEUVERABILITY 859//"" +#define TXI_SS_PH_SHIELDS 860//"" +#define TXI_SS_PH_ORDANCE 861//"" +#define TXI_SS_PH_WINGSPAN 862//"" +#define TXI_SS_PH_LENGTH 863//"" +#define TXI_SS_PH_HEIGHT 864//"" +#define TXI_SS_PH_WEIGHT 865//"" +#define TXI_SS_M_SPEED 866//"" +#define TXI_SS_M_MANEUVERABILITY 867//"" +#define TXI_SS_M_SHIELDS 868//"" +#define TXI_SS_M_ORDANCE 869//"" +#define TXI_SS_M_WINGSPAN 870//"" +#define TXI_SS_M_LENGTH 871//"" +#define TXI_SS_M_HEIGHT 872//"" +#define TXI_SS_M_WEIGHT 873//"" +#define TXT_ENABLED_CONTROLS TXT(874) //"Enabled Controls:" +#define TXT_ENABLED_CONT_FORWARD TXT(875) //"Forward" +#define TXT_ENABLED_CONT_REVERSE TXT(876) //"Reverse" +#define TXT_ENABLED_CONT_SLIDELEFT TXT(877) //"Slide Left" +#define TXT_ENABLED_CONT_SLIDERIGHT TXT(878) //"Slide Right" +#define TXT_ENABLED_CONT_SLIDEUP TXT(879) //"Slide Up" +#define TXT_ENABLED_CONT_SLIDEDOWN TXT(880) //"Slide Down" +#define TXT_ENABLED_CONT_PITCHUP TXT(881) //"Pitch Up" +#define TXT_ENABLED_CONT_PITCHDOWN TXT(882) //"Pitch Down" +#define TXT_ENABLED_CONT_HEADLEFT TXT(883) //"Heading Left" +#define TXT_ENABLED_CONT_HEADRIGHT TXT(884) //"Heading Right" +#define TXT_ENABLED_CONT_BANKLEFT TXT(885) //"Bank Left" +#define TXT_ENABLED_CONT_BANKRIGHT TXT(886) //"Bank Right" +#define TXT_ENABLED_CONT_PRIMWEAP TXT(887) //"Primary Weapon" +#define TXT_ENABLED_CONT_SECWEAP TXT(888) //"Secondary Weapon" +#define TXT_ENABLED_CONT_AFTERBURN TXT(889) //"Afterburner" +#define TXT_RELIABLE_OVERRUN TXT(890) //"Reliable buffer overrun" +#define TXI_HLP_QUIKSAVE 891 // "Quick Save Game" +#define TXI_F9 892 //"F9" +#define TXI_HLP_MULTIMSG 893 // "Send message in multiplayer" +#define TXI_F8 894 // "F8" +#ifdef MACINTOSH +#define TXT_TEXTURE_QUALITY TXT(895) // "Texture Quality" +#define TXT_JOYSTICK_DEADZONE TXT(896) // "Joystick Deadzone" +#define TXT_SPROCKETS_CONFIG TXT(897) // "Sprockets Config" +#define TXI_PAGE_DOWN 898 // "Page Down" +#endif + +//Before adding items search for unused entries! +#endif + diff --git a/Descent3/subtitles.cpp b/Descent3/subtitles.cpp new file mode 100644 index 000000000..18d41a4f1 --- /dev/null +++ b/Descent3/subtitles.cpp @@ -0,0 +1,335 @@ +/* +* $Logfile: /DescentIII/main/subtitles.cpp $ +* $Revision: 7 $ +* $Date: 5/06/99 1:20a $ +* $Author: Samir $ +* +* Subtitle system for cinematics +* +* $Log: /DescentIII/main/subtitles.cpp $ + * + * 7 5/06/99 1:20a Samir + * use mve_ calls for subtitles. + * + * 6 4/20/99 12:48a Matt + * Made subtitles use briefing font instead of HUD font, even though I + * know the subtitles don't actually work. + * + * 5 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 4 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 3 8/29/98 6:53p Jeff + * only do subtitles if -subtitles is given on command line + * + * 2 8/28/98 4:20p Jeff + * got subtitles completly working +* +* $NoKeywords: $ +*/ + + + +#include "subtitles.h" +//#include "gamefont.h" +//#include "grtext.h" +//#include "renderer.h" +#include "pserror.h" +#include "ddio.h" +//#include "ddvid.h" +#include "manage.h" +#include "application.h" +#include "mem.h" +#include "movie.h" +#include "args.h" +#include +#include +#include + +// Subtitle data +typedef struct { + int first_frame,last_frame; + char *msg; +} subtitle; + +#define MAX_SUBTITLES 500 +#define MOVIE_SUBTITLE_EXTENSION ".msb" +#define MESSAGE_LENGTH 300 + +static subtitle Subtitles[MAX_SUBTITLES]; +int Num_subtitles; +int Movie_subtitle_init; + +//Prints out a subtitle on the screen, centered, at y pos y, with color color. +void PrintSubTitle(int y,ddgr_color color,char *msg); +//Parses a subtitle file +void SubtParseSubtitles(CFILE *file); +//clears the area where subtitle text is display (call once a frame) +void ClearSubTitleArea(void); +//Resets the subtitle information +void SubtResetSubTitles(void); + + +void readline(CFILE *file,char *buf,int maxsize) +{ + ASSERT(file); + ASSERT(buf); + buf[0] = '\0'; + bool done = false; + while (!done){ + if(cfeof(file)){ + done = true; + buf[0] = '\0'; + } + if(cf_ReadString(buf,maxsize,file)) + done = true; + } +} + +char *parse_whitespace(char *p) +{ + ASSERT(p); + while (isspace(*p)) + p++; + return p; +} + +char *parse_int(char *buf,int *d) +{ + ASSERT(buf); + ASSERT(d); + char *t; + bool got_digit = 0; + t = buf = parse_whitespace(buf); + + //Check for negative sign + if (*t == '-') + t++; + + //Check for valid characters in number + while (isdigit(*t)) { + t++; + got_digit = 1; + } + + //Make sure at least one digit + if (! got_digit){ + *d = 0; + return NULL; + } + + //Check for valid seperator, and convert + if ((*t == 0) || isspace(*t) || (*t == ',')) { //valid number + *d = atoi(buf); + return t; + } + else { + return NULL; + } +} + +char *parse_token(char *p,char *token) +{ + ASSERT(p); + ASSERT(token); + + int len = strlen(token); + if(!strnicmp(p,token,len)){ + return p+len; + } + + return NULL; +} + +#define PARSE_NEXT_LINE() do{readline(file,buf,MESSAGE_LENGTH); p = buf;}while(0) + +//Parses a subtitle file +void SubtParseSubtitles(CFILE *file) +{ + SubtResetSubTitles(); + + int first_frame, last_frame; + char buf[MESSAGE_LENGTH]; + char *p = buf; + + PARSE_NEXT_LINE(); + + while (!parse_token(buf,"#End")) { + ASSERT( Num_subtitles < MAX_SUBTITLES ); + + // starting frame number + p = parse_int(p,&first_frame); + if(!p){ + mprintf((0,"Couldn't parse first_frame\n")); + goto subt_parse_error; + } + + p = parse_int(p,&last_frame); + if(!p){ + mprintf((0,"Couldn't parse last_frame\n")); + goto subt_parse_error; + } + + p = parse_whitespace(p); + + Subtitles[Num_subtitles].first_frame = first_frame; + Subtitles[Num_subtitles].last_frame = last_frame; + Subtitles[Num_subtitles].msg = (char *)mem_malloc(strlen(p)+1); + if(!Subtitles[Num_subtitles].msg) + goto subt_parse_error; + strcpy(Subtitles[Num_subtitles].msg, p ); // be sure to free this later + + Num_subtitles++; + + PARSE_NEXT_LINE(); + } + return; +subt_parse_error: + mprintf((0,"Error Parsing SubTitle File!\n")); + SubtResetSubTitles(); +} + +//Resets the subtitle information +void SubtResetSubTitles(void) +{ + for(int i=0;i> 2); + Movie_subtitle_init = 0; + } + + //get rid of any subtitles that have expired + for (t=0;t Subtitles[active_subtitles[t]].last_frame) { + int t2; + for (t2=t;t2= Subtitles[next_subtitle].first_frame) ) { + if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES) + Error("Too many active subtitles!"); + active_subtitles[num_active_subtitles++] = next_subtitle; + next_subtitle++; + } + + ClearSubTitleArea(); + + //find y coordinate for first line of subtitles + y = 480-((line_spacing+1)*MAX_ACTIVE_SUBTITLES+2); + int first,last; + + for(t=0;t=frame_num))?GR_RGB(255,255,255):GR_RGB(128,128,128), + Subtitles[active_subtitles[t]].msg); + y += line_spacing+1; + } + } +} + +//Prints out a subtitle on the screen, centered, at y pos y, with color color. +void PrintSubTitle(int y,ddgr_color color,char *msg) +{ + mve_Puts(-1,y, color, msg); +} + +//clears the area where subtitle text is display (call once a frame) +void ClearSubTitleArea(void) +{ + int fh = 12; + int line_spacing = fh + (fh >> 2); + int y = 475-((line_spacing+1)*MAX_ACTIVE_SUBTITLES+2); + + mve_ClearRect(0,y,639,479); +} \ No newline at end of file diff --git a/Descent3/subtitles.h b/Descent3/subtitles.h new file mode 100644 index 000000000..96a6cdc33 --- /dev/null +++ b/Descent3/subtitles.h @@ -0,0 +1,29 @@ +/* +* $Logfile: /DescentIII/main/subtitles.h $ +* $Revision: 2 $ +* $Date: 8/28/98 4:20p $ +* $Author: Jeff $ +* +* Subtitle system for cinematics +* +* $Log: /DescentIII/main/subtitles.h $ + * + * 2 8/28/98 4:20p Jeff + * got subtitles completly working +* +* $NoKeywords: $ +*/ + + + +#ifndef __SUBTITLES_H_ +#define __SUBTITLES_H_ + +//draw the subtitles for this frame +void SubtDrawSubtitles(int frame_num); +//Shutsdown the subtitle system +void SubtCloseSubtitles(); +//Initializes the subtitles for a given movie file +void SubtInitSubtitles(const char *filename); + +#endif \ No newline at end of file diff --git a/Descent3/terrain.cpp b/Descent3/terrain.cpp new file mode 100644 index 000000000..9dc055c9b --- /dev/null +++ b/Descent3/terrain.cpp @@ -0,0 +1,1331 @@ +#ifdef NEWEDITOR + #include "neweditor\globals.h" +#else + #include +#endif +#include "vecmat.h" +#include "object.h" +#include "mono.h" +#include "terrain.h" +#include "pserror.h" +#include "texture.h" +#include "bitmap.h" +#include "gametexture.h" +#include "lighting.h" +#include "lightmap.h" +#include "weather.h" +#include +#include "mem.h" +#include "Macros.h" +#include "dedicated_server.h" +#include "psrand.h" +#ifdef EDITOR + #include "editor\d3edit.h" +#endif + +#define SKY_RADIUS 2500.0 +#define DEFAULT_LIGHT_SOURCE {0,TERRAIN_SIZE*100,0} + +// How far we can see (in world coordinates) +float VisibleTerrainZ; + +terrain_segment Terrain_seg[TERRAIN_WIDTH*TERRAIN_DEPTH]; +terrain_tex_segment Terrain_tex_seg[TERRAIN_TEX_WIDTH*TERRAIN_TEX_DEPTH]; +terrain_sky Terrain_sky; + +#if (!defined(RELEASE) || defined(NEWEDITOR)) +// first object to render after cell has been rendered (only used for SW renderer) +short Terrain_seg_render_objs[TERRAIN_WIDTH*TERRAIN_DEPTH]; +#endif + +// Our lighting maps for the terrain, one for each quadrant (starting at lower left) +int TerrainLightmaps[4]; + +// A list of terrain to render +terrain_render_info Terrain_list[MAX_CELLS_TO_RENDER]; + +ushort *Terrain_rotate_list; // which points have been sub/rotated this frame +g3Point *World_point_buffer; // Rotated points + +// The min/max values for a particular region of terrain +ubyte *Terrain_min_height_int[7]; +ubyte *Terrain_max_height_int[7]; +// Texture values for a particular region + +// Terrain dynamic lighting table +ubyte Terrain_dynamic_table[TERRAIN_WIDTH*TERRAIN_DEPTH]; + +// Terrain normals depending on LOD +terrain_normals *TerrainNormals[MAX_TERRAIN_LOD]; + +// Max deltas per terrain lod block +float *TerrainDeltaBlocks[MAX_TERRAIN_LOD]; + +// Tracks edges of LOD +ubyte TerrainJoinMap[TERRAIN_WIDTH*TERRAIN_DEPTH]; + +// Terrain Y values +float Terrain_y_values[256]; + +#if (defined(EDITOR) || defined(NEWEDITOR)) + ubyte TerrainSelected[TERRAIN_WIDTH*TERRAIN_DEPTH]; + int Num_terrain_selected=0; + int Editor_LOD_engine_off=1; + bool Terrain_render_ext_room_objs = true; +#endif + +int TSearch_on=0,TSearch_found_type,TSearch_x,TSearch_y,TSearch_seg,TSearch_face; + +int Terrain_LOD_engine_off=0; + +int TerrainEdgeTest[MAX_TERRAIN_LOD][16]; +ubyte TerrainEdgeJump[MAX_TERRAIN_LOD]; + +// Unique terrain geometry identifier +int Terrain_checksum=-1; + +// Occlusion data for knowing what to draw +ubyte Terrain_occlusion_map[256][32]; +int Terrain_occlusion_checksum=-2; + +// returns the index of the highest float +int GetHighestDelta (float *deltas,int count) +{ + int high_index=-999; + float high_delta=-99999; + int i; + + for (i=0;i(high_delta)) + { + high_index=i; + high_delta=deltas[i]; + } + } + + ASSERT (high_indexmaxdelta) + maxdelta=maxdelta2; + } + } + + // Now check if there is anything special about this level of detail that + // excludes it from being used in the engine + if (lod==MAX_TERRAIN_LOD-2) + { + int total_counted=0,total_invis=0; + + for (i=y1;ihigh_delta) + { + high_delta=fabs(slopes[t]-slopes[i]); + } + } + } + + return high_delta; +} + +void GenerateSingleLODDelta (int sx,int sz) +{ + int w,h,i,z,x; + int simplemul,rowsize; + int chunk_size=1<<(MAX_TERRAIN_LOD-1); + + sx*=chunk_size; + sz*=chunk_size; + + int save_x=sx; + int save_z=sz; + + //Starts from lower-left, going clockwise + // 0 is lowest_level_detail (blunt) + for (i=0;i>((MAX_TERRAIN_LOD-1)-i); + h=chunk_size>>((MAX_TERRAIN_LOD-1)-i); + + simplemul=1<<((MAX_TERRAIN_LOD-1)-i); + rowsize=TERRAIN_WIDTH/simplemul; + + sx=save_x; + sz=save_z; + + sx/=simplemul; + sz/=simplemul; + + + for (z=sz;z>((MAX_TERRAIN_LOD-1)-i); + h=TERRAIN_DEPTH>>((MAX_TERRAIN_LOD-1)-i); + + simplemul=1<<((MAX_TERRAIN_LOD-1)-i); + rowsize=TERRAIN_WIDTH/simplemul; + + for (z=0;z>i; + h=TERRAIN_WIDTH>>i; + + start=((yoffset*(TERRAIN_WIDTH>>i))*TERRAIN_WIDTH); + start+=(xoffset*(TERRAIN_WIDTH>>i)); + + minheight=999; + maxheight=0; + + if (hmaxheight) + maxheight=cellheight; + + } + } + + if (minheight<0) + minheight=0; + if (maxheight>255) + maxheight=255; + + Terrain_min_height_int[i][yspeed_offset+xoffset]=(minheight); + Terrain_max_height_int[i][yspeed_offset+xoffset]=(maxheight); + + } + } + } +} + + +// Changes the terrain mesh for deformation effect +void DeformTerrainPoint (int x,int z,int change_height) +{ + terrain_segment *tseg=&Terrain_seg[z*TERRAIN_WIDTH+x]; + int i; + + change_height+=tseg->ypos; + + change_height=min(255,change_height); + change_height=max(0,change_height); + + tseg->ypos=change_height; + tseg->y=tseg->ypos*TERRAIN_HEIGHT_INCREMENT; + + int sx=max (0,x-1); + int sz=max (0,z-1); + + // Update min/max + for (i=0;i<7;i++) + { + int row_width=1<>i; + + for (int t=sz;t<=z;t++) + { + for (int k=sx;k<=x;k++) + { + + int offset=((t/div)*row_width)+(k/div); + + if (tseg->ypos>Terrain_max_height_int[i][offset]) + Terrain_max_height_int[i][offset]=tseg->ypos; + if (tseg->yposypos; + + } + } + } + + // Update normals + + for (i=sz;i<=z;i++) + { + for (int t=sx;t<=x;t++) + { + vector a,b,c; + terrain_segment *tseg0=&Terrain_seg[i*TERRAIN_WIDTH+t]; + terrain_segment *tseg1=&Terrain_seg[(i+1)*TERRAIN_WIDTH+t]; + terrain_segment *tseg2=&Terrain_seg[((i+1)*TERRAIN_WIDTH)+t+1]; + terrain_segment *tseg3=&Terrain_seg[(i*TERRAIN_WIDTH)+t+1]; + + // Do upper left triangle + a.x=t*TERRAIN_SIZE; + a.y=tseg0->y; + a.z=i*TERRAIN_SIZE; + + b.x=t*TERRAIN_SIZE; + b.y=tseg1->y; + b.z=(i+1)*TERRAIN_SIZE; + + c.x=(t+1)*TERRAIN_SIZE; + c.y=tseg2->y; + c.z=(i+1)*TERRAIN_SIZE; + + vm_GetNormal(&TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+t].normal1,&a,&b,&c); + + // Now do lower right triangle + a.x=t*TERRAIN_SIZE; + a.y=tseg0->y; + a.z=i*TERRAIN_SIZE; + + b.x=(t+1)*TERRAIN_SIZE; + b.y=tseg2->y; + b.z=(i+1)*TERRAIN_SIZE; + + c.x=(t+1)*TERRAIN_SIZE; + c.y=tseg3->y; + c.z=(i)*TERRAIN_SIZE; + + vm_GetNormal(&TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+t].normal2,&a,&b,&c); + } + } + + +} + +void UpdateSingleTerrainLightmap (int which); +void DeformTerrain (vector *pos,int depth,float size) +{ + int startx,startz,endx,endz; + int changed[4]={0,0,0,0}; + vector local_pos=*pos; + + startx=(pos->x/TERRAIN_SIZE)-(size/TERRAIN_SIZE); + startz=(pos->z/TERRAIN_SIZE)-(size/TERRAIN_SIZE); + endx=(pos->x/TERRAIN_SIZE)+(size/TERRAIN_SIZE); + endz=(pos->z/TERRAIN_SIZE)+(size/TERRAIN_SIZE); + + startx=max(0,startx); + startz=max(0,startz); + endx=min(TERRAIN_WIDTH-1,endx); + endz=min(TERRAIN_DEPTH-1,endz); + + int i,t; + + vector cur_vec; + vector up_vec={0,1,0}; + + local_pos.y=0; + + cur_vec.x=startx*TERRAIN_SIZE; + cur_vec.y=0; + cur_vec.z=startz*TERRAIN_SIZE; + + float max_dist=vm_VectorDistanceQuick (&local_pos,&cur_vec); + + for (i=startz;i<=endz;i++,cur_vec.z+=TERRAIN_SIZE) + { + cur_vec.x=startx*TERRAIN_SIZE; + for (t=startx;t<=endx;t++,cur_vec.x+=TERRAIN_SIZE) + { + terrain_segment *tseg=&Terrain_seg[i*TERRAIN_WIDTH+t]; + + if ((up_vec * TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+t].normal1)<.5) + continue; // not flat enough + if ((up_vec * TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+t].normal2)<.5) + continue; // not flat enough + + float dist=1.0-(vm_VectorDistanceQuick (&local_pos,&cur_vec)/max_dist); + + int height_change=-(dist*(float)depth); + int light_change=height_change*2; + + DeformTerrainPoint (t,i,height_change); + + + tseg->r=max(0,tseg->r+light_change); + tseg->g=max(0,tseg->g+light_change); + tseg->b=max(0,tseg->b+light_change); + + int which=((i/128)*2)+(t/128); + changed[which]=1; + } + } + + int div=(1<<(MAX_TERRAIN_LOD-1)); + + startx/=div; + startz/=div; + endx/=div; + endz/=div; + + GenerateSingleLODDelta (startx,startz); + GenerateSingleLODDelta (endx,startz); + GenerateSingleLODDelta (endx,endz); + GenerateSingleLODDelta (startx,endz); + + for (i=0;i<4;i++) + { + if (changed[i]) + UpdateSingleTerrainLightmap(i); + } + + +} + + + + +// Builds normals for the currently loaded terrain +void BuildTerrainNormals () +{ + int i,t,l,z,x; + vector a,b,c; + int simplemul; + vector up_norm={0,1.0,0}; + + // Set all to be initially up + for (i=0;iy; + a.z=i*TERRAIN_SIZE; + + b.x=t*TERRAIN_SIZE; + b.y=tseg1->y; + b.z=(i+simplemul)*TERRAIN_SIZE; + + c.x=(t+simplemul)*TERRAIN_SIZE; + c.y=tseg2->y; + c.z=(i+simplemul)*TERRAIN_SIZE; + + vm_GetNormal(&TerrainNormals[l][z*(TERRAIN_WIDTH/simplemul)+x].normal1,&a,&b,&c); + + // Now do lower right triangle + a.x=t*TERRAIN_SIZE; + a.y=tseg0->y; + a.z=i*TERRAIN_SIZE; + + b.x=(t+simplemul)*TERRAIN_SIZE; + b.y=tseg2->y; + b.z=(i+simplemul)*TERRAIN_SIZE; + + c.x=(t+simplemul)*TERRAIN_SIZE; + c.y=tseg3->y; + c.z=(i)*TERRAIN_SIZE; + + vm_GetNormal(&TerrainNormals[l][z*(TERRAIN_WIDTH/simplemul)+x].normal2,&a,&b,&c); + } + } + } + +} + + +void GenerateTerrainLight () +{ + // Generates a lighting value for every terrain cell + int i; + + GenerateLightSource(); + + vector camera_light=Terrain_sky.lightsource; + + vm_NormalizeVector (&camera_light); + + for (i=0;iy-=MAX_TERRAIN_HEIGHT; + vec->y+=(rad_diff/4); + } + } + + // Now figure out texture UVS + for (i=0;i<5;i++) + { + float scalar=(float)i/4.0; + int angle_increment=65536/MAX_HORIZON_PIECES; + for (t=0;thighlimit && p<6000) + p=ps_rand()%(65336/4); + + if (p<6000) + highcount++; + + vm_AnglesToMatrix (&tempm,(top+p)%65336,(ps_rand()*ps_rand())%65536,0); + vm_ScaleVector (&starvec,&tempm.fvec,Terrain_sky.radius*500); + Terrain_sky.star_vectors[i]=starvec; + + + // Now figure out the color of this star. The closer to horizon it is, the + // dimmer it is + float ynorm=starvec.y/(Terrain_sky.radius*500); + + float color_norm=ynorm*2; + color_norm=min(1.0,color_norm); + color_norm=max(.2,color_norm); + int color=ps_rand()%6; + int r,g,b; + + if (color<=2) + { + r=255; + g=255; + b=255; + } + else if (color==3) + { + r=255; + g=200; + b=200; + } + else if (color==4) + { + r=255; + g=200; + b=255; + } + else + { + r=255; + g=255; + b=200; + } + + r=((1.0-color_norm)*horizon_r)+(color_norm*r); + g=((1.0-color_norm)*horizon_g)+(color_norm*g); + b=((1.0-color_norm)*horizon_b)+(color_norm*b); + + + Terrain_sky.star_color[i]=GR_RGB(r,g,b); + } + + for (i=0;i=192) + { + unsigned char tb=0; + tb=cf_ReadByte (infile); + for (i=0;i<(buf-192);i++,run++) + lando[run]=tb; + } + else + { + lando[run]=buf; + run++; + } + } + + cfclose (infile); + + for (i=0;i>3)*TERRAIN_TEX_WIDTH)+(t>>3); + + Terrain_dynamic_table[i*TERRAIN_WIDTH+t]=0xFF; + } + } + + for (i=0;i>((MAX_TERRAIN_LOD-1)-i); + int h=TERRAIN_DEPTH>>((MAX_TERRAIN_LOD-1)-i); + + memset (TerrainDeltaBlocks[i],0,w*h*sizeof(float)); + } + } + + } + + BuildTerrainNormals(); + + for (i=0;i>((MAX_TERRAIN_LOD-1)-i); + h=TERRAIN_DEPTH>>((MAX_TERRAIN_LOD-1)-i); + + TerrainDeltaBlocks[i]=(float *)mem_malloc (w*h*sizeof(float)); + } + } + + // Allocate space for lod normals + + for (i=MAX_TERRAIN_LOD-1;i>((MAX_TERRAIN_LOD-1)-i); + h=TERRAIN_DEPTH>>((MAX_TERRAIN_LOD-1)-i); + + TerrainNormals[i]=(terrain_normals *)mem_malloc (w*h*sizeof(terrain_normals)); + memset (TerrainNormals[i],0,w*h*sizeof(terrain_normals)); + } + + // Allocate space for our min/max tables + for (i=0;i<7;i++) + { + w=1<r,tp->g,tp->b); + ushort *data=lm_data (TerrainLightmaps[which]); + + int x=t%128; + int y=127-(i%128); + + + data[y*w+x]=OPAQUE_FLAG | color; + } + } +} + +// Generates lightmaps based on the info given by the .light field of each Terrain_seg +void UpdateTerrainLightmaps () +{ + int i,t; + + // First make the wraparounds work right + + // lower-left strip + for (i=0;i<128;i++) + { + Terrain_seg[i*TERRAIN_WIDTH].r=Terrain_seg[i*TERRAIN_WIDTH+128].r; + Terrain_seg[i*TERRAIN_WIDTH].g=Terrain_seg[i*TERRAIN_WIDTH+128].g; + Terrain_seg[i*TERRAIN_WIDTH].b=Terrain_seg[i*TERRAIN_WIDTH+128].b; + + Terrain_seg[i].r=Terrain_seg[128*TERRAIN_WIDTH+i].r; + Terrain_seg[i].g=Terrain_seg[128*TERRAIN_WIDTH+i].g; + Terrain_seg[i].b=Terrain_seg[128*TERRAIN_WIDTH+i].b; + + } + + // lower-right strip + for (i=0;i<128;i++) + { + Terrain_seg[i*TERRAIN_WIDTH+255].r=Terrain_seg[i*TERRAIN_WIDTH+127].r; + Terrain_seg[i*TERRAIN_WIDTH+255].g=Terrain_seg[i*TERRAIN_WIDTH+127].g; + Terrain_seg[i*TERRAIN_WIDTH+255].b=Terrain_seg[i*TERRAIN_WIDTH+127].b; + + Terrain_seg[i+128].r=Terrain_seg[128*TERRAIN_WIDTH+i+128].r; + Terrain_seg[i+128].g=Terrain_seg[128*TERRAIN_WIDTH+i+128].g; + Terrain_seg[i+128].b=Terrain_seg[128*TERRAIN_WIDTH+i+128].b; + } + + // upper-left strip + for (i=0;i<128;i++) + { + Terrain_seg[(i+128)*TERRAIN_WIDTH].r=Terrain_seg[(i+128)*TERRAIN_WIDTH+128].r; + Terrain_seg[(i+128)*TERRAIN_WIDTH].g=Terrain_seg[(i+128)*TERRAIN_WIDTH+128].g; + Terrain_seg[(i+128)*TERRAIN_WIDTH].b=Terrain_seg[(i+128)*TERRAIN_WIDTH+128].b; + + Terrain_seg[255*TERRAIN_WIDTH+i].r=Terrain_seg[127*TERRAIN_WIDTH+i].r; + Terrain_seg[255*TERRAIN_WIDTH+i].g=Terrain_seg[127*TERRAIN_WIDTH+i].g; + Terrain_seg[255*TERRAIN_WIDTH+i].b=Terrain_seg[127*TERRAIN_WIDTH+i].b; + } + + // upper-right strip + for (i=0;i<128;i++) + { + Terrain_seg[(i+128)*TERRAIN_WIDTH+255].r=Terrain_seg[(i+128)*TERRAIN_WIDTH+127].r; + Terrain_seg[(i+128)*TERRAIN_WIDTH+255].g=Terrain_seg[(i+128)*TERRAIN_WIDTH+127].g; + Terrain_seg[(i+128)*TERRAIN_WIDTH+255].b=Terrain_seg[(i+128)*TERRAIN_WIDTH+127].b; + + Terrain_seg[255*TERRAIN_WIDTH+i+128].r=Terrain_seg[127*TERRAIN_WIDTH+i+128].r; + Terrain_seg[255*TERRAIN_WIDTH+i+128].g=Terrain_seg[127*TERRAIN_WIDTH+i+128].g; + Terrain_seg[255*TERRAIN_WIDTH+i+128].b=Terrain_seg[127*TERRAIN_WIDTH+i+128].b; + } + + int w=lm_w(TerrainLightmaps[0]); + + for (i=0;ir,tp->g,tp->b); + ushort *data=lm_data (TerrainLightmaps[which]); + + data[y*w+x]=OPAQUE_FLAG | color; + } + } + + for (i=0;i<4;i++) + { + GameLightmaps[TerrainLightmaps[i]].flags|=LF_CHANGED; + GameLightmaps[TerrainLightmaps[i]].flags&=~LF_LIMITS; + } + +} + +// Builds the surface normal for terrain segment n +void BuildNormalForTerrainSegment (int n) +{ + int i,t; + + vector a,b,c; + + if (n<0 || n>=(TERRAIN_WIDTH-1)*(TERRAIN_DEPTH-1)) + return; + + i=n/TERRAIN_WIDTH; + t=n%TERRAIN_WIDTH; + + a.x=t*TERRAIN_SIZE; + a.y=Terrain_seg[(i*TERRAIN_WIDTH)+(t)].y; + a.z=i*TERRAIN_SIZE; + + b.x=t*TERRAIN_SIZE; + b.y=Terrain_seg[(i*TERRAIN_WIDTH)+(t+TERRAIN_WIDTH)].y; + b.z=(i+1)*TERRAIN_SIZE; + + c.x=(t+1)*TERRAIN_SIZE; + c.y=Terrain_seg[(i*TERRAIN_WIDTH)+(t+TERRAIN_WIDTH+1)].y; + c.z=(i+1)*TERRAIN_SIZE; + + vm_GetNormal (&TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+t].normal1,&a,&b,&c); + + + a.x=t*TERRAIN_SIZE; + a.y=Terrain_seg[(i*TERRAIN_WIDTH)+(t)].y; + a.z=i*TERRAIN_SIZE; + + b.x=(t+1)*TERRAIN_SIZE; + b.y=Terrain_seg[(i*TERRAIN_WIDTH)+(t+TERRAIN_WIDTH+1)].y; + b.z=(i+1)*TERRAIN_SIZE; + + c.x=(t+1)*TERRAIN_SIZE; + c.y=Terrain_seg[(i*TERRAIN_WIDTH)+(t+1)].y; + c.z=(i)*TERRAIN_SIZE; + + vm_GetNormal (&TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+t].normal2,&a,&b,&c); + +} + +// Builds the vertex normal for terrain segment n +void BuildLightingNormalForSegment (int n) +{ + int i,t,hback,vback; + vector temp; + + if (n<0 || n>=(TERRAIN_WIDTH-1)*(TERRAIN_DEPTH-1)) + return; + + i=n/TERRAIN_WIDTH; + t=n%TERRAIN_WIDTH; + + if (i==0) + vback=TERRAIN_DEPTH-1; + else + vback=i-1; + + if (t==0) + hback=TERRAIN_WIDTH-1; + else + hback=t-1; + + vm_MakeZero(&temp); + + vm_AddVectors (&temp,&temp,&TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+hback].normal2); + vm_AddVectors (&temp,&temp,&TerrainNormals[MAX_TERRAIN_LOD-1][i*TERRAIN_WIDTH+t].normal2); + vm_AddVectors (&temp,&temp,&TerrainNormals[MAX_TERRAIN_LOD-1][(vback)*TERRAIN_WIDTH+hback].normal1); + vm_AddVectors (&temp,&temp,&TerrainNormals[MAX_TERRAIN_LOD-1][(vback)*TERRAIN_WIDTH+t].normal2); + + vm_AverageVector (&temp,4); +// vm_NormalizeVectorFast (&temp); + +} diff --git a/Descent3/terrain.h b/Descent3/terrain.h new file mode 100644 index 000000000..aebbbcd7a --- /dev/null +++ b/Descent3/terrain.h @@ -0,0 +1,339 @@ +#ifndef TERRAIN_H +#define TERRAIN_H + +#include "vecmat.h" +#include "pstypes.h" +#include "3d.h" +#include "grdefs.h" +#include "object.h" + +// How far we texture out to...after this distance we draw flat shaded polygons +#define DEFAULT_TEXTURE_DISTANCE 9999 + +#define TERRAIN_WIDTH 256 // How many cells across +#define TERRAIN_DEPTH 256 // How many cells down +#define TERRAIN_SIZE 16.0 // The size of each segment, must be a power of 2 +#define TERRAIN_TEX_WIDTH 32 +#define TERRAIN_TEX_DEPTH 32 + +// LOD STUFF +#define MAX_TERRAIN_LOD 4 // How many levels of detail we support +#define SHUTOFF_LOD_DELTA 800000.0f // Forces lod engine not to work for a particular cell +#define SHUTOFF_LOD_INVISIBLE 900000.0f // This LOD is totally invisible + +#define MAX_LOD_SIZE (1<<(MAX_TERRAIN_LOD-1)) + +#define MAX_CELLS_TO_RENDER 8000 // The maximum number of cells we can render in a frame + +#define MAX_TERRAIN_HEIGHT 350.0f // Max height of our terrain +#define TERRAIN_HEIGHT_INCREMENT (MAX_TERRAIN_HEIGHT/255.0f) // Incremental jumps +#define DEFAULT_VISIBLE_TERRAIN_DISTANCE 80.0*TERRAIN_SIZE + +// Sky defines +#define MAX_STARS 600 // how many stars in our sky +#define MAX_SATELLITES 5 // max satellites in our sky +#define MAX_HORIZON_PIECES 16 // how many segments of the horizon + // there are around our sphere + +// Sky flags +#define TF_STARS 1 // whether or not our terrain is starred +#define TF_SATELLITES 2 // Draw satellites or no? +#define TF_FOG 4 // Draw fog? +#define TF_ROTATE_STARS 8 +#define TF_ROTATE_SKY 16 + +// Satellite flags +#define TSF_HALO 1 // Draw halo? +#define TSF_ATMOSPHERE 2 // Draw atmosphere + +// occlusion stuff +#define OCCLUSION_SIZE 16 + +// Mine/terrain joining +#define MAX_LINK_TILES 4 // how many terrain segments can be attached to mines + +// Terrain segment flags +#define TF_DYNAMIC 1 +#define TF_SPECIAL_WATER 4 // Water +#define TF_SPECIAL_MINE 8 // This segment has a mine attached to it +#define TF_INVISIBLE 16 // This segment is invisible +#define TFM_REGION_MASK (32+64+128) +// NOTE: 32 64 and 128 are reserved for AI stuff (terrain region partitioning) + +// Terrain cells are on a fixed grid so they have no x and z positions. If you want the x and z +// positions you must calculate them yourself: gridx*TERRAIN_SIZE and gridz*TERRAIN_SIZE + +typedef struct +{ + float y; // Y position of the lower left corner of the terrain cell + float mody; // The modified y position of this cell - used for LOD + + ubyte l,r,g,b; + + short objects; // Index of the first object in this cell + short texseg_index; // index into the tex_segment array + + ubyte flags; // various flags + ubyte lm_quad; // which lightmap quad this index belongs to + ubyte ypos; // this is so we don't have to constantly convert + // floats to ints when traversing the terrain + // it's the integer version of pos.y + ubyte pad; // for alignment +} terrain_segment; + +typedef struct +{ + ubyte rotation; + short tex_index; +} terrain_tex_segment; + +// Data for LOD shutoff code +typedef struct +{ + int cellnum; + float save_delta[MAX_TERRAIN_LOD]; +} lodoff; + + +// Data for the sky spherical map +typedef struct +{ + int textured; // 1=use textures, 0=use gouraud shaded polygon + + // The two subscripts correspond to the top, middle, and bottom of the horizon piece + vector horizon_vectors[MAX_HORIZON_PIECES][6]; + float horizon_u[MAX_HORIZON_PIECES][5]; + float horizon_v[MAX_HORIZON_PIECES][5]; + + short dome_texture; + + float radius; + float rotate_rate; + + ddgr_color sky_color; + ddgr_color horizon_color; + ddgr_color fog_color; + + float satellite_r[MAX_SATELLITES]; + float satellite_g[MAX_SATELLITES]; + float satellite_b[MAX_SATELLITES]; + + vector star_vectors[MAX_STARS]; + vector satellite_vectors[MAX_SATELLITES]; + ubyte satellite_flags[MAX_SATELLITES]; + float satellite_size[MAX_SATELLITES]; + + ubyte num_satellites; + ubyte num_stars; + + short satellite_texture[MAX_SATELLITES]; + + vector lightsource; + angle lightangle; + + float damage_per_second; + float fog_scalar; + + int star_color[MAX_STARS]; + int flags; +} terrain_sky; + +typedef struct +{ + int mine_seg; + int mine_side; + int portal_num; + int terrain_seg; + +} link_tile; + +typedef struct +{ + int terrain_seg; + ubyte num_segs; + short mine_segs[50]; +} terrain_mine_list; + +typedef struct +{ + float z; + ushort right_edge,left_edge,top_edge,bottom_edge; // for fixing tjoint problems + ubyte right_count,left_count,top_count,bottom_count; + ushort segment; // what segment to render + ubyte lod; // what level of detail: 0=16x16, 1=8x8, 2=4x4, 3=2x2, 4=just this segment (1x1) + ubyte pad; +} terrain_render_info; + +typedef struct +{ + vector normal1; // Upper left triangle + vector normal2; // Lower right triangle +} terrain_normals; + +extern ubyte Terrain_dynamic_table[]; +extern terrain_normals *TerrainNormals[MAX_TERRAIN_LOD]; + +// Occlusion data for knowing what to draw +extern ubyte Terrain_occlusion_map[256][32]; +extern int Terrain_occlusion_checksum; + +extern int Check_terrain_portal; +extern int Terrain_checksum; + +// Our lighting maps for the terrain, one for each quadrant (starting at lower left) +extern int TerrainLightmaps[4]; + +extern int GlobalTransCount,TotalDepth; +extern int TerrainEdgeTest[MAX_TERRAIN_LOD][16]; + +extern terrain_render_info Terrain_list[]; +extern ushort TS_FrameCount; + +extern float VisibleTerrainZ; +extern float Terrain_average_height; + +extern float Clip_scale_left,Clip_scale_right,Clip_scale_top,Clip_scale_bot; +extern ubyte Terrain_from_mine; + +extern float Last_terrain_render_time; + +extern terrain_segment Terrain_seg[TERRAIN_WIDTH*TERRAIN_DEPTH]; +extern terrain_tex_segment Terrain_tex_seg[TERRAIN_TEX_WIDTH*TERRAIN_TEX_DEPTH]; + +// first object to render after cell has been rendered (only used for SW renderer) +extern short Terrain_seg_render_objs[]; + +#define TERRAIN_REGION(x) ((Terrain_seg[0x7FFFFFFF&x].flags&TFM_REGION_MASK)>>5) + +extern terrain_sky Terrain_sky; + +#if (defined(EDITOR) || defined(NEWEDITOR)) + extern int Editor_LOD_engine_off; + extern bool Terrain_render_ext_room_objs; +#endif + +extern int Terrain_LOD_engine_off; + +extern float Terrain_texture_distance; // how far we should texture before going to flat shad + +extern ubyte TerrainJoinMap[]; +extern float *TerrainDeltaBlocks[]; +extern ubyte *Terrain_max_height_int[]; +extern ubyte *Terrain_min_height_int[]; +extern ubyte Fast_terrain; +extern ubyte Flat_terrain; +extern ubyte Show_invisible_terrain; + +extern int Camera_direction,Sort_direction; + +#if (defined(_DEBUG) || defined(NEWEDITOR)) + extern ubyte TerrainSelected[]; + extern int Num_terrain_selected; +#endif + + + +extern ushort *Terrain_rotate_list; // which points have been sub/rotated this frame +extern g3Point *World_point_buffer; // Rotated points + + +#define TSEARCH_FOUND_TERRAIN 0 +#define TSEARCH_FOUND_MINE 1 +#define TSEARCH_FOUND_SATELLITE 2 +#define TSEARCH_FOUND_SKY_DOME 3 +#define TSEARCH_FOUND_SKY_BAND 4 +#define TSEARCH_FOUND_OBJECT 5 +#ifdef NEWEDITOR + #define TSEARCH_FOUND_NODE 6 + #define TSEARCH_FOUND_BNODE 7 +#endif + +extern int TSearch_on,TSearch_found_type,TSearch_x,TSearch_y,TSearch_seg,TSearch_face; + +extern void InitTerrain(); + +// Called whenever a new level is initted +extern void ResetTerrain(int force=0); + +extern int GetVisibleTerrain(vector *,matrix *); +extern void DisplayTerrainList (int,bool from_automap=0); +extern int CheckToRenderSky (int); + + +//left,top,right,bot are optional parameters. Omiting them (or setting them to -1) will +//render to the whole screen. Passing valid values will only render tiles visible in the +//specified window (though it won't clip those tiles to the window) +void RenderTerrain(ubyte from_mine,int left=-1,int top=-1,int right=-1,int bot=-1); + +void GetPreRotatedPoint(g3Point*,int,int,int); +void GenerateTerrainLight (); + +void BuildMinMaxTerrain(); +void BuildTerrainNormals(); + +int DrawTerrainTriangles (int n); +int LoadPCXTerrain(char *); + +// Given a position, returns the terrain segment that that position is in/over +// The return value is a pure cell number, meaning it doesn't have the outside flag set +int GetTerrainCellFromPos (vector *pos); + +// Given a position, returns the terrain segment that that position is in/over +// The return value is a valid room number, meaning it has the outside flag set +int GetTerrainRoomFromPos (vector *pos); + +// Given a position, returns the collision terrain segment that that position is in/over +int GetColTerrainSegFromPos (vector *pos); +// Given a terrain cell, returns the collision terrain segment that that position is in/over +int GetColTerrainSegFromTerrainSeg(int cell_index); + +// Computes the center of the segment in x,z and also sets y touching the ground +void ComputeTerrainSegmentCenter (vector *pos,int segnum); +// Given an position, returns the terrain Y coord at that location +// Also now can return the normal at that ground point +float GetTerrainGroundPoint (vector *pos,vector *normal=NULL); + +void SetupSky (float radius,int flags,ubyte randit=0); + +// Builds the surface normal for terrain segment n +void BuildNormalForTerrainSegment (int n); + +// Builds the vertex normal for terrain segment n +void BuildLightingNormalForSegment (int n); + + +// Gets a prerotated point that does not fall exactly on one of our 255 height values +void GetSpecialRotatedPoint(g3Point *dest,int x,int z,float yvalue); + +// Takes our light angle and fills in the appropriate values in the lightsource vector +void GenerateLightSource (); + +// Returns the terrain segment index of the farthest point that mine segment "mine_seg" +// touches. Based on Camera_direction +int GetFurthestMineSegment (int mine_seg); + +//codes a point for visibility in the window passed to RenderTerrain() +ubyte CodeTerrainPoint(g3Point *p); + +// Generates info for the LOD engine...must be called after the geometry changes +void GenerateLODDeltas (); + +// Generates lightmaps based on the info given by the .light field of each Terrain_seg +void UpdateTerrainLightmaps (); + +// Makes a four sided rectangle pinched into a triangle +void MakePinchBitmap (int dest_bm,int src_bm); + +// Gets the dynamic light value for this position +float GetTerrainDynamicScalar (vector *pos,int seg); + +// Shuts off LOD for a given cell +void TurnOffLODForCell (int cellnum); + +// Restores the terrain deltas to their original state +void ClearLODOffs (); + +// Gets the checksum for this terrain +int GetTerrainGeometryChecksum (); + +#endif diff --git a/Descent3/terrainrender.cpp b/Descent3/terrainrender.cpp new file mode 100644 index 000000000..4517d7414 --- /dev/null +++ b/Descent3/terrainrender.cpp @@ -0,0 +1,3906 @@ + /* + * $Logfile: /DescentIII/Main/terrainrender.cpp $ + * $Revision: 242 $ + * $Date: 9/06/01 12:15p $ + * $Author: Matt $ + * + * Terrain rendering code + * + * $Log: /DescentIII/Main/terrainrender.cpp $ + * + * 242 9/06/01 12:15p Matt + * Added bounds checking for postrender list. + * + * 241 4/19/00 5:30p Matt + * From Duane for 1.4 + * Mac sky changes + * + * 240 3/20/00 12:21p Matt + * Merge of Duane's post-1.3 changes. + * Caching optimization. + * + * 239 10/21/99 9:29p Jeff + * B.A. Macintosh code merge + * + * 238 10/21/99 5:27p Gwar + * fixed a pesky crash + * + * 237 10/21/99 4:33p Gwar + * objects weren't displaying in NEWEDITOR release build + * + * 236 10/12/99 10:55a Gwar + * added DrawTerrainOutline for NEWEDITOR + * + * 235 10/12/99 10:19a Gwar + * adding terrain support to NEWEDITOR + * + * 234 7/28/99 3:40p Kevin + * Macintosh! + * + * 233 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 232 5/07/99 7:22p Jason + * changes for cards with low videomemory + * + * 230 5/05/99 6:09p 3dsmax + * checked in dumb hack for now + * + * 229 4/27/99 3:36p Jason + * fixed terrain occlusion bug + * + * 228 4/23/99 7:18p Jason + * fixed some multiplayer issues with observer mode + * + * 227 4/22/99 12:05p Jason + * fixed some more direct3d issues + * + * 226 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 225 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 224 4/16/99 11:33p Jeff + * ifdef out some terrain triangle draw code, needs software renderer (for + * linux) + * + * 223 4/16/99 12:37a Matt + * Made Windows & Linux versions use the same qsort calls. + * + * 222 4/15/99 1:43a Jeff + * changes for linux compile + * + * 221 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 220 4/03/99 2:20p Jason + * added better heat damage effect + * + * 219 4/01/99 1:19p Jason + * draw band even with textured sky + * + * 218 3/31/99 11:41a Jason + * added support for attached thick lightning + * + * 217 3/01/99 2:39p Jason + * fixed star and halo rendering + * + * 216 2/26/99 3:26p Jason + * made satellites clip properly + * + * 215 2/19/99 4:26p Jason + * more work on Katmai support + * + * 214 2/19/99 12:04p Jason + * took out sky band + * + * 213 2/17/99 1:59p Jason + * fixed some postrender issues + * + * 212 2/17/99 1:05p Jason + * revamped object/face/terrain selection code + * + * 211 2/16/99 12:39a Matt + * Removed render_objs from terrain_segment and created parallel array + * + * 210 2/09/99 12:10p Jason + * rewriting indoor engine + * + * 209 2/08/99 5:07p Jason + * orthogonalize sky rotation matrix + * + * 208 2/05/99 3:37p Jason + * added/fixed features for the designers + * + * 207 2/04/99 12:17p Kevin + * Small fix for release builds + * + * 206 2/04/99 12:13p Jason + * fixed release array + * + * 205 2/01/99 10:51a Jason + * fixed outlines on terrain + * + * 204 1/29/99 11:29a Jason + * added misc bug fixes/features + * + * 203 1/24/99 5:43p Jason + * changes for terrain tiling + * + * 202 1/24/99 2:11a Jason + * fixed dumb texture UV problem + * + * 201 1/22/99 3:59p Jason + * added 256x256 textures to help with terrain skies + * + * 200 1/21/99 11:17a Jason + * fixed terrain rendering problem at the edges of the map + * + * 199 1/20/99 3:50p Jason + * fixed automap problem + * + * 197 1/20/99 2:48p Jason + * more terrain speedups + * + * 196 1/20/99 1:40p Jason + * terrain speedups + * + * 195 1/20/99 10:50a Jason + * added new terrain + * + * 194 12/29/98 6:12p Jason + * stupid fix for level designers + * + * 193 12/17/98 2:18p Jason + * fixed mip mapping problem + * + * 192 12/11/98 2:43p Jason + * tweaked snow + * + * 191 12/09/98 1:09p Jason + * second draft of automap + * + * 190 11/18/98 5:10p Jason + * fixed z buffer snow + * + * 189 11/18/98 4:30p Jason + * added some functionality to terrain fog + * + * 188 11/18/98 2:54p Jason + * added snow effect + * + * 187 11/06/98 5:42p Jason + * took out terrain casting + * + * 186 10/22/98 4:02p Jason + * made lightmaps draw even in nolightmap mode + * + * 185 10/22/98 10:21a Jason + * fixed wrap/clamp problem + * + * 184 10/21/98 9:28p Jason + * Made no lightmaps work globally + * + * 183 10/21/98 8:40p Jason + * testing for Mark + * + * 182 10/21/98 6:54p Jason + * changes for shitty direct3d cards + * + * 181 10/19/98 6:30p Jeff + * changes made for detail variables. Put in preset values. Preset + * options. Removed terrain_cast from detail. Put new callbacks in + * UIListBox and UISlider + * + * 180 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 179 10/07/98 5:17p Jason + * fixed lighting on the terrain + * + * 178 10/03/98 11:21p Matt + * Added system to seperately control outline mode for mine, terrain, sky, + * & objects + * + * 177 10/02/98 5:31p Jason + * tweaks for statelimited stuff + * + * 176 10/02/98 12:26p Jason + * fixed fogging of indoor rooms + * + * 175 9/29/98 3:00p Jason + * fixed state limited bug with lightmaps + * + * 174 9/29/98 2:02p Jason + * fixed external rooms rendering even when they shouldn't + * + * 173 9/25/98 9:25p Jason + * did some graphics optimizations + * + * 172 9/25/98 11:58a Jason + * various rendering optimizations + * + * 171 9/24/98 12:57p Jason + * more state limited optimizations + * + * 170 9/23/98 11:16a Jason + * fixed some lightmap problems + * + * 169 9/22/98 11:46a Jason + * made terrain lightmap rendering more state friendly + * + * 168 9/17/98 6:51p Jason + * fixed streaking vectors + * + * 167 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 166 9/11/98 5:09p Jason + * made terrain state-change friendly + * + * 165 8/25/98 6:34p Craig + * Include terrain outline func in non-editor debug versions. + * + * 164 8/25/98 1:50p Jason + * made outlines draw in ndebug mode, instead of editor mode + * + * 163 8/19/98 5:48p Jason + * made robots have at least a mininum amount of lighting + * + * 162 8/19/98 2:19p Jeff + * moved detail globals to a struct + * + * 161 8/17/98 5:27p Jason + * fixed dynamic lighting for objects above the terrain ceiling + * + * 160 8/14/98 4:00p Jason + * added specular objects outside + * + * 159 8/06/98 11:00a Jason + * added blurred stars + * + * 158 8/04/98 2:55p Jason + * added motion blurred stars + * + * 157 7/24/98 4:49p Jason + * made terrain casting work again + * + * 156 7/20/98 12:04p Jason + * added per level satellite lighting + * + * 155 6/24/98 7:03p Jason + * fixed omega and external room sorting problem + * + * 154 6/24/98 3:56p Jason + * added code for new omega cannon + * + * 153 6/16/98 2:56p Jason + * fixed terrain occlusion bug with objects + * + * 152 6/15/98 4:00p Jason + * replaced monochromatic polymodel lighting with rgb lighting + * + * 151 6/12/98 5:15p Jason + * added dynamic volume light system + * + * 150 6/10/98 12:23p Jason + * added new skies + * + * 149 6/04/98 6:45p Jason + * made objects use the terrain occlusion system + * + * 148 5/26/98 6:54p Jason + * viseffect optimizations + * + * 147 5/25/98 3:46p Jason + * added better light glows + * + * 146 5/14/98 2:17p Jason + * more changes to be memory friendly + * + * 145 5/14/98 12:56p Jason + * changes to help lower memory usage + * + * 144 5/11/98 7:03p Jason + * fixed terrain bug with microwave effect + * + * 143 5/06/98 6:18p Jason + * improved terrain raycasting + * + * 142 5/05/98 6:27p Chris + * Added BBoxs and spheres to external rooms (in mode 2) + * + * 141 5/05/98 5:28p Jason + * more terrain speedups + * + * 140 5/05/98 4:38p Jason + * added raycasting test stuff + * + * 139 5/04/98 8:04p Jason + * made wireframe sky view + * + * 138 5/01/98 5:46p Jason + * fixed external room popping bug + * + * 137 5/01/98 3:51p Jason + * sped up terrain rendering by precaching all megacell lod bitmaps + * + * 136 5/01/98 12:42p Jason + * slight framerate enhancement + * + * 135 4/30/98 6:45p Jason + * more changes for weather + * + * 134 4/30/98 12:22p Jason + * did some lo-res model optimizations + * + * 133 4/29/98 3:27p Jason + * added weather engine + * + * 132 4/29/98 11:38a Jason + * added some weather effects (first pass) + * + * 131 4/23/98 12:21p Jason + * fixed rendering bug if satellite values were out of range + * + * 130 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 129 4/17/98 3:57p Jason + * added microwave effect + * + * 128 4/10/98 6:04p Jason + * fixed some lighting issues + * + * 127 4/03/98 1:36p Jason + * saved some memory + * + * 126 4/01/98 11:26a Jeff + * Added an editor bool to skip Terrain object rendering + * + * 125 3/26/98 10:54a Jason + * tweaked some stuff for size independant terrain + * + * 124 3/17/98 5:02p Jason + * sped up rendering the terrain from the mine by a large amount + * + * 123 3/09/98 4:54p Jason + * don't render objects that can't been seen through a portal + * + * 122 3/06/98 2:33p Jason + * fixed potential memory overwrite bug + * + * 121 3/06/98 2:17p Jason + * sped up terrain by making lod'd triangles quads + * + * 120 2/25/98 2:05p Jason + * did FOV and object visibility changes + * + * 119 2/17/98 2:22p Jason + * fixed some potential z sorting problems + * + * 118 2/12/98 1:32p Jason + * got mipmapping working + * + * 117 2/10/98 5:42p Jason + * fixed object rendering with RT_ROOM type + * + * 116 2/10/98 10:28a Matt + * Got rid of compile warning + * + * 115 2/09/98 7:31p Matt + * Changed object rendering code to not look at Viewer_object to determine + * whether to render the object + * + * 114 2/05/98 6:02p Jason + * fixed bug my previous rev introduced + * + * 113 2/05/98 4:41p Jason + * fixed off by one bug + * + * 112 2/05/98 12:35p Jason + * fixed screen clearing problem with gouraud sky + * + * 111 2/04/98 6:58p Matt + * Fixed viseffect rendering on terrain. + * + * 110 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 109 2/02/98 3:19p Jason + * made stars draw with different colors + * + * 108 1/30/98 12:12p Jason + * fixed LOD bug. This gives us twice the framerate in hilly areas!!! + * + * 107 1/29/98 5:50p Matt + * Changed old camera object type to be viewer object (for editor), and + * now camera objects will just be for game cameras. + * + * 106 1/28/98 4:06p Jason + * made stars not darken so much on blur + * + * 105 1/28/98 3:55p Jason + * added stars after the textured sky + * + * 104 1/23/98 6:25p Jason + * Got spray weapons working + * + * 103 1/20/98 2:17p Jason + * fixed weird sorting problem with objects and viseffects + * + * 102 1/20/98 12:10p Jason + * implemented vis effect system + * + * 101 1/16/98 8:06p Jason + * added selectable halos and atmosphere blends to satellites + * + * 100 1/16/98 3:05p Jason + * fixed LOD cracking at terrain edges + * + * 99 1/16/98 2:47p Jason + * tweaked some things for speed reasons + * + * 98 1/16/98 1:04p Jason + * don't do update environment + * + * 97 1/15/98 6:32p Jason + * added v wrapping for sky textures + * + * 96 1/15/98 12:42p Jason + * big fix for terrain sky rendering + * + * 95 1/12/98 3:34p Jason + * sped up indoor rendering by clipping faces against portals + * + * 94 1/07/98 4:19p Jason + * added dome to terrain sky + * + * 93 1/06/98 1:29p Matt + * Cleaned up interfaces to rendering routines, deleted unused code, etc. + * + * 92 1/02/98 6:08p Jason + * fixed bug with alphaed moons and halos + * + * 90 12/30/97 5:45p Jason + * added toggleable fog + * + * 89 12/29/97 5:44p Samir + * Took out references to grViewport and old 2d library. + * + * 88 12/19/97 5:58p Jason + * fixed out-of-bounds bug + * + * 87 12/19/97 5:38p Jason + * fixed errors with 2d/3d integration + * + * 86 12/19/97 12:20p Jason + * changes for better 2d/3d system integration + * + * 85 12/18/97 3:29p Jason + * fixed some lightmap wrapping issues + * + * 84 12/17/97 5:26p Jason + * added support for selectable wraparound sky + * + * 83 12/17/97 4:08p Jason + * took out fog (again) + * + * 82 12/10/97 6:54p Jason + * toned down star blur effect + * + * 81 12/09/97 7:13p Jason + * added support for blurred lines in hardware + * + * 80 12/01/97 10:52a Jason + * separated satellite and star rendering flags + * + * 79 11/21/97 5:33p Jason + * fixed far clip bug + * + * 78 11/19/97 5:23p Jason + * added terrain dynamic lighting + * + * 77 11/17/97 8:33p Jason + * fixed bug with object selection introduced by last rev + * + * 76 11/17/97 6:13p Mark + * allowed object 0 to be selected + * + * 75 11/10/97 12:21p Jason + * implemented different object visibility + * + * 74 11/10/97 12:00p Jason + * fixed view scaling bug in g3_DrawRotatedBitmap + * + * 73 10/30/97 4:38p Jason + * special cased highest-lod in DrawTerrainTrianglesHardware + * + * 72 10/29/97 12:37p Jason + * fixed tjoint problems + * + * 71 10/28/97 2:06p Jason + * check for backfaces early + * + * 70 10/17/97 1:23p Jason + * took out references to bitmap8bit.h + * + * 69 10/13/97 5:06p Chris + * Made it compile in the main project (outline mode only in editor) + * + * 68 10/10/97 6:31p Jason + * added extra level of detail level + * + * 67 10/09/97 11:11a Jason + * turned fog back on for now + * + * 66 10/08/97 4:34p Jason + * fixed line drawing problems + * + * 65 10/06/97 6:39p Jason + * got terrain objects working with lightmaps/radiosity + * + * 64 10/02/97 6:45p Jason + * optimized terrain rotation + * + * 63 10/02/97 6:21p Jason + * checked in for matt + * + * + * 62 10/02/97 3:48p Matt + * Don't render room (via room object) if terrain renderer was called from + * room renderer + * + * 61 10/02/97 2:45p Jason + * got invisible terrain cells working + * + * 60 10/01/97 7:51p Matt + * Added code for external rooms + * + * 59 9/30/97 4:20p Jason + * took out terrain object light + * + * 58 9/29/97 5:40p Jason + * patched circular linked list bug + * + * 57 9/21/97 7:57p Jason + * separated hardware/software rendering pipes, plus got lightmaps working + * with tmap2's + * + * 56 9/21/97 7:17p Jason + * Made terrain lightmaps work again + * + * 55 9/18/97 1:24p Matt + * Changed terrain object rendering to keep its data locally, instead of + * in the object struct + * + * 54 9/17/97 5:08p Jason + * took out fog (maybe just temporary) + * + * + * 53 9/17/97 12:02p Jason + * ripped out segment engine + * + * 52 9/12/97 7:06p Jason + * fixed bug with MAX_RENDER_ROOMS being too low + * + * 51 9/12/97 5:17p Jason + * fixed problem where buildings could be lower than terrain, thus + * generating error + * + * 50 9/10/97 5:17p Jason + * more lighting improvements + * + * 49 9/09/97 6:17p Jason + * fixed bug with color model and dynamic objects + * + * 48 9/09/97 6:15p Jason + * Added groovy dynamic terrain lighting system + * + * 47 9/09/97 1:12p Jason + * fixed color model bug + * + * 46 9/09/97 11:44a Jason + * changed the way alpha works with the renderer + * + * 45 9/08/97 4:13p Jason + * added colored lighting for the terrain + * + * 44 9/05/97 4:38p Jason + * incremental updates + * + * 43 9/02/97 11:07a Jason + * added alpha component to uvl struct + * + * 42 8/29/97 11:41a Jason + * made textures not clamp if renderering the mine view + * + * 41 8/26/97 10:23a Jason + * added some asserts, plus clamping for terrain textures + * + * 40 8/20/97 6:18p Jason + * fixed memory corruption bug + * + * 39 8/20/97 4:22p Matt + * Took out unneeded extern + * + * 38 8/19/97 5:12p Jason + * (hopefully) fixed the polygon object popping problem + * + * 37 8/07/97 5:06p Jason + * added new tiling system for terrain + * + * 36 8/07/97 3:19p Jason + * replaced old terrain tmap system with new one + * + * 35 8/07/97 11:47a Jason + * got tmap2s rotatable by themselves + * + * 34 8/06/97 5:00p Jason + * ripped out lightmaps pending a better lighting system for terrain + * + * 33 8/06/97 11:43a Jason + * made lightmaps work correctly in the terrain + * + * 32 8/01/97 5:46p Jason + * did some housekeeping + * + * 31 7/31/97 3:28p Jason + * fixed bug where memory would get corrupted when drawing newstyle + * gouraud objects + * + * 30 7/29/97 5:33p Jason + * precompute the edge rotation list + * + * 29 7/29/97 10:28a Jason + * added RenderOBject abstraction layer + * + * 28 7/28/97 5:23p Jason + * added features to lod engine - namely dynamic texture generation + * + * 27 7/28/97 1:14p Chris + * Added support for sub-object visability. Plus, debris. + * + * 26 7/25/97 5:25p Jason + * fixed multiple bugs in terrain LOD engine + * + * 25 7/23/97 6:27p Jason + * added code to support terrain simplification + * + * 24 7/21/97 1:59p Jason + * added user adjustable fog parameters + * + * 23 7/20/97 8:37p Jason + * fixed some fog problems + * + * 22 7/20/97 7:37p Jason + * added new sky + * + * 21 7/20/97 3:40p Jason + * got room/terrain links working (at least in the terrain) + * + * 20 7/18/97 7:25p Jason + * do object drawing rather better + * + * 19 7/17/97 3:00p Jason + * changed "moons" to be called satellites since we're going to have suns + * and other things in orbit + * + * + * 18 7/16/97 5:20p Jason + * implemented selection of moons with mouse click + * + * 17 7/16/97 4:18p Matt + * Changes to work with changed 3d draw functions + * + * 16 7/15/97 7:40p Jason + * made fogplane obscenely close + * + * 15 7/15/97 6:44p Mark + * + * 14 7/15/97 5:32p Jason + * got simple static lighting working with objects + * + * 13 7/15/97 4:57p Mark + * Corrected usage of UseHardware flag + * + * 12 7/15/97 4:54p Jason + * made water not move its friends + * + * 11 7/15/97 4:18p Matt + * Don't do water undulation on tiles that have tmap2's on them + * + * 10 7/15/97 4:11p Jason + * added focus selection to terrain objects + * + * 9 7/15/97 3:33p Jason + * fixed some object sorting problems + * + * 8 7/15/97 3:09p Jason + * fixed dumb bug that I introduced in my last rev + * + * 7 7/15/97 2:59p Matt + * Added assert + * + * 39 6/24/97 4:24p Jason + * changes for y only terrain + * + * 38 6/24/97 12:45p Jason + * checked in for safety + * + * 37 6/17/97 11:35a Jason + * checked in for safety + * + * 36 6/16/97 2:34p Jason + * added 3dfx support + * + * 35 6/12/97 12:26p Jason + * fixed dumb rotational bug + * + * 34 5/22/97 5:16p Jason + * added tmap2 capability to the terrain + * + * 33 5/21/97 3:40p Jason + * added terrain editing stuff + * + * 32 5/19/97 5:10p Jason + * changes for our new abstracted renderer code + * + * 31 5/16/97 4:53p Jason + * made terrain clear the screen only when called from inside a mine + * + * 30 5/15/97 4:37p Jason + * fixed some terrain bugs + * + * 29 5/15/97 3:28a Jason + * tried to get subwindow clearing to work with terrain - but it won't + * until samir fixes lock_clear + * + * 28 5/13/97 11:34a Samir + * Changed some NDEBUGs to ifdef EDITORs + * + * 27 5/11/97 1:31p Matt + * Added code to only render terrain that might be visible when terrain + * called from the mine renderer. + * + * $NoKeywords: $ + */ +#ifdef NEWEDITOR + #include "neweditor\globals.h" + void RenderMine(int viewer_roomnum,int flag_automap,int called_from_terrain,bool render_all,bool outline,bool flat,prim *prim); +#endif +#include "terrain.h" +#include "grdefs.h" +#include "3d.h" +#include "pstypes.h" +#include "pserror.h" +#include "renderer.h" +#include "gametexture.h" +#include "descent.h" +#include "render.h" +#include "game.h" +#include "texture.h" +#include "ddio.h" +#include "polymodel.h" +#include "lighting.h" +#include "vecmat.h" +#include "renderobject.h" +#include "findintersection.h" +#include "weapon.h" +#include "weather.h" +#include "viseffect.h" +#ifdef EDITOR + #include "editor\d3edit.h" +#endif +#include "fireball.h" +#include +#include +#include "config.h" +#include "gameloop.h" +#include "postrender.h" +#include "Macros.h" +#include "psrand.h" +#include "player.h" +#define TERRAIN_PERSPECTIVE_TEXTURE_DEPTH 1*TERRAIN_SIZE +#define LOD_ROW_SIZE (MAX_LOD_SIZE+1) +int DrawTerrainTrianglesSoftware (int index,int bm_handle,int upper_left,int lower_right); +int DrawTerrainTrianglesHardware (int index,int bm_handle,int upper_left,int lower_right); +int DrawTerrainTrianglesHardwareNoLight (int index,int bm_handle,int upper_left,int lower_right); +void DrawTerrainLightmapsHardware (int index,int upper_left,int lower_right); +void DrawSky (vector *veye,matrix *vorient); +function_mode View_mode; +int ManageFramerate=0; +int MinAllowableFramerate=15; +ubyte Fast_terrain=1; +float Far_fog_border; +vector Terrain_viewer_eye; +ubyte Terrain_from_mine=0; +ubyte Show_invisible_terrain=0; +int Terrain_objects_drawn=0; +vector Last_frame_stars[MAX_STARS]; +float Terrain_texture_distance=DEFAULT_TEXTURE_DISTANCE; +int Check_terrain_portal=0; +static vector Temp_sky_vectors[MAX_HORIZON_PIECES][6]; +static vector Temp_sky_vectors_unrotated[MAX_HORIZON_PIECES][6]; +// Last time terrain was rendered +float Last_terrain_render_time=-1; +// Sets UV's based on 90 degree rotations +float TerrainUSpeedup[4][LOD_ROW_SIZE*LOD_ROW_SIZE]; +float TerrainVSpeedup[4][LOD_ROW_SIZE*LOD_ROW_SIZE]; +#if (!defined(RELEASE) || defined(NEWEDITOR)) + //for building a render list for each terrain cell + int render_next[MAX_OBJECTS]; +#endif +void DrawPlayerOnWireframe(); +float Clip_scale_left,Clip_scale_top,Clip_scale_right,Clip_scale_bot; +#ifdef NEWEDITOR + bool Rendering_main_view=true; +#endif +void InitTerrainRenderSpeedups() +{ + // Figure out a table of values for rotated uv points + for (int y=0;y<=MAX_LOD_SIZE;y++) + { + for (int x=0;x<=MAX_LOD_SIZE;x++) + { + TerrainUSpeedup[0][(y*LOD_ROW_SIZE)+x]=(float)x/(float)MAX_LOD_SIZE; + TerrainVSpeedup[0][(y*LOD_ROW_SIZE)+x]=(float)y/(float)MAX_LOD_SIZE; + TerrainUSpeedup[1][(y*LOD_ROW_SIZE)+x]=1.0-((float)y/(float)MAX_LOD_SIZE); + TerrainVSpeedup[1][(y*LOD_ROW_SIZE)+x]=(float)x/(float)MAX_LOD_SIZE; + TerrainUSpeedup[2][(y*LOD_ROW_SIZE)+x]=1.0-((float)x/(float)MAX_LOD_SIZE); + TerrainVSpeedup[2][(y*LOD_ROW_SIZE)+x]=1.0-((float)y/(float)MAX_LOD_SIZE); + TerrainUSpeedup[3][(y*LOD_ROW_SIZE)+x]=((float)y/(float)MAX_LOD_SIZE); + TerrainVSpeedup[3][(y*LOD_ROW_SIZE)+x]=1.0-((float)x/(float)MAX_LOD_SIZE); + } + } + +} +//codes a point for visibility in the window passed to RenderTerrain() +ubyte CodeTerrainPoint(g3Point *p) +{ + ubyte cc=0; + if (p->p3_sx>Clip_scale_right) + cc |= CC_OFF_RIGHT; + if (p->p3_sxp3_sy>Clip_scale_bot) + cc |= CC_OFF_BOT; + if (p->p3_sy=(TERRAIN_WIDTH*TERRAIN_DEPTH)) + return 1; + if (bit>=8) + return 1; + ubyte val=Terrain_dynamic_table[seg] & (1<y/y_increment; + int x_int=pos->x/TERRAIN_SIZE; + int z_int=pos->z/TERRAIN_SIZE; + + float x_norm=(pos->x/TERRAIN_SIZE)-x_int; + float z_norm=(pos->z/TERRAIN_SIZE)-z_int; + float y_norm=(pos->y/y_increment)-y_int; + if (y_norm<0) + { + y_norm=0; + y_int=0; + } + if (y_norm>1) + { + y_norm=1.0; + y_int=7; + } + if (x_norm<0 || x_norm>1 || z_norm<0 || z_norm>1) + return .5; + float left_norm,right_norm,top_norm,bottom_norm,scalar; + + cube_values[0]=IsTerrainDynamicChecked(seg,y_int); + cube_values[1]=IsTerrainDynamicChecked(seg+TERRAIN_WIDTH,y_int); + cube_values[2]=IsTerrainDynamicChecked(seg+TERRAIN_WIDTH+1,y_int); + cube_values[3]=IsTerrainDynamicChecked(seg+1,y_int); + cube_values[4]=IsTerrainDynamicChecked(seg,y_int+1); + cube_values[5]=IsTerrainDynamicChecked(seg+TERRAIN_WIDTH,y_int+1); + cube_values[6]=IsTerrainDynamicChecked(seg+TERRAIN_WIDTH+1,y_int+1); + cube_values[7]=IsTerrainDynamicChecked(seg+1,y_int+1); + left_norm=((1-z_norm)*cube_values[0])+(z_norm*cube_values[1]); + right_norm=((1-z_norm)*cube_values[3])+(z_norm*cube_values[2]); + bottom_norm=((1-x_norm)*left_norm)+(x_norm*right_norm); + left_norm=((1-z_norm)*cube_values[4])+(z_norm*cube_values[5]); + right_norm=((1-z_norm)*cube_values[7])+(z_norm*cube_values[6]); + top_norm=((1-x_norm)*left_norm)+(x_norm*right_norm); + scalar=((1-y_norm)*bottom_norm)+(y_norm*top_norm); + ASSERT (scalar>=0 && scalar<=1); + return scalar; +} +// Takes a min,max vector and makes a surrounding cube from it +void MakePointsFromMinMax (vector *corners,vector *minp,vector *maxp); +typedef struct { + int objnum; + float dist; + int vis_effect; +} obj_sort_item; +//Compare function for room face sort +static int obj_sort_func(const obj_sort_item *a, const obj_sort_item *b) +{ + if (a->dist < b->dist) + return -1; + else if (a->dist > b->dist) + return 1; + else + return 0; +} +// Returns true if the object is outside of our terrain portal +int ObjectOutOfPortal (object *obj) +{ + g3Point pnt1,pnt2; + ubyte anded=0xff; + g3_RotatePoint (&pnt1,&obj->min_xyz); + if (pnt1.p3_codes & CC_BEHIND) + return 0; + g3_ProjectPoint (&pnt1); + anded&=CodeTerrainPoint (&pnt1); + g3_RotatePoint (&pnt2,&obj->max_xyz); + if (pnt2.p3_codes & CC_BEHIND) + return 0; + g3_ProjectPoint (&pnt2); + anded&=CodeTerrainPoint (&pnt2); + if (anded) + return 1; + return 0; +} +obj_sort_item objs_to_render[MAX_OBJECTS+MAX_VIS_EFFECTS]; +obj_sort_item rooms_to_render[MAX_ROOMS]; +// Checks to see if this object can even be seen from our current viewpoint +// By shooting rays to it +// Returns true if any of the rays hit +int ShootRaysToObject (object *obj) +{ + vector corners[8]; + if (obj->type==OBJ_ROOM) + { + room *rp=&Rooms[obj->id]; + MakePointsFromMinMax (corners,&rp->min_xyz,&rp->max_xyz); + } + else + { + MakePointsFromMinMax (corners,&obj->min_xyz,&obj->max_xyz); + } + fvi_info hit_info; + fvi_query fq; + fq.p0=&Viewer_object->pos; + fq.startroom=Viewer_object->roomnum; + fq.rad=0.0f; + fq.flags=FQ_NO_RELINK|FQ_EXTERNAL_ROOMS_AS_SPHERE|FQ_IGNORE_EXTERNAL_ROOMS; + fq.thisobjnum = Viewer_object-Objects; + fq.ignore_obj_list = NULL; + + for (int i=0;i<8;i++) + { + fq.p1=&corners[i]; + int fate = fvi_FindIntersection(&fq,&hit_info); + /*g3Point pnt1,pnt2; + vector fpnt = *fq.p0 + 3.0 * Viewer_object->orient.fvec; + g3_RotatePoint (&pnt1,&fpnt); + g3_RotatePoint (&pnt2,&hit_info.hit_pnt); + g3_DrawLine ((GR_RGB(255,255,255)),&pnt1,&pnt2);*/ + if (fate==HIT_NONE) + return 1; + } + return 0; +} +// Returns true if the external room is in the view cone +// Else returns false +bool ExternalRoomVisible (room *rp,vector *center,float *zdist) +{ + ASSERT (rp->flags & RF_EXTERNAL); + g3Point pnt; + ubyte ccode; + + vector corners[8]; + g3_RotatePoint (&pnt,center); + *zdist=pnt.p3_z; + MakePointsFromMinMax (corners,&rp->min_xyz,&rp->max_xyz); + ubyte andbyte=0xff; + for (int t=0;t<8;t++) + { + g3_RotatePoint(&pnt,&corners[t]); + ccode=g3_CodePoint (&pnt); + if (!ccode) + return true; + andbyte&=ccode; + } + if (andbyte) + return false; + return true; +} +// Render all the rooms out on the terrain for this frame +void RenderTerrainRooms () +{ + object *obj; + #ifdef EDITOR + if(!Terrain_render_ext_room_objs) + return; + #endif + + int room_count=0; + float zdist; + int use_occlusion=0; + int src_occlusion_index; + int i; + if (Terrain_from_mine) + return; + if ((Terrain_checksum+1)==Terrain_occlusion_checksum) + { + use_occlusion=1; + int oz=(Viewer_object->pos.z/TERRAIN_SIZE)/OCCLUSION_SIZE; + int ox=(Viewer_object->pos.x/TERRAIN_SIZE)/OCCLUSION_SIZE; + if (oz<0 || oz>=OCCLUSION_SIZE || ox<0 || ox>=OCCLUSION_SIZE) + use_occlusion=0; + src_occlusion_index=oz*OCCLUSION_SIZE+ox; + } + for (i=0;i<=Highest_object_index;i++) + { + obj=&Objects[i]; + if (obj->type!=OBJ_ROOM) + continue; + + if (obj->flags & OF_DEAD) + continue; + if (obj->render_type == RT_NONE) + continue; + if (! OBJECT_OUTSIDE(obj)) + continue; + float size=obj->size; + + if (use_occlusion) + { + int y1=(obj->pos.z/TERRAIN_SIZE)/OCCLUSION_SIZE; + int x1=(obj->pos.x/TERRAIN_SIZE)/OCCLUSION_SIZE; + int dest_occlusion_index=(y1*OCCLUSION_SIZE); + dest_occlusion_index+=x1; + int occ_byte=dest_occlusion_index/8; + int occ_bit=dest_occlusion_index%8; + if (obj->pos.yid],&obj->pos,&zdist)) + continue; + if (!IsPointVisible (&obj->pos,size,&zdist)) + continue; + + if (Check_terrain_portal && ObjectOutOfPortal (obj)) + continue; + /*if (!Terrain_from_mine) + { + if (!ShootRaysToObject (obj)) + continue; + }*/ + rooms_to_render[room_count].vis_effect=0; + rooms_to_render[room_count].objnum = obj-Objects; + rooms_to_render[room_count].dist = zdist; + room_count++; + } + // Sort and draw rooms + qsort(rooms_to_render,room_count,sizeof(*rooms_to_render),(int (*)(const void*,const void*))obj_sort_func); + for (i=room_count-1;i>=0;i--) + { + int objnum = rooms_to_render[i].objnum; + object *obj=&Objects[objnum]; +#ifndef NEWEDITOR + RenderMine(obj->id,0,1); +#else + RenderMine(obj->id,0,1,1,0,0,NULL); +#endif + // Draw a surrounding sphere + #if (defined(_DEBUG) && !defined(NEWEDITOR)) + if(Game_show_sphere == 2) + DrawDebugInfo(obj); + #endif + } +} +// Renders every visible terrain object +void RenderAllTerrainObjects() +{ + object *obj; + int snows[500]; + int num_snows=0; + int obj_count=0; + float zdist; + int use_occlusion=0; + int src_occlusion_index; + int i; + if ((Terrain_checksum+1)==Terrain_occlusion_checksum) + { + use_occlusion=1; + int oz=(Viewer_object->pos.z/TERRAIN_SIZE)/OCCLUSION_SIZE; + int ox=(Viewer_object->pos.x/TERRAIN_SIZE)/OCCLUSION_SIZE; + if (oz<0 || oz>=OCCLUSION_SIZE || ox<0 || ox>=OCCLUSION_SIZE) + use_occlusion=0; + src_occlusion_index=oz*OCCLUSION_SIZE+ox; + } + for (i=0;i<=Highest_object_index;i++) + { + obj=&Objects[i]; + if (obj==Viewer_object) + continue; + // Don't draw piggybacked objects + if (Viewer_object->type==OBJ_OBSERVER && i==Players[Viewer_object->id].piggy_objnum) + continue; + if (obj->type==OBJ_ROOM) + continue; + + if (obj->type==OBJ_NONE) + continue; + if (obj->flags & OF_DEAD) + continue; + if (obj->render_type == RT_NONE) + continue; + if (! OBJECT_OUTSIDE(obj)) + continue; + float size=obj->size; + + // Special case weapons with streamers + if (obj->type==OBJ_WEAPON && (Weapons[obj->id].flags & WF_STREAMER)) + size=Weapons[obj->id].phys_info.velocity.z; + if (use_occlusion) + { + int y1=(obj->pos.z/TERRAIN_SIZE)/OCCLUSION_SIZE; + int x1=(obj->pos.x/TERRAIN_SIZE)/OCCLUSION_SIZE; + int dest_occlusion_index=(y1*OCCLUSION_SIZE); + dest_occlusion_index+=x1; + int occ_byte=dest_occlusion_index/8; + int occ_bit=dest_occlusion_index%8; + if (obj->pos.y+obj->sizetype==OBJ_WEAPON && Weapons[obj->id].flags & WF_ELECTRICAL) + { + // Automatically render all electrical objects + zdist=0; + } + else + { + if (!IsPointVisible (&obj->pos,size,&zdist)) + continue; + + if (Check_terrain_portal && ObjectOutOfPortal (obj)) + continue; + } + + if (UseHardware) + { + if (Num_postrenders < MAX_POSTRENDERS) + { + Postrender_list[Num_postrenders].type=PRT_OBJECT; + Postrender_list[Num_postrenders].z=zdist; + Postrender_list[Num_postrenders++].objnum=obj-Objects; + } + } + else + { + objs_to_render[obj_count].vis_effect=0; + objs_to_render[obj_count].objnum = obj-Objects; + objs_to_render[obj_count].dist = zdist; + obj_count++; + } + } +#ifndef NEWEDITOR + for (i=0;i<=Highest_vis_effect_index;i++) + { + vis_effect *vis=&VisEffects[i]; + + if (vis->type==VIS_NONE) + continue; + if (vis->flags & VF_DEAD) + continue; + if (! ROOMNUM_OUTSIDE(vis->roomnum)) + continue; + // Special case snow + if (vis->id==SNOWFLAKE_INDEX) + { + snows[num_snows]=vis-VisEffects; + num_snows++; + } + else + { + if ((vis->flags & VF_WINDSHIELD_EFFECT) || IsPointVisible (&vis->pos,vis->size,&zdist)) + { + if (vis->flags & VF_WINDSHIELD_EFFECT) + zdist=0; + if (UseHardware) + { + if (Num_postrenders < MAX_POSTRENDERS) + { + Postrender_list[Num_postrenders].type=PRT_VISEFFECT; + Postrender_list[Num_postrenders].z=zdist; + Postrender_list[Num_postrenders++].objnum=vis-VisEffects; + } + } + else + { + objs_to_render[obj_count].vis_effect=1; + objs_to_render[obj_count].objnum = vis-VisEffects; + objs_to_render[obj_count].dist = zdist; + obj_count++; + } + } + } + } +#endif + + // Sort objects + qsort(objs_to_render,obj_count,sizeof(*objs_to_render),(int (*)(const void*,const void*))obj_sort_func); + //Render the objects + for (i=obj_count-1;i>=0;i--) + { + int vis_effect=objs_to_render[i].vis_effect; + int objnum = objs_to_render[i].objnum; + if (vis_effect) + DrawVisEffect (&VisEffects[objnum]); + else + RenderObject (&Objects[objnum]); + } + // Render snows + rend_SetZBufferWriteMask(0); + for (i=0;ithreshold) + { + Check_terrain_portal=0; + } + else + { + Check_terrain_portal=1; + } + } + + if( top < 0 ) + { + top=0; + } + if ((right == -1) || right > render_width) + right = render_width-1; + if ((bot == -1) || bot > render_height) + bot = render_height-1; + Clip_scale_left = left; + Clip_scale_right = right; + Clip_scale_top = top; + Clip_scale_bot = bot; + if (!Terrain_sky.textured) + { + rend_FillRect(Terrain_sky.sky_color,left,top,right+1,bot+1); + } + + rend_SetFlatColor (Terrain_sky.sky_color); + View_mode=GetFunctionMode(); + + // Set this so we don't do reentrant rendering between terrain/mine + Terrain_from_mine=from_mine; + +#ifndef NEWEDITOR + // See if we're supposed change the fog plane distance based on our framerate + if (View_mode !=EDITOR_MODE && ManageFramerate) + { + float fps=GetFPS(); + if (fpsTERRAIN_SIZE) + { + Detail_settings.Terrain_render_distance-=(float)(TERRAIN_SIZE/4); + } + } + else if (fps>MinAllowableFramerate+1) + { + if (Detail_settings.Terrain_render_distance<60*TERRAIN_SIZE) + { + Detail_settings.Terrain_render_distance+=(float)(TERRAIN_SIZE/4); + } + } + } +#endif + + #ifndef NEWEDITOR + const float kTerrainRenderDistance = Detail_settings.Terrain_render_distance; + #else + const float kTerrainRenderDistance = 1200.0f; + #endif + VisibleTerrainZ = kTerrainRenderDistance * Matrix_scale.z; + Far_fog_border = VisibleTerrainZ; + + // Set up our z wall + g3_SetFarClipZ( VisibleTerrainZ ); + + //Get the viewer position & orientation + vector viewer_eye; + matrix viewer_orient; + g3_GetViewPosition( &viewer_eye ); + g3_GetViewMatrix( &viewer_orient ); + + // Get all of the cells visible to us + int nt = GetVisibleTerrain(&viewer_eye,&viewer_orient); + + // Set this to really far away so our sky can render + g3_SetFarClipZ(60000); + rend_SetFogState(0); + + // Draw the sky + DrawSky(&viewer_eye,&viewer_orient); + + //// Set up our z wall + rend_SetZBufferState(1); + rend_SetZBufferWriteMask (1); + g3_SetFarClipZ( VisibleTerrainZ ); + +#ifndef NEWEDITOR + if ((Terrain_sky.flags & TF_FOG) && (UseHardware || (!UseHardware && Lighting_on))) + { + rend_SetZValues(0,VisibleTerrainZ); + rend_SetFogState (1); + rend_SetFogBorders (VisibleTerrainZ*Terrain_sky.fog_scalar,Far_fog_border); + rend_SetFogColor(Terrain_sky.fog_color); + } + else +#endif + { + rend_SetZValues(0,5000); + } + + // And display! + if( nt > 0 ) + { + DisplayTerrainList( nt ); + } + + // Draw rooms + RenderTerrainRooms(); + + // Show objects + if( nt<1 || UseHardware ) + { + RenderAllTerrainObjects(); + } + + mprintf_at((2,5,0,"Objs Drawn=%5d",Terrain_objects_drawn)); + Last_terrain_render_time=Gametime; +} + +// Draws a segment of lightning that is always facing you +// Vectors are in world coords +void DrawLightningSegment (vector *from,vector *to) +{ + vector src_vecs[2],world_vecs[6]; + g3Point rot_src_pnts[2],world_points[6],*pntlist[6]; + static float alphas[]={0.3f,1.0f,0.3f,0.3f,1.0f,0.3f}; + src_vecs[0]=*from; + src_vecs[1]=*to; + + g3_RotatePoint (&rot_src_pnts[0],&src_vecs[0]); + g3_RotatePoint (&rot_src_pnts[1],&src_vecs[1]); + if (rot_src_pnts[0].p3_codes & rot_src_pnts[1].p3_codes) + return; // Don't draw because both points are off screen + vector rvec = Viewer_object->orient.rvec*10; + + // Put all points so that they face the viewer + world_vecs[0]=src_vecs[0]-rvec; + world_vecs[1]=src_vecs[0]; + world_vecs[2]=src_vecs[0]+rvec; + world_vecs[3]=src_vecs[1]+rvec; + world_vecs[4]=src_vecs[1]; + world_vecs[5]=src_vecs[1]-rvec; + for (int i=0;i<6;i++) + { + g3_RotatePoint (&world_points[i],&world_vecs[i]); + world_points[i].p3_flags|=PF_RGBA; + world_points[i].p3_r=.2f; + world_points[i].p3_g=.4f; + world_points[i].p3_b=1.0f; + world_points[i].p3_a=alphas[i]; + pntlist[i]=&world_points[i]; + } + rend_SetTextureType (TT_FLAT); + rend_SetLighting (LS_GOURAUD); + rend_SetAlphaType (AT_SATURATE_VERTEX); + rend_SetColorModel (CM_RGB); + g3_ProjectPoint (&world_points[1]); + g3_ProjectPoint (&world_points[4]); + rend_DrawSpecialLine (&world_points[1],&world_points[4]); + g3_DrawPoly (6,pntlist,0); +} +#define PUSH_LIGHTNING_TREE(f,l,sp) {froms[si]=f; stack_level[si]=l; splits[si]=sp; si++;} +#define POP_LIGHTNING_TREE() {si--; cur_from=froms[si]; level=stack_level[si]; cur_splits=splits[si];} +// Draws an entire strip of lightning +void DrawLightning(void) +{ + angvec player_angs; + matrix mat; + vector froms[50]; + int si=0,level; + int stack_level[50]; + int splits[50]; + int cur_splits=0; + int new_heading; + float scalar; + scalar=((ps_rand()%1000)-500)/500.0; + scalar*=15000; + vm_ExtractAnglesFromMatrix(&player_angs,&Viewer_object->orient); + new_heading=(player_angs.h+(int)scalar)%65536; + vm_AnglesToMatrix (&mat,0,new_heading,0); + // Put the starting point way up in the air + float ylimit=(-(Viewer_object->pos.y*2))+(ps_rand()%400); + vector cur_from=Viewer_object->pos+(mat.fvec*4000); + vector new_vec; + cur_from.y+=800.0f; + cur_from.y+=(ps_rand()%100); + // Set some states + + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (.5 * 255); + rend_SetLighting(LS_NONE); + int bm_handle; + + // See if we should drawn an origin bitmap + if (ps_rand()%3) + { + // Pick an origin bitmap + if (ps_rand()%2) + bm_handle=Fireballs[LIGHTNING_ORIGIN_INDEXA].bm_handle; + else + bm_handle=Fireballs[LIGHTNING_ORIGIN_INDEXB].bm_handle; + //Draw the origin bitmap + int size=300+(ps_rand()%200); + g3_DrawRotatedBitmap (&cur_from,0,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + } + PUSH_LIGHTNING_TREE (cur_from,0,0) + while (si>0) + { + POP_LIGHTNING_TREE() + ASSERT (level<50); + float x_adjust=((ps_rand()%200)-100)/100.0; + float y_adjust=.3+((ps_rand()%100)/100.0); + + new_vec=cur_from; + new_vec+=x_adjust*(mat.rvec*70); + new_vec-=y_adjust*(mat.uvec*100); + DrawLightningSegment (&cur_from,&new_vec); + if (cur_from.yp3_flags = PF_RGBA|PF_ORIGPOINT; + g3_CodePoint(p); + p->p3_a=.3f; + p->p3_r=r; + p->p3_g=g; + p->p3_b=b; + } + pntlist[0]=&pnt[0]; + pntlist[1]=&pnt[1]; + pntlist[2]=&pnt[2]; + g3_DrawPoly (3,pntlist,0); + } + // Draw bottom part + for (int i=1;i<5;i++) + { + for (t=0;tp3_flags = PF_RGBA|PF_ORIGPOINT; + g3_CodePoint(p); + p->p3_a=.3f; + p->p3_r=r; + p->p3_g=g; + p->p3_b=b; + pntlist[k]=p; + + } + g3_DrawPoly (4,pntlist,0); + } + } +} +// Draws the gouraud sky +void DrawGouraudSky(void) +{ + int t,k,tw; + float sr,sg,sb,hr,hg,hb; + + g3Point pnt[4],*pntlist[6]; + g3UVL uvls[10]; +#ifndef MACINTOSH + if (Terrain_sky.sky_color==Terrain_sky.horizon_color) + return; // No sense in drawing anything +#endif + rend_SetTextureType (TT_FLAT); + rend_SetColorModel (CM_RGB); + rend_SetAlphaType (AT_ALWAYS); + // figure out colors for sky +#ifdef MACINTOSH +// if(Terrain_sky.dome_texture) + { + if((Terrain_sky.flags & TF_FOG) && Detail_settings.Fog_enabled) + { + sr=(float)GR_COLOR_RED(Terrain_sky.fog_color)/255.0; + sg=(float)GR_COLOR_GREEN(Terrain_sky.fog_color)/255.0; + sb=(float)GR_COLOR_BLUE(Terrain_sky.fog_color)/255.0; + hr=(float)GR_COLOR_RED(Terrain_sky.fog_color)/255.0; + hg=(float)GR_COLOR_GREEN(Terrain_sky.fog_color)/255.0; + hb=(float)GR_COLOR_BLUE(Terrain_sky.fog_color)/255.0; + } else { + sr=(float)GR_COLOR_RED(Terrain_sky.sky_color)/255.0; + sg=(float)GR_COLOR_GREEN(Terrain_sky.sky_color)/255.0; + sb=(float)GR_COLOR_BLUE(Terrain_sky.sky_color)/255.0; + hr=(float)GR_COLOR_RED(Terrain_sky.sky_color)/255.0; + hg=(float)GR_COLOR_GREEN(Terrain_sky.sky_color)/255.0; + hb=(float)GR_COLOR_BLUE(Terrain_sky.sky_color)/255.0; + } + } +// else +#else + { + sr=(float)GR_COLOR_RED(Terrain_sky.sky_color)/255.0; + sg=(float)GR_COLOR_GREEN(Terrain_sky.sky_color)/255.0; + sb=(float)GR_COLOR_BLUE(Terrain_sky.sky_color)/255.0; + hr=(float)GR_COLOR_RED(Terrain_sky.horizon_color)/255.0; + hg=(float)GR_COLOR_GREEN(Terrain_sky.horizon_color)/255.0; + hb=(float)GR_COLOR_BLUE(Terrain_sky.horizon_color)/255.0; + } +#endif + for (t=0;tp3_flags = PF_RGBA|PF_ORIGPOINT; + g3_CodePoint(p); + pntlist[k]=p; + p->p3_uvl=uvls[k]; + + } + g3_DrawPoly (4,pntlist,0); + } +} +// Draws the sky textures +void DrawTexturedSky(void) +{ + int t,k,tw; + float sr,sg,sb,hr,hg,hb; + + // Change terrain sky if needed + int dome_bm=GetTextureBitmap(Terrain_sky.dome_texture,0); + + g3Point pnt[6],*pntlist[6]; + g3UVL uvls[10]; + + rend_SetWrapType (WT_WRAP); + rend_SetTextureType (TT_PERSPECTIVE); + rend_SetColorModel(CM_MONO); + rend_SetAlphaType (ATF_TEXTURE); + g3_SetTriangulationTest(1); + + // Draw top part + for (t=0;tp3_flags = PF_UV|PF_L|PF_ORIGPOINT; + g3_CodePoint(p); + p->p3_uvl=uvls[k]; + p->p3_l=1; + } + + #if (defined(EDITOR) || defined(NEWEDITOR)) + ddgr_color oldcolor; + if (TSearch_on) + { + rend_SetPixel (GR_RGB(0,255,0),TSearch_x,TSearch_y); + oldcolor = rend_GetPixel(TSearch_x,TSearch_y); //will be different in 15/16-bit color + } + #endif + + pntlist[0]=&pnt[0]; + pntlist[1]=&pnt[1]; + pntlist[2]=&pnt[2]; + g3_DrawPoly(3,pntlist,dome_bm); + + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (TSearch_on) + { + ddgr_color newcolor=rend_GetPixel(TSearch_x,TSearch_y); + if (newcolor != oldcolor) + { + TSearch_seg = t; + TSearch_found_type=TSEARCH_FOUND_SKY_DOME; + } + } + #endif + } + + // Draw bottom part + for (int i=1;i<4;i++) + { + for (t=0;tp3_flags=PF_UV|PF_L|PF_ORIGPOINT; + g3_CodePoint(p); + pntlist[k]=p; + p->p3_uvl=uvls[k]; + p->p3_l=1; + } + #if (defined(EDITOR) || defined(NEWEDITOR)) + ddgr_color oldcolor; + if (TSearch_on) + { + rend_SetPixel (GR_RGB(0,255,0),TSearch_x,TSearch_y); + oldcolor = rend_GetPixel(TSearch_x,TSearch_y); //will be different in 15/16-bit color + } + #endif + + g3_DrawPoly (4,pntlist,dome_bm); + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (TSearch_on) + { + ddgr_color newcolor=rend_GetPixel(TSearch_x,TSearch_y); + if (newcolor != oldcolor) + { + TSearch_seg = t; + TSearch_found_type=TSEARCH_FOUND_SKY_DOME; + } + } + #endif + } + } + // Now draw band + rend_SetTextureType (TT_FLAT); + rend_SetColorModel (CM_RGB); + rend_SetAlphaType (AT_ALWAYS); + + // figure out colors for sky +#ifdef MACINTOSH +// if(Terrain_sky.dome_texture) + { + if((Terrain_sky.flags & TF_FOG) && Detail_settings.Fog_enabled) + { + sr=(float)GR_COLOR_RED(Terrain_sky.fog_color)/255.0; + sg=(float)GR_COLOR_GREEN(Terrain_sky.fog_color)/255.0; + sb=(float)GR_COLOR_BLUE(Terrain_sky.fog_color)/255.0; + hr=(float)GR_COLOR_RED(Terrain_sky.fog_color)/255.0; + hg=(float)GR_COLOR_GREEN(Terrain_sky.fog_color)/255.0; + hb=(float)GR_COLOR_BLUE(Terrain_sky.fog_color)/255.0; + } else { + sr=(float)GR_COLOR_RED(Terrain_sky.sky_color)/255.0; + sg=(float)GR_COLOR_GREEN(Terrain_sky.sky_color)/255.0; + sb=(float)GR_COLOR_BLUE(Terrain_sky.sky_color)/255.0; + hr=(float)GR_COLOR_RED(Terrain_sky.sky_color)/255.0; + hg=(float)GR_COLOR_GREEN(Terrain_sky.sky_color)/255.0; + hb=(float)GR_COLOR_BLUE(Terrain_sky.sky_color)/255.0; + } + } +// else +#else + { + sr=(float)GR_COLOR_RED(Terrain_sky.sky_color)/255.0; + sg=(float)GR_COLOR_GREEN(Terrain_sky.sky_color)/255.0; + sb=(float)GR_COLOR_BLUE(Terrain_sky.sky_color)/255.0; + hr=(float)GR_COLOR_RED(Terrain_sky.horizon_color)/255.0; + hg=(float)GR_COLOR_GREEN(Terrain_sky.horizon_color)/255.0; + hb=(float)GR_COLOR_BLUE(Terrain_sky.horizon_color)/255.0; + } +#endif + + for (t=0;tp3_flags=PF_RGBA|PF_ORIGPOINT; + g3_CodePoint(p); + pntlist[k]=p; + p->p3_uvl=uvls[k]; + + } + g3_DrawPoly( 4, pntlist, 0 ); + } + g3_SetTriangulationTest(0); + rend_SetWrapType( WT_WRAP ); +} + +// Draws the sky textures +void DrawWireframeSky(void) +{ + int t,k,tw,i; + g3Point pnt[6]; + + // Draw top part + for (t=0;t0) + { + matrix mat; + vm_AnglesToMatrix (&mat,0,Terrain_sky.rotate_rate*Frametime*(65536.0/360.0),0); + vm_Orthogonalize(&mat); + for (int i=0;i9000.0 ) + { + streak_vec/=mag; + float norm=(mag/90000); + if (norm>1) + norm=1.0; + float revnorm=1.0-(norm*4); + if (revnorm<0) + revnorm=0; + float color_norm=(.6+(revnorm*.4)); + + lastpnt.p3_vec=starpnt.p3_vec+((norm*.75*90000)*streak_vec); + lastpnt.p3_flags=PF_RGBA; + lastpnt.p3_a=0; + starpnt.p3_a=color_norm; + + g3_CodePoint (&starpnt); + g3_CodePoint (&lastpnt); + rend_SetFlatColor (Terrain_sky.star_color[i]); + g3_DrawSpecialLine(&starpnt,&lastpnt); + } + else + { + starpnt.p3_flags=PF_RGBA; + if (!g3_CodePoint (&starpnt)) // only draw if this point is on screen + { + starpnt.p3_a=1.0; + + g3_ProjectPoint (&starpnt); + lastpnt=starpnt; + lastpnt.p3_sx++; + rend_SetFlatColor (Terrain_sky.star_color[i]); + rend_DrawSpecialLine (&starpnt,&lastpnt); + //rend_SetPixel (Terrain_sky.star_color[i],starpnt.p3_sx,starpnt.p3_sy); + } + } + if (Rendering_main_view) + Last_frame_stars[i]=starpnt.p3_vec; + } + rend_SetZBufferState (1); + g3_SetFarClipZ (60000); +} +// Draw the suns,moons,stars, horizon, etc +void DrawSky(vector *veye,matrix *vorient) +{ + int i,t; + + vector tempvec; + + rend_SetLighting (LS_GOURAUD); + rend_SetZBufferState (0); + rend_SetZBufferWriteMask (0); + + // If the sky is rotating, update the horizon vectors accordingly + if (Rendering_main_view && Terrain_sky.flags & TF_ROTATE_SKY && Terrain_sky.rotate_rate>0) + { + matrix mat; + vm_AnglesToMatrix (&mat,0,Terrain_sky.rotate_rate*Frametime*(65536.0/360.0),0); + vm_Orthogonalize(&mat); + for (i=0;i<6;i++) + { + for (t=0;ty * 0.5f; + vm_MatrixMulVector( &Temp_sky_vectors[t][i], &tempvec, vorient ); + + tempvec = Terrain_sky.horizon_vectors[t][i]; + tempvec.x += veye->x; + tempvec.z += veye->z; + tempvec.y += veye->y * 0.5f; + Temp_sky_vectors_unrotated[t][i] = tempvec; + } + } + float sr=(float)GR_COLOR_RED(Terrain_sky.sky_color)/255.0; + float sg=(float)GR_COLOR_GREEN(Terrain_sky.sky_color)/255.0; + float sb=(float)GR_COLOR_BLUE(Terrain_sky.sky_color)/255.0; + if( !Terrain_sky.textured ) + { + DrawGouraudSky(); + } + else + { + DrawTexturedSky(); + } + + if (Terrain_sky.flags & TF_STARS) + { + DrawStars(vorient); + } + + if (Terrain_sky.flags & TF_SATELLITES) + { + rend_SetWrapType (WT_CLAMP); + rend_SetColorModel(CM_MONO); + + // do satellites + for (i=0;i1.0) + { + r=str/maxc; + g=stg/maxc; + b=stb/maxc; + } + else + { + r=str; + g=stg; + b=stb; + } +#ifndef NEWEDITOR + // Draw halo + if (Terrain_sky.satellite_flags[i] & TSF_HALO) + { + rend_SetZBufferWriteMask (0); + DrawColoredRing (&tempvec,r,g,b,.4f,0,size*1.2,.3f,0,0); + rend_SetZBufferWriteMask (1); + } +#endif + // Draw satellite + if (tex->flags & TF_SATURATE) + rend_SetAlphaType (AT_SATURATE_TEXTURE); + else + rend_SetAlphaType (AT_CONSTANT+AT_TEXTURE); + rend_SetLighting(LS_NONE); + rend_SetAlphaValue (tex->alpha*255); + // Check to see if the user clicked on a satellite + #if (defined(EDITOR) || defined(NEWEDITOR)) + ddgr_color oldcolor; + if (TSearch_on) + { + rend_SetPixel(GR_RGB(0,255,0),TSearch_x,TSearch_y); + oldcolor = rend_GetPixel(TSearch_x,TSearch_y); //will be different in 15/16-bit color + } + #endif + + g3_SetTriangulationTest(1); + g3_DrawPlanarRotatedBitmap (&tempvec,&subvec,0,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + g3_SetTriangulationTest(0); + + // Draw atmosphere blend + if (UseHardware) + { + if (Terrain_sky.satellite_flags[i] & TSF_ATMOSPHERE) + { + angvec angs; + vm_ExtractAnglesFromMatrix (&angs,vorient); + DrawAtmosphereBlend (&tempvec,angs.b,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,sr,sg,sb); + } + } + + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (TSearch_on) + { + if (rend_GetPixel(TSearch_x,TSearch_y) != oldcolor) + { + TSearch_seg = i; + TSearch_found_type=TSEARCH_FOUND_SATELLITE; + } + + } + #endif + } + } +#ifndef NEWEDITOR + if ((Weather.flags & WEATHER_FLAGS_LIGHTNING) && Weather.lightning_sequence==2) + { + DrawLightning (); + Weather.lightning_sequence=0; + } + + if ((Weather.flags & WEATHER_FLAGS_LIGHTNING) && Weather.lightning_sequence==1) + { + DrawLightningSky(); + Weather.lightning_sequence=2; + } +#endif +// DrawCloudLayer(); + #if (!defined(RELEASE) || defined(NEWEDITOR)) + if (OUTLINE_ON(OM_SKY)) + DrawWireframeSky (); + #endif + + rend_SetAlphaValue (255); + rend_SetZBufferState (1); +} + +int AddRenderObjectToTerrainSeg (int n,int objnum); +// Checks to see if we should render objects and also sets which order the objects +// should be rendered in +void SortTerrainObjectsForRendering (int cellcount) +{ + int i,segnum; + object *obj; + #if (!defined(RELEASE) || defined(NEWEDITOR)) + for (i=0;itype==OBJ_NONE) + continue; + if (obj->render_type == RT_NONE) + continue; + if (! OBJECT_OUTSIDE(obj)) + continue; + // Ok, we know that we can see this point. If its segment is in our list + // then make this object draw right after this segment is drawn. Otherwise + // just draw it + segnum = CELLNUM(obj->roomnum); + ASSERT (segnum>=0 && segnum<=(TERRAIN_WIDTH*TERRAIN_DEPTH)); + if (Terrain_rotate_list[segnum]==TS_FrameCount) + { + AddRenderObjectToTerrainSeg (segnum,i); + } + else + AddRenderObjectToTerrainSeg (Terrain_list[cellcount-1].segment,i); + } +} +#if (defined(EDITOR) || defined(NEWEDITOR)) +#define CROSS_WIDTH 8.0 +#define CROSS_HEIGHT 8.0 +void TerrainDrawCurrentVert (int tcell) +{ + if (TerrainSelected[tcell]) + { + g3Point pnt; + pnt.p3_flags=0; + pnt.p3_vec=World_point_buffer[tcell].p3_vec; + g3_ProjectPoint(&pnt); //make sure projected + + rend_SetFlatColor (GR_RGB(0,250,0)); + rend_DrawLine(pnt.p3_sx-CROSS_WIDTH,pnt.p3_sy,pnt.p3_sx,pnt.p3_sy-CROSS_HEIGHT); + rend_DrawLine(pnt.p3_sx,pnt.p3_sy-CROSS_HEIGHT,pnt.p3_sx+CROSS_WIDTH,pnt.p3_sy); + rend_DrawLine(pnt.p3_sx+CROSS_WIDTH,pnt.p3_sy,pnt.p3_sx,pnt.p3_sy+CROSS_HEIGHT); + rend_DrawLine(pnt.p3_sx,pnt.p3_sy+CROSS_HEIGHT,pnt.p3_sx-CROSS_WIDTH,pnt.p3_sy); + } +} +#endif +#if (!defined(RELEASE) || defined(NEWEDITOR)) +#ifndef MACINTOSH +__inline void DrawTerrainOutline(int tcell,int nverts,g3Point **pointlist) +#else +void DrawTerrainOutline(int tcell,int nverts,g3Point **pointlist) +#endif +{ + int i; + g3Point tpnt[256]; + g3Point *tpnt_list[256]; + for (i=0;iz-a->z; +} +void SortTerrainList (int cellcount) +{ + int i,t,k; + int lod,simplemul; + int n[4]; + + for (i=0;i> 8; + + ax=az=simplemul; + + if (cx+ax>=TERRAIN_WIDTH) + ax=(TERRAIN_WIDTH-1)-cx; + if (cz+az>=TERRAIN_DEPTH) + az=(TERRAIN_DEPTH-1)-cz; + + n[0]=t; + n[1]=t+(TERRAIN_WIDTH*az); + n[2]=t+(az*TERRAIN_WIDTH)+ax; + n[3]=t+ax; + // This could be in a loop, but I unrolled it for speed + Terrain_seg[n[0]].mody=Terrain_seg[n[0]].y; + Terrain_seg[n[1]].mody=Terrain_seg[n[1]].y; + Terrain_seg[n[2]].mody=Terrain_seg[n[2]].y; + Terrain_seg[n[3]].mody=Terrain_seg[n[3]].y; + if (StateLimited || from_automap) // Setup for sorting later + { + int unique_id; + unique_id=Terrain_tex_seg[Terrain_seg[t].texseg_index].tex_index; + State_elements[i].facenum=i; + State_elements[i].sort_key=unique_id+(Terrain_seg[t].lm_quad<<24); + } + } + for (i=0;ieffect_info && (Viewer_object->effect_info->type_flags & EF_DEFORM)) + { + float val = (((ps_rand()%1000)-500.0f)/500.0f) * Viewer_object->effect_info->deform_time; + vector jitterVec = Terrain_alter_vec*(Viewer_object->effect_info->deform_range*val); + World_point_buffer[n[k]].p3_vec += jitterVec; + World_point_buffer[n[k]].p3_vecPreRot += jitterVec; + } + g3_CodePoint( &World_point_buffer[n[k]] ); + } + } + } +} + +// Puts a 1 in upperleft,lowerright if those triangles are visible +void TerrainCellVisible(int index,int *upper_left,int *lower_right) +{ + int seg=Terrain_list[index].segment; + int lod=Terrain_list[index].lod; + int simplemul=1<<((MAX_TERRAIN_LOD-1)-lod); + int cx,cz,smul_x,smul_z; + + vector tempv; + vector *corner[4]; + + cx=seg%TERRAIN_WIDTH; + cz=seg/TERRAIN_WIDTH; + if (cx+simplemul==TERRAIN_WIDTH) + smul_x=simplemul-1; + else + smul_x=simplemul; + if (cz+simplemul==TERRAIN_DEPTH) + smul_z=simplemul-1; + else + smul_z=simplemul; + + // Note - this is upper left and proceeds clockwise + corner[0] = &World_point_buffer[seg+(TERRAIN_WIDTH*smul_z)].p3_vec; + corner[1] = &World_point_buffer[seg+(TERRAIN_WIDTH*smul_z)+smul_x].p3_vec; + corner[2] = &World_point_buffer[seg].p3_vec; + corner[3] = &World_point_buffer[seg+smul_x].p3_vec; + + vm_GetPerp(&tempv,corner[0], corner[1], corner[2]); + if ((tempv * *corner[1]) < 0) + *upper_left=1; + else + *upper_left=0; + + // Now do lower right + vm_GetPerp(&tempv,corner[2], corner[1], corner[3]); + if ((tempv * *corner[1]) < 0) + *lower_right=1; + else + *lower_right=0; +} + +void DisplayTerrainList (int cellcount,bool from_automap) +{ + int total=0,on,t,i,lod,simplemul; + int bm_handle; + bool draw_lightmap=false; + int savecell; + int obj_to_draw; + Terrain_objects_drawn=0; + rend_SetWrapType (WT_WRAP); + if (!UseHardware) + rend_SetColorModel (CM_MONO); + else + { + rend_SetColorModel (CM_RGB); + rend_SetTextureType (TT_LINEAR); + rend_SetAlphaType (ATF_CONSTANT+ATF_TEXTURE); + rend_SetLighting(LS_NONE); + if (!StateLimited || UseMultitexture) + draw_lightmap=true; + } + RotateTerrainList (cellcount,from_automap); + if (!UseHardware) + { + SortTerrainList(cellcount); + SortTerrainObjectsForRendering (cellcount); + } + // If state limited, sort by texture + if (StateLimited || from_automap) + SortStates (State_elements,cellcount); + if (from_automap) + { + savecell=cellcount; + cellcount=0; + } + + for (i=0;itexseg_index]; + int rotation=texseg->rotation & 0x0F; + int tile=texseg->rotation >> 4; + int simplemul=1<<((MAX_TERRAIN_LOD-1)-lod); + int cx,cz,smul_x,smul_z; + #if (defined(EDITOR) || defined(NEWEDITOR)) + ddgr_color oldcolor; + #endif + cx=n%TERRAIN_WIDTH; + cz=n/TERRAIN_WIDTH; + int subx=cx % MAX_LOD_SIZE; + int subz=(MAX_LOD_SIZE-1)-((cz+(simplemul-1)) % MAX_LOD_SIZE); + if (cx+simplemul==TERRAIN_WIDTH) + smul_x=simplemul-1; + else + smul_x=simplemul; + if (cz+simplemul==TERRAIN_DEPTH) + smul_z=simplemul-1; + else + smul_z=simplemul; + + // Note - this is upper left and proceeds lockwise + tlist[0]=n+(TERRAIN_WIDTH*smul_z); + tlist[1]=n+(TERRAIN_WIDTH*smul_z)+(smul_x); + tlist[2]=n+(smul_x); + tlist[3]=n; + rend_SetOverlayType (OT_NONE); + for (close=0,i=0;i<4;i++) + { + base[i]=*((g3Point *)&World_point_buffer[tlist[i]]); + base[i].p3_flags|=(PF_L|PF_RGBA); + base[i].p3_l=Ubyte_to_float[Terrain_seg[tlist[i]].l]; + + // only do perspective if all the points are inside our range + if (!UseHardware) + { + if (base[i].p3_vec.zTerrain_texture_distance) + { + rend_SetTextureType (TT_FLAT); + int lightval=Ubyte_to_float [tseg->l]*(MAX_TEXTURE_SHADES-1); + int pix=*bm_data(bm_handle,0); + int fadepixel=(TexShadeTable16[lightval][pix>>8])+TexShadeTable8[lightval][pix & 0xFF]; + color=GR_16_TO_COLOR (fadepixel); + rend_SetFlatColor (color); + g3_DrawPoly(3,slist,0); + } + else + { + if (close) + rend_SetTextureType (TT_PERSPECTIVE); + else + rend_SetTextureType (TT_LINEAR); + + g3_DrawPoly(3,slist,bm_handle); + } + } +#if (!defined(RELEASE) || defined(NEWEDITOR)) + if (OUTLINE_ON(OM_TERRAIN)) + DrawTerrainOutline(n,3, slist); +#endif + // Now do lower right triangle + draw_lower_right: + if (!lower_right) + return 0; + src[0]=3; + src[1]=1; + src[2]=2; + for (lit=0,i=0;i<3;i++) + { + if (base[src[i]].p3_z<=Far_fog_border) + lit=1; + + slist[i]=&base[src[i]]; + } + if (!lit && Lighting_on) + { + rend_SetTextureType(TT_FLAT); + rend_SetFlatColor (0); + g3_DrawPoly(3,slist,0); + } + else + { + // If we're past our texturing distance, flat shade! + if (closest_z>Terrain_texture_distance) + { + rend_SetTextureType (TT_FLAT); + int lightval=Ubyte_to_float[tseg->l]*(MAX_TEXTURE_SHADES-1); + int pix=*bm_data(bm_handle,0); + int fadepixel=(TexShadeTable16[lightval][pix>>8])+TexShadeTable8[lightval][pix & 0xFF]; + color=GR_16_TO_COLOR (fadepixel); + rend_SetFlatColor (color); + g3_DrawPoly(3,slist,0); + } + else + { + if (close) + rend_SetTextureType (TT_PERSPECTIVE); + else + rend_SetTextureType (TT_LINEAR); + } + + g3_DrawPoly(3,slist,bm_handle); + } + + #if (!defined(RELEASE) || defined(NEWEDITOR)) + if (OUTLINE_ON(OM_TERRAIN)) + DrawTerrainOutline(n,3, slist); + #endif + #if (defined(EDITOR) || defined(NEWEDITOR)) + if (TSearch_on) + { + if (rend_GetPixel(TSearch_x,TSearch_y) != oldcolor) + { + TSearch_seg = n; + TSearch_found_type=TSEARCH_FOUND_TERRAIN; + } + } + #endif +#endif//__LINUX__ + */ + return 0; +} +// Draws the 2 triangles of the Terrainlist[index] (hardware) +int DrawTerrainTrianglesHardware (int index,int bm_handle,int upper_left,int lower_right) +{ + int i; + int cur_seg; + int n=Terrain_list[index].segment; + int lod=Terrain_list[index].lod; + int bottom_start,left_start,right_start; + int point_count=0; + int points_this_triangle=0; + + terrain_segment *tseg=&Terrain_seg[n]; + terrain_tex_segment *texseg=&Terrain_tex_seg[tseg->texseg_index]; + int rotator=texseg->rotation & 0x0F; + int tile=texseg->rotation >> 4; + int simplemul=1<<((MAX_TERRAIN_LOD-1)-lod); + int cx,cz,smul_x,smul_z; + cx=n%TERRAIN_WIDTH; + cz=n/TERRAIN_WIDTH; + + // Get lightmap coordinates + float lightmap_u=(cx%128)/128.0; + float lightmap_v=(128-((cz%128)+simplemul))/128.0; + float uvadjust; + int draw_big_square=0; + int subx=cx % MAX_LOD_SIZE; + int subz=(MAX_LOD_SIZE-1)-((cz+(simplemul-1)) % MAX_LOD_SIZE); + bool solid_square=1; + int testt=0,testr=0,testb=0,testl=0; + // Check to make sure we don't access memory that is off the map + if (cx+simplemul==TERRAIN_WIDTH) + { + smul_x=simplemul-1; + solid_square=0; + } + else + smul_x=simplemul; + if (cz+simplemul==TERRAIN_DEPTH) + { + solid_square=0; + smul_z=simplemul-1; + } + else + smul_z=simplemul; + // Build a list of points for our polygon. We must do it this way to + // prevent tjoint cracking + // Do simpler operation if at highest level of detail + if (lod==(MAX_TERRAIN_LOD-1)) + { + uvadjust=(simplemul/128.0); + cur_seg=n+(TERRAIN_WIDTH*smul_z); + base[0]= World_point_buffer[cur_seg]; + + cur_seg=n+(TERRAIN_WIDTH*smul_z)+smul_x; + base[1]= World_point_buffer[cur_seg]; + + cur_seg=n+smul_x; + base[2]= World_point_buffer[cur_seg]; + + base[3]= World_point_buffer[n]; + + base[0].p3_u=tile*TerrainUSpeedup[rotator][subz*LOD_ROW_SIZE+subx]; + base[0].p3_v=tile*TerrainVSpeedup[rotator][subz*LOD_ROW_SIZE+subx]; + base[1].p3_u=tile*TerrainUSpeedup[rotator][subz*LOD_ROW_SIZE+subx+1]; + base[1].p3_v=tile*TerrainVSpeedup[rotator][subz*LOD_ROW_SIZE+subx+1]; + base[2].p3_u=tile*TerrainUSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx+1]; + base[2].p3_v=tile*TerrainVSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx+1]; + base[3].p3_u=tile*TerrainUSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx]; + base[3].p3_v=tile*TerrainVSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx]; + + base[0].p3_u2=lightmap_u; + base[0].p3_v2=lightmap_v; + base[1].p3_u2=lightmap_u+uvadjust; + base[1].p3_v2=lightmap_v; + base[2].p3_u2=lightmap_u+uvadjust; + base[2].p3_v2=lightmap_v+uvadjust; + + base[3].p3_u2=lightmap_u; + base[3].p3_v2=lightmap_v+uvadjust; + } + else + { + uvadjust=(simplemul/128.0)/simplemul; + float uvmul=uvadjust*simplemul; + if (solid_square) + { + right_start=Terrain_list[index].top_count; + bottom_start=right_start+Terrain_list[index].right_count; + left_start=bottom_start+Terrain_list[index].bottom_count; + point_count=left_start+Terrain_list[index].left_count; + + for (i=0;ilm_quad]); + + // Make sure the triangle faces us and if so draw + // Upper left triangle + if (lod!=(MAX_TERRAIN_LOD-1)) + draw_big_square=1; + if (!upper_left && !draw_big_square) + goto draw_lower_right; + + if (lod==(MAX_TERRAIN_LOD-1)) + { + slist[0]=&base[0]; + slist[1]=&base[1]; + slist[2]=&base[3]; + points_this_triangle=3; + } + else + { + points_this_triangle=point_count; + } + + g3_DrawPoly(points_this_triangle,slist,bm_handle); +#if (!defined(RELEASE) || defined(NEWEDITOR)) + if (OUTLINE_ON(OM_TERRAIN)) + DrawTerrainOutline(n,points_this_triangle, slist); +#endif + // If we're LOD'd, we've already drawn our 1 polygon. Return! + if (draw_big_square) + return 0; + + // Now do lower right triangle + draw_lower_right: + if (!lower_right) + return 0; + slist[0]=&base[3]; + slist[1]=&base[1]; + slist[2]=&base[2]; + points_this_triangle=3; + + g3_DrawPoly(points_this_triangle,slist,bm_handle); + +#if (!defined(RELEASE) || defined(NEWEDITOR)) + if (OUTLINE_ON(OM_TERRAIN)) + DrawTerrainOutline(n,points_this_triangle, slist); +#endif + return 0; +} +// Draws the 2 triangles of the Terrainlist[index] (hardware) +int DrawTerrainTrianglesHardwareNoLight (int index,int bm_handle,int upper_left,int lower_right) +{ + int i; + int cur_seg; + int n=Terrain_list[index].segment; + int lod=Terrain_list[index].lod; + int bottom_start,left_start,right_start; + int point_count=0; + int points_this_triangle=0; + + terrain_segment *tseg=&Terrain_seg[n]; + terrain_tex_segment *texseg=&Terrain_tex_seg[tseg->texseg_index]; + int rotator=texseg->rotation & 0x0F; + int tile=texseg->rotation >> 4; + int simplemul=1<<((MAX_TERRAIN_LOD-1)-lod); + int cx,cz,smul_x,smul_z; + cx=n%TERRAIN_WIDTH; + cz=n/TERRAIN_WIDTH; + + int draw_big_square=0; + int subx=cx % MAX_LOD_SIZE; + int subz=(MAX_LOD_SIZE-1)-((cz+(simplemul-1)) % MAX_LOD_SIZE); + bool solid_square=1; + int testt=0,testr=0,testb=0,testl=0; + // Check to make sure we don't access memory that is off the map + if (cx+simplemul==TERRAIN_WIDTH) + { + smul_x=simplemul-1; + solid_square=0; + } + else + smul_x=simplemul; + if (cz+simplemul==TERRAIN_DEPTH) + { + solid_square=0; + smul_z=simplemul-1; + } + else + smul_z=simplemul; + // Build a list of points for our polygon. We must do it this way to + // prevent tjoint cracking + // Do simpler operation if at highest level of detail + if (lod==(MAX_TERRAIN_LOD-1)) + { + cur_seg=n+(TERRAIN_WIDTH*smul_z); + base[0]= World_point_buffer[cur_seg]; + + cur_seg=n+(TERRAIN_WIDTH*smul_z)+smul_x; + base[1]= World_point_buffer[cur_seg]; + + cur_seg=n+smul_x; + base[2]= World_point_buffer[cur_seg]; + + base[3]= World_point_buffer[n]; + + base[0].p3_u=tile*TerrainUSpeedup[rotator][subz*LOD_ROW_SIZE+subx]; + base[0].p3_v=tile*TerrainVSpeedup[rotator][subz*LOD_ROW_SIZE+subx]; + base[1].p3_u=tile*TerrainUSpeedup[rotator][subz*LOD_ROW_SIZE+subx+1]; + base[1].p3_v=tile*TerrainVSpeedup[rotator][subz*LOD_ROW_SIZE+subx+1]; + base[2].p3_u=tile*TerrainUSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx+1]; + base[2].p3_v=tile*TerrainVSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx+1]; + base[3].p3_u=tile*TerrainUSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx]; + base[3].p3_v=tile*TerrainVSpeedup[rotator][(subz+1)*LOD_ROW_SIZE+subx]; + } + else + { + if (solid_square) + { + right_start=Terrain_list[index].top_count; + bottom_start=right_start+Terrain_list[index].right_count; + left_start=bottom_start+Terrain_list[index].bottom_count; + point_count=left_start+Terrain_list[index].left_count; + + for (i=0;ilm_quad],MAP_TYPE_LIGHTMAP); + #if (!defined(RELEASE) || defined(NEWEDITOR)) + if (OUTLINE_ON(OM_TERRAIN)) + DrawTerrainOutline(n,points_this_triangle, slist); + #endif + // If we're LOD'd, we've already drawn our 1 polygon. Return! + if (draw_big_square) + return; + + // Now do lower right triangle + draw_lower_right: + if (!lower_right) + return; + slist[0]=&base[3]; + slist[1]=&base[1]; + slist[2]=&base[2]; + points_this_triangle=3; + + g3_DrawPoly(points_this_triangle,slist,TerrainLightmaps[tseg->lm_quad],MAP_TYPE_LIGHTMAP); + + #if (!defined(RELEASE) || defined(NEWEDITOR)) + if (OUTLINE_ON(OM_TERRAIN)) + DrawTerrainOutline(n,points_this_triangle, slist); + #endif +} +// Adds object obj to terrain segment n. +// This object will be rendered immediately following the rendering of this +// terrain segment +int AddRenderObjectToTerrainSeg (int n,int objnum) +{ + // Uses a linked list to keep track of what objects are in this segment + #if (!defined(RELEASE) || defined(NEWEDITOR)) + if (Terrain_seg_render_objs[n]==objnum) + return 0; + //New object points at first in list + render_next[objnum] = Terrain_seg_render_objs[n]; + //New object becomes first in list + Terrain_seg_render_objs[n] = objnum; + #endif + + return (0); +} diff --git a/Descent3/trigger.cpp b/Descent3/trigger.cpp new file mode 100644 index 000000000..3a646aae2 --- /dev/null +++ b/Descent3/trigger.cpp @@ -0,0 +1,359 @@ +/* + * $Logfile: /DescentIII/main/trigger.cpp $ + * $Revision: 26 $ + * $Date: 5/20/99 9:03p $ + * $Author: Matt $ + * + * Trigger management + * + * $Log: /DescentIII/main/trigger.cpp $ + * + * 26 5/20/99 9:03p Matt + * Changed trigger system so that collisions with rendered portals don't + * set off triggers on the portals. + * + * 25 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 24 3/23/99 5:52p Matt + * Added clutter activators for triggers + * + * 23 2/21/99 4:35p Chris + * Improving the level goal system... Not done. + * + * 22 2/15/99 7:47p Matt + * Added a default case to CheckTrigger + * + * 21 2/11/99 8:30p Matt + * Took out Int3() when couldn't find an object's parent + * + * 20 2/11/99 1:10p Matt + * Added functions to get & set a triggers enabled/disabled state + * + * 19 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 18 1/21/99 11:34a Matt + * Got rid of portal triggers. Since we don't have multi-face portals, a + * face trigger works fine for a portal. Also fixed a few editor/trigger + * bugs. + * + * 17 1/14/99 11:06a Matt + * Added names to triggers + * + * 16 1/08/99 2:56p Samir + * Ripped out OSIRIS1. + * + * 15 12/17/98 12:08p Jeff + * first checkin of new implementation of OSIRIS (old OSIRIS no longer + * works) + * + * 14 10/21/98 9:44p Matt + * Fixed bug that caused 1-shot portal triggers to not be found after the + * first time. + * + * 13 9/01/98 12:04p Matt + * Ripped out multi-face portal code + * + * 12 8/11/98 1:04p Matt + * Fixed bug that caused one-shot triggers to be marked as used even when + * hit by an object that wasn't a valid activator. + * + * 11 5/26/98 7:06p Samir + * took script.is_custom into account for triggers. + * + * 10 4/24/98 1:53a Samir + * added trigger init and free. + * + * 9 1/23/98 5:54p Samir + * New style script trigger support. + * + * 8 1/20/98 4:12p Samir + * New script housekeeping system. + * + * 7 12/23/97 1:33p Samir + * Added pserror.h + * + * 6 9/24/97 6:18p Samir + * Use script names instead of script id values to identify scripts. + * + * 5 9/22/97 12:24p Matt + * Fixed bugs with face flags for portal triggers + * + * 4 9/12/97 3:58p Matt + * Added support for activators and one-shot triggers + * + * 3 9/11/97 1:57p Matt + * Added trigger struct & a bunch of functions. System is not done yet, + * though. + * + * 2 9/08/97 10:01a Matt + * Ripped out old trigger code + * + * $NoKeywords: $ + */ + +#include "trigger.h" +#include "room.h" +#include "object.h" +//@$-#include "d3x.h" +#include "pserror.h" +#include "osiris_dll.h" +#include "levelgoal.h" + +#ifdef LINUX +#include +#endif + +#include + +//The maximum number of triggers that can be in the mine +#define MAX_TRIGGERS 100 + +//The list of triggers for the mine +trigger Triggers[MAX_TRIGGERS]; + +//The number of triggers currently in the mine +int Num_triggers=0; + + +void FreeTriggers(); + +// initializes trigger system +void InitTriggers() +{ + int i; + + for (i = 0; i < MAX_TRIGGERS; i++) + { + Triggers[i].roomnum = -1; + Triggers[i].facenum = -1; + Triggers[i].flags = 0; + + Triggers[i].osiris_script.script_id = -1; + Triggers[i].osiris_script.script_instance= NULL; + } + + Num_triggers = 0; + + atexit(FreeTriggers); +} + +//Free up all the triggers +void FreeTriggers() +{ + int i; + + for (i = 0; i < Num_triggers; i++) + FreeTriggerScript(&Triggers[i]); + + Num_triggers = 0; +} + + +//Returns a pointer to the trigger attached to the given room:face +//Generates an error if the trigger cannot be found +//Paramaters: roomnum,facenum - the room and face with the trigger +trigger *FindTrigger(int roomnum,int facenum) +{ + face *fp = &Rooms[roomnum].faces[facenum]; + trigger *tp; + int i; + + //Go through all the triggers & look for the requested one + for (i=0,tp=Triggers;iroomnum == roomnum) + if (tp->facenum == facenum) + return tp; + + Int3(); //Didn't find the requested trigger -- very bad! + + return NULL; +} + + +//Called to see if a trigger was tripped +void CheckTrigger(int roomnum,int facenum,object *objp,int event_type) +{ + room *rp = &Rooms[roomnum]; + face *fp = &rp->faces[facenum]; + + ASSERT((event_type == TT_PASS_THROUGH) || (event_type == TT_COLLIDE)); + + if (fp->flags & FF_HAS_TRIGGER) { + trigger *tp; + int type; + + tp = FindTrigger(roomnum,facenum); ASSERT(tp != NULL); + + if (tp == NULL) { + Int3(); //very bad! Get Matt. + return; + } + + //Check for dead or disabled trigger + if (tp->flags & (TF_DISABLED | TF_DEAD)) + return; + + //If portal trigger, make sure this is a pass-through event + if ((fp->portal_num != -1) && (event_type != TT_PASS_THROUGH)) + return; + + //Get the activator type for the object that hit the trigger + switch (objp->type) { + case OBJ_PLAYER: type = AF_PLAYER; break; + case OBJ_BUILDING: + case OBJ_ROBOT: type = AF_ROBOT; break; + case OBJ_CLUTTER: type = AF_CLUTTER; break; + case OBJ_WEAPON: { + object *parent = ObjGetUltimateParent(objp); + int parent_type = parent->type; + if (parent_type == OBJ_PLAYER) + type = AF_PLAYER_WEAPON; + else if (parent_type == OBJ_ROBOT || parent_type == OBJ_BUILDING) + type = AF_ROBOT_WEAPON; + else + return; //unknown parent for weapon + break; + } + default: + return; + } + + //Check if this object is a valid activator for this trigger + if (tp->activator & type) + { + mprintf((0,"Hit trigger %d\n",tp-Triggers)); + + //Execute this trigger's script + tOSIRISEventInfo ei; + ei.evt_collide.it_handle = objp->handle; + Osiris_CallTriggerEvent((tp-Triggers),EVT_COLLIDE,&ei); + + //If one-shot, kill this trigger, and mark face as destroyed + if (tp->flags & TF_ONESHOT) { + tp->flags |= TF_DEAD; + if (fp->portal_num == -1) //don't destroy a portal face + fp->flags |= FF_DESTROYED; + } + + if(tp->flags & TF_INFORM_ACTIVATE_TO_LG) + { + Level_goals.Inform(LIT_TRIGGER, LGF_COMP_ACTIVATE, tp - Triggers); + } + } + } +} + +//Enable or disable a trigger +void TriggerSetState(int trigger_num,bool enabled) +{ + trigger *tp = &Triggers[trigger_num]; + + if (enabled) + tp->flags &= ~TF_DISABLED; + else + tp->flags |= TF_DISABLED; +} + +//Determines if a trigger is enabled or disabled +//Returns TRUE if enabled, else false +bool TriggerGetState(int trigger_num) +{ + trigger *tp = &Triggers[trigger_num]; + + return ((tp->flags & TF_DISABLED) == 0); +} + +#ifdef EDITOR + +#include "editor\d3edit.h" + +// +// EDITOR functions follow +// + +//Create a new trigger +//Parameters: roomnum,facenum - where the trigger is +// flags - flags for this trigger +// activator - activator mask +// script - handle for the script for this trigger +//Returns: trigger number of new trigger, or -1 if error +int AddTrigger(char *name,int roomnum,int facenum,int activator,const char *script) +{ + room *rp = &Rooms[roomnum]; + face *fp = &rp->faces[facenum]; + trigger *tp; + + if (Num_triggers >= MAX_TRIGGERS) + return -1; + + if (strlen(name) > TRIG_NAME_LEN) { + Int3(); + return -1; + } + + tp = &Triggers[Num_triggers]; + + strcpy(tp->name,name); + tp->roomnum = roomnum; + tp->facenum = facenum; + tp->activator = activator; + tp->flags = 0; + + //Flag the face + fp->flags |= FF_HAS_TRIGGER; + + //Update count + Num_triggers++; + + //Update flag + World_changed = 1; + + //Everything ok + return Num_triggers-1; +} + +//Remove a trigger +//Paramters: trig_num - the trigger to delete +void DeleteTrigger(int trig_num) +{ + trigger *tp = &Triggers[trig_num]; + room *rp = &Rooms[tp->roomnum]; + face *fp = &rp->faces[tp->facenum]; + + //Clear the face flag + fp->flags &= ~FF_HAS_TRIGGER; + + //Free the script for this trigger + FreeTriggerScript(tp); + + //Move other triggers down in list + for (int i=trig_num;i + * + * $Log: /DescentIII/Main/vclip.cpp $ + * + * 26 5/10/00 2:43p Jeff + * fixed bug when paging in data if the tablefile specifies a full path to + * a primative instead of just a filename (bug in 3rd party tablefile + * editors) + * + * 25 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 24 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 23 5/07/99 7:23p Jason + * changes for low video memory + * + * 22 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 21 4/12/99 3:53p Jason + * fixed loading of erroneous vclips + * + * 20 3/02/99 1:17p Jason + * fixed loading of alpha numeric IFLs + * + * 19 2/10/99 3:47p Jason + * table filter changes + * + * 18 1/22/99 3:59p Jason + * added 256x256 textures to help with terrain skies + * + * 17 1/19/99 11:04a Jason + * fixed scaling problem + * + * 16 1/18/99 11:11a Jason + * made vclips be specular + * + * 15 10/18/98 2:59p Jason + * fixes for beta4 + * + * 14 10/14/98 5:50p Jason + * added lowmem modes for quartering textures + * + * 13 10/09/98 3:32p Kevin + * New memory library + * + * 12 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 11 5/19/98 5:43p Jason + * free all vclips at the end of the program + * + * 10 5/14/98 2:38p Jason + * made temp solution for vclip residency problem + * + * 9 5/14/98 12:56p Jason + * changes to help lower memory usage + * + * 8 2/26/98 2:47p Mark + * added assert + * + * 7 2/11/98 4:58p Jason + * fixed some memory problems with resizing and mipping + * + * 6 11/03/97 6:21p Jason + * fixed some problems with animating textures being remapped improperly + * + * 5 10/31/97 6:33p Jason + * fixed bug with whitespace in ifl files + * + * 4 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 3 9/04/97 12:03p Matt + * Got rid of warnings + * + * 2 8/20/97 6:09p Jason + * fixed out of bounds problem that bounds checker reported + * + * 16 5/13/97 2:24p Jason + * fixed a couple of animating texture bugs + * + * 15 5/12/97 11:41a Jason + * made game work (default) to 16bit no mip maps mode + * Saves us alot of memory + * + * 14 5/08/97 1:16p Jason + * made ChangeEndName work with device independant calls + * + * 13 5/01/97 1:05p Mark + * FROM JASON: Fixed stupid =/== bug in vclip loading + * + * 12 4/28/97 6:36p Jason + * fixed a bug with vclips + * + * 11 4/25/97 3:31p Jason + * implemented better memory management for vclips and bitmaps + * + * 10 4/24/97 5:42p Jason + * got fireball vclips working + * + * 9 3/03/97 6:21p Matt + * Changed cfile functions to use D3 naming convention + * + * 8 2/20/97 3:27p Jason + * made ifl reader ignore lines beginning with spaces + * + * 7 2/19/97 3:23p Jason + * added hooks to import animated iffs + * + * 6 2/19/97 11:37a Jason + * take gametime into consideration when animating a vclip + * + * 5 2/10/97 1:58p Jason + * made vclips free their bitmaps when they are deallocated + * + * 4 2/04/97 3:19p Jason + * better error checking + * + * 3 1/25/97 5:56p Jason + * changes to support new 8bit palettized textures and vclips + * + * 2 1/21/97 12:22p Jason + * added vclip support + * + * $NoKeywords: $ + */ + +#include +#include +#include "pstypes.h" +#include "pserror.h" +#include "bitmap.h" +#include "vclip.h" +#include "CFILE.H" +#include "mono.h" +#include "ddio.h" +#include "gametexture.h" +#include "texture.h" +#include +#include "ctype.h" +#include "mem.h" +#include "game.h" + +vclip GameVClips[MAX_VCLIPS]; +int Num_vclips=0; + +#define DEFAULT_FRAMETIME .07f + +#define VCLIP_VERSION 1 +// Frees all the memory used by vclips +void FreeAllVClips () +{ + mprintf ((0,"Freeing all vclips!\n")); + + for (int i=0;i0) + { + GameVClips[i].used=1; + FreeVClip (i); + } + } +} + +// Simply sets all vclips to unused +void InitVClips() +{ + for (int i=0;i0); + + GameVClips[num].used--; + if (GameVClips[num].used>0) + return; // other things are using this vclip + + if (!(GameVClips[num].flags & VCF_NOT_RESIDENT)) + { + for (int i=0;i=0); +} + +// Frees up the bitmaps used by a vclip +void FreeVClipResidency (int num) +{ + ASSERT (GameVClips[num].used>0); + + mprintf ((0,"Freeing vclip residency!\n")); + + if (!(GameVClips[num].flags & VCF_NOT_RESIDENT)) + { + for (int i=0;iused); + ASSERT (filename!=NULL); + + PageInVClip (num); + + outfile=(CFILE *)cfopen (filename,"wb"); + if (!outfile) + { + mprintf ((0,"Couldn't save vclip %s!\n",filename)); + return 0; + } + + // write out the header for this vclip + cf_WriteByte (outfile,127); + cf_WriteByte (outfile,(sbyte)VCLIP_VERSION); + + cf_WriteByte (outfile,vc->num_frames); + //cf_WriteFloat (outfile,vc->play_time); + cf_WriteFloat (outfile,vc->frame_time); + //cf_WriteInt (outfile,vc->flags); + //cf_WriteFloat (outfile,vc->light_value); + + // Now save each frame of this vclip + for (int i=0;inum_frames;i++) + { + if (bm_SaveBitmap (outfile,vc->frames[i]) != 1) + { + mprintf ((0,"Couldn't save frame %d of vclip %s!\n",i,filename)); + Int3(); + cfclose (outfile); + return 0; + } + } + + cfclose (outfile); + return 1; +} + +extern int Low_vidmem; +// Pages in a vclip if it needs to be +void PageInVClip (int vcnum) +{ + ASSERT (GameVClips[vcnum].used); + if (!(GameVClips[vcnum].flags & VCF_NOT_RESIDENT)) + return; + + int mipped=0; + int texture_size=GameVClips[vcnum].target_size; + vclip *vc=&GameVClips[vcnum]; + if (vc->flags & VCF_WANTS_MIPPED) + mipped=1; + + + CFILE *infile=(CFILE *)cfopen (vc->name,"rb"); + if (!infile) + { + // due to a bug in some 3rd party tablefile editors, full paths might + // have been used when they shouldn't have been + char *end_ptr,*start_ptr; + start_ptr = vc->name; + end_ptr = start_ptr + strlen(start_ptr) - 1; + while( (end_ptr>=start_ptr) && (*end_ptr!='\\') ) end_ptr--; + if(end_ptr < start_ptr) + { + mprintf ((0,"Couldn't load vclip %s!\n",vc->name)); + return; + } + + ASSERT(*end_ptr=='\\'); + end_ptr++; + + infile = (CFILE *)cfopen(end_ptr,"rb"); + if(!infile) + { + mprintf ((0,"Couldn't load vclip %s!\n",vc->name)); + return; + } + } + + mprintf ((0,"Paging in vclip %s!\n",vc->name)); + + ubyte start_val=cf_ReadByte (infile); + int version=0; + if (start_val!=127) + { + version=0; + vc->num_frames=start_val; + cf_ReadFloat (infile); + vc->frame_time=cf_ReadFloat (infile); + cf_ReadInt (infile); + cf_ReadFloat (infile); + vc->frame_time=DEFAULT_FRAMETIME; + } + else + { + version=cf_ReadByte (infile); + vc->num_frames=cf_ReadByte (infile); + vc->frame_time=cf_ReadFloat (infile); + vc->frame_time=DEFAULT_FRAMETIME; + } + + for (int i=0;inum_frames;i++) + { + int n=bm_AllocLoadBitmap (infile,mipped); + + ASSERT (n>0); + + int w,h; + + if (texture_size==NORMAL_TEXTURE) + { + w=TEXTURE_WIDTH; + h=TEXTURE_HEIGHT; + + #ifndef EDITOR +#ifdef MACINTOSH + if (Render_state.cur_texture_quality <= 1 || Low_vidmem) +#else + if (Mem_low_memory_mode || Low_vidmem) +#endif + { + w=TEXTURE_WIDTH/2; + h=TEXTURE_HEIGHT/2; + } + #endif + } + else if (texture_size==SMALL_TEXTURE) + { + // Make small textures a quarter of the size of normal textures + w=TEXTURE_WIDTH/2; + h=TEXTURE_HEIGHT/2; + + #ifndef EDITOR +#ifdef MACINTOSH + if (Render_state.cur_texture_quality <= 1 || Low_vidmem) +#else + if (Mem_low_memory_mode || Low_vidmem) +#endif + { + w=TEXTURE_WIDTH/4; + h=TEXTURE_HEIGHT/4; + } + #endif + } + else if (texture_size==TINY_TEXTURE) + { + // Make these tinys an eigth of the size of normal textures + w=TEXTURE_WIDTH/4; + h=TEXTURE_HEIGHT/4; + } + else if (texture_size==HUGE_TEXTURE) + { + // Make these tinys an eigth of the size of normal textures + w=TEXTURE_WIDTH*2; + h=TEXTURE_HEIGHT*2; + } + else + { + w=bm_w(n,0); + h=bm_h(n,0); + } + + // If differing size, resize! + if (w!=bm_w(n,0) || h!=bm_h(n,0)) + { + int dest_bm; + + dest_bm=bm_AllocBitmap (w,h,mipped*((w*h*2)/3)); + ASSERT (dest_bm>=0); + if (mipped) + GameBitmaps[dest_bm].flags |=BF_MIPMAPPED; + GameBitmaps[dest_bm].format=GameBitmaps[n].format; + + bm_ScaleBitmapToBitmap (dest_bm,n); + strcpy (GameBitmaps[dest_bm].name,GameBitmaps[n].name); + bm_FreeBitmap (n); + + n=dest_bm; + } + + ASSERT (n>=0); + vc->frames[i]=n; // assign frame to bitmap + } + + cfclose (infile); + + vc->flags &=~VCF_NOT_RESIDENT; + +} + +// Allocs and loads a vclip from the file named "filename" +// Returns -1 on error, index into GameVClip array on success +int AllocLoadVClip (char *filename,int texture_size,int mipped,int pageable,int format) +{ + char name[PAGENAME_LEN]; + int i; + + ASSERT (filename!=NULL); + + i=strlen (filename); + + if (filename[i-4]=='.' && filename[i-3]=='i' && filename[i-2]=='f' && filename[i-1]=='l') + return AllocLoadIFLVClip(IGNORE_TABLE(filename),texture_size,mipped,format); + + if (filename[i-4]=='.' && filename[i-3]=='a' && filename[i-2]=='b' && filename[i-1]=='m') + Int3(); // Get Jason + //return AllocLoadIFFAnimClip(filename,); + + ChangeVClipName (filename,name); + + // Check to see if this vclip already exists in memory + if ((i=FindVClipName(IGNORE_TABLE(name)))!=-1) + { + GameVClips[i].used++; + return i; + } + + //mprintf ((0,"Loading OAF vclip %s\n",name)); + + int vcnum=AllocVClip (); + + ASSERT (vcnum>=0); + strncpy (GameVClips[vcnum].name,name,PAGENAME_LEN); + + if (mipped) + GameVClips[vcnum].flags |=VCF_WANTS_MIPPED; + + GameVClips[vcnum].target_size=texture_size; + + + if (pageable==1) + return vcnum; + + + PageInVClip (vcnum); + + if (GameVClips[vcnum].num_frames<1) + { + FreeVClip(vcnum); + return -1; + } + + return vcnum; + +} + +// Allocs and loads a vclip from a 3DS ILS file +// Returns -1 on error, else index into GameVClips on success +// Argument texture means that this vclip is an animated texture and +// needs to have an 8bit version +int AllocLoadIFLVClip (char *filename,int texture_size,int mipped,int format) +{ + CFILE *infile; + char name[PAGENAME_LEN]; + unsigned int i,done=0; + + ASSERT (filename!=NULL); + + ChangeVClipName (filename,name); + + // Check to see if this vclip already exists in memory + if ((i=FindVClipName(IGNORE_TABLE(name)))!=-1) + { + GameVClips[i].used++; + return i; + } + + infile=(CFILE *)cfopen (filename,"rt"); + if (!infile) + { + mprintf ((0,"Couldn't load IFL vclip %s!\n",filename)); + return -1; + } + + mprintf ((0,"Loading IFL vclip %s\n",name)); + + int vcnum=AllocVClip (); + + ASSERT (vcnum>=0); + + vclip *vc=&GameVClips[vcnum]; + + + while (!done) + { + char curline[200]; + + if (cfeof(infile)) + { + done=1; + continue; + } + + // Read a line and parse it + cf_ReadString (curline,200,infile); + + if (curline[0]==';' || curline[1]==';' || curline[0]==' ' || curline[1]==' ') + continue; + if (!(isalnum(curline[0]))) + continue; + + else if (curline[0]=='$') + { + char new_command[50]; + + int i; + for(i = 0; curline[i+1] != '=' && i < 50; i++ ) + { + new_command[i]=curline[i+1]; + } + if (i==50) + { + Int3(); // bad command in IFL! + return -1; + } + + i++; // advance to data + + // parse data + if (!stricmp (new_command,"TIME")) + { + // Set play time + float play_time=atof(&curline[i]); + ASSERT (play_time>=0); + } + } + else + { + int lastslash=-1; + char bmname[200]; + + + for (i=0;inum_frames,filename); + cfclose (infile); + return -1; + } + + int w,h; + + if (texture_size==NORMAL_TEXTURE) + { + w=TEXTURE_WIDTH; + h=TEXTURE_HEIGHT; + } + else if (texture_size==SMALL_TEXTURE) + { + // Make small textures a quarter of the size of normal textures + w=TEXTURE_WIDTH/2; + h=TEXTURE_HEIGHT/2; + } + else if (texture_size==TINY_TEXTURE) + { + // Make these tinys an eigth of the size of normal textures + w=TEXTURE_WIDTH/4; + h=TEXTURE_HEIGHT/4; + } + else + { + w=bm_w(bm,0); + h=bm_h(bm,0); + } + + // If differing size, resize! + if (w!=bm_w(bm,0) || h!=bm_h(bm,0)) + { + int dest_bm; + + dest_bm=bm_AllocBitmap (w,h,mipped*((w*h)/3)); + ASSERT (dest_bm>=0); + if (mipped) + GameBitmaps[dest_bm].flags |=BF_MIPMAPPED; + GameBitmaps[dest_bm].format=GameBitmaps[bm].format; + + bm_ScaleBitmapToBitmap (dest_bm,bm); + strcpy (GameBitmaps[dest_bm].name,GameBitmaps[bm].name); + bm_FreeBitmap (bm); + + bm=dest_bm; + } + + + vc->frames[vc->num_frames]=bm; + vc->num_frames++; + } + } + + cfclose (infile); + + + if (vc->num_frames==0) + { + mprintf ((0,"vclip had no valid bitmap names!\n")); + FreeVClip (vcnum); + return -1; + } + + strcpy (vc->name,name); + return vcnum; + +} + + + +// gets the filename from a path, plus appends our .oaf extension +void ChangeVClipName (char *src,char *dest) +{ + int limit; + char path[256],ext[256],filename[256]; + + limit=PAGENAME_LEN-5; + + ddio_SplitPath (src,path,filename,ext); + + // Make sure we don't go over our name length limit + strncpy (dest,filename,limit); + + strcat (dest,".oaf"); +} +// Searches thru all vclips for a specific name, returns -1 if not found +// or index of vclip with name +int FindVClipName (char *name) +{ + int i; + + for (i=0;i0); + ASSERT (v>=0 && v=0); + + vclip *vc=&GameVClips[v]; + + int bm=vc->frames[frame%vc->num_frames]; + + return bm; + + +} + +// Loads an animation from an IFF ANIM file +int AllocLoadIFFAnimClip (char *filename,int texture) +{ +/* char name[PAGENAME_LEN]; + int i; + + ASSERT (filename!=NULL); + + ChangeVClipName (filename,name); + + if ((i=FindVClipName(name))!=-1) + { + GameVClips[i].used++; + return i; + } + + mprintf ((0,"Loading IFF vclip %s\n",name)); + + int vcnum=AllocVClip (); + + ASSERT (vcnum>=0); + + vclip *vc=&GameVClips[vcnum]; + + vc->num_frames=bm_AllocLoadIFFAnim (filename,vc->frames,0); + if (vc->num_frames==-1) + { + mprintf ((0,"Couldn't load vclip named %d!\n",name)); + FreeVClip (vcnum); + return -1; + } + + strcpy (vc->name,name); + + return vcnum;*/ + + return -1; +} + + + + + + diff --git a/Descent3/vclip.h b/Descent3/vclip.h new file mode 100644 index 000000000..20f35bb16 --- /dev/null +++ b/Descent3/vclip.h @@ -0,0 +1,93 @@ +#ifndef VCLIP_H + +#define VCLIP_H + +#include "pstypes.h" +#include "fix.h" +#include "manage.h" + + +#define MAX_VCLIPS 200 +#define VCLIP_MAX_FRAMES 50 + +#define VCF_NOT_RESIDENT 1 +#define VCF_WANTS_MIPPED 2 + +typedef struct +{ + char name[PAGENAME_LEN]; + short num_frames; + short *frames; // bitmap indices + float frame_time; //time (in seconds) of each frame + int flags; + ubyte target_size; // what size this vclip should use (texture wise) + ubyte used; // Is this vclip in use? +} vclip; + +extern vclip GameVClips[MAX_VCLIPS]; +extern int Num_vclips; + +// Simply sets all vclips to unused +void InitVClips(); + +// Allocs a vclip for use +// Returns -1 on error +int AllocVClip (); + +// Frees a vclip +void FreeVClip (int num); + +// Saves a given video clip to a file +// Returns 1 if everything ok, 0 otherwise +// "num" is index into GameVClip array +int SaveVClip (char *filename,int num); + +// Allocs and loads a vclip from the file named "filename" +// Returns -1 on error, index into GameVClip array on success +// Argument texture means that this vclip is an animated texture and +// needs to have an 8bit version +int AllocLoadVClip (char *filename,int texture_size,int mipped,int pageable=0,int format=0); + +// Allocs and loads a vclip from a 3DS ILS file +// Returns -1 on error, else index into GameVClips on success +// Argument texture means that this vclip is an animated texture and +// needs to have an 8bit version +int AllocLoadIFLVClip (char *filename,int texture_size,int mipped,int format=0); + +// gets the filename from a path, plus appends our .oaf extension +void ChangeVClipName (char *src,char *dest); + +// Searches thru all vclips for a specific name, returns -1 if not found +// or index of vclip with name +int FindVClipName (char *name); + + +// Returns frame "frame" of vclip "vclip". Will mod the frame so that there +// is no overflow +int GetVClipBitmap (int vclip,int frame); + +// Loads an animation from an IFF ANIM file +int AllocLoadIFFAnimClip (char *filename,int texture); + +// Pages in a vclip if it needs to be +void PageInVClip (int vcnum); + + +// Frees up the bitmaps used by a vclip +void FreeVClipResidency (int vcnum); + +#endif + + + + + + + + + + + + + + diff --git a/Descent3/vibeinterface.h b/Descent3/vibeinterface.h new file mode 100644 index 000000000..533db9faf --- /dev/null +++ b/Descent3/vibeinterface.h @@ -0,0 +1,25 @@ +#ifndef VIBE_H_ +#define VIBE_H_ + +#include "application.h" +#include "vecmat_external.h" +#include "controls.h" + +// Initialize IntelliVIBE +void VIBE_Init(oeApplication *app); + +// Shutdown IntelliVIBE +void VIBE_Close(void); + +// Control functions +// Informs us of a quater-frame tick +void VIBE_DoQuaterFrame(bool first_frame); +void VIBE_WeaponFire(int weapon_index); +void VIBE_DoControls(game_controls *controls); +void VIBE_PlayerRespawn(void); +void VIBE_PlayerDeath(void); +void VIBE_DoForce(vector *force_vec); +void VIBE_DoPlayerDamage(float damage_amount); +void VIBE_DoLevelEnd(void); + +#endif diff --git a/Descent3/viseffect.cpp b/Descent3/viseffect.cpp new file mode 100644 index 000000000..013f39384 --- /dev/null +++ b/Descent3/viseffect.cpp @@ -0,0 +1,2490 @@ +/* + * $Logfile: /DescentIII/main/viseffect.cpp $ + * $Revision: 145 $ + * $Date: 4/19/00 5:26p $ + * $Author: Matt $ + * + * System for creating small visual effects visects + * + * $Log: /DescentIII/main/viseffect.cpp $ + * + * 145 4/19/00 5:26p Matt + * From Duane for 1.4 + * Changes to InitVisEffects() + * + * 144 3/21/00 9:58a Matt + * Changed to Mac-only the code that sets a variable number of vis effects + * based on texture quality. + * + * 143 3/20/00 2:33p Matt + * Merge of Duane's post-1.3 changes. + * Small change in freeing viseffect memory. + * Fixed botched change with sine wave code. + * + * 141 11/05/99 3:17p Jeff + * fixed visalloc bug for pc + * + * 140 10/22/99 2:38p Jay + * Update for the FinalBoss Laser Effect + * + * 139 10/21/99 9:29p Jeff + * B.A. Macintosh code merge + * + * 138 10/12/99 11:08a Jeff + * grey lightning effect + * + * 137 9/21/99 2:54p Jeff + * grey spark's want lighting + * + * 136 7/20/99 1:00p Jason + * added auto katmai support + * + * 135 5/17/99 6:11p Jason + * moved center of lightning effect + * + * 134 5/14/99 12:15p Jason + * fixed vis effect problem for luke + * + * 133 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 132 5/10/99 8:20a Chris + * Fixed bug with weather lightning effects and Non-render objects + * + * 131 5/10/99 1:53a Jason + * add blackshark explosion + * + * 130 5/05/99 6:30p Jason + * toned down mass driver effect + * + * 129 5/04/99 7:30p Jason + * fixed viseffect linking problem + * + * 128 5/02/99 4:14p Jason + * fixed bug with attached viseffects + * + * 127 5/02/99 1:36a Jason + * added moving object lighting viseffects + * + * 126 5/02/99 12:21a Jason + * fixed crash bug with napalm vis effects + * + * 125 5/01/99 4:58p Jason + * made real viseffects not get created if not seen + * + * 124 4/30/99 7:51p Jason + * fixed lighting coming from controlled viseffects + * + * 123 4/30/99 6:21p Jeff + * fixed autotile (Jason) + * + * 122 4/30/99 5:42p Jason + * changes for blast rings + * + * 121 4/28/99 11:28a Jason + * made real viseffects not cast light + * + * 120 4/26/99 7:42p Jason + * fixed spew effect + * + * 119 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 118 4/19/99 3:18p Jason + * made real vis effects work + * + * 117 4/19/99 3:47a Jeff + * fixed min/max for Linux + * + * 116 4/14/99 3:57a Jeff + * fixed case mismatch in #includes + * + * 115 4/13/99 12:16p Jason + * more tweaks for the mass driver + * + * 114 4/09/99 7:49p Jason + * yet more mass driver fixes + * + * 113 4/09/99 7:26p Jason + * added better mass driver effect + * + * 112 4/07/99 3:53p Jason + * fixed lightning autotile problem + * + * 111 4/06/99 6:25p Jason + * more mass driver tweaks + * + * 110 4/06/99 4:14p Jason + * made mass driver a little cooler + * + * 109 4/06/99 1:51p Jason + * added new mass driver effect + * + * 108 4/05/99 4:39p Jason + * added groovy new smoke trails + * + * 107 4/03/99 4:25p Jason + * sped up attached vis effects by a large amount + * + * 106 4/02/99 3:58p Jason + * sped up vis effect stuff + * + * 105 4/01/99 1:10p Jason + * made vis effects better for the lighting system + * + * 104 3/31/99 1:20p Kevin + * Fix for Demos and Gamegauge -- to deal with situations where time goes + * backwards + * + * 103 3/31/99 11:42a Jason + * added support for attached thick lightning + * + * 102 3/30/99 7:18p Jason + * made blast rings more noticeable + * + * 101 3/30/99 6:01p Jason + * added new procedural effect + * + * 100 3/30/99 5:19p Jason + * fixed saturation of thicklighting + * + * 99 3/29/99 7:30p Jason + * added cool new energy effect + * + * 98 3/27/99 2:15p Jason + * added better thick lightining + * + * 97 3/25/99 3:30p Jason + * + * 96 3/24/99 6:28p Jason + * fixed size with thick lightning + * + * 95 3/23/99 4:10p Jason + * more tweaks for line sparks + * + * 94 3/23/99 3:52p Jason + * fixed sine wave bug + * + * 93 3/23/99 12:50p Jason + * added line sparks + * + * 92 3/22/99 11:17a Jason + * Katmai enhancements + * + * 91 2/25/99 11:01a Matt + * Added new explosion system. + * + * 90 2/15/99 4:10p Jason + * made non-vis objects kill all their attach sounds/objects + * + * 89 2/09/99 2:39p Jason + * added destroyable faces + * + * 88 1/26/99 6:39p Jason + * added wall effects code + * + * 87 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 86 12/11/98 5:08p Jason + * changed mass driver effect + * + * 85 11/19/98 6:38p Jason + * made a better black shark effect + * + * 84 11/18/98 2:54p Jason + * added snow effect + * + * 83 11/10/98 3:28p Jason + * made dynamic lighting work more sensibly + * + * 82 10/21/98 12:47p Jason + * fixed stupid bug with my last rev + * + * 81 10/20/98 9:52p Jason + * took out assert + * + * 80 10/05/98 11:35p Matt + * Added assert + * + * 79 10/05/98 7:29p Jason + * added cool black smoke + * + * 78 9/30/98 2:26p Jason + * fixed billboard bug + * + * 77 9/30/98 12:59p Jason + * added some cool matcen effects + * + * 76 9/29/98 7:15p Jason + * added axis billboards + * + * 75 9/29/98 12:49p Jason + * worked on matcen effects and lightning + * + * 74 9/28/98 4:41p Jason + * made explosions give dynamic light + * + * 73 9/24/98 12:57p Jason + * more state limited optimizations + * + * 72 9/21/98 7:01p Jason + * miscellaneous visual tweaks + * + * 71 9/21/98 6:53p Sean + * took out stupid debug assert + * + * 70 9/18/98 8:23p Jason + * fixed insidious vis effect errors + * + * 69 9/18/98 11:18a Jason + * fixed miscellaneous bugs with camera system + * + * 68 9/17/98 6:08p Jason + * more tweaks for effects + * + * 67 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 66 9/15/98 6:39p Jason + * made medium smoke saturate + * + * 65 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 64 9/09/98 4:49p Jason + * tweaked black shark lightning effect + * + * 63 9/09/98 4:30p Jason + * fixed a remaining (and insidious) bug with gunpoint instancing + * + * 62 9/09/98 4:28p Jason + * added better weapon effects + * + * 61 8/17/98 12:10p Chris + * Fixed MAJOR bug in getting gunpoint positions + * + * 60 8/03/98 11:12a Jason + * fixed some zero foward vector errors + * + * 59 6/24/98 3:56p Jason + * added code for new omega cannon + * + * 58 6/23/98 3:34p Jason + * added cool lighting effect to gravity field + * + * 57 6/22/98 6:52p Jason + * changed gravity field effect somewhat + * + * 56 6/22/98 6:26p Jason + * added gravity field effect for weapons + * + * 55 6/12/98 5:14p Jason + * made viseffects deal with zbuffer better + * + * 54 6/10/98 4:47p Jason + * smoke trail test + * + * 53 6/02/98 11:02a Jason + * Post E3 Checkin + * + * 52 5/27/98 5:17p Jason + * fixed some bugs for the E3 Demo + * + * 51 5/26/98 6:54p Jason + * viseffect optimizations + * + * 50 5/26/98 6:36p Jason + * made particle viseffects not relink + * + * 49 5/15/98 11:22a Jason + * make smoke trail saturation settable by designers + * + * 48 5/13/98 12:05p Jason + * made napalm explosions bigger for bigger objects + * + * 47 5/06/98 1:25p Jason + * more viseffect tweaks + * + * 46 5/06/98 12:55p Jason + * did some vis effect optimizations + * + * 45 5/01/98 6:23p Jason + * made better puddle rain + * + * 44 4/30/98 6:45p Jason + * more changes for weather + * + * 43 4/30/98 3:39p Jason + * don't make non-billowingfireballs rotate + * + * 42 4/30/98 11:55a Jason + * weather tweaks + * + * 41 4/29/98 3:27p Jason + * added weather engine + * + * 40 4/29/98 12:50p Jason + * more rain changes + * + * 39 4/29/98 11:38a Jason + * added some weather effects (first pass) + * + * 38 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 37 4/20/98 3:58p Jason + * made viseffects attach to subobjects other than zero + * + * 36 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 35 4/17/98 12:45p Jason + * various changes for multiplayer + * + * 34 4/15/98 2:33p Jason + * changed some smoke trail stuff + * + * 33 4/15/98 12:22p Jason + * lots of miscellaneous stuff pertaining to lighting and vis effects + * + * 32 4/10/98 12:39p Jason + * added expanding explosion bitmaps + * + * 31 4/09/98 4:53p Jason + * fixed particle bug + * + * 30 4/07/98 3:31p Jason + * got particle effects working with weapons + * + * 29 4/07/98 12:54p Jason + * changes for viseffects and multiplayer dll + * + * 28 4/02/98 12:24p Jason + * trimmed some fat from our structures + * + * 27 3/31/98 2:22p Jason + * bug fix for dead vis effects + * + * 26 3/30/98 12:46a Jason + * sped up deletion of vis effects + * + * 25 3/09/98 1:00p Jason + * toned down explosion lighting + * + * 24 3/05/98 6:56p Jason + * temp fix for out of bound errors on viseffects + * + * 23 2/26/98 3:20p Jason + * changes for faster lighting + * + * 22 2/25/98 4:59p Jason + * got dynamic explosion lighting working with vis-effects + * + * 21 2/25/98 4:31p Jason + * changes for explosions + * + * 20 2/17/98 1:50p Jason + * fixed viseffect zbias problem + * + * 19 2/17/98 1:00p Jason + * fixed viseffect clipping + * + * 18 2/16/98 2:37p Jason + * added real viseffects + * + * 17 2/06/98 1:00p Jason + * did some state setting for blast rings + * + * 16 2/05/98 6:53p Jason + * added new weapon slot + * + * 15 2/05/98 2:54p Jason + * changes for explosions + * + * 14 2/05/98 12:37p Jason + * added more weapon effects + * + * 13 2/04/98 9:28p Jason + * added some new weapons effects + * + * 12 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + */ + +#include "viseffect.h" +#include "fireball.h" +#include "terrain.h" +#include "game.h" +#include "room.h" +#include "vclip.h" +#include "gametexture.h" +#include "object.h" +#include +#include +#include "PHYSICS.H" +#include "weapon.h" +#include "lighting.h" +#include "dedicated_server.h" +#include "player.h" +#include "config.h" +#include "weather.h" +#include "polymodel.h" +#include "psrand.h" +#include "mem.h" + +#ifdef __LINUX__ +#define min(a,b) ((ab)?a:b) +#endif + +//DAJ vis_effect VisEffects[max_vis_effects]; +//DAJ ushort VisDeadList[max_vis_effects]; +//DAJ static short Vis_free_list[max_vis_effects]; + +vis_effect *VisEffects = NULL; + +static short *Vis_free_list = NULL; +ushort *VisDeadList = NULL; + +ushort max_vis_effects = 0; + +int NumVisDead=0; +int Highest_vis_effect_index=0; +int Num_vis_effects=0; + +void ShutdownVisEffects() +{ + if(VisEffects) + mem_free(VisEffects); + if(VisDeadList) + mem_free(VisDeadList); + if(Vis_free_list) + mem_free(Vis_free_list); +} + +// Goes through our array and clears the slots out +void InitVisEffects() +{ + static ushort old_max_vis = 0; +#ifdef MACINTOSH + if(Render_state.cur_texture_quality == 0) { + max_vis_effects = 1024; + } else if(Render_state.cur_texture_quality == 1) { + max_vis_effects = 2048; + } else if(Render_state.cur_texture_quality == 2) { + max_vis_effects = 4096; + } else { + mprintf((2, "InitVisEffects: what the is this size\n")); + return; + } +#else + max_vis_effects = MAX_VIS_EFFECTS; //always peg to max on PC +#endif + + if(old_max_vis == max_vis_effects) + return; + + if(VisEffects != NULL) { + VisEffects = (vis_effect *)mem_realloc(VisEffects, sizeof(vis_effect)*max_vis_effects); + VisDeadList = (ushort *)mem_realloc(VisDeadList, sizeof(ushort)*max_vis_effects); + Vis_free_list = (short *)mem_realloc(Vis_free_list, sizeof(short)*max_vis_effects); + } + else if(VisEffects == NULL) { + VisEffects = (vis_effect *)mem_malloc(sizeof(vis_effect)*max_vis_effects); + VisDeadList = (ushort *)mem_malloc(sizeof(ushort)*max_vis_effects); + Vis_free_list = (short *)mem_malloc(sizeof(short)*max_vis_effects); + } + for (int i=0;iHighest_vis_effect_index){ + Highest_vis_effect_index=n; + } + + return n; +} + +// Frees up a viseffect for use +int VisEffectFree (int visnum) +{ + ASSERT (visnum>=0 && visnum<=max_vis_effects); + + Vis_free_list[--Num_vis_effects]=visnum; + VisEffects[visnum].type=VIS_NONE; + + if (visnum==Highest_vis_effect_index) + { + while (VisEffects[Highest_vis_effect_index].type!=VIS_NONE && Highest_vis_effect_index>0) + Highest_vis_effect_index--; + } + + return 1; +} + +int VisEffectInitType (vis_effect *vis) +{ + int ret=1; + + vis->size=Fireballs[vis->id].size; + vis->flags|=VF_USES_LIFELEFT; + vis->lifeleft=Fireballs[vis->id].total_life; + vis->lifetime=vis->lifeleft; + + if (Fireballs[vis->id].type==FT_EXPLOSION && vis->id!=CUSTOM_EXPLOSION_INDEX && vis->id!=NAPALM_BALL_INDEX) + vis->lighting_color=OPAQUE_FLAG|GR_RGB16(255,180,20); + + return ret; +} + + +//initialize a new viseffect. adds to the list for the given room +//returns the vis number +int VisEffectCreate(ubyte type,ubyte id,int roomnum,vector *pos) +{ + int visnum; + vis_effect *vis; + + if ((Game_mode & GM_MULTI) && Dedicated_server) + return -1; // Dedicated servers don't need to create vis effects + + if (ROOMNUM_OUTSIDE(roomnum)) + { + if (CELLNUM(roomnum) > TERRAIN_WIDTH*TERRAIN_DEPTH) + return -1; + if (CELLNUM(roomnum)<0) + return -1; + + if (Gametime-Last_terrain_render_time>5.0) + return -1; + } + else + { + ASSERT (roomnum>=0 && roomnum<=Highest_room_index && Rooms[roomnum].used); + + // Don't create viseffects in rooms we haven't seen in a while + if (Gametime-Rooms[roomnum].last_render_time>5.0) + return -1; + } + + // Find next free object + visnum = VisEffectAllocate(); + + if (visnum == -1) //no free objects + return -1; + + ASSERT(VisEffects[visnum].type == VIS_NONE); //make sure unused + + vis = &VisEffects[visnum]; + + ASSERT(vis->roomnum == -1); + + memset( vis, 0, sizeof(vis_effect) ); + + //Fill in fields + vis->type = VIS_FIREBALL; + vis->id = id; + vis->pos = *pos; + vis->flags = 0; + vis->phys_flags=0; + vis->roomnum=roomnum; + vis->movement_type=MT_NONE; + vis->attach_info.end_vertnum=-1; + vis->next=-1; + vis->prev=-1; + vis->lighting_color=0; + + ASSERT (roomnum!=-1); + + vis->creation_time = Gametime; + + //Initialize all the type-specific info + VisEffectInitType(vis); + + VisEffectLink (visnum,roomnum); + + return visnum; +} + +// Creates vis effects but has the caller set their parameters +//initialize a new viseffect. adds to the list for the given room +//returns the vis number +int VisEffectCreateControlled(ubyte type,object *parent,ubyte id,int roomnum,vector *pos,float lifetime,vector *velocity,int phys_flags,float size,float drag,float mass,bool isreal) +{ + int visnum; + vis_effect *vis; + static int napalm_id=-1; + + if (napalm_id==-1) + napalm_id=FindWeaponName ("Napalm"); + + if (isreal) + { + ASSERT (parent!=NULL); // You have a spew that has no parent object? + + if (ROOMNUM_OUTSIDE(roomnum)) + { + if (CELLNUM(roomnum) > TERRAIN_WIDTH*TERRAIN_DEPTH) + return -1; + if (CELLNUM(roomnum)<0) + return -1; + + if (Gametime-Last_terrain_render_time>5.0) + return -1; + } + else + { + ASSERT (roomnum>=0 && roomnum<=Highest_room_index && Rooms[roomnum].used); + + // Don't create real objects in rooms we haven't seen in a while + if (Gametime-Rooms[roomnum].last_render_time>5.0) + return -1; + } + + int objnum=CreateAndFireWeapon (pos,velocity,parent,napalm_id); + if (objnum>=0) + { + object *obj=&Objects[objnum]; + + obj->lifeleft=lifetime; + obj->lifetime=lifetime; + obj->movement_type = MT_PHYSICS; + + if (mass>0 && drag>0) + { + obj->mtype.phys_info.mass = mass; + obj->mtype.phys_info.drag = drag; + } + + if (size>0) + obj->size=size; + + obj->mtype.phys_info.velocity=*velocity; + + if (phys_flags) + obj->mtype.phys_info.flags=phys_flags; + + obj->ctype.laser_info.casts_light=false; + + } + + return objnum; + } + + visnum=VisEffectCreate (type,id,roomnum,pos); + + if (visnum<0) + return -1; // No vis effects free + + vis=&VisEffects[visnum]; + + vis->lifeleft=lifetime; + vis->lifetime=lifetime; + vis->movement_type = MT_PHYSICS; + vis->mass = mass; + vis->drag = drag; + + if (size>0) + vis->size=size; + + vis->velocity=*velocity; + + //float mag=vm_GetMagnitudeFast (&vis->velocity); + //mprintf ((0,"CREATION:Velocity mag is %f\n",mag)); + + if (phys_flags) + vis->phys_flags=phys_flags; + + if (ROOMNUM_OUTSIDE (roomnum)) + vis->phys_flags|=PF_NO_COLLIDE; + + vis->flags|=VF_NO_Z_ADJUST; + vis->lighting_color&=~(0x8000); + + + return visnum; +} + + +//link the viseffect into the list for its room +// Does nothing for effects over terrain +void VisEffectLink(int visnum,int roomnum) +{ + vis_effect *vis = &VisEffects[visnum]; + + ASSERT(visnum != -1); + + if (!ROOMNUM_OUTSIDE(vis->roomnum)) + ASSERT(roomnum>=0 && roomnum<=Highest_room_index); + + vis->roomnum = roomnum; + + if (ROOMNUM_OUTSIDE(vis->roomnum)) + return; + + vis->next = Rooms[roomnum].vis_effects; + Rooms[roomnum].vis_effects = visnum; + ASSERT(vis->next != visnum); + + vis->prev = -1; + + if (vis->next != -1) + VisEffects[vis->next].prev = visnum; +} + +// Unlinks a viseffect from a room +// Does nothing for terrain +void VisEffectUnlink(int visnum) +{ + vis_effect *vis = &VisEffects[visnum]; + + ASSERT(visnum != -1); + + if (ROOMNUM_OUTSIDE(vis->roomnum)) + return; + else + { + + room *rp = &Rooms[vis->roomnum]; + + if (vis->prev == -1) + rp->vis_effects = vis->next; + else + VisEffects[vis->prev].next = vis->next; + + if (vis->next != -1) + VisEffects[vis->next].prev = vis->prev; + + vis->roomnum = -1; + } +} + +//when an effect has moved into a new room, this function unlinks it +//from its old room and links it into the new room +void VisEffectRelink(int visnum,int newroomnum) +{ + ASSERT (visnum>=0 && visnumtype != VIS_NONE); + + VisEffectUnlink(visnum); + + vis->type = VIS_NONE; //unused! + vis->roomnum=-1; // zero it! + + VisEffectFree(visnum); +} + +// Frees all the objects that are currently in use +void FreeAllVisEffects () +{ + + for (int i=0;i=0) + { + vis_effect *vis=&VisEffects[visnum]; + + vis->movement_type=MT_PHYSICS; + vis->mass=500; + vis->drag=.001f; + vis->phys_flags|=PF_GRAVITY|PF_NO_COLLIDE; + + vis->velocity.x=(ps_rand()%100)-50; + vis->velocity.y=(ps_rand()%100); + vis->velocity.z=(ps_rand()%100)-50; + + vm_NormalizeVectorFast (&vis->velocity); + + vis->velocity*=20+(ps_rand()%10); + vis->velocity*=force_scalar; + vis->size=.7+((ps_rand()%10)*.04); + vis->flags|=VF_USES_LIFELEFT; + float lifetime=1+((ps_rand()%10)*.15); + vis->lifeleft=lifetime; + vis->lifetime=lifetime; + + if (color==0) + vis->lighting_color=GR_RGB16 (200+(ps_rand()%50),150+(ps_rand()%50),ps_rand()%50); + else + vis->lighting_color=color; + } + } +} + + +// Creates a some sparks that go in random directions +void CreateRandomSparks (int num_sparks,vector *pos,int roomnum,int which_index,float force_scalar) +{ + // Make more sparks if Katmai + if (Katmai) + num_sparks*=2; + + + // Create some sparks + for (int i=0;i=0) + { + vis_effect *vis=&VisEffects[sparknum]; + + vis->movement_type=MT_PHYSICS; + vis->mass=100; + vis->drag=.1f; + + vis->phys_flags|=PF_GRAVITY|PF_NO_COLLIDE; + + vis->velocity.x=(ps_rand()%100)-50; + vis->velocity.y=(ps_rand()%100); + vis->velocity.z=(ps_rand()%100)-50; + + vm_NormalizeVectorFast (&vis->velocity); + vis->velocity*=10+(ps_rand()%10); + vis->velocity*=force_scalar; + vis->size=.2+((ps_rand()%10)*.01); + vis->flags|=VF_USES_LIFELEFT; + float lifetime=1+((ps_rand()%10)*.15); + vis->lifeleft=lifetime; + vis->lifetime=lifetime; + } + } +} + +// Creates a some particles that go in random directions +void CreateRandomParticles (int num_sparks,vector *pos,int roomnum,int bm_handle,float size,float life) +{ + // Create some sparks + float tenth_life=life/10.0; + float tenth_size=size/10.0; + + for (int i=0;i=0) + { + vis_effect *vis=&VisEffects[sparknum]; + + + vis->movement_type=MT_PHYSICS; + vis->mass=100; + vis->drag=.1f; + + vis->phys_flags|=PF_GRAVITY|PF_NO_COLLIDE; + + vis->velocity.x=(ps_rand()%100)-50; + vis->velocity.y=(ps_rand()%100); + vis->velocity.z=(ps_rand()%100)-50; + + vm_NormalizeVectorFast (&vis->velocity); + vis->velocity*=10+(ps_rand()%10); + vis->size=size+(((ps_rand()%11)-5)*tenth_size); + vis->flags|=VF_USES_LIFELEFT; + float lifetime=life+(((ps_rand()%11)-5)*tenth_life); + vis->lifeleft=lifetime; + vis->lifetime=lifetime; + vis->custom_handle=bm_handle; + } + } +} + + + + +// Draws a weapons fading line +void DrawVisFadingLine (vis_effect *vis) +{ + float norm_time; + float time_live=Gametime-vis->creation_time; + float size=vis->size; + + int visnum=vis-VisEffects; + norm_time=time_live/vis->lifetime; + + if (norm_time>=1) + norm_time=.99999f; // don't go over! + + rend_SetAlphaType (AT_SATURATE_VERTEX); + rend_SetTextureType (TT_FLAT); + rend_SetLighting(LS_GOURAUD); + rend_SetColorModel (CM_RGB); + rend_SetOverlayType (OT_NONE); + + vector vecs[2]; + g3Point pnts[2]; + int i; + + vecs[0]=vis->pos; + vecs[1]=vis->end_pos; + + if (!(vis->flags & VF_WINDSHIELD_EFFECT)) // bash end position + { + vector vel=-vis->velocity; + vm_NormalizeVectorFast (&vel); + vecs[1]=vis->pos+(vel*vis->size); + } + + ddgr_color color=GR_16_TO_COLOR (vis->lighting_color); + int r=GR_COLOR_RED(color); + int g=GR_COLOR_GREEN(color); + int b=GR_COLOR_BLUE(color); + + for (i=0;i<2;i++) + { + g3_RotatePoint (&pnts[i],&vecs[i]); + pnts[i].p3_flags|=PF_RGBA; + pnts[i].p3_r=(r/255.0); + pnts[i].p3_g=(g/255.0);; + pnts[i].p3_b=(b/255.0);; + } + + if (vis->flags & VF_WINDSHIELD_EFFECT) + pnts[0].p3_a=.3f; + else + pnts[0].p3_a=1.0-norm_time; + + pnts[1].p3_a=0.0; + + g3_DrawSpecialLine (&pnts[0],&pnts[1]); + +} + +// Draws a blast ring vis effect +void DrawVisBlastRing(vis_effect *vis) +{ + vector inner_vecs[30],outer_vecs[30]; + g3Point inner_points[30],outer_points[30]; + float lifenorm=(vis->lifetime-vis->lifeleft)/vis->lifetime; + float cur_size=lifenorm*vis->size; + int i; + g3Point *pntlist[4]; + matrix orient; + vector fvec; + + if (vis->flags & VF_REVERSE) + { + lifenorm=1-lifenorm; + cur_size=lifenorm*vis->size; + } + + if (vis->flags & VF_PLANAR) + fvec=vis->end_pos; + else + { + fvec=Viewer_object->pos-vis->pos; + vm_NormalizeVectorFast (&fvec); + } + + if (vm_GetMagnitudeFast (&fvec)<.5) + return; + vm_VectorToMatrix (&orient,&fvec,NULL,NULL); + + int num_segments=20; + + int ring_increment=65536/num_segments; + int ring_angle=0; + + if (lifenorm>1.0) + lifenorm=1.0; + + rend_SetAlphaType(AT_SATURATE_TEXTURE_VERTEX); + rend_SetOverlayType(OT_NONE); + rend_SetTextureType(TT_LINEAR); + rend_SetLighting(LS_NONE); + rend_SetZBias(-1.0); + rend_SetZBufferWriteMask(0); + + ring_angle=0; + + for (i=0;ipos; + + outer_vecs[i]=orient.rvec*(ring_cos*cur_size); + outer_vecs[i]+=orient.uvec*(ring_sin*cur_size); + outer_vecs[i]+=vis->pos; + + g3_RotatePoint(&inner_points[i],&inner_vecs[i]); + g3_RotatePoint(&outer_points[i],&outer_vecs[i]); + outer_points[i].p3_flags|=PF_UV|PF_RGBA; + inner_points[i].p3_flags|=PF_UV|PF_RGBA; + + outer_points[i].p3_a=(1.0-lifenorm); + inner_points[i].p3_a=0; + inner_points[i].p3_l=1; + inner_points[i].p3_l=1; + } + + for (i=0;icustom_handle); + } + + rend_SetZBufferWriteMask (1); + rend_SetZBias(0); +} + +// Draws a raindrop on the players windshield +void DrawVisRainDrop (vis_effect *vis) +{ + float norm_time; + float time_live=Gametime-vis->creation_time; + float size=vis->size; + + int visnum=vis-VisEffects; + int bm_handle; + fireball *fb=&Fireballs[vis->id]; + + norm_time=time_live/vis->lifetime; + + if (norm_time>=1) + norm_time=.99999f; // don't go over! + + size*=(1-(norm_time/2)); + + bm_handle=fb->bm_handle; + + float val; + if (norm_time>.5) + val=1.0-((norm_time-.5)/.5); + else if (norm_time<.1) + val=norm_time/.1; + else + val=1.0; + + + rend_SetAlphaValue (val*.4*255); + rend_SetOverlayType (OT_NONE); + rend_SetWrapType (WT_CLAMP); + rend_SetLighting (LS_NONE); + + // Set position + vector pos; + + if (vis->id==RAINDROP_INDEX) + { + rend_SetZBufferState (0); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (val*.4*255); + + pos=Viewer_object->pos; + pos+=Viewer_object->orient.rvec*vis->pos.x; + pos+=Viewer_object->orient.uvec*vis->pos.y; + pos+=Viewer_object->orient.fvec*vis->pos.z; + g3_DrawRotatedBitmap (&pos,0,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + rend_SetZBufferState (1); + } + else + { + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (val*.2*255); + rend_SetZBufferWriteMask (0); + pos=vis->pos; + ASSERT(! ((vis->end_pos.x == 0.0) && (vis->end_pos.y == 0.0) && (vis->end_pos.z == 0.0))); + g3_DrawPlanarRotatedBitmap (&pos,&vis->end_pos,0,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + rend_SetZBufferWriteMask (1); + } + + rend_SetWrapType (WT_WRAP); + +} + + +// Draws a snowflake +void DrawVisSnowflake (vis_effect *vis) +{ + float norm_time; + float time_live=Gametime-vis->creation_time; + float size=vis->size; + + int visnum=vis-VisEffects; + int bm_handle; + fireball *fb=&Fireballs[vis->id]; + + norm_time=time_live/vis->lifetime; + + if (norm_time>=1) + norm_time=.99999f; // don't go over! + + //size*=(1-(norm_time/2)); + + bm_handle=fb->bm_handle; + + float val; + + val=1.0-(norm_time); + + rend_SetAlphaValue (val*.6*255); + rend_SetOverlayType (OT_NONE); + //rend_SetWrapType (WT_CLAMP); + rend_SetLighting (LS_NONE); + //rend_SetZBufferState (0); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + + ddgr_color color=GR_16_TO_COLOR(vis->lighting_color); + g3_DrawBitmap (&vis->pos,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color); + + //rend_SetZBufferState (1); + //rend_SetWrapType (WT_WRAP); + +} + +// Draws a lighting bolt from one area to another +// Velocity.x is how much randomness goes into drawing +// Velocity.y is the scalar that effects how many segments to draw +void DrawVisLightningBolt (vis_effect *vis) +{ + vector line_norm; + float line_mag; + int num_segs; + float lightning_mag; + + g3Point pnt1,pnt2; + + line_norm=vis->pos-vis->end_pos; + line_mag=vm_GetMagnitudeFast (&line_norm); + + line_norm/=line_mag; + + if (line_mag<1) + return; + + float alpha_norm; + + if (vis->flags & VF_EXPAND) + { + num_segs=line_mag*vis->velocity.y; + lightning_mag=vis->velocity.x; + + alpha_norm=vis->lifeleft/vis->lifetime; + } + else + { + num_segs=line_mag*vis->velocity.y; + lightning_mag=vis->velocity.x; + + alpha_norm=.7f; + + // Make it powerup up based on distance + if (line_mag>30 && (vis->flags & VF_NO_Z_ADJUST)) + { + float scalar=1.0-((line_mag-30)/150.0); + if (scalar<0) + return; + + alpha_norm*=scalar; + } + } + + if (num_segs<2) + return; + + // Set some states + + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_SATURATE_VERTEX); + rend_SetLighting(LS_NONE); + rend_SetZBufferWriteMask (0); + + if(vis->id==GRAY_LIGHTNING_BOLT_INDEX) + { + // get the color from the struct + rend_SetFlatColor (GR_16_TO_COLOR(vis->lighting_color)); + }else + { + rend_SetFlatColor (GR_RGB(10,60,200)); + } + + pnt1.p3_a=alpha_norm; + pnt2.p3_a=alpha_norm; + + vector vecs[50]; + + num_segs=min(num_segs,50); + + CreateLightningRodPositions (&vis->pos,&vis->end_pos,vecs,num_segs,lightning_mag,false); + + for (int i=0;ipos-vis->end_pos; + line_mag=vm_GetMagnitudeFast (&line_norm); + if (line_mag<.1) + return; + + line_norm/=line_mag; + + + float alpha_norm; + + if (vis->flags & VF_EXPAND) + alpha_norm=vis->lifeleft/vis->lifetime; + + num_segs=vis->velocity.x*line_mag; // /2 + line_norm/=vis->velocity.x; // *2 + + alpha_norm=vis->lifeleft/vis->lifetime; + + vm_VectorToMatrix (&mat,&line_norm,&vis->velocity,NULL); + rvec=mat.rvec*vis->velocity.y; // /4 + uvec=mat.uvec*vis->velocity.y; // /4 + + // Set some states + + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_SATURATE_VERTEX); + rend_SetLighting(LS_NONE); + rend_SetZBufferWriteMask (0); + rend_SetFlatColor (GR_RGB(10,60,200)); + int cur_sin=(vis-VisEffects)*5000; + + cur_sin+=(FrameCount*2000); + + base_from=vis->end_pos; + from=base_from; + + pnt1.p3_a=alpha_norm; + pnt2.p3_a=alpha_norm; + + for (int i=0;ip3_flags&PF_ORIGPOINT ); + ASSERT( top_point->p3_flags&PF_ORIGPOINT ); + vector deltaVec = bot_point->p3_vecPreRot - top_point->p3_vecPreRot; + vm_NormalizeVector( &deltaVec ); + + // get the vector from the camera to the top point + vector top = top_point->p3_vecPreRot - viewerPos; + vm_NormalizeVector( &top ); + + // calculate the vector out from the 'rod' that is facing the camera + vector rodNorm; + vm_CrossProduct( &rodNorm, &deltaVec, &top ); + vm_NormalizeVector( &rodNorm ); + + // get the offset vector + vector tempv = rodNorm * width; + + // setup the points + int i, codesAND = 0xFF; + for( i = 0; i < 4; ++i ) + { + float scale = (i == 0 || i == 3) ? -1.0f : 1.0f; + vector bbPt = (( i < 2 ) ? top_point->p3_vecPreRot : bot_point->p3_vecPreRot) + (tempv * scale); + + // initialize the point + codesAND &= g3_RotatePoint( &pnts[i], &bbPt ); + } + + if( codesAND ) + return 0; + + return 1; +} + + +// Draws a thick lighting bolt from one area to another +// Velocity.x is how many times to saturate +// Velocity.y is how fast the lightning moves across the v component +// Velocity.z is how many times the effect is tiled across the v component +// If Billboard_info.texture is not zero, it does autotiling +void DrawVisThickLightning (vis_effect *vis) +{ + //compute the corners of a rod. fills in vertbuf. + g3Point top_point,bot_point; + g3Point pnts[4],*pntlist[4]; + + g3_RotatePoint( &top_point, &vis->pos ); + g3_RotatePoint( &bot_point, &vis->end_pos ); + + if( !GetBillboardCorners( pnts, &top_point, &bot_point, vis->billboard_info.width ) ) + return; + + vector line_norm=vis->end_pos-vis->pos; + float line_mag=vm_GetMagnitudeFast (&line_norm); + + line_norm/=line_mag; + + if (line_mag<1) + return; + + int texnum=vis->custom_handle; + int bm_handle=GetTextureBitmap (texnum,0,true); + float tile_factor=vis->velocity.z; + float alpha_norm; + int i, codes_and; + + if (vis->flags & VF_EXPAND) + alpha_norm=vis->lifeleft/vis->lifetime; + else + alpha_norm=.7f; + + rend_SetOverlayType (OT_NONE); + rend_SetTextureType (TT_LINEAR); + rend_SetLighting (LS_FLAT_GOURAUD); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetWrapType (WT_WRAP); + rend_SetFlatColor (GR_16_TO_COLOR(vis->lighting_color)); + rend_SetAlphaValue (255*alpha_norm); + rend_SetZBufferWriteMask (0); + + for (i=0,codes_and=0xff;i<4;i++) + { + pntlist[i]=&pnts[i]; + } + + if (vis->billboard_info.texture) // do autotiling + { + float ratio=line_mag/(float)vis->billboard_info.width; + tile_factor*=ratio; + } + + pnts[0].p3_u=0; + pnts[0].p3_v=0; + + pnts[1].p3_u=1; + pnts[1].p3_v=0; + + pnts[2].p3_u=1; + pnts[2].p3_v=tile_factor; + + pnts[3].p3_u=0; + pnts[3].p3_v=tile_factor; + + float vchange=0; + + if (vis->velocity.y!=0) + { + int int_time=Gametime/vis->velocity.y; + float norm_time=Gametime-(int_time*vis->velocity.y); + norm_time/=vis->velocity.y; + + vchange=norm_time; + } + + for (i=0;i<4;i++) + { + pnts[i].p3_v+=vchange; + pnts[i].p3_flags |= PF_UV; + } + + // Now draw this thing! + for (i=0;ivelocity.x;i++) + { + g3_DrawPoly( 4, pntlist, bm_handle ); + } + + rend_SetZBufferWriteMask(1); +} + +// Draws a bitmap that can orient around an axis +void DrawVisAxisBillboard(vis_effect *vis) +{ + float norm_time,alpha_norm=1; + float uchange=0,vchange=0; + float time_live=Gametime-vis->creation_time; + float size=vis->size; + + int visnum=vis-VisEffects; + int bm_handle; + int i; + + // Get corners of this billboard + g3Point top_point,bot_point; + g3Point pnts[4],*pntlist[4]; + + g3_RotatePoint(&top_point,&vis->pos); + g3_RotatePoint(&bot_point,&vis->end_pos); + + if (!GetBillboardCorners (pnts,&top_point,&bot_point,vis->billboard_info.width)) + return; + + for (i=0;i<4;i++) + { + pnts[i].p3_flags|=PF_UV; + pntlist[i]=&pnts[i]; + } + + fireball *fb=&Fireballs[vis->id]; + + norm_time=time_live/vis->lifetime; + + if (vis->flags & VF_EXPAND) + alpha_norm=vis->lifeleft/vis->lifetime; + + if (norm_time>=1) + norm_time=0.99999f; // don't go over! + + if (vis->billboard_info.texture) // If its a texture, get image from texture + { + int texnum=vis->custom_handle; + if (GameTextures[texnum].flags & TF_ANIMATED) + { + vclip *vc=&GameVClips[GameTextures[texnum].bm_handle]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + } + else + bm_handle=GetTextureBitmap(texnum,0); + + if (GameTextures[texnum].flags & TF_SATURATE) + rend_SetAlphaType (AT_SATURATE_TEXTURE); + else + rend_SetAlphaType (ATF_CONSTANT+ATF_TEXTURE); + + rend_SetAlphaValue (alpha_norm*GameTextures[texnum].alpha*255); + + if (GameTextures[texnum].slide_u!=0) + { + int int_time=Gametime/GameTextures[texnum].slide_u; + float norm_time=Gametime-(int_time*GameTextures[texnum].slide_u); + norm_time/=GameTextures[texnum].slide_u; + + uchange=norm_time; + } + + if (GameTextures[texnum].slide_v!=0) + { + int int_time=Gametime/GameTextures[texnum].slide_v; + float norm_time=Gametime-(int_time*GameTextures[texnum].slide_v); + norm_time/=GameTextures[texnum].slide_v; + vchange=norm_time; + } + } + else + { + bm_handle=vis->custom_handle; + rend_SetAlphaType (ATF_CONSTANT+ATF_TEXTURE); + rend_SetAlphaValue (alpha_norm*255); + } + + rend_SetOverlayType (OT_NONE); + + rend_SetZBufferWriteMask (0); + + if (uchange==0 && vchange==0) + rend_SetWrapType (WT_CLAMP); + else + rend_SetWrapType (WT_WRAP); + + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetTextureType (TT_LINEAR); + + pnts[0].p3_u=0; + pnts[0].p3_v=0; + + pnts[1].p3_u=1; + pnts[1].p3_v=0; + + pnts[2].p3_u=1; + pnts[2].p3_v=1; + + pnts[3].p3_u=0; + pnts[3].p3_v=1; + + for (i=0;i<4;i++) + { + pnts[i].p3_u+=uchange; + pnts[i].p3_v+=vchange; + } + + g3_DrawPoly (4,pntlist,bm_handle); + + rend_SetZBufferWriteMask (1); + + rend_SetWrapType (WT_WRAP); +} + +// Draws a bitmap that can orient around an axis +void DrawVisBillboardSmoketrail(vis_effect *vis) +{ + float norm_time,alpha_norm=1; + float uchange=0,vchange=0; + float time_live=Gametime-vis->creation_time; + float size=vis->size; + + int visnum=vis-VisEffects; + int bm_handle; + + // Get corners of this billboard + g3Point top_point,bot_point; + g3Point pnts[4],*pntlist[4]; + + g3_RotatePoint(&top_point,&vis->pos); + g3_RotatePoint(&bot_point,&vis->end_pos); + + norm_time=time_live/vis->lifetime; + alpha_norm=vis->lifeleft/vis->lifetime; + float width=vis->billboard_info.width*alpha_norm; + + if (alpha_norm>0.8f) + { + // Make this smoke puff a bit smaller at the beginning of its life + float newnorm=1.0f-((alpha_norm-0.8f)/0.2f); + width*=newnorm; + } + + alpha_norm*=0.7f; + + if (!GetBillboardCorners(pnts,&top_point,&bot_point,width)) + return; + + + fireball *fb=&Fireballs[vis->id]; + + for (int i=0;i<4;i++) + { + pnts[i].p3_flags|=PF_UV|PF_RGBA; + pntlist[i]=&pnts[i]; + pnts[i].p3_a=alpha_norm; + } + + if (norm_time>=1) + norm_time=0.99999f; // don't go over! + + int texnum=vis->custom_handle; + if (GameTextures[texnum].flags & TF_ANIMATED) + { + vclip *vc=&GameVClips[GameTextures[texnum].bm_handle]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + } + else + bm_handle=GetTextureBitmap(texnum,0); + + if (GameTextures[texnum].flags & TF_SATURATE) + rend_SetAlphaType (AT_SATURATE_TEXTURE_VERTEX); + else + rend_SetAlphaType (AT_TEXTURE_VERTEX); + + rend_SetOverlayType (OT_NONE); + + rend_SetZBufferWriteMask (0); + + rend_SetFlatColor (GR_16_TO_COLOR (vis->lighting_color)); + rend_SetColorModel (CM_MONO); + rend_SetTextureType (TT_LINEAR); + rend_SetLighting (LS_FLAT_GOURAUD); + + pnts[0].p3_u=0; + pnts[0].p3_v=0; + + pnts[1].p3_u=1; + pnts[1].p3_v=0; + + pnts[2].p3_u=1; + pnts[2].p3_v=1; + + pnts[3].p3_u=0; + pnts[3].p3_v=1; + + g3_DrawPoly (4,pntlist,bm_handle); + + rend_SetZBufferWriteMask (1); +} + +// Draws a long "stick" to represent the mass driver trail +void DrawVisMassDriverEffect (vis_effect *vis, bool f_boss) +{ + int i,t, k; + static int masstrail=-1; + + if (masstrail==-1) + masstrail=FindTextureName ("MassTrail"); + ASSERT(masstrail!=-1); //DAJ -1FIX + + vector center_vecs[2]; + g3Point arc_points[32],*pntlist[32]; + + center_vecs[0]=vis->pos; + center_vecs[1]=vis->end_pos; + + vector normvec=center_vecs[1]-center_vecs[0]; + matrix orient; + + vm_NormalizeVector(&normvec); + vm_VectorToMatrix (&orient,&normvec,NULL,NULL); + + float size=((float)vis->billboard_info.width)*.25; + int circle_pieces=16; + + if(f_boss) + { + size*=3.0f; + } + + float mag=vm_VectorDistanceQuick (&vis->pos,&vis->end_pos); + vector dir_norm=(vis->end_pos-vis->pos)/mag; + float alpha_norm=(vis->lifeleft/vis->lifetime)*.5; + int bm_handle=GetTextureBitmap (vis->custom_handle,0); + + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetOverlayType (OT_NONE); + rend_SetTextureType (TT_LINEAR); + rend_SetLighting(LS_FLAT_GOURAUD); + rend_SetZBufferWriteMask (0); + if(f_boss) + rend_SetFlatColor (GR_RGB(255,170,170)); + else + rend_SetFlatColor (GR_RGB(200,200,255)); + + rend_SetAlphaValue (255*alpha_norm); + + for (i=0;i<2;i++) + { + for (t=0;tlighting_color)); + rend_SetAlphaValue (255*alpha_norm); + + for (k=0;k400) + { + rings=400; + fsize=mag/400.0; + } + + rend_SetLighting (LS_NONE); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (255*alpha_norm); + + bm_handle=GetTextureBitmap (masstrail,0); + + int int_gametime=Gametime; + int frameroll=(Gametime-int_gametime)*-(65536*4); + + + if(!f_boss) + { + for (i=1;ipos+(dir_norm*(i*fsize)); + float new_size=1.0; + new_size+=FixSin((i*9000)+frameroll)*.3; + + g3_DrawPlanarRotatedBitmap (&pos,&dir_norm,rot_angle,new_size,(new_size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + } + } + rend_SetZBufferWriteMask (1); + +} + +// Renders a vis effect +void DrawVisEffect (vis_effect *vis) +{ + ASSERT (vis->type!=VIS_NONE); + + // First check to see if these are special types + + if (vis->id==LIGHTNING_BOLT_INDEX || vis->id==GRAY_LIGHTNING_BOLT_INDEX) + { + DrawVisLightningBolt (vis); + return; + } + else if (vis->id==MASSDRIVER_EFFECT_INDEX) + { + DrawVisMassDriverEffect (vis, false); + return; + } + else if (vis->id==MERCBOSS_MASSDRIVER_EFFECT_INDEX) + { + DrawVisMassDriverEffect (vis, true); + return; + } + else if (vis->id==BILLBOARD_SMOKETRAIL_INDEX) + { + DrawVisBillboardSmoketrail (vis); + return; + } + else if (vis->id==THICK_LIGHTNING_INDEX) + { + DrawVisThickLightning (vis); + return; + } + else if (vis->id==SINE_WAVE_INDEX) + { + DrawVisSineWave (vis); + return; + } + else if (vis->id==BLAST_RING_INDEX) + { + DrawVisBlastRing (vis); + return; + } + else if (vis->id==FADING_LINE_INDEX) + { + DrawVisFadingLine (vis); + return; + } + else if (vis->id==SNOWFLAKE_INDEX) + { + DrawVisSnowflake (vis); + return; + } + else if (vis->id==RAINDROP_INDEX || vis->id==PUDDLEDROP_INDEX) + { + DrawVisRainDrop (vis); + return; + } + else if (vis->id==AXIS_BILLBOARD_INDEX) + { + DrawVisAxisBillboard (vis); + return; + } + + float norm_time; + float time_live=Gametime-vis->creation_time; + + //This hack is needed for the demo system and Gamegauge, since it adjusts gametime during + //the game there are times when this could be a negative number (which would be bad) + if(time_live<0) + time_live = 0; + + float size=vis->size; + + // Bigger explosions for Katmai + if (Katmai) + { + if (vis->id==BIG_EXPLOSION_INDEX || vis->id==MED_EXPLOSION_INDEX || vis->id==MED_EXPLOSION_INDEX2 || vis->id==MED_EXPLOSION_INDEX3) + size*=1.8f; + } + + int visnum=vis-VisEffects; + int rot_angle; + int bm_handle; + + fireball *fb=&Fireballs[vis->id]; + + if (fb->type==FT_BILLOW) + rot_angle=((visnum*5000)+(FrameCount*160))%65536; + else if (vis->flags & VF_ATTACHED) + rot_angle=0; + else if (vis->id==RUBBLE1_INDEX || vis->id==RUBBLE2_INDEX) + rot_angle=((visnum*5000)+(FrameCount*860))%65536; + else if (vis->id==SUN_CORONA_INDEX) + { + rot_angle=((visnum*5000)+(FrameCount*500))%65536; + size*=1.0+((rand()%10)/100); + } + else + rot_angle=(visnum*5000)%65536; + + norm_time=time_live/vis->lifetime; + + // TEMP!! + if (vis->flags & VF_ATTACHED) + { + int int_time_live=time_live; + norm_time=time_live-int_time_live; + } + + if (norm_time>=1) + norm_time=.99999f; // don't go over! + + if (vis->flags & VF_EXPAND) + { + size=(vis->size/2)+((vis->size*norm_time)/2); + } + + if (vis->id==SMOKE_TRAIL_INDEX) // If its a smoke trail, get image from texture + { + int texnum=vis->custom_handle; + if (GameTextures[texnum].flags & TF_ANIMATED) + { + vclip *vc=&GameVClips[GameTextures[texnum].bm_handle]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + } + else + bm_handle=GameTextures[texnum].bm_handle; + } + else if (vis->id==SPRAY_INDEX) + { + int vnum=vis->custom_handle; + vclip *vc=&GameVClips[vnum]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + + // if (norm_time<.5) + //size=1+((vis->size-1)*(norm_time)); + } + else if (vis->id==CUSTOM_EXPLOSION_INDEX || vis->id==PARTICLE_INDEX) // Do custom + { + if ((GameTextures[vis->custom_handle].flags & TF_ANIMATED)) + { + int vnum=GameTextures[vis->custom_handle].bm_handle; + vclip *vc=&GameVClips[vnum]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + } + else + bm_handle=GetTextureBitmap (vis->custom_handle,0); + } + else if (fb->type==FT_SPARK) // Do spark + { + bm_handle=fb->bm_handle; + size*=(1.0-norm_time); + + } + else if (vis->id==SUN_CORONA_INDEX || vis->id==MUZZLE_FLASH_INDEX || vis->id==RUBBLE1_INDEX || vis->id==RUBBLE2_INDEX) + { + bm_handle=fb->bm_handle; + } + else + { + vclip *vc=&GameVClips[fb->bm_handle]; + int int_frame=vc->num_frames*norm_time; + bm_handle=vc->frames[int_frame]; + } + + + if (fb->type==FT_SMOKE) + { + if (norm_time>.3) + { + float temp_time=(norm_time-.3); + temp_time/=.7f; + + if (vis->flags & VF_REVERSE) + size/=(1+(temp_time*2.3)); + else + size*=(1+(temp_time*2.3)); + } + } + + // Set some alpha + if (vis->id==SMOKE_TRAIL_INDEX || vis->id==CUSTOM_EXPLOSION_INDEX || vis->id==PARTICLE_INDEX) + { + if (GameTextures[vis->custom_handle].flags & TF_SATURATE) + rend_SetAlphaType (AT_SATURATE_TEXTURE); + else + rend_SetAlphaType (ATF_CONSTANT+ATF_TEXTURE); + } + else if (vis->id==BLACK_SMOKE_INDEX) + { + rend_SetAlphaType (AT_LIGHTMAP_BLEND); + } + else if ((fb->type==FT_SMOKE && vis->id!=MED_SMOKE_INDEX) || vis->id==RUBBLE1_INDEX || vis->id==RUBBLE2_INDEX) + { + rend_SetAlphaType (ATF_CONSTANT+ATF_TEXTURE); + } + else + rend_SetAlphaType (AT_SATURATE_TEXTURE); + + float val; + if (norm_time>.5) + val=1.0-((norm_time-.5)/.5); + else + val=1.0; + + // Cap size + if (size>MAX_FIREBALL_SIZE) + size=MAX_FIREBALL_SIZE; + if ((vis->id!=BIG_EXPLOSION_INDEX && vis->id!=BLUE_EXPLOSION_INDEX) && size>(MAX_FIREBALL_SIZE/2)) + size=MAX_FIREBALL_SIZE/2; + + if (vis->id==SMOKE_TRAIL_INDEX || vis->id==CUSTOM_EXPLOSION_INDEX || vis->id==PARTICLE_INDEX) + rend_SetAlphaValue (val*GameTextures[vis->custom_handle].alpha*255); + else if (fb->type==FT_SMOKE) + rend_SetAlphaValue (val*SMOKE_ALPHA*255); + else if (vis->id==RUBBLE1_INDEX || vis->id==RUBBLE2_INDEX) + rend_SetAlphaValue (255); + else if (vis->id==MUZZLE_FLASH_INDEX) + rend_SetAlphaValue (128); + else if (vis->flags & VF_ATTACHED) + rend_SetAlphaValue (FIREBALL_ALPHA*255); + else + rend_SetAlphaValue (val*FIREBALL_ALPHA*255); + + rend_SetOverlayType (OT_NONE); + + if (!(vis->flags & VF_NO_Z_ADJUST)) + rend_SetZBias (-size); + + rend_SetZBufferWriteMask (0); + rend_SetWrapType (WT_CLAMP); + rend_SetLighting (LS_NONE); + + // Draw!! + if (vis->id==RUBBLE1_INDEX || vis->id==RUBBLE2_INDEX || vis->id==GRAY_SPARK_INDEX) + { + int color=GR_16_TO_COLOR(vis->lighting_color); + g3_DrawRotatedBitmap (&vis->pos,rot_angle,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color); + } + else + { + if (vis->flags & VF_PLANAR) + g3_DrawPlanarRotatedBitmap (&vis->pos,&vis->end_pos,rot_angle,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + else + g3_DrawRotatedBitmap (&vis->pos,rot_angle,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + } + + rend_SetZBias (0.0f); + rend_SetZBufferWriteMask (1); + + rend_SetWrapType (WT_WRAP); +} + +void VisEffectSetDeadFlag (vis_effect *vis) +{ + if (vis->flags & VF_DEAD) + return; + if (vis->type==VIS_NONE) + return; + + vis->flags |=VF_DEAD; + + VisDeadList[NumVisDead++]=vis-VisEffects; +} + +// Moves +void VisEffectMoveOne (vis_effect *vis) +{ + if (vis->flags & VF_USES_LIFELEFT) + vis->lifeleft -= Frametime; //...inevitable countdown towards death + + // Chris, do your stuff here + if(vis->movement_type == MT_PHYSICS) + do_vis_physics_sim(vis); + + if (vis->flags & VF_USES_LIFELEFT) + { + if (vis->lifeleft<0) + VisEffectSetDeadFlag (vis); + } + + if (vis->id==SNOWFLAKE_INDEX) + { + Weather.snowflakes_to_create++; + + vis->pos+=vis->velocity*Frametime; + + + if (vis->pos.y<1) + VisEffectSetDeadFlag(vis); + } + + // Do attached viseffect stuff here + if (vis->flags & VF_ATTACHED) + { + int objnum=vis->attach_info.obj_handle & HANDLE_OBJNUM_MASK; + uint sig=vis->attach_info.obj_handle & HANDLE_COUNT_MASK; + object *obj=&Objects[objnum]; + + if ((obj->flags & OF_DEAD) || (obj->handle & HANDLE_COUNT_MASK) !=sig) + { + // The object we're attached to doesn't exist anymore + VisEffectSetDeadFlag (vis); + + } + else if (obj->type==OBJ_PLAYER && (Players[obj->id].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD))) + { + // The object we're attached to doesn't exist anymore + VisEffectSetDeadFlag (vis); + } + else + { + if (vis->id==THICK_LIGHTNING_INDEX) + { + if (vis->flags & VF_PLANAR) + { + // Do object to object attachment + int dest_objnum=vis->attach_info.dest_objhandle & HANDLE_OBJNUM_MASK; + uint dest_sig=vis->attach_info.dest_objhandle & HANDLE_COUNT_MASK; + object *dest_obj=&Objects[dest_objnum]; + + if ((dest_obj->flags & OF_DEAD) || (dest_obj->handle & HANDLE_COUNT_MASK) !=dest_sig) + { + // The object we're attached to doesn't exist anymore + VisEffectSetDeadFlag (vis); + return; + } + + if (dest_obj->type==OBJ_PLAYER && (Players[dest_obj->id].flags & (PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD))) + { + VisEffectSetDeadFlag (vis); + return; + } + + vis->pos=obj->pos; + vis->end_pos=dest_obj->pos; + + // If were are shooting to the exact center of the viewer object then move + // the positions a little bit or it will look wrong: + if (obj==Viewer_object) + { + vis->pos-=(obj->orient.uvec*.1f); + } + + if (dest_obj==Viewer_object) + { + vis->end_pos-=(dest_obj->orient.uvec*.1f); + } + + } + else + { + if (obj->rtype.pobj_info.model_num!=vis->attach_info.modelnum) + { + VisEffectSetDeadFlag (vis); + } + else + { + WeaponCalcGun (&vis->pos,NULL,obj,vis->attach_info.vertnum); + WeaponCalcGun (&vis->end_pos,NULL,obj,vis->attach_info.end_vertnum); + } + } + } + else + { + float normalized_time[MAX_SUBOBJECTS]; + + if (obj->lowest_attached_vis==-1) + { + obj->lowest_attached_vis=vis-VisEffects; + poly_model *pm=&Poly_models[obj->rtype.pobj_info.model_num]; + + int i; + + SetNormalizedTimeObj(obj, normalized_time); + SetModelAngles (pm,normalized_time); + SetModelInterpPos (pm,normalized_time); + + + for (i=vis-VisEffects;i<=Highest_vis_effect_index;i++) + { + vis_effect *this_vis=&VisEffects[i]; + if (this_vis->type!=VIS_NONE && (this_vis->flags & VF_ATTACHED) && ((this_vis->attach_info.obj_handle & HANDLE_OBJNUM_MASK)==objnum) && this_vis->id!=THICK_LIGHTNING_INDEX) + { + bsp_info *sm=&pm->submodel[this_vis->attach_info.subnum]; + GetPolyModelPointInWorld (&this_vis->pos,pm, &obj->pos, &obj->orient, this_vis->attach_info.subnum, normalized_time, &sm->verts[this_vis->attach_info.vertnum], NULL); + + if (this_vis->attach_info.end_vertnum!=-1) + GetPolyModelPointInWorld (&this_vis->end_pos,pm, &obj->pos, &obj->orient, this_vis->attach_info.subnum, normalized_time, &sm->verts[this_vis->attach_info.end_vertnum], NULL); + } + } + } + } + + // Relink if need be + if (obj->roomnum!=vis->roomnum) + VisEffectRelink (vis-VisEffects,obj->roomnum); + + } + } + + // Do vis effect explosion lighting + if (Detail_settings.Dynamic_lighting && vis->lighting_color !=0 && (vis->lighting_color & OPAQUE_FLAG) && (vis->flags & VF_USES_LIFELEFT) && !(vis->flags & VF_DEAD)) + { + float scalar; + + scalar=(vis->lifetime-vis->lifeleft)/vis->lifetime; + + if (scalar>.5) + { + scalar-=.5; + + scalar=1.0-(scalar/.5); + } + else + { + scalar*=2; + } + + if (scalar>.05) + { + int color=GR_16_TO_COLOR (vis->lighting_color); + float r=(GR_COLOR_RED (color))/255.0; + float g=(GR_COLOR_GREEN (color))/255.0; + float b=(GR_COLOR_BLUE (color))/255.0; + + + if (ROOMNUM_OUTSIDE(vis->roomnum)) + { + int cellnum=CELLNUM(vis->roomnum); + + if (cellnum>=0 && cellnumpos,cellnum,vis->size*scalar*3,r,g,b); + else + mprintf ((0,"Vis effect not in world!\n")); + } + else + { + if (vis->roomnum>=0 && vis->roomnum<=Highest_room_index && Rooms[vis->roomnum].used) + ApplyLightingToRooms (&vis->pos,vis->roomnum,vis->size*scalar*3,r,g,b); + } + } + + } + + // Link this effect to the viewer if needed + if (vis->flags & VF_LINK_TO_VIEWER) + { + if (vis->roomnum!=Viewer_object->roomnum) + { + VisEffectRelink(vis-VisEffects,Viewer_object->roomnum); + } + } + +} + +// Moves our visuals +void VisEffectMoveAll () +{ + int i; + + for (i=0;i<=Highest_vis_effect_index;i++) + { + if (VisEffects[i].type!=VIS_NONE) + VisEffectMoveOne (&VisEffects[i]); + } + +} + +/* +// Attaches viseffects that move with an object +void AttachRandomVisEffectsToObject (int num,int handle,object *obj) +{ + int i; + vector zero_pos={0,0,0}; + poly_model *pm = &Poly_models[obj->rtype.pobj_info.model_num]; + + + + for (i=0;iroomnum,&zero_pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + vis->size=1.0; + vis->lifetime=15.0; + vis->lifeleft=15.0; + vis->flags |=VF_ATTACHED; + vis->attach_info.obj_handle=obj->handle; + + int subnum=ps_rand()%pm->n_models; + bsp_info *sm=&pm->submodel[subnum]; + + vis->attach_info.subnum=subnum; + vis->attach_info.vertnum=ps_rand()%sm->nverts; + + } + } +}*/ + +// Attaches viseffects that move with an object +void AttachRandomNapalmEffectsToObject (object *obj) +{ + if (obj->flags & OF_DEAD) + return; + + vector velocity_norm=obj->mtype.phys_info.velocity; + vm_NormalizeVector (&velocity_norm); + vector pos=obj->pos-(velocity_norm*(obj->size/2)); + + if (obj->movement_type==MT_PHYSICS && (OBJECT_OUTSIDE (obj) && (ps_rand()%3)==0)|| (ps_rand()%3)==0) + CreateFireball (&pos,BLACK_SMOKE_INDEX,obj->roomnum,VISUAL_FIREBALL); + + float size_scalar=obj->size/7.0; + + size_scalar=max (1.0,size_scalar); + size_scalar=min (4.0,size_scalar); + + // Create an explosion that follows every now and then + if ((ps_rand()%3)==0) + { + if (!(obj->flags & OF_POLYGON_OBJECT)) + return; + + int num=1; + + num+=(obj->size/15); + + for (int i=0;irtype.pobj_info.model_num]; + + if (pm->n_models==0) + return; + + int subnum=ps_rand()%pm->n_models; + + if (IsNonRenderableSubmodel (pm,subnum)) + continue; + + bsp_info *sm=&pm->submodel[subnum]; + + if (sm->nverts==0) + return; + + int vertnum=ps_rand()%sm->nverts; + + GetPolyModelPointInWorld (&dest,&Poly_models[obj->rtype.pobj_info.model_num], &obj->pos, &obj->orient, subnum, &sm->verts[vertnum] ); + int visnum=VisEffectCreate (VIS_FIREBALL,GetRandomSmallExplosion(),obj->roomnum,&dest); + if(visnum==-1) + return; + + VisEffects[visnum].size+=((ps_rand()%20)/20.0)*1.0; + + VisEffects[visnum].size*=size_scalar; + + if ((ps_rand()%2)) + { + if (obj->movement_type==MT_PHYSICS) + { + VisEffects[visnum].movement_type=MT_PHYSICS; + VisEffects[visnum].velocity=obj->mtype.phys_info.velocity; + VisEffects[visnum].mass=obj->mtype.phys_info.mass; + VisEffects[visnum].drag=obj->mtype.phys_info.drag; + } + } + } + } +} + + + + diff --git a/Descent3/viseffect.h b/Descent3/viseffect.h new file mode 100644 index 000000000..4dd0fd2ca --- /dev/null +++ b/Descent3/viseffect.h @@ -0,0 +1,166 @@ +/* + * $Logfile: /DescentIII/Main/viseffect.h $ + * $Revision: 33 $ + * $Date: 3/20/00 1:35p $ + * $Author: Matt $ + * + * Header for viseffect.cpp + * + * $Log: /DescentIII/Main/viseffect.h $ + * + * 33 3/20/00 1:35p Matt + * Merge of Duane's post-1.3 changes. + * Moved max_vis_effects (the variable, not the constant) from viseffect.h + * to viseffect_external.h. + * + * 31 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 30 4/30/99 7:37p Jeff + * created viseffect_external.h + * + * 29 4/19/99 3:17p Jason + * made real vis effects work + * + * 28 3/31/99 11:40a Jason + * added support for attached thick lightning + * + * 27 3/23/99 4:10p Jason + * more tweaks for line sparks + * + * 26 3/23/99 12:50p Jason + * added line sparks + * + * 25 12/11/98 5:08p Jason + * changed mass driver effect + * + * 24 9/29/98 7:15p Jason + * added axis billboards + * + * 23 9/18/98 8:23p Jason + * fixed insidious vis effect errors + * + * 22 9/17/98 3:03p Jason + * added lightning and invul hit effects + * + * 21 6/23/98 3:34p Jason + * added cool lighting effect to gravity field + * + * 20 6/22/98 6:52p Jason + * changed gravity field effect somewhat + * + * 19 6/12/98 5:14p Jason + * made viseffects deal with zbuffer better + * + * 18 4/29/98 11:38a Jason + * added some weather effects (first pass) + * + * 17 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 16 4/15/98 2:33p Jason + * changed some smoke trail stuff + * + * 15 4/15/98 12:22p Jason + * lots of miscellaneous stuff pertaining to lighting and vis effects + * + * 14 4/10/98 12:39p Jason + * added expanding explosion bitmaps + * + * 13 4/07/98 3:31p Jason + * got particle effects working with weapons + * + * 12 4/07/98 12:54p Jason + * changes for viseffects and multiplayer dll + * + * 11 2/16/98 2:37p Jason + * added real viseffects + * + * 10 2/15/98 1:21a Jason + * doubled the amount of viseffects + * + * 9 2/04/98 9:28p Jason + * added some new weapons effects + * + * 8 2/04/98 6:58p Matt + * Fixed viseffect rendering on terrain. + * + * 7 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + */ + +#ifndef VIS_EFFECT_H +#define VIS_EFFECT_H + +#include "viseffect_external.h" + +extern vis_effect *VisEffects; +extern int Highest_vis_effect_index; + + +// Returns the next free viseffect +int VisEffectAllocate (); + +// Frees up a viseffect for use +int VisEffectFree (int visnum); + + +int VisEffectInitType (vis_effect *vis); + + +//initialize a new viseffect. adds to the list for the given room +//returns the object number +int VisEffectCreate(ubyte type,ubyte id,int roomnum,vector *pos); + +//link the viseffect into the list for its room +// Does nothing for effects over terrain +void VisEffectLink(int visnum,int roomnum); + + +// Unlinks a viseffect from a room +// Does nothing for terrain +void VisEffectUnlink(int visnum); + +//when an effect has moved into a new room, this function unlinks it +//from its old room and links it into the new room +void VisEffectRelink(int visnum,int newroomnum); + +// Frees all the vis effects that are currently in use +void FreeAllVisEffects (); + +// Goes through our array and clears the slots out +void InitVisEffects(); + +//remove viseffect from the world +void VisEffectDelete(int visnum); + +// Kills all the effects that are dead +void VisEffectDeleteDead(); + +// Moves our visuals +void VisEffectMoveAll (); + +// Renders a vis effect +void DrawVisEffect (vis_effect *vis); + +// Creates a some sparks that go in random directions +void CreateRandomSparks (int num_sparks,vector *pos,int roomnum,int which_index=-1,float force_scalar=1); + +// Creates a some line sparks that go in random directions +void CreateRandomLineSparks (int num_sparks,vector *pos,int roomnum,ushort color=0,float force_scalar=1); + +// Creates vis effects but has the caller set their parameters +//initialize a new viseffect. adds to the list for the given room +//returns the vis number +int VisEffectCreateControlled(ubyte type,object *parent,ubyte id,int roomnum,vector *pos,float lifetime,vector *velocity,int phys_flags=0,float size=0,float mass=0.0f,float drag=0.0f,bool isreal=0); + +// Creates a some particles that go in random directions +void CreateRandomParticles (int num_sparks,vector *pos,int roomnum,int bm_handle,float size,float life); + +// Attaches viseffects that move with an object +void AttachRandomNapalmEffectsToObject (object *obj); + +#endif \ No newline at end of file diff --git a/Descent3/viseffect_external.h b/Descent3/viseffect_external.h new file mode 100644 index 000000000..99fe3024e --- /dev/null +++ b/Descent3/viseffect_external.h @@ -0,0 +1,117 @@ +/* +* $Logfile: /DescentIII/main/viseffect_external.h $ +* $Revision: 6 $ +* $Date: 3/21/00 9:58a $ +* $Author: Matt $ +* +* +* +* $Log: /DescentIII/main/viseffect_external.h $ + * + * 6 3/21/00 9:58a Matt + * Changed to Mac-only the code that sets a variable number of vis effects + * based on texture quality. + * + * 5 3/20/00 2:27p Matt + * Merge of Duane's post-1.3 changes. + * Moved max_vis_effects (the variable, not the constant) from viseffect.h + * to viseffect_external.h. + * + * 4 10/21/99 9:30p Jeff + * B.A. Macintosh code merge + * + * 3 5/02/99 1:37a Jason + * added moving object lighting viseffects + * + * 2 4/30/99 7:37p Jeff + * created viseffect_external.h +* +* $NoKeywords: $ +*/ + + +#ifndef __VISEFFECT_EXTERNAL_H_ +#define __VISEFFECT_EXTERNAL_H_ + + +#include "pstypes.h" +#include "pserror.h" +#include "vecmat.h" + +#ifdef MACINTOSH +#define MAX_VIS_EFFECTS 4096 //DAJ utb 5000 +#else +#define MAX_VIS_EFFECTS 5000 +#endif + +// types +#define VIS_NONE 0 +#define VIS_FIREBALL 1 + +// Flags +#define VF_USES_LIFELEFT 1 +#define VF_WINDSHIELD_EFFECT 2 +#define VF_DEAD 4 +#define VF_PLANAR 8 +#define VF_REVERSE 16 +#define VF_EXPAND 32 +#define VF_ATTACHED 64 +#define VF_NO_Z_ADJUST 128 +#define VF_LINK_TO_VIEWER 256 // Always link into the room that the viewer is in + +extern ushort max_vis_effects; + +struct object; + +typedef struct +{ + int obj_handle; + int dest_objhandle; + + ushort modelnum; + ushort vertnum; + ushort end_vertnum; + + ubyte subnum,subnum2; +} vis_attach_info; + +typedef struct +{ + ubyte width; + ubyte height; + ubyte texture; +} axis_billboard_info; + + +typedef struct +{ + vector pos; + vector end_pos; + vector velocity; + float mass; + float drag; + float size; + float lifeleft; + float lifetime; + float creation_time; + + int roomnum; + + int phys_flags; + + short custom_handle; + ushort lighting_color; + + ushort flags; + + short next; + short prev; + + vis_attach_info attach_info; + axis_billboard_info billboard_info; + + ubyte movement_type; + ubyte type,id; +} vis_effect; + +#endif \ No newline at end of file diff --git a/Descent3/voice.cpp b/Descent3/voice.cpp new file mode 100644 index 000000000..31ee21140 --- /dev/null +++ b/Descent3/voice.cpp @@ -0,0 +1,324 @@ +/* +* $Logfile: /DescentIII/Main/voice.cpp $ +* $Revision: 11 $ +* $Date: 5/13/99 3:42p $ +* $Author: Ardussi $ +* +* implementation file for voice system +* +* $Log: /DescentIII/Main/voice.cpp $ + * + * 11 5/13/99 3:42p Ardussi + * changes for compiling on the Mac + * + * 10 2/10/99 4:45p Jeff + * table file parser stuff + * + * 9 9/28/98 4:36p Jeff + * shortcircuit the voices for the demo + * + * 8 7/24/98 5:19p Samir + * took out old stream code. + * + * 7 7/13/98 6:07p Jeff + * handled not removing non-powerup voices on a queue clean up + * + * 6 7/09/98 8:35p Samir + * Changed StreamStop. + * + * 5 6/19/98 6:04p Jeff + * added voice queue + * + * 4 6/18/98 5:18p Jeff + * Use new streamaudio system + * + * 3 6/17/98 8:08p Jeff + * removed DoVoiceFrame + * + * 2 6/08/98 3:55p Jeff + * rough initial creation +* +* $NoKeywords: $ +*/ +#include "voice.h" +#include "streamaudio.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include +#include + +#define MOTHERLOAD_STRING "Cheater!" +#define MOTHERLOAD_FLAGS VF_POWERUP|VF_PLAYTABLE + + +typedef struct{ + int handle; + int flags; + int chandle; + bool inuse; +}tVoice; + +bool PlayPowerupVoice; +bool PlayVoices; + +#define QUEUE_SIZE 3 + +class VoiceQueue +{ +public: + VoiceQueue(); + ~VoiceQueue(); + void AddVoice(char *fn,int flg); + bool GetNextVoice(char *fn,int *flg); + void Clear(bool onlypowerups = false); +private: + char filenames[QUEUE_SIZE][_MAX_PATH]; + int flags[QUEUE_SIZE]; + ubyte currvoice; + ubyte pos; + char motherloadat; + bool inuse[QUEUE_SIZE]; + bool full; +}; + +tVoice CurrentVoiceHandle; +VoiceQueue vq; + +void StartVoice(char *filename,int flags); + +bool InitVoices(void) +{ + mprintf((0,"Voice System: Init\n")); + CurrentVoiceHandle.handle = -1; + CurrentVoiceHandle.flags = 0; + CurrentVoiceHandle.chandle = -1; + CurrentVoiceHandle.inuse = false; + vq.Clear(); + + atexit(CloseVoices); + return true; +} + +void CloseVoices(void) +{ + mprintf((0,"Voice System: Shutdown\n")); + StopVoice(); +} + +void PlayVoice(char *filename,int flags) +{ +#ifdef DEMO + return; +#endif + if((!PlayPowerupVoice)&&(flags&VF_POWERUP)&&(!(flags&VF_FORCE))) + return; + if((CurrentVoiceHandle.inuse)&&(flags&VF_INTERUPT)){ + StopVoice(); + vq.Clear(); + } + vq.AddVoice(filename,flags); +} + +void StartVoice(char *filename,int flags) +{ +#ifdef DEMO + return; +#endif + if((!PlayVoices)&&(!(flags&VF_FORCE))) + return; + + if(flags&VF_PLAYTABLE){ + CurrentVoiceHandle.flags = flags; + CurrentVoiceHandle.inuse = true; + CurrentVoiceHandle.chandle = -1; + int index = FindSoundName(IGNORE_TABLE(filename)); + CurrentVoiceHandle.handle = Sound_system.Play2dSound(index); + return; + } + + bool compressed = (flags&VF_COMPRESSED)?true:false; + bool bit8 = (flags&VF_8BIT)?true:false; + bool stereo = (flags&VF_STEREO)?true:false; + + int vf = 0; + +// if(bit8){ +// if(stereo) +// vf = SAF_8BIT_S; +// else +// vf = SAF_8BIT_M; +// }else{ +// if(stereo) +// vf = SAF_16BIT_S; +// else +// vf = SAF_16BIT_M; +// } +// if(compressed) +// vf |= SAF_COMPRESSED; + + CurrentVoiceHandle.chandle = StreamPlay(filename,(MAX_GAME_VOLUME/2.0),vf); + if(CurrentVoiceHandle.chandle!=-1) + CurrentVoiceHandle.handle = StreamGetSoundHandle(CurrentVoiceHandle.chandle); + + CurrentVoiceHandle.flags = flags; + CurrentVoiceHandle.inuse = true; +} + +void StopVoice(void) +{ + if(CurrentVoiceHandle.inuse){ + if(CurrentVoiceHandle.flags&VF_PLAYTABLE){ + if(CurrentVoiceHandle.handle!=-1) + Sound_system.StopSoundImmediate(CurrentVoiceHandle.handle); + }else{ + if(CurrentVoiceHandle.chandle!=-1) + StreamStop(CurrentVoiceHandle.chandle); + } + } + CurrentVoiceHandle.handle = -1; + CurrentVoiceHandle.flags = 0; + CurrentVoiceHandle.chandle = -1; + CurrentVoiceHandle.inuse = false; +} + +void UpdateVoices(void) +{ + char filename[_MAX_PATH]; + int flags; + + if(!CurrentVoiceHandle.inuse){ + //see if we have something waiting + if(vq.GetNextVoice(filename,&flags)){ + mprintf((0,"Playing queued voice %s\n",filename)); + StartVoice(filename,flags); + return; + } + return; + } + + //according to the handle, the current voice is inuse, lets make sure + if(Sound_system.IsSoundPlaying(CurrentVoiceHandle.handle)) + return; //it is + + //it isn't, so stop it and play the next voice in the queue if any + StopVoice(); + if(vq.GetNextVoice(filename,&flags)){ + mprintf((0,"Playing queued voice %s\n",filename)); + StartVoice(filename,flags); + } +} + + +////////////////////// +//Voice Queue Implementation + +VoiceQueue::VoiceQueue() +{ + currvoice = pos = 0; + motherloadat = -1; + full = false; + for(int i=0;i auto selection dependencies. + * + * 19 11/14/97 5:47p Mark + * Fixed some weapon selection hokeyness. + * + * 18 11/12/97 6:01p Samir + * Added omega and guided names to weapon list. + * + * 17 11/12/97 1:13p Jason + * added weapons that can ramp up + * + * 16 11/11/97 1:28p Samir + * Weapon selection should change hud, I think. + * + * 15 11/05/97 12:21p Chris + * Added weapon remap for player ship weapon batteries + * + * 14 11/04/97 5:52p Jason + * added DrawAplhaBlendedScreen function + * + * 13 10/30/97 4:02p Matt + * Added the flare + * + * 12 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 11 9/16/97 3:03p Samir + * fixed autoselection of weapons when you don't have enough energy. + * + * 10 9/15/97 3:37p Samir + * Updated some more names for weapons. + * + * 9 9/10/97 11:45a Chris + * FIXED a major remapping bug + * Added support for weapon batteries + * + * 8 9/10/97 10:12a Samir + * Added weapon name text. + * + * 7 9/08/97 5:12p Samir + * Fixed weapon selection + * + * 6 9/05/97 12:25p Samir + * Added autoselection of weapons. + * + * 5 9/04/97 3:59p Samir + * Added code to switch weapons. + * + * 4 8/07/97 3:28p Jason + * Assign -1 as the default sound for weapons + * + * 3 8/07/97 1:01p Mark + * fixed a dumb bug + * + * 2 8/07/97 12:53p Mark + * FROM JASON: Made oof readable as discharge files + * + * 7 4/28/97 6:46p Jason + * made ships have multiple gun points + * + * 6 4/25/97 3:59p Jason + * fixed a bug that my last checkin caused + * + * 5 4/25/97 3:31p Jason + * implemented better memory management for vclips and bitmaps + * + * 4 4/17/97 2:54p Jason + * added clueless weapon picker upper + * + * 3 4/14/97 1:50p Jason + * first pass at getting weapons to fire + * + * 2 3/31/97 4:35p Jason + * added weapon page functionality + * + * 1 3/31/97 4:13p Jason + * weapons implementation file + * + +* +* $NoKeywords: $ +*/ + + + +#include "weapon.h" +#include "pstypes.h" +#include "pserror.h" +#include "object.h" +#include "3d.h" +#include +#include +#include +#include +#include "bitmap.h" +#include "vclip.h" +#include "game.h" +#include "polymodel.h" +#include "player.h" +#include "hud.h" +#include "hlsoundlib.h" +#include "soundload.h" +#include "objinfo.h" +#include "gametexture.h" +#include "ship.h" +#include "gauges.h" +#include "sounds.h" +#include "stringtable.h" +#include "Macros.h" +#include "CFILE.H" +#include "AIMain.h" + +//#include "samirlog.h" +#define LOGFILE(_s) + +weapon Weapons[MAX_WEAPONS]; +int Num_weapons=0; + +const char *Static_weapon_names[]={ +// Primary weapons + "Laser", + "Vauss", + "Microwave", + "Plasma", + "Fusion", + "Super Laser", + "Mass Driver", + "Napalm", + "EMD Gun", + "Omega", +// Secondary weapons + "Concussion", + "Homing", + "Impact Mortar", + "Smart", + "Mega", + "Frag", + "Guided", + "Napalm Rocket", + "Cyclone", + "Black Shark", +// The flares + "Yellow Flare", +}; + +int Static_weapon_names_msg[]={ +// Primary weapons + TXI_WPN_LASER, + TXI_WPN_VAUSS, + TXI_WPN_MICROWAVE, + TXI_WPN_PLASMA, + TXI_WPN_FUSION, + TXI_WPN_SUPLASER, + TXI_WPN_MASSDRIVER, + TXI_WPN_NAPALM, + TXI_WPN_EMDGUN, + TXI_WPN_OMEGA, +// Secondary weapons + TXI_WPN_CONCUSSION, + TXI_WPN_HOMING, + TXI_WPN_IMPACT, + TXI_WPN_SMART, + TXI_WPN_MEGA, + TXI_WPN_FRAG, + TXI_WPN_GUIDED, + TXI_WPN_NAPALMR, + TXI_WPN_CYCLONE, + TXI_WPN_BLACKSHARK, +// The flares + TXI_WPN_YELL_FLARE, +}; + +int Static_weapon_ckpt_names[][2] = { +// Primary weapons + {TXI_WPNC_LASER_1,TXI_WPNC_LASER_2}, + {TXI_WPNC_VAUSS_1,TXI_WPNC_VAUSS_2}, + {TXI_WPNC_MICRO_1,TXI_WPNC_MICRO_2}, + {TXI_WPNC_PLASMA_1,TXI_WPNC_PLASMA_2}, + {TXI_WPNC_FUSION_1,TXI_WPNC_FUSION_2}, + {TXI_WPNC_SUPLAS_1,TXI_WPNC_SUPLAS_2}, + {TXI_WPNC_MASSD_1,TXI_WPNC_MASSD_2}, + {TXI_WPNC_NAPALM_1,TXI_WPNC_NAPALM_2}, + {TXI_WPNC_EMD_1,TXI_WPNC_EMD_2}, + {TXI_WPNC_OMEGA_1,TXI_WPNC_OMEGA_2}, +// Secondary weapons + {TXI_WPNC_CONC_1,TXI_WPNC_CONC_2}, + {TXI_WPNC_HOMING_1,TXI_WPNC_HOMING_2}, + {TXI_WPNC_IMPACT_1,TXI_WPNC_IMPACT_2}, + {TXI_WPNC_SMART_1,TXI_WPNC_SMART_2}, + {TXI_WPNC_MEGA_1,TXI_WPNC_MEGA_2}, + {TXI_WPNC_FRAG_1,TXI_WPNC_FRAG_2}, + {TXI_WPNC_GUID_1,TXI_WPNC_GUID_2}, + {TXI_WPNC_NAPALMR_1,TXI_WPNC_NAPALMR_2}, + {TXI_WPNC_CYCLONE_1,TXI_WPNC_CYCLONE_2}, + {TXI_WPNC_BLKSHRK_1,TXI_WPNC_BLKSHRK_2}, +// The flares + {TXI_WPNC_YELFLARE_1,TXI_WPNC_YELFLARE_2}, +}; + + +// Sets all weapons to unused +void InitWeapons () +{ + for (int i=0;i0); + + Weapons[n].used=0; + Weapons[n].name[0]=0; + Num_weapons--; +} + +// Gets next weapon from n that has actually been alloced +int GetNextWeapon (int n) +{ + int i; + + ASSERT (n>=0 && n=0 && n=0;i--) + { + if (Weapons[i].used) + return i; + } + for (i=MAX_WEAPONS-1;i>n;i--) + { + if (Weapons[i].used) + return i; + } + + // this is the only one + return n; + +} +// Searches thru all weapons for a specific name, returns -1 if not found +// or index of weapon with name +int FindWeaponName (char *name) +{ + int i; + + ASSERT (name!=NULL); + + for (i=0;iused); + + cur_frametime=Gametime/vc->frame_time; + int_frame=cur_frametime; + int_frame+=framenum; + + return (vc->frames[int_frame % vc->num_frames]); + } + else + return (Weapons[handle].hud_image_handle); + +} + +// Given a filename, loads either the bitmap or vclip found in that file. If type +// is not NULL, sets it to 1 if file is animation, otherwise sets it to zero +int LoadWeaponHudImage (char *filename,int *type) +{ + int anim=0,bm_handle; + char extension[10]; + + int len=strlen (filename); + + if (len<4) + return -1; + + strncpy (extension,&filename[len-3],5); + + + if ((!strnicmp ("oaf",extension,3)) || (!strnicmp ("ifl",extension,3)) || (!strnicmp ("abm",extension,3))) + anim=1; + + if (type!=NULL) + *type=anim; + + if (anim) + bm_handle=AllocLoadVClip (IGNORE_TABLE(filename),NOT_TEXTURE,0); + else + bm_handle=bm_AllocLoadFileBitmap (IGNORE_TABLE(filename),0); + + return bm_handle; +} + + +// Given a weapon handle, returns that weapons discharge bitmap +int GetWeaponFireImage (int handle,int frame) +{ + if (Weapons[handle].flags & WF_IMAGE_VCLIP) + { + float cur_frametime; + int int_frame; + + PageInVClip (Weapons[handle].fire_image_handle); + + vclip *vc=&GameVClips[Weapons[handle].fire_image_handle]; + ASSERT (vc->used>=1); + + cur_frametime=Gametime/vc->frame_time; + int_frame=cur_frametime; + int_frame+=frame; + return (vc->frames[int_frame % vc->num_frames]); + } + else + return (Weapons[handle].fire_image_handle); +} + +// Given a filename, loads either the bitmap or model found in that file. If type +// is not NULL, sets it to 1 if file is model, otherwise sets it to zero +int LoadWeaponFireImage (char *filename,int *type,int *anim,int pageable) +{ + int model=0,bm_handle; + int is_vclip=0; + char extension[10]; + + int len=strlen (filename); + + if (len<4) + return -1; + + strncpy (extension,&filename[len-3],5); + + + if ((!strnicmp ("pof",extension,3)) || (!strnicmp ("oof",extension,3))) + model=1; + + if (type!=NULL) + *type=model; + if (anim!=NULL) + *anim=is_vclip; + + if (model) + bm_handle=LoadPolyModel (IGNORE_TABLE(filename),0); + else + { + bm_handle=LoadTextureImage (IGNORE_TABLE(filename),anim,NOT_TEXTURE,0); + } + + return bm_handle; +} + + + +// Given a weapon name, assigns that weapon to a specific index into +// the Weapons array. Returns -1 if the named weapon is not found, 0 if the weapon +// is already in its place, or 1 if successfully moved +int MatchWeaponToIndex (char *name,int dest_index) +{ + + ASSERT (dest_index>=0 && dest_index= 0) //DAJ -1FIX + memcpy (&Weapons[new_index],&Weapons[dest_index],sizeof(weapon)); + + // Now copy our new info over and free the old one + memcpy (&Weapons[dest_index],&Weapons[cur_index],sizeof(weapon)); + FreeWeapon (cur_index); + } + else + { + // This slot is unused, so just take it + + Weapons[dest_index].used=1; + Num_weapons++; + + memcpy (&Weapons[dest_index],&Weapons[cur_index],sizeof(weapon)); + FreeWeapon (cur_index); + return 0; + } + + return new_index; // we made it! +} + + +// Moves a weapon from a given index into a new one (above MAX_STATIC_WEAPONS) +// returns new index +int MoveWeaponFromIndex (int index) +{ + ASSERT (index>=0 && indexstatic_wb[weap_index]; + ASSERT(wb != NULL); + + //if secondary or primary that uses ammo, then use the ammo + if((weap_index >= SECONDARY_INDEX) || wb->ammo_usage) + { + //figure out much ammo to add + int added = min(ship->max_ammo[weap_index] - Players[slot].weapon_ammo[weap_index],ammo); + + //now add it + Players[slot].weapon_ammo[weap_index] += (ushort)added; + } + + if (slot==Player_num) + { + if (weap_index < SECONDARY_INDEX) + select_new = AutoSelectWeapon(PW_PRIMARY, weap_index); + else + select_new = AutoSelectWeapon(PW_SECONDARY, weap_index); + + // this should be done in multisafe code now. + //if (!select_new) { + // AddHUDMessage ("%s!",TXT(Static_weapon_names_msg[weap_index])); + //} + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////// +// Weapon selection + +// Note that we allocate five keys per category of weapon (primary, secondary) +// We essentially select the weapon in slot passed. + +#define NUM_PRIMARY_SLOTS 5 +#define NUM_SECONDARY_SLOTS 5 + +// This is NOT a mask of weapons available to the player. This is a mask of what CLASS of +// weapon this slot is currently in. The code below checks this mask to see if it should +// select the higher class weapon in that slot when switching to that slot. +static ushort Weapon_slot_mask = 0; + +void SelectPrimaryWeapon(int slot); +void SelectSecondaryWeapon(int slot); +void SetPrimaryWeapon(int index, int slot); +void SetSecondaryWeapon(int index, int slot); + +inline bool is_weapon_available(unsigned player_weapon_flags, int new_weapon, ushort ammo=0xffff) +{ + return ((player_weapon_flags & HAS_FLAG(new_weapon)) && ammo > 0) ? true : false; +} + + +// used for sequencing +void ResetWeaponSelectStates(ushort new_state) +{ + Weapon_slot_mask = new_state; +} + +void SaveWeaponSelectStates(CFILE *fp) +{ + cf_WriteShort(fp, Weapon_slot_mask); +} + +void LoadWeaponSelectStates(CFILE *fp) +{ + ushort state = (ushort)cf_ReadShort(fp); + ResetWeaponSelectStates(state); +} + + +void SelectWeapon(int slot) +{ + if (Player_object->type!=OBJ_PLAYER) + return; // This can happen when a player is dead and tries to select a weapon + + if (slot < NUM_PRIMARY_SLOTS) + SelectPrimaryWeapon(slot); + else + SelectSecondaryWeapon(slot); +} + + +// slot ranges 0-4 +void SelectPrimaryWeapon(int slot) +{ +// get slot of currently selected weapon + int oldslot; + int nw_low, nw_high; + unsigned avail_flags; + player *plr = &Players[Player_num]; + + avail_flags = plr->weapon_flags; + oldslot = plr->weapon[PW_PRIMARY].index % NUM_PRIMARY_SLOTS; + +// do selection. if we are selecting the same slot of weapon, then we select to the next +// level of weapon. when going from highest level, go to lowest + if (oldslot == slot) { + ushort nw_low = (plr->weapon[PW_PRIMARY].index+NUM_PRIMARY_SLOTS) % MAX_PRIMARY_WEAPONS; + + if (is_weapon_available(avail_flags, nw_low)) { + // toggle class of weapon in specified slot (save for selection) + SetPrimaryWeapon(nw_low, slot); + } + else { + AddHUDMessage(TXT_WPNNOTAVAIL); + Sound_system.Play2dSound(SOUND_DO_NOT_HAVE_IT); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.1f; + hear.curiosity_level = 0.5f; + hear.max_dist = AI_SOUND_SHORT_DIST; + AINotify(&Objects[Players[Player_num].objnum], AIN_HEAR_NOISE, (void *)&hear); + } + + return; + } + else { + // we are selecting a new weapon slot. + nw_low = slot % NUM_PRIMARY_SLOTS; + nw_high = nw_low + NUM_PRIMARY_SLOTS; + + if (Weapon_slot_mask & (1<weapon_flags; + oldslot = (plr->weapon[PW_SECONDARY].index % NUM_SECONDARY_SLOTS) + NUM_PRIMARY_SLOTS; + +// do selection. if we are selecting the same slot of weapon, then we select to the next +// level of weapon. when going from highest level, go to lowest + if (oldslot == slot) { + nw_low = SECONDARY_INDEX + ((plr->weapon[PW_SECONDARY].index+NUM_SECONDARY_SLOTS) % MAX_SECONDARY_WEAPONS); + + if (is_weapon_available(avail_flags, nw_low, plr->weapon_ammo[nw_low])) { + // toggle class of weapon in specified slot (save for selection) + SetSecondaryWeapon(nw_low, slot); + } + else { + AddHUDMessage(TXT_WPNNOTAVAIL); + Sound_system.Play2dSound(SOUND_DO_NOT_HAVE_IT); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.1f; + hear.curiosity_level = 0.5f; + hear.max_dist = AI_SOUND_SHORT_DIST; + AINotify(&Objects[Players[Player_num].objnum], AIN_HEAR_NOISE, (void *)&hear); + } + return; + } + +// we are selecting a new weapon slot. + nw_low = (slot%NUM_SECONDARY_SLOTS) + SECONDARY_INDEX; + nw_high = nw_low + NUM_SECONDARY_SLOTS; + + if (Weapon_slot_mask & (1<weapon_ammo[nw_high])) { + // if this slot had the higher class of weapon then check if we still have the higher class weapon. + SetSecondaryWeapon(nw_high, slot); + } + else if (is_weapon_available(avail_flags, nw_low, plr->weapon_ammo[nw_low])) { + SetSecondaryWeapon(nw_low, slot); + } + else { + AddHUDMessage(TXT_WPNNOTAVAIL); + Sound_system.Play2dSound(SOUND_DO_NOT_HAVE_IT); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.1f; + hear.curiosity_level = 0.5f; + hear.max_dist = AI_SOUND_SHORT_DIST; + AINotify(&Objects[Players[Player_num].objnum], AIN_HEAR_NOISE, (void *)&hear); + } + } + else { + // if this is a lower class of weapon, make sure this slot is flagged as having the lower version + if (is_weapon_available(avail_flags, nw_low, plr->weapon_ammo[nw_low])) { + // if this slot had the higher class of weapon then check if we still have the higher class weapon. + SetSecondaryWeapon(nw_low, slot); + } + else if (is_weapon_available(avail_flags, nw_high, plr->weapon_ammo[nw_high])) { + // check if we have the lower class. + SetSecondaryWeapon(nw_high, slot); + } + else { + AddHUDMessage(TXT_WPNNOTAVAIL); + Sound_system.Play2dSound(SOUND_DO_NOT_HAVE_IT); + + ain_hear hear; + hear.f_directly_player = true; + hear.hostile_level = 0.1f; + hear.curiosity_level = 0.5f; + hear.max_dist = AI_SOUND_SHORT_DIST; + AINotify(&Objects[Players[Player_num].objnum], AIN_HEAR_NOISE, (void *)&hear); + } + } +} + + +void SetPrimaryWeapon(int index, int slot) +{ + dynamic_wb_info *p_dwb = &Player_object->dynamic_wb[index]; + + if (index < NUM_PRIMARY_SLOTS) + Weapon_slot_mask &= ~(1<last_fire_time=Gametime; + // resets reticle to current weapon. + ResetReticle(); + } + +} + + +void SetSecondaryWeapon(int index, int slot) +{ + dynamic_wb_info *p_dwb = &Player_object->dynamic_wb[index]; + + if (index < (NUM_SECONDARY_SLOTS+SECONDARY_INDEX)) + Weapon_slot_mask &= ~(1<last_fire_time=Gametime; + // resets reticle to current weapon. + ResetReticle(); + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Weapon AUTO selection + +const ushort SELLIST_START = 0x7ffe, + SELLIST_END = 0x7fff; + +static ushort PrimaryWpnSelectList[] = +{ + SELLIST_START, + LASER_INDEX, + VAUSS_INDEX, + MICROWAVE_INDEX, + PLASMA_INDEX, + FUSION_INDEX, + SUPER_LASER_INDEX, + MASSDRIVER_INDEX, + NAPALM_INDEX, + EMD_INDEX, + OMEGA_INDEX, + SELLIST_END +}; + +static ushort SecondaryWpnSelectList[] = +{ + SELLIST_START, + CONCUSSION_INDEX, + HOMING_INDEX, + IMPACTMORTAR_INDEX, + SMART_INDEX, + MEGA_INDEX, + FRAG_INDEX, + GUIDED_INDEX+WPNSEL_SKIP, + NAPALMROCKET_INDEX, + CYCLONE_INDEX, + BLACKSHARK_INDEX, + SELLIST_END +}; + + +ushort GetAutoSelectPrimaryWpnIdx(int slot) +{ + int i = -1; + + while (PrimaryWpnSelectList[i+1] != SELLIST_END) + { + if (slot == i) + return PrimaryWpnSelectList[i+1]; + i++; + } + + return WPNSEL_INVALID; +} + + +ushort GetAutoSelectSecondaryWpnIdx(int slot) +{ + int i = -1; + + while (SecondaryWpnSelectList[i+1] != SELLIST_END) + { + if (slot == i) + return SecondaryWpnSelectList[i+1]; + i++; + } + + return WPNSEL_INVALID; +} + + +void SetAutoSelectPrimaryWpnIdx(int slot, ushort idx) +{ + if (slot < 0 || slot >= MAX_PRIMARY_WEAPONS) + Int3(); + + PrimaryWpnSelectList[slot+1] = idx; +} + + +void SetAutoSelectSecondaryWpnIdx(int slot, ushort idx) +{ + if (slot < 0 || slot >= MAX_SECONDARY_WEAPONS) + Int3(); + + SecondaryWpnSelectList[slot+1] = idx; +} + + +const ushort IWPNSEL_SKIP = (~WPNSEL_SKIP); + +#define WPNINDEX(_index) (sel_list[(_index)]&IWPNSEL_SKIP) + + +// automatically switches weapon up to next level in autoselect order to this value. and type. +int SwitchPlayerWeapon(int weapon_type) +{ + player *plr; + ship *ship; + int new_index; + void (*setwpnfunc)(int,int); // Call either primary or secondary set weapon function + ushort *sel_list; + int plr_wpn_index; + + plr = &Players[Player_num]; + ship = &Ships[plr->ship_index]; + plr_wpn_index = plr->weapon[weapon_type].index; + + setwpnfunc = (weapon_type == PW_SECONDARY) ? SetSecondaryWeapon : SetPrimaryWeapon; + sel_list = (weapon_type == PW_SECONDARY) ? SecondaryWpnSelectList : PrimaryWpnSelectList; + + new_index = 0; + while (WPNINDEX(new_index) != plr_wpn_index && WPNINDEX(new_index) != SELLIST_END) + new_index++; + + if (WPNINDEX(new_index) == SELLIST_END) { + Int3(); // every weapon should be in list that's in the game. bad + new_index = 1; // 1st item after SELLIST_START + } + +// select weapon if we can. find a weapon we can select, if we go back to start, return. +// THIS CODE IS SIMILAR BUT NOT THE SAME AS THE AUTO SELECT CODE. + int old_index = new_index; + new_index++; + + while (old_index != new_index) + { + if (WPNINDEX(new_index) == SELLIST_END) { + new_index = 0; + } + else if (WPNINDEX(new_index) == SELLIST_START) { + new_index++; + } + else { + ushort wpn_index = WPNINDEX(new_index); + otype_wb_info *wb = &ship->static_wb[wpn_index]; + int slot = (weapon_type == PW_SECONDARY) ? (((wpn_index-SECONDARY_INDEX) % NUM_SECONDARY_SLOTS)+NUM_PRIMARY_SLOTS) : (wpn_index % NUM_PRIMARY_SLOTS); + + //mprintf((0, "wpn_index = %d\n", wpn_index)); + + if ((Players[Player_num].weapon_flags & HAS_FLAG(wpn_index)) && !(sel_list[new_index]&WPNSEL_SKIP)) { + + + + if (wpn_index >= SECONDARY_INDEX && wb->ammo_usage && (wb->ammo_usage <= plr->weapon_ammo[wpn_index])) + { + // we've found a weapon to select to that uses ammo! + (*setwpnfunc)(wpn_index,slot); + LOGFILE((_logfp, "ammo wpn: switch to new index %d\n", wpn_index)); + break; + } + else if (wpn_index < SECONDARY_INDEX && wb->ammo_usage && plr->weapon_ammo[wpn_index]) + { + // we've found a weapon to select to that uses ammo! + (*setwpnfunc)(wpn_index,slot); + LOGFILE((_logfp, "ammo wpn: switch to new index %d\n", wpn_index)); + break; + } + else if (!wb->ammo_usage && (plr->energy >= wb->energy_usage)) + { + // we've found an energy weapon to select to! + (*setwpnfunc)(wpn_index,slot); + LOGFILE((_logfp, "energy wpn:switch to new index %d\n", wpn_index)); + break; + } + } + + new_index++; + //mprintf((0, "new_index = %d\n", new_index)); + } + } + + return sel_list[new_index] & (~WPNSEL_SKIP); +} + + +// Auto selects a weapon, usually the next best weapon. +// step_up = true if we're going to next higher in order. +// step_up = false if we're going to next lower in order. +bool AutoSelectWeapon(int weapon_type, int new_wpn) +{ + player *plr; + ship *ship; + ushort *sel_list; // a weapon selection list + int weapon_index; // the current weapon index + int list_index; // index into a selection list + int list_initial; // initial index in list. + int slot; + bool sel_new_wpn = false; + + void (*setwpnfunc)(int,int); // Call either primary or secondary set weapon function + + LOGFILE((_logfp, "Entering AutoSelect\n")); + + ASSERT((weapon_type == PW_PRIMARY) || (weapon_type == PW_SECONDARY)); + plr = &Players[Player_num]; + weapon_index = plr->weapon[weapon_type].index; + ship = &Ships[plr->ship_index]; + +// choose primary or secondary list and select function + sel_list = (weapon_type == PW_SECONDARY) ? SecondaryWpnSelectList : PrimaryWpnSelectList; + setwpnfunc = (weapon_type == PW_SECONDARY) ? SetSecondaryWeapon : SetPrimaryWeapon; + + list_index = 0; + list_initial = 0; + while (WPNINDEX(list_index) != SELLIST_END) + { + if (WPNINDEX(list_index) == weapon_index) + list_initial = list_index; + list_index++; + } + + list_index--; + +// this code takes care of selecting a GIVEN new weapon over the existing. + if (new_wpn > -1) { + while (WPNINDEX(list_index) != SELLIST_START && WPNINDEX(list_index) != new_wpn) + list_index--; + if (!(sel_list[list_index] & WPNSEL_SKIP)) { + if (list_initial >= list_index) { + ushort index = WPNINDEX(list_initial); + otype_wb_info *wb = &ship->static_wb[index]; + if (index >= SECONDARY_INDEX && wb->ammo_usage && (wb->ammo_usage <= plr->weapon_ammo[index])) + { + LOGFILE((_logfp, "keep current ammo weapon...(ind=%d)\n", list_index)); + return sel_new_wpn; // the current weapon supercedes the new weapon, (or is the same) so return + } + else if (index < SECONDARY_INDEX && wb->ammo_usage && plr->weapon_ammo[index]) { + LOGFILE((_logfp, "keep current ammo weapon...(ind=%d)\n", list_index)); + return sel_new_wpn; // the current weapon supercedes the new weapon, (or is the same) so return + } + else if (!wb->ammo_usage && (plr->energy >= wb->energy_usage)) { + LOGFILE((_logfp, "keep current energy weapon...(ind=%d)\n", list_index)); + return sel_new_wpn; // the current weapon supercedes the new weapon, (or is the same) so return + } + LOGFILE((_logfp, "tried to keep current weapon, but no ammo!...(ind=%d)\n", list_index)); + } + } + else { + LOGFILE((_logfp, "tried to autoselect skipped weapon, will not do...(ind=%d)\n", list_index)); + return sel_new_wpn; + } + } + + while (1) + { + ushort index = sel_list[list_index]; + otype_wb_info *wb = &ship->static_wb[index]; + + if (index == SELLIST_START) { + break; + } + else { + // we have a real weapon coming up, lets see if we have it, and if we do, then do we have + // ammo + if (!(index & WPNSEL_SKIP)) { + if (plr->weapon_flags & HAS_FLAG(index)) { + slot = (weapon_type == PW_SECONDARY) ? (((index-SECONDARY_INDEX) % NUM_SECONDARY_SLOTS)+NUM_PRIMARY_SLOTS) : (index % NUM_PRIMARY_SLOTS); + if (new_wpn == -1) { + // if no new weapon, then select to next best weapon that can be used + if (index >= SECONDARY_INDEX && wb->ammo_usage && (wb->ammo_usage <= plr->weapon_ammo[index])) + { + // we've found a weapon to select to that uses ammo! + LOGFILE((_logfp, "ammo wpn: auto select to new index %d\n", index)); + (*setwpnfunc)(index,slot); + break; + } + else if (index <= SECONDARY_INDEX && wb->ammo_usage && plr->weapon_ammo[index]) + { + // we've found a weapon to select to that uses ammo! + LOGFILE((_logfp, "ammo wpn: auto select to new index %d\n", index)); + (*setwpnfunc)(index,slot); + break; + } + else if (!wb->ammo_usage && (plr->energy >= wb->energy_usage)) { + // we've found an energy weapon to select to! + LOGFILE((_logfp, "energy wpn: auto select to new index %d\n", index)); + (*setwpnfunc)(index,slot); + break; + } + } + else { + if (index < SECONDARY_INDEX || !wb->ammo_usage || (index >= SECONDARY_INDEX && wb->ammo_usage && (wb->ammo_usage <= plr->weapon_ammo[index]))) + { + // if new weapon, then always select that weapon (already assumes that new weapon is better.) + LOGFILE((_logfp, "auto select to new index with new weapon!! %d\n", index)); + (*setwpnfunc)(index,slot); + sel_new_wpn = true; + break; + } + } + } + } + } + + list_index--; + } + + return sel_new_wpn; +} + +// Draws an alpha blended polygon over the entire 3d rendering scene +// The r,g,b floats specify the color +void DrawAlphaBlendedScreen (float r,float g,float b,float alpha) +{ + g3Point *pntlist[4],points[4]; + ddgr_color color; + int i; + + color=GR_RGB(r*255,g*255,b*255); + + // Set our four corners to cover the screen + points[0].p3_sx=0; + points[0].p3_sy=0; + points[1].p3_sx=Game_window_w; + points[1].p3_sy=0; + points[2].p3_sx=Game_window_w; + points[2].p3_sy=Game_window_h; + points[3].p3_sx=0; + points[3].p3_sy=Game_window_h; + + for (i=0;i<4;i++) + { + points[i].p3_z=0; + points[i].p3_flags=PF_PROJECTED; + pntlist[i]=&points[i]; + } + + rend_SetZBufferState (0); + rend_SetTextureType (TT_FLAT); + rend_SetAlphaType (AT_CONSTANT); + rend_SetAlphaValue (alpha*255); + rend_SetLighting (LS_NONE); + rend_SetFlatColor (color); + + rend_DrawPolygon2D( 0, pntlist, 4 ); + rend_SetZBufferState (1); +} + + +// Retreives the weapon in the weapon array based off of an 'index' from 0-19 (non-mapped +// primary and secondaries) +weapon *GetWeaponFromIndex(int player, int index) +{ + ship *ship = &Ships[Players[player].ship_index]; + otype_wb_info *wb = &ship->static_wb[index]; + object *pobj = &Objects[Players[player].objnum]; + poly_model *pm= &Poly_models[pobj->rtype.pobj_info.model_num]; + dynamic_wb_info *dyn_wb = &pobj->dynamic_wb[index]; + int cur_m_bit; + + for(cur_m_bit = 0; cur_m_bit < pm->poly_wb[0].num_gps; cur_m_bit++) + { + if(wb->gp_fire_masks[dyn_wb->cur_firing_mask] & (0x01 << cur_m_bit)) + { + return &Weapons[wb->gp_weapon_index[cur_m_bit]]; + } + } + + + return NULL; +} + + diff --git a/Descent3/weapon.h b/Descent3/weapon.h new file mode 100644 index 000000000..6be6b1d62 --- /dev/null +++ b/Descent3/weapon.h @@ -0,0 +1,508 @@ +/* + * $Logfile: /DescentIII/main/weapon.h $ + * $Revision: 87 $ + * $Date: 4/12/99 12:49p $ + * $Author: Jeff $ + * + * Header for weapon.cpp & weaponfire.cpp + * + * $Log: /DescentIII/main/weapon.h $ + * + * 87 4/12/99 12:49p Jeff + * added recoil_force to weapon's page + * + * 86 4/05/99 4:39p Jason + * added groovy new smoke trails + * + * 85 3/31/99 10:27a Samir + * code to reset auto select states when ship is initializiaed and code to + * save and load these states from disk. + * + * 84 3/05/99 12:19p Matt + * Delete now-unused function AddWeaponAmmoToPlayer() + * + * 83 3/03/99 5:09p Samir + * AddWeaponAmmoToPlayer added. + * + * 82 3/02/99 7:22p Chris + * Add the 'fire at target' check box for weapon batteries + * + * 81 2/22/99 2:05p Jason + * added different damages for players and generics + * + * 80 2/08/99 5:29p Jason + * added more weapon options + * + * 79 1/25/99 7:43a Chris + * Added the GUID (Goal Unique Id) and added the ability for weapon + * batteries to always fire exactly forward. + * + * 78 1/11/99 2:14p Chris + * Massive work on OSIRIS and AI + * + * 77 11/13/98 12:30p Jason + * changes for weapons + * + * 76 10/22/98 2:41p Samir + * fixed autoselection for good. + * + * 75 10/21/98 11:52p Samir + * print different message if autoselecting a recently acquired weapon. + * + * 74 10/15/98 6:46p Chris + * Added custom size for weapons + * + * 73 10/07/98 9:36p Samir + * added a function to switch a player's weapon to another in the given + * weapon class. + * + * 72 10/01/98 6:56p Jason + * turned off energy weapon hit effects if the object getting hit is the + * viewer - this helps cut down on the general clutter in heavy combat + * + * 71 9/30/98 4:31p Samir + * added functions to handle weapon select list. + * + * 70 9/28/98 10:43a Chris + * Fixed a bug with HAS_FLAG + * + * 69 9/10/98 5:56p Chris + * Added more shit to the matcen code :) + * + * 68 9/08/98 5:35p Jason + * added WF_NO_ROTATE flag to weapons + * + * 67 9/01/98 4:41p Matt + * Removed obsolete fields in the weapon structure + * + * 66 8/31/98 1:35p Keneta + * Made some use count unsigned shorts + * + * 65 8/31/98 11:15a Keneta + * Upp'ed max weapons + * + * 64 8/19/98 2:48p Jason + * fixed some weapon firing and weapon exploding bugs + * + * 63 8/06/98 1:00p Chris + * Added new homing flags + * + * 62 8/03/98 1:09p Jason + * added some more weapons flags + * + * 61 7/31/98 5:23p Jason + * added ship armor scalars + * + * 60 7/30/98 6:03p Jason + * (Jeff) checked in so we can compile...missing 2 WF_* flags + * + * 59 7/30/98 11:09a Jason + * added weapons that freeze and deform terrain + * + * 58 7/06/98 11:52a Jason + * added countermeasure prototype + * + * 57 7/01/98 12:12p Jason + * added countermeasures + * + * 56 6/22/98 6:26p Jason + * added gravity field effect for weapons + * + * 55 6/19/98 12:04p Jason + * + * 54 6/16/98 10:54a Jeff + * + * 53 5/26/98 5:06p Samir + * changed name of weapon indices and added function to get weapon's icon + * image. + * + * 52 5/25/98 8:36p Matt + * Added code to set different sizes for different weapon scorch marks. + * Also, don't leave scorch marks on lights. + * + * 51 5/25/98 8:17p Matt + * Moved MAX_PLAYER_WEAPONS fromw weapon.h to player.h, so that the latter + * doesn't need to include the former, drastically speeding build times + * when weapon.h is changed. + * + * 50 5/25/98 6:39p Jason + * got icons working for weapons + * + * 49 5/22/98 12:34p Matt + * Added scorch mark/bullet hole system. + * + * 48 5/19/98 4:42a Chris + * Added shockwave's -- enjoy. :) + * + * 47 5/07/98 2:40p Chris + * Added death_dot and bounce sound for weapons + * + * 46 4/24/98 8:02a Samir + * added a short weapon name array. + * + * 45 4/19/98 5:00p Jason + * added cool napalm effect, plus made object effects dynamically + * allocated + * + * 44 4/17/98 1:59p Jason + * added cool object effects + * + * 43 4/15/98 12:22p Jason + * lots of miscellaneous stuff pertaining to lighting and vis effects + * + * 42 4/10/98 2:16p Jason + * fixed guided missile problems + * + * 41 4/10/98 12:39p Jason + * added expanding explosion bitmaps + * + * 40 4/09/98 12:05p Chris + * Added parenting for all object types. :) + * + * 39 4/07/98 3:31p Jason + * got particle effects working with weapons + * + * 38 4/06/98 4:53p Jason + * got pageable polymodels working correctly with editor + * + * 37 2/17/98 11:32p Matt + * Fixed player wepon fire problem when down_count != 0 && down_time == 0 + * Add function to clear out player firing activity for when die or switch + * weapons + * + */ + +#ifndef WEAPON_H +#define WEAPON_H + +#include "pstypes.h" +#include "manage.h" +#include "object.h" +#include "objinfo.h" +#include "weapon_external.h" + +#define MAX_PRIMARY_WEAPONS 10 +#define MAX_SECONDARY_WEAPONS 10 + +#define MAX_WEAPON_NOT_HIT_PARENT_TIME 3.0f + +// This can be changed safely +#define MAX_WEAPONS 200 + +// THIS CONSTANT SHOULD EQUAL THE NUMBER OF WEAPONS SELECTABLE!!! +#define MAX_STATIC_WEAPONS 21 + +#define DEFAULT_WEAPON_SIZE 1.0 // Default size of a weapon -- used for bitmap weapons + +#define HAS_FLAG(a) (1<<(a)) + +// Weapon flags +#define WF_HUD_ANIMATED (1<<0) +#define WF_IMAGE_BITMAP (1<<1) // whether or not the firing image is a bitmap or model +#define WF_SMOKE (1<<2) // Weapon drops smoke as it moves +#define WF_MATTER_WEAPON (1<<3) // This a matter weapon, as opposed to an energy weapon +#define WF_ELECTRICAL (1<<4) // This weapons fires as an electrical storm +#define WF_IMAGE_VCLIP (1<<5) // This weapon fire image is a vclip +#define WF_SPRAY (1<<6) // This weapon is a spray, like a flamethrower +#define WF_STREAMER (1<<7) // This weapon has a streamer effect attached +#define WF_INVISIBLE (1<<8) // This weapon is invisible +#define WF_RING (1<<9) // This weapon is drawn ring style +#define WF_SATURATE (1<<10) // Saturate this bitmap weapon +#define WF_BLAST_RING (1<<11) // Creates a blast ring upon explosion +#define WF_PLANAR_BLAST (1<<12) // Blast bitmap takes on the walls plane +#define WF_PLANAR (1<<13) // This weapon doesn't always face you +#define WF_ENABLE_CAMERA (1<<14) // This weapon can be used for missile camera +#define WF_SPAWNS_IMPACT (1<<15) // This weapon spawns others on impact +#define WF_SPAWNS_TIMEOUT (1<<16) // This weapon spawns others when it times out +#define WF_EXPAND (1<<17) // This weapon expands when exploding +#define WF_MUZZLE (1<<18) // This weapon produces a muzzle flash when fired +#define WF_MICROWAVE (1<<19) // This weapon makes a microwave effect on the victim +#define WF_NAPALM (1<<20) // This weapon does a napalm effect to objects it touches +#define WF_REVERSE_SMOKE (1<<21) // The smoke trail gets smaller as it ages +#define WF_GRAVITY_FIELD (1<<22) // This weapon has a gravity field +#define WF_COUNTERMEASURE (1<<23) // This weapon is a countermeasure +#define WF_SPAWNS_ROBOT (1<<24) // This weapon spawns a robot upon death +#define WF_FREEZE (1<<25) // This weapon slows a ship/object down +#define WF_TIMEOUT_WALL (1<<26) // This weapon times out like a wall hit +#define WF_PLANAR_SMOKE (1<<27) // This weapon has a planar smoke trail instead of a blob +#define WF_SILENT_HOMING (1<<28) // This weapon does not give a homing lock sound +#define WF_HOMING_SPLIT (1<<29) // This weapon homes when it splits +#define WF_NO_ROTATE (1<<30) // This weapon does not rotate as a bitmap +#define WF_CUSTOM_SIZE (1<<31) // This weapon uses a custom size + +#define MAX_LASER_LEVEL 4 +#define MAX_SUPER_LASER_LEVEL 6 + +#define PRIMARY_INDEX 0 +#define SECONDARY_INDEX 10 + +// These defines must correspond to the Static_weapons_names array + +#define MAX_WEAPON_SOUNDS 7 +#define WSI_FIRE 0 // chrishack -- removed! Change const when we add a new slot + // grep for occurances of WSI_FIRE and fix (or remove) old code +#define WSI_IMPACT_WALL 1 +#define WSI_FLYING 2 +#define WSI_IMPACT_ROBOT 3 +#define WSI_BOUNCE 4 + +typedef struct +{ + char name[PAGENAME_LEN]; + float player_damage; // how much damage a full impact causes a player + float generic_damage; // how much damage a full impact causes a robot + float alpha; // What alpha to draw this weapon with + short sounds[MAX_WEAPON_SOUNDS]; // sounds for various things + short hud_image_handle; // a handle to a bitmap or vclip for the hud display + short fire_image_handle; // model or bitmap. Shown when you fire this weapon + short explode_image_handle; // exploding vclip + short smoke_handle; // smoke trail handle to texture + short spawn_handle; // weapon handle that gets spawned + short alternate_spawn_handle; // weapon handle that gets spawned (sometimes) + short robot_spawn_handle; // robot that gets spawned as a countermeasure + short particle_handle; // particle handle to texture + short icon_handle; + short scorch_handle; // handle for scorch bitmap, or -1 for none + ubyte spawn_count; // how many of spawn handle gets created + ubyte alternate_chance; // how often the alternate spawn weapon gets chosen (0 to 100) + + unsigned short used; + + ubyte particle_count; + ubyte terrain_damage_depth; + + float terrain_damage_size; + + float scorch_size; // how big the scorch mark is + + int flags; // see above + + float size; + float life_time; + float thrust_time; + float impact_size; + float impact_time; + float impact_player_damage,impact_generic_damage; + float impact_force; + float explode_size; + float explode_time; + float particle_size; + float particle_life; + float gravity_size; + float gravity_time; + float custom_size; + float homing_fov; + float recoil_force; + + light_info lighting_info; + physics_info phys_info; + +} weapon; + +typedef struct +{ + float total_time; + +} fusion_effect; + +extern float Primary_ramp_time,Secondary_ramp_time; + +extern int Num_weapons; +extern weapon Weapons[MAX_WEAPONS]; +extern const char *Static_weapon_names[]; +extern int Static_weapon_names_msg[]; +extern int Static_weapon_ckpt_names[][2]; + +// Sets all weapons to unused +void InitWeapons (); + +// Allocs a weapon for use, returns -1 if error, else index on success +int AllocWeapon (); + +// Frees weapon index n +void FreeWeapon (int n); + +// Gets next weapon from n that has actually been alloced +int GetNextWeapon (int n); + +// Gets previous weapon from n that has actually been alloced +int GetPrevWeapon (int n); + +// Searches thru all weapons for a specific name, returns -1 if not found +// or index of weapon with name +int FindWeaponName (char *name); + +// Given a filename, loads either the model or vclip found in that file. If type +// is not NULL, sets it to 1 if file is model, otherwise sets it to zero +int LoadWeaponHudImage (char *filename,int *type); + +// Given a weapon handle, returns that weapons image for framenum +int GetWeaponHudImage (int handle,int framenum); + +// Given a filename, loads either the model or vclip found in that file. If type +// is not NULL, sets it to 1 if file is model, otherwise sets it to zero +int LoadWeaponFireImage (char *filename,int *type,int *anim,int pageable=1); + +// Given a weapon handle, returns that weapons firing bitmap/model +int GetWeaponFireImage (int handle,int frame); + + +// Given a weapon name, assigns that weapon to a specific index into +// the Weapons array. Returns -1 if the named weapon is not found, 0 if the weapon +// is already in its place, or 1 if successfully moved +int MatchWeaponToIndex (char *name,int dest_index); + +// Moves a weapon from a given index into a new one (above MAX_STATIC_POWERUPS) +// returns new index +int MoveWeaponFromIndex (int index); + +// This is a very confusing function. It takes all the weapons that we have loaded +// and remaps then into their proper places (if they are static). +void RemapWeapons (); + +// goes thru every entity that could possible have a weapon index (ie objects, weapons, etc) +// and changes the old index to the new index +void RemapAllWeaponObjects (int old_index,int new_index); + +// Creates a weapon +// Returns the objnum of the weapon object +int CreateWeaponObject(int weapon_num,int segnum,vector *position,int flags); + +// Creates an weapon and sends it speeding on its way +// returns the objnum of the weapon +int CreateAndFireWeapon (vector *pos,vector *dir,object *parent,int weapon_num); + +// Given an object and a weapon, fires a shot from that object +// returns the object number of the weapon +int FireWeaponFromObject (object *obj,int weapon_num,int gun_num=-1,bool f_force_forward = false,bool f_force_target = false); + + +// Draws a weapon +void DrawWeaponObject (object *obj); + +// unconditionally adds a weapon and ammo to a player. +int AddWeaponToPlayer(int slot,int weap_index, int ammo); + +//Called when a player dies or switches weapons to clear out any active weapon stuff +void ClearPlayerFiring(object *objp,int weapon_type); + +// Fires a weapon from our player. Won't fire if ammo/energy requirements aren't met. +// Parameters: weapon_type - either PW_PRIMARY or PW_SECONDARY +void FireWeaponFromPlayer(object *objp,int weapon_type,int down_count,bool down_state,float down_time); + +// Fires a flare from our player. +// It might make sense to combine this with FireWeaponFromPlayer(), or maybe not +void FireFlareFromPlayer(object *objp); + +// Does per frame weapon code +void WeaponDoFrame(object *obj); + +// Returns the position and the normal of a gun point +bool WeaponCalcGun(vector *gun_point, vector *gun_normal, object *obj, int gun_num); + +// Checks for relation between weapons and other objects +bool ObjectsAreRelated( int o1, int o2 ); + +// A quick way to see where a weapon hits. Weapons make debris. +void CreateWeaponDebris(object *obj); + +// Selects a weapon +void SelectWeapon(int slot); + +// automatically switches primary weapon to this value. and type. +int SwitchPlayerWeapon(int weapon_type); + +// Auto selects a weapon, usually the next best weapon. +// weapon_type is either PW_PRIMARY or PW_SECONDARY +// if new_wpn != -1, then we will see if the current weapon is inferior, to new weapon. If +// it isn't, then the new weapon is selected. +// returns true if selecting new weapon, otherwise false. +bool AutoSelectWeapon(int weapon_type, int new_wpn=-1); + +// is a weapon secondary or primary? +bool IsWeaponSecondary(int index); + +// used for sequencing +// resets memory for what slots have high priority weapons (laser->super_laser, for instance) when user selects +void ResetWeaponSelectStates(ushort new_state=0); + +// save and load weapon state information +void SaveWeaponSelectStates(CFILE *fp); +void LoadWeaponSelectStates(CFILE *fp); + +// Draws an alpha blended polygon over the entire 3d rendering scene +// The r,g,b floats specify the color +void DrawAlphaBlendedScreen (float r,float g,float b,float alpha); + +// Does the weapon spray effect for an object +void DoSprayEffect (object *obj,otype_wb_info *static_wb,ubyte wb_index); + +// Plays the animation that accompanies a weapon death +void DoWeaponExploded (object *,vector *norm=NULL,vector *collision_point=NULL,object *hit_object=NULL); + +void TimeoutWeapon (object *); + +// Creates chidren from a dying weapon +void CreateImpactSpawnFromWeapon (object *obj,vector *norm); + +// Releases the guided missile of a passed in player +void ReleaseGuidedMissile (int slot); + +// Releases the user timeout of a passed in player +void ReleaseUserTimeoutMissile (int slot); + +// Retreives the weapon in the weapon array based off of an 'index' from 0-19 (non-mapped +// primary and secondaries) (we need a ship too.) +weapon *GetWeaponFromIndex(int player, int index); + + +// Creates a robot as a countermeasure +void CreateRobotSpawnFromWeapon (object *obj); + +// Given a parent object and a weapon id, creates that countermeasure +void CreateCountermeasureFromObject (object *parent,int weapon_id); + + +////////////////////////////////////////////////////////////////////////////// +const ushort WPNSEL_SKIP = 0x8000, + WPNSEL_INVALID = 0xffff; + + +const ushort DefaultPrimarySelectList[MAX_PRIMARY_WEAPONS] = +{ + LASER_INDEX, + VAUSS_INDEX, + MICROWAVE_INDEX, + PLASMA_INDEX, + FUSION_INDEX, + SUPER_LASER_INDEX, + MASSDRIVER_INDEX, + NAPALM_INDEX, + EMD_INDEX, + OMEGA_INDEX +}; + + +const ushort DefaultSecondarySelectList[MAX_SECONDARY_WEAPONS] = +{ + CONCUSSION_INDEX, + HOMING_INDEX, + IMPACTMORTAR_INDEX, + SMART_INDEX, + MEGA_INDEX, + FRAG_INDEX, + GUIDED_INDEX+WPNSEL_SKIP, + NAPALMROCKET_INDEX, + CYCLONE_INDEX, + BLACKSHARK_INDEX +}; + + +// weapon auto selection info. +ushort GetAutoSelectPrimaryWpnIdx(int slot); +ushort GetAutoSelectSecondaryWpnIdx(int slot); +void SetAutoSelectPrimaryWpnIdx(int slot, ushort idx); +void SetAutoSelectSecondaryWpnIdx(int slot, ushort idx); + + +#endif \ No newline at end of file diff --git a/Descent3/weapon_external.h b/Descent3/weapon_external.h new file mode 100644 index 000000000..bc1c6025b --- /dev/null +++ b/Descent3/weapon_external.h @@ -0,0 +1,28 @@ +#ifndef WEAPON_EXTERNAL_H_ +#define WEAPON_EXTERNAL_H_ + +#define LASER_INDEX 0 +#define VAUSS_INDEX 1 +#define MICROWAVE_INDEX 2 +#define PLASMA_INDEX 3 +#define FUSION_INDEX 4 +#define SUPER_LASER_INDEX 5 +#define MASSDRIVER_INDEX 6 +#define NAPALM_INDEX 7 +#define EMD_INDEX 8 +#define OMEGA_INDEX 9 +#define CONCUSSION_INDEX 10 +#define HOMING_INDEX 11 +#define IMPACTMORTAR_INDEX 12 +#define SMART_INDEX 13 +#define MEGA_INDEX 14 +#define FRAG_INDEX 15 +#define GUIDED_INDEX 16 +#define NAPALMROCKET_INDEX 17 +#define CYCLONE_INDEX 18 +#define BLACKSHARK_INDEX 19 + +#define FLARE_INDEX 20 + + +#endif \ No newline at end of file diff --git a/Descent3/weather.cpp b/Descent3/weather.cpp new file mode 100644 index 000000000..e84270a46 --- /dev/null +++ b/Descent3/weather.cpp @@ -0,0 +1,335 @@ +#include "pserror.h" +#include "pstypes.h" +#include "fireball.h" +#include "weather.h" +#include "viseffect.h" +#include "object.h" +#include "terrain.h" +#include "room.h" +#include "game.h" +#include "soundload.h" +#include "hlsoundlib.h" +#include "sounds.h" + +#include + +#include "psrand.h" +weather Weather={0}; + +int ThunderA_sound_handle=-1; +int ThunderB_sound_handle=-1; + + +// resets the weather so there is nothing happening +void ResetWeather () +{ + Weather.flags=0; + Weather.last_lightning_evaluation_time=0; +} + +// Makes droplets appear on the windshield, plus makes rain fall in the distance +void DoRainEffect () +{ + // See how many droplets to create on the windshield + // This is dependant on how fast the player is moving forward + int randval=1+((1.0-Weather.rain_intensity_scalar)*MAX_RAIN_INTENSITY); + + if (randval<20) + randval=20; + + vector vel=Viewer_object->mtype.phys_info.velocity; + float mag=vm_GetMagnitudeFast (&vel); + vel/=mag; + + float scalar=vm_DotProduct (&vel,&Viewer_object->orient.fvec); + + vector upvec={0,1.0,0}; + + scalar*=(1+(mag/100)); + + randval-=scalar*10; + + if (randval<2) + randval=2; + else if (randval>80) + randval=80; + + if ((upvec * Viewer_object->orient.uvec)<0) + randval=8000; // Make sure rain does fall upwards + + if (Viewer_object->type==OBJ_PLAYER && OBJECT_OUTSIDE (Viewer_object) && (ps_rand()%randval)==0) + { + // Put some droplets on the windshield + vector pos={0,0,0}; + + pos.x=((ps_rand()%1000)-500)/250.0; + pos.y=((ps_rand()%1000)-500)/350.0; + pos.z=3.0; + + int visnum=VisEffectCreate (VIS_FIREBALL,RAINDROP_INDEX,Viewer_object->roomnum,&pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + float life=.4+((ps_rand()%500)/1000.0); + vis->lifeleft=life; + vis->lifetime=life; + vis->size=.01+((ps_rand()%500)/3000.0); + + if (vis->size>.05) + { + Sound_system.Play2dSound(SOUND_RAINDROP); + } + + vis->flags |=VF_WINDSHIELD_EFFECT; + } + } + + // Create some rain in the distance + + if (OBJECT_OUTSIDE(Viewer_object)) + { + int num=20+(ps_rand()%15); + angvec angs; + int i; + + vm_ExtractAnglesFromMatrix (&angs,&Viewer_object->orient); + matrix mat; + vm_AnglesToMatrix (&mat,0,angs.h,0); + + for (i=0;ipos; + + float z=((ps_rand()%1000)/1000.0)*700; + float x=(((ps_rand()%1000)-500)/500.0)*300; + float y=(((ps_rand()%1000)-500)/500.0)*200; + + pos+=x*mat.rvec; + pos+=y*mat.uvec; + pos+=z*mat.fvec; + + // Create falling rain + int visnum=VisEffectCreate (VIS_FIREBALL,FADING_LINE_INDEX,Viewer_object->roomnum,&pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + float life=.001f; + vis->lifeleft=life; + vis->lifetime=life; + vis->lighting_color=GR_RGB16 (200,200,255); + vis->end_pos=pos; + vis->end_pos.y+=20; + vis->flags |=VF_WINDSHIELD_EFFECT; + vis->pos-=(Viewer_object->mtype.phys_info.velocity/2); + } + } + + num/=2; + for (i=0;ipos; + + float z=((ps_rand()%1000)/1000.0)*700; + float x=(((ps_rand()%1000)-500)/500.0)*300; + float y=(((ps_rand()%1000)-500)/500.0)*200; + + pos+=x*mat.rvec; + pos+=y*mat.uvec; + pos+=z*mat.fvec; + + if (pos.z<0 || pos.z>=TERRAIN_DEPTH*TERRAIN_SIZE) + continue; + if (pos.x<0 || pos.x>=TERRAIN_WIDTH*TERRAIN_SIZE) + continue; + + // Create puddle drops on the terrain + vector norm; + float ypos=GetTerrainGroundPoint (&pos,&norm); + pos.y=ypos; + int visnum=VisEffectCreate (VIS_FIREBALL,PUDDLEDROP_INDEX,Viewer_object->roomnum,&pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + float life=.2f; + float size=.7+((ps_rand()%10)/20.0); + vis->lifeleft=life; + vis->lifetime=life; + vis->end_pos=norm; + vis->flags |=VF_PLANAR; + } + } + + } +} + + +// Creates snow for this frame +void DoSnowEffect() +{ + if (OBJECT_OUTSIDE(Viewer_object)) + { + int num=20+(ps_rand()%15); + + if (Weather.snowflakes_to_create+num>250) + { + num=250-Weather.snowflakes_to_create; + + if (num<1) + { + Weather.snowflakes_to_create=0; + return; + } + } + + //angvec angs; + matrix mat; + + //vm_ExtractAnglesFromMatrix (&angs,&Viewer_object->orient); + //vm_AnglesToMatrix (&mat,0,angs.h,0); + + mat=Viewer_object->orient; + + for (int i=0;ipos; + + float z=((ps_rand()%1000)/1000.0)*300; + float x=(((ps_rand()%1000)-500)/500.0)*200; + int y=(ps_rand()%80); + + pos+=x*mat.rvec; + pos+=y*mat.uvec; + pos+=z*mat.fvec; + + if (pos.z<0 || pos.z>=TERRAIN_DEPTH*TERRAIN_SIZE) + continue; + if (pos.x<0 || pos.x>=TERRAIN_WIDTH*TERRAIN_SIZE) + continue; + + // Create falling rain + vector down_vec={0,-30,0}; + int visnum=VisEffectCreate (VIS_FIREBALL,SNOWFLAKE_INDEX,Viewer_object->roomnum,&pos); + if (visnum>=0) + { + vis_effect *vis=&VisEffects[visnum]; + float life=1.5+((ps_rand()%100)/100.0); + vis->lifeleft=life; + vis->lifetime=life; + vis->lighting_color=GR_RGB16 (200,200,(ps_rand()%50)+200); + vis->size=((ps_rand()%1000)/1000.0)+.5; + + vis->flags |=VF_WINDSHIELD_EFFECT|VF_USES_LIFELEFT; + + vis->velocity=down_vec; + } + } + } + + Weather.snowflakes_to_create=0; +} + +// does all the weather stuff that is going to be done for this frame +void DoWeatherForFrame () +{ + int i; + int hear_thunder=0; + + if (OBJECT_OUTSIDE(Player_object)) + hear_thunder=1; + else + { + int roomnum=Player_object->roomnum; + room *rp=&Rooms[roomnum]; + + for (i=0;inum_portals && hear_thunder==0;i++) + { + if (rp->portals[i].croom==-1) + hear_thunder=1; + else if (Rooms[rp->portals[i].croom].flags & RF_EXTERNAL) + hear_thunder=1; + + } + } + + if (Weather.flags & WEATHER_FLAGS_RAIN) + DoRainEffect(); + + if (Weather.flags & WEATHER_FLAGS_SNOW) + DoSnowEffect(); + + if (Weather.flags & WEATHER_FLAGS_LIGHTNING) + { + if ((Gametime-Weather.last_lightning_evaluation_time)>Weather.lightning_interval_time) + { + // Check to see if we should draw some lightning + Weather.last_lightning_evaluation_time=Gametime; + ASSERT (Weather.lightning_rand_value>0); + if (ps_rand() <= Weather.lightning_rand_value) + { + // Start the lightning sequence + Weather.lightning_sequence=1; + + if ((ps_rand()%7)==0 && hear_thunder) + Sound_system.Play2dSound(SOUND_LIGHTNING); + } + + if (hear_thunder && (ps_rand()%(Weather.lightning_rand_value*3))==0) + { + if (ThunderA_sound_handle==-1) + ThunderA_sound_handle=FindSoundName ("ThunderA"); + if (ThunderB_sound_handle==-1) + ThunderB_sound_handle=FindSoundName ("ThunderB"); + + if (ps_rand()%2) + Sound_system.Play2dSound(ThunderA_sound_handle); + else + Sound_system.Play2dSound(ThunderB_sound_handle); + } + } + } +} + +// Sets the state of the rain to on or off, plus sets the intensity of the rain (0 to 1) +void SetRainState (int on,float intensity) +{ + if (on) + { + Weather.flags |=WEATHER_FLAGS_RAIN; + Weather.rain_intensity_scalar=intensity; + } + else + { + Weather.flags &=~WEATHER_FLAGS_RAIN; + } +} + +// Sets the state of the rain to on or off, plus sets the intensity of the rain (0 to 1) +void SetSnowState (int on,float intensity) +{ + if (on) + { + Weather.flags |=WEATHER_FLAGS_SNOW; + Weather.snow_intensity_scalar=intensity; + } + else + { + Weather.flags &=~WEATHER_FLAGS_SNOW; + } +} + +// Sets the state of lightning to on or off, plus allows the setting of how often to check +// for lightning and the randomness at which lightning happens +void SetLightningState (int on,float interval_time,int randval) +{ + if (on) + { + Weather.flags |=WEATHER_FLAGS_LIGHTNING; + Weather.lightning_sequence=0; + Weather.lightning_rand_value=randval; + Weather.lightning_interval_time=interval_time; + } + else + { + Weather.flags &=~WEATHER_FLAGS_LIGHTNING; + } +} \ No newline at end of file diff --git a/Descent3/weather.h b/Descent3/weather.h new file mode 100644 index 000000000..1e8bca667 --- /dev/null +++ b/Descent3/weather.h @@ -0,0 +1,55 @@ +#ifndef WEATHER_H +#define WEATHER_H + +#define WEATHER_FLAGS_RAIN 1 +#define WEATHER_FLAGS_LIGHTNING 2 +#define WEATHER_FLAGS_SNOW 4 + + +#define MAX_RAIN_INTENSITY 50 +#define MAX_SNOW_INTENSITY 200 + +typedef struct +{ + int flags; // see weather flags, above + + float snow_intensity_scalar; // how hard it is snowing + float rain_intensity_scalar; // how hard its raining + int rain_color; // the color of the rain + int lightning_color; // the color of the lightning + int sky_flash_color; // the color of the sky when lightning occurs + + ubyte lightning_sequence; + + float last_lightning_evaluation_time; + float lightning_interval_time; + int lightning_rand_value; + + int snowflakes_to_create; + +} weather; + +extern weather Weather; + +// resets the weather so there is nothing happening +void ResetWeather (); + +// Makes droplets appear on the windshield, plus makes rain fall in the distance +void DoRainEffect (); + +// does all the weather stuff that is going to be done for this frame +void DoWeatherForFrame (); + +// Sets the state of the rain to on or off, plus sets the intensity of the rain (0 to 1) +void SetRainState (int on,float intensity); + +// Sets the state of lightning to on or off, plus allows the setting of how often to check +// for lightning and the randomness at which lightning happens +void SetLightningState (int on,float interval_time,int randval); + +// Sets the state of the snow to on or off, plus sets the intensity of the snow (0 to 1) +void SetSnowState (int on,float intensity); + + +#endif + diff --git a/Descent3/winmain.cpp b/Descent3/winmain.cpp new file mode 100644 index 000000000..4f0d12527 --- /dev/null +++ b/Descent3/winmain.cpp @@ -0,0 +1,741 @@ +#include +#include +#include +#include +#include "mono.h" +#include "descent.h" +#include "texture.h" +#include "application.h" +#include "appdatabase.h" +#include "pserror.h" +#include "args.h" +#include "init.h" +#include "dedicated_server.h" +#include "resource.h" + +const char *English_strings[] = { + "Descent 3 under Windows NT requires version 4.0 or greater of NT to run.", + "Descent 3 requires Windows 9x, NT 4.0 or greater to run.", + "", + "You must install DirectX through the Descent 3 Launcher before continuing.", + "You must install at least Service Pack 3 to run Descent 3 under Windows NT 4.0.", + "Failed to retrieve DirectX version.", + "Descent 3 requires DirectX 3 or greater on this machine.", + "This version of Windows NT doesn't have DirectX 3 or greater installed.", + "Your processor and system must support Katmai to run this game." +}; + +const char *French_strings[] = { + "L'exTcution de Descent 3 sous Windows NT nTcessite la version 4.0 ou ultTrieure de NT.", + "L'exTcution de Descent 3 nTcessite Windows 9x, NT 4.0 ou ultTrieur.", + "", + "Vous devez installer DirectX a partir de l'installateur Descent 3 avant de continuer.", + "Vous devez installer au moins Service Pack 3 pour exTcuter Descent 3 sous Windows NT 4.0.", + "+chec de la rTcupTration de DirectX.", + "Descent 3 nTcessite DirectX 3 ou ultTrieur sur ce systFme.", + "Cette version de Windows NT n'est pas munie de DirectX 3 ou ultTrieur.", + "Votre processeur et votre systFme doivent prendre en charge Katmai pour exTcuter ce jeu." +}; + +const char *German_strings[] = { + "Descent3 unter Windows NT ben÷tigt fnr die Ausfnhrung die NT-Version 4.0 oder h÷her", + "Descent 3 ben÷tigt fnr die Ausfnhrung Windows 9x, NT 4.0 oder h÷her.", + "", + "Sie mnssen DirectX nber den Descent 3 Starter installieren, bevor Sie fortsetzen.", + "Sie mnssen mindestens Service Paket 3 installieren, um Descent 3 unter Windows NT 4.0 ausfnhren zu k÷nnen.", + "Die DirectX Version konnte nicht abgerufen werden.", + "Descent 3 ben÷tigt DirectX 3 oder h÷her auf diesem Computer.", + "In dieser Windows NT Version ist DirectX 3 oder h÷her nicht installiert.", + "Ihr Prozessor und System mu¯ Katmai unterstntzen, um dieses Spiel auszufnhren." +}; + +const char *Italian_strings[] = { + "Descent 3 per Windows NT richiede la versione NT 4.0 o superiore.", + "Descent 3 funziona solo con Windows 9x, NT 4.0 o superiore.", + "", + "Prima di prosegure installare DirectX per mezzo di Descent 3 Launcher.", + "Per utilizzare Descent 3 sotto Windows NT 4.0 occorre installare Service Pack 3 o sup.", + "Versione di DirectX non trovata.", + "Descent 3 richiede l'installazione di DirectX 3 o superiore.", + "DirectX 3 o superiore non trovato in questa versione di Windows NT.", + "Per questo gioco occorrono un processore e un sistema che supportino Katmai." +}; + +const char *Spanish_strings[] = { + "Descent 3 bajo Windows NT requiere version 4.0 o mejor para correr.", + "Descent 3 requiere Windows 9x, NT 4.0 o mejor para correr.", + "", + "Debe instalar DirectX desde el lanzador de Descent 3 antes de continuar.", + "Debe instalar por lo menos Service Pack 3 para correr Descent 3 bajo Windows NT 4.0.", + "Falla en la detección de la versión de DirectX.", + "Descent 3 requiere DirectX 3 o mejor en el ordenador.", + "Esta versión de Windows NT no tiene DirectX 3 o mejor instalado.", + "Vuestro procesador y ordenador deben soportar Katmai para correr este juego." +}; + +#if 0 +const char *Polish_strings[] = { + "Aby uruchomi‘ grˆ Descent 3 potrzebujesz Windows NT w wersji 4.0 lub nowszej.", + "Descent 3 wymaga Windows 9x albo Windows NT w wersji 4.0 lub nowszej.", + "", + "Zanim uruchomisz grˆ, musisz zainstalowa‘ DirectX przy u¨yciu programu startowego.", + "Musisz zainstalowa‘ co najmniej Service Pack 3, aby uruchomi‘ grˆ Descent 3 pod Windows NT 4.0.", + "Nie uda3o siˆ odczyta‘ numeru wersji sterownik¢w DirectX.", + "Descent 3 wymaga sterownik¢w DirectX 3 lub nowszych.", + "Ta wersja Windows NT nie ma zainstalowanych sterownik¢w DirectX 3 lub nowszych.", + "Tw¢j procesor musi obs3ugiwa‘ rozkazy Katmai, aby uruchomi‘ grˆ." +}; +#endif + +static int m_resource_language = 0; + +#define VENDOR_INTEL 0 +#define VENDOR_AMD 1 +#define VENDOR_CYRIX 2 +#define VENDOR_UMC 3 +#define VENDOR_CENTAUR 4 +#define VENDOR_NEXGEN 5 +#define VENDOR_UNKNOWN 6 + +typedef struct +{ + unsigned char family,model,mask; + int CPUid_level; + int vendor; + unsigned int capability; + char vendor_id[16]; +}cpuinfo; + + +int no_debug_dialog=0; + +void getcpudata(cpuinfo *info); + +// --------------------------------------------------------------------------- +// Define our operating system specific extensions to the gameos system +// --------------------------------------------------------------------------- + +class oeD3Win32App: public oeWin32Application +{ + bool shutdown, final_shutdown; + int old_screen_mode; + HANDLE hAppMutex; + +public: + oeD3Win32App(unsigned flags, HInstance hinst): + oeWin32Application(PRODUCT_NAME, flags, hinst) + { + Descent = this; + shutdown = false; + final_shutdown = false; + hAppMutex = CreateMutex(NULL, FALSE, "D3MainMutexName"); + if (!hAppMutex) { + exit(1); + } + win32_SetResourceDLL(NULL); + }; + + virtual ~oeD3Win32App() + { + win32_SetResourceDLL(NULL); + if (hAppMutex) { + CloseHandle(hAppMutex); + hAppMutex = NULL; + } + + final_shutdown = true; + }; + + void run() + { + Descent3(); + }; + +// returns 0 if we pass to default window handler. + virtual int WndProc( HWnd hwnd, unsigned msg, unsigned wParam, long lParam) + { + if (final_shutdown) + { + return oeWin32Application::WndProc(hwnd, msg, wParam, lParam); + } + + switch (msg) + { + case WM_ACTIVATEAPP: + { + if (wParam == false) + { + this->deactivate(); + + if (!shutdown) + { + ShutdownD3(); + shutdown = true; + ShowWindow((HWND)hwnd, SW_MINIMIZE); + } + } + else + { + this->activate(); + + if (shutdown) + { + ShowWindow((HWND)hwnd, SW_RESTORE); + RestartD3(); + shutdown = false; + } + } + }break; + } + + return oeWin32Application::WndProc(hwnd, msg, wParam, lParam); + } + +}; + + +class oeD3Win32Database: public oeWin32AppDatabase +{ +public: + oeD3Win32Database(); +}; + + + +// --------------------------------------------------------------------------- +// D3WinDatabase operating system specific initialization + +oeD3Win32Database::oeD3Win32Database(): + oeWin32AppDatabase() +{ + char path[_MAX_PATH]; + bool res; + +// create descent III entry if it doesn't exit. + +#if defined(EDITOR) + lstrcat(m_Basepath, "\\D3Edit"); +#elif defined(DEMO) + lstrcat(m_Basepath, "\\Descent3Demo2"); +#elif defined(OEM_V3) + lstrcat(m_Basepath, "\\Descent3_OEM_V3"); +#elif defined(OEM_AUREAL2) + lstrcat(m_Basepath, "\\Descent3_OEM_A2"); +#elif defined(OEM_KATMAI) + lstrcat(m_Basepath, "\\Descent3_OEM_KM"); +#elif defined(GAMEGAUGE) + lstrcat(m_Basepath, "\\Descent3GG"); +#elif defined(OEM) + lstrcat(m_Basepath, "\\Descent3_OEM"); +#else + lstrcat(m_Basepath, "\\Descent3"); +#endif + + + res = lookup_record(m_Basepath); + if (!res) { + res = create_record(m_Basepath); + if (!res) { + Error("Failed to create registry key for %s", PRODUCT_NAME); + } + } + +// create version key. + lstrcpy(path, m_Basepath); + lstrcat(path, "\\Version"); + res = lookup_record(path); + if (!res) { + res = create_record(path); + if (!res) { + Error("Failed to create registry key for %s", PRODUCT_NAME); + } + } + +#ifdef EDITOR //Maybe this code should be in the editor startup + lstrcpy(path, m_Basepath); + lstrcat(path, "\\editor"); + res = lookup_record(path); + if (!res) { + res = create_record(path); + if (!res) { + Error("Failed to create registry key for %s.", PRODUCT_NAME); + } + } +#endif + + res = lookup_record(m_Basepath); + + //Get net directory for manage system + char netpath[_MAX_PATH]; + lstrcpy(netpath, ""); + #ifndef EDITOR + if (FindArg("-update")) //For the game-only build, require -update to update data + #endif + //NOTE LINK TO ABOVE IF + { + char *netdir=getenv("D3_DIR"); + if (netdir) + lstrcpy(netpath, netdir); + } + write("net directory",netpath,lstrlen(netpath)+1); + + Database = this; +} + + +bool Win32JoystickCalibrate() +{ +// sorry. + PROCESS_INFORMATION pi; + STARTUPINFO si; + BOOL flag; + //DWORD dwval; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + + flag = CreateProcess( + NULL, + "rundll32.exe shell32.dll,Control_RunDLL joy.cpl", + NULL, NULL, + FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, + &si, &pi); + if (!flag) { + return false; + } + else { + tWin32AppInfo appinfo; + HWND hWnd; + + Descent->get_info(&appinfo); + hWnd = (HWND)appinfo.hwnd; + + WaitForInputIdle(pi.hProcess, INFINITE); + ShowWindow(hWnd, SW_MINIMIZE); + Descent->delay(0.5f); + + while (WaitForSingleObject(pi.hProcess, 0) != WAIT_OBJECT_0) + { + extern void D3DeferHandler(bool is_active); + D3DeferHandler(false); + } + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + ShowWindow(hWnd, SW_MAXIMIZE); + Descent->delay(0.5f); + } + + return true; +} + + +#ifdef EDITOR +// --------------------------------------------------------------------------- +// WinMainInitEditor +// creates all the App objects for the editor. +// this is all this function should do. +// --------------------------------------------------------------------------- + +#pragma message("Compiling editor WinMain substitute function.") + +void WinMainInitEditor(unsigned hwnd, unsigned hinst) +{ + tWin32AppInfo appinfo; + + appinfo.hwnd = (HWnd)hwnd; + appinfo.hinst = (HInstance)hinst; + appinfo.flags = OEAPP_WINDOWED; + + Descent = new oeWin32Application(&appinfo); + Database = new oeD3Win32Database; +} + +#else + +void D3End() +{ + if (Descent) { + delete Descent; + } +} + +// Localization defines +#define LANGUAGE_ENGLISH 0 +#define LANGUAGE_GERMAN 1 +#define LANGUAGE_SPANISH 2 +#define LANGUAGE_ITALIAN 3 +#define LANGUAGE_FRENCH 4 + + +inline void MessageBoxStr(int id) +{ + const char *txt = "Unknown string"; + + id--; + if (id >= 0 && id <= 8) { + switch (m_resource_language) + { + case LANGUAGE_FRENCH: + txt = French_strings[id]; + break; + case LANGUAGE_GERMAN: + txt = German_strings[id]; + break; + case LANGUAGE_ITALIAN: + txt = Italian_strings[id]; + break; + case LANGUAGE_SPANISH: + txt = Spanish_strings[id]; + break; + default: + txt = English_strings[id]; + } + } + +// win32_GetStringResource(id, str, sizeof(str)); + + MessageBox(NULL, txt, "Outrage Error", MB_OK); +} + + +// Returns true if this machine can support the CPUID instruction +bool SupportsCPUID () +{ + bool enabled=true; + + __try{ + + _asm{ + pushad + __emit 0x0f //CPUID + __emit 0xa2 //CPUID + popad + } + } + __except(1) + { + enabled=false; + } + + return enabled; +} + +// Returns true if this machine can support katmai instructions +bool SupportsKatmai () +{ + // Note: we don't do anything Katmai specific -- just always enable it. + return true; + /* + cpuinfo info; + getcpudata(&info); + + char buffer[584]; + sprintf(buffer, "Vendor ID\t: %s\n" + "CPU Family\t: %c\n" + "Model\t\t: %d\n" + "Is Katmai\t: %s (Really: %s)\n", + info.vendor_id[0] ? info.vendor_id : "Unknown", + info.family + '0', + info.model, + (info.capability&0x02000000)?"Yes":"No", + ((info.capability&0x02000000)&&(info.vendor==VENDOR_INTEL) )?"Yes":"No"); + mprintf((0,buffer)); + return((info.capability&0x02000000)&&(info.vendor==VENDOR_INTEL) )?true:false; + */ +} + +// DirectX check, NT 4.0, 5.0, Win95 check. +bool Win32SystemCheck(HINSTANCE hInst) +{ + tWin32OS os; + int major, minor, build; + bool retval; + + os = oeWin32Application::version(&major, &minor, &build); + + if (os == WinNT) { + if (major < 4) { + MessageBoxStr(ID_TXT_WINNT); + return false; + } + } + else if (os != Win9x) { + MessageBoxStr(ID_TXT_WIN32); + return false; + } + +// perform DirectX system check. + retval = false; + +// This function simply returns the interger version value of DirectX +// installed on the user's system + LONG lResult; + HKEY hKey = NULL; + int version_num=0; + + lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectX", NULL, KEY_QUERY_VALUE, &hKey); + if (lResult == ERROR_SUCCESS) { + char version[32]; + DWORD dwType, dwLen=32; + lResult = RegQueryValueEx(hKey, "Version", NULL, &dwType, (ubyte *) version, &dwLen ); + if (lResult == ERROR_SUCCESS) { + version_num = atoi(strstr(version, ".") + 1); + } else { + int val; + DWORD dwType, dwLen = 4; + + lResult = RegQueryValueEx( hKey, "InstalledVersion", NULL, &dwType, (ubyte *) &val, &dwLen); + if (lResult == ERROR_SUCCESS) { + version_num = val; + } + } + RegCloseKey(hKey); + } + else { + // if we don't have DSETUP.DLL and we are under Win95, then we're in trouble. + if (os == Win9x) { + MessageBoxStr(ID_TXT_DXVER); + MessageBoxStr(ID_TXT_DX95); + goto end_win32_check; + } + } + +// we should be either under NT 4 or greater, or 95 + if (version_num < 3) { + if (os == WinNT) { + if (major==4) { + MessageBoxStr(ID_TXT_DXNT4); + } + else { + MessageBoxStr(ID_TXT_DXNTERR); + } + goto end_win32_check; + } + else { + // Win95 + MessageBoxStr(ID_TXT_DX95); + goto end_win32_check; + } + } + + if( (!SupportsKatmai()) || FindArg("-nopentium3") ) + { + mprintf ((0,"No Katmai detected.\n")); + Katmai=false; + } + else + { + mprintf ((0,"Katmai detected!\n")); + Katmai=true; + } + + retval = true; + +end_win32_check: + return retval; +} + +void getcpudata(cpuinfo *info) +{ + unsigned char family,model,mask; + int level; + unsigned int capability; + unsigned int a,b,c; + + if(SupportsCPUID()) + { + _asm{ + pushad; + + xor eax,eax; // call CPUID with 0 -> return vendor ID + + __emit 0x0f; //CPUID + __emit 0xa2; //CPUID + + mov level,eax; // save CPUID level + mov a,ebx; // lo 4 chars + mov b,edx; // next 4 chars + mov c,ecx; // last 4 chars + + or eax,eax; // do we have processor info as well? + je noinfo; + + mov eax,1; // Use the CPUID instruction to get CPU type + __emit 0x0f; //CPUID + __emit 0xa2; //CPUID + + mov cl,al; // save reg for future use + and ah,15; // mask processor family + mov family,ah; + and al,240; // mask model + shr al,4; + mov model,al; + and cl,15; // mask mask revision + mov mask,cl; + mov capability,edx; + + noinfo: + popad; + } + + // fill in the struct + memset(info->vendor_id,0,16); + memcpy(&info->vendor_id[0],&a,4); + memcpy(&info->vendor_id[4],&b,4); + memcpy(&info->vendor_id[8],&c,4); + info->vendor_id[15] = '\0'; + + info->capability = capability; + info->CPUid_level = level; + info->family = family; + info->mask = mask; + info->model = model; + + if (!strcmp(info->vendor_id, "GenuineIntel")) + info->vendor = VENDOR_INTEL; + else if (!strcmp(info->vendor_id, "AuthenticAMD")) + info->vendor = VENDOR_AMD; + else if (!strcmp(info->vendor_id, "CyrixInstead")) + info->vendor = VENDOR_CYRIX; + else if (!strcmp(info->vendor_id, "UMC UMC UMC ")) + info->vendor = VENDOR_UMC; + else if (!strcmp(info->vendor_id, "CentaurHauls")) + info->vendor = VENDOR_CENTAUR; + else if (!strcmp(info->vendor_id, "NexGenDriven")) + info->vendor = VENDOR_NEXGEN; + else + { + info->vendor = VENDOR_UNKNOWN; + info->vendor_id[0] = '\0'; + } + + }else + { + info->capability = 0; + info->CPUid_level = 0; + info->family = 0; + info->mask = 0; + info->model = 0; + info->vendor_id[0] = '\0'; + info->vendor = VENDOR_UNKNOWN; + } +} + +// --------------------------------------------------------------------------- +// WinMain +// creates all the OS objects and then runs Descent 3. +// this is all this function should do. +// --------------------------------------------------------------------------- + + + +// See below for real WinMain + +int PASCAL HandledWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nCmdShow) +{ +/* initialize our OS Object. This could be a game dependant OS object, or a default OS object. + once we create it, if successful, we can start the game. +*/ + oeD3Win32App *d3; + extern bool w32_mouseman_hack; // from winapp.cpp + extern bool joy_chpro_hack; // located in winjoy.cpp + + strupr(szCmdLine); + GatherArgs (szCmdLine); + + //This must come AFTER the GatherArgs() call, because its constructer used FindArg() + oeD3Win32Database dbase; + + no_debug_dialog = FindArg("-nocrashbox"); + + // If this is a dedicated server, then start one! + if (FindArg("-dedicated")) + StartDedicatedServer (); +#ifdef DEDICATED_ONLY + else + { + MessageBox(NULL,"Error: -dedicated command line required",PRODUCT_NAME " Error",MB_OK); + return 0; + } + +#endif + +#ifndef GAMEGAUGE + if (! FindArg("-launched") && !FindArg("-dedicated") && !FindArg("-timetest") ) { + MessageBox(NULL,"You cannot run this program directly. Please run \"" PRODUCT_NAME ".exe\".",PRODUCT_NAME " Error",MB_OK); + return 0; // pre init return + } +#endif + + if (Dedicated_server) + { + d3 = new oeD3Win32App(OEAPP_CONSOLE, (HInstance)hInst); + } + else + { + unsigned int flags = OEAPP_FULLSCREEN; + if( FindArg("-windowed") ) + { + // switch to windowed mode instead + flags = OEAPP_WINDOWED; + } + + d3 = new oeD3Win32App(flags, (HInstance)hInst); + } + atexit(D3End); + + w32_mouseman_hack = false; + joy_chpro_hack = false; + + if (FindArg("-mouseman")) { + w32_mouseman_hack = true; + } + if (FindArg("-chpro")) { + joy_chpro_hack = true; + } + +// determine preinit language for resource strings + int language = 0; + dbase.read_int("LanguageType", &language); + m_resource_language = language; + + if (!Win32SystemCheck(hInst)) + return 0; + + PreInitD3Systems(); + + d3->init(); + d3->run(); + + return 1; +} + + + +//This is the winmain that tests for exceptions.. +int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nCmdShow) +{ + + int result =-1; + + __try + { + result = HandledWinMain(hInst,hPrevInst,szCmdLine,nCmdShow); + } + __except(RecordExceptionInfo(GetExceptionInformation(), "WinMain()")) + { + + } + return result; +} + + + +#endif + + + + diff --git a/README.md b/README.md index 74775cdea..0ce9e730d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ -# Descent3 -Descent 3 by Outrage Entertainment +# Descent 3 + +This is the latest version of the Descent 3 source code. This includes the '1.5' patch that Jeff Slutter and Kevin Bentley several years ago. At the time, it worked for Windows, Linux, and Mac. + +The first thing I want to do is get everything compiling again, and ideally some CI/CD actions. After that, the code needs to be cleaned up some, to remove old version control comments, etc. A lot of this code was written by a really great team, but keep in mind we were much younger and less experienced back then. + +If you're interested in helping maintain it, please send me a message. Otherwise, I'm happy to take pull requests. + +This is the last update I put out there showing different architectures playing along. Yikes, that was a long time ago, sorry we never released a 1.5 patch. Some logistics got in the way! + +[![Descent3 1.5 Patch Development update](https://img.youtube.com/vi/oasEAoPHk7I/0.jpg)](https://www.youtube.com/watch?v=oasEAoPHk7I) + +Thanks to Jeff Slutter, who did most of the work modernizing the code from the 90's. I'm looking forward to seeing what the community does with it! diff --git a/acmlib/CMakeLists.txt b/acmlib/CMakeLists.txt new file mode 100644 index 000000000..0ddd2bb14 --- /dev/null +++ b/acmlib/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (HEADERS ) +SET (CPPS + acmlib.cpp) + +ADD_LIBRARY(acmlib STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/acmlib/acmlib.cpp b/acmlib/acmlib.cpp new file mode 100644 index 000000000..f65810d86 --- /dev/null +++ b/acmlib/acmlib.cpp @@ -0,0 +1,45 @@ + + +#include "Adecode.h" +#include "mem.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +AudioDecoder * Create_AudioDecoder( + ReadFunction *reader, void *data, + unsigned *pChannels, unsigned *pSampleRate, + long *pSampleCount) +{ + return (AudioDecoder*)mem_malloc(sizeof(AudioDecoder)); +} + +// Read from audio decoder at most the specified qty of bytes +// (each sample takes two bytes). +// Returns zero when the end of file is reached. +unsigned __cdecl AudioDecoder_Read(AudioDecoder *ad, void *buf, unsigned qty) +{ + return 0; +} + +// Close audio decoder +void __cdecl AudioDecoder_Close(AudioDecoder *ad) +{ + if(ad) mem_free(ad); +} + +// Optional interface for supplying your own malloc and free functions +// Default is to use standard malloc and free. + +void __cdecl AudioDecoder_MallocFree(ad_malloc *fn_malloc, ad_free *fn_free) +{ + + +} + + +#ifdef __cplusplus +}; +#endif diff --git a/bitmap/CMakeLists.txt b/bitmap/CMakeLists.txt new file mode 100644 index 000000000..e2d9415ff --- /dev/null +++ b/bitmap/CMakeLists.txt @@ -0,0 +1,10 @@ +SET (HEADERS iff.h) +SET (CPPS + bitmain.cpp + bumpmap.cpp + iff.cpp + lightmap.cpp + pcx.cpp + tga.cpp) + +ADD_LIBRARY(bitmap STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/bitmap/bitmain.cpp b/bitmap/bitmain.cpp new file mode 100644 index 000000000..63dda56eb --- /dev/null +++ b/bitmap/bitmain.cpp @@ -0,0 +1,1924 @@ +/* + * $Logfile: /DescentIII/Main/bitmap/bitmain.cpp $ + * $Revision: 70 $ + * $Date: 5/10/00 5:09p $ + * $Author: Matt $ + * + * Bitmap handling functions + * + * $Log: /DescentIII/Main/bitmap/bitmain.cpp $ + * + * 70 5/10/00 5:09p Matt + * Added casts so code would compile under Visual C++. + * + * 69 5/10/00 4:34p Jeff + * handle buggy tablefile editors that put the full path name to a + * primative instead of just the filename + * + * 68 4/19/00 5:29p Matt + * From Duane for 1.4 + * Added Mac-only error handling + * Added checks & asserts for error return values + * + * 67 4/04/00 6:09p Matt + * Fixed two problems in the hash code: the hash values were + * case-sensitive, which caused misses with mixed-case filenames, and hash + * table nodes were not freed when a bitmap was freed. + * + * 66 3/20/00 12:23p Matt + * Merge of Duane's post-1.3 changes. + * Small optimization + * Error check + * + * 65 2/06/00 3:41a Jason + * fixed memory overrun error in bm_ChangeEndName function + * + * 64 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 63 8/10/99 5:10p Jeff + * delete the hash table on bitmap lib shutdown...just to be clean + * + * 62 7/28/99 5:18p Kevin + * Mac merge fixes + * + * 61 7/28/99 3:37p Kevin + * Mac! + * + * 60 4/29/99 4:59p Jason + * fixed some preuploaded texture problems + * + * 59 4/23/99 10:45p Jeff + * fixed crash with freeing hash table, set it to NULL to prevent double + * free + * + * 58 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 57 4/16/99 11:51p Jeff + * renamed strcmpi to stricmp to be linux nice + * + * 56 4/15/99 2:56p Kevin + * Removed unneeded mprintf and unused variable + * + * 55 4/15/99 12:36p Kevin + * Changed bm_FindBitmapName to use a hash table. + * + * 54 4/14/99 1:07a Jeff + * fixed case mismatched #includes + * + * 53 3/23/99 10:24a Samir + * NULL pointer in bm_DestroyChunkedBitmap. + * + * 52 2/12/99 12:14p Jason + * fixed memory scaling problem + * + * 51 1/19/99 11:17a Jason + * fixed another scaling problem + * + * 50 1/14/99 2:26p Jason + * made data tracking more reliable + * + * 49 1/04/99 6:19p Jason + * fixed bitmap format problem + * + * 48 12/21/98 11:47a Jason + * fixed bitmap problem with 4444 mipping + * + * 47 12/21/98 11:21a Josh + * FROM JASON: Fixed format/mip problem + * + * 46 11/30/98 5:50p Jason + * added 4444 bitmap support + * + * 45 10/21/98 4:36p Jason + * more changes for renderering speedups + * + * 44 10/19/98 4:22p Jason + * more fixes for Beta5 + * + * 43 10/08/98 2:27p Jason + * sped up table file loading + * + * 42 9/25/98 12:01p Samir + * for ndebug builds, removed the return immediately case in + * bm_AllocLoadFileBitmap. + * + * 41 8/21/98 5:14p Jason + * made better memory use of primitives + * + * 40 7/14/98 10:58a Jason + * fixed transparency bug in error texture + * + * 39 5/27/98 5:17p Jason + * fixed some bugs for the E3 Demo + * + * 38 5/20/98 5:43p Jason + * incremental checkin for bumpmapping + * + * 37 5/07/98 3:30p Jeff + * optimized CreateChunkBitmap a bit + * + * 36 5/05/98 1:01p Jeff + * improved on Chunk bitmaps + * + * 35 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 34 4/22/98 12:10p Chris + * Fixed path length problems + * + * 33 4/03/98 12:23p Jason + * dealt with overlay types being loaded from disk more than once + * + * 32 3/31/98 3:48p Jason + * added memory lib + * + * 31 3/23/98 4:42p Jason + * took out dumb ifdef + * + * 30 3/19/98 3:18p Samir + * enforce constant char* arguments when needed. done in CFILE and bitmap + * libraries as well as ddio. + * + * 29 3/17/98 4:33p Jason + * Added size changing to bitmaps/textures + * + * 28 2/17/98 4:57p Jason + * upped bitmap counts + * + * 27 2/13/98 7:23p Brent + * fixed mipping bug + * + * 26 2/12/98 1:32p Jason + * got mipmapping working + * + * 25 2/11/98 7:08p Jason + * took out dumb mprintf + * + * 24 2/09/98 3:38p Jason + * fixed potential problem with overlay bitmaps + * + * 23 2/06/98 7:20p Jason + * made duplicate bitmaps replace themselves in memory + * + * 22 1/26/98 4:32p Jason + * took out some goofy mprintfs + * + * 21 1/16/98 3:14p Samir + * Now store pixel width and height of chunked bitmap. + * + * 20 1/16/98 11:46a Samir + * Added functions to allocate and destroy chunked bitmaps. + * + * 19 1/14/98 12:45p Jeff + * Added a 'clear bitmap' function. Clears to transparent color. + * + * 18 12/23/97 6:32p Mark + * fixed problem with trying to save a bitmap that hasn't been paged in. + * + * 17 12/19/97 5:59p Jason + * sped up bitmap loading + * + * 16 12/19/97 3:36p Jason + * bug fixes for bitmap paging stuff + * + * 15 12/19/97 2:46p Jason + * implemented bitmap paging routines + * + * 14 12/18/97 4:03p Jason + * added error checking for bitmaps initting + * + * 13 11/11/97 1:07p Jason + * fixed multiple names problem in bitmap naming + * + * 12 11/10/97 4:53p Jason + * added warning for names that are too long + * + * 11 10/16/97 4:55p Jason + * fixed stupid off-by-one bug + * + * 10 10/16/97 10:48a Jason + * added bm_set_priority + * + * 9 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 8 9/17/97 10:48a Jason + * added cache_slot variable + * + * 7 9/16/97 5:04p Matt + * Changed conditional for debug code + * + * 6 9/12/97 6:06p Samir + * Added function to get bitmap pixel. + * + * 5 9/10/97 12:13p Samir + * Added function to check if a pixel is transparent. + * + * 4 9/02/97 11:18a Jason + * got rid of compiler warnings + * + * 3 8/29/97 1:22p Jason + * added tga screenshots + * + * 2 8/05/97 10:18a Jason + * added lightmap system + * + * 46 6/24/97 12:41p Jason + * checked in for safety + * + * 45 6/06/97 3:57p Jason + * implemented changes for small textures and automatic tmap2 recognition + * + * 44 6/03/97 12:19p Jason + * cleaned up the bitmap library a bit + * + * 43 5/19/97 5:10p Jason + * changes for our new abstracted renderer code + * + * 42 5/12/97 11:41a Jason + * made game work (default) to 16bit no mip maps mode + * Saves us alot of memory + * + * 41 5/08/97 1:16p Jason + * made ChangeEndName work with device independant calls + * + * 40 5/02/97 5:22p Jason + * added bm_rowsize to return bytes per row + * + * 39 5/01/97 4:37p Jason + * made bitmaps clear their settings when alloced (bug fixed) + * + * 38 4/30/97 3:15p Jason + * changes to support both 8bit and 16bit rendering in software + * + * 37 4/25/97 3:31p Jason + * implemented better memory management for vclips and bitmaps + * + * 36 4/8/97 5:23 PM Jeremy + * #ifdef editor around calls to decrement bitmap_memory_used by checking + * _msize of an array location. _msize is microsoft specific, not cross + * platform, but this is only used by the editor anyway, thus the ifdef + * editor. + * + * 35 3/31/97 7:18p Jason + * made bitmaps easier on memory if not in editor mode + * + * 34 3/28/97 12:22p Jason + * implemented memory sharing scheme between 8 bit bitmaps + * + * 33 3/13/97 1:05p Matt + * Fixed extra-stupid bug + * + * 32 3/13/97 12:35p Matt + * Look for error1.ogf in current directory (explicitely) + * + * 31 3/03/97 6:20p Matt + * Changed cfile functions to use D3 naming convention + * + * $NoKeywords: $ + */ +#include +#include +#include +#include "CFILE.H" +#include "texture.h" +#include "bitmap.h" +#include "pstypes.h" +#include "pserror.h" +#include "mono.h" +#include "iff.h" +#include "ddio.h" +#include "lightmap.h" +#include "bumpmap.h" +#include "mem.h" +#include "psrand.h" + +#include "Macros.h" + +#define BM_FILETYPE_TGA 1 +#define BM_FILETYPE_PCX 2 +#define BM_FILETYPE_IFF 3 +int Num_of_bitmaps=0; +bms_bitmap GameBitmaps[MAX_BITMAPS]; +ulong Bitmap_memory_used=0; +ubyte Bitmaps_initted=0; +/* modify these lines to establish data type */ +typedef bms_bitmap * bm_T; /* type of item to be stored */ +typedef int bm_hashTableIndex; /* index into hash table */ +#define compEQ(a,b) (stricmp((a)->name,(b)->name)==0) +typedef struct bm_Node_ { + struct bm_Node_ *next; /* next bm_Node */ + bm_T data; /* data stored in bm_Node */ +} bm_Node; +bm_Node *bm_findNode (bm_T data); +void bm_deleteNode(bm_T data); +bm_Node *bm_insertNode(bm_T data); +bm_hashTableIndex bm_hash(bm_T data); +bm_Node **bm_hashTable = NULL; +int bm_hashTableSize = (MAX_BITMAPS/2); +void bm_InitHashTable() +{ + bm_hashTable = (bm_Node **)mem_malloc(bm_hashTableSize * sizeof(bm_Node *)); + for(int a=0;anext; + mem_free(curr); + curr = next; + } + bm_hashTable[idx] = NULL; + } + } + + mem_free(bm_hashTable); + bm_hashTable = NULL; + } +} + +bm_hashTableIndex bm_hash(bm_T data) { + /*********************************** + * hash function applied to data * + ***********************************/ + char *p = data->name; + unsigned int hval = strlen(p); + + while(*p) + { + hval += tolower(*p); + p++; + } + + return (hval % (MAX_BITMAPS/2)); +} +bm_Node *bm_insertNode(bm_T data) +{ + bm_Node *p, *p0; + bm_hashTableIndex bucket; + /************************************************ + * allocate bm_Node for data and insert in table * + ************************************************/ + /* insert bm_Node at beginning of list */ + bucket = bm_hash(data); + if ((p = (bm_Node *)mem_malloc(sizeof(bm_Node))) == 0) { + exit(1); + } + p0 = bm_hashTable[bucket]; + bm_hashTable[bucket] = p; + p->next = p0; + p->data = data; + return p; +} +void bm_deleteNode(bm_T data) +{ + bm_Node *p0, *p; + bm_hashTableIndex bucket; + /******************************************** + * delete bm_Node containing data from table * + ********************************************/ + /* find bm_Node */ + p0 = 0; + bucket = bm_hash(data); + p = bm_hashTable[bucket]; + while (p && !compEQ(p->data, data)) { + p0 = p; + p = p->next; + } + if (!p) return; + /* p designates bm_Node to delete, remove it from list */ + if (p0) + /* not first bm_Node, p0 points to previous bm_Node */ + p0->next = p->next; + else + /* first bm_Node on chain */ + bm_hashTable[bucket] = p->next; + mem_free (p); +} +bm_Node *bm_findNode (bm_T data) +{ + bm_Node *p; + if(!bm_hashTable) + return NULL; + /******************************* + * find bm_Node containing data * + *******************************/ + p = bm_hashTable[bm_hash(data)]; + while (p && !compEQ(p->data, data)) + p = p->next; + return p; +} +// simply frees up a bitmap +void bm_FreeBitmapMain(int handle); +// Sets all the bitmaps to unused +void bm_InitBitmaps() +{ + int i,ret; + bm_InitHashTable(); + Bitmaps_initted=1; + for (i=0;i0) + bm_FreeBitmap (i); + } + bm_DeleteHashTable(); + +} +int bm_AllocateMemoryForIndex(int n,int w,int h,int add_mem) +{ + // If no go on the malloc, bail out with -1 + + int size = (w*h*2)+(add_mem)+2; + GameBitmaps[n].data16=(ushort *)mem_malloc (size); + if (!GameBitmaps[n].data16) + { + Int3(); // Ran out of memory! + return -1; + } + Bitmap_memory_used+=(size); + memset(GameBitmaps[n].data16,0xAA,size); + + GameBitmaps[n].format=BITMAP_FORMAT_STANDARD; + GameBitmaps[n].width=w; + GameBitmaps[n].height=h; + GameBitmaps[n].flags=BF_CHANGED|BF_BRAND_NEW; + +#ifdef USE_OPENGL + for(int tmp=w;tmp>0;tmp=tmp>>1) + GameBitmaps[n].mip_levels++; +#else + GameBitmaps[n].mip_levels = NUM_MIP_LEVELS; +#endif + + Num_of_bitmaps++; + return n; +} +// Allocs a bitmap of w x h size +// If add_mem is nonzero, adds that to the amount alloced +// Returns bitmap handle if successful, -1 if otherwise +int bm_AllocBitmap (int w,int h,int add_mem) +{ + int n,i; + if (!Bitmaps_initted) + { + Int3(); + mprintf ((0,"Bitmaps not initted!!!\n")); + return -1; + } + + for (i=0;i=0) + { + GameBitmaps[n].used=1; + GameBitmaps[n].cache_slot=-1; + } + return ret; +} +// Just like bm_AllocBitmap but doesn't actually allocate memory. Useful for paging! +int bm_AllocNoMemBitmap (int w,int h) +{ + int n,i; + if (!Bitmaps_initted) + { + Int3(); + mprintf ((0,"Bitmaps not initted!!!\n")); + return -1; + } + + for (i=0;idata - GameBitmaps)); + return fnode->data - GameBitmaps; + } + else + return -1; + /* + for (i=0;i= start_ptr) && (*end_ptr!='\\') ) end_ptr--; + if(end_ptr < start_ptr) + { + mprintf((0,"Unable to find bitmap %s\n",fname)); + return -1; + } + ASSERT(*end_ptr=='\\'); + end_ptr++; + filename = end_ptr; + mprintf((0,"Couldn't find %s, so gonna try %s\n",fname,filename)); + } + + // Check to see if this bitmap is already in memory, if so, just return that + // bitmaps handle + if ((n=bm_TestName(filename))!=-1) + { + mprintf ((1,"Found duplicate bitmap %s.\n",GameBitmaps[n].name)); + old_used=GameBitmaps[n].used; + GameBitmaps[n].used=1; + bm_FreeBitmap (n); + GameBitmaps[n].used=old_used+1; + + overlay=1; + } + if (!overlay) + bm_ChangeEndName (filename,name); + else + { + strcpy (name,GameBitmaps[n].name); + } + if (strlen(name)>33) + { + mprintf ((0,"ERROR!! This bitmaps name is too long, try shortening it and retry!\n")); + return -1; + } + // Try to open the file. If we can't load it from the network if possible + infile=(CFILE *)cfopen (filename,"rb"); + if( !infile) + { + mprintf ((0,"bm_AllocLoadFileBitmap: Can't open file named %s.\n",filename)); + + return BAD_BITMAP_HANDLE; // return the bad texture + + } + // Check to see if this is IFF. If so, call the IFF reader. If not, + // rewind the file and read as a TGA + + int filetype=bm_GetFileType (infile,filename); + + switch (filetype) + { + case BM_FILETYPE_IFF: + src_bm=bm_iff_alloc_file(infile); + break; + case BM_FILETYPE_TGA: + // reads a tga or an outrage graphics file (ogf) + src_bm=bm_tga_alloc_file(infile,name,format); + break; + case BM_FILETYPE_PCX: + src_bm=bm_pcx_alloc_file(infile); + break; + default: + Int3(); // Can't read this type + break; + } + cfclose (infile); + if (src_bm<0) + { + mprintf ((0,"Couldn't load %s.",filename)); + return -1; + } + + // Allocate space for our bitmap. + if (mipped) + { + if ((bm_mipped(src_bm))==0) // If we want a mipped but we don't have one + { + int w=bm_w(src_bm,0); + int h=bm_h(src_bm,0); + + if (overlay) + { + bm_AllocateMemoryForIndex (n,w,h,mipped*(((w*h*2)/3))); + GameBitmaps[n].format=GameBitmaps[src_bm].format; + } + else + { + n=bm_AllocBitmap (w,h,mipped*(((w*h*2)/3))); + GameBitmaps[n].format=GameBitmaps[src_bm].format; + } + + ASSERT (n>=0); + bm_ScaleBitmapToBitmap (n,src_bm); + bm_FreeBitmap (src_bm); + + bm_GenerateMipMaps (n); + } + else + { + if (!overlay) + n=src_bm; + else + { + int w=bm_w(src_bm,0); + int h=bm_h(src_bm,0); + bm_AllocateMemoryForIndex (n,w,h,mipped*(((w*h*2)/3))); + GameBitmaps[n].flags|=BF_MIPMAPPED; + GameBitmaps[n].format=GameBitmaps[src_bm].format; + bm_ScaleBitmapToBitmap (n,src_bm); + bm_FreeBitmap (src_bm); + } + } + } + else // If we don't want a mipped + { + if ((bm_mipped(src_bm))==0) + { + if (!overlay) + n=src_bm; + else + { + int w=bm_w(src_bm,0); + int h=bm_h(src_bm,0); + bm_AllocateMemoryForIndex (n,w,h,mipped*(((w*h*2)/3))); + GameBitmaps[n].format=GameBitmaps[src_bm].format; + bm_ScaleBitmapToBitmap (n,src_bm); + bm_FreeBitmap (src_bm); + } + } + else // And this is already mipped + { + int w=bm_w(src_bm,0); + int h=bm_h(src_bm,0); + ushort *src_data,*dest_data; + + if (overlay) + { + bm_AllocateMemoryForIndex (n,w,h,mipped*(((w*h*2)/3))); + GameBitmaps[n].format=GameBitmaps[src_bm].format; + } + else + { + n=bm_AllocBitmap (w,h,mipped*(((w*h*2)/3))); + GameBitmaps[n].format=GameBitmaps[src_bm].format; + } + ASSERT (n>=0); + src_data=(ushort *)bm_data(src_bm,0); + dest_data=(ushort *)bm_data(n,0); + memcpy (dest_data,src_data,w*h*2); + + bm_FreeBitmap (src_bm); + } + } + + strcpy (GameBitmaps[n].name,name); + + + //Insert into the hash table! + bm_insertNode(&GameBitmaps[n]); + return n; // We made it! +} +// Allocs and loads a bitmap but doesn't actually load texel data! +// Returns the handle of the loaded bitmap +// Returns -1 if something is wrong +/* +int bm_AllocLoadFileNoMemBitmap (char *fname,int mipped) +{ + CFILE *infile; + if (!Bitmaps_initted) + { + Int3(); + mprintf ((0,"Bitmaps not initted!!!\n")); + return -1; + } + + char *filename,name[BITMAP_NAME_LEN]; + int n,src_bm; + + filename = fname; + + // due to a bug in some people's tablefile editors, we got to make sure there is + // no path if there shouldn't be + if(!cfexist(filename)) + { + // generate a possible new filename + char *end_ptr,*start_ptr; + start_ptr = fname; + end_ptr = start_ptr + strlen(start_ptr) - 1; + while( (end_ptr >= start_ptr) && (*end_ptr!='\\') ) end_ptr--; + if(end_ptr < start_ptr) + { + mprintf((0,"Unable to find bitmap %s\n",fname)); + return -1; + } + ASSERT(*end_ptr=='\\'); + end_ptr++; + filename = end_ptr; + mprintf((0,"Couldn't find %s, so gonna try %s\n",fname,filename)); + } + + + // Check to see if this bitmap is already in memory, if so, just return that + // bitmaps handle + if ((n=bm_TestName(filename))!=-1) + { + GameBitmaps[n].used++; + mprintf ((0,"Found duplicate bitmap %s.\n",GameBitmaps[n].name)); + return n; + } + + bm_ChangeEndName (filename,name); + if (strlen(name)>33) + { + mprintf ((0,"ERROR!! This bitmaps name is too long, try shortening it and retry!\n")); + return -1; + } + // Try to open the file. If we can't load it from the network if possible + infile=(CFILE *)cfopen (filename,"rb"); + if( !infile) + { + mprintf ((0,"bm_AllocLoadFileBitmap: Can't open file named %s.\n",filename)); + + #ifdef _DEBUG + return BAD_BITMAP_HANDLE; // return the bad texture + #else + return -1; + #endif + } + // Check to see if this is IFF. If so, call the IFF reader. If not, + // rewind the file and read as a TGA + + int filetype=bm_GetFileType (infile,filename); + + switch (filetype) + { + case BM_FILETYPE_TGA: + // reads a tga or an outrage graphics file (ogf) + src_bm=bm_tga_load_short_file(infile,name); + break; + default: + Int3(); // Can't read this type + break; + } + cfclose (infile); + if (src_bm<0) + { + mprintf ((0,"Couldn't load %s.",filename)); + return -1; + } + + return src_bm; // We made it! +}*/ +// Allocs and loads a bitmap but doesn't actually load texel data! +// Returns the handle of the loaded bitmap +// Returns -1 if something is wrong +int bm_AllocLoadFileNoMemBitmap (const char *fname,int mipped,int format) +{ + if (!Bitmaps_initted) + { + Int3(); + mprintf ((0,"Bitmaps not initted!!!\n")); + return -1; + } + + char name[BITMAP_NAME_LEN]; + int n; + char *filename = (char *)fname; + + // due to a bug in some people's tablefile editors, we got to make sure there is + // no path if there shouldn't be + if(!cfexist(filename)) + { + // generate a possible new filename + char *end_ptr,*start_ptr; + start_ptr = (char *) fname; + end_ptr = start_ptr + strlen(start_ptr) - 1; + while( (end_ptr >= start_ptr) && (*end_ptr!='\\') ) end_ptr--; + if(end_ptr < start_ptr) + { + mprintf((0,"Unable to find bitmap %s\n",fname)); + return -1; + } + ASSERT(*end_ptr=='\\'); + end_ptr++; + filename = end_ptr; + mprintf((0,"Couldn't find %s, so gonna try %s\n",fname,filename)); + } + + // Check to see if this bitmap is already in memory, if so, just return that + // bitmaps handle + if ((n=bm_TestName(filename))!=-1) + { + GameBitmaps[n].used++; + mprintf ((1,"Found duplicate bitmap %s.\n",GameBitmaps[n].name)); + return n; + } + + bm_ChangeEndName (filename,name); + if (strlen(name)>33) + { + mprintf ((0,"ERROR!! This bitmaps name is too long, try shortening it and retry!\n")); + return -1; + } + n=bm_AllocNoMemBitmap (1,1); + strcpy (GameBitmaps[n].name,name); + if (mipped) + GameBitmaps[n].flags |=BF_WANTS_MIP; + if (format==BITMAP_FORMAT_4444) + GameBitmaps[n].flags |=BF_WANTS_4444; + + return n; // We made it! +} +// Allocs and loads a bitmap from an open file +// Returns the handle of the loaded bitmap +// Returns -1 if something is wrong +// If mipped is non-zero, allocs extra space for mips and computes them +int bm_AllocLoadBitmap (CFILE *infile,int mipped,int format) +{ + char name[BITMAP_NAME_LEN]; + int n,src_bm; + if (!Bitmaps_initted) + { + Int3(); + mprintf ((0,"Bitmaps not initted!!!\n")); + return -1; + } + + // Assume this is an ogf + + src_bm=bm_tga_alloc_file(infile,name,format); + if (src_bm<0) + { + mprintf ((0,"Couldn't load %s.",name)); + return -1; + } + + // Allocate space for our bitmap + if (mipped) + { + if ((bm_mipped(src_bm))==0) // If we want a mipped but we don't have one + { + int w=bm_w(src_bm,0); + int h=bm_h(src_bm,0); + + n=bm_AllocBitmap (w,h,mipped*(((w*h*2)/3))); + + ASSERT (n>=0); + GameBitmaps[n].format=GameBitmaps[src_bm].format; + bm_ScaleBitmapToBitmap (n,src_bm); + bm_FreeBitmap (src_bm); + + bm_GenerateMipMaps (n); + } + else + n=src_bm; + } + else // If we don't want a mipped + { + if ((bm_mipped(src_bm))==0) + n=src_bm; + else // And this is already mipped + { + int w=bm_w(src_bm,0); + int h=bm_h(src_bm,0); + ushort *src_data,*dest_data; + + n=bm_AllocBitmap (w,h,mipped*(((w*h*2)/3))); + ASSERT (n>=0); + src_data=(ushort *)bm_data(src_bm,0); + dest_data=(ushort *)bm_data(n,0); + memcpy (dest_data,src_data,w*h*2); + + bm_FreeBitmap (src_bm); + } + } + strcpy (GameBitmaps[n].name,name); + return n; // We made it! +} +// Given a handle, makes a big random shape to let you know you are screwed. +void bm_MakeBad (int handle) +{ + int i,t,limit; + ushort *dest; + ASSERT (GameBitmaps[handle].used); + if (handle!=BAD_BITMAP_HANDLE) + Int3(); // hmm, you're assigning a random bitmap to something other than + // the default. + if (bm_mipped(handle)) + limit=bm_miplevels(handle); + else + limit=1; + for (i=0;i 0) //DAJ -1FIX + { + GameBitmaps[handle].flags&=~BF_NOT_RESIDENT; + GameBitmaps[handle].flags|=BF_CHANGED|BF_BRAND_NEW; + } + else + { + mprintf ((0,"Error paging in bitmap %s!\n",GameBitmaps[handle].name)); + return 0; + } + } + return 1; +} +// Saves a bitmap to a file. Saves the bitmap as an OUTRAGE_COMPRESSED_OGF. +// Returns -1 if something is wrong. +int bm_SaveFileBitmap (const char *filename,int handle) +{ + int ret; + CFILE *fp; + if (!GameBitmaps[handle].used) + { + mprintf ((0,"bm_SaveBitmap: Trying to save a bitmap that isn't used!\n")); + Int3(); + return -1; + } + if (handle==BAD_BITMAP_HANDLE) + { + Int3(); // Hey, you shouldn't be saving this bitmap! + return -1; + } + if (!bm_MakeBitmapResident(handle)) + { + mprintf ((0,"There was a problem paging this bitmap in!\n")); + return -1; + } + fp=(CFILE *)cfopen (filename,"wb"); + + if (!fp) + { + mprintf ((0,"bm_SaveBitmap: Trying to save a bitmap to a closed file!\n")); + Int3(); + return -1; + } + ret=bm_SaveBitmap (fp,handle); + + cfclose (fp); + return ret; +} +// returns a bitmaps width (based on miplevel), else -1 if something is wrong +int bm_w (int handle,int miplevel) +{ + int w; + if (!GameBitmaps[handle].used) + { + Int3(); + return -1; + } + if (GameBitmaps[handle].flags & BF_NOT_RESIDENT) + if (!bm_MakeBitmapResident(handle)) + return NULL; + if (!(GameBitmaps[handle].flags & BF_MIPMAPPED)) + miplevel=0; + w=GameBitmaps[handle].width; + w>>=miplevel; + return (w); +} +// returns a bitmaps height (based on miplevel), else -1 if something is wrong +int bm_h (int handle,int miplevel) +{ + int h; + if (!GameBitmaps[handle].used) + { + Int3(); + return -1; + } + // If this bitmap is not page in, do so! + if (GameBitmaps[handle].flags & BF_NOT_RESIDENT) + if (!bm_MakeBitmapResident(handle)) + return NULL; + if (!(GameBitmaps[handle].flags & BF_MIPMAPPED)) + miplevel=0; + h=GameBitmaps[handle].height; + h>>=miplevel; + return (h); +} + +// returns the number of levels of mips a bitmap should have +int bm_miplevels (int handle) +{ + int levels = 0; + if (!GameBitmaps[handle].used) + { + Int3(); + return -1; + } + if(GameBitmaps[handle].mip_levels) + return GameBitmaps[handle].mip_levels; + + // If this bitmap is not page in, do so! + if (GameBitmaps[handle].flags & BF_NOT_RESIDENT) + if (!bm_MakeBitmapResident(handle)) + return NULL; + if (GameBitmaps[handle].flags & BF_MIPMAPPED) { + for(int tmp = GameBitmaps[handle].width; tmp > 0; tmp = tmp >> 1) { + levels++; + } + return levels; + } + return 0; +} + +// returns a bitmaps mipped status, else -1 if something is wrong +int bm_mipped (int handle) +{ + if (!GameBitmaps[handle].used) + { + Int3(); + return -1; + } + // If this bitmap is not page in, do so! + if (GameBitmaps[handle].flags & BF_NOT_RESIDENT) + if (!bm_MakeBitmapResident(handle)) + return NULL; + if (GameBitmaps[handle].flags & BF_MIPMAPPED) + return 1; + return 0; +} +// returns a bitmaps data (based on given miplevel), else NULL if something is wrong +ushort *bm_data (int handle,int miplevel) +{ + ushort *d; + int i; + if (!GameBitmaps[handle].used) + { + Int3(); + return NULL; + } + + // If this bitmap is not page in, do so! + if (GameBitmaps[handle].flags & BF_NOT_RESIDENT) + if (!bm_MakeBitmapResident(handle)) + return NULL; + + d=GameBitmaps[handle].data16; + for (i=0;i>i) * (GameBitmaps[handle].height>>i); + } + return (d); +} +// Given a source bitmap, generates mipmaps for it +void bm_GenerateMipMaps (int handle) +{ + int width=bm_w(handle,0); + int height=bm_h(handle,0); + int jump=2; // how many pixels to jump in x and y on source + + //mprintf ((0,"We got a mipper! %d \n",handle)); + + ASSERT (GameBitmaps[handle].used); + GameBitmaps[handle].flags|=BF_MIPMAPPED; + + int levels = bm_miplevels(handle); + + ushort *destdata; + for (int miplevel=1;miplevel>10) & 0x1f; + int g=(pix>>5) & 0x1f; + int b=(pix & 0x1f); + + if (!(pix & OPAQUE_FLAG)) + asum++; + else + { + rsum+=r; + gsum+=g; + bsum+=b; + } + } + // Average our source pixels + rsum/=4; + gsum/=4; + bsum/=4; + destpix=OPAQUE_FLAG | (rsum<<10) | (gsum<<5) | bsum; + + if (asum>2) + destpix=NEW_TRANSPARENT_COLOR; + } + else if (GameBitmaps[handle].format==BITMAP_FORMAT_4444) + { + for (int y=0;y<2;y++) + for (int x=0;x<2;x++) + { + ushort pix=srcptr[y*bm_w(handle,miplevel-1)+x]; + int a=(pix>>12) & 0x0f; + int r=(pix>>8) & 0x0f; + int g=(pix>>4) & 0x0f; + int b=(pix & 0x0f); + + asum+=a; + rsum+=r; + gsum+=g; + bsum+=b; + + } + // Average our source pixels + rsum/=4; + gsum/=4; + bsum/=4; + asum/=4; + destpix=(asum<<12) | (rsum<<8) | (gsum<<4) | bsum; + } + else + { + Int3(); + } + destdata=bm_data (handle,miplevel); + int sw=bm_w(handle,miplevel); + destdata[(i*sw)+t]=destpix; + } + } + + } +} +// Gets bits per pixel for a particular bitmap +// As of 12/30/96 always returns 16 +int bm_bpp (int handle) +{ + return BPP_16; +} +// Given two bitmaps, scales the data from src to the size of dest +// Not a particularly fast implementation +void bm_ScaleBitmapToBitmap (int dest,int src) +{ + ushort *dp=bm_data(dest,0); + ushort *sp=bm_data(src,0); + ASSERT (GameBitmaps[dest].used && dp); + ASSERT (GameBitmaps[src].used && sp); + int smipped=bm_mipped(src); + int dmipped=bm_mipped(dest); + int limit; + ASSERT (smipped==dmipped); + ASSERT (GameBitmaps[dest].format==GameBitmaps[src].format); + + int sw=bm_w(src,0); + int sh=bm_h(src,0); + int dw=bm_w(dest,0); + int dh=bm_h(dest,0); + int i,t; + ushort *sdata; + ushort *ddata; + if (sw==dw && sh==dh) + { + if (smipped) + limit=bm_miplevels(src); + else + limit=1; + for (i=0;i=0 && n=0); + bm_ScaleBitmapToBitmap (n,src_bm); + bm_FreeBitmap (src_bm); + + bm_GenerateMipMaps (n); + } + else + n=src_bm; + } + else // If we don't want a mipped + { + if ((bm_mipped(src_bm))==0) + n=src_bm; + else // And this is already mipped + { + int w=bm_w(src_bm,0); + int h=bm_h(src_bm,0); + ushort *src_data,*dest_data; + + n=bm_AllocBitmap (w,h,mipped*(((w*h*2)/3))); + ASSERT (n>=0); + src_data=(ushort *)bm_data(src_bm,0); + dest_data=(ushort *)bm_data(n,0); + memcpy (dest_data,src_data,w*h*2); + + bm_FreeBitmap (src_bm); + } + } + sprintf (str,"%s%d",name,i); + strcpy (GameBitmaps[n].name,str); + dest_index[i]=n; + bm_FreeBitmap (src_bm); + } + return num_bitmaps; // We made it! +} +// given a handle and a miplevel, returns the bytes per bitmap row +int bm_rowsize (int handle,int miplevel) +{ + int w; + ASSERT (GameBitmaps[handle].used>0); + w=bm_w(handle,miplevel); + w*=2; + return w; +} +// a function to determine if a pixel in a bitmap is transparent +bool bm_pixel_transparent(int bm_handle,int x,int y) +{ + if ((bm_data(bm_handle,0))==NULL) + return 0; // only check 16bit stuff + ushort *data=bm_data(bm_handle,0); + data = data + (bm_w(bm_handle,0)*y) + x; + if (GameBitmaps[bm_handle].format==BITMAP_FORMAT_4444) + { + int pix=*data; + if (!(pix>>12)) + return true; + } + else + { + if (! (*data & OPAQUE_FLAG)) + return true; + } + return false; +} +// a function to determine if a pixel in a bitmap is transparent +ushort bm_pixel(int bm_handle,int x,int y) +{ + if ((bm_data(bm_handle,0))==NULL) + return 0; // only check 16bit stuff + ushort *data=bm_data(bm_handle,0); + data = data + (bm_w(bm_handle,0)*y) + x; + return *data; +} +// Goes through the bitmap and sees if there is any transparency...if so, flag it! +int bm_SetBitmapIfTransparent (int handle) +{ + if ((bm_data(handle,0))==NULL) + return 0; // only check 16bit stuff + int w=bm_w(handle,0); + int h=bm_h(handle,0); + ushort *data=bm_data(handle,0); + if (GameBitmaps[handle].format==BITMAP_FORMAT_4444) + { + for (int i=0;i>12; + if (!pix) + { + GameBitmaps[handle].flags|=BF_TRANSPARENT; + return 1; + } + } + } + else + { + for (int i=0;i>5; + //upside_down=1-upside_down; + ushort *src_data=(ushort *)bm_data(handle,0); + for (i=0;i> 1; + ushort *bmpdata = bm_data(handle, 0); + int w = bm_w(handle, 0); + int h = bm_h(handle, 0); + for(dy = 0; dy < h; dy++) + { + for (dx = 0; dx < w; dx++) + bmpdata[dx] = NEW_TRANSPARENT_COLOR; + + bmpdata += w; + } +} +// Given a bitmap handle, breaks a bitmap into smaller pieces. +// Note, this routine isn't terrible fast or efficient, and you +// must free the bitmaps returned to you in the bm_array +bool bm_CreateChunkedBitmap(int bm_handle, chunked_bitmap *chunk) +{ + int i; + int *bm_array; + int bw=bm_w(bm_handle,0); + int bh=bm_h(bm_handle,0); + //determine optimal size of the square bitmaps + float fopt=128.0f; + int iopt; + //find the smallest dimension and base off that + int smallest = min(bw,bh); + if(smallest<=32) + fopt=32; + else + if(smallest<=64) + fopt=64; + else + fopt=128; + iopt=(int)fopt; + // Get how many pieces we need across and down + float temp=bw/fopt; + int how_many_across=temp; + if ((temp-how_many_across)>0) + how_many_across++; + temp=bh/fopt; + int how_many_down=temp; + if ((temp-how_many_down)>0) + how_many_down++; + ASSERT (how_many_across>0); + ASSERT (how_many_down>0); + // Allocate memory to hold our list of pieces + bm_array=(int *)mem_malloc (how_many_down*how_many_across*sizeof(int)); + ASSERT(bm_array); + for (i=0;i-1); + // Fill our new pieces with transparency + bm_ClearBitmap(bm_array[i]); + } + // Now go through our big bitmap and partition it into pieces + ushort *src_data=bm_data(bm_handle,0); + ushort *sdata; + ushort *ddata; + int shift; + switch(iopt) + { + case 32: + shift = 5; + break; + case 64: + shift = 6; + break; + case 128: + shift = 7; + break; + default: + Int3(); //Get Jeff + break; + } + int maxx,maxy; + int windex,hindex; + int s_y,s_x,d_y,d_x; + for(hindex=0;hindexpw = bw; + chunk->ph = bh; + chunk->w = how_many_across; + chunk->h = how_many_down; + chunk->bm_array = bm_array; + return true; +} +// destroys a chunked bitmap. +void bm_DestroyChunkedBitmap(chunked_bitmap *chunk) +{ + int i, num_bmps = chunk->w*chunk->h; + for (i = 0; i < num_bmps; i++) + bm_FreeBitmap(chunk->bm_array[i]); + mem_free(chunk->bm_array); + chunk->bm_array = NULL; +} +// Changes the size of a bitmap to a new size +void bm_ChangeSize(int handle,int new_w,int new_h) +{ + int mipped=bm_mipped(handle); + int n; + int mem_used=(GameBitmaps[handle].width*GameBitmaps[handle].height*2); + n=bm_AllocBitmap (new_w,new_h,mipped*((new_w*new_h*2)/3)); + ASSERT (n>=0); + if (mipped) + GameBitmaps[n].flags|=BF_MIPMAPPED; + if (GameBitmaps[handle].format==BITMAP_FORMAT_4444) + GameBitmaps[n].format=BITMAP_FORMAT_4444; + + bm_ScaleBitmapToBitmap (n,handle); + Bitmap_memory_used-=mem_used+(mipped*(mem_used/3)); + mem_free (GameBitmaps[handle].data16); + GameBitmaps[handle].data16=GameBitmaps[n].data16; + GameBitmaps[handle].width=new_w; + GameBitmaps[handle].height=new_h; + GameBitmaps[n].used=0; + Num_of_bitmaps--; +} +// Returns the format of this bitmap +int bm_format (int handle) +{ + if (!GameBitmaps[handle].used) + { + Int3(); + return -1; + } + if (GameBitmaps[handle].flags & BF_NOT_RESIDENT) + if (!bm_MakeBitmapResident(handle)) + return NULL; + return (GameBitmaps[handle].format); +} diff --git a/bitmap/bumpmap.cpp b/bitmap/bumpmap.cpp new file mode 100644 index 000000000..d568bd9c8 --- /dev/null +++ b/bitmap/bumpmap.cpp @@ -0,0 +1,124 @@ +#include +#include +#include "bumpmap.h" +#include "pstypes.h" +#include "pserror.h" +#include "mono.h" +#include "mem.h" + +int Num_of_bumpmaps=0; +static ushort Free_bumpmap_list[MAX_BUMPMAPS]; + +bms_bumpmap GameBumpmaps[MAX_BUMPMAPS]; +int Bumpmap_mem_used=0; + +// Sets all the bumpmaps to unused +void bump_InitBumpmaps() +{ + int i; + + for (i=0;i +#include +#include + +#include "mem.h" +#include "iff.h" +#include "BYTESWAP.H" +#include "CFILE.H" +#include "pserror.h" +#include "pstypes.h" +#include "bitmap.h" +#include "mono.h" +#include "grdefs.h" + +//Internal constants and structures for this library + +//Compression types +#define cmpNone 0 +#define cmpByteRun1 1 + +//Masking types +#define mskNone 0 +#define mskHasMask 1 +#define mskHasTransparentColor 2 + +//Palette entry structure +typedef struct +{ + ubyte r,g,b; +} pal_entry; + +//structure of the header in the file +typedef struct iff_bitmap_header { + short w,h; //width and height of this bitmap + short x,y; //generally unused + short type; //see types above + short transparentcolor; //which color is transparent (if any) + short pagewidth,pageheight; //width & height of source screen + ubyte nplanes; //number of planes (8 for 256 color image) + ubyte masking,compression; //see constants above + ubyte xaspect,yaspect; //aspect ratio (usually 5/6) + pal_entry palette[256]; //the palette for this bitmap + ubyte *raw_data; //ptr to array of data + short row_size; //offset to next row +} iff_bitmap_header; + +short iff_transparent_color; +short iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid + +#define MIN(a,b) ((aw = cf_ReadShort(ifile); bmheader->w=MOTOROLA_SHORT (bmheader->w); + bmheader->h = cf_ReadShort(ifile); bmheader->h=MOTOROLA_SHORT (bmheader->h); + bmheader->x = cf_ReadShort(ifile); bmheader->x=MOTOROLA_SHORT (bmheader->x); + bmheader->y = cf_ReadShort(ifile); bmheader->y=MOTOROLA_SHORT (bmheader->y); + + bmheader->nplanes = cf_ReadByte(ifile); + bmheader->masking = cf_ReadByte(ifile); + bmheader->compression = cf_ReadByte(ifile); + cf_ReadByte(ifile); /* skip pad */ + + bmheader->transparentcolor =cf_ReadShort(ifile); bmheader->transparentcolor=MOTOROLA_SHORT (bmheader->transparentcolor); + bmheader->xaspect = cf_ReadByte(ifile); + bmheader->yaspect = cf_ReadByte(ifile); + + bmheader->pagewidth = cf_ReadShort(ifile); + bmheader->pageheight = cf_ReadShort(ifile); + + iff_transparent_color = bmheader->transparentcolor; + + iff_has_transparency = 0; + + if (bmheader->masking == mskHasTransparentColor) + iff_has_transparency = 1; + + else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask) + return IFF_UNKNOWN_MASK; + + return IFF_NO_ERROR; +} + + +// the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data +int bm_iff_parse_body(CFILE *ifile,int len,iff_bitmap_header *bmheader) +{ + ubyte *p=bmheader->raw_data; + int width,depth,done=0; + + if (bmheader->type == TYPE_PBM) { + width=bmheader->w; + depth=1; + } else if (bmheader->type == TYPE_ILBM) { + width = (bmheader->w+7)/8; + depth=bmheader->nplanes; + } + + if (bmheader->compression == cmpNone) // no compression + { + for (int y=0;yh;y++) + { + int x; + + for (x=0;xmasking == mskHasMask) + { + for (int i=0;iw & 1) + cf_ReadByte (ifile); + + } + + } + else if (bmheader->compression == cmpByteRun1) // compression + { + ubyte *data_end=p+(bmheader->h*depth*width); + ubyte mask=(bmheader->masking == mskHasMask); + int cur_width=0,skip_mask=0; + int command; + int plane=0; + + while (!done) + { + if (p>=data_end) + { + done=1; + continue; + } + if (cur_width==width) + { + plane++; + if ((plane==depth && !mask) || (plane==depth+1 && mask)) + { + skip_mask=0; + plane=0; + } + + if (mask && plane==depth) + skip_mask=1; + + + cur_width=0; + } + + command=cf_ReadByte (ifile); + if (command>=0 && command <=127) + { + if (!skip_mask) + { + for (int i=0;i=-127 && command <0) + { + int run=(-command)+1; + int repeat_byte=cf_ReadByte (ifile); + + if (!skip_mask) + { + for (int i=0;iraw_data; + int y; + long chunk_end = cftell(ifile) + len; + + cf_ReadInt(ifile); //longword, seems to be equal to 4. Don't know what it is + + for (y=0;yh;y++) + { + ubyte n_items; + int cnt = bmheader->w; + ubyte code; + + n_items = cf_ReadByte(ifile); + + while (n_items--) + { + code = cf_ReadByte(ifile); + + if (code==0) + { + ubyte rep,val; + + rep = cf_ReadByte(ifile); + val = cf_ReadByte(ifile); + + cnt -= rep; + if (cnt==-1) + rep--; + while (rep--) + *p++ = val; + } + else if (code > 0x80) + { //skip + cnt -= (code-0x80); + p += (code-0x80); + if (cnt==-1) + p--; + } + else + { //literal + cnt -= code; + if (cnt==-1) + code--; + + while (code--) + *p++ = cf_ReadByte(ifile); + + if (cnt==-1) + cf_ReadByte(ifile); + } + + } + + if (cnt == -1) + { + if (!bmheader->w&1) + return IFF_CORRUPT; + } + else if (cnt) + return IFF_CORRUPT; + } + + + if (cftell (ifile) == chunk_end-1) //pad + cf_ReadByte(ifile); + + if (cftell(ifile)!= chunk_end) + { + Int3(); + return IFF_CORRUPT; + } + + else + return IFF_NO_ERROR; + +} + + + +// read an PBM +// Pass pointer to opened file, and to empty bitmap_header structure, and form length +int bm_iff_parse_file(CFILE *ifile,iff_bitmap_header *bmheader,iff_bitmap_header *prev_bm) +{ + uint sig,len; + int done=0; + + while (!done) + { + if (cfeof (ifile)) + { + done=1; + continue; + } + + sig=bm_iff_get_sig (ifile); + + len=cf_ReadInt(ifile); + len=MOTOROLA_INT (len); + + switch (sig) + { + case IFF_SIG_FORM: + { + int newsig=bm_iff_get_sig(ifile); + bmheader->type=TYPE_PBM; + break; + } + case IFF_SIG_BMHD: + { + int ret; + + ret = bm_iff_parse_bmhd(ifile,len,bmheader); + if (ret != IFF_NO_ERROR) + return ret; + else + { + + bmheader->raw_data=(ubyte *)mem_malloc(bmheader->w * bmheader->h); + if (!bmheader->raw_data) + return IFF_NO_MEM; + } + + } + break; + case IFF_SIG_ANHD: + { + + if (!prev_bm) + { + Int3(); + return IFF_CORRUPT; + } + + bmheader->w = prev_bm->w; + bmheader->h = prev_bm->h; + bmheader->type = prev_bm->type; + bmheader->raw_data=(ubyte *)mem_malloc(bmheader->w * bmheader->h); + + if (!bmheader->raw_data) + return IFF_NO_MEM; + + memcpy(bmheader->raw_data, prev_bm->raw_data, bmheader->w * bmheader->h ); + + if (len & 1) + len++; + bm_iff_skip_chunk(ifile,len); + + break; + } + + case IFF_SIG_CMAP: + { + int ncolors=(int) (len/3),cnum; + unsigned char r,g,b; + + for (cnum=0;cnum>= 2; bmheader->palette[cnum].r = r; + g >>= 2; bmheader->palette[cnum].g = g; + b >>= 2; bmheader->palette[cnum].b = b; + } + if (len & 1) + cf_ReadByte(ifile); + + } + break; + + case IFF_SIG_BODY: + { + int r; + if ((r=bm_iff_parse_body(ifile,len,bmheader))!=IFF_NO_ERROR) + return r; + done=1; + break; + } + case IFF_SIG_DELTA: + { + int r; + if ((r=bm_iff_parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR) + return r; + done=1; + break; + } + + + default: + // Don't know this chunk + if (len & 1) + len++; + bm_iff_skip_chunk(ifile,len); + break; + + } + } + + return IFF_NO_ERROR; /* ok! */ +} + +void bm_iff_convert_8_to_16 (int dest_bm,iff_bitmap_header *iffbm) +{ + ASSERT (bm_w(dest_bm,0) == iffbm->w); + ASSERT (bm_h(dest_bm,0) == iffbm->h); + + ushort *data; + + data=(ushort *)bm_data (dest_bm,0); + + for (int i=0;ih;i++) + for (int t=0;tw;t++) + { + ushort pixel; + ubyte c=iffbm->raw_data[i*iffbm->w+t]; + + int r=iffbm->palette[c].r>>1; + int g=iffbm->palette[c].g>>1; + int b=iffbm->palette[c].b>>1; + + pixel=OPAQUE_FLAG |(r<<10) | (g<<5) | b; + if (c==iffbm->transparentcolor) + pixel=NEW_TRANSPARENT_COLOR; + + data[i*bm_w(dest_bm,0)+t]=pixel; + } +} + +// Loads an iff into a structure, allocs bitmap memory and converts 8 bit iff file into +// 16bit bitmap +// Returns bitmap handle on success, or -1 if failed +int bm_iff_alloc_file(CFILE *ifile) +{ + int ret; //return code + iff_bitmap_header bmheader; + int src_bm; + char cur_sig[4]; + + // Ignore FORM and form length + cf_ReadInt(ifile); + cf_ReadInt(ifile); + + // check if this an ILBM + + for (int i=0;i<4;i++) + cur_sig[i]=cf_ReadByte(ifile); + + if (strncmp ("PBM ",cur_sig,4)) + { + mprintf ((0,"IFF file isn't a PBM...aborting.\n")); + return -1; + } + if (!strncmp ("PBM ",cur_sig,4)) + bmheader.type=TYPE_PBM; + else Int3(); // Huh? Get Jason! + + ret=bm_iff_parse_file(ifile,&bmheader,NULL); + + if (ret!=IFF_NO_ERROR) + { + mprintf ((0,"Couldn't load IFF file.\n")); + return -1; + } + + // Alloc our bitmap + src_bm=bm_AllocBitmap (bmheader.w,bmheader.h,0); + if (src_bm<0) + { + mem_free (bmheader.raw_data); + return -1; + } + + // Convert our 8 bit bitmap to 16bit + bm_iff_convert_8_to_16 (src_bm,&bmheader); + free (bmheader.raw_data); + + return src_bm; + +} + + +// returns number of bitmaps or -1 on error +int bm_iff_read_animbrush(const char *ifilename,int *bm_list) +{ + CFILE *ifile; + iff_bitmap_header bm_headers[40]; + iff_bitmap_header *temp_bm_head; + long sig,form_len; + long form_type; + int num_bitmaps=0; + int ret,i; + + for (int t=0;t<40;t++) + bm_headers[t].raw_data=0; + + ifile=cfopen (ifilename,"rb"); + if (!ifile) + return -1; + + sig=bm_iff_get_sig(ifile); + form_len = cf_ReadInt(ifile); + + if (sig != IFF_SIG_FORM) + { + mprintf ((0,"Not a valid IFF file.\n")); + cfclose (ifile); + return -1; + } + + form_type = bm_iff_get_sig(ifile); + + if (form_type == IFF_SIG_ANIM) + { + while (!cfeof (ifile) && num_bitmaps < 40) + { + ret = bm_iff_parse_file(ifile,&bm_headers[num_bitmaps],num_bitmaps>0?&bm_headers[num_bitmaps-1]:NULL); + + if (ret != IFF_NO_ERROR) + goto done; + + num_bitmaps++; + } + + } + else + { + cfclose (ifile); + return -1; + } + +done: + cfclose (ifile); + + + + for (i=0;ipalette,bm_headers[0].palette,sizeof(pal_entry)*256); + temp_bm_head->transparentcolor=bm_headers[0].transparentcolor; + + src_bm=bm_AllocBitmap (temp_bm_head->w,temp_bm_head->h,0); + ASSERT (src_bm>0); + + bm_iff_convert_8_to_16 (src_bm,temp_bm_head); + bm_list[i]=src_bm; + } + + + + for (i=0;i +#include +#include "lightmap.h" +#include "pstypes.h" +#include "pserror.h" +#include "bitmap.h" +#include "mono.h" +#include "mem.h" +#ifdef __LINUX__ +#define max(a,b) ((a>b)?a:b) +#else +#include "Macros.h" +#endif +int Num_of_lightmaps=0; +static ushort Free_lightmap_list[MAX_LIGHTMAPS]; +bms_lightmap GameLightmaps[MAX_LIGHTMAPS]; +int Lightmap_mem_used=0; +// Sets all the lightmaps to unused +void lm_InitLightmaps() +{ + int i; + for (i=0;i0) + lm_FreeLightmap (i); + } + +} +// Allocs a lightmap of w x h size +// Returns lightmap handle if successful, -1 if otherwise +int lm_AllocLightmap (int w,int h) +{ + int n; //,i; + if (Num_of_lightmaps==MAX_LIGHTMAPS) + Int3(); // Ran out of lightmaps! + + n=Free_lightmap_list[Num_of_lightmaps++]; + ASSERT (GameLightmaps[n].used==0); + // If no go on the malloc, bail out with -1 + + memset (&GameLightmaps[n],0,sizeof(bms_lightmap)); + GameLightmaps[n].data=(ushort *)mem_malloc ((w*h*2)); + if (!GameLightmaps[n].data) + { + mprintf ((0,"NOT ENOUGHT MEMORY FOR LIGHTMAP!\n")); + Int3(); + return BAD_LM_INDEX; + } + + GameLightmaps[n].width=w; + GameLightmaps[n].height=h; + GameLightmaps[n].used=1; + GameLightmaps[n].cache_slot=-1; + GameLightmaps[n].flags=LF_CHANGED; + // Figure out square size + // Find power of 2 number + int res=max(w,h); + int lightmap_res=2; + for (int i=0;i<=7;i++) + { + int low_num=1low_num) + { + lightmap_res=hi_num; + break; + } + } + ASSERT (lightmap_res>=2 && lightmap_res<=128); + GameLightmaps[n].square_res=lightmap_res; + Lightmap_mem_used+=(w*h*2); + + return n; +} +// Given a handle, frees the lightmap memory and flags this lightmap as unused +void lm_FreeLightmap (int handle) +{ + if (GameLightmaps[handle].used<1) + return; + GameLightmaps[handle].used--; + if (GameLightmaps[handle].used==0) + { + if (GameLightmaps[handle].data!=NULL) + mem_free (GameLightmaps[handle].data); + Lightmap_mem_used-=(GameLightmaps[handle].width*GameLightmaps[handle].height*2); + + GameLightmaps[handle].data=NULL; + GameLightmaps[handle].cache_slot=-1; + + Free_lightmap_list[--Num_of_lightmaps]=handle; + } +} +// returns a lightmaps width else -1 if something is wrong +int lm_w (int handle) +{ + int w; + if (!GameLightmaps[handle].used) + { + Int3(); + return -1; + } + w=GameLightmaps[handle].width; + + return (w); +} +// returns a lightmaps height , else -1 if something is wrong +int lm_h (int handle) +{ + int h; + if (!GameLightmaps[handle].used) + { + Int3(); + return -1; + } + h=GameLightmaps[handle].height; + + return (h); +} +// returns a lightmaps data else NULL if something is wrong +ushort *lm_data (int handle) +{ + ushort *d; + if (!GameLightmaps[handle].used) + { + Int3(); + return NULL; + } + + d=GameLightmaps[handle].data; + return (d); +} \ No newline at end of file diff --git a/bitmap/pcx.cpp b/bitmap/pcx.cpp new file mode 100644 index 000000000..dfccf676f --- /dev/null +++ b/bitmap/pcx.cpp @@ -0,0 +1,329 @@ +/* + * $Logfile: /DescentIII/Main/bitmap/pcx.cpp $ + * $Revision: 8 $ + * $Date: 3/30/99 12:36a $ + * $Author: Jeff $ + * + * Code to read PCX files + * + * $Log: /DescentIII/Main/bitmap/pcx.cpp $ + * + * 8 3/30/99 12:36a Jeff + * added support for 24bit pcx's + * + * 7 2/16/99 5:50p Jason + * made pcx stuff only work with 8 bit files + * + * 6 10/20/98 1:41a Jeff + * fixed a mem_malloc/free bug + * + * 5 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 4 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 3 12/22/97 7:34p Samir + * Removed instances of gr.h include. Replaced with grdefs.h + * + * 2 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 4 3/03/97 6:20p Matt + * Changed cfile functions to use D3 naming convention + * + * $NoKeywords: $ + */ + +#include "mem.h" +#include "bitmap.h" +#include "pserror.h" +#include "pstypes.h" +#include "grdefs.h" +#include + +// load an 8bit pcx image +int bm_pcx_8bit_alloc_file (CFILE *infile); +// load a 24bit pcx image +int bm_pcx_24bit_alloc_file (CFILE *infile); + + +// Loads a pcx file and converts it to 16 bit. Returns bitmap handle or -1 on error +int bm_pcx_alloc_file (CFILE *infile) +{ + ubyte temp[128]; + + cf_ReadBytes (temp,128,infile); + cfseek(infile,0,SEEK_SET); + + switch(temp[65]) + { + case 1: + // one plane, sounds like an 8bit pcx + return bm_pcx_8bit_alloc_file(infile); + break; + case 3: + // three planes, sounds like a 24bit pcx + return bm_pcx_24bit_alloc_file(infile); + break; + } + + return -1; // Must be 8 bit only +} + +// load an 8bit pcx image +int bm_pcx_8bit_alloc_file (CFILE *infile) +{ + int xmin,ymin,xmax,ymax; + int width,height; + int total,run=0; + int i,t; + int src_bm; + ubyte pred[256],pgreen[256],pblue[256]; + ubyte buf; + ubyte temp[128]; + + cf_ReadBytes (temp,4,infile); + + if (temp[3]!=8) + return -1; // nope...bail + + xmin=cf_ReadShort (infile); + ymin=cf_ReadShort (infile); + xmax=cf_ReadShort (infile); + ymax=cf_ReadShort (infile); + + cf_ReadBytes (temp,116,infile); + + if (temp[65-12]!=1) + return -1; // Must be 8 bit only + + width=1+xmax-xmin; + height=1+ymax-ymin; + total=width*height; + + ubyte *rawdata=(ubyte *)mem_malloc (width*height); + if (!rawdata) + return -1; // no memory! + + run=0; + while (run < total) + { + buf=cf_ReadByte(infile); + if (buf>=192) + { + ubyte tb; + tb=cf_ReadByte(infile); + for (i=0;i<(buf-192);i++,run++) + rawdata[run]=tb; + } + else + { + rawdata[run]=buf; + run++; + } + } + + cf_ReadByte (infile); // ignore pad + + // Read palette + + for (i=0;i<256;i++) + { + pred[i]=cf_ReadByte (infile); + pgreen[i]=cf_ReadByte (infile); + pblue[i]=cf_ReadByte (infile); + + pred[i]>>=3; pgreen[i]>>=3; pblue[i]>>=3; + } + + src_bm=bm_AllocBitmap (width,height,0); + if (src_bm<0) + return -1; // probably out of memory + + ushort *data=bm_data (src_bm,0); + + for (i=0;i=192) + { + ubyte tb; + tb=cf_ReadByte(infile); + for (i=0;i<(buf-192);i++,run++,data_index++) + rawdata[data_index]=tb; + } + else + { + rawdata[data_index]=buf; + run++; + data_index++; + } + } + + //Read green scanline + run = 0; + while (run < BytesPerLine) + { + buf=cf_ReadByte(infile); + if (buf>=192) + { + ubyte tb; + tb=cf_ReadByte(infile); + for (i=0;i<(buf-192);i++,run++,data_index++) + rawdata[data_index]=tb; + } + else + { + rawdata[data_index]=buf; + run++; + data_index++; + } + } + + //Read blue scanline + run = 0; + while (run < BytesPerLine) + { + buf=cf_ReadByte(infile); + if (buf>=192) + { + ubyte tb; + tb=cf_ReadByte(infile); + for (i=0;i<(buf-192);i++,run++,data_index++) + rawdata[data_index]=tb; + } + else + { + rawdata[data_index]=buf; + run++; + data_index++; + } + } + } + + + src_bm=bm_AllocBitmap (width,height,0); + if (src_bm<0) + return -1; // probably out of memory + + ushort *data=bm_data (src_bm,0); + + for (i=0;i +#include "mem.h" + +#include + +char *Tga_file_data=NULL; +int Fake_pos=0; +int Bad_tga=0; +int Fake_file_size=0; + +inline char tga_read_byte () +{ + // Check for bad file + if (Fake_pos+1>Fake_file_size) + { + Bad_tga=1; + return 0; + } + + return Tga_file_data[Fake_pos++]; +} +inline int tga_read_int () +{ + int i; + + // Check for bad file + if (Fake_pos+4>Fake_file_size) + { + Bad_tga=1; + return 0; + } + + i=*(int *)(Tga_file_data+Fake_pos); + Fake_pos+=4; + + return INTEL_INT(i); +} +inline short tga_read_short () +{ + short i; + + // Check for bad file + if (Fake_pos+2>Fake_file_size) + { + Bad_tga=1; + return 0; + } + + i=*(short *)(Tga_file_data+Fake_pos); + Fake_pos+=2; + + return INTEL_SHORT(i); +} + + + +ushort bm_tga_translate_pixel (int pixel,int format) +{ + int red=((pixel>>16) & 0xFF); + int green=((pixel>>8) & 0xFF); + int blue=((pixel) & 0xFF); + int alpha=((pixel>>24) & 0xFF); + ushort newpix; + + if (format==BITMAP_FORMAT_4444) + { + int newred=red>>4; + int newgreen=green>>4; + int newblue=blue>>4; + int newalpha=alpha>>4; + newpix=(newalpha<<12) | (newred<<8) | (newgreen << 4) | (newblue); + } + else + { + int newred=red>>3; + int newgreen=green>>3; + int newblue=blue>>3; + + newpix=OPAQUE_FLAG | (newred<<10) | (newgreen << 5) | (newblue); + + if (alpha==0) + newpix=NEW_TRANSPARENT_COLOR; + } + + + return newpix; + +} + +int bm_tga_read_outrage_compressed16 (CFILE *infile,int n,int num_mips,int type) +{ + ushort *dest_data; + ushort pixel; + int width,height; + int m; + + for (m=0;m>11)<<3; + int g=((pixel & 0x07e0) >>5)<<2; + int b=(pixel & 0x001f)<<3; + + pixel=OPAQUE_FLAG|GR_RGB16(r,g,b); + } + } + + + int i=count/width; + int t=count%width; + dest_data[i*width+t]=pixel; + count++; + } + else if (command>=2 && command<=250) // next pixel is run of pixels + { + pixel=tga_read_short (); + + if (Bad_tga) + return 0; + + + + if (type!=OUTRAGE_1555_COMPRESSED_MIPPED && type!=OUTRAGE_4444_COMPRESSED_MIPPED) + { + if (pixel==0x07e0) + pixel=NEW_TRANSPARENT_COLOR; + else + { + int r=((pixel & 0xF800)>>11)<<3; + int g=((pixel & 0x07e0) >>5)<<2; + int b=(pixel & 0x001f)<<3; + + pixel=OPAQUE_FLAG|GR_RGB16(r,g,b); + } + } + + + for (int k=0;k 1) { + GameBitmaps[n].mip_levels = bm_miplevels(n); + for(m = num_mips; m=0 && pal_num<=MAX_BITMAP_PALETTES); + if (num_mips>1) + GameBitmaps[n].mipmapped=1; + + GameBitmaps[n].palette_num=pal_num; + + for (int m=0;m=2 && command<=250) // next pixel is run of pixels + { + ubyte pixel=tga_read_byte (); + for (int k=0;k1) + mipped=1; + } + + for (i=0;i<9;i++) // ingore next 9 bytes + cf_ReadByte (infile); + + width=cf_ReadShort (infile); + height=cf_ReadShort (infile); + pixsize=cf_ReadByte (infile); + + if (pixsize!=32 && pixsize!=24) + { + mprintf ((0,"bm_tga: This file has a pixsize of field of %d, it should be 32. ",pixsize)); + return -1; + } + + descriptor=cf_ReadByte(infile); + if (((descriptor & 0x0F)!=8) && ((descriptor & 0x0F)!=0)) + { + mprintf ((0,"bm_tga: Descriptor field & 0x0F must be 8 or 0, but this is %d.",descriptor & 0x0F)); + return -1; + } + + for (i=0;i>5; + upside_down=1-upside_down; + + // Load the actual bitmap data in, converting it from 32 bit to 16 bit, and replacing + // that pesky transparency color without our replacement + + if (image_type==10 || image_type==2) + { + if (image_type==10) // compressed tga + { + int total=0; + + while (total<(height*width)) + { + ubyte command=cf_ReadByte (infile); + ubyte len=(command & 127)+1; + + if (command&128) // rle chunk + { + if (pixsize==32) + pixel=cf_ReadInt(infile); + else + { + int r,g,b; + r=cf_ReadByte (infile); + g=cf_ReadByte (infile); + b=cf_ReadByte (infile); + pixel=(255<<24)|(r<<16)|(g<<8)|b; + } + + ushort newpix=bm_tga_translate_pixel (pixel,format); + + for (int k=0;k1) + file_mipped=1; + } + + for (i=0;i<9;i++) // ingore next 9 bytes + cf_ReadByte (infile); + + width=cf_ReadShort (infile); + height=cf_ReadShort (infile); + pixsize=cf_ReadByte (infile); + + if (pixsize!=32 && pixsize!=24) + { + mprintf ((0,"bm_tga: This file has a pixsize of field of %d, it should be 32. ",pixsize)); + return 0; + } + + descriptor=cf_ReadByte(infile); + if (((descriptor & 0x0F)!=8) && ((descriptor & 0x0F)!=0)) + { + mprintf ((0,"bm_tga: Descriptor field & 0x0F must be 8 or 0, but this is %d.",descriptor & 0x0F)); + return 0; + } + + for (i=0;i>5; + upside_down=1-upside_down; + + // Load the actual bitmap data in, converting it from 32 bit to 16 bit, and replacing + // that pesky transparency color without our replacement + if (image_type==OUTRAGE_4444_COMPRESSED_MIPPED || image_type==OUTRAGE_1555_COMPRESSED_MIPPED || image_type==OUTRAGE_NEW_COMPRESSED_MIPPED || image_type==OUTRAGE_COMPRESSED_MIPPED || image_type==OUTRAGE_COMPRESSED_OGF || image_type==OUTRAGE_COMPRESSED_OGF_8BIT) // COMPRESSED OGF + { + // read this ogf in all at once (much faster) + + savepos=cftell(infile); + cfseek (infile,0,SEEK_END); + int lastpos=cftell(infile); + int numleft=lastpos-savepos; + + cfseek (infile,savepos,SEEK_SET); + + Tga_file_data=(char *)mem_malloc (numleft); + ASSERT (Tga_file_data!=NULL); + Fake_pos=0; + Bad_tga=0; + Fake_file_size=numleft; + + cf_ReadBytes ((ubyte *)Tga_file_data,numleft,infile); + + bm_tga_read_outrage_compressed16 (infile,n,num_mips,image_type); + } + + else + Int3(); // Get Jason + + if (image_type==OUTRAGE_COMPRESSED_OGF_8BIT) + Int3(); + //bm_tga_read_outrage_compressed8 (infile,n); + + if (Tga_file_data!=NULL) + { + mem_free (Tga_file_data); + Tga_file_data=NULL; + cfseek (infile,savepos+Fake_pos,SEEK_SET); + } + + cfclose (infile); + + + if ((GameBitmaps[n].flags & BF_WANTS_MIP) && !file_mipped) + { + bm_GenerateMipMaps(n); + } + + return 1; +} + diff --git a/cfile/CFILE.cpp b/cfile/CFILE.cpp new file mode 100644 index 000000000..f0c620ef1 --- /dev/null +++ b/cfile/CFILE.cpp @@ -0,0 +1,1414 @@ +#ifdef MACINTOSH +#include "ddio_mac.h" +#endif + +#include +#include +#include +#include +#include +#include +#ifndef __LINUX__ +//Non-Linux Build Includes +#include +#else +//Linux Build Includes +#include "linux/linux_fix.h" +#endif +#include "BYTESWAP.H" +#include "pserror.h" +#include "ddio.h" +#include "psglob.h" +#include "CFILE.H" +#include "hogfile.h" //info about library file +#include "mem.h" +//Library structures +typedef struct { + char name[PSFILENAME_LEN+1]; //just the filename part + int offset; //offset into library file + int length; //length of this file + ulong timestamp; //time and date of file + int flags; //misc flags +} library_entry; +typedef struct library { + char name[_MAX_PATH]; //includes path + filename + int nfiles; + library_entry *entries; + struct library *next; + int handle; //indentifier for this lib + FILE *file; //pointer to file for this lib, if no one using it +} library; +//entry in extension->path table +typedef struct { + char ext[_MAX_EXT]; + ubyte pathnum; +} ext_entry; +//entry in list of paths +typedef struct { + char path[_MAX_PATH]; + ubyte specific; //if non-zero, only for specific extensions +} path_entry; +#define MAX_PATHS 100 +path_entry paths[MAX_PATHS]; +int N_paths=0; +#define MAX_EXTENSIONS 100 +ext_entry extensions[MAX_EXTENSIONS]; +int N_extensions; +library *Libraries=NULL; +int lib_handle=0; +void cf_Close(); +//Structure thrown on disk error +cfile_error cfe; +//The message for unexpected end of file +char *eof_error = "Unexpected end of file"; +//Generates a cfile error +void ThrowCFileError(int type,CFILE *file,char *msg) +{ + cfe.read_write = type; + cfe.msg = msg; + cfe.file = file; + throw &cfe; +} +//Opens a HOG file. Future calls to cfopen(), etc. will look in this HOG. +//Parameters: libname - the path & filename of the HOG file +//NOTE: libname must be valid for the entire execution of the program. Therefore, it should either +// be a fully-specified path name, or the current directory must not change. +//Returns: 0 if error, else library handle that can be used to close the library +int cf_OpenLibrary(const char *libname) +{ + FILE * fp; + char id[4]; + int i, offset; + library *lib; + static int first_time=1; + tHogHeader header; + tHogFileEntry entry; + + fp = fopen( libname, "rb" ); + if ( fp == NULL ) + return 0; //CF_NO_FILE; + fread( id, strlen(HOG_TAG_STR), 1, fp ); + if ( strncmp( id, HOG_TAG_STR, strlen(HOG_TAG_STR))) { + fclose(fp); + return 0; //CF_BAD_FILE; + } + //check if this if first library opened + if (first_time) { + atexit(cf_Close); + first_time = 0; + } + //allocation library stucture + lib = (library *) mem_malloc(sizeof(*lib)); + if (!lib) { //malloc error + fclose(fp); + return 0; + } + strcpy(lib->name, libname); + // read HOG header + if (!ReadHogHeader(fp, &header)) { + fclose(fp); + mem_free(lib); + return 0; //CF_BAD_LIB; + } +//DAJ lib->nfiles = INTEL_INT(header.nfiles); + lib->nfiles = header.nfiles; + // allocate CFILE hog info. + lib->entries = (library_entry *) mem_malloc(sizeof(library_entry) * lib->nfiles); + if (!lib->entries) { //malloc error + fclose(fp); + mem_free(lib); + return 0; + } + lib->next = Libraries; + Libraries = lib; + //set data offset of first file +//DAJ offset = INTEL_INT(header.file_data_offset); + offset = header.file_data_offset; + //Go to index start + fseek(fp, strlen(HOG_TAG_STR) + HOG_HDR_SIZE, SEEK_SET); + + //read in index table + for (i = 0; i < lib->nfiles; i++) + { + if (!ReadHogEntry(fp, &entry)) { + fclose(fp); + return 0; + } + //Make sure files are in order + ASSERT((i==0) || (stricmp(entry.name,lib->entries[i-1].name) >= 0)); + //Copy into table + strcpy(lib->entries[i].name, entry.name); + lib->entries[i].flags = entry.flags; + lib->entries[i].length = entry.len; + lib->entries[i].offset = offset; + lib->entries[i].timestamp = entry.timestamp; + offset += lib->entries[i].length; + } + //assign a handle + lib->handle = ++lib_handle; + //Save the file pointer + lib->file = fp; + //Sucess. Return the handle + return lib->handle; +} +//Closes a library file. +//Parameters: handle: the handle returned by cf_OpenLibrary() +void cf_CloseLibrary(int handle) +{ + library *lib,*prev=NULL; + for (lib=Libraries;lib;prev=lib,lib=lib->next) { + if (lib->handle == handle) { + if (prev) + prev->next = lib->next; + else + Libraries = lib->next; + if (lib->file) + fclose(lib->file); + mem_free(lib->entries); + mem_free(lib); + return; //sucessful close + } + } +} +//Closes down the CFILE system, freeing up all data, etc. +void cf_Close() +{ + library *next; + while (Libraries) { + next = Libraries->next; + mem_free(Libraries->entries); + mem_free(Libraries); + Libraries = next; + } +} +//Specify a directory to look in for files +//Parameters: path - the directory path. Can be relative to the current cur (the full path will be stored) +// ext - if NULL, look in this dir for all files. If non-null, it is a NULL-terminated list of +// file extensions, & the dir will only be searched for files with a matching extension +//Returns: true if directory added, else false +int cf_SetSearchPath(const char *path,char *ext,...) +{ + if (strlen(path) >= _MAX_PATH) + return 0; + if (N_paths >= MAX_PATHS) + return 0; + //Get & store full path + ddio_GetFullPath(paths[N_paths].path,path); + //Set extenstions for this path + if (ext == NULL) + paths[N_paths].specific = 0; + else { + char **ep = &ext; + paths[N_paths].specific = 1; + while (*ep != NULL) { + if (N_extensions >= MAX_EXTENSIONS) + return 0; + strncpy(extensions[N_extensions].ext,*ep,_MAX_EXT); + extensions[N_extensions].pathnum = N_paths; + N_extensions++; + ep++; + } + } + //This path successfully set + N_paths++; + return 1; +} + +//Removes all search paths that have been added by cf_SetSearchPath +void cf_ClearAllSearchPaths(void) +{ + N_paths = 0; + N_extensions = 0; +} + +// Opens a file for reading in a library, given the library id +CFILE *cf_OpenFileInLibrary(const char *filename,int libhandle) +{ + if(libhandle<=0) + return NULL; + + library *lib; + CFILE *cfile; + lib = Libraries; + + // find the library that we want to use + while (lib) + { + if(lib->handle==libhandle) + break; + lib = lib->next; + } + + if(NULL==lib) + { + // couldn't find the library handle + return NULL; + } + + // now do a binary search for the file entry + int i; + int first = 0,last = lib->nfiles-1,c,found=0; + + do { + i = (first + last) / 2; + c = stricmp(filename,lib->entries[i].name); //compare to current + if (c == 0) { //found it + found = 1; + break; + } + if (first >= last) //exhausted search + break; + if (c > 0) //search key after check key + first = i+1; + else //search key before check key + last = i-1; + } while (1); + + if(!found) + return NULL; // file not in library + + // open the file for reading + FILE *fp; + int r; + //See if there's an available FILE + if (lib->file) { + fp = lib->file; + lib->file = NULL; + } + else { + fp = fopen(lib->name,"rb"); + if (!fp) { + mprintf((1,"Error opening library <%s> when opening file <%s>; errno=%d.",lib->name,filename,errno)); + Int3(); + return NULL; + } + } + cfile = (CFILE *) mem_malloc(sizeof(*cfile)); + if (!cfile) + Error("Out of memory in open_file_in_lib()"); + cfile->name = lib->entries[i].name; + cfile->file = fp; + cfile->lib_handle = lib->handle; + cfile->size = lib->entries[i].length; + cfile->lib_offset = lib->entries[i].offset; + cfile->position = 0; + cfile->flags = 0; + r = fseek(fp,cfile->lib_offset,SEEK_SET); + ASSERT(r == 0); + return cfile; +} + +//searches through the open HOG files, and opens a file if it finds it in any of the libs +CFILE *open_file_in_lib(const char *filename) +{ + library *lib; + CFILE *cfile; + lib = Libraries; + while (lib) { + int i; + //Do binary search for the file + int first = 0, + last = lib->nfiles-1, + c,found=0; + do { + i = (first + last) / 2; + c = stricmp(filename,lib->entries[i].name); //compare to current + if (c == 0) { //found it + found = 1; + break; + } + if (first >= last) //exhausted search + break; + if (c > 0) //search key after check key + first = i+1; + else //search key before check key + last = i-1; + } while (1); + if (found) { + FILE *fp; + int r; + //See if there's an available FILE + if (lib->file) { + fp = lib->file; + lib->file = NULL; + } + else { + fp = fopen(lib->name,"rb"); + if (!fp) { + mprintf((1,"Error opening library <%s> when opening file <%s>; errno=%d.",lib->name,filename,errno)); + Int3(); + return NULL; + } + } + cfile = (CFILE *) mem_malloc(sizeof(*cfile)); + if (!cfile) + Error("Out of memory in open_file_in_lib()"); + cfile->name = lib->entries[i].name; + cfile->file = fp; + cfile->lib_handle = lib->handle; + cfile->size = lib->entries[i].length; + cfile->lib_offset = lib->entries[i].offset; + cfile->position = 0; + cfile->flags = 0; + r = fseek(fp,cfile->lib_offset,SEEK_SET); + ASSERT(r == 0); + return cfile; + } + lib = lib->next; + } + return NULL; +} + +#ifdef __LINUX__ +#include + +static int globerrfn(const char *path,int err) +{ + mprintf((0,"Error accessing %s: %s .... \n",path,strerror(err))); + return 0; +} + +class CFindFiles +{ +public: + CFindFiles() + { + globindex = -1; + } + + bool Start(const char *wildcard, char *namebuf); + bool Next(char *namebuf); + void Close(void); + +private: + int globindex; + glob_t ffres; +}; + +bool CFindFiles::Start(const char *wildcard, char *namebuf) +{ + ASSERT(wildcard); + ASSERT(namebuf); + + if(globindex!=-1) + Close(); + + int rc,flags; + flags = GLOB_MARK; + rc = glob(wildcard,flags,globerrfn,&ffres); + if(rc==GLOB_NOSPACE){ + mprintf((0,"Out of space during glob\n")); + globindex = -1; + return false; + } + if(!ffres.gl_pathc){ + globindex = -1; + return false; + } + + globindex = 0; + char ext[256]; + ddio_SplitPath(ffres.gl_pathv[0],NULL,namebuf,ext); + strcat(namebuf,ext); + return true; +} + +bool CFindFiles::Next(char *namebuf) +{ + ASSERT(namebuf); + if(globindex==-1) + return false; + globindex++; + if(globindex>=ffres.gl_pathc) + return false; + + char ext[256]; + ddio_SplitPath(ffres.gl_pathv[globindex],NULL,namebuf,ext); + strcat(namebuf,ext); + return true; +} + +void CFindFiles::Close(void) +{ + if(globindex==-1) + return; + globindex = -1; + globfree(&ffres); +} + +bool cf_FindRealFileNameCaseInsenstive(const char *directory,const char *fname,char *new_filename) +{ + bool use_dir = false; + char dir_to_use[_MAX_PATH]; + char file_to_use[_MAX_PATH]; + + char *real_dir,*real_file; + + if(directory) + { + // there is a directory for this path + use_dir = true; + real_dir = (char *)directory; + real_file = (char *)fname; + }else + { + // there may be a directory in the path (*sigh*) + char t_ext[256]; + char t_dir[_MAX_PATH]; + char t_filename[_MAX_PATH]; + + ddio_SplitPath(fname,t_dir,t_filename,t_ext); + if(strlen(t_dir)>0) + { + use_dir = true; + strcpy(dir_to_use,t_dir); + real_dir = (char *)dir_to_use; + strcpy(file_to_use,t_filename); + strcat(file_to_use,t_ext); + real_file = (char *)file_to_use; + + mprintf((1,"CFILE: Found directory \"%s\" in filename, new filename is \"%s\"\n",real_dir,real_file)); + }else + { + use_dir = false; + real_dir = NULL; + real_file = (char *)fname; + } + } + + // build up a list of filenames in the current directory that begin with the lowercase and + // upper case first letter of the filename + + // do the case of the first letter to start + int case_val; + char wildcard_pattern[_MAX_PATH]; + int iterations = 1; + bool found_match = false; + + if( (real_file[0]>='a' && real_file[0] <= 'z') || + (real_file[0]>='A' && real_file[0] <= 'Z') ) + { + // alpha first letter...we need to do 2 iterations + iterations = 2; + } + + for(case_val=0;case_val= 'a' && first_letter <= 'z') + { + // we need to uppercase the letter + first_letter = toupper(first_letter); + }else + { + // we need to lowercase the letter + first_letter = tolower(first_letter); + } + + // create a wildcard patter full of ? replacing letters (except the first one) + char *wptr = wildcard_pattern; + char *fptr = &real_file[1]; + *wptr = first_letter; + wptr++; + while(*fptr) + { + if(isalpha(*fptr)) + { + *wptr = '?'; + }else + { + *wptr = *fptr; + } + + fptr++; + wptr++; + } + *wptr = '\0'; + }else + { + // use the case of the first letter + // create a wildcard patter full of ? replacing letters (except the first one) + char *wptr = wildcard_pattern; + char *fptr = &real_file[1]; + *wptr = real_file[0]; + wptr++; + while(*fptr) + { + if(isalpha(*fptr)) + { + *wptr = '?'; + }else + { + *wptr = *fptr; + } + + fptr++; + wptr++; + } + *wptr = '\0'; + } + + // now tack on a directory if we are to use a directory + char *wpattern; + char fullpath[_MAX_PATH]; + if(use_dir) + { + ddio_MakePath(fullpath,real_dir,wildcard_pattern,NULL); + wpattern = fullpath; + }else + { + wpattern = wildcard_pattern; + } + + // ok, we have our wildcard pattern, get all the files that match it + // and search them looking for a match (case insensitive) + char namebuffer[_MAX_PATH]; + bool gotfile; + CFindFiles ff; + for(gotfile = ff.Start(wpattern,namebuffer); gotfile ; gotfile = ff.Next(namebuffer) ) + { + if(!stricmp(namebuffer,real_file)) + { + // we found a match! + found_match = true; + break; + } + } + ff.Close(); + + if(found_match) + { + strcpy(new_filename,namebuffer); + mprintf((1,"CFILE: Using \"%s\" instead of \"%s\"\n",new_filename,real_file)); + break; + } + } + + return found_match; +} + + +FILE *open_file_in_directory_case_sensitive(const char *directory,const char *filename,const char *mode,char *new_filename) +{ + if(cf_FindRealFileNameCaseInsenstive(directory,filename,new_filename)) + { + // we have a file, open it open and use it + char full_path[_MAX_PATH*2]; + if(directory!=NULL) + { + ddio_MakePath(full_path,directory,new_filename,NULL); + }else + { + strcpy(full_path,new_filename); + } + + return fopen(full_path,mode); + } + + return NULL; +} +#endif + + +//look for the file in the specified directory +CFILE *open_file_in_directory(const char *filename,const char *mode,const char *directory) +{ + FILE *fp; + CFILE *cfile; + char path[_MAX_PATH*2]; + char tmode[3] = "rb"; + if (directory != NULL) + { + // Make a full path + ddio_MakePath(path, directory, filename, NULL); + } + else //no directory specified, so just use filename passed + strcpy(path, filename); + //set read or write mode + tmode[0] = mode[0]; + //if mode is "w", then open in text or binary as requested. If "r", alsway open in "rb" + tmode[1] = (mode[0] == 'w') ? mode[1] : 'b'; + //try to open file +#ifdef MACINTOSH + fp = mac_fopen(path,tmode); +#else + fp = fopen( path, tmode ); +#endif + +#ifdef __LINUX__ + // for Filesystems with case sensitive files we'll check for different versions of the filename + // with different case's. + if(fp) + { + // found the file, open it + cfile = (CFILE *) mem_malloc(sizeof(*cfile)); + if (!cfile) + Error("Out of memory in open_file_in_directory()"); + cfile->name = (char *) mem_malloc(sizeof(char) * (strlen(filename)+1)); + if (!cfile->name) + Error("Out of memory in open_file_in_directory()"); + strcpy(cfile->name, filename); + cfile->file = fp; + cfile->lib_handle = -1; + cfile->size = ddio_GetFileLength(fp); + cfile->lib_offset = 0; //0 means on disk, not in HOG + cfile->position = 0; + cfile->flags=0; + return cfile; + }else + { + // try different cases of the filename + char using_filename[_MAX_PATH]; + fp = open_file_in_directory_case_sensitive(directory,filename,tmode,using_filename); + if(!fp) + { + // no dice + return NULL; + }else + { + // found a version of the file! + mprintf((0,"CFILE: Unable to find %s, but using %s instead\n",filename,using_filename)); + cfile = (CFILE *) mem_malloc(sizeof(*cfile)); + if (!cfile) + Error("Out of memory in open_file_in_directory()"); + cfile->name = (char *) mem_malloc(sizeof(char) * (strlen(using_filename)+1)); + if (!cfile->name) + Error("Out of memory in open_file_in_directory()"); + strcpy(cfile->name, using_filename); + cfile->file = fp; + cfile->lib_handle = -1; + cfile->size = ddio_GetFileLength(fp); + cfile->lib_offset = 0; //0 means on disk, not in HOG + cfile->position = 0; + cfile->flags=0; + return cfile; + } + } +#else + if (!fp) //didn't get file + return NULL; + else { //got file + cfile = (CFILE *) mem_malloc(sizeof(*cfile)); + if (!cfile) + Error("Out of memory in open_file_in_directory()"); + cfile->name = (char *) mem_malloc(sizeof(char) * (strlen(filename)+1)); + if (!cfile->name) + Error("Out of memory in open_file_in_directory()"); + strcpy(cfile->name, filename); + cfile->file = fp; + cfile->lib_handle = -1; + cfile->size = ddio_GetFileLength(fp); + cfile->lib_offset = 0; //0 means on disk, not in HOG + cfile->position = 0; + cfile->flags=0; + return cfile; + } +#endif +} +//Opens a file for reading or writing +//If a path is specified, will try to open the file only in that path. +//If no path is specified, will look through search directories and library files. +//Parameters: filename - the name if the file, with or without a path +// mode - the standard C mode string +//Returns: the CFile handle, or NULL if file not opened +CFILE *cfopen(const char * filename, const char * mode) +{ + CFILE *cfile; + char path[_MAX_PATH*2], fname[_MAX_PATH*2], ext[_MAX_EXT]; + int i; + //Check for valid mode + ASSERT((mode[0] == 'r') || (mode[0] == 'w')); + ASSERT((mode[1] == 'b') || (mode[1] == 't')); + //get the parts of the pathname + ddio_SplitPath(filename, path, fname, ext); + //if there is a path specified, use it instead of the libraries, search dirs, etc. + //if the file is writable, just open it, instead of looking in libs, etc. + if (strlen(path) || (mode[0]=='w')) { //found a path + cfile = open_file_in_directory(filename,mode,NULL); //use path specified with file + goto got_file; //don't look in libs, etc. + } +//@@ Don't look in current dir. mt, 3-12-97 +//@@ //first look in current directory +//@@ cfile = open_file_in_directory(filename,mode,"."); //current dir +//@@ if (cfile || (mode[0] == 'w')) +//@@ goto got_file; + //First look in the directories for this file's extension + for (i=0;iflags |= CF_WRITING; + if (mode[1] == 't') + cfile->flags |= CF_TEXT; + } + return cfile; +} +//Returns the length of the specified file +//Parameters: cfp - the file pointer returned by cfopen() +int cfilelength( CFILE *cfp ) +{ + return cfp->size; +} +//Closes an open CFILE. +//Parameters: cfile - the file pointer returned by cfopen() +void cfclose( CFILE * cfp ) +{ + //Either give the file back to the library, or close it + if (cfp->lib_handle != -1) { + library *lib; + for (lib=Libraries;lib;lib=lib->next) { + if (lib->handle == cfp->lib_handle) { //found the library + //if library doesn't already have a file, give it this one + if (lib->file == NULL) { + lib->file = cfp->file; + cfp->file = NULL; + } + break; + } + } + } + //If the file handle wasn't given back to library, close the file + if (cfp->file) + fclose(cfp->file); + //free the name, if allocated + if (!cfp->lib_offset) + mem_free(cfp->name); + //free the cfile struct + mem_free(cfp); +} +//Just like stdio fgetc(), except works on a CFILE +//Returns a char or EOF +int cfgetc( CFILE * cfp ) +{ + int c; + static unsigned char ch[3] = "\0\0"; + if (cfp->position >= cfp->size ) return EOF; + + fread(ch,sizeof(char),1,cfp->file); + c = ch[0]; + //c = getc( cfp->file ); + if(cfeof(cfp)) + c = EOF; + if (c != EOF) { + cfp->position++; + //do special newline handling for text files: + // if CR or LF by itself, return as newline + // if CR/LF pair, return as newline + if (cfp->flags & CF_TEXT) { + if (c == 10) //return LF as newline + c = '\n'; + else if (c == 13) { //check for CR/LF pair + fread(ch,sizeof(char),1,cfp->file); + int cc = ch[0];//getc(cfp->file); + //if (cc != EOF) { + if (!cfeof(cfp)) { + if (cc == 10) //line feed? + cfp->position++; //..yes, so swallow it + else + { + //ungetc(cc,cfp->file); //..no, so put it back + fseek(cfp->file,-1,SEEK_CUR); + } + } + c = '\n'; //return CR or CR/LF pair as newline + } + } + } + return c; +} +//Just like stdio fseek(), except works on a CFILE +int cfseek( CFILE *cfp, long int offset, int where ) +{ + int c, goal_position; + switch( where ) { + case SEEK_SET: + goal_position = offset; + break; + case SEEK_CUR: + goal_position = cfp->position+offset; + break; + case SEEK_END: + goal_position = cfp->size+offset; + break; + default: + return 1; + } + c = fseek( cfp->file, cfp->lib_offset + goal_position, SEEK_SET ); + cfp->position = ftell(cfp->file) - cfp->lib_offset; + return c; +} +//Just like stdio ftell(), except works on a CFILE +int cftell( CFILE * cfp ) +{ + return cfp->position; +} +//Returns true if at EOF +int cfeof(CFILE *cfp) +{ + return (cfp->position >= cfp->size ); +} +// Tells if the file exists +// Returns non-zero if file exists. Also tells if the file is on disk +// or in a hog - See return values in cfile.h +int cfexist( const char * filename ) +{ + CFILE *cfp; + int ret; + + cfp = cfopen(filename,"rb"); + if (!cfp) { //Didn't get file. Why? + if (errno == EACCES) //File exists, but couldn't open it + return CF_ON_DISK; //..so say it exists on the disk +//DAJ if (errno != ENOENT) //Check if error is "file not found" +//DAJ Int3(); //..warn if not + return CF_NOT_FOUND; //Say we didn't find the file + } + ret = cfp->lib_offset ? CF_IN_LIBRARY : CF_ON_DISK; + cfclose(cfp); + return ret; +} +//Reads the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO READ STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes read. +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int cf_ReadBytes(ubyte *buf, int count, CFILE *cfp) +{ + int i; + char *error_msg = eof_error; //default error + ASSERT(! (cfp->flags & CF_TEXT)); + if (cfp->position + count <= cfp->size) { + i = fread ( buf, 1, count, cfp->file ); + if (i == count) { + cfp->position += i; + return i; + } + //if not EOF, then get the error message + if (! feof(cfp->file)) + error_msg = strerror(errno); + } + mprintf((1,"Error reading %d bytes from position %d of file <%s>; errno=%d.",count,cfp->position,cfp->name,errno)); + ThrowCFileError(CFE_READING,cfp,error_msg); + return 0; +} +// The following functions read numeric vales from a CFILE. All values are +// stored in the file in Intel (little-endian) format. These functions +// will convert to big-endian if required. +// These funtions will exit the program with an error if the value +// cannot be read, so do not call these if you don't require the data +// to be present. +//Read and return an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int cf_ReadInt(CFILE *cfp) +{ + int i; + cf_ReadBytes( (ubyte *) &i, sizeof(i), cfp); + return INTEL_INT(i); +} +//Read and return a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +short cf_ReadShort(CFILE *cfp) +{ + short i; + cf_ReadBytes( (ubyte *) &i, sizeof(i), cfp); + return INTEL_SHORT(i); +} +//Read and return a byte (8 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +sbyte cf_ReadByte(CFILE *cfp) +{ + int i; + i = cfgetc(cfp); + if (i == EOF) + ThrowCFileError(CFE_READING,cfp,cfeof(cfp)?eof_error:strerror(errno)); + return (sbyte) i; +} +//Read and return a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +float cf_ReadFloat(CFILE *cfp) +{ + float f; + cf_ReadBytes( (ubyte *) &f, sizeof(f), cfp); +#ifdef MACINTOSH + float e = INTEL_FLOAT(f); //DAJ bash to zero if reads a NaN + if(isnan(e)) + e = 0.0; + return e; +#else + return INTEL_FLOAT(f); +#endif +} +//Read and return a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +double cf_ReadDouble(CFILE *cfp) +{ + double f; + cf_ReadBytes( (ubyte *) &f, sizeof(f), cfp); + #ifdef BIG_ENDIAN + { + double t; + int *sp = (int *) &f; + int *dp = (int *) &t; + dp[0] = SWAPINT(sp[1]); + dp[1] = SWAPINT(sp[0]); + f = t; + } + #endif + return f; +} +//Reads a string from a CFILE. If the file is type binary, this +//function reads until a NULL or EOF is found. If the file is text, +//the function reads until a newline or EOF is found. The string is always +//written to the destination buffer null-terminated, without the newline. +//Parameters: buf - where the string is written +// n - the maximum string length, including the terminating 0 +// cfp - the CFILE pointer +//Returns the number of bytes in the string, before the terminator +//Does not generate an exception on EOF +int cf_ReadString(char * buf, size_t n, CFILE * cfp ) +{ + int c; + uint count; + char *bp; + if (n==0) + return -1; + bp = buf; + for (count=0;;count++) { + c = cfgetc(cfp); + if (c == EOF) { + if (! cfeof(cfp)) //not actually at EOF, so must be error + ThrowCFileError(CFE_READING,cfp,strerror(errno)); + break; + } + + if ((!(cfp->flags & CF_TEXT) && (c == 0)) || ((cfp->flags & CF_TEXT) && (c == '\n'))) + break; //end-of-string + if (count < n-1) //store char if room in buffer + *bp++ = c; + } + *bp = 0; //write terminator + return count; +} +//Writes the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO WRITE STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes written. +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int cf_WriteBytes(const ubyte *buf, int count, CFILE *cfp) +{ + int i; + if (! (cfp->flags & CF_WRITING)) + return 0; + ASSERT (count>0); + i = fwrite( buf, 1, count, cfp->file ); + cfp->position += i; + if (i != count) + ThrowCFileError(CFE_WRITING,cfp,strerror(errno)); + return i; +} +//Writes a null-terminated string to a file. If the file is type binary, +//the string is terminated in the file with a null. If the file is type +//text, the string is terminated with a newline. +//Parameters: buf - pointer to the string +// cfp = the CFILE pointer +//Returns the number of bytes written +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int cf_WriteString(CFILE *cfp, const char *buf) +{ + int len; + len = strlen(buf); + if (len != 0) //write string + cf_WriteBytes((ubyte *) buf, len, cfp); + //Terminate with newline (text file) or NULL (binary file) + cf_WriteByte(cfp,(cfp->flags & CF_TEXT)?'\n':0); + return len+1; +} +//Just like stdio fprintf(), except works on a CFILE +int cfprintf( CFILE *cfp, const char *format, ... ) +{ +#ifndef MACINTOSH + va_list args; + int count; + va_start(args, format ); + count = vfprintf(cfp->file,format,args); + cfp->position += count+1; //count doesn't include terminator + return count; +#endif +} +// The following functions write numeric vales to a CFILE. All values are +// stored to the file in Intel (little-endian) format. +// All these throw an exception if there's an error on write. +//Write an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteInt(CFILE *cfp,int i) +{ + int t = INTEL_INT(i); + cf_WriteBytes( (ubyte *) &t, sizeof(t), cfp); +} +//Write a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteShort(CFILE *cfp,short s) +{ + short t = INTEL_SHORT(s); + cf_WriteBytes( (ubyte *) &t, sizeof(t), cfp); +} +//Write a byte (8 bits). +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteByte(CFILE *cfp,sbyte b) +{ + if (fputc(b,cfp->file) == EOF) + ThrowCFileError(CFE_WRITING,cfp,strerror(errno)); + cfp->position++; + //If text file & writing newline, increment again for LF + if ((cfp->flags & CF_TEXT) && (b == '\n')) //check for text mode newline + cfp->position++; +} +//Write a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteFloat(CFILE *cfp,float f) +{ + float t = INTEL_FLOAT(f); + cf_WriteBytes( (ubyte *) &t, sizeof(t), cfp); +} +//Write a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteDouble(CFILE *cfp,double d) +{ + #ifdef BIG_ENDIAN + { + double t; + int *sp = (int *) &d; + int *dp = (int *) &t; + dp[0] = SWAPINT(sp[1]); + dp[1] = SWAPINT(sp[0]); + d = t; + } + #endif + cf_WriteBytes( (ubyte *) &d, sizeof(d), cfp); +} +//Copies a file. Returns TRUE if copied ok. Returns FALSE if error opening either file. +//Throws an exception of type (cfile_error *) if the OS returns an error on read or write +bool cf_CopyFile (char *dest,const char *src,int copytime) +{ + CFILE *infile,*outfile; + if (!stricmp(dest,src)) + return 1; // don't copy files if they are the same + infile=(CFILE *)cfopen (src,"rb"); + if (!infile) + return 0; + outfile=(CFILE *)cfopen (dest,"wb"); + if (!outfile) + { + cfclose (infile); + return 0; + } + int progress = 0; + int readcount = 0; + #define COPY_CHUNK_SIZE 5000 + ubyte copybuf[COPY_CHUNK_SIZE]; + while (!cfeof(infile)) + { + //ubyte c; + + if(progress+COPY_CHUNK_SIZE<=infile->size) + { + readcount = COPY_CHUNK_SIZE; + } + else + { + readcount = infile->size - progress; + } + cf_ReadBytes(copybuf,readcount,infile); + cf_WriteBytes(copybuf,readcount,outfile); + progress+=readcount; + //c=cf_ReadByte (infile); + //cf_WriteByte (outfile,c); + } + int infile_lib_offset = infile->lib_offset; + cfclose (infile); + cfclose (outfile); + if(!infile_lib_offset && copytime) + { + cf_CopyFileTime(dest,src); + } + return 1; +} +//Checks to see if two files are different. +//Returns TRUE if the files are different, or FALSE if they are the same. +bool cf_Diff (const char *a,const char *b) +{ + return (ddio_FileDiff(a, b)); +} +//Copies the file time from one file to another +void cf_CopyFileTime (char *dest,const char *src) +{ + ddio_CopyFileTime (dest,src); +} +// Changes a files attributes (ie read/write only) +void cf_ChangeFileAttributes (const char *name,int attr) +{ +#ifdef MACINTOSH + DebugStr("\pERROR in cf_ChangeFileAttributes: not supported"); +#else + if( _chmod( name, attr ) == -1 ) + Int3(); // Get Jason or Matt, file not found! +#endif +} +// rewinds cfile position +void cf_Rewind(CFILE *fp) +{ + if (fp->lib_offset) { + int r = fseek(fp->file,fp->lib_offset,SEEK_SET); + ASSERT(r==0); + } + else { + rewind(fp->file); + } + fp->position = 0; +} +//Calculates a 32 bit CRC for the specified file. a return code of -1 means file note found +#define CRC32_POLYNOMIAL 0xEDB88320L +#define CRC_BUFFER_SIZE 5000 + +unsigned int cf_CalculateFileCRC (CFILE *infile) +{ + int i,j; + ubyte crcbuf[CRC_BUFFER_SIZE]; + static bool Cfile_crc_calculated = false; + static unsigned int CRCTable[256]; + unsigned int crc; + unsigned int temp1; + unsigned int temp2; + unsigned int readlen; + + // Only make the lookup table once + if(!Cfile_crc_calculated) + { + Cfile_crc_calculated = true; + + for( i=0;i<=255;i++) + { + crc=i; + for(j=8;j>0;j--) + { + if(crc&1) + crc=(crc>>1)^CRC32_POLYNOMIAL; + else + crc>>=1; + } + CRCTable[i]=crc; + } + } + + crc = 0xffffffffl; + while (!cfeof(infile)) + { + if((infile->size - infile->position)size - infile->position; + else + readlen = CRC_BUFFER_SIZE; + if(!cf_ReadBytes (crcbuf,readlen,infile)) + { + //Doh, error time! + Int3(); + return 0xFFFFFFFF; + } + for(unsigned int a=0;a>8)&0x00FFFFFFL; + temp2=CRCTable[((int)crc^crcbuf[a])&0xff]; + crc=temp1^temp2; + } + } + + return crc^0xffffffffl; +} + +unsigned int cf_GetfileCRC (char *src) +{ + CFILE *infile; + + infile=(CFILE *)cfopen (src,"rb"); + if (!infile) + return 0xFFFFFFFF; + + unsigned int crc = cf_CalculateFileCRC(infile); + cfclose(infile); + + return crc; +} + +char cfile_search_wildcard[256]; +library *cfile_search_library = NULL; +int cfile_search_curr_index = 0; +bool cfile_search_ispattern = false; +// the following cf_LibraryFind function are similar to the ddio_Find functions as they look +// for files that match the wildcard passed in, however, this is to be used for hog files. +bool cf_LibraryFindFirst(int handle,const char *wildcard,char *buffer) +{ + ASSERT (wildcard); + ASSERT (buffer); + if( !wildcard || !buffer ) + return false; + *buffer = '\0'; + if(cfile_search_library) + cf_LibraryFindClose(); + //find the library + cfile_search_library = Libraries; + while( cfile_search_library && cfile_search_library->handle!=handle){ + cfile_search_library = cfile_search_library->next; + } + if(!cfile_search_library) + return false; + //now find the first matching file + strncpy(cfile_search_wildcard,wildcard,255); + cfile_search_wildcard[255] = '\0'; + cfile_search_ispattern = (bool)(PSGlobHasPattern(cfile_search_wildcard)!=0); + cfile_search_curr_index = 0; + + while(cfile_search_curr_indexnfiles){ + if(cfile_search_ispattern){ + if(PSGlobMatch(cfile_search_wildcard,cfile_search_library->entries[cfile_search_curr_index].name,0,0)){ + //it's a match + strcpy(buffer,cfile_search_library->entries[cfile_search_curr_index].name); + cfile_search_curr_index++; + return true; + } + }else{ + if(!stricmp(cfile_search_library->entries[cfile_search_curr_index].name,cfile_search_wildcard)){ + strcpy(buffer,cfile_search_library->entries[cfile_search_curr_index].name); + cfile_search_curr_index++; + return true; + } + } + cfile_search_curr_index++; + } + //we didn't find a match + return false; +} +bool cf_LibraryFindNext(char *buffer) +{ + while(cfile_search_curr_indexnfiles){ + if(cfile_search_ispattern){ + if(PSGlobMatch(cfile_search_wildcard,cfile_search_library->entries[cfile_search_curr_index].name,0,0)){ + //it's a match + strcpy(buffer,cfile_search_library->entries[cfile_search_curr_index].name); + cfile_search_curr_index++; + return true; + } + }else{ + if(!stricmp(cfile_search_library->entries[cfile_search_curr_index].name,cfile_search_wildcard)){ + strcpy(buffer,cfile_search_library->entries[cfile_search_curr_index].name); + cfile_search_curr_index++; + return true; + } + } + cfile_search_curr_index++; + } + return false; +} +void cf_LibraryFindClose(void) +{ + cfile_search_library = NULL; + cfile_search_curr_index = 0; + cfile_search_ispattern = false; +} + + +// returns hog cfile info, using a library handle opened via cf_OpenLibrary. +bool cf_ReadHogFileEntry(int libr, const char *filename, tHogFileEntry *entry, int *fileoffset) +{ +//searches through the open HOG files, and opens a file if it finds it in any of the libs + library *lib; + + lib = Libraries; + + while (lib) + { + int i; + + if (lib->handle == libr || libr == -1) { + //Do binary search for the file + int first = 0, last = lib->nfiles-1, c,found=0; + do { + + i = (first + last) / 2; + c = stricmp(filename,lib->entries[i].name); //compare to current + + if (c == 0) { //found it + found = 1; + break; + } + + if (first >= last) //exhausted search + break; + + if (c > 0) //search key after check key + first = i+1; + else //search key before check key + last = i-1; + + } while (1); + + if (found) { + strcpy(entry->name, lib->entries[i].name); + entry->len = lib->entries[i].length; + entry->flags = lib->entries[i].flags; + entry->timestamp = lib->entries[i].timestamp; + *fileoffset = lib->entries[i].offset; + return true; + } + else if (lib->handle == libr) { + break; + } + } + + lib = lib->next; + } + + return false; +} + + +bool cf_IsFileInHog(char *filename, char *hogname) +{ + library *lib = Libraries; + + while(lib) + { + if(stricmp(lib->name,hogname)==0) + { + //Now look for filename + CFILE *cf; + cf = cf_OpenFileInLibrary(filename,lib->handle); + if(cf) + { + cfclose(cf); + return true; + } + } + lib = lib->next; + } + + return false; +} \ No newline at end of file diff --git a/cfile/CMakeLists.txt b/cfile/CMakeLists.txt new file mode 100644 index 000000000..ae569425b --- /dev/null +++ b/cfile/CMakeLists.txt @@ -0,0 +1,7 @@ +SET (HEADERS ) +SET (CPPS + CFILE.cpp + hog.cpp + InfFile.cpp) + +ADD_LIBRARY(cfile STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/cfile/InfFile.cpp b/cfile/InfFile.cpp new file mode 100644 index 000000000..67b4767e4 --- /dev/null +++ b/cfile/InfFile.cpp @@ -0,0 +1,209 @@ +// InfFile.cpp: implementation of the InfFile class. +// +////////////////////////////////////////////////////////////////////// + +#include "InfFile.h" + +#include "CFILE.H" +#include "pstring.h" +#include "pserror.h" + +#include + +#define INFFILE_NULL -1024 + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +InfFile::InfFile() +{ + m_fp = NULL; +} + + +InfFile::~InfFile() +{ +// close file and free symbol lists + Close(); +} + + +// symbol functions. +void InfFile::AddSymbol(const char * name, const char * text) +{ + tListNode *symbol = new tListNode; + + strcpy(symbol->t.name, name); + symbol->t.text = new char[strlen(text)+1]; + strcpy(symbol->t.text, text); + + m_sym_list.link(symbol); +} + + +void InfFile::FreeSymbols() +{ + tListNode *sym; + + m_sym_list.start(); + + while ((sym = m_sym_list.start())!=0) + { + sym = m_sym_list.unlink(); + if (sym->t.text) + delete sym->t.text; + delete sym; + } +} + + +const char* InfFile::GetSymbolText(const char * name) +{ + tListNode *sym; + + m_sym_list.start(); + for (sym=m_sym_list.start(); sym; sym = sym->next) + { + if (strcmp(sym->t.name, name) == 0) + return (const char *)sym->t.text; + } + + return NULL; +} + + +// opens an inf file, pass in a lexical analyzer that will return a command index. +// tag_check is a string that must appear at the very beginning of the file. +// lexfn is a function to match a command to a number. num <= 0 are taken. +bool InfFile::Open(const char *filename, const char *tag_check, int (*lexfn)(const char *command)) +{ + if (m_fp) + return false; + + m_fp = cfopen(filename, "rt"); + if (!m_fp) + return false; + +// do tag checking. + if (tag_check) { + cf_ReadString(m_linetext, INFFILE_LINELEN, m_fp); + if (strcmp(tag_check, m_linetext) != 0) { + cfclose(m_fp); + m_fp = NULL; + return false; + } + } + +// okay we have a 'valid' inf file. + m_lexfn = lexfn; + m_lineptr = NULL; + m_endptr = NULL; + m_line = 0; + + return true; +} + + +// closes the file +void InfFile::Close() +{ + if (!m_fp) + return; + + FreeSymbols(); + m_sym_list.free(); + cfclose(m_fp); + m_fp = NULL; +} + + +// read a command line. returns false on eof. +bool InfFile::ReadLine() +{ + if (cfeof(m_fp)) + return false; + + cf_ReadString(m_linetext, INFFILE_LINELEN, m_fp); + +// start line. + m_lineptr = &m_linetext[0]; + m_endptr = m_lineptr + strlen(m_lineptr)+1; + m_line++; + + return true; +} + + +// parses a line of 'code'. return value from the lexfn +// a 0 for EOL (end of line). +// -1 for error. +// -2 for comment +int InfFile::ParseLine(char *operand, int oprlen) +{ + if (!m_lineptr) + return INFFILE_EOL; + +// tokenize line. + char command[32]; // Command read from line. + char *opr, *cmd; + int retval = INFFILE_NULL; + + if (strlen(m_lineptr) == 0) + return INFFILE_EOL; + +// parse out command and operand (command=str) +// m_lineptr = command, opr = operand, nextcmd = next lineptr. + cmd = strtok(m_lineptr, " \t=:"); + if (cmd) + opr = strtok(NULL, ",;"); + else + Int3(); + +// clean out trailing and preceeding trash (tabs, spaces, etc) + CleanupStr(command, cmd, sizeof(command)); + if (opr) + CleanupStr(operand, opr, oprlen); + else + operand[0] = 0; + + if (strlen(command) == 0) { + if (strlen(operand) == 0) + return INFFILE_EOL; + } + +// do predefined commands + if (command[0] == '@') + retval = INFFILE_COMMENT; + +// do symbolic commands. + if (operand[0] == '#') { + const char *sym = GetSymbolText(&operand[1]); + if (sym) + strcpy(operand, sym); + else + retval = INFFILE_ERROR; + } + + if (retval == INFFILE_NULL) { + if (command[0] == '#') { + AddSymbol(&command[1], operand); + retval = INFFILE_SYMBOL; + } + else { + // do command through lex analyzer. if not preprocessed. + if (m_lexfn) + retval = (*m_lexfn)(command); + } + } + +// adjust text pointers to prepare for next read + char *lastptr = (opr) ? opr : cmd; + m_lineptr = lastptr + strlen(lastptr)+1; + if (m_lineptr >= m_endptr) + m_lineptr = NULL; + +// end + return retval; +} diff --git a/cfile/hog.cpp b/cfile/hog.cpp new file mode 100644 index 000000000..cedd16a99 --- /dev/null +++ b/cfile/hog.cpp @@ -0,0 +1,430 @@ +/* + * $Logfile: /DescentIII/Main/Cfile/hog.cpp $ + * $Revision: 20 $ + * $Date: 8/06/99 4:29p $ + * $Author: Samir $ + * + * + * + * $Log: /DescentIII/Main/Cfile/hog.cpp $ + * + * 20 8/06/99 4:29p Samir + * added function to read record information for a hog file entry. needed + * from mn3edit. + * + * 19 7/28/99 3:35p Kevin + * Mac! + * + * 18 5/10/99 10:25p Ardussi + * changes to compile on Mac + * + * 17 4/15/99 9:19p Jeff + * Changes for Linux version + * + * 16 2/16/99 2:08p Nate + * Made CreateNewHogFile() a bit friendlier + * + * 15 1/13/99 6:39a Jeff + * fixed some case-sensitive #includes + * + * 13 10/15/98 2:52p Nate + * Fixed FileCopy() by changing free() to mem_free() + * + * 12 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 11 8/16/98 4:23p Nate + * Added CreateNewHogFile() + * + * 10 8/14/98 4:38p Nate + * Fixed a few minor bugs and added better error reporting + * + * 9 8/14/98 1:01p Nate + * Added better error reporting for the HogEditor + * + * 8 7/27/98 9:48a Nate + * Fixed hog file header filling + * + * 7 7/24/98 6:39p Nate + * fixed hog bugs. + * + * 6 7/20/98 3:34p Nate + * Fixed up Hog file stuff + * + * 5 7/13/98 6:20p Nate + * Fixed ReadHogEntry() and WriteHogEntry() + * + * 4 7/13/98 2:18p Nate + * + * 3 4/01/98 7:03p Samir + * modified some cfile stuff. + * + * 2 3/31/98 6:13p Samir + * new hogfile format. + * + * 1 3/31/98 6:08p Samir + * + * + * $NoKeywords: $ + */ +#include +#include +#include +#ifdef MACINTOSH +#include +#else +#include +#endif +#ifndef __LINUX__ +//Non-Linux Builds +#include +#else +//Linux Builds +#include "linux/linux_fix.h" +#endif +#include "byteswap.h" +#include "hogfile.h" +#include "pstypes.h" +#include "Macros.h" +#include "mem.h" +#include "ddio.h" +/* HOG FILE FORMAT v2.0 + + HOG_TAG_STR [strlen()] + NFILES [int32] + HDRINFO [HOG_HDR_SIZE] + FILE_TABLE [sizeof(FILE_ENTRY) * NFILES] + FILE 0 [filelen(FILE 0)] + FILE 1 [filelen(FILE 1)] + . + . + . + FILE NFILES-1 [filelen(NFILES -1)] +*/ +char hogerr_filename[PSPATHNAME_LEN]; // Used by NewHogFile() to return errors +//////////////////////////////////////////////////////////////////////// +/* FileCopy + used to copy one file to another. +*/ +bool FileCopy(FILE *ofp,FILE *ifp,int length) +{ + #define BUFFER_SIZE (1024*1024) // 1 meg + char *buffer; + buffer = (char *)mem_malloc(BUFFER_SIZE); + if (!buffer) + return false; + while (length) + { + size_t n,read_len; + read_len = min(length,(int)BUFFER_SIZE); + n = fread( buffer, 1, read_len, ifp ); + if ( n != read_len ) { + mem_free(buffer); + return false; + } + if (fwrite( buffer, 1, read_len, ofp) != read_len ) { + mem_free(buffer); + return false; + } + length -= read_len; + } + mem_free(buffer); + return true; +} +bool ReadHogHeader(FILE *fp, tHogHeader *header) +{ + int res=0; + res = fread(&header->nfiles, sizeof(header->nfiles), 1, fp); + header->nfiles = INTEL_INT(header->nfiles); + res = fread(&header->file_data_offset, sizeof(header->file_data_offset), 1, fp); + header->file_data_offset = INTEL_INT(header->file_data_offset); + + + if (res) + return true; + else + return false; +} +bool ReadHogEntry(FILE *fp, tHogFileEntry *entry) +{ + int res=0; + res = fread(entry->name, sizeof(char), PSFILENAME_LEN+1, fp); + res = fread(&entry->flags, sizeof(entry->flags), 1, fp); + entry->flags = INTEL_INT(entry->flags); + res = fread(&entry->len, sizeof(entry->len), 1, fp); + entry->len = INTEL_INT(entry->len); + res = fread(&entry->timestamp, sizeof(entry->timestamp), 1, fp); + entry->timestamp = INTEL_INT(entry->timestamp); + + if (res) + return true; + else + return false; +} + + +bool WRITE_FILE_ENTRY(FILE *fp, tHogFileEntry *entry) +{ + int res=0; + res = fwrite(entry->name, sizeof(char), PSFILENAME_LEN+1, fp); + res = fwrite(&entry->flags, sizeof(entry->flags), 1, fp); + res = fwrite(&entry->len, sizeof(entry->len), 1, fp); + res = fwrite(&entry->timestamp, sizeof(entry->timestamp), 1, fp); + + if (res) + return true; + else + return false; +} +//////////////////////////////////////////////////////////////////////// +// create new hog file +int NewHogFile(const char *hogname, int nfiles, const char **filenames) +{ + unsigned i; + int table_pos; + FILE *hog_fp; + tHogHeader header; + tHogFileEntry *table; + char ext[_MAX_EXT]; + hogerr_filename[0]='\0'; +// allocate file table + if (nfiles <= 0) + return HOGMAKER_ERROR; + table = new tHogFileEntry[nfiles]; + if (!table) + return HOGMAKER_MEMORY; +// create new file + hog_fp = fopen( hogname, "wb" ); + if ( hog_fp == NULL ) { + delete[] table; + strcpy(hogerr_filename,hogname); + return HOGMAKER_OPENOUTFILE; + } +//write the tag + if (!fwrite(HOG_TAG_STR, strlen(HOG_TAG_STR), 1, hog_fp )) { + delete[] table; + fclose(hog_fp); + strcpy(hogerr_filename,hogname); + return HOGMAKER_OUTFILE; + } +//write number of files + ubyte filler = 0xff; + header.nfiles = (unsigned)nfiles; + header.file_data_offset = strlen(HOG_TAG_STR) + HOG_HDR_SIZE + (sizeof(tHogFileEntry) * header.nfiles); + if (!fwrite(&header.nfiles,sizeof(header.nfiles),1,hog_fp)) { + delete[] table; + fclose(hog_fp); + strcpy(hogerr_filename,hogname); + return HOGMAKER_OUTFILE; + } + if (!fwrite(&header.file_data_offset,sizeof(header.file_data_offset),1,hog_fp)) { + delete[] table; + fclose(hog_fp); + strcpy(hogerr_filename,hogname); + return HOGMAKER_OUTFILE; + } + // write out filler + for(i=0; i < HOG_HDR_SIZE-sizeof(tHogHeader); i++) + if (!fwrite(&filler,sizeof(ubyte),1,hog_fp)) { + delete[] table; + fclose(hog_fp); + strcpy(hogerr_filename,hogname); + return HOGMAKER_OUTFILE; + } +//save file position of index table and write out dummy table + table_pos = strlen(HOG_TAG_STR) + HOG_HDR_SIZE; + memset(&table[0], 0, sizeof(table[0])); + for (i = 0; i < header.nfiles; i++) + { + if (!WRITE_FILE_ENTRY(hog_fp, &table[0])) { + delete[] table; + fclose(hog_fp); + strcpy(hogerr_filename,hogname); + return HOGMAKER_OUTFILE; + } + } +//write files (& build index) + for (i=0;i +#include +#include +#include +#include +#include +#include "CZip.h" + + +BITFILE *CZip::OpenInputBitFile(char *filename) +{ + BITFILE *bit_file; + bit_file = (BITFILE *)malloc(sizeof(BITFILE)); + if(!bit_file) + return NULL; + //bit_file->file = fopen(filename,"rb"); + bit_file->file = VFopen(filename,"rb"); + bit_file->rack = 0; + bit_file->mask = 0x80; + if(!bit_file->file){ + free(bit_file); + return NULL; + } + return (bit_file); +} + +bool fexist(const char * filename ) +{ + FILE *cfp; + + cfp = fopen(filename,"rb"); + + if (!cfp) { //Didn't get file. Why? + if (errno == EACCES) //File exists, but couldn't open it + return true; //..so say it exists on the disk + return false; //Say we didn't find the file + } + return true; +} + +BITFILE *CZip::OpenOutputBitFile(char *filename) +{ + BITFILE *bit_file; + bit_file = (BITFILE *)malloc(sizeof(BITFILE)); + if(!bit_file) + return NULL; + //does file exist? + if(fexist(filename)){ + bit_file->file = VFopen(filename,"r+b"); + }else{ + bit_file->file = VFopen(filename,"wb"); + } + + bit_file->rack = 0; + bit_file->mask = 0x80; + if(!bit_file->file){ + free(bit_file); + return NULL; + } + return (bit_file); +} + +void CZip::CloseInputBitFile(BITFILE *bfile) +{ + //fclose(bfile->file); + int size = VFclose(bfile->file); + free(bfile); +} + +void CZip::CloseOutputBitFile(BITFILE *bfile) +{ + if(bfile->mask != 0x80){ + //if(putc(bfile->rack,bfile->file) != bfile->rack ){ + if(VFputc(bfile->rack,bfile->file) != bfile->rack ){ + //fatal error closing file + } + } + //fclose(bfile->file); + int size = VFclose(bfile->file); + free(bfile); +} + +void CZip::FlushOutputBitFile(BITFILE *bfile) +{ + if(bfile->mask != 0x80 ){ + if(VFputc(bfile->rack,bfile->file) != bfile->rack ){ + //fatal error closing file + } + bfile->rack = 0; + bfile->mask = 0x80; + } +} + +void CZip::OutputBit(BITFILE *bfile,int bit) +{ + if(bit) + bfile->rack |= bfile->mask; + bfile->mask >>= 1; + if(bfile->mask==0){ + //if(putc(bfile->rack,bfile->file)!=bfile->rack){ + if(VFputc(bfile->rack,bfile->file)!=bfile->rack){ + //fatal error + } + bfile->rack = 0; + bfile->mask = 0x80; + } +} + +void CZip::OutputBits(BITFILE *bfile,ulong code,int count) +{ + ulong mask; + mask = 1L << (count-1); + while(mask!=0){ + if(mask&code) + bfile->rack |= bfile->mask; + bfile->mask >>=1; + if(bfile->mask==0){ + //if(putc(bfile->rack,bfile->file)!=bfile->rack){ + if(VFputc(bfile->rack,bfile->file)!=bfile->rack){ + //fatal error + } + bfile->rack = 0; + bfile->mask = 0x80; + } + mask >>=1; + } +} + +int CZip::InputBit(BITFILE *bfile) +{ + int value; + if(bfile->mask==0x80){ + //bfile->rack = getc(bfile->file); + bfile->rack = VFgetc(bfile->file); + if(bfile->rack==EOF){ + //fatal error + } + } + value = bfile->rack&bfile->mask; + bfile->mask >>=1; + if(bfile->mask==0) + bfile->mask = 0x80; + return (value?1:0); +} + +ulong CZip::InputBits(BITFILE *bfile,int bitcount) +{ + ulong mask; + ulong return_value; + + mask = 1L << (bitcount-1); + return_value = 0; + while(mask!=0){ + if(bfile->mask==0x80){ + //bfile->rack = getc(bfile->file); + bfile->rack = VFgetc(bfile->file); + if(bfile->rack==EOF){ + //fatal error + } + } + if(bfile->rack&bfile->mask) + return_value |= mask; + mask >>=1; + bfile->mask >>=1; + if(bfile->mask==0) + bfile->mask = 0x80; + } + return return_value; +} + +void CZip::FilePrintBinary(FILE *file,uint code,int bits) +{ + uint mask; + mask = 1<<(bits-1); + while(mask!=0){ + if(code&mask) + fputc('1',file); + else + fputc('0',file); + mask>>=1; + } +} + +tVirtualFile *CZip::VFopen(char *filename,char *flags,int size) +{ + tVirtualFile *f; + f = (tVirtualFile *)malloc(sizeof(tVirtualFile)); + if(!f) + return NULL; + + if(!filename){ + //open a memory virtual file + f->type = VFT_MEM; + f->count = 0; + f->size = size; + f->file_size = 0; + f->memory = (ubyte *)malloc(size); + if(!f->memory){ + free(f); + return NULL; + } + }else{ + //open a file + + f->type = VFT_FILE; + f->count = 0; + f->size = 0xFFFFFFFF; + f->file = fopen(filename,flags); + if(!f->file){ + free(f); + return NULL; + } + struct _stat st; + _stat(filename,&st); + f->file_size = st.st_size; + + } + return f; +} + +int CZip::VFclose(tVirtualFile *f) +{ + int s = 0; + if(!f) return s; + + s = f->file_size; + + if(f->type){ + //memory + if(f->memory) + free(f->memory); + }else{ + //file + s = fseek(f->file,0,SEEK_END); + if(f->file) + fclose(f->file); + } + free(f); + return s; +} + +int CZip::VFputc(int value,tVirtualFile *file) +{ + int ret = value; + if(file->type){ + //memory + if(file->countsize) + file->memory[file->count] = value; + else + ret = EOF; + }else{ + //disk + ret = fputc(value,file->file); + } + if(ret!=EOF) + file->count++; + if(file->count>file->file_size) + file->file_size = file->count; + return ret; +} + +int CZip::VFgetc(tVirtualFile *file) +{ + int ret = EOF; + if(file->type){ + //memory + if(file->countsize) + ret = file->memory[file->count]; + }else{ + //disk + ret = fgetc(file->file); + } + + if(ret!=EOF) + file->count++; + if(file->count>file->file_size) + file->file_size = file->count; + return ret; +} + +int CZip::VFwrite(void *buf,int size,int count,tVirtualFile *file) +{ + ubyte *buffer = (ubyte *)buf; + if(file->type){ + //memory + int c = __min(count,(file->size-file->count)/size); + for(int i=0;imemory[file->count],buffer,size); + file->count+=size; + buffer+=size; + } + if(file->count>file->file_size) + file->file_size = file->count; + return c; + }else{ + //disk + return fwrite(buffer,size,count,file->file); + } +} + +int CZip::VFread(void *buf,int size,int count,tVirtualFile *file) +{ + ubyte *buffer = (ubyte *)buf; + if(file->type){ + //memory + int c = __min(count,(file->size-file->count)/size); + for(int i=0;imemory[file->count],size); + file->count+=size; + buffer+=size; + } + if(file->count>file->file_size) + file->file_size = file->count; + return c; + }else{ + //disk + return fread(buffer,size,count,file->file); + } +} + +int CZip::VFtell(tVirtualFile *file) +{ + if(file->type) + return file->count; + else + return ftell(file->file); +} + +int CZip::VFseek(tVirtualFile *file,int offset,int origin) +{ + if(file->type){ + switch(origin){ + case SEEK_SET: + if(file->sizecount = offset; + if(file->count>file->file_size) + file->file_size = file->count; + return 0; + break; + case SEEK_CUR: + { + int newpos = file->count + offset; + if( newpos<0 || newpos>=file->size) + return 1; + file->count = newpos; + if(file->count>file->file_size) + file->file_size = file->count; + return 0; + }break; + case SEEK_END: + int newpos = file->file_size - 1 + offset; + if(offset==0 && newpos<0) + newpos = 0; + if( newpos<0 || newpos>=file->size) + return 1; + file->count = newpos; + if(file->count>file->file_size) + file->file_size = file->count; + return 0; + break; + } + return 1; + }else + return fseek(file->file,offset,origin); +} diff --git a/czip/CMakeLists.txt b/czip/CMakeLists.txt new file mode 100644 index 000000000..a2f884399 --- /dev/null +++ b/czip/CMakeLists.txt @@ -0,0 +1,8 @@ +SET (HEADERS ) +SET (CPPS + BitIO.cpp + CZip.cpp + HuffmanAdapt.cpp + HuffmanBasic.cpp) + +ADD_LIBRARY(czip STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/czip/CZip.cpp b/czip/CZip.cpp new file mode 100644 index 000000000..cf1384bbc --- /dev/null +++ b/czip/CZip.cpp @@ -0,0 +1,780 @@ +/* +* $Logfile: /DescentIII/Main/czip/CZip.cpp $ +* $Revision: 2 $ +* $Date: 8/27/98 3:26p $ +* $Author: Jeff $ +* +* General CZip class functions +* +* $Log: /DescentIII/Main/czip/CZip.cpp $ + * + * 2 8/27/98 3:26p Jeff + * intial creation + * + * 1 8/27/98 3:26p Jeff +* +* $NoKeywords: $ +*/ + + + +#include +#include +#include +#include +#include "pserror.h" +#include "Macros.h" +#include "CZip.h" + +CZip::CZip() +{ + bfile = NULL; + file = NULL; + ok_to_raw_write = ok_to_raw_read = false; +} + +CZip::~CZip() +{ +} + +// Opens an archive file for reading +// open_raw = if this is true, then the file MUST be of the raw compressed type. You cannot +// open an OCF by raw. If a raw type archive is opened, you may only use the read +// byte type functions (ReadRaw*()) +// returns true on success +bool CZip::OpenInputArchive(char *filename,bool open_raw) +{ + if(bfile) + return false; + + m_bRawType = open_raw; + bfile = OpenInputBitFile(filename); + if(!bfile) + return false; + + bool ret = false; + + //read in the header based on what type of file it is + if(open_raw){ + //it should be a raw compressed file + ubyte type; + if(ReadRawHeader(bfile->file,&type)){ + ret = true; + m_iCompressionType = type; + PrepareDecompressor(bfile); + } + }else{ + //it should be an OCF file + ubyte type; + tFileInfo temp; + if(ReadOCFHeader(bfile->file,&temp,&type)){ + ret = true; + } + VFseek(bfile->file,0,SEEK_SET); +/* + tFileInfo *info; + int count = GetOCFFileList(bfile,&info); + if(count<=0 || !info) + printf("Unable to get filelist\n"); + else{ + for(int i=0;ifile,compression_type)){ + //everything's ok + ret = true; + } + PrepareCompressor(bfile); + }else{ + //can't do much here, since we don't know what file is going to be compressed yet + ret = true; + } + + if(!ret && bfile!=NULL){ + CloseInputBitFile(bfile); + bfile = NULL; + } + + return ret; +} + +// Reads in a Raw type file header +// returns true on succes, if so, compr_type contains the compression type +bool CZip::ReadRawHeader(tVirtualFile *file,ubyte *compr_type) +{ + int magic_num; + + if(!VFread(&magic_num,sizeof(int),1,file)) + return false; + + if(magic_num!=0x52415743) + return false; + + //it looks good, get our compression type and scram + ubyte type; + if(!VFread(&type,1,1,bfile->file)) + return false; + + //valid compression type? + + *compr_type = type; + return true; +} + +// Writes out a Raw type file header +// returns true on succes +bool CZip::WriteRawHeader(tVirtualFile *file,ubyte compr_type) +{ + int magic_num = 0x52415743; + + //check compression type!? + + if(!VFwrite(&magic_num,sizeof(int),1,file)) + return false; + + if(!VFwrite(&compr_type,1,1,file)) + return false; + + return true; +} + +// Reads in an OCF header +// returns true on success, info is filled in the appropriate values,compr_type is compression type +bool CZip::ReadOCFHeader(tVirtualFile *file,tFileInfo *info,ubyte *compr_type) +{ + int magic_num; + + if(!VFread(&magic_num,sizeof(int),1,file)) + return false; + + if(magic_num!=0x4D495241) + return false; + + //it looks good, its an OCF at least + ubyte version; + if(!VFread(&version,1,1,file)) + return false; + + if(version!=OCF_VERSION) + return false; + + //version is corrent, fill in the rest + ubyte filename_size; + char buffer[300]; + int lo_time,hi_time; + ubyte ctype; + int compressed_size,expand_size; + ubyte last_ditch_value; + + if(!VFread(&filename_size,1,1,file)) + return false; + + if(filename_size<=0) + return false; + + if(!VFread(buffer,1,filename_size,file)) + return false; + buffer[filename_size] = '\0'; + + if(!VFread(&lo_time,sizeof(int),1,file)) + return false; + + if(!VFread(&hi_time,sizeof(int),1,file)) + return false; + + if(!VFread(&ctype,1,1,file)) + return false; + + if(!VFread(&compressed_size,sizeof(int),1,file)) + return false; + + if(!VFread(&expand_size,sizeof(int),1,file)) + return false; + + if(!VFread(&last_ditch_value,1,1,file)) + return false; + + if(last_ditch_value!=0x01) + return false; + + //everything went ok, *whew* + *compr_type = ctype; + strcpy(info->filename,buffer); + info->lo_time = lo_time; + info->hi_time = hi_time; + info->compressed_size = compressed_size; + info->expanded_size = expand_size; + return true; +} + + +// Writes out an OCF header +// returns true on success +bool CZip::WriteOCFHeader(tVirtualFile *file,tFileInfo *info,ubyte compr_type,int header_pos) +{ + int magic_num = 0x4D495241; + ubyte filename_size; + ubyte last_ditch_value; + ubyte version; + bool ret = false; + + int filepos = VFtell(file); //save the position of the file restorfilepos: + VFseek(file,header_pos,SEEK_SET); //get to the begining of the header + + if(!VFwrite(&magic_num,sizeof(int),1,file)) + goto restorfilepos; + + version = OCF_VERSION; + if(!VFwrite(&version,1,1,file)) + goto restorfilepos; + + //version is corrent, fill in the rest + filename_size = strlen(info->filename); + last_ditch_value = 0x01; + + if(!VFwrite(&filename_size,1,1,file)) + goto restorfilepos; + + if(!VFwrite(info->filename,1,filename_size,file)) + goto restorfilepos; + + if(!VFwrite(&info->lo_time,sizeof(int),1,file)) + goto restorfilepos; + + if(!VFwrite(&info->hi_time,sizeof(int),1,file)) + goto restorfilepos; + + if(!VFwrite(&compr_type,1,1,file)) + goto restorfilepos; + + if(!VFwrite(&info->compressed_size,sizeof(int),1,file)) + goto restorfilepos; + + if(!VFwrite(&info->expanded_size,sizeof(int),1,file)) + goto restorfilepos; + + if(!VFwrite(&last_ditch_value,1,1,file)) + goto restorfilepos; + + ret = true; +restorfilepos: + VFseek(file,filepos,SEEK_SET); //restore the file pos + return ret; +} + +// Writes out a 'dummy' OCF header, just give what the final filename is +// you must call this before you compress data, then when done, call the read WriteOCFHeader +bool CZip::WriteDummyOCFHeader(tVirtualFile *file,char *filename) +{ + int itemp = 0; + ubyte btemp = 0; + + if(!VFwrite(&itemp,sizeof(int),1,file)) + return false; + + if(!VFwrite(&btemp,1,1,file)) + return false; + + //version is corrent, fill in the rest + ubyte filename_size = strlen(filename); + + if(!VFwrite(&filename_size,1,1,file)) + return false; + + if(!VFwrite(filename,1,filename_size,file)) + return false; + + if(!VFwrite(&itemp,sizeof(int),2,file)) + return false; + + if(!VFwrite(&btemp,1,1,file)) + return false; + + if(!VFwrite(&itemp,sizeof(int),2,file)) + return false; + + if(!VFwrite(&btemp,1,1,file)) + return false; + return true; +} + +// Prepares one of the decompressors for raw reading +void CZip::PrepareDecompressor(BITFILE *bfile) +{ + switch(m_iCompressionType){ + case COMPT_HUFFADAPT_0: + ha_PrepareDecompress(); + break; + default: + Int3(); + break; + }; +} + +// Prepares one of the compressors for raw reading +void CZip::PrepareCompressor(BITFILE *bfile) +{ + switch(m_iCompressionType){ + case COMPT_HUFFADAPT_0: + ha_PrepareCompress(); + break; + default: + Int3(); + break; + }; +} + +// Closes an archive that was opened for input +void CZip::CloseInputArchive(void) +{ + if(m_bRawType){ + switch(m_iCompressionType){ + case COMPT_HUFFADAPT_0: + ha_CloseRawDecompress(); + break; + default: + Int3(); + break; + }; + } + + //any more work to do? + if(bfile) + CloseInputBitFile(bfile); + bfile = NULL; +} + +// Closes an archive that was opened for output +void CZip::CloseOutputArchive(void) +{ + //if we have an OCF file, write the real header out + if(!m_bRawType){ + //write out an int of 0 (signifing EOF) + int fake_magic = 0; + VFseek(bfile->file,0,SEEK_END); + VFwrite(&fake_magic,sizeof(int),1,bfile->file); + }else{ + //since we are writing a raw, we need to finish it up + switch(m_iCompressionType){ + case COMPT_HUFFADAPT_0: + ha_CloseRawCompress(bfile); + break; + default: + Int3(); + break; + }; + } + + if(bfile) + CloseOutputBitFile(bfile); + bfile = NULL; +} + +// Adds a given file to the archive +// You may only add files to OCF files (you passed false for open_raw when you opened) +// compression_type = what type of compression you want to use +bool CZip::AddFileToArchive(char *filename,int compression_type) +{ + if(!bfile) + return false; + if(m_bRawType) + return false; + + file = VFopen(filename,"rb"); + if(!file) + return false; + + //Add to the end of the file, so we need to move to the end, back up sizeof(int) to remove + //header + VFseek(bfile->file,0-sizeof(int),SEEK_END); + + int compressed_size = -1; + int header_start = VFtell(bfile->file); + + switch(compression_type){ + case COMPT_NONE: + case COMPT_SHANFANO: + VFclose(file); + return false; + break; + case COMPT_HUFFBAS: + WriteDummyOCFHeader(bfile->file,filename); + compressed_size = hb_CompressFile(file,bfile); + break; + case COMPT_HUFFADAPT_0: + WriteDummyOCFHeader(bfile->file,filename); + compressed_size = ha_CompressFile(file,bfile); + break; + default: + VFclose(file); + return false; + }; + + if(compressed_size==-1){ + VFclose(file); + return false; + } + + //fill in the info + struct _stat st; + + strcpy(current_file.filename,filename); + _stat(filename,&st); + current_file.expanded_size = st.st_size; + VFclose(file); + + current_file.compressed_size = compressed_size; + //TODO: Get FileTimes + current_file.lo_time = current_file.hi_time = 0; + + m_iCompressionType = compression_type; + + WriteOCFHeader(bfile->file,¤t_file,m_iCompressionType,header_start); + + return true; +} + +// Extracts a file from an OCF archive +// filename = The filename specified within the archive +// destfilename = The output filename (what it should get extracted to) pass NULL if it +// is the same as the original filename +bool CZip::ExtractFileFromArchive(char *filename,char *destfilename) +{ + if(!bfile) + return false; + if(m_bRawType) + return false; + + file = VFopen((destfilename)?destfilename:filename,"wb"); + if(!file) + return false; + + int pos = FindFileInOCF(bfile->file,filename); + if(pos==-1) + return false; + VFseek(bfile->file,pos,SEEK_SET); + + //read in header + ubyte compr_type; + if(!ReadOCFHeader(bfile->file,¤t_file,&compr_type)) + return false; + + m_iCompressionType = compr_type; + + switch(m_iCompressionType){ + case COMPT_NONE: + case COMPT_SHANFANO: + VFclose(file); + return false; + break; + case COMPT_HUFFBAS: + hb_ExpandFile(bfile,file); + break; + case COMPT_HUFFADAPT_0: + ha_ExpandFile(bfile,file); + break; + default: + VFclose(file); + return false; + }; + + VFclose(file); + return true; +} + +// Searches through the opened OCF looking for filename, returns the file position +// of the file if found, or -1 if not. +int CZip::FindFileInOCF(tVirtualFile *file,char *filename) +{ + int old_pos = VFtell(file); + VFseek(file,0,SEEK_SET); //go to the beginning of the file + + int magic_num,pos; + ubyte version,filename_size; + ubyte buffer[300]; + bool done = false; + int compsize; + + while(!done){ + //make sure we have an OCF + pos = VFtell(file); + VFread(&magic_num,sizeof(int),1,file); + if(magic_num!=0x4D495241){ + //we don't have an OCF, or end of file + VFseek(file,old_pos,SEEK_SET); + return -1; + } + VFread(&version,1,1,file); + VFread(&filename_size,1,1,file); + VFread(buffer,1,filename_size,file); + buffer[filename_size] = '\0'; + if(!stricmp(filename,(char *)buffer)){ + //we found it...back up a couple bytes + VFseek(file,old_pos,SEEK_SET); + return pos; + } + + //no luck with this file, move to the header of the next + VFseek(file,9,SEEK_CUR); //get to the compressed size + VFread(&compsize,sizeof(int),1,file); //we need this + VFseek(file,5+compsize,SEEK_CUR); //move 5 bytes (rest of header) + data + } + + + VFseek(file,old_pos,SEEK_SET); + return -1; //couldn't find it +} + +// Gets a list of files in the OCF, make sure you free up the array after use +// returns the number of files in the OCF +// 0 or -1 on error (-1 is out of memory) +int CZip::GetFileList(tFileInfo **info) +{ + if(m_bRawType) + return 0; + if(!bfile) + return 0; + return GetOCFFileList(bfile,info); +} + +// Gets a list of files in the OCF, make sure you free up the array after use +// returns the number of files in the OCF +// 0 or -1 on error (-1 is out of memory) +int CZip::GetOCFFileList(BITFILE *bfile,tFileInfo **info) +{ + int count = 0; + + tVirtualFile *file = bfile->file; + + //first find out how many files there are + int old_pos = VFtell(file); + VFseek(file,0,SEEK_SET); //go to the beginning of the file + + int magic_num,compsize; + ubyte filename_size; + bool done = false; + + while(!done){ + //make sure we have an OCF + VFread(&magic_num,sizeof(int),1,file); + if(magic_num!=0x4D495241){ + //we don't have an OCF, or end of file + break; + } + VFseek(file,1,SEEK_CUR); + VFread(&filename_size,1,1,file); + VFseek(file,filename_size+9,SEEK_CUR); + VFread(&compsize,sizeof(int),1,file); //we need this + VFseek(file,5+compsize,SEEK_CUR); //move 5 bytes (rest of header) + data + count++; + } + + if(!count){ + VFseek(file,old_pos,SEEK_SET); + return 0; + } + *info = (tFileInfo *)malloc(sizeof(tFileInfo)*count); + if(!info){ + VFseek(file,old_pos,SEEK_SET); + return -1; + } + + tFileInfo *files; + files = *info; + + //now go back through and get the files + VFseek(file,0,SEEK_SET); + + for(int index=0;index>8); + WriteRawByte(data); + data = (value&0xFF); + WriteRawByte(data); +} +void CZip::WriteRawInt(uint value) +{ + if(!m_bRawType) + return; + //write 4 bytes + ubyte data; + data = ((value&0xFF000000)>>24); + WriteRawByte(data); + data = ((value&0xFF0000)>>16); + WriteRawByte(data); + data = ((value&0xFF00)>>8); + WriteRawByte(data); + data = (value&0xFF); + WriteRawByte(data); +} +void CZip::WriteRawFloat(float value) +{ + if(!m_bRawType) + return; + //write 4 bytes + ubyte data; + uint v = 0; + v = *((int *)&value); + + data = ((v&0xFF000000)>>24); + WriteRawByte(data); + data = ((v&0xFF0000)>>16); + WriteRawByte(data); + data = ((v&0xFF00)>>8); + WriteRawByte(data); + data = (v&0xFF); + WriteRawByte(data); +} + +bool CZip::RawEOF(void) +{ + if(!m_bRawType) + return true; + + return !(ok_to_raw_read); +} diff --git a/czip/HuffmanAdapt.cpp b/czip/HuffmanAdapt.cpp new file mode 100644 index 000000000..29eb732d5 --- /dev/null +++ b/czip/HuffmanAdapt.cpp @@ -0,0 +1,298 @@ +/* +* $Logfile: /DescentIII/Main/czip/HuffmanAdapt.cpp $ +* $Revision: 2 $ +* $Date: 8/27/98 3:26p $ +* $Author: Jeff $ +* +* Huffman-Adaptive compression functions +* +* $Log: /DescentIII/Main/czip/HuffmanAdapt.cpp $ + * + * 2 8/27/98 3:26p Jeff + * intial creation + * + * 1 8/27/98 3:26p Jeff +* +* $NoKeywords: $ +*/ + + + +#include +#include +#include +#include +#include "CZip.h" + +#define END_OF_STREAM 256 +#define ESCAPE 257 +#define ROOT_NODE 0 +#define MAX_WEIGHT 0x8000 + + +//void calculate_rows(tHATree *tree,int node,int level); +//int calculate_columns(tHATree *tree,int node,int starting_guess); +//int find_minimum_column(tHATree *tree,int node,int max_row); +//void rescale_columns(int factor); + +///////////////////////////////////////////// +int CZip::ha_CompressFile(tVirtualFile *input,BITFILE *output) +{ + int original_pos = VFtell(output->file); + int c; + ha_InitializeTree(&Tree); + while((c=VFgetc(input))!=EOF){ + ha_EncodeSymbol(&Tree,c,output); + ha_UpdateModel(&Tree,c); + } + ha_EncodeSymbol(&Tree,END_OF_STREAM,output); + FlushOutputBitFile(output); + int size = VFtell(output->file) - original_pos; + return size; +} + +void CZip::ha_PrepareCompress(void) +{ + ok_to_raw_write = true; + ha_InitializeTree(&Tree); +} + +void CZip::ha_WriteRawByte(ubyte data,BITFILE *output) +{ + if(!ok_to_raw_write) + return; + ha_EncodeSymbol(&Tree,data,output); + ha_UpdateModel(&Tree,data); +} + +void CZip::ha_CloseRawCompress(BITFILE *output) +{ + ha_EncodeSymbol(&Tree,END_OF_STREAM,output); + FlushOutputBitFile(output); + ok_to_raw_write = false; +} + +void CZip::ha_ExpandFile(BITFILE *input,tVirtualFile *output) +{ + int c; + ha_InitializeTree(&Tree); + while((c=ha_DecodeSymbol(&Tree,input))!=END_OF_STREAM){ + if(VFputc(c,output)==EOF){ + //fatal error + } + ha_UpdateModel(&Tree,c); + } +} + +void CZip::ha_PrepareDecompress(void) +{ + ok_to_raw_read = true; + ha_InitializeTree(&Tree); +} + +bool CZip::ha_ReadRawByte(ubyte *data,BITFILE *input) +{ + if(!ok_to_raw_read) + return false; + + int c; + c = ha_DecodeSymbol(&Tree,input); + + if(c==END_OF_STREAM){ + ok_to_raw_read =false; + return false; + } + ha_UpdateModel(&Tree,c); + *data = c; + return true; +} + +void CZip::ha_CloseRawDecompress(void) +{ + ok_to_raw_read = false; +} + + +void CZip::ha_InitializeTree(tHATree *tree) +{ + int i; + + tree->nodes[ROOT_NODE].child = ROOT_NODE + 1; + tree->nodes[ROOT_NODE].child_is_leaf = false; + tree->nodes[ROOT_NODE].weight = 2; + tree->nodes[ROOT_NODE].parent = -1; + + tree->nodes[ROOT_NODE+1].child = END_OF_STREAM; + tree->nodes[ROOT_NODE+1].child_is_leaf = true; + tree->nodes[ROOT_NODE+1].weight = 1; + tree->nodes[ROOT_NODE+1].parent = ROOT_NODE; + tree->leaf[END_OF_STREAM] = ROOT_NODE+1; + + tree->nodes[ROOT_NODE+2].child = ESCAPE; + tree->nodes[ROOT_NODE+2].child_is_leaf = true; + tree->nodes[ROOT_NODE+2].weight = 1; + tree->nodes[ROOT_NODE+2].parent = ROOT_NODE; + tree->leaf[ESCAPE] = ROOT_NODE+2; + tree->next_free_node = ROOT_NODE+3; + for(i=0;ileaf[i] = -1; +} + +void CZip::ha_EncodeSymbol(tHATree *tree,uint c,BITFILE *output) +{ + ulong code; + ulong current_bit; + int code_size; + int current_node; + + code = 0; + current_bit = 1; + code_size = 0; + current_node = tree->leaf[c]; + if(current_node==-1) + current_node = tree->leaf[ESCAPE]; + while(current_node!=ROOT_NODE){ + if((current_node&1)==0) + code |= current_bit; + current_bit <<= 1; + code_size++; + current_node = tree->nodes[current_node].parent; + } + OutputBits(output,code,code_size); + if(tree->leaf[c]==-1){ + OutputBits(output,(ulong)c,8); + ha_add_new_node(tree,c); + } +} + + +int CZip::ha_DecodeSymbol(tHATree *tree,BITFILE *input) +{ + int current_node; + int c; + + current_node = ROOT_NODE; + while(!tree->nodes[current_node].child_is_leaf){ + current_node = tree->nodes[current_node].child; + current_node += InputBit(input); + } + c = tree->nodes[current_node].child; + if(c==ESCAPE){ + c = (int)InputBits(input,8); + ha_add_new_node(tree,c); + } + return c; +} + +void CZip::ha_UpdateModel(tHATree *tree,int c) +{ + int current_node; + int new_node; + + if(tree->nodes[ROOT_NODE].weight==MAX_WEIGHT) + ha_RebuildTree(tree); + current_node = tree->leaf[c]; + while(current_node!=-1){ + tree->nodes[current_node].weight++; + for(new_node = current_node;new_node>ROOT_NODE;new_node--){ + if(tree->nodes[new_node-1].weight >= tree->nodes[current_node].weight) + break; + } + if(current_node!=new_node){ + ha_swap_nodes(tree,current_node,new_node); + current_node = new_node; + } + current_node = tree->nodes[current_node].parent; + } +} + +void CZip::ha_RebuildTree(tHATree *tree) +{ + int i,j,k; + uint weight; + j = tree->next_free_node - 1; + for(i=j;i>=ROOT_NODE;i--){ + if(tree->nodes[i].child_is_leaf){ + tree->nodes[j] = tree->nodes[i]; + tree->nodes[j].weight = (tree->nodes[j].weight+1)/2; + j--; + } + } + + for(i=tree->next_free_node-2;j>=ROOT_NODE;i-=2,j--){ + k = i+1; + tree->nodes[j].weight = tree->nodes[i].weight+tree->nodes[k].weight; + weight = tree->nodes[j].weight; + tree->nodes[j].child_is_leaf = false; + for(k=j+1;weightnodes[k].weight;k++); + k--; + memmove(&tree->nodes[j],&tree->nodes[j+1],(k-j)*sizeof(tHANode)); + tree->nodes[k].weight = weight; + tree->nodes[k].child = i; + tree->nodes[k].child_is_leaf = false; + } + + for(i=tree->next_free_node-1;i>=ROOT_NODE;i--){ + if(tree->nodes[i].child_is_leaf){ + k = tree->nodes[i].child; + tree->leaf[k] = i; + }else{ + k = tree->nodes[i].child; + tree->nodes[k].parent = tree->nodes[k+1].parent = i; + } + } +} + +void CZip::ha_swap_nodes(tHATree *tree,int i,int j) +{ + tHANode temp; + + if(tree->nodes[i].child_is_leaf) + tree->leaf[tree->nodes[i].child] = j; + else{ + tree->nodes[tree->nodes[i].child].parent = j; + tree->nodes[tree->nodes[i].child+1].parent = j; + } + + if(tree->nodes[j].child_is_leaf) + tree->leaf[tree->nodes[j].child] = i; + else{ + tree->nodes[tree->nodes[j].child].parent = i; + tree->nodes[tree->nodes[j].child+1].parent = i; + } + + temp = tree->nodes[i]; + tree->nodes[i] = tree->nodes[j]; + tree->nodes[i].parent = temp.parent; + temp.parent = tree->nodes[j].parent; + tree->nodes[j] = temp; +} + +void CZip::ha_add_new_node(tHATree *tree,int c) +{ + int lightest_node,new_node,zero_weight_node; + + lightest_node = tree->next_free_node - 1; + new_node = tree->next_free_node; + zero_weight_node = tree->next_free_node + 1; + tree->next_free_node += 2; + + tree->nodes[new_node] = tree->nodes[lightest_node]; + tree->nodes[new_node].parent = lightest_node; + tree->leaf[tree->nodes[new_node].child] = new_node; + + tree->nodes[lightest_node].child = new_node; + tree->nodes[lightest_node].child_is_leaf = false; + + tree->nodes[zero_weight_node].child = c; + tree->nodes[zero_weight_node].child_is_leaf = true; + tree->nodes[zero_weight_node].weight = 0; + tree->nodes[zero_weight_node].parent = lightest_node; + tree->leaf[c] = zero_weight_node; + +} + +//void calculate_rows(tHATree *tree,int node,int level); +//int calculate_columns(tHATree *tree,int node,int starting_guess); +//int find_minimum_column(tHATree *tree,int node,int max_row); +//void rescale_columns(int factor); \ No newline at end of file diff --git a/czip/HuffmanBasic.cpp b/czip/HuffmanBasic.cpp new file mode 100644 index 000000000..55c086af4 --- /dev/null +++ b/czip/HuffmanBasic.cpp @@ -0,0 +1,268 @@ +/* +* $Logfile: /DescentIII/Main/czip/HuffmanBasic.cpp $ +* $Revision: 2 $ +* $Date: 8/27/98 3:26p $ +* $Author: Jeff $ +* +* Basic Huffman compression functions +* +* $Log: /DescentIII/Main/czip/HuffmanBasic.cpp $ + * + * 2 8/27/98 3:26p Jeff + * intial creation + * + * 1 8/27/98 3:26p Jeff +* +* $NoKeywords: $ +*/ + + + +#include +#include +#include +#include +#include "CZip.h" + +#define END_OF_STREAM 256 + +int CZip::hb_CompressFile(tVirtualFile *input,BITFILE *output) +{ + ulong *counts; + tH0Node *nodes; + tH0Code *codes; + int root_node; + + int original_pos = VFtell(output->file); + + counts = (ulong *) malloc(256*sizeof(ulong)); + if(!counts) + return -1; + if((nodes = (tH0Node *) malloc(514*sizeof(tH0Node)))==NULL){ + free(counts); + return -1; + } + if((codes = (tH0Code *) malloc( 257*sizeof(tH0Code)))==NULL){ + free(counts); + free(nodes); + return -1; + } + + memset(counts,0,256*sizeof(ulong)); + memset(nodes,0,514*sizeof(tH0Node)); + memset(codes,0,257*sizeof(tH0Code)); + + hb_count_bytes( input, counts ); + hb_scale_counts( counts, nodes ); + hb_output_counts( output, nodes ); + root_node = hb_build_tree( nodes ); + hb_convert_tree_to_code( nodes, codes, 0, 0, root_node ); + hb_compress_data( input, output, codes ); + free( counts ); + free( nodes ); + free( codes ); + FlushOutputBitFile(output); + + int compsize = VFtell(output->file) - original_pos; + return compsize; +} + +bool CZip::hb_ExpandFile(BITFILE *input,tVirtualFile *output) +{ + tH0Node *nodes; + int root_node; + + if ((nodes = (tH0Node *) malloc( 514*sizeof(tH0Node)))==NULL) + return false; + memset(nodes,0,sizeof(tH0Node)*514); + hb_input_counts( input, nodes ); + root_node = hb_build_tree( nodes ); + hb_expand_data( input, output, nodes, root_node ); + free(nodes); + return true; +} + +void CZip::hb_output_counts(BITFILE *output,tH0Node *nodes) +{ + int first,last,next,i; + + first = 0; + while ( first < 255 && nodes[ first ].count == 0 ) + first++; + + for ( ; first < 256 ; first = next ) { + last = first + 1; + for ( ; ; ) { + for ( ; last < 256 ; last++ ) + if ( nodes[ last ].count == 0 ) + break; + last--; + for ( next = last + 1; next < 256 ; next++ ) + if ( nodes[ next ].count != 0 ) + break; + if ( next > 255 ) + break; + if ( ( next - last ) > 3 ) + break; + last = next; + } + + if ( VFputc( first, output->file ) != first ){ + //fatal error + } + if ( VFputc( last, output->file ) != last ){ + //fatal error + } + for ( i = first ; i <= last ; i++ ) { + if ( VFputc( nodes[ i ].count, output->file ) !=(int) nodes[ i ].count ){ + //fatal error + } + } + } + if ( VFputc( 0, output->file ) != 0 ){ + //fatal error + } +} + +void CZip::hb_input_counts(BITFILE *input,tH0Node *nodes) +{ + int first; + int last; + int i; + int c; + + for ( i = 0 ; i < 256 ; i++ ) + nodes[ i ].count = 0; + if ( ( first = VFgetc( input->file ) ) == EOF ){ + // fatal_error( "Error reading byte counts\n" ); + } + if ( ( last = VFgetc( input->file ) ) == EOF ){ + //fatal_error( "Error reading byte counts\n" ); + } + for ( ; ; ) { + for ( i = first ; i <= last ; i++ ) + if ( ( c = VFgetc( input->file ) ) == EOF ){ + // fatal_error( "Error reading byte counts\n" ); + } else + nodes[ i ].count = (unsigned int) c; + if ( ( first = VFgetc( input->file ) ) == EOF ){ + // fatal_error( "Error reading byte counts\n" ); + } + if ( first == 0 ) + break; + if ( ( last = VFgetc( input->file ) ) == EOF ){ + // fatal_error( "Error reading byte counts\n" ); + } + } + nodes[ END_OF_STREAM ].count = 1; +} + +void CZip::hb_count_bytes(tVirtualFile *input,ulong *counts) +{ + long input_marker; + int c; + + input_marker = VFtell( input ); + while ( ( c = VFgetc( input )) != EOF ) + counts[ c ]++; + VFseek( input, input_marker, SEEK_SET ); +} + +void CZip::hb_scale_counts(ulong *counts,tH0Node *nodes) +{ + ulong max_count; + int i; + + max_count = 0; + for ( i = 0 ; i < 256 ; i++ ) + if ( counts[ i ] > max_count ) + max_count = counts[ i ]; + if ( max_count == 0 ) { + counts[ 0 ] = 1; + max_count = 1; + } + max_count = max_count / 255; + max_count = max_count + 1; + for ( i = 0 ; i < 256 ; i++ ) { + nodes[ i ].count = (uint) ( counts[ i ] / max_count ); + if ( nodes[ i ].count == 0 && counts[ i ] != 0 ) + nodes[ i ].count = 1; + } + nodes[ END_OF_STREAM ].count = 1; +} + +int CZip::hb_build_tree(tH0Node *nodes) +{ + int next_free; + int i; + int min_1; + int min_2; + + nodes[ 513 ].count = 0xffff; + for ( next_free = END_OF_STREAM + 1 ; ; next_free++ ) { + min_1 = 513; + min_2 = 513; + for ( i = 0 ; i < next_free ; i++ ) + if ( nodes[ i ].count != 0 ) { + if ( nodes[ i ].count < nodes[ min_1 ].count ) { + min_2 = min_1; + min_1 = i; + } else if ( nodes[ i ].count < nodes[ min_2 ].count ) + min_2 = i; + } + if ( min_2 == 513 ) + break; + + nodes[ next_free ].count = nodes[ min_1 ].count+ nodes[ min_2 ].count; + nodes[ min_1 ].saved_count = nodes[ min_1 ].count; + nodes[ min_1 ].count = 0; + nodes[ min_2 ].saved_count = nodes[ min_2 ].count; + nodes[ min_2 ].count = 0; + nodes[ next_free ].child0 = min_1; + nodes[ next_free ].child1 = min_2; + } + next_free--; + nodes[ next_free ].saved_count = nodes[ next_free ].count; + return( next_free ); +} + +void CZip::hb_convert_tree_to_code(tH0Node *nodes,tH0Code *codes,uint code_so_far,int bits,int node) +{ + if ( node <= END_OF_STREAM ) { + codes[ node ].code = code_so_far; + codes[ node ].code_bits = bits; + return; + } + + code_so_far <<= 1; + bits++; + hb_convert_tree_to_code( nodes, codes, code_so_far, bits, nodes[ node ].child0 ); + hb_convert_tree_to_code( nodes, codes, code_so_far | 1,bits, nodes[ node ].child1 ); +} + +void CZip::hb_compress_data(tVirtualFile *input,BITFILE *output,tH0Code *codes) +{ + int c; + while((c=VFgetc(input))!=EOF) + OutputBits(output,(ulong)codes[c].code,codes[c].code_bits); + OutputBits(output,(ulong)codes[END_OF_STREAM].code,codes[END_OF_STREAM].code_bits); +} + +void CZip::hb_expand_data(BITFILE *input,tVirtualFile *output,tH0Node *nodes,int root_node) +{ + int node; + for(;;){ + node = root_node; + do{ + if(InputBit(input)) + node = nodes[node].child1; + else + node = nodes[node].child0; + }while(node>END_OF_STREAM); + if(node==END_OF_STREAM) + break; + if((VFputc(node,output))!=node){ + //fatal error + } + } +} \ No newline at end of file diff --git a/d3music/CMakeLists.txt b/d3music/CMakeLists.txt new file mode 100644 index 000000000..741b3e9e3 --- /dev/null +++ b/d3music/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (HEADERS ) +SET (CPPS + musicapi.cpp) + +ADD_LIBRARY(d3music STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/d3music/musicapi.cpp b/d3music/musicapi.cpp new file mode 100644 index 000000000..57746ff67 --- /dev/null +++ b/d3music/musicapi.cpp @@ -0,0 +1,504 @@ +#include "d3music.h" +#include "music.h" +#include "Macros.h" +#include + +#ifdef min +# undef min +#endif + +#ifdef max +# undef max +#endif + +// generic constants +#define MUSIC_IDLE_TIME 240.0f // how many seconds before background music silences. + +// flags for musicAI +#define MUSICAIF_PLAYERDEAD 0x1 +#define MUSICAIF_PLAYERINVUL 0x2 +#define MUSICAIF_STARTLEVEL 0x4 // game started a new level +#define MUSICAIF_HOSTILES 0x8 // there are hostiles around +#define MUSICAIF_NEWREGION 0x40 + +// names of themes. (match to music.h THEME_TYPES) +const char *Music_type_names[] = +{ + "No song", + "Intro song", + "Idle song", + "Combat song", + "Death song", + "Idle-Combat song", + "Combat-Idle song" +}; + +// global data +#ifdef _DEBUG +bool Music_debug_verbose = false; // debug flag for music system +#endif + +// in library data. +static OutrageMusicSeq Music_seq; +static bool Music_on = false; +static bool Allow_music = false; +static float Music_volume = 1.0f; + +// music ai structure. +static struct +{ + float peace_timer; // how long has player been out of combat. + int flags; // current state of music AI. + int cur_song; // current song + int n_hostiles; // number of hostiles ai thinks there are. + tMusicVal trigger; + short pending_region; // pending region change. + bool was_toggled_on; // music was turned on by user. + bool immediate_switch; // force switch immediately for regions + bool restart_sequencer; // restarts sequencer next frame. +}MusicAI; + + +// handles operations on current events. +void D3MusicSongSelector(); + +// creates music ai struction from music info passed in. +void D3MusicAI(tMusicSeqInfo *music_info); + + +/////////////////////////////////////////////////////////////////////////////// + +void InitD3Music(bool allow_music) +{ + Music_on = false; + Allow_music = allow_music; +} + + +void CloseD3Music() +{ + Music_on = false; +} + + +// is music system on? +bool IsD3MusicOn() +{ + return Music_on; +} + + +// starts up the music sequencer +void D3MusicStart(const char *theme_file) +{ + if (theme_file && Music_seq.Init(theme_file)) { + Music_on = true; + Music_seq.SetVolume(Music_volume); + } + else { + Music_on = false; + } + + if (!Allow_music) { + Music_on = false; + } + + if (Music_on) + { + if (Music_volume > 0.0f) + { + Music_seq.Start(); + } + else + { + Music_on = false; // volume was 0.0f, so don't mark music as on since we don't start + } + } + +// reset AI state. + MusicAI.cur_song = OMS_THEME_TYPE_NONE; +//@@ MusicAI.mood_default = MUSIC_MOOD_DEFAULT; +//@@ MusicAI.mood = MUSIC_MOOD_DEFAULT; + MusicAI.flags = 0; + MusicAI.n_hostiles = 0; + MusicAI.was_toggled_on = false; + MusicAI.pending_region = -1; + MusicAI.restart_sequencer = false; + MusicAI.immediate_switch = false; + D3MusicSetRegion(0); + +// reset AI timers + MusicAI.peace_timer = 0.0f; +} + + +// stops the music sequencer +void D3MusicStop() +{ + Music_seq.Stop(); + Music_seq.Shutdown(); + Music_on = false; +} + + +// execute music sequencer. +void D3MusicDoFrame(tMusicSeqInfo *music_info) +{ + // update music ai with new music info + if (!Music_on) + return; + + D3MusicAI(music_info); + +// handle current events. + D3MusicSongSelector(); + +// run frame. + Music_seq.Frame(music_info->frametime); + +// end + music_info->cur_loop_name = Music_seq.GetCurrentLoopName(&music_info->cur_loop_count); + music_info->cur_song = MusicAI.cur_song; +} + + +// toggle music system. +void D3MusicToggle() +{ + Music_on = (!Music_on && Allow_music && Music_volume > 0.0f) ? true : false; + if (Music_on) + { + Music_seq.Start(); + MusicAI.was_toggled_on = true; + } + else + { + Music_seq.Stop(); + MusicAI.was_toggled_on = false; + } +} + + +// toggle music system. +void D3MusicToggle(bool state) +{ + if (state && !Music_on && Allow_music && Music_volume > 0.0f) + { + Music_seq.Start(); + MusicAI.was_toggled_on = true; + Music_on = true; + } + else if (!state && Music_on) { + Music_seq.Stop(); + MusicAI.was_toggled_on = false; + Music_on = false; + } +} + + +// pauses and or resumes music +void D3MusicPause() +{ + Music_seq.Pause(); +} + + +void D3MusicResume() +{ + Music_seq.Resume(); +} + + +// volume stuff +float D3MusicGetVolume() +{ + return Music_volume; +} + + +void D3MusicSetVolume(float vol) +{ + const float kEpsilon = std::numeric_limits::min(); + if( vol <= kEpsilon ) + { + Music_seq.Stop(); + MusicAI.was_toggled_on = false; + Music_on = false; + } + else if( Music_volume <= kEpsilon ) + { + Music_on = true; + MusicAI.was_toggled_on = true; + if (MusicAI.pending_region == -1) + { + MusicAI.pending_region = D3MusicGetRegion(); + } + Music_seq.Start(); + } + Music_seq.SetVolume(vol); + Music_volume = vol; +} + + +// New Interface +// Sequencing within level. +// play intro track. +// play main background music. +// occasionally switch to other ambient music options. +// when hostility towards player is upped, play 'hostile' music. +// when player enters combat (player takes damage from hostile), switch to combat music. +// fade out background music after 2-3 minutes if no change in state. +// + +// creates music ai struction from music info passed in. +void D3MusicAI(tMusicSeqInfo *music_info) +{ +// determine flags state. + if (music_info->player_dead) { + MusicAI.flags |= MUSICAIF_PLAYERDEAD; + } + else { + MusicAI.flags &= (~MUSICAIF_PLAYERDEAD); + } + + if (music_info->started_level) { + MusicAI.flags |= MUSICAIF_STARTLEVEL; + MusicAI.peace_timer = 0.0f; + } + else { + MusicAI.flags &= (~MUSICAIF_STARTLEVEL); + } + + if (music_info->n_hostiles && !(MusicAI.flags & MUSICAIF_HOSTILES)) { + MusicAI.flags |= MUSICAIF_HOSTILES; + MusicAI.peace_timer = 0.0f; + } + else if (!music_info->n_hostiles && (MusicAI.flags & MUSICAIF_HOSTILES)) { + // make sure hostiles flags is cleared once. when no hostiles, but hostiles flag is on. + MusicAI.flags &= (~MUSICAIF_HOSTILES); + MusicAI.peace_timer = 0.0f; +//@@ MusicAI.mood_timer = 0.0f; + } + +//@@// set mood timers +//@@ if (!CHECK_FLAG(MusicAI.flags, MUSICAIF_NEGMOODTIMER) && MusicAI.mood < 0) { +//@@ MusicAI.flags |= MUSICAIF_NEGMOODTIMER; +//@@ MusicAI.negmood_timer = 0.0f; +//@@ } +//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_NEGMOODTIMER) && MusicAI.mood >=0) { +//@@ MusicAI.flags &= (~MUSICAIF_NEGMOODTIMER); +//@@ MusicAI.negmood_timer = 0.0f; +//@@ } +//@@ if (!CHECK_FLAG(MusicAI.flags, MUSICAIF_POSMOODTIMER) && MusicAI.mood > 0) { +//@@ MusicAI.flags |= MUSICAIF_POSMOODTIMER; +//@@ MusicAI.posmood_timer = 0.0f; +//@@ } +//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_POSMOODTIMER) && MusicAI.mood <=0) { +//@@ MusicAI.flags &= (~MUSICAIF_POSMOODTIMER); +//@@ MusicAI.posmood_timer = 0.0f; +//@@ } + +//@@// determine mood of music +//@@ float shield_scalar = (float)music_info->player_shield_level/MUSIC_PLR_SHIELD_LVLS; +//@@ if (shield_scalar > 1.0f) shield_scalar = 1.0f; + +//@@// being damaged saddens mood +//@@ if (music_info->player_damaged) { +//@@ MusicAI.mood -= ((1.0f-shield_scalar)*MUSIC_MOOD_DAMAGE_MOD); +//@@ } + +//@@// killing someone HOSTILE raises one's mood. +//@@ MusicAI.mood += (music_info->n_hostiles_player_killed*MUSIC_MOOD_KILLS_MOD); + +//@@// a reduction in hostiles will also modify mood positively, and more hostiles has the opposite effect. +//@@ if ((music_info->n_hostiles-MusicAI.n_hostiles)) { +//@@ MusicAI.mood -= ((1.5f-shield_scalar)*((music_info->n_hostiles-MusicAI.n_hostiles)*MUSIC_MOOD_HOSTILES_MOD)); +//@@ } + +//@@// if no hostiles and mood != mood_default, then modify mood until it equals 0 again. do it every second. +//@@ if (MusicAI.mood != MusicAI.mood_default) { +//@@ short mod = (MusicAI.flags & MUSICAIF_HOSTILES) ? 1 : 2; +//@@ float time_mod = (MusicAI.flags & MUSICAIF_HOSTILES) ? 0.2f : 0.2f; +//@@ if (MusicAI.mood_timer >= time_mod) { +//@@ if (MusicAI.mood > MusicAI.mood_default) { +//@@ MusicAI.mood -= mod; +//@@ if (MusicAI.mood < MusicAI.mood_default) MusicAI.mood = MusicAI.mood_default; +//@@ } +//@@ if (MusicAI.mood < MusicAI.mood_default) { +//@@ MusicAI.mood += mod; +//@@ if (MusicAI.mood > MusicAI.mood_default) MusicAI.mood = MusicAI.mood_default; +//@@ } +//@@ MusicAI.mood_timer = 0.0f; +//@@ } +//@@ MusicAI.mood_timer += music_info->frametime; +//@@ } +//@@ +//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_NEGMOODTIMER)) { +//@@ MusicAI.negmood_timer += music_info->frametime; +//@@ } +//@@ if (CHECK_FLAG(MusicAI.flags, MUSICAIF_POSMOODTIMER)) { +//@@ MusicAI.posmood_timer += music_info->frametime; +//@@ } +//@@ +// other stuff + MusicAI.n_hostiles = music_info->n_hostiles; + + if (!MusicAI.n_hostiles) { // how long have we been without hostiles + MusicAI.peace_timer += music_info->frametime; + } + +//@@ if (MusicAI.flags & MUSICAIF_PLAYERDEAD) { // if dead, mood is always set to default. +//@@ MusicAI.mood = MusicAI.mood_default; +//@@ } + +// output +//@@ Music_seq.SetRegister(MUSICREG_MOOD_VALUE, MusicAI.mood); +//@@ Music_seq.SetRegister(MUSICREG_POSMOOD_TIMER, MusicAI.posmood_timer); +//@@ Music_seq.SetRegister(MUSICREG_NEGMOOD_TIMER, MusicAI.negmood_timer); + Music_seq.SetRegister(MUSICREG_PEACE_TIMER, MusicAI.peace_timer); + Music_seq.SetRegister(MUSICREG_TRIGGER_VALUE, MusicAI.trigger); + + music_info->peace_timer = MusicAI.peace_timer; +//@@ music_info->mood = MusicAI.mood; +//@@ music_info->nmood_timer = MusicAI.negmood_timer; +//@@ music_info->pmood_timer = MusicAI.posmood_timer; + + if (MusicAI.trigger > 0) { + MusicAI.trigger = MUSICTRIGGER_NONE; + } +} + + +// handles operations on current events. +void D3MusicSongSelector() +{ + oms_q_evt evt; + + if ((MusicAI.cur_song != OMS_THEME_TYPE_DEATH) && (MusicAI.flags & MUSICAIF_PLAYERDEAD)) { + MusicAI.cur_song = OMS_THEME_TYPE_DEATH; + Music_seq.StartSong(MusicAI.cur_song, false); + } + + if (MusicAI.was_toggled_on) { + if (MusicAI.pending_region > -1) { + Music_seq.SetCurrentRegion(MusicAI.pending_region); + MusicAI.pending_region = -1; + } + MusicAI.cur_song = OMS_THEME_TYPE_IDLE; + MusicAI.peace_timer = 0.0f; + Music_seq.StartSong(MusicAI.cur_song, false); + MusicAI.was_toggled_on = false; + } + +// intro music will always play at start of level. + if (MusicAI.flags & MUSICAIF_STARTLEVEL) { + MusicAI.cur_song = OMS_THEME_TYPE_IDLE; + Music_seq.SetCurrentRegion(0); + Music_seq.StartSong(MusicAI.cur_song, false); + } + if (MusicAI.pending_region > -1 && MusicAI.immediate_switch) { + MusicAI.cur_song = OMS_THEME_TYPE_IDLE; + Music_seq.SetCurrentRegion(MusicAI.pending_region); + Music_seq.StartSong(MusicAI.cur_song, false); + MusicAI.pending_region = -1; + MusicAI.immediate_switch = false; + } + +// process events and songs. + do + { + if (!Music_seq.m_output_q.recv(&evt)) { + evt.cmd = OMS_EVT_NONE; + } + + switch (MusicAI.cur_song) + { + case OMS_THEME_TYPE_NONE: + if (MusicAI.pending_region > -1) { + mprintf((0, "D3MUSIC: new region request processed.\n")); + MusicAI.cur_song = OMS_THEME_TYPE_IDLE; + Music_seq.SetCurrentRegion(MusicAI.pending_region); + Music_seq.StartSong(MusicAI.cur_song, true); + MusicAI.pending_region = -1; + } + break; + + case OMS_THEME_TYPE_IDLE: // IDLE MUSIC CURRENTLY PLAYING + if (evt.cmd == OMS_EVT_SONGENDED) { + MusicAI.cur_song = OMS_THEME_TYPE_NONE; + // if (Music_seq.GetCurrentRegion() == 0) { + // mprintf((0, "Ending Region 0.\n")); + // MusicAI.cur_song = OMS_THEME_TYPE_IDLE; + // Music_seq.SetCurrentRegion(1); + // Music_seq.StartSong(MusicAI.cur_song, true); + // } + // else { + // mprintf((0, "D3MUSIC: Song ended normally?\n")); + // } + } + else if (evt.cmd == OMS_EVT_LOOPENDING) { + // shall we switch regions? + if (MusicAI.pending_region > -1) { + mprintf((0, "D3MUSIC: new region request processed.\n")); + MusicAI.cur_song = OMS_THEME_TYPE_IDLE; + Music_seq.SetCurrentRegion(MusicAI.pending_region); + Music_seq.StartSong(MusicAI.cur_song, true); + MusicAI.pending_region = -1; + } + } + break; + + case OMS_THEME_TYPE_DEATH: + if (!(MusicAI.flags & MUSICAIF_PLAYERDEAD)) { + MusicAI.cur_song = OMS_THEME_TYPE_IDLE; + MusicAI.peace_timer = 0.0f; + Music_seq.StartSong(MusicAI.cur_song, false); + } + break; + + default: + Int3(); + } + } + while (evt.cmd != OMS_EVT_NONE); +} + + +// set music region +void D3MusicSetRegion(short region,bool immediate) +{ + if (Music_seq.GetCurrentRegion() != region) { + MusicAI.immediate_switch = immediate; + MusicAI.pending_region = region; + //@@ if (MusicAI.immediate_switch) { + //@@ // at next frame, start again! + //@@ Music_seq.Pause(); + //@@ MusicAI.restart_sequencer = true; + //@@ } + } +} + + +// retreives current region +short D3MusicGetRegion() +{ + return Music_seq.GetPlayingRegion(); +} + + +// retreives current PLAYING region. +short D3MusicGetPendingRegion() +{ + return MusicAI.pending_region; +} + + +// starts special in-game cinematic music +void D3MusicStartCinematic() +{ +} + +// stops special in-game cinematic music +void D3MusicStopCinematic() +{ +} diff --git a/dd_grwin32/CMakeLists.txt b/dd_grwin32/CMakeLists.txt new file mode 100644 index 000000000..2436c7217 --- /dev/null +++ b/dd_grwin32/CMakeLists.txt @@ -0,0 +1,11 @@ +SET (HEADERS + ddgrWin32.h + ddgrWin32DX.h + ddgrWin32GDI.h) +SET (CPPS + ddgrWin32API.cpp + ddgrWin32DX.cpp + ddgrWin32GDI.cpp + ddgrWin32Init.cpp) + +ADD_LIBRARY(dd_grwin32 STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/dd_grwin32/ddgrWin32.h b/dd_grwin32/ddgrWin32.h new file mode 100644 index 000000000..76b24eb4e --- /dev/null +++ b/dd_grwin32/ddgrWin32.h @@ -0,0 +1,97 @@ +/* + * $Logfile: /DescentIII/Main/ddgr_win32/ddgrWin32.h $ + * $Revision: 3 $ + * $Date: 4/23/98 6:38p $ + * $Author: Jason $ + * + * DDGR v2.0 Win32 Implementation + * + * $Log: /DescentIII/Main/ddgr_win32/ddgrWin32.h $ + * + * 3 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 2 9/12/97 4:13p Samir + * Added some private data access functions and more DirectX + * functionality. + * + * 1 6/12/97 6:33p Samir + * Initial revision + * + * $NoKeywords: $ + */ + + +#ifndef DDGRWIN32_H +#define DDGRWIN32_H + +#include "ddgr.h" +#include "pstypes.h" + +/* Subsystem constants + + as of v2.0 +*/ +const int DDGR_GDI_SUBSYSTEM = 1, // GDI subsystem + DDGR_GDIX_SUBSYSTEM = 2, // GDIX subsystem (GDI+DirectX) + DDGR_DX_SUBSYSTEM = 3; // DIRECTX subsystem + + +/* Internal Library Data + +*/ +typedef struct tDDGRInternalData +{ + bool init; // Is library currently initialized + int subsystem; // Current active subsystem +} +tDDGRInternalData; + + +/* Global externs +*/ +extern tDDGRInternalData DDGR_lib_data; +extern char *DDGR_subsystem_names[]; +extern int DDGR_subsystems[]; + + +/* Macros +*/ +#define LIB_DATA(_c) DDGR_lib_data._c + +inline unsigned DDGR_MAKERGB16(ddgr_color c) +{ + unsigned char r,g,b; + r = (unsigned char)((c & 0x00ff0000) >> 16); + g = (unsigned char)((c & 0x0000ff00) >> 8); + b = (unsigned char)(c & 0x000000ff); + + return (((r>>3)<<10) + ((g>>3)<<5) + (b>>3)); +} + +inline int DDGR_COLOR_RED(ddgr_color c) +{ + unsigned r = (unsigned char)((c & 0x00ff0000) >> 16); + return (int)r; +} + +inline int DDGR_COLOR_GREEN(ddgr_color c) +{ + unsigned g = (unsigned char)((c & 0x0000ff00) >> 8); + return (int)g; +} + +inline int DDGR_COLOR_BLUE(ddgr_color c) +{ + unsigned b = (unsigned char)(c & 0x000000ff); + return (int)b; +} + + +/* Functions Internal +*/ + +void ddgr_FatalError(char *fmt, ...); +void ddgr_PushError(char *fmt, ...); + +#endif \ No newline at end of file diff --git a/dd_grwin32/ddgrWin32API.cpp b/dd_grwin32/ddgrWin32API.cpp new file mode 100644 index 000000000..dc9d0363e --- /dev/null +++ b/dd_grwin32/ddgrWin32API.cpp @@ -0,0 +1,276 @@ +/* + * $Logfile: /DescentIII/Main/ddgr_win32/ddgrWin32API.cpp $ + * $Revision: 3 $ + * $Date: 9/12/97 4:13p $ + * $Author: Samir $ + * + * DDGR v2.0 Initialization and housekeeping for Win32 + * + * $Log: /DescentIII/Main/ddgr_win32/ddgrWin32API.cpp $ + * + * 3 9/12/97 4:13p Samir + * Added some private data access functions and more DirectX + * functionality. + * + * 2 6/13/97 3:01p Samir + * Fixed flip assertions and error handling. + * + * 1 6/12/97 6:33p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "ddgrWin32.h" +#include "ddgrWin32GDI.h" +#include "ddgrWin32DX.h" +#include "mono.h" +#include "pserror.h" + + +/* Surface Functions +*/ + +/* input: + sf->name is optional. + sf->w, sf->h, sf->bpp are mandatory + sf->type and sf->flags are mandatory. + output: + sf->obj = surface object. + sf->locks = 0 +*/ +bool ddgr_surf_Create(ddgr_surface *sf) +{ + bool success; + + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: success = ddgr_gdi_surf_Create(sf); break; + case DDGR_DX_SUBSYSTEM: success = ddgr_dx_surf_Create(sf); break; + default: Int3(); + } + + if (!success) { + ddgr_FatalError("Failed to create surface <%s>", sf->name); + } + + return success; +} + + +void ddgr_surf_Destroy(ddgr_surface *sf) +{ + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: ddgr_gdi_surf_Destroy(sf); break; + case DDGR_DX_SUBSYSTEM: ddgr_dx_surf_Destroy(sf); break; + default: Int3(); + } +} + + +/* retrieves a pointer to surface memory. allowed to lock one surface multiple times. + ptr is the returned pointer to surface memory. used to unlock surface also + rowsize is the size in bytes of one row of memory. +*/ +bool ddgr_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize) +{ + bool success; + + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: success = ddgr_gdi_surf_Lock(sf, ptr, rowsize); break; + case DDGR_DX_SUBSYSTEM: success = ddgr_dx_surf_Lock(sf, ptr, rowsize); break; + default: Int3(); + } + + if (!success) { + ddgr_FatalError("Failed to lock surface <%s>", sf->name); + } + + return success; +} + + +bool ddgr_surf_Unlock(ddgr_surface *sf, void *ptr) +{ + bool success; + + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: success = ddgr_gdi_surf_Unlock(sf, ptr); break; + case DDGR_DX_SUBSYSTEM: success = ddgr_dx_surf_Unlock(sf, ptr); break; + default: Int3(); + } + + if (!success) { + ddgr_FatalError("Failed to unlock surface <%s>", sf->name); + } + + return success; +} + + +// attaches an OS handle to a surface +void ddgr_surf_AttachHandle(ddgr_surface *sf, unsigned handle) +{ + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: ddgr_gdi_surf_AttachHandle(sf, handle); break; + case DDGR_DX_SUBSYSTEM: ddgr_dx_surf_AttachHandle(sf, handle); break; + default: Int3(); + } +} + + +/* initializes a video screen from the given input surface which specifies the dimensions + of the screen. + sf->name is optional + sf->w, sf->h are mandatory + sf->bpp is recommended. if set to BPP_DEFAULT, it uses the current display bit depth + sf->type = SURFTYPE_VIDEOSCREEN + sf->flags = 0 or SURFFLAG_BACKBUFFER. surfaces with backbuffers allow rendering to + them and display via flip. monopage surfaces don't have this luxury. +*/ +bool ddgr_surf_InitVideo(ddgr_surface *sf) +{ + bool success; + + ASSERT(sf->type == SURFTYPE_VIDEOSCREEN); + + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: success = ddgr_gdi_surf_InitVideo(sf); break; + case DDGR_DX_SUBSYSTEM: success = ddgr_dx_surf_InitVideo(sf); break; + default: Int3(); + } + + if (!success) { + ddgr_FatalError("Failed to initialize video screen <%s>", sf->name); + } + + return success; +} + + +/* close video reverses the operation of init video for that surface. the display should stay + the same, but no more operations may occur to that surface through this library. +*/ +void ddgr_surf_CloseVideo(ddgr_surface *sf) +{ + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: ddgr_gdi_surf_CloseVideo(sf); break; + case DDGR_DX_SUBSYSTEM: ddgr_dx_surf_CloseVideo(sf); break; + default: Int3(); + } +} + + +/* flips the buffers in a surface. really only useful for video screens +*/ +bool ddgr_surf_FlipVideo(ddgr_surface *sf) +{ + bool success; + +// don't report error if there's no backbuffer. + if(!(sf->flags & SURFFLAG_BACKBUFFER)) return true; + + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: success = ddgr_gdi_surf_FlipVideo(sf); break; + case DDGR_DX_SUBSYSTEM: success = ddgr_dx_surf_FlipVideo(sf); break; + default: Int3(); + } + + if (!success) { + ddgr_FatalError("Failed to flip surface <%s>", sf->name); + } + + return success; +} + + +/* graphic primatives +*/ +void ddgr_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h) +{ + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: ddgr_gdi_surf_Clear(dsf,col,l,t,w,h); break; + case DDGR_DX_SUBSYSTEM: ddgr_dx_surf_Clear(dsf,col,l,t,w,h); break; + default: Int3(); + } +} + + +bool ddgr_surf_Blt(ddgr_surface *dsf, int dx, int dy, ddgr_surface *ssf, int sx, int sy, int sw, int sh) +{ + bool success; + + switch(LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: success = ddgr_gdi_surf_Blt(dsf, dx,dy,ssf,sx,sy,sw,sh); break; + case DDGR_DX_SUBSYSTEM: success = ddgr_dx_surf_Blt(dsf, dx,dy,ssf,sx,sy,sw,sh); break; + default: Int3(); + } + + if (!success) { + ddgr_FatalError("Failed to blt surface <%s> to <%s>", ssf->name, dsf->name); + } + + return success; +} + + +// returns internal information about an ddgr_surface (only to low level libraries, on par with the ddgr +// library). +void ddgr_surf_GetPrivateData(ddgr_surface *sf, bool *ddraw_surf, uint *object_ptr) +{ + switch (LIB_DATA(subsystem)) + { + case DDGR_DX_SUBSYSTEM: + { + tDXSurface *bm = (tDXSurface *)sf->obj; + *ddraw_surf = true; + *object_ptr = (bm->backbuffer == true) ? (uint)(bm->lpddsback) : (uint)(bm->lpdds); + break; + } + default: + Int3(); + } +} + + +// returns aspect ratio of current display. +float ddgr_GetAspectRatio() +{ + float aspect_ratio = (float)((3.0 * GetSystemMetrics(SM_CXSCREEN))/(4.0 * GetSystemMetrics(SM_CYSCREEN))); + return aspect_ratio; +} + + +unsigned ddgr_GetDDrawObject() +{ + switch (LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: return (unsigned)(GDI_DATA(lpDD)); + case DDGR_DX_SUBSYSTEM: return (unsigned)(DX_DATA(lpDD)); + default: + Int3(); + } + + return 0; +} + diff --git a/dd_grwin32/ddgrWin32DX.cpp b/dd_grwin32/ddgrWin32DX.cpp new file mode 100644 index 000000000..a54e91b43 --- /dev/null +++ b/dd_grwin32/ddgrWin32DX.cpp @@ -0,0 +1,320 @@ +/* + * $Logfile: /DescentIII/Main/ddgr_win32/ddgrWin32DX.cpp $ + * $Revision: 2 $ + * $Date: 9/12/97 4:13p $ + * $Author: Samir $ + * + * DirectX implementation of DDGR library. + * + * $Log: /DescentIII/Main/ddgr_win32/ddgrWin32DX.cpp $ + * + * 2 9/12/97 4:13p Samir + * Added some private data access functions and more DirectX + * functionality. + * + * $NoKeywords: $ + */ + +#include "DDAccess.h" // Allow this module full DD access +#include "ddgrWin32.h" +#include "ddgrWin32DX.h" +#include "Application.h" +#include "mono.h" +#include "pserror.h" + + +tDDGRDXInternalData DDGR_DX_lib_data; + + +// Initializes DX subsystem +bool ddgr_dx_Init(oeApplication *app) +{ + DX_DATA(hPrimaryWnd) = (HWND)((oeWin32Application *)app)->m_hWnd; + + DX_DATA(lpDD) = NULL; + +// Initialize DirectDraw system if we're going fullscreen, and GDIX system + HRESULT hres; + + hres = DirectDrawCreate(NULL, &DX_DATA(lpDD), NULL); + if (hres != DD_OK) { + ddgr_PushError("Failure to initialize DirectDraw driver (%d)", LOWORD(hres)); + return false; + } + + hres = DX_DATA(lpDD)->SetCooperativeLevel(DX_DATA(hPrimaryWnd), + DDSCL_ALLOWREBOOT | + DDSCL_EXCLUSIVE | + DDSCL_FULLSCREEN); + + if (hres != DD_OK) { + ddgr_PushError("Failed to set DirectDraw fullscreen access level (%d)", LOWORD(hres)); + return false; + } + + DX_DATA(vidrefs) = 0; // reset video reference count. + DX_DATA(init) = true; + + mprintf((0, "DX system initialized.\n")); + + return true; +} + + +// Closes DX subsystem +void ddgr_dx_Close() +{ + ASSERT(DX_DATA(vidrefs) == 0); + + if (DX_DATA(lpDD)) { + DX_DATA(lpDD)->RestoreDisplayMode(); + DX_DATA(lpDD)->Release(); + } + DX_DATA(lpDD) = NULL; + DX_DATA(hPrimaryWnd) = NULL; + DX_DATA(init) = false; + + mprintf((0, "DX system closed.\n")); +} + + +// Initializes the display for use with the DX subsystem. +bool ddgr_dx_surf_InitVideo(ddgr_surface *sf) +{ + tDXSurface *dxsf; + DDSURFACEDESC ddsd; + +// we need to create a primary surface from the display mode we set, and possibly a backbuffer if +// requested. + if (DX_DATA(vidrefs) == 0) { + HRESULT hres; + DDSCAPS ddscaps; + + hres = DX_DATA(lpDD)->SetDisplayMode(sf->w, sf->h, sf->bpp); + if (hres != DD_OK) { + ddgr_PushError("Unable to set DirectDraw display mode (%d)\n", LOWORD(hres)); + return false; + } + + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + ddsd.dwBackBufferCount = 1; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + + hres = DX_DATA(lpDD)->CreateSurface(&ddsd, &DX_DATA(vidsurf.lpdds), NULL); + if (hres != DD_OK) { + ddgr_PushError("Unable to capture DirectDraw display surface (%d)\n", LOWORD(hres)); + return false; + } + + ddscaps.dwCaps = DDSCAPS_BACKBUFFER; + + hres = DX_DATA(vidsurf.lpdds)->GetAttachedSurface(&ddscaps, &DX_DATA(vidsurf.lpddsback)); + if (hres != DD_OK) { + ddgr_PushError("Unable to capture DirectDraw display back surface (%d)\n", LOWORD(hres)); + return false; + } + } + + DX_DATA(vidrefs)++; // increment vidref count + + dxsf = new tDXSurface; + dxsf->lpdds = DX_DATA(vidsurf.lpdds); + +// this means that we are capturing the screen, not creating a screen. +// get the screen's width and height and bitdepth + if (sf->flags == 0) { + memset(&ddsd,0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + dxsf->lpdds->GetSurfaceDesc(&ddsd); + sf->w = ddsd.dwWidth; + sf->h = ddsd.dwHeight; + sf->bpp = ddsd.ddpfPixelFormat.dwRGBBitCount; + } + +// we create a dummy surface for the screen's back buffer, or our accessable buffer +// specifying SURFFLAG_BACKBUFFER ensures that we create a GDI bitmap of size requested +// in 'sf' and it will be our renderable bitmap, or OUR primary render buffer + if (sf->flags & SURFFLAG_BACKBUFFER) { + // set our front buffer to this newly created surface and delete the gdi bitmap object + dxsf->lpddsback = DX_DATA(vidsurf.lpddsback); + dxsf->backbuffer = true; + } + else { + dxsf->lpddsback = NULL; + dxsf->backbuffer = false; + } + + sf->obj = (void *)dxsf; + + return true; +} + + +// Deinitializes the video surface +void ddgr_dx_surf_CloseVideo(ddgr_surface *sf) +{ + tDXSurface *bm = (tDXSurface *)sf->obj; + + ASSERT(DX_DATA(vidrefs) > 0); + + DX_DATA(vidrefs)--; + if (DX_DATA(vidrefs) == 0) { + DX_DATA(vidsurf.lpdds)->Release(); + DX_DATA(vidsurf.lpdds) = NULL; + DX_DATA(vidsurf.lpddsback) = NULL; + } + + delete bm; +} + + +// flips the buffers in a surface. really only useful for video screens +bool ddgr_dx_surf_FlipVideo(ddgr_surface *sf) +{ + tDXSurface *bm = (tDXSurface *)sf->obj; + HRESULT hres; + + if (bm->backbuffer && (sf->flags & SURFFLAG_BACKBUFFER)) { + hres = bm->lpdds->Flip(NULL,0); + if (hres == DDERR_WASSTILLDRAWING || hres == DD_OK) return true; + else return false; + } + + return true; +} + + +/* input: + sf->name is optional. + sf->w, sf->h, sf->bpp are mandatory + sf->type and sf->flags are mandatory. + output: + sf->obj = surface object. + sf->locks = 0 +*/ +bool ddgr_dx_surf_Create(ddgr_surface *sf) +{ + + return true; +} + + +void ddgr_dx_surf_Destroy(ddgr_surface *sf) +{ + +} + + +/* retrieves a pointer to surface memory. allowed to lock one surface multiple times. + ptr is the returned pointer to surface memory. used to unlock surface also + rowsize is the size in bytes of one row of memory. +*/ +bool ddgr_dx_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize) +{ + HRESULT hres; + DDSURFACEDESC ddsd; + tDXSurface *bm = (tDXSurface *)sf->obj; + + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + + if (bm->backbuffer) + hres = bm->lpddsback->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + else + hres = bm->lpdds->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + + if (hres != DD_OK) { + ddgr_PushError("Unable to set lock DirectDraw surface(%d)\n", LOWORD(hres)); + return false; + } + + *rowsize = (int)ddsd.lPitch; + *ptr = ddsd.lpSurface; + + return true; +} + + +bool ddgr_dx_surf_Unlock(ddgr_surface *sf, void *ptr) +{ + HRESULT hres; + tDXSurface *bm = (tDXSurface *)sf->obj; + + if (bm->backbuffer) + hres = bm->lpddsback->Unlock(ptr); + else + hres = bm->lpdds->Unlock(ptr); + + if (hres != DD_OK) { + ddgr_PushError("Unable to unlock DirectDraw surface(%d)\n", LOWORD(hres)); + return false; + } + + return true; +} + + +// attaches an OS handle to a surface +void ddgr_dx_surf_AttachHandle(ddgr_surface *sf, unsigned handle) +{ + +} + + +// The only graphic primatives +// clear +// blt. +void ddgr_dx_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h) +{ + HRESULT hres; + DDBLTFX ddbltfx; + tDXSurface *dbm = (tDXSurface *)dsf->obj; + LPDIRECTDRAWSURFACE lpdds; + RECT rect; + + lpdds = (dbm->backbuffer) ? dbm->lpddsback : dbm->lpdds; + + memset(&ddbltfx, 0, sizeof(DDBLTFX)); + ddbltfx.dwSize = sizeof( ddbltfx ); + ddbltfx.dwFillColor = RGB(DDGR_COLOR_RED(col), DDGR_COLOR_GREEN(col), DDGR_COLOR_BLUE(col)); + SetRect(&rect, l,t,l+w-1, t+h-1); + + hres = lpdds->Blt(&rect, // dest rect + NULL, // src surface + NULL, // src rect + DDBLT_COLORFILL | DDBLT_WAIT, + &ddbltfx); +} + + +bool ddgr_dx_surf_Blt(ddgr_surface *dsf, int dx, int dy, ddgr_surface *ssf, int sx, int sy, int sw, int sh) +{ + HRESULT hres; + tDXSurface *sbm = (tDXSurface *)ssf->obj; + tDXSurface *dbm = (tDXSurface *)dsf->obj; + LPDIRECTDRAWSURFACE lpddsd; + LPDIRECTDRAWSURFACE lpddss; + RECT srect,drect; + + if (ssf->flags & SURFFLAG_BACKBUFFER) lpddss = sbm->lpddsback; + else lpddss = sbm->lpdds; + + if (dsf->flags & SURFFLAG_BACKBUFFER) lpddsd = dbm->lpddsback; + else lpddsd = dbm->lpdds; + + SetRect(&srect, sx,sy,sx+sw-1,sy+sh-1); + SetRect(&drect, dx,dy,dx+sw-1,dy+sh+1); + + hres = lpddsd->Blt(&drect,lpddss,&srect,DDBLT_WAIT,NULL); + if (hres != DD_OK) { + ddgr_PushError("Unable to blt surface %s to DirectDraw surface %s(%d)\n", ssf->name, dsf->name,LOWORD(hres)); + return false; + } + + return true; +} + + + diff --git a/dd_grwin32/ddgrWin32DX.h b/dd_grwin32/ddgrWin32DX.h new file mode 100644 index 000000000..f1e6c0975 --- /dev/null +++ b/dd_grwin32/ddgrWin32DX.h @@ -0,0 +1,107 @@ +/* + * $Logfile: /DescentIII/Main/ddgr_win32/ddgrWin32DX.h $ + * $Revision: 2 $ + * $Date: 9/12/97 4:13p $ + * $Author: Samir $ + * + * DirectX Subsystem Implementation of ddgr library + * + * $Log: /DescentIII/Main/ddgr_win32/ddgrWin32DX.h $ + * + * 2 9/12/97 4:13p Samir + * Added some private data access functions and more DirectX + * functionality. + * + * $NoKeywords: $ + */ + +/* Functions +*/ + +#ifndef DDGRWIN32DX_H +#define DDGRWIN32DX_H + +#define WIN32_LEAN_AND_MEAN +#include + +#include "ddgr.h" +#include "ddraw.h" + +class oeApplication; + + +/* Data structures +*/ +typedef struct tDXSurface +{ + LPDIRECTDRAWSURFACE lpdds; + LPDIRECTDRAWSURFACE lpddsback; + bool backbuffer; +} +tDXSurface; + +#define DX_MAX_DEVMODES 32 +typedef struct tDDGRDXInternalData // Internal data for DX subsystem +{ + bool init; // is library initialized + LPDIRECTDRAW lpDD; // DirectDraw Object for video manipulation + HWND hPrimaryWnd; // window owning display + int vidrefs; // Number of surface references to hPrimaryWnd. + tDXSurface vidsurf; + DDSURFACEDESC vidmodes[DX_MAX_DEVMODES]; // Device modes for GDI subsystem (not GDIX) +} +tDDGRDXInternalData; + + +/* Externs */ +extern tDDGRDXInternalData DDGR_DX_lib_data; + + +/* Macros */ +#define DX_DATA(_c) DDGR_DX_lib_data._c + + +// Initializes DX subsystem +bool ddgr_dx_Init(oeApplication *app); + +// Closes DX subsystem +void ddgr_dx_Close(); + +// Initializes the display for use with the DX subsystem. +bool ddgr_dx_surf_InitVideo(ddgr_surface *sf); + +// Deinitializes the video surface +void ddgr_dx_surf_CloseVideo(ddgr_surface *sf); + +// flips the buffers in a surface. really only useful for video screens +bool ddgr_dx_surf_FlipVideo(ddgr_surface *sf); + +/* input: + sf->name is optional. + sf->w, sf->h, sf->bpp are mandatory + sf->type and sf->flags are mandatory. + output: + sf->obj = surface object. + sf->locks = 0 +*/ +bool ddgr_dx_surf_Create(ddgr_surface *sf); +void ddgr_dx_surf_Destroy(ddgr_surface *sf); + +/* retrieves a pointer to surface memory. allowed to lock one surface multiple times. + ptr is the returned pointer to surface memory. used to unlock surface also + rowsize is the size in bytes of one row of memory. +*/ +bool ddgr_dx_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize); +bool ddgr_dx_surf_Unlock(ddgr_surface *sf, void *ptr); + +// attaches an OS handle to a surface +void ddgr_dx_surf_AttachHandle(ddgr_surface *sf, unsigned handle); + +// The only graphic primatives +// clear +// blt. +void ddgr_dx_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h); +bool ddgr_dx_surf_Blt(ddgr_surface *dsf, int dx, int dy, ddgr_surface *ssf, int sx, int sy, int sw, int sh); + + +#endif diff --git a/dd_grwin32/ddgrWin32GDI.cpp b/dd_grwin32/ddgrWin32GDI.cpp new file mode 100644 index 000000000..bc9f94dfc --- /dev/null +++ b/dd_grwin32/ddgrWin32GDI.cpp @@ -0,0 +1,595 @@ +/* + * $Logfile: /DescentIII/Main/ddgr_win32/ddgrWin32GDI.cpp $ + * $Revision: 10 $ + * $Date: 10/02/98 11:15a $ + * $Author: Jeff $ + * + * DDGR v2.0 GDI(X) implementation + * + * $Log: /DescentIII/Main/ddgr_win32/ddgrWin32GDI.cpp $ + * + * 10 10/02/98 11:15a Jeff + * added HBITMAP, HFONT and HBRUSH type casts where needed to satisfy the + * compiler + * + * 9 6/02/98 12:46p Samir + * removed restriction of ddgr system when going to true-color + * resolutions. + * + * 8 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 7 4/09/98 11:00a Samir + * took out assertion in ddgr_gdi_Close that caused errors and assertions + * to 'crap' out. + * + * 6 11/14/97 12:32p Samir + * DirectDraw works under GDIX windowed subsystem (NORMAL cooperative + * mode). + * + * 5 9/23/97 12:15p Jason + * fixed color bug + * + * 4 9/12/97 4:13p Samir + * Added some private data access functions and more DirectX + * functionality. + * + * 3 8/12/97 10:43p Matt + * Fixed RGB conversion bug + * + * 5 6/16/97 4:46p Samir + * OpenGL works in window too. + * + * 4 6/13/97 3:01p Samir + * Fixed flip assertions and OpenGL imp. + * + * 3 6/13/97 1:24p Samir + * FlipVideo doesn't flip when no backbuffer, so just do nothing. + * + * 2 6/13/97 12:24p Samir + * Fixed OpenGLDC hack and removed assert in lock if data ptr = NULL + * + * 1 6/12/97 6:33p Samir + * Initial revision + * + * $NoKeywords: $ + */ + + +#include "DDAccess.h" // Allow this module full DD access +#include "ddgrWin32.h" +#include "ddgrWin32GDI.h" +#include "Application.h" +#include "mono.h" +#include "pserror.h" +#include "gr.h" + + +/* Global Library Data + */ +tDDGRGDIInternalData DDGR_GDI_lib_data; + + +/* Primary Interface Functions +*/ + +// --------------------------------------------------------------------------- +// initializes the objects needed to use GDI + +bool ddgr_gdi_Init(oeApplication *app, bool fullscreen, bool ddraw) +{ + HRESULT hres; + + GDI_DATA(fullscreen) = fullscreen; + GDI_DATA(hPrimaryWnd) = (HWND)((oeWin32Application *)app)->m_hWnd; + + GDI_DATA(lpDD) = NULL; + if (ddraw) { + hres = DirectDrawCreate(NULL, &GDI_DATA(lpDD), NULL); + if (hres != DD_OK) { + ddgr_PushError("Failure to initialize DirectDraw driver (%d)", LOWORD(hres)); + return false; + } + } + +// Initialize DirectDraw exclusive system if we're going fullscreen, and GDIX system + if (GDI_DATA(fullscreen)) { + if (ddraw) { + hres = GDI_DATA(lpDD)->SetCooperativeLevel(GDI_DATA(hPrimaryWnd), + DDSCL_ALLOWREBOOT | + DDSCL_EXCLUSIVE | + DDSCL_FULLSCREEN); + + if (hres != DD_OK) { + ddgr_PushError("Failed to set DirectDraw fullscreen access level (%d)", LOWORD(hres)); + return false; + } + } + else { + // grab all device modes for the current display device. + unsigned i; + for (i = 0; i < GDI_MAX_DEVMODES; i++) + { + if (EnumDisplaySettings(NULL, i, &GDI_DATA(devmodes[i])) == FALSE) { + break; + } + HDC hdc = GetDC(GDI_DATA(hPrimaryWnd)); + + if (GDI_DATA(devmodes[i].dmBitsPerPel)==(uint)GetDeviceCaps(hdc, BITSPIXEL) && + GDI_DATA(devmodes[i].dmPelsWidth)==(uint)GetDeviceCaps(hdc,HORZRES) && + GDI_DATA(devmodes[i].dmPelsHeight)==(uint)GetDeviceCaps(hdc,VERTRES)) + GDI_DATA(olddispmode) = i; + + ReleaseDC(GDI_DATA(hPrimaryWnd), hdc); + } + } + + GDI_DATA(ddraw) = ddraw; + } + else { + if (ddraw) { + hres = GDI_DATA(lpDD)->SetCooperativeLevel(GDI_DATA(hPrimaryWnd), DDSCL_NORMAL); + if (hres != DD_OK) { + ddgr_PushError("Failed to set DirectDraw normal access level (%d)", LOWORD(hres)); + return false; + } + } + } + + GDI_DATA(hOffscreenDC) = NULL; + GDI_DATA(vidrefs) = 0; // reset video reference count. + GDI_DATA(init) = true; + + mprintf((0, "GDI system initialized.\n")); + + return true; +} + + +// --------------------------------------------------------------------------- +// Deinitialized GDI objects + +void ddgr_gdi_Close() +{ + if (GDI_DATA(hOffscreenDC)) { + DeleteDC(GDI_DATA(hOffscreenDC)); + } + + if (GDI_DATA(fullscreen)) { + if (GDI_DATA(ddraw)) { + // For full screen, deinitialize directdraw/ + if (GDI_DATA(lpDD)) { + GDI_DATA(lpDD)->RestoreDisplayMode(); + GDI_DATA(lpDD)->Release(); + } + GDI_DATA(lpDD) = NULL; + } + else { + // do a ChangeDisplaySettings to old mode. + LONG lres; + GDI_DATA(devmodes[GDI_DATA(olddispmode)]).dmFields = + DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | + DM_DISPLAYFREQUENCY; + + lres = ChangeDisplaySettings(&GDI_DATA(devmodes[GDI_DATA(olddispmode)]), 0); + ASSERT(lres == DISP_CHANGE_SUCCESSFUL); + + } + } + + GDI_DATA(hPrimaryWnd) = NULL; + GDI_DATA(init) = false; + + mprintf((0, "GDI system closed.\n")); +} + + +// --------------------------------------------------------------------------- +// Initializes the video surface for blting +// note that is the surface specified is a back buffer by sf->flags, then we need +// to create a GDI bitmap for the back buffer, and our visible screen is the front buffer + +bool ddgr_gdi_surf_InitVideo(ddgr_surface *sf) +{ + tGDISurface *gdisf, *gdisf2; + +// we need to create a DC for our GDI bitmaps if this is first ref to video surface. + if (GDI_DATA(vidrefs) == 0) { + // if we are in fullscreen, do display mode change + HDC hdc; + + if (GDI_DATA(fullscreen)) { + if (GDI_DATA(ddraw)) { + HRESULT hres; + + hres = GDI_DATA(lpDD)->SetDisplayMode(sf->w, sf->h, sf->bpp); + if (hres != DD_OK) { + ddgr_PushError("Unable to set DirectDraw display mode in fullscreen (%d)\n", LOWORD(hres)); + return false; + } + } + else { + // grab all device modes for the current display device. + unsigned i; + for (i = 0; i < GDI_MAX_DEVMODES; i++) + { + LONG lres; + if ((int)GDI_DATA(devmodes[i].dmBitsPerPel)==sf->bpp && + (int)GDI_DATA(devmodes[i].dmPelsWidth)==sf->w && + (int)GDI_DATA(devmodes[i].dmPelsHeight)==sf->h) { + // do a ChangeDisplaySettings to old mode. + GDI_DATA(devmodes[i]).dmFields = + DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | + DM_DISPLAYFREQUENCY; + + lres = ChangeDisplaySettings(&GDI_DATA(devmodes[i]), 0); + ASSERT(lres == DISP_CHANGE_SUCCESSFUL); + break; + } + } + } + } + // HACKS FOR OPENGL, WILL CHANGE SOON. + hdc = CreateCompatibleDC(NULL); + if (hdc == NULL) { + ddgr_PushError("Unable to create offscreen device context (%x).", GetLastError()); + return false; + } + GDI_DATA(hOffscreenDC) = hdc; + } + + GDI_DATA(vidrefs)++; // increment vidref count + + gdisf = new tGDISurface; + gdisf->hbm = NULL; + gdisf->data = NULL; + gdisf->rowsize = NULL; + gdisf->hwnd = NULL; + +// this means that we are capturing the screen, not creating a screen. +// get the screen's width and height and bitdepth + if (sf->flags == 0) { + sf->w = (int)GetDeviceCaps(GDI_DATA(hOffscreenDC), HORZRES); + sf->h = (int)GetDeviceCaps(GDI_DATA(hOffscreenDC), VERTRES); + sf->bpp = (int)GetDeviceCaps(GDI_DATA(hOffscreenDC), BITSPIXEL); + } + + HDC hdc = GetDC(NULL); + int tbpp = GetDeviceCaps(hdc, BITSPIXEL); + ReleaseDC(NULL, hdc); + + if (tbpp < sf->bpp) { + ddgr_PushError("Desktop color depth must be set to a %d-bit color mode or greater.\n", sf->bpp); + return false; + } + +// we create a dummy surface for the screen's back buffer, or our accessable buffer +// specifying SURFFLAG_BACKBUFFER ensures that we create a GDI bitmap of size requested +// in 'sf' and it will be our renderable bitmap, or OUR primary render buffer + if (sf->flags & SURFFLAG_BACKBUFFER) { + ddgr_surface back_sf; + back_sf.w = sf->w; + back_sf.h = sf->h; + back_sf.bpp = sf->bpp; + back_sf.type = SURFTYPE_GENERIC; + + if (!ddgr_gdi_surf_Create(&back_sf)) { + ddgr_PushError((0, "Failed to create back buffer <%s>", sf->name)); + delete gdisf; + return false; + } + + // set our front buffer to this newly created surface and delete the gdi bitmap object + gdisf2 = (tGDISurface *)back_sf.obj; + gdisf->hbm = gdisf2->hbm; + gdisf->data = gdisf2->data; + gdisf->rowsize = gdisf2->rowsize; + gdisf->backbuffer = true; + delete gdisf2; + } + else { + gdisf->hbm = NULL; // this is really just the screen. + gdisf->backbuffer = false; + } + + gdisf->hwnd = GDI_DATA(hPrimaryWnd); + + sf->obj = (void *)gdisf; + + return true; +} + + +// --------------------------------------------------------------------------- +// deinitializes the DC created in InitVideo + +void ddgr_gdi_surf_CloseVideo(ddgr_surface *sf) +{ + tGDISurface *bm = (tGDISurface *)sf->obj; + + ASSERT(GDI_DATA(vidrefs) > 0); + + GDI_DATA(vidrefs)--; + if (GDI_DATA(vidrefs) == 0) { + if (GDI_DATA(fullscreen)) { + + } + // HACK for OPENGL support. + DeleteDC(GDI_DATA(hOffscreenDC)); + } + + if (sf->flags & SURFFLAG_BACKBUFFER) + DeleteObject(bm->hbm); // eliminate back buffer + + delete bm; +} + + +// --------------------------------------------------------------------------- +// flip, does absolutely nothing + +bool ddgr_gdi_surf_FlipVideo(ddgr_surface *sf) +{ + HBITMAP old_bmp, h_sbm; + HDC hdc_dest; + RECT rect; + int dw, dh; + tGDISurface *bm = (tGDISurface *)sf->obj; + + ASSERT(bm->hwnd != NULL); + +// don't give an error if there's no backbuffer. + if (!bm->backbuffer) return true; + + h_sbm = bm->hbm; + +// if we associated another window with our screem, then make sure their DC is the dest. +// other wise, use our base window's DC for destination, and select our front buffer as the +// source bitmap into our driver's DC. +// if (bm->hwnd == NULL) { +// hdc_dest = GetDC(GDI_DATA(hwnd)); +// GetClientRect(GDI_DATA(hwnd),&rect); +// } +// else { + hdc_dest = GetDC(bm->hwnd); + GetClientRect(bm->hwnd, &rect); +// } + + dw = rect.right - rect.left; + dh = rect.bottom - rect.top; + + old_bmp = (HBITMAP)SelectObject(GDI_DATA(hOffscreenDC), h_sbm); + + BOOL bltres = BitBlt(hdc_dest, 0, 0, dw, dh, GDI_DATA(hOffscreenDC), 0,0,SRCCOPY); + + SelectObject(GDI_DATA(hOffscreenDC), old_bmp); + + ReleaseDC(bm->hwnd, hdc_dest); + + return (bltres) ? true: false; +} + + +// --------------------------------------------------------------------------- +// Creates a GDI DIBitmap + +bool ddgr_gdi_surf_Create(ddgr_surface *sf) +{ + tGDISurface *bm; + +// set up bitmap header + int nw = sf->w; + if (nw % 4) nw = ((sf->w/4)*4)+4; + + bm = new tGDISurface; + + if (sf->bpp == BPP_8) { + tDIBHeader8 header; + + header.bmi.biSize = sizeof(BITMAPINFOHEADER); + header.bmi.biWidth = nw; + header.bmi.biHeight = -sf->h; // Always a top down bitmap!! + header.bmi.biPlanes = 1; + header.bmi.biBitCount = sf->bpp; + header.bmi.biCompression = BI_BITFIELDS; + header.bmi.biSizeImage = 0; + header.bmi.biXPelsPerMeter = 0; + header.bmi.biYPelsPerMeter = 0; + header.bmi.biClrUsed = 0; + header.bmi.biClrImportant = 0; + + bm->hbm = CreateDIBSection(GDI_DATA(hOffscreenDC), (BITMAPINFO *)&header,DIB_RGB_COLORS,&bm->data,NULL,0); + } + else { + tDIBHeader header; + header.bmi.biSize = sizeof(BITMAPINFOHEADER); + header.bmi.biWidth = nw; + header.bmi.biHeight = -sf->h; // Always a top down bitmap!! + header.bmi.biPlanes = 1; + header.bmi.biBitCount = sf->bpp; + header.bmi.biCompression = BI_BITFIELDS; + header.bmi.biSizeImage = 0; + header.bmi.biXPelsPerMeter = 0; + header.bmi.biYPelsPerMeter = 0; + header.bmi.biClrUsed = 0; + header.bmi.biClrImportant = 0; + + // setup RGB bit masks + if (sf->bpp == BPP_16) { + header.red_mask = 0x7c00; + header.green_mask = 0x03e0; + header.blue_mask = 0x001f; + bm->rowsize = nw*2; + } + else if (sf->bpp == BPP_32 || sf->bpp == BPP_24) { + header.red_mask = 0x00ff0000; + header.green_mask = 0x0000ff00; + header.blue_mask = 0x000000ff; + bm->rowsize = nw * 4; + } + + bm->hbm = CreateDIBSection(GDI_DATA(hOffscreenDC), (BITMAPINFO *)&header,DIB_RGB_COLORS,&bm->data,NULL,0); + } + + if (!bm->hbm) { + ddgr_PushError("CreateDIBSection failed."); + delete bm; + return false; + } + bm->hwnd = NULL; // This is set by application + + sf->obj = (void *)bm; + + return true; +} + + +// --------------------------------------------------------------------------- +// destroys the DIB allocated from create + +void ddgr_gdi_surf_Destroy(ddgr_surface *sf) +{ + tGDISurface *bm = (tGDISurface *)sf->obj; + + ASSERT(bm->hbm); + + DeleteObject(bm->hbm); + + delete bm; +} + +// Clears a GDI surface. +void ddgr_gdi_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h) +{ + HBITMAP old_bmp; + HBRUSH clr_brush, old_brush; + HDC hdc; + tGDISurface *dbm = (tGDISurface *)dsf->obj; + int red,green,blue; + ddgr_color color = col; + + switch (dsf->bpp) + { + case BPP_16: + red = GR_COLOR_RED(color); + green = GR_COLOR_GREEN(color); + blue = GR_COLOR_BLUE(color); + break; + + case BPP_32: + case BPP_24: + red = (color & 0x00ff0000) >> 16; + green = (color & 0x0000ff00) >> 8; + blue = (color & 0x000000ff); + break; + + default: + Int3(); // BAD + } + + clr_brush = CreateSolidBrush(RGB((BYTE)red,(BYTE)green,(BYTE)blue)); + +// Are we clearing the primary display? + if (dbm->hbm == NULL) { + if (dbm->hwnd) hdc = GetDC(dbm->hwnd); + else hdc = GetDC(GDI_DATA(hPrimaryWnd)); + } + else { + hdc = GDI_DATA(hOffscreenDC); + old_bmp = (HBITMAP)SelectObject(hdc, dbm->hbm); + } + old_brush = (HBRUSH)SelectObject(hdc, clr_brush); + + Rectangle(hdc, l,t,l+w,t+h); + + SelectObject(hdc, old_brush); + + if (dbm->hbm == NULL) { + // do if clearing was done on primary display. + if (dbm->hwnd) ReleaseDC(dbm->hwnd, hdc); + else ReleaseDC(GDI_DATA(hPrimaryWnd), hdc); + } + else { + SelectObject(hdc, old_bmp); + } + + DeleteObject(clr_brush); +} + + +// --------------------------------------------------------------------------- +// blts one GDI bitmap to another. Ability to blt directly to the screen is also +// supported. + +bool ddgr_gdi_surf_Blt(ddgr_surface *dsf, int dx, int dy, ddgr_surface *ssf, int sx, int sy, int sw, int sh) +{ + HBITMAP old_bmp; + HBITMAP old_bmp2; + HDC hdc_dest; + tGDISurface *dbm = (tGDISurface *)dsf->obj; + tGDISurface *sbm = (tGDISurface *)ssf->obj; + +// note that if our destination bitmap is the 'screen', then we use the GDI hwnd DC +// else we use the DC of the window specified in the destination. + if (dbm->hbm == NULL) { + if (dbm->hwnd == NULL) hdc_dest = GetDC(GDI_DATA(hPrimaryWnd)); + else hdc_dest = GetDC(dbm->hwnd); + } + else { + hdc_dest = CreateCompatibleDC(NULL); + old_bmp2 = (HBITMAP)SelectObject(hdc_dest, dbm->hbm); + } + + old_bmp = (HBITMAP)SelectObject(GDI_DATA(hOffscreenDC), sbm->hbm); + + BOOL bltres = BitBlt(hdc_dest, dx, dy, sw, sh, GDI_DATA(hOffscreenDC), sx,sy,SRCCOPY); + + SelectObject(GDI_DATA(hOffscreenDC), old_bmp); + + if (dbm->hbm == NULL) { + if (dbm->hwnd == NULL) ReleaseDC(GDI_DATA(hPrimaryWnd), hdc_dest); + else ReleaseDC(dbm->hwnd, hdc_dest); + } + else { + SelectObject(hdc_dest, old_bmp2); + DeleteDC(hdc_dest); + } + + return (bltres) ? true: false; +} + + +// --------------------------------------------------------------------------- +// simply extracts the data and rowsize from the DDGR object in 'sf' + +bool ddgr_gdi_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize) +{ + tGDISurface *bm = (tGDISurface *)sf->obj; + + *ptr = bm->data; + *rowsize = bm->rowsize; + + return true; +} + + +// --------------------------------------------------------------------------- +// invalidates the data and rowsize in 'sf' + +bool ddgr_gdi_surf_Unlock(ddgr_surface *sf, void *ptr) +{ + return true; +} + + +// --------------------------------------------------------------------------- +// attaches a window handle to this surface. useful in clearing and blting to +// screens. + +void ddgr_gdi_surf_AttachHandle(ddgr_surface *sf, unsigned handle) +{ + tGDISurface *gbm = (tGDISurface *)sf->obj; + + gbm->hwnd = (HWND)handle; +} + diff --git a/dd_grwin32/ddgrWin32GDI.h b/dd_grwin32/ddgrWin32GDI.h new file mode 100644 index 000000000..98097ae86 --- /dev/null +++ b/dd_grwin32/ddgrWin32GDI.h @@ -0,0 +1,131 @@ +/* + * $Logfile: /DescentIII/Main/ddgr_win32/ddgrWin32GDI.h $ + * $Revision: 2 $ + * $Date: 9/12/97 4:13p $ + * $Author: Samir $ + * + * DDGR v2.0 GDI(X) implementation,. + * + * $Log: /DescentIII/Main/ddgr_win32/ddgrWin32GDI.h $ + * + * 2 9/12/97 4:13p Samir + * Added some private data access functions and more DirectX + * functionality. + * + * 1 6/12/97 6:34p Samir + * initial revision + * + * $NoKeywords: $ + */ + + +#ifndef DDGRWIN32GDI_H +#define DDGRWIN32GDI_H + +#define WIN32_LEAN_AND_MEAN +#include + +#include "ddgr.h" +#include "ddraw.h" + +class oeApplication; + + +/* Data structures +*/ +#define GDI_MAX_DEVMODES 30 +typedef struct tDDGRGDIInternalData // Internal data for GDI subsystem +{ + bool init; // is library initialized + bool ddraw; // do we use DirectDraw for display mode changing. + LPDIRECTDRAW lpDD; // DirectDraw Object for video manipulation + HWND hPrimaryWnd; // window owning display + HDC hOffscreenDC; // DC for our generic surfaces. + bool fullscreen; // Should we run in fullscreen? + int vidrefs; // Number of surface references to hPrimaryWnd. + int olddispmode; // old display mode for GDI subsystem only (not GDIX) + DEVMODE devmodes[GDI_MAX_DEVMODES];// Device modes for GDI subsystem (not GDIX) +} +tDDGRGDIInternalData; + +typedef struct tGDISurface +{ + HBITMAP hbm; // windows DIB section bitmap handle + void *data; // pointer to bitmap bits + bool backbuffer; // is this a dual-page surface? (hbm is offscreen) + int rowsize; // rowsize of bitmap bits array + HWND hwnd; // window attached to surface +} +tGDISurface; + +typedef struct tDIBHeader // used to create and manipulate DIBs (a 16/32BPP surface only) +{ + BITMAPINFOHEADER bmi; + DWORD red_mask; + DWORD green_mask; + DWORD blue_mask; +} gdibmp_header; + +typedef struct tDIBHeader8 // used to create and manipulate DIBs (a 16/32BPP surface only) +{ + BITMAPINFOHEADER bmi; + RGBQUAD rgb[256]; +} tDIBHeader8; + + +/* Externs */ +extern tDDGRGDIInternalData DDGR_GDI_lib_data; + + +/* Macros */ +#define GDI_DATA(_c) DDGR_GDI_lib_data._c + + +/* Functions +*/ + +// Initializes GDI subsystem +bool ddgr_gdi_Init(oeApplication *app, bool fullscreen, bool ddraw); + +// Closes GDI subsystem +void ddgr_gdi_Close(); + +// Initializes the display for use with the GDI subsystem. +bool ddgr_gdi_surf_InitVideo(ddgr_surface *sf); + +// Deinitializes the video surface +void ddgr_gdi_surf_CloseVideo(ddgr_surface *sf); + +// flips the buffers in a surface. really only useful for video screens +bool ddgr_gdi_surf_FlipVideo(ddgr_surface *sf); + +/* input: + sf->name is optional. + sf->w, sf->h, sf->bpp are mandatory + sf->type and sf->flags are mandatory. + output: + sf->obj = surface object. + sf->locks = 0 +*/ +bool ddgr_gdi_surf_Create(ddgr_surface *sf); +void ddgr_gdi_surf_Destroy(ddgr_surface *sf); + +/* retrieves a pointer to surface memory. allowed to lock one surface multiple times. + ptr is the returned pointer to surface memory. used to unlock surface also + rowsize is the size in bytes of one row of memory. +*/ +bool ddgr_gdi_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize); +bool ddgr_gdi_surf_Unlock(ddgr_surface *sf, void *ptr); + +// attaches an OS handle to a surface +void ddgr_gdi_surf_AttachHandle(ddgr_surface *sf, unsigned handle); + +// The only graphic primatives +// clear +// blt. +void ddgr_gdi_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h); +bool ddgr_gdi_surf_Blt(ddgr_surface *dsf, int dx, int dy, ddgr_surface *ssf, int sx, int sy, int sw, int sh); + + +#endif + diff --git a/dd_grwin32/ddgrWin32Init.cpp b/dd_grwin32/ddgrWin32Init.cpp new file mode 100644 index 000000000..4fbd107bb --- /dev/null +++ b/dd_grwin32/ddgrWin32Init.cpp @@ -0,0 +1,217 @@ +/* + * $Logfile: /DescentIII/Main/ddgr_win32/ddgrWin32Init.cpp $ + * $Revision: 6 $ + * $Date: 11/01/98 1:58a $ + * $Author: Jeff $ + * + * DDGR v2.0 Win32 Implementation. + * + * $Log: /DescentIII/Main/ddgr_win32/ddgrWin32Init.cpp $ + * + * 6 11/01/98 1:58a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 5 3/18/98 8:03p Samir + * if library aint initialized don't try to close! + * + * 4 12/23/97 6:16p Samir + * Moved ddgr_Close to header. + * + * 3 9/12/97 4:13p Samir + * Added some private data access functions and more DirectX + * functionality. + * + * 2 6/16/97 4:46p Samir + * OpenGL works in window too. + * + * 1 6/12/97 6:33p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "ddgrWin32.h" +#include "ddgrWin32GDI.h" +#include "ddgrWin32DX.h" +#include "pserror.h" +#include "pstring.h" + +#include +#include +#include +#include + + +/* DDGR_WIN32 Library + v2.0 enhancements = concept of graphic subsystem when initializing. + removal of blting routines. reduced role in + graphic implementation (video and bitmaps management) + + subsystems supported: + GDI32 (fullscreen/windowed) +*/ +char *DDGR_subsystem_names[] = { + "GDI32", // Standard Win32 GDI DIBs + "GDIX", // GDI and DirectDraw for mode settings. + "DX", // Direct X! + NULL +}; + +int DDGR_subsystems[] = { + DDGR_GDI_SUBSYSTEM, + DDGR_GDIX_SUBSYSTEM, + DDGR_DX_SUBSYSTEM, + -1 +}; + +tDDGRInternalData DDGR_lib_data; // Library info. +//HACKS!! + + +static int DDGR_subsysid = 0; +static bool DDGR_fullscreen; + +/* Functions internal to the Init module +*/ + +/* ddgr_Init + app = application object. + subsystem = subsystem name ('DirectDraw', 'GDI') + fullscreen = whether it's full screen or windowed +*/ +bool ddgr_Init(oeApplication *app, char *subsystem, bool fullscreen) +{ + static bool first_time=true; + int subsys_id; + +// close old systems + if (first_time) { + atexit(ddgr_Close); + first_time = false; + } + else if (LIB_DATA(init)) { + ddgr_Close(); + } + +// find subsystem id based off of subsystem requested. + for (subsys_id = 0; DDGR_subsystems[subsys_id] != -1; subsys_id++) + { + if (strcmp(DDGR_subsystem_names[subsys_id], subsystem) == 0) + break; + } + + if (DDGR_subsystems[subsys_id] == -1) + ddgr_FatalError("Subsystem %s not found during startup.", subsystem); + + LIB_DATA(subsystem) = DDGR_subsystems[subsys_id]; + +// Initialize that subsystem + switch (DDGR_subsystems[subsys_id]) + { + case DDGR_GDI_SUBSYSTEM: ddgr_gdi_Init(app, fullscreen, false); break; + case DDGR_GDIX_SUBSYSTEM: ddgr_gdi_Init(app, fullscreen, true); break; + case DDGR_DX_SUBSYSTEM: ddgr_dx_Init(app); break; + + default: + Int3(); + } + + LIB_DATA(init) = true; + DDGR_subsysid = subsys_id; + DDGR_fullscreen = fullscreen; + + return true; +} + + +void ddgr_Close() +{ + if (!LIB_DATA(init)) + return; + + switch (LIB_DATA(subsystem)) + { + case DDGR_GDIX_SUBSYSTEM: + case DDGR_GDI_SUBSYSTEM: ddgr_gdi_Close(); break; + case DDGR_DX_SUBSYSTEM: ddgr_dx_Close(); break; + default: Int3(); + } + + LIB_DATA(subsystem) = 0; + LIB_DATA(init) = false; +} + + +void ddgr_GetSubsystem(char *name, bool *fullscreen) +{ + strcpy(name, DDGR_subsystem_names[DDGR_subsysid]); + *fullscreen = DDGR_fullscreen; +} + + +/* DDGR Internal Error System + This error system will display an error message when a fatal graphics problem occurs. + To display error properly when video screen is in a special mode, we must close down all + graphics and show this error. We also handle multiple errors, so we keep a stack of + error messages, and show them when we're finished. +*/ + +#define DDGR_MAX_ERRORS 10 + +static char *DDGR_error_msgs[DDGR_MAX_ERRORS]; +static int DDGR_num_msgs=0; + +void ddgr_FatalError(char *fmt, ...) +{ +// create our error list and flag a system error + char buf[768]; + +// get subsystem name. + int i; + for (i = 0; DDGR_subsystems[i] != -1; i++) + { + if (DDGR_subsystems[i] == LIB_DATA(subsystem)) { + break; + } + } + +// create whole error message + va_list arglist; + + va_start(arglist,fmt); + Pvsprintf(buf,768,fmt,arglist); + va_end(arglist); + + if (DDGR_subsystems[i] =! -1) { + strcat(buf, "\n\nSubsystem: "); + strcat(buf, DDGR_subsystem_names[i]); + } + strcat(buf, "\n\nError stack:"); + for (i = 0; i < DDGR_num_msgs; i++) + { + strcat(buf, "\n"); + strcat(buf, DDGR_error_msgs[i]); + delete[] DDGR_error_msgs[i]; + } + + Error(buf); +} + + +void ddgr_PushError(char *fmt, ...) +{ + va_list arglist; + char buf[128]; + + va_start(arglist,fmt); + Pvsprintf(buf,128,fmt,arglist); + va_end(arglist); + + DDGR_error_msgs[DDGR_num_msgs] = new char[strlen(buf)+1]; + strcpy(DDGR_error_msgs[DDGR_num_msgs], buf); + DDGR_num_msgs++; +} + + + diff --git a/dd_lnxsound/CMakeLists.txt b/dd_lnxsound/CMakeLists.txt new file mode 100644 index 000000000..e11d0e6f9 --- /dev/null +++ b/dd_lnxsound/CMakeLists.txt @@ -0,0 +1,4 @@ +SET (HEADERS ddlnxsound.h ) +SET (CPPS mixer.cpp sdlsound.cpp ../dd_sndlib/ssl_lib.cpp ../dd_sndlib/ddsoundload.cpp) + +ADD_LIBRARY(dd_lnxsound STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/dd_lnxsound/ddlnxsound.h b/dd_lnxsound/ddlnxsound.h new file mode 100644 index 000000000..5b802bb5f --- /dev/null +++ b/dd_lnxsound/ddlnxsound.h @@ -0,0 +1,58 @@ +#ifndef __LNX__DD_SOUND_H_ +#define __LNX__DD_SOUND_H_ + +//#include +#include "SDL_thread.h" + +// Sound Library Internal Error Codes +#define SSL_OK 0 +#define SSL_ERROR_GENERIC -1 +#define SSL_ERROR_SAMPLE_NODATA -2 +#define SSL_ERROR_STREAMMIXER -3 + +// Sound Status +#define SSF_UNUSED 0 +#define SSF_PLAY_NORMAL 1 +#define SSF_PLAY_LOOPING 2 +#define SSF_PAUSED 4 +#define SSF_PLAY_STREAMING 8 +#define SSF_BUFFERED_LOOP 64 +#define SSF_BUFFERED_STRM 128 + + +#define SBT_PRIMARY 0 +#define SBT_2D 1 +#define SBT_3D 2 + +// looping methods +#define DSLOOP_SMART_METHOD 0 +#define DSLOOP_BUFFER_METHOD 1 +#define DSLOOP_STREAM_METHOD 2 + +#define DSBUFLOOP_INIT_STEP -1 +#define DSBUFLOOP_LOOP_STEP 0 +#define DSBUFLOOP_FINISH_STEP 1 + +// used to time threads. +#define DSPB_TICK_INTERVAL .01 // Primary buffer update rate (in seconds) +#define DSPB_TICK_MILLISECONDS (DSPB_TICK_INTERVAL * 1000) + +typedef struct LNXSTREAMTAG +{ +// pthread_t thread_id; + SDL_Thread *thread_id; + unsigned long thread_handle; + volatile bool thread_request_kill; + volatile bool thread_alive; + volatile bool thread_waiting_for_death; + + int *sound_device; + + void (*fp_SetError)(int code); + void (*fp_ErrorText)(char *fmt, ... ); + int *p_error_code; +} LNXSTREAM; + +#endif + + diff --git a/dd_lnxsound/lnxsound.cpp b/dd_lnxsound/lnxsound.cpp new file mode 100644 index 000000000..a87516b82 --- /dev/null +++ b/dd_lnxsound/lnxsound.cpp @@ -0,0 +1,1347 @@ +/* +* $Logfile: /DescentIII/Main/dd_lnxsound/lnxsound.cpp $ +* $Revision: 1.3 $ +* $Date: 2000/06/24 01:15:15 $ +* $Author: icculus $ +* +* Low-level linux sound driver +* +* $Log: lnxsound.cpp,v $ +* Revision 1.3 2000/06/24 01:15:15 icculus +* patched to compile. +* +* Revision 1.2 2000/05/29 05:17:52 icculus +* Now uses SDL threads instead of pthreads (but NOT SDL audio). Other +* fixes, too. +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 18 10/17/99 3:47p Jeff + * added a function to get sound config parameters + * + * 17 8/26/99 7:25p Jeff + * improved sound. Sounds pretty good now...worried about constant low + * frame rate though (sub 10) + * + * 16 8/24/99 7:57p Jeff + * sound working very well now...although there is a small pause every 2 + * seconds... + * + * 15 8/24/99 4:28a Jeff + * no more circular buffers, just take mixer output and write it right + * away. + * + * 14 8/23/99 4:17a Jeff + * barely 'correct' sound implements. A little static still and some + * sounds don't play (3d issue?) + * + * 13 8/21/99 2:55a Jeff + * fixed typo bug + * + * 12 8/20/99 7:21p Jeff + * sound! well, a little, still pretty buggy, but it's something + * + * 11 8/20/99 1:34a Jeff + * blah...got disconnected checking in the file + * + * 9 8/19/99 7:11p Jeff + * initialize sound library and start mixing thread + * + * 8 8/17/99 3:11p Jeff + * added llsGeometry::Clear + * + * 7 8/17/99 2:36p Jeff + * updated for new geometry functions + * + * 6 4/17/99 2:11a Jeff + * added low level geometry stubs + * + * 5 4/17/99 1:52a Jeff + * commented out Sounds[], included in ddsoundload.cpp + * + * 4 4/17/99 1:15a Jeff + * added SetGlobalReverbProperties + * + * 3 4/16/99 4:00a Jeff + * declare some needed globals + * + * 2 4/14/99 1:55a Jeff + * fixed case mismatched #includes + * + * 1 1/15/99 4:02a Jeff +* +* $NoKeywords: $ +*/ + +// NEED THIS SINCE DDSNDLIB is a DD library. +#include "DDAccess.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CFILE.H" +#include "pserror.h" +#include "mono.h" +#include "soundload.h" +#include "ssl_lib.h" +#include "mem.h" +#include "application.h" +#include "linux/lnxsound.h" +#include "ddlnxsound.h" +#include "mixer.h" +#include "ddio.h" +#include "SDL.h" +#include "SDL_thread.h" + +#define SOUNDLIB_SAMPLE_RATE 22050 +#define SOUNDLIB_SAMPLE_SIZE 16 +#define SOUNDLIB_CHANNELS 2 + +/* +// =============================== +// pthread library functions +// =============================== +#include + +typedef int (*pthread_create_fp)(pthread_t *__thread,__const pthread_attr_t *__attr,void *(*__start_routine) (void *),void *__arg); +typedef void (*pthread_exit_fp)(void *__retval); +typedef int (*pthread_detach_fp)(pthread_t __th); +typedef pthread_t (*pthread_self_fp)(void); + +pthread_create_fp dpthread_create = NULL; +pthread_exit_fp dpthread_exit = NULL; +pthread_detach_fp dpthread_detach = NULL; +pthread_self_fp dpthread_self = NULL; +void *pthread_lib = NULL; +*/ + +// =============================== +#define MAX_SOUNDS_PLAYING_AT_ONCE 256 +static sound_buffer_info sound_cache[MAX_SOUNDS_PLAYING_AT_ONCE]; +static int sound_buffer_size = MAX_SOUNDS_PLAYING_AT_ONCE; + +LNXSTREAM m_sb_info; +lnxsound *ll_sound_ptr; + +class lnxsound_buffer : public sound_buffer +{ +public: + // if sound_device is -1 then it is a primary buffer + lnxsound_buffer(int buffer_size,int *sound_device=NULL); + ~lnxsound_buffer(); + + int GetNumBufferBytes(void); + void Write(unsigned char *buffer,int amount); + +private: + int *m_sound_device; + int m_buffer_size; +}; + +// A peroidic mixer that uses the primary buffer as a stream buffer +int StreamTimer(void *user_ptr); + +lnxsound::lnxsound():llsSystem() +{ +/* + pthread_lib = NULL; + dpthread_create = NULL; + dpthread_exit = NULL; + dpthread_detach = NULL; + dpthread_self = NULL; +*/ + ll_sound_ptr = this; + sound_device = -1; + in_at_exit = false; +} + +lnxsound::~lnxsound() +{ + in_at_exit = true; + DestroySoundLib(); + SetSoundCard(NULL); +} + +// Starts the sound library, maybe have it send back some information -- 3d support? +int lnxsound::InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played) +{ + sound_device = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0); + if (sound_device < 0) + mprintf((0, "Sound: Unable to open /dev/dsp !")); + else + { + //success + mprintf((0, "Sound: /dev/dsp opened for output. Configuring...")); + + fcntl(sound_device, F_SETFL, 0); // remove nonblock flag. + + //setup the soundcard + int arg,status; + + int tmp = 11 | (2<<16); + status = ioctl(sound_device,SNDCTL_DSP_SETFRAGMENT,&tmp); + if(status) + { + Error("Sound: Unable to optimize fragment size\n"); + close(sound_device); + sound_device = -1; + } + + status = ioctl(sound_device,SNDCTL_DSP_RESET,0); + if(status==-1) + { + Error("Sound: Unable to reset sound system\n"); + close(sound_device); + sound_device = -1; + } + + // set sample size + arg = SOUNDLIB_SAMPLE_SIZE; + status = ioctl(sound_device,SOUND_PCM_WRITE_BITS,&arg); + if(status==-1) + { + Error("Sound: Unable to set sample size (try --nosound)\n"); + close(sound_device); + sound_device = -1; + } + if(arg!=SOUNDLIB_SAMPLE_SIZE) + { + Error("Sound: Unable to set sample size to %d bits (try --nosound)\n",SOUNDLIB_SAMPLE_SIZE); + close(sound_device); + sound_device = -1; + } + + // set the data type + arg = AFMT_S16_LE; + status = ioctl(sound_device,SOUND_PCM_SETFMT,&arg); + if(status==-1) + { + Error("Sound: Unable to set data type (try --nosound)\n"); + close(sound_device); + sound_device = -1; + } + if(arg!=AFMT_S16_LE) + { + Error("Sound: Unable to set data type to signed 16bit Little Endian (try --nosound)\n"); + close(sound_device); + sound_device = -1; + } + + // set channels + arg = SOUNDLIB_CHANNELS; + status = ioctl(sound_device,SOUND_PCM_WRITE_CHANNELS,&arg); + if(status==-1) + { + Error("Sound: Unable to set channels (try --nosound)\n"); + close(sound_device); + sound_device = -1; + } + if(arg!=SOUNDLIB_CHANNELS) + { + Error("Sound: Unable to set channels to %s (try --nosound)\n",(SOUNDLIB_CHANNELS==2)?"stereo":"mono"); + close(sound_device); + sound_device = -1; + } + + // set sample rate + arg = SOUNDLIB_SAMPLE_RATE; + status = ioctl(sound_device,SOUND_PCM_WRITE_RATE,&arg); + + mprintf((0, "Sound: Sample rate was set to %d.", arg)); + + if(status==-1) + { + Error("Sound: Unable to set sample rate (try --nosound)\n"); + close(sound_device); + sound_device = -1; + } + + if ((arg < SOUNDLIB_SAMPLE_RATE - 50) && (arg > SOUNDLIB_SAMPLE_RATE + 50)) + { + Error("Sound: Unable to set sample rate to ~%d (try --nosound)\n",SOUNDLIB_SAMPLE_RATE); + close(sound_device); + sound_device = -1; + } + + if (sound_device >= 0) + { + mprintf((0, "Sound: Hardware configured. Kicking off stream thread...")); + StartStreaming(); + m_total_sounds_played = 0; + m_cur_sounds_played = 0; + m_in_sound_frame = false; + m_pending_actions = false; + m_cache_stress_timer = 0.0f; + m_timer_last_frametime = -1; + m_sound_quality = SQT_HIGH; + } // if + } + + return (sound_device>=0)?1:0; +} + +bool lnxsound::GetDeviceSettings(int *device,unsigned int *freq,unsigned int *bit_depth,unsigned int *channels) +{ + if(sound_device==-1) + return false; + + *device = sound_device; + *freq = SOUNDLIB_SAMPLE_RATE; + *bit_depth = SOUNDLIB_SAMPLE_SIZE; + *channels = SOUNDLIB_CHANNELS; + + return true; +} + +// Cleans up after the Sound Library +void lnxsound::DestroySoundLib(void) +{ + EndStreaming(); + if(sound_device > 0) + { + close(sound_device); + sound_device = -1; + } +} + +// Locks and unlocks sounds (used when changing play_info data) +bool lnxsound::LockSound(int sound_uid) +{ + return false; +} + +bool lnxsound::UnlockSound(int sound_uid) +{ + return false; +} + +bool lnxsound::SetSoundQuality(char quality) +{ + int i; + + if(quality == m_sound_quality) + return true; + + // pause any sounds that may be playing + PauseSounds(); + + if(quality == SQT_NORMAL) + { + m_sound_quality = SQT_NORMAL; + } + else + { + m_sound_quality = SQT_HIGH; + } + + + for(i = 0; i < MAX_SOUNDS; i++) + { + if (Sounds[i].used != 0) + { + int j = Sounds[i].sample_index; + + if(SoundFiles[j].sample_8bit && m_sound_quality == SQT_HIGH) + { + GlobalFree(SoundFiles[j].sample_8bit); + SoundFiles[j].sample_8bit = NULL; + + CheckAndForceSoundDataAlloc(i); + } + if(SoundFiles[j].sample_16bit && m_sound_quality == SQT_NORMAL) + { + int count; + + ASSERT(SoundFiles[j].sample_8bit == NULL); + SoundFiles[j].sample_8bit = (unsigned char *) GlobalAlloc(0,SoundFiles[j].sample_length); + + // NOTE: Interesting note on sound conversion: 16 bit sounds are signed (0 biase). 8 bit sounds are unsigned (+128 biase). + for(count = 0; count < (int)SoundFiles[j].sample_length; count++) + { + SoundFiles[j].sample_8bit[count] = (unsigned char)((((int)SoundFiles[j].sample_16bit[count]) + 32767) >> 8); + } + + GlobalFree(SoundFiles[j].sample_16bit); + SoundFiles[j].sample_16bit = NULL; + } + } + } + + ResumeSounds(); + + return true; +} + +char lnxsound::GetSoundQuality(void) +{ + return m_sound_quality; +} + +bool lnxsound::SetSoundMixer(char mixer_type) +{ + return true; +} + +char lnxsound::GetSoundMixer(void) +{ + return SOUND_MIXER_SOFTWARE_16; +} + +//Determines if a sound will play. Takes into account maximum allowable +//sounds. +//Also put prioritization code in here +// ignore reserved slots +#ifdef _DEBUG +short lnxsound::FindFreeSoundSlot(int sound_index, float volume, int priority) +#else +short lnxsound::FindFreeSoundSlot(float volume, int priority) +#endif +{ + int current_slot; + sound_buffer_info *sb; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sb = &sound_cache[current_slot]; + if(sb->m_status == SSF_UNUSED) + { + return current_slot; + } + } + + // no more slots? take priority into account. + // throw out lowest priority sound slot (must be lower than or equal to new sound priority) + float weighted_priority = (priority*2.0f)*volume; + if (current_slot == MAX_SOUNDS_PLAYING_AT_ONCE) { + int throw_out_slot = -1, equiv_priority_slot = -1; + float weighted_priorityA, weighted_priorityB; + for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sb = &sound_cache[current_slot]; + if (!(sb->m_status & (SSF_PLAY_LOOPING+SSF_PLAY_STREAMING))) { + weighted_priorityA = sb->play_info->priority*2.0f*sb->m_volume; + if (weighted_priorityA < weighted_priority) { + if (throw_out_slot == -1) { + throw_out_slot = current_slot; + } + else { + play_information *play_info2 = sound_cache[throw_out_slot].play_info; + weighted_priorityB = play_info2->priority*2.0f*sb->m_volume; + if (weighted_priorityB > weighted_priorityA) { + throw_out_slot = current_slot; + } + } + } + + else if (equiv_priority_slot == -1 && weighted_priorityA == weighted_priority) { + equiv_priority_slot = current_slot; + } + } + } + + // if no slot found to stop, look for a slot with priority == new priority + if (throw_out_slot == -1) + { + throw_out_slot = equiv_priority_slot; + } + if (throw_out_slot > -1) { + sb = &sound_cache[throw_out_slot]; + StopSound(sb->m_unique_id, SKT_HOLD_UNTIL_STOP); + mprintf((0, "DDSNDLIB: Replace sound (p:%d) with sound (p:%d) in slot %d\n", sb->play_info->priority, priority, throw_out_slot)); + return throw_out_slot; + } + } + +#ifdef _DEBUG + if (sound_index > -1) + { + mprintf((0, "DDSNDLIB: Sound %s with priority (%d) too low.\n", Sounds[sound_index].name, priority)); + } + else + { + mprintf((0, "DDSNDLIB: Sound unknown with priority (%d) too low.\n", priority)); + } +#endif + return -1; +} + +// Plays a 2d sound +int lnxsound::PlaySound2d(play_information *play_info, int sound_index, float f_volume, float f_pan, bool f_looped) +{ + sound_buffer_info *sb=NULL; + short sound_slot; + + if (sound_device==-1) + { + return -1; + } + + // calculate volume and pan + f_volume = (f_volume < 0.0f) ? 0.0f : (f_volume > 1.0f) ? 1.0f : f_volume; + play_info->left_volume = play_info->right_volume = f_volume; + + f_pan = (f_pan < -1.0f) ? -1.0f : (f_pan > 1.0f) ? 1.0f : f_pan; + if (f_pan < 0.0) { + play_info->right_volume += f_volume*f_pan; + } + else { + play_info->left_volume -= f_volume*f_pan; + } + + // do common processing. + if (SoundFiles[Sounds[sound_index].sample_index].used == 0) { + mprintf((0, "Tryed to play %d sound, it DNE.\n", sound_index)); + return -1; + } +#ifdef _DEBUG + sound_slot = FindFreeSoundSlot(sound_index, f_volume, play_info->priority); +#else + sound_slot = FindFreeSoundSlot(f_volume, play_info->priority); +#endif + if(sound_slot < 0) + { + // do prioritization code here. + return -1; + } + sb = &sound_cache[sound_slot]; + m_total_sounds_played++; + sb->play_info = play_info; + sb->m_unique_id = MakeUniqueId(sound_slot); + sb->m_buffer_type = SBT_2D; + sb->m_sound_index = sound_index; + sb->m_status = SSF_UNUSED; + + ASSERT(sb->m_unique_id != -1); + + // play 2d sound + sb->m_status = (f_looped) ? SSF_PLAY_LOOPING : SSF_PLAY_NORMAL; + return sb->m_unique_id; +} + +// This function limits the number of sounds cached to 255(8bits) and 256 bit is for invalid channel +// The purpose is to create unique signatures for each sound played (and allow for +// the slot_number to be quickly determined) +inline int lnxsound::MakeUniqueId(int sound_slot) +{ + return ((((int)m_total_sounds_played)<<8) + sound_slot); +} + +inline int lnxsound::ValidateUniqueId(int sound_uid) +{ + if(sound_uid == sound_cache[sound_uid & 0x00FF].m_unique_id) + { + return sound_uid & 0x00FF; + } + else + { + return -1; + } +} + +int lnxsound::PlayStream(play_information *play_info) +{ + short sound_slot; + int ds_flags = 0; + + ASSERT(play_info != NULL); + + float volume = (play_info->left_volume > play_info->right_volume)?play_info->left_volume:play_info->right_volume; + + if (sound_device==-1) return -1; + +#ifdef _DEBUG + sound_slot = FindFreeSoundSlot(-1, volume, play_info->priority); +#else + sound_slot = FindFreeSoundSlot(volume, play_info->priority); +#endif + // Out of sound slots + if(sound_slot < 0) + { + return -1; + } + + m_total_sounds_played++; + sound_cache[sound_slot].play_info = play_info; + + sound_cache[sound_slot].m_unique_id = MakeUniqueId(sound_slot); + ASSERT(sound_cache[sound_slot].m_unique_id != -1); + + sound_cache[sound_slot].m_buffer_type = SBT_2D; + sound_cache[sound_slot].m_status = SSF_PLAY_STREAMING; + + m_cur_sounds_played++; + + return (sound_cache[sound_slot].m_unique_id); +} + +void lnxsound::SetListener(pos_state *cur_pos) +{ + if (sound_device==-1) return; + + m_emulated_listener.orient = *cur_pos->orient; + m_emulated_listener.position = *cur_pos->position; + m_emulated_listener.velocity = *cur_pos->velocity; +} + +int lnxsound::PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float adjusted_volume, bool f_looped, float reverb) //, unsigned short frequency +{ + short sound_slot; + int ds_flags = 0; + float volume; + + volume = adjusted_volume; // Adjust base volume by sent volume, let 3d stuff do the rest + + if (sound_device==-1) return -1; + + ASSERT(Sounds[sound_index].used != 0); + if(Sounds[sound_index].used == 0) return -1; + + float dist; + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float pan; + + dist = vm_NormalizeVector(&dir_to_sound); + if(dist < .1f) + { + dir_to_sound = m_emulated_listener.orient.fvec; + } + + if (dist >= Sounds[sound_index].max_distance) + { + return -1; + } + else if (dist > Sounds[sound_index].min_distance) + { + volume *= (1.0 - ((dist - Sounds[sound_index].min_distance) / (Sounds[sound_index].max_distance - Sounds[sound_index].min_distance))); + } + + pan = (dir_to_sound * m_emulated_listener.orient.rvec); + + if(volume < 0.0f) + volume = 0.0f; + else if(volume > 1.0f) + volume = 1.0f; + + if(pan < -1.0f) + pan = -1.0f; + else if(pan > 1.0f) + pan = 1.0f; + + return PlaySound2d(play_info, sound_index, volume, pan, f_looped); +} + +void lnxsound::AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency) +{ + int current_slot; + + if (sound_device==-1) return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return; + if(sound_cache[current_slot].m_status == SSF_UNUSED) return; + + sound_buffer_info *sb = &sound_cache[current_slot]; + play_information *play_info = sb->play_info; + + play_info->left_volume = play_info->right_volume = f_volume; + if (f_pan < 0.0) + play_info->right_volume += f_volume * f_pan; + else + play_info->left_volume -= f_volume * f_pan; +} + +void lnxsound::AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb) +{ + if (sound_device==-1) return; + + int current_slot; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return; + if(sound_cache[current_slot].m_status == SSF_UNUSED) return; + + // We need to determine the pan and volume + float volume; + + volume = adjusted_volume; + + float dist; + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float pan; + + dist = vm_NormalizeVector(&dir_to_sound); + if(dist < .1f) + { + dir_to_sound = m_emulated_listener.orient.fvec; + } + + if (dist >= Sounds[sound_cache[current_slot].m_sound_index].max_distance) + { + volume = 0.0f; + } + else if (dist > Sounds[sound_cache[current_slot].m_sound_index].min_distance) + { + volume *= (1.0 - ((dist - Sounds[sound_cache[current_slot].m_sound_index].min_distance) / (Sounds[sound_cache[current_slot].m_sound_index].max_distance - Sounds[sound_cache[current_slot].m_sound_index].min_distance))); + } + + pan = (dir_to_sound * m_emulated_listener.orient.rvec); + + if(volume < 0.0f) + volume = 0.0f; + else if(volume > 1.0f) + volume = 1.0f; + + if(pan < -1.0f) + pan = -1.0f; + else if(pan > 1.0f) + pan = 1.0f; + + AdjustSound(sound_cache[current_slot].m_unique_id, volume, pan, 22050); +} + +void lnxsound::StopAllSounds(void) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + if(sound_cache[current_slot].m_status != SSF_UNUSED) + { + StopSound(sound_cache[current_slot].m_unique_id); + } + } +} + +// Checks if a sound is playing (removes finished sound); +bool lnxsound::IsSoundInstancePlaying(int sound_uid) +{ + int current_slot; + + if (sound_device==-1) + return false; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) + return false; + + if(sound_cache[current_slot].m_status != SSF_UNUSED) + { + return true; + } + + return false; +} + +int lnxsound::IsSoundPlaying(int sound_index) +{ + int current_slot; + + if (sound_device==-1) return -1; + + for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + if((sound_cache[current_slot].m_status != SSF_UNUSED) && + (sound_cache[current_slot].m_sound_index == sound_index)) + { + return sound_cache[current_slot].m_unique_id; + } + } + + return -1; +} + +// Stops 2d and 3d sounds +void lnxsound::StopSound(int sound_uid, unsigned char f_immediately = SKT_STOP_IMMEDIATELY) +{ + int current_slot; + sound_buffer_info *sb; + + if (sound_device==-1) + return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) + return; + + sb = &sound_cache[current_slot]; + + if(sb->m_status == SSF_UNUSED) + return; + + // update sound count. + m_cur_sounds_played--; + + if(f_immediately == SKT_STOP_AFTER_LOOP) { + sb->m_status &= ~SSF_PLAY_LOOPING; + sb->m_status |= SSF_PLAY_NORMAL; + return; + } + + sound_cache[current_slot].m_status = SSF_UNUSED; +} + +// Pause all sounds/resume all sounds +void lnxsound::PauseSounds(void) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) + { + sb->m_status |= SSF_PAUSED; + } + } +} + +void lnxsound::ResumeSounds(void) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + + if(sb->m_status != SSF_UNUSED && (sb->m_status & SSF_PAUSED)) + { + sound_cache[current_slot].m_status &= (~SSF_PAUSED); + } + } +} + +void lnxsound::PauseSound(int sound_uid) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + if (sb->m_unique_id == sound_uid) { + if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) + { + sb->m_status |= SSF_PAUSED; + } + break; + } + } +} + +void lnxsound::ResumeSound(int sound_uid) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + if (sound_uid == sound_cache[current_slot].m_unique_id) { + if(sound_cache[current_slot].m_status != SSF_UNUSED && + (sound_cache[current_slot].m_status & SSF_PAUSED)) + { + sound_cache[current_slot].m_status &= (~SSF_PAUSED); + break; + } + } + } +} + +bool lnxsound::CheckAndForceSoundDataAlloc(int sound_index) +{ + int result; + int sound_file_index = Sounds[sound_index].sample_index; + +// ASSERT(sound_file_index >= 0 && sound_file_index < MAX_SOUND_FILES); + + if (sound_file_index < 0 || sound_file_index >= MAX_SOUND_FILES) { + return false; + } + + // Check if the sample data is already loaded + if(SoundFiles[sound_file_index].sample_16bit != NULL || SoundFiles[sound_file_index].sample_8bit != NULL) + return true; + + // If not, get the sound data + result = SoundLoadWaveFile(SoundFiles[sound_file_index].name, Sounds[sound_index].import_volume, sound_file_index, (m_sound_quality == SQT_HIGH), true); + + // Why would it load once (table load time) and not now? + if(!result) + return false; + + mprintf((0, "Sound %s loaded.\n", SoundFiles[sound_file_index].name)); + + return true; +} + +// Begin sound frame +void lnxsound::SoundStartFrame(void) +{ + float frame_time; + int current_slot; + int i; + + if(m_timer_last_frametime == -1) + { + frame_time = 0.0f; + } + else + { + frame_time = (timer_GetMSTime() - m_timer_last_frametime)/1000.0f; + } + m_timer_last_frametime = timer_GetMSTime(); + + // perform necessary functions if sound events are pending for frame, this doesn't have to do anything + // if the mixer doesn't require such actions. Aureal does though. + if (m_pending_actions) + { + mprintf((0, "pending actions\n")); + } + + m_in_sound_frame = true; + m_pending_actions = false; + + int counter = 0, loop_counter = 0, stream_counter=0, buf_loop_counter=0; + +#ifdef _DEBUG + int n_p5=0, n_p4=0, n_p3=0, n_p2=0, n_p1=0, n_p0=0; +#endif + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + if(sb->m_status != SSF_UNUSED) + { + counter++; + if (sb->m_status & SSF_PLAY_LOOPING) { + if (sb->m_status & SSF_BUFFERED_LOOP) buf_loop_counter++; + loop_counter++; + } + if (sb->m_status & SSF_PLAY_STREAMING) stream_counter++; + + #ifdef _DEBUG + if (sb->play_info->priority == SND_PRIORITY_CRITICAL) n_p5++; + else if (sb->play_info->priority == SND_PRIORITY_HIGHEST) n_p4++; + else if (sb->play_info->priority == SND_PRIORITY_HIGH) n_p3++; + else if (sb->play_info->priority == SND_PRIORITY_NORMAL) n_p2++; + else if (sb->play_info->priority == SND_PRIORITY_LOW) n_p1++; + else if (sb->play_info->priority == SND_PRIORITY_LOWEST) n_p0++; + #endif + } + } + + // update cache stress timer. + if (counter < (MAX_SOUNDS_PLAYING_AT_ONCE*3/4)) + { + m_cache_stress_timer += frame_time; + } + else + { + m_cache_stress_timer = 0.0f; + } + +#ifdef _DEBUG + mprintf_at((3,2,0, "LNS: %02d/%02d", counter, MAX_SOUNDS_PLAYING_AT_ONCE)); + mprintf_at((3,3,1, "Lp: %02d", loop_counter)); + mprintf_at((3,4,1, "St: %02d", stream_counter)); + mprintf_at((3,5,0, " Ot: %02d", counter-loop_counter-stream_counter)); + + mprintf_at((3,2,20, "P5:%02d P4:%02d P3:%02d", n_p5,n_p4,n_p3)); + mprintf_at((3,3,20, "P2:%02d P1:%02d P0:%02d", n_p2,n_p1,n_p0)); +#endif + +} + +// End sound frame +void lnxsound::SoundEndFrame(void) +{ + CheckForErrors(); // handles errors. + m_in_sound_frame = false; +} + +// Sound System Error Handler. +void lnxsound::CheckForErrors() +{ +// if a fatal error occurred, quit and display an error +// non fatal errors should be put inside a logfile, or just mprinted out. + switch (m_lib_error_code) + { + case SSL_ERROR_SAMPLE_NODATA: + Error("%s\nSample had no data.",m_error_text); + break; + + case SSL_ERROR_STREAMMIXER: + Error("%s\nMixer alignment check failed.", m_error_text); + break; + + case SSL_ERROR_GENERIC: + Error("%s\nGeneric error.", m_error_text); + break; + } + +// must call! + llsSystem::CheckForErrors(); +} + +// returns the error string. +char *lnxsound::GetErrorStr() const +{ + static char buffer[] = "No Error Given"; + return buffer; +} + +bool lnxsound::SetGlobalReverbProperties(float volume,float damping,float decay) +{ + return false; +} + +void lnxsound::StartStreaming(void) +{ +/* + // Load the thread library + if(!pthread_lib) + { + // load the library + pthread_lib = dlopen("libpthread.so",RTLD_NOW|RTLD_GLOBAL); + if(!pthread_lib) + { + Error("Unable to load libpthread.so\n"); + exit(1); + } + + dpthread_create = (pthread_create_fp)dlsym(pthread_lib,"pthread_create"); + if(!dpthread_create) + { + dlclose(pthread_lib); + pthread_lib = NULL; + Error("Unable to find symbol pthread_create in libpthread.so\n"); + exit(1); + } + + dpthread_exit = (pthread_exit_fp)dlsym(pthread_lib,"pthread_exit"); + if(!dpthread_exit) + { + dlclose(pthread_lib); + pthread_lib = NULL; + Error("Unable to find symbol pthread_exit in libpthread.so\n"); + exit(1); + } + + dpthread_detach = (pthread_detach_fp)dlsym(pthread_lib,"pthread_detach"); + if(!dpthread_detach) + { + dlclose(pthread_lib); + pthread_lib = NULL; + Error("Unable to find symbol pthread_detach in libpthread.so\n"); + exit(1); + } + dpthread_self = (pthread_self_fp)dlsym(pthread_lib,"pthread_self"); + if(!dpthread_self) + { + dlclose(pthread_lib); + pthread_lib = NULL; + Error("Unable to find symbol pthread_detach in libpthread.so\n"); + exit(1); + } + } +*/ + m_sb_info.thread_request_kill = false; + m_sb_info.thread_alive = false; + m_sb_info.thread_waiting_for_death = false; + m_sb_info.sound_device = &sound_device; + m_sb_info.p_error_code = &m_lib_error_code; + m_sb_info.fp_SetError = lnxsound_SetError; + m_sb_info.fp_ErrorText = lnxsound_ErrorText; + + // Start mixing thread. + +// m_sb_info.thread_handle = dpthread_create(&m_sb_info.thread_id, NULL, StreamTimer,&m_sb_info); + m_sb_info.thread_id = SDL_CreateThread(StreamTimer, &m_sb_info); + if (m_sb_info.thread_id == NULL) + m_sb_info.thread_handle = ((m_sb_info.thread_id == NULL) ? -1 : 0); + + if(m_sb_info.thread_handle!=0) + { + mprintf((0, "Thread failed\n")); + Int3(); + return; + } +} + +void lnxsound::EndStreaming(void) +{ + m_sb_info.thread_request_kill = true; + + int count = 150; + mprintf((0,"Waiting for sound thread...")); + while(count>0 && !m_sb_info.thread_waiting_for_death) + { + count--; + Sleep(50); + mprintf((0,".")); + } + mprintf((0,"Done %s\n",(count<=0)?"EARLY":"All Good")); + +/* + if(pthread_lib) + { + if(!in_at_exit) + { + //causes a segfault..BLAH! + //dlclose(pthread_lib); + } + pthread_lib = NULL; + dpthread_create = NULL; + dpthread_exit = NULL; + dpthread_self = NULL; + dpthread_detach = NULL; + } +*/ +} + +// may be called before init (must be to be valid, the card passed here will be initialized in InitSoundLib) +void lnxsound::SetSoundCard(const char *name) +{ +} + +// set special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::SetEnvironmentValues(const t3dEnvironmentValues *env) +{ +} + +// get special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::GetEnvironmentValues(t3dEnvironmentValues *env) +{ +} + +// enable special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::SetEnvironmentToggles(const t3dEnvironmentToggles *env) +{ +} + +// get states of special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::GetEnvironmentToggles(t3dEnvironmentToggles *env) +{ +} + + +void lnxsound_SetError(int code) +{ + ll_sound_ptr->SetError(code); +} + +void lnxsound_ErrorText(char *fmt, ... ) +{ + char buffer[384]; + va_list args; + va_start(args,fmt); + vsprintf(buffer,fmt,args); + va_end(args); + + ll_sound_ptr->ErrorText(buffer); +} + +// A peroidic mixer that uses the primary buffer as a stream buffer +int StreamTimer(void *user_ptr) +{ + LNXSTREAM *sb_info = (LNXSTREAM *)user_ptr; + +/* + int status; + status = dpthread_detach(dpthread_self()); + if(status!=0) + { + Error("Detach thread error %d\n",status); + exit(1); + } +*/ + + sb_info->thread_alive = true; + + int blocksize; + ioctl(*sb_info->sound_device,SNDCTL_DSP_GETBLKSIZE,&blocksize); + + mprintf((0, "Sound: Current block size is (%d).", blocksize)); + +/* + if(blocksize != 2048) + { + Error("Sound: Blocksize != 2048 bytes.\n"); + exit(1); + } // if +*/ + + software_mixer mixer; + lnxsound_buffer primary(blocksize,sb_info->sound_device); + + // setup mixer + tMixerInit mi; + mi.primary_buffer = &primary; + mi.primary_frequency = SOUNDLIB_SAMPLE_RATE; + mi.max_sounds_available = &sound_buffer_size; + mi.sound_cache = sound_cache; + mi.primary_alignment = SOUNDLIB_CHANNELS*(SOUNDLIB_SAMPLE_SIZE>>3); + mi.fp_SetError = sb_info->fp_SetError; + mi.fp_ErrorText = sb_info->fp_ErrorText; + mi.p_error_code = sb_info->p_error_code; + mi.ll_sound_ptr = ll_sound_ptr; + + if(!mixer.Initialize(&mi)) + { +// return NULL; + return(0); + } // if + + int sleep_time_i; + double sleep_time; + sleep_time = ((double)blocksize)/(4.0f*22050.0f); // in seconds + sleep_time_i = (int)(sleep_time * 1000000.0f); + + fd_set fdset; + fd_set scratchset; + struct timeval zerowait = { 0, 0 }; + int rc,mytime = 0; + + FD_ZERO(&fdset); + FD_SET(0, &fdset); + + while(!sb_info->thread_request_kill) + { + mytime = 0; + do + { + scratchset = fdset; + rc = select(FD_SETSIZE, &scratchset, 0, 0, &zerowait); + + if(rc>0) + { + mytime++; + }else if(rc<0) + { + } + }while(rc>0); + mixer.DoFrame(); + } + + mprintf((0,"Sound: Exit sound thread")); + sb_info->thread_alive = false; + sb_info->thread_waiting_for_death = true; + + return 0; +} + +lnxsound_buffer::lnxsound_buffer(int buffer_size,int *sound_device) +{ + m_sound_device = sound_device; + m_buffer_size = buffer_size; +} + +lnxsound_buffer::~lnxsound_buffer() +{ +} + +void lnxsound_buffer::Write(unsigned char *buffer,int amount) +{ + if(m_sound_device <= 0) + return; //only primary buffers supported + + ioctl(*m_sound_device,SNDCTL_DSP_POST,0); + + //slam the entire buffer + int res = write(*m_sound_device,buffer,amount); + if(res==-1) + { + mprintf((0,"ERROR WRITING SOUND BUFFER Device %d amount %d bytes\n",*m_sound_device,amount)); + return; + }else if(res!=amount) + { + mprintf((0,"ERROR: SOUND BUFFER NOT COMPLETELY WRITTEN %d\n",res)); + } +} + +int lnxsound_buffer::GetNumBufferBytes(void) +{ + return m_buffer_size; +} + +/////////////////////////////////////////////////////////////////////// +//llsGeometry +#include "ddsndgeometry.h" + +// specify a sound library to associate geometry with +bool llsGeometry::Init(llsSystem *snd_sys) +{ + return false; +} + +// closes low level geometry system. +void llsGeometry::Shutdown() +{ +} + +void llsGeometry::StartFrame() +{ +} +void llsGeometry::EndFrame() +{ +} + +// clears out geometry info +void llsGeometry::Clear() +{ +} + + +// polygon lists +// is a group cached?, check before rendering it. +void llsGeometry::IsGroupValid(int group) +{ +} + +// marks beginning of a list of polygons to render, (-1 group for non cache) +void llsGeometry::StartPolygonGroup(int group) +{ +} + +// ends a list of polygons to render. +void llsGeometry::EndPolygonGroup(int group) +{ +} + +// renders a group. +void llsGeometry::RenderGroup(int group) +{ +} + +// primatives, nv = number of verts, and verts is an array of pointers to vertices. +// you can pass a sound material value if you want special reflective properties on this polygon. +void llsGeometry::AddPoly(int nv,vector **verts, unsigned tag, tSoundMaterial material) +{ +} + +// 4 verts here. +void llsGeometry::AddQuad(unsigned tag, vector **verts) +{ +} + +// 3 verts here. +void llsGeometry::AddTriangle(unsigned tag, vector **verts) +{ +} + +void llsGeometry::CreateMaterial(tSoundMaterial material, float transmit_gain, float transmit_highfreq, float reflect_gain, float reflect_highfreq) +{ +} + +void llsGeometry::DestroyMaterial(tSoundMaterial material) +{ +} + diff --git a/dd_lnxsound/mixer.cpp b/dd_lnxsound/mixer.cpp new file mode 100644 index 000000000..c03a45729 --- /dev/null +++ b/dd_lnxsound/mixer.cpp @@ -0,0 +1,645 @@ +#include +#include +#include +#include "mono.h" +#include "ssl_lib.h" +#include "mixer.h" +#include "pserror.h" + +#define MIN_SOUND_MIX_VOLUME 0.0f +#define MAX_WRITE_AHEAD 0.04f // Seconds to write ahead of the play position (in seconds) +#define VOLUME_FIX_BITS 1024 + +inline void opti_8m_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume); +inline void opti_8s_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume); +inline void opti_16m_mix(short *cur_sample_16bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume); +inline void opti_16s_mix(short *cur_sample_16bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume); + +software_mixer::software_mixer() +{ + m_init = false; + m_buffer = NULL; +} + +software_mixer::~software_mixer() +{ + if(m_buffer) + { + free(m_buffer); + } +} + +bool software_mixer::Initialize(tMixerInit *mi) +{ + if(m_init) + { + mprintf((0,"Mixer: Already Init\n")); + Int3(); + return false; + } + + // Sam 6/29 - When using SDL, there's no primary buffer + //if(!mi->primary_buffer || !mi->ll_sound_ptr) + if(!mi->ll_sound_ptr) + { + mprintf((0,"Mixer: Bad Value passed on init\n")); + Int3(); + return false; + } + + m_init = true; + + m_ll_sound_ptr = mi->ll_sound_ptr; + m_primary_buffer = mi->primary_buffer; + + if ( m_primary_buffer ) { + m_BufferSize = m_primary_buffer->GetNumBufferBytes(); + } else { + m_BufferSize = 0; + } + + m_max_sounds_available = mi->max_sounds_available; + m_sound_cache = mi->sound_cache; + m_primary_alignment = mi->primary_alignment; + + m_error_code = mi->p_error_code; + m_fpSetError = mi->fp_SetError; + m_fpErrorText = mi->fp_ErrorText; + + if ( m_BufferSize ) { + m_buffer = (unsigned char *)malloc(m_BufferSize); + } + + return true; +} + +// A peroidic mixer that uses the primary buffer as a stream buffer +void software_mixer::DoFrame(void) +{ + if(!m_init) + return; + + int amount = m_BufferSize; + + StreamMixer((char *)m_buffer,amount); + m_primary_buffer->Write(m_buffer,amount); +} + +// The actual mixer code that sum's the sounds on each channel and does all the actual +// mixing and effects (writes data to the locked primary buffer) +void software_mixer::StreamMixer(char *ptr, int len) +{ + int i; + short *mixer_buffer16 = (short *) ptr; + int current_slot = 0; + bool f_loop; + bool f_mono; + + const int buff_len = len / m_primary_alignment; + + // this code will assure that this function will not be called when sound system is in error mode. + if (*m_error_code != SSL_OK) + { + mprintf((0,"MIX: Mixer in error code %d\n",*m_error_code)); + return; + } + + //ASSERT(len <= m_BufferSize); + ASSERT(ptr && len >= 0); + + if ((len % m_primary_alignment) != 0) + { + mprintf((0,"MIX:Len is not aligned!\n")); + (*m_fpSetError)(SSL_ERROR_STREAMMIXER); + (*m_fpErrorText)("ASSERT((len % ll_sound_ptr->m_primary_alignment) == 0)\nLen:%d PrA:%d", len, m_primary_alignment); + return; + } + + memset((char *)ptr, 0, len); + + // Mix the sound slots + while(current_slot < (*m_max_sounds_available) ) + { + sound_buffer_info *cur_buf = &m_sound_cache[current_slot]; + int num_samples = buff_len; + mixer_buffer16 = (short *) ptr; + f_mono = true; + + // Find slots with sounds in them + if((cur_buf->m_status != SSF_UNUSED) && !(cur_buf->m_status & SSF_PAUSED)) + { + float l_volume = cur_buf->play_info->left_volume; + float r_volume = cur_buf->play_info->right_volume; + int skip_interval = cur_buf->play_info->sample_skip_interval; + int samples_played = cur_buf->play_info->m_samples_played; + short *sample_16bit; + unsigned char *sample_8bit; + int np_sample_length; + int sample_length; + int loop_start; + int loop_end; + + if(cur_buf->m_status & SSF_PLAY_STREAMING) + { + switch(cur_buf->play_info->m_stream_format) + { + case SIF_STREAMING_16_M: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + sample_8bit = NULL; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size/2; + break; + case SIF_STREAMING_8_M: + sample_16bit = NULL; + sample_8bit = (unsigned char *) cur_buf->play_info->m_stream_data; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size; + break; + case SIF_STREAMING_16_S: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + sample_8bit = NULL; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size/4; + f_mono = false; + break; + case SIF_STREAMING_8_S: + sample_16bit = NULL; + sample_8bit = (unsigned char *) cur_buf->play_info->m_stream_data; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size/2; + f_mono = false; + break; + default: + Int3(); + break; + } + loop_start = 0; + loop_end = np_sample_length - 1; + } + else + { + int sound_index = cur_buf->m_sound_index; + int sample_index = Sounds[sound_index].sample_index; + sound_file_info *snd_file = &SoundFiles[sample_index]; + sample_16bit = snd_file->sample_16bit; + sample_8bit = snd_file->sample_8bit; + np_sample_length = snd_file->np_sample_length; + sample_length = snd_file->sample_length; + loop_start = Sounds[sound_index].loop_start; + loop_end = Sounds[sound_index].loop_end; + if (!sample_16bit && !sample_8bit) + { + mprintf((0, "sound file %s didn't have data for samples.\n", snd_file->name)); + } + } + + // cleanly continue if this happens, and inform a logfile, if it does. error handling + // ASSERT(sample_16bit || sample_8bit); + if (!sample_16bit && !sample_8bit) + { + sound_file_info *snd_file = &SoundFiles[Sounds[cur_buf->m_sound_index].sample_index]; + mprintf((0,"MIX: No data for %s\n",snd_file->name)); + (*m_fpSetError)(SSL_ERROR_SAMPLE_NODATA); + (*m_fpErrorText)("ASSERT(sample_16bit || sample_8bit)\nNo data found for sound file: %s", snd_file->name); + cur_buf->m_status = SSF_UNUSED; + goto error_bail; + } + + ASSERT(np_sample_length <= sample_length); + ASSERT(samples_played >= 0 || samples_played <= sample_length); + + int num_write; + + error_trap: + + // We have not looped -- yet + f_loop = false; + + // Verify the volume levels are o.k. + ASSERT(l_volume >= 0.0 && l_volume <= 1.0); + ASSERT(r_volume >= 0.0 && r_volume <= 1.0); + + looping: // Will go to this label to do the next iteration of a looping sample + + if(cur_buf->m_status & (SSF_PLAY_LOOPING | SSF_PLAY_STREAMING)) // Looping sample's process is broken up into linear pieces + { + if(f_loop) + { + ASSERT(num_write >= 0); + + num_samples -= num_write; + ASSERT(num_samples > 0); + + mixer_buffer16 += num_write << 1; // update to the new start position + // (2x because of left and right channels) + samples_played = loop_start; + } + + if(cur_buf->m_status & SSF_PLAY_LOOPING) // Looping sample's process is broken up into linear pieces + { + ASSERT(loop_end < sample_length); + if (loop_end < samples_played) + { + if(loop_end != loop_start) + { + while(loop_end < samples_played) + { + samples_played -= loop_end - loop_start; + } + + ASSERT(samples_played >= 0); + } + else + { + cur_buf->m_status &= ~SSF_PLAY_LOOPING; + cur_buf->m_status |= SSF_PLAY_NORMAL; + } + + goto error_trap; + } + } + // The number of samples to write to the primary buffer + num_write = ((num_samples) < (loop_end - samples_played + 1))?(num_samples):(loop_end - samples_played + 1); + ASSERT(num_write >= 0 && num_write <= num_samples); + + if(num_write < num_samples) f_loop = true; + else f_loop = false; + + if(num_write <= 0) + { + num_write = 0; + mprintf((0, "d")); + goto stream_done; + } + } + else + { + // The number of samples to write to the primary buffer + num_write = ((num_samples) < (np_sample_length - samples_played))?(num_samples):(np_sample_length - samples_played); + if(!(num_write > 0 && num_write <= num_samples)) + { + num_write = 0; + goto done; + } + + // Optimization for silent sounds + if(l_volume <= MIN_SOUND_MIX_VOLUME && r_volume <= MIN_SOUND_MIX_VOLUME) + { + cur_buf->play_info->m_samples_played += num_write; + goto done; + } + } + + if( (num_write < 0) || (num_write > num_samples)) // this was an assert + { + num_write = 0; + mprintf((0, "D")); + goto done; + } + + // Mix at 16 bits per sample + if(skip_interval == 0) + { + short *cur_sample_16bit = sample_16bit; + unsigned char *cur_sample_8bit = sample_8bit; + + if(f_mono) + { + if(sample_8bit) + { + cur_sample_8bit += samples_played; + opti_8m_mix(cur_sample_8bit, num_write, samples_played, mixer_buffer16, l_volume, r_volume); + } + else + { + cur_sample_16bit += samples_played; + opti_16m_mix(cur_sample_16bit, num_write, samples_played, mixer_buffer16, l_volume, r_volume); + } + } + else + { + if(sample_8bit) + { + cur_sample_8bit += (samples_played<<1); + opti_8s_mix(cur_sample_8bit, num_write, samples_played, mixer_buffer16, l_volume, r_volume); + } + else + { + cur_sample_16bit += (samples_played<<1); + opti_16s_mix(cur_sample_16bit, num_write, samples_played, mixer_buffer16, l_volume, r_volume); + } + } + } + else + // Account for lower-sampling rate + { + if(skip_interval == 1) + { + for(i = 0; i < (num_write << 1); i += 2) + { + int sample; + + if(sample_16bit) + { + if(samples_played & 0x0001) + { + sample = ((int)sample_16bit[samples_played ^ 0x0001] + + (int)sample_16bit[samples_played + 1]) >> 1; + } + else + sample = sample_16bit[samples_played]; + } + else + { + if(samples_played & 0x0001) + { + // Notes: (<<7) is from a (<<8) - (>>1) + // Notes: (-256) is from (-128) + (-128) + sample = ((int)sample_8bit[samples_played ^ 0x0001] + (int)sample_8bit[samples_played + 1] - 256) << 7; + } + else + sample = (((int)sample_8bit[samples_played]) - (int)128) << 8; + } + + samples_played++; + + ASSERT(i >= 0 && (i + 1 < num_samples * 2)); + + int l_sample = mixer_buffer16[i] + (sample * l_volume); + int r_sample = mixer_buffer16[i + 1] + (sample * r_volume); + + if(l_sample < -32767) l_sample = -32767; + if(l_sample > 32767) l_sample = 32767; + if(r_sample < -32767) r_sample = -32767; + if(r_sample > 32767) r_sample = 32767; + + mixer_buffer16[i] = (short)l_sample; + mixer_buffer16[i + 1] = (short)r_sample; + } + } + else + { + for(i = 0; i < (num_write << 1); i += 2) + { + int sample; + const int mod_pos = samples_played % 4; + + if(sample_16bit) + { + switch(mod_pos) + { + case 0: + sample = sample_16bit[samples_played]; + break; + case 1: + sample = (sample_16bit[samples_played - 1]*3 + + sample_16bit[samples_played + 3]) >> 2; + break; + case 2: + sample = (sample_16bit[samples_played - 2] + + sample_16bit[samples_played + 2]) >> 1; + break; + case 3: + sample = (sample_16bit[samples_played - 3] + + sample_16bit[samples_played + 1]*3) >> 2; + break; + } + } + else + { + switch(mod_pos) + { + case 0: + sample = ((((int)sample_8bit[samples_played]) - 128) << 8); + break; + case 1: + sample = (((((int)sample_8bit[samples_played - 1]) - 128) << 8)*3 + + ((((int)sample_8bit[samples_played + 3]) - 128) << 8)) >> 2; + break; + case 2: + sample = (((((int)sample_8bit[samples_played - 2]) - 128) << 8) + + ((((int)sample_8bit[samples_played + 2]) - 128) << 8)) >> 1; + break; + case 3: + sample = (((((int)sample_8bit[samples_played - 3]) - 128) << 8) + + ((((int)sample_8bit[samples_played + 1]) - 128) << 8)*3) >> 2; + break; + } + } + + samples_played++; + + ASSERT(i >= 0 && (i + 1 < num_samples * 2)); + + int l_sample = mixer_buffer16[i] + (sample * l_volume); + int r_sample = mixer_buffer16[i + 1] + (sample * r_volume); + + if(l_sample < -32767) l_sample = -32767; + if(l_sample > 32767) l_sample = 32767; + if(r_sample < -32767) r_sample = -32767; + if(r_sample > 32767) r_sample = 32767; + + mixer_buffer16[i] = (short)l_sample; + mixer_buffer16[i + 1] = (short)r_sample; + } + } + } + +stream_done: + + cur_buf->play_info->m_samples_played = samples_played; + + if(cur_buf->m_status & SSF_PLAY_STREAMING) + { + if(f_loop) + { + if(cur_buf->play_info->m_stream_cback && cur_buf->play_info->m_stream_data) + { + cur_buf->play_info->m_stream_data = (*cur_buf->play_info->m_stream_cback)(cur_buf->play_info->user_data, cur_buf->play_info->m_stream_handle, &cur_buf->play_info->m_stream_size); +// cur_buf->s->current_position = (char *)cur_buf->play_info->m_stream_data; +// mprintf((0, "%x %d\n", cur_buf->play_info->m_stream_data, cur_buf->play_info->m_stream_size)); + ASSERT(!(cur_buf->play_info->m_stream_data && cur_buf->play_info->m_stream_size <= 0)); +// mprintf((0, "Data %X, length %d\n", cur_buf->play_info->m_stream_data, cur_buf->play_info->m_stream_size)); + + if(cur_buf->play_info->m_stream_data) + { + switch(cur_buf->play_info->m_stream_format) + { + case SIF_STREAMING_16_M: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size/2; + break; + case SIF_STREAMING_8_M: + sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size; + break; + case SIF_STREAMING_16_S: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size/4; + break; + case SIF_STREAMING_8_S: + sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size/2; + break; + default: + Int3(); + break; + } + loop_end -= 1; + } + else + { + mprintf((0, "SE: Data is NULL\n")); + cur_buf->m_status &= ~SSF_PLAY_STREAMING; + f_loop = false; + } + } + else + { + mprintf((0, "SE: Callback/data is NULL\n")); + cur_buf->m_status &= ~SSF_PLAY_STREAMING; + f_loop = false; + } + } + } + + if (f_loop) goto looping; + + done: + + if(cur_buf->play_info->m_samples_played >= (np_sample_length) && !(cur_buf->m_status & (SSF_PLAY_LOOPING | SSF_PLAY_STREAMING))) + { + m_ll_sound_ptr->StopSound(cur_buf->m_unique_id); + } + } + + error_bail: + current_slot++; + } +} + +inline void opti_8m_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + short *mb = mixer_buffer16; + + for(i = 0; i < (num_write << 1); i += 2) + { + short sample; + int l_sample; + int r_sample; + + sample = (((short)(*cur_sample_8bit)) - (short)128) << 8; + cur_sample_8bit++; + + l_sample = *mb + (sample * l_volume); + r_sample = *(mb + 1) + (sample * r_volume); + + samples_played++; + + if(l_sample < -32767) l_sample = -32767; + if(l_sample > 32767) l_sample = 32767; + if(r_sample < -32767) r_sample = -32767; + if(r_sample > 32767) r_sample = 32767; + + *mb = (short)l_sample; + mb++; + *mb = (short)r_sample; + mb++; + } +} + +inline void opti_8s_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + short *mb = mixer_buffer16; + + for(i = 0; i < (num_write << 1); i += 2) + { + short lsample; + short rsample; + int l_sample; + int r_sample; + + lsample = (((short)(*cur_sample_8bit)) - (short)128) << 8; + cur_sample_8bit++; + rsample = (((short)(*cur_sample_8bit)) - (short)128) << 8; + cur_sample_8bit++; + + l_sample = *mb + (lsample * l_volume); + r_sample = *(mb + 1) + (rsample * r_volume); + + samples_played++; + + if(l_sample < -32767) l_sample = -32767; + if(l_sample > 32767) l_sample = 32767; + if(r_sample < -32767) r_sample = -32767; + if(r_sample > 32767) r_sample = 32767; + + *mb = (short)l_sample; + mb++; + *mb = (short)r_sample; + mb++; + } +} + +inline void opti_16m_mix(short *cur_sample_16bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + short *mb = mixer_buffer16; + + for(i = 0; i < (num_write << 1); i += 2) + { + short sample; + int l_sample; + int r_sample; + + sample = *cur_sample_16bit; + cur_sample_16bit++; + + l_sample = *mb + (sample * l_volume); + r_sample = *(mb + 1) + (sample * r_volume); + + samples_played++; + + if(l_sample < -32767) l_sample = -32767; + if(l_sample > 32767) l_sample = 32767; + if(r_sample < -32767) r_sample = -32767; + if(r_sample > 32767) r_sample = 32767; + + *mb = (short)l_sample; + mb++; + *mb = (short)r_sample; + mb++; + } +} + +inline void opti_16s_mix(short *cur_sample_16bit, const int num_write, int &samples_played, short *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + short *mb = mixer_buffer16; + + for(i = 0; i < (num_write << 1); i += 2) + { + short lsample; + short rsample; + int l_sample; + int r_sample; + + lsample = *cur_sample_16bit; + cur_sample_16bit++; + rsample = *cur_sample_16bit; + cur_sample_16bit++; + + l_sample = *mb + (lsample * l_volume); + r_sample = *(mb + 1) + (rsample * r_volume); + + samples_played++; + + if(l_sample < -32767) l_sample = -32767; + if(l_sample > 32767) l_sample = 32767; + if(r_sample < -32767) r_sample = -32767; + if(r_sample > 32767) r_sample = 32767; + + *mb = (short)l_sample; + mb++; + *mb = (short)r_sample; + *mb++; + } +} diff --git a/dd_lnxsound/sdlsound.cpp b/dd_lnxsound/sdlsound.cpp new file mode 100644 index 000000000..d8546418a --- /dev/null +++ b/dd_lnxsound/sdlsound.cpp @@ -0,0 +1,959 @@ +// NEED THIS SINCE DDSNDLIB is a DD library. +#include "DDAccess.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CFILE.H" +#include "pserror.h" +#include "mono.h" +#include "soundload.h" +#include "ssl_lib.h" +#include "mem.h" +#include "application.h" +#include "linux/lnxsound.h" +#include "ddlnxsound.h" +#include "mixer.h" +#include "ddio.h" +#include "SDL.h" +#include "SDL_thread.h" +#include "args.h" + +#define SOUNDLIB_SAMPLE_RATE 22050 +#define SOUNDLIB_SAMPLE_SIZE 16 +#define SOUNDLIB_CHANNELS 2 +#define SOUNDLIB_DEFAULT_SAMPLES 1024 + +// =============================== +#define MAX_SOUNDS_PLAYING_AT_ONCE 256 +static sound_buffer_info sound_cache[MAX_SOUNDS_PLAYING_AT_ONCE]; +static int sound_buffer_size = MAX_SOUNDS_PLAYING_AT_ONCE; + +LNXSTREAM m_sb_info; +lnxsound *ll_sound_ptr; + +// A peroidic mixer that uses the primary buffer as a stream buffer +static void StreamAudio(void *user_ptr, Uint8 *stream, int len); + +lnxsound::lnxsound():llsSystem() +{ + ll_sound_ptr = this; + sound_device = -1; + in_at_exit = false; +} + +lnxsound::~lnxsound() +{ + in_at_exit = true; + DestroySoundLib(); + SetSoundCard(NULL); +} + +// Starts the sound library, maybe have it send back some information -- 3d support? +int lnxsound::InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played) +{ + SDL_AudioSpec spec; + + // setup mixer + tMixerInit mi; + mi.primary_buffer = NULL; + mi.primary_frequency = SOUNDLIB_SAMPLE_RATE; + mi.max_sounds_available = &sound_buffer_size; + mi.sound_cache = sound_cache; + mi.primary_alignment = SOUNDLIB_CHANNELS*(SOUNDLIB_SAMPLE_SIZE>>3); + mi.fp_SetError = lnxsound_SetError; + mi.fp_ErrorText = lnxsound_ErrorText; + mi.p_error_code = &m_lib_error_code; + mi.ll_sound_ptr = ll_sound_ptr; + + if(!m_mixer.Initialize(&mi)) + { + return false; + } // if + + int sampleCount = SOUNDLIB_DEFAULT_SAMPLES; + int sampleArgIndex = FindArg("-sdlSndSize"); + if( sampleArgIndex != 0 ) + { + const char* sampleCountStr = GetArg( sampleArgIndex + 1 ); + if( sampleCountStr ) + { + sampleCount = atoi( sampleCountStr ); + if( sampleCount <= 0 ) + { + sampleCount = SOUNDLIB_DEFAULT_SAMPLES; + } + } + } + + spec.freq = SOUNDLIB_SAMPLE_RATE; + spec.format = SOUNDLIB_SAMPLE_SIZE == 8 ? AUDIO_U8 : AUDIO_S16SYS; + spec.channels = SOUNDLIB_CHANNELS; + spec.samples = sampleCount; + spec.callback = StreamAudio; + spec.userdata = &m_mixer; + + if ( SDL_OpenAudio(&spec, NULL) < 0 ) { + return false; + } + sound_device = 1; + + mprintf((0, "Sound: Hardware configured. Kicking off stream thread...")); + SDL_PauseAudio(0); + + m_total_sounds_played = 0; + m_cur_sounds_played = 0; + m_in_sound_frame = false; + m_pending_actions = false; + m_cache_stress_timer = 0.0f; + m_timer_last_frametime = -1; + m_sound_quality = SQT_HIGH; + + return true; +} + +bool lnxsound::GetDeviceSettings(int *device,unsigned int *freq,unsigned int *bit_depth,unsigned int *channels) +{ + if(sound_device==-1) + return false; + + *device = sound_device; + *freq = SOUNDLIB_SAMPLE_RATE; + *bit_depth = SOUNDLIB_SAMPLE_SIZE; + *channels = SOUNDLIB_CHANNELS; + + return true; +} + +// Cleans up after the Sound Library +void lnxsound::DestroySoundLib(void) +{ + SDL_CloseAudio(); + sound_device = -1; +} + +// Locks and unlocks sounds (used when changing play_info data) +bool lnxsound::LockSound(int sound_uid) +{ + return false; +} + +bool lnxsound::UnlockSound(int sound_uid) +{ + return false; +} + +bool lnxsound::SetSoundQuality(char quality) +{ + int i; + + if(quality == m_sound_quality) + return true; + + // pause any sounds that may be playing + PauseSounds(); + + if(quality == SQT_NORMAL) + { + m_sound_quality = SQT_NORMAL; + } + else + { + m_sound_quality = SQT_HIGH; + } + + + for(i = 0; i < MAX_SOUNDS; i++) + { + if (Sounds[i].used != 0) + { + int j = Sounds[i].sample_index; + + if(SoundFiles[j].sample_8bit && m_sound_quality == SQT_HIGH) + { + GlobalFree(SoundFiles[j].sample_8bit); + SoundFiles[j].sample_8bit = NULL; + + CheckAndForceSoundDataAlloc(i); + } + if(SoundFiles[j].sample_16bit && m_sound_quality == SQT_NORMAL) + { + int count; + + ASSERT(SoundFiles[j].sample_8bit == NULL); + SoundFiles[j].sample_8bit = (unsigned char *) GlobalAlloc(0,SoundFiles[j].sample_length); + + // NOTE: Interesting note on sound conversion: 16 bit sounds are signed (0 biase). 8 bit sounds are unsigned (+128 biase). + for(count = 0; count < (int)SoundFiles[j].sample_length; count++) + { + SoundFiles[j].sample_8bit[count] = (unsigned char)((((int)SoundFiles[j].sample_16bit[count]) + 32767) >> 8); + } + + GlobalFree(SoundFiles[j].sample_16bit); + SoundFiles[j].sample_16bit = NULL; + } + } + } + + ResumeSounds(); + + return true; +} + +char lnxsound::GetSoundQuality(void) +{ + return m_sound_quality; +} + +bool lnxsound::SetSoundMixer(char mixer_type) +{ + return true; +} + +char lnxsound::GetSoundMixer(void) +{ + return SOUND_MIXER_SOFTWARE_16; +} + +//Determines if a sound will play. Takes into account maximum allowable +//sounds. +//Also put prioritization code in here +// ignore reserved slots +#ifdef _DEBUG +short lnxsound::FindFreeSoundSlot(int sound_index, float volume, int priority) +#else +short lnxsound::FindFreeSoundSlot(float volume, int priority) +#endif +{ + int current_slot; + sound_buffer_info *sb; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sb = &sound_cache[current_slot]; + if(sb->m_status == SSF_UNUSED) + { + return current_slot; + } + } + + // no more slots? take priority into account. + // throw out lowest priority sound slot (must be lower than or equal to new sound priority) + float weighted_priority = (priority*2.0f)*volume; + if (current_slot == MAX_SOUNDS_PLAYING_AT_ONCE) { + int throw_out_slot = -1, equiv_priority_slot = -1; + float weighted_priorityA, weighted_priorityB; + for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sb = &sound_cache[current_slot]; + if (!(sb->m_status & (SSF_PLAY_LOOPING+SSF_PLAY_STREAMING))) { + weighted_priorityA = sb->play_info->priority*2.0f*sb->m_volume; + if (weighted_priorityA < weighted_priority) { + if (throw_out_slot == -1) { + throw_out_slot = current_slot; + } + else { + play_information *play_info2 = sound_cache[throw_out_slot].play_info; + weighted_priorityB = play_info2->priority*2.0f*sb->m_volume; + if (weighted_priorityB > weighted_priorityA) { + throw_out_slot = current_slot; + } + } + } + + else if (equiv_priority_slot == -1 && weighted_priorityA == weighted_priority) { + equiv_priority_slot = current_slot; + } + } + } + + // if no slot found to stop, look for a slot with priority == new priority + if (throw_out_slot == -1) + { + throw_out_slot = equiv_priority_slot; + } + if (throw_out_slot > -1) { + sb = &sound_cache[throw_out_slot]; + StopSound(sb->m_unique_id, SKT_HOLD_UNTIL_STOP); + mprintf((0, "DDSNDLIB: Replace sound (p:%d) with sound (p:%d) in slot %d\n", sb->play_info->priority, priority, throw_out_slot)); + return throw_out_slot; + } + } + +#ifdef _DEBUG + if (sound_index > -1) + { + mprintf((0, "DDSNDLIB: Sound %s with priority (%d) too low.\n", Sounds[sound_index].name, priority)); + } + else + { + mprintf((0, "DDSNDLIB: Sound unknown with priority (%d) too low.\n", priority)); + } +#endif + return -1; +} + +// Plays a 2d sound +int lnxsound::PlaySound2d(play_information *play_info, int sound_index, float f_volume, float f_pan, bool f_looped) +{ + sound_buffer_info *sb=NULL; + short sound_slot; + + if (sound_device==-1) + { + return -1; + } + + // calculate volume and pan + f_volume = (f_volume < 0.0f) ? 0.0f : (f_volume > 1.0f) ? 1.0f : f_volume; + play_info->left_volume = play_info->right_volume = f_volume; + + f_pan = (f_pan < -1.0f) ? -1.0f : (f_pan > 1.0f) ? 1.0f : f_pan; + if (f_pan < 0.0) { + play_info->right_volume += f_volume*f_pan; + } + else { + play_info->left_volume -= f_volume*f_pan; + } + + // do common processing. + if (SoundFiles[Sounds[sound_index].sample_index].used == 0) { + mprintf((0, "Tryed to play %d sound, it DNE.\n", sound_index)); + return -1; + } +#ifdef _DEBUG + sound_slot = FindFreeSoundSlot(sound_index, f_volume, play_info->priority); +#else + sound_slot = FindFreeSoundSlot(f_volume, play_info->priority); +#endif + if(sound_slot < 0) + { + // do prioritization code here. + return -1; + } + sb = &sound_cache[sound_slot]; + m_total_sounds_played++; + sb->play_info = play_info; + sb->m_unique_id = MakeUniqueId(sound_slot); + sb->m_buffer_type = SBT_2D; + sb->m_sound_index = sound_index; + sb->m_status = SSF_UNUSED; + + ASSERT(sb->m_unique_id != -1); + + // play 2d sound + sb->m_status = (f_looped) ? SSF_PLAY_LOOPING : SSF_PLAY_NORMAL; + return sb->m_unique_id; +} + +// This function limits the number of sounds cached to 255(8bits) and 256 bit is for invalid channel +// The purpose is to create unique signatures for each sound played (and allow for +// the slot_number to be quickly determined) +inline int lnxsound::MakeUniqueId(int sound_slot) +{ + return ((((int)m_total_sounds_played)<<8) + sound_slot); +} + +inline int lnxsound::ValidateUniqueId(int sound_uid) +{ + if(sound_uid == sound_cache[sound_uid & 0x00FF].m_unique_id) + { + return sound_uid & 0x00FF; + } + else + { + return -1; + } +} + +int lnxsound::PlayStream(play_information *play_info) +{ + short sound_slot; + int ds_flags = 0; + + ASSERT(play_info != NULL); + + float volume = (play_info->left_volume > play_info->right_volume)?play_info->left_volume:play_info->right_volume; + + if (sound_device==-1) return -1; + +#ifdef _DEBUG + sound_slot = FindFreeSoundSlot(-1, volume, play_info->priority); +#else + sound_slot = FindFreeSoundSlot(volume, play_info->priority); +#endif + // Out of sound slots + if(sound_slot < 0) + { + return -1; + } + + m_total_sounds_played++; + sound_cache[sound_slot].play_info = play_info; + + sound_cache[sound_slot].m_unique_id = MakeUniqueId(sound_slot); + ASSERT(sound_cache[sound_slot].m_unique_id != -1); + + sound_cache[sound_slot].m_buffer_type = SBT_2D; + sound_cache[sound_slot].m_status = SSF_PLAY_STREAMING; + + m_cur_sounds_played++; + + return (sound_cache[sound_slot].m_unique_id); +} + +void lnxsound::SetListener(pos_state *cur_pos) +{ + if (sound_device==-1) return; + + m_emulated_listener.orient = *cur_pos->orient; + m_emulated_listener.position = *cur_pos->position; + m_emulated_listener.velocity = *cur_pos->velocity; +} + +int lnxsound::PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float adjusted_volume, bool f_looped, float reverb) //, unsigned short frequency +{ + short sound_slot; + int ds_flags = 0; + float volume; + + volume = adjusted_volume; // Adjust base volume by sent volume, let 3d stuff do the rest + + if (sound_device==-1) return -1; + + ASSERT(Sounds[sound_index].used != 0); + if(Sounds[sound_index].used == 0) return -1; + + float dist; + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float pan; + + dist = vm_NormalizeVector(&dir_to_sound); + if(dist < .1f) + { + dir_to_sound = m_emulated_listener.orient.fvec; + } + + if (dist >= Sounds[sound_index].max_distance) + { + return -1; + } + else if (dist > Sounds[sound_index].min_distance) + { + volume *= (1.0 - ((dist - Sounds[sound_index].min_distance) / (Sounds[sound_index].max_distance - Sounds[sound_index].min_distance))); + } + + pan = (dir_to_sound * m_emulated_listener.orient.rvec); + + if(volume < 0.0f) + volume = 0.0f; + else if(volume > 1.0f) + volume = 1.0f; + + if(pan < -1.0f) + pan = -1.0f; + else if(pan > 1.0f) + pan = 1.0f; + + return PlaySound2d(play_info, sound_index, volume, pan, f_looped); +} + +void lnxsound::AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency) +{ + int current_slot; + + if (sound_device==-1) return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return; + if(sound_cache[current_slot].m_status == SSF_UNUSED) return; + + sound_buffer_info *sb = &sound_cache[current_slot]; + play_information *play_info = sb->play_info; + + play_info->left_volume = play_info->right_volume = f_volume; + if (f_pan < 0.0) + play_info->right_volume += f_volume * f_pan; + else + play_info->left_volume -= f_volume * f_pan; +} + +void lnxsound::AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb) +{ + if (sound_device==-1) return; + + int current_slot; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return; + if(sound_cache[current_slot].m_status == SSF_UNUSED) return; + + // We need to determine the pan and volume + float volume; + + volume = adjusted_volume; + + float dist; + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float pan; + + dist = vm_NormalizeVector(&dir_to_sound); + if(dist < .1f) + { + dir_to_sound = m_emulated_listener.orient.fvec; + } + + if (dist >= Sounds[sound_cache[current_slot].m_sound_index].max_distance) + { + volume = 0.0f; + } + else if (dist > Sounds[sound_cache[current_slot].m_sound_index].min_distance) + { + volume *= (1.0 - ((dist - Sounds[sound_cache[current_slot].m_sound_index].min_distance) / (Sounds[sound_cache[current_slot].m_sound_index].max_distance - Sounds[sound_cache[current_slot].m_sound_index].min_distance))); + } + + pan = (dir_to_sound * m_emulated_listener.orient.rvec); + + if(volume < 0.0f) + volume = 0.0f; + else if(volume > 1.0f) + volume = 1.0f; + + if(pan < -1.0f) + pan = -1.0f; + else if(pan > 1.0f) + pan = 1.0f; + + AdjustSound(sound_cache[current_slot].m_unique_id, volume, pan, 22050); +} + +void lnxsound::StopAllSounds(void) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + if(sound_cache[current_slot].m_status != SSF_UNUSED) + { + StopSound(sound_cache[current_slot].m_unique_id); + } + } +} + +// Checks if a sound is playing (removes finished sound); +bool lnxsound::IsSoundInstancePlaying(int sound_uid) +{ + int current_slot; + + if (sound_device==-1) + return false; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) + return false; + + if(sound_cache[current_slot].m_status != SSF_UNUSED) + { + return true; + } + + return false; +} + +int lnxsound::IsSoundPlaying(int sound_index) +{ + int current_slot; + + if (sound_device==-1) return -1; + + for (current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + if((sound_cache[current_slot].m_status != SSF_UNUSED) && + (sound_cache[current_slot].m_sound_index == sound_index)) + { + return sound_cache[current_slot].m_unique_id; + } + } + + return -1; +} + +// Stops 2d and 3d sounds +void lnxsound::StopSound(int sound_uid, unsigned char f_immediately) +{ + int current_slot; + sound_buffer_info *sb; + + if (sound_device==-1) + return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) + return; + + sb = &sound_cache[current_slot]; + + if(sb->m_status == SSF_UNUSED) + return; + + // update sound count. + m_cur_sounds_played--; + + if(f_immediately == SKT_STOP_AFTER_LOOP) { + sb->m_status &= ~SSF_PLAY_LOOPING; + sb->m_status |= SSF_PLAY_NORMAL; + return; + } + + sound_cache[current_slot].m_status = SSF_UNUSED; +} + +// Pause all sounds/resume all sounds +void lnxsound::PauseSounds(void) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) + { + sb->m_status |= SSF_PAUSED; + } + } +} + +void lnxsound::ResumeSounds(void) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + + if(sb->m_status != SSF_UNUSED && (sb->m_status & SSF_PAUSED)) + { + sound_cache[current_slot].m_status &= (~SSF_PAUSED); + } + } +} + +void lnxsound::PauseSound(int sound_uid) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + if (sb->m_unique_id == sound_uid) { + if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) + { + sb->m_status |= SSF_PAUSED; + } + break; + } + } +} + +void lnxsound::ResumeSound(int sound_uid) +{ + int current_slot; + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + if (sound_uid == sound_cache[current_slot].m_unique_id) { + if(sound_cache[current_slot].m_status != SSF_UNUSED && + (sound_cache[current_slot].m_status & SSF_PAUSED)) + { + sound_cache[current_slot].m_status &= (~SSF_PAUSED); + break; + } + } + } +} + +bool lnxsound::CheckAndForceSoundDataAlloc(int sound_index) +{ + int result; + int sound_file_index = Sounds[sound_index].sample_index; + +// ASSERT(sound_file_index >= 0 && sound_file_index < MAX_SOUND_FILES); + + if (sound_file_index < 0 || sound_file_index >= MAX_SOUND_FILES) { + return false; + } + + // Check if the sample data is already loaded + if(SoundFiles[sound_file_index].sample_16bit != NULL || SoundFiles[sound_file_index].sample_8bit != NULL) + return true; + + // If not, get the sound data + result = SoundLoadWaveFile(SoundFiles[sound_file_index].name, Sounds[sound_index].import_volume, sound_file_index, (m_sound_quality == SQT_HIGH), true); + + // Why would it load once (table load time) and not now? + if(!result) + return false; + + mprintf((0, "Sound %s loaded.\n", SoundFiles[sound_file_index].name)); + + return true; +} + +// Begin sound frame +void lnxsound::SoundStartFrame(void) +{ + float frame_time; + int current_slot; + int i; + + if(m_timer_last_frametime == -1) + { + frame_time = 0.0f; + } + else + { + frame_time = (timer_GetMSTime() - m_timer_last_frametime)/1000.0f; + } + m_timer_last_frametime = timer_GetMSTime(); + + // perform necessary functions if sound events are pending for frame, this doesn't have to do anything + // if the mixer doesn't require such actions. Aureal does though. + if (m_pending_actions) + { + mprintf((0, "pending actions\n")); + } + + m_in_sound_frame = true; + m_pending_actions = false; + + int counter = 0, loop_counter = 0, stream_counter=0, buf_loop_counter=0; + +#ifdef _DEBUG + int n_p5=0, n_p4=0, n_p3=0, n_p2=0, n_p1=0, n_p0=0; +#endif + + for(current_slot = 0; current_slot < MAX_SOUNDS_PLAYING_AT_ONCE; current_slot++) + { + sound_buffer_info *sb = &sound_cache[current_slot]; + if(sb->m_status != SSF_UNUSED) + { + counter++; + if (sb->m_status & SSF_PLAY_LOOPING) { + if (sb->m_status & SSF_BUFFERED_LOOP) buf_loop_counter++; + loop_counter++; + } + if (sb->m_status & SSF_PLAY_STREAMING) stream_counter++; + + #ifdef _DEBUG + if (sb->play_info->priority == SND_PRIORITY_CRITICAL) n_p5++; + else if (sb->play_info->priority == SND_PRIORITY_HIGHEST) n_p4++; + else if (sb->play_info->priority == SND_PRIORITY_HIGH) n_p3++; + else if (sb->play_info->priority == SND_PRIORITY_NORMAL) n_p2++; + else if (sb->play_info->priority == SND_PRIORITY_LOW) n_p1++; + else if (sb->play_info->priority == SND_PRIORITY_LOWEST) n_p0++; + #endif + } + } + + // update cache stress timer. + if (counter < (MAX_SOUNDS_PLAYING_AT_ONCE*3/4)) + { + m_cache_stress_timer += frame_time; + } + else + { + m_cache_stress_timer = 0.0f; + } + +#ifdef _DEBUG + mprintf_at((3,2,0, "LNS: %02d/%02d", counter, MAX_SOUNDS_PLAYING_AT_ONCE)); + mprintf_at((3,3,1, "Lp: %02d", loop_counter)); + mprintf_at((3,4,1, "St: %02d", stream_counter)); + mprintf_at((3,5,0, " Ot: %02d", counter-loop_counter-stream_counter)); + + mprintf_at((3,2,20, "P5:%02d P4:%02d P3:%02d", n_p5,n_p4,n_p3)); + mprintf_at((3,3,20, "P2:%02d P1:%02d P0:%02d", n_p2,n_p1,n_p0)); +#endif + +} + +// End sound frame +void lnxsound::SoundEndFrame(void) +{ + CheckForErrors(); // handles errors. + m_in_sound_frame = false; +} + +// Sound System Error Handler. +void lnxsound::CheckForErrors() +{ +// if a fatal error occurred, quit and display an error +// non fatal errors should be put inside a logfile, or just mprinted out. + switch (m_lib_error_code) + { + case SSL_ERROR_SAMPLE_NODATA: + Error("%s\nSample had no data.",m_error_text); + break; + + case SSL_ERROR_STREAMMIXER: + Error("%s\nMixer alignment check failed.", m_error_text); + break; + + case SSL_ERROR_GENERIC: + Error("%s\nGeneric error.", m_error_text); + break; + } + +// must call! + llsSystem::CheckForErrors(); +} + +// returns the error string. +char *lnxsound::GetErrorStr() const +{ + static char buffer[] = "No Error Given"; + return buffer; +} + +bool lnxsound::SetGlobalReverbProperties(float volume,float damping,float decay) +{ + return false; +} + +void lnxsound::StartStreaming(void) +{ +} + +void lnxsound::EndStreaming(void) +{ +} + +// may be called before init (must be to be valid, the card passed here will be initialized in InitSoundLib) +void lnxsound::SetSoundCard(const char *name) +{ +} + +// set special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::SetEnvironmentValues(const t3dEnvironmentValues *env) +{ +} + +// get special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::GetEnvironmentValues(t3dEnvironmentValues *env) +{ +} + +// enable special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::SetEnvironmentToggles(const t3dEnvironmentToggles *env) +{ +} + +// get states of special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void lnxsound::GetEnvironmentToggles(t3dEnvironmentToggles *env) +{ +} + + +void lnxsound_SetError(int code) +{ + ll_sound_ptr->SetError(code); +} + +void lnxsound_ErrorText(char *fmt, ... ) +{ + char buffer[384]; + va_list args; + va_start(args,fmt); + vsprintf(buffer,fmt,args); + va_end(args); + + ll_sound_ptr->ErrorText(buffer); +} + +// A peroidic mixer that uses the primary buffer as a stream buffer +static void StreamAudio(void *user_ptr, Uint8 *stream, int len) +{ + ((software_mixer *)user_ptr)->StreamMixer((char *)stream, len); +} + +/////////////////////////////////////////////////////////////////////// +//llsGeometry +#include "ddsndgeometry.h" + +// specify a sound library to associate geometry with +bool llsGeometry::Init(llsSystem *snd_sys) +{ + return false; +} + +// closes low level geometry system. +void llsGeometry::Shutdown() +{ +} + +void llsGeometry::StartFrame() +{ +} +void llsGeometry::EndFrame() +{ +} + +// clears out geometry info +void llsGeometry::Clear() +{ +} + + +// polygon lists +// is a group cached?, check before rendering it. +void llsGeometry::IsGroupValid(int group) +{ +} + +// marks beginning of a list of polygons to render, (-1 group for non cache) +void llsGeometry::StartPolygonGroup(int group) +{ +} + +// ends a list of polygons to render. +void llsGeometry::EndPolygonGroup(int group) +{ +} + +// renders a group. +void llsGeometry::RenderGroup(int group) +{ +} + +// primatives, nv = number of verts, and verts is an array of pointers to vertices. +// you can pass a sound material value if you want special reflective properties on this polygon. +void llsGeometry::AddPoly(int nv,vector **verts, unsigned tag, tSoundMaterial material) +{ +} + +// 4 verts here. +void llsGeometry::AddQuad(unsigned tag, vector **verts) +{ +} + +// 3 verts here. +void llsGeometry::AddTriangle(unsigned tag, vector **verts) +{ +} + +void llsGeometry::CreateMaterial(tSoundMaterial material, float transmit_gain, float transmit_highfreq, float reflect_gain, float reflect_highfreq) +{ +} + +void llsGeometry::DestroyMaterial(tSoundMaterial material) +{ +} + diff --git a/dd_sndlib/CMakeLists.txt b/dd_sndlib/CMakeLists.txt new file mode 100644 index 000000000..b0e7c5b51 --- /dev/null +++ b/dd_sndlib/CMakeLists.txt @@ -0,0 +1,18 @@ +SET (HEADERS + auddev.h + ds3dlib_internal.h + eax.h + eax2.h + ia3dapi.h + Ia3dutil.h + vmanpset.h) +SET (CPPS + aureal3d.cpp + ddsoundload.cpp + Ds3dlib.cpp + dsound3d.cpp + eax.cpp + geometry.cpp + ssl_lib.cpp) + +ADD_LIBRARY(dd_sndlib STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/dd_sndlib/Ds3dlib.cpp b/dd_sndlib/Ds3dlib.cpp new file mode 100644 index 000000000..3cb70a994 --- /dev/null +++ b/dd_sndlib/Ds3dlib.cpp @@ -0,0 +1,3860 @@ +/* +* $Logfile: /DescentIII/Main/dd_sndlib/Ds3dlib.cpp $ +* $Revision: 149 $ +* $Date: 9/27/99 5:38p $ +* $Author: Samir $ +* +* DirectSound(2d and 3d) implementation of the Descent III low-level sound interface +* +* $Log: /DescentIII/Main/dd_sndlib/Ds3dlib.cpp $ + * + * 149 9/27/99 5:38p Samir + * EAX 2.0->1.0 compatibility checkin. + * + * 148 8/23/99 5:29p Samir + * incremental EAX 2.0 checkin + * + * 147 8/13/99 2:00p Samir + * more aureal and geometry fixes. + * + * 146 5/23/99 12:48a Samir + * decreased rolloff factor of 3d sounds under 3d sound mixers from 0.75 + * to 0.5 + * + * 145 5/20/99 6:23p Kevin + * minor speed up of software mixer and fix for memory debugging + * + * 144 5/20/99 1:00a Samir + * changes in ordering of EAX and NONE mixers. + * + * 143 5/09/99 7:09p Samir + * fixed sound card selection (enumeration) problems. + * + * 142 5/07/99 5:39p Samir + * better error checking for CheckAndForceDataAlloc for PageInSound. + * + * 141 5/03/99 3:12a Samir + * fixed up aureal so it works (a little slow though...) + * + * 140 5/01/99 10:25p Jeff + * (samir) fixed crash bug when alt-tabbing,inserting cds,etc + * + * 139 4/29/99 4:36p Kevin + * fixed a problem with low quality sounds & the optimizd software mixer + * + * 138 4/29/99 3:01p Samir + * added code for direct sound mixers only (and function for Aureal + * really) that will use direct sound looping for simple loops. + * + * 137 4/27/99 7:08p Kevin + * optimized software mixer! + * + * 136 4/27/99 6:21p Samir + * if callback gets a NULL guid, just return, assume that the sound device + * is the default? + * + * 135 4/27/99 5:19p Samir + * mprintf change. + * + * 134 4/27/99 2:10p Samir + * added code to set the desired sound card given the descriptive name of + * a sound card. + * + * 133 4/25/99 9:52p Samir + * fixed looping sequencing bug with thread for direct sound mixers. + * SSF_PLAY_LOOPING must be set before any buffer filling occurs, + * otherwise bad things happen with very small loops.... + * + * 132 4/23/99 7:51p Samir + * looping fixes for directsound + * + * 131 4/22/99 10:33p Samir + * modifications so that DirectSound mixers use one thread for all looping + * and streaming sounds. It worked without crashing for about twenty + * minutes of playing from level 1 to level 2 of D3. We'll see. + * + * 130 4/18/99 5:37p Kevin + * Very simple optimization for the software mixer + * + * 129 4/13/99 4:15p Jason + * took out register keyword because it was hurting performance + * + * 128 4/13/99 4:09p Samir + * more priority stuff. + * + * + * 126 4/12/99 7:14p Samir + * prioritization code added. + * + * 125 4/10/99 5:08p Samir + * took out obsolete data from play_information structure that should save + * around 70 bytes per instance. + * + * 124 4/09/99 5:00p Kevin + * put the memset in -- memset should be doing at least 32 bit copies, + * which is FAR better than a for loop. + * + * 123 4/09/99 12:03p Samir + * took out windows.h again, this time made HWND a void pointer, and + * checked under both editor and main projects. + * + * 122 4/06/99 8:29p Samir + * added error check system. + * + * 121 3/29/99 11:00a Samir + * added system to support different 3d sound options. + * + * 120 3/17/99 4:20p Samir + * added functions to pause and resume individual sounds. + * + * 119 3/04/99 1:23p Kevin + * Fixed Poping sound!!! + * + * 118 3/03/99 6:53p Matt + * Fixed compile warning + * + * 117 3/03/99 3:12p Chris + * Fixed volume problems + * + * 116 3/01/99 8:12p Samir + * pause sounds when switching sound quality! + * + * 115 2/25/99 4:50p Kevin + * Semi-hack to fix the problem with music looping and stuttering in DS + * + * 114 2/24/99 3:15p Kevin + * OEM menu changes, and bug fixes for the save/load system + * + * 113 2/22/99 6:28p Kevin + * Fixed a bug that was introduced (?) when the thread problem was fixed. + * + * 112 2/20/99 1:14a Kevin + * Fixed another bug + * + * 111 2/19/99 10:45p Kevin + * Fixed bug I just introduced... + * + * 110 2/19/99 5:21p Kevin + * Fixed some connection DLLs and a Direct Sound bug with threads. + * + * 109 2/11/99 3:30p Doug + * error checking in PlaySoundBuffer. + * + * 108 2/04/99 9:46a Kevin + * OEM version changes + * + * 107 1/14/99 6:10p Samir + * added DirectSound buffer duplication code. + * + * 106 1/11/99 5:51p Samir + * reverb on the buffer level. + * + * 105 1/08/99 6:31p Samir + * added reverb + * + * 104 1/08/99 11:36a Samir + * implemented basic Aureal 2.0 support. + * + * 103 1/08/99 10:32a Chris + * + * 102 12/23/98 11:49a Samir + * reorganized code so it works with different APIs. + * + * 101 12/11/98 5:21p Samir + * (chris) fixed problem with software streaming audio. + * + * 100 12/10/98 3:16p Chris + * added mprintfs for later use + * + * 99 12/10/98 2:18p Chris + * Added more asserts to the streaming code + * + * 98 11/13/98 4:55p Nate + * don't do dedicated server check in lowlevel. + * + * 97 11/13/98 12:17p Chris + * + * 96 11/12/98 12:15p Chris + * Fixed a bug with streaming mixers + * + * 95 10/30/98 1:20p Chris + * Fixed a m_unique_id bug where it was being used before it was being + * assigned + * +* +* $NoKeywords: $ +*/ + +// NEED THIS SINCE DDSNDLIB is a DD library. +#include "DDAccess.h" + +#include "ds3dlib_internal.h" + +#include +#include +#include +#include "cfile.h" +#include "pserror.h" +#include "mono.h" +#include "soundload.h" +#include "mem.h" +#include "application.h" +#include "auddev.h" +#include "Macros.h" +#include "ddio.h" + +// Hacked window handle -- chrishack +static oeWin32Application *SoundApp=NULL; +static void *GameWindowHandle; +#define MIN_SOUND_MIX_VOLUME 0.0 +#define MAX_WRITE_AHEAD .04 // Seconds to write ahead of the play position (in seconds) + + // write position + +#define IS_3D_MIXER(_type) ((_type) >= SOUND_MIXER_DS3D_16 && (_type) != SOUND_MIXER_NONE) + +win_llsSystem *ll_sound_ptr; +emulated_listener *g_emulated_listener=NULL; // silly hack (Samir) +short Global_DS_alloced_sounds = 0; + +int *Fast_mixer = NULL; +int Fast_mixer_len = 0; + +#define VOLUME_FIX_BITS 1024 + +// Streaming primary buffer information +DSSTREAM m_sb_info; +char m_sound_device_name[256]=""; // set by set sound card + +// Loads a sound buffer with data +long LoadSoundData(LPDIRECTSOUNDBUFFER lp_dsb, char* sound_data_ptr, ulong total_bytes); + + +/////////////////////////////////////////////////////////////////////////////// + +static t3dEnvironmentValues Env3dValues; +static t3dEnvironmentToggles Env3dToggles; + +#define ENV3DTOG_DOPPLER true +#define ENV3DTOG_GEOMETRY true + +#define ENV3DVAL_DOPPLER_DEFAULT 0.5f +#define ENV3DVAL_ROLLOFF_DEFAULT 0.5f + + +/////////////////////////////////////////////////////////////////////////////// + +win_llsSystem::win_llsSystem(void): llsSystem() +{ + m_lp_ds = NULL; + m_f_sound_lib_init = 0; + m_hwnd_main = NULL; + m_mixer_type = -1; + m_sound_quality = SQT_NORMAL; + m_cache_stress_timer = 0.0f; + ll_sound_ptr = NULL; + m_timer_last_frametime = -1; + memset(&Env3dValues, 0, sizeof(Env3dValues)); + memset(&Env3dToggles, 0, sizeof(Env3dToggles)); + SetError(SSL_OK); +} + + +win_llsSystem::~win_llsSystem(void) +{ + DestroySoundLib(); + SetSoundCard(NULL); +} + + +inline void opti_8m_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, int *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + int *mb = mixer_buffer16; + + const int fix_rvol = r_volume * VOLUME_FIX_BITS; + const int fix_lvol = l_volume * VOLUME_FIX_BITS; + + + for(i = 0; i < (num_write << 1); i += 2) + { + int sample; + int l_sample; + int r_sample; + + sample = (((*cur_sample_8bit)) - 128) << 8; + cur_sample_8bit++; + + l_sample = *mb + (sample * fix_lvol); + r_sample = *(mb + 1) + (sample * fix_rvol); + + *mb = l_sample; + mb++; + *mb = r_sample; + mb++; + + + } + samples_played+= (i/2); +} + +inline void opti_8s_mix(unsigned char *cur_sample_8bit, const int num_write, int &samples_played, int *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + int *mb = mixer_buffer16; + + const int fix_rvol = r_volume * VOLUME_FIX_BITS; + const int fix_lvol = l_volume * VOLUME_FIX_BITS; + + + for(i = 0; i < (num_write << 1); i += 2) + { + int lsample; + int rsample; + int l_sample; + int r_sample; + + lsample = (((*cur_sample_8bit)) - 128) << 8; + cur_sample_8bit++; + rsample = (((*cur_sample_8bit)) - 128) << 8; + cur_sample_8bit++; + + l_sample = *mb + (lsample * fix_lvol); + r_sample = *(mb + 1) + (rsample * fix_rvol); + + *mb = l_sample; + mb++; + *mb = r_sample; + mb++; + + } + samples_played+= (i/2); + +} + +inline void opti_16m_mix(short *cur_sample_16bit, const int num_write, int &samples_played, int *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + int *mb = mixer_buffer16; + + const int fix_rvol = r_volume * VOLUME_FIX_BITS; + const int fix_lvol = l_volume * VOLUME_FIX_BITS; + + + for(i = 0; i < (num_write << 1); i += 2) + { + int sample; + int l_sample; + int r_sample; + + sample = *cur_sample_16bit; + cur_sample_16bit++; + + l_sample = *mb + (sample * fix_lvol); + r_sample = *(mb + 1) + (sample * fix_rvol); + + *mb = l_sample; + mb++; + *mb = r_sample; + mb++; + + + } + samples_played+= (i/2); +} + +inline void opti_16s_mix(short *cur_sample_16bit, const int num_write, int &samples_played, int *mixer_buffer16, const float l_volume, const float r_volume) +{ + int i; + int *mb = mixer_buffer16; + + const int fix_rvol = r_volume * VOLUME_FIX_BITS; + const int fix_lvol = l_volume * VOLUME_FIX_BITS; + + + for(i = 0; i < (num_write << 1); i += 2) + { + int lsample; + int rsample; + int l_sample; + int r_sample; + + lsample = *cur_sample_16bit; + cur_sample_16bit++; + rsample = *cur_sample_16bit; + cur_sample_16bit++; + + l_sample = *mb + (lsample * fix_lvol); + r_sample = *(mb + 1) + (rsample * fix_rvol); + + *mb = l_sample; + mb++; + *mb = r_sample; + *mb++; + + } + samples_played+= (i/2); +} + + +//////////////////////////////////////////////////////////////////////////////////// + +#define MAX_DS_PAN 1000.0f + +int GamePanToDsPan(float pan) +{ + int ds_pan; + ds_pan = (int)(pan*MAX_DS_PAN); + + if(ds_pan > MAX_DS_PAN) ds_pan = MAX_DS_PAN; + if(ds_pan < -MAX_DS_PAN) ds_pan = -MAX_DS_PAN; + + // mprintf((0, "P %f %d\n", pan, ds_pan)); + return ds_pan; +} + +int GameVolumeToDsAttenuation(float volume) +{ + float fvol; + + if (volume <= 0.0f) + { + fvol = 2000.0f; + } + else + { + fvol = logf(volume) / logf(10.0f); + fvol *= 2000.0; + } + + if(volume <= 0.01f) + { + fvol = DSBVOLUME_MIN; + } + + // mprintf((0, "V %f %f\n", volume, fvol)); + return (int)fvol; +} + + +//////////////////////////////////////////////////////////////////////////////////// + +inline char *get_sound_info(sound_buffer_info *sb, int *length, bool *f16bit) +{ + if (sb->m_sound_index < 0) { + if (sb->s) { + *f16bit = sb->s->f_sample_16bit; + } + return NULL; + } + if(SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_8bit) + { + *length = SoundFiles[Sounds[sb->m_sound_index].sample_index].np_sample_length; + *f16bit = false; + return (char *)SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_8bit; + } + else + { + ASSERT(SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_16bit); + *f16bit = true; + *length = SoundFiles[Sounds[sb->m_sound_index].sample_index].np_sample_length*2; + return (char *)SoundFiles[Sounds[sb->m_sound_index].sample_index].sample_16bit; + } +} + + +////////////////////////////////////////////////////////////////////////////////// +#define SB_STATUS_PLAYING 0x1 +#define SB_STATUS_INVALID 0x2 + + +inline int sb_get_status(sound_buffer_info *sb) +{ + int retflags = 0; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + int status; + + if (sb->m_snd_obj) { + status = A3D_GetSourceStatus(sb->m_snd_obj); + if (status & A3D_STATUS_PLAYING) retflags |= SB_STATUS_PLAYING; + } + else { + retflags |= SB_STATUS_INVALID; + } + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + LPDIRECTSOUNDBUFFER sound_ptr = sb->m_sound_buffer; + unsigned long status; + + if (sound_ptr) { + sound_ptr->GetStatus(&status); + if (status & DSBSTATUS_PLAYING) retflags |= SB_STATUS_PLAYING; + } + else { + retflags |= SB_STATUS_INVALID; + } + } + + return retflags; +} + + + +inline void sb_adjust_properties_3d(sound_buffer_info *sb, float f_volume, pos_state *pos, float reverb) +{ + if (!ll_sound_ptr->m_in_sound_frame) ll_sound_ptr->m_pending_actions = true; + + sb->m_volume = f_volume; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + // f_volume = f_volume * 1.2; + if (f_volume > 1.0f) f_volume = 1.0f; + A3D_SetSourceVolume(sb->m_snd_obj, f_volume); +// A3D_SetMinMaxDistance(sb->m_snd_obj, Sounds[sb->m_sound_index].min_distance, Sounds[sb->m_sound_index].max_distance); + A3D_SetMinMaxDistance(sb->m_snd_obj, 30.0f, Sounds[sb->m_sound_index].max_distance); +// A3D_SetSourceCone(sb->m_snd_obj, x,y,z); + A3D_SetSourceOrientation(sb->m_snd_obj, &pos->orient->fvec,&pos->orient->uvec); + A3D_SetSourcePosition(sb->m_snd_obj, pos->position->x, pos->position->y, pos->position->z); + A3D_SetSourceVelocity(sb->m_snd_obj, pos->velocity->x, pos->velocity->y, pos->velocity->z); + } + else +#endif + if (IS_3D_MIXER(sb->m_mixer_type)) { + LPDIRECTSOUNDBUFFER lp_dsb = sb->m_sound_buffer; + LPDIRECTSOUND3DBUFFER lpDSB3D = sb->m_sound_buffer_3d; + + lp_dsb->SetVolume(GameVolumeToDsAttenuation(f_volume)); + + ASSERT(lpDSB3D != NULL); + + if (sb->m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + lpDSB3D->SetMinDistance(30.0f, DS3D_IMMEDIATE); + } + else { + lpDSB3D->SetMinDistance(Sounds[sb->m_sound_index].min_distance, DS3D_IMMEDIATE); + } + lpDSB3D->SetMaxDistance(Sounds[sb->m_sound_index].max_distance, DS3D_IMMEDIATE); + lpDSB3D->SetConeOrientation(pos->orient->fvec.x, pos->orient->fvec.y, pos->orient->fvec.z, DS3D_IMMEDIATE); + lpDSB3D->SetPosition(pos->position->x, pos->position->y, pos->position->z, DS3D_IMMEDIATE); + lpDSB3D->SetVelocity(pos->velocity->x, pos->velocity->y, pos->velocity->z, DS3D_IMMEDIATE); + + // if (sb->m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + // EAX_SetBufferReverbMix(lpDSB3D, reverb); + // } + } + else { + mprintf((0, "m_mixer_type = %d\n", sb->m_mixer_type)); + Int3(); + } +} + + +inline void sb_adjust_properties_2d(sound_buffer_info *sb, float f_volume, float f_pan, ushort frequency) +{ + if (!ll_sound_ptr->m_in_sound_frame) ll_sound_ptr->m_pending_actions = true; + + sb->m_volume = f_volume; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + if (sb->m_snd_obj) { + float l_pan=1.0f, r_pan=1.0f; + if (f_pan < 0.0f) r_pan = r_pan + f_pan; + if (f_pan > 0.0f) l_pan = l_pan - f_pan; + A3D_SetSourceVolume(sb->m_snd_obj, f_volume); + A3D_SetSourcePan(sb->m_snd_obj, l_pan, r_pan); + // mprintf((0, "2d vol:%.1f pan:%.1f,%.1f\n", f_volume, l_pan, r_pan)); + } + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) + { + LPDIRECTSOUNDBUFFER lpdsb; + + ASSERT(f_pan >= -1.0f && f_pan <= 1.0f); + ASSERT(f_volume >= 0.0f && f_volume <= 1.0f); + + lpdsb = sb->m_sound_buffer; + if(lpdsb == NULL) + return; + + // mprintf((0, "Sound UID %d is now at %d volume,%d pan\n", sound_uid, volume, pan)); + + lpdsb->SetVolume(GameVolumeToDsAttenuation(f_volume)); + lpdsb->SetPan(GamePanToDsPan(f_pan)); //chrishack pan is off + lpdsb->SetFrequency(frequency); + } +} + + +// functions for different APIs +int sb_get_current_position(sound_buffer_info *sb, uint *writep) +{ + DWORD playp, wp; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + if (!sb->m_snd_obj) { + *writep = (uint)(-1); + playp = (DWORD)(-1); + } + else { + uint pp; + A3D_GetCurrentPosition(sb->m_snd_obj, &pp); + *writep = pp; + playp = (DWORD)pp; + } + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + if (sb->m_sound_buffer && sb->m_sound_buffer->GetCurrentPosition(&playp, &wp) == DS_OK) { + *writep = (uint)wp; + } + else { + playp = (DWORD)(-1); + *writep = (uint)(-1); + } + } + + return (uint)playp; +} + + +inline void sb_set_current_position(sound_buffer_info *sb, uint pos) +{ + if (!ll_sound_ptr->m_in_sound_frame) ll_sound_ptr->m_pending_actions = true; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_SetCurrentPosition(sb->m_snd_obj, pos); + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + sb->m_sound_buffer->SetCurrentPosition((DWORD)pos); + } +} + + +void sb_free_buffer(sound_buffer_info *sb) +{ + if (!ll_sound_ptr->m_in_sound_frame) ll_sound_ptr->m_pending_actions = true; + if (!sb->m_sound_buffer) return; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_FreeSSource(sb->m_snd_obj); + sb->m_snd_obj = NULL; + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + if (sb->m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + EAX_FreeSource(sb->m_lpksps); + } + + if (sb->m_sound_buffer) { + sb->m_sound_buffer->Release(); + sb->m_sound_buffer = NULL; + } + if (sb->m_sound_buffer_3d) { + sb->m_sound_buffer_3d->Release(); + sb->m_sound_buffer_3d = NULL; + } + } +} + + +void sb_stop_buffer(sound_buffer_info *sb) +{ + if (!ll_sound_ptr->m_in_sound_frame) ll_sound_ptr->m_pending_actions = true; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_Stop(sb->m_snd_obj); + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + sb->m_sound_buffer->Stop(); + } +} + + +bool sb_lock_buffer(sound_buffer_info *sb, uint dwWriteCursor, uint dwWriteBytes, + void **lplpvAudioPtr1, uint *lpdwAudioBytes1, + void **lplpvAudioPtr2, uint *lpdwAudioBytes2) +{ + DWORD len1, len2; + +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + if (A3D_Lock(sb->m_snd_obj, dwWriteCursor, dwWriteBytes, lplpvAudioPtr1, &len1, lplpvAudioPtr2, &len2)) { + *lpdwAudioBytes1 = len1; + *lpdwAudioBytes2 = len2; + return true; + } + + return false; + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + TryLockAgainLabel: + switch (sb->m_sound_buffer->Lock(dwWriteCursor, dwWriteBytes, lplpvAudioPtr1, &len1, lplpvAudioPtr2, &len2, 0)) + { + case DS_OK: + *lpdwAudioBytes1 = len1; + *lpdwAudioBytes2 = len2; + return true; + + case DSERR_BUFFERLOST: + if(sb->m_sound_buffer->Restore() == DS_OK) + goto TryLockAgainLabel; + } + + return false; + } + + return false; +} + + +bool sb_unlock_buffer(sound_buffer_info *sb, void *ptr1, uint len1, void *ptr2, uint len2) +{ +#ifdef SUPPORT_AUREAL + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_Unlock(sb->m_snd_obj, ptr1, len1, ptr2, len2); + } + else +#endif + if (sb->m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + sb->m_sound_buffer->Unlock(ptr1, len1, ptr2, len2); + return true; + } + + return false; +} + + +bool sb_load_buffer(sound_buffer_info *sb, void *sample_data, int length) +{ + if (!ll_sound_ptr->m_in_sound_frame) ll_sound_ptr->m_pending_actions = true; + +#ifdef SUPPORT_AUREAL + switch (sb->m_mixer_type) + { + case SOUND_MIXER_AUREAL: + if (!A3D_LoadSample(sb->m_snd_obj, sample_data, length)) { + Int3(); + sb->m_status = SSF_UNUSED; + return false; + } + break; + default: +#endif + if(LoadSoundData(sb->m_sound_buffer, (char *)sample_data, length) != DS_OK) + { + Int3(); + sb->m_status = SSF_UNUSED; + return false; + } +#ifdef SUPPORT_AUREAL + } +#endif + + return true; +} + + + +/////////////////////////////////////////////////////////////////////////////// + + + +// The actual mixer code that sum's the sounds on each channel and does all the actual +// mixing and effects (writes data to the locked primary buffer) +void StreamMixer(char *ptr, int len) +{ + int i; + short *mixer_buffer16 = (short *) ptr; + int current_slot = 0; + bool f_loop; + bool f_mono; + + const int buff_len = len / ll_sound_ptr->m_primary_alignment; + +// this code will assure that this function will not be called when sound system is in error mode. + if (ll_sound_ptr->m_lib_error_code != SSL_OK) { + return; + } + + ASSERT(len <= m_sb_info.BufferSize); + ASSERT(ptr && len >= 0); + + ASSERT((len % ll_sound_ptr->m_primary_alignment) == 0); + + //memset((char *)ptr, 0, len); + + //This memset is hopefully temporary + memset(Fast_mixer,0,Fast_mixer_len*sizeof(int)); + int *fast_mix_ptr = Fast_mixer; + + // Mix the sound slots + while(current_slot < ll_sound_ptr->m_sound_mixer.m_max_sounds_played) + { + sound_buffer_info *cur_buf = &ll_sound_ptr->m_sound_mixer.m_sound_cache[current_slot]; + int num_samples = buff_len; + //mixer_buffer16 = (short *) ptr; + fast_mix_ptr = Fast_mixer; + f_mono = true; + + // Find slots with sounds in them + if((cur_buf->m_status != SSF_UNUSED) && + !(cur_buf->m_status & SSF_PAUSED)) + { + float l_volume = cur_buf->play_info->left_volume; + float r_volume = cur_buf->play_info->right_volume; + int skip_interval = cur_buf->play_info->sample_skip_interval; + int samples_played = cur_buf->play_info->m_samples_played; + short *sample_16bit; + unsigned char *sample_8bit; + int np_sample_length; + int sample_length; + int loop_start; + int loop_end; + + if(cur_buf->m_status & SSF_PLAY_STREAMING) + { + switch(cur_buf->play_info->m_stream_format) + { + case SIF_STREAMING_16_M: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + sample_8bit = NULL; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size/2; + break; + case SIF_STREAMING_8_M: + sample_16bit = NULL; + sample_8bit = (unsigned char *) cur_buf->play_info->m_stream_data; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size; + break; + case SIF_STREAMING_16_S: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + sample_8bit = NULL; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size/4; + f_mono = false; + break; + case SIF_STREAMING_8_S: + sample_16bit = NULL; + sample_8bit = (unsigned char *) cur_buf->play_info->m_stream_data; + np_sample_length = sample_length = cur_buf->play_info->m_stream_size/2; + f_mono = false; + break; + default: + Int3(); + break; + } + loop_start = 0; + loop_end = np_sample_length - 1; + } + else + { + int sound_index = cur_buf->m_sound_index; + int sample_index = Sounds[sound_index].sample_index; + sound_file_info *snd_file = &SoundFiles[sample_index]; + sample_16bit = snd_file->sample_16bit; + sample_8bit = snd_file->sample_8bit; + np_sample_length = snd_file->np_sample_length; + sample_length = snd_file->sample_length; + loop_start = Sounds[sound_index].loop_start; + loop_end = Sounds[sound_index].loop_end; + if (!sample_16bit && !sample_8bit) + { + mprintf((0, "sound file %s didn't have data for samples.\n", snd_file->name)); + } + } + + // cleanly continue if this happens, and inform a logfile, if it does. error handling + // ASSERT(sample_16bit || sample_8bit); + if (!sample_16bit && !sample_8bit) { + sound_file_info *snd_file = &SoundFiles[Sounds[cur_buf->m_sound_index].sample_index]; + ll_sound_ptr->SetError(SSL_ERROR_SAMPLE_NODATA); + ll_sound_ptr->ErrorText("ASSERT(sample_16bit || sample_8bit)\nNo data found for sound file: %s", snd_file->name); + cur_buf->m_status = SSF_UNUSED; + goto error_bail; + } + + ASSERT(np_sample_length <= sample_length); + ASSERT(samples_played >= 0 || samples_played <= sample_length); + + int num_write; + + error_trap: + + // We have not looped -- yet + f_loop = false; + + // Verify the volume levels are o.k. + ASSERT(l_volume >= 0.0 && l_volume <= 1.0); + ASSERT(r_volume >= 0.0 && r_volume <= 1.0); + + looping: // Will go to this label to do the next iteration of a looping sample + + if(cur_buf->m_status & (SSF_PLAY_LOOPING | SSF_PLAY_STREAMING)) // Looping sample's process is broken up into linear pieces + { + if(f_loop) + { + ASSERT(num_write >= 0); + + num_samples -= num_write; + ASSERT(num_samples > 0); + + fast_mix_ptr += (num_write * 2); + //mixer_buffer16 += num_write << 1; // update to the new start position + // (2x because of left and right channels) + + samples_played = loop_start; + } + + if(cur_buf->m_status & SSF_PLAY_LOOPING) // Looping sample's process is broken up into linear pieces + { + ASSERT(loop_end < sample_length); + if (loop_end < samples_played) + { + // CHRISHACK -- Fuck milestone. Fix later. Code below is a major hack (but it works). :) + + if(loop_end != loop_start) + { + while(loop_end < samples_played) + { + //Int3(); + samples_played -= loop_end - loop_start; + } + + ASSERT(samples_played >= 0); + } + else + { + cur_buf->m_status &= ~SSF_PLAY_LOOPING; + cur_buf->m_status |= SSF_PLAY_NORMAL; + } + + goto error_trap; + } + } + // The number of samples to write to the primary buffer + num_write = ((num_samples) < (loop_end - samples_played + 1))?(num_samples):(loop_end - samples_played + 1); + ASSERT(num_write >= 0 && num_write <= num_samples); + + if(num_write < num_samples) f_loop = true; + else f_loop = false; + + if(num_write <= 0) + { + num_write = 0; + mprintf((0, "d")); + goto stream_done; + } + } + else + { + // The number of samples to write to the primary buffer + num_write = ((num_samples) < (np_sample_length - samples_played))?(num_samples):(np_sample_length - samples_played); + if(!(num_write > 0 && num_write <= num_samples)) + { + num_write = 0; + goto done; + } + + // Optimization for silent sounds + if(l_volume <= MIN_SOUND_MIX_VOLUME && r_volume <= MIN_SOUND_MIX_VOLUME) + { + cur_buf->play_info->m_samples_played += num_write; + goto done; + } + } + + if(!(num_write > 0 && num_write <= num_samples)) // this was an assert + { + num_write = 0; + mprintf((0, "D")); + goto done; + } + + // Mix at 16 bits per sample + if(skip_interval == 0) + { + short *cur_sample_16bit = sample_16bit; + unsigned char *cur_sample_8bit = sample_8bit; + + if(f_mono) + { + if(sample_8bit) + { + cur_sample_8bit += samples_played; + opti_8m_mix(cur_sample_8bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume); + } + else + { + cur_sample_16bit += samples_played; + opti_16m_mix(cur_sample_16bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume); + } + } + else + { + if(sample_8bit) + { + cur_sample_8bit += (samples_played<<1); + opti_8s_mix(cur_sample_8bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume); + } + else + { + cur_sample_16bit += (samples_played<<1); + opti_16s_mix(cur_sample_16bit, num_write, samples_played, fast_mix_ptr, l_volume, r_volume); + } + } + } + else + // Account for lower-sampling rate + { + if(skip_interval == 1) + { + const int fix_rvol = r_volume * VOLUME_FIX_BITS; + const int fix_lvol = l_volume * VOLUME_FIX_BITS; + + for(i = 0; i < (num_write << 1); i += 2) + { + int sample; + + if(sample_16bit) + { + if(samples_played & 0x0001) + { + sample = ((int)sample_16bit[samples_played ^ 0x0001] + + (int)sample_16bit[samples_played + 1]) >> 1; + } + else + sample = sample_16bit[samples_played]; + } + else + { + if(samples_played & 0x0001) + { + // Notes: (<<7) is from a (<<8) - (>>1) + // Notes: (-256) is from (-128) + (-128) + sample = ((int)sample_8bit[samples_played ^ 0x0001] + (int)sample_8bit[samples_played + 1] - 256) << 7; + } + else + sample = (((int)sample_8bit[samples_played]) - (int)128) << 8; + } + + samples_played++; + + ASSERT(i >= 0 && (i + 1 < num_samples * 2)); + + int l_sample = fast_mix_ptr[i] + (sample * fix_lvol); + int r_sample = fast_mix_ptr[i + 1] + (sample * fix_rvol); + + fast_mix_ptr[i] = l_sample; + fast_mix_ptr[i + 1] = r_sample; + + } + } + else + { + const int fix_rvol = r_volume * VOLUME_FIX_BITS; + const int fix_lvol = l_volume * VOLUME_FIX_BITS; + for(i = 0; i < (num_write << 1); i += 2) + { + int sample; + const int mod_pos = samples_played % 4; + + if(sample_16bit) + { + switch(mod_pos) + { + case 0: + sample = sample_16bit[samples_played]; + break; + case 1: + sample = (sample_16bit[samples_played - 1]*3 + + sample_16bit[samples_played + 3]) >> 2; + break; + case 2: + sample = (sample_16bit[samples_played - 2] + + sample_16bit[samples_played + 2]) >> 1; + break; + case 3: + sample = (sample_16bit[samples_played - 3] + + sample_16bit[samples_played + 1]*3) >> 2; + break; + } + } + else + { + switch(mod_pos) + { + case 0: + sample = ((((int)sample_8bit[samples_played]) - 128) << 8); + break; + case 1: + sample = (((((int)sample_8bit[samples_played - 1]) - 128) << 8)*3 + + ((((int)sample_8bit[samples_played + 3]) - 128) << 8)) >> 2; + break; + case 2: + sample = (((((int)sample_8bit[samples_played - 2]) - 128) << 8) + + ((((int)sample_8bit[samples_played + 2]) - 128) << 8)) >> 1; + break; + case 3: + sample = (((((int)sample_8bit[samples_played - 3]) - 128) << 8) + + ((((int)sample_8bit[samples_played + 1]) - 128) << 8)*3) >> 2; + break; + } + } + + samples_played++; + + ASSERT(i >= 0 && (i + 1 < num_samples * 2)); + + int l_sample = fast_mix_ptr[i] + (sample * fix_lvol); + int r_sample = fast_mix_ptr[i + 1] + (sample * fix_rvol); + + + fast_mix_ptr[i] = l_sample; + fast_mix_ptr[i + 1] = r_sample; + } + } + } + +stream_done: + + cur_buf->play_info->m_samples_played = samples_played; + + if(cur_buf->m_status & SSF_PLAY_STREAMING) + { + if(f_loop) + { + if(cur_buf->play_info->m_stream_cback && cur_buf->play_info->m_stream_data) + { + cur_buf->play_info->m_stream_data = (*cur_buf->play_info->m_stream_cback)(cur_buf->play_info->user_data, cur_buf->play_info->m_stream_handle, &cur_buf->play_info->m_stream_size); +// cur_buf->s->current_position = (char *)cur_buf->play_info->m_stream_data; +// mprintf((0, "%x %d\n", cur_buf->play_info->m_stream_data, cur_buf->play_info->m_stream_size)); + ASSERT(!(cur_buf->play_info->m_stream_data && cur_buf->play_info->m_stream_size <= 0)); +// mprintf((0, "Data %X, length %d\n", cur_buf->play_info->m_stream_data, cur_buf->play_info->m_stream_size)); + + if(cur_buf->play_info->m_stream_data) + { + switch(cur_buf->play_info->m_stream_format) + { + case SIF_STREAMING_16_M: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size/2; + break; + case SIF_STREAMING_8_M: + sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size; + break; + case SIF_STREAMING_16_S: + sample_16bit = (short *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size/4; + break; + case SIF_STREAMING_8_S: + sample_8bit = (unsigned char *)cur_buf->play_info->m_stream_data; + loop_end = sample_length = np_sample_length = cur_buf->play_info->m_stream_size/2; + break; + default: + Int3(); + break; + } + loop_end -= 1; + } + else + { + mprintf((0, "SE: Data is NULL\n")); + cur_buf->m_status &= ~SSF_PLAY_STREAMING; + f_loop = false; + } + } + else + { + mprintf((0, "SE: Callback/data is NULL\n")); + cur_buf->m_status &= ~SSF_PLAY_STREAMING; + f_loop = false; + } + } + } + + if (f_loop) goto looping; + + done: + + if(cur_buf->play_info->m_samples_played >= (np_sample_length) && !(cur_buf->m_status & (SSF_PLAY_LOOPING | SSF_PLAY_STREAMING))) { + ll_sound_ptr->StopSound(cur_buf->m_unique_id); + } + } + + error_bail: + current_slot++; + } + + for(int a=0;a<(buff_len*2);a++) + { + Fast_mixer[a] = Fast_mixer[a]/VOLUME_FIX_BITS; + + if(Fast_mixer[a] > 32767) + Fast_mixer[a] = 32767; + if(Fast_mixer[a] < -32767) + Fast_mixer[a] = -32767; + + mixer_buffer16[a] = Fast_mixer[a]; + + Fast_mixer[a+1] = Fast_mixer[a+1]/VOLUME_FIX_BITS; + + if(Fast_mixer[a+1] > 32767) + Fast_mixer[a+1] = 32767; + if(Fast_mixer[a+1] < -32767) + Fast_mixer[a+1] = -32767; + + mixer_buffer16[a+1] = Fast_mixer[a+1]; + + a++; + } + //mprintf((0," -%d- ",a)); + + +} + +// Locks the primary buffer and fills in the new data +void StreamFill(int start_byte, int num_bytes) +{ + char *ptr1; + char *ptr2; + int len1, len2; + + if ((num_bytes % ll_sound_ptr->m_primary_alignment) != 0) { + ll_sound_ptr->SetError(SSL_ERROR_STREAMMIXER); + ll_sound_ptr->ErrorText("ASSERT((len % ll_sound_ptr->m_primary_alignment) == 0)\nLen:%d PrA:%d", num_bytes, ll_sound_ptr->m_primary_alignment); + return; + } + ASSERT((num_bytes % ll_sound_ptr->m_primary_alignment) == 0); + ASSERT((start_byte % ll_sound_ptr->m_primary_alignment) == 0); + + start_byte = (start_byte % m_sb_info.BufferSize); + + TryLockAgainLabel: + + switch(m_sb_info.m_lp_looping_buffer->Lock(start_byte, num_bytes, (void **)&ptr1, (DWORD *)&len1, (void **)&ptr2, (DWORD *)&len2, 0)) + { + ASSERT((len1 % ll_sound_ptr->m_primary_alignment) == 0); + ASSERT((len2 % ll_sound_ptr->m_primary_alignment) == 0); + + // The pointers are to the lock areas of the primary buffer. There are two because locked region + // might be a discontinuous chunk (wrapped around the end of the buffer i.e. 'LLLLBBBBBBLLLLL' + // L is for Locked and B is unlocked buffer. + case DS_OK: + StreamMixer(ptr1, len1); + if(ptr2) + StreamMixer(ptr2, len2); + + m_sb_info.m_lp_looping_buffer->Unlock(ptr1, len1, ptr2, len2); + + m_sb_info.NextWritePos = (start_byte + num_bytes) % m_sb_info.BufferSize; + break; + + case DSERR_BUFFERLOST: + if(m_sb_info.m_lp_primary_buffer->Restore() == DS_OK && + m_sb_info.m_lp_looping_buffer->Restore() == DS_OK) + goto TryLockAgainLabel; + break; + } +} + + +// A peroidic mixer that uses the primary buffer as a stream buffer +void __cdecl StreamTimer(void *user_ptr) +{ + int playp, writep; + int try_count = 0; + DSSTREAM *sb_info = (DSSTREAM *)user_ptr; + DWORD result; + + m_sb_info.m_lp_primary_buffer->Restore(); + m_sb_info.m_lp_primary_buffer->Play(0, 0, DSBPLAY_LOOPING); + + while(!sb_info->thread_request_kill) + { + try_count = 0; + + TryAgain: + try_count++; + +// hresult = m_sb_info.m_lp_primary_buffer->GetStatus(&stat_result); +// +// if(hresult != DS_OK || !(stat_result & DSBSTATUS_LOOPING)) +// { +// m_sb_info.m_lp_primary_buffer->Restore(); + m_sb_info.m_lp_primary_buffer->Restore(); + m_sb_info.m_lp_primary_buffer->Play(0, 0, DSBPLAY_LOOPING); +// } + + result = sb_info->m_lp_looping_buffer->GetCurrentPosition((DWORD *)&playp, (DWORD *)&writep); + + //mprintf((0, "(%d,%d)\n", playp, writep)); + + // If primary buffer was stopped from playing + if(writep == playp) + { + sb_info->NextWritePos = -1; + + if((try_count == 1) && (m_sb_info.m_lp_primary_buffer->Restore() == DS_OK && + m_sb_info.m_lp_looping_buffer->Restore() == DS_OK)) + { + if((try_count == 1) && (m_sb_info.m_lp_looping_buffer->Play(0, 0, DSBPLAY_LOOPING) == DS_OK)) + { + goto TryAgain; + } + } + + goto done; + } + + + // Insert mixer code + { + int num_write_bytes; + + if(sb_info->NextWritePos < 0) + sb_info->NextWritePos = writep; + + // Debugging code (not sure what would happen if we hit these without an Int3()) -- Skipping noise + if(sb_info->LastPlayPos < sb_info->NextWritePos) + { + + if(playp >= sb_info->NextWritePos || playp < sb_info->LastPlayPos) + { + if(!sb_info->m_f_secondary_looping) goto done; + playp = sb_info->NextWritePos - 4; + num_write_bytes = 4 * (int)(22050 * DSPB_TICK_INTERVAL); + } + else + { + // Determine how much we can write out. + num_write_bytes = (sb_info->MaxWriteBytes + playp) - sb_info->NextWritePos; + } + } + else + { + if(playp >= sb_info->NextWritePos && playp < sb_info->LastPlayPos) + { + if(!sb_info->m_f_secondary_looping) goto done; + + playp = sb_info->NextWritePos - 4; + num_write_bytes = 4 * (int)(22050 * DSPB_TICK_INTERVAL); + } + else + { + // Determine how much we can write out. + if(playp < sb_info->NextWritePos) + num_write_bytes = (sb_info->MaxWriteBytes + playp) - sb_info->NextWritePos; + else + num_write_bytes = sb_info->MaxWriteBytes - (sb_info->NextWritePos + (sb_info->BufferSize - playp)); + } + } + + num_write_bytes &= (0xFFFFFFFF & (~(ll_sound_ptr->m_primary_alignment))); + + if(num_write_bytes > 0) + { + //ASSERT(num_write_bytes < sb_info->BufferSize); + if(num_write_bytes >= sb_info->BufferSize) + { + num_write_bytes = sb_info->BufferSize/2; + } + + StreamFill(sb_info->NextWritePos, num_write_bytes); + } + } + + sb_info->LastPlayPos = playp; + + done: + Sleep(DSPB_TICK_MILLISECONDS); + } + + sb_info->thread_alive = false; +} + +// Begins the whole streaming process +bool win_llsSystem::StartStreaming(void) +{ + DSBCAPS dsbcaps; + + dsbcaps.dwSize = sizeof(DSBCAPS); + m_sb_info.m_lp_looping_buffer->GetCaps(&dsbcaps); + + m_sb_info.m_f_secondary_looping = (m_sb_info.m_lp_looping_buffer != m_sb_info.m_lp_primary_buffer); + + m_sb_info.BufferSize = dsbcaps.dwBufferBytes; + + m_sb_info.MaxWriteSamples = m_primary_frequency * MAX_WRITE_AHEAD; + m_sb_info.MaxWriteBytes = m_sb_info.MaxWriteSamples * m_primary_alignment; + + m_sb_info.NextWritePos = -1; + m_sb_info.LastPlayPos = 0; + + StreamFill(0, dsbcaps.dwBufferBytes); + + m_sb_info.thread_request_kill = false; + m_sb_info.thread_alive = true; + + // Start mixing thread. + m_sb_info.thread_handle = _beginthread(StreamTimer, 16384,(void *)&m_sb_info); + if(m_sb_info.thread_handle==-1) + { + m_sb_info.thread_alive = false; + mprintf((0, "Thread failed\n")); + Int3(); + return false; + } + + if(m_sb_info.m_f_secondary_looping) + { + if(!SetThreadPriority((HANDLE)m_sb_info.thread_handle, THREAD_PRIORITY_HIGHEST)) + Int3(); + } + else + { + if(!SetThreadPriority((HANDLE)m_sb_info.thread_handle, THREAD_PRIORITY_TIME_CRITICAL)) + Int3(); + } + + return true; +} + +// Creates a 2d, 3d, or Primary direct sound buffer +long win_llsSystem::CreateDSBuffer(int buffer_type, LPDIRECTSOUNDBUFFER *lp_lp_dsb, LPDIRECTSOUND3DBUFFER *lp_lp_dsb_3d, ulong sound_bytes, ulong frequency, bool f_is_stereo, bool f_is_16_bit) +{ + DSBUFFERDESC dsbd; + tWAVEFORMATEX fmt; + HRESULT result = DS_OK; + + ASSERT(m_lp_ds != NULL && sound_bytes >= 0); + ASSERT(frequency == 44100 || frequency == 22050 || frequency == 11025); + if (!m_f_sound_lib_init) return 0; + + if (lp_lp_dsb) { *lp_lp_dsb = NULL; } + if (lp_lp_dsb_3d) { *lp_lp_dsb_3d = NULL; } + + // Setup the wave format + fmt.nChannels = (f_is_stereo)?2:1; + fmt.wBitsPerSample = (f_is_16_bit)?16:8; + fmt.nSamplesPerSec = ((DWORD)frequency); + fmt.nBlockAlign = fmt.nChannels*(fmt.wBitsPerSample>>3); + fmt.nAvgBytesPerSec = ((DWORD)fmt.nSamplesPerSec)*((DWORD)fmt.nBlockAlign); + fmt.wFormatTag = WAVE_FORMAT_PCM; + + // Setup the secondary direct sound buffer + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.lpwfxFormat = (LPWAVEFORMATEX)&fmt; + dsbd.dwSize = sizeof(DSBUFFERDESC); + dsbd.dwBufferBytes = sound_bytes; + + if(m_mixer_type == SOUND_MIXER_SOFTWARE_16) + { + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2; + + ASSERT(buffer_type == SBT_PRIMARY); + + m_primary_frequency = m_current_frequency = frequency; + m_primary_bit_depth = fmt.wBitsPerSample; + m_primary_alignment = fmt.nBlockAlign; + + dsbd.lpwfxFormat = NULL; + dsbd.dwBufferBytes = 0; + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2; + } + else if (m_mixer_type == SOUND_MIXER_DS_16 || m_mixer_type == SOUND_MIXER_DS_8) + { + // There are three buffer types that we should consider. + switch(buffer_type) + { + case SBT_2D: + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN; + break; + case SBT_PRIMARY: + dsbd.lpwfxFormat = NULL; + dsbd.dwBufferBytes = 0; + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + break; + default: + ASSERT(0); // Invalid type of buffer + } + } + else if(IS_3D_MIXER(m_mixer_type)) { + // There are three buffer types that we should consider. + switch(buffer_type) + { + case SBT_2D: + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN; + break; + case SBT_3D: + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME; + break; + case SBT_PRIMARY: + dsbd.lpwfxFormat = NULL; + dsbd.dwBufferBytes = 0; + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME; + break; + default: + ASSERT(0); // Invalid type of buffer + } + } + else { + mprintf((0, "DS3DLIB: Unsupported function for mixer specfied in CreateDSBuffer (%d,%d)\n", m_mixer_type, buffer_type)); + Int3(); // Get Samir!!! + return DSERR_UNSUPPORTED; + } + + // Create the buffer + result = m_lp_ds->CreateSoundBuffer(&dsbd, lp_lp_dsb, 0); +/// ASSERT(result == DS_OK); + + if (result == DS_OK && buffer_type == SBT_PRIMARY) + { + // Creative EAX Init + if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + if (!EAX_SetPrimaryBuffer()) { + mprintf((0, "CreateDSBuffer: EAX Init failed.\n")); + result = DSERR_UNSUPPORTED; + goto ds_error; + } + } + + // Succeeded. Set primary buffer to desired format. + result = (*lp_lp_dsb)->SetFormat(&fmt); + } + + if (result != DS_OK) { + mprintf((0, "DS3DLIB: Failed to init sound buffer of type %d for mixer %d.\n", buffer_type, m_mixer_type)); + goto ds_error; + } + +// call get format to make sure we're okay. + if (buffer_type == SBT_PRIMARY) + { + } + + +// do 3d sound support stuff. + if(IS_3D_MIXER(m_mixer_type)) { + // a 3d buffer needs a 3d interface pointer + if(buffer_type == SBT_3D) + { + ASSERT(lp_lp_dsb_3d != NULL); + result = (*lp_lp_dsb)->QueryInterface(IID_IDirectSound3DBuffer, (void **)lp_lp_dsb_3d); + if (result != DS_OK) { + goto ds_error; + } + + // ASSERT(result == DS_OK); + } + else if(buffer_type == SBT_PRIMARY) + { + if((result = (*lp_lp_dsb)->QueryInterface(IID_IDirectSound3DListener, (void **)&m_lp_listener)) == S_OK) + { + m_lp_listener->SetRolloffFactor(ENV3DVAL_ROLLOFF_DEFAULT, DS3D_IMMEDIATE); + } + } + } + +ds_error: + if (result != DS_OK) { + mprintf((0, "DS3DLIB:result=%x\n", result)); + if (lp_lp_dsb_3d && (*lp_lp_dsb_3d)) { + (*lp_lp_dsb_3d)->Release(); + *lp_lp_dsb_3d = NULL; + } + + if (lp_lp_dsb && (*lp_lp_dsb)) { + (*lp_lp_dsb)->Release(); + *lp_lp_dsb = NULL; + } + } + + return (long)result; +} + +// Internal function to enumerate sound devices +BOOL CALLBACK LLEnumCallback(LPGUID lp_guid, + LPCSTR lpstr_description, + LPCSTR lpstr_module, + LPVOID lp_Context) +{ + GUID FAR *lp_ret_guid = (GUID FAR *)lp_Context; + + if (m_sound_device_name[0]) { + if (strcmp(lpstr_description, m_sound_device_name) == 0) { + mprintf((0, "Using sound card:%s-%s\n", lpstr_description, lpstr_module)); + if (lp_guid) { + memmove(lp_ret_guid, lp_guid, sizeof(GUID)); + } + return FALSE; + } + } + + return TRUE; +} + + +void win_llsSystem::SetSoundCard(const char *name) +{ + if (name) { + strcpy(m_sound_device_name, name); + } + else { + m_sound_device_name[0] = 0; + } +} + + +// Initializes the sound library +int win_llsSystem::InitSoundLib(char mixer_type, oeApplication *sos, unsigned char MaxSoundsPlayed) // add playlist info +{ + GUID card_guid, zero_card_guid; + GUID *pguid = NULL; + DSCAPS dscaps; + HRESULT hresult; + bool f16bit; + bool retval = true; + +// reset error system. + SetError(SSL_OK); + +#ifdef OEM_AUREAL + mixer_type = SOUND_MIXER_AUREAL; +#endif + + SoundApp = (oeWin32Application *)sos; + + if(sos) { + oeWin32Application *obj = (oeWin32Application *)sos; + + // If the the library if already init'ed, then return o.k. + if(m_f_sound_lib_init) return 1; + + GameWindowHandle = (void *)obj->m_hWnd; + } + else { + ASSERT(GameWindowHandle); + } + + ll_sound_ptr = this; + m_timer_last_frametime = -1; + m_cache_stress_timer = 0.0f; + m_in_sound_frame = false; + m_pending_actions = false; + +////////////////////////////////////////////////////////////////////////////// + // chrishack -- Need to enumerate sound devices for now + // Attempt to enumerate the sound device + // if(DirectSoundEnumerate(&LLEnumCallback, NULL) != DS_OK) return 0; + + // Setup the game handle + m_hwnd_main = GameWindowHandle; + + // Check for invalid values and NULL pointers + ASSERT(m_hwnd_main != NULL); + ASSERT(Sounds != NULL); + ASSERT(SoundFiles != NULL); + ASSERT(MaxSoundsPlayed > 0); + + memset(&m_sb_info, 0, sizeof(DSSTREAM)); + + // Currently restart total sounds played count (for unique ids and stats) + m_total_sounds_played = 0; + + // Setup the sound mixer object + m_sound_mixer.m_cur_sounds_played = 0; + m_sound_mixer.m_max_sounds_played = MaxSoundsPlayed; + m_sound_mixer.m_sound_cache = new sound_buffer_info[MaxSoundsPlayed]; + m_mixer_type = SOUND_MIXER_NONE; + + if (m_sound_mixer.m_sound_cache == NULL) { + Int3(); + goto error_sub; + } + +// Create the Direct Sound Interface +// determine GUID for sound card which was chosen in the launcher... + ZeroMemory(&card_guid, sizeof(GUID)); + ZeroMemory(&zero_card_guid, sizeof(GUID)); + hresult = DirectSoundEnumerate(LLEnumCallback, &card_guid); + if (hresult == DS_OK) { + if (memcmp(&card_guid, &zero_card_guid, sizeof(GUID)) != 0) { + pguid = &card_guid; + } + } + +#ifdef SUPPORT_AUREAL + if (mixer_type == SOUND_MIXER_AUREAL) { + if (!A3D_Init((HWND)GameWindowHandle)) { + retval = false; + goto error_sub; + } + m_lp_ds = NULL; + m_sound_mixer.m_loop_method = DSLOOP_BUFFER_METHOD; + } + else +#endif + if (mixer_type != SOUND_MIXER_NONE) { + if (mixer_type == SOUND_MIXER_CREATIVE_EAX) { + if (!EAX_Create(pguid, &m_lp_ds)) { + mprintf((0, "Sound NT: Error EAX\n")); + retval = false; + goto error_sub; + } + } + else { + hresult = DirectSoundCreate(pguid, &m_lp_ds, NULL); + if (hresult != DS_OK) { + retval = false; + goto error_sub; + } + } + + ASSERT(m_lp_ds != NULL); + + // determine if we're using a crappy card + dscaps.dwSize = sizeof(DSCAPS); + m_lp_ds->GetCaps(&dscaps); + if (dscaps.dwFlags & DSCAPS_EMULDRIVER) { + mixer_type = SOUND_MIXER_DS_8; + mprintf((0, "SOUND INIT(1): We are in NT or have a crappy sound card\n")); + } + + m_sound_mixer.m_loop_method = DSLOOP_STREAM_METHOD; + } + + if (mixer_type != SOUND_MIXER_NONE) { + m_f_sound_lib_init = 1; + } + +// This section initializes the primary buffer +// software mixer try. +retry_mixer_init: + if(mixer_type == SOUND_MIXER_SOFTWARE_16) { + // test different conditions to see if we really can play sound in software + hresult = m_lp_ds->SetCooperativeLevel((HWND)m_hwnd_main, DSSCL_WRITEPRIMARY); + if(hresult != DS_OK) { + mprintf((0, "SOUND INIT(2): SCL: WritePrimary failed. Attempting DS 8 init.\n")); + mixer_type = SOUND_MIXER_DS_8; + goto retry_mixer_init; + } + + // Creates a primary buffer and makes sure we are at the specified frequency, bit depth, and that we + // can to 3d stuff too :) + m_mixer_type = SOUND_MIXER_SOFTWARE_16; + f16bit = true; + hresult = CreateDSBuffer(SBT_PRIMARY, &m_sb_info.m_lp_primary_buffer, + NULL, + 0, + 22050, // frequency + true, // stereo + f16bit); // 16-bit + if (hresult != DS_OK) { + if (m_sb_info.m_lp_primary_buffer) { + m_sb_info.m_lp_primary_buffer->Release(); + m_sb_info.m_lp_primary_buffer = NULL; + } + mprintf((0, "SOUND INIT(3): Cannot create primary buffer.\n")); + mixer_type = SOUND_MIXER_DS_8; + goto retry_mixer_init; + } + else { + // Determine if we are running NT or have a shitty Win95 sound card + DSBCAPS dsbcaps; + + ASSERT(m_sb_info.m_lp_primary_buffer); + + dsbcaps.dwSize = sizeof(DSBCAPS); + m_sb_info.m_lp_primary_buffer->GetCaps(&dsbcaps); + + + //Let's allocate our array of ints for our optimized software mixer! + if(!Fast_mixer) + { + Fast_mixer = (int *)mem_malloc(dsbcaps.dwBufferBytes*sizeof(int)); + Fast_mixer_len = dsbcaps.dwBufferBytes; + mprintf((0,"Using %d ints for fast software mixer\n",dsbcaps.dwBufferBytes)); + } + + // Is you want to see the caps, here is where -- mprintf all you want + if(!(dsbcaps.dwFlags & DSBCAPS_LOCHARDWARE)) + { + mprintf((0, "SOUND INIT(4): Primary is not in hardware\n")); + m_sb_info.m_lp_primary_buffer->Release(); + m_sb_info.m_lp_primary_buffer = NULL; + mixer_type = SOUND_MIXER_DS_8; + goto retry_mixer_init; + } + } + + // looping buffer is the same as the primary buffer for software mixers. + m_sb_info.m_lp_looping_buffer = m_sb_info.m_lp_primary_buffer; + + // Start the primary and have it always play. + m_f_sound_lib_init = StartStreaming(); + if(!m_f_sound_lib_init) { + mprintf((0, "SOUND INIT(5): Something went wrong in StartStreaming\n")); + m_sb_info.m_lp_primary_buffer->Release(); + m_sb_info.m_lp_primary_buffer = NULL; + mixer_type = SOUND_MIXER_DS_8; + goto retry_mixer_init; + } + + } +#ifdef SUPPORT_AUREAL + else if (mixer_type == SOUND_MIXER_AUREAL) { + m_mixer_type = SOUND_MIXER_AUREAL; + if (!A3D_CreateListener()) { + retval = false; + goto error_sub; + } + A3D_SetRolloffFactor(ENV3DVAL_ROLLOFF_DEFAULT); + A3D_SetUnitsPerMeter(1.0f); // feet per meter. + } +#endif + else if (mixer_type == SOUND_MIXER_NONE) { + m_mixer_type = mixer_type; + m_f_sound_lib_init = 0; + } + else { + // This is for DirectSound Internal Mixers. We let DirectSound do its magic. + m_mixer_type = mixer_type; + hresult = m_lp_ds->SetCooperativeLevel((HWND)m_hwnd_main, DSSCL_PRIORITY); + if(hresult != DS_OK) { + mprintf((0, "Sound NT: Error 1\n")); + retval = false; + goto error_sub; + } + + + // start primary buffer + f16bit = (mixer_type == SOUND_MIXER_DS_16 || mixer_type == SOUND_MIXER_DS3D_16 || mixer_type == SOUND_MIXER_CREATIVE_EAX); + hresult = CreateDSBuffer(SBT_PRIMARY, &m_sb_info.m_lp_primary_buffer, + NULL, + 0, + 22050, // frequency + true, // stereo + f16bit); // 8 or 16 bit + if (hresult != DS_OK) { + mprintf((0, "Sound NT: Error 2\n")); + if (m_sb_info.m_lp_primary_buffer) { + m_sb_info.m_lp_primary_buffer->Release(); + m_sb_info.m_lp_primary_buffer = NULL; + } + retval = false; + goto error_sub; + } + + m_sb_info.m_lp_primary_buffer->Play(0, 0, DSBPLAY_LOOPING); + } + +// buffered method doesn't use threads at all. + if (m_mixer_type != SOUND_MIXER_SOFTWARE_16 && m_mixer_type != SOUND_MIXER_NONE && m_sound_mixer.m_loop_method != DSLOOP_BUFFER_METHOD) { + // start looping thread, failure results in a 'clean exit' + if (!sb_loop_thread_init(this)) { + retval = false; + if (m_sb_info.m_lp_primary_buffer) { + m_sb_info.m_lp_primary_buffer->Release(); + m_sb_info.m_lp_primary_buffer = NULL; + } + goto error_sub; + } + } + + mprintf((0, "Sound mixer: ")); + retval = true; + + switch(m_mixer_type) + { + case SOUND_MIXER_SOFTWARE_16: mprintf((0, "Software 16\n")); break; + case SOUND_MIXER_DS_8: mprintf((0, "DS 8\n")); break; + case SOUND_MIXER_DS_16: mprintf((0, "DS 16\n")); break; + case SOUND_MIXER_DS3D_16: mprintf((0, "DS3D 16\n")); break; + case SOUND_MIXER_AUREAL: mprintf((0, "Aureal 3D\n")); break; + case SOUND_MIXER_CREATIVE_EAX: mprintf((0, "Creative EAX\n")); break; + case SOUND_MIXER_NONE: mprintf((0, "None\n")); break; + default: + mprintf((0, "LLSound ERROR: Unsupported mixer")); + Int3(); + retval = false; + break; + } + +// set 3d environment settings + t3dEnvironmentToggles env_toggles; + t3dEnvironmentValues env_values; + + memset(&Env3dValues, 0, sizeof(Env3dValues)); + memset(&Env3dToggles, 0, sizeof(Env3dToggles)); + + env_toggles.flags = 0; // get supported features + GetEnvironmentToggles(&env_toggles); + + env_values.flags = 0; // set up values and toggles to be set + env_toggles.flags = env_toggles.supported; + +// we may want to check this with configured options later -- samir + if (CHECK_FLAG(env_toggles.supported, ENV3DVALF_DOPPLER) && ENV3DTOG_DOPPLER) { + env_values.flags |= ENV3DVALF_DOPPLER; + env_values.doppler_scalar = ENV3DVAL_DOPPLER_DEFAULT; + env_toggles.doppler = true; + } + if (CHECK_FLAG(env_toggles.supported, ENV3DVALF_GEOMETRY) && ENV3DTOG_GEOMETRY) { + env_toggles.geometry = true; + } + SetEnvironmentToggles(&env_toggles); + SetEnvironmentValues(&env_values); + +///////////////////////////////////////////////////////////////////////////// + g_emulated_listener = &m_emulated_listener; + +error_sub: + if (retval == false) { + // Only gets here if there was an error +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_Destroy(); + } + else +#endif + if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + EAX_Destroy(); + } + else if (m_lp_ds) { + m_lp_ds->Release(); + } + m_lp_ds = NULL; + mprintf((0, "Sound Warning: Didn't initialize sound library.\n")); + if(m_sound_mixer.m_sound_cache != NULL) delete[] m_sound_mixer.m_sound_cache; + m_f_sound_lib_init = 0; + m_mixer_type = SOUND_MIXER_NONE; + } + + Global_DS_alloced_sounds=0; + + return (m_f_sound_lib_init); +} + + +// Cleans up after the sound library is done being used +void win_llsSystem::DestroySoundLib(void) +{ + bool f_all_done = false; + + if (!m_f_sound_lib_init) return; + + mprintf((0, "Start of sound system close\n")); + +// kill sound geometry object if any. + if (m_geometry) { + m_geometry->Shutdown(); + m_geometry = NULL; + } + + StopAllSounds(); + + // Wait till they are all done + mprintf((0, "Waiting for sounds to stop\n")); + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16) + { + while(!f_all_done) + { + f_all_done = true; + int i; + + for(i = 0; i < m_sound_mixer.m_max_sounds_played; i++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[i]; + if(sb->s && sb->s->kill_me && !(sb->m_status & (SSF_BUFFERED_LOOP+SSF_BUFFERED_STRM))) { + cleanup_directsound_looping_sb(&m_sound_mixer.m_sound_cache[i]); + } + else if(sb->m_status & (SSF_PLAY_LOOPING | SSF_PLAY_STREAMING)) { + // this should block until the sound is truly free, so we don't need to do a 'all done' + StopSound(m_sound_mixer.m_sound_cache[i].m_unique_id, SKT_HOLD_UNTIL_STOP); + // f_all_done = false; + } + } + } + + // buffered method doesn't use threads at all. + if (m_sound_mixer.m_loop_method != DSLOOP_BUFFER_METHOD) { + sb_loop_thread_kill(); + } +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_Flush(); + } +#endif + } + mprintf((0, "All sounds stopped\n")); + + if(m_mixer_type == SOUND_MIXER_SOFTWARE_16) + { + // Kill thread goes here + if (m_sb_info.thread_handle) + { + m_sb_info.thread_request_kill = true; + while(m_sb_info.thread_alive) + { + } + } + + if(m_mixer_type == SOUND_MIXER_SOFTWARE_16) + { + m_sb_info.m_lp_looping_buffer->Stop(); + m_sb_info.m_lp_looping_buffer->Release(); + } + } + +// free audio device; +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_Destroy(); + } + else +#endif + if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + EAX_Destroy(); + } + else if (m_lp_ds) { + m_lp_ds->Release(); + } + + m_lp_ds = NULL; + m_sb_info.m_lp_looping_buffer = NULL; + + mprintf((0, "End of sound system close\n")); + + if(m_sound_mixer.m_sound_cache != NULL) + delete[] m_sound_mixer.m_sound_cache; + + m_f_sound_lib_init = false; + g_emulated_listener = NULL; + m_mixer_type = -1; + ll_sound_ptr = NULL; + if(Fast_mixer) + { + mem_free(Fast_mixer); + Fast_mixer = NULL; + Fast_mixer_len = 0; + + } +} + + +// used to clean up direct sound buffer stuff +void win_llsSystem::cleanup_directsound_looping_sb(sound_buffer_info *sb) +{ + if (!sb->s) return; + if (!sb->s->kill_me) return; + + // should occur when kill_me set, sb_stop_buffer(&ll_sound_ptr->m_sound_mixer.m_sound_cache[i]); + sb_free_buffer(sb); + + sb->m_sound_buffer = NULL; + sb->s->playing = 0; + + void *p = (void *)sb->s; + sb->s = NULL; + sb->m_status = SSF_UNUSED; + mem_free(p); +} + + +// cleans up a sound -replaces a lot of repeated code whenever we cleanup sounds. +void win_llsSystem::update_directsound_sb(sound_buffer_info *sb, bool update_looping) +{ + if (m_mixer_type == SOUND_MIXER_SOFTWARE_16 || m_mixer_type == SOUND_MIXER_NONE) { + return; + } + +// take care of any streamed threaded method looping sounds that are done. +// orphan streaming structure, clean it up! (sound buffer is NULL yet sb->s is valid + if (sb->s && (((sb->m_status & (SSF_BUFFERED_LOOP+SSF_BUFFERED_STRM)) && !sb->m_sound_buffer) || sb->s->kill_me)) { + cleanup_directsound_looping_sb(sb); + } + else if (sb->m_status && !(sb->m_status & SSF_PAUSED)) { + // non playing sounds should be stopped always + int status = sb_get_status(sb); + + if (update_looping) { + // int status2 = A3D_GetSourceStatus(sb->m_snd_obj); + // mprintf((0, "(%x) status (%x)\n", sb->m_unique_id, status2)); + + if (status & SB_STATUS_PLAYING) { + if ((sb->m_status & (SSF_PLAY_STREAMING+SSF_BUFFERED_STRM)) == (SSF_PLAY_STREAMING+SSF_BUFFERED_STRM)) { + sb_stream_buffered_update(sb); + } + } + else { + if ((sb->m_status & (SSF_PLAY_LOOPING+SSF_BUFFERED_LOOP)) == (SSF_PLAY_LOOPING+SSF_BUFFERED_LOOP)) { + sb_buffered_loop_step(this, sb); + status = sb_get_status(sb); // get new status for following checks. + } + } + } + if(!(status & SB_STATUS_PLAYING)) { + StopSound(sb->m_unique_id); + } + else if (status & SB_STATUS_INVALID) { + StopSound(sb->m_unique_id); + } + } +} + + +// Stops the sound from playing -- f_immediately is used for looping samples -- i.e. so we can +// play the end of loop snipit +void win_llsSystem::StopSound(int sound_uid, unsigned char f_immediately) +{ + int current_slot; + sound_buffer_info *sb; + + if (!m_f_sound_lib_init) + return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) + return; + + sb = &m_sound_mixer.m_sound_cache[current_slot]; + + if(sb->m_status == SSF_UNUSED) + return; + +// update sound count. + m_sound_mixer.m_cur_sounds_played--; + + if(f_immediately == SKT_STOP_AFTER_LOOP) { + sb->m_status &= ~SSF_PLAY_LOOPING; + sb->m_status |= SSF_PLAY_NORMAL; + if (m_mixer_type != SOUND_MIXER_SOFTWARE_16 && (sb->m_status & SSF_BUFFERED_LOOP)) { + sb_buffered_loop_step(this, sb, DSBUFLOOP_FINISH_STEP); + } + return; + } + + if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + if(sb->s && !(sb->m_status & (SSF_BUFFERED_LOOP+SSF_BUFFERED_STRM))) + { + sb_loop_element_kill(sb); + if (f_immediately == SKT_HOLD_UNTIL_STOP) { + sb_loop_element_wait_until_dead(sb); + cleanup_directsound_looping_sb(sb); + } + return; + } + else if (sb->m_sound_buffer) // this works for buffered loops too. + { + if (sb->m_status & (SSF_BUFFERED_LOOP+SSF_BUFFERED_STRM)) { + if (sb->m_status & SSF_PLAY_STREAMING) { + sb_stream_element_kill(sb); + } + + void *p = (void *)sb->s; + if (p) { + mem_free(p); + sb->s = NULL; + } + } + GetSoundPos(sb->m_unique_id); + sb_stop_buffer(sb); + sb_free_buffer(sb); + + if (SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count > 0) { + SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count--; + //DUPSND if (SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count == 0) { + //DUPSND Global_DS_alloced_sounds--; + //DUPSND } + } + Global_DS_alloced_sounds--; + } + m_sound_mixer.m_sound_cache[current_slot].m_sound_buffer = NULL; + } + + +// mprintf((0, "SL cleaning slot %d\n", current_slot)); + m_sound_mixer.m_sound_cache[current_slot].m_status = SSF_UNUSED; +} + + +// Copies sound data from the external sound data block to an individual sound buffer +long LoadSoundData(LPDIRECTSOUNDBUFFER lp_dsb, char* sound_data_ptr, ulong total_bytes) { + LPVOID ptr1, ptr2; + DWORD len1, len2; + HRESULT result; + + ASSERT(lp_dsb != NULL && sound_data_ptr != NULL && total_bytes > 0); + +TryLockAgainLabel: + result = lp_dsb->Lock(0, total_bytes, &ptr1, &len1, &ptr2, &len2, 0); + + switch (result) { + case DS_OK: + memcpy(ptr1, sound_data_ptr, len1); + if(ptr2) + memcpy(ptr2, sound_data_ptr + len1, len2); + lp_dsb->Unlock(ptr1, len1, ptr2, len2); + break; + case DSERR_BUFFERLOST: + result = lp_dsb->Restore(); + if(result == DS_OK) + goto TryLockAgainLabel; + break; + } + + return (long)result; +} + + + +//Determines if a sound will play. Takes into account maximum allowable +//sounds. +//Also put prioritization code in here +// ignore reserved slots + +#ifdef _DEBUG +short win_llsSystem::FindFreeSoundSlot(int sound_index, float volume, int priority) +#else +short win_llsSystem::FindFreeSoundSlot(float volume, int priority) +#endif +{ + int current_slot; + sound_buffer_info *sb; + +// stop any left over sounds for Direct Sound. + if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sb = &m_sound_mixer.m_sound_cache[current_slot]; + update_directsound_sb(sb); + } + } + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sb = &m_sound_mixer.m_sound_cache[current_slot]; + if(sb->m_status == SSF_UNUSED) + { + if(!((m_mixer_type == SOUND_MIXER_DS_8 || m_mixer_type == SOUND_MIXER_DS_16 + || m_mixer_type == SOUND_MIXER_DS3D_16 || m_mixer_type == SOUND_MIXER_AUREAL || + m_mixer_type == SOUND_MIXER_CREATIVE_EAX) && sb->s)) + return current_slot; + + } + } + +// no more slots? take priority into account. +// throw out lowest priority sound slot (must be lower than or equal to new sound priority) + float weighted_priority = (priority*2.0f)*volume; + if (current_slot == m_sound_mixer.m_max_sounds_played) { + int throw_out_slot = -1, equiv_priority_slot = -1; + float weighted_priorityA, weighted_priorityB; + for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sb = &m_sound_mixer.m_sound_cache[current_slot]; + if (!(sb->m_status & (SSF_PLAY_LOOPING+SSF_PLAY_STREAMING))) { + weighted_priorityA = sb->play_info->priority*2.0f*sb->m_volume; + if (weighted_priorityA < weighted_priority) { + if (throw_out_slot == -1) { + throw_out_slot = current_slot; + } + else { + play_information *play_info2 = m_sound_mixer.m_sound_cache[throw_out_slot].play_info; + weighted_priorityB = play_info2->priority*2.0f*sb->m_volume; + if (weighted_priorityB > weighted_priorityA) { + throw_out_slot = current_slot; + } + } + } + + else if (equiv_priority_slot == -1 && weighted_priorityA == weighted_priority) { + equiv_priority_slot = current_slot; + } + } + } + + // if no slot found to stop, look for a slot with priority == new priority + if (throw_out_slot == -1) { + throw_out_slot = equiv_priority_slot; + } + if (throw_out_slot > -1) { + sb = &m_sound_mixer.m_sound_cache[throw_out_slot]; + win_llsSystem::StopSound(sb->m_unique_id, SKT_HOLD_UNTIL_STOP); + // mprintf((0, "DDSNDLIB: Replace sound (p:%d) with sound (p:%d) in slot %d\n", sb->play_info->priority, priority, throw_out_slot)); + return throw_out_slot; + } + } + +#ifdef _DEBUG + if (sound_index > -1) { + mprintf((0, "DDSNDLIB: Sound %s with priority (%d) too low.\n", Sounds[sound_index].name, priority)); + } + else { + mprintf((0, "DDSNDLIB: Sound unknown with priority (%d) too low.\n", priority)); + } +#endif + + + return -1; +} + + +int win_llsSystem::PlaySound2d(play_information *play_info, int sound_index, float f_volume, float f_pan, bool f_looped) +{ + sound_buffer_info *sb=NULL; + short sound_slot; + + if (!m_f_sound_lib_init) { + return -1; + } + +// calculate volume and pan + f_volume = (f_volume < 0.0f) ? 0.0f : (f_volume > 1.0f) ? 1.0f : f_volume; + play_info->left_volume = play_info->right_volume = f_volume; + + f_pan = (f_pan < -1.0f) ? -1.0f : (f_pan > 1.0f) ? 1.0f : f_pan; + if (f_pan < 0.0) { + play_info->right_volume += f_volume*f_pan; + } + else { + play_info->left_volume -= f_volume*f_pan; + } + + // do common processing. + if (SoundFiles[Sounds[sound_index].sample_index].used == 0) { + mprintf((0, "Tryed to play %d sound, it DNE.\n", sound_index)); + return -1; + } +#ifdef _DEBUG + sound_slot = FindFreeSoundSlot(sound_index, f_volume, play_info->priority); +#else + sound_slot = FindFreeSoundSlot(f_volume, play_info->priority); +#endif + if(sound_slot < 0) { + // do prioritization code here. + return -1; + } + sb = &m_sound_mixer.m_sound_cache[sound_slot]; + m_total_sounds_played++; + sb->play_info = play_info; + sb->m_unique_id = MakeUniqueId(sound_slot); + sb->m_buffer_type = SBT_2D; + sb->m_sound_index = sound_index; + sb->m_status = SSF_UNUSED; + + ASSERT(sb->m_unique_id != -1); + + +// play 2d sound + if (m_mixer_type == SOUND_MIXER_SOFTWARE_16) { + sb->m_status = (f_looped) ? SSF_PLAY_LOOPING : SSF_PLAY_NORMAL; + return sb->m_unique_id; + } + + if (f_looped) { + LoopStartStreaming(sb, SBT_2D, f_volume, f_pan, NULL); + if (sb->m_snd_obj == NULL) { + sb->m_status = SSF_UNUSED; + Int3(); + return -1; + } + } + else { + tPSBInfo psb; + + if (SoundFiles[Sounds[sound_index].sample_index].use_count > 0) { + if (DuplicateSoundBuffer(sb)) { + goto play_sound; + } + } + if (!CreateSoundBuffer(sb, false)) { + return -1; + } + if (!LoadSoundBuffer(sb)) { + sb_free_buffer(sb); + return -1; + } + +play_sound: + sb->m_status = SSF_PLAY_NORMAL; + + psb.volume = f_volume; + psb.pan = f_pan; + psb.freq = 22050; + psb.looping = false; + if (!PlaySoundBuffer(sb, &psb)) { + sb_free_buffer(sb); + return -1; + } + } + + m_sound_mixer.m_cur_sounds_played++; + + return sb->m_unique_id; +} + + +void win_llsSystem::LoopStartStreaming(sound_buffer_info *sb, int buffer_type, float volume, float pan, pos_state *cur_pos) +{ +// unsigned long thread_handle; + int sound_length, buffer_size; + int determined_method; + bool f_sample_16bit; + char *sample_ptr; + tPSBInfo psb; + DSLOOPSTREAM *s; + +// setup looping buffer. must clear out before assigning to sound buffer for thread security. + s = (DSLOOPSTREAM *) mem_malloc(sizeof(DSLOOPSTREAM)); + if(s == NULL) { + sb->s = NULL; + return; + } + memset((void *)s, 0, sizeof(DSLOOPSTREAM)); + sb->s = s; + + sample_ptr = get_sound_info(sb, &sound_length, &f_sample_16bit); + sb->s->f_sample_16bit = f_sample_16bit; + sb->m_buffer_type = buffer_type; + + if (m_sound_mixer.m_loop_method == DSLOOP_SMART_METHOD) { + int loop_startb, loop_endb; + bool b; + if (sb_get_loop_info(sb, &loop_startb, &loop_endb, &b)) { + if (SoundFiles[Sounds[sb->m_sound_index].sample_index].np_sample_length == (loop_endb-loop_startb+1)) { + determined_method = DSLOOP_BUFFER_METHOD; + } + else { + determined_method = DSLOOP_STREAM_METHOD; + } + } + else { // wtf? + return; + } + } + else { + determined_method = m_sound_mixer.m_loop_method; + } + + switch (determined_method) + { + case DSLOOP_BUFFER_METHOD: + sound_length = 0; + sb->s->loop_step = -1; + while (!sound_length && sb->s->loop_step < 2) + { + sample_ptr = sb_get_loop_step_info(sb, sb->s->loop_step, f_sample_16bit, &sound_length); + sb->s->loop_step++; + } + if (!sound_length && sb->s->loop_step == 2) { + // hmm, weird loop. no sample? + return; + } + sb->s->loop_step--; // return to proper step. + sb->s->loop_timer = 0.0f; + sb->s->bytes_left = sound_length; + buffer_size = sound_length; + sb->m_status = SSF_PLAY_LOOPING|SSF_BUFFERED_LOOP; + // mprintf((0, "DDSNDLIB: Starting buffered loop %d (step %d).\n", sb->m_unique_id, sb->s->loop_step)); + break; + default: + buffer_size = STREAM_BUFFER_SIZE; + sb->m_status = SSF_PLAY_LOOPING; // must go before buffer is initialized!!!!!! + } + + sb->sample_data = sample_ptr; + +// allocate buffer for playback + if (!CreateSoundBuffer( sb,false,buffer_size,(sb->m_status & SSF_BUFFERED_LOOP)?false:true)) { + return; + } + +// remind sb system and play. + if (determined_method == DSLOOP_BUFFER_METHOD) { + psb.looping = (sb->s->loop_step == 0) ? true : false; + if (!sb_load_buffer(sb, sb->sample_data, sound_length)) + return; + } + else if (sb_loop_element_init(sb, sample_ptr, sound_length, STREAM_BUFFER_SIZE)) { + psb.looping = true; + } + else { + sb->s->playing = 0; + return; + } + + if(buffer_type == SBT_3D) { + psb.cur_pos = cur_pos; + } + else { + ASSERT(buffer_type == SBT_2D); + psb.pan = pan; + } + psb.volume = volume; + psb.freq = 22050; + + PlaySoundBuffer(sb, &psb); + +// must be at end to initiate thread management. + sb->s->playing = 1; +} + + +void win_llsSystem::DSStartStreaming(sound_buffer_info *sb, float volume, float pan) +{ + tPSBInfo psb; + int sound_length; + int stream_buflength; + int determined_method; + char *sample_ptr; + bool f_stereo; + + sb->s = (DSLOOPSTREAM *) mem_malloc(sizeof(DSLOOPSTREAM)); + ASSERT(sb->s); + if(sb->s == NULL) + return; + + memset((void *)sb->s, 0, sizeof(DSLOOPSTREAM)); + + sample_ptr = (char *)sb->play_info->m_stream_data; + sound_length = sb->play_info->m_stream_size; + stream_buflength = sb->play_info->m_stream_bufsize; + + switch(sb->play_info->m_stream_format) + { + case SIF_STREAMING_16_M: + sb->s->f_sample_16bit = true; + f_stereo = false; + break; + + case SIF_STREAMING_8_M: + sb->s->f_sample_16bit = false; + f_stereo = false; + break; + + case SIF_STREAMING_16_S: + sb->s->f_sample_16bit = true; + f_stereo = true; + break; + + case SIF_STREAMING_8_S: + sb->s->f_sample_16bit = false; + f_stereo = true; + break; + + default: + ASSERT(0); + break; + } + + ASSERT(sb->m_buffer_type == SBT_2D); + sb->m_sound_index = -1; + +// determine how we will stream this data + determined_method = m_sound_mixer.m_loop_method; + if (m_sound_mixer.m_loop_method == DSLOOP_SMART_METHOD) { + if (m_mixer_type == SOUND_MIXER_AUREAL) { + determined_method = DSLOOP_BUFFER_METHOD; + } + } + + switch (determined_method) + { + case DSLOOP_BUFFER_METHOD: + sb->m_status = SSF_PLAY_STREAMING | SSF_BUFFERED_STRM; + break; + default: + sb->m_status = SSF_PLAY_STREAMING; // must go before buffer is initialized!!!!!! + break; + } + + if (!CreateSoundBuffer(sb,f_stereo,stream_buflength, true)) { + mem_free((void *)sb->s); + sb->s = NULL; + return; + } + +// set wave event or do thread streamed method + if (sb_stream_element_init(sb, sample_ptr, sound_length, stream_buflength)) { + psb.pan = pan; + psb.volume = volume; + psb.freq = 22050; + psb.looping = true; + + PlaySoundBuffer(sb, &psb); + + // must be at end to initiate thread management. + sb->s->playing = 1; + } +} + + +int win_llsSystem::PlayStream(play_information *play_info) +{ + short sound_slot; + DWORD ds_flags = 0; + + ASSERT(play_info != NULL); + + float volume = (play_info->left_volume > play_info->right_volume)?play_info->left_volume:play_info->right_volume; + + if (!m_f_sound_lib_init) return -1; + +#ifdef _DEBUG + sound_slot = FindFreeSoundSlot(-1, volume, play_info->priority); +#else + sound_slot = FindFreeSoundSlot(volume, play_info->priority); +#endif + // Out of sound slots + if(sound_slot < 0) + { + return -1; + } + +// mprintf((0, "TS(%d)Playing sound index %d at %d volume,%d pan\n", TotalSoundsPlayed, sound_index, volume, pan)); + + m_total_sounds_played++; + m_sound_mixer.m_sound_cache[sound_slot].play_info = play_info; + + m_sound_mixer.m_sound_cache[sound_slot].m_unique_id = MakeUniqueId(sound_slot); + ASSERT(m_sound_mixer.m_sound_cache[sound_slot].m_unique_id != -1); + + m_sound_mixer.m_sound_cache[sound_slot].m_buffer_type = SBT_2D; + + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16) + { + float volume; + float pan; + LPDIRECTSOUNDBUFFER sound_ptr; + + if(play_info->left_volume > play_info->right_volume) + { + volume = play_info->left_volume; + pan = -1.0f + (play_info->right_volume/play_info->left_volume); + } + else + { + volume = play_info->right_volume; + pan = 1.0f - (play_info->left_volume/play_info->right_volume); + } + + DSStartStreaming(&m_sound_mixer.m_sound_cache[sound_slot], volume, pan); + sound_ptr = m_sound_mixer.m_sound_cache[sound_slot].m_sound_buffer; + if(sound_ptr == NULL) + { + m_sound_mixer.m_sound_cache[sound_slot].m_sound_buffer = NULL; + return -1; + } + } + else { + m_sound_mixer.m_sound_cache[sound_slot].m_status = SSF_PLAY_STREAMING; + } + + m_sound_mixer.m_cur_sounds_played++; + + return(m_sound_mixer.m_sound_cache[sound_slot].m_unique_id); +} + +// Checks a Unique Sound ID and determines if that sound is still playing +bool win_llsSystem::IsSoundInstancePlaying(int sound_uid) +{ + int current_slot; + + if (!m_f_sound_lib_init) + return false; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) + return false; + +// mprintf((0, "Checking slot %d of UID %d\n", current_slot, sound_uid)); + + if(m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED) + { + return true; + } + + return false; +} + +//Is this sound placing on any "channel" +int win_llsSystem::IsSoundPlaying(int sound_index) +{ + int current_slot; + + if (!m_f_sound_lib_init) return -1; + + for (current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + if((m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED) && + (m_sound_mixer.m_sound_cache[current_slot].m_sound_index == sound_index)) + { + return m_sound_mixer.m_sound_cache[current_slot].m_unique_id; + } + } + + return -1; +} + +// This function limits the number of sounds cached to 255(8bits) and 256 bit is for invalid channel +// The purpose is to create unique signatures for each sound played (and allow for +// the slot_number to be quickly determined) +inline int win_llsSystem::MakeUniqueId(int sound_slot){ + return ((((int)m_total_sounds_played)<<8) + sound_slot); +} + +inline int win_llsSystem::ValidateUniqueId(int sound_uid) { + if(sound_uid == m_sound_mixer.m_sound_cache[sound_uid & 0x00FF].m_unique_id) + { + return sound_uid & 0x00FF; + } + else + { + return -1; + } +} + +void win_llsSystem::PauseSounds() +{ + int current_slot; + + if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + update_directsound_sb(sb); + + /* if((sb->m_status != SSF_UNUSED) && + ((sb->m_status & SSF_PAUSED) == 0)) + { + int status = sb_get_status(sb); + + if(!(status & SB_STATUS_PLAYING)) { + ll_sound_ptr->StopSound(sb->m_unique_id); + } + else if (status & SB_STATUS_INVALID) { + ll_sound_ptr->StopSound(sb->m_unique_id); + } + } + */ + } + } + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) + { + sb->m_status |= SSF_PAUSED; + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16 && (sb->m_sound_buffer)) + { + sb_stop_buffer(sb); + } + } + } +} + +void win_llsSystem::ResumeSounds() +{ + int current_slot; + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + + if(sb->m_status != SSF_UNUSED && (sb->m_status & SSF_PAUSED)) + { + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16) + { + PlaySoundBuffer(sb, NULL); + } + + m_sound_mixer.m_sound_cache[current_slot].m_status &= (~SSF_PAUSED); + } + } +} + + +void win_llsSystem::PauseSound(int sound_uid) +{ + int current_slot; + + if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + + if (sb->m_unique_id == sound_uid) { + update_directsound_sb(sb); + + /* if((sb->m_status != SSF_UNUSED) && ((sb->m_status & SSF_PAUSED) == 0)) + { + int status = sb_get_status(sb); + + if(!(status & SB_STATUS_PLAYING)) { + ll_sound_ptr->StopSound(sb->m_unique_id); + } + else if (status & SB_STATUS_INVALID) { + ll_sound_ptr->StopSound(sb->m_unique_id); + } + } + */ + break; + } + } + } + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + if (sb->m_unique_id == sound_uid) { + if (sb->m_status != SSF_UNUSED && !(sb->m_status & SSF_PAUSED)) + { + sb->m_status |= SSF_PAUSED; + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16 && (sb->m_sound_buffer)) + { + sb_stop_buffer(sb); + } + } + break; + } + } +} + + +void win_llsSystem::ResumeSound(int sound_uid) +{ + int current_slot; + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + if (sound_uid == m_sound_mixer.m_sound_cache[current_slot].m_unique_id) { + if(m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED && + (m_sound_mixer.m_sound_cache[current_slot].m_status & SSF_PAUSED)) + { + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16) + { + PlaySoundBuffer(&m_sound_mixer.m_sound_cache[current_slot], NULL); + } + + m_sound_mixer.m_sound_cache[current_slot].m_status &= (~SSF_PAUSED); + break; + } + } + } +} + + + +void win_llsSystem::StopAllSounds() +{ + int current_slot; + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + if(m_sound_mixer.m_sound_cache[current_slot].m_status != SSF_UNUSED) + { + StopSound(m_sound_mixer.m_sound_cache[current_slot].m_unique_id); + } + } +} + +// Begin sound frame +void win_llsSystem::SoundStartFrame(void) +{ + float frame_time; + int current_slot; + int i; + + if (m_timer_last_frametime == -1) { + frame_time = 0.0f; + } + else { + frame_time = (timer_GetMSTime() - m_timer_last_frametime)/1000.0f; + } + m_timer_last_frametime = timer_GetMSTime(); + +// perform necessary functions if sound events are pending for frame, this doesn't have to do anything +// if the mixer doesn't require such actions. Aureal does though. + if (m_pending_actions) { + mprintf((0, "pending actions\n")); +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_Flush(); + } +#endif + } + +// start mixer dependant frame +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_StartFrame(); + } +#endif + + m_in_sound_frame = true; + m_pending_actions = false; + +// cleanup sound cache. +// mprintf((0, "StartCleanup\n")); + if (m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + for(i = 0; i < m_sound_mixer.m_max_sounds_played; i++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[i]; + update_directsound_sb(sb, true); + } + } +// mprintf((0, "EndCleanup\n")); + + int counter = 0, loop_counter = 0, stream_counter=0, buf_loop_counter=0; + +#ifdef _DEBUG + int n_p5=0, n_p4=0, n_p3=0, n_p2=0, n_p1=0, n_p0=0; +#endif + + for(current_slot = 0; current_slot < m_sound_mixer.m_max_sounds_played; current_slot++) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + if(sb->m_status != SSF_UNUSED) + { + counter++; + if (sb->m_status & SSF_PLAY_LOOPING) { + if (sb->m_status & SSF_BUFFERED_LOOP) buf_loop_counter++; + loop_counter++; + } + if (sb->m_status & SSF_PLAY_STREAMING) stream_counter++; + + #ifdef _DEBUG + if (sb->play_info->priority == SND_PRIORITY_CRITICAL) n_p5++; + else if (sb->play_info->priority == SND_PRIORITY_HIGHEST) n_p4++; + else if (sb->play_info->priority == SND_PRIORITY_HIGH) n_p3++; + else if (sb->play_info->priority == SND_PRIORITY_NORMAL) n_p2++; + else if (sb->play_info->priority == SND_PRIORITY_LOW) n_p1++; + else if (sb->play_info->priority == SND_PRIORITY_LOWEST) n_p0++; + #endif + } + } + +// update cache stress timer. + if (counter < (m_sound_mixer.m_max_sounds_played*3/4)) { + m_cache_stress_timer += frame_time; + } + else { + m_cache_stress_timer = 0.0f; + } + +#ifdef _DEBUG + mprintf_at((3,2,0, "LNS: %02d/%02d", counter, m_sound_mixer.m_max_sounds_played)); + mprintf_at((3,3,1, "Lp: %02d", loop_counter)); + mprintf_at((3,4,1, "St: %02d", stream_counter)); + mprintf_at((3,5,0, " Ot: %02d", counter-loop_counter-stream_counter)); + + if (m_sound_mixer.m_loop_method != DSLOOP_STREAM_METHOD && m_mixer_type != SOUND_MIXER_SOFTWARE_16) { + mprintf_at((3,3,10,"Bf: %02d", buf_loop_counter)); + } + + mprintf_at((3,2,20, "P5:%02d P4:%02d P3:%02d", n_p5,n_p4,n_p3)); + mprintf_at((3,3,20, "P2:%02d P1:%02d P0:%02d", n_p2,n_p1,n_p0)); +#endif +} + + +// End sound frame +void win_llsSystem::SoundEndFrame(void) +{ +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_EndFrame(); + } +#endif + + CheckForErrors(); // handles errors. + + m_in_sound_frame = false; +} + + +bool win_llsSystem::LockSound(int sound_uid) +{ + return false; +} + +bool win_llsSystem::UnlockSound(int sound_uid) +{ + return false; +} + + +// True means it was already loaded, false means that it was not +bool win_llsSystem::CheckAndForceSoundDataAlloc(int sound_index) +{ + int result; + int sound_file_index = Sounds[sound_index].sample_index; + + ASSERT(sound_file_index >= 0 && sound_file_index < MAX_SOUND_FILES); + + if (sound_file_index < 0 || sound_file_index >= MAX_SOUND_FILES) { + return false; + } + + // Check if the sample data is already loaded + if(SoundFiles[sound_file_index].sample_16bit != NULL || SoundFiles[sound_file_index].sample_8bit != NULL) + return true; + + // If not, get the sound data + result = SoundLoadWaveFile(SoundFiles[sound_file_index].name, Sounds[sound_index].import_volume, sound_file_index, (m_sound_quality == SQT_HIGH), true); + + // Why would it load once (table load time) and not now? + if(!result) + return false; + + mprintf((0, "Sound %s loaded.\n", SoundFiles[sound_file_index].name)); + + return true; +} + +bool win_llsSystem::SetSoundQuality(char quality) +{ + int i; + + if(quality == m_sound_quality) + return true; + +// pause any sounds that may be playing + win_llsSystem::PauseSounds(); + + if(quality == SQT_NORMAL) + { + m_sound_quality = SQT_NORMAL; + } + else + { + m_sound_quality = SQT_HIGH; + } + + + for(i = 0; i < MAX_SOUNDS; i++) + { + if (Sounds[i].used != 0) + { + int j = Sounds[i].sample_index; + + if(SoundFiles[j].sample_8bit && m_sound_quality == SQT_HIGH) + { + GlobalFree(SoundFiles[j].sample_8bit); + SoundFiles[j].sample_8bit = NULL; + + CheckAndForceSoundDataAlloc(i); + } + if(SoundFiles[j].sample_16bit && m_sound_quality == SQT_NORMAL) + { + int count; + + ASSERT(SoundFiles[j].sample_8bit == NULL); + SoundFiles[j].sample_8bit = (unsigned char *) GlobalAlloc(0,SoundFiles[j].sample_length); + + // NOTE: Interesting note on sound conversion: 16 bit sounds are signed (0 biase). 8 bit sounds are unsigned (+128 biase). + for(count = 0; count < (int)SoundFiles[j].sample_length; count++) + { + SoundFiles[j].sample_8bit[count] = (unsigned char)((((int)SoundFiles[j].sample_16bit[count]) + 32767) >> 8); + } + + GlobalFree(SoundFiles[j].sample_16bit); + SoundFiles[j].sample_16bit = NULL; + } + } + } + + win_llsSystem::ResumeSounds(); + + return true; +} + +char win_llsSystem::GetSoundQuality(void) +{ + return m_sound_quality; +} + +bool win_llsSystem::SetSoundMixer(char mixer_type) +{ + if(mixer_type == m_mixer_type) + return true; + else + { + // Chris note: This is not the best way to do this. All the currently playing + // sounds are lost. This shouldn't happen. A real solutions has to take in account + // for three things: Normal sounds, looping sounds, and streaming audio. + DestroySoundLib(); + InitSoundLib(mixer_type, SoundApp, m_sound_mixer.m_max_sounds_played); + SetSoundQuality(m_sound_quality); + } + return true; +} + +char win_llsSystem::GetSoundMixer(void) +{ + return m_mixer_type; +} + + +void win_llsSystem::SetListener(pos_state *cur_pos) +{ + if (!m_f_sound_lib_init) return; + + m_emulated_listener.orient = *cur_pos->orient; + m_emulated_listener.position = *cur_pos->position; + m_emulated_listener.velocity = *cur_pos->velocity; + +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_SetListenerOrient(&cur_pos->orient->fvec, &cur_pos->orient->uvec); + A3D_SetListenerPosition(cur_pos->position->x, cur_pos->position->y, cur_pos->position->z); + A3D_SetListenerVelocity(cur_pos->velocity->x, cur_pos->velocity->y, cur_pos->velocity->z); + } + else +#endif + if(IS_3D_MIXER(m_mixer_type)) { + m_lp_listener->SetOrientation(cur_pos->orient->fvec.x, cur_pos->orient->fvec.y, cur_pos->orient->fvec.z, cur_pos->orient->uvec.x, cur_pos->orient->uvec.y, cur_pos->orient->uvec.z, DS3D_DEFERRED); + m_lp_listener->SetPosition(cur_pos->position->x, cur_pos->position->y, cur_pos->position->z, DS3D_DEFERRED); + m_lp_listener->SetVelocity(cur_pos->velocity->x, cur_pos->velocity->y, cur_pos->velocity->z, DS3D_DEFERRED); + m_lp_listener->CommitDeferredSettings(); + } + +} + + +// AdjustSound2d -- adjusts the volume, pan, and freq. of a sound +void win_llsSystem::AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency) +{ + int current_slot; + + if (!m_f_sound_lib_init) return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return; + if(m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED) return; + + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + play_information *play_info = sb->play_info; + + play_info->left_volume = play_info->right_volume = f_volume; + if (f_pan < 0.0) play_info->right_volume += f_volume * f_pan; + else play_info->left_volume -= f_volume * f_pan; + + sb_adjust_properties_2d(&m_sound_mixer.m_sound_cache[current_slot], f_volume, f_pan, frequency); + + return; +} + +void win_llsSystem::AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb) +{ + if (!m_f_sound_lib_init) return; + + LPDIRECTSOUNDBUFFER lp_dsb; + //LPDIRECTSOUND3DBUFFER lpDSB3D; + int current_slot; + + if (!m_f_sound_lib_init) return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return; + if(m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED) return; + +// sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + + if(IS_3D_MIXER(m_mixer_type)) + { + int sound_index = m_sound_mixer.m_sound_cache[current_slot].m_sound_index; + lp_dsb = m_sound_mixer.m_sound_cache[current_slot].m_sound_buffer; + if(lp_dsb == NULL) return; + + sb_adjust_properties_3d(&m_sound_mixer.m_sound_cache[current_slot],adjusted_volume,cur_pos, reverb); + } + else + { + // We need to determine the pan and volume + float volume; + + volume = adjusted_volume; + + float dist; + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float pan; + + dist = vm_NormalizeVector(&dir_to_sound); + if(dist < .1f) + { + dir_to_sound = m_emulated_listener.orient.fvec; + } + + if (dist >= Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].max_distance) + { + volume = 0.0f; + } + else if (dist > Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].min_distance) + { + volume *= (1.0 - ((dist - Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].min_distance) / (Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].max_distance - Sounds[m_sound_mixer.m_sound_cache[current_slot].m_sound_index].min_distance))); + } + + pan = (dir_to_sound * m_emulated_listener.orient.rvec); + + if(volume < 0.0f) + volume = 0.0f; + else if(volume > 1.0f) + volume = 1.0f; + + if(pan < -1.0f) + pan = -1.0f; + else if(pan > 1.0f) + pan = 1.0f; + + AdjustSound(m_sound_mixer.m_sound_cache[current_slot].m_unique_id, volume, pan, 22050); + } + + return; +} + + +int win_llsSystem::PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float adjusted_volume, bool f_looped, float reverb) +{ + short sound_slot; + DWORD ds_flags = 0; + float volume; + + volume = adjusted_volume; // Adjust base volume by sent volume, let 3d stuff do the rest + + if (!m_f_sound_lib_init) return -1; + + ASSERT(Sounds[sound_index].used != 0); + if(Sounds[sound_index].used == 0) return -1; + + if(!IS_3D_MIXER(m_mixer_type)) + { + float dist; + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float pan; + + dist = vm_NormalizeVector(&dir_to_sound); + if(dist < .1f) + { + dir_to_sound = m_emulated_listener.orient.fvec; + } + + if (dist >= Sounds[sound_index].max_distance) + { + return -1; + } + else if (dist > Sounds[sound_index].min_distance) + { + volume *= (1.0 - ((dist - Sounds[sound_index].min_distance) / (Sounds[sound_index].max_distance - Sounds[sound_index].min_distance))); + } + + pan = (dir_to_sound * m_emulated_listener.orient.rvec); + + if(volume < 0.0f) + volume = 0.0f; + else if(volume > 1.0f) + volume = 1.0f; + + if(pan < -1.0f) + pan = -1.0f; + else if(pan > 1.0f) + pan = 1.0f; + + return PlaySound2d(play_info, sound_index, volume, pan, f_looped); + } + +// Out of sound slots +#ifdef _DEBUG + sound_slot = FindFreeSoundSlot(sound_index, volume, play_info->priority); +#else + sound_slot = FindFreeSoundSlot(volume, play_info->priority); +#endif + + if(sound_slot < 0) { + return -1; + } + + m_sound_mixer.m_sound_cache[sound_slot].play_info = play_info; + m_total_sounds_played++; + m_sound_mixer.m_sound_cache[sound_slot].m_unique_id = MakeUniqueId(sound_slot); + ASSERT(m_sound_mixer.m_sound_cache[sound_slot].m_unique_id != -1); + +// 3-D!! + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[sound_slot]; + + sb->m_buffer_type = SBT_3D; + sb->m_sound_index = sound_index; + + if (f_looped) { + LoopStartStreaming(sb, SBT_3D, adjusted_volume, 0.0, cur_pos); + if (sb->m_snd_obj == NULL) { + sb->m_sound_buffer = NULL; + Int3(); + return -1; + } + } + else { + tPSBInfo psb; + + if (SoundFiles[Sounds[sound_index].sample_index].use_count > 0) { + if (DuplicateSoundBuffer(sb)) { + goto play_sound; + } + } + if (!CreateSoundBuffer(sb, false)) { + return -1; + } + if (!LoadSoundBuffer(sb)) { + sb_free_buffer(sb); + return -1; + } + +play_sound: + sb->m_status = SSF_PLAY_NORMAL; + + psb.cur_pos = cur_pos; + psb.volume = volume; + psb.freq = 22050; + psb.reverb = reverb; + psb.looping = false; + if (!PlaySoundBuffer(sb, &psb)) { + sb_free_buffer(sb); + return -1; + } + +// mprintf((0, "SL Play sound on slot %d, TP = %d UI = %X\n", sound_slot, TotalSoundsPlayed, m_sound_mixer.m_sound_cache[sound_slot].m_unique_id)); + } + + m_sound_mixer.m_cur_sounds_played++; + + return(sb->m_unique_id); +} + +// These work in samples to make things easier in the long run +int win_llsSystem::SetSoundPos(int sound_uid, int pos) +{ + int current_slot; + + if(pos <= 0) + return 1; + + if (!m_f_sound_lib_init) return -1; + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return -1; + if(m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED) return -1; + + m_sound_mixer.m_sound_cache[current_slot].play_info->m_samples_played = pos; + + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + + if (sb->stereo) { + pos *= 2; + } + + if (sb->bps == 16) { + pos *= 2; + } + + sb_set_current_position(sb, pos); + } + + return 1; +} + +// These work in samples to make things easier in the long run +int win_llsSystem::GetSoundPos(int sound_uid) +{ + int current_slot; + uint temp, pos; + + if (!m_f_sound_lib_init) return -1; + if((current_slot = ValidateUniqueId(sound_uid)) == -1) return -1; + if(m_sound_mixer.m_sound_cache[current_slot].m_status == SSF_UNUSED) return -1; + + if(m_mixer_type != SOUND_MIXER_SOFTWARE_16) + { + sound_buffer_info *sb = &m_sound_mixer.m_sound_cache[current_slot]; + + temp = sb_get_current_position(sb, &pos); + + if(sb->stereo) { + pos /= 2; + } + if(sb->bps == 16) { + pos /= 2; + } + + // Updates the readable data + m_sound_mixer.m_sound_cache[current_slot].play_info->m_samples_played = pos; + + return pos; + } + else + { + return m_sound_mixer.m_sound_cache[current_slot].play_info->m_samples_played; + } + + return -1; +} + + +//////////////////////////////////////////////////////////////////////////////// +bool win_llsSystem::CreateSoundBuffer(sound_buffer_info *sb, bool f_is_stereo, int size, bool dynamic) +{ +// do buffer creation + int buftype = sb->m_buffer_type; + bool f_sample_16bit; + int sound_length; + + get_sound_info(sb, &sound_length, &f_sample_16bit); + + if (size != -1) { + sound_length = size; + } + + switch (m_mixer_type) + { + case SOUND_MIXER_SOFTWARE_16: + Int3(); + break; + +#ifdef SUPPORT_AUREAL + case SOUND_MIXER_AUREAL: + sb->m_snd_obj = A3D_CreateSSource(sound_length, + f_sample_16bit ? 16 : 8, + f_is_stereo, + (dynamic) ? true : false, + 22050, + (buftype == SBT_3D) ? true : false); + if (!sb->m_snd_obj) { + Int3(); + sb->m_status = SSF_UNUSED; + return false; + } + break; +#endif + default: + CreateDSBuffer(buftype, &sb->m_sound_buffer, + &sb->m_sound_buffer_3d, + sound_length, + 22050, + f_is_stereo, + f_sample_16bit); + + sb->m_lpksps = NULL; + if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + EAX_InitSource(sb->m_sound_buffer_3d, &sb->m_lpksps); + } + + if (!sb->m_sound_buffer) { + Int3(); + sb->m_status = SSF_UNUSED; + return false; + } + } + + sb->m_mixer_type = m_mixer_type; + sb->bps = f_sample_16bit ? 16 : 8; + sb->stereo = f_is_stereo; + + if (sb->m_sound_index > -1) { + SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count++; + // Global_DS_alloced_sounds++; + } + + return true; +} + + +bool win_llsSystem::LoadSoundBuffer(sound_buffer_info *sb) +{ +// play 2d sound + int buftype = sb->m_buffer_type; + bool f_sample_16bit; + char *sample_ptr; + int sound_length; + +// get pointer + sample_ptr = get_sound_info(sb, &sound_length, &f_sample_16bit); + sb->sample_data = sample_ptr; + sb->sample_length = (int)sound_length; + +// do buffer creation + switch (m_mixer_type) + { + case SOUND_MIXER_SOFTWARE_16: + Int3(); + break; +#ifdef SUPPORT_AUREAL + case SOUND_MIXER_AUREAL: + if (!A3D_LoadSample(sb->m_snd_obj, sample_ptr, sound_length)) { + Int3(); + sb->m_status = SSF_UNUSED; + return false; + } + break; +#endif + default: + if(LoadSoundData(sb->m_sound_buffer, sample_ptr, sound_length) != DS_OK) { + Int3(); + sb->m_status = SSF_UNUSED; + return false; + } + } + + return true; +} + + +bool win_llsSystem::PlaySoundBuffer(sound_buffer_info *sb, tPSBInfo *psb) +{ + bool f_looping; + + if (!m_f_sound_lib_init) return false; + if (!m_in_sound_frame) m_pending_actions = true; + +TryPlayAgainLabel: + if (psb) { + if (sb->m_buffer_type == SBT_2D) { + AdjustSound(sb->m_unique_id, psb->volume, psb->pan, psb->freq); + } + else if (sb->m_buffer_type == SBT_3D) { + AdjustSound(sb->m_unique_id, psb->cur_pos, psb->volume, psb->reverb); + } + + SetSoundPos(sb->m_unique_id, sb->play_info->m_samples_played); + + f_looping = psb->looping; + } + else { + f_looping = ((sb->m_status & SSF_PLAY_LOOPING) || sb->s) ? true : false; + if (f_looping && m_sound_mixer.m_loop_method == DSLOOP_BUFFER_METHOD && sb->s->loop_step != 0 && (sb->m_status&SSF_BUFFERED_LOOP)) { + f_looping = false; + } + } + + switch (m_mixer_type) + { +#ifdef SUPPORT_AUREAL + case SOUND_MIXER_AUREAL: + { + float priority; + switch (sb->play_info->priority) + { + case SND_PRIORITY_CRITICAL: + case SND_PRIORITY_HIGHEST: priority = 1.0f; break; + case SND_PRIORITY_HIGH: priority = 0.85f; break; + case SND_PRIORITY_NORMAL: priority = 0.60f; break; + case SND_PRIORITY_LOW: priority = 0.30f; break; + case SND_PRIORITY_LOWEST: priority = 0.1f; break; + default: priority = 0.5f; + } + A3D_SetSourcePriority(sb->m_snd_obj, priority); + A3D_Play(sb->m_snd_obj, f_looping); + } + break; +#endif + + case SOUND_MIXER_SOFTWARE_16: + Int3(); + break; + + default: + { + LPDIRECTSOUNDBUFFER sound_ptr = sb->m_sound_buffer; + DWORD ds_flags = f_looping ? DSBPLAY_LOOPING : 0; + + ASSERT(sound_ptr); + + if(sound_ptr->Play(0, 0, ds_flags) == DSERR_BUFFERLOST) { + if (LoadSoundData(sound_ptr, sb->sample_data, sb->sample_length) == DS_OK) { + goto TryPlayAgainLabel; + } + + sb->m_status = SSF_UNUSED; + + // Some type of error -- we cannot play the sound? -- get chris + Int3(); + return false; + } + } + } + + return true; +} + + +bool win_llsSystem::DuplicateSoundBuffer(sound_buffer_info *sb) +{ + return false; // for now, let's not do this. + +/* + sound_buffer_info *source_sb = m_sound_mixer.FindSoundBuffer(sb->m_sound_index); + + ASSERT(m_mixer_type != SOUND_MIXER_SOFTWARE_16); + + if (!source_sb) { + Int3(); + return false; + } + +// we have a source sound buffer. let's use it. + if (m_mixer_type == SOUND_MIXER_AUREAL) { + sb->m_snd_obj = A3D_DuplicateSource(source_sb->m_snd_obj); + if (!sb->m_snd_obj) { + return false; + } + } + else { + HRESULT hr; + hr = m_lp_ds->DuplicateSoundBuffer(source_sb->m_sound_buffer, &sb->m_sound_buffer); + if (FAILED(hr)) { + mprintf((0, "DDSNDLIB: Failed to duplicate sound buffer (%x)\n", hr)); + return false; + } + + if(IS_3D_MIXER(m_mixer_type)) { + // a 3d buffer needs a 3d interface pointer + if(sb->m_buffer_type == SBT_3D) + { + hr = sb->m_sound_buffer->QueryInterface(IID_IDirectSound3DBuffer, (void **)&sb->m_sound_buffer_3d); + if (FAILED(hr)) { + mprintf((0, "DDSNDLIB: Failed to acquire 3d interface from duplicate buffer (%x)\n", hr)); + return false; + } + } + } + + } + + sb->m_mixer_type = source_sb->m_mixer_type; + sb->bps = source_sb->bps; + sb->stereo = source_sb->stereo; + sb->sample_data = source_sb->sample_data; + sb->sample_length = source_sb->sample_length; + +// mprintf((0, "Duplicated!!\n")); + SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count++; + + return true; +*/ +} + + +// environmental sound interface +// volume modifier (0-1), damping(0-1), 1 = complete, 0 = none +// decay 0.1 to 100 seconds, how long it takes for a sound to die. +bool win_llsSystem::SetGlobalReverbProperties(float volume, float damping, float decay) +{ +#ifdef SUPPORT_AUREAL + if (m_mixer_type == SOUND_MIXER_AUREAL) { + return A3D_SetEnvironmentalReverb(volume, damping, decay); + } + else +#endif + if (m_mixer_type == SOUND_MIXER_CREATIVE_EAX) { + return EAX_SetEnvironmentalReverb(volume, damping, decay); + } + + return false; +} + + + +sound_buffer_info *sound_buffer_cache::FindSoundBuffer(int sound_index) +{ + int i; + +// is there's no use count for this soudn, then there should be no available buffer. + if (SoundFiles[Sounds[sound_index].sample_index].use_count == 0) { + return NULL; + } + +// returns a sound buffer with the 'sound_index' if it is still active. +// we can use the sound buffer to create duplicates. + for(i = 0; i < m_max_sounds_played; i++) + { + sound_buffer_info *sb = &m_sound_cache[i]; + + if (sb->m_status != SSF_UNUSED) { + if (sb->m_sound_index == sound_index) { + return sb; + } + } + + } + + return NULL; +} + + +// set special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void win_llsSystem::SetEnvironmentValues(const t3dEnvironmentValues *env) +{ + if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) { + Env3dValues.doppler_scalar = env->doppler_scalar; + +#ifdef SUPPORT_AUREAL + switch (m_mixer_type) + { + case SOUND_MIXER_AUREAL: + A3D_SetDopplerFactor(Env3dToggles.doppler ? Env3dValues.doppler_scalar : 0.0f); + break; + } +#endif + } +} + + +// get special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void win_llsSystem::GetEnvironmentValues(t3dEnvironmentValues *env) +{ + if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) { + env->doppler_scalar = Env3dValues.doppler_scalar; + } +} + + +// enable special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void win_llsSystem::SetEnvironmentToggles(const t3dEnvironmentToggles *env) +{ + t3dEnvironmentValues values; + + if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) { + // set toggle then set doppler again. + Env3dToggles.doppler = env->doppler; + values.flags = ENV3DVALF_DOPPLER; + values.doppler_scalar = Env3dValues.doppler_scalar; + SetEnvironmentValues(&Env3dValues); + } + if (CHECK_FLAG(env->flags, ENV3DVALF_GEOMETRY)) { + // initialize geometry object if available and true. + if (m_geometry) { + delete m_geometry; + } + m_geometry = NULL; + if (env->geometry) { + if (m_mixer_type == SOUND_MIXER_AUREAL) { + m_geometry = new llsGeometry; + if (!m_geometry->Init(this)) { + delete m_geometry; + m_geometry = NULL; + } + } + } + Env3dToggles.geometry = m_geometry ? true : false; + } +} + + +// get states of special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void win_llsSystem::GetEnvironmentToggles(t3dEnvironmentToggles *env) +{ + if (CHECK_FLAG(env->flags, ENV3DVALF_DOPPLER)) { + env->doppler = Env3dToggles.doppler; + } + + env->supported = 0; + + switch (m_mixer_type) + { + case SOUND_MIXER_AUREAL: + env->supported |= ENV3DVALF_DOPPLER; + env->supported |= ENV3DVALF_GEOMETRY; + break; + } +} + + +// Sound System Error Handler. +void win_llsSystem::CheckForErrors() +{ +// if a fatal error occurred, quit and display an error +// non fatal errors should be put inside a logfile, or just mprinted out. + switch (m_lib_error_code) + { + case SSL_ERROR_SAMPLE_NODATA: + Error("%s\nSample had no data.",m_error_text); + break; + + case SSL_ERROR_STREAMMIXER: + Error("%s\nMixer alignment check failed.", m_error_text); + break; + + case SSL_ERROR_GENERIC: + Error("%s\nGeneric error.", m_error_text); + break; + } + +// must call! + llsSystem::CheckForErrors(); + +// add our default error string. + char buf[8]; + sprintf(buf,"mix:%d\n", m_mixer_type); + strcat(m_error_text, buf); +} + + +///////////////////////////////////////////////////////////////////////////////////// +// set auxillary 3d sound properties +bool win_llsSystem:: SoundPropertySupport() const +{ + switch (m_mixer_type) + { + case SOUND_MIXER_CREATIVE_EAX: + if (EAX_Caps() & EAXF_SOURCE_OBSTRUCTION) { + return true; + } + break; + } + + return false; +} + + +// sound obstruction from 0 to 1.0 (1.0 = fully obstructed) +void win_llsSystem::SetSoundProperties(int sound_uid, float obstruction) +{ + sound_buffer_info *sb; + int current_slot; + + if (!m_f_sound_lib_init) + return; + + if((current_slot = ValidateUniqueId(sound_uid)) == -1) + return; + + sb = &m_sound_mixer.m_sound_cache[current_slot]; + + switch (m_mixer_type) + { + case SOUND_MIXER_CREATIVE_EAX: + EAX_SetSourceProperties(sb->m_lpksps, obstruction); + break; + } +} diff --git a/dd_sndlib/Ia3dutil.h b/dd_sndlib/Ia3dutil.h new file mode 100644 index 000000000..535ac7e7f --- /dev/null +++ b/dd_sndlib/Ia3dutil.h @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------- + * + * $Workfile: Ia3dutil.h $ + * + *--------------------------------------------------------------------------- + *$NoKeywords: $ + */ + +/* +#ifndef _IA3DUTIL_H_ +#define _IA3DUTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern HRESULT A3dInitialize(void); +extern void A3dUninitialize(void); +extern HRESULT A3dCreate(GUID *,void **,IUnknown FAR *,DWORD); +extern HRESULT A3dRegister(void); + +#ifdef __cplusplus +}; +#endif + +#endif // _IA3DUTIL_H_ +*/ diff --git a/dd_sndlib/auddev.h b/dd_sndlib/auddev.h new file mode 100644 index 000000000..4cebde7c6 --- /dev/null +++ b/dd_sndlib/auddev.h @@ -0,0 +1,221 @@ +/* + * $Source: $ + * $Revision: 16 $ + * $Author: Samir $ + * $Date: 8/23/99 5:29p $ + * + * + * + * $Log: /DescentIII/Main/dd_sndlib/auddev.h $ + * + * 16 8/23/99 5:29p Samir + * incremental EAX 2.0 checkin + * + * 15 8/13/99 2:00p Samir + * more aureal and geometry fixes. + * + * 14 8/11/99 3:12p Samir + * fixes for aureal support. + * + * 13 5/03/99 3:12a Samir + * added event management routines. + * + * 12 4/29/99 3:01p Samir + * added some more functions. + * + * 11 4/23/99 7:51p Samir + * added flush function. + * + * 10 4/13/99 4:09p Samir + * more priority stuff. + * + * 9 4/06/99 8:30p Samir + * added reflection support. + * + * 8 3/29/99 11:02a Samir + * added geometry supoprt. + * + * 7 1/14/99 6:10p Samir + * added DirectSound buffer duplication code. + * + * 6 1/11/99 5:52p Samir + * updated EAX support. + * + * 5 1/08/99 6:31p Samir + * added reverb + * + * 4 1/08/99 11:36a Samir + * implemented basic Aureal 2.0 support. + * + * 3 12/23/98 11:50a Samir + * + * 2 12/23/98 11:48a Samir + * basic functionality. + * + * 1 12/22/98 11:56a Samir + * + */ + +#ifndef AUDDEV_H +#define AUDDEV_H + +#include "dsound.h" +#include "vmanpset.h" +#include "pstypes.h" +#include "vecmat.h" + +#define SOUND_DECAY_MAXTIME 100.0f +#define SOUND_GAIN_MAXSCALAR 1.0f + +//#define SUPPORT_AUREAL + +// Creative labs Environmental Audio Extension Interface + +// starts up standard audio device (DirectSound) +bool EAX_Create(GUID *pguid, LPDIRECTSOUND *lpds); + +// shutdown standard audio device (DirectSound) +void EAX_Destroy(); + +// returns EAX caps +#define EAXF_SOURCE_OBSTRUCTION 16 +int EAX_Caps(); + +// initializes EAX specific interfaces. +bool EAX_SetPrimaryBuffer(); + +// sets up current global environment reverb +bool EAX_SetEnvironmentalReverb(float volume, float damping, float decay); + +// intializes a sound source for EAX +bool EAX_InitSource(LPDIRECTSOUND3DBUFFER lpBuffer3D, LPKSPROPERTYSET *plpksp); + +// frees an eax sound source +void EAX_FreeSource(LPKSPROPERTYSET lpksp); + +// sets source properties +void EAX_SetSourceProperties(LPKSPROPERTYSET lpksp, float obstruction); + + + +#ifdef SUPPORT_AUREAL +////////////////////////////////////////////////////////////////////////////// +// Aureal 2.0 Direct Path Interface + +// Initialize object +bool A3D_Init(HWND hwnd); + +// destroy object +void A3D_Destroy(); + +// sets units of measurement +void A3D_SetUnitsPerMeter(float units); + +// sets rolloff for all source objects (from 0.0f to ???) +void A3D_SetRolloffFactor(float scalar); + +// sets doppler for all source objects (from 0.0f to ???) (1.0f is default, 0.0f is none) +void A3D_SetDopplerFactor(float scalar); + +// create or destroy geometry object +bool A3D_CreateGeometryInterface(); +void A3D_DestroyGeometryInterface(); + +// create listener +bool A3D_CreateListener(); + +// create source +void *A3D_CreateSSource(int size, int bps, bool stereo, bool streaming, ushort freq =22050,bool is3d=false); + +// duplicates source +void *A3D_DuplicateSource(void *src); + +// frees source +void A3D_FreeSSource(void *obj); + +// loads data into source +bool A3D_LoadSample(void *obj, void *src_data, int src_length); + +// plays a sound +void A3D_Play(void *obj, bool looping); + +// stops a sound +void A3D_Stop(void *obj); + +// returns current position in buffer of playback +void A3D_GetCurrentPosition(void *obj,uint *playp); + +// sets current position in buffer +void A3D_SetCurrentPosition(void *obj, uint pos); + +// sets properties for source object +// priority is from 0 to 1.0 +void A3D_SetSourcePriority(void *obj, float priority); + +// pan from 0 to 1.0f +void A3D_SetSourcePan(void *obj, float lpan, float rpan); +void A3D_GetSourcePan(void *obj, float *lpan, float *rpan); + +// volume from 0 to 1.0f +void A3D_SetSourceVolume(void *obj, float volume); +float A3D_GetSourceVolume(void *obj); + +void A3D_SetSourceCone(void *obj, float x, float y, float z); + +void A3D_SetSourceVelocity(void *obj, float x, float y, float z); +void A3D_GetSourceVelocity(void *obj, vector *vel); + +void A3D_SetSourcePosition(void *obj, float x, float y, float z); +void A3D_GetSourcePosition(void *obj, vector *pos); + +void A3D_SetSourceOrientation(void *obj, const vector *fvec, const vector *uvec); +void A3D_GetSourceOrientation(void *obj, matrix *orient); + +bool A3D_SetSourceWaveEvent(void *obj, DWORD offset, HANDLE hEvent); +bool A3D_ClearSourceWaveEvents(void *obj); + +void A3D_SetMinMaxDistance(void *obj, float min, float max, bool mute_max=false); + +// sets properties for listener +void A3D_SetListenerVelocity(float x, float y, float z); +void A3D_SetListenerOrient(vector *fvec, vector *uvec); +void A3D_SetListenerPosition(float x, float y, float z); + +// call at start of sound frame +void A3D_StartFrame(); +void A3D_EndFrame(); +void A3D_Flush(); + +// gets status of current source buffer +#define A3D_STATUS_PLAYING 0x1 + +int A3D_GetSourceStatus(void *obj); + +bool A3D_Lock(void *obj, DWORD dwWriteCursor, DWORD dwWriteBytes, void **ptr1, LPDWORD plen1, void **ptr2, LPDWORD plen2); +bool A3D_Unlock(void *obj, void *ptr1, DWORD len1, void *ptr2, DWORD len2); + + +// sets up current global environment reverb +bool A3D_SetEnvironmentalReverb(float volume, float damping, float decay); + +// room builder +// list = list id. 0 is only valid. +void A3D_GeomListStart(int list); +void A3D_GeomListEnd(int list); +void A3D_GeomListExec(int list); +void A3D_Clear(); // clears geometry info + +// all below functions act on the current list. +void A3D_GeomAddListener(vector *pos, matrix *orient); +void A3D_GeomAddSource(void *obj, vector *pos, matrix *orient); +void A3D_GeomBindMaterial (void *obj); +void A3D_GeomAddTriangle(unsigned tag, vector **verts); +void A3D_GeomAddQuad(unsigned tag, vector **verts); + +// used to create aureal 2.0 materials. +// gain and highfreq values range from 0 to 1.0 +void *A3D_CreateMaterial(float xmit_gain, float xmit_highfreq, float reflect_gain, float reflect_highfreq); +void A3D_DestroyMaterial(void *obj); +#endif + +#endif \ No newline at end of file diff --git a/dd_sndlib/aureal3d.cpp b/dd_sndlib/aureal3d.cpp new file mode 100644 index 000000000..e4912e6fe --- /dev/null +++ b/dd_sndlib/aureal3d.cpp @@ -0,0 +1,1246 @@ +/* + * $Logfile: /DescentIII/Main/dd_sndlib/aureal3d.cpp $ + * $Revision: 18 $ + * $Date: 8/19/99 10:25a $ + * $Author: Samir $ + * + * Aureal enhancements to sound library. + * + * $Log: /DescentIII/Main/dd_sndlib/aureal3d.cpp $ + * + * 18 8/19/99 10:25a Samir + * tweaks for Aureal as requested by Aureal. + * + * 17 8/13/99 2:00p Samir + * more aureal and geometry fixes. + * + * 16 8/11/99 3:12p Samir + * fixes for aureal support. + * + * 15 7/30/99 5:03p Samir + * an attempt to get some aureal reflection support working. + * + * 14 7/20/99 4:09p Samir + * fixed reflection support. tag polygons for reuse. + * + * 13 5/03/99 12:35p Samir + * added config file support for aureal features. + * + * 12 5/03/99 3:12a Samir + * added event management routines. + * + * 11 4/29/99 3:01p Samir + * added some more functions. + * + * 10 4/23/99 7:51p Samir + * added flush function + * + * 9 4/13/99 4:09p Samir + * more priority stuff. + * + * 8 4/06/99 8:30p Samir + * added reflection support. + * + * 7 4/01/99 4:28p Samir + * added some error checking. + * + * 6 3/29/99 10:53a Samir + * added new Aureal 2.0 sdk support. + * + * 5 1/14/99 6:10p Samir + * added DirectSound buffer duplication code. + * + * 4 1/08/99 6:31p Samir + * added reverb + * + * 3 1/08/99 11:36a Samir + * implemented basic Aureal 2.0 support. + * + * 2 12/23/98 11:48a Samir + * basic functionality. + * + * 1 7/02/98 4:38p Samir + * Initial revision. + * + * $NoKeywords: $ + */ + +#if 0 + + +#include "auddev.h" + +#include +#include + +#include +#include + +#include "ia3dutil.h" +#include "ia3dapi.h" +#include "pserror.h" +#include "logfile.h" +#include "Macros.h" +#include "inffile.h" + +typedef struct tA3D +{ + IA3d4 *a3d; + IA3dListener *listener; + IA3dGeom *geom; // Geometry object. + IA3dList *list; // list of geometry commands for a room. + DWORD geom_mode_flags; // active elements in A3d geometry engine. + float global_gain; + float reflect_time; + float reflect_scale; // reflection scale + float reflect_gain; // reflection game + bool do_occlusions; + bool do_reflections; + sbyte reflection_update_interval; +} +tA3D; + +static tA3D A3D = { NULL, NULL, NULL, NULL, 0, }; +static logfile a3dlog; +static bool A3D_first_init = false; + +const DWORD A3D_FEATURES = A3D_OCCLUSIONS | A3D_1ST_REFLECTIONS | A3D_DISABLE_SPLASHSCREEN; +const A3DVAL A3D_PRIORITY_BIAS = 0.60f; +const DWORD A3D_FALLBACK_SOURCES = 16; + +int A3D_buffers_allocated=0; + +void A3D_FinalShutdown(); + + +#define A3DCMD_NUM 7 +#define A3DCMD_GAIN 0 +#define A3DCMD_OCC 1 +#define A3DCMD_REF 2 +#define A3DCMD_REFINT 3 +#define A3DCMD_REFTIME 4 +#define A3DCMD_REFSCALE 5 +#define A3DCMD_REFGAIN 6 + +const char *A3DCommands[A3DCMD_NUM] = { + "gain", + "occlusions", + "reflections", + "reflectinterval", + "reflecttime", + "reflectscale", + "reflectgain" +}; + + + +int A3DLex(const char *command) +{ + for (int i = 0; i < A3DCMD_NUM; i++) + { + if (strcmp(A3DCommands[i], command) == 0) + return i; + } + + return INFFILE_ERROR; +} + + +////////////////////////////////////////////////////////////////////////////// +// Initialize object +bool A3D_Init(HWND hwnd) +{ + InfFile cfgfile; + int hr; + + if (!A3D_first_init) { + a3dlog.start("a3d.log", "Aureal 3D 2.0 Interface"); + atexit(A3D_FinalShutdown); + A3D_first_init = true; + } + + a3dlog.puts("initializing\n"); + +// Core Initialization + hr = A3dInitialize(); + if (SUCCEEDED(hr)) { + hr = A3dCreate(NULL, (void **)&A3D.a3d, NULL, A3D_FEATURES); + if (hr != S_OK) { + a3dlog.printf("failed to create a3d object (%x).\n", hr); + A3dUninitialize(); + return false; + } + } + +// okay read in config file. + A3D.global_gain = 1.0f; + A3D.do_occlusions = true; + A3D.do_reflections = true; + A3D.reflection_update_interval = 1; + A3D.reflect_time = 1.0f; + A3D.reflect_scale = 0.95f; + A3D.reflect_gain = A3D.global_gain; + + if (cfgfile.Open("a3d.inf", NULL, A3DLex)) { + a3dlog.puts("a3d.inf found:\n"); + while (cfgfile.ReadLine()) + { + char operand[32]; + int cmd; + while ((cmd = cfgfile.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) + { + switch (cmd) + { + float fval; + bool bval; + int ival; + + case A3DCMD_GAIN: + fval = atof(operand); + if (fval < 0) fval = 0; + else if (fval > 1.0f) fval = 1.0f; + A3D.global_gain = fval; + a3dlog.printf("gain = %.2f\n", fval); + break; + case A3DCMD_OCC: + bval = (strcmpi(operand, "true")==0) ? true : false; + A3D.do_occlusions = bval; + a3dlog.printf("occlutions = %s\n", operand); + break; + case A3DCMD_REF: + bval = (strcmpi(operand, "true")==0) ? true : false; + A3D.do_reflections = bval; + a3dlog.printf("reflections = %s\n", operand); + break; + case A3DCMD_REFINT: + ival = atoi(operand); + if (ival < 1) ival = 1; + else if (ival > 6) ival = 6; + A3D.reflection_update_interval = ival; + a3dlog.printf("update interval = %d\n", ival); + break; + case A3DCMD_REFTIME: + fval = atof(operand); + if (fval < 0.0f) fval = 0.0f; + A3D.reflect_time = fval; + a3dlog.printf("max reflect time = %.2f", fval); + break; + case A3DCMD_REFSCALE: + fval = atof(operand); + if (fval < 0.0f) fval = 0.0f; + A3D.reflect_scale = fval; + a3dlog.printf("max reflect scale = %.2f", fval); + break; + case A3DCMD_REFGAIN: + fval = atof(operand); + if (fval < 0.0f) fval = 0.0f; + A3D.reflect_gain = fval; + a3dlog.printf("max reflect gain = %.2f", fval); + break; + } + } + } + cfgfile.Close(); + } + else { + a3dlog.puts("no a3d.inf, using defaults.\n"); + } + + A3DCAPS_HARDWARE hwcaps; + memset(&hwcaps, 0 , sizeof(hwcaps)); + hwcaps.dwSize = sizeof(hwcaps); + hr = A3D.a3d->GetHardwareCaps(&hwcaps); + if (hr != S_OK) { + // failed??? + a3dlog.printf("failed hardware.\n"); + A3D.a3d->Release(); + A3D.a3d = NULL; + A3dUninitialize(); + return false; + } + + if (hwcaps.dwFlags & A3D_DIRECT_PATH_A3D) { + bool occlusions = A3D.a3d->IsFeatureAvailable(A3D_OCCLUSIONS) == TRUE; + bool reflections = A3D.a3d->IsFeatureAvailable(A3D_1ST_REFLECTIONS) == TRUE; + + if ((!occlusions && A3D.do_occlusions)) { + a3dlog.printf("failed hardware occlusion test.\n"); + A3D.a3d->Release(); + A3D.a3d = NULL; + A3dUninitialize(); + return false; + } + if (!reflections && A3D.do_reflections) { + a3dlog.printf("failed hardware reflections test.\n"); + A3D.do_reflections = false; + } + } + else { + a3dlog.printf("aureal vortex support failed..\n"); + A3D.a3d->Release(); + A3D.a3d = NULL; + A3dUninitialize(); + return false; + } + + hr = A3D.a3d->SetCooperativeLevel(hwnd, A3D_CL_NORMAL); + if (FAILED(hr)) { + a3dlog.printf("failed to obtain access to device (%x).\n", hr); + A3D.a3d->Release(); + A3D.a3d = NULL; + A3dUninitialize(); + return false; + } + +// set number of fallback a2d sources, say to 20ish? + hr = A3D.a3d->SetNumFallbackSources(A3D_FALLBACK_SOURCES); + if (FAILED(hr)) { + a3dlog.printf("failed to set fallback source count (%x).\n", hr); + } + +// we'll tend to lean towards priority since our code will more likely determine audibility. + hr = A3D.a3d->SetRMPriorityBias(A3D_PRIORITY_BIAS); + if (FAILED(hr)) { + a3dlog.printf("failed to set priority bias (%x).\n", hr); + A3D.a3d->Release(); + A3D.a3d = NULL; + A3dUninitialize(); + return false; + } + +// we use left handed coordinate system! + hr = A3D.a3d->SetCoordinateSystem(A3D_LEFT_HANDED_CS); + if (hr != S_OK) { + a3dlog.printf("couldn't set left handed coord system (%x).\n", hr); + A3D.a3d->Release(); + A3D.a3d = NULL; + A3dUninitialize(); + return false; + } + + hr = A3D.a3d->SetOutputGain(A3D.global_gain); + if (hr != S_OK) { + a3dlog.printf("couldn't set gain (%x).\n", hr); + } + + hr = A3D.a3d->SetMaxReflectionDelayTime(A3D.reflect_time); + if (hr != S_OK) { + a3dlog.printf("couldn't set reflect time (%x).\n", hr); + } + + + A3D.geom_mode_flags = 0; + A3D_buffers_allocated = 0; + A3D.a3d->Clear(); + + a3dlog.update(); + + return true; +} + + +// create or destroy geometry object +bool A3D_CreateGeometryInterface() +{ +// Get geometry interface object. We'll need this. + if (!A3D.do_occlusions) { + A3D.geom = NULL; + return false; + } + + HRESULT hr = A3D.a3d->QueryInterface(IID_IA3dGeom, (void **)&A3D.geom); + if (hr != S_OK) { + a3dlog.printf("failed to query geometry interface (%x).\n", hr); + A3D.geom = NULL; + return false; + } + + A3D.geom->Enable(A3D_OCCLUSIONS); + + if (A3D.do_reflections) { + A3D.geom->Enable(A3D_1ST_REFLECTIONS); + A3D.geom->SetReflectionDelayScale(A3D.reflect_scale); + A3D.geom->SetReflectionGainScale(A3D.reflect_gain); + A3D.geom->SetReflectionUpdateInterval(A3D.reflection_update_interval); + } + +// obtain a new list. + hr = A3D.geom->NewList(&A3D.list); + if (hr != S_OK) { + a3dlog.printf("failed to create a list interface (%x).\n", hr); + A3D.geom->Release(); + A3D.geom = NULL; + return false; + } + + A3D.geom_mode_flags = A3DPOLY_RENDERMODE_OCCLUSIONS; + A3D.a3d->Clear(); + + return true; +} + + +void A3D_DestroyGeometryInterface() +{ + if (A3D.list) { + A3D.list->Release(); + A3D.list = NULL; + } + if (A3D.geom) { + A3D.geom->Release(); + A3D.geom = NULL; + } +} + + +// destroy object +void A3D_Destroy() +{ + A3D_DestroyGeometryInterface(); + if (A3D.geom) A3D.geom->Release(); + if (A3D.listener) A3D.listener->Release(); + if (A3D.a3d) { + A3D.a3d->Release(); + A3D.listener = NULL; + A3D.a3d = NULL; + A3dUninitialize(); + a3dlog.puts("shutdown\n\n"); + a3dlog.update(); + } +} + + +void A3D_FinalShutdown() +{ + a3dlog.puts("final shutdown\n"); + a3dlog.end(); +} + + +// sets rolloff for all source objects +void A3D_SetRolloffFactor(float scalar) +{ + HRESULT hr; + + hr = A3D.a3d->SetDistanceModelScale(scalar); + if (hr != S_OK) { + a3dlog.printf("set distance model scalar failed (%x)\n", hr); + } +} + + +// sets doppler for all source objects (from 0.0f to ???) (1.0f is default, 0.0f is none) +void A3D_SetDopplerFactor(float scalar) +{ + HRESULT hr = A3D.a3d->SetDopplerScale(scalar); + if (hr != S_OK) { + a3dlog.printf("set doppler scalar failed (%x)\n", hr); + } +} + + +// sets units of measurement +void A3D_SetUnitsPerMeter(float units) +{ + HRESULT hr = A3D.a3d->SetUnitsPerMeter(units); + if (hr != S_OK) { + a3dlog.printf("couldn't set distance units (%x).\n", hr); + } +} + + +// create listener +bool A3D_CreateListener() +{ +/* query for the listener */ + int hr = A3D.a3d->QueryInterface(IID_IA3dListener, (void **)&A3D.listener); + if (hr != S_OK) { + a3dlog.printf("create listener failed (%x)!\n", hr); + return false; + } + a3dlog.puts("listener created.\n"); + return true; +} + + +// create source +void *A3D_CreateSSource(int size, int bps, bool stereo, bool streaming, ushort freq, bool is3d) +{ +/* make a new source. at this stage the source is empty - no wave data */ + IA3dSource *a3dsrc=NULL; + DWORD flags=0; + + if (is3d) { + flags = A3DSOURCE_TYPEDEFAULT; + } + else { + flags = A3DSOURCE_INITIAL_RENDERMODE_NATIVE; + } + if (streaming) { + flags |= (A3DSOURCE_TYPESTREAMED); +// a3dlog.printf("new streamed source 3d:%c\n", is3d ? 'y' : 'n'); + } + else { +// a3dlog.printf("new source type:%s\n", is3d ? "default[3d]" : "native[2d]"); + } + + int hr = A3D.a3d->NewSource( flags, &a3dsrc); + if (hr != S_OK) { + a3dlog.printf("failed to create for source (%x)\n", hr); + return NULL; + } + +/* set wave format */ + WAVEFORMATEX fmt; + + fmt.cbSize = sizeof(fmt); + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = (stereo)?2:1; + fmt.wBitsPerSample = bps; + fmt.nSamplesPerSec = ((DWORD)freq); + fmt.nBlockAlign = fmt.nChannels*(fmt.wBitsPerSample/8); + fmt.nAvgBytesPerSec = ((DWORD)fmt.nSamplesPerSec)*((DWORD)fmt.nBlockAlign); + + hr = a3dsrc->SetWaveFormat(&fmt); + if (hr != S_OK) { + a3dlog.printf("failed to set format for source (%x)\n", hr); + a3dsrc->Release(); + return NULL; + } + + hr = a3dsrc->AllocateWaveData(size); + if (hr != S_OK) { + a3dlog.printf("failed to allocate wavedata for source [%d.%d.%c.%c.%c.%08x] (%x).\n", + size,bps,stereo ? 'S' : 'M', streaming ? 'Y' : 'N', is3d ? 'Y' : 'N', hr); + a3dlog.update(); + a3dsrc->Release(); + return NULL; + } + +// if (!CHECK_FLAG(flags, A3DSOURCE_TYPEUNMANAGED)) { +// a3dsrc->SetPriority(0.5f); +// } + + A3D_buffers_allocated++; + + return (void *)a3dsrc; +} + + +// duplicates source +void *A3D_DuplicateSource(void *src) +{ + IA3dSource *a3dsrc = (IA3dSource *)src; + IA3dSource *new_src; + int hr = A3D.a3d->DuplicateSource(a3dsrc, &new_src); + if (hr != S_OK) { + a3dlog.printf("duplicate source failed (%x)\n", hr); + return NULL; + } + + A3D_buffers_allocated++; + + return (void *)new_src; +} + + +// frees source +void A3D_FreeSSource(void *obj) +{ + if (obj) { + IA3dSource *a3dsrc = (IA3dSource *)obj; + a3dsrc->FreeWaveData(); + a3dsrc->Release(); + A3D_buffers_allocated--; + } +} + + +// loads data into source +bool A3D_LoadSample(void *obj, void *src_data, int src_length) +{ + int hr=0; + + if (obj) { + void *srcptr1=0, *srcptr2=0; + DWORD len1, len2; +// IA3dSource *a3dsrc = (IA3dSource *)obj; +// hr = a3dsrc->LoadWaveData(src_data, (DWORD)src_length); +// if (SUCCEEDED(hr)) { +// return true; +// } + if (A3D_Lock(obj, 0, src_length, &srcptr1, &len1, &srcptr2, &len2)) { + memcpy(srcptr1, src_data, len1); + if(srcptr2) { + memcpy(srcptr2, ((char *)src_data) + len1, len2); + } + A3D_Unlock(obj, srcptr1, len1, srcptr2, len2); + return true; + } + } + + a3dlog.printf("lock on load source failed (%x)\n", hr); + + return false; +} + + +// plays a sound +void A3D_Play(void *obj, bool looping) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->Play(looping ? A3D_LOOPED : A3D_SINGLE); + if (hr == A3DERROR_NO_WAVE_DATA) { + a3dlog.printf("play failed because no wave data associated with source!\n"); + } +} + + +// returns current position in buffer of playback +void A3D_GetCurrentPosition(void *obj,uint *playp) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + DWORD pos=0; + + if (!a3dsrc) return; + + hr = a3dsrc->GetWavePosition(&pos); + if (hr != S_OK) { + *playp = (uint)(-1); + a3dlog.printf("get current position failed (%x)\n", hr); + } + else { + *playp = (uint)pos; + } +} + +// sets current position in buffer +void A3D_SetCurrentPosition(void *obj, uint pos) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetWavePosition(pos); + if (hr != S_OK) { + a3dlog.printf("set wave position failed (%x)\n", hr); + } +} + + +// stops a sound +void A3D_Stop(void *obj) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + DWORD pos=0; + + if (!a3dsrc) return; + + hr = a3dsrc->Stop(); +} + + +// priority is from 0 to 1.0 +void A3D_SetSourcePriority(void *obj, float priority) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetPriority((A3DVAL)priority); +} + + +// sets properties for source object +void A3D_SetSourceVolume(void *obj, float volume) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetGain((A3DVAL)volume); +} + + +float A3D_GetSourceVolume(void *obj) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + float gain; + if (!a3dsrc) return 0.0f; + + hr = a3dsrc->GetGain(&gain); + if (hr != S_OK) { + a3dlog.printf("get gain failed (%x)\n",hr); + return 0.0f; + } + + return gain; +} + + +void A3D_SetSourceVelocity(void *obj, float x, float y, float z) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetVelocity3f(x,y,z); + if (hr != S_OK) { + a3dlog.printf("set source velocity failed (%x)\n", hr); + } +} + + +void A3D_GetSourceVelocity(void *obj, vector *vel) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->GetVelocity3f(&vel->x,&vel->y,&vel->z); + if (hr != S_OK) { + a3dlog.printf("get source velocity failed (%x)\n", hr); + vel->x = vel->y = vel->z = 0.0f; + } +} + + +void A3D_SetSourceCone(void *obj, float x, float y, float z) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetCone(x,y,z); +} + + +void A3D_SetSourcePosition(void *obj, float x, float y, float z) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetPosition3f(x,y,z); + if (hr != S_OK) { + a3dlog.printf("set source position failed (%x)\n", hr); + } +} + + +bool A3D_SetSourceWaveEvent(void *obj, DWORD offset, HANDLE hEvent) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return false; + + hr = a3dsrc->SetWaveEvent(offset, hEvent);; + if (hr != S_OK) { + a3dlog.printf("set source waveevent failed (%x)\n", hr); + return false; + } + + return true; +} + + +bool A3D_ClearSourceWaveEvents(void *obj) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return false; + + hr = a3dsrc->ClearWaveEvents(); + if (hr != S_OK) { + a3dlog.printf("clear source waveevent failed (%x)\n", hr); + return false; + } + + return true; +} + + +void A3D_GetSourcePosition(void *obj, vector *pos) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->GetPosition3f(&pos->x,&pos->y,&pos->z); + if (hr != S_OK) { + a3dlog.printf("get source position failed (%x)\n", hr); + } +} + + +void A3D_SetSourceOrientation(void *obj, const vector *fvec, const vector *uvec) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetOrientation6f(fvec->x, fvec->y, fvec->z, uvec->x, uvec->y, uvec->z); + if (hr != S_OK) { + a3dlog.printf("set source orientation failed (%x)\n", hr); + } +} + + +void A3D_GetSourceOrientation(void *obj, matrix *orient) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + vector fvec, uvec; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->GetOrientation6f(&fvec.x, &fvec.y, &fvec.z, &uvec.x, &uvec.y, &uvec.z); + if (hr == S_OK) { + vm_VectorToMatrix(orient, &fvec, &uvec, NULL); + } + else { + vm_MakeIdentity(orient); + a3dlog.printf("get source orientation failed (%x)\n", hr); + } +} + + +void A3D_SetMinMaxDistance(void *obj, float min, float max, bool mute_max) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return; + + hr = a3dsrc->SetMinMaxDistance(min, max, mute_max ? A3D_MUTE : A3D_AUDIBLE); + if (hr != S_OK) { + a3dlog.printf("set minmaxdist failed (%x)\n", hr); + } +} + + +void A3D_SetSourcePan(void *obj, float lpan, float rpan) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + A3DVAL pan[2] = { lpan, rpan }; + + if (!a3dsrc) return; + + hr = a3dsrc->SetPanValues(2, pan); + if (hr != S_OK) { + a3dlog.printf("set pan failed (%x)\n", hr); + } +} + + +void A3D_GetSourcePan(void *obj, float *lpan, float *rpan) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + A3DVAL pan[2]; + + if (!a3dsrc) return; + + hr = a3dsrc->GetPanValues(2, pan); + if (hr != S_OK) { + a3dlog.printf("set pan failed (%x)\n", hr); + *lpan = 0.0f; + *rpan = 0.0f; + } + else { + *lpan = pan[0]; + *rpan = pan[1]; + } +} + + +// sets properties for listener +void A3D_SetListenerVelocity(float x, float y, float z) +{ + HRESULT hr; + + if (!A3D.listener) return; + + hr = A3D.listener->SetVelocity3f(x,y,z); + if (hr != S_OK) { + a3dlog.printf("set listener velocity failed (%x)\n", hr); + } +} + + +void A3D_SetListenerOrient(vector *fvec, vector *uvec) +{ + HRESULT hr; + + if (!A3D.listener) return; + + hr = A3D.listener->SetOrientation6f(fvec->x,fvec->y,fvec->z, uvec->x, uvec->y, uvec->z); + if (hr != S_OK) { + a3dlog.printf("set listener orient failed (%x)\n", hr); + } +} + + +void A3D_SetListenerPosition(float x, float y, float z) +{ + HRESULT hr; + + if (!A3D.listener) return; + + hr = A3D.listener->SetPosition3f(x,y,z); + if (hr != S_OK) { + a3dlog.printf("set listener position failed (%x)\n", hr); + } +} + + +// call at start of sound frame +void A3D_StartFrame() +{ + if (!A3D.a3d) return; + +// mprintf_at((1,5,20,"A3D:%2d", A3D_buffers_allocated)); + +// maybe we shouldn't clear here. flush should do the trick. +// A3D.a3d->Clear(); +} + + +void A3D_EndFrame() +{ + if (!A3D.a3d) return; + + A3D.geom_mode_flags = A3D_OCCLUSIONS; + A3D.a3d->Flush(); + a3dlog.update(); +} + + +void A3D_Flush() +{ + if (!A3D.a3d) return; + A3D.a3d->Flush(); +} + + +// returns status of source buffer +int A3D_GetSourceStatus(void *obj) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + DWORD status=0; + int retval = 0; + + if (!a3dsrc) return 0; + + hr = a3dsrc->GetStatus(&status); + if (SUCCEEDED(hr)) { + if (status & A3DSTATUS_PLAYING) { + retval |= A3D_STATUS_PLAYING; + } + if (status & A3DSTATUS_LOOPING) { + retval |= A3D_STATUS_PLAYING; + } + if (status & A3DSTATUS_WAITING_FOR_FLUSH) { + retval |= A3D_STATUS_PLAYING; + } +// if (status & A3DSTATUS_BUFFERLOST) { +// mprintf((0, "A3D:buffer lost!\n")); +// } +// if (status & A3DSTATUS_LOOPING) { +// mprintf((0, "A3D:looping!\n")); +// } +// if (status & A3DSTATUS_WAITING_FOR_FLUSH) { +// mprintf((0, "A3D:flush wait!\n")); +// } + } + + return retval; +} + + +bool A3D_Lock(void *obj, DWORD dwWriteCursor, DWORD dwWriteBytes, void **ptr1, LPDWORD plen1, void **ptr2, LPDWORD plen2) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return false; + + + hr = a3dsrc->Lock(dwWriteCursor, dwWriteBytes,ptr1,plen1,ptr2,plen2,0); + + if (hr != S_OK) { + return false; + } + + return true; +} + + +bool A3D_Unlock(void *obj, void *ptr1, DWORD len1, void *ptr2, DWORD len2) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + HRESULT hr; + + if (!a3dsrc) return false; + + hr = a3dsrc->Unlock(ptr1,len1,ptr2,len2); + if (hr != S_OK) { + return false; + } + + return true; +} + + +// sets up current global environment reverb +bool A3D_SetEnvironmentalReverb(float volume, float damping, float decay) +{ +// geometry interface is on? then lets emulate this function by setting reflection scalar. + if (!A3D.geom) { + return false; + } + +// here, decay = seconds. since a hangar environment takes 10.0f seconds to decay, this is the most obvious +// a padded cell is 0.1 seconds, to the delay scale is minimal. + float time = decay*4.0f/3.0f; + A3D.a3d->SetMaxReflectionDelayTime(time); +// A3D.geom->SetReflectionDelayScale(0.5f+decay*2.0f); + +// here the volumes passed into our function seem a bit small. we don't really want to diminish gain volumes, +// so we'll scale up of gain a bit. at most it'll be around 1.5 to 2.0. +// A3D.geom->SetReflectionGainScale(volume*3.0f/2.0f); + + return true; +} + + +// list = list id. 0 is only valid. +void A3D_GeomListStart(int list) +{ + if (!A3D.list) return; + + A3D.a3d->Clear(); + +// free old list, start new list. + if (A3D.list) { + A3D.list->Release(); + A3D.list = NULL; + + HRESULT hr = A3D.geom->NewList(&A3D.list); + if (hr != S_OK) { + a3dlog.printf("failed to create a list interface (%x).\n", hr); + return; + } + } + + A3D.list->Begin(); + +// this will be used by geometry engine if on. + if (A3D.geom) { + A3D.geom->LoadIdentity(); + A3D.geom->SetRenderMode(A3D.geom_mode_flags); + } +} + + +void A3D_GeomListEnd(int list) +{ + if (!A3D.list) return; + + A3D.list->End(); +} + + +void A3D_GeomListExec(int list) +{ + if (!A3D.list) return; + + A3D.list->Call(); +} + + +void A3D_GeomBindMaterial (void *obj) +{ + if (A3D.geom) { + if (!obj) { + if (A3D.geom_mode_flags & A3DPOLY_RENDERMODE_1ST_REFLECTIONS) { + A3D.geom_mode_flags &= (~A3DPOLY_RENDERMODE_1ST_REFLECTIONS); + A3D.geom->SetRenderMode(A3D.geom_mode_flags); + } + } + else { + if (A3D.do_reflections) { + IA3dMaterial *matp = (IA3dMaterial *)obj; + + if (!(A3D.geom_mode_flags & A3DPOLY_RENDERMODE_1ST_REFLECTIONS)) { + A3D.geom_mode_flags |= A3DPOLY_RENDERMODE_1ST_REFLECTIONS; + A3D.geom->SetRenderMode(A3D.geom_mode_flags); + } + A3D.geom->BindMaterial(matp); + } + } + } +} + + +// add primatives to geometry engine +void A3D_GeomAddTriangle(unsigned tag, vector **verts) +{ + HRESULT hr; + + if (!A3D.geom) return; + +// A3D.geom->PushMatrix(); + A3D.geom->Begin(A3D_TRIANGLES); + A3D.geom->Tag(tag); + hr = A3D.geom->Vertex3f(verts[0]->x, verts[0]->y, verts[0]->z); + hr = A3D.geom->Vertex3f(verts[1]->x, verts[1]->y, verts[1]->z); + hr = A3D.geom->Vertex3f(verts[2]->x, verts[2]->y, verts[2]->z); + A3D.geom->End(); +// A3D.geom->PopMatrix(); +} + + +void A3D_GeomAddQuad(unsigned tag, vector **verts) +{ + HRESULT hr; + + if (!A3D.geom) return; + +// A3D.geom->PushMatrix(); + A3D.geom->Begin(A3D_QUADS); + A3D.geom->Tag(tag); + hr = A3D.geom->Vertex3f(verts[0]->x, verts[0]->y, verts[0]->z); + hr = A3D.geom->Vertex3f(verts[1]->x, verts[1]->y, verts[1]->z); + hr = A3D.geom->Vertex3f(verts[2]->x, verts[2]->y, verts[2]->z); + hr = A3D.geom->Vertex3f(verts[3]->x, verts[3]->y, verts[3]->z); + A3D.geom->End(); +// A3D.geom->PopMatrix(); +} + + +void A3D_GeomAddListener(vector *pos, matrix *orient) +{ + A3DMATRIX a3dorient; + + if (!A3D.geom) return; + + A3D.geom->PushMatrix(); + + /* translate to position of listener (happens to be 0,0,0 here) */ + A3D.geom->Translate3f(pos->x, pos->y, pos->z); + + /* rotate by 'heading' degrees about the Y (up) axis */ + a3dorient[0] = orient->rvec.x; + a3dorient[1] = orient->rvec.y; + a3dorient[2] = orient->rvec.z; + a3dorient[3] = 0; + a3dorient[4] = orient->uvec.x; + a3dorient[5] = orient->uvec.y; + a3dorient[6] = orient->uvec.z; + a3dorient[7] = 0; + a3dorient[8] = orient->fvec.x; + a3dorient[9] = orient->fvec.y; + a3dorient[10] = orient->fvec.z; + a3dorient[11] = 0; + a3dorient[12] = 0; + a3dorient[13] = 0; + a3dorient[14] = 0; + a3dorient[15] = 1; + A3D.geom->MultMatrix(a3dorient); + + /* make these transformations apply to the listener */ + A3D.geom->BindListener(); + + A3D.geom->PopMatrix(); +} + + +void A3D_GeomAddSource(void *obj, vector *pos, matrix *orient) +{ + IA3dSource *a3dsrc = (IA3dSource *)obj; + A3DMATRIX a3dorient; + + if (!A3D.geom) return; + + A3D.geom->PushMatrix(); + + /* translate to position of listener (happens to be 0,0,0 here) */ + A3D.geom->Translate3f(pos->x, pos->y, pos->z); + + /* rotate by 'heading' degrees about the Y (up) axis */ + a3dorient[0] = orient->rvec.x; + a3dorient[1] = orient->rvec.y; + a3dorient[2] = orient->rvec.z; + a3dorient[3] = 0; + a3dorient[4] = orient->uvec.x; + a3dorient[5] = orient->uvec.y; + a3dorient[6] = orient->uvec.z; + a3dorient[7] = 0; + a3dorient[8] = orient->fvec.x; + a3dorient[9] = orient->fvec.y; + a3dorient[10] = orient->fvec.z; + a3dorient[11] = 0; + a3dorient[12] = 0; + a3dorient[13] = 0; + a3dorient[14] = 0; + a3dorient[15] = 1; + A3D.geom->MultMatrix(a3dorient); + + /* make these transformations apply to the listener */ + A3D.geom->BindSource(a3dsrc); + + A3D.geom->PopMatrix(); +} + + +// used to create aureal 2.0 materials. +void *A3D_CreateMaterial(float xmit_gain, float xmit_highfreq, float reflect_gain, float reflect_highfreq) +{ + IA3dMaterial *matp; + if (!A3D.geom) return NULL; + + A3D.geom->NewMaterial(&matp); + + matp->SetTransmittance(xmit_gain, xmit_highfreq); + matp->SetReflectance(reflect_gain, reflect_highfreq); + + return (void *)matp; +} + + +void A3D_DestroyMaterial(void *obj) +{ + IA3dMaterial *matp = (IA3dMaterial *)obj; + matp->Release(); +} + + + +void A3D_Clear() // clears geometry info +{ + if (!A3D.a3d) return; + A3D.a3d->Clear(); +} + +#endif diff --git a/dd_sndlib/ddsoundload.cpp b/dd_sndlib/ddsoundload.cpp new file mode 100644 index 000000000..1eadefc51 --- /dev/null +++ b/dd_sndlib/ddsoundload.cpp @@ -0,0 +1,544 @@ +/* + * $Logfile: /DescentIII/Main/dd_sndlib/ddsoundload.cpp $ + * $Revision: 17 $ + * $Date: 3/20/00 12:23p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/Main/dd_sndlib/ddsoundload.cpp $ + * + * 17 3/20/00 12:23p Matt + * Merge of Duane's post-1.3 changes. + * Fixed bug with sounds not being deleted + * + * 16 10/23/99 5:12p 3dsmax + * use GlobalAlloc for win32 instead of mem_malloc for now (restored + * original code) + * + * 15 10/22/99 8:10a Kevin + * Fixed bug introduced from the mac merge + * + * 14 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 13 4/17/99 1:53a Jeff + * made platform 'independant' so other low-level sound libs can include + * without having to double code + * + * 12 4/06/99 8:29p Samir + * added error check system. + * + * 11 2/16/99 12:42p Kevin + * Improvements to the paging data progress indicator + * + * 10 2/09/99 7:01p Kevin + * First work for new and improved progress screen while loading a level. + * Note that this is a hack at this point, while I get the details worked + * out, then I'll make it cleaner. + * + * 9 1/14/99 6:10p Samir + * added DirectSound buffer duplication code. + * + * 8 10/22/98 8:31p Chris + * Sounds use GlobalAlloc and GlobalFree + * + * 7 10/19/98 11:57a Chris + * Update the sound system to use the import volume + * + * 6 10/12/98 1:27p Jeff + * all mem_free's do set the value to NULL after they are complete + * + * 5 10/12/98 11:30a Kevin + * More memory stuff + * + * 4 10/08/98 1:18a Chris + * Now it is non-Intel platform friendly. + * + * 3 10/08/98 1:09a Chris + * Speed up the sound paging in. -- Fucks up if byte ordering if different + * then for the Intel platform. + * + * 2 7/24/98 5:23p Samir + * moved sound variables from soundload to ssl_lib.h (and WAV file stuff) + * + * 1 7/20/98 2:54p Samir + * moved code from sndlib. + * + * $NoKeywords: $ + */ + +//yes this is a low-level library for Windows, but no use duplicating code +//when we can fake the OS-specific functions (GlobalAlloc, etc) +#if defined(WIN32) +#include "windows.h" +#include "winbase.h" +#elif defined(__LINUX__) +#include "linux/linux_fix.h" +#endif + +#include "ssl_lib.h" +#if defined(MACOSX) +#include +#else +#include +#endif +#include "CFILE.H" +#include "mem.h" +#include "pserror.h" +#include +#include "BYTESWAP.H" + +sound_info Sounds[MAX_SOUNDS]; +sound_file_info SoundFiles[MAX_SOUND_FILES]; + +#ifdef MACINTOSH +#define CLIP_ATTENUATION 1.0f +#else +#define CLIP_ATTENUATION .5f +#endif + +#define SOUND_FILE_SAMPLE_ALIGNMENT 4 + +#define SND_LOAD_ERROR_NO_FILE 0 +#define SND_LOAD_ERROR_INVALID 1 + +extern int paged_in_count; +extern int paged_in_num; +char SoundLoadWaveFile(char *filename, float percent_volume, int sound_file_index, bool f_high_quality, bool f_load_sample_data, int *e_type) +{ + + // File pointer to sound file + CFILE *cfptr; + + char format_type[80]; // ASCII name of format type + unsigned short fmttag = 0; // Numerical format type + unsigned long ckid; // Current chunk's ID + unsigned long cksize; // Current chunk's size in bytes + unsigned long filesize; // Size of the sound file + unsigned long nextseek = 0; // Location of the next seek + + unsigned long aligned_size; // Sound files are aligned to SOUND_FILE_SAMPLE_ALIGNMENT samples + + // Sound format information + int samples_per_second; + short bits_per_sample; + short number_channels; + + // Used to read temporary long values + unsigned long temp_long; + + // Flags for if we previously read data or a format + char f_data, f_fmt = 0; + + // Loop counter + int count; + + // If there is an error assume it would be because of an invalid file + if(e_type) + *e_type = SND_LOAD_ERROR_INVALID; + + // Setup the initial sound_file state + SoundFiles[sound_file_index].sample_8bit = NULL; + SoundFiles[sound_file_index].sample_16bit = NULL; + SoundFiles[sound_file_index].sample_length = 0; + + // Open the wave file + if((cfptr = cfopen(filename,"rb")) == NULL) + { + if(e_type) + *e_type = SND_LOAD_ERROR_NO_FILE; + + mprintf((0, "SOUND LOADER: %s not found\n", filename)); + goto error_state; + } + + if(!f_load_sample_data) + { + cfclose(cfptr); + return 1; + } + //Used for progress bar when loading the level + paged_in_count += cfilelength(cfptr); + paged_in_num++; + // Make sure that it is a RIFF format + temp_long = (unsigned long) cf_ReadInt(cfptr); + if(temp_long != 0x46464952) + { + mprintf((0, "SOUND LOADER: %s is not a RIFF format file\n", filename)); + goto error_state; + } + + // The first item is the filesize minus the RIFF type specifier and filesize (add current location for total filesize) + filesize = cf_ReadInt(cfptr); + filesize += cftell(cfptr); + + // Make sure it is a wave file + temp_long = (unsigned long) cf_ReadInt(cfptr); + if(temp_long != 0x45564157) + { + mprintf((0, "SOUND LOADER: %s is not a WAVE file\n", filename)); + goto error_state; + } + nextseek = cftell(cfptr); + + // Read in the chunks + while(nextseek < filesize - 9) // Need room for chunk and its header + { + // Move the the beginning of the next chunck + cfseek(cfptr, nextseek, SEEK_SET); + + // Read in the chunk size and type + ckid = cf_ReadInt(cfptr); + cksize = cf_ReadInt(cfptr); + if(cksize <= 0) + { + mprintf((0, "SOUND LOADER: %s has an invalid block length\n", filename)); + goto error_state; + } + + // Determine where the next chunk begins + nextseek = cksize + cftell(cfptr); + + // Determine what to do with the chunk + switch(ckid) + { + case 0x20746D66: //Format Chunk + // Make sure that this format was not preceeded by another format without data inbetween them. + if(f_fmt) + { + mprintf((0, "Sound Loader: Found 2 formats in a row in file %s\n", filename)); + goto error_state; + } + + // Read in the format type + fmttag = (unsigned short) cf_ReadShort(cfptr); + + switch(fmttag) + { + // We only support WAVE_FORMAT_PCM currently + default: + case 0x0000: strcpy(format_type, "WAVE_FORMAT_UNKNOWN"); break; + case 0x0001: strcpy(format_type, "WAVE_FORMAT_PCM"); break; + case 0x0002: strcpy(format_type, "WAVE_FORMAT_ADPCM"); break; + case 0x0005: strcpy(format_type, "WAVE_FORMAT_IBM_CVSD"); break; + case 0x0006: strcpy(format_type, "WAVE_FORMAT_ALAW"); break; + case 0x0007: strcpy(format_type, "WAVE_FORMAT_MULAW"); break; + case 0x0010: strcpy(format_type, "WAVE_FORMAT_OKI_ADPCM"); break; + case 0x0011: strcpy(format_type, "WAVE_FORMAT_DVI_ADPCM"); break; + case 0x0015: strcpy(format_type, "WAVE_FORMAT_DIGISTD"); break; + case 0x0016: strcpy(format_type, "WAVE_FORMAT_DIGIFIX"); break; + case 0x0020: strcpy(format_type, "WAVE_FORMAT_YAMAHA_ADPCM"); break; + case 0x0021: strcpy(format_type, "WAVE_FORMAT_SONARC"); break; + case 0x0022: strcpy(format_type, "WAVE_FORMAT_DSPGROUP_TRUESPEECH"); break; + case 0x0023: strcpy(format_type, "WAVE_FORMAT_ECHOSC1"); break; + case 0x0024: strcpy(format_type, "WAVE_FORMAT_AUDIOFILE_AF18"); break; + case 0x0101: strcpy(format_type, "IBM_FORMAT_MULAW"); break; + case 0x0102: strcpy(format_type, "IBM_FORMAT_ALAW"); break; + case 0x0103: strcpy(format_type, "IBM_FORMAT_ADPCM"); break; + case 0x0200: strcpy(format_type, "WAVE_FORMAT_CREATIVE_ADPCM"); break; + } + + // Currently, we only support PCM wave files + if (fmttag != 0x0001) + { + mprintf((0, "SOUND LOADER: %s is a type %s wavefile, we only support WAVE_FORMAT_PCM waves.\n", filename, format_type)); + goto error_state; + } + + // Get the number of channels + number_channels = cf_ReadShort(cfptr); + if(number_channels != 1) + { + mprintf((0, "SOUND LOADER: Invalid number of channels(%d)in %s, we want mono samples only.\n", number_channels, filename)); + goto error_state; + } + + // Get the number of samples per second + samples_per_second = cf_ReadInt(cfptr); + if(samples_per_second != 22050) + { + mprintf((0, "SOUND LOADER: Invalid sample per second(%d)in %s, we want 22k samples only.\n", samples_per_second, filename)); + goto error_state; + } + + // get the Average bytes per second (ignore it) + cf_ReadInt(cfptr); + + // Get the Block Alignment (ignore it) + cf_ReadShort(cfptr); + + // Get the Bits per sample + bits_per_sample = cf_ReadShort(cfptr); + if(bits_per_sample != 8 && bits_per_sample != 16) + { + mprintf((0, "SOUND LOADER: Invalid bits per sample(%d)in %s, we want 8 or 16 bit samples only.\n", bits_per_sample, filename)); + goto error_state; + } + + // Beginning of some support for other wave file formats + // if(fmttag!=0x0001) + // { + // fread(&ra,1,2,fp); + // printf(" %u Bytes of extra information\n"); + // printf(" ***I have not seen a file like this before, hope it goes well***\n"); + // switch(fmttag) + // { + // case 0x0002: //MS ADPCM? + // fread(&ra,1,2,fp); + // printf(" %u Samples Per Block\n",ra); + // fread(&ra,1,2,fp); + // printf(" %u Coefficients\n",ra); + // for(la=0;la> 8); + } + + GlobalFree(SoundFiles[sound_file_index].sample_16bit); + SoundFiles[sound_file_index].sample_16bit = NULL; + + } + else if(SoundFiles[sound_file_index].sample_16bit == NULL && f_high_quality) + { + SoundFiles[sound_file_index].sample_16bit =(short *)GlobalAlloc(0, SoundFiles[sound_file_index].sample_length*sizeof(short)); + GlobalLock(SoundFiles[sound_file_index].sample_16bit); + + // NOTE: Interesting note on sound conversion: 16 bit sounds are signed (0 biase). 8 bit sounds are unsigned (+128 biase). + for(count = 0; count < (int)SoundFiles[sound_file_index].sample_length; count++) + { + SoundFiles[sound_file_index].sample_16bit[count] = (((short)SoundFiles[sound_file_index].sample_8bit[count]) - 128) * 256; + } + + for(count = 0; count < (int)SoundFiles[sound_file_index].sample_length; count++) + { + SoundFiles[sound_file_index].sample_16bit[count] *= CLIP_ATTENUATION*percent_volume; + } + + GlobalFree(SoundFiles[sound_file_index].sample_8bit); + SoundFiles[sound_file_index].sample_8bit = NULL; + } + +// reset use count + SoundFiles[sound_file_index].use_count = 0; + SoundFiles[sound_file_index].used = true; + + return 1; + +error_state: + + // Free any allocations, this file is a bust! + if(cfptr) + cfclose(cfptr); + + if(SoundFiles[sound_file_index].sample_8bit) + { + GlobalFree(SoundFiles[sound_file_index].sample_8bit); + SoundFiles[sound_file_index].sample_8bit = NULL; + } + + if(SoundFiles[sound_file_index].sample_16bit) + { + GlobalFree(SoundFiles[sound_file_index].sample_16bit); + SoundFiles[sound_file_index].sample_16bit = NULL; + } + + return 0; +} + + +void SoundLoadFree(int sound_file_index) +{ + int i = sound_file_index; + + if (SoundFiles[i].used != 0) + { + if(SoundFiles[i].sample_8bit) + GlobalFree(SoundFiles[i].sample_8bit); + + if(SoundFiles[i].sample_16bit) + GlobalFree(SoundFiles[i].sample_16bit); + } + + SoundFiles[i].sample_8bit = NULL; + SoundFiles[i].sample_16bit = NULL; +} + + diff --git a/dd_sndlib/ds3dlib_internal.h b/dd_sndlib/ds3dlib_internal.h new file mode 100644 index 000000000..542148b5e --- /dev/null +++ b/dd_sndlib/ds3dlib_internal.h @@ -0,0 +1,239 @@ +/* + * $Source: $ + * $Revision: 9 $ + * $Author: Samir $ + * $Date: 8/23/99 5:29p $ + * + * Win32 Sound Library Internal Header. + * + * $Log: /DescentIII/Main/dd_sndlib/ds3dlib_internal.h $ + * + * 9 8/23/99 5:29p Samir + * incremental EAX 2.0 checkin + * + * 8 5/03/99 3:12a Samir + * fixed up aureal so it works (a little slow though...) + * + * 7 4/29/99 3:01p Samir + * added code for direct sound mixers only (and function for Aureal + * really) that will use direct sound looping for simple loops. + * + * 6 4/22/99 10:33p Samir + * modifications so that DirectSound mixers use one thread for all looping + * and streaming sounds. It worked without crashing for about twenty + * minutes of playing from level 1 to level 2 of D3. We'll see. + * + * 5 4/13/99 4:09p Samir + * more priority stuff. + * + * 4 4/12/99 7:14p Samir + * prioritization code added. + * + * 3 4/10/99 5:08p Samir + * took out obsolete data from play_information structure that should save + * around 70 bytes per instance. + * + * 2 4/06/99 8:29p Samir + * added error check system. + * + * 1 4/06/99 8:16p Samir + * Initial rev. + * + */ + +#ifndef DS3DLIB_INTERNAL_H +#define DS3DLIB_INTERNAL_H + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "win\directx\dsound.h" +#include "ssl_lib.h" + +// Sound Library Internal Error Codes +#define SSL_OK 0 +#define SSL_ERROR_GENERIC -1 +#define SSL_ERROR_SAMPLE_NODATA -2 +#define SSL_ERROR_STREAMMIXER -3 + +// Sound Status +#define SSF_UNUSED 0 +#define SSF_PLAY_NORMAL 1 +#define SSF_PLAY_LOOPING 2 +#define SSF_PAUSED 4 +#define SSF_PLAY_STREAMING 8 +#define SSF_BUFFERED_LOOP 64 +#define SSF_BUFFERED_STRM 128 + + +#define SBT_PRIMARY 0 +#define SBT_2D 1 +#define SBT_3D 2 + +// looping methods +#define DSLOOP_SMART_METHOD 0 +#define DSLOOP_BUFFER_METHOD 1 +#define DSLOOP_STREAM_METHOD 2 + +#define DSBUFLOOP_INIT_STEP -1 +#define DSBUFLOOP_LOOP_STEP 0 +#define DSBUFLOOP_FINISH_STEP 1 + +// used to time threads. +#define DSPB_TICK_INTERVAL .01 // Primary buffer update rate (in seconds) +#define DSPB_TICK_MILLISECONDS (DSPB_TICK_INTERVAL * 1000) + +class win_llsSystem; + +typedef struct DSLOOPSTREAM +{ + ubyte method; // method 0 for streamed method, 1 for emulated method + ubyte playing; + ubyte please_close; + + union // dependant on method. + { + ubyte close_on_next; + sbyte loop_step; // method 1, = 0 for start loop, 1 = mid loop, 2 = end loop. + }; + + union // dependant on method. + { + DWORD bytes_left; + float loop_timer; // method 1, time spent in current loop step + }; + + char *current_position; // used for streamed method + HANDLE hEvent; // used for event notification. + DWORD half_buffer_point; + DWORD last_half; + int num_written; + ubyte time_slice; + bool kill_me; + bool f_sample_16bit; + char silence_byte; +} DSLOOPSTREAM; + +typedef struct DSSTREAMTAG +{ + // These 2 pointers are the same on good sound cards + LPDIRECTSOUNDBUFFER m_lp_primary_buffer; // pointer to the primary sound buffer + LPDIRECTSOUNDBUFFER m_lp_looping_buffer; // pointer to the looping sound buffer + bool m_f_secondary_looping; + + int BufferSize; // Size of the primary buffer + + int MaxWriteSamples; + int MaxWriteBytes; + + unsigned long thread_handle; +// unsigned thread_id; + volatile bool thread_request_kill; + volatile bool thread_alive; + + // Updating state variables + int NextWritePos; // Last bit position we wrote sound data to + int LastPlayPos; // Last bit position that the play pointer was recorded at + +} DSSTREAM; + + +typedef struct tPSBInfo +{ + float volume; + union { + float pan; + pos_state *cur_pos; + }; + float reverb; + ushort freq; + ushort priority; + bool looping; +} +tPSBInfo; + +// Sound item info (cache list) +class sound_buffer_info +{ +public: + sound_buffer_info() {m_status = SSF_UNUSED;m_sound_buffer = NULL;m_sound_buffer_3d = NULL;s=NULL; } + + play_information *play_info; + + int m_sound_index; // Index of sound + int m_unique_id; // Unique id for the currently playing sample + + // Not needed by the software mixer + volatile DSLOOPSTREAM* s; // Streaming info for a looping sample + union { + LPDIRECTSOUNDBUFFER m_sound_buffer; // May be a pointer to a 2d or 3d sound buffer + void * m_snd_obj; // used for non direct sound systems. + }; + LPDIRECTSOUND3DBUFFER m_sound_buffer_3d; // Used for 3d interface + LPKSPROPERTYSET m_lpksps; // used mainly for EAX 2.0 but is a direct sound object. + + short m_mixer_type; // aureal, ds3d, ds_8? + short m_buffer_type; // Buffer type 2d or 3d + + char *sample_data; + int sample_length; // used for storage purposes. + + bool stereo; + sbyte bps; + unsigned char m_status; // Sound status + unsigned char pad; + + float m_volume; // kept for priority. +}; + + +// stop a sound buffer. +void sb_stop_buffer(sound_buffer_info *sb); + +// frees a sound buffer. +void sb_free_buffer(sound_buffer_info *sb); + +// load data into sound buffer +bool sb_load_buffer(sound_buffer_info *sb, void *sample_data, int length); + +// update current position of sound buffer +int sb_get_current_position(sound_buffer_info *sb, uint *writep); + +// locks a sound buffer +bool sb_lock_buffer(sound_buffer_info *sb, uint dwWriteCursor, uint dwWriteBytes, + void **lplpvAudioPtr1, uint *lpdwAudioBytes1, + void **lplpvAudioPtr2, uint *lpdwAudioBytes2); + +// unlocks an allocated sound buffer +bool sb_unlock_buffer(sound_buffer_info *sb, void *ptr1, uint len1, void *ptr2, uint len2); + +// this will initialize the looping thread +bool sb_loop_thread_init(win_llsSystem *lls); + +// kill looped thread +void sb_loop_thread_kill(); + +// initializes a loop for the loop thread. +bool sb_loop_element_init(sound_buffer_info *sb, char *sample_ptr, int sound_length, int buffer_size); + +// initialize streaming audio. +bool sb_stream_element_init(sound_buffer_info *sb, char *sample_ptr, int sound_length, int buffer_size); + +// these work on both loops and streams. +void sb_loop_element_kill(sound_buffer_info *sb); +void sb_loop_element_wait_until_dead(sound_buffer_info *sb); + +// these work on buffered streams only +void sb_stream_element_kill(sound_buffer_info *sb); +void sb_stream_buffered_update(sound_buffer_info *sb); + +// used for buffered loops. +char *sb_get_loop_info(const sound_buffer_info *sb, int *loop_byte_start, int *loop_byte_end, bool *is_16_bit); +char *sb_get_loop_step_info(const sound_buffer_info *sb, int step, bool is16bit, int *length); +void sb_buffered_loop_step(win_llsSystem *lls, sound_buffer_info *sb, int force_next_step=-2); + +#include "ds3dlib.h" +#include "ddsndgeometry.h" + +#endif \ No newline at end of file diff --git a/dd_sndlib/dsound3d.cpp b/dd_sndlib/dsound3d.cpp new file mode 100644 index 000000000..0a74f9207 --- /dev/null +++ b/dd_sndlib/dsound3d.cpp @@ -0,0 +1,682 @@ +/* + * $Source: $ + * $Revision: 6 $ + * $Author: Samir $ + * $Date: 5/03/99 3:12a $ + * + * Direct Sound subsystem. + * + * $Log: /DescentIII/Main/dd_sndlib/dsound3d.cpp $ + * + * 6 5/03/99 3:12a Samir + * fixed up aureal so it works (a little slow though...) + * + * 5 4/29/99 3:01p Samir + * added code for direct sound mixers only (and function for Aureal + * really) that will use direct sound looping for simple loops. + * + * 4 4/25/99 9:53p Samir + * added debugging. + * + * 3 4/23/99 7:51p Samir + * looping fixes for directsound. + * + * 2 4/22/99 10:33p Samir + * modifications so that DirectSound mixers use one thread for all looping + * and streaming sounds. It worked without crashing for about twenty + * minutes of playing from level 1 to level 2 of D3. We'll see. + * + * 1 4/22/99 10:30p Samir + * initial revision ( a bit messy) + * + */ + +#include "ds3dlib_internal.h" +#include "auddev.h" + +#include "pserror.h" + +#include + + +static struct t_sb_loop_thread_data +{ + win_llsSystem *m_ll_sndsys; + int thread_handle; + short no_callbacks; + bool request_kill; + bool thread_alive; +} +m_ds; + +static ubyte m_sb_cur_timeslice; + +//////////////////////////////////////////////////////////////////////////////// +// DSLOOP_STREAM_METHOD + + +inline void sb_loop_thread_clean_buffer(sound_buffer_info *sb) +{ + m_ds.m_ll_sndsys->GetSoundPos(sb->m_unique_id); + sb_stop_buffer(sb); + sb->s->playing = 0; + sb->s->kill_me = true; + + if (SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count > 0) { + SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count--; + //DUPSND if (SoundFiles[Sounds[sb->m_sound_index].sample_index].use_count == 0) { + // Global_DS_alloced_sounds--; + //DUPSND } + } +} + + +// helper functions for loop streaming. +void sb_loop_stream_copy(sound_buffer_info *sb, char *ptr, DWORD len) +{ + DWORD amt; + bool f_looping = (sb->m_status & SSF_PLAY_LOOPING) != 0; + char *sample_ptr; + + if(f_looping) + { + const int sound_index = sb->m_sound_index; + int loop_start = Sounds[sound_index].loop_start; + int loop_end = Sounds[sound_index].loop_end; + if(sb->s->f_sample_16bit) + { + loop_start = loop_start << 1; + loop_end = loop_end << 1; + sample_ptr = (char *) SoundFiles[Sounds[sound_index].sample_index].sample_16bit; + } + else + { + sample_ptr = (char *) SoundFiles[Sounds[sound_index].sample_index].sample_8bit; + } + + while(sb->s->num_written + (int)len >= loop_end) + { + int num_till_loop_end = loop_end - sb->s->num_written; + int num_to_loop_start = sb->s->num_written - loop_start; + + if(num_till_loop_end > 0) + { + memcpy(ptr, sb->s->current_position, num_till_loop_end); + len -= num_till_loop_end; + } + ptr += num_till_loop_end; + + sb->s->current_position = sample_ptr + loop_start; +// ASSERT(sb->s->current_position >= sample_ptr && sample_ptr == sb->sample_data); + + sb->s->num_written -= num_to_loop_start; + sb->s->bytes_left += num_to_loop_start; + +// ASSERT(sb->s->num_written == loop_start); + } + } + + amt = (len > sb->s->bytes_left)?sb->s->bytes_left:len; + +// int i_amt = (int)amt; + +// ASSERT(i_amt >= 0); + + if(amt) + { + memcpy(ptr, sb->s->current_position, amt); + sb->s->current_position += amt; + sb->s->bytes_left -= amt; + sb->s->num_written += amt; +// ASSERT(sb->s->current_position >= sb->sample_data); + } + + len -= amt; + + if (len) + { + memset(ptr+amt, sb->s->silence_byte, len); + sb->s->close_on_next = 1; + } +} + + +// helper functions for loop streaming. +void sb_loop_stream_fillhalf(sound_buffer_info *sb, DWORD half) +{ + char* ptr1 = NULL; + char* ptr2 = NULL; + uint len1, len2; + + if (sb_lock_buffer(sb, half, sb->s->half_buffer_point, (void **)&ptr1, &len1, (void **)&ptr2, &len2)) { + //memset(ptr1, sb->s->silence_byte, len1); + sb_loop_stream_copy(sb, ptr1, len1); + if(ptr2){ + sb_loop_stream_copy(sb, ptr2, len2); + } + sb_unlock_buffer(sb, ptr1, len1, ptr2, len2); + } +} + + +// helper functions for streaming +void sb_stream_copy(sound_buffer_info *sb, char *ptr, DWORD len) +{ + DWORD amt; + + new_data: + + amt = (len > sb->s->bytes_left)?sb->s->bytes_left:len; + + ASSERT(amt >= 0); + + if(amt) + { + ASSERT(sb->s->current_position); + + memcpy(ptr, sb->s->current_position, amt); + sb->s->current_position += amt; + sb->s->bytes_left -= amt; + } + + len -= amt; + + if (len) + { + if(sb->play_info->m_stream_cback && sb->s->current_position) + { + memset(ptr+amt, sb->s->silence_byte, len); + int new_len = sb->play_info->m_stream_size; + sb->s->current_position = (char *) (*sb->play_info->m_stream_cback)(sb->play_info->user_data, sb->play_info->m_stream_handle, &new_len); + sb->play_info->m_stream_data = sb->s->current_position; + ASSERT(!(sb->play_info->m_stream_data && sb->play_info->m_stream_size <= 0)); + + sb->s->bytes_left = sb->play_info->m_stream_size; + + if(sb->s->current_position == NULL) + { + sb->s->bytes_left = sb->play_info->m_stream_size = 0; + } + + goto new_data; + } + + memset(ptr+amt, sb->s->silence_byte, len); + sb->s->close_on_next = 1; + } +} + + +void sb_stream_fillhalf(sound_buffer_info *sb, DWORD half) +{ + char* ptr1 = NULL; + char* ptr2 = NULL; + uint len1, len2; + + if (sb_lock_buffer(sb, half, sb->s->half_buffer_point, (void **)&ptr1, &len1, (void **)&ptr2, &len2)) { + sb_stream_copy(sb, ptr1, len1); + if(ptr2) { + sb_stream_copy(sb, ptr2, len2); + } + sb_unlock_buffer(sb, ptr1, len1, ptr2, len2); + } +} + + +// main looping thread. +void __cdecl sb_loop_thread(void *user_ptr) +{ + sound_buffer_cache *sndcache; + DWORD playp, writep, whichhalf; + int i; + ubyte iteration; + +// validate thread + m_ds.m_ll_sndsys = (win_llsSystem *)user_ptr; + m_ds.thread_alive = true; + sndcache = &m_ds.m_ll_sndsys->m_sound_mixer; + iteration = 0; + + mprintf((0, "DS3DLIB: Looping thread begins.\n")); + +// main thread body + while(!m_ds.request_kill) + { + if(m_ds.no_callbacks++ == 0) { + for (i = 0; i < sndcache->m_max_sounds_played; i++) + { + sound_buffer_info *sb = &sndcache->m_sound_cache[i]; + + // skip unused slots and only handle looping (maybe streaming) slots. + if ((sb->m_status == SSF_UNUSED) || (sb->m_status & SSF_BUFFERED_LOOP) || !sb->s) { + continue; + } + if (!sb->s->playing) { // loops before calling play do have SSF_PLAY_LOOPING set, and MUST!! + continue; + } + + // at this point it's definitely either a streaming buffer or loop streaming buffer + if (sb->m_status & SSF_PLAY_STREAMING) { + // streams will stop at the request of the application always, unlike looping buffers (see below) + if (sb->s->please_close) { + sb_loop_thread_clean_buffer(sb); + // mprintf((0, "ds thread pleas_close request processed.\n")); + } + else if (sb->m_status & SSF_PAUSED) { + continue; // just continue + } + else if ((iteration % 4) == (sb->s->time_slice % 4)) { + // update streamed buffer only when allowed + playp = sb_get_current_position(sb, (uint *)&writep); + whichhalf = (playp < sb->s->half_buffer_point)?0:sb->s->half_buffer_point; + + if (whichhalf != sb->s->last_half) { + if(sb->s->close_on_next) { + sb_loop_thread_clean_buffer(sb); + } + else { + sb_stream_fillhalf(sb, sb->s->last_half); + sb->s->last_half = whichhalf; + } + } + // mprintf((0, "ds thread stream update.\n")); + } + } + else if (!(sb->m_status & SSF_BUFFERED_LOOP)) { + // this slot is a looping slot. check to see if app requested closing this loop. + // also we don't check the looping flag because we could be playing the end part of a loop + // and the stream filling code below will set close_on_next when done itself. + if (sb->s->please_close) { + sb_loop_thread_clean_buffer(sb); + } + else if (sb->m_status & SSF_PAUSED) { + continue; // just continue + } + else if ((iteration % 4) == (sb->s->time_slice % 4)) { + // update looped buffer only when allowed + playp = sb_get_current_position(sb, (uint *)&writep); + whichhalf = (playp < sb->s->half_buffer_point)?0:sb->s->half_buffer_point; + + if (whichhalf != sb->s->last_half) { + if(sb->s->close_on_next) { + sb_loop_thread_clean_buffer(sb); + } + else { + sb_loop_stream_fillhalf(sb, sb->s->last_half); + sb->s->last_half = whichhalf; + } + } + } + } + } + iteration++; + } + m_ds.no_callbacks--; + Sleep(DSPB_TICK_MILLISECONDS); // defer to OS + } + +// invalidate thread + m_ds.thread_alive = false; + m_ds.m_ll_sndsys = NULL; + mprintf((0, "DS3DLIB: Looping thread done.\n")); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// this will initialize the looping thread + +bool sb_loop_thread_init(win_llsSystem *lls) +{ +// start looping thread + m_sb_cur_timeslice = 0; + + m_ds.request_kill = false; + m_ds.thread_alive = false; + m_ds.no_callbacks = 1; + + m_ds.thread_handle = _beginthread(sb_loop_thread, 16384, (void *)lls); + if (m_ds.thread_handle == -1) { + return false; + } + if(!SetThreadPriority((HANDLE)m_ds.thread_handle, THREAD_PRIORITY_TIME_CRITICAL)) { + return false; + } + + m_ds.no_callbacks = 0; + + return true; +} + + +void sb_loop_thread_kill() +{ + if (m_ds.thread_alive) { + mprintf((0, "DS3DLIB: Killing looping thread.\n")); + m_ds.request_kill = true; + while (m_ds.thread_alive) + { + Sleep(DSPB_TICK_MILLISECONDS); + } + } +} + + +// a lot of looping info will be initialized here, including copying data, etc. +bool sb_loop_element_init(sound_buffer_info *sb, char *sample_ptr, int sound_length, int buffer_size) +{ +// finish initting loop + if (!sb->s) { + Int3(); + return false; + } + + sb->s->time_slice = m_sb_cur_timeslice; // allocate timeslice for updating. + sb->s->half_buffer_point = buffer_size/2; + sb->s->current_position = sample_ptr; + sb->s->bytes_left = sound_length; + sb->s->silence_byte = (sb->s->f_sample_16bit) ? 0:128; + sb_loop_stream_fillhalf(sb, 0); + sb_loop_stream_fillhalf(sb, sb->s->half_buffer_point); + + sb->s->close_on_next = 0; + sb->s->kill_me = false; + m_ds.m_ll_sndsys->SetSoundPos(sb->m_unique_id, sb->play_info->m_samples_played); + + m_sb_cur_timeslice++; + + return true; +} + + +void sb_loop_element_kill(sound_buffer_info *sb) +{ + sb->s->please_close = 1; +} + + +void sb_loop_element_wait_until_dead(sound_buffer_info *sb) +{ + if (!sb->s) return; + + while (!sb->s->kill_me) + { + } +} + + + +// initialize streaming audio. +bool sb_stream_element_init(sound_buffer_info *sb, char *sample_ptr, int sound_length, int buffer_size) +{ +// finish initting loop + if (!sb->s) { + Int3(); + return false; + } + + sb->s->time_slice = m_sb_cur_timeslice; // allocate timeslice for updating. + + sb->s->half_buffer_point = buffer_size/2; + sb->s->current_position = sample_ptr; + sb->s->bytes_left = sound_length; + sb->s->silence_byte = (sb->s->f_sample_16bit) ? 0:128; + sb_stream_fillhalf(sb, 0); + sb_stream_fillhalf(sb, sb->s->half_buffer_point); + sb->s->close_on_next = 0; + sb->s->kill_me = false; + + if (sb->m_status & SSF_BUFFERED_STRM) { + sb->s->hEvent = NULL; +/* sb->s->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + if (!A3D_SetSourceWaveEvent(sb->m_snd_obj, sb->s->half_buffer_point, sb->s->hEvent)) { + CloseHandle(sb->s->hEvent); + sb->s->hEvent = NULL; + return false; + } + } + else { + Int3(); // unimplemented for normal DirectSound + CloseHandle(sb->s->hEvent); + sb->s->hEvent = NULL; + } +*/ + } + + m_sb_cur_timeslice++; + + return true; +} + + +// these work on buffered streams only +void sb_stream_element_kill(sound_buffer_info *sb) +{ + if (sb->m_status & SSF_BUFFERED_STRM) { + if (sb->s->hEvent) { + //if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + // A3D_SetSourceWaveEvent(sb->m_snd_obj, 0, NULL); + //} + CloseHandle(sb->s->hEvent); + sb->s->hEvent = NULL; + } + } +} + + +void sb_stream_buffered_update(sound_buffer_info *sb) +{ + DWORD playp, writep, whichhalf; + ASSERT((sb->m_status & SSF_BUFFERED_STRM)); + + playp = sb_get_current_position(sb, (uint *)&writep); + whichhalf = (playp < sb->s->half_buffer_point)?0:sb->s->half_buffer_point; + + if (whichhalf != sb->s->last_half) { + // mprintf((0, "DSOUND3D: event triggered. Updating stream half %d.\n", sb->s->last_half)); + if(sb->s->close_on_next) { + extern win_llsSystem *ll_sound_ptr; + ll_sound_ptr->StopSound(sb->m_unique_id); + } + else { + sb_stream_fillhalf(sb, sb->s->last_half); + sb->s->last_half = whichhalf; + } + } + +/* if (WaitForSingleObject(sb->s->hEvent, 0) == WAIT_OBJECT_0) { + // event was signaled by DirectSound/Aureal. do appropriate stream fill. + if (sb->s->close_on_next) { + extern win_llsSystem *ll_sound_ptr; + ll_sound_ptr->StopSound(sb->m_unique_id); + } + else { + mprintf((0, "DSOUND3D: event triggered. Updating stream half %d.\n", sb->s->last_half)); + A3D_ClearSourceWaveEvents(sb->m_snd_obj); + sb_stream_fillhalf(sb, sb->s->last_half); + sb->s->last_half = (sb->s->last_half) ? 0 : sb->s->half_buffer_point; + ResetEvent(sb->s->hEvent); + if (sb->m_mixer_type == SOUND_MIXER_AUREAL) { + A3D_SetSourceWaveEvent(sb->m_snd_obj, sb->s->last_half, sb->s->hEvent); + } + else { + Int3(); + } + } + } +*/ +} + + +//////////////////////////////////////////////////////////////////////////////// +// DSLOOP_BUFFER_METHOD + +char *sb_get_loop_info(const sound_buffer_info *sb, int *loop_start, int *loop_end, bool *is_16_bit) +{ + int sound_index = sb->m_sound_index; + + if (sound_index > -1) { + *loop_start = Sounds[sound_index].loop_start; + *loop_end = Sounds[sound_index].loop_end; + + if (SoundFiles[Sounds[sound_index].sample_index].sample_16bit) { + *is_16_bit = true; + return (char*)SoundFiles[Sounds[sound_index].sample_index].sample_16bit; + } + if (SoundFiles[Sounds[sound_index].sample_index].sample_8bit) { + *is_16_bit = false; + return (char*)SoundFiles[Sounds[sound_index].sample_index].sample_8bit; + } + } + Int3(); + return NULL; +} + + +char *sb_get_loop_step_info(const sound_buffer_info *sb, int step, bool is16bit, int *length) +{ + sound_file_info *sf; + char *sample_ptr; + + if (sb->m_sound_index < 0) { + Int3(); + return NULL; + } + + int loop_start_byte = Sounds[sb->m_sound_index].loop_start; + int loop_end_byte = Sounds[sb->m_sound_index].loop_end; + + if (is16bit) { + loop_start_byte = loop_start_byte << 1; + loop_end_byte = loop_end_byte << 1; + } + + sf = &SoundFiles[Sounds[sb->m_sound_index].sample_index]; + sample_ptr = is16bit ? (char*)sf->sample_16bit : (char*)sf->sample_8bit; + + if (step == DSBUFLOOP_INIT_STEP) { + *length = loop_start_byte; + return sample_ptr; + } + if (step == DSBUFLOOP_LOOP_STEP) { + *length = (loop_end_byte-loop_start_byte); + return (sample_ptr + loop_start_byte); + } + if (step == DSBUFLOOP_FINISH_STEP) { + int sample_length_byte = is16bit ? (sf->np_sample_length*2) : sf->np_sample_length; + *length = (sample_length_byte - loop_end_byte); + return (sample_ptr + loop_end_byte); + } + + Int3(); // illegal step!! + *length = 0; + + return NULL; +} + + +// steps to next state of buffered loop. +// force_next_step = -2, do it automatically, otherwise set 'next_step' using passed value. +// this will stop and free the current ds object +// advance to next valid step (may skip a step) +// if new step is > 1, then we're done. +// else create and play the new one if we can. + +// before destroying old object, we must get the current sound properties depending on buffer type. +// and set them for the new sound object. +// we will call either direct sound or custom mixer functions. +// yuck. + +void sb_buffered_loop_step(win_llsSystem *lls, sound_buffer_info *sb, int force_next_step) +{ + if (!sb->s) return; + if (!(sb->m_status & SSF_BUFFERED_LOOP)) return; + + tPSBInfo psb; + char *sample_ptr; + int sound_length; + + pos_state old_pos_state; + float old_pan; + float old_volume; + + ASSERT(lls->m_mixer_type != SOUND_MIXER_SOFTWARE_16 && lls->m_mixer_type != SOUND_MIXER_NONE); + +// get current properties. + old_pan =0; +#ifdef SUPPORT_AUREAL + if( lls->m_mixer_type == SOUND_MIXER_AUREAL) + { + matrix old_orient; + vector old_pos, old_vel; + old_volume = A3D_GetSourceVolume(sb->m_snd_obj); + + if (sb->m_buffer_type == SBT_3D) { + A3D_GetSourceVelocity(sb->m_snd_obj, &old_vel); + A3D_GetSourcePosition(sb->m_snd_obj, &old_pos); + A3D_GetSourceOrientation(sb->m_snd_obj, &old_orient); + old_pos_state.orient = &old_orient; + old_pos_state.position = &old_pos; + old_pos_state.velocity = &old_vel; + } + else { + float lpan, rpan; + A3D_GetSourcePan(sb->m_snd_obj, &lpan, &rpan); + old_pan = rpan-lpan; + } + } +#endif + +// advance to next step. + sound_length = 0; + sb->s->loop_step = (force_next_step==-2) ? (sb->s->loop_step+1) : force_next_step; + while (!sound_length && sb->s->loop_step < 2) + { + sample_ptr = sb_get_loop_step_info(sb, sb->s->loop_step, sb->s->f_sample_16bit, &sound_length); + sb->s->loop_step++; + } + if (!sound_length && sb->s->loop_step == 2) { + // mprintf((0, "DS3DLIB: Buffered loop %d advancing to post-end step (done)\n", sb->m_unique_id)); + lls->StopSound(sb->m_unique_id); + return; + } + else { + sb_stop_buffer(sb); + sb_free_buffer(sb); + } + + sb->s->loop_step--; // return to proper step. + sb->s->loop_timer = 0.0f; + sb->s->bytes_left = sound_length; + sb->m_status = SSF_PLAY_LOOPING|SSF_BUFFERED_LOOP; + sb->sample_data = sample_ptr; + +// allocate buffer for playback + if (!lls->CreateSoundBuffer( sb,false,sb->s->bytes_left,true)) { + return; + } + + if (!sb_load_buffer(sb, sb->sample_data, sound_length)) { + return; + } + +// using old sound properties, play the next buffer with those qualities! + psb.looping = (sb->s->loop_step == 0) ? true : false; + + if(sb->m_buffer_type == SBT_3D) { + psb.cur_pos = &old_pos_state; + } + else { + psb.pan = old_pan; + } + + psb.volume = old_volume; + psb.freq = 22050; + lls->PlaySoundBuffer(sb, &psb); + +// must be at end to initiate thread management. + sb->s->playing = 1; + +// mprintf((0, "DDSNDLIB: Buffered loop %d advancing to step %d.\n", sb->m_unique_id, sb->s->loop_step)); +} diff --git a/dd_sndlib/eax.cpp b/dd_sndlib/eax.cpp new file mode 100644 index 000000000..6db65857f --- /dev/null +++ b/dd_sndlib/eax.cpp @@ -0,0 +1,373 @@ +/* + * $Source: $ + * $Revision: 10 $ + * $Author: Samir $ + * $Date: 9/27/99 5:38p $ + * + * + * + * $Log: /DescentIII/Main/dd_sndlib/eax.cpp $ + * + * 10 9/27/99 5:38p Samir + * EAX 2.0->1.0 compatibility checkin. + * + * 9 8/24/99 3:42p Samir + * load EAX dynamically + * + * 8 8/24/99 1:47p Samir + * updated header file. + * + * 7 8/23/99 5:29p Samir + * incremental EAX 2.0 checkin + * + * 6 1/11/99 5:54p Samir + * made environment a hangar. + * + * 5 1/11/99 5:52p Samir + * updated EAX support. + * + * 4 1/08/99 6:31p Samir + * added reverb + * + * 3 12/23/98 11:50a Samir + * + * 2 12/23/98 11:48a Samir + * basic functionality. + * + * 1 12/21/98 7:06p Samir + * Creative Labs EAX + * + */ + + +#include "auddev.h" + +#include +#include + +#include "eax.h" +#include "eax2.h" +#include "pserror.h" + + + +// DATA +#define EAX_ENVIRONMENTS_AVAILABLE (1<<0) +#define VOICE_MANAGMENT_AVAILABLE (1<<1) + +struct +{ + HINSTANCE m_dll; + LPDIRECTSOUND m_lpds; + LPDIRECTSOUNDBUFFER m_lpdsb; + LPKSPROPERTYSET m_lpksps; + DWORD m_dwSoundProperties; + VmMode m_vmode; + EAX_REVERBPROPERTIES m_preset; +} +EAX = { NULL, NULL, 0, 0, 0 }; + + +const EAX_REVERBPROPERTIES EAX_Environments[EAX_ENVIRONMENT_COUNT] = +{ + { EAX_PRESET_GENERIC } , + { EAX_PRESET_PADDEDCELL } , + { EAX_PRESET_ROOM } , + { EAX_PRESET_BATHROOM } , + { EAX_PRESET_LIVINGROOM } , + { EAX_PRESET_STONEROOM } , + { EAX_PRESET_AUDITORIUM } , + { EAX_PRESET_CONCERTHALL } , + { EAX_PRESET_CAVE } , + { EAX_PRESET_ARENA } , + { EAX_PRESET_HANGAR } , + { EAX_PRESET_CARPETEDHALLWAY } , + { EAX_PRESET_HALLWAY } , + { EAX_PRESET_STONECORRIDOR } , + { EAX_PRESET_ALLEY } , + { EAX_PRESET_FOREST } , + { EAX_PRESET_CITY } , + { EAX_PRESET_MOUNTAINS } , + { EAX_PRESET_QUARRY } , + { EAX_PRESET_PLAIN } , + { EAX_PRESET_PARKINGLOT } , + { EAX_PRESET_SEWERPIPE } , + { EAX_PRESET_UNDERWATER } , + { EAX_PRESET_DRUGGED } , + { EAX_PRESET_DIZZY } , + { EAX_PRESET_PSYCHOTIC } +}; + + + +// FUNCTIONS + +HRESULT (FAR PASCAL *DLLEAXDirectSoundCreate)(GUID*, LPDIRECTSOUND*, IUnknown FAR *) = NULL; + +// EAX 1.0 support. +bool EAX_SetEnvironmentPreset(unsigned environment); +bool EAX_SetEnvironmentalReverb(float volume, float damping, float decay); + + +// CODE +bool EAX_Create(GUID *pGuid, LPDIRECTSOUND *lpds) +{ + HRESULT hr; + + EAX.m_dll = LoadLibrary("eax.dll"); + if (EAX.m_dll) { + DLLEAXDirectSoundCreate = (LPEAXDIRECTSOUNDCREATE)GetProcAddress(EAX.m_dll, "EAXDirectSoundCreate"); + if (!DLLEAXDirectSoundCreate) { + Error ("EAX DLL doesn't contain latest code for 2.0 functionality."); + return false; + } + + mprintf((0, "EAX 2.0 support detected.\n")); + hr = (*DLLEAXDirectSoundCreate)(pGuid, &EAX.m_lpds, NULL); + } + else { + mprintf((0, "EAX 1.0 support detected.\n")); + hr = DirectSoundCreate(pGuid, &EAX.m_lpds, NULL); + } + + if (hr != DS_OK) { + *lpds = NULL; + return false; + } + EAX.m_lpdsb = NULL; + EAX.m_dwSoundProperties = 0; + + *lpds = EAX.m_lpds; + + return true; +} + + +void EAX_Destroy() +{ + if (EAX.m_lpds) { + if ( EAX.m_lpksps != NULL ) { + EAX.m_lpksps->Release(); + EAX.m_lpksps = NULL; + } + if (EAX.m_lpdsb) { + EAX.m_lpdsb->Release(); + EAX.m_lpdsb = NULL; + } + EAX.m_lpds->Release(); + EAX.m_lpds = NULL; + } + if (EAX.m_dll) { + FreeLibrary(EAX.m_dll); + EAX.m_dll = NULL; + DLLEAXDirectSoundCreate = NULL; + } + EAX.m_dwSoundProperties = 0; +} + + +// returns EAX caps +int EAX_Caps() +{ + return EAX.m_dwSoundProperties; +} + + +// initializes EAX specific interfaces. +bool EAX_SetPrimaryBuffer() +{ + HRESULT hr; + DWORD support; + bool retval = true; + WAVEFORMATEX wave; + DSBUFFERDESC dsbdesc; + EAX_REVERBPROPERTIES props = {EAX_PRESET_HANGAR}; + + ASSERT(EAX.m_lpds); + + memset(&wave, 0, sizeof(WAVEFORMATEX)); + wave.wFormatTag = WAVE_FORMAT_PCM; + wave.nChannels = 2; + wave.nSamplesPerSec = 22050; + wave.wBitsPerSample = 16; + wave.nBlockAlign = wave.wBitsPerSample / 8 * wave.nChannels; + wave.nAvgBytesPerSec = wave.nSamplesPerSec * wave.nBlockAlign; + + memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_STATIC|DSBCAPS_CTRL3D; + dsbdesc.dwBufferBytes = DSBSIZE_MIN*2; + dsbdesc.lpwfxFormat = &wave; + + if ( FAILED(EAX.m_lpds->CreateSoundBuffer(&dsbdesc, &EAX.m_lpdsb, NULL)) ) { + return false; + } + + if (EAX.m_lpksps == NULL) { + if ( FAILED(EAX.m_lpdsb->QueryInterface(IID_IKsPropertySet, (LPVOID *)&EAX.m_lpksps)) ) { + mprintf((0, "EAX: Error failed to query property set interface.\n")); + Int3(); + retval = false; + goto error_sub; + } + } + +// now, query support depending on EAX 2.0 availability + if (EAX.m_dll) { + hr = EAX.m_lpksps->QuerySupport(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENT, &support); + if (SUCCEEDED(hr)) { + if ((support & (KSPROPERTY_SUPPORT_SET | KSPROPERTY_SUPPORT_GET)) == (KSPROPERTY_SUPPORT_SET | KSPROPERTY_SUPPORT_GET)) { + EAX.m_dwSoundProperties |= EAX_ENVIRONMENTS_AVAILABLE; + } + } + + hr = EAX.m_lpksps->QuerySupport(DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OBSTRUCTION, &support); + if (SUCCEEDED(hr)) { + if ((support & (KSPROPERTY_SUPPORT_SET | KSPROPERTY_SUPPORT_GET)) == (KSPROPERTY_SUPPORT_SET | KSPROPERTY_SUPPORT_GET)) { + EAX.m_dwSoundProperties |= EAXF_SOURCE_OBSTRUCTION; + } + } + } + else { + hr = EAX.m_lpksps->QuerySupport(DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ALL, &support); + if (SUCCEEDED(hr)) { + if ( (support & KSPROPERTY_SUPPORT_SET|KSPROPERTY_SUPPORT_GET) == (KSPROPERTY_SUPPORT_SET|KSPROPERTY_SUPPORT_GET) ) { + EAX.m_dwSoundProperties |= EAX_ENVIRONMENTS_AVAILABLE; + /* + Here the reverb environment is initialized to off. + */ + EAX_SetEnvironmentPreset(EAX_ENVIRONMENT_HANGAR); + } + } + } + + retval = (EAX.m_dwSoundProperties & EAX_ENVIRONMENTS_AVAILABLE) ? true : false; + EAX_SetEnvironmentalReverb(props.fVolume, props.fDamping,props.fDecayTime_sec); + +error_sub: + if (retval == false) { + mprintf((0, "EAX: Error failed to query environmental support.\n")); + Int3(); + if (EAX.m_lpksps) { + EAX.m_lpksps->Release(); + EAX.m_lpksps = NULL; + } + } + + return retval; +} + + +/* + + This routine can be used to change the current EAX preset environment. The environment applies + to all 3D buffers. + +*/ +// sets up current global environment reverb +bool EAX_SetEnvironmentalReverb(float volume, float damping, float decay) +{ + if ( EAX.m_dwSoundProperties & EAX_ENVIRONMENTS_AVAILABLE ) { + int i; + + EAX.m_preset.environment = EAX_ENVIRONMENT_GENERIC; + EAX.m_preset.fVolume = volume; + EAX.m_preset.fDecayTime_sec = decay; + EAX.m_preset.fDamping = damping; + + + if (EAX.m_dll) { + for (i = 0; i < EAX_ENVIRONMENT_COUNT; i++) + { + if (volume == EAX_Environments[i].fVolume && damping == EAX_Environments[i].fDamping && decay == EAX_Environments[i].fDecayTime_sec) { + EAX.m_preset.environment = (ulong)i; + break; + } + } + if (FAILED( + EAX.m_lpksps->Set(DSPROPSETID_EAX_ListenerProperties, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + NULL, + 0, + &EAX.m_preset.environment, sizeof(ulong) + ) + ) + ) { + return false; + } + } + else { + if ( FAILED(EAX.m_lpksps->Set(DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_VOLUME, NULL, 0, &EAX.m_preset.fVolume, sizeof(float))) ) { + return false; + } + if ( FAILED(EAX.m_lpksps->Set(DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DECAYTIME, NULL, 0, &EAX.m_preset.fDecayTime_sec, sizeof(float))) ) { + return false; + } + if ( FAILED(EAX.m_lpksps->Set(DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_DAMPING, NULL, 0, &EAX.m_preset.fDamping, sizeof(float))) ) { + return false; + } + } + + return true; + } + + return false; +} + + +// intializes a sound source for EAX +bool EAX_InitSource(LPDIRECTSOUND3DBUFFER lpBuffer3D, LPKSPROPERTYSET *plpksp) +{ + if (!lpBuffer3D) return true; + + if ( SUCCEEDED(lpBuffer3D->QueryInterface(IID_IKsPropertySet, (void**)plpksp)) ) + { + return true; + } + + return false; +} + + +// frees an eax sound source +void EAX_FreeSource(LPKSPROPERTYSET lpksp) +{ + if (lpksp) { + lpksp->Release(); + } +} + + +// sets source properties +void EAX_SetSourceProperties(LPKSPROPERTYSET lpksp, float obstruction) +{ + if (!lpksp) return; + + if (EAX.m_dwSoundProperties & EAXF_SOURCE_OBSTRUCTION) { + LONG lValue = (DWORD)(-10000 * obstruction); + lpksp->Set(DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OBSTRUCTION, NULL, 0, &lValue, sizeof(LONG)); + } +} + + + + +/* + + This routine can be used to change the current EAX preset environment. The environment applies + to all 3D buffers. + +*/ +// sets up current global environment reverb +bool EAX_SetEnvironmentPreset(unsigned environment) +{ + EAX.m_preset.environment = environment; + + if ( FAILED(EAX.m_lpksps->Set(DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ENVIRONMENT, NULL, 0, &EAX.m_preset.environment, sizeof(float))) ) + { + return false; + } + + return true; +} + diff --git a/dd_sndlib/eax.h b/dd_sndlib/eax.h new file mode 100644 index 000000000..8609f2062 --- /dev/null +++ b/dd_sndlib/eax.h @@ -0,0 +1,99 @@ + +// EAX.H -- DirectSound Environmental Audio Extensions + +#ifndef EAX_H_INCLUDED +#define EAX_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +// EAX (listener) reverb property set {4a4e6fc1-c341-11d1-b73a-444553540000} +DEFINE_GUID(DSPROPSETID_EAX_ReverbProperties, + 0x4a4e6fc1, + 0xc341, + 0x11d1, + 0xb7, 0x3a, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00); + +typedef enum +{ + DSPROPERTY_EAX_ALL, // all reverb properties + DSPROPERTY_EAX_ENVIRONMENT, // standard environment no. + DSPROPERTY_EAX_VOLUME, // loudness of the reverb + DSPROPERTY_EAX_DECAYTIME, // how long the reverb lasts + DSPROPERTY_EAX_DAMPING // the high frequencies decay faster +} DSPROPERTY_EAX_REVERBPROPERTY; + +#define EAX_NUM_STANDARD_PROPERTIES (DSPROPERTY_EAX_DAMPING + 1) + +// use this structure for get/set all properties... +typedef struct +{ + unsigned long environment; // 0 to EAX_ENVIRONMENT_COUNT-1 + float fVolume; // 0 to 1 + float fDecayTime_sec; // seconds, 0.1 to 100 + float fDamping; // 0 to 1 +} EAX_REVERBPROPERTIES; + + +//#define EAX_MAX_ENVIRONMENT (EAX_ENVIRONMENT_COUNT - 1) + +// presets +#define EAX_PRESET_GENERIC EAX_ENVIRONMENT_GENERIC,0.5F,1.493F,0.5F +#define EAX_PRESET_PADDEDCELL EAX_ENVIRONMENT_PADDEDCELL,0.25F,0.1F,0.0F +#define EAX_PRESET_ROOM EAX_ENVIRONMENT_ROOM,0.417F,0.4F,0.666F +#define EAX_PRESET_BATHROOM EAX_ENVIRONMENT_BATHROOM,0.653F,1.499F,0.166F +#define EAX_PRESET_LIVINGROOM EAX_ENVIRONMENT_LIVINGROOM,0.208F,0.478F,0.0F +#define EAX_PRESET_STONEROOM EAX_ENVIRONMENT_STONEROOM,0.5F,2.309F,0.888F +#define EAX_PRESET_AUDITORIUM EAX_ENVIRONMENT_AUDITORIUM,0.403F,4.279F,0.5F +#define EAX_PRESET_CONCERTHALL EAX_ENVIRONMENT_CONCERTHALL,0.5F,3.961F,0.5F +#define EAX_PRESET_CAVE EAX_ENVIRONMENT_CAVE,0.5F,2.886F,1.304F +#define EAX_PRESET_ARENA EAX_ENVIRONMENT_ARENA,0.361F,7.284F,0.332F +#define EAX_PRESET_HANGAR EAX_ENVIRONMENT_HANGAR,0.5F,10.0F,0.3F +#define EAX_PRESET_CARPETEDHALLWAY EAX_ENVIRONMENT_CARPETEDHALLWAY,0.153F,0.259F,2.0F +#define EAX_PRESET_HALLWAY EAX_ENVIRONMENT_HALLWAY,0.361F,1.493F,0.0F +#define EAX_PRESET_STONECORRIDOR EAX_ENVIRONMENT_STONECORRIDOR,0.444F,2.697F,0.638F +#define EAX_PRESET_ALLEY EAX_ENVIRONMENT_ALLEY,0.25F,1.752F,0.776F +#define EAX_PRESET_FOREST EAX_ENVIRONMENT_FOREST,0.111F,3.145F,0.472F +#define EAX_PRESET_CITY EAX_ENVIRONMENT_CITY,0.111F,2.767F,0.224F +#define EAX_PRESET_MOUNTAINS EAX_ENVIRONMENT_MOUNTAINS,0.194F,7.841F,0.472F +#define EAX_PRESET_QUARRY EAX_ENVIRONMENT_QUARRY,1.0F,1.499F,0.5F +#define EAX_PRESET_PLAIN EAX_ENVIRONMENT_PLAIN,0.097F,2.767F,0.224F +#define EAX_PRESET_PARKINGLOT EAX_ENVIRONMENT_PARKINGLOT,0.208F,1.652F,1.5F +#define EAX_PRESET_SEWERPIPE EAX_ENVIRONMENT_SEWERPIPE,0.652F,2.886F,0.25F +#define EAX_PRESET_UNDERWATER EAX_ENVIRONMENT_UNDERWATER,1.0F,1.499F,0.0F +#define EAX_PRESET_DRUGGED EAX_ENVIRONMENT_DRUGGED,0.875F,8.392F,1.388F +#define EAX_PRESET_DIZZY EAX_ENVIRONMENT_DIZZY,0.139F,17.234F,0.666F +#define EAX_PRESET_PSYCHOTIC EAX_ENVIRONMENT_PSYCHOTIC,0.486F,7.563F,0.806F + + +// EAX buffer reverb property set {4a4e6fc0-c341-11d1-b73a-444553540000} +DEFINE_GUID(DSPROPSETID_EAXBUFFER_ReverbProperties, + 0x4a4e6fc0, + 0xc341, + 0x11d1, + 0xb7, 0x3a, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00); + +typedef enum +{ + DSPROPERTY_EAXBUFFER_ALL, // all reverb buffer properties + DSPROPERTY_EAXBUFFER_REVERBMIX // the wet source amount +} DSPROPERTY_EAXBUFFER_REVERBPROPERTY; + +// use this structure for get/set all properties... +typedef struct +{ + float fMix; // linear factor, 0.0F to 1.0F +} EAXBUFFER_REVERBPROPERTIES; + +#define EAX_REVERBMIX_USEDISTANCE -1.0F // out of normal range + // signifies the reverb engine should + // calculate it's own reverb mix value + // based on distance + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/dd_sndlib/eax2.h b/dd_sndlib/eax2.h new file mode 100644 index 000000000..7f78af118 --- /dev/null +++ b/dd_sndlib/eax2.h @@ -0,0 +1,409 @@ +/****************************************************************** +* +* EAX.H - DirectSound3D Environmental Audio Extensions version 2.0 +* Updated June 18, 1999 +* +******************************************************************* +*/ + +#ifndef EAX2_H_INCLUDED +#define EAX2_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#pragma pack(push, 4) + +/* + EAX Wrapper Interface +*/ +// {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} +DEFINE_GUID(CLSID_EAXDirectSound, +0x4ff53b81, 0x1ce0, 0x11d3, 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + +__declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + +typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + + +/* +* EAX 2.0 listener property set {0306A6A8-B224-11d2-99E5-0000E8D8C722} +*/ +DEFINE_GUID(DSPROPSETID_EAX20_ListenerProperties, + 0x306a6a8, + 0xb224, + 0x11d2, + 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX20_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times are in seconds +// - the reference for high frequency controls is 5 kHz +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + LONG lRoom; // room effect level at low frequencies + LONG lRoomHF; // room effect high-frequency level re. low frequency level + FLOAT flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + FLOAT flDecayTime; // reverberation decay time at low frequencies + FLOAT flDecayHFRatio; // high-frequency to low-frequency decay time ratio + LONG lReflections; // early reflections level relative to room effect + FLOAT flReflectionsDelay; // initial reflection delay time + LONG lReverb; // late reverberation level relative to room effect + FLOAT flReverbDelay; // late reverberation delay time relative to initial reflection + DWORD dwEnvironment; // sets all listener properties + FLOAT flEnvironmentSize; // environment size in meters + FLOAT flEnvironmentDiffusion; // environment diffusion + FLOAT flAirAbsorptionHF; // change in level per meter at 5 kHz + DWORD dwFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFFC0 // reserved future use + +// property ranges and defaults: + +#define EAXLISTENER_MINROOM -10000 +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM -1000 + +#define EAXLISTENER_MINROOMHF -10000 +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF -100 + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINREFLECTIONS -10000 +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS -2602 + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB -10000 +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF -100.0f +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF -5.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 2.0 buffer property set {0306A6A7-B224-11d2-99E5-0000E8D8C722} +*/ +DEFINE_GUID(DSPROPSETID_EAX20_BufferProperties, + 0x306a6a7, + 0xb224, + 0x11d2, + 0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX20_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + LONG lDirect; // direct path level + LONG lDirectHF; // direct path level at high frequencies + LONG lRoom; // room effect level + LONG lRoomHF; // room effect level at high frequencies + FLOAT flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + LONG lObstruction; // main obstruction control (attenuation at high frequencies) + FLOAT flObstructionLFRatio; // obstruction low-frequency level re. main control + LONG lOcclusion; // main occlusion control (attenuation at high frequencies) + FLOAT flOcclusionLFRatio; // occlusion low-frequency level re. main control + FLOAT flOcclusionRoomRatio; // occlusion room effect level re. main control + LONG lOutsideVolumeHF; // outside sound cone level at high frequencies + FLOAT flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + DWORD dwFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// property ranges and defaults: + +#define EAXBUFFER_MINDIRECT -10000 +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF -10000 +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM -10000 +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF -10000 +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINOBSTRUCTION -10000 +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION -10000 +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 0.5f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF -10000 +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO) + +// Material transmission presets +// 3 values in this order: +// 1: occlusion (or obstruction) +// 2: occlusion LF Ratio (or obstruction LF Ratio) +// 3: occlusion Room Ratio + +// Single window material preset +#define EAX_MATERIAL_SINGLEWINDOW -2800 +#define EAX_MATERIAL_SINGLEWINDOWLF 0.71 +#define EAX_MATERIAL_SINGLEWINDOWROOMRATIO 0.43 + +// Double window material preset +#define EAX_MATERIAL_DOUBLEWINDOW -5000 +#define EAX_MATERIAL_DOUBLEWINDOWHF 0.40 +#define EAX_MATERIAL_DOUBLEWINDOWROOMRATIO 0.24 + +// Thin door material preset +#define EAX_MATERIAL_THINDOOR -1800 +#define EAX_MATERIAL_THINDOORLF 0.66 +#define EAX_MATERIAL_THINDOORROOMRATIO 0.66 + +// Thick door material preset +#define EAX_MATERIAL_THICKDOOR -4400 +#define EAX_MATERIAL_THICKDOORLF 0.64 +#define EAX_MATERIAL_THICKDOORROOMRTATION 0.27 + +// Wood wall material preset +#define EAX_MATERIAL_WOODWALL -4000 +#define EAX_MATERIAL_WOODWALLLF 0.50 +#define EAX_MATERIAL_WOODWALLROOMRATIO 0.30 + +// Brick wall material preset +#define EAX_MATERIAL_BRICKWALL -5000 +#define EAX_MATERIAL_BRICKWALLLF 0.60 +#define EAX_MATERIAL_BRICKWALLROOMRATIO 0.24 + +// Stone wall material preset +#define EAX_MATERIAL_STONEWALL -6000 +#define EAX_MATERIAL_STONEWALLLF 0.68 +#define EAX_MATERIAL_STONEWALLROOMRATIO 0.20 + +// Curtain material preset +#define EAX_MATERIAL_CURTAIN -1200 +#define EAX_MATERIAL_CURTAINLF 0.15 +#define EAX_MATERIAL_CURTAINROOMRATIO 1.00 + + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/dd_sndlib/geometry.cpp b/dd_sndlib/geometry.cpp new file mode 100644 index 000000000..2fa2e1452 --- /dev/null +++ b/dd_sndlib/geometry.cpp @@ -0,0 +1,398 @@ +/* + * $Source: $ + * $Revision: 8 $ + * $Author: Jeff $ + * $Date: 10/21/99 9:28p $ + * + * Hardware occlusion and reflection sound support + * + * $Log: /DescentIII/Main/dd_sndlib/geometry.cpp $ + * + * 8 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 7 8/13/99 2:00p Samir + * more aureal and geometry fixes. + * + * 6 8/11/99 3:12p Samir + * fixes for aureal support. + * + * 5 4/13/99 4:09p Samir + * more priority stuff. + * + * 4 4/06/99 8:30p Samir + * added reflection support. + * + * 3 3/30/99 5:36p Matt + * Fixed compile warnings + * + * 2 3/29/99 10:52a Samir + * occlusion support almost complete. + * + */ + +#ifndef MACINTOSH +#include "ds3dlib_internal.h" +#include "auddev.h" +#else +#include "ddsndgeometry.h" +#endif +#include "pserror.h" + + +////////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////// +llsGeometry::llsGeometry() +{ +#ifndef MACINTOSH + m_snd_system = NULL; + m_snd_mixer = SOUND_MIXER_NONE; + m_lib_init = false; +#endif +} + + +// specify a sound library to associate geometry with +bool llsGeometry::Init(llsSystem *snd_sys) +{ +#ifndef MACINTOSH + int i; + + if (m_lib_init) { + Int3(); // really, this shouldn't happen. + return true; + } + if (!snd_sys) { + Int3(); + return false; + } + +// create hardware geometry interface +#ifdef SUPPORT_AUREAL + switch (snd_sys->GetSoundMixer()) + { + case SOUND_MIXER_AUREAL: + m_lib_init = A3D_CreateGeometryInterface(); + break; + } +#endif + + if (!m_lib_init) { + mprintf((0, "DDSNDGEO: Failed to initialize geometry interface.\n")); + return false; + } + + m_snd_mixer = snd_sys->GetSoundMixer(); +#endif + + m_snd_system = snd_sys; + m_lib_init = true; + +#ifndef MACINTOSH +// create material list. + for (i = 0; i < SNDGEO_MATERIAL_COUNT; i++) + { + m_snd_materials[i] = NULL; + } + + CreateMaterial(SNDGEO_MATERIAL_ROCK, 0.2f, 0.5f, 0.9f, 0.8f); + CreateMaterial(SNDGEO_MATERIAL_WATER, 1.0f, 0.3f, 0.8f, 0.7f); + CreateMaterial(SNDGEO_MATERIAL_METAL, 0.1f, 0.1f, 0.95f, 0.85f); + + mprintf((0, "DDSNDGEO: Initialized.\n")); +#endif + return true; +} + + +// closes low level geometry system. +void llsGeometry::Shutdown() +{ +#ifndef MACINTOSH + int i; + + if (!m_lib_init) { // damn this shouldn't happen. + Int3(); + return; + } + +// destroy materials list + for (i = 0; i < SNDGEO_MATERIAL_COUNT; i++) + { + DestroyMaterial(i); + } + +// destroy geometry interface. +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: + A3D_DestroyGeometryInterface(); + break; + } +#endif + + m_snd_mixer = SOUND_MIXER_NONE; + m_snd_system = NULL; + m_lib_init = false; + + mprintf((0, "DDSNDGEO: Shutdown.\n")); +#endif +} + + +void llsGeometry::StartFrame() +{ +#ifndef MACINTOSH + n_primatives_used = 0; + n_reflections_used = 0; + n_materials_used = 0; +#endif +} + + +void llsGeometry::EndFrame() +{ +#ifndef MACINTOSH + mprintf_at((3,4,20,"sndpoly=%04d", n_primatives_used)); + mprintf_at((3,4,38,"sndmat=%04d", n_materials_used)); + mprintf_at((3,5,20,"sndref=%04d",n_reflections_used)); +#endif +} + + + +// polygon lists + +// marks beginning of a list of polygons to render +// -1 group if non cached (user doesn't want to reuse this. +void llsGeometry::StartPolygonGroup(int group) +{ +#ifndef MACINTOSH + ASSERT(m_lib_init); + +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: + // we flush, so that when starting the geometry list, we can clear the current geometry + A3D_Flush(); + A3D_GeomListStart(group); + break; + } +#endif +#endif +} + + +// ends a list of polygons to render. +void llsGeometry::EndPolygonGroup(int group) +{ +#ifndef MACINTOSH + ASSERT(m_lib_init); + +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: A3D_GeomListEnd(group); break; + } +#endif +#endif +} + + +// renders a group. +void llsGeometry::RenderGroup(int group) +{ +#ifndef MACINTOSH + ASSERT(m_lib_init); + +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: A3D_GeomListExec(group); break; + } +#endif +#endif +} + + +void llsGeometry::Clear() +{ +#ifndef MACINTOSH + ASSERT(m_lib_init); + +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: A3D_Clear(); break; + } +#endif +#endif +} + + +// primatives +// 4 verts here. +void llsGeometry::AddQuad(unsigned tag, vector **verts) +{ +#ifndef MACINTOSH + n_primatives_used++; +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: A3D_GeomAddQuad(tag,verts); break; + } +#endif +#endif +} + + +// 3 verts here. +void llsGeometry::AddTriangle(unsigned tag, vector **verts) +{ +#ifndef MACINTOSH + n_primatives_used++; +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: A3D_GeomAddTriangle(tag,verts); break; + } +#endif +#endif +} + + +void llsGeometry::AddPoly(int nv, vector **verts, unsigned tag, tSoundMaterial material) +{ +#ifndef MACINTOSH + int i, saved_primatives_used; //,p; + void *matp = NULL; + + if (nv < 3) { + Int3(); + return; + } + + if (material >= 0 && material < SNDGEO_MATERIAL_COUNT) { + matp = m_snd_materials[material]; + } + +// bind material to current polygon +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: A3D_GeomBindMaterial(matp); break; + } +#endif + + if (matp) { + n_materials_used++; + } + saved_primatives_used = n_primatives_used; + +// add polygons or split. + switch (nv) + { + case 3: AddTriangle(tag, verts); break ; + case 4: AddQuad(tag, verts); break; + default: + // split up into tris and or quads + if (m_snd_mixer == SOUND_MIXER_AUREAL) { + vector *polyvec[4]; + int nexus_vert = 0; + + for (i = 0; i < nv-3; i+=3) + { + nexus_vert = i; + polyvec[0] = verts[i]; + polyvec[1] = verts[i+1]; + polyvec[2] = verts[i+2]; + polyvec[3] = verts[i+3]; + AddQuad(tag | i, polyvec); + } + + // fan from nexus to (i through nv) + polyvec[0] = verts[nexus_vert]; + polyvec[2] = verts[i]; + for (; i < nv; i++) + { + polyvec[1] = polyvec[2]; + polyvec[2] = ((i+1)= SNDGEO_MATERIAL_COUNT) { + Int3(); // get samir, trying to intiialize a material in an existing slot. + return; + } + +// check values. + if (transmit_gain < 0) { transmit_gain = 0; } + else if (transmit_gain > 1) { transmit_gain = 1; } + if (transmit_highfreq < 0) { transmit_highfreq = 0; } + else if (transmit_highfreq > 1) { transmit_highfreq = 1; } + if (reflect_gain < 0) { reflect_gain = 0; } + else if (reflect_gain > 1) { reflect_gain = 1; } + if (reflect_highfreq < 0) { reflect_highfreq = 0; } + else if (reflect_highfreq > 1) { reflect_highfreq = 1; } + +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: + m_snd_materials[material] = A3D_CreateMaterial(transmit_gain, transmit_highfreq, reflect_gain, reflect_highfreq); + break; + } +#endif +#endif +} + + +void llsGeometry::DestroyMaterial(tSoundMaterial material) +{ +#ifndef MACINTOSH + if (!m_snd_materials[material]) { + if (material < 0 || material >= SNDGEO_MATERIAL_COUNT) { + Int3(); // get samir, trying to destroy a material in an nonexisting slot. + } + return; + } + + if (m_snd_materials[material]) + { +#ifdef SUPPORT_AUREAL + switch (m_snd_mixer) + { + case SOUND_MIXER_AUREAL: + A3D_DestroyMaterial(m_snd_materials[material]); + break; + } +#endif + m_snd_materials[material] = NULL; + } +#endif +} + diff --git a/dd_sndlib/ia3dapi.h b/dd_sndlib/ia3dapi.h new file mode 100644 index 000000000..11aeaca3f --- /dev/null +++ b/dd_sndlib/ia3dapi.h @@ -0,0 +1,1313 @@ +/*--------------------------------------------------------------------------- + * + * A3D COM Interface Header File. + * + *--------------------------------------------------------------------------- + */ + +#ifndef _IA3DAPI_H_ +#define _IA3DAPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// A3D 1.0 Class ID {D8F1EEE0-F634-11cf-8700-00A0245D918B} +DEFINE_GUID(CLSID_A3d, 0xd8f1eee0, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b); + +// A3D 2.0 Class ID {92FA2C24-253C-11d2-90FB-006008A1F441} +DEFINE_GUID(CLSID_A3dApi, 0x92fa2c24, 0x253c, 0x11d2, 0x90, 0xfb, 0x0, 0x60, 0x8, 0xa1, 0xf4, 0x41); + +//=================================================================== +// A3D 2.0 Interfaces +//=================================================================== + +// Forward declaration of COM interfaces +#ifdef __cplusplus +// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined +struct IA3d3; +struct IA3dGeom; +struct IA3dSource; +struct IA3dListener; +struct IA3dList; +struct IA3dMaterial; +struct IA3dEnvironment; +#endif // __cplusplus + +typedef struct IA3d3 *LPA3D3; +typedef struct IA3d4 *LPA3D4; +typedef struct IA3dGeom *LPA3DGEOM; +typedef struct IA3dSource *LPA3DSOURCE; +typedef struct IA3dListener *LPA3DLISTENER; +typedef struct IA3dList *LPA3DLIST; +typedef struct IA3dMaterial *LPA3DMATERIAL; +typedef struct IA3dEnvironment *LPA3DENVIRONMENT; + + +//=================================================================== +// Defined values +//=================================================================== + +// Feature flags +#define A3D_1ST_REFLECTIONS 0x00000002 +#define A3D_DIRECT_PATH_A3D 0x00000008 +#define A3D_DIRECT_PATH_GENERIC 0x00000020 +#define A3D_OCCLUSIONS 0x00000040 +#define A3D_DISABLE_SPLASHSCREEN 0x00000080 + +// Rendering modes +#define A3D_FASTEST 0x00000001 +#define A3D_QUICK 0x00000002 +#define A3D_NICEST 0x00000004 + +// Primitive input modes +#define A3D_INVALID_INPUTMODE -1 + +#define A3D_LINES 0x00000002 +#define A3D_TRIANGLES 0x00000003 +#define A3D_QUADS 0x00000004 +#define A3D_MATERIAL 0x00000005 + +#define A3D_SUBFACE 0x80000000 +#define A3D_VERTEX_MASK 0x0000000F + +#define A3D_SUB_LINES (A3D_LINES | A3D_SUBFACE) +#define A3D_SUB_TRIANGLES (A3D_TRIANGLES | A3D_SUBFACE) +#define A3D_SUB_QUADS (A3D_QUADS | A3D_SUBFACE) + +// Wall flags +#define A3D_SHELL_WALL 0x00000001 +#define A3D_TRANSPARENT_WALL 0x00000002 + +// Data types +typedef float A3DVAL, *LPA3DVAL; + +typedef A3DVAL A3DVECTOR[4]; +typedef A3DVAL A3DVERTEX[4]; + +/* + * NOTE: A3D matrices are column major. Indices are like this: + * + * | 0 4 8 12 | + * | 1 5 9 13 | + * | 2 6 10 14 | + * | 3 7 11 15 | + * + * Indexing is (column*4)+row. + * +*/ +typedef A3DVAL A3DMATRIX[16]; + +#define A3D_TRUE 1 +#define A3D_FALSE 0 + +// Epsilon good as zero gets +#define A3D_EPSILON (1.0e-6f) +#define A3D_EPSILON_SQUARED (1.0e-12f) + +#define A3D_DEFAULT 0 + +// Play options +#define A3D_SINGLE 0 +#define A3D_LOOPED 1 + +// Scene Types +#define A3D_SCENE_2D 2 +#define A3D_SCENE_3D 3 + +#define A3DSTATUS_PLAYING 0x00000001 +#define A3DSTATUS_BUFFERLOST 0x00000002 +#define A3DSTATUS_LOOPING 0x00000004 +#define A3DSTATUS_WAITING_FOR_FLUSH 0x00001000 + +// Coordinate system +#define A3D_RIGHT_HANDED_CS 0x00000000 +#define A3D_LEFT_HANDED_CS 0x00000001 + +// Cooperative Level +#define A3D_CL_NORMAL 0x00000001 +#define A3D_CL_EXCLUSIVE 0x00000003 + +// MaxMinDistance flags +#define A3D_AUDIBLE 0x00000000 +#define A3D_MUTE 0x00000001 + +#define A3DRENDERPREFS_A3D 0x00000000 +#define A3DRENDERPREFS_DEFAULT A3DRENDERPREFS_A3D + +#define A3DSOURCE_TRANSFORMMODE_NORMAL 0x00000000 +#define A3DSOURCE_TRANSFORMMODE_HEADRELATIVE 0x00000001 + +#define A3DSOURCE_RENDERMODE_A3D 0x00000000 +#define A3DSOURCE_RENDERMODE_MONO 0x00000001 +#define A3DSOURCE_RENDERMODE_1ST_REFLECTIONS 0x00000004 +#define A3DSOURCE_RENDERMODE_OCCLUSIONS 0x00000008 +#define A3DSOURCE_RENDERMODE_NATIVE 0x00000020 +#define A3DSOURCE_RENDERMODE_DEFAULT (A3DSOURCE_RENDERMODE_A3D | \ + A3DSOURCE_RENDERMODE_1ST_REFLECTIONS | \ + A3DSOURCE_RENDERMODE_OCCLUSIONS) + +// Notification +#define A3DSOURCE_WAVEEVENT_STOP 0xFFFFFFFF + +// Polygon render mode +#define A3DPOLY_RENDERMODE_1ST_REFLECTIONS 0x00000002 +#define A3DPOLY_RENDERMODE_OCCLUSIONS 0x00000040 + +#define A3DSOURCE_INITIAL_RENDERMODE_A3D 0x00000000 +#define A3DSOURCE_INITIAL_RENDERMODE_NATIVE 0x00000001 +#define A3DSOURCE_TYPEUNMANAGED 0x00000002 +#define A3DSOURCE_TYPESTREAMED 0x00000004 +#define A3DSOURCE_TYPEDEFAULT A3DSOURCE_INITIAL_RENDERMODE_A3D + +// Values for bOutputMode +#define OUTPUT_MODE_STEREO 0x00000001 +#define OUTPUT_MODE_QUAD 0x00000002 + +// Values for FrontXtalkMode and bRearXtalkMode +#define OUTPUT_HEADPHONES 0x00000001 +#define OUTPUT_SPEAKERS_WIDE 0x00000002 +#define OUTPUT_SPEAKERS_NARROW 0x00000003 + +// Values for Resource Management Mode +#define A3D_RESOURCE_MODE_OFF 0x00000000 +#define A3D_RESOURCE_MODE_NOTIFY 0x00000001 +#define A3D_RESOURCE_MODE_DYNAMIC 0x00000002 +#define A3D_RESOURCE_MODE_DYNAMIC_LOOPERS 0x00000003 +#define A3D_RESOURCE_MODE_LAST A3D_RESOURCE_MODE_DYNAMIC_LOOPERS + +// A3d Source Lock modes +#define A3D_FROMWRITECURSOR 0x00000001 +#define A3D_ENTIREBUFFER 0x00000002 + +// Version Definitions for A3DCAPS +#define A3D_CURRENT_VERSION IA3DVERSION_RELEASE20 + +#define IA3DVERSION_RELEASE10 10 +#define IA3DVERSION_RELEASE12 12 +#define IA3DVERSION_RELEASE20 20 + +// A3d Caps structure for A3d2 interface +// If Fail to get IA3d2 interface, version of DLL is IA3DVERSION_PRE12 + +typedef struct __A3DCAPS_SOFTWARE +{ + DWORD dwSize; // Use for internal version control + DWORD dwVersion; // For Backwards capablities purposes + DWORD dwFlags; + DWORD dwReserved; + DWORD dwReserved2; + DWORD dwOutputChannels; + DWORD dwMinSampleRate; + DWORD dwMaxSampleRate; + DWORD dwMax2DBuffers; + DWORD dwMax3DBuffers; +} A3DCAPS_SOFTWARE, *LPA3DCAPS_SOFTWARE; + +typedef struct __A3DCAPS_HARDWARE +{ + DWORD dwSize; // Use for internal version control + DWORD dwFlags; + DWORD dwReserved; + DWORD dwReserved2; + DWORD dwOutputChannels; + DWORD dwMinSampleRate; + DWORD dwMaxSampleRate; + DWORD dwMax2DBuffers; + DWORD dwMax3DBuffers; +} A3DCAPS_HARDWARE, *LPA3DCAPS_HARDWARE; + + +//=================================================================== +// IA3d +// +// The original IA3d interface. +//=================================================================== + +// {D8F1EEE1-F634-11cf-8700-00A0245D918B} +DEFINE_GUID(IID_IA3d, 0xd8f1eee1, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b); + +#undef INTERFACE +#define INTERFACE IA3d + +typedef struct IA3d *LPIA3D; + +DECLARE_INTERFACE_(IA3d, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3d Methods. + STDMETHOD(SetOutputMode) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(GetOutputMode) (THIS_ LPDWORD, LPDWORD, LPDWORD) PURE; + STDMETHOD(SetResourceManagerMode) (THIS_ DWORD) PURE; + STDMETHOD(GetResourceManagerMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetHFAbsorbFactor) (THIS_ FLOAT) PURE; + STDMETHOD(GetHFAbsorbFactor) (THIS_ FLOAT *) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3d_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3d_Release(p) (p)->lpVtbl->Release(p) +#define IA3d_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c) +#define IA3d_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c) +#define IA3d_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a) +#define IA3d_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a) +#define IA3d_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a) +#define IA3d_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3d_AddRef(p) (p)->AddRef() +#define IA3d_Release(p) (p)->Release() +#define IA3d_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c) +#define IA3d_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c) +#define IA3d_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a) +#define IA3d_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a) +#define IA3d_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a) +#define IA3d_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + + +//=================================================================== +// IA3d2 +// +// IA3d2 Interface definition. +//=================================================================== + +// {fb80d1e0-98d3-11d1-90fb-006008a1f441} +DEFINE_GUID(IID_IA3d2, 0xfb80d1e0, 0x98d3, 0x11d1, 0x90, 0xfb, 0x00, 0x60, 0x08, 0xa1, 0xf4, 0x41); + +#undef INTERFACE +#define INTERFACE IA3d2 + +typedef struct IA3d2 *LPIA3D2; + +DECLARE_INTERFACE_(IA3d2, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3d Methods. + STDMETHOD(SetOutputMode) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(GetOutputMode) (THIS_ LPDWORD, LPDWORD, LPDWORD) PURE; + STDMETHOD(SetResourceManagerMode) (THIS_ DWORD) PURE; + STDMETHOD(GetResourceManagerMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetHFAbsorbFactor) (THIS_ FLOAT) PURE; + STDMETHOD(GetHFAbsorbFactor) (THIS_ FLOAT *) PURE; + + // IA3d2 Methods. + STDMETHOD(RegisterVersion) (THIS_ DWORD) PURE; + STDMETHOD(GetSoftwareCaps) (THIS_ LPA3DCAPS_SOFTWARE) PURE; + STDMETHOD(GetHardwareCaps) (THIS_ LPA3DCAPS_HARDWARE) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3d2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3d2_Release(p) (p)->lpVtbl->Release(p) +#define IA3d2_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c) +#define IA3d2_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c) +#define IA3d2_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a) +#define IA3d2_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a) +#define IA3d2_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a) +#define IA3d2_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a) +#define IA3d2_RegisterVersion(p,a) (p)->lpVtbl->RegisterVersion(p,a) +#define IA3d2_GetSoftwareCaps(p,a) (p)->lpVtbl->GetSoftwareCaps(p,a) +#define IA3d2_GetHardwareCaps(p,a) (p)->lpVtbl->GetHardwareCaps(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3d2_AddRef(p) (p)->AddRef() +#define IA3d2_Release(p) (p)->Release() +#define IA3d2_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c) +#define IA3d2_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c) +#define IA3d2_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a) +#define IA3d2_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a) +#define IA3d2_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a) +#define IA3d2_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a) +#define IA3d2_RegisterVersion(p,a) (p)->RegisterVersion(a) +#define IA3d2_GetSoftwareCaps(p,a) (p)->GetSoftwareCaps(a) +#define IA3d2_GetHardwareCaps(p,a) (p)->GetHardwareCaps(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + +//=================================================================== +// IA3d3 +// +// The root object in A3D. +//=================================================================== + +// {C398E560-D90B-11d1-90FB-006008A1F441} +DEFINE_GUID(IID_IA3d3, 0xc398e560, 0xd90b, 0x11d1, 0x90, 0xfb, 0x0, 0x60, 0x8, 0xa1, 0xf4, 0x41); + +#undef INTERFACE +#define INTERFACE IA3d3 + +typedef struct IA3d3 *LPIA3D3; + +DECLARE_INTERFACE_(IA3d3, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3d Methods. + STDMETHOD(SetOutputMode) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(GetOutputMode) (THIS_ LPDWORD, LPDWORD, LPDWORD) PURE; + STDMETHOD(SetResourceManagerMode) (THIS_ DWORD) PURE; + STDMETHOD(GetResourceManagerMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetHFAbsorbFactor) (THIS_ FLOAT) PURE; + STDMETHOD(GetHFAbsorbFactor) (THIS_ FLOAT *) PURE; + + // IA3d2 Methods. + STDMETHOD(RegisterVersion) (THIS_ DWORD) PURE; + STDMETHOD(GetSoftwareCaps) (THIS_ LPA3DCAPS_SOFTWARE) PURE; + STDMETHOD(GetHardwareCaps) (THIS_ LPA3DCAPS_HARDWARE) PURE; + + // IA3d3 Methods. + STDMETHOD(Clear) (THIS) PURE; + STDMETHOD(Flush) (THIS) PURE; + STDMETHOD(Compat) (THIS_ DWORD, DWORD) PURE; + STDMETHOD(Init) (THIS_ LPGUID, DWORD, DWORD) PURE; + STDMETHOD(IsFeatureAvailable) (THIS_ DWORD) PURE; + STDMETHOD(NewSource) (THIS_ DWORD, LPA3DSOURCE *) PURE; + STDMETHOD(DuplicateSource) (THIS_ LPA3DSOURCE, LPA3DSOURCE *) PURE; + STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; + STDMETHOD(GetCooperativeLevel) (THIS_ LPDWORD) PURE; + STDMETHOD(SetMaxReflectionDelayTime) (THIS_ A3DVAL) PURE; + STDMETHOD(GetMaxReflectionDelayTime) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetCoordinateSystem) (THIS_ DWORD) PURE; + STDMETHOD(GetCoordinateSystem) (THIS_ LPDWORD) PURE; + STDMETHOD(SetOutputGain) (THIS_ A3DVAL) PURE; + STDMETHOD(GetOutputGain) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetNumFallbackSources) (THIS_ DWORD) PURE; + STDMETHOD(GetNumFallbackSources) (THIS_ LPDWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3d3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3d3_Release(p) (p)->lpVtbl->Release(p) +#define IA3d3_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c) +#define IA3d3_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c) +#define IA3d3_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a) +#define IA3d3_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a) +#define IA3d3_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a) +#define IA3d3_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a) +#define IA3d3_RegisterVersion(p,a) (p)->lpVtbl->RegisterVersion(p,a) +#define IA3d3_GetSoftwareCaps(p,a) (p)->lpVtbl->GetSoftwareCaps(p,a) +#define IA3d3_GetHardwareCaps(p,a) (p)->lpVtbl->GetHardwareCaps(p,a) +#define IA3d3_Clear(p) (p)->lpVtbl->Clear(p) +#define IA3d3_Flush(p) (p)->lpVtbl->Flush(p) +#define IA3d3_Compat(p,a,b) (p)->lpVtbl->Compat(p,a,b) +#define IA3d3_Init(p,a,b,c) (p)->lpVtbl->Init(p,a,b,c) +#define IA3d3_IsFeatureAvailable(p,a) (p)->lpVtbl->IsFeatureAvailable(p,a) +#define IA3d3_NewSource(p,a,b) (p)->lpVtbl->NewSource(p,a,b) +#define IA3d3_DuplicateSource(p,a,b) (p)->lpVtbl->DuplicateSource(p,a,b) +#define IA3d3_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IA3d3_GetCooperativeLevel(p,a) (p)->lpVtbl->GetCooperativeLevel(p,a) +#define IA3d3_SetMaxReflectionDelayTime(p,a) (p)->lpVtbl->SetMaxReflectionDelayTime(p,a) +#define IA3d3_GetMaxReflectionDelayTime(p,a) (p)->lpVtbl->GetMaxReflectionDelayTime(p,a) +#define IA3d3_SetCoordinateSystem(p,a) (p)->lpVtbl->SetCoordinateSystem(p,a) +#define IA3d3_GetCoordinateSystem(p,a) (p)->lpVtbl->GetCoordinateSystem(p,a) +#define IA3d3_SetOutputGain(p,a) (p)->lpVtbl->SetOutputGain(p,a) +#define IA3d3_GetOutputGain(p,a) (p)->lpVtbl->GetOutputGain(p,a) +#define IA3d3_SetNumFallbackSources(p,a) (p)->lpVtbl->SetNumFallbackSources(p,a) +#define IA3d3_GetNumFallbackSources(p,a) (p)->lpVtbl->GetNumFallbackSources(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3d3_AddRef(p) (p)->AddRef() +#define IA3d3_Release(p) (p)->Release() +#define IA3d3_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c) +#define IA3d3_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c) +#define IA3d3_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a) +#define IA3d3_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a) +#define IA3d3_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a) +#define IA3d3_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a) +#define IA3d3_RegisterVersion(p,a) (p)->RegisterVersion(a) +#define IA3d3_GetSoftwareCaps(p,a) (p)->GetSoftwareCaps(a) +#define IA3d3_GetHardwareCaps(p,a) (p)->GetHardwareCaps(a) +#define IA3d3_Clear(p) (p)->Clear() +#define IA3d3_Flush(p) (p)->Flush() +#define IA3d3_Compat(p,a,b) (p)->Compat(a,b) +#define IA3d3_Init(p,a,b,c) (p)->Init(a,b,c) +#define IA3d3_IsFeatureAvailable(p,a) (p)->IsFeatureAvailable(a) +#define IA3d3_NewSource(p,a,b) (p)->NewSource(a,b) +#define IA3d3_DuplicateSource(p,a,b) (p)->DuplicateSource(a,b) +#define IA3d3_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IA3d3_GetCooperativeLevel(p,a) (p)->GetCooperativeLevel(a) +#define IA3d3_SetMaxReflectionDelayTime(p,a) (p)->SetMaxReflectionDelayTime(a) +#define IA3d3_GetMaxReflectionDelayTime(p,a) (p)->GetMaxReflectionDelayTime(a) +#define IA3d3_SetCoordinateSystem(p,a) (p)->SetCoordinateSystem(a) +#define IA3d3_GetCoordinateSystem(p,a) (p)->GetCoordinateSystem(a) +#define IA3d3_SetOutputGain(p,a) (p)->SetOutputGain(a) +#define IA3d3_GetOutputGain(p,a) (p)->GetOutputGain(a) +#define IA3d3_SetNumFallbackSources(p,a) (p)->SetNumFallbackSources(a) +#define IA3d3_GetNumFallbackSources(p,a) (p)->GetNumFallbackSources(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + + +//=================================================================== +// IA3d4 +// +// The root object in A3D. +//=================================================================== + +// {E4C40280-CCBA-11d2-9DCF-00500411582F} +DEFINE_GUID(IID_IA3d4, 0xe4c40280, 0xccba, 0x11d2, 0x9d, 0xcf, 0x0, 0x50, 0x4, 0x11, 0x58, 0x2f); + +#undef INTERFACE +#define INTERFACE IA3d4 + +DECLARE_INTERFACE_(IA3d4, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3d Methods. + STDMETHOD(SetOutputMode) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(GetOutputMode) (THIS_ LPDWORD, LPDWORD, LPDWORD) PURE; + STDMETHOD(SetResourceManagerMode) (THIS_ DWORD) PURE; + STDMETHOD(GetResourceManagerMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetHFAbsorbFactor) (THIS_ FLOAT) PURE; + STDMETHOD(GetHFAbsorbFactor) (THIS_ FLOAT *) PURE; + + // IA3d2 Methods. + STDMETHOD(RegisterVersion) (THIS_ DWORD) PURE; + STDMETHOD(GetSoftwareCaps) (THIS_ LPA3DCAPS_SOFTWARE) PURE; + STDMETHOD(GetHardwareCaps) (THIS_ LPA3DCAPS_HARDWARE) PURE; + + // IA3d3 Methods. + STDMETHOD(Clear) (THIS) PURE; + STDMETHOD(Flush) (THIS) PURE; + STDMETHOD(Compat) (THIS_ DWORD, DWORD) PURE; + STDMETHOD(Init) (THIS_ LPGUID, DWORD, DWORD) PURE; + STDMETHOD(IsFeatureAvailable) (THIS_ DWORD) PURE; + STDMETHOD(NewSource) (THIS_ DWORD, LPA3DSOURCE *) PURE; + STDMETHOD(DuplicateSource) (THIS_ LPA3DSOURCE, LPA3DSOURCE *) PURE; + STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; + STDMETHOD(GetCooperativeLevel) (THIS_ LPDWORD) PURE; + STDMETHOD(SetMaxReflectionDelayTime) (THIS_ A3DVAL) PURE; + STDMETHOD(GetMaxReflectionDelayTime) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetCoordinateSystem) (THIS_ DWORD) PURE; + STDMETHOD(GetCoordinateSystem) (THIS_ LPDWORD) PURE; + STDMETHOD(SetOutputGain) (THIS_ A3DVAL) PURE; + STDMETHOD(GetOutputGain) (THIS_ LPA3DVAL) PURE; + + // IA3d4 Methods + STDMETHOD(SetNumFallbackSources) (THIS_ DWORD) PURE; + STDMETHOD(GetNumFallbackSources) (THIS_ LPDWORD) PURE; + STDMETHOD(SetRMPriorityBias) (THIS_ A3DVAL) PURE; + STDMETHOD(GetRMPriorityBias) (THIS_ LPA3DVAL) PURE; + STDMETHOD(DisableViewer) (THIS) PURE; + STDMETHOD(SetUnitsPerMeter) (THIS_ A3DVAL) PURE; + STDMETHOD(GetUnitsPerMeter) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetDopplerScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetDopplerScale) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetDistanceModelScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetDistanceModelScale) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetEq) (THIS_ A3DVAL) PURE; + STDMETHOD(GetEq) (THIS_ LPA3DVAL) PURE; + STDMETHOD(Shutdown) (THIS) PURE; + STDMETHOD(RegisterApp) (THIS_ REFIID) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d4_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3d4_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3d4_Release(p) (p)->lpVtbl->Release(p) +#define IA3d4_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c) +#define IA3d4_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c) +#define IA3d4_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a) +#define IA3d4_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a) +#define IA3d4_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a) +#define IA3d4_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a) +#define IA3d4_RegisterVersion(p,a) (p)->lpVtbl->RegisterVersion(p,a) +#define IA3d4_GetSoftwareCaps(p,a) (p)->lpVtbl->GetSoftwareCaps(p,a) +#define IA3d4_GetHardwareCaps(p,a) (p)->lpVtbl->GetHardwareCaps(p,a) +#define IA3d4_Clear(p) (p)->lpVtbl->Clear(p) +#define IA3d4_Flush(p) (p)->lpVtbl->Flush(p) +#define IA3d4_Compat(p,a,b) (p)->lpVtbl->Compat(p,a,b) +#define IA3d4_Init(p,a,b,c) (p)->lpVtbl->Init(p,a,b,c) +#define IA3d4_IsFeatureAvailable(p,a) (p)->lpVtbl->IsFeatureAvailable(p,a) +#define IA3d4_NewSource(p,a,b) (p)->lpVtbl->NewSource(p,a,b) +#define IA3d4_DuplicateSource(p,a,b) (p)->lpVtbl->DuplicateSource(p,a,b) +#define IA3d4_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IA3d4_GetCooperativeLevel(p,a) (p)->lpVtbl->GetCooperativeLevel(p,a) +#define IA3d4_SetMaxReflectionDelayTime(p,a) (p)->lpVtbl->SetMaxReflectionDelayTime(p,a) +#define IA3d4_GetMaxReflectionDelayTime(p,a) (p)->lpVtbl->GetMaxReflectionDelayTime(p,a) +#define IA3d4_SetCoordinateSystem(p,a) (p)->lpVtbl->SetCoordinateSystem(p,a) +#define IA3d4_GetCoordinateSystem(p,a) (p)->lpVtbl->GetCoordinateSystem(p,a) +#define IA3d4_SetOutputGain(p,a) (p)->lpVtbl->SetOutputGain(p,a) +#define IA3d4_GetOutputGain(p,a) (p)->lpVtbl->GetOutputGain(p,a) +#define IA3d4_SetNumFallbackSources(p,a) (p)->lpVtbl->SetNumFallbackSources(p,a) +#define IA3d4_GetNumFallbackSources(p,a) (p)->lpVtbl->GetNumFallbackSources(p,a) +#define IA3d4_SetRMPriorityBias(p,a) (p)->lpVtbl->SetRMPriorityBias(p,a) +#define IA3d4_GetRMPriorityBias(p,a) (p)->lpVtbl->GetRMPriorityBias(p,a) +#define IA3d4_DisableViewer(p) (p)->lpVtbl->DisableViewer(p) +#define IA3d4_SetUnitsPerMeter(p,a) (p)->lpVtbl->SetUnitsPerMeter(p,a) +#define IA3d4_GetUnitsPerMeter(p,a) (p)->lpVtbl->GetUnitsPerMeter(p,a) +#define IA3d4_SetDopplerScale(p,a) (p)->lpVtbl->SetDopplerScale(p,a) +#define IA3d4_GetDopplerScale(p,a) (p)->lpVtbl->GetDopplerScale(p,a) +#define IA3d4_SetDistanceModelScale(p,a) (p)->lpVtbl->SetDistanceModelScale(p,a) +#define IA3d4_GetDistanceModelScale(p,a) (p)->lpVtbl->GetDistanceModelScale(p,a) +#define IA3d4_SetEq(p,a) (p)->lpVtbl->SetEq(p,a) +#define IA3d4_GetEq(p,a) (p)->lpVtbl->GetEq(p,a) +#define IA3d4_Shutdown(p) (p)->lpVtbl->Shutdown(p) +#define IA3d4_RegisterApp(p,a) (p)->lpVtbl->RegisterApp(p,a) + +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d4_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3d4_AddRef(p) (p)->AddRef() +#define IA3d4_Release(p) (p)->Release() +#define IA3d4_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c) +#define IA3d4_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c) +#define IA3d4_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a) +#define IA3d4_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a) +#define IA3d4_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a) +#define IA3d4_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a) +#define IA3d4_RegisterVersion(p,a) (p)->RegisterVersion(a) +#define IA3d4_GetSoftwareCaps(p,a) (p)->GetSoftwareCaps(a) +#define IA3d4_GetHardwareCaps(p,a) (p)->GetHardwareCaps(a) +#define IA3d4_Clear(p) (p)->Clear() +#define IA3d4_Flush(p) (p)->Flush() +#define IA3d4_Compat(p,a,b) (p)->Compat(a,b) +#define IA3d4_Init(p,a,b,c) (p)->Init(a,b,c) +#define IA3d4_IsFeatureAvailable(p,a) (p)->IsFeatureAvailable(a) +#define IA3d4_NewSource(p,a,b) (p)->NewSource(a,b) +#define IA3d4_DuplicateSource(p,a,b) (p)->DuplicateSource(a,b) +#define IA3d4_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IA3d4_GetCooperativeLevel(p,a) (p)->GetCooperativeLevel(a) +#define IA3d4_SetMaxReflectionDelayTime(p,a) (p)->SetMaxReflectionDelayTime(a) +#define IA3d4_GetMaxReflectionDelayTime(p,a) (p)->GetMaxReflectionDelayTime(a) +#define IA3d4_SetCoordinateSystem(p,a) (p)->SetCoordinateSystem(a) +#define IA3d4_GetCoordinateSystem(p,a) (p)->GetCoordinateSystem(a) +#define IA3d4_SetOutputGain(p,a) (p)->SetOutputGain(a) +#define IA3d4_GetOutputGain(p,a) (p)->GetOutputGain(a) +#define IA3d4_SetNumFallbackSources(p,a) (p)->SetNumFallbackSources(a) +#define IA3d4_GetNumFallbackSources(p,a) (p)->GetNumFallbackSources(a) +#define IA3d4_SetRMPriorityBias(p,a) (p)->SetRMPriorityBias(a) +#define IA3d4_GetRMPriorityBias(p,a) (p)->GetRMPriorityBias(a) +#define IA3d4_DisableViewer(p) (p)->DisableViewer() +#define IA3d4_SetUnitsPerMeter(p,a) (p)->SetUnitsPerMeter(a) +#define IA3d4_GetUnitsPerMeter(p,a) (p)->GetUnitsPerMeter(a) +#define IA3d4_SetDopplerScale(p,a) (p)->SetDopplerScale(a) +#define IA3d4_GetDopplerScale(p,a) (p)->GetDopplerScale(a) +#define IA3d4_SetDistanceModelScale(p,a) (p)->SetDistanceModelScale(a) +#define IA3d4_GetDistanceModelScale(p,a) (p)->GetDistanceModelScale(a) +#define IA3d4_SetEq(p,a) (p)->SetEq(a) +#define IA3d4_GetEq(p,a) (p)->GetEq(a) +#define IA3d4_Shutdown(p) (p)->Shutdown() +#define IA3d4_RegisterApp(p,a) (p)->RegisterApp(a) + +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + + +//=================================================================== +// IA3dGeom +// +// The low level geometry renderer. +//=================================================================== + +// {C398E561-D90B-11d1-90FB-006008A1F441} +DEFINE_GUID(IID_IA3dGeom, 0xc398e561, 0xd90b, 0x11d1, 0x90, 0xfb, 0x0, 0x60, 0x8, 0xa1, 0xf4, 0x41); + +#undef INTERFACE +#define INTERFACE IA3dGeom + +DECLARE_INTERFACE_(IA3dGeom, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3dGeom Methods. + STDMETHOD(Enable) (THIS_ DWORD) PURE; + STDMETHOD(Disable) (THIS_ DWORD) PURE; + STDMETHOD_(BOOL, IsEnabled) (THIS_ DWORD) PURE; + STDMETHOD(SetOcclusionMode) (THIS_ DWORD) PURE; + STDMETHOD(GetOcclusionMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetReflectionMode) (THIS_ DWORD) PURE; + STDMETHOD(GetReflectionMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetReflectionGainScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetReflectionGainScale) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetReflectionDelayScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetReflectionDelayScale) (THIS_ LPA3DVAL) PURE; + STDMETHOD_(ULONG, PushMatrix) (THIS) PURE; + STDMETHOD_(ULONG, PopMatrix) (THIS) PURE; + STDMETHOD(LoadIdentity) (THIS) PURE; + STDMETHOD(LoadMatrix) (THIS_ A3DMATRIX) PURE; + STDMETHOD(GetMatrix) (THIS_ A3DMATRIX) PURE; + STDMETHOD(MultMatrix) (THIS_ A3DMATRIX) PURE; + STDMETHOD(Translate3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(Translate3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(Rotate3f) (THIS_ A3DVAL, A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(Rotate3fv) (THIS_ A3DVAL, LPA3DVAL) PURE; + STDMETHOD(Scale3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(Scale3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(Begin) (THIS_ DWORD) PURE; + STDMETHOD(End) (THIS) PURE; + STDMETHOD(Vertex3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(Vertex3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(Normal3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(Normal3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(Tag) (THIS_ DWORD) PURE; + STDMETHOD(SetOpeningFactorf) (THIS_ A3DVAL ) PURE; + STDMETHOD(SetOpeningFactorfv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(NewMaterial) (THIS_ LPA3DMATERIAL *) PURE; + STDMETHOD(BindMaterial) (THIS_ LPA3DMATERIAL) PURE; + STDMETHOD(NewList) (THIS_ LPA3DLIST *) PURE; + STDMETHOD(BindListener) (THIS) PURE; + STDMETHOD(BindSource) (THIS_ LPA3DSOURCE) PURE; + STDMETHOD(NewEnvironment) (THIS_ LPA3DENVIRONMENT *) PURE; + STDMETHOD(BindEnvironment) (THIS_ LPA3DENVIRONMENT ) PURE; + STDMETHOD(SetRenderMode) (THIS_ DWORD) PURE; + STDMETHOD(GetRenderMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetPolygonBloatFactor) (THIS_ A3DVAL) PURE; + STDMETHOD(GetPolygonBloatFactor) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetReflectionUpdateInterval) (THIS_ DWORD) PURE; + STDMETHOD(GetReflectionUpdateInterval) (THIS_ LPDWORD) PURE; + STDMETHOD(SetOcclusionUpdateInterval) (THIS_ DWORD) PURE; + STDMETHOD(GetOcclusionUpdateInterval) (THIS_ LPDWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dGeom_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3dGeom_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3dGeom_Release(p) (p)->lpVtbl->Release(p) +#define IA3dGeom_Enable(p,a) (p)->lpVtbl->Enable(p,a) +#define IA3dGeom_Disable(p,a) (p)->lpVtbl->Disable(p,a) +#define IA3dGeom_IsEnabled(p,a) (p)->lpVtbl->IsEnabled(p,a) +#define IA3dGeom_SetOcclusionMode(p,a) (p)->lpVtbl->SetOcclusionMode(p,a) +#define IA3dGeom_GetOcclusionMode(p,a) (p)->lpVtbl->GetOcclusionMode(p,a) +#define IA3dGeom_SetReflectionMode(p,a) (p)->lpVtbl->SetReflectionMode(p,a) +#define IA3dGeom_GetReflectionMode(p,a) (p)->lpVtbl->GetReflectionMode(p,a) +#define IA3dGeom_SetReflectionGainScale(p,a) (p)->lpVtbl->SetReflectionGainScale(p,a) +#define IA3dGeom_GetReflectionGainScale(p,a) (p)->lpVtbl->GetReflectionGainScale(p,a) +#define IA3dGeom_SetReflectionDelayScale(p,a) (p)->lpVtbl->SetReflectionDelayScale(p,a) +#define IA3dGeom_GetReflectionDelayScale(p,a) (p)->lpVtbl->GetReflectionDelayScale(p,a) +#define IA3dGeom_PushMatrix(p) (p)->lpVtbl->PushMatrix(p) +#define IA3dGeom_PopMatrix(p) (p)->lpVtbl->PopMatrix(p) +#define IA3dGeom_LoadIdentity(p) (p)->lpVtbl->LoadIdentity(p) +#define IA3dGeom_LoadMatrix(p,a) (p)->lpVtbl->LoadMatrix(p,a) +#define IA3dGeom_GetMatrix(p,a) (p)->lpVtbl->GetMatrix(p,a) +#define IA3dGeom_MultMatrix(p,a) (p)->lpVtbl->MultMatrix(p,a) +#define IA3dGeom_Translate3f(p,a,b,c) (p)->lpVtbl->Translate3f(p,a,b,c) +#define IA3dGeom_Translate3fv(p,a) (p)->lpVtbl->Translate3fv(p,a) +#define IA3dGeom_Rotate3f(p,a,b,c,d) (p)->lpVtbl->Rotate3f(p,a,b,c,d) +#define IA3dGeom_Rotate3fv(p,a,b) (p)->lpVtbl->Rotate3fv(p,a,b) +#define IA3dGeom_Scale3f(p,a,b,c) (p)->lpVtbl->Scale3f(p,a,b,c) +#define IA3dGeom_Scale3fv(p,a) (p)->lpVtbl->Scale3fv(p,a) +#define IA3dGeom_Begin(p,a) (p)->lpVtbl->Begin(p,a) +#define IA3dGeom_End(p) (p)->lpVtbl->End(p) +#define IA3dGeom_Vertex3f(p,a,b,c) (p)->lpVtbl->Vertex3f(p,a,b,c) +#define IA3dGeom_Vertex3fv(p,a) (p)->lpVtbl->Vertex3fv(p,a) +#define IA3dGeom_Normal3f(p,a,b,c) (p)->lpVtbl->Normal3f(p,a,b,c) +#define IA3dGeom_Normal3fv(p,a) (p)->lpVtbl->Normal3fv(p,a) +#define IA3dGeom_Tag(p,a) (p)->lpVtbl->Tag(p,a) +#define IA3dGeom_SetOpeningFactorf(p,a) (p)->lpVtbl->SetOpeningFactorf(p,a) +#define IA3dGeom_SetOpeningFactorfv(p,a) (p)->lpVtbl->SetOpeningFactorfv(p,a) +#define IA3dGeom_NewMaterial(p,a) (p)->lpVtbl->NewMaterial(p,a) +#define IA3dGeom_BindMaterial(p,a) (p)->lpVtbl->BindMaterial(p,a) +#define IA3dGeom_NewList(p,a) (p)->lpVtbl->NewList(p,a) +#define IA3dGeom_BindListener(p) (p)->lpVtbl->BindListener(p) +#define IA3dGeom_BindSource(p,a) (p)->lpVtbl->BindSource(p,a) +#define IA3dGeom_NewEnvironment(p,a) (p)->lpVtbl->NewEnvironment(p,a) +#define IA3dGeom_BindEnvironment(p,a) (p)->lpVtbl->BindEnvironment(p,a) +#define IA3dGeom_SetRenderMode(p,a) (p)->lpVtbl->SetRenderMode(p,a) +#define IA3dGeom_GetRenderMode(p,a) (p)->lpVtbl->GetRenderMode(p,a) +#define IA3dGeom_SetPolygonBloatFactor(p,a) (p)->lpVtbl->SetPolygonBloatFactor(p,a) +#define IA3dGeom_GetPolygonBloatFactor(p,a) (p)->lpVtbl->GetPolygonBloatFactor(p,a) +#define IA3dGeom_SetReflectionUpdateInterval(p,a) (p)->lpVtbl->SetReflectionUpdateInterval(p,a) +#define IA3dGeom_GetReflectionUpdateInterval(p,a) (p)->lpVtbl->GetReflectionUpdateInterval(p,a) +#define IA3dGeom_SetOcclusionUpdateInterval(p,a) (p)->lpVtbl->SetOcclusionUpdateInterval(p,a) +#define IA3dGeom_GetOcclusionUpdateInterval(p,a) (p)->lpVtbl->GetOcclusionUpdateInterval(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dGeom_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3dGeom_AddRef(p) (p)->AddRef() +#define IA3dGeom_Release(p) (p)->Release() +#define IA3dGeom_Enable(p,a) (p)->Enable(a) +#define IA3dGeom_Disable(p,a) (p)->Disable(a) +#define IA3dGeom_IsEnabled(p,a) (p)->IsEnabled(a) +#define IA3dGeom_SetOcclusionMode(p,a) (p)->SetOcclusionMode(a) +#define IA3dGeom_GetOcclusionMode(p,a) (p)->GetOcclusionMode(a) +#define IA3dGeom_SetReflectionMode(p,a) (p)->SetReflectionMode(a) +#define IA3dGeom_GetReflectionMode(p,a) (p)->GetReflectionMode(a) +#define IA3dGeom_SetReflectionGainScale(p,a) (p)->SetReflectionGainScale(a) +#define IA3dGeom_GetReflectionGainScale(p,a) (p)->GetReflectionGainScale(a) +#define IA3dGeom_SetReflectionDelayScale(p,a) (p)->SetReflectionDelayScale(a) +#define IA3dGeom_GetReflectionDelayScale(p,a) (p)->GetReflectionDelayScale(a) +#define IA3dGeom_PushMatrix(p) (p)->PushMatrix() +#define IA3dGeom_PopMatrix(p) (p)->PopMatrix() +#define IA3dGeom_LoadIdentity(p) (p)->LoadIdentity() +#define IA3dGeom_LoadMatrix(p,a) (p)->LoadMatrix(a) +#define IA3dGeom_GetMatrix(p,a) (p)->GetMatrix(a) +#define IA3dGeom_MultMatrix(p,a) (p)->MultMatrix(a) +#define IA3dGeom_Translate3f(p,a,b,c) (p)->Translate3f(a,b,c) +#define IA3dGeom_Translate3fv(p,a) (p)->Translate3fv(a) +#define IA3dGeom_Rotate3f(p,a,b,c,d) (p)->Rotate3f(a,b,c,d) +#define IA3dGeom_Rotate3fv(p,a,b) (p)->Rotate3fv(a,b) +#define IA3dGeom_Scale3f(p,a,b,c) (p)->Scale3f(a,b,c) +#define IA3dGeom_Scale3fv(p,a) (p)->Scale3fv(a) +#define IA3dGeom_Begin(p,a) (p)->Begin(a) +#define IA3dGeom_End(p) (p)->End() +#define IA3dGeom_Vertex3f(p,a,b,c) (p)->Vertex3f(a,b,c) +#define IA3dGeom_Vertex3fv(p,a) (p)->Vertex3fv(a) +#define IA3dGeom_Normal3f(p,a,b,c) (p)->Normal3f(a,b,c) +#define IA3dGeom_Normal3fv(p,a) (p)->Normal3fv(a) +#define IA3dGeom_Tag(p,a) (p)->Tag(a) +#define IA3dGeom_SetOpeningFactorf(p,a) (p)->SetOpeningFactorf(p,a) +#define IA3dGeom_SetOpeningFactorfv(p,a) (p)->SetOpeningFactorfv(p,a) +#define IA3dGeom_NewMaterial(p,a) (p)->NewMaterial(a) +#define IA3dGeom_BindMaterial(p,a) (p)->BindMaterial(a) +#define IA3dGeom_NewList(p,a) (p)->NewList(a) +#define IA3dGeom_BindListener(p) (p)->BindListener() +#define IA3dGeom_BindSource(p,a) (p)->BindSource(a) +#define IA3dGeom_NewEnvironment(p,a) (p)->NewEnvironment(a) +#define IA3dGeom_BindEnvironment(p,a) (p)->BindEnvironment(a) +#define IA3dGeom_SetRenderMode(p,a) (p)->SetRenderMode(a) +#define IA3dGeom_GetRenderMode(p,a) (p)->GetRenderMode(a) +#define IA3dGeom_SetPolygonBloatFactor(p,a) (p)->SetPolygonBloatFactor(a) +#define IA3dGeom_GetPolygonBloatFactor(p,a) (p)->GetPolygonBloatFactor(a) +#define IA3dGeom_SetReflectionUpdateInterval(p,a) (p)->SetReflectionUpdateInterval(a) +#define IA3dGeom_GetReflectionUpdateInterval(p,a) (p)->GetReflectionUpdateInterval(a) +#define IA3dGeom_SetOcclusionUpdateInterval(p,a) (p)->SetOcclusionUpdateInterval(a) +#define IA3dGeom_GetOcclusionUpdateInterval(p,a) (p)->GetOcclusionUpdateInterval(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + + +//=================================================================== +// IA3dSource +// +// A sound source. +//=================================================================== + +// {C398E562-D90B-11d1-90FB-006008A1F441} +DEFINE_GUID(IID_IA3dSource, 0xc398e562, 0xd90b, 0x11d1, 0x90, 0xfb, 0x0, 0x60, 0x8, 0xa1, 0xf4, 0x41); + +#undef INTERFACE +#define INTERFACE IA3dSource + +DECLARE_INTERFACE_(IA3dSource, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3dSource Methods. + STDMETHOD(LoadWaveFile) (THIS_ LPSTR) PURE; + STDMETHOD(LoadWaveData) (THIS_ LPVOID, DWORD) PURE; + STDMETHOD(AllocateWaveData) (THIS_ INT) PURE; + STDMETHOD(FreeWaveData) (THIS) PURE; + STDMETHOD(SetWaveFormat) (THIS_ LPVOID) PURE; + STDMETHOD(GetWaveFormat) (THIS_ LPVOID) PURE; + STDMETHOD(GetWaveSize) (THIS) PURE; + STDMETHOD(GetType) (THIS_ LPDWORD) PURE; + STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD) PURE; + STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(Play) (THIS_ INT) PURE; + STDMETHOD(Stop) (THIS) PURE; + STDMETHOD(Rewind) (THIS) PURE; + STDMETHOD(SetWaveTime) (THIS_ A3DVAL) PURE; + STDMETHOD(GetWaveTime) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetWavePosition) (THIS_ DWORD) PURE; + STDMETHOD(GetWavePosition) (THIS_ LPDWORD) PURE; + STDMETHOD(SetPosition3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetPosition3f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetPosition3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetPosition3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetOrientationAngles3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetOrientationAngles3f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetOrientationAngles3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetOrientationAngles3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetOrientation6f) (THIS_ A3DVAL, A3DVAL, A3DVAL, A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetOrientation6f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL, LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetOrientation6fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetOrientation6fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetVelocity3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetVelocity3f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetVelocity3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetVelocity3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetCone) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetCone) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetMinMaxDistance) (THIS_ A3DVAL, A3DVAL, DWORD) PURE; + STDMETHOD(GetMinMaxDistance) (THIS_ LPA3DVAL, LPA3DVAL, LPDWORD) PURE; + STDMETHOD(SetGain) (THIS_ A3DVAL) PURE; + STDMETHOD(GetGain) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetPitch) (THIS_ A3DVAL) PURE; + STDMETHOD(GetPitch) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetDopplerScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetDopplerScale) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetDistanceModelScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetDistanceModelScale) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetEq) (THIS_ A3DVAL) PURE; + STDMETHOD(GetEq) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetPriority) (THIS_ A3DVAL) PURE; + STDMETHOD(GetPriority) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetRenderMode) (THIS_ DWORD) PURE; + STDMETHOD(GetRenderMode) (THIS_ LPDWORD) PURE; + STDMETHOD(GetAudibility) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetOcclusionFactor) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; + STDMETHOD(SetPanValues) (THIS_ DWORD, LPA3DVAL) PURE; + STDMETHOD(GetPanValues) (THIS_ DWORD, LPA3DVAL) PURE; + STDMETHOD(SetWaveEvent) (THIS_ DWORD, HANDLE) PURE; + STDMETHOD(ClearWaveEvents) (THIS) PURE; + STDMETHOD(SetTransformMode) (THIS_ DWORD) PURE; + STDMETHOD(GetTransformMode) (THIS_ LPDWORD) PURE; + STDMETHOD(SetReflectionDelayScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetReflectionDelayScale) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetReflectionGainScale) (THIS_ A3DVAL) PURE; + STDMETHOD(GetReflectionGainScale) (THIS_ LPA3DVAL) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dSource_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3dSource_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3dSource_Release(p) (p)->lpVtbl->Release(p) +#define IA3dSource_LoadWaveFile(p,a) (p)->lpVtbl->LoadWaveFile(p,a) +#define IA3dSource_LoadWaveData(p,a,b) (p)->lpVtbl->LoadWaveData(p,a,b) +#define IA3dSource_AllocateWaveData(p,a) (p)->lpVtbl->AllocateWaveData(p,a) +#define IA3dSource_FreeWaveData(p) (p)->lpVtbl->FreeWaveData(p) +#define IA3dSource_SetWaveFormat(p,a) (p)->lpVtbl->SetWaveFormat(p,a) +#define IA3dSource_GetWaveFormat(p,a) (p)->lpVtbl->GetWaveFormat(p,a) +#define IA3dSource_GetWaveSize(p) (p)->lpVtbl->GetWaveSize(p) +#define IA3dSource_GetType(p,a) (p)->lpVtbl->GetType(p,a) +#define IA3dSource_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) +#define IA3dSource_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) +#define IA3dSource_Play(p,a) (p)->lpVtbl->Play(p,a) +#define IA3dSource_Stop(p) (p)->lpVtbl->Stop(p) +#define IA3dSource_Rewind(p) (p)->lpVtbl->Rewind(p) +#define IA3dSource_SetWaveTime(p,a) (p)->lpVtbl->SetWaveTime(p,a) +#define IA3dSource_GetWaveTime(p,a) (p)->lpVtbl->GetWaveTime(p,a) +#define IA3dSource_SetWavePosition(p,a) (p)->lpVtbl->SetWavePosition(p,a) +#define IA3dSource_GetWavePosition(p,a) (p)->lpVtbl->GetWavePosition(p,a) +#define IA3dSource_SetPosition3f(p,a,b,c) (p)->lpVtbl->SetPosition3f(p,a,b,c) +#define IA3dSource_GetPosition3f(p,a,b,c) (p)->lpVtbl->GetPosition3f(p,a,b,c) +#define IA3dSource_SetPosition3fv(p,a) (p)->lpVtbl->SetPosition3fv(p,a) +#define IA3dSource_GetPosition3fv(p,a) (p)->lpVtbl->GetPosition3fv(p,a) +#define IA3dSource_SetOrientationAngles3f(p,a,b,c) (p)->lpVtbl->SetOrientationAngles3f(p,a,b,c) +#define IA3dSource_GetOrientationAngles3f(p,a,b,c) (p)->lpVtbl->GetOrientationAngles3f(p,a,b,c) +#define IA3dSource_SetOrientationAngles3fv(p,a) (p)->lpVtbl->SetOrientationAngles3fv(p,a) +#define IA3dSource_GetOrientationAngles3fv(p,a) (p)->lpVtbl->GetOrientationAngles3fv(p,a) +#define IA3dSource_SetOrientation6f(p,a,b,c,d,e,f) (p)->lpVtbl->SetOrientation6f(p,a,b,c,d,e,f) +#define IA3dSource_GetOrientation6f(p,a,b,c,d,e,f) (p)->lpVtbl->GetOrientation6f(p,a,b,c,d,e,f) +#define IA3dSource_SetOrientation6fv(p,a) (p)->lpVtbl->SetOrientation6fv(p,a) +#define IA3dSource_GetOrientation6fv(p,a) (p)->lpVtbl->GetOrientation6fv(p,a) +#define IA3dSource_SetVelocity3f(p,a,b,c) (p)->lpVtbl->SetVelocity3f(p,a,b,c) +#define IA3dSource_GetVelocity3f(p,a,b,c) (p)->lpVtbl->GetVelocity3f(p,a,b,c) +#define IA3dSource_SetVelocity3fv(p,a) (p)->lpVtbl->SetVelocity3fv(p,a) +#define IA3dSource_GetVelocity3fv(p,a) (p)->lpVtbl->GetVelocity3fv(p,a) +#define IA3dSource_SetCone(p,a,b,c) (p)->lpVtbl->SetCone(p,a,b,c) +#define IA3dSource_GetCone(p,a,b,c) (p)->lpVtbl->GetCone(p,a,b,c) +#define IA3dSource_SetMinMaxDistance(p,a,b,c) (p)->lpVtbl->SetMinMaxDistance(p,a,b,c) +#define IA3dSource_GetMinMaxDistance(p,a,b,c) (p)->lpVtbl->GetMinMaxDistance(p,a,b,c) +#define IA3dSource_SetGain(p,a) (p)->lpVtbl->SetGain(p,a) +#define IA3dSource_GetGain(p,a) (p)->lpVtbl->GetGain(p,a) +#define IA3dSource_SetPitch(p,a) (p)->lpVtbl->SetPitch(p,a) +#define IA3dSource_GetPitch(p,a) (p)->lpVtbl->GetPitch(p,a) +#define IA3dSource_SetDopplerScale(p,a) (p)->lpVtbl->SetDopplerScale(p,a) +#define IA3dSource_GetDopplerScale(p,a) (p)->lpVtbl->GetDopplerScale(p,a) +#define IA3dSource_SetDistanceModelScale(p,a) (p)->lpVtbl->SetDistanceModelScale(p,a) +#define IA3dSource_GetDistanceModelScale(p,a) (p)->lpVtbl->GetDistanceModelScale(p,a) +#define IA3dSource_SetEq(p,a) (p)->lpVtbl->SetEq(p,a) +#define IA3dSource_GetEq(p,a) (p)->lpVtbl->GetEq(p,a) +#define IA3dSource_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IA3dSource_GetPriority(p,a) (p)->lpVtbl->GetPriority(p,a) +#define IA3dSource_SetRenderMode(p,a) (p)->lpVtbl->SetRenderMode(p,a) +#define IA3dSource_GetRenderMode(p,a) (p)->lpVtbl->GetRenderMode(p,a) +#define IA3dSource_GetAudibility(p,a) (p)->lpVtbl->GetAudibility(p,a) +#define IA3dSource_GetOcclusionFactor(p,a) (p)->lpVtbl->GetOcclusionFactor(p,a) +#define IA3dSource_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) +#define IA3dSource_SetPanValues(p,a,b) (p)->lpVtbl->SetPanValues(p,a,b) +#define IA3dSource_GetPanValues(p,a,b) (p)->lpVtbl->GetPanValues(p,a,b) +#define IA3dSource_SetWaveEvent(p,a,b) (p)->lpVtbl->SetWaveEvent(p,a,b) +#define IA3dSource_ClearWaveEvents(p) (p)->lpVtbl->ClearWaveEvents(p) +#define IA3dSource_SetTransformMode(p,a) (p)->lpVtbl->SetTransformMode(p,a) +#define IA3dSource_GetTransformMode(p,a) (p)->lpVtbl->GetTransformMode(p,a) +#define IA3dSource_SetReflectionDelayScale(p,a) (p)->lpVtbl->SetReflectionDelayScale(p,a) +#define IA3dSource_GetReflectionDelayScale(p,a) (p)->lpVtbl->GetReflectionDelayScale(p,a) +#define IA3dSource_SetReflectionGainScale(p,a) (p)->lpVtbl->SetReflectionGainScale(p,a) +#define IA3dSource_GetReflectionGainScale(p,a) (p)->lpVtbl->GetReflectionGainScale(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dSource_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3dSource_AddRef(p) (p)->AddRef() +#define IA3dSource_Release(p) (p)->Release() +#define IA3dSource_LoadWaveFile(p,a) (p)->LoadWaveFile(a) +#define IA3dSource_LoadWaveData(p,a,b) (p)->LoadWaveData(a,b) +#define IA3dSource_AllocateWaveData(p,a) (p)->AllocateWaveData(a) +#define IA3dSource_FreeWaveData(p) (p)->FreeWaveData() +#define IA3dSource_SetWaveFormat(p,a) (p)->SetWaveFormat(a) +#define IA3dSource_GetWaveFormat(p,a) (p)->GetWaveFormat(a) +#define IA3dSource_GetWaveSize(p) (p)->GetWaveSize() +#define IA3dSource_GetType(p,a) (p)->GetType(a) +#define IA3dSource_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g) +#define IA3dSource_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d) +#define IA3dSource_Play(p,a) (p)->Play(a) +#define IA3dSource_Stop(p) (p)->Stop() +#define IA3dSource_Rewind(p) (p)->Rewind() +#define IA3dSource_SetWaveTime(p,a) (p)->SetWaveTime(a) +#define IA3dSource_GetWaveTime(p,a) (p)->GetWaveTime(a) +#define IA3dSource_SetWavePosition(p,a) (p)->SetWavePosition(a) +#define IA3dSource_GetWavePosition(p,a) (p)->GetWavePosition(a) +#define IA3dSource_SetPosition3f(p,a,b,c) (p)->SetPosition3f(a,b,c) +#define IA3dSource_GetPosition3f(p,a,b,c) (p)->GetPosition3f(a,b,c) +#define IA3dSource_SetPosition3fv(p,a) (p)->SetPosition3fv(a) +#define IA3dSource_GetPosition3fv(p,a) (p)->GetPosition3fv(a) +#define IA3dSource_SetOrientationAngles3f(p,a,b,c) (p)->SetOrientationAngles3f(a,b,c) +#define IA3dSource_GetOrientationAngles3f(p,a,b,c) (p)->GetOrientationAngles3f(a,b,c) +#define IA3dSource_SetOrientationAngles3fv(p,a) (p)->SetOrientationAngles3fv(a) +#define IA3dSource_GetOrientationAngles3fv(p,a) (p)->GetOrientationAngles3fv(a) +#define IA3dSource_SetOrientation6f(p,a,b,c,d,e,f) (p)->SetOrientation6f(a,b,c,d,e,f) +#define IA3dSource_GetOrientation6f(p,a,b,c,d,e,f) (p)->GetOrientation6f(a,b,c,d,e,f) +#define IA3dSource_SetOrientation6fv(p,a) (p)->SetOrientation6fv(a) +#define IA3dSource_GetOrientation6fv(p,a) (p)->GetOrientation6fv(a) +#define IA3dSource_SetVelocity3f(p,a,b,c) (p)->SetVelocity3f(a,b,c) +#define IA3dSource_GetVelocity3f(p,a,b,c) (p)->GetVelocity3f(a,b,c) +#define IA3dSource_SetVelocity3fv(p,a) (p)->SetVelocity3fv(a) +#define IA3dSource_GetVelocity3fv(p,a) (p)->GetVelocity3fv(a) +#define IA3dSource_SetCone(p,a,b,c) (p)->SetCone(a,b,c) +#define IA3dSource_GetCone(p,a,b,c) (p)->GetCone(a,b,c) +#define IA3dSource_SetMinMaxDistance(p,a,b,c) (p)->SetMinMaxDistance(a,b,c) +#define IA3dSource_GetMinMaxDistance(p,a,b,c) (p)->GetMinMaxDistance(a,b,c) +#define IA3dSource_SetGain(p,a) (p)->SetGain(a) +#define IA3dSource_GetGain(p,a) (p)->GetGain(a) +#define IA3dSource_SetPitch(p,a) (p)->SetPitch(a) +#define IA3dSource_GetPitch(p,a) (p)->GetPitch(a) +#define IA3dSource_SetDopplerScale(p,a) (p)->SetDopplerScale(a) +#define IA3dSource_GetDopplerScale(p,a) (p)->GetDopplerScale(a) +#define IA3dSource_SetDistanceModelScale(p,a) (p)->SetDistanceModelScale(a) +#define IA3dSource_GetDistanceModelScale(p,a) (p)->GetDistanceModelScale(a) +#define IA3dSource_SetEq(p,a) (p)->SetEq(a) +#define IA3dSource_GetEq(p,a) (p)->GetEq(a) +#define IA3dSource_SetPriority(p,a) (p)->SetPriority(a) +#define IA3dSource_GetPriority(p,a) (p)->GetPriority(a) +#define IA3dSource_SetRenderMode(p,a) (p)->SetRenderMode(a) +#define IA3dSource_GetRenderMode(p,a) (p)->GetRenderMode(a) +#define IA3dSource_GetAudibility(p,a) (p)->GetAudibility(a) +#define IA3dSource_GetOcclusionFactor(p,a) (p)->GetOcclusionFactor(a) +#define IA3dSource_GetStatus(p,a) (p)->GetStatus(a) +#define IA3dSource_SetPanValues(p,a,b) (p)->SetPanValues(a,b) +#define IA3dSource_GetPanValues(p,a,b) (p)->GetPanValues(a,b) +#define IA3dSource_SetWaveEvent(p,a,b) (p)->SetWaveEvent(a,b) +#define IA3dSource_ClearWaveEvents(p) (p)->ClearWaveEvents() +#define IA3dSource_SetTransformMode(p,a) (p)->SetTransformMode(a) +#define IA3dSource_GetTransformMode(p,a) (p)->GetTransformMode(a) +#define IA3dSource_SetReflectionDelayScale(p,a) (p)->SetReflectionDelayScale(a) +#define IA3dSource_GetReflectionDelayScale(p,a) (p)->GetReflectionDelayScale(a) +#define IA3dSource_SetReflectionGainScale(p,a) (p)->SetReflectionGainScale(a) +#define IA3dSource_GetReflectionGainScale(p,a) (p)->GetReflectionGainScale(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + + +//=================================================================== +// IA3dListener +// +// There is only one listener in A3D. +//=================================================================== + +// {C398E563-D90B-11d1-90FB-006008A1F441} +DEFINE_GUID(IID_IA3dListener, 0xc398e563, 0xd90b, 0x11d1, 0x90, 0xfb, 0x0, 0x60, 0x8, 0xa1, 0xf4, 0x41); + +#undef INTERFACE +#define INTERFACE IA3dListener + +DECLARE_INTERFACE_(IA3dListener, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3dListener Methods. + STDMETHOD(SetPosition3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetPosition3f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetPosition3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetPosition3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetOrientationAngles3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetOrientationAngles3f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetOrientationAngles3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetOrientationAngles3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetOrientation6f) (THIS_ A3DVAL, A3DVAL, A3DVAL, A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetOrientation6f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL, LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetOrientation6fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetOrientation6fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(SetVelocity3f) (THIS_ A3DVAL, A3DVAL, A3DVAL) PURE; + STDMETHOD(GetVelocity3f) (THIS_ LPA3DVAL, LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetVelocity3fv) (THIS_ LPA3DVAL) PURE; + STDMETHOD(GetVelocity3fv) (THIS_ LPA3DVAL) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dListener_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3dListener_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3dListener_Release(p) (p)->lpVtbl->Release(p) +#define IA3dListener_SetPosition3f(p,a,b,c) (p)->lpVtbl->SetPosition3f(p,a,b,c) +#define IA3dListener_GetPosition3f(p,a,b,c) (p)->lpVtbl->GetPosition3f(p,a,b,c) +#define IA3dListener_SetPosition3fv(p,a) (p)->lpVtbl->SetPosition3fv(p,a) +#define IA3dListener_GetPosition3fv(p,a) (p)->lpVtbl->GetPosition3fv(p,a) +#define IA3dListener_SetOrientationAngles3f(p,a,b,c) (p)->lpVtbl->SetOrientationAngles3f(p,a,b,c) +#define IA3dListener_GetOrientationAngles3f(p,a,b,c) (p)->lpVtbl->GetOrientationAngles3f(p,a,b,c) +#define IA3dListener_SetOrientationAngles3fv(p,a) (p)->lpVtbl->SetOrientationAngles3fv(p,a) +#define IA3dListener_GetOrientationAngles3fv(p,a) (p)->lpVtbl->GetOrientationAngles3fv(p,a) +#define IA3dListener_SetOrientation6f(p,a,b,c,d,e,f) (p)->lpVtbl->SetOrientation6f(p,a,b,c,d,e,f) +#define IA3dListener_GetOrientation6f(p,a,b,c,d,e,f) (p)->lpVtbl->GetOrientation6f(p,a,b,c,d,e,f) +#define IA3dListener_SetOrientation6fv(p,a) (p)->lpVtbl->SetOrientation6fv(p,a) +#define IA3dListener_GetOrientation6fv(p,a) (p)->lpVtbl->GetOrientation6fv(p,a) +#define IA3dListener_SetVelocity3f(p,a,b,c) (p)->lpVtbl->SetVelocity3f(p,a,b,c) +#define IA3dListener_GetVelocity3f(p,a,b,c) (p)->lpVtbl->GetVelocity3f(p,a,b,c) +#define IA3dListener_SetVelocity3fv(p,a) (p)->lpVtbl->SetVelocity3fv(p,a) +#define IA3dListener_GetVelocity3fv(p,a) (p)->lpVtbl->GetVelocity3fv(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dListener_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3dListener_AddRef(p) (p)->AddRef() +#define IA3dListener_Release(p) (p)->Release() +#define IA3dListener_SetPosition3f(p,a,b,c) (p)->SetPosition3f(a,b,c) +#define IA3dListener_GetPosition3f(p,a,b,c) (p)->GetPosition3f(a,b,c) +#define IA3dListener_SetPosition3fv(p,a) (p)->SetPosition3fv(a) +#define IA3dListener_GetPosition3fv(p,a) (p)->GetPosition3fv(a) +#define IA3dListener_SetOrientationAngles3f(p,a,b,c) (p)->SetOrientationAngles3f(a,b,c) +#define IA3dListener_GetOrientationAngles3f(p,a,b,c) (p)->GetOrientationAngles3f(a,b,c) +#define IA3dListener_SetOrientationAngles3fv(p,a) (p)->SetOrientationAngles3fv(a) +#define IA3dListener_GetOrientationAngles3fv(p,a) (p)->GetOrientationAngles3fv(a) +#define IA3dListener_SetOrientation6f(p,a,b,c,d,e,f) (p)->SetOrientation6f(a,b,c,d,e,f) +#define IA3dListener_GetOrientation6f(p,a,b,c,d,e,f) (p)->GetOrientation6f(a,b,c,d,e,f) +#define IA3dListener_SetOrientation6fv(p,a) (p)->SetOrientation6fv(a) +#define IA3dListener_GetOrientation6fv(p,a) (p)->GetOrientation6fv(a) +#define IA3dListener_SetVelocity3f(p,a,b,c) (p)->SetVelocity3f(a,b,c) +#define IA3dListener_GetVelocity3f(p,a,b,c) (p)->GetVelocity3f(a,b,c) +#define IA3dListener_SetVelocity3fv(p,a) (p)->SetVelocity3fv(a) +#define IA3dListener_GetVelocity3fv(p,a) (p)->GetVelocity3fv(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + + +//=================================================================== +// IA3dList +// +// List of geometry and state data for IA3dGeom. +//=================================================================== + +// {C398E564-D90B-11d1-90FB-006008A1F441} +DEFINE_GUID(IID_IA3dList, 0xc398e564, 0xd90b, 0x11d1, 0x90, 0xfb, 0x0, 0x60, 0x8, 0xa1, 0xf4, 0x41); + +#undef INTERFACE +#define INTERFACE IA3dList + +DECLARE_INTERFACE_(IA3dList, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3dList Methods. + STDMETHOD(Begin) (THIS) PURE; + STDMETHOD(End) (THIS) PURE; + STDMETHOD(Call) (THIS) PURE; + STDMETHOD(EnableBoundingVol) (THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dList_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3dList_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3dList_Release(p) (p)->lpVtbl->Release(p) +#define IA3dList_Begin(p) (p)->lpVtbl->Begin(p) +#define IA3dList_End(p) (p)->lpVtbl->End(p) +#define IA3dList_Call(p) (p)->lpVtbl->Call(p) +#define IA3dList_EnableBoundingVol(p) (p)->lpVtbl->EnableBoundingVol(p) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dList_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3dList_AddRef(p) (p)->AddRef() +#define IA3dList_Release(p) (p)->Release() +#define IA3dList_Begin(p) (p)->Begin() +#define IA3dList_End(p) (p)->End() +#define IA3dList_Call(p) (p)->Call() +#define IA3dList_EnableBoundingVol(p) (p)->EnableBoundingVol() +#endif // !defined(__cplusplus) || defined(CINTERFACE) + + + +//=================================================================== +// IA3dMaterial +// +// A material builder. +//=================================================================== + +// {C398E565-D90B-11d1-90FB-006008A1F441} +DEFINE_GUID(IID_IA3dMaterial, 0xc398e565, 0xd90b, 0x11d1, 0x90, 0xfb, 0x0, 0x60, 0x8, 0xa1, 0xf4, 0x41); + +#undef INTERFACE +#define INTERFACE IA3dMaterial + +DECLARE_INTERFACE_(IA3dMaterial, IUnknown) +{ + // IUnknown Methods. + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3dMaterial Methods. + STDMETHOD(Load) (THIS_ LPSTR) PURE; + STDMETHOD(Save) (THIS_ LPSTR) PURE; + STDMETHOD(UnSerialize) (THIS_ LPVOID, UINT) PURE; + STDMETHOD(Serialize) (THIS_ LPVOID *, UINT *) PURE; + STDMETHOD(Duplicate) (THIS_ LPA3DMATERIAL *) PURE; + STDMETHOD(SetNameID) (THIS_ LPSTR) PURE; + STDMETHOD(GetNameID) (THIS_ LPSTR, INT) PURE; + STDMETHOD(SelectPreset) (THIS_ DWORD) PURE; + STDMETHOD(GetClosestPreset) (THIS_ LPDWORD) PURE; + STDMETHOD(SetReflectance) (THIS_ A3DVAL, A3DVAL) PURE; + STDMETHOD(GetReflectance) (THIS_ LPA3DVAL, LPA3DVAL) PURE; + STDMETHOD(SetTransmittance) (THIS_ A3DVAL, A3DVAL) PURE; + STDMETHOD(GetTransmittance) (THIS_ LPA3DVAL, LPA3DVAL) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dMaterial_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3dMaterial_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3dMaterial_Release(p) (p)->lpVtbl->Release(p) +#define IA3dMaterial_Load(p,a) (p)->lpVtbl->Load(p,a) +#define IA3dMaterial_Save(p,a) (p)->lpVtbl->Save(p,a) +#define IA3dMaterial_UnSerialize(p,a,b) (p)->lpVtbl->UnSerialize(p,a,b) +#define IA3dMaterial_Serialize(p,a,b) (p)->lpVtbl->Serialize(p,a,b) +#define IA3dMaterial_Duplicate(p,a) (p)->lpVtbl->Duplicate(p,a) +#define IA3dMaterial_SetNameID(p,a) (p)->lpVtbl->SetNameID(p,a) +#define IA3dMaterial_GetNameID(p,a,b) (p)->lpVtbl->GetNameID(p,a,b) +#define IA3dMaterial_SelectPreset(p,a) (p)->lpVtbl->SelectPreset(p,a) +#define IA3dMaterial_GetClosestPreset(p,a) (p)->lpVtbl->GetClosestPreset(p,a) +#define IA3dMaterial_SetReflectance(p,a,b) (p)->lpVtbl->SetReflectance(p,a,b) +#define IA3dMaterial_GetReflectance(p,a,b) (p)->lpVtbl->GetReflectance(p,a,b) +#define IA3dMaterial_SetTransmittance(p,a,b) (p)->lpVtbl->SetTransmittance(p,a,b) +#define IA3dMaterial_GetTransmittance(p,a,b) (p)->lpVtbl->GetTransmittance(p,a,b) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IA3dMaterial_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3dMaterial_AddRef(p) (p)->AddRef() +#define IA3dMaterial_Release(p) (p)->Release() +#define IA3dMaterial_Load(p,a) (p)->Load(a) +#define IA3dMaterial_Save(p,a) (p)->Save(a) +#define IA3dMaterial_UnSerialize(p,a,b) (p)->UnSerialize(a,b) +#define IA3dMaterial_Serialize(p,a,b) (p)->Serialize(a,b) +#define IA3dMaterial_Duplicate(p,a) (p)->Duplicate(a) +#define IA3dMaterial_SetNameID(p,a) (p)->SetNameID(a) +#define IA3dMaterial_GetNameID(p,a,b) (p)->GetNameID(a,b) +#define IA3dMaterial_SelectPreset(p,a) (p)->SelectPreset(a) +#define IA3dMaterial_GetClosestPreset(p,a) (p)->GetClosestPreset(a) +#define IA3dMaterial_SetReflectance(p,a,b) (p)->SetReflectance(a,b) +#define IA3dMaterial_GetReflectance(p,a,b) (p)->GetReflectance(a,b) +#define IA3dMaterial_SetTransmittance(p,a,b) (p)->SetTransmittance(a,b) +#define IA3dMaterial_GetTransmittance(p,a,b) (p)->GetTransmittance(a,b) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +//=================================================================== +// Error Codes +// +// Use macros A3DERROR_CODE(code) for errors and A3DSUCCESS_CODE(code) +// for success codes or predefined universal COM success and failure +// code. +// +// All success codes should be mutally exclusive to all other success other. +// +// All error codes should be mutually exclusive to all other error codes. +// +//=================================================================== + +#define A3DERROR_CODE(code) MAKE_HRESULT(1,FACILITY_ITF, code) +#define A3DSUCCESS_CODE(code) MAKE_HRESULT(0,FACILITY_ITF, code) + +#define A3DERROR_MEMORY_ALLOCATION A3DERROR_CODE(1) +#define A3DERROR_FAILED_CREATE_PRIMARY_BUFFER A3DERROR_CODE(2) +#define A3DERROR_FAILED_CREATE_SECONDARY_BUFFER A3DERROR_CODE(3) +#define A3DERROR_FAILED_INIT_A3D_DRIVER A3DERROR_CODE(4) +#define A3DERROR_FAILED_QUERY_DIRECTSOUND A3DERROR_CODE(5) +#define A3DERROR_FAILED_QUERY_A3D3 A3DERROR_CODE(6) +#define A3DERROR_FAILED_INIT_A3D3 A3DERROR_CODE(7) +#define A3DERROR_FAILED_QUERY_A3D2 A3DERROR_CODE(8) +#define A3DERROR_FAILED_FILE_OPEN A3DERROR_CODE(9) +#define A3DERROR_FAILED_CREATE_SOUNDBUFFER A3DERROR_CODE(10) +#define A3DERROR_FAILED_QUERY_3DINTERFACE A3DERROR_CODE(11) +#define A3DERROR_FAILED_LOCK_BUFFER A3DERROR_CODE(12) +#define A3DERROR_FAILED_UNLOCK_BUFFER A3DERROR_CODE(13) +#define A3DERROR_UNRECOGNIZED_FORMAT A3DERROR_CODE(14) +#define A3DERROR_NO_WAVE_DATA A3DERROR_CODE(15) +#define A3DERROR_UNKNOWN_PLAYMODE A3DERROR_CODE(16) +#define A3DERROR_FAILED_PLAY A3DERROR_CODE(17) +#define A3DERROR_FAILED_STOP A3DERROR_CODE(18) +#define A3DERROR_NEEDS_FORMAT_INFORMATION A3DERROR_CODE(19) +#define A3DERROR_FAILED_ALLOCATE_WAVEDATA A3DERROR_CODE(20) +#define A3DERROR_NOT_VALID_SOURCE A3DERROR_CODE(21) +#define A3DERROR_FAILED_DUPLICATION A3DERROR_CODE(22) +#define A3DERROR_FAILED_INIT A3DERROR_CODE(23) +#define A3DERROR_FAILED_SETCOOPERATIVE_LEVEL A3DERROR_CODE(24) +#define A3DERROR_FAILED_INIT_QUERIED_INTERFACE A3DERROR_CODE(25) +#define A3DERROR_GEOMETRY_INPUT_OUTSIDE_BEGIN_END_BLOCK A3DERROR_CODE(26) +#define A3DERROR_INVALID_NORMAL A3DERROR_CODE(27) +#define A3DERROR_END_BEFORE_VALID_BEGIN_BLOCK A3DERROR_CODE(28) +#define A3DERROR_INVALID_BEGIN_MODE A3DERROR_CODE(29) +#define A3DERROR_INVALID_ARGUMENT A3DERROR_CODE(30) +#define A3DERROR_INVALID_INDEX A3DERROR_CODE(31) +#define A3DERROR_INVALID_VERTEX_INDEX A3DERROR_CODE(32) +#define A3DERROR_INVALID_PRIMITIVE_INDEX A3DERROR_CODE(33) +#define A3DERROR_MIXING_2D_AND_3D_MODES A3DERROR_CODE(34) +#define A3DERROR_2DWALL_REQUIRES_EXACTLY_ONE_LINE A3DERROR_CODE(35) +#define A3DERROR_NO_PRIMITIVES_DEFINED A3DERROR_CODE(36) +#define A3DERROR_PRIMITIVES_NON_PLANAR A3DERROR_CODE(37) +#define A3DERROR_PRIMITIVES_OVERLAPPING A3DERROR_CODE(38) +#define A3DERROR_PRIMITIVES_NOT_ADJACENT A3DERROR_CODE(39) +#define A3DERROR_OBJECT_NOT_FOUND A3DERROR_CODE(40) +#define A3DERROR_ROOM_HAS_NO_SHELL_WALLS A3DERROR_CODE(41) +#define A3DERROR_WALLS_DO_NOT_ENCLOSE_ROOM A3DERROR_CODE(42) +#define A3DERROR_INVALID_WALL A3DERROR_CODE(43) +#define A3DERROR_ROOM_HAS_LESS_THAN_4SHELL_WALLS A3DERROR_CODE(44) +#define A3DERROR_ROOM_HAS_LESS_THAN_3UNIQUE_NORMALS A3DERROR_CODE(45) +#define A3DERROR_INTERSECTING_WALL_EDGES A3DERROR_CODE(46) +#define A3DERROR_INVALID_ROOM A3DERROR_CODE(47) +#define A3DERROR_SCENE_HAS_ROOMS_INSIDE_ANOTHER_ROOMS A3DERROR_CODE(48) +#define A3DERROR_SCENE_HAS_OVERLAPPING_STATIC_ROOMS A3DERROR_CODE(49) +#define A3DERROR_DYNAMIC_OBJ_UNSUPPORTED A3DERROR_CODE(50) +#define A3DERROR_DIR_AND_UP_VECTORS_NOT_PERPENDICULAR A3DERROR_CODE(51) +#define A3DERROR_INVALID_ROOM_INDEX A3DERROR_CODE(52) +#define A3DERROR_INVALID_WALL_INDEX A3DERROR_CODE(53) +#define A3DERROR_SCENE_INVALID A3DERROR_CODE(54) +#define A3DERROR_UNIMPLEMENTED_FUNCTION A3DERROR_CODE(55) +#define A3DERROR_NO_ROOMS_IN_SCENE A3DERROR_CODE(56) +#define A3DERROR_2D_GEOMETRY_UNIMPLEMENTED A3DERROR_CODE(57) +#define A3DERROR_OPENING_NOT_WALL_COPLANAR A3DERROR_CODE(58) +#define A3DERROR_OPENING_NOT_VALID A3DERROR_CODE(59) +#define A3DERROR_INVALID_OPENING_INDEX A3DERROR_CODE(60) +#define A3DERROR_FEATURE_NOT_REQUESTED A3DERROR_CODE(61) +#define A3DERROR_FEATURE_NOT_SUPPORTED A3DERROR_CODE(62) +#define A3DERROR_FUNCTION_NOT_VALID_BEFORE_INIT A3DERROR_CODE(63) +#define A3DERROR_INVALID_NUMBER_OF_CHANNELS A3DERROR_CODE(64) +#define A3DERROR_SOURCE_IN_NATIVE_MODE A3DERROR_CODE(65) +#define A3DERROR_SOURCE_IN_A3D_MODE A3DERROR_CODE(66) +#define A3DERROR_BBOX_CANNOT_ENABLE_AFTER_BEGIN_LIST_CALL A3DERROR_CODE(67) +#define A3DERROR_CANNOT_CHANGE_FORMAT_FOR_ALLOCATED_BUFFER A3DERROR_CODE(68) +#define A3DERROR_FAILED_QUERY_DIRECTSOUNDNOTIFY A3DERROR_CODE(69) +#define A3DERROR_DIRECTSOUNDNOTIFY_FAILED A3DERROR_CODE(70) +#define A3DERROR_RESOURCE_MANAGER_ALWAYS_ON A3DERROR_CODE(71) +#define A3DERROR_CLOSED_LIST_CANNOT_BE_CHANGED A3DERROR_CODE(72) +#define A3DERROR_END_CALLED_BEFORE_BEGIN A3DERROR_CODE(73) +#define A3DERROR_UNMANAGED_BUFFER A3DERROR_CODE(74) +#define A3DERROR_COORD_SYSTEM_CAN_ONLY_BE_SET_ONCE A3DERROR_CODE(75) + +#ifdef __cplusplus +}; +#endif + +#endif // #ifndef _IA3DAPI_H_ + diff --git a/dd_sndlib/ssl_lib.cpp b/dd_sndlib/ssl_lib.cpp new file mode 100644 index 000000000..7f035fbbd --- /dev/null +++ b/dd_sndlib/ssl_lib.cpp @@ -0,0 +1,67 @@ +/* + * $Source: $ + * $Revision: 3 $ + * $Author: Chris $ + * $Date: 10/08/99 4:28p $ + * + * This takes care of any code not exclusive to Win32 Ds3dlib.cpp but part of library + * + * $Log: /DescentIII/Main/dd_sndlib/ssl_lib.cpp $ + * + * 3 10/08/99 4:28p Chris + * Added the forcefield and glass breaking override options + * + * 2 4/06/99 8:29p Samir + * added error check system. + * + * 1 4/06/99 8:16p Samir + * Initial rev. + * + */ + +#include "ssl_lib.h" +#include "pserror.h" +#include "pstring.h" + +#include + +llsSystem::llsSystem() +{ + m_lib_error_code = SSL_OK; + m_geometry = NULL; +} + + +void llsSystem::CheckForErrors() +{ +// if a fatal error occurred, quit and display an error +// non fatal errors should be put inside a logfile, or just mprinted out. + + strcpy(m_error_text, "Internal Sound Error: "); + m_lib_error_code = SSL_OK; +} + + +void llsSystem::ErrorText(char *fmt, ...) +{ + va_list arglist; + char buf[128]; + int len,slen; + + slen = strlen(m_error_text); + + if ((slen+2) > sizeof(m_error_text)) { + return; + } + + strcat(m_error_text, "\n"); + va_start(arglist,fmt); + len = Pvsprintf(buf,128,fmt,arglist); + va_end(arglist); + if (len < 0) return; + + if ((slen+strlen(buf)+1) > sizeof(m_error_text)) { + return; + } + strcat(m_error_text, buf); +} \ No newline at end of file diff --git a/dd_sndlib/vmanpset.h b/dd_sndlib/vmanpset.h new file mode 100644 index 000000000..4c314701c --- /dev/null +++ b/dd_sndlib/vmanpset.h @@ -0,0 +1,66 @@ +/*****************************************************************************/ +/* */ +/* */ +/* Abstract: */ +/* */ +/* DirectSound3D Voice Manager property set definitions. */ +/* April 24, 1998 */ +/*****************************************************************************/ + + +#ifndef _VMANPSET_H_ +#define _VMANPSET_H_ + + +/******************************************************************************/ +/* */ +/* G l o b a l t y p e d e f s */ +/* */ +/******************************************************************************/ + + +typedef enum +{ + DSPROPERTY_VMANAGER_MODE = 0, + DSPROPERTY_VMANAGER_PRIORITY, + DSPROPERTY_VMANAGER_STATE +} DSPROPERTY_VMANAGER; + + +typedef enum +{ + DSPROPERTY_VMANAGER_MODE_DEFAULT = 0, + DSPROPERTY_VMANAGER_MODE_AUTO, + DSPROPERTY_VMANAGER_MODE_REPORT, + DSPROPERTY_VMANAGER_MODE_USER +} VmMode; + + +typedef enum +{ + DSPROPERTY_VMANAGER_STATE_PLAYING3DHW = 0, + DSPROPERTY_VMANAGER_STATE_SILENT, + DSPROPERTY_VMANAGER_STATE_BUMPED, + DSPROPERTY_VMANAGER_STATE_PLAYFAILED +} VmState; + + + + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +// {62A69BAE-DF9D-11d1-99A6-00C04FC99D46} +DEFINE_GUID(DSPROPSETID_VoiceManager, +0x62a69bae, 0xdf9d, 0x11d1, 0x99, 0xa6, 0x0, 0xc0, 0x4f, 0xc9, 0x9d, 0x46); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dd_vidwin32/CMakeLists.txt b/dd_vidwin32/CMakeLists.txt new file mode 100644 index 000000000..855f3afa6 --- /dev/null +++ b/dd_vidwin32/CMakeLists.txt @@ -0,0 +1,8 @@ +SET (HEADERS + ddvidlib.h) +SET (CPPS + video_win32.cpp + vidWin32FS.cpp + vidWin32Win.cpp) + +ADD_LIBRARY(dd_vidwin32 STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/dd_vidwin32/ddvidlib.h b/dd_vidwin32/ddvidlib.h new file mode 100644 index 000000000..dc8b08bdf --- /dev/null +++ b/dd_vidwin32/ddvidlib.h @@ -0,0 +1,121 @@ +/* + * $Logfile: /DescentIII/Main/ddvid_win32/ddvidlib.h $ + * $Revision: 4 $ + * $Date: 3/06/98 2:22p $ + * $Author: Samir $ + * + * Video library internal header + * + * $Log: /DescentIII/Main/ddvid_win32/ddvidlib.h $ + * + * 4 3/06/98 2:22p Samir + * Added fullscreen windowed version. + * + * 3 2/03/98 3:12p Samir + * Enable access to directdraw object by DDAccess libraries. + * + * 2 12/30/97 1:54p Samir + * Upped max modes. + * + * 1 12/23/97 5:46p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef DDVIDLIB_H +#define DDVIDLIB_H + +#include "ddvid.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#define VM_MAX_MODES 96 + +const int VID_GDIF_SUBSYSTEM = 0, // GDI fullscreen subsystem + VID_GDI_SUBSYSTEM = 1, // GDI subsystem + VID_GDIX_SUBSYSTEM = 2, // GDIX subsystem (GDI+DirectX) + VID_DX_SUBSYSTEM = 3; // DIRECTX subsystem + + +class oeWin32Application; + +typedef struct tDDVideoInfo +{ + oeWin32Application *app; + HWND hWnd; + HWND hVidWnd; + int subsystem; + + LPDIRECTDRAW lpDD; + LPDIRECTDRAWSURFACE lpDDSFront; + LPDIRECTDRAWSURFACE lpDDSBack; + DDSURFACEDESC DDModes[VM_MAX_MODES]; + int nDDModes; + int curmode; + char *surf_data; + + struct + { + HBITMAP hBackBmp; + HDC hBackDC; + char *data; + int pitch; + int w, h, color_depth; + int ndevmodes; + int olddevmode; + int curdevmode; + } + gdi; +} +tDDVideoInfo; + +// driver info. +extern tDDVideoInfo DDVideo_info; + +// inits fullscreen system +bool ddvidfs_Init(); + +// closes fullscreen system +void ddvidfs_Close(); + +// uses direct draw. if paged, allows frame buffer access. +bool ddvidfs_SetVideoMode(int w, int h, int color_depth, bool paged); + +// closes video mode for fs +void ddvidfs_CloseVideo(); + +// retrieves screen information for fullscreen version +void ddvidfs_GetVideoProperties(int *w, int *h, int *color_depth); + +// flips screen if there's a back buffer +void ddvidfs_VideoFlip(); + +// returns the directdraw object +uint ddvidfs_GetDirectDrawObject(); + +// inits windowed system +bool ddvidwin_Init(); + +// closes windowed system +void ddvidwin_Close(); + +// creates an offscreen back bitmap if needed. otherwise doesn't do a thing really. +bool ddvidwin_SetVideoMode(int w, int h, int color_depth, bool paged, bool reschange=false); + +// closes video mode for fs +void ddvidwin_CloseVideo(); + +// retrieves screen information for windowed version +void ddvidwin_GetVideoProperties(int *w, int *h, int *color_depth); + +// flips screen if there's a back buffer +void ddvidwin_VideoFlip(); + + + +#endif + diff --git a/dd_vidwin32/vidWin32FS.cpp b/dd_vidwin32/vidWin32FS.cpp new file mode 100644 index 000000000..88c01ad63 --- /dev/null +++ b/dd_vidwin32/vidWin32FS.cpp @@ -0,0 +1,231 @@ +/* + * $Logfile: /DescentIII/Main/ddvid_win32/vidWin32FS.cpp $ + * $Revision: 7 $ + * $Date: 7/15/98 5:56p $ + * $Author: Samir $ + * + * Fullscreen version of Win32 library + * + * $Log: /DescentIII/Main/ddvid_win32/vidWin32FS.cpp $ + * + * 7 7/15/98 5:56p Samir + * commented out GDIX subsystem. + * + * 6 6/04/98 7:04p Samir + * assert res-switch to be at least 16bpp. + * + * 5 3/04/98 5:01p Samir + * May have fixed problem with restoring old display modes. + * + * 4 2/03/98 3:12p Samir + * Enable access to directdraw object by DDAccess libraries. + * + * 3 12/30/97 3:35p Samir + * Added dummy mode to help in mode selection. + * + * 2 12/30/97 2:31p Jason + * Fixed mode finder. (samir) + * + * 1 12/23/97 5:46p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "ddvidlib.h" +#include "pserror.h" + +// DirectDraw Display mode enumeration callback +HRESULT WINAPI DDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext); + + +// inits fullscreen system +bool ddvidfs_Init() +{ + HRESULT hres; + + hres = DirectDrawCreate(NULL, &DDVideo_info.lpDD, NULL); + + if (hres != DD_OK) { + Error("Failure to initialize DirectDraw driver. (%d)", LOWORD(hres)); + } + + + hres = DDVideo_info.lpDD->SetCooperativeLevel(DDVideo_info.hWnd, + DDSCL_ALLOWREBOOT | + DDSCL_EXCLUSIVE | + DDSCL_FULLSCREEN); + if (hres != DD_OK) { + Error("Failed to set access mode for DirectDraw driver. (%d)", LOWORD(hres)); + } + +// dummy mode + DDVideo_info.DDModes[DDVideo_info.nDDModes].ddpfPixelFormat.dwRGBBitCount = 0; + DDVideo_info.DDModes[DDVideo_info.nDDModes].dwWidth = 0; + DDVideo_info.DDModes[DDVideo_info.nDDModes].dwHeight = 0; + DDVideo_info.nDDModes++; + +// enumerate all display modes. + if (DDVideo_info.lpDD->EnumDisplayModes(0,NULL,NULL,DDEnumModesCallback) != DD_OK) { + mprintf((0, "DDVID error: Error enumerating display modes.\n")); + return false; + } + + mprintf((0, "Video fullscreen system initialized.\n")); + + return true; +} + + +// closes fullscreen system +void ddvidfs_Close() +{ + if (DDVideo_info.lpDDSFront) + DDVideo_info.lpDDSFront->Release(); + + if (DDVideo_info.lpDD) { + DDVideo_info.lpDD->RestoreDisplayMode(); + DDVideo_info.lpDD->SetCooperativeLevel(NULL, DDSCL_NORMAL); + DDVideo_info.lpDD->Release(); + DDVideo_info.lpDD = NULL; + } + + DDVideo_info.lpDDSFront = NULL; + DDVideo_info.lpDDSBack = NULL; + DDVideo_info.hWnd = NULL; + DDVideo_info.nDDModes = 0; +} + + +// uses direct draw. if paged, allows frame buffer access. +bool ddvidfs_SetVideoMode(int w, int h, int color_depth, bool paged) +{ + HRESULT hres; + DDSCAPS ddscaps; + DDSURFACEDESC ddsd; + int i, mode; + bool found_mode; + + mode = 0; + found_mode = false; + +// find closest match for video mode. + for (i = 0; i < DDVideo_info.nDDModes; i++) + { + if (color_depth == (int)DDVideo_info.DDModes[i].ddpfPixelFormat.dwRGBBitCount) { + if (DDVideo_info.DDModes[i].dwWidth >= (DWORD)w && DDVideo_info.DDModes[mode].dwWidth < (DWORD)w) + if (DDVideo_info.DDModes[i].dwHeight >=(DWORD)h && DDVideo_info.DDModes[mode].dwHeight < (DWORD)h) { + mode = i; + found_mode = true; + } + } + } + + if (!found_mode) { + // we couldn't find a mode, error! + return false; + } + + if (DDVideo_info.curmode != mode) { + + // mode should contain the video mode. + hres = DDVideo_info.lpDD->SetDisplayMode(DDVideo_info.DDModes[mode].dwWidth, + DDVideo_info.DDModes[mode].dwHeight, + DDVideo_info.DDModes[mode].ddpfPixelFormat.dwRGBBitCount); + ASSERT(DDVideo_info.DDModes[mode].ddpfPixelFormat.dwRGBBitCount >= BPP_16); + if (hres != DD_OK) + Error("Unable to set DirectDraw display mode. (%d)", LOWORD(hres)); + } + + DDVideo_info.curmode = mode; + +// if not paged, then this is a single paged system (no lfb access, good for opengl) + if (!paged) + return true; + +// now create surface, dependant on whether an extra page was requested + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + + if (paged) { + ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + ddsd.dwBackBufferCount = 1; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + } + else { + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + } + + hres = DDVideo_info.lpDD->CreateSurface(&ddsd, &DDVideo_info.lpDDSFront, NULL); + if (hres != DD_OK) + Error("Unable to capture DirectDraw display surface. (%d)", LOWORD(hres)); + + + if (paged) { + ddscaps.dwCaps = DDSCAPS_BACKBUFFER; + + hres = DDVideo_info.lpDDSFront->GetAttachedSurface(&ddscaps, &DDVideo_info.lpDDSBack); + if (hres != DD_OK) { + mprintf((0,"Unable to capture DirectDraw display back surface (%d)", LOWORD(hres))); + return false; + } + } + else { + DDVideo_info.lpDDSBack = NULL; + } + + return true; +} + + +// closes video mode for fs +void ddvidfs_CloseVideo() +{ +// uninitialize old screen + if (DDVideo_info.lpDDSFront) { + DDVideo_info.lpDDSFront->Release(); + DDVideo_info.lpDDSFront = NULL; + DDVideo_info.lpDDSBack = NULL; + } +} + + +// retrieves screen information for fullscreen version +void ddvidfs_GetVideoProperties(int *w, int *h, int *color_depth) +{ + ASSERT(DDVideo_info.curmode > -1); + + *w = DDVideo_info.DDModes[DDVideo_info.curmode].dwWidth; + *h = DDVideo_info.DDModes[DDVideo_info.curmode].dwHeight; + *color_depth = (int)DDVideo_info.DDModes[DDVideo_info.curmode].ddpfPixelFormat.dwRGBBitCount; +} + + +// flips screen if there's a back buffer +void ddvidfs_VideoFlip() +{ + if (DDVideo_info.lpDDSBack) + DDVideo_info.lpDDSFront->Flip(NULL, DDFLIP_WAIT); +} + + +// returns the directdraw object +uint ddvidfs_GetDirectDrawObject() +{ + return (uint)DDVideo_info.lpDD; +} + + +// DirectDraw Display mode enumeration callback +HRESULT WINAPI DDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext) +{ + if (DDVideo_info.nDDModes < VM_MAX_MODES) { + memcpy(&DDVideo_info.DDModes[DDVideo_info.nDDModes], lpDDSurfaceDesc, sizeof(DDSURFACEDESC)); + DDVideo_info.nDDModes++; + } + + return DDENUMRET_OK; +} + + diff --git a/dd_vidwin32/vidWin32Win.cpp b/dd_vidwin32/vidWin32Win.cpp new file mode 100644 index 000000000..b452e7ad4 --- /dev/null +++ b/dd_vidwin32/vidWin32Win.cpp @@ -0,0 +1,270 @@ +/* + * $Logfile: /DescentIII/Main/ddvid_win32/vidWin32Win.cpp $ + * $Revision: 5 $ + * $Date: 10/02/98 11:15a $ + * $Author: Jeff $ + * + * Windowed version of video library. + * + * $Log: /DescentIII/Main/ddvid_win32/vidWin32Win.cpp $ + * + * 5 10/02/98 11:15a Jeff + * added HBITMAP, HFONT and HBRUSH type casts where needed to satisfy the + * compiler + * + * 4 6/04/98 7:04p Samir + * assert res-switch to be at least 16bpp. + * + * 3 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 2 3/06/98 2:22p Samir + * Added fullscreen windowed version. + * + * 1 12/23/97 5:46p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "ddvidlib.h" +#include "pserror.h" +#include "Application.h" + +////////////////////////////////////////////////////////////////////////////// +// Variables + +typedef struct tDIBHeader // used to create and manipulate DIBs (a 16/32BPP surface only) +{ + BITMAPINFOHEADER bmi; + DWORD red_mask; + DWORD green_mask; + DWORD blue_mask; +} +tDIBHeader; + + + +////////////////////////////////////////////////////////////////////////////// +// Functions + +// inits fullscreen system +bool ddvidwin_Init() +{ +// grab all device modes for the current display device. + unsigned i = 0; + DEVMODE devmode; + + while(EnumDisplaySettings(NULL, i, &devmode)) + { + HDC hdc = CreateCompatibleDC(NULL); + + if (devmode.dmBitsPerPel==(uint)GetDeviceCaps(hdc, BITSPIXEL) && + devmode.dmPelsWidth==(uint)GetSystemMetrics(SM_CXSCREEN) && + devmode.dmPelsHeight==(uint)GetSystemMetrics(SM_CYSCREEN)) { + if (DDVideo_info.app->NT() && devmode.dmDisplayFrequency == (uint)GetDeviceCaps(hdc,VREFRESH)) + DDVideo_info.gdi.olddevmode = i; + else if (!DDVideo_info.app->NT()) + DDVideo_info.gdi.olddevmode = i; + } + + DeleteDC(hdc); + i++; + } + DDVideo_info.gdi.ndevmodes = i; + + DDVideo_info.gdi.curdevmode = DDVideo_info.gdi.olddevmode; + + return true; +} + + +// closes windowed system +void ddvidwin_Close() +{ + DEVMODE devmode; + + if (DDVideo_info.gdi.hBackDC) { + DeleteDC(DDVideo_info.gdi.hBackDC); + DDVideo_info.gdi.hBackDC = NULL; + } + + if (DDVideo_info.gdi.hBackBmp) { + DeleteObject(DDVideo_info.gdi.hBackBmp); + DDVideo_info.gdi.hBackBmp = NULL; + } + +// if the current display mode is different than the default one, then restore default display mode + if (DDVideo_info.gdi.olddevmode != DDVideo_info.gdi.curdevmode) { + LONG lres; + + EnumDisplaySettings(NULL, DDVideo_info.gdi.olddevmode, &devmode); + + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY; + + lres = ChangeDisplaySettings(&devmode, 0); + ASSERT(lres == DISP_CHANGE_SUCCESSFUL); + } +} + + +// creates an offscreen back bitmap if needed. otherwise doesn't do a thing really +bool ddvidwin_SetVideoMode(int w, int h, int color_depth, bool paged, bool reschange) +{ + HDC hdc; + int i; + + DDVideo_info.hVidWnd = DDVideo_info.hWnd; + +// if resolution change, then do it. + if (reschange) { + int mode = 0; + bool found_mode = false; + DEVMODE devmode, curdevmode; + + EnumDisplaySettings(NULL, mode, &curdevmode); + curdevmode.dmPelsWidth = 0; + curdevmode.dmPelsHeight = 0; + curdevmode.dmBitsPerPel = 0; + + // check for display mode query match. + for (i = 0; i < DDVideo_info.gdi.ndevmodes; i++) + { + EnumDisplaySettings(NULL, i, &devmode); + + if (color_depth == (int)devmode.dmBitsPerPel) { + if ((int)devmode.dmPelsWidth >= w && (int)curdevmode.dmPelsWidth < w) + if ((int)devmode.dmPelsHeight >= h && (int)curdevmode.dmPelsHeight < h) { + mode = i; + found_mode = true; + EnumDisplaySettings(NULL, mode, &curdevmode); + } + } + } + + if (!found_mode) { + // we couldn't find a mode, error! + Error("Unable to set Win32 device display mode (%dx%dx%d).", w, h, color_depth); + } + + if (DDVideo_info.gdi.curdevmode != mode) { + EnumDisplaySettings(NULL, mode, &devmode); + + devmode.dmFields = + DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | + DM_DISPLAYFREQUENCY; + + ASSERT(devmode.dmBitsPerPel >= BPP_16); + LONG lres = ChangeDisplaySettings(&devmode, 0); + ASSERT(lres == DISP_CHANGE_SUCCESSFUL); + DDVideo_info.gdi.curdevmode = mode; + } + } + +// create a DC for blting and back buffer creation. + hdc = CreateCompatibleDC(NULL); + if (hdc == NULL) { + Error("Unable to create offscreen device context (%x).", GetLastError()); + return false; + } + + DDVideo_info.gdi.hBackDC = hdc; + DDVideo_info.gdi.w = w; + DDVideo_info.gdi.h = h; + DDVideo_info.gdi.color_depth = color_depth; + +// create offscreen back buffer if paged mode is requested. + if (paged) { + int nw = w; + if (nw % 4) nw = ((w/4)*4)+4; + tDIBHeader header; + + if (color_depth == BPP_16) { + header.bmi.biSize = sizeof(BITMAPINFOHEADER); + header.bmi.biWidth = nw; + header.bmi.biHeight = -h; // Always a top down bitmap!! + header.bmi.biPlanes = 1; + header.bmi.biBitCount = BPP_16; + header.bmi.biCompression = BI_BITFIELDS; + header.bmi.biSizeImage = 0; + header.bmi.biXPelsPerMeter = 0; + header.bmi.biYPelsPerMeter = 0; + header.bmi.biClrUsed = 0; + header.bmi.biClrImportant = 0; + + // setup RGB bit masks + header.red_mask = 0x7c00; + header.green_mask = 0x03e0; + header.blue_mask = 0x001f; + DDVideo_info.gdi.pitch = nw * 2; + } + + DDVideo_info.gdi.hBackBmp = CreateDIBSection(DDVideo_info.gdi.hBackDC, (BITMAPINFO *)&header,DIB_RGB_COLORS, + (void **)&DDVideo_info.gdi.data,NULL,0); + + if (!DDVideo_info.gdi.hBackBmp) { + Error("CreateDIBSection failed (%x).", GetLastError()); + } + } + + return true; +} + + +// closes video mode for fs +void ddvidwin_CloseVideo() +{ +// delete DC if it exists. + if (DDVideo_info.gdi.hBackDC) { + DeleteDC(DDVideo_info.gdi.hBackDC); + DDVideo_info.gdi.hBackDC = NULL; + } + +// destroy old back bitmap + if (DDVideo_info.gdi.hBackBmp) { + DeleteObject(DDVideo_info.gdi.hBackBmp); + DDVideo_info.gdi.hBackBmp = NULL; + } +} + + +// retrieves screen information for windowed version +void ddvidwin_GetVideoProperties(int *w, int *h, int *color_depth) +{ + *w = DDVideo_info.gdi.w; + *h = DDVideo_info.gdi.h; + *color_depth = DDVideo_info.gdi.color_depth; +} + + +// flips screen if there's a back buffer +void ddvidwin_VideoFlip() +{ + HBITMAP old_bmp, h_sbm; + HDC hdc_dest; + RECT rect; + int dw, dh; + + ASSERT(DDVideo_info.hVidWnd != NULL); + +// don't give an error if there's no backbuffer. + if (!DDVideo_info.gdi.hBackBmp) + return; + + h_sbm = DDVideo_info.gdi.hBackBmp; + + hdc_dest = GetDC(DDVideo_info.hVidWnd); + GetClientRect(DDVideo_info.hVidWnd, &rect); + + dw = rect.right - rect.left; + dh = rect.bottom - rect.top; + + old_bmp = (HBITMAP)SelectObject(DDVideo_info.gdi.hBackDC, h_sbm); + + BOOL bltres = BitBlt(hdc_dest, 0, 0, dw, dh, DDVideo_info.gdi.hBackDC, 0,0,SRCCOPY); + + SelectObject(DDVideo_info.gdi.hBackDC, old_bmp); + + ReleaseDC(DDVideo_info.hVidWnd, hdc_dest); +} + diff --git a/dd_vidwin32/video_win32.cpp b/dd_vidwin32/video_win32.cpp new file mode 100644 index 000000000..5f2a4d8ff --- /dev/null +++ b/dd_vidwin32/video_win32.cpp @@ -0,0 +1,351 @@ +/* + * $Logfile: /DescentIII/Main/ddvid_win32/video_win32.cpp $ + * $Revision: 6 $ + * $Date: 8/28/98 4:20p $ + * $Author: Jeff $ + * + * Video library. + * + * $Log: /DescentIII/Main/ddvid_win32/video_win32.cpp $ + * + * 6 8/28/98 4:20p Jeff + * fixed cinemtaics so they initialize ddraw to play the movie + * + * 5 7/15/98 5:56p Samir + * commented out GDIX subsystem. + * + * 4 3/06/98 2:22p Samir + * Added fullscreen windowed version. + * + * 3 2/03/98 3:12p Samir + * Enable access to directdraw object by DDAccess libraries. + * + * 2 12/23/97 6:17p Samir + * Added DDGR subsystem interface. + * + * 1 12/22/97 12:45p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "DDAccess.h" // DD Access enabled + +#include "ddvidlib.h" +#include "pserror.h" +#include "Application.h" + +#include + + + +////////////////////////////////////////////////////////////////////////////// +// Variables + +tDDVideoInfo DDVideo_info; +static bool DDVideo_init = false; + +char *DDVID_subsystem_names[] = { + "GDIF", // Win32 GDI Fullscreen + "GDI", // Standard Win32 GDI DIBs + "GDIX", // GDI and DirectDraw for mode settings. + "DX", // Direct X! + NULL +}; + +int DDVID_subsystems[] = { + VID_GDIF_SUBSYSTEM, + VID_GDI_SUBSYSTEM, + VID_GDIX_SUBSYSTEM, + VID_DX_SUBSYSTEM, + -1 +}; + + +////////////////////////////////////////////////////////////////////////////// +// Prototypes + +HRESULT WINAPI DDEnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext); + + + +////////////////////////////////////////////////////////////////////////////// +// Functions + +// called first to allow fullscreen video access +bool ddvid_Init(oeApplication *app, char *driver) +{ + int subsys_id; + +// preinitialize system. + if (!DDVideo_init) { + DDVideo_info.lpDD = NULL; + DDVideo_info.lpDDSFront = NULL; + DDVideo_info.lpDDSBack = NULL; + DDVideo_info.hWnd = NULL; + DDVideo_info.nDDModes = 0; + DDVideo_info.curmode = -1; + DDVideo_info.surf_data = NULL; + DDVideo_info.gdi.hBackBmp = NULL; + DDVideo_info.gdi.hBackDC = NULL; + DDVideo_info.gdi.data = NULL; + DDVideo_info.gdi.pitch = 0; + DDVideo_info.gdi.w = 0; + DDVideo_info.gdi.h = 0; + DDVideo_info.gdi.color_depth = 0; + + atexit(ddvid_Close); + } + else { + ddvid_Close(); + } + + DDVideo_init = true; + + DDVideo_info.app = (oeWin32Application *)app; + + // find subsystem id based off of subsystem requested. + for (subsys_id = 0; DDVID_subsystems[subsys_id] != -1; subsys_id++) + { + if (strcmp(DDVID_subsystem_names[subsys_id], driver) == 0) + break; + } + + if (DDVID_subsystems[subsys_id] == -1) + Error("Subsystem %s not found during startup.", driver); + + DDVideo_info.subsystem = DDVID_subsystems[subsys_id]; + DDVideo_info.hWnd = (HWND)(((oeWin32Application *)app)->m_hWnd); + +// initialize directdraw object. + switch (DDVideo_info.subsystem) + { + case VID_GDIF_SUBSYSTEM: + case VID_GDI_SUBSYSTEM: + return ddvidwin_Init(); + case VID_GDIX_SUBSYSTEM: + return true; + case VID_DX_SUBSYSTEM: + return ddvidfs_Init(); + default: + Int3(); + } + + return false; +} + + +// closes ddvid system manually. +void ddvid_Close() +{ + if (!DDVideo_init) + return; + + switch (DDVideo_info.subsystem) + { + case VID_GDIF_SUBSYSTEM: + case VID_GDI_SUBSYSTEM: + ddvidwin_Close(); + break; + case VID_GDIX_SUBSYSTEM: + break; + case VID_DX_SUBSYSTEM: + ddvidfs_Close(); + break; + default: + Int3(); + } + + DDVideo_init = false; +} + + +// sets the appropriate video mode. +bool ddvid_SetVideoMode(int w, int h, int color_depth, bool paged) +{ + ASSERT(DDVideo_init); + + switch (DDVideo_info.subsystem) + { + case VID_GDIF_SUBSYSTEM: + ddvidwin_CloseVideo(); + return ddvidwin_SetVideoMode(w,h,color_depth,paged, true); + + case VID_GDI_SUBSYSTEM: + ddvidwin_CloseVideo(); + return ddvidwin_SetVideoMode(w,h,color_depth,paged); + + case VID_GDIX_SUBSYSTEM: + ddvidwin_CloseVideo(); + ddvidfs_CloseVideo(); + return (ddvidfs_SetVideoMode(w,h,color_depth,false) && + ddvidwin_SetVideoMode(DDVideo_info.DDModes[DDVideo_info.curmode].dwWidth, + DDVideo_info.DDModes[DDVideo_info.curmode].dwHeight, + color_depth, paged)); + case VID_DX_SUBSYSTEM: + ddvidfs_CloseVideo(); + return ddvidfs_SetVideoMode(w,h,color_depth,paged); + default: + Int3(); + } + + return false; +} + + +// sets screen handle +void ddvid_SetVideoHandle(unsigned handle) +{ + DDVideo_info.hVidWnd = (HWND)handle; +} + + +// retrieves screen information +void ddvid_GetVideoProperties(int *w, int *h, int *color_depth) +{ + ASSERT(DDVideo_init); + + switch (DDVideo_info.subsystem) + { + case VID_GDIF_SUBSYSTEM: + case VID_GDI_SUBSYSTEM: + ddvidwin_GetVideoProperties(w,h,color_depth); + break; + case VID_GDIX_SUBSYSTEM: + ddvidfs_GetVideoProperties(w,h,color_depth); + break; + case VID_DX_SUBSYSTEM: + ddvidfs_GetVideoProperties(w,h,color_depth); + break; + default: + Int3(); + } +} + + +// retrieves screen aspect ratio. +float ddvid_GetAspectRatio() +{ + float aspect_ratio = (float)((3.0 * GetSystemMetrics(SM_CXSCREEN))/(4.0 * GetSystemMetrics(SM_CYSCREEN))); + return aspect_ratio; +} + + +// only available to DD_ACCESS libraries. +// dd_obj is the DIRECTDRAW OBJECT for the system. +// dds_obj is the DIRECTDRAWSURFACE OBJECT for the screen +void ddvid_GetVideoDDrawProps(uint *dd_obj, uint *dds_obj) +{ + switch (DDVideo_info.subsystem) + { + case VID_GDIX_SUBSYSTEM: + *dd_obj = NULL; + break; + case VID_DX_SUBSYSTEM: + *dd_obj = ddvidfs_GetDirectDrawObject(); + break; + } + + *dds_obj = NULL; +} + + +// flips screen if there's a back buffer +void ddvid_VideoFlip() +{ + switch (DDVideo_info.subsystem) + { + case VID_GDIF_SUBSYSTEM: + case VID_GDI_SUBSYSTEM: + ddvidwin_VideoFlip(); + break; + case VID_GDIX_SUBSYSTEM: + ddvidwin_VideoFlip(); + break; + case VID_DX_SUBSYSTEM: + ddvidfs_VideoFlip(); + break; + default: + Int3(); + } +} + + +// retreives frame buffer info for a video mode. +void ddvid_LockFrameBuffer(ubyte **data, int *pitch) +{ +// locks a direct draw paged surface or a paged window. unpaged buffers won't lock. + ASSERT(DDVideo_init); + + switch (DDVideo_info.subsystem) + { + case VID_GDIF_SUBSYSTEM: + case VID_GDI_SUBSYSTEM: + { + *data = (ubyte *)DDVideo_info.gdi.data; + *pitch = DDVideo_info.gdi.pitch; + } + break; + case VID_GDIX_SUBSYSTEM: + { + *data = (ubyte *)DDVideo_info.gdi.data; + *pitch = DDVideo_info.gdi.pitch; + } + break; + case VID_DX_SUBSYSTEM: + { + HRESULT hres; + DDSURFACEDESC ddsd; + + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + + if (DDVideo_info.lpDDSBack) { + hres = DDVideo_info.lpDDSBack->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + if (hres != DD_OK) { + Error("Unable to set lock DirectDraw surface(%d)\n", LOWORD(hres)); + } + *pitch = (int)ddsd.lPitch; + *data = (ubyte *)ddsd.lpSurface; + DDVideo_info.surf_data = (char *)(*data); + } + else { + *data = NULL; + *pitch = 0; + } + } + break; + default: + Int3(); + } +} + + +void ddvid_UnlockFrameBuffer() +{ + ASSERT(DDVideo_init); + + switch (DDVideo_info.subsystem) + { + case VID_GDIF_SUBSYSTEM: + case VID_GDI_SUBSYSTEM: + case VID_GDIX_SUBSYSTEM: + break; + case VID_DX_SUBSYSTEM: + { + HRESULT hres; + + if (DDVideo_info.lpDDSBack) { + hres = DDVideo_info.lpDDSBack->Unlock(DDVideo_info.surf_data); + if (hres != DD_OK) + Error("Unable to unlock DirectDraw surface(%d)\n", LOWORD(hres)); + DDVideo_info.surf_data = NULL; + } + } + break; + default: + Int3(); + } +} + + diff --git a/ddgr_mac/ddgr_mac.cpp b/ddgr_mac/ddgr_mac.cpp new file mode 100644 index 000000000..f7ec988d5 --- /dev/null +++ b/ddgr_mac/ddgr_mac.cpp @@ -0,0 +1,272 @@ +/* + * $Logfile: /Descent3/main/ddgr_mac/ddgr_mac.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:53 $ + * $Author: kevinb $ + * + * macintosh implementation of interface to ddgr library + * + * $Log: ddgr_mac.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:53 kevinb + * initial 1.5 import + * + * + * 6 5/19/97 2:51 PM Jeremy + * added default values to constructor + * + * 5 5/15/97 1:47 AM Jeremy + * changed mprintf's to be standard (with newline at end) + * + * 4 5/11/97 8:01 PM Jeremy + * implemented call to ddgr_os_surf_GetAspectRatio + * + * 3 5/9/97 7:13 PM Jeremy + * some bug fixes in initialization code + * + * 2 4/15/97 7:02 PM Jeremy + * initial implementation of initialization and closing of ddgr mac + * library. also added c version of scale bitmap16 + * + * 1 4/9/97 7:16 PM Jeremy + * initial check in + * + * $NoKeywords: $ + */ + +// --------------------------------------------------------------------------- +// ANSI Headers +// --------------------------------------------------------------------------- +#include +#include + +// --------------------------------------------------------------------------- +// Macintosh Headers +// --------------------------------------------------------------------------- +#include + +// --------------------------------------------------------------------------- +// Descent3 Headers +// --------------------------------------------------------------------------- +#include "pserror.h" +#include "gameos.h" + +#include "ddgr.h" +#include "ddgr_mac.h" +#include "macOSSurface.h" +#include "bitmap.h" + +// --------------------------------------------------------------------------- +// File Level Prototypes +// --------------------------------------------------------------------------- +// Clean up the ddgr library +void ddgr_Close(void); + +// --------------------------------------------------------------------------- +// File Level DataTypes +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// File Level Globals +// --------------------------------------------------------------------------- +#include "ddgr_mac.h" + +// --------------------------------------------------------------------------- +// Public Globals +// --------------------------------------------------------------------------- + + +// ---------------------------------------------------- +// This object stores the data for the graphics library +// on the mac. +// If we were really using good OOP, all of the +// functions in this file and this data libaray object +// would be encapulated together, Ah, c'est la vie. +// ---------------------------------------------------- +ddgr_mac_lib Mac_DDGR_Lib; + +// --------------------------------------------------------------------------- +// Mac data library implementations +// --------------------------------------------------------------------------- +ddgr_mac_lib::ddgr_mac_lib(void) +{ + m_initted = false; + m_windowed_mode = false; + m_color_key = 0; +} + +ddgr_mac_lib::~ddgr_mac_lib(void) +{ + ; +} + +void ddgr_mac_lib::SetWindowedMode(bool inWindowMode) +{ + m_windowed_mode = inWindowMode; +} + +bool ddgr_mac_lib::IsWindowed(void) +{ + return m_windowed_mode; +} + +bool ddgr_mac_lib::IsInitted(void) +{ + return m_initted; +} + +void ddgr_mac_lib::SetInitted(bool inInitState) +{ + m_initted = inInitState; +} + +/*void ddgr_mac_lib::SetDefaultColorKey(ddgr_color inColor) +{ + m_color_key = inColor; +} +*/ + +// ------------------------------------------------------------ +// ddgr_Init +// info->hwnd = window handle +// info->windowed = are screens windowed or fullscreen. +// info->debug = debug mode +// 0 = no debug +// 1 = full debug +// note; +// this function can be reused. This is, you can call this +// function as many times as you want. You can switch from +// windowed to full screen, debug to nodebug. +// ------------------------------------------------------------ +ddgr_error ddgr_Init(ddgr_init_info *info) +{ + ddgr_error err = DDGRERR_SUCCESS; + OSErr macErr = noErr; + static int firstTime = true; + + // If we're reinitializing the graphics system, then close the current graphic driver + if (Mac_DDGR_Lib.IsInitted()) + { + ddgr_Close(); + } + + if (firstTime) + { + // first time initialization! + atexit(ddgr_Close); + firstTime = false; + } + + // Inititializing mac os video graphics system + if (info->windowed) + { + Mac_DDGR_Lib.SetWindowedMode(true); + mprintf((0, "Attempting to initialize ddgr in windowed mode.\n")); + + mprintf((0, "Windowed mode currently not implemented.\n")); + err = DDGRERR_DRIVERINIT; + } + else + { + Mac_DDGR_Lib.SetWindowedMode(false); + mprintf((0, "Attempting to initialize ddgr in full screen mode.\n")); + + err = ddgr_os_surf_fullscreen_Init(info); + } + + // Success!! + if (!err) + { + Mac_DDGR_Lib.SetInitted(true); + mprintf((0, "DDGR Initialized\n")); + } + + return err; +} + +void ddgr_Close() +{ + ddgr_error err = DDGRERR_SUCCESS; + + if (Mac_DDGR_Lib.IsInitted()) + { + // Closing mac os video graphics system + if (Mac_DDGR_Lib.IsWindowed()) + { + mprintf((0, "Attempting to close ddgr in windowed mode.\n")); + mprintf((0, "Windowed mode currently not implemented.\n")); + } + else + { + mprintf((0, "Attempting to close ddgr in fullscreen mode.\n")); + ddgr_os_surf_fullscreen_Close(); + } + + Mac_DDGR_Lib.SetInitted(false); + mprintf((0, "DDGR is closed.\n")); + } + else + { + mprintf((0, "Attempted closing *uninitialized* Mac DDGR!\n")); + } +} + +// GetAspectRatio +// returns aspect ratio calculated in video screen initialization +float ddgr_GetAspectRatio() +{ + return ddgr_os_surf_GetAspectRatio(); +} + +// Set's the surface color key in ddgr_color format +void ddgr_SetSurfColorKey(ddgr_color color) +{ + Mac_DDGR_Lib.SetDefaultColorKey(color); +} + +// A C version of a scaled bitmap blitter +void ddgr_ScaleBitmap16 (ushort *dest_data,int dest_width,int x1,int y1,int x2,int y2, + int bm,fix u0,fix v0,fix u1,fix v1) + +{ + int screen_width=(x2-x1); // The size of bitmap on the screen + int screen_height=(y2-y1); + + ushort *perm_src_data=bm_data (bm,0); + ushort *src_data=perm_src_data; + int src_w=bm_w(bm,0); + + // Set up our initial coordinates + + if (screen_width==0 || screen_height==0) + return; + + dest_data+=(dest_width*y1+x1); + + fix xstep=(u1-u0)/(screen_width); + fix ystep=(v1-v0)/(screen_height); + + fix fx_u= u0; + fix fx_v= v0; + + fix u; + int y=y1; + int x,t; + + for (int i=0;i + +#include "ddgr.h" +#include "pserror.h" +#include "gameos.h" + +class ddgr_mac_lib +{ + public: + ddgr_mac_lib(void); + ~ddgr_mac_lib(void); + void SetWindowedMode(bool inWindowMode = true); + bool IsWindowed(void); + + void SetInitted(bool inInitState = true); + bool IsInitted(void); + + void SetDefaultColorKey(ddgr_color inColor); + + private: + bool m_initted; + bool m_windowed_mode; + ddgr_color m_color_key; +}; + +extern ddgr_mac_lib Mac_DDGR_Lib; + +#endif \ No newline at end of file diff --git a/ddgr_mac/macOSSurface.cpp b/ddgr_mac/macOSSurface.cpp new file mode 100644 index 000000000..df2a08605 --- /dev/null +++ b/ddgr_mac/macOSSurface.cpp @@ -0,0 +1,1050 @@ +/* + * $Logfile: /Descent3/main/ddgr_mac/macOSSurface.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:53 $ + * $Author: kevinb $ + * + * macintosh implementation of ddgr_surfaces + * + * $Log: macOSSurface.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:53 kevinb + * initial 1.5 import + * + * + * 10 5/21/97 5:09 PM Jeremy + * made a quick hack to convert the contents of the frame buffer from + * 5-6-5 format to 1-5-5-5 format pixel data right before the video flip. + * use the #define USE_COLOR_CONVERSION_HACK to turn this on or off (see + * flipVideo for details) + * + * 9 5/20/97 11:38 PM Jeremy + * temporarily removed the mprintf's in os_surf_clear + * + * 8 5/19/97 7:08 PM Jeremy + * removed extra gamma fade in of monitor when closing down video + * + * 7 5/17/97 6:50 PM Jeremy + * implemented clearing function + * + * 6 5/15/97 1:47 AM Jeremy + * changed mprintf's to be standard (with newline at end), also fixed a + * potential bug where a variable was declared twice in a function (blit) + * but in different scopes + * + * 5 5/13/97 11:15 AM Jeremy + * removed bug in initialization code (was checking an extra variable + * m_initted instead of m_video_initted) + * + * 4 5/11/97 8:00 PM Jeremy + * implemented ddgr_os_surf_GetAspectRatio + * + * 3 5/9/97 7:13 PM Jeremy + * implemented blitting (still need to implement clearing) + * + * 2 4/15/97 7:01 PM Jeremy + * initial implementation of fullscreen (via DrawSprocket) os_surfaces. + * right now only initialization and closing of surfaces/video is + * implemented. still need to implement surface + * creation/deletion/blitting. + * + * 1 4/9/97 7:16 PM Jeremy + * initial check in + * + * $NoKeywords: $ + */ + +//#define check + +// --------------------------------------------------------------------------- +// Macintosh Headers +// --------------------------------------------------------------------------- +#include +#include + +// --------------------------------------------------------------------------- +// Descent3 Headers +// --------------------------------------------------------------------------- +#include "pserror.h" + +#include "gr.h" +#include "ddgr_mac.h" +#include "macOSSurface.h" + +// --------------------------------------------------------------------------- +// Local File Types +// --------------------------------------------------------------------------- + +// The FullScreenData class is intended to encapsulate the +// data necessary for interacting with DrawSprocket +// and some utility functions for dealing with full screen info +// into one structure +class CFullScreenDataObj +{ + public: + CFullScreenDataObj(void); + ~CFullScreenDataObj(void); + + bool m_video_initted; + DSpContextAttributes m_screen_attr; + DSpContextReference m_context; + DSpAltBufferReference m_underlay; + GDHandle m_save_gdevice; + GrafPtr m_save_port; +}; + +// This is the data type of the object pointed to in the surfaces obj ptr +// for os_surfaces +class CMacOffscreenDataObj +{ + public: + CMacOffscreenDataObj(void) + { m_GWorld = nil; } + + GWorldPtr m_GWorld; +}; + +// The Error class is used for passing error information around +// It is internal to this file +class CMacOSSurfErr +{ + public: + CMacOSSurfErr(char* inErrStr, OSErr inMacErr, ddgr_error inDDGRErr) + { + m_ErrStr = inErrStr; + m_MacErr = inMacErr; + m_DDGRErr = inDDGRErr; + } + + char* m_ErrStr; + OSErr m_MacErr; + ddgr_error m_DDGRErr; +}; + +// --------------------------------------------------------------------------- +// File Level Globals +// --------------------------------------------------------------------------- +static CFullScreenDataObj gFullScreenData; + +// ------------------------------------------------------------ +// Implementation of the CFullScreenDataObj class +// It is intended that an instance of this object class +// be around for the whole game if full screen mode is used. +// ------------------------------------------------------------ +CFullScreenDataObj::CFullScreenDataObj(void) +{ + // Initialize the data members + m_video_initted = false; + + memset(&m_screen_attr, 0, sizeof(m_screen_attr)); + + m_context = nil; + m_underlay = nil; + m_save_gdevice = nil; + m_save_port = nil; +} + +CFullScreenDataObj::~CFullScreenDataObj(void) +{ + // Release the contexts if they were allocated? +} + +// --------------------------------------------------------------------------- +// ddgr_os_surf_InitVideo + +ddgr_error ddgr_os_surf_InitVideo(ddgr_surface *sf) +{ + ddgr_error err = DDGRERR_SUCCESS; + OSErr macErr = noErr; + + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_VIDEOSCREEN); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + + mprintf((0, "Initializing video.\n")); + + bool userCanSelect = false; + + // ¥ Initialize the desired screen attributes structure + gFullScreenData.m_screen_attr.displayWidth = sf->w; + gFullScreenData.m_screen_attr.displayHeight = sf->h; + gFullScreenData.m_screen_attr.colorNeeds = kDSpColorNeeds_Require; + gFullScreenData.m_screen_attr.backBufferDepthMask = sf->bpp; + gFullScreenData.m_screen_attr.displayDepthMask = sf->bpp; + gFullScreenData.m_screen_attr.backBufferBestDepth = sf->bpp; + gFullScreenData.m_screen_attr.displayBestDepth = sf->bpp; + gFullScreenData.m_screen_attr.pageCount = (sf->flags & SURFFLAG_BACKBUFFER) ? 2 : 1; + + try + { + // ¥ Check whether multiple monitors are available + macErr = DSpCanUserSelectContext(&(gFullScreenData.m_screen_attr), &userCanSelect); + if (macErr) + throw (CMacOSSurfErr("Error checking if user could select from different monitors.", + macErr, DDGRERR_DRIVERINIT)); + + if (userCanSelect) + { + // ¥ Allow user to select a monitor from those available + macErr = DSpUserSelectContext(&(gFullScreenData.m_screen_attr), + 0, + nil, + &(gFullScreenData.m_context)); + if (macErr) + throw (CMacOSSurfErr("Error while allowing user to choose a monitor.", + macErr, DDGRERR_DRIVERINIT)); + } + else + { + // ¥ Autoselect the best monitor/context + macErr = DSpFindBestContext( &(gFullScreenData.m_screen_attr), + &(gFullScreenData.m_context) ); + if (macErr) + throw (CMacOSSurfErr("Error while autoselecting a monitor.", + macErr, DDGRERR_DRIVERINIT)); + } + + // ¥ Request hardware pageflipping if it is available when reserving the context + gFullScreenData.m_screen_attr.contextOptions |= kDSpContextOption_PageFlip; + + // ¥ Reserve the context (take over the monitor) + macErr = DSpContext_Reserve(gFullScreenData.m_context, &(gFullScreenData.m_screen_attr)); + if (macErr) + throw (CMacOSSurfErr("Error while reserving the draw sprocket context.", + macErr, DDGRERR_DRIVERINIT)); + + // ¥ Fade out the monitor while resolution switching + macErr = DSpContext_FadeGammaOut( gFullScreenData.m_context, NULL ); + if (macErr) + throw (CMacOSSurfErr("Error while fading out monitor.", + macErr, DDGRERR_DRIVERINIT)); + + // ¥ Activate the context (do the resolution/depth switch) + macErr = DSpContext_SetState( gFullScreenData.m_context, kDSpContextState_Active ); + if (macErr) + throw (CMacOSSurfErr("Error while activating context", + macErr, DDGRERR_DRIVERINIT)); + + // ¥ Fade the monitor back in at the new resolution/depth + macErr = DSpContext_FadeGammaIn( gFullScreenData.m_context, NULL ); + if (macErr) + throw (CMacOSSurfErr("Error while fading in monitor ", + macErr, DDGRERR_DRIVERINIT)); + + //¥ All's well! + gFullScreenData.m_video_initted = true; + } + catch (CMacOSSurfErr theCaughtError) + { + mprintf((0, theCaughtError.m_ErrStr)); + mprintf((0, "DSp MacOS Error: %d\n", theCaughtError.m_MacErr)); + if (gFullScreenData.m_context) + { + //¥ The monitor might be faded out so fade it back in and release the context + DSpContext_FadeGammaIn( gFullScreenData.m_context, NULL ); + DSpContext_Release( gFullScreenData.m_context ); + gFullScreenData.m_context = nil; + } + err = theCaughtError.m_DDGRErr; + } + + return err; +} + + +// --------------------------------------------------------------------------- +// ddgr_os_surf_CloseVideo +// will kill all objects created in os_surf_InitVideo + +void ddgr_os_surf_CloseVideo(ddgr_surface *sf) +{ + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_VIDEOSCREEN); + ASSERT(sf->locks == 0); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + + OSErr macErr = noErr; + + mprintf((0, "Closing video.\n")); + + try + { + // ¥ Fade out the monitor while resolution switching + macErr = DSpContext_FadeGammaOut( gFullScreenData.m_context, NULL ); + if (macErr) + throw (CMacOSSurfErr("Error while fading out monitor.", + macErr, DDGRERR_DRIVERINIT)); + + // ¥ Deactivate the context (undo the resolution/depth switch) + macErr = DSpContext_SetState( gFullScreenData.m_context, kDSpContextState_Inactive ); + if (macErr) + throw (CMacOSSurfErr("Error while de-activating context", + macErr, DDGRERR_DRIVERINIT)); + + } + catch (CMacOSSurfErr theCaughtError) + { + mprintf((0, "Error while closing video!\n")); + mprintf((0, theCaughtError.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "---> DSp MacOS Error: %d\n", theCaughtError.m_MacErr)); + } + + if (gFullScreenData.m_context) + { + //¥ The monitor might be faded out so fade it back in and release the context + DSpContext_FadeGammaIn( gFullScreenData.m_context, NULL ); + DSpContext_Release( gFullScreenData.m_context ); + gFullScreenData.m_context = nil; + } + + //¥ Video is deinitialized! + gFullScreenData.m_video_initted = false; +} + + +// --------------------------------------------------------------------------- +// ddgr_os_surf_FlipVideo +// if this surface supports page flipping, will page flip. Otherwise, we +// flag an error. + +// This allows the use of the (costly) color conversion to get 5-6-5 ---> 1-5-5-5 +// but it only works when you are redrawing every pixel on the screen because +// it steps through the frame buffer and converts each pixel +#define USE_COLOR_CONVERSION_HACK + +ddgr_error ddgr_os_surf_FlipVideo(ddgr_surface *sf) +{ + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_VIDEOSCREEN); + ASSERT(sf->locks == 0); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + +// mprintf((0, "Clearing OS Surface!\n")); + + ddgr_error err = DDGRERR_SUCCESS; + + CGrafPtr macBackBufferPtr = NULL; + PixMapHandle dstPixMapH = NULL; + CGrafPtr savePort = NULL; + GDHandle saveGDevice = NULL; + OSErr macErr = noErr; + + GetGWorld(&savePort, &saveGDevice); + + try + { + if (!(sf->flags & SURFFLAG_BACKBUFFER)) + { + throw (CMacOSSurfErr("Tried to swap surface with no backbuffer.\n", noErr, DDGRERR_FAIL)); + } + + #ifdef USE_COLOR_CONVERSION_HACK + + macErr = DSpContext_GetBackBuffer(gFullScreenData.m_context, + kDSpBufferKind_Normal, + &macBackBufferPtr); + if (macErr || !macBackBufferPtr) + { + throw (CMacOSSurfErr("Could not obtain full screen surface backbuffer ptr!", + macErr, + DDGRERR_FAIL)); + } + + dstPixMapH = macBackBufferPtr->portPixMap; + SetGWorld(macBackBufferPtr, nil); + + if (!dstPixMapH) + { + throw (CMacOSSurfErr("Could not obtain full screen pixmaps for clearing os surface", + macErr, + DDGRERR_FAIL)); + } + + bool lockSuccess = false; + lockSuccess = LockPixels(dstPixMapH); + if (!lockSuccess) + { + throw (CMacOSSurfErr("Error: Could not lock destination pixels for flipping!", + 0, + DDGRERR_FAIL)); + } + + PixMapPtr macPixMapPtr = *dstPixMapH; + + // For now assume, 16 bpp + ASSERT(sf->bpp == BPP_16); + + //!! Do the Conversion + int i = 0; + int numRows = macPixMapPtr->bounds.bottom - macPixMapPtr->bounds.top; + int numCols = macPixMapPtr->bounds.right - macPixMapPtr->bounds.left; + int numTimes = numRows * numCols; + ushort oldColor = 0; + ushort newColor = 0; + ushort r = 0; + ushort g = 0; + ushort b = 0; + + ushort* base = (ushort*) macPixMapPtr->baseAddr; + + for (i = 0; i < numTimes; i++) + { + oldColor = base[i]; + + r = (oldColor & 0xF800) >> 11; + g = (oldColor & 0x07E0) >> 6; + b = (oldColor & 0x001F); + + newColor = 0; + newColor |= (r << 10); + newColor |= (g << 5); + newColor |= b; + + base[i] = newColor; + } + + UnlockPixels(dstPixMapH); + + #endif //USE_COLOR_CONVERSION_HACK + + // swap the buffers + macErr = DSpContext_SwapBuffers( gFullScreenData.m_context, NULL, 0 ); + if( macErr ) + { + throw (CMacOSSurfErr("DSpContext_SwapBuffers returned error: %d\n", + macErr, + DDGRERR_FAIL)); + } + } + catch (CMacOSSurfErr errObj) + { + mprintf((0, errObj.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "MacOS Error: %d\n", errObj.m_MacErr)); + err = errObj.m_DDGRErr; + } + + SetGWorld(savePort, saveGDevice); + return err; +} + +// --------------------------------------------------------------------------- +// ddgr_os_surf_Create +// create a surface in we are in fullscreen mode + +ddgr_error ddgr_os_surf_Create(ddgr_surface *sf) +{ + ddgr_error err = DDGRERR_SUCCESS; + OSErr macErr = noErr; + + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_OFFSCREEN_OS); + + mprintf((0, "Creating OS Surface!\n")); + + try + { + // Allocate a mac os surface data object + CMacOffscreenDataObj* surfDataObj = nil; + surfDataObj = new CMacOffscreenDataObj; + if (surfDataObj == nil) + { + throw (CMacOSSurfErr("Error allocating memory for mac os surface data object!", + noErr, + DDGRERR_OUTOFMEMORY)); + } + + // Store the reference to the new data object for use later + sf->obj = surfDataObj; + + // Allocate a new offscreen graphics world (cgrafptr) + GWorldPtr newOffscreen = nil; + Rect surfBoundsRect = {0,0,0,0}; + + surfBoundsRect.top = 0; + surfBoundsRect.left = 0; + surfBoundsRect.right = surfBoundsRect.left + sf->w; + surfBoundsRect.bottom = surfBoundsRect.top + sf->h; + + mprintf((0,"OS SURFACE CREATE: surface bounds rect = %d, %d, %d, %d (t,l,b,r). Is this correct?\n", + surfBoundsRect.top, + surfBoundsRect.left, + surfBoundsRect.right, + surfBoundsRect.bottom)); + + macErr = NewGWorld(&newOffscreen, sf->bpp, &surfBoundsRect, nil, nil, 0); + if (macErr) + { + throw (CMacOSSurfErr("Error allocating memory for mac offscreen buffer!", + noErr, + DDGRERR_OUTOFMEMORY)); + } + + // Store the new offscreen gworld for use later (blitting and such) + ((CMacOffscreenDataObj*) sf->obj)->m_GWorld = newOffscreen; + + // Reset unused data members + sf->locks = 0; + sf->rowsize = 0; + } + catch (CMacOSSurfErr errObj) + { + mprintf((0, errObj.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "MacOS Error: %d\n", errObj.m_MacErr)); + err = errObj.m_DDGRErr; + } + + return err; +} + +// --------------------------------------------------------------------------- +// ddgr_os_surf_Destroy + +void ddgr_os_surf_Destroy(ddgr_surface *sf) +{ + OSErr macErr = noErr; + + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_OFFSCREEN_OS); + ASSERT(sf->locks == 0); + + mprintf((0, "Deleting OS Surface!\n")); + + try + { + // Get the mac os surface data object + CMacOffscreenDataObj* surfDataObj = nil; + + surfDataObj = (CMacOffscreenDataObj*) sf->obj; + if (surfDataObj == nil) + { + throw (CMacOSSurfErr("Error: No data object associated with offscreen surface!", + 0, + DDGRERR_FAIL)); + } + + // Free the associated new offscreen graphics world (cgrafptr) + DisposeGWorld(surfDataObj->m_GWorld); + + // Clear the data member + surfDataObj->m_GWorld = nil; + + // Dispose of the surface data object + delete surfDataObj; + + // Clear the surface's data member + sf->obj = nil; + } + catch (CMacOSSurfErr errObj) + { + mprintf((0, "Could not destroy surface!\n")); + mprintf((0, errObj.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "MacOS Error: %d\n", errObj.m_MacErr)); + } +} + + +// --------------------------------------------------------------------------- +// ddgr_os_surf_Clear +// clears a surface based on the given rectangle in left, top, width, height + +ddgr_error ddgr_os_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h) +{ + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(dsf != NULL); + ASSERT(dsf->type == SURFTYPE_OFFSCREEN_OS || dsf->type == SURFTYPE_VIDEOSCREEN); + ASSERT(dsf->locks == 0); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + +// mprintf((0, "Clearing OS Surface!\n")); + + ddgr_error err = DDGRERR_SUCCESS; + + CGrafPtr macBackBufferPtr = NULL; + PixMapHandle dstPixMapH = NULL; + CGrafPtr savePort = NULL; + GDHandle saveGDevice = NULL; + OSErr macErr = noErr; + + GetGWorld(&savePort, &saveGDevice); + + try + { + if (dsf->type == SURFTYPE_OFFSCREEN_OS) + { + CMacOffscreenDataObj* dataObj = nil; + dataObj = (CMacOffscreenDataObj*) dsf->obj; + if (dataObj == nil) + { + throw (CMacOSSurfErr("Error: No data object associated with offscreen surface!", + 0, + DDGRERR_FAIL)); + } + + dstPixMapH = GetGWorldPixMap(dataObj->m_GWorld); + SetGWorld(dataObj->m_GWorld, nil); + } + else // FullScreen + { + macErr = DSpContext_GetBackBuffer(gFullScreenData.m_context, + kDSpBufferKind_Normal, + &macBackBufferPtr); + if (macErr || !macBackBufferPtr) + { + throw (CMacOSSurfErr("Could not obtain full screen surface backbuffer ptr!", + macErr, + DDGRERR_FAIL)); + } + + dstPixMapH = macBackBufferPtr->portPixMap; + SetGWorld(macBackBufferPtr, nil); + } + + if (!dstPixMapH) + { + throw (CMacOSSurfErr("Could not obtain full screen pixmaps for clearing os surface", + macErr, + DDGRERR_FAIL)); + } + + bool lockSuccess = false; + lockSuccess = LockPixels(dstPixMapH); + if (!lockSuccess) + { + throw (CMacOSSurfErr("Error: Could not lock destination pixels for blit!", + 0, + DDGRERR_FAIL)); + } + + PixMapPtr macPixMapPtr = *dstPixMapH; + + //!! Do the Clear + int i = 0; + int j = 0; + int bitsPerPixel = macPixMapPtr->pixelSize; + + ASSERT((bitsPerPixel == BPP_DEFAULT) || + (bitsPerPixel == BPP_8) || + (bitsPerPixel == BPP_16) || + (bitsPerPixel == BPP_24) || + (bitsPerPixel == BPP_32)); + + + RGBColor clearColor = {0,0,0}; + RGBColor saveColor = {0,0,0}; + Rect clearRect = {0,0,0,0}; + + ::GetForeColor(&saveColor); + + // Set up the color + clearColor.red = GR_COLOR_RED(col); + clearColor.green = GR_COLOR_GREEN(col); + clearColor.blue = GR_COLOR_BLUE(col); + + // Set up the bounding rect + clearRect.top = t; + clearRect.bottom = t + h; + clearRect.left = l; + clearRect.right = l + w; + + // Do the clear +// mprintf((0, "Clearing OS Surface to ddgr_color = %d, RGB = (%d, %d, %d)\n", +// col, clearColor.red, clearColor.green, clearColor.blue)); + + ::RGBForeColor(&clearColor); + ::PaintRect(&clearRect); + + ::RGBForeColor(&saveColor); + + UnlockPixels(dstPixMapH); + } + catch (CMacOSSurfErr errObj) + { + mprintf((0, errObj.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "MacOS Error: %d\n", errObj.m_MacErr)); + err = errObj.m_DDGRErr; + } + + SetGWorld(savePort, saveGDevice); + return err; +} + + +// --------------------------------------------------------------------------- +// ddgr_os_surf_Blt +// blts a non-scaled bitmap using windowed or fullscreen modes +// note that we DONT have to handle fullscreen->windowed or vice-versa, +// since they will never coexist + +ddgr_error ddgr_os_surf_Blt(ddgr_surface *dsf, int dx, int dy, + ddgr_surface *ssf, int sx, int sy, int sw, int sh) +{ + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(dsf != NULL && ssf != NULL); + ASSERT(dsf->type == SURFTYPE_VIDEOSCREEN || dsf->type == SURFTYPE_OFFSCREEN_OS); + ASSERT(ssf->type == SURFTYPE_VIDEOSCREEN || ssf->type == SURFTYPE_OFFSCREEN_OS); + ASSERT(dsf->locks == 0); + ASSERT(ssf->locks == 0); + + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + + ddgr_error err = DDGRERR_SUCCESS; + + CGrafPtr macBackBufferPtr = NULL; + PixMapHandle dstPixMapH = NULL; + PixMapHandle srcPixMapH = NULL; + CGrafPtr savePort = NULL; + GDHandle saveGDevice = NULL; + OSErr macErr = noErr; + + + GetGWorld(&savePort, &saveGDevice); + + try + { + if (dsf->type == SURFTYPE_OFFSCREEN_OS) + { + CMacOffscreenDataObj* dataObj = nil; + dataObj = (CMacOffscreenDataObj*) dsf->obj; + if (dataObj == nil) + { + throw (CMacOSSurfErr("Error: No data object associated with offscreen surface!", + 0, + DDGRERR_FAIL)); + } + + dstPixMapH = GetGWorldPixMap(dataObj->m_GWorld); + SetGWorld(dataObj->m_GWorld, nil); + } + else // FullScreen + { + macErr = DSpContext_GetBackBuffer(gFullScreenData.m_context, + kDSpBufferKind_Normal, + &macBackBufferPtr); + if (macErr || !macBackBufferPtr) + { + throw (CMacOSSurfErr("Could not obtain full screen surface backbuffer ptr!", + macErr, + DDGRERR_FAIL)); + } + + dstPixMapH = macBackBufferPtr->portPixMap; + SetGWorld(macBackBufferPtr, nil); + } + + if (ssf->type == SURFTYPE_OFFSCREEN_OS) + { + CMacOffscreenDataObj* dataObj = nil; + dataObj = (CMacOffscreenDataObj*) ssf->obj; + if (dataObj == nil) + { + throw (CMacOSSurfErr("Error: No data object associated with offscreen surface!", + 0, + DDGRERR_FAIL)); + } + + srcPixMapH = GetGWorldPixMap(dataObj->m_GWorld); + } + else // FullScreen + { + macErr = DSpContext_GetBackBuffer(gFullScreenData.m_context, + kDSpBufferKind_Normal, + &macBackBufferPtr); + if (macErr || !macBackBufferPtr) + { + throw (CMacOSSurfErr("Could not obtain full screen surface backbuffer ptr!", + macErr, + DDGRERR_FAIL)); + } + + srcPixMapH = macBackBufferPtr->portPixMap; + } + + if (!srcPixMapH || !dstPixMapH) + { + throw (CMacOSSurfErr("Could not obtain full screen pixmaps for blitting os surface", + macErr, + DDGRERR_FAIL)); + } + + bool lockSuccess = false; + lockSuccess = LockPixels(srcPixMapH); + if (!lockSuccess) + { + throw (CMacOSSurfErr("Error: Could not lock source pixels for blit!", + 0, + DDGRERR_FAIL)); + } + + lockSuccess = LockPixels(dstPixMapH); + if (!lockSuccess) + { + throw (CMacOSSurfErr("Error: Could not lock destination pixels for blit!", + 0, + DDGRERR_FAIL)); + } + + // Source and dest rectangles are the same (no scaling) + Rect srcRect = {sy, sx, sx+sw, sy+sh}; + Rect dstRect = {dy, dx, dx+sw, dy+sh}; + + // DO THE BLIT! + CopyBits((BitMap*) (*srcPixMapH), (BitMap*) (*dstPixMapH), &srcRect, &dstRect, srcCopy, nil); + + UnlockPixels(srcPixMapH); + UnlockPixels(dstPixMapH); + + } + catch (CMacOSSurfErr errObj) + { + mprintf((0, errObj.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "MacOS Error: %d\n", errObj.m_MacErr)); + err = errObj.m_DDGRErr; + } + + SetGWorld(savePort, saveGDevice); + return err; +} + + +// --------------------------------------------------------------------------- +// ddgr_os_surf_Lock and Unlock +// performs a lock which retrieves the data pointer and rowsize of a surface +// for direct rendering. + +ddgr_error ddgr_os_surf_Lock(ddgr_surface *sf) +{ + ddgr_error err = DDGRERR_SUCCESS; + OSErr macErr = noErr; + + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_OFFSCREEN_OS || sf->type == SURFTYPE_VIDEOSCREEN); + + CGrafPtr macBackBufferPtr = NULL; + PixMapHandle macPixMapH = NULL; + + try + { + if (sf->type == SURFTYPE_OFFSCREEN_OS) + { + CMacOffscreenDataObj* dataObj = nil; + dataObj = (CMacOffscreenDataObj*) sf->obj; + if (dataObj == nil) + { + throw (CMacOSSurfErr("Error: No data object associated with offscreen surface!", + 0, + DDGRERR_FAIL)); + } + + macPixMapH = GetGWorldPixMap(dataObj->m_GWorld); + } + else if (sf->type == SURFTYPE_VIDEOSCREEN) + { + macErr = DSpContext_GetBackBuffer(gFullScreenData.m_context, + kDSpBufferKind_Normal, + &macBackBufferPtr); + if (macErr) + { + throw (CMacOSSurfErr("Could not obtain full screen surface backbuffer ptr!", + macErr, + DDGRERR_FAIL)); + } + + macPixMapH = macBackBufferPtr->portPixMap; + } + else + { + throw (CMacOSSurfErr("Attempted to lock surface with invalid surftype!", + 0, + DDGRERR_FAIL)); + } + + Boolean lockSuccess = false; + lockSuccess = LockPixels(macPixMapH); + if (!lockSuccess) + { + throw (CMacOSSurfErr("Error: Could not lock pixel data!", noErr, DDGRERR_FAIL)); + } + + PixMapPtr macPixMapPtr = *macPixMapH; + + sf->w = macPixMapPtr->bounds.right - macPixMapPtr->bounds.left; + sf->h = macPixMapPtr->bounds.bottom - macPixMapPtr->bounds.top; + sf->bpp = macPixMapPtr->pixelSize; + sf->rowsize = (macPixMapPtr->rowBytes) & 0x7FFF; + + sf->data = GetPixBaseAddr(macPixMapH); + + sf->locks++; + } + catch (CMacOSSurfErr errObj) + { + mprintf((0, errObj.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "MacOS Error: %d\n", errObj.m_MacErr)); + err = errObj.m_DDGRErr; + } + + return err; +} + +ddgr_error ddgr_os_surf_Unlock(ddgr_surface *sf) +{ + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_OFFSCREEN_OS || sf->type == SURFTYPE_VIDEOSCREEN); + ASSERT(sf->locks); + + ddgr_error err = DDGRERR_SUCCESS; + OSErr macErr = noErr; + + CGrafPtr macBackBufferPtr = NULL; + PixMapHandle macPixMapH = nil; + + try + { + if (sf->type == SURFTYPE_OFFSCREEN_OS) + { + CMacOffscreenDataObj* dataObj = nil; + + dataObj = (CMacOffscreenDataObj*) sf->obj; + if (dataObj == nil) + { + throw (CMacOSSurfErr("Error: No data object associated with offscreen surface!", + 0, + DDGRERR_FAIL)); + } + + macPixMapH = GetGWorldPixMap(dataObj->m_GWorld); + } + else if (sf->type == SURFTYPE_VIDEOSCREEN) + { + macErr = DSpContext_GetBackBuffer(gFullScreenData.m_context, + kDSpBufferKind_Normal, + &macBackBufferPtr); + if (macErr) + { + throw (CMacOSSurfErr("Could not obtain full screen surface backbuffer ptr!", + macErr, + DDGRERR_FAIL)); + } + + macPixMapH = macBackBufferPtr->portPixMap; + } + else + { + throw (CMacOSSurfErr("Attempted to lock surface with invalid surftype!", + 0, + DDGRERR_FAIL)); + } + + UnlockPixels(macPixMapH); + + sf->data = NULL; + sf->locks--; + } + catch (CMacOSSurfErr errObj) + { + mprintf((0, errObj.m_ErrStr)); + mprintf((0, "\n")); + mprintf((0, "MacOS Error: %d\n", errObj.m_MacErr)); + err = errObj.m_DDGRErr; + } + + + return err; +} + +// --------------------------------------------------------------------------- +// ddgr_os_surf_AttachHandle(ddgr_suface *sf, unsigned handle) +// attaches an OS handle to a surface + +ddgr_error ddgr_os_surf_AttachHandle(ddgr_surface *sf, unsigned handle) +{ + ddgr_error err = DDGRERR_SUCCESS; + + ASSERT(Mac_DDGR_Lib.IsInitted()); + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(sf != NULL); + ASSERT(sf->type == SURFTYPE_OFFSCREEN_OS || sf->type == SURFTYPE_VIDEOSCREEN); + + err = DDGRERR_FAIL; + mprintf((0, "Attaching handle to OS Surface is *NOT* implemented\n")); + return err; +} + +//¥====================================================== +// MacOS Full Screen Graphics routines +//¥====================================================== + +// Initialize the macintosh for full screen graphics +ddgr_error ddgr_os_surf_fullscreen_Init(ddgr_init_info *info) +{ + ddgr_error err = DDGRERR_SUCCESS; + OSErr macErr = noErr; + + // Save the curreent macintosh port and graphics device to be restored later + GetPort( &(gFullScreenData.m_save_port) ); + gFullScreenData.m_save_gdevice = GetGDevice(); + + // Initialize DrawSprocket + macErr = DSpStartup(); + if (macErr) + { + mprintf((0, "Error initializing draw sprocket!: %d\n", macErr)); + err = DDGRERR_DRIVERINIT; + } + + return err; +} + +ddgr_error ddgr_os_surf_fullscreen_Close(void) +{ + ddgr_error err = DDGRERR_SUCCESS; + OSErr macErr = noErr; + + // Close DrawSprocket + macErr = DSpShutdown(); + if (macErr) + { + mprintf((0, "Error initializing draw sprocket!: %d\n", macErr)); + err = DDGRERR_DRIVERINIT; + } + + // Save the curreent macintosh port and graphics device to be restored later + SetPort(gFullScreenData.m_save_port); + SetGDevice(gFullScreenData.m_save_gdevice); + + return err; +} + +float +ddgr_os_surf_GetAspectRatio(void) +{ + ASSERT(!Mac_DDGR_Lib.IsWindowed()); + ASSERT(gFullScreenData.m_screen_attr.displayWidth != 0); + ASSERT(gFullScreenData.m_screen_attr.displayHeight != 0); + + float aspect = 1.0; + + aspect = (float) gFullScreenData.m_screen_attr.displayWidth / + (float) gFullScreenData.m_screen_attr.displayHeight; + + return aspect; +} \ No newline at end of file diff --git a/ddgr_mac/macOSSurface.h b/ddgr_mac/macOSSurface.h new file mode 100644 index 000000000..41085c735 --- /dev/null +++ b/ddgr_mac/macOSSurface.h @@ -0,0 +1,47 @@ +/* + * $Logfile: /Descent3/main/ddgr_mac/macOSSurface.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:53 $ + * $Author: kevinb $ + * + * macintosh implementation of interface to ddgr library + * + * $Log: macOSSurface.h,v $ + * Revision 1.1.1.1 2003/08/26 03:56:53 kevinb + * initial 1.5 import + * + * + * 3 5/11/97 8:00 PM Jeremy + * prototype for ddgr_os_surf_GetAspectRatio + * + * 2 4/15/97 7:01 PM Jeremy + * initial implementation of fullscreen (via DrawSprocket) os_surfaces. + * right now only initialization and closing of surfaces/video is + * implemented. still need to implement surface + * creation/deletion/blitting. + * + * 1 4/11/97 6:02 PM Jeremy + * initial checkin + * + * 1 4/9/97 7:16 PM Jeremy + * initial check in + * + * $NoKeywords: $ + */ + +#include "ddgr_mac.h" +#include "pserror.h" + +// Initialize the macintosh for full screen graphics +ddgr_error ddgr_os_surf_fullscreen_Init(ddgr_init_info *info); +// Close down the macintosh full screen graphics system +ddgr_error ddgr_os_surf_fullscreen_Close(void); + +// Initialize the macintosh for windowed graphics +ddgr_error ddgr_os_surf_windowed_Init(ddgr_init_info *info); +// Close down the macintosh windowed graphics system +ddgr_error ddgr_os_surf_windowed_Close(void); + +// Get the Aspect Ratio for the current main video surface +float ddgr_os_surf_GetAspectRatio(void); + diff --git a/ddio_common/CMakeLists.txt b/ddio_common/CMakeLists.txt new file mode 100644 index 000000000..098a0bea0 --- /dev/null +++ b/ddio_common/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (HEADERS ) +SET (CPPS + ddio.cpp + key.cpp) + +ADD_LIBRARY(ddio_common STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/ddio_common/ddio.cpp b/ddio_common/ddio.cpp new file mode 100644 index 000000000..f5051e126 --- /dev/null +++ b/ddio_common/ddio.cpp @@ -0,0 +1,137 @@ +/* + * $Logfile: /DescentIII/Main/ddio_common/ddio.cpp $ + * $Revision: 18 $ + * $Date: 10/21/99 9:27p $ + * $Author: Jeff $ + * + * Common DDIO Initialization + * + * $Log: /DescentIII/Main/ddio_common/ddio.cpp $ + * + * 18 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 17 8/11/99 3:09p Samir + * win32 as well calls ddioInternalJoyFrame. + * + * 16 7/28/99 5:18p Kevin + * Mac merge fixes + * + * 15 7/28/99 3:33p Kevin + * Mac! + * + * 14 4/22/99 1:56a Jeff + * pass in ddio_init_info to keyboard handlers + * + * 13 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 12 2/21/99 6:38p Samir + * mouse and key input better. buffered mouse. + * + * 11 1/25/99 6:47p Samir + * allow slow keyboard + * + * 10 10/20/98 12:58a Jeff + * added a way to force a lo-resolution timer + * + * 9 10/16/98 1:03p Samir + * fixed timer issue when closing ddio, should not close timer. + * + * 8 6/30/98 4:20p Samir + * added ddio_Close as standalone. ddio_Init will no longer close + * itself. + * + * 7 6/29/98 6:46p Samir + * MouseInit no longer takes in required number of mouse buttons. + * + * 6 12/09/97 2:08p Samir + * Non preemptive keyboard as of now? + * + * 5 10/23/97 2:58p Samir + * Took out extranneous messages. + * + * 4 10/17/97 5:18p Samir + * ddio_Init now does mouse opening and closing, where it should be. + * + * 3 10/17/97 5:03p Samir + * Default to preemptive keyboard handler (not under NT). + * + * 2 10/16/97 2:28p Samir + * move keyboard init into ddio_init and added preemptive select for + * keyboard. + * + * 2 5/12/97 1:21p Samir + * Commented out timer hook function calls. + * + * 1 5/08/97 1:54p Samir + * Created skeletal initialization functions. + * + * $NoKeywords: $ + */ +#include "ddio.h" +#include "joystick.h" +#include "pserror.h" +#include +static bool DDIO_initialized = false; +// ---------------------------------------------------------------------------- +// Common initialization +// ---------------------------------------------------------------------------- +// Initializes DDIO system +bool ddio_Init(ddio_init_info *init_info) +{ + static bool first_time=true; + bool res; + ASSERT(!DDIO_initialized); + if (first_time) { + atexit(ddio_Close); + } + mprintf((0, "DDIO system initializing...\n")); + res = ddio_InternalInit(init_info); + if (res) { + if (first_time) { // initialize once and only once. + timer_Init(0,init_info->use_lo_res_time); + } + if (!ddio_KeyInit(init_info)) + Error("Failed to initialize keyboard system."); + ddio_MouseInit(); + } + first_time = false; + DDIO_initialized = true; + joy_Init(init_info->joy_emulation); + return res; +} +void ddio_Close() +{ + if (DDIO_initialized) { + joy_Close(); + ddio_MouseClose(); + ddio_KeyClose(); + ddio_InternalClose(); + mprintf((0, "DDIO system closed.\n")); + DDIO_initialized = false; + } +} +void ddio_Suspend() +{ + ddio_InternalKeySuspend(); + ddio_InternalMouseSuspend(); +#ifdef MACINTOSH + ddio_InternalJoySuspend(); //DAJ make sprockets happy +#endif +} +void ddio_Resume() +{ + ddio_InternalKeyResume(); + ddio_InternalMouseResume(); +#ifdef MACINTOSH + ddio_InternalJoyResume(); +#endif +} +// handles buffered input from devices once per frame. +void ddio_Frame() +{ + ddio_InternalKeyFrame(); + ddio_InternalMouseFrame(); + ddio_InternalJoyFrame(); +} diff --git a/ddio_common/key.cpp b/ddio_common/key.cpp new file mode 100644 index 000000000..035729f3e --- /dev/null +++ b/ddio_common/key.cpp @@ -0,0 +1,417 @@ +/* + * $Logfile: /DescentIII/Main/ddio_common/key.cpp $ + * $Revision: 27 $ + * $Date: 10/21/99 9:28p $ + * $Author: Jeff $ + * + * Keyboard common interface + * + * $Log: /DescentIII/Main/ddio_common/key.cpp $ + * + * 27 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 26 8/31/99 5:30p Jason + * network statistics code + * + * 25 4/24/99 8:40p Samir + * debug key for mono screens moved into key handler. + * + * 24 4/24/99 5:41p Samir + * moved key to ascii, ascii to key to the ddio_common library. + * + * 23 4/22/99 2:00a Jeff + * pass in ddio_init_info + * + * 22 2/21/99 6:38p Samir + * mouse and key input better. buffered mouse. + * + * 21 2/05/99 1:16p Samir + * reset low level keys when flushing keyboard in the high level. added a + * function to the low level to reset the status of a key, called from + * high level key flush. + * + * 20 1/25/99 7:27p Samir + * fixed timing issues with emulated keyboard key down times. (Win32 sends + * multiple keydown messages, so ignore them.) + * + * 19 1/25/99 6:47p Samir + * allow slow keyboard + * + * 18 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 17 10/21/98 6:51p Samir + * took out dynamic key updating. + * + * 16 10/21/98 12:02p Samir + * properly update odd keys when they are released throug + * ddio_KeyGetDownTime. + * + * 15 10/16/98 12:16p Samir + * more accurate key timing broke ddio_KeyDownTime, so key down time is + * rounded off 5 decimal places. + * + * 14 9/17/98 12:50p Samir + * added ddio_KeyFlushKey. + * + * 13 7/01/98 4:56p Samir + * changed some deinit stuff. + * + * 12 3/31/98 12:46p Samir + * keyboard IO system better. uses getmsgproc windows hook. + * + * 11 3/24/98 4:26p Samir + * added function to return the state of an adjusted key. + * + * 10 2/25/98 6:11p Samir + * Added functions to better deal with key flushing. + * + * 9 2/03/98 5:16p Matt + * Changed key buffer to be ushorts so shift,ctrl,etc. bits don't get + * lost. + * Added support for KEY_DEBUGGED. + * + * 8 1/21/98 6:47p Samir + * Added debugging info. + * + * 7 12/10/97 1:12p Samir + * Pass time to ddio_UpdateKeyState + * + * 6 10/29/97 4:14p Samir + * Keep record of key down count per key. + * + * 5 10/16/97 2:28p Samir + * move keyboard init into ddio_init and added preemptive select for + * keyboard. + * + * 4 8/29/97 11:54a Samir + * Fixed some problem with keycodes that were greater than 0x80, being + * sign extended upon return from KeyInKey. + * + * 3 8/13/97 5:41p Samir + * Took out annoying mprintf of keycode. + * + * 2 7/25/97 6:31p Samir + * Fixed some really stupid key queue buffer check bugs that could cause + * instability. + * + * 3 5/09/97 6:45p Samir + * Common keystate update function + * + * 2 5/08/97 1:54p Samir + * Took out prototype to ddio_Close + * + * 1 5/08/97 12:22p Samir + * Moved common keyboard code from ddio_win to ddio_common. + * + * $NoKeywords: $ + */ + + +// Handles keyboard queue management + +#include "ddio.h" +#include "pserror.h" +#include "Macros.h" + +#include + +#define KEY_QUEUE_SIZE 16 + +//This is the key used as a debugging modifier +#ifdef MACINTOSH +#define KEY_DEBUG KEY_BACKSP +#else +#define KEY_DEBUG KEY_DELETE +#endif + +// ---------------------------------------------------------------------------- +// Data for ddio_common keyboard code. +// ---------------------------------------------------------------------------- + +static bool DDIO_key_init = 0; +volatile ubyte DDIO_key_state[DDIO_MAX_KEYS]; +volatile short DDIO_key_down_count[DDIO_MAX_KEYS]; + +static struct t_key_queue { + ushort buffer[KEY_QUEUE_SIZE]; // Keyboard buffer queue + int tail; + int head; +} DDIO_key_queue; + + +// ---------------------------------------------------------------------------- +// Common initialization +// ---------------------------------------------------------------------------- + +bool ddio_KeyInit(ddio_init_info *init_info) +{ + if (ddio_InternalKeyInit(init_info)) { + DDIO_key_init = 1; + ddio_KeyFlush(); + return 1; + } + + return 0; +} + + +void ddio_KeyClose() +{ + if (DDIO_key_init) { + ddio_InternalKeyClose(); + DDIO_key_init = 0; + } +} + + + +// ---------------------------------------------------------------------------- +// Keyboard accessor functions +// ---------------------------------------------------------------------------- + +float ddio_KeyDownTime(int key) +{ + ASSERT(DDIO_key_init); + +// snap off shift states, etc. + return ddio_InternalKeyDownTime((ubyte)(key&0xff)); +} + + +// return number of times a key's been down since last call. +int ddio_KeyDownCount(int key) +{ + int count = DDIO_key_down_count[key]; + DDIO_key_down_count[key] = 0; + + return count; +} + + +// returns the state of an ADJUSTED KEY VALUE +bool ddio_GetAdjKeyState(int adj_key) +{ + ubyte key_raw = adj_key & 0xff; + int key_test = 0; + + if (adj_key & KEY_SHIFTED) { + if (KEY_STATE(KEY_LSHIFT) || KEY_STATE(KEY_RSHIFT)) + key_test |= KEY_SHIFTED; + } + if (adj_key & KEY_ALTED) { + if (KEY_STATE(KEY_LALT) || KEY_STATE(KEY_RALT)) + key_test |= KEY_ALTED; + } + if (adj_key & KEY_CTRLED) { + if (KEY_STATE(KEY_LCTRL) || KEY_STATE(KEY_RCTRL)) + key_test |= KEY_CTRLED; + } + if (adj_key & KEY_DEBUGGED) { + if (KEY_STATE(KEY_DEBUG)) + key_test |= KEY_DEBUGGED; + } + + if (KEY_STATE(key_raw)) + key_test |= key_raw; + + if (key_test == adj_key) + return true; + else + return false; +} + + + +// ---------------------------------------------------------------------------- +// Keyboard Queue functions +// ---------------------------------------------------------------------------- + +void ddio_KeyFlush() +{ + int i; + + if (!DDIO_key_init) return; + +// flush out internal key input system by gathering all data in buffer. + DDIO_key_queue.head = DDIO_key_queue.tail = 0; + + for (i = 0; i < KEY_QUEUE_SIZE; i++) + { + DDIO_key_queue.buffer[i] = 0; + } + +// flush keyboard array too + for (i = 0; i < DDIO_MAX_KEYS; i++) + { + DDIO_key_down_count[i] = 0; + DDIO_key_state[i] = 0; + ddio_InternalResetKey(i); + } + +} + + +void ddio_KeyFlushKey(int key) +{ + DDIO_key_down_count[key] = 0; + DDIO_key_state[key] = 0; +} + +int ddio_KeyInKey() +{ + unsigned key=0; + + if (DDIO_key_queue.head != DDIO_key_queue.tail) { + key = (unsigned)DDIO_key_queue.buffer[DDIO_key_queue.head]; + // mprintf((1, "%x ", key)); + DDIO_key_queue.head++; + if (DDIO_key_queue.head >= KEY_QUEUE_SIZE) DDIO_key_queue.head = 0; + } + + return (int)key; +} + + +int ddio_KeyPeek() +{ + if (DDIO_key_queue.head != DDIO_key_queue.tail) { + return (int)DDIO_key_queue.buffer[DDIO_key_queue.head]; + } + return 0; +} + + +void ddio_AddKeyToQueue(int key) +{ + int keycode = key; + int temp; + + if (DDIO_key_state[KEY_LSHIFT] || DDIO_key_state[KEY_RSHIFT]) keycode |= KEY_SHIFTED; + if (DDIO_key_state[KEY_LCTRL] || DDIO_key_state[KEY_RCTRL]) keycode |= KEY_CTRLED; + if (DDIO_key_state[KEY_LALT] || DDIO_key_state[KEY_RALT]) keycode |= KEY_ALTED; + if (DDIO_key_state[KEY_CMD]) keycode |= KEY_CMD; //DAJ + + #ifdef _DEBUG + if (DDIO_key_state[KEY_DEBUG]) keycode |= KEY_DEBUGGED; + if (keycode == (KEY_DEBUGGED+KEY_SHIFTED+KEY_M)) { + static int current_virtual_window = 1; + + current_virtual_window++; + if (current_virtual_window == 2) { // skip stats window + current_virtual_window++; + } + if (current_virtual_window == 5) { + current_virtual_window = 1; + } + Debug_ConsoleRedirectMessages(current_virtual_window, 1); + } + #endif + + temp = DDIO_key_queue.tail+1; + if (temp >= KEY_QUEUE_SIZE) temp = 0; + + if (temp != DDIO_key_queue.head) { + DDIO_key_queue.buffer[DDIO_key_queue.tail] = (ushort)keycode; + DDIO_key_queue.tail = temp; + // mprintf((1, "%d ", keycode)); + } +} + + +// Updates timing info for keys +void ddio_UpdateKeyState(int key, bool isdown) +{ + if (isdown) { + if (!DDIO_key_state[key]) { + ddio_AddKeyToQueue(key); + DDIO_key_down_count[key]++; + } + DDIO_key_state[key] = 1; + } + else { + DDIO_key_state[key] = 0; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +static unsigned char ascii_table[128] = +{ 255, 255, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',255,255, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 255, 255, + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39, '`', + 255, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 255,'*', + 255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,'7', + '8', '9', '-','4','5','6','+','1','2','3','0','.',255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255 }; + + +static unsigned char shifted_ascii_table[128] = +{ 255, 255, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+',255,255, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 255, 255, + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', + 255, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 255,255, + 255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,'7', + '8', '9', '-','4','5','6','+','1','2','3','0','.',255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255 }; + + +#define SHK KEY_SHIFTED + +// keycode value for each ascii. +static int keycode_table[128] = +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_TAB, 0, 0, 0, KEY_ENTER,0,0, // 0-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_ESC, 0, 0, 0, 0, // 16-31 + KEY_SPACEBAR,SHK+KEY_1,SHK+KEY_RAPOSTRO,SHK+KEY_3,SHK+KEY_4,SHK+KEY_5, // 32-37 + SHK+KEY_7,KEY_RAPOSTRO,SHK+KEY_9,SHK+KEY_0,SHK+KEY_8,SHK+KEY_EQUAL, // 38-43 + KEY_COMMA,KEY_MINUS,KEY_PERIOD,KEY_SLASH,KEY_0,KEY_1,KEY_2,KEY_3, // 44-51 + KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,SHK+KEY_SEMICOL,KEY_SEMICOL, // 52-59 + SHK+KEY_COMMA,KEY_EQUAL,SHK+KEY_PERIOD,SHK+KEY_SLASH,SHK+KEY_2, // 60-64 + KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K, // 65-75 + KEY_L,KEY_M,KEY_N,KEY_O,KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V, // 76-86 + KEY_W,KEY_X,KEY_Y,KEY_Z,KEY_LBRACKET,KEY_BACKSLASH,KEY_RBRACKET, // 87-93 + SHK+KEY_6,SHK+KEY_MINUS,KEY_LAPOSTRO, // 94-96 + KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K, // 97-107 + KEY_L,KEY_M,KEY_N,KEY_O,KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V, // 108-118 + KEY_W,KEY_X,KEY_Y,KEY_Z,SHK+KEY_LBRACKET,SHK+KEY_BACKSLASH, // 119-124 + SHK+KEY_RBRACKET,SHK+KEY_LAPOSTRO,0, // 125-127 +}; + + +// converts keycode to ASCII +int ddio_KeyToAscii(int code) +{ + int shifted; + + shifted = code & KEY_SHIFTED; + code &= 0xFF; + + if ( code>=127 ) + return 255; + + if (shifted) + return (int)shifted_ascii_table[code]; + else + return (int)ascii_table[code]; +} + + +// converts ascii code to key +int ddio_AsciiToKey(int ascii) +{ + if (ascii < 0 || ascii >= 128) return 0; + + if (ascii >= 'A' && ascii <= 'Z') { + // I'm lazy + return (SHK+keycode_table[ascii]); + } + else { + return keycode_table[ascii]; + } +} + diff --git a/ddio_lnx/CMakeLists.txt b/ddio_lnx/CMakeLists.txt new file mode 100644 index 000000000..772ab2fe3 --- /dev/null +++ b/ddio_lnx/CMakeLists.txt @@ -0,0 +1,4 @@ +SET (HEADERS ddio_lnx.h ) +SET (CPPS lnxfile.cpp lnxforcefeedback.cpp lnxio.cpp sdljoy.cpp lnxkey.cpp lnxkey_null.cpp lnxmouse.cpp lnxtimer.cpp lnxkey_raw.cpp lnxcdrom.cpp lnxkey_sdl.cpp) + +ADD_LIBRARY(ddio_lnx STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/ddio_lnx/ddio_lnx.h b/ddio_lnx/ddio_lnx.h new file mode 100644 index 000000000..670589ab7 --- /dev/null +++ b/ddio_lnx/ddio_lnx.h @@ -0,0 +1,56 @@ +/* + * + * $NoKeywords: $ + */ + + +#ifndef DDIO_LNX_H +#define DDIO_LNX_H + +#include "pstypes.h" + +class oeLnxApplication; +extern oeLnxApplication *Lnx_app_obj; + +/* +typedef struct dinput_data { + oeWin32Application *app; + HWND hwnd; + LPDIRECTINPUT lpdi; + + LPDIRECTINPUTDEVICE lpkeydev; + HHOOK hkeyhook; + BOOL key_acquired; + bool preemptive; + + LPDIRECTINPUTDEVICE lpmsedev; + HHOOK hmsehook; + BOOL mse_acquired; + int num_mse_buttons; + int num_mse_axes; +} dinput_data; + +extern dinput_data DInputData; +extern bool DDIO_init; +extern bool DDIO_preemptive; + + +// Internal functions +// thread handlers +// -keyboard + +bool ddio_JoyHandler(); +void ddio_DebugMessage(unsigned err, char *fmt, ...); +float ddio_TickToSeconds(unsigned long ticks); + +void ddio_KeyHandler(MSG *msg); +void ddio_MouseHandler(MSG *msg); + +#define DDIO_MESSAGE(_m) ddio_DebugMessage _m + +#define MAKE_DDIO_TIME(_ms) ddio_TickToSeconds(_ms) + +*/ +#endif + + diff --git a/ddio_lnx/lnxcdrom.cpp b/ddio_lnx/lnxcdrom.cpp new file mode 100644 index 000000000..edd0cfe3a --- /dev/null +++ b/ddio_lnx/lnxcdrom.cpp @@ -0,0 +1,929 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxcdrom.cpp $ +* $Revision: 1.12 $ +* $Date: 2004/12/05 04:00:20 $ +* $Author: ryan $ +* +* Linux cdrom routines (borrowed some code from SDL) +* +* $Log: lnxcdrom.cpp,v $ +* Revision 1.12 2004/12/05 04:00:20 ryan +* MacOS X patches. +* +* Revision 1.11 2004/03/21 17:11:39 kevinb +* Fixes so linux will compile again. Tested with gcc-2.96 +* +* Revision 1.10 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.9 2001/01/11 11:41:29 icculus +* Mounting fix. +* +* Revision 1.8 2000/07/07 01:45:32 icculus +* Reinit functionality. +* +* Revision 1.7 2000/07/06 22:15:49 icculus +* Changed to work with GetMultiCDPath()'s new behaviour. +* +* Revision 1.6 2000/06/29 08:57:34 icculus +* Took out the Mount() and Unmount() code, 'cause it was retarded. Do it +* outside the program. +* +* Revision 1.5 2000/06/29 06:41:23 icculus +* mad commits. +* +* Revision 1.4 2000/06/24 01:15:15 icculus +* patched to compile. +* +* Revision 1.3 2000/04/27 11:16:34 icculus +* Took out SDL_Init() call. It's handled in $/Main/lnxmain.cpp, now. +* +* Revision 1.2 2000/04/24 03:18:00 icculus +* SDLified and loki_utilified this code. Lots of paradigm fixes, too. It's +* VERY different now. +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 5 12/01/99 3:30p Jeff + * ddio_GetCDDrive now returns NULL instead of "" + * + * 4 10/05/99 12:54p Jeff + * removed system() call + * + * 3 10/02/99 6:50p Jeff + * pretty solid cd-rom support now. Tested and seems to work. Need to + * figure out a better way of handlng /proc/mounts + * + * 2 10/02/99 3:00p Jeff + * slightly rewritten due to some early design flaws (namely multiple + * cd-rom drives, and the drive would be mounted when you try to switch + * CDs). This should be better (though only 1 cd-rom drive is supported + * now). May not compile yet. + * + * 1 10/01/99 7:44p Jeff + * initial checkin +* +* $NoKeywords: $ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mem.h" +#include "mono.h" +#include "ddio.h" +#include "debug.h" +#include "pserror.h" +#include "loki_utils.h" + +#define VD_LSN 16 // first logical sector of volume descriptor table +#define CDROM_LSECSZ 2048 // initial logical sector size of a CDROM +#define CDROM_LBS CDROM_LSECSZ + +class system_cdroms +{ +public: + system_cdroms(); + ~system_cdroms(); + + bool Mount(void); + bool UnMount(void); + + // Given a device specified by the user, it will make it the + // default device for the system. + bool SetDefaultDevice(char *device); + + // resets the device mountpoint. If the mountpoint was created, it will + // unmount the drive and remove the mountpoint directory + void ResetMountpoint(void); + + // This function will check the default device to see if it + // has a CD in it with the given volume. If it doesn't + // then it will unmount the device (if it is mounted), in preparation + // for a CD switch. If it does have the correct CD in it, then + // it wil be mounted (if it isn't already) and the mount position + // will be given in mountpoint. If it can't mount it will return false; + bool PrepareForVolume(char *volume,char *mountpoint); + + // Returns the device name of the default drive + const char *GetDeviceName(void); + + // Returns the volume name of the default drive + const char *GetVolumeName(void); + + // Returns true if a CD is in the default device drive + bool IsCdInDrive(void); + +private: + bool IsCdInDrive(int drive); + const char *CDName(int drive); + const char *GetVolume(int drive); + int GetNumCDRoms(void); + + // checks to see if the given cd-rom device is mounted + // if so, it returns true and fills in mountpoint with the + // directory it is mounted in + bool IsDeviceMounted(const char *device,char *mountpoint, int max_size); + // Check a drive to see if it is a CD-ROM + int CheckDrive(char *drive, struct stat *stbuf); + // Add a CD-ROM drive to our list of valid drives + void AddDrive(char *drive, struct stat *stbuf); + void Shutdown(void); + + // Locks the CD-ROM + void LockDevice(void); + // UnLocks the CD-ROM + void UnLockDevice(void); + // This function will go through our list of devices + // when it comes across the first CD-ROM (which can only be + // detected when a CD is in the drive), it will make it the default + // device. +public: + void FindDefaultDevice(void); + void reinit(void); + +private: + // Queries the settings for the default drive + // First it checks to see if the drive is already mounted + // If it is, it's mountpoint is saved and we continue + // If it isn't mounted, the it will create a mountpoint for the device + // and mount it there. + void QueryDefaultDevice(void); + + bool m_CreatedMountPoint; + bool m_DefaultMounted; + +public: // !!! hack. Make an accessor method. + char m_MountedDir[_MAX_PATH]; + +private: + char m_MountDirectory[_MAX_PATH]; + int m_NumCDRoms; + int m_DeviceToUse; +}; + +system_cdroms CDROM_Devices; + +// Given a device/drive, this marks it as the default CD-ROM drive +void ddio_MarkDefaultCDDrive(char *drive) +{ + bool ret = CDROM_Devices.SetDefaultDevice(drive); +/* + if(!ret) + { + Error("Unable to set %s as the default CD-ROM drive\n",drive); + } +*/ +} + + +void ddio_ReinitCDSystem(void) +{ + CDROM_Devices.reinit(); +} + +//Give a volume label to look for, and if it's found returns a path +//If it isn't found, return NULL +char *ddio_GetCDDrive(char *vol) +{ + static char drivepath[_MAX_PATH]; + +/* + if(*vol=='\0') + return NULL; + + bool ret = CDROM_Devices.PrepareForVolume(vol,drivepath); + if(!ret) + { + // nope...not yet + return NULL; + } +*/ + + CDROM_Devices.FindDefaultDevice(); //returns if already set. + + if (CDROM_Devices.Mount() == false) + return(NULL); + + strcpy(drivepath,CDROM_Devices.m_MountedDir); + return drivepath; +} + +void cdrom_system_shutdown(void) +{ + CDROM_Devices.ResetMountpoint(); +} + +void cdrom_set_system_shutdown(void) +{ + static bool set = false; + if(set) + return; + set = true; + atexit(cdrom_system_shutdown); +} + +system_cdroms::system_cdroms(void) +{ + reinit(); +} + +void system_cdroms::reinit(void) +{ + SDL_QuitSubSystem(SDL_INIT_CDROM); + SDL_Init(SDL_INIT_CDROM); + m_CreatedMountPoint = false; + m_NumCDRoms = 0; + m_DeviceToUse = -1; + m_DefaultMounted = false; + + m_NumCDRoms = GetNumCDRoms(); +} // reinit + + +system_cdroms::~system_cdroms(void) +{ +// SDL_QuitSubSystem(SDL_INIT_CDROM); +} + +int system_cdroms::GetNumCDRoms(void) +{ + return(SDL_CDNumDrives()); +} + +// Returns the device name of the default drive +const char *system_cdroms::GetDeviceName(void) +{ + return CDName(m_DeviceToUse); +} + +// Returns the volume name of the default drive +const char *system_cdroms::GetVolumeName(void) +{ + return GetVolume(m_DeviceToUse); +} + +const char *system_cdroms::CDName(int drive) +{ + return(SDL_CDName(drive)); +} + +const char *system_cdroms::GetVolume(int drive) +{ +#if 1 //MACOSX + assert(0); +#else + static char buffer[256]; + + if(drive<0 || drive>=m_NumCDRoms) + return NULL; + + struct iso_primary_descriptor *ipd; + int cdfd,cdromfmt=0; + + if(!IsCdInDrive(drive)) + return NULL; + + cdfd = open(CDName(drive),(O_RDONLY|O_EXCL|O_NONBLOCK), 0); + if(cdfd<0) + return NULL; + + // locate at the beginning of the descriptor table + lseek(cdfd, VD_LSN*CDROM_LSECSZ, SEEK_SET); + + ipd = (struct iso_primary_descriptor *) buffer; + + // walk descriptor table + for(;;) + { + unsigned char type; + read(cdfd, buffer, sizeof(buffer)); + + // make sure it's ISO format + if(cdromfmt==0) + { + if(strncmp (ipd->id, ISO_STANDARD_ID, sizeof(ipd->id)) != 0) + { + //not an ISO9660 cdrom + close(cdfd); + return NULL; + }else + { + //ISO9660 + cdromfmt = 1; + } + } + + // type of descriptor + type = (unsigned char)ipd->type[0]; + + // terminating volume + if(type==ISO_VD_END) + { + break; + } + + // ISO 9660 filestructure + if(cdromfmt==1 && type==ISO_VD_PRIMARY && !strncmp(ipd->id,ISO_STANDARD_ID,sizeof(ipd->id))) + { + char *ptr; + int len = sizeof(ipd->volume_id); + bcopy(ipd->volume_id,buffer,len); + buffer[len] = '\0'; + + for(ptr=buffer+len-1;ptr>buffer;ptr--) + { + if(*ptr==' ') + { + *ptr = 0; + } + } + + close(cdfd); + return buffer; + } + } + + close(cdfd); +#endif + return NULL; +} + +// Returns true if a CD is in the default device drive +bool system_cdroms::IsCdInDrive(void) +{ + return IsCdInDrive(m_DeviceToUse); +} + +bool system_cdroms::IsCdInDrive(int drive) +{ +// int cdfd,is_in=0; +// struct cdrom_subchnl info; +// struct stat stbuf; + CDstatus cdStat; + SDL_CD cdInfo; + bool retVal = true; + + if(drive<0 || drive>=m_NumCDRoms) + retVal = false; + else + { + cdStat = SDL_CDStatus(&cdInfo); + if ((cdStat == CD_TRAYEMPTY) || (cdStat == CD_ERROR)) + retVal = false; + } // else + return(retVal); +} + +static int read_file_string(FILE *fd,char *buffer,int maxsize) +{ + int ret; + int size = 0; + + ret = fgetc(fd); + while(ret!=EOF && sizemnt_fsname) == 0) // found device? + { + strcpy(mountpoint, mntEntry->mnt_dir); //!!! overflows? + getOut = retVal = true; + } // if + } // else + } while (getOut == false); + + endmntent(mountFile); +#endif + return(retVal); +} + + +bool system_cdroms::IsDeviceMounted(const char *device,char *mountpoint, int max_size) +{ +#if 1 //MACOSX + int rc = loki_getmountpoint(device, mountpoint, max_size); + return(rc > 0); +#else + return false; +#endif +} + + +bool system_cdroms::Mount(void) +{ + if(m_DeviceToUse==-1) + return false; + + // make sure the atexit is set so we can properly clear all created tmp dirs + cdrom_set_system_shutdown(); + + // first check to see if it is mounted + const char *name = CDName(m_DeviceToUse); + char mountpoint[_MAX_PATH]; + if(IsDeviceMounted(name,mountpoint, sizeof (mountpoint))) + { + strcpy(m_MountedDir, mountpoint); + m_DefaultMounted = true; + return true; + } + + + /* don't do this. + // Not mounted? Do it ourselves. + // so we now have our directory, try to mount + unsigned long int rwflag = MS_MGC_VAL|MS_RDONLY; + int ret = mount(name,m_MountedDir,"iso9660",rwflag,NULL); + if(ret!=0) + { + switch(errno) + { + case EPERM: + fprintf(stdout,"Unable to mount %s, you are not superuser\n",name); + break; + case ENODEV: + fprintf(stdout,"iso9660 filesystem not supported by kernel\n"); + break; + case ENOTBLK: + fprintf(stdout,"Device %s not a block device\n",name); + break; + case EBUSY: + fprintf(stdout,"Device %s already mounted\n",name); + Int3(); + break; + case ENAMETOOLONG: + fprintf(stdout,"Attempting to mount %s to %s (but name too long)\n",name,m_MountedDir); + break; + case ENOTDIR: + fprintf(stdout,"%s not a directory\n",m_MountedDir); + break; + default: + //case EINVAL: + //case EFAULT: + //case ENOMEM: + //case EMXIO: + //case EMFILE: + //case ENOENT: + //case EACCES: + fprintf(stdout,"Unknown error %d trying to mount %s\n",errno,name); + break; + } + }else + { + // we mounted! + m_DefaultMounted = true; + LockDevice();//make sure it is locked + return true; + } + */ + return false; +} + +bool system_cdroms::UnMount(void) +{ + if(m_DeviceToUse==-1) + return false; +/* + // find the drive that is mounted + char mountpoint[_MAX_PATH]; + const char *name = CDName(m_DeviceToUse); + if (IsDeviceMounted(name,mountpoint,sizeof (mountpoint)) == false) + { + m_DefaultMounted = false; + UnLockDevice();//make sure it is unlocked + return true;//drive isn't mounted anywhere + } + + UnLockDevice();//make sure it is unlocked + int ret = umount(name); + if(ret==0) + { + m_DefaultMounted = false; + return true; + }else + { + mprintf((0,"Error %d trying to unmount %s\n",errno,name)); + //lock it again...something went wrong + LockDevice(); + return false; + } +*/ + return false; +} + +// Given a device specified by the user, it will make it the +// default device for the system. +bool system_cdroms::SetDefaultDevice(char *device) +{ +/* + if(m_DeviceToUse!=-1) + { + Int3(); + return false; + } +*/ + // first find which id the device is + int i,dev_id=-1; + + for(i=0;iname, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + if(fileid>=0) + { + int arg = 1; + //retval = ioctl(fileid,CDROM_LOCKDOOR,arg); + retval = ioctl(fileid,CDC_LOCK,arg); + if(retval<0) + { + mprintf((0,"CDROM: ioctl error locking drive (%d)\n",retval)); + } + close(fileid); + } +*/ +} + +// UnLocks the CD-ROM +void system_cdroms::UnLockDevice(void) +{ +/* + if(m_DeviceToUse==-1) + return; + + int retval; + int fileid; + fileid = open(m_CDList[m_DeviceToUse]->name, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); + if(fileid>=0) + { + int arg = 0; + + //retval = ioctl(fileid,CDROM_LOCKDOOR,arg); + retval = ioctl(fileid,CDC_LOCK,arg); + if(retval<0) + { + mprintf((0,"CDROM: ioctl error unlocking drive (%d)\n",retval)); + } + close(fileid); + } +*/ +} + diff --git a/ddio_lnx/lnxfile.cpp b/ddio_lnx/lnxfile.cpp new file mode 100644 index 000000000..160980e04 --- /dev/null +++ b/ddio_lnx/lnxfile.cpp @@ -0,0 +1,1156 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxfile.cpp $ +* $Revision: 1.5 $ +* $Date: 2004/12/05 09:02:45 $ +* $Author: ryan $ +* +* Linux file routines +* +* $Log: lnxfile.cpp,v $ +* Revision 1.5 2004/12/05 09:02:45 ryan +* Valgrind fix. +* +* Revision 1.4 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.3 2001/01/11 11:41:57 icculus +* Allow .dotfiles to be shown in file dialogs. +* +* Revision 1.2 2000/06/24 01:15:15 icculus +* patched to compile. +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 13 10/02/99 2:59p Jeff + * moved ddio_GetCDDrive to lnxcdrom.cpp + * + * 12 9/07/99 4:36p Jeff + * fixed ddio_FindFileFirst/Next when a directory is returned + * + * 11 8/22/99 7:10p Jeff + * fix directory lock function if the pid check returns an error, now + * assume the pid is invalid + * + * 10 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include "ddio.h" +#include "ddio_lnx.h" +#include "pserror.h" +#include "mem.h" +#include "lnxfix.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _MAX_DIR 256 + + +// --------------------------------------------------------------------------- +// File operations + +// creates a directory or folder on disk +bool ddio_CreateDir(const char *path) +{ + return (mkdir(path,S_IRWXU))?false:true; +} + + +// destroys a directory +bool ddio_RemoveDir(const char *path) +{ + return (rmdir(path))?false:true; +} + +// retrieve the current working folder where file operation will occur. +void ddio_GetWorkingDir(char *path, int len) +{ + getcwd(path,len); +} + +bool ddio_SetWorkingDir(const char *path) +{ + return (chdir(path)) ? false : true; +} + +bool ddio_FileDiff(const char *path1, const char *path2) +{ + struct stat abuf,bbuf; + + if (stat(path1,&abuf)) + Int3();//error getting stat info + + if (stat(path2,&bbuf)) + Int3();//error getting stat info + + if ((abuf.st_size != bbuf.st_size) || (abuf.st_mtime != bbuf.st_mtime)) + return true; + return false; +} + +// get a file's length +int ddio_GetFileLength(FILE* filePtr) +{ + int size = -1; + if (filePtr){ + int filedes = fileno(filePtr); + struct stat info; + + fstat(filedes, &info); + size = info.st_size; + }else{ + mprintf((0, "Tried getting length of NULL fileptr!\n")); + Int3(); + } + return size; +} + +// Split a pathname into its component parts +void ddio_SplitPath(const char* srcPath, char* path, char* filename, char* ext) +{ + int pathStart = -1; + int pathEnd = -1; + int fileStart = -1; + int fileEnd = -1; + int extStart = -1; + int extEnd = -1; + + int totalLen = strlen(srcPath); + + // Check for an extension + /////////////////////////////////////// + int t = totalLen - 1; + while( (srcPath[t]!='.') && (srcPath[t]!='/') && (t>=0) ) t--; + //see if we are at an extension + if((t>=0)&&(srcPath[t]=='.')){ + //we have an extension + extStart = t; + extEnd = totalLen - 1; + if(ext) + { + strncpy(ext,&(srcPath[extStart]),extEnd - extStart + 1); + ext[extEnd - extStart + 1] = '\0'; + } + }else{ + //no extension + if(ext) + ext[0] = '\0'; + } + + // Check for file name + //////////////////////////////////// + int temp = (extStart!=-1)?(extStart):(totalLen-1); + while( (temp>=0) && (srcPath[temp]!='/') ) temp--; + if(temp<0) + temp = 0; + if(srcPath[temp]=='/'){ + //we have a file + fileStart = temp + 1; + if(extStart!=-1) + fileEnd = extStart - 1; + else + fileEnd = totalLen - 1; + if(filename) + { + strncpy(filename,&(srcPath[fileStart]),fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = '\0'; + } + pathStart = 0; + pathEnd = fileStart - 2; + //Copy the rest into the path name + if(path) + { + strncpy(path, &(srcPath[pathStart]),pathEnd - pathStart + 1); + path[pathEnd - pathStart + 1] = 0; + } + }else{ + //only file, no path + fileStart = 0; + if(extStart != -1) + fileEnd = extStart - 1; + else + fileEnd = totalLen - 1; + + if(filename) + { + strncpy(filename, &(srcPath[fileStart]), fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = 0; + } + + // Only file no path + if(path) + { + path[0] = 0; + } + } +} + +void ddio_CopyFileTime (char *destname,const char *srcname) +{ + struct stat abuf; + + if (stat(srcname,&abuf)) + Int3(); + + struct utimbuf bbuf; + bbuf.actime = abuf.st_atime; + bbuf.modtime = abuf.st_mtime; + + if (utime(destname,&bbuf)) + Int3(); +} + +// deletes a file. Returns 1 if successful, 0 on failure +int ddio_DeleteFile (char *name) +{ + return (!unlink (name)); +} + + +// Save/Restore the current working directory + +static char SavedWorkingDir[_MAX_DIR]; + +void ddio_SaveWorkingDir(void) +{ + ddio_GetWorkingDir(SavedWorkingDir,_MAX_DIR); +} + + +void ddio_RestoreWorkingDir(void) +{ + ddio_SetWorkingDir(SavedWorkingDir); +} + + +// Checks if a directory exists (returns 1 if it does, 0 if not) +// This pathname is *RELATIVE* not fully qualified +bool ddio_DirExists(const char* path) +{ + bool res; + + ddio_SaveWorkingDir(); + res = ddio_SetWorkingDir(path); + ddio_RestoreWorkingDir(); + + return (res) ? true: false; +} + + +// rcg06192000 extern "C" is my add, so nettest would link. +//extern "C" +//{ +// Constructs a path in the local file system's syntax +// newPath: stores the constructed path +// absolutePathHeader: absolute path on which the sub directories will be appended +// (specified in local file system syntax) +// takes a variable number of subdirectories which will be concatenated on to the path +// the last argument in the list of sub dirs *MUST* be NULL to terminate the list +void ddio_MakePath(char* newPath, const char* absolutePathHeader, const char* subDir, ...) +{ + const char delimiter = '/'; + va_list args; + char* currentDir = NULL; + int pathLength = 0; + + ASSERT(newPath); + ASSERT(absolutePathHeader); + ASSERT(subDir); + + if (newPath != absolutePathHeader){ + strcpy(newPath, absolutePathHeader); + } + + // Add the first sub directory + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter){ + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, subDir); + + // Add the additional subdirectories + va_start(args, subDir); + while ((currentDir = va_arg(args, char*)) != NULL){ + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter){ + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, currentDir); + } + va_end(args); +} + + //} + + +//These functions allow one to find a file +//You use FindFileStart by giving it a wildcard (like *.*, *.txt, u??.*, whatever). It returns +// a filename in namebuf. +// Use FindNextFile to get the next file found with the wildcard given in FindFileStart. +// Use FindFileClose to end your search. +glob_t ffres = {0,NULL,0}; +int globindex = -1; +int globerrfn(const char *path,int err) +{ + mprintf((0,"Error accessing %s: %s .... \n",path,strerror(err))); + return 0; +} + + +#if MACOSX +int noglob_findnext(struct find_t *f); +struct find_t +{ + DIR *dir; + char pattern[_MAX_PATH]; + char name[_MAX_PATH]; +}; + +static find_t finder; + +int noglob_findfirst(char *filename, int x, struct find_t *f) +{ + char *ptr; + + if (strlen(filename) >= sizeof (f->pattern)) + return(1); + + strcpy(f->pattern, filename); + //FixFilePath(f->pattern); + ptr = strrchr(f->pattern, '/'); + + if (ptr == NULL) + { + ptr = filename; + f->dir = opendir("."); + } + else + { + *ptr = '\0'; + f->dir = opendir(f->pattern); + memmove(f->pattern, ptr + 1, strlen(ptr + 1) + 1); + } + + return(noglob_findnext(f)); +} + + +static int check_pattern_nocase(const char *x, const char *y) +{ + if ((x == NULL) || (y == NULL)) + return(0); /* not a match. */ + + while ((*x) && (*y)) + { + if (*x == '*') + { + x++; + while (*y != '\0') + { + if (toupper((int) *x) == toupper((int) *y)) + break; + y++; + } + } + + else if (*x == '?') + { + if (*y == '\0') + return(0); /* anything but EOS is okay. */ + } + + else + { + if (toupper((int) *x) != toupper((int) *y)) + return(0); /* not a match. */ + } + + x++; + y++; + } + + return(*x == *y); /* it's a match (both should be EOS). */ +} + +int noglob_findnext(struct find_t *f) +{ + struct dirent *dent; + + if (f->dir == NULL) + return(1); /* no such dir or we're just done searching. */ + + while ((dent = readdir(f->dir)) != NULL) + { + if (check_pattern_nocase(f->pattern, dent->d_name)) + { + if (strlen(dent->d_name) < sizeof (f->name)) + { + strcpy(f->name, dent->d_name); + return(0); /* match. */ + } + } + } + + //closedir(f->dir); + //f->dir = NULL; + return(1); /* no match in whole directory. */ +} +#endif + + +bool ddio_FindFileStart(const char *wildcard, char *namebuf) +{ + ASSERT(wildcard); + ASSERT(namebuf); + if(globindex!=-1) + ddio_FindFileClose(); + +#if MACOSX + if (noglob_findfirst((char *)wildcard, 0, &finder) == 0) + { + strcpy(namebuf, finder.name); + return true; + } + return false; +#else + int rc,flags; + flags = GLOB_MARK | GLOB_PERIOD | GLOB_TILDE; + rc = glob(wildcard,flags,globerrfn,&ffres); + if(rc==GLOB_NOSPACE){ + mprintf((0,"Out of space during glob\n")); + globindex = -1; + return false; + } + if(!ffres.gl_pathc){ + globindex = -1; + return false; + } + + globindex = 0; + + // if the last character is a / then we are to return that last section (it is a directory and + // ddio_splitpath can't handle that). + int gllen = strlen(ffres.gl_pathv[0]); + if(ffres.gl_pathv[0][gllen-1]=='/') + { + // directory to return! + char *ptr = &ffres.gl_pathv[0][gllen-2]; + char *start = &ffres.gl_pathv[0][0]; + char *end = ptr; + while(ptr>=start) + { + if(*ptr=='/') + { + // stop! it's the end of the directory + break; + } + ptr--; + } + if(ptr=ffres.gl_pathc) + return false; + + // if the last character is a / then we are to return that last section (it is a directory and + // ddio_splitpath can't handle that). + int gllen = strlen(ffres.gl_pathv[globindex]); + if(ffres.gl_pathv[globindex][gllen-1]=='/') + { + // directory to return! + char *ptr = &ffres.gl_pathv[globindex][gllen-2]; + char *start = &ffres.gl_pathv[globindex][0]; + char *end = ptr; + while(ptr>=start) + { + if(*ptr=='/') + { + // stop! it's the end of the directory + break; + } + ptr--; + } + if(ptr=fdres.gl_pathv[0] && *endptr!='/')endptr--; + if(*endptr=='/') + { + strcpy(namebuf,endptr+1); + }else + { + strcpy(namebuf,fdres.gl_pathv[0]); + } + return true; + }else + { + return ddio_FindNextDir(namebuf); + } + + namebuf[0] = '\0'; + return false; +} + +bool ddio_FindNextDir(char *namebuf) +{ + ASSERT(namebuf); + if(globdirindex==-1) + return false; + globdirindex++; + if(globdirindex>=fdres.gl_pathc) + return false; + + bool done = false; + bool found = false; + + while( !done ) + { + if(globdirindex>=fdres.gl_pathc) + { + namebuf[0] = '\0'; + done = true; + continue; + } + int glpathlen = strlen(fdres.gl_pathv[globdirindex]); + if(fdres.gl_pathv[globdirindex][glpathlen-1]=='/') + { + //return only the last directory + fdres.gl_pathv[globdirindex][glpathlen-1] = '\0'; //lose the ending '/' + char *endptr = &fdres.gl_pathv[globdirindex][glpathlen-1]; + while(endptr>=fdres.gl_pathv[globdirindex] && *endptr!='/')endptr--; + if(*endptr=='/') + { + strcpy(namebuf,endptr+1); + }else + { + strcpy(namebuf,fdres.gl_pathv[globdirindex]); + } + done = true; + found = true; + }else + { + globdirindex++; + } + } + + return found; +} + +void ddio_FindDirClose() +{ + if(globdirindex==-1) + return; + globdirindex = -1; + globfree(&fdres); +} + + +// pass in a pathname (could be from ddio_SplitPath), root_path will have the drive name. +void ddio_GetRootFromPath(const char *srcPath, char *root_path) +{ + assert(root_path); + assert(srcPath); + + strcpy(root_path,"/"); +} + +// retrieve root names, free up roots array (allocated with malloc) after use +int ddio_GetFileSysRoots(char **roots, int max_roots) +{ + if(max_roots<1) + return 0; + + roots[0] = mem_strdup("/"); + return 1; +} + +// given a path, it cleans it up (if the path is /usr/lib/../src it would make it /usr/src) +// srcPath is the original path +// dest is the finished cleaned path. +// dest should be at least _MAX_PATH in size +void ddio_CleanPath(char *dest,const char* srcPath) +{ + //NOTE: we may want to use getcwd() here if we don't want symbolic links + //but I think we do + //////////////////////////////////////////////////////////////////////// + + strcpy(dest,srcPath); + + //break the path into directories + char **directories; + int dirs; + int path_length; + + //make sure the path ends with a / for sanity + path_length = strlen(dest); + + if(dest[path_length-1]!='/'){ + dest[path_length]='/'; + dest[path_length+1]='\0'; + path_length++; + } + + //now divide the full path into seperate NULL terminated strings,counting the number + //of directories in the process + dirs = 0; + char *strptr; + if(dest[0]=='/') + strptr = dest + 1; //skip first / of root dir + else + strptr = dest; + + while(*strptr!='\0'){ + if(*strptr=='/'){ + *strptr = '\0'; + dirs++; + } + strptr++; + } + + //check to make sure we have a directory, if we don't then return the original path given + if(dirs==0){ + strcpy(dest,srcPath); + return; + } + + //allocate the memory needed for the seperate strings of each directory + directories = (char **)mem_malloc(sizeof(char *)*dirs); + if(!directories){ + strcpy(dest,srcPath); + return; + } + + //now get all the directories, and place into the individual strings + strptr = dest; + int count = 0; + while(count0)&&(dest[path_length-1]=='/')) + dest[path_length-1]='\0'; + + //free up all the allocated memory and we're done + for(count=0;count64) + return false; + + ddio_GetWorkingDir(old_workdir,_MAX_PATH); + + if(!ddio_SetWorkingDir(basedir)) + { + return false; //invalid base directory + } + + char randname[10]; + int index; + int tries = 0; + char rc; + + bool created = false; + + index = 0; + + while(!success && tries<20) + { + //generate a bunch of random characters + rc = (rand()%128); + if( (rc>='a' && rc<='z') || (rc>='A' && rc<='Z') || (rc>='0' && rc<='9') ) + { + //valid character + randname[index] = rc; + index++; + + if(index==10) + { + //we hit the size of our max, see if we generated a unique filename + char t[_MAX_PATH]; + randname[9] = '\0'; + sprintf(t,"%s%s.tmp",prefix,randname); + + //see if we can find this file + FILE *fd = fopen(t,"rb"); + if(!fd) + { + //we found a good file! + ddio_MakePath(filename,basedir,t,NULL); + success = true; + created = true; + }else + { + //already taken + fclose(fd); + tries++; + index = 0; + } + } + }else + { + continue; //try again + } + } + + ddio_SetWorkingDir(old_workdir); + return created; +} + +// Checks to see if a lock file is located in the specified directory. +// Parameters: +// dir Directory for which the lock file should be checked +// Returns: +// 1 Lock file doesn't exist +// 2 Lock file was in a directory, but it belonged to a process that no longer +// exists, so a lock file _can_ be made in the directory. +// 3 Lock file for this process already exists +// 0 Lock file currently exists in directory +// -1 Illegal directory +// -2 There is a lock file in the directory, but it is in an illegal format +int ddio_CheckLockFile(const char *dir) +{ + pid_t curr_pid = getpid(); + +// rcg 06092000 what's all this working dir shite? +// char old_directory[_MAX_PATH]; +// ddio_GetWorkingDir(old_directory,_MAX_PATH); +// if(!ddio_SetWorkingDir(dir)) +// return -1; + + // rcg 06092000 The buffer stuff throughout is my add. + char buffer[strlen(dir) + strlen(".lock") + 2]; + bool found_lock_file_in_dir = false; + FILE *file; + + sprintf(buffer, "%s/%s", dir, ".lock"); + mprintf((0, "LockFile: Checking [%s]...", buffer)); + chmod(buffer,S_IREAD|S_IWRITE); + file = fopen(buffer,"rb"); + + if(!file) + { + //File exists, but couldn't open it + if (errno == EACCES) + { +// ddio_SetWorkingDir(old_directory); + return -2; + } + + found_lock_file_in_dir = false; + }else + { + found_lock_file_in_dir = true; + + //check the file, see if it is a lock file + char c; +// c = fgetc(file); if(c!='L') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} +// c = fgetc(file); if(c!='O') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} +// c = fgetc(file); if(c!='C') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} +// c = fgetc(file); if(c!='K') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} + c = fgetc(file); if(c!='L') {fclose(file); return -2;} + c = fgetc(file); if(c!='O') {fclose(file); return -2;} + c = fgetc(file); if(c!='C') {fclose(file); return -2;} + c = fgetc(file); if(c!='K') {fclose(file); return -2;} + + //it appears to be a lock file, check the pid + pid_t f_pid; + fread(&f_pid,sizeof(pid_t),1,file); + fclose(file); + + //check the file id in the file, compared to our pid + if(f_pid==curr_pid) + { + //lock file already exists for the current process +// ddio_SetWorkingDir(old_directory); + return 3; + } + + if(kill(f_pid, 0) == -1) + { + if(errno == ESRCH) + { + /* pid does not exist */ + + }else + { + /* some other error */ + //technically this shouldn't happen, but I get it + //when the pid no longer exists...so I'm going to + //pretend it doesn't + mprintf((0,"Error sending signal to pid for lock check (%d)\n",f_pid)); + //perror ("Error sending signal to pid for lock check, maybe remove lock file in temp directory"); + //ddio_SetWorkingDir(old_directory); + //return 0; + } + + }else + { + /* pid exists */ +// ddio_SetWorkingDir(old_directory); + return 0; + } + + //the process no longer exists, we can create a lock file if needed + //we'll delete the useless one now + ddio_DeleteFile(".lock"); + } + +// ddio_SetWorkingDir(old_directory); + return (found_lock_file_in_dir)?2:1; +} + +// Creates a lock file in the specified directory +// Parameters: +// dir Directory for which the lock file should be created in +// Returns: +// 1 Lock file created +// 2 Lock file created (there was a lock file in that directory, but it belonged +// to a process that no longer exists) +// 3 Lock file for this process already exists +// 0 Lock file not created, a lock file currently exists in the directory +// -1 Illegal directory +// -2 There is a lock file in the directory, but it is in an illegal format +// -3 Unable to create lock file +int ddio_CreateLockFile(const char *dir) +{ + int result = ddio_CheckLockFile(dir); + switch(result) + { + case 0: + case -1: + case -2: + case 3: + return result; + }; + + pid_t curr_pid = getpid(); + char old_directory[_MAX_PATH]; + ddio_GetWorkingDir(old_directory,_MAX_PATH); + if(!ddio_SetWorkingDir(dir)) + return -1; + + FILE *file; + file = fopen(".lock","wb"); + if(!file) + { + ddio_SetWorkingDir(old_directory); + return -3; + } + + fputc('L',file); + fputc('O',file); + fputc('C',file); + fputc('K',file); + fwrite(&curr_pid,sizeof(pid_t),1,file); + + fclose(file); + ddio_SetWorkingDir(old_directory); + + // at this point result will either be 1 or 2 from checking the lock file + // either way, a lock file has been created + return result; +} + +// Deletes a lock file (for the current process) in the specified directory +// Parameters: +// dir Directory for which the lock file should be deleted from +// Returns: +// 1 Lock file deleted +// 0 Lock file not deleted, the lock file in the directory does not belong to our +// process +// -1 Illegal directory +// -2 A lock file exists in the directory, but wasn't deleted...illegal format +// -3 Unable to delete file +int ddio_DeleteLockFile(const char *dir) +{ + pid_t curr_pid = getpid(); + char old_directory[_MAX_PATH]; + ddio_GetWorkingDir(old_directory,_MAX_PATH); + if(!ddio_SetWorkingDir(dir)) + return -1; + + FILE *file; + + chmod(".lock",S_IREAD|S_IWRITE); + file = fopen(".lock","rb"); + + if(!file) + return 1; + + //check the file, see if it is a lock file + char c; + c = fgetc(file); if(c!='L') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} + c = fgetc(file); if(c!='O') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} + c = fgetc(file); if(c!='C') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} + c = fgetc(file); if(c!='K') {fclose(file); ddio_SetWorkingDir(old_directory); return -2;} + + //it appears to be a lock file, check the pid + pid_t f_pid; + fread(&f_pid,sizeof(pid_t),1,file); + fclose(file); + + //check the file id in the file, compared to our pid + if(f_pid!=curr_pid) + { + //it doesn't belong to + ddio_SetWorkingDir(old_directory); + return 0; + } + + //the lock file in the directory belongs to us! + if(!ddio_DeleteFile(".lock")) + { + ddio_SetWorkingDir(old_directory); + return -3; + } + + ddio_SetWorkingDir(old_directory); + + return 1; +} diff --git a/ddio_lnx/lnxforcefeedback.cpp b/ddio_lnx/lnxforcefeedback.cpp new file mode 100644 index 000000000..e1bcacb98 --- /dev/null +++ b/ddio_lnx/lnxforcefeedback.cpp @@ -0,0 +1,357 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxforcefeedback.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2000/04/18 00:00:33 $ +* $Author: icculus $ +* +* Linux force feedback stub +* +* $Log: lnxforcefeedback.cpp,v $ +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 3 4/16/99 8:28p Jeff + * created more stub functions + * + * 2 4/14/99 1:57a Jeff + * fixed case mismatched #includes + * + * 1 1/18/99 12:13a Jeff +* +* $NoKeywords: $ +*/ + + + +#include "DDAccess.h" +#include +#include +#include + +#include "pserror.h" +#include "pstring.h" +#include "mono.h" +#include "ddio.h" +#include "application.h" +#include "forcefeedback.h" +#include "mem.h" + +bool ddForce_found; //a Force Feedback device was found +bool ddForce_enabled; //Force Feedback is ready and can be used + +// ------------------------------------------------------------------- +// ddio_ff_AttachForce +// Purpose: +// Attaches variables initialized in the general ddio system to +// the force feedback system. +// ------------------------------------------------------------------- +void ddio_ff_AttachForce(void) +{ +} + +// ------------------------------------------------------------------- +// ddio_ff_DetachForce +// Purpose: +// Detaches variables used by the force-feedback system from the +// ddio system +// ------------------------------------------------------------------- +void ddio_ff_DetachForce(void) +{ +} + +// ------------------------------------------------------------------- +// ddio_ff_Init +// Purpose: +// Initialize force feedback if available. +// ------------------------------------------------------------------- +int ddio_ff_Init(void) +{ + ddForce_found = ddForce_enabled = false; + return 0; + +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_Init +// Purpose: +// Creates and acquires all joysticks +// +// Input: +// None +// +// Return: +// # of sticks acquired +// +// Description: +// +// ------------------------------------------------------------------- +int ddio_ffjoy_Init(void) +{ + ddForce_found = ddForce_enabled = false; + return 0; +} + +// ------------------------------------------------------------------- +// ddio_ff_Acquire +// Purpose: +// Acquires a direct input device for use. +// +// Input: +// The device to acquire (use kDI_MaxJoy to acquire all available +// joysticks). +// +// Return: +// # of devices acquired. +// +// Description: +// Call this to gain access to a device after the device has been +// created & after regaining the focus after losing it. +// +// ------------------------------------------------------------------- +int ddio_ff_Acquire(tDevice) +{ + return 0; +} + +// ------------------------------------------------------------------- +// ddio_ff_Unacquire +// Purpose: +// Unacquires a direct input device +// +// Input: +// The device to unacquire (use kDI_MaxJoy to unacquire all available +// joysticks). +// +// Return: +// # of devices unacquired. +// +// Description: +// Call this to lose access to a device after the device has been +// aquired +// +// ------------------------------------------------------------------- +int ddio_ff_Unacquire(tDevice) +{ + return 0; +} + +// ------------------------------------------------------------------- +// ddio_ff_SetCoopLevel +// ------------------------------------------------------------------- +int ddio_ff_SetCoopLevel(tDevice, int) +{ + return 0; +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_Query +// Purpose: +// Besides checking what buttons/axis are available, this function +// also checks for force feedback support. +// ------------------------------------------------------------------- +int ddio_ffjoy_Query(int , int* , int* ) +{ + return 0; +} + + + +/* +======================================================================== + Force Feedback Effect Functions +======================================================================== + + +*/ +// ------------------------------------------------------------------- +// ddio_ff_GetInfo +// Purpose: +// Returns information about the current state of the low-level +// Force Feedback system. +// ------------------------------------------------------------------- +void ddio_ff_GetInfo(bool *ff_found,bool *ff_enabled) +{ + if(ff_found) + *ff_found = false; + if(ff_enabled) + *ff_enabled = false; +} + +// ------------------------------------------------------------------- +// ddio_ffb_Pause +// Purpose: +// Pause the FFB output on the given device. Use ddio_ffb_Continue to +// continue where you left off. +// ------------------------------------------------------------------- +void ddio_ffb_Pause(tDevice) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_Continue +// Purpose: +// Unpause the FFB output on the given device. Complimentary to +// ddio_ffb_Pause. +// ------------------------------------------------------------------- +void ddio_ffb_Continue(tDevice) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_Enable +// Purpose: +// Must be called after initialization in order to activate the +// device. +// Use ddio_ffb_Pause & ddio_ffb_Continue if you want disable forces +// temporarily and resume later. +// ------------------------------------------------------------------- +void ddio_ffb_Enable(tDevice) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_Disable +// Purpose: +// Turns off FFB, but effects still play on processor. +// ------------------------------------------------------------------- +void ddio_ffb_Disable(tDevice) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectCreate +// Purpose: +// Create a single effect for future playback. +// Effect is given a logical ID +// ------------------------------------------------------------------- +int ddio_ffb_effectCreate(tDevice, tFFB_Effect* ) +{ + return 0; +} + +// ------------------------------------------------------------------- +// ddio_ffb_DestroyAll +// Purpose: +// Destroys all created effects +// ------------------------------------------------------------------- +void ddio_ffb_DestroyAll(void) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectPlay +// Purpose: +// Play an effect that was previously created. +// ------------------------------------------------------------------- +void ddio_ffb_effectPlay(short) +{ + +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectStop +// Purpose: +// Stop a single effect. +// ------------------------------------------------------------------- +void ddio_ffb_effectStop(short) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectStopAll +// Purpose: +// Stops all forces on the given device. +// ------------------------------------------------------------------- +void ddio_ffb_effectStopAll(tDevice) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectUnload +// Purpose: +// Unload a single effect... Necessary to make room for other +// effects. +// ------------------------------------------------------------------- +void ddio_ffb_effectUnload(short) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectModify +// Purpose: +// Modifies a single effect, only if the given parameters are +// different from what's currently loaded. +// ------------------------------------------------------------------- +void ddio_ffb_effectModify(short, int* , unsigned int* , unsigned int* , unsigned int* , tEffInfo* , tEffEnvelope* ) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffb_GetEffectData +// Purpose: +// Retrieves affect data for the given parameters, pass NULL for those you don't want +// ------------------------------------------------------------------- +void ddio_ffb_GetEffectData(short, int* , unsigned int* , unsigned int* , unsigned int* , tEffInfo* , tEffEnvelope* ) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_EnableAutoCenter +// Purpose: +// Disables/Enables the autocentering of the joystick +// ------------------------------------------------------------------- +void ddio_ffjoy_EnableAutoCenter(tDevice,bool) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_SetGain +// Purpose: +// Sets the gain for joystick, pass a value of 0-1 +// ------------------------------------------------------------------- +void ddio_ffjoy_SetGain(tDevice,float ) +{ +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_IsAutoCentered +// Purpose: +// Returns true if the joystick is set for autocentering +// ------------------------------------------------------------------- +bool ddio_ffjoy_IsAutoCentered(tDevice) +{ + return true; +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_SupportAutoCenter +// Purpose: +// Returns true if the FF joystick supports auto centering +// ------------------------------------------------------------------- +bool ddio_ffjoy_SupportAutoCenter(tDevice) +{ + return false; +} + +// Given a filename resource, this loads the file and creates a resource +// for it. It returns a handle to that resource. +// If it returns NULL, then it couldn't load the project. +// Make sure device is aquired before calling. +FORCEPROJECT ddio_ForceLoadProject(char *filename,tDevice dev) +{ + return NULL; +} + +// Unloads a FORCEPROJECT file +void ddio_ForceUnloadProject(FORCEPROJECT prj) +{ +} + +// Given a handle to a resource, and the name of the effect to load +// it will load that effect. Returns the effect ID, or -1 if it couldn't +// be created +int ddio_CreateForceFromProject(FORCEPROJECT project,char *forcename) +{ + return -1; +} diff --git a/ddio_lnx/lnxio.cpp b/ddio_lnx/lnxio.cpp new file mode 100644 index 000000000..168901952 --- /dev/null +++ b/ddio_lnx/lnxio.cpp @@ -0,0 +1,214 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxio.cpp $ +* $Revision: 1.6 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* Linux IO routines +* +* $Log: lnxio.cpp,v $ +* Revision 1.6 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.5 2000/05/29 05:20:13 icculus +* Removed a real repetitive mprintf(). +* +* Revision 1.4 2000/05/29 05:19:17 icculus +* Serial behaviour is now correct (and the Rock'n'Ride works!). A +* little more debugging code was added. +* +* Revision 1.3 2000/04/25 06:10:27 icculus +* Added buttloads of debug information, and fixed some more glitches. +* +* Revision 1.2 2000/04/24 03:18:53 icculus +* Serial port i/o code written, so I can ROCK'n'RIDE it! +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 5 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +// ---------------------------------------------------------------------------- +// Linux IO System Main Library Interface +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "pserror.h" +#include "application.h" +#include "ddio_lnx.h" +#include "ddio.h" + +bool DDIO_init = false; +oeLnxApplication *Lnx_app_obj = NULL; +static struct termios oldtio[4]; +static int portfd[4] = {-1, -1, -1, -1}; + +// ---------------------------------------------------------------------------- +// Initialization and destruction functions +// ---------------------------------------------------------------------------- + +bool ddio_InternalInit(ddio_init_info *init_info) +{ + mprintf((0, "DDIO: ddio_InternalInit() called.")); + Lnx_app_obj = (oeLnxApplication *)init_info->obj; + DDIO_init = true; + return true; +} + + +void ddio_InternalClose() +{ + mprintf((0, "DDIO: ddio_InternalClose() called.")); + + if (DDIO_init) + { + for (int i = 0; i < (sizeof (portfd) / sizeof (portfd[0])); i++) + { + if (portfd[i] != -1) + ddio_SerialClosePort((tSerialPort) &portfd[i]); + } // for + + DDIO_init = false; + Lnx_app_obj = NULL; + } // if + + mprintf((0, "DDIO: ddio_InternalClose() returning.")); +} + + +void ddio_DebugMessage(unsigned err, char *fmt, ...) +{ + char buf[128]; + va_list arglist; + + va_start(arglist,fmt); + vsprintf(buf,fmt,arglist); + va_end(arglist); + + mprintf((0, "%s\n", buf)); +} + +// takes port number 1-4, returns a port object +tSerialPort ddio_SerialOpenPort(int port_number, int baud) +{ +#if MACOSX + return NULL; +#else + char devName[50]; + struct termios newtio; + unsigned long _baud; + + mprintf((0, "DDIO: ddio_SerialOpenPort(%d) called.", port_number)); + + if ((port_number < 0) || (port_number > 3)) + return(NULL); + + if (baud == 1200) _baud = B1200; + else if (baud == 2400) _baud = B2400; + else if (baud == 4800) _baud = B4800; + else if (baud == 9600) _baud = B9600; + else if (baud == 19200) _baud = B19200; + else if (baud == 38400) _baud = B38400; + else if (baud == 57600) _baud = B57600; + else if (baud == 115200) _baud = B115200; + else return(NULL); + + snprintf(devName, sizeof (devName), "/dev/ttyS%d", port_number); + int fd = open(devName, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK); + if (fd == -1) + { + mprintf((0, "DDIO: ddio_SerialOpenPort(%d) FAILED.", port_number)); + return(NULL); + } // if + + mprintf((0, "DDIO: opened file descriptor is (%d).", fd)); + + portfd[port_number] = fd; + + tcgetattr(fd, &oldtio[port_number]); + memset(&newtio, '\0', sizeof (newtio)); + + newtio.c_cflag = _baud | CLOCAL | CS8; //_baud | CRTSCTS | CLOCAL | CS8; + newtio.c_iflag = 0; //IGNPAR | IGNBRK; + newtio.c_oflag = 0; + newtio.c_lflag = 0; //ICANON; + + newtio.c_cc[VINTR] = 0; /* Ctrl-c */ + newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */ + newtio.c_cc[VERASE] = 0; /* del */ + newtio.c_cc[VKILL] = 0; /* @ */ + newtio.c_cc[VEOF] = 4; /* Ctrl-d */ + newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ + newtio.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */ + newtio.c_cc[VSWTC] = 0; /* '\0' */ + newtio.c_cc[VSTART] = 0; /* Ctrl-q */ + newtio.c_cc[VSTOP] = 0; /* Ctrl-s */ + newtio.c_cc[VSUSP] = 0; /* Ctrl-z */ + newtio.c_cc[VEOL] = 0; /* '\0' */ + newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */ + newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */ + newtio.c_cc[VWERASE] = 0; /* Ctrl-w */ + newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */ + newtio.c_cc[VEOL2] = 0; /* '\0' */ + + tcflush(fd, TCIOFLUSH); + tcsetattr(fd, TCSANOW, &newtio); + + mprintf((0, "DDIO: ddio_SerialOpenPort(%d) succeeded.", port_number)); + return(&portfd[port_number]); +#endif +} + +// takes port structure and frees it. +void ddio_SerialClosePort(tSerialPort port) +{ +#if !MACOSX + int fd = *((int *) port); + int index = (int) (( ((char *) port) - ((char *) &portfd) ) / sizeof (int)); + + mprintf((0, "DDIO: ddio_SerialClosePort(comport == %d) called.", index)); + mprintf((0, "DDIO: Serial port file descriptor to close is (%d).", fd)); + + if (fd != -1) + { + tcsetattr(fd, TCSANOW, &oldtio[index]); + close(fd); + *((int *) port) = -1; + } // if +#endif +} + +// writes one byte. true return value means it worked. +bool ddio_SerialWriteByte(tSerialPort port, ubyte b) +{ +#if MACOSX + return false; +#else + int fd = *((int *) port); + bool retVal = ( (write(fd, &b, sizeof (b)) == sizeof (b)) ? true : false ); + if (retVal == false) + { + mprintf((0, "DDIO: Writing byte (%u) to descriptor (%d) failed!", + (unsigned int) b, fd)); + } // if + + return(retVal); +#endif +} + diff --git a/ddio_lnx/lnxjoy.cpp b/ddio_lnx/lnxjoy.cpp new file mode 100644 index 000000000..4cfdbeacb --- /dev/null +++ b/ddio_lnx/lnxjoy.cpp @@ -0,0 +1,624 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxjoy.cpp $ +* $Revision: 1.4 $ +* $Date: 2000/06/29 06:41:23 $ +* $Author: icculus $ +* +* Linux joystick routines +* +* $Log: lnxjoy.cpp,v $ +* Revision 1.4 2000/06/29 06:41:23 icculus +* mad commits. +* +* Revision 1.3 2000/06/24 01:15:15 icculus +* patched to compile. +* +* Revision 1.2 2000/05/29 05:21:09 icculus +* Changed a fprintf(stderr, ...) to an mprintf()... +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 9 8/22/99 5:55p Jeff + * fixed assert + * + * 8 8/19/99 3:46p Jeff + * removed mprintfs + * + * 7 8/19/99 3:22p Jeff + * added support for joystick driver version pre 1.0 + * + * 6 8/18/99 9:47p Jeff + * joystick support! for kernel 2..2+ clients...need to handle before that + * still. + * + * 5 8/17/99 2:32p Jeff + * fixed joy_GetPos + * + * 4 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + + +#include +#include "joystick.h" +#include "pserror.h" +#include "pstypes.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//rcg06182000 need this for specific joystick stuff. +#include "args.h" + +#define JOY_DEV "/dev/js" + +typedef struct tJoystickRecord +{ + bool valid; // is this a valid device. + bool remote; // is this device on another computer + bool use_old_driver; // use pre joystick 1.0 driver interface + tJoystick joy; // joystick + union + { + int joyid; + } + id; // information on accessing the device + tJoyInfo caps; // controller capabilities + tJoyPos pos; // current position +} +tJoystickRecord; + + +// --------------------------------------------------------------------------- +// globals + +static int specificJoy = 0; +static tJoystickRecord Joysticks[MAX_JOYSTICKS]; +static int Joy_initialized = 0; // is our joystick system initialized? +void joy_Close(); +bool joy_InitRemoteStick(tJoystick joy, char *server_adr, tJoystickRecord *stick); +void joy_GetState(tJoystick stick, tJoyPos *pos); +void joy_GetRemoteState(tJoystick stick, tJoyPos *pos); +int joyGetNumDevs(void); +// closes a stick +// closes connection with controller. +void joy_CloseStick(tJoystick joy); +// initializes a joystick +// if server_adr is valid, a link is opened to another machine with a controller. +bool joy_InitStick(tJoystick joy, char *server_adr); + +// --------------------------------------------------------------------------- +// functions + + +// joystick system initialization +bool joy_Init(bool remote) +{ + int i; + + // reinitialize joystick if already initialized. + if (Joy_initialized) { + joy_Close(); + } + else { + atexit(joy_Close); + } + + // check if this OS supports joysticks + if (!joyGetNumDevs() && !remote) { + Joy_initialized = 0; + return false; + } + + Joy_initialized = 1; + + //rcg06182000 specific joystick support. + if (specificJoy) + { + Joysticks[0].valid = false; + joy_InitStick((tJoystick)0, NULL); + } // if + else + { + // initialize joystick list + for (i = 0; i < MAX_JOYSTICKS; i++) + { + Joysticks[i].valid = false; + joy_InitStick((tJoystick)i,NULL); + } + } // else + + return true; +} + + +void joy_Close() +{ + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + if (Joysticks[i].valid) + joy_CloseStick((i == 0) ? JOYSTICK_1 : (i==1) ? JOYSTICK_2 : JOYSTICK_1); +} + + +// initializes a joystick +// if server_adr is valid, a link is opened to another machine with a controller. +bool joy_InitStick(tJoystick joy, char *server_adr) +{ + ASSERT(Joy_initialized); + + // close down already open joystick. + if (Joysticks[(int)joy].valid) + joy_CloseStick(joy); + + // okay, now if this is a remote joystick, open it + if (server_adr) { + return joy_InitRemoteStick(joy, server_adr, &Joysticks[(int)joy]); + } + else { + int fd; + char joy_dev[32]; + int num_of_axis=0, num_of_buttons=0; + char name_of_joystick[80]; + + // rcg06182000 specific joy support. + if (specificJoy) + strcpy(joy_dev, GameArgs[FindArgChar("-joystick", 'j') + 1]); + else + sprintf(joy_dev,"%s%d",JOY_DEV,(int)joy); + + if( ( fd = open( joy_dev , O_RDONLY)) == -1 ) + { + if(errno==EBUSY) + { + mprintf((0, "JOYSTICK: Couldn't open joystick %d, in use.", joy)); + } + return false; + } + + // determine what version of the joystick driver we are using + unsigned int version = 0; + ioctl( fd, JSIOCGVERSION, &version); + if(version<0x10000) + { + Joysticks[joy].use_old_driver = true; + }else + { + Joysticks[joy].use_old_driver = false; + } + + fcntl( fd, F_SETFL, O_NONBLOCK ); + + if(Joysticks[joy].use_old_driver) + { + num_of_axis = 2; + num_of_buttons = 2; + strcpy(name_of_joystick,"Generic"); + + mprintf((0, "Joystick %d opened: Using OLD JOYSTICK driver, %d buttons and %d axis\n",(int)joy,num_of_buttons,num_of_axis)); + + Joysticks[joy].caps.minx = 0; + Joysticks[joy].caps.maxx = 255; + Joysticks[joy].caps.miny = 0; + Joysticks[joy].caps.maxy = 255; + Joysticks[joy].caps.minz = 0; + Joysticks[joy].caps.maxz = 255; + Joysticks[joy].caps.minr = 0; + Joysticks[joy].caps.maxr = 255; + Joysticks[joy].caps.minu = -32767; + Joysticks[joy].caps.maxu = 32767; + Joysticks[joy].caps.minv = -32767; + Joysticks[joy].caps.maxv = 32767; + }else + { + ioctl( fd, JSIOCGAXES, &num_of_axis ); + ioctl( fd, JSIOCGBUTTONS, &num_of_buttons ); + ioctl( fd, JSIOCGNAME(80), &name_of_joystick ); + + mprintf((0, "Joystick %d opened: %s with %d buttons and %d axis\n",(int)joy,name_of_joystick,num_of_buttons,num_of_axis)); + + Joysticks[joy].caps.minx = -32767; + Joysticks[joy].caps.maxx = 32767; + Joysticks[joy].caps.miny = -32767; + Joysticks[joy].caps.maxy = 32767; + Joysticks[joy].caps.minz = -32767; + Joysticks[joy].caps.maxz = 32767; + Joysticks[joy].caps.minr = -32767; + Joysticks[joy].caps.maxr = 32767; + Joysticks[joy].caps.minu = -32767; + Joysticks[joy].caps.maxu = 32767; + Joysticks[joy].caps.minv = -32767; + Joysticks[joy].caps.maxv = 32767; + } + + Joysticks[joy].valid = true; + Joysticks[joy].remote = false; + Joysticks[joy].joy = joy; + Joysticks[joy].id.joyid = fd; + + strcpy(Joysticks[joy].caps.name,name_of_joystick); + Joysticks[joy].caps.num_btns = num_of_buttons; + Joysticks[joy].caps.axes_mask = JOYFLAG_XVALID|JOYFLAG_YVALID; + + if(num_of_axis>2) + Joysticks[joy].caps.axes_mask |= JOYFLAG_ZVALID; + if(num_of_axis>3) + Joysticks[joy].caps.axes_mask |= JOYFLAG_RVALID; + if(num_of_axis>4) + { + // rcg06182000 God, I hope this works. + // rcg06182000 Attempt to hack 2 more axes into this driver... + // rcg06182000 Before, it was just JOYFLAG_POVVALID... + Joysticks[joy].caps.axes_mask |= JOYFLAG_UVALID; + Joysticks[joy].caps.axes_mask |= JOYFLAG_VVALID; + + Joysticks[joy].caps.axes_mask |= JOYFLAG_POVVALID; + } // if + memset(&Joysticks[joy].pos,0,sizeof(tJoyPos)); + + return true; + } + + return false; +} + + +//this function takes a server address and tries to initialize a stick that's on a machine +//running the remote control server. +bool joy_InitRemoteStick(tJoystick joy, char *server_adr, tJoystickRecord *stick) +{ + return false; +} + + +// closes a stick +// closes connection with controller. +void joy_CloseStick(tJoystick joy) +{ + ASSERT(Joysticks[(int)joy].valid); + + if (Joysticks[(int)joy].remote) { + } + + //CLOSE joystick here + close( Joysticks[(int)joy].id.joyid ); + Joysticks[(int)joy].valid = false; +} + + +// returns true if joystick valid +bool joy_IsValid(tJoystick joy) +{ + if (Joysticks[(int)joy].valid) return true; + else return false; +} + + +// retreive information about joystick. +void joy_GetJoyInfo(tJoystick joy, tJoyInfo *info) +{ + ASSERT(Joy_initialized); + + if (Joysticks[(int)joy].valid) { + memcpy(info, &Joysticks[(int)joy].caps, sizeof(tJoyInfo)); + } + else + Int3(); // This should never happen. +} + + +// retreive uncalibrated position of joystick +#define LNX_JOYAXIS_RANGE 65535 +void joy_GetRawPos(tJoystick joy, tJoyPos *pos) +{ + joy_GetPos(joy, pos); + + if(!Joysticks[joy].use_old_driver) + { + pos->x = (pos->x+32767); + pos->y = (pos->y+32767); + pos->z = (pos->z+32767); + pos->r = (pos->r+32767); + pos->u = (pos->u+32767); + pos->v = (pos->v+32767); + } +} + + +// returns the state of a stick, remote or otherwise +void joy_GetPos(tJoystick stick, tJoyPos *pos) +{ + int i; + + for (i = 0; i < JOYPOV_NUM; i++) + { + pos->pov[i] = 0; + } + pos->x = 0; + pos->y = 0; + pos->z = 0; + pos->r = 0; + pos->u = 0; + pos->v = 0; + pos->buttons = 0; + pos->btn = 0; + + if(!Joy_initialized) + return; + + if (!Joysticks[(int)stick].valid) + return; + + // retrieve joystick info from the net, or locally. + if (Joysticks[(int)stick].remote) { + joy_GetRemoteState(stick, pos); + } + else { + memcpy(pos,&Joysticks[(int)stick].pos,sizeof(tJoyPos)); + } +} + + +// returns the position of a remote stick. +void joy_GetRemoteState(tJoystick stick, tJoyPos *pos) +{ +} + + + +int joyGetNumDevs(void) +{ + int found = 0; + int count = 0,fd; + char joy_dev[80]; + + // rcg06182000 add support for specific joydev. + int rc = FindArgChar("-joystick", 'j'); + if ((rc > 0) && (GameArgs[rc + 1] != NULL)) + { + fd = open(GameArgs[rc + 1], O_RDONLY); + if(fd != -1 ) + { + specificJoy = 1; + close(fd); + return(1); + } // if + } // if + // rcg end addition + + for(count=0;countpov[0]) + { + case 0x00: //up + new_pov = PV_UP; + break; + case 0x20: //up-right; + new_pov = PV_UP|PV_RIGHT; + break; + case 0x40: //right + new_pov = PV_RIGHT; + break; + case 0x60: //down-right + new_pov = PV_DOWN|PV_RIGHT; + break; + case 0x80: //down + new_pov = PV_DOWN; + break; + case 0xA0: //down-left + new_pov = PV_DOWN|PV_LEFT; + break; + case 0xC0: //left + new_pov = PV_LEFT; + break; + case 0xE0: //up-left + new_pov = PV_UP|PV_LEFT; + break; + } + + FD_ZERO(&watchset); + FD_SET(Joysticks[joy].id.joyid,&watchset); + + while(1 && count<50) + { + if(select(Joysticks[joy].id.joyid+1,&watchset,NULL,NULL,&tv)<0) + { + perror("Error reading joystick file descriptor \n"); + break; + } + + if(!FD_ISSET(Joysticks[joy].id.joyid,&watchset)) + break; //done reading, nothing waiting + + + if(Joysticks[joy].use_old_driver) + { + if( read( Joysticks[joy].id.joyid, &old_js, JS_RETURN ) != JS_RETURN ) + { + break; + } + + pos->x = old_js.x; + pos->y = old_js.y; + pos->buttons = 0; + + pos->buttons |= (old_js.buttons&1)?0x01:0; + pos->buttons |= (old_js.buttons&2)?0x02:0; + + break; + }else + { + // read the joystick state + read(Joysticks[joy].id.joyid, &js, sizeof(struct js_event)); + + // see what to do with the event + switch (js.type & ~JS_EVENT_INIT) + { + case JS_EVENT_AXIS: + switch(js.number) + { + case 0: // X Axis + pos->x = js.value; + break; + case 1: // Y Axis + pos->y = js.value; + break; + case 2: // Z Axis + pos->z = js.value; + break; + case 3: // R Axis + pos->r = js.value; + break; + case 4: // POV U Axis + // rcg 06152000 set pos->u. Wasn't here. !? + pos->u = js.value; + + + new_pov &= ~(PV_LEFT|PV_RIGHT); + + if(js.value>0) + { + //mprintf((0,"POV RIGHT\n")); + new_pov |= PV_RIGHT; + } + else if(js.value<0) + { + //mprintf((0,"POV LEFT\n")); + new_pov |= PV_LEFT; + } + break; + case 5: // POV V Axis + // rcg 06152000 set pos->v. Wasn't here. !? + pos->v = js.value; + + + new_pov &= ~(PV_UP|PV_DOWN); + + if(js.value>0) + { + //mprintf((0,"POV DOWN\n")); + new_pov |= PV_DOWN; + } + else if(js.value<0) + { + //mprintf((0,"POV UP\n")); + new_pov |= PV_UP; + } + break; + } + + break; + case JS_EVENT_BUTTON: + int bit; + bit = 0x01; + bit = bit<buttons |= bit; + }else + { + //button release + pos->buttons &= ~bit; + } + break; + } + } + + count++; + } + + if(count==50) + { + //mprintf((0,"JOYSTICK BREAK!\n")); + } + + //recalc pov + switch(new_pov) + { + case 0: //center + pos->pov[0] = JOYPOV_CENTER; + break; + case PV_UP: + pos->pov[0] = 0x00; + break; + case PV_UP|PV_RIGHT: + pos->pov[0] = 0x20; + break; + case PV_RIGHT: + pos->pov[0] = 0x40; + break; + case PV_DOWN|PV_RIGHT: + pos->pov[0] = 0x60; + break; + case PV_DOWN: + pos->pov[0] = 0x80; + break; + case PV_DOWN|PV_LEFT: + pos->pov[0] = 0xA0; + break; + case PV_LEFT: + pos->pov[0] = 0xC0; + break; + case PV_UP|PV_LEFT: + pos->pov[0] = 0xE0; + break; + } + } +} + + diff --git a/ddio_lnx/lnxkey.cpp b/ddio_lnx/lnxkey.cpp new file mode 100644 index 000000000..0c7f49a27 --- /dev/null +++ b/ddio_lnx/lnxkey.cpp @@ -0,0 +1,363 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxkey.cpp $ +* $Revision: 1.3 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* Linux keyboard routines +* +* $Log: lnxkey.cpp,v $ +* Revision 1.3 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.2 2000/04/27 11:17:07 icculus +* Removed some keyboard drivers, added SDL hooks. +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 13 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +/* + * Linux Keyboard Interface, Non-SVGALIB Implementation + * $NoKeywords: $ + */ + +// ---------------------------------------------------------------------------- +// Keyboard Interface +// ---------------------------------------------------------------------------- +#include "pserror.h" +#include "mono.h" +#include "ddio.h" +#include "ddio_lnx.h" +#include "application.h" +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +#include +#include +//#include +#include +#include +#include +#include +//#include + +volatile struct tLnxKeys +{ + union{ + int up_ticks; + float up_time; + }; + union{ + int down_ticks; + float down_time; + }; + bool status; +}LKeys[DDIO_MAX_KEYS]; + +//normal mode ddio +bool ddio_normal_InternalKeyInit(ddio_init_info *init_info); +void ddio_normal_InternalKeyClose(); +bool ddio_normal_InternalKeyState(ubyte key); +void ddio_normal_InternalKeySuspend(); +void ddio_normal_InternalKeyResume(); +float ddio_normal_InternalKeyDownTime(ubyte key); +void ddio_normal_InternalResetKey(ubyte key); +bool ddio_normal_KeyFrame(); +void ddio_normal_InternalKeyFrame(void); + +//null mode ddio +bool ddio_null_InternalKeyInit(ddio_init_info *init_info); +void ddio_null_InternalKeyClose(); +bool ddio_null_InternalKeyState(ubyte key); +void ddio_null_InternalKeySuspend(); +void ddio_null_InternalKeyResume(); +float ddio_null_InternalKeyDownTime(ubyte key); +void ddio_null_InternalResetKey(ubyte key); +bool ddio_null_KeyFrame(); +void ddio_null_InternalKeyFrame(void); + +// sdl mode ddio +bool ddio_sdl_InternalKeyInit(ddio_init_info *init_info); +void ddio_sdl_InternalKeyClose(); +bool ddio_sdl_InternalKeyState(ubyte key); +void ddio_sdl_InternalKeySuspend(); +void ddio_sdl_InternalKeyResume(); +float ddio_sdl_InternalKeyDownTime(ubyte key); +void ddio_sdl_InternalResetKey(ubyte key); +bool ddio_sdl_KeyFrame(); +void ddio_sdl_InternalKeyFrame(void); + +/* +//xwin mode ddio +bool ddio_xwin_InternalKeyInit(ddio_init_info *init_info); +void ddio_xwin_InternalKeyClose(); +bool ddio_xwin_InternalKeyState(ubyte key); +void ddio_xwin_InternalKeySuspend(); +void ddio_xwin_InternalKeyResume(); +float ddio_xwin_InternalKeyDownTime(ubyte key); +void ddio_xwin_InternalResetKey(ubyte key); +void ddio_xwin_InternalKeyFrame(void); + +//svga mode ddio +bool ddio_svga_InternalKeyInit(ddio_init_info *init_info); +void ddio_svga_InternalKeyClose(); +bool ddio_svga_InternalKeyState(ubyte key); +void ddio_svga_InternalKeySuspend(); +void ddio_svga_InternalKeyResume(); +float ddio_svga_InternalKeyDownTime(ubyte key); +void ddio_svga_InternalResetKey(ubyte key); +bool ddio_svga_KeyFrame(); +void ddio_svga_InternalKeyFrame(void); +*/ + +enum{ + Input_normal,Input_null,Input_sdl //Input_svga,Input_xwin +}Keyboard_mode; + +// ---------------------------------------------------------------------------- +// Initialization of keyboard device. +// ---------------------------------------------------------------------------- + +bool ddio_InternalKeyInit(ddio_init_info *init_info) +{ + oeLnxApplication *app = (oeLnxApplication *)init_info->obj; + tLnxAppInfo app_info; + + if(!app) + { + return false; + } + + app->get_info(&app_info); + + //determine if we are to use normal or null mode + Keyboard_mode = Input_normal; + if(app_info.flags&APPFLAG_USESERVICE) + { + //use null mode! + Keyboard_mode = Input_null; + }else + { + Keyboard_mode = Input_sdl; +/* + if(app_info.flags&APPFLAG_USESVGA) + { + //use svgalib mode! + Keyboard_mode = Input_svga; + } + else if (app_info.m_Display) { + // we're under xwindows and we're not forcing any type of keyboard input. + Keyboard_mode = Input_xwin; + } +*/ + } + + switch(Keyboard_mode) + { + case Input_normal: + return ddio_normal_InternalKeyInit(init_info); + case Input_null: + return ddio_null_InternalKeyInit(init_info); + case Input_sdl: + return ddio_sdl_InternalKeyInit(init_info); + +// case Input_svga: +// return ddio_svga_InternalKeyInit(init_info); +// case Input_xwin: +// return ddio_xwin_InternalKeyInit(init_info); + } + + return false; +} + +void ddio_InternalKeyClose() +{ + switch(Keyboard_mode) + { + case Input_normal: + return ddio_normal_InternalKeyClose(); + case Input_null: + return ddio_null_InternalKeyClose(); + case Input_sdl: + return ddio_sdl_InternalKeyClose(); +/* + case Input_svga: + return ddio_svga_InternalKeyClose(); + case Input_xwin: + return ddio_xwin_InternalKeyClose(); +*/ + } +} + +bool ddio_InternalKeyState(ubyte key) +{ + switch(Keyboard_mode) + { + case Input_normal: + return ddio_normal_InternalKeyState(key); + case Input_null: + return ddio_null_InternalKeyState(key); + case Input_sdl: + return ddio_sdl_InternalKeyState(key); +/* + case Input_svga: + return ddio_svga_InternalKeyState(key); + case Input_xwin: + return ddio_xwin_InternalKeyState(key); +*/ + } + + return false; +} + +void ddio_InternalKeySuspend() +{ + switch(Keyboard_mode) + { + case Input_normal: + ddio_normal_InternalKeySuspend(); + break; + case Input_null: + ddio_null_InternalKeySuspend(); + break; + case Input_sdl: + return ddio_sdl_InternalKeySuspend(); + +/* + case Input_svga: + ddio_svga_InternalKeySuspend(); + break; + case Input_xwin: + ddio_xwin_InternalKeySuspend(); + break; +*/ + } +} + +void ddio_InternalKeyResume() +{ + switch(Keyboard_mode) + { + case Input_normal: + ddio_normal_InternalKeyResume(); + break; + case Input_null: + ddio_null_InternalKeyResume(); + break; + case Input_sdl: + return ddio_sdl_InternalKeyResume(); + +/* + case Input_svga: + ddio_svga_InternalKeyResume(); + break; + case Input_xwin: + ddio_xwin_InternalKeyResume(); + break; +*/ + } +} + +float ddio_InternalKeyDownTime(ubyte key) +{ + switch(Keyboard_mode) + { + case Input_normal: + return ddio_normal_InternalKeyDownTime(key); + case Input_null: + return ddio_null_InternalKeyDownTime(key); + case Input_sdl: + return ddio_sdl_InternalKeyDownTime(key); +/* + case Input_svga: + return ddio_svga_InternalKeyDownTime(key); + case Input_xwin: + return ddio_xwin_InternalKeyDownTime(key); +*/ + } + + return 0.0f; +} + +void ddio_InternalResetKey(ubyte key) +{ + switch(Keyboard_mode) + { + case Input_normal: + ddio_normal_InternalResetKey(key); + break; + case Input_null: + ddio_null_InternalResetKey(key); + break; + case Input_sdl: + return ddio_sdl_InternalResetKey(key); +/* + case Input_svga: + ddio_svga_InternalResetKey(key); + break; + case Input_xwin: + ddio_xwin_InternalResetKey(key); + break; +*/ + } +} + +bool ddio_KeyFrame() +{ + switch(Keyboard_mode) + { + case Input_normal: + return ddio_normal_KeyFrame(); + case Input_null: + return ddio_null_KeyFrame(); +/* + case Input_svga: + return ddio_svga_KeyFrame(); +*/ + } + + return true; +} + + +void ddio_InternalKeyFrame(void) +{ + switch(Keyboard_mode) + { + case Input_normal: + ddio_normal_InternalKeyFrame(); + break; + case Input_null: + ddio_null_InternalKeyFrame(); + break; + case Input_sdl: + return ddio_sdl_InternalKeyFrame(); +/* + case Input_svga: + ddio_svga_InternalKeyFrame(); + break; + case Input_xwin: + ddio_xwin_InternalKeyFrame(); + break; +*/ + } +} + +void ddio_SetKeyboardLanguage(int) +{ +} + + diff --git a/ddio_lnx/lnxkey_ggi.cpp b/ddio_lnx/lnxkey_ggi.cpp new file mode 100644 index 000000000..04df61743 --- /dev/null +++ b/ddio_lnx/lnxkey_ggi.cpp @@ -0,0 +1,512 @@ +// Keyboard handler for SVGAlib + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ddio_common.h" +#include "ddio.h" +#include "mono.h" + +//########################################################## +//GGI Interface Functions +//########################################################## +void ddio_ggi_EmergencyQuit(int id); +void ddio_ggi_InternalKeyInit(void); +static int ddio_ggi_TranslateKey(int c, int code); +void ddio_ggi_DoKeyFrame(void); +void ddio_ggi_InternalClose(void); +void ddio_ggi_KeyHandler(void); +gii_input_t GGI_input_handle; +void ddio_ggi_KeyBoardEventHandler(int transkey,int press); +//########################################################## + +volatile struct tLnxKeys +{ + union{ + int up_ticks; + float up_time; + }; + union{ + int down_ticks; + float down_time; + }; + bool status; +}LKeys[DDIO_MAX_KEYS]; +bool DDIO_key_suspend = false; + + +bool ddio_InternalKeyInit(bool preemptive) +{ + static bool first_call = true; + //reset key list + for(int i=0;i', '?', 255,255, + 255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,255, + 255, 255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255 }; + + +// converts keycode to ASCII +int ddio_KeyToAscii(int code) +{ + int shifted; + + shifted = code & KEY_SHIFTED; + code &= 0xFF; + + if ( code>=127 ) + { + return 255; + } + + int out; + if (shifted) + { + out = (int)shifted_ascii_table[code]; + }else + { + out = (int)ascii_table[code]; + } + return out; +} + + + +//################################################# +//ggi Interface Functions +//################################################# +void ddio_ggi_InternalKeyInit(void) +{ + static bool first_time = true; + + if(!first_time) + { + return; + } + first_time = false; + + if (giiInit()) + { + mprintf((0,"Unable to initialize GII!\n")); + exit(1); + } + + gii_input_t inp2; + // Open the nulldevice for testing ... + if ((GGI_input_handle=giiOpen("input-null",NULL)) == NULL) { + giiPanic("Unable to open input-null\n"); + exit(1); + } + + // Open stdin for testing ... + if ((inp2=giiOpen("input-stdin",NULL)) == NULL) { + giiPanic("Unable to open input-stdin\n"); + exit(1); + } + + // Now join them. Note the usage of _i_n_p_=_giiJoin(inp,inp2); + // This is the recommended way to do this. + GGI_input_handle = giiJoinInputs(GGI_input_handle,inp2); + + giiSetEventMask(GGI_input_handle, emKey); + + struct sigaction sact; + sact.sa_handler = ddio_ggi_EmergencyQuit; + sigfillset(&sact.sa_mask); + sact.sa_flags = 0; + sigaction(SIGINT, &sact, NULL); +} + +void ddio_ggi_InternalClose(void) +{ + static bool first_time = true; + + if(first_time) + { + giiExit(); + first_time = false; + } +} + +void ddio_ggi_EmergencyQuit(int id) +{ + giiPanic ("GII System shutdown...\n"); + //somehow we need to signal quit here + exit(-1); +} + +void ddio_ggi_KeyBoardEventHandler(int transkey,int press) +{ + if (press == 1) { + LKeys[transkey].status = 1; + LKeys[transkey].down_time = timer_GetTime(); + ddio_UpdateKeyState(transkey,true); + } + if (press == 0) { + LKeys[transkey].status = 0; + LKeys[transkey].up_time = timer_GetTime(); + ddio_UpdateKeyState(transkey,false); + } +} + +void ddio_ggi_DoKeyFrame(void) +{ + ddio_ggi_KeyHandler(); +} + + +void ddio_ggi_KeyHandler(void) +{ + ubyte state; + gii_event event; + gii_event_mask event_mask; + int key_sym; + int i, keycode, event_key, key_state; + unsigned char temp; + int ggi_key_repeat=0; //should disable when playing, enable otherwise? + struct timeval tv; + int c; + + event_key = -1; + + event_mask= (gii_event_mask) (emKeyPress | emKeyRelease); + if (ggi_key_repeat) + event_mask = (gii_event_mask)event_mask|((gii_event_mask)emKeyRepeat); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + while (giiEventPoll(GGI_input_handle, event_mask, &tv)) + { + giiEventRead(GGI_input_handle, &event, event_mask); + switch(event.any.type) { + case evKeyRepeat: + case evKeyPress: + keycode=event.key.button; + c=event.key.sym; + mprintf((0,"ggi key press %d (sym=%d)\n", keycode, c)); + break; + case evKeyRelease: + keycode=event.key.button; + c=event.key.sym; + mprintf((0,"ggi key release %d (sym=%d)\n", keycode, c)); + break; + } + + if ((event.any.type == evKeyPress) || (event.any.type == evKeyRelease)) + { + event_key = event.key.button; + key_sym=event.key.sym; + event_key=ddio_ggi_TranslateKey(c, keycode); + + if (event.any.type == evKeyPress) + key_state = 1; + else + key_state = 0; + + ddio_ggi_KeyBoardEventHandler(event_key,key_state); + } + +/* + for (i = 127; i >= 0; i--) { + keycode = i; + + key = &(key_data.keys[keycode]); + + if (i == event_key) + state = key_state; + else + state = key->last_state; + + if ( key->last_state == state ) { + if (state) { + key->counter++; + keyd_last_pressed = keycode; + keyd_time_when_last_pressed = timer_get_fixed_seconds(); + } + } else { + if (state) { + keyd_last_pressed = keycode; + keyd_pressed[keycode] = 1; + key->downcount += state; + key->state = 1; + key->timewentdown = keyd_time_when_last_pressed = timer_get_fixed_seconds(); + key->counter++; + } else { + keyd_pressed[keycode] = 0; + keyd_last_released = keycode; + key->upcount += key->state; + key->state = 0; + key->counter = 0; + key->timehelddown += timer_get_fixed_seconds() - key->timewentdown; + } + } + if ( (state && !key->last_state) || (state && key->last_state && (key->counter > 30) && (key->counter & 0x01)) ) { + if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT]) + keycode |= KEY_SHIFTED; + if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT]) + keycode |= KEY_ALTED; + if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL]) + keycode |= KEY_CTRLED; + if ( keyd_pressed[KEY_CMD] ) + keycode |= KEY_COMMAND; + temp = key_data.keytail+1; + if ( temp >= KEY_BUFFER_SIZE ) temp=0; + if (temp!=key_data.keyhead) { + key_data.keybuffer[key_data.keytail] = keycode; + key_data.time_pressed[key_data.keytail] = keyd_time_when_last_pressed; + key_data.keytail = temp; + } + } + key->last_state = state; + }*/ + } //ggi event reading loop +} + +int TranslateGGIToKey[96] = { + // !"#$%&'()*+,-./ + KEY_SPACEBAR, KEY_1, KEY_RAPOSTRO, KEY_3, KEY_4, KEY_5, KEY_7, KEY_RAPOSTRO, + KEY_9, KEY_0, KEY_8, KEY_EQUAL, KEY_COMMA, KEY_MINUS, KEY_PERIOD, KEY_SLASH, + + //0123456789:;<=>? + KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, + KEY_8, KEY_9, KEY_SEMICOL, KEY_SEMICOL, KEY_COMMA, KEY_EQUAL, KEY_PERIOD, KEY_SLASH, + + //@ABCDEFGHIJKLMNO + KEY_2, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, + KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, + + //PQRSTUVWXYZ[\]^_ + KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, + KEY_X, KEY_Y, KEY_Z, KEY_LBRACKET, KEY_SLASH, KEY_RBRACKET, KEY_6, KEY_MINUS, + + //`abcdefghijklmno + KEY_LAPOSTRO, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, + KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, + + //pqrstuvwxyz{|}~ + KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, + KEY_X, KEY_Y, KEY_Z, KEY_LBRACKET, KEY_SLASH, KEY_RBRACKET, KEY_LAPOSTRO, KEY_MINUS /* KEY_MINUS just for completeness */ +}; + +static int ddio_ggi_TranslateKey(int c, int code) { + int result; + + switch(KTYP( GII_KVAL(c))) { + case KT_LATIN: + case KT_LETTER: + case KT_META: + result = KVAL( GII_KVAL(c)); + if (result<32) { + if (result == 9) result=KEY_TAB; + else if (result == 8) result=KEY_BACKSP; + else if (result == 27) result=KEY_ESC; + else if (result == 28) result=KEY_PRINT_SCREEN; + else { + result +='A'-1; + mprintf((0,"DDIO: Unhandled Latin Symbol <32 (%d)\n", result)); + } + } + else if (result==127) + result=KEY_DELETE; + else { + result=tolower(result); + result=TranslateGGIToKey[result-32]; + } + mprintf((0,"DDIO: Decoded Key = %d\n", result)); + return result; + default: + switch( GII_KVAL(c)) { + case K_NUM: return KEY_NUMLOCK; + + case K_P0: return KEY_PAD0; + case K_P1: return KEY_PAD1; + case K_P2: return KEY_PAD2; + case K_P3: return KEY_PAD3; + case K_P4: return KEY_PAD4; + case K_P5: return KEY_PAD5; + case K_P6: return KEY_PAD6; + case K_P7: return KEY_PAD7; + case K_P8: return KEY_PAD8; + case K_P9: return KEY_PAD9; + + case K_PPLUS: return KEY_PADPLUS; + case K_PMINUS: return KEY_PADMINUS; + case K_PSTAR: return KEY_PADMULTIPLY; + case K_PSLASH: return KEY_PADDIVIDE; + case K_PENTER: return KEY_PADENTER; +// case K_PCOMMA: return KEY_PAD; + case K_PDOT: return KEY_PADPERIOD; +// case K_PPLUSMINUS: return KEY_PAD; +// case K_PPARENL: return KEY_PAD; +// case K_PPARENR: return KEY_PAD; +// case K_P: return KEY_PAD; +// case K_HOME: return KEY_HOME; + case K_FIND: return KEY_HOME; +// case K_END: return KEY_END; + case K_SELECT: return KEY_END; + + case K_UP: return KEY_UP; + case K_DOWN: return KEY_DOWN; + case K_LEFT: return KEY_LEFT; + case K_RIGHT: return KEY_RIGHT; + + //Should choose the next two depending on keycode & keysym: +//@@ case K_NORMAL_SHIFT: +//@@ if (code==54) return KEY_RSHIFT; +//@@ return KEY_LSHIFT; +//@@ case K_NORMAL_CTRL: +//@@ if (code==97) return KEY_RCTRL; +//@@ return KEY_LCTRL; +//@@ case K_NORMAL_ALT: return KEY_LALT; +//@@ case K_NORMAL_ALTGR: return KEY_RALT; +//@@ case K_NORMAL_SHIFTL: return KEY_LSHIFT; +//@@ case K_NORMAL_SHIFTR: return KEY_RSHIFT; +//@@ case K_NORMAL_CTRLL: return KEY_LCTRL; +//@@ case K_NORMAL_CTRLR: return KEY_LCTRL; + + case K_CAPS: return KEY_CAPSLOCK; + + case K_ENTER: return KEY_ENTER; + + case K_PGUP: return KEY_PAGEUP; + case K_PGDN: return KEY_PAGEDOWN; + + case K_INSERT: return KEY_INSERT; + case K_REMOVE: return KEY_DELETE; + + case K_PAUSE: return KEY_PAUSE; + case K_HOLD: return KEY_SCROLLOCK; + +//K_TAB is not defined, but = latin 9. +// case K_TAB: return XK_Tab; + case K_F1: return KEY_F1; + case K_F2: return KEY_F2; + case K_F3: return KEY_F3; + case K_F4: return KEY_F4; + case K_F5: return KEY_F5; + case K_F6: return KEY_F6; + case K_F7: return KEY_F7; + case K_F8: return KEY_F8; + case K_F9: return KEY_F9; + case K_F10: return KEY_F10; + case K_F11: return KEY_F11; + case K_F12: return KEY_F12; + + default: + mprintf((0,"GGI undhandled key event, keycode=%d (keysym=%d)\n", code, c)); + return 0; + } + } + return 0; +} diff --git a/ddio_lnx/lnxkey_null.cpp b/ddio_lnx/lnxkey_null.cpp new file mode 100644 index 000000000..edcf8b6af --- /dev/null +++ b/ddio_lnx/lnxkey_null.cpp @@ -0,0 +1,122 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxkey_null.cpp $ +* $Revision: 1.2 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* Linux NULL keyboard routines +* +* $Log: lnxkey_null.cpp,v $ +* Revision 1.2 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 2 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include "pserror.h" +#include "mono.h" +#include "ddio.h" +#include "ddio_lnx.h" +#include "application.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +//#include +//#include + +extern volatile struct tLnxKeys +{ + union{ + int up_ticks; + float up_time; + }; + union{ + int down_ticks; + float down_time; + }; + bool status; +}LKeys[DDIO_MAX_KEYS]; + + +bool ddio_null_InternalKeyInit(ddio_init_info *init_info) +{ + //reset key list + for(int i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +//#include +#include +#include + + +#include "ddio_common.h" +#include "ddio.h" +#include "mono.h" + +//########################################################## +//raw Interface Functions +//########################################################## +void ddio_raw_EmergencyQuit(int id); +void ddio_raw_InternalKeyInit(void); +void ddio_raw_KeyBoardEventHandler(int scancode, int press); +static int ddio_raw_TransKey(int scancode); +void ddio_raw_DoKeyFrame(void); +void ddio_raw_InternalClose(void); +void init_keyboard(); +void close_keyboard(); +int kbhit(); +int readch(); +//########################################################## + +extern volatile struct tLnxKeys +{ + union{ + int up_ticks; + float up_time; + }; + union{ + int down_ticks; + float down_time; + }; + bool status; +}LKeys[DDIO_MAX_KEYS]; +bool DDIO_key_suspend = false; + + +bool ddio_normal_InternalKeyInit(ddio_init_info *init_info) +{ + static bool first_call = true; + //reset key list + for(int i=0;i= SDLK_a && rc <= SDLK_z) { + rc = (rc - SDLK_a)+1; + } + else { + rc = 0; + } + ASSERT(rc >= 0 && rc <=26); + rc = sdlkey_to_ddiocode[rc]; + break; + } + + return (ubyte)rc; +} + + +int sdlKeyFilter(const SDL_Event *event) +{ + ubyte kc = 0; + + if ((event->type != SDL_KEYUP) && (event->type != SDL_KEYDOWN)) + return(1); + + switch(event->key.state) + { + case SDL_PRESSED: + kc = sdlkeycode_to_keycode(event->key.keysym.sym); + if (event->key.keysym.mod & KMOD_CTRL) + { + switch (kc) + { + case KEY_G: // toggle grabbed input. + if ( ddio_mouseGrabbed ) + { + SDL_WM_GrabInput(SDL_GRAB_OFF); + ddio_mouseGrabbed = false; + } // if + else + { + SDL_WM_GrabInput(SDL_GRAB_ON); + ddio_mouseGrabbed = true; + } // else + return(0); + + #ifdef __PERMIT_GL_LOGGING + case KEY_INSERT: + if (__glLog == false) + { + DGL_EnableLogging(1); + __glLog = true; + mprintf((0, "OpenGL: Logging enabled.")); + } // if + return(0); + + case KEY_DELETE: + if (__glLog == true) + { + DGL_EnableLogging(0); + __glLog = false; + mprintf((0, "OpenGL: Logging disabled.")); + } // if + return(0); + #endif + } // switch + } // if + + else if (event->key.keysym.mod & KMOD_ALT) + { + if ((kc == KEY_ENTER) || (kc == KEY_PADENTER)) + { + SDL_WM_ToggleFullScreen(SDL_GetVideoSurface()); + return(0); + } // if + } // else if + + LKeys[kc].down_time = timer_GetTime(); + LKeys[kc].status = true; + ddio_UpdateKeyState(kc, true); + break; + + case SDL_RELEASED: + kc = sdlkeycode_to_keycode(event->key.keysym.sym); + if (LKeys[kc].status) + { + LKeys[kc].up_time = timer_GetTime(); + LKeys[kc].status = false; + ddio_UpdateKeyState(kc, false); + } // if + break; + } // switch + + return(0); +} // sdlKeyFilter + + +bool ddio_sdl_InternalKeyInit(ddio_init_info *init_info) +{ + //reset key list + for(int i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ddio_common.h" +#include "ddio.h" +#include "mono.h" +#include "pserror.h" + +//########################################################## +//SVGALib Interface Functions +//########################################################## +#define DECLARE_POINTERS +#include "linux/dyna_svga.h" +#undef DECLARE_POINTERS + +void ddio_SVGA_EmergencyQuit(int id); +void ddio_SVGA_InternalKeyInit(void); +void ddio_SVGA_KeyBoardEventHandler(int scancode, int press); +static int ddio_SVGA_TransKey(int scancode); +void ddio_SVGA_DoKeyFrame(void); +void ddio_SVGA_InternalClose(void); +void ddio_svga_InternalKeyClose(void); + +//########################################################## + +extern volatile struct tLnxKeys +{ + union{ + int up_ticks; + float up_time; + }; + union{ + int down_ticks; + float down_time; + }; + bool status; +}LKeys[DDIO_MAX_KEYS]; +bool DDIO_svga_key_suspend = false; + + +bool ddio_svga_InternalKeyInit(ddio_init_info *init_info) +{ + if(!LoadSVGALib(true)) + { + fprintf(stderr,"Unable to initialize SVGAlib\n"); + return false; + } + + static bool first_call = true; + //reset key list + for(int i=0;i +//#include +#include + +extern volatile struct tLnxKeys +{ + union{ + int up_ticks; + float up_time; + }; + union{ + int down_ticks; + float down_time; + }; + bool status; +}LKeys[DDIO_MAX_KEYS]; + +// we need this for our XWindows calls. +static Display *X_display = NULL; + +bool ddio_xwin_InternalKeyInit(ddio_init_info *init_info) +{ + oeLnxApplication *app = (oeLnxApplication *)init_info->obj; + + X_display = app->m_Display; + +//reset key list + for(int i=0;i= XK_a && rc <= XK_z) { + rc = (rc - XK_a)+1; + } + else if (rc >= XK_A && rc <= XK_Z) { + rc = (rc - XK_A)+1; + } + else { + rc = 0; + } + ASSERT(rc >= 0 && rc <=26); + rc = xkey_to_ddiocode[rc]; + break; + } + + return (ubyte)rc; +} + + +void ddio_xwin_InternalKeyFrame(void) +{ + XEvent evt; + +// this is the big function. +// we'll grab all the keyboard events from the event queue currently +// and process them accordingly. + + while (XCheckMaskEvent(X_display,KeyPressMask|KeyReleaseMask,&evt)) + { + ubyte kc; + + switch(evt.type) + { + case KeyPress: + kc = xkeycode_to_keycode(evt.xkey.keycode); + LKeys[kc].down_time = timer_GetTime(); + LKeys[kc].status = true; + ddio_UpdateKeyState(kc, true); + //mprintf((0, "key %x down (state=%d).\n", (int)kc, (int)evt.xkey.state)); + break; + + case KeyRelease: + kc = xkeycode_to_keycode(evt.xkey.keycode); + if (LKeys[kc].status) { + LKeys[kc].up_time = timer_GetTime(); + LKeys[kc].status = false; + ddio_UpdateKeyState(kc, false); + //mprintf((0, "key %x up (state=%d).\n", (int)kc, (int)evt.xkey.state)); + } + break; + } + } +} \ No newline at end of file diff --git a/ddio_lnx/lnxmouse.cpp b/ddio_lnx/lnxmouse.cpp new file mode 100644 index 000000000..c5b8496a8 --- /dev/null +++ b/ddio_lnx/lnxmouse.cpp @@ -0,0 +1,586 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxmouse.cpp $ +* $Revision: 1.10 $ +* $Date: 2000/07/07 03:47:19 $ +* $Author: hercules $ +* +* Linux mouse routines +* +* $Log: lnxmouse.cpp,v $ +* Revision 1.10 2000/07/07 03:47:19 hercules +* Scale the ball values to mouse range (64000 -> 640) +* +* Revision 1.9 2000/07/04 00:30:04 icculus +* Again. +* +* Revision 1.8 2000/07/04 00:29:00 icculus +* Cleanups. Removed chunks of commented-out, abandoned attempts, and some +* no-longer used global variables. +* +* Revision 1.7 2000/06/29 10:38:45 hercules +* Don't warp the mouse during mouse reset +* +* Revision 1.6 2000/06/29 09:51:53 hercules +* Added support for wheel mouse +* +* Revision 1.5 2000/06/29 06:41:23 icculus +* mad commits. +* +* Revision 1.4 2000/06/24 01:15:15 icculus +* patched to compile. +* +* Revision 1.3 2000/04/28 20:13:42 icculus +* Took out some mprintfs +* +* Revision 1.2 2000/04/27 11:36:29 icculus +* Rewritten to use SDL. +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 17 9/09/99 4:36p Jeff + * use DGA extension for much better mouse control + * + * 16 9/07/99 4:35p Jeff + * added a call to XSync() to attempt to better the mouse performance... + * + * 15 8/22/99 10:00p Jeff + * attempt to better the mouse motion...but it's not working...XWindows + * seems to buffer up motion motion events when the mouse moves very + * small. Added support for up to 5 mouse buttons + * + * 14 8/19/99 2:24p Jeff + * mouse resets to center of the screen + * + * 13 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +// ---------------------------------------------------------------------------- +// Mouse Interface +// ---------------------------------------------------------------------------- + +#include +#include +#include "pserror.h" +#include "psclass.h" +#include "mono.h" +#include "ddio.h" +#include "ddio_lnx.h" +#include "application.h" +#include "args.h" +//#include "../lib/linux/dyna_xwin.h" +#include "SDL.h" + +bool ddio_mouseGrabbed = false; +static bool DDIO_mouse_init = false; + + +static struct mses_state { + int fd; // file descriptor of mouse + int t,l,b,r; // limit rectangle of absolute mouse coords + int x, y,dx,dy,cx,cy; // current x,y,z in absolute mouse coords + int btn_mask; +} DDIO_mouse_state; + +typedef struct t_mse_button_info +{ + bool is_down[N_MSEBTNS]; + ubyte down_count[N_MSEBTNS]; + ubyte up_count[N_MSEBTNS]; + float time_down[N_MSEBTNS]; + float time_up[N_MSEBTNS]; +} +t_mse_button_info; + +typedef struct t_mse_event +{ + short btn; + short state; +} +t_mse_event; + +static t_mse_button_info DIM_buttons; +static tQueue MB_queue; +static int Mouse_mode = MOUSE_STANDARD_MODE; + +// --------------------------------------------------------------------------- +// Initialization Functions + +bool ddio_MouseInit(void) +{ + DDIO_mouse_init = true; + + SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE); + SDL_EventState(SDL_MOUSEBUTTONUP, SDL_ENABLE); + SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE); + ddio_MouseReset(); + return true; +} + + +int ddio_MouseGetCaps(int *buttons, int *axes) +{ + *buttons = 6; + *axes = 2; + return MOUSE_LB | MOUSE_CB | MOUSE_RB; +} + + +void ddio_MouseClose() +{ + DDIO_mouse_init = false; +} + + +void ddio_MouseSetLimits(int left, int top, int right, int bottom, int zmin, int zmax) +{ + DDIO_mouse_state.l = left; + DDIO_mouse_state.t = top; + DDIO_mouse_state.r = right; + DDIO_mouse_state.b = bottom; +} + + +void ddio_MouseGetLimits(int *left, int *top, int *right, int *bottom, int *zmin, int *zmax) +{ + *left = DDIO_mouse_state.l; + *top = DDIO_mouse_state.t; + *right = DDIO_mouse_state.r; + *bottom = DDIO_mouse_state.b; + + if (zmin) *zmin = 0; + if (zmax) *zmax = 10; +} + + +void ddio_MouseReset() +{ + ddio_MouseSetLimits(0,0,Lnx_app_obj->m_W,Lnx_app_obj->m_H); + DDIO_mouse_state.btn_mask = 0; + + // reset button states + ddio_MouseQueueFlush(); + +/* + if(Mouse_mode==MOUSE_STANDARD_MODE) + return; + bool use_dga = false; + + if(Lnx_app_obj && Lnx_app_obj->m_Flags&APPFLAG_DGAMOUSE) + use_dga = true; + + // warp the mouse. + if(!use_dga) + { + XWarpPointer(Lnx_app_obj->m_Display, None, Lnx_app_obj->m_Window, + 0,0,0,0, Lnx_app_obj->m_W/2, Lnx_app_obj->m_H/2); + } +*/ + + DDIO_mouse_state.x = DDIO_mouse_state.cx = Lnx_app_obj->m_W/2; + DDIO_mouse_state.y = DDIO_mouse_state.cy = Lnx_app_obj->m_H/2; + DDIO_mouse_state.dy = DDIO_mouse_state.dx = 0; +} + + +// displays the mouse pointer. Each Hide call = Show call. + +static int Mouse_counter=0; + +void ddio_MouseShow() +{ + Mouse_counter++; +} + +void ddio_MouseHide() +{ + Mouse_counter--; +} + + +void ddio_InternalMouseSuspend(void) +{ +} + + +void ddio_InternalMouseResume(void) +{ +} + + +void ddio_MouseMode(int mode) +{ + Mouse_mode = mode; +} + + +// virtual coordinate system for mouse (match to video resolution set for optimal mouse usage. +void ddio_MouseSetVCoords(int width, int height) +{ + ddio_MouseSetLimits(0,0,width,height); +} + + +int sdlMouseButtonDownFilter(SDL_Event const *event) +{ + ASSERT(event->type == SDL_MOUSEBUTTONDOWN); + + const SDL_MouseButtonEvent *ev = &event->button; + t_mse_event mevt; + + if ( ev->button == 1 ) { //(evt.xbutton.state & Button1Mask) || (evt.xbutton.button == Button1)) { + DDIO_mouse_state.btn_mask |= MOUSE_LB; + DIM_buttons.down_count[0]++; + DIM_buttons.time_down[0] = timer_GetTime(); + DIM_buttons.is_down[0] = true; + mevt.btn = 0; + mevt.state = true; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 0: Down\n")); + } + else if ( ev->button == 2 ) { //((evt.xbutton.state & Button2Mask) || (evt.xbutton.button == Button2)) { + DDIO_mouse_state.btn_mask |= MOUSE_RB; + DIM_buttons.down_count[1]++; + DIM_buttons.time_down[1] = timer_GetTime(); + DIM_buttons.is_down[1] = true; + mevt.btn = 1; + mevt.state = true; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 1: Down\n")); + } +// if ((evt.xbutton.state & Button3Mask) || (evt.xbutton.button == Button3)) { + else if ( ev->button == 3 ) { + DDIO_mouse_state.btn_mask |= MOUSE_CB; + DIM_buttons.down_count[2]++; + DIM_buttons.time_down[2] = timer_GetTime(); + DIM_buttons.is_down[2] = true; + mevt.btn = 2; + mevt.state = true; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 2: Down\n")); + } +// if ((evt.xbutton.state & Button4Mask) || (evt.xbutton.button == Button4)) { + else if ( ev->button == 4 ) { /* Mouse scroll up */ + DDIO_mouse_state.btn_mask |= MOUSE_B5; + DIM_buttons.down_count[4]++; + DIM_buttons.time_down[4] = timer_GetTime(); + DIM_buttons.is_down[4] = true; + mevt.btn = 4; + mevt.state = true; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 4: Down\n")); + } +// if ((evt.xbutton.state & Button5Mask) || (evt.xbutton.button == Button5)) { + else if ( ev->button == 5 ) { /* Mouse scroll down */ + DDIO_mouse_state.btn_mask |= MOUSE_B6; + DIM_buttons.down_count[5]++; + DIM_buttons.time_down[5] = timer_GetTime(); + DIM_buttons.is_down[5] = true; + mevt.btn = 5; + mevt.state = true; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 5: Down\n")); + } + return(0); +} + + +int sdlMouseButtonUpFilter(SDL_Event const *event) +{ + ASSERT(event->type == SDL_MOUSEBUTTONUP); + + const SDL_MouseButtonEvent *ev = &event->button; + t_mse_event mevt; + +// if ((evt.xbutton.state & Button1Mask) || (evt.xbutton.button == Button1)) { + if ( ev->button == 1 ) { + DDIO_mouse_state.btn_mask &= (~MOUSE_LB); + DIM_buttons.up_count[0]++; + DIM_buttons.is_down[0] = false; + DIM_buttons.time_up[0] = timer_GetTime(); + mevt.btn = 0; + mevt.state = false; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 0: Up\n")); + } +// if ((evt.xbutton.state & Button2Mask) || (evt.xbutton.button == Button2)) { + else if ( ev->button == 2 ) { + DDIO_mouse_state.btn_mask &= (~MOUSE_RB); + DIM_buttons.up_count[1]++; + DIM_buttons.is_down[1] = false; + DIM_buttons.time_up[1] = timer_GetTime(); + mevt.btn = 1; + mevt.state = false; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 1: Up\n")); + } +// if ((evt.xbutton.state & Button3Mask) || (evt.xbutton.button == Button3)) { + else if ( ev->button == 3 ) { + DDIO_mouse_state.btn_mask &= (~MOUSE_CB); + DIM_buttons.up_count[2]++; + DIM_buttons.is_down[2] = false; + DIM_buttons.time_up[2] = timer_GetTime(); + mevt.btn = 2; + mevt.state = false; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 2: Up\n")); + } +// if ((evt.xbutton.state & Button4Mask) || (evt.xbutton.button == Button4)) { + else if ( ev->button == 4 ) { + DDIO_mouse_state.btn_mask &= (~MOUSE_B5); + DIM_buttons.up_count[4]++; + DIM_buttons.is_down[4] = false; + DIM_buttons.time_up[4] = timer_GetTime(); + mevt.btn = 4; + mevt.state = false; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 4: Up\n")); + } +// if ((evt.xbutton.state & Button5Mask) || (evt.xbutton.button == Button5)) { + else if ( ev->button == 5 ) { + DDIO_mouse_state.btn_mask &= (~MOUSE_B6); + DIM_buttons.up_count[5]++; + DIM_buttons.is_down[5] = false; + DIM_buttons.time_up[5] = timer_GetTime(); + mevt.btn = 5; + mevt.state = false; + MB_queue.send(mevt); +// mprintf((0, "MOUSE Button 5: Up\n")); + } + + return(0); +} + +int sdlMouseMotionFilter(SDL_Event const *event) +{ +/* + int oldmx, oldmy; + int deltamx,deltamy; + oldmx = DDIO_mouse_state.cx; + oldmy = DDIO_mouse_state.cy; + deltamx = deltamy = 0; + + bool use_dga = false; + + if(Lnx_app_obj->m_Flags&APPFLAG_DGAMOUSE) + use_dga = true; + + if(Mouse_mode==MOUSE_STANDARD_MODE) + return; + + while (XCheckMaskEvent(Lnx_app_obj->m_Display, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, &evt)) + { + switch (evt.type) + { + case MotionNotify: + if(use_dga) + { + + deltamx += event->motion.x; //evt.xmotion.x_root; + deltamy += event->motion.y; //evt.xmotion.y_root; + + }else + { + if ((evt.xmotion.x != DDIO_mouse_state.cx) && (evt.xmotion.y != DDIO_mouse_state.cy)) { + deltamx += (evt.xmotion.x - oldmx); + deltamy += (evt.xmotion.y - oldmy); + oldmx = evt.xmotion.x; + oldmy = evt.xmotion.y; + } + } + break; + case ButtonPress: + case ButtonRelease: + break; + } + } + + if( (deltamx) || (deltamy) ) + { + DDIO_mouse_state.dx = deltamx; + DDIO_mouse_state.dy = deltamy; + DDIO_mouse_state.x += deltamx; + DDIO_mouse_state.y += deltamy; + if (DDIO_mouse_state.x < DDIO_mouse_state.l) DDIO_mouse_state.x = DDIO_mouse_state.l; + if (DDIO_mouse_state.x >= DDIO_mouse_state.r) DDIO_mouse_state.x = DDIO_mouse_state.r-1; + if (DDIO_mouse_state.y < DDIO_mouse_state.t) DDIO_mouse_state.y = DDIO_mouse_state.t; + if (DDIO_mouse_state.y >= DDIO_mouse_state.b) DDIO_mouse_state.y = DDIO_mouse_state.b-1; + + // warp the mouse. + if(!use_dga) + { + XWarpPointer(Lnx_app_obj->m_Display, None, Lnx_app_obj->m_Window, + 0,0,0,0, Lnx_app_obj->m_W/2, Lnx_app_obj->m_H/2); + } + } +*/ + + if (event->type == SDL_JOYBALLMOTION) + { + DDIO_mouse_state.dx = event->jball.xrel/100; + DDIO_mouse_state.dy = event->jball.yrel/100; + DDIO_mouse_state.x += DDIO_mouse_state.dx; + DDIO_mouse_state.y += DDIO_mouse_state.dy; + } // if + else + { + if (ddio_mouseGrabbed) + { + DDIO_mouse_state.dx = event->motion.xrel; + DDIO_mouse_state.dy = event->motion.yrel; + DDIO_mouse_state.x += DDIO_mouse_state.dx; + DDIO_mouse_state.y += DDIO_mouse_state.dy; + } // if + else + { + DDIO_mouse_state.dx = event->motion.x - DDIO_mouse_state.x; + DDIO_mouse_state.dy = event->motion.y - DDIO_mouse_state.y; + DDIO_mouse_state.x = event->motion.x; + DDIO_mouse_state.y = event->motion.y; + } // else + } // else + + if (DDIO_mouse_state.x < DDIO_mouse_state.l) DDIO_mouse_state.x = DDIO_mouse_state.l; + if (DDIO_mouse_state.x >= DDIO_mouse_state.r) DDIO_mouse_state.x = DDIO_mouse_state.r-1; + if (DDIO_mouse_state.y < DDIO_mouse_state.t) DDIO_mouse_state.y = DDIO_mouse_state.t; + if (DDIO_mouse_state.y >= DDIO_mouse_state.b) DDIO_mouse_state.y = DDIO_mouse_state.b-1; + + return(0); +} + + + +// This function will handle all mouse events. +void ddio_InternalMouseFrame(void) +{ + static unsigned frame_count=0; + SDL_PumpEvents(); + frame_count++; +} + + +/* x, y = absolute mouse position + dx, dy = mouse deltas since last call + return value is mouse button mask +*/ +int ddio_MouseGetState(int *x, int *y, int *dx, int *dy, int *z, int *dz) +{ + // update mouse timer. + int btn_mask = DDIO_mouse_state.btn_mask; + + // get return values. + if (x) *x = DDIO_mouse_state.x; + if (y) *y = DDIO_mouse_state.y; + if (z) *z = 0; + if (dx) *dx = DDIO_mouse_state.dx; + if (dy) *dy = DDIO_mouse_state.dy; + if (dz) *dz = 0; + + DDIO_mouse_state.dx = 0; + DDIO_mouse_state.dy = 0; + return btn_mask; +} + + +// resets mouse queue and button info only. +void ddio_MouseQueueFlush() +{ + memset(&DIM_buttons, 0, sizeof(DIM_buttons)); + MB_queue.flush(); +} + + +// gets a mouse button event, returns false if none. +bool ddio_MouseGetEvent(int *btn, bool *state) +{ + t_mse_event evt; + + if (MB_queue.recv(&evt)) { + *btn = (int)evt.btn; + *state = evt.state ? true : false; + return true; + } + + return false; +} + + +// return mouse button down time. +float ddio_MouseBtnDownTime(int btn) +{ + float dtime, curtime = timer_GetTime(); + + ASSERT(btn >=0 && btn < N_MSEBTNS); + + if (DIM_buttons.is_down[btn]) { + dtime = curtime - DIM_buttons.time_down[btn]; + DIM_buttons.time_down[btn] = curtime; + } + else { + dtime = DIM_buttons.time_up[btn] - DIM_buttons.time_down[btn]; + DIM_buttons.time_down[btn] = 0; + DIM_buttons.time_up[btn] = 0; + } + + return dtime; +} + + +// return mouse button down time +int ddio_MouseBtnDownCount(int btn) +{ + if (btn < 0 || btn >= N_MSEBTNS) return 0; + int n_downs = DIM_buttons.down_count[btn]; + + if (n_downs) { + DIM_buttons.down_count[btn] = 0; + } + + return n_downs; +} + + +// return mouse button up count +int ddio_MouseBtnUpCount(int btn) +{ + if (btn < 0 || btn >= N_MSEBTNS) return 0; + int n_ups = DIM_buttons.up_count[btn]; + DIM_buttons.up_count[btn] = 0; + return n_ups; +} + + +char Ctltext_MseBtnBindings[N_MSEBTNS][32] = { + "mse-1\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-2\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-3\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-4\0\0\0\0\0\0\0\0\0\0\0", + "msew-u\0\0\0\0\0\0\0\0\0\0\0", + "msew-d\0\0\0\0\0\0\0\0\0\0\0", + "","" +}; + +char Ctltext_MseAxisBindings[][32] = { + "mse-X\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-Y\0\0\0\0\0\0\0\0\0\0\0\0", + "msewheel\0\0\0\0\0\0\0\0\0\0" +}; + + +// returns string to binding. +const char *ddio_MouseGetBtnText(int btn) +{ + if (btn >=N_MSEBTNS || btn < 0) return (""); + return Ctltext_MseBtnBindings[btn]; +} + +const char *ddio_MouseGetAxisText(int axis) +{ + if (axis >= (sizeof(Ctltext_MseAxisBindings)/sizeof(char*)) || axis < 0) return (""); + return Ctltext_MseAxisBindings[axis]; +} + diff --git a/ddio_lnx/lnxtimer.cpp b/ddio_lnx/lnxtimer.cpp new file mode 100644 index 000000000..117a746b0 --- /dev/null +++ b/ddio_lnx/lnxtimer.cpp @@ -0,0 +1,155 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxtimer.cpp $ +* $Revision: 1.2 $ +* $Date: 2000/06/29 08:50:17 $ +* $Author: icculus $ +* +* Linux timer routines +* +* $Log: lnxtimer.cpp,v $ +* Revision 1.2 2000/06/29 08:50:17 icculus +* Fixed to be more sane. Probably fixes lockup bug, too. +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 7 8/20/99 7:19p Jeff + * rewrote timer code...trying to get rid of mysterious bug + * + * 6 8/19/99 3:40p Jeff + * possibly fix any holes with signed/unsigned issues + * + * 5 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include "ddio_lnx.h" +#include "ddio.h" + +#include +#include +#include + +#include "pserror.h" +#include "mono.h" + +#include "SDL.h" + +// --------------------------------------------------------------------------- +// Variables +// --------------------------------------------------------------------------- + +static bool Timer_initialized = 0; +//rcg06292000 not used with SDL. +//static unsigned long long Timer_sys_start_time = 0; +//static unsigned long long Timer_accum = 0,Timer_high_mark = 0; +//void timer_Normalize(); +//unsigned long long timer_GetTickCount(); + +// --------------------------------------------------------------------------- + +bool timer_Init(int preemptive,bool force_lores) +{ +// struct timeval t; +// gettimeofday(&t,NULL); +// Timer_sys_start_time = t.tv_sec*1000000.0 + t.tv_usec; +// Timer_sys_start_time = 0; + + Timer_initialized = 1; + +// Timer_accum = 0; +// Timer_high_mark = 0; + return true; +} + +void timer_Close() +{ + ASSERT(Timer_initialized); + Timer_initialized = 0; +} + + +float ddio_TickToSeconds(unsigned long ticks) +{ +//rcg06292000 not used with SDL. +// unsigned long time_ms; +// unsigned long long new_ticks = ticks; + +// timer_Normalize(); +// time_ms = new_ticks;// - Timer_sys_start_time; + +// return (float)((double)time_ms/((double)1000000.0)); + + return(ticks / 1000.0); +} + +float timer_GetTime() +{ +//rcg06292000 ain't working. +// unsigned long time_ms; +// timer_Normalize(); +// time_ms = timer_GetTickCount() - Timer_sys_start_time; +// return (float)((double)time_ms/((double)1000000.0)); + +//rcg06292000 'dis sucka's MINE! + return((float) SDL_GetTicks() / 1000.0); +} + +longlong timer_GetMSTime() +{ +//rcg06292000 not used with SDL. +// unsigned long time_ms; +// timer_Normalize(); +// time_ms = timer_GetTickCount() - Timer_sys_start_time; +// return (longlong)((double)time_ms/((double)1000.0)); + + return(SDL_GetTicks()); +} + +// --------------------------------------------------------------------------- +// Internal functions +// --------------------------------------------------------------------------- + +//rcg06292000 not used with SDL. +/* +void timer_Normalize() +{ + unsigned long long new_time; + new_time = timer_GetTickCount(); + + if (new_time < Timer_sys_start_time) { + Timer_sys_start_time = new_time; + return; + } +} + +unsigned long long timer_GetTickCount(void) +{ + unsigned long long ret; + struct timeval t; + gettimeofday(&t,NULL); + + ret = t.tv_sec*1000000.0 + t.tv_usec; + + if(ret > Timer_high_mark) + { + Timer_high_mark = ret; + }else + { + // timer roll over + mprintf((0,"TIMER: ROLL OVER\n")); + if(Timer_high_mark>0) + Timer_accum += (Timer_high_mark - Timer_sys_start_time); + Timer_high_mark = ret; + Timer_sys_start_time = ret; + ret = 100;//give some time + } + + return Timer_accum + ret; +} +*/ + + diff --git a/ddio_lnx/sdljoy.cpp b/ddio_lnx/sdljoy.cpp new file mode 100644 index 000000000..036fb7a12 --- /dev/null +++ b/ddio_lnx/sdljoy.cpp @@ -0,0 +1,360 @@ +/* +* $Logfile: /DescentIII/Main/ddio_lnx/lnxjoy.cpp $ +* $Revision: 1.3 $ +* $Date: 2001/02/07 09:16:45 $ +* $Author: icculus $ +* +* Linux joystick routines +* +* $Log: sdljoy.cpp,v $ +* Revision 1.3 2001/02/07 09:16:45 icculus +* More robust debugging information. +* +* Revision 1.2 2000/06/29 22:15:25 hercules +* Fixed hat motion +* +* Revision 1.1 2000/06/29 09:53:00 hercules +* Use SDL joystick support (hats off to you! :) +* +* Revision 1.3 2000/06/24 01:15:15 icculus +* patched to compile. +* +* Revision 1.2 2000/05/29 05:21:09 icculus +* Changed a fprintf(stderr, ...) to an mprintf()... +* +* Revision 1.1.1.1 2000/04/18 00:00:33 icculus +* initial checkin +* + * + * 9 8/22/99 5:55p Jeff + * fixed assert + * + * 8 8/19/99 3:46p Jeff + * removed mprintfs + * + * 7 8/19/99 3:22p Jeff + * added support for joystick driver version pre 1.0 + * + * 6 8/18/99 9:47p Jeff + * joystick support! for kernel 2..2+ clients...need to handle before that + * still. + * + * 5 8/17/99 2:32p Jeff + * fixed joy_GetPos + * + * 4 7/14/99 9:06p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + + +#include "joystick.h" +#include "pserror.h" +#include "pstypes.h" +#include +#include +#include +#include "SDL.h" + +//rcg06182000 need this for specific joystick stuff. +#include "args.h" + +// --------------------------------------------------------------------------- +// globals + +static int specificJoy = -1; +static struct { + SDL_Joystick *handle; + tJoyInfo caps; +} Joysticks[MAX_JOYSTICKS]; +void joy_Close(); +void joy_GetState(tJoystick stick, tJoyPos *pos); +static int joyGetNumDevs(void); +// closes a stick +// closes connection with controller. +static void joy_CloseStick(tJoystick joy); +// initializes a joystick +// if server_adr is valid, a link is opened to another machine with a controller. +static bool joy_InitStick(tJoystick joy, char *server_adr); + +// --------------------------------------------------------------------------- +// functions + + +// joystick system initialization +bool joy_Init(bool remote) +{ + // reinitialize joystick if already initialized. + joy_Close(); + if ( SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) { + // FIXME: report an error? + return false; + } + SDL_EventState(SDL_JOYAXISMOTION, SDL_IGNORE); + SDL_EventState(SDL_JOYHATMOTION, SDL_IGNORE); + SDL_EventState(SDL_JOYBUTTONDOWN, SDL_IGNORE); + SDL_EventState(SDL_JOYBUTTONUP, SDL_IGNORE); + + // check if this OS supports joysticks + if (!joyGetNumDevs() && !remote) { + return false; + } + + //rcg06182000 specific joystick support. + if (specificJoy >= 0) + { + joy_InitStick((tJoystick)specificJoy, NULL); + } // if + else + { + // initialize joystick list + for (int i = 0; i < MAX_JOYSTICKS; i++) + { + joy_InitStick((tJoystick)i,NULL); + } + } // else + return true; +} + + +void joy_Close() +{ + // initialize joystick list + for (int i = 0; i < MAX_JOYSTICKS; i++) + { + joy_CloseStick((tJoystick)i); + } + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); +} + + +// initializes a joystick +// if server_adr is valid, a link is opened to another machine with a controller. +static bool joy_InitStick(tJoystick joy, char *server_adr) +{ + SDL_Joystick *stick; + + // close down already open joystick. + joy_CloseStick(joy); + + // okay, now if this is a remote joystick, open it + if (server_adr) { + return false; + } + stick = SDL_JoystickOpen(joy); + Joysticks[joy].handle = stick; + if ( stick ) { + tJoyInfo caps; + + memset(&caps, 0, (sizeof caps)); + strncpy(caps.name, SDL_JoystickName(joy), sizeof(caps.name)-1); + caps.num_btns = SDL_JoystickNumButtons(stick); + int axes = SDL_JoystickNumAxes(stick); + switch (axes) { + default: + // Fall through to 6 axes + case 6: + caps.axes_mask |= JOYFLAG_VVALID; + caps.minv = -32767; + caps.maxv = 32768; + case 5: + caps.axes_mask |= JOYFLAG_UVALID; + caps.minu = -32767; + caps.maxu = 32768; + case 4: + caps.axes_mask |= JOYFLAG_RVALID; + caps.minr = -32767; + caps.maxr = 32768; + case 3: + caps.axes_mask |= JOYFLAG_ZVALID; + caps.minz = -32767; + caps.maxz = 32768; + case 2: + caps.axes_mask |= JOYFLAG_YVALID; + caps.miny = -32767; + caps.maxy = 32768; + case 1: + caps.axes_mask |= JOYFLAG_XVALID; + caps.minx = -32767; + caps.maxx = 32768; + case 0: + break; + } + int hats = SDL_JoystickNumHats(stick); + switch (hats) { + default: + // Fall through to 4 hats + case 4: + caps.axes_mask |= JOYFLAG_POV4VALID; + case 3: + caps.axes_mask |= JOYFLAG_POV3VALID; + case 2: + caps.axes_mask |= JOYFLAG_POV2VALID; + case 1: + caps.axes_mask |= JOYFLAG_POVVALID; + case 0: + break; + } + Joysticks[joy].caps = caps; + + mprintf((0, "JOYSTICK: Initialized stick named [%s].", caps.name)); + mprintf((0, "JOYSTICK: (%d) axes, (%d) hats, and (%d) buttons.", + axes, hats, caps.num_btns)); + } + + return(Joysticks[joy].handle != NULL); +} + + +// closes a stick +// closes connection with controller. +static void joy_CloseStick(tJoystick joy) +{ + //CLOSE joystick here + SDL_JoystickClose(Joysticks[joy].handle); + Joysticks[joy].handle = 0; +} + + +// returns true if joystick valid +bool joy_IsValid(tJoystick joy) +{ + if ( specificJoy >= 0 ) { + if ( joy != specificJoy ) { + return false; + } + } + return(Joysticks[joy].handle != NULL); +} + + +// retreive information about joystick. +void joy_GetJoyInfo(tJoystick joy, tJoyInfo *info) +{ + memcpy(info, &Joysticks[(int)joy].caps, sizeof(tJoyInfo)); +} + + +// retreive uncalibrated position of joystick +#define LNX_JOYAXIS_RANGE 65535 +void joy_GetRawPos(tJoystick joy, tJoyPos *pos) +{ + joy_GetPos(joy, pos); + + pos->x = (pos->x+32767); + pos->y = (pos->y+32767); + pos->z = (pos->z+32767); + pos->r = (pos->r+32767); + pos->u = (pos->u+32767); + pos->v = (pos->v+32767); +} + +static inline unsigned int map_hat(Uint8 value) +{ + unsigned int mapped; + + switch (value) { + case SDL_HAT_CENTERED: + mapped = JOYPOV_CENTER; + break; + case SDL_HAT_UP: + mapped = 0x00; + break; + case SDL_HAT_UP|SDL_HAT_RIGHT: + mapped = 0x20; + break; + case SDL_HAT_RIGHT: + mapped = 0x40; + break; + case SDL_HAT_RIGHT|SDL_HAT_DOWN: + mapped = 0x60; + break; + case SDL_HAT_DOWN: + mapped = 0x80; + break; + case SDL_HAT_DOWN|SDL_HAT_LEFT: + mapped = 0xA0; + break; + case SDL_HAT_LEFT: + mapped = 0xC0; + break; + case SDL_HAT_LEFT|SDL_HAT_UP: + mapped = 0xE0; + break; + } + return mapped; +} + +// returns the state of a stick, remote or otherwise +void joy_GetPos(tJoystick joy, tJoyPos *pos) +{ + SDL_Joystick *stick; + int i; + + memset(pos, 0, (sizeof *pos)); + + // retrieve joystick info from the net, or locally. + stick = Joysticks[joy].handle; + if ( stick ) { + unsigned int mask; + + mask = Joysticks[joy].caps.axes_mask; + pos->x = SDL_JoystickGetAxis(stick, 0); + pos->y = SDL_JoystickGetAxis(stick, 1); + if ( mask & JOYFLAG_ZVALID ) { + pos->z = SDL_JoystickGetAxis(stick, 2); + } + if ( mask & JOYFLAG_RVALID ) { + pos->r = SDL_JoystickGetAxis(stick, 3); + } + if ( mask & JOYFLAG_UVALID ) { + pos->u = SDL_JoystickGetAxis(stick, 4); + } + if ( mask & JOYFLAG_VVALID ) { + pos->v = SDL_JoystickGetAxis(stick, 5); + } + for ( i=0; ipov[i] = map_hat(SDL_JoystickGetHat(stick, i)); + } + } + for ( i=Joysticks[joy].caps.num_btns; i >= 0; --i ) { + if ( SDL_JoystickGetButton(stick, i) ) { + pos->buttons |= (1< 0) && (GameArgs[rc + 1] != NULL)) + { + specificJoy = atoi(GameArgs[rc + 1]); + if ( (specificJoy >= 0) && (specificJoy < SDL_NumJoysticks()) ) { + found = 1; + } else { + specificJoy = -1; + } + } + if ( specificJoy < 0 ) { + found = SDL_NumJoysticks(); + } + + mprintf((0, "Joystick: Found %d joysticks.", found)); + return found; +} + +void ddio_InternalJoyFrame(void) +{ + // All the work is done already in SDL_PumpEvents() +} + + diff --git a/ddio_mac/ddio_mac.h b/ddio_mac/ddio_mac.h new file mode 100644 index 000000000..537cb7477 --- /dev/null +++ b/ddio_mac/ddio_mac.h @@ -0,0 +1,80 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/ddio_mac.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * macintosh implementation of device dependent io functions + * + * $Log: ddio_mac.h,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 4 3/20/00 12:43p Matt + * Merge of Duane's post-1.3 changes. + * + * 3 10/21/99 3:33p Jeff + * Macintosh merges + * + * 2 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 4 5/15/97 2:33 PM Jeremy + * made macKeyboardHandler return true if more keys are waiting. This way + * it can be repeatedly called to get all pending keys or just called once + * to get one key. + * + * 3 5/15/97 1:45 AM Jeremy + * added keyboard handler prototype + * + * 2 5/9/97 7:14 PM Jeremy + * initial checkin + * + * 1 3/13/97 6:53 PM Jeremy + * macintosh implementation of device dependent io functions + * + * $NoKeywords: $ + */ +#ifndef DDIO_MAC_H +#define DDIO_MAC_H +#include "pstypes.h" +// Keyboard handler which checks the OS for keyboard events +// returns true if there are more keys waiting to be read, false if done +bool MacKeyboardHandler(void); + +#define INTERRUPT_MS 90 + +// Interrupt timer data structures +typedef struct timer_info +{ + TMTask timer_task; +} timer_info; + +extern timer_info ktimer_info; + +extern UniversalProcPtr timer_proc; + +// Interrupt macro functions + +#define _INTERRUPT_DISABLE() RmvTime((QElemPtr)(&ktimer_info)) + +#define _INTERRUPT_ENABLE() \ + ktimer_info.timer_task.tmAddr = timer_proc; \ + ktimer_info.timer_task.tmWakeUp = 0; \ + ktimer_info.timer_task.tmReserved = 0; \ + InsXTime((QElemPtr)(&ktimer_info)); \ + PrimeTime((QElemPtr)(&ktimer_info), INTERRUPT_MS); + +// Interrupt timer routines + +void interrupt_Init(); +void interrupt_Handler(timer_info *tminfo); +void interrupt_Close(); +void interrupt_Flush(); + +FILE * mac_fopen(char *filename, const char * open_mode); +FILE * FSp_fopen(ConstFSSpecPtr spec, const char * open_mode, OSType type); + +void setup_sioux(void); //DAJ debug console +#endif diff --git a/ddio_mac/macController.cpp b/ddio_mac/macController.cpp new file mode 100644 index 000000000..1e49cfa83 --- /dev/null +++ b/ddio_mac/macController.cpp @@ -0,0 +1,975 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/macController.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * Keyboard IO for macintosh + * + * $Log: macController.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 5 4/12/00 6:52p Matt + * From Duane for 1.4: deadzone stuff + * + * 4 3/20/00 12:43p Matt + * Merge of Duane's post-1.3 changes. + * + * 3 10/21/99 3:33p Jeff + * Macintosh merges + * + * 2 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 4 5/21/97 5:17 PM Jeremy + * switch keyboard controls to default values of PC (still need to + * implement needs/controls structure) + * + * 3 5/15/97 1:43 AM Jeremy + * changed mprintf's to be standard (with newline at end) + * + * 2 5/13/97 11:45 AM Jeremy + * implementation for gameMacController object as well as create/destroy + * controller object (still stubbed out) + * + * 1 5/11/97 8:09 PM Jeremy + * Initial check in + * + * $NoKeywords: $ + */ +// ---------------------------------------------------------------------------- +// Keyboard Interface +// ---------------------------------------------------------------------------- +// ANSI Headers +#include +#include + +// Macintosh Headers +#include +#include +#include "InSprocket.h" +// Descent 3 Headers +#include "pserror.h" +#include "mono.h" +#include "macController.h" +#include "Controller.h" +#include "controls.h" +#include "ddio_common.h" +#include "ddio.h" +#include "hud.h" + +#define SIGN(a) ((a<0)?-1:1) + +typedef struct { + ubyte pcAction; + ubyte isEvent; +} PC_IS_Map; +PC_IS_Map m_pc_is_map[] = { + { ctfFORWARD_THRUSTAXIS, kInputEvent_None }, + { ctfFORWARD_THRUSTKEY, kInputEvent_None }, + { ctfREVERSE_THRUSTKEY, kInputEvent_None }, + { ctfUP_THRUSTAXIS, kInputEvent_None }, + { ctfUP_THRUSTKEY, kInputEvent_None }, + { ctfDOWN_THRUSTKEY, kInputEvent_None }, + { ctfRIGHT_THRUSTAXIS, kInputEvent_None }, + { ctfRIGHT_THRUSTKEY, kInputEvent_None }, + { ctfLEFT_THRUSTKEY, kInputEvent_None }, + { ctfPITCH_DOWNAXIS, kInputEvent_None }, + { ctfPITCH_DOWNKEY, kInputEvent_None }, + { ctfPITCH_UPKEY, kInputEvent_None }, + { ctfBANK_RIGHTAXIS, kInputEvent_None }, + { ctfBANK_RIGHTKEY, kInputEvent_None }, + { ctfBANK_LEFTKEY, kInputEvent_None }, + { ctfHEADING_RIGHTAXIS, kInputEvent_None }, + { ctfHEADING_RIGHTKEY, kInputEvent_None }, + { ctfHEADING_LEFTKEY, kInputEvent_None }, + { ctfFIREPRIMARY_BUTTON, kInputEvent_FirePrim_On }, + { ctfFIREPRIMARY_KEY, kInputEvent_None }, + { ctfFIREPRIMARY_KEY2, kInputEvent_None }, + { ctfFIRESECONDARY_BUTTON, kInputEvent_FireSecond_On }, + { ctfFIRESECONDARY_KEY, kInputEvent_None }, + { ctfTOGGLE_SLIDEKEY, kInputEvent_None }, + { ctfTOGGLE_SLIDEBUTTON, kInputEvent_Slide_On }, + { ctfFIREFLARE_KEY, kInputEvent_None }, + { ctfFIREFLARE_BUTTON, kInputEvent_Flare }, + { ctfUP_BUTTON, kInputEvent_SlideUp_On }, + { ctfDOWN_BUTTON, kInputEvent_SlideDown_On }, + { ctfLEFT_BUTTON, kInputEvent_SlideLeft_On }, + { ctfRIGHT_BUTTON, kInputEvent_SlideRight_On }, + { ctfAFTERBURN_KEY, kInputEvent_None }, + { ctfAFTERBURN_BUTTON, kInputEvent_AfterBurn_On }, + { ctfFORWARD_BUTTON, kInputEvent_Accelerate_On }, + { ctfREVERSE_BUTTON, kInputEvent_Reverse_On }, + { ctfTOGGLE_BANKKEY, kInputEvent_None }, + { ctfTOGGLE_BANKBUTTON, kInputEvent_Bank_On }, + { ctfHEADING_LEFTBUTTON, kInputEvent_TurnLeft_On }, + { ctfHEADING_RIGHTBUTTON, kInputEvent_TurnRight_On }, + { ctfPITCH_UPBUTTON, kInputEvent_PitchUp_On }, + { ctfPITCH_DOWNBUTTON, kInputEvent_PitchDown_On }, + { ctfBANK_LEFTBUTTON, kInputEvent_BankLeft_On }, + { ctfBANK_RIGHTBUTTON, kInputEvent_BankRight_On }, + { ctfAUTOMAP_KEY, kInputEvent_None }, + { ctfPREV_INVKEY, kInputEvent_None }, + { ctfNEXT_INVKEY, kInputEvent_None }, + { ctfINV_USEKEY, kInputEvent_None }, + { ctfPREV_CNTMSKEY, kInputEvent_None }, + { ctfNEXT_CNTMSKEY, kInputEvent_None }, + { ctfCNTMS_USEKEY, kInputEvent_None }, + { ctfHEADLIGHT_KEY, kInputEvent_None }, + { ctfHEADLIGHT_BUTTON, kInputEvent_HeadLight }, + { ctfAUTOMAP_BUTTON, kInputEvent_Map }, + { ctfPREV_INVBTN, kInputEvent_InventoryPrev }, + { ctfNEXT_INVBTN, kInputEvent_InventoryNext }, + { ctfINV_USEBTN, kInputEvent_InventoryUse }, + { ctfPREV_CNTMSBTN, kInputEvent_CounterPrev }, + { ctfNEXT_CNTMSBTN, kInputEvent_CounterNext }, + { ctfCNTMS_USEBTN, kInputEvent_CounterUse }, + { ctfWPNSEL_PCYCLEKEY, kInputEvent_None }, + { ctfWPNSEL_PCYCLEBTN, kInputEvent_NextPrim }, + { ctfWPNSEL_SCYCLEKEY, kInputEvent_None }, + { ctfWPNSEL_SCYCLEBTN, kInputEvent_NextSecond }, + { ctfREARVIEW_KEY, kInputEvent_None }, + { ctfREARVIEW_BTN, kInputEvent_RearView_On }, + { ctfAUDIOTAUNT1_KEY, kInputEvent_None }, + { ctfAUDIOTAUNT1_BTN, kInputEvent_ATaunt1 }, + { ctfAUDIOTAUNT2_KEY, kInputEvent_None }, + { ctfAUDIOTAUNT2_BTN, kInputEvent_ATaunt2 }, + { ctfAUDIOTAUNT3_KEY, kInputEvent_None }, + { ctfAUDIOTAUNT3_BTN, kInputEvent_ATaunt3 }, + { ctfAUDIOTAUNT4_KEY, kInputEvent_None }, + { ctfAUDIOTAUNT4_BTN, kInputEvent_ATaunt4 } +}; +inline ubyte ConvertPC_IS(ubyte action) +{ + if(m_pc_is_map[action].pcAction == action) { + return m_pc_is_map[action].isEvent; + } else { + for(int i = 0; i < NUM_CONTROLLER_FUNCTIONS; i++) { + if(m_pc_is_map[i].pcAction == action) { + return m_pc_is_map[i].isEvent; + } + } + } + return 0; +} + +struct { + float DownTime; + float UpTime; + bool state; +} iSbutton[kNumEvents]; + +gameMacController::gameMacController(int num_funcs, ct_function* gameFunctions, char *remote_adr) + : gameController(num_funcs, gameFunctions) +{ + for(int i = 0; i < kNumEvents; i++) { + iSbutton[i].DownTime = 0.0; + iSbutton[i].UpTime = 0.0; + iSbutton[i].state = false; + + } +// inSprocket_Init(); + + enum_controllers(remote_adr); + for (int i = 0; i < num_funcs; i++) + assign_function(&gameFunctions[i]); + m_Suspended = 0; +// m_frame_timer_ms = -1; +// m_frame_time = 1.0f; +// m_ControlList[MC_JOY].deadzone = 8; + + gameMacController::flush(); +} +gameMacController::~gameMacController(void) +{ +// inSprocket_Exit(); +} +// these functions suspend or resume any controller reading. this is really only useful for +// preemptive controller polling, but they should be used to activate and deactivate controller +// reading. +void gameMacController::suspend() +{ + inSprocket_Activate(false); + + for(int i = 0; i < kNumEvents; i++) { + iSbutton[i].DownTime = 0.0; + iSbutton[i].UpTime = 0.0; + iSbutton[i].state = false; + + } +} +void gameMacController::flush() +{ + InSprocket_Flush(); + for(int i=0; i < kNumEvents; i++) { + iSbutton[i].DownTime = 0; + iSbutton[i].UpTime = 0; + iSbutton[i].state = false; + } +} + +void gameMacController::resume() +{ + inSprocket_Activate(true); +} +// this functions polls the controllers if needed. some systems may not need to implement +// this function. +#define IS_MOUSE 0 +#define IS_JOY 1 + +float is_mouse[6]; +int is_joy[6]; +void MacInSprocketHandler(void) +{ + TInputEvent iSevent; + + float cur_time = timer_GetTime(); + + is_mouse[kNeed_Yaw] = inSprocket_GetMouse(kNeed_Yaw); + is_mouse[kNeed_Pitch] = inSprocket_GetMouse(kNeed_Pitch); + is_mouse[kNeed_Roll] = inSprocket_GetMouse(kNeed_Roll); + is_mouse[kNeed_MoveX] = inSprocket_GetMouse(kNeed_MoveX); + is_mouse[kNeed_MoveZ] = inSprocket_GetMouse(kNeed_MoveZ); + is_mouse[kNeed_MoveY] = inSprocket_GetMouse(kNeed_MoveY); + is_joy[kNeed_Yaw] = inSprocket_GetAxisInt(kNeed_Yaw); + is_joy[kNeed_Pitch] = inSprocket_GetAxisInt(kNeed_Pitch); + is_joy[kNeed_Roll] = inSprocket_GetAxisInt(kNeed_Roll); + is_joy[kNeed_MoveX] = inSprocket_GetAxisInt(kNeed_MoveX); + is_joy[kNeed_MoveZ] = inSprocket_GetAxisInt(kNeed_MoveZ); + is_joy[kNeed_MoveY] = inSprocket_GetAxisInt(kNeed_MoveY); + + while((iSevent = inSprocket_GetButtonEvent()) != kInputEvent_None) { + switch(iSevent) { + case kInputEvent_Accelerate_On: + case kInputEvent_Reverse_On: + case kInputEvent_SlideLeft_On: + case kInputEvent_SlideRight_On: + case kInputEvent_SlideUp_On: + case kInputEvent_SlideDown_On: + case kInputEvent_BankLeft_On: + case kInputEvent_BankRight_On: + case kInputEvent_TurnLeft_On: + case kInputEvent_TurnRight_On: + case kInputEvent_PitchUp_On: + case kInputEvent_PitchDown_On: + case kInputEvent_FirePrim_On: + case kInputEvent_FireSecond_On: + iSbutton[iSevent].state = true; + if(iSbutton[iSevent].DownTime == 0.0) + iSbutton[iSevent].DownTime = cur_time; + break; + + case kInputEvent_Accelerate_Off: + case kInputEvent_Reverse_Off: + case kInputEvent_SlideLeft_Off: + case kInputEvent_SlideRight_Off: + case kInputEvent_SlideUp_Off: + case kInputEvent_SlideDown_Off: + case kInputEvent_BankLeft_Off: + case kInputEvent_BankRight_Off: + case kInputEvent_TurnLeft_Off: + case kInputEvent_TurnRight_Off: + case kInputEvent_PitchUp_Off: + case kInputEvent_PitchDown_Off: + case kInputEvent_FirePrim_Off: + case kInputEvent_FireSecond_Off: + iSbutton[iSevent-1].state = false; + iSbutton[iSevent-1].UpTime = cur_time; + break; + + case kInputEvent_CounterUse: + case kInputEvent_CounterNext: + case kInputEvent_CounterPrev: + case kInputEvent_InventoryUse: + case kInputEvent_InventoryNext: + case kInputEvent_InventoryPrev: + case kInputEvent_NextPrim: + case kInputEvent_NextSecond: + case kInputEvent_Map: + case kInputEvent_Flare: + case kInputEvent_HeadLight: + case kInputEvent_Bank_On: + case kInputEvent_Slide_On: + case kInputEvent_AfterBurn_On: + case kInputEvent_RearView_On: + iSbutton[iSevent].state = true; + break; + case kInputEvent_Bank_Off: + case kInputEvent_Slide_Off: + case kInputEvent_AfterBurn_Off: + case kInputEvent_RearView_Off: + iSbutton[iSevent-1].state = false; + break; + } + } +} +void gameMacController::poll() +{ +// TInputEvent iSevent; +// extern float Frametime; + /* + float cur_time = timer_GetTime(); + +// while((iSevent = inSprocket_GetEvent()) != kInputEvent_None) { + for(int i = 0; i < kNumEvents; i++) { + switch(i) { + case kInputEvent_SlideLeft_On: + mprintf((1, "ln %f %f\n", iSbutton[i].UpTime, iSbutton[i].DownTime)); + break; + } + switch(i) { + case kInputEvent_EnergyShield_Off: + case kInputEvent_Accelerate_Off: + case kInputEvent_Reverse_Off: + case kInputEvent_SlideLeft_Off: + case kInputEvent_SlideRight_Off: + case kInputEvent_SlideUp_Off: + case kInputEvent_SlideDown_Off: + case kInputEvent_BankLeft_Off: + case kInputEvent_BankRight_Off: + case kInputEvent_TurnLeft_Off: + case kInputEvent_TurnRight_Off: + case kInputEvent_PitchUp_Off: + case kInputEvent_PitchDown_Off: + iSbutton[i-1].UpTime = cur_time; + break; + case kInputEvent_Flare: + case kInputEvent_Bomb: + case kInputEvent_Map: + case kInputEvent_NextPrim: + case kInputEvent_PrevPrim: + case kInputEvent_NextSecond: + case kInputEvent_PrevSecond: + case kInputEvent_HeadLight: + case kInputEvent_Bank_On: + case kInputEvent_Slide_On: + case kInputEvent_FirePrim_On: + case kInputEvent_FireSecond_On: + case kInputEvent_AfterBurn_On: + case kInputEvent_RearView_On: + case kInputEvent_CounterPrev: + if(iSbutton[i].DownTime != 0.0) + iSbutton[i].DeltaTime = 1.0; + + if(iSbutton[i].UpTime) { + iSbutton[i].DownTime = iSbutton[i].UpTime = 0.0; + } + break; + + case kInputEvent_Accelerate_On: + case kInputEvent_Reverse_On: + case kInputEvent_SlideLeft_On: + case kInputEvent_BankRight_On: + case kInputEvent_TurnRight_On: + case kInputEvent_SlideDown_On: + case kInputEvent_PitchUp_On: + case kInputEvent_SlideRight_On: + case kInputEvent_BankLeft_On: + case kInputEvent_TurnLeft_On: + case kInputEvent_SlideUp_On: + case kInputEvent_PitchDown_On: + if(iSbutton[i].DownTime != 0.0) { + iSbutton[i].DeltaTime = cur_time - iSbutton[i].DownTime; + } + break; + + case kInputEvent_Bank_Off: + case kInputEvent_Slide_Off: + + case kInputEvent_FirePrim_Off: + case kInputEvent_FireSecond_Off: + case kInputEvent_AfterBurn_Off: + case kInputEvent_RearView_Off: + case kInputEvent_InventoryPrev: + case kInputEvent_CounterNext: + case kInputEvent_EnergyShield_Off: + case kInputEvent_Accelerate_Off: + case kInputEvent_Reverse_Off: + case kInputEvent_SlideLeft_Off: + case kInputEvent_SlideRight_Off: + case kInputEvent_SlideUp_Off: + case kInputEvent_SlideDown_Off: + case kInputEvent_BankLeft_Off: + case kInputEvent_BankRight_Off: + case kInputEvent_TurnLeft_Off: + case kInputEvent_TurnRight_Off: + case kInputEvent_PitchUp_Off: + case kInputEvent_PitchDown_Off: + if(iSbutton[i-1].DownTime != 0.0) { + if(iSbutton[i-1].UpTime != 0) + iSbutton[i-1].DeltaTime = iSbutton[i-1].UpTime - iSbutton[i-1].DownTime; + iSbutton[i-1].UpTime = iSbutton[i-1].DownTime = 0.0; + } else { + iSbutton[i-1].DeltaTime = 0.0; + } + break; + } + } +*/ +} +// all systems need to implement this function. this returns information about the controller +bool gameMacController::get_packet(int id, ct_packet *packet, ct_format alt_format) +{ + bool success = false; + float val= (float)0.0; + packet->flags = 0; + int i; + packet->format = (alt_format != ctNoFormat) ? alt_format : m_ElementList[id].format; + alt_format = packet->format; + if (!m_ElementList[id].enabled) { + return success; + } + if(m_ElementList[id].ctype[0] == ctKey) + val += get_key_value(m_ElementList[id].value[0], alt_format); + if(m_ElementList[id].ctype[1] == ctKey) + val += get_key_value(m_ElementList[id].value[1], alt_format); + + if(m_ElementList[id].ctype[0] == ctButton) + val += get_button_value(id); + if(m_ElementList[id].ctype[1] == ctButton) + val += get_button_value(id); + + if(m_ElementList[id].ctype[0] == ctAxis) + val += get_axis_value(0, m_ElementList[id].value[0], alt_format, false); + + if (val) + packet->flags |= CTPK_ELEMENTACTIVE; + packet->value = val; + + return success; +} +gameController *CreateController(int num_funcs, ct_function* gameFunctions, char *remote_ip) +{ + mprintf((0, "Creating mac game controller.\n")); + + return new gameMacController(num_funcs, gameFunctions); +} +void DestroyController(gameController *ctl) +{ + mprintf((0, "Destroying mac game controller.\n")); + + if (ctl) + delete ctl; +} +// temporarily enables or disables a function +void gameMacController::enable_function(int id, bool enable) +{ + m_ElementList[id].enabled = enable; +} +// returns information about a requested function +void gameMacController::get_controller_function(int id, ct_type *type, ct_config_data *value, ubyte *flags) +{ + type[0] = m_ElementList[id].ctype[0]; + type[1] = m_ElementList[id].ctype[1]; + *value = makeword(CONTROLLER_CTL_INFO(m_ElementList[id].ctl[0],m_ElementList[id].ctl[1]), + CONTROLLER_CTL_VALUE(m_ElementList[id].value[0], m_ElementList[id].value[1])); + flags[0] = m_ElementList[id].flags[0]; + flags[1] = m_ElementList[id].flags[1]; +} +// sets the configuration of a function +void gameMacController::set_controller_function(int id, const ct_type *type, ct_config_data value, const ubyte *flags) +{ + ct_element elem; + if (id >= CT_MAX_ELEMENTS) return; +// auto assign keyboard controller if type is key. + if (type[0] == ctKey) + elem.ctl[0] = CONTROLLER_CTL1_INFO(0); + else + elem.ctl[0] = CONTROLLER_CTL1_INFO(CONTROLLER_INFO(value)); + if (type[1] == ctKey) + elem.ctl[1] = CONTROLLER_CTL2_INFO(0); + else + elem.ctl[1] = CONTROLLER_CTL2_INFO(CONTROLLER_INFO(value)); + elem.ctype[0] = type[0]; + elem.ctype[1] = type[1]; + elem.format = m_ElementList[id].format; + elem.value[0] = CONTROLLER_CTL1_VALUE(CONTROLLER_VALUE(value)); + elem.value[1] = CONTROLLER_CTL2_VALUE(CONTROLLER_VALUE(value)); + elem.flags[0] = flags[0]; + elem.flags[1] = flags[1]; + elem.enabled = m_ElementList[id].enabled; +// if controller doesn't exist, set it to invalid. + if (elem.ctl[0] > CT_MAX_CONTROLLERS) + elem.ctl[0] = NULL_WINCONTROLLER; + if (elem.ctl[1] >= CT_MAX_CONTROLLERS) + elem.ctl[1] = NULL_WINCONTROLLER; + assign_element(id, &elem); +} +#define TABLE_LOOKUP +// note controller is index into ControlList. +float gameMacController::get_axis_value(sbyte controller, ubyte axis, ct_format format, bool invert) +{ + float axisval = 0.0; + + switch (axis) + { + // note we take care of mouse controls and external controls here + case CT_X_AXIS: + axisval += is_mouse[kNeed_Yaw]; + if(ABS(is_joy[kNeed_Yaw]) > m_ControlList[MC_JOY].deadzone && m_ControlList[MC_JOY].sens[AXIS_YAW] != 0.0) + { + #if defined TABLE_LOOKUP + axisval += SIGN(is_joy[kNeed_Yaw])*m_ControlList[MC_JOY].sens_curve[AXIS_YAW][ABS(is_joy[kNeed_Yaw])]; +#elif defined REAL_POW + if(m_ControlList[MC_JOY].sens[AXIS_YAW] == 1.0) + axisval += is_joy[kNeed_Yaw]; + else + axisval += powf(ABS(is_joy[kNeed_Yaw]), m_ControlList[MC_JOY].inv_sens[AXIS_YAW])*SIGN(is_joy[kNeed_Yaw]); +#else + axisval += is_joy[kNeed_Yaw]*m_ControlList[MC_JOY].sens[AXIS_YAW]; +#endif + } + break; + + case CT_Y_AXIS: + axisval -= is_mouse[kNeed_Pitch]; + if(ABS(is_joy[kNeed_Pitch]) > m_ControlList[MC_JOY].deadzone && m_ControlList[MC_JOY].sens[AXIS_PITCH] != 0.0) + { +#if defined TABLE_LOOKUP + axisval -= SIGN(is_joy[kNeed_Pitch])*m_ControlList[MC_JOY].sens_curve[AXIS_PITCH][ABS((int)(is_joy[kNeed_Pitch]))]; +#elif defined REAL_POW + if(m_ControlList[MC_JOY].sens[AXIS_PITCH] == 1.0) + axisval -= is_joy[kNeed_Pitch]; + else + axisval -= powf(ABS(is_joy[kNeed_Pitch]), m_ControlList[MC_JOY].inv_sens[AXIS_PITCH])*SIGN(is_joy[kNeed_Pitch]); +#else + axisval -= is_joy[kNeed_Pitch]*m_ControlList[MC_JOY].sens[AXIS_PITCH]; +#endif + } + break; + + case CT_R_AXIS: + axisval += is_mouse[kNeed_Roll]; + if(ABS(is_joy[kNeed_Roll]) > m_ControlList[MC_JOY].deadzone && m_ControlList[MC_JOY].sens[AXIS_ROLL] != 0.0) + { +#if defined TABLE_LOOKUP + axisval += SIGN(is_joy[kNeed_Roll])*m_ControlList[MC_JOY].sens_curve[AXIS_ROLL][ABS((int)(is_joy[kNeed_Roll]))]; +#elif defined REAL_POW + if(m_ControlList[MC_JOY].sens[AXIS_ROLL] == 1.0) + axisval += is_joy[kNeed_Roll]; + else + axisval += powf(ABS(is_joy[kNeed_Roll]), m_ControlList[MC_JOY].inv_sens[AXIS_ROLL])*SIGN(is_joy[kNeed_Roll]); +#else + axisval += is_joy[kNeed_Roll]*m_ControlList[MC_JOY].sens[AXIS_ROLL]; +#endif + } + break; + + case CT_Z_AXIS: + axisval -= is_mouse[kNeed_MoveZ]; + if(ABS(is_joy[kNeed_MoveZ]) > m_ControlList[MC_JOY].deadzone && m_ControlList[MC_JOY].sens[AXIS_FORWARD] != 0.0) + { +#if defined TABLE_LOOKUP + axisval -= SIGN(is_joy[kNeed_MoveZ])*m_ControlList[MC_JOY].sens_curve[AXIS_FORWARD][ABS((int)(is_joy[kNeed_MoveZ]))]; +#elif defined REAL_POW + if(m_ControlList[MC_JOY].sens[AXIS_FORWARD] == 1.0) + axisval -= is_joy[kNeed_MoveZ]; + else + axisval -= powf(ABS(is_joy[kNeed_MoveZ]), m_ControlList[MC_JOY].inv_sens[AXIS_FORWARD])*SIGN(is_joy[kNeed_MoveZ]); +#else + axisval -= is_joy[kNeed_MoveZ]*m_ControlList[MC_JOY].sens[AXIS_FORWARD]; +#endif + } + break; + + case CT_U_AXIS: + axisval += is_mouse[kNeed_MoveX]; + if(ABS(is_joy[kNeed_MoveX]) > m_ControlList[MC_JOY].deadzone && m_ControlList[MC_JOY].sens[AXIS_SIDE] != 0.0) + { +#if defined TABLE_LOOKUP + axisval += SIGN(is_joy[kNeed_MoveX])*m_ControlList[MC_JOY].sens_curve[AXIS_SIDE][ABS((int)(is_joy[kNeed_MoveX]))]; +#elif defined REAL_POW + if(m_ControlList[MC_JOY].sens[AXIS_SIDE] == 1.0) + axisval += is_joy[kNeed_MoveX]; + else + axisval += powf(ABS(is_joy[kNeed_MoveX]), m_ControlList[MC_JOY].inv_sens[AXIS_SIDE])*SIGN(is_joy[kNeed_MoveX]); +#else + axisval += is_joy[kNeed_MoveX]*m_ControlList[MC_JOY].sens[AXIS_SIDE]; +#endif + } + break; + + case CT_V_AXIS: + axisval -= is_mouse[kNeed_MoveY]; + if(ABS(is_joy[kNeed_MoveY]) > m_ControlList[MC_JOY].deadzone && m_ControlList[MC_JOY].sens[AXIS_VERT] != 0.0) { +#if defined TABLE_LOOKUP + axisval -= SIGN(is_joy[kNeed_MoveY])*m_ControlList[MC_JOY].sens_curve[AXIS_VERT][ABS((int)(is_joy[kNeed_MoveY]))]; +#elif defined REAL_POW + if(m_ControlList[MC_JOY].sens[AXIS_VERT] == 1.0) + axisval -= is_joy[kNeed_MoveY]; + else + axisval -= powf(ABS(is_joy[kNeed_MoveY]), m_ControlList[MC_JOY].inv_sens[AXIS_VERT])*SIGN(is_joy[kNeed_MoveY]); +#else + axisval -= is_joy[kNeed_MoveY]*m_ControlList[MC_JOY].sens[AXIS_VERT]; +#endif + } + break; + default: + mprintf((2, "invalid axis %d\n", axis)); + Int3(); // NOT A VALID AXIS + break; + } +// mprintf((2, "x %d y %d z %d p %d h %d r %d\n", is_joy[kNeed_MoveX],is_joy[kNeed_MoveY],is_joy[kNeed_MoveZ],is_joy[kNeed_Pitch],is_joy[kNeed_Yaw],is_joy[kNeed_Roll])); + + return axisval; +} +// get keyboard info +float gameMacController::get_key_value(int key, ct_format format) +{ + float val = (float)0.0; + ASSERT(key < DDIO_MAX_KEYS); + switch (format) + { + // note we take care of mouse controls and external controls here + case ctDigital: + if (KEY_STATE(key)) + val = 1.0f; + break; + case ctDownCount: + val = (float)ddio_KeyDownCount(key); + break; + case ctTime: + val = (float)ddio_KeyDownTime(key); + break; + default: + mprintf((1, "gameController::key unsupported format for function\n")); + } + return val; +} +// get keyboard info +float gameMacController::get_button_value(int button) +{ + float down_time = (float)0.0; + int sprockButton = ConvertPC_IS(button); + float cur_time = timer_GetTime(); + + + switch(button) { + case ctfAFTERBURN_BUTTON: + case ctfTOGGLE_SLIDEBUTTON: + case ctfTOGGLE_BANKBUTTON: + if(iSbutton[sprockButton].state) + down_time = 1; + else + down_time = 0; + break; + + case ctfWPNSEL_PCYCLEBTN: + case ctfWPNSEL_SCYCLEBTN: + case ctfREARVIEW_BTN: + case ctfPREV_CNTMSBTN: + case ctfNEXT_CNTMSBTN: + case ctfCNTMS_USEBTN: + case ctfPREV_INVBTN: + case ctfNEXT_INVBTN: + case ctfINV_USEBTN: + case ctfHEADLIGHT_BUTTON: + case ctfFIREFLARE_BUTTON: + case ctfAUTOMAP_BUTTON: + if(iSbutton[sprockButton].state) { + down_time = 1; + iSbutton[sprockButton].state = false; + } else { + down_time = 0; + } + break; + + default: + if(iSbutton[sprockButton].state) { + if(iSbutton[sprockButton].DownTime != 0.0) { + down_time = cur_time - iSbutton[sprockButton].DownTime; + } + } else { + if(iSbutton[sprockButton].UpTime != 0.0 && iSbutton[sprockButton].DownTime != 0.0) { + down_time = iSbutton[sprockButton].UpTime - iSbutton[sprockButton].DownTime; + iSbutton[sprockButton].UpTime = iSbutton[sprockButton].DownTime = 0.0; + } + } + } + if(!(button == ctfFIREPRIMARY_BUTTON || button == ctfFIRESECONDARY_BUTTON)) + if(down_time > 1.0) + down_time = 1.0; + return down_time; +} +int gameMacController::assign_function(ct_function *func) +{ +// for now this is a straight forward translation (that is, no mapping of needs to controller +// list to create elements. + ct_element elem; + int i; + for (i = 0; i < CTLBINDS_PER_FUNC; i++) + { + elem.ctl[i] = NULL_WINCONTROLLER; + switch (func->ctype[i]) + { + case ctNone: + break; + case ctKey: + elem.ctl[i] = 0; + break; + case ctAxis: + elem.ctl[i] = get_axis_controller(func->value[i]); + break; + case ctButton: + elem.ctl[i] = get_button_controller(func->value[i]); + break; + case ctMouseAxis: + elem.ctl[i] = 1; + break; + case ctMouseButton: + // find a free mouse button. + if ((m_ControlList[1].btnmask & (1 << (func->value[i]-1))) && ((func->value[i]-1) < m_ControlList[1].buttons)) { + elem.ctl[i] = 1; + } + break; + case ctPOV: + elem.ctl[i] = get_pov_controller(); + break; + } + elem.ctype[i] = func->ctype[i]; + elem.value[i] = func->value[i]; + } + elem.format = func->format; + elem.flags[0] = func->flags[0]; + elem.flags[1] = func->flags[1]; + elem.enabled = true; + assign_element(func->id, &elem); + return func->id; +} +// sets up an elements information structure +void gameMacController::assign_element(int id, ct_element *elem) +{ +// assign element, check to see if valid. + int i; + m_ElementList[id].format = elem->format; + m_ElementList[id].flags[0] = elem->flags[0]; + m_ElementList[id].flags[1] = elem->flags[1]; + m_ElementList[id].enabled = elem->enabled; +// look through each controller and validate each element + for (i = 0; i < CTLBINDS_PER_FUNC; i++) + { + m_ElementList[id].ctl[i] = elem->ctl[i]; + m_ElementList[id].value[i] = elem->value[i]; + m_ElementList[id].ctype[i] = elem->ctype[i]; + if (m_ElementList[id].ctl[i] != NULL_WINCONTROLLER) { + // this function shouldn't do any error checking!!!! keep same controller values and bindings unless + // bindings are truly bogus. + switch (m_ElementList[id].ctype[i]) + { + case ctMouseButton: + case ctButton: + if (elem->value[i] > CT_MAX_BUTTONS) { + m_ElementList[id].ctl[i] = NULL_WINCONTROLLER; + m_ElementList[id].value[i] = NULL_BINDING; + } + break; + case ctMouseAxis: + case ctAxis: +// if (!(m_ControlList[elem->ctl[i]].flags & (1<<(elem->value[i]-1)))) +// m_ElementList[id].ctl[i] = NULL_WINCONTROLLER; + break; + case ctPOV: +// if (!(m_ControlList[elem->ctl[i]].flags & CTF_POV)) +// m_ElementList[id].ctl[i] = NULL_WINCONTROLLER; + break; + case ctKey: + break; + default: + m_ElementList[id].value[i] = NULL_BINDING; + m_ElementList[id].ctl[i] = NULL_WINCONTROLLER; + } + } + else { + m_ElementList[id].value[i] = NULL_BINDING; + } + } +} +sbyte gameMacController::get_axis_controller(ubyte axis) +{ + return 2; +// return NULL_CONTROLLER; +} +sbyte gameMacController::get_button_controller(ubyte btn) +{ + return NULL_CONTROLLER; +} +sbyte gameMacController::get_pov_controller() +{ + return NULL_CONTROLLER; +} +// enumerate all controllers on system +bool gameMacController::enum_controllers(char *remote_adr) +{ + int num_devs=0, dev; + int i; + for (i=0; i< CT_MAX_CONTROLLERS;i++) + m_ControlList[i].id = CTID_INVALID; +// Add keyboard controller + m_ControlList[num_devs].id = CTID_KEYBOARD; + m_ControlList[num_devs].buttons = 0; + m_ControlList[num_devs].flags = 0; + num_devs++; + return true; +} +// gets sensitivity of axis item +float gameMacController::get_axis_sensitivity(ct_type axis_type, ubyte axis) +{ + axis--; + ASSERT(axis < CT_NUM_AXES); + switch (axis_type) + { + case ctMouseAxis: + return m_ControlList[1].sens[axis]; + case ctAxis: + return m_ControlList[MC_JOY].sens[axis]; + default: + Int3(); + } + return 0.0f; +} +// sets sensitivity of axis item +void gameMacController::set_axis_sensitivity(ct_type axis_type, ubyte axis, float val) +{ + int i; + axis--; + ASSERT(axis < CT_NUM_AXES); + switch (axis_type) + { + case ctMouseAxis: + m_ControlList[1].sens[axis] = val; + break; + case ctAxis: + for (i = 2; i < CT_MAX_CONTROLLERS;i++) { + m_ControlList[i].sens[axis] = val; +// m_ControlList[i].inv_sens[axis] = 1.0/val; + m_ControlList[i].sens_curve[axis][0] = 0; + for(int j = 1; j <= JOY_SAMPLES; j++) { + m_ControlList[i].sens_curve[axis][j] = powf((float)j/(float)(JOY_SAMPLES), 1.0/val); + } + } + break; + default: + Int3(); + } +} +//#ifndef MACINTOSH +// toggles use of deadzone for controllers. ctl can be 0 to ??? +// dead zone is from 0.0 to 0.5 +void gameMacController::set_controller_deadzone(int ctl, float deadzone) +{ + if (deadzone < 0.0) deadzone = 0.0; + if (deadzone > 25) deadzone = 25; + m_ControlList[MC_JOY].deadzone = deadzone*2; +} +float gameMacController::get_controller_deadzone(int ctl) +{ + float val = m_ControlList[MC_JOY].deadzone/2; + return val; +} + +//#endif +char Ctltext_AxisBindings[][16] = { + "", "X-axis", "Y-axis", "Z-axis", "R-axis", "U-axis", "V-axis" +}; +char Ctltext_BtnBindings[][16] = { + "", "btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8", "btn9", "btn10", "btn11", + "btn12", "btn13", "btn14", "btn15", "btn16", "btn17", "btn18", "btn19", "btn20", "btn21", "btn22", + "btn23", "btn24", "btn25", "btn26", "btn27", "btn28", "btn29", "btn30", "btn31", "btn32" +}; +char Ctltext_PovBindings[][16] = { + "","pov-U", "pov-R", "pov-D", "pov-L" +}; +#define NUM_AXISBINDSTRINGS (sizeof(Ctltext_AxisBindings)/sizeof(Ctltext_AxisBindings[0])) +#define NUM_BTNBINDSTRINGS (sizeof(Ctltext_BtnBindings)/sizeof(Ctltext_AxisBindings[0])) +#ifdef MACINTOSH +// retrieves binding text for desired function, binding, etc. +const char *gameMacController::get_binding_text(ct_type type, ubyte ctrl, ubyte bind) +{ + static char binding_text[16]; + const char *str; + if (ctrl == NULL_CONTROLLER) { + return NULL; + } + switch (type) + { + case ctAxis: + { + ASSERT (bind < NUM_AXISBINDSTRINGS); + str = Ctltext_AxisBindings[bind]; + if ((ctrl-2) > 0) { + sprintf(binding_text,"J%d:%s", (ctrl-2)+1, str); + } + else { + return str; + } + break; + } + case ctMouseAxis: + { + str = ddio_MouseGetAxisText(((sbyte)bind)-1); + return str; + } + case ctButton: + { + ASSERT(bind < NUM_BTNBINDSTRINGS); + str = Ctltext_BtnBindings[bind]; + if ((ctrl-2) > 0) { + sprintf(binding_text,"J%d:%s", (ctrl-2)+1, str); + } + else { + return str; + } + break; + } + case ctMouseButton: + { + str = ddio_MouseGetBtnText(((sbyte)bind)-1); + return str; + } + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + { + ushort povpos = bind; + if (povpos == JOYPOV_UP) + str = Ctltext_PovBindings[1]; + else if (povpos == JOYPOV_DOWN) + str = Ctltext_PovBindings[3]; + else if (povpos == JOYPOV_LEFT) + str = Ctltext_PovBindings[4]; + else if (povpos == JOYPOV_RIGHT) + str = Ctltext_PovBindings[2]; + else + str = Ctltext_PovBindings[0]; + if ((ctrl-2) > 0) { + if (type-ctPOV) { + sprintf(binding_text,"J%d:%s%d", (ctrl-2)+1, str, type-ctPOV); + } + else { + sprintf(binding_text,"J%d:%s", (ctrl-2)+1, str); + } + } + else { + if (type-ctPOV) { + sprintf(binding_text,"%s%d", str, type-ctPOV); + } + else { + return str; + } + } + break; + } + case ctKey: + break; + default: + if (type == ctNone) { + Int3(); + } + binding_text[0] = 0; + } + return binding_text; +} +#endif \ No newline at end of file diff --git a/ddio_mac/macController.h b/ddio_mac/macController.h new file mode 100644 index 000000000..b32dd5648 --- /dev/null +++ b/ddio_mac/macController.h @@ -0,0 +1,199 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/macController.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * Joystick, InputSprocket controller support + * + * $Log: macController.h,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 4 3/20/00 12:43p Matt + * Merge of Duane's post-1.3 changes. + * + * 3 10/21/99 3:33p Jeff + * Macintosh merges + * + * 2 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 3 5/21/97 5:13 PM Jeremy + * changed constructor to accept elements of type ct_function instead of + * ct_need + * + * 2 5/13/97 11:45 AM Jeremy + * prototype for gameMacController object + * + * 1 5/12/97 4:45 PM Jeremy + * initial checkin + * + * 1 5/11/97 8:09 PM Jeremy + * Initial check in + * + * $NoKeywords: $ + */ +#ifndef MAC_CONTROLLER_H +#define MAC_CONTROLLER_H +// ANSI Headers +// Macintosh Headers +// Descent 3 Headers +#include "Controller.h" +#include "joystick.h" +#define NULL_WINCONTROLLER ((sbyte)NULL_CONTROLLER) +const CTF_POV = 64, // POV control + CTF_POVANALOG = 128; // Analog POV. +const unsigned CTF_X_AXIS = (1<<(CT_X_AXIS-1)), // AXIS constants for ctAxis + CTF_Y_AXIS = (1<<(CT_Y_AXIS-1)), + CTF_Z_AXIS = (1<<(CT_Z_AXIS-1)), + CTF_R_AXIS = (1<<(CT_R_AXIS-1)), + CTF_U_AXIS = (1<<(CT_U_AXIS-1)), + CTF_V_AXIS = (1<<(CT_V_AXIS-1)); +const unsigned CT_MAX_CONTROLLERS = 4, //DAJ utb 32 + CT_MAX_ELEMENTS = 255, + CT_MAX_EXTCTLS = 16, + CT_MAX_BUTTONS = 32; +// rules for adding controllers +// any nonstandard special controllers should be added to the below list starting at +// CTID_MOUSE-1 (meaning a value of -3, -4 and so on) +const int CTID_KEYBOARD = -1, // always -1 for keyboards + CTID_MOUSE = -2, // always -2 for mice + CTID_INVALID = -3; // invalid controller +// External controls +// these are standard controllers handled through DDIO interface +// like joysticks, etc. +const int CTID_EXTCONTROL0 = 0; + +#define AXIS_YAW 0 +#define AXIS_PITCH 1 +#define AXIS_ROLL 2 +#define AXIS_SIDE 3 +#define AXIS_VERT 4 +#define AXIS_FORWARD 5 + +#define MC_KEY 0 +#define MC_MOUSE 1 +#define MC_JOY 2 + +#define JOY_SAMPLES 129 +void MacInSprocketHandler(void); +class gameMacController : public gameController +{ + public: + gameMacController(int num_funcs, ct_function* gameFunctions, char *remote_adr=NULL); + virtual ~gameMacController(void); + // these functions suspend or resume any controller reading. this is really only useful for + // preemptive controller polling, but they should be used to activate and deactivate controller + // reading. + virtual void suspend(); + virtual void resume(); + // this functions polls the controllers if needed. some systems may not need to implement + // this function. + virtual void poll(); +// flushes all controller information + virtual void flush(); +// returns the value of a requested controller type. + virtual ct_config_data get_controller_value(ct_type type_req) {return 0;}; +// sets the configuration of a function + virtual void set_controller_function(int id, const ct_type *type, ct_config_data value, const ubyte *flags); +// returns information about a requested function + virtual void get_controller_function(int id, ct_type *type, ct_config_data *value, ubyte *flags); +// temporarily enables or disables a function + virtual void enable_function(int id, bool enable); + virtual bool get_packet(int id, ct_packet *packet, ct_format alt_format=ctNoFormat); +// gets sensitivity of axis item + virtual float get_axis_sensitivity(ct_type axis_type, ubyte axis); +// sets sensitivity of axis item + virtual void set_axis_sensitivity(ct_type axis_type, ubyte axis, float val); +// assigns an individual function + virtual int assign_function(ct_function *fn); //DAJ {return 0;}; +// activates or deactivates mouse and or controller + virtual void mask_controllers(bool joystick, bool mouse) {}; +// retrieves binding text for desired function, binding, etc. + virtual const char *get_binding_text(ct_type type, ubyte ctrl, ubyte bind); +// get raw values for the controllers + virtual int get_mouse_raw_values(int *x, int *y) {return 0;}; + virtual unsigned get_joy_raw_values(int *x, int *y) {return 0;}; +// set dead zone + virtual void set_controller_deadzone(int ctl, float deadzone); +// get dead zone + virtual float get_controller_deadzone(int ctl); +private: + int m_NumControls; // number of controllers available + int m_Suspended; // is controller polling suspended? + bool m_JoyActive, m_MouseActive; // enables or disables mouse, joystick control + struct t_controller { + int id; + ushort flags; + ushort buttons; + unsigned btnmask; +// float normalizer[CT_NUM_AXES]; + float sens[CT_NUM_AXES+1]; + float inv_sens[CT_NUM_AXES+1]; + float sens_curve[CT_NUM_AXES+1][JOY_SAMPLES]; + ushort deadzone; +// float deadzone; + } m_ControlList[CT_MAX_CONTROLLERS]; // the control list. + struct ct_element { + ct_format format; + sbyte ctl[CTLBINDS_PER_FUNC]; + ubyte value[CTLBINDS_PER_FUNC]; + ct_type ctype[CTLBINDS_PER_FUNC]; + ubyte flags[2]; + bool enabled; + } m_ElementList[CT_MAX_ELEMENTS]; + bool enum_controllers(char *remote_adr); +// sets up an elements information structure + void assign_element(int id, ct_element *elem); +// this returns an index into the control list. + sbyte get_axis_controller(ubyte axis); +// returns controller with specified button + sbyte get_button_controller(ubyte btn); +// returns the controller with a pov hat + sbyte get_pov_controller(); +// note controller is index into ControlList. + float get_axis_value(sbyte controller, ubyte axis, ct_format format,bool invert = false); +// get value of button in seconds, presses, etc. + float get_button_value(sbyte controller, ct_format format, ubyte button); +// get value of pov (using JOYPOV values) + float get_pov_value(sbyte controller, ct_format format, ubyte pov); +// get keyboard info + float get_key_value(int key, ct_format format); +// get button value Duh! + float get_button_value(int button); +// toggles use of deadzone for controllers. ctl can be 0 to ??? + +private: +/* + struct t_msestate { + int x, y, z; + int mx,my; + unsigned btnmask; + } m_MseState; + struct t_extctlstate { + int x,y,z,r,u,v; + int pov; + int last_pov; + float povstarts[JOYPOV_DIR]; + float povtimes[JOYPOV_DIR]; + ubyte povpresses[JOYPOV_DIR]; + unsigned buttons; + ubyte btnpresses[CT_MAX_BUTTONS]; + float btnstarts[CT_MAX_BUTTONS]; + float btntimes[CT_MAX_BUTTONS]; + } m_ExtCtlStates[CT_MAX_EXTCTLS]; +// thread info. + longlong m_frame_timer_ms; + float m_frame_time; +// note id is id value from controller in control list. + void extctl_getpos(int id); + void extctl_geteval(int id); +// this gets timings for mouse buttons + void mouse_getpos(); + void mouse_geteval(); + static unsigned int ctrl_thread(void *this_ptr); + */ +}; +#endif \ No newline at end of file diff --git a/ddio_mac/macfile.cpp b/ddio_mac/macfile.cpp new file mode 100644 index 000000000..459d05cb4 --- /dev/null +++ b/ddio_mac/macfile.cpp @@ -0,0 +1,1040 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/macfile.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * File operations not covered properly in ANSI C + * + * $Log: macfile.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 4 10/21/99 3:33p Jeff + * Macintosh merges + * + * 3 8/01/99 10:37p Duane + * Works with paht+wildcard or just wildcard + * + * 2 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 6 5/15/97 1:44 AM Jeremy + * changed mprintf's to be standard (with newline at end), fixed two bugs: + * ddio_filediff was returning opposite of correct value, ddio function to + * set file time stamp had arguments in reverse order + * + * 5 5/9/97 8:04 PM Jeremy + * changed include of to + * + * 4 5/6/97 6:37 PM Jeremy + * implemented splitpath + * + * 3 5/6/97 4:22 PM Jeremy + * fixed a bug in ddio_MakePath + * + * 2 5/6/97 3:11 PM Jeremy + * added implementation of ddio_MakePath + * + * 1 3/13/97 6:53 PM Jeremy + * macintosh implementation of device dependent io functions + * + * $NoKeywords: $ + */ +// ANSI Headers +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +// Mac Headers +#include +#include +#include +// Descent 3 Headers +#include "mono.h" +#include "pserror.h" +#include "ddio.h" +#include "ddio_mac.h" +#include "psglob.h" +#include "mem.h" +#include "cfile.h" +// --------------------------------------------------------------------------- +// File Level Globals +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// Local File Prototypes +// --------------------------------------------------------------------------- +/******************************************* + Utility functions specific to the Mac OS +******************************************* +int +stricmp(const char* s1, const char* s2) +{ + char c1, c2; + while (1) + { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} +int +strnicmp(const char *s1, const char *s2, int n) +{ + int i; + char c1, c2; + for (i=0; i c2) return 1; + if (!c1) return 0; + } + return 0; +} +/************************/ +// --------------------------------------------------------------------------- +// File Operations +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// File operations +bool ddio_CreateDir(const char *path) +{ + bool failed = true; + failed = mkdir(path, 0); + + return !failed; +} +bool ddio_RemoveDir(const char *path) +{ + bool failed = true; + + failed = rmdir(path); + + return (!failed); +} +// retrieve the current working folder where file operation will occur. +// Note ---> The path in Get/Set working directory is in the *LOCAL* file system's syntax +// This pathname is relative *OR* fully qualified +static char SavedWorkingDir[_MAX_PATH]; + +void ddio_GetWorkingDir(char *path, int len) +{ + getcwd(path, len); + len = strlen(path); + if(path[len-1] == ':') { + path[len-1] = '\0'; + } +} +// set the current working folder where file operation will occur. +// Note ---> The path in Get/Set working directory is in the *LOCAL* file system's syntax +// This pathname is relative *OR* fully qualified +bool ddio_SetWorkingDir(const char *path) +{ + bool failed = true; + + failed = chdir(path); + + if(!failed) { +// if(path[strlen(path)-1] != ':') +// strcat(path, ":"); + strcpy(SavedWorkingDir,path); + return true; + } else { + return false; + } +} +// check if two files are different +bool ddio_FileDiff(const char* fileNameA, const char* fileNameB) +{ + struct stat infoA; + struct stat infoB; + + bool filesAreEqual = false; + if (fileNameA && fileNameB) + { + if (!(strcmp(fileNameA, fileNameB))) + { + filesAreEqual = true; + } + else // test file info + { + stat(fileNameA, &infoA); + stat(fileNameB, &infoB); + + if ((infoA.st_mode == infoB.st_mode) && + (infoA.st_size == infoB.st_size) && + (infoA.st_mtime == infoB.st_mtime)) + { + filesAreEqual = true; + } + } + } + + return !filesAreEqual; +} +// get a file length of a FILE +int ddio_GetFileLength(FILE* filePtr) +{ + int size = -1; + if (filePtr) + { + int filedes = fileno(filePtr); + struct stat info; + + fstat(filedes, &info); + + size = info.st_size; + } + else + { + mprintf((0, "Tried getting length of NULL fileptr!\n")); + Debugger(); + } + + return size; +} +// Split a pathname into its component parts +void ddio_SplitPath(const char* srcPath, char* path, char* filename, char* ext) +{ + int pathStart = -1; + int pathEnd = -1; + int fileStart = -1; + int fileEnd = -1; + int extStart = -1; + int extEnd = -1; + int totalLen = strlen(srcPath); + // Check for an extension + /////////////////////////////////////// + int t = totalLen - 1; + while( (srcPath[t]!='.') && (srcPath[t]!=':') && (t>=0) ) t--; + //see if we are at an extension + if((t>=0)&&(srcPath[t]=='.')){ + //we have an extension + extStart = t; + extEnd = totalLen - 1; + if(ext) + { + strncpy(ext,&(srcPath[extStart]),extEnd - extStart + 1); + ext[extEnd - extStart + 1] = '\0'; + } + }else{ + //no extension + if(ext) + ext[0] = '\0'; + } + // Check for file name + //////////////////////////////////// + int temp = (extStart!=-1)?(extStart):(totalLen-1); + while( (srcPath[temp]!=':') && (temp>=0) ) temp--; + if(temp<0) + temp = 0; + if(srcPath[temp]==':'){ + //we have a file + fileStart = temp + 1; + if(extStart!=-1) + fileEnd = extStart - 1; + else + fileEnd = totalLen - 1; + if(filename) + { + strncpy(filename,&(srcPath[fileStart]),fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = '\0'; + } + pathStart = 0; + pathEnd = fileStart - 2; + //Copy the rest into the path name + if(path) + { + strncpy(path, &(srcPath[pathStart]),pathEnd - pathStart + 1); + path[pathEnd - pathStart + 1] = 0; + } + }else{ + //only file, no path + fileStart = 0; + if(extStart != -1) + fileEnd = extStart - 1; + else + fileEnd = totalLen - 1; + if(filename) + { + strncpy(filename, &(srcPath[fileStart]), fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = 0; + } + + // Only file no path + if(path) + { + path[0] = 0; + } + } +#if 0 + // Check for an extension + if (srcPath[totalLen - 4] == '.') + { + extStart = totalLen - 4; + extEnd = totalLen - 1; + strncpy(ext, &(srcPath[extStart]), extEnd - extStart + 1); + ext[extEnd - extStart + 1] = 0; + } + else + { + ext[0] = 0; + } + + // Check for file name + char* namePtr = strrchr(srcPath, ':'); + if (namePtr) + { + fileStart = namePtr - srcPath + 1; + if (extStart != -1) + { + fileEnd = extStart - 2; + } + else + { + fileEnd = totalLen - 1; + } + + strncpy(filename, &(srcPath[fileStart]), fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = 0; + + pathStart = 0; + pathEnd = fileStart - 2; + + // Copy the rest into the path name + strncpy(path, &(srcPath[pathStart]), pathEnd - pathStart + 1); + path[pathEnd - pathStart + 1] = 0; + } + else + { + fileStart = 0; + if (extStart != -1) + { + fileEnd = extStart - 2; + } + else + { + fileEnd = totalLen - 1; + } + + strncpy(filename, &(srcPath[fileStart]), fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = 0; + // Only file no path + path[0] = 0; + } +#endif +} +void ddio_CopyFileTime (char *dstFile,const char *srcFile) +{ + CInfoPBRec srcPB; + CInfoPBRec dstPB; + OSErr err = noErr; + Str255 srcMacFile = "\p"; + Str255 dstMacFile = "\p"; + Str255 volName = "\p"; + short volRefNum = 0; + long dirID = 0; + + // Get the info for the current default volume (where the working directory lives) + err = HGetVol(volName, &volRefNum, &dirID); + if (err) + { + mprintf((0, "Error getting default volume info in ddio_CopyFileTime: %d\n", err)); + Debugger(); + } + else + { + // Convert the c-string version of the path to a p-string + strcpy((char*) srcMacFile, srcFile); + c2pstr((char*) srcMacFile); + // Set up the source file's parameter block + memset(&srcPB, 0, sizeof(srcPB)); + srcPB.hFileInfo.ioNamePtr = srcMacFile; + srcPB.hFileInfo.ioFDirIndex = 0; + srcPB.hFileInfo.ioVRefNum = volRefNum; + srcPB.hFileInfo.ioDirID = dirID; + + // Get the source file's info + err = PBGetCatInfoSync(&srcPB); + if (err == noErr) + { + // Convert the c-string version of the path to a p-string + strcpy((char*) dstMacFile, dstFile); + c2pstr((char*) dstMacFile); + // Set up the target file's parameter block + memset(&dstPB, 0, sizeof(dstPB)); + dstPB.hFileInfo.ioNamePtr = dstMacFile; + dstPB.hFileInfo.ioVRefNum = volRefNum; + + dstPB.hFileInfo.ioFDirIndex = 0; + dstPB.hFileInfo.ioDirID = dirID; + + // Get the target file's info + err = PBGetCatInfoSync(&dstPB); + if (err == noErr) + { + // Transfer the src file's time stamp to the target + dstPB.hFileInfo.ioFlCrDat = srcPB.hFileInfo.ioFlCrDat; + dstPB.hFileInfo.ioFlMdDat = srcPB.hFileInfo.ioFlMdDat; + dstPB.hFileInfo.ioFlBkDat = srcPB.hFileInfo.ioFlBkDat; + + // Restore the directory id (which got bashed to the fileID by PBGetCatInfo + dstPB.hFileInfo.ioDirID = dirID; + + err = PBSetCatInfoSync(&dstPB); + if (err == noErr) + { + ; // success + } + else + { + mprintf((0, "Error setting target file's timestamp in ddio_CopyFileTime: %d\n", err)); + Debugger(); + } + } + else + { + mprintf((0, "Error getting target file's info in ddio_CopyFileTime: %d\n", err)); + Debugger(); + } + } + else + { + mprintf((0, "Error getting source file's time stamp in ddio_CopyFileTime: %d\n", err)); + Int3(); + } + } +} +// deletes a file. Returns 1 if successful, 0 on failure +int ddio_DeleteFile (char *name) +{ + bool failed = true; + + failed = remove(name); + + return (!failed); +} +// Save/Restore the current working directory +void ddio_SaveWorkingDir(void) +{ + ddio_GetWorkingDir(SavedWorkingDir,_MAX_PATH); +} +void ddio_RestoreWorkingDir(void) +{ + ddio_SetWorkingDir(SavedWorkingDir); +} +// Checks if a directory exists (returns 1 if it does, 0 if not) +// This pathname is *RELATIVE* not fully qualified +bool ddio_DirExists(const char* path) +{ + bool res; + + ddio_SaveWorkingDir(); + res = chdir(path); + if(res) { + char tempbuffer[_MAX_PATH]; + strcpy(tempbuffer, ":"); + strcat(tempbuffer, path); + res = chdir(tempbuffer); + } + ddio_RestoreWorkingDir(); + return (!res) ? true: false; +} +// Constructs a path in the local file system's syntax +// newPath: stores the constructed path +// absolutePathHeader: absolute path on which the sub directories will be appended +// (specified in local file system syntax) +// takes a variable number of subdirectories which will be concatenated on to the path +// the last argument in the list of sub dirs *MUST* be NULL to terminate the list +void ddio_MakePath(char* newPath, const char* absolutePathHeader, const char* subDir, ...) +{ + const char delimiter = ':'; + va_list args; + char* currentDir = NULL; + int pathLength = 0; + + assert(newPath); + if(absolutePathHeader == 0) + mprintf((2, "WOOPS\n")); + assert(absolutePathHeader); + assert(subDir); + + if (newPath != absolutePathHeader) + { + strcpy(newPath, absolutePathHeader); + } + // Add the first sub directory + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, subDir); + + // Add the additional subdirectories + va_start(args, subDir); + while ((currentDir = va_arg(args, char*)) != NULL) + { + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, currentDir); + } + va_end(args); +} +bool ddio_GetTempFileName(char *basedir,char *prefix,char *filename) +{ + char old_workdir[_MAX_PATH]; + bool success = false; + if(strlen(prefix)>64) + return false; + ddio_GetWorkingDir(old_workdir,_MAX_PATH); + if(!ddio_DirExists(basedir)) + { + return false; //invalid base directory + } + char randname[10]; + int index; + int tries = 0; + char rc; + bool created = false; + index = 0; + + while(!success && tries<20) + { + //generate a bunch of random characters + rc = (rand()%128); + if( (rc>='a' && rc<='z') || (rc>='A' && rc<='Z') || (rc>='0' && rc<='9') ) + { + //valid character + randname[index] = rc; + index++; + if(index==10) + { + //we hit the size of our max, see if we generated a unique filename + char t[_MAX_PATH]; + randname[9] = '\0'; + sprintf(t,"%s%s.tmp",prefix,randname); + //see if we can find this file + FILE *fd = mac_fopen(t,"rb"); + if(!fd) + { + //we found a good file! + ddio_MakePath(filename,basedir,t,NULL); + success = true; + created = true; + }else + { + //already taken + fclose(fd); + tries++; + index = 0; + } + } + }else + { + continue; //try again + } + } + ddio_SetWorkingDir(old_workdir); + return created; +} +// These functions allow one to find a file +// You use FindFileStart by giving it a wildcard (like *.*, *.txt, u??.*, whatever). It returns +// a filename in namebuf. +// Use FindNextFile to get the next file found with the wildcard given in FindFileStart. +// Use FindFileClose to end your search. +//These functions allow one to find a file +//You use FindFileStart by giving it a wildcard (like *.*, *.txt, u??.*, whatever). It returns +// a filename in namebuf. +// Use FindNextFile to get the next file found with the wildcard given in FindFileStart. +// Use FindFileClose to end your search. +int fileIndex = 1; +char wildsave[10]; +bool ddio_FindFileStart(const char *wildcard, char *namebuf) +{ + ASSERT(wildcard); + ASSERT(namebuf); + + fileIndex = 1; + char tempbuffer[_MAX_PATH]; + + if(strrchr(wildcard, ':')) { + strcpy(tempbuffer, wildcard); + + char * c = strrchr(tempbuffer, '*'); + strcpy(wildsave, c); + *c = '\0'; + + if(!ddio_DirExists(tempbuffer)) + return(false); + + } else { + strcpy(wildsave, wildcard); + } + + ddio_SaveWorkingDir(); + ddio_SetWorkingDir(tempbuffer); + if(ddio_FindNextFile(namebuf)) + return(true); + else + return(false); +} +bool ddio_FindNextFile(char *namebuf) +{ + short err; + FileParam fpb; + CInfoPBRec cpb; + Str255 volName = "\p"; + short volRefNum = 0; + long dirID = 0; + ASSERT(namebuf); +getAnother: + namebuf[0] = 0; + err = HGetVol(volName, &volRefNum, &dirID); + if (err) return false; +/* + fpb.ioNamePtr=(unsigned char *)namebuf; + fpb.ioFDirIndex=fileIndex; + fpb.ioVRefNum=0; + // retrun just file info + err = PBGetFInfoSync( (ParmBlkPtr)&fpb ); +*/ + // returns both file & dir info + cpb.hFileInfo.ioVRefNum = volRefNum; + cpb.hFileInfo.ioDirID = dirID; + cpb.hFileInfo.ioNamePtr = (unsigned char *)namebuf; + cpb.hFileInfo.ioFDirIndex = fileIndex; + + err = PBGetCatInfoSync(&cpb); + +// if((cpb.hFileInfo.io.FlAttrib & (1<<4))) + if(err == fnfErr) { + return(false); + } else { + p2cstr( (unsigned char *)namebuf ); + fileIndex++; + if( PSGlobMatch(wildsave, namebuf, 0, 0)) { + return (true); + } else { + goto getAnother; + } + } + return(false); +} +void ddio_FindFileClose() +{ + ddio_RestoreWorkingDir(); + fileIndex = 1; + wildsave[0] = NULL; +} +// pass in a pathname (could be from ddio_SplitPath), root_path will have the drive name. +void ddio_GetRootFromPath(const char *srcPath, char *root_path) +{ + assert(root_path); + assert(srcPath); + //the first char should be drive letter, second char should be a : +// if(!(srcPath[0]==':')){ + //everything is ok +// strncpy(root_path,srcPath,2); +// root_path[2] = '\0'; +// }else{ + //invalid path (no root) + root_path[0]='\0'; +// } +} +// retrieve root names, free up roots array (allocated with malloc) after use +int ddio_GetFileSysRoots(char **roots, int max_roots) +{ +#ifdef FIXED + char buffer[100]; + int ret = GetLogicalDriveStrings(100,buffer); + if(ret==0){ + //there was an error + return 0; + } + if( ret > 100 ){ + //we didn't have enough space + return 0; + } + int count = 0; + bool done = false; + char *strptr = buffer; + char *string; + int strsize; + while( (count0)&&(dest[path_length-1]==':')) + dest[path_length-1]='\0'; + //free up all the allocated memory and we're done + for(count=0;count +//#include +#include +#include "file_io.h" + +#define text_type 'TEXT' +#define binary_type 'BINA' +#define creator 'GSCe' + +FILE * mac_fopen(char *filename, const char * open_mode) +{ + __file_modes mode; + short permission; + OSErr ioResult; + short refNum; + OSType file_type; + FILE * file; + + FSSpec spec; + Str255 macName; + + strcpy((char*) macName, filename); + c2pstr((char*) macName); + + ioResult = FSMakeFSSpec(0, 0L, macName, &spec); + + if (!__get_file_modes(open_mode, &mode)) + return(NULL); + + permission = (mode.io_mode == __read) ? fsRdPerm : fsRdWrPerm; + + ioResult = FSpOpenDF(&spec, permission, &refNum); + + if (ioResult) + { + if (ioResult != fnfErr || mode.open_mode == __must_exist) + return(NULL); + + file_type = 'GSCf'; +// file_type = (mode.binary_io) ? binary_type : text_type; + + if (!(ioResult = FSpCreate(&spec, creator, file_type, smSystemScript))) + ioResult = FSpOpenDF(&spec, permission, &refNum); + + if (ioResult) + return(NULL); + } + else if (mode.open_mode == __create_or_truncate) + { + ioResult = SetEOF(refNum, 0); + + if (ioResult) + { + FSClose(refNum); + return(NULL); + } + } + + file = __handle_open(refNum, open_mode); + + if (!file) + FSClose(refNum); + + return(file); +} +FILE * FSp_fopen(ConstFSSpecPtr spec, const char * open_mode, OSType type) +{ + __file_modes mode; + short permission; + OSErr ioResult; + short refNum; + OSType file_type; + FILE * file; + + if (!__get_file_modes(open_mode, &mode)) + return(NULL); + + permission = (mode.io_mode == __read) ? fsRdPerm : fsRdWrPerm; + + ioResult = FSpOpenDF(spec, permission, &refNum); + + if (ioResult) + { + if (ioResult != fnfErr || mode.open_mode == __must_exist) + return(NULL); + + if (!(ioResult = FSpCreate(spec, creator, type, smSystemScript))) + ioResult = FSpOpenDF(spec, permission, &refNum); + + if (ioResult) + return(NULL); + } + else if (mode.open_mode == __create_or_truncate) + { + ioResult = SetEOF(refNum, 0); + + if (ioResult) + { + FSClose(refNum); + return(NULL); + } + } + + file = __handle_open(refNum, open_mode); + + if (!file) + FSClose(refNum); + + return(file); +} diff --git a/ddio_mac/macio.cpp b/ddio_mac/macio.cpp new file mode 100644 index 000000000..b3071b323 --- /dev/null +++ b/ddio_mac/macio.cpp @@ -0,0 +1,55 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/macio.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * macintosh implementation of device dependent io functions * + * + * $Log: macio.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 3 10/21/99 3:33p Jeff + * Macintosh merges + * + * 2 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 3 5/11/97 7:58 PM Jeremy + * added stubs for internal init/close functions + * + * 2 5/9/97 7:14 PM Jeremy + * initial checkin + * + * 1 3/13/97 6:53 PM Jeremy + * macintosh implementation of device dependent io functions + * + * $NoKeywords: $ + */ +// ---------------------------------------------------------------------------- +// Mac IO System Main Library Interface +// ---------------------------------------------------------------------------- +#include +#include "pserror.h" +#include "mono.h" +#include "gameos.h" +#include "ddio_mac.h" +#include "ddio.h" +// ---------------------------------------------------------------------------- +// Initialization and destruction functions +// ---------------------------------------------------------------------------- +bool +ddio_InternalInit(ddio_init_info *init_info) +{ + bool result = true; + + return result; +} +void +ddio_InternalClose() +{ + ; +} + diff --git a/ddio_mac/macjoy.cpp b/ddio_mac/macjoy.cpp new file mode 100644 index 000000000..d3d6c13fa --- /dev/null +++ b/ddio_mac/macjoy.cpp @@ -0,0 +1,312 @@ +/* + * joystick header + * + * $NoKeywords: $ + */ +#include "joystick.h" +#include "pserror.h" +#include "pstypes.h" +#include +#include +#include +typedef struct tJoystickRecord +{ + bool valid; // is this a valid device. + bool remote; // is this device on another computer + tJoystick joy; // joystick + union + { + int joyid; + //SOCKET joysock; + } + id; // information on accessing the device + tJoyInfo caps; // controller capabilities + tJoyPos pos; // current position +} +tJoystickRecord; +// --------------------------------------------------------------------------- +// globals +static tJoystickRecord Joysticks[MAX_JOYSTICKS]; +int Joy_initialized = 0; // is our joystick system initialized? +void joy_Close(); +bool joy_InitRemoteStick(tJoystick joy, char *server_adr, tJoystickRecord *stick); +void joy_GetState(tJoystick stick, tJoyPos *pos); +void joy_GetRemoteState(tJoystick stick, tJoyPos *pos); +int joyGetNumDevs(void); +// closes a stick +// closes connection with controller. +void joy_CloseStick(tJoystick joy); +// --------------------------------------------------------------------------- +// functions +// joystick system initialization +bool joy_Init(bool remote) +{ + int i; + + // reinitialize joystick if already initialized. + if (Joy_initialized) { + joy_Close(); + } + else { + atexit(joy_Close); + } + + // check if this OS supports joysticks + if (!joyGetNumDevs() && !remote) { + Joy_initialized = 0; + return false; + } + + // initialize joystick list + for (i = 0; i < MAX_JOYSTICKS; i++) + Joysticks[i].valid = false; + + Joy_initialized = 1; + + return true; +} +void joy_Close() +{ + int i; + for (i = 0; i < MAX_JOYSTICKS; i++) + if (Joysticks[i].valid) + joy_CloseStick((i == 0) ? JOYSTICK_1 : (i==1) ? JOYSTICK_2 : JOYSTICK_1); +} +// initializes a joystick +// if server_adr is valid, a link is opened to another machine with a controller. +bool joy_InitStick(tJoystick joy, char *server_adr) +{ + ASSERT(Joy_initialized); + // close down already open joystick. + if (Joysticks[(int)joy].valid) + joy_CloseStick(joy); + // okay, now if this is a remote joystick, open it + if (server_adr) { + return joy_InitRemoteStick(joy, server_adr, &Joysticks[(int)joy]); + } + else if (joyGetNumDevs() == 0) { + return false; + } + else { + //OPEN JOYSTICK HERE! + //Setup struct + return true; + } + + return false; +} +//this function takes a server address and tries to initialize a stick that's on a machine +//running the remote control server. +bool joy_InitRemoteStick(tJoystick joy, char *server_adr, tJoystickRecord *stick) +{ + /* + sockaddr_in servaddr; + int adr[4]; + + ASSERT(server_adr); + + Joysticks[(int)joy].id.joysock = socket(AF_INET, SOCK_STREAM, 0); + if (Joysticks[(int)joy].id.joysock == INVALID_SOCKET) + return false; + + sscanf(server_adr, "%d.%d.%d.%d", &adr[0], &adr[1], &adr[2], &adr[3]); + mprintf((0, "Connecting to remote control on %d.%d.%d.%d...\n", adr[0],adr[1],adr[2],adr[3])); + + servaddr.sin_family = AF_INET; + servaddr.sin_port = JOY_PORT; + servaddr.sin_addr.S_un.S_un_b.s_b1 = (ubyte)adr[0]; + servaddr.sin_addr.S_un.S_un_b.s_b2 = (ubyte)adr[1]; + servaddr.sin_addr.S_un.S_un_b.s_b3 = (ubyte)adr[2]; + servaddr.sin_addr.S_un.S_un_b.s_b4 = (ubyte)adr[3]; + + if (connect(Joysticks[(int)joy].id.joysock, (struct sockaddr *)&servaddr, sizeof(servaddr)) == SOCKET_ERROR) { + shutdown(Joysticks[(int)joy].id.joysock, 1); + closesocket(Joysticks[(int)joy].id.joysock); + Joysticks[(int)joy].valid = false; + return false; + } + else { + tJoyPacket packet; + LINGER linger; + int res; + + linger.l_onoff = 1; + linger.l_linger = 2; + setsockopt(Joysticks[(int)joy].id.joysock, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger)); + + Joysticks[(int)joy].valid = true; + Joysticks[(int)joy].remote = true; + Joysticks[(int)joy].joy = joy; + + // now wait to receive acknowledgement packet (joystick info will be stored here in a tJoyInfo format) "INFO" + res = recv(Joysticks[(int)joy].id.joysock, (char *)&packet, sizeof(tJoyInfo) + strlen(JOY_INFO),0); + + if ( res == SOCKET_ERROR || !res) { + shutdown(Joysticks[(int)joy].id.joysock, 1); + closesocket(Joysticks[(int)joy].id.joysock); + Joysticks[(int)joy].valid = false; + return false; + } + mprintf((0, "RECV %c%c%c%c \n", packet.coda[0], packet.coda[1], packet.coda[2], packet.coda[3])); + if (memcmp(packet.coda, JOY_INFO, strlen(JOY_INFO)) != 0) { + mprintf((0, "Received non information packet from control server.\n")); + shutdown(Joysticks[(int)joy].id.joysock, 1); + closesocket(Joysticks[(int)joy].id.joysock); + Joysticks[(int)joy].valid = false; + return false; + } + memcpy(&Joysticks[(int)joy].caps, packet.buf, sizeof(tJoyInfo)); + + // start polling. send "POLL" + memcpy(packet.coda, JOY_POLL, strlen(JOY_POLL)); + send(Joysticks[(int)joy].id.joysock, (char *)&packet, strlen(JOY_POLL), 0); + + // start packet read thread. + //@@ Thread_running[(int)joy] = false; + //@@ unsigned thread_id; + //@@ unsigned long thread_handle = _beginthreadex(NULL, 0, joy_ReadRemotePacket, &Joysticks[(int)joy], 1, &thread_id); + //@@ if (thread_handle == 0) { + //@@ Error("Remote joystick thread creation failed."); + //@@ } + //@@ + //@@ if (SetThreadPriority((HANDLE)thread_handle, THREAD_PRIORITY_NORMAL) == FALSE) + //@@ Error("Remote joystick thread prioritization failed."); + } + */ + return true; +} +// closes a stick +// closes connection with controller. +void joy_CloseStick(tJoystick joy) +{ + ASSERT(Joysticks[(int)joy].valid); + if (Joysticks[(int)joy].remote) { + //@@ u_long arg = 1; + //@@ ioctlsocket(Joysticks[(int)joy].id.joysock, FIONBIO, &arg); + //@@ Thread_running[(int)joy] = false; + //@@ Sleep(200); + //shutdown(Joysticks[(int)joy].id.joysock, 1); + //closesocket(Joysticks[(int)joy].id.joysock); + } + //CLOSE joystick here + Joysticks[(int)joy].valid = false; +} +// returns true if joystick valid +bool joy_IsValid(tJoystick joy) +{ + if (Joysticks[(int)joy].valid) return true; + else return false; +} +// retreive information about joystick. +void joy_GetJoyInfo(tJoystick joy, tJoyInfo *info) +{ + ASSERT(Joy_initialized); + if (Joysticks[(int)joy].valid) { + memcpy(info, &Joysticks[(int)joy].caps, sizeof(tJoyInfo)); + } + else + Int3(); // This should never happen. +} +// retreive uncalibrated position of joystick +void joy_GetRawPos(tJoystick joy, tJoyPos *pos) +{ + joy_GetPos(joy, pos); + pos->x = (pos->x+128)*JOYAXIS_RANGE; + pos->y = (pos->y+128)*JOYAXIS_RANGE; + pos->z = (pos->z+128)*JOYAXIS_RANGE; + pos->r = (pos->r+128)*JOYAXIS_RANGE; + pos->u = (pos->u+128)*JOYAXIS_RANGE; + pos->v = (pos->v+128)*JOYAXIS_RANGE; +} +// returns the state of a stick, remote or otherwise +void joy_GetPos(tJoystick stick, tJoyPos *pos) +{ +// ASSERT(Joy_initialized); + + pos->x = 0; + pos->y = 0; + pos->z = 0; + pos->r = 0; + pos->u = 0; + pos->v = 0; + pos->pov[0] = 0; + pos->pov[1] = 0; + pos->pov[2] = 0; + pos->pov[3] = 0; + pos->buttons = 0; + pos->btn = 0; + + if (!Joysticks[(int)stick].valid) + return; + + // retrieve joystick info from the net, or locally. + if (Joysticks[(int)stick].remote) { + joy_GetRemoteState(stick, pos); + } + else { + //READ JOYSTICK HERE!!!!!!!!!!! + } +} +// returns the position of a remote stick. +void joy_GetRemoteState(tJoystick stick, tJoyPos *pos) +{ + /* + ASSERT(Joysticks[(int)stick].remote); + + tJoyPacket packet; + // get next POSI packet + recv(Joysticks[(int)stick].id.joysock, (char *)packet.coda, strlen(JOY_POS),0); + if (memcmp(packet.coda, JOY_POS, strlen(JOY_POS)) == 0) { + recv(Joysticks[(int)stick].id.joysock, (char *)packet.buf, sizeof(tJoyPos),0); + memcpy(&Joysticks[(int)stick].pos, packet.buf, sizeof(tJoyPos)); + } + pos->x = (int)((Joysticks[(int)stick].pos.x<<8)/(Joysticks[(int)stick].caps.maxx - Joysticks[(int)stick].caps.minx)) - 128; + pos->y = (int)((Joysticks[(int)stick].pos.y<<8)/(Joysticks[(int)stick].caps.maxy - Joysticks[(int)stick].caps.miny)) - 128; + if (Joysticks[(int)stick].caps.axes_mask & JOYFLAG_ZVALID) + pos->z = (int)((Joysticks[(int)stick].pos.z<<8)/(Joysticks[(int)stick].caps.maxz - Joysticks[(int)stick].caps.minz)) - 128; + else + pos->z = 0; + if (Joysticks[(int)stick].caps.axes_mask & JOYFLAG_RVALID) + pos->r = (int)((Joysticks[(int)stick].pos.r<<8)/(Joysticks[(int)stick].caps.maxr - Joysticks[(int)stick].caps.minr)) - 128; + else + pos->r = 0; + if (Joysticks[(int)stick].caps.axes_mask & JOYFLAG_UVALID) + pos->u = (int)((Joysticks[(int)stick].pos.u<<8)/(Joysticks[(int)stick].caps.maxu - Joysticks[(int)stick].caps.minu)) - 128; + else + pos->u = 0; + if (Joysticks[(int)stick].caps.axes_mask & JOYFLAG_VVALID) + pos->v = (int)((Joysticks[(int)stick].pos.v<<8)/(Joysticks[(int)stick].caps.maxv - Joysticks[(int)stick].caps.minv)) - 128; + else + pos->v = 0; + if (Joysticks[(int)stick].caps.axes_mask & JOYFLAG_POVVALID) { + pos->pov = (Joysticks[(int)stick].pos.pov == JOY_POVBACKWARD) ? JOYPOV_DOWN : + (Joysticks[(int)stick].pos.pov == JOY_POVFORWARD) ? JOYPOV_UP : + (Joysticks[(int)stick].pos.pov == JOY_POVLEFT) ? JOYPOV_LEFT : + (Joysticks[(int)stick].pos.pov == JOY_POVRIGHT) ? JOYPOV_RIGHT : + JOYPOV_CENTER; + } + else + pos->pov = JOYPOV_CENTER; + pos->buttons = (unsigned)Joysticks[(int)stick].pos.buttons; + pos->btn = (unsigned)Joysticks[(int)stick].pos.btn; + + // request poll for next packet POLL + memcpy(packet.coda, JOY_POLL, strlen(JOY_POLL)); + send(Joysticks[(int)stick].id.joysock, (char *)&packet, strlen(JOY_POLL), 0); + */ +} +void ddio_InternalJoySuspend(void) +{ +} +void ddio_InternalJoyResume(void) +{ +} +int joyGetNumDevs(void) +{ + return 0; +} +void ddio_InternalJoyFrame(void) +{ +} + + diff --git a/ddio_mac/mackey.cpp b/ddio_mac/mackey.cpp new file mode 100644 index 000000000..f2d24b4b9 --- /dev/null +++ b/ddio_mac/mackey.cpp @@ -0,0 +1,308 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/mackey.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * Keyboard IO for macintosh + * + * $Log: mackey.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 4 3/20/00 12:43p Matt + * Merge of Duane's post-1.3 changes. + * + * 3 10/21/99 3:33p Jeff + * Macintosh merges + * + * 2 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 8 5/21/97 3:40 PM Jeremy + * fixed a bug where the mac/pc keycodes would get sign extended when + * converted to an int because they were declared as char instead of + * unsigned char + * + * 7 5/20/97 11:37 PM Jeremy + * fixed the no-keyup reporting bug in the keyboard handler + * + * 6 5/20/97 4:59 PM Jeremy + * added some calls to set and reset the event mask to handle key up + * events + * + * 5 5/19/97 9:42 AM Jeremy + * in keyboard handler changed call to getnextevent to getosevent which + * does not yield time to other processses, should make this routine more + * interrupt safe if it is called via interrupt instead of polled by + * osObject::defer + * + * 4 5/15/97 2:31 PM Jeremy + * made macKeyboardHandler return true if more keys are waiting. This way + * it can be repeatedly called to get all pending keys or just called once + * to get one key. Also set the application event mask to allow key up + * events (for timing reasons) + * + * 3 5/11/97 7:58 PM Jeremy + * added stubs for internal init/close functions + * + * 2 5/9/97 7:06 PM Jeremy + * implemented mac keyboard handler + * + * 1 3/13/97 6:53 PM Jeremy + * macintosh implementation of device dependent io functions + * + * $NoKeywords: $ + */ +// ---------------------------------------------------------------------------- +// Keyboard Interface +// ---------------------------------------------------------------------------- +// ANSI Headers +#include +// Macintosh Headers +#include +#include +#include +#include +// Descent 3 Headers +#include "pserror.h" +#include "mono.h" +#include "ddio.h" +#include "ddio_common.h" +#include "ddio_mac.h" +// ---------------------------------------------------------------------------- +// File-Level global data +// ---------------------------------------------------------------------------- +// Translates from macintosh to pc scancodes, value at table[macCode] = pcCode; +// mac keycodes are in commments: /* 0xHH */ +// 0xFF means this keycode is unused on the macintosh and does not translate +static unsigned char mac_to_pc_scancode_table[128] = +{ + 0x1E /* 0x00 */, 0x1F /* 0x01 */, 0x20 /* 0x02 */, 0x21 /* 0x03 */, + 0x23 /* 0x04 */, 0x22 /* 0x05 */, 0x2C /* 0x06 */, 0x2D /* 0x07 */, + 0x2E /* 0x08 */, 0x2F /* 0x09 */, 0xFF /* 0x0A */, 0x30 /* 0x0B */, + 0x10 /* 0x0C */, 0x11 /* 0x0D */, 0x12 /* 0x0E */, 0x13 /* 0x0F */, + 0x15 /* 0x10 */, 0x14 /* 0x11 */, 0x02 /* 0x12 */, 0x03 /* 0x13 */, + 0x04 /* 0x14 */, 0x05 /* 0x15 */, 0x07 /* 0x16 */, 0x06 /* 0x17 */, + 0x0D /* 0x18 */, 0x0A /* 0x19 */, 0x08 /* 0x1A */, 0x0C /* 0x1B */, + 0x09 /* 0x1C */, 0x0B /* 0x1D */, 0x1B /* 0x1E */, 0x18 /* 0x1F */, + 0x16 /* 0x20 */, 0x1A /* 0x21 */, 0x17 /* 0x22 */, 0x19 /* 0x23 */, + 0x1C /* 0x24 */, 0x26 /* 0x25 */, 0x24 /* 0x26 */, 0x28 /* 0x27 */, + 0x25 /* 0x28 */, 0x27 /* 0x29 */, 0x2B /* 0x2A */, 0x33 /* 0x2B */, + 0x35 /* 0x2C */, 0x31 /* 0x2D */, 0x32 /* 0x2E */, 0x34 /* 0x2F */, + 0x0F /* 0x30 */, 0x39 /* 0x31 */, 0x29 /* 0x32 */, 0x0E /* 0x33 */, + 0xFF /* 0x34 */, 0x01 /* 0x35 */, 0xFF /* 0x36 */, 0xE0 /* 0x37 */, + 0x2A /* 0x38 */, 0x3A /* 0x39 */, 0x38 /* 0x3A */, 0x1D /* 0x3B */, + 0x36 /* 0x3C */, 0xB8 /* 0x3D */, 0x9D /* 0x3E */, 0xFF /* 0x3F */, + 0xFF /* 0x40 */, 0x53 /* 0x41 */, 0xFF /* 0x42 */, 0x37 /* 0x43 */, + 0xFF /* 0x44 */, 0x4E /* 0x45 */, 0xFF /* 0x46 */, 0x45 /* 0x47 */, + 0xFF /* 0x48 */, 0xFF /* 0x49 */, 0xFF /* 0x4A */, 0xB5 /* 0x4B */, + 0x9C /* 0x4C */, 0xFF /* 0x4D */, 0x4A /* 0x4E */, 0xFF /* 0x4F */, + 0xFF /* 0x50 */, 0xFF /* 0x51 */, 0x52 /* 0x52 */, 0x4F /* 0x53 */, + 0x50 /* 0x54 */, 0x51 /* 0x55 */, 0x4B /* 0x56 */, 0x4C /* 0x57 */, + 0x4D /* 0x58 */, 0x47 /* 0x59 */, 0xFF /* 0x5A */, 0x48 /* 0x5B */, + 0x49 /* 0x5C */, 0xFF /* 0x5D */, 0xFF /* 0x5E */, 0xFF /* 0x5F */, + 0x3F /* 0x60 */, 0x40 /* 0x61 */, 0x41 /* 0x62 */, 0x3D /* 0x63 */, + 0x42 /* 0x64 */, 0x43 /* 0x65 */, 0xFF /* 0x66 */, 0x57 /* 0x67 */, + 0xFF /* 0x68 */, 0xB7 /* 0x69 */, 0xFF /* 0x6A */, 0x46 /* 0x6B */, + 0xFF /* 0x6C */, 0x44 /* 0x6D */, 0xFF /* 0x6E */, 0x58 /* 0x6F */, + 0xFF /* 0x70 */, 0x61 /* 0x71 */, 0xD2 /* 0x72 */, 0xC7 /* 0x73 */, + 0xC9 /* 0x74 */, 0xD3 /* 0x75 */, 0x3E /* 0x76 */, 0xCF /* 0x77 */, + 0x3C /* 0x78 */, 0xD1 /* 0x79 */, 0x3B /* 0x7A */, 0xCB /* 0x7B */, + 0xCD /* 0x7C */, 0xD0 /* 0x7D */, 0xC8 /* 0x7E */, 0xFF /* 0x7F */ +}; +const unsigned char kUnusedScanCode = 0xFF; +struct MacKeys +{ + union{ + int up_ticks; + float up_time; + }; + union{ + int down_ticks; + float down_time; + }; +// bool status; +}MKeys[DDIO_MAX_KEYS]; +// ---------------------------------------------------------------------------- +// Local Prototypes +// ---------------------------------------------------------------------------- +// translates scan code to foreign equivs. +ubyte xlate_scancode(ubyte scan_code); + +// ---------------------------------------------------------------------------- +// Macintosh Keyboard Handler +// ---------------------------------------------------------------------------- +bool +MacKeyboardHandler(void) +{ + unsigned char macKeyCode = 255; + unsigned char pcScanCode = 255; + bool b_shift = false; + bool b_option = false; + bool b_control = false; + bool b_command = false; + + EventRecord event; + + // Allow us to get key up events + SetEventMask(everyEvent); + LMSetSysEvtMask(everyEvent); + + float now_time = timer_GetTime(); + // Get the event + while (::GetOSEvent(keyDownMask + keyUpMask + autoKeyMask, &event)) { +// if(::GetOSEvent(keyDownMask + keyUpMask + autoKeyMask, &event)) { + macKeyCode = (event.message & keyCodeMask) >> 8; + ASSERT(macKeyCode < 128); + pcScanCode = xlate_scancode(mac_to_pc_scancode_table[macKeyCode]); + if (pcScanCode != kUnusedScanCode) + { + if(event.what == keyUp) { + MKeys[pcScanCode].up_time = now_time; + } + else { + if(MKeys[pcScanCode].down_time == 0.0) { + MKeys[pcScanCode].down_time = now_time; + } + } + ddio_UpdateKeyState(pcScanCode, (event.what == keyDown) || (event.what == autoKey)); + } + } + + b_shift = (event.modifiers & shiftKey) >> 9; + ddio_UpdateKeyState(KEY_LSHIFT, b_shift); + ddio_UpdateKeyState(KEY_RSHIFT, b_shift); + if(b_shift && MKeys[KEY_LSHIFT].down_time == 0.0) + MKeys[KEY_LSHIFT].down_time = now_time; + else if (MKeys[KEY_LSHIFT].down_time != 0.0) + MKeys[KEY_LSHIFT].up_time = now_time; + + + b_option = (event.modifiers & optionKey) >> 11; + ddio_UpdateKeyState(KEY_RALT, b_option); + ddio_UpdateKeyState(KEY_LALT, b_option); + if(b_option && MKeys[KEY_RALT].down_time == 0.0) + MKeys[KEY_RALT].down_time = now_time; + else if (MKeys[KEY_RALT].down_time != 0.0) + MKeys[KEY_RALT].up_time = now_time; + + b_control = (event.modifiers & controlKey) >> 12; + ddio_UpdateKeyState(KEY_LCTRL, b_control); + if(b_control && MKeys[KEY_LCTRL].down_time == 0.0) + MKeys[KEY_LCTRL].down_time = now_time; + else if (MKeys[KEY_LCTRL].down_time != 0.0) + MKeys[KEY_LCTRL].up_time = now_time; + + b_command = (event.modifiers & cmdKey) >> 8; + ddio_UpdateKeyState(KEY_CMD, b_command); + if(b_command && MKeys[KEY_CMD].down_time == 0.0) + MKeys[KEY_CMD].down_time = now_time; + else if (MKeys[KEY_CMD].down_time != 0.0) + MKeys[KEY_CMD].up_time = now_time; + + return false; +} +// ---------------------------------------------------------------------------- +// Keyboard Interface +// ---------------------------------------------------------------------------- +bool ddio_InternalKeyInit(ddio_init_info *init_info) +{ + bool result = true; + //¥ Make is so we can get key ups + SetEventMask(everyEvent); + LMSetSysEvtMask(everyEvent); + + SetEventMask(everyEvent); + LMSetSysEvtMask(everyEvent); + FlushEvents(everyEvent, 0); + return result; +} +void +ddio_InternalKeyClose() +{ + //¥ Restore the normal event mask + SetEventMask(everyEvent - keyUpMask); + LMSetSysEvtMask(everyEvent - keyUpMask); +} +bool ddio_InternalKeyState(ubyte key) +{ + return false; +} +void ddio_InternalKeySuspend() +{ + _INTERRUPT_DISABLE(); + FlushEvents(everyEvent, 0); +} +void ddio_InternalKeyResume() +{ + FlushEvents(everyEvent, 0); + _INTERRUPT_ENABLE(); +} +float ddio_InternalKeyDownTime(ubyte key) +{ + float down_time = 0.0f; + if(DDIO_key_state[key]) { + float cur_time = timer_GetTime(); + if(MKeys[key].down_time != 0.0) { + down_time = cur_time - MKeys[key].down_time; + } + } else { + if(MKeys[key].up_time != 0.0 && MKeys[key].down_time != 0.0) { + down_time = MKeys[key].up_time - MKeys[key].down_time; + MKeys[key].down_time = MKeys[key].up_time = 0.0f; + } + } + return down_time; +} +void ddio_InternalResetKey(ubyte key) +{ + MKeys[key].down_time = MKeys[key].up_time = 0.0f; +} +bool ddio_KeyFrame() +{ + return true; +} +void ddio_InternalKeyFrame(void) +{ +} + +static int DDIO_key_language = KBLANG_AMERICAN; + +void ddio_SetKeyboardLanguage(int language) +{ + DDIO_key_language = language; +} + + +// translates scan code to foreign equivs. +ubyte xlate_scancode(ubyte scan_code) +{ + ubyte code = scan_code; + + if (DDIO_key_language == KBLANG_FRENCH) { + switch (scan_code) { + case KEY_A: code = KEY_Q; break; + case KEY_M: code = KEY_COMMA; break; + case KEY_Q: code = KEY_A; break; + case KEY_W: code = KEY_Z; break; + case KEY_Z: code = KEY_W; break; + case KEY_SEMICOL: code = KEY_M; break; + case KEY_COMMA: code = KEY_SEMICOL; break; + } + } + else if (DDIO_key_language == KBLANG_GERMAN) { + switch (scan_code) { + case KEY_Y: code = KEY_Z; break; + case KEY_Z: code = KEY_Y; break; + } + } + else if (DDIO_key_language == KBLANG_BRITISH) { + if ( scan_code == KEY_BSLASH_UK ) { // KEY_SLASH_UK == 0x56 + code = KEY_SLASH; // KEY_SLASH is really the backslash, 0x2B + } + } + + return code; +} diff --git a/ddio_mac/macmouse.cpp b/ddio_mac/macmouse.cpp new file mode 100644 index 000000000..3d1287328 --- /dev/null +++ b/ddio_mac/macmouse.cpp @@ -0,0 +1,268 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/macmouse.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * Non-Game control mouse support (ie for menus, etc) + * + * $Log: macmouse.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 3 10/21/99 3:33p Jeff + * Macintosh merges + * + * 2 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 3 5/15/97 1:46 AM Jeremy + * fixed a bug in the mouse error handler for error reporting (was not + * setting member variables to input parameters) + * + * 2 5/13/97 11:13 AM Jeremy + * simple mouse implementation (one button-two axis). this is only for + * non-game control (menus,etc.) + * + * 1 5/12/97 11:54 AM Jeremy + * initial checkin + * + * + * $NoKeywords: $ + */ +// ANSI Headers +#include +// Macintosh Headers +#include +#include +// Descent 3 Headers +#include "pserror.h" +#include "mono.h" +#include "ddio.h" +// ---------------------------------------------------------------------------- +// File Level Constants +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// File Level Data Structures +// ---------------------------------------------------------------------------- +class CMouseData +{ + public: + CMouseData(void); + ~CMouseData(void); + + public: + bool mInitted; + + int mXAxis; + int mYAxis; + + int mMaxX; + int mMinX; + int mMaxY; + int mMinY; +}; +CMouseData::CMouseData(void) +{ + mInitted = false; + mXAxis = NULL; + mYAxis = NULL; + + mMaxX = 0x7FFFFFFF; + mMinX = 0xFFFFFFFF; + mMaxY = 0x7FFFFFFF; + mMinY = 0xFFFFFFFF; +} +CMouseData::~CMouseData(void) +{ + ; +} +class CMouseErr +{ + public: + CMouseErr(char* inErrStr = NULL, OSErr inMacErr = noErr) + { + mErrStr = inErrStr; + mMacErr = inMacErr; + } + + char* mErrStr; + OSErr mMacErr; +}; +// ---------------------------------------------------------------------------- +// File Level Global +// ---------------------------------------------------------------------------- +static CMouseData gMouse; +// ---------------------------------------------------------------------------- +// Functions +// ---------------------------------------------------------------------------- +void ddio_MouseClose(void); +// initializes mouse. specify maximum number of buttons to support. +bool ddio_MouseInit(void) +{ + return true; +} +void ddio_GetMouseCaps(int *buttons, int *axes) +{ + *buttons = 1; + *axes = 2; +} +void ddio_MouseClose() +{ +} +// set bounds for return values of absolute coordinates. +void ddio_MouseSetLimits(int left, int top, int right, int bottom, int zmin, int zmax) +{ + gMouse.mMaxX = right; + gMouse.mMinX = left; + + gMouse.mMaxY = bottom; + gMouse.mMinY = top; +} +void ddio_MouseGetLimits(int *left, int *top, int *right, int *bottom, int *zmin, int *zmax) +{ + *right = gMouse.mMaxX; + *left = gMouse.mMinX; + + *bottom = gMouse.mMaxY; + *top = gMouse.mMinY; + *zmin = 0; + *zmax = 0; +} +// resets position of mouse to 0,0. +void ddio_MouseReset() +{ + ddio_MouseSetLimits(0, 0 ,640, 480); +} +// returns absolute position of mouse, button state and mouse deltas. +int ddio_MouseGetState(int *x, int *y, int *dx, int *dy, int *z, int *dz) +{ + int buttonState = 0; + + int mx, my; + Point mouseLoc = {0,0}; + ::GetMouse(&mouseLoc); + ::LocalToGlobal(&mouseLoc); + mx = mouseLoc.h; + my = mouseLoc.v; +// fprintf(stderr, "%06d %06d\n", mx, my); + + *x = mx; + *y = my; + +/* + static int lastX = 0; + static int lastY = 0; + *x = (mouseLoc.h > gMouse.mMaxX) ? gMouse.mMaxX : mouseLoc.h; + *x = (mouseLoc.h < gMouse.mMinX) ? gMouse.mMinX : *x; + + *dx = *x - lastX; + lastX = *x; + + *y = (mouseLoc.v > gMouse.mMaxY) ? gMouse.mMaxY : mouseLoc.v; + *y = (mouseLoc.v < gMouse.mMinY) ? gMouse.mMinY : *y; + *dy = *y - lastY; + lastY = *y; + + if (z) + { + *z = 0; + } + if (dz) + { + *dz = 0; + } +*/ + if (Button()) + { + buttonState = MOUSE_LB; + } + + return buttonState; +} +// displays the mouse pointer. Each Hide call = Show call. +static int Mouse_counter=0; +void ddio_MouseShow() +{ + Mouse_counter++; + //if (Mouse_counter == 1) ShowCursor(TRUE); +} +void ddio_MouseHide() +{ + Mouse_counter--; + //if (Mouse_counter == 0) ShowCursor(FALSE); +} +void ddio_InternalMouseSuspend(void) +{ +} +void ddio_InternalMouseResume(void) +{ +} +void ddio_InternalMouseFrame(void) +{ +} +// resets mouse queue and button info only. +void ddio_MouseQueueFlush() +{ +} +// returns string to binding. +const char *ddio_MouseGetBtnText(int btn) +{ + return "MSE"; +} +const char *ddio_MouseGetAxisText(int axis) +{ + return "MSE"; +} +void ddio_MouseMode(int mode) +{ +} +// virtual coordinate system for mouse (match to video resolution set for optimal mouse usage. +void ddio_MouseSetVCoords(int width, int height) +{ +} +// gets a mouse button event, returns false if none. +bool ddio_MouseGetEvent(int *btn, bool *state) +{ + EventRecord event; + +// GetNextEvent(everyEvent - diskMask, &event); +// GetOSEvent(mDownMask + mUpMask, &event); + GetNextEvent(mDownMask + mUpMask, &event); +// GetNextEvent(everyEvent, &event); + +// SIOUXHandleOneEvent(&event); + + if ( event.what == mouseDown) { +// mprintf((2, "mouse event down\n")); + *btn = 1; + *state = true; + return true; + } else if ( event.what == mouseUp) { +// mprintf((2, "mouse event up\n")); + *btn = 1; + *state = false; + return true; + } else { +// mprintf((1, "mouse NC\n")); + *btn = 1; + *state = false; + return false; + } +} +/////////////////////////////////////////////////////////////////////////////// +char Ctltext_MseBtnBindings[N_MSEBTNS][32] = { + "mse-l\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-r\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-c\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-b4\0\0\0\0\0\0\0\0\0\0\0", + "msew-u\0\0\0\0\0\0\0\0\0\0\0", + "msew-d\0\0\0\0\0\0\0\0\0\0\0", + "","" +}; +char Ctltext_MseAxisBindings[][32] = { + "mse-X\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-Y\0\0\0\0\0\0\0\0\0\0\0\0", + "msewheel\0\0\0\0\0\0\0\0\0\0" +}; diff --git a/ddio_mac/mactimer.cpp b/ddio_mac/mactimer.cpp new file mode 100644 index 000000000..9f21a4b1a --- /dev/null +++ b/ddio_mac/mactimer.cpp @@ -0,0 +1,210 @@ +/* + * $Logfile: /DescentIII/Main/ddio_mac/mactimer.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:56:55 $ + * $Author: kevinb $ + * + * Macintosh timer + * + * $Log: mactimer.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:56:55 kevinb + * initial 1.5 import + * + * + * 5 3/20/00 12:43p Matt + * Merge of Duane's post-1.3 changes. + * + * 4 10/21/99 3:33p Jeff + * Macintosh merges + * + * 3 7/28/99 3:31p Kevin + * Mac Stuff! + * + * 2 5/10/99 10:28p Ardussi + * changes to compile on Mac + * + * 3 5/15/97 1:43 AM Jeremy + * changed mprintf's to be standard (with newline at end) + * + * 2 5/7/97 11:26 AM Jeremy + * completed implementation of timer based on microseconds (still need to + * implement hook functions) + * + * $NoKeywords: $ + */ +// ANSI Headers +#include +// Macintosh Headers +#include +#include +#include +#include +#include +// Descent 3 Headers +#include "ddio_mac.h" +#include "ddio.h" +#include "pserror.h" +#include "mono.h" +#include "maccontroller.h" +//=========================================== +// Local Data Types +//=========================================== +class CTimerInfo +{ + public: + CTimerInfo(void); + + bool mInitted; + float mElapsedTime; + float mStartTime; +}; +timer_info ktimer_info; +UniversalProcPtr timer_proc = NULL; +//=========================================== +// File Level Globals +//=========================================== +static CTimerInfo gTimerInfo; +static unsigned char Installed=0; +//=========================================== +// Helpful Inline Functions +//=========================================== +//=========================================== +// Functions +//=========================================== +CTimerInfo::CTimerInfo(void) +{ + mInitted = false; + mElapsedTime = 0.0; + mStartTime = 0.0; +} +bool timer_Init(int preemptive, bool /*force_lores*/) +{ + mprintf((0, "Initializing Timer.\n")); +// long double currentTimeLD = 0.0; +// UInt64 currentTimeUI = 0; + + // Get the current time in microseconds +// Microseconds((UnsignedWide*) ¤tTimeUI); + // Convert to a long double +// currentTimeLD = UInt64ToLongDouble(currentTimeUI); + + float currentTime; + UnsignedWide startmicro; + AbsoluteTime startAbs = UpTime(); + ISpTimeToMicroseconds(&startAbs, &startmicro); +// currentTimeLD = UInt64ToLongDouble(* (UInt64 *) &startmicro); + currentTime = (float)(* (UInt64 *) &startmicro); + + // Convert from microseconds to seconds + gTimerInfo.mStartTime = currentTime / 1000000; + gTimerInfo.mElapsedTime = 0; + + // Timer Initted + gTimerInfo.mInitted = true; + + return (gTimerInfo.mInitted); +} +void timer_Close() +{ + mprintf((0, "Closing Timer.\n")); + gTimerInfo.mInitted = false; + gTimerInfo.mElapsedTime = 0.0; +} +// returns time in seconds +float timer_GetTime() +{ + if (gTimerInfo.mInitted) + { +// long double currentTimeLD = 0.0; +// UInt64 currentTimeUI = 0; + + // Get the current time in microseconds +// Microseconds((UnsignedWide*)(¤tTimeUI)); + // Convert to a long double +// currentTimeLD = UInt64ToLongDouble(currentTimeUI); + + float currentTime; + UnsignedWide startmicro; + AbsoluteTime startAbs = UpTime(); + ISpTimeToMicroseconds(&startAbs, &startmicro); +// currentTimeLD = UInt64ToLongDouble(* (UInt64 *) &startmicro); + currentTime = (float)(* (UInt64 *) &startmicro); + + // Convert from microseconds to seconds + gTimerInfo.mElapsedTime = (currentTime / 1000000); + gTimerInfo.mElapsedTime -= gTimerInfo.mStartTime; + } + + return gTimerInfo.mElapsedTime; +} +longlong timer_GetMSTime() +{ + UInt64 currentTimeUI = 0; + + // Get the current time in microseconds +// Microseconds((UnsignedWide*)(¤tTimeUI)); +// return(currentTimeUI>>10); + + UnsignedWide startmicro; + AbsoluteTime startAbs = UpTime(); + ISpTimeToMicroseconds(&startAbs, &startmicro); + return((* (UInt64 *) &startmicro)>>10); + +} +/* +// hook in timer function at certain period. returns a handle to this function +int timer_HookFunction(void (*fncptr)(), int period) +{ +} +// clears function from hook list specified by a handle returned from HookFunction +void timer_ReleaseFunction(int func) +{ +} +*/ +// Interrupt time routines +void interrupt_Handler(timer_info *tminfo) +{ + MacKeyboardHandler(); + MacInSprocketHandler(); + + PrimeTime((QElemPtr)(&ktimer_info), INTERRUPT_MS); +} +void interrupt_Close() +{ + if (!Installed) + return; + _INTERRUPT_DISABLE(); + Installed = 0; +} +void interrupt_Flush() +{ + if (!Installed) + interrupt_Init(); + + _INTERRUPT_DISABLE(); + FlushEvents(everyEvent, 0); + _INTERRUPT_ENABLE(); +} +void interrupt_Init() +{ +#ifdef DAJ_DEBUG + return; +#endif + if (Installed) + return; + Installed = 1; + + SetEventMask(0xffff); + LMSetSysEvtMask(0xffff); + +// now set up the structure for the keyboard timer + if (timer_proc == NULL) { + timer_proc = NewTimerProc(interrupt_Handler); + } + + _INTERRUPT_ENABLE(); + + // Clear the keyboard array + interrupt_Flush(); + atexit(interrupt_Close); +} diff --git a/ddio_win/CMakeLists.txt b/ddio_win/CMakeLists.txt new file mode 100644 index 000000000..491621596 --- /dev/null +++ b/ddio_win/CMakeLists.txt @@ -0,0 +1,12 @@ +SET (HEADERS ddio_win.h ) +SET (CPPS + serial.cpp + winfile.cpp + winforcefeedback.cpp + winio.cpp + winjoy.cpp + winkey.cpp + winmouse.cpp + wintimer.cpp) + +ADD_LIBRARY(ddio_win STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/ddio_win/ddio_win.h b/ddio_win/ddio_win.h new file mode 100644 index 000000000..d0f20c907 --- /dev/null +++ b/ddio_win/ddio_win.h @@ -0,0 +1,94 @@ +/* + * $Logfile: /DescentIII/Main/ddio_win/ddio_win.h $ + * $Revision: 11 $ + * $Date: 4/09/99 12:02p $ + * $Author: Samir $ + * + * + * + * $Log: /DescentIII/Main/ddio_win/ddio_win.h $ + * + * 11 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 10 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 9 10/15/98 6:48p Samir + * added timer hooks. + * + * 8 6/29/98 6:43p Samir + * Took out GetMsgProc and legacy keyboard variables. + * + * 7 5/08/98 12:54p Samir + * newer unused mouse handler. + * + * 6 3/31/98 12:46p Samir + * keyboard IO system better. uses getmsgproc windows hook. + * + * 5 12/10/97 1:12p Samir + * Use timestamp from DirectInput Key calls. + * + * 4 10/16/97 2:29p Samir + * Changed DirectInput Keyboard to FOREGROUND + * + * 3 8/13/97 3:02p Samir + * A 'cleaner' way to do DirectInput mouse stuff. + * + * 2 8/01/97 7:30p Samir + * Better NT keyboard support. + * + * 7 5/08/97 1:57p Samir + * Moved keys info structure to ddio_common.h + * + * 6 4/16/97 11:32a Samir + * Added z axis support. + * + * 5 1/23/97 2:24p Samir + * Took out ddio_KeyHandler prototype. + * + * $NoKeywords: $ + */ + + +#ifndef DDIO_WIN_H +#define DDIO_WIN_H + +#include "pstypes.h" +#include "win/DirectX/dinput.h" + +class oeWin32Application; + +typedef struct dinput_data { + oeWin32Application *app; + HWND hwnd; + LPDIRECTINPUT lpdi; + bool preemptive; +} dinput_data; + +extern dinput_data DInputData; +extern bool DDIO_init; +extern bool DDIO_preemptive; + + +bool ddio_JoyHandler(); +float ddio_TickToSeconds(unsigned long ticks); + +#ifdef _DEBUG +void ddio_DebugMessage(unsigned err, char *fmt, ...); +#define DDIO_MESSAGE(_m) ddio_DebugMessage _m +#else +#define DDIO_MESSAGE(_m) +#endif + +#define MAKE_DDIO_TIME(_ms) ddio_TickToSeconds(_ms) + + +// hook in timer function at certain period. returns a handle to this function +DWORD timer_HookFunction(void (CALLBACK *fncptr)(UINT, UINT, DWORD, DWORD, DWORD), UINT period); + +// clears function from hook list specified by a handle returned from HookFunction +void timer_ReleaseFunction(DWORD func); + + +#endif \ No newline at end of file diff --git a/ddio_win/serial.cpp b/ddio_win/serial.cpp new file mode 100644 index 000000000..04579d742 --- /dev/null +++ b/ddio_win/serial.cpp @@ -0,0 +1,178 @@ +/* + * $Source: $ + * $Revision: 2 $ + * $Author: Jeff $ + * $Date: 5/11/99 11:28a $ + * + * COM port interface + * + * $Log: /DescentIII/Main/ddio_win/serial.cpp $ + * + * 2 5/11/99 11:28a Jeff + * added serial support (SAMIR) + * + * 1 5/10/99 9:27p Jeff + * + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include "ddio_win.h" +#include "mem.h" +#include "pserror.h" +#include "ddio.h" + +#include + +#define ASCII_XON 0x11 +#define ASCII_XOFF 0x13 + + +#define N_SERIAL_PORTS 4 + +// internal struct +typedef struct tWin32SerialPort +{ + sbyte port; + sbyte connect; + HANDLE hFile; + DCB dcb; +}tWin32SerialPort; + + +// takes port number 1-4, returns a port object +tSerialPort ddio_SerialOpenPort(int port_number, int baud) +{ + COMMTIMEOUTS ctimeouts; + char filename[16]; + tWin32SerialPort port_obj; + + ASSERT(port_number >= 1 && port_number <= N_SERIAL_PORTS); + + if (port_number < 1 || port_number > N_SERIAL_PORTS) { + return NULL; + } + + sprintf(filename, "COM%d", port_number); + + port_obj.hFile = CreateFile(filename, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (port_obj.hFile == INVALID_HANDLE_VALUE) { + return NULL; + } + port_obj.port = (sbyte)port_number; + +// Modem COMport is open. + SetCommMask(port_obj.hFile, 0); + SetupComm(port_obj.hFile, 1024, 1024); + PurgeComm(port_obj.hFile, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + +// Timeout after 10 sec. + ctimeouts.ReadIntervalTimeout = 0xffffffff; + ctimeouts.ReadTotalTimeoutMultiplier = 0; + ctimeouts.ReadTotalTimeoutConstant = 10000; + ctimeouts.WriteTotalTimeoutMultiplier = 0; + ctimeouts.WriteTotalTimeoutConstant = 10000; + SetCommTimeouts(port_obj.hFile, &ctimeouts); + +// Setup parameters for Connection +// 38400 8N1 + port_obj.dcb.DCBlength = sizeof(DCB); + GetCommState(port_obj.hFile, &port_obj.dcb); + + port_obj.dcb.BaudRate = baud; + + mprintf((0, "COM%d (%d) is opened.\n", port_obj.port, baud)); + + port_obj.dcb.fBinary = 1; + port_obj.dcb.Parity = NOPARITY; + port_obj.dcb.fNull = 0; + port_obj.dcb.XonChar = ASCII_XON; + port_obj.dcb.XoffChar = ASCII_XOFF; + port_obj.dcb.XonLim = 1024; + port_obj.dcb.XoffLim = 1024; + port_obj.dcb.EofChar = 0; + port_obj.dcb.EvtChar = 0; + port_obj.dcb.fDtrControl = DTR_CONTROL_ENABLE;// dtr=on + port_obj.dcb.fRtsControl = RTS_CONTROL_ENABLE; + + port_obj.dcb.ByteSize = 8; + port_obj.dcb.StopBits = ONESTOPBIT; + port_obj.dcb.fParity = FALSE; + + port_obj.dcb.fOutxDsrFlow = FALSE; + port_obj.dcb.fOutxCtsFlow = FALSE; // rts/cts off + +// obj->dcb.fInX = obj->dcb.fOutX = 1; // Software flow control XON/XOFF + + if (SetCommState(port_obj.hFile, &port_obj.dcb) == TRUE) + { + // Send DTR + EscapeCommFunction(port_obj.hFile, SETDTR); + port_obj.connect = 1; + } + else { + mprintf((1, "COMM: Unable to set CommState: (%x)\n", GetLastError())); + CloseHandle(port_obj.hFile); + port_obj.connect = 0; + + return NULL; + } + + + tWin32SerialPort *obj = (tWin32SerialPort *)mem_malloc(sizeof(tWin32SerialPort)); + if (obj) { + memcpy(obj, &port_obj, sizeof(port_obj)); + } + else { + CloseHandle(port_obj.hFile); + port_obj.connect = 0; + return NULL; + } + + return obj; +} + + +// takes port structure +void ddio_SerialClosePort(tSerialPort port) +{ + tWin32SerialPort *obj = (tWin32SerialPort *)port; + + if (!port) return; + + if (obj->connect) { + SetCommMask(obj->hFile, 0); + EscapeCommFunction(obj->hFile, CLRDTR); + PurgeComm( obj->hFile, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + CloseHandle(obj->hFile); + + obj->connect = 0; + } + + obj->hFile = NULL; + mprintf((0, "COM%d closed.\n", obj->port)); + + mem_free(obj); +} + + +bool ddio_SerialWriteByte(tSerialPort port, ubyte b) +{ + tWin32SerialPort *obj = (tWin32SerialPort *)port; + DWORD length; + + if (!obj->connect) return false; + + if (!WriteFile(obj->hFile, &b, 1, &length,NULL)) { + mprintf((0, "Failed to write byte to COM%d\n", obj->port)); + return false; + } + + return true; +} \ No newline at end of file diff --git a/ddio_win/winfile.cpp b/ddio_win/winfile.cpp new file mode 100644 index 000000000..3e6cbf9c3 --- /dev/null +++ b/ddio_win/winfile.cpp @@ -0,0 +1,892 @@ +/* + * $Logfile: /DescentIII/Main/ddio_win/winfile.cpp $ + * $Revision: 27 $ + * $Date: 10/22/99 1:23p $ + * $Author: Kevin $ + * + * File operations not covered properly in ANSI C + * + * $Log: /DescentIII/Main/ddio_win/winfile.cpp $ + * + * 27 10/22/99 1:23p Kevin + * Made ddio_GetCDDrive return null if the cd isn't found + * + * 26 7/12/99 6:44p Jeff + * added directory lock file API functions + * + * 25 6/24/99 2:04p Kevin + * Fixed bug with 2 CD rom drives & ddio_GetCDDrive + * + * 24 3/19/99 1:53p Kevin + * Fixed a bug with ddio_GetCDDrive + * + * 23 2/09/99 4:46p Jeff + * removed int3 in CopyFileTime + * + * 22 2/04/99 11:19a Kevin + * Added function to find a CD drive letter based on it's volume name + * + * 21 12/08/98 12:17p Jeff + * added 3 functions (similar to ddio_FindFiles) for finding directories + * in a path + * + * 20 11/19/98 5:40p Kevin + * Demo system + * + * 19 10/16/98 11:55a Kevin + * Made dlls loadable in a hog + * + * 18 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 17 8/19/98 2:18p Jeff + * changed ddio_CleanPath + * + * 16 8/16/98 2:04p Matt + * Fixed super-stupid bug + * + * 15 8/15/98 3:42p Matt + * Added new function, ddio_GetFullPath() + * + * 14 7/29/98 5:38p Jeff + * added ddio functions to get parent path, and to clean a path name into + * the real path + * + * 13 7/28/98 6:05p Jeff + * added functions to get root directories (aka drives in windows/dos) + * + * 12 6/01/98 10:34a Matt + * If can't copy file time, wait half a second and try again. + * + * 11 5/26/98 7:35p Jason + * took out filetime int 3 + * + * 10 4/06/98 1:24p Samir + * support NULL arguments to ddio_SpltPath. + * + * 9 3/19/98 3:18p Samir + * enforce constant char* arguments when needed. done in CFILE and bitmap + * libraries as well as ddio. + * + * 8 2/20/98 3:37p Jason + * fixed error message to print out before subsequent file operations + * happen + * + * 7 2/17/98 9:40p Jason + * print out error number for copy file time + * + * 6 2/16/98 11:08a Jason + * fixed copyfiletime bug + * + * 5 2/16/98 1:48a Matt + * Added some additional error checking + * + * 4 9/10/97 12:06p Samir + * FIxed FindFile functions. + * + * 3 8/15/97 6:31p Samir + * Added findfile functions. + * + * 2 7/17/97 3:00p Jason + * took out int3 on copy file time + * + * 10 6/11/97 2:39p Samir + * Fixed bools + * + * 9 5/06/97 4:27p Samir + * Added ddio_MakePath + * + * 8 5/06/97 12:35p Samir + * Impllemented DirectoryExists and Save/Restore Working paths. + * + * 7 4/25/97 6:16p Jason + * added ddio_Deletefile function + * + * 6 4/03/97 4:34p Jason + * added CopyFileTime to the cfile, ddio libs + * + * 5 3/14/97 6:52p Samir + * Fixed jeremy bugs. + * + * 4 3/14/97 6:53 PM Jeremy + * added implementation of ddio_GetFileLength, ddio_SplitPath + * + * 3 3/14/97 6:38p Samir + * Added ddio_FileDiff + * + * 2 3/13/97 11:09a Samir + * Moved file stuff from gameos to ddio library + * + * 1 3/13/97 11:01a Samir + * Moved from gameos library + * + * $NoKeywords: $ + */ + +#include "ddio.h" +#include "ddio_win.h" +#include "pserror.h" +#include "mem.h" +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include + + +// --------------------------------------------------------------------------- +// File operations + +// creates a directory or folder on disk +bool ddio_CreateDir(const char *path) +{ + return (CreateDirectory(path, NULL)) ? true : false; +} + + +// destroys a directory +bool ddio_RemoveDir(const char *path) +{ + return (RemoveDirectory(path)) ? true : false; +} + + +// retrieve the current working folder where file operation will occur. +void ddio_GetWorkingDir(char *path, int len) +{ + GetCurrentDirectory(len,path); +} + +bool ddio_SetWorkingDir(const char *path) +{ + return (SetCurrentDirectory(path)) ? true : false; +} + +bool ddio_FileDiff(const char *path1, const char *path2) +{ + struct _stat abuf,bbuf; + + if (_stat(path1,&abuf)) + Int3(); //error getting stat info + + if (_stat(path2,&bbuf)) + Int3(); //error getting stat info + + if ((abuf.st_size != bbuf.st_size) || (abuf.st_mtime != bbuf.st_mtime)) + return true; + + return false; +} + +// get a file's length +int ddio_GetFileLength(FILE* filePtr) +{ + return (filelength(fileno(filePtr))); +} + +// Split a pathname into its component parts +void ddio_SplitPath(const char* srcPath, char* path, char* filename, char* ext) +{ + char drivename[_MAX_DRIVE], dirname[_MAX_DIR]; + + _splitpath(srcPath, drivename, dirname, filename, ext); + + if (path) + sprintf(path, "%s%s", drivename, dirname); +} + +void ddio_CopyFileTime (char *destname,const char *srcname) +{ + HANDLE desthandle,srchandle; + FILETIME a,b,c; + bool first_time = 1; + +try_again:; + + desthandle=CreateFile(destname,GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + srchandle=CreateFile(srcname,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + + if (desthandle==INVALID_HANDLE_VALUE || srchandle==INVALID_HANDLE_VALUE) + { + mprintf ((0,"Couldn't copy file time for %s! Error=%d\n",destname,GetLastError())); + + if (desthandle!=INVALID_HANDLE_VALUE) + CloseHandle(desthandle); + + if (srchandle!=INVALID_HANDLE_VALUE) + CloseHandle(srchandle); + + if (first_time) { + first_time = 0; + Sleep(500); + goto try_again; + } + + //JEFF: This Int3 has become really annoying...all we do is blow by it anyway + //removed for sanity. + //@@Int3(); //Let Matt know if you hit this! + return; + } + + GetFileTime (srchandle,&a,&b,&c); + SetFileTime (desthandle,&a,&b,&c); + + CloseHandle (desthandle); + CloseHandle (srchandle); +} + +// deletes a file. Returns 1 if successful, 0 on failure +int ddio_DeleteFile (char *name) +{ + return (DeleteFile (name)); +} + + +// Save/Restore the current working directory + +static char SavedWorkingDir[_MAX_DIR]; + +void ddio_SaveWorkingDir(void) +{ + GetCurrentDirectory(_MAX_DIR, SavedWorkingDir); +} + + +void ddio_RestoreWorkingDir(void) +{ + SetCurrentDirectory(SavedWorkingDir); +} + + +// Checks if a directory exists (returns 1 if it does, 0 if not) +// This pathname is *RELATIVE* not fully qualified +bool ddio_DirExists(const char* path) +{ + BOOL res; + + ddio_SaveWorkingDir(); + res = SetCurrentDirectory(path); + ddio_RestoreWorkingDir(); + + return (res) ? true: false; +} + + +// Constructs a path in the local file system's syntax +// newPath: stores the constructed path +// absolutePathHeader: absolute path on which the sub directories will be appended +// (specified in local file system syntax) +// takes a variable number of subdirectories which will be concatenated on to the path +// the last argument in the list of sub dirs *MUST* be NULL to terminate the list +void ddio_MakePath(char* newPath, const char* absolutePathHeader, const char* subDir, ...) +{ + const char delimiter = '\\'; + va_list args; + char* currentDir = NULL; + int pathLength = 0; + + assert(newPath); + assert(absolutePathHeader); + assert(subDir); + + if (newPath != absolutePathHeader) + { + strcpy(newPath, absolutePathHeader); + } + + // Add the first sub directory + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, subDir); + + // Add the additional subdirectories + va_start(args, subDir); + while ((currentDir = va_arg(args, char*)) != NULL) + { + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, currentDir); + } + va_end(args); +} + +bool ddio_GetTempFileName(char *basedir,char *prefix,char *filename) +{ + + if(!GetTempFileName(basedir,prefix,0,filename)) + return false; + else + return true; +} + + +// These functions allow one to find a file +// You use FindFileStart by giving it a wildcard (like *.*, *.txt, u??.*, whatever). It returns +// a filename in namebuf. +// Use FindNextFile to get the next file found with the wildcard given in FindFileStart. +// Use FindFileClose to end your search. +static HANDLE hFindFile = INVALID_HANDLE_VALUE; +static WIN32_FIND_DATA FindFileData; + +bool ddio_FindFileStart(const char *wildcard, char *namebuf) +{ + if (hFindFile != INVALID_HANDLE_VALUE) + ddio_FindFileClose(); + + hFindFile = FindFirstFile(wildcard, &FindFileData); + if (hFindFile != INVALID_HANDLE_VALUE) { + strcpy(namebuf, FindFileData.cFileName); + return true; + } + else { + namebuf[0] = 0; + return false; + } +} + + +bool ddio_FindNextFile(char *namebuf) +{ + + if (!hFindFile) return false; + + if (FindNextFile(hFindFile, &FindFileData)) { + strcpy(namebuf, FindFileData.cFileName); + return true; + } + else { + namebuf[0] = 0; + return false; + } +} + + +void ddio_FindFileClose() +{ + if (hFindFile != INVALID_HANDLE_VALUE) FindClose(hFindFile); + hFindFile = INVALID_HANDLE_VALUE; +} + + +static HANDLE hFindDir = INVALID_HANDLE_VALUE; +static WIN32_FIND_DATA FindDirData; +bool ddio_FindDirStart(const char *wildcard, char *namebuf) +{ + if (hFindDir != INVALID_HANDLE_VALUE) + ddio_FindDirClose(); + + hFindDir = FindFirstFile(wildcard, &FindDirData); + if (hFindDir != INVALID_HANDLE_VALUE) { + + if(FindDirData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY){ + strcpy(namebuf, FindDirData.cFileName); + return true; + }else{ + return ddio_FindNextDir(namebuf); + } + } + else { + namebuf[0] = 0; + return false; + } +} + +bool ddio_FindNextDir(char *namebuf) +{ + + if (!hFindDir) return false; + + bool done = false; + bool found = false; + + while( !done ){ + + if (FindNextFile(hFindDir, &FindDirData)){ + if(FindDirData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY){ + strcpy(namebuf, FindDirData.cFileName); + done = true; + found = true; + } + }else{ + namebuf[0] = '\0'; + done = true; + } + } + + return found; +} + +void ddio_FindDirClose() +{ + if (hFindDir != INVALID_HANDLE_VALUE) FindClose(hFindDir); + hFindDir = INVALID_HANDLE_VALUE; +} + + +// pass in a pathname (could be from ddio_SplitPath), root_path will have the drive name. +void ddio_GetRootFromPath(const char *srcPath, char *root_path) +{ + assert(root_path); + assert(srcPath); + + //the first char should be drive letter, second char should be a : + if((isalpha(srcPath[0]))&&(srcPath[1]==':')){ + //everything is ok + strncpy(root_path,srcPath,2); + root_path[2] = '\0'; + }else{ + //invalid path (no root) + root_path[0]='\0'; + } +} + +// retrieve root names, free up roots array (allocated with malloc) after use +int ddio_GetFileSysRoots(char **roots, int max_roots) +{ + char buffer[100]; + + int ret = GetLogicalDriveStrings(100,buffer); + if(ret==0){ + //there was an error + return 0; + } + + if( ret > 100 ){ + //we didn't have enough space + return 0; + } + + int count = 0; + bool done = false; + char *strptr = buffer; + char *string; + int strsize; + while( (count0)&&(dest[path_length-1]=='\\')) + dest[path_length-1]='\0'; + + //free up all the allocated memory and we're done + for(count=0;count +#include +#include + +#include "pserror.h" +#include "pstring.h" +#include "mono.h" +#include "ddio.h" +#include "ddio_win.h" +#include "Application.h" +#include "forcefeedback.h" +#include "mem.h" +#include "dinput.h" + +//#include "iforce2.h" + +void ddio_LoadImmersionDLL(void); +void ddio_FreeImmersionDLL(void); + + +void PrintDirectInputErrorString( HRESULT hr,char *format, ... ); + +#define DDIO_JOY_COOP_FLAGS (DISCL_EXCLUSIVE | DISCL_BACKGROUND) + +bool ddForce_found = false; //a Force Feedback device was found +bool ddForce_enabled = false; //Force Feedback is ready and can be used + + +// Force Feedback Effect Data +// -------------------------- +const GUID *effectGUID[kMaxEffectSubTypes] = { + &GUID_ConstantForce, + &GUID_RampForce, + &GUID_CustomForce, + // period + &GUID_Square, + &GUID_Sine, + &GUID_Triangle, + &GUID_SawtoothUp, + &GUID_SawtoothDown, + // condition + &GUID_Spring, + &GUID_Damper, + &GUID_Inertia, + &GUID_Friction +}; + +typedef union { + DICONSTANTFORCE constant; + DIRAMPFORCE ramp; + DIPERIODIC period; + DICONDITION condition; + DICUSTOMFORCE custom; +}tEffectClasses; + +typedef struct tEffect{ + DIEFFECT general; + tEffectClasses specific; + DIENVELOPE envelope; + LONG direction[2]; +}tddEffect; + +static tddEffect ddEffect[DDIO_FF_MAXEFFECTS]; + +// DInput Data +// ----------- +static LPDIRECTINPUT DI = NULL; +static LPDIRECTINPUTDEVICE DID1_Rat = NULL; +static LPDIRECTINPUTDEVICE DID1_Key = NULL; +static LPDIRECTINPUTDEVICE2 DID2_Joy[kMaxJoy]; +static LPDIRECTINPUTEFFECT DIE_hEffect[DDIO_FF_MAXEFFECTS]; + +// Joystick Data +// ------------- +static int maskFFB = 0; +static int numJoy = 0; +static int numEffects = 0; +static char isWheel[kMaxJoy]; +static char joyName[kMaxJoy][MAX_PATH]; +static DWORD dwAxes[2] = { DIJOFS_X, DIJOFS_Y }; + +// Private Functions +// ----------------- +BOOL CALLBACK FFEnumCallback(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef); +static int ddio_ffjoy_AcquireErr(HRESULT res, int dev_num); +static int ddio_ffjoy_UnacquireErr(HRESULT res, int dev_num); + +static int ddio_ff_SetCoopLevel(tDevice dev, int coop_level); +int ddio_ffb_Init(void); + +void *getwindowhandle(void) +{ + return DInputData.hwnd; +} + +/* +===================================================================== += = += Device & Low-Level Driver Functions = += = +===================================================================== +*/ + +LPDIRECTINPUTDEVICE2 ddio_ff_get_joystick_obj(tDevice dev) +{ + return DID2_Joy[(int)dev]; +} + + +// ------------------------------------------------------------------- +// ddio_ff_AttachForce +// Purpose: +// Attaches variables initialized in the general ddio system to +// the force feedback system. +// ------------------------------------------------------------------- +void ddio_ff_AttachForce(void) +{ + ddio_LoadImmersionDLL(); + + for(int i=0;iEnumDevices(DIDEVTYPE_JOYSTICK,FF_CB_PTR,DI,DIEDFL_ATTACHEDONLY); + + for (i=0; i kAllDevices){ + mprintf((0,"ddio_ff_Acquire: Invalid device ID, out of range\n")); + return 0; + }else{ + if (DID2_Joy[dev]){ + int ret = ddio_ffjoy_AcquireErr(IDirectInputDevice2_Acquire(DID2_Joy[dev]),dev); + return ret; + } + }break; + } + }else + mprintf((0,"ddio_ff_Acquire: Direct Input object not initialized...\n")); + + return cnt; +} + +// ------------------------------------------------------------------- +// ddio_ff_Unacquire +// Purpose: +// Unacquires a direct input device +// +// Input: +// The device to unacquire (use kDI_MaxJoy to unacquire all available +// joysticks). +// +// Return: +// # of devices unacquired. +// +// Description: +// Call this to lose access to a device after the device has been +// aquired +// +// ------------------------------------------------------------------- +int ddio_ff_Unacquire(tDevice dev) +{ + int i,cnt=0; + + if(DI){ + if (dev == kAllDevices){ + cnt += ddio_ff_Unacquire(kMaxJoy); + return cnt; + } + + switch (dev){ + case kMaxJoy: + { + for (i=0; i kAllDevices){ + mprintf((0,"ddio_ff_Unacquire: Invalid device ID, out of range\n")); + return 0; + }else{ + if (DID2_Joy[dev]){ + int ret = ddio_ffjoy_UnacquireErr(IDirectInputDevice2_Unacquire(DID2_Joy[dev]),dev); + return ret; + } + }break; + } + }else + mprintf((0,"ddio_ff_Unacquire: Direct Input object not initialized...\n")); + + return cnt; +} + + +// ------------------------------------------------------------------- +// ddio_ff_SetCoopLevel +// ------------------------------------------------------------------- +static int ddio_ff_SetCoopLevel(tDevice dev, int coop_level) +{ + HWND hwin; + + if (!(hwin=(HWND)getwindowhandle())) + { + mprintf((0,"ddio_ff_SetCoopLevel: couldn't get window handle\n")); + } + + // Set a single joystick + // --------------------- + if (dev < kMaxJoy){ + if (DID2_Joy[dev]){ + // Set the cooperative level to share the device + // --------------------------------------------- + if (IDirectInputDevice2_SetCooperativeLevel(DID2_Joy[dev], (HWND)getwindowhandle(), coop_level)!= DI_OK){ + mprintf((0,"ddio_ff_SetCoopLevel: Could not set dinput device coop level\n")); + return 0; + } + } + }else// Set all single joysticks + if (dev == kMaxJoy){ + int i; + for (i=kJoy1; idwDevType)) + isWheel[numJoy] = TRUE; + else + isWheel[numJoy] = FALSE; + + // Create an instance of the device + // -------------------------------- + if (IDirectInput_CreateDevice(pdi, pdinst->guidInstance, &pdev, NULL)!= DI_OK){ + mprintf((0,"DIEnumJoysticks_Callback: Could not create dinput device obj\n")); + return DIENUM_CONTINUE; + } + + // Set the data format to the default + // ---------------------------------- + if (IDirectInputDevice_SetDataFormat(pdev, &c_dfDIJoystick)!= DI_OK){ + mprintf((0,"DIEnumJoysticks_Callback: Could not set dinput device data format\n")); + IDirectInputDevice_Unacquire(pdev); + IDirectInputDevice_Release (pdev); + return DIENUM_CONTINUE; + } + + // Get the DID2 from DID1 + // ---------------------- + if (IDirectInputDevice_QueryInterface(pdev, IID_IDirectInputDevice2, (void **) &DID2_Joy[numJoy])!= DI_OK){ + mprintf((0,"DIEnumJoysticks_Callback: QueryInterface did not return DI_OK\n")); + IDirectInputDevice_Unacquire(pdev); + IDirectInputDevice_Release (pdev); + return DIENUM_CONTINUE; + } + + // Set the cooperative level + // ------------------------- + if (!ddio_ff_SetCoopLevel((tDevice)numJoy, DDIO_JOY_COOP_FLAGS)){ + mprintf((0,"DIEnumJoysticks_Callback: Could not set dinput coop level\n")); + return FALSE; + } + + // Done with Device1 + // ----------------- + IDirectInputDevice_Unacquire(pdev); + IDirectInputDevice_Release (pdev); + + // Device was added successfully + // ----------------------------- + numJoy++; + + return DIENUM_CONTINUE; +} + + +// ------------------------------------------------------------------- +// ddio_ffjoy_Query +// Purpose: +// Besides checking what buttons/axis are available, this function +// also checks for force feedback support. +// ------------------------------------------------------------------- +int ddio_ffjoy_Query(int dev, int* but_flags, int* axis_flags) +{ + unsigned int i,bit; + DIDEVCAPS DICaps; + DIDEVICEOBJECTINSTANCE DIObjInst; + DWORD DIAxisOFS[6] = { DIJOFS_X, + DIJOFS_Y, + DIJOFS_Z, + DIJOFS_RX, + DIJOFS_SLIDER(0), + DIJOFS_SLIDER(1) }; + + // Make sure Main DInput OBJ has been created + // ------------------------------------------ + if (!DI){ + ddio_ff_Init(); + if (!DI){ + mprintf((0,"ddio_ffjoy_Query: Dinput not initialized yet\n")); + return FALSE; + } + } + + if (!DID2_Joy[dev]){ + mprintf((0,"ddio_ffjoy_Query: device not found #%d\n",dev)); + return 0; + } + + if (!numJoy){ + if (but_flags)*but_flags=0; + if (axis_flags)*axis_flags=0; + return 0; + } + + ddio_ff_Acquire((tDevice)dev); + + DICaps.dwSize = sizeof(DIDEVCAPS); + + if (IDirectInputDevice2_GetCapabilities(DID2_Joy[dev],&DICaps) != DI_OK){ + mprintf((0,"ddio_ffjoy_Query: Failed getting device caps\n")); + return 0; + } + + if (DICaps.dwFlags & DIDC_FORCEFEEDBACK){ + mprintf((0,"ddio_ff_joy_Query: ffb support\n")); + maskFFB |= 1<= kMaxJoy){ + return; + } + + if (DID2_Joy[dev] && (maskFFB & 1<SetProperty(DIPROP_AUTOCENTER,&DIPropAutoCenter.diph); + ddio_ff_Acquire(dev); + + if (FAILED(hr)){ + PrintDirectInputErrorString(hr,"Force: Failed to change autocenter property."); + } + } +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_SetGain +// Purpose: +// Sets the gain for joystick, pass a value of 0-1 +// ------------------------------------------------------------------- +void ddio_ffjoy_SetGain(tDevice dev,float value) +{ + DIPROPDWORD DIPropAutoCenter; + DIPropAutoCenter.diph.dwSize = sizeof(DIPropAutoCenter); + DIPropAutoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER); + DIPropAutoCenter.diph.dwObj = 0; + DIPropAutoCenter.diph.dwHow = DIPH_DEVICE; + + DIPropAutoCenter.dwData = (DWORD)(value*10000.0f); + + if (dev >= kMaxJoy){ + return; + } + + if (DID2_Joy[dev] && (maskFFB & 1<SetProperty(DIPROP_FFGAIN ,&DIPropAutoCenter.diph); + + if (FAILED(hr)){ + PrintDirectInputErrorString(hr,"Force: Failed to set gain."); + } + } +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_IsAutoCentered +// Purpose: +// Returns true if the joystick is set for autocentering +// ------------------------------------------------------------------- +bool ddio_ffjoy_IsAutoCentered(tDevice dev) +{ + DIPROPDWORD DIPropAutoCenter; + DIPropAutoCenter.diph.dwSize = sizeof(DIPropAutoCenter); + DIPropAutoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER); + DIPropAutoCenter.diph.dwObj = 0; + DIPropAutoCenter.diph.dwHow = DIPH_DEVICE; + + if (dev >= kMaxJoy){ + return false; + } + + if (DID2_Joy[dev] && (maskFFB & 1<GetProperty(DIPROP_AUTOCENTER,&DIPropAutoCenter.diph); + ddio_ff_Acquire(dev); + + if (FAILED(hr)){ + PrintDirectInputErrorString(hr,"Force: Failed to get autocenter property"); + return false; + } + return (DIPropAutoCenter.dwData)?true:false; + } + return false; +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_SupportAutoCenter +// Purpose: +// Returns true if the FF joystick supports auto centering +// ------------------------------------------------------------------- +bool ddio_ffjoy_SupportAutoCenter(tDevice dev) +{ + if (dev >= kMaxJoy){ + return false; + } + + if (DID2_Joy[dev] && (maskFFB & 1<SetProperty(DIPROP_AUTOCENTER,&DIPropAutoCenter.diph); + ddio_ff_Acquire(dev); + + switch(hr){ + case DI_OK: + case S_FALSE: + return true; + default: + PrintDirectInputErrorString(hr,""); + return false; + } + } + + return false; +} + +/* +============================================================================= += = += Force Feedback Effect Functions = += = +============================================================================= +*/ + +// ------------------------------------------------------------------- +// ddio_ff_GetInfo +// Purpose: +// Returns information about the current state of the low-level +// Force Feedback system. +// ------------------------------------------------------------------- +void ddio_ff_GetInfo(bool *ff_found,bool *ff_enabled) +{ + if(ddForce_found){ + if(ff_found) + *ff_found = true; + if(ff_enabled) + *ff_enabled = ddForce_enabled; + + }else{ + if(ff_found) + *ff_found = false; + if(ff_enabled) + *ff_enabled = false; + } +} + + +// ------------------------------------------------------------------- +// ddio_ffb_Pause +// Purpose: +// Pause the FFB output on the given device. Use ddio_ffb_Continue to +// continue where you left off. +// ------------------------------------------------------------------- +void ddio_ffb_Pause(tDevice dev) +{ + if (dev == kMaxJoy){ + int i; + for (i=0; i= DDIO_FF_MAXEFFECTS){ + mprintf((0,"ddio_ffb_effectCreate: Reached hardcoded limit for # of effects.\n")); + return -1; + } + + if (!(DID2_Joy[dev] && (maskFFB & 1<Duration; + ddEffect[numEffects].general.dwSamplePeriod = eff->Period; + ddEffect[numEffects].general.dwGain = eff->Gain; + ddEffect[numEffects].general.dwTriggerButton = (eff->Trigger==kNoButton?DIEB_NOTRIGGER:DIJOFS_BUTTON(eff->Trigger)); + ddEffect[numEffects].general.dwTriggerRepeatInterval = eff->TriggerRepeatTime; + ddEffect[numEffects].direction[0] = eff->Direction; + memcpy(&ddEffect[numEffects].specific,&eff->TypeInfo,sizeof(tEffectClasses)); + + // COM related + ddEffect[numEffects].general.dwSize = sizeof(DIEFFECT); + ddEffect[numEffects].general.cAxes = isWheel[dev]?1:2; + ddEffect[numEffects].general.rgdwAxes = &dwAxes[0]; + ddEffect[numEffects].general.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS; + ddEffect[numEffects].general.lpvTypeSpecificParams = &ddEffect[numEffects].specific; + ddEffect[numEffects].general.rglDirection = (LONG *)ddEffect[numEffects].direction; + ddEffect[numEffects].direction[1] = 0L; + ddEffect[numEffects].general.lpEnvelope = NULL; + + if(eff->Flags&FF_USEENVELOPE){ + ddEffect[numEffects].envelope.dwSize = sizeof(DIENVELOPE); + ddEffect[numEffects].envelope.dwAttackLevel = eff->Envelope.AttackLevel; + ddEffect[numEffects].envelope.dwAttackTime = eff->Envelope.AttackTime; + ddEffect[numEffects].envelope.dwFadeLevel = eff->Envelope.FadeLevel; + ddEffect[numEffects].envelope.dwFadeTime = eff->Envelope.FadeTime; + ddEffect[numEffects].general.lpEnvelope = &ddEffect[numEffects].envelope; + } + + + switch (eff->Type){ + case kConstant: + ddEffect[numEffects].general.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + break; + case kRamp: + ddEffect[numEffects].general.cbTypeSpecificParams = sizeof(DIRAMPFORCE); + break; + case kCustom: + ddEffect[numEffects].general.cbTypeSpecificParams = sizeof(DICUSTOMFORCE); + break; + case kWave_Square: + case kWave_Sine: + case kWave_Triangle: + case kWave_SawUp: + case kWave_SawDown: + ddEffect[numEffects].general.cbTypeSpecificParams = sizeof(DIPERIODIC); + break; + case kCondition_Spring: + case kCondition_Damper: + case kCondition_Inertia: + case kCondition_Friction: + ddEffect[numEffects].general.cbTypeSpecificParams = sizeof(DICONDITION); + break; + default: + mprintf((0,"ddio_ffb_effectCreate: bad effect subType\n")); + return -1; + } + + hr=IDirectInputDevice2_CreateEffect( DID2_Joy[dev], + *effectGUID[eff->Type], + (LPCDIEFFECT) &ddEffect[numEffects], + (LPDIRECTINPUTEFFECT*) &DIE_hEffect[numEffects], 0); + + switch (hr){ + case DI_OK: + ddio_ffb_effectUnload(numEffects); + numEffects++; + return (numEffects-1); + case DIERR_DEVICENOTREG: + mprintf((0,"ddio_ffb_effectCreate: effect not created, DIERR_DEVICENOTREG\n")); + return -1; + case DIERR_DEVICEFULL: + mprintf((0,"ddio_ffb_effectCreate: effect not created, DIERR_DEVICEFULL\n")); + return -1; + default: + PrintDirectInputErrorString(hr,"Force Effect Create"); + break; + } + return -1; +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectPlay +// Purpose: +// Play an effect that was previously created. +// ------------------------------------------------------------------- +void ddio_ffb_effectPlay(short eID) +{ + if(eID<0 || eID>=DDIO_FF_MAXEFFECTS){ + Int3(); //invalid eID + return; + } + bool try_again = true; + +ff_try: + HRESULT hr = IDirectInputEffect_Start(DIE_hEffect[eID],1,0); + + + if(FAILED(hr)){ + PrintDirectInputErrorString(hr,"Force Play"); + if(try_again){ + ddio_ff_Acquire(kMaxJoy); + try_again = false; + goto ff_try; + } + } + +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectStop +// Purpose: +// Stop a single effect. +// ------------------------------------------------------------------- +void ddio_ffb_effectStop(short eID) +{ + if(eID<0 || eID>=DDIO_FF_MAXEFFECTS){ + Int3(); //invalid eID + return; + } + + IDirectInputEffect_Stop(DIE_hEffect[eID]); +} + +// ------------------------------------------------------------------- +// ddio_ffb_effectStopAll +// Purpose: +// Stops all forces on the given device. +// ------------------------------------------------------------------- +void ddio_ffb_effectStopAll(tDevice dev) +{ + if (DID2_Joy[dev] && (maskFFB & 1<=DDIO_FF_MAXEFFECTS){ + Int3(); //invalid eID + return; + } + IDirectInputEffect_Unload(DIE_hEffect[eID]); +} + + +// ------------------------------------------------------------------- +// ddio_ffb_effectModify +// Purpose: +// Modifies a single effect, only if the given parameters are +// different from what's currently loaded. +// ------------------------------------------------------------------- +void ddio_ffb_effectModify(short eID, int* Direction, unsigned int* Duration, unsigned int* Gain, unsigned int* Period, tEffInfo* TypeInfo, tEffEnvelope* Envelope) +{ + unsigned int flags = 0; + + //return; + + if (Direction){ + if (ddEffect[eID].direction[0] != *Direction){ + ddEffect[eID].direction[0] = *Direction; + flags |= DIEP_DIRECTION; + } + } + + if (Duration){ + if (ddEffect[eID].general.dwDuration != *Duration){ + ddEffect[eID].general.dwDuration = *Duration; + flags |= DIEP_DURATION; + } + } + + if (Gain){ + if (ddEffect[eID].general.dwGain != *Gain){ + ddEffect[eID].general.dwGain = *Gain; + flags |= DIEP_GAIN; + } + } + + if (Period){ + if (ddEffect[eID].general.dwSamplePeriod != *Period){ + ddEffect[eID].general.dwSamplePeriod = *Period; + flags |= DIEP_SAMPLEPERIOD; + } + } + + if (TypeInfo){ + if (!memcmp( &ddEffect[eID].specific, TypeInfo, ddEffect[eID].general.cbTypeSpecificParams)){ + memcpy( &ddEffect[eID].specific, TypeInfo, ddEffect[eID].general.cbTypeSpecificParams); + flags |= DIEP_TYPESPECIFICPARAMS; + } + } + + if (Envelope){ + if (ddEffect[eID].envelope.dwAttackLevel != Envelope->AttackLevel || + ddEffect[eID].envelope.dwAttackTime != Envelope->AttackTime || + ddEffect[eID].envelope.dwFadeLevel != Envelope->FadeLevel || + ddEffect[eID].envelope.dwFadeTime != Envelope->FadeTime ){ + ddEffect[eID].envelope.dwAttackLevel = Envelope->AttackLevel; + ddEffect[eID].envelope.dwAttackTime = Envelope->AttackTime; + ddEffect[eID].envelope.dwFadeLevel = Envelope->FadeLevel; + ddEffect[eID].envelope.dwFadeTime = Envelope->FadeTime; + flags |= DIEP_ENVELOPE; + } + } + + if (!flags) + return; + + IDirectInputEffect_SetParameters(DIE_hEffect[eID], (LPCDIEFFECT)&ddEffect[eID], flags); +} + +// ------------------------------------------------------------------- +// ddio_ffb_GetEffectData +// Purpose: +// Retrieves affect data for the given parameters, pass NULL for those you don't want +// ------------------------------------------------------------------- +void ddio_ffb_GetEffectData(short eID, int* Direction, unsigned int* Duration, unsigned int* Gain, unsigned int* Period, tEffInfo* TypeInfo, tEffEnvelope* Envelope) +{ + if (Direction){ + *Direction = ddEffect[eID].direction[0]; + } + + if (Duration){ + *Duration = ddEffect[eID].general.dwDuration; + } + + if (Gain){ + *Gain = ddEffect[eID].general.dwGain; + } + + if (Period){ + *Period = ddEffect[eID].general.dwSamplePeriod; + } + + if (TypeInfo){ + memcpy( TypeInfo,&ddEffect[eID].specific,ddEffect[eID].general.cbTypeSpecificParams); + } + + if (Envelope){ + Envelope->AttackLevel = ddEffect[eID].envelope.dwAttackLevel; + Envelope->AttackTime = ddEffect[eID].envelope.dwAttackTime; + Envelope->FadeLevel = ddEffect[eID].envelope.dwFadeLevel; + Envelope->FadeTime = ddEffect[eID].envelope.dwFadeTime; + } +} + + +//==================================================================== +// Private Fuctions +//==================================================================== + +// ------------------------------------------------------------------- +// ddio_ffjoy_AcquireErr +// Purpose: +// Handle success/err reporting +// ------------------------------------------------------------------- +static int ddio_ffjoy_AcquireErr(HRESULT res, int dev_num) +{ + if (res == DI_OK){ + mprintf((0,"device #%d acquired\n",dev_num)); + return TRUE; + } + else + if (res == S_FALSE){ + mprintf((0,"device #%d already acquired\n",dev_num)); + return TRUE; + } + else + if (res == DIERR_INVALIDPARAM){ + mprintf((0,"device %d DIERR_INVALIDPARAM\n",dev_num)); + return FALSE; + } + else + if (res == DIERR_OTHERAPPHASPRIO){ + mprintf((0,"device %d DIERR_OTHERAPPHASPRIO\n",dev_num)); + return TRUE; + } + else + mprintf((0,"Unknown Error acquiring device %d\n",dev_num)); + + return FALSE; +} + +// ------------------------------------------------------------------- +// ddio_ffjoy_UnacquireErr +// Purpose: +// Handle success/err reporting +// ------------------------------------------------------------------- +static int ddio_ffjoy_UnacquireErr(HRESULT res, int dev_num) +{ + if (res == DI_OK){ + mprintf((0,"device #%d unacquired\n",dev_num)); + return TRUE; + } + else + if (res == S_FALSE){ + mprintf((0,"device #%d already unacquired\n",dev_num)); + return TRUE; + } + else + if (res == DIERR_INVALIDPARAM){ + mprintf((0,"device %d DIERR_INVALIDPARAM\n",dev_num)); + return FALSE; + } + else + if (res == DIERR_OTHERAPPHASPRIO){ + mprintf((0,"device %d DIERR_OTHERAPPHASPRIO\n",dev_num)); + return TRUE; + } + else + mprintf((0,"Unknown Error unacquiring device %d\n",dev_num)); + + return FALSE; +} + + +/* +***************************************************************** + + Immersion + +***************************************************************** +*/ +#if 0 +#pragma message("Compiling Immersion Support.") + +HMODULE ImmersionHandle = NULL; +typedef HIFORCEPROJECT (__stdcall *IFLoadProjectFile_fp)(LPCSTR pProjectFileName,LPDIRECTINPUTDEVICE2A pDevice ); +IFLoadProjectFile_fp d_IFLoadProjectFile = NULL; + +typedef BOOL (__stdcall *IFReleaseProject_fp)(HIFORCEPROJECT hProject); +IFReleaseProject_fp d_IFReleaseProject = NULL; + +typedef LPDIRECTINPUTEFFECT *(__stdcall *IFCreateEffects_fp)(HIFORCEPROJECT hProject,LPCSTR pObjectName,int *pNumEffects ); +IFCreateEffects_fp d_IFCreateEffects = NULL; + + +void ddio_LoadImmersionDLL(void) +{ + if(ImmersionHandle!=NULL){ + ddio_FreeImmersionDLL(); + } + + ImmersionHandle = LoadLibrary("IForce2.dll"); + + if(ImmersionHandle==NULL){ + //not found + mprintf((0,"Force: Unable to find Immersion DLL\n")); + return; + } + + //get functions + d_IFLoadProjectFile = (IFLoadProjectFile_fp)GetProcAddress(ImmersionHandle,"_IFLoadProjectFile@8"); + d_IFReleaseProject = (IFReleaseProject_fp)GetProcAddress(ImmersionHandle,"_IFReleaseProject@4"); + d_IFCreateEffects = (IFCreateEffects_fp)GetProcAddress(ImmersionHandle,"_IFCreateEffects@12"); + + if( !d_IFLoadProjectFile || + !d_IFReleaseProject || + !d_IFCreateEffects){ + mprintf((0,"Force: Unable to bind Immersion functions\n")); + ddio_FreeImmersionDLL(); + return; + } + + mprintf((0,"Immersion IFORCE2 DLL loaded successfully\n")); +} + +void ddio_FreeImmersionDLL(void) +{ + if(ImmersionHandle){ + FreeLibrary(ImmersionHandle); + ImmersionHandle = NULL; + d_IFLoadProjectFile = NULL; + d_IFReleaseProject = NULL; + d_IFCreateEffects = NULL; + } +} + +// Given a filename resource, this loads the file and creates a resource +// for it. It returns a handle to that resource. +// If it returns NULL, then it couldn't load the project. +// Make sure device is aquired before calling. +FORCEPROJECT ddio_ForceLoadProject(char *filename,tDevice dev) +{ + if(!d_IFLoadProjectFile){ + return NULL; + } + + if( dev < 0 || dev >kMaxJoy ){ + mprintf((0,"illegal device id passed to ddio_ForceLoadProject() - %d\n",(int)dev)); + return NULL; + } + + HIFORCEPROJECT prj = d_IFLoadProjectFile(filename,DID2_Joy[dev]); + return (FORCEPROJECT)prj; +} + +// Unloads a FORCEPROJECT file +void ddio_ForceUnloadProject(FORCEPROJECT prj) +{ + if(!d_IFReleaseProject) + return; + + if(!prj) + return; + + d_IFReleaseProject((HIFORCEPROJECT)prj); +} + +// Given a handle to a resource, and the name of the effect to load +// it will load that effect. Returns the effect ID, or -1 if it couldn't +// be created +int ddio_CreateForceFromProject(FORCEPROJECT project,char *forcename) +{ + if(!d_IFCreateEffects){ + return -1; + } + + LPDIRECTINPUTEFFECT * gppdie; + HIFORCEPROJECT prj; + + if(!project) + return -1; + + prj = (HIFORCEPROJECT)project; + + gppdie = d_IFCreateEffects(prj,forcename,NULL); + + if( !gppdie ){ + return -1; + } + + DIE_hEffect[numEffects] = *gppdie; + numEffects++; + return (numEffects-1); +} + +#else +#pragma message("Not Compiling For Immersion Support.") +// Given a filename resource, this loads the file and creates a resource +// for it. It returns a handle to that resource. +// If it returns NULL, then it couldn't load the project. +// Make sure device is aquired before calling. +FORCEPROJECT ddio_ForceLoadProject(char *filename,tDevice dev) +{ + return NULL; +} + +// Unloads a FORCEPROJECT file +void ddio_ForceUnloadProject(FORCEPROJECT prj) +{ + return; +} + +// Given a handle to a resource, and the name of the effect to load +// it will load that effect. Returns the effect ID, or -1 if it couldn't +// be created +int ddio_CreateForceFromProject(FORCEPROJECT project,char *forcename) +{ + return -1; +} + +void ddio_LoadImmersionDLL(void) +{ +} + +void ddio_FreeImmersionDLL(void) +{ +} + +#endif + + +//========================================================== +// Function: PrintDirectInputErrorString +// +// Description: +// Prints a debug message(s) of errors based on error +// code passed in. +// +//========================================================== +void PrintDirectInputErrorString( HRESULT hr,char *format, ... ) +{ +#ifdef RELEASE + return; +#endif + char buffer[2048]; + + va_list marker; + va_start(marker,format); + Pvsprintf(buffer,2048,format,marker); + va_end(marker); + strcat(buffer,": \n"); + + if(hr==S_FALSE){ + strcat(buffer,"*S_FALSE\n"); + mprintf((0,buffer)); + return; + } + + if(hr==DI_BUFFEROVERFLOW) + strcat(buffer,"*The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value.\n"); + if(hr==DI_DOWNLOADSKIPPED) + strcat(buffer,"*The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode. \n"); + if(hr==DI_EFFECTRESTARTED) + strcat(buffer,"*The effect was stopped, the parameters were updated, and the effect was restarted.\n"); + if(hr==DI_NOEFFECT) + strcat(buffer,"*The operation had no effect. This value is equal to the S_FALSE standard COM return value.\n"); + if(hr==DI_NOTATTACHED) + strcat(buffer,"*The device exists but is not currently attached. This value is equal to the S_FALSE standard COM return value.\n"); + if(hr==DI_OK) + strcat(buffer,"*The operation completed successfully. This value is equal to the S_OK standard COM return value.\n"); + if(hr==DI_POLLEDDEVICE) + strcat(buffer,"*The device is a polled device. As a result, device buffering will not collect any data and event notifications will not be signaled until the IDirectInputDevice2::Poll method is called.\n"); + if(hr==DI_PROPNOEFFECT) + strcat(buffer,"*The change in device properties had no effect. This value is equal to the S_FALSE standard COM return value.\n"); + if(hr==DI_TRUNCATED) + strcat(buffer,"*The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value.\n"); + if(hr==DI_TRUNCATEDANDRESTARTED) + strcat(buffer,"*Equal to DI_EFFECTRESTARTED | DI_TRUNCATED.\n"); + if(hr==DIERR_ACQUIRED) + strcat(buffer,"*The operation cannot be performed while the device is acquired.\n"); + if(hr==DIERR_ALREADYINITIALIZED) + strcat(buffer,"*This object is already initialized\n"); + if(hr==DIERR_BADDRIVERVER) + strcat(buffer,"*The object could not be created due to an incompatible driver version or mismatched or incomplete driver components.\n"); + if(hr==DIERR_BETADIRECTINPUTVERSION) + strcat(buffer,"*The application was written for an unsupported prerelease version of DirectInput.\n"); + if(hr==DIERR_DEVICEFULL) + strcat(buffer,"*The device is full.\n"); + if(hr==DIERR_DEVICENOTREG) + strcat(buffer,"*The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value.\n"); + if(hr==DIERR_EFFECTPLAYING) + strcat(buffer,"*The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing. \n"); + if(hr==DIERR_HASEFFECTS) + strcat(buffer,"*The device cannot be reinitialized because there are still effects attached to it.\n"); + if(hr==DIERR_GENERIC) + strcat(buffer,"*An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value.\n"); + if(hr==DIERR_HANDLEEXISTS) + strcat(buffer,"*The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value.\n"); + if(hr==DIERR_INCOMPLETEEFFECT) + strcat(buffer,"*The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied.\n"); + if(hr==DIERR_INPUTLOST) + strcat(buffer,"*Access to the input device has been lost. It must be reacquired.\n"); + if(hr==DIERR_INVALIDPARAM) + strcat(buffer,"*An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value.\n"); + if(hr==DIERR_MOREDATA) + strcat(buffer,"*Not all the requested information fitted into the buffer.\n"); + if(hr==DIERR_NOAGGREGATION) + strcat(buffer,"*This object does not support aggregation.\n"); + if(hr==DIERR_NOINTERFACE) + strcat(buffer,"*The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value.\n"); + if(hr==DIERR_NOTACQUIRED) + strcat(buffer,"*The operation cannot be performed unless the device is acquired.\n"); + if(hr==DIERR_NOTBUFFERED) + strcat(buffer,"*The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering. \n"); + if(hr==DIERR_NOTDOWNLOADED) + strcat(buffer,"*The effect is not downloaded.\n"); + if(hr==DIERR_NOTEXCLUSIVEACQUIRED) + strcat(buffer,"*The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode. \n"); + if(hr==DIERR_NOTFOUND) + strcat(buffer,"*The requested object does not exist.\n"); + if(hr==DIERR_NOTINITIALIZED) + strcat(buffer,"*This object has not been initialized.\n"); + if(hr==DIERR_OBJECTNOTFOUND) + strcat(buffer,"*The requested object does not exist.\n"); + if(hr==DIERR_OLDDIRECTINPUTVERSION) + strcat(buffer,"*The application requires a newer version of DirectInput.\n"); + if(hr==DIERR_OTHERAPPHASPRIO) + strcat(buffer,"*Another application has a higher priority level, preventing this call from succeeding. This value is equal to the E_ACCESSDENIED standard COM return value. This error can be returned when an application has only foreground access to a device but is attempting to acquire the device while in the background. \n"); + if(hr==DIERR_OUTOFMEMORY) + strcat(buffer,"*The DirectInput subsystem couldn't allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value.\n"); + if(hr==DIERR_READONLY) + strcat(buffer,"*The specified property cannot be changed. This value is equal to the E_ACCESSDENIED standard COM return value.\n"); + //@@if(hr==DIERR_REPORTFULL) + //@@ strcat(buffer,"*More information was requested to be sent than can be sent to the device.\n"); + //@@if(hr==DIERR_UNPLUGGED) + //@@ strcat(buffer,"*The operation could not be completed because the device is not plugged in.\n"); + if(hr==DIERR_UNSUPPORTED) + strcat(buffer,"*The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value. \n"); + if(hr==E_PENDING) + strcat(buffer,"*Data is not yet available.\n"); + mprintf((0,buffer)); +} + +#else + +#include "forcefeedback.h" + +void ddio_ff_GetInfo(bool *ff_found,bool *ff_enabled) +{ + if( ff_found ) + { + *ff_found = false; + } + + if( ff_enabled ) + { + *ff_enabled = false; + } +} + +void ddio_ffb_Pause(tDevice dev) +{ +} + +void ddio_ffb_Continue(tDevice dev) +{ +} + +int ddio_ff_Acquire(tDevice dev) +{ + return 0; +} + +void ddio_ffjoy_EnableAutoCenter(tDevice dev,bool enable) +{ +} + +void ddio_ffjoy_SetGain(tDevice dev,float value) +{ +} + +bool ddio_ffjoy_SupportAutoCenter(tDevice dev) +{ + return false; +} + +void ddio_ffb_DestroyAll(void) +{ +} + +void ddio_ffb_effectPlay(short eID) +{ +} + +void ddio_ffb_effectModify(short eID, int* Direction, unsigned int* Duration, unsigned int* Gain, unsigned int* Period, tEffInfo* TypeInfo, tEffEnvelope* Envelope) +{ +} + +int ddio_ffb_effectCreate(tDevice dev, tFFB_Effect* eff) +{ + return -1; +} + +int ddio_CreateForceFromProject(FORCEPROJECT project,char *forcename) +{ + return -1; +} + +void ddio_ForceUnloadProject(FORCEPROJECT prj) +{ +} + +FORCEPROJECT ddio_ForceLoadProject(char *filename,tDevice dev) +{ + return NULL; +} + +int ddio_ffjoy_Init(void) +{ + return 0; +} + +int ddio_ff_Init(void) +{ + return 0; +} + +void ddio_ff_AttachForce(void) +{ +} + +void ddio_ff_DetachForce(void) +{ +} + +LPDIRECTINPUTDEVICE2 ddio_ff_get_joystick_obj(tDevice dev) +{ + return NULL; +} + +#endif diff --git a/ddio_win/winio.cpp b/ddio_win/winio.cpp new file mode 100644 index 000000000..ce758382f --- /dev/null +++ b/ddio_win/winio.cpp @@ -0,0 +1,186 @@ +/* + * $Logfile: /DescentIII/Main/ddio_win/winio.cpp $ + * $Revision: 17 $ + * $Date: 7/09/01 1:55p $ + * $Author: Matt $ + * + * + * + * $Log: /DescentIII/Main/ddio_win/winio.cpp $ + * + * 17 7/09/01 1:55p Matt + * Try opening DX5 for NT so force feedback will work. + * + * 16 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 15 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 14 11/18/98 5:50p Jeff + * free up force feedback before unloading DI + * + * 13 11/03/98 6:43p Jeff + * new low-level & high level Force Feedback system implemented, handles + * window losing focus, etc. + * + * 12 11/01/98 1:58a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 11 10/18/98 6:18p Matt + * Turn the pointer on when shutting down. This fixes a problem where you + * couldn't click Ok on an error message box because the pointer was off + * for our app. + * + * 10 9/18/98 7:38p Jeff + * creation of low-level forcefeedback and beginning of high-level + * forcefeedback + * + * 9 9/15/98 12:25p Jeff + * If we are in NT, then only require DX3, else require DX6 (we may need + * to change this due to NT5) + * + * 8 9/15/98 12:05p Jeff + * initial creation of low-level forcefeedback + * + * 7 6/29/98 6:43p Samir + * Took out GetMsgProc and legacy keyboard variables. + * + * 6 5/08/98 12:54p Samir + * newer unused mouse handler. + * + * 5 3/31/98 12:46p Samir + * keyboard IO system better. uses getmsgproc windows hook. + * + * 4 10/23/97 2:58p Samir + * Took out extranneous messages. + * + * 3 10/16/97 2:29p Samir + * Changed DirectInput Keyboard to FOREGROUND + * + * 2 8/01/97 7:30p Samir + * Better NT keyboard support. + * + * 10 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 9 5/08/97 1:56p Samir + * These functions should only be applicable to windows (no shared code) + * + * 8 3/18/97 11:14a Samir + * Use real error call when DirectInput init fails. + * + * 7 2/28/97 11:03a Samir + * Changes to reflect newer gameos interface. + * + * 6 1/30/97 6:08p Samir + * Reflects changes in gameos.h + * + * 5 1/23/97 2:23p Samir + * Preemptive flag now just affects timer. + * + * $NoKeywords: $ + */ + +// ---------------------------------------------------------------------------- +// Win32 IO System Main Library Interface +// ---------------------------------------------------------------------------- +#include "DDAccess.h" + +#include +#include + +#include "pserror.h" +#include "pstring.h" +#include "Application.h" +#include "ddio_win.h" +#include "ddio.h" +#include "dinput.h" + +#include "forcefeedback.h" + +bool DDIO_init = 0; +dinput_data DInputData; + + + +// ---------------------------------------------------------------------------- +// Initialization and destruction functions +// ---------------------------------------------------------------------------- + +bool ddio_InternalInit(ddio_init_info *init_info) +{ + oeWin32Application *obj = (oeWin32Application *)init_info->obj; + LPDIRECTINPUT lpdi; + HRESULT dires; + + ASSERT(!DDIO_init); + +// Initialize DirectInput subsystem + mprintf((0, "DI system initializing.\n")); + + //Try to open DirectX 5.00 + dires = DirectInputCreate((HINSTANCE)obj->m_hInstance, DIRECTINPUT_VERSION, &lpdi, NULL); + + //Deal with error opening DX5 + if (dires != DI_OK) { + + //If running NT, try DirectX 3.0 + if(obj->NT()){ + + dires = DirectInputCreate((HINSTANCE)obj->m_hInstance, 0x0300, &lpdi, NULL); + if (dires != DI_OK) { + Error("Unable to DirectInput system (Requires at least DirectX 3.0 For NT) [DirectInput:%x]\n", dires); + } + } + else { //Not running NT, so error out + + //Couldn't open DirectX, so print error + Error("Unable to DirectInput system (Requires at least DirectX 5.0) [DirectInput:%x]\n", dires); + } + } + + DInputData.app = obj; + DInputData.lpdi = lpdi; + DInputData.hwnd = (HWND)obj->m_hWnd; + + DDIO_init = 1; + + return 1; +} + + +void ddio_InternalClose() +{ + ASSERT(DDIO_init); + +// //Close down forcefeedback +// ddio_ff_DetachForce(); + + DInputData.lpdi->Release(); + DInputData.lpdi = NULL; + DDIO_init = 0; + + mprintf((0, "DI system closed.\n")); +} + + +#ifdef _DEBUG +void ddio_DebugMessage(unsigned err, char *fmt, ...) +{ + char buf[128]; + va_list arglist; + + va_start(arglist,fmt); + Pvsprintf(buf,128,fmt,arglist); + va_end(arglist); + + mprintf((0, "DDIO: %s\n", buf)); + if (err) { + mprintf((1, "DIERR %x.\n", err)); + } +} +#endif + + diff --git a/ddio_win/winjoy.cpp b/ddio_win/winjoy.cpp new file mode 100644 index 000000000..02e9cf972 --- /dev/null +++ b/ddio_win/winjoy.cpp @@ -0,0 +1,788 @@ +/* + * $Logfile: /DescentIII/Main/ddio_win/winjoy.cpp $ + * $Revision: 19 $ + * $Date: 8/10/99 5:11p $ + * $Author: Jeff $ + * + * joystick header + * + * $Log: /DescentIII/Main/ddio_win/winjoy.cpp $ + * + * 19 8/10/99 5:11p Jeff + * always call detachforce, it needs to free DLLs + * + * 18 7/30/99 1:06p Samir + * fixed hat code for direct input and apply sensitivities for calibration + * of axes. + * + * 17 7/26/99 11:59a Samir + * add code to get name of joystick + * + * 16 7/16/99 11:14a Samir + * multiple hat support and improved direct input support. + * + * 15 5/23/99 12:45a Samir + * if joystick was never found, when returning from joygetpos, set all + * position values to a neutral value. + * + * 14 5/19/99 4:38p Samir + * trying 'centered' flag for joyGetPosEx to automatically center joystick + * on midpoint of min and max values for axis. I don't know why this + * isn't the case anyway, but... + * + * 13 5/10/99 9:22p Samir + * added CH Flightstick Pro hack. + * + * 12 5/08/99 1:05a Samir + * initialize joysticks always, but return NULL values if the joystick is + * unplugged. + * + * 11 5/05/99 10:55p Samir + * moved force feedback code so it's always initialized. + * + * 10 4/24/99 8:41p Samir + * added debug code to get controller positions on mono. + * + * 9 4/12/99 12:12p Samir + * took out int3. + * + * 8 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 7 2/21/99 6:38p Samir + * mouse and key input better. buffered mouse. + * + * 6 2/03/99 6:47p Jeff + * (Samir) hacked pov value, so it's always a word + * + * 5 10/18/98 7:25p Samir + * made joy_GetPos safe. + * + * 4 6/02/98 4:37p Samir + * multiple joysticks supported. + * + * 3 6/01/98 4:27p Samir + * pov may return multiple positions. + * + * 2 12/03/97 7:33p Samir + * Improved joystick interface. + * + * 4 6/11/97 2:40p Samir + * fixed bools. + * + * 3 2/26/97 6:16p Samir + * joy_init returns a 0 if no joysticks are present. + * + * 2 2/26/97 12:09p Samir + * Added some debug and error checking. + * + * $NoKeywords: $ + */ + + + +#include "joystick.h" +#include "forcefeedback.h" +#include "pserror.h" +#include "pstypes.h" +#include "mem.h" +#include "ddio_win.h" +#include "Macros.h" +#include "logfile.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////////// +typedef struct tJoystickRecord +{ + ubyte valid; // is this a valid device. + ubyte flags; // defined in ddio_win.h + short spad; + + union + { + int joyid; + tDevice ffdev; + } + joy_context; // used to identify the joystick. + + tJoyInfo caps; // controller capabilities + tJoyPos pos; // current position +} +tJoystickRecord; + + +////////////////////////////////////////////////////////////////////////////// +static struct tJoystickLibraryData +{ + short init; // library initialized? + short njoy; // maximum number of joysticks supported. + tJoystickRecord *joystick; // list of joysticks. + + LPDIRECTINPUT lpdi; // if lpdi != NULL, we use direct input for joysticks +} +WJD = {0,0,NULL,NULL }; + + + +////////////////////////////////////////////////////////////////////////////// + +bool joyw_get_name(int id, char *buf, const char *regkey); + +bool joymm_init(); +void joymm_close(); +tJoystick joymm_init_stick(int joyid); +void joymm_close_stick(tJoystick handle); +void joymm_get_pos(tJoystick handle, tJoyPos *pos); + +tJoystick joydi_init_stick(int ffjoyid); +void joydi_get_pos(tJoystick handle, tJoyPos *pos); + +// joystick forcefeedback shares +extern LPDIRECTINPUTDEVICE2 ddio_ff_get_joystick_obj(tDevice dev); + +bool joy_chpro_hack = false; // flightstick pro hack for Win32 only + + +////////////////////////////////////////////////////////////////////////////// +// initializes joystick interface. + +bool joy_Init(bool emulation) +{ + //Can we initialize DirectInput version? + //Try to init forcefeedback (which initializes joysticks) + uint i, n; // = joyGetNumDevs(); + + ddio_ff_AttachForce(); + +// initializes forcefeedback system? + if(ddio_ff_Init()){ + mprintf((0,"DDForce: Force Feedback Joystick Found\n")); + } + +// initialize data structures. + n = 2; + if (n) { + WJD.joystick = (tJoystickRecord *)mem_malloc(sizeof(tJoystickRecord)*n); + for (i = 0;i < n;i++) + { + WJD.joystick[i].valid = 0; + } + } + else { + WJD.init = 0; + return false; + } + WJD.njoy = n; + +// attempt directinput initialization. + n = (!emulation) ? ddio_ffjoy_Init() : 0; + + if(n > 0) { + WJD.lpdi = DInputData.lpdi; + + // map all ffjoysticks to our joystick structure. + for (i = 0; i < n; i++) + { + joydi_init_stick(i); + } + + } +// enumerate all windows joysticks (1 and 2 only) + else if (joymm_init()) { + WJD.lpdi = NULL; + } + else { + WJD.init = 0; + return false; + } + + WJD.init = 1; + + return true; +} + + +void joy_Close() +{ + ddio_ff_DetachForce(); + + if (!WJD.init) return; + + if (!WJD.lpdi) { + joymm_close(); + } + + mem_free(WJD.joystick); + WJD.joystick = NULL; + WJD.njoy = 0; + WJD.lpdi = NULL; + WJD.init = 0; +} + + +// returns true if joystick valid +bool joy_IsValid(tJoystick handle) +{ + if (!WJD.init) return false; + if (handle < 0 || handle >= WJD.njoy) return false; + + return WJD.joystick[handle].valid ? true : false; +} + + +// retreive uncalibrated position of joystick +void joy_GetRawPos(tJoystick joy, tJoyPos *pos) +{ + joy_GetPos(joy, pos); + pos->x = (pos->x+128)*JOYAXIS_RANGE; + pos->y = (pos->y+128)*JOYAXIS_RANGE; + pos->z = (pos->z+128)*JOYAXIS_RANGE; + pos->r = (pos->r+128)*JOYAXIS_RANGE; + pos->u = (pos->u+128)*JOYAXIS_RANGE; + pos->v = (pos->v+128)*JOYAXIS_RANGE; +} + + + +// returns the state of a stick, remote or otherwise +void joy_GetPos(tJoystick stick, tJoyPos *pos) +{ + ASSERT(stick >= 0 || stick < WJD.njoy); + int i; + + for (i = 0; i < JOYPOV_NUM; i++) + { + pos->pov[i] = JOYPOV_CENTER; + } + + if (stick < 0 || stick >= WJD.njoy) { + memset(pos, 0, sizeof(tJoyPos)); + return; + } + if (!WJD.joystick[stick].valid) { + memset(pos, 0, sizeof(tJoyPos)); + return; + } + + if (WJD.lpdi) { + joydi_get_pos(stick, pos); + } + else { + joymm_get_pos(stick, pos); + } +} + + +// retreive information about joystick. +void joy_GetJoyInfo(tJoystick joy, tJoyInfo *info) +{ + tJoyInfo *srcinfo; + ASSERT(joy >= 0 || joy < WJD.njoy); + + if (!WJD.init) return; + if (!WJD.joystick[joy].valid) return; + srcinfo = &WJD.joystick[joy].caps; + + memcpy(info, srcinfo, sizeof(tJoyInfo)); +} + + +// hook into ddio system if joystick needs any additonal processing per frame. +void ddio_InternalJoyFrame() +{ + if (!WJD.init) return; + + if (WJD.lpdi) { + int i; + tJoystickRecord *joystick = &WJD.joystick[0]; + for (i = 0; i < WJD.njoy; i++) + { + int try_count = 0; + + if (joystick->valid) { + LPDIRECTINPUTDEVICE2 lpdidev = ddio_ff_get_joystick_obj(joystick->joy_context.ffdev); + + retry_joy_poll: + HRESULT hr = lpdidev->Poll(); + if (hr == DIERR_INPUTLOST && try_count < 2) { + ddio_ff_Acquire(joystick->joy_context.ffdev); + try_count++; + goto retry_joy_poll; + } + } + joystick++; + } + } +} + + + +////////////////////////////////////////////////////////////////////////////// +// initializes multimedia version of joystick interface. +bool joymm_init() +{ + joymm_init_stick(JOYSTICKID1); + if (!joy_chpro_hack) { + joymm_init_stick(JOYSTICKID2); + } + + return true; +} + + +// frees joystick library. +void joymm_close() +{ + int i; + for (i = 0; i < WJD.njoy; i++) + { + if (WJD.joystick[i].valid) { + joymm_close_stick(WJD.joystick[i].joy_context.joyid); + } + } +} + + +// initializes one stick. +tJoystick joymm_init_stick(int joyid) +{ + int i; + +// find free joystick slot + for (i = 0; i < WJD.njoy; i++) + { + if (!WJD.joystick[i].valid) { + // found slot, lets initialize it and return. + JOYCAPS jc; + + if (joyGetDevCaps(joyid, &jc, sizeof(jc))==JOYERR_NOERROR) { + tJoyInfo *caps = &WJD.joystick[i].caps; + + WJD.joystick[i].valid = 1; + WJD.joystick[i].joy_context.joyid = joyid; + + memset(caps, 0, sizeof(tJoyInfo)); + caps->axes_mask = ((jc.wCaps & JOYCAPS_HASR) ? JOYFLAG_RVALID : 0) | + ((jc.wCaps & JOYCAPS_HASZ) ? JOYFLAG_ZVALID : 0) | + ((jc.wCaps & JOYCAPS_HASU) ? JOYFLAG_UVALID : 0) | + ((jc.wCaps & JOYCAPS_HASV) ? JOYFLAG_VVALID : 0) | + ((jc.wCaps & JOYCAPS_HASPOV) ? JOYFLAG_POVVALID : 0) | (JOYFLAG_XVALID+JOYFLAG_YVALID); + caps->num_btns = (joy_chpro_hack) ? 32 : jc.wNumButtons; + caps->minx = jc.wXmin; + caps->miny = jc.wYmin; + caps->minz = jc.wZmin; + caps->minr = jc.wRmin; + caps->minu = jc.wUmin; + caps->minv = jc.wVmin; + caps->maxx = jc.wXmax; + caps->maxy = jc.wYmax; + caps->maxz = jc.wZmax; + caps->maxr = jc.wRmax; + caps->maxu = jc.wUmax; + caps->maxv = jc.wVmax; + + if (!joyw_get_name(joyid, caps->name, jc.szRegKey)) { + sprintf(caps->name, "Joystick-%d", i); + } + + return (tJoystick)(i); + } + else { + return (tJoystick)(-1); + } + } + } + + return -1; +} + + +void joymm_close_stick(tJoystick handle) +{ + if (handle < 0 || handle >= WJD.njoy) return; + + WJD.joystick[handle].valid = 0; +} + + +void joymm_get_pos(tJoystick handle, tJoyPos *pos) +{ + JOYINFOEX ji; + tJoyInfo *caps; + + if (handle < 0 || handle >= WJD.njoy) { + return; + } + if (!WJD.joystick[handle].valid) { + return; + } + + caps = &WJD.joystick[handle].caps; + + ZeroMemory(&ji, sizeof(JOYINFOEX)); + + ji.dwSize = sizeof(JOYINFOEX); + ji.dwFlags = JOY_RETURNCENTERED | JOY_USEDEADZONE | + JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU | JOY_RETURNV | + JOY_RETURNPOV | JOY_RETURNBUTTONS; + + if (joyGetPosEx(WJD.joystick[handle].joy_context.joyid, &ji) != JOYERR_NOERROR) { + memset(pos, 0, sizeof(tJoyPos)); + pos->pov[0] = JOYPOV_CENTER; + return; + } + + pos->x = (int)((ji.dwXpos<<8)/(caps->maxx - caps->minx)) - 128; + pos->y = (int)((ji.dwYpos<<8)/(caps->maxy - caps->miny)) - 128; + + if (caps->axes_mask & JOYFLAG_ZVALID) { + pos->z = (int)((ji.dwZpos<<8)/(caps->maxz - caps->minz)) - 128; + } + else { pos->z = 0; } + if (caps->axes_mask & JOYFLAG_RVALID) { + pos->r = (int)((ji.dwRpos<<8)/(caps->maxr - caps->minr)) - 128; + } + else { pos->r = 0; } + if (caps->axes_mask & JOYFLAG_UVALID) { + pos->u = (int)((ji.dwUpos<<8)/(caps->maxu - caps->minu)) - 128; + } + else { pos->u = 0; } + if (caps->axes_mask & JOYFLAG_VVALID) { + pos->v = (int)((ji.dwVpos<<8)/(caps->maxv - caps->minv)) - 128; + } + else { pos->v = 0; } + + if (caps->axes_mask & JOYFLAG_POVVALID) { + ji.dwPOV = ji.dwPOV & 0x0000ffff; + if (ji.dwPOV == JOY_POVCENTERED) + pos->pov[0] = JOYPOV_CENTER; + else + pos->pov[0] = (unsigned)(ji.dwPOV*JOYPOV_MAXVAL/35900.0f); + } + else { + pos->pov[0] = JOYPOV_CENTER; + } + + pos->buttons = 0; + if (joy_chpro_hack) { + if (ji.dwButtons > 0 && ji.dwButtons <=32) { + pos->buttons = 1<<(ji.dwButtons-1); + mprintf((0, "btns=%x\n", pos->buttons)); + } + } + else { + pos->buttons = (unsigned)ji.dwButtons; + } + + pos->btn = (unsigned)ji.dwButtonNumber; + +#ifdef _DEBUG + mprintf_at((4,handle+1,0, "%d:X:%04d Y:%04d Z:%04d %d:R:%04d U:%04d V:%04d", handle, pos->x,pos->y,pos->z,pos->r,pos->u,pos->v,pos->buttons)); +#endif +} + + +void joydi_get_pos(tJoystick handle, tJoyPos *pos) +{ + tJoyInfo *caps; + tJoystickRecord *joystick; + LPDIRECTINPUTDEVICE2 lpdidev2; + DIJOYSTATE ji; + HRESULT hr; + int try_count=0; + unsigned i; + + if (handle < 0 || handle >= WJD.njoy) return; + if (!WJD.joystick[handle].valid) return; + + joystick = &WJD.joystick[handle]; + caps = &joystick->caps; + lpdidev2 = ddio_ff_get_joystick_obj(joystick->joy_context.ffdev); + + if (!lpdidev2) { return; } + +retry_joy_input: + hr = lpdidev2->GetDeviceState(sizeof(ji), &ji); + if (hr == DIERR_NOTACQUIRED && try_count < 4) { + ddio_ff_Acquire(joystick->joy_context.ffdev); + try_count++; + goto retry_joy_input; + } + else if (hr != DI_OK) { + memset(pos, 0, sizeof(pos)); + pos->pov[0] = JOYPOV_CENTER; + } + +// interpret data. + if (caps->axes_mask & JOYFLAG_XVALID) { + pos->x = (int)((ji.lX<<8)/(caps->maxx - caps->minx)) - 128; + } + else { pos->x = 0; } + if (caps->axes_mask & JOYFLAG_YVALID) { + pos->y = (int)((ji.lY<<8)/(caps->maxy - caps->miny)) - 128; + } + else { pos->y = 0; } + if (caps->axes_mask & JOYFLAG_ZVALID) { + pos->z = (int)((ji.lZ<<8)/(caps->maxz - caps->minz)) - 128; + } + else { pos->z = 0; } + if (caps->axes_mask & JOYFLAG_RVALID) { + pos->r = (int)((ji.lRz<<8)/(caps->maxr - caps->minr)) - 128; + } + else { pos->r = 0; } + if (caps->axes_mask & JOYFLAG_UVALID) { + pos->u = (int)((ji.rglSlider[0]<<8)/(caps->maxu - caps->minu)) - 128; + } + else { pos->u = 0; } + if (caps->axes_mask & JOYFLAG_VVALID) { + pos->v = (int)((ji.rglSlider[1]<<8)/(caps->maxv - caps->minv)) - 128; + } + else { pos->v = 0; } + + for (i = 0; i < 4; i++) + { + if (caps->axes_mask & (JOYFLAG_POVVALID << i)) { + if (loword(ji.rgdwPOV[i]) == 0xffff) { + pos->pov[i] = JOYPOV_CENTER; + } + else { + int index = (ji.rgdwPOV[i]/4500); + pos->pov[i] = index * 0x20; + //@@ switch (index) + //@@ { + //@@ case 0: pos->pov = JOYPOV_UP; break; + //@@ case 1: pos->pov = JOYPOV_RIGHT; break; + //@@ case 2: pos->pov = JOYPOV_DOWN; break; + //@@ case 3: pos->pov = JOYPOV_LEFT; break; + //@@ default: pos->pov = JOYPOV_CENTER; + //@@ } + } + } + else { + pos->pov[i] = JOYPOV_CENTER; + } + } + + pos->buttons = 0; + for (i = 0; i < caps->num_btns; i++) + { + if (ji.rgbButtons[i] & 0x80) { + pos->buttons |= (1<btn = i; + } + } + +#ifdef _DEBUG + mprintf_at((4,(handle*2)+1,0, "J%d:X:%04d Y:%04d Z:%04d Rz:%04d S0:%04d S1:%04d", handle, pos->x,pos->y,pos->z,pos->r,pos->u,pos->v)); + mprintf_at((4,(handle*2)+2,0, " POV0:%04d POV1:%04d POV2:%04d POV3:%04d", handle, pos->pov[0], pos->pov[1], pos->pov[2], pos->pov[3])); +#endif +} + + +// direct input implementation. +tJoystick joydi_init_stick(int ffjoyid) +{ + LPDIRECTINPUTDEVICE2 lpdidev2; + + lpdidev2 = ddio_ff_get_joystick_obj((tDevice)ffjoyid); + if (lpdidev2) { + // find free joystick slot + int i; + + for (i = 0; i < WJD.njoy; i++) + { + if (!WJD.joystick[i].valid) { + // found slot, lets initialize it and return. + // get axis, and each of their ranges + DIDEVICEOBJECTINSTANCE DIObjInst; + DIDEVICEINSTANCE DIDevInst; + DIPROPRANGE dirange; + DIDEVCAPS dicaps; + tDevice dev = (tDevice)ffjoyid; + tJoyInfo *caps = &WJD.joystick[i].caps; + + memset(caps, 0, sizeof(tJoyInfo)); + + WJD.joystick[i].joy_context.ffdev = dev; + WJD.joystick[i].valid = 1; + + // get device info + DIDevInst.dwSize = sizeof(DIDEVICEINSTANCE); + if (lpdidev2->GetDeviceInfo(&DIDevInst) == DI_OK) { + strcpy(WJD.joystick[i].caps.name, DIDevInst.tszProductName); + } + else { + sprintf(WJD.joystick[i].caps.name, "Joystick-%d", i); + } + + // get device object info + ddio_ff_Acquire(dev); + dicaps.dwSize = sizeof(DIDEVCAPS); + + if (lpdidev2->GetCapabilities(&dicaps) != DI_OK) { + mprintf((0,"ddio_ffjoy_Query: Failed getting device caps\n")); + return -1; + } + + DIObjInst.dwSize = sizeof(DIDEVICEOBJECTINSTANCE); + + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_X, DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_XVALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_Y, DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_YVALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_Z, DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_ZVALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_RZ, DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_RVALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_SLIDER(0), DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_UVALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_SLIDER(1), DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_VVALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_POV(0), DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_POVVALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_POV(1), DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_POV2VALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_POV(2), DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_POV3VALID; + } + if (lpdidev2->GetObjectInfo(&DIObjInst, DIJOFS_POV(3), DIPH_BYOFFSET)==DI_OK) { + caps->axes_mask |= JOYFLAG_POV4VALID; + } + + caps->num_btns = dicaps.dwButtons; + + dirange.diph.dwSize = sizeof(DIPROPRANGE); + dirange.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dirange.diph.dwHow = DIPH_BYOFFSET; + + dirange.diph.dwObj = DIJOFS_X; + if (lpdidev2->GetProperty(DIPROP_RANGE, (DIPROPHEADER *)&dirange)==DI_OK) { + caps->minx = (int)dirange.lMin; + caps->maxx = (int)dirange.lMax; + } + dirange.diph.dwObj = DIJOFS_Y; + if (lpdidev2->GetProperty(DIPROP_RANGE, (DIPROPHEADER *)&dirange)==DI_OK) { + caps->miny = (int)dirange.lMin; + caps->maxy = (int)dirange.lMax; + } + dirange.diph.dwObj = DIJOFS_Z; + if (lpdidev2->GetProperty(DIPROP_RANGE, (DIPROPHEADER *)&dirange)==DI_OK) { + caps->minz = (int)dirange.lMin; + caps->maxz = (int)dirange.lMax; + } + dirange.diph.dwObj = DIJOFS_RZ; + if (lpdidev2->GetProperty(DIPROP_RANGE, (DIPROPHEADER *)&dirange)==DI_OK) { + caps->minr = (int)dirange.lMin; + caps->maxr = (int)dirange.lMax; + } + dirange.diph.dwObj = DIJOFS_SLIDER(0); + if (lpdidev2->GetProperty(DIPROP_RANGE, (DIPROPHEADER *)&dirange)==DI_OK) { + caps->minu = (int)dirange.lMin; + caps->maxu = (int)dirange.lMax; + } + dirange.diph.dwObj = DIJOFS_SLIDER(1); + if (lpdidev2->GetProperty(DIPROP_RANGE, (DIPROPHEADER *)&dirange)==DI_OK) { + caps->minv = (int)dirange.lMin; + caps->maxv = (int)dirange.lMax; + } + + lpdidev2->Poll(); + + return (tJoystick)(i); + } + else { + return (tJoystick)(-1); + } + } + } + + Int3(); + return -1; +} + + + + + +// JAS: Taken from Nov 1996 Game Developer, page 49. +// Damn, that is some UGLY code! (But it works...) +/*---------------------------------------------------------------------*/ +bool joyw_get_name(int joy_num, char *szReturnName, const char *szRegKey) +/* + Description : Opens the MediaResources\Joysitick\mjstick.drv\JoystickSettings and + extracts Joystick%dOEMName string + Arguments : joy_num (r/o) - Joystick Number + szRegKey (r/o) - Registry Key of the msjstick.drv + ReturnName (r/w) - Return String for name listed in Control Panel + Returns : 0 for success 1 for failure +/*-----------------------------------------------------------------------*/ +{ + BYTE KeyStr[MAX_PATH] = REGSTR_PATH_JOYCONFIG; // found in regstr.h + BYTE KeyJoySetStr[MAX_PATH] = REGSTR_KEY_JOYSETTINGS; // found in Regstr.h + BYTE szOEMName[MAX_PATH]; // OEM name from Current Settings + HKEY ConfigKey; + HKEY JoyConfigKey; // Joystick Configuration + HKEY DriverKey; // Joystick Driver Key + HKEY OEMPropKey; + HKEY PropKey; + DWORD Length; + if( ERROR_SUCCESS != RegOpenKey( HKEY_LOCAL_MACHINE,REGSTR_PATH_JOYCONFIG,&ConfigKey ) ) + + { + return( false ); // It should never get here key received from Caps + } + + if( ERROR_SUCCESS != RegOpenKey( ConfigKey, szRegKey, &DriverKey ) ) + { + return( false ); // It should never get here key received from Caps + } + // Open CurrentSettings Key + + if( ERROR_SUCCESS != RegOpenKey( DriverKey, REGSTR_KEY_JOYCURR, &JoyConfigKey ) ) + { + return( false ); // It should never get here always a Current Settings + } + sprintf((char *)KeyStr,(char *)REGSTR_VAL_JOYNOEMNAME,joy_num+1); + Length=sizeof(szOEMName); // Get OEMNAME Configuration + + if( ERROR_SUCCESS != RegQueryValueEx( JoyConfigKey,(char *)KeyStr,NULL,NULL,(unsigned char *)&szOEMName,&Length)) + { + return( false ); // No OEM name listed return error + } + RegCloseKey( ConfigKey ); // Closes the registry Key + + // Open OEM Properties Key + if( ERROR_SUCCESS != RegOpenKey(HKEY_LOCAL_MACHINE,REGSTR_PATH_JOYOEM,&PropKey ) ) + { + return( false ); // It should never get here key received from Caps + } + + if( ERROR_SUCCESS != RegOpenKey( PropKey, (char *)szOEMName, &OEMPropKey ) ) + { + return( false ); // It should never get here if device is selected + } + Length=MAX_PATH; // Get Name as listed in Control Panel + + if( ERROR_SUCCESS != RegQueryValueEx( OEMPropKey,REGSTR_VAL_JOYOEMNAME,NULL,NULL,(unsigned char *)szReturnName,&Length)) + { + return( false ); // No OEM name listed return error + } + RegCloseKey( OEMPropKey ); // Closes the registry Key + return true; + +} /* End GetJoystickName */ diff --git a/ddio_win/winkey.cpp b/ddio_win/winkey.cpp new file mode 100644 index 000000000..da34c7a0b --- /dev/null +++ b/ddio_win/winkey.cpp @@ -0,0 +1,839 @@ +/* +* $Logfile: /DescentIII/Main/ddio_win/winkey.cpp $ +* $Revision: 36 $ +* $Date: 4/24/99 5:41p $ +* $Author: Samir $ +* +* Keyboard IO with DirectInput 3.0 +* +* $Log: /DescentIII/Main/ddio_win/winkey.cpp $ + * + * 36 4/24/99 5:41p Samir + * moved key to ascii, ascii to key to the ddio_common library. + * + * 35 4/22/99 2:02a Jeff + * pass ddio_init_info through to keyboard handlers + * + * 34 3/05/99 3:27p Samir + * screenshot key works properly now. + * + * 33 3/02/99 2:01p Samir + * some small directinput changes. (note, for D3, we're now using emulated + * key input because of the silly pause and numlock issue which for some + * reason fails to work properly under DirectInput. + * + * 32 3/01/99 12:39a Samir + * key ups only occur when key was initially down. because of this, + * ddio_KeyFlush cleared the down events, but if the user released after + * flush, the up event would leak through and cause the illusion of key + * presses leaking through. HOPEFULLY this doesn't break anything else, + * but I played through the game a bit and it seems to work fine. + * + * 31 2/25/99 10:43p Matt + * Took out mprintf() + * + * 30 2/21/99 6:38p Samir + * mouse and key input better. buffered mouse. + * + * 29 2/05/99 1:16p Samir + * reset low level keys when flushing keyboard in the high level. added a + * function to the low level to reset the status of a key, called from + * high level key flush. + * + * 28 2/04/99 12:31p Samir + * use our timer for key capture if we're using the hook method. + * + * 27 1/28/99 6:22p Samir + * may fix thread issues. + * + * 26 1/25/99 7:27p Samir + * fixed timing issues with emulated keyboard key down times. (Win32 sends + * multiple keydown messages, so ignore them.) + * + * 25 1/25/99 6:47p Samir + * allow slow keyboard + * + * 24 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 23 10/22/98 11:06a Samir + * added numeric keypad ascii translations. + * + * 22 10/21/98 12:02p Samir + * properly update odd keys when they are released throug + * ddio_KeyGetDownTime. + * + * 21 10/16/98 12:15p Samir + * took out ddio_KeyFrame + * + * 20 10/15/98 6:48p Samir + * added timer hooks. + * + * 19 9/17/98 12:50p Samir + * language. + * + * 18 6/29/98 6:43p Samir + * Took out GetMsgProc and legacy keyboard variables. + * + * 17 4/09/98 6:56p Samir + * PRINTSCREEN MUST WORK. + * + * 16 4/08/98 8:40p Samir + * Screen shots work with print screen by checking VK code. + * + * 15 3/31/98 12:46p Samir + * keyboard IO system better. uses getmsgproc windows hook. + * + * 14 3/24/98 11:21a Samir + * redid key handler. + * + * 13 2/25/98 6:11p Samir + * Added functions to better deal with key flushing. + * + * 12 1/21/98 6:46p Samir + * NT and 95 should have same keyboard behavior for framed handlers. + * + * 11 1/02/98 12:52p Samir + * Added ascii->keycode translation tables + * + * 10 12/10/97 1:12p Samir + * Use timestamp from DirectInput Key calls. + * + * 9 11/07/97 6:17p Samir + * Rollbacked some more efficient code since it slowed down some machines + * in the editor. Sleep thread until closed a little faster. + * + * 8 10/23/97 2:59p Samir + * Keyboard thread uses C runtime lib functions, and definitely ends. + * + * 7 10/22/97 4:37p Samir + * Thread doesn't end if thread is still blocking, I think. + * + * 6 10/17/97 5:03p Samir + * Default to preemptive keyboard handler (not under NT). + * + * 5 10/16/97 5:35p Samir + * Use different cooperative level for Win95 vs. NT. + * + * 4 10/16/97 2:29p Samir + * Changed DirectInput Keyboard to FOREGROUND + * + * 3 8/01/97 8:14p Samir + * Fixed keyboard handler for NT to work with extended keys. + * + * 2 8/01/97 7:30p Samir + * Better NT keyboard support. + * + * 14 5/23/97 4:09p Samir + * Keyboard thread uses new task system. + * + * 13 5/09/97 6:45p Samir + * Took out shared keyboard code from handler to ddio_common + * + * 12 5/08/97 1:56p Samir + * Moved a bunch of keyboard code to ddio_common library. + * + * 11 3/20/97 11:08a Samir + * Added function to peek for keys in queue without removing them. + * + * 10 3/13/97 3:02p Samir + * Hopefully fixed keyboard thread problem. + * + * 9 2/20/97 9:58a Matt + * Took out Int3() for directinput key buffer overflow, which would get + * hit if you switched to another task, typed a bunch, and switched back + * into the editor + * + * 8 1/23/97 2:22p Samir + * Keyboard thread now blocks properly, and added functionality for + * nonpreemptive keyboard polling. + * + * 7 1/20/97 3:46p Samir + * RCS header check in +* +* $NoKeywords: $ +*/ + + + +// ---------------------------------------------------------------------------- +// Keyboard Interface +// ---------------------------------------------------------------------------- +#include "DDAccess.h" + +#include "pserror.h" +#include "mono.h" +#include "ddio.h" +#include "ddio_win.h" +#include "Application.h" +#include "TaskSystem.h" + +#include +#include + + +// ---------------------------------------------------------------------------- +// Local global data +// ---------------------------------------------------------------------------- +#define DIKEY_BUFFER_SIZE 32 + +static struct tWinKeyData +{ + LPDIRECTINPUTDEVICE lpdikey; // key device + HANDLE evtnotify; // notify event + HHOOK winhook; // windows hook + unsigned long thread; // thread id +// osMutex keyframe_mutex; // mutex between internal key frame and key thread. + bool nextframe; // used for mutexing between keyframe and thread. + bool acquired; // device acquired? + bool thread_active; // used in thread. + bool suspended; +} +WKD = { + NULL, + NULL, + NULL, + 0xffffffff, + false, + false, + false, + true +}; + +volatile struct tWinKeys +{ + union { + DWORD up_ticks; // windows ticks when key went up last + float up_time; + }; + union { + DWORD down_ticks; // windows ticks when key went down last + float down_time; + }; + bool status; // is it down? + bool mutex_flag; // done for mutexing between ddio_Internal and KeyThread + ushort mutex_data; +} +WKeys[DDIO_MAX_KEYS]; + + +static int DDIO_key_language = KBLANG_AMERICAN; + + +/////////////////////////////////////////////////////////////////////////////// +// Initializes DirectInputDevice keyboard if we are under Win9x or at least NT5 +// we set the cooperative level, etc. +LPDIRECTINPUTDEVICE dikey_Init(LPDIRECTINPUT lpdi, HWND hwnd); + +// Shutsdown DirectInputDevice keyboard if passed device is valid. +void dikey_Shutdown(LPDIRECTINPUTDEVICE lpdikey); + +// sets up preemptive keyboard handling. +HANDLE dikey_EnableNotify(LPDIRECTINPUTDEVICE lpdikey); + +// disables event based notification. +bool dikey_DisableNotify(LPDIRECTINPUTDEVICE lpdikey, HANDLE evthandle); + +// acquires or unacquires device +// returns device acquisition state. +bool dikey_Acquire(LPDIRECTINPUTDEVICE lpdikey, bool acquire); + +// emulated keyboard functionality +bool ddio_Win_KeyInit(); +void ddio_Win_KeyClose(); + +int ddio_KeyHandler(HWnd wnd, unsigned msg, unsigned wParam, long lParam); + +void CALLBACK key_TimerProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); +LRESULT CALLBACK KeyboardProc( int code, WPARAM wParam, LPARAM lParam); + +// DirectInput Keyboard Thread +void __cdecl dikey_Thread(void *dp); + +// translates scan code to foreign equivs. +ubyte xlate_scancode(ubyte scan_code); + + +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// Initialization of keyboard device. +// ---------------------------------------------------------------------------- + +// this version will try to initialize a direct input keyboard device. if it fails +// it falls back to the old keyboard hook (less reliable but works.) +bool ddio_InternalKeyInit(ddio_init_info *init_info) +{ + bool emulated = init_info->key_emulation; + + LPDIRECTINPUTDEVICE lpdikey = NULL; + int i; + +// reset key list + for (i = 0; i < DDIO_MAX_KEYS; i++) + { + WKeys[i].down_ticks = 0; + WKeys[i].up_ticks = 0; + WKeys[i].status = false; + } + +// start init. + if (!emulated) { + lpdikey = dikey_Init(DInputData.lpdi, (HWND)DInputData.app->m_hWnd); + } + +retry_key_init: + if (lpdikey) { + // direct input keyboard can work, so we initialize preemptive handling of the keyboard + HANDLE hevt = dikey_EnableNotify(lpdikey); + + if (hevt) { + // event handling will work, so let's create the keyboard thread. + bool acquired = dikey_Acquire(lpdikey, true); + if (acquired) { + // create keyboard thread + unsigned long thrid; + WKD.thread_active = true; + WKD.lpdikey = lpdikey; + WKD.thread = 0; + WKD.evtnotify = hevt; + WKD.acquired = acquired; + WKD.winhook = NULL; + WKD.suspended = false; + WKD.nextframe = false; + thrid = _beginthread(dikey_Thread, 0, NULL); + if (thrid == (unsigned long)(-1)) { + mprintf((0, "DDIO: DI_Keyboard thread failed to initialize.\n")); + WKD.thread_active = false; + WKD.lpdikey = NULL; + WKD.thread = 0xffffffff; + WKD.evtnotify = NULL; + WKD.acquired = false; + WKD.winhook = NULL; + WKD.suspended = false; + acquired = dikey_Acquire(lpdikey, false); + dikey_DisableNotify(lpdikey, hevt); + dikey_Shutdown(lpdikey); + lpdikey = NULL; + } + else { + // success! init data. + // set priority of thread too. + WKD.thread = thrid; + if (SetThreadPriority((HANDLE)thrid, THREAD_PRIORITY_TIME_CRITICAL) == FALSE) { + SetThreadPriority((HANDLE)thrid, THREAD_PRIORITY_HIGHEST); + } + mprintf((0, "DDIO: DI_Keyboard initialized.\n")); + } + } + else { + // failed to acquire device? can't do. + dikey_DisableNotify(lpdikey, hevt); + dikey_Shutdown(lpdikey); + lpdikey = NULL; + } + } + else { + // somehow event notification failed, can't do. + dikey_Shutdown(lpdikey); + lpdikey = NULL; + } + + if (lpdikey == NULL) { + goto retry_key_init; + } + } + else { + // here, initialize hook procedure. + return ddio_Win_KeyInit(); + } + + return true; +} + + +// this will shutdown direct input or the windows hook, whichever was chosen. +void ddio_InternalKeyClose() +{ + if (WKD.lpdikey) { + // deactivate thread and free it, then unacquire device, disable event notification, and shutdown! + WKD.thread_active = false; + SetEvent(WKD.evtnotify); + Sleep(500); + WKD.acquired = dikey_Acquire(WKD.lpdikey, false); + dikey_DisableNotify(WKD.lpdikey, WKD.evtnotify); + dikey_Shutdown(WKD.lpdikey); + WKD.thread = 0xffffffff; + WKD.evtnotify = NULL; + WKD.lpdikey = NULL; + mprintf((0, "DDIO: DI_Keyboard shutdown.\n")); + } + if (WKD.winhook) { + ddio_Win_KeyClose(); + } +} + + +// handled internally if keyboard system needs additional processing per frame +void ddio_InternalKeyFrame() +{ +} + + +////////////////////////////////////////////////////////////////////////////// +// Miscellaneous API +////////////////////////////////////////////////////////////////////////////// + + +// returns if key is up or down +bool ddio_InternalKeyState(ubyte key) +{ + return WKeys[key].status; +} + + +float ddio_InternalKeyDownTime(ubyte key) +{ + float down_time = 0.0f; + if (WKeys[key].status) { + if (WKD.winhook) { + float timer = timer_GetTime(); + down_time = timer - WKeys[key].down_time; + WKeys[key].down_time = timer; + } + else { + DWORD curtickcount = GetTickCount(); + DWORD ticks = curtickcount - WKeys[key].down_ticks; + if (ticks == 0) { + //mprintf((0, "ticks=%d\n", ticks)); + } + WKeys[key].down_ticks = curtickcount; + down_time = (ticks/1000.0f); + } + } + else { + if (WKD.winhook) { + down_time = WKeys[key].up_time - WKeys[key].down_time; + WKeys[key].down_time = WKeys[key].up_time = 0.0f; + } + else { + DWORD ticks = WKeys[key].up_ticks - WKeys[key].down_ticks; + WKeys[key].down_ticks = 0; + WKeys[key].up_ticks = 0; + down_time = (ticks/1000.0f); + } + } + + return down_time; +} + + +// flush a key internally +void ddio_InternalResetKey(ubyte key) +{ + WKeys[key].down_ticks = 0; + WKeys[key].up_ticks = 0; + WKeys[key].status = false; + WKeys[key].mutex_flag = false; + WKeys[key].mutex_data = 0; +} + + +// sets type of keyboard to emulate +// #define KBLANG_AMERICAN 0 +// #define KBLANG_BRITISH 1 +// #define KBLANG_FRENCH 2 +// #define KBLANG_GERMAN 3 + +void ddio_SetKeyboardLanguage(int language) +{ + DDIO_key_language = language; +} + + +// translates scan code to foreign equivs. +ubyte xlate_scancode(ubyte scan_code) +{ + ubyte code = scan_code; + + if (DDIO_key_language == KBLANG_FRENCH) { + switch (scan_code) { + case KEY_A: code = KEY_Q; break; + case KEY_M: code = KEY_COMMA; break; + case KEY_Q: code = KEY_A; break; + case KEY_W: code = KEY_Z; break; + case KEY_Z: code = KEY_W; break; + case KEY_SEMICOL: code = KEY_M; break; + case KEY_COMMA: code = KEY_SEMICOL; break; + } + } + else if (DDIO_key_language == KBLANG_GERMAN) { + switch (scan_code) { + case KEY_Y: code = KEY_Z; break; + case KEY_Z: code = KEY_Y; break; + } + } + else if (DDIO_key_language == KBLANG_BRITISH) { + if ( scan_code == KEY_BSLASH_UK ) { // KEY_SLASH_UK == 0x56 + code = KEY_SLASH; // KEY_SLASH is really the backslash, 0x2B + } + } + + return code; +} + + + +////////////////////////////////////////////////////////////////////////////// +// DirectInput Functions +////////////////////////////////////////////////////////////////////////////// + +inline bool IS_NT4_OR_LOWER() +{ + int maj,min; + tWin32OS os = oeWin32Application::version(&maj,&min); + return (os == WinNT && maj < 5) ? true : false; +} + + +// Initializes DirectInputDevice keyboard if we are under Win9x or at least NT5 +// we set the cooperative level, etc. +LPDIRECTINPUTDEVICE dikey_Init(LPDIRECTINPUT lpdi, HWND hwnd) +{ + LPDIRECTINPUTDEVICE lpdikey; + HRESULT hr; + + if (IS_NT4_OR_LOWER()) { + return NULL; + } + +// see if we can get the keyboard device. + hr = lpdi->CreateDevice(GUID_SysKeyboard, &lpdikey, NULL); + if (hr != DI_OK) { + DDIO_MESSAGE((hr, "DI_Keyboard initialization failed.")); + return NULL; + } + + hr = lpdikey->SetDataFormat(&c_dfDIKeyboard); + if (hr != DI_OK) { + DDIO_MESSAGE((hr, "DI_Keyboard data format specification failed.")); + lpdikey->Release(); + return NULL; + } + + hr = lpdikey->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + if (hr != DI_OK) { + DDIO_MESSAGE((hr, "DI_Keyboard set cooperative level failed.")); + lpdikey->Release(); + return NULL; + } + + return lpdikey; +} + + +// Shutsdown DirectInputDevice keyboard if passed device is valid. +void dikey_Shutdown(LPDIRECTINPUTDEVICE lpdikey) +{ + if (lpdikey) { + lpdikey->Release(); + } +} + + +// sets up preemptive keyboard handling. +HANDLE dikey_EnableNotify(LPDIRECTINPUTDEVICE lpdikey) +{ + if (lpdikey) { + HANDLE hEvt = CreateEvent(NULL, TRUE, FALSE, "DDIOKeyEvent"); + HRESULT hr; + DIPROPDWORD dipropdw = { + { + sizeof(DIPROPDWORD), + sizeof(DIPROPHEADER), + 0, + DIPH_DEVICE, + }, + DIKEY_BUFFER_SIZE, + }; + + if (!hEvt) { + DDIO_MESSAGE((0, "DI_Keyboard failed to init system event.")); + return NULL; + } + + // set event + hr = lpdikey->SetEventNotification(hEvt); + if (hr != DI_OK) { + DDIO_MESSAGE((hr, "DI_Keyboard failed to set preemptive key event notification.")); + CloseHandle(hEvt); + return NULL; + } + + // set key buffer size + hr = lpdikey->SetProperty(DIPROP_BUFFERSIZE, &dipropdw.diph); + if (FAILED(hr)) { + DDIO_MESSAGE((hr, "DI_keyboard buffering failed.")); + lpdikey->SetEventNotification(NULL); + CloseHandle(hEvt); + return NULL; + } + + return hEvt; + } + + return NULL; +} + + +// disables event based notification. +bool dikey_DisableNotify(LPDIRECTINPUTDEVICE lpdikey, HANDLE evthandle) +{ + if (lpdikey) { + lpdikey->SetEventNotification(NULL); + CloseHandle(evthandle); + return true; + } + + return false; +} + + +// acquires or unacquires device +// returns device acquisition state. +bool dikey_Acquire(LPDIRECTINPUTDEVICE lpdikey, bool acquire) +{ + HRESULT hr = acquire ? lpdikey->Acquire() : lpdikey->Unacquire(); + + if (FAILED(hr)) { + DDIO_MESSAGE((hr, "DI_keyboard acquire/unacquire fail.")); + return !acquire; + } + + return acquire; +} + + +// DirectInput Keyboard Thread +void __cdecl dikey_Thread(void *dp) +{ + unsigned event_count = 0; + + GetAsyncKeyState(VK_PAUSE); // this will tell if the 'pause' key is toggled + + while (WKD.thread_active) + { + // this thread will hold until the direct input key event has been signaled by the OS. + // after this, we will get all the keys in the io buffer and register them with the + // key system. + + switch (WaitForSingleObject(WKD.evtnotify, INFINITE)) + { + case WAIT_TIMEOUT: // this shouldn't happen, but if it does, no big deal. + break; + case WAIT_ABANDONED: // usually means calling thread quit. + WKD.thread_active = false; + break; + case WAIT_OBJECT_0: // event was signalled normally + { + DIDEVICEOBJECTDATA diobjdata[DIKEY_BUFFER_SIZE]; + DWORD diobjitems = DIKEY_BUFFER_SIZE; + HRESULT hr; + // SHORT async_key_state; + ubyte key; + + int i; + + // don't read if suspended! + if (WKD.suspended) { + break; + } + + event_count++; + + // attempt acquisition if keyboard not already acquired + if (!WKD.acquired) { + WKD.acquired = dikey_Acquire(WKD.lpdikey, true); + } + if (WKD.acquired) { + hr = WKD.lpdikey->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diobjdata, &diobjitems, 0); + if (SUCCEEDED(hr)) { + // the pause is key under Windows seems to act kinda strange. it's a toggle button + // and USUALLY is preceeded by a KEY_LCTRL press. If a pause toggle was indicated, flush the + // keyboard and quit. + + // note that dwOfs is the acutal key that is either down or up, and dwData tells us if + // it is up or down. So place on key array, and in queue. + for (i =0; i < (int)diobjitems; i++) + { + key = xlate_scancode((ubyte)(diobjdata[i].dwOfs)); + if (diobjdata[i].dwData & 0x80) { + if (WKeys[key].status) { + WKeys[key].up_ticks = 0; + } + WKeys[key].down_ticks = diobjdata[i].dwTimeStamp; + WKeys[key].status = true; + if (key == KEY_LCTRL) { + WKeys[key].mutex_flag = true; + } + mprintf((0, "dkey=%x\n", key)); + } + else { + if (WKeys[key].status) { + WKeys[key].up_ticks = diobjdata[i].dwTimeStamp; + WKeys[key].status = false; + mprintf((0, "ukey=%x\n", key)); + } + } + + ddio_UpdateKeyState(key,WKeys[key].status); + } + } + else { + if (hr == DIERR_INPUTLOST) { + WKD.acquired = dikey_Acquire(WKD.lpdikey, true); + } + else { + DDIO_MESSAGE((hr, "DI_keyboard unable to read.")); + } + } + } + } + break; + } + + ResetEvent(WKD.evtnotify); + } + + DDIO_MESSAGE((0, "DI_Keyboard thread ended.")); +} + + +void ddio_InternalKeySuspend() +{ + if (WKD.lpdikey && WKD.acquired) { + WKD.acquired = dikey_Acquire(WKD.lpdikey, false); + } + WKD.suspended = true; +} + + +void ddio_InternalKeyResume() +{ + if (WKD.lpdikey && !WKD.acquired) { + WKD.acquired = dikey_Acquire(WKD.lpdikey, true); + } + WKD.suspended = false; +} + + + + +// Win32 non-threaded version + +bool ddio_Win_KeyInit() +{ +/* Initialize hook handlers */ + WKD.winhook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, (HINSTANCE)DInputData.app->m_hInstance, GetCurrentThreadId()); + if (!WKD.winhook) { + return false; + } + + mprintf((0, "Keyboard initialized.\n")); + + return true; +} + + +void ddio_Win_KeyClose() +{ +/* Free up message handlers */ + if (WKD.winhook) { + UnhookWindowsHookEx(WKD.winhook); + WKD.winhook = NULL; + } + + mprintf((0, "Keyboard released.\n")); +} + + +// ---------------------------------------------------------------------------- +// non DirectInput keyboard handler +// ---------------------------------------------------------------------------- + + +LRESULT CALLBACK KeyboardProc( int code, WPARAM wParam, LPARAM lParam) +{ + int res; + + if (code < 0) { + return CallNextHookEx(WKD.winhook, code, wParam, lParam); + } + + if (lParam&0x80000000) + res = ddio_KeyHandler( 0, WM_KEYUP, wParam, lParam); + else + res = ddio_KeyHandler( 0, WM_KEYDOWN, wParam, lParam); + + return (!res); +} + + +int ddio_KeyHandler(HWnd wnd, unsigned msg, unsigned wParam, long lParam) +{ + ubyte scan_code; + float timer = timer_GetTime(); + + if (!WKD.winhook) + return 1; + + switch ((UINT)msg) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + scan_code = (unsigned char )((lParam>>16) & 0xff); + if (lParam & 0x01000000) + scan_code |= 0x80; + + scan_code = xlate_scancode(scan_code); + + // print screen is a weird case. only accept key ups. + if (wParam != VK_SNAPSHOT) { + if (!WKeys[scan_code].status) { + WKeys[scan_code].up_time = 0; + WKeys[scan_code].down_time = timer; + } + else { + WKeys[scan_code].up_time = 0; + } + WKeys[scan_code].status = true; + ddio_UpdateKeyState(scan_code, true); + } + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + scan_code = (unsigned char )((lParam>>16) & 0xff); + if (lParam & 0x01000000) + scan_code |= 0x80; + + scan_code = xlate_scancode(scan_code); + + // handle special keys. print screen, we will simulate the keypress. + if (wParam == VK_SNAPSHOT) { + scan_code = KEY_PRINT_SCREEN; + WKeys[scan_code].down_time = timer; + WKeys[scan_code].status = true; + ddio_UpdateKeyState(scan_code, true); + } + if (WKeys[scan_code].status) { + WKeys[scan_code].status = false; + WKeys[scan_code].up_time = timer; + ddio_UpdateKeyState(scan_code, false); + } + + break; + } + + return 1; +} diff --git a/ddio_win/winmouse.cpp b/ddio_win/winmouse.cpp new file mode 100644 index 000000000..e12cc9476 --- /dev/null +++ b/ddio_win/winmouse.cpp @@ -0,0 +1,900 @@ +// ---------------------------------------------------------------------------- +// Mouse Interface +// ---------------------------------------------------------------------------- + +#include + +#include "DDAccess.h" +#include "pserror.h" +#include "mono.h" +#include "ddio.h" +#include "ddio_win.h" +#include "Application.h" +#include "psclass.h" +#include "Macros.h" + +typedef struct t_mse_button_info +{ + bool is_down[N_MSEBTNS]; + ubyte down_count[N_MSEBTNS]; + ubyte up_count[N_MSEBTNS]; + DWORD time_down[N_MSEBTNS]; // in milliseconds windows ticks + DWORD time_up[N_MSEBTNS]; +} +t_mse_button_info; + +typedef struct t_mse_event +{ + short btn; + short state; +} +t_mse_event; + +#define MOUSE_ZMIN 0 // mouse wheel z min and max (increments of 120 = 10 units) +#define MOUSE_ZMAX 1200 +#define N_DIMSEBTNS 4 // # of REAL mouse buttons +#define MSEBTN_WHL_UP (N_DIMSEBTNS) // button index for mouse wheel up +#define MSEBTN_WHL_DOWN (N_DIMSEBTNS+1) // button index for mouse wheel down + +// taken from winuser.h +#ifndef WHEEL_DELTA +#define WHEEL_DELTA 120 +#endif +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x20a +#endif + + +// ---------------------------------------------------------------------------- + +static bool DDIO_mouse_init = 0; + +static struct mses_state +{ + LPDIRECTINPUTDEVICE lpdimse; + RECT brect; // limit rectangle of absolute mouse coords + short x, y, z; // current x,y,z in absolute mouse coords + short cx, cy, cz; // prior values of x,y,z from last mouse frame + short zmin, zmax; // 3 dimensional mouse devices use this + int btn_mask, btn_flags; // btn_flags are the avaiable buttons on this device in mask form. + bool emulated; // are we emulating direct input? + bool acquired; + bool suspended; + sbyte cursor_count; + float x_aspect, y_aspect; // used in calculating coordinates returned from ddio_MouseGetState + HANDLE hmseevt; // signaled if mouse input is awaiting. + short dx, dy, dz, imm_dz; + short mode; // mode of mouse operation. + short nbtns, naxis; // device caps. +} DDIO_mouse_state; + + + +static t_mse_button_info DIM_buttons; +static tQueue MB_queue; + +// ---------------------------------------------------------------------------- + +// di_mouse initialization. +LPDIRECTINPUTDEVICE dimouse_Init(LPDIRECTINPUT lpdi, HWND hwnd); + +// di_mouse close +void dimouse_Shutdown(LPDIRECTINPUTDEVICE lpdimse); + +// di_mouse acquire device, returns acquisition state of device +bool dimouse_Acquire(LPDIRECTINPUTDEVICE lpdimse, bool acquire); + +// returns device data. +void dimouse_GetDeviceData(LPDIRECTINPUTDEVICE lpdimse); + +// emulated get device state +void winmouse_GetDeviceData(POINT &pt); + + +inline void DDIOShowCursor(BOOL show) +{ + if (show) + { + if (DDIO_mouse_state.cursor_count == -1) + { + ShowCursor(TRUE); + } + DDIO_mouse_state.cursor_count = 0; + } + else + { + if (DDIO_mouse_state.cursor_count == 0) + { + ShowCursor(FALSE); + } + DDIO_mouse_state.cursor_count = -1; + } +} + +// --------------------------------------------------------------------------- +// Initialization Functions +extern bool w32_mouseman_hack; + + +bool ddio_MouseInit() +{ + tWin32OS os; + int major, minor; + + // see if we need to emulate directinput. + os = oeWin32Application::version(&major, &minor); + DDIO_mouse_state.emulated = (os == WinNT && major < 5) ? true : false; + DDIO_mouse_state.lpdimse = NULL; + if (w32_mouseman_hack) + { + DDIO_mouse_state.emulated = true; + } + + if (!DDIO_mouse_state.emulated) + { + // initialize direct input version? attempt acquisition if so + DDIO_mouse_state.lpdimse = dimouse_Init(DInputData.lpdi, (HWND)DInputData.app->m_hWnd); + if (DDIO_mouse_state.lpdimse) + { + DDIO_mouse_state.acquired = dimouse_Acquire(DDIO_mouse_state.lpdimse, true); + } + } + else { + DDIO_mouse_state.nbtns = 3; + DDIO_mouse_state.naxis = 2; + DDIO_mouse_state.btn_flags = (MOUSE_LB+MOUSE_RB+MOUSE_CB); + if (DDIO_mouse_state.naxis >= 3 || w32_mouseman_hack) + { + DDIO_mouse_state.zmin = MOUSE_ZMIN; + DDIO_mouse_state.zmax = MOUSE_ZMAX; + DDIO_mouse_state.nbtns = N_DIMSEBTNS+2; // 2 next button slots reserved for mouse wheel. + DDIO_mouse_state.btn_flags |= (1<= 0) // hide cursor until truly hidden. + { + DDIO_mouse_state.cursor_count = ShowCursor(FALSE); + } + + ddio_MouseMode(MOUSE_STANDARD_MODE); + + DDIO_mouse_state.suspended = false; + ddio_MouseReset(); + DDIO_mouse_init = true; + + return true; +} + + +// here we deinitialize our Mouse from DirectInput. +void ddio_MouseClose() +{ + if (!DDIO_mouse_init) + return; + + // direct input shutdown. + if (DDIO_mouse_state.emulated == false) + { + dimouse_Shutdown(DDIO_mouse_state.lpdimse); + } + + DDIO_mouse_init = false; +} + + +// get device caps +int ddio_MouseGetCaps(int *btn, int *axis) +{ + *btn = (int)DDIO_mouse_state.nbtns; + *axis = (int)DDIO_mouse_state.naxis; + + return DDIO_mouse_state.btn_flags; +} + + +void ddio_MouseMode(int mode) +{ + if (mode == MOUSE_EXCLUSIVE_MODE) + { + DDIOShowCursor(FALSE); + } + else if (mode == MOUSE_STANDARD_MODE) + { + DDIOShowCursor(TRUE); + } + else + { + Int3(); + return; + } + + DDIO_mouse_state.mode = mode; +} + + +// ddio_MouseReset +// resets the mouse system, resetting boundaries to application window size, and mouse pointer +// to the center of the screen. +void ddio_MouseQueueFlush() +{ + ZeroMemory(&DIM_buttons, sizeof(DIM_buttons)); + MB_queue.flush(); +} + + +void ddio_MouseReset() +{ + tWin32AppInfo appi; + + DInputData.app->get_info(&appi); + DDIO_mouse_state.cx = appi.wnd_x + (appi.wnd_w/2); + DDIO_mouse_state.cy = appi.wnd_y + (appi.wnd_h/2); + + SetRect(&DDIO_mouse_state.brect, 0,0,GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + DDIO_mouse_state.zmin = MOUSE_ZMIN; + DDIO_mouse_state.zmax = MOUSE_ZMAX; + +// set up new coordinates for mouse pointer. + DDIO_mouse_state.btn_mask = 0; + DDIO_mouse_state.dx = 0; + DDIO_mouse_state.dy = 0; + DDIO_mouse_state.dz = 0; + DDIO_mouse_state.imm_dz = 0; + DDIO_mouse_state.x = (short)(DDIO_mouse_state.brect.right-DDIO_mouse_state.brect.left)/2; + DDIO_mouse_state.y = (short)(DDIO_mouse_state.brect.bottom-DDIO_mouse_state.brect.top)/2; + DDIO_mouse_state.z = (DDIO_mouse_state.zmax = DDIO_mouse_state.zmin)/2; + DDIO_mouse_state.cz = 0; + DDIO_mouse_state.x_aspect = 1.0f; + DDIO_mouse_state.y_aspect = 1.0f; + + // reset button states + ddio_MouseQueueFlush(); +} + + +// gets limits on the position of the mouse cursor (or atleast what's returned from GetState) +void ddio_MouseGetLimits(int *left, int *top, int *right, int *bottom, int *zmin, int *zmax) +{ + *left = DDIO_mouse_state.brect.left; + *top = DDIO_mouse_state.brect.top; + *right = DDIO_mouse_state.brect.right; + *bottom = DDIO_mouse_state.brect.bottom; + + if (zmin) + *zmin = DDIO_mouse_state.zmin; + if (zmax) + *zmax = DDIO_mouse_state.zmax; +} + + +// sets limits on the position of the mouse cursor (or atleast what's returned from GetState) +void ddio_MouseSetLimits(int left, int top, int right, int bottom, int zmin, int zmax) +{ + bool zaxis = (DDIO_mouse_state.naxis >= 3); + SetRect(&DDIO_mouse_state.brect, left, top, right, bottom); + DDIO_mouse_state.zmin = (!zmin && zaxis) ? MOUSE_ZMIN : zmin; + DDIO_mouse_state.zmax = (!zmax && zaxis) ? MOUSE_ZMAX : zmax; + DDIO_mouse_state.cx = left+ (right-left)/2; + DDIO_mouse_state.cy = top +(bottom-top)/2; +} + + +// virtual coordinate system for mouse (match to video resolution set for optimal mouse usage. +void ddio_MouseSetVCoords(int width, int height) +{ + ddio_MouseSetLimits(0,0,width,height); +} + +/* x, y = absolute mouse position + dx, dy = mouse deltas since last call + return value is mouse button mask +*/ +int ddio_MouseGetState(int *x, int *y, int *dx, int *dy, int *z, int *dz) +{ + int btn_mask = DDIO_mouse_state.btn_mask; + +// get return values. + if (x) *x = DDIO_mouse_state.x; + if (y) *y = DDIO_mouse_state.y; + if (z) *z = DDIO_mouse_state.z; + if (dx) *dx = DDIO_mouse_state.dx; + if (dy) *dy = DDIO_mouse_state.dy; + if (dz) *dz = DDIO_mouse_state.dz; + +#ifdef _DEBUG + mprintf_at((4,0,0, "MSE:X:%04d Y:%04d dX:%04d dY:%04d WM_MOUSEWHEEL:%04d", DDIO_mouse_state.x, DDIO_mouse_state.y, DDIO_mouse_state.dx, DDIO_mouse_state.dy, w32_msewhl_delta)); +#endif + + DDIO_mouse_state.dx = 0; + DDIO_mouse_state.dy = 0; + DDIO_mouse_state.dz = 0; + DDIO_mouse_state.btn_mask = 0; + + return btn_mask; +} + + +// gets a mouse button event, returns false if none. +bool ddio_MouseGetEvent(int *btn, bool *state) +{ + t_mse_event evt; + + if (MB_queue.recv(&evt)) { + *btn = (int)evt.btn; + *state = evt.state ? true : false; + return true; + } + + return false; +} + + +// return mouse button down time. +float ddio_MouseBtnDownTime(int btn) +{ + DWORD ticks, curticks = GetTickCount(); + + ASSERT(btn >=0 && btn < N_MSEBTNS); + + if (DIM_buttons.is_down[btn]) { + ticks = curticks - DIM_buttons.time_down[btn]; + DIM_buttons.time_down[btn] = curticks; + } + else { + ticks = DIM_buttons.time_up[btn] - DIM_buttons.time_down[btn]; + DIM_buttons.time_down[btn] = 0; + DIM_buttons.time_up[btn] = 0; + } + + return (float)(ticks/1000.0f); +} + + +// return mouse button down time +int ddio_MouseBtnDownCount(int btn) +{ + if (btn < 0 || btn >= N_MSEBTNS) return 0; + int n_downs = DIM_buttons.down_count[btn]; + + if (n_downs) { + DIM_buttons.down_count[btn] = 0; + } + + return n_downs; +} + + +// return mouse button up count +int ddio_MouseBtnUpCount(int btn) +{ + if (btn < 0 || btn >= N_MSEBTNS) return 0; + int n_ups = DIM_buttons.up_count[btn]; + DIM_buttons.up_count[btn] = 0; + return n_ups; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// called by ddio system. +/////////////////////////////////////////////////////////////////////////////// + +void ddio_InternalMouseFrame() +{ + DWORD res; + POINT pt; + + if (!DDIO_mouse_init) + return; + + if (DDIO_mouse_state.suspended) { + DDIO_mouse_state.btn_mask = 0; + DDIO_mouse_state.dx = 0; + DDIO_mouse_state.dy = 0; + DDIO_mouse_state.dz = 0; + return; + } + + GetCursorPos(&pt); + + // in emulation mode + if (DDIO_mouse_state.emulated) + { + winmouse_GetDeviceData(pt); + } + else + { + // check if event has been signaled. + res = WaitForSingleObject(DDIO_mouse_state.hmseevt, 0); + + switch (res) + { + case WAIT_OBJECT_0: + dimouse_GetDeviceData(DDIO_mouse_state.lpdimse); + ResetEvent(DDIO_mouse_state.hmseevt); // clear for next frame. + break; + } + } + + // create button mask now that DIM_buttons should be valid. + int btn_mask = 0; + + if (DIM_buttons.is_down[0]) btn_mask |= MOUSE_LB; + if (DIM_buttons.is_down[1]) btn_mask |= MOUSE_RB; + if (DIM_buttons.is_down[2]) btn_mask |= MOUSE_CB; + if (DIM_buttons.is_down[3]) btn_mask |= MOUSE_B4; + if (DIM_buttons.is_down[4]) btn_mask |= MOUSE_B5; + if (DIM_buttons.is_down[5]) btn_mask |= MOUSE_B6; + if (DIM_buttons.is_down[6]) btn_mask |= MOUSE_B7; + if (DIM_buttons.is_down[7]) btn_mask |= MOUSE_B8; + + // reset button states for wheel up and down, since we're emulating them. + DIM_buttons.is_down[MSEBTN_WHL_UP] = false; + DIM_buttons.is_down[MSEBTN_WHL_DOWN] = false; + + DDIO_mouse_state.btn_mask = btn_mask; + + // cleanup for specific mouse modes. + if (DDIO_mouse_state.mode == MOUSE_STANDARD_MODE) + { + // figure out where the top-left of the screen is + RECT clientRect; + GetClientRect( (HWND)DInputData.app->m_hWnd, &clientRect ); + + POINT topLeftWindow; + topLeftWindow.x = clientRect.left; + topLeftWindow.y = clientRect.top; + ClientToScreen( (HWND)DInputData.app->m_hWnd, &topLeftWindow ); + + // if in standard mode, don't use x,y,z retrieved from dimouse_GetDeviceData + DDIO_mouse_state.x = (short)(pt.x - topLeftWindow.x); + DDIO_mouse_state.y = (short)(pt.y - topLeftWindow.y); + DDIO_mouse_state.z = 0; + } + + if (DDIO_mouse_state.mode == MOUSE_EXCLUSIVE_MODE) + { + if (pt.x != DDIO_mouse_state.cx || pt.y != DDIO_mouse_state.cy) + { + SetCursorPos(DDIO_mouse_state.cx, DDIO_mouse_state.cy); + } + } + + // check bounds of mouse cursor. + if (DDIO_mouse_state.x < DDIO_mouse_state.brect.left) + DDIO_mouse_state.x = (short)DDIO_mouse_state.brect.left; + if (DDIO_mouse_state.x >= DDIO_mouse_state.brect.right) + DDIO_mouse_state.x = (short)DDIO_mouse_state.brect.right-1; + if (DDIO_mouse_state.y < DDIO_mouse_state.brect.top) + DDIO_mouse_state.y = (short)DDIO_mouse_state.brect.top; + if (DDIO_mouse_state.y >= DDIO_mouse_state.brect.bottom) + DDIO_mouse_state.y = (short)DDIO_mouse_state.brect.bottom-1; + if (DDIO_mouse_state.z > DDIO_mouse_state.zmax) + DDIO_mouse_state.z = (short)DDIO_mouse_state.zmax; + if (DDIO_mouse_state.z < DDIO_mouse_state.zmin) + DDIO_mouse_state.z = (short)DDIO_mouse_state.zmin; +} + + +// used to prevent mouse input from being registered +void ddio_InternalMouseSuspend() +{ + if (!DDIO_mouse_init) return; + + if (DDIO_mouse_state.lpdimse) { + DDIO_mouse_state.acquired = dimouse_Acquire(DDIO_mouse_state.lpdimse, false); + } + DDIO_mouse_state.suspended = true; +} + + +void ddio_InternalMouseResume() +{ + if (!DDIO_mouse_init) return; + + if (DDIO_mouse_state.lpdimse) { + DDIO_mouse_state.acquired = dimouse_Acquire(DDIO_mouse_state.lpdimse, true); + } + DDIO_mouse_state.suspended = false; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// DirectInput mouse interface +/////////////////////////////////////////////////////////////////////////////// + +#define DINPUT_BUFFERSIZE 64 + +// di_mouse initialization. +LPDIRECTINPUTDEVICE dimouse_Init(LPDIRECTINPUT lpdi, HWND hwnd) +{ + LPDIRECTINPUTDEVICE lpdimse; + HRESULT hr; + HANDLE hevt; + DIDEVCAPS devcaps; + DIPROPDWORD dipropdw = { + { + sizeof(DIPROPDWORD), + sizeof(DIPROPHEADER), + 0, + DIPH_DEVICE, + }, + DIPROPAXISMODE_REL + }; + int i; + + DDIO_mouse_state.hmseevt = NULL; + +// see if we need to emulate directinput. + int major, minor; + tWin32OS os = oeWin32Application::version(&major, &minor); + if (os == WinNT && major < 5) { + return NULL; + } + +// see if we can get the keyboard device. + hr = lpdi->CreateDevice(GUID_SysMouse, &lpdimse, NULL); + if (hr != DI_OK) { + DDIO_MESSAGE((hr, "DI_Mouse initialization failed.")); + return NULL; + } + + hr = lpdimse->SetDataFormat(&c_dfDIMouse); + if (hr != DI_OK) { + DDIO_MESSAGE((hr, "DI_Mouse data format specification failed.")); + lpdimse->Release(); + return NULL; + } + + // set key buffer size + hr = lpdimse->SetProperty(DIPROP_AXISMODE, &dipropdw.diph); + if (FAILED(hr)) { + DDIO_MESSAGE((hr, "DI_mouse relative mode failed..")); + lpdimse->Release(); + return NULL; + } + + hr = lpdimse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + if (hr != DI_OK) { + DDIO_MESSAGE((hr, "DI_Mouse set cooperative level failed.")); + lpdimse->Release(); + return NULL; + } + +// enable event notification. + hevt = CreateEvent(NULL, TRUE, FALSE, "DDIOMouseEvent"); + if (!hevt) { + DDIO_MESSAGE((0, "DI_mouse data event create failed.")); + lpdimse->Release(); + return NULL; + } + + hr = lpdimse->SetEventNotification(hevt); + if (FAILED(hr)) { + DDIO_MESSAGE((0, "DI_mouse event notification failed.")); + CloseHandle(hevt); + lpdimse->Release(); + return NULL; + } + +// use buffered input. + DIPROPDWORD dipdw = + { + // the header + { + sizeof(DIPROPDWORD), // diph.dwSize + sizeof(DIPROPHEADER), // diph.dwHeaderSize + 0, // diph.dwObj + DIPH_DEVICE, // diph.dwHow + }, + // the data + DINPUT_BUFFERSIZE, // dwData + }; + + hr = lpdimse->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph); + + if (FAILED(hr)) { + DDIO_MESSAGE((hr, "DI_mouse Set buffer size(SysMouse)")); + CloseHandle(hevt); + lpdimse->Release(); + return NULL; + } + +// we have a handle + DDIO_mouse_state.hmseevt = hevt; + +// determine if there's a mousewheel (z-axis) attached. + devcaps.dwSize = sizeof(DIDEVCAPS); + hr = lpdimse->GetCapabilities(&devcaps); + if (FAILED(hr) || !CHECK_FLAG(devcaps.dwFlags, DIDC_ATTACHED)) { + DDIO_MESSAGE((hr, "DI_mouse: GetCaps failed")); + CloseHandle(hevt); + lpdimse->Release(); + return NULL; + } + + DDIO_mouse_state.naxis = (short)((devcaps.dwAxes > 3) ? 3 : devcaps.dwAxes); + DDIO_mouse_state.nbtns = N_DIMSEBTNS; // cap at max number of buttons directinput accepts for mice + DDIO_mouse_state.btn_flags = 0; + for (i = 0; (i < (int)devcaps.dwButtons) && (i < N_DIMSEBTNS); i++) + { + DDIO_mouse_state.btn_flags |= (1<= 3) { + DDIO_mouse_state.zmin = MOUSE_ZMIN; + DDIO_mouse_state.zmax = MOUSE_ZMAX; + DDIO_mouse_state.nbtns = N_DIMSEBTNS+2; // 2 next button slots reserved for mouse wheel. + DDIO_mouse_state.btn_flags |= (1<SetEventNotification(NULL); + CloseHandle(DDIO_mouse_state.hmseevt); + DDIO_mouse_state.hmseevt = NULL; + } + lpdimse->Release(); + } +} + + +// di_mouse acquire device, returns acquisition state of device +bool dimouse_Acquire(LPDIRECTINPUTDEVICE lpdimse, bool acquire) +{ + HRESULT hr = acquire ? lpdimse->Acquire() : lpdimse->Unacquire(); + + if (FAILED(hr)) { + mprintf((0, "ACK: %x\n", hr)); + } + + return (acquire && SUCCEEDED(hr)) ? true : false; +} + + +// returns device data. +void dimouse_GetDeviceData(LPDIRECTINPUTDEVICE lpdimse) +{ + DIDEVICEOBJECTDATA diobjdata[DINPUT_BUFFERSIZE]; + DWORD diobjitems = DINPUT_BUFFERSIZE; + HRESULT hr; + int i; + + DDIO_mouse_state.dx = 0; + DDIO_mouse_state.dy = 0; + DDIO_mouse_state.dz = 0; + +// don't read if suspended! + if (DDIO_mouse_state.suspended) { + return; + } + +// attempt to acquire device. + if (!DDIO_mouse_state.acquired) { + DDIO_mouse_state.acquired = dimouse_Acquire(lpdimse, true); + } + +// read device. + if (DDIO_mouse_state.acquired) { + hr = lpdimse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diobjdata, &diobjitems, 0); + if (SUCCEEDED(hr)) { + // read and translate data + for (i =0; i < (int)diobjitems; i++) + { + int idx=-1; + t_mse_event evt; + + switch (diobjdata[i].dwOfs) + { + case DIMOFS_X: + DDIO_mouse_state.x += (int)diobjdata[i].dwData; + DDIO_mouse_state.dx += (int)diobjdata[i].dwData; + break; + case DIMOFS_Y: + DDIO_mouse_state.y += (int)diobjdata[i].dwData; + DDIO_mouse_state.dy += (int)diobjdata[i].dwData; + break; + case DIMOFS_Z: + // for z, simulate mouse press for emulated mouse buttons. + DDIO_mouse_state.z += (int)diobjdata[i].dwData; + DDIO_mouse_state.dz += (int)diobjdata[i].dwData; + DDIO_mouse_state.imm_dz += (int)diobjdata[i].dwData; + // mprintf((0, "Zmse=%d\n", diobjdata[i].dwData)); + if (DDIO_mouse_state.imm_dz >=WHEEL_DELTA) { + idx = MSEBTN_WHL_UP; + DDIO_mouse_state.imm_dz = 0; + } + else if (DDIO_mouse_state.imm_dz <= (-WHEEL_DELTA)) { + idx = MSEBTN_WHL_DOWN; + DDIO_mouse_state.imm_dz = 0; + } + if (idx != -1) { + DIM_buttons.down_count[idx]++; + DIM_buttons.up_count[idx]++; + DIM_buttons.is_down[idx] = true; // reset in internal mouse frame. + DIM_buttons.time_down[idx] = diobjdata[i].dwTimeStamp; + DIM_buttons.time_up[idx] = (diobjdata[i].dwTimeStamp+100); // 10th of a second. + } + break; + case DIMOFS_BUTTON0: idx = (idx==-1) ? 0 : idx; + case DIMOFS_BUTTON1: idx = (idx==-1) ? 1 : idx; + case DIMOFS_BUTTON2: idx = (idx==-1) ? 2 : idx; + case DIMOFS_BUTTON3: idx = (idx==-1) ? 3 : idx; + evt.btn = (short)idx; + if ((diobjdata[i].dwData & 0x80) && !DIM_buttons.is_down[idx]) { + DIM_buttons.down_count[idx]++; + DIM_buttons.is_down[idx] = true; + DIM_buttons.time_down[idx] = diobjdata[i].dwTimeStamp; + evt.state = 1; + MB_queue.send(evt); + } + else if (!(diobjdata[i].dwData & 0x80) && DIM_buttons.is_down[idx]) { + DIM_buttons.up_count[idx]++; + DIM_buttons.is_down[idx] = false; + DIM_buttons.time_up[idx] = diobjdata[i].dwTimeStamp; + evt.state = 0; + MB_queue.send(evt); + } + break; + + default: + mprintf((0, "di:unknown mouse event (%x,%x)\n", diobjdata[i].dwOfs, diobjdata[i].dwData)); + break; + } + } + } + else if (hr == DIERR_INPUTLOST) { + DDIO_mouse_state.acquired = dimouse_Acquire(lpdimse, true); + } + else { + DDIO_MESSAGE((hr, "DI_mouse unable to read.")); + } + } +} + + +// emulated get device state +void winmouse_GetDeviceData(POINT &pt) +{ + t_mse_event evt; + short mode = DDIO_mouse_state.mode; + + DDIO_mouse_state.dx = (short)pt.x - ((mode==MOUSE_STANDARD_MODE) ? DDIO_mouse_state.x : DDIO_mouse_state.cx); + DDIO_mouse_state.dy = (short)pt.y - ((mode==MOUSE_STANDARD_MODE) ? DDIO_mouse_state.y : DDIO_mouse_state.cy); + DDIO_mouse_state.dz = 0; + + if ((ushort)GetKeyState(VK_LBUTTON) & 0x8000) { + if (!DIM_buttons.is_down[0]) { + DIM_buttons.down_count[0]++; + DIM_buttons.time_down[0] = GetTickCount(); + DIM_buttons.is_down[0] = true; + + evt.btn = 0; + evt.state = true; + MB_queue.send(evt); + mprintf((0, "MOUSE Button 0: Down\n")); + } + } + else if (DIM_buttons.is_down[0]) { + DIM_buttons.up_count[0]++; + DIM_buttons.is_down[0] = false; + DIM_buttons.time_up[0] = GetTickCount(); + evt.btn = 0; + evt.state = false; + MB_queue.send(evt); + mprintf((0, "MOUSE Button 0: Up\n")); + } + + if ((ushort)GetKeyState(VK_RBUTTON) & 0x8000) { + if (!DIM_buttons.is_down[1]) { + DIM_buttons.down_count[1]++; + DIM_buttons.time_down[1] = GetTickCount(); + DIM_buttons.is_down[1] = true; + evt.btn = 1; + evt.state = true; + MB_queue.send(evt); + } + } + else if (DIM_buttons.is_down[1]) { + DIM_buttons.up_count[1]++; + DIM_buttons.is_down[1] = false; + DIM_buttons.time_up[1] = GetTickCount(); + evt.btn = 1; + evt.state = false; + MB_queue.send(evt); + } + + if ((ushort)GetKeyState(VK_MBUTTON) & 0x8000) { + if (!DIM_buttons.is_down[2]) { + DIM_buttons.down_count[2]++; + DIM_buttons.time_down[2] = GetTickCount(); + DIM_buttons.is_down[2] = true; + evt.btn = 2; + evt.state = true; + MB_queue.send(evt); + } + } + else if (DIM_buttons.is_down[2]) { + DIM_buttons.up_count[2]++; + DIM_buttons.is_down[2] = false; + DIM_buttons.time_up[2] = GetTickCount(); + evt.btn = 2; + evt.state = false; + MB_queue.send(evt); + } + + if (mode == MOUSE_EXCLUSIVE_MODE) { + DDIO_mouse_state.x += DDIO_mouse_state.dx; + DDIO_mouse_state.y += DDIO_mouse_state.dy; + DDIO_mouse_state.z += DDIO_mouse_state.dz; + } + + int idx = -1; + DDIO_mouse_state.imm_dz += w32_msewhl_delta; + if (DDIO_mouse_state.imm_dz >=WHEEL_DELTA) { + idx = MSEBTN_WHL_UP; + DDIO_mouse_state.imm_dz = 0; + } + else if (DDIO_mouse_state.imm_dz <= (-WHEEL_DELTA)) { + idx = MSEBTN_WHL_DOWN; + DDIO_mouse_state.imm_dz = 0; + } + if (idx != -1) { + DIM_buttons.down_count[idx]++; + DIM_buttons.up_count[idx]++; + DIM_buttons.is_down[idx] = true; // reset in internal mouse frame. + DIM_buttons.time_down[idx] = GetTickCount(); + DIM_buttons.time_up[idx] = (DIM_buttons.time_down[idx]+100); // 10th of a second. + mprintf((0, "registered mouse wheel event %d\n", idx)); + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +char Ctltext_MseBtnBindings[N_MSEBTNS][32] = { + "mse-l\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-r\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-c\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-b4\0\0\0\0\0\0\0\0\0\0\0", + "msew-u\0\0\0\0\0\0\0\0\0\0\0", + "msew-d\0\0\0\0\0\0\0\0\0\0\0", + "","" +}; + +char Ctltext_MseAxisBindings[][32] = { + "mse-X\0\0\0\0\0\0\0\0\0\0\0\0", + "mse-Y\0\0\0\0\0\0\0\0\0\0\0\0", + "msewheel\0\0\0\0\0\0\0\0\0\0" +}; + + +// returns string to binding. +const char *ddio_MouseGetBtnText(int btn) +{ + if (btn >=N_MSEBTNS || btn < 0) return (""); + return Ctltext_MseBtnBindings[btn]; +} + +const char *ddio_MouseGetAxisText(int axis) +{ + if (axis >= (sizeof(Ctltext_MseAxisBindings)/sizeof(char*)) || axis < 0) return (""); + return Ctltext_MseAxisBindings[axis]; +} + + + diff --git a/ddio_win/wintimer.cpp b/ddio_win/wintimer.cpp new file mode 100644 index 000000000..4d674d4af --- /dev/null +++ b/ddio_win/wintimer.cpp @@ -0,0 +1,323 @@ + +#include "ddio_win.h" +#include "ddio.h" + +#include + +#include "pserror.h" +#include "mono.h" + +#define MAX_TIMER_HOOKS 4 +#define TIMER_CLOCK_RATE 100 // ms + +// --------------------------------------------------------------------------- +// Variables +// --------------------------------------------------------------------------- + +static bool Timer_initialized = 0; +static bool Timer_use_highres_timer = 0; +static bool Timer_firstinit = false; +static bool Timer_preemptive = 0; +static int Timer_resolution = 0; +static DWORD Timer_sys_start_time = 0; + + +bool timerhi_Init(void); +void timerhi_Close(void); +float ddio_TickToSeconds(LARGE_INTEGER ticks); +float timerhi_GetTime(); +longlong timerhi_GetMSTime(); +void timerhi_Normalize(); +LARGE_INTEGER Timer_hi_sys_start_time; +LARGE_INTEGER Timer_hi_resolution; + + +void timer_Normalize(); + +//@@static void (*Timer_function_hook[MAX_TIMER_HOOKS])(); +//@@static int Timer_function_period[MAX_TIMER_HOOKS]; + +//@@void CALLBACK timer_Proc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); + + +// --------------------------------------------------------------------------- + +bool timer_Init(int preemptive,bool force_lores) +{ + TIMECAPS tc; + MMRESULT mmresult; + + ASSERT(!Timer_initialized); + + if(force_lores){ + Timer_use_highres_timer = false; + mprintf((0,"Timer: Forcing to Lo-Res Timer\n")); + }else{ + if(timerhi_Init()) + mprintf((0,"Timer: Hi-Resolution Timer available\n")); + else + mprintf((0,"Timer: Hi-Resolution Timer NOT available\n")); + } + + + if (!Timer_firstinit) { + Timer_sys_start_time = GetTickCount(); + Timer_firstinit = true; + atexit(timer_Close); + } + + if (preemptive) { + // Initialize Win32 Timer system + //@@ timeGetDevCaps(&tc, sizeof(tc)); + //@@ if (tc.wPeriodMin < 50) tc.wPeriodMin = 50; + //@@ + //@@ Timer_resolution = tc.wPeriodMin; + //@@ + //@@ mmresult = timeBeginPeriod(tc.wPeriodMin); + //@@ if (mmresult != TIMERR_NOERROR) { + //@@ Error("Unable to initialize Win32 timer (err:%x).\n", mmresult); + //@@ } + //@@ + //@@ Timer_preemptive = 1; + //@@ + //@@ // clear out timer function hook list + //@@ for (int i = 0; i < MAX_TIMER_HOOKS;i++) + //@@ Timer_function_hook[i] = NULL; + //@@ + //@@ Timer_event_id = timeSetEvent(TIMER_CLOCK_RATE, Timer_resolution, timer_Proc, 0, TIME_PERIODIC); + //@@ if (Timer_event_id == 0) Error("Unable to instantiate the timer procedure.\n"); + //@@ + //@@ mprintf((0, "Preemptive timer system initialized.\n")); + mprintf((0, "No preemptive timer, default to non-preemptive!")); + Int3(); + Timer_preemptive = 0; + } + else { + mprintf((0, "Timer system initialized.\n")); + Timer_preemptive = 0; + } + +// Initialize Win32 Timer system + timeGetDevCaps(&tc, sizeof(tc)); + if (tc.wPeriodMin < 50) tc.wPeriodMin = 50; + + Timer_resolution = tc.wPeriodMin; + + mmresult = timeBeginPeriod(tc.wPeriodMin); + if (mmresult != TIMERR_NOERROR) { + //Error("Unable to initialize Win32 timer (err:%x).\n", mmresult); + } + + Timer_initialized = 1; + + return 1; +} + + +void timer_Close() +{ + ASSERT(Timer_initialized); + +// mprintf((0, "Timer system closed.\n")); + + if (Timer_use_highres_timer){ + timerhi_Close(); + }else{ + if (Timer_resolution) { + timeEndPeriod(Timer_resolution); + Timer_resolution = 0; + } + } + + Timer_initialized = 0; +} + + +float ddio_TickToSeconds(unsigned long ticks) +{ + if (Timer_use_highres_timer){ + LARGE_INTEGER t; + t.QuadPart = ticks; + return ddio_TickToSeconds(t); + }else{ + timer_Normalize(); + + DWORD time_ms; + + time_ms = ticks - Timer_sys_start_time; + + return ((float)time_ms/((float )1000.0)); + } + + Int3(); //ack!! + return 0; +} + + + +float timer_GetTime() +{ + if (Timer_use_highres_timer){ + return timerhi_GetTime(); + }else{ + DWORD time_ms; + + timer_Normalize(); + time_ms = GetTickCount() - Timer_sys_start_time; + + return ((float)time_ms/((float )1000.0)); + } + + Int3(); //ack!! + return 0; +} + + +longlong timer_GetMSTime() +{ + if (Timer_use_highres_timer){ + return timerhi_GetMSTime(); + }else{ + DWORD time_ms; + + timer_Normalize(); + time_ms = GetTickCount() - Timer_sys_start_time; + + return time_ms; + } + + Int3(); //ack!! + return 0; +} + +// --------------------------------------------------------------------------- +// Internal functions +// --------------------------------------------------------------------------- +// hook in timer function at certain period. returns a handle to this function +DWORD timer_HookFunction(void (CALLBACK *fncptr)(UINT, UINT, DWORD, DWORD, DWORD), UINT delay) +{ + DWORD time_event_id; + + time_event_id = timeSetEvent(delay, Timer_resolution, fncptr, 0, TIME_PERIODIC); + + return time_event_id; +} + + +// clears function from hook list specified by a handle returned from HookFunction +void timer_ReleaseFunction(DWORD func) +{ + timeKillEvent(func); +} + + +void timer_Normalize() +{ + DWORD new_time; + + new_time = GetTickCount(); + + if (new_time < Timer_sys_start_time) { + Timer_sys_start_time = new_time; + return; + } +} + + +// --------------------------------------------------------------------------- +// Support for Win32 timer functions +// We support a timer callback function which we can hook functions to. +// Call function hooks at specified period +// --------------------------------------------------------------------------- + + + +//@@void CALLBACK timer_Proc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +//@@{ +//@@ static int ticks = 0; +//@@ static BOOL in_func = FALSE; +//@@ int i; +//@@ +//@@/* Hopefully this will prevent some reentrance, but the timer functions should be very minimal +//@@ and follow Win32 conventions +//@@*/ +//@@ if (!in_func) { +//@@ in_func = TRUE; +//@@ +//@@ for (i = 0; i < MAX_TIMER_HOOKS; i++) +//@@ { +//@@ if (Timer_function_hook[i] && !(ticks % Timer_function_period[i])) +//@@ (*Timer_function_hook[i])(); +//@@ } +//@@ +//@@ in_func = FALSE; +//@@ } +//@@ +//@@ ticks++; +//@@ if (ticks == 256) ticks = 0; +//@@} + +bool timerhi_Init(void) +{ + // See if we can get the Windows hi res time + Timer_use_highres_timer = (bool)(QueryPerformanceFrequency(&Timer_hi_resolution)!=FALSE); + + if (!Timer_use_highres_timer) + return false; + + // determine the start tick + QueryPerformanceCounter(&Timer_hi_sys_start_time); + return true; +} + +void timerhi_Close(void) +{ + //do any shutdown here!? +} + +float ddio_TickToSeconds(LARGE_INTEGER ticks) +{ + timerhi_Normalize(); + + LARGE_INTEGER time_ms; + + time_ms.QuadPart = ticks.QuadPart - Timer_hi_sys_start_time.QuadPart; + + return (float)((double)time_ms.QuadPart/((double)Timer_hi_resolution.QuadPart)); +} + +float timerhi_GetTime() +{ + LARGE_INTEGER time_tick; + + timerhi_Normalize(); + + QueryPerformanceCounter(&time_tick); + + return (float) ( ((double)time_tick.QuadPart - Timer_hi_sys_start_time.QuadPart)/((double)Timer_hi_resolution.QuadPart)); +} + + +//This should return a timer in milliseconds +longlong timerhi_GetMSTime() +{ + LARGE_INTEGER time_tick; + + timerhi_Normalize(); + + QueryPerformanceCounter(&time_tick); + + return ( (time_tick.QuadPart - Timer_hi_sys_start_time.QuadPart)/(Timer_hi_resolution.QuadPart/1000)); +} + +void timerhi_Normalize() +{ + LARGE_INTEGER new_time; + + QueryPerformanceCounter(&new_time); + + if ( new_time.QuadPart < Timer_hi_sys_start_time.QuadPart ){ + Timer_hi_sys_start_time = new_time; + } +} + diff --git a/ddvid_lnx/CMakeLists.txt b/ddvid_lnx/CMakeLists.txt new file mode 100644 index 000000000..04e5304eb --- /dev/null +++ b/ddvid_lnx/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (HEADERS ) +SET (CPPS + video_lnx.cpp) + +ADD_LIBRARY(ddvid_lnx STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/ddvid_lnx/video_lnx.cpp b/ddvid_lnx/video_lnx.cpp new file mode 100644 index 000000000..d9e7006c6 --- /dev/null +++ b/ddvid_lnx/video_lnx.cpp @@ -0,0 +1,230 @@ +/* + * Video library. + * + * $NoKeywords: $ + */ + +#include "pserror.h" +#include "ddvid.h" +#include "application.h" +#include "linux/lnxapp.h" +#include +#include +#include +#undef EGA +//#include +typedef struct +{ + int width,height,bytesperpixel,linewidth; +}tinfo; + +typedef struct tDDVideoInfo{ + oeLnxApplication *app; + //vga_modeinfo *info; + tinfo *info; + bool paged; +}tDDVideoInfo; + +////////////////////////////////////////////////////////////////////////////// +// Variables + +tDDVideoInfo DDVideo_info; +static bool DDVideo_init = false; + +////////////////////////////////////////////////////////////////////////////// +// Prototypes + + + +////////////////////////////////////////////////////////////////////////////// +// Functions + +// called first to allow fullscreen video access +bool ddvid_Init(oeApplication *app, char *driver) +{ + int subsys_id; + + // preinitialize system. + if (!DDVideo_init) { + DDVideo_info.app = NULL; + DDVideo_info.info = NULL; + atexit(ddvid_Close); + } + else { + ddvid_Close(); + } + + DDVideo_init = true; + + DDVideo_info.app = (oeLnxApplication *)app; + + //vga_init(); + return true; +} + + +// closes ddvid system manually. +void ddvid_Close() +{ + if (!DDVideo_init) + return; + +/* + if(vga_getcurrentmode()!=TEXT) + vga_setmode(TEXT); +*/ + DDVideo_init = false; +} + + +// sets the appropriate video mode. +bool ddvid_SetVideoMode(int w, int h, int color_depth, bool paged) +{ +/* + ASSERT(DDVideo_init); + + int mode = -1; + + if( (w==640) && (h==480) ){ + switch(color_depth){ + case BPP_8: + mode = G640x480x256; + break; + case BPP_16: + mode = G640x480x32K; + break; + case BPP_24: + mode = G640x480x64K; + break; + case BPP_32: + mode = G640x480x16M; + break; + } + }else + if( (w==512) && (h==384) ){ + mode = -1; + }else + if( (w==800) && (h==600) ){ + switch(color_depth){ + case BPP_8: + mode = G800x600x256; + break; + case BPP_16: + mode = G800x600x32K; + break; + case BPP_24: + mode = G800x600x64K; + break; + case BPP_32: + mode = G800x600x16M; + break; + } + } + + if(mode==-1) + return false; + + if(!vga_hasmode(mode)) + return false; + + DDVideo_info.info = vga_getmodeinfo(mode); + + DDVideo_info.paged = (bool)(DDVideo_info.info->flags&HAVE_RWPAGE); + + if( (paged) && (!(DDVideo_info.paged)) ){ + DDVideo_info.info = NULL; + Error("Pages not supported by mode %d\n",mode); + return false; + } + + vga_setmode(mode); +*/ + return true; +} + + +// sets screen handle +void ddvid_SetVideoHandle(unsigned handle) +{ +} + + +// retrieves screen information +void ddvid_GetVideoProperties(int *w, int *h, int *color_depth) +{ + *w = 640; + *h = 480; + *color_depth = 16; + +/* + ASSERT(DDVideo_init); + if(!DDVideo_info.info){ + *w = *h = *color_depth = 0; + return; + } + *w = DDVideo_info.info->width; + *h = DDVideo_info.info->height; + *color_depth = DDVideo_info.info->bytesperpixel; +*/ +} + + +// retrieves screen aspect ratio. +float ddvid_GetAspectRatio() +{ +/* + float aspect_ratio = (float)((3.0 * vga_getxdim())/(4.0 * vga_getydim())); + return aspect_ratio; +*/ +return 0.75f; +} + +// flips screen if there's a back buffer +void ddvid_VideoFlip() +{ + if(!DDVideo_info.paged) + return; + //vga_flip(); +} + + +// retreives frame buffer info for a video mode. +void ddvid_LockFrameBuffer(ubyte **data, int *pitch) +{ + *data = NULL; + *pitch = 0; + +/* + ASSERT(DDVideo_init); + if(!DDVideo_info.info){ + *data = NULL; + *pitch = 0; + return; + } + + //*data = vga_getgraphmem(); + *pitch = DDVideo_info.info->linewidth; +*/ +} + + +void ddvid_UnlockFrameBuffer() +{ + //ASSERT(DDVideo_init); +} + + + + + + + + + + + + + + + + diff --git a/fix/CMakeLists.txt b/fix/CMakeLists.txt new file mode 100644 index 000000000..fce5ea4f6 --- /dev/null +++ b/fix/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (HEADERS ) +SET (CPPS + fix.cpp) + +ADD_LIBRARY(fix STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/fix/fix.cpp b/fix/fix.cpp new file mode 100644 index 000000000..6c2e15746 --- /dev/null +++ b/fix/fix.cpp @@ -0,0 +1,278 @@ +/* + * $Logfile: /DescentIII/Main/fix/fix.cpp $ + * $Revision: 6 $ + * $Date: 4/22/99 8:29p $ + * $Author: Kevin $ + * + * Fixed-point math functions, including trig & float conversions + * + * $Log: /DescentIII/Main/fix/fix.cpp $ + * + * 6 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 5 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 4 8/26/97 10:15a Jason + * corrected the FixCeil function + * + * 3 8/22/97 12:34p Jason + * added FixCeil and FixFloor + * + * 2 8/21/97 5:57p Samir + * Added initialization of random number generator. + * + * 7 2/27/97 4:47 PM Jeremy + * moved inline assembly language functions processor for + * fixmul/fixdiv/fixmuldiv into fixmac.h and fixwin32.h + * + * 6 2/12/97 5:35p Jason + * fixed yet another dumb syntax error...will I ever learn? + * + * 5 2/12/97 5:34p Jason + * fixed dumb syntax error + * + * 4 2/12/97 5:27p Jason + * implemented asin,acos,atan2 + * + * 3 2/10/97 1:57p Jason + * changed FloatToFix back to what it originally was + * + * 2 2/07/97 5:44p Matt + * Moved fixed-point math funcs from vecmat to fix + * Changed trig functions to use small table like D2 + * + * $NoKeywords: $ + */ + +#include "fix.h" + +#include +#include +#include "psrand.h" +//Tables for trig functions +float sincos_table[321]; //256 entries + 64 sin-only + 1 for interpolation +angle asin_table[257]; // 1 quadrants worth, +1 for interpolation +angle acos_table[257]; + +#define PI 3.141592654 + +//Generate the data for the trig tables +void InitMathTables () +{ + int i; + float rad,s,c; + + for (i=0;i<321;i++) { + rad = (float) ((double) i / 256.0 * 2 * PI); + sincos_table[i] = (float) sin(rad); + } + + for (i=0;i<256;i++) { + + s=asin((float)i/256.0); + c=acos((float)i/256.0); + + s=(s/(PI*2)); + c=(c/(PI*2)); + + asin_table[i] = FloatToFix(s); + acos_table[i] = FloatToFix(c); + } + + asin_table[256]=asin_table[255]; + acos_table[256]=acos_table[255]; + +// Initialize a random seed. + ps_srand(time(NULL)); +} + +//Returns the sine of the given angle. Linearly interpolates between two entries in a 256-entry table +float FixSin(angle a) +{ + int i,f; + float s0,s1; + + i = (a>>8)&0xff; + f = a&0xff; + + s0 = sincos_table[i]; + s1 = sincos_table[i+1]; + return (float) (s0 + ((s1 - s0) * (double) f / 256.0)); +} + +//Returns the cosine of the given angle. Linearly interpolates between two entries in a 256-entry table +float FixCos(angle a) +{ + int i,f; + float c0,c1; + + i = (a>>8)&0xff; + f = a&0xff; + + c0 = sincos_table[i+64]; + c1 = sincos_table[i+64+1]; + return (float) (c0 + ((c1 - c0) * (double) f / 256.0)); +} + +//Returns the sine of the given angle, but does no interpolation +float FixSinFast(angle a) +{ + int i; + + i = ((a+0x80)>>8)&0xff; + + return sincos_table[i]; +} + +//Returns the cosine of the given angle, but does no interpolation +float FixCosFast(angle a) +{ + int i; + + i = ((a+0x80)>>8)&0xff; + + return sincos_table[i+64]; +} + +// use this instead of: +// for: (int)floor(x+0.5f) use FloatRound(x) +// (int)ceil(x-0.5f) use FloatRound(x) +// (int)floor(x-0.5f) use FloatRound(x-1.0f) +// (int)floor(x) use FloatRound(x-0.5f) +// for values in the range -2048 to 2048 + +// Set a vector to {0,0,0} +int FloatRound( float x ) +{ + float nf; + nf = x + 8390656.0f; + return ((*((int *)&nf)) & 0x7FFFFF)-2048; +} + +// A fast way to convert floats to fix +fix FloatToFixFast( float x ) +{ + + float nf; + nf = x*65536.0f + 8390656.0f; + return ((*((int *)&nf)) & 0x7FFFFF)-2048; +} + +//Get rid of the "no return value" warnings in the next three functions +#pragma warning (disable:4035) + +//compute inverse sine +angle FixAsin(float v) +{ + fix vv; + int i,f,aa; + + vv = FloatToFix(fabs(v)); + + if (vv >= F1_0) //check for out of range + return 0x4000; + + i = (vv>>8)&0xff; + f = vv&0xff; + + aa = asin_table[i]; + aa = aa + (((asin_table[i+1] - aa) * f)>>8); + + if (v < 0) + aa = F1_0-aa; + + return aa; +} + +//compute inverse cosine +angle FixAcos(float v) +{ + fix vv; + int i,f,aa; + + vv = FloatToFix(fabs(v)); + + if (vv >= F1_0) //check for out of range + return 0; + + i = (vv>>8)&0xff; + f = vv&0xff; + + aa = acos_table[i]; + aa = aa + (((acos_table[i+1] - aa) * f)>>8); + + if (v < 0) + aa = 0x8000 - aa; + + return aa; +} + +//given cos & sin of an angle, return that angle. +//parms need not be normalized, that is, the ratio of the parms cos/sin must +//equal the ratio of the actual cos & sin for the result angle, but the parms +//need not be the actual cos & sin. +//NOTE: this is different from the standard C atan2, since it is left-handed. +angle FixAtan2(float cos,float sin) +{ + float q,m; + angle t; + + //find smaller of two + + + q=(sin*sin)+(cos*cos); + + m = sqrt(q); + + if (m==0) + return 0; + + if (fabs(sin) < fabs(cos)) + { + //sin is smaller, use arcsin + t = FixAsin(sin/m); + if (cos<0) + t = 0x8000 - t; + + return t; + } + else + { + t = FixAcos(cos/m); + if (sin<0) + t = F1_0-t; + + return t; + } + +} + + +// Does a ceiling operation on a fixed number +fix FixCeil (fix num) +{ + int int_num; + fix new_num; + + int_num=FixToInt (num); + + if (num & 0xFFFF) + { + new_num=IntToFix(int_num+1); + return new_num; + } + + new_num=IntToFix(int_num); + return (new_num); +} + +// Floors a fixed number +fix FixFloor (fix num) +{ + int int_num=FixToInt (num); + + return (IntToFix (int_num)); +} + diff --git a/grtext/CMakeLists.txt b/grtext/CMakeLists.txt new file mode 100644 index 000000000..d0ea9f06b --- /dev/null +++ b/grtext/CMakeLists.txt @@ -0,0 +1,7 @@ +SET (HEADERS grtextlib.h ) +SET (CPPS + grfont.cpp + grtext.cpp + textaux.cpp) + +ADD_LIBRARY(grtext STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/grtext/grfont.cpp b/grtext/grfont.cpp new file mode 100644 index 000000000..2a545f01b --- /dev/null +++ b/grtext/grfont.cpp @@ -0,0 +1,1203 @@ +/* + * $Logfile: /DescentIII/Main/grtext/grfont.cpp $ + * $Revision: 25 $ + * $Date: 11/16/99 4:56p $ + * $Author: Samir $ + * + * + * + * $Log: /DescentIII/Main/grtext/grfont.cpp $ + * + * 25 11/16/99 4:56p Samir + * upped max texture count per font. + * + * 24 11/16/99 3:18p Samir + * added new data to font file and kept compatibility with D3 fonts: + * tracking value. + * + * 23 8/10/99 5:11p Jeff + * close open files + * + * 22 6/08/99 5:45p Samir + * correctly take care of values about 128 when character passed to + * grfont_GetCharWidth + * + * 21 4/24/99 8:41p Samir + * fixed clipped text scaling probs. + * + * 20 4/17/99 6:16p Samir + * added kerning and 4444 alphaed font support. + * + * 19 4/14/99 1:15a Jeff + * fixed case mismatched #includes + * + * 18 4/01/99 5:23p Samir + * Added function to get character info. + * + * 17 3/02/99 6:26p Samir + * added font template width and height functions. + * + * 16 2/21/99 6:39p Samir + * added function to get ascii value of font character. + * + * 15 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 14 5/22/98 12:44p Samir + * fixed grayscale conversion bug. + * + * 13 5/15/98 5:36p Samir + * reset references count of font when freeing. + * + * 12 5/08/98 5:23p Samir + * added font brightness and grayscale caps. + * + * 11 5/01/98 3:09p Samir + * properly translate monochrome fonts to 555 output. + * + * 10 4/27/98 3:46p Samir + * scaling fonts. + * + * 9 4/24/98 8:00a Samir + * hopefully fixed font pixel format translation to 555 problem. + * + * 8 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 7 4/15/98 12:10p Samir + * commented out unnecessary mprints. + * + * 6 3/31/98 3:49p Jason + * added memory lib + * + * 5 1/12/98 7:03p Samir + * Centering works fully. + * + * 4 1/12/98 5:24p Samir + * Fixed font reading and created font spew test function. + * + * 3 1/02/98 12:53p Samir + * Convert lowercase to uppercase if there are no lowercase chars in the + * font. + * + * 2 12/29/97 5:48p Samir + * Fixed problem with non clipping text rendering (a hack.) + * + * 1 12/29/97 3:24p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "grtextlib.h" +#include "CFILE.H" +#include "bitmap.h" +#include "pserror.h" +#include "renderer.h" +#include "mem.h" +#include "ddio.h" + +#include +#include +#include +#include + +const int MAX_FONTS = 16, + MAX_FONT_BITMAPS = 32; + +#define FT_UPPERCASE 128 + +#define GRFONT_SURFACE_WIDTH 128 +#define GRFONT_SURFACE_HEIGHT 128 + +#define BITS_TO_BYTES(_c) (((_c)+7)>>3) +#define BITS_TO_SHORTS(_c) (((_c)+15)>>4) + + +////////////////////////////////////////////////////////////////////////////// +// Variables + + +typedef struct tFontInfo +{ + char filename[32]; // filename of font + int references; // number of references of that font + int bmps[MAX_FONT_BITMAPS]; // font bitmap handles + ubyte *ch_u, *ch_v, *ch_w, *ch_h; + int *ch_bmp; + float *ch_uf, *ch_vf, *ch_wf, *ch_hf; + tFontFileInfo font; +} +tFontInfo; + + +// Font information stored here. +static tFontInfo Fonts[MAX_FONTS]; +static bool Font_init = false; + +#define GRFONT_SURFACE_WIDTH 128 +#define GRFONT_SURFACE_HEIGHT 128 + +// ---------------------------------------------------------------------------- +// Macros for file io. +// ---------------------------------------------------------------------------- + +typedef CFILE* FONTFILE; + +inline int READ_FONT_INT(FONTFILE ffile) { + return cf_ReadInt(ffile); +} + +inline short READ_FONT_SHORT(FONTFILE ffile) { + return cf_ReadShort(ffile); +} + +inline ubyte READ_FONT_BYTE(FONTFILE ffile) { + return (ubyte)cf_ReadByte(ffile); +} + +inline int READ_FONT_DATA(FONTFILE ffile, void *buf, int size, int nelem) +{ + int i; + + i = cf_ReadBytes((ubyte *)buf, size*nelem, ffile); + + ASSERT(i == (size*nelem)); + + return i; +} + +inline FONTFILE OPEN_FONT(char *filename) { + FONTFILE fp; + int file_id; + + fp = (FONTFILE)cfopen(filename, "rb"); + if (!fp) return NULL; + + file_id = READ_FONT_INT(fp); + if (file_id != 0xfeedbaba) return (FONTFILE)(-1); + + return fp; +} + +inline void CLOSE_FONT(FONTFILE ffile) { + cfclose(ffile); +} + + +typedef FILE* FONTFILE2; + +inline int WRITE_FONT_INT(FONTFILE2 ffile, int i) { + return fwrite(&i, sizeof(i), 1, (FILE *)ffile); +} + +inline int WRITE_FONT_SHORT(FONTFILE2 ffile, short s) { + return fwrite(&s, sizeof(s), 1, (FILE *)ffile); +} + +inline int WRITE_FONT_BYTE(FONTFILE2 ffile, ubyte c) { + return fwrite(&c, sizeof(c), 1, (FILE *)ffile); +} + +inline int WRITE_FONT_DATA(FONTFILE2 ffile, void *buf, int size, int nelem) +{ + int i; + i = (int)fwrite(buf, size, nelem, (FILE *)ffile); + + ASSERT(i == nelem); + + return i; +} + +inline FONTFILE2 OPEN_FONT2(char *filename) { + FONTFILE2 fp; + + fp = (FONTFILE2)fopen(filename, "wb"); + if (!fp) return NULL; + + return fp; +} + +inline void CLOSE_FONT2(FONTFILE2 ffile) { + fclose((FILE *)ffile); +} + + + +////////////////////////////////////////////////////////////////////////////// +// Functions + +void grfont_Close(); +void grfont_TranslateToBitmaps(int handle); +void grfont_XlateMonoChar(int bmp_handle, int x, int y, int index, tFontFileInfo *ft, int width); +void grfont_XlateColorChar(int bmp_handle, int x, int y, int index, tFontFileInfo *ft, int width); +void grfont_XlateColorGrayChar(int bmp_handle, int x, int y, int index, tFontFileInfo *ft, int width); +void grfont_ClearBitmap(int bmp_handle); + + +// clears out font buffer. +void grfont_Reset() +{ + int i; + + if (Font_init) { + // free all allocated fonts. + grfont_Close(); + } + else { + atexit(grfont_Close); + } + + for (i = 0; i < MAX_FONTS; i++) + Fonts[i].references = 0; + + Font_init = true; +} + + +void grfont_Close() +{ + int i; + + ASSERT(Font_init); + + for (i = 0; i < MAX_FONTS; i++) + if (Fonts[i].references) + grfont_Free(i); + + Font_init = false; +} + + + +// returns a handle to a loaded font. +int grfont_Load(char *fname) +{ + FONTFILE ff; + tFontFileInfo fnt; + char fontname[32]; + int num_char, i, handle = -1; + + ASSERT(Font_init); + +// check if font is in list. if so, just return the handle. + +// find free slot in font list. + for (i = 0; i < MAX_FONTS; i++) + { + if (Fonts[i].references == 0) + break; + } + ASSERT(i < MAX_FONTS); + + handle = i; + +// open file. + ff = OPEN_FONT(fname); + if (!ff) { + return false; + } + else if (ff == (FONTFILE)0xffffffff) { + mprintf((0,"Illegal font file: %s.\n", fname)); + return false; + } + +// read header information + fnt.width = READ_FONT_SHORT(ff); + fnt.height = READ_FONT_SHORT(ff); + fnt.flags = READ_FONT_SHORT(ff); + fnt.baseline = READ_FONT_SHORT(ff); + fnt.min_ascii = READ_FONT_BYTE(ff); + fnt.max_ascii = READ_FONT_BYTE(ff); + READ_FONT_DATA(ff, fontname, 32, 1); + +// read new info + if (fnt.flags & FT_FFI2) { + fnt.ffi2.tracking = READ_FONT_SHORT(ff); + READ_FONT_DATA(ff, fnt.ffi2.reserved, sizeof(fnt.ffi2.reserved), 1); + } + else { + fnt.ffi2.tracking = 0; + } + + fnt.brightness = ((fnt.baseline >> 8) / 10.0f); + +// mprintf((0, "%s font.\n", fname)); +// mprintf((0, " ::::::", fnt.height, fnt.min_ascii, fnt.max_ascii, fnt.baseline)); + num_char = fnt.max_ascii-fnt.min_ascii+1; + + if (fnt.max_ascii < 'a') { + fnt.flags |= FT_UPPERCASE; + } + +// Read in all widths + if (fnt.flags & FT_PROPORTIONAL) { + fnt.char_widths = (ubyte *)mem_malloc(sizeof(ubyte)*num_char); + for (i = 0; i < num_char; i++) + fnt.char_widths[i] = (ubyte)READ_FONT_SHORT(ff); +// mprintf((0, "::proportional")); + } + else { + fnt.char_widths = NULL; + } + +// Read in kerning data + if (fnt.flags & FT_KERNED) { + int n_pairs = (int)READ_FONT_SHORT(ff); + fnt.kern_data = (ubyte *)mem_malloc(sizeof(ubyte)*3*(n_pairs+1)); + for (i = 0; i < n_pairs; i++) + { + fnt.kern_data[i*3] = READ_FONT_BYTE(ff); + fnt.kern_data[i*3+1] = READ_FONT_BYTE(ff); + fnt.kern_data[i*3+2] = READ_FONT_BYTE(ff); + } + fnt.kern_data[i*3] = 255; + fnt.kern_data[i*3+1] = 255; + fnt.kern_data[i*3+2]= 0; + } + else { + fnt.kern_data= NULL; + } + +// Read in pixel data. +// for color fonts, read in byte count and then the data, +// generate character data pointer table +// for mono fonts, read in byte count, then the data, convert to bits and store +// generate character data pointer table + int bytesize = READ_FONT_INT(ff); + + fnt.raw_data = (ubyte *)mem_malloc(bytesize); + fnt.char_data = (ubyte **)mem_malloc(num_char * sizeof(ubyte *)); + + READ_FONT_DATA(ff, fnt.raw_data, bytesize, 1); + + if (fnt.flags & FT_COLOR) { + int off = 0; +// mprintf((0, "::color")); + for (i = 0; i < num_char; i++) + { + fnt.char_data[i] = fnt.raw_data + off; + if (fnt.flags & FT_PROPORTIONAL) + off += (fnt.char_widths[i]*fnt.height*BITS_TO_BYTES(BPP_16)); + else + off += (fnt.width*fnt.height*BITS_TO_BYTES(BPP_16)); + } + } + else { // Monochrome + ubyte *ptr = fnt.raw_data; +// mprintf((0, "::mono")); + for (i = 0; i < num_char; i++) + { + fnt.char_data[i] = ptr; + if (fnt.flags & FT_PROPORTIONAL) + ptr += BITS_TO_BYTES(fnt.char_widths[i]) * fnt.height; + else + ptr += BITS_TO_BYTES(fnt.width) * fnt.height; + } + } + +// Then read in + CLOSE_FONT(ff); + +// mprintf((0, "\n")); + + strcpy(Fonts[handle].filename, fname); + Fonts[handle].references = 1; + Fonts[handle].font = fnt; + +// draw font to bitmaps, load into surfaces too. + grfont_TranslateToBitmaps(handle); + + return handle; +} + + +// frees a loaded font +void grfont_Free(int handle) +{ + tFontInfo *ft; + int i; + + ASSERT(Font_init); + ASSERT(handle > -1 && handle < MAX_FONTS); + +// delete font surface info. + ft = &Fonts[handle]; + if (ft->ch_bmp) delete[] ft->ch_bmp; + if (ft->ch_wf) delete[] ft->ch_wf; + if (ft->ch_hf) delete[] ft->ch_hf; + if (ft->ch_uf) delete[] ft->ch_uf; + if (ft->ch_vf) delete[] ft->ch_vf; + if (ft->ch_w) delete[] ft->ch_w; + if (ft->ch_h) delete[] ft->ch_h; + if (ft->ch_u) delete[] ft->ch_u; + if (ft->ch_v) delete[] ft->ch_v; + + for (i = 0; i < MAX_FONT_BITMAPS; i++) + { + if (ft->bmps[i] != -1) + bm_FreeBitmap(ft->bmps[i]); + } + +// delete font file info. + if ((ft->font.flags & FT_KERNED) && ft->font.kern_data) { + mem_free(ft->font.kern_data); + } + + if (ft->font.flags & FT_PROPORTIONAL) { + mem_free(ft->font.char_widths); + } + + ft->references = 0; + + mem_free(ft->font.raw_data); + mem_free(ft->font.char_data); +} + + +// loads a font template +bool grfont_LoadTemplate(char *fname, tFontTemplate *ft) +{ + FONTFILE ff; + char fontname[32]; + short ft_width, ft_height, ft_flags, ft_minasc, ft_maxasc, num_char, i; + tFontFileInfo2 ffi2; + +// open file. + ff = OPEN_FONT(fname); + if (!ff) { + Error("Unable to open font %s.\n", fname); + } + else if (ff == (FONTFILE)0xffffffff) { + Error("Illegal font file: %s.\n", fname); + } + +// read header information + ft_width = READ_FONT_SHORT(ff); + ft_height = READ_FONT_SHORT(ff); + ft_flags = READ_FONT_SHORT(ff); + READ_FONT_SHORT(ff); // skip baseline value (HACKED FOR BRIGHTNESS) + ft_minasc = READ_FONT_BYTE(ff); + ft_maxasc = READ_FONT_BYTE(ff); + READ_FONT_DATA(ff, fontname, 32, 1); // read namae + +// read ffi2 font info + if (ft_flags & FT_FFI2) { + ffi2.tracking = READ_FONT_SHORT(ff); + READ_FONT_DATA(ff,&ffi2.reserved, sizeof(ffi2.reserved), 1); + } + + num_char = ft_maxasc-ft_minasc+1; + +// Read in all widths + if (ft_flags & FT_PROPORTIONAL) { + ft->ch_widths = (ubyte *)mem_malloc(num_char); + for (i = 0; i < num_char; i++) + ft->ch_widths[i] = (ubyte)READ_FONT_SHORT(ff); + } + else { + ft->ch_widths = NULL; + } + + if (ft_flags & FT_KERNED) { + int n_pairs = (int)READ_FONT_SHORT(ff); + ft->kern_data = (ubyte *)mem_malloc(sizeof(ubyte) * 3 * (n_pairs+1)); + for (i = 0; i < n_pairs; i++) + { + ft->kern_data[i*3] = READ_FONT_BYTE(ff); + ft->kern_data[i*3+1] = READ_FONT_BYTE(ff); + ft->kern_data[i*3+2] = READ_FONT_BYTE(ff); + } + ft->kern_data[i*3] = 255; + ft->kern_data[i*3+1] = 255; + ft->kern_data[i*3+2]= 0; + } + else { + ft->kern_data= NULL; + } + + ft->ch_height = (ubyte)ft_height; + ft->ch_maxwidth = (ubyte)ft_width; + ft->max_ascii = ft_maxasc; + ft->min_ascii = ft_minasc; + ft->proportional = (ft_flags&FT_PROPORTIONAL) ? true : false; + ft->uppercase = (ft_maxasc < 'a'); + ft->monochromatic = (ft_flags & FT_COLOR) ? false : true; + ft->newstyle = (ft_flags & FT_FMT4444) ? true : false; + ft->ffi2 = (ft_flags & FT_FFI2) ? true : false; + + ft->ch_tracking = (sbyte)ffi2.tracking; + + CLOSE_FONT(ff); + + return true; +} + + +// frees a font template +void grfont_FreeTemplate(tFontTemplate *ft) +{ + if (ft->kern_data) { + mem_free(ft->kern_data); + ft->kern_data = NULL; + } + if (ft->ch_widths) { + mem_free(ft->ch_widths); + ft->ch_widths = NULL; + } +} + + +// sets a template to a font, be careful. +bool grfont_SetTemplate(const char *pathname, const tFontTemplate *ft) +{ +// okay, load the font manually, set the template members, then save it out. + tFontFileInfo fnt; + char tempstr[32]; + FONTFILE ffin; + FONTFILE2 ffout; + int num_char,i; + + tFontFileInfo2 ffi2; + + ffin = OPEN_FONT((char *)pathname); + if (!ffin) { + return false; + } + else if (ffin == (FONTFILE)0xffffffff) { + mprintf((0, "Illegal font file %s\n", pathname)); + return false; + } + +// read header information + fnt.width = READ_FONT_SHORT(ffin); + fnt.height = READ_FONT_SHORT(ffin); + fnt.flags = READ_FONT_SHORT(ffin); + fnt.baseline = READ_FONT_SHORT(ffin); + fnt.min_ascii = READ_FONT_BYTE(ffin); + fnt.max_ascii = READ_FONT_BYTE(ffin); + READ_FONT_DATA(ffin, tempstr, 32, 1); + +// read ffi2 font info + if (fnt.flags & FT_FFI2) { + ffi2.tracking = READ_FONT_SHORT(ffin); + READ_FONT_DATA(ffin,&ffi2.reserved, sizeof(ffi2.reserved), 1); + } + + fnt.brightness = ((fnt.baseline >> 8) / 10.0f); + + num_char = fnt.max_ascii-fnt.min_ascii+1; + +// Read in all widths + if (fnt.flags & FT_PROPORTIONAL) { + fnt.char_widths = (ubyte *)mem_malloc(sizeof(ubyte)*num_char); + for (i = 0; i < num_char; i++) + fnt.char_widths[i] = (ubyte)READ_FONT_SHORT(ffin); + } + else { + fnt.char_widths = NULL; + } + +// Read in kerning data + if (fnt.flags & FT_KERNED) { + int n_pairs = (int)READ_FONT_SHORT(ffin); + fnt.kern_data = (ubyte *)mem_malloc(sizeof(ubyte)*3*(n_pairs+1)); + for (i = 0; i < n_pairs; i++) + { + fnt.kern_data[i*3] = READ_FONT_BYTE(ffin); + fnt.kern_data[i*3+1] = READ_FONT_BYTE(ffin); + fnt.kern_data[i*3+2] = READ_FONT_BYTE(ffin); + } + fnt.kern_data[i*3] = 255; + fnt.kern_data[i*3+1] = 255; + fnt.kern_data[i*3+2]= 0; + } + else { + fnt.kern_data= NULL; + } + +// Read in pixel data. + int bytesize = READ_FONT_INT(ffin); + + fnt.raw_data = (ubyte *)mem_malloc(bytesize); + + READ_FONT_DATA(ffin, fnt.raw_data, bytesize, 1); + + CLOSE_FONT(ffin); + +// set template values + fnt.width = ft->ch_maxwidth; + fnt.height = ft->ch_height; + fnt.flags = (ft->proportional ? FT_PROPORTIONAL : 0) + ((!ft->monochromatic) ? FT_COLOR : 0) + (ft->kern_data ? FT_KERNED : 0) + + (ft->newstyle ? FT_FMT4444 : 0); + fnt.min_ascii = (ubyte)ft->min_ascii; + fnt.max_ascii = (ubyte)ft->max_ascii; + + if (ft->ffi2) fnt.flags |= FT_FFI2; + + if (fnt.kern_data) { + mem_free(fnt.kern_data); + } + if (fnt.char_widths) { + mem_free(fnt.char_widths); + } + fnt.kern_data = ft->kern_data; + fnt.char_widths = ft->ch_widths; + +// write out font. + ffout = OPEN_FONT2((char *)pathname); + if (!ffout) { + return false; + } + +// Write file id. + WRITE_FONT_INT(ffout, 0xfeedbaba); + WRITE_FONT_SHORT(ffout, fnt.width); + WRITE_FONT_SHORT(ffout, fnt.height); + WRITE_FONT_SHORT(ffout, fnt.flags); + WRITE_FONT_SHORT(ffout, fnt.baseline); + WRITE_FONT_BYTE(ffout, fnt.min_ascii); + WRITE_FONT_BYTE(ffout, fnt.max_ascii); + WRITE_FONT_DATA(ffout, tempstr, 32, 1); + + if (fnt.flags & FT_FFI2) { + WRITE_FONT_SHORT(ffout, (short)ft->ch_tracking); + WRITE_FONT_DATA(ffout, ffi2.reserved, sizeof(ffi2.reserved),1); + } + + num_char = ft->max_ascii - ft->min_ascii + 1; + +// Write widths now if necessary.(FT_PROPORTIONAL) + if (fnt.flags & FT_PROPORTIONAL) { + for (int i = 0; i < num_char; i++) + WRITE_FONT_SHORT(ffout, (short)fnt.char_widths[i]); + } + + if (fnt.flags & FT_KERNED) { + // iterate new kerning info bytes + ubyte *ch = fnt.kern_data; + int n_bytes = 0, n_pairs; + while (ch[n_bytes] != 255) + { + n_bytes+= 3; + } + + ASSERT((n_bytes % 3)==0); + n_pairs = n_bytes/3; + WRITE_FONT_SHORT(ffout, (short)n_pairs); + + for (i = 0; i < n_pairs; i++) + { + WRITE_FONT_BYTE(ffout, ch[i*3]); + WRITE_FONT_BYTE(ffout, ch[i*3+1]); + WRITE_FONT_BYTE(ffout, ch[i*3+2]); + } + } + + WRITE_FONT_INT(ffout, bytesize); + WRITE_FONT_DATA(ffout, fnt.raw_data, bytesize, 1); + + CLOSE_FONT2(ffout); + +// free raw data loaded, don't free kern and widths, already freed, template versions remain + mem_free(fnt.raw_data); + + return true; +} + + +// sets a font's template without saving... +bool grfont_SetKerning(int font, ubyte *kern_data) +{ + tFontInfo *oldft = &Fonts[font]; + int n_pairs=0; + +// reset kerning... + if (oldft->font.kern_data) { + mem_free(oldft->font.kern_data); + oldft->font.kern_data = NULL; + } + + if (kern_data) { + while (kern_data[n_pairs*3] != 255) + n_pairs++; + + oldft->font.kern_data = (ubyte *)mem_malloc((n_pairs+1)*3*sizeof(ubyte)); + n_pairs=0; + while (kern_data[n_pairs*3] != 255) + { + oldft->font.kern_data[n_pairs*3] = kern_data[n_pairs*3]; + oldft->font.kern_data[n_pairs*3+1] = kern_data[n_pairs*3+1]; + oldft->font.kern_data[n_pairs*3+2] = kern_data[n_pairs*3+2]; + n_pairs++; + } + oldft->font.kern_data[n_pairs*3] = 255; + oldft->font.kern_data[n_pairs*3+1] = 255; + oldft->font.kern_data[n_pairs*3+2] = 0; + } + +// we're not going to reset other stuff for now... just kerning! + return true; +} + + +// sets a font's tracking +bool grfont_SetTracking(int font, int tracking) +{ + ASSERT(font >= 0 && font < MAX_FONTS); + + tFontInfo *oldft = &Fonts[font]; + + oldft->font.ffi2.tracking = (short)tracking; + + return false; +} + + +int grfont_GetTracking(int font) +{ + ASSERT(font >= 0 && font < MAX_FONTS); +// if (Fonts[font].font.flags & FT_FFI2) { + return (int)Fonts[font].font.ffi2.tracking; +// } + +// return 0; +} + + +// render a character +int grfont_BltChar(int font, tCharBlt *cbi) +{ + ASSERT(font > -1 && font < MAX_FONTS); + + tFontInfo *ft = &Fonts[font]; + +// save current x and get this font + if (cbi->ch > ft->font.max_ascii && (ft->font.flags & FT_UPPERCASE)) { + cbi->ch = toupper(cbi->ch); + } + + if ((cbi->ch < ft->font.min_ascii) || (cbi->ch > ft->font.max_ascii)) + return (cbi->x+1); + + cbi->ch = cbi->ch - ft->font.min_ascii; + + if (!cbi->clipped) { + + if (ft->font.flags & FT_PROPORTIONAL) + cbi->sw = (int)(ft->font.char_widths[cbi->ch]); + else + cbi->sw = (int)(ft->font.width); + cbi->sh = (int)(ft->font.height); + cbi->sx = 0; + cbi->sy = 0; + + rend_DrawFontCharacter (ft->bmps[ft->ch_bmp[cbi->ch]], cbi->x,cbi->y, + (int)(cbi->x+cbi->sw*cbi->dsw), + (int)(cbi->y+cbi->sh*cbi->dsh), + ft->ch_uf[cbi->ch], + ft->ch_vf[cbi->ch], + ft->ch_wf[cbi->ch], + ft->ch_hf[cbi->ch]); + return (cbi->x + (int)(cbi->sw*cbi->dsw)); +//@@ rend_DrawFontCharacter (ft->bmps[ft->ch_bmp[cbi->ch]], cbi->x,cbi->y, +//@@ cbi->x+ft->ch_w[cbi->ch], cbi->y+ft->ch_h[cbi->ch], +//@@ ft->ch_uf[cbi->ch], ft->ch_vf[cbi->ch], +//@@ ft->ch_wf[cbi->ch], ft->ch_hf[cbi->ch]); +//@@ return (cbi->x + ft->ch_w[cbi->ch] - 1); + } + else { + rend_DrawFontCharacter (ft->bmps[ft->ch_bmp[cbi->ch]], cbi->x,cbi->y, + (int)(cbi->x+cbi->sw), // don't scale since these values are already scaled + (int)(cbi->y+cbi->sh), + ft->ch_uf[cbi->ch]+(((float)cbi->sx)/((float)GRFONT_SURFACE_WIDTH)), + ft->ch_vf[cbi->ch]+(((float)cbi->sy)/((float)GRFONT_SURFACE_HEIGHT)), + ((float)cbi->sw)/((float)GRFONT_SURFACE_WIDTH), + ((float)cbi->sh)/((float)GRFONT_SURFACE_HEIGHT)); + return (cbi->x + (int)(cbi->sw)); // scaled value already + } +} + + +// returns a character's width +int grfont_GetCharWidth(int font, int ch) +{ + tFontFileInfo *ft; + + ASSERT(font > -1 && font < MAX_FONTS); + + ft = &Fonts[font].font; + ch = (int)((ubyte)ch); + + if (ch > ft->max_ascii && (ft->flags & FT_UPPERCASE)) { + ch = toupper(ch); + } + + if (ch < ft->min_ascii || ch > ft->max_ascii) + return 0; + else if (ft->flags & FT_PROPORTIONAL) + return ft->char_widths[ch-ft->min_ascii]; + else + return ft->width; +} + + +// returns a font's height +int grfont_GetHeight(int font) +{ + ASSERT(font > -1 && font < MAX_FONTS); + return Fonts[font].font.height; +} + + +// translates raw font data to bitmaps. +void grfont_TranslateToBitmaps(int handle) +{ + tFontFileInfo *fntfile; + tFontInfo *fnt; + int i; + + fnt = &Fonts[handle]; + fntfile = &Fonts[handle].font; + +// start creating font surfaces, map these surfaces onto bitmaps created via bitmap library +// this is needed for the renderer library. +// create a 128x128 bitmap. +// draw each character into bitmap until we need to create another +// surface. + ubyte u=0, v=0, w; + int ch, num_ch; + ubyte surf_index = 0; + + num_ch = fntfile->max_ascii-fntfile->min_ascii+1; + +// initialize memory + fnt->ch_w = new ubyte[num_ch]; + fnt->ch_h = new ubyte[num_ch]; + fnt->ch_u = new ubyte[num_ch]; + fnt->ch_v = new ubyte[num_ch]; + fnt->ch_bmp = new int[num_ch]; + fnt->ch_uf = new float[num_ch]; + fnt->ch_vf = new float[num_ch]; + fnt->ch_wf = new float[num_ch]; + fnt->ch_hf = new float[num_ch]; + + for (i = 0; i < MAX_FONT_BITMAPS; i++) + fnt->bmps[i] = -1; + + fnt->bmps[surf_index] = bm_AllocBitmap(GRFONT_SURFACE_WIDTH, GRFONT_SURFACE_HEIGHT, 0); + if (fnt->bmps[surf_index] == -1 || fnt->bmps[surf_index] == BAD_BITMAP_HANDLE) + Error("TranslateToBitmaps "); + if (fntfile->flags & FT_FMT4444) { + GameBitmaps[fnt->bmps[surf_index]].format = BITMAP_FORMAT_4444; + } + + grfont_ClearBitmap(fnt->bmps[surf_index]); + surf_index++; + + for (ch = 0; ch < num_ch; ch++) + { + if (fntfile->flags & FT_PROPORTIONAL) w = (int)fntfile->char_widths[ch]; + else w = (int)fntfile->width; + + if ((u+w) > GRFONT_SURFACE_WIDTH) { + u = 0; + v += fntfile->height; + if ((v+fntfile->height) > GRFONT_SURFACE_HEIGHT) { + if (surf_index == MAX_FONT_BITMAPS) Int3(); + fnt->bmps[surf_index] = bm_AllocBitmap(GRFONT_SURFACE_WIDTH, GRFONT_SURFACE_HEIGHT, 0); + if (fnt->bmps[surf_index] == -1 || fnt->bmps[surf_index] == BAD_BITMAP_HANDLE) + Error("TranslateToBitmaps "); + if (fntfile->flags & FT_FMT4444) { + GameBitmaps[fnt->bmps[surf_index]].format = BITMAP_FORMAT_4444; + } + grfont_ClearBitmap(fnt->bmps[surf_index]); + surf_index++; + v = 0; + } + } + + // blt each character + if (fntfile->flags & FT_COLOR) { + if (fntfile->flags & FT_GRADIENT) + grfont_XlateColorGrayChar(fnt->bmps[surf_index-1], u,v,ch,fntfile,w); +// else + grfont_XlateColorChar(fnt->bmps[surf_index-1], u,v,ch,fntfile,w); + } + else { // font monochrome, convert bits to pixels + grfont_XlateMonoChar(fnt->bmps[surf_index-1], u,v,ch,fntfile,w); + } + + fnt->ch_h[ch] = (ubyte)fntfile->height; + fnt->ch_w[ch] = w; + fnt->ch_u[ch] = u; + fnt->ch_v[ch] = v; + fnt->ch_bmp[ch] = surf_index-1; + fnt->ch_hf[ch] = ((float)fntfile->height)/((float)GRFONT_SURFACE_HEIGHT); + fnt->ch_wf[ch] = ((float)w)/((float)GRFONT_SURFACE_WIDTH); + fnt->ch_uf[ch] = ((float)u)/((float)GRFONT_SURFACE_WIDTH); + fnt->ch_vf[ch] = ((float)v)/((float)GRFONT_SURFACE_HEIGHT); + + // check to adjust uv's if we are outside surface. + u+= w; + } +} + + +// Font translation routines +void grfont_XlateMonoChar(int bmp_handle, int x, int y, int index, tFontFileInfo *ft, int width) +{ + int row,col; // byte width of char + ubyte bit_mask=0, byte; + ubyte *fp; + + fp = ft->char_data[index]; + +/* draw one-bit one color. */ + ushort *dest_ptr; + ushort col_w = GR_COLOR_TO_16(GR_RGB(255,255,255)); + int rowsize_w; + + dest_ptr = bm_data(bmp_handle,0); + rowsize_w = bm_rowsize(bmp_handle, 0)>>1; + dest_ptr += (y*rowsize_w)+x; + + for (row = 0; row < ft->height; row++) + { + bit_mask = 0; + for (col = 0; col < width; col++) + { + if (bit_mask == 0) { + byte = *fp++; + bit_mask = 0x80; + } + + if (byte & bit_mask) + dest_ptr[col] = (col_w|OPAQUE_FLAG); + + bit_mask >>=1; + } + dest_ptr += rowsize_w; + } +} + + +void grfont_XlateColorChar(int bmp_handle, int x, int y, int index, tFontFileInfo *ft, int width) +{ +/* 16-bit copy from source bitmap to destination surface just created and + locked + This function performs scaling if the source width and height don't match + that of the destinations - JL +*/ + ushort *dptr, *sptr; + int row, col; + int rowsize_w; + + dptr = bm_data(bmp_handle, 0); + sptr = (ushort *)ft->char_data[index]; + rowsize_w = bm_rowsize(bmp_handle, 0)/2; + + //height = SET_MIN(h, ddsfObj.h); + //width = SET_MIN(w, ddsfObj.w); + dptr = dptr + (y * rowsize_w); + + if (ft->flags & FT_FMT4444) { + for (row = 0; row < ft->height; row++) + { + for (col = 0; col < width; col++) + { + dptr[x+col] = *(sptr++); + } + dptr += rowsize_w; + } + } + else { + // old style + for (row = 0; row < ft->height; row++) + { + for (col = 0; col < width; col++) + { + ushort col565 = *(sptr++); + if (col565 == 0x07e0) + dptr[x+col] = NEW_TRANSPARENT_COLOR; + else + dptr[x+col] = (((col565 & 0xf800) >> 1) | ((col565 & 0x07c0) >> 1) | (col565 & 0x001f)) | OPAQUE_FLAG; + } + dptr += rowsize_w; + } + } +} + + +void grfont_XlateColorGrayChar(int bmp_handle, int x, int y, int index, tFontFileInfo *ft, int width) +{ +/* 16-bit copy from source bitmap to destination surface just created and + locked + This function performs scaling if the source width and height don't match + that of the destinations - JL +*/ + ushort *dptr, *sptr; + int row, col; + int rowsize_w; + + dptr = bm_data(bmp_handle, 0); + sptr = (ushort *)ft->char_data[index]; + rowsize_w = bm_rowsize(bmp_handle, 0)/2; + + //height = SET_MIN(h, ddsfObj.h); + //width = SET_MIN(w, ddsfObj.w); + dptr = dptr + (y * rowsize_w); + + float recip32 = 1.0f/32.0f; + + for (row = 0; row < ft->height; row++) + { + for (col = 0; col < width; col++) + { + ushort col565 = *(sptr++); + if (col565 == 0x07e0) + dptr[x+col] = NEW_TRANSPARENT_COLOR; + else { + ubyte r = (ubyte)((col565 & 0xf800) >> 11); + ubyte g = (ubyte)((col565 & 0x07c0) >> 6); + ubyte b = (ubyte)(col565 & 0x001f); + float brightness = ((r * 0.30f) + (g * 0.59f) + (b * 0.11f)) * recip32; + ubyte elem = (ubyte)(255*brightness*ft->brightness); + if ((brightness*ft->brightness) > 1.0f) + elem = 255; + dptr[x+col] = GR_RGB16(elem,elem, elem) | OPAQUE_FLAG; + } + } + dptr += rowsize_w; + } +} + + +void grfont_ClearBitmap(int bmp_handle) +{ + int dx, dy; + int rowsize_w = bm_rowsize(bmp_handle,0) / 2; + ushort *bmpdata = bm_data(bmp_handle, 0); + + for(dy = 0; dy < bm_h(bmp_handle, 0); dy++) + { + for (dx = 0; dx < rowsize_w; dx++) + bmpdata[dx] = NEW_TRANSPARENT_COLOR; + + bmpdata += rowsize_w; + } +} + +#ifndef RELEASE +void grfont_Spew(int font, int x, int y) +{ + int i; + tFontInfo *fnt; + + fnt = &Fonts[font]; + + for (i = 0; i < MAX_FONT_BITMAPS; i++) + if (fnt->bmps[i] > -1) + rend_DrawScaledBitmap(i*GRFONT_SURFACE_WIDTH, 0, (i+1)*GRFONT_SURFACE_WIDTH, GRFONT_SURFACE_HEIGHT, + fnt->bmps[i], 0,0,1.0,1.0); +} +#endif + + + +// returns character equivalent in font (converts lowercase to uppercase if no lowercase version avail) +// similar to ddio_KeyToAscii +int grfont_KeyToAscii(int font, int key) +{ + key = ddio_KeyToAscii(key); + + if (font >= 0 && font < MAX_FONTS) { + tFontInfo *ft = &Fonts[font]; + + // save current x and get this font + if (key > ft->font.max_ascii && (ft->font.flags & FT_UPPERCASE)) { + return toupper(key); + } + } + + return key; +} + + + +// returns the raw bitmap data for a character in a font, its width and height +// returned data should be in 565 hicolor format if (*mono) is false. if (*mono) is true, +// then a bitmask will be returned, and you should treat a bit as a pixel. +ushort *grfont_GetRawCharacterData(int font, int ch, int *w, int *h, bool *mono) +{ + tFontFileInfo *fntfile; + tFontInfo *fnt; + + ASSERT(font > -1 && font < MAX_FONTS); + + fnt = &Fonts[font]; + fntfile = &fnt->font; + + if (ch > fnt->font.max_ascii && (fntfile->flags & FT_UPPERCASE)) { + ch = toupper(ch); + } + + if ((ch < fnt->font.min_ascii) || (ch > fnt->font.max_ascii)) + return NULL; + + ch = ch - fnt->font.min_ascii; + *mono = !(fnt->font.flags & FT_COLOR); + + if (fnt->font.flags & FT_PROPORTIONAL) { + *w = (int)(fnt->font.char_widths[ch]); + } + else { + *w = (int)(fnt->font.width); + } + + *h = fnt->font.height; + + return (ushort *)fnt->font.char_data[ch]; +} + + +// returns a character's width +int grfont_GetKernedSpacing(int font, int ch1, int ch2) +{ + tFontFileInfo *ft; + + ASSERT(font > -1 && font < MAX_FONTS); + + ft = &Fonts[font].font; + + if (ft->kern_data) { + ubyte *kern = ft->kern_data; + while (kern[0]!=255) + { + if (ch1==kern[0] && ch2 == kern[1]) { + return (int)((sbyte)kern[2]); + } + kern+=3; + } + } + return 0; +} + + +// returns a character's width +int grfont_GetKernedSpacingTemp(const tFontTemplate *ft, int ch1, int ch2) +{ + if (ft->kern_data) { + ubyte *kern = ft->kern_data; + while (kern[0]!=255) + { + if (ch1==kern[0] && ch2 == kern[1]) { + return (int)((sbyte)kern[2]); + } + kern+=3; + } + } + return 0; +} + + diff --git a/grtext/grtext.cpp b/grtext/grtext.cpp new file mode 100644 index 000000000..438b031f6 --- /dev/null +++ b/grtext/grtext.cpp @@ -0,0 +1,1122 @@ +/* + * $Logfile: /DescentIII/Main/grtext/grtext.cpp $ + * $Revision: 38 $ + * $Date: 11/16/99 3:18p $ + * $Author: Samir $ + * + * + * + * $Log: /DescentIII/Main/grtext/grtext.cpp $ + * + * 38 11/16/99 3:18p Samir + * added new data to font file and kept compatibility with D3 fonts: + * tracking value. + * + * 37 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 36 5/02/99 7:16p Samir + * fixed text line width problem with newline chars. + * + * 35 5/02/99 1:50a Samir + * fixed nasty bug where intrastring text colors weren't getting set due + * to a change in how we do color for text now. + * + * 34 4/26/99 6:56p Kevin + * doh! forgot the 0 termination + * + * 33 4/26/99 6:55p Kevin + * added some more naughty words + * + * 32 4/24/99 8:41p Samir + * fixed clipped text scaling probs. + * + * 31 4/17/99 6:16p Samir + * added kerning and 4444 alphaed font support. + * + * 30 4/13/99 4:39p Samir + * simplified color scheme. + * + * 29 4/09/99 3:16p Kevin + * Fixed a stupid memory leak + * + * 28 4/02/99 3:49p Kevin + * Added profanity filter code + * + * 27 3/02/99 6:26p Samir + * added font template width and height functions. + * + * 26 1/20/99 3:47a Jeff + * added function to get clipping parameters + * + * 25 11/03/98 7:04p Samir + * made Grtext_spacing global so word wrapper could access it. + * + * 24 11/01/98 1:58a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 23 10/22/98 2:41p Samir + * made grtext_Puts public. + * + * 22 10/19/98 10:42p Jason + * made Int3s into breaks + * + * 21 9/24/98 2:56p Samir + * added ability to format strings inside string. + * + * 20 9/21/98 2:58p Samir + * may have fixed GetTextHeight function. + * + * 19 9/18/98 11:29a Samir + * use strchr instead of strtok. + * + * 18 9/08/98 5:42p Samir + * fixed memory leak + * + * 17 9/08/98 10:27a Samir + * added function to get text height. + * + * 16 9/04/98 3:22p Samir + * newlines handled a bit better. + * + * 15 8/24/98 3:12p Samir + * fixed text clipping + * + * 14 6/23/98 5:05p Samir + * streamlined strlen calls + * + * 13 5/15/98 5:35p Samir + * fixed instring text color formatting. + * + * 12 5/05/98 6:29p Samir + * spaces don't render, just width of it counts. + * + * 11 4/27/98 3:46p Samir + * scaling fonts. + * + * 10 4/18/98 2:08a Samir + * extended text buffer. + * + * 9 2/13/98 6:37p Samir + * Fixed tabs. + * + * 8 2/03/98 12:20p Samir + * Be sure to set alphatype when Grtext_SetFlags operation occurs. + * + * 7 2/03/98 12:13p Samir + * Font shadowing support added. + * + * 6 1/30/98 2:15p Samir + * Allow for text saturation. + * + * 5 1/23/98 6:53p Samir + * Added grtext_PutChar. + * + * 4 1/08/98 12:16p Samir + * GetLineWidth now takes a const char * + * + * 3 12/30/97 5:20p Samir + * Some stuff. + * + * 2 12/29/97 5:49p Samir + * Added centering text capability. + * + * 1 12/29/97 3:24p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#include "grtextlib.h" + +#include "renderer.h" +#include "pserror.h" +#include "pstring.h" +#include "mem.h" + +#include +#include +#include +#include +#include + +#define CLIP_LEFT Grtext_left +#define CLIP_TOP Grtext_top +#define CLIP_RIGHT Grtext_right +#define CLIP_BOTTOM Grtext_bottom + +// Text opcodes +#define GRTEXTOP_PUTS 0x1 // 2 shorts, x and y +#define GRTEXTOP_SETCOLOR 0x2 // 1 ddgr_color +#define GRTEXTOP_FANCYCOLOR 0x3 // 4 ddgr_colors +#define GRTEXTOP_SETFONT 0x4 // 1 font handle (int) +#define GRTEXTOP_SETALPHA 0x5 // 1 ubyte +#define GRTEXTOP_PUTCHAR 0x6 // (2 shorts) 1 ubyte +#define GRTEXTOP_SETFLAGS 0x7 // (flags) = 1 int. +#define GRTEXTOP_SCALE 0x8 // 1 float (scale) scale current font. + +// Text execution buffer +int Grtext_spacing = 1; +static char Grtext_buffer[GRTEXT_BUFLEN]; +static int Grtext_ptr = 0; +static int Grtext_font = 0; +static ubyte Grtext_alpha = 255; +static sbyte Grtext_alphatype = ATF_TEXTURE+ATF_CONSTANT; +static bool Grtext_shadow = false; +static int Grtext_line_spacing = 1; +static int Grtext_tabspace=0; +static int Grtext_left, Grtext_top, Grtext_right, Grtext_bottom; +static ddgr_color Grtext_colors[2]; +static float Grtext_scale = 1.0f; +static ddgr_color Grtext_color = GR_BLACK; + +// draws a string +void grtext_RenderString(int x, int y, char *str); +void grtext_DrawTextLine(int x, int y, char *str); +void grtext_DrawTextLineClip(int x, int y, char *str); + +#define XORVAL 205 +#define MAX_BAD_WORD_LEN 10 +typedef unsigned char badword[MAX_BAD_WORD_LEN]; +//These all need to be lower case! +badword bad_words[] = +{ + //These are just XOR'd with XORVAL +{ 0xab, 0xb8, 0xae, 0xa6, 0x00 },// = fuck +{ 0xbe, 0xa5, 0xa4, 0xb9, 0x00 },// = shit +{ 0xae, 0xb8, 0xa3, 0xb9, 0x00 },// = cunt +{ 0xba, 0xa5, 0xa2, 0xbf, 0xa8, 0x00 },// = whore +{ 0xac, 0xbe, 0xbe, 0xa5, 0xa2, 0xa1, 0xa8, 0x00 },// = asshole +{ 0xa9, 0xac, 0xa0, 0xa3, 0x00 },// = damn +{ 0xbd, 0xb8, 0xbe, 0xbe, 0xb4, 0x00 },//pussy +{ 0xaf, 0xa4, 0xb9, 0xae, 0xa5, 0x00 },//bitch +{ 0xa9, 0xac, 0xa0, 0xa3, 0x00 },//damn +{ 0xbd, 0xa4, 0xbe, 0xbe, 0x00 } //piss +}; + + +#define NUM_BAD_WORDS (sizeof(bad_words)/sizeof(badword)) + +char subst_chars[] = "#!&@&#%*"; + +#define NUM_SUBST_CHARS (sizeof(subst_chars)-1) + +bool grtext_FilterProfanity = false; + +void grtext_SetProfanityFilter(bool enabled) +{ + grtext_FilterProfanity = enabled; +} +//Right now this just decrypts the bad words +void grtext_Init(void) +{ + for(int i=0;i ft->max_ascii && ft->uppercase) { + ch = toupper(ch); + } + else if (ch < ft->min_ascii || ch > ft->max_ascii) { + return 0; + } + + return (int)((ft->proportional) ? ft->ch_widths[ch-ft->min_ascii] : ft->ch_maxwidth); +} + +inline int CHAR_HEIGHT_TEMP(const tFontTemplate *ft) +{ + return (int)ft->ch_height; +} + +inline int CHAR_SPACING_TEMP(const tFontTemplate *ft, int ch1, int ch2) +{ + if (ch1 > ft->max_ascii && ft->uppercase) { + ch1 = toupper(ch1); + } + if (ch2 > ft->max_ascii && ft->uppercase) { + ch2 = toupper(ch2); + } + + return (int)(grfont_GetKernedSpacingTemp(ft, ch1, ch2)); +} + + + +// clears text buffer. doesn't render. +void grtext_Reset() +{ + Grtext_buffer[0] = 0; + Grtext_ptr = 0; + Grtext_alpha = 255; + Grtext_scale = 1.0f; + Grtext_spacing = 1; + grtext_SetFlags(0); + grtext_SetColor(GR_BLACK); +} + + +// sets text clipping parameters +void grtext_SetParameters(int left, int top, int right, int bottom, int tabspace) +{ + Grtext_left = left; + Grtext_top = top; + Grtext_right = right; + Grtext_bottom = bottom; + Grtext_tabspace = tabspace; +} + +// gets text clipping parameters +void grtext_GetParameters(int *left, int *top, int *right, int *bottom, int *tabspace) +{ + if(left) + *left = Grtext_left; + if(top) + *top = Grtext_top; + if(right) + *right = Grtext_right; + if(bottom) + *bottom = Grtext_bottom; + if(tabspace) + *tabspace = Grtext_tabspace; +} + +// sets the color for text +void grtext_SetColor(ddgr_color col) +{ + struct { + char op; + ddgr_color col; + } + cmd; + + ASSERT((Grtext_ptr+sizeof(cmd)) < GRTEXT_BUFLEN); + + cmd.op = GRTEXTOP_SETCOLOR; + cmd.col = col; + + memcpy(&Grtext_buffer[Grtext_ptr], &cmd, sizeof(cmd)); + Grtext_ptr += sizeof(cmd); + Grtext_color = col; +} + + +// returns color set by grtext_SetColor (does not count intra-string color changes) +ddgr_color grtext_GetColor() +{ + return Grtext_color; +} + + +// sets font scale (1.0 = normal, 0.5 = 1/2, 2.0 = twice as large. +void grtext_SetFontScale(float scale) +{ + struct { + char op; + float scale; + } + cmd; + + ASSERT((Grtext_ptr+sizeof(cmd)) < GRTEXT_BUFLEN); + + cmd.op = GRTEXTOP_SCALE; + cmd.scale = scale; + + memcpy(&Grtext_buffer[Grtext_ptr], &cmd, sizeof(cmd)); + Grtext_ptr += sizeof(cmd); + + Grtext_scale = scale; +} + + +// sets fancy color for text +void grtext_SetFancyColor(ddgr_color col1, ddgr_color col2, ddgr_color col3, ddgr_color col4) +{ + struct { + char op; + // ddgr_color col[4]; + ddgr_color col; + } + cmd; + + ASSERT((Grtext_ptr+sizeof(cmd)) < GRTEXT_BUFLEN); + + cmd.op = GRTEXTOP_FANCYCOLOR; + cmd.col = col1; + + memcpy(&Grtext_buffer[Grtext_ptr], &cmd, sizeof(cmd)); + Grtext_ptr += sizeof(cmd); +} + + +// sets the alpha value for text +void grtext_SetAlpha(ubyte alpha) +{ + struct { + char op; + ubyte alpha; + } + cmd; + + ASSERT((Grtext_ptr+sizeof(cmd)) < GRTEXT_BUFLEN); + cmd.op = GRTEXTOP_SETALPHA; + cmd.alpha = alpha; + Grtext_alpha = alpha; + + memcpy(&Grtext_buffer[Grtext_ptr], &cmd, sizeof(cmd)); + Grtext_ptr += sizeof(cmd); +} + + +// gets font alpha +ubyte grtext_GetAlpha() +{ + return Grtext_alpha; +} + + +// toggles text saturation +void grtext_SetFlags(int flags) +{ + struct { + char op; + int flags; + } + cmd; + + ASSERT((Grtext_ptr+sizeof(cmd)) < GRTEXT_BUFLEN); + + cmd.op = GRTEXTOP_SETFLAGS; + cmd.flags = flags; + + memcpy(&Grtext_buffer[Grtext_ptr], &cmd, sizeof(cmd)); + Grtext_ptr += sizeof(cmd); +} + + +// sets the font for text +void grtext_SetFont(int font_handle) +{ + struct { + char op; + int handle; + } + cmd; + + ASSERT((Grtext_ptr+sizeof(cmd)) < GRTEXT_BUFLEN); + cmd.op = GRTEXTOP_SETFONT; + cmd.handle = font_handle; + Grtext_font = cmd.handle; + Grtext_spacing = 1 + grfont_GetTracking(Grtext_font); + + memcpy(&Grtext_buffer[Grtext_ptr], &cmd, sizeof(cmd)); + Grtext_ptr += sizeof(cmd); +} + + +// puts a formatted string in the text buffer +void grtext_Printf(int x, int y, const char *fmt, ...) +{ + va_list arglist; + int len; + char buf[512]; + + va_start(arglist,fmt); + len = Pvsprintf(buf,512,fmt,arglist); + va_end(arglist); + if (len < 0) return; + + grtext_Puts(x, y, buf); +} + + +// gets the current font +int grtext_GetFont() +{ + return Grtext_font; +} + + +// puts a centered string in the text buffer. +void grtext_CenteredPrintf(int xoff, int y, const char *fmt, ...) +{ + int new_x; + va_list arglist; + int len; + char buf[512]; + + va_start(arglist,fmt); + len = Pvsprintf(buf,512,fmt,arglist); + va_end(arglist); + if (len < 0) return; + + new_x = Grtext_left + (Grtext_right - Grtext_left)/2 - grtext_GetTextLineWidth(buf)/2; + + grtext_Puts(new_x+xoff, y, buf); +} + +#define GR_STR_LEN 128 +// draws a string +void grtext_Puts(int x, int y, const char *str) +{ + struct { + char op; + short x, y; + } + cmd; + + ASSERT((Grtext_ptr + sizeof(cmd) + strlen(str) + 1) < GRTEXT_BUFLEN); + + cmd.op = GRTEXTOP_PUTS; + cmd.x = (short)x; + cmd.y = (short)y; + + + + + + + ASSERT((Grtext_ptr + sizeof(cmd) + strlen(str) + 1) < GRTEXT_BUFLEN); + + cmd.op = GRTEXTOP_PUTS; + cmd.x = (short)x; + cmd.y = (short)y; + memcpy(&Grtext_buffer[Grtext_ptr], &cmd, sizeof(cmd)); + Grtext_ptr += sizeof(cmd); + strcpy(&Grtext_buffer[Grtext_ptr], str); + + if(grtext_FilterProfanity) + { + //DAJ changed to local to reduce memory thrashing +//DAJ char *lowerstr = (char *)mem_malloc(strlen(str)+1); + char lowerstr[GR_STR_LEN]; + + int slen = strlen(str); + if(slen >= GR_STR_LEN) + slen = GR_STR_LEN-1; + + for(int a=0;a (gy+CHAR_HEIGHT(Grtext_font))) || (CLIP_BOTTOM < gy)) clipped = 2; + else if ((CLIP_LEFT > (gx+line_width)) || (CLIP_RIGHT < gx)) clipped = 2; + + if (clipped != 2) { + if (CLIP_LEFT > gx || CLIP_RIGHT < (gx+line_width)) clipped = 1; + if (CLIP_TOP > gy || CLIP_BOTTOM < (gy+CHAR_HEIGHT(Grtext_font))) clipped = 1; + + if (clipped == 0) + grtext_DrawTextLine(gx, gy, line); + else if (clipped == 1) + grtext_DrawTextLineClip(gx, gy, line); + } + cur_y += (Grtext_line_spacing + CHAR_HEIGHT(Grtext_font)); + cur_x = CLIP_LEFT; + + //replace the newline, if there was one + if (save_pos) + *save_pos++ = '\n'; + + line = save_pos; + + } + while (line); + + +} + + +int grtext_GetTextHeightTemplate(tFontTemplate *ft, const char *str) +{ + int cur_h=0; + const char *spos = str; + + do + { + cur_h += (Grtext_line_spacing + CHAR_HEIGHT_TEMP(ft)); + + spos = strchr(spos, '\n'); + if (spos) + spos++; + } + while (spos); + + return cur_h; +} + + +int grtext_GetTextHeight(const char *str) +{ + int cur_h=0; + const char *spos = str; + + do + { + cur_h += (Grtext_line_spacing + CHAR_HEIGHT(Grtext_font)); + + spos = strchr(spos, '\n'); + if (spos) + spos++; + } + while (spos); + + return cur_h; +} + + + +int grtext_GetTextLineWidth(const char *str) +{ + int line_width = 0, max_width=0; + int rgb_define_mode = 0; + int strsize = strlen(str); + int line_idx = 0; + + for (int i = 0; i < strsize; i++) + { + int width; + char ch = str[i], ch2 = str[i+1]; + + // note that if we hit the GR_COLOR_CHAR then the next three values should + // not count when defining the width of the line. + if (rgb_define_mode == 3) rgb_define_mode = 0; + else if (ch == GR_COLOR_CHAR) rgb_define_mode = 1; + if (!rgb_define_mode) { + if (ch == '\t') { //tab char + int space_width; + space_width = (CHAR_WIDTH(Grtext_font, ' ')+Grtext_spacing) * Grtext_tabspace; + line_width = (line_width + space_width) / space_width * space_width; + } + else if (ch == GRTEXT_FORMAT_CHAR) { + if ((i+1) >= strsize) + break; + line_width = ((int)str[i+1]*GRTEXT_FORMAT_SCALAR); + i++; + } + else if (ch == '\n') { + if (line_width > max_width) { + max_width = line_width; + } + line_width = 0; + } + else { + width = CHAR_WIDTH(Grtext_font, ch); + line_width += (width+Grtext_spacing+CHAR_SPACING(Grtext_font, ch, ch2)); + } + } + else rgb_define_mode++; + } + + if (line_width > max_width) { + max_width = line_width; + } + return (max_width) ? max_width - Grtext_spacing : 0; +} + + +// returns width of text using a font template +int grtext_GetTextLineWidthTemplate(const tFontTemplate *ft, const char *str) +{ + int max_width=0, i, line_width=0, strsize = strlen(str); + int rgb_define_mode = 0; + + for (i = 0; i < strsize; i++) + { + int width; + char ch = str[i], ch2 = str[i+1]; + + // note that if we hit the GR_COLOR_CHAR then the next three values should + // not count when defining the width of the line. + if (rgb_define_mode == 3) rgb_define_mode = 0; + else if (ch == GR_COLOR_CHAR) rgb_define_mode = 1; + if (!rgb_define_mode) { + if (ch == '\t') { //tab char + int space_width; + space_width = (CHAR_WIDTH_TEMP(ft, ' ')+Grtext_spacing) * Grtext_tabspace; + line_width = (line_width + space_width) / space_width * space_width; + } + else if (ch == GRTEXT_FORMAT_CHAR) { + if ((i+1) >= strsize) + break; + line_width = ((int)str[i+1]*GRTEXT_FORMAT_SCALAR); + i++; + } + else if (ch == '\n') { + if (line_width > max_width) { + max_width = line_width; + } + line_width = 0; + } + else { + width = CHAR_WIDTH_TEMP(ft, ch); + line_width += (width+Grtext_spacing+CHAR_SPACING_TEMP(ft, ch, ch2)); + } + } + else rgb_define_mode++; + } + + if (max_width < line_width) { + max_width = line_width; + } + return (max_width) ? max_width - Grtext_spacing : 0; +} + + + +/* These internal routines just draw a line of text. depending on the function + we clip or don't clip +*/ +void grtext_DrawTextLineClip(int x, int y, char *str) +{ + int ch_y, ch_x, ch_w, ch_h; // what part of the character to draw + int i, cur_x, draw_x, draw_y; // where to draw character section + int h; + tCharBlt cbi; + int strsize = strlen(str); + +/* by clipping, we should first determine what our vertical clipping is. then + go through each character in the line and determine what is totally clipped, + partially clipped and by how much, and not clipped at all and draw accordingly +*/ + h = CHAR_HEIGHT(Grtext_font); + ch_y = 0; + ch_h = h; + draw_y = y; +// determine each character bitmap y and height to blt. + if (CLIP_TOP >= y) { + ch_y = CLIP_TOP - y; + draw_y = CLIP_TOP; + } + if (CLIP_BOTTOM < (y+CHAR_HEIGHT(Grtext_font))) { + ch_h = CLIP_BOTTOM - y; + } + ch_h = ch_h - ch_y; // do this to clip both top and bottom + + cur_x = x; + for (i = 0; i < strsize; i++) + { + ubyte ch, ch2; + int w; + ch = (ubyte)str[i]; + ch2 = (ubyte)str[i+1]; + + w = CHAR_WIDTH(Grtext_font, ch); + + if (ch == GR_COLOR_CHAR) { + ddgr_color col; + if ((i+3) >= strsize) + break; // This shouldn't happen! bad string! + col = GR_RGB(str[i+1], str[i+2], str[i+3]); + rend_SetFlatColor(col); + // rend_SetCharacterParameters(col, col, col, col); + i += 3; + } + else if (ch == '\t') { //tab char + int space_width; + space_width = (CHAR_WIDTH(Grtext_font, ' ')+Grtext_spacing) * Grtext_tabspace; + cur_x = (cur_x + space_width) / space_width * space_width; + } + else if (((cur_x+w) < CLIP_LEFT) || (cur_x > CLIP_RIGHT)) { + cur_x += (Grtext_spacing+w); + } + else if (ch == GRTEXT_FORMAT_CHAR) { + if ((i+1) >= strsize) + break; // This shouldn't happen! bad string! + cur_x = x + ((int)str[i+1]*GRTEXT_FORMAT_SCALAR); + i++; + } + else if (ch == ' ') { + cur_x += (CHAR_WIDTH(Grtext_font, ' ') + Grtext_spacing+CHAR_SPACING(Grtext_font, ch,ch2)); + } + else { + ch_x = 0; + ch_w = w; + draw_x = cur_x; + if (CLIP_LEFT > cur_x) { + ch_x = CLIP_LEFT - cur_x; + draw_x = CLIP_LEFT; + } + if (CLIP_RIGHT < (cur_x+w)) { + ch_w = CLIP_RIGHT - cur_x; + } + ch_w = ch_w - ch_x; + + if (ch_x == 0 && ch_w == w && ch_y == 0 && ch_h == h) { + cbi.ch = ch; + cbi.clipped = false; // if =1, use sx,sy,sw,sh + cbi.x = draw_x; + cbi.y = draw_y; + cbi.dsw = cbi.dsh = Grtext_scale; + } + else { + cbi.ch = ch; + cbi.clipped = true; // if =1, use sx,sy,sw,sh + cbi.x = draw_x; + cbi.y = draw_y; + cbi.dsw = cbi.dsh = Grtext_scale; // this should be useless since ch_w and ch_h are scaled values + cbi.sx = ch_x; + cbi.sy = ch_y; + cbi.sw = ch_w; + cbi.sh = ch_h; + } + cur_x = grfont_BltChar(Grtext_font, &cbi); + cur_x+= (Grtext_spacing +CHAR_SPACING(Grtext_font, ch, ch2)); + } + } +} + + +void grtext_DrawTextLine(int x, int y, char *str) +{ + int i, cur_x; + tCharBlt cbi; + int strsize = strlen(str); + +/* by clipping, we should first determine what our vertical clipping is. then + go through each character in the line and determine what is totally clipped, + partially clipped and by how much, and not clipped at all and draw accordingly + + perform string drawing without viewport clipping. + supports bitmap fonts or color(alpha) mapped fonts. +*/ + cur_x = x; + for (i = 0; i < strsize; i++) + { + ubyte ch, ch2; + int w; + ch = (ubyte)str[i]; + ch2 = (ubyte)str[i+1]; + w = CHAR_WIDTH(Grtext_font, ch); + + if (ch == GR_COLOR_CHAR) { + ddgr_color col; + if ((i+3) >= strsize) + break; // This shouldn't happen! bad string! + col = GR_RGB(str[i+1], str[i+2], str[i+3]); + rend_SetFlatColor(col); +// rend_SetCharacterParameters(col, col, col, col); + i += 3; + } + else if (ch == '\t') { //tab char + int space_width; + space_width = (CHAR_WIDTH(Grtext_font, ' ')+Grtext_spacing) * Grtext_tabspace; + cur_x = (cur_x + space_width) / space_width * space_width; + } + else if (ch == GRTEXT_FORMAT_CHAR) { + if ((i+1) >= strsize) + break; // This shouldn't happen! bad string! + cur_x = x + ((int)str[i+1]*GRTEXT_FORMAT_SCALAR); + i++; + } + else if (ch == ' ') { + cur_x += (CHAR_WIDTH(Grtext_font, ' ') + Grtext_spacing); + } + else { + cbi.ch = ch; + cbi.clipped = false; // if =1, use sx,sy,sw,sh + cbi.x = cur_x; + cbi.y = y; + cbi.dsw = cbi.dsh = Grtext_scale; + cur_x = grfont_BltChar(Grtext_font, &cbi); + cur_x+= (Grtext_spacing + CHAR_SPACING(Grtext_font, ch, ch2)); + } + } +} + + diff --git a/grtext/grtextlib.h b/grtext/grtextlib.h new file mode 100644 index 000000000..6ed7f2f54 --- /dev/null +++ b/grtext/grtextlib.h @@ -0,0 +1,41 @@ +/* + * $Logfile: /DescentIII/Main/grtext/grtextlib.h $ + * $Revision: 2 $ + * $Date: 4/27/98 3:46p $ + * $Author: Samir $ + * + * + * + * $Log: /DescentIII/Main/grtext/grtextlib.h $ + * + * 2 4/27/98 3:46p Samir + * scaling fonts. + * + * 1 12/29/97 3:24p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef GRTEXTLIB_H +#define GRTEXTLIB_H + +#include "grtext.h" +#include "pstypes.h" + +typedef struct tCharBlt +{ + ushort ch; + ushort clipped; // if =1, use sx,sy,sw,sh + int x, y; + float dsw, dsh; // scale vertical and horiz. + int sx, sy; // source rectangle within character in pixels. + int sw, sh; +} +tCharBlt; + + +// render a character +int grfont_BltChar(int font, tCharBlt *cbi); + +#endif \ No newline at end of file diff --git a/grtext/textaux.cpp b/grtext/textaux.cpp new file mode 100644 index 000000000..9106e5b40 --- /dev/null +++ b/grtext/textaux.cpp @@ -0,0 +1,203 @@ +/* +* $Logfile: /DescentIII/Main/grtext/textaux.cpp $ +* $Revision: 6 $ +* $Date: 5/19/99 10:00p $ +* $Author: Jeff $ +* +* Auxillary Text functions (helper function, not necessarily belonging to grtext) +* +* $Log: /DescentIII/Main/grtext/textaux.cpp $ + * + * 6 5/19/99 10:00p Jeff + * fixed word-wrap not to wrap words that are bigger than the line + * + * 5 11/03/98 7:04p Samir + * made Grtext_spacing global so word wrapper could access it. + * + * 4 11/03/98 3:12p Jeff + * fixed wordwrap so it doesn't modify source buffer + * + * 3 7/14/98 2:47p Jeff + * added textaux_ClipString + * + * 2 7/14/98 11:53a Samir + * moved textaux to it's own libtary again. + * + * 1 7/13/98 4:41p Samir + * + * 2 7/11/98 9:14p Jeff + * initial creation (moved from TelCom) +* +* $NoKeywords: $ +*/ + +#include "grtext.h" +#include +#include +#include + +// textaux_WordWrap +// +// Given a buffer of text, and an empty buffer of same size +// it will break the source buffer up into lines (seperated by /n) of size width or smaller (in pixels). +// All /n within the source buffer are preserved. Source buffer is also not changed. +void textaux_WordWrap(const char *src,char *dest,int width,int font) +{ + //make sure we are kosher with the values past in + if( (!src) || (!dest) ) + return; + if( src[0]=='\0' ) + return; + + char *last_word; //points to the character after the last complete word + char *start_line; //points to the starting char of the line + //char *destpos; //points to the current position of the dest buffer + int curr_width; //current width + int index; //index into source string + bool done = false; //if we are done adjusting for word-wrap + + //copy the source buffer into the destination buffer, where all work will go + strcpy(dest,src); + + //initialize vars + last_word = start_line = dest; + curr_width = 0; + index = 0; + + int num_words_on_line = 0; + + while(!done) + { + curr_width = 0; + index = 0; + num_words_on_line = 0; + + while(curr_width<=width || num_words_on_line==0) + { + switch(start_line[index]) + { + case '\0': + //we are done with everything + done = true; + case '\n': + //we are done with this line + curr_width = width+1; + case ' ': + //update last_word + last_word = &start_line[index]; + num_words_on_line++; + break; + } + if( (start_line[index]!='\0') && (start_line[index]!='\n') ) + curr_width += (grfont_GetCharWidth(font,start_line[index]) + Grtext_spacing); + index++; + } + //put a newline char + if(!done) + { + last_word[0] = '\n'; + } + + //copy from start_line->last_word into the dest buffer + start_line += (last_word-start_line+1); + last_word = start_line; + } +} + +// textaux_CopyTextLine +// +// This function goes hand-in-hand with textaux_WordWrap(). Given a buffer of data it will fill in +// the dest buffer until it hits a /n or /0. It returns a pointer to the start position of the next line, +// or NULL if it's done with the buffer (it hit a /0). +char *textaux_CopyTextLine(char *src,char *dest) +{ + //make sure src and dest are allocated + if(!src) + { + if(dest) + dest[0] = '\0'; + return NULL; + } + if(!dest) + return NULL; + //see if we are at the end of the src + if( (src[0]=='\0') ) + { + dest[0] = '\0'; + return NULL; + } + int i; + i = 0; + //find the end + while( (src[i]!='\n') && (src[i]!='\0') ) i++; + if(src[i]=='\0') + { + //no more lines left after this + strncpy(dest,src,i); + dest[i] = '\0'; + return NULL; + } + else + { + //we hit a newline char + strncpy(dest,src,i); + dest[i] = '\0'; + i++; + return &src[i]; + } +} + +// textaux_ClipString +// Given a width (in pixels), and a string, this function will truncate the string +// to at most width pixels. If the end parameter is not 0, then that char is attached to +// the end of the string if it has to clip(the char's width is taken into consideration). +// It is based off the current font. if horizont_ratio is given it is used to correct for +// possible different sized hud fonts. For instance, if this string is going to be printed on +// the hud, then you should always pass (DEFAULT_HUD_WIDTH/((float)*Game_window_w)) as the +// horizont_ratio paramter. +void textaux_ClipString(int width,char *string,float horizont_ratio,char end) +{ + if(!string) + return; + + float ratio = horizont_ratio; + + int string_length = strlen(string); + char arrow_string[2]; + int arrow_length = 0; + + if(end){ + sprintf(arrow_string,"%c",end); + arrow_length = (int)(ratio * ((float)grtext_GetTextLineWidth(arrow_string))); + } + + if(width=width){ + //We have to clip + size--; + if(end){ + string[size] = end; + string[size+1] = '\0'; + } + return; + } + //replace the char and move to the next + string[size] = save; + size++; + save = string[size]; + } + //The string didn't need to be clipped +} + diff --git a/lib/3d.h b/lib/3d.h new file mode 100644 index 000000000..4687547cc --- /dev/null +++ b/lib/3d.h @@ -0,0 +1,433 @@ +/* + * $Logfile: /DescentIII/Main/Lib/3d.h $ + * $Revision: 36 $ + * $Date: 10/21/99 9:27p $ + * $Author: Jeff $ + * + * Header file for Descent 3 3d library + * + * $Log: /DescentIII/Main/Lib/3d.h $ + * + * 36 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 35 5/13/99 10:57p Jason + * added extra parameter to g3_DrawPoly + * + * 34 4/22/99 5:14p Matt + * Changed the and & or fields in the grCodes struct to cc_and & cc_or, + * since the former names didn't work with some compilers. + * + * 33 3/26/99 3:53p Jason + * returned clip codes to their original order + * + * 32 3/26/99 3:25p Jason + * fixed remaining mirror problems + * + * 31 2/19/99 4:26p Jason + * more work on Katmai support + * + * 30 2/18/99 5:45p Jason + * added custom clip plane + * + * 29 1/26/99 6:39p Jason + * added wall effects code + * + * 28 5/25/98 3:45p Jason + * made g3_DrawPoly return 1 if it drew + * + * 27 5/20/98 5:43p Jason + * incremental checkin for bumpmapping + * + * 26 5/19/98 12:27p Jason + * cleaned up some 3d stuff + * + * 25 5/14/98 12:56p Jason + * changes to help lower memory usage + * + * 24 4/01/98 12:02p Jason + * incremental checkin for rendering changes + * + * 23 2/05/98 11:14a Jason + * added g3_DrawPlanarRotatedBitmap function + * + * 22 1/28/98 5:36p Jason + * added streamer weapons + * + * 21 1/07/98 3:57p Jason + * fixed spelling of triangulation (doh!) + * + * 20 1/07/98 3:55p Jason + * made Triangulate test a user callable function + * + * + * 19 12/29/97 5:49p Samir + * 3d.h doesn't include gr.h anymore. + * + * 18 12/23/97 10:59a Samir + * Temporarily put gr.h back in. + * + * 17 12/22/97 7:21p Samir + * replaced ddvid.h with grdefs.h + * + * 16 12/22/97 6:57p Samir + * Removed instances of gr.h + * + * 15 12/19/97 12:20p Jason + * changes for better 2d/3d system integration + * + * 14 11/10/97 12:10p Jason + * implemented g3_GetMatrixScale + * + * 13 10/22/97 4:18p Jason + * added g3_DrawRotatedBitmap + * + * 12 10/17/97 5:09p Jason + * added more fireball stuff + * + * 11 10/15/97 2:27p Jason + * changes for prelim fireball stuff + * + * 10 10/13/97 3:56p Jason + * made a better 3d bitmap system + * + * 9 9/16/97 4:23p Matt + * Made ClipPolygon() a public function (g3_ClipPolygon()), and added + * g3_FreeTempPoints(). + * + * 8 9/10/97 3:50p Jason + * added code for instancing the unscaled view matrix + * + * 7 9/04/97 11:59a Matt + * Added new function g3_ResetFarClipZ() + * + * 6 9/03/97 2:20p Samir + * Added function to get viewer matrix. + * + * 5 9/02/97 11:07a Jason + * added alpha component to uvl struct + * + * 4 8/20/97 4:17p Matt + * Added g3_GetViewPosition() + * + * 3 8/04/97 6:40p Jason + * added 2nd level UV floats for lightmaps + * + * 2 7/16/97 4:12p Matt + * Changed and cleaned up a bunch of stuff: + * + * 1. Modified clipper to not change the order of the vertices + * 2. Cleaned up drawer/clipper integration + * 3. Renamed internal functions to conform to nameing standards + * 4. Changed tmap drawer to not take list of uvls. UVL values must now + * be added to the points by the caller + * 5. Renamed g3_DrawTmap() to g3_DrawPoly(), and killed the old + * g3_DrawPoly() + * 6. Made line always do 3d clip if they go offscreen + * + * 14 6/25/97 5:45p Matt + * Made 3D draw functions void, to get rid of warnings about bool return + * values that were never used anyway. + * + * 13 5/08/97 8:11p Matt + * Added code to create subwindows that show a subsection of the 3d view + * + * 12 4/10/97 2:24p Jason + * added the existance of a far clip plane. + * + * 11 4/01/97 5:17p Matt + * Got g3_DrawSphere() working + * + * 10 2/28/97 10:43a Matt + * Added g3_DrawBox() to draw a wireframe box (used to show the current + * object in the editor). + * + * 9 2/27/97 5:01p Jason + * added 3d model functionality and created model library + * + * 8 2/26/97 6:00p Matt + * Renamed 3d lib structs for D3 naming convention + * + * 7 2/21/97 7:29p Matt + * Added rgb fields to g3s_uvl struct + * + * 6 2/19/97 3:32p Matt + * gr_DrawPoly() now takes the color + * + * 5 2/07/97 5:58p Matt + * Took out now-obsolete jason_point struct + * Renamed vector.h to vecmat.h + * + * $NoKeywords: $ + */ + +#ifndef _3D_H +#define _3D_H + +#include "pstypes.h" +#include "vecmat.h" //the vector/matrix library +#include "grdefs.h" +#include "float.h" + +extern int g3d_interp_outline; //if on, polygon models outlined in white + +extern vector Matrix_scale; //how the matrix is currently scaled + +//Structure for storing u,v,light values. This structure doesn't have a +//prefix because it was defined somewhere else before it was moved here +typedef struct g3UVL +{ + //texture coordinates + float u,v; + float u2,v2; + + union + { + float l; //intensity lighting + float r; + }; + float g,b,a; //rgba lighting +} g3UVL; + +// Structure to store clipping codes in a word +typedef struct g3Codes +{ + ubyte cc_or,cc_and; +} g3Codes; + +//flags for point structure +#define PF_PROJECTED 1 //has been projected, so sx,sy valid +#define PF_FAR_ALPHA 2 //past fog zone +#define PF_TEMP_POINT 4 //created during clip +#define PF_UV 8 //has uv values set +#define PF_L 16 //has lighting values set +#define PF_RGBA 32 //has RGBA lighting values set +#define PF_UV2 64 //has lightmap uvs as well +#define PF_ORIGPOINT 128 + +//clipping codes flags +#define CC_OFF_LEFT 1 +#define CC_OFF_RIGHT 2 +#define CC_OFF_BOT 4 +#define CC_OFF_TOP 8 +#define CC_OFF_FAR 16 +#define CC_OFF_CUSTOM 32 +#define CC_BEHIND 128 + +//Used to store rotated points for mines. Has frame count to indicate +//if rotated, and flag to indicate if projected. +typedef struct g3Point +{ + float p3_sx,p3_sy; //screen x&y + ubyte p3_codes; //clipping codes + ubyte p3_flags; //projected? + short p3_pad; //keep structure longword aligned + vector p3_vec; //x,y,z of rotated point + vector p3_vecPreRot; //original XYZ of the point + g3UVL p3_uvl; //uv & lighting values +} g3Point; + +//macros to reference x,y,z elements of a 3d point +#define p3_x p3_vec.x +#define p3_y p3_vec.y +#define p3_z p3_vec.z + +//macros to reference individual elements of the uvls struct +#define p3_u p3_uvl.u +#define p3_v p3_uvl.v +#define p3_l p3_uvl.l +#define p3_r p3_uvl.r +#define p3_g p3_uvl.g +#define p3_b p3_uvl.b +#define p3_a p3_uvl.a +#define p3_u2 p3_uvl.u2 +#define p3_v2 p3_uvl.v2 + +//Functions in library + +//3d system startup and shutdown: + +//initialize the 3d system +void g3_Init(void); + +//close down the 3d system +void g3_Close(void); + + +//Frame setup functions: + +//start the frame, specifying view position, matrix, & zoom +void g3_StartFrame(vector *view_pos,matrix *view_matrix,float zoom); + +//end the frame +void g3_EndFrame(void); + +//get the current view position +void g3_GetViewPosition(vector *vp); + +// returns the current view matrix +void g3_GetViewMatrix(matrix *mat); + +// returns the current unscaled view matrix +void g3_GetUnscaledMatrix(matrix *mat); + + +//Instancing + +//instance at specified point with specified orientation +void g3_StartInstanceMatrix(vector *pos,matrix *orient); + +//instance at specified point with specified orientation +void g3_StartInstanceAngles(vector *pos,angvec *angles); + +//pops the old context +void g3_DoneInstance(); + +//Misc utility functions: + +//get current field of view. Fills in angle for x & y +void g3_GetFOV(float *fov_x,float *fov_y); + +//get zoom. For a given window size, return the zoom which will achieve +//the given FOV along the given axis. +float g3_GetZoom(char axis,float fov,short window_width,short window_height); + +//returns the normalized, unscaled view vectors +void g3_GetViewVectors(vector *forward,vector *up,vector *right); + +//returns true if a plane is facing the viewer. takes the unrotated surface +//normal of the plane, and a point on it. The normal need not be normalized +bool g3_CheckNormalFacing(vector *v,vector *norm); + +//Point definition and rotation functions: + +//returns codes_and & codes_or of a list of points numbers +g3Codes g3_CheckCodes(int nv,g3Point **pointlist); + +//rotates a point. returns codes. does not check if already rotated +ubyte g3_RotatePoint(g3Point *dest,vector *src); + +//projects a point +void g3_ProjectPoint(g3Point *point); + +//calculate the depth of a point - returns the z coord of the rotated point +float g3_CalcPointDepth(vector *pnt); + +//from a 2d point, compute the vector through that point +void g3_Point2Vec(vector *v,short sx,short sy); + +//code a point. fills in the p3_codes field of the point, and returns the codes +ubyte g3_CodePoint(g3Point *point); + +//delta rotation functions +vector *g3_RotateDeltaX(vector *dest,float dx); +vector *g3_RotateDeltaY(vector *dest,float dy); +vector *g3_RotateDeltaZ(vector *dest,float dz); +vector *g3_RotateDeltaVec(vector *dest,vector *src); +ubyte g3_AddDeltaVec(g3Point *dest,g3Point *src,vector *deltav); + +//Drawing functions: +//draw a polygon +//Parameters: nv - the number of verts in the poly +// pointlist - a pointer to a list of pointers to points +// bm - the bitmap handle if texturing. ignored if flat shading +int g3_DrawPoly(int nv,g3Point **pointlist,int bm,int map_type=0,g3Codes *clip_codes=NULL); + +//draw a sortof sphere - i.e., the 2d radius is proportional to the 3d +//radius, but not to the distance from the eye +void g3_DrawSphere(ddgr_color color,g3Point *pnt,float rad); + +//like g3_DrawPoly(), but checks to see if facing. If surface normal is +//NULL, this routine must compute it, which will be slow. It is better to +//pre-compute the normal, and pass it to this function. When the normal +//is passed, this function works like g3_CheckNormalFacing() plus +//g3_DrawPoly(). +void g3_CheckAndDrawPoly(int nv,g3Point **pointlist,int bm,vector *norm,vector *pnt); + +//draws a line. takes two points. +void g3_DrawLine(ddgr_color color,g3Point *p0,g3Point *p1); + +//draws a bitmap with the specified 3d width & height +//returns 1 if off screen, 0 if drew +void g3_DrawBitmap(vector *pos,float width,float height,int bm,int color=-1); + +// Draws a bitmap that has been rotated about its center. Angle of rotation is passed as 'rot_angle' +void g3_DrawRotatedBitmap (vector *pos,angle rot_angle,float width,float height,int bm,int color=-1); + +//specifies 2d drawing routines to use instead of defaults. Passing +//NULL for either or both restores defaults +void g3_SetSpecialRender(void (*tmap_drawer)(),void (*flat_drawer)(),int (*line_drawer)()); + +//Object functions: + +//init code for bitmap models +void g3_InitPolygonModel(void *model_ptr); + +//un-initialize, i.e., convert color entries back to RGB15 +void g3_UninitPolygonModel(void *model_ptr); + +//alternate interpreter for morphing object +void g3_DrawMorphingModel(void *model_ptr,int *model_bitmaps,angvec *anim_angles,float light,vector *new_points); + +//this remaps the 15bpp colors for the models into a new palette. It should +//be called whenever the palette changes +void g3_RemapInterpColors(void); + +//Draw a wireframe box aligned with the screen. Used for the editor. +//Parameters: color - the color to draw the lines +// pnt - the center point +// rad - specifies the width/2 & height/2 of the box +void g3_DrawBox(ddgr_color color,g3Point *pnt,float rad); + +// Sets up a custom clipping plane - g3_StartFrame must be called before this is called +void g3_SetCustomClipPlane (ubyte state,vector *pnt,vector *normal); + +// sets the z distance of the far clipping plane +void g3_SetFarClipZ (float z); + +//Disables the far clip plane +inline void g3_ResetFarClipZ() +{ + g3_SetFarClipZ(FLT_MAX); +} + +//Clips a polygon +//Parameters: pointlist - pointer to a list of pointers to points +// nv - the number of points in the polygon +// cc - the clip codes for this polygon +//Returns: a pointer to a list of pointer of points in the clipped polygon +//NOTE: You MUST call g3_FreeTempPoints() when you're done with the clipped polygon +g3Point **g3_ClipPolygon(g3Point **pointlist,int *nv,g3Codes *cc); + +//Free up any temp points (created by the clipper) in the given pointlist +//Parameters: pointlist - pointer to list of pointers to points, returned by g3_ClipPolygon() +// nv - the number of points in pointlist +void g3_FreeTempPoints(g3Point **pointlist,int nv); + +// Gets the matrix scale vector +void g3_GetMatrixScale (vector *matrix_scale); + +// Sets the triangulation test to on or off +void g3_SetTriangulationTest (int state); + +//draws a line based on the current setting of render states. takes two points. returns true if drew +void g3_DrawSpecialLine(g3Point *p0,g3Point *p1); + +// Draws a bitmap on a specific plane. Also does rotation. Angle of rotation is passed as 'rot_angle' +void g3_DrawPlanarRotatedBitmap (vector *pos,vector *norm,angle rot_angle,float width,float height,int bm); + + + +void g3_TransformVert( float res[4], float pt[4], float a[4][4] ); +void g3_TransformMult( float res[4][4], float a[4][4], float b[4][4] ); +void g3_TransformTrans( float res[4][4], float t[4][4] ); +void g3_GetModelViewMatrix( const vector *viewPos, const matrix *viewMatrix, float *mvMat ); +extern float gTransformViewPort[4][4]; +extern float gTransformProjection[4][4]; +extern float gTransformModelView[4][4]; +extern float gTransformFull[4][4]; + +void g3_RefreshTransforms(bool usePassthru); + +#endif + diff --git a/lib/Adecode.h b/lib/Adecode.h new file mode 100644 index 000000000..e36ffb0a3 --- /dev/null +++ b/lib/Adecode.h @@ -0,0 +1,43 @@ +#ifndef AUDIODECODE_H_ +#define AUDIODECODE_H_ + +namespace AudioDecoder +{ + typedef unsigned int uint32; + typedef signed int sint32; + typedef unsigned short uint16; + typedef signed short sint16; + typedef unsigned char uint8; + typedef signed char sint8; + + class IAudioDecoder + { + public: + virtual ~IAudioDecoder(){} + + // Read data from the audio decoder. + // pBuffer: The buffer to receive the data from + // amount: How much data to read + // Returns the number of bytes read - zero when we're at the end of the file + virtual uint32 Read( void* pBuffer, uint32 amount ) = 0; + }; + + // Create an audio decoder + // You supply a function for reading bytes from the compressed data via a + // void* pData handle, and the handle itself (typically a FILE *). + // Create_AudioDecoder returns a new AudioDecoder which can be used to + // read uncompressed decoded data from the compressed stream, + // and also returns the number of channels (1 or 2), the sample rate + // (e.g. 22050), and the number of samples contained in the compressed file + // (in case you want to pre-allocate a buffer to load them all into memory). + typedef sint32 (*ReadDataFunction)( void* pData, void* pBuffer, unsigned int amount ); + IAudioDecoder* CreateDecoder( ReadDataFunction reader, void* pData, uint32& numChannels, uint32& sampleRate, uint32& sampleCount ); + + // Optional interface for supplying your own malloc and free functions + // Default is to use standard malloc and free. + typedef void *(*MemoryAllocFunc)(uint32 size); + typedef void (*MemoryFreeFunc)(void *p); + void RegisterMemoryFunctions( MemoryAllocFunc memAlloc, MemoryFreeFunc memFree ); +} + +#endif diff --git a/lib/Aencode.h b/lib/Aencode.h new file mode 100644 index 000000000..c739884a8 --- /dev/null +++ b/lib/Aencode.h @@ -0,0 +1,15 @@ +#ifndef AENCODE_H_ +#define AENCODE_H_ + +#include + +typedef long ReadSampleFunction( void *data ); +enum AudioError +{ + ReadSampleEof = 0x80000000, +}; + +unsigned long AudioEncode( ReadSampleFunction *read, void *data, unsigned channels, unsigned sample_rate, float volume, + FILE *out, int levels, int samples_per_subband, float comp_ratio ); + +#endif diff --git a/lib/AppConsole.h b/lib/AppConsole.h new file mode 100644 index 000000000..dffa05a3a --- /dev/null +++ b/lib/AppConsole.h @@ -0,0 +1,30 @@ +/* + * $Logfile: /DescentIII/Main/lib/AppConsole.h $ + * $Revision: 3 $ + * $Date: 10/30/98 5:52p $ + * $Author: Jeff $ + * + * + * + * $Log: /DescentIII/Main/lib/AppConsole.h $ + * + * 3 10/30/98 5:52p Jeff + * implemented up arrow for last command, and esc to clear the line + * + * 2 9/16/98 5:02p Samir + * reimplemented X style console. + * + * 1 9/14/98 4:02p Samir + * new console library. + * + * $NoKeywords: $ + */ + +#ifndef APPCONSOLE_H +#define APPCONSOLE_H + +#define CON_MAX_STRINGLEN 768 +void con_Printf(const char *fmt, ...); +bool con_Input(char *buf, int buflen); + +#endif \ No newline at end of file diff --git a/lib/BYTESWAP.H b/lib/BYTESWAP.H new file mode 100644 index 000000000..95709a177 --- /dev/null +++ b/lib/BYTESWAP.H @@ -0,0 +1,116 @@ +/* + * $Logfile: /DescentIII/Main/lib/BYTESWAP.H $ + * $Revision: 8 $ + * $Date: 4/19/00 5:32p $ + * $Author: Matt $ + * + * Byteswapping macros (for big-endian machines) + * + * $Log: /DescentIII/Main/lib/BYTESWAP.H $ + * + * 8 4/19/00 5:32p Matt + * From Duane for 1.4 + * Added casts to SWAPSHORT and SWAPINT macros + * + * 7 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 6 5/09/99 11:41p Jeff + * function free + * + * 5 5/05/99 5:27a Jeff + * renamed endian.h to psendian.h + * + * 4 5/01/99 2:52p Jeff + * added automatic endian detection of the system + * + * 3 4/17/99 7:49p Jeff + * for some reason Linux thinks it's big endian, temp fix (undef) until I + * get around to writting a endian check function + * + * 2 1/09/99 4:38p Jeff + * added some ifdefs and fixes to get files to compile under Linux + * + * 5 5/15/97 2:22p Matt + * Fixed (hopefully; it's not tested yet) byteswapping for floats + * + * 4 2/10/97 2:22p Matt + * Added cast + * + * 3 2/10/97 2:14p Matt + * Added BIG_ENDIAN define, & INT_FLOAT() macro + * + * $NoKeywords: $ + */ + +#ifndef _BYTESWAP_H +#define _BYTESWAP_H + +#include "pstypes.h" + +/* +#include "psendian.h" + +#define SWAPSHORT(x) ( ((x) << 8) | (((ushort)(x)) >> 8) ) +#define SWAPINT(x) ( ((x) << 24) | (((ulong)(x)) >> 24) | (((x) & 0x0000ff00) << 8) | (((x) & 0x00ff0000) >> 8) ) + +//Stupid function to trick the compiler into letting me byteswap a float +inline float SWAPFLOAT(float x) +{ + int i = SWAPINT(*((int *) &(x))); + return *((float *) &(i)); +} + +// INTEL_ assumes the returned value will be in "Little Endian Format" +#define INTEL_INT(x) Endian_SwapInt(x) +#define INTEL_SHORT(x) Endian_SwapShort(x) +#define INTEL_FLOAT(x) Endian_SwapFloat(x) + +// MOTOROLA_ assumes the returned value will be in "Big Endian Format" +#define MOTOROLA_INT(x) SWAPINT(Endian_SwapInt(x)) +#define MOTOROLA_SHORT(x) SWAPSHORT(Endian_SwapShort(x)) +#define MOTOROLA_FLOAT(x) SWAPFLOAT(Endian_SwapFloat(x)) +*/ +#define SWAPSHORT(x) (short)(0xFFFF & ( ((x) << 8) | (((ushort)(x)) >> 8) )) +#define SWAPINT(x) (int)( ((x) << 24) | (((ulong)(x)) >> 24) | (((x) & 0x0000ff00) << 8) | (((x) & 0x00ff0000) >> 8) ) + +//Stupid function to trick the compiler into letting me byteswap a float +inline float SWAPFLOAT(float x) +{ + int i = SWAPINT(*((int *) &(x))); + return *((float *) &(i)); +} + + +//Default is little endian, so change for Macintosh +#if (MACOSX && MACOSXPPC) + #define OUTRAGE_BIG_ENDIAN +#endif + +#if (defined __LINUX__) && (!defined(MACOSX)) + #include + + #if BYTE_ORDER == BIG_ENDIAN + #define OUTRAGE_BIG_ENDIAN + #endif +#endif + +#ifndef OUTRAGE_BIG_ENDIAN + + + #define INTEL_INT(x) x + #define INTEL_SHORT(x) x + #define INTEL_FLOAT(x) x + #define MOTOROLA_INT(x) SWAPINT(x) + #define MOTOROLA_SHORT(x) SWAPSHORT(x) + #define MOTOROLA_FLOAT(x) SWAPFLOAT(x) +#else + #define INTEL_INT(x) SWAPINT(x) + #define INTEL_SHORT(x) SWAPSHORT(x) + #define INTEL_FLOAT(x) SWAPFLOAT(x) + #define MOTOROLA_INT(x) x + #define MOTOROLA_SHORT(x) x + #define MOTOROLA_FLOAT(x) x +#endif + +#endif diff --git a/lib/CFILE.H b/lib/CFILE.H new file mode 100644 index 000000000..47f52df9b --- /dev/null +++ b/lib/CFILE.H @@ -0,0 +1,298 @@ +/* + * $Logfile: /DescentIII/Main/lib/CFILE.H $ + * $Revision: 16 $ + * $Date: 10/18/99 1:27p $ + * $Author: Kevin $ + * + * Functions for reading & writing files. Includes code to read from libraries. + * + * $Log: /DescentIII/Main/lib/CFILE.H $ + * + * 16 10/18/99 1:27p Kevin + * Added cf_IsFileInHog + * + * 15 9/14/99 7:49p Jeff + * added cf_OpenFileInLibrary() to force a file to be opened from a + * specific library. Added a way to get a crc of a file given it's CFILE + * *. + * + * 14 5/20/99 5:32p Matt + * Store a lib handle, instead of a lib pointer, in the cfile struct. + * This will keep us from using a pointer to lib that's already been + * closed. + * + * 13 3/22/99 6:26p Matt + * Cleaned up error handling in cfile and editor level loads. + * + * 12 1/07/99 10:51p Jeff + * added psglob and support to do find in files for hog files + * + * 11 11/16/98 3:49p Jason + * changes for manage system + * + * 10 11/11/98 2:58p Jeff + * added cf_ClearAllSearchPaths() function + * + * 9 10/22/98 10:48a Matt + * Added code to keep the library file open all the time, which will + * hopefully speed up file loads. + * + * 8 8/14/98 6:31p Matt + * Changed comment + * + * 7 7/28/98 12:27p Kevin + * Added CRC function + * + * 6 7/09/98 8:33p Samir + * Added cf_Rewind. + * + * 5 3/19/98 3:18p Samir + * enforce constant char* arguments when needed. done in CFILE and bitmap + * libraries as well as ddio. + * + * 4 2/26/98 11:01a Jason + * added cf_ChangeFileAttributes function + * + * 3 2/15/98 7:44p Matt + * Added groovy try/catch/throw error checking for cfile functions + * + * 2 12/17/97 4:09p Jason + * fixed compiler warning + * + * 11 5/23/97 2:27p Matt + * Text file newlines now handled internally. + * Don't look in libs for files opened for writing. + * Changed error message to print out errno. + * + * 10 4/03/97 4:34p Jason + * added CopyFileTime to the cfile, ddio libs + * + * 9 3/03/97 6:21p Matt + * Changed cfile functions to use D3 naming convention + * + * $NoKeywords: $ + */ + +#ifndef CFILE_H +#define CFILE_H + +#include + +#include "pstypes.h" + +struct library; + +//The structure for a CFILE +typedef struct CFILE { + char *name; //pointer to filename + FILE *file; //the file itself (on disk) or the HOG + int lib_handle; //the handle of the library, or -1 + int size; //length of this file + int lib_offset; //offset into HOG of start of file, or 0 if on disk + int position; //current position in file + int flags; //see values below +} CFILE; + +//Defines for cfile_error +#define CFE_READING 1 +#define CFE_WRITING 2 + +//The structure thrown by a cfile error +typedef struct { + int read_write; //reading or writing? See defines. + char *msg; //the error message + CFILE *file; //the file that got the error +} cfile_error; + +//Flags for CFILE struct +#define CF_TEXT 1 //if this bit set, file is text +#define CF_WRITING 2 //if bit set, file opened for writing + + +//See if a file is in a hog +bool cf_IsFileInHog(char *filename, char *hogname); + +//Opens a HOG file. Future calls to cfopen(), etc. will look in this HOG. +//Parameters: libname - the path & filename of the HOG file +//NOTE: libname must be valid for the entire execution of the program. Therefore, it should either +// be a fully-specified path name, or the current directory must not change. +//Returns: 0 if error, else library handle that can be used to close the library +int cf_OpenLibrary(const char *libname); + +//Closes a library file. +//Parameters: handle: the handle returned by cf_OpenLibrary() +void cf_CloseLibrary(int handle); + +//Specify a directory to look in for files +//if ext==NULL, look in this directory for all files. If ext is non-null, +//it is a NULL-terminated list of file extensions. If extensions are +//specifed, the directory will only be searched for files that match +//one of the listed extensions. +int cf_SetSearchPath(const char *path,char *ext,...); + +//Removes all search paths that have been added by cf_SetSearchPath +void cf_ClearAllSearchPaths(void); + +//Opens a file for reading or writing +//If a path is specified, will try to open the file only in that path. +//If no path is specified, will look through search directories and library files. +//Parameters: filename - the name if the file, with or without a path +// mode - the standard C mode string +//Returns: the CFile handle, or NULL if file not opened +CFILE *cfopen(const char *filename, const char *mode); + +// Opens a file for reading in a library, given the library id. +// Works just like cfopen, except it assumes "rb" mode and forces the file to be +// opened from the given library. Returns the CFILE handle or NULL if file +// couldn't be found or open. +CFILE *cf_OpenFileInLibrary(const char *filename,int libhandle); + +//Returns the length of the specified file +//Parameters: cfp - the file pointer returned by cfopen() +int cfilelength( CFILE *cfp ); + +//Closes an open CFILE. +//Parameters: cfile - the file pointer returned by cfopen() +void cfclose( CFILE * cfp ); + +//Just like stdio fgetc(), except works on a CFILE +//Returns a char or EOF +int cfgetc( CFILE * cfp ); + +//Just like stdio fseek(), except works on a CFILE +int cfseek( CFILE *cfp, long int offset, int where ); + +//Just like stdio ftell(), except works on a CFILE +int cftell( CFILE * cfp ); + +//Returns true if at EOF +int cfeof(CFILE *cfp); + +//return values for cfexist() +#define CF_NOT_FOUND 0 +#define CF_ON_DISK 1 +#define CF_IN_LIBRARY 2 + +// Tells if the file exists +// Returns non-zero if file exists. Also tells if the file is on disk +// or in a hog - See return values in cfile.h +int cfexist( const char * filename ); + +//Reads the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO READ STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes read. +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int cf_ReadBytes(ubyte *buf, int count, CFILE *cfp); + +// The following functions read numeric vales from a CFILE. All values are +// stored in the file in Intel (little-endian) format. These functions +// will convert to big-endian if required. +// These funtions will throw an exception of if the value cannot be read, +// so do not call these if you don't require the data to be present. + +//Read and return an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +int cf_ReadInt(CFILE *cfp); + +//Read and return a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +short cf_ReadShort(CFILE *cfp); + +//Read and return a byte (8 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +sbyte cf_ReadByte(CFILE *cfp); + +//Read and return a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +float cf_ReadFloat(CFILE *cfp); + +//Read and return a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on read +double cf_ReadDouble(CFILE *cfp); + +//Reads a string from a CFILE. If the file is type binary, this +//function reads until a NULL or EOF is found. If the file is text, +//the function reads until a newline or EOF is found. The string is always +//written to the destination buffer null-terminated, without the newline. +//Parameters: buf - where the string is written +// n - the maximum string length, including the terminating 0 +// cfp - the CFILE pointer +//Returns the number of bytes in the string, before the terminator +//Does not generate an exception on EOF +int cf_ReadString(char * buf, size_t n, CFILE * cfp ); + +//Writes the specified number of bytes from a file into the buffer +//DO NOT USE THIS TO WRITE STRUCTURES. This function is for byte +//data, such as a string or a bitmap of 8-bit pixels. +//Returns the number of bytes written. +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int cf_WriteBytes(const ubyte *buf, int count, CFILE *cfp); + +//Writes a null-terminated string to a file. If the file is type binary, +//the string is terminated in the file with a null. If the file is type +//text, the string is terminated with a newline. +//Parameters: buf - pointer to the string +// cfp = the CFILE pointer +//Returns the number of bytes written +//Throws an exception of type (cfile_error *) if the OS returns an error on write +int cf_WriteString(CFILE *cfp, const char *buf); + +//Just like stdio fprintf(), except works on a CFILE +int cfprintf( CFILE *cfp, const char *format, ... ); + +// The following functions write numeric vales to a CFILE. All values are +// stored to the file in Intel (little-endian) format. +// All these throw an exception if there's an error on write. + + +//Write an integer (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteInt(CFILE *cfp,int i); + +//Write a short (16 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteShort(CFILE *cfp,short s); + +//Write a byte (8 bits). If the byte is a newline & the file is a text file, writes a CR/LF pair. +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteByte(CFILE *cfp,sbyte b); + +//Write a float (32 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteFloat(CFILE *cfp,float f); + +//Write a double (64 bits) +//Throws an exception of type (cfile_error *) if the OS returns an error on write +void cf_WriteDouble(CFILE *cfp,double d); + +//Copies a file. Returns TRUE if copied ok. Returns FALSE if error opening either file. +//Throws an exception of type (cfile_error *) if the OS returns an error on read or write +// If copytime is nonzero, copies the filetime info as well +bool cf_CopyFile (char *dest,const char *src,int copytime=0); + +//Checks to see if two files are different. +//Returns TRUE if the files are different, or FALSE if they are the same. +bool cf_Diff (const char *a,const char *b); + +// Copies the file time from one file to another +void cf_CopyFileTime (char *dest,const char *src); + + +// Changes a files attributes (ie read/write only) +void cf_ChangeFileAttributes (const char *name,int attr); + +// rewinds cfile position +void cf_Rewind(CFILE *fp); + +// Calculates a 32 bit CRC +unsigned int cf_GetfileCRC (char *src); +unsigned int cf_CalculateFileCRC (CFILE *fp);//same as cf_GetfileCRC, except works with CFILE pointers + +// the following cf_LibraryFind function are similar to the ddio_Find functions as they look +// for files that match the wildcard passed in, however, this is to be used for hog files. +bool cf_LibraryFindFirst(int handle,const char *wildcard,char *buffer); +bool cf_LibraryFindNext(char *buffer); +void cf_LibraryFindClose(void); + +#endif diff --git a/lib/CZip.h b/lib/CZip.h new file mode 100644 index 000000000..a3ee49fe0 --- /dev/null +++ b/lib/CZip.h @@ -0,0 +1,266 @@ +#ifndef __CZIPFILE_H_ +#define __CZIPFILE_H_ + + +#define OCF_VERSION 0x01 + +//////////////////////////////////////// +// Compression Types +#define COMPT_NONE 255 //No compression, just stored (Not Implemented Yet) +#define COMPT_SHANFANO 0 //Shannon-Fano Compression +#define COMPT_HUFFBAS 1 //Huffman Encoding, Basic +#define COMPT_HUFFADAPT_0 2 //Huffman Encoding, Adaptive Order 0 + +// Virtual file (disk or memory files) +#define VFT_FILE 0 +#define VFT_MEM 1 + +/* + *File Formats: + *------------- + * Compression Types + * ================= + * 0 - Shannon-Fano (Can't be used for raw files) + * 1 - Huffman Basic (Can't be used for raw files) + * 2 - Huffman Adaptive Order-0 + * + * + * OCF File Type + * ------------- + * 4 - Magic Number (0x4D495241) + * 1 - Version (Current 0x01) + * 1 - Filename length + * x - Original Filename + * 8 - Original Filetime + * 1 - Compression Type (see above) + * 4 - Compressed Filesize + * 4 - Expanded Filesize + * 1 - 0x01 + * x - Compressed Data + * -------> Repeat for multiple files + * + * + * raw compressed files (any file extension, for use with readbyte, writebyte, etc) + * -------------------- + * 4 - Magic Number (0x52415743) + * 1 - Compression Type (see above) + * x - Compressed Data + * + */ + +#define ubyte unsigned char +#define uint unsigned int +#define ushort unsigned short +#define ulong unsigned long + +typedef struct{ + ubyte type; + int count; + int size; //only used for memory...size allocated for buffer + int file_size; //max count we reached + union{ + FILE *file; + ubyte *memory; + }; +}tVirtualFile; + + +//////////////////////////// +// BitIO ////// +//////////////////////// +typedef struct tBitFile{ + tVirtualFile *file; + ubyte mask; + int rack; +}BITFILE; + +///////////////////////////// +// HuffmanBasic /////// +///////////////////////// +typedef struct tHuffman0TreeNode{ + uint count; + uint saved_count; + int child0,child1; +}tH0Node; + +typedef struct{ + uint code; + int code_bits; +}tH0Code; + +/////////////////////////// +// Huffman Adaptive ////// +///////////////////////// +#define SYMBOL_COUNT 258 +#define NODE_TABLE_COUNT ((SYMBOL_COUNT*2)-1) +typedef struct tHANode{ + uint weight; + int parent; + bool child_is_leaf; + int child; +}tHANode; + +typedef struct tHATree{ + int leaf[SYMBOL_COUNT]; + int next_free_node; + tHANode nodes[NODE_TABLE_COUNT]; +}tHATree; + +typedef struct{ + char filename[_MAX_PATH]; //filename + int lo_time,hi_time; //filetime + int compressed_size; // + int expanded_size; // +}tFileInfo; + + +//////////////////////////////////////////////// +// CZip Class Header ////////// +//////////////////////////////////////////// +class CZip +{ +public: + CZip(); + ~CZip(); + + // Opens an archive file for reading + // open_raw = if this is true, then the file MUST be of the raw compressed type. You cannot + // open an OCF by raw. If a raw type archive is opened, you may only use the read + // byte type functions (ReadRaw*()) + // returns true on success + bool OpenInputArchive(char *filename,bool open_raw=false); + + // Opens an archive file for writing + // open_raw = if this is true, then the output archive will be of the raw compressed type. + // You must used the write byte type functions (WriteRaw*()) + // compression_type = You only need to specify this if you opened raw + // returns true on success + bool OpenOutputArchive(char *filename,bool open_raw=false,int compression_type=-1); + + // Closes an archive that was opened for input + void CloseInputArchive(void); + + // Closes an archive that was opened for output + void CloseOutputArchive(void); + + // Adds a given file to the archive + // You may only add files to OCF files (you passed false for open_raw when you opened) + // compression_type = what type of compression you want to use + bool AddFileToArchive(char *filename,int compression_type=COMPT_HUFFADAPT_0); + + // Extracts a file from an OCF archive + // filename = The filename specified within the archive + // destfilename = The output filename (what it should get extracted to) pass NULL if it + // is the same as the original filename + bool ExtractFileFromArchive(char *filename,char *destfilename=NULL); + + // Gets a list of files in the OCF, make sure you free up the array after use + // returns the number of files in the OCF + // 0 or -1 on error (-1 is out of memory) + int GetFileList(tFileInfo **info); + + ///////////////////////////////////////// + // Raw type compressed files operations only + int ReadBytes(char *data,int count); + void WriteBytes(char *data,int count); + ubyte ReadRawByte(void); + ushort ReadRawShort(void); + uint ReadRawInt(void); + float ReadRawFloat(void); + void WriteRawByte(ubyte value); + void WriteRawShort(ushort value); + void WriteRawInt(uint value); + void WriteRawFloat(float value); + bool RawEOF(void); +private: + //////////////////////////////// + // CZip Variables + BITFILE *bfile; + tVirtualFile *file; + bool m_bRawType; + int m_iCompressionType; + tFileInfo current_file; + // Reads in a Raw type file header + // returns true on succes, if so, compr_type contains the compression type + bool ReadRawHeader(tVirtualFile *file,ubyte *compr_type); + // Writes out a Raw type file header + // returns true on succes + bool WriteRawHeader(tVirtualFile *file,ubyte compr_type); + // Reads in an OCF header + // returns true on success, info is filled in the appropriate values,compr_type is compression type + bool ReadOCFHeader(tVirtualFile *file,tFileInfo *info,ubyte *compr_type); + // Writes out an OCF header + // returns true on success + bool WriteOCFHeader(tVirtualFile *file,tFileInfo *info,ubyte compr_type,int header_pos); + // Writes out a 'dummy' OCF header, just give what the final filename is + // you must call this before you compress data, then when done, call the read WriteOCFHeader + bool WriteDummyOCFHeader(tVirtualFile *file,char *filename); + // Searches through the opened OCF looking for filename, returns the file position + // of the file if found, or -1 if not. + int FindFileInOCF(tVirtualFile *file,char *filename); + // Gets a list of files in the OCF, make sure you free up the array after use + // returns the number of files in the OCF + // 0 or -1 on error (-1 is out of memory) + int GetOCFFileList(BITFILE *file,tFileInfo **info); + // Prepares one of the decompressors for raw reading + void PrepareDecompressor(BITFILE *bfile); + // Prepares one of the compressors for raw reading + void PrepareCompressor(BITFILE *bfile); + + + //////////////////////////////////////////// + //BitIO + BITFILE *OpenInputBitFile(char *filename); + BITFILE *OpenOutputBitFile(char *filename); + void OutputBit(BITFILE *bfile,int bit); + void OutputBits(BITFILE *bfile,ulong code,int count); + int InputBit(BITFILE *bfile); + ulong InputBits(BITFILE *bfile,int bitcount); + void CloseInputBitFile(BITFILE *bfile); + void CloseOutputBitFile(BITFILE *bfile); + void FilePrintBinary(FILE *file,uint code,int bits); + tVirtualFile *VFopen(char *filename,char *flags,int size=0); + int VFclose(tVirtualFile *f); + int VFputc(int value,tVirtualFile *file); + int VFgetc(tVirtualFile *file); + int VFwrite(void *buffer,int size,int count,tVirtualFile *file); + int VFread(void *buffer,int size,int count,tVirtualFile *file); + int VFtell(tVirtualFile *file); + int VFseek(tVirtualFile *file,int offset,int origin); + void FlushOutputBitFile(BITFILE *bfile); + + ////////////////////////////////////////////// + //HuffmanBase + int hb_CompressFile(tVirtualFile *input,BITFILE *output); + bool hb_ExpandFile(BITFILE *input,tVirtualFile *output); + void hb_compress_data(tVirtualFile *input,BITFILE *output,tH0Code *codes); + void hb_expand_data(BITFILE *input,tVirtualFile *output,tH0Node *nodes,int root_node); + void hb_count_bytes(tVirtualFile *input,ulong *long_counts); + void hb_scale_counts(ulong *long_counts,tH0Node *nodes); + int hb_build_tree(tH0Node *nodes); + void hb_convert_tree_to_code(tH0Node *nodes, tH0Code *codes,uint code_so_far,int bits,int node); + void hb_output_counts(BITFILE *output,tH0Node *nodes); + void hb_input_counts(BITFILE *input,tH0Node *nodes); + + ////////////////////////////////////////////// + // Huffman Adaptive + tHATree Tree; + bool ok_to_raw_write,ok_to_raw_read; + int ha_CompressFile(tVirtualFile *input,BITFILE *output); + void ha_ExpandFile(BITFILE *input,tVirtualFile *output); + void ha_InitializeTree(tHATree *tree); + void ha_EncodeSymbol(tHATree *tree,uint c,BITFILE *output); + int ha_DecodeSymbol(tHATree *tree,BITFILE *input); + void ha_UpdateModel(tHATree *tree,int c); + void ha_RebuildTree(tHATree *tree); + void ha_swap_nodes(tHATree *tree,int i,int j); + void ha_add_new_node(tHATree *tree,int c); + void ha_CloseRawDecompress(void); + bool ha_ReadRawByte(ubyte *data,BITFILE *input); + void ha_PrepareDecompress(void); + void ha_CloseRawCompress(BITFILE *output); + void ha_WriteRawByte(ubyte data,BITFILE *output); + void ha_PrepareCompress(void); +}; + +#endif \ No newline at end of file diff --git a/lib/Controller.h b/lib/Controller.h new file mode 100644 index 000000000..86bbc97f8 --- /dev/null +++ b/lib/Controller.h @@ -0,0 +1,264 @@ +/* + * $Logfile: /DescentIII/Main/lib/Controller.h $ + * $Revision: 21 $ + * $Date: 3/20/00 12:25p $ + * $Author: Matt $ + * + * Universal controller header + * + * $Log: /DescentIII/Main/lib/Controller.h $ + * + * 21 3/20/00 12:25p Matt + * Merge of Duane's post-1.3 changes. + * Added declaration. + * + * 20 8/11/99 6:06p Samir + * added flag to determine whether packet contained mouse info. + * + * 19 7/20/99 4:53p Samir + * added ability to manually set the deadzone for a controller. + * + * 18 7/16/99 11:14a Samir + * multiple hat support and improved direct input support. + * + * 17 4/29/99 2:23a Samir + * moved binding text functions to wincontroller.cpp and new text for + * multiple joysticks. + * + * 16 4/16/99 7:34p Jeff + * added linux controller + * + * 15 2/16/99 11:59a Samir + * added proper constants for controller and binding null values. + * + * 14 12/18/98 6:00p Samir + * added enable_function. + * + * 13 10/24/98 2:18p Samir + * added mouse and joytick raw value retrieval functions. + * + * 12 10/21/98 10:36a Samir + * added code to turn on or off joystick or mouse. + * + * 11 10/18/98 7:29p Samir + * made assign_function public. + * + * 10 10/17/98 7:31p Samir + * added invertible axes + * + * 9 9/10/98 12:39p Samir + * added senstivity issures for controller. + * + * 8 6/18/98 4:48p Samir + * added changes for multiple configs for joystick controls. + * + * 7 5/15/98 3:13p Samir + * added 2 key support for controller functions. + * + * 6 2/24/98 11:03a Samir + * Added flush function to controller system. + * + * 5 2/16/98 3:04p Samir + * ctAxis instead of ctXAxis, ctYAxis, etc. + * + * 4 2/13/98 6:38p Samir + * Added get and set controller function. + * + * 3 12/03/97 7:35p Samir + * Newer joystick library support and some POV. + * + * 2 10/29/97 4:44p Samir + * Added ctDownCount format. + * + * 8 5/21/97 3:51p Samir + * changed ct_need to ct_function + * + * 7 5/12/97 4:39 PM Jeremy + * #include of macController.h on Macintosh + * + * 6 5/12/97 1:21p Samir + * Added suspend and resume functions. + * + * 5 4/23/97 1:07p Samir + * Now we can poll for either positonal or evaluator data. + * + * 4 4/16/97 1:04p Samir + * For get packet, allow one to return an alternate format value if that + * function supports it. + * + * 3 4/16/97 12:27p Samir + * Added mouse support. + * + * 2 4/11/97 2:13p Samir + * Universal header for control system. first version + * + * $NoKeywords: $ + */ + +#ifndef CONTROLLER_H +#define CONTROLLER_H + +#include "pstypes.h" +#include "Macros.h" + +typedef enum ct_format { + ctNoFormat, + ctAnalog, // analog information (-1.0 to 1.0) + ctDigital, // digital information (0 or 1) + ctDownCount, // special information for key presses and button/mouse presses. + ctTime // time in seconds format +} ct_format; + +typedef enum ct_type { + ctNone, + ctAxis, // axis element of controller. + ctPOV, // function value (hiword = +axis, loword = -axis) + ctButton, // fn value: controller button # + ctKey, // fn value: lobyte(key constant), hibyte(alternate key constant) + ctMouseAxis, + ctMouseButton, + ctPOV2, + ctPOV3, + ctPOV4 // auxillary POV values. +} ct_type; + + +typedef struct ct_function { + int id; // identifier for the function (like forward thrust) + ct_format format; // what format should the return value be for this function + ct_type ctype[2]; // type of controller requested for this id. (1 for each value packed.) + ubyte value[2]; // corresponding value to ctype + ubyte flags[2]; // flags. +} ct_function; + + +typedef struct ct_packet { + ct_format format; // format of value. + float value; // time value for buttons, absolute value for axis values + unsigned flags; // additional information (see below) +} ct_packet; + +typedef unsigned ct_config_data; // passed by controller system to the outside, and back to controller system + +// values for ct_packet.flags +#define CTPK_ELEMENTACTIVE 0x1 // indicates element was activated but no time/analog information is available. +#define CTPK_MOUSE 0x2 // this is coming from a mouse device. default is joystick/keyboard. + +// element values +const ubyte CT_X_AXIS = 1, // AXIS constants for ctAxis + CT_Y_AXIS = 2, + CT_Z_AXIS = 3, + CT_R_AXIS = 4, + CT_U_AXIS = 5, + CT_V_AXIS = 6, + CT_NUM_AXES = 6; // number of axes + +// ct_function flags +#define CTFNF_INVERT 0x1 // invert values returned via get_packet. + + + +#define NULL_BINDING 0x00 +#define NULL_CONTROLLER 0xff +#define INVALID_CONTROLLER_INFO 0xffff + +#define CONTROLLER_KEY1_VALUE(_b) lobyte(_b) +#define CONTROLLER_KEY2_VALUE(_b) hibyte(_b) +#define CONTROLLER_KEY_VALUE(_k1, _k2) makeshort(_k2, _k1) + +#define CONTROLLER_CTL1_VALUE(_b) CONTROLLER_KEY1_VALUE(_b) +#define CONTROLLER_CTL2_VALUE(_b) CONTROLLER_KEY2_VALUE(_b) +#define CONTROLLER_CTL_VALUE(_l, _h) makeshort(_h, _l) + +#define CONTROLLER_CTL1_INFO(_b) ((sbyte)lobyte(_b)) +#define CONTROLLER_CTL2_INFO(_b) ((sbyte)hibyte(_b)) +#define CONTROLLER_CTL_INFO(_l, _h) makeshort(_h, _l) + +#define CONTROLLER_VALUE(_l) ((ushort)loword(_l)) +#define CONTROLLER_INFO(_l) ((short)hiword(_l)) +#define MAKE_CONFIG_DATA(_c, _v) makeword(_c,_v) + +#define CTLBINDS_PER_FUNC 2 + + + + + +class gameController +{ +public: + gameController(int num_funcs, ct_function *funcs) {}; + virtual ~gameController() {}; + +// these functions suspend or resume any controller reading. this is really only useful for +// preemptive controller polling, but they should be used to activate and deactivate controller +// reading. + virtual void suspend() {}; + virtual void resume() {}; + +// this functions polls the controllers if needed. some systems may not need to implement +// this function. + virtual void poll() {}; + +// flushes all controller information + virtual void flush() = 0; + +// returns the value of a requested controller type. make sure you flush the controller before polling. + virtual ct_config_data get_controller_value(ct_type type_req) = 0; + +// sets the configuration of a function (type must be of an array == CTLBINDS_PER_FUNC) + virtual void set_controller_function(int id, const ct_type *type, ct_config_data value, const ubyte *flags) = 0; + +// returns information about a requested function (type must be of an array == CTLBINDS_PER_FUNC) + virtual void get_controller_function(int id, ct_type *type, ct_config_data *value, ubyte *flags) = 0; + +// temporarily enables or disables a function + virtual void enable_function(int id, bool enable) = 0; + +// all systems need to implement this function. this returns information about the controller + virtual bool get_packet(int id, ct_packet *packet, ct_format alt_format=ctNoFormat) = 0; + +// gets sensitivity of axis item + virtual float get_axis_sensitivity(ct_type axis_type, ubyte axis)=0; + +// sets sensitivity of axis item + virtual void set_axis_sensitivity(ct_type axis_type, ubyte axis, float val)=0; + +// assigns an individual function + virtual int assign_function(ct_function *fn) = 0; + +// activates or deactivates mouse and or controller + virtual void mask_controllers(bool joystick, bool mouse) = 0; + +// retrieves binding text for desired function, binding, etc. + virtual const char *get_binding_text(ct_type type, ubyte ctrl, ubyte bind) = 0; + +// get raw values for the controllers + virtual int get_mouse_raw_values(int *x, int *y) = 0; + virtual unsigned get_joy_raw_values(int *x, int *y) = 0; + +// toggles use of deadzone for controllers. ctl can be 0 to ??? +// dead zone is from 0.0 to 0.5 + virtual void set_controller_deadzone(int ctl, float deadzone) {}; + virtual float get_controller_deadzone(int ctl) { return 0; }; + +// toggles use of axis on controllers. ctl can be 0 to ??? +// axis is a CT_?_AXIS value + void toggle_controller_axis(int ctl, int axis, bool toggle) {}; + +}; + +gameController *CreateController(int num_funcs, ct_function *funcs, char *remote_ip); +void DestroyController(gameController *ctl); + + +#if defined(WIN32) + #include "win\WinController.h" +#elif defined(MACINTOSH) + #include "macController.h" +#elif defined(__LINUX__) + #include "linux/lnxcontroller.h" +#endif + + +#endif \ No newline at end of file diff --git a/lib/DDAccess.h b/lib/DDAccess.h new file mode 100644 index 000000000..79a2929a4 --- /dev/null +++ b/lib/DDAccess.h @@ -0,0 +1,48 @@ +/* + * $Logfile: /DescentIII/Main/lib/DDAccess.h $ + * $Revision: 3 $ + * $Date: 8/13/97 3:02p $ + * $Author: Samir $ + * + * Device Dependant Access Manager. (Allow access of libraries to OS information) + * + * $Log: /DescentIII/Main/lib/DDAccess.h $ + * + * 3 8/13/97 3:02p Samir + * A 'cleaner' way to do DirectInput mouse stuff. + * + * 2 8/01/97 7:31p Samir + * Slightly better interface. + * + * 1 5/23/97 4:07p Samir + * Device Dependant Data Access Manager. + * + * $NoKeywords: $ + */ + +#ifndef DDACCESS_H +#define DDACCESS_H + +// allows access to DD system information, such as OS specific info. + +#define DD_ACCESS_RING + + +// Win32 region of DD_ACCESS +// Include the standard windows header since API calls are available in DD_ACCESS_RING +// Define any additional messages too. + +#if defined(WIN32) + +// Headers +#define WIN32_LEAN_AND_MEAN +#include + +// Messages +#define WM_SYNCMOUSEACQUIRE (WM_USER+0) + +#endif // WIN32 + + +#endif + diff --git a/lib/Ddgr.h b/lib/Ddgr.h new file mode 100644 index 000000000..91bf746f8 --- /dev/null +++ b/lib/Ddgr.h @@ -0,0 +1,265 @@ +/* + * $Logfile: /DescentIII/Main/lib/Ddgr.h $ + * $Revision: 6 $ + * $Date: 12/23/97 6:16p $ + * $Author: Samir $ + * + * DDGR library interface header + * + * $Log: /DescentIII/Main/lib/Ddgr.h $ + * + * 6 12/23/97 6:16p Samir + * Moved ddgr_Close to header. + * + * 5 12/22/97 7:24p Samir + * took out transparent color defs. + * + * 4 12/22/97 7:13p Samir + * Moved constants to grdefs.h + * + * 3 9/12/97 4:13p Samir + * Added some private data access functions. + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 26 6/12/97 6:30p Samir + * DDGR v2.0 Changes in 2d system implemented. + * + * 25 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 24 5/22/97 4:03p Samir + * Hacked OpenGL stuff. WILL CHANGE + * + * 23 5/15/97 2:08p Samir + * Added 8-bit support and driver info structure. + * + * 22 3/28/97 3:07p Samir + * Clear function now takes a color + * + * 21 3/27/97 11:11a Samir + * Revised headers to reflect movement of functions from ddgr to 2dlib + * + * 20 3/26/97 7:44p Samir + * Took out prototypes to unimplemented functions. + * + * 19 2/27/97 6:16p Samir + * moved high level functions to 2dlib internal library + * + * 18 2/03/97 6:00p Jason + * added 2d bitmap scaling functions + * + * 17 1/30/97 6:01p Samir + * The partition of memory and os surfaces as well as the change in + * ddgr_surface structure and all related changes. + * + * $NoKeywords: $ + */ + + +#ifndef _DDGR_H +#define _DDGR_H + +#include "pstypes.h" +#include "Macros.h" +#include "grdefs.h" + +// ---------------------------------------------------------------------------- +// DDGR +// version 1.0 // DirectDraw, GDI support +// version 2.x // fullscreen-windowed remanagement. +// ---------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------- +// Data structures +// ---------------------------------------------------------------------------- + +/* Type definitions */ + +class oeApplication; + +/* + Diagnostic DDGR system types and functions +*/ + +typedef struct ddgr_init_info +{ + oeApplication *obj; // the app object created by app calling + char *subsystem; // subsystem name (i.e. 'DirectDraw', 'GDI') + ushort windowed:1; // are we running in a fullscreen or windowed mode + ushort debug:1; // are we running in debug mode? +} +ddgr_init_info; + +/* app = application object. + subsystem = subsystem name ('DirectDraw', 'GDI') + fullscreen = whether it's full screen or windowed +*/ +bool ddgr_Init(oeApplication *app, char *subsystem, bool fullscreen); +void ddgr_Close(); +void ddgr_GetSubsystem(char *name, bool *fullscreen); + +// returns aspect ratio of current display. +float ddgr_GetAspectRatio(); + + +/* Surface bit depths + The bitdepth of the surface determines the color format of the surface. Renderers + should use this value to plot +*/ + +// Green with all 6 bits set to 1 is our transparent color +// default transparent color to 16bit, but all code should use the first 2 defines. + +/* Surface types + Surfaces symbolize a flat 2d surface where pixels are lit. Essentially, a surface could + be a bitmap, but if it's the display, it's a bitmap managed by the video card. + The goal of this library is to provide a clean, device-independent method to accessing + surfaces. + + SURFTYPE_VIDEOSCREEN: + The video screen is a physical or virtual display. + When you create a videoscreen, you may specify either it is monopage or dualpage (backbuffer) + When dualpage, the program can RENDER to the 1st page. To update the video display, you + must perform a surface flip. + + SURFTYPE_GENERIC: + This is a generic bitmap managed by the operating system. RENDERING is allowed. + */ +const int SURFTYPE_VIDEOSCREEN = 1, // This is equivical to the display. + SURFTYPE_GENERIC = 2; // This is an offscreen bitmap. + + +/* Surface flags + Flags modify a surfaces behavior. A surface may have a backbuffer, which is only really + useful for videoscreens. + + SURFFLAG_BACKBUFFER: + This surface has a backbuffer. + v2.0 Supported in only videoscreen surfaces. + + SURFFLAG_COLORKEY: + This surface was created with colorkeying in mind. + Renderers should keep this in mind, and check this flag. + */ +const int SURFFLAG_BACKBUFFER = 1, + SURFFLAG_COLORKEY = 2; + + +/* Surface structures + The surface +*/ +const int SURF_NAMELEN = 16; + +typedef struct ddgr_surface +{ + void *obj; // internal structure info to library + char name[SURF_NAMELEN]; // name + int w,h,bpp; // width, height and bit depth + ushort type; // how driver handles this surface + ushort flags; // colorkeying, etc. + int locks; // lock count. +} +ddgr_surface; + +#if defined(DD_ACCESS_RING) // only available to DD_ACCESS libraries. +#if defined(WIN32) +/* + ddraw_surf = true if this is a true DirectDraw surface, false if a GDI surface + object_ptr = DirectDraw object if ddraw_surf = true, a HDC if a GDI surface. +*/ +void ddgr_surf_GetPrivateData(ddgr_surface *sf, bool *ddraw_surf, uint *object_ptr); + +// you must typecase this return value to LPDIRECTDRAW +unsigned ddgr_GetDDrawObject(); +#endif // WIN32 +#endif // DD_ACCESS_RING + + +/* Surface Functions +*/ + +/* input: + sf->name is optional. + sf->w, sf->h, sf->bpp are mandatory + sf->type and sf->flags are mandatory. + output: + sf->obj = surface object. + sf->locks = 0 +*/ +bool ddgr_surf_Create(ddgr_surface *sf); +void ddgr_surf_Destroy(ddgr_surface *sf); + +/* retrieves a pointer to surface memory. allowed to lock one surface multiple times. + ptr is the returned pointer to surface memory. used to unlock surface also + rowsize is the size in bytes of one row of memory. +*/ +bool ddgr_surf_Lock(ddgr_surface *sf, void **ptr, int *rowsize); +bool ddgr_surf_Unlock(ddgr_surface *sf, void *ptr); + +// attaches an OS handle to a surface +void ddgr_surf_AttachHandle(ddgr_surface *sf, unsigned handle); + +/* initializes a video screen from the given input surface which specifies the dimensions + of the screen. + sf->name is optional + sf->w, sf->h are mandatory + sf->bpp is recommended. if set to BPP_DEFAULT, it uses the current display bit depth + sf->type = SURFTYPE_VIDEOSCREEN + sf->flags = 0 or SURFFLAG_BACKBUFFER. surfaces with backbuffers allow rendering to + them and display via flip. monopage surfaces don't have this luxury. +*/ +bool ddgr_surf_InitVideo(ddgr_surface *sf); + +/* close video reverses the operation of init video for that surface. the display should stay + the same, but no more operations may occur to that surface through this library. +*/ +void ddgr_surf_CloseVideo(ddgr_surface *sf); + +/* flips the buffers in a surface. really only useful for video screens +*/ +bool ddgr_surf_FlipVideo(ddgr_surface *sf); + +/* graphic primatives +*/ +void ddgr_surf_Clear(ddgr_surface *dsf, ddgr_color col, int l, int t, int w, int h); +bool ddgr_surf_Blt(ddgr_surface *dsf, int dx, int dy, ddgr_surface *ssf, int sx, int sy, int sw, int sh); + + +/* + 8-bit palette surface structures +*/ +typedef struct ddgr_rgb +{ + ubyte r,g,b; // RGB triplet + ubyte x; // reserved... +} +ddgr_rgb; + +typedef struct ddgr_palette +{ + void *obj; // internal object + ddgr_rgb rgb[256]; // rgb values for palette. +} +ddgr_palette; + +// attaches a palette to an 8-bit surface only. +bool ddgr_surf_AttachPalette(ddgr_surface *surf, ddgr_palette *pal); + +// grabs a palette. +bool ddgr_surf_GetPalette(ddgr_surface *surf, ddgr_palette *pal); + +// creates and destroys palette objects. +bool ddgr_8bit_CreatePalette(ddgr_palette *pal); +bool ddgr_8bit_DestroyPalette(ddgr_palette *pal); + +// loads a palette with specified entriyes +bool ddgr_8bit_LoadPalette(ddgr_palette *pal, int start, int count); + +// returns an index into the palette +int ddgr_8bit_FindClosestColor(ddgr_palette *pal,ddgr_color col); + +#endif + diff --git a/lib/IMeshBuilder.h b/lib/IMeshBuilder.h new file mode 100644 index 000000000..bd6633b09 --- /dev/null +++ b/lib/IMeshBuilder.h @@ -0,0 +1,64 @@ +#ifndef __IMESH_BUILDER_H__ +#define __IMESH_BUILDER_H__ + +#include "IMeshHandle.h" + +namespace RZ +{ + namespace Renderer + { + class IMeshBuilder + { + public: + enum PrimitiveType + { + kTriangle, + kTriangleFan, + kTriangleStrip, + }; + + enum VertexType + { + kPosition = 0x01, + kColor = 0x02, + kTexture1 = 0x04, + //kTexture2 = 0x08, + kNormal = 0x10, + }; + + virtual ~IMeshBuilder(){ } + + // Begin + // Begins the generation of a mesh. You must supply + // the primitive type, size of the vertex buffer, and + // the number of primitives. + // vertexFlags are a combination of VertexType values + // that represent what data should be stored in the verts. + virtual void Begin( PrimitiveType type, unsigned int vertexFlags, unsigned int numPrims, unsigned int numVerts, bool autogenNormals ) = 0; + + // End + // Ends the build process which will cause the generation of the mesh data. + // Returned will be a handle to the mesh + virtual MeshHandle End( void ) = 0; + + // SetStreamData + // Stores stream data for one or more vertex fields. + // streamId: Which field we are setting data for + // startVertIndex: Which vertex in vertex buffer are we starting at + // numVerts: How many full verts of data are we processing + // dataPtr: Incoming data. All data is float based. + // elementsPerField: How many floats-per-field for this data. + // NOTE: This is for verification purposes. Positions and + // Normals are 3 elements. Texture UVs are 2 elements. Colors + // are 4 elements. + // stride: The number of bytes from the START of one field to the next on the incoming stream. + virtual void SetStreamData( VertexType streamId, unsigned int startVertIndex, unsigned int numVerts, const float *dataPtr, unsigned int elementsPerField, unsigned int stride ) = 0; + + // AddPrimitive + // Adds another primitive to the buffer. + virtual void AddPrimitive( unsigned int numVerts, unsigned int *vertexIndices ) = 0; + }; + } +} + +#endif diff --git a/lib/IMeshHandle.h b/lib/IMeshHandle.h new file mode 100644 index 000000000..68b3ee140 --- /dev/null +++ b/lib/IMeshHandle.h @@ -0,0 +1,21 @@ +#ifndef __IMESH_HANDLE_H__ +#define __IMESH_HANDLE_H__ + +#include +#include + +namespace RZ +{ + namespace Renderer + { + class IMeshHandle + { + public: + virtual ~IMeshHandle(){} + }; + + typedef boost::shared_ptr MeshHandle; + } +} + +#endif diff --git a/lib/InfFile.h b/lib/InfFile.h new file mode 100644 index 000000000..bbdf2dc2d --- /dev/null +++ b/lib/InfFile.h @@ -0,0 +1,72 @@ +// InfFile.h: interface for the InfFile class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_INFFILE_H__D8F94664_216E_11D2_AF2D_0060089A8025__INCLUDED_) +#define AFX_INFFILE_H__D8F94664_216E_11D2_AF2D_0060089A8025__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include "pstypes.h" +#include "psclass.h" + +struct CFILE; + +#define INFFILE_LINELEN 256 +#define INFFILE_CUSTOM -128 +#define INFFILE_ERROR -1 +#define INFFILE_COMMENT -2 +#define INFFILE_EOL -3 +#define INFFILE_SYMBOL 1024 + +class InfFile +{ +public: + InfFile(); + virtual ~InfFile(); + +// opens an inf file, pass in a lexical analyzer that will return a command index. +// tag_check is a string that must appear at the very beginning of the file. +// lexfn is a function to match a command to a number. num <= 0 and num >=1024 are taken. + bool Open(const char *filename, const char *tag_check, int (*lexfn)(const char *command)); + +// closes the file + void Close(); + +// read a command line. returns false on eof. + bool ReadLine(); + +// parses a line of 'code'. return value from the lexfn +// check INFFILE constants above. (any value less than 0, you should read in a new line.) + int ParseLine(char *operand, int oprlen); + +private: + CFILE* m_fp; // file object. + int (*m_lexfn)(const char *); // lexical analyzer. + char m_linetext[INFFILE_LINELEN]; + char *m_lineptr; // pointer to current text line. + char *m_endptr; // pointer to last character in text line + int m_line; // line # + + struct sym_info // full runtime symbol + { + char name[PSFILENAME_LEN+1]; + char *text; + }; + +// inf file symbol list + tList m_sym_list; // symbol list. + +private: + void AddSymbol(const char *name, const char *text); + const char* GetSymbolText(const char *name); + void FreeSymbols(); + +public: +// line number + int line() const { return m_line; }; +}; + +#endif // !defined(AFX_INFFILE_H__D8F94664_216E_11D2_AF2D_0060089A8025__INCLUDED_) diff --git a/lib/Macros.h b/lib/Macros.h new file mode 100644 index 000000000..780e8d5a2 --- /dev/null +++ b/lib/Macros.h @@ -0,0 +1,72 @@ +/* + * $Logfile: /DescentIII/Main/Lib/Macros.h $ + * $Revision: 11 $ + * $Date: 10/21/99 9:28p $ + * $Author: Jeff $ + * + * Macros. + * + * $Log: /DescentIII/Main/Lib/Macros.h $ + * + * 11 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 10 7/28/99 5:18p Kevin + * Mac merge fixes + * + * 9 7/28/99 3:17p Kevin + * Mac Changes + * + * 8 12/16/98 1:57p Samir + * Replaced CleanupString2 with CleanupStr + * + * 7 12/02/98 11:44a Samir + * added CHECK_FLAG macro + * + * 6 8/10/98 5:53p Samir + * macro to convert KB to bytes. + * + * 5 7/14/98 11:52a Samir + * added cleanup string 'macro'. + * + * 4 5/15/98 3:13p Samir + * added some ushort/byte macros. + * + * 3 3/31/98 7:48p Samir + * fixed horrible makeword macro bug. + * + * 2 12/03/97 7:37p Samir + * Added makeword, hiword and loword macros. + * + * $NoKeywords: $ + */ +#ifndef _MACROS_H +#define _MACROS_H +#define SWAP(a,b) do { int _swap_var_=(a); (a)=(b); (b)=_swap_var_; } while (0) +#define SET_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define CHECK_FLAG(_var, _flag) ((_var) & (_flag)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define makeword(_h,_l) (((_h)<<16) + ((_l)&0xffff)) +#define hiword(_v) ((_v)>>16) +#define loword(_v) ((_v)& 0x0000ffff) +#define makeshort(_h, _l) (((_h)<<8) + ((_l)&0x00ff)) +#define hibyte(_w) ((_w)>> 8) +#define lobyte(_w) ((_w)& 0x00ff) +#define kb_to_bytes(_kb) ((_kb) * 1024) +#define _min(a,b) min(a,b) +#define _max(a,b) max(a,b) +#define ABS(a) ((a<0)?(-a):(a)) + +#ifndef WIN32 +#define __min(a,b) min(a,b) +#define __max(a,b) max(a,b) +#endif + +#ifdef LINUX +#define stricmp(a,b) strcasecmp(a,b) +#define _fstat32 fstat +#define _stat stat +#endif + +#endif diff --git a/lib/PHYSICS.H b/lib/PHYSICS.H new file mode 100644 index 000000000..59e904b21 --- /dev/null +++ b/lib/PHYSICS.H @@ -0,0 +1,120 @@ +/* +* $Logfile: /DescentIII/Main/Lib/PHYSICS.H $ +* $Revision: 15 $ +* $Date: 4/14/99 3:59a $ +* $Author: Jeff $ +* +* Descent III Physics Header +* +* $Log: /DescentIII/Main/Lib/PHYSICS.H $ + * + * 15 4/14/99 3:59a Jeff + * fixed case mismatches in #includes + * + * 14 1/10/99 6:45a Jeff + * Changes made to get linux version to compile + * + * 13 12/21/98 1:39p Chris + * Matcens can hurt (or not hurt) players that are close to them + * + * 12 11/10/98 5:17p Jeff + * peppered in some more forcefeedback effects + * + * 11 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 10 10/15/98 3:26p Chris + * Fixed known ground plane issues and used PhysCalcGround everywhere + * + * 9 8/27/98 6:23p Jeff + * changed autoleveling in config so it is a slider...had to convert + * global from bool->ubyte. Added fast headlight and mirrored surfaces to + * config menu + * + * 8 6/15/98 7:01a Chris + * Cleaned out DII stuff and added new PhysicsSim extern's + * + * 7 5/01/98 4:27p Chris + * Added externals for terrain and room autoleveling + * + * 6 4/16/98 6:50p Chris + * + * 5 2/19/98 6:17p Chris + * Added some debug info + * + * 4 1/20/98 5:27p Chris + * Added physics support for the vis. system + * + * 3 10/28/97 12:23p Chris + * Added a walking sim + * + * 2 9/11/97 12:43p Chris + * Added new support for face_physics_info from room.h. Also, changed how + * we detect portals. Also added recorded faces. + * + * 6 3/27/97 10:50a Chris + * Added bounce constant (PHYSICS_BOUNCE_UNLIMITED) for objects that can + * bounce forever + * + * 5 3/15/97 1:29p Chris +* +* $NoKeywords: $ +*/ + +#ifndef _PHYSICS_H +#define _PHYSICS_H + +#include "vecmat.h" +#include "findintersection.h" +#include "object.h" +#include "viseffect.h" + +extern int Physics_normal_counter; +extern int Physics_normal_looping_counter; +extern int Physics_walking_counter; +extern int Physics_walking_looping_counter; +extern int Physics_vis_counter; + +// The current strength of the world's gravity +extern float Gravity_strength; + +#define PHYSICS_UNLIMITED_BOUNCE -1 + +#ifdef _DEBUG + extern int Physics_player_verbose; +#endif + +extern ubyte Default_player_terrain_leveling; +extern ubyte Default_player_room_leveling; + +bool PhysicsDoSimRot(object *obj, float frame_time, matrix *orient, vector *rotforce, vector *rotvel, angle *turn_roll); +void PhysicsDoSimLinear(const object& obj, const vector& pos, const vector& force, vector& velocity, vector& movementVec, vector& movementPos, float simTime, int count); + +extern int Physics_NumLinked; +extern int PhysicsLinkList[MAX_OBJECTS]; + +//Simulate a physics object for this frame +void do_physics_sim(object *obj); + +// Quick sim for vis stuff +void do_vis_physics_sim(vis_effect *vis); + +//Simulate a physics object for this frame +void do_walking_sim(object *obj); + +//Applies an instantaneous force on an object, resulting in an instantaneous +//change in velocity. +void phys_apply_force(object *obj,vector *force_vec,short weapon_index=-1); +void phys_apply_rot(object *obj,vector *force_vec); + +//this routine will set the thrust for an object to a value that will +//(hopefully) maintain the object's current velocity +void set_thrust_from_velocity(object *obj); + +// Determines the point and normal of the ground point +bool PhysCalcGround(vector *ground_point, vector *ground_normal, object *obj, int ground_num); + +// Linked objects arbitrary linking for things like weapons (unlike attached objects) +void DoPhysLinkedFrame(object *obj); + +#endif diff --git a/lib/TaskSystem.h b/lib/TaskSystem.h new file mode 100644 index 000000000..a5bc371b3 --- /dev/null +++ b/lib/TaskSystem.h @@ -0,0 +1,123 @@ +/* + * $Logfile: /DescentIII/Main/lib/TaskSystem.h $ + * $Revision: 3 $ + * $Date: 9/14/98 4:02p $ + * $Author: Samir $ + * + * Task manager + * + * $Log: /DescentIII/Main/lib/TaskSystem.h $ + * + * 3 9/14/98 4:02p Samir + * allow variable lengths of blocking for tasks. + * + * 2 8/10/98 5:54p Samir + * added mutexes. + * + * 1 5/23/97 4:07p Samir + * Initial revision. + * + * $NoKeywords: $ + */ + + +#ifndef TASKSYSTEM_H +#define TASKSYSTEM_H + + +#include "pstypes.h" + +typedef enum tTaskPriority { + TASKPRIORITY_HIGHEST, + TASKPRIORITY_NORMAL, + TASKPRIORITY_LOWEST +} tTaskPriority; + + +// Since this system handles concepts such as multitasking, and the such, +// we will define the event structures differently, since different systems +// handle multitasking differently. + +class osEvent +{ +#if defined(DD_ACCESS_RING) +public: +#else +private: +#endif // DD_ACCESS_RING_0 +#if defined(WIN32) + unsigned event_os_handle; // this is the Win32 Event Handle +#endif // WIN32 + +public: + osEvent(char *name); + ~osEvent(); + + bool error() const; // reports error + + void signal(); // signal the event so blocking can stop + void clear(); // clear the event so blocking can continue + bool block(int timeout=-1); // block until signaled (-1 = full blocking, else time in ms to block) +}; + + +class osTask +{ +#if defined(DD_ACCESS_RING) +public: +#else +private: +#endif // DD_ACCESS_RING_0 +#if defined(WIN32) + unsigned task_os_handle; // This is the Win32 EventHandle + unsigned task_os_id; // Win32 Thread ID +#endif // WIN32 + +public: + osTask(unsigned (*func)(void *), tTaskPriority priority, void *parm=NULL); + ~osTask(); + + bool error() const; // reports error + + void suspend(); // suspends task + void resume(); // resumes task +}; + + +// This establishes a mutual exclusion object. once one thread locks the object, +// any code in another thread that contains a mutex check will block or skip that code until +// the locking thread unlocks it. +class osMutex +{ +#if defined(DD_ACCESS_RING) +public: +#else +private: +#endif + +#if defined(WIN32) + unsigned mutex_os_handle; +#endif + +public: + osMutex(); + ~osMutex(); + + bool Create(); // creates a mutex object. + void Destroy(); // destroys a mutex object + +// calling thread will attempt to acquire mutex (wait until timeout in ms.) if timeout == -1, wait forever... + bool Acquire(int timeout=-1); + +// calling thread releases control of mutex. + void Release(); +}; + + + +#endif + + + + + \ No newline at end of file diff --git a/lib/appdatabase.h b/lib/appdatabase.h new file mode 100644 index 000000000..7eff2e14e --- /dev/null +++ b/lib/appdatabase.h @@ -0,0 +1,96 @@ +/* + * $Logfile: /DescentIII/Main/lib/appdatabase.h $ + * $Revision: 6 $ + * $Date: 5/13/99 5:04p $ + * $Author: Ardussi $ + * + * Application Database + * + * $Log: /DescentIII/Main/lib/appdatabase.h $ + * + * 6 5/13/99 5:04p Ardussi + * changes for compiling on the Mac + * + * 5 5/10/99 10:53p Ardussi + * changes to compile on Mac + * + * 4 4/15/99 1:44a Jeff + * changes for linux compile + * + * 3 7/24/97 3:06p Matt + * Added functions to read & write bools & variable-length integers, and + * fixed a few small bugs. + * + * 2 6/11/97 1:09p Samir + * Virtual destructors. + * + * 1 6/10/97 4:55p Samir + * Old osDatabase code merged into AppDatabase. + * + * $NoKeywords: $ + */ + +#ifndef APPDATABASE +#define APPDATABASE + +#include "pstypes.h" + +/* oeAppDatabase + to get info about the application from a managed database (or a custom info file) + Again, this class should be the parent of a platform specific class like osWinDatabase, for instance. +*/ + +class oeAppDatabase +{ +public: + oeAppDatabase() {}; + +// you can also create a reference to a current database. this is good if +// you have a hierachical database. + oeAppDatabase(oeAppDatabase *parent) {}; + virtual ~oeAppDatabase() {}; + +// creates an empty classification or structure where you can store information + virtual bool create_record(const char *pathname) = 0; + +// set current database focus to a particular record + virtual bool lookup_record(const char *pathname) = 0; + +// read either an integer or string from the current record + virtual bool read(const char *label, char *entry, int *entrylen) = 0; + virtual bool read(const char *label, void *entry,int wordsize) = 0; //read an variable-sized integer + virtual bool read(const char *label, bool *entry) = 0; + +// write either an integer or string to a record. + virtual bool write(const char *label, const char *entry, int entrylen) = 0; + virtual bool write(const char *label, int entry) = 0; + +// get the current user's name. + virtual void get_user_name(char* buffer, ulong* size) = 0; +}; + +// JCA: moved these from the Win32Database + +//Handy macro to read an int without having to specify the wordsize +#define read_int(label,varp) read(label,varp,sizeof(*varp)) + +//Macro to write a string +#define write_string(label,varp) write(label,varp,strlen(varp)) + +/* This section includes the platform-specific header files + Add a platform to this list once implemented: + + Win32 Samir Win32Database.h 06/97 +*/ + +#if defined(WIN32) +#include "win\Win32Database.h" +#elif defined(__LINUX__) +#include "linux/lnxdatabase.h" +#elif defined(MACINTOSH) +#include "macdatabase.h" +#endif + + + +#endif \ No newline at end of file diff --git a/lib/application.h b/lib/application.h new file mode 100644 index 000000000..1508221ef --- /dev/null +++ b/lib/application.h @@ -0,0 +1,100 @@ +/* + * $Logfile: /DescentIII/Main/lib/application.h $ + * $Revision: 11 $ + * $Date: 7/28/99 3:29p $ + * $Author: Kevin $ + * + * Generic Application object. Used to pass info to libraries + * + * $Log: /DescentIII/Main/lib/application.h $ + * + * 11 7/28/99 3:29p Kevin + * Macintosh! + * + * 10 4/15/99 1:44a Jeff + * changes for linux compile + * + * 9 10/08/98 7:26p Samir + * changed the prototype for the defer handler callback. + * + * 8 9/14/98 4:03p Samir + * added console support. + * + * 7 3/23/98 8:04p Samir + * defer handler now returns a bool. + * + * 6 2/26/98 1:00p Samir + * Added application activation functions. + * + * 5 2/23/98 4:30p Samir + * added init function to oeApplication. + * + * 4 10/16/97 2:30p Samir + * Added Idle processing. + * + * 3 9/16/97 1:04p Samir + * Added delay function. + * + * 2 7/28/97 3:46p Samir + * Added OEAPP_TOPMOST flag. + * + * 2 6/11/97 2:39p Samir + * Added destructors. + * + * 1 6/10/97 4:55p Samir + * New App object. + * + * $NoKeywords: $ + */ +#ifndef APP_H +#define APP_H +#include "pstypes.h" +/* Basic Application data types */ +/* Application Object + This object entails initialization and cleanup of all operating system + elements, as well as data that libraries may need to initialize their + systems. Look under the specific header file for a platform for information + about what's needed for the target machine. +*/ +/* Application flags */ +const int OEAPP_WINDOWED = 1, // App will run in a window. May not be supported. + OEAPP_FULLSCREEN = 2, // App will run in fullscreen. May not be supported. + OEAPP_TOPMOST = 4, // App will be on the topmost display. May not be supported. + OEAPP_CONSOLE = 8; // App will run in a console style window. +class oeApplication +{ +protected: + bool m_AppActive; +public: + oeApplication() { m_AppActive = true; }; + virtual ~oeApplication() {}; +// initializes the object + virtual void init() = 0; +// Function to retrieve information from object through a platform defined structure. + virtual void get_info(void *buffer) = 0; +// Function to get the flags + virtual int flags(void) const = 0; +// defer returns some flags. essentially this function defers program control to OS. + virtual unsigned defer() = 0; +// suspends application for a certain amout of time... + virtual void delay(float secs) = 0 ; +// set a function to run when deferring to OS. + virtual void set_defer_handler(void (*func)(bool)) = 0; +public: +// checks if the application is active + bool active() const { return m_AppActive; }; + void activate() { m_AppActive = true; }; + void deactivate() { m_AppActive = false; }; +}; +/* This section includes the platform-specific header files + Add a platform to this list once implemented: + Win32 Samir Win32App.h 06/97 +*/ +#if defined(WIN32) +#include "win\Win32App.h" +#elif defined(__LINUX__) +#include "linux/lnxapp.h" +#elif defined(MACINTOSH) +#include "macapp.h" +#endif +#endif diff --git a/lib/audio_encode.h b/lib/audio_encode.h new file mode 100644 index 000000000..caf3f5694 --- /dev/null +++ b/lib/audio_encode.h @@ -0,0 +1,28 @@ +/* +* $Logfile: /DescentIII/Main/lib/audio_encode.h $ +* $Revision: 1 $ +* $Date: 1/27/99 1:33a $ +* $Author: Jeff $ +* +* Header file for audio compression library +* +* $Log: /DescentIII/Main/lib/audio_encode.h $ + * + * 1 1/27/99 1:33a Jeff +* +* $NoKeywords: $ +*/ + + +#ifndef __AUDIO_ENCODE_H_ +#define __AUDIO_ENCODE_H_ + +//input_levels (default 7 or for 2k total) +//input_samples (default 16 or for 2k total) +//input_rate (default 22K) +//input_channels (default 1) +//input_factor (compression factor) (default 4 for 22K, 8 for 44K) +//input_volscale (Volume scaling) (slightly <= 1.0, default ,97) +int aenc_Compress(char *input_filename,char *output_filename,int *input_levels=NULL,int *input_samples=NULL,int *input_rate=NULL,int *input_channels=NULL,float *input_factor=NULL,float *input_volscale=NULL); + +#endif \ No newline at end of file diff --git a/lib/bitmap.h b/lib/bitmap.h new file mode 100644 index 000000000..4b436b96b --- /dev/null +++ b/lib/bitmap.h @@ -0,0 +1,142 @@ +#ifndef PSBITMAP_H +#define PSBITMAP_H +#include "pstypes.h" +#include "CFILE.H" +#ifdef __LINUX__ +#include "linux/linux_fix.h" //needed for stricmp's throughout bitmap lib +#endif +#define MAX_BITMAPS 5000 +#ifndef MACINTOSH //DAJ do this in opengl/glide headers +#define NUM_MIP_LEVELS 5 +#endif +// It really doesn't matter what these are, as long as its above 10 +#define OUTRAGE_4444_COMPRESSED_MIPPED 121 +#define OUTRAGE_1555_COMPRESSED_MIPPED 122 +#define OUTRAGE_NEW_COMPRESSED_MIPPED 123 +#define OUTRAGE_COMPRESSED_MIPPED 124 +#define OUTRAGE_COMPRESSED_OGF_8BIT 125 +#define OUTRAGE_TGA_TYPE 126 +#define OUTRAGE_COMPRESSED_OGF 127 +#define BITMAP_NAME_LEN 35 +#define BAD_BITMAP_HANDLE 0 +// Bitmap flags +#define BF_TRANSPARENT 1 +#define BF_CHANGED 2 // this bitmap has changed since last frame (useful for hardware cacheing) +#define BF_MIPMAPPED 4 // This bitmap has mip levels +#define BF_NOT_RESIDENT 8 // This bitmap is not paged in +#define BF_WANTS_MIP 16 // Calculate mip levels when this bitmap is paged in +#define BF_WANTS_4444 32 // Read data as 4444 when this bitmap is paged in +#define BF_BRAND_NEW 64 // This bitmap was just allocated and hasn't been to the video card +#define BF_COMPRESSABLE 128 // This bitmap is compressable for 3dhardware that supports it +// Bitmap priorities +#define BITMAP_FORMAT_STANDARD 0 +#define BITMAP_FORMAT_1555 0 +#define BITMAP_FORMAT_4444 1 +typedef struct +{ + ushort *data16; // 16bit data + ushort width,height; // Width and height in pixels + ushort used; // Is this bitmap free to be allocated? + + short cache_slot; // For use by the rendering lib + ubyte mip_levels; + ubyte flags; + + ubyte format; // See bitmap format types above + char name[BITMAP_NAME_LEN]; // Whats the name of this bitmap? (ie SteelWall) +} bms_bitmap; +typedef struct chunked_bitmap +{ + int pw, ph; // pixel width and height + int w, h; // width and height in square bitmaps. + int *bm_array; // array of bitmap handles. +} +chunked_bitmap; +extern bms_bitmap GameBitmaps[MAX_BITMAPS]; +extern ulong Bitmap_memory_used; +extern ubyte Memory_map[]; +// Sets all the bitmaps to unused +void bm_InitBitmaps(); +// Frees up all memory used by bitmaps +void bm_ShutdownBitmaps(void); +// Allocs a bitmap of w x h size +// If add_mem is nonzero, adds that to the amount alloced +// (added due to the way the tmapper works) +// Returns bitmap handle if successful, -1 if otherwise +int bm_AllocBitmap (int w,int h,int add_mem); +// Given a handle, frees the bitmap memory and flags this bitmap as unused +void bm_FreeBitmap (int handle); +// Allocs and loads a bitmap +// Loads either regular TGAs or OUTRAGE_TGA_TYPEs +// Returns the handle of the loaded bitmap +// Returns -1 if something is wrong +int bm_AllocLoadFileBitmap (const char *filename,int mipped,int format=BITMAP_FORMAT_1555); +// Allocs and loads a bitmap from an open file +// Returns the handle of the loaded bitmap +// Returns -1 if something is wrong +// If mipped is non-zero, allocs extra space for mips and computes them +int bm_AllocLoadBitmap (CFILE *infile,int mipped,int format=BITMAP_FORMAT_1555); +// Given a handle, makes a big random shape to let you know you are screwed. +void bm_MakeBad (int handle); +// Searches thru all bitmaps for a specific name, returns -1 if not found +// or index of bitmap with name +int bm_FindBitmapName (const char *name); +// Saves a bitmap as an OUTRAGE_TGA_TYPE to an open file +// Returns -1 if something is wrong. +int bm_SaveBitmap (CFILE *fp,int handle); +// Saves a bitmap to a file. Saves the bitmap as an OUTRAGE_TGA_TYPE. +// Returns -1 if something is wrong. +int bm_SaveFileBitmap (const char *filename,int handle); +// given a handle to a bitmap, returns its width, or -1 if handle is invalid +int bm_w (int handle,int miplevel); +// given a handle to a bitmap, returns its height, or -1 if handle is invalid +int bm_h (int handle,int miplevel); +// given a handle to a bitmap, returns mipped status, or -1 if handle is invalid +int bm_mipped (int handle); +// a function to determine if a pixel in a bitmap is transparent +bool bm_pixel_transparent(int bm_handle,int x,int y); +// a function to determine if a pixel in a bitmap is transparent +ushort bm_pixel(int bm_handle,int x,int y); +// given a handle to a bitmap, returns a pointer to its data, or NULL if handle is invalid +ushort *bm_data (int handle,int miplevel); +// Gets bits per pixel for a particular bitmap +// As of 12/30/96 always returns 16 +int bm_bpp (int handle); +// Given a source bitmap, generates mipmaps for it +void bm_GenerateMipMaps (int handle); +// Given two bitmaps, scales the data from src to the size of dest +void bm_ScaleBitmapToBitmap (int dest,int src); +// Returns whether or not this bitmap is in use +int bm_used (int n); +// Loads a series of bitmaps from an IFF file +int bm_AllocLoadIFFAnim (const char *filename,int *dest_index,int mipped); +// given a handle and a miplevel, returns the bytes per bitmap row +int bm_rowsize (int handle,int miplevel); +// Goes through the bitmap and sees if there is any transparency...if so, flag it! +int bm_SetBitmapIfTransparent (int handle); +// Saves the passed bitmap handle as a 24 bit uncompressed tga +int bm_SaveBitmapTGA (const char *filename,int handle); +// Sets the bitmap priority. This comes in handy for our 3d hardware +void bm_set_priority (int handle,int priority); +// Allocs and loads a bitmap but doesn't actually load texel data! +// Returns the handle of the loaded bitmap +// Returns -1 if something is wrong +int bm_AllocLoadFileNoMemBitmap (const char *filename,int mipped,int format=BITMAP_FORMAT_1555); +// Just like bm_AllocBitmap but doesn't actually allocate memory. Useful for paging! +int bm_AllocNoMemBitmap (int w,int h); +// clears bitmap +void bm_ClearBitmap(int handle); +// Changes the size of a bitmap to a new size +void bm_ChangeSize(int handle,int new_w,int new_h); +void bm_ChangeEndName (const char *src,char *dest); +// takes a large static bitmap and breaks it into smaller managable bitmaps +bool bm_CreateChunkedBitmap(int bm_handle, chunked_bitmap *chunk); +// destroys a chunked bitmap. +void bm_DestroyChunkedBitmap(chunked_bitmap *chunk); +// simply frees up a bitmap +void bm_FreeBitmapData(int handle); +// Returns the format of this bitmap +int bm_format (int handle); +// Returns the number of mipmap levels +int bm_miplevels (int handle); +#endif diff --git a/lib/bumpmap.h b/lib/bumpmap.h new file mode 100644 index 000000000..9770706c1 --- /dev/null +++ b/lib/bumpmap.h @@ -0,0 +1,46 @@ +#ifndef BUMPMAP_H +#define BUMPMAP_H + +#include "pstypes.h" + +#ifdef MACINTOSH +#define MAX_BUMPMAPS 1 +#else +#define MAX_BUMPMAPS 500 +#endif +#define BAD_BUMP_INDEX 65535 + +#define BUMPF_USED 1 +#define BUMPF_CHANGED 2 + +typedef struct +{ + ushort *data; // 8bit data + short cache_slot; // for the renderers use + ubyte width,height; + ubyte flags,pad; +} bms_bumpmap; + +extern bms_bumpmap GameBumpmaps[MAX_BUMPMAPS]; + +// Sets all the bumpmaps to unused +void bump_InitBumpmaps(); + +void bump_ShutdownBumpmaps (void); + +// Allocs a bumpmap of BUMP_WIDTH * BUMP_HEIGHT size +// Returns bumpmap handle if successful, -1 if otherwise +int bump_AllocBumpmap (int w,int h); + +// Given a handle, frees the bumpmap memory and flags this bumpmap as unused +void bump_FreeBumpmap (int handle); + +// returns a bumpmaps data else NULL if something is wrong +ushort *bump_data (int handle); + +// returns width or height of the passed in bumpmap +ubyte bump_w(int handle); +ubyte bump_h(int handle); + + +#endif diff --git a/lib/collide.h b/lib/collide.h new file mode 100644 index 000000000..ff6632793 --- /dev/null +++ b/lib/collide.h @@ -0,0 +1,121 @@ +/* +* $Logfile: /DescentIII/Main/lib/collide.h $ +* $Revision: 15 $ +* $Date: 6/10/99 6:41p $ +* $Author: Chris $ +* +* Descent III collide code +* +* $Log: /DescentIII/Main/lib/collide.h $ + * + * 15 6/10/99 6:41p Chris + * Added ned_physics support + * + * 14 4/23/99 9:22p Matt + * Changed weapons to keep going after breaking glass. + * + * 13 4/14/99 3:59a Jeff + * fixed case mismatches in #includes + * + * 12 1/29/99 5:09p Chris + * Made changes for ROCKS + * + * 11 1/01/99 4:10p Chris + * Added some const parameters, improved ray cast object collide/rejection + * code + * + * 10 10/09/98 7:47p Chris + * Added ObjSetDeadFlag + * + * 9 6/15/98 7:01a Chris + * Cleaned out DII stuff and added new PhysicsSim extern's + * + * 8 5/07/98 2:22p Chris + * Hit die dot + * + * 7 5/05/98 3:42p Chris + * Code cleanup and fixed the collide_XXX_with_wall code. The wall_normal + * was know by fvi; so, it is passed to the function (instead of + * generating it again) + * + * 6 2/05/98 2:02p Chris + * Fixed the hitseg and hitwall in collide_object_with_wall and + * scrape_object_with_wall + * + * 5 10/21/97 4:15p Chris + * Incremental integration of the fvi/physics/collide code + * + * 4 10/20/97 11:55a Chris + * Added some support for the new collide system. + * + * 3 9/17/97 10:59a Chris + * Added a new way to compute radi + * + * 2 9/15/97 5:17a Chris + * Removed the Explosion Vclips since they do not play correctly in + * Hardware + * Added support for building collisons. + * working on SPhere to non-moving sphere collisions + * + * 5 3/15/97 1:29p Chris +* +* $NoKeywords: $ +*/ + +#ifndef _COLLIDE_H +#define _COLLIDE_H + +#include "object.h" +#include "vecmat.h" +#include "findintersection.h" + +extern ubyte CollisionResult[MAX_OBJECT_TYPES][MAX_OBJECT_TYPES]; +extern ubyte CollisionRayResult[MAX_OBJECT_TYPES]; + +void CollideInit(); +void collide_two_objects( object * A, object * B, vector *collision_point, vector *collision_normal, fvi_info *hit_info = NULL); +extern void apply_damage_to_player(object *player, object *killer, float damage); + +//Process a collision between an object and a wall +//Returns true if the object hits the wall, and false if should keep going though the wall (for breakable glass) +bool collide_object_with_wall( object * A, float hitspeed, int hitseg, int hitwall, vector * hitpt, vector *wall_normal, float hit_dot ); + +extern int apply_damage_to_robot(object *robot, float damage, int killer_objnum); + +extern int Immaterial; + +extern void collide_player_and_weapon( object * player, object * weapon, vector *collision_point ); +extern void collide_player_and_materialization_center(object *objp); +extern void collide_robot_and_materialization_center(object *objp); + +extern void scrape_object_on_wall(object *obj, int hitseg, int hitwall, vector * hitpt, vector * wall_normal ); +extern int maybe_detonate_weapon(object *obj0p, object *obj, vector *pos); + +extern void collide_player_and_nasty_robot( object * player, object * robot, vector *collision_point ); + +extern void net_destroy_controlcen(object *controlcen); +extern void collide_player_and_powerup( object * player, object * powerup, vector *collision_point ); +//extern int check_effect_blowup(segment *seg,int side,vector *pnt, object *blower, int force_blowup_flag); +extern void apply_damage_to_controlcen(object *controlcen, float damage, short who); +extern void bump_one_object(object *obj0, vector *hit_dir, float damage); + +void ConvertEulerToAxisAmount(vector *e, vector *n, float *w); +void ConvertAxisAmountToEuler(vector *n, float *w, vector *e); + +void bump_obj_against_fixed(object *obj, vector *collision_point, vector *collision_normal); + +#ifndef NED_PHYSICS +#define RESULT_NOTHING 0 +#define RESULT_CHECK_SPHERE_SPHERE 1 +#define RESULT_CHECK_SPHERE_POLY 2 +#define RESULT_CHECK_POLY_SPHERE 3 +#define RESULT_CHECK_BBOX_POLY 4 +#define RESULT_CHECK_POLY_BBOX 5 +#define RESULT_CHECK_BBOX_BBOX 6 +#define RESULT_CHECK_BBOX_SPHERE 7 +#define RESULT_CHECK_SPHERE_BBOX 8 +#define RESULT_CHECK_SPHERE_ROOM 9 +#define RESULT_CHECK_BBOX_ROOM 10 +#endif + +#endif diff --git a/lib/d3events.h b/lib/d3events.h new file mode 100644 index 000000000..b5911744b --- /dev/null +++ b/lib/d3events.h @@ -0,0 +1,106 @@ +/* + * $Source: $ + * $Revision: 9 $ + * $Author: Jeff $ + * $Date: 7/13/99 10:04a $ + * + * D3 Events moved from D3OsiEvents.h + * + * $Log: /DescentIII/Main/lib/d3events.h $ + * + * 9 7/13/99 10:04a Jeff + * text taunt token decoding + * + * 8 5/10/99 2:42a Jeff + * added event/function to get the team of a connecting player + * + * 7 5/08/99 4:30a Jeff + * fixed sequencing bug where clients never got a level end event for the + * multiplayer games + * + * 6 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 5 2/07/99 1:19a Jeff + * added new multiplayer game events EVT_GAMEOBJKILLED and + * EVT_GAMEOBJDESTROYED + * + * 4 1/18/99 12:04a Jeff + * added some new events + * + * 3 1/15/99 8:29p Jeff + * updates to powerball + * + * 2 1/15/99 3:57a Jeff + * added 2 new events (for weapon collisions) + * + * 1 1/08/99 4:14p Samir + * + */ + +#ifndef D3EVENTS_H +#define D3EVENTS_H + +// game events. + +#define EVT_GAMEPLAYERKILLED 0x500 //called in game script when a player gets killed +#define EVT_GAMEPLAYEREXPLODED 0x501 //called in game script when a player explodes +#define EVT_GAMECOLLIDE 0x502 //called in game script when 2 object collide (one of the two must be player or robot) +#define EVT_GAMEPLAYERCHANGESEG 0x503 //called when a player changes rooms or 'large' terrain cell (8x8 terrain cell block) +#define EVT_GAMEPLAYERENTERSGAME 0x504 //called when a player enters the game +#define EVT_GAMEPLAYERDISCONNECT 0x505 //called when a player disconnects from the game +#define EVT_GAMECREATED 0x506 // called when a server creates a game +#define EVT_GAMELEVELCHANGE 0x507 // called when the server changes a level +#define EVT_GAMELEVELSTART 0x508 // called when the server starts a new level +#define EVT_GAMEOBJECTSHIELDSCHANGED 0x510 // called when an object's shields changes (higher or lower) +#define EVT_GAMECHECKBAN 0x511 // called to the dlls with a network address to determine if a player is banned +#define EVT_GAMEDOCONTROLS 0x512 // called to the multiplayer games so they can do any controller related movement +#define EVT_GAMEWALLCOLLIDE 0x513 // called when there is a collision between an object and a wall +#define EVT_GAMEOBJCHANGESEG 0x514 // called when an object (other than OBJ_PLAYER) changes room/segment +#define EVT_GAMEOBJDESTROYED 0x515 // called when an object is about to be deleted +#define EVT_GAMEOBJKILLED 0x516 // called when an object was killed by another object +#define EVT_GAME_GET_PLAYER_TEAM 0x517 // called when the game needs to get the team of a player + +// Multiplayer client events +#define EVT_CLIENT_INTERVAL 0x600 //called every frame +#define EVT_CLIENT_AI 0x601 //called every frame for AI info +#define EVT_CLIENT_DAMAGED 0x602 //called when object is damaged +#define EVT_CLIENT_COLLIDE 0x603 //called when object collides with something +#define EVT_CLIENT_CREATED 0x604 //called when object created +#define EVT_CLIENT_DESTROY 0x605 //called when object destroyed +#define EVT_CLIENT_TIMER 0x606 //called when a timer event is signalled +#define EVT_CLIENT_USE 0x607 //called when item is selected for use from inventory +#define EVT_CLIENT_GAMEPLAYERKILLED 0x608 //called in game script when a player gets killed +#define EVT_CLIENT_GAMEPLAYEREXPLODED 0x609 //called in game script when a player explodes +#define EVT_CLIENT_GAMECOLLIDE 0x60A //called in game script when 2 object collide (one of the two must be player or robot) +#define EVT_CLIENT_GAMEPLAYERCHANGESEG 0x60B //called when a player changes rooms or 'large' terrain cell (8x8 terrain cell block) +#define EVT_CLIENT_GAMEPLAYERENTERSGAME 0x60C //called when a player enters the game +#define EVT_CLIENT_GAMEPLAYERDISCONNECT 0x60D //called when a player disconnects from the game +#define EVT_CLIENT_GAMECREATED 0x60E // called when a multiplayer game is created +#define EVT_CLIENT_GAMELEVELCHANGE 0x60F // called when the server changes a level +#define EVT_CLIENT_GAMESPECIALPACKET 0x610 // called when a special packet has arrived +#define EVT_CLIENT_HUD_INTERVAL 0x611 // called once a frame when the hud is being drawn +#define EVT_CLIENT_KEYPRESS 0x612 // called whenever a keypress is made +#define EVT_CLIENT_INPUT_STRING 0x613 // called when a player types in a message +#define EVT_CLIENT_GAMELEVELSTART 0x614 // called when the server starts a new level +#define EVT_CLIENT_GAMELEVELEND 0x615 // called when the server ends a level +#define EVT_CLIENT_GAMEPOSTLEVELRESULTS 0x616 // called when the dll needs to display the post level results +#define EVT_CLIENT_GAMEOBJECTSHIELDSCHANGED 0x617 // called when an object's shields changes (higher or lower) +#define EVT_CLIENT_LEVELSTART EVT_CLIENT_CREATED +#define EVT_CLIENT_LEVELEND EVT_CLIENT_DESTROY +#define EVT_CLIENT_CHANGESEG 0x618 // called when an object changes rooms. +#define EVT_CLIENT_SHOWUI 0x619 //The game is allowing the client DLL to show UI windows +#define EVT_WEAPONFIRED 0x620 // called whenever a weapon is fired +#define EVT_CLIENT_GAMEPLAYERENTERSOBSERVER 0x621 //A player is entering observer mode +#define EVT_CLIENT_GAMEPLAYEREXITSOBSERVER 0x622 //A player is exiting observer mode +#define EVT_CLIENT_GETCOLOREDNAME 0x623 // DLL is telling game what color to draw the names in +#define EVT_GAME_INTERVAL 0x624 // The interval frame for a game +#define EVT_GAME_DISCONNECTED 0x625 // Event called if you disconnect from the server +#define EVT_CLIENT_GAMEWALLCOLLIDE 0x626 // called when there is a collision between an object and a wall +#define EVT_CLIENT_GAMEOBJCHANGESEG 0x627 // called when an object (other than OBJ_PLAYER) changes room/segment +#define EVT_CLIENT_GAMEOBJDESTROYED 0x628 // called when an object is about to be killed +#define EVT_CLIENT_GAMEOBJKILLED 0x629 // called when an object was killed by another object +#define EVT_CLIENT_PLAYERPLAYSAUDIOTAUNT 0x62A // called when a player is playing an audio taunt +#define EVT_CLIENT_DECODETEXTMACRO 0x62B // called when a player uses a text macro +#endif \ No newline at end of file diff --git a/lib/d3music.h b/lib/d3music.h new file mode 100644 index 000000000..60b25be59 --- /dev/null +++ b/lib/d3music.h @@ -0,0 +1,158 @@ +/* + * $Source: $ + * $Revision: 14 $ + * $Author: Samir $ + * $Date: 3/27/99 4:45p $ + * + * D3 Music System + * + * $Log: /DescentIII/Main/Lib/d3music.h $ + * + * 14 3/27/99 4:45p Samir + * reinstate pause music when pausing game. + * + * 13 3/23/99 9:07p Samir + * turn on and off music when pausing and resuming game, if appropriate. + * + * 12 3/18/99 10:13a Samir + * msuic update. + * + * 11 2/27/99 8:23p Samir + * fixes to music system to act nicely to sudden and frequent region + * changes. + * + * 10 2/27/99 6:51p Samir + * added code for music tester to display current stream and loop/region. + * + * 9 2/27/99 4:37p Samir + * return name of loop currently playing. + * + * 8 2/19/99 10:31p Samir + * added music volume. + * + * 7 2/18/99 6:47p Jeff + * added call to start/stop special cinematics music + * + * 6 1/28/99 2:22p Samir + * simplified music system for D3. + * + * 5 1/07/99 12:28p Samir + * Call to InitD3Music added parameter. + * + * 4 12/07/98 11:44a Samir + * added new themes. + * + * 3 12/03/98 12:49p Samir + * added positive and negative mood timers. + * + * 2 11/20/98 5:23p Samir + * pass mood register to sequencer. + * + * 1 11/13/98 2:30p Samir + * initial rev. + * + */ + +#ifndef D3MUSIC_H +#define D3MUSIC_H + +#include "pstypes.h" + +// register constants for the sequencer +#define MUSICREG_TRIGGER_VALUE 1 // trigger value set by calling app to sequencer +//@@#define MUSICREG_MOOD_VALUE 2 // current mood of player stored here. +//@@#define MUSICREG_NEGMOOD_TIMER 3 // time in negative mood +//@@#define MUSICREG_POSMOOD_TIMER 4 // time in positive mood +#define MUSICREG_PEACE_TIMER 5 // amount of time in 'non-combat' mode. + +// types of triggers +#define MUSICTRIGGER_NONE 0 +#define MUSICTRIGGER_NEWREGION 1 // player entered new region + +extern const char *Music_type_names[]; // contains type names. + +// structure passed to music frame +typedef struct tMusicSeqInfo +{ +// INPUT + bool started_level; // player started level + bool player_dead; // did player die? + bool player_damaged; // was player hit by enemy fire? + bool player_invulnerable; // is player invulnerable? + bool player_terrain; // is player in terrain (if not, in mine) + ubyte player_shield_level; // what shield level the player is at? (0-10) + ubyte n_hostiles; // number of hostiles + ubyte n_hostiles_player_killed; // number hostiles killed by player this frame. + ubyte pad; + + float frametime; // frame time. + +// OUTPUT + short cur_song; // current song. + float peace_timer; // current peace timer + + const char *cur_loop_name; // current loop playing (NULL if none.) + int cur_loop_count; +} +tMusicSeqInfo; + +// this is located in gameloop.cpp. kept here so files that need this data structure don't have to include +// gameloop (and to stop gameloop.h from including d3music.h) +extern tMusicSeqInfo Game_music_info; + +#ifdef _DEBUG +// if true, turns on debugging for music. +extern bool Music_debug_verbose; +#endif + +// Music extensions +void InitD3Music(bool allow_music); + +// closes music system +void CloseD3Music(); + +// starts up the music sequencer +void D3MusicStart(const char *theme_file); + +// stops the music sequencer +void D3MusicStop(); + +// execute music sequencer. +void D3MusicDoFrame(tMusicSeqInfo *music_info); + +// toggle music system. +void D3MusicToggle(); + +// toggle music system. +void D3MusicToggle(bool state); + +// pauses and or resumes music +void D3MusicPause(); +void D3MusicResume(); + +// returns true if music system is on. +bool IsD3MusicOn(); + +// set music region +void D3MusicSetRegion(short region,bool immediate=false); + +// retreives current region (can be different than regin passed to D3MusicSetRegion), +// returns current played region, not waiting region. +short D3MusicGetRegion(); + +// retreives current pending region +short D3MusicGetPendingRegion(); + +// starts special in-game cinematic music +void D3MusicStartCinematic(); + +// stops special in-game cinematic music +void D3MusicStopCinematic(); + +// volume stuff +float D3MusicGetVolume(); +void D3MusicSetVolume(float vol); + + + +#endif \ No newline at end of file diff --git a/lib/d3x_op.h b/lib/d3x_op.h new file mode 100644 index 000000000..ab9564fb8 --- /dev/null +++ b/lib/d3x_op.h @@ -0,0 +1,300 @@ +/* + * $Logfile: /DescentIII/Main/lib/d3x_op.h $ + * $Revision: 20 $ + * $Date: 4/03/98 5:15p $ + * $Author: Samir $ + * + * D3X Opcodes. + * + * $Log: /DescentIII/Main/lib/d3x_op.h $ + * + * 20 4/03/98 5:15p Samir + * Added event parameters and upped registers. + * + * 19 3/26/98 12:18p Samir + * Updated D3X version. + * + * 18 3/19/98 9:41p Samir + * updated script file to now store length of script names. this wasn't + * happening before, and was a major bug that seemed to go unnoticed. + * + * 17 3/11/98 11:22a Samir + * Added new integer opcodes. + * + * 16 2/10/98 10:44a Samir + * Added gamemode script type. + * + * 15 2/05/98 2:44p Samir + * Added dynamic strings. + * + * 14 1/22/98 6:22p Samir + * Moved script parameter info to d3x.h. + * + * 13 1/20/98 4:12p Samir + * New script housekeeping system. + * + * 12 1/19/98 3:48p Samir + * Added script parameter support. + * + * 11 1/19/98 2:44p Samir + * Use one script per object and started parameter passing support. + * + * 10 9/22/97 5:59p Samir + * Added a name field to scripts, so we create threads using name instead + * of number + * + * 9 9/10/97 2:01p Samir + * Added level type for scripts. + * + * 8 9/08/97 4:28p Samir + * Added script type object support, for specialized trigger/object + * scripts. Needed to add type to map file. + * + * 7 9/02/97 6:11p Samir + * Added constant for number of arguments to a script. + * + * 6 8/21/97 5:55p Samir + * Updated map file to store memory usage per script. + * + * 5 8/12/97 3:27p Samir + * Renamed registers to VF (Vector-float) + * + * 4 8/05/97 6:42p Samir + * D3X Script map structure. + * + * 3 8/05/97 10:47a Samir + * Added endscript processing. + * + * 2 8/04/97 4:21p Samir + * Finalized opcodes for D3X processor. + * + * $NoKeywords: $ + */ + +#ifndef OPCODES_H +#define OPCODES_H + +/* This is a list of macro-opcodes */ + +/* + Memory operations + LOAD rx, (ABSOLUTE) + LOAD rx, (ADx+IMM) + LOAD rx, IMM + STORE rx, (ABSOLUTE) + STORE rx, (ADx+IMM) + + Math functions + ADD rx, rx + ADDI rx, rx + SUB rx, rx + MUL rx, rx + DIV rx, rx + AND rx, rx + MOD rx, rx + BAND rx, rx + BANDI rx, rx + OR rx, rx + BOR rx, rx + BORI rx, rx + EQU rx, rx + NEQ rx, rx + LT rx, rx + LTE rx, rx + GT rx, rx + GTE rx, rx + NEG rx + NOT rx + ABS rx + + Math vector functions (Vector element extraction) + VEX rx, vx + VEY rx, vx + VEY rx, vx + XEV vx, rx + YEV vx, rx + ZEV vx, rx + SCALEV vx, i/fx + + Execution Buffer + EPUSH rx + EPOP rx + PCALL FN + CALL ABSOLUTE + + Call Stack Data Operations + CPUSH rx + CPOP rx + TOCSP ADx Transfer ADx reg to CSP reg + FROMCSP ADx Transger CSP reg to ADx reg + RETURN grabs address from current CSP (return from CALL) + + BRANCHING + + JUMP ABSOLUTE Branch uncoditonally + JUMPN ABSOLUTE, rx Jump if rx == 0 +*/ + +/* + registers + ad0, ad1 Address registers + i0, i1 Integer registers + f0, f1 Float registers + v0, v1 Vector registers +*/ + +#define OP_BREAK 0 +#define OP_LOAD_ABS 1 +#define OP_LOAD_ADI 2 +#define OP_LOAD_IMM 3 +#define OP_LOAD_PARM 4 +#define OP_STORE_ABS 5 +#define OP_STORE_ADI 6 +#define OP_STORE_PARM 7 +#define OP_ADD 10 +#define OP_SUB 11 +#define OP_MUL 12 +#define OP_DIV 13 +#define OP_MOD 14 +#define OP_AND 15 +#define OP_OR 16 +#define OP_NEG 17 +#define OP_NOT 18 +#define OP_ABS 19 +#define OP_EQU 20 +#define OP_NEQ 21 +#define OP_LT 22 +#define OP_LTE 23 +#define OP_GT 24 +#define OP_GTE 25 +#define OP_BOR 26 +#define OP_BAND 27 +#define OP_VEX 28 +#define OP_VEY 29 +#define OP_VEZ 30 +#define OP_XEV 31 +#define OP_YEV 32 +#define OP_ZEV 33 +#define OP_ADDI 34 +#define OP_BANDI 35 +#define OP_BORI 36 +#define OP_EPUSH 40 +#define OP_EPOP 41 +#define OP_PCALL 42 +#define OP_CALL 43 +#define OP_CPUSH 44 +#define OP_CPOP 45 +#define OP_TOCSP 46 +#define OP_FROMCSP 47 +#define OP_RET 48 +#define OP_DEFER 49 +#define OP_JUMP_ABS 50 +#define OP_JUMP_NCOND 51 + +#define VFREG_START 0 +#define VFREG_NUM 16 +#define REG_VF0 0 +#define REG_VF1 1 +#define REG_VF2 2 +#define REG_VF3 3 +#define REG_VF4 4 +#define REG_VF5 5 +#define REG_VF6 6 +#define REG_VF7 7 + + +/* Address registers allow indirect addressing. + Ad0 typically accesses local variables from the stack. +*/ +#define AREG_START VFREG_START+VFREG_NUM +#define AREG_NUM 2 +#define REG_AD0 0 +#define REG_AD1 1 + +#define MAX_D3X_REGS 32 + + +typedef struct tD3XInstruction +{ + unsigned char opc; /* Opcode */ + union + { + struct /* Register and or immediate */ + { + unsigned char d; + union + { + int i; + float f; + } + imm; + } + ri; + struct /* register <- Address-Immediate indirect addressing */ + { + unsigned char d,a; + unsigned short imm; + } + aii; + struct /* jump absolute <- register or jump/call absolute (r = 0xff) */ + { + unsigned short abs; + unsigned char r; + } + ra; + struct /* register <- Address-Immediate indirect addressing */ + { + unsigned char d,s; + } + rr; + } + opr; /* Operand depends on Opcode */ +} +tD3XInstruction; + + +/* Values used for defer opcode */ +#define DEFER_END 0 +#define DEFER_DEFAULT 1 + +/* Values defined for type of object the 'me' or 'it' variable will be */ +#define REF_OBJTYPE 0 +#define REF_TRIGTYPE 1 +#define REF_LEVELTYPE 2 +#define REF_GAMEMODE 3 + +/* parameter types for scripts */ +#define PARMTYPE_NUMBER 0 +#define PARMTYPE_VECTOR 1 +#define PARMTYPE_REF 2 +#define PARMTYPE_STRREF 3 + +/* Offsets into call stack which contain the passed arguments to a script + SCRARG_NUM = number of arguments of script (including event arguments) +*/ +#define SCRARG_NUM 4 +#define SCRARG_EVENT 0 +#define SCRARG_ME 1 +#define SCRARG_ITTYPE 2 +#define SCRARG_IT 3 +#define EVTARG_NUM 8 +#define SCRSTACK_START SCRARG_NUM + EVTARG_NUM + +/* Program map entry */ +#define MAX_D3XID_NAME 32 + +typedef struct tD3XPMap +{ + char name[MAX_D3XID_NAME]; + unsigned short ip; + unsigned short mem; + unsigned short type; + unsigned short parms; +} +tD3XPMap; + +#define D3X_TAG "D3X5" + + +#endif + diff --git a/lib/ddio.h b/lib/ddio.h new file mode 100644 index 000000000..824df3315 --- /dev/null +++ b/lib/ddio.h @@ -0,0 +1,453 @@ +/* + * $Logfile: /DescentIII/Main/Lib/ddio.h $ + * $Revision: 30 $ + * $Date: 7/12/99 6:44p $ + * $Author: Jeff $ + * + * Device Dependent IO system header + * + * $Log: /DescentIII/Main/Lib/ddio.h $ + * + * 30 7/12/99 6:44p Jeff + * added directory lock file API functions + * + * 29 4/24/99 5:41p Samir + * moved key to ascii, ascii to key to the ddio_common library. + * + * 28 4/22/99 2:03a Jeff + * pass ddio_init_info through to keyboard handlers + * + * 27 4/01/99 11:26a Samir + * ddio_MouseGetCaps returns the button mask containing valid buttons. + * + * 26 3/23/99 9:05p Samir + * implemented mouse z axis (for Win32, mouse wheel.) and added functions + * to get binding text values. + * + * 25 2/21/99 6:38p Samir + * mouse and key input better. buffered mouse. + * + * 24 2/16/99 5:24p Kevin + * Converted timer to use 64bit int. + * + * 23 2/05/99 1:16p Samir + * reset low level keys when flushing keyboard in the high level. added a + * function to the low level to reset the status of a key, called from + * high level key flush. + * + * 22 2/04/99 11:19a Kevin + * Added function to find a CD drive letter based on it's volume name + * + * 21 1/25/99 6:47p Samir + * allow slow keyboard + * + * 20 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 19 12/08/98 12:17p Jeff + * added 3 functions (similar to ddio_FindFiles) for finding directories + * in a path + * + * 18 11/19/98 5:40p Kevin + * Demo system + * + * 17 10/21/98 12:02p Samir + * properly update odd keys when they are released throug + * ddio_KeyGetDownTime. + * + * 16 10/20/98 12:58a Jeff + * added a way to force a lo-resolution timer + * + * 15 10/16/98 11:55a Kevin + * Made dlls loadable in a hog + * + * 14 8/19/98 2:18p Jeff + * changed ddio_CleanPath + * + * 13 8/15/98 3:42p Matt + * Added new function, ddio_GetFullPath() + * + * 12 7/29/98 5:38p Jeff + * added ddio functions to get parent path, and to clean a path name into + * the real path + * + * 11 7/28/98 6:05p Jeff + * added functions to get root directories (aka drives in windows/dos) + * + * 10 7/28/98 5:05p Samir + * added drive retrieval functions for Jeff. + * + * 9 7/01/98 4:55p Samir + * new simplified mouse system. + * + * 8 6/29/98 6:46p Samir + * MouseInit no longer takes in required number of mouse buttons. + * + * 7 3/19/98 3:18p Samir + * enforce constant char* arguments when needed. done in CFILE and bitmap + * libraries as well as ddio. + * + * 6 2/25/98 6:11p Samir + * Added functions to better deal with key flushing. + * + * 5 1/02/98 12:52p Samir + * Added function to translate keycode to ascii values. + * + * 4 10/16/97 2:29p Samir + * Changed DirectInput Keyboard to FOREGROUND + * + * 3 8/15/97 6:31p Samir + * Added findfile functions. + * + * 2 7/28/97 2:54p Samir + * Temp fix for absolute mouse position return values (Uses GetCursorPos.) + * Added Show and Hide cursor too. + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 26 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 25 5/12/97 1:21p Samir + * Commented out timer hook function calls. + * + * 24 5/08/97 1:55p Samir + * Moved a bunch of functions to ddio_common.h + * + * 23 5/06/97 4:23p Samir + * Ha ha. Jeremy's fault.... + * + * 22 5/6/97 4:24 PM Jeremy + * fixed a bug in ddio_MakePath + * + * 21 5/6/97 3:05 PM Jeremy + * added prototype for ddio_makepath + * + * 20 5/6/97 12:06 PM Jeremy + * changed comments to reflect convention for split path to be in local + * file system syntax + * + * 19 5/6/97 11:45 AM Jeremy + * changed split path to only look for path, filename, extension instead + * of including directory name as well, added prototypes for save/restore + * working dir and changed conventions for relative/absolute nature of + * directory calls + * + * 18 4/25/97 6:17p Jason + * added ddio_Deletefile function + * + * 17 4/16/97 11:32a Samir + * Added z axis support. + * + * 16 4/03/97 4:34p Jason + * added CopyFileTime to the cfile, ddio libs + * + * 15 3/20/97 11:08a Samir + * Added function to peek for keys in queue without removing them. + * + * 14 3/14/97 6:54 PM Jeremy + * added prototypes of ddio_FileDiff, ddio_GetFileLength, ddio_SplitPath + * + * 13 3/13/97 11:09a Samir + * Moved file stuff from gameos to ddio library + * + * 12 1/30/97 6:08p Samir + * Reflect new osObject type instead of gameos_object + * + * 11 1/23/97 2:23p Samir + * Added some more keyboard constants + * + * 10 1/20/97 3:48p Samir + * RCS check in + * + * $NoKeywords: $ + */ + +// ---------------------------------------------------------------------------- +// Device Dependent IO System Main Library Interface - functions here are located +// in the ddio_??? system where ??? is the os. +// ---------------------------------------------------------------------------- + +#ifndef DDIO_H +#define DDIO_H + +class oeApplication; + +#include + +#include "pstypes.h" +#include "ddio_common.h" + +// ---------------------------------------------------------------------------- +// Initialization and destruction functions +// ---------------------------------------------------------------------------- + +// preemptive = 1 for thread based IO. Initializes the IO system +bool ddio_InternalInit(ddio_init_info *init_info); +void ddio_InternalClose(); + +// ---------------------------------------------------------------------------- +// Keyboard Interface +// ---------------------------------------------------------------------------- + +// used by ddio system to initialize machine specific key code. +bool ddio_InternalKeyInit(ddio_init_info *init_info); +void ddio_InternalKeyClose(); + +// handled internally if keyboard system needs additional processing per frame +void ddio_InternalKeyFrame(); +void ddio_InternalKeySuspend(); +void ddio_InternalKeyResume(); + +// returns if key is up or down +bool ddio_InternalKeyState(ubyte key); + +// returns internal key down time +float ddio_InternalKeyDownTime(ubyte key); + +// flush a key internally +void ddio_InternalResetKey(ubyte key); + + +// ---------------------------------------------------------------------------- +// Device Dependent Timer Interface +// ---------------------------------------------------------------------------- + +bool timer_Init(int preemptive,bool force_lores); +void timer_Close(); + +// returns time in seconds +float timer_GetTime(); + +//returns time in milliseconds +longlong timer_GetMSTime(); + +// hook in timer function at certain period. returns a handle to this function +//@@int timer_HookFunction(void (*fncptr)(), int period); + +// clears function from hook list specified by a handle returned from HookFunction +//@@void timer_ReleaseFunction(int func); + + +// ---------------------------------------------------------------------------- +// Device Dependent Mouse Interface +// ---------------------------------------------------------------------------- + +#define MOUSE_LB 1 // mouse button masks +#define MOUSE_RB 2 +#define MOUSE_CB 4 +#define MOUSE_B4 8 +#define MOUSE_B5 16 +#define MOUSE_B6 32 +#define MOUSE_B7 64 +#define MOUSE_B8 128 + +#define N_MSEBTNS 8 + +// initializes mouse. +bool ddio_MouseInit(); +void ddio_MouseClose(); + +// get device caps +int ddio_MouseGetCaps(int *btn, int *axis); + +// use these extensions to set exclusive or standard mouse modes +#define MOUSE_STANDARD_MODE 1 // uses absolute coordinates and simple buttons +#define MOUSE_EXCLUSIVE_MODE 2 // uses relative coordinates and advanced button information +void ddio_MouseMode(int mode); + +// resets position of mouse to center, resets virtual coord system to equal screen coordinate system +void ddio_MouseReset(); + +// resets mouse queue and button info only. +void ddio_MouseQueueFlush(); + +// handled internally if mouse system needs additional processing per frame +void ddio_InternalMouseFrame(); + +// used to prevent mouse input from being registered +void ddio_InternalMouseSuspend(); +void ddio_InternalMouseResume(); + +// returns absolute position of mouse, button state and mouse deltas. +/* x, y = absolute mouse position + dx, dy = mouse deltas since last call + return value is mouse button mask. values not needed should pass NULL pointers. +*/ +int ddio_MouseGetState(int *x, int *y, int *dx, int *dy, int *z=NULL, int *dz=NULL); + +// gets a mouse button event, returns false if none. +bool ddio_MouseGetEvent(int *btn, bool *state); + +// returns string to binding. +const char *ddio_MouseGetBtnText(int btn); +const char *ddio_MouseGetAxisText(int axis); + +// return mouse button down time. +float ddio_MouseBtnDownTime(int btn); + +// return mouse button down time +int ddio_MouseBtnDownCount(int btn); + +// return mouse button up count +int ddio_MouseBtnUpCount(int btn); + +// set bounds for system polling of coordinates +void ddio_MouseSetLimits(int left, int top, int right, int bottom, int zmin=0, int zmax=0); +void ddio_MouseGetLimits(int *left, int *top, int *right, int *bottom, int *zmin=0, int *zmax=0); + +// virtual coordinate system for mouse (match to video resolution set for optimal mouse usage. +void ddio_MouseSetVCoords(int width, int height); + + +// --------------------------------------------------------------------------- +// File Operations +// --------------------------------------------------------------------------- + +// creates or destroys a directory or folder on disk +// This pathname is *RELATIVE* not fully qualified +bool ddio_CreateDir(const char *path); +bool ddio_RemoveDir(const char *path); + +// deletes a file. Returns 1 if successful, 0 on failure +// This pathname is *RELATIVE* not fully qualified +int ddio_DeleteFile (char *name); + +// Save/Restore the current working directory +void ddio_SaveWorkingDir(void); +void ddio_RestoreWorkingDir(void); + +// retrieve the current working folder where file operation will occur. +// Note ---> The path in Get/Set working directory is in the *LOCAL* file system's syntax +// This pathname is relative *OR* fully qualified +void ddio_GetWorkingDir(char *path, int len); +bool ddio_SetWorkingDir(const char *path); + +// Checks if a directory exists (returns 1 if it does, 0 if not) +// This pathname is *RELATIVE* not fully qualified +bool ddio_DirExists(const char* path); + +// get a file length of a FILE +int ddio_GetFileLength(FILE* filePtr); + +// check if two files are different +// This pathname is *RELATIVE* not fully qualified +bool ddio_FileDiff(const char* fileNameA, const char* fileNameB); + +// copies one files timestamp to another +void ddio_CopyFileTime(char* dest, const char* src); + +// Split a pathname into its component parts +// The path in splitpath is in the *LOCAL* file system's syntax +void ddio_SplitPath(const char* srcPath, char* path, char* filename, char* ext); + +// pass in a pathname (could be from ddio_SplitPath), root_path will have the drive name. +void ddio_GetRootFromPath(const char *srcPath, char *root_path); + +// retrieve root names, free up roots array (allocated with malloc) after use +int ddio_GetFileSysRoots(char **roots, int max_roots); + +// Constructs a path in the local file system's syntax +// builtPath: stores the constructed path +// absolutePathHeader: absolute path on which the sub directories will be appended +// (specified in local file system syntax) +// subdir: the first subdirectory +// takes a variable number of additional subdirectories which will be concatenated on to the path +// the last argument in the list of sub dirs *MUST* be NULL to terminate the list +void ddio_MakePath(char* newPath, const char* absolutePathHeader, const char* subDir, ...); + +// These functions allow one to find a file +// You use FindFileStart by giving it a wildcard (like *.*, *.txt, u??.*, whatever). It returns +// a filename in namebuf. +// Use FindNextFile to get the next file found with the wildcard given in FindFileStart. +// Use FindFileClose to end your search. +bool ddio_FindFileStart(const char *wildcard, char *namebuf); +bool ddio_FindNextFile(char *namebuf); +void ddio_FindFileClose(); + +void ddio_FindDirClose(); +bool ddio_FindDirStart(const char *wildcard, char *namebuf); +bool ddio_FindNextDir(char *namebuf); + + +// given a path (with no filename), it will return the parent path +// srcPath is the source given path +// dest is where the parent path will be placed +// returns true on success +// dest should be at least _MAX_PATH in length +bool ddio_GetParentPath(char *dest,const char* srcPath); + +// given a path, it cleans it up (if the path is c:\windows\..\dos it would make it c:\dos) +// srcPath is the original path +// dest is the finished cleaned path. +// dest should be at least _MAX_PATH in size +void ddio_CleanPath(char *dest,const char* srcPath); + +//Finds a full path from a relative path +//Parameters: full_path - filled in with the fully-specified path. Buffer must be at least _MAX_PATH bytes long +// rel_path - a path specification, either relative or absolute +//Returns TRUE if successful, FALSE if an error +bool ddio_GetFullPath(char *full_path,const char *rel_path); + +//Generates a temporary filename based on the prefix, and basedir +//Parameters: +// basedir - directory to put the files +// prefix - prefix for the temp filename +// filename - buffer to hold generated filename (must be at least _MAX_PATH in length) +// +//Returns TRUE if successful, FALSE if an error +bool ddio_GetTempFileName(char *basedir,char *prefix,char *filename); + +//Renames file +//Returns true on success or false on an error +bool ddio_RenameFile(char *oldfile,char *newfile); + + +//Give a volume label to look for, and if it's found returns a path +//If it isn't found, return "" +char * ddio_GetCDDrive(char *vol); + +// Checks to see if a lock file is located in the specified directory. +// Parameters: +// dir Directory for which the lock file should be checked +// Returns: +// 1 Lock file doesn't exist +// 2 Lock file was in a directory, but it belonged to a process that no longer +// exists, so a lock file _can_ be made in the directory. +// 3 Lock file for this process already exists +// 0 Lock file currently exists in directory +// -1 Illegal directory +// -2 There is a lock file in the directory, but it is in an illegal format +int ddio_CheckLockFile(const char *dir); + +// Creates a lock file in the specified directory +// Parameters: +// dir Directory for which the lock file should be created in +// Returns: +// 1 Lock file created +// 2 Lock file created (there was a lock file in that directory, but it belonged +// to a process that no longer exists) +// 3 Lock file for this process already exists +// 0 Lock file not created, a lock file currently exists in the directory +// -1 Illegal directory +// -2 There is a lock file in the directory, but it is in an illegal format +// -3 Unable to create lock file +int ddio_CreateLockFile(const char *dir); + +// Deletes a lock file (for the current process) in the specified directory +// Parameters: +// dir Directory for which the lock file should be deleted from +// Returns: +// 1 Lock file deleted +// 0 Lock file not deleted, the lock file in the directory does not belong to our +// process +// -1 Illegal directory +// -2 A lock file exists in the directory, but wasn't deleted...illegal format +// -3 Unable to delete file +int ddio_DeleteLockFile(const char *dir); + + + + +#endif \ No newline at end of file diff --git a/lib/ddio_common.h b/lib/ddio_common.h new file mode 100644 index 000000000..43cadfcde --- /dev/null +++ b/lib/ddio_common.h @@ -0,0 +1,432 @@ +/* + * $Logfile: /DescentIII/Main/Lib/ddio_common.h $ + * $Revision: 1.3 $ + * $Date: 2004/02/09 04:14:51 $ + * $Author: kevinb $ + * + * Common DDIO functions + * + * $Log: ddio_common.h,v $ + * Revision 1.3 2004/02/09 04:14:51 kevinb + * Added newlines to all headers to reduce number of warnings printed + * + * Made some small changes to get everything compiling. + * + * All Ready to merge the 1.5 tree. + * + * Revision 1.2 2000/09/22 19:04:37 icculus + * SDLK_WORLD support + * + * Revision 1.1.1.1 2000/04/18 00:00:38 icculus + * initial checkin + * + * + * 20 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 19 5/11/99 11:18a Jeff + * added serial support + * + * 18 4/24/99 5:41p Samir + * moved key to ascii, ascii to key to the ddio_common library. + * + * 17 4/22/99 2:48a Jeff + * fixed changed prototype + * + * 16 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 15 2/21/99 6:38p Samir + * mouse and key input better. buffered mouse. + * + * 14 1/25/99 6:47p Samir + * allow slow keyboard + * + * 13 1/25/99 11:02a Samir + * revamped mouse and key controls. + * + * 12 10/20/98 12:58a Jeff + * added a way to force a lo-resolution timer + * + * 11 9/17/98 12:50p Samir + * added ddio_KeyFlushKey. + * + * 10 8/12/98 2:54p Matt + * Renamed the slash and backslash key constants. + * + * 9 7/01/98 4:55p Samir + * new simplified mouse system. + * + * 8 6/30/98 4:20p Samir + * added ddio_Close as standalone. ddio_Init will no longer close + * itself. + * + * 7 6/24/98 3:26p Samir + * changed codes for pause and NUMLOCK, since they seem different under + * windows. + * + * 6 3/24/98 4:26p Samir + * added function to return the state of an adjusted key. + * + * 5 2/25/98 6:11p Samir + * Added functions to better deal with key flushing. + * + * 4 12/10/97 1:12p Samir + * Pass time to ddio_UpdateKeyState + * + * 3 10/29/97 4:14p Samir + * Keep record of key down count per key. + * + * 2 10/16/97 2:28p Samir + * move keyboard init into ddio_init and added preemptive select for + * keyboard. + * + * 4 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 3 5/09/97 6:45p Samir + * Common keystate update function + * + * 2 5/08/97 1:55p Samir + * Move initialization stuff to ddio_common.h + * + * 1 5/08/97 12:34p Samir + * Moved functions from ddio.h to ddio_common.h + * + * $NoKeywords: $ + */ + +#ifndef DDIO_COMMON_H +#define DDIO_COMMON_H + +#include "pstypes.h" + + +class oeApplication; + +#define DDIO_MOUSE_LITE 2 // some ddio systems support the 'lite' extension of mice. + +typedef struct ddio_init_info { + oeApplication *obj; // App object used to initialize to IO system + bool use_lo_res_time; + bool key_emulation; // keyboard emulation + bool joy_emulation; // joystick emulation +} ddio_init_info; + + +// ---------------------------------------------------------------------------- +// Keyboard Initialization and destruction functions +// ---------------------------------------------------------------------------- + +// Initializes DDIO system +bool ddio_Init(ddio_init_info *init_info); +void ddio_Close(); + +// suspends ddio input system +void ddio_Suspend(); +void ddio_Resume(); + +// performs io per frame. +void ddio_Frame(); + +// initializes keyboard +bool ddio_KeyInit(ddio_init_info *init_info); + +// closes keyboard +void ddio_KeyClose(); + +// Handles keyboard queue management(TAKES ADJUSTED KEY VALUES) +void ddio_AddKeyToQueue(int key); + +// Updates timing info for keys +void ddio_UpdateKeyState(int key, bool isdown); + +// given a key code defined below, returns time in seconds since last call. +float ddio_KeyDownTime(int key); + +// return number of times a key's been down since last call. +int ddio_KeyDownCount(int key); + +// returns the state of an ADJUSTED KEY VALUE +bool ddio_GetAdjKeyState(int adj_key); + +// flush keyboard queue +void ddio_KeyFlush(); + +// flushes an individual key's timing values. +void ddio_KeyFlushKey(int key); + +// gets ADJUSTED KEY from queue +int ddio_KeyInKey(); + +// determine if there are ADJUSTED keys in keyqueue +int ddio_KeyPeek(); + +// handle input of keyboard per frame. +bool ddio_KeyFrame(); + +// converts keycode to ASCII +int ddio_KeyToAscii(int code); + +// converts ascii code to key +int ddio_AsciiToKey(int ascii); + + +// sets type of keyboard to emulate +#define KBLANG_AMERICAN 0 +#define KBLANG_BRITISH 1 +#define KBLANG_FRENCH 2 +#define KBLANG_GERMAN 3 + +void ddio_SetKeyboardLanguage(int language); + + +// ---------------------------------------------------------------------------- +// SERIAL CONSTANTS AND FUNCTIONS +// ---------------------------------------------------------------------------- +// serial port data type +typedef void *tSerialPort; + +// takes port number 1-4, returns a port object +tSerialPort ddio_SerialOpenPort(int port_number, int baud); + +// takes port structure and frees it. +void ddio_SerialClosePort(tSerialPort port); + +// writes one byte. true return value means it worked. +bool ddio_SerialWriteByte(tSerialPort port, ubyte b); + +// ---------------------------------------------------------------------------- +// KEYBOARD CONSTANTS +// ---------------------------------------------------------------------------- + +// array of key states. +extern volatile short DDIO_key_down_count[]; +extern volatile ubyte DDIO_key_state[]; + +#define KEY_STATE(_c) DDIO_key_state[_c] +#define DDIO_MAX_KEYS 256 + +// masks used to specify current keycode state matched with scancode +#define KEY_SHIFTED 0x100 +#define KEY_ALTED 0x200 +#define KEY_CTRLED 0x400 +#define KEY_DEBUGGED 0x800 + + +// used when defining a system code to use in KeyUpdateState +// masks +#define KEY_STATE_DOWN 0x80000000 +#define KEY_STATE_SYSTEM 0x40000000 + +// codes +#define KEY_0 0x0B +#define KEY_1 0x02 +#define KEY_2 0x03 +#define KEY_3 0x04 +#define KEY_4 0x05 +#define KEY_5 0x06 +#define KEY_6 0x07 +#define KEY_7 0x08 +#define KEY_8 0x09 +#define KEY_9 0x0A +#define KEY_A 0x1E +#define KEY_B 0x30 +#define KEY_C 0x2E +#define KEY_D 0x20 +#define KEY_E 0x12 +#define KEY_F 0x21 +#define KEY_G 0x22 +#define KEY_H 0x23 +#define KEY_I 0x17 +#define KEY_J 0x24 +#define KEY_K 0x25 +#define KEY_L 0x26 +#define KEY_M 0x32 +#define KEY_N 0x31 +#define KEY_O 0x18 +#define KEY_P 0x19 +#define KEY_Q 0x10 +#define KEY_R 0x13 +#define KEY_S 0x1F +#define KEY_T 0x14 +#define KEY_U 0x16 +#define KEY_V 0x2F +#define KEY_W 0x11 +#define KEY_X 0x2D +#define KEY_Y 0x15 +#define KEY_Z 0x2C +#define KEY_MINUS 0x0C +#define KEY_EQUAL 0x0D +#define KEY_SLASH 0x35 //was KEY_DIVIDE in Descent & D2 +#define KEY_BACKSLASH 0x2B //was KEY_SLASH in Descent and D2 +#define KEY_BSLASH_UK 0x56 //UK keyboards have diffent backslash scan code +#define KEY_COMMA 0x33 +#define KEY_PERIOD 0x34 +#define KEY_SEMICOL 0x27 +#define KEY_LBRACKET 0x1A +#define KEY_RBRACKET 0x1B +#define KEY_RAPOSTRO 0x28 +#define KEY_LAPOSTRO 0x29 +#define KEY_ESC 0x01 +#define KEY_ENTER 0x1C +#define KEY_BACKSP 0x0E +#define KEY_TAB 0x0F +#define KEY_SPACEBAR 0x39 +#define KEY_NUMLOCK 0xC5 +#define KEY_SCROLLOCK 0x46 +#define KEY_CAPSLOCK 0x3A +#define KEY_LSHIFT 0x2A +#define KEY_RSHIFT 0x36 +#define KEY_LALT 0x38 +#define KEY_RALT 0xB8 +#define KEY_LCTRL 0x1D +#define KEY_RCTRL 0x9D + +#define KEY_F1 0x3B +#define KEY_F2 0x3C +#define KEY_F3 0x3D +#define KEY_F4 0x3E +#define KEY_F5 0x3F +#define KEY_F6 0x40 +#define KEY_F7 0x41 +#define KEY_F8 0x42 +#define KEY_F9 0x43 +#define KEY_F10 0x44 +#define KEY_F11 0x57 +#define KEY_F12 0x58 + +#define KEY_PAD0 0x52 +#define KEY_PAD1 0x4F +#define KEY_PAD2 0x50 +#define KEY_PAD3 0x51 +#define KEY_PAD4 0x4B +#define KEY_PAD5 0x4C +#define KEY_PAD6 0x4D +#define KEY_PAD7 0x47 +#define KEY_PAD8 0x48 +#define KEY_PAD9 0x49 +#define KEY_PADMINUS 0x4A +#define KEY_PADPLUS 0x4E +#define KEY_PADPERIOD 0x53 +#define KEY_PADDIVIDE 0xB5 +#define KEY_PADMULTIPLY 0x37 +#define KEY_PADENTER 0x9C + +#define KEY_INSERT 0xD2 +#define KEY_HOME 0xC7 +#define KEY_PAGEUP 0xC9 +#define KEY_DELETE 0xD3 +#define KEY_END 0xCF +#define KEY_PAGEDOWN 0xD1 +#define KEY_UP 0xC8 +#define KEY_DOWN 0xD0 +#define KEY_LEFT 0xCB +#define KEY_RIGHT 0xCD +#define KEY_PRINT_SCREEN 0xB7 +#define KEY_PAUSE 0x45 + +#define KEY_WORLD0 0x54 +#define KEY_WORLD1 0x55 +#define KEY_WORLD2 0x56 +#define KEY_WORLD3 0x57 +#define KEY_WORLD4 0x58 +#define KEY_WORLD5 0x59 +#define KEY_WORLD6 0x5A +#define KEY_WORLD7 0x5B +#define KEY_WORLD8 0x5C +#define KEY_WORLD9 0x5D +#define KEY_WORLD10 0x5E +#define KEY_WORLD11 0x5F +#define KEY_WORLD12 0x60 +#define KEY_WORLD13 0x61 +#define KEY_WORLD14 0x62 +#define KEY_WORLD15 0x63 +#define KEY_WORLD16 0x64 +#define KEY_WORLD17 0x65 +#define KEY_WORLD18 0x66 +#define KEY_WORLD19 0x67 +#define KEY_WORLD20 0x68 +#define KEY_WORLD21 0x69 +#define KEY_WORLD22 0x6A +#define KEY_WORLD23 0x6B +#define KEY_WORLD24 0x6C +#define KEY_WORLD25 0x6D +#define KEY_WORLD26 0x6E +#define KEY_WORLD27 0x6F +#define KEY_WORLD28 0x70 +#define KEY_WORLD29 0x71 +#define KEY_WORLD30 0x72 +#define KEY_WORLD31 0x73 +#define KEY_WORLD32 0x74 +#define KEY_WORLD33 0x75 +#define KEY_WORLD34 0x76 +#define KEY_WORLD35 0x77 +#define KEY_WORLD36 0x78 +#define KEY_WORLD37 0x79 +#define KEY_WORLD38 0x7A +#define KEY_WORLD39 0x7B +#define KEY_WORLD40 0x7C +#define KEY_WORLD41 0x7D +#define KEY_WORLD42 0x7E +#define KEY_WORLD43 0x7F +#define KEY_WORLD44 0x80 +#define KEY_WORLD45 0x81 +#define KEY_WORLD46 0x82 +#define KEY_WORLD47 0x83 +#define KEY_WORLD48 0x84 +#define KEY_WORLD49 0x85 +#define KEY_WORLD50 0x86 +#define KEY_WORLD51 0x87 +#define KEY_WORLD52 0x88 +#define KEY_WORLD53 0x89 +#define KEY_WORLD54 0x8A +#define KEY_WORLD55 0x8B +#define KEY_WORLD56 0x8C +#define KEY_WORLD57 0x8D +#define KEY_WORLD58 0x8E +#define KEY_WORLD59 0x8F +#define KEY_WORLD60 0x90 +#define KEY_WORLD61 0x91 +#define KEY_WORLD62 0x92 +#define KEY_WORLD63 0x93 +#define KEY_WORLD64 0x94 +#define KEY_WORLD65 0x95 +#define KEY_WORLD66 0x96 +#define KEY_WORLD67 0x97 +#define KEY_WORLD68 0x98 +#define KEY_WORLD69 0x99 +#define KEY_WORLD70 0x9A +#define KEY_WORLD71 0x9B +// --- rcg07312000 ...skip... +#define KEY_WORLD72 0xE1 +#define KEY_WORLD73 0xE2 +#define KEY_WORLD74 0xE3 +#define KEY_WORLD75 0xE4 +#define KEY_WORLD76 0xE5 +#define KEY_WORLD77 0xE6 +#define KEY_WORLD78 0xE7 +#define KEY_WORLD79 0xE8 +#define KEY_WORLD80 0xE9 +#define KEY_WORLD81 0xE0 +#define KEY_WORLD82 0xEA +#define KEY_WORLD83 0xEB +#define KEY_WORLD84 0xEC +#define KEY_WORLD85 0xED +#define KEY_WORLD86 0xEE +#define KEY_WORLD87 0xEF +#define KEY_WORLD88 0xF0 +#define KEY_WORLD89 0xF1 +#define KEY_WORLD90 0xF2 +#define KEY_WORLD91 0xF3 +#define KEY_WORLD92 0xF4 +#define KEY_WORLD93 0xF5 +#define KEY_WORLD94 0xF6 +#define KEY_WORLD95 0xF7 + +#define KEY_CMD 0xE0 //DAJ mac command key + +#endif + + + diff --git a/lib/ddsndgeometry.h b/lib/ddsndgeometry.h new file mode 100644 index 000000000..d83545f5c --- /dev/null +++ b/lib/ddsndgeometry.h @@ -0,0 +1,91 @@ +/* + * $Source: $ + * $Revision: 5 $ + * $Author: Samir $ + * $Date: 8/13/99 2:00p $ + * + * Hardware occlusion and reflection sound support. + * + * $Log: /DescentIII/Main/Lib/ddsndgeometry.h $ + * + * 5 8/13/99 2:00p Samir + * more aureal and geometry fixes. + * + * 4 8/11/99 3:12p Samir + * fixes for aureal support. + * + * 3 4/06/99 8:30p Samir + * added reflection support. + * + * 2 3/29/99 10:52a Samir + * occlusion support almost complete. + * + */ + +#ifndef DDSNDGEOMETRY_H +#define DDSNDGEOMETRY_H + +#include "vecmat.h" + +// constants +typedef int tSoundMaterial; // value returned by geometry system. + +// a list of predefined sound materials. +const int SNDGEO_MATERIAL_COUNT = 8; +const tSoundMaterial SNDGEO_MATERIAL_NONE = -1, + SNDGEO_MATERIAL_ROCK = 0, + SNDGEO_MATERIAL_WATER = 1, + SNDGEO_MATERIAL_METAL = 2; + + +////////////////////////////////////////////////////////////////////////////// + +class llsSystem; + +class llsGeometry +{ +public: + llsGeometry(); + + bool Init(llsSystem *snd_sys); // specify a sound library to associate geometry with + void Shutdown(); // closes low level geometry system. + + void StartFrame(); + void EndFrame(); + +// polygon lists + void IsGroupValid(int group); // is a group cached?, check before rendering it. + void StartPolygonGroup(int group); // marks beginning of a list of polygons to render, (-1 group for non cache) + void EndPolygonGroup(int group); // ends a list of polygons to render. + void RenderGroup(int group); // renders a group. + +// clears out geometry info + void Clear(); + +// primatives, nv = number of verts, and verts is an array of pointers to vertices. +// you can pass a sound material value if you want special reflective properties on this polygon. + void AddPoly(int nv,vector **verts, unsigned tag, tSoundMaterial material=SNDGEO_MATERIAL_NONE); + +private: + void AddQuad(unsigned tag, vector **verts); // 4 verts here. + void AddTriangle(unsigned tag, vector **verts); // 3 verts here. + +private: + llsSystem *m_snd_system; // sound library attached. + int n_primatives_used; // number of primatives rendered this frame. + int n_reflections_used; + int n_materials_used; + char m_snd_mixer; // current sound mixer attached. + bool m_in_group; // a group of polys is being defined. + bool m_lib_init; + +private: + void CreateMaterial(tSoundMaterial material, float transmit_gain, float transmit_highfreq, float reflect_gain, float reflect_highfreq); + void DestroyMaterial(tSoundMaterial material); + +private: // sound material list. + void *m_snd_materials[SNDGEO_MATERIAL_COUNT]; +}; + + +#endif \ No newline at end of file diff --git a/lib/ddvid.h b/lib/ddvid.h new file mode 100644 index 000000000..8528c3593 --- /dev/null +++ b/lib/ddvid.h @@ -0,0 +1,71 @@ +/* + * $Logfile: /DescentIII/Main/lib/ddvid.h $ + * $Revision: 4 $ + * $Date: 12/29/97 5:50p $ + * $Author: Samir $ + * + * Video library. + * + * $Log: /DescentIII/Main/lib/ddvid.h $ + * + * 4 12/29/97 5:50p Samir + * Added ability to close ddvid system. + * + * 3 12/22/97 7:13p Samir + * Moved constants to grdefs.h + * + * 2 12/22/97 6:58p Samir + * Restored type ddgr_color + * + * 1 12/22/97 12:45p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef DDVID_H +#define DDVID_H + +#include "pstypes.h" +#include "grdefs.h" + +class oeApplication; + +// called first to allow fullscreen video access +bool ddvid_Init(oeApplication *app, char *driver); +void ddvid_Close(); + +// sets the appropriate video mode. +bool ddvid_SetVideoMode(int w, int h, int color_depth, bool paged); + +// sets screen handle +void ddvid_SetVideoHandle(unsigned handle); + +// retrieves screen information +void ddvid_GetVideoProperties(int *w, int *h, int *color_depth); + +// retrieves screen aspect ratio. +float ddvid_GetAspectRatio(); + +// retreives frame buffer info for a video mode. +void ddvid_LockFrameBuffer(ubyte **data, int *pitch); +void ddvid_UnlockFrameBuffer(); + +// flips screen if there's a back buffer +void ddvid_VideoFlip(); + + +// only available to DD_ACCESS libraries. +#if defined(DD_ACCESS_RING) +#if defined(WIN32) + +// dd_obj is the DIRECTDRAW OBJECT for the system. +// dds_obj is the DIRECTDRAWSURFACE OBJECT for the screen +void ddvid_GetVideoDDrawProps(uint *dd_obj, uint *dds_obj); + +#endif // WIN32 + +#endif // DD_ACCESS + + +#endif diff --git a/lib/debug.h b/lib/debug.h new file mode 100644 index 000000000..4a44f912a --- /dev/null +++ b/lib/debug.h @@ -0,0 +1,192 @@ +/* + * $Logfile: /DescentIII/Main/lib/debug.h $ + * $Revision: 23 $ + * $Date: 4/19/00 5:20p $ + * $Author: Matt $ + * + * Debug functions + * + * $Log: /DescentIII/Main/lib/debug.h $ + * + * 23 4/19/00 5:20p Matt + * From Duane for 1.4 + * Mac debug_break changes + * + * 22 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 21 7/28/99 3:25p Kevin + * Macintosh! + * + * 20 4/17/99 7:50p Jeff + * kill keyboard thread before int3 in linux (else it locks up)...temp + * hack until I replace ddio key + * + * 19 4/12/99 7:14p Samir + * added multiple pages per mono window. + * + * 18 1/13/99 6:47a Jeff + * fixed debug_break() for linux + * + * 17 1/09/99 1:11a Jeff + * put in #ifdef around some windows specific code + * + * 16 1/05/99 4:28p Kevin + * Moved exception handling code to windebug.cpp + * + * 15 10/18/98 8:52p Matt + * Revamped debug/error system. + * + * 14 10/13/98 12:03p Kevin + * Changed use of preprocessors for debug, etc. + * + * 13 10/12/98 10:20a Samir + * added parameter to debug init. + * + * 12 6/23/98 2:40p Matt + * Added Yes/No/Cancel type to OutrageMessageBox() and + * Debug_MesssageBox(), and changed return value from a bool to an + * integer. + * + * 11 5/12/98 11:45a Samir + * added logfile. + * + * 10 4/08/98 7:19p Samir + * Added runtime debugging option. + * + * 9 4/07/98 9:20p Samir + * Changes to debug stuff. + * + * 8 4/03/98 5:15p Samir + * Implemented simple debug message filtering. + * + * 7 3/20/98 2:43p Samir + * Some better Int3 support. + * + * 6 3/10/98 5:16p Samir + * Got debug callbacks working when you hit an Int3. + * + * 5 1/29/98 12:24p Samir + * Added logfile support. + * + * 4 1/28/98 11:17a Samir + * Added debugbreak stuff. + * + * 3 10/13/97 2:41p Samir + * Debug breaks now are macros to work depending on the operating system. + * + * 2 9/04/97 12:00p Matt + * Changed Debug_MessageBox() to return bool instead of int, since it was + * already being used that way. + * + * 1 6/10/97 4:54p Samir + * Took headers from gameos.h and isolated them. + * + * $NoKeywords: $ + */ +#ifndef DEBUG_H +#define DEBUG_H +#include "pstypes.h" +// --------------------------------------------------------------------------- +// Debug system is a member of the 'platform' library. +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- +#ifndef IDOK +#define IDOK 1 +#endif +#ifndef IDCANCEL +#define IDCANCEL 2 +#endif +#ifndef IDABORT +#define IDABORT 3 +#endif +#ifndef IDRETRY +#define IDRETRY 4 +#endif +#ifndef IDIGNORE +#define IDIGNORE 5 +#endif +#ifndef IDYES +#define IDYES 6 +#endif +#ifndef IDNO +#define IDNO 7 +#endif +//#define DEBUG_LEVEL 0 //DAJ +#if defined(WIN32) || defined(__LINUX__) +static const int OSMBOX_OK = 1; +static const int OSMBOX_YESNO = 2; +static const int OSMBOX_YESNOCANCEL = 3; +static const int OSMBOX_ABORTRETRYIGNORE = 4; +static const int OSMBOX_OKCANCEL = 5; +#else +#define OSMBOX_OK 1 +#define OSMBOX_YESNO 2 +#define OSMBOX_YESNOCANCEL 3 +#define OSMBOX_ABORTRETRYIGNORE 4 +#define OSMBOX_OKCANCEL 5 +#endif +// --------------------------------------------------------------------------- +// Functions +// --------------------------------------------------------------------------- +extern bool Debug_break; +// if we are running under a debugger, then pass true +bool Debug_Init(bool debugger, bool mono_debug); +//Does a messagebox with a stack dump +//Messagebox shows topstring, then stack dump, then bottomstring +//Return types are the same as the Windows return values +int Debug_ErrorBox(int type,const char *topstring, const char *title,const char *bottomstring); +// displays an message box +// Returns the same values as the Win32 MessageBox() function +int Debug_MessageBox(int type, const char *title, const char *str); +// these functions deal with debug spew support +bool Debug_Logfile(const char *filename); +void Debug_LogWrite(const char *str); +bool Debug_ConsoleInit(); +void Debug_ConsoleOpen(int n, int row, int col, int width, int height, char * title ); +void Debug_ConsoleClose(int n); +void Debug_ConsolePrintf( int n, char * format, ... ); +void Debug_ConsolePrintf( int n, int row, int col, char * format, ... ); +void Debug_ConsoleRedirectMessages(int virtual_window, int physical_window); +// DEBUGGING MACROS +//Break into the debugger, if this feature was enabled in Debug_init() +#if !defined(RELEASE) +#if defined(WIN32) + #define debug_break() \ + do { \ + if (Debug_break) \ + __asm int 3 \ + } while (0) +#elif defined(__LINUX__) + void ddio_InternalKeyClose(); + //#define debug_break() do{__asm__ __volatile__ ( "int $3" );}while(0) +#ifndef MACOSXPPC + #define debug_break() do{ ddio_InternalKeyClose(); __asm__ __volatile__ ( "int $3" ); }while(0) +#else + #define debug_break() do{ ddio_InternalKeyClose(); /*nop*/ }while(0) +#endif +#elif defined(MACINTOSH) + extern void SuspendControls(); + extern void ResumeControls(); + #define debug_break() \ + do { \ + if (Debug_break) \ + SuspendControls(); \ + Debugger(); \ + ResumeControls(); \ + } while (0) +#else + #define debug_break() +#endif +#else + #define debug_break() +#endif +#if defined (WIN32) +// We forward declare PEXCEPTION_POINTERS so that the function +// prototype doesn't needlessly require windows.h. +typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; +int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS data, const char *Message); +#endif +#endif diff --git a/lib/dedicated_server.h b/lib/dedicated_server.h new file mode 100644 index 000000000..f6bdce233 --- /dev/null +++ b/lib/dedicated_server.h @@ -0,0 +1,71 @@ +#ifndef DEDICATED_SERVER_H +#define DEDICATED_SERVER_H + +#define CVAR_GAMEINIT 0x0001 //this variable can be set/changed during server init +#define CVAR_GAMEPLAY 0x0002 //this variable can be set/changed during game play + +typedef enum +{ + CVAR_TYPE_INT, + CVAR_TYPE_FLOAT, + CVAR_TYPE_STRING, + CVAR_TYPE_NONE, +} cvar_type; + +typedef struct +{ + char *varname; + cvar_type type; + void *dest_variable; + int var_min,var_max; + short permissions; +} cvar_entry; + +extern bool Dedicated_server; + +// Sets the value for a cvar INT type +void SetCVarInt (int index,int val); + +// Sets the value for a cvar FLOAT type +void SetCVarFloat (int index,float val); + +// Sets the value for a cvar string type +void SetCVarString (int index,char *val); + +// The accessor function that sets the value of a cvar +void SetCVar (char *cvar_string,char *cvar_argument,bool in_game_init); + +// The accessor function that sets the value of a cvar...INT only +void SetCVar (char *cvar_string,int cvar_argument); + +// The accessor function that sets the value of a cvar...FLOAT only +void SetCVar (char *cvar_string,float cvar_argument); + + +// Starts a dedicated server and loads the server config file +void StartDedicatedServer (); + +// Reads in the server config file for a dedicated server +// Returns true if everything is ok +int LoadServerConfigFile (); + +// Called once per frame for the dedicated server +void DoDedicatedServerFrame (); + +// Prints a message to the console if the dedicated server is active +void PrintDedicatedMessage(const char *fmt, ...); + + +//Reads incoming data from the telnet connection to the server +void DedicatedReadTelnet(void); + +//Sends a string to all connected and logged in clients +void DedicatedSocketputs(char *str); + +//Look for incoming connections +void ListenDedicatedSocket (void); + +//Init the socket and start listening +void InitDedicatedSocket(ushort port); + +#endif \ No newline at end of file diff --git a/lib/demofile.h b/lib/demofile.h new file mode 100644 index 000000000..7df2ed763 --- /dev/null +++ b/lib/demofile.h @@ -0,0 +1,236 @@ +/* + * $Logfile: /DescentIII/main/lib/demofile.h $ + * $Revision: 25 $ + * $Date: 7/21/99 12:03p $ + * $Author: Kevin $ + * + * + * + * $Log: /DescentIII/main/lib/demofile.h $ + * + * 25 7/21/99 12:03p Kevin + * Changed version numbers around + * + * 24 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 23 3/12/99 7:53p Jeff + * save player rotating balls info, handle obj_observer with obj_player, + * handle objects that use life left, handle 2d sound playing + * + * 22 3/11/99 11:40a Kevin + * New stuff for multi + * + * 21 3/10/99 2:25p Kevin + * Save/Load and Demo file fixes + * + * 20 2/25/99 11:01a Matt + * Added new explosion system. + * + * 19 2/23/99 12:45a Jeff + * added support for in-game-cinematics in demo system + * + * 18 2/22/99 9:21p Kevin + * Added DT_CINEMATICS + * + * 17 2/08/99 7:05p Kevin + * Trying to get demo system working with powerup scripts + * + * 16 1/23/99 10:12p Kevin + * Added the start of osiris support into the demo system + * + * 15 11/24/98 3:57p Kevin + * Demo system immprovements + * + * 14 11/24/98 12:08p Kevin + * + * 13 11/24/98 10:41a Kevin + * Demo system + * + * 12 11/23/98 4:52p Kevin + * Demo system enhancments + * + * 11 11/23/98 3:11p Kevin + * Demo system + * + * 10 11/19/98 5:40p Kevin + * Demo system + * + * 9 11/17/98 4:17p Kevin + * Demo recording system + * + * 8 11/11/98 2:46p Kevin + * Demo recording system work + * + * 7 11/09/98 4:12p Kevin + * + * 6 11/05/98 5:54p Kevin + * Demo system work + * + * 5 10/08/98 12:00p Kevin + * Demo system work + * + * 4 10/06/98 5:46p Kevin + * Added new configuration for demo + * + * 3 10/05/98 12:09p Kevin + * Converted projects to VC6 and demo file stuff added + * + * 2 10/05/98 10:32a Kevin + * Initial Version + * + * 1 10/05/98 10:22a Kevin + * + * $NoKeywords: $ + */ + +#ifndef _DEMO_FILE_HEADER_ +#define _DEMO_FILE_HEADER_ + +extern char Demo_fname[_MAX_PATH*2]; + +extern unsigned int Demo_flags; +extern bool Demo_paused; +extern bool Demo_do_one_frame; +extern bool Demo_restart; +extern bool Demo_auto_play; +extern float Demo_frame_ofs; +#define DF_NONE 0 +#define DF_RECORDING 1 +#define DF_PLAYBACK 2 + +#define D3_DEMO_SIG "D3DEM" +#define D3_DEMO_SIG_NEW "D3DM1" + +#define DT_OBJ 1 //Object data +#define DT_NEW_FRAME 2 //Start of a new frame +#define DT_WEAPON_FIRE 3 //Someone fired a weapon +#define DT_HUD_MESSAGE 4 //Display a hud message +#define DT_3D_SOUND 5 //Play a 3d sound (associated with a weapon firing usually) +#define DT_OBJ_CREATE 6 //A new object was created... +#define DT_OBJ_ANIM 7 //Object animation has changed +#define DT_OBJ_TURRET 8 //Object's turrets have changed +#define DT_OBJ_EXPLODE 9 //Explode object +#define DT_PLAYER_DEATH 10 //Player died +#define DT_COLLIDE_PLR 11 //Player collided with a weapon +#define DT_COLLIDE_GEN 12 //generic collided with a weapon +#define DT_ATTACH 13 //Attach some objects +#define DT_ATTACH_RAD 14 //Attach some objects with a radius? +#define DT_UNATTACH 15 //Unattach some stuff +#define DT_WEAP_FIRE_FLAG 16 //flags like spraying and on/off +#define DT_PLAYER_INFO 17 //Player 1's info like energy/sheilds, etc. +#define DT_MSAFE 18 //MSAFE data (ie script stuff) +#define DT_POWERUP 19 //Powerups data +#define DT_CINEMATICS 20 //Cinematic info +#define DT_PERSISTANT_HUD 21 //Persistant hud message +#define DT_SETOBJDEAD 22 //Mark an object as dead +#define DT_PLAYERBALLS 23 //Rotating balls around player ship +#define DT_PLAYERTYPECHNG 24 //Player type is changing +#define DT_SETOBJLIFELEFT 25 //Object is getting OF_LIFELEFT flag changed +#define DT_2D_SOUND 26 //Play a 2d sound + +//If not recording prompts user for filename and starts recording if successfull +//If recording, close the demo file +void DemoToggleRecording(); + +void DemoWriteChangedObj(object *op); + +void DemoWriteHeader(); + +void DemoStartNewFrame(); + +void DemoWriteHudMessage(unsigned int color,bool blink,char *msg); + +void DemoWriteChangedObjects(); + +void DemoWriteWeaponFire(unsigned short objectnum,vector *pos,vector *dir,unsigned short weaponnum,unsigned short weapobjnum,short gunnum); + +void DemoWriteObjCreate(ubyte type, ushort id, int roomnum, vector *pos, const matrix *orient, int parent_handle,object * obj); + +void DemoWriteTurretChanged(unsigned short objnum); + +int DemoReadHeader(); + +int DemoPlaybackFile(char *filename); + +bool LoadDemoDialog(); + +void DemoFrame(); + +void DemoAbort(bool deletefile=false); + +void DemoWriteKillObject(object *hit_obj,object *killer,float damage,int death_flags,float delay,int seed); + +void DemoWritePlayerDeath(object *player,bool melee,int fate=-1); + +void DemoWrite3DSound(short soundidx,ushort objnum,int priority,float volume= 0.5f); + +void DemoWriteCollidePlayerWeapon(object * playerobj, object * weapon, vector *collision_point, vector *collision_normal, bool f_reverse_normal, void *hit_info ); + +void DemoWriteCollideGenericWeapon( object * robotobj, object * weapon, vector *collision_point, vector *collision_normal, bool f_reverse_normal, void *hit_info ); + +void DemoReadCollidePlayerWeapon(void); + +void DemoReadCollideGenericWeapon(void); + +void DemoReadNewFrame(void); + +void DemoWriteObjAnimChanged(unsigned short objnum); + +void DemoWriteAttachObjRad(object *parent, char parent_ap, object *child, float rad); + +void DemoReadAttachObjRad(void); + +void DemoWriteAttachObj(object *parent, char parent_ap, object *child, char child_ap, bool f_aligned); + +void DemoReadAttachObj(void); + +void DemoWriteUnattachObj(object *child); + +void DemoReadUnattachObj(void); + +void DemoPostPlaybackMenu(void); + +void DemoReadObjWeapFireFlagChanged(void); + +void DemoWriteObjWeapFireFlagChanged(short objnum); + +void DemoWritePlayerInfo(void); + +void DemoReadPlayerInfo(void); + +void DemoPlayAutoDemo(void); + +void DemoWriteMSafe(ubyte *data,unsigned short len); + +void DemoReadMSafe(); + +void DemoWritePowerup(ubyte *data,unsigned short len); + +void DemoReadPowerups(); + +void DemoReadCinematics(); + +void DemoWriteCinematics(ubyte *data,unsigned short len); + +void DemoWritePersistantHUDMessage(ddgr_color color,int x, int y, float time, int flags, int sound_index,char *msg); + +void DemoReadPersistantHUDMessage(); + +void DemoWriteSetObjDead(object *obj); +void DemoReadSetObjDead(); + +void DemoWritePlayerBalls(int pnum); +void DemoReadPlayerBalls(void); + +void DemoWritePlayerTypeChange(int slot,bool stop_observing=false,int observer_mode=-1,int piggy_objnum=-1); +void DemoReadPlayerTypeChange(void); + +void DemoWriteObjLifeLeft(object *obj); +void DemoReadObjLifeLeft(void); + +void DemoWrite2DSound(short soundidx,float volume= 0.5f); +void DemoRead2DSound(void); + +#endif \ No newline at end of file diff --git a/lib/directplay.h b/lib/directplay.h new file mode 100644 index 000000000..1e85444be --- /dev/null +++ b/lib/directplay.h @@ -0,0 +1,104 @@ +/* + * $Logfile: /DescentIII/Main/lib/directplay.h $ + * $Revision: 5 $ + * $Date: 9/02/98 6:54p $ + * $Author: Kevin $ + * + * Directplay API header + * + * $Log: /DescentIII/Main/lib/directplay.h $ + * + * 5 9/02/98 6:54p Kevin + * Fixed general directplay support up, and got modem-modem working + * + * 4 8/24/98 10:55a Kevin + * new directplay stuff + * + * 3 8/14/98 4:54p Kevin + * More directplay stuff + * + * 2 8/13/98 6:32p Kevin + * Initial implementation of directplay API + * + * 1 8/13/98 6:25p Kevin + * +*/ +#ifndef _OUTRAGE_DIRECTPLAY_HEADER +#define _OUTRAGE_DIRECTPLAY_HEADER + + +#include "dplay.h" +#include "dplobby.h" + +typedef struct _modem_list +{ + char name[200]; +}modem_list; + + +#define MAX_MODEMS 15 +#define MAX_DP_GAMES 32 +#define MAX_DIRECTPLAY_CONNECTIONS 10 +#define MAX_PENDING_NEW_CONNECTIONS 10 + +extern bool Use_DirectPlay; +extern bool Directplay_lobby_launched_game; + +extern DPSESSIONDESC2 Directplay_sessions[MAX_DP_GAMES]; +extern int Num_directplay_games; + +extern unsigned long Pending_dp_conn[MAX_PENDING_NEW_CONNECTIONS]; + +// This is called when a game is started, so Directplay will be happy +int dp_StartGame(char *gamename); + +// This is called when the game ends, to tell directplay the game is over +void dp_EndGame(); + +// This is called to request a list of ongoig direct play games +int dp_ListDirectPlayGames(); + +// Send a packet to a direct play user +int dp_DirectPlaySend(network_address *who_to,ubyte *data,int len,bool reliable); + +// This function will look for incoming messages, and dispatch them accordingly +void dp_DirectPlayDispatch(); + +// Initialize stuff +int dp_InitDirectPlay(char *conn_name, void *parms = NULL,int num_elements = 0); + +// Shutdown things +void dp_ShutdownDirectPlay(); + +// Destroy a player object +void dp_DirectPlayDestroyPlayer(DPID who); + +// Join a network game +int dp_DirectPlayJoinGame(LPDPSESSIONDESC2 session); + +//Call this function with size set to 0, and it will fill in size with +//the amount of buffer space you need +//Otherwise, it will fill in the buffer with a bunch of null delimited +//strings, with a double null at the end. +int dp_GetModemChoices(char *buffer,unsigned long *size); + + +// Register a DirectPlay lobby aware application +// Use this so a directplay lobby provider such as zone.com can launch the game +// +// Parameters: +// appname The non-localized name of the application (ie. "Descent 3") DON'T LOCALIZE THIS! IT IS AN ID +// exefile Executable file name (without path) +// exepath Path to executable file +// arguments Any command line arguments the app needs +// workingdir The Working directory for the application +// description Localized description of the application +void dp_RegisterLobbyApplication(char *appname,char *exefile,char *exepath,char *arguments,char *workingdir,char *description); + +// Returns TRUE if the game was launched from a lobby +bool dp_DidLobbyLaunchGame(); + +// Autoconnects to a game or starts one, based on the directplay lobby stuff +bool dp_AutoConnect(); + +#endif \ No newline at end of file diff --git a/lib/ds3dlib.h b/lib/ds3dlib.h new file mode 100644 index 000000000..a5dc3fe0c --- /dev/null +++ b/lib/ds3dlib.h @@ -0,0 +1,436 @@ +/* + * $Logfile: /DescentIII/Main/lib/ds3dlib.h $ + * $Revision: 49 $ + * $Date: 8/23/99 5:29p $ + * $Author: Samir $ + * + * Low-level library for 3d sound + * + * $Log: /DescentIII/Main/lib/ds3dlib.h $ + * + * 49 8/23/99 5:29p Samir + * incremental EAX 2.0 checkin + * + * 48 5/03/99 3:12a Samir + * fixed up aureal so it works (a little slow though...) + * + * 47 4/29/99 3:01p Samir + * added code for direct sound mixers only (and function for Aureal + * really) that will use direct sound looping for simple loops. + * + * 46 4/27/99 2:10p Samir + * added code to set the desired sound card given the descriptive name of + * a sound card. + * + * 45 4/23/99 7:51p Samir + * looping fixes for directsound. + * + * 44 4/22/99 10:33p Samir + * modifications so that DirectSound mixers use one thread for all looping + * and streaming sounds. It worked without crashing for about twenty + * minutes of playing from level 1 to level 2 of D3. We'll see. + * + * 43 4/13/99 4:09p Samir + * more priority stuff. + * + * 42 4/12/99 7:14p Samir + * prioritization code added. + * + * 41 4/10/99 5:08p Samir + * took out obsolete data from play_information structure that should save + * around 70 bytes per instance. + * + * 40 4/09/99 12:03p Samir + * took out windows.h again, this time made HWND a void pointer, and + * checked under both editor and main projects. + * + * 39 4/07/99 12:38a 3dsmax + * Misc changes to allow editor build, as per Samir's phone instructions. + * Will this break the game build? Who knows? (Matt, ChrisP) + * + * 38 4/06/99 8:29p Samir + * added error check system. + * + * 37 3/29/99 11:01a Samir + * added ability to change 3d environment. + * + * 36 3/17/99 4:20p Samir + * added functions to pause and resume individual sounds. + * + * 35 2/19/99 5:21p Kevin + * Fixed some connection DLLs and a Direct Sound bug with threads. + * + * 34 1/14/99 6:10p Samir + * added DirectSound buffer duplication code. + * + * 33 1/11/99 5:51p Samir + * reverb on the buffer level. + * + * 32 1/08/99 6:31p Samir + * added reverb + * + * 31 1/08/99 11:36a Samir + * implemented basic Aureal 2.0 support. + * + * 30 12/23/98 11:49a Samir + * reorganized code so it works with different APIs. + * + * 29 7/08/98 12:09p Samir + * use C RTL for thread creation. + * + * 28 7/06/98 1:48p Samir + * added A3d support. + * + * 27 6/29/98 4:15p Chris + * Streaming audio works + * + * 26 6/29/98 9:29a Chris + * Added some support for Direct Sound 3d + * + * 25 6/25/98 11:28a Chris + * Added some DS3d support + * + * 24 6/24/98 6:43p Chris + * Furthered the endless work on the sound system + * + * 23 6/24/98 4:39p Chris + * Improved the multiple mixer support + * + * 22 6/24/98 11:31a Chris + * Fixed an ordering bug + * + * 21 6/24/98 11:14a Chris + * Added more support and bug fixes + * + * 20 6/24/98 1:07a Chris + * Intermediate checkin + * + * 19 6/23/98 6:00p Chris + * DirectSound 16 mixer is active with 8 bit samples + * + * 18 6/22/98 12:00p Chris + * Working on sound system to make it in a nice shape. + * + * 17 6/19/98 3:09p Chris + * Improved IsSoundPlaying + * + * 16 6/19/98 3:03p Chris + * Made CheckAndForceSoundDataAlloc a SoundSystem function - useful for + * multiple mixers. Added IsSoundPlaying to the high level sound lib. + * + * 15 6/16/98 3:48p Chris + * Updated the sound system and added the start of sound streaming + * + * 14 2/26/98 5:11p Chris + * Removed a unused data element from the llsoundlib + * + * 13 2/26/98 3:08p Chris + * More NT stuff + * + * 12 2/25/98 3:19p Jeff + * more NT fixes + * + * 11 2/25/98 2:47p Jeff + * CHRIS: Added some NT support for sound code. It is pretty hokey right + * now. + * + * 10 2/25/98 12:13p Chris + * NT support? Not sure... I do not have the Editor in NT. + * + * 9 2/15/98 7:12p Chris + * More improvements to the sound lib + * + * 8 1/23/98 4:21p Chris + * Changed the low-level sound code from multimedia timers to a thread + * that sleeps(); + * + * 7 1/02/98 5:32p Chris + * More radical changes to the sound system + * + * 6 12/31/97 2:58a Chris + * Another major revision to the SoundLib. This cleaned up the code after + * removing the direct sound secondary buffers. + * + * 5 12/22/97 6:19p Chris + * Working on new mixer functionality + * + * 4 12/19/97 4:08p Chris + * New dynamically loading sounds + * + * 3 12/10/97 4:47p Chris + * Revision 1.0 of new sound library (no hardware -- uses primary buffer + * streaming) + * + * 2 11/21/97 1:10p Chris + * Incremental Improvements + * + * 18 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 17 5/22/97 5:15p Chris + * + * 16 4/29/97 7:26a Chris + * Improved the sound code. It is healthy. + * + * 15 4/28/97 11:21a Chris + * Incremental improvements is the sound system + * + * 14 4/24/97 3:25a Chris + * // Added some more support for 3d sounds + * + * 13 4/03/97 8:37p Chris + * Added the Main_OS object pass in the sound library + * initialization code. + * + * 12 4/01/97 10:00a Chris + * + * 11 3/17/97 5:12p Chris + * Allowed new sound code to be demo'ed. + * + * 10 3/14/97 12:10p Chris + * Made a nice C++ abstract type for the low-level. It should make the + * MacMan very joyful. :) + * + * 9 2/07/97 5:48p Matt + * Renamed vector.h to vecmat.h to fix DevStudio problem + * + * $NoKeywords: $ + */ + +#ifndef __ds3dlib_h__ +#define __ds3dlib_h__ + +//#include +//#include // Multi-media system support +//#include "dsound.h" // Direct sound header file + +#include "ssl_lib.h" // Shared sound header (between high and low-levels) +#include "vecmat.h" + +#ifndef DS3DLIB_INTERNAL_H +typedef void *LPDIRECTSOUND; +typedef void *LPDIRECTSOUNDBUFFER; +typedef void *LPDIRECTSOUND3DLISTENER; +typedef void *LPDIRECTSOUND3DBUFFER; +typedef void *LPDIRECTSOUNDCAPTURE; +typedef void *LPDIRECTSOUNDCAPTUREBUFFER; +typedef void *LPDIRECTSOUNDNOTIFY; +//typedef void *HWND; +class sound_buffer_info; +struct tPSBInfo; +#endif + +// Sound cache list +class sound_buffer_cache +{ +public: + class sound_buffer_info * m_sound_cache; // List of all sounds current sounds + unsigned short m_max_sounds_played; // Maximum sounds played at any given moment + unsigned short m_cur_sounds_played; // Current number of sounds playing + unsigned m_loop_method; // what method do we do looping sounds (direct sound only) + +public: + sound_buffer_info *FindSoundBuffer(int sound_index); + +}; + +class emulated_listener +{ +public: + matrix orient; + vector position; + vector velocity; +}; + +// Sound library object + +// Commonly passed arguments: +// sound_index -- index of a particular sample in the SoundArray +// sound_uid -- unique id of a particular sound being played +// sound_slot -- "channel" that a sample is being played on (derivable from sound_uid) + +class win_llsSystem : public llsSystem +{ +private: + // Windows and Direct Sound information + LPDIRECTSOUND m_lp_ds; // pointer to direct sound object + void *m_hwnd_main; // Handle to main window -- needed for direct sound + + // For DS3D + LPDIRECTSOUND3DLISTENER m_lp_listener; + + // For 3d sounds and non-3D mixers + emulated_listener m_emulated_listener; + + // For DS and DS3d mixers + void LoopStartStreaming(sound_buffer_info *sb, int buffer_type, float volume, float pan, pos_state *cur_pos); + void DSStartStreaming(sound_buffer_info *sb, float volume, float pan); + + friend void __cdecl LoopStreamTimer(void *user_ptr); + + // Creates a sound buffer + long CreateDSBuffer(int buffer_type, LPDIRECTSOUNDBUFFER *lp_lp_dsb, LPDIRECTSOUND3DBUFFER *lp_lp_dsb_3d, ulong sound_bytes, ulong frequency, bool f_is_stereo, bool f_is_16_bit); + + // Finds a free slot for a new sound, slot_uid is an SBID_xxx value. +#ifdef _DEBUG + short FindFreeSoundSlot(int sound_index, float volume, int priority); +#else + short FindFreeSoundSlot(float volume, int priority); +#endif + + // updates a directsound buffer + void update_directsound_sb(sound_buffer_info *sb, bool update_looping = false); + + // used to clean up direct sound buffer stuff + void cleanup_directsound_looping_sb(sound_buffer_info *sb); + + // Makes a unique sound id from a sound slot number + inline int MakeUniqueId(int sound_slot); + // Validates and converts a unique sound id to a sound slot number + inline int ValidateUniqueId(int sound_uid); + + friend void StreamMixer(char *ptr, int len); + friend void StreamFill(int start_byte, int num_bytes); + friend void __cdecl StreamTimer(void *user_ptr); + + bool StartDSCleaner(void); + friend void __cdecl DSCleanerThread(void *user_ptr); + + bool StartStreaming(void); + +private: + // Total samples played since start of library + unsigned short m_total_sounds_played; // Used for unique ids and for stats + + // Sound library status + unsigned char m_f_sound_lib_init; // Flag is set if sound library is initialized -- cmphack + unsigned char m_f_sound_lib_status; // Paused or running -- cmphack + + // May not need some of these + int m_primary_frequency; // Set to the primary buffers frequency -- cmphack + int m_primary_alignment; + int m_current_frequency; // -- cmphack + + unsigned char m_primary_bit_depth; // 16 or 8 bits per sample -- cmphack + char m_sound_quality; + + // muted looped sound system + longlong m_timer_last_frametime; + float m_cache_stress_timer; + +// clean interface +#ifdef DS3DLIB_INTERNAL_H // following data is available to internal functions +public: +#else +private: +#endif + bool CreateSoundBuffer(sound_buffer_info *sb, bool f_is_stereo, int size=-1, bool dynamic = false); + bool LoadSoundBuffer(sound_buffer_info *sb); + bool PlaySoundBuffer(sound_buffer_info *sb, tPSBInfo *psb); + bool DuplicateSoundBuffer(sound_buffer_info *sb); + + bool m_in_sound_frame; + bool m_pending_actions; // are there sound events pending (outside of of start/end) + char m_mixer_type; + + // Public functions +public: + // Initializes the object, but does not activate the sound library + win_llsSystem(void); + // Destructor -- calls the cleanup code. + ~win_llsSystem(void); + + // Starts the sound library + virtual int InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played); + + // Cleans up after the Sound Library + virtual void DestroySoundLib(void); + +public: // only for win32 instances + int SetSoundPos(int sound_uid, int pos); + int GetSoundPos(int sound_uid); + +public: + // reserve sound slots., returns an ID to this slot. + + virtual void SetSoundCard(const char *name); + + virtual void SetListener(pos_state *cur_pos); + virtual int PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float master_volume, bool f_looped, float reverb=0.5f); //, unsigned short frequency) + virtual void AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency); + virtual void AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb=0.5f); + + // Sample cache and list of samples + class sound_buffer_cache m_sound_mixer; // Lists all stored samples (playing, cached, or unused) + + // Plays a 2d sound + virtual int PlaySound2d(play_information *play_info, int sound_index, float volume, float pan, bool f_looped = false); + virtual int PlayStream(play_information *play_info); + virtual void StopAllSounds(void); + + virtual bool SetSoundQuality(char quality); + virtual char GetSoundQuality(void); + + virtual bool SetSoundMixer(char mixer_type); + virtual char GetSoundMixer(void); + + // Checks if a sound is playing + virtual bool IsSoundInstancePlaying(int sound_uid); + virtual int IsSoundPlaying(int sound_index); + + // Locks and unlocks sounds (used when changing play_info data) + virtual bool LockSound(int sound_uid); + virtual bool UnlockSound(int sound_uid); + + // Stops 2d and 3d sounds + virtual void StopSound(int sound_uid, unsigned char f_immediately = SKT_STOP_IMMEDIATELY); + + // Pause all sounds/resume all sounds + virtual void PauseSounds(void); + virtual void ResumeSounds(void); + virtual void PauseSound(int sound_uid); + virtual void ResumeSound(int sound_uid); + + // Loads the actual sound data into memory + virtual bool CheckAndForceSoundDataAlloc(int sound_file_index); + + // Begin sound frame + virtual void SoundStartFrame(void); + + // End sound frame + virtual void SoundEndFrame(void); + + // environmental sound interface + // volume modifier (0-1), damping(0-1), 1 = complete, 0 = none + // decay 0.1 to 100 seconds, how long it takes for a sound to die. + virtual bool SetGlobalReverbProperties(float volume, float damping, float decay); + + // set special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentValues(const t3dEnvironmentValues *env); + + // get special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentValues(t3dEnvironmentValues *env); + + // enable special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentToggles(const t3dEnvironmentToggles *env); + + // get states of special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentToggles(t3dEnvironmentToggles *env); + +protected: + virtual void CheckForErrors(); // part of sound system error handler. + +///////////////////////////////////////////////////////////////////////////////// +public: + // set auxillary 3d sound properties + virtual bool SoundPropertySupport() const; + + // sound obstruction from 0 to 1.0 (1.0 = fully obstructed) + virtual void SetSoundProperties(int sound_uid, float obstruction); +}; + +#endif \ No newline at end of file diff --git a/lib/findintersection.h b/lib/findintersection.h new file mode 100644 index 000000000..a57adc4d0 --- /dev/null +++ b/lib/findintersection.h @@ -0,0 +1,438 @@ +/* +* $Logfile: /DescentIII/Main/lib/findintersection.h $ +* $Revision: 51 $ +* $Date: 6/10/99 6:32p $ +* $Author: Chris $ +* +* Find intersection header +* +* $Log: /DescentIII/Main/lib/findintersection.h $ + * + * 51 6/10/99 6:32p Chris + * New Editor Support + * + * 50 5/18/99 11:10a Matt + * Added variable ceiling height. + * + * 49 4/20/99 8:14p Chris + * Added support for object's that hit the ceiling and for making the + * level always check for the ceiling (inside and outside the mine) + * + * 48 1/15/99 5:59p Chris + * + * 47 1/13/99 2:29a Chris + * Massive AI, OSIRIS update + * + * 46 1/05/99 12:24p Chris + * More OSIRIS improvements + * + * 45 1/01/99 4:10p Chris + * Added some const parameters, improved ray cast object collide/rejection + * code + * + * 44 11/23/98 11:07a Chris + * Added another parameter to fvi_QuickDistObjectList + * + * 43 10/29/98 5:20p Chris + * Player ships collide smaller now + * + * 42 10/22/98 10:25p Kevin + * took out volatile + * + * 41 10/21/98 9:41p Chris + * Improved walking code! + * + * 40 10/17/98 12:25p Chris + * Fixed attached flares + * + * 39 10/16/98 8:33p Chris + * Fixed attached flare problem + * + * 38 10/16/98 3:39p Chris + * Improved the object linking system and AI and physics + * + * 37 10/16/98 1:07p Chris + * Lowered the max hits to 2 for now (we don't even use the info anyhow) + * + * 36 8/03/98 3:59p Chris + * Added support for FQ_IGNORE_WEAPONS, added .000001 attach code, fix a + * bug in polymodel collision detection + * + * 35 7/23/98 12:46p Chris + * Added a flag to ignore external rooms + * + * 34 7/02/98 2:51p Chris + * Added a fast line to bbox function + * + * 33 6/15/98 7:01a Chris + * Cleaned out DII stuff and added new PhysicsSim extern's + * + * 32 6/03/98 6:42p Chris + * Added multipoint collision detection an Assert on invalid (infinite + * endpoint). + * + * 31 5/22/98 4:44p Chris + * Added better melee hit prediction + * + * 30 5/05/98 5:23p Chris + * Faster external room collisions with FQ_EXTERNAL_ROOMS_AS_SPHERE + * + * 29 3/23/98 11:37a Chris + * Added the f_lightmap_only parameter to fvi_QuickDistObjectList + * + * 28 3/23/98 11:18a Chris + * Added int fvi_QuickDistObjectList(vector *pos, int init_room_index, + * float rad, short *object_index_list, int max_elements) + * + * + * 27 3/17/98 11:33a Chris + * Improved performance (AABB updates on poly-sphere collide) + * + * 26 3/16/98 12:50p Chris + * Speed up? + * + * 25 3/16/98 9:43a Chris + * Added FQ_NO_RELINK + * + * 24 2/19/98 6:17p Chris + * Added some debug info + * + * 23 2/11/98 10:04p Chris + * Added FQ_ONLY_DOOR_OBJ and fixed a bug with FQ_ONLY_PLAYER_OBJ + * + * 22 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 21 1/30/98 4:34p Jason + * FROM CHRIS: Changed COL_CELL size to 1 + * + * 20 1/20/98 11:35a Chris + * Raised ceiling to MAX_TERRAIN_HEIGHT + * + * 19 1/19/98 5:05p Chris + * Added ceiling collisions for players and powerups + * + * 18 12/16/97 6:11p Chris + * Attempt to improve FVI for outside + * + * 17 10/28/97 5:46p Chris + * Added a quick cell list function + * + * 16 10/28/97 12:20p Chris + * Added support to ignore wall collisions + * + * 15 10/22/97 12:41p Chris + * Incremental BBOX stuff and added support for subobject hit returned + * from fvi + * + * 14 10/07/97 4:03p Chris + * Added support for object backface checking + * + * 13 10/06/97 10:52a Jason + * added "FQ_IGNORE_NON_LIGHTMAP_OBJECTS" flag + * + * 12 9/19/97 6:58p Chris + * Fixed some bugs with the big object system and removed some annoying + * mprintf's + * + * 11 9/19/97 1:01p Chris + * Added better large object support + * + * 10 9/17/97 11:00a Chris + * Removed dependance on segment.h + * + * 9 9/15/97 6:24p Chris + * Added a second chance check if no rooms are initialy found + * + * 8 9/15/97 5:20a Chris + * Added sphere2poly support. Rearranged object collisions. + * + * 7 9/12/97 6:36p Chris + * Added collision terrain segment support + * Added some LARGE OBJECT collision support + * + * 6 9/11/97 3:08p Chris + * Added support for weapons flying through holes in transparent textures. + * + * 5 9/11/97 12:43p Chris + * Added new support for face_physics_info from room.h. Also, changed how + * we detect portals. Also added recorded faces. + * + * 4 9/02/97 11:41a Chris + * Added support the the quick face/room from distance function + * + * 3 8/18/97 1:53a Chris + * Added fvi_Relink + * + * 2 8/04/97 5:35p Chris + * Added support for back face collisions and new fvi code + * + * 10 6/11/97 10:43a Chris + * + * 9 6/10/97 5:34p Chris + * + * 8 5/13/97 5:52p Chris + * Added ability to exit and enter mine. Also did some + * incremental improvements. + * + * 7 5/05/97 5:41a Chris + * Added some better polygon collision code. It is still rough though. + * + * 6 4/17/97 3:10p Chris + * Added edge of world object delete. Also did some incremental + * improvements. + * + * 5 4/15/97 4:15p Chris + * + * 4 3/24/97 12:18p Chris + * + * 3 3/03/97 1:02p Chris + * + * 2 3/03/97 5:52a Chris + * Pre-alpha 0.01 -- just a starting point that compiles + * + * 1 3/03/97 3:26a Chris +* +* $NoKeywords: $ +*/ + +#ifndef _FVI_H +#define _FVI_H + +#include "object.h" +#include "vecmat.h" +#include "terrain.h" +//#include "room.h" +#include "findintersection_external.h" + +extern float Ceiling_height; + +#define CEILING_HEIGHT (Ceiling_height) + +#define PLAYER_SIZE_SCALAR 0.8f + +extern bool FVI_always_check_ceiling; + +// Big Object Info +#define CELLS_PER_COL_CELL 1 +#define COL_TERRAIN_SIZE (TERRAIN_SIZE*(float)CELLS_PER_COL_CELL) + +#define MIN_BIG_OBJ_RAD (COL_TERRAIN_SIZE) + +// chrishack -- we could turn backface, on and off so that we +// can individually use backface checking on object and/or wall... :) + +#define MAX_FVI_SEGS 100 + +#define MAX_HITS 2 + +#define Q_RIGHT 0 +#define Q_LEFT 1 +#define Q_MIDDLE 2 + +inline bool FastVectorBBox(const float *min, const float *max, const float *origin, const float *dir) +{ + bool f_inside = true; + char quad[3]; + register int i; + float max_t[3]; + float can_plane[3]; + int which_plane; + float coord[3]; + + for(i = 0; i < 3; i++) + { + if(origin[i] < min[i]) + { + quad[i] = Q_LEFT; + can_plane[i] = min[i]; + f_inside = false; + } + else if (origin[i] > max[i]) + { + quad[i] = Q_RIGHT; + can_plane[i] = max[i]; + f_inside = false; + } + else + { + quad[i] = Q_MIDDLE; + } + } + + if(f_inside) + { + return true; + } + + for(i = 0; i < 3; i++) + { + if(quad[i] != Q_MIDDLE && dir[i] != 0.0f) + max_t[i] = (can_plane[i] - origin[i])/dir[i]; + else + max_t[i] = -1.0f; + } + + which_plane = 0; + + for(i = 0; i < 3; i++) + if(max_t[which_plane] < max_t[i]) + which_plane = i; + + if(max_t[which_plane] < 0.0f) + return false; + + for(i = 0; i < 3; i++) + { + if (which_plane != i) + { + coord[i] = origin[i] + max_t[which_plane] * dir[i]; + + if((quad[i] == Q_RIGHT && coord[i] < min[i]) || + (quad[i] == Q_LEFT && coord[i] > max[i])) + { + return false; + } + } + else + { + coord[i] = can_plane[i]; + } + } + + return true; +} + +//this data structure gets filled in by find_vector_intersection() +typedef struct fvi_info +{ + vector hit_pnt; // centerpoint when we hit + int hit_room; // what room hit_pnt is in + float hit_dist; // distance of the hit + + int num_hits; // Number of recorded hits + + int hit_type[MAX_HITS]; //what sort of intersection + vector hit_face_pnt[MAX_HITS]; // actual collision point (edge of rad) + + int hit_face_room[MAX_HITS]; // what room the hit face is in + int hit_face[MAX_HITS]; // if hit wall, which face + vector hit_wallnorm[MAX_HITS]; // if hit wall, ptr to its surface normal + + int hit_object[MAX_HITS]; // if object hit, which object + int hit_subobject[MAX_HITS]; // if a POLY_2_SPHERE hit, then it has the poly involved + + int n_rooms; // how many segs we went through + int roomlist[MAX_FVI_SEGS]; // list of segs vector went through + + // BBox hit results + matrix hit_orient; + vector hit_rotvel; + angle hit_turnroll; + vector hit_velocity; + float hit_time; + + vector hit_subobj_fvec; + vector hit_subobj_uvec; + vector hit_subobj_pos; +} fvi_info; + +//this data contains the parms to fvi() +typedef struct fvi_query +{ + vector *p0,*p1; + int startroom; + float rad; + short thisobjnum; + int *ignore_obj_list; + int flags; + + // BBox stuff... + matrix *o_orient; + vector *o_rotvel; + vector *o_rotthrust; + vector *o_velocity; + angle *o_turnroll; + vector *o_thrust; + float frametime; +} fvi_query; + +//Find out if a vector intersects with anything. +//Fills in hit_data, an fvi_info structure (see above). +//Parms: +// p0 & startseg describe the start of the vector +// p1 the end of the vector +// rad the radius of the cylinder +// thisobjnum used to prevent an object with colliding with itself +// ingore_obj_list NULL, or ptr to a list of objnums to ignore, terminated with -1 +// check_obj_flag determines whether collisions with objects are checked +//Returns the hit_data->hit_type +extern int fvi_FindIntersection(fvi_query *fq,fvi_info *hit_data, bool no_subdivision = false); + +// Face/Room list for some fvi call(s) +typedef struct fvi_face_room_list +{ + ushort face_index; + ushort room_index; +} fvi_face_room_list; + +#define MAX_RECORDED_FACES 200 + +// Recorded face list +extern fvi_face_room_list Fvi_recorded_faces[MAX_RECORDED_FACES]; +extern int Fvi_num_recorded_faces; + +// Generates a list of faces(with corresponding room numbers) within a given distance to a position. +// Return value is the number of faces in the list +extern int fvi_QuickDistFaceList(int init_room_index, vector *pos, float rad, fvi_face_room_list *quick_fr_list, int max_elements); +// Returns the number of cells that are approximately within the specified radius +extern int fvi_QuickDistCellList(int init_cell_index, vector *pos, float rad, int *quick_cell_list, int max_elements); + +// Returns the number of objects that are approximately within the specified radius +int fvi_QuickDistObjectList(vector *pos, int init_roomnum, float rad, short *object_index_list, int max_elements, bool f_lightmap_only, bool f_only_players_and_ais = false, bool f_include_non_collide_objects = false, bool f_stop_at_closed_doors = false); + +//finds the uv coords of the given point on the given seg & side +//fills in u & v. if l is non-NULL fills it in also +//extern void fvi_FindHitpointUV(float *u,float *v,float *l, vector *pnt,segment *seg,int sidenum,int facenum); + +//Returns true if the object is through any walls +extern int fvi_ObjectIntersectsWall(object *objp); + +extern int FVI_counter; +extern int FVI_room_counter; + +bool fvi_QuickRoomCheck(vector *pos, room *cur_room, bool try_again = false); + +extern fvi_info * fvi_hit_data_ptr; +extern fvi_query * fvi_query_ptr; +extern float fvi_collision_dist; +extern int fvi_curobj; +extern int fvi_moveobj; + +bool PolyCollideObject(object *obj); + +bool BBoxPlaneIntersection(bool fast_exit, vector *collision_point, vector *collision_normal, object *obj, vector *new_pos, int nv, vector **vertex_ptr_list, vector *face_normal, matrix *orient); + +extern int check_vector_to_sphere_1(vector *intp, float *col_dist, const vector *p0, const vector *p1,vector *sphere_pos,float sphere_rad, bool f_correcting, bool f_init_collisions); + +extern int check_line_to_face(vector *newp, vector *colp, float *col_dist, vector *wall_norm,const vector *p0,const vector *p1, vector *face_normal, vector **vertex_ptr_list, const int nv,const float rad); +extern void InitFVI(void); + +// Types of supported collisions +#ifdef NED_PHYSICS +#define RESULT_NOTHING 0 +#define RESULT_CHECK_SPHERE_SPHERE 1 +#define RESULT_CHECK_SPHERE_POLY 2 +#define RESULT_CHECK_POLY_SPHERE 3 +#define RESULT_CHECK_BBOX_POLY 4 +#define RESULT_CHECK_POLY_BBOX 5 +#define RESULT_CHECK_BBOX_BBOX 6 +#define RESULT_CHECK_BBOX_SPHERE 7 +#define RESULT_CHECK_SPHERE_BBOX 8 +#define RESULT_CHECK_SPHERE_ROOM 9 +#define RESULT_CHECK_BBOX_ROOM 10 +#endif + +#endif + diff --git a/lib/findintersection_external.h b/lib/findintersection_external.h new file mode 100644 index 000000000..a6bb51778 --- /dev/null +++ b/lib/findintersection_external.h @@ -0,0 +1,69 @@ +/* +* $Logfile: /DescentIII/main/lib/findintersection_external.h $ +* $Revision: 6 $ +* $Date: 4/18/99 5:42a $ +* $Author: Chris $ +* +* Description goes here +* +* $Log: /DescentIII/main/lib/findintersection_external.h $ + * + * 6 4/18/99 5:42a Chris + * Added the FQ_IGNORE_RENDER_THROUGH_PORTALS flag + * + * 5 4/05/99 3:58p Chris + * Made the FQ_ flags easier to read + * + * 4 3/27/99 3:25p Chris + * Added comment headers +* +* $NoKeywords: $ +*/ + +#ifndef FINDINTERSECTION_EXTERNAL_H_ +#define FINDINTERSECTION_EXTERNAL_H_ + +//return values for find_vector_intersection() +#define HIT_NONE 0 //we hit nothing +#define HIT_WALL 1 //we hit a wall +#define HIT_OBJECT 2 //we hit an object +#define HIT_TERRAIN 3 //we hit the terrain +#define HIT_BAD_P0 4 //start point not is specified segment +#define HIT_OUT_OF_TERRAIN_BOUNDS 5 // End point is outside of the terrain +#define HIT_BACKFACE 6 // We hit the backface of a wall... +#define HIT_SPHERE_2_POLY_OBJECT 7 // Hit a sphere to a real polygon +#define HIT_CEILING 8 // Object hit the ceiling +#define HIT_CORNER_WALL 9 +#define HIT_EDGE_WALL 10 +#define HIT_FACE_WALL 11 + +//flags for fvi query +#define FQ_CHECK_OBJS 1 // check against objects? +#define FQ_OBJ_BACKFACE (1<<1) // Hit the backfaces of polyobjs +#define FQ_TRANSPOINT (1<<2) // go through trans wall if hit point is transparent +#define FQ_IGNORE_POWERUPS (1<<3) // ignore powerups +#define FQ_BACKFACE (1<<4) // Check for collisions with backfaces, usually they are ignored +#define FQ_SOLID_PORTALS (1<<5) // Makes connectivity disappear for FVI +#define FQ_RECORD (1<<6) // Records faces that should be recorded +#define FQ_NEW_RECORD_LIST (1<<7) // Records faces that should be recorded +#define FQ_IGNORE_MOVING_OBJECTS (1<<8) // Ignores all objects that move +#define FQ_IGNORE_NON_LIGHTMAP_OBJECTS (1<<9) // Ignores all objects that are not associated with lightmaps +#define FQ_ONLY_PLAYER_OBJ (1<<10) // Ignores all objects besides the player +#define FQ_IGNORE_WALLS (1<<11) // Ignores all walls (it will still hit OBJ_ROOMS) +#define FQ_CHECK_CEILING (1<<12) // Checks if object hits the imaginary ceiling +#define FQ_ONLY_DOOR_OBJ (1<<13) // Ignores all objects, except doors +#define FQ_NO_RELINK (1<<14) // Does not determine the hitseg +#define FQ_EXTERNAL_ROOMS_AS_SPHERE (1<<15) // Hmmm.... +#define FQ_MULTI_POINT (1<<16) // Enable Multi-point collision +#define FQ_LIGHTING (1<<17) // Lighting only optimizations +#define FQ_COMPUTE_MOVEMENT_TIME (1<<18) +#define FQ_IGNORE_EXTERNAL_ROOMS (1<<19) +#define FQ_IGNORE_WEAPONS (1<<20) +#define FQ_IGNORE_TERRAIN (1<<21) +#define FQ_PLAYERS_AS_SPHERE (1<<22) +#define FQ_ROBOTS_AS_SPHERE (1<<23) +#define FQ_IGNORE_CLUTTER_COLLISIONS (1<<24) +#define FQ_IGNORE_RENDER_THROUGH_PORTALS (1<<25) + + +#endif \ No newline at end of file diff --git a/lib/fix.h b/lib/fix.h new file mode 100644 index 000000000..6e4f9ccaa --- /dev/null +++ b/lib/fix.h @@ -0,0 +1,120 @@ +/* + * $Logfile: /DescentIII/Main/Lib/fix.h $ + * $Revision: 4 $ + * $Date: 10/21/99 9:27p $ + * $Author: Jeff $ + * + * Header for fixed-point math functions + * + * $Log: /DescentIII/Main/Lib/fix.h $ + * + * 4 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 3 9/09/97 3:53p Chris + * Added a #define PI to fix.h + * + * 2 8/22/97 12:34p Jason + * added FixCeil and FixFloor + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 7 2/27/97 4:57p Samir + * include fixwin32.h from win\fixwin32.h + * + * 6 2/27/97 4:52 PM Jeremy + * moved inline asm fixmul/div functions into separate header files for + * mac and pc. + * + * 5 2/12/97 5:27p Jason + * implemented asin,acos,atan2 + * + * 4 2/10/97 1:57p Jason + * changed FloatToFix back to what it originally was + * + * 3 2/07/97 5:44p Matt + * Moved fixed-point math funcs from vecmat to fix + * Changed trig functions to use small table like D2 + * + * $NoKeywords: $ + */ + +#ifndef _FIX_H +#define _FIX_H + +#include "math.h" + +// Disable the "possible loss of data" warning +#pragma warning (disable:4244) + +//Angles are unsigned shorts +typedef unsigned short angle; + +//The basic fixed-point type +typedef long fix; + +#define PI 3.141592654 +#define PIOVER2 1.570796327 //DAJ + +//Constants for converted between fix and float +#define FLOAT_SCALER 65536.0 +#define FIX_SHIFT 16 + +//1.0 in fixed-point +#define F1_0 (1 << FIX_SHIFT) + +//Generate the data for the trig tables. Must be called before trig functions +void InitMathTables (); + +//Returns the sine of the given angle. Linearly interpolates between two entries in a 256-entry table +float FixSin(angle a); + +//Returns the cosine of the given angle. Linearly interpolates between two entries in a 256-entry table +float FixCos(angle a); + +//Returns the sine of the given angle, but does no interpolation +float FixSinFast(angle a); + +//Returns the cosine of the given angle, but does no interpolation +float FixCosFast(angle a); + +#define Round(x) ((int) (x + 0.5)) + +fix FloatToFixFast(float num); + +//Conversion macros +//??#define FloatToFix(num) Round((num) * FLOAT_SCALER) +#define FloatToFix(num) ((fix)((num)*FLOAT_SCALER)) +#define IntToFix(num) ((num) << FIX_SHIFT) +#define ShortToFix(num) (((long) (num)) << FIX_SHIFT) +#define FixToFloat(num) (((float) (num)) / FLOAT_SCALER) +#define FixToInt(num) ((num) >> FIX_SHIFT) +#define FixToShort(num) ((short) ((num) >> FIX_SHIFT)) + +//Fixed-point math functions in inline ASM form +#if defined(MACINTOSH) + #include "fixmac.h" +#elif defined(WIN32) + #include "win\fixwin32.h" +#endif + +// use this instead of: +// for: (int)floor(x+0.5f) use FloatRound(x) +// (int)ceil(x-0.5f) use FloatRound(x) +// (int)floor(x-0.5f) use FloatRound(x-1.0f) +// (int)floor(x) use FloatRound(x-0.5f) +// for values in the range -2048 to 2048 +int FloatRound( float x ); + +angle FixAtan2(float cos,float sin); +angle FixAsin(float v); +angle FixAcos(float v); + +// Does a ceiling operation on a fixed number +fix FixCeil (fix num); + +// Floors a fixed number +fix FixFloor (fix num); + +#endif diff --git a/lib/forcefeedback.h b/lib/forcefeedback.h new file mode 100644 index 000000000..5706205ac --- /dev/null +++ b/lib/forcefeedback.h @@ -0,0 +1,446 @@ +/* +* $Logfile: /DescentIII/Main/lib/forcefeedback.h $ +* $Revision: 15 $ +* $Date: 7/28/99 3:23p $ +* $Author: Kevin $ +* +* Low-Level ForceFeedback header +* +* $Log: /DescentIII/Main/lib/forcefeedback.h $ + * + * 15 7/28/99 3:23p Kevin + * Macintosh + * + * 14 4/16/99 8:28p Jeff + * moved placement of #endif + * + * 13 4/15/99 1:44a Jeff + * changes for linux compile + * + * 12 1/30/99 11:27p Jeff + * added immersion support + * + * 11 1/28/99 12:09p Jeff + * added force feedback to player shake...fixed spelling error in define + * for forcefeedback + * + * 10 1/13/99 6:48a Jeff + * made file linux friendly (removed dinput specific stuff to ifdef) + * + * 8 11/10/98 5:16p Jeff + * updated forcefeedback system...pretty complete now + * + * 7 11/06/98 7:00p Jeff + * first round of new force feedback installed + * + * 6 11/03/98 6:43p Jeff + * new low-level & high level Force Feedback system implemented, handles + * window losing focus, etc. + * + * 5 10/12/98 3:49p Jeff + * struct changes + * + * 4 9/21/98 11:10a Jeff + * general update, new low level, small high level implementation + * + * 3 9/18/98 7:38p Jeff + * creation of low-level forcefeedback and beginning of high-level + * forcefeedback + * + * 2 9/15/98 12:05p Jeff + * initial creation of low-level forcefeedback +* +* $NoKeywords: $ +*/ +#ifndef __DDIO_FORCEFEEDBACK_H_ +#define __DDIO_FORCEFEEDBACK_H_ +#include "pstypes.h" +#include "string.h" +#define kMAX_Str 80 +#define kInfinite_Duration 0xFFFFFF +#define FF_USEENVELOPE 0x01 +#define HZ_to_uS(hz) ((int)(1000000.0/(double)(hz) + 0.5)) +#define DDIO_FF_MAXEFFECTS 30 +#if defined(WIN32) +//WINDOWS +#include "win/DirectX/dinput.h" +#define FF_DEGREES DI_DEGREES +#define FF_NOMINALMAX DI_FFNOMINALMAX +#define FF_SECONDS DI_SECONDS +#elif defined(__LINUX__) || defined(MACINTOSH) +//LINUX +#define FF_DEGREES 360 //fake value +#define FF_NOMINALMAX 10000 //fake value +#define FF_SECONDS 1000 //fake value +#endif +#define FORCEPROJECT void * +typedef enum{ + kJoy1=0, + kJoy2, + kJoy3, + kJoy4, + kJoy5, + kJoy6, + kJoy7, + kJoy8, + kJoy9, + kJoy10, + kJoy11, + kJoy12, + kJoy13, + kJoy14, + kJoy15, + kJoy16, + kMaxJoy, + kMouse, + kKeyBoard, + kAllDevices +}tDevice; +typedef enum{ + kNoButton=-1, + kButton0=0, + kButton1, + kButton2, + kButton3, + kButton4, + kButton5, + kButton6, + kButton7, + kButton8, + kButton9, + kButton10, + kButton31=31, + kButtonMax +}tJoyButtons; +typedef enum{ + kStick = 1<<0, + kWheel = 1<<1, + kGamePad= 1<<2, +}tDeviceMask; +typedef struct{ + int ButtonMask; + int AxisMask; + tDeviceMask DevType; + char Name[kMAX_Str]; +}tFFJoyInfo; +typedef enum{ + kDontPlayNow=0, + kPlayNow, + kPlayNowIfModified, +}tLoadEffect; +typedef enum { + kConstant=0, + kRamp, + kCustom, + kWave_Square, + kWave_Sine, + kWave_Triangle, + kWave_SawUp, + kWave_SawDown, + kCondition_Spring, + kCondition_Damper, + kCondition_Inertia, + kCondition_Friction, + kMaxEffectSubTypes +}tEffType; +typedef struct tEffectConstant{ + long Mag; // +- 10,000 +}tEffConstant; +typedef struct tEffectRamp{ + long Start; // +- 10,000 + long End; // +- 10,000 +}tEffRamp; +typedef struct tEffectWave{ + unsigned long Mag; // 0 to 10,000 + long Offset; // +- 10,000 + unsigned long Phase; // 0 to 35,999 + unsigned long Period; +}tEffWave; +typedef struct tEffectCondition{ + long Offset; // +- 10,000 + long PositiveCoefficient; // +- 10,000 + long NegativeCoefficient; // +- 10,000 + unsigned long PositiveSaturation; // 0 to 10,000 + unsigned long NegativeSaturation; // 0 to 10,000 + long DeadBand; // 0 to 10,000 +}tEffCondition; +typedef struct tEffectCustom{ + int Channels; + int Period; + int Samples; + long *ForceData; +}tEffCustom; +typedef union tEffectInfo{ + tEffConstant Constant; + tEffRamp Ramp; + tEffWave Wave; + tEffCondition Condition; + tEffCustom Custom; +}tEffInfo; +typedef struct tEffectEnvelope{ + unsigned long AttackLevel; + unsigned long AttackTime; + unsigned long FadeLevel; + unsigned long FadeTime; +}tEffEnvelope; +typedef enum{ + kXAxisOnly, + kYAxisOnly, + kBothAxes +}tEffAxis; +typedef struct tFFB_Effect{ + int Flags; + tEffType Type; + //tEffInfo TypeInfo[2]; + tEffInfo TypeInfo; + unsigned long Duration; + unsigned long Gain; // 0-10000 -- scales all magnitudes and envelope + unsigned long Period; + tEffAxis Axis; + tJoyButtons Trigger; + unsigned long TriggerRepeatTime; + long Direction; // 0 to 360 deg. + tEffEnvelope Envelope; +}tFFB_Effect; +extern bool ddForce_found; //a Force Feedback device was found +extern bool ddForce_enabled; //Force Feedback is ready and can be used +// =================================================================== +// Function Prototypes +// =================================================================== +// ------------------------------------------------------------------- +// ddio_ff_AttachForce +// Purpose: +// Attaches variables initialized in the general ddio system to +// the force feedback system. +// ------------------------------------------------------------------- +void ddio_ff_AttachForce(void); +// ------------------------------------------------------------------- +// ddio_ff_DetachForce +// Purpose: +// Detaches variables used by the force-feedback system from the +// ddio system +// ------------------------------------------------------------------- +void ddio_ff_DetachForce(void); +// ------------------------------------------------------------------- +// ddio_ff_Init +// Purpose: +// Initialize force feedback if available. +// ------------------------------------------------------------------- +int ddio_ff_Init(void); +// ------------------------------------------------------------------- +// ddio_ffjoy_Init +// Purpose: +// Creates and acquires all joysticks +// +// Input: +// None +// +// Return: +// # of sticks acquired +// +// Description: +// +// ------------------------------------------------------------------- +int ddio_ffjoy_Init(void); +// ------------------------------------------------------------------- +// ddio_ff_Acquire +// Purpose: +// Acquires a direct input device for use. +// +// Input: +// The device to acquire (use kDI_MaxJoy to acquire all available +// joysticks). +// +// Return: +// # of devices acquired. +// +// Description: +// Call this to gain access to a device after the device has been +// created & after regaining the focus after losing it. +// +// ------------------------------------------------------------------- +int ddio_ff_Acquire(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ff_Unacquire +// Purpose: +// Unacquires a direct input device +// +// Input: +// The device to unacquire (use kDI_MaxJoy to unacquire all available +// joysticks). +// +// Return: +// # of devices unacquired. +// +// Description: +// Call this to lose access to a device after the device has been +// aquired +// +// ------------------------------------------------------------------- +int ddio_ff_Unacquire(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ff_SetCoopLevel +// ------------------------------------------------------------------- +static int ddio_ff_SetCoopLevel(tDevice dev, int coop_level); +// ------------------------------------------------------------------- +// ddio_ffjoy_Query +// Purpose: +// Besides checking what buttons/axis are available, this function +// also checks for force feedback support. +// ------------------------------------------------------------------- +int ddio_ffjoy_Query(int dev, int* but_flags, int* axis_flags); +/* +======================================================================== + Force Feedback Effect Functions +======================================================================== +*/ +// ------------------------------------------------------------------- +// ddio_ff_GetInfo +// Purpose: +// Returns information about the current state of the low-level +// Force Feedback system. +// ------------------------------------------------------------------- +void ddio_ff_GetInfo(bool *ff_found,bool *ff_enabled); +// ------------------------------------------------------------------- +// ddio_ffb_Pause +// Purpose: +// Pause the FFB output on the given device. Use ddio_ffb_Continue to +// continue where you left off. +// ------------------------------------------------------------------- +void ddio_ffb_Pause(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ffb_Continue +// Purpose: +// Unpause the FFB output on the given device. Complimentary to +// ddio_ffb_Pause. +// ------------------------------------------------------------------- +void ddio_ffb_Continue(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ffb_Enable +// Purpose: +// Must be called after initialization in order to activate the +// device. +// Use ddio_ffb_Pause & ddio_ffb_Continue if you want disable forces +// temporarily and resume later. +// ------------------------------------------------------------------- +void ddio_ffb_Enable(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ffb_Disable +// Purpose: +// Turns off FFB, but effects still play on processor. +// ------------------------------------------------------------------- +void ddio_ffb_Disable(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ffb_effectCreate +// Purpose: +// Create a single effect for future playback. +// Effect is given a logical ID +// ------------------------------------------------------------------- +int ddio_ffb_effectCreate(tDevice dev, tFFB_Effect* eff); +// ------------------------------------------------------------------- +// ddio_ffb_DestroyAll +// Purpose: +// Destroys all created effects +// ------------------------------------------------------------------- +void ddio_ffb_DestroyAll(void); +// ------------------------------------------------------------------- +// ddio_ffb_effectPlay +// Purpose: +// Play an effect that was previously created. +// ------------------------------------------------------------------- +void ddio_ffb_effectPlay(short eID); +// ------------------------------------------------------------------- +// ddio_ffb_effectStop +// Purpose: +// Stop a single effect. +// ------------------------------------------------------------------- +void ddio_ffb_effectStop(short eID); +// ------------------------------------------------------------------- +// ddio_ffb_effectStopAll +// Purpose: +// Stops all forces on the given device. +// ------------------------------------------------------------------- +void ddio_ffb_effectStopAll(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ffb_effectUnload +// Purpose: +// Unload a single effect... Necessary to make room for other +// effects. +// ------------------------------------------------------------------- +void ddio_ffb_effectUnload(short eID); +// ------------------------------------------------------------------- +// ddio_ffb_effectModify +// Purpose: +// Modifies a single effect, only if the given parameters are +// different from what's currently loaded. +// ------------------------------------------------------------------- +void ddio_ffb_effectModify(short eID, int* Direction, unsigned int* Duration, unsigned int* Gain, unsigned int* Period, tEffInfo* TypeInfo, tEffEnvelope* Envelope); +// ------------------------------------------------------------------- +// ddio_ffb_GetEffectData +// Purpose: +// Retrieves affect data for the given parameters, pass NULL for those you don't want +// ------------------------------------------------------------------- +void ddio_ffb_GetEffectData(short eID, int* Direction, unsigned int* Duration, unsigned int* Gain, unsigned int* Period, tEffInfo* TypeInfo, tEffEnvelope* Envelope); +// ------------------------------------------------------------------- +// ddio_ffjoy_EnableAutoCenter +// Purpose: +// Disables/Enables the autocentering of the joystick +// ------------------------------------------------------------------- +void ddio_ffjoy_EnableAutoCenter(tDevice dev,bool enable); +// ------------------------------------------------------------------- +// ddio_ffjoy_SetGain +// Purpose: +// Sets the gain for joystick, pass a value of 0-1 +// ------------------------------------------------------------------- +void ddio_ffjoy_SetGain(tDevice dev,float value); +// ------------------------------------------------------------------- +// ddio_ffjoy_IsAutoCentered +// Purpose: +// Returns true if the joystick is set for autocentering +// ------------------------------------------------------------------- +bool ddio_ffjoy_IsAutoCentered(tDevice dev); +// ------------------------------------------------------------------- +// ddio_ffjoy_SupportAutoCenter +// Purpose: +// Returns true if the FF joystick supports auto centering +// ------------------------------------------------------------------- +bool ddio_ffjoy_SupportAutoCenter(tDevice dev); +/* +=========================================================================== + Private Functions +=========================================================================== +*/ +#if defined(WIN32) +// ------------------------------------------------------------------- +// ddio_ffjoy_AcquireErr +// Purpose: +// Handle success/err reporting +// ------------------------------------------------------------------- +static int ddio_ffjoy_AcquireErr(HRESULT res, int dev_num); +// ------------------------------------------------------------------- +// FFEnumCallback +// Purpose: +// Initialize all connected joysticks. +// +// Input: +// pdinst info about the current joystick being enumed. +// pvRef the direct input object that was passed in before +// starting the enum process. +// Return: +// DIENUM_CONTINUE continue calling us with any more devices +// DIENUM_STOP all done, don't call us back anymore, go away. +// ------------------------------------------------------------------- +BOOL CALLBACK FFEnumCallback(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef); +#endif +// Given a filename resource, this loads the file and creates a resource +// for it. It returns a handle to that resource. +// If it returns NULL, then it couldn't load the project. +// Make sure device is aquired before calling. +FORCEPROJECT ddio_ForceLoadProject(char *filename,tDevice dev); +// Unloads a FORCEPROJECT file +void ddio_ForceUnloadProject(FORCEPROJECT prj); +// Given a handle to a resource, and the name of the effect to load +// it will load that effect. Returns the effect ID, or -1 if it couldn't +// be created +int ddio_CreateForceFromProject(FORCEPROJECT project,char *forcename); +#endif \ No newline at end of file diff --git a/lib/game2dll.h b/lib/game2dll.h new file mode 100644 index 000000000..42991a56b --- /dev/null +++ b/lib/game2dll.h @@ -0,0 +1,98 @@ +#ifndef GAME2DLL_H +#define GAME2DLL_H + +#include "pstypes.h" +#include "multi.h" + +#include "d3events.h" +#include "vecmat.h" + +typedef struct +{ + bool is_banned; + int team; +} tPreJoinData; + +typedef struct +{ + vector point,normal; + float hitspeed, hit_dot; + int hitseg, hitwall; +}game_collide_info; + +typedef struct +{ + int me_handle; + int it_handle; + ubyte *special_data; + char *input_string; + int input_key; + union{ + int iRet; + float fRet; + }; + float fParam; + int iParam; + game_collide_info collide_info; + int newseg,oldseg; +} dllinfo; + +#define MAX_GAMENAME_LEN 32 +#define MAX_REQUIREMENT_LEN 384 +#define DOF_MAXTEAMS 0x0001 //max_teams member is valid +#define DOF_MINTEAMS 0x0002 +//this struct is used to return game specific information to Descent 3 +typedef struct +{ + //general flags, also specifies what members of the structure are valid + int flags; + + //0 or 1 for non-team games...maximum value is 4. If not specified, than it is assumed 0 + int max_teams; + //must be less then or equal to max_teams. If not specified, then it is assumed 0 for + //non-team games, 2 for team games. (max_teams will always tell you if it's a team game) + int min_teams; + + //gives the full name of the game (must be set) + char game_name[MAX_GAMENAME_LEN]; + + //this is an array of semicolor seperated 'string identifiers' that serve as requirements + //the mission needs to support in order for it to be playable with the game. + //this parameter must be set (even it is just a '\0' for the first character..aka no requirements) + char requirements[MAX_REQUIREMENT_LEN]; + +} tDLLOptions; + +extern dllinfo DLLInfo; + +// The chokepoint function to call the dll function +void CallGameDLL (int eventnum,dllinfo *data); + +// Frees the dll if its in memory +void FreeGameDLL (); + +// Loads the game dll. Returns 1 on success, else 0 on failure +int LoadGameDLL (char *name,int num_teams_to_use=-1); + +// If this function is called than the DLL is to be closed, because there was an error running it +// if reason is not NULL than that is the reason why +void DLLFatalError(char *reason=NULL); + +// Call this function right after a player connects to the game to see if a player is banned +bool GameDLLIsAddressBanned(network_address *addr,char *tracker_id); + +// Call this function to get information/options from a unloaded mod +bool GetDLLGameInfo(char *name,tDLLOptions *options); + +// Call this function to get information about the number of teams for the game +// Returns true if it's a team game...false if it's a non-team game. +// If it returns true, then min is filled in with the minumum number of teams needed for the game +// and max is filled in with the maximum number of teams for the game...if they are the same +// value, then it is the only number of teams supported. +bool GetDLLNumTeamInfo(char *name,int *mint,int *maxt); + +// Call this function to get the list of requirements that the given module needs in order +// to be playable. Returns the number of requirements it needs...-1 on error. +int GetDLLRequirements(char *name,char *requirements,int buflen); + +#endif diff --git a/lib/gameos.h b/lib/gameos.h new file mode 100644 index 000000000..c38cd0040 --- /dev/null +++ b/lib/gameos.h @@ -0,0 +1,154 @@ +/* + * $Logfile: /DescentIII/Main/lib/gameos.h $ + * $Revision: 3 $ + * $Date: 7/28/99 3:24p $ + * $Author: Kevin $ + * + * Operating system management library + * + * $Log: /DescentIII/Main/lib/gameos.h $ + * + * 3 7/28/99 3:24p Kevin + * Mac + * + * 2 5/10/99 10:53p Ardussi + * changes to compile on Mac + * + * 17 3/14/97 6:11 PM Jeremy + * added get_user_name to osDatabase + * + * 16 3/14/97 4:15p Matt + * Changed title of OutrageMessageBox() dialogs + * + * 15 3/13/97 11:04a Samir + * Took out file functions. Kept console because the OS must initialize + * the console before doing anything else to report messages. + * + * 14 3/10/97 11:13a Samir + * Changes made to reflect deletion of OSDebug class from system + * + * 13 3/04/97 1:00p Samir + * Fixed return type prototype errors. + * + * 12 3/04/97 12:54p Samir + * Added type constants for message boxes. + * + * 11 3/03/97 3:12p Samir + * Fixed GetWorkingDir prototype. + * + * 10 3/03/97 1:16p Samir + * Added some file operations + * + * 9 2/28/97 11:04a Samir + * Use a generic structure to pass in init info instead of params. + * + * 8 2/27/97 5:26 PM Jeremy + * removed mac\oemac_os.h and replaced it with #include "oemac_os.h" + * because the mac compiler doesn't need the path and the "\" doesn't work + * anyway. mac path delimiter is a ":" + * + * 7 2/26/97 7:36p Samir + * include oexxx_os.h from mac or win directories. + * + * 6 2/26/97 7:05p Samir + * debug_break function in osObject. + * + * 5 2/04/97 5:03p Samir + * Adjusted create to take out code_write parm + * + * 4 1/30/97 6:07p Samir + * A more object oriented approach to our OS library + * + * $NoKeywords: $ + */ +#ifndef GAMEOS_H +#define GAMEOS_H +#include "pstypes.h" +typedef struct gameos_packet { + int code; + unsigned time_stamp; +} gameos_packet; +const int GAMEOS_QUIT = 1, // This gameos object is shutting down + GAMEOS_IDLE = 2, // currently idle. + GAMEOS_UNKNOWN = 255; // Unknown message +/* class osObject + contains code to initialize all the OS dependent info and structures for an application + this class should be the parent of a class created by the programmer to match the + platform he's developing on. i.e. there should be a osWinObject for the Win32 version, + and must have all the functionality of this base Class. +*/ +class osObject +{ +protected: + bool m_Init, m_Created; // did we initialize this object or created and init. + osObject *m_ParentOS; // determines if this is a parent process + virtual void os_init() = 0; // os specific initialization + +public: + osObject() { m_Init = 0; m_Created = 0; }; + virtual ~osObject() {}; + +// parent_os is the osObject that is the parent of this new object. +// info is a device dependent structure passed in using device independent parms. + virtual void init(osObject *parent_os, void *info) = 0; + virtual bool create(osObject *parent_os, void *info) = 0; +// returns a packet with a code telling us either to quit or that we are in some +// sort of idle state, which means we perform tasks like a game. + virtual gameos_packet *defer() = 0; // defers game operation to OS. + virtual void get_info(void *info, int size_str) = 0; +}; +/* osDatabase + to get info about the application from an OS managed database (or a custom info file) + Again, this class should be the parent of a OS specific class like osWinDatabase, for instance. +*/ +class osDatabase +{ +public: + osDatabase() {}; + virtual ~osDatabase() {}; + virtual bool init() = 0; +// creates an empty classification or structure where you can store information + virtual bool create_record(const char *pathname) = 0; +// set current database focus to a particular record + virtual bool lookup_record(const char *pathname) = 0; +// read either an integer or string from the current record + virtual bool read(const char *label, char *entry, int *entrylen) = 0; + virtual bool read(const char *label, int *entry) = 0; +// write either an integer or string to a record. + virtual bool write(const char *label, char *entry, int entrylen) = 0; + virtual bool write(const char *label, int *entry) = 0; + +// get the current user's name from the os + virtual void get_user_name(char* buffer, ulong* size) = 0; +}; +// Data structures +typedef struct os_date { + ushort year; // 1-65535 A.D. (or C.E.) + ubyte month; + ubyte day; + ubyte hour; + ubyte min; + ubyte sec; + ubyte pad; // to keep dword aligned +} os_date; +// --------------------------------------------------------------------------- +// Debug system functions +// --------------------------------------------------------------------------- +#define OSMBOX_OK 1 +#define OSMBOX_YESNO 2 +bool os_ErrorBox(const char *str); // displays an error message on the screen +int os_MessageBox(int type, const char *title, const char *str); +bool os_ConsoleInit(); +void os_ConsoleOpen(int n, int row, int col, int width, int height, char * title ); +void os_ConsoleClose(int n); +void os_ConsolePrintf( int n, char * format, ... ); +void os_ConsolePrintf( int n, int row, int col, char * format, ... ); +// --------------------------------------------------------------------------- +// Operating system includes +// --------------------------------------------------------------------------- +//#if defined(WIN32) +// #include "win\oewin_os.h" +//#elif defined(MACINTOSH) // JCA: not needed +// #include "oemac_os.h" +//#endif +#endif diff --git a/lib/gr.h b/lib/gr.h new file mode 100644 index 000000000..3bd7d2024 --- /dev/null +++ b/lib/gr.h @@ -0,0 +1,567 @@ +/* + * $Logfile: /DescentIII/Main/Lib/gr.h $ + * $Revision: 18 $ + * $Date: 4/17/99 6:15p $ + * $Author: Samir $ + * + * the 2d lib main header + * + * $Log: /DescentIII/Main/Lib/gr.h $ + * + * 18 4/17/99 6:15p Samir + * replaced gr.h with grdefs.h and fixed resulting compile bugs. + * + * 17 4/14/99 3:59a Jeff + * fixed case mismatches in #includes + * + * 16 11/30/98 5:50p Jason + * added 4444 bitmap support + * + * 15 12/29/97 5:50p Samir + * Moved GR_COLOR_CHAR to grdefs.h from gr.h. + * + * 14 12/22/97 7:39p Samir + * Moved color constants from gr.h to grdefs.h + * + * 13 12/22/97 7:13p Samir + * Moved constants to grdefs.h + * + * 12 12/19/97 12:33p Samir + * Added alpha for text and took out abilitiy to create viewports inside + * viewports. + * + * 11 12/12/97 6:40p Samir + * Some viewport font functions. + * + * 10 11/16/97 6:54p Samir + * Added font::get_name function. + * + * 9 11/16/97 2:20p Samir + * Added macro to draw a bitmap to a viewport. + * + * 8 11/11/97 1:27p Samir + * Added functions to set text alpha values. + * + * 7 11/04/97 4:57p Samir + * Added get_text_color. + * + * 6 10/13/97 3:56p Jason + * made a better 3d bitmap system + * + * 5 10/03/97 11:59a Samir + * Added some color constants. + * + * 4 9/19/97 5:37p Samir + * Software font coloring works. + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 39 6/16/97 3:01p Samir + * Changed font system to work better with renderer. Fonts are rendered + * on bitmaps of 128x128 at load time. + * + * 38 6/12/97 6:30p Samir + * DDGR v2.0 Changes in 2d system implemented. + * + * 37 6/09/97 3:47p Jason + * fixed some color mapping problems + * + * 36 5/21/97 3:39p Jason + * added terrain editing stuff + * + * 35 5/21/97 1:08p Samir + * added conversion from 565 to ddgr_color format. + * + * 34 5/15/97 2:08p Samir + * Added grSurface::get_surface_desc and grViewport::get_parent. + * + * 33 5/13/97 6:36p Samir + * added lock_clear functions. + * + * + * $NoKeywords: $ + */ + + +#ifndef _GR_H +#define _GR_H + +#include "Ddgr.h" +#include "fix.h" +#include "pserror.h" +#include "pstypes.h" + +class grMemorySurface; +class grViewport; +class grSurface; +class grPen; + + +// a 'pen' structure used by the 2d system to draw things. +typedef struct gr_pen +{ + char *data; + int rowsize; + ddgr_color rgbcolor; + ddgr_color color; + ddgr_surface *sf; +} +gr_pen; + + +typedef enum grTextAlign +{ // used to align text along viewport + grTextUnjustified, + grTextLeft, + grTextCentered, + grTextRight, + grTextWordWrap +} grTextAlign; + + +typedef struct tCharProperties +{ + ubyte alpha; + ddgr_color col[4]; +} +tCharProperties; + +#define GR_VPCP_ALPHA 1 // alpha value is valid in CharProperties +#define GR_VPCP_COLOR 2 // color values are valid in CharProperties. +#define GR_VPCP_ALL (1+2) + + +// ---------------------------------------------------------------------------- +// class grFont +// will load and manage a font's capabilities. +// all fonts are stored in a 16bit hicolor format, or bitmasked. +// ---------------------------------------------------------------------------- + +const int MAX_FONTS = 16, + MAX_FONT_BITMAPS = 12; + +#define DEFAULT_FONT 0 + +typedef struct gr_font_file_record +{ + short width, height; // width of widest character and height of longest char + short flags; // flags used by the character renderer + short baseline; // pixels given to lowercase below script line start at baseline + ubyte min_ascii; // minimum ascii value used by font + ubyte max_ascii; // max ascii value used by the font + short byte_width; // width of a character in the font in bytes + ubyte *raw_data; // pixel, map data. + ubyte **char_data; // pointers to each character + short *char_widths; // individual pixel widths of each character + unsigned char *kern_data; // kerning information for specific letter combos +} gr_font_file_record; + +typedef struct gr_font_record +{ + char name[32]; + char filename[32]; // filename of font + int references; // number of references of that font + int bmps[MAX_FONT_BITMAPS]; // font bitmap handles + grMemorySurface *surfs[MAX_FONT_BITMAPS]; + ubyte *ch_u, *ch_v, *ch_w, *ch_h, *ch_surf; + float *ch_uf, *ch_vf, *ch_wf, *ch_hf; + gr_font_file_record font; + +} gr_font_record; + + +class grFont +{ + static gr_font_record m_FontList[MAX_FONTS]; + static grMemorySurface *m_CharSurf; + + int m_FontHandle; // offset into m_FontList + +public: + grFont(); + ~grFont(); + grFont(const grFont& font) + { + m_FontHandle = font.m_FontHandle; + }; + + const grFont& operator =(const grFont& font) + { + m_FontHandle = font.m_FontHandle; + return *this; + }; + +public: // Manipulation functions + static void init_system(); + static void close_system(); + static int register_font(char *fontname, char *filename); + + void init(const char *fontname); + void free(); + +// draw_char returns the next x value taking spacing and kerning into consideration. + int draw_char(grSurface *sf, int x, int y, int ch, tCharProperties *chprop); + int draw_char(grSurface *sf, int x, int y, int ch, int sx, int sy, int sw, int sh, tCharProperties *chprop); + +private: + static void free_font(gr_font_record *ft); + + static void load(char *filename, int slot); + static void translate_to_surfaces(int slot); + static void translate_mono_char(grSurface *sf, int x, int y, int index, gr_font_file_record *ft, int width); + + ubyte *get_kern_info(ubyte c1, ubyte c2); + void charblt16(grSurface *dsf, ddgr_color col, int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh); + +public: // Accessor functions + char *get_name() const { if (m_FontHandle == -1) return NULL; else return m_FontList[m_FontHandle].name; }; + int min_ascii() const { ASSERT(m_FontHandle > -1); return m_FontList[m_FontHandle].font.min_ascii; }; + int max_ascii() const { ASSERT(m_FontHandle > -1); return m_FontList[m_FontHandle].font.max_ascii; }; + int height() const { ASSERT(m_FontHandle > -1); return m_FontList[m_FontHandle].font.height; }; + int get_char_info(int ch, int *width); +}; + + + +// ---------------------------------------------------------------------------- +// class grSurface +// contains a ddgr_bitmap object and allows for blting +// ---------------------------------------------------------------------------- + +typedef struct ddgr_surface_node { + ddgr_surface *sf; + ddgr_surface_node *prev; +} ddgr_surface_node; + +const int SURFTYPE_MEMORY = 256; + +const int SURFFLAG_RENDERER = 256; // SURFACE will use renderer functions. + +class grSurface +{ +public: + static void init_system(); // initializes some global stuff for surface system + static void close_system(); + +private: + friend class grViewport; + int surf_Locked; // same value for all grViewport objects + int m_SurfInit; // surface initialized + char *m_OrigDataPtr; // non-adjusted data pointer + char *m_DataPtr; // adjusted data pointer + int m_DataRowsize; // Data rowsize + + static ddgr_surface_node *surf_list; // the surface list + static ddgr_surface_node *surf_list_cur; // current node on list. + static ddgr_surface *scratch_mem_surf; // use as temporary memory surface + +private: +// coversion blts. + void xlat8_16(char *data, int w, int h,char *pal); + void xlat16_16(char *data, int w, int h,int format=0); + void xlat16_24(char *data, int w, int h); + void xlat24_16(char *data, int w, int h); + void xlat32_16(char *data, int w, int h); + + void uniblt(int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh); + +protected: + ddgr_surface ddsfObj; // Used to call ddgr_surf_ functions + +protected: + void add_to_list(ddgr_surface *sf); // used for surface heap management + void remove_from_list(ddgr_surface *sf); + + int surf_init() const { return m_SurfInit; }; + void surf_init(int init) { m_SurfInit = init; }; + +public: + grSurface(); + virtual ~grSurface(); + +// creates a surface, allocates memory based off of parameters. + grSurface(int w, int h, int bpp, unsigned type, unsigned flags=0, const char *name=NULL); + +public: + unsigned get_flags() const { return ddsfObj.flags; }; + + void create(int w, int h, int bpp, unsigned type, unsigned flags=0, const char *name=NULL); + + void load(char *data, int w, int h, int bpp,int format=0); + void load(int handle); + void load(char *data, int w, int h, char *pal); + void load(int handle, char *pal); + + void free(); // non-dynamic version + + int width() const { ASSERT(m_SurfInit); return ddsfObj.w; }; + int height() const { ASSERT(m_SurfInit); return ddsfObj.h; }; + int bpp() const { ASSERT(m_SurfInit); return ddsfObj.bpp; }; + unsigned flags() const { ASSERT(m_SurfInit); return ddsfObj.flags; }; + + int rowsize() const { ASSERT(m_SurfInit); return m_DataRowsize; }; + char *data() const { ASSERT(m_SurfInit); return m_DataPtr; }; + + void clear(ddgr_color col=0); + void clear(int l, int t, int w, int h, ddgr_color col=0); + void replace_color(ddgr_color sc, ddgr_color dc); + + void blt(int dx, int dy, grSurface *ssf); + void blt(int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh); + + char *lock(int *rowsize); + char *lock(int x, int y, int *rowsize); + void unlock(); + + void get_surface_desc(ddgr_surface *surf) {*surf = ddsfObj; }; + +// This is VERY system dependent when called, sorry but I think this may be necessary + void attach_to_window(unsigned handle); +}; + + +// ---------------------------------------------------------------------------- +// class grMemorySurface +// this is a software bitmap not video driver dependant +// ---------------------------------------------------------------------------- + +class grMemorySurface: public grSurface +{ + bool m_FirstTimeInit; // do special stuff if we are initializing for first time + bool m_AllowInit; // we only allow initialization if we didn't create it. + +public: + grMemorySurface(); + virtual ~grMemorySurface(); + +// allocates a surface from system memory (quicker version of grSurface) + grMemorySurface(int w, int h, int bpp, unsigned flags=0, const char *name=NULL); + +// initializes a surface using a bitmap handle (no allocation) + grMemorySurface(int bm); + +// inititalizes a surface using raw data,width,rowsize info information. + bool init(int w, int h, int bpp, char *data, int rowsize, unsigned flags=0, const char *name=NULL); +}; + + +// ---------------------------------------------------------------------------- +// class grHardwareSurface +// this is a hardware bitmap allocated from the video driver +// ---------------------------------------------------------------------------- + +class grHardwareSurface: public grSurface +{ +public: + grHardwareSurface(); + grHardwareSurface(int w, int h, int bpp, unsigned flags=0, const char *name=NULL); + virtual ~grHardwareSurface(); + + bool create(int w, int h, int bpp, unsigned flags=0, const char *name=NULL); +}; + + +// ---------------------------------------------------------------------------- +// class grScreen +// child class of grSurface. Handles initializing the screen, and +// is used by the grViewport class to draw onto the screen +// ---------------------------------------------------------------------------- + +class grScreen: public grSurface +{ + friend class grViewport; + +public: + grScreen(int w, int h, int bpp, const char *name=NULL); + virtual ~grScreen(); + + void flip(); +}; + + +// ---------------------------------------------------------------------------- +// class grViewport +// a viewport is a bounded rectangle on either a screen or bitmap. +// one should be able to draw any 2d object on a viewport region. +// ---------------------------------------------------------------------------- + +// contains state information for a viewport. +typedef struct tVPState +{ + grFont font; + int text_spacing; + int text_line_spacing; + tCharProperties chprops; +} +tVPState; + + +class grViewport +{ + static int vp_Locked; // tells us if any viewport is locked + static grViewport *vp_Current_VP; // current locked viewport + + grFont text_Font; + int text_Spacing; + int text_Line_spacing; + tCharProperties char_Props; + +private: + void draw_text(grTextAlign align, int x, int y, char *str); + void draw_text_line(int x, int y, char *str); + void draw_text_line_clip(int x, int y, char *str); + int clip_line(int &l, int &t, int &r, int &b); + int clip_rect(int &x1, int &y1, int &x2, int &y2); + +protected: + int vp_Left, vp_Top, vp_Right, // viewport's global screen/bitmap bounds + vp_Bottom; + + int vp_InitLeft, vp_InitTop, + vp_InitRight, vp_InitBottom; // viewport's initial global screen coords + + grSurface *sf_Parent; // bitmap where viewport points to + gr_pen pen_Obj; + +public: + grViewport (grScreen *scr_parent); + grViewport (grSurface *sf_parent); + ~grViewport(); + + grSurface *get_parent() const { return sf_Parent; }; + +// these are the viewport's initial bounds with respect to parent surface +// these can't be changed. + int width() const { return (vp_InitRight-vp_InitLeft)+1; }; + int height() const { return (vp_InitBottom-vp_InitTop)+1; }; + int left() const { return vp_InitLeft; }; + int top() const { return vp_InitTop; }; + int right() const { return vp_InitRight; }; + int bottom() const { return vp_InitBottom; }; + +// these clipping coordinates are local to the viewports initial rectangle, based off +// of parent surface. All 2d functions use these coords to base their drawing. +// If we draw at (0,0) and the clip region is at (80,90,200,160) then we will draw at 80,90. + int clip_width() const { return (vp_Right-vp_Left)+1; }; + int clip_height() const { return (vp_Bottom-vp_Top)+1; }; + int clip_left() const { return vp_Left; }; + int clip_top() const { return vp_Top; }; + int clip_right() const { return vp_Right; }; + int clip_bottom() const { return vp_Bottom; }; + void set_clipping_rect(int left, int top, int right, int bottom); + +// these convert local viewport coords to global screen/surface coords + int global_x(int lx) const { return vp_InitLeft + vp_Left + lx; }; + int global_y(int ly) const { return vp_InitTop + vp_Top + ly; }; + int local_x(int gx) const { return gx - vp_Left - vp_InitLeft; }; + int local_y(int gy) const { return gy - vp_Top - vp_InitTop; }; + +// these functions set and restore the state of the viewport (font, colors, etc.) + void get_state(tVPState *state); + void set_state(const tVPState *state); + +// bltting functions on viewport + void clear(ddgr_color col=0); // clear with background color + void lock_clear(ddgr_color col=0); // software clear. must be used in lock + void lock_clear(ddgr_color col, int l, int t, int w, int h); + void blt(int dx, int dy, grSurface *ssf); + void blt(int dx, int dy, grSurface *ssf, int sx, int sy, int sw, int sh); + +public: + grSurface *lock(); // lock and + void unlock(); // unlock (implicitly locks surface) + + friend grSurface *VP_GET_DRAWING_SURFACE(); + friend void VP_BEGIN_DRAWING(grViewport *vp); + friend void VP_END_DRAWING(); + +// these functions only work on locked surfaces. + void hline(ddgr_color, int x1, int x2, int y1); + void line(ddgr_color color, int x1, int y1, int x2, int y2); + void rect(ddgr_color color, int l, int t, int r, int b); + void fillrect(ddgr_color color, int l, int t, int r, int b); + void setpixel(ddgr_color col, int x, int y); + void setpixelc(ddgr_color col, int x, int y); + ddgr_color getpixel(int x, int y); + ddgr_color getpixelc(int x, int y); + void circle(ddgr_color col, int xc, int yc, int r); + void fillcircle(ddgr_color col, int xc, int yc, int r); +// end + +public: +// These functions only work on locked surfaces + int printf(grTextAlign align, int x, int y, char *fmt, ...); + int printf(int x, int y, char *fmt, ...); + int puts(grTextAlign align, int x, int y, char *str); + int puts(int x, int y, char *str); + +// text formatting functions +public: + int get_text_line_width(char *text); + void set_font(const char *fontname); + void get_font(char *fontname, int size); + int get_current_font_height() const { return text_Font.height(); }; + int get_text_spacing() const { return text_Spacing; }; + int get_text_line_spacing() const { return text_Line_spacing; }; + void set_text_spacing(int spacing) { text_Spacing = spacing; }; + void set_text_line_spacing(int spacing) { text_Line_spacing = spacing; }; + +// visual properties of text functions +public: + void set_text_color(ddgr_color color); + ddgr_color get_text_color() const; + void set_char_properties(unsigned flags, const tCharProperties *props); + void get_char_properties(tCharProperties *props) const; +// end +}; + + +// inline definitions for grViewport functions + +inline void grViewport::set_text_color(ddgr_color color) +{ + char_Props.col[0] = color; + char_Props.col[1] = color; + char_Props.col[2] = color; + char_Props.col[3] = color; +} + +inline ddgr_color grViewport::get_text_color() const +{ + return char_Props.col[0]; +} + +inline void grViewport::set_char_properties(unsigned flags, const tCharProperties *props) +{ + if (flags & GR_VPCP_COLOR) { + char_Props.col[0] = props->col[0]; + char_Props.col[1] = props->col[1]; + char_Props.col[2] = props->col[2]; + char_Props.col[3] = props->col[3]; + } + if (flags & GR_VPCP_ALPHA) { + char_Props.alpha = props->alpha; + } +} + +inline void grViewport::get_char_properties(tCharProperties *props) const +{ + props->col[0] = char_Props.col[0]; + props->col[1] = char_Props.col[1]; + props->col[2] = char_Props.col[2]; + props->col[3] = char_Props.col[3]; + props->alpha = char_Props.alpha; +} + + +// useful macros + +inline void VP_BITBLT(grViewport *vp, int dx, int dy, int bm) +{ + grMemorySurface surf(bm); + vp->blt(dx,dy, &surf); +} + + +#endif diff --git a/lib/grdefs.h b/lib/grdefs.h new file mode 100644 index 000000000..903e3287e --- /dev/null +++ b/lib/grdefs.h @@ -0,0 +1,123 @@ +/* + * $Logfile: /DescentIII/Main/lib/grdefs.h $ + * $Revision: 8 $ + * $Date: 10/21/98 11:54p $ + * $Author: Samir $ + * + * + * + * $Log: /DescentIII/Main/lib/grdefs.h $ + * + * 8 10/21/98 11:54p Samir + * added GR_NULL + * + * 7 5/06/98 4:33p Samir + * added fixed screen width and height to grdefs.h (default res.) + * + * 6 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 5 12/29/97 5:50p Samir + * Moved GR_COLOR_CHAR to grdefs.h from gr.h. + * + * 4 12/22/97 7:39p Samir + * Moved color constants from gr.h to grdefs.h + * + * 3 12/22/97 7:34p Samir + * Added transparent color info. + * + * $NoKeywords: $ + */ + + +#ifndef GRDEFS_H +#define GRDEFS_H + +#include "pstypes.h" + +// bit depth info +#define BPP_TO_BYTESPP(x) (((x)+7)>>3) + +#define BPP_DEFAULT 0 // default for current display +#define BPP_8 8 // 8-bit paletted. +#define BPP_15 15 // 5-5-5 + chroma Hicolor +#define BPP_16 16 // 5-6-5 Hicolor +#define BPP_24 24 // 24 bit true color +#define BPP_32 32 // 32 bit true color + +#define FIXED_SCREEN_WIDTH 640 +#define FIXED_SCREEN_HEIGHT 480 + +// transparent color constant here. +#define OPAQUE_FLAG16 0x8000 +#define TRANSPARENT_COLOR32 0x0000FF00 +#define NEW_TRANSPARENT_COLOR 0x0000 +#define OPAQUE_FLAG OPAQUE_FLAG16 + +// a new color definition +typedef uint ddgr_color; + +// Color constants +const ddgr_color GR_NULL = 0xffffffff, // don't do a thing with this. + GR_BLACK = 0x00000000, + GR_GREEN = 0x0000ff00, + GR_RED = 0x00ff0000, + GR_BLUE = 0x000000ff, + GR_DARKGRAY = 0x00404040, + GR_LIGHTGRAY = 0x00c0c0c0, + GR_WHITE = 0x00ffffff; + +#define GR_COLOR_CHAR 1 // ASCII 1 and (r,g,b) changes current text color in string. + +// MACROS +inline ddgr_color GR_RGB(int r, int g, int b) +{ + return ((r << 16) + (g << 8) + b); +} + +inline ushort GR_RGB16(int r, int g, int b) +{ + return (((r>>3)<<10) + ((g>>3)<<5) + (b>>3)); +} + +inline ushort GR_COLOR_TO_16(ddgr_color c) +{ + int r,g,b; + r = ((c & 0x00ff0000) >> 16); + g = ((c & 0x0000ff00) >> 8); + b = (c & 0x000000ff); + + return (ushort)(((r>>3)<<10) + ((g>>3)<<5) + (b>>3)); +} + +inline int GR_COLOR_RED(ddgr_color c) +{ + int r = ((c & 0x00ff0000) >> 16); + return (int)r; +} + +inline int GR_COLOR_GREEN(ddgr_color c) +{ + int g = ((c & 0x0000ff00) >> 8); + return (int)g; +} + +inline int GR_COLOR_BLUE(ddgr_color c) +{ + int b = (c & 0x000000ff); + return (int)b; +} + +inline ddgr_color GR_16_TO_COLOR(ushort col) +{ + int r,g,b; + + r = (col & 0x7c00) >> 7; + g = (col & 0x03e0) >> 2; + b = (col & 0x001f) << 3; + + return GR_RGB(r,g,b); +} + + +#endif \ No newline at end of file diff --git a/lib/grtext.h b/lib/grtext.h new file mode 100644 index 000000000..be2b35d89 --- /dev/null +++ b/lib/grtext.h @@ -0,0 +1,318 @@ +/* + * $Logfile: /DescentIII/Main/Lib/grtext.h $ + * $Revision: 25 $ + * $Date: 11/16/99 3:18p $ + * $Author: Samir $ + * + * + * + * $Log: /DescentIII/Main/Lib/grtext.h $ + * + * 25 11/16/99 3:18p Samir + * added new data to font file and kept compatibility with D3 fonts: + * tracking value. + * + * 24 4/17/99 6:16p Samir + * added kerning and 4444 alphaed font support. + * + * 23 4/02/99 3:49p Kevin + * Added profanity filter code + * + * 22 4/01/99 5:23p Samir + * Added function to get character info. + * + * 21 3/02/99 6:26p Samir + * added font template width and height functions. + * + * 20 2/21/99 6:39p Samir + * added function to get ascii value of font character. + * + * 19 1/20/99 3:43a Jeff + * added function to get clipping parameters + * + * 18 11/03/98 7:04p Samir + * made Grtext_spacing global so word wrapper could access it. + * + * 17 10/22/98 2:41p Samir + * made grtext_Puts public. + * + * 16 10/21/98 11:50p Samir + * added prototype for advanced grtext get char info function. + * + * 15 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 14 9/24/98 2:56p Samir + * added ability to format strings inside string. + * + * 13 9/08/98 10:27a Samir + * added function to get text height. + * + * 12 6/23/98 5:04p Samir + * added grtext_GetColor + * + * 11 4/27/98 3:46p Samir + * scaling fonts. + * + * 10 4/18/98 2:08a Samir + * extended text buffer. + * + * 9 2/13/98 6:37p Samir + * Fixed tabs. + * + * 8 2/03/98 12:13p Samir + * Font shadowing support added. + * + * 7 1/30/98 2:15p Samir + * Allow for text saturation. + * + * 6 1/23/98 6:53p Samir + * Added grtext_PutChar. + * + * 5 1/12/98 5:24p Samir + * Fixed font reading and created font spew test function. + * + * 4 1/08/98 12:16p Samir + * GetLineWidth now takes a const char * + * + * 3 12/30/97 5:23p Samir + * More stuff. + * + * 2 12/29/97 5:50p Samir + * Added ability to set text window. + * + * 1 12/29/97 3:24p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef GRTEXT_H +#define GRTEXT_H + +#include "pstypes.h" +#include "ddvid.h" + +#define GRTEXT_BUFLEN 16384 +#define GRTEXT_FORMAT_CHAR 0x2 +#define GRTEXT_FORMAT_SCALAR 4 // value mulitplied to formatting value in string. + +#define GRTEXTFLAG_SATURATE 1 +#define GRTEXTFLAG_SHADOW 2 + +struct tFontTemplate; + +extern int Grtext_spacing; + +// sets text clipping parameters +void grtext_SetParameters(int left, int top, int right, int bottom, int tabspace=4); + +// gets text clipping parameters +void grtext_GetParameters(int *left, int *top, int *right, int *bottom, int *tabspace); + +// clears text buffer. doesn't render. +void grtext_Reset(); + +// sets the color for text +void grtext_SetColor(ddgr_color col); + +// returns color set by grtext_SetColor (does not count intra-string color changes) +ddgr_color grtext_GetColor(); + +// sets fancy color for text +void grtext_SetFancyColor(ddgr_color col1, ddgr_color col2, ddgr_color col3, ddgr_color col4); + +// sets the alpha value for text +void grtext_SetAlpha(ubyte alpha); + +// gets font alpha +ubyte grtext_GetAlpha(); + +// toggles text saturation +void grtext_SetFlags(int flags); + +// sets the font for text +void grtext_SetFont(int font_handle); + +// sets font scale (1.0 = normal, 0.5 = 1/2, 2.0 = twice as large. +void grtext_SetFontScale(float scale); + +// gets the current font +int grtext_GetFont(); + +// returns the height of a string in current font. +int grtext_GetTextHeight(const char *str); +#define grtext_GetHeight(_s) grtext_GetTextHeight(_s) + +// returns height of text using a font template +int grtext_GetTextHeightTemplate(tFontTemplate *ft, const char *str); +#define grtext_GetHeightTemplate(_t, _s) grtext_GetTextHeightTemplate(_t,_s) + +// returns width of text in current font. +int grtext_GetTextLineWidth(const char *str); +#define grtext_GetLineWidth(_s) grtext_GetTextLineWidth(_s) + +// returns width of text using a font template +int grtext_GetTextLineWidthTemplate(const tFontTemplate *ft, const char *str); +#define grtext_GetLineWidthTemplate(_t, _s) grtext_GetTextLineWidthTemplate(_t, _s) + +// puts a formatted string in the text buffer +void grtext_Printf(int x, int y, const char *fmt, ...); + +// puts a string on the buffer +void grtext_Puts(int x, int y, const char *str); + +// puts a character down +void grtext_PutChar(int x, int y, int ch); + +// puts a centered string in the text buffer. +void grtext_CenteredPrintf(int xoff, int y, const char *fmt, ...); + +// renders all text in buffer and clears buffer. USUALLY CALL THIS TO RENDER TEXT +void grtext_Flush(); + +// renders all text but DOESN'T flush buffer +void grtext_Render(); + + +// gets character information for the current string +// ch is a SINGLE CHARACTER +// col is the color of the current string as reported through formatting, or GR_NULL if no info +// x is the old x before call and the new potential adjusted x after the call. +// newline if it's a newline. +// line start x; +typedef struct tGetCharInfo +{ + int sx; // this will not be modified. the initial x on the same line as the string + + ddgr_color col; // these values will be modified + int x, w; // x of char, and width of char. + bool newline; // reached a newline? + bool font_char; // character exists in font? + char ch; +} +tGetCharInfo; + +const char *grtext_GetChar(const char *str, tGetCharInfo *ci); + + +////////////////////////////////////////////////////////////////////////////// +// font functions! + +// we can load a font template into this structure. call grfont_FreeTemplate(tFontTemplate) to free memory here. +typedef struct tFontTemplate +{ + ushort min_ascii, max_ascii; + ubyte *ch_widths; + ubyte *kern_data; + ubyte ch_height; + ubyte ch_maxwidth; // max width of character in font. + bool proportional; // is this font proportional? if so use array of widths, else use maxwidth + bool uppercase; // uppercase font? + bool monochromatic; // font is monochromatic? + bool newstyle; // new style 4444 font. + bool ffi2; // new font info added. + +// ffi2 style (font file info 2) + sbyte ch_tracking; // global tracking for font. +} +tFontTemplate; + + +// clears out font buffer. +void grfont_Reset(); + +// returns a handle to a loaded font. +int grfont_Load(char *fname); + +// frees a loaded font +void grfont_Free(int handle); + +// loads a font template +bool grfont_LoadTemplate(char *fname, tFontTemplate *ft); + +// frees a font template +void grfont_FreeTemplate(tFontTemplate *ft); + +// returns a character's width +int grfont_GetCharWidth(int font, int ch); + +// returns a font's height +int grfont_GetHeight(int font); + +// returns a character's width +int grfont_GetKernedSpacing(int font, int ch1, int ch2); + +// returns a character's width +int grfont_GetKernedSpacingTemp(const tFontTemplate *ft, int ch1, int ch2); + +// converts a key code to ascii version, and checks if in font. converts lowercase to upper if font +// doesn't have a lowercase set. useful alternative to ddio_KeyToAscii. +int grfont_KeyToAscii(int font, int key); + +// returns the raw bitmap data for a character in a font, its width and height +// returned data should be in 565 hicolor format if (*mono) is false. if (*mono) is true, +// then a bitmask will be returned, and you should treat a bit as a pixel. +ushort *grfont_GetRawCharacterData(int font, int ch, int *w, int *h, bool *mono); + + +///////////////////////////////////////////////////////////////////// +//EDITING FUNCTIONS ONLY + +// sets a template to a font, be careful. +bool grfont_SetTemplate(const char *pathname, const tFontTemplate *ft); + +// sets a font's template without saving... +bool grfont_SetKerning(int font, ubyte *kern_data); + +// sets a font's tracking +bool grfont_SetTracking(int font, int tracking); +int grfont_GetTracking(int font); + + +///////////////////////////////////////////////////////////////////// + +//Init functions -- call before anything is rendered! +void grtext_Init(void); + +#ifndef RELEASE +void grfont_Spew(int font, int x, int y); +#endif + +typedef struct tFontFileInfo2 // to maintain compatibility with older fonts (64 bytes long) +{ + short tracking; + char reserved[62]; +} +tFontFileInfo2; + +// font data structure internal to library but available for font editors +typedef struct tFontFileInfo +{ + short width, height; // width of widest character and height of longest char + short flags; // flags used by the character renderer + short baseline; // pixels given to lowercase below script line start at baseline + ubyte min_ascii; // minimum ascii value used by font + ubyte max_ascii; // max ascii value used by the font + short byte_width; // width of a character in the font in bytes + ubyte *raw_data; // pixel, map data. + ubyte **char_data; // pointers to each character + ubyte *char_widths; // individual pixel widths of each character + ubyte *kern_data; // kerning information for specific letter combos + +// FFI2 (newstyle) data + tFontFileInfo2 ffi2; + +// misc. + float brightness; // this IS NOT in the file, but a part of the baseline element. (upper 8bits) +} tFontFileInfo; + + +#define FT_COLOR 1 +#define FT_PROPORTIONAL 2 +#define FT_KERNED 4 +#define FT_GRADIENT 8 +#define FT_FMT4444 16 +#define FT_FFI2 32 // all fonts made after D3 should have this flag set. + +#endif \ No newline at end of file diff --git a/lib/hlsoundlib.h b/lib/hlsoundlib.h new file mode 100644 index 000000000..f38f0f113 --- /dev/null +++ b/lib/hlsoundlib.h @@ -0,0 +1,393 @@ +/* +* $Logfile: /DescentIII/Main/lib/hlsoundlib.h $ +* $Revision: 47 $ +* $Date: 3/20/00 12:26p $ +* $Author: Matt $ +* +* Descent III Device-independant sound library +* +* $Log: /DescentIII/Main/lib/hlsoundlib.h $ + * + * 47 3/20/00 12:26p Matt + * Merge of Duane's post-1.3 changes. + * Lower MAX_SOUND_OBJECTS for Mac (Mac only) + * + * 46 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 45 5/10/99 10:53p Ardussi + * changes to compile on Mac + * + * 44 4/27/99 2:10p Samir + * added code to set the desired sound card given the descriptive name of + * a sound card. + * + * 43 4/12/99 7:14p Samir + * prioritization code added. + * + * 42 4/11/99 2:17p Chris + * Made max sounds 3000 DAMN! + * + * 41 4/10/99 5:09p Samir + * beginning to add prioritization of sounds code. currently non + * functional, but allows me to change all calls to Play3dSound so that I + * can test later. + * + * 40 4/01/99 6:37p Chris + * + * 39 4/01/99 6:37p Chris + * Increased max sound objects + * + * 38 4/01/99 4:28p Samir + * hardware geometry integration if it's available. + * + * 37 2/21/99 5:48p Matt + * Added SetVolumeObject() + * + * 36 1/30/99 3:44p Chris + * Added support for time-offset'ed 3d sounds (like start at 1.2 seconds + * into the sound) + * + * 35 1/29/99 12:48p Matt + * Rewrote the doorway system + * + * 34 1/26/99 5:16p Matt + * Added StopObjectSound() + * + * 33 1/12/99 4:04p Samir + * added environmental audio on a per room basis. + * + * 32 1/08/99 6:31p Samir + * added reverb + * + * 31 1/08/99 5:37p Samir + * reverb values per room. + * + * 30 11/16/98 4:29p Chris + * Added pause new flag to the high level system + * + * 29 11/13/98 2:27p Samir + * moved music stuff to d3music.h + * + * 28 10/16/98 5:42p Chris + * + * 27 8/18/98 12:17p Chris + * f_in_game for begin sound frame (to do 3d processing) + * + * 26 8/10/98 5:52p Samir + * added tension levels for music. + * + * 25 7/28/98 5:41p Samir + * change some music system protos. + * + * 24 7/24/98 5:26p Samir + * added some D3 music functions. + * + * 23 7/09/98 8:34p Samir + * added argument to callback for streams. + * + * 22 6/29/98 9:29a Chris + * Added some support for Direct Sound 3d + * + * 21 6/24/98 12:09p Chris + * Update + * + * 20 6/22/98 12:00p Chris + * Working on sound system to make it in a nice shape. + * + * 19 6/19/98 3:03p Chris + * Made CheckAndForceSoundDataAlloc a SoundSystem function - useful for + * multiple mixers. Added IsSoundPlaying to the high level sound lib. + * + * 18 6/17/98 7:04p Chris + * Added support for 8bit stream data to be played by our 16bit sound + * mixer + * + * 17 6/16/98 3:48p Chris + * Updated the sound system and added the start of sound streaming + * + * 16 3/20/98 2:59p Chris + * Added a wall hit sound for the player and added support for a base + * volume for 3d sounds + * + * 15 2/27/98 5:31p Chris + * Changed how master_volume is set. + * + * 14 2/23/98 6:32p Chris + * Added support for a hlsoundlib unique id. This is what all the public + * functions use. Also, looping sounds are now correctly turned off and + * on. + * + * 13 2/15/98 6:41p Chris + * Added Update2dSound + * + * 12 2/13/98 5:15p Chris + * Fixed multiple problems with looping sounds not being able to be + * stopped + * + * 11 1/19/98 10:04a Matt + * Added new object handle system + * + * 10 1/13/98 5:08p Chris + * Increased sound object limit + * + * 9 1/06/98 2:12p Chris + * Added muffled sounds v.1 and made 3d sounds keep their roomnum. + * + * 8 1/02/98 5:32p Chris + * More radical changes to the sound system + * + * 7 12/31/97 2:58a Chris + * Another major revision to the SoundLib. This cleaned up the code after + * removing the direct sound secondary buffers. + * + * 6 12/30/97 2:15p Chris + * Adding further support for software 3d stuff + * + * 5 12/12/97 11:43a Chris + * Added support to toggle sndlib on/off + * + * 4 12/11/97 12:58p Chris + * Added support for fixed 3d position updating sounds. + * + * 3 12/10/97 4:47p Chris + * Revision 1.0 of new sound library (no hardware -- uses primary buffer + * streaming) + * + * 2 11/21/97 1:10p Chris + * Incremental Improvements + * + * 20 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 19 5/22/97 6:18p Chris + * + * 18 5/22/97 5:15p Chris + * + * 17 5/15/97 1:59a Chris + * + * 16 4/29/97 8:03a Chris + * Improved the sound code. High-level sound now + * fully uses the flags in the sound page and it + * resulted in a simpilier coding interface :) + * + * 15 4/28/97 11:21a Chris + * Incremental improvements is the sound system + * + * 14 4/24/97 3:25a Chris + * // Added some more support for 3d sounds + * + * 13 4/03/97 8:37p Chris + * Added the Main_OS object pass in the sound library + * initialization code. + * + * 12 3/15/97 1:34p Chris +* +* $NoKeywords: $ +*/ + +#ifndef __HLSOUNDLIB_H__ +#define __HLSOUNDLIB_H__ + +#include "ssl_lib.h" +#include "object.h" + + +////////////////////////////////////////////////////////////////////////// + +#ifdef MACINTOSH +#define MAX_SOUNDS_MIXED 32 +#define MIN_SOUNDS_MIXED 8 +#define MAX_SOUND_OBJECTS 3000 +#else +#define MAX_SOUNDS_MIXED 40 +#define MIN_SOUNDS_MIXED 20 +#define MAX_SOUND_OBJECTS 3000 +#endif + +extern char Sound_quality; +extern char Sound_mixer; +extern char Sound_card_name[]; + +class sound_object +{ +public: + sound_object() { m_obj_type_flags = SIF_UNUSED; } + +public: + unsigned int m_obj_type_flags; + int m_sound_uid; + int m_sound_index; + int m_hlsound_uid; + + play_information play_info; + + float volume_3d; // Used so that 3d sounds can have a base volume (for 2d this is in play_information) + + union + { + struct + { + int segnum; // Use physics' bit-bit stuff (inside/outside) + vector pos; + matrix orient; // only need pitch and heading -- not roll (sound cones are symetrical) + } pos_info; + + int object_handle; + } m_link_info; +}; + + + +class hlsSystem +{ + int m_f_hls_system_init; + + class sound_object m_sound_objects[MAX_SOUND_OBJECTS]; + + float m_master_volume; + int m_sounds_played; + + bool m_pause_new; + ubyte m_cur_environment; // current environment being played. + int n_lls_sounds; // number of sounds that we want the low level mixer to mix. + + bool Emulate3dSound(int sound_obj_index); + bool ComputePlayInfo(int sound_obj_index, vector *virtual_pos, vector *virtual_vel, float *adjusted_volume); + + inline int MakeUniqueId(int sound_obj_index); + inline int ValidateUniqueId(int hl_sound_uid); + + // Forcefully ends a sound + void StopSound(int sound_obj_index, unsigned char f_immediately = SKT_STOP_IMMEDIATELY); + +private: + int Play3dSound(int sound_index, pos_state *cur_pos, object *cur_obj, int priority, float volume, int flags, float offset=0.0); + +public: + + // Include a lowel-level sound system + class llsSystem *m_ll_sound_ptr; + + hlsSystem();// {m_f_hls_system_init = 0; m_sounds_played=0; m_master_volume = 1.0; m_pause_new = false;} + ~hlsSystem() {KillSoundLib(true);} + + bool IsActive(void); + + // Start and clean-up after the sound library + int InitSoundLib(oeApplication *sos, char mixer_type, char quality, bool f_kill_sound_lib = false); + void KillSoundLib(bool f_kill_sound_list); + void SetLLSoundQuantity(int n_sounds); + int GetLLSoundQuantity(); + + bool SetLLevelType(); // These are + + // Pause and Resume the library + void PauseSounds(bool f_all_sounds = false); + void ResumeSounds(); + void StopAllSounds(); + + // Code for the beginning and ending of a frame of action + + // Begin_sound_frame(listener pos/orient/velocity) + // SyncSounds + // Do sound pos updates -- IF VOLUME IS LOW AND NOT FOREVER, THEN STOP SOUND + // compute echo / reverb + // indirect/direct path sounds + void BeginSoundFrame(bool f_in_game = true); + + // Plays the deffered 3d stuff + void EndSoundFrame(); + + // Functions that play a sound + + // 3d functions (we use the sound flags in the page to determine all the cool stuff) + // Functions that play a 3d sound -- includes the 2d emulation of 3d sound + int Play3dSound(int sound_index, pos_state *cur_pos, float volume=MAX_GAME_VOLUME, int flags=0, float offset=0.0); + int Play3dSound(int sound_index, object *cur_obj, float volume=MAX_GAME_VOLUME, int flags=0, float offset=0.0); + + int Play3dSound(int sound_index, int priority, pos_state *cur_pos, float volume = MAX_GAME_VOLUME, int flags = 0, float offset=0.0); + int Play3dSound(int sound_index, int priority, object *cur_obj, float volume = MAX_GAME_VOLUME, int flags = 0, float offset=0.0); + + int PlayStream(int unique_handle, void *data, int size, int stream_format, float volume, void *stream_callback(void *user_data, int handle, int *size) = NULL); + + // 2d functions + int Play2dSound(int sound_index, float volume = MAX_GAME_VOLUME/2, float pan = 0.0, unsigned short frequency = 22050); + + int Play2dSound(int sound_index, int priority, float volume = MAX_GAME_VOLUME/2, float pan = 0.0, unsigned short frequency = 22050); + + int Update2dSound(int hlsound_uid, float volume, float pan); + + // Do nice looping stop stuff + void StopSoundLooping(int hlsound_uid); + void StopSoundImmediate(int hlsound_uid); + + // Stop all sounds attached to an object + void StopObjectSound(int objhandle); + + // Set the volume for all the sounds attached to an object + void SetVolumeObject(int objhandle,float volume); + + // Master volume controls for sound effects + void SetMasterVolume(float volume); + float GetMasterVolume(); + + // Queued sound functions + void Add2dSoundQueued(int q_num, int sound_index, float volume, float pan, unsigned short frequency); + void KillQueue(int q_num = 0); + void KillAllQueues(); + + bool CheckAndForceSoundDataAlloc(int sound_file_index); + bool SetSoundQuality(char quality); + char GetSoundQuality(void); + bool SetSoundMixer(char mixer_type); + char GetSoundMixer(void); + + bool IsSoundPlaying(int hlsound_uid); + + // Midi play stuff + void SetMidiVolume(); + void GetMidiVolume(); + void PlayMidi(); + void StopMidi(); + void PauseMidi(); + void ResumeMidi(); +}; + +extern hlsSystem Sound_system; + + +////////////////////////////////////////////////////////////////////////// +// ENVIRONMENTAL REVERB PRESETS + +#define N_ENVAUDIO_PRESETS 26 + +#define ENVAUD_PRESET_NONE 0 +#define ENVAUD_PRESET_PADDEDCELL 1 +#define ENVAUD_PRESET_ROOM 2 +#define ENVAUD_PRESET_BATHROOM 3 +#define ENVAUD_PRESET_LIVINGROOM 4 +#define ENVAUD_PRESET_STONEROOM 5 +#define ENVAUD_PRESET_AUDITORIUM 6 +#define ENVAUD_PRESET_CONCERTHALL 7 +#define ENVAUD_PRESET_CAVE 8 +#define ENVAUD_PRESET_ARENA 9 +#define ENVAUD_PRESET_HANGAR 10 +#define ENVAUD_PRESET_CARPETEDHALLWAY 11 +#define ENVAUD_PRESET_HALLWAY 12 +#define ENVAUD_PRESET_STONECORRIDOR 13 +#define ENVAUD_PRESET_ALLEY 14 +#define ENVAUD_PRESET_FOREST 15 +#define ENVAUD_PRESET_CITY 16 +#define ENVAUD_PRESET_MOUNTAINS 17 +#define ENVAUD_PRESET_QUARRY 18 +#define ENVAUD_PRESET_PLAIN 19 +#define ENVAUD_PRESET_PARKINGLOT 20 +#define ENVAUD_PRESET_SEWERPIPE 21 +#define ENVAUD_PRESET_UNDERWATER 22 +#define ENVAUD_PRESET_DRUGGED 23 +#define ENVAUD_PRESET_DIZZY 24 +#define ENVAUD_PRESET_PSYCHOTIC 25 + + +#endif \ No newline at end of file diff --git a/lib/hogfile.h b/lib/hogfile.h new file mode 100644 index 000000000..5d80d2bfb --- /dev/null +++ b/lib/hogfile.h @@ -0,0 +1,94 @@ +/* + * $Logfile: /DescentIII/Main/Lib/hogfile.h $ + * $Revision: 10 $ + * $Date: 8/06/99 4:29p $ + * $Author: Samir $ + * + * HOG file constants. + * + * $Log: /DescentIII/Main/Lib/hogfile.h $ + * + * 10 8/06/99 4:29p Samir + * added function to read record information for a hog file entry. needed + * from mn3edit. + * + * 9 2/16/99 2:08p Nate + * Made CreateNewHogFile() a bit friendlier + * + * 8 10/30/98 11:16a Nate + * Added function prototype for writing out hog file entries + * + * 7 8/16/98 4:23p Nate + * Added CreateNewHogFile() + * + * 6 8/14/98 4:38p Nate + * Fixed a few minor bugs and added better error reporting + * + * 5 8/14/98 1:01p Nate + * Added better error reporting for the HogEditor + * + * 4 7/20/98 3:34p Nate + * Added FileCopy() prototype + * + * 3 4/01/98 7:03p Samir + * modified some cfile stuff. + * + * 2 3/31/98 6:13p Samir + * new hogfile format. + * + * $NoKeywords: $ + */ + +#ifndef HOGFILE_H +#define HOGFILE_H + +#include "pstypes.h" + +#define HOG_HDR_SIZE 64 +#define HOG_TAG_STR "HOG2" + + +typedef struct tHogHeader +{ + unsigned nfiles; // number of files in header + unsigned file_data_offset; // offset in file to filedata. +} +tHogHeader; + +typedef struct tHogFileEntry +{ + char name[PSFILENAME_LEN+1]; // file name + unsigned flags; // extra info + unsigned len; // length of file + unsigned long timestamp; // time of file. +} +tHogFileEntry; + + + +#define HOGMAKER_ERROR 0 // Incorrect number of files passed in +#define HOGMAKER_OK 1 // Hog file was created successfully +#define HOGMAKER_MEMORY 2 // Could not allocated hog entry table +#define HOGMAKER_OUTFILE 3 // Error occurred writing to output hog file +#define HOGMAKER_INFILE 4 // An input file could not be found (filename is stored in hogerr_filename) +#define HOGMAKER_COPY 5 // An error occurred copying an input file into the hog file +#define HOGMAKER_OPENOUTFILE 6 // The specified hog file could not be opened for output + +// Used to return filenames involved in a NewHogFile() error +extern char hogerr_filename[PSPATHNAME_LEN]; + +int NewHogFile(const char *hogname, int nfiles, const char **filenames); +bool ReadHogHeader(FILE *fp, tHogHeader *header); +bool ReadHogEntry(FILE *fp, tHogFileEntry *entry); +bool WRITE_FILE_ENTRY(FILE *fp, tHogFileEntry *entry); +bool FileCopy(FILE *ofp,FILE *ifp,int length); + +int CreateNewHogFile(const char *hogname, int nfiles, const char **filenames, + void(* UpdateFunction)(char *)); + +// returns hog cfile info, using a library handle opened via cf_OpenLibrary. +bool cf_ReadHogFileEntry(int library, const char *filename, tHogFileEntry *entry, int *fileoffset); + + + +#endif \ No newline at end of file diff --git a/lib/intelliVIBE.h b/lib/intelliVIBE.h new file mode 100644 index 000000000..e056ce11a --- /dev/null +++ b/lib/intelliVIBE.h @@ -0,0 +1,128 @@ +#ifndef __INTELLIVIBE_H_ +#define __INTELLIVIBE_H_ + +#include //needed for HWND and HINSTANCE + +typedef struct +{ + float x,y,z; +}fvector; + +typedef struct +{ + HWND hwnd; //handle to the Window associated with Descent 3 + HINSTANCE hinst; //instance of the Descent 3 application +}d3_init_info; + +typedef struct +{ + float frametime; // time, in seconds, that the last frame of the game took + float gametime; // time, in seconds, that we have been actively playing the current level + int game_paused; // 1 if the game is currently paused, 0 if it isn't. Note: if the game is paused, Gametime and Frametime will be invalid +}d3_frame_info; + +typedef struct +{ + int weapon_index; // what kind of weapon the player is firing, see weapon defines) +}d3_fire_info; + +typedef struct +{ + //Values for thrust are from -1.0 to 1.0) + float pitch_thrust; + float heading_thrust; + float bank_thrust; + float vertical_thrust; + float sideways_thrust; + float forward_thrust; + float afterburn_thrust; + + fvector current_velocity; // current velocity of the ship + float shake_magnitude; // If the player's ship is shaking due to some external force, it will be in this value, 0<=magnitude<=120 + +}d3_controls_info; + +typedef struct +{ + fvector force_vector; // direction and magnitude of the instantaneous force +}d3_force_info; + +typedef struct +{ + float damage_amount; // how much damage is being done +}d3_damage_info; + +typedef struct +{ + d3_frame_info frame_info; + d3_fire_info fire_info; + d3_controls_info controls_info; + d3_force_info force_info; + d3_damage_info damage_info; + int flags; +}d3_intellivibe; + +//////////////////////////////////////////////////////////// +// Flag Defines +#define VIBEFLAG_PLAYER_DEAD 0x00000001 +#define VIBEFLAG_PLAYER_RESPAWN 0x00000002 +#define VIBEFLAG_LEVEL_END 0x00000004 +#define VIBEFLAG_WEAPON_FIRED 0x00000008 +#define VIBEFLAG_FORCE_APPLIED 0x00000010 +#define VIBEFLAG_PLAYER_DAMAGED 0x00000020 +#define VIBEFLAG_QUATERFRAME_0 0x00000040 +#define VIBEFLAG_QUATERFRAME_1 0x00000080 +#define VIBEFLAG_QUATERFRAME_2 0x00000100 +#define VIBEFLAG_QUATERFRAME_3 0x00000200 + +//////////////////////////////////////////////////////////// +// Weapon Defines +#define WEAPON_LASER 0 +#define WEAPON_VAUSS 1 +#define WEAPON_MICROWAVE 2 +#define WEAPON_PLASMA 3 +#define WEAPON_FUSION 4 +#define WEAPON_SUPER_LASER 5 +#define WEAPON_MASSDRIVER 6 +#define WEAPON_NAPALM 7 +#define WEAPON_EMD 8 +#define WEAPON_OMEGA 9 +#define WEAPON_CONCUSSION 10 +#define WEAPON_HOMING 11 +#define WEAPON_IMPACTMORTAR 12 +#define WEAPON_SMART 13 +#define WEAPON_MEGA 14 +#define WEAPON_FRAG 15 +#define WEAPON_GUIDED 16 +#define WEAPON_NAPALMROCKET 17 +#define WEAPON_CYCLONE 18 +#define WEAPON_BLACKSHARK 19 +#define WEAPON_FLARE 20 + +//////////////////////////////////////////////////////////// +// Functions + +#define STDCALLFUNC _stdcall + +#ifdef __cplusplus +#define CEXTERN extern "C" +#else +#define CEXTERN +#endif + +// Called once, during initialization to initialize the IntelliVIBE device. +// If initialization fails, then it should return 0, else return 1. +CEXTERN int STDCALLFUNC IntelliVIBE_Initialize(d3_init_info *init_info); +typedef int (STDCALLFUNC *IntelliVIBE_Initialize_fp)(d3_init_info *init_info); + +// Called once when Descent 3 is about to shutdown, to do any final shutdown +// procedures needed by the device. +CEXTERN void STDCALLFUNC IntelliVIBE_Shutdown(void); +typedef void (STDCALLFUNC *IntelliVIBE_Shutdown_fp)(void); + +// Called once per frame during game play. This allows the device to perform +// anything that needs to be done on a frame interval. +CEXTERN void STDCALLFUNC IntelliVIBE_DoQuaterFrame(d3_intellivibe *frame_info); +typedef void (STDCALLFUNC *IntelliVIBE_DoQuaterFrame_fp)(d3_intellivibe *frame_info); + +#endif \ No newline at end of file diff --git a/lib/joystick.h b/lib/joystick.h new file mode 100644 index 000000000..6d55cddb8 --- /dev/null +++ b/lib/joystick.h @@ -0,0 +1,163 @@ +/* + * $Logfile: /DescentIII/Main/Lib/joystick.h $ + * $Revision: 11 $ + * $Date: 10/21/99 9:27p $ + * $Author: Jeff $ + * + * Joystick interface. + * + * $Log: /DescentIII/Main/Lib/joystick.h $ + * + * 11 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 10 7/28/99 3:19p Kevin + * Mac Stuff + * + * 9 7/26/99 11:59a Samir + * add code to get name of joystick + * + * 8 7/16/99 11:14a Samir + * multiple hat support and improved direct input support. + * + * 7 4/09/99 12:02p Samir + * joystick changes (Win32 DirectInput support) + * + * 6 6/18/98 4:49p Samir + * modified for better mouse support? + * + * 5 6/02/98 4:37p Samir + * multiple joysticks supported. + * + * 4 6/01/98 4:27p Samir + * pov may return multiple positions. + * + * 3 12/05/97 12:49p Samir + * New POV constants. + * + * 2 12/03/97 7:33p Samir + * Newer joystick library. + * + * 1 11/24/97 3:27p Samir + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef JOYSTICK_H +#define JOYSTICK_H + +// joystick ids. used to initialize a stick and get its position +#ifdef MACINTOSH +#define MAX_JOYSTICKS 1 +#define JOYPOV_NUM 1 +#else +#define MAX_JOYSTICKS 8 +#define JOYPOV_NUM 4 +#endif + +// these flags tell what axes these controllers control. +#define JOYFLAG_XVALID 1 +#define JOYFLAG_YVALID 2 +#define JOYFLAG_ZVALID 4 +#define JOYFLAG_RVALID 8 +#define JOYFLAG_UVALID 16 +#define JOYFLAG_VVALID 32 +#define JOYFLAG_POVVALID 64 +#define JOYFLAG_POV2VALID 128 +#define JOYFLAG_POV3VALID 256 +#define JOYFLAG_POV4VALID 512 + +// set in joystate.pov +#define JOYPOV_DIR 8 +#define JOYPOV_MAXVAL 0x100 +#define JOYPOV_UP 0 +#define JOYPOV_RIGHT 0x40 +#define JOYPOV_DOWN 0x80 +#define JOYPOV_LEFT 0xc0 +#define JOYPOV_CENTER 0xff + +#define JOYAXIS_RANGE 256 + +typedef int tJoystick; + +#define JOYSTICK_1 0 +#define JOYSTICK_2 1 +#define JOYSTICK_3 2 +#define JOYSTICK_4 3 +#define JOYSTICK_5 4 +#define JOYSTICK_6 5 +#define JOYSTICK_7 6 +#define JOYSTICK_8 7 + + +typedef struct tJoyInfo +{ + char name[128]; + unsigned axes_mask; + unsigned num_btns; + int minx, maxx; + int miny, maxy; + int minz, maxz; + int minr, maxr; + int minu, maxu; + int minv, maxv; +} +tJoyInfo; + + +// shared between joystick remote server and local client. +#define JOY_PORT 3192 +#define JOY_REQTERM "RTRM" +#define JOY_TERM "TERM" +#define JOY_POS "POSI" +#define JOY_INFO "INFO" +#define JOY_POLL "POLL" + +typedef struct tJoyPacket +{ + char coda[4]; // used to identify packet + char buf[128]; +} +tJoyPacket; + +typedef struct tJoyPos +{ + int x; + int y; + int z; + int r; + int u; + int v; + unsigned buttons; + unsigned btn; + unsigned pov[JOYPOV_NUM]; +} +tJoyPos; + + +// joystick system initialization +bool joy_Init(bool emulation); +void joy_Close(); + +// retreive information about joystick. +void joy_GetJoyInfo(tJoystick joy, tJoyInfo *info); + +// retreive position of joystick +void joy_GetPos(tJoystick joy, tJoyPos *pos); + +// retreive uncalibrated position of joystick +void joy_GetRawPos(tJoystick joy, tJoyPos *pos); + +// returns true if joystick valid +bool joy_IsValid(tJoystick joy); + +// run by ddio_Frame +void ddio_InternalJoyFrame(); + +//DAJ Added to support InSprocket +#ifdef MACINTOSH +void ddio_InternalJoySuspend(void); +void ddio_InternalJoyResume(void); +#endif +#endif diff --git a/lib/lightmap.h b/lib/lightmap.h new file mode 100644 index 000000000..906cb0ea8 --- /dev/null +++ b/lib/lightmap.h @@ -0,0 +1,55 @@ +#ifndef LIGHTMAP_H +#define LIGHTMAP_H + +#include "pstypes.h" + +#ifdef MACINTOSH +#define MAX_LIGHTMAPS (60000) +#else +#define MAX_LIGHTMAPS (65534) +#endif +#define BAD_LM_INDEX 65535 + +// lightmap flags +#define LF_CHANGED 1 // this bitmap has changed since last frame (useful for hardware cacheing) +#define LF_LIMITS 2 // This lightmap has a specific area that has changed since last frame +#define LF_WRAP 4 // This lightmap should be drawn with wrapping (not clamping) +#define LF_BRAND_NEW 8 // This lightmap is brand new and hasn't been to the video card yet + +typedef struct +{ + ubyte width,height; // Width and height in pixels + ushort *data; // 16bit data + + ushort used; + ubyte flags; + short cache_slot; // for the renderers use + ubyte square_res; // for renderers use + ubyte cx1,cy1,cx2,cy2; // Change x and y coords +} bms_lightmap; + +extern bms_lightmap GameLightmaps[MAX_LIGHTMAPS]; + +// Sets all the lightmaps to unused +void lm_InitLightmaps(); + +void lm_ShutdownLightmaps (void); + +// Allocs a lightmap of w x h size +// Returns lightmap handle if successful, -1 if otherwise +int lm_AllocLightmap (int w,int h); + +// Given a handle, frees the lightmap memory and flags this lightmap as unused +void lm_FreeLightmap (int handle); + +// returns a lightmaps width else -1 if something is wrong +int lm_w (int handle); + +// returns a lightmaps height , else -1 if something is wrong +int lm_h (int handle); + +// returns a lightmaps data else NULL if something is wrong +ushort *lm_data (int handle); + + +#endif diff --git a/lib/linux/byteswap.h b/lib/linux/byteswap.h new file mode 100644 index 000000000..6ff81d890 --- /dev/null +++ b/lib/linux/byteswap.h @@ -0,0 +1,71 @@ +/* + * $Logfile: /DescentIII/Main/Lib/BYTESWAP.H $ + * $Revision: 1.5 $ + * $Date: 2004/12/05 04:00:20 $ + * $Author: ryan $ + * + * Byteswapping macros (for big-endian machines) + * + * $Log: byteswap.h,v $ + * Revision 1.5 2004/12/05 04:00:20 ryan + * MacOS X patches. + * + * Revision 1.4 2004/02/25 00:04:06 ryan + * Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). + * + * Revision 1.3 2004/02/09 04:14:51 kevinb + * Added newlines to all headers to reduce number of warnings printed + * + * Made some small changes to get everything compiling. + * + * All Ready to merge the 1.5 tree. + * + * Revision 1.2 2000/06/03 14:33:51 icculus + * Merge with Outrage 1.4 tree... + * + * Revision 1.1 2000/04/18 00:28:25 icculus + * Used to be capitalized: BYTESWAP.H. Yuck. + * + * Revision 1.1.1.1 2000/04/18 00:00:38 icculus + * initial checkin + * + * + * 7 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 6 5/09/99 11:41p Jeff + * function free + * + * 5 5/05/99 5:27a Jeff + * renamed endian.h to psendian.h + * + * 4 5/01/99 2:52p Jeff + * added automatic endian detection of the system + * + * 3 4/17/99 7:49p Jeff + * for some reason Linux thinks it's big endian, temp fix (undef) until I + * get around to writting a endian check function + * + * 2 1/09/99 4:38p Jeff + * added some ifdefs and fixes to get files to compile under Linux + * + * 5 5/15/97 2:22p Matt + * Fixed (hopefully; it's not tested yet) byteswapping for floats + * + * 4 2/10/97 2:22p Matt + * Added cast + * + * 3 2/10/97 2:14p Matt + * Added BIG_ENDIAN define, & INT_FLOAT() macro + * + * $NoKeywords: $ + */ + +#ifndef _BYTESWAP_LINUX_H +#define _BYTESWAP_LINUX_H + +#include "BYTESWAP.H" + +#endif + + diff --git a/lib/linux/dyna_curses.h b/lib/linux/dyna_curses.h new file mode 100644 index 000000000..6464fb9bb --- /dev/null +++ b/lib/linux/dyna_curses.h @@ -0,0 +1,196 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include + +#define LINES (*soLINES) +#define COLS (*soCOLS) +#define stdscr (*sostdscr) +#define initscr soinitscr +#define newwin sonewwin +#define wclear sowclear +#define wrefresh sowrefresh +#define scrollok soscrollok +#define leaveok soleaveok +#define wmove sowmove +#define delwin sodelwin +#define endwin soendwin +#define wprintw sowprintw +#define wnoutrefresh sownoutrefresh +#define mvwprintw somvwprintw +#define wtouchln sowtouchln +#define doupdate sodoupdate +#define mvwin somvwin + +FEXTERN int *soLINES; +FEXTERN int *soCOLS; +FEXTERN WINDOW **sostdscr; + +typedef WINDOW *(*initscr_fp)(void); +FEXTERN initscr_fp soinitscr; + +typedef WINDOW *(*newwin_fp)(int,int,int,int); +FEXTERN newwin_fp sonewwin; + +typedef int (*wclear_fp)(WINDOW *); +FEXTERN wclear_fp sowclear; + +typedef int (*wrefresh_fp)(WINDOW *); +FEXTERN wrefresh_fp sowrefresh; + +typedef int (*scrollok_fp)(WINDOW *,bool); +FEXTERN scrollok_fp soscrollok; + +typedef int (*leaveok_fp)(WINDOW *,bool); +FEXTERN leaveok_fp soleaveok; + +typedef int (*wmove_fp)(WINDOW *,int,int); +FEXTERN wmove_fp sowmove; + +typedef int (*delwin_fp)(WINDOW *); +FEXTERN delwin_fp sodelwin; + +typedef int (*endwin_fp)(void); +FEXTERN endwin_fp soendwin; + +typedef int (*wprintw_fp)(WINDOW *,const char *,...); +FEXTERN wprintw_fp sowprintw; + +typedef int (*wnoutrefresh_fp)(WINDOW *); +FEXTERN wnoutrefresh_fp sownoutrefresh; + +typedef int (*mvwprintw_fp)(WINDOW*,int,int,const char *,...); +FEXTERN mvwprintw_fp somvwprintw; + +typedef int (*wtouchln_fp)(WINDOW *,int,int,int); +FEXTERN wtouchln_fp sowtouchln; + +typedef int (*doupdate_fp)(void); +FEXTERN doupdate_fp sodoupdate; + +typedef int (*mvwin_fp)(WINDOW *,int,int); +FEXTERN mvwin_fp somvwin; + +#ifndef DECLARE_POINTERS +bool LoadCursesLib(bool load = true); +#else +#include +#include +#include +void LoadCursesLibSetNULL(void) +{ + soLINES = NULL; + soCOLS = NULL; + sostdscr = NULL; + soinitscr = NULL; + sonewwin = NULL; + sowclear = NULL; + sowrefresh = NULL; + soscrollok = NULL; + soleaveok = NULL; + sowmove = NULL; + sodelwin = NULL; + soendwin = NULL; + sowprintw = NULL; + sownoutrefresh = NULL; + somvwprintw = NULL; + sowtouchln = NULL; + sodoupdate = NULL; + somvwin = NULL; +} + +bool LoadCursesLib(bool load) +{ +#define CURSESLIB "libncurses.so" + static void *handle = NULL; + + if(!load) + { + LoadCursesLibSetNULL(); + if(handle) + { + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",CURSESLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(CURSESLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",CURSESLIB); + return false; + } + + soLINES = (int *)dlsym(handle,"LINES"); + if(!soLINES) goto load_error; + + soCOLS = (int *)dlsym(handle,"COLS"); + if(!soCOLS) goto load_error; + + sostdscr = (WINDOW **)dlsym(handle,"stdscr"); + if(!sostdscr) goto load_error; + + soinitscr = (initscr_fp)dlsym(handle,"initscr"); + if(!soinitscr) goto load_error; + + sonewwin = (newwin_fp)dlsym(handle,"newwin"); + if(!sonewwin) goto load_error; + + sowclear = (wclear_fp)dlsym(handle,"wclear"); + if(!sowclear) goto load_error; + + sowrefresh = (wrefresh_fp)dlsym(handle,"wrefresh"); + if(!sowrefresh) goto load_error; + + soscrollok = (scrollok_fp)dlsym(handle,"scrollok"); + if(!soscrollok) goto load_error; + + soleaveok = (leaveok_fp)dlsym(handle,"leaveok"); + if(!soleaveok) goto load_error; + + sowmove = (wmove_fp)dlsym(handle,"wmove"); + if(!sowmove) goto load_error; + + sodelwin = (delwin_fp)dlsym(handle,"delwin"); + if(!sodelwin) goto load_error; + + soendwin = (endwin_fp)dlsym(handle,"endwin"); + if(!soendwin) goto load_error; + + sowprintw = (wprintw_fp)dlsym(handle,"wprintw"); + if(!sowprintw) goto load_error; + + sownoutrefresh = (wnoutrefresh_fp)dlsym(handle,"wnoutrefresh"); + if(!sownoutrefresh) goto load_error; + + somvwprintw = (mvwprintw_fp)dlsym(handle,"mvwprintw"); + if(!somvwprintw) goto load_error; + + sowtouchln = (wtouchln_fp)dlsym(handle,"wtouchln"); + if(!sowtouchln) goto load_error; + + sodoupdate = (doupdate_fp)dlsym(handle,"doupdate"); + if(!sodoupdate) goto load_error; + + somvwin = (mvwin_fp)dlsym(handle,"mvwin"); + if(!somvwin) goto load_error; + + return true; + +load_error: + LoadCursesLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",CURSESLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lib/linux/dyna_glide.h b/lib/linux/dyna_glide.h new file mode 100644 index 000000000..b59667fb2 --- /dev/null +++ b/lib/linux/dyna_glide.h @@ -0,0 +1,5763 @@ +/******************************************************************* + + * THIS HEADER HAS BEEN HACKED BY AN EXPERIMENTAL PERL SCRIPT + + * dynafy.pl + + * on 7 Jul 97 + + * + + * The new functionality allows glide2x.dll to be loaded dynamically. + + * To do this, + + * 1. #define DYNAHEADER wherever you include this header, + + * glide.h. + + + * 2. You must also #define DYNAHEADER _CREATE_STORAGE in your + + * file where main() is defined. + + * 3. call GetProcAddresses(); + + */ + +/* + +** Copyright (c) 1995, 3Dfx Interactive, Inc. + +** All Rights Reserved. + +** + +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; + +** the contents of this file may not be disclosed to third parties, copied or + +** duplicated in any form, in whole or in part, without the prior written + +** permission of 3Dfx Interactive, Inc. + +** + +** RESTRICTED RIGHTS LEGEND: + +** Use, duplication or disclosure by the Government is subject to restrictions + +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data + +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or + +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - + +** rights reserved under the Copyright Laws of the United States. + +*/ + + + +/* + +** GLIDE.H + +** + +** The following #defines are relevant when using Glide: + +** + +** One of the following "platform constants" must be defined during + +** compilation: + +** + +** __DOS__ Defined for 32-bit DOS applications + +** __WIN32__ Defined for 32-bit Windows applications + +** __sparc__ Defined for Sun Solaris/SunOS + +** __linux__ Defined for Linux applications + +** __IRIX__ Defined for SGI Irix applications + +** + +*/ + +#ifndef __GLIDE_H__ + +#define __GLIDE_H__ + + + +/* The following include has been replaced + + * by the processed text from the header file. + + * #include <3dfx.h> + + */ + +/* + +** Copyright (c) 1995, 3Dfx Interactive, Inc. + +** All Rights Reserved. + +** + +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; + +** the contents of this file may not be disclosed to third parties, copied or + +** duplicated in any form, in whole or in part, without the prior written + +** permission of 3Dfx Interactive, Inc. + +** + +** RESTRICTED RIGHTS LEGEND: + +** Use, duplication or disclosure by the Government is subject to restrictions + +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data + +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or + +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - + +** rights reserved under the Copyright Laws of the United States. + +** + +** $Revision: 1.1.1.1 $ + +** $Date: 2003/08/26 03:58:12 $ + +*/ + +#ifndef __3DFX_H__ + +#define __3DFX_H__ + + + +/* + +** basic data types + +*/ + +typedef unsigned char FxU8; + +typedef signed char FxI8; + +typedef unsigned short FxU16; + +typedef signed short FxI16; + +typedef signed long FxI32; + +typedef unsigned long FxU32; + +typedef int FxBool; + +typedef float FxFloat; + +typedef double FxDouble; + + + +/* + +** color types + +*/ + +typedef unsigned long FxColor_t; + +typedef struct { float r, g, b, a; } FxColor4; + + + +/* + +** fundamental types + +*/ + +#define FXTRUE 1 + +#define FXFALSE 0 + + + +/* + +** helper macros + +*/ + +#define FXUNUSED( a ) ( (a) = (a) ) + +#define FXBIT( i ) ( 1L << (i) ) + + + +/* + +** export macros + +*/ + + + +#if defined(__MSC__) || defined(__WATCOMC__) + + #define FX_ENTRY extern + + #define FX_CALL __stdcall + + #if defined (MSVC16) + + #undef FX_ENTRY + + #undef FX_CALL + + #define FX_ENTRY + + #define FX_CALL + + #endif + +#elif defined(__DJGPP__) + + #define FX_ENTRY extern + + #define FX_CALL + +#elif defined(__unix__) + + #define FX_ENTRY extern + + #define FX_CALL + +#else + + #warning define FX_ENTRY & FX_CALL for your compiler + + #define FX_ENTRY extern + + #define FX_CALL + +#endif + + + +/* + +** x86 compiler specific stuff + +*/ + +#if defined(__BORLANDC_) + + + +# define REALMODE + + + +# define REGW( a, b ) ((a).x.b) + +# define REGB( a, b ) ((a).h.b) + +# define INT86( a, b, c ) int86(a,b,c) + +# define INT86X( a, b, c, d ) int86x(a,b,c,d) + + + +# define RM_SEG( a ) FP_SEG( a ) + +# define RM_OFF( a ) FP_OFF( a ) + + + +#elif defined(__WATCOMC__) + + + +# undef FP_SEG + +# undef FP_OFF + + + +# define REGW( a, b ) ((a).w.b) + +# define REGB( a, b ) ((a).h.b) + +# define INT86( a, b, c ) int386(a,b,c) + +# define INT86X( a, b, c, d ) int386x(a,b,c,d) + + + +# define RM_SEG( a ) ( ( ( ( FxU32 ) (a) ) & 0x000F0000 ) >> 4 ) + +# define RM_OFF( a ) ( ( FxU16 ) (a) ) + + + +#endif + + + +#endif + +/* The following include has been replaced + + * by the processed text from the header file. + + * #include + + */ + +/* + +** Copyright (c) 1995, 3Dfx Interactive, Inc. + +** All Rights Reserved. + +** + +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; + +** the contents of this file may not be disclosed to third parties, copied or + +** duplicated in any form, in whole or in part, without the prior written + +** permission of 3Dfx Interactive, Inc. + +** + +** RESTRICTED RIGHTS LEGEND: + +** Use, duplication or disclosure by the Government is subject to restrictions + +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data + +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or + +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - + +** rights reserved under the Copyright Laws of the United States. + +** + +** $Header: /cvs/kevlar.net/descent3/Main/lib/linux/dyna_glide.h,v 1.1.1.1 2003/08/26 03:58:12 kevinb Exp $ + +** $Log: dyna_glide.h,v $ +** Revision 1.1.1.1 2003/08/26 03:58:12 kevinb +** initial 1.5 import +** + * + * 2 7/14/99 6:43p Jeff + * change to load module globally + * + * 1 6/22/99 7:02p Jeff + * + * 4 2/19/99 11:49a Jason + * added more resolutions to glide + * + * 3 7/06/98 7:16p Jeff + * LoadGlideDLL no longer windows specific, calls module library + * + * 3 7/06/98 10:46a Jeff + * LoadGlideDLL uses the module library so it isn't Windows specific + * + * 2 3/02/98 5:15p Jason + * added support for dynamically loadable dlls + * + * 1 3/02/98 5:15p Jason + * header file for dynamically loadable dlls + + * + + * 5 7/24/96 3:43p Sellers + + * added 512x384 @ 60 Hz for arcade monitors + + * added 512x256 @ 60 Hz for arcade monitors + + * + + * 4 7/18/96 10:58a Sellers + + * fixed FT and TF clock delay values for lower frequencies with + + * .5/.5 combos + + * + + * 3 6/18/96 6:54p Sellers + + * added sst1InitShutdownSli() to fix Glide Splash screen problems with + + * SLI + + * + + * 2 6/13/96 7:45p Sellers + + * added "voodoo.ini" support + + * added DirectX support + + * misc cleanup + + * + + * 2 6/11/96 1:43p Sellers + + * added support for 60, 75, 85, and 120 Hz refresh rates for "most" + + * resolutions + + * + + * 1 5/08/96 5:43p Paik + + * Video definitions + +*/ + +#ifndef __SST1VID_H__ + +#define __SST1VID_H__ + + + +#ifdef __cplusplus + +extern "C" { + +#endif + + + +/* Video defines */ + + + +typedef FxI32 GrScreenRefresh_t; + +#define GR_REFRESH_60Hz 0x0 + +#define GR_REFRESH_70Hz 0x1 + +#define GR_REFRESH_72Hz 0x2 + +#define GR_REFRESH_75Hz 0x3 + +#define GR_REFRESH_80Hz 0x4 + +#define GR_REFRESH_90Hz 0x5 + +#define GR_REFRESH_100Hz 0x6 + +#define GR_REFRESH_85Hz 0x7 + +#define GR_REFRESH_120Hz 0x8 + +#define GR_REFRESH_NONE 0xff + + + +typedef FxI32 GrScreenResolution_t; + +#define GR_RESOLUTION_320x200 0x0 +#define GR_RESOLUTION_320x240 0x1 +#define GR_RESOLUTION_400x256 0x2 +#define GR_RESOLUTION_512x384 0x3 +#define GR_RESOLUTION_640x200 0x4 +#define GR_RESOLUTION_640x350 0x5 +#define GR_RESOLUTION_640x400 0x6 +#define GR_RESOLUTION_640x480 0x7 +#define GR_RESOLUTION_800x600 0x8 +#define GR_RESOLUTION_960x720 0x9 +#define GR_RESOLUTION_856x480 0xa +#define GR_RESOLUTION_512x256 0xb +#define GR_RESOLUTION_1024x768 0xC +#define GR_RESOLUTION_1280x1024 0xD +#define GR_RESOLUTION_1600x1200 0xE +#define GR_RESOLUTION_400x300 0xF +#define GR_RESOLUTION_NONE 0xff + + + +#ifdef __cplusplus + +} + +#endif + + + +#endif /* __SST1VID_H__ */ + +/* The following include has been replaced + + * by the processed text from the header file. + + * #include + + */ + +/* + +** Copyright (c) 1995, 3Dfx Interactive, Inc. + +** All Rights Reserved. + +** + +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; + +** the contents of this file may not be disclosed to third parties, copied or + +** duplicated in any form, in whole or in part, without the prior written + +** permission of 3Dfx Interactive, Inc. + +** + +** RESTRICTED RIGHTS LEGEND: + +** Use, duplication or disclosure by the Government is subject to restrictions + +n** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data + +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or + +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - + +** rights reserved under the Copyright Laws of the United States. + +** + +** $Header: /cvs/kevlar.net/descent3/Main/lib/linux/dyna_glide.h,v 1.1.1.1 2003/08/26 03:58:12 kevinb Exp $ + +** $Log: dyna_glide.h,v $ +** Revision 1.1.1.1 2003/08/26 03:58:12 kevinb +** initial 1.5 import +** + * + * 2 7/14/99 6:43p Jeff + * change to load module globally + * + * 1 6/22/99 7:02p Jeff + * + * 4 2/19/99 11:49a Jason + * added more resolutions to glide + * + * 3 7/06/98 7:16p Jeff + * LoadGlideDLL no longer windows specific, calls module library + * + * 3 7/06/98 10:46a Jeff + * LoadGlideDLL uses the module library so it isn't Windows specific + * + * 2 3/02/98 5:15p Jason + * added support for dynamically loadable dlls + * + * 1 3/02/98 5:15p Jason + * header file for dynamically loadable dlls + +*/ + +#ifndef __GLIDESYS_H__ + +#define __GLIDESYS_H__ + + + +/* + +n** ----------------------------------------------------------------------- + +** COMPILER/ENVIRONMENT CONFIGURATION + +** ----------------------------------------------------------------------- + +*/ + + + +/* Endianness is stored in bits [30:31] */ + +#define GLIDE_ENDIAN_SHIFT 30 + +#define GLIDE_ENDIAN_LITTLE (0x1 << GLIDE_ENDIAN_SHIFT) + +#define GLIDE_ENDIAN_BIG (0x2 << GLIDE_ENDIAN_SHIFT) + + + +/* OS is stored in bits [0:6] */ + +#define GLIDE_OS_SHIFT 0 + +#define GLIDE_OS_UNIX 0x1 + +#define GLIDE_OS_DOS32 0x2 + +#define GLIDE_OS_WIN32 0x4 + +#define GLIDE_OS_SYSTEM7 0x8 + +#define GLIDE_OS_OS2 0x10 + +#define GLIDE_OS_OTHER 0x20 /* For Proprietary Arcade HW */ + + + +/* Sim vs. Hardware is stored in bits [7:8] */ + +#define GLIDE_SST_SHIFT 7 + +#define GLIDE_SST_SIM (0x1 << GLIDE_SST_SHIFT) + +#define GLIDE_SST_HW (0x2 << GLIDE_SST_SHIFT ) + + + +/* Hardware Type is stored in bits [9:12] */ + +#define GLIDE_HW_SHIFT 9 + +#define GLIDE_HW_SST1 (0x1 << GLIDE_HW_SHIFT) + +#define GLIDE_HW_SST96 (0x2 << GLIDE_HW_SHIFT) + +#define GLIDE_HW_SSTH3 (0x4 << GLIDE_HW_SHIFT) + +#define GLIDE_HW_SST2 (0x8 << GLIDE_HW_SHIFT) + + + +/* + +** Make sure we handle all instances of WIN32 + +*/ + +#ifndef __WIN32__ + +# if defined ( _WIN32 ) || defined (WIN32) || defined(__NT__) + +# define __WIN32__ + +# endif + +#endif + + + +/* We need two checks on the OS: one for endian, the other for OS */ + +/* Check for endianness */ + +#if defined(__IRIX__) || defined(__sparc__) || defined(MACOS) + +# define GLIDE_ENDIAN GLIDE_ENDIAN_BIG + +#else + +# define GLIDE_ENDIAN GLIDE_ENDIAN_LITTLE + +#endif + + + +/* Check for OS */ + +#if defined(__IRIX__) || defined(__sparc__) || defined(__linux__) + +# define GLIDE_OS GLIDE_OS_UNIX + +#elif defined(__DOS__) + +# define GLIDE_OS GLIDE_OS_DOS32 + +#elif defined(__WIN32__) + +# define GLIDE_OS GLIDE_OS_WIN32 + +#endif + + + +/* Check for Simulator vs. Hardware */ + +#ifdef GLIDE_SIMULATOR + +# define GLIDE_SST GLIDE_SST_SIM + +#else + +# define GLIDE_SST GLIDE_SST_HW + +#endif + + + +/* Check for type of hardware */ + +#ifdef SST96 + +# define GLIDE_HW GLIDE_HW_SST96 + +#elif defined(SSTH3) + +# define GLIDE_HW GLIDE_HW_SSTH3 + +#elif defined(SST2) + +# define GLIDE_HW GLIDE_HW_SST2 + +#else /* Default to SST1 */ + +# define GLIDE_HW GLIDE_HW_SST1 + +#endif + + + + + +#define GLIDE_PLATFORM (GLIDE_ENDIAN | GLIDE_OS | GLIDE_SST | GLIDE_HW) + + + +/* + +** Control the number of TMUs + +*/ + +#ifndef GLIDE_NUM_TMU + +# define GLIDE_NUM_TMU 2 + +#endif + + + + + +#if ( ( GLIDE_NUM_TMU < 0 ) && ( GLIDE_NUM_TMU > 3 ) ) + +# error "GLIDE_NUM_TMU set to an invalid value" + +#endif + + + +#endif /* __GLIDESYS_H__ */ + + + +#ifdef __cplusplus + +extern "C" { + +#endif + + + +/* + +** ----------------------------------------------------------------------- + +** TYPE DEFINITIONS + +** ----------------------------------------------------------------------- + +*/ + +typedef FxU32 GrColor_t; + +typedef FxU8 GrAlpha_t; + +typedef FxU32 GrMipMapId_t; + +typedef FxU8 GrFog_t; + + + +/* + +** ----------------------------------------------------------------------- + +** CONSTANTS AND TYPES + +** ----------------------------------------------------------------------- + +*/ + +#define MAX_NUM_SST 4 + +#define MAX_MIPMAPS_PER_SST 1024 + +#define GR_FOG_TABLE_SIZE 64 + +#define GR_NULL_MIPMAP_HANDLE ((GrMipMapId_t) -1) + +#define GR_ZDEPTHVALUE_NEAREST 0xFFFF + +#define GR_ZDEPTHVALUE_FARTHEST 0x0000 + +#define GR_WDEPTHVALUE_NEAREST 0x0000 + +#define GR_WDEPTHVALUE_FARTHEST 0xFFFF + + + +#define GR_MIPMAPLEVELMASK_EVEN FXBIT(0) + +#define GR_MIPMAPLEVELMASK_ODD FXBIT(1) + +#define GR_MIPMAPLEVELMASK_BOTH (GR_MIPMAPLEVELMASK_EVEN | GR_MIPMAPLEVELMASK_ODD ) + + + +#define GR_LODBIAS_BILINEAR 0.5 + +#define GR_LODBIAS_TRILINEAR 0.0 + + + +typedef FxI32 GrChipID_t; + +#define GR_TMU0 0x0 + +#define GR_TMU1 0x1 + +#define GR_TMU2 0x2 + +#define GR_FBI 0x3 + + + +typedef FxI32 GrCombineFunction_t; + +#define GR_COMBINE_FUNCTION_ZERO 0x0 + +#define GR_COMBINE_FUNCTION_NONE GR_COMBINE_FUNCTION_ZERO + +#define GR_COMBINE_FUNCTION_LOCAL 0x1 + +#define GR_COMBINE_FUNCTION_LOCAL_ALPHA 0x2 + +#define GR_COMBINE_FUNCTION_SCALE_OTHER 0x3 + +#define GR_COMBINE_FUNCTION_BLEND_OTHER GR_COMBINE_FUNCTION_SCALE_OTHER + +#define GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL 0x4 + +#define GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL_ALPHA 0x5 + +#define GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL 0x6 + +#define GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL_ADD_LOCAL 0x7 + +#define GR_COMBINE_FUNCTION_BLEND GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL_ADD_LOCAL + +#define GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL_ADD_LOCAL_ALPHA 0x8 + +#define GR_COMBINE_FUNCTION_SCALE_MINUS_LOCAL_ADD_LOCAL 0x9 + +#define GR_COMBINE_FUNCTION_BLEND_LOCAL GR_COMBINE_FUNCTION_SCALE_MINUS_LOCAL_ADD_LOCAL + +#define GR_COMBINE_FUNCTION_SCALE_MINUS_LOCAL_ADD_LOCAL_ALPHA 0x10 + + + +typedef FxI32 GrCombineFactor_t; + +#define GR_COMBINE_FACTOR_ZERO 0x0 + +#define GR_COMBINE_FACTOR_NONE GR_COMBINE_FACTOR_ZERO + +#define GR_COMBINE_FACTOR_LOCAL 0x1 + +#define GR_COMBINE_FACTOR_OTHER_ALPHA 0x2 + +#define GR_COMBINE_FACTOR_LOCAL_ALPHA 0x3 + +#define GR_COMBINE_FACTOR_TEXTURE_ALPHA 0x4 + +#define GR_COMBINE_FACTOR_DETAIL_FACTOR GR_COMBINE_FACTOR_TEXTURE_ALPHA + +#define GR_COMBINE_FACTOR_LOD_FRACTION 0x5 + +#define GR_COMBINE_FACTOR_ONE 0x8 + +#define GR_COMBINE_FACTOR_ONE_MINUS_LOCAL 0x9 + +#define GR_COMBINE_FACTOR_ONE_MINUS_OTHER_ALPHA 0xa + +#define GR_COMBINE_FACTOR_ONE_MINUS_LOCAL_ALPHA 0xb + +#define GR_COMBINE_FACTOR_ONE_MINUS_TEXTURE_ALPHA 0xc + +#define GR_COMBINE_FACTOR_ONE_MINUS_DETAIL_FACTOR GR_COMBINE_FACTOR_ONE_MINUS_TEXTURE_ALPHA + +#define GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION 0xd + + + + + +typedef FxI32 GrCombineLocal_t; + +#define GR_COMBINE_LOCAL_ITERATED 0x0 + +#define GR_COMBINE_LOCAL_CONSTANT 0x1 + +#define GR_COMBINE_LOCAL_NONE GR_COMBINE_LOCAL_CONSTANT + +#define GR_COMBINE_LOCAL_DEPTH 0x2 + + + +typedef FxI32 GrCombineOther_t; + +#define GR_COMBINE_OTHER_ITERATED 0x0 + +#define GR_COMBINE_OTHER_TEXTURE 0x1 + +#define GR_COMBINE_OTHER_CONSTANT 0x2 + +#define GR_COMBINE_OTHER_NONE GR_COMBINE_OTHER_CONSTANT + + + + + +typedef FxI32 GrAlphaSource_t; + +#define GR_ALPHASOURCE_CC_ALPHA 0x0 + +#define GR_ALPHASOURCE_ITERATED_ALPHA 0x1 + +#define GR_ALPHASOURCE_TEXTURE_ALPHA 0x2 + +#define GR_ALPHASOURCE_TEXTURE_ALPHA_TIMES_ITERATED_ALPHA 0x3 + + + + + +typedef FxI32 GrColorCombineFnc_t; + +#define GR_COLORCOMBINE_ZERO 0x0 + +#define GR_COLORCOMBINE_CCRGB 0x1 + +#define GR_COLORCOMBINE_ITRGB 0x2 + +#define GR_COLORCOMBINE_ITRGB_DELTA0 0x3 + +#define GR_COLORCOMBINE_DECAL_TEXTURE 0x4 + +#define GR_COLORCOMBINE_TEXTURE_TIMES_CCRGB 0x5 + +#define GR_COLORCOMBINE_TEXTURE_TIMES_ITRGB 0x6 + +#define GR_COLORCOMBINE_TEXTURE_TIMES_ITRGB_DELTA0 0x7 + +#define GR_COLORCOMBINE_TEXTURE_TIMES_ITRGB_ADD_ALPHA 0x8 + +#define GR_COLORCOMBINE_TEXTURE_TIMES_ALPHA 0x9 + +#define GR_COLORCOMBINE_TEXTURE_TIMES_ALPHA_ADD_ITRGB 0xa + +#define GR_COLORCOMBINE_TEXTURE_ADD_ITRGB 0xb + +#define GR_COLORCOMBINE_TEXTURE_SUB_ITRGB 0xc + +#define GR_COLORCOMBINE_CCRGB_BLEND_ITRGB_ON_TEXALPHA 0xd + +#define GR_COLORCOMBINE_DIFF_SPEC_A 0xe + +#define GR_COLORCOMBINE_DIFF_SPEC_B 0xf + +#define GR_COLORCOMBINE_ONE 0x10 + + + +typedef FxI32 GrAlphaBlendFnc_t; + +#define GR_BLEND_ZERO 0x0 + +#define GR_BLEND_SRC_ALPHA 0x1 + +#define GR_BLEND_SRC_COLOR 0x2 + +#define GR_BLEND_DST_COLOR GR_BLEND_SRC_COLOR + +#define GR_BLEND_DST_ALPHA 0x3 + +#define GR_BLEND_ONE 0x4 + +#define GR_BLEND_ONE_MINUS_SRC_ALPHA 0x5 + +#define GR_BLEND_ONE_MINUS_SRC_COLOR 0x6 + +#define GR_BLEND_ONE_MINUS_DST_COLOR GR_BLEND_ONE_MINUS_SRC_COLOR + +#define GR_BLEND_ONE_MINUS_DST_ALPHA 0x7 + +#define GR_BLEND_RESERVED_8 0x8 + +#define GR_BLEND_RESERVED_9 0x9 + +#define GR_BLEND_RESERVED_A 0xa + +#define GR_BLEND_RESERVED_B 0xb + +#define GR_BLEND_RESERVED_C 0xc + +#define GR_BLEND_RESERVED_D 0xd + +#define GR_BLEND_RESERVED_E 0xe + +#define GR_BLEND_ALPHA_SATURATE 0xf + +#define GR_BLEND_PREFOG_COLOR GR_BLEND_ALPHA_SATURATE + + + +typedef FxI32 GrAspectRatio_t; + +#define GR_ASPECT_8x1 0x0 /* 8W x 1H */ + +#define GR_ASPECT_4x1 0x1 /* 4W x 1H */ + +#define GR_ASPECT_2x1 0x2 /* 2W x 1H */ + +#define GR_ASPECT_1x1 0x3 /* 1W x 1H */ + +#define GR_ASPECT_1x2 0x4 /* 1W x 2H */ + +#define GR_ASPECT_1x4 0x5 /* 1W x 4H */ + +#define GR_ASPECT_1x8 0x6 /* 1W x 8H */ + + + +typedef FxI32 GrBuffer_t; + +#define GR_BUFFER_FRONTBUFFER 0x0 + +#define GR_BUFFER_BACKBUFFER 0x1 + +#define GR_BUFFER_AUXBUFFER 0x2 + +#define GR_BUFFER_DEPTHBUFFER 0x3 + +#define GR_BUFFER_ALPHABUFFER 0x4 + +#define GR_BUFFER_TRIPLEBUFFER 0x5 + + + +typedef FxI32 GrChromakeyMode_t; + +#define GR_CHROMAKEY_DISABLE 0x0 + +#define GR_CHROMAKEY_ENABLE 0x1 + + + +typedef FxI32 GrCmpFnc_t; + +#define GR_CMP_NEVER 0x0 + +#define GR_CMP_LESS 0x1 + +#define GR_CMP_EQUAL 0x2 + +#define GR_CMP_LEQUAL 0x3 + +#define GR_CMP_GREATER 0x4 + +#define GR_CMP_NOTEQUAL 0x5 + +#define GR_CMP_GEQUAL 0x6 + +#define GR_CMP_ALWAYS 0x7 + + + +typedef FxI32 GrColorFormat_t; + +#define GR_COLORFORMAT_ARGB 0x0 + +#define GR_COLORFORMAT_ABGR 0x1 + + + +#define GR_COLORFORMAT_RGBA 0x2 + +#define GR_COLORFORMAT_BGRA 0x3 + + + +typedef FxI32 GrCullMode_t; + +#define GR_CULL_DISABLE 0x0 + +#define GR_CULL_NEGATIVE 0x1 + +#define GR_CULL_POSITIVE 0x2 + + + +typedef FxI32 GrDepthBufferMode_t; + +#define GR_DEPTHBUFFER_DISABLE 0x0 + +#define GR_DEPTHBUFFER_ZBUFFER 0x1 + +#define GR_DEPTHBUFFER_WBUFFER 0x2 + +#define GR_DEPTHBUFFER_ZBUFFER_COMPARE_TO_BIAS 0x3 + +#define GR_DEPTHBUFFER_WBUFFER_COMPARE_TO_BIAS 0x4 + + + +typedef FxI32 GrDitherMode_t; + +#define GR_DITHER_DISABLE 0x0 + +#define GR_DITHER_2x2 0x1 + +#define GR_DITHER_4x4 0x2 + + + +typedef FxI32 GrFogMode_t; + +#define GR_FOG_DISABLE 0x0 + +#define GR_FOG_WITH_ITERATED_ALPHA 0x1 + +#define GR_FOG_WITH_TABLE 0x2 + +#define GR_FOG_MULT2 0x100 + +#define GR_FOG_ADD2 0x200 + + + +typedef FxU32 GrLock_t; + +#define GR_LFB_READ_ONLY 0x00 + +#define GR_LFB_WRITE_ONLY 0x01 + +#define GR_LFB_IDLE 0x00 + +#define GR_LFB_NOIDLE 0x10 + + + +typedef FxI32 GrLfbBypassMode_t; + +#define GR_LFBBYPASS_DISABLE 0x0 + +#define GR_LFBBYPASS_ENABLE 0x1 + + + +typedef FxI32 GrLfbWriteMode_t; + +#define GR_LFBWRITEMODE_565 0x0 /* RGB:RGB */ + +#define GR_LFBWRITEMODE_555 0x1 /* RGB:RGB */ + +#define GR_LFBWRITEMODE_1555 0x2 /* ARGB:ARGB */ + +#define GR_LFBWRITEMODE_RESERVED1 0x3 + +#define GR_LFBWRITEMODE_888 0x4 /* RGB */ + +#define GR_LFBWRITEMODE_8888 0x5 /* ARGB */ + +#define GR_LFBWRITEMODE_RESERVED2 0x6 + +#define GR_LFBWRITEMODE_RESERVED3 0x7 + +#define GR_LFBWRITEMODE_RESERVED4 0x8 + +#define GR_LFBWRITEMODE_RESERVED5 0x9 + +#define GR_LFBWRITEMODE_RESERVED6 0xa + +#define GR_LFBWRITEMODE_RESERVED7 0xb + +#define GR_LFBWRITEMODE_565_DEPTH 0xc /* RGB:DEPTH */ + +#define GR_LFBWRITEMODE_555_DEPTH 0xd /* RGB:DEPTH */ + +#define GR_LFBWRITEMODE_1555_DEPTH 0xe /* ARGB:DEPTH */ + +#define GR_LFBWRITEMODE_ZA16 0xf /* DEPTH:DEPTH */ + +#define GR_LFBWRITEMODE_ANY 0xFF + + + + + +typedef FxI32 GrOriginLocation_t; + +#define GR_ORIGIN_UPPER_LEFT 0x0 + +#define GR_ORIGIN_LOWER_LEFT 0x1 + +#define GR_ORIGIN_ANY 0xFF + + + +typedef struct { + + int size; + + void *lfbPtr; + + FxU32 strideInBytes; + + GrLfbWriteMode_t writeMode; + + GrOriginLocation_t origin; + +} GrLfbInfo_t; + + + +typedef FxI32 GrLOD_t; + +#define GR_LOD_256 0x0 + +#define GR_LOD_128 0x1 + +#define GR_LOD_64 0x2 + +#define GR_LOD_32 0x3 + +#define GR_LOD_16 0x4 + +#define GR_LOD_8 0x5 + +#define GR_LOD_4 0x6 + +#define GR_LOD_2 0x7 + +#define GR_LOD_1 0x8 + + + +typedef FxI32 GrMipMapMode_t; + +#define GR_MIPMAP_DISABLE 0x0 /* no mip mapping */ + +#define GR_MIPMAP_NEAREST 0x1 /* use nearest mipmap */ + +#define GR_MIPMAP_NEAREST_DITHER 0x2 /* GR_MIPMAP_NEAREST + LOD dith */ + + + + + +typedef FxI32 GrSmoothingMode_t; + +#define GR_SMOOTHING_DISABLE 0x0 + +#define GR_SMOOTHING_ENABLE 0x1 + + + +typedef FxI32 GrTextureClampMode_t; + +#define GR_TEXTURECLAMP_WRAP 0x0 + +#define GR_TEXTURECLAMP_CLAMP 0x1 + + + +typedef FxI32 GrTextureCombineFnc_t; + +#define GR_TEXTURECOMBINE_ZERO 0x0 /* texout = 0 */ + +#define GR_TEXTURECOMBINE_DECAL 0x1 /* texout = texthis */ + +#define GR_TEXTURECOMBINE_OTHER 0x2 /* this TMU in passthru mode */ + +#define GR_TEXTURECOMBINE_ADD 0x3 /* tout = tthis + t(this+1) */ + +#define GR_TEXTURECOMBINE_MULTIPLY 0x4 /* texout = tthis * t(this+1) */ + +#define GR_TEXTURECOMBINE_SUBTRACT 0x5 /* Sutract from upstream TMU */ + +#define GR_TEXTURECOMBINE_DETAIL 0x6 /* detail--detail on tthis */ + +#define GR_TEXTURECOMBINE_DETAIL_OTHER 0x7 /* detail--detail on tthis+1 */ + +#define GR_TEXTURECOMBINE_TRILINEAR_ODD 0x8 /* trilinear--odd levels tthis*/ + +#define GR_TEXTURECOMBINE_TRILINEAR_EVEN 0x9 /*trilinear--even levels tthis*/ + +#define GR_TEXTURECOMBINE_ONE 0xa /* texout = 0xFFFFFFFF */ + + + +typedef FxI32 GrTextureFilterMode_t; + +#define GR_TEXTUREFILTER_POINT_SAMPLED 0x0 + +#define GR_TEXTUREFILTER_BILINEAR 0x1 + + + +typedef FxI32 GrTextureFormat_t; + +#define GR_TEXFMT_8BIT 0x0 + +#define GR_TEXFMT_RGB_332 GR_TEXFMT_8BIT + +#define GR_TEXFMT_YIQ_422 0x1 + +#define GR_TEXFMT_ALPHA_8 0x2 /* (0..0xFF) alpha */ + +#define GR_TEXFMT_INTENSITY_8 0x3 /* (0..0xFF) intensity */ + +#define GR_TEXFMT_ALPHA_INTENSITY_44 0x4 + +#define GR_TEXFMT_P_8 0x5 /* 8-bit palette */ + +#define GR_TEXFMT_RSVD0 0x6 + +#define GR_TEXFMT_RSVD1 0x7 + +#define GR_TEXFMT_16BIT 0x8 + +#define GR_TEXFMT_ARGB_8332 GR_TEXFMT_16BIT + +#define GR_TEXFMT_AYIQ_8422 0x9 + +#define GR_TEXFMT_RGB_565 0xa + +#define GR_TEXFMT_ARGB_1555 0xb + +#define GR_TEXFMT_ARGB_4444 0xc + +#define GR_TEXFMT_ALPHA_INTENSITY_88 0xd + +#define GR_TEXFMT_AP_88 0xe /* 8-bit alpha 8-bit palette */ + +#define GR_TEXFMT_RSVD2 0xf + + + +typedef FxU32 GrTexTable_t; + +#define GR_TEXTABLE_NCC0 0x0 + +#define GR_TEXTABLE_NCC1 0x1 + +#define GR_TEXTABLE_PALETTE 0x2 + + + +typedef FxU32 GrNCCTable_t; + +#define GR_NCCTABLE_NCC0 0x0 + +#define GR_NCCTABLE_NCC1 0x1 + + + +typedef FxU32 GrTexBaseRange_t; + +#define GR_TEXBASE_256 0x0 + +#define GR_TEXBASE_128 0x1 + +#define GR_TEXBASE_64 0x2 + +#define GR_TEXBASE_32_TO_1 0x3 + + + +#define GLIDE_STATE_PAD_SIZE 312 + +#ifdef GLIDE_LIB + +typedef struct _GrState_s GrState; + +#else + +typedef struct _GrState_s { + + char pad[GLIDE_STATE_PAD_SIZE]; + +} GrState; + +#endif + + + +/* + +** ----------------------------------------------------------------------- + +** STRUCTURES + +** ----------------------------------------------------------------------- + +*/ + +/* + +** 3DF texture file structs + +*/ + +typedef struct + +{ + + FxU32 width, height; + + int small_lod, large_lod; + + GrAspectRatio_t aspect_ratio; + + GrTextureFormat_t format; + +} Gu3dfHeader; + + + +typedef struct + +{ + + FxU8 yRGB[16]; + + FxI16 iRGB[4][3]; + + FxI16 qRGB[4][3]; + + FxU32 packed_data[12]; + +} GuNccTable; + + + +typedef struct { + + FxU32 data[256]; + +} GuTexPalette; + + + +typedef union { + + GuNccTable nccTable; + + GuTexPalette palette; + +} GuTexTable; + + + +typedef struct + +{ + + Gu3dfHeader header; + + GuTexTable table; + + void *data; + + FxU32 mem_required; /* memory required for mip map in bytes. */ + +} Gu3dfInfo; + + + +typedef struct { + + GrLOD_t smallLod; + + GrLOD_t largeLod; + + GrAspectRatio_t aspectRatio; + + GrTextureFormat_t format; + + void *data; + +} GrTexInfo; + + + +typedef struct + +{ + + int sst; /* SST where this texture map was stored */ + + FxBool valid; /* set when this table entry is allocated*/ + + int width, height; + + GrAspectRatio_t aspect_ratio; /* aspect ratio of the mip map. */ + + void *data; /* actual texture data */ + + + + GrTextureFormat_t format; /* format of the texture table */ + + GrMipMapMode_t mipmap_mode; /* mip map mode for this texture */ + + GrTextureFilterMode_t magfilter_mode; /* filtering to be used when magnified */ + + GrTextureFilterMode_t minfilter_mode; /* filtering to be used with minified */ + + GrTextureClampMode_t s_clamp_mode; /* how this texture should be clamped in s */ + + GrTextureClampMode_t t_clamp_mode; /* how this texture should be clamped in t */ + + FxU32 tLOD; /* Register value for tLOD register */ + + FxU32 tTextureMode; /* Register value for tTextureMode register + + not including non-texture specific bits */ + + FxU32 lod_bias; /* LOD bias of the mip map in preshifted 4.2*/ + + GrLOD_t lod_min, lod_max; /* largest and smallest levels of detail */ + + int tmu; /* tmu on which this texture resides */ + + FxU32 odd_even_mask; /* mask specifying levels on this tmu */ + + FxU32 tmu_base_address; /* base addr (in TMU mem) of this texture */ + + FxBool trilinear; /* should we blend by lod? */ + + + + GuNccTable ncc_table; /* NCC compression table (optional) */ + +} GrMipMapInfo; + + + +typedef int GrSstType; + +#define GR_SSTTYPE_VOODOO 0 + +#define GR_SSTTYPE_SST96 1 + +#define GR_SSTTYPE_AT3D 2 + + + +typedef struct GrTMUConfig_St { + + int tmuRev; /* Rev of Texelfx chip */ + + int tmuRam; /* 1, 2, or 4 MB */ + +} GrTMUConfig_t; + + + +typedef struct GrVoodooConfig_St { + + int fbRam; /* 1, 2, or 4 MB */ + + int fbiRev; /* Rev of Pixelfx chip */ + + int nTexelfx; /* How many texelFX chips are there? */ + + FxBool sliDetect; /* Is it a scan-line interleaved board? */ + + GrTMUConfig_t tmuConfig[GLIDE_NUM_TMU]; /* Configuration of the Texelfx chips */ + +} GrVoodooConfig_t; + + + +typedef struct GrSst96Config_St { + + int fbRam; /* How much? */ + + int nTexelfx; + + GrTMUConfig_t tmuConfig; + +} GrSst96Config_t; + + + +typedef struct GrAT3DConfig_St { + + int rev; + +} GrAT3DConfig_t; + + + + + +typedef struct { + + int num_sst; /* # of HW units in the system */ + + struct { + + GrSstType type; /* Which hardware is it? */ + + union SstBoard_u { + + GrVoodooConfig_t VoodooConfig; + + GrSst96Config_t SST96Config; + + GrAT3DConfig_t AT3DConfig; + + } sstBoard; + + } SSTs[MAX_NUM_SST]; /* configuration for each board */ + +} GrHwConfiguration; + + + +typedef struct GrSstPerfStats_s { + + FxU32 pixelsIn; /* # pixels processed (minus buffer clears) */ + + FxU32 chromaFail; /* # pixels not drawn due to chroma key */ + + FxU32 zFuncFail; /* # pixels not drawn due to Z comparison */ + + FxU32 aFuncFail; /* # pixels not drawn due to alpha comparison */ + + FxU32 pixelsOut; /* # pixels drawn (including buffer clears) */ + +} GrSstPerfStats_t; + + + + + +typedef struct { + + float sow; /* s texture ordinate (s over w) */ + + float tow; /* t texture ordinate (t over w) */ + + float oow; /* 1/w (used mipmapping - really 0xfff/w) */ + +} GrTmuVertex; + + + +/* + +** GrVertex + +** If these are changed the C & assembly language trisetup routines MUST + +** be changed, for they will no longer work. + +*/ + +typedef struct + +{ + + float x, y, z; /* X, Y, and Z of scrn space -- Z is ignored */ + + float r, g, b; /* R, G, B, ([0..255.0]) */ + + float ooz; /* 65535/Z (used for Z-buffering) */ + + float a; /* Alpha [0..255.0] */ + + float oow; /* 1/W (used for W-buffering, texturing) */ + + GrTmuVertex tmuvtx[GLIDE_NUM_TMU]; + +} GrVertex; + + + +/* For indexing GrVertex as a float *. + + CHANGE THESE IF THE VERTEX STRUCTURE CHANGES! + + */ + +#define GR_VERTEX_X_OFFSET 0 + +#define GR_VERTEX_Y_OFFSET 1 + +#define GR_VERTEX_Z_OFFSET 2 + +#define GR_VERTEX_R_OFFSET 3 + +#define GR_VERTEX_G_OFFSET 4 + +#define GR_VERTEX_B_OFFSET 5 + +#define GR_VERTEX_OOZ_OFFSET 6 + +#define GR_VERTEX_A_OFFSET 7 + +#define GR_VERTEX_OOW_OFFSET 8 + +#define GR_VERTEX_SOW_TMU0_OFFSET 9 + +#define GR_VERTEX_TOW_TMU0_OFFSET 10 + +#define GR_VERTEX_OOW_TMU0_OFFSET 11 + +#define GR_VERTEX_SOW_TMU1_OFFSET 12 + +#define GR_VERTEX_TOW_TMU1_OFFSET 13 + +#define GR_VERTEX_OOW_TMU1_OFFSET 14 + +#if (GLIDE_NUM_TMU > 2) + +#define GR_VERTEX_SOW_TMU2_OFFSET 15 + +#define GR_VERTEX_TOW_TMU2_OFFSET 16 + +#define GR_VERTEX_OOW_TMU2_OFFSET 17 + +#endif + + + +typedef FxU32 GrLfbSrcFmt_t; + +#define GR_LFB_SRC_FMT_565 0x00 + +#define GR_LFB_SRC_FMT_555 0x01 + +#define GR_LFB_SRC_FMT_1555 0x02 + +#define GR_LFB_SRC_FMT_888 0x04 + +#define GR_LFB_SRC_FMT_8888 0x05 + +#define GR_LFB_SRC_FMT_565_DEPTH 0x0c + +#define GR_LFB_SRC_FMT_555_DEPTH 0x0d + +#define GR_LFB_SRC_FMT_1555_DEPTH 0x0e + +#define GR_LFB_SRC_FMT_ZA16 0x0f + +#define GR_LFB_SRC_FMT_RLE16 0x80 + + + +typedef FxI32 GrPassthruMode_t; + +#define GR_PASSTHRU_SHOW_VGA 0x0 + +#define GR_PASSTHRU_SHOW_SST1 0x1 + + + +typedef FxU32 GrHint_t; + +#define GR_HINTTYPE_MIN 0 + +#define GR_HINT_STWHINT 0 + +#define GR_HINT_FIFOCHECKHINT 1 + +#define GR_HINT_FPUPRECISION 2 + +#define GR_HINT_ALLOW_MIPMAP_DITHER 3 + +#define GR_HINTTYPE_MAX 3 + + + +typedef FxU32 GrSTWHint_t; + +#define GR_STWHINT_W_DIFF_FBI FXBIT(0) + +#define GR_STWHINT_W_DIFF_TMU0 FXBIT(1) + +#define GR_STWHINT_ST_DIFF_TMU0 FXBIT(2) + +#define GR_STWHINT_W_DIFF_TMU1 FXBIT(3) + +#define GR_STWHINT_ST_DIFF_TMU1 FXBIT(4) + +#define GR_STWHINT_W_DIFF_TMU2 FXBIT(5) + +#define GR_STWHINT_ST_DIFF_TMU2 FXBIT(6) + + + +typedef FxU32 GrControl_t; + +#define GR_CONTROL_ACTIVATE 0x1 + +#define GR_CONTROL_DEACTIVATE 0x2 + +#define GR_CONTROL_RESIZE 0x3 + +#define GR_CONTROL_MOVE 0x4 + + + +#define GR_GENERATE_FIFOCHECK_HINT_MASK(swHWM, swLWM) \ +(((swHWM & 0xffff) << 16) | (swLWM & 0xffff)) + + + +/* + +** ----------------------------------------------------------------------- + +** FUNCTION PROTOTYPES + +** ----------------------------------------------------------------------- + +*/ + +#ifndef FX_GLIDE_NO_FUNC_PROTO + +/* + +** rendering functions + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDrawLine_fpt )( const GrVertex *v1, const GrVertex *v2 ); + +#else + + + +FX_ENTRY void FX_CALL + +grDrawLine( const GrVertex *v1, const GrVertex *v2 ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDrawPlanarPolygon_fpt )( int nverts, const int ilist[], const GrVertex vlist[] ); + +#else + + + +FX_ENTRY void FX_CALL + +grDrawPlanarPolygon( int nverts, const int ilist[], const GrVertex vlist[] ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDrawPlanarPolygonVertexList_fpt )( int nverts, const GrVertex vlist[] ); + +#else + + + +FX_ENTRY void FX_CALL + +grDrawPlanarPolygonVertexList( int nverts, const GrVertex vlist[] ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDrawPoint_fpt )( const GrVertex *pt ); + +#else + + + +FX_ENTRY void FX_CALL + +grDrawPoint( const GrVertex *pt ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDrawPolygon_fpt )( int nverts, const int ilist[], const GrVertex vlist[] ); + +#else + + + +FX_ENTRY void FX_CALL + +grDrawPolygon( int nverts, const int ilist[], const GrVertex vlist[] ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDrawPolygonVertexList_fpt )( int nverts, const GrVertex vlist[] ); + +#else + + + +FX_ENTRY void FX_CALL + +grDrawPolygonVertexList( int nverts, const GrVertex vlist[] ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDrawTriangle_fpt )( const GrVertex *a, const GrVertex *b, const GrVertex *c ); + +#else + + + +FX_ENTRY void FX_CALL + +grDrawTriangle( const GrVertex *a, const GrVertex *b, const GrVertex *c ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** buffer management + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grBufferClear_fpt )( GrColor_t color, GrAlpha_t alpha, FxU16 depth ); + +#else + + + +FX_ENTRY void FX_CALL + +grBufferClear( GrColor_t color, GrAlpha_t alpha, FxU16 depth ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef int( FX_CALL *grBufferNumPending_fpt )( void ); + +#else + + + +FX_ENTRY int FX_CALL + +grBufferNumPending( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grBufferSwap_fpt )( int swap_interval ); + +#else + + + +FX_ENTRY void FX_CALL + +grBufferSwap( int swap_interval ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grRenderBuffer_fpt )( GrBuffer_t buffer ); + +#else + + + +FX_ENTRY void FX_CALL + +grRenderBuffer( GrBuffer_t buffer ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** error management + +*/ + +typedef void (*GrErrorCallbackFnc_t)( const char *string, FxBool fatal ); + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grErrorSetCallback_fpt )( GrErrorCallbackFnc_t fnc ); + +#else + + + +FX_ENTRY void FX_CALL + +grErrorSetCallback( GrErrorCallbackFnc_t fnc ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** SST routines + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grSstIdle_fpt )(void ); + +#else + + + +FX_ENTRY void FX_CALL + +grSstIdle(void); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *grSstVideoLine_fpt )( void ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +grSstVideoLine( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grSstVRetraceOn_fpt )( void ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grSstVRetraceOn( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grSstIsBusy_fpt )( void ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grSstIsBusy( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grSstWinOpen_fpt )( FxU32 hWnd, GrScreenResolution_t screen_resolution, GrScreenRefresh_t refresh_rate, GrColorFormat_t color_format, GrOriginLocation_t origin_location, int nColBuffers, int nAuxBuffers ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grSstWinOpen( + + FxU32 hWnd, + + GrScreenResolution_t screen_resolution, + + GrScreenRefresh_t refresh_rate, + + GrColorFormat_t color_format, + + GrOriginLocation_t origin_location, + + int nColBuffers, + + int nAuxBuffers); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grSstWinClose_fpt )( void ); + +#else + + + +FX_ENTRY void FX_CALL + +grSstWinClose( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grSstControl_fpt )( FxU32 code ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grSstControl( FxU32 code ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grSstQueryHardware_fpt )( GrHwConfiguration *hwconfig ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grSstQueryHardware( GrHwConfiguration *hwconfig ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grSstQueryBoards_fpt )( GrHwConfiguration *hwconfig ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grSstQueryBoards( GrHwConfiguration *hwconfig ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grSstOrigin_fpt )(GrOriginLocation_t origin ); + +#else + + + +FX_ENTRY void FX_CALL + +grSstOrigin(GrOriginLocation_t origin); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grSstSelect_fpt )( int which_sst ); + +#else + + + +FX_ENTRY void FX_CALL + +grSstSelect( int which_sst ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef int( FX_CALL *grSstScreenHeight_fpt )( void ); + +#else + + + +FX_ENTRY int FX_CALL + +grSstScreenHeight( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef int( FX_CALL *grSstScreenWidth_fpt )( void ); + +#else + + + +FX_ENTRY int FX_CALL + +grSstScreenWidth( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *grSstStatus_fpt )( void ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +grSstStatus( void ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** Drawing Statistics + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grSstPerfStats_fpt )(GrSstPerfStats_t *pStats ); + +#else + + + +FX_ENTRY void FX_CALL + +grSstPerfStats(GrSstPerfStats_t *pStats); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grSstResetPerfStats_fpt )(void ); + +#else + + + +FX_ENTRY void FX_CALL + +grSstResetPerfStats(void); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grResetTriStats_fpt )( ); + +#else + + + +FX_ENTRY void FX_CALL + +grResetTriStats(); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTriStats_fpt )(FxU32 *trisProcessed, FxU32 *trisDrawn ); + +#else + + + +FX_ENTRY void FX_CALL + +grTriStats(FxU32 *trisProcessed, FxU32 *trisDrawn); + + + +#endif /* DYNAHEADER */ + + + +/* + +** Glide configuration and special effect maintenance functions + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAlphaBlendFunction_fpt )( GrAlphaBlendFnc_t rgb_sf, GrAlphaBlendFnc_t rgb_df, GrAlphaBlendFnc_t alpha_sf, GrAlphaBlendFnc_t alpha_df ); + +#else + + + +FX_ENTRY void FX_CALL + +grAlphaBlendFunction( + + GrAlphaBlendFnc_t rgb_sf, GrAlphaBlendFnc_t rgb_df, + + GrAlphaBlendFnc_t alpha_sf, GrAlphaBlendFnc_t alpha_df + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAlphaCombine_fpt )( GrCombineFunction_t function, GrCombineFactor_t factor, GrCombineLocal_t local, GrCombineOther_t other, FxBool invert ); + +#else + + + +FX_ENTRY void FX_CALL + +grAlphaCombine( + + GrCombineFunction_t function, GrCombineFactor_t factor, + + GrCombineLocal_t local, GrCombineOther_t other, + + FxBool invert + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAlphaControlsITRGBLighting_fpt )( FxBool enable ); + +#else + + + +FX_ENTRY void FX_CALL + +grAlphaControlsITRGBLighting( FxBool enable ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAlphaTestFunction_fpt )( GrCmpFnc_t function ); + +#else + + + +FX_ENTRY void FX_CALL + +grAlphaTestFunction( GrCmpFnc_t function ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAlphaTestReferenceValue_fpt )( GrAlpha_t value ); + +#else + + + +FX_ENTRY void FX_CALL + +grAlphaTestReferenceValue( GrAlpha_t value ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grChromakeyMode_fpt )( GrChromakeyMode_t mode ); + +#else + + + +FX_ENTRY void FX_CALL + +grChromakeyMode( GrChromakeyMode_t mode ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grChromakeyValue_fpt )( GrColor_t value ); + +#else + + + +FX_ENTRY void FX_CALL + +grChromakeyValue( GrColor_t value ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grClipWindow_fpt )( int minx, int miny, int maxx, int maxy ); + +#else + + + +FX_ENTRY void FX_CALL + +grClipWindow( int minx, int miny, int maxx, int maxy ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grColorCombine_fpt )( GrCombineFunction_t function, GrCombineFactor_t factor, GrCombineLocal_t local, GrCombineOther_t other, FxBool invert ); + +#else + + + +FX_ENTRY void FX_CALL + +grColorCombine( + + GrCombineFunction_t function, GrCombineFactor_t factor, + + GrCombineLocal_t local, GrCombineOther_t other, + + FxBool invert ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grColorMask_fpt )( FxBool rgb, FxBool a ); + +#else + + + +FX_ENTRY void FX_CALL + +grColorMask( FxBool rgb, FxBool a ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grCullMode_fpt )( GrCullMode_t mode ); + +#else + + + +FX_ENTRY void FX_CALL + +grCullMode( GrCullMode_t mode ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grConstantColorValue_fpt )( GrColor_t value ); + +#else + + + +FX_ENTRY void FX_CALL + +grConstantColorValue( GrColor_t value ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grConstantColorValue4_fpt )( float a, float r, float g, float b ); + +#else + + + +FX_ENTRY void FX_CALL + +grConstantColorValue4( float a, float r, float g, float b ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDepthBiasLevel_fpt )( FxI16 level ); + +#else + + + +FX_ENTRY void FX_CALL + +grDepthBiasLevel( FxI16 level ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDepthBufferFunction_fpt )( GrCmpFnc_t function ); + +#else + + + +FX_ENTRY void FX_CALL + +grDepthBufferFunction( GrCmpFnc_t function ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDepthBufferMode_fpt )( GrDepthBufferMode_t mode ); + +#else + + + +FX_ENTRY void FX_CALL + +grDepthBufferMode( GrDepthBufferMode_t mode ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDepthMask_fpt )( FxBool mask ); + +#else + + + +FX_ENTRY void FX_CALL + +grDepthMask( FxBool mask ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDisableAllEffects_fpt )( void ); + +#else + + + +FX_ENTRY void FX_CALL + +grDisableAllEffects( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grDitherMode_fpt )( GrDitherMode_t mode ); + +#else + + + +FX_ENTRY void FX_CALL + +grDitherMode( GrDitherMode_t mode ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grFogColorValue_fpt )( GrColor_t fogcolor ); + +#else + + + +FX_ENTRY void FX_CALL + +grFogColorValue( GrColor_t fogcolor ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grFogMode_fpt )( GrFogMode_t mode ); + +#else + + + +FX_ENTRY void FX_CALL + +grFogMode( GrFogMode_t mode ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grFogTable_fpt )( const GrFog_t ft[GR_FOG_TABLE_SIZE] ); + +#else + + + +FX_ENTRY void FX_CALL + +grFogTable( const GrFog_t ft[GR_FOG_TABLE_SIZE] ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grGammaCorrectionValue_fpt )( float value ); + +#else + + + +FX_ENTRY void FX_CALL + +grGammaCorrectionValue( float value ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grSplash_fpt )(float x, float y, float width, float height, FxU32 frame ); + +#else + + + +FX_ENTRY void FX_CALL + +grSplash(float x, float y, float width, float height, FxU32 frame); + + + +#endif /* DYNAHEADER */ + + + +/* + +** texture mapping control functions + +*/ + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *grTexCalcMemRequired_fpt )( GrLOD_t lodmin, GrLOD_t lodmax, GrAspectRatio_t aspect, GrTextureFormat_t fmt ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +grTexCalcMemRequired( + + GrLOD_t lodmin, GrLOD_t lodmax, + + GrAspectRatio_t aspect, GrTextureFormat_t fmt); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *grTexTextureMemRequired_fpt )( FxU32 evenOdd, GrTexInfo *info ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +grTexTextureMemRequired( FxU32 evenOdd, + + GrTexInfo *info ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *grTexMinAddress_fpt )( GrChipID_t tmu ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +grTexMinAddress( GrChipID_t tmu ); + + + +#endif /* DYNAHEADER */ + + + + + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *grTexMaxAddress_fpt )( GrChipID_t tmu ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +grTexMaxAddress( GrChipID_t tmu ); + + + +#endif /* DYNAHEADER */ + + + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexNCCTable_fpt )( GrChipID_t tmu, GrNCCTable_t table ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexNCCTable( GrChipID_t tmu, GrNCCTable_t table ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexSource_fpt )( GrChipID_t tmu, FxU32 startAddress, FxU32 evenOdd, GrTexInfo *info ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexSource( GrChipID_t tmu, + + FxU32 startAddress, + + FxU32 evenOdd, + + GrTexInfo *info ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexClampMode_fpt )( GrChipID_t tmu, GrTextureClampMode_t s_clampmode, GrTextureClampMode_t t_clampmode ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexClampMode( + + GrChipID_t tmu, + + GrTextureClampMode_t s_clampmode, + + GrTextureClampMode_t t_clampmode + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexCombine_fpt )( GrChipID_t tmu, GrCombineFunction_t rgb_function, GrCombineFactor_t rgb_factor, GrCombineFunction_t alpha_function, GrCombineFactor_t alpha_factor, FxBool rgb_invert, FxBool alpha_invert ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexCombine( + + GrChipID_t tmu, + + GrCombineFunction_t rgb_function, + + GrCombineFactor_t rgb_factor, + + GrCombineFunction_t alpha_function, + + GrCombineFactor_t alpha_factor, + + FxBool rgb_invert, + + FxBool alpha_invert + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexCombineFunction_fpt )( GrChipID_t tmu, GrTextureCombineFnc_t fnc ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexCombineFunction( + + GrChipID_t tmu, + + GrTextureCombineFnc_t fnc + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexDetailControl_fpt )( GrChipID_t tmu, int lod_bias, FxU8 detail_scale, float detail_max ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexDetailControl( + + GrChipID_t tmu, + + int lod_bias, + + FxU8 detail_scale, + + float detail_max + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexFilterMode_fpt )( GrChipID_t tmu, GrTextureFilterMode_t minfilter_mode, GrTextureFilterMode_t magfilter_mode ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexFilterMode( + + GrChipID_t tmu, + + GrTextureFilterMode_t minfilter_mode, + + GrTextureFilterMode_t magfilter_mode + + ); + + + +#endif /* DYNAHEADER */ + + + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexLodBiasValue_fpt )(GrChipID_t tmu, float bias ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexLodBiasValue(GrChipID_t tmu, float bias ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexDownloadMipMap_fpt )( GrChipID_t tmu, FxU32 startAddress, FxU32 evenOdd, GrTexInfo *info ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexDownloadMipMap( GrChipID_t tmu, + + FxU32 startAddress, + + FxU32 evenOdd, + + GrTexInfo *info ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexDownloadMipMapLevel_fpt )( GrChipID_t tmu, FxU32 startAddress, GrLOD_t thisLod, GrLOD_t largeLod, GrAspectRatio_t aspectRatio, GrTextureFormat_t format, FxU32 evenOdd, void *data ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexDownloadMipMapLevel( GrChipID_t tmu, + + FxU32 startAddress, + + GrLOD_t thisLod, + + GrLOD_t largeLod, + + GrAspectRatio_t aspectRatio, + + GrTextureFormat_t format, + + FxU32 evenOdd, + + void *data ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexDownloadMipMapLevelPartial_fpt )( GrChipID_t tmu, FxU32 startAddress, GrLOD_t thisLod, GrLOD_t largeLod, GrAspectRatio_t aspectRatio, GrTextureFormat_t format, FxU32 evenOdd, void *data, int start, int end ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexDownloadMipMapLevelPartial( GrChipID_t tmu, + + FxU32 startAddress, + + GrLOD_t thisLod, + + GrLOD_t largeLod, + + GrAspectRatio_t aspectRatio, + + GrTextureFormat_t format, + + FxU32 evenOdd, + + void *data, + + int start, + + int end ); + + + +#endif /* DYNAHEADER */ + + + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *ConvertAndDownloadRle_fpt )( GrChipID_t tmu, FxU32 startAddress, GrLOD_t thisLod, GrLOD_t largeLod, GrAspectRatio_t aspectRatio, GrTextureFormat_t format, FxU32 evenOdd, FxU8 *bm_data, long bm_h, FxU32 u0, FxU32 v0, FxU32 width, FxU32 height, FxU32 dest_width, FxU32 dest_height, FxU16 *tlut ); + +#else + + + +FX_ENTRY void FX_CALL + +ConvertAndDownloadRle( GrChipID_t tmu, + + FxU32 startAddress, + + GrLOD_t thisLod, + + GrLOD_t largeLod, + + GrAspectRatio_t aspectRatio, + + GrTextureFormat_t format, + + FxU32 evenOdd, + + FxU8 *bm_data, + + long bm_h, + + FxU32 u0, + + FxU32 v0, + + FxU32 width, + + FxU32 height, + + FxU32 dest_width, + + FxU32 dest_height, + + FxU16 *tlut); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grCheckForRoom_fpt )(FxI32 n ); + +#else + + + +FX_ENTRY void FX_CALL + +grCheckForRoom(FxI32 n); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexDownloadTable_fpt )( GrChipID_t tmu, GrTexTable_t type, void *data ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexDownloadTable( GrChipID_t tmu, + + GrTexTable_t type, + + void *data ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexDownloadTablePartial_fpt )( GrChipID_t tmu, GrTexTable_t type, void *data, int start, int end ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexDownloadTablePartial( GrChipID_t tmu, + + GrTexTable_t type, + + void *data, + + int start, + + int end ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexMipMapMode_fpt )( GrChipID_t tmu, GrMipMapMode_t mode, FxBool lodBlend ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexMipMapMode( GrChipID_t tmu, + + GrMipMapMode_t mode, + + FxBool lodBlend ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexMultibase_fpt )( GrChipID_t tmu, FxBool enable ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexMultibase( GrChipID_t tmu, + + FxBool enable ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grTexMultibaseAddress_fpt )( GrChipID_t tmu, GrTexBaseRange_t range, FxU32 startAddress, FxU32 evenOdd, GrTexInfo *info ); + +#else + + + +FX_ENTRY void FX_CALL + +grTexMultibaseAddress( GrChipID_t tmu, + + GrTexBaseRange_t range, + + FxU32 startAddress, + + FxU32 evenOdd, + + GrTexInfo *info ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** utility texture functions + +*/ + +#ifdef DYNAHEADER + +typedef GrMipMapId_t( FX_CALL *guTexAllocateMemory_fpt )( GrChipID_t tmu, FxU8 odd_even_mask, int width, int height, GrTextureFormat_t fmt, GrMipMapMode_t mm_mode, GrLOD_t smallest_lod, GrLOD_t largest_lod, GrAspectRatio_t aspect, GrTextureClampMode_t s_clamp_mode, GrTextureClampMode_t t_clamp_mode, GrTextureFilterMode_t minfilter_mode, GrTextureFilterMode_t magfilter_mode, float lod_bias, FxBool trilinear ); + +#else + + + +FX_ENTRY GrMipMapId_t FX_CALL + +guTexAllocateMemory( + + GrChipID_t tmu, + + FxU8 odd_even_mask, + + int width, int height, + + GrTextureFormat_t fmt, + + GrMipMapMode_t mm_mode, + + GrLOD_t smallest_lod, GrLOD_t largest_lod, + + GrAspectRatio_t aspect, + + GrTextureClampMode_t s_clamp_mode, + + GrTextureClampMode_t t_clamp_mode, + + GrTextureFilterMode_t minfilter_mode, + + GrTextureFilterMode_t magfilter_mode, + + float lod_bias, + + FxBool trilinear + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *guTexChangeAttributes_fpt )( GrMipMapId_t mmid, int width, int height, GrTextureFormat_t fmt, GrMipMapMode_t mm_mode, GrLOD_t smallest_lod, GrLOD_t largest_lod, GrAspectRatio_t aspect, GrTextureClampMode_t s_clamp_mode, GrTextureClampMode_t t_clamp_mode, GrTextureFilterMode_t minFilterMode, GrTextureFilterMode_t magFilterMode ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +guTexChangeAttributes( + + GrMipMapId_t mmid, + + int width, int height, + + GrTextureFormat_t fmt, + + GrMipMapMode_t mm_mode, + + GrLOD_t smallest_lod, GrLOD_t largest_lod, + + GrAspectRatio_t aspect, + + GrTextureClampMode_t s_clamp_mode, + + GrTextureClampMode_t t_clamp_mode, + + GrTextureFilterMode_t minFilterMode, + + GrTextureFilterMode_t magFilterMode + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guTexCombineFunction_fpt )( GrChipID_t tmu, GrTextureCombineFnc_t fnc ); + +#else + + + +FX_ENTRY void FX_CALL + +guTexCombineFunction( + + GrChipID_t tmu, + + GrTextureCombineFnc_t fnc + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef GrMipMapId_t( FX_CALL *guTexGetCurrentMipMap_fpt )( GrChipID_t tmu ); + +#else + + + +FX_ENTRY GrMipMapId_t FX_CALL + +guTexGetCurrentMipMap( GrChipID_t tmu ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef GrMipMapInfo *( FX_CALL *guTexGetMipMapInfo_fpt )( GrMipMapId_t mmid ); + +#else + + + +FX_ENTRY GrMipMapInfo * FX_CALL + +guTexGetMipMapInfo( GrMipMapId_t mmid ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *guTexMemQueryAvail_fpt )( GrChipID_t tmu ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +guTexMemQueryAvail( GrChipID_t tmu ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guTexMemReset_fpt )( void ); + +#else + + + +FX_ENTRY void FX_CALL + +guTexMemReset( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guTexDownloadMipMap_fpt )( GrMipMapId_t mmid, const void *src, const GuNccTable *table ); + +#else + + + +FX_ENTRY void FX_CALL + +guTexDownloadMipMap( + + GrMipMapId_t mmid, + + const void *src, + + const GuNccTable *table + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guTexDownloadMipMapLevel_fpt )( GrMipMapId_t mmid, GrLOD_t lod, const void **src ); + +#else + + + +FX_ENTRY void FX_CALL + +guTexDownloadMipMapLevel( + + GrMipMapId_t mmid, + + GrLOD_t lod, + + const void **src + + ); + + + +#endif /* DYNAHEADER */ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guTexSource_fpt )( GrMipMapId_t id ); + +#else + + + +FX_ENTRY void FX_CALL + +guTexSource( GrMipMapId_t id ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** linear frame buffer functions + +*/ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grLfbLock_fpt )( GrLock_t type, GrBuffer_t buffer, GrLfbWriteMode_t writeMode, GrOriginLocation_t origin, FxBool pixelPipeline, GrLfbInfo_t *info ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grLfbLock( GrLock_t type, GrBuffer_t buffer, GrLfbWriteMode_t writeMode, + + GrOriginLocation_t origin, FxBool pixelPipeline, + + GrLfbInfo_t *info ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grLfbUnlock_fpt )( GrLock_t type, GrBuffer_t buffer ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grLfbUnlock( GrLock_t type, GrBuffer_t buffer ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grLfbConstantAlpha_fpt )( GrAlpha_t alpha ); + +#else + + + +FX_ENTRY void FX_CALL + +grLfbConstantAlpha( GrAlpha_t alpha ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grLfbConstantDepth_fpt )( FxU16 depth ); + +#else + + + +FX_ENTRY void FX_CALL + +grLfbConstantDepth( FxU16 depth ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grLfbWriteColorSwizzle_fpt )(FxBool swizzleBytes, FxBool swapWords ); + +#else + + + +FX_ENTRY void FX_CALL + +grLfbWriteColorSwizzle(FxBool swizzleBytes, FxBool swapWords); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grLfbWriteColorFormat_fpt )(GrColorFormat_t colorFormat ); + +#else + + + +FX_ENTRY void FX_CALL + +grLfbWriteColorFormat(GrColorFormat_t colorFormat); + + + +#endif /* DYNAHEADER */ + + + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grLfbWriteRegion_fpt )( GrBuffer_t dst_buffer, FxU32 dst_x, FxU32 dst_y, GrLfbSrcFmt_t src_format, FxU32 src_width, FxU32 src_height, FxI32 src_stride, void *src_data ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grLfbWriteRegion( GrBuffer_t dst_buffer, + + FxU32 dst_x, FxU32 dst_y, + + GrLfbSrcFmt_t src_format, + + FxU32 src_width, FxU32 src_height, + + FxI32 src_stride, void *src_data ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *grLfbReadRegion_fpt )( GrBuffer_t src_buffer, FxU32 src_x, FxU32 src_y, FxU32 src_width, FxU32 src_height, FxU32 dst_stride, void *dst_data ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +grLfbReadRegion( GrBuffer_t src_buffer, + + FxU32 src_x, FxU32 src_y, + + FxU32 src_width, FxU32 src_height, + + FxU32 dst_stride, void *dst_data ); + + + +#endif /* DYNAHEADER */ + + + + + +/* + +** Antialiasing Functions + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAADrawLine_fpt )(const GrVertex *v1, const GrVertex *v2 ); + +#else + + + +FX_ENTRY void FX_CALL + +grAADrawLine(const GrVertex *v1, const GrVertex *v2); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAADrawPoint_fpt )(const GrVertex *pt ); + +#else + + + +FX_ENTRY void FX_CALL + +grAADrawPoint(const GrVertex *pt ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAADrawPolygon_fpt )(const int nverts, const int ilist[], const GrVertex vlist[] ); + +#else + + + +FX_ENTRY void FX_CALL + +grAADrawPolygon(const int nverts, const int ilist[], const GrVertex vlist[]); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAADrawPolygonVertexList_fpt )(const int nverts, const GrVertex vlist[] ); + +#else + + + +FX_ENTRY void FX_CALL + +grAADrawPolygonVertexList(const int nverts, const GrVertex vlist[]); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grAADrawTriangle_fpt )( const GrVertex *a, const GrVertex *b, const GrVertex *c, FxBool ab_antialias, FxBool bc_antialias, FxBool ca_antialias ); + +#else + + + +FX_ENTRY void FX_CALL + +grAADrawTriangle( + + const GrVertex *a, const GrVertex *b, const GrVertex *c, + + FxBool ab_antialias, FxBool bc_antialias, FxBool ca_antialias + + ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** glide management functions + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grGlideInit_fpt )( void ); + +#else + + + +FX_ENTRY void FX_CALL + +grGlideInit( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grGlideShutdown_fpt )( void ); + +#else + + + +FX_ENTRY void FX_CALL + +grGlideShutdown( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grGlideGetVersion_fpt )( char version[80] ); + +#else + + + +FX_ENTRY void FX_CALL + +grGlideGetVersion( char version[80] ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grGlideGetState_fpt )( GrState *state ); + +#else + + + +FX_ENTRY void FX_CALL + +grGlideGetState( GrState *state ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grGlideSetState_fpt )( const GrState *state ); + +#else + + + +FX_ENTRY void FX_CALL + +grGlideSetState( const GrState *state ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grGlideShamelessPlug_fpt )(const FxBool on ); + +#else + + + +FX_ENTRY void FX_CALL + +grGlideShamelessPlug(const FxBool on); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *grHints_fpt )(GrHint_t hintType, FxU32 hintMask ); + +#else + + + +FX_ENTRY void FX_CALL + +grHints(GrHint_t hintType, FxU32 hintMask); + + + +#endif /* DYNAHEADER */ + + + +#endif /* FX_GLIDE_NO_FUNC_PROTO */ + + + +#ifdef __cplusplus + +} + +#endif + + + +/* The following include has been replaced + + * by the processed text from the header file. + + * #include + + */ + +/* + +** Copyright (c) 1995, 3Dfx Interactive, Inc. + +** All Rights Reserved. + +** + +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; + +** the contents of this file may not be disclosed to third parties, copied or + +** duplicated in any form, in whole or in part, without the prior written + +** permission of 3Dfx Interactive, Inc. + +** + +** RESTRICTED RIGHTS LEGEND: + +** Use, duplication or disclosure by the Government is subject to restrictions + +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data + +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or + +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - + +** rights reserved under the Copyright Laws of the United States. + +** + +** $Header: /cvs/kevlar.net/descent3/Main/lib/linux/dyna_glide.h,v 1.1.1.1 2003/08/26 03:58:12 kevinb Exp $ + +** $Log: dyna_glide.h,v $ +** Revision 1.1.1.1 2003/08/26 03:58:12 kevinb +** initial 1.5 import +** + * + * 2 7/14/99 6:43p Jeff + * change to load module globally + * + * 1 6/22/99 7:02p Jeff + * + * 4 2/19/99 11:49a Jason + * added more resolutions to glide + * + * 3 7/06/98 7:16p Jeff + * LoadGlideDLL no longer windows specific, calls module library + * + * 3 7/06/98 10:46a Jeff + * LoadGlideDLL uses the module library so it isn't Windows specific + * + * 2 3/02/98 5:15p Jason + * added support for dynamically loadable dlls + * + * 1 3/02/98 5:15p Jason + * header file for dynamically loadable dlls + + * + + * 4 3/05/97 9:36p Jdt + + * Removed guFbWriteRegion added guEncodeRLE16 + + * + + * 3 1/16/97 3:45p Dow + + * Embedded fn protos in ifndef FX_GLIDE_NO_FUNC_PROTO + +*/ + + + +/* Glide Utility routines */ + + + +#ifndef __GLIDEUTL_H__ + +#define __GLIDEUTL_H__ + + + +#ifdef __cplusplus + +extern "C" { + +#endif + + + +#ifndef FX_GLIDE_NO_FUNC_PROTO + +/* + +** rendering functions + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guAADrawTriangleWithClip_fpt )( const GrVertex *a, const GrVertex *b, const GrVertex *c ); + +#else + + + +FX_ENTRY void FX_CALL + +guAADrawTriangleWithClip( const GrVertex *a, const GrVertex + + *b, const GrVertex *c); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guDrawTriangleWithClip_fpt )( const GrVertex *a, const GrVertex *b, const GrVertex *c ); + +#else + + + +FX_ENTRY void FX_CALL + +guDrawTriangleWithClip( + + const GrVertex *a, + + const GrVertex *b, + + const GrVertex *c + + ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guDrawPolygonVertexListWithClip_fpt )( int nverts, const GrVertex vlist[] ); + +#else + + + +FX_ENTRY void FX_CALL + +guDrawPolygonVertexListWithClip( int nverts, const GrVertex vlist[] ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** hi-level rendering utility functions + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guAlphaSource_fpt )( GrAlphaSource_t mode ); + +#else + + + +FX_ENTRY void FX_CALL + +guAlphaSource( GrAlphaSource_t mode ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guColorCombineFunction_fpt )( GrColorCombineFnc_t fnc ); + +#else + + + +FX_ENTRY void FX_CALL + +guColorCombineFunction( GrColorCombineFnc_t fnc ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guFbReadRegion_fpt )( const int src_x, const int src_y, const int w, const int h, const void *dst, const int strideInBytes ); + +#else + + + +FX_ENTRY void FX_CALL + +guFbReadRegion( + + const int src_x, const int src_y, + + const int w, const int h, const void *dst, + + const int strideInBytes + + ); + + + +#endif /* DYNAHEADER */ + + + + + +#ifdef DYNAHEADER + +typedef int( FX_CALL *guEncodeRLE16_fpt )( void *dst, void *src, FxU32 width, FxU32 height ); + +#else + + + +FX_ENTRY int FX_CALL + +guEncodeRLE16( void *dst, + + void *src, + + FxU32 width, + + FxU32 height ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxU16 *( FX_CALL *guTexCreateColorMipMap_fpt )( void ); + +#else + + + +FX_ENTRY FxU16 * FX_CALL + +guTexCreateColorMipMap( void ); + + + +#endif /* DYNAHEADER */ + + + +#if ( GLIDE_PLATFORM & GLIDE_SST_SIM ) + +/* + +** movie capture stuff + +*/ + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guMovieStart_fpt )( void ); + +#else + + + +FX_ENTRY void + +FX_CALL guMovieStart( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guMovieStop_fpt )( void ); + +#else + + + +FX_ENTRY void + +FX_CALL guMovieStop( void ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guMovieSetName_fpt )( const char *name ); + +#else + + + +FX_ENTRY void + +FX_CALL guMovieSetName( const char *name ); + + + +#endif /* DYNAHEADER */ + +#endif + + + +/* + +** fog stuff + +*/ + +#ifdef DYNAHEADER + +typedef float( FX_CALL *guFogTableIndexToW_fpt )( int i ); + +#else + + + +FX_ENTRY float FX_CALL + +guFogTableIndexToW( int i ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guFogGenerateExp_fpt )( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); + +#else + + + +FX_ENTRY void FX_CALL + +guFogGenerateExp( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guFogGenerateExp2_fpt )( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); + +#else + + + +FX_ENTRY void FX_CALL + +guFogGenerateExp2( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef void( FX_CALL *guFogGenerateLinear_fpt )( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float nearZ, float farZ ); + +#else + + + +FX_ENTRY void FX_CALL + +guFogGenerateLinear( + + GrFog_t fogtable[GR_FOG_TABLE_SIZE], + + float nearZ, float farZ ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** endian stuff + +*/ + +#ifdef DYNAHEADER + +typedef FxU32( FX_CALL *guEndianSwapWords_fpt )( FxU32 value ); + +#else + + + +FX_ENTRY FxU32 FX_CALL + +guEndianSwapWords( FxU32 value ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxU16( FX_CALL *guEndianSwapBytes_fpt )( FxU16 value ); + +#else + + + +FX_ENTRY FxU16 FX_CALL + +guEndianSwapBytes( FxU16 value ); + + + +#endif /* DYNAHEADER */ + + + +/* + +** hi-level texture manipulation tools. + +*/ + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *gu3dfGetInfo_fpt )( const char *filename, Gu3dfInfo *info ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +gu3dfGetInfo( const char *filename, Gu3dfInfo *info ); + + + +#endif /* DYNAHEADER */ + + + +#ifdef DYNAHEADER + +typedef FxBool( FX_CALL *gu3dfLoad_fpt )( const char *filename, Gu3dfInfo *data ); + +#else + + + +FX_ENTRY FxBool FX_CALL + +gu3dfLoad( const char *filename, Gu3dfInfo *data ); + + + +#endif /* DYNAHEADER */ + + + +#endif /* FX_GLIDE_NO_FUNC_PROTO */ + + + +/* + +** Glide structure-less primitive handling + +*/ + +#define GU_PRIM_MAX_VERTICES 100 + + + +/* GMT: BUG what the hell is this!!! */ + +#if 0 + +extern GrVertex __gu_prim_vertex_array[GU_PRIM_MAX_VERTICES]; + +extern GrVertex * __gu_prim_current_vtx; + +extern int __gu_prim_num_verts; + + + +#define guPolygonBegin() __gu_prim_current_vtx = &__gu_prim_vertex_array[0]; __gu_prim_num_verts = 0; + +#define guPolygonEnd() + + + +#ifdef GLIDE_DEBUG + +# define guPolygonDraw() grDrawPolygonVertexList( __gu_prim_num_verts, __gu_prim_vertex_array ); + +# define guPolygonDrawClipped() guDrawPolygonVertexListWithClip( __gu_prim_num_verts, __gu_prim_vertex_array ); + +#else + +# define guPolygonDraw() if ( __gu_prim_num_verts > GU_VTX_MAX_VERTICES ) \ + + grErrorCallback( "guPolygonEnd (GLIDE.H) : GU_VTX_MAX_VERTICES exceed", FXTRUE ); \ + + if ( __gu_prim_num_verts < 3 ) \ + + grErrorCallback( "guPolygonEnd (GLIDE.H) : num_verts< 3", FXTRUE ); \ + + grDrawPolygonVertexList( __gu_prim_num_verts, __gu_prim_vertex_array ); + +# define guPolygonDrawClipped() if ( __gu_prim_num_verts > GU_VTX_MAX_VERTICES ) \ + + grErrorCallback( "guPolygonEnd (GLIDE.H) : GU_VTX_MAX_VERTICES exceed", FXTRUE ); \ + + if ( __gu_prim_num_verts < 3 ) \ + + grErrorCallback( "guPolygonEnd (GLIDE.H) : num_verts< 3", FXTRUE ); \ + + guDrawPolygonVertexListWithClip( __gu_prim_num_verts, __gu_prim_vertex_array ); + +#endif + + + +#define guVertexBegin() + +#define guVertexXY( x, y ) __gu_prim_current_vtx->x = x; __gu_prim_current_vtx->y = y; + +#define guVertexXYZ( x, y, z ) __gu_prim_current_vtx->x = x; __gu_prim_current_vtx->y = y; __gu_prim_current_vtx->ooz = z; + +#define guVertexXYW( x, y, w ) __gu_prim_current_vtx->x = x; __gu_prim_current_vtx->y = y; __gu_prim_current_vtx->oow = w; + +#define guVertexRGB( r, g, b ) __gu_prim_current_vtx->r = r; __gu_prim_current_vtx->g = g; __gu_prim_current_vtx->b = b; + +#define guVertexRGBA( r, g, b, a ) __gu_prim_current_vtx->r = r; __gu_prim_current_vtx->g = g; __gu_prim_current_vtx->b = b; __gu_prim_current_vtx->a = a; + +#define guVertexA( a ) __gu_prim_current_vtx->a = a; + +#define guVertexSTW0( c, s, t, w ) __gu_prim_current_vtx->tmuvtx[(c)]->sow = s; \ + + __gu_prim_current_vtx->tmuvtx[(c)]->tow = t; \ + + __gu_prim_current_vtx->tmuvtx[(c)]->oow = w; + +#define guVertexEnd() __gu_prim_current_vtx++; __gu_prim_num_verts++; + + + +#endif + + + +#ifdef __cplusplus + +} + +#endif + + + +#endif /* __GLIDEUTL_H__ */ + + + +#ifdef DYNAHEADER_CREATE_STORAGE + +#include "module.h" +static module glideDLLInst; + +ConvertAndDownloadRle_fpt ConvertAndDownloadRle=NULL; + +grAADrawLine_fpt grAADrawLine=NULL; + +grAADrawPoint_fpt grAADrawPoint=NULL; + +grAADrawPolygon_fpt grAADrawPolygon=NULL; + +grAADrawPolygonVertexList_fpt grAADrawPolygonVertexList=NULL; + +grAADrawTriangle_fpt grAADrawTriangle=NULL; + +grAlphaBlendFunction_fpt grAlphaBlendFunction=NULL; + +grAlphaCombine_fpt grAlphaCombine=NULL; + +grAlphaControlsITRGBLighting_fpt grAlphaControlsITRGBLighting=NULL; + +grAlphaTestFunction_fpt grAlphaTestFunction=NULL; + +grAlphaTestReferenceValue_fpt grAlphaTestReferenceValue=NULL; + +grBufferClear_fpt grBufferClear=NULL; + +grBufferNumPending_fpt grBufferNumPending=NULL; + +grBufferSwap_fpt grBufferSwap=NULL; + +grCheckForRoom_fpt grCheckForRoom=NULL; + +grChromakeyMode_fpt grChromakeyMode=NULL; + +grChromakeyValue_fpt grChromakeyValue=NULL; + +grClipWindow_fpt grClipWindow=NULL; + +grColorCombine_fpt grColorCombine=NULL; + +grColorMask_fpt grColorMask=NULL; + +grConstantColorValue_fpt grConstantColorValue=NULL; + +grConstantColorValue4_fpt grConstantColorValue4=NULL; + +grCullMode_fpt grCullMode=NULL; + +grDepthBiasLevel_fpt grDepthBiasLevel=NULL; + +grDepthBufferFunction_fpt grDepthBufferFunction=NULL; + +grDepthBufferMode_fpt grDepthBufferMode=NULL; + +grDepthMask_fpt grDepthMask=NULL; + +grDisableAllEffects_fpt grDisableAllEffects=NULL; + +grDitherMode_fpt grDitherMode=NULL; + +grDrawLine_fpt grDrawLine=NULL; + +grDrawPlanarPolygon_fpt grDrawPlanarPolygon=NULL; + +grDrawPlanarPolygonVertexList_fpt grDrawPlanarPolygonVertexList=NULL; + +grDrawPoint_fpt grDrawPoint=NULL; + +grDrawPolygon_fpt grDrawPolygon=NULL; + +grDrawPolygonVertexList_fpt grDrawPolygonVertexList=NULL; + +grDrawTriangle_fpt grDrawTriangle=NULL; + +grErrorSetCallback_fpt grErrorSetCallback=NULL; + +grFogColorValue_fpt grFogColorValue=NULL; + +grFogMode_fpt grFogMode=NULL; + +grFogTable_fpt grFogTable=NULL; + +grGammaCorrectionValue_fpt grGammaCorrectionValue=NULL; + +grGlideGetState_fpt grGlideGetState=NULL; + +grGlideGetVersion_fpt grGlideGetVersion=NULL; + +grGlideInit_fpt grGlideInit=NULL; + +grGlideSetState_fpt grGlideSetState=NULL; + +grGlideShamelessPlug_fpt grGlideShamelessPlug=NULL; + +grGlideShutdown_fpt grGlideShutdown=NULL; + +grHints_fpt grHints=NULL; + +grLfbConstantAlpha_fpt grLfbConstantAlpha=NULL; + +grLfbConstantDepth_fpt grLfbConstantDepth=NULL; + +grLfbLock_fpt grLfbLock=NULL; + +grLfbReadRegion_fpt grLfbReadRegion=NULL; + +grLfbUnlock_fpt grLfbUnlock=NULL; + +grLfbWriteColorFormat_fpt grLfbWriteColorFormat=NULL; + +grLfbWriteColorSwizzle_fpt grLfbWriteColorSwizzle=NULL; + +grLfbWriteRegion_fpt grLfbWriteRegion=NULL; + +grRenderBuffer_fpt grRenderBuffer=NULL; + +grResetTriStats_fpt grResetTriStats=NULL; + +grSplash_fpt grSplash=NULL; + +grSstControl_fpt grSstControl=NULL; + +grSstIdle_fpt grSstIdle=NULL; + +grSstIsBusy_fpt grSstIsBusy=NULL; + +grSstOrigin_fpt grSstOrigin=NULL; + +grSstPerfStats_fpt grSstPerfStats=NULL; + +grSstQueryBoards_fpt grSstQueryBoards=NULL; + +grSstQueryHardware_fpt grSstQueryHardware=NULL; + +grSstResetPerfStats_fpt grSstResetPerfStats=NULL; + +grSstScreenHeight_fpt grSstScreenHeight=NULL; + +grSstScreenWidth_fpt grSstScreenWidth=NULL; + +grSstSelect_fpt grSstSelect=NULL; + +grSstStatus_fpt grSstStatus=NULL; + +grSstVRetraceOn_fpt grSstVRetraceOn=NULL; + +grSstVideoLine_fpt grSstVideoLine=NULL; + +grSstWinClose_fpt grSstWinClose=NULL; + +grSstWinOpen_fpt grSstWinOpen=NULL; + +grTexCalcMemRequired_fpt grTexCalcMemRequired=NULL; + +grTexClampMode_fpt grTexClampMode=NULL; + +grTexCombine_fpt grTexCombine=NULL; + +grTexCombineFunction_fpt grTexCombineFunction=NULL; + +grTexDetailControl_fpt grTexDetailControl=NULL; + +grTexDownloadMipMap_fpt grTexDownloadMipMap=NULL; + +grTexDownloadMipMapLevel_fpt grTexDownloadMipMapLevel=NULL; + +grTexDownloadMipMapLevelPartial_fpt grTexDownloadMipMapLevelPartial=NULL; + +grTexDownloadTable_fpt grTexDownloadTable=NULL; + +grTexDownloadTablePartial_fpt grTexDownloadTablePartial=NULL; + +grTexFilterMode_fpt grTexFilterMode=NULL; + +grTexLodBiasValue_fpt grTexLodBiasValue=NULL; + +grTexMaxAddress_fpt grTexMaxAddress=NULL; + +grTexMinAddress_fpt grTexMinAddress=NULL; + +grTexMipMapMode_fpt grTexMipMapMode=NULL; + +grTexMultibase_fpt grTexMultibase=NULL; + +grTexMultibaseAddress_fpt grTexMultibaseAddress=NULL; + +grTexNCCTable_fpt grTexNCCTable=NULL; + +grTexSource_fpt grTexSource=NULL; + +grTexTextureMemRequired_fpt grTexTextureMemRequired=NULL; + +grTriStats_fpt grTriStats=NULL; + +gu3dfGetInfo_fpt gu3dfGetInfo=NULL; + +gu3dfLoad_fpt gu3dfLoad=NULL; + +guAADrawTriangleWithClip_fpt guAADrawTriangleWithClip=NULL; + +guAlphaSource_fpt guAlphaSource=NULL; + +guColorCombineFunction_fpt guColorCombineFunction=NULL; + +guDrawPolygonVertexListWithClip_fpt guDrawPolygonVertexListWithClip=NULL; + +guDrawTriangleWithClip_fpt guDrawTriangleWithClip=NULL; + +guEncodeRLE16_fpt guEncodeRLE16=NULL; + +guEndianSwapBytes_fpt guEndianSwapBytes=NULL; + +guEndianSwapWords_fpt guEndianSwapWords=NULL; +guFbReadRegion_fpt guFbReadRegion=NULL; +guFogGenerateExp_fpt guFogGenerateExp=NULL; +guFogGenerateExp2_fpt guFogGenerateExp2=NULL; +guFogGenerateLinear_fpt guFogGenerateLinear=NULL; +guFogTableIndexToW_fpt guFogTableIndexToW=NULL; +guTexAllocateMemory_fpt guTexAllocateMemory=NULL; +guTexChangeAttributes_fpt guTexChangeAttributes=NULL; +guTexCombineFunction_fpt guTexCombineFunction=NULL; +guTexCreateColorMipMap_fpt guTexCreateColorMipMap=NULL; +guTexDownloadMipMap_fpt guTexDownloadMipMap=NULL; +guTexDownloadMipMapLevel_fpt guTexDownloadMipMapLevel=NULL; +guTexGetCurrentMipMap_fpt guTexGetCurrentMipMap=NULL; +guTexGetMipMapInfo_fpt guTexGetMipMapInfo=NULL; +guTexMemQueryAvail_fpt guTexMemQueryAvail=NULL; +guTexMemReset_fpt guTexMemReset=NULL; +guTexSource_fpt guTexSource=NULL; + +module *LoadGlideDLL() +{ + if(!mod_LoadModule(&glideDLLInst,"libglide2x.so",MODF_LAZY|MODF_GLOBAL)){ + int err = mod_GetLastError(); + return NULL; + } + +#if defined(WIN32) + ConvertAndDownloadRle = (ConvertAndDownloadRle_fpt)mod_GetSymbol(&glideDLLInst,"ConvertAndDownloadRle",64); + + if(! ConvertAndDownloadRle) goto glide_load_dll_error; +#endif + grAADrawLine = (grAADrawLine_fpt)mod_GetSymbol(&glideDLLInst,"grAADrawLine",8); + + if(! grAADrawLine) goto glide_load_dll_error; + + grAADrawPoint = (grAADrawPoint_fpt)mod_GetSymbol(&glideDLLInst,"grAADrawPoint",4); + + if(! grAADrawPoint) goto glide_load_dll_error; + + grAADrawPolygon = (grAADrawPolygon_fpt)mod_GetSymbol(&glideDLLInst,"grAADrawPolygon",12); + + if(! grAADrawPolygon) goto glide_load_dll_error; + + grAADrawPolygonVertexList = (grAADrawPolygonVertexList_fpt)mod_GetSymbol(&glideDLLInst,"grAADrawPolygonVertexList",8); + + if(! grAADrawPolygonVertexList) goto glide_load_dll_error; + + grAADrawTriangle = (grAADrawTriangle_fpt)mod_GetSymbol(&glideDLLInst,"grAADrawTriangle",24); + + if(! grAADrawTriangle) goto glide_load_dll_error; + + grAlphaBlendFunction = (grAlphaBlendFunction_fpt)mod_GetSymbol(&glideDLLInst,"grAlphaBlendFunction",16); + + if(! grAlphaBlendFunction) goto glide_load_dll_error; + + grAlphaCombine = (grAlphaCombine_fpt)mod_GetSymbol(&glideDLLInst,"grAlphaCombine",20); + + if(! grAlphaCombine) goto glide_load_dll_error; + + grAlphaControlsITRGBLighting = (grAlphaControlsITRGBLighting_fpt)mod_GetSymbol(&glideDLLInst,"grAlphaControlsITRGBLighting",4); + + if(! grAlphaControlsITRGBLighting) goto glide_load_dll_error; + + grAlphaTestFunction = (grAlphaTestFunction_fpt)mod_GetSymbol(&glideDLLInst,"grAlphaTestFunction",4); + + if(! grAlphaTestFunction) goto glide_load_dll_error; + + grAlphaTestReferenceValue = (grAlphaTestReferenceValue_fpt)mod_GetSymbol(&glideDLLInst,"grAlphaTestReferenceValue",4); + + if(! grAlphaTestReferenceValue) goto glide_load_dll_error; + + grBufferClear = (grBufferClear_fpt)mod_GetSymbol(&glideDLLInst,"grBufferClear",12); + + if(! grBufferClear) goto glide_load_dll_error; + + grBufferNumPending = (grBufferNumPending_fpt)mod_GetSymbol(&glideDLLInst,"grBufferNumPending",0); + + if(! grBufferNumPending) goto glide_load_dll_error; + + grBufferSwap = (grBufferSwap_fpt)mod_GetSymbol(&glideDLLInst,"grBufferSwap",4); + + if(! grBufferSwap) goto glide_load_dll_error; + + grCheckForRoom = (grCheckForRoom_fpt)mod_GetSymbol(&glideDLLInst,"grCheckForRoom",4); + + if(! grCheckForRoom) goto glide_load_dll_error; + + grChromakeyMode = (grChromakeyMode_fpt)mod_GetSymbol(&glideDLLInst,"grChromakeyMode",4); + + if(! grChromakeyMode) goto glide_load_dll_error; + + grChromakeyValue = (grChromakeyValue_fpt)mod_GetSymbol(&glideDLLInst,"grChromakeyValue",4); + + if(! grChromakeyValue) goto glide_load_dll_error; + + grClipWindow = (grClipWindow_fpt)mod_GetSymbol(&glideDLLInst,"grClipWindow",16); + + if(! grClipWindow) goto glide_load_dll_error; + + grColorCombine = (grColorCombine_fpt)mod_GetSymbol(&glideDLLInst,"grColorCombine",20); + + if(! grColorCombine) goto glide_load_dll_error; + + grColorMask = (grColorMask_fpt)mod_GetSymbol(&glideDLLInst,"grColorMask",8); + + if(! grColorMask) goto glide_load_dll_error; + + grConstantColorValue = (grConstantColorValue_fpt)mod_GetSymbol(&glideDLLInst,"grConstantColorValue",4); + + if(! grConstantColorValue) goto glide_load_dll_error; + + grConstantColorValue4 = (grConstantColorValue4_fpt)mod_GetSymbol(&glideDLLInst,"grConstantColorValue4",16); + + if(! grConstantColorValue4) goto glide_load_dll_error; + + grCullMode = (grCullMode_fpt)mod_GetSymbol(&glideDLLInst,"grCullMode",4); + + if(! grCullMode) goto glide_load_dll_error; + + grDepthBiasLevel = (grDepthBiasLevel_fpt)mod_GetSymbol(&glideDLLInst,"grDepthBiasLevel",4); + + if(! grDepthBiasLevel) goto glide_load_dll_error; + + grDepthBufferFunction = (grDepthBufferFunction_fpt)mod_GetSymbol(&glideDLLInst,"grDepthBufferFunction",4); + + if(! grDepthBufferFunction) goto glide_load_dll_error; + + grDepthBufferMode = (grDepthBufferMode_fpt)mod_GetSymbol(&glideDLLInst,"grDepthBufferMode",4); + + if(! grDepthBufferMode) goto glide_load_dll_error; + + grDepthMask = (grDepthMask_fpt)mod_GetSymbol(&glideDLLInst,"grDepthMask",4); + + if(! grDepthMask) goto glide_load_dll_error; + + grDisableAllEffects = (grDisableAllEffects_fpt)mod_GetSymbol(&glideDLLInst,"grDisableAllEffects",0); + + if(! grDisableAllEffects) goto glide_load_dll_error; + + grDitherMode = (grDitherMode_fpt)mod_GetSymbol(&glideDLLInst,"grDitherMode",4); + + if(! grDitherMode) goto glide_load_dll_error; + + grDrawLine = (grDrawLine_fpt)mod_GetSymbol(&glideDLLInst,"grDrawLine",8); + + if(! grDrawLine) goto glide_load_dll_error; + + grDrawPlanarPolygon = (grDrawPlanarPolygon_fpt)mod_GetSymbol(&glideDLLInst,"grDrawPlanarPolygon",12); + + if(! grDrawPlanarPolygon) goto glide_load_dll_error; + + grDrawPlanarPolygonVertexList = (grDrawPlanarPolygonVertexList_fpt)mod_GetSymbol(&glideDLLInst,"grDrawPlanarPolygonVertexList",8); + + if(! grDrawPlanarPolygonVertexList) goto glide_load_dll_error; + + grDrawPoint = (grDrawPoint_fpt)mod_GetSymbol(&glideDLLInst,"grDrawPoint",4); + + if(! grDrawPoint) goto glide_load_dll_error; + + grDrawPolygon = (grDrawPolygon_fpt)mod_GetSymbol(&glideDLLInst,"grDrawPolygon",12); + + if(! grDrawPolygon) goto glide_load_dll_error; + + grDrawPolygonVertexList = (grDrawPolygonVertexList_fpt)mod_GetSymbol(&glideDLLInst,"grDrawPolygonVertexList",8); + + if(! grDrawPolygonVertexList) goto glide_load_dll_error; + + grDrawTriangle = (grDrawTriangle_fpt)mod_GetSymbol(&glideDLLInst,"grDrawTriangle",12); + + if(! grDrawTriangle) goto glide_load_dll_error; + + grErrorSetCallback = (grErrorSetCallback_fpt)mod_GetSymbol(&glideDLLInst,"grErrorSetCallback",4); + + if(! grErrorSetCallback) goto glide_load_dll_error; + + grFogColorValue = (grFogColorValue_fpt)mod_GetSymbol(&glideDLLInst,"grFogColorValue",4); + + if(! grFogColorValue) goto glide_load_dll_error; + + grFogMode = (grFogMode_fpt)mod_GetSymbol(&glideDLLInst,"grFogMode",4); + + if(! grFogMode) goto glide_load_dll_error; + + grFogTable = (grFogTable_fpt)mod_GetSymbol(&glideDLLInst,"grFogTable",4); + + if(! grFogTable) goto glide_load_dll_error; + + grGammaCorrectionValue = (grGammaCorrectionValue_fpt)mod_GetSymbol(&glideDLLInst,"grGammaCorrectionValue",4); + + if(! grGammaCorrectionValue) goto glide_load_dll_error; + + grGlideGetState = (grGlideGetState_fpt)mod_GetSymbol(&glideDLLInst,"grGlideGetState",4); + + if(! grGlideGetState) goto glide_load_dll_error; + + grGlideGetVersion = (grGlideGetVersion_fpt)mod_GetSymbol(&glideDLLInst,"grGlideGetVersion",4); + + if(! grGlideGetVersion) goto glide_load_dll_error; + + grGlideInit = (grGlideInit_fpt)mod_GetSymbol(&glideDLLInst,"grGlideInit",0); + + if(! grGlideInit) goto glide_load_dll_error; + + grGlideSetState = (grGlideSetState_fpt)mod_GetSymbol(&glideDLLInst,"grGlideSetState",4); + + if(! grGlideSetState) goto glide_load_dll_error; + + grGlideShamelessPlug = (grGlideShamelessPlug_fpt)mod_GetSymbol(&glideDLLInst,"grGlideShamelessPlug",4); + + if(! grGlideShamelessPlug) goto glide_load_dll_error; + + grGlideShutdown = (grGlideShutdown_fpt)mod_GetSymbol(&glideDLLInst,"grGlideShutdown",0); + + if(! grGlideShutdown) goto glide_load_dll_error; + + grHints = (grHints_fpt)mod_GetSymbol(&glideDLLInst,"grHints",8); + + if(! grHints) goto glide_load_dll_error; + + grLfbConstantAlpha = (grLfbConstantAlpha_fpt)mod_GetSymbol(&glideDLLInst,"grLfbConstantAlpha",4); + + if(! grLfbConstantAlpha) goto glide_load_dll_error; + + grLfbConstantDepth = (grLfbConstantDepth_fpt)mod_GetSymbol(&glideDLLInst,"grLfbConstantDepth",4); + + if(! grLfbConstantDepth) goto glide_load_dll_error; + + grLfbLock = (grLfbLock_fpt)mod_GetSymbol(&glideDLLInst,"grLfbLock",24); + + if(! grLfbLock) goto glide_load_dll_error; + + grLfbReadRegion = (grLfbReadRegion_fpt)mod_GetSymbol(&glideDLLInst,"grLfbReadRegion",28); + + if(! grLfbReadRegion) goto glide_load_dll_error; + + grLfbUnlock = (grLfbUnlock_fpt)mod_GetSymbol(&glideDLLInst,"grLfbUnlock",8); + + if(! grLfbUnlock) goto glide_load_dll_error; + + grLfbWriteColorFormat = (grLfbWriteColorFormat_fpt)mod_GetSymbol(&glideDLLInst,"grLfbWriteColorFormat",4); + + if(! grLfbWriteColorFormat) goto glide_load_dll_error; + + grLfbWriteColorSwizzle = (grLfbWriteColorSwizzle_fpt)mod_GetSymbol(&glideDLLInst,"grLfbWriteColorSwizzle",8); + + if(! grLfbWriteColorSwizzle) goto glide_load_dll_error; + + grLfbWriteRegion = (grLfbWriteRegion_fpt)mod_GetSymbol(&glideDLLInst,"grLfbWriteRegion",32); + + if(! grLfbWriteRegion) goto glide_load_dll_error; + + grRenderBuffer = (grRenderBuffer_fpt)mod_GetSymbol(&glideDLLInst,"grRenderBuffer",4); + + if(! grRenderBuffer) goto glide_load_dll_error; + + grResetTriStats = (grResetTriStats_fpt)mod_GetSymbol(&glideDLLInst,"grResetTriStats",0); + + if(! grResetTriStats) goto glide_load_dll_error; + + grSplash = (grSplash_fpt)mod_GetSymbol(&glideDLLInst,"grSplash",20); + + if(! grSplash) goto glide_load_dll_error; + + grSstControl = (grSstControl_fpt)mod_GetSymbol(&glideDLLInst,"grSstControl",4); + + if(! grSstControl) goto glide_load_dll_error; + + grSstIdle = (grSstIdle_fpt)mod_GetSymbol(&glideDLLInst,"grSstIdle",0); + + if(! grSstIdle) goto glide_load_dll_error; + + grSstIsBusy = (grSstIsBusy_fpt)mod_GetSymbol(&glideDLLInst,"grSstIsBusy",0); + + if(! grSstIsBusy) goto glide_load_dll_error; + + grSstOrigin = (grSstOrigin_fpt)mod_GetSymbol(&glideDLLInst,"grSstOrigin",4); + + if(! grSstOrigin) goto glide_load_dll_error; + + grSstPerfStats = (grSstPerfStats_fpt)mod_GetSymbol(&glideDLLInst,"grSstPerfStats",4); + + if(! grSstPerfStats) goto glide_load_dll_error; + + grSstQueryBoards = (grSstQueryBoards_fpt)mod_GetSymbol(&glideDLLInst,"grSstQueryBoards",4); + + if(! grSstQueryBoards) goto glide_load_dll_error; + + grSstQueryHardware = (grSstQueryHardware_fpt)mod_GetSymbol(&glideDLLInst,"grSstQueryHardware",4); + + if(! grSstQueryHardware) goto glide_load_dll_error; + + grSstResetPerfStats = (grSstResetPerfStats_fpt)mod_GetSymbol(&glideDLLInst,"grSstResetPerfStats",0); + + if(! grSstResetPerfStats) goto glide_load_dll_error; + + grSstScreenHeight = (grSstScreenHeight_fpt)mod_GetSymbol(&glideDLLInst,"grSstScreenHeight",0); + + if(! grSstScreenHeight) goto glide_load_dll_error; + + grSstScreenWidth = (grSstScreenWidth_fpt)mod_GetSymbol(&glideDLLInst,"grSstScreenWidth",0); + + if(! grSstScreenWidth) goto glide_load_dll_error; + + grSstSelect = (grSstSelect_fpt)mod_GetSymbol(&glideDLLInst,"grSstSelect",4); + + if(! grSstSelect) goto glide_load_dll_error; + + grSstStatus = (grSstStatus_fpt)mod_GetSymbol(&glideDLLInst,"grSstStatus",0); + + if(! grSstStatus) goto glide_load_dll_error; + + grSstVRetraceOn = (grSstVRetraceOn_fpt)mod_GetSymbol(&glideDLLInst,"grSstVRetraceOn",0); + + if(! grSstVRetraceOn) goto glide_load_dll_error; + + grSstVideoLine = (grSstVideoLine_fpt)mod_GetSymbol(&glideDLLInst,"grSstVideoLine",0); + + if(! grSstVideoLine) goto glide_load_dll_error; + + grSstWinClose = (grSstWinClose_fpt)mod_GetSymbol(&glideDLLInst,"grSstWinClose",0); + + if(! grSstWinClose) goto glide_load_dll_error; + + grSstWinOpen = (grSstWinOpen_fpt)mod_GetSymbol(&glideDLLInst,"grSstWinOpen",28); + + if(! grSstWinOpen) goto glide_load_dll_error; + + grTexCalcMemRequired = (grTexCalcMemRequired_fpt)mod_GetSymbol(&glideDLLInst,"grTexCalcMemRequired",16); + + if(! grTexCalcMemRequired) goto glide_load_dll_error; + + grTexClampMode = (grTexClampMode_fpt)mod_GetSymbol(&glideDLLInst,"grTexClampMode",12); + + if(! grTexClampMode) goto glide_load_dll_error; + + grTexCombine = (grTexCombine_fpt)mod_GetSymbol(&glideDLLInst,"grTexCombine",28); + + if(! grTexCombine) goto glide_load_dll_error; + + grTexCombineFunction = (grTexCombineFunction_fpt)mod_GetSymbol(&glideDLLInst,"grTexCombineFunction",8); + + if(! grTexCombineFunction) goto glide_load_dll_error; + + grTexDetailControl = (grTexDetailControl_fpt)mod_GetSymbol(&glideDLLInst,"grTexDetailControl",16); + + if(! grTexDetailControl) goto glide_load_dll_error; + + grTexDownloadMipMap = (grTexDownloadMipMap_fpt)mod_GetSymbol(&glideDLLInst,"grTexDownloadMipMap",16); + + if(! grTexDownloadMipMap) goto glide_load_dll_error; + + grTexDownloadMipMapLevel = (grTexDownloadMipMapLevel_fpt)mod_GetSymbol(&glideDLLInst,"grTexDownloadMipMapLevel",32); + + if(! grTexDownloadMipMapLevel) goto glide_load_dll_error; + + grTexDownloadMipMapLevelPartial = (grTexDownloadMipMapLevelPartial_fpt)mod_GetSymbol(&glideDLLInst,"grTexDownloadMipMapLevelPartial",40); + + if(! grTexDownloadMipMapLevelPartial) goto glide_load_dll_error; + + grTexDownloadTable = (grTexDownloadTable_fpt)mod_GetSymbol(&glideDLLInst,"grTexDownloadTable",12); + + if(! grTexDownloadTable) goto glide_load_dll_error; + + grTexDownloadTablePartial = (grTexDownloadTablePartial_fpt)mod_GetSymbol(&glideDLLInst,"grTexDownloadTablePartial",20); + + if(! grTexDownloadTablePartial) goto glide_load_dll_error; + + grTexFilterMode = (grTexFilterMode_fpt)mod_GetSymbol(&glideDLLInst,"grTexFilterMode",12); + + if(! grTexFilterMode) goto glide_load_dll_error; + + grTexLodBiasValue = (grTexLodBiasValue_fpt)mod_GetSymbol(&glideDLLInst,"grTexLodBiasValue",8); + + if(! grTexLodBiasValue) goto glide_load_dll_error; + + grTexMaxAddress = (grTexMaxAddress_fpt)mod_GetSymbol(&glideDLLInst,"grTexMaxAddress",4); + + if(! grTexMaxAddress) goto glide_load_dll_error; + + grTexMinAddress = (grTexMinAddress_fpt)mod_GetSymbol(&glideDLLInst,"grTexMinAddress",4); + + if(! grTexMinAddress) goto glide_load_dll_error; + + grTexMipMapMode = (grTexMipMapMode_fpt)mod_GetSymbol(&glideDLLInst,"grTexMipMapMode",12); + + if(! grTexMipMapMode) goto glide_load_dll_error; + + grTexMultibase = (grTexMultibase_fpt)mod_GetSymbol(&glideDLLInst,"grTexMultibase",8); + + if(! grTexMultibase) goto glide_load_dll_error; + + grTexMultibaseAddress = (grTexMultibaseAddress_fpt)mod_GetSymbol(&glideDLLInst,"grTexMultibaseAddress",20); + + if(! grTexMultibaseAddress) goto glide_load_dll_error; + + grTexNCCTable = (grTexNCCTable_fpt)mod_GetSymbol(&glideDLLInst,"grTexNCCTable",8); + + if(! grTexNCCTable) goto glide_load_dll_error; + + grTexSource = (grTexSource_fpt)mod_GetSymbol(&glideDLLInst,"grTexSource",16); + + if(! grTexSource) goto glide_load_dll_error; + + grTexTextureMemRequired = (grTexTextureMemRequired_fpt)mod_GetSymbol(&glideDLLInst,"grTexTextureMemRequired",8); + + if(! grTexTextureMemRequired) goto glide_load_dll_error; + + grTriStats = (grTriStats_fpt)mod_GetSymbol(&glideDLLInst,"grTriStats",8); + + if(! grTriStats) goto glide_load_dll_error; + + gu3dfGetInfo = (gu3dfGetInfo_fpt)mod_GetSymbol(&glideDLLInst,"gu3dfGetInfo",8); + + if(! gu3dfGetInfo) goto glide_load_dll_error; + + gu3dfLoad = (gu3dfLoad_fpt)mod_GetSymbol(&glideDLLInst,"gu3dfLoad",8); + + if(! gu3dfLoad) goto glide_load_dll_error; + + guAADrawTriangleWithClip = (guAADrawTriangleWithClip_fpt)mod_GetSymbol(&glideDLLInst,"guAADrawTriangleWithClip",12); + + if(! guAADrawTriangleWithClip) goto glide_load_dll_error; + + guAlphaSource = (guAlphaSource_fpt)mod_GetSymbol(&glideDLLInst,"guAlphaSource",4); + + if(! guAlphaSource) goto glide_load_dll_error; + + guColorCombineFunction = (guColorCombineFunction_fpt)mod_GetSymbol(&glideDLLInst,"guColorCombineFunction",4); + + if(! guColorCombineFunction) goto glide_load_dll_error; + + guDrawPolygonVertexListWithClip = (guDrawPolygonVertexListWithClip_fpt)mod_GetSymbol(&glideDLLInst,"guDrawPolygonVertexListWithClip",8); + + if(! guDrawPolygonVertexListWithClip) goto glide_load_dll_error; + + guDrawTriangleWithClip = (guDrawTriangleWithClip_fpt)mod_GetSymbol(&glideDLLInst,"guDrawTriangleWithClip",12); + + if(! guDrawTriangleWithClip) goto glide_load_dll_error; + + guEncodeRLE16 = (guEncodeRLE16_fpt)mod_GetSymbol(&glideDLLInst,"guEncodeRLE16",16); + + if(! guEncodeRLE16) goto glide_load_dll_error; + + guEndianSwapBytes = (guEndianSwapBytes_fpt)mod_GetSymbol(&glideDLLInst,"guEndianSwapBytes",4); + + if(! guEndianSwapBytes) goto glide_load_dll_error; + + guEndianSwapWords = (guEndianSwapWords_fpt)mod_GetSymbol(&glideDLLInst,"guEndianSwapWords",4); + + if(! guEndianSwapWords) goto glide_load_dll_error; + + guFogGenerateExp = (guFogGenerateExp_fpt)mod_GetSymbol(&glideDLLInst,"guFogGenerateExp",8); + + if(! guFogGenerateExp) goto glide_load_dll_error; + + guFogGenerateExp2 = (guFogGenerateExp2_fpt)mod_GetSymbol(&glideDLLInst,"guFogGenerateExp2",8); + + if(! guFogGenerateExp2) goto glide_load_dll_error; + + guFogGenerateLinear = (guFogGenerateLinear_fpt)mod_GetSymbol(&glideDLLInst,"guFogGenerateLinear",12); + + if(! guFogGenerateLinear) goto glide_load_dll_error; + + guFogTableIndexToW = (guFogTableIndexToW_fpt)mod_GetSymbol(&glideDLLInst,"guFogTableIndexToW",4); + + if(! guFogTableIndexToW) goto glide_load_dll_error; + + guTexAllocateMemory = (guTexAllocateMemory_fpt)mod_GetSymbol(&glideDLLInst,"guTexAllocateMemory",60); + + if(! guTexAllocateMemory) goto glide_load_dll_error; + + guTexChangeAttributes = (guTexChangeAttributes_fpt)mod_GetSymbol(&glideDLLInst,"guTexChangeAttributes",48); + + if(! guTexChangeAttributes) goto glide_load_dll_error; + + guTexCombineFunction = (guTexCombineFunction_fpt)mod_GetSymbol(&glideDLLInst,"guTexCombineFunction",8); + + if(! guTexCombineFunction) goto glide_load_dll_error; + + guTexCreateColorMipMap = (guTexCreateColorMipMap_fpt)mod_GetSymbol(&glideDLLInst,"guTexCreateColorMipMap",0); + + if(! guTexCreateColorMipMap) goto glide_load_dll_error; + + guTexDownloadMipMap = (guTexDownloadMipMap_fpt)mod_GetSymbol(&glideDLLInst,"guTexDownloadMipMap",12); + + if(! guTexDownloadMipMap) goto glide_load_dll_error; + + guTexDownloadMipMapLevel = (guTexDownloadMipMapLevel_fpt)mod_GetSymbol(&glideDLLInst,"guTexDownloadMipMapLevel",12); + + if(! guTexDownloadMipMapLevel) goto glide_load_dll_error; + + guTexGetCurrentMipMap = (guTexGetCurrentMipMap_fpt)mod_GetSymbol(&glideDLLInst,"guTexGetCurrentMipMap",4); + + if(! guTexGetCurrentMipMap) goto glide_load_dll_error; + + guTexGetMipMapInfo = (guTexGetMipMapInfo_fpt)mod_GetSymbol(&glideDLLInst,"guTexGetMipMapInfo",4); + + if(! guTexGetMipMapInfo) goto glide_load_dll_error; + + guTexMemQueryAvail = (guTexMemQueryAvail_fpt)mod_GetSymbol(&glideDLLInst,"guTexMemQueryAvail",4); + + if(! guTexMemQueryAvail) goto glide_load_dll_error; + + guTexMemReset = (guTexMemReset_fpt)mod_GetSymbol(&glideDLLInst,"guTexMemReset",0); + + if(! guTexMemReset) goto glide_load_dll_error; + + guTexSource = (guTexSource_fpt)mod_GetSymbol(&glideDLLInst,"guTexSource",4); + + if(! guTexSource) goto glide_load_dll_error; + + return &glideDLLInst; + +glide_load_dll_error: + mod_FreeModule(&glideDLLInst); + return NULL; +} +/* #endif DYNAHEADER_CREATE_STORAGE */ + +#elif DYNAHEADER + +extern ConvertAndDownloadRle_fpt ConvertAndDownloadRle; + +extern grAADrawLine_fpt grAADrawLine; + +extern grAADrawPoint_fpt grAADrawPoint; + +extern grAADrawPolygon_fpt grAADrawPolygon; + +extern grAADrawPolygonVertexList_fpt grAADrawPolygonVertexList; + +extern grAADrawTriangle_fpt grAADrawTriangle; + +extern grAlphaBlendFunction_fpt grAlphaBlendFunction; + +extern grAlphaCombine_fpt grAlphaCombine; + +extern grAlphaControlsITRGBLighting_fpt grAlphaControlsITRGBLighting; + +extern grAlphaTestFunction_fpt grAlphaTestFunction; + +extern grAlphaTestReferenceValue_fpt grAlphaTestReferenceValue; + +extern grBufferClear_fpt grBufferClear; + +extern grBufferNumPending_fpt grBufferNumPending; + +extern grBufferSwap_fpt grBufferSwap; + +extern grCheckForRoom_fpt grCheckForRoom; + +extern grChromakeyMode_fpt grChromakeyMode; + +extern grChromakeyValue_fpt grChromakeyValue; + +extern grClipWindow_fpt grClipWindow; + +extern grColorCombine_fpt grColorCombine; + +extern grColorMask_fpt grColorMask; + +extern grConstantColorValue_fpt grConstantColorValue; + +extern grConstantColorValue4_fpt grConstantColorValue4; + +extern grCullMode_fpt grCullMode; + +extern grDepthBiasLevel_fpt grDepthBiasLevel; + +extern grDepthBufferFunction_fpt grDepthBufferFunction; + +extern grDepthBufferMode_fpt grDepthBufferMode; + +extern grDepthMask_fpt grDepthMask; + +extern grDisableAllEffects_fpt grDisableAllEffects; + +extern grDitherMode_fpt grDitherMode; + +extern grDrawLine_fpt grDrawLine; + +extern grDrawPlanarPolygon_fpt grDrawPlanarPolygon; + +extern grDrawPlanarPolygonVertexList_fpt grDrawPlanarPolygonVertexList; + +extern grDrawPoint_fpt grDrawPoint; + +extern grDrawPolygon_fpt grDrawPolygon; + +extern grDrawPolygonVertexList_fpt grDrawPolygonVertexList; + +extern grDrawTriangle_fpt grDrawTriangle; + +extern grErrorSetCallback_fpt grErrorSetCallback; + +extern grFogColorValue_fpt grFogColorValue; + +extern grFogMode_fpt grFogMode; + +extern grFogTable_fpt grFogTable; + +extern grGammaCorrectionValue_fpt grGammaCorrectionValue; + +extern grGlideGetState_fpt grGlideGetState; + +extern grGlideGetVersion_fpt grGlideGetVersion; + +extern grGlideInit_fpt grGlideInit; + +extern grGlideSetState_fpt grGlideSetState; + +extern grGlideShamelessPlug_fpt grGlideShamelessPlug; + +extern grGlideShutdown_fpt grGlideShutdown; + +extern grHints_fpt grHints; + +extern grLfbConstantAlpha_fpt grLfbConstantAlpha; + +extern grLfbConstantDepth_fpt grLfbConstantDepth; + +extern grLfbLock_fpt grLfbLock; + +extern grLfbReadRegion_fpt grLfbReadRegion; + +extern grLfbUnlock_fpt grLfbUnlock; + +extern grLfbWriteColorFormat_fpt grLfbWriteColorFormat; + +extern grLfbWriteColorSwizzle_fpt grLfbWriteColorSwizzle; + +extern grLfbWriteRegion_fpt grLfbWriteRegion; + +extern grRenderBuffer_fpt grRenderBuffer; + +extern grResetTriStats_fpt grResetTriStats; + +extern grSplash_fpt grSplash; + +extern grSstControl_fpt grSstControl; + +extern grSstIdle_fpt grSstIdle; + +extern grSstIsBusy_fpt grSstIsBusy; + +extern grSstOrigin_fpt grSstOrigin; + +extern grSstPerfStats_fpt grSstPerfStats; + +extern grSstQueryBoards_fpt grSstQueryBoards; + +extern grSstQueryHardware_fpt grSstQueryHardware; + +extern grSstResetPerfStats_fpt grSstResetPerfStats; + +extern grSstScreenHeight_fpt grSstScreenHeight; + +extern grSstScreenWidth_fpt grSstScreenWidth; + +extern grSstSelect_fpt grSstSelect; + +extern grSstStatus_fpt grSstStatus; + +extern grSstVRetraceOn_fpt grSstVRetraceOn; + +extern grSstVideoLine_fpt grSstVideoLine; + +extern grSstWinClose_fpt grSstWinClose; + +extern grSstWinOpen_fpt grSstWinOpen; + +extern grTexCalcMemRequired_fpt grTexCalcMemRequired; + +extern grTexClampMode_fpt grTexClampMode; + +extern grTexCombine_fpt grTexCombine; + +extern grTexCombineFunction_fpt grTexCombineFunction; + +extern grTexDetailControl_fpt grTexDetailControl; + +extern grTexDownloadMipMap_fpt grTexDownloadMipMap; + +extern grTexDownloadMipMapLevel_fpt grTexDownloadMipMapLevel; + +extern grTexDownloadMipMapLevelPartial_fpt grTexDownloadMipMapLevelPartial; + +extern grTexDownloadTable_fpt grTexDownloadTable; + +extern grTexDownloadTablePartial_fpt grTexDownloadTablePartial; + +extern grTexFilterMode_fpt grTexFilterMode; + +extern grTexLodBiasValue_fpt grTexLodBiasValue; + +extern grTexMaxAddress_fpt grTexMaxAddress; + +extern grTexMinAddress_fpt grTexMinAddress; + +extern grTexMipMapMode_fpt grTexMipMapMode; + +extern grTexMultibase_fpt grTexMultibase; + +extern grTexMultibaseAddress_fpt grTexMultibaseAddress; + +extern grTexNCCTable_fpt grTexNCCTable; + +extern grTexSource_fpt grTexSource; + +extern grTexTextureMemRequired_fpt grTexTextureMemRequired; + +extern grTriStats_fpt grTriStats; + +extern gu3dfGetInfo_fpt gu3dfGetInfo; + +extern gu3dfLoad_fpt gu3dfLoad; + +extern guAADrawTriangleWithClip_fpt guAADrawTriangleWithClip; + +extern guAlphaSource_fpt guAlphaSource; + +extern guColorCombineFunction_fpt guColorCombineFunction; + +extern guDrawPolygonVertexListWithClip_fpt guDrawPolygonVertexListWithClip; + +extern guDrawTriangleWithClip_fpt guDrawTriangleWithClip; + +extern guEncodeRLE16_fpt guEncodeRLE16; + +extern guEndianSwapBytes_fpt guEndianSwapBytes; + +extern guEndianSwapWords_fpt guEndianSwapWords; + +extern guFbReadRegion_fpt guFbReadRegion; + +extern guFogGenerateExp_fpt guFogGenerateExp; + +extern guFogGenerateExp2_fpt guFogGenerateExp2; + +extern guFogGenerateLinear_fpt guFogGenerateLinear; + +extern guFogTableIndexToW_fpt guFogTableIndexToW; + +extern guMovieSetName_fpt guMovieSetName; + +extern guMovieStart_fpt guMovieStart; + +extern guMovieStop_fpt guMovieStop; + +extern guTexAllocateMemory_fpt guTexAllocateMemory; + +extern guTexChangeAttributes_fpt guTexChangeAttributes; + +extern guTexCombineFunction_fpt guTexCombineFunction; + +extern guTexCreateColorMipMap_fpt guTexCreateColorMipMap; + +extern guTexDownloadMipMap_fpt guTexDownloadMipMap; + +extern guTexDownloadMipMapLevel_fpt guTexDownloadMipMapLevel; + +extern guTexGetCurrentMipMap_fpt guTexGetCurrentMipMap; + +extern guTexGetMipMapInfo_fpt guTexGetMipMapInfo; + +extern guTexMemQueryAvail_fpt guTexMemQueryAvail; + +extern guTexMemReset_fpt guTexMemReset; + +extern guTexSource_fpt guTexSource; + +#include "module.h" +extern module glideDLLInst = {NULL}; + +#endif /* DYNAHEADER */ + +#endif /* __GLIDE_H__ */ + diff --git a/lib/linux/dyna_oxlib.h b/lib/linux/dyna_oxlib.h new file mode 100644 index 000000000..499eff69a --- /dev/null +++ b/lib/linux/dyna_oxlib.h @@ -0,0 +1,83 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include +#include +#include + +#define XF86DGAQueryVersion DGAQueryVersion +#define XF86DGAQueryExtension DGAQueryExtension +#define XF86DGADirectVideo DGADirectVideo + +typedef Bool (*DGAQueryVersion_fp)(Display* pa,int* pb,int* pc); +FEXTERN DGAQueryVersion_fp DGAQueryVersion; + +typedef Bool (*DGAQueryExtension_fp)(Display* pa,int* pb,int* pc); +FEXTERN DGAQueryExtension_fp DGAQueryExtension; + +typedef Status (*DGADirectVideo_fp)(Display* pa,int pb,int pc); +FEXTERN DGADirectVideo_fp DGADirectVideo; + +#ifndef DECLARE_POINTERS +bool LoadOutrageXWindowsLib(bool load = true); +#else +#include +#include +#include +void LoadOutrageXWindowsLibSetNULL(void) +{ + DGAQueryVersion = NULL; + DGAQueryExtension = NULL; + DGADirectVideo = NULL; +} + +bool LoadOutrageXWindowsLib(bool load) +{ +#define XWINDOWSEXTLIB "OutrageDynXLib.so" + static void *handle = NULL; + + if(!load) + { + LoadOutrageXWindowsLibSetNULL(); + if(handle) + { + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",XWINDOWSEXTLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(XWINDOWSEXTLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",XWINDOWSEXTLIB); + return false; + } + + DGAQueryVersion = (DGAQueryVersion_fp)dlsym(handle,"DGAQueryVersion"); + if(!DGAQueryVersion) goto load_error; + + DGAQueryExtension = (DGAQueryExtension_fp)dlsym(handle,"DGAQueryExtension"); + if(!DGAQueryExtension) goto load_error; + + DGADirectVideo = (DGADirectVideo_fp)dlsym(handle,"DGADirectVideo"); + if(!DGADirectVideo) goto load_error; + + return true; + +load_error: + LoadOutrageXWindowsLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",XWINDOWSEXTLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lib/linux/dyna_pthread.h b/lib/linux/dyna_pthread.h new file mode 100644 index 000000000..2b59d2ab4 --- /dev/null +++ b/lib/linux/dyna_pthread.h @@ -0,0 +1,89 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include + +#define pthread_create sopthread_create +#define pthread_exit sopthread_exit +#define pthread_detach sopthread_detach +#define pthread_self sopthread_self + +typedef int (*pthread_create_fp)(pthread_t *__threadx,__const pthread_attr_t *__attr,void *(*__start_routine) (void *),void *__arg); +FEXTERN pthread_create_fp sopthread_create; + +typedef void (*pthread_exit_fp)(void *__retval); +FEXTERN pthread_exit_fp sopthread_exit; + +typedef int (*pthread_detach_fp)(pthread_t __th); +FEXTERN pthread_detach_fp sopthread_detach; + +typedef pthread_t (*pthread_self_fp)(void); +FEXTERN pthread_self_fp sopthread_self; + +#ifndef DECLARE_POINTERS +bool LoadPThreadLib(bool load = true); +#else +#include +#include +#include +void LoadPThreadLibSetNULL(void) +{ + sopthread_create = NULL; + sopthread_exit = NULL; + sopthread_detach = NULL; + sopthread_self = NULL; +} + +bool LoadPThreadLib(bool load) +{ +#define PTHREADLIB "libpthread.so" + static void *handle = NULL; + + if(!load) + { + LoadPThreadLibSetNULL(); + if(handle) + { + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",PTHREADLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(PTHREADLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",PTHREADLIB); + return false; + } + + sopthread_create = (pthread_create_fp)dlsym(handle,"pthread_create"); + if(!sopthread_create) goto load_error; + + sopthread_exit = (pthread_exit_fp)dlsym(handle,"pthread_exit"); + if(!sopthread_exit) goto load_error; + + sopthread_detach = (pthread_detach_fp)dlsym(handle,"pthread_detach"); + if(!sopthread_detach) goto load_error; + + sopthread_self = (pthread_self_fp)dlsym(handle,"pthread_self"); + if(!sopthread_self) goto load_error; + + return true; + +load_error: + LoadPThreadLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",PTHREADLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif diff --git a/lib/linux/dyna_svga.h b/lib/linux/dyna_svga.h new file mode 100644 index 000000000..d55319a5d --- /dev/null +++ b/lib/linux/dyna_svga.h @@ -0,0 +1,91 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include +#include + +//remapping +#define keyboard_init sokeyboard_init +#define keyboard_seteventhandler sokeyboard_seteventhandler +#define keyboard_close sokeyboard_close +#define keyboard_update sokeyboard_update + +typedef int (*keyboard_init_fp)(void); +FEXTERN keyboard_init_fp sokeyboard_init; + +typedef void (*keyboard_seteventhandler_fp)(__keyboard_handler handler); +FEXTERN keyboard_seteventhandler_fp sokeyboard_seteventhandler; + +typedef void (*keyboard_close_fp)(void); +FEXTERN keyboard_close_fp sokeyboard_close; + +typedef int (*keyboard_update_fp)(void); +FEXTERN keyboard_update_fp sokeyboard_update; + +#ifndef DECLARE_POINTERS +bool LoadSVGALib(bool load = true); +#else +#include +#include +#include +void LoadSVGALibSetNULL(void) +{ + sokeyboard_init = NULL; + sokeyboard_seteventhandler = NULL; + sokeyboard_close = NULL; + sokeyboard_update = NULL; +} + +bool LoadSVGALib(bool load) +{ +#define SVGALIB "libvga.so" + static void *handle = NULL; + + if(!load) + { + LoadSVGALibSetNULL(); + if(handle) + { + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",SVGALIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(SVGALIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",SVGALIB); + return false; + } + + sokeyboard_init = (keyboard_init_fp)dlsym(handle,"keyboard_init"); + if(!sokeyboard_init) goto load_error; + + sokeyboard_seteventhandler = (keyboard_seteventhandler_fp)dlsym(handle,"keyboard_seteventhandler"); + if(!sokeyboard_seteventhandler) goto load_error; + + sokeyboard_close = (keyboard_close_fp)dlsym(handle,"keyboard_close"); + if(!sokeyboard_close) goto load_error; + + sokeyboard_update = (keyboard_update_fp)dlsym(handle,"keyboard_update"); + if(!sokeyboard_update) goto load_error; + + return true; + +load_error: + LoadSVGALibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",SVGALIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lib/linux/dyna_xext.h b/lib/linux/dyna_xext.h new file mode 100644 index 000000000..47cf605d9 --- /dev/null +++ b/lib/linux/dyna_xext.h @@ -0,0 +1,99 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include +#include +#include + +#define XShmAttach soXShmAttach +#define XShmCreateImage soXShmCreateImage +#define XShmDetach soXShmDetach +#define XShmPutImage soXShmPutImage +#define XShmQueryExtension soXShmQueryExtension + +typedef Status (*XShmAttach_fp)(Display*,XShmSegmentInfo*); +FEXTERN XShmAttach_fp soXShmAttach; + +typedef XImage *(*XShmCreateImage_fp)(Display*,Visual*,unsigned int,int,char*,XShmSegmentInfo*,unsigned int,unsigned int); +FEXTERN XShmCreateImage_fp soXShmCreateImage; + +typedef Status (*XShmDetach_fp)(Display*,XShmSegmentInfo*); +FEXTERN XShmDetach_fp soXShmDetach; + +typedef Status (*XShmPutImage_fp)(Display*,Drawable,GC,XImage*,int,int,int,int,unsigned int,unsigned int,Bool); +FEXTERN XShmPutImage_fp soXShmPutImage; + +typedef Bool (*XShmQueryExtension_fp)(Display*); +FEXTERN XShmQueryExtension_fp soXShmQueryExtension; + +#ifndef DECLARE_POINTERS +bool LoadXWindowsExtLib(bool load = true); +#else +#include +#include +#include +void LoadXWindowsExtLibSetNULL(void) +{ + soXShmAttach = NULL; + soXShmCreateImage = NULL; + soXShmDetach = NULL; + soXShmPutImage = NULL; + soXShmQueryExtension = NULL; +} + +bool LoadXWindowsExtLib(bool load) +{ +#define XWINDOWSEXTLIB "libXext.so" + static void *handle = NULL; + + if(!load) + { + LoadXWindowsExtLibSetNULL(); + if(handle) + { + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",XWINDOWSEXTLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(XWINDOWSEXTLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",XWINDOWSEXTLIB); + return false; + } + + soXShmAttach = (XShmAttach_fp)dlsym(handle,"XShmAttach"); + if(!soXShmAttach) goto load_error; + + soXShmCreateImage = (XShmCreateImage_fp)dlsym(handle,"XShmCreateImage"); + if(!soXShmCreateImage) goto load_error; + + soXShmDetach = (XShmDetach_fp)dlsym(handle,"XShmDetach"); + if(!soXShmDetach) goto load_error; + + soXShmPutImage = (XShmPutImage_fp)dlsym(handle,"XShmPutImage"); + if(!soXShmPutImage) goto load_error; + + soXShmQueryExtension = (XShmQueryExtension_fp)dlsym(handle,"XShmQueryExtension"); + if(!soXShmQueryExtension) goto load_error; + + return true; + +load_error: + LoadXWindowsExtLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",XWINDOWSEXTLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lib/linux/dyna_xwin.h b/lib/linux/dyna_xwin.h new file mode 100644 index 000000000..513a422ae --- /dev/null +++ b/lib/linux/dyna_xwin.h @@ -0,0 +1,431 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include +#include + +#define XAllocClassHint soXAllocClassHint +#define XAllocSizeHints soXAllocSizeHints +#define XAllocWMHints soXAllocWMHints +#define XCloseDisplay soXCloseDisplay +#define XCreateGC soXCreateGC +#define XCreateImage soXCreateImage +#define XCreatePixmap soXCreatePixmap +#define XCreatePixmapCursor soXCreatePixmapCursor +#define XCreateWindow soXCreateWindow +#define XDefineCursor soXDefineCursor +#define XFillRectangle soXFillRectangle +#define XFreeGC soXFreeGC +#define XFreePixmap soXFreePixmap +#define XGrabPointer soXGrabPointer +#define XKeycodeToKeysym soXKeycodeToKeysym +#define XCheckMaskEvent soXCheckMaskEvent +#define XLookupString soXLookupString +#define XMapWindow soXMapWindow +#define XMatchVisualInfo soXMatchVisualInfo +#define XNextEvent soXNextEvent +#define XOpenDisplay soXOpenDisplay +#define XPutImage soXPutImage +#define XSetIconName soXSetIconName +#define XSetWMNormalHints soXSetWMNormalHints +#define XStoreName soXStoreName +#define XSync soXSync +#define XWarpPointer soXWarpPointer +#define XAutoRepeatOn soXAutoRepeatOn +#define XAutoRepeatOff soXAutoRepeatOff +#define XGetWindowProperty soXGetWindowProperty +#define XQueryTree soXQueryTree +#define XInternAtom soXInternAtom +#define XLowerWindow soXLowerWindow +#define XRaiseWindow soXRaiseWindow +#define XChangeProperty soXChangeProperty +#define XChangeWindowAttributes soXChangeWindowAttributes +#define XMoveResizeWindow soXMoveResizeWindow +#define XUnmapWindow soXUnmapWindow +#define XDestroyWindow soXDestroyWindow +#define XFree soXFree +#define XGetWMNormalHints soXGetWMNormalHints +#define XSetWMProtocols soXSetWMProtocols +#define XSetWMHints soXSetWMHints +#define XSetStandardProperties soXSetStandardProperties +#define XFlush soXFlush +#define XMaskEvent soXMaskEvent + +typedef XClassHint *(*XAllocClassHint_fp)(void); +FEXTERN XAllocClassHint_fp soXAllocClassHint; + +typedef XSizeHints *(*XAllocSizeHints_fp)(void); +FEXTERN XAllocSizeHints_fp soXAllocSizeHints; + +typedef XWMHints *(*XAllocWMHints_fp)(void); +FEXTERN XAllocWMHints_fp soXAllocWMHints; + +typedef int (*XCloseDisplay_fp)(Display *); +FEXTERN XCloseDisplay_fp soXCloseDisplay; + +typedef GC (*XCreateGC_fp)(Display*,Drawable,unsigned long,XGCValues*); +FEXTERN XCreateGC_fp soXCreateGC; + +typedef XImage *(*XCreateImage_fp)(Display*,Visual*,unsigned int,int,int,char*,unsigned int,unsigned int,int,int); +FEXTERN XCreateImage_fp soXCreateImage; + +typedef Pixmap (*XCreatePixmap_fp)(Display*,Drawable,unsigned int,unsigned int,unsigned int); +FEXTERN XCreatePixmap_fp soXCreatePixmap; + +typedef Cursor (*XCreatePixmapCursor_fp)(Display*,Pixmap,Pixmap,XColor*,XColor*,unsigned int,unsigned int); +FEXTERN XCreatePixmapCursor_fp soXCreatePixmapCursor; + +typedef Window (*XCreateWindow_fp)(Display *,Window,int,int,unsigned int,unsigned int,unsigned int,int,unsigned int,Visual *,unsigned long,XSetWindowAttributes *); +FEXTERN XCreateWindow_fp soXCreateWindow; + +typedef int (*XDefineCursor_fp)(Display *,Window,Cursor); +FEXTERN XDefineCursor_fp soXDefineCursor; + +typedef int (*XFillRectangle_fp)(Display*,Drawable,GC,int,int,unsigned int,unsigned int); +FEXTERN XFillRectangle_fp soXFillRectangle; + +typedef int (*XFreeGC_fp)(Display*,GC); +FEXTERN XFreeGC_fp soXFreeGC; + +typedef int (*XFreePixmap_fp)(Display*,Pixmap); +FEXTERN XFreePixmap_fp soXFreePixmap; + +typedef int (*XGrabPointer_fp)(Display *,Window,Bool,unsigned int,int,int,Window,Cursor,Time); +FEXTERN XGrabPointer_fp soXGrabPointer; + +typedef KeySym (*XKeycodeToKeysym_fp)(Display *, KeyCode, int); +FEXTERN XKeycodeToKeysym_fp soXKeycodeToKeysym; + +typedef Bool (*XCheckMaskEvent_fp)(Display *, long, XEvent *); +FEXTERN XCheckMaskEvent_fp soXCheckMaskEvent; + +typedef int (*XLookupString_fp)(XKeyEvent *, char *,int, KeySym, XComposeStatus *); +FEXTERN XLookupString_fp soXLookupString; + +typedef int (*XMapWindow_fp)(Display *,Window); +FEXTERN XMapWindow_fp soXMapWindow; + +typedef Status (*XMatchVisualInfo_fp)(Display *,int,int,int,XVisualInfo *); +FEXTERN XMatchVisualInfo_fp soXMatchVisualInfo; + +typedef int (*XNextEvent_fp)(Display *,XEvent *); +FEXTERN XNextEvent_fp soXNextEvent; + +typedef Display *(*XOpenDisplay_fp)(_Xconst char *); +FEXTERN XOpenDisplay_fp soXOpenDisplay; + +typedef int (*XPutImage_fp)(Display*,Drawable,GC,XImage*,int,int,int,int,unsigned int,unsigned int); +FEXTERN XPutImage_fp soXPutImage; + +typedef int (*XSetIconName_fp)(Display *,Window,_Xconst char *); +FEXTERN XSetIconName_fp soXSetIconName; + +typedef void (*XSetWMNormalHints_fp)(Display *,Window,XSizeHints *); +FEXTERN XSetWMNormalHints_fp soXSetWMNormalHints; + +typedef int (*XStoreName_fp)(Display *,Window,_Xconst char *); +FEXTERN XStoreName_fp soXStoreName; + +typedef int (*XSync_fp)(Display *,Bool); +FEXTERN XSync_fp soXSync; + +typedef int (*XWarpPointer_fp)(Display*,Window,Window,int,int,unsigned int,unsigned int,int,int); +FEXTERN XWarpPointer_fp soXWarpPointer; + +typedef int (*XAutoRepeatOn_fp)(Display*); +FEXTERN XAutoRepeatOn_fp soXAutoRepeatOn; + +typedef int (*XAutoRepeatOff_fp)(Display*); +FEXTERN XAutoRepeatOff_fp soXAutoRepeatOff; + +typedef int (*XGetWindowProperty_fp)(Display*,Window,Atom,long,long,Bool,Atom,Atom*,int*,unsigned long*,unsigned long*,unsigned char**); +FEXTERN XGetWindowProperty_fp soXGetWindowProperty; + +typedef Status (*XQueryTree_fp)(Display*,Window,Window*,Window*,Window**,unsigned int*); +FEXTERN XQueryTree_fp soXQueryTree; + +typedef Atom (*XInternAtom_fp)(Display*,_Xconst char*,Bool); +FEXTERN XInternAtom_fp soXInternAtom; + +typedef int (*XLowerWindow_fp)(Display*,Window); +FEXTERN XLowerWindow_fp soXLowerWindow; + +typedef int (*XRaiseWindow_fp)(Display*,Window); +FEXTERN XRaiseWindow_fp soXRaiseWindow; + +typedef int (*XChangeProperty_fp)(Display*,Window,Atom,Atom,int,int,_Xconst unsigned char*,int); +FEXTERN XChangeProperty_fp soXChangeProperty; + +typedef int (*XChangeWindowAttributes_fp)(Display*,Window,unsigned long,XSetWindowAttributes*); +FEXTERN XChangeWindowAttributes_fp soXChangeWindowAttributes; + +typedef int (*XMoveResizeWindow_fp)(Display*,Window,int,int,unsigned int,unsigned int); +FEXTERN XMoveResizeWindow_fp soXMoveResizeWindow; + +typedef int (*XUnmapWindow_fp)(Display*,Window); +FEXTERN XUnmapWindow_fp soXUnmapWindow; + +typedef int (*XDestroyWindow_fp)(Display*,Window); +FEXTERN XDestroyWindow_fp soXDestroyWindow; + +typedef int (*XFree_fp)(void*); +FEXTERN XFree_fp soXFree; + +typedef Status (*XGetWMNormalHints_fp)(Display*,Window,XSizeHints*,long*); +FEXTERN XGetWMNormalHints_fp soXGetWMNormalHints; + +typedef Status (*XSetWMProtocols_fp)(Display*,Window,Atom*,int); +FEXTERN XSetWMProtocols_fp soXSetWMProtocols; + +typedef int (*XSetWMHints_fp)(Display*,Window,XWMHints*); +FEXTERN XSetWMHints_fp soXSetWMHints; + +typedef int (*XSetStandardProperties_fp)(Display*,Window,_Xconst char*,_Xconst char*,Pixmap,char**,int,XSizeHints*); +FEXTERN XSetStandardProperties_fp soXSetStandardProperties; + +typedef int (*XFlush_fp)(Display*); +FEXTERN XFlush_fp soXFlush; + +typedef int (*XMaskEvent_fp)(Display*,long,XEvent*); +FEXTERN XMaskEvent_fp soXMaskEvent; + +#ifndef DECLARE_POINTERS +bool LoadXWindowsLib(bool load = true); +#else +#include +#include +#include + +void LoadXWindowsLibSetNULL(void) +{ + soXAllocClassHint = NULL; + soXAllocSizeHints = NULL; + soXAllocWMHints = NULL; + soXCloseDisplay = NULL; + soXCreateGC = NULL; + soXCreateImage = NULL; + soXCreatePixmap = NULL; + soXCreatePixmapCursor = NULL; + soXCreateWindow = NULL; + soXDefineCursor = NULL; + soXFillRectangle = NULL; + soXFreeGC = NULL; + soXFreePixmap = NULL; + soXGrabPointer = NULL; + soXLookupString = NULL; + soXMapWindow = NULL; + soXMatchVisualInfo = NULL; + soXNextEvent = NULL; + soXOpenDisplay = NULL; + soXPutImage = NULL; + soXSetIconName = NULL; + soXSetWMNormalHints = NULL; + soXStoreName = NULL; + soXSync = NULL; + soXWarpPointer = NULL; + soXCheckMaskEvent = NULL; + soXKeycodeToKeysym = NULL; + soXLookupString = NULL; + soXAutoRepeatOn = NULL; + soXAutoRepeatOff = NULL; + soXGetWindowProperty = NULL; + soXQueryTree = NULL; + soXInternAtom = NULL; + soXLowerWindow = NULL; + soXRaiseWindow = NULL; + soXChangeProperty = NULL; + soXChangeWindowAttributes = NULL; + soXMoveResizeWindow = NULL; + soXUnmapWindow = NULL; + soXDestroyWindow = NULL; + soXFree = NULL; + soXGetWMNormalHints = NULL; + soXSetWMProtocols = NULL; + soXSetWMHints = NULL; + soXSetStandardProperties = NULL; + soXFlush = NULL; + soXMaskEvent = NULL; +} + +bool LoadXWindowsLib(bool load) +{ +#define XWINDOWSLIB "libX11.so" + static void *handle = NULL; + + if(!load) + { + if(handle) + { + LoadXWindowsLibSetNULL(); + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",XWINDOWSLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(XWINDOWSLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",XWINDOWSLIB); + return false; + } + + soXAllocClassHint = (XAllocClassHint_fp)dlsym(handle,"XAllocClassHint"); + if(!soXAllocClassHint) goto load_error; + + soXAllocSizeHints = (XAllocSizeHints_fp)dlsym(handle,"XAllocSizeHints"); + if(!soXAllocSizeHints) goto load_error; + + soXAllocWMHints = (XAllocWMHints_fp)dlsym(handle,"XAllocWMHints"); + if(!soXAllocWMHints) goto load_error; + + soXCloseDisplay = (XCloseDisplay_fp)dlsym(handle,"XCloseDisplay"); + if(!soXCloseDisplay) goto load_error; + + soXCreateGC = (XCreateGC_fp)dlsym(handle,"XCreateGC"); + if(!soXCreateGC) goto load_error; + + soXCreateImage = (XCreateImage_fp)dlsym(handle,"XCreateImage"); + if(!soXCreateImage) goto load_error; + + soXCreatePixmap = (XCreatePixmap_fp)dlsym(handle,"XCreatePixmap"); + if(!soXCreatePixmap) goto load_error; + + soXCreatePixmapCursor = (XCreatePixmapCursor_fp)dlsym(handle,"XCreatePixmapCursor"); + if(!soXCreatePixmapCursor) goto load_error; + + soXCheckMaskEvent = (XCheckMaskEvent_fp)dlsym(handle,"XCheckMaskEvent"); + if(!soXCheckMaskEvent) goto load_error; + + soXKeycodeToKeysym = (XKeycodeToKeysym_fp)dlsym(handle,"XKeycodeToKeysym"); + if(!soXKeycodeToKeysym) goto load_error; + + soXLookupString = (XLookupString_fp)dlsym(handle,"XLookupString"); + if(!soXLookupString) goto load_error; + + soXCreateWindow = (XCreateWindow_fp)dlsym(handle,"XCreateWindow"); + if(!soXCreateWindow) goto load_error; + + soXDefineCursor = (XDefineCursor_fp)dlsym(handle,"XDefineCursor"); + if(!soXDefineCursor) goto load_error; + + soXFillRectangle = (XFillRectangle_fp)dlsym(handle,"XFillRectangle"); + if(!soXFillRectangle) goto load_error; + + soXFreeGC = (XFreeGC_fp)dlsym(handle,"XFreeGC"); + if(!soXFreeGC) goto load_error; + + soXFreePixmap = (XFreePixmap_fp)dlsym(handle,"XFreePixmap"); + if(!soXFreePixmap) goto load_error; + + soXGrabPointer = (XGrabPointer_fp)dlsym(handle,"XGrabPointer"); + if(!soXGrabPointer) goto load_error; + + soXLookupString = (XLookupString_fp)dlsym(handle,"XLookupString"); + if(!soXLookupString) goto load_error; + + soXMapWindow = (XMapWindow_fp)dlsym(handle,"XMapWindow"); + if(!soXMapWindow) goto load_error; + + soXMatchVisualInfo = (XMatchVisualInfo_fp)dlsym(handle,"XMatchVisualInfo"); + if(!soXMatchVisualInfo) goto load_error; + + soXNextEvent = (XNextEvent_fp)dlsym(handle,"XNextEvent"); + if(!soXNextEvent) goto load_error; + + soXOpenDisplay = (XOpenDisplay_fp)dlsym(handle,"XOpenDisplay"); + if(!soXOpenDisplay) goto load_error; + + soXPutImage = (XPutImage_fp)dlsym(handle,"XPutImage"); + if(!soXPutImage) goto load_error; + + soXSetIconName = (XSetIconName_fp)dlsym(handle,"XSetIconName"); + if(!soXSetIconName) goto load_error; + + soXSetWMNormalHints = (XSetWMNormalHints_fp)dlsym(handle,"XSetWMNormalHints"); + if(!soXSetWMNormalHints) goto load_error; + + soXStoreName = (XStoreName_fp)dlsym(handle,"XStoreName"); + if(!soXStoreName) goto load_error; + + soXSync = (XSync_fp)dlsym(handle,"XSync"); + if(!soXSync) goto load_error; + + soXWarpPointer = (XWarpPointer_fp)dlsym(handle,"XWarpPointer"); + if(!soXWarpPointer) goto load_error; + + soXAutoRepeatOn = (XAutoRepeatOn_fp)dlsym(handle, "XAutoRepeatOn"); + if (!soXAutoRepeatOn) goto load_error; + + soXAutoRepeatOff = (XAutoRepeatOff_fp)dlsym(handle, "XAutoRepeatOff"); + if (!soXAutoRepeatOff) goto load_error; + + soXGetWindowProperty = (XGetWindowProperty_fp)dlsym(handle,"XGetWindowProperty"); + if(!soXGetWindowProperty) goto load_error; + + soXQueryTree = (XQueryTree_fp)dlsym(handle,"XQueryTree"); + if(!soXQueryTree) goto load_error; + + soXInternAtom = (XInternAtom_fp)dlsym(handle,"XInternAtom"); + if(!soXInternAtom) goto load_error; + + soXLowerWindow = (XLowerWindow_fp)dlsym(handle,"XLowerWindow"); + if(!soXLowerWindow) goto load_error; + + soXRaiseWindow = (XRaiseWindow_fp)dlsym(handle,"XRaiseWindow"); + if(!soXRaiseWindow) goto load_error; + + soXChangeProperty = (XChangeProperty_fp)dlsym(handle,"XChangeProperty"); + if(!soXChangeProperty) goto load_error; + + soXChangeWindowAttributes = (XChangeWindowAttributes_fp)dlsym(handle,"XChangeWindowAttributes"); + if(!soXChangeWindowAttributes) goto load_error; + + soXMoveResizeWindow = (XMoveResizeWindow_fp)dlsym(handle,"XMoveResizeWindow"); + if(!soXMoveResizeWindow) goto load_error; + + soXUnmapWindow = (XUnmapWindow_fp)dlsym(handle,"XUnmapWindow"); + if(!soXUnmapWindow) goto load_error; + + soXDestroyWindow = (XDestroyWindow_fp)dlsym(handle,"XDestroyWindow"); + if(!soXDestroyWindow) goto load_error; + + soXFree = (XFree_fp)dlsym(handle,"XFree"); + if(!soXFree) goto load_error; + + soXGetWMNormalHints = (XGetWMNormalHints_fp)dlsym(handle,"XGetWMNormalHints"); + if(!soXGetWMNormalHints) goto load_error; + + soXSetWMProtocols = (XSetWMProtocols_fp)dlsym(handle,"XSetWMProtocols"); + if(!soXSetWMProtocols) goto load_error; + + soXSetWMHints = (XSetWMHints_fp)dlsym(handle,"XSetWMHints"); + if(!soXSetWMHints) goto load_error; + + soXSetStandardProperties = (XSetStandardProperties_fp)dlsym(handle,"XSetStandardProperties"); + if(!soXSetStandardProperties) goto load_error; + + soXFlush = (XFlush_fp)dlsym(handle,"XFlush"); + if(!soXFlush) goto load_error; + + soXMaskEvent = (XMaskEvent_fp)dlsym(handle,"XMaskEvent"); + if(!soXMaskEvent) goto load_error; + + return true; + +load_error: + LoadXWindowsLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",XWINDOWSLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lib/linux/linux_fix.h b/lib/linux/linux_fix.h new file mode 100644 index 000000000..57d1b6a26 --- /dev/null +++ b/lib/linux/linux_fix.h @@ -0,0 +1,61 @@ +#ifndef __LINUX_FIX_H_ +#define __LINUX_FIX_H_ + +#include + + +#define LOKI_VERSION "" + + +#define HGLOBAL void* + +void GlobalFree(void *); +void *GlobalAlloc(int flags,int size); +void *GlobalLock(HGLOBAL hMem); +void Sleep(int millis); +char *itoa(int value,char *string,int radix); +char *strupr(char *string); +// Replace missing defines from stdlib.h +#define _MAX_PATH 260 /* max. length of full pathname*/ +#define _MAX_FNAME 256 /* max. length of path component*/ +#define _MAX_EXT 256 /* max. length of extension component*/ +#if !defined(MAX_PATH) +#define MAX_PATH _MAX_PATH +#endif +#define HANDLE int +// _cdecl replacement +#define __cdecl __attribute__((cdecl)) +// _stdcall replacement +#define _stdcall __attribute__((stdcall)) + +inline int _filelength(int fd) +{ + struct stat statbuf; + + if(fstat(fd,&statbuf)!=0) + { + return -1; + } + + return statbuf.st_size; +} + +#ifndef stricmp +#define stricmp(a,b) strcasecmp(a,b) +#endif + +#define strnicmp(a,b,c) strncasecmp(a,b,c) +#define _fstat(a,b) fstat(a,b) +#define _fileno(a) fileno(a) +#define strcmpi(a,b) stricmp(a,b) +#define strcmpni(a,b,c) strnicmp(a,b,c) +#define _chmod(a,b) chmod(a,b) +#define _finite(a) finite(a) +#define _min(a,b) min(a,b) +#define _max(a,b) max(a,b) +#define __min(a,b) min(a,b) +#define __max(a,b) max(a,b) + +#endif + + diff --git a/lib/linux/lnxapp.h b/lib/linux/lnxapp.h new file mode 100644 index 000000000..04762732c --- /dev/null +++ b/lib/linux/lnxapp.h @@ -0,0 +1,79 @@ +#ifndef LNXAPP_H +#define LNXAPP_H + +#include "linux_fix.h" +#include +#include + +// if no-display/input specifier is given, it will use defaults +#define APPFLAG_USESERVICE 0x00000100 //console (run no output/input) +#define APPFLAG_USESVGA 0x00000200 //console (use svgalib for input/output) +#define APPFLAG_NOMOUSECAPTURE 0x00000400 //don't capture the mouse in x-win +#define APPFLAG_NOSHAREDMEMORY 0x00000800 //force no-shared memory +#define APPFLAG_WINDOWEDMODE 0x00001000 //run in a window +#define APPFLAG_DGAMOUSE 0x00002000 //use DGA for Mouse + + +/* Basic Application Linux data types */ + +// This structure is used to retrieve and set +typedef struct tLnxAppInfo { + Display *m_Display; // X-Windows display + Window m_window; // X-Windows window + XVisualInfo m_VisualInfo; // X-Window visual info + unsigned flags; // Application Flags + int wnd_x, wnd_y, wnd_w, wnd_h; // Window dimensions +} tLnxAppInfo; + + +/* Linux Application Object + This object entails initialization and cleanup of all operating system + elements, as well as data that libraries may need to initialize their + systems. + + We also allow the option of setting these handles from outside the Application object. +*/ + +/* Callbacks return a 0 if we don't want to call the default action for the message, otherwise return 1 +*/ +//typedef int (*tOELnxMsgCallback)(HWnd,unsigned,unsigned,unsigned); + +class oeLnxApplication: public oeApplication +{ + bool m_WasCreated; // Tells us if this app set graphics or not. + void (*m_DeferFunc)(bool idle); // function to call when deffering to OS + static bool os_initialized; // is the OS check initialized? + static bool first_time; // first time init? +public: +// Creates the application object + oeLnxApplication(unsigned flags); +// Create object with a premade info + oeLnxApplication(tLnxAppInfo *appinfo); + virtual ~oeLnxApplication(); +// initializes the object + virtual void init(); +// Function to retrieve information from object through a platform defined structure. + virtual void get_info(void *appinfo); +// defer returns some flags. essentially this function defers program control to OS. + virtual unsigned defer(); + virtual char *get_window_name(void); + virtual void clear_window(void); +// set a function to run when deferring to OS. + virtual void set_defer_handler(void (*func)(bool isactive)); +// delays app for a certain amount of time + virtual void delay(float secs); +// Sizes the displayable region of the app (the window) + void set_sizepos(int x, int y, int w, int h); + + virtual int flags(void) const; + + unsigned m_Flags; + int m_X, m_Y, m_W, m_H; // window dimensions. + Display *m_Display; // X-Windows Display + Window m_Window; // X-Windows Window + XVisualInfo m_VisualInfo; // X-Windows Visual Info +private: + void os_init(); // initializes OS components. +}; + +#endif diff --git a/lib/linux/lnxcontroller.h b/lib/linux/lnxcontroller.h new file mode 100644 index 000000000..f30fa2737 --- /dev/null +++ b/lib/linux/lnxcontroller.h @@ -0,0 +1,181 @@ +#ifndef __LNXCONTROLLER_H_ +#define __LNXCONTROLLER_H_ + +#include "Controller.h" +#include "joystick.h" + +#define NULL_LNXCONTROLLER ((sbyte)NULL_CONTROLLER) + +const int CTF_POV = 64, // POV control + CTF_POV2 = 128, // POV 2 + CTF_POV3 = 256, // POV 3 + CTF_POV4 = 512; // POV 4 + +const unsigned CTF_X_AXIS = (1<<(CT_X_AXIS-1)), // AXIS constants for ctAxis + CTF_Y_AXIS = (1<<(CT_Y_AXIS-1)), + CTF_Z_AXIS = (1<<(CT_Z_AXIS-1)), + CTF_R_AXIS = (1<<(CT_R_AXIS-1)), + CTF_U_AXIS = (1<<(CT_U_AXIS-1)), + CTF_V_AXIS = (1<<(CT_V_AXIS-1)); + +const unsigned CT_MAX_CONTROLLERS = 32, + CT_MAX_ELEMENTS = 255, + CT_MAX_EXTCTLS = 16, + CT_MAX_BUTTONS = 32; + +// rules for adding controllers +// any nonstandard special controllers should be added to the below list starting at +// CTID_MOUSE-1 (meaning a value of -3, -4 and so on) + +const int CTID_KEYBOARD = -1, // always -1 for keyboards + CTID_MOUSE = -2, // always -2 for mice + CTID_INVALID = -3; // invalid controller + +// External controls +// these are standard controllers handled through DDIO interface +// like joysticks, etc. + +const int CTID_EXTCONTROL0 = 0; + + +class lnxgameController : public gameController +{ +public: + lnxgameController(int num_funcs, ct_function *funcs); + ~lnxgameController(); + +// these functions suspend or resume any controller reading. this is really only useful for +// preemptive controller polling, but they should be used to activate and deactivate controller +// reading. + virtual void suspend(); + virtual void resume(); + +// this functions polls the controllers if needed. some systems may not need to implement +// this function. + virtual void poll(); + +// flushes all controller information + virtual void flush(); + +// returns the value of a requested controller type. make sure you flush the controller before polling. + virtual ct_config_data get_controller_value(ct_type type_req); + +// sets the configuration of a function (type must be of an array == CTLBINDS_PER_FUNC) + virtual void set_controller_function(int id, const ct_type *type, ct_config_data value, const ubyte *flags); + +// returns information about a requested function (type must be of an array == CTLBINDS_PER_FUNC) + virtual void get_controller_function(int id, ct_type *type, ct_config_data *value, ubyte *flags); + +// temporarily enables or disables a function + virtual void enable_function(int id, bool enable); + +// all systems need to implement this function. this returns information about the controller + virtual bool get_packet(int id, ct_packet *packet, ct_format alt_format=ctNoFormat); + +// gets sensitivity of axis item + virtual float get_axis_sensitivity(ct_type axis_type, ubyte axis); + +// sets sensitivity of axis item + virtual void set_axis_sensitivity(ct_type axis_type, ubyte axis, float val); + +// assigns an individual function + virtual int assign_function(ct_function *fn); + +// activates or deactivates mouse and or controller + virtual void mask_controllers(bool joystick, bool mouse); + +// get raw values for the controllers + virtual int get_mouse_raw_values(int *x, int *y); + virtual unsigned get_joy_raw_values(int *x, int *y); + +// retrieves binding text for desired function, binding, etc. + virtual const char *get_binding_text(ct_type type, ubyte ctrl, ubyte bind); + + //toggles use of deadzone for controllers + void set_controller_deadzone(int ctl,float deadzone); + +private: + int m_NumControls; // number of controllers available + int m_Suspended; // is controller polling suspended? + bool m_JoyActive, m_MouseActive; // enables or disables mouse, joystick control + + struct t_controller { + int id; + ushort flags; + ushort buttons; + unsigned btnmask; + float normalizer[CT_NUM_AXES]; + float sens[CT_NUM_AXES]; + float sensmod[CT_NUM_AXES]; + float deadzone; + } m_ControlList[CT_MAX_CONTROLLERS]; // the control list. + + struct ct_element { + ct_format format; + sbyte ctl[CTLBINDS_PER_FUNC]; + ubyte value[CTLBINDS_PER_FUNC]; + ct_type ctype[CTLBINDS_PER_FUNC]; + ubyte flags[2]; + bool enabled; + } m_ElementList[CT_MAX_ELEMENTS]; + + bool enum_controllers(); + +// sets up an elements information structure + void assign_element(int id, ct_element *elem); + +// this returns an index into the control list. + sbyte get_axis_controller(ubyte axis); + +// returns controller with specified button + sbyte get_button_controller(ubyte btn); + +// returns the controller with a pov hat + sbyte get_pov_controller(ubyte pov); + +// note controller is index into ControlList. + float get_axis_value(sbyte controller, ubyte axis, ct_format format,bool invert = false); + +// get value of button in seconds, presses, etc. + float get_button_value(sbyte controller, ct_format format, ubyte button); + +// get value of pov (using JOYPOV values) + float get_pov_value(sbyte controller, ct_format format, ubyte pov_number,ubyte pov); + +// get keyboard info + float get_key_value(int key, ct_format format); + + void parse_ctl_file(int devnum,const char *ctlname); + +private: + struct t_msestate { + int x, y, z; + int mx,my; + unsigned btnmask; + } m_MseState; + + struct t_extctlstate { + int x,y,z,r,u,v; + int pov[JOYPOV_NUM]; + int last_pov[JOYPOV_NUM]; + float povstarts[JOYPOV_NUM][JOYPOV_DIR]; + float povtimes[JOYPOV_NUM][JOYPOV_DIR]; + ubyte povpresses[JOYPOV_NUM][JOYPOV_DIR]; + unsigned buttons; + ubyte btnpresses[CT_MAX_BUTTONS]; + float btnstarts[CT_MAX_BUTTONS]; + float btntimes[CT_MAX_BUTTONS]; + } m_ExtCtlStates[CT_MAX_EXTCTLS]; + +// thread info. + longlong m_frame_timer_ms; + float m_frame_time; + +// note id is id value from controller in control list. + void extctl_getpos(int id); + +// this gets timings for mouse buttons + void mouse_geteval(); +}; + +#endif \ No newline at end of file diff --git a/lib/linux/lnxdatabase.h b/lib/linux/lnxdatabase.h new file mode 100644 index 000000000..ec652b1a9 --- /dev/null +++ b/lib/linux/lnxdatabase.h @@ -0,0 +1,58 @@ +/* + * Application Database for Linux version + * + * $NoKeywords: $ + */ + +#ifndef LINUXDATABASE +#define LINUXDATABASE + +#include +#include + +class CRegistry; + +#include "Macros.h" + +/* oeLnxAppDatabase + to get info about the application from a managed database (or a custom info file) +*/ + +class oeLnxAppDatabase: public oeAppDatabase +{ + +protected: + CRegistry *database; +public: + oeLnxAppDatabase(); + oeLnxAppDatabase(oeLnxAppDatabase *parent); + virtual ~oeLnxAppDatabase(); + CRegistry *GetSystemRegistry(); + +// creates an empty classification or structure where you can store information + virtual bool create_record(const char *pathname); + +// set current database focus to a particular record + virtual bool lookup_record(const char *pathname); + +// read either an integer or string from the current record + virtual bool read(const char *label, char *entry, int *entrylen); + virtual bool read(const char *label, void *entry, int wordsize); //read a variable-size int + virtual bool read(const char *label, bool *entry); + +// write either an integer or string to a record. + virtual bool write(const char *label, const char *entry, int entrylen); + virtual bool write(const char *label, int entry); + +// get the current user's name. + virtual void get_user_name(char* buffer, ulong* size); +}; + +//Handy macro to read an int without having to specify the wordsize +#define read_int(label,varp) read(label,varp,sizeof(*varp)) + +//Macro to write a string +#define write_string(label,varp) write(label,varp,strlen(varp)) + +#endif + diff --git a/lib/linux/lnxdraw.h b/lib/linux/lnxdraw.h new file mode 100644 index 000000000..ff7299e5b --- /dev/null +++ b/lib/linux/lnxdraw.h @@ -0,0 +1,134 @@ +#ifndef __LNXDRAW_H_ +#define __LNXDRAW_H_ + +//#include "dyna_xwin.h" +//#include "dyna_xext.h" + +#include + +typedef struct +{ +// Display *dDisplay; // Which X-Windows Display to use + int nScreen; // Which X-Windows screen to use +}LnxVideoDesc; + + +typedef struct +{ + unsigned int bpp; // bits per pixel. + unsigned int dwFlags; // flags for window + unsigned int dwWidth; // width of the created window + unsigned int dwHeight; // height of the created window + unsigned int dwXPos; // Top-Left X position + unsigned int dwYPos; // Top-Left Y position + + char *lpszName; // Window name +// Window *pre_created_window; // Window already created +// XVisualInfo pre_created_visinfo; +}LnxWindowDesc; +//#define LNXDRAWF_USEPRECREATEDWIN 0x01 + +typedef struct +{ +// XSizeHints *lpSizeHints; +// XWMHints *lpWmHints; +// XClassHint *lpClassHints; +// Visual *lpXvisual; +// Window wWindow; + SDL_Surface *surface; +// bool bHaveSharedMemory; + bool bLocked; +// bool WindowPreCreated; +// XVisualInfo viVisualInfo; +// Colormap cmColorMap; + unsigned char *lpBuffer; +// GC m_GC; +// XImage *lpImage; +// XShmSegmentInfo shmInfo; + unsigned int dwWidth; // width of the created window + unsigned int dwHeight; // height of the created window + bool fullScreen; + + unsigned int lock_x,lock_y,lock_w,lock_h; + unsigned char *lock_ptr; +}LnxWindow; + +////////////////////// +// LnxDraw_InitVideo +////////////////////// +// Initializes the Linux video system (for X-Windows) +// +// Returns: +// 0 : no error +// -1 : invalid parameter +// -2 : already initialized +int LnxDraw_InitVideo(LnxVideoDesc *ldesc); + +///////////////////////// +// LnxDraw_CreateWindow +///////////////////////// + +// Creates and displays a window +// +// Returns: +// 0 : no error (handle in lphandle) +// -1 : invalid parameter +// -2 : Display not opened +// -3 : Out of memory +int LnxDraw_CreateWindow(LnxWindowDesc *ldesc,LnxWindow **lphandle); + +////////////////////////// +// LnxDraw_DestroyWindow +////////////////////////// +// Closes and deletes a window +// +// Returns: +// 0 : no error +// -1 : invalid parameter +int LnxDraw_DestroyWindow(LnxWindow *handle); + +//////////////////////// +// LnxDraw_LockSurface +//////////////////////// +// Locks the window surface, giving you a pointer to write data to +// +// Returns: +// true : success +// false : error +bool LnxDraw_LockSurface(LnxWindow *wnd,unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2,unsigned char **ptr,int *pitch); + +////////////////////////// +// LnxDraw_UnlockSurface +////////////////////////// +// Unlocks the window surface, blitting the buffer +// +void LnxDraw_UnlockSurface(LnxWindow *wnd,unsigned char *ptr); + +//////////////////////////// +/// LnxDraw_Blit +//////////////////////////// +// Blits a buffer to the window +// +// Returns: +// 0 : no error +// -1 : invalid parameter +// -2 : unknown error +int LnxDraw_Blit(LnxWindow *wnd,unsigned char *ptr,unsigned int x,unsigned int y,unsigned int w,unsigned int h); + +//////////////////////// +// LnxDraw_GetRGBMasks +//////////////////////// +// Returns the RGB masks for the display +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxDraw_GetRGBMasks(LnxWindow *wnd,unsigned int *r,unsigned int *g,unsigned int *b); + +#endif + + + + + + diff --git a/lib/linux/lnxsound.h b/lib/linux/lnxsound.h new file mode 100644 index 000000000..5cd5a9c66 --- /dev/null +++ b/lib/linux/lnxsound.h @@ -0,0 +1,153 @@ +#ifndef __LINUX_DD_SOUND_H_ +#define __LINUX_DD_SOUND_H_ + +#include +#include "ssl_lib.h" +#include "mixer.h" + +class sound_buffer_info; + +void lnxsound_SetError(int code); +void lnxsound_ErrorText(char *fmt, ... ); +inline void sb_adjust_properties_2d(sound_buffer_info *sb, float f_volume, float f_pan, ushort frequency); + +class emulated_listener +{ +public: + matrix orient; + vector position; + vector velocity; +}; + +class lnxsound : public llsSystem +{ +// Public functions +public: + lnxsound(); + ~lnxsound(); + + // may be called before init (must be to be valid, the card passed here will be initialized in InitSoundLib) + virtual void SetSoundCard(const char *name); + virtual void PauseSound(int sound_uid); + virtual void ResumeSound(int sound_uid); + // set special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentValues(const t3dEnvironmentValues *env); + + // get special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentValues(t3dEnvironmentValues *env); + + // enable special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentToggles(const t3dEnvironmentToggles *env); + + // get states of special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentToggles(t3dEnvironmentToggles *env); + + // Starts the sound library, maybe have it send back some information -- 3d support? + virtual int InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played); + // Cleans up after the Sound Library + virtual void DestroySoundLib(void); + + // Locks and unlocks sounds (used when changing play_info data) + virtual bool LockSound(int sound_uid); + virtual bool UnlockSound(int sound_uid); + + virtual bool SetSoundQuality(char quality); + virtual char GetSoundQuality(void); + virtual bool SetSoundMixer(char mixer_type); + virtual char GetSoundMixer(void); + + // Plays a 2d sound + virtual int PlaySound2d(play_information *play_info, int sound_index, float volume, float pan, bool f_looped); + virtual int PlayStream(play_information *play_info); + + virtual void SetListener(pos_state *cur_pos); + virtual int PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float master_volume, bool f_looped, float reverb=0.5f); //, unsigned short frequency + virtual void AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency); + virtual void AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb=0.5f); + + virtual void StopAllSounds(void); + + // Checks if a sound is playing (removes finished sound); + virtual bool IsSoundInstancePlaying(int sound_uid); + virtual int IsSoundPlaying(int sound_index); + +// virtual void AdjustSound(int sound_uid, play_information *play_info) = 0; + + // Stops 2d and 3d sounds + virtual void StopSound(int sound_uid, unsigned char f_immediately = SKT_STOP_IMMEDIATELY); + + // Pause all sounds/resume all sounds + virtual void PauseSounds(void); + virtual void ResumeSounds(void); + virtual bool CheckAndForceSoundDataAlloc(int sound_file_index); + + // Begin sound frame + virtual void SoundStartFrame(void); + + // End sound frame + virtual void SoundEndFrame(void); + + // returns the error string. + virtual char *GetErrorStr() const; + + // Returns current error code + int GetLastError() { int code = m_lib_error_code; m_lib_error_code = 0; return code; }; + + // environmental sound interface + // volume modifier (0-1), damping(0-1), 1 = complete, 0 = none + // decay 0.1 to 100 seconds, how long it takes for a sound to die. + virtual bool SetGlobalReverbProperties(float volume, float damping, float decay); + + virtual bool GetDeviceSettings(int *sound_device,unsigned int *freq,unsigned int *bit_depth,unsigned int *channels); + + friend void lnxsound_SetError(int code); + friend void lnxsound_ErrorText(char *fmt, ... ); + friend inline void sb_adjust_properties_2d(sound_buffer_info *sb, float f_volume, float f_pan, ushort frequency); + +protected: +#ifdef _DEBUG + short FindFreeSoundSlot(int sound_index, float volume, int priority); +#else + short FindFreeSoundSlot(float volume, int priority); +#endif + // This function limits the number of sounds cached to 255(8bits) and 256 bit is for invalid channel + // The purpose is to create unique signatures for each sound played (and allow for + // the slot_number to be quickly determined) + inline int MakeUniqueId(int sound_slot); + inline int ValidateUniqueId(int sound_uid); + + // Sound System Error Handler. + void CheckForErrors(void); + + void SetError(int code) { m_lib_error_code = code; }; + void StartStreaming(void); + void EndStreaming(void); + + int m_primary_frequency; // Set to the primary buffers frequency -- cmphack + int m_primary_alignment; + + int sound_device; + bool in_at_exit; + bool m_in_sound_frame; + bool m_pending_actions; + char m_sound_quality; + float m_cache_stress_timer; + float m_timer_last_frametime; + + int m_total_sounds_played; + int m_cur_sounds_played; + + emulated_listener m_emulated_listener; + + // Sam 6/29 - Added support for SDL + software_mixer m_mixer; +}; + + +#endif + + diff --git a/lib/linux/mixer.h b/lib/linux/mixer.h new file mode 100644 index 000000000..53283bc74 --- /dev/null +++ b/lib/linux/mixer.h @@ -0,0 +1,140 @@ +#ifndef __SOUND_MIXER_H______ +#define __SOUND_MIXER_H______ + +#include "ssl_lib.h" + +// Sound Library Internal Error Codes +#define SSL_OK 0 +#define SSL_ERROR_GENERIC -1 +#define SSL_ERROR_SAMPLE_NODATA -2 +#define SSL_ERROR_STREAMMIXER -3 + +// Sound Status +#define SSF_UNUSED 0 +#define SSF_PLAY_NORMAL 1 +#define SSF_PLAY_LOOPING 2 +#define SSF_PAUSED 4 +#define SSF_PLAY_STREAMING 8 +#define SSF_BUFFERED_LOOP 64 +#define SSF_BUFFERED_STRM 128 + +#define SBT_PRIMARY 0 +#define SBT_2D 1 +#define SBT_3D 2 + +// looping methods +#define DSLOOP_SMART_METHOD 0 +#define DSLOOP_BUFFER_METHOD 1 +#define DSLOOP_STREAM_METHOD 2 + +#define DSBUFLOOP_INIT_STEP -1 +#define DSBUFLOOP_LOOP_STEP 0 +#define DSBUFLOOP_FINISH_STEP 1 + +// This is the update rate at which to call sound_mixer::DoFrame() +// (sleep this amount each frame of the thread) +#define MIXER_TICK_INTERVAL 0.01f + +class sound_buffer +{ +public: + virtual int GetNumBufferBytes(void) = 0; + virtual void Write(unsigned char *buffer,int amount) = 0; +private: +}; + +typedef struct DSLOOPSTREAM +{ + int playing; + int please_close; + char *current_position; + int bytes_left; + int no_callbacks; + int half_buffer_point; + int last_half; + int close_on_next; + char silence_byte; + bool f_sample_16bit; + int num_written; + bool kill_me; +} DSLOOPSTREAM; + +// Sound item info (cache list) +// override if you need to keep more information +class sound_buffer_info +{ +public: + sound_buffer_info() {m_status = SSF_UNUSED;s=NULL; } + + play_information *play_info; + + int m_sound_index; // Index of sound + int m_unique_id; // Unique id for the currently playing sample + + // Not needed by the software mixer + volatile DSLOOPSTREAM* s; // Streaming info for a looping sample + + short m_mixer_type; // aureal, ds3d, ds_8? + short m_buffer_type; // Buffer type 2d or 3d + + char *sample_data; + int sample_length; // used for storage purposes. + + float m_volume; + + bool stereo; + sbyte bps; + unsigned char m_status; // Sound status + unsigned char pad; +}; + +typedef struct +{ + sound_buffer *primary_buffer; + int primary_frequency; + int primary_alignment; + int *max_sounds_available; // pointer to the variable that is updated with the # of sounds in the sound_cache + sound_buffer_info *sound_cache; // the array of sound information + + void (*fp_SetError)(int code); + void (*fp_ErrorText)(char *fmt, ... ); + int *p_error_code; + + llsSystem *ll_sound_ptr; +}tMixerInit; + +class software_mixer +{ +public: + software_mixer(); + ~software_mixer(); + + bool Initialize(tMixerInit *mi); + void DoFrame(void); + + // The actual mixer code that sum's the sounds on each channel and does all the actual + // mixing and effects (writes data to the locked primary buffer) + void StreamMixer(char *ptr, int len); + +private: + llsSystem *m_ll_sound_ptr; + bool m_init; + sound_buffer *m_primary_buffer; + int *Fast_mixer; + int Fast_mixer_len; + int m_primary_alignment; + int m_BufferSize; + + unsigned char *m_buffer; + + int *m_max_sounds_available; + sound_buffer_info *m_sound_cache; + + void (*m_fpSetError)(int code); + void (*m_fpErrorText)(char *fmt, ... ); + int *m_error_code; + +}; + +#endif + diff --git a/lib/linux/oelnx_os.h b/lib/linux/oelnx_os.h new file mode 100644 index 000000000..ecf90037b --- /dev/null +++ b/lib/linux/oelnx_os.h @@ -0,0 +1,139 @@ +/* + * $Logfile: /DescentIII/Main/lib/linux/oelnx_os.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:13 $ + * $Author: kevinb $ + * + * + * $Log: oelnx_os.h,v $ + * Revision 1.1.1.1 2003/08/26 03:58:13 kevinb + * initial 1.5 import + * + * + * 1 1/07/99 11:14p Jeff + * + * 3 5/15/97 1:48 AM Jeremy + * added define of ASSERT macro to call debugger function, implemented + * more parts of osMacDatabase in header, redefined max-filename, + * directory-name lengths to be correct size + * + * 2 5/9/97 7:15 PM Jeremy + * redefinition of object database class and #defines of some constants + * from the dos world + * + * 1 2/26/97 6:57 PM Jeremy + * Mac OS Specific header/initialization/debug/database stuff + * + * $NoKeywords: $ + */ + +#ifndef OEMAC_OS_H +#define OEMAC_OS_H + +#define ASSERT(x) do {if (!(x)) debug_break();} while(0) + +// ANSI Headers +#include + +// Macintosh Headers +#include + +class osMacObject : public osObject +{ + protected: + virtual void os_init(void); // mac os specific initialization + + public: + osMacObject(void); + virtual ~osMacObject(void); // mac os specific shutdown + +// parent_os is the osObject that is the parent of this new object. +// info is a device dependent structure passed in using device independent parms. + virtual void init(osObject *parent_os, void *info); + virtual bool create(osObject *parent_os, void *info); + +// returns a packet with a code telling us either to quit or that we are in some +// sort of idle state, which means we perform tasks like a game. + virtual gameos_packet *defer(void); // defers game operation to OS. + + virtual void get_info(void *info, int size_str); +}; + +/* osDatabase + to get info about the application from an OS managed database (or a custom info file) +*/ + +class osMacDatabase : public osDatabase +{ + public: + osMacDatabase(); + virtual ~osMacDatabase(); + + virtual bool init(); + + // creates an empty classification or structure where you can store information + virtual bool create_record(const char *pathname); + + // set current database focus to a particular record + virtual bool lookup_record(const char *pathname); + + // read either an integer or string from the current record + virtual bool read(const char *label, char *entry, int *entrylen); + virtual bool read(const char *label, int *entry); + + // write either an integer or string to a record. + virtual bool write(const char *label, char *entry, int entrylen); + virtual bool write(const char *label, int *entry); + + // get the current user's name from the os + virtual void get_user_name(char* buffer, ulong* size); + + protected: + // Additional Macintosh Functions, return true if successful + virtual bool FillOutPrefsInfo(void); + virtual bool InitPrefsFile(void); + + enum { kMacDatabaseResourceType = 'DatB' }; + virtual bool WriteDataToResourceFork (const char* label, void* entry, int entrylen); + virtual bool ReadDataFromResourceFork(const char* label, void* entry, int* entrylen); + + protected: + bool mInitted; + + OSType mAppSignature; + + // Prefs information + Str255 mPrefsFileName; + Str255 mPrefsFolderName; + OSType mPrefsFileType; + FSSpec mPrefsFileSpec; + short mPrefsFileRefNum; +}; + + +/******************************************* + Utility functions specific to the Mac OS +*******************************************/ + +int stricmp(const char* s1, const char* s2); +int strnicmp(const char* s1, const char* s2, int n); + +/******************************************* + Constants/#defines functions specific to the Mac OS +*******************************************/ +#define _MAX_DIR 32 +#define _MAX_FNAME 32 + +// fakeouts from windows/intel +#define _MAX_EXT 4 +#define _MAX_DRIVE 4 + +// Boolean values +#ifndef true + #define true 1 +#endif +#ifndef false + #define false 0 +#endif + +#endif \ No newline at end of file diff --git a/lib/lnxfix.h b/lib/lnxfix.h new file mode 100644 index 000000000..ae5bd609b --- /dev/null +++ b/lib/lnxfix.h @@ -0,0 +1,39 @@ +/* + * $Logfile: /DescentIII/Main/lnxfix.h $ + * $Revision: 1.2 $ + * $Date: 2004/02/09 04:14:49 $ + * $Author: kevinb $ + * + * File operations not covered properly in ANSI C + * + * $Log: lnxfix.h,v $ + * Revision 1.2 2004/02/09 04:14:49 kevinb + * Added newlines to all headers to reduce number of warnings printed + * + * Made some small changes to get everything compiling. + * + * All Ready to merge the 1.5 tree. + * + * Revision 1.1.1.1 2000/04/18 00:00:32 icculus + * initial checkin + * + * + * 2 4/15/99 1:39a Jeff + * changes for linux compile + * + * 1 4/14/99 7:51p Jeff + * + * + * $NoKeywords: $ + */ + +#ifndef LINUX_FIX_H_ +#define LINUX_FIX_H_ +#if defined(__LINUX__) + +#define stricmp(x,y) strcasecmp(x,y) +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) + +extern bool Dedicated_server; +#endif +#endif diff --git a/lib/lnxscreenmode.h b/lib/lnxscreenmode.h new file mode 100644 index 000000000..7c37c01de --- /dev/null +++ b/lib/lnxscreenmode.h @@ -0,0 +1,83 @@ +/* + * $Logfile: /DescentIII/Main/renderer/lnxscreenmode.h $ + * $Revision: 1.3 $ + * $Date: 2004/02/09 04:14:52 $ + * $Author: kevinb $ + * + * + * + * $Log: lnxscreenmode.h,v $ + * Revision 1.3 2004/02/09 04:14:52 kevinb + * Added newlines to all headers to reduce number of warnings printed + * + * Made some small changes to get everything compiling. + * + * All Ready to merge the 1.5 tree. + * + * Revision 1.2 2000/04/28 20:25:51 icculus + * Updates, cleanups, and SDLification. + * + * Revision 1.1.1.1 2000/04/18 00:00:49 icculus + * initial checkin + * + * + * 3 9/06/99 9:21p Jeff + * lock video mode switching, don't set viewport when restoring (causing a + * bunch of screen garbage) + * + * 2 9/05/99 9:41p Jeff + * first checkin of Linux Screen manager + * + * $NoKeywords: $ + */ + +#ifndef __LNXVIDEOMODE_H__ +#define __LNXVIDEOMODE_H__ + +#include "linux/linux_fix.h" +//#include "linux/dyna_xwin.h" +//#include +#include + +#define MODE_OK 0 +#define MODE_HSYNC 1 /* hsync out of range */ +#define MODE_VSYNC 2 /* vsync out of range */ +#define MODE_BAD 255 /* unspecified reason */ + +class CLnxVideoModes +{ +public: + CLnxVideoModes(); + ~CLnxVideoModes(); + + bool Init(void); //Display *dpy,int screen); + void RestoreVideoMode(void); + + //bool QueryExtension(Display *dpy); + // void GetModeLine(Display *dpy,int screen,int *dotclock,XF86VidModeModeLine *modeline); + + bool SwitchResolution(int width,int height); + void Lock(bool lock); + + SDL_Rect **getModes(void) { return m_ModeInfoList; } + Uint32 getSDLFlags(void) { return(sdlflags); } + +private: +// bool SwitchToMode(XF86VidModeModeInfo *mode); + bool LoadLibraries(void); + + bool m_Inited; + bool m_VideoResolutionChanged; + int m_NumVideoModes; +// XF86VidModeModeInfo **m_ModeInfoList; + SDL_Rect **m_ModeInfoList; + Uint32 sdlflags; +// Display *m_Display; +// int m_Screen; +}; + +extern CLnxVideoModes LinuxVideoMode; + +#endif + + diff --git a/lib/logfile.h b/lib/logfile.h new file mode 100644 index 000000000..ed78b8578 --- /dev/null +++ b/lib/logfile.h @@ -0,0 +1,28 @@ + +#ifndef LOGFILE_H +#define LOGFILE_H + +void log_Enable(bool enable); +void log_Disable(); + + +class logfile +{ + void *fp; + +public: + logfile(); // simple constructor + ~logfile(); + + void start(const char *fname, const char *longname=0); // restarts the logfile (opens a new one.) + void end(); + + void printf(const char *fmt, ...); + void puts(const char *msg); + void update(); // call to update logfile on disk (done by OS otherwise) +}; + +#endif + + + diff --git a/lib/mac/fixmac.h b/lib/mac/fixmac.h new file mode 100644 index 000000000..56311d846 --- /dev/null +++ b/lib/mac/fixmac.h @@ -0,0 +1,52 @@ +/* + * $Logfile: /DescentIII/Main/lib/mac/fixmac.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:13 $ + * $Author: kevinb $ + * + * $Log: fixmac.h,v $ + * Revision 1.1.1.1 2003/08/26 03:58:13 kevinb + * initial 1.5 import + * + * + * 2 5/10/99 10:55p Ardussi + * changes to compile on Mac + * + * 2 2/27/97 5:09 PM Jeremy + * added inline definitions of fixmul/div. Taken from Descent II's fixc.c + * + * 1 2/27/97 4:44 PM Jeremy + * inline assembly language functions for PowerPC for + * fixmul/fixdiv/fixmuldiv + * + * $NoKeywords: $ + */ + +#ifndef _FIXMAC_H +#define _FIXMAC_H + +#ifdef NEEDED +inline asm fix FixMul(fix a, fix b) +{ + mullw r5,r3,r4 // multiply low word + mulhw r6,r3,r4 // multiply high word (signed) + rlwinm r3,r5,16,16,31 // mask off low portion of result + rlwimi r3,r6,16,0,15 // insert high portion of result + blr // return to caller +} + +inline fix FixDiv(fix a, fix b) +{ + return (fix)(((double)a * 65536.0) / (double)b); +} + +inline fix FixMulDiv(fix a, fix b, fix c) +{ + double d; + + d = (double)a * (double) b; + return (fix)(d / (double) c); +} +#endif + +#endif diff --git a/lib/mac/mac_llsound.h b/lib/mac/mac_llsound.h new file mode 100644 index 000000000..ed82f136a --- /dev/null +++ b/lib/mac/mac_llsound.h @@ -0,0 +1,237 @@ +/* + * $Logfile: /DescentIII/Main/lib/mac/mac_llsound.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:13 $ + * $Author: kevinb $ + * + * Mac implementation of low level sound library + * + * $Log: mac_llsound.h,v $ + * Revision 1.1.1.1 2003/08/26 03:58:13 kevinb + * initial 1.5 import + * + * + * 5 4/12/00 6:56p Matt + * From Duane for 1.4 + * + * 4 7/28/99 5:02p Kevin + * Mac! + * + * 3 5/17/99 1:51p Ardussi + * changed to compile on Mac + * + * 2 5/13/99 5:05p Ardussi + * changes for compiling on the Mac + * + * 3 5/21/97 6:54 PM Jeremy + * changes to comply with chris's changes to the low level sound library + * api + * + * 2 3/21/97 7:03 PM Jeremy + * added declaration of low level mac sound interface virtual functions. + * + * 1 3/14/97 5:27 PM Jeremy + * first checkin + * + * $NoKeywords: $ + */ + +#ifndef MAC_LL_SOUND_SYSTEM_H +#define MAC_LL_SOUND_SYSTEM_H + +#include +#include +#include + +#include "hlsoundlib.h" +#include "application.h" +#include "ssl_lib.h" + +#define DIST_SCALE 8 + +class sound_channel_info +{ + SndChannelPtr snd_channel; + SSpLocalizationData snd_localization; + + play_information *play_info; + + int m_sound_uid; // Index of sound + int m_unique_id; // Unique id for the currently playing sample + + short m_buffer_type; // Buffer type 2d or 3d + + char *sample_data; + int sample_length; // used for storage purposes. + + bool stereo; + unsigned char m_status; // Sound status + float m_volume; // kept for priority. +}; +class emulated_listener +{ +public: + matrix orient; + vector position; + vector velocity; +}; + +//extern float ll_volume; +//void SetLLVolume(float volume); +//float GetLLVolume(void); + +extern void SetLLMasterVolume(float volume); + +class mac_llsSystem : public llsSystem +{ + +// Public functions +public: + virtual void SetMasterVolume(float volume); + virtual float GetMasterVolume(void); + + // may be called before init (must be to be valid, the card passed here will be initialized in InitSoundLib) + virtual void SetSoundCard(const char *name); + + // Starts the sound library, maybe have it send back some information -- 3d support? + virtual int InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played); +// virtual int InitSoundLib(oeApplication *sos, char mixer_type, char quality, bool f_kill_sound_list); + // Cleans up after the Sound Library + virtual void DestroySoundLib(void); + + virtual void NewSoundList(unsigned short num_sounds, unsigned int *sound_offset_array, + char *sound_data, unsigned int sound_data_size); + + // Locks and unlocks sounds (used when changing play_info data) + virtual bool LockSound(int sound_uid); + virtual bool UnlockSound(int sound_uid); + + virtual bool SetSoundQuality(char quality); + virtual char GetSoundQuality(void); + virtual bool SetSoundMixer(char mixer_type); + virtual char GetSoundMixer(void); + + // Plays a 2d sound + virtual int PlaySound2d(play_information *play_info, int sound_uid, float volume, float pan, bool f_looped); + virtual int PlayStream(play_information *play_info); + + virtual void SetListener(pos_state *cur_pos); + virtual int PlaySound3d(play_information *play_info, int sound_uid, pos_state *cur_pos, float master_volume, bool f_looped, float reverb=0.5f); //, unsigned short frequency) + virtual void AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency); + virtual void AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb); + + virtual void StopAllSounds(void); + + // Checks if a sound is playing (removes finished sound); + virtual bool IsSoundInstancePlaying(int sound_uid); + virtual int IsSoundPlaying(int sound_uid); + + // Stops 2d and 3d sounds + virtual void StopSound(int sound_uid, unsigned char f_immediately = SKT_STOP_IMMEDIATELY); + + // Pause all sounds/resume all sounds + virtual void PauseSounds(void); + virtual void ResumeSounds(void); + virtual void PauseSound(int sound_uid); + virtual void ResumeSound(int sound_uid); + + virtual bool CheckAndForceSoundDataAlloc(int sound_file_index); + + // Begin sound frame + virtual void SoundStartFrame(void); + + // End sound frame + virtual void SoundEndFrame(void); + + // Returns current error code + int GetLastError() { int code = m_lib_error_code; m_lib_error_code = 0; return code; }; + + // environmental sound interface + // volume modifier (0-1), damping(0-1), 1 = complete, 0 = none + // decay 0.1 to 100 seconds, how long it takes for a sound to die. + virtual bool SetGlobalReverbProperties(float volume, float damping, float decay); + + // set special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentValues(const t3dEnvironmentValues *env); + + // get special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentValues(t3dEnvironmentValues *env); + + // enable special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentToggles(const t3dEnvironmentToggles *env); + + // get states of special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentToggles(t3dEnvironmentToggles *env); + + virtual ubyte GetNumChannels(void); + virtual void SetNumChannels(ubyte num_chan); + + virtual int FindFreeChannel(float volume, int priority); + virtual void PlaySound(int file_index, int channel, bool f_looped); + virtual void SetChannelVolume(int channel, float volume); + + inline int MakeUniqueId(int channel); + +protected: + typedef struct mac_sound_struct { + SndChannelPtr snd_channel; + int channel_snd_index; + int sound_priority; + float channel_priority; + bool looped; + } mac_sound_struct; + + // Total samples played since start of library + unsigned short m_total_sounds_played; // Used for unique ids and for stats + + // Sound library status + unsigned char m_f_sound_lib_init; // Flag is set if sound library is initialized -- cmphack + unsigned char m_f_sound_lib_status; // Paused or running -- cmphack + + // May not need some of these + int m_primary_frequency; // Set to the primary buffers frequency -- cmphack + int m_primary_alignment; + int m_current_frequency; // -- cmphack + + bool m_in_sound_frame; + bool m_pending_actions; // are there sound events pending (outside of of start/end) + char m_mixer_type; + + virtual void CheckForErrors(); // part of sound system error handler. + void SetError(int code) { m_lib_error_code = code; }; + +private: + + unsigned char m_primary_bit_depth; // 16 or 8 bits per sample -- cmphack + char m_sound_quality; + int m_channel_count; + + SSpListenerReference m_lr; + SSpSourceReference m_sr[MAX_SOUNDS_MIXED]; + emulated_listener m_emulated_listener; + + long master_save; + float m_volume; + + SSpLocalizationData snd_localization[3]; //for the two sound qualitys that have filtering + + mac_llsSystem *ll_sound_ptr; + + SndChannelPtr snd_channel[MAX_SOUNDS_MIXED]; + int channel_snd_index[MAX_SOUNDS_MIXED]; + int sound_priority[MAX_SOUNDS_MIXED]; + float channel_priority[MAX_SOUNDS_MIXED]; + bool looped[MAX_SOUNDS_MIXED]; + +// mac_sound_struct macSound[MAX_SOUNDS_MIXED]; + + bool sndsprk_initialized; + +}; + + +#endif \ No newline at end of file diff --git a/lib/mac/mac_settings.h b/lib/mac/mac_settings.h new file mode 100644 index 000000000..96d1d0e50 --- /dev/null +++ b/lib/mac/mac_settings.h @@ -0,0 +1,24 @@ +/* + * $Logfile: /Descent3/main/lib/mac/mac_settings.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:13 $ + * $Author: kevinb $ + * + * + * $Log: mac_settings.h,v $ + * Revision 1.1.1.1 2003/08/26 03:58:13 kevinb + * initial 1.5 import + * + * + * 1 2/27/97 5:04 PM Jeremy + * macintosh settings for the compiler + * + * $NoKeywords: $ + */ + +#ifndef MAC_SETTINGS_H +#define MAC_SETTINGS_H + +#define MACINTOSH + +#endif \ No newline at end of file diff --git a/lib/mac/oemac_os.h b/lib/mac/oemac_os.h new file mode 100644 index 000000000..885d2d364 --- /dev/null +++ b/lib/mac/oemac_os.h @@ -0,0 +1,137 @@ +/* + * $Logfile: /Descent3/main/lib/mac/oemac_os.h $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:13 $ + * $Author: kevinb $ + * + * + * $Log: oemac_os.h,v $ + * Revision 1.1.1.1 2003/08/26 03:58:13 kevinb + * initial 1.5 import + * + * + * 3 5/15/97 1:48 AM Jeremy + * added define of ASSERT macro to call debugger function, implemented + * more parts of osMacDatabase in header, redefined max-filename, + * directory-name lengths to be correct size + * + * 2 5/9/97 7:15 PM Jeremy + * redefinition of object database class and #defines of some constants + * from the dos world + * + * 1 2/26/97 6:57 PM Jeremy + * Mac OS Specific header/initialization/debug/database stuff + * + * $NoKeywords: $ + */ + +#ifndef OEMAC_OS_H +#define OEMAC_OS_H + +#define ASSERT(x) do {if (!(x)) debug_break();} while(0) + +// ANSI Headers +#include + +// Macintosh Headers +#include + +class osMacObject : public osObject +{ + protected: + virtual void os_init(void); // mac os specific initialization + + public: + osMacObject(void); + virtual ~osMacObject(void); // mac os specific shutdown + +// parent_os is the osObject that is the parent of this new object. +// info is a device dependent structure passed in using device independent parms. + virtual void init(osObject *parent_os, void *info); + virtual bool create(osObject *parent_os, void *info); + +// returns a packet with a code telling us either to quit or that we are in some +// sort of idle state, which means we perform tasks like a game. + virtual gameos_packet *defer(void); // defers game operation to OS. + + virtual void get_info(void *info, int size_str); +}; + +/* osDatabase + to get info about the application from an OS managed database (or a custom info file) +*/ + +class osMacDatabase : public osDatabase +{ + public: + osMacDatabase(); + virtual ~osMacDatabase(); + + virtual bool init(); + + // creates an empty classification or structure where you can store information + virtual bool create_record(const char *pathname); + + // set current database focus to a particular record + virtual bool lookup_record(const char *pathname); + + // read either an integer or string from the current record + virtual bool read(const char *label, char *entry, int *entrylen); + virtual bool read(const char *label, int *entry); + + // write either an integer or string to a record. + virtual bool write(const char *label, char *entry, int entrylen); + virtual bool write(const char *label, int *entry); + + // get the current user's name from the os + virtual void get_user_name(char* buffer, ulong* size); + + protected: + // Additional Macintosh Functions, return true if successful + virtual bool FillOutPrefsInfo(void); + virtual bool InitPrefsFile(void); + + enum { kMacDatabaseResourceType = 'DatB' }; + virtual bool WriteDataToResourceFork (const char* label, void* entry, int entrylen); + virtual bool ReadDataFromResourceFork(const char* label, void* entry, int* entrylen); + + protected: + bool mInitted; + + OSType mAppSignature; + + // Prefs information + Str255 mPrefsFileName; + Str255 mPrefsFolderName; + OSType mPrefsFileType; + FSSpec mPrefsFileSpec; + short mPrefsFileRefNum; +}; + + +/******************************************* + Utility functions specific to the Mac OS +*******************************************/ + +int stricmp(const char* s1, const char* s2); +int strnicmp(const char* s1, const char* s2, int n); + +/******************************************* + Constants/#defines functions specific to the Mac OS +*******************************************/ +#define _MAX_DIR 32 +#define _MAX_FNAME 32 + +// fakeouts from windows/intel +#define _MAX_EXT 4 +#define _MAX_DRIVE 4 + +// Boolean values +#ifndef true + #define true 1 +#endif +#ifndef false + #define false 0 +#endif + +#endif \ No newline at end of file diff --git a/lib/manage.h b/lib/manage.h new file mode 100644 index 000000000..f94390f7a --- /dev/null +++ b/lib/manage.h @@ -0,0 +1,281 @@ + +#ifndef MANAGE_H +#define MANAGE_H + +#include +#include "CFILE.H" +#include "bitmap.h" +#include "manage_external.h" + +#if defined(__LINUX__) +#include "linux/linux_fix.h" //for strnicmp,etc. +#endif + + +#define LOCAL_TABLE "Table.loc" +#define TEMP_LOCAL_TABLE "Tablr.loc" + +#define NET_TABLE "Table.gam" +#define TEMP_NET_TABLE "Tablr.tmp" + +// Notes: Pagelocks are for keeping track of what pages are locked by which users +// Tracklocks are for keeping track of what pages the local user is working on or has locked + +#define PAGELOCK_NAME_LEN 30 +#define TABLE_NAME_LEN PSPATHNAME_LEN +#define INFO_STRING_LEN 100 +#define MAX_PAGELOCKS 1000 +#define MAX_TRACKLOCKS 5000 + +#define PAGETYPE_UNKNOWN 0 +#define PAGETYPE_TEXTURE 1 +#define PAGETYPE_WEAPON 2 +#define PAGETYPE_ROBOT 3 +#define PAGETYPE_POWERUP 4 +#define PAGETYPE_DOOR 5 +#define PAGETYPE_SHIP 6 +#define PAGETYPE_SOUND 7 +#define PAGETYPE_MEGACELL 8 +#define PAGETYPE_GAMEFILE 9 +#define PAGETYPE_GENERIC 10 + + +typedef struct +{ + ubyte pagetype; // of type PAGETYPE above + char name[PAGENAME_LEN]; + char holder[PAGENAME_LEN]; +} mngs_Pagelock; + +typedef struct +{ + ubyte used; + ubyte overlay; + ubyte pagetype; + ubyte __pad; + int stack_filepos; // file position of this page in the tablefile (the value we are + // pushing, for addon tables) + char name[PAGENAME_LEN]; +} mngs_track_lock; + + +// For addon data +#define MAX_ADDON_TRACKLOCKS 1000 +#define MAX_ADDON_TABLES 2 + +typedef struct +{ + char AddOnTableFilename[TABLE_NAME_LEN]; + int Num_addon_tracklocks; + mngs_track_lock *Addon_tracklocks; +}AddOnTablefile; +extern AddOnTablefile AddOnDataTables[MAX_ADDON_TABLES]; +extern int Num_addon_tables; + +// Takes our addon pages and frees/restores our data to the appropriate pages +void mng_PopAddonPages(); + +// Simply sets no addon data to be loaded +void mng_ClearAddonTables (); + +// Push the given table file as an addon table file +// returns true on success +bool mng_SetAddonTable (char *name); + +// Pushes an addon pack onto the stack so we can keep track of it +void mng_PushAddonPage (int pagetype,char *name,int overlay); + +// Loads and allocs all pages found locally +void mng_LoadAddonPages (); + +// signifies whether or not the network is up +extern int Network_up,Stand_alone; + +// Starting editor? +extern int Starting_editor,Loading_locals,Loading_addon_table; + +extern char LocalD3Dir[]; +extern char NetD3Dir[]; +extern char TableFilename[]; +extern char TableLockFilename[]; +extern char LocalTableFilename[]; +extern char LocalTempTableFilename[]; +extern char LocalLevelsDir[]; +extern char ManageGraphicsDir[]; +extern char LocalManageGraphicsDir[]; +extern char LocalModelsDir[]; +extern char NetModelsDir[]; +extern char LocalSoundsDir[]; +extern char NetSoundsDir[]; +extern char LocalRoomsDir[]; +extern char NetRoomsDir[]; +extern char LocalTableDir[]; +extern char NetMiscDir[]; +extern char LocalMiscDir[]; +extern char NetMusicDir[]; +extern char LocalMusicDir[]; +extern char NetScriptDir[]; +extern char LocalScriptDir[]; +extern char NetArtDir[]; +extern char LocalArtDir[]; + +extern char LocalCustomGraphicsDir[]; +extern char LocalCustomSoundsDir[]; + +extern char TempTableFilename[]; +extern char TempTableLockFilename[]; +extern char ErrorString[]; +extern char InfoString[]; +extern char TableUser[]; +extern char LockerFile[]; +extern char VersionFile[]; +extern mngs_Pagelock GlobalPagelocks[]; +extern mngs_track_lock GlobalTrackLocks[]; + +int mng_InitTableFiles(); + +// Loads our tables +int mng_LoadTableFiles (int show_progress); + +int mng_InitLocalTables(); +int mng_InitNetTables(); + +// Checks to see if there is a table file...if not, create one with a dummy page +void mng_CheckToCreateLocalTables(); +void mng_CheckToCreateNetTables(); + +// Creates directories if needed +void mng_InitLocalDirectories(); +void mng_InitNetDirectories(); + +void mng_ReadDummyPage (CFILE *infile,ubyte pagetype); +void mng_ReadWriteDummyPage (CFILE *infile,CFILE *outfile,ubyte pagetype); + +// Function for writing out "undefined" page...useful for placeholding +void mng_WriteUnknownPage (CFILE *outfile); + + + +// Lock functions +//----------------------------------------------- + +// If table lock file not found, create a dummy one +void mng_InitPagelocks (); + +// Writes a pagelock to an open file +void mng_WritePagelock (CFILE *fp,mngs_Pagelock *pl); + +// Reads a pagelock from the fp file. Returns 1 if successfully read, 0 if eof was encountered. +int mng_ReadPagelock (CFILE *,mngs_Pagelock *); + +// Given a page name, checks to see if it is locked. +// Returns 1 if locked, 0 if not. +int mng_CheckIfPageLocked (mngs_Pagelock *); + +// Given a page name, checks to see if it is locked by owner. +// Returns 1 if locked, 0 if not. +int mng_CheckIfPageOwned (mngs_Pagelock *,char *owner); + +// Given a pagelock, replaces the one already inside the lock file, or if not present, adds it to +// the lock file. Returns 0 on error, or 1 if successful. +int mng_ReplacePagelock (char *,mngs_Pagelock *); + +int mng_GetListOfLocks (mngs_Pagelock *pl,int max,char *who); + +// Given a name and a pagetype, deletes the one already inside the lock file +int mng_DeletePagelock (char *name,int pagetype); + +// Call this before any chokepoint functions are executed. +// Locks the whole table system for our exclusive use +// Returns 0 if can't lock +int mng_MakeLocker(); + +// Given a list of names and a pagetype, deletes the ones already inside the lock file +int mng_DeletePagelockSeries (char *names[],int num,int pagetype); + +// Simply erases the Lockerfile +void mng_EraseLocker(); + +// Open the net table file and read in all pages +int mng_LoadNetPages(int show_progress); +int mng_LoadLocalPages(); + + +//--------------------------------------------------------------- + +// Clear out tracklocks +void mng_InitTrackLocks (); + +// Given a name, returns the index of the tracklock with that name +// -1 indicates that it wasn't found +int mng_FindTrackLock (char *name,int pagetype); + +// Searches through global array of tracklocks and returns first free one +// returns -1 if none free +int mng_AllocTrackLock(char *name,int pagetype); + +// Frees a tracklock +void mng_FreeTrackLock (int n); + +//---------------------------------------------------------------- + +// Displays all the locks of "name" +void mng_DisplayLockList (char *name); + +// Renames a page on the network +int mng_RenamePage (char *oldname,char *newname,int pagetype); + +// Removes a file, then renames another file to be the removed file. Get it? +// Returns 1 on success, else 0 on fail +int SwitcherooFiles (char *name,char *tempname); + +// Returns true if the passed in pagelock is in the LockList, else false +bool InLockList (mngs_Pagelock *pl); + +// Takes a pagelock and sets its holder name to UNLOCKED +void mng_OverrideToUnlocked (mngs_Pagelock *temp_pl); + +// Returns true if the passed in primitive is old (ie needs to be updated from the network) +bool IsPrimitiveOld (char *name); + +// Updates a primitive if needed +// Localname = local version of the primname (with path) +// Netname = Network version of the primname (with path) +void UpdatePrimitive (char *localname,char *netname,char *primname,int pagetype,char *pagename); + +//Writes a chunk header. Writes chunk id & placeholder length. Returns chunk start pos +int StartManagePage(CFILE *ofile,ubyte pagetype); + +//Fill in page length when done writing +void EndManagePage(CFILE *ofile,int chunk_start_pos); + +struct physics_info; +struct otype_wb_info; + +// Reads a physics chunk in from the table file +void mng_ReadPhysicsChunk (physics_info *phys_info,CFILE *infile); +void mng_WritePhysicsChunk (physics_info *phys_info,CFILE *outfile); + +// Writes out weapon battery info +void mng_WriteWeaponBatteryChunk (otype_wb_info *static_wb,CFILE *outfile); + +// Reads in weapon battery info +void mng_ReadWeaponBatteryChunk (otype_wb_info *static_wb,CFILE *infile,int version); + +// Given a texture handle, searches the table file and replaces the texture with the same name +// If local=1, then does it to the users local copy +// Returns 0 on error, else 1 if all is good +int mng_ReplacePage (char *srcname,char *destname,int handle,int dest_pagetype,int local); + +// Given a texture name, finds it in the table file and deletes it +// If local is 1, deletes from the local table file +int mng_DeletePage (char *name,int dest_pagetype,int local); + +void mng_FreePagetypePrimitives (int pagetype,char *name,int freetype); + +extern int Old_table_method; + +//Error reporting +void DataError(char *fmt,...); + +#endif \ No newline at end of file diff --git a/lib/manage_external.h b/lib/manage_external.h new file mode 100644 index 000000000..a608a9ba9 --- /dev/null +++ b/lib/manage_external.h @@ -0,0 +1,45 @@ +/* +* $Logfile: /DescentIII/main/lib/manage_external.h $ +* $Revision: 4 $ +* $Date: 2/10/99 4:38p $ +* $Author: Matt $ +* +* Any defines, structs, etc. manage related that can be exported to DLLs +* +* $Log: /DescentIII/main/lib/manage_external.h $ + * + * 4 2/10/99 4:38p Matt + * Misc. changes for table file parsing + * + * 3 2/05/99 7:03p Jeff + * table file parsing macros put in + * + * 2 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h +* +* $NoKeywords: $ +*/ + + +#ifndef __MANAGE_EXTERNAL_H_ +#define __MANAGE_EXTERNAL_H_ + +#define PAGENAME_LEN 35 + +//Use this macro around your parameter in a table-file lookup, etc. to have the +//table file parser ignore this instance of the function (because you might be +//using one of the $$TABLE_ defines in a comment to force add items +#define IGNORE_TABLE(s) s + +//Use these macros around literal strings that are in arrays. +//NOTE that these strings must be inside curly braces +#define TBL_SOUND(s) s +#define TBL_GENERIC(s) s +#define TBL_GAMEFILE(s) s + +#endif \ No newline at end of file diff --git a/lib/megacell.h b/lib/megacell.h new file mode 100644 index 000000000..bf1ca7328 --- /dev/null +++ b/lib/megacell.h @@ -0,0 +1,53 @@ +#ifndef MEGACELL_H +#define MEGACELL_H + +#include "pstypes.h" +#include "manage.h" +#include "gametexture.h" + +#define MAX_MEGACELLS 100 + +#define DEFAULT_MEGACELL_WIDTH 8 +#define DEFAULT_MEGACELL_HEIGHT 8 + +#define MAX_MEGACELL_WIDTH 8 +#define MAX_MEGACELL_HEIGHT 8 + +typedef struct +{ + char name[PAGENAME_LEN]; + sbyte width; + sbyte height; + + short texture_handles[MAX_MEGACELL_WIDTH*MAX_MEGACELL_HEIGHT]; + int flags; + ubyte used; +} megacell; + +extern int Num_megacells; +extern megacell Megacells[MAX_MEGACELLS]; + +// Sets all MEGACELLs to unused +void InitMegacells (); + +// Allocs a MEGACELL for use, returns -1 if error, else index on success +int AllocMegacell (); + +// Frees MEGACELL index n +void FreeMegacell (int n); + +// Gets next MEGACELL from n that has actually been alloced +int GetNextMegacell (int n); + +// Gets previous MEGACELL from n that has actually been alloced +int GetPrevMegacell (int n); + +// Searches thru all MEGACELLs for a specific name, returns -1 if not found +// or index of MEGACELL with name +int FindMegacellName (char *name); + + + +#endif + + diff --git a/lib/mem.h b/lib/mem.h new file mode 100644 index 000000000..956998323 --- /dev/null +++ b/lib/mem.h @@ -0,0 +1,116 @@ +/* + * $Logfile: /DescentIII/Main/lib/mem.h $ + * $Revision: 18 $ + * $Date: 3/20/00 12:26p $ + * $Author: Matt $ + * + * Memory header. + * + * $Log: /DescentIII/Main/lib/mem.h $ + * + * 18 3/20/00 12:26p Matt + * Merge of Duane's post-1.3 changes. + * Added declaration + * + * 17 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 16 8/10/99 5:12p Jeff + * added a debug function to dump the current state of the mem library to + * file + * + * 15 3/15/99 11:24a Kevin + * Improved memory leak detection for mem_strdup and turned on debugging + * + * 14 10/13/98 3:42p Kevin + * bug fixes + * + * 13 10/13/98 9:25a Kevin + * + * 12 10/12/98 8:39p Kevin + * removed mprintf's and fixed some smallish bugs + * + * 11 10/12/98 11:54a Kevin + * + * 10 10/12/98 10:55a Kevin + * Don't use memory lib for editor currently + * + * 9 10/12/98 10:22a Samir + * made mem_strdup take a const char pointer. + * + * 8 10/09/98 3:32p Kevin + * New memory library + * + * 7 10/08/98 4:24p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 6 10/08/98 2:40p Kevin + * + * 5 7/31/98 5:44p Samir + * improved memory debugging. + * + * $NoKeywords: $ + */ + +#ifndef MEM_H +#define MEM_H + + +//#define MEM_USE_RTL 1 + +#ifdef MACINTOSH +#undef MEM_USE_RTL +#endif + + +#ifdef MEM_USE_RTL +#define mem_malloc(d) malloc(d) //Use this if your going to run BoundsChecker +#define mem_free(d) free(d) +#define mem_strdup(d) strdup(d) +#define mem_size(d) _msize(d) +#define mem_realloc(d,e) realloc(d,e) +#else +//Use this if your going to NOT run BoundsChecker +#define mem_malloc(d) mem_malloc_sub(d, __FILE__, __LINE__) +#define mem_free(d) mem_free_sub(d) +#define mem_strdup(d) mem_strdup_sub(d, __FILE__, __LINE__) +#define mem_size(d) mem_size_sub(d) +#define mem_realloc(d,e) mem_realloc_sub(d,e) +#endif + +extern bool Mem_low_memory_mode; +extern bool Mem_superlow_memory_mode; //DAJ + +// use if you want to manually print out a memory error +#define mem_error() mem_error_msg(__FILE__, __LINE__) + +// initializes memory library. +void mem_Init(); + +// shutsdown memory +void mem_shutdown(void); + +// Returns the number of dynamically allocated bytes +int mem_GetTotalMemoryUsed (); + +// Allocates a block of memory and returns a pointer to it +void *mem_malloc_sub (int size, const char *file, int line); + +// Frees a previously allocated block of memory +void mem_free_sub (void *memblock); + +// prints out a memory error message +void mem_error_msg(const char *file, int line, int size=-1); + +char * mem_strdup_sub(const char *src,char *file,int line); + +void * mem_realloc_sub(void * memblock,int size); + +int mem_size_sub(void *memblock); + +bool mem_dumpmallocstofile(char *filename); + +void mem_heapcheck(void); + +#endif \ No newline at end of file diff --git a/lib/module.h b/lib/module.h new file mode 100644 index 000000000..bd288900d --- /dev/null +++ b/lib/module.h @@ -0,0 +1,156 @@ +/* +* $Logfile: /DescentIII/Main/lib/module.h $ +* $Revision: 12 $ +* $Date: 7/14/99 6:43p $ +* $Author: Jeff $ +* +* Header for Dynamic Loadable Modules +* +* $Log: /DescentIII/Main/lib/module.h $ + * + * 12 7/14/99 6:43p Jeff + * don't load modules globally by default + * + * 11 7/13/99 5:40p Jeff + * fixes for Linux compile + * + * 10 6/25/99 4:58p Jeff + * default module load as global + * + * 9 5/10/99 10:53p Ardussi + * changes to compile on Mac + * + * 8 4/19/99 3:57a Jeff + * changed define for Linux in order for multiplayer games to compile + * + * 7 4/15/99 2:57a Jeff + * added missing defines for linux + * + * 6 1/11/99 12:53p Jeff + * added a function that given a module name it will make sure it has an + * extension. Made Osiris friendly with modules with no extension + * + * 5 1/04/99 12:24p Jeff + * updated...added compiler friendly define for dll functions that return + * a pointer + * + * 4 11/13/98 6:36p Jeff + * created dmfc_dll (a DLL version of DMFC) and converted current mods to + * use it + * + * 3 7/06/98 10:45a Jeff + * Made Linux friendly + * + * 2 6/05/98 2:15p Jeff + * Initial creation + * + * 1 6/05/98 2:14p Jeff +* +* $NoKeywords: $ +*/ + +#ifndef __DLMODULE_H_ +#define __DLMODULE_H_ + + +#ifdef __cplusplus +#define CPPEXTERN extern "C" +#else +#define CPPEXTERN extern +#endif + + +#ifdef WIN32 +//=========================Windows Definition============================ +#include "windows.h" + + +#ifdef _MSC_VER //Visual C++ Build +#define DLLFUNCCALL __stdcall +#define DLLFUNCCALLPTR *DLLFUNCCALL +#else //Non-Visual C++ Build +#define DLLFUNCCALL __attribute__((stdcall)) +#define DLLFUNCCALLPTR DLLFUNCCALL* +#endif + +#define MODPROCADDRESS FARPROC +#define DLLFUNCEXPORT __declspec (dllexport) +#define DLLFUNCIMPORT __declspec (dllimport) +#define DLLEXPORT CPPEXTERN DLLFUNCEXPORT +typedef struct{ + HINSTANCE handle; //handle to the DLL +}module; +//======================================================================= +#elif defined (__LINUX__) +//==========================Linux Definitions============================ +#include + +#define MODPROCADDRESS void* +#define DLLFUNCCALL __attribute__((stdcall)) +#define DLLFUNCCALLPTR DLLFUNCCALL* +#define DLLFUNCEXPORT +#define DLLFUNCIMPORT +#define DLLEXPORT CPPEXTERN + +typedef struct{ + void *handle; //handle to the DLL +}module; +//======================================================================= +#elif defined (MACINTOSH) +//==========================Mac Definitions============================ +#define MODPROCADDRESS void* +#define DLLFUNCCALL +#define DLLFUNCCALLPTR DLLFUNCCALL* +#define DLLFUNCEXPORT +#define DLLFUNCIMPORT +#define DLLEXPORT + +typedef struct{ + void *handle; //handle to the DLL +}module; +//======================================================================= + +#endif + + +//Mod error codes +#define MODERR_NOERROR 0 //There was no error +#define MODERR_INVALIDMOD 1 //This is not a valid module +#define MODERR_MODNOTFOUND 2 //The module couldn't be found +#define MODERR_MODINITFAIL 3 //The module initialization routine failed +#define MODERR_NOMOD 4 //The value you past in for the module isn't a module, or nothing has been loaded +#define MODERR_INVALIDHANDLE 5 //The module handle passed in is NULL +#define MODERR_OTHER 255 //Some other error occured + +//Flags +#define MODF_LAZY 0x001 //Symbol resolution on demand +#define MODF_NOW 0x002 //Resolve all symbols before returning +#define MODF_GLOBAL 0x200 // + +// Returns the real name of the module. If a given file has an extension, it will +// just return that filename. If the given file has no given extension, the +// system specific extension is concatted and returned. +void mod_GetRealModuleName(const char *modfilename,char *realmodfilename); + +//Loads a dynamic module into memory for use. If no extension is given, the default +// system specific extension is used. +//Returns true on success, false otherwise +bool mod_LoadModule(module *handle,char *modfilename,int flags=MODF_LAZY); + +//Frees a previously loaded module from memory, it can no longer be used +//Returns true on success, false otherwise +bool mod_FreeModule(module *handle); + +//Returns a pointer to a function within a loaded module. If it returns NULL there was an error. Check mod_GetLastError +//to see if there was an error +//symstr is the name of the function you want to get the symbol for (Do NOT give any pre/suffix to this name) +//parmbytes is the size (in bytes) of the parameter list the function should have +MODPROCADDRESS mod_GetSymbol(module *handle,char *symstr,unsigned char parmbytes); + +//Returns an error code to what the last error was. When this function is called the last error is cleared, so by calling +//this function it not only returns the last error, but it removes it, so if you were to call this function again, it would +//return no error +int mod_GetLastError(void); + + +#endif \ No newline at end of file diff --git a/lib/mono.h b/lib/mono.h new file mode 100644 index 000000000..f3d6f40a9 --- /dev/null +++ b/lib/mono.h @@ -0,0 +1,65 @@ +/* + * $Source: f:/miner/source/bios/rcs/mono.h $ + * $Revision: 9 $ + * $Author: Kevin $ + * $Date: 7/28/99 3:16p $ + * + * Header for monochrome/mprintf functions + * + * $Log: /DescentIII/Main/lib/mono.h $ + * + * 9 7/28/99 3:16p Kevin + * Mac Stuff + * + * 8 1/26/99 9:44p Jeff + * moved tcplog functions to mono library + * + * 7 1/09/99 4:39p Jeff + * added some ifdefs and fixes to get files to compile under Linux + * + * 6 10/18/98 8:54p Matt + * Fixed macro which used an if statement not inside of a do..while block + * + * 5 10/13/98 12:03p Kevin + * Changed use of preprocessors for debug, etc. + * + * 4 10/09/98 12:52p Samir + * mono define also defines mprintf. + * + * 3 4/22/98 3:50p Chris + * Added DebugBlockPrint + * + * 2 9/16/97 5:06p Matt + * Disable mprintf() & mprintf_at() when _DEBUG not set + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 3 6/11/97 1:11p Samir + * Implemented new Debug system + * + * 2 3/10/97 12:29p Samir + * Moved mono code and altered lowlevel console interface to machine + * library. + * + */ +#ifndef _MONO_H +#define _MONO_H +#include "debug.h" +void nw_InitTCPLogging(char *ip,unsigned short port); +void nw_TCPPrintf(int n, char * format, ... ); +#if (!defined(RELEASE)) && defined(MONO) + extern bool Debug_print_block; + // Prints a formatted string to the debug window + #define mprintf(args) Debug_ConsolePrintf args + // Prints a formatted string on window n at row, col. + #define mprintf_at(args) Debug_ConsolePrintf args + #define DebugBlockPrint(args) do { if(Debug_print_block)mprintf_at((1,5,51,args)); } while (0) +#else //ifdef _DEBUG +#ifndef MACINTOSH + #define mprintf(args) //DAJ defined in target headers +#endif + #define mprintf_at(args) + #define DebugBlockPrint(args) +#endif //ifdef _DEBUG +#endif diff --git a/lib/movie.h b/lib/movie.h new file mode 100644 index 000000000..02dfc6376 --- /dev/null +++ b/lib/movie.h @@ -0,0 +1,46 @@ +#ifndef MOVIE_H__ +#define MOVIE_H__ + +#include "renderer.h" + +#define MVELIB_NOERROR 0 +#define MVELIB_FILE_ERROR -1 +#define MVELIB_NOTINIT_ERROR -2 +#define MVELIB_DDGR_ERROR -3 +#define MVELIB_INIT_ERROR -4 +#define MVELIB_PLAYBACK_ERROR -5 +#define MVELIB_PLAYBACK_ABORTED -6 + +class oeApplication; + +int mve_Init( const char *dir, const char *sndcard ); + +// simply plays a movie. +int mve_PlayMovie( const char *mvename, oeApplication *app ); + +// used to copy movie data to a pointer, looping will loop, fhandle will be a pointer to a file handle to be returned +// handle to movie sequence is returned by function. +unsigned int mve_SequenceStart( const char *mvename, int *fhandle, oeApplication *app, bool looping = false ); + +// frames movies started with SequenceStart. Optionally, pass a pointer to a variable which will receive +// bitmap handle containing data, nx and ny will contain adjusted x and y coordinates if needed. +// passing -1 to x and y centers the frame on that axis +// handle is the movie handle returned by mve_Sequence start +// returned value is the handle passed into mve_SequenceFrame or new one if movie looped. +unsigned int mve_SequenceFrame( unsigned int handle, int fhandle, bool sequence, int *bm_handle ); + +bool mve_SequenceClose( unsigned int handle, int fhandle ); + +// sets render frame boundaries. +void mve_SetRenderProperties( short x, short y, short w, short h, renderer_type type, bool hicolor ); + +// called every frame. +typedef void (*MovieFrameCallback_fp)( int x, int y, int movieFrameNum ); +void mve_SetCallback( MovieFrameCallback_fp callBack ); + +// call to print out text. +void mve_Puts( short x, short y, ddgr_color col, const char *txt ); + +void mve_ClearRect( short x1, short y1, short x2, short y2 ); + +#endif diff --git a/lib/music.h b/lib/music.h new file mode 100644 index 000000000..6be98ded0 --- /dev/null +++ b/lib/music.h @@ -0,0 +1,386 @@ +/* + * $Logfile: /DescentIII/Main/Lib/music.h $ + * $Revision: 21 $ + * $Date: 3/18/99 10:13a $ + * $Author: Samir $ + * + * Event driven music system + * + * $Log: /DescentIII/Main/Lib/music.h $ + * + * 21 3/18/99 10:13a Samir + * msuic update. + * + * 20 3/03/99 5:08p Samir + * added slew of debug code: hopefully the library will be revamped after + * OEM. + * + * 19 2/27/99 8:23p Samir + * fixes to music system to act nicely to sudden and frequent region + * changes. + * + * 18 2/27/99 6:52p Samir + * added function to get current loop count + * + * 17 2/27/99 4:37p Samir + * return name of loop currently playing. + * + * 16 2/26/99 5:26p Samir + * fixes to streaming audio to reflect fix in direct sound mixer. + * + * 15 2/22/99 3:23p Luke + * upped size of music buffers + * + * 14 1/28/99 2:22p Samir + * simplified music system for D3. + * + * 13 12/15/98 10:22a Samir + * upped max labels. + * + * 12 12/11/98 4:03p Samir + * made stream public for oms_stream. + * + * 11 12/10/98 10:12a Samir + * uses newer streaming audio library + * + * 10 12/07/98 11:44a Samir + * added new themes. + * + * 9 12/03/98 12:51p Samir + * changed WasStopped to HasStopped. + * + * 8 11/20/98 5:21p Samir + * added SEND_STRM_NEXT + * + * 7 11/13/98 2:29p Samir + * added oms_tracklist and newer music code. + * + * 6 8/14/98 7:37p Matt + * Since PSFILENAME_LEN is now considered to be the max length of the + * string itself (not counting the terminating null), buffers for file + * name need to be PSFILENAME_LEN+1 bytes long. + * + * 5 8/10/98 5:53p Samir + * improved switching between streams and basic background/combat music. + * + * 4 7/28/98 5:43p Samir + * reorg of music system. + * + * 3 7/24/98 5:20p Samir + * first real music checkin. + * + * 2 7/08/98 6:26p Samir + * took out obsolete code. + * + * 2 6/09/97 4:16p Samir + * RCS check in + * + * $NoKeywords: $ + */ + +#ifndef MUSIC_H +#define MUSIC_H + +#include "pstypes.h" +#include "pserror.h" +#include "psclass.h" +#include "streamaudio.h" + + +/////////////////////////////////////////////////////////////////////////////// +// These are internal constants for the music sequencer + +#define OMS_NUM_STRM 2 +#define OMS_STRM_QSIZE 8 + +// OMS Theme Types +#define OMS_THEME_TYPES 8 +#define OMS_THEME_TYPE_NONE 0 +#define OMS_THEME_TYPE_INTRO 1 +#define OMS_THEME_TYPE_IDLE 2 +#define OMS_THEME_TYPE_COMBAT 3 +#define OMS_THEME_TYPE_DEATH 4 +#define OMS_THEME_TYPE_IDLE_TO_COMBAT 5 +#define OMS_THEME_TYPE_COMBAT_TO_IDLE 6 + +// OMS Stream commands +#define OMS_STRM_FREE 255 // indicates free stream. +#define OMS_STRM_STOP 0 // stop current stream. +#define OMS_STRM_PLAY 1 // play current stream. +#define OMS_STRM_FADEOUT 2 // fades out current stream in (parm secs). +#define OMS_STRM_FADEIN 3 // plays current stream, fade it in. at (parm secs) +#define OMS_STRM_DELAY 4 // standard delay (parm = time) +#define OMS_STRM_LOAD 5 // loads song (parm = song type). +#define OMS_STRM_PARM 6 // extra parameter for last stream command (parm = parameter) +#define OMS_STRM_SWITCH 7 // switches on next measure of this calling stream to the dominant one. +#define OMS_STRM_NEXT 8 // specifies setting a 'next' request on an opened stream. + + +// //////////////////////////////////////////////////////////////////////////// +// generic event item for the queues. +struct oms_q_evt +{ + ubyte cmd; + union { float f; int i; void *p; } parm; +}; + +class OutrageMusicSeq; + +// internal class to the sequencer, handles command-based streams. +class oms_stream +{ + friend class OutrageMusicSeq; + + OutrageMusicSeq *m_seq; // parent sequencer. + tQueue m_q; // queue of stream commands. + float m_timer,m_timer_init; // timer managed by user. + float m_maxvol; // sustained max volume of stream (used in fading). + oms_q_evt m_status; // current command status (operation q item) + int m_stopcount; // number of times stopped. + + union { + int i; + float f; + void *p; + } m_data; // data for internal ops. + union { + int i; + float f; + void *p; + } m_data2; // data for internal ops. + +private: + inline void STREAM_COMMANDP(ubyte cmd, void *p=NULL) { + m_status.cmd = cmd; + m_status.parm.p = p; + } + inline void STREAM_COMMANDF(ubyte cmd, float cmdparm=0) { + m_status.cmd = cmd; + m_status.parm.f = cmdparm; + }; + inline void STREAM_COMMANDI(ubyte cmd, int cmdparm=0) { + m_status.cmd = cmd; + m_status.parm.f = cmdparm; + }; + inline void STREAM_TIMER(float time_init) { + m_timer = time_init; + m_timer_init = time_init; + }; + + bool processCommand(); // processes current command on stream + void processQLoad(const char *fname); // processes load song q event. + +public: + oms_stream(); + ~oms_stream(); + + AudioStream m_stream; // current audio stream playing + +public: + void Process(float frmtime); // processes a stream's events. + void Send(oms_q_evt *evt); // sends an event to the stream. + void Reset(OutrageMusicSeq *seq=NULL); // reset stream. + void SetVolume(float vol); // volume. + int GetPlayRequestCount(); // tells how many play events send are pending processing. + +//event senders +public: + void SEND_STRM_LOAD(const char *fname); + void SEND_STRM_FADEOUT(float time); + void SEND_STRM_FADEIN(float time); + void SEND_STRM_STOP(); + void SEND_STRM_PLAY(float vol, ubyte count); + void SEND_STRM_FREE(); + void SEND_STRM_SWITCH(bool *switch_flag); // when stream is done, changes value of flag. + void SEND_STRM_NEXT(const char *fname, float vol, ubyte count,bool *switch_flag); // specifies next stream to play in current stream. + +// return values (after process is called, this is updated.) +public: + bool m_valid_result; // is this a valid returned result? + oms_q_evt m_result; // result event returned from process. +}; + + +class oms_tracklist +{ + char **m_fnamelist; // filename list + char **m_symlist; // symbolic list + short m_numtracks; // number of tracks + short m_maxtracks; + bool m_init; // determines if initialized + +public: + oms_tracklist(); + ~oms_tracklist(); + + void init(short maxtracks); // initializes track list system + void free(); // frees track list system + void reset(); // resets track list to 0 + + bool add(const char *fname, const char *sym); + const char *get(const char *sym); // returns a track filename. +}; + + + +// //////////////////////////////////////////////////////////////////////////// +// The Outrage Music Sequencer v1.0 +// playback songs with special FX. +// this is an intermediate between song playback and event driven music. +// the calling application must act as the 'brain' in most cases. + +typedef short tMusicVal; // used for paramaters + +inline float NORMALIZE_MUSICVAL(tMusicVal val) { return ((float)val)/255.0f; } +inline tMusicVal QUANTIZE_MUSICVAL(float fval) { return (fval * 255); } + +#define MAX_MUSIC_INSTRUCTIONS 1280 +#define MAX_MUSIC_STRLEN 2048 +#define N_MUSIC_REGS 16 + + +// OMS Events +#define OMSEVT_MAKE(_evt, _priority) (((_evt)<<2)+(_priority&0x03)) +#define OMSEVT_PRIORITY(_evt) ((_evt) & 0x3) + +#define OMS_EVT_NONE OMSEVT_MAKE(0,0) +#define OMS_EVT_SONGENDED OMSEVT_MAKE(32,0) // music completed (parm tells us what theme) +#define OMS_EVT_LOOPENDING OMSEVT_MAKE(31,0) // a loop is ending. + + +// class +class OutrageMusicSeq +{ + friend class oms_stream; // stream should know about data here. + +// music scripting system. + struct music_ins { + ubyte cmd; // OMFCMD_ + union { + tMusicVal num; + char *str; + } + opr; // operand is either numeric or a string + }; + + struct music_stream { + oms_stream *strm; // attached stream. + oms_stream *old_strm; // previous stream. + const char *loop_name; + const char *pending_loop_name; // loop name preparing to play. + short region; // region id. + short type; + music_ins *ins; // intro, combat, etc. section + short last_ip; // last ip value + short ip; + tMusicVal b_reg; // branch register + tMusicVal c_reg; // compare regs + tMusicVal p_reg; // play count register. + tMusicVal i_reg; // iteration register + const char *ln_reg; // loopname register + bool request_stop; // we're requesting to stop at end of song. + bool ifi_block; // are we executing an ifi block? + bool immediate_switch; // immediate switch to new song. + bool stream_idle; // are we in a next switch? + bool error; + ubyte pad[3]; + }; + +// data + tMusicVal m_registers[N_MUSIC_REGS]; // registers + music_stream *m_active_song, *m_pending_song, *m_playing_song; + oms_stream m_strm[OMS_NUM_STRM]; // stream attached to it. + + bool m_sequencer_run; // do we run the sequencer. + bool m_sequencer_init; // is sequencer initialized. + int m_dominant_strm; // which stream is dominant. + float m_timer; // master timer. + float m_mastervol; // master volume. + + oms_tracklist m_tracklist; // sequencer track list. + tList m_music_list; // list of tracks. + + short m_curregion; // current region. + +private: +// Music list management + music_stream *AddToList(short region, short theme_type, int n_inst, const music_ins *ins); + void FreeList(); + music_stream *GetSong(short region, short theme_type); + bool LoadTheme(const char *file); // takes a filename containing oms data. + + void ExecScript(music_stream *strm); + + inline int DOMINANT_STRM_ADJUST() { + int nstrm = m_dominant_strm+1; + if (nstrm == OMS_NUM_STRM) + nstrm = 0; + return nstrm; + }; + +private: + music_ins *m_ins_buffer; // music instruction buffer + music_ins *m_ins_curptr; // current pointer to free instruction buffer. + char *m_str_buffer; // string buffer. + char *m_str_curptr; // current pointer to free string + + music_ins *alloc_and_copy_ins(int len, const music_ins *ins); + char *alloc_and_copy_str(const char *str); + +public: + OutrageMusicSeq(); + ~OutrageMusicSeq(); + +// takes a OMS theme file. + bool Init(const char *theme_file); + void Shutdown(); + + bool IsSequencerRunning() const + { + return m_sequencer_init && m_sequencer_run; + } + +// starts the sequencer (usually at the beginning of a level) + void Start(); + +// stops the sequencer, flushes events + void Stop(); + +// do a frame + void Frame(float frame_time); + +// set volume of sequencer + void SetVolume(float vol); + +// start a song, stopping the old either cleanly (on measure) or abruptly. + void StartSong(int song, bool clean_switch); + +// stops song, cold. + void StopSong(); + +// pauses + void Pause(); + +// resumes + void Resume(); + +// get and set user defined (theme file) parameters + void SetRegister(int parm, tMusicVal val); + tMusicVal GetRegister(int parm); + +// region setting + void SetCurrentRegion(short region); + short GetCurrentRegion() const; + +// gets current region PLAYING, not PENDING like above. + short GetPlayingRegion() const; + +// get current loop playing + const char *GetCurrentLoopName(int *loop_count); + +public: + tQueue m_output_q; // output events +}; + + +#endif \ No newline at end of file diff --git a/lib/mvelibw.h b/lib/mvelibw.h new file mode 100644 index 000000000..f4c0b4d11 --- /dev/null +++ b/lib/mvelibw.h @@ -0,0 +1,349 @@ +#if 0 +/* +** mvelibw.h +** +** Interplay Movie File (MVE) Player +** Library Definitions (32-Bit Win95 Version) +** Written by Paul Allen Edelstein, Interplay Productions. +** +** (c) 1997 Interplay Productions. All Rights Reserved. +** This file is confidential and consists of proprietary information +** of Interplay Productions. This file and associated libraries +** may not, in whole or in part, be disclosed to third parties, +** incorporated into any software product which is not being created +** for Interplay Productions, copied or duplicated in any form, +** without the prior written permission of Interplay Productions. +** Further, you may not reverse engineer, decompile or otherwise +** attempt to derive source code of this material. +*/ + +#ifndef _MVELIB_H_INCLUDED + +#include +#include "ddraw.h" +#include "dsound.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Call this function to provide hooks into your memory management. +*/ +typedef void *(__cdecl mve_cb_alloc)(unsigned size); +typedef void (__cdecl mve_cb_free)(void *p); +void __cdecl MVE_memCallbacks(mve_cb_alloc *fn_alloc, + mve_cb_free *fn_free); + +/* This function remains from the DOS version of mvelib. +** It allows you to provide a preallocated buffer for file I/O, +** but under Windows there's no real point to doing this. +*/ +void __cdecl MVE_memIO(void *p, unsigned size); + +/* Call this function to provide hook into your file io. +*/ +typedef unsigned __cdecl mve_cb_read(int handle, void *buf, unsigned count); +void __cdecl MVE_ioCallbacks(mve_cb_read *fn_read); + +/* Call this function to provide hook into your digital sound driver. +** Call with NULL if no sound support is available (default). +*/ +void __cdecl MVE_sndInit(LPDIRECTSOUND lpDS); + +/* Volume controls. +** These functions are equivalent to the IDirectSoundBuffer +** SetVolume and SetPan functions. They take effect immediately +** and do NOT reset when a new movie starts. +** Volume ranges from 0 (0 db, no volume change) to -10,000 (-100db, essentially silent). +** Pan ranges from -10,000 (left full volume, right -100db), thru 0 (both full), +** thru 10,000 (left -100db, right full volume). +** The default value for volume and pan is zero. +*/ +void __cdecl MVE_dsbSetVolume(long lVolume); +void __cdecl MVE_dsbSetPan(long lPan); + + +/* Only call this function to configure software to work with a Super VGA +** mode if you do not have VESA support. +** +** Restrictions/Assumptions: +** 64K >= WinSize >= WinGran +** WinSize % WinGran == 0 +** WinGran of 64K is represented by 0 +** SetBank is address of function with following protocol: +** bh: 0=Set window, 1=Get Window +** bl: Window number (0 or 1) +** dx: Window position in video memory in units of WinGran. +** on return, registers AX and DX are destroyed. +** +** Hicolor is 0 for 8-bit color, 1 for 15-bit rgb color, 2 +** for byte swapped 15-bit rgb color. +*/ + +/* Note: 16-bit WriteWinSeg replaced with 32-bit WriteWinPtr */ + +/* The functionality of the following function is reduced in the Windows +** version of the player. Call it as follows: +** MVE_sfSVGA(w,h,w,0,NULL,0,0,NULL,hicolor) +** where w and h are the width and height of your window, +** and hicolor is a boolean which indicates if the screen +** is operating in hi color, rather than 8-bit paletted color. +** Under windows, the information provided by this function +** is just used for window centering and for determining +** how and when to do palette callbacks. +*/ +void __cdecl +MVE_sfSVGA(unsigned w, unsigned h, unsigned LineWidth, + unsigned WriteWin, unsigned char *WriteWinPtr, + unsigned long WinSize, unsigned WinGran, + void *SetBank, unsigned hicolor); + +/* This function alters the display from 640x480 or 640x400 to 640x350 resolution. +*/ +void __cdecl +MVE_ForceVres350(void); + +/* This function alters the display from 640x480/400/350 to +** 640x240/200/175. +*/ +void __cdecl +MVE_ForceVresHalf(void); + +/* **NOTE** There still need to be calls to restore original screen resolution +** after using MVE_ForceVres350() or MVE_ForceVresHalf()! +*/ + +/* Only call this function to either +** 1. Replace method of copying frame to screen (perhaps for a nonstandard +** screen format). +** 2. Wrap your own code around the transfer of frame to screen +** or modify which portions of the screen are updated. +** This function replaces calls to the default MVE_ShowFrame function +** with calls to your function, which can itself call MVE_ShowFrame. +*/ +typedef void __cdecl mve_cb_ShowFrame + (LPDIRECTDRAWSURFACE buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned hicolor); +void __cdecl MVE_sfCallbacks(mve_cb_ShowFrame *fn_ShowFrame); + +typedef void __cdecl mve_cb_SetPalette + (unsigned char *p, unsigned start, unsigned count); +void __cdecl +MVE_palCallbacks(mve_cb_SetPalette *fn_SetPalette); + +void __cdecl +MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); + +/* Configure the software for a graphics mode, optionally setting the +** display to that mode (see the MVE_GFX_xxx constants defined below). +*/ +unsigned __cdecl MVE_gfxMode(short mode); + +/* Reset the screen to text mode (usually done before exiting a program). +*/ +void __cdecl MVE_gfxReset(void); + +/* Set line for split screen graphics */ +/* {Use vbe_SetDisplayStart(x,y) to set vid buf for upper screen} */ +void __cdecl MVE_gfxSetSplit(unsigned line); + +/* Setup double buffering */ +void __cdecl MVE_gfxSetDoubleBuffer(unsigned y1, unsigned y2, unsigned vis); + +/* Get double buffering state */ +void __cdecl MVE_gfxGetDoubleBuffer(unsigned *vis_y, unsigned *hid_y); + +/* Enable double buffering for auto screen modes */ +void __cdecl MVE_sfAutoDoubleBuffer(unsigned on); + +/* Wait for video retrace off (0) or on (1) */ +void __cdecl MVE_gfxWaitRetrace(unsigned state); + +/* Establish link to DirectDraw +*/ +void __cdecl MVE_rmDirectDraw(LPDIRECTDRAW lpDD); + +/* Establish primary to DirectDraw Surface +** This is only necessary for playing back hi-color movies. +** Its establishes the rgb format for decompression. +*/ +void __cdecl MVE_rmPrimarySurface(LPDIRECTDRAWSURFACE lpDD); + +/* Establish callback for user control of movie playback. +*/ +typedef int __cdecl mve_cb_ctl(void); +void __cdecl MVE_rmCallbacks(mve_cb_ctl *fn_ctl); + +/* Specify playback fastmode option (default is MVE_RM_NORMAL). +*/ +#define MVE_RM_NORMAL 0 /* Normal playback */ +#define MVE_RM_HALF 1 /* Half height (even lines only) */ +#define MVE_RM_DITHERED 2 /* Half height (dither between lines) */ +#define MVE_RM_HALF_2 5 /* Full height, even lines only */ +#define MVE_RM_DITHERED_2 6 /* Full height, dither, even lines only */ + +void __cdecl MVE_rmFastMode(int mode); + +/* Specifying horizontal magnification: +** 3: 4/3 horizontal magnification +** 4: normal +*/ +void __cdecl MVE_rmHScale(int hscale); + +/* Get frame count and number of dropped frames from last movie played. +*/ +void __cdecl MVE_rmFrameCounts(unsigned *FrameCount, unsigned *FrameDropCount); + +/* Dump timing statistics (if enabled). +*/ +void __cdecl MVE_logDumpStats(void); + +/* Run a compressed movie by reading data starting at the current +** position in the file specified by handle hFile. +** The movie window is displaced by dx,dy from the upper left hand corner +** or is centered if dx,dy is -1,-1. +** track specifies which audio track to play (usually 0 for a single +** audio track). +** +** Returns an error/result code. +** +** Memory may be dynamically allocated while movie runs. +*/ +int __cdecl MVE_RunMovie(int hFile, int dx, int dy, unsigned track); + +/* MVE_RunMovieContinue is the same as MVE_RunMovie except that it does not +** automatically call MVE_rmEndMovie(). This may improve the smoothness +** of immediately playing another movie afterwards. +*/ +int __cdecl MVE_RunMovieContinue(int hFile, int dx, int dy, unsigned track); + +/* +** Alternative to using MVE_RunMovie() and MVE_rmCallbacks(). +** Call MVE_rmPrepMovie() to prepare movie for playing. +** Call MVE_rmStepMovie() to display next frame of movie until nonzero +** result is returned (MVE_ERR_EOF for no next frame or some other error). +** Call MVE_rmHoldMovie() to hold on current frame (and pause audio). +** Call MVE_rmEndMovie() to abort movie. +** All functions except MVE_rmEndMovie() return an error code. +*/ +int __cdecl MVE_rmPrepMovie(int hFile, int dx, int dy, unsigned track); +int __cdecl MVE_rmStepMovie(void); +int __cdecl MVE_rmHoldMovie(void); +void __cdecl MVE_rmEndMovie(void); + +/* Frame Reader Streams +** This is a special interface to the movie system which +** allows a movie file to be opened as a stream from which +** its frames may be retrieved. Audio and timing information +** are ignored. For 256-color screen applications, palette +** information is also typically ignored, and movies with a common +** predefined palette are used. However, for hi-color screen +** applications, an interface to obtain palette information has +** been provided. This system is intended for use by video sprites +** played off of the hard drive or out of memory. +*/ +typedef struct _MVE_frstream *MVE_frStream; + +/* MVE_frOpen +** Before calling this function, be sure to call MVE_memCallbacks() +** and MVE_rmDirectDraw(). +** +** fn_read specifies a file reader similar to the one +** used by MVE_ioCallbacks(). +** handle specifies a file handle for an already opened +** movie file. It is used by the file reader and is similar +** to hFile argument used by MVE_RunMovie() and MVE_rmPrepMovie(). +** fr_callback is normally NULL, but can be used to supply +** a handler for user data which has been interleaved into +** the movie stream. +** +** If the movie file is invalid or the call otherwise fails, +** NULL is returned. +*/ +MVE_frStream __cdecl MVE_frOpen(unsigned (__cdecl *fn_read)(int handle, void *buf, + unsigned count), + int handle, + int (__cdecl *fr_callback)(unsigned op, unsigned subop, + void *buf)); + +/* MVE_frGet +** Returns the next frame from the specified frame reader stream +** a nonzero error code {the same codes as returned by MVE_RunMovie() +** and MVE_rmStepMovie()}. +** If successful, MVE_frGet(frs, &buf, &w, &h) returns a pointer +** to a direct draw surface containing the frame in buf, +** and its width and height in w and h. +*/ +int __cdecl MVE_frGet(MVE_frStream frs, + LPDIRECTDRAWSURFACE *pBuf, + unsigned *width, unsigned *height); + +/* MVE_frPal +** After each successful call to MVE_frGet(), this call may be used to +** obtain corresponding palette information. It returns a pointer to the +** entire current palette for the frame, and the subportion of the palette +** which has changed this frame is identified by start and count (they will +** both be zero on frames for which the palette has not changed). +** +** Paltbl points to 256*3 bytes of 6-bit r,g,b triples. +** Start ranges from 0 to 255. Count from 0 to 256. +** +** These conventions are similar to those used by the palette callback arguments +** with the standard player interface, except that this interface requires +** polling each frame instead, and must be passed pointers to the variables where +** the values will be returned. +** +*/ +void __cdecl MVE_frPal(MVE_frStream frs, + unsigned char **pPaltbl, unsigned *pStart, unsigned *pCount); + +/* MVE_frClose +** Closes the specified Frame Reader Stream frs. +** Frees all storage associated with the stream. +** The specified frs must not be used after this call. +** Note that the open file handle specified in MVE_frOpen() is +** not closed by this call...that is the caller's responsibility. +*/ +void __cdecl MVE_frClose(MVE_frStream frs); + + +/* Release any memory dynamically allocated by MVE_RunMovie. +*/ +void __cdecl MVE_ReleaseMem(void); + +/* Return string corresponding to MVE_RunMovie result code. +*/ +char* __cdecl MVE_strerror(int code); + +/* RunMovie callback control code and result codes. +** Codes > 1 are user defined. +*/ + +#define MVE_CTL_HOLD -1 /* Returned by rmCtl() to hold current frame */ +#define MVE_CTL_EXIT 1 /* Returned by rmCtl() to end movie */ + +#define MVE_ERR_EOF -1 /* Returned by StepMovie() for end of movie */ +#define MVE_ERR_IO -2 /* File I/O error or unable to alloc memory. */ +#define MVE_ERR_SYNC -3 /* Timer error. */ +#define MVE_ERR_SND -4 /* Unable to allocate memory for sound */ +#define MVE_ERR_NF -5 /* Unable to allocate memory for video */ +#define MVE_ERR_GFX_FIT -6 /* Screen size too small for movie */ +#define MVE_ERR_GFX_FAIL -7 /* Failed to set desired graphics mode */ +#define MVE_ERR_BADFMT -8 /* Not a MVE file or unacceptable version */ +#define MVE_ERR_GFX_CLR -9 /* Incorrect screen color mode */ +#define MVE_ERR_PREP -10 /* StepMovie() without PrepMovie() */ +#define MVE_ERR_DD -11 /* Unable to initialize DirectDraw */ +#define MVE_ERR_LOST -12 /* Direct Draw Surface Lost */ + +#define MVE_ERR_LAST -12 + +#define _MVELIB_H_INCLUDED +#ifdef __cplusplus +}; +#endif +#endif + +#else +#include "../libmve/mvelibl.h" +#endif diff --git a/lib/networking.h b/lib/networking.h new file mode 100644 index 000000000..c16a1d385 --- /dev/null +++ b/lib/networking.h @@ -0,0 +1,617 @@ +/* + * $Logfile: /DescentIII/Main/lib/networking.h $ + * $Revision: 1.9 $ + * $Date: 2004/12/05 04:00:20 $ + * $Author: ryan $ + * + * + * + * $Log: networking.h,v $ + * Revision 1.10 2004/12/05 04:38:23 ryan + * Disable IPX support. + * + * Revision 1.9 2004/12/05 04:00:20 ryan + * MacOS X patches. + * + * Revision 1.8 2004/03/21 17:11:39 kevinb + * Fixes so linux will compile again. Tested with gcc-2.96 + * + * Revision 1.7 2004/02/25 00:04:06 ryan + * Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). + * + * Revision 1.6 2004/02/09 04:14:51 kevinb + * Added newlines to all headers to reduce number of warnings printed + * + * Made some small changes to get everything compiling. + * + * All Ready to merge the 1.5 tree. + * + * Revision 1.5 2001/01/13 21:48:46 icculus + * patched to (re)compile on win32. + * + * Revision 1.4 2000/09/22 19:05:40 icculus + * updated + * + * Revision 1.3 2000/06/24 01:15:15 icculus + * patched to compile. + * + * Revision 1.2 2000/06/03 14:33:51 icculus + * Merge with Outrage 1.4 tree... + * + * Revision 1.1.1.1 2000/04/18 00:00:38 icculus + * initial checkin + * + * + * 49 8/31/99 5:30p Jason + * network statistics code + * + * 48 8/21/99 3:14a Jeff + * real async lookup in linux + * + * 47 7/29/99 10:33a Kevin + * Macintosh Updates + * + * 45 7/28/99 ????? Kevin + * Mac Stuff + * + * 44 5/22/99 1:57a Kevin + * increased timeout in reliable code + * + * 43 5/19/99 5:21p Kevin + * changed return value of nw_GetHostAddressFromNumbers() + * + * 42 5/10/99 10:53p Ardussi + * changes to compile on Mac + * + * 41 4/30/99 5:06p Kevin + * misc dedicated server, networking and low memory enhancements + * + * 40 4/17/99 3:44p Kevin + * Demo2 changes & fixes + * + * 39 4/17/99 6:14a Jeff + * fixed errors compiling due to typos + * + * 38 4/17/99 5:50a Jeff + * added defines and macros for Linux port + * + * 37 4/16/99 6:00p Kevin + * Bunches of Demo stuff + * + * 36 4/15/99 2:57a Jeff + * added some defines and includes needed for linux ipx + * + * 35 4/12/99 3:16p Kevin + * Added a bunch of stuff for the linux version + * + * 34 2/24/99 12:48p Kevin + * Added urgent flag to nw_SendReliable. It causes the packet to go out + * that frame. + * + * 33 2/05/99 7:24p Kevin + * Added NAGLE type packet buffering to reliable networking code + * + * 32 1/26/99 9:44p Jeff + * moved tcplog functions to mono library + * + * 31 1/13/99 6:48a Jeff + * made linux friendly with some #ifdef's + * + * 29 1/08/99 2:58p Kevin + * Added TCP mprintf support so you can log to a remote machine. + * + * 28 12/23/98 6:38p Kevin + * All UDP data (except gamespy) now uses one (registered) port number + * + * 27 12/03/98 9:29a Kevin + * Added better ip selection code + * + * 26 11/02/98 6:41p Jason + * changed demo port number + * + * 25 10/12/98 8:39p Kevin + * removed mprintf's and fixed some smallish bugs + * + * 24 10/01/98 11:37a Kevin + * UI fixes and stuff + * + * 23 9/28/98 11:02a Kevin + * added Networking defer, and fixed some UI issues + * + * 22 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 21 9/21/98 11:19a Kevin + * check protocol before entering multiplayer screens + * + * 20 9/04/98 1:51p Kevin + * implemented asyncronous gethostbyname + * + * 19 8/14/98 4:54p Kevin + * More directplay stuff + * + * 18 8/13/98 6:32p Kevin + * Initial implementation of directplay API + * +*/ +#ifndef NETWORKING_H +#define NETWORKING_H + +#include "pstypes.h" + +#if defined(WIN32) +//Windows includes +#include + +//helper macros for working with SOCKADDR_IN to make it look nicer between windows and Linux +inline void INADDR_SET_SUN_SADDR(struct in_addr* st,unsigned int value) +{ + st->S_un.S_addr = value; +} +inline void INADDR_GET_SUN_SADDR(struct in_addr* st,unsigned int *value) +{ + *value = st->S_un.S_addr; +} +inline void INADDR_SET_SUN_SUNW(struct in_addr* st,unsigned short s_w1,unsigned short s_w2) +{ + st->S_un.S_un_w.s_w1 = s_w1; + st->S_un.S_un_w.s_w2 = s_w2; +} +inline void INADDR_GET_SUN_SUNW(struct in_addr* st,unsigned short *s_w1,unsigned short *s_w2) +{ + *s_w1 = st->S_un.S_un_w.s_w1; + *s_w2 = st->S_un.S_un_w.s_w2; +} +inline void INADDR_SET_SUN_SUNB(struct in_addr* st,unsigned char s_b1,unsigned char s_b2,unsigned char s_b3,unsigned char s_b4) +{ + st->S_un.S_un_b.s_b1 = s_b1; + st->S_un.S_un_b.s_b2 = s_b2; + st->S_un.S_un_b.s_b3 = s_b3; + st->S_un.S_un_b.s_b4 = s_b4; +} +inline void INADDR_GET_SUN_SUNB(struct in_addr* st,unsigned char *s_b1,unsigned char *s_b2,unsigned char *s_b3,unsigned char *s_b4) +{ + *s_b1 = st->S_un.S_un_b.s_b1; + *s_b2 = st->S_un.S_un_b.s_b2; + *s_b3 = st->S_un.S_un_b.s_b3; + *s_b4 = st->S_un.S_un_b.s_b4; +} + +#elif defined(__LINUX__) +//Linux includes/defines + +#if __SUPPORT_IPX +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//rcg06212000 my SDL adds. +#include "SDL.h" +#include "SDL_thread.h" + +#include "linux/linux_fix.h" + +#ifndef SOCKET +#define SOCKET int +#endif + +#define BOOL bool +#define SOCKADDR_IN sockaddr_in +#define SOCKADDR_IPX sockaddr_ipx +#define SOCKADDR sockaddr +#define INVALID_SOCKET -1 +#define NSPROTO_IPX AF_IPX + +#ifdef TRUE +#undef TRUE +#endif +#define TRUE true + +#ifdef FALSE +#undef FALSE +#endif +#define FALSE false +#define HOSTENT struct hostent +#define SOCKET_ERROR -1 + +//Winsock = sockets error translation +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINVAL EINVAL +#define WSAENOPROTOOPT ENOPROTOOPT + +inline int WSAGetLastError(){return errno;} +extern bool Use_DirectPlay; + +//helper macros for working with SOCKADDR_IN to make it look nicer between windows and Linux +inline void INADDR_SET_SUN_SADDR(struct in_addr* st,unsigned int value) +{ + st->s_addr = value; +} +inline void INADDR_GET_SUN_SADDR(struct in_addr* st,unsigned int *value) +{ + *value = st->s_addr; +} +inline void INADDR_SET_SUN_SUNW(struct in_addr* st,unsigned short s_w1,unsigned short s_w2) +{ + union{ + struct{ unsigned char s_b1,s_b2,s_b3,s_b4;}S_un_b; + struct{ unsigned short s_w1,s_w2;} S_un_w; + unsigned int S_addr; + }S_un; + + S_un.S_un_w.s_w1 = s_w1; + S_un.S_un_w.s_w2 = s_w2; + st->s_addr = S_un.S_addr; +} +inline void INADDR_GET_SUN_SUNW(struct in_addr* st,unsigned short *s_w1,unsigned short *s_w2) +{ + union{ + struct{ unsigned char s_b1,s_b2,s_b3,s_b4;}S_un_b; + struct{ unsigned short s_w1,s_w2;} S_un_w; + unsigned int S_addr; + }S_un; + + S_un.S_addr = st->s_addr; + *s_w1 = S_un.S_un_w.s_w1; + *s_w2 = S_un.S_un_w.s_w2; +} +inline void INADDR_SET_SUN_SUNB(struct in_addr* st,unsigned char s_b1,unsigned char s_b2,unsigned char s_b3,unsigned char s_b4) +{ + union{ + struct{ unsigned char s_b1,s_b2,s_b3,s_b4;}S_un_b; + struct{ unsigned short s_w1,s_w2;} S_un_w; + unsigned int S_addr; + }S_un; + + S_un.S_un_b.s_b1 = s_b1; + S_un.S_un_b.s_b2 = s_b2; + S_un.S_un_b.s_b3 = s_b3; + S_un.S_un_b.s_b4 = s_b4; + st->s_addr = S_un.S_addr; +} +inline void INADDR_GET_SUN_SUNB(struct in_addr* st,unsigned char *s_b1,unsigned char *s_b2,unsigned char *s_b3,unsigned char *s_b4) +{ + union{ + struct{ unsigned char s_b1,s_b2,s_b3,s_b4;}S_un_b; + struct{ unsigned short s_w1,s_w2;} S_un_w; + unsigned int S_addr; + }S_un; + + S_un.S_addr = st->s_addr; + *s_b1 = S_un.S_un_b.s_b1; + *s_b2 = S_un.S_un_b.s_b2; + *s_b3 = S_un.S_un_b.s_b3; + *s_b4 = S_un.S_un_b.s_b4; +} +#elif defined(MACINTOSH) + +#include +#include +#include +#include "otsockets.h" + +#include "macsock.h" + +#define BOOL bool +#define SOCKET unsigned int +#define SOCKADDR_IN sockaddr_in +//#define SOCKADDR_IPX sockaddr_ipx +#define SOCKADDR sockaddr +//#define INVALID_SOCKET -1 +//#define NSPROTO_IPX AF_IPX +#define TRUE true +#define FALSE false +#define HOSTENT struct hostent +//#define SOCKET_ERROR -1 + +//Winsock = sockets error translation +//#define WSAEWOULDBLOCK EWOULDBLOCK +//#define WSAEINVAL EINVAL +//#define WSAENOPROTOOPT ENOPROTOOPT + +extern bool Use_DirectPlay; + +//#ifdef FIXED +//inline int WSAGetLastError(){return errno;} + +//helper macros for working with SOCKADDR_IN to make it look nicer between windows and Linux +inline void INADDR_SET_SUN_SADDR(struct in_addr* st,unsigned int value) +{ + st->S_un.S_addr = value; +} +inline void INADDR_GET_SUN_SADDR(struct in_addr* st,unsigned int *value) +{ + *value = st->S_un.S_addr; +} +inline void INADDR_SET_SUN_SUNW(struct in_addr* st,unsigned short s_w1,unsigned short s_w2) +{ + st->S_un.S_un_w.s_w1 = s_w1; + st->S_un.S_un_w.s_w2 = s_w2; +} +inline void INADDR_GET_SUN_SUNW(struct in_addr* st,unsigned short *s_w1,unsigned short *s_w2) +{ + *s_w1 = st->S_un.S_un_w.s_w1; + *s_w2 = st->S_un.S_un_w.s_w2; +} +inline void INADDR_SET_SUN_SUNB(struct in_addr* st,unsigned char s_b1,unsigned char s_b2,unsigned char s_b3,unsigned char s_b4) +{ + st->S_un.S_un_b.s_b1 = s_b1; + st->S_un.S_un_b.s_b2 = s_b2; + st->S_un.S_un_b.s_b3 = s_b3; + st->S_un.S_un_b.s_b4 = s_b4; +} +inline void INADDR_GET_SUN_SUNB(struct in_addr* st,unsigned char *s_b1,unsigned char *s_b2,unsigned char *s_b3,unsigned char *s_b4) +{ + *s_b1 = st->S_un.S_un_b.s_b1; + *s_b2 = st->S_un.S_un_b.s_b2; + *s_b3 = st->S_un.S_un_b.s_b3; + *s_b4 = st->S_un.S_un_b.s_b4; +} +//#endif // FIXED +#endif // OS + + +#define NWT_UNRELIABLE 1 +#define NWT_RELIABLE 2 + + +// This is the max size of a packet - DO NOT INCREASE THIS NUMBER ABOVE 512! +#define MAX_PACKET_SIZE 512 +#if 1 //ndef DEMO +#define DEFAULT_GAME_PORT D3_DEFAULT_PORT +#else +#define DEFAULT_GAME_PORT 6250 +#endif +// Network flags +#define NF_CHECKSUM 1 +#define NF_NOSEQINC 2 + +typedef enum +{ + NP_NONE, + NP_TCP, + NP_IPX, + NP_DIRECTPLAY +} network_protocol; + +typedef struct +{ + ubyte address[6]; + ushort port; + ubyte net_id[4]; + network_protocol connection_type; // IPX, IP, modem, etc. +} network_address; + +extern BOOL DP_active; +extern BOOL TCP_active; +extern BOOL IPX_active; +//Get the info from RAS +unsigned int psnet_ras_status(); + +// function to shutdown and close the given socket. It takes a couple of things into consideration +// when closing, such as possibly reiniting reliable sockets if they are closed here. +void nw_CloseSocket( SOCKET *sockp ); + +// Inits the sockets layer to activity +void nw_InitNetworking (int iReadBufSizeOverride = -1); + +// called by psnet_init to initialize the listen socket used by a host/server +int nw_InitReliableSocket(); + +// function which checks the Listen_socket for possibly incoming requests to be connected. +// returns 0 on error or nothing waiting. 1 if we should try to accept +int nw_CheckListenSocket(network_address *from_addr); + +// Inits the sockets that the application will be using +void nw_InitSockets(ushort port); + +// Connects a client to a server +void nw_ConnectToServer(SOCKET *socket, network_address *server_addr); + +// Returns internet address format from string address format...ie "204.243.217.14" +// turns into 1414829242 +unsigned long nw_GetHostAddressFromNumbers (char *str); + +// Fills in the string with the string address from the internet address +void nw_GetNumbersFromHostAddress(network_address * address,char *str); + +// returns the ip address of this computer +unsigned int nw_GetThisIP(); + +// function which checks the Listen_socket for possibly incoming requests to be connected. +// returns 0 on error or nothing waiting. 1 if we should try to accept +int nw_CheckListenSocket(network_address *from_addr); + + +// Calculates a unique ushort checksum for a stream of data +ushort nw_CalculateChecksum( void * vptr, int len ); + +// Sends data on an unreliable socket +int nw_Send( network_address * who_to, void * data, int len, int flags ); + + +// nw_ReceiveFromSocket will get data out of the socket and stuff it into the packet_buffers +// nw_Receive now calls this function, then determines which of the packet buffers +// to package up and use +void nw_ReceiveFromSocket(); + + +// routine to "free" a packet buffer +void nw_FreePacket( int id ); + +// nw_Recieve will call the above function to read data out of the socket. It will then determine +// which of the buffers we should use and pass to the routine which called us +int nw_Receive( void * data, network_address *from_addr ); + +// nw_SendReliable sends the given data through the given reliable socket. +int nw_SendReliable(unsigned int socketid, ubyte *data, int length,bool urgent=false ); + +// function which reads data off of a reliable socket. recv() should read the totaly amount of data +// available I believe. (i.e. we shouldn't read only part of a message with one call....I may be wrong +// and this may be a source of bugs). +int nw_ReceiveReliable(SOCKET socket, ubyte *buffer, int max_len); + +// Returns the current protocol in use +int nw_GetProtocolType (); + +// Copies my address into the passed argument +void nw_GetMyAddress (network_address *addr); + +//Sends a packet to the game tracker +int nw_SendGameTrackerPacker(void *packet); + +//Checks for an incoming game tracker packet. +int nw_ReceiveGameTracker(void *packet); + +//Send a packet to the pilot tracker +int nw_SendPilotTrackerPacket(void *packet); + +//Checks for an incoming pilot tracker packet. +int nw_ReceivePilotTracker(void *packet); + +int nw_PingCompare( const void *arg1, const void *arg2 ); + +// initialize the buffering system +void nw_psnet_buffer_init(); + +// buffer a packet (maintain order!) +void nw_psnet_buffer_packet(ubyte *data, int length, network_address *from); + +// get the index of the next packet in order! +int nw_psnet_buffer_get_next(ubyte *data, int *length, network_address *from); + +// get the index of the next packet in order! +int nw_psnet_buffer_get_next_by_dpid(ubyte *data, int *length, unsigned long dpid); + +//This is all the reliable UDP stuff... +#define MAXNETBUFFERS 150 //Maximum network buffers (For between network and upper level functions, which is + //required in case of out of order packets +#define NETRETRYTIME .75 //Time after sending before we resend +#define MIN_NET_RETRYTIME .2 +#define NETTIMEOUT 300 //Time after receiving the last packet before we drop that user +#define NETHEARTBEATTIME 10 //How often to send a heartbeat +#define MAXRELIABLESOCKETS 40 //Max reliable sockets to open at once... +#define NETBUFFERSIZE 600 //Max size of a network packet + +//Network Types +#define RNT_ACK 1 //ACK Packet +#define RNT_DATA 2 //Data Packet +#define RNT_DATA_COMP 3 //Compressed Data Packet +#define RNT_REQ_CONN 4 //Requesting a connection +#define RNT_DISCONNECT 5 //Disconnecting a connection +#define RNT_HEARTBEAT 6 //Heartbeat -- send every NETHEARTBEATTIME +#define RNT_I_AM_HERE 7 + +//Reliable socket states +#define RNF_UNUSED 0 //Completely clean socket.. +#define RNF_CONNECTED 1 //Connected and running fine +#define RNF_BROKEN 2 //Broken - disconnected abnormally +#define RNF_DISCONNECTED 3 //Disconnected cleanly +#define RNF_CONNECTING 4 //We received the connecting message, but haven't told the game yet. +#define RNF_LIMBO 5 //between connecting and connected + +void nw_SendReliableAck(SOCKADDR *raddr,unsigned int sig, network_protocol link_type,float time_sent); +void nw_WorkReliable(ubyte * data,int len,network_address *naddr); +int nw_Compress(void *srcdata,void *destdata,int count); +int nw_Uncompress(void *compdata,void *uncompdata,int count); + +#define NW_AGHBN_CANCEL 1 +#define NW_AGHBN_LOOKUP 2 +#define NW_AGHBN_READ 3 + +typedef struct _async_dns_lookup +{ + unsigned int ip; //resolved host. Write only to worker thread. + char * host;//host name to resolve. read only to worker thread + bool done; //write only to the worker thread. Signals that the operation is complete + bool error; //write only to worker thread. Thread sets this if the name doesn't resolve + bool abort; //read only to worker thread. If this is set, don't fill in the struct. + + // rcg06212000 added to let us join the thread at completion... + #ifdef __LINUX__ + SDL_Thread *threadId; + #endif +}async_dns_lookup; + +#ifdef WIN32 +#define CDECLCALL __cdecl +#else +#define CDECLCALL +#endif +#ifdef __LINUX__ +// rcg06192000 used to return void *. +int CDECLCALL gethostbynameworker(void *parm); +#else +void CDECLCALL gethostbynameworker(void *parm); +#endif + +int nw_Asyncgethostbyname(unsigned int *ip,int command, char *hostname); +int nw_ReccomendPPS(); +void nw_DoNetworkIdle(void); + +typedef void * ( * NetworkReceiveCallback ) (ubyte *data,int len, network_address *from); +int nw_RegisterCallback(NetworkReceiveCallback nfp, ubyte id); +NetworkReceiveCallback nw_UnRegisterCallback(ubyte id); +int nw_SendWithID(ubyte id,ubyte *data,int len,network_address *who_to); +int nw_DoReceiveCallbacks(void); +void nw_HandleConnectResponse(ubyte *data,int len,network_address *server_addr); +int nw_RegisterCallback(NetworkReceiveCallback nfp, ubyte id); +void nw_HandleUnreliableData(ubyte *data,int len,network_address *from_addr); +void nw_ReliableResend(void); +int nw_CheckReliableSocket(int socknum); + +typedef struct +{ + // TCP/IP Status lines + int udp_total_packets_sent; // total number of packets sent out on the network (unreliable) + int udp_total_packets_rec; // total number of packets recieved on the network (unrealiable) + int udp_total_bytes_sent; // total number of bytes sent (unreliable) + int udp_total_bytes_rec; // total number of bytes recieved (unreliable) + + int tcp_total_packets_sent; // total number of packets sent out on the network (reliable) + int tcp_total_packets_rec; // total number of packets recieved on the network (reliable) + int tcp_total_bytes_sent; // total number of bytes sent (reliable) + int tcp_total_bytes_rec; // total number of bytes recieved (reliable) + int tcp_total_packets_resent; // total number of packets resent (reliable) + int tcp_total_bytes_resent; // total number of bytes resent (reliable) + + // IPX Status lines + int ipx_total_packets_sent; // total number of packets sent out on the network (unreliable) + int ipx_total_packets_rec; // total number of packets recieved on the network (unrealiable) + int ipx_total_bytes_sent; // total number of bytes sent (unreliable) + int ipx_total_bytes_rec; // total number of bytes recieved (unreliable) + + int spx_total_packets_sent; // total number of packets sent out on the network (reliable) + int spx_total_packets_rec; // total number of packets recieved on the network (reliable) + int spx_total_bytes_sent; // total number of bytes sent (reliable) + int spx_total_bytes_rec; // total number of bytes recieved (reliable) + int spx_total_packets_resent; // total number of packets resent (reliable) + int spx_total_bytes_resent; // total number of bytes resent (reliable) +}tNetworkStatus; // network status information + +// fills in the buffer with network stats +// pass NULL to reset the stats +void nw_GetNetworkStats(tNetworkStatus *stats); + +#endif + + diff --git a/lib/polymodel.h b/lib/polymodel.h new file mode 100644 index 000000000..657f2b5bf --- /dev/null +++ b/lib/polymodel.h @@ -0,0 +1,424 @@ +/* + * $Logfile: /DescentIII/Main/lib/polymodel.h $ + * $Revision: 83 $ + * $Date: 7/08/99 5:47p $ + * $Author: Jason $ + * + * Header for polygon model loading funcs + * + * $Log: /DescentIII/Main/lib/polymodel.h $ + * + * 83 7/08/99 5:47p Jason + * changes for new bumpmapping system in 1.1 update patch + * + * 82 6/08/99 1:00p Jason + * changes for bumpmapping + * + * 81 4/09/99 12:06p Jason + * made model setup code faster + * + * 80 1/21/99 11:16p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 79 1/20/99 5:36p Jason + * added custom glows for teams + * + * 78 1/13/99 12:43p Jason + * added some more detail settings + * + * 77 1/07/99 5:01p Keneta + * added more polymodel slots + * + * 76 11/30/98 3:59p Jason + * changed dynamic lighting to be better + * + * 75 10/12/98 9:30a Jason + * new polymodel memory scheme + * + * 74 10/07/98 5:07p Jason + * added new polymodel effect type + * + * 73 10/07/98 3:40p Chris + * Added $thruster + * + * 72 9/09/98 7:09p Jason + * changed afterburner effect for ships + * + * 71 8/31/98 1:35p Keneta + * Made some use count unsigned shorts + * + * 70 8/24/98 1:01p Jason + * made object specular faces selectable by texture + * + * 69 8/21/98 5:14p Jason + * made better memory use of primitives + * + * 68 8/18/98 11:38a Jason + * fixed polymodel fog lighting + * + * 67 8/18/98 11:26a Matt + * Changed outline code to only outline polymodel objects when they're + * being rendered in the mine, and not when they're being drawn in the + * editor, main menu, cockpit, etc. + * + * 66 8/14/98 4:00p Jason + * added specular objects outside + * + * 65 8/13/98 6:56p Jason + * made objects foggable correctly + * + * 64 8/12/98 12:04p Chris + * Attach system version .5 (still needs more work to be multiplayer + * friendly) + * + * 63 8/04/98 2:32p Chris + * Improved attach code added more fixes to the AABB partial computation + * patch + * + * 62 7/29/98 5:05p Chris + * Made more of the wb info dynamically allocated. Added Attach points to + * the polymodel structure + * + * 61 7/27/98 10:39a Jason + * added customizable skins + * + * 60 6/22/98 7:31p Samir + * added mask for monitors. + * + * 59 6/15/98 4:00p Jason + * replaced monochromatic polymodel lighting with rgb lighting + * + * 58 5/13/98 12:06p Jason + * trimmed some memory usage + * + * 57 4/30/98 12:22p Jason + * did some lo-res model optimizations + * + * 56 4/30/98 11:32a Chris + * ClearWB to WBClear + * + * 55 4/17/98 12:45p Jason + * various changes for multiplayer + * + * 54 4/15/98 3:28p Jason + * changed glow stuff to work with new system + * + * 53 4/07/98 6:22p Samir + * restored a couple of variables to the poly_model array that were used + * elsewhere in the code. left out the nasty one though. + * + * 52 4/07/98 5:59p Jason + * saved about 15 megs of storage by deleting an unused array + * + * 51 4/06/98 3:03p Jason + * added polygon rendering overlays + * + * 50 4/03/98 1:13p Jason + * shaved off a few more bytes from the polyface structure + * + * 49 4/03/98 10:07a Chris + * Added support for objects getting their size computed when the + * polymodel is paged in the first time as an object + * + * 48 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 47 4/02/98 12:22p Jason + * changes for deformation effect + * + * 46 4/01/98 6:23p Jason + * added a slew of stuff for multiplayer + * + * 45 3/31/98 11:12a Brent + * upped number of textures for the undisciplined bastard know as BRENT + * + * 44 3/29/98 11:58p Jason + * saved some memory with object lightmaps + * + * 43 3/26/98 2:14p Jason + * added dynamic lighting for objects + * + * 42 3/24/98 5:16p Jason + * upped polyvecs for that fucker brent + * + * 41 3/23/98 10:03a Chris + * Added independant wb animations + * + * 40 3/19/98 4:30p Samir + * added ability to mark subobjects as layered. + * + * 39 3/18/98 6:24p Samir + * Fixed some bugs with normalizing MONITORs and added VIEWER flag. + * + * 38 3/16/98 3:47p Jason + * got rid of circular dependencies for objects and polymodels + * + * 37 3/16/98 3:24p Chris + * Removed my the object.h dependancy + * + * 36 3/16/98 11:34a Chris + * Polymodels have 3 levels of AABB (object, subobject, and face level) + * + * 35 3/13/98 5:55p Chris + * Added the new collision spheres + * + * 34 3/11/98 4:59p Chris + * Added a few new sphere types (wall and anim) + * + * 33 3/10/98 6:54p Chris + * All start and end frame for ComputeDefaultSize + * + * 32 2/17/98 12:19p Jason + * upped max polymodel count to 500 + * + * 31 2/16/98 2:49p Chris + * Made the MAX_SUBOBJECTS determine the number of normalized_time values + * to be processed. No longer a 'literal' problem. + * + * 30 2/16/98 2:12p Jason + * fixed bug with lightmaps and door shells + * + * 29 2/13/98 12:44p Jason + * upped max number of subobjects + * + * 28 2/04/98 9:28p Jason + * added some new weapons effects + * + * 27 1/13/98 3:09p Jason + * added glow effect for engines + * + * 26 12/31/97 3:34p Jason + * added alpha per vertex for polymodels + * + * 25 11/17/97 6:28p Mark + * FROM Jason:fixed memory leak + * + * 24 11/06/97 4:18p Samir + * texture numbers are now shorts in polymodel + * + * 23 11/04/97 4:58p Samir + * Added 4 more monitors. + * + * 22 10/28/97 6:37p Samir + * Submodels now can be monitors. + * + * 21 10/23/97 2:07p Jason + * fixed some problems with debris not spinning about their center point + * + * 20 10/01/97 7:00p Jason + * did more work on object lightmaps + * + * 19 9/30/97 6:40p Jason + * got lightmap stuff sort of working with objects + * + * 18 9/26/97 12:09p Jason + * made positional/rotational animations have differing + * track_min/track_max + * + * 17 9/25/97 4:54p Jason + * added timer info to polymodels + * + * 16 9/17/97 10:59a Chris + * Added a new way to compute radi + * + * 15 9/11/97 5:38p Jason + * initial door coding for room engine + * + * 14 9/10/97 5:36p Jason + * changed version number for new oofs + * + * 13 9/10/97 5:17p Jason + * more lighting improvements + * + * 12 9/09/97 6:15p Jason + * made dynamic lighting on objects more memory efficient + * + * 11 9/04/97 3:49p Chris + * Added additional turret information from pofgen, added ground plane + * stuff + * + * 10 9/03/97 4:11p Chris + * Moved some code so that pofview will compile + * + * 9 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 8 8/25/97 6:19p Chris + * Added support for knowing which subobject a gun point is linked to + * + * 7 8/20/97 3:16p Chris + * Added some hooks for turrets + * + * 6 8/03/97 2:25p Jason + * made polymodels use less memory + * + * 5 7/28/97 1:16p Chris + * + * 4 7/23/97 11:48a Jason + * added support for newstyle pof format + * + * 3 7/22/97 1:34a Jason + * added code to support newstyle polymodels + * + * 18 6/24/97 6:13p Matt + * Got rid of DrawPolygonObject(), since it required the inclusion of + * object.h, which is a main directory header file (& thus shouldn't be + * accessible by a library). + * + * $NoKeywords: $ + */ + +#ifndef POLYMODEL_H +#define POLYMODEL_H + +#include "manage.h" +#include "pstypes.h" +#include "vecmat.h" +#include "3d.h" +#include "robotfirestruct.h" +#include "polymodel_external.h" +#include "object_external_struct.h" + +#define PM_COMPATIBLE_VERSION 1807 +#define PM_OBJFILE_VERSION 2300 + +#define WB_INDEX_SHIFT 16 // bits to shift over to get the weapon battery index (after masking out flags) +#define SOF_WB_MASKS 0x01F0000 // Room for 32 weapon batteries (currently we only use 21 slots) + +#define SOF_MONITOR_MASK 0x0ff0 // mask for monitors + + +extern int Num_poly_models; +extern poly_model Poly_models[]; + +extern int Polymodel_use_effect; +extern polymodel_effect Polymodel_effect; +extern polymodel_light_type Polymodel_light_type; +extern float Polylighting_static_red; +extern float Polylighting_static_green; +extern float Polylighting_static_blue; +extern ubyte *Polylighting_gouraud; +extern vector *Polymodel_light_direction,Polymodel_fog_portal_vert,Polymodel_fog_plane,Polymodel_specular_pos,Polymodel_bump_pos; +extern lightmap_object *Polylighting_lightmap_object; + +extern vector Model_eye_position; +extern vector Interp_pos_instance_vec; +extern g3Point Robot_points[]; + +//Flag to draw an outline around the faces +extern bool Polymodel_outline_mode; + +inline float POLY_WIDTH(int model_num) +{ + return Poly_models[model_num].maxs.x - Poly_models[model_num].mins.x; +} + +inline float POLY_HEIGHT(int model_num) +{ + return Poly_models[model_num].maxs.y - Poly_models[model_num].mins.y; +} + +inline float POLY_DEPTH(int model_num) +{ + return Poly_models[model_num].maxs.z - Poly_models[model_num].mins.z; +} + +// given a filename, reads in a POF and returns an index into the Poly_models array +// returns -1 if something is wrong +int LoadPolyModel (char *filename,int pageable); + +// gets the filename from a path, plus appends our .pof extension +void ChangePolyModelName (char *src,char *dest); + +// Searches thru all polymodels for a specific name, returns -1 if not found +// or index of polymodel with name +int FindPolyModelName (char *name); + +// Draws a polygon model to the viewport +// Normalized_time is an array of floats from 0 to 1 that represent how far into +// an animation state we are + +// This one is for static lighting - ie 1 light value for the entire model +void DrawPolygonModel(vector *pos,matrix *orient,int model_num,float *normalized_time,int flags,float r,float g,float b, uint f_render_sub = 0xFFFFFFFF,ubyte use_effect=0,ubyte overlay=0); + +// This one is for gouraud shading - the lightdir is the normalized light direction, and lightscalar is a 0-1 scalar to apply +void DrawPolygonModel(vector *pos,matrix *orient,int model_num,float *normalized_time,int flags,vector *lightdir,float r,float g,float b, uint f_render_sub=0xFFFFFFFF,ubyte use_effect=0,ubyte overlay=0); + +// This one is for lightmap rendering +void DrawPolygonModel(vector *pos,matrix *orient,int model_num,float *normalized_time,int flags,lightmap_object *lm_object, uint f_render_sub,ubyte use_effect=0,ubyte overlay=0); + +//gives the interpreter an array of points to use +void g3_SetInterpPoints(g3Point *pointlist); + +//calls the object interpreter to render an object. The object renderer +//is really a seperate pipeline. returns true if drew +int InterpPolygonModel(poly_model * pm); + +// Inits our models array +int InitModels (); + +// Frees polymodel located in index of Poly_models array +void FreePolyModel (int index); + +// Given an actual keyframe number, returns the normalized (0 to 1) position of that +// keyframe. Handle is an index into the Poly_models array +float GetNormalizedKeyframe (int handle,float num); + +// Goes through all poly models and gets all missing textures +void RemapPolyModels (); + +// For macintosh, we must swap the interpreted model code +void SwapPolymodelData (ubyte *data); + +// Sets a positional instance +void StartPolyModelPosInstance (vector *posvec); + +// Pops a positional instance +void DonePolyModelPosInstance (); + +void SetModelAngles (poly_model *po,float *normalized_angles); +void SetModelInterpPos (poly_model *po,float *normalized_pos); + +void SetNormalizedTimeObj(object *obj, float *normalized_time); +void SetNormalizedTimeAnim(float norm_anim_frame, float *normalized_time, poly_model *pm); + +void WBClearInfo(poly_model *pm); + +// Computes the size of a polymodel. +float ComputeDefaultSize(int type, int handle, float *size_ptr); + +// Returns the total number of faces in a model +int CountFacesInPolymodel (poly_model *pm); + +// Rendering functions +int RenderPolygonModel (poly_model *, uint f_render_sub = 0xFFFFFFFF); +void RenderSubmodel (poly_model *pm,bsp_info *sm, uint f_render_sub); + +// returns point within polymodel/submodel in world coordinates. +void GetPolyModelPointInWorld (vector *dest,poly_model *pm, vector *wpos, matrix *orient,int subnum, vector *pos,vector *norm=NULL); +void GetPolyModelPointInWorld (vector *dest,poly_model *pm, vector *wpos, matrix *orient,int subnum, float *normalized_time, vector *pos, vector *norm=NULL); + +// Returns 1 if this submodel shouldn't be rendered +int IsNonRenderableSubmodel (poly_model *pm,int submodelnum); + +// Sets the effect used by a polymodel +void SetPolymodelEffect (polymodel_effect *); + +// Pages in a polymodel if its not already in memory +void PageInPolymodel (int polynum, int type = -1, float *size_ptr = NULL); + +// Gets a pointer to a polymodel. Pages it in if neccessary +poly_model *GetPolymodelPointer (int polynum); + +// Frees all the polymodel data, but doesn't free the actual polymodel itself +void FreePolymodelData (int i); + +// Sets the position and rotation of a polymodel. Used for rendering and collision detection +void SetModelAnglesAndPos (poly_model *po,float *normalized_time,uint subobj_flags=0xFFFFFFFF); + +#endif diff --git a/lib/polymodel_external.h b/lib/polymodel_external.h new file mode 100644 index 000000000..be0d907d1 --- /dev/null +++ b/lib/polymodel_external.h @@ -0,0 +1,328 @@ +/* +* $Logfile: /DescentIII/Main/lib/polymodel_external.h $ +* $Revision: 7 $ +* $Date: 10/02/01 8:56a $ +* $Author: Matt $ +* +* Polymodel defines, flags and structs (anything that could be exported to a DLL) +* +* $Log: /DescentIII/Main/lib/polymodel_external.h $ + * + * 7 10/02/01 8:56a Matt + * Increased MAX_POLY_MODELS from 1000 to 1200 + * + * 6 7/08/99 5:47p Jason + * changes for new bumpmapping system in 1.1 update patch + * + * 5 6/08/99 1:00p Jason + * changes for bumpmapping + * + * 4 4/20/99 1:02p 3dsmax + * fixed polymodel loimt + * + * 3 4/08/99 11:45a Jason + * greatly sped up the time it takes to get model anges/positions by + * precalculation + * + * 2 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h +* +* $NoKeywords: $ +*/ + + +#ifndef __POLYMODEL_EXTERNAL_H_ +#define __POLYMODEL_EXTERNAL_H_ + +#include "vecmat_external.h" +#include "pstypes.h" +#include "grdefs.h" +#include "manage_external.h" + +#define MAX_POLY_MODELS 1200 + +#define MAX_MODEL_TEXTURES 35 +#define MAX_POLYGON_VECS 2500 +#define MAX_DETAIL_LEVELS 3 +#define MAX_PROP_LEN 256 +#define MAX_NAME_LEN 32 + +#define MAX_GROUND_PLANES_PER_MODEL 10 +#define MAX_GUNS_PER_MODEL 64 +#define MAX_SUBOBJECTS 30 +#define MAX_POINTS_PER_SUBOBJECT 300 + +// Subobject flags +#define SOF_ROTATE 0x01 // This subobject is a rotator +#define SOF_TURRET 0x02 // This subobject is a turret that tracks +#define SOF_SHELL 0x04 // This subobject is a door housing +#define SOF_FRONTFACE 0x08 // This subobject contains the front face for the door +#define SOF_MONITOR1 0x010 // This subobject contains it's first monitor +#define SOF_MONITOR2 0x020 // This subobject contains it's second monitor +#define SOF_MONITOR3 0x040 // This subobject contains it's third monitor +#define SOF_MONITOR4 0x080 // This subobject contains it's fourth monitor +#define SOF_MONITOR5 0x0100 +#define SOF_MONITOR6 0x0200 +#define SOF_MONITOR7 0x0400 +#define SOF_MONITOR8 0x0800 +#define SOF_FACING 0x01000 // This subobject always faces you +#define SOF_VIEWER 0x02000 // This subobject is marked as a 'viewer'. +#define SOF_LAYER 0x04000 // This subobject is marked as part of possible secondary model rendering. +#define SOF_WB 0x08000 // This subobject is part of a weapon battery +#define SOF_GLOW 0x0200000 // This subobject glows +#define SOF_CUSTOM 0x0400000 // This subobject has textures/colors that are customizable +#define SOF_THRUSTER 0x0800000 // This is a thruster subobject +#define SOF_JITTER 0x01000000 // This object jitters by itself +#define SOF_HEADLIGHT 0x02000000 // This suboject is a headlight + +// gun bank +typedef struct w_bank +{ + int parent; + vector pnt; + vector norm; +} w_bank; + +// attach bank +typedef struct a_bank +{ + int parent; + vector pnt; + vector norm; + vector uvec; + bool f_uvec; +} a_bank; + +typedef struct +{ + ubyte num_verts; + ushort lmi_handle; + vector rvec,uvec; + float *u2,*v2; +} lightmap_object_face; + +typedef struct +{ + ubyte num_models; + + short num_faces[MAX_SUBOBJECTS]; + lightmap_object_face *lightmap_faces[MAX_SUBOBJECTS]; + ubyte used; + +} lightmap_object; + +typedef struct polyface +{ + sbyte nverts; + short *vertnums; + float *u; + float *v; + + //float *u2,*v2; // For lightmaps only + + ddgr_color color; + short texnum; + + vector normal; +} polyface; + +// glow info +typedef struct +{ + float glow_r,glow_g,glow_b,glow_size,glow_length; + vector center,normal; +} glowinfo; + +// bsp information +typedef struct bsp_info { + char name[PAGENAME_LEN]; // name of the subsystem. Probably displayed on HUD + int movement_type; //-1 if no movement, otherwise rotational or positional movement -- subobjects only + int movement_axis; //which axis this subobject moves or rotates on. + int tree_offset; //offset of tree data (children included) into the model_data + int data_offset; //offset of data into the model_data + vector offset; //3d offset from parent object + vector norm; //norm for sep plane + float d; //norm d for sep plane + vector pnt; //point for sep plane + vector geometric_center; //geometric center of this subobject. In the same Frame Of + //Reference as all other vertices in this submodel. (Relative to pivot point) + float rad; //radius for each submodel + + vector *verts; // vertices for the submodel (NEWSTYLE) + vector *vertnorms; + float *alpha; + polyface *faces; // faces for the submodel (NEWSTYLE) + vector *face_min; + vector *face_max; + + short *vertnum_memory; + float *u_memory; + float *v_memory; + + int nverts; + int num_faces; // amount of faces (NEWSTYLE); + + sbyte children[MAX_SUBOBJECTS]; // children of this submodel + int parent; //what is parent for each submodel + ubyte num_children; + + vector min; + vector max; + int blown_off; // If set, this subobject is blown off. Stuffed by model_set_instance + + angvec angs; + matrix mod_matrix; // The angles from parent. Stuffed by model_set_instance + vector mod_pos; // The modified position of this object. Used for positional interpolation + + vector *keyframe_axis; // the axis of rotation for each keyframe + int *keyframe_angles; // The destination angles for each key frame + vector *keyframe_pos; + matrix *keyframe_matrix; // the combined rotation matrices up to frame n + ushort *tick_pos_remap; // For looking up keyframes fast + ushort *tick_ang_remap; // For looking up keyframes fast + int *rot_start_time; + int *pos_start_time; + + int num_key_angles; + int num_key_pos; + + int flags; // see SOF_FLAGS above + + int rot_track_min,rot_track_max; + int pos_track_min,pos_track_max; + + float rps; // if SOF_ROTATE or SOF_TURRET is set, this is the rotations per second + float fov; // Half the normalized angle that this turret is allowed to turn (amount in each direction) + float think_interval; // How quickly a turret updates which way it should move + + float normalized_angle; + + glowinfo *glow_info; +} bsp_info; + + +#define PMF_LIGHTMAP_RES 1 +#define PMF_TIMED 2 // Uses new timed animation +#define PMF_ALPHA 4 // Has alpha per vertex qualities +#define PMF_FACING 8 // Has a submodel that is always facing +#define PMF_NOT_RESIDENT 16 // This polymodel is not in memory +#define PMF_SIZE_COMPUTED 32 // This polymodel's size is computed + +//used to describe a polygon model +typedef struct poly_model +{ + unsigned short used; + + int flags; // PMF_flags, see above + ubyte new_style; // if 1, then this polymodel is in the new outrage format (oof) + int id; // what the polygon model number is. (Index in Poly_models) + int version; + char name[PAGENAME_LEN]; + + int n_models; + int model_data_size; + ubyte *model_data; + + vector mins,maxs; //min,max for whole model + vector view_pos; //viewing position. Default to {0,0,0}. + + float wall_size; + vector wall_size_offset; + + float anim_size; + vector anim_size_offset; + + float rad; + int n_textures; + short textures[MAX_MODEL_TEXTURES]; // a list of bitmap indices + + bsp_info *submodel; // an array of size n_models of submodel info. + int num_key_angles; + int num_key_pos; + int max_keys; // the greater number of num_key_pos or num_key_angles + + int frame_min,frame_max; // For TIMED polymodels, the min/max frames + + int n_guns; + w_bank *gun_slots; // array of gun banks + + int n_ground; + w_bank *ground_slots; // array of ground planes + + int n_attach; + a_bank *attach_slots; + + int num_wbs; + poly_wb_info *poly_wb; // array of weapon batteries + + int *render_order; // internal use + +} poly_model; + + +// Which kind of lighting model for this polymodel +typedef enum +{ + POLYMODEL_LIGHTING_STATIC, + POLYMODEL_LIGHTING_GOURAUD, + POLYMODEL_LIGHTING_LIGHTMAP, +} polymodel_light_type; + +// polymodel effects stuff +// Effect flags: +#define PEF_ALPHA 1 +#define PEF_DEFORM 2 +#define PEF_COLOR 4 +#define PEF_MED_RES 8 +#define PEF_LO_RES 16 +#define PEF_FOG 32 +#define PEF_CUSTOM_COLOR 64 +#define PEF_CUSTOM_TEXTURE 128 +#define PEF_FOGGED_MODEL 256 +#define PEF_SPECULAR_MODEL 512 +#define PEF_SPECULAR_FACES 1024 +#define PEF_GLOW_SCALAR 2048 +#define PEF_THRUSTER_SCALAR 4096 +#define PEF_DRAW_HEADLIGHTS 8192 +#define PEF_NO_GLOWS (8192<<1) +#define PEF_CUSTOM_GLOW (8192<<2) +#define PEF_BUMPMAPPED (8192<<3) + +typedef struct +{ + int type; + float alpha; + float deform_range; + float r,g,b; + float fog_r,fog_g,fog_b; + int custom_texture; + int custom_color; + + float spec_r,spec_g,spec_b; + vector spec_light_pos; + float spec_scalar; + + vector bump_light_pos; + float bump_scalar; + + float fog_depth; + float fog_eye_distance; + float fog_distance; + vector fog_plane,fog_portal_vert; + int fog_plane_check; + + float glow_length_scalar; + float glow_size_scalar; + + float glow_r,glow_g,glow_b; + +} polymodel_effect; + + + +#endif \ No newline at end of file diff --git a/lib/psclass.h b/lib/psclass.h new file mode 100644 index 000000000..31f5cd328 --- /dev/null +++ b/lib/psclass.h @@ -0,0 +1,160 @@ +/* + * $Logfile: /DescentIII/Main/lib/psclass.h $ + * $Revision: 8 $ + * $Date: 4/16/99 4:26a $ + * $Author: Jeff $ + * + * New Class Library + * + * $Log: /DescentIII/Main/lib/psclass.h $ + * + * 8 4/16/99 4:26a Jeff + * removed ifdef of linux since new compiler compiles it + * + * 7 4/15/99 1:44a Jeff + * changes for linux compile + * + * 6 12/16/98 1:57p Samir + * Replaced CleanupString2 with CleanupStr + * + * 5 10/02/98 11:16a Jeff + * removed 'struct' from the template, generates an error in VC6 that + * keeps it from compiling + * + * 4 9/17/98 7:05p Samir + * added string token class. + * + * 3 7/28/98 5:42p Samir + * added queue class. + * + * 2 7/23/98 11:36a Samir + * Added class library header. + * + * $NoKeywords: $ + */ + +#ifndef PSCLASS_H +#define PSCLASS_H + +// a universal list node to use with the list type +template struct tListNode +{ + T t; + tListNode *next; +}; + + +// LIST CLASSES +// + +// tList +// this is a VERY BASIC list class that encapsulates a lot +// of list operations into one class. this class handles list NODES ONLY. +// a more advanced form is to come, but this is for those hardcode programmers. + +// to declare say, a list that will contain integers, do this: +// tList list; +// tListNode *node; +// node = new tListNode; +// node->data = 1; +// list.Link(node); +// it is the programmers responsibility to create the nodes DYNAMICALLY + +// universal single-linked-list type +template class tList +{ + tListNode *m_link, *m_mark; + int m_length; + +public: + tList() { m_link = m_mark = NULL; m_length = 0; }; + ~tList() { tList::free(); }; + +// moves to start of list. used for iteration + tListNode *start() { m_mark = m_link; return m_mark; } + +// gets next list item in iteration. if at end of list, last item will be returned. + tListNode *next() { m_mark = (m_mark->next) ? m_mark->next : m_mark; return get(); }; + +// returns the node at the current link location in iteration. + tListNode *get() const { return m_mark ? m_mark : NULL; }; + +// length + int length() const { return m_length; }; + +// frees list + void free() { + m_link = m_mark = NULL; + m_length = 0; + }; + +// make a link at the current mark location. + void link(tListNode *node) { + if (m_link) { + node->next = m_mark->next; + m_mark->next = node; + } + else { + m_link = node; + node->next = NULL; + } + m_mark = node; + m_length++; + }; + +// unlink a node on the list at the current mark, returning the unlinked node. + tListNode *unlink() { + tListNode *freenode, *node; + if (!m_link) return NULL; + if (m_link == m_mark) { + freenode = m_mark; + m_mark = m_link = m_link->next; + } + else { + node = m_link; + while (node->next != m_mark) + node = node->next; + freenode = m_mark; + node->next = m_mark->next; + } + freenode->next = NULL; + return freenode; + }; +}; + +// tQueue +// a circular queue implementation + +template class tQueue +{ + T m_items[t_LEN]; + short m_head, m_tail; + +public: + tQueue() { m_head = m_tail = 0; }; + ~tQueue() { }; + + void send(T& item) { // sends an item onto the queue + short temp = m_tail+1; + if (temp == t_LEN) + temp = 0; + if (temp != m_head) { + m_items[m_tail] = item; + m_tail = temp; + } + }; + bool recv(T* item) { // returns an item from the queue, false if no item. + if (m_head == m_tail) + return false; + *item = m_items[m_head++]; + if (m_head == t_LEN) + m_head = 0; + return true; + }; + + void flush() { // flush queue entries. + m_head = m_tail = 0; + }; +}; + +#endif \ No newline at end of file diff --git a/lib/psendian.h b/lib/psendian.h new file mode 100644 index 000000000..e9ee90464 --- /dev/null +++ b/lib/psendian.h @@ -0,0 +1,43 @@ +/* +* $Logfile: /DescentIII/Main/lib/psendian.h $ +* $Revision: 1 $ +* $Date: 5/05/99 5:26a $ +* $Author: Jeff $ +* +* Big/Little Endian detection and functions +* +* $Log: /DescentIII/Main/lib/psendian.h $ + * + * 1 5/05/99 5:26a Jeff + * + * 3 5/01/99 8:47p Jeff + * removed 2 useless functions, use externC for linux compile + * + * 2 5/01/99 2:52p Jeff + * added automatic endian detection of the system +* +* $NoKeywords: $ +*/ + + +#ifndef __ENDIAN_H___ +#define __ENDIAN_H___ + +extern "C" +{ +// Endian_IsLittleEndian +// +// Returns true if the machine is Little Endian (i.e. 80x86) +// Returns false if the machine is Big Endian (i.e. Macintosh) +bool Endian_IsLittleEndian(void); + +// Swaps (if needed) a short value (2 bytes) (assumes incoming value is in little endian format) +short Endian_SwapShort(short value); + +// Swaps (if needed) an int value (4 bytes) (assumes incoming value is in little endian format) +int Endian_SwapInt(int value); + +// Swaps (if needed) a float value (4 bytes) (assumes incoming value is in little endian format) +float Endian_SwapFloat(float value); +} +#endif \ No newline at end of file diff --git a/lib/pserror.h b/lib/pserror.h new file mode 100644 index 000000000..4925218f4 --- /dev/null +++ b/lib/pserror.h @@ -0,0 +1,257 @@ +/* + * $Logfile: /DescentIII/Main/lib/pserror.h $ + * $Revision: 25 $ + * $Date: 3/20/00 12:26p $ + * $Author: Matt $ + * + * error functions + * + * $Log: /DescentIII/Main/lib/pserror.h $ + * + * 25 3/20/00 12:26p Matt + * Merge of Duane's post-1.3 changes. + * Mac include and assert changes + * + * 24 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 23 7/28/99 3:13p Kevin + * Mac + * + * 22 5/10/99 10:53p Ardussi + * changes to compile on Mac + * + * 21 4/14/99 10:46a Kevin + * Removed OutrageMessageBox from release builds + * + * 20 4/01/99 4:50p Matt + * Took out Warning() function, chaning those calls to mprintf() + * + * 19 1/09/99 4:39p Jeff + * added some ifdefs and fixes to get files to compile under Linux + * + * 18 10/18/98 8:52p Matt + * Revamped debug/error system. + * + * 17 10/13/98 12:03p Kevin + * Changed use of preprocessors for debug, etc. + * + * 16 10/12/98 10:20a Samir + * added parameter to debug init. + * + * 15 6/23/98 2:40p Matt + * Added Yes/No/Cancel type to OutrageMessageBox() and + * Debug_MesssageBox(), and changed return value from a bool to an + * integer. + * + * 14 6/01/98 4:28p Samir + * added HEAPCHECK macro. + * + * 13 4/23/98 4:14a Samir + * added warning system. + * + * 12 4/08/98 7:19p Samir + * Added runtime debugging option. + * + * 11 4/07/98 9:20p Samir + * Changes to debug stuff. + * + * 10 4/03/98 5:15p Samir + * Implemented simple debug message filtering. + * + * 9 3/20/98 2:43p Samir + * Some better Int3 support. + * + * 8 3/19/98 11:27a Samir + * Better error checking. + * + * 7 3/17/98 2:37p Samir + * Added debug callback support for assert. + * + * 6 3/10/98 5:16p Samir + * Got debug callbacks working when you hit an Int3. + * + * 5 2/20/98 12:11p Matt + * Changed ASSERT() to print its own mono message and call debug_break + * directly, instead of using Int3(). + * + * 4 12/01/97 2:12p Samir + * Int3 for assertions. + * + * 3 9/16/97 5:07p Matt + * Disable Int3() when _DEBUG not set + * + * 2 9/04/97 12:02p Matt + * Changed the simple form OutrageMessageBox() to be void (instead of + * bool) since it wasn't returning a meaningful value anyway + * + * 19 6/11/97 1:11p Samir + * Implemented new Debug system + * + * 18 3/14/97 4:15p Matt + * Changed title of OutrageMessageBox() dialogs + * + * 17 3/10/97 11:13a Samir + * Changes made to reflect deletion of OSDebug class from system + * + * 16 3/04/97 1:00p Samir + * Fixed return type prototype errors. + * + * 15 3/04/97 12:55p Samir + * Added yesno and ok messageboxes. + * + * 14 3/03/97 5:22p Samir + * Pass debug object to error_init. + * + * 13 2/26/97 7:06p Samir + * call err_Break instead of Int3 to keep PC code out of pserror.h + * + * 12 2/26/97 4:49 PM Jeremy + * Put "assert.h" as since it's an ANSI file not one of ours. + * + * 11 2/06/97 6:32p Samir + * Int3 now tells us where we broke. + * + * 10 2/06/97 11:28a Matt + * Put do..while around Int3 definition so Int3() acts like a normal + * statement. + * + * 9 1/31/97 2:12p Samir + * Fixed prototype for error_init + * + * 8 1/31/97 1:52p Samir + * added error_init function + * + * 7 1/30/97 6:02p Samir + * Added DEBUG_STACK macro + * + * $NoKeywords: $ + */ +#ifndef PSERROR_H +#define PSERROR_H +#include +#include "debug.h" +#include "mono.h" +// initializes error handler. +bool error_Init(bool debugger, bool mono_debug, const char *app_title); +// exits the application and prints out a standard error message +void Error(char *fmt,...); +// prints out an assertion error +void AssertionFailed(char *expstr, char *file, int line); +//Brings up an error message for an int3 +void Int3MessageBox(char *file,int line); +// Message box functions +#define MBOX_OK 1 +#define MBOX_YESNO 2 +#define MBOX_YESNOCANCEL 3 +#define MBOX_ABORTRETRYIGNORE 4 +#ifndef RELEASE +// prints out a standard OS messagebox +void OutrageMessageBox(char *str, ...); +int OutrageMessageBox (int type, char *str, ...); +#endif +// Sets the title for future OutrageMessageBox() dialogs +void SetMessageBoxTitle(char *title); +//Write a block of text to the system clipboard +void DumpTextToClipboard(char *text); +////////////////////////////////////////////////////////////////////////////// +// development debugging functions +// adds a function to be called when a debug break occurs. +// undefine any ASSERT macro previously defined. +#ifdef ASSERT +#undef ASSERT +#endif +// this callback is invoked when a DEBUG_BREAK macro is used. +// arguments +// style = 1 if ASSERT +// = 0 if Int3 debugger break. +extern void (*DebugBreak_callback_stop)(); +extern void (*DebugBreak_callback_resume)(); +// set DEBUG_BREAK callback +inline void SetDebugBreakHandlers(void (*stop)(), void (*resume)()) { + DebugBreak_callback_stop = stop; + DebugBreak_callback_resume = resume; +} +// DEBUG_BREAK() +// Calls the debug_break() macro surrounded by calls to the debug callbacks (to turn off & on graphics) +// ASSERT() +// Like the standard C assert(), but if the condition failed and debugging on, +// does a DEBUG_BREAK(). If debugging on, brings up a dialog. +// Int3() +// Does a DEBUG_BREAK() if debugging is turned on. Also does an mprintf(). +// Define the macros +#ifndef RELEASE +#if defined(MACOSX) + #include +#else + #include +#endif +#if defined (WIN32) + #define DEBUG_BREAK() \ + do { \ + if (DebugBreak_callback_stop) \ + (*DebugBreak_callback_stop)(); \ + debug_break(); \ + if (DebugBreak_callback_resume) \ + (*DebugBreak_callback_resume)(); \ + } while(0) + #define ASSERT(x) \ + do { \ + if (!(unsigned)(x)) { \ + mprintf((0, "Assertion failed (%s) in %s line %d.\n", #x, __FILE__, __LINE__)); \ + if (Debug_break) \ + DEBUG_BREAK(); \ + else \ + AssertionFailed(#x, __FILE__, __LINE__); \ + } \ + } while(0) + #define Int3() do { \ + mprintf((0, "Int3 at %s line %d.\n", __FILE__, __LINE__)); \ + if (Debug_break) \ + DEBUG_BREAK(); \ + else \ + Int3MessageBox(__FILE__,__LINE__); \ + } while (0) + #define HEAPCHECK() do { if (_heapchk() != _HEAPOK) Int3(); } while(0) +#elif defined (LINUX) + //For some reason Linux doesn't like the \ continuation character, so I have to uglify this + #define DEBUG_BREAK() do{ if(DebugBreak_callback_stop) (*DebugBreak_callback_stop)(); debug_break(); if(DebugBreak_callback_resume) (*DebugBreak_callback_resume)(); }while(0) + #define ASSERT(x) do{ if(!(unsigned long long)(x)){mprintf((0,"Assertion failed (%s) in %s line %d.\n", #x, __FILE__, __LINE__)); if(Debug_break) DEBUG_BREAK(); else AssertionFailed(#x,__FILE__,__LINE__);}}while(0) + #define Int3() do{ mprintf((0, "Int3 at %s line %d.\n",__FILE__,__LINE__)); if(Debug_break) DEBUG_BREAK(); else Int3MessageBox(__FILE__,__LINE__);}while(0) + #define HEAPCHECK() +#elif defined (MACINTOSH) + #define DEBUG_BREAK() \ + do { \ + if (DebugBreak_callback_stop) \ + (*DebugBreak_callback_stop)(); \ + debug_break(); \ + if (DebugBreak_callback_resume) \ + (*DebugBreak_callback_resume)(); \ + } while(0) +///#define ASSERT(x) assert(x) + #define ASSERT(x) \ + do { \ + if (!(unsigned)(x)) { \ + mprintf((2, "Assertion failed (%s) in %s line %d.\n", #x, __FILE__, __LINE__)); \ + if (Debug_break) \ + DEBUG_BREAK(); \ + else \ + AssertionFailed(#x, __FILE__, __LINE__); \ + } \ + } while(0) + #define Int3() do { \ + mprintf((0, "Int3 at %s line %d.\n", __FILE__, __LINE__)); \ + if (Debug_break) \ + DEBUG_BREAK(); \ + else \ + Int3MessageBox(__FILE__,__LINE__); \ + } while (0) + #define HEAPCHECK() +#endif +#else + #define DEBUG_BREAK() + #define ASSERT(x) + #define Int3() + #define HEAPCHECK() +#endif +#endif \ No newline at end of file diff --git a/lib/psglob.h b/lib/psglob.h new file mode 100644 index 000000000..2cc1a17a6 --- /dev/null +++ b/lib/psglob.h @@ -0,0 +1,47 @@ +/* +* $Logfile: /DescentIII/main/Lib/psglob.h $ +* $Revision: 2 $ +* $Date: 1/07/99 10:51p $ +* $Author: Jeff $ +* +* string globbing function header +* +* $Log: /DescentIII/main/Lib/psglob.h $ + * + * 2 1/07/99 10:51p Jeff + * added psglob and support to do find in files for hog files + * + * 1 1/07/99 7:28p Jeff +* +* $NoKeywords: $ +*/ + +#ifndef __PSGLOB_H_ +#define __PSGLOB_H_ + +// Returns 1 if string contains globbing characters in it +int PSGlobHasPattern(char *string); + +// PSGlobMatch +// Matches the pattern passed in to the string in text. If the pattern matches +// it returns 1, if not, it returns 0. In order to have a match, the following +// conditions must be met: +// 1) The entire string (text) is used for matching. +// 2) '*' matches any sequence of characters. +// 3) '?' matches any character +// 4) [SET] matches any character in the specified set. +// 5) [!SET] matches any character _not_ in the specified set. +// A set is composed of characters or ranges. A range looks like +// character hyphen character (as in 0-9 or A-Z). [0-9a-zA-Z_] is the set +// of all characters allowed in C identifiers. +// 6) Any other character in the pattern must be matched exactly.(see 9) +// 7) Because of the syntactic significance of []*?!- and \ to match +// these characters exactly, preced it with a '\'. +// 8) If dot_special is not zero, '*' and '?' do not match '.' at the beginning of text +// 9) If case_sensitive is 0, than case does not matter for the non-pattern characters +int PSGlobMatch(char *pattern, char *text, int case_sensitive, int dot_special); + +//Like PSGlobMatch, but match pattern against any final segment of text +int PSGlobMatchAfterStar(char *pattern, int case_sensitive, char *text); + +#endif \ No newline at end of file diff --git a/lib/psrand.h b/lib/psrand.h new file mode 100644 index 000000000..cbcc90b2d --- /dev/null +++ b/lib/psrand.h @@ -0,0 +1,25 @@ +/* +* $Logfile: /DescentIII/Main/lib/psrand.h $ +* $Revision: 2 $ +* $Date: 4/21/99 11:05a $ +* $Author: Kevin $ +* +* Outrage random number generator code +* +* $Log: /DescentIII/Main/lib/psrand.h $ + * + * 2 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 1 4/21/99 10:16a Kevin +* +* $NoKeywords: $ +*/ + +#undef RAND_MAX + +#define RAND_MAX 0x7fff + +void ps_srand(unsigned int seed); + +int ps_rand(void); \ No newline at end of file diff --git a/lib/pstring.h b/lib/pstring.h new file mode 100644 index 000000000..bbaa7b128 --- /dev/null +++ b/lib/pstring.h @@ -0,0 +1,62 @@ +/* +* $Logfile: /DescentIII/Main/lib/pstring.h $ +* $Revision: 3 $ +* $Date: 12/16/98 1:57p $ +* $Author: Samir $ +* +* Safe string manipulation and creation functions +* +* $Log: /DescentIII/Main/lib/pstring.h $ + * + * 3 12/16/98 1:57p Samir + * Replaced CleanupString2 with CleanupStr + * + * 2 11/01/98 1:56a Jeff + * added pstring.cpp/.h +* +* $NoKeywords: $ +*/ + + +#ifndef _PSTRING_H_ +#define _PSTRING_H_ + +#include + +// Pvsprintf +// Similar to vsprintf/_vsnprintf, however handles the case of a buffer overflow. Arguments are the +// same as _vsnprintf. In the case of count, pass in the size of the buffer, in the case where there is +// a buffer overflow, a \0 will be tacked on as the last byte in the buffer, ensuring a valid string. +int Pvsprintf(char *buffer, int count, const char *format, va_list argptr); + +// Psprintf +// Similar to sprintf/_snprintf, however handles the case of a buffer overflow. Arguments are the +// same as _snprintf. In the case of count, pass in the size of the buffer, in the case where there is +// a buffer overflow, a \0 will be tacked on as the last byte in the buffer, ensuring a valid string. +int Psprintf(char *buffer, int count, const char *format, ... ); + +// CleanupStr +// this function strips all leading and trailing spaces, keeping internal spaces. this goes +// for tabs too. +int CleanupStr(char *dest, const char *src, int destlen); + +// tStringTok +// you may start a string tokenization object by calling the start function +// then call next, to get the following tokens. +// note that this class uses it's own copy of the string to ensure that strtok doesn't +// get corrupted. + +class tStringTok +{ + char *m_strbuf; + char *m_curptr; + +public: + tStringTok() : m_strbuf(0), m_curptr(0) {}; + ~tStringTok(); + + char *start(const char *str, const char *tokens); + char *next(const char *tokens); +}; + +#endif \ No newline at end of file diff --git a/lib/pstypes.h b/lib/pstypes.h new file mode 100644 index 000000000..e8368351b --- /dev/null +++ b/lib/pstypes.h @@ -0,0 +1,45 @@ +#ifndef _TYPES_H +#define _TYPES_H + + +//define unsigned types; +typedef unsigned char ubyte; +typedef signed char sbyte; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifdef _MSC_VER //only Visual C++ has __int64 +typedef __int64 longlong; +#else +typedef long long longlong; +#endif + +#ifndef NULL +#define NULL 0 +#endif + +//The maximum length for a file name, including extension. It *does not* include the +//terminating NULL, so a buffer to hold a filename needs to be PSFILENAME_LEN+1 bytes long. +#define PSFILENAME_LEN 35 + +//The maximum length of a path (or path+filename). The seems (from looking at the code) to +//include the terminating NULL. lengthened to 512 to prevent problems with some long pathnames. +#define PSPATHNAME_LEN 512 + +#if !defined(__APPLE__) +#define HOST_BIGENDIAN @HOST_BIGENDIAN@ +#else +# if defined(__BIG_ENDIAN__) +# define HOST_BIGENDIAN 1 +# define MACOSXPPC 1 +# elif defined (__LITTLE_ENDIAN__) +# define HOST_BIGENDIAN 0 +# define MACOSX86 1 +# else +# error "Unknown HOST_BIGENDIAN!" +# endif +#endif + +#endif + diff --git a/lib/rend_d3d.h b/lib/rend_d3d.h new file mode 100644 index 000000000..4a2d28253 --- /dev/null +++ b/lib/rend_d3d.h @@ -0,0 +1,164 @@ +#ifndef REND_D3D_H +#define REND_D3D_H + +#include "renderer.h" +#include "3d.h" + +class oeApplication; + +// Starts up glide +int d3d_Init (oeApplication *app,renderer_preferred_state *pref_state); + +// Closes down glide +void d3d_Close(); + +// The main drawing function...draws a flat/textured/gouraud polygon +void d3d_DrawPolygon(int,g3Point **,int,int); + +void d3d_SetFlatColor (ddgr_color color); + +// Ends a frame +void d3d_EndFrame(); + +// Flips the screen +void d3d_Flip(); + +// Does setup for a new frame +void d3d_BeginFrame(int,int,int,int,int); + +// Tells glide what kind of texturing (linear/perspective) we want +void d3d_SetTextureType (texture_type); + +// Sets the lighting state of glide +void d3d_SetLightingState (light_state state); + +// Sets the glide color model (either rgb or mono) +void d3d_SetColorModel (color_model state); + +// Sets the state of bilinear filtering for our textures +void d3d_SetFiltering (sbyte state); + +// Sets the state of zbuffering to on or off +void d3d_SetZBufferState (sbyte state); + +// Sets the near/far z values for zbuffering +void d3d_SetZValues (float nearz,float farz); + +// Sets a bitmap as a lightmap to rendered on top of the next texture map +// a -1 value indicates no lighting map +void d3d_SetLightingMap (int handle); + +// Clears the display to a specified color +void d3d_ClearScreen (ddgr_color color); + +// Fills a rectangle on the display +void d3d_FillRect (ddgr_color color,int x1,int y1,int x2,int y2); + +// Sets a pixel on the display +void d3d_SetPixel (ddgr_color color,int x,int y); + +// Sets the near and far plane of fog +void d3d_SetFogBorders (float nearz,float farz); + +// Sets the fog state to on or off +void d3d_SetFogState (sbyte state); + +// Draws a 2d line +void d3d_DrawLine (int x1,int y1,int x2,int y2); + +// sets fog color +void d3d_SetFogColor (ddgr_color fogcolor); + +// sets the alpha type +void d3d_SetAlphaType (sbyte); + +// Sets the constant alpha value +void d3d_SetAlphaValue (ubyte val); + +// Sets the overall alpha scale factor (all alpha values are scaled by this value) +// usefull for motion blur effect +void d3d_SetAlphaFactor(float val); + +// Returns the current Alpha factor +float d3d_GetAlphaFactor(void); + +// Sets the glide wrapping type +void d3d_SetWrapType (wrap_type val); + +// Takes a screenshot of the frontbuffer and puts it into the passed bitmap handle +void d3d_Screenshot (int bm_handle); + +// Sets write mask for depth buffer +void d3d_SetZBufferWriteMask (int state); + +// Locks the linear frame buffer for application access +void d3d_GetLFBLock (renderer_lfb *lfb); + +// Releases the previous LFB lock +void d3d_ReleaseLFBLock (renderer_lfb *lfb); + +void d3d_GetProjectionParameters(int *width,int *height); +float d3d_GetAspectRatio(); + + +// Draws a line using the states set by the renderer lib +void d3d_DrawSpecialLine (g3Point *p0,g3Point *p1); + +void d3d_SetMipState (sbyte); + +// Sets up a some global preferences for glide +int d3d_SetPreferredState (renderer_preferred_state *pref_state); + +// Sets the gamma correction value +void d3d_SetGammaValue (float val); + +// Gets the current state of the renderer +void d3d_GetRenderState (rendering_state *rstate); + +// Sets the resolution that glide uses +void d3d_SetResolution (int width,int height); + +// Sets the coplanar z bias for rendered polygons +void d3d_SetCoplanarPolygonOffset (float factor); + +// Creates a on the video card if needed +void d3d_PreUploadTextureToCard (int handle,int map_type); + +// Evicts local texture memory +void d3d_FreePreUploadedTexture (int handle,int map_type); + +// Returns a string describing the passed in error +char *d3d_ErrorString(int error); + +// Takes a screenshot of the frontbuffer and puts it into the passed bitmap handle +void d3d_Screenshot (int bm_handle); + +// Returns 1 if the renderer supports bumpmapping +int d3d_SupportsBumpmapping (); + +// Clears the zbuffer +void d3d_ClearZBuffer (); + +// Clears the texture cache +void d3d_ResetCache (); + +// Creates or destroys a linear framebuffer lock +void d3d_GetLFBLock (renderer_lfb *lfb); +void d3d_ReleaseLFBLock (renderer_lfb *lfb); + +// Takes a bitmap and blits it to the screen using linear frame buffer stuff +// X and Y are the destination X,Y +void d3d_CopyBitmapToFramebuffer (int bm_handle,int x,int y); + +// Gets a renderer ready for a framebuffer copy, or stops a framebuffer copy +void d3d_SetFrameBufferCopyState (bool state); + +#if defined(WIN32) +// returns directdraw object +void *d3d_DirectDrawObj(void **frontsurf, void **backsurf); +#endif + +// returns rendering statistics for the frame +void d3d_GetStatistics(tRendererStats *stats); + +#endif diff --git a/lib/rend_opengl.h b/lib/rend_opengl.h new file mode 100644 index 000000000..fea502592 --- /dev/null +++ b/lib/rend_opengl.h @@ -0,0 +1,147 @@ +#ifndef REND_OPENGL_H +#define REND_OPENGL_H + +#include "renderer.h" +#include "3d.h" + +class oeApplication; + +// Starts up opengl +int opengl_Init (oeApplication *app,renderer_preferred_state *pref_state); + +// Closes down opengl +void opengl_Close(); +void opengl_Shutdown(); + +// The main drawing function...draws a flat/textured/gouraud polygon +void opengl_DrawPolygon(int,g3Point **,int,int); + +void opengl_SetFlatColor (ddgr_color color); + +// Ends a frame +void opengl_EndFrame(); + +// Flips the screen +void opengl_Flip(); + +// Does setup for a new frame +void opengl_BeginFrame(int,int,int,int,int); + +// Tells opengl what kind of texturing (linear/perspective) we want +void opengl_SetTextureType (texture_type); + +// Sets the lighting state of opengl +void opengl_SetLightingState (light_state state); + +// Sets the opengl color model (either rgb or mono) +void opengl_SetColorModel (color_model state); + +// Sets the state of bilinear filtering for our textures +void opengl_SetFiltering (sbyte state); + +// Sets the state of zbuffering to on or off +void opengl_SetZBufferState (sbyte state); + +// Sets the near/far z values for zbuffering +void opengl_SetZValues (float nearz,float farz); + +// Sets a bitmap as a lightmap to rendered on top of the next texture map +// a -1 value indicates no lighting map +void opengl_SetLightingMap (int handle); + +// Clears the display to a specified color +void opengl_ClearScreen (ddgr_color color); + +// Fills a rectangle on the display +void opengl_FillRect (ddgr_color color,int x1,int y1,int x2,int y2); + +// Sets a pixel on the display +void opengl_SetPixel (ddgr_color color,int x,int y); + +// Sets the near and far plane of fog +void opengl_SetFogBorders (float nearz,float farz); + +// Sets the fog state to on or off +void opengl_SetFogState (sbyte state); + +// Fills in projection variables +void opengl_GetProjectionParameters(int *width,int *height); +void opengl_GetProjectionScreenParameters( int &screenLX, int &screenTY, int &screenW, int &screenH ); + +// Returns the aspect ratio of the physical screen +float opengl_GetAspectRatio (); + +// Sets texture wrapping type +void opengl_SetWrapType (wrap_type val); + +// Sets the constant alpha value +void opengl_SetAlphaValue (ubyte val); + +// Sets the overall alpha scale factor (all alpha values are scaled by this value) +// usefull for motion blur effect +void opengl_SetAlphaFactor(float val); + +// Returns the current Alpha factor +float opengl_GetAlphaFactor(void); + +// Sets the type of alpha blending you want +void opengl_SetAlphaType (sbyte atype); + +// Sets whether or not to write into the zbuffer +void opengl_SetZBufferWriteMask (int state); + +// Gets the current state of the renderer +void opengl_GetRenderState (rendering_state *rstate); + +// draws a line +void opengl_DrawLine (int x1,int y1,int x2,int y2); + +// draws a line +void opengl_DrawSpecialLine (g3Point *p0,g3Point *p1); + +// Sets the color that opengl uses for fog +void opengl_SetFogColor (ddgr_color color); + +// Sets the coplanar z bias for rendered polygons +void opengl_SetCoplanarPolygonOffset (float factor); + +// Sets up a some global preferences for openGL +int opengl_SetPreferredState (renderer_preferred_state *pref_state); + +// Sets the gamma correction value +void opengl_SetGammaValue (float val); + +// Takes a screenshot of the frontbuffer and puts it into the passed bitmap handle +void opengl_Screenshot (int bm_handle); + +// Returns the pixel color at x,y +ddgr_color opengl_GetPixel (int,int); + +// Clears the zbuffer +void opengl_ClearZBuffer (); + +// Clears the texture cache +void opengl_ResetCache (); + +// Takes a bitmap and blits it to the screen using linear frame buffer stuff +// X and Y are the destination X,Y +void opengl_CopyBitmapToFramebuffer (int bm_handle,int x,int y); + +// Gets a renderer ready for a framebuffer copy, or stops a framebuffer copy +void opengl_SetFrameBufferCopyState (bool state); + +#if defined(WIN32) +// returns directdraw object +void *opengl_DirectDrawObj(); +#endif + +// returns rendering statistics for the frame +void opengl_GetStatistics(tRendererStats *stats); + +#ifdef MACINTOSH +void opengl_DettachContext(void); +void opengl_AttachContext(CGrafPtr); +void opengl_ResetContext(void); +#endif + +#endif diff --git a/lib/renderer.h b/lib/renderer.h new file mode 100644 index 000000000..e2dab54a6 --- /dev/null +++ b/lib/renderer.h @@ -0,0 +1,689 @@ +/* + * $Logfile: /DescentIII/Main/lib/renderer.h $ + * $Revision: 80 $ + * $Date: 4/19/00 5:22p $ + * $Author: Matt $ + * + * Header for renderer library + * + * $Log: /DescentIII/Main/lib/renderer.h $ + * + * 80 4/19/00 5:22p Matt + * From Duane for 1.4 + * Added extern + * + * 79 10/21/99 1:43p Kevin + * Mac Merge + * + * 78 9/18/99 9:22p Jeff + * Get/Set Alpha factor functions + * + * 77 8/10/99 10:51p Duane + * Added UseMipmap global + * + * 76 7/22/99 8:37p Jeff + * added texture upload to renderstats + * + * 75 7/15/99 6:38p Jeff + * created function to get rendering stats + * + * 74 4/01/99 10:56a Jason + * added better support for movie rendering + * + * 73 3/31/99 3:48p Jason + * changes for cinematics + * + * 72 3/29/99 7:29p Jason + * made renderer handle default resolution more gracefully + * + * 71 3/24/99 11:55a Jason + * added S3 texture compression + * + * 70 3/22/99 5:51p Jason + * enhancements to mirrors + * + * 69 1/29/99 6:29p Jason + * first pass at adding bumpmaps + * + * 68 1/22/99 1:09a Jason + * added vid mem checking + * + * 67 1/18/99 10:45a Samir + * added function to get DirectDraw object from Win32 apps. + * + * 66 12/23/98 5:35p Jason + * added saturated lightmap blending + * + * 65 12/18/98 5:25p Jason + * added specularity to mine walls + * + * 64 12/09/98 5:28p Jason + * added more options + * + * 63 12/08/98 2:29p Jason + * fog rendering changes for patch + * + * 62 10/21/98 9:28p Jason + * Made no lightmaps work globally + * + * 61 10/21/98 12:05p Jason + * changes for data paging + * + * 60 10/17/98 3:10p Jason + * added Set/Get error message stuff + * + * 59 10/08/98 3:36p Jason + * fixes for the demo + * + * 58 9/25/98 1:17a Jason + * added rend_SetCoplanarPolygonOffset functions + * + * 57 9/18/98 1:28p Jason + * cleaned up renderer initting + * + * 56 9/11/98 4:03p Jason + * added better multitexture support + * + * 55 6/11/98 3:38p Jason + * added a general StateLimit render boolean for APIs that are state + * limited (like OpenGL or D3D) + * + * 54 6/09/98 4:47p Jason + * added windowed openGL mode + * + * 53 5/26/98 3:59p Jason + * added LIGHTMAP_BLEND_CONSTANT alpha type + * + * 52 5/20/98 5:44p Jason + * incremental checkin for bumpmapping + * + * 51 5/19/98 12:27p Jason + * cleaned up some 3d stuff + * + * 50 5/06/98 1:39p Jason + * added rend_SetResolution + * + * 49 5/06/98 10:51a Jeff + * put rend_DrawScaledChunkBitmap back in the header + * + * 48 5/05/98 3:02p Jason + * attempting to add different screen resolutions + * + * 46 4/23/98 6:38p Jason + * made bitmaps use 1555 format + * + * 45 4/17/98 4:27p Jason + * added alpha per vertex stuff to drawscaledbitmap + * + * 44 4/08/98 12:27p Jason + * added rend_drawsimplebitmap + * + * 43 4/01/98 12:02p Jason + * incremental checkin for rendering changes + * + * 42 3/13/98 1:22p Jason + * Moved UseHardware flag to the renderer lib where it belongs + * + * 41 3/03/98 6:50p Jason + * added gamma stuff in preferred state + * + * 40 3/02/98 5:53p Jason + * added gamma functionality + * + * 39 2/14/98 10:48p Jason + * got preferred rendering working + * + * 38 2/12/98 1:32p Jason + * got mipmapping working + * + * 37 2/05/98 6:53p Jason + * added new weapon slot + * + * 36 2/03/98 3:16p Jason + * added the ability to specify saturation textures for models + * + * 35 1/28/98 5:37p Jason + * added streamer weapons + * + * 34 1/19/98 2:32p Jason + * added the ability to set clear flags on rend_StartFrame + * + * 33 1/16/98 11:54a Samir + * Added support for rendering chunked bitmaps. + * + * 32 1/15/98 6:32p Jason + * added v wrapping for sky textures + * + * 31 12/22/97 7:23p Samir + * took out transparent colors again. + * + * 30 12/22/97 7:15p Samir + * removed references of ddgr and gr.h. replace with grdefs.h + * + * 29 12/19/97 5:22p Samir + * Added more drawing primatives for software. + * + * 28 12/19/97 2:18p Jason + * fixed some more 2d/3d integration + * + * 27 12/19/97 12:20p Jason + * changes for better 2d/3d system integration + * + * 26 11/25/97 12:33p Jason + * added antialiasing mode + * + * 25 11/19/97 5:25p Jason + * added new alpha mode - AT_FLAT_BLEND + * + * 24 11/13/97 4:06p Jason + * added rend_DrawLFBBitmap + * + * 23 11/13/97 3:52p Jason + * added lfb stuff for renderer + * + * 22 10/29/97 12:36p Jason + * remove byte/bool conflicts in the renderer lib + * + * 21 10/24/97 4:39p Jason + * added support to not write into the zbuffer + * + * 20 10/24/97 2:29p Jason + * added alpha saturation type + * + * 19 10/22/97 4:19p Jason + * added smoke trail effects + * + * 18 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 17 10/13/97 3:56p Jason + * made a better 3d bitmap system + * + * 16 9/16/97 4:09p Jason + * implemented software zbuffer + * + * 15 9/09/97 12:09p Jason + * took transparency out of texture_types + * + * 14 9/09/97 11:44a Jason + * changed the way alpha works with the renderer + * + * 13 9/04/97 5:52p Matt + * Took out include of 3d.h + * + * 12 8/29/97 11:59a Jason + * implemented screenshot functions + * + * 11 8/24/97 2:45p Jason + * implemented texture wrapping + * + * 10 8/07/97 11:46a Jason + * implemented tmap overlay system + * + * 9 8/06/97 11:43a Jason + * made lightmaps work correctly in the terrain + * + * 8 8/04/97 6:46p Jason + * added code for a lightmap system + * + * 7 8/04/97 3:28p Jason + * added alpha blending per texture + * + * 6 7/20/97 7:36p Jason + * added support for colored fog + * + * 2 6/24/97 7:45p Matt + * Removed include of manage.h + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 11 6/16/97 5:02p Jason + * added flip function + * + * 10 6/16/97 3:44p Jason + * added line drawing + * + * 9 6/16/97 2:54p Jason + * added rend_DrawFontCharacter function for drawing fonts + * + * 8 6/16/97 2:34p Jason + * added 3dfx support + * + * 7 6/06/97 11:35a Jason + * added missing functions for pixel primitives + * + * 6 6/03/97 12:19p Jason + * more functions added for opengl + * + * 5 5/27/97 4:39p Jason + * changes to support OpenGL + * + * 4 5/22/97 11:59a Jason + * add a ScaleBitmap function to the abstracted render library + * + * 3 5/21/97 12:30p Jason + * calls rend_start and rend_end when frames begin/end + * + * 2 5/19/97 5:10p Jason + * changes for our new abstracted renderer code + * + * 1 5/19/97 3:23p Jason + * + * $NoKeywords: $ + */ + +#ifndef RENDERER_H +#define RENDERER_H + +#include "pstypes.h" +#include "grdefs.h" + +//Declare this here so we don't need to include 3d.h +typedef struct g3Point g3Point; +typedef struct chunked_bitmap chunked_bitmap; + +// for rend_Init prototype +class oeApplication; + +#define TEXTURE_WIDTH 128 +#define TEXTURE_HEIGHT 128 +#define TEXTURE_BPP 2 + +#define FLAT_SHADE_COLOR 0x7C01 +// If an incoming texture has the above color in it, change that color to this color +#define REPLACEMENT_COLOR 0x07C0 + +extern int Triangles_drawn; + +// Is this hardware or software rendered? +typedef enum +{ + RENDERER_SOFTWARE_8BIT, + RENDERER_SOFTWARE_16BIT, + RENDERER_OPENGL, + RENDERER_DIRECT3D, + RENDERER_GLIDE, + RENDERER_NONE, +} renderer_type; + +extern renderer_type Renderer_type; + +// renderer clear flags +#define RF_CLEAR_ZBUFFER 1 +#define RF_CLEAR_COLOR 2 + +// Overlay texture settings +#define OT_NONE 0 // No overlay +#define OT_BLEND 1 // Draw a lightmap texture afterwards +#define OT_REPLACE 2 // Draw a tmap2 style texture afterwards +#define OT_FLAT_BLEND 3 // Draw a gouraud shaded polygon afterwards +#define OT_BLEND_VERTEX 4 // Like OT_BLEND, but take constant alpha into account +#define OT_BUMPMAP 5 // Draw a saturated bumpmap afterwards +#define OT_BLEND_SATURATE 6 // Add a lightmap in + + +extern ubyte Overlay_type; +extern int Overlay_map; +extern int Bumpmap_ready,Bump_map; +extern float Z_bias; +extern bool UseHardware; +extern bool StateLimited; +extern bool NoLightmaps; +extern bool UseMultitexture; +extern bool UseWBuffer; +extern bool UseMipmap; //DAJ +extern bool ATIRagePro; //DAJ +extern bool Formac; //DAJ + +// various state setting functions +//------------------------------------ + +// Sets our renderer +void rend_SetRendererType (renderer_type state); + +#define MAP_TYPE_BITMAP 0 +#define MAP_TYPE_LIGHTMAP 1 +#define MAP_TYPE_BUMPMAP 2 +#define MAP_TYPE_UNKNOWN 3 + +// lighting state +typedef enum +{ + LS_NONE, // no lighting, fully lit rendering + LS_GOURAUD, // Gouraud shading + LS_PHONG, // Phong shading + LS_FLAT_GOURAUD // Take color from flat color +} light_state; + +void rend_SetLighting(light_state); + +typedef enum +{ + CM_MONO, // monochromatic (intensity) model - default + CM_RGB, // RGB model +} color_model; + +// color model +void rend_SetColorModel (color_model); + +typedef enum +{ + TT_FLAT, // solid color + TT_LINEAR, // textured linearly + TT_PERSPECTIVE, // texture perspectively + TT_LINEAR_SPECIAL, // A textured polygon drawn as a flat color + TT_PERSPECTIVE_SPECIAL, // A textured polygon drawn as a flat color +} texture_type; + +// Alpha type flags - used to decide what type of alpha blending to use +#define ATF_CONSTANT 1 // Take constant alpha into account +#define ATF_TEXTURE 2 // Take texture alpha into account +#define ATF_VERTEX 4 // Take vertex alpha into account + +// Alpha types +#define AT_ALWAYS 0 // Alpha is always 255 (1.0) +#define AT_CONSTANT 1 // constant alpha across the whole image +#define AT_TEXTURE 2 // Only uses texture alpha +#define AT_CONSTANT_TEXTURE 3 // Use texture * constant alpha +#define AT_VERTEX 4 // Use vertex alpha only +#define AT_CONSTANT_VERTEX 5 // Use vertex * constant alpha +#define AT_TEXTURE_VERTEX 6 // Use texture * vertex alpha +#define AT_CONSTANT_TEXTURE_VERTEX 7 // Use all three (texture constant vertex) +#define AT_LIGHTMAP_BLEND 8 // dest*src colors +#define AT_SATURATE_TEXTURE 9 // Saturate up to white when blending +#define AT_FLAT_BLEND 10 // Like lightmap blend, but uses gouraud shaded flat polygon +#define AT_ANTIALIAS 11 // Draws an antialiased polygon +#define AT_SATURATE_VERTEX 12 // Saturation with vertices +#define AT_SATURATE_CONSTANT_VERTEX 13 // Constant*vertex saturation +#define AT_SATURATE_TEXTURE_VERTEX 14 // Texture * vertex saturation +#define AT_LIGHTMAP_BLEND_VERTEX 15 // Like AT_LIGHTMAP_BLEND, but take vertex alpha into account +#define AT_LIGHTMAP_BLEND_CONSTANT 16 // Like AT_LIGHTMAP_BLEND, but take constant alpha into account +#define AT_SPECULAR 32 +#define AT_LIGHTMAP_BLEND_SATURATE 33 // Light lightmap blend, but add instead of multiply + +#define LFB_LOCK_READ 0 +#define LFB_LOCK_WRITE 1 + +typedef enum +{ + WT_WRAP, // Texture repeats + WT_CLAMP, // Texture clamps + WT_WRAP_V // Texture wraps in v +} wrap_type; + +typedef struct +{ + sbyte initted; + + sbyte cur_bilinear_state; + sbyte cur_zbuffer_state; + sbyte cur_fog_state; + sbyte cur_mip_state; + + texture_type cur_texture_type; + color_model cur_color_model; + light_state cur_light_state; + sbyte cur_alpha_type; + + wrap_type cur_wrap_type; + + float cur_fog_start,cur_fog_end; + float cur_near_z,cur_far_z; + float gamma_value; + + int cur_alpha; + ddgr_color cur_color; + ddgr_color cur_fog_color; + + sbyte cur_texture_quality; // 0-none, 1-linear, 2-perspective + + int clip_x1,clip_x2,clip_y1,clip_y2; + int screen_width,screen_height; + +} rendering_state; + +typedef struct +{ + ubyte mipping; + ubyte filtering; + float gamma; + ubyte bit_depth; + int width,height; + ubyte vsync_on; +} renderer_preferred_state; + +typedef struct +{ + int type; + ushort *data; + int bytes_per_row; +} renderer_lfb; + +typedef struct +{ + int poly_count; + int vert_count; + int texture_uploads; +}tRendererStats; + +// returns rendering statistics for the frame +void rend_GetStatistics(tRendererStats *stats); + +void rend_SetTextureType (texture_type); + +// Given a handle to a bitmap and nv point vertices, draws a 3D polygon +void rend_DrawPolygon3D(int handle,g3Point **p,int nv,int map_type=MAP_TYPE_BITMAP); + +// Given a handle to a bitmap and nv point vertices, draws a 2D polygon +void rend_DrawPolygon2D(int handle,g3Point **p,int nv); + +// Tells the software renderer whether or not to use mipping +void rend_SetMipState (sbyte); + +// Sets the fog state to TRUE or FALSE +void rend_SetFogState (sbyte on); + +// Sets the near and far plane of fog +void rend_SetFogBorders (float fog_near,float fog_far); + +// Sets the color for fill based primitives; +void rend_SetFlatColor (ddgr_color color); + +// Tells the renderer we're starting a frame. Clear flags tells the renderer +// what buffer (if any) to clear +void rend_StartFrame(int x1,int y1,int x2,int y2,int clear_flags=RF_CLEAR_ZBUFFER); + +// Tells the renderer the frame is over +void rend_EndFrame(); + +// Init our renderer, pass the application object also. +int rend_Init (renderer_type state, oeApplication *app,renderer_preferred_state *pref_state); + +// de-init the renderer +void rend_Close (); + +// Draws a scaled 2d bitmap to our buffer +// NOTE: scripts are expecting the old prototype that has a zvalue (which is ignored) before color +void rend_DrawScaledBitmap (int x1,int y1,int x2,int y2,int bm,float u0,float v0,float u1,float v1,int color=-1,float *alphas=NULL); + +// Sets the state of bilinear filtering for our textures +void rend_SetFiltering (sbyte state); + +// Sets the state of zbuffering to on or off +void rend_SetZBufferState (sbyte state); + +// Sets the near and far planes for z buffer +void rend_SetZValues (float nearz,float farz); + +// Sets a bitmap as an overlay to rendered on top of the next texture map +void rend_SetOverlayMap (int handle); + + +// Sets the type of overlay operation +void rend_SetOverlayType (ubyte type); + +// Clears the display to a specified color +void rend_ClearScreen (ddgr_color color); + +// Fills a rectangle on the display +void rend_FillRect (ddgr_color color,int x1,int y1,int x2,int y2); + +// Sets a pixel on the display +void rend_SetPixel (ddgr_color color,int x,int y); + +// Sets a pixel on the display +ddgr_color rend_GetPixel (int x,int y); + +// Sets up a font character to draw. We draw our fonts as pieces of textures +void rend_DrawFontCharacter (int bm_handle,int x1,int y1,int x2,int y2,float u,float v,float w,float h); + +// Draws a line +void rend_DrawLine (int x1,int y1,int x2,int y2); + +// Draws spheres +void rend_FillCircle(ddgr_color col, int x, int y, int rad); + +// draws circles +void rend_DrawCircle(int x, int y, int rad); + +// Flips the surface +void rend_Flip(); + +// Sets the argb characteristics of the font characters. color1 is the upper left and proceeds clockwise +void rend_SetCharacterParameters (ddgr_color color1,ddgr_color color2,ddgr_color color3,ddgr_color color4); + +// Sets the color of fog +void rend_SetFogColor (ddgr_color fogcolor); + +// sets the alpha type +void rend_SetAlphaType (sbyte); + +// Sets the constant alpha value +void rend_SetAlphaValue (ubyte val); + +// Sets the overall alpha scale factor (all alpha values are scaled by this value) +// usefull for motion blur effect +void rend_SetAlphaFactor(float val); + +// Returns the current Alpha factor +float rend_GetAlphaFactor(void); + +// Sets the wrap parameter +void rend_SetWrapType (wrap_type val); + +// Takes a screenshot of the current frame and puts it into the handle passed +void rend_Screenshot (int bm_handle); + +// Adds a bias to each coordinates z value. This is useful for making 2d bitmaps +// get drawn without being clipped by the zbuffer +void rend_SetZBias (float z_bias); + +// Enables/disables writes the depth buffer +void rend_SetZBufferWriteMask (int state); + +// Sets where the software renderer should write to +void rend_SetSoftwareParameters(float aspect,int width,int height,int pitch,ubyte *framebuffer); + +// Fills in some variables so the 3d math routines know how to project +void rend_GetProjectionParameters(int *width,int *height); +void rend_GetProjectionScreenParameters( int &screenLX, int &screenTY, int &screenW, int &screenH ); + +// Returns the aspect ratio of the physical screen +float rend_GetAspectRatio(); + +// Gets a pointer to a linear frame buffer +void rend_GetLFBLock (renderer_lfb *lfb); + +// Releases an lfb lock +void rend_ReleaseLFBLock (renderer_lfb *lfb); + +// Given a source x,y and width,height, draws any sized bitmap into the renderer lfb +void rend_DrawLFBBitmap (int sx,int sy,int w,int h,int dx,int dy,ushort *data,int rowsize); + +// given a chunked bitmap, renders it. +void rend_DrawChunkedBitmap(chunked_bitmap *chunk, int x, int y, ubyte alpha); + +// given a chunked bitmap, renders it.scaled +void rend_DrawScaledChunkedBitmap(chunked_bitmap *chunk, int x, int y, int neww, int newh, ubyte alpha); + +// Draws a line using the states of the renderer +void rend_DrawSpecialLine (g3Point *p0,g3Point *p1); + +// Sets some global preferences for the renderer +// Returns -1 if it had to use the default resolution/bitdepth +int rend_SetPreferredState (renderer_preferred_state *pref_state); + +// Sets the gamma value +void rend_SetGammaValue (float val); + +// Fills in the passed in pointer with the current rendering state +void rend_GetRenderState (rendering_state *rstate); + +// Draws a simple bitmap at the specified x,y location +void rend_DrawSimpleBitmap (int bm_handle,int x,int y); + +// Changes the resolution of the renderer +void rend_SetResolution (int width,int height); + +// Gets OpenGL ready to work in a window +int rend_InitOpenGLWindow (oeApplication *app,renderer_preferred_state *pref_state); + +// Shuts down OpenGL in a window +void rend_CloseOpenGLWindow (); + +// Sets the state of the OpenGLWindow to on or off +void rend_SetOpenGLWindowState (int state,oeApplication *app,renderer_preferred_state *pref_state); + +// Sets the hardware bias level for coplanar polygons +// This helps reduce z buffer artifaces +void rend_SetCoplanarPolygonOffset (float factor); + +// Gets the error message string +char *rend_GetErrorMessage (); + +// Sets the error message string +void rend_SetErrorMessage (char *str); + +// Preuploads a bitmap to the card +void rend_PreUploadTextureToCard (int,int); +void rend_FreePreUploadedTexture (int,int); + +// Returns 1 if there is mid video memory, 2 if there is low vid memory, or 0 if there is large vid memory +int rend_LowVidMem (); + +// Returns 1 if the renderer supports bumpmapping +int rend_SupportsBumpmapping (); + +// Gets a bumpmap ready for drawing, or turns off bumpmapping +void rend_SetBumpmapReadyState (int state,int map); + +// Clears the zbuffer +void rend_ClearZBuffer (); + +// Clears the texture cache +void rend_ResetCache (); + +// Takes a bitmap and blits it to the screen using linear frame buffer stuff +// X and Y are the destination X,Y. +void rend_CopyBitmapToFramebuffer (int bm_handle,int x,int y); + +// Gets a renderer ready for a framebuffer copy, or stops a framebuffer copy +void rend_SetFrameBufferCopyState (bool state); + +#if defined(DD_ACCESS_RING) +#if defined(WIN32) +// returns the direct draw object +void *rend_RetrieveDirectDrawObj(void **frontsurf, void **backsurf); +#endif +#endif + +/////////////////////////////////////////////////////////////// +#include "../renderer/RendererConfig.h" +#ifdef USE_SOFTWARE_TNL +#include "IMeshBuilder.h" + +RZ::Renderer::IMeshBuilder *rend_CreateMeshBuilder(void); +#endif + +#endif diff --git a/lib/rtperformance.h b/lib/rtperformance.h new file mode 100644 index 000000000..768889442 --- /dev/null +++ b/lib/rtperformance.h @@ -0,0 +1,263 @@ +#ifndef _RUN_TIME_PROFILING_ +#define _RUN_TIME_PROFILING_ + +//uncomment the following if you want to enable Run-time Profiling +#ifndef RELEASE +#define USE_RTP +#endif + +#if defined(__LINUX__) || defined(MACINTOSH) +//#if defined(__LINUX__) + #ifdef USE_RTP + #undef USE_RTP //no rtp for now + #endif +#endif + +// use INT64 for 64bit integers +#ifdef USE_RTP + #if defined(__LINUX__) || defined(MACINTOSH) + #define INT64 long long + #else + #define INT64 signed __int64 + #endif +#else + #define INT64 unsigned char +#endif + +#if defined(MACINTOSH) + #define LARGE_INTEGER long long +#endif + +// struct of information to be saved per frame (note: use INT64 for timer info) +// ----------------------------------------------------------------------------------- +typedef struct{ + INT64 frame_num; //the frame number, used internally + INT64 renderframe_time; + INT64 multiframe_time; + INT64 musicframe_time; + INT64 ambsound_frame_time; + INT64 weatherframe_time; + INT64 playerframe_time; + INT64 doorframe_time; + INT64 levelgoal_time; + INT64 matcenframe_time; + INT64 objframe_time; + INT64 aiframeall_time; + INT64 processkeys_time; + INT64 normalevent_time; + INT64 ct_flying_time; + INT64 ct_aidoframe_time; + INT64 ct_weaponframe_time; + INT64 ct_explosionframe_time; + INT64 ct_debrisframe_time; + INT64 ct_splinterframe_time; + INT64 mt_physicsframe_time; + INT64 mt_walkingframe_time; + INT64 mt_shockwave_time; + INT64 obj_doeffect_time; + INT64 obj_move_player_time; + INT64 obj_d3xint_time; + INT64 obj_objlight_time; + INT64 cycle_anim; + INT64 vis_eff_move; + INT64 phys_link; + INT64 obj_do_frm; + INT64 fvi_time; + + int texture_uploads; + int polys_drawn; + int fvi_calls; + float frame_time; //how long the frame took. A float because it's already calc'd so we might as well save it +}tRTFrameInfo; + +// Flags for Runtime Performance Counters (these are 64 bit) +// -------------------------------------------------------------------- +#define RTI_FRAMETIME 0x00000001 +#define RTI_RENDERFRAMETIME 0x00000002 +#define RTI_MULTIFRAMETIME 0x00000004 +#define RTI_MUSICFRAMETIME 0x00000008 +#define RTI_AMBSOUNDFRAMETIME 0x00000010 +#define RTI_WEATHERFRAMETIME 0x00000020 +#define RTI_PLAYERFRAMETIME 0x00000040 +#define RTI_DOORFRAMETIME 0x00000080 +#define RTI_LEVELGOALTIME 0x00000100 +#define RTI_MATCENFRAMETIME 0x00000200 +#define RTI_OBJFRAMETIME 0x00000400 +#define RTI_AIFRAMETIME 0x00000800 +#define RTI_PROCESSKEYTIME 0x00001000 +#define RTI_TEXTUREUPLOADS 0x00002000 +#define RTI_POLYSDRAWN 0x00004000 + +// Macros to be used internally and externally +// -------------------------------------------------- + +#ifndef USE_RTP + +#define RTP_CLOCKTOSECONDS(c1,c2,t) +#define RTP_CLOCKSECONDS(c,t) +#define RTP_RECORDVALUE(member,value) +#define RTP_INCRVALUE(member,amount) +#define RTP_DECRVALUE(member,amount) +#define RTP_GETVALUE(member,value) +#define RTP_STARTTIME(member) +#define RTP_tSTARTTIME(member,time) +#define RTP_ENDTIME(member) +#define RTP_tENDTIME(member,time) +#define RTP_GETCLOCK(time) +#define RTP_STARTINCTIME(member) +#define RTP_ENDINCTIME(member) +#define RTP_ENABLEFLAGS(flags) +#define RTP_DISABLEFLAGS(flags) + +#else + +#define RTP_ENABLEFLAGS(flags) rtp_EnableFlags(flags) + +#define RTP_DISABLEFLAGS(flags) rtp_EnableFlags(flags) + +// given 2 clocks (INT64's) it will return the time in seconds between them (double) +// (internal use) +// c1 = start time +// c2 = end time +// t = time in seconds +#define RTP_CLOCKTOSECONDS(c1,c2,t) do{ if(Runtime_performance_enabled){INT64 diff = (c2) - (c1); t = ((double)diff)/((double)Runtime_performance_clockfreq);} }while(0) + +// given a clock time, it will convert it to seconds +#define RTP_CLOCKSECONDS(c,t) do{if(Runtime_performance_enabled){t = ((double)c)/((double)Runtime_performance_clockfreq);}}while(0) + +// sets a value of a member of the current tRTFrameInfo frame +// (external use) +// member = the member of the tRTFrameInfo to set +// value = the value to set it to +#define RTP_RECORDVALUE(member,value) do{ if(Runtime_performance_enabled){RTP_SingleFrame.member = (value);}}while(0) + +// increments a value of a member of the current tRTFrameInfo frame +// (external use) +// member = the member of the tRTFrameInfo to increment +// amount = how much to increment it +#define RTP_INCRVALUE(member,amount) do{if(Runtime_performance_enabled){RTP_SingleFrame.member+=(amount);}}while(0) + +// decrements a value of a member of the current tRTFrameInfo frame +// (external use) +// member = the member of the tRTFrameInfo to decrement +// amount = how much to decrement it +#define RTP_DECRVALUE(member,amount) do{if(Runtime_performance_enabled){RTP_SingleFrame.member-=(amount);}}while(0) + +// returns the value of a member of the current tRTFrameInfo frame +// (external use) +// member = the member to retrieve +// value = where to store the value +#define RTP_GETVALUE(member,value) do{if(Runtime_performance_enabled){value = RTP_SingleFrame.member;}}while(0) + +// starts a frame time calculation for a time member of the tRTFrameInfo frame +// this version calls the clock to get the current time +// member = member to start recording time on +#define RTP_STARTTIME(member) do{if(Runtime_performance_enabled){RTP_SingleFrame.member = rtp_GetClock();}}while(0) + +// starts a frame time calculation given a time to start it with (similar to the above +// except you give it the starting time). This should be used (along with +// RTP_tENDTIME(member,time)) in places where you do a bunch of frame times in a row +// to reduce the number of calls to rtp_GetClock(), however, this way will give +// a few cycles off, compared to the RTP_STARTTIME(member)/RTP_ENDTIME(member) way +// member = member to start recording time on +#define RTP_tSTARTTIME(member,time) do{if(Runtime_performance_enabled){RTP_SingleFrame.member = time;}}while(0) + +// ends a frame time calculation for a member of tRTFrameInfo frame +// member = member to stop recording time for +#define RTP_ENDTIME(member) do{if(Runtime_performance_enabled){RTP_SingleFrame.member = rtp_GetClock() - RTP_SingleFrame.member;}}while(0) + +// same as above, but will return the time that it stopped the recording time +#define RTP_tENDTIME(member,time) do{if(Runtime_performance_enabled){time = rtp_GetClock(); RTP_SingleFrame.member = time - RTP_SingleFrame.member;}}while(0) + +// returns the current clock time +#define RTP_GETCLOCK(time) do {if(Runtime_performance_enabled){time = rtp_GetClock();}}while(0) + +// Starts a frame time calculation, but when ending the frame, it will increment the time by +// the difference instead of just setting it +// RTP_STARTINCTIME and RTP_ENDINCTIME must be in the same scope +#define RTP_STARTINCTIME(member) INT64 __start_time_; do{if(Runtime_performance_enabled){__start_time_ = rtp_GetClock();}}while(0) + +// Ends a frame time calculation and increments the member by the time for that frame +#define RTP_ENDINCTIME(member) do{ if(Runtime_performance_enabled){RTP_SingleFrame.member += (rtp_GetClock() - __start_time_); }} while(0) + + +#endif + + +// Internal Global Vars +// ------------------------------ +extern tRTFrameInfo RTP_SingleFrame; +extern INT64 Runtime_performance_clockfreq; +extern unsigned char Runtime_performance_enabled; + +/* +void rtp_Init + This function initilizes the runtime performance system so it's ready to be used +*/ +void rtp_Init(void); + +/* +void rtp_EnableFlags + This function enables the performance profiling of whatever flags you pass in +*/ +void rtp_EnableFlags(INT64 flags); + +/* +void rtp_DisableFlags + This function disables the performance profiling of whatever flags you pass in +*/ +void rtp_DisableFlags(INT64 flags); + +/* +void rtp_StartLog + Calling this function will reset the log and start a new log, recording immediatly +*/ +void rtp_StartLog(void); + +/* +void rtp_StopLog + Calling this function will stop the currently processing log and write it out to + file. +*/ +void rtp_StopLog(void); + +/* +void rtp_PauseLog + Calling this function will pause the log recording until rtp_ResumeLog is called +*/ +void rtp_PauseLog(void); + +/* +void rtp_ResumeLog + Calling this will resume a paused log, starting at where it left off +*/ +void rtp_ResumeLog(void); + +/* +void rtp_RecordFrame + Calling this will record the data of the frame into the internal log, and prepare for + the next frame +*/ +void rtp_RecordFrame(void); + +/* +INT64 rtp_GetClock + Returns the current hi-resolution clock value...no checking for overflow +*/ +INT64 rtp_GetClock(void); + +/* +void rtp_WriteBufferLog + Writes the buffer of frames to file... +*/ +void rtp_WriteBufferLog(void); + + + + + + + + + +#endif \ No newline at end of file diff --git a/lib/soundload.h b/lib/soundload.h new file mode 100644 index 000000000..d13eefdc4 --- /dev/null +++ b/lib/soundload.h @@ -0,0 +1,58 @@ +#ifndef SOUNDLOAD_H_ +#define SOUNDLOAD_H_ + +#include "ssl_lib.h" + +extern int Num_sounds; +extern int Num_sound_files; + +// Allocs a sound file for use, returns -1 if error, else index on success +int AllocSoundFile (); + +// Frees sound index n +void FreeSoundFile (int n); + +// Gets next sound file from n that has actually been alloced +int GetNextSoundFile (int n); + +// Gets previous sound file from n that has actually been alloced +int GetPrevSoundFile (int n); + +// Searches thru all sounds for a specific name, returns -1 if not found +// or index of sound with name +int FindSoundFileName (char *name); + +// Given a filename, loads the sound file. +int LoadSoundFile (char *filename, float import_volume, bool f_get_data = false); + +// Sets all sounds to unused +void InitSounds (); + +// Allocs a sound for use, returns -1 if error, else index on success +int AllocSound (); + +// Frees sound index n +void FreeSound (int n); + +// Gets next sound from n that has actually been alloced +int GetNextSound (int n); + +// Gets previous sound from n that has actually been alloced +int GetPrevSound (int n); + +// Searches thru all sounds for a specific name, returns -1 if not found +// or index of sound with name +int FindSoundName (char *name); + +// Given a filename, loads the sound. +int LoadSound (char *filename); + +// This is a very confusing function. It takes all the sounds that we have loaded +// and remaps then into their proper places (if they are static). +void RemapSounds (); + +// goes thru every entity that could possible have a sound index (ie objects, robots, etc) +// and changes the old index to the new index +void RemapAllSoundObjects (int old_index,int new_index); + +#endif \ No newline at end of file diff --git a/lib/ssl_lib.h b/lib/ssl_lib.h new file mode 100644 index 000000000..d691367a1 --- /dev/null +++ b/lib/ssl_lib.h @@ -0,0 +1,566 @@ +/* + * $Logfile: /DescentIII/Main/lib/ssl_lib.h $ + * $Revision: 57 $ + * $Date: 3/20/00 12:27p $ + * $Author: Matt $ + * + * Header for shared sound library + * + * $Log: /DescentIII/Main/lib/ssl_lib.h $ + * + * 57 3/20/00 12:27p Matt + * Merge of Duane's post-1.3 changes. + * Lower MAX_SOUNDS and MAX_SOUND_FILES for Mac. + * + * 56 10/23/99 3:24p 3dsmax + * + * 55 10/21/99 4:05p Chris + * Removed a bad thing + * + * 54 10/21/99 1:48p Kevin + * Mac Merge + * + * 53 10/08/99 4:29p Chris + * Added the forcefield and glass breaking override options + * + * 52 8/23/99 5:29p Samir + * incremental EAX 2.0 checkin + * + * 51 8/10/99 2:08p Gwar + * added sound support to neweditor + * + * 50 5/20/99 1:00a Samir + * changes in ordering of EAX and NONE mixers. + * + * 49 5/11/99 2:08a 3dsmax + * + * 48 5/11/99 2:05a 3dsmax + * Increased MAX_SOUNDS so we can work + * + * 47 4/30/99 12:56p Kevin + * Lowered values for MAX_SOUNDS, MAX_ROOMS, MAX_TEXTURES and MAX_OBJIDS. + * Talk to me before changing any of these again. + * + * 46 4/27/99 2:10p Samir + * added code to set the desired sound card given the descriptive name of + * a sound card. + * + * 45 4/14/99 3:43p Matt + * Increased the number of sounds from 700 to 750. When I lowered this + * number to 700 recently, I counted that we were using around 600 sounds. + * Either I miscounted or we added a lot of sounds in the last few days. + * + * 44 4/11/99 5:04p Matt + * Reduced the maximum number of sounds from 1000 to 700. Currently we're + * only using 607 sounds, and the new static sound remapping code doesn't + * require any extra space, so 700 is probably ok for now at least. + * + * 43 4/10/99 5:09p Samir + * beginning to add prioritization of sounds code. currently non + * functional, but allows me to change all calls to Play3dSound so that I + * can test later. + * + * 42 4/06/99 8:29p Samir + * added error check system. + * + * 41 3/30/99 4:44p Matt + * Increased max sounds from 700 to 1000 + * + * 40 3/29/99 11:01a Samir + * added prototypes for 3d sound environment options. + * + * 39 3/17/99 4:20p Samir + * added functions to pause and resume individual sounds. + * + * 38 1/14/99 6:10p Samir + * added DirectSound buffer duplication code. + * + * 37 1/11/99 5:51p Samir + * reverb on the buffer level. + * + * 36 1/08/99 6:31p Samir + * added reverb + * + * 35 12/23/98 11:49a Samir + * added EAX mixer. + * + * 34 10/28/98 3:56p Chris + * + * 33 10/28/98 3:55p Chris + * Resized the buffers + * + * 32 10/19/98 11:57a Chris + * Update the sound system to use the import volume + * + * 31 10/14/98 5:12p Samir + * added a null SOUND_MIXER. + * + * 30 8/10/98 5:55p Samir + * added element in play_info to specify streaming buffer size. + * + * 29 7/24/98 5:23p Samir + * moved sound variables from soundload to ssl_lib.h (and WAV file stuff) + * + * 28 7/09/98 8:34p Samir + * added parameter to play_information. + * + * 27 7/06/98 1:49p Samir + * added some error checking. + * + * 26 6/29/98 10:44p Chris + * Added support for SIF_ONCE_PER_OBJ + * + * 25 6/29/98 7:35p Chris + * Increased sound buffer size + * + * 24 6/29/98 4:15p Chris + * Streaming audio works + * + * 23 6/29/98 9:29a Chris + * Added some support for Direct Sound 3d + * + * 22 6/25/98 11:28a Chris + * Added some DS3d support + * + * 21 6/24/98 6:43p Chris + * Furthered the endless work on the sound system + * + * 20 6/22/98 12:00p Chris + * Working on sound system to make it in a nice shape. + * + * 19 6/19/98 3:09p Chris + * Improved IsSoundPlaying + * + * 18 6/19/98 3:03p Chris + * Made CheckAndForceSoundDataAlloc a SoundSystem function - useful for + * multiple mixers. Added IsSoundPlaying to the high level sound lib. + * + * 17 6/17/98 7:04p Chris + * Added support for 8bit stream data to be played by our 16bit sound + * mixer + * + * 16 6/17/98 4:39p Chris + * Added the playing of 8bit samples with the 16 bit sound mixer + * + * 15 6/16/98 4:46p Chris + * Revision 2 of the stream audio stuff + * + * 14 6/16/98 3:48p Chris + * Updated the sound system and added the start of sound streaming + * + * 13 2/15/98 7:12p Chris + * More improvements to the sound lib + * + * 12 2/04/98 6:09p Matt + * Changed object room number to indicate a terrain cell via a flag. Got + * rid of the object flag which used to indicate terrain. + * + * 11 1/06/98 2:12p Chris + * Added muffled sounds v.1 and made 3d sounds keep their roomnum. + * + * 10 1/05/98 3:54p Chris + * Added ambient and explosion sounds + * + * 9 1/02/98 5:32p Chris + * More radical changes to the sound system + * + * 8 12/31/97 2:58a Chris + * Another major revision to the SoundLib. This cleaned up the code after + * removing the direct sound secondary buffers. + * + * 7 12/30/97 2:15p Chris + * Adding further support for software 3d stuff + * + * 6 12/29/97 4:50p Chris + * Added SPF_NO_UPDATE + * + * 5 12/22/97 6:19p Chris + * Working on new mixer functionality + * + * 4 12/18/97 12:26p Chris + * Incremental new lib improvements + * + * 3 12/10/97 4:47p Chris + * Revision 1.0 of new sound library (no hardware -- uses primary buffer + * streaming) + * + * 2 11/21/97 1:10p Chris + * Incremental Improvements + * + * 18 6/11/97 2:31p Samir + * Incorporated changes in gameos to new system + * + * 17 5/22/97 5:15p Chris + * + * 16 5/05/97 5:41a Chris + * Changed an include for MacMAN! + * + * 15 4/29/97 8:03a Chris + * Improved the sound code. High-level sound now + * fully uses the flags in the sound page and it + * resulted in a simpilier coding interface :) + * + * 14 4/28/97 11:21a Chris + * Incremental improvements is the sound system + * + * 13 4/24/97 4:19a Chris + * Modified the pos_state struct to be more compact. + * + * 12 4/24/97 3:25a Chris + * // Added some more support for 3d sounds + * + * 11 4/03/97 8:37p Chris + * Added the Main_OS object pass in the sound library + * initialization code. + * + * 10 4/01/97 2:13p Jason + * changes for sound page functionality + * + * 9 4/01/97 10:00a Chris + * + * 8 3/17/97 5:12p Chris + * Allowed new sound code to be demo'ed. + * + * 7 3/14/97 12:10p Chris + * Made a nice C++ abstract type for the low-level. It should make the + * MacMan very joyful. :) + * + * 6 2/07/97 5:48p Matt + * Renamed vector.h to vecmat.h to fix DevStudio problem + * + * $NoKeywords: $ + */ + +#ifndef __SSL_LIB_H__ +#define __SSL_LIB_H__ + +#include "vecmat.h" + +#ifndef NEWEDITOR +#include "manage.h" +#else + #include "..\neweditor\ned_TableFile.h" +#endif + +class oeApplication; // reference to oeApplication class. + +#define MAX_GAME_VOLUME (float) 1.0 //helps against clipping + +// Size of streaming buffers +#define STREAM_BUFFER_SIZE 4096 //(4 * 1024) + +//object information needed by the sound code. Calling code should copy data into here & pass to sound +typedef struct +{ + vector *position; // Where in the world is this object + matrix *orient; + vector *velocity; + int roomnum; +} pos_state; + +//#define MAX_EFFECT_OFFSETS 5 + +// sound priority values. +#define SND_PRIORITY_CRITICAL 5 // usually streams have this priority, bumps off any other sounds. +#define SND_PRIORITY_HIGHEST 4 +#define SND_PRIORITY_HIGH 3 +#define SND_PRIORITY_NORMAL 2 +#define SND_PRIORITY_LOW 1 +#define SND_PRIORITY_LOWEST 0 + + +typedef struct +{ + + void *(*m_stream_cback)(void *user_data, int handle, int *size); // Streaming callback + void *m_stream_data; // passed in + int m_stream_size; // passed in + int m_stream_handle; // passed in + int m_stream_bufsize; // passed in + void *user_data; // this is passed to the stream callback by the caller that defined this. + + ubyte sample_skip_interval; // Allows us to skip samples (i.e. simulate lower sampling rates) + ubyte priority; // priority of sound. + ushort m_stream_format; // passed in + +// internal data. + int m_samples_played; + +#ifndef MACINTOSH + float samples_per_22khz_sample; // passed in + float left_volume; + float right_volume; + + float m_ticks; +#endif // Always incrementing counter (current sample position if no looping) +} play_information; + +typedef struct sound_file_info +{ + char name[PAGENAME_LEN]; + char used; + int use_count; // how many buffers does this sound take up. + + unsigned char *sample_8bit; // 8bit sound data + short * sample_16bit; // 16bit sound data + + int sample_length; // Length of sound in samples + int np_sample_length; // non-padded + +} sound_file_info; + +typedef struct sound_info +{ + char name[PAGENAME_LEN]; + char used; + + int sample_index; + + int loop_start; // Start byte of repeated loop for looping samples + int loop_end; // End byte of repeating loop for looping samples + unsigned int flags; // 2d/3d, variable frequency + float max_distance; // Maximum distance in which a sound is heard + float min_distance; // Sound gets no louder at min_distance + int inner_cone_angle; // Angle in which sound is played at full base volume + int outer_cone_angle; // Angle in which sound is at its lowest base volume + float outer_cone_volume;// A sounds lowest base volume level + float import_volume; // Volume multiplier +} sound_info; + +// Supported sound mixers +#define SOUND_MIXER_SOFTWARE_16 0 +#define SOUND_MIXER_DS_16 1 +#define SOUND_MIXER_DS_8 2 +#define SOUND_MIXER_DS3D_16 3 +#define SOUND_MIXER_AUREAL 4 +#define SOUND_MIXER_CREATIVE_EAX 6 // switched because launcher uses 5 as NONE. +#define SOUND_MIXER_NONE 5 + +// Support sound qualities +#ifdef MACINTOSH +#define SQT_LOW 0 +#define SQT_NORMAL 1 +#define SQT_HIGH 2 +#else +#define SQT_NORMAL 0 +#define SQT_HIGH 1 +#endif +// Parameters of the sound library +#define SLF_USE_3D 1 // Use 3d effects +#define SLF_DELTA_FREQ 2 // Use frequency shifts (i.e. water effects) +#define SLF_USE_16_BIT 4 // Use 16bit samples (else 8bit) +#define SLF_USE_22_KHZ 8 // Use 22khz (else 44khz) +#define SLF_PAUSED 16 // Sound library is currently paused +#define SLF_FULL_3D 32 // Full 3d hardware support +#define SLF_MOST_3D 64 // No fully static 3d -- i.e. cockpit type stuff (use 2d instead) +#define SLF_LIGHT_3D 128 // Dynamically updating 3d sounds if sound is longer than a given threshold +#define SLF_GOOD_2D 256 // all linked sounds update position +#define SLF_OK_2D 512 // if a sound is longer than a threshold, it updates + +// Sound Properties Flags +#define SPF_LOOPED 1 // Sound is looped +#define SPF_FIXED_FREQ 2 // No doppler shift +#define SPF_OBJ_UPDATE 4 // Sound updates with attached object movements +#define SPF_FOREVER 8 // Always plays in high-level, this flag should be ignored in low-level +#define SPF_PLAYS_EXCLUSIVELY 16 +#define SPF_PLAYS_ONCE 32 +#define SPF_USE_CONE 64 +#define SPF_LISTENER_UPDATE 128 // Sound updates with listener movements +#define SPF_ONCE_PER_OBJ 256 + +// Sound Instance flags (Move this out of here) +#define SIF_UNUSED 0 // Not a valid sound item +#define SIF_PLAYING_2D 1 // Sound is currently playing +#define SIF_PLAYING_3D 2 +#define SIF_OBJ_UPDATE 4 +#define SIF_TOO_FAR 8 // We will play it, but it currently too far away(stop sound in low-level) +#define SIF_NO_3D_EFFECTS 16 +#define SIF_LOOPING 32 +#define SIF_STREAMING_8_M 64 +#define SIF_STREAMING_16_M 128 +#define SIF_STREAMING_8_S 256 +#define SIF_STREAMING_16_S 512 +#define SIF_STREAMING (64 | 128 | 256 | 512) + +// What is the sound cone linked to (and mask to make it else to look at the important bits) +#define SPFT_CONE_LINK_MASK 0x00000300 +#define SPFT_CONE_LINK_OBJECT 0x00000000 +#define SPFT_CONE_LINK_TURRET1 0x00000100 +#define SPFT_CONE_LINK_TURRET2 0x00000200 +#define SPFT_CONE_LINK_TURRET3 0x00000300 + +// Direction of the sound cone relative to its link (and mask to make it else to look at the important bits) +#define SPFT_CONE_DIR_MASK 0x00000C00 +#define SPFT_CONE_DIR_FORWARD 0x00000000 +#define SPFT_CONE_DIR_BACKWARD 0x00000400 +#define SPFT_CONE_DIR_UPWARD 0x00000800 +#define SPFT_CONE_DIR_DOWNWARD 0x00000C00 + +// Sound kill types +#define SKT_STOP_AFTER_LOOP 0 // Allows a looping sample to play until the end of the sample +#define SKT_STOP_IMMEDIATELY 1 // Stops and cleans up after a sound (For StopAllSounds) +#define SKT_HOLD_UNTIL_STOP 2 // Hold until sound stops. + +// Sound Library Internal Error Codes +#define SSL_OK 0 + +// structure to get and set environment values +#define ENV3DVALF_DOPPLER 1 +#define ENV3DVALF_GEOMETRY 2 + +typedef struct t3dEnvironmentValues +{ + int flags; // use flags above + + float doppler_scalar; // values from 0.0f to ???? (1.0f = normal) +} +t3dEnvironmentValues; + +typedef struct t3dEnvironmentToggles +{ + int flags; // use flags above + int supported; // returns flag values to inform caller of supported features (doppler, ie.) + + bool doppler; // state of doppler effects + bool geometry; // support hardware geometry +} +t3dEnvironmentToggles; + + + +///////////////////////////////////////////////////////////////////////////////// +// Looping constants + +class llsGeometry; + +class llsSystem +{ +protected: + llsGeometry *m_geometry; // geometry object. + int m_lib_error_code; // library error code + char m_error_text[512]; // text for error. + +protected: + void SetError(int code) { m_lib_error_code = code; }; + void ErrorText(char *fmt, ...); // error text function called inside library. a stack is kept of errors + virtual void CheckForErrors(); // called by sound library every frame to reset error count. + +// Public functions +public: + llsSystem(); + + // may be called before init (must be to be valid, the card passed here will be initialized in InitSoundLib) + virtual void SetSoundCard(const char *name) = 0; + + // Starts the sound library, maybe have it send back some information -- 3d support? + virtual int InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played) = 0; + // Cleans up after the Sound Library + virtual void DestroySoundLib(void) = 0; + + // Locks and unlocks sounds (used when changing play_info data) + virtual bool LockSound(int sound_uid) = 0; + virtual bool UnlockSound(int sound_uid) = 0; + + virtual bool SetSoundQuality(char quality) = 0; + virtual char GetSoundQuality(void) = 0; + virtual bool SetSoundMixer(char mixer_type) = 0; + virtual char GetSoundMixer(void) = 0; + + // Plays a 2d sound + virtual int PlaySound2d(play_information *play_info, int sound_index, float volume, float pan, bool f_looped) = 0; + virtual int PlayStream(play_information *play_info) = 0; + + virtual void SetListener(pos_state *cur_pos) = 0; + virtual int PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float master_volume, bool f_looped, float reverb=0.5f) = 0; //, unsigned short frequency) + virtual void AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency) = 0; + virtual void AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb=0.5f) = 0; + + virtual void StopAllSounds(void) = 0; + + // Checks if a sound is playing (removes finished sound); + virtual bool IsSoundInstancePlaying(int sound_uid) = 0; + virtual int IsSoundPlaying(int sound_index) = 0; + +// virtual void AdjustSound(int sound_uid, play_information *play_info) = 0; + + // Stops 2d and 3d sounds + virtual void StopSound(int sound_uid, unsigned char f_immediately = SKT_STOP_IMMEDIATELY) = 0; + + // Pause all sounds/resume all sounds + virtual void PauseSounds(void) = 0; + virtual void ResumeSounds(void) = 0; + virtual void PauseSound(int sound_uid) = 0; + virtual void ResumeSound(int sound_uid) = 0; + + virtual bool CheckAndForceSoundDataAlloc(int sound_file_index) = 0; + + // Begin sound frame + virtual void SoundStartFrame(void) = 0; + + // End sound frame + virtual void SoundEndFrame(void) = 0; + + // Returns current error code + int GetLastError() { int code = m_lib_error_code; m_lib_error_code = 0; return code; }; + + // environmental sound interface + // volume modifier (0-1), damping(0-1), 1 = complete, 0 = none + // decay 0.1 to 100 seconds, how long it takes for a sound to die. + virtual bool SetGlobalReverbProperties(float volume, float damping, float decay) = 0; + + // set special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentValues(const t3dEnvironmentValues *env) = 0; + + // get special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentValues(t3dEnvironmentValues *env) = 0; + + // enable special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void SetEnvironmentToggles(const t3dEnvironmentToggles *env) = 0; + + // get states of special parameters for the 3d environment. + // of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify + virtual void GetEnvironmentToggles(t3dEnvironmentToggles *env) = 0; + + // returns interface to sound geometry manipulation if available. + virtual llsGeometry *GetGeometryInterface() { + return m_geometry; + }; +#ifdef MACINTOSH + virtual void SetNumChannels(ubyte num_chan) = 0; +#endif +///////////////////////////////////////////////////////////////////////////////// + // set auxillary 3d sound properties + virtual bool SoundPropertySupport() const { return false; }; + + // sound obstruction from 0 to 1.0 (1.0 = fully obstructed) + virtual void SetSoundProperties(int sound_uid, float obstruction) {}; +}; + +// TAKEN FROM SNDLIB SOUNDLOAD.CPP TO SEPARATE CODE REQUIRED BY THE LOWLEVEL SYSTEM AND THE +// HIGH LEVEL SYSTEM - Samir + +#ifndef NEWEDITOR + #ifdef MACINTOSH + #define MAX_SOUNDS 750 + #define MAX_SOUND_FILES 750 + #else + #define MAX_SOUNDS 1000 + #define MAX_SOUND_FILES 1000 + #endif + extern sound_info Sounds[MAX_SOUNDS]; + extern sound_file_info SoundFiles[MAX_SOUND_FILES]; +#else + #include "..\neweditor\ned_Sound.h" +#endif + +extern sound_file_info SoundFiles[MAX_SOUND_FILES]; + +// loads a sound from a wavefile. +char SoundLoadWaveFile(char *filename, float percent_volume, int sound_file_index, bool f_high_quality, bool f_load_sample_data, int *e_type = NULL); + +void SoundLoadFree(int sound_file_index); + +#endif \ No newline at end of file diff --git a/lib/streamaudio.h b/lib/streamaudio.h new file mode 100644 index 000000000..c73366330 --- /dev/null +++ b/lib/streamaudio.h @@ -0,0 +1,345 @@ +/* +* $Logfile: /DescentIII/Main/lib/streamaudio.h $ +* $Revision: 1.2 $ +* $Date: 2004/02/09 04:14:51 $ +* $Author: kevinb $ +* +* Interface for streaming audio, compressed or not +* +* $Log: streamaudio.h,v $ +* Revision 1.2 2004/02/09 04:14:51 kevinb +* Added newlines to all headers to reduce number of warnings printed +* +* Made some small changes to get everything compiling. +* +* All Ready to merge the 1.5 tree. +* +* Revision 1.1.1.1 2000/04/18 00:00:38 icculus +* initial checkin +* + * + * 30 10/25/99 6:00p Jeff + * fixed adecode.h include for Linux + * + * 29 10/21/99 1:52p Kevin + * Mac Merge! + * + * 28 9/30/99 4:35p Jeff + * fixed linux typo + * + * 27 9/30/99 4:29p Jeff + * Linux compile + * + * 26 7/28/99 3:04p Kevin + * Macintosh! + * + * 25 5/17/99 1:50p Ardussi + * changed to compile on Mac + * + * 24 5/13/99 5:04p Ardussi + * changes for compiling on the Mac + * + * 23 5/10/99 9:22p Samir + * added code to allow for certain streams to be specified to start on + * next sound frame. + * + * 22 4/15/99 1:44a Jeff + * changes for linux compile + * + * 21 4/10/99 5:09p Samir + * beginning to add prioritization of sounds code. currently non + * functional, but allows me to change all calls to Play3dSound so that I + * can test later. + * + * 20 3/04/99 6:38p Samir + * Saved stream state per frame. + * + * 19 3/03/99 5:08p Samir + * added slew of debug code: hopefully the library will be revamped after + * OEM. + * + * 18 2/28/99 5:11p Nate + * added playcount. + * + * 17 2/27/99 6:52p Samir + * added function to get current loop count + * + * 16 2/26/99 5:26p Samir + * fixes to streaming audio to reflect fix in direct sound mixer. + * + * 15 1/13/99 6:48a Jeff + * made linux friendly with some #ifdef's + * + * 14 12/13/98 6:50p Samir + * fixed problems with looping and next.switching sync. + * + * 13 12/11/98 3:27p Samir + * add debug code. + * + * 12 12/10/98 7:12p Samir + * fixed some bugs in file/playback buffer sequencing with new system. + * + * 11 12/10/98 10:11a Samir + * newer streaming audio library. + * + * 8 11/20/98 5:20p Samir + * correcting a lot of errors in AudioStream::Next + * + * 7 11/13/98 2:25p Samir + * added 'next' stream processing. + * + * 7 11/13/98 10:48a Samir + * added 'next' stream capability. + * + * 6 10/23/98 7:05p Samir + * added pause and resume + * + * 5 8/10/98 5:54p Samir + * added looping streams and soft measure stopping. + * + * 4 7/24/98 5:19p Samir + * added osfarchive class, + * + * 3 7/09/98 8:36p Samir + * fully implemented redo of Stream interface. + * + * 2 7/08/98 6:27p Samir + * stream library integrated with highlevel sound system. + * + * 1 7/08/98 6:22p Samir + * moved from man. + * + * 3 6/19/98 6:07p Jeff + * added a function to return a handle to the sound + * + * 2 6/18/98 5:17p Jeff + * Initial creation, support should be pretty much complete +* +* $NoKeywords: $ +*/ +#ifndef __STREAMAUDIO_H_ +#define __STREAMAUDIO_H_ + +#include "Adecode.h" + +#include "ssl_lib.h" +#include "TaskSystem.h" + +void *AudioStreamCB(void *user_data, int handle, int *size); +int ADecodeFileRead(void *data, void *buf, unsigned int qty); +int StreamPlay(const char *filename, float volume, int flags); +void StreamStop(int handle); +int StreamGetSoundHandle(int handle); + +////////////////////////////////////////////////////////////////////////////// +class llsSystem; +struct CFILE; +// stream file info +#define OSF_HDR_SIZE 128 +#define OSF_TAG "OSF1" +#define OSF_HDR_TITLE_OFS 64 +#define OSF_HDR_TITLE_LEN 32 +// stream type +#define OSF_DIGITAL_STRM 0 +// compression type +#define OSF_DIGIRAW_STRM 0 +#define OSF_DIGIACM_STRM 1 +// stream format +#define SAF_8BIT_MASK 0x00 +#define SAF_16BIT_MASK 0x01 +#define SAF_MONO_MASK 0x00 +#define SAF_STEREO_MASK 0x10 + +#define SAF_8BIT_M (SAF_8BIT_MASK | SAF_MONO_MASK) //0x00 +#define SAF_8BIT_S (SAF_8BIT_MASK | SAF_STEREO_MASK) //0x10 +#define SAF_16BIT_M (SAF_16BIT_MASK | SAF_MONO_MASK) //0x01 +#define SAF_16BIT_S (SAF_16BIT_MASK | SAF_STEREO_MASK) //0x11 +////////////////////////////////////////////////////////////////////////////// +typedef struct tOSFDigiHdr // this struct belongs to OSF_DIGITAL_STRM +{ + uint measure; +} +tOSFDigiHdr; +class OSFArchive +{ + CFILE *m_fp; + uint m_length; // file information stored such as length + ubyte m_type; // stream type + ubyte m_comp; // compression type + ubyte m_flags; // format + uint m_rate; // frequency + bool m_writemode; // are we in write mode? + union { + tOSFDigiHdr digi; + } + m_hdr; + char m_name[OSF_HDR_TITLE_LEN]; // title string length. + int m_datastart; // pointer to start of data. +public: + OSFArchive(); + ~OSFArchive(); + bool Open(const char *filename, bool write=false); + void Close(); + bool Opened() const { return m_fp ? true : false; }; + void Rewind(); +// write out operations. + bool SaveHeader(ubyte type, ubyte comp, ubyte flags, uint rate, uint length, void *hdr, const char *name); + bool WriteBlock(ubyte *blk, int size); +// read in operations. + int Read(ubyte *blk, int size); +// get archive info. + ubyte StreamType() const { return m_type; }; + ubyte StreamComp() const { return m_comp; }; + ubyte StreamFormat() const { return m_flags; }; + uint StreamRate() const { return m_rate; }; + uint StreamLength() const { return m_length; }; + const char *StreamName() const { return (const char *)m_name; }; +// get header info. + const void *StreamHeader() const { return (void *)&m_hdr.digi; }; +}; +////////////////////////////////////////////////////////////////////////////// +// streamaudio constants. +#ifdef MACINTOSH +#define STRM_BUFCOUNT 2 // MUST be a power of 2. +#else +#define STRM_BUFCOUNT 4 +#endif +#define STRM_BUFSIZE STREAM_BUFFER_SIZE +#define STRM_LIMIT 4 +#define STRM_STOPPED 0x0 +#define STRM_PLAYING 0x1 +#define STRM_PAUSED 0x2 +#define STRM_STOPPING 0x4 +#define STRM_PENDING 0x8 //DAJ +#define STRM_INVALID 0xff +// flags used to open stream. +#define STRM_OPNF_ONETIME 0x1 +#define STRM_OPNF_GRADUAL 0x2 +/* This class will handle streams for the music system. + Including allowing an interface to dynamically change the stream. +*/ +#define STRM_BUFF_USED 0x1 // allocated buffer with data +#define STRM_BUFF_TERMINAL 0x2 // terminates on this buffer +#define STRM_BUFF_LOOPEND 0x4 // marks last buffer in measure +class AudioStream +{ + OSFArchive m_archive; // audio stream archive object. + AudioDecoder::IAudioDecoder* m_decoder; // audio codec object +#ifdef MACINTOSH + SndDoubleBufferHeader doubleHeader; + SndChannelPtr strm_channel; +#endif + struct { // mixing buffers + ubyte *data; + int nbytes; // number of bytes of valid data. + int flags; + int id; + } + m_buffer[STRM_BUFCOUNT]; + int m_bufsize; // size of each buffer. + float m_measure_timer; // timer for measure checking. + float m_measure_time; // amount of time per measure. + float m_last_frametime; + ubyte m_sbufidx; // stream position markers + ubyte m_fbufidx; // file position markers + ubyte m_curbufidx; // current buffer in measure index + ubyte m_playcount; + bool m_readahead; // if stream is currently reading from disk + bool m_readahead_finished_loop; // if a loop's readahead has finished + short m_nbufs; // number of buffers streamed so far. + play_information m_playinfo; // used by llsSystem + float m_volume; // volume of stream. + short m_state, m_laststate; // current state of stream playing + int m_llshandle; // internal sound handle. + int m_flags; // stream playing options. + short m_streamindex; // index into active stream table. + short m_loopcount; // loop counter. + int m_bytesleft; // number of bytes left in file + int m_curmeasure; // current measure. + int m_playbytesleft, m_playbytestotal; + int m_curid; // stream's id # + int *m_stopflag; // location of stop flag used in stop function + osMutex m_loopmutex; // stop flag is manipulated by caller and stream thread. + bool m_loop; // are we looping? + bool m_stopnextmeasure; // stop on next measure. + bool m_start_on_frame; // we will play this stream on the next ::Frame call. + bool m_start_on_frame_looped; // the stream that will play on next frame is looped. +private: + friend void *AudioStreamCB(void *user_data, int handle, int *size); + friend int ADecodeFileRead(void *data, void *buf, unsigned int qty); + void *StreamCallback(int *size); // invoked by omsStreamCB. + int ReadFileData(int buf, int len); // reads in decompressed raw data. + int ReadFileDirect(char * buf, int len); // reads in decompressed raw data. + void UpdateData(); // updates file buffers + void End(); // cleans up after a stop. + void Reset(); // resets to start of stream. + bool OpenDigitalStream(); // opens and prepares a digital stream + bool ReopenDigitalStream(ubyte fbufidx, int nbufs); +private: +// attach a low level sound system to all streams. + static llsSystem *m_ll_sndsys; +// list of all currently played streams + static AudioStream *m_streams[STRM_LIMIT]; + static int m_thisid; +// allocates a stream slot for a stream + bool ActivateStream(AudioStream *stream); + void DeactivateStream(AudioStream *stream); +public: +// sets the low-level sound object + static void InitSystem(llsSystem *sndsys); +// shutdsown + static void Shutdown(); +// called by application to allow stream playback + static void Frame(); +// called to pause all streams. + static void PauseAll(); + static void ResumeAll(); +#ifdef MACINTOSH + bool IsPlaying(void); + int PlayStream(play_information *play_info); + void * MyDoubleBackStart (SndChannelPtr channel, SndDoubleBufferPtr doubleBuffer); +#endif +public: + AudioStream(); + ~AudioStream(); +// simple operations +// flags specify what type of stream you want. + bool Open(const char *filename, int open_flags=0); +// specifies next stream to be opened when current one ends. + void Close(); +// simple requests + bool Play(bool start_on_frame=false); // plays a stream + void Stop(bool on_measure=false,int *stop_flag=NULL); // causes a rewind to start of stream, if on_measure is true, stop occurs when measure ends + void SetVolume(float vol); // sets volume + float GetVolume(); // returns volume +// misc requests + int GetLoopCount() const; // 0 = infinite. + void SetLoopCount(int loop_count); // sets the number of times this stream will loop (0 = infinite!) + void SetFlags(int flags); // sets flags for playback (STRM_OPNF_XXX) + void Pause(); // pauses a stream + void Resume(); // resumes a paused stream. +// state. + bool ReadAhead(); // are we still reading from disk? + bool ReadAheadFinishedLoop(); // has stream finished with its readahead of current loop? + bool IsReady(); // is this stopped stream ready to play? + int State() const { // returns current state + return m_state; + }; +// information + int GetLength() const { return m_archive.StreamLength(); }; + int GetPos() const { return m_archive.StreamLength() - m_bytesleft; }; + int GetBufferSize() const { return m_bufsize; }; + int CurrentMeasure() const { // returns current measure. + return m_curmeasure; + }; + int TotalMeasures() const; // total measure count + int GetSoundHandle() const { // returns the sound library handle for this stream. + return m_llshandle; + }; +// these functions are the 'simplified' stream interface from Jeff (most of this code is from Jeff) + friend int StreamPlay(const char *filename, float volume, int flags); + friend void StreamStop(int handle); + friend int StreamGetSoundHandle(int handle); +}; +#endif + diff --git a/lib/textaux.h b/lib/textaux.h new file mode 100644 index 000000000..aa527e8a3 --- /dev/null +++ b/lib/textaux.h @@ -0,0 +1,72 @@ +/* +* $Logfile: /DescentIII/Main/lib/textaux.h $ +* $Revision: 4 $ +* $Date: 11/03/98 7:04p $ +* $Author: Samir $ +* +* Auxillary Text functions (helper function, not necessarily belonging to grtext) +* +* $Log: /DescentIII/Main/lib/textaux.h $ + * + * 4 11/03/98 7:04p Samir + * made Grtext_spacing global so word wrapper could access it. + * + * 3 7/14/98 2:47p Jeff + * added textaux_ClipString + * + * 2 7/14/98 11:53a Samir + * moved textaux to it's own libtary again. + * + * 1 7/13/98 4:41p Samir + * + * 2 7/11/98 9:14p Jeff + * initial creation (moved from TelCom) +* +* $NoKeywords: $ +*/ + +#ifndef TEXTAUX_H +#define TEXTAUX_H + +// textaux_WordWrap +// +// Given a buffer of text, and an empty buffer of same size +// it will break the source buffer up into lines (seperated by /n) of size width or smaller (in pixels). +// All /n within the source buffer are preserved. Source buffer is also not changed. +void textaux_WordWrap(const char *src,char *dest,int width,int font); + +// textaux_CopyTextLine +// +// This function goes hand-in-hand with textaux_WordWrap(). Given a buffer of data it will fill in +// the dest buffer until it hits a /n or /0. It returns a pointer to the start position of the next line, +// or NULL if it's done with the buffer (it hit a /0). +// +// Example of use: +// +// char *nextline; +// char linebuffer[256]; +// int y = INITIAL_Y; +// int x = INITIAL_X; +// +// nextline = textaux_CopyTextLine(buffer,linebuffer); +// +// while((nextline)&&((y+font_height)= m_Range) pos = m_Range-1; + m_Pos = pos; + }; + int GetPos() const { // gets position of slider. + return m_Pos; + }; + + void SetSelectChangeCallback(void (*fn)(int)); + void SetSelectChangeCallback(void (*fn)(int,void *),void *ptr); + +// called from outside gadget hierarchy. +protected: + virtual void OnFormat(); // override: called when resized or before drawing. + virtual void OnLostFocus() {}; // override: behavior when gadget loses input focus. + virtual void OnGainFocus() {}; // override: behavior when gadget gains input focus. + virtual void OnDraw(); // behavior when gadget is being drawn. + virtual void OnDestroy(); // behavior when gadget is being destroyed. + virtual void OnKeyDown(int key); // behavior when key is pressed. + virtual void OnKeyUp(int key); // behavior when key is released. + virtual void OnMouseBtnDown(int btn); // behavior when mouse button is pressed. + virtual void OnMouseBtnUp(int btn); // behavior when mouse button is released. + virtual void OnSelect(); // override +}; + + + +// ---------------------------------------------------------------------------- +// UIListBox +// A listbox may contain text items +// Allows the user to scroll through them too. + +#define LISTBOX_BUFFER_SIZE 20 + +class UIListBox: public UIGadget +{ + void (*selectchange_fn)(int); // callback when selection changes in listbox. + void (*selectchange_id_fn)(int,void *); // callback when selection changes in listbox..also return the ID + void *m_callbackptr; // user defined callback pointer + ddgr_color m_SelectColor; // selected text color + ddgr_color m_HiliteColor; // color of hilite bar + int m_LastMseX, m_LastMseY; // used for double clicking. + ubyte m_Alpha; // alpha value of listbox. + + bool MseCheckSelect(int x, int y); // check if x and y selection hits a selected item. + void SetInternalSelectedIndex(int index); // call by listbox system. + +protected: + int m_TextOffX, m_TextOffY; // offset of text to border. + int m_ArrowWidth; // with of arrow. + int m_CX, m_CY, m_CX2, m_CY2; // clipping text. + +public: + UIListBox(); + virtual ~UIListBox(); + + void Create(UIWindow *parent, int id, int x, int y, int w, int h, int flags=0); + + virtual tUIClass Class() const { // Overide this function to name the class + return uiListBox; + }; + +// settings + void AddItem(const UIItem *item); // adds an item to the list, no sorting + void RemoveItem(const UIItem *item); // removes an item from the list. + void RemoveAll(); // empties listbox + void SelectItem(const UIItem *item); // selects the given item in the list. + UIItem *GetItem(int index) const; // returns the item at listbox index given. + + int GetListIndex() const; // returns the current listbox index + void SetListIndex(int index); // sets the index of a listbox + + int GetSelectedIndex() const; // returns which item index is selected + void SetSelectedIndex(int index); // selects an item based on index and moves listbox + void SetSelectedColor(ddgr_color col); // selected text color. + void SetHiliteColor(ddgr_color col); // sets the hilite bar color + + void SetSelectChangeCallback(void (*fn)(int)); + void SetSelectChangeCallback(void (*fn)(int,void *),void *ptr); + +// called from outside gadget hierarchy. +protected: + virtual void OnLostFocus(); // override: behavior when gadget loses input focus. + virtual void OnGainFocus(); // override: behavior when gadget gains input focus. + virtual void OnDraw(); // behavior when gadget is being drawn. + virtual void OnKeyDown(int key); // behavior when key is pressed. + virtual void OnKeyUp(int key); // behavior when key is released. + virtual void OnMouseBtnDown(int btn); // behavior when mouse button is pressed. + virtual void OnMouseBtnUp(int btn); // behavior when mouse button is released. + virtual void OnSelect(); + virtual void OnDestroy(); // called when destroyed. + virtual void OnUserProcess(); // override: behavior when gadget is processed + +private: + UIItem **m_ItemList; // list of items in listbox + int *m_Virt2Real; //translates virtual(user) id to real index + int *m_Real2Virt; //translates real index into virtual(user) id + + float m_ClickTime; // done to check for double clicks? + int m_MouseState; // done for mouse selection. + int m_MouseX, m_MouseY; + int m_NumItems; // number of items in list. + int m_SelectedIndex; // current selected index into listbox + int m_Index; // current index of visible items. + bool m_ShowDown,m_ShowUp; //whether the down and up arrows are displayed + int m_UpArrowY0, m_DownArrowY0; // up and down arrow y locations. + int m_UpArrowY1, m_DownArrowY1; // up and down arrow y max locations. + int m_NumVisibleItems; // number of visible items in list viewport. + +public: + int GetNumItems() const { + return m_NumItems; + }; +}; + + +// ---------------------------------------------------------------------------- +// UIComboBox +// A listbox may contain text items, BUT only one will be visible +// Allows the user to scroll through them too. + +class UIComboBox: public UIGadget +{ +public: + UIComboBox(); + virtual ~UIComboBox(); + + void Create(UIWindow *parent, int id, int x, int y, int w, int h, int flags=0); + +private: + UIItem **m_ItemList; // list of items in listbox + int m_nItems; // number of items. + int m_ArrY,m_ArrH; // up and down arrow dimensions + int m_UpArrX,m_UpArrW,m_DownArrX,m_DownArrW; + int m_Index; // current index. + void (*selectchange_fn)(int); // callback when selection changes in listbox. + bool m_ShowUp, m_ShowDown; + float m_scrolltimer; // scroll timer for delay in mouse. + int m_scrollstage; // determines how scrolling will work. + +public: + void AddItem(const UIItem *item); + void RemoveItem(const UIItem *item); // removes an item from the list. + void RemoveAll(); // empties listbox + void SelectItem(const UIItem *item); // selects the given item in the list. + UIItem *GetItem(int index) const; // returns the item at listbox index given. + + int GetSelectedIndex() const { // returns which item index is selected + return m_Index; + }; + void SetSelectedIndex(int index); // sets the selected index. + + void SetSelectChangeCallback(void (*fn)(int)); + +protected: + virtual void OnDraw(); // behavior when gadget is being drawn. + virtual void OnKeyDown(int key); // behavior when key is pressed. + virtual void OnMouseBtnDown(int btn); // behavior when mouse button is pressed. + virtual void OnMouseBtnUp(int btn); // behavior when mouse button is released + virtual void OnSelect(); + virtual void OnDestroy(); // called when destroyed. + +protected: + int m_CX, m_CY, m_CX2, m_CY2; // text clippers. +}; + + +// ---------------------------------------------------------------------------- +// UIConsoleGadget +// a console that's a gadget within a window + +class UIConsoleGadget: public UIGadget +{ + int m_ConsoleFont; // standard font for text in console + ddgr_color *m_ColorRows; // color per row. + char *m_ConsoleBuffer; // text buffer of console. + int m_Rows, m_Cols; // dimensions in text. + int m_VisRows; // visible rows and columns + int m_VisRowStart; // scroll start of visible region of buffer. + int m_CurRow, m_CurCol; // current row and column of text pointer + int m_OffX, m_OffY; + int m_Rowsize; // rowsize of character buffer. + ddgr_color m_LineColor; // color of current line. + int m_LineIndex; // index within current row memory-wise(not column wise). + int m_PutsBufLen; // current length of puts buffer + char *m_PutsBuffer; // puts buffer. + +private: + void PutChar(int ch); // outputs a character onto the console. + void Scroll(); // scrolls window. + +protected: + virtual void OnDraw(); // behavior when gadget is being drawn. + virtual void OnUserProcess(); // behavior when gadget is processed + virtual void OnDestroy(); // behavior when gadget is destroyed. + +public: + UIConsoleGadget(); + virtual ~UIConsoleGadget(); + +// font = console font, NOT GADGET FONT. This font is used to proportion window + void Create(UIWindow *parent, int id, int x, int y, int font, int cols, int rows, int flags=UIF_BORDER); + +// functions + void puts(ddgr_color color, const char *str); +}; + + + +// ---------------------------------------------------------------------------- +// UIWindow +// A window contains different UIGadgets. +// This is where all objects will interact with the user. +#define N_WINDOW_ACCELS 48 + +class UIWindow: public UIObject +{ + friend class UIGadget; + + UIGadget *m_GadgetHead; // gadget list for window + UIGadget *m_GadgetTail; + UIGadget *m_GadgetCur; + UIGadget *m_LockedGadget; // marks a particular gadget that will always have focus + UIItem *m_BackItem; // background UI item. + + float m_KeyTime; // time when key went down. + + ddgr_color m_BackColor; // unconditional background color + int m_FontHandle; + int m_LastKey; // the last key pressed. + int m_LastKeyCount; // number of times last key was pressed. + float m_LastKeyTime; // last key time. + bool m_HoldHotkeys; + bool m_ResetCurGadget; // next process will reset current gadget to 1st valid one. + bool m_CurGadgetInGroup; // is m_GadgetCur inside a group? + struct { + int key; + int id; + } + m_Accelerators[N_WINDOW_ACCELS]; // used for quick key press interaction + int m_naccels; + + static int m_WindowFont; // global window default font + +private: + bool TrapKey(int key); + void UntrapKey(int key); + void LockFocusOnGadget(UIGadget *gadget); // forces a particular gadget into focus, always until unlocked. + void UnlockFocusOnGadget(); + +protected: + ddgr_color GetBackColor() const { + return m_BackColor; + }; + + int m_Flags; // window flags. + +// inheritable actions +protected: + virtual void OnDraw(); // overridable draws the window background before gadgets + virtual void OnUserProcess(); // overridable: runs after window is processed, to supplement it. + virtual void OnDestroy() {}; // overridable: called in Destroy + virtual void OnKeyDown(int key); // behavior when key is pressed. + virtual void OnKeyUp(int key); // behavior when key is released. + +public: + static void SetDefaultFont(int font) { // sets the default font for any window + UIWindow::m_WindowFont = font; + }; + +public: + UIWindow(); + virtual ~UIWindow(); + + void Destroy(); // must call if you created window. will be called in destructor + + void Create(int x, int y, int w, int h, int flags=0); + int GetFlags() const { return m_Flags; }; + +// used to mark key pressed to id numbers + void AddAcceleratorKey(int key, int id); + void ResetAcceleratorKey(); + +// gadget management + void AddGadget(UIGadget *gadget); // adds a gadget to the gadget list + void RemoveGadget(UIGadget *gadget); // removes a gadget from the list. + UIGadget *GetGadgetFromID(int id); // returns the gadget pointer given an id if in window + void SetFocusOnGadget(UIGadget *gadget, bool key=false); + + UIGadget* GetFocus() { return m_GadgetCur; }; + +// input/output management + virtual int Process(); // handles gadget selection/focus and calls gadgets. + void Render(); // renders one window frame. + void Open(); // adds window to ui list. + void Close(); // removes window from ui list. + +// settings + void SetFont(int handle); // sets window's font. + void SetBackItem(UIItem *item) { // sets the background drawing item. + if (m_BackItem) + delete m_BackItem; + m_BackItem = NULL; + if (item) + m_BackItem = item->CopyUIItem(); + }; // + void SetBackColor(ddgr_color col) { // sets the background color. this is ALWAYS first, then the backitem + m_BackColor = col; + }; + + void HoldHotkeys() { m_HoldHotkeys = true; }; // no hotkeys will be processed until ResumeHotkeys is called. + void ResumeHotkeys() { m_HoldHotkeys = false; }; + +// class id + virtual tUIClass Class() const { // Overide this function to name the class + return uiWindow; + }; + +// OBSOLETE + void LockKeyFocusOnGadget(UIGadget *gadget); // forces all keyinput onto one gadget. (be wary) +}; + + +// Special windows +// titled window (functions as a standard dialog box.), without modality though. + +class UITitledWindow: public UIWindow +{ + UITextItem m_Title; // title of window + int m_CaptionColor; // caption's color. + int m_BorderThickness; // border thickness. + +public: + UITitledWindow(); + virtual ~UITitledWindow(); + + void Create(UITextItem &title, int x, int y, int w, int h); + +// class id + virtual tUIClass Class() const { // Overide this function to name the class + return uiTitledWindow; + }; + +// settings + void SetText(UITextItem &text) { // sets the text for a window title. + m_Title = text; + }; + void SetCaptionColor(ddgr_color col) { // sets the color of the caption region + m_CaptionColor = col; + }; + void SetBorderThickness(int thickness) { // sets the pixel thickness of the border + m_BorderThickness = thickness; + }; + + int GetBorderThickness() const { + return m_BorderThickness; + }; // returns border thickness. + +// inheritable actions +protected: + virtual void OnDraw(); // overridable draws the window background before gadgets +}; + + +// UIConsole +// should display a simple console window where anyone can dump text into + +class UIConsole: public UIWindow +{ + UIConsoleGadget m_Console; + +protected: + virtual void OnDraw(); + +public: + UIConsole(); + virtual ~UIConsole(); + +// font = console font, NOT GADGET FONT. This font is used to proportion window + void Create(int x, int y, int font, int cols, int rows); + void Destroy(); + +// functions + void puts(const char *str); + void puts(ddgr_color col, const char *str); +}; + + +// other inline functions +inline bool UIGadget::HasFocus() const +{ + return ((m_Wnd->GetFocus()==this) ? true : false); +}; + + + +#include "uisys.h" +#include "uidraw.h" + +#endif diff --git a/lib/uidraw.h b/lib/uidraw.h new file mode 100644 index 000000000..e9cb5e2ea --- /dev/null +++ b/lib/uidraw.h @@ -0,0 +1,42 @@ + + +#ifndef UIDRAW_H +#define UIDRAW_H + +// Graphics Primatives. +void ui_StartDraw(int left, int top, int right, int bottom ); +void ui_EndDraw(); + +// sets text clipping within the startdraw region +void ui_SetTextClip(int left, int top, int right, int bottom); +void ui_ResetTextClip(); + +// draw primatives +void ui_DrawSetAlpha(ubyte alpha); +void ui_DrawLine(ddgr_color color, int x1, int y1, int x2, int y2); +void ui_DrawBox(ddgr_color, int l, int t, int r, int b); +void ui_DrawLTBox(ddgr_color lt_col, ddgr_color rb_col, int l, int t, int r, int b); +void ui_DrawRect(ddgr_color, int l, int t, int r, int b); + +// draws all textures derived from ui_bm_handle. +void ui_DrawBitmap(UIBitmapItem *bi, int x, int y, ubyte alpha); +int ui_GetBitmapWidth(UIBitmapItem *bi); +int ui_GetBitmapHeight(UIBitmapItem *bi); + +// sets font for text drawing. +void ui_DrawSetFont(int handle); +int ui_DrawGetFont(); +int ui_GetFontHeight(); +void ui_SetCharAlpha(ubyte alpha); +int ui_GetTextWidth(const char *text); +int ui_GetTextHeight(const char *text); + +#define UI_TEXTTYPE_SATURATE 1 +#define UI_TEXTTYPE_SHADOW 2 +void ui_DrawSetTextType(int type); + +// draws a line of text. +void ui_DrawString(ddgr_color col, int x, int y, const char *str); +void ui_DrawCenteredString(ddgr_color col, int xoff, int y, const char *str); + +#endif \ No newline at end of file diff --git a/lib/uires.h b/lib/uires.h new file mode 100644 index 000000000..733625f04 --- /dev/null +++ b/lib/uires.h @@ -0,0 +1,347 @@ +/* + * $Logfile: /DescentIII/main/lib/uires.h $ + * $Revision: 19 $ + * $Date: 10/21/99 1:53p $ + * $Author: Kevin $ + * + * Resources + * + * $Log: /DescentIII/main/lib/uires.h $ + * + * 19 10/21/99 1:53p Kevin + * Mac Merge + * + * 18 7/06/99 5:52p Kevin + * PXO & multiplayer fixes for the patch + * + * 17 5/20/99 10:50p Samir + * changed way memory was allocated for strings in text items, empty + * strings all point to same empty buffer for efficiency. + * + * 16 5/10/99 10:53p Ardussi + * changes to compile on Mac + * + * 15 9/30/98 4:32p Samir + * added snazzy text. + * + * 14 9/23/98 11:46a Samir + * added static text saturation. + * + * 13 8/20/98 6:45p Samir + * use const char for UITextItem create. + * + * 12 5/25/98 8:18p Samir + * added function to get text pointer to text item. + * + * 11 5/01/98 6:24p Samir + * added draw class field to draw for each resource. + * + * 10 3/13/98 11:51a Samir + * default text color is black. + * + * 9 3/10/98 7:26p Samir + * Fixed UIBitmapItem::get_chunked_bitmap. + * + * 8 3/05/98 6:40p Samir + * Virtualized a lot of functions. + * + * 7 2/13/98 6:34p Samir + * Added UIStatic class. + * + * 6 2/10/98 4:55p Samir + * Added radio buttons. + * + * 5 1/30/98 7:04p Samir + * Moved from ui.h and uilib.h. + * + * 4 1/18/98 4:22p Samir + * Implemented new UIItem system. + * + * 3 1/02/98 12:50p Samir + * Added font caps to UITextItem. + * + * 2 12/30/97 5:13p Samir + * INitial revision + * + * $NoKeywords: $ + */ + + +#ifndef UIRES_H +#define UIRES_H + +#include "grdefs.h" +#include "bitmap.h" + + +typedef enum tUIResClass { + uiItem, + uiTextItem, + uiBitmapItem +} +tUIResClass; + +typedef enum tUIDrawClass { + uiDrawNormal, + uiDrawAlphaSaturate, + uiDrawFaded +} +tUIDrawClass; + + +// UIItem +// the root class for all resource items + +class UIItem +{ +public: + UIItem() {}; + virtual ~UIItem() {}; + +// if returns false, then it didn't draw. + virtual bool draw(int x, int y, tUIDrawClass draw_class=uiDrawNormal) { return false; }; + virtual bool draw(int x, int y, int w, int h) { return false; }; + virtual int width() { return 0; }; + virtual int height() { return 0; }; + virtual tUIResClass class_type() { + return uiItem; + }; + virtual void set_alpha(ubyte alpha) {}; + virtual ubyte get_alpha() const { return 0; }; + virtual void set_color(ddgr_color col) {}; + virtual ddgr_color get_color() const { return GR_BLACK; }; + virtual UIItem *CopyUIItem() { return NULL; }; +}; + + +// UITextItem +// used by user interface system, contains information about how to render +// text. allows for alpha, color and different fonts. + +class UITextItem: public UIItem +{ + friend void SetUITextItemText(UITextItem *uit,char *newtext,unsigned int color); + + ubyte m_Alpha; // alpha value of text. + ddgr_color m_Color; // color of text. + int m_Font; + + static int m_DefaultFont; + static int m_Sat; + +protected: + char *m_Text; + +public: + static void SetDefaultFont(int font) { + UITextItem::m_DefaultFont = font; + } + static void SetSaturationFactor(int sat) { + UITextItem::m_Sat = sat; + } + static char dummy_str[4]; + +public: + UITextItem() { m_Text = NULL; m_Alpha = 255; m_Color = GR_WHITE; m_Font = m_DefaultFont; }; + UITextItem(const char *text, ddgr_color color=GR_WHITE, ubyte alpha=255); + UITextItem(int font, const char *text, ddgr_color color=GR_WHITE, ubyte alpha=255); + virtual ~UITextItem(); + +// if returns false, then it didn't draw. + virtual bool draw(int x, int y, tUIDrawClass draw_class=uiDrawNormal); + virtual bool draw(int x, int y, int w, int h) { return draw(x,y); }; + virtual int width(); + virtual int height(); + virtual tUIResClass class_type() const { + return uiTextItem; + }; + virtual UIItem *CopyUIItem(); + +// set visual characteristics of text item + virtual void set_alpha(ubyte alpha) { + m_Alpha = alpha; + }; + virtual void set_color(ddgr_color col) { + m_Color = col; + }; + + void set_font(int font) { + m_Font = font; + }; + +// get visual characteristics of text item + virtual ubyte get_alpha() const { + return m_Alpha; + }; + virtual ddgr_color get_color() const { + return m_Color; + }; + int get_font() const { + return m_Font; + }; + +// operators + operator const char*() const { // access m_Text + return m_Text; + }; + + const char *GetBuffer() const { + return m_Text; + }; + + const UITextItem& operator =(const UITextItem& item); +}; + + +#define UISNAZZYTEXTF_BLINKING 0x1 +#define UISNAZZYTEXTF_RESERVED 0xffff0000 + +class UISnazzyTextItem: public UITextItem +{ + unsigned m_flags; + + union { + int i; + float f; + } + m_internaldata; + + union { + int i; + float f; + } + m_data; + +public: + UISnazzyTextItem() {m_flags = m_data.i = m_internaldata.i = 0;}; + UISnazzyTextItem(unsigned flags, const char *text, ddgr_color color=GR_WHITE, ubyte alpha=255); + UISnazzyTextItem(unsigned flags, int font, const char *text, ddgr_color color=GR_WHITE, ubyte alpha=255); + + void set_data(int data) { m_data.i = data; }; + void set_data(float data) { m_data.f = data; }; + + void set_flags(unsigned flags); + + virtual bool draw(int x, int y, tUIDrawClass draw_class=uiDrawNormal); + virtual UIItem *CopyUIItem(); + + const UISnazzyTextItem& operator =(const UISnazzyTextItem& item); +}; + + +// UIBitmapItem +// used by user interface system, contains information about how to render +// text. allows for alpha, color and different fonts. + +class UIBitmapItem: public UIItem +{ + bool m_IsValid; + bool m_IsChunked; // is this a chunked bitmap? + + union + { + chunked_bitmap *chunk; // a chunked bitmap. + int handle; // a simple bitmap + } + m_Bitmap; // a bitmap. + + ubyte m_Alpha; // alpha value of text. + +public: + UIBitmapItem() { m_IsValid = false; m_Alpha = 255; }; + UIBitmapItem(chunked_bitmap *chunk, ubyte alpha=255) { + m_IsValid = true; + m_Bitmap.chunk = chunk; m_Alpha = alpha; m_IsChunked = true; + }; + UIBitmapItem(int bm_handle, ubyte alpha=255) { + m_IsValid = true; m_Alpha = alpha; m_IsChunked = false; m_Bitmap.handle = bm_handle; + }; + virtual ~UIBitmapItem() {}; + +// if returns false, then it didn't draw. + virtual bool draw(int x, int y, tUIDrawClass draw_class=uiDrawNormal); + virtual bool draw(int x, int y, int w, int h) { return draw(x,y); }; + virtual int width(); + virtual int height(); + virtual tUIResClass class_type() const { + return uiBitmapItem; + }; + virtual UIItem *CopyUIItem(); + +// flag checking + bool is_chunked() const { return m_IsChunked; }; + bool is_valid() const { return m_IsValid; }; + +// set visual characteristics of bitmap + void set_chunked_bitmap(chunked_bitmap *chunk) { + m_IsValid = true; + m_IsChunked = true; + m_Bitmap.chunk = chunk; + }; + void set_bitmap(int bm_handle) { + m_IsValid = true; + m_IsChunked = false; + m_Bitmap.handle = bm_handle; + }; + virtual void set_alpha(ubyte alpha) { // sets the alpha + m_Alpha = alpha; + }; + +// get visual characteristics + chunked_bitmap *get_chunked_bitmap() const { + return (chunked_bitmap *)m_Bitmap.chunk; + }; + int get_bitmap() const { + return m_Bitmap.handle; + }; + virtual ubyte get_alpha() const { // gets alpha. + return m_Alpha; + }; + + //const UIBitmapItem& UIBitmapItem::operator =(const UIBitmapItem& item); // JCA made Mac compatible + const UIBitmapItem& operator =(const UIBitmapItem& item); +}; + + +// UIPrimativeItem +// used to render simple 2d backgrounds. + +class UIPrimativeItem: public UIItem +{ + ddgr_color color; + ubyte alpha; + +public: + UIPrimativeItem(ddgr_color col, ubyte alph=255) {color =col; alpha = alph; }; + + virtual UIItem *CopyUIItem(); + + virtual void set_color(ddgr_color col) { + color = col; + }; + virtual ddgr_color get_color() const { + return color; + }; + virtual void set_alpha(ubyte alph) { + alpha = alph; + }; + virtual ubyte get_alpha() const { + return alpha; + }; + + //const UIPrimativeItem& UIPrimativeItem::operator =(const UIPrimativeItem& item) // JCA made Mac compatible + const UIPrimativeItem& operator =(const UIPrimativeItem& item) + { + color = item.color; + alpha = item.alpha; + return *this; + }; + +// if returns false, then it didn't draw. + virtual bool draw(int x, int y, tUIDrawClass draw_class=uiDrawNormal) { return false; }; + virtual bool draw(int x, int y, int w, int h); +}; + + +#endif + diff --git a/lib/uisys.h b/lib/uisys.h new file mode 100644 index 000000000..6b9f3d340 --- /dev/null +++ b/lib/uisys.h @@ -0,0 +1,165 @@ + + +#ifndef UISYS_H +#define UISYS_H + + +///////////////////////////////////////////////////////////////////////////// +// DEFINITIONS + +#define UIMSEBTN_PRESSED 1 +#define UIMSEBTN_RELEASED 2 +#define UIMSEBTN_CLICKED 3 + +#define UIKEY_PRESSED 1 +#define UIKEY_RELEASED 2 +#define UIKEY_CLICKED 3 + +// variables (OnMouseDown, etc) +#define UILMSEBTN 1 +#define UIRMSEBTN 2 + +// mouse click properties +#ifdef MACINTOSH +#define UI_DBLCLICK_DELAY 1.20f +#else +#define UI_DBLCLICK_DELAY 0.60f +#endif +#define UI_DBLCLICK_MSEDELTA 4 + + +typedef struct tUIInitInfo +{ + int window_font; // default font for windows + int w, h; // width and height of screen. +} +tUIInitInfo; + +typedef struct tUIInput +{ + int mx, my, last_mx, last_my; + int b1_status, b1_last_status; + int b1_count; + int key, last_key; + int key_status, last_key_status; + float cur_time; + + bool key_first_press; // if the key was really pressed or just held down. + bool printscreen; +} +tUIInput; + +// user interface frame time +extern float UIFrameTime; + +// user input structure +extern tUIInput UI_input; +extern int UI_screen_width, UI_screen_height; + +class oeApplication; + + +////////////////////////////////////////////////////////////////////////////// +// MACROS + +#define ISKEYPRESSED(_k) ((UI_input.key == (_k) && (UI_input.key_status == UIKEY_PRESSED || UI_input.key_status == UIKEY_CLICKED)) ? true : false) +#define ISKEYRELEASED(_k) ((UI_input.key == (_k) && UI_input.key_status == UIKEY_RELEASED) ? true : false) + + +// inlined supporter functions +inline bool PT_IN_GADGET(UIWindow *wnd, UIGadget *gadget, int x, int y) +{ + int gl, gt, gr, gb; + gl = wnd->X() + gadget->X(); + gt = wnd->Y() + gadget->Y(); + gr = wnd->X() + gadget->X() + gadget->W(); + gb = wnd->Y() + gadget->Y() + gadget->H(); + + if (x >= gl && x < gr) + if (y >= gt && y < gb) + return true; + + return false; +} + +inline bool PT_IN_RECT(int x, int y, int l, int t, int w, int h) +{ + if (x >= l && x < (l+w) && y >= t && y < (t+h)) + return true; + else + return false; +} + +inline int SCREEN_TO_WND_X(UIWindow *wnd, int x) +{ + return (x - wnd->X()); +} + +inline int SCREEN_TO_WND_Y(UIWindow *wnd, int y) +{ + return (y - wnd->Y()); +} + +inline int SCREEN_TO_GAD_X(UIGadget *gad, int x) +{ + return (SCREEN_TO_WND_X(gad->GetWindow(), x) - gad->X()); +} + +inline int SCREEN_TO_GAD_Y(UIGadget *gad, int y) +{ + return (SCREEN_TO_WND_Y(gad->GetWindow(), y) - gad->Y()); +} + +#define LOCK_FOCUS(_gadget) (_gadget)->LockFocus(); + +#define UNLOCK_FOCUS(_gadget) (_gadget)->UnlockFocus(); + +inline float UI_TIME() +{ + return UI_input.cur_time; +} + + +////////////////////////////////////////////////////////////////////////////// +// FUNCTIONS + +// call this function to initialize the UI system. pass a viewport where all UI will occur +void ui_Init(oeApplication *app, tUIInitInfo *init_info); + +// closes UI system. do this when setting a new surface. +void ui_Close(); + +// set user interface screen resolution +void ui_SetScreenMode(int w, int h); + +// adds a window to the ui list. it is topmost, and has focus. +void ui_AddWindow(UIWindow *wnd); + +// removes a window from ui list.if topmost, the next topmost window has focus. +void ui_RemoveAllWindows(); + +// removes a window from ui list.if topmost, the next topmost window has focus. +void ui_RemoveWindow(UIWindow *wnd); + +// does a ui frame given a list of windows. returns result list. +int ui_DoFrame(bool input=true); + +// does a ui frame and gets mouse and key information. +int ui_DoFrame(tUIInput *input, bool doinput=true); + +// ability to load/use mouse cursors +void ui_UseCursor(char *fname); + +// hide and show cursor. effects are cumulative (returns whether the cursor was already shown or hidden.) +bool ui_ShowCursor(); // return value false if cursor is currently visible. +bool ui_HideCursor(); // return value true if cursor was already hidden +bool ui_IsCursorVisible(); // is the cursor visible? + +// frees ui input cache +void ui_Flush(); + +// does screen shot +void ui_DoScreenshot(); + + +#endif \ No newline at end of file diff --git a/lib/unzip.h b/lib/unzip.h new file mode 100644 index 000000000..21a1436ac --- /dev/null +++ b/lib/unzip.h @@ -0,0 +1,134 @@ +/* +* $Logfile: /DescentIII/Main/Lib/unzip.h $ +* $Revision: 3 $ +* $Date: 8/15/99 8:07p $ +* $Author: Jeff $ +* +* Unzip class +* +* $Log: /DescentIII/Main/Lib/unzip.h $ + * + * 3 8/15/99 8:07p Jeff + * handle disk write errors when extracting + * + * 2 8/13/99 8:01p Jeff + * initial creation of zip class +* +* $NoKeywords: $ +*/ + + + +#ifndef __UNZIP_H +#define __UNZIP_H + +#include +#include "pstypes.h" + +typedef struct +{ + uint cent_file_header_sig; + ubyte version_made_by; + ubyte host_os; + ubyte version_needed_to_extract; + ubyte os_needed_to_extract; + ushort general_purpose_bit_flag; + ushort compression_method; + ushort last_mod_file_time; + ushort last_mod_file_date; + uint crc32; + uint compressed_size; + uint uncompressed_size; + ushort filename_length; + ushort extra_field_length; + ushort file_comment_length; + ushort disk_number_start; + ushort internal_file_attrib; + uint external_file_attrib; + uint offset_lcl_hdr_frm_frst_disk; + char* name; +}zipentry; + +class ZIP +{ +public: + //constructor/destructor + ZIP(); + ~ZIP(); + +public: + //opens a zip file for reading + //returns true on success + bool OpenZip(const char *path); + + //closes an open zip file + void CloseZip(void); + + // Reads the current zip entry from the zip file (and moves + // to the next entry). Returns NULL if there are no more entries + zipentry* ReadNextZipEntry(void); + + // Resets a ZIP file to the first entry + void Rewind(void); + + // Reads a file from the given zip entry into a buffer in memory + // -1 : no ZIP file open + // -2 : corrupt file + // -3 : Version too new + // -4 : OS not supported + // -5 : No Disk Spanning + // -6 : Error inflating + // -7 : Compression Type Not Supported + int ReadFile(zipentry *ent,char *data); + + // Extracts a file from the given zip entry into another file + // -1 : no ZIP file open + // -2 : corrupt file + // -3 : Version too new + // -4 : OS not supported + // -5 : No Disk Spanning + // -6 : Error inflating + // -7 : Compression Type Not Supported + // -8 : Unable to open output + // -9 : Error writing to file + int ExtractFile(zipentry *ent,const char *filename); + +private: + bool FindECDSignature(char *buffer,int buflen,int *offset); + int ReadECD(void); + int ReadZipData(zipentry* ent,char* data); + int ReadZipDataToFile(zipentry* ent,FILE *file); + int SeekToCompressedData(zipentry* ent); + int InflateFile(FILE* in_file,unsigned in_size,ubyte* out_data,unsigned out_size); + int InflateFileToFile(FILE* in_file,unsigned in_size,FILE *file,unsigned out_size); +private: + bool m_open; + char* m_zip; // zip name + FILE* m_fp; // zip handler + long m_length; // length of zip file + + char* m_ecd; // end_of_cent_dir data + unsigned m_ecd_length;// end_of_cent_dir length + + char* m_cd; // cent_dir data + + unsigned m_cd_pos; // position in cent_dir + + zipentry m_ent; // buffer for readzip + + // end_of_cent_dir + uint m_end_of_cent_dir_sig; + ushort m_number_of_this_disk; + ushort m_number_of_disk_start_cent_dir; + ushort m_total_entries_cent_dir_this_disk; + ushort m_total_entries_cent_dir; + uint m_size_of_cent_dir; + uint m_offset_to_start_of_cent_dir; + ushort m_zipfile_comment_length; + char* m_zipfile_comment; // pointer in ecd +}; + +// Compare two filename without using directory +int CompareZipFileName(const char* zipfile, const char* file); + +#endif diff --git a/lib/vecmat.h b/lib/vecmat.h new file mode 100644 index 000000000..d56d8c4a5 --- /dev/null +++ b/lib/vecmat.h @@ -0,0 +1,320 @@ +/* + * $Logfile: /DescentIII/Main/lib/vecmat.h $ + * $Revision: 21 $ + * $Date: 1/21/99 11:16p $ + * $Author: Jeff $ + * + * Vector/Matrix routines + * + * $Log: /DescentIII/Main/lib/vecmat.h $ + * + * 21 1/21/99 11:16p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 20 1/11/99 4:45p Jason + * added first pass at katmai support + * + * 19 1/01/99 4:10p Chris + * Added some const parameters, improved ray cast object collide/rejection + * code + * + * 18 5/25/98 3:45p Jason + * added vm_GetCentroidFast + * + * 17 3/12/98 7:30p Chris + * Added ObjSetOrient + * + * 16 2/08/98 6:01p Matt + * Added functions to multiply by a transposed matrix, and simplified some + * other code a bit. + * + * 15 2/06/98 11:02a Matt + * Added missing include + * + * 14 2/06/98 10:43a Matt + * Made vm_VectorToMatrix() take any one or two vectors, & not require + * forward vec. + * Also, made the uvec and rvec parameters default to NULL if not + * specified. + * + * 13 2/02/98 8:17p Chris + * Added a != operator and a Zero_vector constant + * + * 12 1/20/98 4:04p Matt + * Made vm_GetNormalizedDir() and vm_GetNormalizeDirFast() return the + * distance between the two input points. + * + * 11 1/13/98 1:30p Jason + * changed vm_GetCentroid to also return the size of the area + * + * 10 11/04/97 6:21p Chris + * Allowed other files to use the vm_DeltaAngVecNorm function + * + * 9 10/25/97 7:15p Jason + * implemented vm_ComputeBoundingSphere + * + * 8 10/14/97 4:35p Samir + * Added vm_MakeRandomVector. + * + * 7 9/23/97 2:26p Matt + * Made vm_GetNormal() return the magnitude of the normal (before it was + * normalized) + * + * 6 8/28/97 4:56p Jason + * implemented vm_GetCentroid + * + * 5 8/18/97 6:39p Matt + * Added vm_VectorAngleToMatrix() + * + * 4 8/04/97 12:36p Chris + * Added an == operator for vectors + * + * 3 7/17/97 3:56p Matt + * Added vm_Orthogonalize() + * + * 2 7/16/97 5:15p Chris + * Moved the XYZ() macro to vecmat.h + * + * 1 6/23/97 9:25p Samir + * added because source safe sucks + * + * 31 4/18/97 2:14p Samir + * Added vm_DeltaAngVec. + * + * 30 2/27/97 6:16p Chris + * Added the vector_array type + * + * 29 2/27/97 6:08 PM Jeremy + * added prototypes for vm_MakeInverseMatrix and vm_SinCosToMatrix + * + * 28 2/27/97 5:23p Chris + * Removed the remainding extern inline + * + * 27 2/27/97 4:56p Samir + * took out ifndef MAC stuff + * + * 26 2/27/97 1:40p Chris + * Added a function to compute the determinate -- + * BTW on the last rev. I moved all inline functions + * to the header. (So they will be inlined) + * + * 25 2/26/97 7:33p Chris + * + * 24 2/26/97 6:17 PM Jeremy + * put #pragma warning inside #ifndef macintosh + * + * 23 2/20/97 11:41a Chris + * Added a negate unary operator for vectors + * + * 22 2/12/97 5:28p Jason + * implemented ExtractAnglesFromMatrix function + * + * 21 2/11/97 6:49p Matt + * Added vm_VectorToMatrix() + * Made vm_NormalizeVector() return the old vector mag + * Fixed bug in inline version of crossprod + * + * 20 2/11/97 11:54a Jason + * + * 19 2/10/97 3:36p Matt + * Fixed (added) IDENTITY_MATRIX define + * + * 18 2/07/97 5:38p Matt + * Moved fixed-point math funcs to fix.lib + * + * $NoKeywords: $ + */ + +#ifndef _VECMAT_H +#define _VECMAT_H + +#include "pstypes.h" +#include "math.h" +#include "fix.h" + +//what does this do? Why didn't Jason put a comment here? +// Jason replies: This pragma disables the "possible loss of data" warning that +// is generated when converting doubles to floats +// A thousand pardons for the confusion + +#pragma warning (disable:4244) + +// All structs, defines and inline functions are located in vecmat_external.h +// vecmat_external.h is where anything that can be used by DLLs should be. +#include "vecmat_external.h" + + +extern const vector Zero_vector; +extern const matrix Identity_matrix; + +// Used for debugging. It is used in printf's so we do not have to write out the structure 3 times +// to print all the coordinates. +#define XYZ(v) (v)->x,(v)->y,(v)->z + +// Given a matrix, makes it an identity matrix +extern void vm_MakeIdentity (matrix *); + +// Set a vector to {0,0,0} +extern void vm_MakeZero(vector *v); + +// Set an angvec to {0,0,0} +extern void vm_MakeZero(angvec *a); + +// Rotates a vector thru a matrix +extern void vm_MatrixMulVector (vector *,vector *,matrix *); + +//Multiply a vector times the transpose of a matrix +void vm_VectorMulTMatrix(vector *result,vector *v,matrix *m); + +// Multiplies 2 3x3 matrixes, returning the result in first argument +extern void vm_MatrixMul (matrix *,matrix *,matrix *); + +//Multiply a matrix times the transpose of a matrix +void vm_MatrixMulTMatrix(matrix *dest,matrix *src0,matrix *src1); + +// Computes all math look up tables, must be called before any vector stuff is used +extern void vm_InitMathTables(); + +// Given a vector, returns the magnitude. Uses sqrt so it's slow +extern float vm_GetMagnitude (vector *); + +// Given a vector, returns an approximation of the magnitude +extern float vm_GetMagnitudeFast (vector *); + +// Returns the dot product of the two given vectors +extern float vm_DotProduct(const vector *,const vector *); + +// Returns a perpendicular vector to the two given vectors +extern void vm_CrossProduct (vector *,vector *,vector *); + +// Returns the difference between two vectors +extern void vm_SubVectors (vector *,const vector *,const vector *); + +// Returns adds two vectors, returns result in first arg +extern void vm_AddVectors (vector *,vector *,vector *); + +// Inits vector to 0,0,0 +extern void vm_CenterVector (vector *); + +// Given a vector, divides second arg by vector components +extern void vm_AverageVector (vector *,int); + +// Normalizes a vector +// Returns the magnitude before normalization +extern float vm_NormalizeVector (vector *); + +// Scales second arg vector by 3rd arg, placing result in first arg +extern void vm_ScaleVector (vector *,vector *,float); + +// Scales all components of vector v by value s adds the result to p and stores result in vector d +extern void vm_ScaleAddVector (vector *d,vector *p,vector *v,float s); + +// Divides second vector components by 3rd arg, placing result in first arg. Useful for parametric lines +extern void vm_DivVector (vector *,vector *,float); + +// Same as NormalizeVector, but uses approximation +extern float vm_NormalizeVectorFast (vector *); + +// Clears a matrix to zero +extern void vm_ClearMatrix (matrix *); + +// Transposes a matrix in place +extern void vm_TransposeMatrix (matrix *); + +// Given 3 angles (p,h,b), makes a rotation matrix out of them +extern void vm_AnglesToMatrix (matrix *,angle p,angle h,angle b); + +//Ensure that a matrix is orthogonal +void vm_Orthogonalize(matrix *m); + +//Compute a matrix from one or two vectors. At least one and at most two vectors must/can be specified. +//Parameters: m - filled in with the orienation matrix +// fvec,uvec,rvec - pointers to vectors that determine the matrix. +// One or two of these must be specified, with the other(s) set to NULL. +void vm_VectorToMatrix(matrix *m,vector *fvec,vector *uvec=NULL,vector *rvec=NULL); + +//Computes a matrix from a vector and and angle of rotation around that vector +//Parameters: m - filled in with the computed matrix +// v - the forward vector of the new matrix +// a - the angle of rotation around the forward vector +void vm_VectorAngleToMatrix(matrix *m,vector *v,angle a); + +// Given an angle, places sin in 2nd arg, cos in 3rd. Either can be null +extern void vm_SinCos (angle,float *,float *); + +// Given x1,y1,x2,y2, returns the slope +extern float vm_GetSlope (float,float,float,float); + +//Calculates the perpendicular vector given three points +//Parms: n - the computed perp vector (filled in) +// v0,v1,v2 - three clockwise vertices +void vm_GetPerp(vector *n,vector *a,vector *b,vector *c); + +//Calculates the (normalized) surface normal give three points +//Parms: n - the computed surface normal (filled in) +// v0,v1,v2 - three clockwise vertices +//Returns the magnitude of the normal before it was normalized. +//The bigger this value, the better the normal. +float vm_GetNormal(vector *n,vector *v0,vector *v1,vector *v2); + +// Gets the distances (magnitude) between two vectors. Slow. +extern float vm_VectorDistance (const vector *a, const vector *b); + +// Gets the approx distances (magnitude) between two vectors. Faster. +extern float vm_VectorDistanceQuick (vector *a,vector *b); + +//Computes a normalized direction vector between two points +//Parameters: dest - filled in with the normalized direction vector +// start,end - the start and end points used to calculate the vector +//Returns: the distance between the two input points +float vm_GetNormalizedDir (vector *dest,vector *end,vector *start); + +// Returns a normalized direction vector between two points +// Uses sloppier magnitude, less precise +float vm_GetNormalizedDirFast (vector *dest,vector *end,vector *start); + +//extract angles from a matrix +angvec *vm_ExtractAnglesFromMatrix(angvec *a,matrix *m); + +// returns the angle between two vectors and a forward vector +angle vm_DeltaAngVec(vector *v0,vector *v1,vector *fvec); + +// returns the angle between two normalized vectors and a forward vector +angle vm_DeltaAngVecNorm(vector *v0,vector *v1,vector *fvec); + +// Computes the distance from a point to a plane. +// Parms: checkp - the point to check +// Parms: norm - the (normalized) surface normal of the plane +// planep - a point on the plane +// Returns: The signed distance from the plane; negative dist is on the back of the plane +float vm_DistToPlane(vector *checkp,vector *norm,vector *planep); + +//returns the value of a determinant +float calc_det_value(matrix *det); + +void vm_MakeInverseMatrix (matrix *dest); +void vm_SinCosToMatrix(matrix *m,float sinp,float cosp,float sinb,float cosb,float sinh,float cosh); + +// Gets the real center of a polygon +float vm_GetCentroid (vector *centroid,vector *src,int nv); + +// retrieves a random vector in values -RAND_MAX/2 to RAND_MAX/2 +void vm_MakeRandomVector(vector *vec); + +// Given a set of points, computes the minimum bounding sphere of those points +float vm_ComputeBoundingSphere(vector *center,vector *vecs,int num_verts); + +// Gets the real center of a polygon, but uses fast magnitude calculation +// Returns the size of the passed in stuff +float vm_GetCentroidFast (vector *centroid,vector *src,int nv); + +// Here are the C++ operator overloads -- they do as expected +extern matrix operator *(matrix src0, matrix src1); +extern matrix operator *=(matrix &src0, matrix src1); + +#endif diff --git a/lib/vecmat_external.h b/lib/vecmat_external.h new file mode 100644 index 000000000..18584172d --- /dev/null +++ b/lib/vecmat_external.h @@ -0,0 +1,307 @@ +/* +* $Logfile: /DescentIII/Main/lib/vecmat_external.h $ +* $Revision: 3 $ +* $Date: 2/19/99 4:26p $ +* $Author: Jason $ +* +* Contains any header information that can/should be exported to DLLs +* +* $Log: /DescentIII/Main/lib/vecmat_external.h $ + * + * 3 2/19/99 4:26p Jason + * more work on Katmai support + * + * 2 1/21/99 11:15p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h +* +* $NoKeywords: $ +*/ + +#ifndef VECMAT_EXTERNAL_H +#define VECMAT_EXTERNAL_H + +//Angles are unsigned shorts +typedef unsigned short angle; //make sure this matches up with fix.h + + +typedef struct { + angle p,h,b; +} angvec; + +#define IDENTITY_MATRIX {{1.0,0,0},{0,1.0,0},{0,0,1.0}} + +typedef struct +{ + float x, y, z; +} vector; + +typedef struct vector4 +{ + float x,y,z,kat_pad; +} vector4; + + +typedef struct +{ + float xyz[3]; +} vector_array; + +typedef struct +{ + vector rvec,uvec,fvec; +} matrix; + +typedef struct +{ + vector4 rvec,uvec,fvec; +} matrix4; + + + +// Zero's out a vector +inline void vm_MakeZero(vector *v) +{ + v->x = v->y = v->z=0; +} + +// Set an angvec to {0,0,0} +inline void vm_MakeZero(angvec *a) +{ + a->p = a->h = a->b=0; +} + +// Checks for equality +inline bool operator ==(vector a, vector b) +{ + bool equality = false; + // Adds two vectors. + + if(a.x == b.x && a.y == b.y && a.z == b.z) equality = true; + + return equality; +} + +// Checks for inequality +inline bool operator !=(vector a, vector b) +{ + bool equality = true; + // Adds two vectors. + + if(a.x == b.x && a.y == b.y && a.z == b.z) equality = false; + + return equality; +} + + +// Adds 2 vectors +inline vector operator +(vector a, vector b) +{ + // Adds two vectors. + + a.x += b.x; + a.y += b.y; + a.z += b.z; + + return a; +} + +// Adds 2 vectors +inline vector operator +=(vector &a, vector b) +{ + return(a = a + b); +} + +// Adds 2 matrices +inline matrix operator +(matrix a, matrix b) +{ + // Adds two 3x3 matrixs. + + a.rvec += b.rvec; + a.uvec += b.uvec; + a.fvec += b.fvec; + + return a; +} + +// Adds 2 matrices +inline matrix operator +=(matrix &a, matrix b) +{ + return (a = a + b); +} + +// Subtracts 2 vectors +inline vector operator -(vector a, vector b) +{ + // subtracts two vectors + + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + + return a; +} + +// Subtracts 2 vectors +inline vector operator -=(vector &a, vector b) +{ + return (a = a - b); +} + +// Subtracts 2 matrices +inline matrix operator -(matrix a, matrix b) +{ + // subtracts two 3x3 matrices + + a.rvec = a.rvec - b.rvec; + a.uvec = a.uvec - b.uvec; + a.fvec = a.fvec - b.fvec; + + return a; +} + +// Subtracts 2 matrices +inline matrix operator -=(matrix &a, matrix b) +{ + return (a = a - b); +} + +// Does a simple dot product calculation +inline float operator *(vector u, vector v) +{ + return (u.x * v.x) + (u.y * v.y) + (u.z * v.z); +} + +// Scalar multiplication +inline vector operator *(vector v, float s) +{ + v.x *= s; + v.y *= s; + v.z *= s; + + return v; +} + +// Scalar multiplication +inline vector operator *=(vector &v, float s) +{ + return (v = v * s); +} + +// Scalar multiplication +inline vector operator *(float s, vector v) +{ + return v*s; +} + +// Scalar multiplication +inline matrix operator *(float s, matrix m) +{ + m.fvec = m.fvec * s; + m.uvec = m.uvec * s; + m.rvec = m.rvec * s; + + return m; +} + +// Scalar multiplication +inline matrix operator *(matrix m, float s) +{ + return s*m; +} + +// Scalar multiplication +inline matrix operator *=(matrix &m, float s) +{ + return (m = m * s); +} + +// Scalar division +inline vector operator /(vector src, float n) +{ + src.x /= n; + src.y /= n; + src.z /= n; + + return src; +} + +// Scalar division +inline vector operator /=(vector &src, float n) +{ + return (src = src / n); +} + +// Scalar division +inline matrix operator /(matrix src, float n) +{ + src.fvec = src.fvec / n; + src.rvec = src.rvec / n; + src.uvec = src.uvec / n; + + return src; +} + +// Scalar division +inline matrix operator /=(matrix &src, float n) +{ + return (src = src / n); +} + +// Computes a cross product between u and v, returns the result +// in Normal. +inline vector operator ^(vector u, vector v) +{ + vector dest; + + dest.x = (u.y * v.z ) - (u.z * v.y); + dest.y = (u.z * v.x ) - (u.x * v.z); + dest.z = (u.x * v.y ) - (u.y * v.x); + + return dest; +} + +// Matrix transpose +inline matrix operator ~(matrix m) { + float t; + + t = m.uvec.x; m.uvec.x = m.rvec.y; m.rvec.y = t; + t = m.fvec.x; m.fvec.x = m.rvec.z; m.rvec.z = t; + t = m.fvec.y; m.fvec.y = m.uvec.z; m.uvec.z = t; + + return m; +} + +// Negate vector +inline vector operator -(vector a) { + a.x *= -1; + a.y *= -1; + a.z *= -1; + + return a; +} + +// Apply a matrix to a vector +inline vector operator *(vector v, matrix m) +{ + vector result; + + result.x = v * m.rvec; + result.y = v * m.uvec; + result.z = v * m.fvec; + + return result; +} + +inline float vm_Dot3Vector(float x,float y,float z,vector *v) +{ + return (x*v->x) + (y*v->y) + (z*v->z); +} + +#define vm_GetSurfaceNormal vm_GetNormal + +#endif \ No newline at end of file diff --git a/lib/win/Adecode.lib b/lib/win/Adecode.lib new file mode 100644 index 000000000..6ac834483 Binary files /dev/null and b/lib/win/Adecode.lib differ diff --git a/lib/win/Aencode.lib b/lib/win/Aencode.lib new file mode 100644 index 000000000..1b936ab15 Binary files /dev/null and b/lib/win/Aencode.lib differ diff --git a/lib/win/DirectX/DSETUP.H b/lib/win/DirectX/DSETUP.H new file mode 100644 index 000000000..ea5936a5b --- /dev/null +++ b/lib/win/DirectX/DSETUP.H @@ -0,0 +1,267 @@ +/*========================================================================== + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dsetup.h + * Content: DirectXSetup, error codes and flags + ***************************************************************************/ + +#ifndef __DSETUP_H__ +#define __DSETUP_H__ + +#include // windows stuff + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#else +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +// DSETUP Error Codes, must remain compatible with previous setup. +#define DSETUPERR_SUCCESS_RESTART 1 +#define DSETUPERR_SUCCESS 0 +#define DSETUPERR_BADWINDOWSVERSION -1 +#define DSETUPERR_SOURCEFILENOTFOUND -2 +#define DSETUPERR_BADSOURCESIZE -3 +#define DSETUPERR_BADSOURCETIME -4 +#define DSETUPERR_NOCOPY -5 +#define DSETUPERR_OUTOFDISKSPACE -6 +#define DSETUPERR_CANTFINDINF -7 +#define DSETUPERR_CANTFINDDIR -8 +#define DSETUPERR_INTERNAL -9 +#define DSETUPERR_NTWITHNO3D -10 /* REM: obsolete, you'll never see this */ +#define DSETUPERR_UNKNOWNOS -11 +#define DSETUPERR_USERHITCANCEL -12 +#define DSETUPERR_NOTPREINSTALLEDONNT -13 + +// DSETUP flags. DirectX 5.0 apps should use these flags only. +#define DSETUP_DDRAWDRV 0x00000008 /* install DirectDraw Drivers */ +#define DSETUP_DSOUNDDRV 0x00000010 /* install DirectSound Drivers */ +#define DSETUP_DXCORE 0x00010000 /* install DirectX runtime */ +#define DSETUP_DIRECTX (DSETUP_DXCORE|DSETUP_DDRAWDRV|DSETUP_DSOUNDDRV) +#define DSETUP_TESTINSTALL 0x00020000 /* just test install, don't do anything */ + +// These OBSOLETE flags are here for compatibility with pre-DX5 apps only. +// They are present to allow DX3 apps to be recompiled with DX5 and still work. +// DO NOT USE THEM for DX5. They will go away in future DX releases. +#define DSETUP_DDRAW 0x00000001 /* OBSOLETE. install DirectDraw */ +#define DSETUP_DSOUND 0x00000002 /* OBSOLETE. install DirectSound */ +#define DSETUP_DPLAY 0x00000004 /* OBSOLETE. install DirectPlay */ +#define DSETUP_DPLAYSP 0x00000020 /* OBSOLETE. install DirectPlay Providers */ +#define DSETUP_DVIDEO 0x00000040 /* OBSOLETE. install DirectVideo */ +#define DSETUP_D3D 0x00000200 /* OBSOLETE. install Direct3D */ +#define DSETUP_DINPUT 0x00000800 /* OBSOLETE. install DirectInput */ +#define DSETUP_DIRECTXSETUP 0x00001000 /* OBSOLETE. install DirectXSetup DLL's */ +#define DSETUP_NOUI 0x00002000 /* OBSOLETE. install DirectX with NO UI */ +#define DSETUP_PROMPTFORDRIVERS 0x10000000 /* OBSOLETE. prompt when replacing display/audio drivers */ +#define DSETUP_RESTOREDRIVERS 0x20000000 /* OBSOLETE. restore display/audio drivers */ + + + +//****************************************************************** +// DirectX Setup Callback mechanism +//****************************************************************** + +// DSETUP Message Info Codes, passed to callback as Reason parameter. +#define DSETUP_CB_MSG_NOMESSAGE 0 +#define DSETUP_CB_MSG_CANTINSTALL_UNKNOWNOS 1 +#define DSETUP_CB_MSG_CANTINSTALL_NT 2 +#define DSETUP_CB_MSG_CANTINSTALL_BETA 3 +#define DSETUP_CB_MSG_CANTINSTALL_NOTWIN32 4 +#define DSETUP_CB_MSG_CANTINSTALL_WRONGLANGUAGE 5 +#define DSETUP_CB_MSG_CANTINSTALL_WRONGPLATFORM 6 +#define DSETUP_CB_MSG_PREINSTALL_NT 7 +#define DSETUP_CB_MSG_NOTPREINSTALLEDONNT 8 +#define DSETUP_CB_MSG_SETUP_INIT_FAILED 9 +#define DSETUP_CB_MSG_INTERNAL_ERROR 10 +#define DSETUP_CB_MSG_CHECK_DRIVER_UPGRADE 11 +#define DSETUP_CB_MSG_OUTOFDISKSPACE 12 +#define DSETUP_CB_MSG_BEGIN_INSTALL 13 +#define DSETUP_CB_MSG_BEGIN_INSTALL_RUNTIME 14 +#define DSETUP_CB_MSG_BEGIN_INSTALL_DRIVERS 15 +#define DSETUP_CB_MSG_BEGIN_RESTORE_DRIVERS 16 +#define DSETUP_CB_MSG_FILECOPYERROR 17 + + +#define DSETUP_CB_UPGRADE_TYPE_MASK 0x000F +#define DSETUP_CB_UPGRADE_KEEP 0x0001 +#define DSETUP_CB_UPGRADE_SAFE 0x0002 +#define DSETUP_CB_UPGRADE_FORCE 0x0004 +#define DSETUP_CB_UPGRADE_UNKNOWN 0x0008 + +#define DSETUP_CB_UPGRADE_HASWARNINGS 0x0100 +#define DSETUP_CB_UPGRADE_CANTBACKUP 0x0200 + +#define DSETUP_CB_UPGRADE_DEVICE_ACTIVE 0x0800 + +#define DSETUP_CB_UPGRADE_DEVICE_DISPLAY 0x1000 +#define DSETUP_CB_UPGRADE_DEVICE_MEDIA 0x2000 + + +typedef struct _DSETUP_CB_UPGRADEINFO +{ + DWORD UpgradeFlags; +} DSETUP_CB_UPGRADEINFO; + +typedef struct _DSETUP_CB_FILECOPYERROR +{ + DWORD dwError; +} DSETUP_CB_FILECOPYERROR; + + +#ifdef _WIN32 +// +// Data Structures +// +#ifndef UNICODE_ONLY +typedef struct _DIRECTXREGISTERAPPA { + DWORD dwSize; + DWORD dwFlags; + LPSTR lpszApplicationName; + LPGUID lpGUID; + LPSTR lpszFilename; + LPSTR lpszCommandLine; + LPSTR lpszPath; + LPSTR lpszCurrentDirectory; +} DIRECTXREGISTERAPPA, *PDIRECTXREGISTERAPPA, *LPDIRECTXREGISTERAPPA; +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +typedef struct _DIRECTXREGISTERAPPW { + DWORD dwSize; + DWORD dwFlags; + LPWSTR lpszApplicationName; + LPGUID lpGUID; + LPWSTR lpszFilename; + LPWSTR lpszCommandLine; + LPWSTR lpszPath; + LPWSTR lpszCurrentDirectory; +} DIRECTXREGISTERAPPW, *PDIRECTXREGISTERAPPW, *LPDIRECTXREGISTERAPPW; +#endif //!ANSI_ONLY +#ifdef UNICODE +typedef DIRECTXREGISTERAPPW DIRECTXREGISTERAPP; +typedef PDIRECTXREGISTERAPPW PDIRECTXREGISTERAPP; +typedef LPDIRECTXREGISTERAPPW LPDIRECTXREGISTERAPP; +#else +typedef DIRECTXREGISTERAPPA DIRECTXREGISTERAPP; +typedef PDIRECTXREGISTERAPPA PDIRECTXREGISTERAPP; +typedef LPDIRECTXREGISTERAPPA LPDIRECTXREGISTERAPP; +#endif // UNICODE + + +// +// API +// +#ifndef UNICODE_ONLY +INT +WINAPI +DirectXSetupA( + HWND hWnd, + LPSTR lpszRootPath, + DWORD dwFlags + ); +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +INT +WINAPI +DirectXSetupW( + HWND hWnd, + LPWSTR lpszRootPath, + DWORD dwFlags + ); +#endif //!ANSI_ONLY +#ifdef UNICODE +#define DirectXSetup DirectXSetupW +#else +#define DirectXSetup DirectXSetupA +#endif // !UNICODE + +#ifndef UNICODE_ONLY +INT +WINAPI +DirectXDeviceDriverSetupA( + HWND hWnd, + LPSTR lpszDriverClass, + LPSTR lpszDriverPath, + DWORD dwFlags + ); +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +INT +WINAPI +DirectXDeviceDriverSetupW( + HWND hWnd, + LPWSTR lpszDriverClass, + LPWSTR lpszDriverPath, + DWORD dwFlags + ); +#endif //!ANSI_ONLY +#ifdef UNICODE +#define DirectXDeviceDriverSetup DirectXDeviceDriverSetupW +#else +#define DirectXDeviceDriverSetup DirectXDeviceDriverSetupA +#endif // !UNICODE + +#ifndef UNICODE_ONLY +INT +WINAPI +DirectXRegisterApplicationA( + HWND hWnd, + LPDIRECTXREGISTERAPPA lpDXRegApp + ); +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +INT +WINAPI +DirectXRegisterApplicationW( + HWND hWnd, + LPDIRECTXREGISTERAPPW lpDXRegApp + ); +#endif //!ANSI_ONLY +#ifdef UNICODE +#define DirectXRegisterApplication DirectXRegisterApplicationW +#else +#define DirectXRegisterApplication DirectXRegisterApplicationA +#endif // !UNICODE + +INT +WINAPI +DirectXUnRegisterApplication( + HWND hWnd, + LPGUID lpGUID + ); + +// +// Function Pointers +// +#ifdef UNICODE +typedef INT (WINAPI * LPDIRECTXSETUP)(HWND, LPWSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXDEVICEDRIVERSETUP)(HWND, LPWSTR, LPSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXREGISTERAPPLICATION)(HWND, LPDIRECTXREGISTERAPPW); +#else +typedef INT (WINAPI * LPDIRECTXSETUP)(HWND, LPSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXDEVICEDRIVERSETUP)(HWND, LPSTR, LPSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXREGISTERAPPLICATION)(HWND, LPDIRECTXREGISTERAPPA); +#endif // UNICODE + +typedef DWORD (FAR PASCAL * DSETUP_CALLBACK)(DWORD Reason, + DWORD MsgType, /* Same as flags to MessageBox */ + LPSTR szMessage, + LPSTR szName, + void *pInfo); + +INT WINAPI DirectXSetupSetCallback(DSETUP_CALLBACK Callback); +INT WINAPI DirectXSetupGetVersion(DWORD *lpdwVersion, DWORD *lpdwMinorVersion); + +#endif // WIN32 + + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/lib/win/DirectX/DSETUP.LIB b/lib/win/DirectX/DSETUP.LIB new file mode 100644 index 000000000..073054529 Binary files /dev/null and b/lib/win/DirectX/DSETUP.LIB differ diff --git a/lib/win/DirectX/d3d.h b/lib/win/DirectX/d3d.h new file mode 100644 index 000000000..935dae529 --- /dev/null +++ b/lib/win/DirectX/d3d.h @@ -0,0 +1,1320 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1998 Microsoft Corporation. All Rights Reserved. + * + * File: d3d.h + * Content: Direct3D include file + * + ****************************************************************************/ + +#ifndef _D3D_H_ +#define _D3D_H_ + + +#include + +#define COM_NO_WINDOWS_H +#include + +#define D3DAPI WINAPI + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x0600 +#endif + +/* + * Interface IID's + */ +#if defined( _WIN32 ) && !defined( _NO_COM) +DEFINE_GUID( IID_IDirect3D, 0x3BBA0080,0x2421,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3D2, 0x6aae1ec1,0x662a,0x11d0,0x88,0x9d,0x00,0xaa,0x00,0xbb,0xb7,0x6a); +DEFINE_GUID( IID_IDirect3D3, 0xbb223240,0xe72b,0x11d0,0xa9,0xb4,0x00,0xaa,0x00,0xc0,0x99,0x3e); + +DEFINE_GUID( IID_IDirect3DRampDevice, 0xF2086B20,0x259F,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3DRGBDevice, 0xA4665C60,0x2673,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3DHALDevice, 0x84E63dE0,0x46AA,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DMMXDevice, 0x881949a1,0xd6f3,0x11d0,0x89,0xab,0x00,0xa0,0xc9,0x05,0x41,0x29 ); + +DEFINE_GUID( IID_IDirect3DRefDevice, 0x50936643, 0x13e9, 0x11d1, 0x89, 0xaa, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DNullDevice, 0x8767df22, 0xbacc, 0x11d1, 0x89, 0x69, 0x0, 0xa0, 0xc9, 0x6, 0x29, 0xa8); + +/* + * Internal Guid to distinguish requested MMX from MMX being used as an RGB rasterizer + */ + +DEFINE_GUID( IID_IDirect3DDevice, 0x64108800,0x957d,0X11d0,0x89,0xab,0x00,0xa0,0xc9,0x05,0x41,0x29 ); +DEFINE_GUID( IID_IDirect3DDevice2, 0x93281501, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DDevice3, 0xb0ab3b60, 0x33d7, 0x11d1, 0xa9, 0x81, 0x0, 0xc0, 0x4f, 0xd7, 0xb1, 0x74); + +DEFINE_GUID( IID_IDirect3DTexture, 0x2CDCD9E0,0x25A0,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3DTexture2, 0x93281502, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DLight, 0x4417C142,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DMaterial, 0x4417C144,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DMaterial2, 0x93281503, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DMaterial3, 0xca9c46f4, 0xd3c5, 0x11d1, 0xb7, 0x5a, 0x0, 0x60, 0x8, 0x52, 0xb3, 0x12); +DEFINE_GUID( IID_IDirect3DExecuteBuffer,0x4417C145,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DViewport, 0x4417C146,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DViewport2, 0x93281500, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DViewport3, 0xb0ab3b61, 0x33d7, 0x11d1, 0xa9, 0x81, 0x0, 0xc0, 0x4f, 0xd7, 0xb1, 0x74); +DEFINE_GUID( IID_IDirect3DVertexBuffer, 0x7a503555, 0x4a83, 0x11d1, 0xa5, 0xdb, 0x0, 0xa0, 0xc9, 0x3, 0x67, 0xf8); +#endif + +#ifdef __cplusplus +struct IDirect3D; +struct IDirect3D2; +struct IDirect3D3; +struct IDirect3DDevice; +struct IDirect3DDevice2; +struct IDirect3DDevice3; +struct IDirect3DExecuteBuffer; +struct IDirect3DLight; +struct IDirect3DMaterial; +struct IDirect3DMaterial2; +struct IDirect3DTexture; +struct IDirect3DTexture2; +struct IDirect3DViewport; +struct IDirect3DViewport2; +struct IDirect3DViewport3; +struct IDirect3DVertexBuffer; +typedef struct IDirect3D *LPDIRECT3D; +typedef struct IDirect3D2 *LPDIRECT3D2; +typedef struct IDirect3D3 *LPDIRECT3D3; +typedef struct IDirect3DDevice *LPDIRECT3DDEVICE; +typedef struct IDirect3DDevice2 *LPDIRECT3DDEVICE2; +typedef struct IDirect3DDevice3 *LPDIRECT3DDEVICE3; +typedef struct IDirect3DExecuteBuffer *LPDIRECT3DEXECUTEBUFFER; +typedef struct IDirect3DLight *LPDIRECT3DLIGHT; +typedef struct IDirect3DMaterial *LPDIRECT3DMATERIAL; +typedef struct IDirect3DMaterial2 *LPDIRECT3DMATERIAL2; +typedef struct IDirect3DMaterial3 *LPDIRECT3DMATERIAL3; +typedef struct IDirect3DTexture *LPDIRECT3DTEXTURE; +typedef struct IDirect3DTexture2 *LPDIRECT3DTEXTURE2; +typedef struct IDirect3DViewport *LPDIRECT3DVIEWPORT; +typedef struct IDirect3DViewport2 *LPDIRECT3DVIEWPORT2; +typedef struct IDirect3DViewport3 *LPDIRECT3DVIEWPORT3; +typedef struct IDirect3DVertexBuffer *LPDIRECT3DVERTEXBUFFER; + +#else + +typedef struct IDirect3D *LPDIRECT3D; +typedef struct IDirect3D2 *LPDIRECT3D2; +typedef struct IDirect3D3 *LPDIRECT3D3; +typedef struct IDirect3DDevice *LPDIRECT3DDEVICE; +typedef struct IDirect3DDevice2 *LPDIRECT3DDEVICE2; +typedef struct IDirect3DDevice3 *LPDIRECT3DDEVICE3; +typedef struct IDirect3DExecuteBuffer *LPDIRECT3DEXECUTEBUFFER; +typedef struct IDirect3DLight *LPDIRECT3DLIGHT; +typedef struct IDirect3DMaterial *LPDIRECT3DMATERIAL; +typedef struct IDirect3DMaterial2 *LPDIRECT3DMATERIAL2; +typedef struct IDirect3DMaterial3 *LPDIRECT3DMATERIAL3; +typedef struct IDirect3DTexture *LPDIRECT3DTEXTURE; +typedef struct IDirect3DTexture2 *LPDIRECT3DTEXTURE2; +typedef struct IDirect3DViewport *LPDIRECT3DVIEWPORT; +typedef struct IDirect3DViewport2 *LPDIRECT3DVIEWPORT2; +typedef struct IDirect3DViewport3 *LPDIRECT3DVIEWPORT3; +typedef struct IDirect3DVertexBuffer *LPDIRECT3DVERTEXBUFFER; + +#endif + +#include "d3dtypes.h" +#include "d3dcaps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Direct3D interfaces + */ +#undef INTERFACE +#define INTERFACE IDirect3D + +DECLARE_INTERFACE_(IDirect3D, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3D methods ***/ + STDMETHOD(Initialize)(THIS_ REFCLSID) PURE; + STDMETHOD(EnumDevices)(THIS_ LPD3DENUMDEVICESCALLBACK,LPVOID) PURE; + STDMETHOD(CreateLight)(THIS_ LPDIRECT3DLIGHT*,IUnknown*) PURE; + STDMETHOD(CreateMaterial)(THIS_ LPDIRECT3DMATERIAL*,IUnknown*) PURE; + STDMETHOD(CreateViewport)(THIS_ LPDIRECT3DVIEWPORT*,IUnknown*) PURE; + STDMETHOD(FindDevice)(THIS_ LPD3DFINDDEVICESEARCH,LPD3DFINDDEVICERESULT) PURE; +}; + +typedef struct IDirect3D *LPDIRECT3D; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3D_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3D_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3D_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3D_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirect3D_EnumDevices(p,a,b) (p)->lpVtbl->EnumDevices(p,a,b) +#define IDirect3D_CreateLight(p,a,b) (p)->lpVtbl->CreateLight(p,a,b) +#define IDirect3D_CreateMaterial(p,a,b) (p)->lpVtbl->CreateMaterial(p,a,b) +#define IDirect3D_CreateViewport(p,a,b) (p)->lpVtbl->CreateViewport(p,a,b) +#define IDirect3D_FindDevice(p,a,b) (p)->lpVtbl->FindDevice(p,a,b) +#else +#define IDirect3D_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3D_AddRef(p) (p)->AddRef() +#define IDirect3D_Release(p) (p)->Release() +#define IDirect3D_Initialize(p,a) (p)->Initialize(a) +#define IDirect3D_EnumDevices(p,a,b) (p)->EnumDevices(a,b) +#define IDirect3D_CreateLight(p,a,b) (p)->CreateLight(a,b) +#define IDirect3D_CreateMaterial(p,a,b) (p)->CreateMaterial(a,b) +#define IDirect3D_CreateViewport(p,a,b) (p)->CreateViewport(a,b) +#define IDirect3D_FindDevice(p,a,b) (p)->FindDevice(a,b) +#endif + +#undef INTERFACE +#define INTERFACE IDirect3D2 + +DECLARE_INTERFACE_(IDirect3D2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3D2 methods ***/ + STDMETHOD(EnumDevices)(THIS_ LPD3DENUMDEVICESCALLBACK,LPVOID) PURE; + STDMETHOD(CreateLight)(THIS_ LPDIRECT3DLIGHT*,IUnknown*) PURE; + STDMETHOD(CreateMaterial)(THIS_ LPDIRECT3DMATERIAL2*,IUnknown*) PURE; + STDMETHOD(CreateViewport)(THIS_ LPDIRECT3DVIEWPORT2*,IUnknown*) PURE; + STDMETHOD(FindDevice)(THIS_ LPD3DFINDDEVICESEARCH,LPD3DFINDDEVICERESULT) PURE; + STDMETHOD(CreateDevice)(THIS_ REFCLSID,LPDIRECTDRAWSURFACE,LPDIRECT3DDEVICE2*) PURE; +}; + +typedef struct IDirect3D2 *LPDIRECT3D2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3D2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3D2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3D2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3D2_EnumDevices(p,a,b) (p)->lpVtbl->EnumDevices(p,a,b) +#define IDirect3D2_CreateLight(p,a,b) (p)->lpVtbl->CreateLight(p,a,b) +#define IDirect3D2_CreateMaterial(p,a,b) (p)->lpVtbl->CreateMaterial(p,a,b) +#define IDirect3D2_CreateViewport(p,a,b) (p)->lpVtbl->CreateViewport(p,a,b) +#define IDirect3D2_FindDevice(p,a,b) (p)->lpVtbl->FindDevice(p,a,b) +#define IDirect3D2_CreateDevice(p,a,b,c) (p)->lpVtbl->CreateDevice(p,a,b,c) +#else +#define IDirect3D2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3D2_AddRef(p) (p)->AddRef() +#define IDirect3D2_Release(p) (p)->Release() +#define IDirect3D2_EnumDevices(p,a,b) (p)->EnumDevices(a,b) +#define IDirect3D2_CreateLight(p,a,b) (p)->CreateLight(a,b) +#define IDirect3D2_CreateMaterial(p,a,b) (p)->CreateMaterial(a,b) +#define IDirect3D2_CreateViewport(p,a,b) (p)->CreateViewport(a,b) +#define IDirect3D2_FindDevice(p,a,b) (p)->FindDevice(a,b) +#define IDirect3D2_CreateDevice(p,a,b,c) (p)->CreateDevice(a,b,c) +#endif + +#undef INTERFACE +#define INTERFACE IDirect3D3 + +DECLARE_INTERFACE_(IDirect3D3, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3D3 methods ***/ + STDMETHOD(EnumDevices)(THIS_ LPD3DENUMDEVICESCALLBACK,LPVOID) PURE; + STDMETHOD(CreateLight)(THIS_ LPDIRECT3DLIGHT*,LPUNKNOWN) PURE; + STDMETHOD(CreateMaterial)(THIS_ LPDIRECT3DMATERIAL3*,LPUNKNOWN) PURE; + STDMETHOD(CreateViewport)(THIS_ LPDIRECT3DVIEWPORT3*,LPUNKNOWN) PURE; + STDMETHOD(FindDevice)(THIS_ LPD3DFINDDEVICESEARCH,LPD3DFINDDEVICERESULT) PURE; + STDMETHOD(CreateDevice)(THIS_ REFCLSID,LPDIRECTDRAWSURFACE4,LPDIRECT3DDEVICE3*,LPUNKNOWN) PURE; + STDMETHOD(CreateVertexBuffer)(THIS_ LPD3DVERTEXBUFFERDESC,LPDIRECT3DVERTEXBUFFER*,DWORD,LPUNKNOWN) PURE; + STDMETHOD(EnumZBufferFormats)(THIS_ REFCLSID,LPD3DENUMPIXELFORMATSCALLBACK,LPVOID) PURE; + STDMETHOD(EvictManagedTextures)(THIS) PURE; +}; + +typedef struct IDirect3D3 *LPDIRECT3D3; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3D3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3D3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3D3_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3D3_EnumDevices(p,a,b) (p)->lpVtbl->EnumDevices(p,a,b) +#define IDirect3D3_CreateLight(p,a,b) (p)->lpVtbl->CreateLight(p,a,b) +#define IDirect3D3_CreateMaterial(p,a,b) (p)->lpVtbl->CreateMaterial(p,a,b) +#define IDirect3D3_CreateViewport(p,a,b) (p)->lpVtbl->CreateViewport(p,a,b) +#define IDirect3D3_FindDevice(p,a,b) (p)->lpVtbl->FindDevice(p,a,b) +#define IDirect3D3_CreateDevice(p,a,b,c,d) (p)->lpVtbl->CreateDevice(p,a,b,c,d) +#define IDirect3D3_CreateVertexBuffer(p,a,b,c,d) (p)->lpVtbl->CreateVertexBuffer(p,a,b,c,d) +#define IDirect3D3_EnumZBufferFormats(p,a,b,c) (p)->lpVtbl->EnumZBufferFormats(p,a,b,c) +#define IDirect3D3_EvictManagedTextures(p) (p)->lpVtbl->EvictManagedTextures(p) +#else +#define IDirect3D3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3D3_AddRef(p) (p)->AddRef() +#define IDirect3D3_Release(p) (p)->Release() +#define IDirect3D3_EnumDevices(p,a,b) (p)->EnumDevices(a,b) +#define IDirect3D3_CreateLight(p,a,b) (p)->CreateLight(a,b) +#define IDirect3D3_CreateMaterial(p,a,b) (p)->CreateMaterial(a,b) +#define IDirect3D3_CreateViewport(p,a,b) (p)->CreateViewport(a,b) +#define IDirect3D3_FindDevice(p,a,b) (p)->FindDevice(a,b) +#define IDirect3D3_CreateDevice(p,a,b,c,d) (p)->CreateDevice(a,b,c,d) +#define IDirect3D3_CreateVertexBuffer(p,a,b,c,d) (p)->CreateVertexBuffer(a,b,c,d) +#define IDirect3D3_EnumZBufferFormats(p,a,b,c) (p)->EnumZBufferFormats(a,b,c) +#define IDirect3D3_EvictManagedTextures(p) (p)->EvictManagedTextures() +#endif + +/* + * Direct3D Device interfaces + */ +#undef INTERFACE +#define INTERFACE IDirect3DDevice + +DECLARE_INTERFACE_(IDirect3DDevice, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DDevice methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3D,LPGUID,LPD3DDEVICEDESC) PURE; + STDMETHOD(GetCaps)(THIS_ LPD3DDEVICEDESC,LPD3DDEVICEDESC) PURE; + STDMETHOD(SwapTextureHandles)(THIS_ LPDIRECT3DTEXTURE,LPDIRECT3DTEXTURE) PURE; + STDMETHOD(CreateExecuteBuffer)(THIS_ LPD3DEXECUTEBUFFERDESC,LPDIRECT3DEXECUTEBUFFER*,IUnknown*) PURE; + STDMETHOD(GetStats)(THIS_ LPD3DSTATS) PURE; + STDMETHOD(Execute)(THIS_ LPDIRECT3DEXECUTEBUFFER,LPDIRECT3DVIEWPORT,DWORD) PURE; + STDMETHOD(AddViewport)(THIS_ LPDIRECT3DVIEWPORT) PURE; + STDMETHOD(DeleteViewport)(THIS_ LPDIRECT3DVIEWPORT) PURE; + STDMETHOD(NextViewport)(THIS_ LPDIRECT3DVIEWPORT,LPDIRECT3DVIEWPORT*,DWORD) PURE; + STDMETHOD(Pick)(THIS_ LPDIRECT3DEXECUTEBUFFER,LPDIRECT3DVIEWPORT,DWORD,LPD3DRECT) PURE; + STDMETHOD(GetPickRecords)(THIS_ LPDWORD,LPD3DPICKRECORD) PURE; + STDMETHOD(EnumTextureFormats)(THIS_ LPD3DENUMTEXTUREFORMATSCALLBACK,LPVOID) PURE; + STDMETHOD(CreateMatrix)(THIS_ LPD3DMATRIXHANDLE) PURE; + STDMETHOD(SetMatrix)(THIS_ D3DMATRIXHANDLE,const LPD3DMATRIX) PURE; + STDMETHOD(GetMatrix)(THIS_ D3DMATRIXHANDLE,LPD3DMATRIX) PURE; + STDMETHOD(DeleteMatrix)(THIS_ D3DMATRIXHANDLE) PURE; + STDMETHOD(BeginScene)(THIS) PURE; + STDMETHOD(EndScene)(THIS) PURE; + STDMETHOD(GetDirect3D)(THIS_ LPDIRECT3D*) PURE; +}; + +typedef struct IDirect3DDevice *LPDIRECT3DDEVICE; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DDevice_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DDevice_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DDevice_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DDevice_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c) +#define IDirect3DDevice_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirect3DDevice_SwapTextureHandles(p,a,b) (p)->lpVtbl->SwapTextureHandles(p,a,b) +#define IDirect3DDevice_CreateExecuteBuffer(p,a,b,c) (p)->lpVtbl->CreateExecuteBuffer(p,a,b,c) +#define IDirect3DDevice_GetStats(p,a) (p)->lpVtbl->GetStats(p,a) +#define IDirect3DDevice_Execute(p,a,b,c) (p)->lpVtbl->Execute(p,a,b,c) +#define IDirect3DDevice_AddViewport(p,a) (p)->lpVtbl->AddViewport(p,a) +#define IDirect3DDevice_DeleteViewport(p,a) (p)->lpVtbl->DeleteViewport(p,a) +#define IDirect3DDevice_NextViewport(p,a,b,c) (p)->lpVtbl->NextViewport(p,a,b,c) +#define IDirect3DDevice_Pick(p,a,b,c,d) (p)->lpVtbl->Pick(p,a,b,c,d) +#define IDirect3DDevice_GetPickRecords(p,a,b) (p)->lpVtbl->GetPickRecords(p,a,b) +#define IDirect3DDevice_EnumTextureFormats(p,a,b) (p)->lpVtbl->EnumTextureFormats(p,a,b) +#define IDirect3DDevice_CreateMatrix(p,a) (p)->lpVtbl->CreateMatrix(p,a) +#define IDirect3DDevice_SetMatrix(p,a,b) (p)->lpVtbl->SetMatrix(p,a,b) +#define IDirect3DDevice_GetMatrix(p,a,b) (p)->lpVtbl->GetMatrix(p,a,b) +#define IDirect3DDevice_DeleteMatrix(p,a) (p)->lpVtbl->DeleteMatrix(p,a) +#define IDirect3DDevice_BeginScene(p) (p)->lpVtbl->BeginScene(p) +#define IDirect3DDevice_EndScene(p) (p)->lpVtbl->EndScene(p) +#define IDirect3DDevice_GetDirect3D(p,a) (p)->lpVtbl->GetDirect3D(p,a) +#else +#define IDirect3DDevice_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DDevice_AddRef(p) (p)->AddRef() +#define IDirect3DDevice_Release(p) (p)->Release() +#define IDirect3DDevice_Initialize(p,a,b,c) (p)->Initialize(a,b,c) +#define IDirect3DDevice_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirect3DDevice_SwapTextureHandles(p,a,b) (p)->SwapTextureHandles(a,b) +#define IDirect3DDevice_CreateExecuteBuffer(p,a,b,c) (p)->CreateExecuteBuffer(a,b,c) +#define IDirect3DDevice_GetStats(p,a) (p)->GetStats(a) +#define IDirect3DDevice_Execute(p,a,b,c) (p)->Execute(a,b,c) +#define IDirect3DDevice_AddViewport(p,a) (p)->AddViewport(a) +#define IDirect3DDevice_DeleteViewport(p,a) (p)->DeleteViewport(a) +#define IDirect3DDevice_NextViewport(p,a,b,c) (p)->NextViewport(a,b,c) +#define IDirect3DDevice_Pick(p,a,b,c,d) (p)->Pick(a,b,c,d) +#define IDirect3DDevice_GetPickRecords(p,a,b) (p)->GetPickRecords(a,b) +#define IDirect3DDevice_EnumTextureFormats(p,a,b) (p)->EnumTextureFormats(a,b) +#define IDirect3DDevice_CreateMatrix(p,a) (p)->CreateMatrix(a) +#define IDirect3DDevice_SetMatrix(p,a,b) (p)->SetMatrix(a,b) +#define IDirect3DDevice_GetMatrix(p,a,b) (p)->GetMatrix(a,b) +#define IDirect3DDevice_DeleteMatrix(p,a) (p)->DeleteMatrix(a) +#define IDirect3DDevice_BeginScene(p) (p)->BeginScene() +#define IDirect3DDevice_EndScene(p) (p)->EndScene() +#define IDirect3DDevice_GetDirect3D(p,a) (p)->GetDirect3D(a) +#endif + +#undef INTERFACE +#define INTERFACE IDirect3DDevice2 + +DECLARE_INTERFACE_(IDirect3DDevice2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DDevice2 methods ***/ + STDMETHOD(GetCaps)(THIS_ LPD3DDEVICEDESC,LPD3DDEVICEDESC) PURE; + STDMETHOD(SwapTextureHandles)(THIS_ LPDIRECT3DTEXTURE2,LPDIRECT3DTEXTURE2) PURE; + STDMETHOD(GetStats)(THIS_ LPD3DSTATS) PURE; + STDMETHOD(AddViewport)(THIS_ LPDIRECT3DVIEWPORT2) PURE; + STDMETHOD(DeleteViewport)(THIS_ LPDIRECT3DVIEWPORT2) PURE; + STDMETHOD(NextViewport)(THIS_ LPDIRECT3DVIEWPORT2,LPDIRECT3DVIEWPORT2*,DWORD) PURE; + STDMETHOD(EnumTextureFormats)(THIS_ LPD3DENUMTEXTUREFORMATSCALLBACK,LPVOID) PURE; + STDMETHOD(BeginScene)(THIS) PURE; + STDMETHOD(EndScene)(THIS) PURE; + STDMETHOD(GetDirect3D)(THIS_ LPDIRECT3D2*) PURE; + STDMETHOD(SetCurrentViewport)(THIS_ LPDIRECT3DVIEWPORT2) PURE; + STDMETHOD(GetCurrentViewport)(THIS_ LPDIRECT3DVIEWPORT2 *) PURE; + STDMETHOD(SetRenderTarget)(THIS_ LPDIRECTDRAWSURFACE,DWORD) PURE; + STDMETHOD(GetRenderTarget)(THIS_ LPDIRECTDRAWSURFACE *) PURE; + STDMETHOD(Begin)(THIS_ D3DPRIMITIVETYPE,D3DVERTEXTYPE,DWORD) PURE; + STDMETHOD(BeginIndexed)(THIS_ D3DPRIMITIVETYPE,D3DVERTEXTYPE,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(Vertex)(THIS_ LPVOID) PURE; + STDMETHOD(Index)(THIS_ WORD) PURE; + STDMETHOD(End)(THIS_ DWORD) PURE; + STDMETHOD(GetRenderState)(THIS_ D3DRENDERSTATETYPE,LPDWORD) PURE; + STDMETHOD(SetRenderState)(THIS_ D3DRENDERSTATETYPE,DWORD) PURE; + STDMETHOD(GetLightState)(THIS_ D3DLIGHTSTATETYPE,LPDWORD) PURE; + STDMETHOD(SetLightState)(THIS_ D3DLIGHTSTATETYPE,DWORD) PURE; + STDMETHOD(SetTransform)(THIS_ D3DTRANSFORMSTATETYPE,LPD3DMATRIX) PURE; + STDMETHOD(GetTransform)(THIS_ D3DTRANSFORMSTATETYPE,LPD3DMATRIX) PURE; + STDMETHOD(MultiplyTransform)(THIS_ D3DTRANSFORMSTATETYPE,LPD3DMATRIX) PURE; + STDMETHOD(DrawPrimitive)(THIS_ D3DPRIMITIVETYPE,D3DVERTEXTYPE,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DrawIndexedPrimitive)(THIS_ D3DPRIMITIVETYPE,D3DVERTEXTYPE,LPVOID,DWORD,LPWORD,DWORD,DWORD) PURE; + STDMETHOD(SetClipStatus)(THIS_ LPD3DCLIPSTATUS) PURE; + STDMETHOD(GetClipStatus)(THIS_ LPD3DCLIPSTATUS) PURE; +}; + +typedef struct IDirect3DDevice2 *LPDIRECT3DDEVICE2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DDevice2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DDevice2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DDevice2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DDevice2_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirect3DDevice2_SwapTextureHandles(p,a,b) (p)->lpVtbl->SwapTextureHandles(p,a,b) +#define IDirect3DDevice2_GetStats(p,a) (p)->lpVtbl->GetStats(p,a) +#define IDirect3DDevice2_AddViewport(p,a) (p)->lpVtbl->AddViewport(p,a) +#define IDirect3DDevice2_DeleteViewport(p,a) (p)->lpVtbl->DeleteViewport(p,a) +#define IDirect3DDevice2_NextViewport(p,a,b,c) (p)->lpVtbl->NextViewport(p,a,b,c) +#define IDirect3DDevice2_EnumTextureFormats(p,a,b) (p)->lpVtbl->EnumTextureFormats(p,a,b) +#define IDirect3DDevice2_BeginScene(p) (p)->lpVtbl->BeginScene(p) +#define IDirect3DDevice2_EndScene(p) (p)->lpVtbl->EndScene(p) +#define IDirect3DDevice2_GetDirect3D(p,a) (p)->lpVtbl->GetDirect3D(p,a) +#define IDirect3DDevice2_SetCurrentViewport(p,a) (p)->lpVtbl->SetCurrentViewport(p,a) +#define IDirect3DDevice2_GetCurrentViewport(p,a) (p)->lpVtbl->GetCurrentViewport(p,a) +#define IDirect3DDevice2_SetRenderTarget(p,a,b) (p)->lpVtbl->SetRenderTarget(p,a,b) +#define IDirect3DDevice2_GetRenderTarget(p,a) (p)->lpVtbl->GetRenderTarget(p,a) +#define IDirect3DDevice2_Begin(p,a,b,c) (p)->lpVtbl->Begin(p,a,b,c) +#define IDirect3DDevice2_BeginIndexed(p,a,b,c,d,e) (p)->lpVtbl->BeginIndexed(p,a,b,c,d,e) +#define IDirect3DDevice2_Vertex(p,a) (p)->lpVtbl->Vertex(p,a) +#define IDirect3DDevice2_Index(p,a) (p)->lpVtbl->Index(p,a) +#define IDirect3DDevice2_End(p,a) (p)->lpVtbl->End(p,a) +#define IDirect3DDevice2_GetRenderState(p,a,b) (p)->lpVtbl->GetRenderState(p,a,b) +#define IDirect3DDevice2_SetRenderState(p,a,b) (p)->lpVtbl->SetRenderState(p,a,b) +#define IDirect3DDevice2_GetLightState(p,a,b) (p)->lpVtbl->GetLightState(p,a,b) +#define IDirect3DDevice2_SetLightState(p,a,b) (p)->lpVtbl->SetLightState(p,a,b) +#define IDirect3DDevice2_SetTransform(p,a,b) (p)->lpVtbl->SetTransform(p,a,b) +#define IDirect3DDevice2_GetTransform(p,a,b) (p)->lpVtbl->GetTransform(p,a,b) +#define IDirect3DDevice2_MultiplyTransform(p,a,b) (p)->lpVtbl->MultiplyTransform(p,a,b) +#define IDirect3DDevice2_DrawPrimitive(p,a,b,c,d,e) (p)->lpVtbl->DrawPrimitive(p,a,b,c,d,e) +#define IDirect3DDevice2_DrawIndexedPrimitive(p,a,b,c,d,e,f,g) (p)->lpVtbl->DrawIndexedPrimitive(p,a,b,c,d,e,f,g) +#define IDirect3DDevice2_SetClipStatus(p,a) (p)->lpVtbl->SetClipStatus(p,a) +#define IDirect3DDevice2_GetClipStatus(p,a) (p)->lpVtbl->GetClipStatus(p,a) +#else +#define IDirect3DDevice2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DDevice2_AddRef(p) (p)->AddRef() +#define IDirect3DDevice2_Release(p) (p)->Release() +#define IDirect3DDevice2_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirect3DDevice2_SwapTextureHandles(p,a,b) (p)->SwapTextureHandles(a,b) +#define IDirect3DDevice2_GetStats(p,a) (p)->GetStats(a) +#define IDirect3DDevice2_AddViewport(p,a) (p)->AddViewport(a) +#define IDirect3DDevice2_DeleteViewport(p,a) (p)->DeleteViewport(a) +#define IDirect3DDevice2_NextViewport(p,a,b,c) (p)->NextViewport(a,b,c) +#define IDirect3DDevice2_EnumTextureFormats(p,a,b) (p)->EnumTextureFormats(a,b) +#define IDirect3DDevice2_BeginScene(p) (p)->BeginScene() +#define IDirect3DDevice2_EndScene(p) (p)->EndScene() +#define IDirect3DDevice2_GetDirect3D(p,a) (p)->GetDirect3D(a) +#define IDirect3DDevice2_SetCurrentViewport(p,a) (p)->SetCurrentViewport(a) +#define IDirect3DDevice2_GetCurrentViewport(p,a) (p)->GetCurrentViewport(a) +#define IDirect3DDevice2_SetRenderTarget(p,a,b) (p)->SetRenderTarget(a,b) +#define IDirect3DDevice2_GetRenderTarget(p,a) (p)->GetRenderTarget(a) +#define IDirect3DDevice2_Begin(p,a,b,c) (p)->Begin(a,b,c) +#define IDirect3DDevice2_BeginIndexed(p,a,b,c,d,e) (p)->BeginIndexed(a,b,c,d,e) +#define IDirect3DDevice2_Vertex(p,a) (p)->Vertex(a) +#define IDirect3DDevice2_Index(p,a) (p)->Index(a) +#define IDirect3DDevice2_End(p,a) (p)->End(a) +#define IDirect3DDevice2_GetRenderState(p,a,b) (p)->GetRenderState(a,b) +#define IDirect3DDevice2_SetRenderState(p,a,b) (p)->SetRenderState(a,b) +#define IDirect3DDevice2_GetLightState(p,a,b) (p)->GetLightState(a,b) +#define IDirect3DDevice2_SetLightState(p,a,b) (p)->SetLightState(a,b) +#define IDirect3DDevice2_SetTransform(p,a,b) (p)->SetTransform(a,b) +#define IDirect3DDevice2_GetTransform(p,a,b) (p)->GetTransform(a,b) +#define IDirect3DDevice2_MultiplyTransform(p,a,b) (p)->MultiplyTransform(a,b) +#define IDirect3DDevice2_DrawPrimitive(p,a,b,c,d,e) (p)->DrawPrimitive(a,b,c,d,e) +#define IDirect3DDevice2_DrawIndexedPrimitive(p,a,b,c,d,e,f,g) (p)->DrawIndexedPrimitive(a,b,c,d,e,f,g) +#define IDirect3DDevice2_SetClipStatus(p,a) (p)->SetClipStatus(a) +#define IDirect3DDevice2_GetClipStatus(p,a) (p)->GetClipStatus(a) +#endif + +#undef INTERFACE +#define INTERFACE IDirect3DDevice3 + +DECLARE_INTERFACE_(IDirect3DDevice3, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DDevice3 methods ***/ + STDMETHOD(GetCaps)(THIS_ LPD3DDEVICEDESC,LPD3DDEVICEDESC) PURE; + STDMETHOD(GetStats)(THIS_ LPD3DSTATS) PURE; + STDMETHOD(AddViewport)(THIS_ LPDIRECT3DVIEWPORT3) PURE; + STDMETHOD(DeleteViewport)(THIS_ LPDIRECT3DVIEWPORT3) PURE; + STDMETHOD(NextViewport)(THIS_ LPDIRECT3DVIEWPORT3,LPDIRECT3DVIEWPORT3*,DWORD) PURE; + STDMETHOD(EnumTextureFormats)(THIS_ LPD3DENUMPIXELFORMATSCALLBACK,LPVOID) PURE; + STDMETHOD(BeginScene)(THIS) PURE; + STDMETHOD(EndScene)(THIS) PURE; + STDMETHOD(GetDirect3D)(THIS_ LPDIRECT3D3*) PURE; + STDMETHOD(SetCurrentViewport)(THIS_ LPDIRECT3DVIEWPORT3) PURE; + STDMETHOD(GetCurrentViewport)(THIS_ LPDIRECT3DVIEWPORT3 *) PURE; + STDMETHOD(SetRenderTarget)(THIS_ LPDIRECTDRAWSURFACE4,DWORD) PURE; + STDMETHOD(GetRenderTarget)(THIS_ LPDIRECTDRAWSURFACE4 *) PURE; + STDMETHOD(Begin)(THIS_ D3DPRIMITIVETYPE,DWORD,DWORD) PURE; + STDMETHOD(BeginIndexed)(THIS_ D3DPRIMITIVETYPE,DWORD,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(Vertex)(THIS_ LPVOID) PURE; + STDMETHOD(Index)(THIS_ WORD) PURE; + STDMETHOD(End)(THIS_ DWORD) PURE; + STDMETHOD(GetRenderState)(THIS_ D3DRENDERSTATETYPE,LPDWORD) PURE; + STDMETHOD(SetRenderState)(THIS_ D3DRENDERSTATETYPE,DWORD) PURE; + STDMETHOD(GetLightState)(THIS_ D3DLIGHTSTATETYPE,LPDWORD) PURE; + STDMETHOD(SetLightState)(THIS_ D3DLIGHTSTATETYPE,DWORD) PURE; + STDMETHOD(SetTransform)(THIS_ D3DTRANSFORMSTATETYPE,LPD3DMATRIX) PURE; + STDMETHOD(GetTransform)(THIS_ D3DTRANSFORMSTATETYPE,LPD3DMATRIX) PURE; + STDMETHOD(MultiplyTransform)(THIS_ D3DTRANSFORMSTATETYPE,LPD3DMATRIX) PURE; + STDMETHOD(DrawPrimitive)(THIS_ D3DPRIMITIVETYPE,DWORD,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DrawIndexedPrimitive)(THIS_ D3DPRIMITIVETYPE,DWORD,LPVOID,DWORD,LPWORD,DWORD,DWORD) PURE; + STDMETHOD(SetClipStatus)(THIS_ LPD3DCLIPSTATUS) PURE; + STDMETHOD(GetClipStatus)(THIS_ LPD3DCLIPSTATUS) PURE; + STDMETHOD(DrawPrimitiveStrided)(THIS_ D3DPRIMITIVETYPE,DWORD,LPD3DDRAWPRIMITIVESTRIDEDDATA,DWORD,DWORD) PURE; + STDMETHOD(DrawIndexedPrimitiveStrided)(THIS_ D3DPRIMITIVETYPE,DWORD,LPD3DDRAWPRIMITIVESTRIDEDDATA,DWORD,LPWORD,DWORD,DWORD) PURE; + STDMETHOD(DrawPrimitiveVB)(THIS_ D3DPRIMITIVETYPE,LPDIRECT3DVERTEXBUFFER,DWORD,DWORD,DWORD) PURE; + STDMETHOD(DrawIndexedPrimitiveVB)(THIS_ D3DPRIMITIVETYPE,LPDIRECT3DVERTEXBUFFER,LPWORD,DWORD,DWORD) PURE; + STDMETHOD(ComputeSphereVisibility)(THIS_ LPD3DVECTOR,LPD3DVALUE,DWORD,DWORD,LPDWORD) PURE; + STDMETHOD(GetTexture)(THIS_ DWORD,LPDIRECT3DTEXTURE2 *) PURE; + STDMETHOD(SetTexture)(THIS_ DWORD,LPDIRECT3DTEXTURE2) PURE; + STDMETHOD(GetTextureStageState)(THIS_ DWORD,D3DTEXTURESTAGESTATETYPE,LPDWORD) PURE; + STDMETHOD(SetTextureStageState)(THIS_ DWORD,D3DTEXTURESTAGESTATETYPE,DWORD) PURE; + STDMETHOD(ValidateDevice)(THIS_ LPDWORD) PURE; +}; + +typedef struct IDirect3DDevice3 *LPDIRECT3DDEVICE3; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DDevice3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DDevice3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DDevice3_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DDevice3_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirect3DDevice3_GetStats(p,a) (p)->lpVtbl->GetStats(p,a) +#define IDirect3DDevice3_AddViewport(p,a) (p)->lpVtbl->AddViewport(p,a) +#define IDirect3DDevice3_DeleteViewport(p,a) (p)->lpVtbl->DeleteViewport(p,a) +#define IDirect3DDevice3_NextViewport(p,a,b,c) (p)->lpVtbl->NextViewport(p,a,b,c) +#define IDirect3DDevice3_EnumTextureFormats(p,a,b) (p)->lpVtbl->EnumTextureFormats(p,a,b) +#define IDirect3DDevice3_BeginScene(p) (p)->lpVtbl->BeginScene(p) +#define IDirect3DDevice3_EndScene(p) (p)->lpVtbl->EndScene(p) +#define IDirect3DDevice3_GetDirect3D(p,a) (p)->lpVtbl->GetDirect3D(p,a) +#define IDirect3DDevice3_SetCurrentViewport(p,a) (p)->lpVtbl->SetCurrentViewport(p,a) +#define IDirect3DDevice3_GetCurrentViewport(p,a) (p)->lpVtbl->GetCurrentViewport(p,a) +#define IDirect3DDevice3_SetRenderTarget(p,a,b) (p)->lpVtbl->SetRenderTarget(p,a,b) +#define IDirect3DDevice3_GetRenderTarget(p,a) (p)->lpVtbl->GetRenderTarget(p,a) +#define IDirect3DDevice3_Begin(p,a,b,c) (p)->lpVtbl->Begin(p,a,b,c) +#define IDirect3DDevice3_BeginIndexed(p,a,b,c,d,e) (p)->lpVtbl->BeginIndexed(p,a,b,c,d,e) +#define IDirect3DDevice3_Vertex(p,a) (p)->lpVtbl->Vertex(p,a) +#define IDirect3DDevice3_Index(p,a) (p)->lpVtbl->Index(p,a) +#define IDirect3DDevice3_End(p,a) (p)->lpVtbl->End(p,a) +#define IDirect3DDevice3_GetRenderState(p,a,b) (p)->lpVtbl->GetRenderState(p,a,b) +#define IDirect3DDevice3_SetRenderState(p,a,b) (p)->lpVtbl->SetRenderState(p,a,b) +#define IDirect3DDevice3_GetLightState(p,a,b) (p)->lpVtbl->GetLightState(p,a,b) +#define IDirect3DDevice3_SetLightState(p,a,b) (p)->lpVtbl->SetLightState(p,a,b) +#define IDirect3DDevice3_SetTransform(p,a,b) (p)->lpVtbl->SetTransform(p,a,b) +#define IDirect3DDevice3_GetTransform(p,a,b) (p)->lpVtbl->GetTransform(p,a,b) +#define IDirect3DDevice3_MultiplyTransform(p,a,b) (p)->lpVtbl->MultiplyTransform(p,a,b) +#define IDirect3DDevice3_DrawPrimitive(p,a,b,c,d,e) (p)->lpVtbl->DrawPrimitive(p,a,b,c,d,e) +#define IDirect3DDevice3_DrawIndexedPrimitive(p,a,b,c,d,e,f,g) (p)->lpVtbl->DrawIndexedPrimitive(p,a,b,c,d,e,f,g) +#define IDirect3DDevice3_SetClipStatus(p,a) (p)->lpVtbl->SetClipStatus(p,a) +#define IDirect3DDevice3_GetClipStatus(p,a) (p)->lpVtbl->GetClipStatus(p,a) +#define IDirect3DDevice3_DrawPrimitiveStrided(p,a,b,c,d,e) (p)->lpVtbl->DrawPrimitiveStrided(p,a,b,c,d,e) +#define IDirect3DDevice3_DrawIndexedPrimitiveStrided(p,a,b,c,d,e,f,g) (p)->lpVtbl->DrawIndexedPrimitiveStrided(p,a,b,c,d,e,f,g) +#define IDirect3DDevice3_DrawPrimitiveVB(p,a,b,c,d,e) (p)->lpVtbl->DrawPrimitiveVB(p,a,b,c,d,e) +#define IDirect3DDevice3_DrawIndexedPrimitiveVB(p,a,b,c,d,e) (p)->lpVtbl->DrawIndexedPrimitiveVB(p,a,b,c,d,e) +#define IDirect3DDevice3_ComputeSphereVisibility(p,a,b,c,d,e) (p)->lpVtbl->ComputeSphereVisibility(p,a,b,c,d,e) +#define IDirect3DDevice3_GetTexture(p,a,b) (p)->lpVtbl->GetTexture(p,a,b) +#define IDirect3DDevice3_SetTexture(p,a,b) (p)->lpVtbl->SetTexture(p,a,b) +#define IDirect3DDevice3_GetTextureStageState(p,a,b,c) (p)->lpVtbl->GetTextureStageState(p,a,b,c) +#define IDirect3DDevice3_SetTextureStageState(p,a,b,c) (p)->lpVtbl->SetTextureStageState(p,a,b,c) +#define IDirect3DDevice3_ValidateDevice(p,a) (p)->lpVtbl->ValidateDevice(p,a) +#else +#define IDirect3DDevice3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DDevice3_AddRef(p) (p)->AddRef() +#define IDirect3DDevice3_Release(p) (p)->Release() +#define IDirect3DDevice3_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirect3DDevice3_GetStats(p,a) (p)->GetStats(a) +#define IDirect3DDevice3_AddViewport(p,a) (p)->AddViewport(a) +#define IDirect3DDevice3_DeleteViewport(p,a) (p)->DeleteViewport(a) +#define IDirect3DDevice3_NextViewport(p,a,b,c) (p)->NextViewport(a,b,c) +#define IDirect3DDevice3_EnumTextureFormats(p,a,b) (p)->EnumTextureFormats(a,b) +#define IDirect3DDevice3_BeginScene(p) (p)->BeginScene() +#define IDirect3DDevice3_EndScene(p) (p)->EndScene() +#define IDirect3DDevice3_GetDirect3D(p,a) (p)->GetDirect3D(a) +#define IDirect3DDevice3_SetCurrentViewport(p,a) (p)->SetCurrentViewport(a) +#define IDirect3DDevice3_GetCurrentViewport(p,a) (p)->GetCurrentViewport(a) +#define IDirect3DDevice3_SetRenderTarget(p,a,b) (p)->SetRenderTarget(a,b) +#define IDirect3DDevice3_GetRenderTarget(p,a) (p)->GetRenderTarget(a) +#define IDirect3DDevice3_Begin(p,a,b,c) (p)->Begin(a,b,c) +#define IDirect3DDevice3_BeginIndexed(p,a,b,c,d,e) (p)->BeginIndexed(a,b,c,d,e) +#define IDirect3DDevice3_Vertex(p,a) (p)->Vertex(a) +#define IDirect3DDevice3_Index(p,a) (p)->Index(a) +#define IDirect3DDevice3_End(p,a) (p)->End(a) +#define IDirect3DDevice3_GetRenderState(p,a,b) (p)->GetRenderState(a,b) +#define IDirect3DDevice3_SetRenderState(p,a,b) (p)->SetRenderState(a,b) +#define IDirect3DDevice3_GetLightState(p,a,b) (p)->GetLightState(a,b) +#define IDirect3DDevice3_SetLightState(p,a,b) (p)->SetLightState(a,b) +#define IDirect3DDevice3_SetTransform(p,a,b) (p)->SetTransform(a,b) +#define IDirect3DDevice3_GetTransform(p,a,b) (p)->GetTransform(a,b) +#define IDirect3DDevice3_MultiplyTransform(p,a,b) (p)->MultiplyTransform(a,b) +#define IDirect3DDevice3_DrawPrimitive(p,a,b,c,d,e) (p)->DrawPrimitive(a,b,c,d,e) +#define IDirect3DDevice3_DrawIndexedPrimitive(p,a,b,c,d,e,f,g) (p)->DrawIndexedPrimitive(a,b,c,d,e,f,g) +#define IDirect3DDevice3_SetClipStatus(p,a) (p)->SetClipStatus(a) +#define IDirect3DDevice3_GetClipStatus(p,a) (p)->GetClipStatus(a) +#define IDirect3DDevice3_DrawPrimitiveStrided(p,a,b,c,d,e) (p)->DrawPrimitiveStrided(a,b,c,d,e) +#define IDirect3DDevice3_DrawIndexedPrimitiveStrided(p,a,b,c,d,e,f,g) (p)->DrawIndexedPrimitiveStrided(a,b,c,d,e,f,g) +#define IDirect3DDevice3_DrawPrimitiveVB(p,a,b,c,d,e) (p)->DrawPrimitiveVB(a,b,c,d,e) +#define IDirect3DDevice3_DrawIndexedPrimitiveVB(p,a,b,c,d,e) (p)->DrawIndexedPrimitiveVB(a,b,c,d,e) +#define IDirect3DDevice3_ComputeSphereVisibility(p,a,b,c,d,e) (p)->ComputeSphereVisibility(a,b,c,d,e) +#define IDirect3DDevice3_GetTexture(p,a,b) (p)->GetTexture(a,b) +#define IDirect3DDevice3_SetTexture(p,a,b) (p)->SetTexture(a,b) +#define IDirect3DDevice3_GetTextureStageState(p,a,b,c) (p)->GetTextureStageState(a,b,c) +#define IDirect3DDevice3_SetTextureStageState(p,a,b,c) (p)->SetTextureStageState(a,b,c) +#define IDirect3DDevice3_ValidateDevice(p,a) (p)->ValidateDevice(a) +#endif + +/* + * Execute Buffer interface + */ +#undef INTERFACE +#define INTERFACE IDirect3DExecuteBuffer + +DECLARE_INTERFACE_(IDirect3DExecuteBuffer, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DExecuteBuffer methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3DDEVICE,LPD3DEXECUTEBUFFERDESC) PURE; + STDMETHOD(Lock)(THIS_ LPD3DEXECUTEBUFFERDESC) PURE; + STDMETHOD(Unlock)(THIS) PURE; + STDMETHOD(SetExecuteData)(THIS_ LPD3DEXECUTEDATA) PURE; + STDMETHOD(GetExecuteData)(THIS_ LPD3DEXECUTEDATA) PURE; + STDMETHOD(Validate)(THIS_ LPDWORD,LPD3DVALIDATECALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(Optimize)(THIS_ DWORD) PURE; +}; + +typedef struct IDirect3DExecuteBuffer *LPDIRECT3DEXECUTEBUFFER; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DExecuteBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DExecuteBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DExecuteBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DExecuteBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirect3DExecuteBuffer_Lock(p,a) (p)->lpVtbl->Lock(p,a) +#define IDirect3DExecuteBuffer_Unlock(p) (p)->lpVtbl->Unlock(p) +#define IDirect3DExecuteBuffer_SetExecuteData(p,a) (p)->lpVtbl->SetExecuteData(p,a) +#define IDirect3DExecuteBuffer_GetExecuteData(p,a) (p)->lpVtbl->GetExecuteData(p,a) +#define IDirect3DExecuteBuffer_Validate(p,a,b,c,d) (p)->lpVtbl->Validate(p,a,b,c,d) +#define IDirect3DExecuteBuffer_Optimize(p,a) (p)->lpVtbl->Optimize(p,a) +#else +#define IDirect3DExecuteBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DExecuteBuffer_AddRef(p) (p)->AddRef() +#define IDirect3DExecuteBuffer_Release(p) (p)->Release() +#define IDirect3DExecuteBuffer_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirect3DExecuteBuffer_Lock(p,a) (p)->Lock(a) +#define IDirect3DExecuteBuffer_Unlock(p) (p)->Unlock() +#define IDirect3DExecuteBuffer_SetExecuteData(p,a) (p)->SetExecuteData(a) +#define IDirect3DExecuteBuffer_GetExecuteData(p,a) (p)->GetExecuteData(a) +#define IDirect3DExecuteBuffer_Validate(p,a,b,c,d) (p)->Validate(a,b,c,d) +#define IDirect3DExecuteBuffer_Optimize(p,a) (p)->Optimize(a) +#endif + +/* + * Light interfaces + */ +#undef INTERFACE +#define INTERFACE IDirect3DLight + +DECLARE_INTERFACE_(IDirect3DLight, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DLight methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3D) PURE; + STDMETHOD(SetLight)(THIS_ LPD3DLIGHT) PURE; + STDMETHOD(GetLight)(THIS_ LPD3DLIGHT) PURE; +}; + +typedef struct IDirect3DLight *LPDIRECT3DLIGHT; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DLight_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DLight_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DLight_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DLight_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirect3DLight_SetLight(p,a) (p)->lpVtbl->SetLight(p,a) +#define IDirect3DLight_GetLight(p,a) (p)->lpVtbl->GetLight(p,a) +#else +#define IDirect3DLight_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DLight_AddRef(p) (p)->AddRef() +#define IDirect3DLight_Release(p) (p)->Release() +#define IDirect3DLight_Initialize(p,a) (p)->Initialize(a) +#define IDirect3DLight_SetLight(p,a) (p)->SetLight(a) +#define IDirect3DLight_GetLight(p,a) (p)->GetLight(a) +#endif + +/* + * Material interfaces + */ +#undef INTERFACE +#define INTERFACE IDirect3DMaterial + +DECLARE_INTERFACE_(IDirect3DMaterial, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DMaterial methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3D) PURE; + STDMETHOD(SetMaterial)(THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetMaterial)(THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetHandle)(THIS_ LPDIRECT3DDEVICE,LPD3DMATERIALHANDLE) PURE; + STDMETHOD(Reserve)(THIS) PURE; + STDMETHOD(Unreserve)(THIS) PURE; +}; + +typedef struct IDirect3DMaterial *LPDIRECT3DMATERIAL; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DMaterial_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DMaterial_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DMaterial_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DMaterial_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirect3DMaterial_SetMaterial(p,a) (p)->lpVtbl->SetMaterial(p,a) +#define IDirect3DMaterial_GetMaterial(p,a) (p)->lpVtbl->GetMaterial(p,a) +#define IDirect3DMaterial_GetHandle(p,a,b) (p)->lpVtbl->GetHandle(p,a,b) +#define IDirect3DMaterial_Reserve(p) (p)->lpVtbl->Reserve(p) +#define IDirect3DMaterial_Unreserve(p) (p)->lpVtbl->Unreserve(p) +#else +#define IDirect3DMaterial_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DMaterial_AddRef(p) (p)->AddRef() +#define IDirect3DMaterial_Release(p) (p)->Release() +#define IDirect3DMaterial_Initialize(p,a) (p)->Initialize(a) +#define IDirect3DMaterial_SetMaterial(p,a) (p)->SetMaterial(a) +#define IDirect3DMaterial_GetMaterial(p,a) (p)->GetMaterial(a) +#define IDirect3DMaterial_GetHandle(p,a,b) (p)->GetHandle(a,b) +#define IDirect3DMaterial_Reserve(p) (p)->Reserve() +#define IDirect3DMaterial_Unreserve(p) (p)->Unreserve() +#endif + +#undef INTERFACE +#define INTERFACE IDirect3DMaterial2 + +DECLARE_INTERFACE_(IDirect3DMaterial2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DMaterial2 methods ***/ + STDMETHOD(SetMaterial)(THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetMaterial)(THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetHandle)(THIS_ LPDIRECT3DDEVICE2,LPD3DMATERIALHANDLE) PURE; +}; + +typedef struct IDirect3DMaterial2 *LPDIRECT3DMATERIAL2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DMaterial2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DMaterial2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DMaterial2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DMaterial2_SetMaterial(p,a) (p)->lpVtbl->SetMaterial(p,a) +#define IDirect3DMaterial2_GetMaterial(p,a) (p)->lpVtbl->GetMaterial(p,a) +#define IDirect3DMaterial2_GetHandle(p,a,b) (p)->lpVtbl->GetHandle(p,a,b) +#else +#define IDirect3DMaterial2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DMaterial2_AddRef(p) (p)->AddRef() +#define IDirect3DMaterial2_Release(p) (p)->Release() +#define IDirect3DMaterial2_SetMaterial(p,a) (p)->SetMaterial(a) +#define IDirect3DMaterial2_GetMaterial(p,a) (p)->GetMaterial(a) +#define IDirect3DMaterial2_GetHandle(p,a,b) (p)->GetHandle(a,b) +#endif + +#undef INTERFACE +#define INTERFACE IDirect3DMaterial3 + +DECLARE_INTERFACE_(IDirect3DMaterial3, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DMaterial3 methods ***/ + STDMETHOD(SetMaterial)(THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetMaterial)(THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetHandle)(THIS_ LPDIRECT3DDEVICE3,LPD3DMATERIALHANDLE) PURE; +}; + +typedef struct IDirect3DMaterial3 *LPDIRECT3DMATERIAL3; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DMaterial3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DMaterial3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DMaterial3_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DMaterial3_SetMaterial(p,a) (p)->lpVtbl->SetMaterial(p,a) +#define IDirect3DMaterial3_GetMaterial(p,a) (p)->lpVtbl->GetMaterial(p,a) +#define IDirect3DMaterial3_GetHandle(p,a,b) (p)->lpVtbl->GetHandle(p,a,b) +#else +#define IDirect3DMaterial3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DMaterial3_AddRef(p) (p)->AddRef() +#define IDirect3DMaterial3_Release(p) (p)->Release() +#define IDirect3DMaterial3_SetMaterial(p,a) (p)->SetMaterial(a) +#define IDirect3DMaterial3_GetMaterial(p,a) (p)->GetMaterial(a) +#define IDirect3DMaterial3_GetHandle(p,a,b) (p)->GetHandle(a,b) +#endif + +/* + * Texture interfaces + */ +#undef INTERFACE +#define INTERFACE IDirect3DTexture + +DECLARE_INTERFACE_(IDirect3DTexture, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DTexture methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3DDEVICE,LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(GetHandle)(THIS_ LPDIRECT3DDEVICE,LPD3DTEXTUREHANDLE) PURE; + STDMETHOD(PaletteChanged)(THIS_ DWORD,DWORD) PURE; + STDMETHOD(Load)(THIS_ LPDIRECT3DTEXTURE) PURE; + STDMETHOD(Unload)(THIS) PURE; +}; + +typedef struct IDirect3DTexture *LPDIRECT3DTEXTURE; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DTexture_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DTexture_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DTexture_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DTexture_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirect3DTexture_GetHandle(p,a,b) (p)->lpVtbl->GetHandle(p,a,b) +#define IDirect3DTexture_PaletteChanged(p,a,b) (p)->lpVtbl->PaletteChanged(p,a,b) +#define IDirect3DTexture_Load(p,a) (p)->lpVtbl->Load(p,a) +#define IDirect3DTexture_Unload(p) (p)->lpVtbl->Unload(p) +#else +#define IDirect3DTexture_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DTexture_AddRef(p) (p)->AddRef() +#define IDirect3DTexture_Release(p) (p)->Release() +#define IDirect3DTexture_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirect3DTexture_GetHandle(p,a,b) (p)->GetHandle(a,b) +#define IDirect3DTexture_PaletteChanged(p,a,b) (p)->PaletteChanged(a,b) +#define IDirect3DTexture_Load(p,a) (p)->Load(a) +#define IDirect3DTexture_Unload(p) (p)->Unload() +#endif + +#undef INTERFACE +#define INTERFACE IDirect3DTexture2 + +DECLARE_INTERFACE_(IDirect3DTexture2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DTexture2 methods ***/ + STDMETHOD(GetHandle)(THIS_ LPDIRECT3DDEVICE2,LPD3DTEXTUREHANDLE) PURE; + STDMETHOD(PaletteChanged)(THIS_ DWORD,DWORD) PURE; + STDMETHOD(Load)(THIS_ LPDIRECT3DTEXTURE2) PURE; +}; + +typedef struct IDirect3DTexture2 *LPDIRECT3DTEXTURE2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DTexture2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DTexture2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DTexture2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DTexture2_GetHandle(p,a,b) (p)->lpVtbl->GetHandle(p,a,b) +#define IDirect3DTexture2_PaletteChanged(p,a,b) (p)->lpVtbl->PaletteChanged(p,a,b) +#define IDirect3DTexture2_Load(p,a) (p)->lpVtbl->Load(p,a) +#else +#define IDirect3DTexture2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DTexture2_AddRef(p) (p)->AddRef() +#define IDirect3DTexture2_Release(p) (p)->Release() +#define IDirect3DTexture2_GetHandle(p,a,b) (p)->GetHandle(a,b) +#define IDirect3DTexture2_PaletteChanged(p,a,b) (p)->PaletteChanged(a,b) +#define IDirect3DTexture2_Load(p,a) (p)->Load(a) +#endif + +/* + * Viewport interfaces + */ +#undef INTERFACE +#define INTERFACE IDirect3DViewport + +DECLARE_INTERFACE_(IDirect3DViewport, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DViewport methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3D) PURE; + STDMETHOD(GetViewport)(THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(SetViewport)(THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(TransformVertices)(THIS_ DWORD,LPD3DTRANSFORMDATA,DWORD,LPDWORD) PURE; + STDMETHOD(LightElements)(THIS_ DWORD,LPD3DLIGHTDATA) PURE; + STDMETHOD(SetBackground)(THIS_ D3DMATERIALHANDLE) PURE; + STDMETHOD(GetBackground)(THIS_ LPD3DMATERIALHANDLE,LPBOOL) PURE; + STDMETHOD(SetBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(GetBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE*,LPBOOL) PURE; + STDMETHOD(Clear)(THIS_ DWORD,LPD3DRECT,DWORD) PURE; + STDMETHOD(AddLight)(THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(DeleteLight)(THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(NextLight)(THIS_ LPDIRECT3DLIGHT,LPDIRECT3DLIGHT*,DWORD) PURE; +}; + +typedef struct IDirect3DViewport *LPDIRECT3DVIEWPORT; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DViewport_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DViewport_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DViewport_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DViewport_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirect3DViewport_GetViewport(p,a) (p)->lpVtbl->GetViewport(p,a) +#define IDirect3DViewport_SetViewport(p,a) (p)->lpVtbl->SetViewport(p,a) +#define IDirect3DViewport_TransformVertices(p,a,b,c,d) (p)->lpVtbl->TransformVertices(p,a,b,c,d) +#define IDirect3DViewport_LightElements(p,a,b) (p)->lpVtbl->LightElements(p,a,b) +#define IDirect3DViewport_SetBackground(p,a) (p)->lpVtbl->SetBackground(p,a) +#define IDirect3DViewport_GetBackground(p,a,b) (p)->lpVtbl->GetBackground(p,a,b) +#define IDirect3DViewport_SetBackgroundDepth(p,a) (p)->lpVtbl->SetBackgroundDepth(p,a) +#define IDirect3DViewport_GetBackgroundDepth(p,a,b) (p)->lpVtbl->GetBackgroundDepth(p,a,b) +#define IDirect3DViewport_Clear(p,a,b,c) (p)->lpVtbl->Clear(p,a,b,c) +#define IDirect3DViewport_AddLight(p,a) (p)->lpVtbl->AddLight(p,a) +#define IDirect3DViewport_DeleteLight(p,a) (p)->lpVtbl->DeleteLight(p,a) +#define IDirect3DViewport_NextLight(p,a,b,c) (p)->lpVtbl->NextLight(p,a,b,c) +#else +#define IDirect3DViewport_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DViewport_AddRef(p) (p)->AddRef() +#define IDirect3DViewport_Release(p) (p)->Release() +#define IDirect3DViewport_Initialize(p,a) (p)->Initialize(a) +#define IDirect3DViewport_GetViewport(p,a) (p)->GetViewport(a) +#define IDirect3DViewport_SetViewport(p,a) (p)->SetViewport(a) +#define IDirect3DViewport_TransformVertices(p,a,b,c,d) (p)->TransformVertices(a,b,c,d) +#define IDirect3DViewport_LightElements(p,a,b) (p)->LightElements(a,b) +#define IDirect3DViewport_SetBackground(p,a) (p)->SetBackground(a) +#define IDirect3DViewport_GetBackground(p,a,b) (p)->GetBackground(a,b) +#define IDirect3DViewport_SetBackgroundDepth(p,a) (p)->SetBackgroundDepth(a) +#define IDirect3DViewport_GetBackgroundDepth(p,a,b) (p)->GetBackgroundDepth(a,b) +#define IDirect3DViewport_Clear(p,a,b,c) (p)->Clear(a,b,c) +#define IDirect3DViewport_AddLight(p,a) (p)->AddLight(a) +#define IDirect3DViewport_DeleteLight(p,a) (p)->DeleteLight(a) +#define IDirect3DViewport_NextLight(p,a,b,c) (p)->NextLight(a,b,c) +#endif + +#undef INTERFACE +#define INTERFACE IDirect3DViewport2 + +DECLARE_INTERFACE_(IDirect3DViewport2, IDirect3DViewport) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DViewport methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3D) PURE; + STDMETHOD(GetViewport)(THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(SetViewport)(THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(TransformVertices)(THIS_ DWORD,LPD3DTRANSFORMDATA,DWORD,LPDWORD) PURE; + STDMETHOD(LightElements)(THIS_ DWORD,LPD3DLIGHTDATA) PURE; + STDMETHOD(SetBackground)(THIS_ D3DMATERIALHANDLE) PURE; + STDMETHOD(GetBackground)(THIS_ LPD3DMATERIALHANDLE,LPBOOL) PURE; + STDMETHOD(SetBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(GetBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE*,LPBOOL) PURE; + STDMETHOD(Clear)(THIS_ DWORD,LPD3DRECT,DWORD) PURE; + STDMETHOD(AddLight)(THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(DeleteLight)(THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(NextLight)(THIS_ LPDIRECT3DLIGHT,LPDIRECT3DLIGHT*,DWORD) PURE; + STDMETHOD(GetViewport2)(THIS_ LPD3DVIEWPORT2) PURE; + STDMETHOD(SetViewport2)(THIS_ LPD3DVIEWPORT2) PURE; +}; + +typedef struct IDirect3DViewport2 *LPDIRECT3DVIEWPORT2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DViewport2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DViewport2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DViewport2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DViewport2_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirect3DViewport2_GetViewport(p,a) (p)->lpVtbl->GetViewport(p,a) +#define IDirect3DViewport2_SetViewport(p,a) (p)->lpVtbl->SetViewport(p,a) +#define IDirect3DViewport2_TransformVertices(p,a,b,c,d) (p)->lpVtbl->TransformVertices(p,a,b,c,d) +#define IDirect3DViewport2_LightElements(p,a,b) (p)->lpVtbl->LightElements(p,a,b) +#define IDirect3DViewport2_SetBackground(p,a) (p)->lpVtbl->SetBackground(p,a) +#define IDirect3DViewport2_GetBackground(p,a,b) (p)->lpVtbl->GetBackground(p,a,b) +#define IDirect3DViewport2_SetBackgroundDepth(p,a) (p)->lpVtbl->SetBackgroundDepth(p,a) +#define IDirect3DViewport2_GetBackgroundDepth(p,a,b) (p)->lpVtbl->GetBackgroundDepth(p,a,b) +#define IDirect3DViewport2_Clear(p,a,b,c) (p)->lpVtbl->Clear(p,a,b,c) +#define IDirect3DViewport2_AddLight(p,a) (p)->lpVtbl->AddLight(p,a) +#define IDirect3DViewport2_DeleteLight(p,a) (p)->lpVtbl->DeleteLight(p,a) +#define IDirect3DViewport2_NextLight(p,a,b,c) (p)->lpVtbl->NextLight(p,a,b,c) +#define IDirect3DViewport2_GetViewport2(p,a) (p)->lpVtbl->GetViewport2(p,a) +#define IDirect3DViewport2_SetViewport2(p,a) (p)->lpVtbl->SetViewport2(p,a) +#else +#define IDirect3DViewport2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DViewport2_AddRef(p) (p)->AddRef() +#define IDirect3DViewport2_Release(p) (p)->Release() +#define IDirect3DViewport2_Initialize(p,a) (p)->Initialize(a) +#define IDirect3DViewport2_GetViewport(p,a) (p)->GetViewport(a) +#define IDirect3DViewport2_SetViewport(p,a) (p)->SetViewport(a) +#define IDirect3DViewport2_TransformVertices(p,a,b,c,d) (p)->TransformVertices(a,b,c,d) +#define IDirect3DViewport2_LightElements(p,a,b) (p)->LightElements(a,b) +#define IDirect3DViewport2_SetBackground(p,a) (p)->SetBackground(a) +#define IDirect3DViewport2_GetBackground(p,a,b) (p)->GetBackground(a,b) +#define IDirect3DViewport2_SetBackgroundDepth(p,a) (p)->SetBackgroundDepth(a) +#define IDirect3DViewport2_GetBackgroundDepth(p,a,b) (p)->GetBackgroundDepth(a,b) +#define IDirect3DViewport2_Clear(p,a,b,c) (p)->Clear(a,b,c) +#define IDirect3DViewport2_AddLight(p,a) (p)->AddLight(a) +#define IDirect3DViewport2_DeleteLight(p,a) (p)->DeleteLight(a) +#define IDirect3DViewport2_NextLight(p,a,b,c) (p)->NextLight(a,b,c) +#define IDirect3DViewport2_GetViewport2(p,a) (p)->GetViewport2(a) +#define IDirect3DViewport2_SetViewport2(p,a) (p)->SetViewport2(a) +#endif + + +#undef INTERFACE +#define INTERFACE IDirect3DViewport3 + +DECLARE_INTERFACE_(IDirect3DViewport3, IDirect3DViewport2) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DViewport2 methods ***/ + STDMETHOD(Initialize)(THIS_ LPDIRECT3D) PURE; + STDMETHOD(GetViewport)(THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(SetViewport)(THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(TransformVertices)(THIS_ DWORD,LPD3DTRANSFORMDATA,DWORD,LPDWORD) PURE; + STDMETHOD(LightElements)(THIS_ DWORD,LPD3DLIGHTDATA) PURE; + STDMETHOD(SetBackground)(THIS_ D3DMATERIALHANDLE) PURE; + STDMETHOD(GetBackground)(THIS_ LPD3DMATERIALHANDLE,LPBOOL) PURE; + STDMETHOD(SetBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(GetBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE*,LPBOOL) PURE; + STDMETHOD(Clear)(THIS_ DWORD,LPD3DRECT,DWORD) PURE; + STDMETHOD(AddLight)(THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(DeleteLight)(THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(NextLight)(THIS_ LPDIRECT3DLIGHT,LPDIRECT3DLIGHT*,DWORD) PURE; + STDMETHOD(GetViewport2)(THIS_ LPD3DVIEWPORT2) PURE; + STDMETHOD(SetViewport2)(THIS_ LPD3DVIEWPORT2) PURE; + STDMETHOD(SetBackgroundDepth2)(THIS_ LPDIRECTDRAWSURFACE4) PURE; + STDMETHOD(GetBackgroundDepth2)(THIS_ LPDIRECTDRAWSURFACE4*,LPBOOL) PURE; + STDMETHOD(Clear2)(THIS_ DWORD,LPD3DRECT,DWORD,D3DCOLOR,D3DVALUE,DWORD) PURE; +}; + +typedef struct IDirect3DViewport3 *LPDIRECT3DVIEWPORT3; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DViewport3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DViewport3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DViewport3_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DViewport3_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirect3DViewport3_GetViewport(p,a) (p)->lpVtbl->GetViewport(p,a) +#define IDirect3DViewport3_SetViewport(p,a) (p)->lpVtbl->SetViewport(p,a) +#define IDirect3DViewport3_TransformVertices(p,a,b,c,d) (p)->lpVtbl->TransformVertices(p,a,b,c,d) +#define IDirect3DViewport3_LightElements(p,a,b) (p)->lpVtbl->LightElements(p,a,b) +#define IDirect3DViewport3_SetBackground(p,a) (p)->lpVtbl->SetBackground(p,a) +#define IDirect3DViewport3_GetBackground(p,a,b) (p)->lpVtbl->GetBackground(p,a,b) +#define IDirect3DViewport3_SetBackgroundDepth(p,a) (p)->lpVtbl->SetBackgroundDepth(p,a) +#define IDirect3DViewport3_GetBackgroundDepth(p,a,b) (p)->lpVtbl->GetBackgroundDepth(p,a,b) +#define IDirect3DViewport3_Clear(p,a,b,c) (p)->lpVtbl->Clear(p,a,b,c) +#define IDirect3DViewport3_AddLight(p,a) (p)->lpVtbl->AddLight(p,a) +#define IDirect3DViewport3_DeleteLight(p,a) (p)->lpVtbl->DeleteLight(p,a) +#define IDirect3DViewport3_NextLight(p,a,b,c) (p)->lpVtbl->NextLight(p,a,b,c) +#define IDirect3DViewport3_GetViewport2(p,a) (p)->lpVtbl->GetViewport2(p,a) +#define IDirect3DViewport3_SetViewport2(p,a) (p)->lpVtbl->SetViewport2(p,a) +#define IDirect3DViewport3_SetBackgroundDepth2(p,a) (p)->lpVtbl->SetBackgroundDepth2(p,a) +#define IDirect3DViewport3_GetBackgroundDepth2(p,a,b) (p)->lpVtbl->GetBackgroundDepth2(p,a,b) +#define IDirect3DViewport3_Clear2(p,a,b,c,d,e,f) (p)->lpVtbl->Clear2(p,a,b,c,d,e,f) +#else +#define IDirect3DViewport3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DViewport3_AddRef(p) (p)->AddRef() +#define IDirect3DViewport3_Release(p) (p)->Release() +#define IDirect3DViewport3_Initialize(p,a) (p)->Initialize(a) +#define IDirect3DViewport3_GetViewport(p,a) (p)->GetViewport(a) +#define IDirect3DViewport3_SetViewport(p,a) (p)->SetViewport(a) +#define IDirect3DViewport3_TransformVertices(p,a,b,c,d) (p)->TransformVertices(a,b,c,d) +#define IDirect3DViewport3_LightElements(p,a,b) (p)->LightElements(a,b) +#define IDirect3DViewport3_SetBackground(p,a) (p)->SetBackground(a) +#define IDirect3DViewport3_GetBackground(p,a,b) (p)->GetBackground(a,b) +#define IDirect3DViewport3_SetBackgroundDepth(p,a) (p)->SetBackgroundDepth(a) +#define IDirect3DViewport3_GetBackgroundDepth(p,a,b) (p)->GetBackgroundDepth(a,b) +#define IDirect3DViewport3_Clear(p,a,b,c) (p)->Clear(a,b,c) +#define IDirect3DViewport3_AddLight(p,a) (p)->AddLight(a) +#define IDirect3DViewport3_DeleteLight(p,a) (p)->DeleteLight(a) +#define IDirect3DViewport3_NextLight(p,a,b,c) (p)->NextLight(a,b,c) +#define IDirect3DViewport3_GetViewport2(p,a) (p)->GetViewport2(a) +#define IDirect3DViewport3_SetViewport2(p,a) (p)->SetViewport2(a) +#define IDirect3DViewport3_SetBackgroundDepth2(p,a) (p)->SetBackgroundDepth2(a) +#define IDirect3DViewport3_GetBackgroundDepth2(p,a,b) (p)->GetBackgroundDepth2(a,b) +#define IDirect3DViewport3_Clear2(p,a,b,c,d,e,f) (p)->Clear2(a,b,c,d,e,f) +#endif + +#undef INTERFACE +#define INTERFACE IDirect3DVertexBuffer + +DECLARE_INTERFACE_(IDirect3DVertexBuffer, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DVertexBuffer methods ***/ + STDMETHOD(Lock)(THIS_ DWORD,LPVOID*,LPDWORD) PURE; + STDMETHOD(Unlock)(THIS) PURE; + STDMETHOD(ProcessVertices)(THIS_ DWORD,DWORD,DWORD,LPDIRECT3DVERTEXBUFFER,DWORD,LPDIRECT3DDEVICE3,DWORD) PURE; + STDMETHOD(GetVertexBufferDesc)(THIS_ LPD3DVERTEXBUFFERDESC) PURE; + STDMETHOD(Optimize)(THIS_ LPDIRECT3DDEVICE3,DWORD) PURE; +}; + +typedef struct IDirect3DVertexBuffer *LPDIRECT3DVERTEXBUFFER; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DVertexBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DVertexBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DVertexBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DVertexBuffer_Lock(p,a,b,c) (p)->lpVtbl->Lock(p,a,b,c) +#define IDirect3DVertexBuffer_Unlock(p) (p)->lpVtbl->Unlock(p) +#define IDirect3DVertexBuffer_ProcessVertices(p,a,b,c,d,e,f,g) (p)->lpVtbl->ProcessVertices(p,a,b,c,d,e,f,g) +#define IDirect3DVertexBuffer_GetVertexBufferDesc(p,a) (p)->lpVtbl->GetVertexBufferDesc(p,a) +#define IDirect3DVertexBuffer_Optimize(p,a,b) (p)->lpVtbl->Optimize(p,a,b) +#else +#define IDirect3DVertexBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DVertexBuffer_AddRef(p) (p)->AddRef() +#define IDirect3DVertexBuffer_Release(p) (p)->Release() +#define IDirect3DVertexBuffer_Lock(p,a,b,c) (p)->Lock(a,b,c) +#define IDirect3DVertexBuffer_Unlock(p) (p)->Unlock() +#define IDirect3DVertexBuffer_ProcessVertices(p,a,b,c,d,e,f,g) (p)->ProcessVertices(a,b,c,d,e,f,g) +#define IDirect3DVertexBuffer_GetVertexBufferDesc(p,a) (p)->GetVertexBufferDesc(a) +#define IDirect3DVertexBuffer_Optimize(p,a,b) (p)->Optimize(a,b) +#endif + +/**************************************************************************** + * + * Flags for IDirect3DDevice::NextViewport + * + ****************************************************************************/ + +/* + * Return the next viewport + */ +#define D3DNEXT_NEXT 0x00000001l + +/* + * Return the first viewport + */ +#define D3DNEXT_HEAD 0x00000002l + +/* + * Return the last viewport + */ +#define D3DNEXT_TAIL 0x00000004l + + +/**************************************************************************** + * + * Flags for DrawPrimitive/DrawIndexedPrimitive + * Also valid for Begin/BeginIndexed + * Also valid for VertexBuffer::CreateVertexBuffer + ****************************************************************************/ + +/* + * Wait until the device is ready to draw the primitive + * This will cause DP to not return DDERR_WASSTILLDRAWING + */ +#define D3DDP_WAIT 0x00000001l + +#if (DIRECT3D_VERSION == 0x0500) +/* + * Hint that it is acceptable to render the primitive out of order. + */ +#define D3DDP_OUTOFORDER 0x00000002l +#endif + + +/* + * Hint that the primitives have been clipped by the application. + */ +#define D3DDP_DONOTCLIP 0x00000004l + +/* + * Hint that the extents need not be updated. + */ +#define D3DDP_DONOTUPDATEEXTENTS 0x00000008l + + +/* + * Hint that the lighting should not be applied on vertices. + */ + +#define D3DDP_DONOTLIGHT 0x00000010l + + +/* + * Direct3D Errors + * DirectDraw error codes are used when errors not specified here. + */ +#define D3D_OK DD_OK +#define D3DERR_BADMAJORVERSION MAKE_DDHRESULT(700) +#define D3DERR_BADMINORVERSION MAKE_DDHRESULT(701) + +/* + * An invalid device was requested by the application. + */ +#define D3DERR_INVALID_DEVICE MAKE_DDHRESULT(705) +#define D3DERR_INITFAILED MAKE_DDHRESULT(706) + +/* + * SetRenderTarget attempted on a device that was + * QI'd off the render target. + */ +#define D3DERR_DEVICEAGGREGATED MAKE_DDHRESULT(707) + +#define D3DERR_EXECUTE_CREATE_FAILED MAKE_DDHRESULT(710) +#define D3DERR_EXECUTE_DESTROY_FAILED MAKE_DDHRESULT(711) +#define D3DERR_EXECUTE_LOCK_FAILED MAKE_DDHRESULT(712) +#define D3DERR_EXECUTE_UNLOCK_FAILED MAKE_DDHRESULT(713) +#define D3DERR_EXECUTE_LOCKED MAKE_DDHRESULT(714) +#define D3DERR_EXECUTE_NOT_LOCKED MAKE_DDHRESULT(715) + +#define D3DERR_EXECUTE_FAILED MAKE_DDHRESULT(716) +#define D3DERR_EXECUTE_CLIPPED_FAILED MAKE_DDHRESULT(717) + +#define D3DERR_TEXTURE_NO_SUPPORT MAKE_DDHRESULT(720) +#define D3DERR_TEXTURE_CREATE_FAILED MAKE_DDHRESULT(721) +#define D3DERR_TEXTURE_DESTROY_FAILED MAKE_DDHRESULT(722) +#define D3DERR_TEXTURE_LOCK_FAILED MAKE_DDHRESULT(723) +#define D3DERR_TEXTURE_UNLOCK_FAILED MAKE_DDHRESULT(724) +#define D3DERR_TEXTURE_LOAD_FAILED MAKE_DDHRESULT(725) +#define D3DERR_TEXTURE_SWAP_FAILED MAKE_DDHRESULT(726) +#define D3DERR_TEXTURE_LOCKED MAKE_DDHRESULT(727) +#define D3DERR_TEXTURE_NOT_LOCKED MAKE_DDHRESULT(728) +#define D3DERR_TEXTURE_GETSURF_FAILED MAKE_DDHRESULT(729) + +#define D3DERR_MATRIX_CREATE_FAILED MAKE_DDHRESULT(730) +#define D3DERR_MATRIX_DESTROY_FAILED MAKE_DDHRESULT(731) +#define D3DERR_MATRIX_SETDATA_FAILED MAKE_DDHRESULT(732) +#define D3DERR_MATRIX_GETDATA_FAILED MAKE_DDHRESULT(733) +#define D3DERR_SETVIEWPORTDATA_FAILED MAKE_DDHRESULT(734) + +#define D3DERR_INVALIDCURRENTVIEWPORT MAKE_DDHRESULT(735) +#define D3DERR_INVALIDPRIMITIVETYPE MAKE_DDHRESULT(736) +#define D3DERR_INVALIDVERTEXTYPE MAKE_DDHRESULT(737) +#define D3DERR_TEXTURE_BADSIZE MAKE_DDHRESULT(738) +#define D3DERR_INVALIDRAMPTEXTURE MAKE_DDHRESULT(739) + +#define D3DERR_MATERIAL_CREATE_FAILED MAKE_DDHRESULT(740) +#define D3DERR_MATERIAL_DESTROY_FAILED MAKE_DDHRESULT(741) +#define D3DERR_MATERIAL_SETDATA_FAILED MAKE_DDHRESULT(742) +#define D3DERR_MATERIAL_GETDATA_FAILED MAKE_DDHRESULT(743) + +#define D3DERR_INVALIDPALETTE MAKE_DDHRESULT(744) + +#define D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY MAKE_DDHRESULT(745) +#define D3DERR_ZBUFF_NEEDS_VIDEOMEMORY MAKE_DDHRESULT(746) +#define D3DERR_SURFACENOTINVIDMEM MAKE_DDHRESULT(747) + +#define D3DERR_LIGHT_SET_FAILED MAKE_DDHRESULT(750) +#define D3DERR_LIGHTHASVIEWPORT MAKE_DDHRESULT(751) +#define D3DERR_LIGHTNOTINTHISVIEWPORT MAKE_DDHRESULT(752) + +#define D3DERR_SCENE_IN_SCENE MAKE_DDHRESULT(760) +#define D3DERR_SCENE_NOT_IN_SCENE MAKE_DDHRESULT(761) +#define D3DERR_SCENE_BEGIN_FAILED MAKE_DDHRESULT(762) +#define D3DERR_SCENE_END_FAILED MAKE_DDHRESULT(763) + +#define D3DERR_INBEGIN MAKE_DDHRESULT(770) +#define D3DERR_NOTINBEGIN MAKE_DDHRESULT(771) +#define D3DERR_NOVIEWPORTS MAKE_DDHRESULT(772) +#define D3DERR_VIEWPORTDATANOTSET MAKE_DDHRESULT(773) +#define D3DERR_VIEWPORTHASNODEVICE MAKE_DDHRESULT(774) +#define D3DERR_NOCURRENTVIEWPORT MAKE_DDHRESULT(775) + +#define D3DERR_INVALIDVERTEXFORMAT MAKE_DDHRESULT(2048) + +/* + * Attempted to CreateTexture on a surface that had a color key + */ +#define D3DERR_COLORKEYATTACHED MAKE_DDHRESULT(2050) + +#define D3DERR_VERTEXBUFFEROPTIMIZED MAKE_DDHRESULT(2060) +#define D3DERR_VBUF_CREATE_FAILED MAKE_DDHRESULT(2061) +#define D3DERR_VERTEXBUFFERLOCKED MAKE_DDHRESULT(2062) + +#define D3DERR_ZBUFFER_NOTPRESENT MAKE_DDHRESULT(2070) +#define D3DERR_STENCILBUFFER_NOTPRESENT MAKE_DDHRESULT(2071) + +#define D3DERR_WRONGTEXTUREFORMAT MAKE_DDHRESULT(2072) +#define D3DERR_UNSUPPORTEDCOLOROPERATION MAKE_DDHRESULT(2073) +#define D3DERR_UNSUPPORTEDCOLORARG MAKE_DDHRESULT(2074) +#define D3DERR_UNSUPPORTEDALPHAOPERATION MAKE_DDHRESULT(2075) +#define D3DERR_UNSUPPORTEDALPHAARG MAKE_DDHRESULT(2076) +#define D3DERR_TOOMANYOPERATIONS MAKE_DDHRESULT(2077) +#define D3DERR_CONFLICTINGTEXTUREFILTER MAKE_DDHRESULT(2078) +#define D3DERR_UNSUPPORTEDFACTORVALUE MAKE_DDHRESULT(2079) +#define D3DERR_CONFLICTINGRENDERSTATE MAKE_DDHRESULT(2081) +#define D3DERR_UNSUPPORTEDTEXTUREFILTER MAKE_DDHRESULT(2082) +#define D3DERR_TOOMANYPRIMITIVES MAKE_DDHRESULT(2083) +#define D3DERR_INVALIDMATRIX MAKE_DDHRESULT(2084) +#define D3DERR_TOOMANYVERTICES MAKE_DDHRESULT(2085) +#define D3DERR_CONFLICTINGTEXTUREPALETTE MAKE_DDHRESULT(2086) + + + +#ifdef __cplusplus +}; +#endif + +#endif /* _D3D_H_ */ + diff --git a/lib/win/DirectX/d3dcaps.h b/lib/win/DirectX/d3dcaps.h new file mode 100644 index 000000000..dbf856f93 --- /dev/null +++ b/lib/win/DirectX/d3dcaps.h @@ -0,0 +1,447 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1998 Microsoft Corporation. All Rights Reserved. + * + * File: d3dcaps.h + * Content: Direct3D capabilities include file + * + ***************************************************************************/ + +#ifndef _D3DCAPS_H +#define _D3DCAPS_H + +/* + * Pull in DirectDraw include file automatically: + */ +#include + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x0600 +#endif + +#pragma pack(4) + +/* Description of capabilities of transform */ + +typedef struct _D3DTRANSFORMCAPS { + DWORD dwSize; + DWORD dwCaps; +} D3DTRANSFORMCAPS, *LPD3DTRANSFORMCAPS; + +#define D3DTRANSFORMCAPS_CLIP 0x00000001L /* Will clip whilst transforming */ + +/* Description of capabilities of lighting */ + +typedef struct _D3DLIGHTINGCAPS { + DWORD dwSize; + DWORD dwCaps; /* Lighting caps */ + DWORD dwLightingModel; /* Lighting model - RGB or mono */ + DWORD dwNumLights; /* Number of lights that can be handled */ +} D3DLIGHTINGCAPS, *LPD3DLIGHTINGCAPS; + +#define D3DLIGHTINGMODEL_RGB 0x00000001L +#define D3DLIGHTINGMODEL_MONO 0x00000002L + +#define D3DLIGHTCAPS_POINT 0x00000001L /* Point lights supported */ +#define D3DLIGHTCAPS_SPOT 0x00000002L /* Spot lights supported */ +#define D3DLIGHTCAPS_DIRECTIONAL 0x00000004L /* Directional lights supported */ +#define D3DLIGHTCAPS_PARALLELPOINT 0x00000008L /* Parallel point lights supported */ +#if(DIRECT3D_VERSION < 0x500) +#define D3DLIGHTCAPS_GLSPOT 0x00000010L /* GL syle spot lights supported */ +#endif + +/* Description of capabilities for each primitive type */ + +typedef struct _D3DPrimCaps { + DWORD dwSize; + DWORD dwMiscCaps; /* Capability flags */ + DWORD dwRasterCaps; + DWORD dwZCmpCaps; + DWORD dwSrcBlendCaps; + DWORD dwDestBlendCaps; + DWORD dwAlphaCmpCaps; + DWORD dwShadeCaps; + DWORD dwTextureCaps; + DWORD dwTextureFilterCaps; + DWORD dwTextureBlendCaps; + DWORD dwTextureAddressCaps; + DWORD dwStippleWidth; /* maximum width and height of */ + DWORD dwStippleHeight; /* of supported stipple (up to 32x32) */ +} D3DPRIMCAPS, *LPD3DPRIMCAPS; + +/* D3DPRIMCAPS dwMiscCaps */ + +#define D3DPMISCCAPS_MASKPLANES 0x00000001L +#define D3DPMISCCAPS_MASKZ 0x00000002L +#define D3DPMISCCAPS_LINEPATTERNREP 0x00000004L +#define D3DPMISCCAPS_CONFORMANT 0x00000008L +#define D3DPMISCCAPS_CULLNONE 0x00000010L +#define D3DPMISCCAPS_CULLCW 0x00000020L +#define D3DPMISCCAPS_CULLCCW 0x00000040L + +/* D3DPRIMCAPS dwRasterCaps */ + +#define D3DPRASTERCAPS_DITHER 0x00000001L +#define D3DPRASTERCAPS_ROP2 0x00000002L +#define D3DPRASTERCAPS_XOR 0x00000004L +#define D3DPRASTERCAPS_PAT 0x00000008L +#define D3DPRASTERCAPS_ZTEST 0x00000010L +#define D3DPRASTERCAPS_SUBPIXEL 0x00000020L +#define D3DPRASTERCAPS_SUBPIXELX 0x00000040L +#define D3DPRASTERCAPS_FOGVERTEX 0x00000080L +#define D3DPRASTERCAPS_FOGTABLE 0x00000100L +#define D3DPRASTERCAPS_STIPPLE 0x00000200L +#define D3DPRASTERCAPS_ANTIALIASSORTDEPENDENT 0x00000400L +#define D3DPRASTERCAPS_ANTIALIASSORTINDEPENDENT 0x00000800L +#define D3DPRASTERCAPS_ANTIALIASEDGES 0x00001000L +#define D3DPRASTERCAPS_MIPMAPLODBIAS 0x00002000L +#define D3DPRASTERCAPS_ZBIAS 0x00004000L +#define D3DPRASTERCAPS_ZBUFFERLESSHSR 0x00008000L +#define D3DPRASTERCAPS_FOGRANGE 0x00010000L +#define D3DPRASTERCAPS_ANISOTROPY 0x00020000L +#define D3DPRASTERCAPS_WBUFFER 0x00040000L +#define D3DPRASTERCAPS_TRANSLUCENTSORTINDEPENDENT 0x00080000L +#define D3DPRASTERCAPS_WFOG 0x00100000L + +/* D3DPRIMCAPS dwZCmpCaps, dwAlphaCmpCaps */ + +#define D3DPCMPCAPS_NEVER 0x00000001L +#define D3DPCMPCAPS_LESS 0x00000002L +#define D3DPCMPCAPS_EQUAL 0x00000004L +#define D3DPCMPCAPS_LESSEQUAL 0x00000008L +#define D3DPCMPCAPS_GREATER 0x00000010L +#define D3DPCMPCAPS_NOTEQUAL 0x00000020L +#define D3DPCMPCAPS_GREATEREQUAL 0x00000040L +#define D3DPCMPCAPS_ALWAYS 0x00000080L + +/* D3DPRIMCAPS dwSourceBlendCaps, dwDestBlendCaps */ + +#define D3DPBLENDCAPS_ZERO 0x00000001L +#define D3DPBLENDCAPS_ONE 0x00000002L +#define D3DPBLENDCAPS_SRCCOLOR 0x00000004L +#define D3DPBLENDCAPS_INVSRCCOLOR 0x00000008L +#define D3DPBLENDCAPS_SRCALPHA 0x00000010L +#define D3DPBLENDCAPS_INVSRCALPHA 0x00000020L +#define D3DPBLENDCAPS_DESTALPHA 0x00000040L +#define D3DPBLENDCAPS_INVDESTALPHA 0x00000080L +#define D3DPBLENDCAPS_DESTCOLOR 0x00000100L +#define D3DPBLENDCAPS_INVDESTCOLOR 0x00000200L +#define D3DPBLENDCAPS_SRCALPHASAT 0x00000400L +#define D3DPBLENDCAPS_BOTHSRCALPHA 0x00000800L +#define D3DPBLENDCAPS_BOTHINVSRCALPHA 0x00001000L + +/* D3DPRIMCAPS dwShadeCaps */ + +#define D3DPSHADECAPS_COLORFLATMONO 0x00000001L +#define D3DPSHADECAPS_COLORFLATRGB 0x00000002L +#define D3DPSHADECAPS_COLORGOURAUDMONO 0x00000004L +#define D3DPSHADECAPS_COLORGOURAUDRGB 0x00000008L +#define D3DPSHADECAPS_COLORPHONGMONO 0x00000010L +#define D3DPSHADECAPS_COLORPHONGRGB 0x00000020L + +#define D3DPSHADECAPS_SPECULARFLATMONO 0x00000040L +#define D3DPSHADECAPS_SPECULARFLATRGB 0x00000080L +#define D3DPSHADECAPS_SPECULARGOURAUDMONO 0x00000100L +#define D3DPSHADECAPS_SPECULARGOURAUDRGB 0x00000200L +#define D3DPSHADECAPS_SPECULARPHONGMONO 0x00000400L +#define D3DPSHADECAPS_SPECULARPHONGRGB 0x00000800L + +#define D3DPSHADECAPS_ALPHAFLATBLEND 0x00001000L +#define D3DPSHADECAPS_ALPHAFLATSTIPPLED 0x00002000L +#define D3DPSHADECAPS_ALPHAGOURAUDBLEND 0x00004000L +#define D3DPSHADECAPS_ALPHAGOURAUDSTIPPLED 0x00008000L +#define D3DPSHADECAPS_ALPHAPHONGBLEND 0x00010000L +#define D3DPSHADECAPS_ALPHAPHONGSTIPPLED 0x00020000L + +#define D3DPSHADECAPS_FOGFLAT 0x00040000L +#define D3DPSHADECAPS_FOGGOURAUD 0x00080000L +#define D3DPSHADECAPS_FOGPHONG 0x00100000L + +/* D3DPRIMCAPS dwTextureCaps */ + +/* + * Perspective-correct texturing is supported + */ +#define D3DPTEXTURECAPS_PERSPECTIVE 0x00000001L + +/* + * Power-of-2 texture dimensions are required + */ +#define D3DPTEXTURECAPS_POW2 0x00000002L + +/* + * Alpha in texture pixels is supported + */ +#define D3DPTEXTURECAPS_ALPHA 0x00000004L + +/* + * Color-keyed textures are supported + */ +#define D3DPTEXTURECAPS_TRANSPARENCY 0x00000008L + +/* + * obsolete, see D3DPTADDRESSCAPS_BORDER + */ +#define D3DPTEXTURECAPS_BORDER 0x00000010L + +/* + * Only square textures are supported + */ +#define D3DPTEXTURECAPS_SQUAREONLY 0x00000020L + +/* + * Texture indices are not scaled by the texture size prior + * to interpolation. + */ +#define D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE 0x00000040L + +/* + * Device can draw alpha from texture palettes + */ +#define D3DPTEXTURECAPS_ALPHAPALETTE 0x00000080L + + +/* D3DPRIMCAPS dwTextureFilterCaps */ + +#define D3DPTFILTERCAPS_NEAREST 0x00000001L +#define D3DPTFILTERCAPS_LINEAR 0x00000002L +#define D3DPTFILTERCAPS_MIPNEAREST 0x00000004L +#define D3DPTFILTERCAPS_MIPLINEAR 0x00000008L +#define D3DPTFILTERCAPS_LINEARMIPNEAREST 0x00000010L +#define D3DPTFILTERCAPS_LINEARMIPLINEAR 0x00000020L + +/* Device3 Min Filter */ +#define D3DPTFILTERCAPS_MINFPOINT 0x00000100L +#define D3DPTFILTERCAPS_MINFLINEAR 0x00000200L +#define D3DPTFILTERCAPS_MINFANISOTROPIC 0x00000400L + +/* Device3 Mip Filter */ +#define D3DPTFILTERCAPS_MIPFPOINT 0x00010000L +#define D3DPTFILTERCAPS_MIPFLINEAR 0x00020000L + +/* Device3 Mag Filter */ +#define D3DPTFILTERCAPS_MAGFPOINT 0x01000000L +#define D3DPTFILTERCAPS_MAGFLINEAR 0x02000000L +#define D3DPTFILTERCAPS_MAGFANISOTROPIC 0x04000000L +#define D3DPTFILTERCAPS_MAGFAFLATCUBIC 0x08000000L +#define D3DPTFILTERCAPS_MAGFGAUSSIANCUBIC 0x10000000L + +/* D3DPRIMCAPS dwTextureBlendCaps */ + +#define D3DPTBLENDCAPS_DECAL 0x00000001L +#define D3DPTBLENDCAPS_MODULATE 0x00000002L +#define D3DPTBLENDCAPS_DECALALPHA 0x00000004L +#define D3DPTBLENDCAPS_MODULATEALPHA 0x00000008L +#define D3DPTBLENDCAPS_DECALMASK 0x00000010L +#define D3DPTBLENDCAPS_MODULATEMASK 0x00000020L +#define D3DPTBLENDCAPS_COPY 0x00000040L +#define D3DPTBLENDCAPS_ADD 0x00000080L + +/* D3DPRIMCAPS dwTextureAddressCaps */ +#define D3DPTADDRESSCAPS_WRAP 0x00000001L +#define D3DPTADDRESSCAPS_MIRROR 0x00000002L +#define D3DPTADDRESSCAPS_CLAMP 0x00000004L +#define D3DPTADDRESSCAPS_BORDER 0x00000008L +#define D3DPTADDRESSCAPS_INDEPENDENTUV 0x00000010L + + +/* D3DDEVICEDESC dwStencilCaps */ + +#define D3DSTENCILCAPS_KEEP 0x00000001L +#define D3DSTENCILCAPS_ZERO 0x00000002L +#define D3DSTENCILCAPS_REPLACE 0x00000004L +#define D3DSTENCILCAPS_INCRSAT 0x00000008L +#define D3DSTENCILCAPS_DECRSAT 0x00000010L +#define D3DSTENCILCAPS_INVERT 0x00000020L +#define D3DSTENCILCAPS_INCR 0x00000040L +#define D3DSTENCILCAPS_DECR 0x00000080L + +/* D3DDEVICEDESC dwTextureOpCaps */ + +#define D3DTEXOPCAPS_DISABLE 0x00000001L +#define D3DTEXOPCAPS_SELECTARG1 0x00000002L +#define D3DTEXOPCAPS_SELECTARG2 0x00000004L +#define D3DTEXOPCAPS_MODULATE 0x00000008L +#define D3DTEXOPCAPS_MODULATE2X 0x00000010L +#define D3DTEXOPCAPS_MODULATE4X 0x00000020L +#define D3DTEXOPCAPS_ADD 0x00000040L +#define D3DTEXOPCAPS_ADDSIGNED 0x00000080L +#define D3DTEXOPCAPS_ADDSIGNED2X 0x00000100L +#define D3DTEXOPCAPS_SUBTRACT 0x00000200L +#define D3DTEXOPCAPS_ADDSMOOTH 0x00000400L +#define D3DTEXOPCAPS_BLENDDIFFUSEALPHA 0x00000800L +#define D3DTEXOPCAPS_BLENDTEXTUREALPHA 0x00001000L +#define D3DTEXOPCAPS_BLENDFACTORALPHA 0x00002000L +#define D3DTEXOPCAPS_BLENDTEXTUREALPHAPM 0x00004000L +#define D3DTEXOPCAPS_BLENDCURRENTALPHA 0x00008000L +#define D3DTEXOPCAPS_PREMODULATE 0x00010000L +#define D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR 0x00020000L +#define D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA 0x00040000L +#define D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR 0x00080000L +#define D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA 0x00100000L +#define D3DTEXOPCAPS_BUMPENVMAP 0x00200000L +#define D3DTEXOPCAPS_BUMPENVMAPLUMINANCE 0x00400000L +#define D3DTEXOPCAPS_DOTPRODUCT3 0x00800000L + +/* D3DDEVICEDESC dwFVFCaps flags */ + +#define D3DFVFCAPS_TEXCOORDCOUNTMASK 0x0000ffffL /* mask for texture coordinate count field */ +#define D3DFVFCAPS_DONOTSTRIPELEMENTS 0x00080000L /* Device prefers that vertex elements not be stripped */ + + +/* + * Description for a device. + * This is used to describe a device that is to be created or to query + * the current device. + */ +typedef struct _D3DDeviceDesc { + DWORD dwSize; /* Size of D3DDEVICEDESC structure */ + DWORD dwFlags; /* Indicates which fields have valid data */ + D3DCOLORMODEL dcmColorModel; /* Color model of device */ + DWORD dwDevCaps; /* Capabilities of device */ + D3DTRANSFORMCAPS dtcTransformCaps; /* Capabilities of transform */ + BOOL bClipping; /* Device can do 3D clipping */ + D3DLIGHTINGCAPS dlcLightingCaps; /* Capabilities of lighting */ + D3DPRIMCAPS dpcLineCaps; + D3DPRIMCAPS dpcTriCaps; + DWORD dwDeviceRenderBitDepth; /* One of DDBB_8, 16, etc.. */ + DWORD dwDeviceZBufferBitDepth;/* One of DDBD_16, 32, etc.. */ + DWORD dwMaxBufferSize; /* Maximum execute buffer size */ + DWORD dwMaxVertexCount; /* Maximum vertex count */ +#if(DIRECT3D_VERSION >= 0x0500) + // *** New fields for DX5 *** // + + // Width and height caps are 0 for legacy HALs. + DWORD dwMinTextureWidth, dwMinTextureHeight; + DWORD dwMaxTextureWidth, dwMaxTextureHeight; + DWORD dwMinStippleWidth, dwMaxStippleWidth; + DWORD dwMinStippleHeight, dwMaxStippleHeight; +#endif /* DIRECT3D_VERSION >= 0x0500 */ + +#if(DIRECT3D_VERSION >= 0x0600) + // New fields for DX6 + DWORD dwMaxTextureRepeat; + DWORD dwMaxTextureAspectRatio; + DWORD dwMaxAnisotropy; + + // Guard band that the rasterizer can accommodate + // Screen-space vertices inside this space but outside the viewport + // will get clipped properly. + D3DVALUE dvGuardBandLeft; + D3DVALUE dvGuardBandTop; + D3DVALUE dvGuardBandRight; + D3DVALUE dvGuardBandBottom; + + D3DVALUE dvExtentsAdjust; + DWORD dwStencilCaps; + + DWORD dwFVFCaps; /* low 4 bits: 0 implies TLVERTEX only, 1..8 imply FVF aware */ + DWORD dwTextureOpCaps; + WORD wMaxTextureBlendStages; + WORD wMaxSimultaneousTextures; + +#endif /* DIRECT3D_VERSION >= 0x0600 */ +} D3DDEVICEDESC, *LPD3DDEVICEDESC; + +#define D3DDEVICEDESCSIZE (sizeof(D3DDEVICEDESC)) + +typedef HRESULT (CALLBACK * LPD3DENUMDEVICESCALLBACK)(GUID FAR *lpGuid, LPSTR lpDeviceDescription, LPSTR lpDeviceName, LPD3DDEVICEDESC, LPD3DDEVICEDESC, LPVOID); + +/* D3DDEVICEDESC dwFlags indicating valid fields */ + +#define D3DDD_COLORMODEL 0x00000001L /* dcmColorModel is valid */ +#define D3DDD_DEVCAPS 0x00000002L /* dwDevCaps is valid */ +#define D3DDD_TRANSFORMCAPS 0x00000004L /* dtcTransformCaps is valid */ +#define D3DDD_LIGHTINGCAPS 0x00000008L /* dlcLightingCaps is valid */ +#define D3DDD_BCLIPPING 0x00000010L /* bClipping is valid */ +#define D3DDD_LINECAPS 0x00000020L /* dpcLineCaps is valid */ +#define D3DDD_TRICAPS 0x00000040L /* dpcTriCaps is valid */ +#define D3DDD_DEVICERENDERBITDEPTH 0x00000080L /* dwDeviceRenderBitDepth is valid */ +#define D3DDD_DEVICEZBUFFERBITDEPTH 0x00000100L /* dwDeviceZBufferBitDepth is valid */ +#define D3DDD_MAXBUFFERSIZE 0x00000200L /* dwMaxBufferSize is valid */ +#define D3DDD_MAXVERTEXCOUNT 0x00000400L /* dwMaxVertexCount is valid */ + +/* D3DDEVICEDESC dwDevCaps flags */ + +#define D3DDEVCAPS_FLOATTLVERTEX 0x00000001L /* Device accepts floating point */ + /* for post-transform vertex data */ +#define D3DDEVCAPS_SORTINCREASINGZ 0x00000002L /* Device needs data sorted for increasing Z */ +#define D3DDEVCAPS_SORTDECREASINGZ 0X00000004L /* Device needs data sorted for decreasing Z */ +#define D3DDEVCAPS_SORTEXACT 0x00000008L /* Device needs data sorted exactly */ + +#define D3DDEVCAPS_EXECUTESYSTEMMEMORY 0x00000010L /* Device can use execute buffers from system memory */ +#define D3DDEVCAPS_EXECUTEVIDEOMEMORY 0x00000020L /* Device can use execute buffers from video memory */ +#define D3DDEVCAPS_TLVERTEXSYSTEMMEMORY 0x00000040L /* Device can use TL buffers from system memory */ +#define D3DDEVCAPS_TLVERTEXVIDEOMEMORY 0x00000080L /* Device can use TL buffers from video memory */ +#define D3DDEVCAPS_TEXTURESYSTEMMEMORY 0x00000100L /* Device can texture from system memory */ +#define D3DDEVCAPS_TEXTUREVIDEOMEMORY 0x00000200L /* Device can texture from device memory */ +#define D3DDEVCAPS_DRAWPRIMTLVERTEX 0x00000400L /* Device can draw TLVERTEX primitives */ +#define D3DDEVCAPS_CANRENDERAFTERFLIP 0x00000800L /* Device can render without waiting for flip to complete */ +#define D3DDEVCAPS_TEXTURENONLOCALVIDMEM 0x00001000L /* Device can texture from nonlocal video memory */ +#define D3DDEVCAPS_DRAWPRIMITIVES2 0x00002000L /* Device can support DrawPrimitives2 */ +#define D3DDEVCAPS_SEPARATETEXTUREMEMORIES 0x00004000L/* Device is texturing from separate memory pools */ + +#define D3DFDS_COLORMODEL 0x00000001L /* Match color model */ +#define D3DFDS_GUID 0x00000002L /* Match guid */ +#define D3DFDS_HARDWARE 0x00000004L /* Match hardware/software */ +#define D3DFDS_TRIANGLES 0x00000008L /* Match in triCaps */ +#define D3DFDS_LINES 0x00000010L /* Match in lineCaps */ +#define D3DFDS_MISCCAPS 0x00000020L /* Match primCaps.dwMiscCaps */ +#define D3DFDS_RASTERCAPS 0x00000040L /* Match primCaps.dwRasterCaps */ +#define D3DFDS_ZCMPCAPS 0x00000080L /* Match primCaps.dwZCmpCaps */ +#define D3DFDS_ALPHACMPCAPS 0x00000100L /* Match primCaps.dwAlphaCmpCaps */ +#define D3DFDS_SRCBLENDCAPS 0x00000200L /* Match primCaps.dwSourceBlendCaps */ +#define D3DFDS_DSTBLENDCAPS 0x00000400L /* Match primCaps.dwDestBlendCaps */ +#define D3DFDS_SHADECAPS 0x00000800L /* Match primCaps.dwShadeCaps */ +#define D3DFDS_TEXTURECAPS 0x00001000L /* Match primCaps.dwTextureCaps */ +#define D3DFDS_TEXTUREFILTERCAPS 0x00002000L /* Match primCaps.dwTextureFilterCaps */ +#define D3DFDS_TEXTUREBLENDCAPS 0x00004000L /* Match primCaps.dwTextureBlendCaps */ +#define D3DFDS_TEXTUREADDRESSCAPS 0x00008000L /* Match primCaps.dwTextureBlendCaps */ + +/* + * FindDevice arguments + */ +typedef struct _D3DFINDDEVICESEARCH { + DWORD dwSize; + DWORD dwFlags; + BOOL bHardware; + D3DCOLORMODEL dcmColorModel; + GUID guid; + DWORD dwCaps; + D3DPRIMCAPS dpcPrimCaps; +} D3DFINDDEVICESEARCH, *LPD3DFINDDEVICESEARCH; + +typedef struct _D3DFINDDEVICERESULT { + DWORD dwSize; + GUID guid; /* guid which matched */ + D3DDEVICEDESC ddHwDesc; /* hardware D3DDEVICEDESC */ + D3DDEVICEDESC ddSwDesc; /* software D3DDEVICEDESC */ +} D3DFINDDEVICERESULT, *LPD3DFINDDEVICERESULT; + +/* + * Description of execute buffer. + */ +typedef struct _D3DExecuteBufferDesc { + DWORD dwSize; /* size of this structure */ + DWORD dwFlags; /* flags indicating which fields are valid */ + DWORD dwCaps; /* capabilities of execute buffer */ + DWORD dwBufferSize; /* size of execute buffer data */ + LPVOID lpData; /* pointer to actual data */ +} D3DEXECUTEBUFFERDESC, *LPD3DEXECUTEBUFFERDESC; + +/* D3DEXECUTEBUFFER dwFlags indicating valid fields */ + +#define D3DDEB_BUFSIZE 0x00000001l /* buffer size valid */ +#define D3DDEB_CAPS 0x00000002l /* caps valid */ +#define D3DDEB_LPDATA 0x00000004l /* lpData valid */ + +/* D3DEXECUTEBUFFER dwCaps */ + +#define D3DDEBCAPS_SYSTEMMEMORY 0x00000001l /* buffer in system memory */ +#define D3DDEBCAPS_VIDEOMEMORY 0x00000002l /* buffer in device memory */ +#define D3DDEBCAPS_MEM (D3DDEBCAPS_SYSTEMMEMORY|D3DDEBCAPS_VIDEOMEMORY) + +#pragma pack() + +#endif /* _D3DCAPS_H_ */ + diff --git a/lib/win/DirectX/d3dtypes.h b/lib/win/DirectX/d3dtypes.h new file mode 100644 index 000000000..4ca2d07d1 --- /dev/null +++ b/lib/win/DirectX/d3dtypes.h @@ -0,0 +1,1498 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1998 Microsoft Corporation. All Rights Reserved. + * + * File: d3dtypes.h + * Content: Direct3D types include file + * + ***************************************************************************/ + +#ifndef _D3DTYPES_H_ +#define _D3DTYPES_H_ + +#include + +#include +#include + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x0600 +#endif + +#pragma pack(4) + + +/* D3DVALUE is the fundamental Direct3D fractional data type */ + +#define D3DVALP(val, prec) ((float)(val)) +#define D3DVAL(val) ((float)(val)) +typedef float D3DVALUE, *LPD3DVALUE; +#define D3DDivide(a, b) (float)((double) (a) / (double) (b)) +#define D3DMultiply(a, b) ((a) * (b)) + +typedef LONG D3DFIXED; + +#ifndef RGB_MAKE +/* + * Format of CI colors is + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | alpha | color index | fraction | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define CI_GETALPHA(ci) ((ci) >> 24) +#define CI_GETINDEX(ci) (((ci) >> 8) & 0xffff) +#define CI_GETFRACTION(ci) ((ci) & 0xff) +#define CI_ROUNDINDEX(ci) CI_GETINDEX((ci) + 0x80) +#define CI_MASKALPHA(ci) ((ci) & 0xffffff) +#define CI_MAKE(a, i, f) (((a) << 24) | ((i) << 8) | (f)) + +/* + * Format of RGBA colors is + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | alpha | red | green | blue | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define RGBA_GETALPHA(rgb) ((rgb) >> 24) +#define RGBA_GETRED(rgb) (((rgb) >> 16) & 0xff) +#define RGBA_GETGREEN(rgb) (((rgb) >> 8) & 0xff) +#define RGBA_GETBLUE(rgb) ((rgb) & 0xff) +#define RGBA_MAKE(r, g, b, a) ((D3DCOLOR) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))) + +/* D3DRGB and D3DRGBA may be used as initialisers for D3DCOLORs + * The float values must be in the range 0..1 + */ +#define D3DRGB(r, g, b) \ + (0xff000000L | ( ((long)((r) * 255)) << 16) | (((long)((g) * 255)) << 8) | (long)((b) * 255)) +#define D3DRGBA(r, g, b, a) \ + ( (((long)((a) * 255)) << 24) | (((long)((r) * 255)) << 16) \ + | (((long)((g) * 255)) << 8) | (long)((b) * 255) \ + ) + +/* + * Format of RGB colors is + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ignored | red | green | blue | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define RGB_GETRED(rgb) (((rgb) >> 16) & 0xff) +#define RGB_GETGREEN(rgb) (((rgb) >> 8) & 0xff) +#define RGB_GETBLUE(rgb) ((rgb) & 0xff) +#define RGBA_SETALPHA(rgba, x) (((x) << 24) | ((rgba) & 0x00ffffff)) +#define RGB_MAKE(r, g, b) ((D3DCOLOR) (((r) << 16) | ((g) << 8) | (b))) +#define RGBA_TORGB(rgba) ((D3DCOLOR) ((rgba) & 0xffffff)) +#define RGB_TORGBA(rgb) ((D3DCOLOR) ((rgb) | 0xff000000)) + +#endif + +/* + * Flags for Enumerate functions + */ + +/* + * Stop the enumeration + */ +#define D3DENUMRET_CANCEL DDENUMRET_CANCEL + +/* + * Continue the enumeration + */ +#define D3DENUMRET_OK DDENUMRET_OK + +typedef HRESULT (CALLBACK* LPD3DVALIDATECALLBACK)(LPVOID lpUserArg, DWORD dwOffset); +typedef HRESULT (CALLBACK* LPD3DENUMTEXTUREFORMATSCALLBACK)(LPDDSURFACEDESC lpDdsd, LPVOID lpContext); +typedef HRESULT (CALLBACK* LPD3DENUMPIXELFORMATSCALLBACK)(LPDDPIXELFORMAT lpDDPixFmt, LPVOID lpContext); + +typedef DWORD D3DCOLOR, *LPD3DCOLOR; + +typedef DWORD D3DMATERIALHANDLE, *LPD3DMATERIALHANDLE; +typedef DWORD D3DTEXTUREHANDLE, *LPD3DTEXTUREHANDLE; +typedef DWORD D3DMATRIXHANDLE, *LPD3DMATRIXHANDLE; + +typedef struct _D3DCOLORVALUE { + union { + D3DVALUE r; + D3DVALUE dvR; + }; + union { + D3DVALUE g; + D3DVALUE dvG; + }; + union { + D3DVALUE b; + D3DVALUE dvB; + }; + union { + D3DVALUE a; + D3DVALUE dvA; + }; +} D3DCOLORVALUE, *LPD3DCOLORVALUE; + +typedef struct _D3DRECT { + union { + LONG x1; + LONG lX1; + }; + union { + LONG y1; + LONG lY1; + }; + union { + LONG x2; + LONG lX2; + }; + union { + LONG y2; + LONG lY2; + }; +} D3DRECT, *LPD3DRECT; + +typedef struct _D3DVECTOR { + union { + D3DVALUE x; + D3DVALUE dvX; + }; + union { + D3DVALUE y; + D3DVALUE dvY; + }; + union { + D3DVALUE z; + D3DVALUE dvZ; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + +public: + + // ===================================== + // Constructors + // ===================================== + + _D3DVECTOR() { } + _D3DVECTOR(D3DVALUE f); + _D3DVECTOR(D3DVALUE _x, D3DVALUE _y, D3DVALUE _z); + _D3DVECTOR(const D3DVALUE f[3]); + + // ===================================== + // Access grants + // ===================================== + + const D3DVALUE&operator[](int i) const; + D3DVALUE&operator[](int i); + + // ===================================== + // Assignment operators + // ===================================== + + _D3DVECTOR& operator += (const _D3DVECTOR& v); + _D3DVECTOR& operator -= (const _D3DVECTOR& v); + _D3DVECTOR& operator *= (const _D3DVECTOR& v); + _D3DVECTOR& operator /= (const _D3DVECTOR& v); + _D3DVECTOR& operator *= (D3DVALUE s); + _D3DVECTOR& operator /= (D3DVALUE s); + + // ===================================== + // Unary operators + // ===================================== + + friend _D3DVECTOR operator + (const _D3DVECTOR& v); + friend _D3DVECTOR operator - (const _D3DVECTOR& v); + + + // ===================================== + // Binary operators + // ===================================== + + // Addition and subtraction + friend _D3DVECTOR operator + (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR operator - (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + // Scalar multiplication and division + friend _D3DVECTOR operator * (const _D3DVECTOR& v, D3DVALUE s); + friend _D3DVECTOR operator * (D3DVALUE s, const _D3DVECTOR& v); + friend _D3DVECTOR operator / (const _D3DVECTOR& v, D3DVALUE s); + // Memberwise multiplication and division + friend _D3DVECTOR operator * (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR operator / (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Vector dominance + friend int operator < (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend int operator <= (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Bitwise equality + friend int operator == (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Length-related functions + friend D3DVALUE SquareMagnitude (const _D3DVECTOR& v); + friend D3DVALUE Magnitude (const _D3DVECTOR& v); + + // Returns vector with same direction and unit length + friend _D3DVECTOR Normalize (const _D3DVECTOR& v); + + // Return min/max component of the input vector + friend D3DVALUE Min (const _D3DVECTOR& v); + friend D3DVALUE Max (const _D3DVECTOR& v); + + // Return memberwise min/max of input vectors + friend _D3DVECTOR Minimize (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR Maximize (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Dot and cross product + friend D3DVALUE DotProduct (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR CrossProduct (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + +#endif +} D3DVECTOR, *LPD3DVECTOR; + +/* + * Vertex data types supported in an ExecuteBuffer. + */ + +/* + * Homogeneous vertices + */ + +typedef struct _D3DHVERTEX { + DWORD dwFlags; /* Homogeneous clipping flags */ + union { + D3DVALUE hx; + D3DVALUE dvHX; + }; + union { + D3DVALUE hy; + D3DVALUE dvHY; + }; + union { + D3DVALUE hz; + D3DVALUE dvHZ; + }; +} D3DHVERTEX, *LPD3DHVERTEX; + +/* + * Transformed/lit vertices + */ +typedef struct _D3DTLVERTEX { + union { + D3DVALUE sx; /* Screen coordinates */ + D3DVALUE dvSX; + }; + union { + D3DVALUE sy; + D3DVALUE dvSY; + }; + union { + D3DVALUE sz; + D3DVALUE dvSZ; + }; + union { + D3DVALUE rhw; /* Reciprocal of homogeneous w */ + D3DVALUE dvRHW; + }; + union { + D3DCOLOR color; /* Vertex color */ + D3DCOLOR dcColor; + }; + union { + D3DCOLOR specular; /* Specular component of vertex */ + D3DCOLOR dcSpecular; + }; + union { + D3DVALUE tu; /* Texture coordinates */ + D3DVALUE dvTU; + }; + union { + D3DVALUE tv; + D3DVALUE dvTV; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + _D3DTLVERTEX() { } + _D3DTLVERTEX(const D3DVECTOR& v, float _rhw, + D3DCOLOR _color, D3DCOLOR _specular, + float _tu, float _tv) + { sx = v.x; sy = v.y; sz = v.z; rhw = _rhw; + color = _color; specular = _specular; + tu = _tu; tv = _tv; + } +#endif +} D3DTLVERTEX, *LPD3DTLVERTEX; + +/* + * Untransformed/lit vertices + */ +typedef struct _D3DLVERTEX { + union { + D3DVALUE x; /* Homogeneous coordinates */ + D3DVALUE dvX; + }; + union { + D3DVALUE y; + D3DVALUE dvY; + }; + union { + D3DVALUE z; + D3DVALUE dvZ; + }; + DWORD dwReserved; + union { + D3DCOLOR color; /* Vertex color */ + D3DCOLOR dcColor; + }; + union { + D3DCOLOR specular; /* Specular component of vertex */ + D3DCOLOR dcSpecular; + }; + union { + D3DVALUE tu; /* Texture coordinates */ + D3DVALUE dvTU; + }; + union { + D3DVALUE tv; + D3DVALUE dvTV; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + _D3DLVERTEX() { } + _D3DLVERTEX(const D3DVECTOR& v, + D3DCOLOR _color, D3DCOLOR _specular, + float _tu, float _tv) + { x = v.x; y = v.y; z = v.z; dwReserved = 0; + color = _color; specular = _specular; + tu = _tu; tv = _tv; + } +#endif +} D3DLVERTEX, *LPD3DLVERTEX; + +/* + * Untransformed/unlit vertices + */ + +typedef struct _D3DVERTEX { + union { + D3DVALUE x; /* Homogeneous coordinates */ + D3DVALUE dvX; + }; + union { + D3DVALUE y; + D3DVALUE dvY; + }; + union { + D3DVALUE z; + D3DVALUE dvZ; + }; + union { + D3DVALUE nx; /* Normal */ + D3DVALUE dvNX; + }; + union { + D3DVALUE ny; + D3DVALUE dvNY; + }; + union { + D3DVALUE nz; + D3DVALUE dvNZ; + }; + union { + D3DVALUE tu; /* Texture coordinates */ + D3DVALUE dvTU; + }; + union { + D3DVALUE tv; + D3DVALUE dvTV; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + _D3DVERTEX() { } + _D3DVERTEX(const D3DVECTOR& v, const D3DVECTOR& n, float _tu, float _tv) + { x = v.x; y = v.y; z = v.z; + nx = n.x; ny = n.y; nz = n.z; + tu = _tu; tv = _tv; + } +#endif +} D3DVERTEX, *LPD3DVERTEX; + + +/* + * Matrix, viewport, and tranformation structures and definitions. + */ + +typedef struct _D3DMATRIX { +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + union { + struct { +#endif + + D3DVALUE _11, _12, _13, _14; + D3DVALUE _21, _22, _23, _24; + D3DVALUE _31, _32, _33, _34; + D3DVALUE _41, _42, _43, _44; + +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + }; + D3DVALUE m[4][4]; + }; + _D3DMATRIX() { } + _D3DMATRIX( D3DVALUE _m00, D3DVALUE _m01, D3DVALUE _m02, D3DVALUE _m03, + D3DVALUE _m10, D3DVALUE _m11, D3DVALUE _m12, D3DVALUE _m13, + D3DVALUE _m20, D3DVALUE _m21, D3DVALUE _m22, D3DVALUE _m23, + D3DVALUE _m30, D3DVALUE _m31, D3DVALUE _m32, D3DVALUE _m33 + ) + { + m[0][0] = _m00; m[0][1] = _m01; m[0][2] = _m02; m[0][3] = _m03; + m[1][0] = _m10; m[1][1] = _m11; m[1][2] = _m12; m[1][3] = _m13; + m[2][0] = _m20; m[2][1] = _m21; m[2][2] = _m22; m[2][3] = _m23; + m[3][0] = _m30; m[3][1] = _m31; m[3][2] = _m32; m[3][3] = _m33; + } + + D3DVALUE& operator()(int iRow, int iColumn) { return m[iRow][iColumn]; } + const D3DVALUE& operator()(int iRow, int iColumn) const { return m[iRow][iColumn]; } + friend _D3DMATRIX operator* (const _D3DMATRIX&, const _D3DMATRIX&); +#endif +} D3DMATRIX, *LPD3DMATRIX; + +#if (defined __cplusplus) && (defined D3D_OVERLOADS) +#include "d3dvec.inl" +#endif + +typedef struct _D3DVIEWPORT { + DWORD dwSize; + DWORD dwX; + DWORD dwY; /* Top left */ + DWORD dwWidth; + DWORD dwHeight; /* Dimensions */ + D3DVALUE dvScaleX; /* Scale homogeneous to screen */ + D3DVALUE dvScaleY; /* Scale homogeneous to screen */ + D3DVALUE dvMaxX; /* Min/max homogeneous x coord */ + D3DVALUE dvMaxY; /* Min/max homogeneous y coord */ + D3DVALUE dvMinZ; + D3DVALUE dvMaxZ; /* Min/max homogeneous z coord */ +} D3DVIEWPORT, *LPD3DVIEWPORT; + +typedef struct _D3DVIEWPORT2 { + DWORD dwSize; + DWORD dwX; + DWORD dwY; /* Viewport Top left */ + DWORD dwWidth; + DWORD dwHeight; /* Viewport Dimensions */ + D3DVALUE dvClipX; /* Top left of clip volume */ + D3DVALUE dvClipY; + D3DVALUE dvClipWidth; /* Clip Volume Dimensions */ + D3DVALUE dvClipHeight; + D3DVALUE dvMinZ; /* Min/max of clip Volume */ + D3DVALUE dvMaxZ; +} D3DVIEWPORT2, *LPD3DVIEWPORT2; + +/* + * Values for clip fields. + */ +#define D3DCLIP_LEFT 0x00000001L +#define D3DCLIP_RIGHT 0x00000002L +#define D3DCLIP_TOP 0x00000004L +#define D3DCLIP_BOTTOM 0x00000008L +#define D3DCLIP_FRONT 0x00000010L +#define D3DCLIP_BACK 0x00000020L +#define D3DCLIP_GEN0 0x00000040L +#define D3DCLIP_GEN1 0x00000080L +#define D3DCLIP_GEN2 0x00000100L +#define D3DCLIP_GEN3 0x00000200L +#define D3DCLIP_GEN4 0x00000400L +#define D3DCLIP_GEN5 0x00000800L + +/* + * Values for d3d status. + */ +#define D3DSTATUS_CLIPUNIONLEFT D3DCLIP_LEFT +#define D3DSTATUS_CLIPUNIONRIGHT D3DCLIP_RIGHT +#define D3DSTATUS_CLIPUNIONTOP D3DCLIP_TOP +#define D3DSTATUS_CLIPUNIONBOTTOM D3DCLIP_BOTTOM +#define D3DSTATUS_CLIPUNIONFRONT D3DCLIP_FRONT +#define D3DSTATUS_CLIPUNIONBACK D3DCLIP_BACK +#define D3DSTATUS_CLIPUNIONGEN0 D3DCLIP_GEN0 +#define D3DSTATUS_CLIPUNIONGEN1 D3DCLIP_GEN1 +#define D3DSTATUS_CLIPUNIONGEN2 D3DCLIP_GEN2 +#define D3DSTATUS_CLIPUNIONGEN3 D3DCLIP_GEN3 +#define D3DSTATUS_CLIPUNIONGEN4 D3DCLIP_GEN4 +#define D3DSTATUS_CLIPUNIONGEN5 D3DCLIP_GEN5 + +#define D3DSTATUS_CLIPINTERSECTIONLEFT 0x00001000L +#define D3DSTATUS_CLIPINTERSECTIONRIGHT 0x00002000L +#define D3DSTATUS_CLIPINTERSECTIONTOP 0x00004000L +#define D3DSTATUS_CLIPINTERSECTIONBOTTOM 0x00008000L +#define D3DSTATUS_CLIPINTERSECTIONFRONT 0x00010000L +#define D3DSTATUS_CLIPINTERSECTIONBACK 0x00020000L +#define D3DSTATUS_CLIPINTERSECTIONGEN0 0x00040000L +#define D3DSTATUS_CLIPINTERSECTIONGEN1 0x00080000L +#define D3DSTATUS_CLIPINTERSECTIONGEN2 0x00100000L +#define D3DSTATUS_CLIPINTERSECTIONGEN3 0x00200000L +#define D3DSTATUS_CLIPINTERSECTIONGEN4 0x00400000L +#define D3DSTATUS_CLIPINTERSECTIONGEN5 0x00800000L +#define D3DSTATUS_ZNOTVISIBLE 0x01000000L +/* Do not use 0x80000000 for any status flags in future as it is reserved */ + +#define D3DSTATUS_CLIPUNIONALL ( \ + D3DSTATUS_CLIPUNIONLEFT | \ + D3DSTATUS_CLIPUNIONRIGHT | \ + D3DSTATUS_CLIPUNIONTOP | \ + D3DSTATUS_CLIPUNIONBOTTOM | \ + D3DSTATUS_CLIPUNIONFRONT | \ + D3DSTATUS_CLIPUNIONBACK | \ + D3DSTATUS_CLIPUNIONGEN0 | \ + D3DSTATUS_CLIPUNIONGEN1 | \ + D3DSTATUS_CLIPUNIONGEN2 | \ + D3DSTATUS_CLIPUNIONGEN3 | \ + D3DSTATUS_CLIPUNIONGEN4 | \ + D3DSTATUS_CLIPUNIONGEN5 \ + ) + +#define D3DSTATUS_CLIPINTERSECTIONALL ( \ + D3DSTATUS_CLIPINTERSECTIONLEFT | \ + D3DSTATUS_CLIPINTERSECTIONRIGHT | \ + D3DSTATUS_CLIPINTERSECTIONTOP | \ + D3DSTATUS_CLIPINTERSECTIONBOTTOM | \ + D3DSTATUS_CLIPINTERSECTIONFRONT | \ + D3DSTATUS_CLIPINTERSECTIONBACK | \ + D3DSTATUS_CLIPINTERSECTIONGEN0 | \ + D3DSTATUS_CLIPINTERSECTIONGEN1 | \ + D3DSTATUS_CLIPINTERSECTIONGEN2 | \ + D3DSTATUS_CLIPINTERSECTIONGEN3 | \ + D3DSTATUS_CLIPINTERSECTIONGEN4 | \ + D3DSTATUS_CLIPINTERSECTIONGEN5 \ + ) + +#define D3DSTATUS_DEFAULT ( \ + D3DSTATUS_CLIPINTERSECTIONALL | \ + D3DSTATUS_ZNOTVISIBLE) + + +/* + * Options for direct transform calls + */ +#define D3DTRANSFORM_CLIPPED 0x00000001l +#define D3DTRANSFORM_UNCLIPPED 0x00000002l + +typedef struct _D3DTRANSFORMDATA { + DWORD dwSize; + LPVOID lpIn; /* Input vertices */ + DWORD dwInSize; /* Stride of input vertices */ + LPVOID lpOut; /* Output vertices */ + DWORD dwOutSize; /* Stride of output vertices */ + LPD3DHVERTEX lpHOut; /* Output homogeneous vertices */ + DWORD dwClip; /* Clipping hint */ + DWORD dwClipIntersection; + DWORD dwClipUnion; /* Union of all clip flags */ + D3DRECT drExtent; /* Extent of transformed vertices */ +} D3DTRANSFORMDATA, *LPD3DTRANSFORMDATA; + +/* + * Structure defining position and direction properties for lighting. + */ +typedef struct _D3DLIGHTINGELEMENT { + D3DVECTOR dvPosition; /* Lightable point in model space */ + D3DVECTOR dvNormal; /* Normalised unit vector */ +} D3DLIGHTINGELEMENT, *LPD3DLIGHTINGELEMENT; + +/* + * Structure defining material properties for lighting. + */ +typedef struct _D3DMATERIAL { + DWORD dwSize; + union { + D3DCOLORVALUE diffuse; /* Diffuse color RGBA */ + D3DCOLORVALUE dcvDiffuse; + }; + union { + D3DCOLORVALUE ambient; /* Ambient color RGB */ + D3DCOLORVALUE dcvAmbient; + }; + union { + D3DCOLORVALUE specular; /* Specular 'shininess' */ + D3DCOLORVALUE dcvSpecular; + }; + union { + D3DCOLORVALUE emissive; /* Emissive color RGB */ + D3DCOLORVALUE dcvEmissive; + }; + union { + D3DVALUE power; /* Sharpness if specular highlight */ + D3DVALUE dvPower; + }; + D3DTEXTUREHANDLE hTexture; /* Handle to texture map */ + DWORD dwRampSize; +} D3DMATERIAL, *LPD3DMATERIAL; + +typedef enum _D3DLIGHTTYPE { + D3DLIGHT_POINT = 1, + D3DLIGHT_SPOT = 2, + D3DLIGHT_DIRECTIONAL = 3, + D3DLIGHT_PARALLELPOINT = 4, +#if(DIRECT3D_VERSION < 0x0500) // For backward compatible headers + D3DLIGHT_GLSPOT = 5, +#endif + D3DLIGHT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DLIGHTTYPE; + +/* + * Structure defining a light source and its properties. + */ +typedef struct _D3DLIGHT { + DWORD dwSize; + D3DLIGHTTYPE dltType; /* Type of light source */ + D3DCOLORVALUE dcvColor; /* Color of light */ + D3DVECTOR dvPosition; /* Position in world space */ + D3DVECTOR dvDirection; /* Direction in world space */ + D3DVALUE dvRange; /* Cutoff range */ + D3DVALUE dvFalloff; /* Falloff */ + D3DVALUE dvAttenuation0; /* Constant attenuation */ + D3DVALUE dvAttenuation1; /* Linear attenuation */ + D3DVALUE dvAttenuation2; /* Quadratic attenuation */ + D3DVALUE dvTheta; /* Inner angle of spotlight cone */ + D3DVALUE dvPhi; /* Outer angle of spotlight cone */ +} D3DLIGHT, *LPD3DLIGHT; + +/* + * Structure defining a light source and its properties. + */ + +/* flags bits */ +#define D3DLIGHT_ACTIVE 0x00000001 +#define D3DLIGHT_NO_SPECULAR 0x00000002 + +/* maximum valid light range */ +#define D3DLIGHT_RANGE_MAX ((float)sqrt(FLT_MAX)) + +typedef struct _D3DLIGHT2 { + DWORD dwSize; + D3DLIGHTTYPE dltType; /* Type of light source */ + D3DCOLORVALUE dcvColor; /* Color of light */ + D3DVECTOR dvPosition; /* Position in world space */ + D3DVECTOR dvDirection; /* Direction in world space */ + D3DVALUE dvRange; /* Cutoff range */ + D3DVALUE dvFalloff; /* Falloff */ + D3DVALUE dvAttenuation0; /* Constant attenuation */ + D3DVALUE dvAttenuation1; /* Linear attenuation */ + D3DVALUE dvAttenuation2; /* Quadratic attenuation */ + D3DVALUE dvTheta; /* Inner angle of spotlight cone */ + D3DVALUE dvPhi; /* Outer angle of spotlight cone */ + DWORD dwFlags; +} D3DLIGHT2, *LPD3DLIGHT2; + +typedef struct _D3DLIGHTDATA { + DWORD dwSize; + LPD3DLIGHTINGELEMENT lpIn; /* Input positions and normals */ + DWORD dwInSize; /* Stride of input elements */ + LPD3DTLVERTEX lpOut; /* Output colors */ + DWORD dwOutSize; /* Stride of output colors */ +} D3DLIGHTDATA, *LPD3DLIGHTDATA; + +/* + * Before DX5, these values were in an enum called + * D3DCOLORMODEL. This was not correct, since they are + * bit flags. A driver can surface either or both flags + * in the dcmColorModel member of D3DDEVICEDESC. + */ +#define D3DCOLOR_MONO 1 +#define D3DCOLOR_RGB 2 + +typedef DWORD D3DCOLORMODEL; + +/* + * Options for clearing + */ +#define D3DCLEAR_TARGET 0x00000001l /* Clear target surface */ +#define D3DCLEAR_ZBUFFER 0x00000002l /* Clear target z buffer */ +#define D3DCLEAR_STENCIL 0x00000004l /* Clear stencil planes */ + +/* + * Execute buffers are allocated via Direct3D. These buffers may then + * be filled by the application with instructions to execute along with + * vertex data. + */ + +/* + * Supported op codes for execute instructions. + */ +typedef enum _D3DOPCODE { + D3DOP_POINT = 1, + D3DOP_LINE = 2, + D3DOP_TRIANGLE = 3, + D3DOP_MATRIXLOAD = 4, + D3DOP_MATRIXMULTIPLY = 5, + D3DOP_STATETRANSFORM = 6, + D3DOP_STATELIGHT = 7, + D3DOP_STATERENDER = 8, + D3DOP_PROCESSVERTICES = 9, + D3DOP_TEXTURELOAD = 10, + D3DOP_EXIT = 11, + D3DOP_BRANCHFORWARD = 12, + D3DOP_SPAN = 13, + D3DOP_SETSTATUS = 14, + D3DOP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DOPCODE; + +typedef struct _D3DINSTRUCTION { + BYTE bOpcode; /* Instruction opcode */ + BYTE bSize; /* Size of each instruction data unit */ + WORD wCount; /* Count of instruction data units to follow */ +} D3DINSTRUCTION, *LPD3DINSTRUCTION; + +/* + * Structure for texture loads + */ +typedef struct _D3DTEXTURELOAD { + D3DTEXTUREHANDLE hDestTexture; + D3DTEXTUREHANDLE hSrcTexture; +} D3DTEXTURELOAD, *LPD3DTEXTURELOAD; + +/* + * Structure for picking + */ +typedef struct _D3DPICKRECORD { + BYTE bOpcode; + BYTE bPad; + DWORD dwOffset; + D3DVALUE dvZ; +} D3DPICKRECORD, *LPD3DPICKRECORD; + +/* + * The following defines the rendering states which can be set in the + * execute buffer. + */ + +typedef enum _D3DSHADEMODE { + D3DSHADE_FLAT = 1, + D3DSHADE_GOURAUD = 2, + D3DSHADE_PHONG = 3, + D3DSHADE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DSHADEMODE; + +typedef enum _D3DFILLMODE { + D3DFILL_POINT = 1, + D3DFILL_WIREFRAME = 2, + D3DFILL_SOLID = 3, + D3DFILL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DFILLMODE; + +typedef struct _D3DLINEPATTERN { + WORD wRepeatFactor; + WORD wLinePattern; +} D3DLINEPATTERN; + +typedef enum _D3DTEXTUREFILTER { + D3DFILTER_NEAREST = 1, + D3DFILTER_LINEAR = 2, + D3DFILTER_MIPNEAREST = 3, + D3DFILTER_MIPLINEAR = 4, + D3DFILTER_LINEARMIPNEAREST = 5, + D3DFILTER_LINEARMIPLINEAR = 6, + D3DFILTER_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTUREFILTER; + +typedef enum _D3DBLEND { + D3DBLEND_ZERO = 1, + D3DBLEND_ONE = 2, + D3DBLEND_SRCCOLOR = 3, + D3DBLEND_INVSRCCOLOR = 4, + D3DBLEND_SRCALPHA = 5, + D3DBLEND_INVSRCALPHA = 6, + D3DBLEND_DESTALPHA = 7, + D3DBLEND_INVDESTALPHA = 8, + D3DBLEND_DESTCOLOR = 9, + D3DBLEND_INVDESTCOLOR = 10, + D3DBLEND_SRCALPHASAT = 11, + D3DBLEND_BOTHSRCALPHA = 12, + D3DBLEND_BOTHINVSRCALPHA = 13, + D3DBLEND_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DBLEND; + +typedef enum _D3DTEXTUREBLEND { + D3DTBLEND_DECAL = 1, + D3DTBLEND_MODULATE = 2, + D3DTBLEND_DECALALPHA = 3, + D3DTBLEND_MODULATEALPHA = 4, + D3DTBLEND_DECALMASK = 5, + D3DTBLEND_MODULATEMASK = 6, + D3DTBLEND_COPY = 7, + D3DTBLEND_ADD = 8, + D3DTBLEND_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTUREBLEND; + +typedef enum _D3DTEXTUREADDRESS { + D3DTADDRESS_WRAP = 1, + D3DTADDRESS_MIRROR = 2, + D3DTADDRESS_CLAMP = 3, + D3DTADDRESS_BORDER = 4, + D3DTADDRESS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTUREADDRESS; + +typedef enum _D3DCULL { + D3DCULL_NONE = 1, + D3DCULL_CW = 2, + D3DCULL_CCW = 3, + D3DCULL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DCULL; + +typedef enum _D3DCMPFUNC { + D3DCMP_NEVER = 1, + D3DCMP_LESS = 2, + D3DCMP_EQUAL = 3, + D3DCMP_LESSEQUAL = 4, + D3DCMP_GREATER = 5, + D3DCMP_NOTEQUAL = 6, + D3DCMP_GREATEREQUAL = 7, + D3DCMP_ALWAYS = 8, + D3DCMP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DCMPFUNC; + +typedef enum _D3DSTENCILOP { + D3DSTENCILOP_KEEP = 1, + D3DSTENCILOP_ZERO = 2, + D3DSTENCILOP_REPLACE = 3, + D3DSTENCILOP_INCRSAT = 4, + D3DSTENCILOP_DECRSAT = 5, + D3DSTENCILOP_INVERT = 6, + D3DSTENCILOP_INCR = 7, + D3DSTENCILOP_DECR = 8, + D3DSTENCILOP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DSTENCILOP; + +typedef enum _D3DFOGMODE { + D3DFOG_NONE = 0, + D3DFOG_EXP = 1, + D3DFOG_EXP2 = 2, + D3DFOG_LINEAR = 3, + D3DFOG_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DFOGMODE; + +typedef enum _D3DZBUFFERTYPE { + D3DZB_FALSE = 0, + D3DZB_TRUE = 1, // Z buffering + D3DZB_USEW = 2, // W buffering + D3DZB_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DZBUFFERTYPE; + +typedef enum _D3DANTIALIASMODE { + D3DANTIALIAS_NONE = 0, + D3DANTIALIAS_SORTDEPENDENT = 1, + D3DANTIALIAS_SORTINDEPENDENT = 2, + D3DANTIALIAS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DANTIALIASMODE; + +// Vertex types supported by Direct3D +typedef enum _D3DVERTEXTYPE { + D3DVT_VERTEX = 1, + D3DVT_LVERTEX = 2, + D3DVT_TLVERTEX = 3, + D3DVT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DVERTEXTYPE; + +// Primitives supported by draw-primitive API +typedef enum _D3DPRIMITIVETYPE { + D3DPT_POINTLIST = 1, + D3DPT_LINELIST = 2, + D3DPT_LINESTRIP = 3, + D3DPT_TRIANGLELIST = 4, + D3DPT_TRIANGLESTRIP = 5, + D3DPT_TRIANGLEFAN = 6, + D3DPT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DPRIMITIVETYPE; + +/* + * Amount to add to a state to generate the override for that state. + */ +#define D3DSTATE_OVERRIDE_BIAS 256 + +/* + * A state which sets the override flag for the specified state type. + */ +#define D3DSTATE_OVERRIDE(type) (D3DRENDERSTATETYPE)(((DWORD) (type) + D3DSTATE_OVERRIDE_BIAS)) + +typedef enum _D3DTRANSFORMSTATETYPE { + D3DTRANSFORMSTATE_WORLD = 1, + D3DTRANSFORMSTATE_VIEW = 2, + D3DTRANSFORMSTATE_PROJECTION = 3, + D3DTRANSFORMSTATE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTRANSFORMSTATETYPE; + +typedef enum _D3DLIGHTSTATETYPE { + D3DLIGHTSTATE_MATERIAL = 1, + D3DLIGHTSTATE_AMBIENT = 2, + D3DLIGHTSTATE_COLORMODEL = 3, + D3DLIGHTSTATE_FOGMODE = 4, + D3DLIGHTSTATE_FOGSTART = 5, + D3DLIGHTSTATE_FOGEND = 6, + D3DLIGHTSTATE_FOGDENSITY = 7, + D3DLIGHTSTATE_COLORVERTEX = 8, + D3DLIGHTSTATE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DLIGHTSTATETYPE; + +typedef enum _D3DRENDERSTATETYPE { + D3DRENDERSTATE_TEXTUREHANDLE = 1, /* Texture handle for legacy interfaces (Texture,Texture2) */ + D3DRENDERSTATE_ANTIALIAS = 2, /* D3DANTIALIASMODE */ + D3DRENDERSTATE_TEXTUREADDRESS = 3, /* D3DTEXTUREADDRESS */ + D3DRENDERSTATE_TEXTUREPERSPECTIVE = 4, /* TRUE for perspective correction */ + D3DRENDERSTATE_WRAPU = 5, /* TRUE for wrapping in u */ + D3DRENDERSTATE_WRAPV = 6, /* TRUE for wrapping in v */ + D3DRENDERSTATE_ZENABLE = 7, /* D3DZBUFFERTYPE (or TRUE/FALSE for legacy) */ + D3DRENDERSTATE_FILLMODE = 8, /* D3DFILL_MODE */ + D3DRENDERSTATE_SHADEMODE = 9, /* D3DSHADEMODE */ + D3DRENDERSTATE_LINEPATTERN = 10, /* D3DLINEPATTERN */ + D3DRENDERSTATE_MONOENABLE = 11, /* TRUE to enable mono rasterization */ + D3DRENDERSTATE_ROP2 = 12, /* ROP2 */ + D3DRENDERSTATE_PLANEMASK = 13, /* DWORD physical plane mask */ + D3DRENDERSTATE_ZWRITEENABLE = 14, /* TRUE to enable z writes */ + D3DRENDERSTATE_ALPHATESTENABLE = 15, /* TRUE to enable alpha tests */ + D3DRENDERSTATE_LASTPIXEL = 16, /* TRUE for last-pixel on lines */ + D3DRENDERSTATE_TEXTUREMAG = 17, /* D3DTEXTUREFILTER */ + D3DRENDERSTATE_TEXTUREMIN = 18, /* D3DTEXTUREFILTER */ + D3DRENDERSTATE_SRCBLEND = 19, /* D3DBLEND */ + D3DRENDERSTATE_DESTBLEND = 20, /* D3DBLEND */ + D3DRENDERSTATE_TEXTUREMAPBLEND = 21, /* D3DTEXTUREBLEND */ + D3DRENDERSTATE_CULLMODE = 22, /* D3DCULL */ + D3DRENDERSTATE_ZFUNC = 23, /* D3DCMPFUNC */ + D3DRENDERSTATE_ALPHAREF = 24, /* D3DFIXED */ + D3DRENDERSTATE_ALPHAFUNC = 25, /* D3DCMPFUNC */ + D3DRENDERSTATE_DITHERENABLE = 26, /* TRUE to enable dithering */ + D3DRENDERSTATE_ALPHABLENDENABLE = 27, /* TRUE to enable alpha blending */ + D3DRENDERSTATE_FOGENABLE = 28, /* TRUE to enable fog */ + D3DRENDERSTATE_SPECULARENABLE = 29, /* TRUE to enable specular */ + D3DRENDERSTATE_ZVISIBLE = 30, /* TRUE to enable z checking */ + D3DRENDERSTATE_SUBPIXEL = 31, /* TRUE to enable subpixel correction */ + D3DRENDERSTATE_SUBPIXELX = 32, /* TRUE to enable correction in X only */ + D3DRENDERSTATE_STIPPLEDALPHA = 33, /* TRUE to enable stippled alpha */ + D3DRENDERSTATE_FOGCOLOR = 34, /* D3DCOLOR */ + D3DRENDERSTATE_FOGTABLEMODE = 35, /* D3DFOGMODE */ + D3DRENDERSTATE_FOGTABLESTART = 36, /* Fog table start */ + D3DRENDERSTATE_FOGTABLEEND = 37, /* Fog table end */ + D3DRENDERSTATE_FOGTABLEDENSITY = 38, /* Fog table density */ + D3DRENDERSTATE_STIPPLEENABLE = 39, /* TRUE to enable stippling */ + D3DRENDERSTATE_EDGEANTIALIAS = 40, /* TRUE to enable edge antialiasing */ + D3DRENDERSTATE_COLORKEYENABLE = 41, /* TRUE to enable source colorkeyed textures */ + D3DRENDERSTATE_BORDERCOLOR = 43, /* Border color for texturing w/border */ + D3DRENDERSTATE_TEXTUREADDRESSU = 44, /* Texture addressing mode for U coordinate */ + D3DRENDERSTATE_TEXTUREADDRESSV = 45, /* Texture addressing mode for V coordinate */ + D3DRENDERSTATE_MIPMAPLODBIAS = 46, /* D3DVALUE Mipmap LOD bias */ + D3DRENDERSTATE_ZBIAS = 47, /* LONG Z bias */ + D3DRENDERSTATE_RANGEFOGENABLE = 48, /* Enables range-based fog */ + D3DRENDERSTATE_ANISOTROPY = 49, /* Max. anisotropy. 1 = no anisotropy */ + D3DRENDERSTATE_FLUSHBATCH = 50, /* Explicit flush for DP batching (DX5 Only) */ + D3DRENDERSTATE_TRANSLUCENTSORTINDEPENDENT=51, /* BOOL enable sort-independent transparency */ + D3DRENDERSTATE_STENCILENABLE = 52, /* BOOL enable/disable stenciling */ + D3DRENDERSTATE_STENCILFAIL = 53, /* D3DSTENCILOP to do if stencil test fails */ + D3DRENDERSTATE_STENCILZFAIL = 54, /* D3DSTENCILOP to do if stencil test passes and Z test fails */ + D3DRENDERSTATE_STENCILPASS = 55, /* D3DSTENCILOP to do if both stencil and Z tests pass */ + D3DRENDERSTATE_STENCILFUNC = 56, /* D3DCMPFUNC fn. Stencil Test passes if ((ref & mask) stencilfn (stencil & mask)) is true */ + D3DRENDERSTATE_STENCILREF = 57, /* Reference value used in stencil test */ + D3DRENDERSTATE_STENCILMASK = 58, /* Mask value used in stencil test */ + D3DRENDERSTATE_STENCILWRITEMASK = 59, /* Write mask applied to values written to stencil buffer */ + D3DRENDERSTATE_TEXTUREFACTOR = 60, /* D3DCOLOR used for multi-texture blend */ + D3DRENDERSTATE_STIPPLEPATTERN00 = 64, /* Stipple pattern 01... */ + D3DRENDERSTATE_STIPPLEPATTERN01 = 65, + D3DRENDERSTATE_STIPPLEPATTERN02 = 66, + D3DRENDERSTATE_STIPPLEPATTERN03 = 67, + D3DRENDERSTATE_STIPPLEPATTERN04 = 68, + D3DRENDERSTATE_STIPPLEPATTERN05 = 69, + D3DRENDERSTATE_STIPPLEPATTERN06 = 70, + D3DRENDERSTATE_STIPPLEPATTERN07 = 71, + D3DRENDERSTATE_STIPPLEPATTERN08 = 72, + D3DRENDERSTATE_STIPPLEPATTERN09 = 73, + D3DRENDERSTATE_STIPPLEPATTERN10 = 74, + D3DRENDERSTATE_STIPPLEPATTERN11 = 75, + D3DRENDERSTATE_STIPPLEPATTERN12 = 76, + D3DRENDERSTATE_STIPPLEPATTERN13 = 77, + D3DRENDERSTATE_STIPPLEPATTERN14 = 78, + D3DRENDERSTATE_STIPPLEPATTERN15 = 79, + D3DRENDERSTATE_STIPPLEPATTERN16 = 80, + D3DRENDERSTATE_STIPPLEPATTERN17 = 81, + D3DRENDERSTATE_STIPPLEPATTERN18 = 82, + D3DRENDERSTATE_STIPPLEPATTERN19 = 83, + D3DRENDERSTATE_STIPPLEPATTERN20 = 84, + D3DRENDERSTATE_STIPPLEPATTERN21 = 85, + D3DRENDERSTATE_STIPPLEPATTERN22 = 86, + D3DRENDERSTATE_STIPPLEPATTERN23 = 87, + D3DRENDERSTATE_STIPPLEPATTERN24 = 88, + D3DRENDERSTATE_STIPPLEPATTERN25 = 89, + D3DRENDERSTATE_STIPPLEPATTERN26 = 90, + D3DRENDERSTATE_STIPPLEPATTERN27 = 91, + D3DRENDERSTATE_STIPPLEPATTERN28 = 92, + D3DRENDERSTATE_STIPPLEPATTERN29 = 93, + D3DRENDERSTATE_STIPPLEPATTERN30 = 94, + D3DRENDERSTATE_STIPPLEPATTERN31 = 95, + + /* + * 128 values [128, 255] are reserved for texture coordinate wrap flags. + * These are constructed with the D3DWRAP_U and D3DWRAP_V macros. Using + * a flags word preserves forward compatibility with texture coordinates + * that are >2D. + */ + D3DRENDERSTATE_WRAP0 = 128, /* wrap for 1st texture coord. set */ + D3DRENDERSTATE_WRAP1 = 129, /* wrap for 2nd texture coord. set */ + D3DRENDERSTATE_WRAP2 = 130, /* wrap for 3rd texture coord. set */ + D3DRENDERSTATE_WRAP3 = 131, /* wrap for 4th texture coord. set */ + D3DRENDERSTATE_WRAP4 = 132, /* wrap for 5th texture coord. set */ + D3DRENDERSTATE_WRAP5 = 133, /* wrap for 6th texture coord. set */ + D3DRENDERSTATE_WRAP6 = 134, /* wrap for 7th texture coord. set */ + D3DRENDERSTATE_WRAP7 = 135, /* wrap for 8th texture coord. set */ + D3DRENDERSTATE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DRENDERSTATETYPE; + +// For back-compatibility with legacy compilations +#define D3DRENDERSTATE_BLENDENABLE D3DRENDERSTATE_ALPHABLENDENABLE + + +// Bias to apply to the texture coordinate set to apply a wrap to. +#define D3DRENDERSTATE_WRAPBIAS 128UL + +/* Flags to construct the WRAP render states */ +#define D3DWRAP_U 0x00000001L +#define D3DWRAP_V 0x00000002L + + +#define D3DRENDERSTATE_STIPPLEPATTERN(y) (D3DRENDERSTATE_STIPPLEPATTERN00 + (y)) + +typedef struct _D3DSTATE { + union { + D3DTRANSFORMSTATETYPE dtstTransformStateType; + D3DLIGHTSTATETYPE dlstLightStateType; + D3DRENDERSTATETYPE drstRenderStateType; + }; + union { + DWORD dwArg[1]; + D3DVALUE dvArg[1]; + }; +} D3DSTATE, *LPD3DSTATE; + +/* + * Operation used to load matrices + * hDstMat = hSrcMat + */ +typedef struct _D3DMATRIXLOAD { + D3DMATRIXHANDLE hDestMatrix; /* Destination matrix */ + D3DMATRIXHANDLE hSrcMatrix; /* Source matrix */ +} D3DMATRIXLOAD, *LPD3DMATRIXLOAD; + +/* + * Operation used to multiply matrices + * hDstMat = hSrcMat1 * hSrcMat2 + */ +typedef struct _D3DMATRIXMULTIPLY { + D3DMATRIXHANDLE hDestMatrix; /* Destination matrix */ + D3DMATRIXHANDLE hSrcMatrix1; /* First source matrix */ + D3DMATRIXHANDLE hSrcMatrix2; /* Second source matrix */ +} D3DMATRIXMULTIPLY, *LPD3DMATRIXMULTIPLY; + +/* + * Operation used to transform and light vertices. + */ +typedef struct _D3DPROCESSVERTICES { + DWORD dwFlags; /* Do we transform or light or just copy? */ + WORD wStart; /* Index to first vertex in source */ + WORD wDest; /* Index to first vertex in local buffer */ + DWORD dwCount; /* Number of vertices to be processed */ + DWORD dwReserved; /* Must be zero */ +} D3DPROCESSVERTICES, *LPD3DPROCESSVERTICES; + +#define D3DPROCESSVERTICES_TRANSFORMLIGHT 0x00000000L +#define D3DPROCESSVERTICES_TRANSFORM 0x00000001L +#define D3DPROCESSVERTICES_COPY 0x00000002L +#define D3DPROCESSVERTICES_OPMASK 0x00000007L + +#define D3DPROCESSVERTICES_UPDATEEXTENTS 0x00000008L +#define D3DPROCESSVERTICES_NOCOLOR 0x00000010L + + + + +/* + * State enumerants for per-stage texture processing. + */ +typedef enum _D3DTEXTURESTAGESTATETYPE +{ + D3DTSS_COLOROP = 1, /* D3DTEXTUREOP - per-stage blending controls for color channels */ + D3DTSS_COLORARG1 = 2, /* D3DTA_* (texture arg) */ + D3DTSS_COLORARG2 = 3, /* D3DTA_* (texture arg) */ + D3DTSS_ALPHAOP = 4, /* D3DTEXTUREOP - per-stage blending controls for alpha channel */ + D3DTSS_ALPHAARG1 = 5, /* D3DTA_* (texture arg) */ + D3DTSS_ALPHAARG2 = 6, /* D3DTA_* (texture arg) */ + D3DTSS_BUMPENVMAT00 = 7, /* D3DVALUE (bump mapping matrix) */ + D3DTSS_BUMPENVMAT01 = 8, /* D3DVALUE (bump mapping matrix) */ + D3DTSS_BUMPENVMAT10 = 9, /* D3DVALUE (bump mapping matrix) */ + D3DTSS_BUMPENVMAT11 = 10, /* D3DVALUE (bump mapping matrix) */ + D3DTSS_TEXCOORDINDEX = 11, /* identifies which set of texture coordinates index this texture */ + D3DTSS_ADDRESS = 12, /* D3DTEXTUREADDRESS for both coordinates */ + D3DTSS_ADDRESSU = 13, /* D3DTEXTUREADDRESS for U coordinate */ + D3DTSS_ADDRESSV = 14, /* D3DTEXTUREADDRESS for V coordinate */ + D3DTSS_BORDERCOLOR = 15, /* D3DCOLOR */ + D3DTSS_MAGFILTER = 16, /* D3DTEXTUREMAGFILTER filter to use for magnification */ + D3DTSS_MINFILTER = 17, /* D3DTEXTUREMINFILTER filter to use for minification */ + D3DTSS_MIPFILTER = 18, /* D3DTEXTUREMIPFILTER filter to use between mipmaps during minification */ + D3DTSS_MIPMAPLODBIAS = 19, /* D3DVALUE Mipmap LOD bias */ + D3DTSS_MAXMIPLEVEL = 20, /* DWORD 0..(n-1) LOD index of largest map to use (0 == largest) */ + D3DTSS_MAXANISOTROPY = 21, /* DWORD maximum anisotropy */ + D3DTSS_BUMPENVLSCALE = 22, /* D3DVALUE scale for bump map luminance */ + D3DTSS_BUMPENVLOFFSET = 23, /* D3DVALUE offset for bump map luminance */ + D3DTSS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTURESTAGESTATETYPE; + +/* + * Enumerations for COLOROP and ALPHAOP texture blending operations set in + * texture processing stage controls in D3DRENDERSTATE. + */ +typedef enum _D3DTEXTUREOP +{ +// Control + D3DTOP_DISABLE = 1, // disables stage + D3DTOP_SELECTARG1 = 2, // the default + D3DTOP_SELECTARG2 = 3, + +// Modulate + D3DTOP_MODULATE = 4, // multiply args together + D3DTOP_MODULATE2X = 5, // multiply and 1 bit + D3DTOP_MODULATE4X = 6, // multiply and 2 bits + +// Add + D3DTOP_ADD = 7, // add arguments together + D3DTOP_ADDSIGNED = 8, // add with -0.5 bias + D3DTOP_ADDSIGNED2X = 9, // as above but left 1 bit + D3DTOP_SUBTRACT = 10, // Arg1 - Arg2, with no saturation + D3DTOP_ADDSMOOTH = 11, // add 2 args, subtract product + // Arg1 + Arg2 - Arg1*Arg2 + // = Arg1 + (1-Arg1)*Arg2 + +// Linear alpha blend: Arg1*(Alpha) + Arg2*(1-Alpha) + D3DTOP_BLENDDIFFUSEALPHA = 12, // iterated alpha + D3DTOP_BLENDTEXTUREALPHA = 13, // texture alpha + D3DTOP_BLENDFACTORALPHA = 14, // alpha from D3DRENDERSTATE_TEXTUREFACTOR + // Linear alpha blend with pre-multiplied arg1 input: Arg1 + Arg2*(1-Alpha) + D3DTOP_BLENDTEXTUREALPHAPM = 15, // texture alpha + D3DTOP_BLENDCURRENTALPHA = 16, // by alpha of current color + +// Specular mapping + D3DTOP_PREMODULATE = 17, // modulate with next texture before use + D3DTOP_MODULATEALPHA_ADDCOLOR = 18, // Arg1.RGB + Arg1.A*Arg2.RGB + // COLOROP only + D3DTOP_MODULATECOLOR_ADDALPHA = 19, // Arg1.RGB*Arg2.RGB + Arg1.A + // COLOROP only + D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20, // (1-Arg1.A)*Arg2.RGB + Arg1.RGB + // COLOROP only + D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21, // (1-Arg1.RGB)*Arg2.RGB + Arg1.A + // COLOROP only + +// Bump mapping + D3DTOP_BUMPENVMAP = 22, // per pixel env map perturbation + D3DTOP_BUMPENVMAPLUMINANCE = 23, // with luminance channel + // This can do either diffuse or specular bump mapping with correct input. + // Performs the function (Arg1.R*Arg2.R + Arg1.G*Arg2.G + Arg1.B*Arg2.B) + // where each component has been scaled and offset to make it signed. + // The result is replicated into all four (including alpha) channels. + // This is a valid COLOROP only. + D3DTOP_DOTPRODUCT3 = 24, + + D3DTOP_FORCE_DWORD = 0x7fffffff, +} D3DTEXTUREOP; + +/* + * Values for COLORARG1,2 and ALPHAARG1,2 texture blending operations + * set in texture processing stage controls in D3DRENDERSTATE. + */ +#define D3DTA_SELECTMASK 0x0000000f // mask for arg selector +#define D3DTA_DIFFUSE 0x00000000 // select diffuse color +#define D3DTA_CURRENT 0x00000001 // select result of previous stage +#define D3DTA_TEXTURE 0x00000002 // select texture color +#define D3DTA_TFACTOR 0x00000003 // select RENDERSTATE_TEXTUREFACTOR + +#define D3DTA_COMPLEMENT 0x00000010 // take 1.0 - x +#define D3DTA_ALPHAREPLICATE 0x00000020 // replicate alpha to color components + +/* + * IDirect3DTexture2 State Filter Types + */ +typedef enum _D3DTEXTUREMAGFILTER +{ + D3DTFG_POINT = 1, // nearest + D3DTFG_LINEAR = 2, // linear interpolation + D3DTFG_FLATCUBIC = 3, // cubic + D3DTFG_GAUSSIANCUBIC = 4, // different cubic kernel + D3DTFG_ANISOTROPIC = 5, // + D3DTFG_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} D3DTEXTUREMAGFILTER; + +typedef enum _D3DTEXTUREMINFILTER +{ + D3DTFN_POINT = 1, // nearest + D3DTFN_LINEAR = 2, // linear interpolation + D3DTFN_ANISOTROPIC = 3, // + D3DTFN_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} D3DTEXTUREMINFILTER; + +typedef enum _D3DTEXTUREMIPFILTER +{ + D3DTFP_NONE = 1, // mipmapping disabled (use MAG filter) + D3DTFP_POINT = 2, // nearest + D3DTFP_LINEAR = 3, // linear interpolation + D3DTFP_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} D3DTEXTUREMIPFILTER; + + +/* + * Triangle flags + */ + +/* + * Tri strip and fan flags. + * START loads all three vertices + * EVEN and ODD load just v3 with even or odd culling + * START_FLAT contains a count from 0 to 29 that allows the + * whole strip or fan to be culled in one hit. + * e.g. for a quad len = 1 + */ +#define D3DTRIFLAG_START 0x00000000L +#define D3DTRIFLAG_STARTFLAT(len) (len) /* 0 < len < 30 */ +#define D3DTRIFLAG_ODD 0x0000001eL +#define D3DTRIFLAG_EVEN 0x0000001fL + +/* + * Triangle edge flags + * enable edges for wireframe or antialiasing + */ +#define D3DTRIFLAG_EDGEENABLE1 0x00000100L /* v0-v1 edge */ +#define D3DTRIFLAG_EDGEENABLE2 0x00000200L /* v1-v2 edge */ +#define D3DTRIFLAG_EDGEENABLE3 0x00000400L /* v2-v0 edge */ +#define D3DTRIFLAG_EDGEENABLETRIANGLE \ + (D3DTRIFLAG_EDGEENABLE1 | D3DTRIFLAG_EDGEENABLE2 | D3DTRIFLAG_EDGEENABLE3) + +/* + * Primitive structures and related defines. Vertex offsets are to types + * D3DVERTEX, D3DLVERTEX, or D3DTLVERTEX. + */ + +/* + * Triangle list primitive structure + */ +typedef struct _D3DTRIANGLE { + union { + WORD v1; /* Vertex indices */ + WORD wV1; + }; + union { + WORD v2; + WORD wV2; + }; + union { + WORD v3; + WORD wV3; + }; + WORD wFlags; /* Edge (and other) flags */ +} D3DTRIANGLE, *LPD3DTRIANGLE; + +/* + * Line list structure. + * The instruction count defines the number of line segments. + */ +typedef struct _D3DLINE { + union { + WORD v1; /* Vertex indices */ + WORD wV1; + }; + union { + WORD v2; + WORD wV2; + }; +} D3DLINE, *LPD3DLINE; + +/* + * Span structure + * Spans join a list of points with the same y value. + * If the y value changes, a new span is started. + */ +typedef struct _D3DSPAN { + WORD wCount; /* Number of spans */ + WORD wFirst; /* Index to first vertex */ +} D3DSPAN, *LPD3DSPAN; + +/* + * Point structure + */ +typedef struct _D3DPOINT { + WORD wCount; /* number of points */ + WORD wFirst; /* index to first vertex */ +} D3DPOINT, *LPD3DPOINT; + + +/* + * Forward branch structure. + * Mask is logically anded with the driver status mask + * if the result equals 'value', the branch is taken. + */ +typedef struct _D3DBRANCH { + DWORD dwMask; /* Bitmask against D3D status */ + DWORD dwValue; + BOOL bNegate; /* TRUE to negate comparison */ + DWORD dwOffset; /* How far to branch forward (0 for exit)*/ +} D3DBRANCH, *LPD3DBRANCH; + +/* + * Status used for set status instruction. + * The D3D status is initialised on device creation + * and is modified by all execute calls. + */ +typedef struct _D3DSTATUS { + DWORD dwFlags; /* Do we set extents or status */ + DWORD dwStatus; /* D3D status */ + D3DRECT drExtent; +} D3DSTATUS, *LPD3DSTATUS; + +#define D3DSETSTATUS_STATUS 0x00000001L +#define D3DSETSTATUS_EXTENTS 0x00000002L +#define D3DSETSTATUS_ALL (D3DSETSTATUS_STATUS | D3DSETSTATUS_EXTENTS) + +typedef struct _D3DCLIPSTATUS { + DWORD dwFlags; /* Do we set 2d extents, 3D extents or status */ + DWORD dwStatus; /* Clip status */ + float minx, maxx; /* X extents */ + float miny, maxy; /* Y extents */ + float minz, maxz; /* Z extents */ +} D3DCLIPSTATUS, *LPD3DCLIPSTATUS; + +#define D3DCLIPSTATUS_STATUS 0x00000001L +#define D3DCLIPSTATUS_EXTENTS2 0x00000002L +#define D3DCLIPSTATUS_EXTENTS3 0x00000004L + +/* + * Statistics structure + */ +typedef struct _D3DSTATS { + DWORD dwSize; + DWORD dwTrianglesDrawn; + DWORD dwLinesDrawn; + DWORD dwPointsDrawn; + DWORD dwSpansDrawn; + DWORD dwVerticesProcessed; +} D3DSTATS, *LPD3DSTATS; + +/* + * Execute options. + * When calling using D3DEXECUTE_UNCLIPPED all the primitives + * inside the buffer must be contained within the viewport. + */ +#define D3DEXECUTE_CLIPPED 0x00000001l +#define D3DEXECUTE_UNCLIPPED 0x00000002l + +typedef struct _D3DEXECUTEDATA { + DWORD dwSize; + DWORD dwVertexOffset; + DWORD dwVertexCount; + DWORD dwInstructionOffset; + DWORD dwInstructionLength; + DWORD dwHVertexOffset; + D3DSTATUS dsStatus; /* Status after execute */ +} D3DEXECUTEDATA, *LPD3DEXECUTEDATA; + +/* + * Palette flags. + * This are or'ed with the peFlags in the PALETTEENTRYs passed to DirectDraw. + */ +#define D3DPAL_FREE 0x00 /* Renderer may use this entry freely */ +#define D3DPAL_READONLY 0x40 /* Renderer may not set this entry */ +#define D3DPAL_RESERVED 0x80 /* Renderer may not use this entry */ + + + +typedef struct _D3DVERTEXBUFFERDESC { + DWORD dwSize; + DWORD dwCaps; + DWORD dwFVF; + DWORD dwNumVertices; +} D3DVERTEXBUFFERDESC, *LPD3DVERTEXBUFFERDESC; + +/* These correspond to DDSCAPS_* flags */ +#define D3DVBCAPS_SYSTEMMEMORY 0x00000800l +#define D3DVBCAPS_WRITEONLY 0x00010000l +#define D3DVBCAPS_OPTIMIZED 0x80000000l + +/* Vertex Operations for ProcessVertices */ +#define D3DVOP_LIGHT (1 << 10) +#define D3DVOP_TRANSFORM (1 << 0) +#define D3DVOP_CLIP (1 << 2) +#define D3DVOP_EXTENTS (1 << 3) + +//------------------------------------------------------------------- + +// Flexible vertex format bits +// +#define D3DFVF_RESERVED0 0x001 +#define D3DFVF_POSITION_MASK 0x00E +#define D3DFVF_XYZ 0x002 +#define D3DFVF_XYZRHW 0x004 +#define D3DFVF_NORMAL 0x010 +#define D3DFVF_RESERVED1 0x020 +#define D3DFVF_DIFFUSE 0x040 +#define D3DFVF_SPECULAR 0x080 + +#define D3DFVF_TEXCOUNT_MASK 0xf00 +#define D3DFVF_TEXCOUNT_SHIFT 8 +#define D3DFVF_TEX0 0x000 +#define D3DFVF_TEX1 0x100 +#define D3DFVF_TEX2 0x200 +#define D3DFVF_TEX3 0x300 +#define D3DFVF_TEX4 0x400 +#define D3DFVF_TEX5 0x500 +#define D3DFVF_TEX6 0x600 +#define D3DFVF_TEX7 0x700 +#define D3DFVF_TEX8 0x800 + +#define D3DFVF_RESERVED2 0xf000 // 4 reserved bits + +#define D3DFVF_VERTEX ( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 ) +#define D3DFVF_LVERTEX ( D3DFVF_XYZ | D3DFVF_RESERVED1 | D3DFVF_DIFFUSE | \ + D3DFVF_SPECULAR | D3DFVF_TEX1 ) +#define D3DFVF_TLVERTEX ( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | \ + D3DFVF_TEX1 ) + +typedef struct _D3DDP_PTRSTRIDE +{ + LPVOID lpvData; + DWORD dwStride; +} D3DDP_PTRSTRIDE; + +#define D3DDP_MAXTEXCOORD 8 + +typedef struct _D3DDRAWPRIMITIVESTRIDEDDATA +{ + D3DDP_PTRSTRIDE position; + D3DDP_PTRSTRIDE normal; + D3DDP_PTRSTRIDE diffuse; + D3DDP_PTRSTRIDE specular; + D3DDP_PTRSTRIDE textureCoords[D3DDP_MAXTEXCOORD]; +} D3DDRAWPRIMITIVESTRIDEDDATA, *LPD3DDRAWPRIMITIVESTRIDEDDATA; +//--------------------------------------------------------------------- +// ComputeSphereVisibility return values +// +#define D3DVIS_INSIDE_FRUSTUM 0 +#define D3DVIS_INTERSECT_FRUSTUM 1 +#define D3DVIS_OUTSIDE_FRUSTUM 2 +#define D3DVIS_INSIDE_LEFT 0 +#define D3DVIS_INTERSECT_LEFT (1 << 2) +#define D3DVIS_OUTSIDE_LEFT (2 << 2) +#define D3DVIS_INSIDE_RIGHT 0 +#define D3DVIS_INTERSECT_RIGHT (1 << 4) +#define D3DVIS_OUTSIDE_RIGHT (2 << 4) +#define D3DVIS_INSIDE_TOP 0 +#define D3DVIS_INTERSECT_TOP (1 << 6) +#define D3DVIS_OUTSIDE_TOP (2 << 6) +#define D3DVIS_INSIDE_BOTTOM 0 +#define D3DVIS_INTERSECT_BOTTOM (1 << 8) +#define D3DVIS_OUTSIDE_BOTTOM (2 << 8) +#define D3DVIS_INSIDE_NEAR 0 +#define D3DVIS_INTERSECT_NEAR (1 << 10) +#define D3DVIS_OUTSIDE_NEAR (2 << 10) +#define D3DVIS_INSIDE_FAR 0 +#define D3DVIS_INTERSECT_FAR (1 << 12) +#define D3DVIS_OUTSIDE_FAR (2 << 12) + +#define D3DVIS_MASK_FRUSTUM (3 << 0) +#define D3DVIS_MASK_LEFT (3 << 2) +#define D3DVIS_MASK_RIGHT (3 << 4) +#define D3DVIS_MASK_TOP (3 << 6) +#define D3DVIS_MASK_BOTTOM (3 << 8) +#define D3DVIS_MASK_NEAR (3 << 10) +#define D3DVIS_MASK_FAR (3 << 12) + + +#pragma pack() +#endif /* _D3DTYPES_H_ */ + diff --git a/lib/win/DirectX/ddraw.h b/lib/win/DirectX/ddraw.h new file mode 100644 index 000000000..feaec6981 --- /dev/null +++ b/lib/win/DirectX/ddraw.h @@ -0,0 +1,4844 @@ +/*==========================================================================; + * + * Copyright (C) 1994-1997 Microsoft Corporation. All Rights Reserved. + * + * File: ddraw.h + * Content: DirectDraw include file + * + ***************************************************************************/ + +#ifndef __DDRAW_INCLUDED__ +#define __DDRAW_INCLUDED__ + +/* + * If you wish an application built against the newest version of DirectDraw + * to run against an older DirectDraw run time then define DIRECTDRAW_VERSION + * to be the earlies version of DirectDraw you wish to run against. For, + * example if you wish an application to run against a DX 3 runtime define + * DIRECTDRAW_VERSION to be 0x0300. + */ +#ifndef DIRECTDRAW_VERSION +#define DIRECTDRAW_VERSION 0x0600 +#endif /* DIRECTDRAW_VERSION */ + +#if defined( _WIN32 ) && !defined( _NO_COM ) +#define COM_NO_WINDOWS_H +#include +#else +#define IUnknown void +#if !defined( NT_BUILD_ENVIRONMENT ) && !defined(WINNT) + #define CO_E_NOTINITIALIZED 0x800401F0L +#endif +#endif + +#define _FACDD 0x876 +#define MAKE_DDHRESULT( code ) MAKE_HRESULT( 1, _FACDD, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +// +// For compilers that don't support nameless unions, do a +// +// #define NONAMELESSUNION +// +// before #include +// +#ifndef DUMMYUNIONNAMEN +#if defined(__cplusplus) || !defined(NONAMELESSUNION) +#define DUMMYUNIONNAMEN(n) +#else +#define DUMMYUNIONNAMEN(n) u##n +#endif +#endif + +#ifndef MAKEFOURCC + #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \ + ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 )) +#endif //defined(MAKEFOURCC) + +/* + * FOURCC codes for DX compressed-texture pixel formats + */ +#define FOURCC_DXT1 (MAKEFOURCC('D','X','T','1')) +#define FOURCC_DXT2 (MAKEFOURCC('D','X','T','2')) +#define FOURCC_DXT3 (MAKEFOURCC('D','X','T','3')) +#define FOURCC_DXT4 (MAKEFOURCC('D','X','T','4')) +#define FOURCC_DXT5 (MAKEFOURCC('D','X','T','5')) + +/* + * GUIDS used by DirectDraw objects + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) + +DEFINE_GUID( CLSID_DirectDraw, 0xD7B70EE0,0x4340,0x11CF,0xB0,0x63,0x00,0x20,0xAF,0xC2,0xCD,0x35 ); +DEFINE_GUID( CLSID_DirectDrawClipper, 0x593817A0,0x7DB3,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xb9,0x33,0x56 ); +DEFINE_GUID( IID_IDirectDraw, 0x6C14DB80,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirectDraw4, 0x9c59509a,0x39bd,0x11d1,0x8c,0x4a,0x00,0xc0,0x4f,0xd9,0x30,0xc5 ); +DEFINE_GUID( IID_IDirectDrawSurface, 0x6C14DB81,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 ); +DEFINE_GUID( IID_IDirectDrawSurface3, 0xDA044E00,0x69B2,0x11D0,0xA1,0xD5,0x00,0xAA,0x00,0xB8,0xDF,0xBB ); +DEFINE_GUID( IID_IDirectDrawSurface4, 0x0B2B8630,0xAD35,0x11D0,0x8E,0xA6,0x00,0x60,0x97,0x97,0xEA,0x5B ); + +DEFINE_GUID( IID_IDirectDrawPalette, 0x6C14DB84,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDrawClipper, 0x6C14DB85,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDrawColorControl, 0x4B9F0EE0,0x0D7E,0x11D0,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8 ); +DEFINE_GUID( IID_IDirectDrawGammaControl, 0x69C11C3E,0xB46B,0x11D1,0xAD,0x7A,0x00,0xC0,0x4F,0xC2,0x9B,0x4E ); + +#endif + +/*============================================================================ + * + * DirectDraw Structures + * + * Various structures used to invoke DirectDraw. + * + *==========================================================================*/ + +struct IDirectDraw; +struct IDirectDrawSurface; +struct IDirectDrawPalette; +struct IDirectDrawClipper; + +typedef struct IDirectDraw FAR *LPDIRECTDRAW; +typedef struct IDirectDraw2 FAR *LPDIRECTDRAW2; +typedef struct IDirectDraw4 FAR *LPDIRECTDRAW4; +typedef struct IDirectDrawSurface FAR *LPDIRECTDRAWSURFACE; +typedef struct IDirectDrawSurface2 FAR *LPDIRECTDRAWSURFACE2; +typedef struct IDirectDrawSurface3 FAR *LPDIRECTDRAWSURFACE3; +typedef struct IDirectDrawSurface4 FAR *LPDIRECTDRAWSURFACE4; + +typedef struct IDirectDrawPalette FAR *LPDIRECTDRAWPALETTE; +typedef struct IDirectDrawClipper FAR *LPDIRECTDRAWCLIPPER; +typedef struct IDirectDrawColorControl FAR *LPDIRECTDRAWCOLORCONTROL; +typedef struct IDirectDrawGammaControl FAR *LPDIRECTDRAWGAMMACONTROL; + +typedef struct _DDFXROP FAR *LPDDFXROP; +typedef struct _DDSURFACEDESC FAR *LPDDSURFACEDESC; +typedef struct _DDSURFACEDESC2 FAR *LPDDSURFACEDESC2; +typedef struct _DDCOLORCONTROL FAR *LPDDCOLORCONTROL; + +/* + * API's + */ +#if (defined (WIN32) || defined( _WIN32 ) ) && !defined( _NO_COM ) +//#if defined( _WIN32 ) && !defined( _NO_ENUM ) + typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKA)(GUID FAR *, LPSTR, LPSTR, LPVOID); + typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKW)(GUID FAR *, LPWSTR, LPWSTR, LPVOID); + extern HRESULT WINAPI DirectDrawEnumerateW( LPDDENUMCALLBACKW lpCallback, LPVOID lpContext ); + extern HRESULT WINAPI DirectDrawEnumerateA( LPDDENUMCALLBACKA lpCallback, LPVOID lpContext ); + /* + * Protect against old SDKs + */ + #ifndef SM_CMONITORS + #define HMONITOR HANDLE + #endif + typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HMONITOR); + typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXW)(GUID FAR *, LPWSTR, LPWSTR, LPVOID, HMONITOR); + extern HRESULT WINAPI DirectDrawEnumerateExW( LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags); + extern HRESULT WINAPI DirectDrawEnumerateExA( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags); + typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEXA)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags); + typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEXW)( LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags); + + #ifdef UNICODE + typedef LPDDENUMCALLBACKW LPDDENUMCALLBACK; + #define DirectDrawEnumerate DirectDrawEnumerateW + typedef LPDDENUMCALLBACKEXW LPDDENUMCALLBACKEX; + typedef LPDIRECTDRAWENUMERATEEXW LPDIRECTDRAWENUMERATEEX; + #define DirectDrawEnumerateEx DirectDrawEnumerateExW + #else + typedef LPDDENUMCALLBACKA LPDDENUMCALLBACK; + #define DirectDrawEnumerate DirectDrawEnumerateA + typedef LPDDENUMCALLBACKEXA LPDDENUMCALLBACKEX; + typedef LPDIRECTDRAWENUMERATEEXA LPDIRECTDRAWENUMERATEEX; + #define DirectDrawEnumerateEx DirectDrawEnumerateExA + #endif + extern HRESULT WINAPI DirectDrawCreate( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter ); + extern HRESULT WINAPI DirectDrawCreateClipper( DWORD dwFlags, LPDIRECTDRAWCLIPPER FAR *lplpDDClipper, IUnknown FAR *pUnkOuter ); +#endif +/* + * Flags for DirectDrawEnumerateEx + * DirectDrawEnumerateEx supercedes DirectDrawEnumerate. You must use GetProcAddress to + * obtain a function pointer (of type LPDIRECTDRAWENUMERATEEX) to DirectDrawEnumerateEx. + * By default, only the primary display device is enumerated. + * DirectDrawEnumerate is equivalent to DirectDrawEnumerate(,,DDENUM_NONDISPLAYDEVICES) + */ + +/* + * This flag causes enumeration of any GDI display devices which are part of + * the Windows Desktop + */ +#define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001L + +/* + * This flag causes enumeration of any GDI display devices which are not + * part of the Windows Desktop + */ +#define DDENUM_DETACHEDSECONDARYDEVICES 0x00000002L + +/* + * This flag causes enumeration of non-display devices + */ +#define DDENUM_NONDISPLAYDEVICES 0x00000004L + + +#define REGSTR_KEY_DDHW_DESCRIPTION "Description" +#define REGSTR_KEY_DDHW_DRIVERNAME "DriverName" +#define REGSTR_PATH_DDHW "Hardware\\DirectDrawDrivers" + +#define DDCREATE_HARDWAREONLY 0x00000001l +#define DDCREATE_EMULATIONONLY 0x00000002l + +#if defined(WINNT) || !defined(WIN32) +typedef long HRESULT; +#endif + +//#ifndef WINNT +typedef HRESULT (FAR PASCAL * LPDDENUMMODESCALLBACK)(LPDDSURFACEDESC, LPVOID); +typedef HRESULT (FAR PASCAL * LPDDENUMMODESCALLBACK2)(LPDDSURFACEDESC2, LPVOID); +typedef HRESULT (FAR PASCAL * LPDDENUMSURFACESCALLBACK)(LPDIRECTDRAWSURFACE, LPDDSURFACEDESC, LPVOID); +typedef HRESULT (FAR PASCAL * LPDDENUMSURFACESCALLBACK2)(LPDIRECTDRAWSURFACE4, LPDDSURFACEDESC2, LPVOID); +//#endif + +/* + * Generic pixel format with 8-bit RGB and alpha components + */ +typedef struct _DDRGBA +{ + BYTE red; + BYTE green; + BYTE blue; + BYTE alpha; +} DDRGBA; + +typedef DDRGBA FAR *LPDDRGBA; + +/* + * DDCOLORKEY + */ +typedef struct _DDCOLORKEY +{ + DWORD dwColorSpaceLowValue; // low boundary of color space that is to + // be treated as Color Key, inclusive + DWORD dwColorSpaceHighValue; // high boundary of color space that is + // to be treated as Color Key, inclusive +} DDCOLORKEY; + +typedef DDCOLORKEY FAR* LPDDCOLORKEY; + +/* + * DDBLTFX + * Used to pass override information to the DIRECTDRAWSURFACE callback Blt. + */ +typedef struct _DDBLTFX +{ + DWORD dwSize; // size of structure + DWORD dwDDFX; // FX operations + DWORD dwROP; // Win32 raster operations + DWORD dwDDROP; // Raster operations new for DirectDraw + DWORD dwRotationAngle; // Rotation angle for blt + DWORD dwZBufferOpCode; // ZBuffer compares + DWORD dwZBufferLow; // Low limit of Z buffer + DWORD dwZBufferHigh; // High limit of Z buffer + DWORD dwZBufferBaseDest; // Destination base value + DWORD dwZDestConstBitDepth; // Bit depth used to specify Z constant for destination + union + { + DWORD dwZDestConst; // Constant to use as Z buffer for dest + LPDIRECTDRAWSURFACE lpDDSZBufferDest; // Surface to use as Z buffer for dest + } DUMMYUNIONNAMEN(1); + DWORD dwZSrcConstBitDepth; // Bit depth used to specify Z constant for source + union + { + DWORD dwZSrcConst; // Constant to use as Z buffer for src + LPDIRECTDRAWSURFACE lpDDSZBufferSrc; // Surface to use as Z buffer for src + } DUMMYUNIONNAMEN(2); + DWORD dwAlphaEdgeBlendBitDepth; // Bit depth used to specify constant for alpha edge blend + DWORD dwAlphaEdgeBlend; // Alpha for edge blending + DWORD dwReserved; + DWORD dwAlphaDestConstBitDepth; // Bit depth used to specify alpha constant for destination + union + { + DWORD dwAlphaDestConst; // Constant to use as Alpha Channel + LPDIRECTDRAWSURFACE lpDDSAlphaDest; // Surface to use as Alpha Channel + } DUMMYUNIONNAMEN(3); + DWORD dwAlphaSrcConstBitDepth; // Bit depth used to specify alpha constant for source + union + { + DWORD dwAlphaSrcConst; // Constant to use as Alpha Channel + LPDIRECTDRAWSURFACE lpDDSAlphaSrc; // Surface to use as Alpha Channel + } DUMMYUNIONNAMEN(4); + union + { + DWORD dwFillColor; // color in RGB or Palettized + DWORD dwFillDepth; // depth value for z-buffer + DWORD dwFillPixel; // pixel value for RGBA or RGBZ + LPDIRECTDRAWSURFACE lpDDSPattern; // Surface to use as pattern + } DUMMYUNIONNAMEN(5); + DDCOLORKEY ddckDestColorkey; // DestColorkey override + DDCOLORKEY ddckSrcColorkey; // SrcColorkey override +} DDBLTFX; + +typedef DDBLTFX FAR* LPDDBLTFX; + + +/* + * DDSCAPS + */ +typedef struct _DDSCAPS +{ + DWORD dwCaps; // capabilities of surface wanted +} DDSCAPS; + +typedef DDSCAPS FAR* LPDDSCAPS; + + +/* + * DDOSCAPS + */ +typedef struct _DDOSCAPS +{ + DWORD dwCaps; // capabilities of surface wanted +} DDOSCAPS; + +typedef DDOSCAPS FAR* LPDDOSCAPS; + +/* + * This structure is used internally by DirectDraw. + */ +typedef struct _DDSCAPSEX +{ + DWORD dwCaps2; + DWORD dwCaps3; + DWORD dwCaps4; +} DDSCAPSEX, FAR * LPDDSCAPSEX; + +/* + * DDSCAPS2 + */ +typedef struct _DDSCAPS2 +{ + DWORD dwCaps; // capabilities of surface wanted + DWORD dwCaps2; + DWORD dwCaps3; + DWORD dwCaps4; +} DDSCAPS2; + +typedef DDSCAPS2 FAR* LPDDSCAPS2; + +/* + * DDCAPS + */ +#define DD_ROP_SPACE (256/32) // space required to store ROP array + +/* + * This structure is the DDCAPS structure as it was in version 2 and 3 of Direct X. + * It is present for back compatability. + */ +typedef struct _DDCAPS_DX3 +{ + DWORD dwSize; // size of the DDDRIVERCAPS structure + DWORD dwCaps; // driver specific capabilities + DWORD dwCaps2; // more driver specific capabilites + DWORD dwCKeyCaps; // color key capabilities of the surface + DWORD dwFXCaps; // driver specific stretching and effects capabilites + DWORD dwFXAlphaCaps; // alpha driver specific capabilities + DWORD dwPalCaps; // palette capabilities + DWORD dwSVCaps; // stereo vision capabilities + DWORD dwAlphaBltConstBitDepths; // DDBD_2,4,8 + DWORD dwAlphaBltPixelBitDepths; // DDBD_1,2,4,8 + DWORD dwAlphaBltSurfaceBitDepths; // DDBD_1,2,4,8 + DWORD dwAlphaOverlayConstBitDepths; // DDBD_2,4,8 + DWORD dwAlphaOverlayPixelBitDepths; // DDBD_1,2,4,8 + DWORD dwAlphaOverlaySurfaceBitDepths; // DDBD_1,2,4,8 + DWORD dwZBufferBitDepths; // DDBD_8,16,24,32 + DWORD dwVidMemTotal; // total amount of video memory + DWORD dwVidMemFree; // amount of free video memory + DWORD dwMaxVisibleOverlays; // maximum number of visible overlays + DWORD dwCurrVisibleOverlays; // current number of visible overlays + DWORD dwNumFourCCCodes; // number of four cc codes + DWORD dwAlignBoundarySrc; // source rectangle alignment + DWORD dwAlignSizeSrc; // source rectangle byte size + DWORD dwAlignBoundaryDest; // dest rectangle alignment + DWORD dwAlignSizeDest; // dest rectangle byte size + DWORD dwAlignStrideAlign; // stride alignment + DWORD dwRops[DD_ROP_SPACE]; // ROPS supported + DDSCAPS ddsCaps; // DDSCAPS structure has all the general capabilities + DWORD dwMinOverlayStretch; // minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMaxOverlayStretch; // maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMinLiveVideoStretch; // minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMaxLiveVideoStretch; // maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMinHwCodecStretch; // minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMaxHwCodecStretch; // maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwReserved1; // reserved + DWORD dwReserved2; // reserved + DWORD dwReserved3; // reserved + DWORD dwSVBCaps; // driver specific capabilities for System->Vmem blts + DWORD dwSVBCKeyCaps; // driver color key capabilities for System->Vmem blts + DWORD dwSVBFXCaps; // driver FX capabilities for System->Vmem blts + DWORD dwSVBRops[DD_ROP_SPACE];// ROPS supported for System->Vmem blts + DWORD dwVSBCaps; // driver specific capabilities for Vmem->System blts + DWORD dwVSBCKeyCaps; // driver color key capabilities for Vmem->System blts + DWORD dwVSBFXCaps; // driver FX capabilities for Vmem->System blts + DWORD dwVSBRops[DD_ROP_SPACE];// ROPS supported for Vmem->System blts + DWORD dwSSBCaps; // driver specific capabilities for System->System blts + DWORD dwSSBCKeyCaps; // driver color key capabilities for System->System blts + DWORD dwSSBFXCaps; // driver FX capabilities for System->System blts + DWORD dwSSBRops[DD_ROP_SPACE];// ROPS supported for System->System blts + DWORD dwReserved4; // reserved + DWORD dwReserved5; // reserved + DWORD dwReserved6; // reserved +} DDCAPS_DX3; +typedef DDCAPS_DX3 FAR* LPDDCAPS_DX3; + +/* + * This structure is the DDCAPS structure as it was in version 5 of Direct X. + * It is present for back compatability. + */ +typedef struct _DDCAPS_DX5 +{ +/* 0*/ DWORD dwSize; // size of the DDDRIVERCAPS structure +/* 4*/ DWORD dwCaps; // driver specific capabilities +/* 8*/ DWORD dwCaps2; // more driver specific capabilites +/* c*/ DWORD dwCKeyCaps; // color key capabilities of the surface +/* 10*/ DWORD dwFXCaps; // driver specific stretching and effects capabilites +/* 14*/ DWORD dwFXAlphaCaps; // alpha driver specific capabilities +/* 18*/ DWORD dwPalCaps; // palette capabilities +/* 1c*/ DWORD dwSVCaps; // stereo vision capabilities +/* 20*/ DWORD dwAlphaBltConstBitDepths; // DDBD_2,4,8 +/* 24*/ DWORD dwAlphaBltPixelBitDepths; // DDBD_1,2,4,8 +/* 28*/ DWORD dwAlphaBltSurfaceBitDepths; // DDBD_1,2,4,8 +/* 2c*/ DWORD dwAlphaOverlayConstBitDepths; // DDBD_2,4,8 +/* 30*/ DWORD dwAlphaOverlayPixelBitDepths; // DDBD_1,2,4,8 +/* 34*/ DWORD dwAlphaOverlaySurfaceBitDepths; // DDBD_1,2,4,8 +/* 38*/ DWORD dwZBufferBitDepths; // DDBD_8,16,24,32 +/* 3c*/ DWORD dwVidMemTotal; // total amount of video memory +/* 40*/ DWORD dwVidMemFree; // amount of free video memory +/* 44*/ DWORD dwMaxVisibleOverlays; // maximum number of visible overlays +/* 48*/ DWORD dwCurrVisibleOverlays; // current number of visible overlays +/* 4c*/ DWORD dwNumFourCCCodes; // number of four cc codes +/* 50*/ DWORD dwAlignBoundarySrc; // source rectangle alignment +/* 54*/ DWORD dwAlignSizeSrc; // source rectangle byte size +/* 58*/ DWORD dwAlignBoundaryDest; // dest rectangle alignment +/* 5c*/ DWORD dwAlignSizeDest; // dest rectangle byte size +/* 60*/ DWORD dwAlignStrideAlign; // stride alignment +/* 64*/ DWORD dwRops[DD_ROP_SPACE]; // ROPS supported +/* 84*/ DDSCAPS ddsCaps; // DDSCAPS structure has all the general capabilities +/* 88*/ DWORD dwMinOverlayStretch; // minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 8c*/ DWORD dwMaxOverlayStretch; // maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 90*/ DWORD dwMinLiveVideoStretch; // minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 94*/ DWORD dwMaxLiveVideoStretch; // maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 98*/ DWORD dwMinHwCodecStretch; // minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 9c*/ DWORD dwMaxHwCodecStretch; // maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* a0*/ DWORD dwReserved1; // reserved +/* a4*/ DWORD dwReserved2; // reserved +/* a8*/ DWORD dwReserved3; // reserved +/* ac*/ DWORD dwSVBCaps; // driver specific capabilities for System->Vmem blts +/* b0*/ DWORD dwSVBCKeyCaps; // driver color key capabilities for System->Vmem blts +/* b4*/ DWORD dwSVBFXCaps; // driver FX capabilities for System->Vmem blts +/* b8*/ DWORD dwSVBRops[DD_ROP_SPACE];// ROPS supported for System->Vmem blts +/* d8*/ DWORD dwVSBCaps; // driver specific capabilities for Vmem->System blts +/* dc*/ DWORD dwVSBCKeyCaps; // driver color key capabilities for Vmem->System blts +/* e0*/ DWORD dwVSBFXCaps; // driver FX capabilities for Vmem->System blts +/* e4*/ DWORD dwVSBRops[DD_ROP_SPACE];// ROPS supported for Vmem->System blts +/*104*/ DWORD dwSSBCaps; // driver specific capabilities for System->System blts +/*108*/ DWORD dwSSBCKeyCaps; // driver color key capabilities for System->System blts +/*10c*/ DWORD dwSSBFXCaps; // driver FX capabilities for System->System blts +/*110*/ DWORD dwSSBRops[DD_ROP_SPACE];// ROPS supported for System->System blts +// Members added for DX5: +/*130*/ DWORD dwMaxVideoPorts; // maximum number of usable video ports +/*134*/ DWORD dwCurrVideoPorts; // current number of video ports used +/*138*/ DWORD dwSVBCaps2; // more driver specific capabilities for System->Vmem blts +/*13c*/ DWORD dwNLVBCaps; // driver specific capabilities for non-local->local vidmem blts +/*140*/ DWORD dwNLVBCaps2; // more driver specific capabilities non-local->local vidmem blts +/*144*/ DWORD dwNLVBCKeyCaps; // driver color key capabilities for non-local->local vidmem blts +/*148*/ DWORD dwNLVBFXCaps; // driver FX capabilities for non-local->local blts +/*14c*/ DWORD dwNLVBRops[DD_ROP_SPACE]; // ROPS supported for non-local->local blts +} DDCAPS_DX5; +typedef DDCAPS_DX5 FAR* LPDDCAPS_DX5; + +typedef struct _DDCAPS_DX6 +{ +/* 0*/ DWORD dwSize; // size of the DDDRIVERCAPS structure +/* 4*/ DWORD dwCaps; // driver specific capabilities +/* 8*/ DWORD dwCaps2; // more driver specific capabilites +/* c*/ DWORD dwCKeyCaps; // color key capabilities of the surface +/* 10*/ DWORD dwFXCaps; // driver specific stretching and effects capabilites +/* 14*/ DWORD dwFXAlphaCaps; // alpha caps +/* 18*/ DWORD dwPalCaps; // palette capabilities +/* 1c*/ DWORD dwSVCaps; // stereo vision capabilities +/* 20*/ DWORD dwAlphaBltConstBitDepths; // DDBD_2,4,8 +/* 24*/ DWORD dwAlphaBltPixelBitDepths; // DDBD_1,2,4,8 +/* 28*/ DWORD dwAlphaBltSurfaceBitDepths; // DDBD_1,2,4,8 +/* 2c*/ DWORD dwAlphaOverlayConstBitDepths; // DDBD_2,4,8 +/* 30*/ DWORD dwAlphaOverlayPixelBitDepths; // DDBD_1,2,4,8 +/* 34*/ DWORD dwAlphaOverlaySurfaceBitDepths; // DDBD_1,2,4,8 +/* 38*/ DWORD dwZBufferBitDepths; // DDBD_8,16,24,32 +/* 3c*/ DWORD dwVidMemTotal; // total amount of video memory +/* 40*/ DWORD dwVidMemFree; // amount of free video memory +/* 44*/ DWORD dwMaxVisibleOverlays; // maximum number of visible overlays +/* 48*/ DWORD dwCurrVisibleOverlays; // current number of visible overlays +/* 4c*/ DWORD dwNumFourCCCodes; // number of four cc codes +/* 50*/ DWORD dwAlignBoundarySrc; // source rectangle alignment +/* 54*/ DWORD dwAlignSizeSrc; // source rectangle byte size +/* 58*/ DWORD dwAlignBoundaryDest; // dest rectangle alignment +/* 5c*/ DWORD dwAlignSizeDest; // dest rectangle byte size +/* 60*/ DWORD dwAlignStrideAlign; // stride alignment +/* 64*/ DWORD dwRops[DD_ROP_SPACE]; // ROPS supported +/* 84*/ DDSCAPS ddsOldCaps; // Was DDSCAPS ddsCaps. ddsCaps is of type DDSCAPS2 for DX6 +/* 88*/ DWORD dwMinOverlayStretch; // minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 8c*/ DWORD dwMaxOverlayStretch; // maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 90*/ DWORD dwMinLiveVideoStretch; // minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 94*/ DWORD dwMaxLiveVideoStretch; // maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 98*/ DWORD dwMinHwCodecStretch; // minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 9c*/ DWORD dwMaxHwCodecStretch; // maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* a0*/ DWORD dwReserved1; // reserved +/* a4*/ DWORD dwReserved2; // reserved +/* a8*/ DWORD dwReserved3; // reserved +/* ac*/ DWORD dwSVBCaps; // driver specific capabilities for System->Vmem blts +/* b0*/ DWORD dwSVBCKeyCaps; // driver color key capabilities for System->Vmem blts +/* b4*/ DWORD dwSVBFXCaps; // driver FX capabilities for System->Vmem blts +/* b8*/ DWORD dwSVBRops[DD_ROP_SPACE];// ROPS supported for System->Vmem blts +/* d8*/ DWORD dwVSBCaps; // driver specific capabilities for Vmem->System blts +/* dc*/ DWORD dwVSBCKeyCaps; // driver color key capabilities for Vmem->System blts +/* e0*/ DWORD dwVSBFXCaps; // driver FX capabilities for Vmem->System blts +/* e4*/ DWORD dwVSBRops[DD_ROP_SPACE];// ROPS supported for Vmem->System blts +/*104*/ DWORD dwSSBCaps; // driver specific capabilities for System->System blts +/*108*/ DWORD dwSSBCKeyCaps; // driver color key capabilities for System->System blts +/*10c*/ DWORD dwSSBFXCaps; // driver FX capabilities for System->System blts +/*110*/ DWORD dwSSBRops[DD_ROP_SPACE];// ROPS supported for System->System blts +/*130*/ DWORD dwMaxVideoPorts; // maximum number of usable video ports +/*134*/ DWORD dwCurrVideoPorts; // current number of video ports used +/*138*/ DWORD dwSVBCaps2; // more driver specific capabilities for System->Vmem blts +/*13c*/ DWORD dwNLVBCaps; // driver specific capabilities for non-local->local vidmem blts +/*140*/ DWORD dwNLVBCaps2; // more driver specific capabilities non-local->local vidmem blts +/*144*/ DWORD dwNLVBCKeyCaps; // driver color key capabilities for non-local->local vidmem blts +/*148*/ DWORD dwNLVBFXCaps; // driver FX capabilities for non-local->local blts +/*14c*/ DWORD dwNLVBRops[DD_ROP_SPACE]; // ROPS supported for non-local->local blts +// Members added for DX6 release +/*16c*/ DDSCAPS2 ddsCaps; // Surface Caps +} DDCAPS_DX6; +typedef DDCAPS_DX6 FAR* LPDDCAPS_DX6; + + +#if DIRECTDRAW_VERSION <= 0x300 + typedef DDCAPS_DX3 DDCAPS; +#elif DIRECTDRAW_VERSION <= 0x500 + typedef DDCAPS_DX5 DDCAPS; +#else + typedef DDCAPS_DX6 DDCAPS; +#endif + +typedef DDCAPS FAR* LPDDCAPS; + + + +/* + * DDPIXELFORMAT + */ +typedef struct _DDPIXELFORMAT +{ + DWORD dwSize; // size of structure + DWORD dwFlags; // pixel format flags + DWORD dwFourCC; // (FOURCC code) + union + { + DWORD dwRGBBitCount; // how many bits per pixel + DWORD dwYUVBitCount; // how many bits per pixel + DWORD dwZBufferBitDepth; // how many total bits/pixel in z buffer (including any stencil bits) + DWORD dwAlphaBitDepth; // how many bits for alpha channels + DWORD dwLuminanceBitCount; // how many bits per pixel + DWORD dwBumpBitCount; // how many bits per "buxel", total + } DUMMYUNIONNAMEN(1); + union + { + DWORD dwRBitMask; // mask for red bit + DWORD dwYBitMask; // mask for Y bits + DWORD dwStencilBitDepth; // how many stencil bits (note: dwZBufferBitDepth-dwStencilBitDepth is total Z-only bits) + DWORD dwLuminanceBitMask; // mask for luminance bits + DWORD dwBumpDuBitMask; // mask for bump map U delta bits + } DUMMYUNIONNAMEN(2); + union + { + DWORD dwGBitMask; // mask for green bits + DWORD dwUBitMask; // mask for U bits + DWORD dwZBitMask; // mask for Z bits + DWORD dwBumpDvBitMask; // mask for bump map V delta bits + } DUMMYUNIONNAMEN(3); + union + { + DWORD dwBBitMask; // mask for blue bits + DWORD dwVBitMask; // mask for V bits + DWORD dwStencilBitMask; // mask for stencil bits + DWORD dwBumpLuminanceBitMask; // mask for luminance in bump map + } DUMMYUNIONNAMEN(4); + union + { + DWORD dwRGBAlphaBitMask; // mask for alpha channel + DWORD dwYUVAlphaBitMask; // mask for alpha channel + DWORD dwLuminanceAlphaBitMask;// mask for alpha channel + DWORD dwRGBZBitMask; // mask for Z channel + DWORD dwYUVZBitMask; // mask for Z channel + } DUMMYUNIONNAMEN(5); +} DDPIXELFORMAT; + +typedef DDPIXELFORMAT FAR* LPDDPIXELFORMAT; + +/* + * DDOVERLAYFX + */ +typedef struct _DDOVERLAYFX +{ + DWORD dwSize; // size of structure + DWORD dwAlphaEdgeBlendBitDepth; // Bit depth used to specify constant for alpha edge blend + DWORD dwAlphaEdgeBlend; // Constant to use as alpha for edge blend + DWORD dwReserved; + DWORD dwAlphaDestConstBitDepth; // Bit depth used to specify alpha constant for destination + union + { + DWORD dwAlphaDestConst; // Constant to use as alpha channel for dest + LPDIRECTDRAWSURFACE lpDDSAlphaDest; // Surface to use as alpha channel for dest + } DUMMYUNIONNAMEN(1); + DWORD dwAlphaSrcConstBitDepth; // Bit depth used to specify alpha constant for source + union + { + DWORD dwAlphaSrcConst; // Constant to use as alpha channel for src + LPDIRECTDRAWSURFACE lpDDSAlphaSrc; // Surface to use as alpha channel for src + } DUMMYUNIONNAMEN(2); + DDCOLORKEY dckDestColorkey; // DestColorkey override + DDCOLORKEY dckSrcColorkey; // DestColorkey override + DWORD dwDDFX; // Overlay FX + DWORD dwFlags; // flags +} DDOVERLAYFX; + +typedef DDOVERLAYFX FAR *LPDDOVERLAYFX; + + +/* + * DDBLTBATCH: BltBatch entry structure + */ +typedef struct _DDBLTBATCH +{ + LPRECT lprDest; + LPDIRECTDRAWSURFACE lpDDSSrc; + LPRECT lprSrc; + DWORD dwFlags; + LPDDBLTFX lpDDBltFx; +} DDBLTBATCH; + +typedef DDBLTBATCH FAR * LPDDBLTBATCH; + + +/* + * DDGAMMARAMP + */ +typedef struct _DDGAMMARAMP +{ + WORD red[256]; + WORD green[256]; + WORD blue[256]; +} DDGAMMARAMP; +typedef DDGAMMARAMP FAR * LPDDGAMMARAMP; + +/* + * This is the structure within which DirectDraw returns data about the current graphics driver and chipset + */ + +#define MAX_DDDEVICEID_STRING 512 + +typedef struct tagDDDEVICEIDENTIFIER +{ + /* + * These elements are for presentation to the user only. They should not be used to identify particular + * drivers, since this is unreliable and many different strings may be associated with the same + * device, and the same driver from different vendors. + */ + char szDriver[MAX_DDDEVICEID_STRING]; + char szDescription[MAX_DDDEVICEID_STRING]; + + /* + * This element is the version of the DirectDraw/3D driver. It is legal to do <, > comparisons + * on the whole 64 bits. Caution should be exercised if you use this element to identify problematic + * drivers. It is recommended that guidDeviceIdentifier is used for this purpose. + * + * This version has the form: + * wProduct = HIWORD(liDriverVersion.HighPart) + * wVersion = LOWORD(liDriverVersion.HighPart) + * wSubVersion = HIWORD(liDriverVersion.LowPart) + * wBuild = LOWORD(liDriverVersion.LowPart) + */ +#ifdef _WIN32 + LARGE_INTEGER liDriverVersion; /* Defined for applications and other 32 bit components */ +#else + DWORD dwDriverVersionLowPart; /* Defined for 16 bit driver components */ + DWORD dwDriverVersionHighPart; +#endif + + + /* + * These elements can be used to identify particular chipsets. Use with extreme caution. + * dwVendorId Identifies the manufacturer. May be zero if unknown. + * dwDeviceId Identifies the type of chipset. May be zero if unknown. + * dwSubSysId Identifies the subsystem, typically this means the particular board. May be zero if unknown. + * dwRevision Identifies the revision level of the chipset. May be zero if unknown. + */ + DWORD dwVendorId; + DWORD dwDeviceId; + DWORD dwSubSysId; + DWORD dwRevision; + + /* + * This element can be used to check changes in driver/chipset. This GUID is a unique identifier for the + * driver/chipset pair. Use this element if you wish to track changes to the driver/chipset in order to + * reprofile the graphics subsystem. + * This element can also be used to identify particular problematic drivers. + */ + GUID guidDeviceIdentifier; +} DDDEVICEIDENTIFIER, * LPDDDEVICEIDENTIFIER; + +/* + * Flags for the IDirectDraw4::GetDeviceIdentifier method + */ + +/* + * This flag causes GetDeviceIdentifier to return information about the host (typically 2D) adapter in a system equipped + * with a stacked secondary 3D adapter. Such an adapter appears to the application as if it were part of the + * host adapter, but is typically physcially located on a separate card. The stacked secondary's information is + * returned when GetDeviceIdentifier's dwFlags field is zero, since this most accurately reflects the qualities + * of the DirectDraw object involved. + */ +#define DDGDI_GETHOSTIDENTIFIER 0x00000001L + + + +/* + * callbacks + */ +typedef DWORD (FAR PASCAL *LPCLIPPERCALLBACK)(LPDIRECTDRAWCLIPPER lpDDClipper, HWND hWnd, DWORD code, LPVOID lpContext ); +#ifdef STREAMING +typedef DWORD (FAR PASCAL *LPSURFACESTREAMINGCALLBACK)(DWORD); +#endif + + +/* + * INTERACES FOLLOW: + * IDirectDraw + * IDirectDrawClipper + * IDirectDrawPalette + * IDirectDrawSurface + */ + +/* + * IDirectDraw + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDraw +DECLARE_INTERFACE_( IDirectDraw, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDraw methods ***/ + STDMETHOD(Compact)(THIS) PURE; + STDMETHOD(CreateClipper)(THIS_ DWORD, LPDIRECTDRAWCLIPPER FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreatePalette)(THIS_ DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC, LPDIRECTDRAWSURFACE FAR *, IUnknown FAR *) PURE; + STDMETHOD(DuplicateSurface)( THIS_ LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE FAR * ) PURE; + STDMETHOD(EnumDisplayModes)( THIS_ DWORD, LPDDSURFACEDESC, LPVOID, LPDDENUMMODESCALLBACK ) PURE; + STDMETHOD(EnumSurfaces)(THIS_ DWORD, LPDDSURFACEDESC, LPVOID,LPDDENUMSURFACESCALLBACK ) PURE; + STDMETHOD(FlipToGDISurface)(THIS) PURE; + STDMETHOD(GetCaps)( THIS_ LPDDCAPS, LPDDCAPS) PURE; + STDMETHOD(GetDisplayMode)( THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD, LPDWORD ) PURE; + STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE FAR *) PURE; + STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD) PURE; + STDMETHOD(GetScanLine)(THIS_ LPDWORD) PURE; + STDMETHOD(GetVerticalBlankStatus)(THIS_ LPBOOL ) PURE; + STDMETHOD(Initialize)(THIS_ GUID FAR *) PURE; + STDMETHOD(RestoreDisplayMode)(THIS) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND, DWORD) PURE; + STDMETHOD(SetDisplayMode)(THIS_ DWORD, DWORD,DWORD) PURE; + STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD, HANDLE ) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDraw_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDraw_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDraw_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDraw_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectDraw_CreateClipper(p, a, b, c) (p)->lpVtbl->CreateClipper(p, a, b, c) +#define IDirectDraw_CreatePalette(p, a, b, c, d) (p)->lpVtbl->CreatePalette(p, a, b, c, d) +#define IDirectDraw_CreateSurface(p, a, b, c) (p)->lpVtbl->CreateSurface(p, a, b, c) +#define IDirectDraw_DuplicateSurface(p, a, b) (p)->lpVtbl->DuplicateSurface(p, a, b) +#define IDirectDraw_EnumDisplayModes(p, a, b, c, d) (p)->lpVtbl->EnumDisplayModes(p, a, b, c, d) +#define IDirectDraw_EnumSurfaces(p, a, b, c, d) (p)->lpVtbl->EnumSurfaces(p, a, b, c, d) +#define IDirectDraw_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p) +#define IDirectDraw_GetCaps(p, a, b) (p)->lpVtbl->GetCaps(p, a, b) +#define IDirectDraw_GetDisplayMode(p, a) (p)->lpVtbl->GetDisplayMode(p, a) +#define IDirectDraw_GetFourCCCodes(p, a, b) (p)->lpVtbl->GetFourCCCodes(p, a, b) +#define IDirectDraw_GetGDISurface(p, a) (p)->lpVtbl->GetGDISurface(p, a) +#define IDirectDraw_GetMonitorFrequency(p, a) (p)->lpVtbl->GetMonitorFrequency(p, a) +#define IDirectDraw_GetScanLine(p, a) (p)->lpVtbl->GetScanLine(p, a) +#define IDirectDraw_GetVerticalBlankStatus(p, a) (p)->lpVtbl->GetVerticalBlankStatus(p, a) +#define IDirectDraw_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirectDraw_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p) +#define IDirectDraw_SetCooperativeLevel(p, a, b) (p)->lpVtbl->SetCooperativeLevel(p, a, b) +#define IDirectDraw_SetDisplayMode(p, a, b, c) (p)->lpVtbl->SetDisplayMode(p, a, b, c) +#define IDirectDraw_WaitForVerticalBlank(p, a, b) (p)->lpVtbl->WaitForVerticalBlank(p, a, b) +#else +#define IDirectDraw_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDraw_AddRef(p) (p)->AddRef() +#define IDirectDraw_Release(p) (p)->Release() +#define IDirectDraw_Compact(p) (p)->Compact() +#define IDirectDraw_CreateClipper(p, a, b, c) (p)->CreateClipper(a, b, c) +#define IDirectDraw_CreatePalette(p, a, b, c, d) (p)->CreatePalette(a, b, c, d) +#define IDirectDraw_CreateSurface(p, a, b, c) (p)->CreateSurface(a, b, c) +#define IDirectDraw_DuplicateSurface(p, a, b) (p)->DuplicateSurface(a, b) +#define IDirectDraw_EnumDisplayModes(p, a, b, c, d) (p)->EnumDisplayModes(a, b, c, d) +#define IDirectDraw_EnumSurfaces(p, a, b, c, d) (p)->EnumSurfaces(a, b, c, d) +#define IDirectDraw_FlipToGDISurface(p) (p)->FlipToGDISurface() +#define IDirectDraw_GetCaps(p, a, b) (p)->GetCaps(a, b) +#define IDirectDraw_GetDisplayMode(p, a) (p)->GetDisplayMode(a) +#define IDirectDraw_GetFourCCCodes(p, a, b) (p)->GetFourCCCodes(a, b) +#define IDirectDraw_GetGDISurface(p, a) (p)->GetGDISurface(a) +#define IDirectDraw_GetMonitorFrequency(p, a) (p)->GetMonitorFrequency(a) +#define IDirectDraw_GetScanLine(p, a) (p)->GetScanLine(a) +#define IDirectDraw_GetVerticalBlankStatus(p, a) (p)->GetVerticalBlankStatus(a) +#define IDirectDraw_Initialize(p, a) (p)->Initialize(a) +#define IDirectDraw_RestoreDisplayMode(p) (p)->RestoreDisplayMode() +#define IDirectDraw_SetCooperativeLevel(p, a, b) (p)->SetCooperativeLevel(a, b) +#define IDirectDraw_SetDisplayMode(p, a, b, c) (p)->SetDisplayMode(a, b, c) +#define IDirectDraw_WaitForVerticalBlank(p, a, b) (p)->WaitForVerticalBlank(a, b) +#endif + +#endif + +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDraw2 +DECLARE_INTERFACE_( IDirectDraw2, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDraw methods ***/ + STDMETHOD(Compact)(THIS) PURE; + STDMETHOD(CreateClipper)(THIS_ DWORD, LPDIRECTDRAWCLIPPER FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreatePalette)(THIS_ DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC, LPDIRECTDRAWSURFACE FAR *, IUnknown FAR *) PURE; + STDMETHOD(DuplicateSurface)( THIS_ LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE FAR * ) PURE; + STDMETHOD(EnumDisplayModes)( THIS_ DWORD, LPDDSURFACEDESC, LPVOID, LPDDENUMMODESCALLBACK ) PURE; + STDMETHOD(EnumSurfaces)(THIS_ DWORD, LPDDSURFACEDESC, LPVOID,LPDDENUMSURFACESCALLBACK ) PURE; + STDMETHOD(FlipToGDISurface)(THIS) PURE; + STDMETHOD(GetCaps)( THIS_ LPDDCAPS, LPDDCAPS) PURE; + STDMETHOD(GetDisplayMode)( THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD, LPDWORD ) PURE; + STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE FAR *) PURE; + STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD) PURE; + STDMETHOD(GetScanLine)(THIS_ LPDWORD) PURE; + STDMETHOD(GetVerticalBlankStatus)(THIS_ LPBOOL ) PURE; + STDMETHOD(Initialize)(THIS_ GUID FAR *) PURE; + STDMETHOD(RestoreDisplayMode)(THIS) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND, DWORD) PURE; + STDMETHOD(SetDisplayMode)(THIS_ DWORD, DWORD,DWORD, DWORD, DWORD) PURE; + STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD, HANDLE ) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetAvailableVidMem)(THIS_ LPDDSCAPS, LPDWORD, LPDWORD) PURE; +}; +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDraw2_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDraw2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDraw2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDraw2_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectDraw2_CreateClipper(p, a, b, c) (p)->lpVtbl->CreateClipper(p, a, b, c) +#define IDirectDraw2_CreatePalette(p, a, b, c, d) (p)->lpVtbl->CreatePalette(p, a, b, c, d) +#define IDirectDraw2_CreateSurface(p, a, b, c) (p)->lpVtbl->CreateSurface(p, a, b, c) +#define IDirectDraw2_DuplicateSurface(p, a, b) (p)->lpVtbl->DuplicateSurface(p, a, b) +#define IDirectDraw2_EnumDisplayModes(p, a, b, c, d) (p)->lpVtbl->EnumDisplayModes(p, a, b, c, d) +#define IDirectDraw2_EnumSurfaces(p, a, b, c, d) (p)->lpVtbl->EnumSurfaces(p, a, b, c, d) +#define IDirectDraw2_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p) +#define IDirectDraw2_GetCaps(p, a, b) (p)->lpVtbl->GetCaps(p, a, b) +#define IDirectDraw2_GetDisplayMode(p, a) (p)->lpVtbl->GetDisplayMode(p, a) +#define IDirectDraw2_GetFourCCCodes(p, a, b) (p)->lpVtbl->GetFourCCCodes(p, a, b) +#define IDirectDraw2_GetGDISurface(p, a) (p)->lpVtbl->GetGDISurface(p, a) +#define IDirectDraw2_GetMonitorFrequency(p, a) (p)->lpVtbl->GetMonitorFrequency(p, a) +#define IDirectDraw2_GetScanLine(p, a) (p)->lpVtbl->GetScanLine(p, a) +#define IDirectDraw2_GetVerticalBlankStatus(p, a) (p)->lpVtbl->GetVerticalBlankStatus(p, a) +#define IDirectDraw2_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirectDraw2_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p) +#define IDirectDraw2_SetCooperativeLevel(p, a, b) (p)->lpVtbl->SetCooperativeLevel(p, a, b) +#define IDirectDraw2_SetDisplayMode(p, a, b, c, d, e) (p)->lpVtbl->SetDisplayMode(p, a, b, c, d, e) +#define IDirectDraw2_WaitForVerticalBlank(p, a, b) (p)->lpVtbl->WaitForVerticalBlank(p, a, b) +#define IDirectDraw2_GetAvailableVidMem(p, a, b, c) (p)->lpVtbl->GetAvailableVidMem(p, a, b, c) +#else +#define IDirectDraw2_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDraw2_AddRef(p) (p)->AddRef() +#define IDirectDraw2_Release(p) (p)->Release() +#define IDirectDraw2_Compact(p) (p)->Compact() +#define IDirectDraw2_CreateClipper(p, a, b, c) (p)->CreateClipper(a, b, c) +#define IDirectDraw2_CreatePalette(p, a, b, c, d) (p)->CreatePalette(a, b, c, d) +#define IDirectDraw2_CreateSurface(p, a, b, c) (p)->CreateSurface(a, b, c) +#define IDirectDraw2_DuplicateSurface(p, a, b) (p)->DuplicateSurface(a, b) +#define IDirectDraw2_EnumDisplayModes(p, a, b, c, d) (p)->EnumDisplayModes(a, b, c, d) +#define IDirectDraw2_EnumSurfaces(p, a, b, c, d) (p)->EnumSurfaces(a, b, c, d) +#define IDirectDraw2_FlipToGDISurface(p) (p)->FlipToGDISurface() +#define IDirectDraw2_GetCaps(p, a, b) (p)->GetCaps(a, b) +#define IDirectDraw2_GetDisplayMode(p, a) (p)->GetDisplayMode(a) +#define IDirectDraw2_GetFourCCCodes(p, a, b) (p)->GetFourCCCodes(a, b) +#define IDirectDraw2_GetGDISurface(p, a) (p)->GetGDISurface(a) +#define IDirectDraw2_GetMonitorFrequency(p, a) (p)->GetMonitorFrequency(a) +#define IDirectDraw2_GetScanLine(p, a) (p)->GetScanLine(a) +#define IDirectDraw2_GetVerticalBlankStatus(p, a) (p)->GetVerticalBlankStatus(a) +#define IDirectDraw2_Initialize(p, a) (p)->Initialize(a) +#define IDirectDraw2_RestoreDisplayMode(p) (p)->RestoreDisplayMode() +#define IDirectDraw2_SetCooperativeLevel(p, a, b) (p)->SetCooperativeLevel(a, b) +#define IDirectDraw2_SetDisplayMode(p, a, b, c, d, e) (p)->SetDisplayMode(a, b, c, d, e) +#define IDirectDraw2_WaitForVerticalBlank(p, a, b) (p)->WaitForVerticalBlank(a, b) +#define IDirectDraw2_GetAvailableVidMem(p, a, b, c) (p)->GetAvailableVidMem(a, b, c) +#endif + +#endif + +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDraw4 +DECLARE_INTERFACE_( IDirectDraw4, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDraw methods ***/ + STDMETHOD(Compact)(THIS) PURE; + STDMETHOD(CreateClipper)(THIS_ DWORD, LPDIRECTDRAWCLIPPER FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreatePalette)(THIS_ DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC2, LPDIRECTDRAWSURFACE4 FAR *, IUnknown FAR *) PURE; + STDMETHOD(DuplicateSurface)( THIS_ LPDIRECTDRAWSURFACE4, LPDIRECTDRAWSURFACE4 FAR * ) PURE; + STDMETHOD(EnumDisplayModes)( THIS_ DWORD, LPDDSURFACEDESC2, LPVOID, LPDDENUMMODESCALLBACK2 ) PURE; + STDMETHOD(EnumSurfaces)(THIS_ DWORD, LPDDSURFACEDESC2, LPVOID,LPDDENUMSURFACESCALLBACK2 ) PURE; + STDMETHOD(FlipToGDISurface)(THIS) PURE; + STDMETHOD(GetCaps)( THIS_ LPDDCAPS, LPDDCAPS) PURE; + STDMETHOD(GetDisplayMode)( THIS_ LPDDSURFACEDESC2) PURE; + STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD, LPDWORD ) PURE; + STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE4 FAR *) PURE; + STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD) PURE; + STDMETHOD(GetScanLine)(THIS_ LPDWORD) PURE; + STDMETHOD(GetVerticalBlankStatus)(THIS_ LPBOOL ) PURE; + STDMETHOD(Initialize)(THIS_ GUID FAR *) PURE; + STDMETHOD(RestoreDisplayMode)(THIS) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND, DWORD) PURE; + STDMETHOD(SetDisplayMode)(THIS_ DWORD, DWORD,DWORD, DWORD, DWORD) PURE; + STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD, HANDLE ) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetAvailableVidMem)(THIS_ LPDDSCAPS2, LPDWORD, LPDWORD) PURE; + /*** Added in the V4 Interface ***/ + STDMETHOD(GetSurfaceFromDC) (THIS_ HDC, LPDIRECTDRAWSURFACE4 *) PURE; + STDMETHOD(RestoreAllSurfaces)(THIS) PURE; + STDMETHOD(TestCooperativeLevel)(THIS) PURE; + STDMETHOD(GetDeviceIdentifier)(THIS_ LPDDDEVICEIDENTIFIER, DWORD ) PURE; +}; +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDraw4_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDraw4_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDraw4_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDraw4_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectDraw4_CreateClipper(p, a, b, c) (p)->lpVtbl->CreateClipper(p, a, b, c) +#define IDirectDraw4_CreatePalette(p, a, b, c, d) (p)->lpVtbl->CreatePalette(p, a, b, c, d) +#define IDirectDraw4_CreateSurface(p, a, b, c) (p)->lpVtbl->CreateSurface(p, a, b, c) +#define IDirectDraw4_DuplicateSurface(p, a, b) (p)->lpVtbl->DuplicateSurface(p, a, b) +#define IDirectDraw4_EnumDisplayModes(p, a, b, c, d) (p)->lpVtbl->EnumDisplayModes(p, a, b, c, d) +#define IDirectDraw4_EnumSurfaces(p, a, b, c, d) (p)->lpVtbl->EnumSurfaces(p, a, b, c, d) +#define IDirectDraw4_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p) +#define IDirectDraw4_GetCaps(p, a, b) (p)->lpVtbl->GetCaps(p, a, b) +#define IDirectDraw4_GetDisplayMode(p, a) (p)->lpVtbl->GetDisplayMode(p, a) +#define IDirectDraw4_GetFourCCCodes(p, a, b) (p)->lpVtbl->GetFourCCCodes(p, a, b) +#define IDirectDraw4_GetGDISurface(p, a) (p)->lpVtbl->GetGDISurface(p, a) +#define IDirectDraw4_GetMonitorFrequency(p, a) (p)->lpVtbl->GetMonitorFrequency(p, a) +#define IDirectDraw4_GetScanLine(p, a) (p)->lpVtbl->GetScanLine(p, a) +#define IDirectDraw4_GetVerticalBlankStatus(p, a) (p)->lpVtbl->GetVerticalBlankStatus(p, a) +#define IDirectDraw4_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirectDraw4_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p) +#define IDirectDraw4_SetCooperativeLevel(p, a, b) (p)->lpVtbl->SetCooperativeLevel(p, a, b) +#define IDirectDraw4_SetDisplayMode(p, a, b, c, d, e) (p)->lpVtbl->SetDisplayMode(p, a, b, c, d, e) +#define IDirectDraw4_WaitForVerticalBlank(p, a, b) (p)->lpVtbl->WaitForVerticalBlank(p, a, b) +#define IDirectDraw4_GetAvailableVidMem(p, a, b, c) (p)->lpVtbl->GetAvailableVidMem(p, a, b, c) +#define IDirectDraw4_GetSurfaceFromDC(p, a, b) (p)->lpVtbl->GetSurfaceFromDC(p, a, b) +#define IDirectDraw4_RestoreAllSurfaces(p) (p)->lpVtbl->RestoreAllSurfaces(p) +#define IDirectDraw4_TestCooperativeLevel(p) (p)->lpVtbl->TestCooperativeLevel(p) +#define IDirectDraw4_GetDeviceIdentifier(p,a,b) (p)->lpVtbl->GetDeviceIdentifier(p,a,b) +#else +#define IDirectDraw4_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDraw4_AddRef(p) (p)->AddRef() +#define IDirectDraw4_Release(p) (p)->Release() +#define IDirectDraw4_Compact(p) (p)->Compact() +#define IDirectDraw4_CreateClipper(p, a, b, c) (p)->CreateClipper(a, b, c) +#define IDirectDraw4_CreatePalette(p, a, b, c, d) (p)->CreatePalette(a, b, c, d) +#define IDirectDraw4_CreateSurface(p, a, b, c) (p)->CreateSurface(a, b, c) +#define IDirectDraw4_DuplicateSurface(p, a, b) (p)->DuplicateSurface(a, b) +#define IDirectDraw4_EnumDisplayModes(p, a, b, c, d) (p)->EnumDisplayModes(a, b, c, d) +#define IDirectDraw4_EnumSurfaces(p, a, b, c, d) (p)->EnumSurfaces(a, b, c, d) +#define IDirectDraw4_FlipToGDISurface(p) (p)->FlipToGDISurface() +#define IDirectDraw4_GetCaps(p, a, b) (p)->GetCaps(a, b) +#define IDirectDraw4_GetDisplayMode(p, a) (p)->GetDisplayMode(a) +#define IDirectDraw4_GetFourCCCodes(p, a, b) (p)->GetFourCCCodes(a, b) +#define IDirectDraw4_GetGDISurface(p, a) (p)->GetGDISurface(a) +#define IDirectDraw4_GetMonitorFrequency(p, a) (p)->GetMonitorFrequency(a) +#define IDirectDraw4_GetScanLine(p, a) (p)->GetScanLine(a) +#define IDirectDraw4_GetVerticalBlankStatus(p, a) (p)->GetVerticalBlankStatus(a) +#define IDirectDraw4_Initialize(p, a) (p)->Initialize(a) +#define IDirectDraw4_RestoreDisplayMode(p) (p)->RestoreDisplayMode() +#define IDirectDraw4_SetCooperativeLevel(p, a, b) (p)->SetCooperativeLevel(a, b) +#define IDirectDraw4_SetDisplayMode(p, a, b, c, d, e) (p)->SetDisplayMode(a, b, c, d, e) +#define IDirectDraw4_WaitForVerticalBlank(p, a, b) (p)->WaitForVerticalBlank(a, b) +#define IDirectDraw4_GetAvailableVidMem(p, a, b, c) (p)->GetAvailableVidMem(a, b, c) +#define IDirectDraw4_GetSurfaceFromDC(p, a, b) (p)->GetSurfaceFromDC(a, b) +#define IDirectDraw4_RestoreAllSurfaces(p) (p)->RestoreAllSurfaces() +#define IDirectDraw4_TestCooperativeLevel(p) (p)->TestCooperativeLevel() +#define IDirectDraw4_GetDeviceIdentifier(p,a,b) (p)->GetDeviceIdentifier(a,b) +#endif + +#endif + + +/* + * IDirectDrawPalette + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawPalette +DECLARE_INTERFACE_( IDirectDrawPalette, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawPalette methods ***/ + STDMETHOD(GetCaps)(THIS_ LPDWORD) PURE; + STDMETHOD(GetEntries)(THIS_ DWORD,DWORD,DWORD,LPPALETTEENTRY) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, DWORD, LPPALETTEENTRY) PURE; + STDMETHOD(SetEntries)(THIS_ DWORD,DWORD,DWORD,LPPALETTEENTRY) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawPalette_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDrawPalette_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawPalette_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawPalette_GetCaps(p, a) (p)->lpVtbl->GetCaps(p, a) +#define IDirectDrawPalette_GetEntries(p, a, b, c, d) (p)->lpVtbl->GetEntries(p, a, b, c, d) +#define IDirectDrawPalette_Initialize(p, a, b, c) (p)->lpVtbl->Initialize(p, a, b, c) +#define IDirectDrawPalette_SetEntries(p, a, b, c, d) (p)->lpVtbl->SetEntries(p, a, b, c, d) +#else +#define IDirectDrawPalette_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDrawPalette_AddRef(p) (p)->AddRef() +#define IDirectDrawPalette_Release(p) (p)->Release() +#define IDirectDrawPalette_GetCaps(p, a) (p)->GetCaps(a) +#define IDirectDrawPalette_GetEntries(p, a, b, c, d) (p)->GetEntries(a, b, c, d) +#define IDirectDrawPalette_Initialize(p, a, b, c) (p)->Initialize(a, b, c) +#define IDirectDrawPalette_SetEntries(p, a, b, c, d) (p)->SetEntries(a, b, c, d) +#endif + +#endif + + + +/* + * IDirectDrawClipper + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawClipper +DECLARE_INTERFACE_( IDirectDrawClipper, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawClipper methods ***/ + STDMETHOD(GetClipList)(THIS_ LPRECT, LPRGNDATA, LPDWORD) PURE; + STDMETHOD(GetHWnd)(THIS_ HWND FAR *) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, DWORD) PURE; + STDMETHOD(IsClipListChanged)(THIS_ BOOL FAR *) PURE; + STDMETHOD(SetClipList)(THIS_ LPRGNDATA,DWORD) PURE; + STDMETHOD(SetHWnd)(THIS_ DWORD, HWND ) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawClipper_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDrawClipper_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawClipper_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawClipper_GetClipList(p, a, b, c) (p)->lpVtbl->GetClipList(p, a, b, c) +#define IDirectDrawClipper_GetHWnd(p, a) (p)->lpVtbl->GetHWnd(p, a) +#define IDirectDrawClipper_Initialize(p, a, b) (p)->lpVtbl->Initialize(p, a, b) +#define IDirectDrawClipper_IsClipListChanged(p, a) (p)->lpVtbl->IsClipListChanged(p, a) +#define IDirectDrawClipper_SetClipList(p, a, b) (p)->lpVtbl->SetClipList(p, a, b) +#define IDirectDrawClipper_SetHWnd(p, a, b) (p)->lpVtbl->SetHWnd(p, a, b) +#else +#define IDirectDrawClipper_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDrawClipper_AddRef(p) (p)->AddRef() +#define IDirectDrawClipper_Release(p) (p)->Release() +#define IDirectDrawClipper_GetClipList(p, a, b, c) (p)->GetClipList(a, b, c) +#define IDirectDrawClipper_GetHWnd(p, a) (p)->GetHWnd(a) +#define IDirectDrawClipper_Initialize(p, a, b) (p)->Initialize(a, b) +#define IDirectDrawClipper_IsClipListChanged(p, a) (p)->IsClipListChanged(a) +#define IDirectDrawClipper_SetClipList(p, a, b) (p)->SetClipList(a, b) +#define IDirectDrawClipper_SetHWnd(p, a, b) (p)->SetHWnd(a, b) +#endif + +#endif + +/* + * IDirectDrawSurface and related interfaces + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawSurface +DECLARE_INTERFACE_( IDirectDrawSurface, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawSurface methods ***/ + STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT) PURE; + STDMETHOD(Blt)(THIS_ LPRECT,LPDIRECTDRAWSURFACE, LPRECT,DWORD, LPDDBLTFX) PURE; + STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH, DWORD, DWORD ) PURE; + STDMETHOD(BltFast)(THIS_ DWORD,DWORD,LPDIRECTDRAWSURFACE, LPRECT,DWORD) PURE; + STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD,LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD,LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE, DWORD) PURE; + STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS, LPDIRECTDRAWSURFACE FAR *) PURE; + STDMETHOD(GetBltStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetCaps)(THIS_ LPDDSCAPS) PURE; + STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER FAR*) PURE; + STDMETHOD(GetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(GetDC)(THIS_ HDC FAR *) PURE; + STDMETHOD(GetFlipStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetOverlayPosition)(THIS_ LPLONG, LPLONG ) PURE; + STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE FAR*) PURE; + STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT) PURE; + STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, LPDDSURFACEDESC) PURE; + STDMETHOD(IsLost)(THIS) PURE; + STDMETHOD(Lock)(THIS_ LPRECT,LPDDSURFACEDESC,DWORD,HANDLE) PURE; + STDMETHOD(ReleaseDC)(THIS_ HDC) PURE; + STDMETHOD(Restore)(THIS) PURE; + STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER) PURE; + STDMETHOD(SetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(SetOverlayPosition)(THIS_ LONG, LONG ) PURE; + STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE) PURE; + STDMETHOD(Unlock)(THIS_ LPVOID) PURE; + STDMETHOD(UpdateOverlay)(THIS_ LPRECT, LPDIRECTDRAWSURFACE,LPRECT,DWORD, LPDDOVERLAYFX) PURE; + STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD) PURE; + STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD, LPDIRECTDRAWSURFACE) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawSurface_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectDrawSurface_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawSurface_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawSurface_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a) +#define IDirectDrawSurface_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a) +#define IDirectDrawSurface_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e) +#define IDirectDrawSurface_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c) +#define IDirectDrawSurface_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e) +#define IDirectDrawSurface_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b) +#define IDirectDrawSurface_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b) +#define IDirectDrawSurface_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c) +#define IDirectDrawSurface_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IDirectDrawSurface_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b) +#define IDirectDrawSurface_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a) +#define IDirectDrawSurface_GetCaps(p,b) (p)->lpVtbl->GetCaps(p,b) +#define IDirectDrawSurface_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a) +#define IDirectDrawSurface_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b) +#define IDirectDrawSurface_GetDC(p,a) (p)->lpVtbl->GetDC(p,a) +#define IDirectDrawSurface_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a) +#define IDirectDrawSurface_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b) +#define IDirectDrawSurface_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a) +#define IDirectDrawSurface_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a) +#define IDirectDrawSurface_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a) +#define IDirectDrawSurface_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectDrawSurface_IsLost(p) (p)->lpVtbl->IsLost(p) +#define IDirectDrawSurface_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirectDrawSurface_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a) +#define IDirectDrawSurface_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectDrawSurface_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a) +#define IDirectDrawSurface_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b) +#define IDirectDrawSurface_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b) +#define IDirectDrawSurface_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a) +#define IDirectDrawSurface_Unlock(p,b) (p)->lpVtbl->Unlock(p,b) +#define IDirectDrawSurface_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e) +#define IDirectDrawSurface_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a) +#define IDirectDrawSurface_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b) +#else +#define IDirectDrawSurface_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectDrawSurface_AddRef(p) (p)->AddRef() +#define IDirectDrawSurface_Release(p) (p)->Release() +#define IDirectDrawSurface_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a) +#define IDirectDrawSurface_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a) +#define IDirectDrawSurface_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e) +#define IDirectDrawSurface_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c) +#define IDirectDrawSurface_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e) +#define IDirectDrawSurface_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b) +#define IDirectDrawSurface_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b) +#define IDirectDrawSurface_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c) +#define IDirectDrawSurface_Flip(p,a,b) (p)->Flip(a,b) +#define IDirectDrawSurface_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b) +#define IDirectDrawSurface_GetBltStatus(p,a) (p)->GetBltStatus(a) +#define IDirectDrawSurface_GetCaps(p,b) (p)->GetCaps(b) +#define IDirectDrawSurface_GetClipper(p,a) (p)->GetClipper(a) +#define IDirectDrawSurface_GetColorKey(p,a,b) (p)->GetColorKey(a,b) +#define IDirectDrawSurface_GetDC(p,a) (p)->GetDC(a) +#define IDirectDrawSurface_GetFlipStatus(p,a) (p)->GetFlipStatus(a) +#define IDirectDrawSurface_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b) +#define IDirectDrawSurface_GetPalette(p,a) (p)->GetPalette(a) +#define IDirectDrawSurface_GetPixelFormat(p,a) (p)->GetPixelFormat(a) +#define IDirectDrawSurface_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a) +#define IDirectDrawSurface_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectDrawSurface_IsLost(p) (p)->IsLost() +#define IDirectDrawSurface_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirectDrawSurface_ReleaseDC(p,a) (p)->ReleaseDC(a) +#define IDirectDrawSurface_Restore(p) (p)->Restore() +#define IDirectDrawSurface_SetClipper(p,a) (p)->SetClipper(a) +#define IDirectDrawSurface_SetColorKey(p,a,b) (p)->SetColorKey(a,b) +#define IDirectDrawSurface_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b) +#define IDirectDrawSurface_SetPalette(p,a) (p)->SetPalette(a) +#define IDirectDrawSurface_Unlock(p,b) (p)->Unlock(b) +#define IDirectDrawSurface_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e) +#define IDirectDrawSurface_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a) +#define IDirectDrawSurface_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b) +#endif + +/* + * IDirectDrawSurface2 and related interfaces + */ +#undef INTERFACE +#define INTERFACE IDirectDrawSurface2 +DECLARE_INTERFACE_( IDirectDrawSurface2, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawSurface methods ***/ + STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE2) PURE; + STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT) PURE; + STDMETHOD(Blt)(THIS_ LPRECT,LPDIRECTDRAWSURFACE2, LPRECT,DWORD, LPDDBLTFX) PURE; + STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH, DWORD, DWORD ) PURE; + STDMETHOD(BltFast)(THIS_ DWORD,DWORD,LPDIRECTDRAWSURFACE2, LPRECT,DWORD) PURE; + STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD,LPDIRECTDRAWSURFACE2) PURE; + STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD,LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE2, DWORD) PURE; + STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS, LPDIRECTDRAWSURFACE2 FAR *) PURE; + STDMETHOD(GetBltStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetCaps)(THIS_ LPDDSCAPS) PURE; + STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER FAR*) PURE; + STDMETHOD(GetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(GetDC)(THIS_ HDC FAR *) PURE; + STDMETHOD(GetFlipStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetOverlayPosition)(THIS_ LPLONG, LPLONG ) PURE; + STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE FAR*) PURE; + STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT) PURE; + STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, LPDDSURFACEDESC) PURE; + STDMETHOD(IsLost)(THIS) PURE; + STDMETHOD(Lock)(THIS_ LPRECT,LPDDSURFACEDESC,DWORD,HANDLE) PURE; + STDMETHOD(ReleaseDC)(THIS_ HDC) PURE; + STDMETHOD(Restore)(THIS) PURE; + STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER) PURE; + STDMETHOD(SetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(SetOverlayPosition)(THIS_ LONG, LONG ) PURE; + STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE) PURE; + STDMETHOD(Unlock)(THIS_ LPVOID) PURE; + STDMETHOD(UpdateOverlay)(THIS_ LPRECT, LPDIRECTDRAWSURFACE2,LPRECT,DWORD, LPDDOVERLAYFX) PURE; + STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD) PURE; + STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD, LPDIRECTDRAWSURFACE2) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetDDInterface)(THIS_ LPVOID FAR *) PURE; + STDMETHOD(PageLock)(THIS_ DWORD) PURE; + STDMETHOD(PageUnlock)(THIS_ DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawSurface2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectDrawSurface2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawSurface2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawSurface2_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a) +#define IDirectDrawSurface2_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a) +#define IDirectDrawSurface2_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e) +#define IDirectDrawSurface2_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c) +#define IDirectDrawSurface2_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e) +#define IDirectDrawSurface2_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b) +#define IDirectDrawSurface2_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b) +#define IDirectDrawSurface2_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c) +#define IDirectDrawSurface2_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IDirectDrawSurface2_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b) +#define IDirectDrawSurface2_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a) +#define IDirectDrawSurface2_GetCaps(p,b) (p)->lpVtbl->GetCaps(p,b) +#define IDirectDrawSurface2_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a) +#define IDirectDrawSurface2_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b) +#define IDirectDrawSurface2_GetDC(p,a) (p)->lpVtbl->GetDC(p,a) +#define IDirectDrawSurface2_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a) +#define IDirectDrawSurface2_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b) +#define IDirectDrawSurface2_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a) +#define IDirectDrawSurface2_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a) +#define IDirectDrawSurface2_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a) +#define IDirectDrawSurface2_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectDrawSurface2_IsLost(p) (p)->lpVtbl->IsLost(p) +#define IDirectDrawSurface2_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirectDrawSurface2_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a) +#define IDirectDrawSurface2_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectDrawSurface2_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a) +#define IDirectDrawSurface2_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b) +#define IDirectDrawSurface2_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b) +#define IDirectDrawSurface2_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a) +#define IDirectDrawSurface2_Unlock(p,b) (p)->lpVtbl->Unlock(p,b) +#define IDirectDrawSurface2_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e) +#define IDirectDrawSurface2_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a) +#define IDirectDrawSurface2_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b) +#define IDirectDrawSurface2_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a) +#define IDirectDrawSurface2_PageLock(p,a) (p)->lpVtbl->PageLock(p,a) +#define IDirectDrawSurface2_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a) +#else +#define IDirectDrawSurface2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectDrawSurface2_AddRef(p) (p)->AddRef() +#define IDirectDrawSurface2_Release(p) (p)->Release() +#define IDirectDrawSurface2_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a) +#define IDirectDrawSurface2_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a) +#define IDirectDrawSurface2_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e) +#define IDirectDrawSurface2_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c) +#define IDirectDrawSurface2_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e) +#define IDirectDrawSurface2_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b) +#define IDirectDrawSurface2_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b) +#define IDirectDrawSurface2_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c) +#define IDirectDrawSurface2_Flip(p,a,b) (p)->Flip(a,b) +#define IDirectDrawSurface2_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b) +#define IDirectDrawSurface2_GetBltStatus(p,a) (p)->GetBltStatus(a) +#define IDirectDrawSurface2_GetCaps(p,b) (p)->GetCaps(b) +#define IDirectDrawSurface2_GetClipper(p,a) (p)->GetClipper(a) +#define IDirectDrawSurface2_GetColorKey(p,a,b) (p)->GetColorKey(a,b) +#define IDirectDrawSurface2_GetDC(p,a) (p)->GetDC(a) +#define IDirectDrawSurface2_GetFlipStatus(p,a) (p)->GetFlipStatus(a) +#define IDirectDrawSurface2_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b) +#define IDirectDrawSurface2_GetPalette(p,a) (p)->GetPalette(a) +#define IDirectDrawSurface2_GetPixelFormat(p,a) (p)->GetPixelFormat(a) +#define IDirectDrawSurface2_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a) +#define IDirectDrawSurface2_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectDrawSurface2_IsLost(p) (p)->IsLost() +#define IDirectDrawSurface2_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirectDrawSurface2_ReleaseDC(p,a) (p)->ReleaseDC(a) +#define IDirectDrawSurface2_Restore(p) (p)->Restore() +#define IDirectDrawSurface2_SetClipper(p,a) (p)->SetClipper(a) +#define IDirectDrawSurface2_SetColorKey(p,a,b) (p)->SetColorKey(a,b) +#define IDirectDrawSurface2_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b) +#define IDirectDrawSurface2_SetPalette(p,a) (p)->SetPalette(a) +#define IDirectDrawSurface2_Unlock(p,b) (p)->Unlock(b) +#define IDirectDrawSurface2_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e) +#define IDirectDrawSurface2_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a) +#define IDirectDrawSurface2_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b) +#define IDirectDrawSurface2_GetDDInterface(p,a) (p)->GetDDInterface(a) +#define IDirectDrawSurface2_PageLock(p,a) (p)->PageLock(a) +#define IDirectDrawSurface2_PageUnlock(p,a) (p)->PageUnlock(a) +#endif + +/* + * IDirectDrawSurface3 and related interfaces + */ +#undef INTERFACE +#define INTERFACE IDirectDrawSurface3 +DECLARE_INTERFACE_( IDirectDrawSurface3, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawSurface methods ***/ + STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE3) PURE; + STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT) PURE; + STDMETHOD(Blt)(THIS_ LPRECT,LPDIRECTDRAWSURFACE3, LPRECT,DWORD, LPDDBLTFX) PURE; + STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH, DWORD, DWORD ) PURE; + STDMETHOD(BltFast)(THIS_ DWORD,DWORD,LPDIRECTDRAWSURFACE3, LPRECT,DWORD) PURE; + STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD,LPDIRECTDRAWSURFACE3) PURE; + STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD,LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE3, DWORD) PURE; + STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS, LPDIRECTDRAWSURFACE3 FAR *) PURE; + STDMETHOD(GetBltStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetCaps)(THIS_ LPDDSCAPS) PURE; + STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER FAR*) PURE; + STDMETHOD(GetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(GetDC)(THIS_ HDC FAR *) PURE; + STDMETHOD(GetFlipStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetOverlayPosition)(THIS_ LPLONG, LPLONG ) PURE; + STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE FAR*) PURE; + STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT) PURE; + STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, LPDDSURFACEDESC) PURE; + STDMETHOD(IsLost)(THIS) PURE; + STDMETHOD(Lock)(THIS_ LPRECT,LPDDSURFACEDESC,DWORD,HANDLE) PURE; + STDMETHOD(ReleaseDC)(THIS_ HDC) PURE; + STDMETHOD(Restore)(THIS) PURE; + STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER) PURE; + STDMETHOD(SetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(SetOverlayPosition)(THIS_ LONG, LONG ) PURE; + STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE) PURE; + STDMETHOD(Unlock)(THIS_ LPVOID) PURE; + STDMETHOD(UpdateOverlay)(THIS_ LPRECT, LPDIRECTDRAWSURFACE3,LPRECT,DWORD, LPDDOVERLAYFX) PURE; + STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD) PURE; + STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD, LPDIRECTDRAWSURFACE3) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetDDInterface)(THIS_ LPVOID FAR *) PURE; + STDMETHOD(PageLock)(THIS_ DWORD) PURE; + STDMETHOD(PageUnlock)(THIS_ DWORD) PURE; + /*** Added in the V3 interface ***/ + STDMETHOD(SetSurfaceDesc)(THIS_ LPDDSURFACEDESC, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawSurface3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectDrawSurface3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawSurface3_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawSurface3_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a) +#define IDirectDrawSurface3_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a) +#define IDirectDrawSurface3_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e) +#define IDirectDrawSurface3_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c) +#define IDirectDrawSurface3_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e) +#define IDirectDrawSurface3_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b) +#define IDirectDrawSurface3_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b) +#define IDirectDrawSurface3_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c) +#define IDirectDrawSurface3_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IDirectDrawSurface3_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b) +#define IDirectDrawSurface3_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a) +#define IDirectDrawSurface3_GetCaps(p,b) (p)->lpVtbl->GetCaps(p,b) +#define IDirectDrawSurface3_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a) +#define IDirectDrawSurface3_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b) +#define IDirectDrawSurface3_GetDC(p,a) (p)->lpVtbl->GetDC(p,a) +#define IDirectDrawSurface3_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a) +#define IDirectDrawSurface3_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b) +#define IDirectDrawSurface3_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a) +#define IDirectDrawSurface3_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a) +#define IDirectDrawSurface3_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a) +#define IDirectDrawSurface3_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectDrawSurface3_IsLost(p) (p)->lpVtbl->IsLost(p) +#define IDirectDrawSurface3_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirectDrawSurface3_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a) +#define IDirectDrawSurface3_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectDrawSurface3_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a) +#define IDirectDrawSurface3_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b) +#define IDirectDrawSurface3_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b) +#define IDirectDrawSurface3_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a) +#define IDirectDrawSurface3_Unlock(p,b) (p)->lpVtbl->Unlock(p,b) +#define IDirectDrawSurface3_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e) +#define IDirectDrawSurface3_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a) +#define IDirectDrawSurface3_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b) +#define IDirectDrawSurface3_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a) +#define IDirectDrawSurface3_PageLock(p,a) (p)->lpVtbl->PageLock(p,a) +#define IDirectDrawSurface3_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a) +#define IDirectDrawSurface3_SetSurfaceDesc(p,a,b) (p)->lpVtbl->SetSurfaceDesc(p,a,b) +#else +#define IDirectDrawSurface3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectDrawSurface3_AddRef(p) (p)->AddRef() +#define IDirectDrawSurface3_Release(p) (p)->Release() +#define IDirectDrawSurface3_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a) +#define IDirectDrawSurface3_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a) +#define IDirectDrawSurface3_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e) +#define IDirectDrawSurface3_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c) +#define IDirectDrawSurface3_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e) +#define IDirectDrawSurface3_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b) +#define IDirectDrawSurface3_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b) +#define IDirectDrawSurface3_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c) +#define IDirectDrawSurface3_Flip(p,a,b) (p)->Flip(a,b) +#define IDirectDrawSurface3_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b) +#define IDirectDrawSurface3_GetBltStatus(p,a) (p)->GetBltStatus(a) +#define IDirectDrawSurface3_GetCaps(p,b) (p)->GetCaps(b) +#define IDirectDrawSurface3_GetClipper(p,a) (p)->GetClipper(a) +#define IDirectDrawSurface3_GetColorKey(p,a,b) (p)->GetColorKey(a,b) +#define IDirectDrawSurface3_GetDC(p,a) (p)->GetDC(a) +#define IDirectDrawSurface3_GetFlipStatus(p,a) (p)->GetFlipStatus(a) +#define IDirectDrawSurface3_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b) +#define IDirectDrawSurface3_GetPalette(p,a) (p)->GetPalette(a) +#define IDirectDrawSurface3_GetPixelFormat(p,a) (p)->GetPixelFormat(a) +#define IDirectDrawSurface3_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a) +#define IDirectDrawSurface3_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectDrawSurface3_IsLost(p) (p)->IsLost() +#define IDirectDrawSurface3_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirectDrawSurface3_ReleaseDC(p,a) (p)->ReleaseDC(a) +#define IDirectDrawSurface3_Restore(p) (p)->Restore() +#define IDirectDrawSurface3_SetClipper(p,a) (p)->SetClipper(a) +#define IDirectDrawSurface3_SetColorKey(p,a,b) (p)->SetColorKey(a,b) +#define IDirectDrawSurface3_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b) +#define IDirectDrawSurface3_SetPalette(p,a) (p)->SetPalette(a) +#define IDirectDrawSurface3_Unlock(p,b) (p)->Unlock(b) +#define IDirectDrawSurface3_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e) +#define IDirectDrawSurface3_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a) +#define IDirectDrawSurface3_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b) +#define IDirectDrawSurface3_GetDDInterface(p,a) (p)->GetDDInterface(a) +#define IDirectDrawSurface3_PageLock(p,a) (p)->PageLock(a) +#define IDirectDrawSurface3_PageUnlock(p,a) (p)->PageUnlock(a) +#define IDirectDrawSurface3_SetSurfaceDesc(p,a,b) (p)->SetSurfaceDesc(a,b) +#endif + +/* + * IDirectDrawSurface4 and related interfaces + */ +#undef INTERFACE +#define INTERFACE IDirectDrawSurface4 +DECLARE_INTERFACE_( IDirectDrawSurface4, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawSurface methods ***/ + STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE4) PURE; + STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT) PURE; + STDMETHOD(Blt)(THIS_ LPRECT,LPDIRECTDRAWSURFACE4, LPRECT,DWORD, LPDDBLTFX) PURE; + STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH, DWORD, DWORD ) PURE; + STDMETHOD(BltFast)(THIS_ DWORD,DWORD,LPDIRECTDRAWSURFACE4, LPRECT,DWORD) PURE; + STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD,LPDIRECTDRAWSURFACE4) PURE; + STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID,LPDDENUMSURFACESCALLBACK2) PURE; + STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD,LPVOID,LPDDENUMSURFACESCALLBACK2) PURE; + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE4, DWORD) PURE; + STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS2, LPDIRECTDRAWSURFACE4 FAR *) PURE; + STDMETHOD(GetBltStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetCaps)(THIS_ LPDDSCAPS2) PURE; + STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER FAR*) PURE; + STDMETHOD(GetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(GetDC)(THIS_ HDC FAR *) PURE; + STDMETHOD(GetFlipStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetOverlayPosition)(THIS_ LPLONG, LPLONG ) PURE; + STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE FAR*) PURE; + STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT) PURE; + STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC2) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, LPDDSURFACEDESC2) PURE; + STDMETHOD(IsLost)(THIS) PURE; + STDMETHOD(Lock)(THIS_ LPRECT,LPDDSURFACEDESC2,DWORD,HANDLE) PURE; + STDMETHOD(ReleaseDC)(THIS_ HDC) PURE; + STDMETHOD(Restore)(THIS) PURE; + STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER) PURE; + STDMETHOD(SetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(SetOverlayPosition)(THIS_ LONG, LONG ) PURE; + STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE) PURE; + STDMETHOD(Unlock)(THIS_ LPRECT) PURE; + STDMETHOD(UpdateOverlay)(THIS_ LPRECT, LPDIRECTDRAWSURFACE4,LPRECT,DWORD, LPDDOVERLAYFX) PURE; + STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD) PURE; + STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD, LPDIRECTDRAWSURFACE4) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetDDInterface)(THIS_ LPVOID FAR *) PURE; + STDMETHOD(PageLock)(THIS_ DWORD) PURE; + STDMETHOD(PageUnlock)(THIS_ DWORD) PURE; + /*** Added in the v3 interface ***/ + STDMETHOD(SetSurfaceDesc)(THIS_ LPDDSURFACEDESC2, DWORD) PURE; + /*** Added in the v4 interface ***/ + STDMETHOD(SetPrivateData)(THIS_ REFGUID, LPVOID, DWORD, DWORD) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID, LPVOID, LPDWORD) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID) PURE; + STDMETHOD(GetUniquenessValue)(THIS_ LPDWORD) PURE; + STDMETHOD(ChangeUniquenessValue)(THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawSurface4_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectDrawSurface4_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawSurface4_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawSurface4_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a) +#define IDirectDrawSurface4_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a) +#define IDirectDrawSurface4_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e) +#define IDirectDrawSurface4_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c) +#define IDirectDrawSurface4_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e) +#define IDirectDrawSurface4_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b) +#define IDirectDrawSurface4_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b) +#define IDirectDrawSurface4_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c) +#define IDirectDrawSurface4_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IDirectDrawSurface4_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b) +#define IDirectDrawSurface4_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a) +#define IDirectDrawSurface4_GetCaps(p,b) (p)->lpVtbl->GetCaps(p,b) +#define IDirectDrawSurface4_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a) +#define IDirectDrawSurface4_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b) +#define IDirectDrawSurface4_GetDC(p,a) (p)->lpVtbl->GetDC(p,a) +#define IDirectDrawSurface4_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a) +#define IDirectDrawSurface4_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b) +#define IDirectDrawSurface4_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a) +#define IDirectDrawSurface4_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a) +#define IDirectDrawSurface4_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a) +#define IDirectDrawSurface4_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectDrawSurface4_IsLost(p) (p)->lpVtbl->IsLost(p) +#define IDirectDrawSurface4_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirectDrawSurface4_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a) +#define IDirectDrawSurface4_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectDrawSurface4_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a) +#define IDirectDrawSurface4_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b) +#define IDirectDrawSurface4_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b) +#define IDirectDrawSurface4_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a) +#define IDirectDrawSurface4_Unlock(p,b) (p)->lpVtbl->Unlock(p,b) +#define IDirectDrawSurface4_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e) +#define IDirectDrawSurface4_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a) +#define IDirectDrawSurface4_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b) +#define IDirectDrawSurface4_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a) +#define IDirectDrawSurface4_PageLock(p,a) (p)->lpVtbl->PageLock(p,a) +#define IDirectDrawSurface4_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a) +#define IDirectDrawSurface4_SetSurfaceDesc(p,a,b) (p)->lpVtbl->SetSurfaceDesc(p,a,b) +#define IDirectDrawSurface4_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirectDrawSurface4_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirectDrawSurface4_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirectDrawSurface4_GetUniquenessValue(p, a) (p)->lpVtbl->GetUniquenessValue(p, a) +#define IDirectDrawSurface4_ChangeUniquenessValue(p) (p)->lpVtbl->ChangeUniquenessValue(p) +#else +#define IDirectDrawSurface4_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectDrawSurface4_AddRef(p) (p)->AddRef() +#define IDirectDrawSurface4_Release(p) (p)->Release() +#define IDirectDrawSurface4_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a) +#define IDirectDrawSurface4_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a) +#define IDirectDrawSurface4_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e) +#define IDirectDrawSurface4_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c) +#define IDirectDrawSurface4_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e) +#define IDirectDrawSurface4_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b) +#define IDirectDrawSurface4_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b) +#define IDirectDrawSurface4_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c) +#define IDirectDrawSurface4_Flip(p,a,b) (p)->Flip(a,b) +#define IDirectDrawSurface4_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b) +#define IDirectDrawSurface4_GetBltStatus(p,a) (p)->GetBltStatus(a) +#define IDirectDrawSurface4_GetCaps(p,b) (p)->GetCaps(b) +#define IDirectDrawSurface4_GetClipper(p,a) (p)->GetClipper(a) +#define IDirectDrawSurface4_GetColorKey(p,a,b) (p)->GetColorKey(a,b) +#define IDirectDrawSurface4_GetDC(p,a) (p)->GetDC(a) +#define IDirectDrawSurface4_GetFlipStatus(p,a) (p)->GetFlipStatus(a) +#define IDirectDrawSurface4_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b) +#define IDirectDrawSurface4_GetPalette(p,a) (p)->GetPalette(a) +#define IDirectDrawSurface4_GetPixelFormat(p,a) (p)->GetPixelFormat(a) +#define IDirectDrawSurface4_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a) +#define IDirectDrawSurface4_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectDrawSurface4_IsLost(p) (p)->IsLost() +#define IDirectDrawSurface4_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirectDrawSurface4_ReleaseDC(p,a) (p)->ReleaseDC(a) +#define IDirectDrawSurface4_Restore(p) (p)->Restore() +#define IDirectDrawSurface4_SetClipper(p,a) (p)->SetClipper(a) +#define IDirectDrawSurface4_SetColorKey(p,a,b) (p)->SetColorKey(a,b) +#define IDirectDrawSurface4_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b) +#define IDirectDrawSurface4_SetPalette(p,a) (p)->SetPalette(a) +#define IDirectDrawSurface4_Unlock(p,b) (p)->Unlock(b) +#define IDirectDrawSurface4_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e) +#define IDirectDrawSurface4_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a) +#define IDirectDrawSurface4_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b) +#define IDirectDrawSurface4_GetDDInterface(p,a) (p)->GetDDInterface(a) +#define IDirectDrawSurface4_PageLock(p,a) (p)->PageLock(a) +#define IDirectDrawSurface4_PageUnlock(p,a) (p)->PageUnlock(a) +#define IDirectDrawSurface4_SetSurfaceDesc(p,a,b) (p)->SetSurfaceDesc(a,b) +#define IDirectDrawSurface4_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirectDrawSurface4_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirectDrawSurface4_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirectDrawSurface4_GetUniquenessValue(p, a) (p)->GetUniquenessValue(a) +#define IDirectDrawSurface4_ChangeUniquenessValue(p) (p)->ChangeUniquenessValue() +#endif + + + +/* + * IDirectDrawColorControl + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawColorControl +DECLARE_INTERFACE_( IDirectDrawColorControl, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawColorControl methods ***/ + STDMETHOD(GetColorControls)(THIS_ LPDDCOLORCONTROL) PURE; + STDMETHOD(SetColorControls)(THIS_ LPDDCOLORCONTROL) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawColorControl_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDrawColorControl_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawColorControl_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawColorControl_GetColorControls(p, a) (p)->lpVtbl->GetColorControls(p, a) +#define IDirectDrawColorControl_SetColorControls(p, a) (p)->lpVtbl->SetColorControls(p, a) +#else +#define IDirectDrawColorControl_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDrawColorControl_AddRef(p) (p)->AddRef() +#define IDirectDrawColorControl_Release(p) (p)->Release() +#define IDirectDrawColorControl_GetColorControls(p, a) (p)->GetColorControls(a) +#define IDirectDrawColorControl_SetColorControls(p, a) (p)->SetColorControls(a) +#endif + +#endif + + +/* + * IDirectDrawGammaControl + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawGammaControl +DECLARE_INTERFACE_( IDirectDrawGammaControl, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawColorControl methods ***/ + STDMETHOD(GetGammaRamp)(THIS_ DWORD, LPDDGAMMARAMP) PURE; + STDMETHOD(SetGammaRamp)(THIS_ DWORD, LPDDGAMMARAMP) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawGammaControl_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDrawGammaControl_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawGammaControl_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawGammaControl_GetGammaRamp(p, a, b) (p)->lpVtbl->GetGammaRamp(p, a, b) +#define IDirectDrawGammaControl_SetGammaRamp(p, a, b) (p)->lpVtbl->SetGammaRamp(p, a, b) +#else +#define IDirectDrawGammaControl_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDrawGammaControl_AddRef(p) (p)->AddRef() +#define IDirectDrawGammaControl_Release(p) (p)->Release() +#define IDirectDrawGammaControl_GetGammaRamp(p, a, b) (p)->GetGammaRamp(a, b) +#define IDirectDrawGammaControl_SetGammaRamp(p, a, b) (p)->SetGammaRamp(a, b) +#endif + +#endif + + + +#endif + + +/* + * DDSURFACEDESC + */ +typedef struct _DDSURFACEDESC +{ + DWORD dwSize; // size of the DDSURFACEDESC structure + DWORD dwFlags; // determines what fields are valid + DWORD dwHeight; // height of surface to be created + DWORD dwWidth; // width of input surface + union + { + LONG lPitch; // distance to start of next line (return value only) + DWORD dwLinearSize; // Formless late-allocated optimized surface size + } DUMMYUNIONNAMEN(1); + DWORD dwBackBufferCount; // number of back buffers requested + union + { + DWORD dwMipMapCount; // number of mip-map levels requested + DWORD dwZBufferBitDepth; // depth of Z buffer requested + DWORD dwRefreshRate; // refresh rate (used when display mode is described) + } DUMMYUNIONNAMEN(2); + DWORD dwAlphaBitDepth; // depth of alpha buffer requested + DWORD dwReserved; // reserved + LPVOID lpSurface; // pointer to the associated surface memory + DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use + DDCOLORKEY ddckCKDestBlt; // color key for destination blt use + DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use + DDCOLORKEY ddckCKSrcBlt; // color key for source blt use + DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface + DDSCAPS ddsCaps; // direct draw surface capabilities +} DDSURFACEDESC; + +/* + * DDSURFACEDESC2 + */ +typedef struct _DDSURFACEDESC2 +{ + DWORD dwSize; // size of the DDSURFACEDESC structure + DWORD dwFlags; // determines what fields are valid + DWORD dwHeight; // height of surface to be created + DWORD dwWidth; // width of input surface + union + { + LONG lPitch; // distance to start of next line (return value only) + DWORD dwLinearSize; // Formless late-allocated optimized surface size + } DUMMYUNIONNAMEN(1); + DWORD dwBackBufferCount; // number of back buffers requested + union + { + DWORD dwMipMapCount; // number of mip-map levels requestde + // dwZBufferBitDepth removed, use ddpfPixelFormat one instead + DWORD dwRefreshRate; // refresh rate (used when display mode is described) + } DUMMYUNIONNAMEN(2); + DWORD dwAlphaBitDepth; // depth of alpha buffer requested + DWORD dwReserved; // reserved + LPVOID lpSurface; // pointer to the associated surface memory + DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use + DDCOLORKEY ddckCKDestBlt; // color key for destination blt use + DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use + DDCOLORKEY ddckCKSrcBlt; // color key for source blt use + DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface + DDSCAPS2 ddsCaps; // direct draw surface capabilities + DWORD dwTextureStage; // stage in multitexture cascade +} DDSURFACEDESC2; + +/* + * ddsCaps field is valid. + */ +#define DDSD_CAPS 0x00000001l // default + +/* + * dwHeight field is valid. + */ +#define DDSD_HEIGHT 0x00000002l + +/* + * dwWidth field is valid. + */ +#define DDSD_WIDTH 0x00000004l + +/* + * lPitch is valid. + */ +#define DDSD_PITCH 0x00000008l + +/* + * dwBackBufferCount is valid. + */ +#define DDSD_BACKBUFFERCOUNT 0x00000020l + +/* + * dwZBufferBitDepth is valid. (shouldnt be used in DDSURFACEDESC2) + */ +#define DDSD_ZBUFFERBITDEPTH 0x00000040l + +/* + * dwAlphaBitDepth is valid. + */ +#define DDSD_ALPHABITDEPTH 0x00000080l + + +/* + * lpSurface is valid. + */ +#define DDSD_LPSURFACE 0x00000800l + +/* + * ddpfPixelFormat is valid. + */ +#define DDSD_PIXELFORMAT 0x00001000l + +/* + * ddckCKDestOverlay is valid. + */ +#define DDSD_CKDESTOVERLAY 0x00002000l + +/* + * ddckCKDestBlt is valid. + */ +#define DDSD_CKDESTBLT 0x00004000l + +/* + * ddckCKSrcOverlay is valid. + */ +#define DDSD_CKSRCOVERLAY 0x00008000l + +/* + * ddckCKSrcBlt is valid. + */ +#define DDSD_CKSRCBLT 0x00010000l + +/* + * dwMipMapCount is valid. + */ +#define DDSD_MIPMAPCOUNT 0x00020000l + + /* + * dwRefreshRate is valid + */ +#define DDSD_REFRESHRATE 0x00040000l + +/* + * dwLinearSize is valid + */ +#define DDSD_LINEARSIZE 0x00080000l + +/* + * dwTextureStage is valid + */ +#define DDSD_TEXTURESTAGE 0x00100000l +/* + * All input fields are valid. + */ +#define DDSD_ALL 0x001ff9eel + + +/* + * DDOPTSURFACEDESC + */ +typedef struct _DDOPTSURFACEDESC +{ + DWORD dwSize; // size of the DDOPTSURFACEDESC structure + DWORD dwFlags; // determines what fields are valid + DDSCAPS2 ddSCaps; // Common caps like: Memory type + DDOSCAPS ddOSCaps; // Common caps like: Memory type + GUID guid; // Compression technique GUID + DWORD dwCompressionRatio; // Compression ratio +} DDOPTSURFACEDESC; + +/* + * guid field is valid. + */ +#define DDOSD_GUID 0x00000001l + +/* + * dwCompressionRatio field is valid. + */ +#define DDOSD_COMPRESSION_RATIO 0x00000002l + +/* + * ddSCaps field is valid. + */ +#define DDOSD_SCAPS 0x00000004l + +/* + * ddOSCaps field is valid. + */ +#define DDOSD_OSCAPS 0x00000008l + +/* + * All input fields are valid. + */ +#define DDOSD_ALL 0x0000000fl + +/* + * The surface's optimized pixelformat is compressed + */ +#define DDOSDCAPS_OPTCOMPRESSED 0x00000001l + +/* + * The surface's optimized pixelformat is reordered + */ +#define DDOSDCAPS_OPTREORDERED 0x00000002l + +/* + * The opt surface is a monolithic mipmap + */ +#define DDOSDCAPS_MONOLITHICMIPMAP 0x00000004l + +/* + * The valid Surf caps: + * #define DDSCAPS_SYSTEMMEMORY 0x00000800l + * #define DDSCAPS_VIDEOMEMORY 0x00004000l + * #define DDSCAPS_LOCALVIDMEM 0x10000000l + * #define DDSCAPS_NONLOCALVIDMEM 0x20000000l + */ +#define DDOSDCAPS_VALIDSCAPS 0x30004800l + +/* + * The valid OptSurf caps + */ +#define DDOSDCAPS_VALIDOSCAPS 0x00000007l + + +/* + * DDCOLORCONTROL + */ +typedef struct _DDCOLORCONTROL +{ + DWORD dwSize; + DWORD dwFlags; + LONG lBrightness; + LONG lContrast; + LONG lHue; + LONG lSaturation; + LONG lSharpness; + LONG lGamma; + LONG lColorEnable; + DWORD dwReserved1; +} DDCOLORCONTROL; + + +/* + * lBrightness field is valid. + */ +#define DDCOLOR_BRIGHTNESS 0x00000001l + +/* + * lContrast field is valid. + */ +#define DDCOLOR_CONTRAST 0x00000002l + +/* + * lHue field is valid. + */ +#define DDCOLOR_HUE 0x00000004l + +/* + * lSaturation field is valid. + */ +#define DDCOLOR_SATURATION 0x00000008l + +/* + * lSharpness field is valid. + */ +#define DDCOLOR_SHARPNESS 0x00000010l + +/* + * lGamma field is valid. + */ +#define DDCOLOR_GAMMA 0x00000020l + +/* + * lColorEnable field is valid. + */ +#define DDCOLOR_COLORENABLE 0x00000040l + + + +/*============================================================================ + * + * Direct Draw Capability Flags + * + * These flags are used to describe the capabilities of a given Surface. + * All flags are bit flags. + * + *==========================================================================*/ + +/**************************************************************************** + * + * DIRECTDRAWSURFACE CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * This bit is reserved. It should not be specified. + */ +#define DDSCAPS_RESERVED1 0x00000001l + +/* + * Indicates that this surface contains alpha-only information. + * (To determine if a surface is RGBA/YUVA, the pixel format must be + * interrogated.) + */ +#define DDSCAPS_ALPHA 0x00000002l + +/* + * Indicates that this surface is a backbuffer. It is generally + * set by CreateSurface when the DDSCAPS_FLIP capability bit is set. + * It indicates that this surface is THE back buffer of a surface + * flipping structure. DirectDraw supports N surfaces in a + * surface flipping structure. Only the surface that immediately + * precedeces the DDSCAPS_FRONTBUFFER has this capability bit set. + * The other surfaces are identified as back buffers by the presence + * of the DDSCAPS_FLIP capability, their attachment order, and the + * absence of the DDSCAPS_FRONTBUFFER and DDSCAPS_BACKBUFFER + * capabilities. The bit is sent to CreateSurface when a standalone + * back buffer is being created. This surface could be attached to + * a front buffer and/or back buffers to form a flipping surface + * structure after the CreateSurface call. See AddAttachments for + * a detailed description of the behaviors in this case. + */ +#define DDSCAPS_BACKBUFFER 0x00000004l + +/* + * Indicates a complex surface structure is being described. A + * complex surface structure results in the creation of more than + * one surface. The additional surfaces are attached to the root + * surface. The complex structure can only be destroyed by + * destroying the root. + */ +#define DDSCAPS_COMPLEX 0x00000008l + +/* + * Indicates that this surface is a part of a surface flipping structure. + * When it is passed to CreateSurface the DDSCAPS_FRONTBUFFER and + * DDSCAP_BACKBUFFER bits are not set. They are set by CreateSurface + * on the resulting creations. The dwBackBufferCount field in the + * DDSURFACEDESC structure must be set to at least 1 in order for + * the CreateSurface call to succeed. The DDSCAPS_COMPLEX capability + * must always be set with creating multiple surfaces through CreateSurface. + */ +#define DDSCAPS_FLIP 0x00000010l + +/* + * Indicates that this surface is THE front buffer of a surface flipping + * structure. It is generally set by CreateSurface when the DDSCAPS_FLIP + * capability bit is set. + * If this capability is sent to CreateSurface then a standalonw front buffer + * is created. This surface will not have the DDSCAPS_FLIP capability. + * It can be attached to other back buffers to form a flipping structure. + * See AddAttachments for a detailed description of the behaviors in this + * case. + */ +#define DDSCAPS_FRONTBUFFER 0x00000020l + +/* + * Indicates that this surface is any offscreen surface that is not an overlay, + * texture, zbuffer, front buffer, back buffer, or alpha surface. It is used + * to identify plain vanilla surfaces. + */ +#define DDSCAPS_OFFSCREENPLAIN 0x00000040l + +/* + * Indicates that this surface is an overlay. It may or may not be directly visible + * depending on whether or not it is currently being overlayed onto the primary + * surface. DDSCAPS_VISIBLE can be used to determine whether or not it is being + * overlayed at the moment. + */ +#define DDSCAPS_OVERLAY 0x00000080l + +/* + * Indicates that unique DirectDrawPalette objects can be created and + * attached to this surface. + */ +#define DDSCAPS_PALETTE 0x00000100l + +/* + * Indicates that this surface is the primary surface. The primary + * surface represents what the user is seeing at the moment. + */ +#define DDSCAPS_PRIMARYSURFACE 0x00000200l + +/* + * Indicates that this surface is the primary surface for the left eye. + * The primary surface for the left eye represents what the user is seeing + * at the moment with the users left eye. When this surface is created the + * DDSCAPS_PRIMARYSURFACE represents what the user is seeing with the users + * right eye. + */ +#define DDSCAPS_PRIMARYSURFACELEFT 0x00000400l + +/* + * Indicates that this surface memory was allocated in system memory + */ +#define DDSCAPS_SYSTEMMEMORY 0x00000800l + +/* + * Indicates that this surface can be used as a 3D texture. It does not + * indicate whether or not the surface is being used for that purpose. + */ +#define DDSCAPS_TEXTURE 0x00001000l + +/* + * Indicates that a surface may be a destination for 3D rendering. This + * bit must be set in order to query for a Direct3D Device Interface + * from this surface. + */ +#define DDSCAPS_3DDEVICE 0x00002000l + +/* + * Indicates that this surface exists in video memory. + */ +#define DDSCAPS_VIDEOMEMORY 0x00004000l + +/* + * Indicates that changes made to this surface are immediately visible. + * It is always set for the primary surface and is set for overlays while + * they are being overlayed and texture maps while they are being textured. + */ +#define DDSCAPS_VISIBLE 0x00008000l + +/* + * Indicates that only writes are permitted to the surface. Read accesses + * from the surface may or may not generate a protection fault, but the + * results of a read from this surface will not be meaningful. READ ONLY. + */ +#define DDSCAPS_WRITEONLY 0x00010000l + +/* + * Indicates that this surface is a z buffer. A z buffer does not contain + * displayable information. Instead it contains bit depth information that is + * used to determine which pixels are visible and which are obscured. + */ +#define DDSCAPS_ZBUFFER 0x00020000l + +/* + * Indicates surface will have a DC associated long term + */ +#define DDSCAPS_OWNDC 0x00040000l + +/* + * Indicates surface should be able to receive live video + */ +#define DDSCAPS_LIVEVIDEO 0x00080000l + +/* + * Indicates surface should be able to have a stream decompressed + * to it by the hardware. + */ +#define DDSCAPS_HWCODEC 0x00100000l + +/* + * Surface is a ModeX surface. + * + */ +#define DDSCAPS_MODEX 0x00200000l + +/* + * Indicates surface is one level of a mip-map. This surface will + * be attached to other DDSCAPS_MIPMAP surfaces to form the mip-map. + * This can be done explicitly, by creating a number of surfaces and + * attaching them with AddAttachedSurface or by implicitly by CreateSurface. + * If this bit is set then DDSCAPS_TEXTURE must also be set. + */ +#define DDSCAPS_MIPMAP 0x00400000l + +/* + * This bit is reserved. It should not be specified. + */ +#define DDSCAPS_RESERVED2 0x00800000l + + +/* + * Indicates that memory for the surface is not allocated until the surface + * is loaded (via the Direct3D texture Load() function). + */ +#define DDSCAPS_ALLOCONLOAD 0x04000000l + +/* + * Indicates that the surface will recieve data from a video port. + */ +#define DDSCAPS_VIDEOPORT 0x08000000l + +/* + * Indicates that a video memory surface is resident in true, local video + * memory rather than non-local video memory. If this flag is specified then + * so must DDSCAPS_VIDEOMEMORY. This flag is mutually exclusive with + * DDSCAPS_NONLOCALVIDMEM. + */ +#define DDSCAPS_LOCALVIDMEM 0x10000000l + +/* + * Indicates that a video memory surface is resident in non-local video + * memory rather than true, local video memory. If this flag is specified + * then so must DDSCAPS_VIDEOMEMORY. This flag is mutually exclusive with + * DDSCAPS_LOCALVIDMEM. + */ +#define DDSCAPS_NONLOCALVIDMEM 0x20000000l + +/* + * Indicates that this surface is a standard VGA mode surface, and not a + * ModeX surface. (This flag will never be set in combination with the + * DDSCAPS_MODEX flag). + */ +#define DDSCAPS_STANDARDVGAMODE 0x40000000l + +/* + * Indicates that this surface will be an optimized surface. This flag is + * currently only valid in conjunction with the DDSCAPS_TEXTURE flag. The surface + * will be created without any underlying video memory until loaded. + */ +#define DDSCAPS_OPTIMIZED 0x80000000l + + + + +/* + * Indicates that this surface will receive data from a video port using + * the de-interlacing hardware. This allows the driver to allocate memory + * for any extra buffers that may be required. The DDSCAPS_VIDEOPORT and + * DDSCAPS_OVERLAY flags must also be set. + */ +#define DDSCAPS2_HARDWAREDEINTERLACE 0x00000002L + +/* + * Indicates to the driver that this surface will be locked very frequently + * (for procedural textures, dynamic lightmaps, etc). Surfaces with this cap + * set must also have DDSCAPS_TEXTURE. This cap cannot be used with + * DDSCAPS2_HINTSTATIC and DDSCAPS2_OPAQUE. + */ +#define DDSCAPS2_HINTDYNAMIC 0x00000004L + +/* + * Indicates to the driver that this surface can be re-ordered/retiled on + * load. This operation will not change the size of the texture. It is + * relatively fast and symmetrical, since the application may lock these + * bits (although it will take a performance hit when doing so). Surfaces + * with this cap set must also have DDSCAPS_TEXTURE. This cap cannot be + * used with DDSCAPS2_HINTDYNAMIC and DDSCAPS2_OPAQUE. + */ +#define DDSCAPS2_HINTSTATIC 0x00000008L + +/* + * Indicates that the client would like this texture surface to be managed by the + * DirectDraw/Direct3D runtime. Surfaces with this cap set must also have + * DDSCAPS_TEXTURE set. + */ +#define DDSCAPS2_TEXTUREMANAGE 0x00000010L + +/* + * These bits are reserved for internal use */ +#define DDSCAPS2_RESERVED1 0x00000020L +#define DDSCAPS2_RESERVED2 0x00000040L + +/* + * Indicates to the driver that this surface will never be locked again. + * The driver is free to optimize this surface via retiling and actual compression. + * All calls to Lock() or Blts from this surface will fail. Surfaces with this + * cap set must also have DDSCAPS_TEXTURE. This cap cannot be used with + * DDSCAPS2_HINTDYNAMIC and DDSCAPS2_HINTSTATIC. + */ +#define DDSCAPS2_OPAQUE 0x00000080L + +/* + * Applications should set this bit at CreateSurface time to indicate that they + * intend to use antialiasing. Only valid if DDSCAPS_3DDEVICE is also set. + */ +#define DDSCAPS2_HINTANTIALIASING 0x00000100L + + + + + /**************************************************************************** + * + * DIRECTDRAW DRIVER CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Display hardware has 3D acceleration. + */ +#define DDCAPS_3D 0x00000001l + +/* + * Indicates that DirectDraw will support only dest rectangles that are aligned + * on DIRECTDRAWCAPS.dwAlignBoundaryDest boundaries of the surface, respectively. + * READ ONLY. + */ +#define DDCAPS_ALIGNBOUNDARYDEST 0x00000002l + +/* + * Indicates that DirectDraw will support only source rectangles whose sizes in + * BYTEs are DIRECTDRAWCAPS.dwAlignSizeDest multiples, respectively. READ ONLY. + */ +#define DDCAPS_ALIGNSIZEDEST 0x00000004l +/* + * Indicates that DirectDraw will support only source rectangles that are aligned + * on DIRECTDRAWCAPS.dwAlignBoundarySrc boundaries of the surface, respectively. + * READ ONLY. + */ +#define DDCAPS_ALIGNBOUNDARYSRC 0x00000008l + +/* + * Indicates that DirectDraw will support only source rectangles whose sizes in + * BYTEs are DIRECTDRAWCAPS.dwAlignSizeSrc multiples, respectively. READ ONLY. + */ +#define DDCAPS_ALIGNSIZESRC 0x00000010l + +/* + * Indicates that DirectDraw will create video memory surfaces that have a stride + * alignment equal to DIRECTDRAWCAPS.dwAlignStride. READ ONLY. + */ +#define DDCAPS_ALIGNSTRIDE 0x00000020l + +/* + * Display hardware is capable of blt operations. + */ +#define DDCAPS_BLT 0x00000040l + +/* + * Display hardware is capable of asynchronous blt operations. + */ +#define DDCAPS_BLTQUEUE 0x00000080l + +/* + * Display hardware is capable of color space conversions during the blt operation. + */ +#define DDCAPS_BLTFOURCC 0x00000100l + +/* + * Display hardware is capable of stretching during blt operations. + */ +#define DDCAPS_BLTSTRETCH 0x00000200l + +/* + * Display hardware is shared with GDI. + */ +#define DDCAPS_GDI 0x00000400l + +/* + * Display hardware can overlay. + */ +#define DDCAPS_OVERLAY 0x00000800l + +/* + * Set if display hardware supports overlays but can not clip them. + */ +#define DDCAPS_OVERLAYCANTCLIP 0x00001000l + +/* + * Indicates that overlay hardware is capable of color space conversions during + * the overlay operation. + */ +#define DDCAPS_OVERLAYFOURCC 0x00002000l + +/* + * Indicates that stretching can be done by the overlay hardware. + */ +#define DDCAPS_OVERLAYSTRETCH 0x00004000l + +/* + * Indicates that unique DirectDrawPalettes can be created for DirectDrawSurfaces + * other than the primary surface. + */ +#define DDCAPS_PALETTE 0x00008000l + +/* + * Indicates that palette changes can be syncd with the veritcal refresh. + */ +#define DDCAPS_PALETTEVSYNC 0x00010000l + +/* + * Display hardware can return the current scan line. + */ +#define DDCAPS_READSCANLINE 0x00020000l + +/* + * Display hardware has stereo vision capabilities. DDSCAPS_PRIMARYSURFACELEFT + * can be created. + */ +#define DDCAPS_STEREOVIEW 0x00040000l + +/* + * Display hardware is capable of generating a vertical blank interrupt. + */ +#define DDCAPS_VBI 0x00080000l + +/* + * Supports the use of z buffers with blt operations. + */ +#define DDCAPS_ZBLTS 0x00100000l + +/* + * Supports Z Ordering of overlays. + */ +#define DDCAPS_ZOVERLAYS 0x00200000l + +/* + * Supports color key + */ +#define DDCAPS_COLORKEY 0x00400000l + +/* + * Supports alpha surfaces + */ +#define DDCAPS_ALPHA 0x00800000l + +/* + * colorkey is hardware assisted(DDCAPS_COLORKEY will also be set) + */ +#define DDCAPS_COLORKEYHWASSIST 0x01000000l + +/* + * no hardware support at all + */ +#define DDCAPS_NOHARDWARE 0x02000000l + +/* + * Display hardware is capable of color fill with bltter + */ +#define DDCAPS_BLTCOLORFILL 0x04000000l + +/* + * Display hardware is bank switched, and potentially very slow at + * random access to VRAM. + */ +#define DDCAPS_BANKSWITCHED 0x08000000l + +/* + * Display hardware is capable of depth filling Z-buffers with bltter + */ +#define DDCAPS_BLTDEPTHFILL 0x10000000l + +/* + * Display hardware is capable of clipping while bltting. + */ +#define DDCAPS_CANCLIP 0x20000000l + +/* + * Display hardware is capable of clipping while stretch bltting. + */ +#define DDCAPS_CANCLIPSTRETCHED 0x40000000l + +/* + * Display hardware is capable of bltting to or from system memory + */ +#define DDCAPS_CANBLTSYSMEM 0x80000000l + + + /**************************************************************************** + * + * MORE DIRECTDRAW DRIVER CAPABILITY FLAGS (dwCaps2) + * + ****************************************************************************/ + +/* + * Display hardware is certified + */ +#define DDCAPS2_CERTIFIED 0x00000001l + +/* + * Driver cannot interleave 2D operations (lock and blt) to surfaces with + * Direct3D rendering operations between calls to BeginScene() and EndScene() + */ +#define DDCAPS2_NO2DDURING3DSCENE 0x00000002l + +/* + * Display hardware contains a video port + */ +#define DDCAPS2_VIDEOPORT 0x00000004l + +/* + * The overlay can be automatically flipped according to the video port + * VSYNCs, providing automatic doubled buffered display of video port + * data using an overlay + */ +#define DDCAPS2_AUTOFLIPOVERLAY 0x00000008l + +/* + * Overlay can display each field of interlaced data individually while + * it is interleaved in memory without causing jittery artifacts. + */ +#define DDCAPS2_CANBOBINTERLEAVED 0x00000010l + +/* + * Overlay can display each field of interlaced data individually while + * it is not interleaved in memory without causing jittery artifacts. + */ +#define DDCAPS2_CANBOBNONINTERLEAVED 0x00000020l + +/* + * The overlay surface contains color controls (brightness, sharpness, etc.) + */ +#define DDCAPS2_COLORCONTROLOVERLAY 0x00000040l + +/* + * The primary surface contains color controls (gamma, etc.) + */ +#define DDCAPS2_COLORCONTROLPRIMARY 0x00000080l + +/* + * RGBZ -> RGB supported for 16:16 RGB:Z + */ +#define DDCAPS2_CANDROPZ16BIT 0x00000100l + +/* + * Driver supports non-local video memory. + */ +#define DDCAPS2_NONLOCALVIDMEM 0x00000200l + +/* + * Dirver supports non-local video memory but has different capabilities for + * non-local video memory surfaces. If this bit is set then so must + * DDCAPS2_NONLOCALVIDMEM. + */ +#define DDCAPS2_NONLOCALVIDMEMCAPS 0x00000400l + +/* + * Driver neither requires nor prefers surfaces to be pagelocked when performing + * blts involving system memory surfaces + */ +#define DDCAPS2_NOPAGELOCKREQUIRED 0x00000800l + +/* + * Driver can create surfaces which are wider than the primary surface + */ +#define DDCAPS2_WIDESURFACES 0x00001000l + +/* + * Driver supports bob without using a video port by handling the + * DDFLIP_ODD and DDFLIP_EVEN flags specified in Flip. + */ +#define DDCAPS2_CANFLIPODDEVEN 0x00002000l + +/* + * Driver supports bob using hardware + */ +#define DDCAPS2_CANBOBHARDWARE 0x00004000l + +/* + * Driver supports bltting any FOURCC surface to another surface of the same FOURCC + */ +#define DDCAPS2_COPYFOURCC 0x00008000l + + +/* + * Driver supports loadable gamma ramps for the primary surface + */ +#define DDCAPS2_PRIMARYGAMMA 0x00020000l + +/* + * Driver can render in windowed mode. + */ +#define DDCAPS2_CANRENDERWINDOWED 0x00080000l + +/* + * A calibrator is available to adjust the gamma ramp according to the + * physical display properties so that the result will be identical on + * all calibrated systems. + */ +#define DDCAPS2_CANCALIBRATEGAMMA 0x00100000l + +/* + * Indicates that the driver will respond to DDFLIP_INTERVALn flags + */ +#define DDCAPS2_FLIPINTERVAL 0x00200000l + +/* + * Indicates that the driver will respond to DDFLIP_NOVSYNC + */ +#define DDCAPS2_FLIPNOVSYNC 0x00400000l + + +/**************************************************************************** + * + * DIRECTDRAW FX ALPHA CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Supports alpha blending around the edge of a source color keyed surface. + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHAEDGEBLEND 0x00000001l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value becomes + * more opaque as the alpha value increases. (0 is transparent.) + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHAPIXELS 0x00000002l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value + * becomes more transparent as the alpha value increases. (0 is opaque.) + * This flag can only be set if DDCAPS_ALPHA is set. + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHAPIXELSNEG 0x00000004l + +/* + * Supports alpha only surfaces. The bit depth of an alpha only surface can be + * 1,2,4, or 8. The alpha value becomes more opaque as the alpha value increases. + * (0 is transparent.) + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHASURFACES 0x00000008l + +/* + * The depth of the alpha channel data can range can be 1,2,4, or 8. + * The NEG suffix indicates that this alpha channel becomes more transparent + * as the alpha value increases. (0 is opaque.) This flag can only be set if + * DDCAPS_ALPHA is set. + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHASURFACESNEG 0x00000010l + +/* + * Supports alpha blending around the edge of a source color keyed surface. + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHAEDGEBLEND 0x00000020l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value becomes + * more opaque as the alpha value increases. (0 is transparent.) + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHAPIXELS 0x00000040l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value + * becomes more transparent as the alpha value increases. (0 is opaque.) + * This flag can only be set if DDCAPS_ALPHA is set. + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHAPIXELSNEG 0x00000080l + +/* + * Supports alpha only surfaces. The bit depth of an alpha only surface can be + * 1,2,4, or 8. The alpha value becomes more opaque as the alpha value increases. + * (0 is transparent.) + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHASURFACES 0x00000100l + +/* + * The depth of the alpha channel data can range can be 1,2,4, or 8. + * The NEG suffix indicates that this alpha channel becomes more transparent + * as the alpha value increases. (0 is opaque.) This flag can only be set if + * DDCAPS_ALPHA is set. + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHASURFACESNEG 0x00000200l + +#if DIRECTDRAW_VERSION < 0x0600 +#endif //DIRECTDRAW_VERSION + + + + +/**************************************************************************** + * + * DIRECTDRAW FX CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Uses arithmetic operations to stretch and shrink surfaces during blt + * rather than pixel doubling techniques. Along the Y axis. + */ +#define DDFXCAPS_BLTARITHSTRETCHY 0x00000020l + +/* + * Uses arithmetic operations to stretch during blt + * rather than pixel doubling techniques. Along the Y axis. Only + * works for x1, x2, etc. + */ +#define DDFXCAPS_BLTARITHSTRETCHYN 0x00000010l + +/* + * Supports mirroring left to right in blt. + */ +#define DDFXCAPS_BLTMIRRORLEFTRIGHT 0x00000040l + +/* + * Supports mirroring top to bottom in blt. + */ +#define DDFXCAPS_BLTMIRRORUPDOWN 0x00000080l + +/* + * Supports arbitrary rotation for blts. + */ +#define DDFXCAPS_BLTROTATION 0x00000100l + +/* + * Supports 90 degree rotations for blts. + */ +#define DDFXCAPS_BLTROTATION90 0x00000200l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKX 0x00000400l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKXN 0x00000800l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * y axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKY 0x00001000l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the y axis (vertical direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKYN 0x00002000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHX 0x00004000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHXN 0x00008000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * y axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHY 0x00010000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the y axis (vertical direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHYN 0x00020000l + +/* + * Uses arithmetic operations to stretch and shrink surfaces during + * overlay rather than pixel doubling techniques. Along the Y axis + * for overlays. + */ +#define DDFXCAPS_OVERLAYARITHSTRETCHY 0x00040000l + +/* + * Uses arithmetic operations to stretch surfaces during + * overlay rather than pixel doubling techniques. Along the Y axis + * for overlays. Only works for x1, x2, etc. + */ +#define DDFXCAPS_OVERLAYARITHSTRETCHYN 0x00000008l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKX 0x00080000l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKXN 0x00100000l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * y axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKY 0x00200000l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the y axis (vertical direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKYN 0x00400000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHX 0x00800000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHXN 0x01000000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * y axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHY 0x02000000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the y axis (vertical direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHYN 0x04000000l + +/* + * DirectDraw supports mirroring of overlays across the vertical axis + */ +#define DDFXCAPS_OVERLAYMIRRORLEFTRIGHT 0x08000000l + +/* + * DirectDraw supports mirroring of overlays across the horizontal axis + */ +#define DDFXCAPS_OVERLAYMIRRORUPDOWN 0x10000000l + +/* + * Driver can do alpha blending for blits. + */ +#define DDFXCAPS_BLTALPHA 0x00000001l + +/* + * Driver can do geometric transformations (or warps) for blits. + */ +#define DDFXCAPS_BLTTRANSFORM 0x00000002l + +/* + * Driver can do surface-reconstruction filtering for warped blits. + */ +#define DDFXCAPS_BLTFILTER DDFXCAPS_BLTARITHSTRETCHY + +/* + * Driver can do alpha blending for overlays. + */ +#define DDFXCAPS_OVERLAYALPHA 0x00000004l + +/* + * Driver can do geometric transformations (or warps) for overlays. + */ +#define DDFXCAPS_OVERLAYTRANSFORM 0x20000000l + +/* + * Driver can do surface-reconstruction filtering for warped overlays. + */ +#define DDFXCAPS_OVERLAYFILTER DDFXCAPS_OVERLAYARITHSTRETCHY + + +/**************************************************************************** + * + * DIRECTDRAW STEREO VIEW CAPABILITIES + * + ****************************************************************************/ + +/* + * The stereo view is accomplished via enigma encoding. + */ +#define DDSVCAPS_ENIGMA 0x00000001l + +/* + * The stereo view is accomplished via high frequency flickering. + */ +#define DDSVCAPS_FLICKER 0x00000002l + +/* + * The stereo view is accomplished via red and blue filters applied + * to the left and right eyes. All images must adapt their colorspaces + * for this process. + */ +#define DDSVCAPS_REDBLUE 0x00000004l + +/* + * The stereo view is accomplished with split screen technology. + */ +#define DDSVCAPS_SPLIT 0x00000008l + +/**************************************************************************** + * + * DIRECTDRAWPALETTE CAPABILITIES + * + ****************************************************************************/ + +/* + * Index is 4 bits. There are sixteen color entries in the palette table. + */ +#define DDPCAPS_4BIT 0x00000001l + +/* + * Index is onto a 8 bit color index. This field is only valid with the + * DDPCAPS_1BIT, DDPCAPS_2BIT or DDPCAPS_4BIT capability and the target + * surface is in 8bpp. Each color entry is one byte long and is an index + * into destination surface's 8bpp palette. + */ +#define DDPCAPS_8BITENTRIES 0x00000002l + +/* + * Index is 8 bits. There are 256 color entries in the palette table. + */ +#define DDPCAPS_8BIT 0x00000004l + +/* + * Indicates that this DIRECTDRAWPALETTE should use the palette color array + * passed into the lpDDColorArray parameter to initialize the DIRECTDRAWPALETTE + * object. + */ +#define DDPCAPS_INITIALIZE 0x00000008l + +/* + * This palette is the one attached to the primary surface. Changing this + * table has immediate effect on the display unless DDPSETPAL_VSYNC is specified + * and supported. + */ +#define DDPCAPS_PRIMARYSURFACE 0x00000010l + +/* + * This palette is the one attached to the primary surface left. Changing + * this table has immediate effect on the display for the left eye unless + * DDPSETPAL_VSYNC is specified and supported. + */ +#define DDPCAPS_PRIMARYSURFACELEFT 0x00000020l + +/* + * This palette can have all 256 entries defined + */ +#define DDPCAPS_ALLOW256 0x00000040l + +/* + * This palette can have modifications to it synced with the monitors + * refresh rate. + */ +#define DDPCAPS_VSYNC 0x00000080l + +/* + * Index is 1 bit. There are two color entries in the palette table. + */ +#define DDPCAPS_1BIT 0x00000100l + +/* + * Index is 2 bit. There are four color entries in the palette table. + */ +#define DDPCAPS_2BIT 0x00000200l + +/* + * The peFlags member of PALETTEENTRY denotes an 8 bit alpha value + */ +#define DDPCAPS_ALPHA 0x00000400l + + +/**************************************************************************** + * + * DIRECTDRAWPALETTE SETENTRY CONSTANTS + * + ****************************************************************************/ + + +/**************************************************************************** + * + * DIRECTDRAWPALETTE GETENTRY CONSTANTS + * + ****************************************************************************/ + +/* 0 is the only legal value */ + +/**************************************************************************** + * + * DIRECTDRAWSURFACE SETPRIVATEDATA CONSTANTS + * + ****************************************************************************/ + +/* + * The passed pointer is an IUnknown ptr. The cbData argument to SetPrivateData + * must be set to sizeof(IUnknown*). DirectDraw will call AddRef through this + * pointer and Release when the private data is destroyed. This includes when + * the surface or palette is destroyed before such priovate data is destroyed. + */ +#define DDSPD_IUNKNOWNPOINTER 0x00000001L + +/* + * Private data is only valid for the current state of the object, + * as determined by the uniqueness value. + */ +#define DDSPD_VOLATILE 0x00000002L + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE SETPALETTE CONSTANTS + * + ****************************************************************************/ + + +/**************************************************************************** + * + * DIRECTDRAW BITDEPTH CONSTANTS + * + * NOTE: These are only used to indicate supported bit depths. These + * are flags only, they are not to be used as an actual bit depth. The + * absolute numbers 1, 2, 4, 8, 16, 24 and 32 are used to indicate actual + * bit depths in a surface or for changing the display mode. + * + ****************************************************************************/ + +/* + * 1 bit per pixel. + */ +#define DDBD_1 0x00004000l + +/* + * 2 bits per pixel. + */ +#define DDBD_2 0x00002000l + +/* + * 4 bits per pixel. + */ +#define DDBD_4 0x00001000l + +/* + * 8 bits per pixel. + */ +#define DDBD_8 0x00000800l + +/* + * 16 bits per pixel. + */ +#define DDBD_16 0x00000400l + +/* + * 24 bits per pixel. + */ +#define DDBD_24 0X00000200l + +/* + * 32 bits per pixel. + */ +#define DDBD_32 0x00000100l + +/**************************************************************************** + * + * DIRECTDRAWSURFACE SET/GET COLOR KEY FLAGS + * + ****************************************************************************/ + +/* + * Set if the structure contains a color space. Not set if the structure + * contains a single color key. + */ +#define DDCKEY_COLORSPACE 0x00000001l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a destination color key for blt operations. + */ +#define DDCKEY_DESTBLT 0x00000002l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a destination color key for overlay operations. + */ +#define DDCKEY_DESTOVERLAY 0x00000004l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a source color key for blt operations. + */ +#define DDCKEY_SRCBLT 0x00000008l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a source color key for overlay operations. + */ +#define DDCKEY_SRCOVERLAY 0x00000010l + + +/**************************************************************************** + * + * DIRECTDRAW COLOR KEY CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Supports transparent blting using a color key to identify the replaceable + * bits of the destination surface for RGB colors. + */ +#define DDCKEYCAPS_DESTBLT 0x00000001l + +/* + * Supports transparent blting using a color space to identify the replaceable + * bits of the destination surface for RGB colors. + */ +#define DDCKEYCAPS_DESTBLTCLRSPACE 0x00000002l + +/* + * Supports transparent blting using a color space to identify the replaceable + * bits of the destination surface for YUV colors. + */ +#define DDCKEYCAPS_DESTBLTCLRSPACEYUV 0x00000004l + +/* + * Supports transparent blting using a color key to identify the replaceable + * bits of the destination surface for YUV colors. + */ +#define DDCKEYCAPS_DESTBLTYUV 0x00000008l + +/* + * Supports overlaying using colorkeying of the replaceable bits of the surface + * being overlayed for RGB colors. + */ +#define DDCKEYCAPS_DESTOVERLAY 0x00000010l + +/* + * Supports a color space as the color key for the destination for RGB colors. + */ +#define DDCKEYCAPS_DESTOVERLAYCLRSPACE 0x00000020l + +/* + * Supports a color space as the color key for the destination for YUV colors. + */ +#define DDCKEYCAPS_DESTOVERLAYCLRSPACEYUV 0x00000040l + +/* + * Supports only one active destination color key value for visible overlay + * surfaces. + */ +#define DDCKEYCAPS_DESTOVERLAYONEACTIVE 0x00000080l + +/* + * Supports overlaying using colorkeying of the replaceable bits of the + * surface being overlayed for YUV colors. + */ +#define DDCKEYCAPS_DESTOVERLAYYUV 0x00000100l + +/* + * Supports transparent blting using the color key for the source with + * this surface for RGB colors. + */ +#define DDCKEYCAPS_SRCBLT 0x00000200l + +/* + * Supports transparent blting using a color space for the source with + * this surface for RGB colors. + */ +#define DDCKEYCAPS_SRCBLTCLRSPACE 0x00000400l + +/* + * Supports transparent blting using a color space for the source with + * this surface for YUV colors. + */ +#define DDCKEYCAPS_SRCBLTCLRSPACEYUV 0x00000800l + +/* + * Supports transparent blting using the color key for the source with + * this surface for YUV colors. + */ +#define DDCKEYCAPS_SRCBLTYUV 0x00001000l + +/* + * Supports overlays using the color key for the source with this + * overlay surface for RGB colors. + */ +#define DDCKEYCAPS_SRCOVERLAY 0x00002000l + +/* + * Supports overlays using a color space as the source color key for + * the overlay surface for RGB colors. + */ +#define DDCKEYCAPS_SRCOVERLAYCLRSPACE 0x00004000l + +/* + * Supports overlays using a color space as the source color key for + * the overlay surface for YUV colors. + */ +#define DDCKEYCAPS_SRCOVERLAYCLRSPACEYUV 0x00008000l + +/* + * Supports only one active source color key value for visible + * overlay surfaces. + */ +#define DDCKEYCAPS_SRCOVERLAYONEACTIVE 0x00010000l + +/* + * Supports overlays using the color key for the source with this + * overlay surface for YUV colors. + */ +#define DDCKEYCAPS_SRCOVERLAYYUV 0x00020000l + +/* + * there are no bandwidth trade-offs for using colorkey with an overlay + */ +#define DDCKEYCAPS_NOCOSTOVERLAY 0x00040000l + + +/**************************************************************************** + * + * DIRECTDRAW PIXELFORMAT FLAGS + * + ****************************************************************************/ + +/* + * The surface has alpha channel information in the pixel format. + */ +#define DDPF_ALPHAPIXELS 0x00000001l + +/* + * The pixel format contains alpha only information + */ +#define DDPF_ALPHA 0x00000002l + +/* + * The FourCC code is valid. + */ +#define DDPF_FOURCC 0x00000004l + +/* + * The surface is 4-bit color indexed. + */ +#define DDPF_PALETTEINDEXED4 0x00000008l + +/* + * The surface is indexed into a palette which stores indices + * into the destination surface's 8-bit palette. + */ +#define DDPF_PALETTEINDEXEDTO8 0x00000010l + +/* + * The surface is 8-bit color indexed. + */ +#define DDPF_PALETTEINDEXED8 0x00000020l + +/* + * The RGB data in the pixel format structure is valid. + */ +#define DDPF_RGB 0x00000040l + +/* + * The surface will accept pixel data in the format specified + * and compress it during the write. + */ +#define DDPF_COMPRESSED 0x00000080l + +/* + * The surface will accept RGB data and translate it during + * the write to YUV data. The format of the data to be written + * will be contained in the pixel format structure. The DDPF_RGB + * flag will be set. + */ +#define DDPF_RGBTOYUV 0x00000100l + +/* + * pixel format is YUV - YUV data in pixel format struct is valid + */ +#define DDPF_YUV 0x00000200l + +/* + * pixel format is a z buffer only surface + */ +#define DDPF_ZBUFFER 0x00000400l + +/* + * The surface is 1-bit color indexed. + */ +#define DDPF_PALETTEINDEXED1 0x00000800l + +/* + * The surface is 2-bit color indexed. + */ +#define DDPF_PALETTEINDEXED2 0x00001000l + +/* + * The surface contains Z information in the pixels + */ +#define DDPF_ZPIXELS 0x00002000l + +/* + * The surface contains stencil information along with Z + */ +#define DDPF_STENCILBUFFER 0x00004000l + +/* + * Premultiplied alpha format -- the color components have been + * premultiplied by the alpha component. + */ +#define DDPF_ALPHAPREMULT 0x00008000l + + +/* + * Luminance data in the pixel format is valid. + * Use this flag for luminance-only or luminance+alpha surfaces, + * the bit depth is then ddpf.dwLuminanceBitCount. + */ +#define DDPF_LUMINANCE 0x00020000l + +/* + * Luminance data in the pixel format is valid. + * Use this flag when hanging luminance off bumpmap surfaces, + * the bit mask for the luminance portion of the pixel is then + * ddpf.dwBumpLuminanceBitMask + */ +#define DDPF_BUMPLUMINANCE 0x00040000l + +/* + * Bump map dUdV data in the pixel format is valid. + */ +#define DDPF_BUMPDUDV 0x00080000l + +/*=========================================================================== + * + * + * DIRECTDRAW CALLBACK FLAGS + * + * + *==========================================================================*/ + +/**************************************************************************** + * + * DIRECTDRAW ENUMSURFACES FLAGS + * + ****************************************************************************/ + +/* + * Enumerate all of the surfaces that meet the search criterion. + */ +#define DDENUMSURFACES_ALL 0x00000001l + +/* + * A search hit is a surface that matches the surface description. + */ +#define DDENUMSURFACES_MATCH 0x00000002l + +/* + * A search hit is a surface that does not match the surface description. + */ +#define DDENUMSURFACES_NOMATCH 0x00000004l + +/* + * Enumerate the first surface that can be created which meets the search criterion. + */ +#define DDENUMSURFACES_CANBECREATED 0x00000008l + +/* + * Enumerate the surfaces that already exist that meet the search criterion. + */ +#define DDENUMSURFACES_DOESEXIST 0x00000010l + + +/**************************************************************************** + * + * DIRECTDRAW SETDISPLAYMODE FLAGS + * + ****************************************************************************/ + +/* + * The desired mode is a standard VGA mode + */ +#define DDSDM_STANDARDVGAMODE 0x00000001l + + + +/**************************************************************************** + * + * DIRECTDRAW ENUMDISPLAYMODES FLAGS + * + ****************************************************************************/ + +/* + * Enumerate Modes with different refresh rates. EnumDisplayModes guarantees + * that a particular mode will be enumerated only once. This flag specifies whether + * the refresh rate is taken into account when determining if a mode is unique. + */ +#define DDEDM_REFRESHRATES 0x00000001l + +/* + * Enumerate VGA modes. Specify this flag if you wish to enumerate supported VGA + * modes such as mode 0x13 in addition to the usual ModeX modes (which are always + * enumerated if the application has previously called SetCooperativeLevel with the + * DDSCL_ALLOWMODEX flag set). + */ +#define DDEDM_STANDARDVGAMODES 0x00000002L + + +/**************************************************************************** + * + * DIRECTDRAW SETCOOPERATIVELEVEL FLAGS + * + ****************************************************************************/ + +/* + * Exclusive mode owner will be responsible for the entire primary surface. + * GDI can be ignored. used with DD + */ +#define DDSCL_FULLSCREEN 0x00000001l + +/* + * allow CTRL_ALT_DEL to work while in fullscreen exclusive mode + */ +#define DDSCL_ALLOWREBOOT 0x00000002l + +/* + * prevents DDRAW from modifying the application window. + * prevents DDRAW from minimize/restore the application window on activation. + */ +#define DDSCL_NOWINDOWCHANGES 0x00000004l + +/* + * app wants to work as a regular Windows application + */ +#define DDSCL_NORMAL 0x00000008l + +/* + * app wants exclusive access + */ +#define DDSCL_EXCLUSIVE 0x00000010l + + +/* + * app can deal with non-windows display modes + */ +#define DDSCL_ALLOWMODEX 0x00000040l + +/* + * this window will receive the focus messages + */ +#define DDSCL_SETFOCUSWINDOW 0x00000080l + +/* + * this window is associated with the DDRAW object and will + * cover the screen in fullscreen mode + */ +#define DDSCL_SETDEVICEWINDOW 0x00000100l + +/* + * app wants DDRAW to create a window to be associated with the + * DDRAW object + */ +#define DDSCL_CREATEDEVICEWINDOW 0x00000200l + +/* + * App explicitly asks DDRAW/D3D to be multithread safe. This makes D3D + * take the global crtisec more frequently. + */ +#define DDSCL_MULTITHREADED 0x00000400l + +/* + * App hints that it would like to keep the FPU set up for optimal Direct3D + * performance (single precision and exceptions disabled) so Direct3D + * does not need to explicitly set the FPU each time + */ +#define DDSCL_FPUSETUP 0x00000800l + + +/**************************************************************************** + * + * DIRECTDRAW BLT FLAGS + * + ****************************************************************************/ + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the destination surface as the alpha channel for this blt. + */ +#define DDBLT_ALPHADEST 0x00000001l + +/* + * Use the dwConstAlphaDest field in the DDBLTFX structure as the alpha channel + * for the destination surface for this blt. + */ +#define DDBLT_ALPHADESTCONSTOVERRIDE 0x00000002l + +/* + * The NEG suffix indicates that the destination surface becomes more + * transparent as the alpha value increases. (0 is opaque) + */ +#define DDBLT_ALPHADESTNEG 0x00000004l + +/* + * Use the lpDDSAlphaDest field in the DDBLTFX structure as the alpha + * channel for the destination for this blt. + */ +#define DDBLT_ALPHADESTSURFACEOVERRIDE 0x00000008l + +/* + * Use the dwAlphaEdgeBlend field in the DDBLTFX structure as the alpha channel + * for the edges of the image that border the color key colors. + */ +#define DDBLT_ALPHAEDGEBLEND 0x00000010l + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the source surface as the alpha channel for this blt. + */ +#define DDBLT_ALPHASRC 0x00000020l + +/* + * Use the dwConstAlphaSrc field in the DDBLTFX structure as the alpha channel + * for the source for this blt. + */ +#define DDBLT_ALPHASRCCONSTOVERRIDE 0x00000040l + +/* + * The NEG suffix indicates that the source surface becomes more transparent + * as the alpha value increases. (0 is opaque) + */ +#define DDBLT_ALPHASRCNEG 0x00000080l + +/* + * Use the lpDDSAlphaSrc field in the DDBLTFX structure as the alpha channel + * for the source for this blt. + */ +#define DDBLT_ALPHASRCSURFACEOVERRIDE 0x00000100l + +/* + * Do this blt asynchronously through the FIFO in the order received. If + * there is no room in the hardware FIFO fail the call. + */ +#define DDBLT_ASYNC 0x00000200l + +/* + * Uses the dwFillColor field in the DDBLTFX structure as the RGB color + * to fill the destination rectangle on the destination surface with. + */ +#define DDBLT_COLORFILL 0x00000400l + +/* + * Uses the dwDDFX field in the DDBLTFX structure to specify the effects + * to use for the blt. + */ +#define DDBLT_DDFX 0x00000800l + +/* + * Uses the dwDDROPS field in the DDBLTFX structure to specify the ROPS + * that are not part of the Win32 API. + */ +#define DDBLT_DDROPS 0x00001000l + +/* + * Use the color key associated with the destination surface. + */ +#define DDBLT_KEYDEST 0x00002000l + +/* + * Use the dckDestColorkey field in the DDBLTFX structure as the color key + * for the destination surface. + */ +#define DDBLT_KEYDESTOVERRIDE 0x00004000l + +/* + * Use the color key associated with the source surface. + */ +#define DDBLT_KEYSRC 0x00008000l + +/* + * Use the dckSrcColorkey field in the DDBLTFX structure as the color key + * for the source surface. + */ +#define DDBLT_KEYSRCOVERRIDE 0x00010000l + +/* + * Use the dwROP field in the DDBLTFX structure for the raster operation + * for this blt. These ROPs are the same as the ones defined in the Win32 API. + */ +#define DDBLT_ROP 0x00020000l + +/* + * Use the dwRotationAngle field in the DDBLTFX structure as the angle + * (specified in 1/100th of a degree) to rotate the surface. + */ +#define DDBLT_ROTATIONANGLE 0x00040000l + +/* + * Z-buffered blt using the z-buffers attached to the source and destination + * surfaces and the dwZBufferOpCode field in the DDBLTFX structure as the + * z-buffer opcode. + */ +#define DDBLT_ZBUFFER 0x00080000l + +/* + * Z-buffered blt using the dwConstDest Zfield and the dwZBufferOpCode field + * in the DDBLTFX structure as the z-buffer and z-buffer opcode respectively + * for the destination. + */ +#define DDBLT_ZBUFFERDESTCONSTOVERRIDE 0x00100000l + +/* + * Z-buffered blt using the lpDDSDestZBuffer field and the dwZBufferOpCode + * field in the DDBLTFX structure as the z-buffer and z-buffer opcode + * respectively for the destination. + */ +#define DDBLT_ZBUFFERDESTOVERRIDE 0x00200000l + +/* + * Z-buffered blt using the dwConstSrcZ field and the dwZBufferOpCode field + * in the DDBLTFX structure as the z-buffer and z-buffer opcode respectively + * for the source. + */ +#define DDBLT_ZBUFFERSRCCONSTOVERRIDE 0x00400000l + +/* + * Z-buffered blt using the lpDDSSrcZBuffer field and the dwZBufferOpCode + * field in the DDBLTFX structure as the z-buffer and z-buffer opcode + * respectively for the source. + */ +#define DDBLT_ZBUFFERSRCOVERRIDE 0x00800000l + +/* + * wait until the device is ready to handle the blt + * this will cause blt to not return DDERR_WASSTILLDRAWING + */ +#define DDBLT_WAIT 0x01000000l + +/* + * Uses the dwFillDepth field in the DDBLTFX structure as the depth value + * to fill the destination rectangle on the destination Z-buffer surface + * with. + */ +#define DDBLT_DEPTHFILL 0x02000000l + + +/**************************************************************************** + * + * BLTFAST FLAGS + * + ****************************************************************************/ + +#define DDBLTFAST_NOCOLORKEY 0x00000000 +#define DDBLTFAST_SRCCOLORKEY 0x00000001 +#define DDBLTFAST_DESTCOLORKEY 0x00000002 +#define DDBLTFAST_WAIT 0x00000010 + + + + +/**************************************************************************** + * + * FLIP FLAGS + * + ****************************************************************************/ + +#define DDFLIP_WAIT 0x00000001L + +/* + * Indicates that the target surface contains the even field of video data. + * This flag is only valid with an overlay surface. + */ +#define DDFLIP_EVEN 0x00000002L + +/* + * Indicates that the target surface contains the odd field of video data. + * This flag is only valid with an overlay surface. + */ +#define DDFLIP_ODD 0x00000004L + +/* + * Causes DirectDraw to perform the physical flip immediately and return + * to the application. Typically, what was the front buffer but is now the back + * buffer will still be visible (depending on timing) until the next vertical + * retrace. Subsequent operations involving the two flipped surfaces will + * not check to see if the physical flip has finished (i.e. will not return + * DDERR_WASSTILLDRAWING for that reason (but may for other reasons)). + * This allows an application to perform Flips at a higher frequency than the + * monitor refresh rate, but may introduce visible artifacts. + * Only effective if DDCAPS2_FLIPNOVSYNC is set. If that bit is not set, + * DDFLIP_NOVSYNC has no effect. + */ +#define DDFLIP_NOVSYNC 0x00000008L + + +/* + * Flip Interval Flags. These flags indicate how many vertical retraces to wait between + * each flip. The default is one. DirectDraw will return DDERR_WASSTILLDRAWING for each + * surface involved in the flip until the specified number of vertical retraces has + * ocurred. Only effective if DDCAPS2_FLIPINTERVAL is set. If that bit is not set, + * DDFLIP_INTERVALn has no effect. + */ + +/* + * DirectDraw will flip on every other vertical sync + */ +#define DDFLIP_INTERVAL2 0x02000000L + + +/* + * DirectDraw will flip on every third vertical sync + */ +#define DDFLIP_INTERVAL3 0x03000000L + + +/* + * DirectDraw will flip on every fourth vertical sync + */ +#define DDFLIP_INTERVAL4 0x04000000L + + + +/**************************************************************************** + * + * DIRECTDRAW SURFACE OVERLAY FLAGS + * + ****************************************************************************/ + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the destination surface as the alpha channel for the + * destination overlay. + */ +#define DDOVER_ALPHADEST 0x00000001l + +/* + * Use the dwConstAlphaDest field in the DDOVERLAYFX structure as the + * destination alpha channel for this overlay. + */ +#define DDOVER_ALPHADESTCONSTOVERRIDE 0x00000002l + +/* + * The NEG suffix indicates that the destination surface becomes more + * transparent as the alpha value increases. + */ +#define DDOVER_ALPHADESTNEG 0x00000004l + +/* + * Use the lpDDSAlphaDest field in the DDOVERLAYFX structure as the alpha + * channel destination for this overlay. + */ +#define DDOVER_ALPHADESTSURFACEOVERRIDE 0x00000008l + +/* + * Use the dwAlphaEdgeBlend field in the DDOVERLAYFX structure as the alpha + * channel for the edges of the image that border the color key colors. + */ +#define DDOVER_ALPHAEDGEBLEND 0x00000010l + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the source surface as the source alpha channel for this overlay. + */ +#define DDOVER_ALPHASRC 0x00000020l + +/* + * Use the dwConstAlphaSrc field in the DDOVERLAYFX structure as the source + * alpha channel for this overlay. + */ +#define DDOVER_ALPHASRCCONSTOVERRIDE 0x00000040l + +/* + * The NEG suffix indicates that the source surface becomes more transparent + * as the alpha value increases. + */ +#define DDOVER_ALPHASRCNEG 0x00000080l + +/* + * Use the lpDDSAlphaSrc field in the DDOVERLAYFX structure as the alpha channel + * source for this overlay. + */ +#define DDOVER_ALPHASRCSURFACEOVERRIDE 0x00000100l + +/* + * Turn this overlay off. + */ +#define DDOVER_HIDE 0x00000200l + +/* + * Use the color key associated with the destination surface. + */ +#define DDOVER_KEYDEST 0x00000400l + +/* + * Use the dckDestColorkey field in the DDOVERLAYFX structure as the color key + * for the destination surface + */ +#define DDOVER_KEYDESTOVERRIDE 0x00000800l + +/* + * Use the color key associated with the source surface. + */ +#define DDOVER_KEYSRC 0x00001000l + +/* + * Use the dckSrcColorkey field in the DDOVERLAYFX structure as the color key + * for the source surface. + */ +#define DDOVER_KEYSRCOVERRIDE 0x00002000l + +/* + * Turn this overlay on. + */ +#define DDOVER_SHOW 0x00004000l + +/* + * Add a dirty rect to an emulated overlayed surface. + */ +#define DDOVER_ADDDIRTYRECT 0x00008000l + +/* + * Redraw all dirty rects on an emulated overlayed surface. + */ +#define DDOVER_REFRESHDIRTYRECTS 0x00010000l + +/* + * Redraw the entire surface on an emulated overlayed surface. + */ +#define DDOVER_REFRESHALL 0x00020000l + + +/* + * Use the overlay FX flags to define special overlay FX + */ +#define DDOVER_DDFX 0x00080000l + +/* + * Autoflip the overlay when ever the video port autoflips + */ +#define DDOVER_AUTOFLIP 0x00100000l + +/* + * Display each field of video port data individually without + * causing any jittery artifacts + */ +#define DDOVER_BOB 0x00200000l + +/* + * Indicates that bob/weave decisions should not be overridden by other + * interfaces. + */ +#define DDOVER_OVERRIDEBOBWEAVE 0x00400000l + +/* + * Indicates that the surface memory is composed of interleaved fields. + */ +#define DDOVER_INTERLEAVED 0x00800000l + +/* + * Indicates that bob will be performed using hardware rather than + * software or emulated. + */ +#define DDOVER_BOBHARDWARE 0x01000000l + + + + + + + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE LOCK FLAGS + * + ****************************************************************************/ + +/* + * The default. Set to indicate that Lock should return a valid memory pointer + * to the top of the specified rectangle. If no rectangle is specified then a + * pointer to the top of the surface is returned. + */ +#define DDLOCK_SURFACEMEMORYPTR 0x00000000L // default + +/* + * Set to indicate that Lock should wait until it can obtain a valid memory + * pointer before returning. If this bit is set, Lock will never return + * DDERR_WASSTILLDRAWING. + */ +#define DDLOCK_WAIT 0x00000001L + +/* + * Set if an event handle is being passed to Lock. Lock will trigger the event + * when it can return the surface memory pointer requested. + */ +#define DDLOCK_EVENT 0x00000002L + +/* + * Indicates that the surface being locked will only be read from. + */ +#define DDLOCK_READONLY 0x00000010L + +/* + * Indicates that the surface being locked will only be written to + */ +#define DDLOCK_WRITEONLY 0x00000020L + + +/* + * Indicates that a system wide lock should not be taken when this surface + * is locked. This has several advantages (cursor responsiveness, ability + * to call more Windows functions, easier debugging) when locking video + * memory surfaces. However, an application specifying this flag must + * comply with a number of conditions documented in the help file. + * Furthermore, this flag cannot be specified when locking the primary. + */ +#define DDLOCK_NOSYSLOCK 0x00000800L + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE PAGELOCK FLAGS + * + ****************************************************************************/ + +/* + * No flags defined at present + */ + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE PAGEUNLOCK FLAGS + * + ****************************************************************************/ + +/* + * No flags defined at present + */ + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE BLT FX FLAGS + * + ****************************************************************************/ + +/* + * If stretching, use arithmetic stretching along the Y axis for this blt. + */ +#define DDBLTFX_ARITHSTRETCHY 0x00000001l + +/* + * Do this blt mirroring the surface left to right. Spin the + * surface around its y-axis. + */ +#define DDBLTFX_MIRRORLEFTRIGHT 0x00000002l + +/* + * Do this blt mirroring the surface up and down. Spin the surface + * around its x-axis. + */ +#define DDBLTFX_MIRRORUPDOWN 0x00000004l + +/* + * Schedule this blt to avoid tearing. + */ +#define DDBLTFX_NOTEARING 0x00000008l + +/* + * Do this blt rotating the surface one hundred and eighty degrees. + */ +#define DDBLTFX_ROTATE180 0x00000010l + +/* + * Do this blt rotating the surface two hundred and seventy degrees. + */ +#define DDBLTFX_ROTATE270 0x00000020l + +/* + * Do this blt rotating the surface ninety degrees. + */ +#define DDBLTFX_ROTATE90 0x00000040l + +/* + * Do this z blt using dwZBufferLow and dwZBufferHigh as range values + * specified to limit the bits copied from the source surface. + */ +#define DDBLTFX_ZBUFFERRANGE 0x00000080l + +/* + * Do this z blt adding the dwZBufferBaseDest to each of the sources z values + * before comparing it with the desting z values. + */ +#define DDBLTFX_ZBUFFERBASEDEST 0x00000100l + +/**************************************************************************** + * + * DIRECTDRAWSURFACE OVERLAY FX FLAGS + * + ****************************************************************************/ + +/* + * If stretching, use arithmetic stretching along the Y axis for this overlay. + */ +#define DDOVERFX_ARITHSTRETCHY 0x00000001l + +/* + * Mirror the overlay across the vertical axis + */ +#define DDOVERFX_MIRRORLEFTRIGHT 0x00000002l + +/* + * Mirror the overlay across the horizontal axis + */ +#define DDOVERFX_MIRRORUPDOWN 0x00000004l + +/**************************************************************************** + * + * Flags for dwDDFX member of DDSPRITEFX structure + * + ****************************************************************************/ +/* + * Use affine transformation matrix in fTransform member. + */ +#define DDSPRITEFX_AFFINETRANSFORM 0x00000001l + +/* + * Use RGBA scaling factors in ddrgbaScaleFactors member. + */ +#define DDSPRITEFX_RGBASCALING 0x00000002l + +/* + * Degrade RGBA scaling factors to accommodate driver's capabilities. + */ +#define DDSPRITEFX_DEGRADERGBASCALING 0x00000004l + +/* + * Do bilinear filtering of stretched or warped sprite. + */ +#define DDSPRITEFX_BILINEARFILTER 0x00000008l + +/* + * Do "blur" filtering of stretched or warped sprite. + */ +#define DDSPRITEFX_BLURFILTER 0x00000010l + +/* + * Do "flat" filtering of stretched or warped sprite. + */ +#define DDSPRITEFX_FLATFILTER 0x00000020l + +/* + * Degrade filtering operation to accommodate driver's capabilities. + */ +#define DDSPRITEFX_DEGRADEFILTER 0x00000040l + + +/**************************************************************************** + * + * DIRECTDRAW WAITFORVERTICALBLANK FLAGS + * + ****************************************************************************/ + +/* + * return when the vertical blank interval begins + */ +#define DDWAITVB_BLOCKBEGIN 0x00000001l + +/* + * set up an event to trigger when the vertical blank begins + */ +#define DDWAITVB_BLOCKBEGINEVENT 0x00000002l + +/* + * return when the vertical blank interval ends and display begins + */ +#define DDWAITVB_BLOCKEND 0x00000004l + +/**************************************************************************** + * + * DIRECTDRAW GETFLIPSTATUS FLAGS + * + ****************************************************************************/ + +/* + * is it OK to flip now? + */ +#define DDGFS_CANFLIP 0x00000001l + +/* + * is the last flip finished? + */ +#define DDGFS_ISFLIPDONE 0x00000002l + +/**************************************************************************** + * + * DIRECTDRAW GETBLTSTATUS FLAGS + * + ****************************************************************************/ + +/* + * is it OK to blt now? + */ +#define DDGBS_CANBLT 0x00000001l + +/* + * is the blt to the surface finished? + */ +#define DDGBS_ISBLTDONE 0x00000002l + + +/**************************************************************************** + * + * DIRECTDRAW ENUMOVERLAYZORDER FLAGS + * + ****************************************************************************/ + +/* + * Enumerate overlays back to front. + */ +#define DDENUMOVERLAYZ_BACKTOFRONT 0x00000000l + +/* + * Enumerate overlays front to back + */ +#define DDENUMOVERLAYZ_FRONTTOBACK 0x00000001l + +/**************************************************************************** + * + * DIRECTDRAW UPDATEOVERLAYZORDER FLAGS + * + ****************************************************************************/ + +/* + * Send overlay to front + */ +#define DDOVERZ_SENDTOFRONT 0x00000000l + +/* + * Send overlay to back + */ +#define DDOVERZ_SENDTOBACK 0x00000001l + +/* + * Move Overlay forward + */ +#define DDOVERZ_MOVEFORWARD 0x00000002l + +/* + * Move Overlay backward + */ +#define DDOVERZ_MOVEBACKWARD 0x00000003l + +/* + * Move Overlay in front of relative surface + */ +#define DDOVERZ_INSERTINFRONTOF 0x00000004l + +/* + * Move Overlay in back of relative surface + */ +#define DDOVERZ_INSERTINBACKOF 0x00000005l + + + +/**************************************************************************** + * + * DIRECTDRAW SETGAMMARAMP FLAGS + * + ****************************************************************************/ + +/* + * Request calibrator to adjust the gamma ramp according to the physical + * properties of the display so that the result should appear identical + * on all systems. + */ +#define DDSGR_CALIBRATE 0x00000001L + + +/*=========================================================================== + * + * + * DIRECTDRAW RETURN CODES + * + * The return values from DirectDraw Commands and Surface that return an HRESULT + * are codes from DirectDraw concerning the results of the action + * requested by DirectDraw. + * + *==========================================================================*/ + +/* + * Status is OK + * + * Issued by: DirectDraw Commands and all callbacks + */ +#define DD_OK 0 +#define DD_FALSE S_FALSE + +/**************************************************************************** + * + * DIRECTDRAW ENUMCALLBACK RETURN VALUES + * + * EnumCallback returns are used to control the flow of the DIRECTDRAW and + * DIRECTDRAWSURFACE object enumerations. They can only be returned by + * enumeration callback routines. + * + ****************************************************************************/ + +/* + * stop the enumeration + */ +#define DDENUMRET_CANCEL 0 + +/* + * continue the enumeration + */ +#define DDENUMRET_OK 1 + +/**************************************************************************** + * + * DIRECTDRAW ERRORS + * + * Errors are represented by negative values and cannot be combined. + * + ****************************************************************************/ + +/* + * This object is already initialized + */ +#define DDERR_ALREADYINITIALIZED MAKE_DDHRESULT( 5 ) + +/* + * This surface can not be attached to the requested surface. + */ +#define DDERR_CANNOTATTACHSURFACE MAKE_DDHRESULT( 10 ) + +/* + * This surface can not be detached from the requested surface. + */ +#define DDERR_CANNOTDETACHSURFACE MAKE_DDHRESULT( 20 ) + +/* + * Support is currently not available. + */ +#define DDERR_CURRENTLYNOTAVAIL MAKE_DDHRESULT( 40 ) + +/* + * An exception was encountered while performing the requested operation + */ +#define DDERR_EXCEPTION MAKE_DDHRESULT( 55 ) + +/* + * Generic failure. + */ +#define DDERR_GENERIC E_FAIL + +/* + * Height of rectangle provided is not a multiple of reqd alignment + */ +#define DDERR_HEIGHTALIGN MAKE_DDHRESULT( 90 ) + +/* + * Unable to match primary surface creation request with existing + * primary surface. + */ +#define DDERR_INCOMPATIBLEPRIMARY MAKE_DDHRESULT( 95 ) + +/* + * One or more of the caps bits passed to the callback are incorrect. + */ +#define DDERR_INVALIDCAPS MAKE_DDHRESULT( 100 ) + +/* + * DirectDraw does not support provided Cliplist. + */ +#define DDERR_INVALIDCLIPLIST MAKE_DDHRESULT( 110 ) + +/* + * DirectDraw does not support the requested mode + */ +#define DDERR_INVALIDMODE MAKE_DDHRESULT( 120 ) + +/* + * DirectDraw received a pointer that was an invalid DIRECTDRAW object. + */ +#define DDERR_INVALIDOBJECT MAKE_DDHRESULT( 130 ) + +/* + * One or more of the parameters passed to the callback function are + * incorrect. + */ +#define DDERR_INVALIDPARAMS E_INVALIDARG + +/* + * pixel format was invalid as specified + */ +#define DDERR_INVALIDPIXELFORMAT MAKE_DDHRESULT( 145 ) + +/* + * Rectangle provided was invalid. + */ +#define DDERR_INVALIDRECT MAKE_DDHRESULT( 150 ) + +/* + * Operation could not be carried out because one or more surfaces are locked + */ +#define DDERR_LOCKEDSURFACES MAKE_DDHRESULT( 160 ) + +/* + * There is no 3D present. + */ +#define DDERR_NO3D MAKE_DDHRESULT( 170 ) + +/* + * Operation could not be carried out because there is no alpha accleration + * hardware present or available. + */ +#define DDERR_NOALPHAHW MAKE_DDHRESULT( 180 ) + + +/* + * no clip list available + */ +#define DDERR_NOCLIPLIST MAKE_DDHRESULT( 205 ) + +/* + * Operation could not be carried out because there is no color conversion + * hardware present or available. + */ +#define DDERR_NOCOLORCONVHW MAKE_DDHRESULT( 210 ) + +/* + * Create function called without DirectDraw object method SetCooperativeLevel + * being called. + */ +#define DDERR_NOCOOPERATIVELEVELSET MAKE_DDHRESULT( 212 ) + +/* + * Surface doesn't currently have a color key + */ +#define DDERR_NOCOLORKEY MAKE_DDHRESULT( 215 ) + +/* + * Operation could not be carried out because there is no hardware support + * of the dest color key. + */ +#define DDERR_NOCOLORKEYHW MAKE_DDHRESULT( 220 ) + +/* + * No DirectDraw support possible with current display driver + */ +#define DDERR_NODIRECTDRAWSUPPORT MAKE_DDHRESULT( 222 ) + +/* + * Operation requires the application to have exclusive mode but the + * application does not have exclusive mode. + */ +#define DDERR_NOEXCLUSIVEMODE MAKE_DDHRESULT( 225 ) + +/* + * Flipping visible surfaces is not supported. + */ +#define DDERR_NOFLIPHW MAKE_DDHRESULT( 230 ) + +/* + * There is no GDI present. + */ +#define DDERR_NOGDI MAKE_DDHRESULT( 240 ) + +/* + * Operation could not be carried out because there is no hardware present + * or available. + */ +#define DDERR_NOMIRRORHW MAKE_DDHRESULT( 250 ) + +/* + * Requested item was not found + */ +#define DDERR_NOTFOUND MAKE_DDHRESULT( 255 ) + +/* + * Operation could not be carried out because there is no overlay hardware + * present or available. + */ +#define DDERR_NOOVERLAYHW MAKE_DDHRESULT( 260 ) + +/* + * Operation could not be carried out because the source and destination + * rectangles are on the same surface and overlap each other. + */ +#define DDERR_OVERLAPPINGRECTS MAKE_DDHRESULT( 270 ) + +/* + * Operation could not be carried out because there is no appropriate raster + * op hardware present or available. + */ +#define DDERR_NORASTEROPHW MAKE_DDHRESULT( 280 ) + +/* + * Operation could not be carried out because there is no rotation hardware + * present or available. + */ +#define DDERR_NOROTATIONHW MAKE_DDHRESULT( 290 ) + +/* + * Operation could not be carried out because there is no hardware support + * for stretching + */ +#define DDERR_NOSTRETCHHW MAKE_DDHRESULT( 310 ) + +/* + * DirectDrawSurface is not in 4 bit color palette and the requested operation + * requires 4 bit color palette. + */ +#define DDERR_NOT4BITCOLOR MAKE_DDHRESULT( 316 ) + +/* + * DirectDrawSurface is not in 4 bit color index palette and the requested + * operation requires 4 bit color index palette. + */ +#define DDERR_NOT4BITCOLORINDEX MAKE_DDHRESULT( 317 ) + +/* + * DirectDraw Surface is not in 8 bit color mode and the requested operation + * requires 8 bit color. + */ +#define DDERR_NOT8BITCOLOR MAKE_DDHRESULT( 320 ) + +/* + * Operation could not be carried out because there is no texture mapping + * hardware present or available. + */ +#define DDERR_NOTEXTUREHW MAKE_DDHRESULT( 330 ) + +/* + * Operation could not be carried out because there is no hardware support + * for vertical blank synchronized operations. + */ +#define DDERR_NOVSYNCHW MAKE_DDHRESULT( 335 ) + +/* + * Operation could not be carried out because there is no hardware support + * for zbuffer blting. + */ +#define DDERR_NOZBUFFERHW MAKE_DDHRESULT( 340 ) + +/* + * Overlay surfaces could not be z layered based on their BltOrder because + * the hardware does not support z layering of overlays. + */ +#define DDERR_NOZOVERLAYHW MAKE_DDHRESULT( 350 ) + +/* + * The hardware needed for the requested operation has already been + * allocated. + */ +#define DDERR_OUTOFCAPS MAKE_DDHRESULT( 360 ) + +/* + * DirectDraw does not have enough memory to perform the operation. + */ +#define DDERR_OUTOFMEMORY E_OUTOFMEMORY + +/* + * DirectDraw does not have enough memory to perform the operation. + */ +#define DDERR_OUTOFVIDEOMEMORY MAKE_DDHRESULT( 380 ) + +/* + * hardware does not support clipped overlays + */ +#define DDERR_OVERLAYCANTCLIP MAKE_DDHRESULT( 382 ) + +/* + * Can only have ony color key active at one time for overlays + */ +#define DDERR_OVERLAYCOLORKEYONLYONEACTIVE MAKE_DDHRESULT( 384 ) + +/* + * Access to this palette is being refused because the palette is already + * locked by another thread. + */ +#define DDERR_PALETTEBUSY MAKE_DDHRESULT( 387 ) + +/* + * No src color key specified for this operation. + */ +#define DDERR_COLORKEYNOTSET MAKE_DDHRESULT( 400 ) + +/* + * This surface is already attached to the surface it is being attached to. + */ +#define DDERR_SURFACEALREADYATTACHED MAKE_DDHRESULT( 410 ) + +/* + * This surface is already a dependency of the surface it is being made a + * dependency of. + */ +#define DDERR_SURFACEALREADYDEPENDENT MAKE_DDHRESULT( 420 ) + +/* + * Access to this surface is being refused because the surface is already + * locked by another thread. + */ +#define DDERR_SURFACEBUSY MAKE_DDHRESULT( 430 ) + +/* + * Access to this surface is being refused because no driver exists + * which can supply a pointer to the surface. + * This is most likely to happen when attempting to lock the primary + * surface when no DCI provider is present. + * Will also happen on attempts to lock an optimized surface. + */ +#define DDERR_CANTLOCKSURFACE MAKE_DDHRESULT( 435 ) + +/* + * Access to Surface refused because Surface is obscured. + */ +#define DDERR_SURFACEISOBSCURED MAKE_DDHRESULT( 440 ) + +/* + * Access to this surface is being refused because the surface is gone. + * The DIRECTDRAWSURFACE object representing this surface should + * have Restore called on it. + */ +#define DDERR_SURFACELOST MAKE_DDHRESULT( 450 ) + +/* + * The requested surface is not attached. + */ +#define DDERR_SURFACENOTATTACHED MAKE_DDHRESULT( 460 ) + +/* + * Height requested by DirectDraw is too large. + */ +#define DDERR_TOOBIGHEIGHT MAKE_DDHRESULT( 470 ) + +/* + * Size requested by DirectDraw is too large -- The individual height and + * width are OK. + */ +#define DDERR_TOOBIGSIZE MAKE_DDHRESULT( 480 ) + +/* + * Width requested by DirectDraw is too large. + */ +#define DDERR_TOOBIGWIDTH MAKE_DDHRESULT( 490 ) + +/* + * Action not supported. + */ +#define DDERR_UNSUPPORTED E_NOTIMPL + +/* + * FOURCC format requested is unsupported by DirectDraw + */ +#define DDERR_UNSUPPORTEDFORMAT MAKE_DDHRESULT( 510 ) + +/* + * Bitmask in the pixel format requested is unsupported by DirectDraw + */ +#define DDERR_UNSUPPORTEDMASK MAKE_DDHRESULT( 520 ) + +/* + * The specified stream contains invalid data + */ +#define DDERR_INVALIDSTREAM MAKE_DDHRESULT( 521 ) + +/* + * vertical blank is in progress + */ +#define DDERR_VERTICALBLANKINPROGRESS MAKE_DDHRESULT( 537 ) + +/* + * Informs DirectDraw that the previous Blt which is transfering information + * to or from this Surface is incomplete. + */ +#define DDERR_WASSTILLDRAWING MAKE_DDHRESULT( 540 ) + + +/* + * Rectangle provided was not horizontally aligned on reqd. boundary + */ +#define DDERR_XALIGN MAKE_DDHRESULT( 560 ) + +/* + * The GUID passed to DirectDrawCreate is not a valid DirectDraw driver + * identifier. + */ +#define DDERR_INVALIDDIRECTDRAWGUID MAKE_DDHRESULT( 561 ) + +/* + * A DirectDraw object representing this driver has already been created + * for this process. + */ +#define DDERR_DIRECTDRAWALREADYCREATED MAKE_DDHRESULT( 562 ) + +/* + * A hardware only DirectDraw object creation was attempted but the driver + * did not support any hardware. + */ +#define DDERR_NODIRECTDRAWHW MAKE_DDHRESULT( 563 ) + +/* + * this process already has created a primary surface + */ +#define DDERR_PRIMARYSURFACEALREADYEXISTS MAKE_DDHRESULT( 564 ) + +/* + * software emulation not available. + */ +#define DDERR_NOEMULATION MAKE_DDHRESULT( 565 ) + +/* + * region passed to Clipper::GetClipList is too small. + */ +#define DDERR_REGIONTOOSMALL MAKE_DDHRESULT( 566 ) + +/* + * an attempt was made to set a clip list for a clipper objec that + * is already monitoring an hwnd. + */ +#define DDERR_CLIPPERISUSINGHWND MAKE_DDHRESULT( 567 ) + +/* + * No clipper object attached to surface object + */ +#define DDERR_NOCLIPPERATTACHED MAKE_DDHRESULT( 568 ) + +/* + * Clipper notification requires an HWND or + * no HWND has previously been set as the CooperativeLevel HWND. + */ +#define DDERR_NOHWND MAKE_DDHRESULT( 569 ) + +/* + * HWND used by DirectDraw CooperativeLevel has been subclassed, + * this prevents DirectDraw from restoring state. + */ +#define DDERR_HWNDSUBCLASSED MAKE_DDHRESULT( 570 ) + +/* + * The CooperativeLevel HWND has already been set. + * It can not be reset while the process has surfaces or palettes created. + */ +#define DDERR_HWNDALREADYSET MAKE_DDHRESULT( 571 ) + +/* + * No palette object attached to this surface. + */ +#define DDERR_NOPALETTEATTACHED MAKE_DDHRESULT( 572 ) + +/* + * No hardware support for 16 or 256 color palettes. + */ +#define DDERR_NOPALETTEHW MAKE_DDHRESULT( 573 ) + +/* + * If a clipper object is attached to the source surface passed into a + * BltFast call. + */ +#define DDERR_BLTFASTCANTCLIP MAKE_DDHRESULT( 574 ) + +/* + * No blter. + */ +#define DDERR_NOBLTHW MAKE_DDHRESULT( 575 ) + +/* + * No DirectDraw ROP hardware. + */ +#define DDERR_NODDROPSHW MAKE_DDHRESULT( 576 ) + +/* + * returned when GetOverlayPosition is called on a hidden overlay + */ +#define DDERR_OVERLAYNOTVISIBLE MAKE_DDHRESULT( 577 ) + +/* + * returned when GetOverlayPosition is called on a overlay that UpdateOverlay + * has never been called on to establish a destionation. + */ +#define DDERR_NOOVERLAYDEST MAKE_DDHRESULT( 578 ) + +/* + * returned when the position of the overlay on the destionation is no longer + * legal for that destionation. + */ +#define DDERR_INVALIDPOSITION MAKE_DDHRESULT( 579 ) + +/* + * returned when an overlay member is called for a non-overlay surface + */ +#define DDERR_NOTAOVERLAYSURFACE MAKE_DDHRESULT( 580 ) + +/* + * An attempt was made to set the cooperative level when it was already + * set to exclusive. + */ +#define DDERR_EXCLUSIVEMODEALREADYSET MAKE_DDHRESULT( 581 ) + +/* + * An attempt has been made to flip a surface that is not flippable. + */ +#define DDERR_NOTFLIPPABLE MAKE_DDHRESULT( 582 ) + +/* + * Can't duplicate primary & 3D surfaces, or surfaces that are implicitly + * created. + */ +#define DDERR_CANTDUPLICATE MAKE_DDHRESULT( 583 ) + +/* + * Surface was not locked. An attempt to unlock a surface that was not + * locked at all, or by this process, has been attempted. + */ +#define DDERR_NOTLOCKED MAKE_DDHRESULT( 584 ) + +/* + * Windows can not create any more DCs, or a DC was requested for a paltte-indexed + * surface when the surface had no palette AND the display mode was not palette-indexed + * (in this case DirectDraw cannot select a proper palette into the DC) + */ +#define DDERR_CANTCREATEDC MAKE_DDHRESULT( 585 ) + +/* + * No DC was ever created for this surface. + */ +#define DDERR_NODC MAKE_DDHRESULT( 586 ) + +/* + * This surface can not be restored because it was created in a different + * mode. + */ +#define DDERR_WRONGMODE MAKE_DDHRESULT( 587 ) + +/* + * This surface can not be restored because it is an implicitly created + * surface. + */ +#define DDERR_IMPLICITLYCREATED MAKE_DDHRESULT( 588 ) + +/* + * The surface being used is not a palette-based surface + */ +#define DDERR_NOTPALETTIZED MAKE_DDHRESULT( 589 ) + + +/* + * The display is currently in an unsupported mode + */ +#define DDERR_UNSUPPORTEDMODE MAKE_DDHRESULT( 590 ) + +/* + * Operation could not be carried out because there is no mip-map + * texture mapping hardware present or available. + */ +#define DDERR_NOMIPMAPHW MAKE_DDHRESULT( 591 ) + +/* + * The requested action could not be performed because the surface was of + * the wrong type. + */ +#define DDERR_INVALIDSURFACETYPE MAKE_DDHRESULT( 592 ) + + + +/* + * Device does not support optimized surfaces, therefore no video memory optimized surfaces + */ +#define DDERR_NOOPTIMIZEHW MAKE_DDHRESULT( 600 ) + +/* + * Surface is an optimized surface, but has not yet been allocated any memory + */ +#define DDERR_NOTLOADED MAKE_DDHRESULT( 601 ) + +/* + * Attempt was made to create or set a device window without first setting + * the focus window + */ +#define DDERR_NOFOCUSWINDOW MAKE_DDHRESULT( 602 ) + +/* + * A DC has already been returned for this surface. Only one DC can be + * retrieved per surface. + */ +#define DDERR_DCALREADYCREATED MAKE_DDHRESULT( 620 ) + +/* + * An attempt was made to allocate non-local video memory from a device + * that does not support non-local video memory. + */ +#define DDERR_NONONLOCALVIDMEM MAKE_DDHRESULT( 630 ) + +/* + * The attempt to page lock a surface failed. + */ +#define DDERR_CANTPAGELOCK MAKE_DDHRESULT( 640 ) + + +/* + * The attempt to page unlock a surface failed. + */ +#define DDERR_CANTPAGEUNLOCK MAKE_DDHRESULT( 660 ) + +/* + * An attempt was made to page unlock a surface with no outstanding page locks. + */ +#define DDERR_NOTPAGELOCKED MAKE_DDHRESULT( 680 ) + +/* + * There is more data available than the specified buffer size could hold + */ +#define DDERR_MOREDATA MAKE_DDHRESULT( 690 ) + +/* + * The data has expired and is therefore no longer valid. + */ +#define DDERR_EXPIRED MAKE_DDHRESULT( 691 ) + +/* + * The video port is not active + */ +#define DDERR_VIDEONOTACTIVE MAKE_DDHRESULT( 695 ) + +/* + * Surfaces created by one direct draw device cannot be used directly by + * another direct draw device. + */ +#define DDERR_DEVICEDOESNTOWNSURFACE MAKE_DDHRESULT( 699 ) + + +/* + * An attempt was made to invoke an interface member of a DirectDraw object + * created by CoCreateInstance() before it was initialized. + */ +#define DDERR_NOTINITIALIZED CO_E_NOTINITIALIZED + + +/* Alpha bit depth constants */ + + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/lib/win/DirectX/ddraw.lib b/lib/win/DirectX/ddraw.lib new file mode 100644 index 000000000..ed1343491 Binary files /dev/null and b/lib/win/DirectX/ddraw.lib differ diff --git a/lib/win/DirectX/dinput.h b/lib/win/DirectX/dinput.h new file mode 100644 index 000000000..5bf9f5ae4 --- /dev/null +++ b/lib/win/DirectX/dinput.h @@ -0,0 +1,1849 @@ +/**************************************************************************** + * + * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dinput.h + * Content: DirectInput include file + * + ****************************************************************************/ + +#ifndef __DINPUT_INCLUDED__ +#define __DINPUT_INCLUDED__ + +#ifndef DIJ_RINGZERO + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#endif + +#endif /* DIJ_RINGZERO */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DIRECTINPUT_VERSION +#define DIRECTINPUT_VERSION 0x0500 +#endif + +#ifndef DIJ_RINGZERO +/**************************************************************************** + * + * Class IDs + * + ****************************************************************************/ + +DEFINE_GUID(CLSID_DirectInput, 0x25E609E0,0xB259,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(CLSID_DirectInputDevice,0x25E609E1,0xB259,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +/**************************************************************************** + * + * Interfaces + * + ****************************************************************************/ + +DEFINE_GUID(IID_IDirectInputA, 0x89521360,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputW, 0x89521361,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInput2A, 0x5944E662,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInput2W, 0x5944E663,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(IID_IDirectInputDeviceA, 0x5944E680,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputDeviceW, 0x5944E681,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputDevice2A,0x5944E682,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputDevice2W,0x5944E683,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(IID_IDirectInputEffect, 0xE7E1F7C0,0x88D2,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); + +/**************************************************************************** + * + * Predefined object types + * + ****************************************************************************/ + +DEFINE_GUID(GUID_XAxis, 0xA36D02E0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_YAxis, 0xA36D02E1,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_ZAxis, 0xA36D02E2,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RxAxis, 0xA36D02F4,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RyAxis, 0xA36D02F5,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RzAxis, 0xA36D02E3,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Slider, 0xA36D02E4,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(GUID_Button, 0xA36D02F0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Key, 0x55728220,0xD33C,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(GUID_POV, 0xA36D02F2,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(GUID_Unknown, 0xA36D02F3,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +/**************************************************************************** + * + * Predefined product GUIDs + * + ****************************************************************************/ + +DEFINE_GUID(GUID_SysMouse, 0x6F1D2B60,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_SysKeyboard,0x6F1D2B61,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Joystick ,0x6F1D2B70,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +/**************************************************************************** + * + * Predefined force feedback effects + * + ****************************************************************************/ + +DEFINE_GUID(GUID_ConstantForce,0x13541C20,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_RampForce, 0x13541C21,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Square, 0x13541C22,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Sine, 0x13541C23,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Triangle, 0x13541C24,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_SawtoothUp, 0x13541C25,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_SawtoothDown, 0x13541C26,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Spring, 0x13541C27,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Damper, 0x13541C28,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Inertia, 0x13541C29,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Friction, 0x13541C2A,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_CustomForce, 0x13541C2B,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); + + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * Interfaces and Structures... + * + ****************************************************************************/ + +#if(DIRECTINPUT_VERSION >= 0x0500) + +/**************************************************************************** + * + * IDirectInputEffect + * + ****************************************************************************/ + +#define DIEFT_ALL 0x00000000 + +#define DIEFT_CONSTANTFORCE 0x00000001 +#define DIEFT_RAMPFORCE 0x00000002 +#define DIEFT_PERIODIC 0x00000003 +#define DIEFT_CONDITION 0x00000004 +#define DIEFT_CUSTOMFORCE 0x00000005 +#define DIEFT_HARDWARE 0x000000FF + +#define DIEFT_FFATTACK 0x00000200 +#define DIEFT_FFFADE 0x00000400 +#define DIEFT_SATURATION 0x00000800 +#define DIEFT_POSNEGCOEFFICIENTS 0x00001000 +#define DIEFT_POSNEGSATURATION 0x00002000 +#define DIEFT_DEADBAND 0x00004000 + +#define DIEFT_GETTYPE(n) LOBYTE(n) + +#define DI_DEGREES 100 +#define DI_FFNOMINALMAX 10000 +#define DI_SECONDS 1000000 + +typedef struct DICONSTANTFORCE { + LONG lMagnitude; +} DICONSTANTFORCE, *LPDICONSTANTFORCE; +typedef const DICONSTANTFORCE *LPCDICONSTANTFORCE; + +typedef struct DIRAMPFORCE { + LONG lStart; + LONG lEnd; +} DIRAMPFORCE, *LPDIRAMPFORCE; +typedef const DIRAMPFORCE *LPCDIRAMPFORCE; + +typedef struct DIPERIODIC { + DWORD dwMagnitude; + LONG lOffset; + DWORD dwPhase; + DWORD dwPeriod; +} DIPERIODIC, *LPDIPERIODIC; +typedef const DIPERIODIC *LPCDIPERIODIC; + +typedef struct DICONDITION { + LONG lOffset; + LONG lPositiveCoefficient; + LONG lNegativeCoefficient; + DWORD dwPositiveSaturation; + DWORD dwNegativeSaturation; + LONG lDeadBand; +} DICONDITION, *LPDICONDITION; +typedef const DICONDITION *LPCDICONDITION; + +typedef struct DICUSTOMFORCE { + DWORD cChannels; + DWORD dwSamplePeriod; + DWORD cSamples; + LPLONG rglForceData; +} DICUSTOMFORCE, *LPDICUSTOMFORCE; +typedef const DICUSTOMFORCE *LPCDICUSTOMFORCE; + +typedef struct DIENVELOPE { + DWORD dwSize; /* sizeof(DIENVELOPE) */ + DWORD dwAttackLevel; + DWORD dwAttackTime; /* Microseconds */ + DWORD dwFadeLevel; + DWORD dwFadeTime; /* Microseconds */ +} DIENVELOPE, *LPDIENVELOPE; +typedef const DIENVELOPE *LPCDIENVELOPE; + +typedef struct DIEFFECT { + DWORD dwSize; /* sizeof(DIEFFECT) */ + DWORD dwFlags; /* DIEFF_* */ + DWORD dwDuration; /* Microseconds */ + DWORD dwSamplePeriod; /* Microseconds */ + DWORD dwGain; + DWORD dwTriggerButton; /* or DIEB_NOTRIGGER */ + DWORD dwTriggerRepeatInterval; /* Microseconds */ + DWORD cAxes; /* Number of axes */ + LPDWORD rgdwAxes; /* Array of axes */ + LPLONG rglDirection; /* Array of directions */ + LPDIENVELOPE lpEnvelope; /* Optional */ + DWORD cbTypeSpecificParams; /* Size of params */ + LPVOID lpvTypeSpecificParams; /* Pointer to params */ +} DIEFFECT, *LPDIEFFECT; +typedef const DIEFFECT *LPCDIEFFECT; + +#define DIEFF_OBJECTIDS 0x00000001 +#define DIEFF_OBJECTOFFSETS 0x00000002 +#define DIEFF_CARTESIAN 0x00000010 +#define DIEFF_POLAR 0x00000020 +#define DIEFF_SPHERICAL 0x00000040 + +#define DIEP_DURATION 0x00000001 +#define DIEP_SAMPLEPERIOD 0x00000002 +#define DIEP_GAIN 0x00000004 +#define DIEP_TRIGGERBUTTON 0x00000008 +#define DIEP_TRIGGERREPEATINTERVAL 0x00000010 +#define DIEP_AXES 0x00000020 +#define DIEP_DIRECTION 0x00000040 +#define DIEP_ENVELOPE 0x00000080 +#define DIEP_TYPESPECIFICPARAMS 0x00000100 +#define DIEP_ALLPARAMS 0x000001FF +#define DIEP_START 0x20000000 +#define DIEP_NORESTART 0x40000000 +#define DIEP_NODOWNLOAD 0x80000000 +#define DIEB_NOTRIGGER 0xFFFFFFFF + +#define DIES_SOLO 0x00000001 +#define DIES_NODOWNLOAD 0x80000000 + +#define DIEGES_PLAYING 0x00000001 +#define DIEGES_EMULATED 0x00000002 + +typedef struct DIEFFESCAPE { + DWORD dwSize; + DWORD dwCommand; + LPVOID lpvInBuffer; + DWORD cbInBuffer; + LPVOID lpvOutBuffer; + DWORD cbOutBuffer; +} DIEFFESCAPE, *LPDIEFFESCAPE; + +#ifndef DIJ_RINGZERO + +#undef INTERFACE +#define INTERFACE IDirectInputEffect + +DECLARE_INTERFACE_(IDirectInputEffect, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputEffect methods ***/ + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; + STDMETHOD(GetEffectGuid)(THIS_ LPGUID) PURE; + STDMETHOD(GetParameters)(THIS_ LPDIEFFECT,DWORD) PURE; + STDMETHOD(SetParameters)(THIS_ LPCDIEFFECT,DWORD) PURE; + STDMETHOD(Start)(THIS_ DWORD,DWORD) PURE; + STDMETHOD(Stop)(THIS) PURE; + STDMETHOD(GetEffectStatus)(THIS_ LPDWORD) PURE; + STDMETHOD(Download)(THIS) PURE; + STDMETHOD(Unload)(THIS) PURE; + STDMETHOD(Escape)(THIS_ LPDIEFFESCAPE) PURE; +}; + +typedef struct IDirectInputEffect *LPDIRECTINPUTEFFECT; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInputEffect_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInputEffect_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInputEffect_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInputEffect_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c) +#define IDirectInputEffect_GetEffectGuid(p,a) (p)->lpVtbl->GetEffectGuid(p,a) +#define IDirectInputEffect_GetParameters(p,a,b) (p)->lpVtbl->GetParameters(p,a,b) +#define IDirectInputEffect_SetParameters(p,a,b) (p)->lpVtbl->SetParameters(p,a,b) +#define IDirectInputEffect_Start(p,a,b) (p)->lpVtbl->Start(p,a,b) +#define IDirectInputEffect_Stop(p) (p)->lpVtbl->Stop(p) +#define IDirectInputEffect_GetEffectStatus(p,a) (p)->lpVtbl->GetEffectStatus(p,a) +#define IDirectInputEffect_Download(p) (p)->lpVtbl->Download(p) +#define IDirectInputEffect_Unload(p) (p)->lpVtbl->Unload(p) +#define IDirectInputEffect_Escape(p,a) (p)->lpVtbl->Escape(p,a) +#else +#define IDirectInputEffect_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInputEffect_AddRef(p) (p)->AddRef() +#define IDirectInputEffect_Release(p) (p)->Release() +#define IDirectInputEffect_Initialize(p,a,b,c) (p)->Initialize(a,b,c) +#define IDirectInputEffect_GetEffectGuid(p,a) (p)->GetEffectGuid(a) +#define IDirectInputEffect_GetParameters(p,a,b) (p)->GetParameters(a,b) +#define IDirectInputEffect_SetParameters(p,a,b) (p)->SetParameters(a,b) +#define IDirectInputEffect_Start(p,a,b) (p)->Start(a,b) +#define IDirectInputEffect_Stop(p) (p)->Stop() +#define IDirectInputEffect_GetEffectStatus(p,a) (p)->GetEffectStatus(a) +#define IDirectInputEffect_Download(p) (p)->Download() +#define IDirectInputEffect_Unload(p) (p)->Unload() +#define IDirectInputEffect_Escape(p,a) (p)->Escape(a) +#endif + +#endif /* DIJ_RINGZERO */ + +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +/**************************************************************************** + * + * IDirectInputDevice + * + ****************************************************************************/ + +#define DIDEVTYPE_DEVICE 1 +#define DIDEVTYPE_MOUSE 2 +#define DIDEVTYPE_KEYBOARD 3 +#define DIDEVTYPE_JOYSTICK 4 +#define DIDEVTYPE_HID 0x00010000 + +#define DIDEVTYPEMOUSE_UNKNOWN 1 +#define DIDEVTYPEMOUSE_TRADITIONAL 2 +#define DIDEVTYPEMOUSE_FINGERSTICK 3 +#define DIDEVTYPEMOUSE_TOUCHPAD 4 +#define DIDEVTYPEMOUSE_TRACKBALL 5 + +#define DIDEVTYPEKEYBOARD_UNKNOWN 0 +#define DIDEVTYPEKEYBOARD_PCXT 1 +#define DIDEVTYPEKEYBOARD_OLIVETTI 2 +#define DIDEVTYPEKEYBOARD_PCAT 3 +#define DIDEVTYPEKEYBOARD_PCENH 4 +#define DIDEVTYPEKEYBOARD_NOKIA1050 5 +#define DIDEVTYPEKEYBOARD_NOKIA9140 6 +#define DIDEVTYPEKEYBOARD_NEC98 7 +#define DIDEVTYPEKEYBOARD_NEC98LAPTOP 8 +#define DIDEVTYPEKEYBOARD_NEC98106 9 +#define DIDEVTYPEKEYBOARD_JAPAN106 10 +#define DIDEVTYPEKEYBOARD_JAPANAX 11 +#define DIDEVTYPEKEYBOARD_J3100 12 + +#define DIDEVTYPEJOYSTICK_UNKNOWN 1 +#define DIDEVTYPEJOYSTICK_TRADITIONAL 2 +#define DIDEVTYPEJOYSTICK_FLIGHTSTICK 3 +#define DIDEVTYPEJOYSTICK_GAMEPAD 4 +#define DIDEVTYPEJOYSTICK_RUDDER 5 +#define DIDEVTYPEJOYSTICK_WHEEL 6 +#define DIDEVTYPEJOYSTICK_HEADTRACKER 7 + +#define GET_DIDEVICE_TYPE(dwDevType) LOBYTE(dwDevType) +#define GET_DIDEVICE_SUBTYPE(dwDevType) HIBYTE(dwDevType) + +#if(DIRECTINPUT_VERSION >= 0x0500) +/* This structure is defined for DirectX 3.0 compatibility */ + +typedef struct DIDEVCAPS_DX3 { + DWORD dwSize; + DWORD dwFlags; + DWORD dwDevType; + DWORD dwAxes; + DWORD dwButtons; + DWORD dwPOVs; +} DIDEVCAPS_DX3, *LPDIDEVCAPS_DX3; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIDEVCAPS { + DWORD dwSize; + DWORD dwFlags; + DWORD dwDevType; + DWORD dwAxes; + DWORD dwButtons; + DWORD dwPOVs; +#if(DIRECTINPUT_VERSION >= 0x0500) + DWORD dwFFSamplePeriod; + DWORD dwFFMinTimeResolution; + DWORD dwFirmwareRevision; + DWORD dwHardwareRevision; + DWORD dwFFDriverVersion; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVCAPS, *LPDIDEVCAPS; + +#define DIDC_ATTACHED 0x00000001 +#define DIDC_POLLEDDEVICE 0x00000002 +#define DIDC_EMULATED 0x00000004 +#define DIDC_POLLEDDATAFORMAT 0x00000008 +#if(DIRECTINPUT_VERSION >= 0x0500) +#define DIDC_FORCEFEEDBACK 0x00000100 +#define DIDC_FFATTACK 0x00000200 +#define DIDC_FFFADE 0x00000400 +#define DIDC_SATURATION 0x00000800 +#define DIDC_POSNEGCOEFFICIENTS 0x00001000 +#define DIDC_POSNEGSATURATION 0x00002000 +#define DIDC_DEADBAND 0x00004000 +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +#define DIDFT_ALL 0x00000000 + +#define DIDFT_RELAXIS 0x00000001 +#define DIDFT_ABSAXIS 0x00000002 +#define DIDFT_AXIS 0x00000003 + +#define DIDFT_PSHBUTTON 0x00000004 +#define DIDFT_TGLBUTTON 0x00000008 +#define DIDFT_BUTTON 0x0000000C + +#define DIDFT_POV 0x00000010 + +#define DIDFT_COLLECTION 0x00000040 +#define DIDFT_NODATA 0x00000080 + +#define DIDFT_ANYINSTANCE 0x00FFFF00 +#define DIDFT_INSTANCEMASK DIDFT_ANYINSTANCE +#define DIDFT_MAKEINSTANCE(n) ((WORD)(n) << 8) +#define DIDFT_GETTYPE(n) LOBYTE(n) +#define DIDFT_GETINSTANCE(n) LOWORD((n) >> 8) +#define DIDFT_FFACTUATOR 0x01000000 +#define DIDFT_FFEFFECTTRIGGER 0x02000000 + +#define DIDFT_ENUMCOLLECTION(n) ((WORD)(n) << 8) +#define DIDFT_NOCOLLECTION 0x00FFFF00 + + +#ifndef DIJ_RINGZERO + +typedef struct _DIOBJECTDATAFORMAT { + const GUID *pguid; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; +} DIOBJECTDATAFORMAT, *LPDIOBJECTDATAFORMAT; +typedef const DIOBJECTDATAFORMAT *LPCDIOBJECTDATAFORMAT; + +typedef struct _DIDATAFORMAT { + DWORD dwSize; + DWORD dwObjSize; + DWORD dwFlags; + DWORD dwDataSize; + DWORD dwNumObjs; + LPDIOBJECTDATAFORMAT rgodf; +} DIDATAFORMAT, *LPDIDATAFORMAT; +typedef const DIDATAFORMAT *LPCDIDATAFORMAT; + +#define DIDF_ABSAXIS 0x00000001 +#define DIDF_RELAXIS 0x00000002 + +extern const DIDATAFORMAT c_dfDIMouse; +extern const DIDATAFORMAT c_dfDIKeyboard; +extern const DIDATAFORMAT c_dfDIJoystick; +extern const DIDATAFORMAT c_dfDIJoystick2; + +#if(DIRECTINPUT_VERSION >= 0x0500) +/* These structures are defined for DirectX 3.0 compatibility */ + +typedef struct DIDEVICEOBJECTINSTANCE_DX3A { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + CHAR tszName[MAX_PATH]; +} DIDEVICEOBJECTINSTANCE_DX3A, *LPDIDEVICEOBJECTINSTANCE_DX3A; +typedef struct DIDEVICEOBJECTINSTANCE_DX3W { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + WCHAR tszName[MAX_PATH]; +} DIDEVICEOBJECTINSTANCE_DX3W, *LPDIDEVICEOBJECTINSTANCE_DX3W; +#ifdef UNICODE +typedef DIDEVICEOBJECTINSTANCE_DX3W DIDEVICEOBJECTINSTANCE_DX3; +typedef LPDIDEVICEOBJECTINSTANCE_DX3W LPDIDEVICEOBJECTINSTANCE_DX3; +#else +typedef DIDEVICEOBJECTINSTANCE_DX3A DIDEVICEOBJECTINSTANCE_DX3; +typedef LPDIDEVICEOBJECTINSTANCE_DX3A LPDIDEVICEOBJECTINSTANCE_DX3; +#endif // UNICODE +typedef const DIDEVICEOBJECTINSTANCE_DX3A *LPCDIDEVICEOBJECTINSTANCE_DX3A; +typedef const DIDEVICEOBJECTINSTANCE_DX3W *LPCDIDEVICEOBJECTINSTANCE_DX3W; +typedef const DIDEVICEOBJECTINSTANCE_DX3 *LPCDIDEVICEOBJECTINSTANCE_DX3; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIDEVICEOBJECTINSTANCEA { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + CHAR tszName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + DWORD dwFFMaxForce; + DWORD dwFFForceResolution; + WORD wCollectionNumber; + WORD wDesignatorIndex; + WORD wUsagePage; + WORD wUsage; + DWORD dwDimension; + WORD wExponent; + WORD wReserved; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEOBJECTINSTANCEA, *LPDIDEVICEOBJECTINSTANCEA; +typedef struct DIDEVICEOBJECTINSTANCEW { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + WCHAR tszName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + DWORD dwFFMaxForce; + DWORD dwFFForceResolution; + WORD wCollectionNumber; + WORD wDesignatorIndex; + WORD wUsagePage; + WORD wUsage; + DWORD dwDimension; + WORD wExponent; + WORD wReserved; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEOBJECTINSTANCEW, *LPDIDEVICEOBJECTINSTANCEW; +#ifdef UNICODE +typedef DIDEVICEOBJECTINSTANCEW DIDEVICEOBJECTINSTANCE; +typedef LPDIDEVICEOBJECTINSTANCEW LPDIDEVICEOBJECTINSTANCE; +#else +typedef DIDEVICEOBJECTINSTANCEA DIDEVICEOBJECTINSTANCE; +typedef LPDIDEVICEOBJECTINSTANCEA LPDIDEVICEOBJECTINSTANCE; +#endif // UNICODE +typedef const DIDEVICEOBJECTINSTANCEA *LPCDIDEVICEOBJECTINSTANCEA; +typedef const DIDEVICEOBJECTINSTANCEW *LPCDIDEVICEOBJECTINSTANCEW; +typedef const DIDEVICEOBJECTINSTANCE *LPCDIDEVICEOBJECTINSTANCE; + +typedef BOOL (FAR PASCAL * LPDIENUMDEVICEOBJECTSCALLBACKA)(LPCDIDEVICEOBJECTINSTANCEA, LPVOID); +typedef BOOL (FAR PASCAL * LPDIENUMDEVICEOBJECTSCALLBACKW)(LPCDIDEVICEOBJECTINSTANCEW, LPVOID); +#ifdef UNICODE +#define LPDIENUMDEVICEOBJECTSCALLBACK LPDIENUMDEVICEOBJECTSCALLBACKW +#else +#define LPDIENUMDEVICEOBJECTSCALLBACK LPDIENUMDEVICEOBJECTSCALLBACKA +#endif // !UNICODE + +#if(DIRECTINPUT_VERSION >= 0x0500) +#define DIDOI_FFACTUATOR 0x00000001 +#define DIDOI_FFEFFECTTRIGGER 0x00000002 +#define DIDOI_POLLED 0x00008000 +#define DIDOI_ASPECTPOSITION 0x00000100 +#define DIDOI_ASPECTVELOCITY 0x00000200 +#define DIDOI_ASPECTACCEL 0x00000300 +#define DIDOI_ASPECTFORCE 0x00000400 +#define DIDOI_ASPECTMASK 0x00000F00 +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIPROPHEADER { + DWORD dwSize; + DWORD dwHeaderSize; + DWORD dwObj; + DWORD dwHow; +} DIPROPHEADER, *LPDIPROPHEADER; +typedef const DIPROPHEADER *LPCDIPROPHEADER; + +#define DIPH_DEVICE 0 +#define DIPH_BYOFFSET 1 +#define DIPH_BYID 2 + +typedef struct DIPROPDWORD { + DIPROPHEADER diph; + DWORD dwData; +} DIPROPDWORD, *LPDIPROPDWORD; +typedef const DIPROPDWORD *LPCDIPROPDWORD; + +typedef struct DIPROPRANGE { + DIPROPHEADER diph; + LONG lMin; + LONG lMax; +} DIPROPRANGE, *LPDIPROPRANGE; +typedef const DIPROPRANGE *LPCDIPROPRANGE; + +#define DIPROPRANGE_NOMIN ((LONG)0x80000000) +#define DIPROPRANGE_NOMAX ((LONG)0x7FFFFFFF) + +#ifdef __cplusplus +#define MAKEDIPROP(prop) (*(const GUID *)(prop)) +#else +#define MAKEDIPROP(prop) ((REFGUID)(prop)) +#endif + +#define DIPROP_BUFFERSIZE MAKEDIPROP(1) + +#define DIPROP_AXISMODE MAKEDIPROP(2) + +#define DIPROPAXISMODE_ABS 0 +#define DIPROPAXISMODE_REL 1 + +#define DIPROP_GRANULARITY MAKEDIPROP(3) + +#define DIPROP_RANGE MAKEDIPROP(4) + +#define DIPROP_DEADZONE MAKEDIPROP(5) + +#define DIPROP_SATURATION MAKEDIPROP(6) + +#define DIPROP_FFGAIN MAKEDIPROP(7) + +#define DIPROP_FFLOAD MAKEDIPROP(8) + +#define DIPROP_AUTOCENTER MAKEDIPROP(9) + +#define DIPROPAUTOCENTER_OFF 0 +#define DIPROPAUTOCENTER_ON 1 + +#define DIPROP_CALIBRATIONMODE MAKEDIPROP(10) + +#define DIPROPCALIBRATIONMODE_COOKED 0 +#define DIPROPCALIBRATIONMODE_RAW 1 + +typedef struct DIDEVICEOBJECTDATA { + DWORD dwOfs; + DWORD dwData; + DWORD dwTimeStamp; + DWORD dwSequence; +} DIDEVICEOBJECTDATA, *LPDIDEVICEOBJECTDATA; +typedef const DIDEVICEOBJECTDATA *LPCDIDEVICEOBJECTDATA; + +#define DIGDD_PEEK 0x00000001 + +#define DISEQUENCE_COMPARE(dwSequence1, cmp, dwSequence2) \ + ((int)((dwSequence1) - (dwSequence2)) cmp 0) +#define DISCL_EXCLUSIVE 0x00000001 +#define DISCL_NONEXCLUSIVE 0x00000002 +#define DISCL_FOREGROUND 0x00000004 +#define DISCL_BACKGROUND 0x00000008 + +#if(DIRECTINPUT_VERSION >= 0x0500) +/* These structures are defined for DirectX 3.0 compatibility */ + +typedef struct DIDEVICEINSTANCE_DX3A { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + CHAR tszInstanceName[MAX_PATH]; + CHAR tszProductName[MAX_PATH]; +} DIDEVICEINSTANCE_DX3A, *LPDIDEVICEINSTANCE_DX3A; +typedef struct DIDEVICEINSTANCE_DX3W { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + WCHAR tszInstanceName[MAX_PATH]; + WCHAR tszProductName[MAX_PATH]; +} DIDEVICEINSTANCE_DX3W, *LPDIDEVICEINSTANCE_DX3W; +#ifdef UNICODE +typedef DIDEVICEINSTANCE_DX3W DIDEVICEINSTANCE_DX3; +typedef LPDIDEVICEINSTANCE_DX3W LPDIDEVICEINSTANCE_DX3; +#else +typedef DIDEVICEINSTANCE_DX3A DIDEVICEINSTANCE_DX3; +typedef LPDIDEVICEINSTANCE_DX3A LPDIDEVICEINSTANCE_DX3; +#endif // UNICODE +typedef const DIDEVICEINSTANCE_DX3A *LPCDIDEVICEINSTANCE_DX3A; +typedef const DIDEVICEINSTANCE_DX3W *LPCDIDEVICEINSTANCE_DX3W; +typedef const DIDEVICEINSTANCE_DX3 *LPCDIDEVICEINSTANCE_DX3; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIDEVICEINSTANCEA { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + CHAR tszInstanceName[MAX_PATH]; + CHAR tszProductName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + GUID guidFFDriver; + WORD wUsagePage; + WORD wUsage; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEINSTANCEA, *LPDIDEVICEINSTANCEA; +typedef struct DIDEVICEINSTANCEW { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + WCHAR tszInstanceName[MAX_PATH]; + WCHAR tszProductName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + GUID guidFFDriver; + WORD wUsagePage; + WORD wUsage; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEINSTANCEW, *LPDIDEVICEINSTANCEW; +#ifdef UNICODE +typedef DIDEVICEINSTANCEW DIDEVICEINSTANCE; +typedef LPDIDEVICEINSTANCEW LPDIDEVICEINSTANCE; +#else +typedef DIDEVICEINSTANCEA DIDEVICEINSTANCE; +typedef LPDIDEVICEINSTANCEA LPDIDEVICEINSTANCE; +#endif // UNICODE +typedef const DIDEVICEINSTANCEA *LPCDIDEVICEINSTANCEA; +typedef const DIDEVICEINSTANCEW *LPCDIDEVICEINSTANCEW; +typedef const DIDEVICEINSTANCE *LPCDIDEVICEINSTANCE; + +#undef INTERFACE +#define INTERFACE IDirectInputDeviceW + +DECLARE_INTERFACE_(IDirectInputDeviceW, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceW methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEW,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEW) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; +}; + +typedef struct IDirectInputDeviceW *LPDIRECTINPUTDEVICEW; + +#undef INTERFACE +#define INTERFACE IDirectInputDeviceA + +DECLARE_INTERFACE_(IDirectInputDeviceA, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceA methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEA,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEA) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; +}; + +typedef struct IDirectInputDeviceA *LPDIRECTINPUTDEVICEA; + +#ifdef UNICODE +#define IID_IDirectInputDevice IID_IDirectInputDeviceW +#define IDirectInputDevice IDirectInputDeviceW +#define IDirectInputDeviceVtbl IDirectInputDeviceWVtbl +#else +#define IID_IDirectInputDevice IID_IDirectInputDeviceA +#define IDirectInputDevice IDirectInputDeviceA +#define IDirectInputDeviceVtbl IDirectInputDeviceAVtbl +#endif +typedef struct IDirectInputDevice *LPDIRECTINPUTDEVICE; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInputDevice_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInputDevice_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInputDevice_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInputDevice_GetCapabilities(p,a) (p)->lpVtbl->GetCapabilities(p,a) +#define IDirectInputDevice_EnumObjects(p,a,b,c) (p)->lpVtbl->EnumObjects(p,a,b,c) +#define IDirectInputDevice_GetProperty(p,a,b) (p)->lpVtbl->GetProperty(p,a,b) +#define IDirectInputDevice_SetProperty(p,a,b) (p)->lpVtbl->SetProperty(p,a,b) +#define IDirectInputDevice_Acquire(p) (p)->lpVtbl->Acquire(p) +#define IDirectInputDevice_Unacquire(p) (p)->lpVtbl->Unacquire(p) +#define IDirectInputDevice_GetDeviceState(p,a,b) (p)->lpVtbl->GetDeviceState(p,a,b) +#define IDirectInputDevice_GetDeviceData(p,a,b,c,d) (p)->lpVtbl->GetDeviceData(p,a,b,c,d) +#define IDirectInputDevice_SetDataFormat(p,a) (p)->lpVtbl->SetDataFormat(p,a) +#define IDirectInputDevice_SetEventNotification(p,a) (p)->lpVtbl->SetEventNotification(p,a) +#define IDirectInputDevice_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IDirectInputDevice_GetObjectInfo(p,a,b,c) (p)->lpVtbl->GetObjectInfo(p,a,b,c) +#define IDirectInputDevice_GetDeviceInfo(p,a) (p)->lpVtbl->GetDeviceInfo(p,a) +#define IDirectInputDevice_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInputDevice_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c) +#else +#define IDirectInputDevice_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInputDevice_AddRef(p) (p)->AddRef() +#define IDirectInputDevice_Release(p) (p)->Release() +#define IDirectInputDevice_GetCapabilities(p,a) (p)->GetCapabilities(a) +#define IDirectInputDevice_EnumObjects(p,a,b,c) (p)->EnumObjects(a,b,c) +#define IDirectInputDevice_GetProperty(p,a,b) (p)->GetProperty(a,b) +#define IDirectInputDevice_SetProperty(p,a,b) (p)->SetProperty(a,b) +#define IDirectInputDevice_Acquire(p) (p)->Acquire() +#define IDirectInputDevice_Unacquire(p) (p)->Unacquire() +#define IDirectInputDevice_GetDeviceState(p,a,b) (p)->GetDeviceState(a,b) +#define IDirectInputDevice_GetDeviceData(p,a,b,c,d) (p)->GetDeviceData(a,b,c,d) +#define IDirectInputDevice_SetDataFormat(p,a) (p)->SetDataFormat(a) +#define IDirectInputDevice_SetEventNotification(p,a) (p)->SetEventNotification(a) +#define IDirectInputDevice_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IDirectInputDevice_GetObjectInfo(p,a,b,c) (p)->GetObjectInfo(a,b,c) +#define IDirectInputDevice_GetDeviceInfo(p,a) (p)->GetDeviceInfo(a) +#define IDirectInputDevice_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInputDevice_Initialize(p,a,b,c) (p)->Initialize(a,b,c) +#endif + +#endif /* DIJ_RINGZERO */ + + +#if(DIRECTINPUT_VERSION >= 0x0500) + +#define DISFFC_RESET 0x00000001 +#define DISFFC_STOPALL 0x00000002 +#define DISFFC_PAUSE 0x00000004 +#define DISFFC_CONTINUE 0x00000008 +#define DISFFC_SETACTUATORSON 0x00000010 +#define DISFFC_SETACTUATORSOFF 0x00000020 + +#define DIGFFS_EMPTY 0x00000001 +#define DIGFFS_STOPPED 0x00000002 +#define DIGFFS_PAUSED 0x00000004 +#define DIGFFS_ACTUATORSON 0x00000010 +#define DIGFFS_ACTUATORSOFF 0x00000020 +#define DIGFFS_POWERON 0x00000040 +#define DIGFFS_POWEROFF 0x00000080 +#define DIGFFS_SAFETYSWITCHON 0x00000100 +#define DIGFFS_SAFETYSWITCHOFF 0x00000200 +#define DIGFFS_USERFFSWITCHON 0x00000400 +#define DIGFFS_USERFFSWITCHOFF 0x00000800 +#define DIGFFS_DEVICELOST 0x80000000 + +#ifndef DIJ_RINGZERO + +typedef struct DIEFFECTINFOA { + DWORD dwSize; + GUID guid; + DWORD dwEffType; + DWORD dwStaticParams; + DWORD dwDynamicParams; + CHAR tszName[MAX_PATH]; +} DIEFFECTINFOA, *LPDIEFFECTINFOA; +typedef struct DIEFFECTINFOW { + DWORD dwSize; + GUID guid; + DWORD dwEffType; + DWORD dwStaticParams; + DWORD dwDynamicParams; + WCHAR tszName[MAX_PATH]; +} DIEFFECTINFOW, *LPDIEFFECTINFOW; +#ifdef UNICODE +typedef DIEFFECTINFOW DIEFFECTINFO; +typedef LPDIEFFECTINFOW LPDIEFFECTINFO; +#else +typedef DIEFFECTINFOA DIEFFECTINFO; +typedef LPDIEFFECTINFOA LPDIEFFECTINFO; +#endif // UNICODE +typedef const DIEFFECTINFOA *LPCDIEFFECTINFOA; +typedef const DIEFFECTINFOW *LPCDIEFFECTINFOW; +typedef const DIEFFECTINFO *LPCDIEFFECTINFO; + +typedef BOOL (FAR PASCAL * LPDIENUMEFFECTSCALLBACKA)(LPCDIEFFECTINFOA, LPVOID); +typedef BOOL (FAR PASCAL * LPDIENUMEFFECTSCALLBACKW)(LPCDIEFFECTINFOW, LPVOID); +#ifdef UNICODE +#define LPDIENUMEFFECTSCALLBACK LPDIENUMEFFECTSCALLBACKW +#else +#define LPDIENUMEFFECTSCALLBACK LPDIENUMEFFECTSCALLBACKA +#endif // !UNICODE +typedef BOOL (FAR PASCAL * LPDIENUMCREATEDEFFECTOBJECTSCALLBACK)(LPDIRECTINPUTEFFECT, LPVOID); + +#undef INTERFACE +#define INTERFACE IDirectInputDevice2W + +DECLARE_INTERFACE_(IDirectInputDevice2W, IDirectInputDeviceW) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceW methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEW,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEW) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; + + /*** IDirectInputDevice2W methods ***/ + STDMETHOD(CreateEffect)(THIS_ REFGUID,LPCDIEFFECT,LPDIRECTINPUTEFFECT *,LPUNKNOWN) PURE; + STDMETHOD(EnumEffects)(THIS_ LPDIENUMEFFECTSCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetEffectInfo)(THIS_ LPDIEFFECTINFOW,REFGUID) PURE; + STDMETHOD(GetForceFeedbackState)(THIS_ LPDWORD) PURE; + STDMETHOD(SendForceFeedbackCommand)(THIS_ DWORD) PURE; + STDMETHOD(EnumCreatedEffectObjects)(THIS_ LPDIENUMCREATEDEFFECTOBJECTSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(Escape)(THIS_ LPDIEFFESCAPE) PURE; + STDMETHOD(Poll)(THIS) PURE; + STDMETHOD(SendDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; +}; + +typedef struct IDirectInputDevice2W *LPDIRECTINPUTDEVICE2W; + +#undef INTERFACE +#define INTERFACE IDirectInputDevice2A + +DECLARE_INTERFACE_(IDirectInputDevice2A, IDirectInputDeviceA) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceA methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEA,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEA) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; + + /*** IDirectInputDevice2A methods ***/ + STDMETHOD(CreateEffect)(THIS_ REFGUID,LPCDIEFFECT,LPDIRECTINPUTEFFECT *,LPUNKNOWN) PURE; + STDMETHOD(EnumEffects)(THIS_ LPDIENUMEFFECTSCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetEffectInfo)(THIS_ LPDIEFFECTINFOA,REFGUID) PURE; + STDMETHOD(GetForceFeedbackState)(THIS_ LPDWORD) PURE; + STDMETHOD(SendForceFeedbackCommand)(THIS_ DWORD) PURE; + STDMETHOD(EnumCreatedEffectObjects)(THIS_ LPDIENUMCREATEDEFFECTOBJECTSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(Escape)(THIS_ LPDIEFFESCAPE) PURE; + STDMETHOD(Poll)(THIS) PURE; + STDMETHOD(SendDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; +}; + +typedef struct IDirectInputDevice2A *LPDIRECTINPUTDEVICE2A; + +#ifdef UNICODE +#define IID_IDirectInputDevice2 IID_IDirectInputDevice2W +#define IDirectInputDevice2 IDirectInputDevice2W +#define IDirectInputDevice2Vtbl IDirectInputDevice2WVtbl +#else +#define IID_IDirectInputDevice2 IID_IDirectInputDevice2A +#define IDirectInputDevice2 IDirectInputDevice2A +#define IDirectInputDevice2Vtbl IDirectInputDevice2AVtbl +#endif +typedef struct IDirectInputDevice2 *LPDIRECTINPUTDEVICE2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInputDevice2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInputDevice2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInputDevice2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInputDevice2_GetCapabilities(p,a) (p)->lpVtbl->GetCapabilities(p,a) +#define IDirectInputDevice2_EnumObjects(p,a,b,c) (p)->lpVtbl->EnumObjects(p,a,b,c) +#define IDirectInputDevice2_GetProperty(p,a,b) (p)->lpVtbl->GetProperty(p,a,b) +#define IDirectInputDevice2_SetProperty(p,a,b) (p)->lpVtbl->SetProperty(p,a,b) +#define IDirectInputDevice2_Acquire(p) (p)->lpVtbl->Acquire(p) +#define IDirectInputDevice2_Unacquire(p) (p)->lpVtbl->Unacquire(p) +#define IDirectInputDevice2_GetDeviceState(p,a,b) (p)->lpVtbl->GetDeviceState(p,a,b) +#define IDirectInputDevice2_GetDeviceData(p,a,b,c,d) (p)->lpVtbl->GetDeviceData(p,a,b,c,d) +#define IDirectInputDevice2_SetDataFormat(p,a) (p)->lpVtbl->SetDataFormat(p,a) +#define IDirectInputDevice2_SetEventNotification(p,a) (p)->lpVtbl->SetEventNotification(p,a) +#define IDirectInputDevice2_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IDirectInputDevice2_GetObjectInfo(p,a,b,c) (p)->lpVtbl->GetObjectInfo(p,a,b,c) +#define IDirectInputDevice2_GetDeviceInfo(p,a) (p)->lpVtbl->GetDeviceInfo(p,a) +#define IDirectInputDevice2_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInputDevice2_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c) +#define IDirectInputDevice2_CreateEffect(p,a,b,c,d) (p)->lpVtbl->CreateEffect(p,a,b,c,d) +#define IDirectInputDevice2_EnumEffects(p,a,b,c) (p)->lpVtbl->EnumEffects(p,a,b,c) +#define IDirectInputDevice2_GetEffectInfo(p,a,b) (p)->lpVtbl->GetEffectInfo(p,a,b) +#define IDirectInputDevice2_GetForceFeedbackState(p,a) (p)->lpVtbl->GetForceFeedbackState(p,a) +#define IDirectInputDevice2_SendForceFeedbackCommand(p,a) (p)->lpVtbl->SendForceFeedbackCommand(p,a) +#define IDirectInputDevice2_EnumCreatedEffectObjects(p,a,b,c) (p)->lpVtbl->EnumCreatedEffectObjects(p,a,b,c) +#define IDirectInputDevice2_Escape(p,a) (p)->lpVtbl->Escape(p,a) +#define IDirectInputDevice2_Poll(p) (p)->lpVtbl->Poll(p) +#define IDirectInputDevice2_SendDeviceData(p,a,b,c,d) (p)->lpVtbl->SendDeviceData(p,a,b,c,d) +#else +#define IDirectInputDevice2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInputDevice2_AddRef(p) (p)->AddRef() +#define IDirectInputDevice2_Release(p) (p)->Release() +#define IDirectInputDevice2_GetCapabilities(p,a) (p)->GetCapabilities(a) +#define IDirectInputDevice2_EnumObjects(p,a,b,c) (p)->EnumObjects(a,b,c) +#define IDirectInputDevice2_GetProperty(p,a,b) (p)->GetProperty(a,b) +#define IDirectInputDevice2_SetProperty(p,a,b) (p)->SetProperty(a,b) +#define IDirectInputDevice2_Acquire(p) (p)->Acquire() +#define IDirectInputDevice2_Unacquire(p) (p)->Unacquire() +#define IDirectInputDevice2_GetDeviceState(p,a,b) (p)->GetDeviceState(a,b) +#define IDirectInputDevice2_GetDeviceData(p,a,b,c,d) (p)->GetDeviceData(a,b,c,d) +#define IDirectInputDevice2_SetDataFormat(p,a) (p)->SetDataFormat(a) +#define IDirectInputDevice2_SetEventNotification(p,a) (p)->SetEventNotification(a) +#define IDirectInputDevice2_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IDirectInputDevice2_GetObjectInfo(p,a,b,c) (p)->GetObjectInfo(a,b,c) +#define IDirectInputDevice2_GetDeviceInfo(p,a) (p)->GetDeviceInfo(a) +#define IDirectInputDevice2_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInputDevice2_Initialize(p,a,b,c) (p)->Initialize(a,b,c) +#define IDirectInputDevice2_CreateEffect(p,a,b,c,d) (p)->CreateEffect(a,b,c,d) +#define IDirectInputDevice2_EnumEffects(p,a,b,c) (p)->EnumEffects(a,b,c) +#define IDirectInputDevice2_GetEffectInfo(p,a,b) (p)->GetEffectInfo(a,b) +#define IDirectInputDevice2_GetForceFeedbackState(p,a) (p)->GetForceFeedbackState(a) +#define IDirectInputDevice2_SendForceFeedbackCommand(p,a) (p)->SendForceFeedbackCommand(a) +#define IDirectInputDevice2_EnumCreatedEffectObjects(p,a,b,c) (p)->EnumCreatedEffectObjects(a,b,c) +#define IDirectInputDevice2_Escape(p,a) (p)->Escape(a) +#define IDirectInputDevice2_Poll(p) (p)->Poll() +#define IDirectInputDevice2_SendDeviceData(p,a,b,c,d) (p)->SendDeviceData(a,b,c,d) +#endif + +#endif /* DIJ_RINGZERO */ + +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +/**************************************************************************** + * + * Mouse + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +typedef struct _DIMOUSESTATE { + LONG lX; + LONG lY; + LONG lZ; + BYTE rgbButtons[4]; +} DIMOUSESTATE, *LPDIMOUSESTATE; + +#define DIMOFS_X FIELD_OFFSET(DIMOUSESTATE, lX) +#define DIMOFS_Y FIELD_OFFSET(DIMOUSESTATE, lY) +#define DIMOFS_Z FIELD_OFFSET(DIMOUSESTATE, lZ) +#define DIMOFS_BUTTON0 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 0) +#define DIMOFS_BUTTON1 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 1) +#define DIMOFS_BUTTON2 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 2) +#define DIMOFS_BUTTON3 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 3) + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * Keyboard + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +/**************************************************************************** + * + * DirectInput keyboard scan codes + * + ****************************************************************************/ + +#define DIK_ESCAPE 0x01 +#define DIK_1 0x02 +#define DIK_2 0x03 +#define DIK_3 0x04 +#define DIK_4 0x05 +#define DIK_5 0x06 +#define DIK_6 0x07 +#define DIK_7 0x08 +#define DIK_8 0x09 +#define DIK_9 0x0A +#define DIK_0 0x0B +#define DIK_MINUS 0x0C /* - on main keyboard */ +#define DIK_EQUALS 0x0D +#define DIK_BACK 0x0E /* backspace */ +#define DIK_TAB 0x0F +#define DIK_Q 0x10 +#define DIK_W 0x11 +#define DIK_E 0x12 +#define DIK_R 0x13 +#define DIK_T 0x14 +#define DIK_Y 0x15 +#define DIK_U 0x16 +#define DIK_I 0x17 +#define DIK_O 0x18 +#define DIK_P 0x19 +#define DIK_LBRACKET 0x1A +#define DIK_RBRACKET 0x1B +#define DIK_RETURN 0x1C /* Enter on main keyboard */ +#define DIK_LCONTROL 0x1D +#define DIK_A 0x1E +#define DIK_S 0x1F +#define DIK_D 0x20 +#define DIK_F 0x21 +#define DIK_G 0x22 +#define DIK_H 0x23 +#define DIK_J 0x24 +#define DIK_K 0x25 +#define DIK_L 0x26 +#define DIK_SEMICOLON 0x27 +#define DIK_APOSTROPHE 0x28 +#define DIK_GRAVE 0x29 /* accent grave */ +#define DIK_LSHIFT 0x2A +#define DIK_BACKSLASH 0x2B +#define DIK_Z 0x2C +#define DIK_X 0x2D +#define DIK_C 0x2E +#define DIK_V 0x2F +#define DIK_B 0x30 +#define DIK_N 0x31 +#define DIK_M 0x32 +#define DIK_COMMA 0x33 +#define DIK_PERIOD 0x34 /* . on main keyboard */ +#define DIK_SLASH 0x35 /* / on main keyboard */ +#define DIK_RSHIFT 0x36 +#define DIK_MULTIPLY 0x37 /* * on numeric keypad */ +#define DIK_LMENU 0x38 /* left Alt */ +#define DIK_SPACE 0x39 +#define DIK_CAPITAL 0x3A +#define DIK_F1 0x3B +#define DIK_F2 0x3C +#define DIK_F3 0x3D +#define DIK_F4 0x3E +#define DIK_F5 0x3F +#define DIK_F6 0x40 +#define DIK_F7 0x41 +#define DIK_F8 0x42 +#define DIK_F9 0x43 +#define DIK_F10 0x44 +#define DIK_NUMLOCK 0x45 +#define DIK_SCROLL 0x46 /* Scroll Lock */ +#define DIK_NUMPAD7 0x47 +#define DIK_NUMPAD8 0x48 +#define DIK_NUMPAD9 0x49 +#define DIK_SUBTRACT 0x4A /* - on numeric keypad */ +#define DIK_NUMPAD4 0x4B +#define DIK_NUMPAD5 0x4C +#define DIK_NUMPAD6 0x4D +#define DIK_ADD 0x4E /* + on numeric keypad */ +#define DIK_NUMPAD1 0x4F +#define DIK_NUMPAD2 0x50 +#define DIK_NUMPAD3 0x51 +#define DIK_NUMPAD0 0x52 +#define DIK_DECIMAL 0x53 /* . on numeric keypad */ +#define DIK_F11 0x57 +#define DIK_F12 0x58 + +#define DIK_F13 0x64 /* (NEC PC98) */ +#define DIK_F14 0x65 /* (NEC PC98) */ +#define DIK_F15 0x66 /* (NEC PC98) */ + +#define DIK_KANA 0x70 /* (Japanese keyboard) */ +#define DIK_CONVERT 0x79 /* (Japanese keyboard) */ +#define DIK_NOCONVERT 0x7B /* (Japanese keyboard) */ +#define DIK_YEN 0x7D /* (Japanese keyboard) */ +#define DIK_NUMPADEQUALS 0x8D /* = on numeric keypad (NEC PC98) */ +#define DIK_CIRCUMFLEX 0x90 /* (Japanese keyboard) */ +#define DIK_AT 0x91 /* (NEC PC98) */ +#define DIK_COLON 0x92 /* (NEC PC98) */ +#define DIK_UNDERLINE 0x93 /* (NEC PC98) */ +#define DIK_KANJI 0x94 /* (Japanese keyboard) */ +#define DIK_STOP 0x95 /* (NEC PC98) */ +#define DIK_AX 0x96 /* (Japan AX) */ +#define DIK_UNLABELED 0x97 /* (J3100) */ +#define DIK_NUMPADENTER 0x9C /* Enter on numeric keypad */ +#define DIK_RCONTROL 0x9D +#define DIK_NUMPADCOMMA 0xB3 /* , on numeric keypad (NEC PC98) */ +#define DIK_DIVIDE 0xB5 /* / on numeric keypad */ +#define DIK_SYSRQ 0xB7 +#define DIK_RMENU 0xB8 /* right Alt */ +#define DIK_HOME 0xC7 /* Home on arrow keypad */ +#define DIK_UP 0xC8 /* UpArrow on arrow keypad */ +#define DIK_PRIOR 0xC9 /* PgUp on arrow keypad */ +#define DIK_LEFT 0xCB /* LeftArrow on arrow keypad */ +#define DIK_RIGHT 0xCD /* RightArrow on arrow keypad */ +#define DIK_END 0xCF /* End on arrow keypad */ +#define DIK_DOWN 0xD0 /* DownArrow on arrow keypad */ +#define DIK_NEXT 0xD1 /* PgDn on arrow keypad */ +#define DIK_INSERT 0xD2 /* Insert on arrow keypad */ +#define DIK_DELETE 0xD3 /* Delete on arrow keypad */ +#define DIK_LWIN 0xDB /* Left Windows key */ +#define DIK_RWIN 0xDC /* Right Windows key */ +#define DIK_APPS 0xDD /* AppMenu key */ + +/* + * Alternate names for keys, to facilitate transition from DOS. + */ +#define DIK_BACKSPACE DIK_BACK /* backspace */ +#define DIK_NUMPADSTAR DIK_MULTIPLY /* * on numeric keypad */ +#define DIK_LALT DIK_LMENU /* left Alt */ +#define DIK_CAPSLOCK DIK_CAPITAL /* CapsLock */ +#define DIK_NUMPADMINUS DIK_SUBTRACT /* - on numeric keypad */ +#define DIK_NUMPADPLUS DIK_ADD /* + on numeric keypad */ +#define DIK_NUMPADPERIOD DIK_DECIMAL /* . on numeric keypad */ +#define DIK_NUMPADSLASH DIK_DIVIDE /* / on numeric keypad */ +#define DIK_RALT DIK_RMENU /* right Alt */ +#define DIK_UPARROW DIK_UP /* UpArrow on arrow keypad */ +#define DIK_PGUP DIK_PRIOR /* PgUp on arrow keypad */ +#define DIK_LEFTARROW DIK_LEFT /* LeftArrow on arrow keypad */ +#define DIK_RIGHTARROW DIK_RIGHT /* RightArrow on arrow keypad */ +#define DIK_DOWNARROW DIK_DOWN /* DownArrow on arrow keypad */ +#define DIK_PGDN DIK_NEXT /* PgDn on arrow keypad */ + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * Joystick + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +typedef struct DIJOYSTATE { + LONG lX; /* x-axis position */ + LONG lY; /* y-axis position */ + LONG lZ; /* z-axis position */ + LONG lRx; /* x-axis rotation */ + LONG lRy; /* y-axis rotation */ + LONG lRz; /* z-axis rotation */ + LONG rglSlider[2]; /* extra axes positions */ + DWORD rgdwPOV[4]; /* POV directions */ + BYTE rgbButtons[32]; /* 32 buttons */ +} DIJOYSTATE, *LPDIJOYSTATE; + +typedef struct DIJOYSTATE2 { + LONG lX; /* x-axis position */ + LONG lY; /* y-axis position */ + LONG lZ; /* z-axis position */ + LONG lRx; /* x-axis rotation */ + LONG lRy; /* y-axis rotation */ + LONG lRz; /* z-axis rotation */ + LONG rglSlider[2]; /* extra axes positions */ + DWORD rgdwPOV[4]; /* POV directions */ + BYTE rgbButtons[128]; /* 128 buttons */ + LONG lVX; /* x-axis velocity */ + LONG lVY; /* y-axis velocity */ + LONG lVZ; /* z-axis velocity */ + LONG lVRx; /* x-axis angular velocity */ + LONG lVRy; /* y-axis angular velocity */ + LONG lVRz; /* z-axis angular velocity */ + LONG rglVSlider[2]; /* extra axes velocities */ + LONG lAX; /* x-axis acceleration */ + LONG lAY; /* y-axis acceleration */ + LONG lAZ; /* z-axis acceleration */ + LONG lARx; /* x-axis angular acceleration */ + LONG lARy; /* y-axis angular acceleration */ + LONG lARz; /* z-axis angular acceleration */ + LONG rglASlider[2]; /* extra axes accelerations */ + LONG lFX; /* x-axis force */ + LONG lFY; /* y-axis force */ + LONG lFZ; /* z-axis force */ + LONG lFRx; /* x-axis torque */ + LONG lFRy; /* y-axis torque */ + LONG lFRz; /* z-axis torque */ + LONG rglFSlider[2]; /* extra axes forces */ +} DIJOYSTATE2, *LPDIJOYSTATE2; + +#define DIJOFS_X FIELD_OFFSET(DIJOYSTATE, lX) +#define DIJOFS_Y FIELD_OFFSET(DIJOYSTATE, lY) +#define DIJOFS_Z FIELD_OFFSET(DIJOYSTATE, lZ) +#define DIJOFS_RX FIELD_OFFSET(DIJOYSTATE, lRx) +#define DIJOFS_RY FIELD_OFFSET(DIJOYSTATE, lRy) +#define DIJOFS_RZ FIELD_OFFSET(DIJOYSTATE, lRz) +#define DIJOFS_SLIDER(n) (FIELD_OFFSET(DIJOYSTATE, rglSlider) + \ + (n) * sizeof(LONG)) +#define DIJOFS_POV(n) (FIELD_OFFSET(DIJOYSTATE, rgdwPOV) + \ + (n) * sizeof(DWORD)) +#define DIJOFS_BUTTON(n) (FIELD_OFFSET(DIJOYSTATE, rgbButtons) + (n)) +#define DIJOFS_BUTTON0 DIJOFS_BUTTON(0) +#define DIJOFS_BUTTON1 DIJOFS_BUTTON(1) +#define DIJOFS_BUTTON2 DIJOFS_BUTTON(2) +#define DIJOFS_BUTTON3 DIJOFS_BUTTON(3) +#define DIJOFS_BUTTON4 DIJOFS_BUTTON(4) +#define DIJOFS_BUTTON5 DIJOFS_BUTTON(5) +#define DIJOFS_BUTTON6 DIJOFS_BUTTON(6) +#define DIJOFS_BUTTON7 DIJOFS_BUTTON(7) +#define DIJOFS_BUTTON8 DIJOFS_BUTTON(8) +#define DIJOFS_BUTTON9 DIJOFS_BUTTON(9) +#define DIJOFS_BUTTON10 DIJOFS_BUTTON(10) +#define DIJOFS_BUTTON11 DIJOFS_BUTTON(11) +#define DIJOFS_BUTTON12 DIJOFS_BUTTON(12) +#define DIJOFS_BUTTON13 DIJOFS_BUTTON(13) +#define DIJOFS_BUTTON14 DIJOFS_BUTTON(14) +#define DIJOFS_BUTTON15 DIJOFS_BUTTON(15) +#define DIJOFS_BUTTON16 DIJOFS_BUTTON(16) +#define DIJOFS_BUTTON17 DIJOFS_BUTTON(17) +#define DIJOFS_BUTTON18 DIJOFS_BUTTON(18) +#define DIJOFS_BUTTON19 DIJOFS_BUTTON(19) +#define DIJOFS_BUTTON20 DIJOFS_BUTTON(20) +#define DIJOFS_BUTTON21 DIJOFS_BUTTON(21) +#define DIJOFS_BUTTON22 DIJOFS_BUTTON(22) +#define DIJOFS_BUTTON23 DIJOFS_BUTTON(23) +#define DIJOFS_BUTTON24 DIJOFS_BUTTON(24) +#define DIJOFS_BUTTON25 DIJOFS_BUTTON(25) +#define DIJOFS_BUTTON26 DIJOFS_BUTTON(26) +#define DIJOFS_BUTTON27 DIJOFS_BUTTON(27) +#define DIJOFS_BUTTON28 DIJOFS_BUTTON(28) +#define DIJOFS_BUTTON29 DIJOFS_BUTTON(29) +#define DIJOFS_BUTTON30 DIJOFS_BUTTON(30) +#define DIJOFS_BUTTON31 DIJOFS_BUTTON(31) + + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * IDirectInput + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +#define DIENUM_STOP 0 +#define DIENUM_CONTINUE 1 + +typedef BOOL (FAR PASCAL * LPDIENUMDEVICESCALLBACKA)(LPCDIDEVICEINSTANCEA, LPVOID); +typedef BOOL (FAR PASCAL * LPDIENUMDEVICESCALLBACKW)(LPCDIDEVICEINSTANCEW, LPVOID); +#ifdef UNICODE +#define LPDIENUMDEVICESCALLBACK LPDIENUMDEVICESCALLBACKW +#else +#define LPDIENUMDEVICESCALLBACK LPDIENUMDEVICESCALLBACKA +#endif // !UNICODE + +#define DIEDFL_ALLDEVICES 0x00000000 +#define DIEDFL_ATTACHEDONLY 0x00000001 +#if(DIRECTINPUT_VERSION >= 0x0500) +#define DIEDFL_FORCEFEEDBACK 0x00000100 +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +#undef INTERFACE +#define INTERFACE IDirectInputW + +DECLARE_INTERFACE_(IDirectInputW, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputW methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEW *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; +}; + +typedef struct IDirectInputW *LPDIRECTINPUTW; + +#undef INTERFACE +#define INTERFACE IDirectInputA + +DECLARE_INTERFACE_(IDirectInputA, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputA methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEA *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; +}; + +typedef struct IDirectInputA *LPDIRECTINPUTA; + +#ifdef UNICODE +#define IID_IDirectInput IID_IDirectInputW +#define IDirectInput IDirectInputW +#define IDirectInputVtbl IDirectInputWVtbl +#else +#define IID_IDirectInput IID_IDirectInputA +#define IDirectInput IDirectInputA +#define IDirectInputVtbl IDirectInputAVtbl +#endif +typedef struct IDirectInput *LPDIRECTINPUT; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInput_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInput_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInput_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInput_CreateDevice(p,a,b,c) (p)->lpVtbl->CreateDevice(p,a,b,c) +#define IDirectInput_EnumDevices(p,a,b,c,d) (p)->lpVtbl->EnumDevices(p,a,b,c,d) +#define IDirectInput_GetDeviceStatus(p,a) (p)->lpVtbl->GetDeviceStatus(p,a) +#define IDirectInput_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInput_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#else +#define IDirectInput_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInput_AddRef(p) (p)->AddRef() +#define IDirectInput_Release(p) (p)->Release() +#define IDirectInput_CreateDevice(p,a,b,c) (p)->CreateDevice(a,b,c) +#define IDirectInput_EnumDevices(p,a,b,c,d) (p)->EnumDevices(a,b,c,d) +#define IDirectInput_GetDeviceStatus(p,a) (p)->GetDeviceStatus(a) +#define IDirectInput_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInput_Initialize(p,a,b) (p)->Initialize(a,b) +#endif + +#undef INTERFACE +#define INTERFACE IDirectInput2W + +DECLARE_INTERFACE_(IDirectInput2W, IDirectInputW) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputW methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEW *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; + + /*** IDirectInput2W methods ***/ + STDMETHOD(FindDevice)(THIS_ REFGUID,LPCWSTR,LPGUID) PURE; +}; + +typedef struct IDirectInput2W *LPDIRECTINPUT2W; + +#undef INTERFACE +#define INTERFACE IDirectInput2A + +DECLARE_INTERFACE_(IDirectInput2A, IDirectInputA) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputA methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEA *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; + + /*** IDirectInput2A methods ***/ + STDMETHOD(FindDevice)(THIS_ REFGUID,LPCSTR,LPGUID) PURE; +}; + +typedef struct IDirectInput2A *LPDIRECTINPUT2A; + +#ifdef UNICODE +#define IID_IDirectInput2 IID_IDirectInput2W +#define IDirectInput2 IDirectInput2W +#define IDirectInput2Vtbl IDirectInput2WVtbl +#else +#define IID_IDirectInput2 IID_IDirectInput2A +#define IDirectInput2 IDirectInput2A +#define IDirectInput2Vtbl IDirectInput2AVtbl +#endif +typedef struct IDirectInput2 *LPDIRECTINPUT2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInput2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInput2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInput2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInput2_CreateDevice(p,a,b,c) (p)->lpVtbl->CreateDevice(p,a,b,c) +#define IDirectInput2_EnumDevices(p,a,b,c,d) (p)->lpVtbl->EnumDevices(p,a,b,c,d) +#define IDirectInput2_GetDeviceStatus(p,a) (p)->lpVtbl->GetDeviceStatus(p,a) +#define IDirectInput2_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInput2_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectInput2_FindDevice(p,a,b,c) (p)->lpVtbl->FindDevice(p,a,b,c) +#else +#define IDirectInput2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInput2_AddRef(p) (p)->AddRef() +#define IDirectInput2_Release(p) (p)->Release() +#define IDirectInput2_CreateDevice(p,a,b,c) (p)->CreateDevice(a,b,c) +#define IDirectInput2_EnumDevices(p,a,b,c,d) (p)->EnumDevices(a,b,c,d) +#define IDirectInput2_GetDeviceStatus(p,a) (p)->GetDeviceStatus(a) +#define IDirectInput2_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInput2_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectInput2_FindDevice(p,a,b,c) (p)->FindDevice(a,b,c) +#endif + +extern HRESULT WINAPI DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter); +extern HRESULT WINAPI DirectInputCreateW(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTW *ppDI, LPUNKNOWN punkOuter); +#ifdef UNICODE +#define DirectInputCreate DirectInputCreateW +#else +#define DirectInputCreate DirectInputCreateA +#endif // !UNICODE + +#endif /* DIJ_RINGZERO */ + + +/**************************************************************************** + * + * Return Codes + * + ****************************************************************************/ + +/* + * The operation completed successfully. + */ +#define DI_OK S_OK + +/* + * The device exists but is not currently attached. + */ +#define DI_NOTATTACHED S_FALSE + +/* + * The device buffer overflowed. Some input was lost. + */ +#define DI_BUFFEROVERFLOW S_FALSE + +/* + * The change in device properties had no effect. + */ +#define DI_PROPNOEFFECT S_FALSE + +/* + * The operation had no effect. + */ +#define DI_NOEFFECT S_FALSE + +/* + * The device is a polled device. As a result, device buffering + * will not collect any data and event notifications will not be + * signalled until GetDeviceState is called. + */ +#define DI_POLLEDDEVICE ((HRESULT)0x00000002L) + +/* + * The parameters of the effect were successfully updated by + * IDirectInputEffect::SetParameters, but the effect was not + * downloaded because the device is not exclusively acquired + * or because the DIEP_NODOWNLOAD flag was passed. + */ +#define DI_DOWNLOADSKIPPED ((HRESULT)0x00000003L) + +/* + * The parameters of the effect were successfully updated by + * IDirectInputEffect::SetParameters, but in order to change + * the parameters, the effect needed to be restarted. + */ +#define DI_EFFECTRESTARTED ((HRESULT)0x00000004L) + +/* + * The parameters of the effect were successfully updated by + * IDirectInputEffect::SetParameters, but some of them were + * beyond the capabilities of the device and were truncated. + */ +#define DI_TRUNCATED ((HRESULT)0x00000008L) + +/* + * Equal to DI_EFFECTRESTARTED | DI_TRUNCATED. + */ +#define DI_TRUNCATEDANDRESTARTED ((HRESULT)0x0000000CL) + +/* + * The application requires a newer version of DirectInput. + */ +#define DIERR_OLDDIRECTINPUTVERSION \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_OLD_WIN_VERSION) + +/* + * The application was written for an unsupported prerelease version + * of DirectInput. + */ +#define DIERR_BETADIRECTINPUTVERSION \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_RMODE_APP) + +/* + * The object could not be created due to an incompatible driver version + * or mismatched or incomplete driver components. + */ +#define DIERR_BADDRIVERVER \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_BAD_DRIVER_LEVEL) + +/* + * The device or device instance or effect is not registered with DirectInput. + */ +#define DIERR_DEVICENOTREG REGDB_E_CLASSNOTREG + +/* + * The requested object does not exist. + */ +#define DIERR_NOTFOUND \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND) + +/* + * The requested object does not exist. + */ +#define DIERR_OBJECTNOTFOUND \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND) + +/* + * An invalid parameter was passed to the returning function, + * or the object was not in a state that admitted the function + * to be called. + */ +#define DIERR_INVALIDPARAM E_INVALIDARG + +/* + * The specified interface is not supported by the object + */ +#define DIERR_NOINTERFACE E_NOINTERFACE + +/* + * An undetermined error occured inside the DInput subsystem + */ +#define DIERR_GENERIC E_FAIL + +/* + * The DInput subsystem couldn't allocate sufficient memory to complete the + * caller's request. + */ +#define DIERR_OUTOFMEMORY E_OUTOFMEMORY + +/* + * The function called is not supported at this time + */ +#define DIERR_UNSUPPORTED E_NOTIMPL + +/* + * This object has not been initialized + */ +#define DIERR_NOTINITIALIZED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_NOT_READY) + +/* + * This object is already initialized + */ +#define DIERR_ALREADYINITIALIZED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_ALREADY_INITIALIZED) + +/* + * This object does not support aggregation + */ +#define DIERR_NOAGGREGATION CLASS_E_NOAGGREGATION + +/* + * Another app has a higher priority level, preventing this call from + * succeeding. + */ +#define DIERR_OTHERAPPHASPRIO E_ACCESSDENIED + +/* + * Access to the device has been lost. It must be re-acquired. + */ +#define DIERR_INPUTLOST \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_READ_FAULT) + +/* + * The operation cannot be performed while the device is acquired. + */ +#define DIERR_ACQUIRED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_BUSY) + +/* + * The operation cannot be performed unless the device is acquired. + */ +#define DIERR_NOTACQUIRED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INVALID_ACCESS) + +/* + * The specified property cannot be changed. + */ +#define DIERR_READONLY E_ACCESSDENIED + +/* + * The device already has an event notification associated with it. + */ +#define DIERR_HANDLEEXISTS E_ACCESSDENIED + +/* + * Data is not yet available. + */ +#ifndef E_PENDING +#define E_PENDING 0x80070007L +#endif + +/* + * Unable to IDirectInputJoyConfig_Acquire because the user + * does not have sufficient privileges to change the joystick + * configuration. + */ +#define DIERR_INSUFFICIENTPRIVS 0x80040200L + +/* + * The device is full. + */ +#define DIERR_DEVICEFULL 0x80040201L + +/* + * Not all the requested information fit into the buffer. + */ +#define DIERR_MOREDATA 0x80040202L + +/* + * The effect is not downloaded. + */ +#define DIERR_NOTDOWNLOADED 0x80040203L + +/* + * The device cannot be reinitialized because there are still effects + * attached to it. + */ +#define DIERR_HASEFFECTS 0x80040204L + +/* + * The operation cannot be performed unless the device is acquired + * in DISCL_EXCLUSIVE mode. + */ +#define DIERR_NOTEXCLUSIVEACQUIRED 0x80040205L + +/* + * The effect could not be downloaded because essential information + * is missing. For example, no axes have been associated with the + * effect, or no type-specific information has been created. + */ +#define DIERR_INCOMPLETEEFFECT 0x80040206L + +/* + * Attempted to read buffered device data from a device that is + * not buffered. + */ +#define DIERR_NOTBUFFERED 0x80040207L + +/* + * An attempt was made to modify parameters of an effect while it is + * playing. Not all hardware devices support altering the parameters + * of an effect while it is playing. + */ +#define DIERR_EFFECTPLAYING 0x80040208L + +#ifdef __cplusplus +}; +#endif + +#endif /* __DINPUT_INCLUDED__ */ + +/**************************************************************************** + * + * Definitions for non-IDirectInput (VJoyD) features defined more recently + * than the current sdk files + * + ****************************************************************************/ + +#ifdef _INC_MMSYSTEM +#ifndef MMNOJOY + +#ifndef __VJOYDX_INCLUDED__ +#define __VJOYDX_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Flag to indicate that the dwReserved2 field of the JOYINFOEX structure + * contains mini-driver specific data to be passed by VJoyD to the mini- + * driver instead of doing a poll. + */ +#define JOY_PASSDRIVERDATA 0x10000000l + +/* + * Informs the joystick driver that the configuration has been changed + * and should be reloaded from the registery. + * dwFlags is reserved and should be set to zero + */ +WINMMAPI MMRESULT WINAPI joyConfigChanged( DWORD dwFlags ); + +/* + * Hardware Setting indicating that the device is a headtracker + */ +#define JOY_HWS_ISHEADTRACKER 0x02000000l + +/* + * Hardware Setting indicating that the VxD is used to replace + * the standard analog polling + */ +#define JOY_HWS_ISGAMEPORTDRIVER 0x04000000l + +/* + * Hardware Setting indicating that the driver needs a standard + * gameport in order to communicate with the device. + */ +#define JOY_HWS_ISANALOGPORTDRIVER 0x08000000l + +/* + * Hardware Setting indicating that VJoyD should not load this + * driver, it will be loaded externally and will register with + * VJoyD of it's own accord. + */ +#define JOY_HWS_AUTOLOAD 0x10000000l + +/* + * Hardware Setting indicating that the driver acquires any + * resources needed without needing a devnode through VJoyD. + */ +#define JOY_HWS_NODEVNODE 0x20000000l + +/* + * Hardware Setting indicating that the VxD can be used as + * a port 201h emulator. + */ +#define JOY_HWS_ISGAMEPORTEMULATOR 0x40000000l + + +/* + * Usage Setting indicating that the settings are volatile and + * should be removed if still present on a reboot. + */ +#define JOY_US_VOLATILE 0x00000008L + +#ifdef __cplusplus +}; +#endif + +#endif /* __VJOYDX_INCLUDED__ */ + +#endif /* not MMNOJOY */ +#endif /* _INC_MMSYSTEM */ + +/**************************************************************************** + * + * Definitions for non-IDirectInput (VJoyD) features defined more recently + * than the current ddk files + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +#ifdef _INC_MMDDK +#ifndef MMNOJOYDEV + +#ifndef __VJOYDXD_INCLUDED__ +#define __VJOYDXD_INCLUDED__ +/* + * Poll type in which the do_other field of the JOYOEMPOLLDATA + * structure contains mini-driver specific data passed from an app. + */ +#define JOY_OEMPOLL_PASSDRIVERDATA 7 + +#endif /* __VJOYDXD_INCLUDED__ */ + +#endif /* not MMNOJOYDEV */ +#endif /* _INC_MMDDK */ + +#endif /* DIJ_RINGZERO */ diff --git a/lib/win/DirectX/dinput.lib b/lib/win/DirectX/dinput.lib new file mode 100644 index 000000000..d105311a3 Binary files /dev/null and b/lib/win/DirectX/dinput.lib differ diff --git a/lib/win/DirectX/dplay.h b/lib/win/DirectX/dplay.h new file mode 100644 index 000000000..6e1952f84 --- /dev/null +++ b/lib/win/DirectX/dplay.h @@ -0,0 +1,2127 @@ +/*==========================================================================; + * + * Copyright (C) 1994-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dplay.h + * Content: DirectPlay include file + * + ***************************************************************************/ + +#ifndef __DPLAY_INCLUDED__ +#define __DPLAY_INCLUDED__ + +#include // for DECLARE_INTERFACE and HRESULT + +/* + * Some types + */ + +typedef LPVOID (*LPRGLPVOID)[]; +typedef LPRGLPVOID PRGPVOID, LPRGPVOID, PRGLPVOID, PAPVOID, LPAPVOID, PALPVOID, LPALPVOID; + +#define VOL volatile +typedef VOID *VOL LPVOIDV; + + +#define _FACDP 0x877 +#define MAKE_DPHRESULT( code ) MAKE_HRESULT( 1, _FACDP, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * GUIDS used by DirectPlay objects + */ +DEFINE_GUID(IID_IDirectPlay2, 0x2b74f7c0, 0x9154, 0x11cf, 0xa9, 0xcd, 0x0, 0xaa, 0x0, 0x68, 0x86, 0xe3); +DEFINE_GUID(IID_IDirectPlay2A,0x9d460580, 0xa822, 0x11cf, 0x96, 0xc, 0x0, 0x80, 0xc7, 0x53, 0x4e, 0x82); + +DEFINE_GUID(IID_IDirectPlay3, 0x133efe40, 0x32dc, 0x11d0, 0x9c, 0xfb, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); +DEFINE_GUID(IID_IDirectPlay3A,0x133efe41, 0x32dc, 0x11d0, 0x9c, 0xfb, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); + +DEFINE_GUID(IID_IDirectPlay4, 0xab1c530, 0x4745, 0x11d1, 0xa7, 0xa1, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); +DEFINE_GUID(IID_IDirectPlay4A,0xab1c531, 0x4745, 0x11d1, 0xa7, 0xa1, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); + +// {D1EB6D20-8923-11d0-9D97-00A0C90A43CB} +DEFINE_GUID(CLSID_DirectPlay,0xd1eb6d20, 0x8923, 0x11d0, 0x9d, 0x97, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); + +/* + * GUIDS used by Service Providers shipped with DirectPlay + * Use these to identify Service Provider returned by EnumConnections + */ + +// GUID for IPX service provider +// {685BC400-9D2C-11cf-A9CD-00AA006886E3} +DEFINE_GUID(DPSPGUID_IPX, +0x685bc400, 0x9d2c, 0x11cf, 0xa9, 0xcd, 0x0, 0xaa, 0x0, 0x68, 0x86, 0xe3); + +// GUID for TCP/IP service provider +// 36E95EE0-8577-11cf-960C-0080C7534E82 +DEFINE_GUID(DPSPGUID_TCPIP, +0x36E95EE0, 0x8577, 0x11cf, 0x96, 0xc, 0x0, 0x80, 0xc7, 0x53, 0x4e, 0x82); + +// GUID for Serial service provider +// {0F1D6860-88D9-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPSPGUID_SERIAL, +0xf1d6860, 0x88d9, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// GUID for Modem service provider +// {44EAA760-CB68-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPSPGUID_MODEM, +0x44eaa760, 0xcb68, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/**************************************************************************** + * + * DirectPlay Structures + * + * Various structures used to invoke DirectPlay. + * + ****************************************************************************/ + +#ifndef IDIRECTPLAY2_OR_GREATER +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#else +typedef struct IUnknown FAR *LPDIRECTPLAY; +#endif + +typedef struct IDirectPlay2 FAR *LPDIRECTPLAY2; +typedef struct IDirectPlay2 FAR *LPDIRECTPLAY2A; +typedef struct IDirectPlay2 IDirectPlay2A; + +typedef struct IDirectPlay3 FAR *LPDIRECTPLAY3; +typedef struct IDirectPlay3 FAR *LPDIRECTPLAY3A; +typedef struct IDirectPlay3 IDirectPlay3A; + +typedef struct IDirectPlay4 FAR *LPDIRECTPLAY4; +typedef struct IDirectPlay4 FAR *LPDIRECTPLAY4A; +typedef struct IDirectPlay4 IDirectPlay4A; + +/* + * DPID + * DirectPlay player and group ID + */ +typedef DWORD DPID, FAR *LPDPID; + +/* + * DPID that system messages come from + */ +#define DPID_SYSMSG 0 + +/* + * DPID representing all players in the session + */ +#define DPID_ALLPLAYERS 0 + +/* + * DPID representing the server player + */ +#define DPID_SERVERPLAYER 1 + + +/* + * DPID representing the maxiumum ID in the range of DPID's reserved for + * use by DirectPlay. + */ +#define DPID_RESERVEDRANGE 100 + +/* + * The player ID is unknown (used with e.g. DPSESSION_NOMESSAGEID) + */ +#define DPID_UNKNOWN 0xFFFFFFFF + +/* + * DPCAPS + * Used to obtain the capabilities of a DirectPlay object + */ +typedef struct +{ + DWORD dwSize; // Size of structure, in bytes + DWORD dwFlags; // DPCAPS_xxx flags + DWORD dwMaxBufferSize; // Maximum message size, in bytes, for this service provider + DWORD dwMaxQueueSize; // Obsolete. + DWORD dwMaxPlayers; // Maximum players/groups (local + remote) + DWORD dwHundredBaud; // Bandwidth in 100 bits per second units; + // i.e. 24 is 2400, 96 is 9600, etc. + DWORD dwLatency; // Estimated latency; 0 = unknown + DWORD dwMaxLocalPlayers; // Maximum # of locally created players allowed + DWORD dwHeaderLength; // Maximum header length, in bytes, on messages + // added by the service provider + DWORD dwTimeout; // Service provider's suggested timeout value + // This is how long DirectPlay will wait for + // responses to system messages +} DPCAPS, FAR *LPDPCAPS; + +/* + * This DirectPlay object is the session host. If the host exits the + * session, another application will become the host and receive a + * DPSYS_HOST system message. + */ +#define DPCAPS_ISHOST 0x00000002 + +/* + * The service provider bound to this DirectPlay object can optimize + * group messaging. + */ +#define DPCAPS_GROUPOPTIMIZED 0x00000008 + +/* + * The service provider bound to this DirectPlay object can optimize + * keep alives (see DPSESSION_KEEPALIVE) + */ +#define DPCAPS_KEEPALIVEOPTIMIZED 0x00000010 + +/* + * The service provider bound to this DirectPlay object can optimize + * guaranteed message delivery. + */ +#define DPCAPS_GUARANTEEDOPTIMIZED 0x00000020 + +/* + * This DirectPlay object supports guaranteed message delivery. + */ +#define DPCAPS_GUARANTEEDSUPPORTED 0x00000040 + +/* + * This DirectPlay object supports digital signing of messages. + */ +#define DPCAPS_SIGNINGSUPPORTED 0x00000080 + +/* + * This DirectPlay object supports encryption of messages. + */ +#define DPCAPS_ENCRYPTIONSUPPORTED 0x00000100 + +/* + * This DirectPlay player was created on this machine + */ +#define DPPLAYERCAPS_LOCAL 0x00000800 + +/* + * Current Open settings supports all forms of Cancel + */ +#define DPCAPS_ASYNCCANCELSUPPORTED 0x00001000 + +/* + * Current Open settings supports CancelAll, but not Cancel + */ +#define DPCAPS_ASYNCCANCELALLSUPPORTED 0x00002000 + +/* + * Current Open settings supports Send Timeouts for sends + */ +#define DPCAPS_SENDTIMEOUTSUPPORTED 0x00004000 + +/* + * Current Open settings supports send priority + */ +#define DPCAPS_SENDPRIORITYSUPPORTED 0x00008000 + +/* + * Current Open settings supports DPSEND_ASYNC flag + */ +#define DPCAPS_ASYNCSUPPORTED 0x00010000 + + +/* + * DPSESSIONDESC2 + * Used to describe the properties of a DirectPlay + * session instance + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // DPSESSION_xxx flags + GUID guidInstance; // ID for the session instance + GUID guidApplication; // GUID of the DirectPlay application. + // GUID_NULL for all applications. + DWORD dwMaxPlayers; // Maximum # players allowed in session + DWORD dwCurrentPlayers; // Current # players in session (read only) + union + { // Name of the session + LPWSTR lpszSessionName; // Unicode + LPSTR lpszSessionNameA; // ANSI + }; + union + { // Password of the session (optional) + LPWSTR lpszPassword; // Unicode + LPSTR lpszPasswordA; // ANSI + }; + DWORD dwReserved1; // Reserved for future MS use. + DWORD dwReserved2; + DWORD dwUser1; // For use by the application + DWORD dwUser2; + DWORD dwUser3; + DWORD dwUser4; +} DPSESSIONDESC2, FAR *LPDPSESSIONDESC2; + +typedef DPSESSIONDESC2 * VOL LPDPSESSIONDESC2_V; + +/* + * LPCDPSESSIONDESC2 + * A constant pointer to DPSESSIONDESC2 + */ +typedef const DPSESSIONDESC2 FAR *LPCDPSESSIONDESC2; + +/* + * Applications cannot create new players in this session. + */ +#define DPSESSION_NEWPLAYERSDISABLED 0x00000001 + +/* + * If the DirectPlay object that created the session, the host, + * quits, then the host will attempt to migrate to another + * DirectPlay object so that new players can continue to be created + * and new applications can join the session. + */ +#define DPSESSION_MIGRATEHOST 0x00000004 + +/* + * This flag tells DirectPlay not to set the idPlayerTo and idPlayerFrom + * fields in player messages. This cuts two DWORD's off the message + * overhead. + */ +#define DPSESSION_NOMESSAGEID 0x00000008 + + +/* + * This flag tells DirectPlay to not allow any new applications to + * join the session. Applications already in the session can still + * create new players. + */ +#define DPSESSION_JOINDISABLED 0x00000020 + +/* + * This flag tells DirectPlay to detect when remote players + * exit abnormally (e.g. their computer or modem gets unplugged) + */ +#define DPSESSION_KEEPALIVE 0x00000040 + +/* + * This flag tells DirectPlay not to send a message to all players + * when a players remote data changes + */ +#define DPSESSION_NODATAMESSAGES 0x00000080 + +/* + * This flag indicates that the session belongs to a secure server + * and needs user authentication + */ +#define DPSESSION_SECURESERVER 0x00000100 + +/* + * This flag indicates that the session is private and requirs a password + * for EnumSessions as well as Open. + */ +#define DPSESSION_PRIVATE 0x00000200 + +/* + * This flag indicates that the session requires a password for joining. + */ +#define DPSESSION_PASSWORDREQUIRED 0x00000400 + +/* + * This flag tells DirectPlay to route all messages through the server + */ +#define DPSESSION_MULTICASTSERVER 0x00000800 + +/* + * This flag tells DirectPlay to only download information about the + * DPPLAYER_SERVERPLAYER. + */ +#define DPSESSION_CLIENTSERVER 0x00001000 + +/* + * This flag tells DirectPlay to use the protocol built into dplay + * for reliability and statistics all the time. When this bit is + * set, only other sessions with this bit set can join or be joined. + */ +#define DPSESSION_DIRECTPLAYPROTOCOL 0x00002000 + +/* + * This flag tells DirectPlay that preserving order of received + * packets is not important, when using reliable delivery. This + * will allow messages to be indicated out of order if preceding + * messages have not yet arrived. Otherwise DPLAY will wait for + * earlier messages before delivering later reliable messages. + */ +#define DPSESSION_NOPRESERVEORDER 0x00004000 + + +/* + * This flag tells DirectPlay to optimize communication for latency + */ +#define DPSESSION_OPTIMIZELATENCY 0x00008000 + +/* + * DPNAME + * Used to hold the name of a DirectPlay entity + * like a player or a group + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // The short or friendly name + LPWSTR lpszShortName; // Unicode + LPSTR lpszShortNameA; // ANSI + }; + union + { // The long or formal name + LPWSTR lpszLongName; // Unicode + LPSTR lpszLongNameA; // ANSI + }; + +} DPNAME, FAR *LPDPNAME; + +/* + * LPCDPNAME + * A constant pointer to DPNAME + */ +typedef const DPNAME FAR *LPCDPNAME; + +/* + * DPCREDENTIALS + * Used to hold the user name and password of a DirectPlay user + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // User name of the account + LPWSTR lpszUsername; // Unicode + LPSTR lpszUsernameA; // ANSI + }; + union + { // Password of the account + LPWSTR lpszPassword; // Unicode + LPSTR lpszPasswordA; // ANSI + }; + union + { // Domain name of the account + LPWSTR lpszDomain; // Unicode + LPSTR lpszDomainA; // ANSI + }; +} DPCREDENTIALS, FAR *LPDPCREDENTIALS; + +typedef const DPCREDENTIALS FAR *LPCDPCREDENTIALS; + +/* + * DPSECURITYDESC + * Used to describe the security properties of a DirectPlay + * session instance + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // SSPI provider name + LPWSTR lpszSSPIProvider; // Unicode + LPSTR lpszSSPIProviderA; // ANSI + }; + union + { // CAPI provider name + LPWSTR lpszCAPIProvider; // Unicode + LPSTR lpszCAPIProviderA; // ANSI + }; + DWORD dwCAPIProviderType; // Crypto Service Provider type + DWORD dwEncryptionAlgorithm; // Encryption Algorithm type +} DPSECURITYDESC, FAR *LPDPSECURITYDESC; + +typedef const DPSECURITYDESC FAR *LPCDPSECURITYDESC; + +/* + * DPACCOUNTDESC + * Used to describe a user membership account + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // Account identifier + LPWSTR lpszAccountID; // Unicode + LPSTR lpszAccountIDA; // ANSI + }; +} DPACCOUNTDESC, FAR *LPDPACCOUNTDESC; + +typedef const DPACCOUNTDESC FAR *LPCDPACCOUNTDESC; + +/* + * LPCGUID + * A constant pointer to a guid + */ +typedef const GUID FAR *LPCGUID; + +/* + * DPLCONNECTION + * Used to hold all in the informaion needed to connect + * an application to a session or create a session + */ +typedef struct +{ + DWORD dwSize; // Size of this structure + DWORD dwFlags; // Flags specific to this structure + LPDPSESSIONDESC2 lpSessionDesc; // Pointer to session desc to use on connect + LPDPNAME lpPlayerName; // Pointer to Player name structure + GUID guidSP; // GUID of the DPlay SP to use + LPVOID lpAddress; // Address for service provider + DWORD dwAddressSize; // Size of address data +} DPLCONNECTION, FAR *LPDPLCONNECTION; + +/* + * LPCDPLCONNECTION + * A constant pointer to DPLCONNECTION + */ +typedef const DPLCONNECTION FAR *LPCDPLCONNECTION; + +/* + * DPCHAT + * Used to hold the a DirectPlay chat message + */ +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + union + { // Message string + LPWSTR lpszMessage; // Unicode + LPSTR lpszMessageA; // ANSI + }; +} DPCHAT, FAR * LPDPCHAT; + +/* + * SGBUFFER + * Scatter Gather Buffer used for SendEx + */ +typedef struct +{ + UINT len; // length of buffer data + PUCHAR pData; // pointer to buffer data +} SGBUFFER, *PSGBUFFER, FAR *LPSGBUFFER; + + +/**************************************************************************** + * + * Prototypes for DirectPlay callback functions + * + ****************************************************************************/ + +/* + * Callback for IDirectPlay2::EnumSessions + */ +typedef BOOL (FAR PASCAL * LPDPENUMSESSIONSCALLBACK2)( + LPCDPSESSIONDESC2 lpThisSD, + LPDWORD lpdwTimeOut, + DWORD dwFlags, + LPVOID lpContext ); + +/* + * This flag is set on the EnumSessions callback dwFlags parameter when + * the time out has occurred. There will be no session data for this + * callback. If *lpdwTimeOut is set to a non-zero value and the + * EnumSessionsCallback function returns TRUE then EnumSessions will + * continue waiting until the next timeout occurs. Timeouts are in + * milliseconds. + */ +#define DPESC_TIMEDOUT 0x00000001 + + +/* + * Callback for IDirectPlay2::EnumPlayers + * IDirectPlay2::EnumGroups + * IDirectPlay2::EnumGroupPlayers + */ +typedef BOOL (FAR PASCAL *LPDPENUMPLAYERSCALLBACK2)( + DPID dpId, + DWORD dwPlayerType, + LPCDPNAME lpName, + DWORD dwFlags, + LPVOID lpContext ); + + +/* + * Unicode callback for DirectPlayEnumerate + * This callback prototype will be used if compiling + * for Unicode strings + */ +typedef BOOL (FAR PASCAL * LPDPENUMDPCALLBACK)( + LPGUID lpguidSP, + LPWSTR lpSPName, + DWORD dwMajorVersion, + DWORD dwMinorVersion, + LPVOID lpContext); + +/* + * ANSI callback for DirectPlayEnumerate + * This callback prototype will be used if compiling + * for ANSI strings + */ +typedef BOOL (FAR PASCAL * LPDPENUMDPCALLBACKA)( + LPGUID lpguidSP, + LPSTR lpSPName, + DWORD dwMajorVersion, + DWORD dwMinorVersion, + LPVOID lpContext); + +/* + * Callback for IDirectPlay3(A)::EnumConnections + */ +typedef BOOL (FAR PASCAL * LPDPENUMCONNECTIONSCALLBACK)( + LPCGUID lpguidSP, + LPVOID lpConnection, + DWORD dwConnectionSize, + LPCDPNAME lpName, + DWORD dwFlags, + LPVOID lpContext); + + +/* + * API's + */ + +#ifdef UNICODE +#define DirectPlayEnumerate DirectPlayEnumerateW +#else +#define DirectPlayEnumerate DirectPlayEnumerateA +#endif // UNICODE + +extern HRESULT WINAPI DirectPlayEnumerateA( LPDPENUMDPCALLBACKA, LPVOID ); +extern HRESULT WINAPI DirectPlayEnumerateW( LPDPENUMDPCALLBACK, LPVOID ); +extern HRESULT WINAPI DirectPlayCreate( LPGUID lpGUID, LPDIRECTPLAY *lplpDP, IUnknown *pUnk); + +/**************************************************************************** + * + * IDirectPlay2 (and IDirectPlay2A) Interface + * + ****************************************************************************/ + +#undef INTERFACE +#define INTERFACE IDirectPlay2 +DECLARE_INTERFACE_( IDirectPlay2, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay2 methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS,DWORD) PURE; + STDMETHOD(GetGroupData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetGroupName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerAddress) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID,LPDPCAPS,DWORD) PURE; + STDMETHOD(GetPlayerData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetSessionDesc) (THIS_ LPVOID,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetGroupData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetGroupName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetPlayerData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetSessionDesc) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; +}; + +/**************************************************************************** + * + * IDirectPlay2 interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlay2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlay2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectPlay2_AddPlayerToGroup(p,a,b) (p)->lpVtbl->AddPlayerToGroup(p,a,b) +#define IDirectPlay2_Close(p) (p)->lpVtbl->Close(p) +#define IDirectPlay2_CreateGroup(p,a,b,c,d,e) (p)->lpVtbl->CreateGroup(p,a,b,c,d,e) +#define IDirectPlay2_CreatePlayer(p,a,b,c,d,e,f) (p)->lpVtbl->CreatePlayer(p,a,b,c,d,e,f) +#define IDirectPlay2_DeletePlayerFromGroup(p,a,b) (p)->lpVtbl->DeletePlayerFromGroup(p,a,b) +#define IDirectPlay2_DestroyGroup(p,a) (p)->lpVtbl->DestroyGroup(p,a) +#define IDirectPlay2_DestroyPlayer(p,a) (p)->lpVtbl->DestroyPlayer(p,a) +#define IDirectPlay2_EnumGroupPlayers(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupPlayers(p,a,b,c,d,e) +#define IDirectPlay2_EnumGroups(p,a,b,c,d) (p)->lpVtbl->EnumGroups(p,a,b,c,d) +#define IDirectPlay2_EnumPlayers(p,a,b,c,d) (p)->lpVtbl->EnumPlayers(p,a,b,c,d) +#define IDirectPlay2_EnumSessions(p,a,b,c,d,e) (p)->lpVtbl->EnumSessions(p,a,b,c,d,e) +#define IDirectPlay2_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirectPlay2_GetMessageCount(p,a,b) (p)->lpVtbl->GetMessageCount(p,a,b) +#define IDirectPlay2_GetGroupData(p,a,b,c,d) (p)->lpVtbl->GetGroupData(p,a,b,c,d) +#define IDirectPlay2_GetGroupName(p,a,b,c) (p)->lpVtbl->GetGroupName(p,a,b,c) +#define IDirectPlay2_GetPlayerAddress(p,a,b,c) (p)->lpVtbl->GetPlayerAddress(p,a,b,c) +#define IDirectPlay2_GetPlayerCaps(p,a,b,c) (p)->lpVtbl->GetPlayerCaps(p,a,b,c) +#define IDirectPlay2_GetPlayerData(p,a,b,c,d) (p)->lpVtbl->GetPlayerData(p,a,b,c,d) +#define IDirectPlay2_GetPlayerName(p,a,b,c) (p)->lpVtbl->GetPlayerName(p,a,b,c) +#define IDirectPlay2_GetSessionDesc(p,a,b) (p)->lpVtbl->GetSessionDesc(p,a,b) +#define IDirectPlay2_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirectPlay2_Open(p,a,b) (p)->lpVtbl->Open(p,a,b) +#define IDirectPlay2_Receive(p,a,b,c,d,e) (p)->lpVtbl->Receive(p,a,b,c,d,e) +#define IDirectPlay2_Send(p,a,b,c,d,e) (p)->lpVtbl->Send(p,a,b,c,d,e) +#define IDirectPlay2_SetGroupData(p,a,b,c,d) (p)->lpVtbl->SetGroupData(p,a,b,c,d) +#define IDirectPlay2_SetGroupName(p,a,b,c) (p)->lpVtbl->SetGroupName(p,a,b,c) +#define IDirectPlay2_SetPlayerData(p,a,b,c,d) (p)->lpVtbl->SetPlayerData(p,a,b,c,d) +#define IDirectPlay2_SetPlayerName(p,a,b,c) (p)->lpVtbl->SetPlayerName(p,a,b,c) +#define IDirectPlay2_SetSessionDesc(p,a,b) (p)->lpVtbl->SetSessionDesc(p,a,b) + +#else /* C++ */ + +#define IDirectPlay2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlay2_AddRef(p) (p)->AddRef() +#define IDirectPlay2_Release(p) (p)->Release() +#define IDirectPlay2_AddPlayerToGroup(p,a,b) (p)->AddPlayerToGroup(a,b) +#define IDirectPlay2_Close(p) (p)->Close() +#define IDirectPlay2_CreateGroup(p,a,b,c,d,e) (p)->CreateGroup(a,b,c,d,e) +#define IDirectPlay2_CreatePlayer(p,a,b,c,d,e,f) (p)->CreatePlayer(a,b,c,d,e,f) +#define IDirectPlay2_DeletePlayerFromGroup(p,a,b) (p)->DeletePlayerFromGroup(a,b) +#define IDirectPlay2_DestroyGroup(p,a) (p)->DestroyGroup(a) +#define IDirectPlay2_DestroyPlayer(p,a) (p)->DestroyPlayer(a) +#define IDirectPlay2_EnumGroupPlayers(p,a,b,c,d,e) (p)->EnumGroupPlayers(a,b,c,d,e) +#define IDirectPlay2_EnumGroups(p,a,b,c,d) (p)->EnumGroups(a,b,c,d) +#define IDirectPlay2_EnumPlayers(p,a,b,c,d) (p)->EnumPlayers(a,b,c,d) +#define IDirectPlay2_EnumSessions(p,a,b,c,d,e) (p)->EnumSessions(a,b,c,d,e) +#define IDirectPlay2_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirectPlay2_GetMessageCount(p,a,b) (p)->GetMessageCount(a,b) +#define IDirectPlay2_GetGroupData(p,a,b,c,d) (p)->GetGroupData(a,b,c,d) +#define IDirectPlay2_GetGroupName(p,a,b,c) (p)->GetGroupName(a,b,c) +#define IDirectPlay2_GetPlayerAddress(p,a,b,c) (p)->GetPlayerAddress(a,b,c) +#define IDirectPlay2_GetPlayerCaps(p,a,b,c) (p)->GetPlayerCaps(a,b,c) +#define IDirectPlay2_GetPlayerData(p,a,b,c,d) (p)->GetPlayerData(a,b,c,d) +#define IDirectPlay2_GetPlayerName(p,a,b,c) (p)->GetPlayerName(a,b,c) +#define IDirectPlay2_GetSessionDesc(p,a,b) (p)->GetSessionDesc(a,b) +#define IDirectPlay2_Initialize(p,a) (p)->Initialize(a) +#define IDirectPlay2_Open(p,a,b) (p)->Open(a,b) +#define IDirectPlay2_Receive(p,a,b,c,d,e) (p)->Receive(a,b,c,d,e) +#define IDirectPlay2_Send(p,a,b,c,d,e) (p)->Send(a,b,c,d,e) +#define IDirectPlay2_SetGroupData(p,a,b,c,d) (p)->SetGroupData(a,b,c,d) +#define IDirectPlay2_SetGroupName(p,a,b,c) (p)->SetGroupName(a,b,c) +#define IDirectPlay2_SetPlayerData(p,a,b,c,d) (p)->SetPlayerData(a,b,c,d) +#define IDirectPlay2_SetPlayerName(p,a,b,c) (p)->SetPlayerName(a,b,c) +#define IDirectPlay2_SetSessionDesc(p,a,b) (p)->SetSessionDesc(a,b) + +#endif + +/**************************************************************************** + * + * IDirectPlay3 (and IDirectPlay3A) Interface + * + ****************************************************************************/ + +#undef INTERFACE +#define INTERFACE IDirectPlay3 +DECLARE_INTERFACE_( IDirectPlay3, IDirectPlay2 ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay2 methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS,DWORD) PURE; + STDMETHOD(GetGroupData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetGroupName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerAddress) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID,LPDPCAPS,DWORD) PURE; + STDMETHOD(GetPlayerData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetSessionDesc) (THIS_ LPVOID,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetGroupData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetGroupName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetPlayerData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetSessionDesc) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + /*** IDirectPlay3 methods ***/ + STDMETHOD(AddGroupToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(CreateGroupInGroup) (THIS_ DPID,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeleteGroupFromGroup) (THIS_ DPID,DPID) PURE; + STDMETHOD(EnumConnections) (THIS_ LPCGUID,LPDPENUMCONNECTIONSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroupsInGroup) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetGroupConnectionSettings)(THIS_ DWORD, DPID, LPVOID, LPDWORD) PURE; + STDMETHOD(InitializeConnection) (THIS_ LPVOID,DWORD) PURE; + STDMETHOD(SecureOpen) (THIS_ LPCDPSESSIONDESC2,DWORD,LPCDPSECURITYDESC,LPCDPCREDENTIALS) PURE; + STDMETHOD(SendChatMessage) (THIS_ DPID,DPID,DWORD,LPDPCHAT) PURE; + STDMETHOD(SetGroupConnectionSettings)(THIS_ DWORD,DPID,LPDPLCONNECTION) PURE; + STDMETHOD(StartSession) (THIS_ DWORD,DPID) PURE; + STDMETHOD(GetGroupFlags) (THIS_ DPID,LPDWORD) PURE; + STDMETHOD(GetGroupParent) (THIS_ DPID,LPDPID) PURE; + STDMETHOD(GetPlayerAccount) (THIS_ DPID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(GetPlayerFlags) (THIS_ DPID,LPDWORD) PURE; +}; + +/**************************************************************************** + * + * IDirectPlay3 interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlay3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlay3_Release(p) (p)->lpVtbl->Release(p) +#define IDirectPlay3_AddPlayerToGroup(p,a,b) (p)->lpVtbl->AddPlayerToGroup(p,a,b) +#define IDirectPlay3_Close(p) (p)->lpVtbl->Close(p) +#define IDirectPlay3_CreateGroup(p,a,b,c,d,e) (p)->lpVtbl->CreateGroup(p,a,b,c,d,e) +#define IDirectPlay3_CreatePlayer(p,a,b,c,d,e,f) (p)->lpVtbl->CreatePlayer(p,a,b,c,d,e,f) +#define IDirectPlay3_DeletePlayerFromGroup(p,a,b) (p)->lpVtbl->DeletePlayerFromGroup(p,a,b) +#define IDirectPlay3_DestroyGroup(p,a) (p)->lpVtbl->DestroyGroup(p,a) +#define IDirectPlay3_DestroyPlayer(p,a) (p)->lpVtbl->DestroyPlayer(p,a) +#define IDirectPlay3_EnumGroupPlayers(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupPlayers(p,a,b,c,d,e) +#define IDirectPlay3_EnumGroups(p,a,b,c,d) (p)->lpVtbl->EnumGroups(p,a,b,c,d) +#define IDirectPlay3_EnumPlayers(p,a,b,c,d) (p)->lpVtbl->EnumPlayers(p,a,b,c,d) +#define IDirectPlay3_EnumSessions(p,a,b,c,d,e) (p)->lpVtbl->EnumSessions(p,a,b,c,d,e) +#define IDirectPlay3_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirectPlay3_GetMessageCount(p,a,b) (p)->lpVtbl->GetMessageCount(p,a,b) +#define IDirectPlay3_GetGroupData(p,a,b,c,d) (p)->lpVtbl->GetGroupData(p,a,b,c,d) +#define IDirectPlay3_GetGroupName(p,a,b,c) (p)->lpVtbl->GetGroupName(p,a,b,c) +#define IDirectPlay3_GetPlayerAddress(p,a,b,c) (p)->lpVtbl->GetPlayerAddress(p,a,b,c) +#define IDirectPlay3_GetPlayerCaps(p,a,b,c) (p)->lpVtbl->GetPlayerCaps(p,a,b,c) +#define IDirectPlay3_GetPlayerData(p,a,b,c,d) (p)->lpVtbl->GetPlayerData(p,a,b,c,d) +#define IDirectPlay3_GetPlayerName(p,a,b,c) (p)->lpVtbl->GetPlayerName(p,a,b,c) +#define IDirectPlay3_GetSessionDesc(p,a,b) (p)->lpVtbl->GetSessionDesc(p,a,b) +#define IDirectPlay3_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirectPlay3_Open(p,a,b) (p)->lpVtbl->Open(p,a,b) +#define IDirectPlay3_Receive(p,a,b,c,d,e) (p)->lpVtbl->Receive(p,a,b,c,d,e) +#define IDirectPlay3_Send(p,a,b,c,d,e) (p)->lpVtbl->Send(p,a,b,c,d,e) +#define IDirectPlay3_SetGroupData(p,a,b,c,d) (p)->lpVtbl->SetGroupData(p,a,b,c,d) +#define IDirectPlay3_SetGroupName(p,a,b,c) (p)->lpVtbl->SetGroupName(p,a,b,c) +#define IDirectPlay3_SetPlayerData(p,a,b,c,d) (p)->lpVtbl->SetPlayerData(p,a,b,c,d) +#define IDirectPlay3_SetPlayerName(p,a,b,c) (p)->lpVtbl->SetPlayerName(p,a,b,c) +#define IDirectPlay3_SetSessionDesc(p,a,b) (p)->lpVtbl->SetSessionDesc(p,a,b) +#define IDirectPlay3_AddGroupToGroup(p,a,b) (p)->lpVtbl->AddGroupToGroup(p,a,b) +#define IDirectPlay3_CreateGroupInGroup(p,a,b,c,d,e,f) (p)->lpVtbl->CreateGroupInGroup(p,a,b,c,d,e,f) +#define IDirectPlay3_DeleteGroupFromGroup(p,a,b) (p)->lpVtbl->DeleteGroupFromGroup(p,a,b) +#define IDirectPlay3_EnumConnections(p,a,b,c,d) (p)->lpVtbl->EnumConnections(p,a,b,c,d) +#define IDirectPlay3_EnumGroupsInGroup(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupsInGroup(p,a,b,c,d,e) +#define IDirectPlay3_GetGroupConnectionSettings(p,a,b,c,d) (p)->lpVtbl->GetGroupConnectionSettings(p,a,b,c,d) +#define IDirectPlay3_InitializeConnection(p,a,b) (p)->lpVtbl->InitializeConnection(p,a,b) +#define IDirectPlay3_SecureOpen(p,a,b,c,d) (p)->lpVtbl->SecureOpen(p,a,b,c,d) +#define IDirectPlay3_SendChatMessage(p,a,b,c,d) (p)->lpVtbl->SendChatMessage(p,a,b,c,d) +#define IDirectPlay3_SetGroupConnectionSettings(p,a,b,c) (p)->lpVtbl->SetGroupConnectionSettings(p,a,b,c) +#define IDirectPlay3_StartSession(p,a,b) (p)->lpVtbl->StartSession(p,a,b) +#define IDirectPlay3_GetGroupFlags(p,a,b) (p)->lpVtbl->GetGroupFlags(p,a,b) +#define IDirectPlay3_GetGroupParent(p,a,b) (p)->lpVtbl->GetGroupParent(p,a,b) +#define IDirectPlay3_GetPlayerAccount(p,a,b,c,d) (p)->lpVtbl->GetPlayerAccount(p,a,b,c,d) +#define IDirectPlay3_GetPlayerFlags(p,a,b) (p)->lpVtbl->GetPlayerFlags(p,a,b) + +#else /* C++ */ + +#define IDirectPlay3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlay3_AddRef(p) (p)->AddRef() +#define IDirectPlay3_Release(p) (p)->Release() +#define IDirectPlay3_AddPlayerToGroup(p,a,b) (p)->AddPlayerToGroup(a,b) +#define IDirectPlay3_Close(p) (p)->Close() +#define IDirectPlay3_CreateGroup(p,a,b,c,d,e) (p)->CreateGroup(a,b,c,d,e) +#define IDirectPlay3_CreatePlayer(p,a,b,c,d,e,f) (p)->CreatePlayer(a,b,c,d,e,f) +#define IDirectPlay3_DeletePlayerFromGroup(p,a,b) (p)->DeletePlayerFromGroup(a,b) +#define IDirectPlay3_DestroyGroup(p,a) (p)->DestroyGroup(a) +#define IDirectPlay3_DestroyPlayer(p,a) (p)->DestroyPlayer(a) +#define IDirectPlay3_EnumGroupPlayers(p,a,b,c,d,e) (p)->EnumGroupPlayers(a,b,c,d,e) +#define IDirectPlay3_EnumGroups(p,a,b,c,d) (p)->EnumGroups(a,b,c,d) +#define IDirectPlay3_EnumPlayers(p,a,b,c,d) (p)->EnumPlayers(a,b,c,d) +#define IDirectPlay3_EnumSessions(p,a,b,c,d,e) (p)->EnumSessions(a,b,c,d,e) +#define IDirectPlay3_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirectPlay3_GetMessageCount(p,a,b) (p)->GetMessageCount(a,b) +#define IDirectPlay3_GetGroupData(p,a,b,c,d) (p)->GetGroupData(a,b,c,d) +#define IDirectPlay3_GetGroupName(p,a,b,c) (p)->GetGroupName(a,b,c) +#define IDirectPlay3_GetPlayerAddress(p,a,b,c) (p)->GetPlayerAddress(a,b,c) +#define IDirectPlay3_GetPlayerCaps(p,a,b,c) (p)->GetPlayerCaps(a,b,c) +#define IDirectPlay3_GetPlayerData(p,a,b,c,d) (p)->GetPlayerData(a,b,c,d) +#define IDirectPlay3_GetPlayerName(p,a,b,c) (p)->GetPlayerName(a,b,c) +#define IDirectPlay3_GetSessionDesc(p,a,b) (p)->GetSessionDesc(a,b) +#define IDirectPlay3_Initialize(p,a) (p)->Initialize(a) +#define IDirectPlay3_Open(p,a,b) (p)->Open(a,b) +#define IDirectPlay3_Receive(p,a,b,c,d,e) (p)->Receive(a,b,c,d,e) +#define IDirectPlay3_Send(p,a,b,c,d,e) (p)->Send(a,b,c,d,e) +#define IDirectPlay3_SetGroupData(p,a,b,c,d) (p)->SetGroupData(a,b,c,d) +#define IDirectPlay3_SetGroupName(p,a,b,c) (p)->SetGroupName(a,b,c) +#define IDirectPlay3_SetPlayerData(p,a,b,c,d) (p)->SetPlayerData(a,b,c,d) +#define IDirectPlay3_SetPlayerName(p,a,b,c) (p)->SetPlayerName(a,b,c) +#define IDirectPlay3_SetSessionDesc(p,a,b) (p)->SetSessionDesc(a,b) +#define IDirectPlay3_AddGroupToGroup(p,a,b) (p)->AddGroupToGroup(a,b) +#define IDirectPlay3_CreateGroupInGroup(p,a,b,c,d,e,f) (p)->CreateGroupInGroup(a,b,c,d,e,f) +#define IDirectPlay3_DeleteGroupFromGroup(p,a,b) (p)->DeleteGroupFromGroup(a,b) +#define IDirectPlay3_EnumConnections(p,a,b,c,d) (p)->EnumConnections(a,b,c,d) +#define IDirectPlay3_EnumGroupsInGroup(p,a,b,c,d,e) (p)->EnumGroupsInGroup(a,b,c,d,e) +#define IDirectPlay3_GetGroupConnectionSettings(p,a,b,c,d) (p)->GetGroupConnectionSettings(a,b,c,d) +#define IDirectPlay3_InitializeConnection(p,a,b) (p)->InitializeConnection(a,b) +#define IDirectPlay3_SecureOpen(p,a,b,c,d) (p)->SecureOpen(a,b,c,d) +#define IDirectPlay3_SendChatMessage(p,a,b,c,d) (p)->SendChatMessage(a,b,c,d) +#define IDirectPlay3_SetGroupConnectionSettings(p,a,b,c) (p)->SetGroupConnectionSettings(a,b,c) +#define IDirectPlay3_StartSession(p,a,b) (p)->StartSession(a,b) +#define IDirectPlay3_GetGroupFlags(p,a,b) (p)->GetGroupFlags(a,b) +#define IDirectPlay3_GetGroupParent(p,a,b) (p)->GetGroupParent(a,b) +#define IDirectPlay3_GetPlayerAccount(p,a,b,c,d) (p)->GetPlayerAccount(a,b,c,d) +#define IDirectPlay3_GetPlayerFlags(p,a,b) (p)->GetPlayerFlags(a,b) + +#endif + +/**************************************************************************** + * + * IDirectPlay4 (and IDirectPlay4A) Interface + * + ****************************************************************************/ + +#undef INTERFACE +#define INTERFACE IDirectPlay4 +DECLARE_INTERFACE_( IDirectPlay4, IDirectPlay3 ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay2 methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS,DWORD) PURE; + STDMETHOD(GetGroupData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetGroupName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerAddress) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID,LPDPCAPS,DWORD) PURE; + STDMETHOD(GetPlayerData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetSessionDesc) (THIS_ LPVOID,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetGroupData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetGroupName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetPlayerData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetSessionDesc) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + /*** IDirectPlay3 methods ***/ + STDMETHOD(AddGroupToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(CreateGroupInGroup) (THIS_ DPID,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeleteGroupFromGroup) (THIS_ DPID,DPID) PURE; + STDMETHOD(EnumConnections) (THIS_ LPCGUID,LPDPENUMCONNECTIONSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroupsInGroup) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetGroupConnectionSettings)(THIS_ DWORD, DPID, LPVOID, LPDWORD) PURE; + STDMETHOD(InitializeConnection) (THIS_ LPVOID,DWORD) PURE; + STDMETHOD(SecureOpen) (THIS_ LPCDPSESSIONDESC2,DWORD,LPCDPSECURITYDESC,LPCDPCREDENTIALS) PURE; + STDMETHOD(SendChatMessage) (THIS_ DPID,DPID,DWORD,LPDPCHAT) PURE; + STDMETHOD(SetGroupConnectionSettings)(THIS_ DWORD,DPID,LPDPLCONNECTION) PURE; + STDMETHOD(StartSession) (THIS_ DWORD,DPID) PURE; + STDMETHOD(GetGroupFlags) (THIS_ DPID,LPDWORD) PURE; + STDMETHOD(GetGroupParent) (THIS_ DPID,LPDPID) PURE; + STDMETHOD(GetPlayerAccount) (THIS_ DPID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(GetPlayerFlags) (THIS_ DPID,LPDWORD) PURE; + /*** IDirectPlay4 methods ***/ + STDMETHOD(GetGroupOwner) (THIS_ DPID, LPDPID) PURE; + STDMETHOD(SetGroupOwner) (THIS_ DPID, DPID) PURE; + STDMETHOD(SendEx) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD, DWORD, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(GetMessageQueue) (THIS_ DPID, DPID, DWORD, LPDWORD, LPDWORD) PURE; + STDMETHOD(CancelMessage) (THIS_ DWORD, DWORD) PURE; + STDMETHOD(CancelPriority) (THIS_ DWORD, DWORD, DWORD) PURE; +}; + +/**************************************************************************** + * + * IDirectPlayX interface macros (for IDirectPlay4 and beyond) + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlayX_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlayX_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlayX_Release(p) (p)->lpVtbl->Release(p) +#define IDirectPlayX_AddPlayerToGroup(p,a,b) (p)->lpVtbl->AddPlayerToGroup(p,a,b) +#define IDirectPlayX_CancelMessage(p,a,b) (p)->lpVtbl->CancelMessage(p,a,b) +#define IDirectPlayX_CancelPriority(p,a,b,c) (p)->lpVtbl->CancelPriority(p,a,b,c) +#define IDirectPlayX_Close(p) (p)->lpVtbl->Close(p) +#define IDirectPlayX_CreateGroup(p,a,b,c,d,e) (p)->lpVtbl->CreateGroup(p,a,b,c,d,e) +#define IDirectPlayX_CreatePlayer(p,a,b,c,d,e,f) (p)->lpVtbl->CreatePlayer(p,a,b,c,d,e,f) +#define IDirectPlayX_DeletePlayerFromGroup(p,a,b) (p)->lpVtbl->DeletePlayerFromGroup(p,a,b) +#define IDirectPlayX_DestroyGroup(p,a) (p)->lpVtbl->DestroyGroup(p,a) +#define IDirectPlayX_DestroyPlayer(p,a) (p)->lpVtbl->DestroyPlayer(p,a) +#define IDirectPlayX_EnumGroupPlayers(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupPlayers(p,a,b,c,d,e) +#define IDirectPlayX_EnumGroups(p,a,b,c,d) (p)->lpVtbl->EnumGroups(p,a,b,c,d) +#define IDirectPlayX_EnumPlayers(p,a,b,c,d) (p)->lpVtbl->EnumPlayers(p,a,b,c,d) +#define IDirectPlayX_EnumSessions(p,a,b,c,d,e) (p)->lpVtbl->EnumSessions(p,a,b,c,d,e) +#define IDirectPlayX_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirectPlayX_GetMessageCount(p,a,b) (p)->lpVtbl->GetMessageCount(p,a,b) +#define IDirectPlayX_GetMessageQueue(p,a,b,c,d,e) (p)->lpVtbl->GetMessageQueue(p,a,b,c,d,e) +#define IDirectPlayX_GetGroupData(p,a,b,c,d) (p)->lpVtbl->GetGroupData(p,a,b,c,d) +#define IDirectPlayX_GetGroupName(p,a,b,c) (p)->lpVtbl->GetGroupName(p,a,b,c) +#define IDirectPlayX_GetPlayerAddress(p,a,b,c) (p)->lpVtbl->GetPlayerAddress(p,a,b,c) +#define IDirectPlayX_GetPlayerCaps(p,a,b,c) (p)->lpVtbl->GetPlayerCaps(p,a,b,c) +#define IDirectPlayX_GetPlayerData(p,a,b,c,d) (p)->lpVtbl->GetPlayerData(p,a,b,c,d) +#define IDirectPlayX_GetPlayerName(p,a,b,c) (p)->lpVtbl->GetPlayerName(p,a,b,c) +#define IDirectPlayX_GetSessionDesc(p,a,b) (p)->lpVtbl->GetSessionDesc(p,a,b) +#define IDirectPlayX_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirectPlayX_Open(p,a,b) (p)->lpVtbl->Open(p,a,b) +#define IDirectPlayX_Receive(p,a,b,c,d,e) (p)->lpVtbl->Receive(p,a,b,c,d,e) +#define IDirectPlayX_Send(p,a,b,c,d,e) (p)->lpVtbl->Send(p,a,b,c,d,e) +#define IDirectPlayX_SendEx(p,a,b,c,d,e,f,g,h,i) (p)->lpVtbl->SendEx(p,a,b,c,d,e,f,g,h,i) +#define IDirectPlayX_SetGroupData(p,a,b,c,d) (p)->lpVtbl->SetGroupData(p,a,b,c,d) +#define IDirectPlayX_SetGroupName(p,a,b,c) (p)->lpVtbl->SetGroupName(p,a,b,c) +#define IDirectPlayX_SetPlayerData(p,a,b,c,d) (p)->lpVtbl->SetPlayerData(p,a,b,c,d) +#define IDirectPlayX_SetPlayerName(p,a,b,c) (p)->lpVtbl->SetPlayerName(p,a,b,c) +#define IDirectPlayX_SetSessionDesc(p,a,b) (p)->lpVtbl->SetSessionDesc(p,a,b) +#define IDirectPlayX_AddGroupToGroup(p,a,b) (p)->lpVtbl->AddGroupToGroup(p,a,b) +#define IDirectPlayX_CreateGroupInGroup(p,a,b,c,d,e,f) (p)->lpVtbl->CreateGroupInGroup(p,a,b,c,d,e,f) +#define IDirectPlayX_DeleteGroupFromGroup(p,a,b) (p)->lpVtbl->DeleteGroupFromGroup(p,a,b) +#define IDirectPlayX_EnumConnections(p,a,b,c,d) (p)->lpVtbl->EnumConnections(p,a,b,c,d) +#define IDirectPlayX_EnumGroupsInGroup(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupsInGroup(p,a,b,c,d,e) +#define IDirectPlayX_GetGroupConnectionSettings(p,a,b,c,d) (p)->lpVtbl->GetGroupConnectionSettings(p,a,b,c,d) +#define IDirectPlayX_InitializeConnection(p,a,b) (p)->lpVtbl->InitializeConnection(p,a,b) +#define IDirectPlayX_SecureOpen(p,a,b,c,d) (p)->lpVtbl->SecureOpen(p,a,b,c,d) +#define IDirectPlayX_SendChatMessage(p,a,b,c,d) (p)->lpVtbl->SendChatMessage(p,a,b,c,d) +#define IDirectPlayX_SetGroupConnectionSettings(p,a,b,c) (p)->lpVtbl->SetGroupConnectionSettings(p,a,b,c) +#define IDirectPlayX_StartSession(p,a,b) (p)->lpVtbl->StartSession(p,a,b) +#define IDirectPlayX_GetGroupFlags(p,a,b) (p)->lpVtbl->GetGroupFlags(p,a,b) +#define IDirectPlayX_GetGroupParent(p,a,b) (p)->lpVtbl->GetGroupParent(p,a,b) +#define IDirectPlayX_GetPlayerAccount(p,a,b,c,d) (p)->lpVtbl->GetPlayerAccount(p,a,b,c,d) +#define IDirectPlayX_GetPlayerFlags(p,a,b) (p)->lpVtbl->GetPlayerFlags(p,a,b) +#define IDirectPlayX_GetGroupOwner(p,a,b) (p)->lpVtbl->GetGroupOwner(p,a,b) +#define IDirectPlayX_SetGroupOwner(p,a,b) (p)->lpVtbl->SetGroupOwner(p,a,b) + +#else /* C++ */ + +#define IDirectPlayX_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlayX_AddRef(p) (p)->AddRef() +#define IDirectPlayX_Release(p) (p)->Release() +#define IDirectPlayX_AddPlayerToGroup(p,a,b) (p)->AddPlayerToGroup(a,b) +#define IDirectPlayX_CancelMessage(p,a,b) (p)->CancelMessage(a,b) +#define IDirectPlayX_CancelPriority(p,a,b,c) (p)->CancelPriority(a,b,c) +#define IDirectPlayX_Close(p) (p)->Close() +#define IDirectPlayX_CreateGroup(p,a,b,c,d,e) (p)->CreateGroup(a,b,c,d,e) +#define IDirectPlayX_CreatePlayer(p,a,b,c,d,e,f) (p)->CreatePlayer(a,b,c,d,e,f) +#define IDirectPlayX_DeletePlayerFromGroup(p,a,b) (p)->DeletePlayerFromGroup(a,b) +#define IDirectPlayX_DestroyGroup(p,a) (p)->DestroyGroup(a) +#define IDirectPlayX_DestroyPlayer(p,a) (p)->DestroyPlayer(a) +#define IDirectPlayX_EnumGroupPlayers(p,a,b,c,d,e) (p)->EnumGroupPlayers(a,b,c,d,e) +#define IDirectPlayX_EnumGroups(p,a,b,c,d) (p)->EnumGroups(a,b,c,d) +#define IDirectPlayX_EnumPlayers(p,a,b,c,d) (p)->EnumPlayers(a,b,c,d) +#define IDirectPlayX_EnumSessions(p,a,b,c,d,e) (p)->EnumSessions(a,b,c,d,e) +#define IDirectPlayX_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirectPlayX_GetMessageCount(p,a,b) (p)->GetMessageCount(a,b) +#define IDirectPlayX_GetMessageQueue(p,a,b,c,d,e) (p)->GetMessageQueue(a,b,c,d,e) +#define IDirectPlayX_GetGroupData(p,a,b,c,d) (p)->GetGroupData(a,b,c,d) +#define IDirectPlayX_GetGroupName(p,a,b,c) (p)->GetGroupName(a,b,c) +#define IDirectPlayX_GetPlayerAddress(p,a,b,c) (p)->GetPlayerAddress(a,b,c) +#define IDirectPlayX_GetPlayerCaps(p,a,b,c) (p)->GetPlayerCaps(a,b,c) +#define IDirectPlayX_GetPlayerData(p,a,b,c,d) (p)->GetPlayerData(a,b,c,d) +#define IDirectPlayX_GetPlayerName(p,a,b,c) (p)->GetPlayerName(a,b,c) +#define IDirectPlayX_GetSessionDesc(p,a,b) (p)->GetSessionDesc(a,b) +#define IDirectPlayX_Initialize(p,a) (p)->Initialize(a) +#define IDirectPlayX_Open(p,a,b) (p)->Open(a,b) +#define IDirectPlayX_Receive(p,a,b,c,d,e) (p)->Receive(a,b,c,d,e) +#define IDirectPlayX_Send(p,a,b,c,d,e) (p)->Send(a,b,c,d,e) +#define IDirectPlayX_SendEx(p,a,b,c,d,e,f,g,h,i) (p)->SendEx(a,b,c,d,e,f,g,h,i) +#define IDirectPlayX_SetGroupData(p,a,b,c,d) (p)->SetGroupData(a,b,c,d) +#define IDirectPlayX_SetGroupName(p,a,b,c) (p)->SetGroupName(a,b,c) +#define IDirectPlayX_SetPlayerData(p,a,b,c,d) (p)->SetPlayerData(a,b,c,d) +#define IDirectPlayX_SetPlayerName(p,a,b,c) (p)->SetPlayerName(a,b,c) +#define IDirectPlayX_SetSessionDesc(p,a,b) (p)->SetSessionDesc(a,b) +#define IDirectPlayX_AddGroupToGroup(p,a,b) (p)->AddGroupToGroup(a,b) +#define IDirectPlayX_CreateGroupInGroup(p,a,b,c,d,e,f) (p)->CreateGroupInGroup(a,b,c,d,e,f) +#define IDirectPlayX_DeleteGroupFromGroup(p,a,b) (p)->DeleteGroupFromGroup(a,b) +#define IDirectPlayX_EnumConnections(p,a,b,c,d) (p)->EnumConnections(a,b,c,d) +#define IDirectPlayX_EnumGroupsInGroup(p,a,b,c,d,e) (p)->EnumGroupsInGroup(a,b,c,d,e) +#define IDirectPlayX_GetGroupConnectionSettings(p,a,b,c,d) (p)->GetGroupConnectionSettings(a,b,c,d) +#define IDirectPlayX_InitializeConnection(p,a,b) (p)->InitializeConnection(a,b) +#define IDirectPlayX_SecureOpen(p,a,b,c,d) (p)->SecureOpen(a,b,c,d) +#define IDirectPlayX_SendChatMessage(p,a,b,c,d) (p)->SendChatMessage(a,b,c,d) +#define IDirectPlayX_SetGroupConnectionSettings(p,a,b,c) (p)->SetGroupConnectionSettings(a,b,c) +#define IDirectPlayX_StartSession(p,a,b) (p)->StartSession(a,b) +#define IDirectPlayX_GetGroupFlags(p,a,b) (p)->GetGroupFlags(a,b) +#define IDirectPlayX_GetGroupParent(p,a,b) (p)->GetGroupParent(a,b) +#define IDirectPlayX_GetPlayerAccount(p,a,b,c,d) (p)->GetPlayerAccount(a,b,c,d) +#define IDirectPlayX_GetPlayerFlags(p,a,b) (p)->GetPlayerFlags(a,b) +#define IDirectPlayX_GetGroupOwner(p,a,b) (p)->GetGroupOwner(a,b) +#define IDirectPlayX_SetGroupOwner(p,a,b) (p)->SetGroupOwner(a,b) + +#endif + +/**************************************************************************** + * + * EnumConnections API flags + * + ****************************************************************************/ + +/* + * Enumerate Service Providers + */ +#define DPCONNECTION_DIRECTPLAY 0x00000001 + +/* + * Enumerate Lobby Providers + */ +#define DPCONNECTION_DIRECTPLAYLOBBY 0x00000002 + + +/**************************************************************************** + * + * EnumPlayers API flags + * + ****************************************************************************/ + +/* + * Enumerate all players in the current session + */ +#define DPENUMPLAYERS_ALL 0x00000000 +#define DPENUMGROUPS_ALL DPENUMPLAYERS_ALL + + +/* + * Enumerate only local (created by this application) players + * or groups + */ +#define DPENUMPLAYERS_LOCAL 0x00000008 +#define DPENUMGROUPS_LOCAL DPENUMPLAYERS_LOCAL + +/* + * Enumerate only remote (non-local) players + * or groups + */ +#define DPENUMPLAYERS_REMOTE 0x00000010 +#define DPENUMGROUPS_REMOTE DPENUMPLAYERS_REMOTE + +/* + * Enumerate groups along with the players + */ +#define DPENUMPLAYERS_GROUP 0x00000020 + +/* + * Enumerate players or groups in another session + * (must supply lpguidInstance) + */ +#define DPENUMPLAYERS_SESSION 0x00000080 +#define DPENUMGROUPS_SESSION DPENUMPLAYERS_SESSION + +/* + * Enumerate server players + */ +#define DPENUMPLAYERS_SERVERPLAYER 0x00000100 + +/* + * Enumerate spectator players + */ +#define DPENUMPLAYERS_SPECTATOR 0x00000200 + +/* + * Enumerate shortcut groups + */ +#define DPENUMGROUPS_SHORTCUT 0x00000400 + +/* + * Enumerate staging area groups + */ +#define DPENUMGROUPS_STAGINGAREA 0x00000800 + +/* + * Enumerate hidden groups + */ +#define DPENUMGROUPS_HIDDEN 0x00001000 + +/* + * Enumerate the group's owner + */ +#define DPENUMPLAYERS_OWNER 0x00002000 + + +/**************************************************************************** + * + * CreatePlayer API flags + * + ****************************************************************************/ + +/* + * This flag indicates that this player should be designated + * the server player. The app should specify this at CreatePlayer. + */ +#define DPPLAYER_SERVERPLAYER DPENUMPLAYERS_SERVERPLAYER + +/* + * This flag indicates that this player should be designated + * a spectator. The app should specify this at CreatePlayer. + */ +#define DPPLAYER_SPECTATOR DPENUMPLAYERS_SPECTATOR + +/* + * This flag indicates that this player was created locally. + * (returned from GetPlayerFlags) + */ +#define DPPLAYER_LOCAL DPENUMPLAYERS_LOCAL + +/* + * This flag indicates that this player is the group's owner + * (Only returned in EnumGroupPlayers) + */ +#define DPPLAYER_OWNER DPENUMPLAYERS_OWNER + +/**************************************************************************** + * + * CreateGroup API flags + * + ****************************************************************************/ + + +/* + * This flag indicates that the StartSession can be called on the group. + * The app should specify this at CreateGroup, or CreateGroupInGroup. + */ +#define DPGROUP_STAGINGAREA DPENUMGROUPS_STAGINGAREA + +/* + * This flag indicates that this group was created locally. + * (returned from GetGroupFlags) + */ +#define DPGROUP_LOCAL DPENUMGROUPS_LOCAL + +/* + * This flag indicates that this group was created hidden. + */ +#define DPGROUP_HIDDEN DPENUMGROUPS_HIDDEN + + +/**************************************************************************** + * + * EnumSessions API flags + * + ****************************************************************************/ + +/* + * Enumerate sessions which can be joined + */ +#define DPENUMSESSIONS_AVAILABLE 0x00000001 + +/* + * Enumerate all sessions even if they can't be joined. + */ +#define DPENUMSESSIONS_ALL 0x00000002 + + + + +/* + * Start an asynchronous enum sessions + */ + #define DPENUMSESSIONS_ASYNC 0x00000010 + +/* + * Stop an asynchronous enum sessions + */ + #define DPENUMSESSIONS_STOPASYNC 0x00000020 + +/* + * Enumerate sessions even if they require a password + */ + #define DPENUMSESSIONS_PASSWORDREQUIRED 0x00000040 + +/* + * Return status about progress of enumeration instead of + * showing any status dialogs. + */ + #define DPENUMSESSIONS_RETURNSTATUS 0x00000080 + +/**************************************************************************** + * + * GetCaps and GetPlayerCaps API flags + * + ****************************************************************************/ + +/* + * The latency returned should be for guaranteed message sending. + * Default is non-guaranteed messaging. + */ +#define DPGETCAPS_GUARANTEED 0x00000001 + + +/**************************************************************************** + * + * GetGroupData, GetPlayerData API flags + * Remote and local Group/Player data is maintained separately. + * Default is DPGET_REMOTE. + * + ****************************************************************************/ + +/* + * Get the remote data (set by any DirectPlay object in + * the session using DPSET_REMOTE) + */ +#define DPGET_REMOTE 0x00000000 + +/* + * Get the local data (set by this DirectPlay object + * using DPSET_LOCAL) + */ +#define DPGET_LOCAL 0x00000001 + + +/**************************************************************************** + * + * Open API flags + * + ****************************************************************************/ + +/* + * Join the session that is described by the DPSESSIONDESC2 structure + */ +#define DPOPEN_JOIN 0x00000001 + +/* + * Create a new session as described by the DPSESSIONDESC2 structure + */ +#define DPOPEN_CREATE 0x00000002 + +/* + * Return status about progress of open instead of showing + * any status dialogs. + */ + #define DPOPEN_RETURNSTATUS DPENUMSESSIONS_RETURNSTATUS + + + +/**************************************************************************** + * + * DPLCONNECTION flags + * + ****************************************************************************/ + +/* + * This application should create a new session as + * described by the DPSESIONDESC structure + */ +#define DPLCONNECTION_CREATESESSION DPOPEN_CREATE + +/* + * This application should join the session described by + * the DPSESIONDESC structure with the lpAddress data + */ +#define DPLCONNECTION_JOINSESSION DPOPEN_JOIN + +/**************************************************************************** + * + * Receive API flags + * Default is DPRECEIVE_ALL + * + ****************************************************************************/ + +/* + * Get the first message in the queue + */ +#define DPRECEIVE_ALL 0x00000001 + +/* + * Get the first message in the queue directed to a specific player + */ +#define DPRECEIVE_TOPLAYER 0x00000002 + +/* + * Get the first message in the queue from a specific player + */ +#define DPRECEIVE_FROMPLAYER 0x00000004 + +/* + * Get the message but don't remove it from the queue + */ +#define DPRECEIVE_PEEK 0x00000008 + + +/**************************************************************************** + * + * Send API flags + * + ****************************************************************************/ + +/* + * Send the message using a guaranteed send method. + * Default is non-guaranteed. + */ +#define DPSEND_GUARANTEED 0x00000001 + + +/* + * This flag is obsolete. It is ignored by DirectPlay + */ +#define DPSEND_HIGHPRIORITY 0x00000002 + +/* + * This flag is obsolete. It is ignored by DirectPlay + */ +#define DPSEND_OPENSTREAM 0x00000008 + +/* + * This flag is obsolete. It is ignored by DirectPlay + */ +#define DPSEND_CLOSESTREAM 0x00000010 + +/* + * Send the message digitally signed to ensure authenticity. + */ +#define DPSEND_SIGNED 0x00000020 + +/* + * Send the message with encryption to ensure privacy. + */ +#define DPSEND_ENCRYPTED 0x00000040 + +/* + * The message is a lobby system message + */ +#define DPSEND_LOBBYSYSTEMMESSAGE 0x00000080 + + +/* + * Send message asynchronously, must check caps + * before using this flag. It is always provided + * if the protocol flag is set. + */ +#define DPSEND_ASYNC 0x00000200 + +/* + * When an message is completed, don't tell me. + * by default the application is notified with a system message. + */ +#define DPSEND_NOSENDCOMPLETEMSG 0x00000400 + + +/* + * Maximum priority for sends available to applications + */ +#define DPSEND_MAX_PRI 0x0000FFFF +#define DPSEND_MAX_PRIORITY DPSEND_MAX_PRI + + +/**************************************************************************** + * + * SetGroupData, SetGroupName, SetPlayerData, SetPlayerName, + * SetSessionDesc API flags. + * Default is DPSET_REMOTE. + * + ****************************************************************************/ + +/* + * Propagate the data to all players in the session + */ +#define DPSET_REMOTE 0x00000000 + +/* + * Do not propagate the data to other players + */ +#define DPSET_LOCAL 0x00000001 + +/* + * Used with DPSET_REMOTE, use guaranteed message send to + * propagate the data + */ +#define DPSET_GUARANTEED 0x00000002 + +/**************************************************************************** + * + * GetMessageQueue API flags. + * Default is DPMESSAGEQUEUE_SEND + * + ****************************************************************************/ + +/* + * Get Send Queue - requires Service Provider Support + */ +#define DPMESSAGEQUEUE_SEND 0x00000001 + +/* + * Get Receive Queue + */ +#define DPMESSAGEQUEUE_RECEIVE 0x00000002 + + +/**************************************************************************** + * + * Connect API flags + * + ****************************************************************************/ + + +/* + * Start an asynchronous connect which returns status codes + */ +#define DPCONNECT_RETURNSTATUS (DPENUMSESSIONS_RETURNSTATUS) + + +/**************************************************************************** + * + * DirectPlay system messages and message data structures + * + * All system message come 'From' player DPID_SYSMSG. To determine what type + * of message it is, cast the lpData from Receive to DPMSG_GENERIC and check + * the dwType member against one of the following DPSYS_xxx constants. Once + * a match is found, cast the lpData to the corresponding of the DPMSG_xxx + * structures to access the data of the message. + * + ****************************************************************************/ + +/* + * A new player or group has been created in the session + * Use DPMSG_CREATEPLAYERORGROUP. Check dwPlayerType to see if it + * is a player or a group. + */ +#define DPSYS_CREATEPLAYERORGROUP 0x0003 + +/* + * A player has been deleted from the session + * Use DPMSG_DESTROYPLAYERORGROUP + */ +#define DPSYS_DESTROYPLAYERORGROUP 0x0005 + +/* + * A player has been added to a group + * Use DPMSG_ADDPLAYERTOGROUP + */ +#define DPSYS_ADDPLAYERTOGROUP 0x0007 + +/* + * A player has been removed from a group + * Use DPMSG_DELETEPLAYERFROMGROUP + */ +#define DPSYS_DELETEPLAYERFROMGROUP 0x0021 + +/* + * This DirectPlay object lost its connection with all the + * other players in the session. + * Use DPMSG_SESSIONLOST. + */ +#define DPSYS_SESSIONLOST 0x0031 + +/* + * The current host has left the session. + * This DirectPlay object is now the host. + * Use DPMSG_HOST. + */ +#define DPSYS_HOST 0x0101 + +/* + * The remote data associated with a player or + * group has changed. Check dwPlayerType to see + * if it is a player or a group + * Use DPMSG_SETPLAYERORGROUPDATA + */ +#define DPSYS_SETPLAYERORGROUPDATA 0x0102 + +/* + * The name of a player or group has changed. + * Check dwPlayerType to see if it is a player + * or a group. + * Use DPMSG_SETPLAYERORGROUPNAME + */ +#define DPSYS_SETPLAYERORGROUPNAME 0x0103 + +/* + * The session description has changed. + * Use DPMSG_SETSESSIONDESC + */ +#define DPSYS_SETSESSIONDESC 0x0104 + +/* + * A group has been added to a group + * Use DPMSG_ADDGROUPTOGROUP + */ +#define DPSYS_ADDGROUPTOGROUP 0x0105 + +/* + * A group has been removed from a group + * Use DPMSG_DELETEGROUPFROMGROUP + */ +#define DPSYS_DELETEGROUPFROMGROUP 0x0106 + +/* + * A secure player-player message has arrived. + * Use DPMSG_SECUREMESSAGE + */ +#define DPSYS_SECUREMESSAGE 0x0107 + +/* + * Start a new session. + * Use DPMSG_STARTSESSION + */ +#define DPSYS_STARTSESSION 0x0108 + +/* + * A chat message has arrived + * Use DPMSG_CHAT + */ +#define DPSYS_CHAT 0x0109 + +/* + * The owner of a group has changed + * Use DPMSG_SETGROUPOWNER + */ +#define DPSYS_SETGROUPOWNER 0x010A + +/* + * An async send has finished, failed or been cancelled + * Use DPMSG_SENDCOMPLETE + */ +#define DPSYS_SENDCOMPLETE 0x010d + + +/* + * Used in the dwPlayerType field to indicate if it applies to a group + * or a player + */ +#define DPPLAYERTYPE_GROUP 0x00000000 +#define DPPLAYERTYPE_PLAYER 0x00000001 + + +/* + * DPMSG_GENERIC + * Generic message structure used to identify the message type. + */ +typedef struct +{ + DWORD dwType; // Message type +} DPMSG_GENERIC, FAR *LPDPMSG_GENERIC; + +/* + * DPMSG_CREATEPLAYERORGROUP + * System message generated when a new player or group + * created in the session with information about it. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // ID of the player or group + DWORD dwCurrentPlayers; // current # players & groups in session + LPVOID lpData; // pointer to remote data + DWORD dwDataSize; // size of remote data + DPNAME dpnName; // structure with name info + // the following fields are only available when using + // the IDirectPlay3 interface or greater + DPID dpIdParent; // id of parent group + DWORD dwFlags; // player or group flags +} DPMSG_CREATEPLAYERORGROUP, FAR *LPDPMSG_CREATEPLAYERORGROUP; + +/* + * DPMSG_DESTROYPLAYERORGROUP + * System message generated when a player or group is being + * destroyed in the session with information about it. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // player ID being deleted + LPVOID lpLocalData; // copy of players local data + DWORD dwLocalDataSize; // sizeof local data + LPVOID lpRemoteData; // copy of players remote data + DWORD dwRemoteDataSize; // sizeof remote data + // the following fields are only available when using + // the IDirectPlay3 interface or greater + DPNAME dpnName; // structure with name info + DPID dpIdParent; // id of parent group + DWORD dwFlags; // player or group flags +} DPMSG_DESTROYPLAYERORGROUP, FAR *LPDPMSG_DESTROYPLAYERORGROUP; + +/* + * DPMSG_ADDPLAYERTOGROUP + * System message generated when a player is being added + * to a group. + */ +typedef struct +{ + DWORD dwType; // Message type + DPID dpIdGroup; // group ID being added to + DPID dpIdPlayer; // player ID being added +} DPMSG_ADDPLAYERTOGROUP, FAR *LPDPMSG_ADDPLAYERTOGROUP; + +/* + * DPMSG_DELETEPLAYERFROMGROUP + * System message generated when a player is being + * removed from a group + */ +typedef DPMSG_ADDPLAYERTOGROUP DPMSG_DELETEPLAYERFROMGROUP; +typedef DPMSG_DELETEPLAYERFROMGROUP FAR *LPDPMSG_DELETEPLAYERFROMGROUP; + +/* + * DPMSG_ADDGROUPTOGROUP + * System message generated when a group is being added + * to a group. + */ +typedef struct +{ + DWORD dwType; // Message type + DPID dpIdParentGroup; // group ID being added to + DPID dpIdGroup; // group ID being added +} DPMSG_ADDGROUPTOGROUP, FAR *LPDPMSG_ADDGROUPTOGROUP; + +/* + * DPMSG_DELETEGROUPFROMGROUP + * System message generated when a GROUP is being + * removed from a group + */ +typedef DPMSG_ADDGROUPTOGROUP DPMSG_DELETEGROUPFROMGROUP; +typedef DPMSG_DELETEGROUPFROMGROUP FAR *LPDPMSG_DELETEGROUPFROMGROUP; + +/* + * DPMSG_SETPLAYERORGROUPDATA + * System message generated when remote data for a player or + * group has changed. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // ID of player or group + LPVOID lpData; // pointer to remote data + DWORD dwDataSize; // size of remote data +} DPMSG_SETPLAYERORGROUPDATA, FAR *LPDPMSG_SETPLAYERORGROUPDATA; + +/* + * DPMSG_SETPLAYERORGROUPNAME + * System message generated when the name of a player or + * group has changed. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // ID of player or group + DPNAME dpnName; // structure with new name info +} DPMSG_SETPLAYERORGROUPNAME, FAR *LPDPMSG_SETPLAYERORGROUPNAME; + +/* + * DPMSG_SETSESSIONDESC + * System message generated when session desc has changed + */ +typedef struct +{ + DWORD dwType; // Message type + DPSESSIONDESC2 dpDesc; // Session desc +} DPMSG_SETSESSIONDESC, FAR *LPDPMSG_SETSESSIONDESC; + +/* + * DPMSG_HOST + * System message generated when the host has migrated to this + * DirectPlay object. + * + */ +typedef DPMSG_GENERIC DPMSG_HOST; +typedef DPMSG_HOST FAR *LPDPMSG_HOST; + +/* + * DPMSG_SESSIONLOST + * System message generated when the connection to the session is lost. + * + */ +typedef DPMSG_GENERIC DPMSG_SESSIONLOST; +typedef DPMSG_SESSIONLOST FAR *LPDPMSG_SESSIONLOST; + +/* + * DPMSG_SECUREMESSAGE + * System message generated when a player requests a secure send + */ +typedef struct +{ + DWORD dwType; // Message Type + DWORD dwFlags; // Signed/Encrypted + DPID dpIdFrom; // ID of Sending Player + LPVOID lpData; // Player message + DWORD dwDataSize; // Size of player message +} DPMSG_SECUREMESSAGE, FAR *LPDPMSG_SECUREMESSAGE; + +/* + * DPMSG_STARTSESSION + * System message containing all information required to + * start a new session + */ +typedef struct +{ + DWORD dwType; // Message type + LPDPLCONNECTION lpConn; // DPLCONNECTION structure +} DPMSG_STARTSESSION, FAR *LPDPMSG_STARTSESSION; + +/* + * DPMSG_CHAT + * System message containing a chat message + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwFlags; // Message flags + DPID idFromPlayer; // ID of the Sending Player + DPID idToPlayer; // ID of the To Player + DPID idToGroup; // ID of the To Group + LPDPCHAT lpChat; // Pointer to a structure containing the chat message +} DPMSG_CHAT, FAR *LPDPMSG_CHAT; + +/* + * DPMSG_SETGROUPOWNER + * System message generated when the owner of a group has changed + */ +typedef struct +{ + DWORD dwType; // Message type + DPID idGroup; // ID of the group + DPID idNewOwner; // ID of the player that is the new owner + DPID idOldOwner; // ID of the player that used to be the owner +} DPMSG_SETGROUPOWNER, FAR *LPDPMSG_SETGROUPOWNER; + +/* + * DPMSG_SENDCOMPLETE + * System message generated when finished with an Async Send message + * + * NOTE SENDPARMS has an overlay for DPMSG_SENDCOMPLETE, don't + * change this message w/o changing SENDPARMS. + */ +typedef struct +{ + DWORD dwType; + DPID idFrom; + DPID idTo; + DWORD dwFlags; + DWORD dwPriority; + DWORD dwTimeout; + LPVOID lpvContext; + DWORD dwMsgID; + HRESULT hr; + DWORD dwSendTime; +} DPMSG_SENDCOMPLETE, *LPDPMSG_SENDCOMPLETE; + +/**************************************************************************** + * + * DIRECTPLAY ERRORS + * + * Errors are represented by negative values and cannot be combined. + * + ****************************************************************************/ +#define DP_OK S_OK +#define DPERR_ALREADYINITIALIZED MAKE_DPHRESULT( 5 ) +#define DPERR_ACCESSDENIED MAKE_DPHRESULT( 10 ) +#define DPERR_ACTIVEPLAYERS MAKE_DPHRESULT( 20 ) +#define DPERR_BUFFERTOOSMALL MAKE_DPHRESULT( 30 ) +#define DPERR_CANTADDPLAYER MAKE_DPHRESULT( 40 ) +#define DPERR_CANTCREATEGROUP MAKE_DPHRESULT( 50 ) +#define DPERR_CANTCREATEPLAYER MAKE_DPHRESULT( 60 ) +#define DPERR_CANTCREATESESSION MAKE_DPHRESULT( 70 ) +#define DPERR_CAPSNOTAVAILABLEYET MAKE_DPHRESULT( 80 ) +#define DPERR_EXCEPTION MAKE_DPHRESULT( 90 ) +#define DPERR_GENERIC E_FAIL +#define DPERR_INVALIDFLAGS MAKE_DPHRESULT( 120 ) +#define DPERR_INVALIDOBJECT MAKE_DPHRESULT( 130 ) +#define DPERR_INVALIDPARAM E_INVALIDARG +#define DPERR_INVALIDPARAMS DPERR_INVALIDPARAM +#define DPERR_INVALIDPLAYER MAKE_DPHRESULT( 150 ) +#define DPERR_INVALIDGROUP MAKE_DPHRESULT( 155 ) +#define DPERR_NOCAPS MAKE_DPHRESULT( 160 ) +#define DPERR_NOCONNECTION MAKE_DPHRESULT( 170 ) +#define DPERR_NOMEMORY E_OUTOFMEMORY +#define DPERR_OUTOFMEMORY DPERR_NOMEMORY +#define DPERR_NOMESSAGES MAKE_DPHRESULT( 190 ) +#define DPERR_NONAMESERVERFOUND MAKE_DPHRESULT( 200 ) +#define DPERR_NOPLAYERS MAKE_DPHRESULT( 210 ) +#define DPERR_NOSESSIONS MAKE_DPHRESULT( 220 ) +#define DPERR_PENDING E_PENDING +#define DPERR_SENDTOOBIG MAKE_DPHRESULT( 230 ) +#define DPERR_TIMEOUT MAKE_DPHRESULT( 240 ) +#define DPERR_UNAVAILABLE MAKE_DPHRESULT( 250 ) +#define DPERR_UNSUPPORTED E_NOTIMPL +#define DPERR_BUSY MAKE_DPHRESULT( 270 ) +#define DPERR_USERCANCEL MAKE_DPHRESULT( 280 ) +#define DPERR_NOINTERFACE E_NOINTERFACE +#define DPERR_CANNOTCREATESERVER MAKE_DPHRESULT( 290 ) +#define DPERR_PLAYERLOST MAKE_DPHRESULT( 300 ) +#define DPERR_SESSIONLOST MAKE_DPHRESULT( 310 ) +#define DPERR_UNINITIALIZED MAKE_DPHRESULT( 320 ) +#define DPERR_NONEWPLAYERS MAKE_DPHRESULT( 330 ) +#define DPERR_INVALIDPASSWORD MAKE_DPHRESULT( 340 ) +#define DPERR_CONNECTING MAKE_DPHRESULT( 350 ) +#define DPERR_CONNECTIONLOST MAKE_DPHRESULT( 360 ) +#define DPERR_UNKNOWNMESSAGE MAKE_DPHRESULT( 370 ) +#define DPERR_CANCELFAILED MAKE_DPHRESULT( 380 ) +#define DPERR_INVALIDPRIORITY MAKE_DPHRESULT( 390 ) +#define DPERR_NOTHANDLED MAKE_DPHRESULT( 400 ) +#define DPERR_CANCELLED MAKE_DPHRESULT( 410 ) +#define DPERR_ABORTED MAKE_DPHRESULT( 420 ) + + +#define DPERR_BUFFERTOOLARGE MAKE_DPHRESULT( 1000 ) +#define DPERR_CANTCREATEPROCESS MAKE_DPHRESULT( 1010 ) +#define DPERR_APPNOTSTARTED MAKE_DPHRESULT( 1020 ) +#define DPERR_INVALIDINTERFACE MAKE_DPHRESULT( 1030 ) +#define DPERR_NOSERVICEPROVIDER MAKE_DPHRESULT( 1040 ) +#define DPERR_UNKNOWNAPPLICATION MAKE_DPHRESULT( 1050 ) +#define DPERR_NOTLOBBIED MAKE_DPHRESULT( 1070 ) +#define DPERR_SERVICEPROVIDERLOADED MAKE_DPHRESULT( 1080 ) +#define DPERR_ALREADYREGISTERED MAKE_DPHRESULT( 1090 ) +#define DPERR_NOTREGISTERED MAKE_DPHRESULT( 1100 ) + +// +// Security related errors +// +#define DPERR_AUTHENTICATIONFAILED MAKE_DPHRESULT( 2000 ) +#define DPERR_CANTLOADSSPI MAKE_DPHRESULT( 2010 ) +#define DPERR_ENCRYPTIONFAILED MAKE_DPHRESULT( 2020 ) +#define DPERR_SIGNFAILED MAKE_DPHRESULT( 2030 ) +#define DPERR_CANTLOADSECURITYPACKAGE MAKE_DPHRESULT( 2040 ) +#define DPERR_ENCRYPTIONNOTSUPPORTED MAKE_DPHRESULT( 2050 ) +#define DPERR_CANTLOADCAPI MAKE_DPHRESULT( 2060 ) +#define DPERR_NOTLOGGEDIN MAKE_DPHRESULT( 2070 ) +#define DPERR_LOGONDENIED MAKE_DPHRESULT( 2080 ) + + +/**************************************************************************** + * + * dplay 1.0 obsolete structures + interfaces + * Included for compatibility only. New apps should + * use IDirectPlay2 + * + ****************************************************************************/ + +// define this to ignore obsolete interfaces and constants +#ifndef IDIRECTPLAY2_OR_GREATER + +#define DPOPEN_OPENSESSION DPOPEN_JOIN +#define DPOPEN_CREATESESSION DPOPEN_CREATE + +#define DPENUMSESSIONS_PREVIOUS 0x00000004 + +#define DPENUMPLAYERS_PREVIOUS 0x00000004 + +#define DPSEND_GUARANTEE DPSEND_GUARANTEED +#define DPSEND_TRYONCE 0x00000004 + +#define DPCAPS_NAMESERVICE 0x00000001 +#define DPCAPS_NAMESERVER DPCAPS_ISHOST +#define DPCAPS_GUARANTEED 0x00000004 + +#define DPLONGNAMELEN 52 +#define DPSHORTNAMELEN 20 +#define DPSESSIONNAMELEN 32 +#define DPPASSWORDLEN 16 +#define DPUSERRESERVED 16 + +#define DPSYS_ADDPLAYER 0x0003 +#define DPSYS_DELETEPLAYER 0x0005 + +#define DPSYS_DELETEGROUP 0x0020 +#define DPSYS_DELETEPLAYERFROMGRP 0x0021 +#define DPSYS_CONNECT 0x484b + +typedef struct +{ + DWORD dwType; + DWORD dwPlayerType; + DPID dpId; + char szLongName[DPLONGNAMELEN]; + char szShortName[DPSHORTNAMELEN]; + DWORD dwCurrentPlayers; +} DPMSG_ADDPLAYER; + +typedef DPMSG_ADDPLAYER DPMSG_ADDGROUP; + +typedef struct +{ + DWORD dwType; + DPID dpIdGroup; + DPID dpIdPlayer; +} DPMSG_GROUPADD; + +typedef DPMSG_GROUPADD DPMSG_GROUPDELETE; +typedef struct +{ + DWORD dwType; + DPID dpId; +} DPMSG_DELETEPLAYER; + +typedef BOOL (PASCAL *LPDPENUMPLAYERSCALLBACK)( + DPID dpId, + LPSTR lpFriendlyName, + LPSTR lpFormalName, + DWORD dwFlags, + LPVOID lpContext ); + +typedef struct +{ + DWORD dwSize; + GUID guidSession; + DWORD dwSession; + DWORD dwMaxPlayers; + DWORD dwCurrentPlayers; + DWORD dwFlags; + char szSessionName[DPSESSIONNAMELEN]; + char szUserField[DPUSERRESERVED]; + DWORD dwReserved1; + char szPassword[DPPASSWORDLEN]; + DWORD dwReserved2; + DWORD dwUser1; + DWORD dwUser2; + DWORD dwUser3; + DWORD dwUser4; +} DPSESSIONDESC,*LPDPSESSIONDESC; + +typedef BOOL (PASCAL * LPDPENUMSESSIONSCALLBACK)( + LPDPSESSIONDESC lpDPSessionDesc, + LPVOID lpContext, + LPDWORD lpdwTimeOut, + DWORD dwFlags); + +/* + * IDirectPlay + */ +#undef INTERFACE +#define INTERFACE IDirectPlay +DECLARE_INTERFACE_( IDirectPlay, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPSTR,LPSTR,LPHANDLE) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPSTR,LPSTR) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(EnableNewPlayers) (THIS_ BOOL) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ DWORD, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ DWORD, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC,DWORD,LPDPENUMSESSIONSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID, LPDPCAPS) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPSTR,LPDWORD,LPSTR,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(SaveSession) (THIS_ LPSTR) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPSTR,LPSTR) PURE; +}; + +/**************************************************************************** + * + * IDirectPlay interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay_AddPlayerToGroup(p,a,b) (p)->lpVtbl->AddPlayerToGroup(p,a,b) +#define IDirectPlay_Close(p) (p)->lpVtbl->Close(p) +#define IDirectPlay_CreateGroup(p,a,b,c) (p)->lpVtbl->CreateGroup(p,a,b,c) +#define IDirectPlay_CreatePlayer(p,a,b,c,d) (p)->lpVtbl->CreatePlayer(p,a,b,c,d) +#define IDirectPlay_DeletePlayerFromGroup(p,a,b) (p)->lpVtbl->DeletePlayerFromGroup(p,a,b) +#define IDirectPlay_DestroyGroup(p,a) (p)->lpVtbl->DestroyGroup(p,a) +#define IDirectPlay_DestroyPlayer(p,a) (p)->lpVtbl->DestroyPlayer(p,a) +#define IDirectPlay_EnableNewPlayers(p,a) (p)->lpVtbl->EnableNewPlayers(p,a) +#define IDirectPlay_EnumGroupPlayers(p,a,b,c,d) (p)->lpVtbl->EnumGroupPlayers(p,a,b,c,d) +#define IDirectPlay_EnumGroups(p,a,b,c,d) (p)->lpVtbl->EnumGroups(p,a,b,c,d) +#define IDirectPlay_EnumPlayers(p,a,b,c,d) (p)->lpVtbl->EnumPlayers(p,a,b,c,d) +#define IDirectPlay_EnumSessions(p,a,b,c,d,e) (p)->lpVtbl->EnumSessions(p,a,b,c,d,e) +#define IDirectPlay_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectPlay_GetMessageCount(p,a,b) (p)->lpVtbl->GetMessageCount(p,a,b) +#define IDirectPlay_GetPlayerCaps(p,a,b) (p)->lpVtbl->GetPlayerCaps(p,a,b) +#define IDirectPlay_GetPlayerName(p,a,b,c,d,e) (p)->lpVtbl->GetPlayerName(p,a,b,c,d,e) +#define IDirectPlay_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirectPlay_Open(p,a) (p)->lpVtbl->Open(p,a) +#define IDirectPlay_Receive(p,a,b,c,d,e) (p)->lpVtbl->Receive(p,a,b,c,d,e) +#define IDirectPlay_SaveSession(p,a) (p)->lpVtbl->SaveSession(p,a) +#define IDirectPlay_Send(p,a,b,c,d,e) (p)->lpVtbl->Send(p,a,b,c,d,e) +#define IDirectPlay_SetPlayerName(p,a,b,c) (p)->lpVtbl->SetPlayerName(p,a,b,c) + +#else /* C++ */ + +#define IDirectPlay_AddPlayerToGroup(p,a,b) (p)->AddPlayerToGroup(a,b) +#define IDirectPlay_Close(p) (p)->Close() +#define IDirectPlay_CreateGroup(p,a,b,c) (p)->CreateGroup(a,b,c) +#define IDirectPlay_CreatePlayer(p,a,b,c,d) (p)->CreatePlayer(a,b,c,d) +#define IDirectPlay_DeletePlayerFromGroup(p,a,b) (p)->DeletePlayerFromGroup(a,b) +#define IDirectPlay_DestroyGroup(p,a) (p)->DestroyGroup(a) +#define IDirectPlay_DestroyPlayer(p,a) (p)->DestroyPlayer(a) +#define IDirectPlay_EnableNewPlayers(p,a) (p)->EnableNewPlayers(a) +#define IDirectPlay_EnumGroupPlayers(p,a,b,c,d) (p)->EnumGroupPlayers(a,b,c,d) +#define IDirectPlay_EnumGroups(p,a,b,c,d) (p)->EnumGroups(a,b,c,d) +#define IDirectPlay_EnumPlayers(p,a,b,c,d) (p)->EnumPlayers(a,b,c,d) +#define IDirectPlay_EnumSessions(p,a,b,c,d,e) (p)->EnumSessions(a,b,c,d,e) +#define IDirectPlay_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectPlay_GetMessageCount(p,a,b) (p)->GetMessageCount(a,b) +#define IDirectPlay_GetPlayerCaps(p,a,b) (p)->GetPlayerCaps(a,b) +#define IDirectPlay_GetPlayerName(p,a,b,c,d,e) (p)->GetPlayerName(a,b,c,d,e) +#define IDirectPlay_Initialize(p,a) (p)->Initialize(a) +#define IDirectPlay_Open(p,a) (p)->Open(a) +#define IDirectPlay_Receive(p,a,b,c,d,e) (p)->Receive(a,b,c,d,e) +#define IDirectPlay_SaveSession(p,a) (p)->SaveSession(a) +#define IDirectPlay_Send(p,a,b,c,d,e) (p)->Send(a,b,c,d,e) +#define IDirectPlay_SetPlayerName(p,a,b,c) (p)->SetPlayerName(a,b,c) + +#endif + +DEFINE_GUID(IID_IDirectPlay, 0x5454e9a0, 0xdb65, 0x11ce, 0x92, 0x1c, 0x00, 0xaa, 0x00, 0x6c, 0x49, 0x72); + +#endif // IDIRECTPLAY2_OR_GREATER + +/**************************************************************************** + * + * IDirectPlay macros (included regardless of IDIRECTPLAY2_OR_GREATER flag) + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlay_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlay_Release(p) (p)->lpVtbl->Release(p) + +#else + +#define IDirectPlay_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlay_AddRef(p) (p)->AddRef() +#define IDirectPlay_Release(p) (p)->Release() + +#endif // IDirectPlay interface macros + + + + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/lib/win/DirectX/dplayx.lib b/lib/win/DirectX/dplayx.lib new file mode 100644 index 000000000..70b1e92a1 Binary files /dev/null and b/lib/win/DirectX/dplayx.lib differ diff --git a/lib/win/DirectX/dplobby.h b/lib/win/DirectX/dplobby.h new file mode 100644 index 000000000..c3999edad --- /dev/null +++ b/lib/win/DirectX/dplobby.h @@ -0,0 +1,839 @@ +/*==========================================================================; + * + * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dplobby.h + * Content: DirectPlayLobby include file + *@@BEGIN_MSINTERNAL + * History: + * Date By Reason + * =========== ======= ========== + * 4/13/96 myronth Created it + * 6/20/96 myronth Cleaned it up + * 6/24/96 myronth More cleanup + * 7/1/96 myronth Changed DPLERR_ to DPERR_ + * 7/9/96 ajayj Cleaned up formatting to match dplay.h + * 7/15/96 ajayj Added DPADDRESS chunk tags for dplaunch + * sample app. + * 7/16/96 kipo changed address types to be GUIDs instead of 4CC + * 8/1/96 sohailm Added IDirectPlayLobby interface macros + * 8/10/96 sohailm Bug#2227: declared pointers in callbacks as const. + * 8/15/96 sohailm Added type definition for LPCDPLCONNECTION + * Changed LPGUIDs to REFGUIDs in IDirectPlayLobby interface + * Made function parameters constant where appropriate + * 8/16/96 andyco took out some const and ref stuff for ship + * 8/16/96 myronth Added SetConnectionSettings & Connect methods + * 8/21/96 ajayj replace tabs with spaces + * 8/21/96 kipo Added DPCOMPORTADDRESS structure + * 9/5/96 myronth Wrapped DPLCAPS with MSINTERNAL + * 10/23/96 myronth Added first cut of client/server methods + * 10/25/96 myronth Added DX5 methods + * 12/13/96 myronth Changed DPLAD_SYSTEM to DPLMSG_SYSTEM + * 1/30/97 myronth Added MSINTERNAL's to DX5 stuff for meltdown + * 2/12/97 myronth Removed meltdown changes + * 2/12/97 myronth Mass DX5 changes + * 2/18/97 myronth Implemented GetObjectCaps + * 3/5/97 andyco added DPAID_TotalSize + * 3/12/97 myronth Added DPAID_LobbyProvider chunk + * 3/17/97 kipo Added data structures for CreateCompoundAddress() and + * added guids for Unicode phone numbers and IP addresses + * 3/24/97 kipo Added support for IDirectPlayLobby2 interface + * 4/11/97 myronth Added CLSID_DirectPlayLobby clsid + * 5/8/97 myronth Moved DPLCONNECTION structure definition to dplay.h + * 5/21/97 ajayj Added structures and definitions for standard lobby + * messages + * 5/23/97 ajayj Added DPLDATA_PLAYERGUID structure + * 5/30/97 ajayj Added DPLPROPERTY_MessagesSupported GUID + * 6/7/97 myronth Fixed CreateCompoundAddress macros + * 6/6/97 kipo Added "FAR" to all structures + * 8/19/97 myronth Added DPLMSG_NEWSESSIONHOST structure + * 11/13/97 myronth Added guidInstance to lobby system messages (#10944) + * 12/2/97 myronth Added Register/UnregisterApplication + * 12/4/97 myronth Added ConnectEx + * 1/20/98 myronth Added WaitForConnectionSettings + * 1/29/98 sohailm Added DPAID_INetPort. + * 2/11/98 a-peterz Clarify that DPAID_INetPort value is a word (#18210) + * 4/15/98 a-peterz Fix IDirectPlayLobby_CreateCompoundAddress C++ version + *@@END_MSINTERNAL + ***************************************************************************/ +#ifndef __DPLOBBY_INCLUDED__ +#define __DPLOBBY_INCLUDED__ + +#include "dplay.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * GUIDS used by DirectPlay objects + */ + +/* {AF465C71-9588-11cf-A020-00AA006157AC} */ +DEFINE_GUID(IID_IDirectPlayLobby, 0xaf465c71, 0x9588, 0x11cf, 0xa0, 0x20, 0x0, 0xaa, 0x0, 0x61, 0x57, 0xac); +/* {26C66A70-B367-11cf-A024-00AA006157AC} */ +DEFINE_GUID(IID_IDirectPlayLobbyA, 0x26c66a70, 0xb367, 0x11cf, 0xa0, 0x24, 0x0, 0xaa, 0x0, 0x61, 0x57, 0xac); +/* {0194C220-A303-11d0-9C4F-00A0C905425E} */ +DEFINE_GUID(IID_IDirectPlayLobby2, 0x194c220, 0xa303, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); +/* {1BB4AF80-A303-11d0-9C4F-00A0C905425E} */ +DEFINE_GUID(IID_IDirectPlayLobby2A, 0x1bb4af80, 0xa303, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); +/* {2DB72490-652C-11d1-A7A8-0000F803ABFC} */ +DEFINE_GUID(IID_IDirectPlayLobby3, 0x2db72490, 0x652c, 0x11d1, 0xa7, 0xa8, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); +/* {2DB72491-652C-11d1-A7A8-0000F803ABFC} */ +DEFINE_GUID(IID_IDirectPlayLobby3A, 0x2db72491, 0x652c, 0x11d1, 0xa7, 0xa8, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); +/* {2FE8F810-B2A5-11d0-A787-0000F803ABFC} */ +DEFINE_GUID(CLSID_DirectPlayLobby, 0x2fe8f810, 0xb2a5, 0x11d0, 0xa7, 0x87, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); + + +/**************************************************************************** + * + * IDirectPlayLobby Structures + * + * Various structures used to invoke DirectPlayLobby. + * + ****************************************************************************/ + +typedef struct IDirectPlayLobby FAR *LPDIRECTPLAYLOBBY; +typedef struct IDirectPlayLobby FAR *LPDIRECTPLAYLOBBYA; +typedef struct IDirectPlayLobby IDirectPlayLobbyA; + +typedef struct IDirectPlayLobby2 FAR *LPDIRECTPLAYLOBBY2; +typedef struct IDirectPlayLobby2 FAR *LPDIRECTPLAYLOBBY2A; +typedef struct IDirectPlayLobby2 IDirectPlayLobby2A; + +typedef struct IDirectPlayLobby3 FAR *LPDIRECTPLAYLOBBY3; +typedef struct IDirectPlayLobby3 FAR *LPDIRECTPLAYLOBBY3A; +typedef struct IDirectPlayLobby3 IDirectPlayLobby3A; + + +/* + * DPLAPPINFO + * Used to hold information about a registered DirectPlay + * application + */ +typedef struct DPLAPPINFO +{ + DWORD dwSize; // Size of this structure + GUID guidApplication; // GUID of the Application + union + { + LPSTR lpszAppNameA; // Pointer to the Application Name + LPWSTR lpszAppName; + }; + +} DPLAPPINFO, FAR *LPDPLAPPINFO; + +/* + * LPCDPLAPPINFO + * A constant pointer to DPLAPPINFO + */ +typedef const DPLAPPINFO FAR *LPCDPLAPPINFO; + +/* + * DPCOMPOUNDADDRESSELEMENT + * + * An array of these is passed to CreateCompoundAddresses() + */ +typedef struct DPCOMPOUNDADDRESSELEMENT +{ + GUID guidDataType; + DWORD dwDataSize; + LPVOID lpData; +} DPCOMPOUNDADDRESSELEMENT, FAR *LPDPCOMPOUNDADDRESSELEMENT; + +/* + * LPCDPCOMPOUNDADDRESSELEMENT + * A constant pointer to DPCOMPOUNDADDRESSELEMENT + */ +typedef const DPCOMPOUNDADDRESSELEMENT FAR *LPCDPCOMPOUNDADDRESSELEMENT; + +/* + * LPDPAPPLICATIONDESC + * Used to register a DirectPlay application + */ +typedef struct DPAPPLICATIONDESC +{ + DWORD dwSize; + DWORD dwFlags; + union + { + LPSTR lpszApplicationNameA; + LPWSTR lpszApplicationName; + }; + GUID guidApplication; + union + { + LPSTR lpszFilenameA; + LPWSTR lpszFilename; + }; + union + { + LPSTR lpszCommandLineA; + LPWSTR lpszCommandLine; + }; + union + { + LPSTR lpszPathA; + LPWSTR lpszPath; + }; + union + { + LPSTR lpszCurrentDirectoryA; + LPWSTR lpszCurrentDirectory; + }; + LPSTR lpszDescriptionA; + LPWSTR lpszDescriptionW; +} DPAPPLICATIONDESC, *LPDPAPPLICATIONDESC; + + +/**************************************************************************** + * + * Enumeration Method Callback Prototypes + * + ****************************************************************************/ + +/* + * Callback for EnumAddress() + */ +typedef BOOL (FAR PASCAL *LPDPENUMADDRESSCALLBACK)( + REFGUID guidDataType, + DWORD dwDataSize, + LPCVOID lpData, + LPVOID lpContext); + +/* + * Callback for EnumAddressTypes() + */ +typedef BOOL (FAR PASCAL *LPDPLENUMADDRESSTYPESCALLBACK)( + REFGUID guidDataType, + LPVOID lpContext, + DWORD dwFlags); + +/* + * Callback for EnumLocalApplications() + */ +typedef BOOL (FAR PASCAL * LPDPLENUMLOCALAPPLICATIONSCALLBACK)( + LPCDPLAPPINFO lpAppInfo, + LPVOID lpContext, + DWORD dwFlags); + + +/**************************************************************************** + * + * DirectPlayLobby API Prototypes + * + ****************************************************************************/ +#ifdef UNICODE +#define DirectPlayLobbyCreate DirectPlayLobbyCreateW +#else +#define DirectPlayLobbyCreate DirectPlayLobbyCreateA +#endif /* UNICODE */ + +extern HRESULT WINAPI DirectPlayLobbyCreateW(LPGUID, LPDIRECTPLAYLOBBY *, IUnknown *, LPVOID, DWORD ); +extern HRESULT WINAPI DirectPlayLobbyCreateA(LPGUID, LPDIRECTPLAYLOBBYA *, IUnknown *, LPVOID, DWORD ); + + +/**************************************************************************** + * + * IDirectPlayLobby (and IDirectPlayLobbyA) Interface + * + ****************************************************************************/ +#undef INTERFACE +#define INTERFACE IDirectPlayLobby +DECLARE_INTERFACE_( IDirectPlayLobby, IUnknown ) +{ + /* IUnknown Methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /* IDirectPlayLobby Methods */ + STDMETHOD(Connect) (THIS_ DWORD, LPDIRECTPLAY2 *, IUnknown FAR *) PURE; + STDMETHOD(CreateAddress) (THIS_ REFGUID, REFGUID, LPCVOID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(EnumAddress) (THIS_ LPDPENUMADDRESSCALLBACK, LPCVOID, DWORD, LPVOID) PURE; + STDMETHOD(EnumAddressTypes) (THIS_ LPDPLENUMADDRESSTYPESCALLBACK, REFGUID, LPVOID, DWORD) PURE; + STDMETHOD(EnumLocalApplications)(THIS_ LPDPLENUMLOCALAPPLICATIONSCALLBACK, LPVOID, DWORD) PURE; + STDMETHOD(GetConnectionSettings)(THIS_ DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(ReceiveLobbyMessage) (THIS_ DWORD, DWORD, LPDWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(RunApplication) (THIS_ DWORD, LPDWORD, LPDPLCONNECTION, HANDLE) PURE; + STDMETHOD(SendLobbyMessage) (THIS_ DWORD, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetConnectionSettings)(THIS_ DWORD, DWORD, LPDPLCONNECTION) PURE; + STDMETHOD(SetLobbyMessageEvent) (THIS_ DWORD, DWORD, HANDLE) PURE; + +}; + +/**************************************************************************** + * + * IDirectPlayLobby2 (and IDirectPlayLobby2A) Interface + * + ****************************************************************************/ +#undef INTERFACE +#define INTERFACE IDirectPlayLobby2 +DECLARE_INTERFACE_( IDirectPlayLobby2, IDirectPlayLobby ) +{ + /* IUnknown Methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /* IDirectPlayLobby Methods */ + STDMETHOD(Connect) (THIS_ DWORD, LPDIRECTPLAY2 *, IUnknown FAR *) PURE; + STDMETHOD(CreateAddress) (THIS_ REFGUID, REFGUID, LPCVOID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(EnumAddress) (THIS_ LPDPENUMADDRESSCALLBACK, LPCVOID, DWORD, LPVOID) PURE; + STDMETHOD(EnumAddressTypes) (THIS_ LPDPLENUMADDRESSTYPESCALLBACK, REFGUID, LPVOID, DWORD) PURE; + STDMETHOD(EnumLocalApplications)(THIS_ LPDPLENUMLOCALAPPLICATIONSCALLBACK, LPVOID, DWORD) PURE; + STDMETHOD(GetConnectionSettings)(THIS_ DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(ReceiveLobbyMessage) (THIS_ DWORD, DWORD, LPDWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(RunApplication) (THIS_ DWORD, LPDWORD, LPDPLCONNECTION, HANDLE) PURE; + STDMETHOD(SendLobbyMessage) (THIS_ DWORD, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetConnectionSettings)(THIS_ DWORD, DWORD, LPDPLCONNECTION) PURE; + STDMETHOD(SetLobbyMessageEvent) (THIS_ DWORD, DWORD, HANDLE) PURE; + + /* IDirectPlayLobby2 Methods */ + STDMETHOD(CreateCompoundAddress)(THIS_ LPCDPCOMPOUNDADDRESSELEMENT,DWORD,LPVOID,LPDWORD) PURE; +}; + +/**************************************************************************** + * + * IDirectPlayLobby3 (and IDirectPlayLobby3A) Interface + * + ****************************************************************************/ +#undef INTERFACE +#define INTERFACE IDirectPlayLobby3 +DECLARE_INTERFACE_( IDirectPlayLobby3, IDirectPlayLobby ) +{ + /* IUnknown Methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /* IDirectPlayLobby Methods */ + STDMETHOD(Connect) (THIS_ DWORD, LPDIRECTPLAY2 *, IUnknown FAR *) PURE; + STDMETHOD(CreateAddress) (THIS_ REFGUID, REFGUID, LPCVOID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(EnumAddress) (THIS_ LPDPENUMADDRESSCALLBACK, LPCVOID, DWORD, LPVOID) PURE; + STDMETHOD(EnumAddressTypes) (THIS_ LPDPLENUMADDRESSTYPESCALLBACK, REFGUID, LPVOID, DWORD) PURE; + STDMETHOD(EnumLocalApplications)(THIS_ LPDPLENUMLOCALAPPLICATIONSCALLBACK, LPVOID, DWORD) PURE; + STDMETHOD(GetConnectionSettings)(THIS_ DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(ReceiveLobbyMessage) (THIS_ DWORD, DWORD, LPDWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(RunApplication) (THIS_ DWORD, LPDWORD, LPDPLCONNECTION, HANDLE) PURE; + STDMETHOD(SendLobbyMessage) (THIS_ DWORD, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetConnectionSettings)(THIS_ DWORD, DWORD, LPDPLCONNECTION) PURE; + STDMETHOD(SetLobbyMessageEvent) (THIS_ DWORD, DWORD, HANDLE) PURE; + + /* IDirectPlayLobby2 Methods */ + STDMETHOD(CreateCompoundAddress)(THIS_ LPCDPCOMPOUNDADDRESSELEMENT,DWORD,LPVOID,LPDWORD) PURE; + + /* IDirectPlayLobby3 Methods */ + STDMETHOD(ConnectEx) (THIS_ DWORD, REFIID, LPVOID *, IUnknown FAR *) PURE; + STDMETHOD(RegisterApplication) (THIS_ DWORD, LPDPAPPLICATIONDESC) PURE; + STDMETHOD(UnregisterApplication)(THIS_ DWORD, REFGUID) PURE; + STDMETHOD(WaitForConnectionSettings)(THIS_ DWORD) PURE; +}; + +/**************************************************************************** + * + * IDirectPlayLobby interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlayLobby_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlayLobby_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlayLobby_Release(p) (p)->lpVtbl->Release(p) +#define IDirectPlayLobby_Connect(p,a,b,c) (p)->lpVtbl->Connect(p,a,b,c) +#define IDirectPlayLobby_ConnectEx(p,a,b,c,d) (p)->lpVtbl->ConnectEx(p,a,b,c,d) +#define IDirectPlayLobby_CreateAddress(p,a,b,c,d,e,f) (p)->lpVtbl->CreateAddress(p,a,b,c,d,e,f) +#define IDirectPlayLobby_CreateCompoundAddress(p,a,b,c,d) (p)->lpVtbl->CreateCompoundAddress(p,a,b,c,d) +#define IDirectPlayLobby_EnumAddress(p,a,b,c,d) (p)->lpVtbl->EnumAddress(p,a,b,c,d) +#define IDirectPlayLobby_EnumAddressTypes(p,a,b,c,d) (p)->lpVtbl->EnumAddressTypes(p,a,b,c,d) +#define IDirectPlayLobby_EnumLocalApplications(p,a,b,c) (p)->lpVtbl->EnumLocalApplications(p,a,b,c) +#define IDirectPlayLobby_GetConnectionSettings(p,a,b,c) (p)->lpVtbl->GetConnectionSettings(p,a,b,c) +#define IDirectPlayLobby_ReceiveLobbyMessage(p,a,b,c,d,e) (p)->lpVtbl->ReceiveLobbyMessage(p,a,b,c,d,e) +#define IDirectPlayLobby_RegisterApplication(p,a,b) (p)->lpVtbl->RegisterApplication(p,a,b) +#define IDirectPlayLobby_RunApplication(p,a,b,c,d) (p)->lpVtbl->RunApplication(p,a,b,c,d) +#define IDirectPlayLobby_SendLobbyMessage(p,a,b,c,d) (p)->lpVtbl->SendLobbyMessage(p,a,b,c,d) +#define IDirectPlayLobby_SetConnectionSettings(p,a,b,c) (p)->lpVtbl->SetConnectionSettings(p,a,b,c) +#define IDirectPlayLobby_SetLobbyMessageEvent(p,a,b,c) (p)->lpVtbl->SetLobbyMessageEvent(p,a,b,c) +#define IDirectPlayLobby_UnregisterApplication(p,a,b) (p)->lpVtbl->UnregisterApplication(p,a,b) +#define IDirectPlayLobby_WaitForConnectionSettings(p,a) (p)->lpVtbl->WaitForConnectionSettings(p,a) + +#else /* C++ */ + +#define IDirectPlayLobby_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlayLobby_AddRef(p) (p)->AddRef() +#define IDirectPlayLobby_Release(p) (p)->Release() +#define IDirectPlayLobby_Connect(p,a,b,c) (p)->Connect(a,b,c) +#define IDirectPlayLobby_ConnectEx(p,a,b,c,d) (p)->ConnectEx(a,b,c,d) +#define IDirectPlayLobby_CreateAddress(p,a,b,c,d,e,f) (p)->CreateAddress(a,b,c,d,e,f) +#define IDirectPlayLobby_CreateCompoundAddress(p,a,b,c,d) (p)->CreateCompoundAddress(a,b,c,d) +#define IDirectPlayLobby_EnumAddress(p,a,b,c,d) (p)->EnumAddress(a,b,c,d) +#define IDirectPlayLobby_EnumAddressTypes(p,a,b,c,d) (p)->EnumAddressTypes(a,b,c,d) +#define IDirectPlayLobby_EnumLocalApplications(p,a,b,c) (p)->EnumLocalApplications(a,b,c) +#define IDirectPlayLobby_GetConnectionSettings(p,a,b,c) (p)->GetConnectionSettings(a,b,c) +#define IDirectPlayLobby_ReceiveLobbyMessage(p,a,b,c,d,e) (p)->ReceiveLobbyMessage(a,b,c,d,e) +#define IDirectPlayLobby_RegisterApplication(p,a,b) (p)->RegisterApplication(a,b) +#define IDirectPlayLobby_RunApplication(p,a,b,c,d) (p)->RunApplication(a,b,c,d) +#define IDirectPlayLobby_SendLobbyMessage(p,a,b,c,d) (p)->SendLobbyMessage(a,b,c,d) +#define IDirectPlayLobby_SetConnectionSettings(p,a,b,c) (p)->SetConnectionSettings(a,b,c) +#define IDirectPlayLobby_SetLobbyMessageEvent(p,a,b,c) (p)->SetLobbyMessageEvent(a,b,c) +#define IDirectPlayLobby_UnregisterApplication(p,a,b) (p)->UnregisterApplication(a,b) +#define IDirectPlayLobby_WaitForConnectionSettings(p,a) (p)->WaitForConnectionSettings(a) + +#endif + +/**************************************************************************** + * + * DirectPlayLobby Flags + * + ****************************************************************************/ + +/* + * This flag is used by IDirectPlayLobby->WaitForConnectionSettings to + * cancel a current wait that is in progress. + */ +#define DPLWAIT_CANCEL 0x00000001 + + +/* + * This is a message flag used by ReceiveLobbyMessage. It can be + * returned in the dwMessageFlags parameter to indicate a message from + * the system. + */ +#define DPLMSG_SYSTEM 0x00000001 + +/* + * This is a message flag used by ReceiveLobbyMessage and SendLobbyMessage. + * It is used to indicate that the message is a standard lobby message. + * DPLMSG_SETPROPERTY, DPLMSG_SETPROPERTYRESPONSE, DPLMSG_GETPROPERTY, + * DPLMSG_GETPROPERTYRESPONSE + */ +#define DPLMSG_STANDARD 0x00000002 + + +/**************************************************************************** + * + * DirectPlayLobby messages and message data structures + * + * All system messages have a dwMessageFlags value of DPLMSG_SYSTEM returned + * from a call to ReceiveLobbyMessage. + * + * All standard messages have a dwMessageFlags value of DPLMSG_STANDARD returned + * from a call to ReceiveLobbyMessage. + * + ****************************************************************************/ + +/* + * DPLMSG_GENERIC + * Generic message structure used to identify the message type. + */ +typedef struct _DPLMSG_GENERIC +{ + DWORD dwType; // Message type +} DPLMSG_GENERIC, FAR *LPDPLMSG_GENERIC; + +/* + * DPLMSG_SYSTEMMESSAGE + * Generic message format for all system messages -- + * DPLSYS_CONNECTIONSETTINGSREAD, DPLSYS_DPLYCONNECTSUCCEEDED, + * DPLSYS_DPLAYCONNECTFAILED, DPLSYS_APPTERMINATED, DPLSYS_NEWCONNECTIONSETTINGS + */ +typedef struct _DPLMSG_SYSTEMMESSAGE +{ + DWORD dwType; // Message type + GUID guidInstance; // Instance GUID of the dplay session the message corresponds to +} DPLMSG_SYSTEMMESSAGE, FAR *LPDPLMSG_SYSTEMMESSAGE; + +/* + * DPLMSG_SETPROPERTY + * Standard message sent by an application to a lobby to set a + * property + */ +typedef struct _DPLMSG_SETPROPERTY +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID (DPL_NOCONFIRMATION if no confirmation desired) + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID + DWORD dwDataSize; // Size of data + DWORD dwPropertyData[1]; // Buffer containing data +} DPLMSG_SETPROPERTY, FAR *LPDPLMSG_SETPROPERTY; + +#define DPL_NOCONFIRMATION 0 + +/* + * DPLMSG_SETPROPERTYRESPONSE + * Standard message returned by a lobby to confirm a + * DPLMSG_SETPROPERTY message. + */ +typedef struct _DPLMSG_SETPROPERTYRESPONSE +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID + HRESULT hr; // Return Code +} DPLMSG_SETPROPERTYRESPONSE, FAR *LPDPLMSG_SETPROPERTYRESPONSE; + +/* + * DPLMSG_GETPROPERTY + * Standard message sent by an application to a lobby to request + * the current value of a property + */ +typedef struct _DPLMSG_GETPROPERTY +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID +} DPLMSG_GETPROPERTY, FAR *LPDPLMSG_GETPROPERTY; + +/* + * DPLMSG_GETPROPERTYRESPONSE + * Standard message returned by a lobby in response to a + * DPLMSG_GETPROPERTY message. + */ +typedef struct _DPLMSG_GETPROPERTYRESPONSE +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID + HRESULT hr; // Return Code + DWORD dwDataSize; // Size of data + DWORD dwPropertyData[1]; // Buffer containing data +} DPLMSG_GETPROPERTYRESPONSE, FAR *LPDPLMSG_GETPROPERTYRESPONSE; + +/* + * DPLMSG_NEWSESSIONHOST + * Standard message returned by a lobby in response to a + * the session host migrating to a new client + */ +typedef struct _DPLMSG_NEWSESSIONHOST +{ + DWORD dwType; // Message type + GUID guidInstance; // GUID Instance of the session +} DPLMSG_NEWSESSIONHOST, FAR *LPDPLMSG_NEWSESSIONHOST; + + +/****************************************** + * + * DirectPlay Lobby message dwType values + * + *****************************************/ + +/* + * The application has read the connection settings. + * It is now O.K. for the lobby client to release + * its IDirectPlayLobby interface. + */ +#define DPLSYS_CONNECTIONSETTINGSREAD 0x00000001 + +/* + * The application's call to DirectPlayConnect failed + */ +#define DPLSYS_DPLAYCONNECTFAILED 0x00000002 + +/* + * The application has created a DirectPlay session. + */ +#define DPLSYS_DPLAYCONNECTSUCCEEDED 0x00000003 + +/* + * The application has terminated. + */ +#define DPLSYS_APPTERMINATED 0x00000004 + +/* + * The message is a DPLMSG_SETPROPERTY message. + */ +#define DPLSYS_SETPROPERTY 0x00000005 + +/* + * The message is a DPLMSG_SETPROPERTYRESPONSE message. + */ +#define DPLSYS_SETPROPERTYRESPONSE 0x00000006 + +/* + * The message is a DPLMSG_GETPROPERTY message. + */ +#define DPLSYS_GETPROPERTY 0x00000007 + +/* + * The message is a DPLMSG_GETPROPERTYRESPONSE message. + */ +#define DPLSYS_GETPROPERTYRESPONSE 0x00000008 + +/* + * The message is a DPLMSG_NEWSESSIONHOST message. + */ +#define DPLSYS_NEWSESSIONHOST 0x00000009 + +/* + * New connection settings are available. + */ +#define DPLSYS_NEWCONNECTIONSETTINGS 0x0000000A + + +/**************************************************************************** + * + * DirectPlay defined property GUIDs and associated data structures + * + ****************************************************************************/ + +/* + * DPLPROPERTY_MessagesSupported + * + * Request whether the lobby supports standard. Lobby with respond with either + * TRUE or FALSE or may not respond at all. + * + * Property data is a single BOOL with TRUE or FALSE + */ +// {762CCDA1-D916-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_MessagesSupported, +0x762ccda1, 0xd916, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLPROPERTY_LobbyGuid + * + * Request the GUID that identifies the lobby software that the application + * is communicating with. + * + * Property data is a single GUID. + */ +// {F56920A0-D218-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_LobbyGuid, +0xf56920a0, 0xd218, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLPROPERTY_PlayerGuid + * + * Request the GUID that identifies the player on this machine for sending + * property data back to the lobby. + * + * Property data is the DPLDATA_PLAYERDATA structure + */ +// {B4319322-D20D-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_PlayerGuid, +0xb4319322, 0xd20d, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLDATA_PLAYERGUID + * + * Data structure to hold the GUID of the player and player creation flags + * from the lobby. + */ +typedef struct _DPLDATA_PLAYERGUID +{ + GUID guidPlayer; + DWORD dwPlayerFlags; +} DPLDATA_PLAYERGUID, FAR *LPDPLDATA_PLAYERGUID; + +/* + * DPLPROPERTY_PlayerScore + * + * Used to send an array of long integers to the lobby indicating the + * score of a player. + * + * Property data is the DPLDATA_PLAYERSCORE structure. + */ +// {48784000-D219-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_PlayerScore, +0x48784000, 0xd219, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLDATA_PLAYERSCORE + * + * Data structure to hold an array of long integers representing a player score. + * Application must allocate enough memory to hold all the scores. + */ +typedef struct _DPLDATA_PLAYERSCORE +{ + DWORD dwScoreCount; + LONG Score[1]; +} DPLDATA_PLAYERSCORE, FAR *LPDPLDATA_PLAYERSCORE; + +/**************************************************************************** + * + * DirectPlay Address ID's + * + ****************************************************************************/ + +/* DirectPlay Address + * + * A DirectPlay address consists of multiple chunks of data, each tagged + * with a GUID signifying the type of data in the chunk. The chunk also + * has a length so that unknown chunk types can be skipped. + * + * The EnumAddress() function is used to parse these address data chunks. + */ + +/* + * DPADDRESS + * + * Header for block of address data elements + */ +typedef struct _DPADDRESS +{ + GUID guidDataType; + DWORD dwDataSize; +} DPADDRESS; + +typedef DPADDRESS FAR *LPDPADDRESS; + +/* + * DPAID_TotalSize + * + * Chunk is a DWORD containing size of entire DPADDRESS structure + */ + +// {1318F560-912C-11d0-9DAA-00A0C90A43CB} +DEFINE_GUID(DPAID_TotalSize, +0x1318f560, 0x912c, 0x11d0, 0x9d, 0xaa, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); + +/* + * DPAID_ServiceProvider + * + * Chunk is a GUID describing the service provider that created the chunk. + * All addresses must contain this chunk. + */ + +// {07D916C0-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_ServiceProvider, +0x7d916c0, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPAID_LobbyProvider + * + * Chunk is a GUID describing the lobby provider that created the chunk. + * All addresses must contain this chunk. + */ + +// {59B95640-9667-11d0-A77D-0000F803ABFC} +DEFINE_GUID(DPAID_LobbyProvider, +0x59b95640, 0x9667, 0x11d0, 0xa7, 0x7d, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); + +/* + * DPAID_Phone and DPAID_PhoneW + * + * Chunk is a string containing a phone number (i.e. "1-800-555-1212") + * in ANSI or UNICODE format + */ + +// {78EC89A0-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_Phone, +0x78ec89a0, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// {BA5A7A70-9DBF-11d0-9CC1-00A0C905425E} +DEFINE_GUID(DPAID_PhoneW, +0xba5a7a70, 0x9dbf, 0x11d0, 0x9c, 0xc1, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPAID_Modem and DPAID_ModemW + * + * Chunk is a string containing a modem name registered with TAPI + * in ANSI or UNICODE format + */ + +// {F6DCC200-A2FE-11d0-9C4F-00A0C905425E} +DEFINE_GUID(DPAID_Modem, +0xf6dcc200, 0xa2fe, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// {01FD92E0-A2FF-11d0-9C4F-00A0C905425E} +DEFINE_GUID(DPAID_ModemW, +0x1fd92e0, 0xa2ff, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPAID_Inet and DPAID_InetW + * + * Chunk is a string containing a TCP/IP host name or an IP address + * (i.e. "dplay.microsoft.com" or "137.55.100.173") in ANSI or UNICODE format + */ + +// {C4A54DA0-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_INet, +0xc4a54da0, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// {E63232A0-9DBF-11d0-9CC1-00A0C905425E} +DEFINE_GUID(DPAID_INetW, +0xe63232a0, 0x9dbf, 0x11d0, 0x9c, 0xc1, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPAID_InetPort + * + * Chunk is the port number used for creating the apps TCP and UDP sockets. + * WORD value (i.e. 47624). + */ + +// {E4524541-8EA5-11d1-8A96-006097B01411} +DEFINE_GUID(DPAID_INetPort, +0xe4524541, 0x8ea5, 0x11d1, 0x8a, 0x96, 0x0, 0x60, 0x97, 0xb0, 0x14, 0x11); + +#ifdef BIGMESSAGEDEFENSE +//@@BEGIN_MSINTERNAL +/* + * DPAID_MaxMessageSize + * + * Tells DPLAY what the maximum allowed message size is. Enables SPs to + * combat Denial of Service attacks + */ + + // this terrible hack is needed so the SP can work with the Elmer build. + // it can be removed when the MSINTERNAL stuff is removed + #define MAXMSGSIZEGUIDDEFINED + +// {F5D09980-F0C4-11d1-8326-006097B01411} +DEFINE_GUID(DPAID_MaxMessageSize, +0xf5d09980, 0xf0c4, 0x11d1, 0x83, 0x26, 0x0, 0x60, 0x97, 0xb0, 0x14, 0x11); +//@@END_MSINTERNAL +#endif + +/* + * DPCOMPORTADDRESS + * + * Used to specify com port settings. The constants that define baud rate, + * stop bits and parity are defined in WINBASE.H. The constants for flow + * control are given below. + */ + +#define DPCPA_NOFLOW 0 // no flow control +#define DPCPA_XONXOFFFLOW 1 // software flow control +#define DPCPA_RTSFLOW 2 // hardware flow control with RTS +#define DPCPA_DTRFLOW 3 // hardware flow control with DTR +#define DPCPA_RTSDTRFLOW 4 // hardware flow control with RTS and DTR + +typedef struct _DPCOMPORTADDRESS +{ + DWORD dwComPort; // COM port to use (1-4) + DWORD dwBaudRate; // baud rate (100-256k) + DWORD dwStopBits; // no. stop bits (1-2) + DWORD dwParity; // parity (none, odd, even, mark) + DWORD dwFlowControl; // flow control (none, xon/xoff, rts, dtr) +} DPCOMPORTADDRESS; + +typedef DPCOMPORTADDRESS FAR *LPDPCOMPORTADDRESS; + +/* + * DPAID_ComPort + * + * Chunk contains a DPCOMPORTADDRESS structure defining the serial port. + */ + +// {F2F0CE00-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_ComPort, +0xf2f0ce00, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/**************************************************************************** + * + * dplobby 1.0 obsolete definitions + * Included for compatibility only. + * + ****************************************************************************/ +#define DPLAD_SYSTEM DPLMSG_SYSTEM + + +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + +#endif /* __DPLOBBY_INCLUDED__ */ diff --git a/lib/win/DirectX/dsound.h b/lib/win/DirectX/dsound.h new file mode 100644 index 000000000..0f4b2d3f3 --- /dev/null +++ b/lib/win/DirectX/dsound.h @@ -0,0 +1,863 @@ +/*==========================================================================; + * + * Copyright (C) 1995,1996 Microsoft Corporation. All Rights Reserved. + * + * File: dsound.h + * Content: DirectSound include file + * + **************************************************************************/ + +#ifndef __DSOUND_INCLUDED__ +#define __DSOUND_INCLUDED__ + +#include + +#define COM_NO_WINDOWS_H +#include + +#define _FACDS 0x878 +#define MAKE_DSHRESULT(code) MAKE_HRESULT(1, _FACDS, code) + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Direct Sound Component GUID {47D4D946-62E8-11cf-93BC-444553540000} +DEFINE_GUID(CLSID_DirectSound, 0x47d4d946, 0x62e8, 0x11cf, 0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + +// DirectSound Capture Component GUID {B0210780-89CD-11d0-AF08-00A0C925CD16} +DEFINE_GUID(CLSID_DirectSoundCapture, 0xb0210780, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +// +// Structures +// + +#ifdef __cplusplus +// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined +struct IDirectSound; +struct IDirectSoundBuffer; +struct IDirectSound3DListener; +struct IDirectSound3DBuffer; +struct IDirectSoundCapture; +struct IDirectSoundCaptureBuffer; +struct IDirectSoundNotify; +#endif // __cplusplus + +typedef struct IDirectSound *LPDIRECTSOUND; +typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER; +typedef struct IDirectSound3DListener *LPDIRECTSOUND3DLISTENER; +typedef struct IDirectSound3DBuffer *LPDIRECTSOUND3DBUFFER; +typedef struct IDirectSoundCapture *LPDIRECTSOUNDCAPTURE; +typedef struct IDirectSoundCaptureBuffer *LPDIRECTSOUNDCAPTUREBUFFER; +typedef struct IDirectSoundNotify *LPDIRECTSOUNDNOTIFY; + +typedef struct _DSCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMinSecondarySampleRate; + DWORD dwMaxSecondarySampleRate; + DWORD dwPrimaryBuffers; + DWORD dwMaxHwMixingAllBuffers; + DWORD dwMaxHwMixingStaticBuffers; + DWORD dwMaxHwMixingStreamingBuffers; + DWORD dwFreeHwMixingAllBuffers; + DWORD dwFreeHwMixingStaticBuffers; + DWORD dwFreeHwMixingStreamingBuffers; + DWORD dwMaxHw3DAllBuffers; + DWORD dwMaxHw3DStaticBuffers; + DWORD dwMaxHw3DStreamingBuffers; + DWORD dwFreeHw3DAllBuffers; + DWORD dwFreeHw3DStaticBuffers; + DWORD dwFreeHw3DStreamingBuffers; + DWORD dwTotalHwMemBytes; + DWORD dwFreeHwMemBytes; + DWORD dwMaxContigFreeHwMemBytes; + DWORD dwUnlockTransferRateHwBuffers; + DWORD dwPlayCpuOverheadSwBuffers; + DWORD dwReserved1; + DWORD dwReserved2; +} DSCAPS, *LPDSCAPS; + +typedef const DSCAPS *LPCDSCAPS; + +typedef struct _DSBCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} DSBCAPS, *LPDSBCAPS; + +typedef const DSBCAPS *LPCDSBCAPS; + +typedef struct _DSBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSBUFFERDESC, *LPDSBUFFERDESC; + +typedef const DSBUFFERDESC *LPCDSBUFFERDESC; + +typedef struct _DS3DBUFFER +{ + DWORD dwSize; + D3DVECTOR vPosition; + D3DVECTOR vVelocity; + DWORD dwInsideConeAngle; + DWORD dwOutsideConeAngle; + D3DVECTOR vConeOrientation; + LONG lConeOutsideVolume; + D3DVALUE flMinDistance; + D3DVALUE flMaxDistance; + DWORD dwMode; +} DS3DBUFFER, *LPDS3DBUFFER; + +typedef const DS3DBUFFER *LPCDS3DBUFFER; + +typedef struct _DS3DLISTENER +{ + DWORD dwSize; + D3DVECTOR vPosition; + D3DVECTOR vVelocity; + D3DVECTOR vOrientFront; + D3DVECTOR vOrientTop; + D3DVALUE flDistanceFactor; + D3DVALUE flRolloffFactor; + D3DVALUE flDopplerFactor; +} DS3DLISTENER, *LPDS3DLISTENER; + +typedef const DS3DLISTENER *LPCDS3DLISTENER; + +typedef struct _DSCCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwFormats; + DWORD dwChannels; +} DSCCAPS, *LPDSCCAPS; + +typedef const DSCCAPS *LPCDSCCAPS; + +typedef struct _DSCBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSCBUFFERDESC, *LPDSCBUFFERDESC; + +typedef const DSCBUFFERDESC *LPCDSCBUFFERDESC; + +typedef struct _DSCBCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; +} DSCBCAPS, *LPDSCBCAPS; + +typedef const DSCBCAPS *LPCDSCBCAPS; + +typedef struct _DSBPOSITIONNOTIFY +{ + DWORD dwOffset; + HANDLE hEventNotify; +} DSBPOSITIONNOTIFY, *LPDSBPOSITIONNOTIFY; + +typedef const DSBPOSITIONNOTIFY *LPCDSBPOSITIONNOTIFY; + +// +// Compatibility typedefs +// + +typedef LPDIRECTSOUND *LPLPDIRECTSOUND; +typedef LPDIRECTSOUNDBUFFER *LPLPDIRECTSOUNDBUFFER; +typedef LPDIRECTSOUND3DLISTENER *LPLPDIRECTSOUND3DLISTENER; +typedef LPDIRECTSOUND3DBUFFER *LPLPDIRECTSOUND3DBUFFER; +typedef LPDIRECTSOUNDCAPTURE *LPLPDIRECTSOUNDCAPTURE; +typedef LPDIRECTSOUNDCAPTUREBUFFER *LPLPDIRECTSOUNDCAPTUREBUFFER; +typedef LPDIRECTSOUNDNOTIFY *LPLPDIRECTSOUNDNOTIFY; +typedef LPVOID *LPLPVOID; +typedef const WAVEFORMATEX *LPCWAVEFORMATEX; + +// +// DirectSound API +// + +typedef BOOL (CALLBACK *LPDSENUMCALLBACKW)(LPGUID, LPCWSTR, LPCWSTR, LPVOID); +typedef BOOL (CALLBACK *LPDSENUMCALLBACKA)(LPGUID, LPCSTR, LPCSTR, LPVOID); + +extern HRESULT WINAPI DirectSoundCreate(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); +extern HRESULT WINAPI DirectSoundEnumerateW(LPDSENUMCALLBACKW, LPVOID); +extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA, LPVOID); + +extern HRESULT WINAPI DirectSoundCaptureCreate(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN); +extern HRESULT WINAPI DirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW, LPVOID); +extern HRESULT WINAPI DirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA, LPVOID); + +#ifdef UNICODE +#define LPDSENUMCALLBACK LPDSENUMCALLBACKW +#define DirectSoundEnumerate DirectSoundEnumerateW +#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateW +#else // UNICODE +#define LPDSENUMCALLBACK LPDSENUMCALLBACKA +#define DirectSoundEnumerate DirectSoundEnumerateA +#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateA +#endif // UNICODE + +// +// IDirectSound +// + +DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSound + +DECLARE_INTERFACE_(IDirectSound, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSound methods + STDMETHOD(CreateSoundBuffer) (THIS_ LPCDSBUFFERDESC, LPDIRECTSOUNDBUFFER *, LPUNKNOWN) PURE; + STDMETHOD(GetCaps) (THIS_ LPDSCAPS) PURE; + STDMETHOD(DuplicateSoundBuffer) (THIS_ LPDIRECTSOUNDBUFFER, LPDIRECTSOUNDBUFFER *) PURE; + STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; + STDMETHOD(Compact) (THIS) PURE; + STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE; + STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSound_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSound_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->lpVtbl->CreateSoundBuffer(p,a,b,c) +#define IDirectSound_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->lpVtbl->DuplicateSoundBuffer(p,a,b) +#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IDirectSound_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectSound_GetSpeakerConfig(p,a) (p)->lpVtbl->GetSpeakerConfig(p,a) +#define IDirectSound_SetSpeakerConfig(p,b) (p)->lpVtbl->SetSpeakerConfig(p,b) +#define IDirectSound_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSound_AddRef(p) (p)->AddRef() +#define IDirectSound_Release(p) (p)->Release() +#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->CreateSoundBuffer(a,b,c) +#define IDirectSound_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->DuplicateSoundBuffer(a,b) +#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IDirectSound_Compact(p) (p)->Compact() +#define IDirectSound_GetSpeakerConfig(p,a) (p)->GetSpeakerConfig(a) +#define IDirectSound_SetSpeakerConfig(p,b) (p)->SetSpeakerConfig(b) +#define IDirectSound_Initialize(p,a) (p)->Initialize(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundBuffer +// + +DEFINE_GUID(IID_IDirectSoundBuffer, 0x279AFA85, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSoundBuffer + +DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundBuffer methods + STDMETHOD(GetCaps) (THIS_ LPDSBCAPS) PURE; + STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; + STDMETHOD(GetVolume) (THIS_ LPLONG) PURE; + STDMETHOD(GetPan) (THIS_ LPLONG) PURE; + STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPDIRECTSOUND, LPCDSBUFFERDESC) PURE; + STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD) PURE; + STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE; + STDMETHOD(SetFormat) (THIS_ LPCWAVEFORMATEX) PURE; + STDMETHOD(SetVolume) (THIS_ LONG) PURE; + STDMETHOD(SetPan) (THIS_ LONG) PURE; + STDMETHOD(SetFrequency) (THIS_ DWORD) PURE; + STDMETHOD(Stop) (THIS) PURE; + STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(Restore) (THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) +#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c) +#define IDirectSoundBuffer_GetVolume(p,a) (p)->lpVtbl->GetVolume(p,a) +#define IDirectSoundBuffer_GetPan(p,a) (p)->lpVtbl->GetPan(p,a) +#define IDirectSoundBuffer_GetFrequency(p,a) (p)->lpVtbl->GetFrequency(p,a) +#define IDirectSoundBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) +#define IDirectSoundBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) +#define IDirectSoundBuffer_Play(p,a,b,c) (p)->lpVtbl->Play(p,a,b,c) +#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->lpVtbl->SetCurrentPosition(p,a) +#define IDirectSoundBuffer_SetFormat(p,a) (p)->lpVtbl->SetFormat(p,a) +#define IDirectSoundBuffer_SetVolume(p,a) (p)->lpVtbl->SetVolume(p,a) +#define IDirectSoundBuffer_SetPan(p,a) (p)->lpVtbl->SetPan(p,a) +#define IDirectSoundBuffer_SetFrequency(p,a) (p)->lpVtbl->SetFrequency(p,a) +#define IDirectSoundBuffer_Stop(p) (p)->lpVtbl->Stop(p) +#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) +#define IDirectSoundBuffer_Restore(p) (p)->lpVtbl->Restore(p) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundBuffer_AddRef(p) (p)->AddRef() +#define IDirectSoundBuffer_Release(p) (p)->Release() +#define IDirectSoundBuffer_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b) +#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c) +#define IDirectSoundBuffer_GetVolume(p,a) (p)->GetVolume(a) +#define IDirectSoundBuffer_GetPan(p,a) (p)->GetPan(a) +#define IDirectSoundBuffer_GetFrequency(p,a) (p)->GetFrequency(a) +#define IDirectSoundBuffer_GetStatus(p,a) (p)->GetStatus(a) +#define IDirectSoundBuffer_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g) +#define IDirectSoundBuffer_Play(p,a,b,c) (p)->Play(a,b,c) +#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->SetCurrentPosition(a) +#define IDirectSoundBuffer_SetFormat(p,a) (p)->SetFormat(a) +#define IDirectSoundBuffer_SetVolume(p,a) (p)->SetVolume(a) +#define IDirectSoundBuffer_SetPan(p,a) (p)->SetPan(a) +#define IDirectSoundBuffer_SetFrequency(p,a) (p)->SetFrequency(a) +#define IDirectSoundBuffer_Stop(p) (p)->Stop() +#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d) +#define IDirectSoundBuffer_Restore(p) (p)->Restore() +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSound3DListener +// + +DEFINE_GUID(IID_IDirectSound3DListener, 0x279AFA84, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSound3DListener + +DECLARE_INTERFACE_(IDirectSound3DListener, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSound3D methods + STDMETHOD(GetAllParameters) (THIS_ LPDS3DLISTENER) PURE; + STDMETHOD(GetDistanceFactor) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetDopplerFactor) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetOrientation) (THIS_ LPD3DVECTOR, LPD3DVECTOR) PURE; + STDMETHOD(GetPosition) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(GetRolloffFactor) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetVelocity) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(SetAllParameters) (THIS_ LPCDS3DLISTENER, DWORD) PURE; + STDMETHOD(SetDistanceFactor) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetDopplerFactor) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetOrientation) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetPosition) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetRolloffFactor) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetVelocity) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(CommitDeferredSettings) (THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DListener_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSound3DListener_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSound3DListener_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound3DListener_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a) +#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->lpVtbl->GetDistanceFactor(p,a) +#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->lpVtbl->GetDopplerFactor(p,a) +#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->lpVtbl->GetOrientation(p,a,b) +#define IDirectSound3DListener_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a) +#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->lpVtbl->GetRolloffFactor(p,a) +#define IDirectSound3DListener_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a) +#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b) +#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->lpVtbl->SetDistanceFactor(p,a,b) +#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->lpVtbl->SetDopplerFactor(p,a,b) +#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->lpVtbl->SetOrientation(p,a,b,c,d,e,f,g) +#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d) +#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->lpVtbl->SetRolloffFactor(p,a,b) +#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d) +#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->lpVtbl->CommitDeferredSettings(p) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DListener_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSound3DListener_AddRef(p) (p)->AddRef() +#define IDirectSound3DListener_Release(p) (p)->Release() +#define IDirectSound3DListener_GetAllParameters(p,a) (p)->GetAllParameters(a) +#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->GetDistanceFactor(a) +#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->GetDopplerFactor(a) +#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->GetOrientation(a,b) +#define IDirectSound3DListener_GetPosition(p,a) (p)->GetPosition(a) +#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->GetRolloffFactor(a) +#define IDirectSound3DListener_GetVelocity(p,a) (p)->GetVelocity(a) +#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b) +#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->SetDistanceFactor(a,b) +#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->SetDopplerFactor(a,b) +#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->SetOrientation(a,b,c,d,e,f,g) +#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d) +#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->SetRolloffFactor(a,b) +#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d) +#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->CommitDeferredSettings() +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSound3DBuffer +// + +DEFINE_GUID(IID_IDirectSound3DBuffer, 0x279AFA86, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSound3DBuffer + +DECLARE_INTERFACE_(IDirectSound3DBuffer, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundBuffer3D methods + STDMETHOD(GetAllParameters) (THIS_ LPDS3DBUFFER) PURE; + STDMETHOD(GetConeAngles) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD(GetConeOrientation) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(GetConeOutsideVolume) (THIS_ LPLONG) PURE; + STDMETHOD(GetMaxDistance) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetMinDistance) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetMode) (THIS_ LPDWORD) PURE; + STDMETHOD(GetPosition) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(GetVelocity) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(SetAllParameters) (THIS_ LPCDS3DBUFFER, DWORD) PURE; + STDMETHOD(SetConeAngles) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(SetConeOrientation) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetConeOutsideVolume) (THIS_ LONG, DWORD) PURE; + STDMETHOD(SetMaxDistance) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetMinDistance) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetMode) (THIS_ DWORD, DWORD) PURE; + STDMETHOD(SetPosition) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetVelocity) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSound3DBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSound3DBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a) +#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->lpVtbl->GetConeAngles(p,a,b) +#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->lpVtbl->GetConeOrientation(p,a) +#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->lpVtbl->GetConeOutsideVolume(p,a) +#define IDirectSound3DBuffer_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a) +#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->lpVtbl->GetMinDistance(p,a) +#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->lpVtbl->GetMaxDistance(p,a) +#define IDirectSound3DBuffer_GetMode(p,a) (p)->lpVtbl->GetMode(p,a) +#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a) +#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b) +#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->lpVtbl->SetConeAngles(p,a,b,c) +#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->lpVtbl->SetConeOrientation(p,a,b,c,d) +#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b)(p)->lpVtbl->SetConeOutsideVolume(p,a,b) +#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d) +#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->lpVtbl->SetMinDistance(p,a,b) +#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->lpVtbl->SetMaxDistance(p,a,b) +#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->lpVtbl->SetMode(p,a,b) +#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSound3DBuffer_AddRef(p) (p)->AddRef() +#define IDirectSound3DBuffer_Release(p) (p)->Release() +#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->GetAllParameters(a) +#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->GetConeAngles(a,b) +#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->GetConeOrientation(a) +#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->GetConeOutsideVolume(a) +#define IDirectSound3DBuffer_GetPosition(p,a) (p)->GetPosition(a) +#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->GetMinDistance(a) +#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->GetMaxDistance(a) +#define IDirectSound3DBuffer_GetMode(p,a) (p)->GetMode(a) +#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->GetVelocity(a) +#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b) +#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->SetConeAngles(a,b,c) +#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->SetConeOrientation(a,b,c,d) +#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b)(p)->SetConeOutsideVolume(a,b) +#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d) +#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->SetMinDistance(a,b) +#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->SetMaxDistance(a,b) +#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->SetMode(a,b) +#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundCapture +// + +DEFINE_GUID(IID_IDirectSoundCapture, 0xb0210781, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +#undef INTERFACE +#define INTERFACE IDirectSoundCapture + +DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundCapture methods + STDMETHOD(CreateCaptureBuffer) (THIS_ LPCDSCBUFFERDESC, LPDIRECTSOUNDCAPTUREBUFFER *, LPUNKNOWN) PURE; + STDMETHOD(GetCaps) (THIS_ LPDSCCAPS ) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCapture_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundCapture_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundCapture_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->lpVtbl->CreateCaptureBuffer(p,a,b,c) +#define IDirectSoundCapture_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSoundCapture_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCapture_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundCapture_AddRef(p) (p)->AddRef() +#define IDirectSoundCapture_Release(p) (p)->Release() +#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->CreateCaptureBuffer(a,b,c) +#define IDirectSoundCapture_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSoundCapture_Initialize(p,a) (p)->Initialize(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundCaptureBuffer +// + +DEFINE_GUID(IID_IDirectSoundCaptureBuffer, 0xb0210782, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +#undef INTERFACE +#define INTERFACE IDirectSoundCaptureBuffer + +DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundCaptureBuffer methods + STDMETHOD(GetCaps) (THIS_ LPDSCBCAPS ) PURE; + STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD ) PURE; + STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD ) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD ) PURE; + STDMETHOD(Initialize) (THIS_ LPDIRECTSOUNDCAPTURE, LPCDSCBUFFERDESC) PURE; + STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD) PURE; + STDMETHOD(Start) (THIS_ DWORD) PURE; + STDMETHOD(Stop) (THIS) PURE; + STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCaptureBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundCaptureBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundCaptureBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) +#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c) +#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) +#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) +#define IDirectSoundCaptureBuffer_Start(p,a) (p)->lpVtbl->Start(p,a) +#define IDirectSoundCaptureBuffer_Stop(p) (p)->lpVtbl->Stop(p) +#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCaptureBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundCaptureBuffer_AddRef(p) (p)->AddRef() +#define IDirectSoundCaptureBuffer_Release(p) (p)->Release() +#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b) +#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c) +#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->GetStatus(a) +#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g) +#define IDirectSoundCaptureBuffer_Start(p,a) (p)->Start(a) +#define IDirectSoundCaptureBuffer_Stop(p) (p)->Stop() +#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundNotify +// + +DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +#undef INTERFACE +#define INTERFACE IDirectSoundNotify + +DECLARE_INTERFACE_(IDirectSoundNotify, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundNotify methods + STDMETHOD(SetNotificationPositions) (THIS_ DWORD, LPCDSBPOSITIONNOTIFY) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundNotify_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundNotify_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundNotify_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->lpVtbl->SetNotificationPositions(p,a,b) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundNotify_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundNotify_AddRef(p) (p)->AddRef() +#define IDirectSoundNotify_Release(p) (p)->Release() +#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->SetNotificationPositions(a,b) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IKsPropertySet +// + +#ifndef _IKsPropertySet_ +#define _IKsPropertySet_ + +#ifdef __cplusplus +// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined +struct IKsPropertySet; +#endif // __cplusplus + +typedef struct IKsPropertySet *LPKSPROPERTYSET; + +#define KSPROPERTY_SUPPORT_GET 0x00000001 +#define KSPROPERTY_SUPPORT_SET 0x00000002 + +DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); + +#undef INTERFACE +#define INTERFACE IKsPropertySet + +DECLARE_INTERFACE_(IKsPropertySet, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IKsPropertySet methods + STDMETHOD(Get) (THIS_ REFGUID, ULONG, LPVOID, ULONG, LPVOID, ULONG, PULONG) PURE; + STDMETHOD(Set) (THIS_ REFGUID, ULONG, LPVOID, ULONG, LPVOID, ULONG) PURE; + STDMETHOD(QuerySupport) (THIS_ REFGUID, ULONG, PULONG) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IKsPropertySet_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IKsPropertySet_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IKsPropertySet_Release(p) (p)->lpVtbl->Release(p) +#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->lpVtbl->Get(p,a,b,c,d,e,f,g) +#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->lpVtbl->Set(p,a,b,c,d,e,f) +#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->lpVtbl->QuerySupport(p,a,b,c) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IKsPropertySet_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IKsPropertySet_AddRef(p) (p)->AddRef() +#define IKsPropertySet_Release(p) (p)->Release() +#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->Get(a,b,c,d,e,f,g) +#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->Set(a,b,c,d,e,f) +#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->QuerySupport(a,b,c) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +#endif // _IKsPropertySet_ + +// +// Return Codes +// + +#define DS_OK 0 + +// The call failed because resources (such as a priority level) +// were already being used by another caller. +#define DSERR_ALLOCATED MAKE_DSHRESULT(10) + +// The control (vol,pan,etc.) requested by the caller is not available. +#define DSERR_CONTROLUNAVAIL MAKE_DSHRESULT(30) + +// An invalid parameter was passed to the returning function +#define DSERR_INVALIDPARAM E_INVALIDARG + +// This call is not valid for the current state of this object +#define DSERR_INVALIDCALL MAKE_DSHRESULT(50) + +// An undetermined error occured inside the DirectSound subsystem +#define DSERR_GENERIC E_FAIL + +// The caller does not have the priority level required for the function to +// succeed. +#define DSERR_PRIOLEVELNEEDED MAKE_DSHRESULT(70) + +// Not enough free memory is available to complete the operation +#define DSERR_OUTOFMEMORY E_OUTOFMEMORY + +// The specified WAVE format is not supported +#define DSERR_BADFORMAT MAKE_DSHRESULT(100) + +// The function called is not supported at this time +#define DSERR_UNSUPPORTED E_NOTIMPL + +// No sound driver is available for use +#define DSERR_NODRIVER MAKE_DSHRESULT(120) + +// This object is already initialized +#define DSERR_ALREADYINITIALIZED MAKE_DSHRESULT(130) + +// This object does not support aggregation +#define DSERR_NOAGGREGATION CLASS_E_NOAGGREGATION + +// The buffer memory has been lost, and must be restored. +#define DSERR_BUFFERLOST MAKE_DSHRESULT(150) + +// Another app has a higher priority level, preventing this call from +// succeeding. +#define DSERR_OTHERAPPHASPRIO MAKE_DSHRESULT(160) + +// This object has not been initialized +#define DSERR_UNINITIALIZED MAKE_DSHRESULT(170) + +// The requested COM interface is not available +#define DSERR_NOINTERFACE E_NOINTERFACE + +// +// Flags +// + +#define DSCAPS_PRIMARYMONO 0x00000001 +#define DSCAPS_PRIMARYSTEREO 0x00000002 +#define DSCAPS_PRIMARY8BIT 0x00000004 +#define DSCAPS_PRIMARY16BIT 0x00000008 +#define DSCAPS_CONTINUOUSRATE 0x00000010 +#define DSCAPS_EMULDRIVER 0x00000020 +#define DSCAPS_CERTIFIED 0x00000040 +#define DSCAPS_SECONDARYMONO 0x00000100 +#define DSCAPS_SECONDARYSTEREO 0x00000200 +#define DSCAPS_SECONDARY8BIT 0x00000400 +#define DSCAPS_SECONDARY16BIT 0x00000800 + +#define DSBPLAY_LOOPING 0x00000001 + +#define DSBSTATUS_PLAYING 0x00000001 +#define DSBSTATUS_BUFFERLOST 0x00000002 +#define DSBSTATUS_LOOPING 0x00000004 + +#define DSBLOCK_FROMWRITECURSOR 0x00000001 +#define DSBLOCK_ENTIREBUFFER 0x00000002 + +#define DSSCL_NORMAL 0x00000001 +#define DSSCL_PRIORITY 0x00000002 +#define DSSCL_EXCLUSIVE 0x00000003 +#define DSSCL_WRITEPRIMARY 0x00000004 + +#define DS3DMODE_NORMAL 0x00000000 +#define DS3DMODE_HEADRELATIVE 0x00000001 +#define DS3DMODE_DISABLE 0x00000002 + +#define DS3D_IMMEDIATE 0x00000000 +#define DS3D_DEFERRED 0x00000001 + +#define DS3D_MINDISTANCEFACTOR 0.0f +#define DS3D_MAXDISTANCEFACTOR 10.0f +#define DS3D_DEFAULTDISTANCEFACTOR 1.0f + +#define DS3D_MINROLLOFFFACTOR 0.0f +#define DS3D_MAXROLLOFFFACTOR 10.0f +#define DS3D_DEFAULTROLLOFFFACTOR 1.0f + +#define DS3D_MINDOPPLERFACTOR 0.0f +#define DS3D_MAXDOPPLERFACTOR 10.0f +#define DS3D_DEFAULTDOPPLERFACTOR 1.0f + +#define DS3D_DEFAULTMINDISTANCE 1.0f +#define DS3D_DEFAULTMAXDISTANCE 1000000000.0f + +#define DS3D_MINCONEANGLE 0 +#define DS3D_MAXCONEANGLE 360 +#define DS3D_DEFAULTCONEANGLE 360 + +#define DS3D_DEFAULTCONEOUTSIDEVOLUME 0 + +#define DSBCAPS_PRIMARYBUFFER 0x00000001 +#define DSBCAPS_STATIC 0x00000002 +#define DSBCAPS_LOCHARDWARE 0x00000004 +#define DSBCAPS_LOCSOFTWARE 0x00000008 +#define DSBCAPS_CTRL3D 0x00000010 +#define DSBCAPS_CTRLFREQUENCY 0x00000020 +#define DSBCAPS_CTRLPAN 0x00000040 +#define DSBCAPS_CTRLVOLUME 0x00000080 +#define DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 +#define DSBCAPS_CTRLDEFAULT 0x000000E0 +#define DSBCAPS_CTRLALL 0x000001F0 +#define DSBCAPS_STICKYFOCUS 0x00004000 +#define DSBCAPS_GLOBALFOCUS 0x00008000 +#define DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#define DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 + +#define DSCBCAPS_WAVEMAPPED 0x80000000 + +#define DSSPEAKER_HEADPHONE 0x00000001 +#define DSSPEAKER_MONO 0x00000002 +#define DSSPEAKER_QUAD 0x00000003 +#define DSSPEAKER_STEREO 0x00000004 +#define DSSPEAKER_SURROUND 0x00000005 + +#define DSSPEAKER_GEOMETRY_MIN 0x00000005 // 5 degrees +#define DSSPEAKER_GEOMETRY_NARROW 0x0000000A // 10 degrees +#define DSSPEAKER_GEOMETRY_WIDE 0x00000014 // 20 degrees +#define DSSPEAKER_GEOMETRY_MAX 0x000000B4 // 180 degrees + +#define DSSPEAKER_COMBINED(c, g) ((DWORD)(((BYTE)(c)) | ((DWORD)((BYTE)(g))) << 16)) +#define DSSPEAKER_CONFIG(a) ((BYTE)(a)) +#define DSSPEAKER_GEOMETRY(a) ((BYTE)(((DWORD)(a) >> 16) & 0x00FF)) + +#define DSCCAPS_EMULDRIVER 0x00000020 + +#define DSCBLOCK_ENTIREBUFFER 0x00000001 + +#define DSCBSTATUS_CAPTURING 0x00000001 +#define DSCBSTATUS_LOOPING 0x00000002 + +#define DSCBSTART_LOOPING 0x00000001 + +#define DSBFREQUENCY_MIN 100 +#define DSBFREQUENCY_MAX 100000 +#define DSBFREQUENCY_ORIGINAL 0 + +#define DSBPAN_LEFT -10000 +#define DSBPAN_CENTER 0 +#define DSBPAN_RIGHT 10000 + +#define DSBVOLUME_MIN -10000 +#define DSBVOLUME_MAX 0 + +#define DSBSIZE_MIN 4 +#define DSBSIZE_MAX 0x0FFFFFFF + +#define DSBPN_OFFSETSTOP 0xFFFFFFFF + +#ifdef __cplusplus +}; +#endif // __cplusplus + +#endif // __DSOUND_INCLUDED__ diff --git a/lib/win/DirectX/dsound.lib b/lib/win/DirectX/dsound.lib new file mode 100644 index 000000000..fb4d55989 Binary files /dev/null and b/lib/win/DirectX/dsound.lib differ diff --git a/lib/win/DirectX/dxguid.lib b/lib/win/DirectX/dxguid.lib new file mode 100644 index 000000000..eb5641174 Binary files /dev/null and b/lib/win/DirectX/dxguid.lib differ diff --git a/lib/win/Mvelibw.lib b/lib/win/Mvelibw.lib new file mode 100644 index 000000000..b8432b1c6 Binary files /dev/null and b/lib/win/Mvelibw.lib differ diff --git a/lib/win/arb_extensions.h b/lib/win/arb_extensions.h new file mode 100644 index 000000000..6d799b05f --- /dev/null +++ b/lib/win/arb_extensions.h @@ -0,0 +1,54 @@ +#ifndef ARB_EXTENSIONS_H + +#define ARB_EXTENSIONS_H + +#ifndef APIENTRY +#include +#endif + +/* ARB_multitexture */ +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURES_UNITS_ARB 0x84E2 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 + +/* ARB_multitexture */ +typedef void (APIENTRY * PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (APIENTRY * PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (APIENTRY * PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (APIENTRY * PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (APIENTRY * PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRY * PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRY * PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRY * PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRY * PFNGLACTIVETEXTUREARBPROC) (GLenum target); +typedef void (APIENTRY * PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum target); + +#endif diff --git a/lib/win/eax.lib b/lib/win/eax.lib new file mode 100644 index 000000000..0983675da Binary files /dev/null and b/lib/win/eax.lib differ diff --git a/lib/win/fixwin32.h b/lib/win/fixwin32.h new file mode 100644 index 000000000..e324b4d85 --- /dev/null +++ b/lib/win/fixwin32.h @@ -0,0 +1,71 @@ +/* + * $Logfile: /descent3/main/lib/win/fixwin32.h $ + * $Revision: 3 $ + * $Date: 2/27/97 4:57p $ + * $Author: Samir $ + * + * $Log: /descent3/main/lib/win/fixwin32.h $ + * + * 3 2/27/97 4:57p Samir + * disabled annoying no return value message for this header. + * + * 2 2/27/97 4:49 PM Jeremy + * fixed a typo + * + * 1 2/27/97 4:45 PM Jeremy + * inline assembly language functions for Intel processor for + * fixmul/fixdiv/fixmuldiv + * + * $NoKeywords: $ + */ + +#ifndef _FIXWIN32_H +#define _FIXWIN32_H + +//what does this do? Why didn't Jason put a comment here? +// Jason replies: This pragma disables the "no return value" warning that +// is generated when converting doubles to floats +// A thousand pardons for the confusion + +#pragma warning (disable:4035) + +inline fix FixDiv (fix a,fix b) +{ + __asm + { + mov eax,a + mov ebx,b + cdq + shld edx, eax, 16 + sal eax,16 + idiv ebx + } +} + + +inline fix FixMul (fix a,fix b) +{ + __asm + { + mov eax,a + mov ebx,b + imul ebx + shrd eax,edx,16 + } +} + +inline fix FixMulDiv (fix a,fix b,fix c) +{ + __asm + { + mov eax, a + mov edx, b + mov ebx, c + imul edx + idiv ebx + } +} + +#pragma warning (default:4035) + +#endif diff --git a/lib/win/win32app.h b/lib/win/win32app.h new file mode 100644 index 000000000..93fcc2112 --- /dev/null +++ b/lib/win/win32app.h @@ -0,0 +1,227 @@ +/* + * $Logfile: /DescentIII/Main/Lib/Win/win32app.h $ + * $Revision: 15 $ + * $Date: 5/02/99 3:06p $ + * $Author: Samir $ + * + * Application object for Win32 + * + * $Log: /DescentIII/Main/Lib/Win/win32app.h $ + * + * 15 5/02/99 3:06p Samir + * added handling for power management messages. + * + * 14 4/27/99 2:06p Samir + * added function to get system info. + * + * 13 4/06/99 8:30p Samir + * organized defer code so delay procedure doesn't stall on idle. + * + * 12 10/16/98 11:07a Samir + * new OS version check stuff. + * + * 11 10/08/98 7:26p Samir + * changed the prototype for the defer handler callback. + * + * 10 6/29/98 6:45p Samir + * callback system repaired. + * + * 9 3/23/98 8:04p Samir + * defer handler now returns a bool. + * + * 8 2/23/98 5:07p Samir + * Modified init somewhat. + * + * 7 2/23/98 4:30p Samir + * added init function to oeApplication. + * + * 6 11/17/97 4:57p Samir + * Fixed up winapp data structures. + * + * 5 10/16/97 2:30p Samir + * Added Idle processing. + * + * 4 9/16/97 1:04p Samir + * Added delay function. + * + * 3 8/01/97 7:30p Samir + * Better messaging support and NT support. + * + * 2 7/28/97 3:46p Samir + * Added Topmost window optional support and NT detection. + * + * 2 6/11/97 2:39p Samir + * Added destructors. + * + * 1 6/10/97 4:54p Samir + * Win32 Application object conversion from old osWinObject. + * + * $NoKeywords: $ + */ + +#ifndef WIN32APP_H +#define WIN32APP_H + +#define MAX_MSG_FUNCTIONS 64 + +/* Basic Application Win32 data types */ +typedef unsigned int HWnd; +typedef unsigned int HInstance; + +// This structure is used to retrieve and set +typedef struct tWin32AppInfo { + unsigned flags; // Application Flags + HWnd hwnd; // Window Handle + HInstance hinst; // Window Instance + int wnd_x, wnd_y, wnd_w, wnd_h; // Window dimensions +} tWin32AppInfo; + +typedef enum tWin32OS { + NoWin32, + Win9x, + WinNT, + WinCE +} tWin32OS; + +/* Win32 Application Object + This object entails initialization and cleanup of all operating system + elements, as well as data that libraries may need to initialize their + systems. + + The Win32 Application object creates the application window and housekeeps + the window and instance handle for the application. + + We also allow the option of setting these handles from outside the Application object. + + +tOEWin32MsgCallback: + Callbacks return a 0 if we don't want to call the default action for the message, otherwise return 1 + Callbacks are executed in the Window's message procedure, so the calling program need not manually run + the handler (although, I give you a function to do just that, if you can't depend on the WndProc). + NOTE: the callbacks are executed in the window's root procedure and not in the inherited WndProc function. + This means, that callbacks have priority over the application defined WndProc. + + General priority of message handling. + Outrage Window Procedure: + takes care of window creation, destruction and system menus. + if message's window not registered then + calls Win32 Window Procedure + else + runs OEWin32MsgCallbacks for that message + if OEWin32MsgCallback functions return 0 then + leave Outrage Window Procedure + + calls oeWin32Application->WndProc hierarchy which + optionally calls Win32 Window Procedure. + endif +*/ + +typedef int (*tOEWin32MsgCallback)(HWnd,unsigned,unsigned,long); + +class oeWin32Application: public oeApplication +{ +#if defined(OEAPP_INTERNAL_MODULE) +public: +#else +private: +#endif + bool m_WasCreated; // Tells us if this app created the window handle or not. + + int m_NumMsgFn; // Number of message functions. + + struct { // assign functions to messages. + unsigned msg; + tOEWin32MsgCallback fn; + } + m_MsgFn[MAX_MSG_FUNCTIONS]; + + bool m_NTFlag; // Are we in NT? + + void (*m_DeferFunc)(bool); // function to call when deffering to OS (OnIdle for instance) + + char m_WndName[64]; // name of window. + + static bool os_initialized; // is the OS check initialized? + static bool first_time; // first time init? + +private: + int defer_block(); // real defer code. + +public: +// Creates the window handle + oeWin32Application(const char *name, unsigned flags, HInstance hinst); + +// Create object with a premade window handle/instance +// we just give it the window handle, instance handle and flags + oeWin32Application(tWin32AppInfo *appinfo); + + virtual ~oeWin32Application(); + +// initializes the object + virtual void init(); + +// Function to retrieve information from object through a platform defined structure. + virtual void get_info(void *appinfo); + + virtual int flags(void) const; + +// defer returns some flags. essentially this function defers program control to OS. + virtual unsigned defer(); + +// set a function to run when deferring to OS. + virtual void set_defer_handler(void (*func)(bool)); + +// delays app for a certain amount of time + virtual void delay(float secs); + +// Sizes the displayable region of the app (the window) + void set_sizepos(int x, int y, int w, int h); + +// returns -1 if we pass to default window handler. + virtual int WndProc( HWnd hwnd, unsigned msg, unsigned wParam, long lParam); + +// These functions allow you to add message handlers. + bool add_handler(unsigned msg, tOEWin32MsgCallback fn); + +// These functions remove a handler + bool remove_handler(unsigned msg, tOEWin32MsgCallback fn); + +// Run handler for message (added by add_handler) + bool run_handler(HWnd wnd, unsigned msg, unsigned wParam, long lParam); + +// clears handler list + void clear_handlers(); + +// tells us if we're in NT + bool NT() const { return m_NTFlag; } + +// retreive full version information + static tWin32OS version(int *major, int *minor, int *build=NULL, char *desc=NULL); + +// detect if application can handle what we want of it. + static bool GetSystemSpecs(const char *fname); + +// These variables are only accessable to modules that have DD_ACCESS. +#if defined(DD_ACCESS_RING) +public: +#else +private: +#endif + HWnd m_hWnd; // handles created by the system + HInstance m_hInstance; + unsigned m_Flags; + int m_X, m_Y, m_W, m_H; // window dimensions. + +private: + void os_init(); // initializes OS components. +}; + + +// the following data is communicated by the application library to other DDAccessed libraries. +#if defined(DD_ACCESS_RING) + +// system mouse info. +extern short w32_msewhl_delta; // value of mouse wheel delta for frame + +#endif +#endif \ No newline at end of file diff --git a/lib/win/win32database.h b/lib/win/win32database.h new file mode 100644 index 000000000..88ebb2762 --- /dev/null +++ b/lib/win/win32database.h @@ -0,0 +1,82 @@ +/* + * $Logfile: /DescentIII/Main/lib/win/win32database.h $ + * $Revision: 6 $ + * $Date: 5/11/99 12:12a $ + * $Author: Ardussi $ + * + * Application Database for Win32 version + * + * $Log: /DescentIII/Main/lib/win/win32database.h $ + * + * 6 5/11/99 12:12a Ardussi + * changes to compile on Mac + * + * 5 4/24/99 5:47p Samir + * added functions to set current win32 resource dll or exe. + * + * 4 7/27/97 11:07p Matt + * Added write_string() macro + * + * 3 7/24/97 3:06p Matt + * Added functions to read & write bools & variable-length integers, and + * fixed a few small bugs. + * + * 2 6/11/97 2:41p Samir + * fixed class declaration. + * + * 1 6/10/97 4:53p Samir + * Database control for Win32 systems + * + * $NoKeywords: $ + */ + +#ifndef WIN32DATABASE +#define WIN32DATABASE + + +/* oeWin32AppDatabase + to get info about the application from a managed database (or a custom info file) + we get our information from the registry! +*/ + +class oeWin32AppDatabase: public oeAppDatabase +{ + unsigned hBaseKey; // look up from this key. + unsigned hCurKey; // current key for lookup + +protected: + char m_Basepath[256]; + +public: + oeWin32AppDatabase(); + oeWin32AppDatabase(oeWin32AppDatabase *parent); + virtual ~oeWin32AppDatabase(); + +// creates an empty classification or structure where you can store information + virtual bool create_record(const char *pathname); + +// set current database focus to a particular record + virtual bool lookup_record(const char *pathname); + +// read either an integer or string from the current record + virtual bool read(const char *label, char *entry, int *entrylen); + virtual bool read(const char *label, void *entry, int wordsize); //read a variable-size int + virtual bool read(const char *label, bool *entry); + +// write either an integer or string to a record. + virtual bool write(const char *label, const char *entry, int entrylen); + virtual bool write(const char *label, int entry); + +// get the current user's name. + virtual void get_user_name(char* buffer, ulong* size); +}; + +// pass name of dll which contains desired language +// NULL library is the default resource DLL +bool win32_SetResourceDLL(const char *libname); + +// returns a string from the current resource +bool win32_GetStringResource(int txt_id, char *buffer, int buflen); + + +#endif \ No newline at end of file diff --git a/lib/win/wincontroller.h b/lib/win/wincontroller.h new file mode 100644 index 000000000..22951453f --- /dev/null +++ b/lib/win/wincontroller.h @@ -0,0 +1,298 @@ +/* + * $Logfile: /DescentIII/Main/Lib/Win/WinController.h $ + * $Revision: 27 $ + * $Date: 7/26/99 12:00p $ + * $Author: Samir $ + * + * Win32 controller header. + * + * $Log: /DescentIII/Main/Lib/Win/WinController.h $ + * + * 27 7/26/99 12:00p Samir + * added code to read config files for different controllers. + * + * 26 7/20/99 4:53p Samir + * added ability to manually set the deadzone for a controller. + * + * 25 7/16/99 11:14a Samir + * multiple hat support and improved direct input support. + * + * 24 5/03/99 12:36p Samir + * poll mouse deltas and button masks not as often, to allow deltas to + * accumulate. also decreased normalizer for mouse axes'. + * + * 23 4/29/99 2:23a Samir + * moved binding text functions to wincontroller.cpp and new text for + * multiple joysticks. + * + * 22 4/26/99 4:31p Samir + * get_mouse_raw_values now returns screen coords, not deltas. + * + * 21 4/24/99 5:39p Samir + * mouse control now framerate independant. + * + * 20 4/16/99 2:02p Kevin + * Added mouselook support + * + * 19 2/16/99 11:59a Samir + * added proper constants for controller and binding null values. + * + * 18 12/18/98 6:00p Samir + * added enable_function. + * + * 17 10/24/98 2:18p Samir + * added mouse and joytick raw value retrieval functions. + * + * 16 10/21/98 10:36a Samir + * added code to turn on or off joystick or mouse. + * + * 15 10/18/98 7:29p Samir + * made assign_function public. + * + * 14 10/17/98 7:31p Samir + * added invertible axes + * + * 13 9/10/98 12:39p Samir + * added senstivity issures for controller. + * + * 12 6/29/98 6:47p Samir + * made suspended flag an int. + * + * 11 6/18/98 4:48p Samir + * added changes for multiple configs for joystick controls. + * + * 10 2/24/98 11:03a Samir + * Added flush function to controller system. + * + * 9 2/17/98 10:59a Samir + * Added invalid controller type. + * + * 8 2/16/98 3:04p Samir + * ctAxis instead of ctXAxis, ctYAxis, etc. + * + * 7 2/13/98 6:38p Samir + * Added get and set controller function. + * + * 6 12/05/97 12:49p Samir + * POV timing support + * + * 5 12/05/97 10:58a Samir + * Null zone for joysticks applied here. + * + * 4 12/03/97 7:35p Samir + * Newer joystick library support and some POV. + * + * 3 11/05/97 3:46p Samir + * Use C runtime calls for thread creattion. + * + * 2 10/29/97 4:44p Samir + * Added ctDownCount format. + * + * 7 5/12/97 1:20p Samir + * Preemptive thread for joystick buttons. + * + * 6 4/23/97 1:07p Samir + * Now we can poll for either positonal or evaluator data. + * + * 5 4/16/97 1:04p Samir + * For get packet, allow one to return an alternate format value if that + * function supports it. + * + * 4 4/16/97 12:27p Samir + * Added mouse support. + * + * 3 4/14/97 12:56p Samir + * Added information for button timings. + * + * 2 4/11/97 2:13p Samir + * Win32 controller interface works for keyboards and DirectInput devices + * through (not mouse yet). + * + * $NoKeywords: $ + */ + +#ifndef WINCONTROLLER_H +#define WINCONTROLLER_H + +#include "Controller.h" +#include "joystick.h" + +#define NULL_WINCONTROLLER ((sbyte)NULL_CONTROLLER) + +const int CTF_POV = 64, // POV control + CTF_POV2 = 128, // POV 2 + CTF_POV3 = 256, // POV 3 + CTF_POV4 = 512; // POV 4 + + + +const unsigned CTF_X_AXIS = (1<<(CT_X_AXIS-1)), // AXIS constants for ctAxis + CTF_Y_AXIS = (1<<(CT_Y_AXIS-1)), + CTF_Z_AXIS = (1<<(CT_Z_AXIS-1)), + CTF_R_AXIS = (1<<(CT_R_AXIS-1)), + CTF_U_AXIS = (1<<(CT_U_AXIS-1)), + CTF_V_AXIS = (1<<(CT_V_AXIS-1)); + +const unsigned CT_MAX_CONTROLLERS = 32, + CT_MAX_ELEMENTS = 255, + CT_MAX_EXTCTLS = 16, + CT_MAX_BUTTONS = 32; + +// rules for adding controllers +// any nonstandard special controllers should be added to the below list starting at +// CTID_MOUSE-1 (meaning a value of -3, -4 and so on) + +const int CTID_KEYBOARD = -1, // always -1 for keyboards + CTID_MOUSE = -2, // always -2 for mice + CTID_INVALID = -3; // invalid controller + +// External controls +// these are standard controllers handled through DDIO interface +// like joysticks, etc. + +const int CTID_EXTCONTROL0 = 0; + + +class gameWinController: public gameController +{ +public: +// initialization of controller with needs + gameWinController(int num_funcs, ct_function *funcs, char *remote_adr=NULL); + virtual ~gameWinController(); + + virtual void suspend(); + virtual void resume(); + +// this functions polls the controllers if needed. some systems may not need to implement +// this function. + virtual void poll(); + +// flushes all controller information + virtual void flush(); + +// returns the value of a requested controller type. + virtual ct_config_data get_controller_value(ct_type type_req); + +// sets the configuration of a function + virtual void set_controller_function(int id, const ct_type *type, ct_config_data value, const ubyte *flags); + +// returns information about a requested function + virtual void get_controller_function(int id, ct_type *type, ct_config_data *value, ubyte *flags); + +// temporarily enables or disables a function + virtual void enable_function(int id, bool enable); + + virtual bool get_packet(int id, ct_packet *packet, ct_format alt_format=ctNoFormat); + +// gets sensitivity of axis item + virtual float get_axis_sensitivity(ct_type axis_type, ubyte axis); + +// sets sensitivity of axis item + virtual void set_axis_sensitivity(ct_type axis_type, ubyte axis, float val); + +// assigns an individual function + virtual int assign_function(ct_function *fn); + +// activates or deactivates mouse and or controller + virtual void mask_controllers(bool joystick, bool mouse); + +// retrieves binding text for desired function, binding, etc. + virtual const char *get_binding_text(ct_type type, ubyte ctrl, ubyte bind); + +// get raw values for the controllers + virtual int get_mouse_raw_values(int *x, int *y); + virtual unsigned get_joy_raw_values(int *x, int *y); + +// toggles use of deadzone for controllers. ctl can be 0 to ??? +// dead zone is from 0.0 to 0.5 + void set_controller_deadzone(int ctl, float deadzone); + + +private: + int m_NumControls; // number of controllers available + int m_Suspended; // is controller polling suspended? + bool m_JoyActive, m_MouseActive; // enables or disables mouse, joystick control + + struct t_controller { + int id; + ushort flags; + ushort buttons; + unsigned btnmask; + float normalizer[CT_NUM_AXES]; + float sens[CT_NUM_AXES]; + float sensmod[CT_NUM_AXES]; + float deadzone; + } m_ControlList[CT_MAX_CONTROLLERS]; // the control list. + + struct ct_element { + ct_format format; + sbyte ctl[CTLBINDS_PER_FUNC]; + ubyte value[CTLBINDS_PER_FUNC]; + ct_type ctype[CTLBINDS_PER_FUNC]; + ubyte flags[2]; + bool enabled; + } m_ElementList[CT_MAX_ELEMENTS]; + + bool enum_controllers(char *remote_adr); + +// sets up an elements information structure + void assign_element(int id, ct_element *elem); + +// this returns an index into the control list. + sbyte get_axis_controller(ubyte axis); + +// returns controller with specified button + sbyte get_button_controller(ubyte btn); + +// returns the controller with a pov hat + sbyte get_pov_controller(ubyte pov); + +// note controller is index into ControlList. + float get_axis_value(sbyte controller, ubyte axis, ct_format format,bool invert = false); + +// get value of button in seconds, presses, etc. + float get_button_value(sbyte controller, ct_format format, ubyte button); + +// get value of pov (using JOYPOV values) + float get_pov_value(sbyte controller, ct_format format, ubyte pov_number, ubyte pov); + +// get keyboard info + float get_key_value(int key, ct_format format); + +// okay, now search for a '****.ctl' file in the current directory. + void parse_ctl_file(int devnum, const char *ctlname); + +private: + struct t_msestate + { + int m_deltaX, m_deltaY, m_deltaZ; + int m_absX, m_absY; + unsigned int m_buttonMask; + }m_MseState; + + struct t_extctlstate { + int x,y,z,r,u,v; + int pov[JOYPOV_NUM]; + int last_pov[JOYPOV_NUM]; + float povstarts[JOYPOV_NUM][JOYPOV_DIR]; + float povtimes[JOYPOV_NUM][JOYPOV_DIR]; + ubyte povpresses[JOYPOV_NUM][JOYPOV_DIR]; + unsigned buttons; + ubyte btnpresses[CT_MAX_BUTTONS]; + float btnstarts[CT_MAX_BUTTONS]; + float btntimes[CT_MAX_BUTTONS]; + } m_ExtCtlStates[CT_MAX_EXTCTLS]; + +// thread info. + longlong m_frame_timer_ms; + float m_frame_time; + +// note id is id value from controller in control list. + void extctl_getpos(int id); + void extctl_geteval(int id); + +// this gets timings for mouse buttons + void mouse_geteval(); +}; + +#endif diff --git a/libacm/CMakeLists.txt b/libacm/CMakeLists.txt new file mode 100644 index 000000000..b456c381a --- /dev/null +++ b/libacm/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (HEADERS ) +SET (CPPS + aencode.cpp + libacm.cpp) + +ADD_LIBRARY(libacm STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/libacm/aencode.cpp b/libacm/aencode.cpp new file mode 100644 index 000000000..38325af57 --- /dev/null +++ b/libacm/aencode.cpp @@ -0,0 +1,1263 @@ +#include +#include +#include +#include +#include +#include "Aencode.h" + +typedef unsigned int uint32; +typedef signed int sint32; +typedef unsigned short uint16; +typedef signed short sint16; +typedef unsigned char uint8; +typedef signed char sint8; + +struct BitsEncoder +{ + FILE* m_outFile; // var50 | offset 0x10 + uint32 m_bitData; // var4C | offset 0x14 + uint32 m_bitCount; // var48 | offset 0x18 + + void WriteBits( sint32 val, uint32 numBits ) + { + assert( (numBits + m_bitCount) <= 32 ); + m_bitData |= static_cast( val << m_bitCount ); + m_bitCount += numBits; + + while( m_bitCount >= 8 ) + { + uint8 v = m_bitData & 0xFF; + putc( v, m_outFile ); + + m_bitData >>= 8; + m_bitCount -= 8; + } + } + + void Flush() + { + while( m_bitCount >= 8 ) + { + uint8 v = m_bitData & 0xFF; + putc( v, m_outFile ); + + m_bitData >>= 8; + m_bitCount -= 8; + } + + if( m_bitCount > 0 ) + { + uint8 v = m_bitData & 0xFF; + putc( v, m_outFile ); + m_bitCount = 0; + m_bitData = 0; + } + } +}; + +struct Encoder +{ + ReadSampleFunction* m_reader; // var60 | offset 0x00 + void* m_pReaderData; // var5C | offset 0x04 + uint32 m_sampleCount; // var58 | offset 0x08 + float m_volume; // var54 | offset 0x0C + BitsEncoder m_bits; // var50 - var48 | offset 0x10 - 0x18 + sint8 m_levels; // var44* | offset 0x1C + sint8 m_pad[3]; // 43, 42, 41 + sint32 m_numColumns; // var40 | offset 0x20 + sint32 m_samples_per_subband; // var3C | offset 0x24 + sint32 m_samplesPerBlock; // var38 | offset 0x28 + sint32 m_adjustedSamplesTimeNumColumns; // var34 | offset 0x2C + float** m_levelSlots; // var30 | offset 0x30 + float* m_pCurrBlockData; // var2C | offset 0x34 + sint32 m_blockSamplesRemaining; // var28 | offset 0x38 + sint32 m_bandWriteEnabled; // var24 | offset 0x3C + sint32 m_finishedReading; // var20 | offset 0x40 + sint32 m_someVal; // var1C | offset 0x44 + float* m_lo_filter; // var18 | offset 0x48 + float* m_hi_filter; // var14 | offset 0x4C + uint32* m_pFormatIdPerColumn; // var10 | offset 0x50 + sint32 m_currBlockBitPower; // var0C | offset 0x54 + sint32 m_currBlockBitValue; // var08 | offset 0x58 + sint32 m_threshold; // var04 | offset 0x5C +}; + +typedef void (*WriteBandFunc)(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt0(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt3_16(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt17(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt18(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt19(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt20(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt21(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt22(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt23(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt24(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt26(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt27(Encoder& enc, sint32 colIndex, uint32 packerId); +void WriteBand_Fmt29(Encoder& enc, sint32 colIndex, uint32 packerId); + +WriteBandFunc WriteBand_tbl[] = +{ + WriteBand_Fmt0, NULL, NULL, WriteBand_Fmt3_16, + WriteBand_Fmt3_16, WriteBand_Fmt3_16, WriteBand_Fmt3_16, WriteBand_Fmt3_16, + WriteBand_Fmt3_16, WriteBand_Fmt3_16, WriteBand_Fmt3_16, WriteBand_Fmt3_16, + WriteBand_Fmt3_16, WriteBand_Fmt3_16, WriteBand_Fmt3_16, WriteBand_Fmt3_16, + WriteBand_Fmt3_16, WriteBand_Fmt17, WriteBand_Fmt18, WriteBand_Fmt19, + WriteBand_Fmt20, WriteBand_Fmt21, WriteBand_Fmt22, WriteBand_Fmt23, + WriteBand_Fmt24, NULL, WriteBand_Fmt26, WriteBand_Fmt27, + NULL, WriteBand_Fmt29, NULL, NULL +}; + +float std_lo_filter[] = +{ + -0.0012475221f, -0.0024950907f, + 0.0087309526f, 0.019957958f, + -0.050528999f, -0.12055097f, + 0.29304558f, 0.70617616f, +}; + +float std_hi_filter[] = +{ + 0.0012475221f, -0.0024950907f, + -0.0087309526f, 0.019957958f, + 0.050528999f, -0.12055097f, + -0.29304558f, 0.70617616f, +}; + +const float T911 = -32767.0f; +const float T913 = 32767.0f; +const float T1266 = 0.0f; + +void WriteBand_Fmt0(Encoder& enc, sint32 colIndex, uint32 formatId) +{ +} + +void WriteBand_Fmt3_16(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockValue = float(enc.m_currBlockBitValue); + const float halfCurrBlockValue = currBlockValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockValue ); + + float* pColumnData = &enc.m_levelSlots[enc.m_levels][colIndex]; + for( sint32 i = 0; i < enc.m_samples_per_subband; ++i ) + { + sint32 val = (sint32)floorf( ( *pColumnData + halfCurrBlockValue ) / currBlockValue ); + if( minValue > val ) + { + val = minValue; + } + else if( val > maxValue ) + { + val = maxValue; + } + + pColumnData += enc.m_numColumns; + enc.m_bits.WriteBits( val + (1 << (formatId - 1)), formatId ); + } +} + +void WriteBand_Fmt17(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBitValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBitValue = currBitValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBitValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBitValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 val = (sint32)floorf( ( *pCurrSample + halfCurrBitValue ) / currBitValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pCurrSample += enc.m_numColumns; + if( val == 0 ) + { + if( currSampleIndex != 0 && !(int)floorf( ( *pCurrSample + halfCurrBitValue ) / currBitValue ) ) + { + enc.m_bits.WriteBits( 0, 1 ); + + if( currSampleIndex == 0 ) + return; + --currSampleIndex; + pCurrSample += enc.m_numColumns; + continue; + } + + enc.m_bits.WriteBits( 1, 2 ); + continue; + } + + enc.m_bits.WriteBits( 3, 2 ); + enc.m_bits.WriteBits( (val == 1) ? 1 : 0, 1 ); + } +} + +void WriteBand_Fmt18(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockBitValue = static_cast(enc.m_currBlockBitValue); + const float halfCurrBlockBitValue = enc.m_currBlockBitValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockBitValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockBitValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 val = (sint32)floorf( ( *pCurrSample + halfCurrBlockBitValue ) / currBlockBitValue ); + if( minValue > val ) + { + val = minValue; + } + else if( val > maxValue ) + { + val = maxValue; + } + + pCurrSample += enc.m_numColumns; + + if( !val ) + { + enc.m_bits.WriteBits( 0, 1 ); + continue; + } + + enc.m_bits.WriteBits( 1, 1 ); + enc.m_bits.WriteBits( (val == 1) ? 1 : 0, 1 ); + } +} + +void WriteBand_Fmt19(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockBitValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockBitValue = currBlockBitValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockBitValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockBitValue ); + + const float* pCurrSample = &enc.m_levelSlots[ enc.m_levels ][ colIndex ]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 workingVal = (sint32)floorf( ( *pCurrSample + halfCurrBlockBitValue ) / currBlockBitValue ); + if( minValue > workingVal ) + { + workingVal = minValue; + } + else if( maxValue < workingVal ) + { + workingVal = maxValue; + } + pCurrSample += enc.m_numColumns; + sint32 baseValue = workingVal + 1; + if( currSampleIndex ) + { + --currSampleIndex; + workingVal = (sint32)floorf( ( *pCurrSample + halfCurrBlockBitValue ) / currBlockBitValue ); + if( minValue > workingVal ) + { + workingVal = minValue; + } + else if( maxValue < workingVal ) + { + workingVal = maxValue; + } + pCurrSample += enc.m_numColumns; + } + else + { + workingVal = 0; + } + + baseValue += workingVal*3 + 3; + if( currSampleIndex ) + { + --currSampleIndex; + workingVal = (sint32)floorf( ( *pCurrSample + halfCurrBlockBitValue ) / currBlockBitValue ); + if( minValue > workingVal ) + { + workingVal = minValue; + } + else if( maxValue < workingVal ) + { + workingVal = maxValue; + } + pCurrSample += enc.m_numColumns; + } + else + { + workingVal = 0; + } + + enc.m_bits.WriteBits( workingVal*9 + 9 + baseValue, 5 ); + } +} + +void WriteBand_Fmt20(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockValue = currBlockValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 val = (sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pCurrSample += enc.m_numColumns; + if( val == 0 ) + { + if( currSampleIndex != 0 && !(sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ) ) + { + enc.m_bits.WriteBits( 0, 1 ); + + if( currSampleIndex == 0 ) + return; + --currSampleIndex; + pCurrSample += enc.m_numColumns; + continue; + } + enc.m_bits.WriteBits( 1, 2 ); + continue; + } + enc.m_bits.WriteBits( 3, 2 ); + if( val < 0 ) + { + val += 2; + } + else + { + ++val; + } + + enc.m_bits.WriteBits( val, 2 ); + } +} + +void WriteBand_Fmt21(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockValue = currBlockValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 val = (sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pCurrSample += enc.m_numColumns; + if( !val) + { + enc.m_bits.WriteBits( 0, 1 ); + continue; + } + enc.m_bits.WriteBits( 1, 1 ); + + if( val < 0 ) + { + val += 2; + } + else + { + ++val; + } + + enc.m_bits.WriteBits( val, 2 ); + } +} + +void WriteBand_Fmt22(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockValue = currBlockValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 workingVal = (sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ); + if( minValue > workingVal ) + { + workingVal = minValue; + } + else if( maxValue < workingVal ) + { + workingVal = maxValue; + } + + sint32 baseValue = workingVal + 2; + pCurrSample += enc.m_numColumns; + if( currSampleIndex ) + { + --currSampleIndex; + workingVal = (sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ); + if( minValue > workingVal ) + { + workingVal = minValue; + } + else if( maxValue < workingVal ) + { + workingVal = maxValue; + } + pCurrSample += enc.m_numColumns; + } + else + { + workingVal = 0; + } + + baseValue += workingVal*5 + 10; + if( currSampleIndex ) + { + --currSampleIndex; + workingVal = (sint32)floorf( (*pCurrSample + halfCurrBlockValue) / currBlockValue ); + if( minValue > workingVal ) + { + workingVal = minValue; + } + else if( maxValue < workingVal ) + { + workingVal = maxValue; + } + pCurrSample += enc.m_numColumns; + } + else + { + workingVal = 0; + } + + enc.m_bits.WriteBits( workingVal*25 + 50 + baseValue, 7 ); + } +} + +void WriteBand_Fmt23(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockValue = currBlockValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + + sint32 val = (sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pCurrSample += enc.m_numColumns; + if( !val ) + { + if( currSampleIndex != 0 ) + { + if( !(sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ) ) + { + enc.m_bits.WriteBits( 0, 1 ); + + if( currSampleIndex == 0 ) + return; + --currSampleIndex; + + pCurrSample += enc.m_numColumns; + continue; + } + } + + enc.m_bits.WriteBits( 1, 2 ); + continue; + } + + enc.m_bits.WriteBits( 3, 2 ); + + if( val != -1 && val != 1 ) + { + enc.m_bits.WriteBits( 1, 1 ); + + if( val < 0 ) + { + val += 3; + } + + enc.m_bits.WriteBits( val, 2 ); + continue; + } + + enc.m_bits.WriteBits( 0, 1 ); + enc.m_bits.WriteBits( ( val == 1 ) ? 1 : 0, 1 ); + } +} + +void WriteBand_Fmt24(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockBitValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockBitValue = currBlockBitValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockBitValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockBitValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 val = (sint32)floorf( ( *pCurrSample + halfCurrBlockBitValue ) / currBlockBitValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pCurrSample += enc.m_numColumns; + if( !val ) + { + enc.m_bits.WriteBits( 0, 1 ); + continue; + } + + enc.m_bits.WriteBits( 1, 1 ); + + if( val != -1 && val != 1 ) + { + enc.m_bits.WriteBits( 1, 1 ); + + if( val < 0 ) + { + val += 3; + } + enc.m_bits.WriteBits( val, 2 ); + continue; + } + + enc.m_bits.WriteBits( 0, 1 ); + enc.m_bits.WriteBits( (val == 1) ? 1 : 0, 1 ); + } +} + +void WriteBand_Fmt26(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockValue = currBlockValue * 0.5f; + const sint32 minValue = static_cast( ceilf( -32767.0f / currBlockValue ) ); + const sint32 maxValue = static_cast( floorf( 32767.0f / currBlockValue ) ); + const float* pColumnData = &enc.m_levelSlots[enc.m_levels][colIndex]; + + sint32 currSampleIdx = enc.m_samples_per_subband; + while( currSampleIdx ) + { + --currSampleIdx; + + sint32 val = (sint32)floorf( ( *pColumnData + halfCurrBlockValue ) / currBlockValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pColumnData += enc.m_numColumns; + if( !val ) + { + if( currSampleIdx ) + { + sint32 testVal = (sint32)floorf( ( *pColumnData + halfCurrBlockValue ) / currBlockValue ); + if( !testVal ) + { + enc.m_bits.WriteBits( 0, 1 ); + + if( currSampleIdx == 0 ) + return; + + --currSampleIdx; + pColumnData += enc.m_numColumns; + continue; + } + } + + enc.m_bits.WriteBits( 1, 2 ); + continue; + } + + enc.m_bits.WriteBits( 3, 2 ); + + if( val >= 0 ) + { + val += 3; + } + else + { + val += 4; + } + + enc.m_bits.WriteBits( val, 3 ); + } +} + +void WriteBand_Fmt27(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBlockValue = static_cast( enc.m_currBlockBitValue ); + const float halfCurrBlockValue = currBlockValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBlockValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBlockValue ); + + const float* pCurrSample = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + sint32 val = (sint32)floorf( ( *pCurrSample + halfCurrBlockValue ) / currBlockValue ); + if( val < minValue ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pCurrSample += enc.m_numColumns; + if( !val ) + { + enc.m_bits.WriteBits( 0, 1 ); + continue; + } + + enc.m_bits.WriteBits( 1, 1 ); + + if( val < 0 ) + { + val += 4; + } + else + { + val += 3; + } + + enc.m_bits.WriteBits( val, 3 ); + } +} + +void WriteBand_Fmt29(Encoder& enc, sint32 colIndex, uint32 formatId) +{ + const float currBitValue = (float)enc.m_currBlockBitValue; + const float halfCurrBitValue = currBitValue * 0.5f; + const sint32 minValue = (sint32)ceilf( -32767.0f / currBitValue ); + const sint32 maxValue = (sint32)floorf( 32767.0f / currBitValue ); + + const float* pColumnData = &enc.m_levelSlots[enc.m_levels][colIndex]; + sint32 currSampleIndex = enc.m_samples_per_subband; + while( currSampleIndex ) + { + --currSampleIndex; + + sint32 val = (sint32)floorf( ( *pColumnData + halfCurrBitValue ) / currBitValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + sint32 baseValue = val + 5; + pColumnData += enc.m_numColumns; + if( currSampleIndex != 0 ) + { + --currSampleIndex; + + val = (sint32)floorf( ( *pColumnData + halfCurrBitValue ) / currBitValue ); + if( minValue > val ) + { + val = minValue; + } + else if( maxValue < val ) + { + val = maxValue; + } + + pColumnData += enc.m_numColumns; + } + else + { + val = 0; + } + + enc.m_bits.WriteBits( 11*val + 55 + baseValue, 7 ); + } +} + +int ReadSample_init( Encoder& enc, ReadSampleFunction *read, void *data ) +{ + enc.m_reader = read; + enc.m_pReaderData = data; + return 1; +} + +int bits_init( BitsEncoder& bits, FILE* out ) +{ + bits.m_outFile = out; + bits.m_bitData = 0; + bits.m_bitCount = 0; + return 1; +} + +int SetupEncoder( Encoder& enc, int someVal, float std_lo_filter[], float std_hi_filter[], sint8 levels, int samples_per_subband ) +{ + enc.m_someVal = someVal; + enc.m_lo_filter = std_lo_filter; + enc.m_hi_filter = std_hi_filter; + enc.m_levels = levels; + enc.m_numColumns = 1 << levels; + enc.m_samples_per_subband = samples_per_subband; + enc.m_samplesPerBlock = samples_per_subband * enc.m_numColumns; + + int adjustedSomeVal = (((someVal < -1) ? (someVal + 1) : (someVal)) + 1) >> 1; + enc.m_adjustedSamplesTimeNumColumns = adjustedSomeVal * (enc.m_numColumns - 1); + enc.m_levelSlots = reinterpret_cast( malloc( sizeof(float*) * (levels + 1) ) ); + if( enc.m_levelSlots == NULL ) + return 0; + + if( levels >= 0 ) + { + for( sint8 i = 0; i <= levels; ++i ) + { + int extraSamples = 0; + if( i != levels ) + { + extraSamples = (someVal - 1) << i; + } + + float* blockData = reinterpret_cast( malloc( ( enc.m_samplesPerBlock + extraSamples ) * sizeof(float) ) ); + enc.m_levelSlots[ i ] = blockData; + if( blockData == NULL ) + return 0; + + memset( blockData, 0, (enc.m_samplesPerBlock + extraSamples) * sizeof(float) ); + enc.m_levelSlots[i] += extraSamples; + } + } + + enc.m_pFormatIdPerColumn = reinterpret_cast( malloc( enc.m_numColumns * sizeof(uint32) ) ); + if( enc.m_pFormatIdPerColumn == NULL ) + return 0; + + enc.m_sampleCount = 0; + + sint32 edxOffset = ( (enc.m_samplesPerBlock * sizeof(float) * 25) - enc.m_adjustedSamplesTimeNumColumns ) % enc.m_samplesPerBlock; + enc.m_pCurrBlockData = enc.m_levelSlots[0] + edxOffset; + enc.m_blockSamplesRemaining = enc.m_samplesPerBlock - edxOffset; + enc.m_bandWriteEnabled = 0; + enc.m_finishedReading = 0; + return 1; +} + +void DestroyEncoder( Encoder& enc ) +{ + if( enc.m_levelSlots != NULL ) + { + for( int i = 0; i <= enc.m_levels; ++i ) + { + if( enc.m_levelSlots[i] != 0 ) + { + int extraSamples = 0; + if( enc.m_levels != i ) + { + extraSamples = (enc.m_someVal - 1) << i; + } + + free( enc.m_levelSlots[i] - extraSamples ); + } + } + + free( enc.m_levelSlots ); + } + + if( enc.m_pFormatIdPerColumn != NULL ) + { + free( enc.m_pFormatIdPerColumn ); + } +} + +void transform_subband( Encoder& enc, float* pD0, float* pD1, sint32 subBandCount, sint32 sampleCount ) +{ + if( sampleCount <= 0 ) + return; + + const sint32 var_8 = (enc.m_someVal - 1) >> 1; + const sint32 edx = var_8 * subBandCount; + pD0 -= edx; + + for( int i = 0; i < sampleCount; ++i ) + { + float* pFilter = (i & 1) ? enc.m_hi_filter : enc.m_lo_filter; + float* ebx = pD0 - edx; + float* eax = pD0 + edx; + + float var_4 = 0.0f; + if( var_8 > 0 ) + { + for( sint32 ebp = var_8; ebp != 0; --ebp ) + { + var_4 += ( *eax + *ebx ) * *pFilter++; + ebx += subBandCount; + eax -= subBandCount; + } + } + + *pD1 = (*ebx * *pFilter) + var_4; + pD1 += subBandCount; + pD0 += subBandCount; + } +} + +void transform_all( Encoder& enc ) +{ + if( enc.m_levels <= 0 ) + return; + + sint32 subBandCount = 1; + sint32 sampleCount = enc.m_samplesPerBlock; + for( int i = 0; i < enc.m_levels; ++i ) + { + float* levelDataEBX = enc.m_levelSlots[i]; + float* levelDataEBP = enc.m_levelSlots[i+1]; + + for( int sbc = 0; sbc < subBandCount; ++sbc ) + { + transform_subband( enc, levelDataEBX++, levelDataEBP++, subBandCount, sampleCount ); + } + + subBandCount += subBandCount; + sampleCount >>= 1; + } +} + +sint32 calc_bits( Encoder& enc, sint32 val ) +{ + static uint32 calc_bits_data[] = { 0x00, 0x13, 0x16, 0x03, 0x1D, 0x00 }; + + sint32 bitPower = 3; + sint32 result = enc.m_numColumns * 5 + 20; + + float halfVal = float(val) * 0.5f; + float* var_18 = enc.m_levelSlots[enc.m_levels]; + float* var_8 = enc.m_levelSlots[enc.m_levels]; + + for( sint32 var_1C = 0; var_1C < enc.m_numColumns; ++var_1C ) + { + sint32 minValue = 0x10000; + sint32 maxValue = -0x10000; + if( enc.m_samples_per_subband > 0 ) + { + float* pSlotData = var_8; + for( int ebp = enc.m_samples_per_subband; ebp != 0; --ebp ) + { + sint32 testVal = (sint32)floor( ( *pSlotData + halfVal ) / float(val) ); + if( minValue > testVal ) + { + minValue = testVal; + } + + if( maxValue < testVal ) + { + maxValue = testVal; + } + + pSlotData += enc.m_numColumns; + } + } + + sint32 absMaxVal = abs( minValue ); + if( absMaxVal < maxValue ) + { + absMaxVal = maxValue; + } + else if( absMaxVal < -maxValue ) + { + absMaxVal = -maxValue; + } + + if( absMaxVal == 0 ) + { + minValue = 0; + enc.m_pFormatIdPerColumn[var_1C] = 0; + } + else if( absMaxVal <= 4 ) + { + sint32 ebx = 1; + sint32 var_28 = absMaxVal * 3 + 14; + if( absMaxVal != 1 ) + { + ebx = (( absMaxVal - 2 ) < 1 ) ? 2 : 3; + } + + minValue = 0; + maxValue = 0; + for( int ebp = 0; ebp < enc.m_samples_per_subband; ++ebp ) + { + sint32 v = (int)floor( ( var_18[(ebp * enc.m_numColumns) + var_1C] + halfVal ) / float(val) ); + if( v ) + { + if( ebx != 1 ) + { + if( v == -1 || v == 1 ) + { + minValue += 4; + maxValue += 3; + } + else + { + minValue += ebx + 2; + maxValue += ebx + 1; + } + } + else + { + minValue += 3; + maxValue += 2; + } + } + else if( (enc.m_samples_per_subband - 1) <= ebp ) + { + minValue += 2; + ++maxValue; + } + else + { + sint32 v = (int)floor( ( var_18[ ((ebp + 1) * enc.m_numColumns) + var_1C ] + halfVal ) / float(val) ); + if( v ) + { + minValue += 2; + ++maxValue; + } + else + { + ++minValue; + maxValue += 2; + ++ebp; + } + } + } + + if( minValue > maxValue ) + { + minValue = maxValue; + ++var_28; + } + + sint32 ecx; + if( absMaxVal != 4 ) + { + ecx = (( enc.m_samples_per_subband + 2 ) / 3) * (( absMaxVal * 2 ) + 3); + } + else + { + ecx = ((( enc.m_samples_per_subband < -1 ) ? (enc.m_samples_per_subband + 2) : (enc.m_samples_per_subband + 1)) >> 1) * 7; + } + + if( minValue > ecx ) + { + minValue = ecx; + var_28 = calc_bits_data[absMaxVal]; + } + + enc.m_pFormatIdPerColumn[var_1C] = var_28; + } + else if( minValue >= -5 && maxValue <= 5 ) + { + minValue = ((( enc.m_samples_per_subband < -1 ) ? ( enc.m_samples_per_subband + 2 ) : ( enc.m_samples_per_subband + 1 )) >> 1) * 7; + enc.m_pFormatIdPerColumn[var_1C] = 0x1D; + } + else + { + sint32 eax = 0; + if( minValue < 0 ) + { + eax = ~minValue; + } + + if( maxValue > 0 && ( (unsigned int)(eax) < (unsigned int)(maxValue) ) ) + { + eax = maxValue; + } + + minValue = 1; + while( eax ) + { + eax >>= 1; + ++minValue; + } + + if( bitPower < (minValue - 1) ) + { + bitPower = minValue - 1; + } + enc.m_pFormatIdPerColumn[var_1C] = minValue; + minValue *= enc.m_samples_per_subband; + } + + result += minValue; + ++var_8; + } + + enc.m_currBlockBitPower = bitPower; + enc.m_currBlockBitValue = val; + return result; +} + +void DetermineStep( Encoder& enc ) +{ + sint32 lo = 1; + sint32 hi = 0x7FFF; + + do + { + const sint32 midPoint = (lo + hi) >> 1; + sint32 errorAmt = calc_bits( enc, midPoint ); + if( enc.m_threshold < errorAmt ) + { + lo = midPoint + 1; + } + else + { + hi = midPoint - 1; + } + }while( hi >= lo ); + + if( enc.m_currBlockBitValue != lo ) + { + calc_bits( enc, lo ); + } +} + +void WriteBands( Encoder& enc ) +{ + enc.m_bits.WriteBits( enc.m_currBlockBitPower, 4 ); + enc.m_bits.WriteBits( enc.m_currBlockBitValue, 16 ); + + for( int i = 0; i < enc.m_numColumns; ++i ) + { + const uint32 formatId = enc.m_pFormatIdPerColumn[i]; + enc.m_bits.WriteBits( formatId, 5 ); + int currPos = ftell(enc.m_bits.m_outFile); + WriteBand_tbl[ formatId ]( enc, i, formatId ); + } +} + +void shift_transform_levels( Encoder& enc ) +{ + sint32 levelCount = enc.m_someVal - 1; + for( int i = 0; i < enc.m_levels; ++i, levelCount += levelCount ) + { + float* pDst = enc.m_levelSlots[i] - levelCount; + float* pSrc = pDst + enc.m_samplesPerBlock; + memcpy( pDst, pSrc, levelCount * sizeof(float) ); + } +} + +void ProcessBlock( Encoder& enc ) +{ + transform_all( enc ); + + if( enc.m_bandWriteEnabled != 0 ) + { + DetermineStep( enc ); + + WriteBands( enc ); + } + + shift_transform_levels( enc ); + + enc.m_blockSamplesRemaining += enc.m_samplesPerBlock; + enc.m_pCurrBlockData -= enc.m_samplesPerBlock; +} + +void EncodeSample( Encoder& enc ) +{ + sint32 sample = 0; + if( enc.m_finishedReading == 0 ) + { + sample = (*enc.m_reader)( enc.m_pReaderData ); + if( sample == ReadSampleEof ) + { + enc.m_finishedReading = 1; + return; + } + + ++enc.m_sampleCount; + } + + *enc.m_pCurrBlockData++ = enc.m_volume * float(sample); + + if( --enc.m_blockSamplesRemaining == 0 ) + { + ProcessBlock( enc ); + } +} + +void EncodeFlush( Encoder& enc ) +{ + if( enc.m_samplesPerBlock == enc.m_blockSamplesRemaining ) + { + // no data in the block + return; + } + + // Zero out the remaining data in the block + while( enc.m_blockSamplesRemaining != 0 ) + { + *enc.m_pCurrBlockData++ = 0.0f; + --enc.m_blockSamplesRemaining; + } + + // Send it off for processing + ProcessBlock( enc ); +} + +unsigned long AudioEncode( ReadSampleFunction *read, void *data, unsigned channels, unsigned sample_rate, float volume, + FILE *out, int levels, int samples_per_subband, float comp_ratio ) +{ + Encoder enc; + memset( &enc, 0, sizeof(enc) ); + + if( !ReadSample_init( enc, read, data ) ) + { + DestroyEncoder( enc ); + return 0; + } + + if( !bits_init( enc.m_bits, out ) ) + { + DestroyEncoder( enc ); + return 0; + } + + enc.m_volume = volume; + if( !SetupEncoder( enc, 0xF, std_lo_filter, std_hi_filter, levels, samples_per_subband ) ) + { + DestroyEncoder( enc ); + return 0; + } + + enc.m_threshold = (sint32)( float(enc.m_samplesPerBlock) * comp_ratio * 16.0f ); + + int originalPosVAR64 = ftell( out ); + + // Header + enc.m_bits.WriteBits( 0x97, 8 ); + enc.m_bits.WriteBits( 0x28, 8 ); + enc.m_bits.WriteBits( 0x03, 8 ); + + // Version + enc.m_bits.WriteBits( 1, 8 ); + + // Sample Count (Placeholder 32bits for now) + enc.m_bits.WriteBits( 0, 8 ); + enc.m_bits.WriteBits( 0, 8 ); + enc.m_bits.WriteBits( 0, 8 ); + enc.m_bits.WriteBits( 0, 8 ); + + // Number of channels + enc.m_bits.WriteBits( channels, 16 ); + + // Sample Rate + enc.m_bits.WriteBits( sample_rate, 16 ); + + // Levels + enc.m_bits.WriteBits( levels, 4 ); + + // Samples per Sub-band (rows) + enc.m_bits.WriteBits( samples_per_subband, 12 ); + + enc.m_bandWriteEnabled = 0; + + sint32 esi = enc.m_adjustedSamplesTimeNumColumns; + while( esi ) + { + EncodeSample( enc ); + --esi; + } + + enc.m_bandWriteEnabled = 1; + + while( !enc.m_finishedReading ) + { + EncodeSample( enc ); + } + + esi = enc.m_adjustedSamplesTimeNumColumns; + while( esi ) + { + EncodeSample( enc ); + --esi; + } + + EncodeFlush( enc ); + + ///////////// + // NOTE: The Interplay one doesn't do this ... but it should as there + // may be bits left in the bit processor that should go out + enc.m_bits.Flush(); + ///////////// + + // Go back and write the Sample Count out proper + int endPos = ftell( out ); + fseek( out, originalPosVAR64 + 4, 0 ); + putc( (enc.m_sampleCount >> 0) & 0xFF, out ); + putc( (enc.m_sampleCount >> 8) & 0xFF, out ); + putc( (enc.m_sampleCount >> 16) & 0xFF, out ); + putc( (enc.m_sampleCount >> 24) & 0xFF, out ); + fseek( out, endPos, 0 ); + + DestroyEncoder( enc ); + return endPos; +} diff --git a/libacm/libacm.cpp b/libacm/libacm.cpp new file mode 100644 index 000000000..d53483213 --- /dev/null +++ b/libacm/libacm.cpp @@ -0,0 +1,1219 @@ +// Interplay ACM audio codec decoder +// +// Based on code from the decoder source of libacm, licensed under a minimal BSD/ISC license: +// Copyright (c) 2004-2008, Marko Kreen +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#include "Adecode.h" +#include +#ifdef MACOSX +#include +#else +#include +#endif +#include +#include "pserror.h" +#include "BYTESWAP.H" +using namespace AudioDecoder; + +namespace +{ + // default memory allocation function + void* DefaultMalloc(uint32 size) + { + return malloc(size); + } + + // default memory release function + void DefaultFree(void* pPtr) + { + free( pPtr ); + } + + // Constants + const uint32 kBitBufferSize = 64 * 1024; + const uint32 kNumAmpSamples = 64 * 1024; + const uint32 kACMId = 0x032897; + const uint32 kACMVersion = 1; + + // Internal Classes + struct ACMInfo + { + uint32 m_id; + uint32 m_version; + uint32 m_sampleCount; + uint32 m_numChannels; + uint32 m_sampleRate; + uint32 m_level; + uint32 m_columns; + uint32 m_rows; + }; + + class InternalAudioDecoder : public IAudioDecoder + { + public: + InternalAudioDecoder( ReadDataFunction readerFunction, void* pReaderData ); + ~InternalAudioDecoder(); + + // Extract the header information + const ACMInfo& GetHeader() const + { + return m_acm; + } + + // Initialize the decoder + bool Initialize(); + + // Read data from the audio decoder. + // pBuffer: The buffer to receive the data from + // amount: How much data to read + // Returns the number of bytes read - zero when we're at the end of the file + uint32 Read( void* pBuffer, uint32 amount ); + + // Operator overloads + void* operator new(size_t numBytes); + void operator delete(void* pPtr); + + // Unpackers + static bool UnpackZeroFill( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackIgnore( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackLinear( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK13( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK12( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK24( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK23( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK35( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK34( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK45( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackK44( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackT15( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackT27( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + static bool UnpackT37( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); + + static void SetMemoryFunctions( MemoryAllocFunc memAlloc, MemoryFreeFunc memFree ); + private: + // Read data from the read function + sint32 ReadData( void* pBuffer, uint32 amount ); + uint32 InternalRead( void* pBuffer, uint32 amount ); + + bool LoadBitBufferFromFile(); + bool LoadBitBuffer(); + bool ReloadBits(uint32 bits, uint32& newData, bool& hitEOF); + bool GetBits(uint32 bits, uint32& resultData); + bool GetBitsEOF(uint32 bits, uint32& resultData, bool& hadEOF); + + bool DecodeBlock( bool& atEOF ); + bool UnpackBlock( bool& atEOF ); + void OutputValues( const sint32* pSrcData, void* pBuffer, uint32 numWords ); + + void JuggleBlock(); + + ACMInfo m_acm; + ReadDataFunction m_readerFunction; + void* m_pReaderData; + + uint8* m_pFileBitBuffer; + uint32 m_bitBufferAvailableSize; + uint32 m_bitBufferCurrPos; + uint32 m_numBitsAvailable; + uint32 m_bitData; + + uint32 m_blockLengthInSamples; + uint32 m_wrapBufferLength; + sint32* m_pBlock; + sint32* m_pWrapBuffer; + sint32* m_pAmpBuffer; + sint32* m_pMidBuffer; + + bool m_blockReady; + bool m_bitBufferAtEOF; + uint32 m_streamPos; + uint32 m_blockPos; + + // Memory management + void* m_pMemoryBuffer; + static MemoryAllocFunc s_Malloc; + static MemoryFreeFunc s_Free; + }; + + // Tables + const sint32 gMap1Bit[] = { -1, +1 }; + const sint32 gMap2BitNear[] = { -2, -1, +1, +2 }; + const sint32 gMap2BitFar[] = { -3, -2, +2, +3 }; + const sint32 gMap3Bit[] = { -4, -3, -2, -1, +1, +2, +3, +4 }; + + const uint16 gMul3x3[27] = + { + 0x0000, 0x0001, 0x0002, + 0x0010, 0x0011, 0x0012, + 0x0020, 0x0021, 0x0022, + 0x0100, 0x0101, 0x0102, + 0x0110, 0x0111, 0x0112, + 0x0120, 0x0121, 0x0122, + 0x0200, 0x0201, 0x0202, + 0x0210, 0x0211, 0x0212, + 0x0220, 0x0221, 0x0222, + }; + + const uint16 gMul3x5[125] = + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, + 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, + 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, + 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, + 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, + 0x0140, 0x0141, 0x0142, 0x0143, 0x0144, + 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, + 0x0210, 0x0211, 0x0212, 0x0213, 0x0214, + 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, + 0x0230, 0x0231, 0x0232, 0x0233, 0x0234, + 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, + 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, + 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, + 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, + 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, + 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, + }; + + const uint8 gMul2x11[121] = + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA + }; +} + +/**************************************************************/ +/* Interface Functions */ +/**************************************************************/ + +// Optional interface for supplying your own malloc and free functions +// Default is to use standard malloc and free. +void RegisterMemoryFunctions( MemoryAllocFunc memAlloc, MemoryFreeFunc memFree ) +{ + InternalAudioDecoder::SetMemoryFunctions( memAlloc, memFree ); +} + +// Create an audio decoder +// You supply a function for reading bytes from the compressed data via a +// void* pData handle, and the handle itself (typically a FILE *). +// Create_AudioDecoder returns a new AudioDecoder which can be used to +// read uncompressed decoded data from the compressed stream, +// and also returns the number of channels (1 or 2), the sample rate +// (e.g. 22050), and the number of samples contained in the compressed file +// (in case you want to pre-allocate a buffer to load them all into memory). +IAudioDecoder* AudioDecoder::CreateDecoder( ReadDataFunction readerFunction, void* pReaderData, uint32& numChannels, uint32& sampleRate, uint32& sampleCount ) +{ + // allocate our decoder + InternalAudioDecoder* pDecoder = new InternalAudioDecoder( readerFunction, pReaderData ); + if( pDecoder == NULL ) + return NULL; + + // initialize + if( !pDecoder->Initialize() ) + { + // Failed + delete pDecoder; + return NULL; + } + + // extract the header information for the caller + const ACMInfo& header = pDecoder->GetHeader(); + numChannels = header.m_numChannels; + sampleRate = header.m_sampleRate; + sampleCount = header.m_sampleCount; + + // return the decoder back to the user + return pDecoder; +} + +/**************************************************************/ +/* Memory Management */ +/**************************************************************/ + +// Static memory +MemoryAllocFunc InternalAudioDecoder::s_Malloc = DefaultMalloc; +MemoryFreeFunc InternalAudioDecoder::s_Free = DefaultFree; + +void InternalAudioDecoder::SetMemoryFunctions( MemoryAllocFunc memAlloc, MemoryFreeFunc memFree ) +{ + if( (memAlloc && !memFree) || (!memAlloc && memFree) ) + return; + + s_Malloc = ( memAlloc ) ? memAlloc : DefaultMalloc; + s_Free = ( memFree ) ? memFree : DefaultFree; +} + +void* InternalAudioDecoder::operator new(size_t numBytes) +{ + return s_Malloc( static_cast(numBytes) ); +} + +void InternalAudioDecoder::operator delete(void* pPtr) +{ + if( pPtr ) + { + s_Free( pPtr ); + } +} + +/**************************************************************/ +/* Construction */ +/**************************************************************/ + +InternalAudioDecoder::InternalAudioDecoder( ReadDataFunction readerFunction, void* pReaderData ) +: m_readerFunction( readerFunction ), m_pReaderData( pReaderData ), + m_pFileBitBuffer( NULL ), m_bitBufferAvailableSize( 0 ), m_bitBufferCurrPos( 0 ), m_numBitsAvailable( 0 ), m_bitData( 0 ), + m_blockLengthInSamples( 0 ), m_wrapBufferLength( 0 ), m_pBlock( NULL ), m_pWrapBuffer( NULL ), m_pAmpBuffer( NULL ), m_pMidBuffer( NULL ), + m_blockReady( false ), m_bitBufferAtEOF( false ), m_streamPos( 0 ), m_blockPos( 0 ), m_pMemoryBuffer( NULL ) +{ + std::memset( &m_acm, 0, sizeof(m_acm) ); +} + +// Initialize the decoder +bool InternalAudioDecoder::Initialize() +{ + // Allocate the bit buffer before we start reading it + m_pFileBitBuffer = reinterpret_cast( s_Malloc(kBitBufferSize) ); + if( !m_pFileBitBuffer ) + return false; + + // Read in the ACM header + if( !GetBits( 24, m_acm.m_id ) ) + return false; + if( m_acm.m_id != kACMId ) + return false; + if( !GetBits( 8, m_acm.m_version ) ) + return false; + if( m_acm.m_version != kACMVersion ) + return false; + + // total value count + uint32 temp; + if( !GetBits( 16, m_acm.m_sampleCount ) ) + return false; + if( !GetBits( 16, temp ) ) + return false; + m_acm.m_sampleCount += temp << 16; + + if( m_acm.m_sampleCount == 0 ) + return false; + + // num channels + if( !GetBits( 16, m_acm.m_numChannels ) ) + return false; + + if( m_acm.m_numChannels < 1 || m_acm.m_numChannels > 2 ) + return false; + + // sample rate + if( !GetBits( 16, m_acm.m_sampleRate ) ) + return false; + + if( m_acm.m_sampleRate < 4096 ) + return false; + + // level + if( !GetBits( 4, m_acm.m_level ) ) + return false; + + // rows + if( !GetBits( 12, m_acm.m_rows ) ) + return false; + + if( m_acm.m_rows == 0 ) + return false; + + // Calculate the blocks + m_acm.m_columns = 1 << m_acm.m_level; + m_wrapBufferLength = 2 * m_acm.m_columns - 2; + m_blockLengthInSamples = m_acm.m_rows * m_acm.m_columns; + + // Calculate the amount of memory that needs to be allocated + const size_t blockMemSize = m_blockLengthInSamples * sizeof(uint32); + const size_t wrapMemSize = m_wrapBufferLength * sizeof(uint32); + const size_t ampMemSize = kNumAmpSamples * sizeof(uint32); + const size_t totalMemSize = blockMemSize + wrapMemSize + ampMemSize; + m_pMemoryBuffer = s_Malloc( static_cast( totalMemSize ) ); + if( m_pMemoryBuffer == NULL ) + return false; + + // Assign buffer pointers + m_pBlock = reinterpret_cast( m_pMemoryBuffer ); + m_pWrapBuffer = reinterpret_cast( m_pBlock + m_blockLengthInSamples ); + m_pAmpBuffer = reinterpret_cast( m_pWrapBuffer + m_wrapBufferLength ); + m_pMidBuffer = reinterpret_cast( m_pAmpBuffer + (kNumAmpSamples>>1) ); + + // Initialize data buffers + std::memset( m_pWrapBuffer, 0, m_wrapBufferLength * sizeof(uint32) ); + + // Lets get going + return true; +} + +/**************************************************************/ +/* Destruction */ +/**************************************************************/ + +InternalAudioDecoder::~InternalAudioDecoder() +{ + if( m_pMemoryBuffer ) + { + s_Free( m_pMemoryBuffer ); + } + + if( m_pFileBitBuffer ) + { + s_Free( m_pFileBitBuffer ); + } +} + +/**************************************************************/ +/* Reading */ +/**************************************************************/ + +// Read data from the audio decoder. +// pBuffer: The buffer to receive the data from +// amount: How much data to read +// Returns the number of bytes read - zero when we're at the end of the file +uint32 InternalAudioDecoder::Read( void* pBuffer, uint32 amount ) +{ + uint32 totalBytesRead = 0; + uint8* pBuf = reinterpret_cast( pBuffer ); + + do + { + uint32 numWords = amount >> 1; + if( m_streamPos + numWords > m_acm.m_sampleCount ) + { + // Don't read past the end of file + numWords = m_acm.m_sampleCount - m_streamPos; + } + + if( m_acm.m_numChannels > 1 ) + { + // Read full channel data + numWords -= numWords & 1; + } + + if( numWords == 0 ) + break; + + uint32 res = InternalRead( pBuf, numWords << 1 ); + if( res == 0 ) + break; + + pBuf += res; + amount -= res; + totalBytesRead += res; + } while(amount > 0); + + return totalBytesRead; +} + +uint32 InternalAudioDecoder::InternalRead( void* pBuffer, uint32 amount ) +{ + // Check for End-of-File + if( m_streamPos >= m_acm.m_sampleCount ) + return 0; + + uint32 numWords = amount >> 1; + if( !m_blockReady ) + { + bool atEOF; + if( !DecodeBlock( atEOF ) ) + { + // TODO: Report proper error? + return atEOF ? 0 : 0; + } + } + + uint32 numAvailableWords = m_blockLengthInSamples - m_blockPos; + if( numAvailableWords < numWords ) + { + // Don't read too past the end of the block + numWords = numAvailableWords; + } + + if( m_streamPos + numWords > m_acm.m_sampleCount ) + { + // Don't read past the end of file + numWords = m_acm.m_sampleCount - m_streamPos; + } + + if( m_acm.m_numChannels > 1 ) + { + // Read full channel data + numWords -= numWords & 1; + } + + const sint32* pSrcData = m_pBlock + m_blockPos; + OutputValues( pSrcData, pBuffer, numWords ); + + m_streamPos += numWords; + m_blockPos += numWords; + if( m_blockPos == m_blockLengthInSamples ) + { + m_blockReady = false; + } + + return numWords << 1; +} + + +/**************************************************************/ +/* Output */ +/**************************************************************/ + +void InternalAudioDecoder::OutputValues( const sint32* pSrcData, void* pBuffer, uint32 numWords ) +{ + uint8* pDst = reinterpret_cast( pBuffer ); + + const uint32 shiftAmount = m_acm.m_level; + while( numWords-- ) + { + sint32 src = *pSrcData++; + sint32 val = INTEL_INT(src) >> shiftAmount; + *pDst++ = val & 0xFF; + *pDst++ = (val >> 8) & 0xFF; + } +} + +/**************************************************************/ +/* Decoding */ +/**************************************************************/ + +bool InternalAudioDecoder::DecodeBlock( bool& atEOF ) +{ + // Reset block state + m_blockReady = false; + m_blockPos = 0; + + // Read in data + uint32 bitPower; + if( !GetBitsEOF( 4, bitPower, atEOF ) ) + return false; + + uint32 bitValue; + if( !GetBitsEOF( 16, bitValue, atEOF ) ) + return false; + + // Process + sint32 x; + uint32 i, count = 1 << bitPower; + for( i = 0, x = 0; i < count; ++i ) + { + m_pMidBuffer[ i ] = x; + x += bitValue; + } + + for( i = 1, x = -static_cast(bitValue); i <= count; ++i ) + { + m_pMidBuffer[ -static_cast(i) ] = x; + x -= bitValue; + } + + if( !UnpackBlock( atEOF ) ) + return false; + + JuggleBlock(); + + m_blockReady = true; + return true; +} + +static void Juggle( sint32* pWrap, sint32* pBlock, uint32 subLen, uint32 subCount ) +{ + for( uint32 i = 0; i < subLen; ++i ) + { + sint32* pPtr = pBlock; + sint32 r0 = pWrap[0]; + sint32 r1 = pWrap[1]; + + uint32 cnt = subCount >> 1; + for( uint32 j = 0; j < cnt; ++j ) + { + sint32 r2 = *pPtr; *pPtr = (r1<<1) + (r0 + r2); pPtr += subLen; + sint32 r3 = *pPtr; *pPtr = (r2<<1) - (r1 + r3); pPtr += subLen; + + r0 = r2; + r1 = r3; + } + + *pWrap++ = r0; + *pWrap++ = r1; + ++pBlock; + } +} + +void InternalAudioDecoder::JuggleBlock() +{ + // check subblock length + if( m_acm.m_level == 0 ) + return; + + // Apply juggle() (rows)x(cols) + // from (step_subcount * 2) x (subblock_len/2) + // to (step_subcount * subblock_len) x (1) + const uint32 stepSubCount = ( m_acm.m_level > 9 ) ? 1 : (( 2048 >> m_acm.m_level ) - 2); + uint32 todoCount = m_acm.m_rows; + sint32* pBlock = m_pBlock; + while( true ) + { + sint32* pWrap = m_pWrapBuffer; + uint32 subCount = stepSubCount; + if( subCount > todoCount ) + { + subCount = todoCount; + } + + uint32 subLen = m_acm.m_columns >> 1; + subCount <<= 1; + + Juggle( pWrap, pBlock, subLen, subCount ); + pWrap += subLen << 1; + + uint32 i; + sint32* pPtr; + for( i = 0, pPtr = pBlock; i < subCount; ++i ) + { + ++pPtr[0]; + pPtr += subLen; + } + + while( subLen > 1 ) + { + subLen >>= 1; + subCount <<= 1; + Juggle( pWrap, pBlock, subLen, subCount ); + pWrap += subLen << 1; + } + + if( todoCount <= stepSubCount ) + break; + + todoCount -= stepSubCount; + pBlock += stepSubCount << m_acm.m_level; + } +} + +/**************************************************************/ +/* Unpacking */ +/**************************************************************/ + +bool InternalAudioDecoder::UnpackZeroFill( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + const sint32 midZero = decoder.m_pMidBuffer[0]; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + const uint32 pos = (i << level) + col; + pBlock[pos] = midZero; + } + + return true; +} + +bool InternalAudioDecoder::UnpackIgnore( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + return true; +} + +bool InternalAudioDecoder::UnpackLinear( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 middleIndex = 1 << (id - 1); + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( id, offset, atEOF ) ) + return false; + + const uint32 pos = (i << level) + col; + pBlock[pos] = decoder.m_pMidBuffer[ offset - middleIndex ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK13( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 flag; + if( !decoder.GetBitsEOF( 1, flag, atEOF ) ) + return false; + + if( flag == 0 ) + { + pBlock[(i++ << level) + col] = midZero; + if( i >= numRows ) + break; + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, flag, atEOF ) ) + return false; + + if( flag == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, flag, atEOF ) ) + return false; + + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap1Bit[flag] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK12( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + + if( offset == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap1Bit[offset] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK24( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + + if( offset == 0 ) + { + pBlock[(i++ << level) + col] = midZero; + if( i >= numRows ) + break; + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 2, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap2BitNear[offset] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK23( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + + if( offset == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 2, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap2BitNear[offset] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK35( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + + if( offset == 0 ) + { + pBlock[(i++ << level) + col] = midZero; + if( i >= numRows ) + break; + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap1Bit[offset] ]; + continue; + } + + if( !decoder.GetBitsEOF( 2, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap2BitFar[offset] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK34( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap1Bit[offset] ]; + continue; + } + + if( !decoder.GetBitsEOF( 2, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap2BitFar[offset] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK45( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + pBlock[(i++ << level) + col] = midZero; + if( i >= numRows ) + break; + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 3, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap3Bit[offset] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackK44( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const sint32 midZero = decoder.m_pMidBuffer[0]; + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 1, offset, atEOF ) ) + return false; + if( offset == 0 ) + { + pBlock[(i << level) + col] = midZero; + continue; + } + + if( !decoder.GetBitsEOF( 3, offset, atEOF ) ) + return false; + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ gMap3Bit[offset] ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackT15( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 5, offset, atEOF ) ) + return false; + + uint32 n1 = (gMul3x3[offset] & 0x0F) - 1; + uint32 n2 = ((gMul3x3[offset] >> 4) & 0x0F) - 1; + uint32 n3 = ((gMul3x3[offset] >> 8) & 0x0F) - 1; + + pBlock[(i++ << level) + col] = decoder.m_pMidBuffer[ n1 ]; + if( i >= numRows ) + break; + + pBlock[(i++ << level) + col] = decoder.m_pMidBuffer[ n2 ]; + if( i >= numRows ) + break; + + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ n3 ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackT27( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 7, offset, atEOF ) ) + return false; + + uint32 n1 = (gMul3x5[offset] & 0x0F) - 2; + uint32 n2 = ((gMul3x5[offset] >> 4) & 0x0F) - 2; + uint32 n3 = ((gMul3x5[offset] >> 8) & 0x0F) - 2; + + pBlock[(i++ << level) + col] = decoder.m_pMidBuffer[ n1 ]; + if( i >= numRows ) + break; + + pBlock[(i++ << level) + col] = decoder.m_pMidBuffer[ n2 ]; + if( i >= numRows ) + break; + + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ n3 ]; + } + + return true; +} + +bool InternalAudioDecoder::UnpackT37( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ) +{ + const uint32 numRows = decoder.m_acm.m_rows; + const uint32 level = decoder.m_acm.m_level; + sint32* pBlock = decoder.m_pBlock; + + for( uint32 i = 0; i < numRows; ++i ) + { + uint32 offset; + if( !decoder.GetBitsEOF( 7, offset, atEOF ) ) + return false; + + uint32 n1 = (gMul2x11[offset] & 0x0F) - 5; + uint32 n2 = ((gMul2x11[offset] >> 4) & 0x0F) - 5; + + pBlock[(i++ << level) + col] = decoder.m_pMidBuffer[ n1 ]; + if( i >= numRows ) + break; + + pBlock[(i << level) + col] = decoder.m_pMidBuffer[ n2 ]; + } + + return true; +} + +typedef bool (*UnpackerFunction)( InternalAudioDecoder& decoder, uint32 id, uint32 col, bool& atEOF ); +static const UnpackerFunction Unpacker[] = +{ + InternalAudioDecoder::UnpackZeroFill, InternalAudioDecoder::UnpackIgnore, InternalAudioDecoder::UnpackIgnore, InternalAudioDecoder::UnpackLinear, + InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, + InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, + InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackLinear, + InternalAudioDecoder::UnpackLinear, InternalAudioDecoder::UnpackK13, InternalAudioDecoder::UnpackK12, InternalAudioDecoder::UnpackT15, + InternalAudioDecoder::UnpackK24, InternalAudioDecoder::UnpackK23, InternalAudioDecoder::UnpackT27, InternalAudioDecoder::UnpackK35, + InternalAudioDecoder::UnpackK34, InternalAudioDecoder::UnpackIgnore, InternalAudioDecoder::UnpackK45, InternalAudioDecoder::UnpackK44, + InternalAudioDecoder::UnpackIgnore, InternalAudioDecoder::UnpackT37, InternalAudioDecoder::UnpackIgnore, InternalAudioDecoder::UnpackIgnore, +}; + +bool InternalAudioDecoder::UnpackBlock( bool& atEOF ) +{ + for( uint32 i = 0; i < m_acm.m_columns; ++i ) + { + uint32 unpackerId; + if( !GetBitsEOF( 5, unpackerId, atEOF ) ) + return false; + + // Jump to the unpacker + if( !Unpacker[ unpackerId ]( *this, unpackerId, i, atEOF ) ) + return false; + } + + return true; +} + +/**************************************************************/ +/* Loading Data */ +/**************************************************************/ + +bool InternalAudioDecoder::LoadBitBufferFromFile() +{ + if( m_bitBufferAtEOF ) + return true; + + // Callback to the user to get more data + sint32 res = m_readerFunction( m_pReaderData, m_pFileBitBuffer, kBitBufferSize ); + if( res < 0 ) + return false; + + if( res == 0 ) + { + // No more data + m_bitBufferAtEOF = true; + m_pFileBitBuffer[0] = 0; + m_bitBufferAvailableSize = 1; + } + else + { + // We still have data + m_bitBufferAvailableSize = static_cast( res ); + } + + m_bitBufferCurrPos = 0; + return true; +} + +bool InternalAudioDecoder::LoadBitBuffer() +{ + uint32 numLoadedBits = 0; // How many bits are still available + uint32 loadedDataBits = 0; // The bit data still available + + // How many bytes of data remain? + const uint32 bufferRemaining = m_bitBufferAvailableSize - m_bitBufferCurrPos; + ASSERT( bufferRemaining < 4 ); + + // Bring in the remaining bits from the buffer + const uint8 *pCurrBuffer = m_pFileBitBuffer + m_bitBufferCurrPos; + switch( bufferRemaining ) + { + case 3: loadedDataBits += pCurrBuffer[2] << 16; + case 2: loadedDataBits += pCurrBuffer[1] << 8; + case 1: loadedDataBits += pCurrBuffer[0]; + } + numLoadedBits = bufferRemaining << 3; + + // Fill back up the file buffer + if( !LoadBitBufferFromFile() ) + return false; + + // Bring in the rest of the bits that we can to fill up the 32 bits (or + // when we run out of available buffer) + while( numLoadedBits < 32 ) + { + if( m_bitBufferAvailableSize - m_bitBufferCurrPos == 0 ) + break; + + // Bring in the next byte + loadedDataBits |= m_pFileBitBuffer[ m_bitBufferCurrPos ] << numLoadedBits; + numLoadedBits += 8; + ++m_bitBufferCurrPos; + } + + // Continue with processing + m_bitData = loadedDataBits; + m_numBitsAvailable = numLoadedBits; + return true; +} + +// Reloads the available bits back to a full 32 +bool InternalAudioDecoder::ReloadBits(uint32 bits, uint32& newData, bool& hitEOF) +{ + ASSERT( bits > m_numBitsAvailable ); + hitEOF = false; + + // Bring in the bits that we can + uint32 currBitData = m_bitData; + uint32 numLoadedBits = m_numBitsAvailable; + bits -= numLoadedBits; + + // Bring in more bits from the buffer + uint32 bitData, bitsAvail; + if( (m_bitBufferAvailableSize - m_bitBufferCurrPos) >= 4 ) + { + // We have a full 32bits available + uint8* pCurrBuffer = m_pFileBitBuffer + m_bitBufferCurrPos; + m_bitBufferCurrPos += 4; + bitData = pCurrBuffer[0] + (pCurrBuffer[1] << 8) + (pCurrBuffer[2] << 16) + (pCurrBuffer[3] << 24); + bitsAvail = 32; + } + else + { + // We're almost out of buffer space - fill it back up + if( !LoadBitBuffer() ) + return false; + + if( m_numBitsAvailable < bits ) + { + // Unexpected EOF + hitEOF = true; + return false; + } + + bitData = m_bitData; + bitsAvail = m_numBitsAvailable; + } + + // Fold in the bits necessary to fill back up + currBitData |= (bitData & ((1 << bits) - 1)) << numLoadedBits; + m_bitData = bitData >> bits; + m_numBitsAvailable = bitsAvail - bits; + newData = currBitData; + return true; +} + +/**************************************************************/ +/* Data Retrieval */ +/**************************************************************/ + +bool InternalAudioDecoder::GetBits(uint32 bits, uint32& resultData) +{ + if( m_numBitsAvailable >= bits ) + { + // We have enough data to pull from + resultData = m_bitData & ((1 << bits) - 1); + m_bitData >>= bits; + m_numBitsAvailable -= bits; + return true; + } + + // Reload our bitData + bool hadEOF; + bool res = ReloadBits( bits, resultData, hadEOF ); + ASSERT( !hadEOF ); + return res; +} + +bool InternalAudioDecoder::GetBitsEOF(uint32 bits, uint32& resultData, bool& hadEOF) +{ + if( m_numBitsAvailable >= bits ) + { + // We have enough data to pull from + resultData = m_bitData & ((1 << bits) - 1); + m_bitData >>= bits; + m_numBitsAvailable -= bits; + return true; + } + + // Reload our bitData + return ReloadBits( bits, resultData, hadEOF ); +} diff --git a/libacm/libacm.vcproj b/libacm/libacm.vcproj new file mode 100644 index 000000000..d6db675a5 --- /dev/null +++ b/libacm/libacm.vcproj @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libmve/CMakeLists.txt b/libmve/CMakeLists.txt new file mode 100644 index 000000000..d17f7eceb --- /dev/null +++ b/libmve/CMakeLists.txt @@ -0,0 +1,21 @@ +SET (HEADERS mvegfx.h + mvelibi.h + mvelibl.h + platform.h + snd8to16.h + SystemInterfaces.h ) +SET (CPPS + mveasm.cpp + mvelibl.cpp + platform.cpp) + + + +SET (PLATFORM_CPPS ) + +IF (UNIX) + SET (PLATFORM_CPPS "lnxdsound.cpp") +ENDIF() + +ADD_LIBRARY(libmve STATIC ${HEADERS} ${CPPS} ${PLATFORM_CPPS}) + diff --git a/libmve/SystemInterfaces.h b/libmve/SystemInterfaces.h new file mode 100644 index 000000000..631e7f5d0 --- /dev/null +++ b/libmve/SystemInterfaces.h @@ -0,0 +1,198 @@ +#ifndef MVE_SYSTEM_INTERFACES_H__ +#define MVE_SYSTEM_INTERFACES_H__ + +// Max&Min values for settings +#define LNXSND_VOLUME_MAX 0 +#define LNXSND_VOLUME_MIN -10000 +#define LNXSND_PAN_LEFT -10000 +#define LNXSND_PAN_RIGHT 10000 + +// Status/Buffer flags +#define LNXSND_PLAYING 0x0001 +#define LNXSND_LOOPING 0x0002 + +// Buffer lock flags +#define LNXSND_LOCK_FROMWRITECURSOR 0x0001 +#define LNXSND_LOCK_ENTIREBUFFER 0x0002 + +// Capability flags +#define LNXSND_CAPS_PRIMARYBUFFER 0x0001 +#define LNXSND_CAPS_CTRLVOLUME 0x0080 +#define LNXSND_CAPS_CTRLPAN 0x0040 +#define LNXSND_CAPS_CTRLFREQUENCY 0x0020 +#define LNXSND_CAPS_CTRLDEFAULT 0x00E0 +#define LNXSND_CAPS_LOCSOFTWARE 0x0008 +#define LNXSND_CAPS_LOCHARDWARE 0x0004 + +struct SoundWAVEFormatEx +{ + unsigned short wFormatTag; + unsigned short nChannels; + unsigned int nSamplesPerSec; + unsigned int nAvgBytesPerSec; + unsigned short nBlockAlign; + unsigned short wBitsPerSample; + unsigned short cbSize; +}; +#define SOUND_WAVE_FORMAT_PCM 0x01 + +struct SysSoundCaps +{ + unsigned int dwFlags; + unsigned int dwBufferBytes; +}; + +class SysSoundBufferDesc +{ +public: + SoundWAVEFormatEx *lpwfxFormat; + unsigned int dwBufferBytes; + unsigned int dwFlags; +}; + +class ISysSoundBuffer +{ +public: + //////////////////////////// + // Release + //////////////////////////// + // Releases the memory associated with a sound buffer. This pointer is + // no longer valid after return. + // + // Returns: + // -1 : Invalid Parameter + // 0 : Ok! + virtual int Release() = 0; + + ////////////////////////////// + // SetVolume + ////////////////////////////// + // Sets the volume of a buffer. + // + // Returns: + // 0 : no error + // -1 : Cannot set volume + // -2 : Invalid parameters + virtual int SetVolume(signed long vol) = 0; + + /////////////////////////// + // SetPan + /////////////////////////// + // Sets the pan of a buffer. + // + // Returns: + // 0 : no error + // -1 : Cannot set pan + // -2 : Invalid parameters + virtual int SetPan(signed long pan) = 0; + + ///////////////////////// + // Stop + ///////////////////////// + // Stops a buffer from playing + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int Stop() = 0; + + ///////////////////////// + // Play + ///////////////////////// + // Starts a buffer playing (or changes the flags for a buffer currently + // playing). + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int Play(unsigned int flags) = 0; + + //////////////////////////// + // GetCaps + //////////////////////////// + // Get the capabilities of a sound buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int GetCaps(SysSoundCaps *caps) = 0; + + ////////////////////////////// + // GetStatus + ////////////////////////////// + // Returns the status of a buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int GetStatus(unsigned int *status) = 0; + + /////////////////////////////////////// + // GetCurrentPosition + /////////////////////////////////////// + // Returns the current play and write positions of the buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int GetCurrentPosition(unsigned int *ppos,unsigned int *wpos) = 0; + + /////////////////////////////////////// + // SetCurrentPosition + /////////////////////////////////////// + // Sets the current play position of the buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int SetCurrentPosition(unsigned int pos) = 0; + + ///////////////////////// + // Lock + ///////////////////////// + // Locks the given buffer, returning pointer(s) to the buffer(s) along with + // available the size of the buffer(s) for writing. + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int Lock( + unsigned int pos, + unsigned int numbytes, + void **ptr1, + unsigned int *numbytes1, + void **ptr2, + unsigned int *numbytes2, + unsigned int flags) = 0; + + /////////////////////////// + // Unlock + /////////////////////////// + // Unlocks a buffer. + // + // Returns: + // 0 : no error + // -1 : invalid parameters + virtual int Unlock( + void *ptr1, + unsigned int num1, + void *ptr2, + unsigned int num2) = 0; +}; + +class ISoundDevice +{ +public: + /////////////////////////////// + // CreateSoundBuffer + /////////////////////////////// + // Creates a sound buffer to be used with mixing and output. + // + // Returns: + // -1 : Invalid Parameter + // -2 : Out of memory + // 0 : Ok! + virtual int CreateSoundBuffer( SysSoundBufferDesc *lbdesc, ISysSoundBuffer **lsndb ) = 0; +}; + +#endif diff --git a/libmve/lnxdsound.cpp b/libmve/lnxdsound.cpp new file mode 100644 index 000000000..bb9584b4f --- /dev/null +++ b/libmve/lnxdsound.cpp @@ -0,0 +1,865 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lnxdsound.h" +#include "args.h" + +#include "SDL.h" +#include "SDL_audio.h" + +#include + +#define FRAGMENT_LENGTH (LnxBuffers[0]->bps>>4) +#define FREQUENCY_SHIFT (14) + +/* + * TODO: + * * Might be wise to use mutex's for the enter/exit critical functions + */ +static int LnxNumBuffers = 0; +static LnxSoundBuffer **LnxBuffers = NULL; +static LnxSoundDevice LinuxSoundDevice; + +static bool StartupSoundSystem(LnxSoundDevice *dev); +static void ShutdownSoundSystem(void); +static void LinuxSoundMixWithVolume(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len); +static unsigned int LinuxSoundMixNormalize(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len); +static unsigned int LinuxSoundMixInMainBuffer(LnxSoundBuffer *dsb, int len); +static void LinuxSoundMixBuffersIntoMain(int len); +static void LinuxSoundThreadHandler(void *unused, Uint8 *stream, int len); + +static inline void enter_critical(void) +{ + SDL_LockAudio(); +} + +static inline void exit_critical(void) +{ + SDL_UnlockAudio(); +} + +/////////////////////////////// +// LnxSound_CreateSoundBuffer +/////////////////////////////// +// Creates a sound buffer to be used with mixing and output. +// +// Returns: +// -1 : Invalid Parameter +// -2 : Out of memory +// 0 : Ok! +int LnxSound_CreateSoundBuffer(LnxSoundDevice *dev,LnxBufferDesc *lbdesc,LnxSoundBuffer **lsndb) +{ + WAVEFORMATEX *wfex; + + if(!lbdesc || !lsndb || !dev) + return -1; + + wfex = lbdesc->lpwfxFormat; + if(!wfex) + return -1; + + // Check to see if we have a primary buffer yet, if not, create it + // now + if(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER) + { + if(LnxNumBuffers!=0) + return -1; + }else + { + if(LnxNumBuffers==0) + { + // we need to create a primary buffer + LnxSoundBuffer *primary; + LnxBufferDesc primdesc; + WAVEFORMATEX wf; + + memset(&primdesc,0,sizeof(LnxBufferDesc)); + memset(&wf,0,sizeof(wf)); + + primdesc.dwBufferBytes = 0; + primdesc.dwFlags = LNXSND_CAPS_PRIMARYBUFFER; + primdesc.lpwfxFormat = &wf; + + int ret = LnxSound_CreateSoundBuffer(dev,&primdesc,&primary); + if(ret!=0) + return ret; + } + } + + *lsndb = (LnxSoundBuffer *)malloc(sizeof(LnxSoundBuffer)); + if(!(*lsndb)) + return -2; + memset(*lsndb,0,sizeof(LnxSoundBuffer)); + + if(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER) + { + (*lsndb)->buffer_len = dev->bps; + (*lsndb)->freq = dev->freq; + (*lsndb)->bps = dev->bps; + (*lsndb)->buffer = NULL; + }else + { + (*lsndb)->buffer_len = lbdesc->dwBufferBytes; + (*lsndb)->freq = lbdesc->lpwfxFormat->nSamplesPerSec; + + (*lsndb)->buffer = (unsigned char *)malloc((*lsndb)->buffer_len); + if(!(*lsndb)->buffer) + { + free(*lsndb); + *lsndb = NULL; + return -2; + } + memset((*lsndb)->buffer,0,(*lsndb)->buffer_len); + } + + (*lsndb)->play_cursor = 0; + (*lsndb)->write_cursor = 0; + (*lsndb)->playing = 0; + (*lsndb)->left_vol = (1 << 15); + (*lsndb)->right_vol = (1 << 15); + + if(!(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER)) + { + (*lsndb)->freq_adjustment = ((*lsndb)->freq<freq; + (*lsndb)->bps = (*lsndb)->freq * lbdesc->lpwfxFormat->nBlockAlign; + } + + memcpy(&((*lsndb)->lbdesc),lbdesc,sizeof(LnxBufferDesc)); + + if(!(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER)) + { + memcpy(&((*lsndb)->wfx),lbdesc->lpwfxFormat,sizeof(WAVEFORMATEX)); + }else + { + // set up the wave format based on the device settings (primary) + (*lsndb)->wfx.wFormatTag = WAVE_FORMAT_PCM; + (*lsndb)->wfx.nChannels = dev->channels; + (*lsndb)->wfx.nSamplesPerSec = dev->freq; + (*lsndb)->wfx.nBlockAlign = dev->channels * (dev->bit_depth/8); + (*lsndb)->wfx.nAvgBytesPerSec = dev->freq * (*lsndb)->wfx.nBlockAlign; + (*lsndb)->wfx.wBitsPerSample = dev->bit_depth; + (*lsndb)->wfx.cbSize = 0; + } + + + if(LnxBuffers) + { + enter_critical(); + LnxBuffers = (LnxSoundBuffer **)realloc(LnxBuffers,sizeof(LnxSoundBuffer *)*(LnxNumBuffers+1)); + LnxBuffers[LnxNumBuffers] = *lsndb; + LnxNumBuffers++; + exit_critical(); + }else + { + LnxBuffers = (LnxSoundBuffer **)malloc(sizeof(LnxSoundBuffer *)); + LnxBuffers[0] = *lsndb; + LnxNumBuffers++; + + // Initialize the Sound system and thread + StartupSoundSystem(dev); + } + + return 0; +} + +//////////////////////////// +// LnxSoundBuffer_Release +//////////////////////////// +// Releases the memory associated with a sound buffer. This pointer is +// no longer valid after return. +// +// Returns: +// -1 : Invalid Parameter +// 0 : Ok! +int LnxSoundBuffer_Release(LnxSoundBuffer *buff) +{ + int i; + + if(!buff) + return -1; + + for(i=0;ibuffer) + free(buff->buffer); + free(buff); + }else + return -1; + + if(LnxNumBuffers==1) + { + // we freed the last non-primary buffer + // so remove the primary buffer that is remaining + return LnxSoundBuffer_Release(LnxBuffers[0]); + } + + return 0; +} + +////////////////////////////// +// LnxSoundBuffer_SetVolume +////////////////////////////// +// Sets the volume of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set volume +// -2 : Invalid parameters +int LnxSoundBuffer_SetVolume(LnxSoundBuffer *buff,signed long vol) +{ + if(!buff) + return -1; + + if(!(buff->lbdesc.dwFlags&LNXSND_CAPS_CTRLVOLUME)) + return -1; + + if((vol>LNXSND_VOLUME_MAX)||(vollbdesc.dwFlags&LNXSND_CAPS_PRIMARYBUFFER) + { + // not supported + enter_critical(); + buff->volume = vol; + exit_critical(); + return 0; + } + + enter_critical(); + + buff->volume = vol; + + double vt; + vt = (double)(buff->volume-(buff->pan>0?buff->pan:0)); + buff->left_vol = (unsigned long)(pow(2.0,vt/600.0)*32768.0); + vt = (double)(buff->volume+(buff->pan<0?buff->pan:0)); + buff->right_vol = (unsigned long)(pow(2.0,vt/600.0)*32768.0); + + exit_critical(); + + return 0; +} + +/////////////////////////// +// LnxSoundBuffer_SetPan +/////////////////////////// +// Sets the pan of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set pan +// -2 : Invalid parameters +int LnxSoundBuffer_SetPan(LnxSoundBuffer *buff,signed long pan) +{ + if(!buff) + return -1; + + if((pan > LNXSND_PAN_RIGHT) || (pan < LNXSND_PAN_LEFT)) + return -2; + + if(!(buff->lbdesc.dwFlags&LNXSND_CAPS_CTRLPAN)|| + (buff->lbdesc.dwFlags&LNXSND_CAPS_PRIMARYBUFFER)) + { + return -1; + } + + enter_critical(); + + buff->pan = pan; + + double pt; + pt = (double)(buff->volume-(buff->pan>0?buff->pan:0)); + buff->left_vol = (unsigned long)(pow(2.0,pt/600.0)*32768.0); + pt = (double)(buff->volume+(buff->pan<0?buff->pan:0)); + buff->right_vol = (unsigned long)(pow(2.0,pt/600.0)*32768.0); + + exit_critical(); + + return 0; +} + +///////////////////////// +// LnxSoundBuffer_Stop +///////////////////////// +// Stops a buffer from playing +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Stop(LnxSoundBuffer *buff) +{ + if(!buff) + return -1; + + enter_critical(); + buff->playing = 0; + exit_critical(); + return 0; +} + +///////////////////////// +// LnxSoundBuffer_Play +///////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Play(LnxSoundBuffer *buff,unsigned int flags) +{ + if(!buff) + return -1; + + enter_critical(); + buff->flags = flags; + buff->playing = 1; + exit_critical(); + return 0; +} + +//////////////////////////// +// LnxSoundBuffer_GetCaps +//////////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCaps(LnxSoundBuffer *buff,LinuxSoundCaps *caps) +{ + if(!caps || !buff) + return -1; + + caps->dwFlags = buff->lbdesc.dwFlags|LNXSND_CAPS_LOCSOFTWARE; + caps->dwBufferBytes = buff->lbdesc.dwBufferBytes; + + return 0; +} + +////////////////////////////// +// LnxSoundBuffer_GetStatus +////////////////////////////// +// Returns the status of a buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetStatus(LnxSoundBuffer *buff,unsigned int *status) +{ + if(!status || !buff) + return -1; + + *status = 0; + if(buff->playing) *status |= LNXSND_PLAYING; + if(buff->flags&LNXSND_LOOPING) *status |= LNXSND_LOOPING; + + return 0; +} + +/////////////////////////////////////// +// LnxSoundBuffer_GetCurrentPosition +/////////////////////////////////////// +// Returns the current play and write positions of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCurrentPosition(LnxSoundBuffer *buff,unsigned int *ppos,unsigned int *wpos) +{ + if(!buff) + return -1; + + if(ppos) *ppos = buff->play_cursor; + if(wpos) *wpos = buff->write_cursor; + + return 0; +} + +/////////////////////////////////////// +// LnxSoundBuffer_SetCurrentPosition +/////////////////////////////////////// +// Sets the current play position of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_SetCurrentPosition(LnxSoundBuffer *buff,unsigned int pos) +{ + if(!buff) + return -1; + + enter_critical(); + buff->play_cursor = pos; + exit_critical(); + return 0; +} + +///////////////////////// +// LnxSoundBuffer_Lock +///////////////////////// +// Locks the given buffer, returning pointer(s) to the buffer(s) along with +// available the size of the buffer(s) for writing. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Lock( + LnxSoundBuffer *buff, + unsigned int pos, + unsigned int numbytes, + void **ptr1, + unsigned int *numbytes1, + void **ptr2, + unsigned int *numbytes2, + unsigned int flags) +{ + if(!buff) + return -1; + + if(flags&LNXSND_LOCK_FROMWRITECURSOR) + pos += buff->write_cursor; + if(flags&LNXSND_LOCK_ENTIREBUFFER) + numbytes = buff->buffer_len; + if(numbytes>buff->buffer_len) + numbytes = buff->buffer_len; + + assert(numbytes1!=numbytes2); + assert(ptr1!=ptr2); + + if(pos+numbytes<=buff->buffer_len) + { + *(unsigned char **)ptr1 = buff->buffer+pos; + *numbytes1 = numbytes; + if(ptr2) + *(unsigned char **)ptr2 = NULL; + if(numbytes2) + *numbytes2 = 0; + }else + { + *(unsigned char **)ptr1 = buff->buffer+pos; + *numbytes1 = buff->buffer_len-pos; + if(ptr2) + *(unsigned char **)ptr2 = buff->buffer; + if(numbytes2) + *numbytes2 = numbytes-(buff->buffer_len-pos); + } + return 0; +} + +/////////////////////////// +// LnxSoundBuffer_Unlock +/////////////////////////// +// Unlocks a buffer. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Unlock( + LnxSoundBuffer *buff, + void *ptr1, + unsigned int num1, + void *ptr2, + unsigned int num2) +{ + if(!buff) + return -1; + + return 0; +} + +/////////////////////////////////////////// +// Internal Sound System routines +////////////////////////////////////////////////////////////// + +// Starts up the sound processing thread +static bool StartupSoundSystem(LnxSoundDevice *dev) +{ + SDL_AudioSpec spec; + + if(LnxNumBuffers<1) + return false; + + const int kDefaultSampleCount = 1024; + int sampleCount = kDefaultSampleCount; + int sampleArgIndex = FindArg("-sdlSndSizeMovie"); + if( sampleArgIndex == 0 ) + { + sampleArgIndex = FindArg("-sdlSndSize"); + } + if( sampleArgIndex != 0 ) + { + const char* sampleCountStr = GetArg( sampleArgIndex + 1 ); + if( sampleCountStr ) + { + sampleCount = atoi( sampleCountStr ); + if( sampleCount <= 0 ) + { + sampleCount = kDefaultSampleCount; + } + } + } + + memcpy(&LinuxSoundDevice,dev,sizeof(LnxSoundDevice)); + spec.freq = dev->freq; + spec.format = dev->bit_depth == 8 ? AUDIO_U8 : AUDIO_S16SYS; + spec.channels = dev->channels; + spec.samples = sampleCount; + spec.callback = LinuxSoundThreadHandler; + + if ( SDL_OpenAudio(&spec, NULL) < 0 ) { + return false; + } + SDL_PauseAudio(0); + return true; +} + +// Shutsdown the sound processing thread +static void ShutdownSoundSystem(void) +{ + SDL_CloseAudio(); +} + +static inline void GetValues(const LnxSoundBuffer *dsb, unsigned char *buf, unsigned int *fl, unsigned int *fr) +{ + signed short *bufs = (signed short *) buf; + + // 8 bit stereo + if((dsb->wfx.wBitsPerSample==8)&&dsb->wfx.nChannels==2) + { + *fl = (*buf - 128) << 8; + *fr = (*(buf+1) - 128) << 8; + return; + } + + // 16 bit stereo + if((dsb->wfx.wBitsPerSample==16)&&dsb->wfx.nChannels==2) + { + *fl = *bufs; + *fr = *(bufs + 1); + return; + } + + // 8 bit mono + if((dsb->wfx.wBitsPerSample==8)&&dsb->wfx.nChannels==1) + { + *fl = (*buf - 128)<<8; + *fr = *fl; + return; + } + + // 16 bit mono + if((dsb->wfx.wBitsPerSample==16)&&dsb->wfx.nChannels==1) + { + *fl = *bufs; + *fr = *bufs; + return; + } + return; +} + +static inline void SetValues(unsigned char *buf,unsigned int fl,unsigned int fr) +{ + signed short *bufs = (signed short *) buf; + + // 8 bit stereo + if((LnxBuffers[0]->wfx.wBitsPerSample==8)&&(LnxBuffers[0]->wfx.nChannels==2)) + { + *buf = (fl+32768)>>8; + *(buf + 1) = (fr+32768)>>8; + return; + } + + // 16 bit stereo + if((LnxBuffers[0]->wfx.wBitsPerSample==16)&&(LnxBuffers[0]->wfx.nChannels==2)) + { + *bufs = fl; + *(bufs + 1) = fr; + return; + } + + // 8 bit mono + if((LnxBuffers[0]->wfx.wBitsPerSample==8)&&(LnxBuffers[0]->wfx.nChannels==1)) + { + *buf = (((fl+fr)>>1)+32768)>>8; + return; + } + + // 16 bit mono + if((LnxBuffers[0]->wfx.wBitsPerSample==16)&&(LnxBuffers[0]->wfx.nChannels==1)) + { + *bufs = (fl+fr)>>1; + return; + } + return; +} + +static void LinuxSoundMixWithVolume(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len) +{ + unsigned int i,inc=(LnxBuffers[0]->wfx.wBitsPerSample>>3); + unsigned char *bpc=buf; + signed short *bps=(signed short *)buf; + + if ((!(dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLPAN)||(dsb->pan==0)) && + (!(dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLVOLUME)||(dsb->volume==0))) + return; + + for(i=0;iright_vol:dsb->left_vol))>>15); + *bpc=val+128; + bpc++; + }break; + case 2: + { + val=*bps; + val=((val*((i&inc)?dsb->right_vol:dsb->left_vol))>>15); + *bps=val; + bps++; + }break; + } + } +} + +static unsigned int LinuxSoundMixNormalize(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len) +{ + unsigned int i, size, ipos, ilen, fieldL, fieldR; + unsigned char *ibp, *obp; + unsigned int iAdvance = dsb->wfx.nBlockAlign; + unsigned int oAdvance = LnxBuffers[0]->wfx.nBlockAlign; + + ibp=dsb->buffer+dsb->play_cursor; + obp=buf; + + if ((dsb->freq==LnxBuffers[0]->wfx.nSamplesPerSec) && + (dsb->wfx.wBitsPerSample==LnxBuffers[0]->wfx.wBitsPerSample) && + (dsb->wfx.nChannels==LnxBuffers[0]->wfx.nChannels)) + { + if((ibp+len)<(unsigned char *)(dsb->buffer+dsb->buffer_len)) + memcpy(obp,ibp,len); + else + { + memcpy(obp,ibp,dsb->buffer_len-dsb->play_cursor); + memcpy(obp+(dsb->buffer_len-dsb->play_cursor),dsb->buffer,len-(dsb->buffer_len-dsb->play_cursor)); + } + return len; + } + + if(dsb->freq==LnxBuffers[0]->wfx.nSamplesPerSec) + { + ilen = 0; + for(i=0;i=(unsigned char *)(dsb->buffer+dsb->buffer_len)) + ibp=dsb->buffer; + } + return (ilen); + } + + size=len/oAdvance; + ilen=((size*dsb->freq_adjustment)>>FREQUENCY_SHIFT)*iAdvance; + for(i=0;ifreq_adjustment)>>FREQUENCY_SHIFT)*iAdvance)+dsb->play_cursor; + + if(ipos>=dsb->buffer_len) + ipos%=dsb->buffer_len; + + GetValues(dsb,(dsb->buffer+ipos),&fieldL,&fieldR); + SetValues(obp,fieldL,fieldR); + obp+=oAdvance; + } + return ilen; +} + +int DoMulDiv(int nNumber,int nNumerator,int nDenominator) +{ + if (!nDenominator) + return -1; + long long ret; + ret = (((long long)nNumber*nNumerator)+(nDenominator/2))/nDenominator; + + if((ret>0x7FFFFFFF)||(ret<0xFFFFFFFF)) + return -1; + return ret; +} + +static void *TempSoundBuffer = NULL; +static int TempSoundBufferLen = 0; +static unsigned int LinuxSoundMixInMainBuffer(LnxSoundBuffer *dsb, int len) +{ + unsigned int i,ilen,advance = (LnxBuffers[0]->wfx.wBitsPerSample>>3); + unsigned char *buf,*ibuf,*obuf; + signed int temp,field; + signed short *ibufs,*obufs; + + if(!(dsb->flags&LNXSND_LOOPING)) + { + temp=DoMulDiv(LnxBuffers[0]->wfx.nAvgBytesPerSec,dsb->buffer_len,dsb->bps)-DoMulDiv(LnxBuffers[0]->wfx.nAvgBytesPerSec,dsb->play_cursor,dsb->bps); + len=(len>temp)?temp:len; + } + len&=~3;//align to 4 byte boundary + + if(!len) + { + dsb->playing=0; + dsb->write_cursor=0; + dsb->play_cursor=0; + return 0; + } + + if(len>TempSoundBufferLen) + { + void *nb=realloc(TempSoundBuffer,len); + if(nb) + { + TempSoundBuffer=nb; + TempSoundBufferLen=len; + buf=ibuf=(unsigned char *)nb; + }else + { + return 0; + } + }else + { + buf=ibuf=(unsigned char *)TempSoundBuffer; + } + + ilen = LinuxSoundMixNormalize(dsb,ibuf,len); + if((dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLPAN)||(dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLVOLUME)) + { + LinuxSoundMixWithVolume(dsb,ibuf,len); + } + + obuf = LnxBuffers[0]->buffer + LnxBuffers[0]->play_cursor; + for(i=0;iwfx.wBitsPerSample==16) + { + field=*ibufs; + field+=*obufs; + field=(field>32767)?(32767):field; + field=(field<-32768)?(-32768):field; + *obufs=field; + }else + { + field=(*ibuf-128); + field+=(*obuf-128); + field=(field>127)?(127):field; + field=(field<-128)?(-128):field; + *obuf=field+128; + } + ibuf+=advance; + obuf+=advance; + if(obuf>=(unsigned char *)(LnxBuffers[0]->buffer+LnxBuffers[0]->buffer_len)) + obuf = LnxBuffers[0]->buffer; + } + + // adjust positions of the cursors in the buffer + dsb->play_cursor+=ilen; + dsb->write_cursor=dsb->play_cursor+ilen; + + if(dsb->play_cursor>=dsb->buffer_len) + { + if(!(dsb->flags&LNXSND_LOOPING)) + { + // we're not looping, this buffer is done, reset it + dsb->playing=0; + dsb->write_cursor=0; + dsb->play_cursor=0; + }else + { + // loop back around + dsb->play_cursor=dsb->play_cursor%dsb->buffer_len; + } + } + + if(dsb->write_cursor>=dsb->buffer_len) + { + dsb->write_cursor=dsb->write_cursor%dsb->buffer_len; + } + + return len; +} + +static void LinuxSoundMixBuffersIntoMain(int len) +{ + LnxSoundBuffer *dsb; + + // only go to 1 since 0 is the main buffer + for(int i=LnxNumBuffers-1;i>0;i--) + { + if(!(dsb=LnxBuffers[i])) + continue; + + if(dsb->buffer_len && dsb->playing) { + LinuxSoundMixInMainBuffer(dsb, len); + } + } +} + + +static void LinuxSoundThreadHandler(void *unused, Uint8 *stream, int len) +{ + LnxBuffers[0]->buffer = stream; + LnxBuffers[0]->buffer_len = len; + LnxBuffers[0]->play_cursor = 0; + LnxBuffers[0]->write_cursor = 0; + + LinuxSoundMixBuffersIntoMain(len); + + LnxBuffers[0]->buffer = NULL; +} diff --git a/libmve/lnxdsound.h b/libmve/lnxdsound.h new file mode 100644 index 000000000..31a1dbf9e --- /dev/null +++ b/libmve/lnxdsound.h @@ -0,0 +1,187 @@ +#ifndef __LNX_DSOUND_H_ +#define __LNX_DSOUND_H_ + +#include "SystemInterfaces.h" +typedef struct +{ + int sound_device; // file device handle for sound + unsigned int bps; // (bytes per second) channels*freq*bit_depth/8 + unsigned int freq; // frequency (22050, etc.) + unsigned int bit_depth; // 8 or 16 + unsigned int channels; // 1 or 2 (mono or stereo) +}LnxSoundDevice; + +#define WAVEFORMATEX SoundWAVEFormatEx + +#define WAVE_FORMAT_PCM 0x01 + +#define LinuxSoundCaps SysSoundCaps + +#define LnxBufferDesc SysSoundBufferDesc + + +typedef struct +{ + int freq_adjustment; + int bps; + unsigned int buffer_len; + unsigned int play_cursor; + unsigned int write_cursor; + unsigned int flags; + unsigned long left_vol,right_vol; + + unsigned char *buffer; + + signed long volume; + signed long pan; + + WAVEFORMATEX wfx; + + LnxBufferDesc lbdesc; + + unsigned short freq; + char playing; + char __pad; +} LnxSoundBuffer; + +/////////////////////////////// +// LnxSound_CreateSoundBuffer +/////////////////////////////// +// Creates a sound buffer to be used with mixing and output. +// +// Returns: +// -1 : Invalid Parameter +// -2 : Out of memory +// 0 : Ok! +int LnxSound_CreateSoundBuffer(LnxSoundDevice *dev,LnxBufferDesc *lbdesc,LnxSoundBuffer **lsndb); + +//////////////////////////// +// LnxSoundBuffer_Release +//////////////////////////// +// Releases the memory associated with a sound buffer. This pointer is +// no longer valid after return. +// +// Returns: +// -1 : Invalid Parameter +// 0 : Ok! +int LnxSoundBuffer_Release(LnxSoundBuffer *buff); + +////////////////////////////// +// LnxSoundBuffer_SetVolume +////////////////////////////// +// Sets the volume of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set volume +// -2 : Invalid parameters +int LnxSoundBuffer_SetVolume(LnxSoundBuffer *buff,signed long vol); + +/////////////////////////// +// LnxSoundBuffer_SetPan +/////////////////////////// +// Sets the pan of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set pan +// -2 : Invalid parameters +int LnxSoundBuffer_SetPan(LnxSoundBuffer *buff,signed long pan); + +///////////////////////// +// LnxSoundBuffer_Stop +///////////////////////// +// Stops a buffer from playing +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Stop(LnxSoundBuffer *buff); + +///////////////////////// +// LnxSoundBuffer_Play +///////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Play(LnxSoundBuffer *buff,unsigned int flags); + +//////////////////////////// +// LnxSoundBuffer_GetCaps +//////////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCaps(LnxSoundBuffer *buff,LinuxSoundCaps *caps); + +////////////////////////////// +// LnxSoundBuffer_GetStatus +////////////////////////////// +// Returns the status of a buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetStatus(LnxSoundBuffer *buff,unsigned int *status); + +/////////////////////////////////////// +// LnxSoundBuffer_GetCurrentPosition +/////////////////////////////////////// +// Returns the current play and write positions of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCurrentPosition(LnxSoundBuffer *buff,unsigned int *ppos,unsigned int *wpos); + +/////////////////////////////////////// +// LnxSoundBuffer_SetCurrentPosition +/////////////////////////////////////// +// Sets the current play position of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_SetCurrentPosition(LnxSoundBuffer *buff,unsigned int pos); + +///////////////////////// +// LnxSoundBuffer_Lock +///////////////////////// +// Locks the given buffer, returning pointer(s) to the buffer(s) along with +// available the size of the buffer(s) for writing. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Lock( + LnxSoundBuffer *buff, + unsigned int pos, + unsigned int numbytes, + void **ptr1, + unsigned int *numbytes1, + void **ptr2, + unsigned int *numbytes2, + unsigned int flags); + +/////////////////////////// +// LnxSoundBuffer_Unlock +/////////////////////////// +// Unlocks a buffer. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Unlock( + LnxSoundBuffer *buff, + void *ptr1, + unsigned int num1, + void *ptr2, + unsigned int num2); + +#endif diff --git a/libmve/mve.asm b/libmve/mve.asm new file mode 100644 index 000000000..e7fcf3749 --- /dev/null +++ b/libmve/mve.asm @@ -0,0 +1,9635 @@ +; .386 + .486 ; I only need .386, but I wanted the 486 cycle timings + .MODEL FLAT, C + +;;--- Types --- + +PTRBYTE TYPEDEF PTR BYTE +PTRWORD TYPEDEF PTR WORD +PTRDWORD TYPEDEF PTR DWORD +PTRPROC TYPEDEF PTR PROC + +;;--- Constants --- + +; Width and height of sections in pixels. +SWIDTH equ 8 +SHEIGHT equ 8 + +LOG2_SWIDTH equ 3 +LOG2_SHEIGHT equ 3 + +;;--- + +EXTERN pal_tbl:BYTE ;unsigned char pal_tbl[3*256]; +EXTERN pal15_tbl:WORD ;unsigned short pal15_tbl[256]; +;; NextFrame working storage + ; MemRec nf_mem_buf1; + ; MemRec nf_mem_buf2; +EXTERN nf_buf_cur: PTRBYTE ; unsigned char* nf_buf_cur; +EXTERN nf_buf_prv: PTRBYTE ; unsigned char* nf_buf_prv; + +;; NextFrame parameters +EXTERN nf_wqty: BYTE ;unsigned char nf_wqty; // (width/SWIDTH) +EXTERN nf_hqty: BYTE ;unsigned char nf_hqty; // (height/SHEIGHT) +EXTERN nf_fqty: BYTE ;unsigned char nf_fqty; // Number of fields +EXTERN nf_hicolor: DWORD ;unsigned nf_hicolor; // HiColor (0:none,1:normal,2:swapped) +;; +EXTERN nf_width: DWORD ;unsigned nf_width; // wqty * SWIDTH +EXTERN nf_height: DWORD ;unsigned nf_height; // hqty * SHEIGHT; +EXTERN nf_new_line: DWORD ;unsigned nf_new_line; // width - SWIDTH +EXTERN nf_new_row0: DWORD ;unsigned nf_new_row0; // SHEIGHT*width*2-width +EXTERN nf_back_right: DWORD ;unsigned nf_back_right; // (SHEIGHT-1)*width + +;; Frame parameters +;; Portion of current frame which has been updated +;; and needs to be sent to screen. +;; +EXTERN nf_new_x: DWORD ;unsigned nf_new_x; +EXTERN nf_new_y: DWORD ;unsigned nf_new_y; +EXTERN nf_new_w: DWORD ;unsigned nf_new_w; +EXTERN nf_new_h: DWORD ;unsigned nf_new_h; + + .data + + BYTE "(c) 1997 Interplay Productions. All Rights Reserved.\n" + BYTE "This file is confidential and consists of proprietary information\n" + BYTE "of Interplay Productions. This file and associated libraries\n" + BYTE "may not, in whole or in part, be disclosed to third parties,\n" + BYTE "incorporated into any software product which is not being created\n" + BYTE "for Interplay Productions, copied or duplicated in any form,\n" + BYTE "without the prior written permission of Interplay Productions.\n" + BYTE "Further, you may not reverse engineer, decompile or otherwise\n" + BYTE "attempt to derive source code of this material.\n",0 + + .code + + +NF_DECOMP_INIT MACRO HI_COLOR_FLAG: REQ + + mov ax, ds ; Insure es==ds for symantec flat mode + mov es, ax + + mov eax, nf_buf_prv ; DiffBufPtrs = nf_buf_prv - nf_buf_cur + sub eax, nf_buf_cur + mov DiffBufPtrs, eax + + xor ebx, ebx ; ebx = nf_fqty (convert to 32-bits) + mov bl, nf_fqty + + mov eax, x ; nf_new_x = x*SWIDTH*2^HI_COLOR_FLAG; + shl eax, LOG2_SWIDTH+HI_COLOR_FLAG + mov nf_new_x, eax + + mov eax, w ; nf_new_w = w*SWIDTH*2^HI_COLOR_FLAG; + shl eax, LOG2_SWIDTH+HI_COLOR_FLAG + mov nf_new_w, eax + + mov eax, y ; nf_new_y = y*nf_fqty*SHEIGHT; + shl eax, LOG2_SHEIGHT + mul ebx ;nf_fqty + mov nf_new_y, eax + + mov eax, h ; nf_new_h = h*nf_fqty*SHEIGHT; + shl eax, LOG2_SHEIGHT + mul ebx ;nf_fqty + mov nf_new_h, eax + + mov eax, nf_new_row0 ; new_row = nf_new_row0 - nf_new_w; + sub eax, nf_new_w + mov new_row, eax + + ;; Move to correct place in current buffer + mov eax, nf_buf_cur ; tbuf = nf_buf_cur + mov tbuf, eax + .if x || y ; if (x||y) + mov eax, nf_new_y ; tbuf += nf_new_y*nf_width + nf_new_x; + mul nf_width + add eax, nf_new_x + add tbuf, eax + .endif + + ENDM ; DECOMP_INIT + +DECOMP_BODY MACRO HI_COLOR_FLAG:REQ + + LOCAL HI_COLOR_SCALE + HI_COLOR_SCALE equ HI_COLOR_FLAG+1 + NF_DECOMP_INIT HI_COLOR_FLAG + + mov eax, w ; parms_sz = (w*h*nf_fqty)<<1 + mul h + mul ebx ;nf_fqty + shl eax, 1 + mov parms_sz, eax + + ; esi indexes comp (to get new section data) + ; edi indexes current screen buffer + ; edx is a frequently used constant + ; ebx indexes section params + mov edi, tbuf + mov edx, nf_new_line ; width - SWIDTH + mov ebx, comp ; Parms index + mov esi, ebx + add esi, parms_sz ; Skip over flags (w*h*2) + + ; Iterate over params and copy new hires data to appropriate sections. + mov cl, nf_fqty +ns_0f: push ecx + push edi + mov ch, byte ptr h +ns_0: mov cl, byte ptr w +ns_1: cmp word ptr [ebx],0 + je ns_10 + add edi, SWIDTH*HI_COLOR_SCALE +ns_2: add ebx, 2 + dec cl + jnz ns_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ns_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ns_0f + jmp ns_99 + + ; Copy new data to one section + ; Enter with esi pointing to source data, edi to screen section. + + ; Assumes SWIDTH=8 (16-bit data) and SHEIGHT=8 +ns_10: + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + jmp ns_2 + +ns_99: + + ; Iterate over flags and motion source addresses from params + ; to determine which sections to move. + ; ebx indexes params. + ; esi indexes source from buffer + ; esi will be computed as +- 16K relative to edi. + + sub ebx, parms_sz ; Move back to start of section parms + mov edi, tbuf + mov cl, nf_fqty + xor esi, esi +ms_0f: push ecx + push edi + mov ch, byte ptr h +ms_0: mov cl, byte ptr w +ms_1: or si, [ebx] + jg ms_10 + jl ms_j30 + add edi, SWIDTH*HI_COLOR_SCALE +ms_2: add ebx, 2 + dec cl + jnz ms_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_0f + jmp ms_99 + +ms_j30: jmp ms_30 + + ; Move one section from current screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_10: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-04000h*HI_COLOR_SCALE+edi] + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + xor esi, esi ; Reset esi to zero + jmp ms_2 + + +ms_20f: push ecx + push edi + mov ch, byte ptr h +ms_20: mov cl, byte ptr w +ms_21: or si, [ebx] + jl ms_30 + jg ms_j10 + add edi, SWIDTH*HI_COLOR_SCALE +ms_22: add ebx, 2 + dec cl + jnz ms_21 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_20 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_20f + jmp ms_99 + +ms_j10: jmp ms_10 + + ; Move one section from previous screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_30: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-0C000h*HI_COLOR_SCALE+edi] + add esi, DiffBufPtrs ; and point to other buffer + + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + xor esi, esi ; Reset esi to zero + jmp ms_22 + +ms_99: + ENDM ; DECOMP_BODY + +; Non-HiColor versions + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; +;void nfDecomp(unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfDecomp PROC USES ESI EDI EBX, comp:PTRBYTE, x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + LOCAL parms_sz: DWORD + + .if nf_hicolor + INVOKE nfHiColorDecomp, comp,x,y,w,h + ret + .endif + + DECOMP_BODY 0 ; Not HiColor + + ret +nfDecomp ENDP + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; + +;void +;nfHiColorDecomp(unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfHiColorDecomp PROC USES ESI EDI EBX,comp:PTRBYTE,x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + LOCAL parms_sz: DWORD + + DECOMP_BODY 1 ; HiColor + + ret +nfHiColorDecomp ENDP + +DECOMP_CHG_BODY MACRO HI_COLOR_FLAG:REQ + + LOCAL HI_COLOR_SCALE + HI_COLOR_SCALE equ HI_COLOR_FLAG+1 + NF_DECOMP_INIT HI_COLOR_FLAG + + ; esi indexes comp (to get new section data) + ; edi indexes current screen buffer + ; edx is a frequently used constant + ; ebx indexes section params + mov edi, tbuf + mov edx, nf_new_line ; width - SWIDTH + mov esi, comp + mov ebx, parms + + ; Iterate over params and copy new hires data to appropriate sections. + + mov eax, chgs + mov pChgs, eax + mov eax, 0 + mov cl, nf_fqty +ns_0f: push ecx + push edi + mov ch, byte ptr h +ns_0: mov cl, byte ptr w +ns_1: add ax, ax + ja ns_1b + jz ns_5 + cmp word ptr [ebx],0 + je ns_10 + add ebx, 2 +ns_1b: add edi, SWIDTH*HI_COLOR_SCALE +ns_2: dec cl + jnz ns_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ns_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ns_0f + jmp ns_99 + +ns_5: mov eax, pChgs + add pChgs, 2 + mov ax, [eax] + jmp ns_1 + + ; Copy new data to one section + ; Enter with ds:si pointing to source data, es:di to screen section. + + ; Assumes SWIDTH=8 (16-bit data) and SHEIGHT=8 +ns_10: + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + add ebx, 2 + jmp ns_2 + +ns_99: + + ; Iterate over flags and motion source addresses from params + ; to determine which sections to move. + ; ebx indexes params. + ; esi indexes source from buffer + ; esi will be computed as +- 16K relative to edi. + + mov edi, tbuf + mov ebx, parms + + mov eax, chgs + mov pChgs, eax + mov eax, 0 + mov cl, byte ptr nf_fqty + xor esi, esi +ms_0f: push ecx + push edi + mov ch, byte ptr h +ms_0: mov cl, byte ptr w +ms_1: add ax, ax + ja ms_1b + jz ms_5 + or si, [ebx] + jg ms_10 + jl ms_j30 + add ebx, 2 +ms_1b: add edi, SWIDTH*HI_COLOR_SCALE +ms_2: dec cl + jnz ms_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_0f + jmp ms_99 + +ms_5: mov eax, pChgs + add pChgs, 2 + mov ax, word ptr [eax] + jmp ms_1 + + +ms_j30: jmp ms_30 + + ; Move one section from current screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_10: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-04000h*HI_COLOR_SCALE+edi] + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + xor esi, esi ; Reset esi to zero + add ebx, 2 + jmp ms_2 + + +ms_20f: push ecx + push edi + mov ch, byte ptr h +ms_20: mov cl, byte ptr w +ms_21: add ax, ax + ja ms_21b + jz ms_25 + or si, [ebx] + jl ms_30 + jg ms_j10 + add ebx, 2 +ms_21b: add edi, SWIDTH*HI_COLOR_SCALE +ms_22: dec cl + jnz ms_21 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_20 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_20f + jmp ms_99 + +ms_25: mov eax, pChgs + add pChgs, 2 + mov ax, [eax] + jmp ms_21 + +ms_j10: jmp ms_10 + + ; Move one section from previous screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_30: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-0C000h*HI_COLOR_SCALE+edi] + add esi, DiffBufPtrs ; and point to other buffer + + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + add ebx, 2 + xor esi, esi ; Reset esi to zero + jmp ms_22 + +ms_99: + ENDM ; DECOMP_CHG_BODY + + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; Chgs specifies which squares to update. +; Parms are motion parms for squares to update. +; +;void +;nfDecompChg(unsigned short *chgs, +; unsigned short *parms, +; unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfDecompChg PROC USES ESI EDI EBX,chgs:PTRWORD, parms:PTRWORD,comp:PTRBYTE,x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + LOCAL pChgs: PTRBYTE + + .if nf_hicolor + INVOKE nfHiColorDecompChg, chgs,parms,comp,x,y,w,h + ret + .endif + + DECOMP_CHG_BODY 0 ; Not HiColor + + ret +nfDecompChg ENDP + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; Chgs specifies which squares to update. +; Parms are motion parms for squares to update. +; +;void +;nfHiColorDecompChg(unsigned short *chgs, +; unsigned short *parms, +; unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfHiColorDecompChg PROC USES ESI EDI EBX,chgs:PTRWORD,parms:PTRWORD,comp:PTRBYTE,x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + LOCAL pChgs: PTRBYTE + + DECOMP_CHG_BODY 1 ; HiColor + ret +nfHiColorDecompChg ENDP + + +.data + +; luminace table for palette entries +lum_tbl DWORD 256 DUP (0) + +; signed 8-bit y * nf_width +nfpk_ShiftY DWORD 256 DUP (0) + +; Constant tables + +; 8-bit -8:7 x nf_width + -8:7 +nfpk_ShiftP1 LABEL WORD +FOR y, <-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7> + FOR x, <-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7> + BYTE x,y + ENDM +ENDM + +; 8-bit to right and below in roughly 0:14*nf_width + -14:14 (-3 cases) +; negative is +; 8-bit to left and above in roughly -14:0*nf_width + -14:14 (-3 cases) +nfpk_ShiftP2 LABEL WORD +FOR y, <0,1,2,3,4,5,6,7> + FOR x, <8,9,10,11,12,13,14> + BYTE x,y + ENDM +ENDM +FOR y, <8,9,10,11,12,13> + FOR x, <-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1> + BYTE x,y + ENDM + FOR x, <0,1,2,3,4,5,6,7,8,9,10,11,12,13,14> + BYTE x,y + ENDM +ENDM +FOR x, <-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1> + BYTE x,14 +ENDM +FOR x, <0,1,2,3,4,5,6,7,8,9,10,11> + BYTE x,14 +ENDM + +nfpk_mov4l LABEL DWORD +; mov ax, bx,cx +MOV4L_REGS TEXTEQU > +%FOR m4, MOV4L_REGS +% FOR m3, MOV4L_REGS +% FOR m2, MOV4L_REGS +% FOR m1, MOV4L_REGS + BYTE m2,m1,m4,m3 + ENDM + ENDM + ENDM + ENDM + +nfpk_mov8 LABEL DWORD +; mov ax, bx/dx/cx/bp +MOV8_REGS TEXTEQU > +%FOR m4, MOV8_REGS +% FOR m3, MOV8_REGS +% FOR m2, MOV8_REGS +% FOR m1, MOV8_REGS + BYTE m2,m1,m4,m3 + ENDM + ENDM + ENDM + ENDM + +nfpk_mov4 LABEL DWORD +; mov al, bl/bh/cl/ch +MOV4_REGS0 TEXTEQU > +; mov ah, bl/bh/cl/ch +MOV4_REGS1 TEXTEQU > +%FOR m4, MOV4_REGS1 +% FOR m3, MOV4_REGS0 +% FOR m2, MOV4_REGS1 +% FOR m1, MOV4_REGS0 + BYTE m3,m4,m1,m2 + ENDM + ENDM + ENDM + ENDM + +.code + +; nfPkConfig initializes tables used by nfPkDecomp +; which are dependent on screen size. +nfPkConfig PROC USES ESI EDI EBX + + ; Build ShiftY table + ; + lea edi, nfpk_ShiftY + mov ebx, nf_width + + mov eax, 0 + mov ecx, 128 +lp1: mov [edi], eax + add edi,4 + add eax,ebx + dec ecx + jne lp1 + + mov eax, ebx + shl eax, 7 + neg eax + mov ecx, 128 +lp2: mov [edi], eax + add edi,4 + add eax,ebx + dec ecx + jne lp2 + + ret +nfPkConfig ENDP + + + +EXTERN sf_LineWidth: DWORD ;unsigned sf_LineWidth; // Distance between lines in memory + +; Banked screen parameters +EXTERN sf_SetBank: PTRPROC ;unsigned long sf_SetBank; +EXTERN sf_WinGran: DWORD ;unsigned sf_WinGran; +EXTERN sf_WinSize: DWORD ;unsigned long sf_WinSize; +EXTERN sf_WinGranPerSize: DWORD ;unsigned sf_WinGranPerSize; +;{sf_WriteWinPtr and sf_WriteWinLimit replace sf_WriteWinSeg, see mveliba.asm} +EXTERN sf_WriteWinPtr: PTRBYTE ;unsigned char *sf_WriteWinPtr; +EXTERN sf_WriteWinLimit: PTRBYTE ;unsigned char *WriteWinLimit; +EXTERN sf_WriteWin: DWORD ;unsigned sf_WriteWin; + +EXTERN opt_hscale_step: DWORD +EXTERN opt_hscale_adj: DWORD + + +;void mve_ShowFrameField( +; unsigned char *buf, unsigned bufw, unsigned bufh, +; unsigned sx, unsigned sy, unsigned w, unsigned h, +; unsigned dstx, unsigned dsty, unsigned field) + +mve_ShowFrameField PROC USES ESI EDI EBX, buf:PTRBYTE, bufw:DWORD, bufh:DWORD, sx:DWORD, sy:DWORD, w:DWORD, h:DWORD, dstx:DWORD, dsty:DWORD, field:DWORD + LOCAL bank:DWORD + LOCAL w4:DWORD + LOCAL new_src_line:DWORD + LOCAL linestep:DWORD + LOCAL new_dst_line:DWORD + + mov ax, ds ; Insure es==ds for symantec flat mode + mov es, ax + + mov eax, w ; w4 = w>>2 + shr eax, 2 + mov w4, eax + +;;; +;;; In stretched width mode, we either keep 4/5 (a) of the source pixels, +;;; or duplicate every fourth pixel to magnify by 5/4 (b). +;;; In these cases, new_src_line is either bufw-w*5/4 (a) or bufw-w*4/5 (b). +;;; Let ScaleStep be 5 (a) or 3 (b) instead of 4. This is the amount to advance +;;; the source after copying 32-bits from source to destination. +;;; The coordinate system used for the source will be a simulated scaled system. +;;; Rather than scale height, I plan to use alternate vertical resolutions. However, +;;; it might be a good idea to also provide for scaled height in case we want a +;;; higher resolution border. +;;; Question: Do we still need to support transferring subrectangles? + + .if opt_hscale_step==4 + mov eax, bufw ; new_src_line = bufw - w + sub eax, w + mov new_src_line, eax + .else + mov eax, opt_hscale_adj + mov new_src_line, eax + .endif + + mov eax, sf_LineWidth ; linestep = sf_LineWidth<<1; + .if field ; if (field) + add eax, eax ; linestep <<= 1; + .endif + mov linestep, eax + + sub eax, w ; new_dst_line = linestep - w; + mov new_dst_line, eax + + mov eax, sy ; buf += sy*bufw + sx + mul bufw + add eax, sx + add buf, eax + + mov eax, sx ; dstx += sx + add dstx, eax + + ; This is a hack. We should pass in src x,y of origin + ; or make dstx/dsty absolute. + ; + mov eax, bufw ; if (field && sx >= (bufw>>1) + shr eax, 1 + .if field && sx >= eax + sub dstx, eax ; dstx -= bufw>>1 + .endif + + mov eax, sy ; dsty += sy + add dsty, eax + + .if sf_SetBank==0 ;------------------ + + + ; dst = WriteWinPtr + (dsty*linestep+dstx) + mov edi, sf_WriteWinPtr + mov eax, dsty + mul linestep + add eax, dstx + add edi, eax + + .if field & 1 + add edi, sf_LineWidth; + .endif + + mov eax, new_src_line + mov edx, new_dst_line + mov esi, buf + mov ebx, h + + .if opt_hscale_step==3 + sub edi, 8 +sf_lp2a:mov ecx, w4 + shr ecx, 2 + ALIGN 4 +sf_lp2b:mov eax, [esi] + mov [edi+8], eax + mov eax, [esi+3] + mov [edi+12], eax + add edi, 16 + mov eax, [esi+6] + mov [edi], eax + mov eax, [esi+9] + mov [edi+4], eax + add esi, 12 + dec ecx + jnz sf_lp2b + ; To avoid problem of last pixel coming from next line + ; with arrange for w%16==12, so here is where we copy + ; last 12 pixels. + mov eax, [esi] + mov [edi+8], eax + mov eax, [esi+3] + mov [edi+12], eax + add edi, 12 + mov eax, [esi+6] + mov [edi+4], eax + add esi, 9 + add esi, new_src_line + add edi, edx + dec ebx + jnz sf_lp2a + add edi, 8 + .else +sf_lp: mov ecx, w4 ;width/4 + rep movsd + add esi, eax + add edi, edx + dec ebx + jnz sf_lp + .endif + + .else ; sf_SetBank ;------------------ + + + mov esi, buf + + ; start = dsty * linestep + dstx + + mov eax, linestep + mul dsty + .if field & 1 + add eax, sf_LineWidth + .endif + add eax, dstx + ; bank = start / WinGran + ; dst = (start % WinGran) + sf_WriteWinPtr + mov edx, 0 + div sf_WinGran + mov bank, eax + mov edi, edx + add edi, sf_WriteWinPtr + + ; Select new bank + mov bh, 0 + mov bl, byte ptr sf_WriteWin + mov edx, bank + call sf_SetBank + ; eax/edx destroyed by sf_SetBank + +sf_0: ; rem = sf_WriteWinLimit - dst + mov eax, sf_WriteWinLimit + sub eax, edi + ; h2 = (rem+(LineWidth-w))/LineWidth + add eax, linestep + sub eax, w + mov edx, 0 + div linestep + ; if (h + +extern unsigned char* nf_buf_cur; +extern unsigned char* nf_buf_prv; +extern unsigned nf_new_x; +extern unsigned nf_new_y; +extern unsigned nf_new_w; +extern unsigned nf_new_h; +extern unsigned char nf_fqty; // Number of fields +extern unsigned nf_new_row0; // SHEIGHT*width*2-width +extern unsigned nf_width; // wqty * SWIDTH +extern unsigned nf_new_line; // width - SWIDTH +extern unsigned nf_back_right; // (SHEIGHT-1)*width +extern unsigned nf_hicolor; + +extern unsigned short nf_trans16_lo[256]; +extern unsigned short nf_trans16_hi[256]; + +extern signed short snd_8to16[256]; +void PkDecompWorker( const bool hiColor, const unsigned char *ops, const unsigned char *comp, const unsigned x, const unsigned y, const unsigned w, const unsigned h ); +void nfHPkDecomp(unsigned char *ops,unsigned char *comp,int x,int y,int w,int h); +void nfPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void nfPkConfig(void); +unsigned sndDecompM16(unsigned short *dst, unsigned char *src,unsigned len, unsigned prev); +unsigned sndDecompS16(unsigned short *dst, unsigned char *src,unsigned len, unsigned prev); + +void Trans16Blk(unsigned char *edi,const unsigned char *idx); +void DOnf_xycshift(const bool hiColor, const unsigned int eax, unsigned char *&edi,const int nfpk_back_right); +void DOnf_xypshift( const bool hiColor, const unsigned int eax, unsigned char *&edi, const int nfpk_back_right, const int DiffBufPtrs ); +void DOnf_shift( const bool hiColor, int eax, unsigned char *&edi, const int nfpk_back_right ); + +//-------------------------------------------------------------------- +// Sound Management +//-------------------- + +// Decompresses a mono stream containing len samples +// (src is len bytes, dst is len*2 bytes) +// prev is the previous decompression state or zero. +// Returns new decompression state. +unsigned sndDecompM16(unsigned short *dst, unsigned char *src,unsigned len, unsigned prev) +{ + unsigned int i,eax,ebx; + if(len==0) + return prev; + eax = prev; + ebx = 0; + + for(i=0;i>16); + ebx = 0; + + for(i=0;iDiffBufPtrs = nf_buf_prv - nf_buf_cur; + + if( HI_COLOR_FLAG ) + { + nf_new_x = nf->x<<(LOG2_SWIDTH+1); + nf_new_w = nf->w<<(LOG2_SWIDTH+1); + } + else + { + nf_new_x = nf->x<w<y<h<new_row = nf_new_row0 - nf_new_w; + + // Move to correct place in current buffer + nf->tbuf = nf_buf_cur; + if( nf->x || nf->y ) + { + nf->tbuf += nf_new_y*nf_width + nf_new_x; + } +} + +//---------------------------------------------------------------------- + +// signed 8-bit y * nf_width +signed int nfpk_ShiftY[256]; + +// Constant tables + +// 8-bit -8:7 x nf_width + -8:7 +signed short nfpk_ShiftP1[256]; +signed short nfpk_ShiftP2[256]; + +// Constant tables +// mov eax, ebx/ecx +// EBX = 0 +// ECX = 1 +unsigned char nfhpk_mov4l[64]; + +// mov ax, bx,cx +// EBX = 0 +// ECX = 1 +unsigned char nfpk_mov4l[64]; + +// mov ds:[edi+0/4/8/12], ebx/edx/ecx/ebp +// EBX = 0 +// EDX = 1 +// ECX = 2 +// EBP = 3 +unsigned char nfhpk_mov8[1024]; + +// mov ax, bx/dx/cx/bp +// BX = 0 +// DX = 1 +// CX = 2 +// BP = 3 +unsigned char nfpk_mov8[1024]; + +// mov eax, ebx/edx/ecx/ebp +// EBX = 0 +// EDX = 1 +// ECX = 2 +// EBP = 3 +unsigned char nfhpk_mov4[1024]; + +// mov al, bl/bh/cl/ch +// BL = 0 +// BH = 1 +// CL = 2 +// CH = 3 +unsigned char nfpk_mov4[1024]; + +class initme +{ +public: + initme() + { + int x,y; + int m4,m3,m2,m1; + signed char *ptr; + unsigned char *uptr; + + // Do nfhpk_mov4l + uptr = nfhpk_mov4l; + for(m4=0;m4<2;++m4) + for(m3=0;m3<2;++m3) + for(m2=0;m2<2;++m2) + for(m1=0;m1<2;++m1) + { + *uptr++ = m1; + *uptr++ = m2; + *uptr++ = m3; + *uptr++ = m4; + } + + // Do nfpk_mov4l + uptr = nfpk_mov4l; + for(m4=0;m4<2;++m4) + for(m3=0;m3<2;++m3) + for(m2=0;m2<2;++m2) + for(m1=0;m1<2;++m1) + { + *uptr++ = m1; + *uptr++ = m2; + *uptr++ = m3; + *uptr++ = m4; + } + + // Do nfhpk_mov8 + uptr = nfhpk_mov8; + for(m4=0;m4<4;++m4) + for(m3=0;m3<4;++m3) + for(m2=0;m2<4;++m2) + for(m1=0;m1<4;++m1) + { + *uptr++ = m1; + *uptr++ = m2; + *uptr++ = m3; + *uptr++ = m4; + } + + // Do nfpk_mov8 + uptr = nfpk_mov8; + for(m4=0;m4<4;++m4) + for(m3=0;m3<4;++m3) + for(m2=0;m2<4;++m2) + for(m1=0;m1<4;++m1) + { + *uptr++ = m1; + *uptr++ = m2; + *uptr++ = m3; + *uptr++ = m4; + } + + // Do nfhpk_mov4 + uptr = nfhpk_mov4; + for(m4=0;m4<4;++m4) + for(m3=0;m3<4;++m3) + for(m2=0;m2<4;++m2) + for(m1=0;m1<4;++m1) + { + *uptr++ = m1; + *uptr++ = m2; + *uptr++ = m3; + *uptr++ = m4; + } + + // Do nfpk_mov4 + uptr = nfpk_mov4; + for(m4=0;m4<4;++m4) + for(m3=0;m3<4;++m3) + for(m2=0;m2<4;++m2) + for(m1=0;m1<4;++m1) + { + *uptr++ = m1; + *uptr++ = m2; + *uptr++ = m3; + *uptr++ = m4; + } + + // do nfpk_ShiftP1 + ptr = (signed char *)nfpk_ShiftP1; + for(y=-8;y!=8;y++) + { + for(x=-8;x!=8;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + ptr[0] = y; + ptr[1] = x; + #else + ptr[0] = x; + ptr[1] = y; + #endif + ptr+=2; + } + } + + // do nfpk_ShiftP2[] + ptr = (signed char *)nfpk_ShiftP2; + for(y=0;y!=8;y++) + { + for(x=8;x!=15;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + ptr[0] = y; + ptr[1] = x; + #else + ptr[0] = x; + ptr[1] = y; + #endif + ptr+=2; + } + } + + for(y=8;y!=14;y++) + { + for(x=-14;x!=0;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + ptr[0] = y; + ptr[1] = x; + #else + ptr[0] = x; + ptr[1] = y; + #endif + ptr+=2; + } + + for(x=0;x!=15;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + ptr[0] = y; + ptr[1] = x; + #else + ptr[0] = x; + ptr[1] = y; + #endif + ptr+=2; + } + } + for(x=-14;x!=0;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + ptr[0] = 14; + ptr[1] = x; + #else + ptr[0] = x; + ptr[1] = 14; + #endif + ptr+=2; + } + for(x=0;x!=12;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + ptr[0] = 14; + ptr[1] = x; + #else + ptr[0] = x; + ptr[1] = 14; + #endif + ptr+=2; + } + } +}; + +initme _initme; + +// nfPkConfig initializes tables used by nfPkDecomp +// which are dependent on screen size. +void nfPkConfig(void) +{ + // Build ShiftY table + int index = 0; + int val; + + val = 0; + for(int i=0;i<128;++i) + { + nfpk_ShiftY[ index++ ] = val; + val += nf_width; + } + + val = -int( nf_width << 7 ); + for(int i=0;i<128;++i) + { + nfpk_ShiftY[ index++ ] = val; + val += nf_width; + } +} + +unsigned short Trans16( const unsigned char* idx ) +{ + return nf_trans16_lo[ idx[0] ] | nf_trans16_hi[ idx[1] ]; +} + +int SkipOpcode( int opcode_to_use, bool hiColor, const unsigned char* &esi, const unsigned char* &bcomp, unsigned char* &edi ) +{ + switch( opcode_to_use ) + { + case 0: + case 1: + case 2: + // no esi change + break; + case 3: + case 4: + if( hiColor ) + { + ++bcomp; + } + else + { + ++esi; + } + break; + case 5: + esi += 2; + break; + case 6: + if( hiColor ) + { + esi += 2; + } + else + { + // we can't skip this -- though I've never came across this + return opcode_to_use; + } + break; + case 7: + { + bool donf23 = false; + if( hiColor ) + { + unsigned short val = IntelSwapper(*(unsigned short *)esi); + if(val&0x8000) + { + donf23 = true; + } + } + else + { + if( esi[0] > esi[1] ) + { + donf23 = true; + } + } + + if( donf23 ) + { + esi += ( hiColor ) ? 6 : 4; + } + else + { + esi += ( hiColor ) ? 12 : 10; + } + }break; + + case 8: + { + bool mode2 = false; + if( hiColor ) + { + unsigned short val = IntelSwapper(*(unsigned short *)esi); + if(val&0x8000) + { + mode2 = true; + } + } + else + { + if( esi[0] > esi[1] ) + { + mode2 = true; + } + } + + if( !mode2 ) + { + esi += (hiColor) ? 24 : 16; + } + else + { + esi += (hiColor) ? 16 : 12; + } + }break; + + case 9: + { + bool mode2 = false; + bool mode3 = false; + + if( hiColor ) + { + unsigned short val = IntelSwapper(*(unsigned short *)esi); + if(val&0x8000) + { + mode2 = true; + } + else + { + val = IntelSwapper(*(unsigned short *)(esi+4)); + if(val&0x8000) + { + mode3 = true; + } + } + } + else + { + if( esi[0] > esi[1] ) + { + mode2 = true; + } + else if( esi[2] > esi[3] ) + { + mode3 = true; + } + } + + if( mode2 ) esi += (hiColor)?16:12; + else if( mode3 ) esi += (hiColor)?12:8; + else esi += (hiColor)?24:20; + }break; + case 10: + { + // 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + bool mode2 = false; + + if( hiColor ) + { + int val1 = IntelSwapper(*(unsigned short *)esi); + if(val1&0x8000) + { + mode2 = true; + } + } + else + { + if( esi[0] > esi[1] ) + { + mode2 = true; + } + } + + if( !mode2 ) + { + esi += (hiColor)?48:32; + } + else + { + esi += (hiColor)?32:24; + } + }break; + + case 11: + { + esi += (hiColor) ? 128 : 64; + }break; + + case 12: + { + esi += (hiColor) ? 32 : 16; + }break; + + case 13: + { + esi += (hiColor) ? 8 : 4; + }break; + + case 14: + { + esi += (hiColor) ? 2 : 1; + }break; + + case 15: + { + esi += (hiColor) ? 0 : 2; + }break; + } + return -1; +} + +// HiColor version +// +void nfHPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + PkDecompWorker( true, ops, comp, x, y, w, h ); +} + +void nfPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + PkDecompWorker( false, ops, comp, x, y, w, h ); +} + +void PkDecompWorker( const bool hiColor, const unsigned char *ops, const unsigned char *comp, const unsigned x, const unsigned y, unsigned w, unsigned h ) +{ +#ifdef OUTRAGE_BIG_ENDIAN +typedef struct { unsigned short hax,ax;}reg_word; +typedef struct { signed char hah,hal,ah,al;}reg_byte; +#else +typedef struct { unsigned short ax,hax;}reg_word; +typedef struct { signed char al,ah,hal,hah;}reg_byte; +#endif + + tNextFrame nf; + nf.w = w; + nf.h = h; + nf.x = x; + nf.y = y; + + NF_DECOMP_INIT( (hiColor) ? 1 : 0, &nf ); + const unsigned int kSWidthScaled = ( hiColor ) ? SWIDTH*2 : SWIDTH; + + int nfpk_back_right = nf_back_right - kSWidthScaled; + + const unsigned char* esi = comp; + unsigned char* edi = nf.tbuf; + + const unsigned char *bcomp = NULL; + if( hiColor ) + { + unsigned int bcompOffset = IntelSwapper(*((const unsigned short*)esi)); + bcomp = esi + bcompOffset; + esi += 2; + } + + int wcnt = w >> 1; + do + { + if( --wcnt < 0 ) + { + edi += nf.new_row; + --h; + wcnt = w >> 1; + continue; + } + + bool first_opcode = true; + bool skipNextOpCode = false; + + unsigned char opcode = *ops++; + int opcode_to_use = opcode & 0xF; + +do_next_opcode: + + + ///////////////////////////////////////////////////////////////////////////// + // Note: this is all you should have to tweak + bool opcodesToProcess[16] = { true, true, true, true, // 0 - 3 + true, true, true, true, // 4 - 7 + true, true, false, true, // 8 - 11 + true, true, true, true }; // 12 - 15 + if( !opcodesToProcess[opcode_to_use] ) + { + opcode_to_use = SkipOpcode( opcode_to_use, hiColor, esi, bcomp, edi ); + } + ///////////////////////////////////////////////////////////////////////////// + + + switch( opcode_to_use ) + { + case -1: + { + // DEBUG: skip processing + // Note: esi will have been adjusted above in SkipOpcode + + if( hiColor ) + { + // Here we can set the color to exactly what we want + const unsigned short kColor = 0; + unsigned short* pDst = reinterpret_cast( edi ); + for( int r = 0; r < 8; ++r ) + { + pDst[0] = kColor; + pDst[1] = kColor; + pDst[2] = kColor; + pDst[3] = kColor; + pDst[4] = kColor; + pDst[5] = kColor; + pDst[6] = kColor; + pDst[7] = kColor; + pDst += nf_width >> 1; + } + } + else + { + // Here we paint with the palette index we want + const unsigned int kPaletteIndex = 0; + + unsigned int* pDst = reinterpret_cast( edi ); + const unsigned int kPalIndexDWord = (kPaletteIndex << 24) | (kPaletteIndex << 16) | (kPaletteIndex << 8) | kPaletteIndex; + for( int r = 0; r < 8; ++r ) + { + pDst[0] = kPalIndexDWord; + pDst[1] = kPalIndexDWord; + pDst += nf_width >> 2; + } + } + + // Adjust edi + edi += kSWidthScaled; + }break; + + case 0: + { + // No change from previous buffer + DOnf_shift( hiColor, nf.DiffBufPtrs, edi, nfpk_back_right ); + }break; + case 1: + { + // No change (and copied to screen) + edi += kSWidthScaled; + }break; + case 2: + { + // Near shift from older part of current buffer + unsigned int offset; + if( hiColor ) + { + offset = *bcomp++; + } + else + { + offset = *esi++; + } + DOnf_xycshift( hiColor, nfpk_ShiftP2[ offset ], edi, nfpk_back_right ); + }break; + case 3: + { + // Near shift from newer part of current buffer + union + { + unsigned int eax; + reg_word word; + reg_byte byte; + }myeax; + + if( hiColor ) + { + myeax.eax = *bcomp++; + } + else + { + myeax.eax = *esi++; + } + + myeax.word.ax = nfpk_ShiftP2[ myeax.eax ]; + myeax.byte.al = -myeax.byte.al; + myeax.byte.ah = -myeax.byte.ah; + + DOnf_xycshift( hiColor, myeax.eax, edi, nfpk_back_right ); + }break; + case 4: + { + // Near shift from previous buffer + union + { + unsigned int eax; + reg_word word; + reg_byte byte; + }myeax; + + if( hiColor ) + { + myeax.eax = *bcomp++; + } + else + { + myeax.eax = *esi++; + } + + myeax.word.ax = nfpk_ShiftP1[myeax.eax]; + DOnf_xypshift( hiColor, myeax.eax, edi, nfpk_back_right, nf.DiffBufPtrs ); + }break; + case 5: + { + // Far shift from previous buffer + unsigned int eax = IntelSwapper(*(unsigned short *)(esi)); + esi += 2; + DOnf_xypshift( hiColor, eax, edi, nfpk_back_right, nf.DiffBufPtrs ); + }break; + case 6: + { + if( hiColor ) + { + // Far shift from current buffer + unsigned int val1 = IntelSwapper(*(unsigned short *)esi); + esi += 2; + DOnf_xycshift( hiColor, val1, edi, nfpk_back_right ); + } + else + { + // Run of no changes (must only appear in first nibble opcodes) + // Next nibble k specifies 2k+4 squares with no changes + skipNextOpCode = true; // Next nibble is not an opcode + unsigned int cnt = ((opcode >> 4)&0xF) + 2; // (minimum of 4 squares) + + while( true ) + { + edi += SWIDTH * 2; // Advance over two squares + if( --cnt == 0 ) + break; // Last pair of squares + + if( --wcnt >= 0 ) // Same row? + continue; // Yes + + edi += nf.new_row; // Advance to next row + --h; // Decrement row count (should never become zero here) + wcnt = (w >> 1) - 1; // Reset wcnt + } + } + }break; + case 7: + { + bool donf23 = false; + if( hiColor ) + { + unsigned short val = IntelSwapper(*(unsigned short *)esi); + if(val&0x8000) + { + donf23 = true; + } + } + else + { + if( esi[0] > esi[1] ) + { + donf23 = true; + } + } + + if( !donf23 ) + { + // 8x8x1 (12/10 bytes) + unsigned int colors[4]; + if( hiColor ) + { + unsigned int ecx = ( Trans16( esi + 2 ) << 16 ) | Trans16( esi ); + unsigned int edx = ( ecx >> 16 ) | ( ecx << 16 ); + unsigned int ebx = ( edx & 0xFFFF0000 ) | ( ecx & 0xFFFF ); + unsigned int ebp = ( ecx & 0xFFFF0000 ) | ( edx & 0xFFFF ); + colors[0] = ebx; + colors[1] = edx; + colors[2] = ecx; + colors[3] = ebp; + } + else + { + unsigned int tcLo = esi[0]; + unsigned int tcHi = esi[1]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + + const unsigned char* lookupTable = ( hiColor ) ? nfhpk_mov8 : nfpk_mov8; + const int kOffset = ( hiColor ) ? 4 : 2; + const int max_repcount = 8; + for( int rep_count = 0; rep_count < max_repcount; ++rep_count ) + { + const unsigned char* color_idx = lookupTable + (esi[rep_count + kOffset]*4); +#ifdef OUTRAGE_BIG_ENDIAN + unsigned int w1 = colors[ color_idx[3] ]; + unsigned int w2 = colors[ color_idx[2] ]; + unsigned int w3 = colors[ color_idx[1] ]; + unsigned int w4 = colors[ color_idx[0] ]; +#else + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; +#endif + if( hiColor ) + { + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + } + else + { + *(unsigned short *)(edi + 0) = w1; + *(unsigned short *)(edi + 2) = w2; + *(unsigned short *)(edi + 4) = w3; + *(unsigned short *)(edi + 6) = w4; + } + + edi += nf_width; + } + esi += hiColor ? 12 : 10; + edi -= nf_width; + edi -= nfpk_back_right; + } + else + { + // low 4x4x1 (6/4 bytes) + unsigned int colors[2]; + if( hiColor ) + { + unsigned int temp = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[0] = (temp<<16) | temp; + temp = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[1] = (temp<<16) | temp; + } + else + { + unsigned int tcLo = esi[0]; + unsigned int tcHi = esi[1]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcHi<<8); + } + + const unsigned char* esiOffset = ( hiColor ) ? (esi+4) : (esi+2); + const unsigned char* lookupTable = ( hiColor ) ? nfhpk_mov4l : nfpk_mov4l; + const int max_repcount = 4; + for( int rep_count = 0; rep_count < max_repcount; ++rep_count ) + { + int idx; + switch(rep_count) + { + case 0: idx = esiOffset[0] & 0xF; break; + case 1: idx = esiOffset[0]>>4; break; + case 2: idx = esiOffset[1] & 0xF; break; + case 3: idx = esiOffset[1]>>4; break; + } + + const unsigned char* color_idx = lookupTable + (idx*4); +#ifdef OUTRAGE_BIG_ENDIAN + unsigned int w1 = colors[ color_idx[3] ]; + unsigned int w2 = colors[ color_idx[2] ]; + unsigned int w3 = colors[ color_idx[1] ]; + unsigned int w4 = colors[ color_idx[0] ]; +#else + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; +#endif + + if( hiColor ) + { + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + *(unsigned int *)(edi + nf_width+0) = w1; + *(unsigned int *)(edi + nf_width+4) = w2; + *(unsigned int *)(edi + nf_width+8) = w3; + *(unsigned int *)(edi + nf_width+12) = w4; + } + else + { + *(unsigned short *)(edi + 0) = w1; + *(unsigned short *)(edi + 2) = w2; + *(unsigned short *)(edi + 4) = w3; + *(unsigned short *)(edi + 6) = w4; + *(unsigned short *)(edi + 0 + nf_width) = w1; + *(unsigned short *)(edi + 2 + nf_width) = w2; + *(unsigned short *)(edi + 4 + nf_width) = w3; + *(unsigned short *)(edi + 6 + nf_width) = w4; + } + + edi += nf_width*2; + } + edi -= nf_width*2; + edi += nf_width; + edi -= nfpk_back_right; + esi += ( hiColor ) ? 6 : 4; + } + }break; + case 8: + { + bool donf24 = false; + bool donf40 = false; + + if( hiColor ) + { + unsigned short val = IntelSwapper(*(unsigned short *)esi); + if(val&0x8000) + { + val = IntelSwapper(*(unsigned short *)(esi+8)); + if(val&0x8000) + { + donf40 = true; + } + else + { + donf24 = true; + } + } + } + else + { + if( esi[0] > esi[1] ) + { + if( esi[6] > esi[7] ) + { + donf40 = true; + } + else + { + donf24 = true; + } + } + } + + if( !donf24 && !donf40 ) + { + unsigned int colors[4]; + int max_repcount = 8; + + if( hiColor ) + { + unsigned int tempcolor; + tempcolor = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+0)] | nf_trans16_hi[*(esi+1)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + unsigned int tcLo = esi[0]; + unsigned int tcHi = esi[1]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + + int repLookup[8] = { 4, 5, 10, 11, 16, 17, 22, 23 }; + if( !hiColor ) + { + repLookup[0] -= 2; repLookup[1] -= 2; + repLookup[2] -= 4; repLookup[3] -= 4; + repLookup[4] -= 6; repLookup[5] -= 6; + repLookup[6] -= 8; repLookup[7] -= 8; + } + const unsigned char* lookupTable = ( hiColor ) ? nfhpk_mov8 : nfpk_mov8; + + for( int rep_count=0; rep_count < max_repcount; ++rep_count ) + { + int idx = repLookup[ rep_count ]; + const unsigned char* color_idx = lookupTable + (esi[idx]*4); +#ifdef OUTRAGE_BIG_ENDIAN + unsigned int w1 = colors[ color_idx[3] ]; + unsigned int w2 = colors[ color_idx[2] ]; + unsigned int w3 = colors[ color_idx[1] ]; + unsigned int w4 = colors[ color_idx[0] ]; +#else + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; +#endif + + if( hiColor ) + { + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + edi += nf_width; + *(unsigned int *)(edi + 0) = w3; + *(unsigned int *)(edi + 4) = w4; + edi += nf_width; + } + else + { + *(unsigned short *)(edi + 0) = w1; + *(unsigned short *)(edi + 2) = w2; + edi += nf_width; + *(unsigned short *)(edi + 0) = w3; + *(unsigned short *)(edi + 2) = w4; + edi += nf_width; + } + + if(rep_count==1) + { + if( hiColor ) + { + unsigned int tempcolor = nf_trans16_lo[*(esi+8)] | nf_trans16_hi[*(esi+9)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + unsigned int tcLo = esi[4]; + unsigned int tcHi = esi[5]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + } + else if(rep_count==3) + { + if( hiColor ) + { + edi -= nf_width*8-8; + unsigned int tempcolor = nf_trans16_lo[*(esi+14)] | nf_trans16_hi[*(esi+15)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+12)] | nf_trans16_hi[*(esi+13)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + edi -= nf_width*8-4; + unsigned int tcLo = esi[8]; + unsigned int tcHi = esi[9]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + } + else if(rep_count==5) + { + if( hiColor ) + { + unsigned int tempcolor = nf_trans16_lo[*(esi+20)] | nf_trans16_hi[*(esi+21)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+18)] | nf_trans16_hi[*(esi+19)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + unsigned int tcLo = esi[12]; + unsigned int tcHi = esi[13]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + } + } + + edi -= nf_width; + esi += (hiColor) ? 24 : 16; + edi -= ((hiColor) ? 8 : 4) + nfpk_back_right; + } + + if( donf24 ) + { + unsigned int colors[4]; + + if( hiColor ) + { + unsigned int tempcolor = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+0)] | nf_trans16_hi[*(esi+1)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + unsigned int tcLo = esi[0]; + unsigned int tcHi = esi[1]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + + int repLookupTable[8] = { 4, 5, 6, 7, 12, 13, 14, 15 }; + if( !hiColor ) + { + repLookupTable[0] -= 2; repLookupTable[1] -= 2; + repLookupTable[2] -= 2; repLookupTable[3] -= 2; + repLookupTable[4] -= 4; repLookupTable[5] -= 4; + repLookupTable[6] -= 4; repLookupTable[7] -= 4; + } + + const unsigned char* lookupTable = (hiColor) ? nfhpk_mov8 : nfpk_mov8; + + int max_repcount = 8; + for(int rep_count=0;rep_count>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + edi -= nf_width*8 - 4; + + unsigned int tcLo = esi[6]; + unsigned int tcHi = esi[7]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + } + } + + esi += (hiColor) ? 16 : 12; + edi -= nf_width + ((hiColor)?8:4) + nfpk_back_right; + } + + if( donf40 ) + { + unsigned int colors[4]; + + if( hiColor ) + { + unsigned int tempcolor = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+0)] | nf_trans16_hi[*(esi+1)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + unsigned int tcLo = esi[0]; + unsigned int tcHi = esi[1]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + + int repLookupTable[8] = { 4, 5, 6, 7, 12, 13, 14, 15 }; + if( !hiColor ) + { + repLookupTable[0] -= 2; repLookupTable[1] -= 2; + repLookupTable[2] -= 2; repLookupTable[3] -= 2; + repLookupTable[4] -= 4; repLookupTable[5] -= 4; + repLookupTable[6] -= 4; repLookupTable[7] -= 4; + } + const unsigned char* lookupTable = (hiColor) ? nfhpk_mov8 : nfpk_mov8; + + int max_repcount = 8; + for( int rep_count = 0; rep_count < max_repcount; ++rep_count ) + { + int idx = repLookupTable[ rep_count ]; + const unsigned char* color_idx = lookupTable + (esi[idx]*4); +#ifdef OUTRAGE_BIG_ENDIAN + unsigned int w1 = colors[ color_idx[3] ]; + unsigned int w2 = colors[ color_idx[2] ]; + unsigned int w3 = colors[ color_idx[1] ]; + unsigned int w4 = colors[ color_idx[0] ]; +#else + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; +#endif + if( hiColor ) + { + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + } + else + { + *(unsigned short *)(edi + 0) = w1; + *(unsigned short *)(edi + 2) = w2; + *(unsigned short *)(edi + 4) = w3; + *(unsigned short *)(edi + 6) = w4; + } + edi += nf_width; + + if( rep_count == 3 ) + { + if( hiColor ) + { + unsigned int tempcolor = nf_trans16_lo[*(esi+10)] | nf_trans16_hi[*(esi+11)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+8)] | nf_trans16_hi[*(esi+9)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + else + { + unsigned int tcLo = esi[6]; + unsigned int tcHi = esi[7]; + colors[0] = tcLo | (tcLo<<8); + colors[1] = tcHi | (tcLo<<8); + colors[2] = tcLo | (tcHi<<8); + colors[3] = tcHi | (tcHi<<8); + } + } + } + + esi += (hiColor)?16:12; + edi -= nf_width + nfpk_back_right; + } + }break; + case 9: + { + bool donf41 = false; + bool donf25 = false; + bool donf57 = false; + + if( hiColor ) + { + unsigned short val = IntelSwapper(*(unsigned short *)esi); + if(val&0x8000) + { + val = IntelSwapper(*(unsigned short *)(esi+4)); + if(val&0x8000) + { + donf57 = true; + }else + { + donf41 = true; + } + + } + else + { + val = IntelSwapper(*(unsigned short *)(esi+4)); + if(val&0x8000) + { + donf25 = true; + } + } + } + else + { + if( esi[0] > esi[1] ) + { + if( esi[2] > esi[3] ) + { + donf57 = true; + } + else + { + donf41 = true; + } + } + else if( esi[2] > esi[3] ) + { + donf25 = true; + } + } + + if( donf57 ) + { + unsigned short colors[4]; + if( hiColor ) + { + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + } + else + { + colors[0] = esi[0]; + colors[1] = esi[1]; + colors[2] = esi[2]; + colors[3] = esi[3]; + } + + const int max_repcount = 8; + const unsigned char* lookupTable = (hiColor) ? nfhpk_mov4 : nfpk_mov4; + for(int rep_count = 0; rep_count < max_repcount; ++rep_count ) + { + int idx = rep_count + ((hiColor)?8:4); + const unsigned char* color_idx = lookupTable + (esi[idx]*4); + + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; + + if(rep_count&1) + { +#ifdef OUTRAGE_BIG_ENDIAN + if( hiColor ) + { + *(unsigned int *)(edi + 8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + + *(unsigned int *)(edi + 12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + } + else + { + *(unsigned char*)( edi + 4 ) = w4; + *(unsigned char*)( edi + 5 ) = w3; + *(unsigned char*)( edi + 6 ) = w2; + *(unsigned char*)( edi + 7 ) = w1; + + *(unsigned char*)( edi + nf_width + 4 ) = w4; + *(unsigned char*)( edi + nf_width + 5 ) = w3; + *(unsigned char*)( edi + nf_width + 6 ) = w2; + *(unsigned char*)( edi + nf_width + 7 ) = w1; + } +#else + if( hiColor ) + { + *(unsigned int *)(edi + 8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + + *(unsigned int *)(edi + 12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + } + else + { + *(unsigned char*)( edi + 4 ) = w1; + *(unsigned char*)( edi + 5 ) = w2; + *(unsigned char*)( edi + 6 ) = w3; + *(unsigned char*)( edi + 7 ) = w4; + + *(unsigned char*)( edi + nf_width + 4 ) = w1; + *(unsigned char*)( edi + nf_width + 5 ) = w2; + *(unsigned char*)( edi + nf_width + 6 ) = w3; + *(unsigned char*)( edi + nf_width + 7 ) = w4; + } +#endif + edi += nf_width*2; + } + else + { + if( hiColor ) + { +#ifdef OUTRAGE_BIG_ENDIAN + *(unsigned int *)(edi + 0) = (w4&0xFFFF)|((w3&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width) = (w4&0xFFFF)|((w3&0xFFFF)<<16); + + *(unsigned int *)(edi + 4) = (w2&0xFFFF)|((w1&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 4) = (w2&0xFFFF)|((w1&0xFFFF)<<16); +#else + *(unsigned int *)(edi + 0) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + + *(unsigned int *)(edi + 4) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 4) = (w3&0xFFFF)|((w4&0xFFFF)<<16); +#endif + } + else + { + const unsigned int value = IntelSwapper((unsigned int)( w4 << 24 ) | ( w3 << 16 ) | ( w2 << 8 ) | w1); + *(unsigned int*)( edi ) = value; + *(unsigned int*)( edi + nf_width ) = value; + } + } + } + + esi += (hiColor)?16:12; + edi -= nf_width*2; + edi += nf_width; + edi -= nfpk_back_right; + } + else if( donf41 ) + { + unsigned int colors[4]; + if( hiColor ) + { + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[0] = ((colors[0])<<16) | colors[0]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[1] = ((colors[1])<<16) | colors[1]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[2] = ((colors[2])<<16) | colors[2]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + colors[3] = ((colors[3])<<16) | colors[3]; + } + else + { + colors[0] = esi[0]; + colors[1] = esi[1]; + colors[2] = esi[2]; + colors[3] = esi[3]; + } + + const int max_repcount = 8; + const unsigned char* lookupTable = (hiColor) ? nfhpk_mov8 : nfpk_mov4; + for(int rep_count = 0; rep_count < max_repcount; ++rep_count ) + { + int idx = rep_count + ((hiColor)?8:4); + const unsigned char* color_idx = lookupTable + ( esi[idx] * 4 ); + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; +#ifdef OUTRAGE_BIG_ENDIAN + if( hiColor ) + { + *(unsigned int *)(edi + 0) = w4; + *(unsigned int *)(edi + 4) = w3; + *(unsigned int *)(edi + 8) = w2; + *(unsigned int *)(edi + 12) = w1; + } + else + { + *(unsigned char *)(edi + 0) = w4; + *(unsigned char *)(edi + 1) = w4; + *(unsigned char *)(edi + 2) = w3; + *(unsigned char *)(edi + 3) = w3; + *(unsigned char *)(edi + 4) = w2; + *(unsigned char *)(edi + 5) = w2; + *(unsigned char *)(edi + 6) = w1; + *(unsigned char *)(edi + 7) = w1; + } +#else + if( hiColor ) + { + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + } + else + { + *(unsigned char *)(edi + 0) = w1; + *(unsigned char *)(edi + 1) = w1; + *(unsigned char *)(edi + 2) = w2; + *(unsigned char *)(edi + 3) = w2; + *(unsigned char *)(edi + 4) = w3; + *(unsigned char *)(edi + 5) = w3; + *(unsigned char *)(edi + 6) = w4; + *(unsigned char *)(edi + 7) = w4; + } + +#endif + edi += nf_width; + } + + esi += (hiColor)?16:12; + edi -= nf_width; + edi -= nfpk_back_right; + } + else if( donf25 ) + { + unsigned int colors[4]; + if( hiColor ) + { + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[0] = ((colors[0])<<16) | colors[0]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[1] = ((colors[1])<<16) | colors[1]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[2] = ((colors[2])<<16) | colors[2]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + colors[3] = ((colors[3])<<16) | colors[3]; + } + else + { + colors[0] = esi[0]; + colors[1] = esi[1]; + colors[2] = esi[2]; + colors[3] = esi[3]; + } + + const unsigned char* lookupTable = (hiColor) ? nfhpk_mov4 : nfpk_mov4; + const int max_repcount = 4; + for(int rep_count = 0; rep_count < max_repcount; ++rep_count ) + { + int idx = rep_count + (hiColor?8:4); + const unsigned char* color_idx = lookupTable + (esi[idx]*4); + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; + + if( hiColor ) + { +#ifdef OUTRAGE_BIG_ENDIAN + *(unsigned int *)(edi + 0) = w4; + *(unsigned int *)(edi + nf_width) = w4; + *(unsigned int *)(edi + 4) = w3; + *(unsigned int *)(edi + nf_width + 4) = w3; + *(unsigned int *)(edi + 8) = w2; + *(unsigned int *)(edi + nf_width + 8) = w2; + *(unsigned int *)(edi + 12) = w1; + *(unsigned int *)(edi + nf_width + 12) = w1; +#else + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + nf_width) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + nf_width + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + nf_width + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + *(unsigned int *)(edi + nf_width + 12) = w4; +#endif + } + else + { + unsigned int c0 = IntelSwapper((unsigned int)( w2 << 24 ) | ( w2 << 16 ) | ( w1 << 8 ) | w1); + unsigned int c1 = IntelSwapper((unsigned int)( w4 << 24 ) | ( w4 << 16 ) | ( w3 << 8 ) | w3); + *(unsigned int *)(edi + 0) = c0; + *(unsigned int *)(edi + nf_width) = c0; + *(unsigned int *)(edi + 4) = c1; + *(unsigned int *)(edi + nf_width + 4) = c1; + } + + edi += nf_width*2; + } + + edi -= nf_width*2; + edi += nf_width; + esi += (hiColor)?12:8; + edi -= nfpk_back_right; + } + else + { + unsigned short colors[4]; + if( hiColor ) + { + colors[0] = Trans16( esi + 0 ); + colors[1] = Trans16( esi + 2 ); + colors[2] = Trans16( esi + 4 ); + colors[3] = Trans16( esi + 6 ); + } + else + { + colors[0] = esi[0]; + colors[1] = esi[1]; + colors[2] = esi[2]; + colors[3] = esi[3]; + } + + const unsigned char* lookupTable = (hiColor) ? nfhpk_mov4 : nfpk_mov4; + const int max_repcount = 16; + for(int rep_count = 0; rep_count < max_repcount; ++rep_count ) + { + int idx = rep_count + ((hiColor) ? 8 : 4); + const unsigned char* color_idx = lookupTable + (esi[idx]*4); + + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; + + if( rep_count & 1 ) + { + if( hiColor ) + { +#ifdef OUTRAGE_BIG_ENDIAN + *(unsigned int *)(edi + 8) = (w4&0xFFFF) | ((w3&0xFFFF)<<16); + *(unsigned int *)(edi + 12) = (w2&0xFFFF)| ((w1&0xFFFF)<<16); +#else + *(unsigned int *)(edi + 8) = (w1&0xFFFF) | ((w2&0xFFFF)<<16); + *(unsigned int *)(edi + 12) = (w3&0xFFFF)| ((w4&0xFFFF)<<16); +#endif + } + else + { + *(unsigned int*)(edi + 4) = IntelSwapper((unsigned int)( w4 << 24 ) | ( w3 << 16 ) | ( w2 << 8 ) | w1); + } + + edi += nf_width; + } + else + { + if( hiColor ) + { +#ifdef OUTRAGE_BIG_ENDIAN + *(unsigned int *)(edi + 0) = (w4&0xFFFF) | ((w3&0xFFFF)<<16); + *(unsigned int *)(edi + 4) = (w2&0xFFFF) | ((w1&0xFFFF)<<16); +#else + *(unsigned int *)(edi + 0) = (w1&0xFFFF) | ((w2&0xFFFF)<<16); + *(unsigned int *)(edi + 4) = (w3&0xFFFF) | ((w4&0xFFFF)<<16); +#endif + } + else + { + *(unsigned int*)(edi + 0) = IntelSwapper((unsigned int)( w4 << 24 ) | ( w3 << 16 ) | ( w2 << 8 ) | w1); + } + } + } + + edi -= nf_width; + esi += (hiColor)?24:20; + edi -= nfpk_back_right; + } + }break; + case 10: + { + // 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + bool do26 = false; + bool do42 = false; + + if( hiColor ) + { + int val1 = IntelSwapper(*(unsigned short *)esi); + if(val1&0x8000) + { + val1 = IntelSwapper(*(unsigned short *)(esi+16)); + if(val1&0x8000) + { + do42 = true; + } + else + { + do26 = true; + } + } + } + else + { + if( esi[0] > esi[1] ) + { + if( esi[12] > esi[13] ) + { + do42 = true; + } + else + { + do26 = true; + } + } + } + + // Load bx,dx,cx,bp with four colors + unsigned short colors[4]; + if( hiColor ) + { + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + } + else + { + colors[0] = esi[0]; + colors[1] = esi[1]; + colors[2] = esi[2]; + colors[3] = esi[3]; + } + + // build our iteration lookup table + int repLookup[16]; + if( do26 || do42 ) + { + int baseCnt1 = ( hiColor ) ? 8 :4; + int baseCnt2 = ( hiColor ) ? 24 : 16; + for( int i = 0; i < 8; ++i ) + { + repLookup[ i + 0 ] = baseCnt1 + i; + repLookup[ i + 8 ] = baseCnt2 + i; + } + } + else + { + int baseCnt1 = ( hiColor ) ? 8 : 4; + int baseCnt2 = ( hiColor ) ? 20 : 12; + int baseCnt3 = ( hiColor ) ? 32 : 20; + int baseCnt4 = ( hiColor ) ? 44 : 28; + for( int i = 0; i < 4; ++i ) + { + repLookup[ i + 0 ] = baseCnt1 + i; + repLookup[ i + 4 ] = baseCnt2 + i; + repLookup[ i + 8 ] = baseCnt3 + i; + repLookup[ i + 12 ] = baseCnt4 + i; + } + } + + const unsigned char* lookupTable = (hiColor) ? nfhpk_mov4 : nfpk_mov4; + for( int rep_count = 0; rep_count < 16; ++rep_count ) + { + int idx = repLookup[ rep_count ]; + const unsigned char* color_idx = lookupTable + ( esi[idx] * 4 ); + unsigned int w1 = colors[ color_idx[0] ]; + unsigned int w2 = colors[ color_idx[1] ]; + unsigned int w3 = colors[ color_idx[2] ]; + unsigned int w4 = colors[ color_idx[3] ]; + + if(!do42) + { + if( hiColor ) + { +#ifdef OUTRAGE_BIG_ENDIAN + *(unsigned int *)(edi+0) = ((unsigned int)(w4&0xFFFF)|((w3&0xFFFF)<<16)); + *(unsigned int *)(edi+4) = ((unsigned int)(w2&0xFFFF)|((w1&0xFFFF)<<16)); +#else + *(unsigned int *)(edi+0) = ((unsigned int)(w1&0xFFFF)|((w2&0xFFFF)<<16)); + *(unsigned int *)(edi+4) = ((unsigned int)(w3&0xFFFF)|((w4&0xFFFF)<<16)); +#endif + } + else + { + *(unsigned int *)(edi+0) = IntelSwapper((unsigned int)(w4 << 24) | (w3 << 16) | (w2 << 8) | w1); + } + edi += nf_width; + } + else + { + // 42 + if( rep_count & 1 ) + { + if( hiColor ) + { +#ifdef OUTRAGE_BIG_ENDIAN + *(unsigned int *)(edi+8) = ((unsigned int)(w4&0xFFFF)|((w3&0xFFFF)<<16)); + *(unsigned int *)(edi+12) = ((unsigned int)(w2&0xFFFF)|((w1&0xFFFF)<<16)); +#else + + *(unsigned int *)(edi+8) = ((unsigned int)(w1&0xFFFF)|((w2&0xFFFF)<<16)); + *(unsigned int *)(edi+12) = ((unsigned int)(w3&0xFFFF)|((w4&0xFFFF)<<16)); +#endif + } + else + { + *(unsigned int *)(edi+4) = IntelSwapper((unsigned int)(w4 << 24) | (w3 << 16) | (w2 << 8) | w1); + } + edi += nf_width; + } + else + { + if( hiColor ) + { +#ifdef OUTRAGE_BIG_ENDIAN + *(unsigned int *)(edi+0) = ((unsigned int)(w4&0xFFFF)|((w3&0xFFFF)<<16)); + *(unsigned int *)(edi+4) = ((unsigned int)(w2&0xFFFF)|((w1&0xFFFF)<<16)); +#else + *(unsigned int *)(edi+0) = ((unsigned int)(w1&0xFFFF)|((w2&0xFFFF)<<16)); + *(unsigned int *)(edi+4) = ((unsigned int)(w3&0xFFFF)|((w4&0xFFFF)<<16)); +#endif + } + else + { + *(unsigned int *)(edi+0) = IntelSwapper((unsigned int)(w4 << 24) | (w3 << 16) | (w2 << 8) | w1); + } + } + } + + if(rep_count==3 && !do42 && !do26) + { + if( hiColor ) + { + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+12)] | nf_trans16_hi[*(esi+13)]; + colors[1] = nf_trans16_lo[*(esi+14)] | nf_trans16_hi[*(esi+15)]; + colors[2] = nf_trans16_lo[*(esi+16)] | nf_trans16_hi[*(esi+17)]; + colors[3] = nf_trans16_lo[*(esi+18)] | nf_trans16_hi[*(esi+19)]; + } + else + { + colors[0] = esi[8]; + colors[1] = esi[9]; + colors[2] = esi[10]; + colors[3] = esi[11]; + } + } + + if(rep_count==7) + { + if(!do42 && !do26) + { + edi -= nf_width*8 - ((hiColor)?8:4); + + if( hiColor ) + { + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+24)] | nf_trans16_hi[*(esi+25)]; + colors[1] = nf_trans16_lo[*(esi+26)] | nf_trans16_hi[*(esi+27)]; + colors[2] = nf_trans16_lo[*(esi+28)] | nf_trans16_hi[*(esi+29)]; + colors[3] = nf_trans16_lo[*(esi+30)] | nf_trans16_hi[*(esi+31)]; + } + else + { + colors[0] = esi[16]; + colors[1] = esi[17]; + colors[2] = esi[18]; + colors[3] = esi[19]; + } + } + + if(do26 || do42) + { + if( do26 ) + { + edi -= nf_width*8 - ((hiColor)?8:4); + } + + if( hiColor ) + { + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+16)] | nf_trans16_hi[*(esi+17)]; + colors[1] = nf_trans16_lo[*(esi+18)] | nf_trans16_hi[*(esi+19)]; + colors[2] = nf_trans16_lo[*(esi+20)] | nf_trans16_hi[*(esi+21)]; + colors[3] = nf_trans16_lo[*(esi+22)] | nf_trans16_hi[*(esi+23)]; + } + else + { + colors[0] = esi[12]; + colors[1] = esi[13]; + colors[2] = esi[14]; + colors[3] = esi[15]; + } + } + } + + if(rep_count==11 && !do42 && !do26) + { + if( hiColor ) + { + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+36)] | nf_trans16_hi[*(esi+37)]; + colors[1] = nf_trans16_lo[*(esi+38)] | nf_trans16_hi[*(esi+39)]; + colors[2] = nf_trans16_lo[*(esi+40)] | nf_trans16_hi[*(esi+41)]; + colors[3] = nf_trans16_lo[*(esi+42)] | nf_trans16_hi[*(esi+43)]; + } + else + { + colors[0] = esi[24]; + colors[1] = esi[25]; + colors[2] = esi[26]; + colors[3] = esi[27]; + } + } + } + + if(!do42 && !do26) + { + esi += (hiColor)?48:32; + } + else + { + esi += (hiColor)?32:24; + } + + edi -= nf_width; + if(!do42) + { + edi -= ((hiColor)?8:4) + nfpk_back_right; + } + else + { + edi -= nfpk_back_right; + } + }break; + + case 11: + { + //8x8x16 (128 bytes) + if( hiColor ) + { + Trans16Blk(edi,esi); + edi += nf_width; + + Trans16Blk(edi,esi+16); + edi += nf_width; + + Trans16Blk(edi,esi+32); + edi += nf_width; + + Trans16Blk(edi,esi+48); + edi += nf_width; + + Trans16Blk(edi,esi+64); + edi += nf_width; + + Trans16Blk(edi,esi+80); + edi += nf_width; + + Trans16Blk(edi,esi+96); + edi += nf_width; + + Trans16Blk(edi,esi+112); + + esi += 128; + } + else + { + // 8x8x8 (64 bytes) + unsigned int* ediW = (unsigned int*)edi; + unsigned int* esiW = (unsigned int*)esi; + const unsigned int width = nf_width >> 2; + ediW[0] = (esiW[0]); + ediW[1] = (esiW[1]); + ediW += width; + ediW[0] = (esiW[2]); + ediW[1] = (esiW[3]); + ediW += width; + ediW[0] = (esiW[4]); + ediW[1] = (esiW[5]); + ediW += width; + ediW[0] = (esiW[6]); + ediW[1] = (esiW[7]); + ediW += width; + ediW[0] = (esiW[8]); + ediW[1] = (esiW[9]); + ediW += width; + ediW[0] = (esiW[10]); + ediW[1] = (esiW[11]); + ediW += width; + ediW[0] = (esiW[12]); + ediW[1] = (esiW[13]); + ediW += width; + ediW[0] = (esiW[14]); + ediW[1] = (esiW[15]); + esi += 64; + edi = ((unsigned char*)ediW); + } + edi -= nfpk_back_right; + }break; + case 12: + { + for(int i = 0; i < 4; ++i ) + { + if( hiColor ) + { + // low 4x4x16 (32 bytes) + unsigned int eax, ebx; + + eax = *(unsigned char *)(esi+i*8+0); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+1); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+0) = eax; + *(unsigned int *)(edi+nf_width) = eax; + + eax = *(unsigned char *)(esi+i*8+2); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+3); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+4) = eax; + *(unsigned int *)(edi+nf_width+4) = eax; + + eax = *(unsigned char *)(esi+i*8+4); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+5); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+8) = eax; + *(unsigned int *)(edi+nf_width+8) = eax; + + eax = *(unsigned char *)(esi+i*8+6); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+7); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+12) = eax; + *(unsigned int *)(edi+nf_width+12) = eax; + } + else + { + // low 4x4x8 (16 bytes) + unsigned int eax, ebx; + + eax = IntelSwapper(((unsigned int*)esi)[ i ]); + ebx = (( eax&0xFF00 )<<16) | (( eax&0xFF00 )<<8) | (( eax & 0xFF )<<8) | (eax & 0xFF); + ((unsigned int*)edi)[0] = ebx; + ((unsigned int*)(edi+nf_width))[0] = ebx; + + eax = eax >> 16; + ebx = (( eax&0xFF00 )<<16) | (( eax&0xFF00 )<<8) | (( eax & 0xFF )<<8) | (eax & 0xFF); + ((unsigned int*)edi)[1] = ebx; + ((unsigned int*)(edi+nf_width))[1] = ebx; + } + + edi += nf_width*2; + } + + edi -= nf_width*2; + edi += nf_width; + edi -= nfpk_back_right; + esi += (hiColor)?32:16; + }break; + case 13: + { + if( hiColor ) + { + // 2x2 4x4x0 (8 bytes) + for( int i = 0; i < 2; ++i ) + { + const unsigned char* loopEsi = esi + (i*4); + + unsigned int temp = nf_trans16_lo[loopEsi[0]] | nf_trans16_hi[loopEsi[1]]; + unsigned int ebx = ((temp&0xFFFF)<<16) | (temp&0xFFFF); + + temp = nf_trans16_lo[loopEsi[2]] | nf_trans16_hi[loopEsi[3]]; + unsigned int ecx = ((temp&0xFFFF)<<16) | (temp&0xFFFF); + + *(unsigned int *)(edi + 0) = ebx; + *(unsigned int *)(edi + 4) = ebx; + *(unsigned int *)(edi + 8) = ecx; + *(unsigned int *)(edi + 12) = ecx; + *(unsigned int *)(edi + nf_width) = ebx; + *(unsigned int *)(edi + nf_width+4) = ebx; + *(unsigned int *)(edi + nf_width+8) = ecx; + *(unsigned int *)(edi + nf_width+12) = ecx; + + edi += nf_width*2; + + *(unsigned int *)(edi + 0) = ebx; + *(unsigned int *)(edi + 4) = ebx; + *(unsigned int *)(edi + 8) = ecx; + *(unsigned int *)(edi + 12) = ecx; + *(unsigned int *)(edi + nf_width) = ebx; + *(unsigned int *)(edi + nf_width+4) = ebx; + *(unsigned int *)(edi + nf_width+8) = ecx; + *(unsigned int *)(edi + nf_width+12) = ecx; + + edi += nf_width*2; + } + } + else + { + // 2x2 4x4x0 (4 bytes) + for( int i = 0; i < 2; ++i ) + { + unsigned int c1 = esi[(i<<1) + 0]; + unsigned int eax = (c1 << 24) | (c1 << 16) | (c1 <<8 ) | c1; + + unsigned int c2 = esi[(i<<1) + 1]; + unsigned int ebx = (c2 << 24) | (c2 << 16) | (c2 << 8) | c2; + + *(unsigned int *)( edi + 0 ) = eax; + *(unsigned int *)( edi + 4 ) = ebx; + *(unsigned int *)( edi + 0 + nf_width ) = eax; + *(unsigned int *)( edi + 4 + nf_width ) = ebx; + edi += nf_width * 2; + + *(unsigned int *)( edi + 0 ) = eax; + *(unsigned int *)( edi + 4 ) = ebx; + *(unsigned int *)( edi + 0 + nf_width ) = eax; + *(unsigned int *)( edi + 4 + nf_width ) = ebx; + edi += nf_width * 2; + } + } + + edi -= nf_width * 2; + edi += nf_width; + edi -= nfpk_back_right; + esi += (hiColor)?8:4; + }break; + case 14: + { + if( hiColor ) + { + // 8x8x0 (2 bytes) + unsigned int ecx = nf_trans16_lo[esi[0]] | nf_trans16_hi[esi[1]]; + unsigned int ebx = ((ecx&0xFFFF)<<16) | (ecx&0xFFFF); + esi += 2; + + for( int i = 0; i < 8; ++i ) + { + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + } + + edi -= nf_width; + edi -= nfpk_back_right; + } + else + { + // 8x8x0 (1 byte) + unsigned int c = *esi++; // Copy color into 8 positions + unsigned int col = ( c << 24 ) | ( c << 16 ) | ( c << 8 ) | c; + + for( int i = 0; i < 8; ++i ) + { + *(unsigned int*)( edi + 0 ) = col; + *(unsigned int*)( edi + 4 ) = col; + edi += nf_width; + } + edi -= nf_width; + edi -= nfpk_back_right; + } + }break; + case 15: + { + if( !hiColor ) + { + // mix 8x8x0 (2 bytes) + unsigned int ebx = IntelSwapper(*(unsigned short*)( esi )); // Copy 2 colors into 8 positions + esi += 2; // in a checkerboard + unsigned int eax = ( ebx << 16 ) | ebx; + ebx = ( eax << 8 ) | (( eax >> 24 ) & 0xFF); + + for( int i = 0; i < 4; ++i ) + { + *(unsigned int*)( edi + 0 ) = eax; + *(unsigned int*)( edi + 4 ) = eax; + edi += nf_width; + + *(unsigned int*)( edi + 0 ) = ebx; + *(unsigned int*)( edi + 4 ) = ebx; + edi += nf_width; + } + edi -= nf_width; + edi -= nfpk_back_right; + } + }break; + } + + if( first_opcode && !skipNextOpCode ) + { + first_opcode = false; + opcode_to_use = (opcode>>4); + goto do_next_opcode; + } + }while( h != 0 ); +} + +void Trans16Blk(unsigned char *edi,const unsigned char *idx) +{ + *((unsigned short *)(edi+0)) = nf_trans16_lo[*(idx+0)] | nf_trans16_hi[*(idx+1)]; + *((unsigned short *)(edi+2)) = nf_trans16_lo[*(idx+2)] | nf_trans16_hi[*(idx+3)]; + *((unsigned short *)(edi+4)) = nf_trans16_lo[*(idx+4)] | nf_trans16_hi[*(idx+5)]; + *((unsigned short *)(edi+6)) = nf_trans16_lo[*(idx+6)] | nf_trans16_hi[*(idx+7)]; + *((unsigned short *)(edi+8)) = nf_trans16_lo[*(idx+8)] | nf_trans16_hi[*(idx+9)]; + *((unsigned short *)(edi+10)) = nf_trans16_lo[*(idx+10)] | nf_trans16_hi[*(idx+11)]; + *((unsigned short *)(edi+12)) = nf_trans16_lo[*(idx+12)] | nf_trans16_hi[*(idx+13)]; + *((unsigned short *)(edi+14)) = nf_trans16_lo[*(idx+14)] | nf_trans16_hi[*(idx+15)]; +} + + +void DOnf_xycshift( const bool hiColor, const unsigned int eax, unsigned char* &edi, const int nfpk_back_right ) +{ + unsigned int ebx = (eax >> 8) & 0xFF; + + // get the lower byte of the offset, but sign extend it + int offset = eax & 0xFF; + if( eax & 0x80 ) + { + // extend... + offset |= 0xFFFFFF00; + } + + // hiColor is multiplied by two + if( hiColor ) + { + offset <<= 1; + } + + // factor in the table + offset += nfpk_ShiftY[ ebx ]; + DOnf_shift( hiColor, offset, edi, nfpk_back_right ); +} + +void DOnf_xypshift(const bool hiColor, const unsigned int eax, unsigned char * &edi, const int nfpk_back_right, const int DiffBufPtrs ) +{ + unsigned int ebx = (eax >> 8) & 0xFF; + + // get the lower byte of the offset, but sign extend it + int offset = eax & 0xFF; + if( eax & 0x80 ) + { + // extend... + offset |= 0xFFFFFF00; + } + + // hiColor is multiplied by two + if( hiColor ) + { + offset <<= 1; + } + + // factor in the table + offset += nfpk_ShiftY[ ebx ] + DiffBufPtrs; + DOnf_shift( hiColor, offset, edi, nfpk_back_right ); +} + +// Copy the 128/64 bytes from an offset of edi to edi is. +void DOnf_shift( const bool hiColor, const int offset, unsigned char * &edi, const int nfpk_back_right ) +{ + union ptr + { + unsigned int* pAsInt; + unsigned char* pAsChar; + }; + ptr dstBuffer; dstBuffer.pAsChar = edi; + ptr srcBuffer; srcBuffer.pAsChar = edi + offset; + + for( int i = 0; i < 8; ++i ) + { + dstBuffer.pAsInt[ 0 ] = (srcBuffer.pAsInt[ 0 ]); + dstBuffer.pAsInt[ 1 ] = (srcBuffer.pAsInt[ 1 ]); + + if( hiColor ) + { + dstBuffer.pAsInt[ 2 ] = (srcBuffer.pAsInt[ 2 ]); + dstBuffer.pAsInt[ 3 ] = (srcBuffer.pAsInt[ 3 ]); + } + + dstBuffer.pAsChar += nf_width; + srcBuffer.pAsChar += nf_width; + } + + // restore edi + dstBuffer.pAsChar -= nf_width; + dstBuffer.pAsChar -= nfpk_back_right; + edi = dstBuffer.pAsChar; +} + +//////////////////////////////////////////////// +// Non-Implemented Functions +//////////////////////////////////////////////// +void nfHiColorDecomp(const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h); +void nfHiColorDecompChg(const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h); +void nfDecomp(const unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfDecompChg(const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h); +void nfPkDecompH(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void nfPkDecompD(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field); +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field); +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty); +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty); +void mve_sfPkShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty); +void mve_sfPkHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty); + +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); +void palLoadCompPalette(unsigned char *buf); +void gfxMode(unsigned mode); +void gfxLoadCrtc(unsigned char *crtc,unsigned char chain4,unsigned char res); +void gfxGetCrtc(unsigned char *crtc); +void gfxVres(unsigned char misc,unsigned char *crtc); +void MVE_gfxWaitRetrace(int state); +void MVE_gfxSetSplit(unsigned line); + + +#if defined(__LINUX__) +#ifndef MACOSXPPC +#define int3 __asm__ __volatile__ ( "int $3" ); +#else +#define int3 +#endif +#else +#define int3 __asm{ int 3 }; +#endif + +void DECOMP_BODY( bool HI_COLOR_FLAG, const unsigned char* &comp, unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h ) +{ + unsigned int HI_COLOR_SCALE = ( HI_COLOR_FLAG ) ? 2 : 1; + + tNextFrame nf; + nf.w = _w; + nf.h = _h; + nf.x = _x; + nf.y = _y; + NF_DECOMP_INIT( HI_COLOR_FLAG ? 1 : 0, &nf ); + + unsigned int parms_sz = ( nf.w * nf.h * nf_fqty ) << 1; + const unsigned char* compData = comp + parms_sz; + + // New Data + //===================== + + // Iterate over params and copy new hires data to appropriate sections. + unsigned char* curr_tbuf = nf.tbuf; + const unsigned short* compAsWord = (const unsigned short*)comp; + for( unsigned char fqIt = nf_fqty; fqIt != 0; --fqIt, curr_tbuf += nf_width ) + { + unsigned char* this_tbuf = curr_tbuf; + for( unsigned int ch = nf.h; ch != 0; --ch, this_tbuf += nf.new_row ) + { + for( unsigned int cl = nf.w; cl != 0; --cl ) + { + int flags = (int)IntelSwapper( *compAsWord++ ); + if( flags != 0 ) + { + this_tbuf += SWIDTH * HI_COLOR_SCALE; + continue; + } + + // Copy new data to one section + // Enter with esi(compData) pointing to source data, edi(this_tbuf) to screen section. + // Assumes SWIDTH=8 (16-bit data) and SHEIGHT=8 + for( int currH = 0; currH < 8; ++currH ) + { + size_t amt = sizeof(unsigned int) * 2 * HI_COLOR_SCALE; + //memcpy( this_tbuf, compData, amt ); + for(unsigned int ii=0;ii<(amt/2);ii++) + { + unsigned short *destword = (unsigned short *)this_tbuf[ii]; + unsigned short *srcword = (unsigned short *)compData[ii]; + *destword = IntelSwapper(*srcword); + } + compData += amt; + this_tbuf += amt + nf_new_line; + } + this_tbuf -= nf_new_line; + this_tbuf -= nf_back_right; + } + } + } + + // Motion Compensation + //===================== + + // Iterate over flags and motion source addresses from params + // to determine which sections to move. + // ebx indexes params. + // esi indexes source from buffer + // esi will be computed as +- 16K relative to edi. + compAsWord = (const unsigned short*)comp; + curr_tbuf = nf.tbuf; + for( unsigned char fqIt = nf_fqty; fqIt != 0; --fqIt, curr_tbuf += nf_width ) + { + unsigned char* this_tbuf = curr_tbuf; + for( unsigned int ch = nf.h; ch != 0; --ch, this_tbuf += nf.new_row ) + { + for( unsigned int cl = nf.w; cl != 0; --cl ) + { + int flags = (int)IntelSwapper(*compAsWord++); + if( flags == 0 ) + { + this_tbuf += SWIDTH * HI_COLOR_SCALE; + continue; + } + + // Make esi absolute + unsigned char* src = this_tbuf; + if( flags > 0 ) + { + // jg + src += (flags - 0x4000) * HI_COLOR_SCALE; + } + else + { + // jl + src += (flags - 0xC000) * HI_COLOR_SCALE + nf.DiffBufPtrs; + } + + for( int i = 0; i < 8; ++i ) + { + size_t amt = sizeof(unsigned int) * 2 * HI_COLOR_SCALE; + //memcpy( this_tbuf, src, amt ); + for(unsigned int ii=0;ii<(amt/2);ii++) + { + unsigned short *destword = (unsigned short *)this_tbuf[ii]; + unsigned short *srcword = (unsigned short *)src[ii]; + *destword = IntelSwapper(*srcword); + } + src += amt + nf_new_line; + this_tbuf += amt + nf_new_line; + } + + this_tbuf -= nf_new_line; + this_tbuf -= nf_back_right; + } + } + } +} + +void DECOMP_CHG_BODY(bool HI_COLOR_FLAG, + const unsigned short* &chgs, const unsigned short* &parms, + const unsigned char* &comp, unsigned int _x, unsigned int _y, unsigned int _w, unsigned int _h ) +{ + unsigned int HI_COLOR_SCALE = ( HI_COLOR_FLAG ) ? 2 : 1; + + tNextFrame nf; + nf.w = _w; + nf.h = _h; + nf.x = _x; + nf.y = _y; + NF_DECOMP_INIT( HI_COLOR_FLAG ? 1 : 0, &nf ); + + // Iterate over params and copy new hires data to appropriate sections. + const unsigned short* pChgs = chgs; + unsigned int eax = 0; + const unsigned char* compData = comp; + unsigned char* curr_tbuf = nf.tbuf; + const unsigned short* curr_parms = parms; + for( unsigned char fqIt = nf_fqty; fqIt != 0; --fqIt, curr_tbuf += nf_width ) + { + unsigned char* this_tbuf = curr_tbuf; + for( unsigned int ch = nf.h; ch != 0; --ch, this_tbuf += nf.new_row ) + { + for( unsigned int cl = nf.w; cl != 0; --cl ) + { + eax *= 2; + while( eax == 0 ) + { + eax = IntelSwapper(*pChgs++); + eax *= 2; + } + + if( ((int)eax) > 0 ) + { + this_tbuf += SWIDTH * HI_COLOR_SCALE; + continue; + } + + unsigned short flags = IntelSwapper(*curr_parms++); + if( flags != 0 ) + { + this_tbuf += SWIDTH * HI_COLOR_SCALE; + continue; + } + + // Copy new data to one section + // Enter with ds:si pointing to source data, es:di to screen section. + // Assumes SWIDTH=8 (16-bit data) and SHEIGHT=8 + for( int i = 0; i < 8; ++i ) + { + size_t amt = sizeof(unsigned int) * 2 * HI_COLOR_SCALE; + // TODO: Do these bytes need swapping? Is this data shorts? + //memcpy( this_tbuf, compData, amt ); + for(unsigned int ii=0;ii<(amt/2);ii++) + { + unsigned short *dest = (unsigned short *)this_tbuf[ii]; + unsigned short *src = (unsigned short *)compData[ii]; + *dest = IntelSwapper(*src); + } + compData += amt; + this_tbuf += amt + nf_new_line; + } + this_tbuf -= nf_new_line; + this_tbuf -= nf_back_right; + } + } + } + + // Iterate over flags and motion source addresses from params + // to determine which sections to move. + // ebx indexes params. + // esi indexes source from buffer + // esi will be computed as +- 16K relative to edi. + curr_tbuf = nf.tbuf; + curr_parms = parms; + pChgs = chgs; + eax = 0; + + for( unsigned char fqIt = nf_fqty; fqIt != 0; --fqIt, curr_tbuf += nf_width ) + { + unsigned char* this_tbuf = curr_tbuf; + for( unsigned int ch = nf.h; ch != 0; --ch, this_tbuf += nf.new_row ) + { + for( unsigned int cl = nf.w; cl != 0; --cl ) + { + eax *= 2; + while( eax == 0 ) + { + eax = IntelSwapper(*pChgs++); + eax *= 2; + } + + if( ((int)eax) > 0 ) + { + this_tbuf += SWIDTH * HI_COLOR_SCALE; + continue; + } + + int flags = (int)IntelSwapper((*curr_parms++)); + if( flags == 0 ) + { + this_tbuf += SWIDTH * HI_COLOR_SCALE; + continue; + } + + // Make esi absolute + unsigned char* src = this_tbuf; + if( flags > 0 ) + { + src += (flags - 0x4000) * HI_COLOR_SCALE; + } + else + { + src += (flags - 0xC000) * HI_COLOR_SCALE; + src += nf.DiffBufPtrs; // and point to other buffer + } + + for( int i = 0; i < 8; ++i ) + { + size_t amt = sizeof(unsigned int) * 2 * HI_COLOR_SCALE; + //memcpy( this_tbuf, src, amt ); + for(unsigned int ii=0;ii<(amt/2);ii++) + { + unsigned short *destword = (unsigned short *)this_tbuf[ii]; + unsigned short *srcword = (unsigned short *)src[ii]; + *destword = IntelSwapper(*srcword); + } + src += amt + nf_new_line; + this_tbuf += amt + nf_new_line; + } + this_tbuf -= nf_new_line; + this_tbuf -= nf_back_right; // (SHEIGHT-1)*width + } + } + } +} + +// Decompress into subsection of current buffer specified by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +void nfHiColorDecomp(const unsigned char *comp, unsigned int x, unsigned int y, unsigned int w, unsigned int h) +{ + DECOMP_BODY( true, comp, x, y, w, h ); +} + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// Chgs specifies which squares to update. +// Parms are motion parms for squares to update. +void nfHiColorDecompChg( const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h ) +{ + DECOMP_CHG_BODY( true, chgs, parms, comp, x, y, w, h ); +} + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +void nfDecomp(const unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + if( nf_hicolor ) + { + nfHiColorDecomp( comp, x, y, w, h ); + return; + } + + DECOMP_BODY( false, comp, x, y, w, h ); +} + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// Chgs specifies which squares to update. +// Parms are motion parms for squares to update. +void nfDecompChg(const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h ) +{ + if( nf_hicolor ) + { + nfHiColorDecompChg( chgs, parms, comp, x, y, w, h ); + return; + } + + DECOMP_CHG_BODY( false, chgs, parms, comp, x, y, w, h ); +} + + + +void nfPkDecompH(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3 +} +void nfPkDecompD(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3 +} +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field) +{ + int3 +} +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field) +{ + int3 +} +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty) +{ + int3 +} +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty) +{ + int3 +} +void mve_sfPkShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty) +{ + int3 +} +void mve_sfPkHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty) +{ + int3 +} +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count) +{ + int3 +} +void palLoadCompPalette(unsigned char *buf) +{ + int3 +} +void gfxMode(unsigned mode) +{ + int3 +} +void gfxLoadCrtc(unsigned char *crtc,unsigned char chain4,unsigned char res) +{ + int3 +} +void gfxGetCrtc(unsigned char *crtc) +{ + int3 +} +void gfxVres(unsigned char misc,unsigned char *crtc) +{ + int3 +} +void MVE_gfxWaitRetrace(int state) +{ + int3 +} +void MVE_gfxSetSplit(unsigned line) +{ + int3 +} diff --git a/libmve/mvegfx.h b/libmve/mvegfx.h new file mode 100644 index 000000000..6892d644e --- /dev/null +++ b/libmve/mvegfx.h @@ -0,0 +1,31 @@ +#define MVE_GFX_VESA_640_400 0x100 +#define MVE_GFX_VESA_640_200 0x8100 +#define MVE_GFX_VESA_640_480 0x101 +#define MVE_GFX_VESA_640_240 0x8101 +#define MVE_GFX_VESA_800_600 0x103 +#define MVE_GFX_VESA_800_300 0x8103 +#define MVE_GFX_VESA_1024_768 0x105 +#define MVE_GFX_VESA_1024_384 0x8105 +#define MVE_GFX_VESA_1280_1024 0x107 +#define MVE_GFX_VESA_1280_512 0x8107 + +#define MVE_GFX_VESA_320_200_HI 0x10d /* 1:5:5:5 Hi Color modes */ +#define MVE_GFX_VESA_640_480_HI 0x110 +#define MVE_GFX_VESA_800_600_HI 0x113 +#define MVE_GFX_VESA_1024_768_HI 0x116 +#define MVE_GFX_VESA_1280_1024_HI 0x119 + +/* Some hacked vesa modes */ +#define MVE_GFX_VESA_640_350 0x0F00 /* 640x350 */ +#define MVE_GFX_VESA_640_175 0x8F00 /* 640x175 */ +#define MVE_GFX_VESA_640_160 0x0F01 /* 640x160 */ + +#define MVE_GFX_VESA_CURRENT 0 /* Use current VESA mode */ + +#define MVE_GFX_AUTO (-1) /* Choose mode based on movie's preference */ +#define MVE_GFX_VGA 0x13 /* 320x200 */ +#define MVE_GFX_VGA_CURRENT (-2) /* 320x200 (mode already set) */ +#define MVE_GFX_VGA_MEDRES (-3) /* 248x264 out of 320x350 */ +#define MVE_GFX_VGA_HIRES (-4) /* 224x288 out of 360x480 */ +#define MVE_GFX_VGA_LORES (-5) /* 288x224 out of 320x240 */ +#define MVE_GFX_VESA_320_480 (-6)/* 320x480 */ diff --git a/libmve/mvelibi.h b/libmve/mvelibi.h new file mode 100644 index 000000000..f25e44546 --- /dev/null +++ b/libmve/mvelibi.h @@ -0,0 +1,333 @@ +#ifndef MVELIBI_H__ +#define MVELIBI_H__ + +#if __SC__ +# pragma SC align 1 +#elif __WATCOMC__ +# pragma pack(1); +#elif _MSC_VER +# pragma pack(1) +#elif __LINUX__ +# pragma pack(1) +#else +# error No platform defined +#endif + +#include "BYTESWAP.H" +//-------------------------------- +// Compressed Video Constants +//-------------------------------- + +// Width and height of sections in pixels. +#define SWIDTH 8 +#define SHEIGHT 8 + +#define LOG2_SWIDTH 3 +#define LOG2_SHEIGHT 3 + +//------------------------------ +// Movie File Header +//------------------------------ + +#define MVE_FILE_TYPE "Interplay MVE File\x1A\0" +#define MVE_FILE_VERSION 0x0100 + +// some inlines to prevent macro craziness when using incrementers and dereferencing, and so I can use operator overloading +inline unsigned short IntelSwapper(unsigned short a) +{ + return INTEL_SHORT(a); +} + +inline short IntelSwapper(short a) +{ + return INTEL_SHORT(a); +} + +inline unsigned int IntelSwapper(unsigned int a) +{ + return INTEL_INT(a); +} + +inline int IntelSwapper(int a) +{ + return INTEL_INT(a); +} + +inline unsigned long IntelSwapper(unsigned long a) +{ + return INTEL_INT(a); +} + +inline long IntelSwapper(long a) +{ + return INTEL_INT(a); +} + +typedef struct _mve_hdr +{ + char FileType[20]; // MVE_FILE_TYPE + unsigned short HdrSize; // sizeof(mve_hdr) + unsigned short version; // MVE_FILE_VERSION + unsigned short id; // ~MVE_FILE_VERSION+0x1234 + void SwapBytes() + { + HdrSize = IntelSwapper(HdrSize); + version = IntelSwapper(version); + id = IntelSwapper(id); + } +} mve_hdr; + +//------------------------------ +// Movie File Records +//------------------------------ + +typedef struct _io_hdr +{ + unsigned short len; // Length of record data (pad to even) + unsigned short kind; // See IO_REC_xxx +// unsigned char data[0]; // Record data + void SwapBytes() + { + len = IntelSwapper(len); + kind = IntelSwapper(kind); + } +} ioHdrRec; + +// This record classifications simplify utilities which must operate on +// records. They are not used by this library when running a movie. +// +#define IO_REC_SND_INIT 0 // Sound setup +#define IO_REC_SND_PRELOAD 1 // Sound preload +#define IO_REC_FRAME_INIT 2 // Frame (video) setup +#define IO_REC_FRAME 3 // Movie frames +#define IO_REC_END 4 // Last Record (End of Movie) +#define IO_REC_EOF 5 // Empty eof record at end of file. + +//------------------------------ +// Movie File Major Opcodes +//------------------------------ +// + +#define MCMD_DATA(arg) ( (unsigned char *) ((arg)+1) ) + +typedef struct _mcmd_hdr +{ + unsigned short len; // Length of data (pad to even) + unsigned char major; // Major opcode + unsigned char minor; // Minor opcode +// unsigned char data[0]; // Opcode data + void SwapBytes() + { + len = IntelSwapper(len); + } +} mcmd_hdr; + +#define mcmd_end 0 // End processing of movie + +#define mcmd_next 1 // Advance to next movie record + +#define mcmd_syncInit 2 +typedef struct _syncInit +{ + unsigned long period; // period of quanta + unsigned short wait_quanta; // # of quanta per frame + void SwapBytes() + { + period = IntelSwapper(period); + wait_quanta = IntelSwapper(wait_quanta); + } +} marg_syncInit; + +#define mcmd_sndConfigure 3 +typedef struct _sndConfigure +{ + unsigned short rate; // 65536-(256E6/(frequency*(stereo+1))) + // comp16 is a minor opcode 1 field + // It indicates that 16-bit data has been compressed to 8-bits. + // When it is set, bits16 will also be set. + // Each record will contain initial 16-bit sample followed + // by remaining compressed 8-bit samples. + // For stereo, there will be two initial 16-bit samples. + // and compressed streams will be interleaved. +#ifdef OUTRAGE_BIG_ENDIAN + unsigned char pad:5; + unsigned char stereo:1, bits16:1, comp16:1; + unsigned char pad2; +#else + unsigned char stereo:1, bits16:1, comp16:1; + unsigned char pad; +#endif + unsigned short frequency; + // Minor opcode 1 extends buflen to be a long + unsigned long buflen; + void SwapBytes() + { + rate = IntelSwapper(rate); + frequency = IntelSwapper(frequency); + buflen = IntelSwapper(buflen); + } +} marg_sndConfigure; + +#define mcmd_sndSync 4 + +#define mcmd_nfConfig 5 +typedef struct _nfConfig +{ + unsigned short wqty; + unsigned short hqty; + // Minor opcode 1 fields: + unsigned short fqty; + // Minor opcode 2 fields: + unsigned short hicolor; /*0=256-color, 1=HiColor, 2=HiColorSwapped*/ + void SwapBytes() + { + wqty = IntelSwapper(wqty); + hqty = IntelSwapper(hqty); + fqty = IntelSwapper(fqty); + hicolor = IntelSwapper(hicolor); + } +} marg_nfConfig; + +#define mcmd_nfDecomp 6 +#define mcmd_nfDecompChg 16 +#define mcmd_nfPkDecomp 17 +typedef struct _nfDecomp +{ + unsigned short prev; // info:Prev frames+1 needed for full picture + unsigned short iframe; // info:Current internal frame # + unsigned short x; + unsigned short y; + unsigned short w; + unsigned short h; +#ifdef OUTRAGE_BIG_ENDIAN + unsigned char bitpadder:7; + unsigned char advance:1; + unsigned char dummy1; +#else + unsigned short advance:1; + unsigned short pad:15; +#endif + + void SwapBytes() + { + prev = IntelSwapper(prev); + iframe = IntelSwapper(iframe); + x = IntelSwapper(x); + y = IntelSwapper(y); + w = IntelSwapper(w); + h = IntelSwapper(h); + } +} marg_nfDecomp; + +#define mcmd_sfShowFrame 7 +#if 0 // Not supported +#define mcmd_sfPkShowFrameChg 18 +#endif +typedef struct _sfShowFrame +{ + unsigned short pal_start; + unsigned short pal_count; + // Minor opcode 1 fields: + unsigned short field; // 0:none, 2:send to even, 3:send to odd + void SwapBytes() + { + pal_start = IntelSwapper(pal_start); + pal_count = IntelSwapper(pal_count); + field = IntelSwapper(field); + } +} marg_sfShowFrame; + +#define mcmd_sndAdd 8 +#define mcmd_sndSilence 9 +typedef struct _sndAdd +{ + unsigned short iframe; //info: iframe # of sound + unsigned short TrackMask; + unsigned short qty; //Uncompressed audio size in bytes +// unsigned char data[0]; + void SwapBytes() + { + iframe = IntelSwapper(iframe); + TrackMask = IntelSwapper(TrackMask); + qty = IntelSwapper(qty); + } +} marg_sndAdd; + +#define mcmd_gfxMode 10 +typedef struct _gfxMode +{ + unsigned short minw; + unsigned short minh; + unsigned short mode; + void SwapBytes() + { + minw = IntelSwapper(minw); + minh = IntelSwapper(minh); + mode = IntelSwapper(mode); + } +} marg_gfxMode; + +#define mcmd_palMakeSynthPalette 11 +typedef struct _palMakeSynthPalette +{ + unsigned char base_r; + unsigned char range_r; + unsigned char range_rb; + unsigned char base_g; + unsigned char range_g; + unsigned char range_gb; + void SwapBytes() + { + + } +} marg_palMakeSynthPalette; + +#define mcmd_palLoadPalette 12 +typedef struct _palLoadPalette +{ + unsigned short start; + unsigned short count; +// unsigned char data[0]; + void SwapBytes() + { + start = IntelSwapper(start); + count = IntelSwapper(count); + } +} marg_palLoadPalette; + +#define mcmd_palLoadCompPalette 13 + +#define mcmd_nfChanges 14 +#define mcmd_nfParms 15 +// 16 is used for mcmd_nfDecompChg, see above. +// 17 is used for mcmd_nfPkDecomp, see above. +// 18 is used for mcmd_nfPkShowFrameChg, see above + +#define mcmd_nfPkInfo 19 +#define mcmd_nfHPkInfo 20 +typedef struct _nfPkInfo +{ + unsigned long error; // scaled by 10000 + unsigned short usage[64]; +} marg_nfPkInfo; + +#define mcmd_idcode 21 +typedef struct _idcode +{ + unsigned long idcode; // Code identifying version mcomp used to create +} marg_idcode; + +#if __SC__ +# pragma SC align +#elif __WATCOMC__ +# pragma pack(); +#elif _MSC_VER +# pragma pack() +#elif __LINUX__ +# pragma pack() +#else +# error No platform defined +#endif + + +#endif diff --git a/libmve/mvelibl.cpp b/libmve/mvelibl.cpp new file mode 100644 index 000000000..c2ffac7ed --- /dev/null +++ b/libmve/mvelibl.cpp @@ -0,0 +1,1994 @@ +/* +** mvelibl.cpp +** +** Interplay Movie File (MVE) Player Library (32-Bit Linux Version) +** Written by Paul Allen Edelstein. Partial Linux port by Jeff Slutter. +** +** (c) 1997 Interplay Productions. All Rights Reserved. +** This file is confidential and consists of proprietary information +** of Interplay Productions. This file and associated libraries +** may not, in whole or in part, be disclosed to third parties, +** incorporated into any software product which is not being created +** for Interplay Productions, copied or duplicated in any form, +** without the prior written permission of Interplay Productions. +** Further, you may not reverse engineer, decompile or otherwise +** attempt to derive source code of this material. +** +*/ + +/* +** Linux Specific Notes: +** This library is dependant off of: +** -POSIX Threads (-lpthread) +** -X11 Library (-lX11) +** -Xext Library (-Xext) +** +** Only HiColor mode movies are supported. Others could +** be supported, but the nf*, etc. functions need to be +** implemented to do so. See mveasm.cpp. +** +** No gfx or resolution changing functions are implemented in +** here. See the xf86vm X11 extension on how to do this. +*/ +static char notice1[] = +"(c) 1997 Interplay Productions. All Rights Reserved.\n" +"This file is confidential and consists of proprietary information\n" +"of Interplay Productions. This file and associated libraries\n" +"may not, in whole or in part, be disclosed to third parties,\n" +"incorporated into any software product which is not being created\n" +"for Interplay Productions, copied or duplicated in any form,\n" +"without the prior written permission of Interplay Productions.\n" +"Further, you may not reverse engineer, decompile or otherwise\n" +"attempt to derive source code of this material.\n"; + +#include +#include +#include + +#include "platform.h" +#include "mvelibi.h" +#include "mvegfx.h" +#include "mvelibl.h" + +static unsigned opt_fastmode = 0; // 0:normal, 1:even lines only, 2:dither between even/odd lines |4 to spread lines + +unsigned opt_hscale_step = 4; // 3: 4/3, 4:4/4, 5:4/5 (not fully implemented) +unsigned opt_hscale_adj; +extern unsigned sf_ScreenHeight; +extern unsigned sf_ScreenWidth; + +#define logLabel(x) + +#include "snd8to16.h" +// len always specifies length of destination in bytes. +unsigned sndDecompM16(unsigned short *dst, unsigned char *src, unsigned len, unsigned state); +unsigned sndDecompS16(unsigned short *dst, unsigned char *src, unsigned len, unsigned state); + +//---------------------------------------------------------------------- +// Memory Management +//-------------------- + +static void *(*mem_alloc)(unsigned size); +static void (*mem_free)(void *p); + +typedef struct _mem +{ + void *ptr; + unsigned size; + bool dynamic; +} MemRec, *MemPtr; + +static void MemInit(MemPtr m, unsigned size, void *p); +static void *MemAlloc(MemPtr m, unsigned size); +static void MemFree(MemPtr m); + +void MVE_memCallbacks(void *(*fn_alloc)(unsigned size),void (*fn_free)(void *p)) +{ + mem_alloc = fn_alloc; + mem_free = fn_free; +} + +static void MemFree(MemPtr m) +{ + if (m->dynamic && mem_free) + { + (*mem_free)( m->ptr ); + m->dynamic = false; // prevent from being freed again! + } + m->size = 0; +} + +static void MemInit(MemPtr m, unsigned size, void *p) +{ + if (!p) + return; + + MemFree(m); + m->ptr = p; + m->size = size; + m->dynamic = false; +} + +static void *MemAlloc(MemPtr m, unsigned size) +{ + if (size <= m->size) + return m->ptr; + + if (mem_alloc) + { + void *p; + MemFree(m); + size += 100; // Add some pad to reduce chance of another realloc. + p = (*mem_alloc)(size); + if (!p) + return (void *)NULL; + MemInit(m, size, p); + m->dynamic = true; + return m->ptr; + } + + return (void *)NULL; +} + +//---------------------------------------------------------------------- +// Synchronization Management +//----------------------------- + +static bool sync_active = false; +static int sync_time = 0; +static int sync_wait_quanta = 0; + +static bool sync_late = false; +static bool sync_FrameDropped = false; + +static void syncReset(unsigned long wait_quanta); +static void syncRelease(void); +static bool syncInit(unsigned long period, unsigned wait_quanta); +static bool syncWait(void); +static void syncSync(void); + +static void syncReset(unsigned long wait_quanta) +{ + sync_time = wait_quanta - platform_timeGetTime() * 1000; + sync_active = true; +} + +static void syncRelease(void) +{ + sync_active = false; +} + +static bool syncInit(unsigned long period, unsigned wait_quanta) +{ + int new_wait_quanta = -(long)( period * wait_quanta + (wait_quanta>>1) ); + + // If timer is still running and has same timing + // characteristics, assume we are trying to continue smoothly + // with another movie and ignore new syncInit() call. + if (sync_active && sync_wait_quanta==new_wait_quanta) + return true; + + syncWait(); + sync_wait_quanta = new_wait_quanta; + syncReset(sync_wait_quanta); + return true; +} + +// Returns true if actually waited, false if called too late to wait. +static bool syncWait(void) +{ + bool waited = false; + if (!sync_active) + return false; + + while( (int)( sync_time + platform_timeGetTime() * 1000 ) < 0 ) + { + waited = true; + } + + sync_time += sync_wait_quanta; + return waited; +} + +// Returns true if actually waited, false if called too late to wait. +static int syncWaitLevel(int level) +{ + int waited; + if (!sync_active) + return 0; + + level += sync_time; + + for (;;) + { + waited = level + platform_timeGetTime() * 1000; + if (waited >= 0) + break; + } + + sync_time += sync_wait_quanta; + return waited; +} + +static void syncSync(void) +{ + if (sync_active) + { + while( (int)( sync_time + platform_timeGetTime() * 1000 ) < 0 ); + } +} + +int MVE_syncTime(void) +{ + if (sync_active) + return sync_time + platform_timeGetTime() * 1000; + return 0; +} + +void MVE_logDumpStats(void) +{ +} + +//---------------------------------------------------------------------- +// I/O Management +//----------------- + +static unsigned (*io_read)(int handle, void *buf, unsigned count); + +static MemRec io_mem_buf; + +static int io_handle; +static ioHdrRec io_next_hdr; + +static bool ioReset(int h); +static unsigned char *ioRead(unsigned qty); +static unsigned char *ioNextRecord(void); +static void ioRelease(void); + + +void MVE_ioCallbacks(unsigned (*fn_read)(int handle, void *buf,unsigned count)) +{ + io_read = fn_read; +} + +static bool ioReset(int h) +{ + mve_hdr *hdr; + + io_handle = h; + hdr = (mve_hdr *)ioRead(sizeof(mve_hdr) + sizeof(ioHdrRec)); + if(!hdr) + return false; + hdr->SwapBytes(); + if( strcmp(hdr->FileType, MVE_FILE_TYPE) != 0 || + hdr->id != ~hdr->version + 0x1234 || + // The following two checks may eventually be weakened. + hdr->version != MVE_FILE_VERSION || + hdr->HdrSize != sizeof(mve_hdr)) + return false; + + io_next_hdr = *(ioHdrRec *)(hdr+1); + io_next_hdr.SwapBytes(); + return true; +} + +void MVE_memIO(void *p, unsigned size) +{ + MemInit(&io_mem_buf, size, p); +} + +static unsigned char *ioRead(unsigned len) +{ + unsigned char *buf; + + buf = (unsigned char *)MemAlloc(&io_mem_buf, len); + if (!buf) + return (unsigned char *)NULL; + + if (!(*io_read)(io_handle, buf, len)) + return (unsigned char *)NULL; + + return buf; +} + +static unsigned char *ioNextRecord(void) +{ + unsigned char *buf; + logLabel("StartRead"); + buf = ioRead(io_next_hdr.len + sizeof(ioHdrRec)); + logLabel("EndRead"); + if (!buf) + return (unsigned char *)NULL; + io_next_hdr = *(ioHdrRec *)(buf+io_next_hdr.len); + io_next_hdr.SwapBytes(); + return buf; +} + +static void ioRelease(void) +{ + MemFree(&io_mem_buf); +} + + +//---------------------------------------------------------------------- +// Sound Management +//----------------------- + +#define SOUND_SUPPORT 1 + +#if SOUND_SUPPORT + +static ISoundDevice *snd_ds = NULL; +static ISysSoundBuffer *snd_buffer = NULL; +static SysSoundCaps snd_buffer_caps; +static unsigned int snd_write_cursor = 0; + +enum {snd_queue_max=60}; + +static struct _snd_queue +{ + unsigned ptr; +} snd_queue[snd_queue_max]; +static unsigned snd_fill = 0; +static unsigned snd_empty = 0; +static int snd_pad = 0; +static unsigned snd_stereo = 0; +static unsigned snd_comp16 = 0; +static unsigned snd_bits16 = 0; +static long snd_volume = 0; +static long snd_pan = 0; + +#endif + +void MVE_sndInit(ISoundDevice *lpDS) +{ +#if SOUND_SUPPORT + snd_ds = lpDS; +#endif +} + +void MVE_dsbSetVolume(long lVolume) +{ +#if SOUND_SUPPORT + snd_volume = lVolume; + if (snd_buffer) + { + snd_buffer->SetVolume( snd_volume ); + } +#endif +} + +void MVE_dsbSetPan(long lPan) +{ +#if SOUND_SUPPORT + snd_pan = lPan; + if (snd_buffer) + { + snd_buffer->SetPan( snd_pan ); + } +#endif +} + +static void sndReset(void) +{ +#if SOUND_SUPPORT + if (snd_buffer) + { + snd_buffer->Stop(); + snd_buffer->Release(); + snd_buffer = NULL; + } +#endif +} + +static bool sndConfigure(unsigned rate, unsigned buflen, unsigned stereo, unsigned frequency, unsigned bits16, unsigned comp16) +{ +#if SOUND_SUPPORT + if (!snd_ds) + return true; + + syncSync(); + sndReset(); + + snd_stereo = stereo; + snd_bits16 = bits16; + snd_comp16 = comp16; + snd_fill = 0; + snd_empty = 0; + + SoundWAVEFormatEx snd_wfx; + snd_wfx.wFormatTag = SOUND_WAVE_FORMAT_PCM; + snd_wfx.nChannels = stereo?2:1; + snd_wfx.nSamplesPerSec = frequency; + snd_wfx.nBlockAlign = (stereo?2:1)*(bits16?2:1); + snd_wfx.nAvgBytesPerSec = frequency * snd_wfx.nBlockAlign; + snd_wfx.wBitsPerSample = bits16?16:8; + + SysSoundBufferDesc snd_bufferdesc; + snd_bufferdesc.dwFlags = LNXSND_CAPS_CTRLDEFAULT; + snd_bufferdesc.dwBufferBytes = (buflen + (buflen>>1)) & ~3; + snd_bufferdesc.lpwfxFormat = &snd_wfx; + + int dsrval = snd_ds->CreateSoundBuffer( &snd_bufferdesc, &snd_buffer ); + if( dsrval != 0 ) + return false; + + snd_buffer->SetVolume( snd_volume ); + snd_buffer->SetPan( snd_pan ); + + snd_write_cursor = 0; + + dsrval = snd_buffer->GetCaps( &snd_buffer_caps ); + if (dsrval != 0) + return false; +#endif + + return true; +} + +static void sndSync(void) +{ + //Better frame dropping using more flexible synchronization + sync_late = syncWaitLevel(sync_wait_quanta>>2)>(-sync_wait_quanta>>1) && !sync_FrameDropped; + sync_FrameDropped = false; + +#if SOUND_SUPPORT + if (!snd_ds || !snd_buffer) + return; + + unsigned int dsbstatus; + unsigned int target; + unsigned int play_cursor, write_cursor; +#define set_target(t) (target = (snd_queue[snd_empty].ptr+(t)+snd_buffer_caps.dwBufferBytes)% snd_buffer_caps.dwBufferBytes) +#define target_pending() ( (play_cursor<=write_cursor)?(play_cursor <= target && target < write_cursor):(play_cursor <= target || target < write_cursor)) + + bool need_resync = false; + for (;;) + { + int dsrval = snd_buffer->GetStatus( &dsbstatus ); + if (dsrval != 0) + return; + + dsrval = snd_buffer->GetCurrentPosition( &play_cursor, &write_cursor ); + if (dsrval != 0) + return; + write_cursor = snd_write_cursor; + + // Don't get too far ahead of sound (target-pad not yet played) + set_target(-snd_pad); + if ( target_pending() && (dsbstatus & LNXSND_PLAYING) ) + { + need_resync = true; + } + else + { + break; + } + } + + if (need_resync) + { + syncReset( sync_wait_quanta+(sync_wait_quanta>>2) ); + } + + if (!(dsbstatus & LNXSND_PLAYING)) // If currently not playing + { + // Don't restart too soon (restart when target hasn't been played) + set_target(0); + if (target_pending()) + { + int dsrval = snd_buffer->SetCurrentPosition( target ); + if (dsrval != 0) + return; + + dsrval = snd_buffer->Play( LNXSND_LOOPING ); + if (dsrval != 0) + return; + + syncReset(sync_wait_quanta); + } + } + // Because DirectSound can consume an unpredictable amount into primary, this won't always be invoked when it should + else + { + // Don't get too far behind sound (has target+pad already been played?) + set_target(snd_pad); + { + int amt = write_cursor - play_cursor; + if (amt < 0) amt += snd_buffer_caps.dwBufferBytes; + amt = snd_buffer_caps.dwBufferBytes - amt - 1; + if (amt > (int)snd_buffer_caps.dwBufferBytes/2) + { + amt = snd_buffer_caps.dwBufferBytes/2; + } + play_cursor = (play_cursor - amt + snd_buffer_caps.dwBufferBytes) % snd_buffer_caps.dwBufferBytes; + } + + if (!target_pending()) + { + snd_buffer->Stop(); + //dsrval = snd_buffer->GetCurrentPosition( &snd_write_cursor, &write_cursor ); + } + } + + if (snd_empty != snd_fill) + { + if (snd_empty==snd_queue_max-1) + { + snd_empty=0; + } + else + { + ++snd_empty; + } + } +#endif +} + +// For compressed streams, assumes len (which is in bytes) will be in multiples +// of 2 for mono and 4 for stereo. +static unsigned sndAddHelper(unsigned char *dst, unsigned char **pSrc, unsigned len, unsigned state, bool init) +{ +#if SOUND_SUPPORT + unsigned char *src; + src = *pSrc; + if (!src) + { + memset(dst, (snd_bits16?0:0x80), len); + } + else + { + if (snd_comp16) + { + if (!snd_stereo) + { + if (init) + { + state = IntelSwapper(*(unsigned short *)src); + *(unsigned short *)dst = state; + src += 2; + dst += 2; + len -= 2; + } + + state = sndDecompM16((unsigned short *)dst, src, len>>1, state); + + src += len>>1; + } + else + { + if (init) + { + state = IntelSwapper(*(unsigned long *)src); + *(unsigned long *)dst = state; + src += 4; + dst += 4; + len -= 4; + } + state = sndDecompS16((unsigned short *)dst, src, len>>2, state); + src += len>>1; + } + } + else + { + memcpy(dst, src, len); + src += len; + } + } + + *pSrc = src; + return state; +#else + return 0; +#endif +} + +static void sndAdd(unsigned char *buf, unsigned len) +{ +#if SOUND_SUPPORT + int dsrval; + unsigned int play_cursor,write_cursor; + unsigned int len1, len2; + + unsigned state = 0; + bool init = true; + unsigned char *ptr1, *ptr2; + + snd_pad = len; + + if (!snd_buffer || snd_fill+1==(snd_empty?snd_empty:snd_queue_max)) + return; + + dsrval = snd_buffer->GetCurrentPosition(&play_cursor, &write_cursor); + if (dsrval != 0) + return; + write_cursor = snd_write_cursor; + + dsrval = snd_buffer->Lock(write_cursor,len,(void **)&ptr1,&len1,(void **)&ptr2,&len2, 0/*LNXSND_LOCK_FROMWRITECURSOR*/ /*flags*/); + if (dsrval != 0) + return; + + if (len1) + { + state = sndAddHelper(ptr1, &buf, len1, state, init); + init = false; + snd_write_cursor += len1; + } + + if (len2) + { + sndAddHelper(ptr2, &buf, len2, state, init); + snd_write_cursor = len2; + } + + if (snd_write_cursor==snd_buffer_caps.dwBufferBytes) + { + snd_write_cursor = 0; + } + + snd_buffer->Unlock(ptr1,len1,ptr2,len2); + + snd_queue[snd_fill].ptr = write_cursor; + if (snd_fill==snd_queue_max-1) + { + snd_fill=0; + } + else + { + ++snd_fill; + } +#endif +} + +static void sndRelease(void) +{ +#if SOUND_SUPPORT + // Nothing to free +#endif +} + +static void sndPause(void) +{ +#if SOUND_SUPPORT + if (snd_buffer) + { + snd_buffer->Stop(); + } +#endif +} + +static void sndResume(void) +{ + //Nothing need be done here to resume sound + //The next call to sndSync will automatically resume the sound. +} + +//-------------------------------------------------------------------- +// NextFrame (Video Decompression) +//---------------------------------- + +// static removed from most nf_ vars to support mveliba.asm + +static bool nf_memory_mode = false; + +// NextFrame working storage +unsigned char* nf_buf_cur = NULL; +unsigned char* nf_buf_prv = NULL; +static MemRec nf_mem_buf1; +static MemRec nf_mem_buf2; + +// NextFrame parameters +unsigned char nf_wqty = 0; // (width/SWIDTH) +unsigned char nf_hqty = 0; // (height/SHEIGHT) +unsigned char nf_fqty = 0; // Number of fields +unsigned nf_hicolor = 0; // HiColor (0:none,1:normal,2:swapped) +// +unsigned nf_width = 0; // wqty * SWIDTH +unsigned nf_height = 0; // hqty * SHEIGHT; +unsigned nf_new_line = 0; // width - SWIDTH +unsigned nf_new_row0 = 0; // SHEIGHT*width*2-width +unsigned nf_back_right = 0; // (SHEIGHT-1)*width + +// Frame parameters +// Portion of current frame which has been updated +// and needs to be sent to screen. +// +unsigned nf_new_x = 0; +unsigned nf_new_y = 0; +unsigned nf_new_w = 0; +unsigned nf_new_h = 0; + +// Hicolor format translation tables +unsigned short nf_trans16_lo[256]; +unsigned short nf_trans16_hi[256]; + +void MVE_memVID(void *p1, void *p2, unsigned size) +{ + MemInit(&nf_mem_buf1, size, p1); + MemInit(&nf_mem_buf2, size, p2); +} + +void nfPkConfig(void); + +// ffs() +// Returns position of most significant bit set (0 to 31). +// Assumes bits is nonzero. +static int ffs(unsigned bits) +{ + int pos; + unsigned t; + t = bits & 0xFFFF0000; + if (t) bits=t, pos=16; + else pos=0; + t = bits & 0xFF00FF00; + if (t) bits=t, pos|=8; + t = bits & 0xF0F0F0F0; + if (t) bits=t, pos|=4; + t = bits & 0xCCCCCCCC; + if (t) bits=t, pos|=2; + if (bits & 0xAAAAAAAA) pos|=1; + return pos; +} + +static bool nfConfig(int wqty, int hqty, int fqty, int hicolor) +{ + unsigned size; + + if (!nf_memory_mode) + { + if (nf_buf_cur) + { + free(nf_buf_cur); + nf_buf_cur = NULL; + } + if (nf_buf_prv) + { + free(nf_buf_prv); + nf_buf_prv = NULL; + } + } + + nf_wqty = (unsigned char)wqty; + nf_hqty = (unsigned char)hqty; + nf_fqty = (unsigned char)fqty; + nf_width = wqty * SWIDTH; + nf_height = hqty * fqty * SHEIGHT; + if (opt_fastmode) + { + nf_height >>= 1; + } + + { + if (hicolor) + { + const unsigned int pal_rmask = 0x001F; + const unsigned int pal_bmask = 0xF800; + const unsigned int pal_gmask = 0x07E0; + + const int pal_rshift = ffs(pal_rmask)-4; + const int pal_gshift = ffs(pal_gmask)-4; + const int pal_bshift = ffs(pal_bmask)-4; + unsigned i, r,g,b; + for (i=0, r=0, g=0; g<8; ++g) + { + for (b=0; b<32; ++b, ++i) + { + nf_trans16_lo[i] = (((pal_rshift>0?r<>-pal_rshift)&pal_rmask) | + ((pal_gshift>0?g<>-pal_gshift)&pal_gmask) | + ((pal_bshift>0?b<>-pal_bshift)&pal_bmask) ); + } + } + + for (i=0, r=0, b=0; r<32; ++r) + { + for (g=0; g<32; g+=8, ++i) + { + nf_trans16_hi[i+128] = + nf_trans16_hi[i] = (((pal_rshift>0?r<>-pal_rshift)&pal_rmask) | + ((pal_gshift>0?g<>-pal_gshift)&pal_gmask) | + ((pal_bshift>0?b<>-pal_bshift)&pal_bmask) ); + } + } + } + int size = nf_width * nf_height<<1; + + nf_buf_cur = (unsigned char *)malloc(size); + nf_buf_prv = (unsigned char *)malloc(size); + } + + nf_new_line = nf_width * fqty - SWIDTH; + nf_hicolor = hicolor; + if (hicolor) + { + nf_width <<= 1; + nf_new_line <<= 1; + } + + nf_new_row0 = fqty * SHEIGHT * nf_width; + nf_back_right = fqty * (SHEIGHT-1) * nf_width; + size = nf_width*nf_height; + + nfPkConfig(); + + return true; +} + +static void nfRelease(void) +{ + free(nf_buf_cur); + free(nf_buf_prv); + nf_buf_cur = NULL; + nf_buf_prv = NULL; +} + +static void nfAdvance(void) +{ + unsigned char *tmp = nf_buf_prv; + nf_buf_prv = nf_buf_cur; + nf_buf_cur = tmp; +} + + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// +void nfHiColorDecomp(const unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// Chgs specifies which squares to update. +// Parms are motion parms for squares to update. +// +void nfHiColorDecompChg( const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h ); + + +// Non-HiColor versions + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// +void nfDecomp(const unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + +void nfPkDecomp(unsigned char *ops, unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfPkDecompH(unsigned char *ops, unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + +void nfHPkDecomp(unsigned char *ops, unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// Chgs specifies which squares to update. +// Parms are motion parms for squares to update. +// +void nfDecompChg(const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h); + +//--------------------------------------------------------------------- +// ShowFrame +//------------ + +// TODO: +// Support for software cursor. +// Issues & Considerations: +// 1. Current code only deals with writing to screen, not reading it. +// 2. To prevent flicker, cursor should be drawn into current buffer +// before it is sent to screen, first saving area overwritten. +// After current before is sent to screen, the area modified +// by the cursor in the current buffer should be restored. +// 3. Screen must also be updated in outside areas if cursor +// appears there. This requires read/modify/write to screen, +// because current buffer may not exist or be valid for area +// of screen. Also, contents of screen must be saved so that +// if next frame doesn't modify that area of screen, it can be +// restored when mouse moves away. +// In other words: +// (a): +// Save area on screen where mouse will go. +// Draw mouse onto screen. +// When mouse moves, restore previous contents of screen and +// loop back to (a). +// When screen is to be redrawn with video, draw mouse clipped +// into buffer, saving previous contents. Draw video, then restore +// buffer. Note that saving previous contents saves into same +// area that was used when area under mouse was originally saved, +// but it may only be a subrectangle. +// Question: Should I implement VESA read from screen code? +// Or work with caller to implement? +// With caller: +// Caller provides pointer to mouse image and save area buffers, +// and screen location. Caller informs us when mouse changes +// (position or contents). We deal with drawing mouse into internal +// buffer and updating save area. Caller deals with drawing +// mouse on screen and saving areas from screen. Color zero will +// be assumed transparent. If old and new locations are within +// area we are about to draw, caller need do nothing other than +// call us (we can return a bool to let him know that). We can +// call him to draw cursor just before showframe (whereas +// call back from pausing would be just after showframe). +// Without special support? +// Everytime frame is shown, redraw mouse on screen, saving previous +// contents. If mouse moves, restore previous contents and redraw. +// Result will be a flickering mouse (mostly on, briefly off). + +static mve_cb_ShowFrame sf_ShowFrame = NULL; + +static unsigned sf_ResolutionHeight = 0; // Height of screen +static unsigned sf_ResolutionWidth = 0; // Width of screen +unsigned sf_ScreenHeight = 0; // Height of modifiable screen +unsigned sf_ScreenWidth = 0; // Width of modifiable screen +// Private, see mveliba.asm : +unsigned sf_LineWidth = 0; // Distance between lines in memory +static unsigned sf_hicolor = 0; // Hicolor mode (0:none,1:normal,2:swapped) + +// Banked screen parameters, Private, see mveliba.asm +void *sf_SetBank = NULL; +unsigned sf_WinGran = 0; +unsigned long sf_WinSize = 0; +unsigned sf_WinGranPerSize = 0; +//{sf_WriteWinPtr and sf_WriteWinLimit replace sf_WriteWinSeg, see mveliba.asm} +unsigned char *sf_WriteWinPtr = NULL; +unsigned char *sf_WriteWinLimit = NULL; +unsigned sf_WriteWin = 0; + +// +static bool sf_auto = true; // True if mode can be set from movie + // (mode not specified by caller). +static int sf_auto_mode=0; // Current sf_auto mode. + +static void sfVGA(unsigned w, unsigned h, unsigned resw, unsigned resh); +static void sfShowFrame(int dx, int dy, unsigned field); + +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned field); +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned field); + + +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned field); + +// Restrictions/Assumptions: +// 64K >= WinSize >= WinGran +// WinSize % WinGran == 0 +// WinGran of 64K is represented by 0 +// SetBank is address of function with following protocol: +// bh: 0=Set window, 1=Get Window +// bl: Window number (0 or 1) +// dx: Window position in video memory in units of WinGran. +// on return, registers AX and DX are destroyed. +void MVE_sfSVGA(unsigned w, unsigned h, unsigned LineWidth,unsigned WriteWin, unsigned char *WriteWinPtr, + unsigned long WinSize, unsigned WinGran,void *SetBank, unsigned hicolor) +{ + sf_ScreenWidth = w; + sf_ScreenHeight = h; + sf_ResolutionWidth = w; + sf_ResolutionHeight = h; + sf_LineWidth = LineWidth; + if (opt_fastmode & 4) + { + sf_LineWidth<<=1; + } + sf_WriteWin = WriteWin; + sf_WinSize = WinSize; + sf_WriteWinPtr = WriteWinPtr; + sf_WriteWinLimit = sf_WriteWinPtr + sf_WinSize; + sf_WinGran = WinGran; + sf_SetBank = SetBank; + if (WinGran) + { + // Assumes WinGran divides evenly into WinSize. + sf_WinGranPerSize = (unsigned)WinSize / WinGran; + } + else + { + // Assumes WinSize is also 64K. + sf_WinGranPerSize = 1; + } + sf_auto = false; + sf_hicolor = hicolor; +} + +//QUESTION: Should sfShowFrame also take x,y,w,h as command args? +// The issue is, will it always be true that area to update on +// screen matches area updated in memory, since previous contents +// of memory will be from two frames back, not one! +// Answer: By having compressor compare previous screen to desired screen, +// areas that are identical can be located. Only areas that change +// need to be built in memory. Remaining areas will not be correct, +// but can still be used for source data for next screen. +// Therefore, additional x,y,w,h args are not needed. +// However, should this prove to be wrong, a minor opcode variant +// can always be added which supplies the additional arguments. +static void sfShowFrame(int dx, int dy, unsigned field) +{ + unsigned scaled_width = nf_width*4/opt_hscale_step; + scaled_width = ((scaled_width-12)&~0xf) + 12; // Round down to a multiple of 16 + 12. + opt_hscale_adj = nf_width - (scaled_width/4*opt_hscale_step); + + logLabel("StartShow"); + if (dx<0) + { + if (nf_hicolor) + { + dx = (sf_ScreenWidth - (scaled_width>>1)) >> 1; + } + else + { + dx = (sf_ScreenWidth - scaled_width) >> 1; + } + } + + if (nf_hicolor) + { + dx <<= 1; + } + + if (dy<0) + { + if (opt_fastmode&4) //HACK + { + dy = (sf_ScreenHeight - nf_height*2) >> 1; + } + else + { + dy = (sf_ScreenHeight - nf_height) >> 1; + } + } + + dx &= ~3; // Force to a multiple of 4 boundary for performance! + + if (opt_fastmode&4) //HACK + { + dy>>=1; + } + + if (field) + { + mve_ShowFrameField(nf_buf_cur, nf_width, nf_height,nf_new_x, nf_new_y, nf_new_w, nf_new_h,dx, dy, field); + } + else + { + if (opt_hscale_step!=4) + { + sf_ShowFrame(nf_buf_cur, nf_width, nf_height,0, nf_new_y, scaled_width, nf_new_h, dx, dy, nf_hicolor); + } + else + { + sf_ShowFrame(nf_buf_cur, nf_width, nf_height,nf_new_x, nf_new_y, nf_new_w, nf_new_h,dx, dy, nf_hicolor); + } + } + + logLabel("EndShow"); +} + +void MVE_sfCallbacks(mve_cb_ShowFrame fn_ShowFrame) +{ + sf_ShowFrame = fn_ShowFrame; +} + +// Restriction: w must be a multiple of 4. +// Strong Recommendation: sx and dstx should be multiples of 4. +// dstx & dsty are deltas relative to sx & sy. +// Field is a hack and should be removed. Instead, nfConfig +// should specify interlace mode and cause linestep to be twice +// sf_LineWidth. dstx/dsty should be made absolute to allow interlace +// field to be determined by dst. +// ON THE OTHER HAND -- All this makes user clipping quite complex... +// A caller would probably like to deal in ordinary coordinates, +// but then we still need 'field'. +// Also note that when field is on, the actual height of the image +// on the screen is 2*h alternate lines. +// + +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h, + unsigned short *chgs,unsigned dstx, unsigned dsty); + + +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h, + unsigned short *chgs,unsigned dstx, unsigned dsty); + +static void sfShowFrameChg(int dx, int dy, unsigned short *chgs) +{ + logLabel("StartShowChg"); +} + +//--------------------------------------------------------------------- +// Palette Management +//--------------------- + +static void (*pal_SetPalette)(unsigned char *p, unsigned start, unsigned count) = MVE_SetPalette; + +unsigned char pal_tbl[3*256]; // Private, see mveliba.asm +#if DBL_DBG +unsigned char pal_tbl_old[3*256]; +#endif +unsigned short pal15_tbl[256]; // Private, see mveliba.asm + +void MVE_palCallbacks(void (*fn_SetPalette)(unsigned char *p, unsigned start, unsigned count)) +{ + pal_SetPalette = fn_SetPalette; +} + +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); + +static void palSetPalette(unsigned start, unsigned count) +{ + if (!nf_hicolor) + { + (*pal_SetPalette)((unsigned char *)pal_tbl,start,count); + } +} + +static void palClrPalette(unsigned start, unsigned count) +{ + static unsigned char clr_pal_tbl[256*3]; + if (!nf_hicolor) + { + (*pal_SetPalette)((unsigned char *)clr_pal_tbl,start,count); + } +} + +// Old Synth: 0,21,6 +// 128,32,4 +// +// New Synth: 129,21,6 +// 17,28,4 +// +static void palMakeSynthPalette(int base_r, int range_r, int range_rb,int base_g, int range_g, int range_gb) +{ + unsigned char (*SynthPal)[3] = (unsigned char (*)[3])pal_tbl; + int i,j; + + for (i=0; i= 3) + { + printf("***"); + } + + } +*/ + switch (hdr.major) + { + default: + continue; + + case mcmd_end: + result = MVE_ERR_EOF; + goto movie_eof; + case mcmd_next: + break; + case mcmd_syncInit: + { + marg_syncInit *arg =(marg_syncInit *)p; + arg->SwapBytes(); + if (!syncInit(arg->period, arg->wait_quanta)) + { + result = MVE_ERR_SYNC; + goto done; + } + continue; + } + + case mcmd_sndConfigure: + { + marg_sndConfigure *arg = (marg_sndConfigure *)p; + arg->SwapBytes(); + unsigned comp16 = hdr.minor>=1?arg->comp16:0; + unsigned buflen = arg->buflen; + if (hdr.minor==0) buflen &= 0xFFFF; + if (!sndConfigure(arg->rate, buflen, arg->stereo,arg->frequency, arg->bits16, comp16)) + { + result = MVE_ERR_SND; + goto done; + } + continue; + } + + case mcmd_sndSync: + { + sndSync(); + continue; + } + + case mcmd_nfConfig: + { + marg_nfConfig *arg =(marg_nfConfig *)p; + arg->SwapBytes(); + unsigned hicolor = hdr.minor>=2?arg->hicolor:0; + if (!nfConfig(arg->wqty, arg->hqty,(hdr.minor>=1?arg->fqty:1), hicolor)) + { + result = MVE_ERR_NF; + goto done; + } + // To handle interlace mode, we need the following: + // A window width/height in addition to nf_width/height. + // Window width/height should be used here and in centering + // code in sfShowFrame. + { + unsigned scaled_width = nf_width*4/opt_hscale_step; + scaled_width &= ~0xf; // Round down to a multiple of 16. + if (nf_hicolor) scaled_width>>=1; /*HACK*/ + if (scaled_width + (rm_dx<0?0:rm_dx) > sf_ScreenWidth || + nf_height + (rm_dy<0?0:rm_dy) > sf_ScreenHeight) + { + result = MVE_ERR_GFX_FIT; + goto done; + } + } + // NOTE: Eventually, change this to allow sf_hicolor when !nf_hicolor and + // have show function convert from 256-color to hicolor format. + // HiColor will also need to disable hardware palette changes and + // maintain 15-bit software palette along with 24-bit palette. + if (nf_hicolor && !sf_hicolor) + { + result = MVE_ERR_GFX_FIT; + goto done; + } + continue; + } + + case mcmd_nfDecomp: + { + marg_nfDecomp *arg = (marg_nfDecomp *)p; + arg->SwapBytes(); + if (arg->advance) + { + nfAdvance(); + } + + nfDecomp(MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h); + continue; + } + + case mcmd_sfShowFrame: + { + unsigned field = 0; + marg_sfShowFrame *arg = (marg_sfShowFrame *)p; + arg->SwapBytes(); + + ++rm_FrameCount; + + if (hdr.minor >= 1) + { + field = arg->field; + } + + if (arg->pal_count && !DecompChg_chgs && !sf_hicolor) + { + palClrPalette(arg->pal_start, arg->pal_count); + } + else + { + palSetPalette(arg->pal_start, arg->pal_count); + } + + if (DecompChg_chgs) + { + sfShowFrameChg(rm_dx, rm_dy, DecompChg_chgs); + } + else + { + if (sync_late && arg->pal_count==0) + { + sync_FrameDropped = true; + ++rm_FrameDropCount; + }else + { + sfShowFrame(rm_dx, rm_dy, field); + } + } + + if (arg->pal_count && !DecompChg_chgs && !sf_hicolor) + { + palSetPalette(arg->pal_start, arg->pal_count); + } + + goto FrameDone; + } + + case mcmd_sndAdd: + case mcmd_sndSilence: + { + marg_sndAdd *arg = (marg_sndAdd *)p; + arg->SwapBytes(); + if (arg->TrackMask & rm_track_bit) + sndAdd((hdr.major==mcmd_sndAdd?MCMD_DATA(arg):(unsigned char *)NULL),arg->qty); + continue; + } + + case mcmd_gfxMode: + { + marg_gfxMode *arg = (marg_gfxMode *)p; + arg->SwapBytes(); + if (sf_auto) + { + short mode = arg->mode; + if (opt_fastmode && (opt_fastmode&4)==0) mode |= 0x8000; + if (sf_auto_mode != mode) + if (!MVE_gfxMode(mode)) + { + result = MVE_ERR_GFX_FAIL; + goto done; + } + sf_auto = true; + sf_auto_mode = mode; + } + continue; + } + + case mcmd_palMakeSynthPalette: + { + marg_palMakeSynthPalette *arg = (marg_palMakeSynthPalette *)p; + arg->SwapBytes(); + palMakeSynthPalette(arg->base_r, arg->range_r, arg->range_rb, + arg->base_g, arg->range_g, arg->range_gb); + continue; + } + + case mcmd_palLoadPalette: + { + marg_palLoadPalette *arg = (marg_palLoadPalette *)p; + arg->SwapBytes(); + palLoadPalette(MCMD_DATA(arg), arg->start, arg->count); + continue; + } + + case mcmd_palLoadCompPalette: + { + palLoadCompPalette(p); + continue; + } + + case mcmd_nfChanges: + { + DecompChg_chgs = (unsigned short *)p; + continue; + } + case mcmd_nfParms: + { + DecompChg_parms = (unsigned short *)p; + continue; + } + case mcmd_nfDecompChg: + { + marg_nfDecomp *arg = (marg_nfDecomp *)p; + arg->SwapBytes(); + if (arg->advance) + { + nfAdvance(); + } + + nfDecompChg(DecompChg_chgs, DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + continue; + } + case mcmd_nfPkDecomp: + { + marg_nfDecomp *arg = (marg_nfDecomp *)p; + arg->SwapBytes(); + if (hdr.minor<3) + { + result = MVE_ERR_BADFMT; + goto done; + } + + if (arg->advance) + { + nfAdvance(); + } + + if (nf_hicolor) + { + if (opt_fastmode) + { + result = MVE_ERR_BADFMT; + goto done; + } + + nfHPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + } + else if ((opt_fastmode&3)==1) + { + // Half mode (half height, even lines only) + nfPkDecompH((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + } + else if ((opt_fastmode&3)==2) + { + // Support for dithered mode disabled... + // so just use half mode instead + nfPkDecompH((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + } + else //opt_fastmode==0 + { + // Normal mode + nfPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + } + continue; + } + } + break; + } + } +done: //Error + MVE_rmEndMovie(); + +movie_eof: //End of movie + return result; + +FrameDone: //Done with frame, return to caller for between frame processing + rm_p = p; + rm_len = len; + return 0; +} + +void MVE_rmEndMovie(void) +{ + if (rm_active) + { + syncWait(); + syncRelease(); + sndReset(); + rm_active = false; + } +} + +int MVE_RunMovie(int hFile, int dx, int dy, unsigned track) +{ + int result; + result = MVE_rmPrepMovie(hFile,dx,dy,track); + while (!result) + { + result = MVE_rmStepMovie(); + logLabel("StartUser"); + while (!result) + { + result = (*rm_ctl)(); + if (result!=MVE_CTL_HOLD) + break; + result = MVE_rmHoldMovie(); + } + logLabel("EndUser"); + } + MVE_rmEndMovie(); + return (result==MVE_ERR_EOF)?0:result; +} + +int MVE_RunMovieContinue(int hFile, int dx, int dy, unsigned track) +{ + int result; + result = MVE_rmPrepMovie(hFile,dx,dy,track); + while (!result) + { + result = MVE_rmStepMovie(); + logLabel("StartUser"); + while (!result) + { + result = (*rm_ctl)(); + if (result!=MVE_CTL_HOLD) + break; + result = MVE_rmHoldMovie(); + } + logLabel("EndUser"); + } + //Continue version doesn't call MVE_rmEndMovie; + return (result==MVE_ERR_EOF)?0:result; +} + +void MVE_ReleaseMem(void) +{ + MVE_rmEndMovie(); + ioRelease(); + sndRelease(); + nfRelease(); +} + +//---------------------------------------------------------------------- + +char* MVE_strerror(int code) +{ + char *errors[] = + { + "Movie aborted with special code", + "Movie aborted", + "Movie completed normally", //0 + "Movie completed normally", //-1 + "File I/O error or Unable to allocate I/O buffers", + "Unable to create timer", + "Unable to allocate sound buffers", + "Unable to allocate video buffers", + "Insufficient screen resolution for movie", + "Unable to setup graphics mode used by movie", + "Invalid movie file", + "Incorrect screen color mode", + "StepMovie() without PrepMovie()", + "Unable to initialize Linux Draw System", + "Unable to lock window surface", + "Unknown movie error code" + }; + + if (code >= MVE_CTL_EXIT+1) code = MVE_CTL_EXIT+1; + if (code <= MVE_ERR_LAST-1) code = MVE_ERR_LAST-1; + return errors[MVE_CTL_EXIT+1 - code]; +} + +//---------------------------------------------------------------------- +// Frame Reader +//-------------- + +typedef struct _MVE_frstream +{ + int (*callback)(unsigned op, unsigned subop, void *buf); + + // I/O Stream state + unsigned (*io_read)(int handle, void *buf, unsigned count); + MemRec io_mem_buf; + int io_handle; + ioHdrRec io_next_hdr; + unsigned char *p; + unsigned len; + + // NextFrame working storage + bool nf_memory_mode; + + MemRec nf_mem_buf1; + MemRec nf_mem_buf2; + unsigned char* nf_buf_cur; + unsigned char* nf_buf_prv; + // NextFrame parameters + unsigned char nf_wqty; // (width/SWIDTH) + unsigned char nf_hqty; // (height/SHEIGHT) + unsigned char nf_fqty; // Number of fields + unsigned nf_hicolor; // HiColor (0:none,1:normal,2:swapped) + // NextFrame derived quantities + unsigned nf_width; // wqty * SWIDTH + unsigned nf_height; // hqty * SHEIGHT; + unsigned nf_new_line; // width - SWIDTH + unsigned nf_new_row0; // SHEIGHT*width*2-width + unsigned nf_back_right; // (SHEIGHT-1)*width + // Palette + unsigned char pal_tbl[3*256]; + unsigned pal_start, pal_count; + +} MVE_frStreamRec; + +static void frLoad(MVE_frStream frs) +{ + io_read = frs->io_read; + io_mem_buf = frs->io_mem_buf; + io_handle = frs->io_handle; + io_next_hdr = frs->io_next_hdr; + + nf_memory_mode = frs->nf_memory_mode; + nf_mem_buf1 = frs->nf_mem_buf1; + nf_mem_buf2 = frs->nf_mem_buf2; + nf_buf_cur = frs->nf_buf_cur; + nf_buf_prv = frs->nf_buf_prv; + nf_wqty = frs->nf_wqty; + nf_hqty = frs->nf_hqty; + nf_fqty = frs->nf_fqty; + nf_hicolor = frs->nf_hicolor; + nf_width = frs->nf_width; + nf_height = frs->nf_height; + nf_new_line = frs->nf_new_line; + nf_new_row0 = frs->nf_new_row0; + nf_back_right = frs->nf_back_right; +} + +static void frSave(MVE_frStream frs) +{ + frs->io_read = io_read; + frs->io_mem_buf = io_mem_buf; + frs->io_handle = io_handle; + frs->io_next_hdr = io_next_hdr; + + frs->nf_memory_mode = nf_memory_mode; + frs->nf_mem_buf1 = nf_mem_buf1; + frs->nf_mem_buf2 = nf_mem_buf2; + frs->nf_buf_cur = nf_buf_cur; + frs->nf_buf_prv = nf_buf_prv; + frs->nf_wqty = nf_wqty; + frs->nf_hqty = nf_hqty; + frs->nf_fqty = nf_fqty; + frs->nf_hicolor = nf_hicolor; + frs->nf_width = nf_width; + frs->nf_height = nf_height; + frs->nf_new_line = nf_new_line; + frs->nf_new_row0 = nf_new_row0; + frs->nf_back_right = nf_back_right; +} + +MVE_frStream MVE_frOpen(unsigned (*fn_read)(int handle, void *buf,unsigned count),int handle, + int (*fr_callback)(unsigned, unsigned,void *buf)) +{ + MVE_frStream frs; + MVE_frStreamRec save; + bool failed = false; + + if( !mem_alloc ) + return (MVE_frStream)NULL; + + frs = (struct _MVE_frstream *)(*mem_alloc)(sizeof(*frs)); + if (!frs) + return frs; + memset(frs, 0, sizeof(*frs)); + + frSave(&save); + frLoad(frs); + + MVE_ioCallbacks(fn_read); + failed = !ioReset(handle); + frs->callback = fr_callback; + + if (!failed) + { + frs->p = ioNextRecord(); + frs->len = 0; + } + + frSave(frs); + frLoad(&save); + + if (failed) + { + MVE_frClose(frs); + return (MVE_frStream)NULL; + } + + return frs; +} + +int MVE_frGet(MVE_frStream frs,unsigned char **pBuf,unsigned int *width, unsigned int *height, unsigned int *hicolor) +{ + MVE_frStreamRec save; + unsigned char *p; + unsigned len; + int result = 0; + + frSave(&save); + frLoad(frs); + p = frs->p; + len = frs->len; + + for (;;p=ioNextRecord(),len=0) + { + unsigned short *DecompChg_parms = (unsigned short *)NULL; + + if (!p) + { + result = MVE_ERR_IO; + goto done; + } + for (;;) + { + mcmd_hdr hdr; + p += len; //Advance past data of previous command + hdr = *(mcmd_hdr *)p; + hdr.SwapBytes(); + p += sizeof(hdr); + len = hdr.len; + switch (hdr.major) + { + default: + if (frs->callback) + { + result = (*frs->callback)(hdr.major,hdr.minor,p); + if (result) + goto done; + } + continue; + + case mcmd_end: + result = MVE_ERR_EOF; + goto done; + + case mcmd_next: + break; + + case mcmd_nfConfig: + { + marg_nfConfig *arg = (marg_nfConfig *)p; + arg->SwapBytes(); + unsigned hicolor = hdr.minor>=2?arg->hicolor:0; + unsigned opt_fastmode_save = opt_fastmode; + opt_fastmode = 0; + if (hicolor ||(hdr.minor>=1 && arg->fqty!=1) ||!nfConfig(arg->wqty, arg->hqty,(hdr.minor>=1)?arg->fqty:1, hicolor)) + { + opt_fastmode = opt_fastmode_save; + result = MVE_ERR_NF; + goto done; + } + opt_fastmode = opt_fastmode_save; + continue; + } + + case mcmd_sfShowFrame: + { + unsigned field = 0; + marg_sfShowFrame *arg =(marg_sfShowFrame *)p; + arg->SwapBytes(); + if (hdr.minor >= 1) field = arg->field; + if (field) + { + result = MVE_ERR_BADFMT; + goto done; + } + *pBuf = nf_buf_cur; + *width = nf_width; + *height = nf_height; + *hicolor = nf_hicolor; + frs->pal_start = arg->pal_start; + frs->pal_count = arg->pal_count; + goto done; + } + + case mcmd_nfParms: + { + DecompChg_parms = (unsigned short *)p; + continue; + } + + case mcmd_nfPkDecomp: + { + marg_nfDecomp *arg = (marg_nfDecomp *)p; + arg->SwapBytes(); + if (hdr.minor<3) + { + result = MVE_ERR_BADFMT; + goto done; + } + if (arg->advance) + { + nfAdvance(); + } + if (nf_hicolor) + { + result = MVE_ERR_BADFMT; + goto done; + } + nfPkConfig(); + nfPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + continue; + } + case mcmd_palLoadPalette: + { + marg_palLoadPalette *arg =(marg_palLoadPalette *)p; + arg->SwapBytes(); + memcpy(&frs->pal_tbl[arg->start*3], MCMD_DATA(arg), arg->count*3); + continue; + } + } + break; + } + } + +done: + frSave(frs); + frs->p = p; + frs->len = len; + frLoad(&save); + nfPkConfig(); + if (result) + frs->pal_start = frs->pal_count = 0; + return result; +} + +void MVE_frPal(MVE_frStream frs, unsigned char **p, unsigned *start, unsigned *count) +{ + *p = frs->pal_tbl; + *start = frs->pal_start; + *count = frs->pal_count; +} + +void MVE_frClose(MVE_frStream frs) +{ + MVE_frStreamRec save; + + frSave(&save); + frLoad(frs); + + ioRelease(); + nfRelease(); + + frLoad(&save); + if (mem_free) + { + (*mem_free)(frs); + } +} diff --git a/libmve/mvelibl.h b/libmve/mvelibl.h new file mode 100644 index 000000000..f1fba8dff --- /dev/null +++ b/libmve/mvelibl.h @@ -0,0 +1,273 @@ +/* +** mvelibl.h +** +** Interplay Movie File (MVE) Player +** Library Definitions (32-Bit Linux Version) +** Written by Paul Allen Edelstein, Interplay Productions. +** Partial Linux port by Jeff Slutter, Outrage Entertainment. +** +** (c) 1997 Interplay Productions. All Rights Reserved. +** This file is confidential and consists of proprietary information +** of Interplay Productions. This file and associated libraries +** may not, in whole or in part, be disclosed to third parties, +** incorporated into any software product which is not being created +** for Interplay Productions, copied or duplicated in any form, +** without the prior written permission of Interplay Productions. +** Further, you may not reverse engineer, decompile or otherwise +** attempt to derive source code of this material. +*/ +#ifndef MVELIB_H_INCLUDED +#define MVELIB_H_INCLUDED + +#include "SystemInterfaces.h" +#if defined(__LINUX__) +#include "lnxdsound.h" +#endif +// Call this function to provide hooks into your memory management. +typedef void *(mve_cb_alloc)(unsigned size); +typedef void (mve_cb_free)(void *p); +void MVE_memCallbacks( mve_cb_alloc *fn_alloc, mve_cb_free *fn_free ); + +// This function remains from the DOS version of mvelib. +// It allows you to provide a preallocated buffer for file I/O, +// but under Windows there's no real point to doing this. +void MVE_memIO(void *p, unsigned size); + +// Call this function to provide hook into your file io. +typedef unsigned mve_cb_read(int handle, void *buf, unsigned count); +void MVE_ioCallbacks(mve_cb_read *fn_read); + +// Call this function to provide hook into your digital sound driver. +// Call with NULL if no sound support is available (default). +void MVE_sndInit(ISoundDevice *lpDS); + +// Volume controls. +// These functions are equivalent to the IDirectSoundBuffer +// SetVolume and SetPan functions. They take effect immediately +// and do NOT reset when a new movie starts. +// Volume ranges from 0 (0 db, no volume change) to -10,000 (-100db, essentially silent). +// Pan ranges from -10,000 (left full volume, right -100db), thru 0 (both full), +// thru 10,000 (left -100db, right full volume). +// The default value for volume and pan is zero. +void MVE_dsbSetVolume(long lVolume); +void MVE_dsbSetPan(long lPan); + +// Only call this function to configure software to work with a Super VGA +// mode if you do not have VESA support. +// Restrictions/Assumptions: +// 64K >= WinSize >= WinGran +// WinSize % WinGran == 0 +// WinGran of 64K is represented by 0 +// SetBank is address of function with following protocol: +// bh: 0=Set window, 1=Get Window +// bl: Window number (0 or 1) +// dx: Window position in video memory in units of WinGran. +// on return, registers AX and DX are destroyed. +// +// Hicolor is 0 for 8-bit color, 1 for 15-bit rgb color, 2 +// for byte swapped 15-bit rgb color. +// +// Note: 16-bit WriteWinSeg replaced with 32-bit WriteWinPtr +// +// The functionality of the following function is reduced in the Windows +// version of the player. Call it as follows: +// MVE_sfSVGA(w,h,w,0,NULL,0,0,NULL,hicolor) +// where w and h are the width and height of your window, +// and hicolor is a boolean which indicates if the screen +// is operating in hi color, rather than 8-bit paletted color. +// Under windows, the information provided by this function +// is just used for window centering and for determining +// how and when to do palette callbacks. +void MVE_sfSVGA( unsigned w, unsigned h, unsigned LineWidth, unsigned WriteWin, unsigned char *WriteWinPtr, unsigned long WinSize, unsigned WinGran, void *SetBank, unsigned hicolor); + +// This function alters the display from 640x480 or 640x400 to 640x350 resolution. +void MVE_ForceVres350(void); + +// This function alters the display from 640x480/400/350 to 640x240/200/175. +void MVE_ForceVresHalf(void); + +// **NOTE** There still need to be calls to restore original screen resolution +// after using MVE_ForceVres350() or MVE_ForceVresHalf()! +// Only call this function to either +// 1. Replace method of copying frame to screen (perhaps for a nonstandard +// screen format). +// 2. Wrap your own code around the transfer of frame to screen +// or modify which portions of the screen are updated. +// This function replaces calls to the default MVE_ShowFrame function +// with calls to your function, which can itself call MVE_ShowFrame. +typedef void (*mve_cb_ShowFrame)(unsigned char *buf, unsigned int bufw, unsigned int bufh, + unsigned int sx, unsigned int sy, unsigned int w, unsigned int h, + unsigned int dstx, unsigned int dsty, unsigned int hicolor); +void MVE_sfCallbacks(mve_cb_ShowFrame fn_ShowFrame); + +typedef void mve_cb_SetPalette(unsigned char *p, unsigned start, unsigned count); +void MVE_palCallbacks(mve_cb_SetPalette *fn_SetPalette); + +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); + +// Configure the software for a graphics mode, optionally setting the +// display to that mode (see the MVE_GFX_xxx constants defined below). +bool MVE_gfxMode(short mode); + +// Reset the screen to text mode (usually done before exiting a program). +void MVE_gfxReset(void); + +// Set line for split screen graphics +// {Use vbe_SetDisplayStart(x,y) to set vid buf for upper screen} +void MVE_gfxSetSplit(unsigned line); + +// Setup double buffering +void MVE_gfxSetDoubleBuffer(unsigned y1, unsigned y2, unsigned vis); + +// Get double buffering state +void MVE_gfxGetDoubleBuffer(unsigned *vis_y, unsigned *hid_y); + +// Enable double buffering for auto screen modes +void MVE_sfAutoDoubleBuffer(unsigned on); + +// Wait for video retrace off (0) or on (1) +void MVE_gfxWaitRetrace(unsigned state); + +//--------------------------------------------- +// Establish callback for user control of movie playback. +typedef int mve_cb_ctl(void); +void MVE_rmCallbacks(mve_cb_ctl *fn_ctl); + +// Specify playback fastmode option (default is MVE_RM_NORMAL). +#define MVE_RM_NORMAL 0 // Normal playback +#define MVE_RM_HALF 1 // Half height (even lines only) +#define MVE_RM_DITHERED 2 // Half height (dither between lines) +#define MVE_RM_HALF_2 5 // Full height, even lines only +#define MVE_RM_DITHERED_2 6 // Full height, dither, even lines only + +void MVE_rmFastMode(int mode); + +// Specifying horizontal magnification: +// 3: 4/3 horizontal magnification +// 4: normal +void MVE_rmHScale(int hscale); + +// Get frame count and number of dropped frames from last movie played. +void MVE_rmFrameCounts(unsigned *FrameCount, unsigned *FrameDropCount); + +// Dump timing statistics (if enabled). +void MVE_logDumpStats(void); + +// Run a compressed movie by reading data starting at the current +// position in the file specified by handle hFile. +// The movie window is displaced by dx,dy from the upper left hand corner +// or is centered if dx,dy is -1,-1. +// track specifies which audio track to play (usually 0 for a single +// audio track). +// +// Returns an error/result code. +// +// Memory may be dynamically allocated while movie runs. +int MVE_RunMovie(int hFile, int dx, int dy, unsigned track); + +// MVE_RunMovieContinue is the same as MVE_RunMovie except that it does not +// automatically call MVE_rmEndMovie(). This may improve the smoothness +// of immediately playing another movie afterwards. +int MVE_RunMovieContinue(int hFile, int dx, int dy, unsigned track); + +// Alternative to using MVE_RunMovie() and MVE_rmCallbacks(). +// Call MVE_rmPrepMovie() to prepare movie for playing. +// Call MVE_rmStepMovie() to display next frame of movie until nonzero +// result is returned (MVE_ERR_EOF for no next frame or some other error). +// Call MVE_rmHoldMovie() to hold on current frame (and pause audio). +// Call MVE_rmEndMovie() to abort movie. +// All functions except MVE_rmEndMovie() return an error code. +int MVE_rmPrepMovie(int hFile, int dx, int dy, unsigned track); +int MVE_rmStepMovie(void); +int MVE_rmHoldMovie(void); +void MVE_rmEndMovie(void); + +// Frame Reader Streams +// This is a special interface to the movie system which +// allows a movie file to be opened as a stream from which +// its frames may be retrieved. Audio and timing information +// are ignored. For 256-color screen applications, palette +// information is also typically ignored, and movies with a common +// predefined palette are used. However, for hi-color screen +// applications, an interface to obtain palette information has +// been provided. This system is intended for use by video sprites +// played off of the hard drive or out of memory. +typedef struct _MVE_frstream *MVE_frStream; + +// MVE_frOpen +// Before calling this function, be sure to call MVE_memCallbacks() +// and MVE_rmDirectDraw(). +// fn_read specifies a file reader similar to the one +// used by MVE_ioCallbacks(). +// handle specifies a file handle for an already opened +// movie file. It is used by the file reader and is similar +// to hFile argument used by MVE_RunMovie() and MVE_rmPrepMovie(). +// fr_callback is normally NULL, but can be used to supply +// a handler for user data which has been interleaved into +// the movie stream. +// +// If the movie file is invalid or the call otherwise fails, +// NULL is returned. +MVE_frStream MVE_frOpen( unsigned (*fn_read)(int handle, void *buf, unsigned count), int handle, int (*fr_callback)(unsigned op, unsigned subop, void *buf) ); + +// MVE_frGet +// Returns the next frame from the specified frame reader stream +// a nonzero error code {the same codes as returned by MVE_RunMovie() +// and MVE_rmStepMovie()}. +// If successful, MVE_frGet(frs, &buf, &w, &h) returns a pointer +// to a surface containing the frame in pBuf, +// and its width and height in w and h. +int MVE_frGet(MVE_frStream frs,unsigned char **pBuf,unsigned int *width, unsigned int *height, unsigned int *hicolor); + +// MVE_frPal +// After each successful call to MVE_frGet(), this call may be used to +// obtain corresponding palette information. It returns a pointer to the +// entire current palette for the frame, and the subportion of the palette +// which has changed this frame is identified by start and count (they will +// both be zero on frames for which the palette has not changed). +// +// Paltbl points to 256*3 bytes of 6-bit r,g,b triples. +// Start ranges from 0 to 255. Count from 0 to 256. +// +// These conventions are similar to those used by the palette callback arguments +// with the standard player interface, except that this interface requires +// polling each frame instead, and must be passed pointers to the variables where +// the values will be returned. +// +void MVE_frPal(MVE_frStream frs, unsigned char **pPaltbl, unsigned *pStart, unsigned *pCount); + +// MVE_frClose +// Closes the specified Frame Reader Stream frs. +// Frees all storage associated with the stream. +// The specified frs must not be used after this call. +// Note that the open file handle specified in MVE_frOpen() is +// not closed by this call...that is the caller's responsibility. +void MVE_frClose(MVE_frStream frs); + +// Release any memory dynamically allocated by MVE_RunMovie. +void MVE_ReleaseMem(void); + +// Return string corresponding to MVE_RunMovie result code. +char* MVE_strerror(int code); + +// RunMovie callback control code and result codes. +// Codes > 1 are user defined. +#define MVE_CTL_HOLD -1 // Returned by rmCtl() to hold current frame +#define MVE_CTL_EXIT 1 // Returned by rmCtl() to end movie + +#define MVE_ERR_EOF -1 // Returned by StepMovie() for end of movie +#define MVE_ERR_IO -2 // File I/O error or unable to alloc memory. +#define MVE_ERR_SYNC -3 // Timer error. +#define MVE_ERR_SND -4 // Unable to allocate memory for sound +#define MVE_ERR_NF -5 // Unable to allocate memory for video +#define MVE_ERR_GFX_FIT -6 // Screen size too small for movie +#define MVE_ERR_GFX_FAIL -7 // Failed to set desired graphics mode +#define MVE_ERR_BADFMT -8 // Not a MVE file or unacceptable version +#define MVE_ERR_GFX_CLR -9 // Incorrect screen color mode +#define MVE_ERR_PREP -10 // StepMovie() without PrepMovie() +#define MVE_ERR_LD -11 // Unable to initialize Draw system (DirectDraw, etc) +#define MVE_ERR_LOST -12 // Direct Draw Surface Lost + +#define MVE_ERR_LAST -12 + +#endif diff --git a/libmve/mvelibwa.asm b/libmve/mvelibwa.asm new file mode 100644 index 000000000..10cdd9ba1 --- /dev/null +++ b/libmve/mvelibwa.asm @@ -0,0 +1,15054 @@ +; mvelibwa.c +; +; Interplay Movie (MVE) File Player Library (32-Bit Win95 Version) +; Assembly Language Components +; Written by Paul Allen Edelstein +; +; (c) 1997 Interplay Productions. All Rights Reserved. +; This file is confidential and consists of proprietary information +; of Interplay Productions. This file and associated libraries +; may not, in whole or in part, be disclosed to third parties, +; incorporated into any software product which is not being created +; for Interplay Productions, copied or duplicated in any form, +; without the prior written permission of Interplay Productions. +; Further, you may not reverse engineer, decompile or otherwise +; attempt to derive source code of this material. +; + +; .386 + .486 ; I only need .386, but I wanted the 486 cycle timings +ifdef SYMANTEC + .MODEL SMALL, C +DGROUP group _TEXT, _DATA +else + .MODEL FLAT, C +endif + +;;--- Options --- + +ONLYNEW equ 0 ; For debug, disables motion comp +LOGGING equ 0 ; Log timing statistics +PARTIAL equ 1 ; Support for partial updates +PKDATA equ 1 ; Support for packed data +HICOLOR equ 1 ; Support for HiColor +INTERP equ 0 ; Interpolated squares + ; 0:none (4x4x8), 1:generic dither, + ; 2:direction dither, 3:blend +COMPOPS equ 1 ; Compressed opcode table +SCALING equ 1 ; Scaling support +DECOMPD equ 0 ; Support for dithered half vert res +TRANS16 equ 1 ; Support for translating 16-bit rgb format + +;;--- Types --- + +PTRBYTE TYPEDEF PTR BYTE +PTRWORD TYPEDEF PTR WORD +PTRDWORD TYPEDEF PTR DWORD +PTRPROC TYPEDEF PTR PROC + +;;--- Constants --- + +; Width and height of sections in pixels. +SWIDTH equ 8 +SHEIGHT equ 8 + +LOG2_SWIDTH equ 3 +LOG2_SHEIGHT equ 3 + +;;--- + +EXTERN pal_tbl:BYTE ; unsigned char pal_tbl[3*256]; +EXTERN pal15_tbl:WORD ; unsigned short pal15_tbl[256]; +if INTERP eq 3 +EXTERN blend_tbl: PTRDWORD ; unsigned *blend_tbl; +endif + .data + + BYTE "(c) 1997 Interplay Productions. All Rights Reserved.\n" + BYTE "This file is confidential and consists of proprietary information\n" + BYTE "of Interplay Productions. This file and associated libraries\n" + BYTE "may not, in whole or in part, be disclosed to third parties,\n" + BYTE "incorporated into any software product which is not being created\n" + BYTE "for Interplay Productions, copied or duplicated in any form,\n" + BYTE "without the prior written permission of Interplay Productions.\n" + BYTE "Further, you may not reverse engineer, decompile or otherwise\n" + BYTE "attempt to derive source code of this material.\n",0 + + .code + +PUBLIC mveliba_start, mveliba_end + +mveliba_start: + +;---------------------------------------------------------------------- +; Logging Support +;----------------- + +if LOGGING + +;void logLabel(char *label) +; +logLabel PROTO lbl:PTRBYTE + +LOG_LABEL MACRO msg +LOCAL lbl + .data +lbl BYTE msg,0 + .code + INVOKE logLabel, offset lbl + ENDM + +else + +LOG_LABEL MACRO msg + ENDM + +endif + +;-------------------------------------------------------------------- +; Sound Management +;-------------------- + +EXTERN snd_8to16: WORD ; short snd_8to16[256]; + +;unsigned sndDecompM16(unsigned short *dst, unsigned char *src, +; unsigned len, unsigned prev); +; +;Decompresses a mono stream containing len samples +;(src is len bytes, dst is len*2 bytes) +;prev is the previous decompression state or zero. +;Returns new decompression state. +; +sndDecompM16 PROC USES ESI EDI EBX, \ + dst:PTRWORD, src:PTRBYTE, len:DWORD, prev:DWORD + mov eax, prev + + mov ecx, len + jecxz done + + mov esi, src + mov edi, dst + + xor ebx, ebx + +lp: mov bl, byte ptr [esi] + add esi, 1 + add ax, word ptr snd_8to16[ebx*2] + mov word ptr [edi], ax + add edi, 2 + dec ecx + jnz lp + +done: ret +sndDecompM16 ENDP + +;unsigned sndDecompS16(unsigned short *dst, unsigned char *src, +; unsigned len, unsigned prev); +; +;Decompresses a stereo stream containing len samples +;(src is len*2 bytes, dst is len*4 bytes) +;prev is the previous decompression state or zero +; (It encodes the 16-bit states of the two stereo channels +; in its low and high order 16-bit halves.) +;Returns new decompression state. +; +sndDecompS16 PROC USES ESI EDI EBX, \ + dst:PTRWORD, src:PTRBYTE, len:DWORD, prev:DWORD + movzx eax, word ptr prev + movzx edx, word ptr prev+2 + + mov ecx, len + jecxz done + + mov esi, src + mov edi, dst + + xor ebx, ebx + +lp: mov bl, byte ptr [esi] + add esi, 1 + add ax, word ptr snd_8to16[ebx*2] + mov word ptr [edi], ax + add edi, 2 + + mov bl, byte ptr [esi] + add esi, 1 + add dx, word ptr snd_8to16[ebx*2] + mov word ptr [edi], dx + add edi, 2 + + dec ecx + jnz lp + +done: shl edx, 16 + or eax, edx + ret +sndDecompS16 ENDP + +;-------------------------------------------------------------------- +; NextFrame (Video Decompression) +;---------------------------------- + +;; NextFrame working storage + ; MemRec nf_mem_buf1; + ; MemRec nf_mem_buf2; +EXTERN nf_buf_cur: PTRBYTE ; unsigned char* nf_buf_cur; +EXTERN nf_buf_prv: PTRBYTE ; unsigned char* nf_buf_prv; + +;; NextFrame parameters +EXTERN nf_wqty: BYTE ;unsigned char nf_wqty; // (width/SWIDTH) +EXTERN nf_hqty: BYTE ;unsigned char nf_hqty; // (height/SHEIGHT) +EXTERN nf_fqty: BYTE ;unsigned char nf_fqty; // Number of fields +if HICOLOR +EXTERN nf_hicolor: DWORD ;unsigned nf_hicolor; // HiColor (0:none,1:normal,2:swapped) +endif +;; +EXTERN nf_width: DWORD ;unsigned nf_width; // wqty * SWIDTH +EXTERN nf_height: DWORD ;unsigned nf_height; // hqty * SHEIGHT; +EXTERN nf_new_line: DWORD ;unsigned nf_new_line; // width - SWIDTH +EXTERN nf_new_row0: DWORD ;unsigned nf_new_row0; // SHEIGHT*width*2-width +EXTERN nf_back_right: DWORD ;unsigned nf_back_right; // (SHEIGHT-1)*width + +;; Frame parameters +;; Portion of current frame which has been updated +;; and needs to be sent to screen. +;; +EXTERN nf_new_x: DWORD ;unsigned nf_new_x; +EXTERN nf_new_y: DWORD ;unsigned nf_new_y; +EXTERN nf_new_w: DWORD ;unsigned nf_new_w; +EXTERN nf_new_h: DWORD ;unsigned nf_new_h; + + +NF_DECOMP_INIT MACRO HI_COLOR_FLAG: REQ + + mov ax, ds ; Insure es==ds for symantec flat mode + mov es, ax + + mov eax, nf_buf_prv ; DiffBufPtrs = nf_buf_prv - nf_buf_cur + sub eax, nf_buf_cur + mov DiffBufPtrs, eax + + xor ebx, ebx ; ebx = nf_fqty (convert to 32-bits) + mov bl, nf_fqty + + mov eax, x ; nf_new_x = x*SWIDTH*2^HI_COLOR_FLAG; + shl eax, LOG2_SWIDTH+HI_COLOR_FLAG + mov nf_new_x, eax + + mov eax, w ; nf_new_w = w*SWIDTH*2^HI_COLOR_FLAG; + shl eax, LOG2_SWIDTH+HI_COLOR_FLAG + mov nf_new_w, eax + + mov eax, y ; nf_new_y = y*nf_fqty*SHEIGHT; + shl eax, LOG2_SHEIGHT + mul ebx ;nf_fqty + mov nf_new_y, eax + + mov eax, h ; nf_new_h = h*nf_fqty*SHEIGHT; + shl eax, LOG2_SHEIGHT + mul ebx ;nf_fqty + mov nf_new_h, eax + + mov eax, nf_new_row0 ; new_row = nf_new_row0 - nf_new_w; + sub eax, nf_new_w + mov new_row, eax + + ;; Move to correct place in current buffer + mov eax, nf_buf_cur ; tbuf = nf_buf_cur + mov tbuf, eax + .if x || y ; if (x||y) + mov eax, nf_new_y ; tbuf += nf_new_y*nf_width + nf_new_x; + mul nf_width + add eax, nf_new_x + add tbuf, eax + .endif + + ENDM ; DECOMP_INIT + + +DECOMP_BODY MACRO HI_COLOR_FLAG:REQ + + LOCAL HI_COLOR_SCALE +HI_COLOR_SCALE equ HI_COLOR_FLAG+1 + + NF_DECOMP_INIT HI_COLOR_FLAG + + mov eax, w ; parms_sz = (w*h*nf_fqty)<<1 + mul h + mul ebx ;nf_fqty + shl eax, 1 + mov parms_sz, eax + + ; esi indexes comp (to get new section data) + ; edi indexes current screen buffer + ; edx is a frequently used constant + ; ebx indexes section params + mov edi, tbuf + mov edx, nf_new_line ; width - SWIDTH + mov ebx, comp ; Parms index + mov esi, ebx + add esi, parms_sz ; Skip over flags (w*h*2) + + ; Iterate over params and copy new hires data to appropriate sections. + mov cl, nf_fqty +ns_0f: push ecx + push edi + mov ch, byte ptr h +ns_0: mov cl, byte ptr w +ns_1: cmp word ptr [ebx],0 + je ns_10 + add edi, SWIDTH*HI_COLOR_SCALE +ns_2: add ebx, 2 + dec cl + jnz ns_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ns_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ns_0f + jmp ns_99 + + ; Copy new data to one section + ; Enter with esi pointing to source data, edi to screen section. + + ; Assumes SWIDTH=8 (16-bit data) and SHEIGHT=8 +ns_10: + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + jmp ns_2 + +ns_99: + +ife ONLYNEW ; if !ONLYNEW + ; Iterate over flags and motion source addresses from params + ; to determine which sections to move. + ; ebx indexes params. + ; esi indexes source from buffer + ; esi will be computed as +- 16K relative to edi. + + sub ebx, parms_sz ; Move back to start of section parms + mov edi, tbuf + mov cl, nf_fqty + xor esi, esi +ms_0f: push ecx + push edi + mov ch, byte ptr h +ms_0: mov cl, byte ptr w +ms_1: or si, [ebx] + jg ms_10 + jl ms_j30 + add edi, SWIDTH*HI_COLOR_SCALE +ms_2: add ebx, 2 + dec cl + jnz ms_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_0f + jmp ms_99 + +ms_j30: jmp ms_30 + + ; Move one section from current screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_10: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-04000h*HI_COLOR_SCALE+edi] + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + xor esi, esi ; Reset esi to zero + jmp ms_2 + + +ms_20f: push ecx + push edi + mov ch, byte ptr h +ms_20: mov cl, byte ptr w +ms_21: or si, [ebx] + jl ms_30 + jg ms_j10 + add edi, SWIDTH*HI_COLOR_SCALE +ms_22: add ebx, 2 + dec cl + jnz ms_21 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_20 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_20f + jmp ms_99 + +ms_j10: jmp ms_10 + + ; Move one section from previous screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_30: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-0C000h*HI_COLOR_SCALE+edi] + add esi, DiffBufPtrs ; and point to other buffer + + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + xor esi, esi ; Reset esi to zero + jmp ms_22 + +ms_99: +endif ; #endif !ONLYNEW + ENDM ; DECOMP_BODY + +if PARTIAL + +DECOMP_CHG_BODY MACRO HI_COLOR_FLAG:REQ + + LOCAL HI_COLOR_SCALE +HI_COLOR_SCALE equ HI_COLOR_FLAG+1 + + NF_DECOMP_INIT HI_COLOR_FLAG + + ; esi indexes comp (to get new section data) + ; edi indexes current screen buffer + ; edx is a frequently used constant + ; ebx indexes section params + mov edi, tbuf + mov edx, nf_new_line ; width - SWIDTH + mov esi, comp + mov ebx, parms + + ; Iterate over params and copy new hires data to appropriate sections. + + mov eax, chgs + mov pChgs, eax + mov eax, 0 + mov cl, nf_fqty +ns_0f: push ecx + push edi + mov ch, byte ptr h +ns_0: mov cl, byte ptr w +ns_1: add ax, ax + ja ns_1b + jz ns_5 + cmp word ptr [ebx],0 + je ns_10 + add ebx, 2 +ns_1b: add edi, SWIDTH*HI_COLOR_SCALE +ns_2: dec cl + jnz ns_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ns_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ns_0f + jmp ns_99 + +ns_5: mov eax, pChgs + add pChgs, 2 + mov ax, [eax] + jmp ns_1 + + ; Copy new data to one section + ; Enter with ds:si pointing to source data, es:di to screen section. + + ; Assumes SWIDTH=8 (16-bit data) and SHEIGHT=8 +ns_10: + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + add ebx, 2 + jmp ns_2 + +ns_99: + +ife ONLYNEW ; if !ONLYNEW + ; Iterate over flags and motion source addresses from params + ; to determine which sections to move. + ; ebx indexes params. + ; esi indexes source from buffer + ; esi will be computed as +- 16K relative to edi. + + mov edi, tbuf + mov ebx, parms + + mov eax, chgs + mov pChgs, eax + mov eax, 0 + mov cl, byte ptr nf_fqty + xor esi, esi +ms_0f: push ecx + push edi + mov ch, byte ptr h +ms_0: mov cl, byte ptr w +ms_1: add ax, ax + ja ms_1b + jz ms_5 + or si, [ebx] + jg ms_10 + jl ms_j30 + add ebx, 2 +ms_1b: add edi, SWIDTH*HI_COLOR_SCALE +ms_2: dec cl + jnz ms_1 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_0 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_0f + jmp ms_99 + +ms_5: mov eax, pChgs + add pChgs, 2 + mov ax, word ptr [eax] + jmp ms_1 + + +ms_j30: jmp ms_30 + + ; Move one section from current screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_10: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-04000h*HI_COLOR_SCALE+edi] + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + xor esi, esi ; Reset esi to zero + add ebx, 2 + jmp ms_2 + + +ms_20f: push ecx + push edi + mov ch, byte ptr h +ms_20: mov cl, byte ptr w +ms_21: add ax, ax + ja ms_21b + jz ms_25 + or si, [ebx] + jl ms_30 + jg ms_j10 + add ebx, 2 +ms_21b: add edi, SWIDTH*HI_COLOR_SCALE +ms_22: dec cl + jnz ms_21 + add edi, new_row ; SHEIGHT*width - SWIDTH*w + dec ch + jnz ms_20 + pop edi + pop ecx + add edi, nf_width + dec cl + jnz ms_20f + jmp ms_99 + +ms_25: mov eax, pChgs + add pChgs, 2 + mov ax, [eax] + jmp ms_21 + +ms_j10: jmp ms_10 + + ; Move one section from previous screen to current screen. + ; Enter with + ; edi pointing to destination screen section, + ; relative value of source offset in esi. + + ; The following assumes SWIDTH==8 and SHEIGHT==8 + +ms_30: ; Make esi absolute + lea esi, [esi*HI_COLOR_SCALE-0C000h*HI_COLOR_SCALE+edi] + add esi, DiffBufPtrs ; and point to other buffer + + REPEAT 7 + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + add esi, edx + add edi, edx + ENDM + REPEAT 2*HI_COLOR_SCALE + movsd + ENDM + + sub edi, nf_back_right ; (SHEIGHT-1)*width + add ebx, 2 + xor esi, esi ; Reset esi to zero + jmp ms_22 + +ms_99: +endif ; !ONLYNEW + + ENDM ; DECOMP_CHG_BODY + +endif ; PARTIAL + +;;--- HiColor versions + +if HICOLOR + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; + +;void +;nfHiColorDecomp(unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfHiColorDecomp PROC USES ESI EDI EBX, \ + comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + + LOCAL parms_sz: DWORD + + LOG_LABEL "StartHiColorDecomp" + DECOMP_BODY 1 ; HiColor + LOG_LABEL "EndHiColorDecomp" + + ret +nfHiColorDecomp ENDP + +if PARTIAL + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; Chgs specifies which squares to update. +; Parms are motion parms for squares to update. +; +;void +;nfHiColorDecompChg(unsigned short *chgs, +; unsigned short *parms, +; unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfHiColorDecompChg PROC USES ESI EDI EBX, \ + chgs:PTRWORD, \ + parms:PTRWORD, \ + comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + + LOCAL pChgs: PTRBYTE + + LOG_LABEL "StartHiColorDecompChg" + DECOMP_CHG_BODY 1 ; HiColor + LOG_LABEL "EndHiColorDecompChg" + ret +nfHiColorDecompChg ENDP + + +endif ; PARTIAL + + +endif ; HICOLOR + +; Non-HiColor versions + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; +;void nfDecomp(unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfDecomp PROC USES ESI EDI EBX, \ + comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + + LOCAL parms_sz: DWORD + +if HICOLOR + .if nf_hicolor + INVOKE nfHiColorDecomp, comp,x,y,w,h + ret + .endif +endif + + LOG_LABEL "StartDecomp" + DECOMP_BODY 0 ; Not HiColor + LOG_LABEL "EndDecomp" + + ret +nfDecomp ENDP + +if PARTIAL + +; Decompress into subsection of current buffer specified +; by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +; Chgs specifies which squares to update. +; Parms are motion parms for squares to update. +; +;void +;nfDecompChg(unsigned short *chgs, +; unsigned short *parms, +; unsigned char *comp, +; unsigned x, unsigned y, unsigned w, unsigned h) +; +nfDecompChg PROC USES ESI EDI EBX, \ + chgs:PTRWORD, \ + parms:PTRWORD, \ + comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row: DWORD + LOCAL DiffBufPtrs: DWORD + + LOCAL pChgs: PTRBYTE + +if HICOLOR + .if nf_hicolor + INVOKE nfHiColorDecompChg, chgs,parms,comp,x,y,w,h + ret + .endif +endif + + LOG_LABEL "StartDecompChg" + DECOMP_CHG_BODY 0 ; Not HiColor + LOG_LABEL "EndDecompChg" + + ret +nfDecompChg ENDP + + +endif ; PARTIAL + +;---------------------------------------------------------------------- + +if PKDATA + .data + +if (INTERP eq 1) or (INTERP eq 2) ; *** Old version for dithering *** +; luminace table for palette entries +lum_tbl DWORD 256 DUP (0) +endif + +; signed 8-bit y * nf_width +nfpk_ShiftY DWORD 256 DUP (0) + +; Constant tables + +; 8-bit -8:7 x nf_width + -8:7 +nfpk_ShiftP1 LABEL WORD +FOR y, <-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7> + FOR x, <-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7> + BYTE x,y + ENDM +ENDM + +; 8-bit to right and below in roughly 0:14*nf_width + -14:14 (-3 cases) +; negative is +; 8-bit to left and above in roughly -14:0*nf_width + -14:14 (-3 cases) +nfpk_ShiftP2 LABEL WORD +FOR y, <0,1,2,3,4,5,6,7> + FOR x, <8,9,10,11,12,13,14> + BYTE x,y + ENDM +ENDM +FOR y, <8,9,10,11,12,13> + FOR x, <-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1> + BYTE x,y + ENDM + FOR x, <0,1,2,3,4,5,6,7,8,9,10,11,12,13,14> + BYTE x,y + ENDM +ENDM +FOR x, <-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1> + BYTE x,14 +ENDM +FOR x, <0,1,2,3,4,5,6,7,8,9,10,11> + BYTE x,14 +ENDM + +nfpk_mov4l LABEL DWORD +; mov ax, bx,cx +MOV4L_REGS TEXTEQU > +%FOR m4, MOV4L_REGS +% FOR m3, MOV4L_REGS +% FOR m2, MOV4L_REGS +% FOR m1, MOV4L_REGS + BYTE m2,m1,m4,m3 + ENDM + ENDM + ENDM + ENDM + +nfpk_mov8 LABEL DWORD +; mov ax, bx/dx/cx/bp +MOV8_REGS TEXTEQU > +%FOR m4, MOV8_REGS +% FOR m3, MOV8_REGS +% FOR m2, MOV8_REGS +% FOR m1, MOV8_REGS + BYTE m2,m1,m4,m3 + ENDM + ENDM + ENDM + ENDM + +nfpk_mov4 LABEL DWORD +; mov al, bl/bh/cl/ch +MOV4_REGS0 TEXTEQU > +; mov ah, bl/bh/cl/ch +MOV4_REGS1 TEXTEQU > +%FOR m4, MOV4_REGS1 +% FOR m3, MOV4_REGS0 +% FOR m2, MOV4_REGS1 +% FOR m1, MOV4_REGS0 + BYTE m3,m4,m1,m2 + ENDM + ENDM + ENDM + ENDM + + .code + +; nfPkConfig initializes tables used by nfPkDecomp +; which are dependent on screen size. +nfPkConfig PROC USES ESI EDI EBX + + ; Build ShiftY table + ; + lea edi, nfpk_ShiftY + mov ebx, nf_width + + mov eax, 0 + mov ecx, 128 +lp1: mov [edi], eax + add edi,4 + add eax,ebx + dec ecx + jne lp1 + + mov eax, ebx + shl eax, 7 + neg eax + mov ecx, 128 +lp2: mov [edi], eax + add edi,4 + add eax,ebx + dec ecx + jne lp2 + + ret +nfPkConfig ENDP + +if (INTERP eq 1) or (INTERP eq 2) +; nfPkPal initializes tables used by nfPkDecomp +; which are dependent on palette. +nfPkPal PROC USES ESI EDI EBX + + ; Build palette luminance table + ; + lea esi, pal_tbl + lea edi, lum_tbl + mov ecx, 256 +lp3: xor eax, eax + xor ebx, ebx + xor edx, edx + mov al, [esi] ; r + mov bl, [esi+1] ; g + mov dl, [esi+2] ; b + add esi, 3 + imul eax, 2990 + imul ebx, 5866 + imul edx, 1144 + add eax, ebx + add eax, edx + mov [edi], eax + add edi, 4 + dec ecx + jnz lp3 + + ret +nfPkPal ENDP + +elseif INTERP eq 3 + +nfPkInterp1 MACRO left:REQ, right:REQ + xor eax, eax + mov al, left + mov ah, right + mov eax, [esi+eax*4] + mov edx, eax + mov dl, dh + shl edx, 8 + mov dl, left + mov [edi], edx + mov ah, right + ror eax, 16 + mov [edi+4], eax + ENDM + +nfPkInterp2 MACRO left:REQ, right:REQ + xor eax, eax + mov al, left + mov ah, right + mov eax, [esi+eax*4] + mov edx, eax + mov dl, dh + shl edx, 8 + mov dl, left + mov [edi], edx + mov [edi+ebp*1], edx + mov ah, right + ror eax, 16 + mov [edi+4], eax + mov [edi+4+ebp*1], eax + ENDM + +endif + +ifdef SYMANTEC +EXTERN _data_bottom:PTRBYTE +endif + +; Normal version +; +nfPkDecomp PROC USES ESI EDI EBX, \ + ops:PTRBYTE, comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row:DWORD + LOCAL DiffBufPtrs:DWORD + + LOCAL nfpk_back_right: DWORD + LOCAL wcnt:DWORD + + LOG_LABEL "StartPkDecomp" + +.data +nfpk_OpTbl label dword + dword offset nf0 ; Prev Same (0) + dword offset nf1 ; No change (and copied to screen) (0) + dword offset nf2 ; Near shift from older part of current buf (1) + dword offset nf3 ; Near shift from newer part of current buf (1) + dword offset nf4 ; Near shift from previous buffer (1) + dword offset nf5 ; Far shift from previous buffer (2) + dword offset nf6 ; Far shift from current buffer (2) + ; [Or if COMPOPS, run of no changes (0)] + dword offset nf7 ; 8x8x1 (10 bytes) or low 4x4x1 (4 bytes) + dword offset nf8 ; 2x2 4x4x1 (16 bytes) or 2x1 4x8x1 (12 bytes) or 1x2 8x4x1 (12 bytes) + dword offset nf9 ; 8x8x2 (20 bytes) or low 4x4x2 (8 bytes) or + ; low 4x8x2 (12 bytes) or low 8x4x2 (12 bytes) + dword offset nf10 ; 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + dword offset nf11 ; 8x8x8 (64 bytes) + dword offset nf12 ; low 4x4x8 (16 bytes) + dword offset nf13 ; 2x2 4x4x0 (ie 2x2x8) (4 bytes) + dword offset nf14 ; 8x8x0 (1 byte) + dword offset nf15 ; mix 8x8x0 (2 bytes) +.code + +ifdef SYMANTEC + mov ebx, ds ; Allow DS to access code + mov ecx, 0 + mov ax, 3505h + int 21h +endif + + NF_DECOMP_INIT 0 + + mov eax, nf_back_right + sub eax, SWIDTH + mov nfpk_back_right, eax + + mov esi, comp + mov edi, tbuf +nf_StartRow: + mov eax, w + shr eax, 1 + mov wcnt,eax + ALIGN 4 +nf_NextPair: + dec wcnt + js nf_NextRow + mov ebx, ops + mov al, [ebx] + inc ebx + mov ops, ebx + + xor ebx, ebx + mov bl, al + shr bl, 4 + and eax, 0Fh + push offset nf_NextPair + push nfpk_OpTbl[ebx*4] + jmp nfpk_OpTbl[eax*4] + +nf_NextRow: + add edi, new_row + dec h + jnz nf_StartRow + LOG_LABEL "EndPkDecomp" + +ifdef SYMANTEC + mov ebx, ds ; Disable DS from accessing code + mov ecx, offset DGROUP:_data_bottom[-1] + mov ax, 3505h + int 21h +endif + ret + +;---------------------------------------- + ALIGN 4 +if INTERP eq 0 + +nf0: ; No change from previous buffer + mov eax, DiffBufPtrs + jmp nf_shift + +elseif INTERP eq 3 +nf0: ; Interpolated (1 byte) + push ebp + + mov ebp, nf_width + sub edi, ebp ; Get four corner colors + mov bl, [edi-1] ; into bl,bh,cl,ch + mov bh, [edi+7] + mov cl, [edi+ebp*8-1] + mov ch, [esi] + inc esi + add edi, ebp + + push esi + mov esi, blend_tbl + + nfPkInterp1 bl,bh + add edi, ebp + push ebx + push ecx + xor eax, eax + mov al, bl + mov ah, cl + mov edx, [esi+eax*4] + mov al, bh + mov ah, ch + mov ecx, [esi+eax*4] + mov ebx, edx + nfPkInterp2 bh,ch + lea edi, [edi+ebp*2] + ror ebx, 16 + ror ecx, 16 + nfPkInterp2 bl,cl + lea edi, [edi+ebp*2] + nfPkInterp2 bh,ch + lea edi, [edi+ebp*2] + pop ecx + pop ebx + nfPkInterp1 cl,ch + + pop esi + pop ebp + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +elseif INTERP eq 2 + +nf0: ; Interpolated (1 byte) + mov edx, nf_width + sub edi, edx ; Get four corner colors + sub edi, edx ;xxx + mov bl, [edi-1] ; into bl,bh,cl,ch + mov bh, [edi+7] + mov cl, [edi+edx*8-1] + mov ch, [esi] + inc esi + add edi, edx ;xxx + add edi, edx + +; Get four luminances into eax, ebx, ebp, ecx +; Use edx for temp, esi for closest luminance, edi for closest pair + push ebx + push ecx + push esi + push edi + push ebp + + xor edx, edx + mov dl, bl + mov eax, lum_tbl[edx*4] + mov dl, bh + mov ebx, lum_tbl[edx*4] + mov dl, cl + mov ebp, lum_tbl[edx*4] + mov dl, ch + mov ecx, lum_tbl[edx*4] + + mov edx, eax + sub edx, ebx + jns nf0a + neg edx +nf0a: mov esi, edx + mov edi, 0 ; Vert + + mov edx, eax + sub edx, ebp + jns nf0b + neg edx +nf0b: cmp edx, esi + ja nf0c + mov esi, edx + mov edi, 1 ; Horiz + +nf0c: mov edx, eax + sub edx, ecx + jns nf0d + neg edx +nf0d: cmp edx, esi + ja nf0e + mov esi, edx + mov edi, 2 ; \ Diag + +nf0e: mov edx, ebx + sub edx, ebp + jns nf0f + neg edx +nf0f: cmp edx, esi + ja nf0g + mov esi, edx ; / RDiag + mov edi, 3 + +nf0g: + mov edx, ebx + sub edx, ecx + jns nf0h + neg edx +nf0h: cmp edx, esi + ja nf0i + mov esi, edx + mov edi, 1 ; Horiz + +nf0i: mov edx, ebp + sub edx, ecx + jns nf0j + neg edx +nf0j: cmp edx, esi + ja nf0k + mov edi, 0 + +nf0k: mov eax, edi + pop ebp + pop edi + pop esi + pop ecx + pop ebx + mov edx, nf_width + + cmp eax, 2 + jae nfdiag + or eax, eax + jz nf0_v + jmp nf0_h + +nfdiag: jz nf0_d + jmp nf0_r + +if 1 ; Newer versions of Vertical and Horizontal blend that use 0%,25%,50%,75%,100% instead of just 0%,50%,100% + +; Vertical blend +; 0 1 +; 01010101 1 +; 00121013 2 +; 02010311 3 +; 20203131 4 +; 02021313 5 +; 23202331 6 +; 20332123 7 +;2 22233233 8 +; + +nf0_v: + ; 3412 (low to high) + ;------ + mov al, bl ; 0101 (1) + mov ah, bh + shl eax, 16 + mov al, bl + mov ah, bh + mov [edi], eax + mov [edi+4], eax ; 0101 + add edi, edx + + mov al, bh ; 0012 (2) + mov ah, cl + shl eax, 8 + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 1013 + mov ah, ch + shl eax, 16 + mov al, bh + mov ah, bl + mov [edi+4], eax + add edi, edx + + mov al, bl ; 0201 (3) + mov ah, bh + shl eax, 16 + mov al, bl + mov ah, cl + mov [edi], eax + mov al, bh ; 0311 + mov ah, bh + shl eax, 16 + mov al, bl + mov ah, ch + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2020 (4), 0202 (5) + mov ah, bl + shl eax, 16 + mov al, cl + mov ah, bl + mov [edi], eax + ror eax, 8 + mov [edi+edx], eax + mov al, ch ; 3131, 1313 + mov ah, bh + shl eax, 16 + mov al, ch + mov ah, bh + mov [edi+4], eax + ror eax, 8 + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + + mov al, cl ; 2320 (6) + mov ah, bl + shl eax, 16 + mov al, cl + mov ah, ch + mov [edi], eax + mov al, ch ; 2331 + mov ah, bh + shl eax, 16 + mov al, cl + mov ah, ch + mov [edi+4], eax + add edi, edx + + rol eax, 8 ; 2033 (7) + mov al, cl + mov ah, bl + mov [edi], eax + mov al, cl ; 2123 + mov ah, ch + shl eax, 16 + mov al, cl + mov ah, bh + mov [edi+4], eax + add edi, edx + + mov ah, cl ; 2223 (8) + mov [edi], eax + mov al, ch ; 3233 + mov ah, ch + shl eax, 16 + mov al, ch + mov ah, cl + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +; Horizontal blend +; 0 1 +; 00010111 1 +; 20101301 2 +; 02010131 3 +; 21201033 4 +; 02032113 5 +; 20323321 6 +; 02232313 7 +;2 23223233 8 + +nf0_h: + ; 3412 (low to high) + ;------ + mov al, bl ; 0001 (1) + mov ah, bh + shl eax, 16 + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 0111 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + ror eax, 8 ; 2010 (2) + mov al, cl + mov ah, bl + mov [edi], eax + rol eax, 8 + mov al, bh ; 1301 + mov ah, ch + mov [edi+4], eax + add edi, edx + + mov al, bl ; 0201 (3) + mov ah, cl + mov [edi], eax + mov al, ch ; 0131 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2120 (4) + mov ah, bl + shl eax, 16 + mov al, cl + mov ah, bh + mov [edi], eax + mov al, ch ; 1033 + mov ah, ch + shl eax, 16 + mov al, bh + mov ah, bl + mov [edi+4], eax + add edi, edx + + rol eax, 8 ; 0203 (5) + mov al, bl + mov ah, cl + mov [edi], eax + mov al, bh ; 2113 + mov ah, ch + shl eax, 16 + mov al, cl + mov ah, bh + mov [edi+4], eax + add edi, edx + + ror eax, 8 ; 2032 (6) + mov al, cl + mov ah, bl + mov [edi], eax + mov al, bh ; 3321 + mov ah, ch + ror eax, 8 + mov [edi+4], eax + add edi, edx + + mov al, cl ; 0223 (7) + mov ah, ch + shl eax, 16 + mov al, bl + mov ah, cl + mov [edi], eax + mov al, bh ; 2313 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + + shl eax, 16 ; 2322 (8) + mov al, cl + mov ah, ch + mov [edi], eax + mov al, ch ; 3233 + mov ah, ch + shl eax, 16 + mov al, ch + mov ah, cl + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +else + +; Vertical blend +;0 1 +; 00101011 1 +; 00010111 2 +; 20203131 3 +; 02021313 4 +; 20203131 5 +; 02021313 6 +; 22323233 7 +;2 22232333 8 +; +nf0_v: + push ebp + ; 3412 (low to high) + ;------ + mov al, bh ; 0010 (1) + mov ah, bl + shl eax, 16 + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 1011 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + rol eax, 8 ; 0001 (2) + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 0111 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2020 (3+5) + mov ah, bl + shl eax, 16 + mov al, cl + mov ah, bl + mov ebp, eax + mov [edi], eax + mov [edi+edx*2], eax + mov al, ch ; 3131 + mov ah, bh + shl eax, 16 + mov al, ch + mov ah, bh + mov [edi+4], eax + mov [edi+edx*2+4], eax + add edi, edx + + rol ebp, 8 ; 0202 (4+6) + mov [edi], ebp + mov [edi+edx*2], ebp + rol eax, 8 ; 1313 + mov [edi+4], eax + mov [edi+edx*2+4], eax + add edi, edx + lea edi, [edi+edx*2] + + mov al, ch ; 2232 (7) + mov ah, cl + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 3233 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2223 (8) + mov ah, ch + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 2333 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + + pop ebp + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +; Horizontal blend +;0 1 +; 00101011 1 +; 00010111 2 +; 20101031 3 +; 02010113 4 +; 20323231 5 +; 02232313 6 +; 22323233 7 +;2 22232333 8 +; +nf0_h: + ; 3412 (low to high) + ;------ + mov al, bh ; 0010 (1) + mov ah, bl + shl eax, 16 + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 1011 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + rol eax, 8 ; 0001 (2) + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 0111 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + ror eax, 8 ; 2010 (3) + mov al, cl + mov ah, bl + mov [edi], eax + mov al, ch ; 1031 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, bl ; 0201 (4) + mov ah, bh + rol eax, 16 + mov al, bl + mov ah, cl + mov [edi], eax + mov al, bh ; 0113 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, ch ; 2032 (5) + mov ah, cl + shl eax, 16 + mov al, cl + mov ah, bl + mov [edi], eax + mov al, ch ; 3231 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + rol eax, 8 ; 0223 (6) + mov al, bl + mov ah, cl + mov [edi], eax + mov al, bh ; 2313 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, ch ; 2232 (7) + mov ah, cl + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 3233 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2223 (8) + mov ah, ch + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 2333 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn +endif + +; \ Diagonal blend +;0 1 +; 00010101 1 +; 00001313 2 +; 20303101 3 +; 02030313 4 +; 23203031 5 +; 02020333 6 +; 23232333 7 +;2 22023233 8 +; +nf0_d: + ; 3412 (low to high) + ;------ + mov al, bl ; 0001 (1) + mov ah, bh + shl eax, 16 + mov al, bl + mov ah, bl + mov [edi], eax + mov ah, bh ; 0101 + mov [edi+4], eax + add edi, edx + + mov ah, bl ; 0000 (2) + rol eax, 16 + mov ah, bl + mov [edi], eax + mov al, bh ; 1313 + mov ah, ch + shl eax, 16 + mov al, bh + mov ah, ch + mov [edi+4], eax + add edi, edx + + mov al, ch ; 2030 (3) + mov ah, bl + shl eax, 16 + mov al, cl + mov ah, bl + mov [edi], eax + mov al, bl ; 3101 + mov ah, bh + shl eax, 16 + mov al, ch + mov ah, bh + mov [edi+4], eax + add edi, edx + + mov al, bl ; 0203 (4) + mov ah, ch + shl eax, 16 + mov al, bl + mov ah, cl + mov [edi], eax + mov al, bh ; 0313 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2320 (5) + mov ah, bl + shl eax, 16 + mov al, cl + mov ah, ch + mov [edi], eax + mov al, ch ; 3031 + mov ah, bh + shl eax, 16 + mov al, ch + mov ah, bl + mov [edi+4], eax + add edi, edx + + mov al, bl ; 0202 (6) + mov ah, cl + shl eax, 16 + mov al, bl + mov ah, cl + mov [edi], eax + mov ah, ch ; 0333 + shl eax, 16 + mov al, ch + mov ah, ch + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2323 (7) + rol eax, 16 + mov al, cl + mov [edi], eax + mov al, ch ; 2333 + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, bl ; 2202 (8) + mov ah, cl + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 3233 + mov ah, ch + shl eax, 16 + mov al, ch + mov ah, cl + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + + +; / RDiagonal blend +;0 1 +; 01010111 1 +; 20201111 2 +; 01021313 3 +; 20212131 4 +; 02121323 5 +; 22213131 6 +; 22232323 7 +;2 22323133 8 +; +nf0_r: + ; 3412 (low to high) + ;------ + mov al, bl ; 0101 (1) + mov ah, bh + shl eax, 16 + mov al, bl + mov ah, bh + mov [edi], eax + mov al, bh ; 0111 + rol eax, 16 + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2020 (2) + mov ah, bl + shl eax, 16 + mov al, cl + mov ah, bl + mov [edi], eax + mov al, bh ; 1111 + mov ah, bh + shl eax, 16 + mov al, bh + mov ah, bh + mov [edi+4], eax + add edi, edx + + mov al, bl ; 0102 (3) + mov ah, cl + rol eax, 16 + mov al, bl + mov [edi], eax + mov al, bh ; 1313 + mov ah, ch + shl eax, 16 + mov al, bh + mov ah, ch + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2021 (4) + mov ah, bh + shl eax, 16 + mov al, cl + mov ah, bl + mov [edi], eax + mov al, ch ; 2131 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + + ror eax, 8 ; 0212 (5) + mov al, bl + mov ah, cl + mov [edi], eax + mov al, cl ; 1323 + mov ah, ch + shl eax, 16 + mov al, bh + mov ah, ch + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2221 (6) + mov ah, bh + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 3131 + mov ah, bh + shl eax, 16 + mov al, ch + mov ah, bh + mov [edi+4], eax + add edi, edx + + mov al, cl ; 2223 (7) + mov ah, ch + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov ah, ch ; 2323 + rol eax, 16 + mov [edi+4], eax + add edi, edx + + rol eax, 8 ; 2232 (8) + mov al, cl + mov [edi], eax + mov al, ch ; 3133 + mov ah, ch + shl eax, 16 + mov al, ch + mov ah, bh + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +elseif INTERP eq 1 + +nf0: ; Interpolated (1 byte) + mov edx, nf_width + sub edi, edx ; Get four corner colors + sub edi, edx ;xxx + mov bl, [edi-1] ; into bl,bh,cl,ch + mov bh, [edi+7] + mov cl, [edi+edx*8-1] + mov ch, [esi] + inc esi + add edi, edx ;xxx + add edi, edx + +; Pattern for interpolating four corners: +;0 1 +; 00101011 1 +; 00010111 2 +; 20023113 3 +; 02101031 4 +; 20323213 5 +; 02201331 6 +; 22232333 7 +;2 22323233 8 + ; 3412 (low to high) + ;------ +nf0_1: + mov al, bh ; 0010 + mov ah, bl + shl eax, 16 + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 1011 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + +nf0_2: rol eax, 8 ; 0001 + mov al, bl + mov ah, bl + mov [edi], eax + mov al, bh ; 0111 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + +nf0_3: mov al, bl ; 2002 + mov ah, cl + shl eax, 16 + mov al, cl + mov ah, bl + mov [edi], eax + mov al, bh ; 3113 + mov ah, ch + shl eax, 16 + mov al, ch + mov ah, bh + mov [edi+4], eax + add edi, edx + +nf0_4: mov al, bh ; 0210 + mov ah, bl + shl eax, 16 + mov al, bl + mov ah, cl + mov [edi], eax + mov al, ch ; 1031 + mov ah, bh + rol eax, 16 + mov [edi+4], eax + add edi, edx + +nf0_5: mov al, cl ; 2032 + mov ah, ch + shl eax, 16 + mov al, cl + mov ah, bl + mov [edi], eax + mov al, bh ; 3213 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + +nf0_6: mov al, cl ; 0220 + mov ah, bl + shl eax, 16 + mov al, bl + mov ah, cl + mov [edi], eax + mov al, ch ; 1331 + mov ah, bh + shl eax, 16 + mov al, bh + mov ah, ch + mov [edi+4], eax + add edi, edx + +nf0_7: mov al, cl ; 2223 + mov ah, ch + shl eax, 16 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 2333 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + add edi, edx + +nf0_8: ror eax, 8 ; 2232 + mov al, cl + mov ah, cl + mov [edi], eax + mov al, ch ; 3233 + mov ah, ch + rol eax, 16 + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +endif + +;---------------------------------------- + ALIGN 4 +nf1: ; No change (and copied to screen) + add edi, SWIDTH + retn + +;---------------------------------------- + ALIGN 4 +nf2: ; Near shift from older part of current buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP2[eax*2] +nf_xyc_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24 + add eax, nfpk_ShiftY[ebx*4] + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf3: ; Near shift from newer part of current buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP2[eax*2] + neg al + neg ah + jmp nf_xyc_shift + +;---------------------------------------- + ALIGN 4 +nf4: ; Near shift from previous buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP1[eax*2] + jmp nf_xyp_shift + +;---------------------------------------- + ALIGN 4 +nf5: ; Far shift from previous buffer + mov ax, [esi] + add esi, 2 +nf_xyp_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24 + add eax, nfpk_ShiftY[ebx*4] + add eax, DiffBufPtrs + jmp nf_shift + +;---------------------------------------- + ALIGN 4 + +if COMPOPS + +nf6: ; Run of no changes (must only appear in first nibble opcodes) + ; Next nibble k specifies 2k+4 squares with no changes + add esp, 4 ; Next nibble is not an opcode + add ebx, 2 ; (minimum of 4 squares) + ALIGN 4 +nf6a: add edi, SWIDTH*2 ; Advance over two squares + dec ebx + jz nf6z ; Last pair of squares + dec wcnt ; Same row? + jns nf6a ; Yes + add edi, new_row ; Advance to next row + dec h ; Decrement row count (should never become zero here) + mov eax, w ; Reset wcnt + shr eax ,1 + dec eax + mov wcnt, eax + jmp nf6a + +nf6z: retn + +else + +nf6: ; Far shift from current buffer + mov ax, [esi] + add esi, 2 + jmp nf_xyc_shift + +endif + +;---------------------------------------- + ALIGN 4 +nf_shift: +if 0 ;debug + mov eax, 0 + mov ebx, eax + jmp nf_solid +endif + mov ebx, esi ; save esi + lea esi, [edi+eax] + mov edx, nf_width + + REPEAT 7 + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + add esi, edx + add edi, edx + ENDM + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + mov esi, ebx ; restore esi + retn + +;---------------------------------------- + ALIGN 4 +nf7: ; 8x8x1 (10 bytes) + + mov ax, [esi] + cmp al, ah + ja nf23 + +if 0 ;debug + add esi, 10 + mov eax, 0fefefefeH + mov ebx, eax + jmp nf_solid +endif + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf7_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_11-nf7_11)], bl + mov [edx+(nf7_12-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_13-nf7_11)], bl + mov [edx+(nf7_14-nf7_11)], bh + + mov al, [esi+3] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_21-nf7_11)], bl + mov [edx+(nf7_22-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_23-nf7_11)], bl + mov [edx+(nf7_24-nf7_11)], bh + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_31-nf7_11)], bl + mov [edx+(nf7_32-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_33-nf7_11)], bl + mov [edx+(nf7_34-nf7_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_41-nf7_11)], bl + mov [edx+(nf7_42-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_43-nf7_11)], bl + mov [edx+(nf7_44-nf7_11)], bh + + lea edx, [edx+(nf7_51-nf7_11)] + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_51-nf7_51)], bl + mov [edx+(nf7_52-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_53-nf7_51)], bl + mov [edx+(nf7_54-nf7_51)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_61-nf7_51)], bl + mov [edx+(nf7_62-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_63-nf7_51)], bl + mov [edx+(nf7_64-nf7_51)], bh + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_71-nf7_51)], bl + mov [edx+(nf7_72-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_73-nf7_51)], bl + mov [edx+(nf7_74-nf7_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_81-nf7_51)], bl + mov [edx+(nf7_82-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_83-nf7_51)], bl + mov [edx+(nf7_84-nf7_51)], bh + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi,nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + jmp nf7_0 ; flush prefetch + ALIGN 4 +nf7_0: +nf7_11: mov ax, bx + shl eax, 16 +nf7_12: mov ax, bx + mov [edi], eax +nf7_13: mov ax, bx + shl eax, 16 +nf7_14: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_21: mov ax, bx + shl eax, 16 +nf7_22: mov ax, bx + mov [edi], eax +nf7_23: mov ax, bx + shl eax, 16 +nf7_24: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_31: mov ax, bx + shl eax, 16 +nf7_32: mov ax, bx + mov [edi], eax +nf7_33: mov ax, bx + shl eax, 16 +nf7_34: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_41: mov ax, bx + shl eax, 16 +nf7_42: mov ax, bx + mov [edi], eax +nf7_43: mov ax, bx + shl eax, 16 +nf7_44: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_51: mov ax, bx + shl eax, 16 +nf7_52: mov ax, bx + mov [edi], eax +nf7_53: mov ax, bx + shl eax, 16 +nf7_54: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_61: mov ax, bx + shl eax, 16 +nf7_62: mov ax, bx + mov [edi], eax +nf7_63: mov ax, bx + shl eax, 16 +nf7_64: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_71: mov ax, bx + shl eax, 16 +nf7_72: mov ax, bx + mov [edi], eax +nf7_73: mov ax, bx + shl eax, 16 +nf7_74: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_81: mov ax, bx + shl eax, 16 +nf7_82: mov ax, bx + mov [edi], eax +nf7_83: mov ax, bx + shl eax, 16 +nf7_84: mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 10 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf7+16 +nf23: ; low 4x4x1 (4 bytes) + + xor eax, eax + lea ecx, nfpk_mov4l + lea edx, byte ptr ds:nf23_11+2 + + mov al, [esi+2] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_11-nf23_11)], bl + mov [edx+(nf23_12-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_13-nf23_11)], bl + mov [edx+(nf23_14-nf23_11)], bh + + mov al, [esi+2] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_31-nf23_11)], bl + mov [edx+(nf23_32-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_33-nf23_11)], bl + mov [edx+(nf23_34-nf23_11)], bh + + + mov al, [esi+3] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_51-nf23_11)], bl + mov [edx+(nf23_52-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_53-nf23_11)], bl + mov [edx+(nf23_54-nf23_11)], bh + + mov al, [esi+3] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_71-nf23_11)], bl + mov [edx+(nf23_72-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_73-nf23_11)], bl + mov [edx+(nf23_74-nf23_11)], bh + + mov edx, nf_width + + ; load bx,cx with 00,11 color combinations + mov bx, [esi] + mov cl, bh + mov bh, bl + mov ch, cl + + jmp nf23_0 ; flush prefetch + ALIGN 4 +nf23_0: + +nf23_11:mov ax, bx + shl eax, 16 +nf23_12:mov ax, bx + mov [edi], eax + mov [edi+edx], eax + +nf23_13:mov ax, bx + shl eax, 16 +nf23_14:mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf23_31:mov ax, bx + shl eax, 16 +nf23_32:mov ax, bx + mov [edi], eax + mov [edi+edx], eax + +nf23_33:mov ax, bx + shl eax, 16 +nf23_34:mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf23_51:mov ax, bx + shl eax, 16 +nf23_52:mov ax, bx + mov [edi], eax + mov [edi+edx], eax + +nf23_53:mov ax, bx + shl eax, 16 +nf23_54:mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf23_71:mov ax, bx + shl eax, 16 +nf23_72:mov ax, bx + mov [edi], eax + mov [edi+edx], eax + +nf23_73:mov ax, bx + shl eax, 16 +nf23_74:mov ax, bx + mov [edi+4], eax + add edi, edx + mov [edi+4], eax + + sub edi, nfpk_back_right + add esi, 4 + retn + +;---------------------------------------- + ALIGN 4 +nf8: ; 2x2 4x4x1 (16 bytes) + + mov ax, [esi] + cmp al, ah + ja nf24 + + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf8_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_11-nf8_11)], bl + mov [edx+(nf8_12-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_13-nf8_11)], bl + mov [edx+(nf8_14-nf8_11)], bh + + mov al, [esi+3] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_21-nf8_11)], bl + mov [edx+(nf8_22-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_23-nf8_11)], bl + mov [edx+(nf8_24-nf8_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_31-nf8_11)], bl + mov [edx+(nf8_32-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_33-nf8_11)], bl + mov [edx+(nf8_34-nf8_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_41-nf8_11)], bl + mov [edx+(nf8_42-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_43-nf8_11)], bl + mov [edx+(nf8_44-nf8_11)], bh + + add edx, nf8_51-nf8_11 + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_51-nf8_51)], bl + mov [edx+(nf8_52-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_53-nf8_51)], bl + mov [edx+(nf8_54-nf8_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_61-nf8_51)], bl + mov [edx+(nf8_62-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_63-nf8_51)], bl + mov [edx+(nf8_64-nf8_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_71-nf8_51)], bl + mov [edx+(nf8_72-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_73-nf8_51)], bl + mov [edx+(nf8_74-nf8_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_81-nf8_51)], bl + mov [edx+(nf8_82-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_83-nf8_51)], bl + mov [edx+(nf8_84-nf8_51)], bh + + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf8_0 ; flush prefetch + ALIGN 4 +nf8_0: +nf8_11: mov ax, bx + shl eax, 16 +nf8_12: mov ax, bx + mov [edi], eax + add edi, esi +nf8_13: mov ax, bx + shl eax, 16 +nf8_14: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_21: mov ax, bx + shl eax, 16 +nf8_22: mov ax, bx + mov [edi], eax + add edi, esi +nf8_23: mov ax, bx + shl eax, 16 +nf8_24: mov ax, bx + mov [edi], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+4] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_31: mov ax, bx + shl eax, 16 +nf8_32: mov ax, bx + mov [edi], eax + add edi, esi +nf8_33: mov ax, bx + shl eax, 16 +nf8_34: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_41: mov ax, bx + shl eax, 16 +nf8_42: mov ax, bx + mov [edi], eax + add edi, esi +nf8_43: mov ax, bx + shl eax, 16 +nf8_44: mov ax, bx + mov [edi], eax + add edi, esi + + lea eax, [esi*8-4] + sub edi, eax + + mov eax, [esp] + mov cx, [eax+8] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_51: mov ax, bx + shl eax, 16 +nf8_52: mov ax, bx + mov [edi], eax + add edi, esi +nf8_53: mov ax, bx + shl eax, 16 +nf8_54: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_61: mov ax, bx + shl eax, 16 +nf8_62: mov ax, bx + mov [edi], eax + add edi, esi +nf8_63: mov ax, bx + shl eax, 16 +nf8_64: mov ax, bx + mov [edi], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+12] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_71: mov ax, bx + shl eax, 16 +nf8_72: mov ax, bx + mov [edi], eax + add edi, esi +nf8_73: mov ax, bx + shl eax, 16 +nf8_74: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_81: mov ax, bx + shl eax, 16 +nf8_82: mov ax, bx + mov [edi], eax + add edi, esi +nf8_83: mov ax, bx + shl eax, 16 +nf8_84: mov ax, bx + mov [edi], eax + + pop esi + pop ebp + add esi, 16 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+16 +nf24: ; 2x1 4x8x1 (12 bytes) + + mov ax, [esi+6] + cmp al, ah + ja nf40 + + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf24_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_11-nf24_11)], bl + mov [edx+(nf24_12-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_13-nf24_11)], bl + mov [edx+(nf24_14-nf24_11)], bh + + mov al, [esi+3] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_21-nf24_11)], bl + mov [edx+(nf24_22-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_23-nf24_11)], bl + mov [edx+(nf24_24-nf24_11)], bh + + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_31-nf24_11)], bl + mov [edx+(nf24_32-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_33-nf24_11)], bl + mov [edx+(nf24_34-nf24_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_41-nf24_11)], bl + mov [edx+(nf24_42-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_43-nf24_11)], bl + mov [edx+(nf24_44-nf24_11)], bh + + add edx, nf24_51-nf24_11 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_51-nf24_51)], bl + mov [edx+(nf24_52-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_53-nf24_51)], bl + mov [edx+(nf24_54-nf24_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_61-nf24_51)], bl + mov [edx+(nf24_62-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_63-nf24_51)], bl + mov [edx+(nf24_64-nf24_51)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_71-nf24_51)], bl + mov [edx+(nf24_72-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_73-nf24_51)], bl + mov [edx+(nf24_74-nf24_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_81-nf24_51)], bl + mov [edx+(nf24_82-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_83-nf24_51)], bl + mov [edx+(nf24_84-nf24_51)], bh + + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf24_0 ; flush prefetch + ALIGN 4 +nf24_0: +nf24_11:mov ax, bx + shl eax, 16 +nf24_12:mov ax, bx + mov [edi], eax + add edi, esi +nf24_13:mov ax, bx + shl eax, 16 +nf24_14:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_21:mov ax, bx + shl eax, 16 +nf24_22:mov ax, bx + mov [edi], eax + add edi, esi +nf24_23:mov ax, bx + shl eax, 16 +nf24_24:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_31:mov ax, bx + shl eax, 16 +nf24_32:mov ax, bx + mov [edi], eax + add edi, esi +nf24_33:mov ax, bx + shl eax, 16 +nf24_34:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_41:mov ax, bx + shl eax, 16 +nf24_42:mov ax, bx + mov [edi], eax + add edi, esi +nf24_43:mov ax, bx + shl eax, 16 +nf24_44:mov ax, bx + mov [edi], eax + add edi, esi + + lea eax, [esi*8-4] + sub edi, eax + + mov eax, [esp] + mov cx, [eax+6] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf24_51:mov ax, bx + shl eax, 16 +nf24_52:mov ax, bx + mov [edi], eax + add edi, esi +nf24_53:mov ax, bx + shl eax, 16 +nf24_54:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_61:mov ax, bx + shl eax, 16 +nf24_62:mov ax, bx + mov [edi], eax + add edi, esi +nf24_63:mov ax, bx + shl eax, 16 +nf24_64:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_71:mov ax, bx + shl eax, 16 +nf24_72:mov ax, bx + mov [edi], eax + add edi, esi +nf24_73:mov ax, bx + shl eax, 16 +nf24_74:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_81:mov ax, bx + shl eax, 16 +nf24_82:mov ax, bx + mov [edi], eax + add edi, esi +nf24_83:mov ax, bx + shl eax, 16 +nf24_84:mov ax, bx + mov [edi], eax + + pop esi + pop ebp + add esi, 12 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+32 +nf40: ; 1x2 8x4x1 (12 bytes) + + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf40_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_11-nf40_11)], bl + mov [edx+(nf40_12-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_13-nf40_11)], bl + mov [edx+(nf40_14-nf40_11)], bh + + mov al, [esi+3] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_21-nf40_11)], bl + mov [edx+(nf40_22-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_23-nf40_11)], bl + mov [edx+(nf40_24-nf40_11)], bh + + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_31-nf40_11)], bl + mov [edx+(nf40_32-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_33-nf40_11)], bl + mov [edx+(nf40_34-nf40_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_41-nf40_11)], bl + mov [edx+(nf40_42-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_43-nf40_11)], bl + mov [edx+(nf40_44-nf40_11)], bh + + add edx, nf40_51-nf40_11 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_51-nf40_51)], bl + mov [edx+(nf40_52-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_53-nf40_51)], bl + mov [edx+(nf40_54-nf40_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_61-nf40_51)], bl + mov [edx+(nf40_62-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_63-nf40_51)], bl + mov [edx+(nf40_64-nf40_51)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_71-nf40_51)], bl + mov [edx+(nf40_72-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_73-nf40_51)], bl + mov [edx+(nf40_74-nf40_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_81-nf40_51)], bl + mov [edx+(nf40_82-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_83-nf40_51)], bl + mov [edx+(nf40_84-nf40_51)], bh + + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf40_0 ; flush prefetch + ALIGN 4 +nf40_0: +nf40_11:mov ax, bx + shl eax, 16 +nf40_12:mov ax, bx + mov [edi], eax +nf40_13:mov ax, bx + shl eax, 16 +nf40_14:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_21:mov ax, bx + shl eax, 16 +nf40_22:mov ax, bx + mov [edi], eax +nf40_23:mov ax, bx + shl eax, 16 +nf40_24:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_31:mov ax, bx + shl eax, 16 +nf40_32:mov ax, bx + mov [edi], eax +nf40_33:mov ax, bx + shl eax, 16 +nf40_34:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_41:mov ax, bx + shl eax, 16 +nf40_42:mov ax, bx + mov [edi], eax +nf40_43:mov ax, bx + shl eax, 16 +nf40_44:mov ax, bx + mov [edi+4], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+6] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf40_51:mov ax, bx + shl eax, 16 +nf40_52:mov ax, bx + mov [edi], eax +nf40_53:mov ax, bx + shl eax, 16 +nf40_54:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_61:mov ax, bx + shl eax, 16 +nf40_62:mov ax, bx + mov [edi], eax +nf40_63:mov ax, bx + shl eax, 16 +nf40_64:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_71:mov ax, bx + shl eax, 16 +nf40_72:mov ax, bx + mov [edi], eax +nf40_73:mov ax, bx + shl eax, 16 +nf40_74:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_81:mov ax, bx + shl eax, 16 +nf40_82:mov ax, bx + mov [edi], eax +nf40_83:mov ax, bx + shl eax, 16 +nf40_84:mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +nf9: ; 8x8x2 (20 bytes) + + mov eax, [esi] + cmp al, ah + ja nf41 + + shr eax, 16 + cmp al, ah + ja nf25 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf9_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_11-nf9_11)], bl + mov [edx+(nf9_12-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_13-nf9_11)], bl + mov [edx+(nf9_14-nf9_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_15-nf9_11)], bl + mov [edx+(nf9_16-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_17-nf9_11)], bl + mov [edx+(nf9_18-nf9_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_21-nf9_11)], bl + mov [edx+(nf9_22-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_23-nf9_11)], bl + mov [edx+(nf9_24-nf9_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_25-nf9_11)], bl + mov [edx+(nf9_26-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_27-nf9_11)], bl + mov [edx+(nf9_28-nf9_11)], bh + + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_31-nf9_11)], bl + mov [edx+(nf9_32-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_33-nf9_11)], bl + mov [edx+(nf9_34-nf9_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_35-nf9_11)], bl + mov [edx+(nf9_36-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_37-nf9_11)], bl + mov [edx+(nf9_38-nf9_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_41-nf9_11)], bl + mov [edx+(nf9_42-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_43-nf9_11)], bl + mov [edx+(nf9_44-nf9_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_45-nf9_11)], bl + mov [edx+(nf9_46-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_47-nf9_11)], bl + mov [edx+(nf9_48-nf9_11)], bh + + + lea edx, [edx+(nf9_51-nf9_11)] + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_51-nf9_51)], bl + mov [edx+(nf9_52-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_53-nf9_51)], bl + mov [edx+(nf9_54-nf9_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_55-nf9_51)], bl + mov [edx+(nf9_56-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_57-nf9_51)], bl + mov [edx+(nf9_58-nf9_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_61-nf9_51)], bl + mov [edx+(nf9_62-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_63-nf9_51)], bl + mov [edx+(nf9_64-nf9_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_65-nf9_51)], bl + mov [edx+(nf9_66-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_67-nf9_51)], bl + mov [edx+(nf9_68-nf9_51)], bh + + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_71-nf9_51)], bl + mov [edx+(nf9_72-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_73-nf9_51)], bl + mov [edx+(nf9_74-nf9_51)], bh + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_75-nf9_51)], bl + mov [edx+(nf9_76-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_77-nf9_51)], bl + mov [edx+(nf9_78-nf9_51)], bh + + + mov al, [esi+18] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_81-nf9_51)], bl + mov [edx+(nf9_82-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_83-nf9_51)], bl + mov [edx+(nf9_84-nf9_51)], bh + + mov al, [esi+19] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_85-nf9_51)], bl + mov [edx+(nf9_86-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_87-nf9_51)], bl + mov [edx+(nf9_88-nf9_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf9_0 ; flush prefetch + ALIGN 4 +nf9_0: +nf9_11: mov al, bl +nf9_12: mov ah, bl + shl eax, 16 +nf9_13: mov al, bl +nf9_14: mov ah, bl + mov [edi], eax + +nf9_15: mov al, bl +nf9_16: mov ah, bl + shl eax, 16 +nf9_17: mov al, bl +nf9_18: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_21: mov al, bl +nf9_22: mov ah, bl + shl eax, 16 +nf9_23: mov al, bl +nf9_24: mov ah, bl + mov [edi], eax + +nf9_25: mov al, bl +nf9_26: mov ah, bl + shl eax, 16 +nf9_27: mov al, bl +nf9_28: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_31: mov al, bl +nf9_32: mov ah, bl + shl eax, 16 +nf9_33: mov al, bl +nf9_34: mov ah, bl + mov [edi], eax + +nf9_35: mov al, bl +nf9_36: mov ah, bl + shl eax, 16 +nf9_37: mov al, bl +nf9_38: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_41: mov al, bl +nf9_42: mov ah, bl + shl eax, 16 +nf9_43: mov al, bl +nf9_44: mov ah, bl + mov [edi], eax + +nf9_45: mov al, bl +nf9_46: mov ah, bl + shl eax, 16 +nf9_47: mov al, bl +nf9_48: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_51: mov al, bl +nf9_52: mov ah, bl + shl eax, 16 +nf9_53: mov al, bl +nf9_54: mov ah, bl + mov [edi], eax + +nf9_55: mov al, bl +nf9_56: mov ah, bl + shl eax, 16 +nf9_57: mov al, bl +nf9_58: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_61: mov al, bl +nf9_62: mov ah, bl + shl eax, 16 +nf9_63: mov al, bl +nf9_64: mov ah, bl + mov [edi], eax + +nf9_65: mov al, bl +nf9_66: mov ah, bl + shl eax, 16 +nf9_67: mov al, bl +nf9_68: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_71: mov al, bl +nf9_72: mov ah, bl + shl eax, 16 +nf9_73: mov al, bl +nf9_74: mov ah, bl + mov [edi], eax + +nf9_75: mov al, bl +nf9_76: mov ah, bl + shl eax, 16 +nf9_77: mov al, bl +nf9_78: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_81: mov al, bl +nf9_82: mov ah, bl + shl eax, 16 +nf9_83: mov al, bl +nf9_84: mov ah, bl + mov [edi], eax + +nf9_85: mov al, bl +nf9_86: mov ah, bl + shl eax, 16 +nf9_87: mov al, bl +nf9_88: mov ah, bl + mov [edi+4], eax + + add esi, 20 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +;nf9+16 +nf25: ; low 4x4x2 (8 bytes) + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 8 + jmp nf_solid +endif + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf25_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_14-nf25_11)], bl + mov [edx+(nf25_13-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_12-nf25_11)], bl + mov [edx+(nf25_11-nf25_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_24-nf25_11)], bl + mov [edx+(nf25_23-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_22-nf25_11)], bl + mov [edx+(nf25_21-nf25_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_34-nf25_11)], bl + mov [edx+(nf25_33-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_32-nf25_11)], bl + mov [edx+(nf25_31-nf25_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_44-nf25_11)], bl + mov [edx+(nf25_43-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_42-nf25_11)], bl + mov [edx+(nf25_41-nf25_11)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf25_0 ; flush prefetch + ALIGN 4 +nf25_0: +nf25_11:mov ah, bl + mov al, ah + shl eax, 16 +nf25_12:mov al, bl + mov ah, al + mov [edi], eax + mov [edi+edx], eax +nf25_13:mov ah, bl + mov al, ah + shl eax, 16 +nf25_14:mov al, bl + mov ah, al + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf25_21:mov ah, bl + mov al, ah + shl eax, 16 +nf25_22:mov al, bl + mov ah, al + mov [edi], eax + mov [edi+edx], eax +nf25_23:mov ah, bl + mov al, ah + shl eax, 16 +nf25_24:mov al, bl + mov ah, al + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf25_31:mov ah, bl + mov al, ah + shl eax, 16 +nf25_32:mov al, bl + mov ah, al + mov [edi], eax + mov [edi+edx], eax +nf25_33:mov ah, bl + mov al, ah + shl eax, 16 +nf25_34:mov al, bl + mov ah, al + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf25_41:mov ah, bl + mov al, ah + shl eax, 16 +nf25_42:mov al, bl + mov ah, al + mov [edi], eax + mov [edi+edx], eax +nf25_43:mov ah, bl + mov al, ah + shl eax, 16 +nf25_44:mov al, bl + mov ah, al + mov [edi+4], eax + mov [edi+edx+4], eax + add edi, edx + + add esi, 8 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+32 +nf41: ; low 4x8x2 (12 bytes) + shr eax, 16 + cmp al, ah + ja nf57 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf41_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_14-nf41_11)], bl + mov [edx+(nf41_13-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_12-nf41_11)], bl + mov [edx+(nf41_11-nf41_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_24-nf41_11)], bl + mov [edx+(nf41_23-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_22-nf41_11)], bl + mov [edx+(nf41_21-nf41_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_34-nf41_11)], bl + mov [edx+(nf41_33-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_32-nf41_11)], bl + mov [edx+(nf41_31-nf41_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_44-nf41_11)], bl + mov [edx+(nf41_43-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_42-nf41_11)], bl + mov [edx+(nf41_41-nf41_11)], bh + + lea edx, [edx+(nf41_51-nf41_11)] + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_54-nf41_51)], bl + mov [edx+(nf41_53-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_52-nf41_51)], bl + mov [edx+(nf41_51-nf41_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_64-nf41_51)], bl + mov [edx+(nf41_63-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_62-nf41_51)], bl + mov [edx+(nf41_61-nf41_51)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_74-nf41_51)], bl + mov [edx+(nf41_73-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_72-nf41_51)], bl + mov [edx+(nf41_71-nf41_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_84-nf41_51)], bl + mov [edx+(nf41_83-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_82-nf41_51)], bl + mov [edx+(nf41_81-nf41_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf41_0 ; flush prefetch + ALIGN 4 +nf41_0: +nf41_11:mov ah, bl + mov al, ah + shl eax, 16 +nf41_12:mov al, bl + mov ah, al + mov [edi], eax +nf41_13:mov ah, bl + mov al, ah + shl eax, 16 +nf41_14:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_21:mov ah, bl + mov al, ah + shl eax, 16 +nf41_22:mov al, bl + mov ah, al + mov [edi], eax +nf41_23:mov ah, bl + mov al, ah + shl eax, 16 +nf41_24:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_31:mov ah, bl + mov al, ah + shl eax, 16 +nf41_32:mov al, bl + mov ah, al + mov [edi], eax +nf41_33:mov ah, bl + mov al, ah + shl eax, 16 +nf41_34:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_41:mov ah, bl + mov al, ah + shl eax, 16 +nf41_42:mov al, bl + mov ah, al + mov [edi], eax +nf41_43:mov ah, bl + mov al, ah + shl eax, 16 +nf41_44:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_51:mov ah, bl + mov al, ah + shl eax, 16 +nf41_52:mov al, bl + mov ah, al + mov [edi], eax +nf41_53:mov ah, bl + mov al, ah + shl eax, 16 +nf41_54:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_61:mov ah, bl + mov al, ah + shl eax, 16 +nf41_62:mov al, bl + mov ah, al + mov [edi], eax +nf41_63:mov ah, bl + mov al, ah + shl eax, 16 +nf41_64:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_71:mov ah, bl + mov al, ah + shl eax, 16 +nf41_72:mov al, bl + mov ah, al + mov [edi], eax +nf41_73:mov ah, bl + mov al, ah + shl eax, 16 +nf41_74:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_81:mov ah, bl + mov al, ah + shl eax, 16 +nf41_82:mov al, bl + mov ah, al + mov [edi], eax +nf41_83:mov ah, bl + mov al, ah + shl eax, 16 +nf41_84:mov al, bl + mov ah, al + mov [edi+4], eax + + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+48 +nf57: ; low 8x4x2 (12 bytes) + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf57_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_11-nf57_11)], bl + mov [edx+(nf57_12-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_13-nf57_11)], bl + mov [edx+(nf57_14-nf57_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_15-nf57_11)], bl + mov [edx+(nf57_16-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_17-nf57_11)], bl + mov [edx+(nf57_18-nf57_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_21-nf57_11)], bl + mov [edx+(nf57_22-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_23-nf57_11)], bl + mov [edx+(nf57_24-nf57_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_25-nf57_11)], bl + mov [edx+(nf57_26-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_27-nf57_11)], bl + mov [edx+(nf57_28-nf57_11)], bh + + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_31-nf57_11)], bl + mov [edx+(nf57_32-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_33-nf57_11)], bl + mov [edx+(nf57_34-nf57_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_35-nf57_11)], bl + mov [edx+(nf57_36-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_37-nf57_11)], bl + mov [edx+(nf57_38-nf57_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_41-nf57_11)], bl + mov [edx+(nf57_42-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_43-nf57_11)], bl + mov [edx+(nf57_44-nf57_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_45-nf57_11)], bl + mov [edx+(nf57_46-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_47-nf57_11)], bl + mov [edx+(nf57_48-nf57_11)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf57_0 ; flush prefetch + ALIGN 4 +nf57_0: +nf57_11:mov al, bl +nf57_12:mov ah, bl + shl eax, 16 +nf57_13:mov al, bl +nf57_14:mov ah, bl + mov [edi], eax + mov [edi+edx], eax + +nf57_15:mov al, bl +nf57_16:mov ah, bl + shl eax, 16 +nf57_17:mov al, bl +nf57_18:mov ah, bl + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf57_21:mov al, bl +nf57_22:mov ah, bl + shl eax, 16 +nf57_23:mov al, bl +nf57_24:mov ah, bl + mov [edi], eax + mov [edi+edx], eax + +nf57_25:mov al, bl +nf57_26:mov ah, bl + shl eax, 16 +nf57_27:mov al, bl +nf57_28:mov ah, bl + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf57_31:mov al, bl +nf57_32:mov ah, bl + shl eax, 16 +nf57_33:mov al, bl +nf57_34:mov ah, bl + mov [edi], eax + mov [edi+edx], eax + +nf57_35:mov al, bl +nf57_36:mov ah, bl + shl eax, 16 +nf57_37:mov al, bl +nf57_38:mov ah, bl + mov [edi+4], eax + mov [edi+edx+4], eax + lea edi, [edi+edx*2] + +nf57_41:mov al, bl +nf57_42:mov ah, bl + shl eax, 16 +nf57_43:mov al, bl +nf57_44:mov ah, bl + mov [edi], eax + mov [edi+edx], eax + +nf57_45:mov al, bl +nf57_46:mov ah, bl + shl eax, 16 +nf57_47:mov al, bl +nf57_48:mov ah, bl + mov [edi+4], eax + mov [edi+edx+4], eax + add edi, edx + + add esi, 12 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf10: ; 2x2 4x4x2 (32 bytes) + + mov ax, [esi] + cmp al, ah + ja nf26 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf10_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_11-nf10_11)], bl + mov [edx+(nf10_12-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_13-nf10_11)], bl + mov [edx+(nf10_14-nf10_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_15-nf10_11)], bl + mov [edx+(nf10_16-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_17-nf10_11)], bl + mov [edx+(nf10_18-nf10_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_21-nf10_11)], bl + mov [edx+(nf10_22-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_23-nf10_11)], bl + mov [edx+(nf10_24-nf10_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_25-nf10_11)], bl + mov [edx+(nf10_26-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_27-nf10_11)], bl + mov [edx+(nf10_28-nf10_11)], bh + + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_31-nf10_11)], bl + mov [edx+(nf10_32-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_33-nf10_11)], bl + mov [edx+(nf10_34-nf10_11)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_35-nf10_11)], bl + mov [edx+(nf10_36-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_37-nf10_11)], bl + mov [edx+(nf10_38-nf10_11)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_41-nf10_11)], bl + mov [edx+(nf10_42-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_43-nf10_11)], bl + mov [edx+(nf10_44-nf10_11)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_45-nf10_11)], bl + mov [edx+(nf10_46-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_47-nf10_11)], bl + mov [edx+(nf10_48-nf10_11)], bh + + + lea edx, [edx+(nf10_51-nf10_11)] + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_51-nf10_51)], bl + mov [edx+(nf10_52-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_53-nf10_51)], bl + mov [edx+(nf10_54-nf10_51)], bh + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_55-nf10_51)], bl + mov [edx+(nf10_56-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_57-nf10_51)], bl + mov [edx+(nf10_58-nf10_51)], bh + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_61-nf10_51)], bl + mov [edx+(nf10_62-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_63-nf10_51)], bl + mov [edx+(nf10_64-nf10_51)], bh + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_65-nf10_51)], bl + mov [edx+(nf10_66-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_67-nf10_51)], bl + mov [edx+(nf10_68-nf10_51)], bh + + + mov al, [esi+28] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_71-nf10_51)], bl + mov [edx+(nf10_72-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_73-nf10_51)], bl + mov [edx+(nf10_74-nf10_51)], bh + + mov al, [esi+29] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_75-nf10_51)], bl + mov [edx+(nf10_76-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_77-nf10_51)], bl + mov [edx+(nf10_78-nf10_51)], bh + + + mov al, [esi+30] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_81-nf10_51)], bl + mov [edx+(nf10_82-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_83-nf10_51)], bl + mov [edx+(nf10_84-nf10_51)], bh + + mov al, [esi+31] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_85-nf10_51)], bl + mov [edx+(nf10_86-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_87-nf10_51)], bl + mov [edx+(nf10_88-nf10_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf10_0 ; flush prefetch + ALIGN 4 +nf10_0: +nf10_11:mov al, bl +nf10_12:mov ah, bl + shl eax, 16 +nf10_13:mov al, bl +nf10_14:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_15:mov al, bl +nf10_16:mov ah, bl + shl eax, 16 +nf10_17:mov al, bl +nf10_18:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_21:mov al, bl +nf10_22:mov ah, bl + shl eax, 16 +nf10_23:mov al, bl +nf10_24:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_25:mov al, bl +nf10_26:mov ah, bl + shl eax, 16 +nf10_27:mov al, bl +nf10_28:mov ah, bl + mov [edi], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+8] + mov cx, [esi+10] + +nf10_31:mov al, bl +nf10_32:mov ah, bl + shl eax, 16 +nf10_33:mov al, bl +nf10_34:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_35:mov al, bl +nf10_36:mov ah, bl + shl eax, 16 +nf10_37:mov al, bl +nf10_38:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_41:mov al, bl +nf10_42:mov ah, bl + shl eax, 16 +nf10_43:mov al, bl +nf10_44:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_45:mov al, bl +nf10_46:mov ah, bl + shl eax, 16 +nf10_47:mov al, bl +nf10_48:mov ah, bl + mov [edi], eax + add edi, edx + + lea eax, [edx*8-4] + sub edi, eax + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+16] + mov cx, [esi+18] + +nf10_51:mov al, bl +nf10_52:mov ah, bl + shl eax, 16 +nf10_53:mov al, bl +nf10_54:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_55:mov al, bl +nf10_56:mov ah, bl + shl eax, 16 +nf10_57:mov al, bl +nf10_58:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_61:mov al, bl +nf10_62:mov ah, bl + shl eax, 16 +nf10_63:mov al, bl +nf10_64:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_65:mov al, bl +nf10_66:mov ah, bl + shl eax, 16 +nf10_67:mov al, bl +nf10_68:mov ah, bl + mov [edi], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+24] + mov cx, [esi+26] + +nf10_71:mov al, bl +nf10_72:mov ah, bl + shl eax, 16 +nf10_73:mov al, bl +nf10_74:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_75:mov al, bl +nf10_76:mov ah, bl + shl eax, 16 +nf10_77:mov al, bl +nf10_78:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_81:mov al, bl +nf10_82:mov ah, bl + shl eax, 16 +nf10_83:mov al, bl +nf10_84:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_85:mov al, bl +nf10_86:mov ah, bl + shl eax, 16 +nf10_87:mov al, bl +nf10_88:mov ah, bl + mov [edi], eax + + add esi, 32 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+16 +nf26: ; 2x1 4x8x2 (24 bytes) + + mov ax, [esi+12] + cmp al, ah + ja nf42 + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 24 + jmp nf_solid +endif + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf26_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_11-nf26_11)], bl + mov [edx+(nf26_12-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_13-nf26_11)], bl + mov [edx+(nf26_14-nf26_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_15-nf26_11)], bl + mov [edx+(nf26_16-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_17-nf26_11)], bl + mov [edx+(nf26_18-nf26_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_21-nf26_11)], bl + mov [edx+(nf26_22-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_23-nf26_11)], bl + mov [edx+(nf26_24-nf26_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_25-nf26_11)], bl + mov [edx+(nf26_26-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_27-nf26_11)], bl + mov [edx+(nf26_28-nf26_11)], bh + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_31-nf26_11)], bl + mov [edx+(nf26_32-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_33-nf26_11)], bl + mov [edx+(nf26_34-nf26_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_35-nf26_11)], bl + mov [edx+(nf26_36-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_37-nf26_11)], bl + mov [edx+(nf26_38-nf26_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_41-nf26_11)], bl + mov [edx+(nf26_42-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_43-nf26_11)], bl + mov [edx+(nf26_44-nf26_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_45-nf26_11)], bl + mov [edx+(nf26_46-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_47-nf26_11)], bl + mov [edx+(nf26_48-nf26_11)], bh + + + lea edx, [edx+(nf26_51-nf26_11)] + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_51-nf26_51)], bl + mov [edx+(nf26_52-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_53-nf26_51)], bl + mov [edx+(nf26_54-nf26_51)], bh + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_55-nf26_51)], bl + mov [edx+(nf26_56-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_57-nf26_51)], bl + mov [edx+(nf26_58-nf26_51)], bh + + + mov al, [esi+18] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_61-nf26_51)], bl + mov [edx+(nf26_62-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_63-nf26_51)], bl + mov [edx+(nf26_64-nf26_51)], bh + + mov al, [esi+19] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_65-nf26_51)], bl + mov [edx+(nf26_66-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_67-nf26_51)], bl + mov [edx+(nf26_68-nf26_51)], bh + + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_71-nf26_51)], bl + mov [edx+(nf26_72-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_73-nf26_51)], bl + mov [edx+(nf26_74-nf26_51)], bh + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_75-nf26_51)], bl + mov [edx+(nf26_76-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_77-nf26_51)], bl + mov [edx+(nf26_78-nf26_51)], bh + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_81-nf26_51)], bl + mov [edx+(nf26_82-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_83-nf26_51)], bl + mov [edx+(nf26_84-nf26_51)], bh + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_85-nf26_51)], bl + mov [edx+(nf26_86-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_87-nf26_51)], bl + mov [edx+(nf26_88-nf26_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf26_0 ; flush prefetch + ALIGN 4 +nf26_0: +nf26_11:mov al, bl +nf26_12:mov ah, bl + shl eax, 16 +nf26_13:mov al, bl +nf26_14:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_15:mov al, bl +nf26_16:mov ah, bl + shl eax, 16 +nf26_17:mov al, bl +nf26_18:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_21:mov al, bl +nf26_22:mov ah, bl + shl eax, 16 +nf26_23:mov al, bl +nf26_24:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_25:mov al, bl +nf26_26:mov ah, bl + shl eax, 16 +nf26_27:mov al, bl +nf26_28:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_31:mov al, bl +nf26_32:mov ah, bl + shl eax, 16 +nf26_33:mov al, bl +nf26_34:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_35:mov al, bl +nf26_36:mov ah, bl + shl eax, 16 +nf26_37:mov al, bl +nf26_38:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_41:mov al, bl +nf26_42:mov ah, bl + shl eax, 16 +nf26_43:mov al, bl +nf26_44:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_45:mov al, bl +nf26_46:mov ah, bl + shl eax, 16 +nf26_47:mov al, bl +nf26_48:mov ah, bl + mov [edi], eax + add edi, edx + + lea eax, [edx*8-4] + sub edi, eax + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+12] + mov cx, [esi+14] + +nf26_51:mov al, bl +nf26_52:mov ah, bl + shl eax, 16 +nf26_53:mov al, bl +nf26_54:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_55:mov al, bl +nf26_56:mov ah, bl + shl eax, 16 +nf26_57:mov al, bl +nf26_58:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_61:mov al, bl +nf26_62:mov ah, bl + shl eax, 16 +nf26_63:mov al, bl +nf26_64:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_65:mov al, bl +nf26_66:mov ah, bl + shl eax, 16 +nf26_67:mov al, bl +nf26_68:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_71:mov al, bl +nf26_72:mov ah, bl + shl eax, 16 +nf26_73:mov al, bl +nf26_74:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_75:mov al, bl +nf26_76:mov ah, bl + shl eax, 16 +nf26_77:mov al, bl +nf26_78:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_81:mov al, bl +nf26_82:mov ah, bl + shl eax, 16 +nf26_83:mov al, bl +nf26_84:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_85:mov al, bl +nf26_86:mov ah, bl + shl eax, 16 +nf26_87:mov al, bl +nf26_88:mov ah, bl + mov [edi], eax + + add esi, 24 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+32 +nf42: ; 1x2 8x4x2 (24 bytes) + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 24 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf42_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_11-nf42_11)], bl + mov [edx+(nf42_12-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_13-nf42_11)], bl + mov [edx+(nf42_14-nf42_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_15-nf42_11)], bl + mov [edx+(nf42_16-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_17-nf42_11)], bl + mov [edx+(nf42_18-nf42_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_21-nf42_11)], bl + mov [edx+(nf42_22-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_23-nf42_11)], bl + mov [edx+(nf42_24-nf42_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_25-nf42_11)], bl + mov [edx+(nf42_26-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_27-nf42_11)], bl + mov [edx+(nf42_28-nf42_11)], bh + + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_31-nf42_11)], bl + mov [edx+(nf42_32-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_33-nf42_11)], bl + mov [edx+(nf42_34-nf42_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_35-nf42_11)], bl + mov [edx+(nf42_36-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_37-nf42_11)], bl + mov [edx+(nf42_38-nf42_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_41-nf42_11)], bl + mov [edx+(nf42_42-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_43-nf42_11)], bl + mov [edx+(nf42_44-nf42_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_45-nf42_11)], bl + mov [edx+(nf42_46-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_47-nf42_11)], bl + mov [edx+(nf42_48-nf42_11)], bh + + + lea edx, [edx+(nf42_51-nf42_11)] + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_51-nf42_51)], bl + mov [edx+(nf42_52-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_53-nf42_51)], bl + mov [edx+(nf42_54-nf42_51)], bh + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_55-nf42_51)], bl + mov [edx+(nf42_56-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_57-nf42_51)], bl + mov [edx+(nf42_58-nf42_51)], bh + + + mov al, [esi+18] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_61-nf42_51)], bl + mov [edx+(nf42_62-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_63-nf42_51)], bl + mov [edx+(nf42_64-nf42_51)], bh + + mov al, [esi+19] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_65-nf42_51)], bl + mov [edx+(nf42_66-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_67-nf42_51)], bl + mov [edx+(nf42_68-nf42_51)], bh + + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_71-nf42_51)], bl + mov [edx+(nf42_72-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_73-nf42_51)], bl + mov [edx+(nf42_74-nf42_51)], bh + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_75-nf42_51)], bl + mov [edx+(nf42_76-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_77-nf42_51)], bl + mov [edx+(nf42_78-nf42_51)], bh + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_81-nf42_51)], bl + mov [edx+(nf42_82-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_83-nf42_51)], bl + mov [edx+(nf42_84-nf42_51)], bh + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_85-nf42_51)], bl + mov [edx+(nf42_86-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_87-nf42_51)], bl + mov [edx+(nf42_88-nf42_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf42_0 ; flush prefetch + ALIGN 4 +nf42_0: +nf42_11:mov al, bl +nf42_12:mov ah, bl + shl eax, 16 +nf42_13:mov al, bl +nf42_14:mov ah, bl + mov [edi], eax + +nf42_15:mov al, bl +nf42_16:mov ah, bl + shl eax, 16 +nf42_17:mov al, bl +nf42_18:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_21:mov al, bl +nf42_22:mov ah, bl + shl eax, 16 +nf42_23:mov al, bl +nf42_24:mov ah, bl + mov [edi], eax + +nf42_25:mov al, bl +nf42_26:mov ah, bl + shl eax, 16 +nf42_27:mov al, bl +nf42_28:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_31:mov al, bl +nf42_32:mov ah, bl + shl eax, 16 +nf42_33:mov al, bl +nf42_34:mov ah, bl + mov [edi], eax + +nf42_35:mov al, bl +nf42_36:mov ah, bl + shl eax, 16 +nf42_37:mov al, bl +nf42_38:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_41:mov al, bl +nf42_42:mov ah, bl + shl eax, 16 +nf42_43:mov al, bl +nf42_44:mov ah, bl + mov [edi], eax + +nf42_45:mov al, bl +nf42_46:mov ah, bl + shl eax, 16 +nf42_47:mov al, bl +nf42_48:mov ah, bl + mov [edi+4], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+12] + mov cx, [esi+14] + +nf42_51:mov al, bl +nf42_52:mov ah, bl + shl eax, 16 +nf42_53:mov al, bl +nf42_54:mov ah, bl + mov [edi], eax + +nf42_55:mov al, bl +nf42_56:mov ah, bl + shl eax, 16 +nf42_57:mov al, bl +nf42_58:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_61:mov al, bl +nf42_62:mov ah, bl + shl eax, 16 +nf42_63:mov al, bl +nf42_64:mov ah, bl + mov [edi], eax + +nf42_65:mov al, bl +nf42_66:mov ah, bl + shl eax, 16 +nf42_67:mov al, bl +nf42_68:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_71:mov al, bl +nf42_72:mov ah, bl + shl eax, 16 +nf42_73:mov al, bl +nf42_74:mov ah, bl + mov [edi], eax + +nf42_75:mov al, bl +nf42_76:mov ah, bl + shl eax, 16 +nf42_77:mov al, bl +nf42_78:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_81:mov al, bl +nf42_82:mov ah, bl + shl eax, 16 +nf42_83:mov al, bl +nf42_84:mov ah, bl + mov [edi], eax + +nf42_85:mov al, bl +nf42_86:mov ah, bl + shl eax, 16 +nf42_87:mov al, bl +nf42_88:mov ah, bl + mov [edi+4], eax + + add esi, 24 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf11: ; 8x8x8 (64 bytes) +if 0 ;debug + add esi, 64 + mov eax, 0fefefefeH +; mov ebx, eax + mov ebx, 0 + jmp nf_solid +endif + mov edx, nf_width + + mov eax, [esi] ;0 + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + add edi, edx + mov eax, [esi+8] ;1 + mov [edi], eax + mov eax, [esi+12] + mov [edi+4], eax + add edi, edx + mov eax, [esi+16] ;2 + mov [edi], eax + mov eax, [esi+20] + mov [edi+4], eax + add edi, edx + mov eax, [esi+24] ;3 + mov [edi], eax + mov eax, [esi+28] + mov [edi+4], eax + add edi, edx + mov eax, [esi+32] ;4 + mov [edi], eax + mov eax, [esi+36] + mov [edi+4], eax + add edi, edx + mov eax, [esi+40] ;5 + mov [edi], eax + mov eax, [esi+44] + mov [edi+4], eax + add edi, edx + mov eax, [esi+48] ;6 + mov [edi], eax + mov eax, [esi+52] + mov [edi+4], eax + add edi, edx + mov eax, [esi+56] ;7 + mov [edi], eax + mov eax, [esi+60] + mov [edi+4], eax + + add esi, 64 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +;---------------------------------------- + ALIGN 4 +nf12: ; low 4x4x8 (16 bytes) + mov edx, nf_width + + mov eax, [esi] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + mov [edi+edx], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + + mov eax, [esi+4] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + mov [edi+edx], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + + mov eax, [esi+8] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + mov [edi+edx], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + + mov eax, [esi+12] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + mov [edi+edx], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + mov [edi+edx+4], ebx + add edi, edx + + sub edi, nfpk_back_right + add esi, 16 + retn + +;---------------------------------------- + ALIGN 4 +nf13: ; 2x2 4x4x0 (4 bytes) + mov edx, nf_width + + mov cl, [esi] + mov ch, cl + mov eax, ecx + shl eax, 16 + mov ax, cx + + mov cl, [esi+1] + mov ch, cl + mov ebx, ecx + shl ebx, 16 + mov bx, cx + + mov [edi], eax + mov [edi+4], ebx + mov [edi+edx], eax + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + mov [edi], eax + mov [edi+4], ebx + mov [edi+edx], eax + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + + mov cl, [esi+2] + mov ch, cl + mov eax, ecx + shl eax, 16 + mov ax, cx + + mov cl, [esi+3] + mov ch, cl + mov ebx, ecx + shl ebx, 16 + mov bx, cx + + mov [edi], eax + mov [edi+4], ebx + mov [edi+edx], eax + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + mov [edi], eax + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], ebx + + sub edi, nfpk_back_right + add esi, 4 + + retn + +;---------------------------------------- + ALIGN 4 +nf14: ; 8x8x0 (1 byte) +if 0 ;debug + jmp nf0 +endif + mov bl, [esi] ; Copy color into 8 positions + inc esi + mov bh, bl + mov eax, ebx + shl eax, 16 + mov ax, bx + mov ebx, eax +if 0 ;debug + mov eax, 080808080h + mov ebx, eax +endif + jmp nf_solid + + retn + +;---------------------------------------- + ALIGN 4 +nf15: ; mix 8x8x0 (2 bytes) +if 0 ;debug + inc esi + jmp nf0 +endif + mov bx, [esi] ; Copy 2 colors into 8 positions + add esi, 2 ; in a checkerboard + mov ax, bx + shl eax, 16 + mov ax, bx + mov ebx, eax + rol ebx, 8 +if 0 ;debug + mov eax, 080808080h + mov ebx, eax +endif +nf_solid: + mov edx, nf_width + + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +nfPkDecomp ENDP + +; Half vertical resolution version (skip odd lines) +; +nfPkDecompH PROC USES ESI EDI EBX, \ + ops:PTRBYTE, comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row:DWORD + LOCAL DiffBufPtrs:DWORD + + LOCAL nfpk_back_right: DWORD + LOCAL wcnt:DWORD + + LOG_LABEL "StartPkDecomp" + +.data +nfpk_OpTblH label dword + dword offset nf0 ; Prev Same (0) + dword offset nf1 ; No change (and copied to screen) (0) + dword offset nf2 ; Near shift from older part of current buf (1) + dword offset nf3 ; Near shift from newer part of current buf (1) + dword offset nf4 ; Near shift from previous buffer (1) + dword offset nf5 ; Far shift from previous buffer (2) + dword offset nf6 ; Far shift from current buffer (2) + ; [Or if COMPOPS, run of no changes (0)] + dword offset nf7 ; 8x8x1 (10 bytes) or low 4x4x1 (4 bytes) + dword offset nf8 ; 2x2 4x4x1 (16 bytes) or 2x1 4x8x1 (12 bytes) or 1x2 8x4x1 (12 bytes) + dword offset nf9 ; 8x8x2 (20 bytes) or low 4x4x2 (8 bytes) or + ; low 4x8x2 (12 bytes) or low 8x4x2 (12 bytes) + dword offset nf10 ; 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + dword offset nf11 ; 8x8x8 (64 bytes) + dword offset nf12 ; low 4x4x8 (16 bytes) + dword offset nf13 ; 2x2 4x4x0 (ie 2x2x8) (4 bytes) + dword offset nf14 ; 8x8x0 (1 byte) + dword offset nf15 ; mix 8x8x0 (2 bytes) +.code + +ifdef SYMANTEC + mov ebx, ds ; Allow DS to access code + mov ecx, 0 + mov ax, 3505h + int 21h +endif + + NF_DECOMP_INIT 0 + + mov eax, nf_width + shl eax, 2 + sub eax, nf_new_w + mov new_row, eax + + shr nf_new_h, 1 + + mov eax, nf_width + lea eax, [eax*2+eax-SWIDTH] + mov nfpk_back_right, eax + + mov esi, comp + mov edi, tbuf +nf_StartRow: + mov eax, w + shr eax, 1 + mov wcnt,eax + ALIGN 4 +nf_NextPair: + dec wcnt + js nf_NextRow + mov ebx, ops + mov al, [ebx] + inc ebx + mov ops, ebx + + xor ebx, ebx + mov bl, al + shr bl, 4 + and eax, 0Fh + push offset nf_NextPair + push nfpk_OpTblH[ebx*4] + jmp nfpk_OpTblH[eax*4] + +nf_NextRow: + add edi, new_row + dec h + jnz nf_StartRow + LOG_LABEL "EndPkDecomp" + +ifdef SYMANTEC + mov ebx, ds ; Disable DS from accessing code + mov ecx, offset DGROUP:_data_bottom[-1] + mov ax, 3505h + int 21h +endif + ret + +;---------------------------------------- + ALIGN 4 +nf0: ; No change from previous buffer + mov eax, DiffBufPtrs + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf1: ; No change (and copied to screen) + add edi, SWIDTH + retn + +;---------------------------------------- + ALIGN 4 +nf2: ; Near shift from older part of current buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP2[eax*2] +nf_xyc_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24 + add bl, 080h + adc bl, 080h + sar bl, 1 + add eax, nfpk_ShiftY[ebx*4] + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf3: ; Near shift from newer part of current buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP2[eax*2] + neg al + neg ah + jmp nf_xyc_shift + +;---------------------------------------- + ALIGN 4 +nf4: ; Near shift from previous buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP1[eax*2] + jmp nf_xyp_shift + +;---------------------------------------- + ALIGN 4 +nf5: ; Far shift from previous buffer + mov ax, [esi] + add esi, 2 +nf_xyp_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24 + add bl, 080h + adc bl, 080h + sar bl, 1 + add eax, nfpk_ShiftY[ebx*4] + add eax, DiffBufPtrs + jmp nf_shift + +;---------------------------------------- + ALIGN 4 + +if COMPOPS + +nf6: ; Run of no changes (must only appear in first nibble opcodes) + ; Next nibble k specifies 2k+4 squares with no changes + add esp, 4 ; Next nibble is not an opcode + add ebx, 2 ; (minimum of 4 squares) + ALIGN 4 +nf6a: add edi, SWIDTH*2 ; Advance over two squares + dec ebx + jz nf6z ; Last pair of squares + dec wcnt ; Same row? + jns nf6a ; Yes + add edi, new_row ; Advance to next row + dec h ; Decrement row count (should never become zero here) + mov eax, w ; Reset wcnt + shr eax ,1 + dec eax + mov wcnt, eax + jmp nf6a + +nf6z: retn + +else + +nf6: ; Far shift from current buffer + mov ax, [esi] + add esi, 2 + jmp nf_xyc_shift +endif + +;---------------------------------------- + ALIGN 4 +nf_shift: +if 0 ;debug + mov eax, 0 + mov ebx, eax + jmp nf_solid +endif + mov ebx, esi ; save esi + lea esi, [edi+eax] + mov edx, nf_width + + REPEAT 3 + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + add esi, edx + add edi, edx + ENDM + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + mov esi, ebx ; restore esi + retn + +;---------------------------------------- + ALIGN 4 +nf7: ; 8x8x1 (10 bytes) + + mov ax, [esi] + cmp al, ah + ja nf23 + +if 0 ;debug + add esi, 10 + mov eax, 0fefefefeH + mov ebx, eax + jmp nf_solid +endif + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf7_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_11-nf7_11)], bl + mov [edx+(nf7_12-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_13-nf7_11)], bl + mov [edx+(nf7_14-nf7_11)], bh + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_31-nf7_11)], bl + mov [edx+(nf7_32-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_33-nf7_11)], bl + mov [edx+(nf7_34-nf7_11)], bh + + lea edx, [edx+(nf7_51-nf7_11)] + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_51-nf7_51)], bl + mov [edx+(nf7_52-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_53-nf7_51)], bl + mov [edx+(nf7_54-nf7_51)], bh + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_71-nf7_51)], bl + mov [edx+(nf7_72-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_73-nf7_51)], bl + mov [edx+(nf7_74-nf7_51)], bh + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi,nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + jmp nf7_0 ; flush prefetch + ALIGN 4 +nf7_0: +nf7_11: mov ax, bx + shl eax, 16 +nf7_12: mov ax, bx + mov [edi], eax +nf7_13: mov ax, bx + shl eax, 16 +nf7_14: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_31: mov ax, bx + shl eax, 16 +nf7_32: mov ax, bx + mov [edi], eax +nf7_33: mov ax, bx + shl eax, 16 +nf7_34: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_51: mov ax, bx + shl eax, 16 +nf7_52: mov ax, bx + mov [edi], eax +nf7_53: mov ax, bx + shl eax, 16 +nf7_54: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_71: mov ax, bx + shl eax, 16 +nf7_72: mov ax, bx + mov [edi], eax +nf7_73: mov ax, bx + shl eax, 16 +nf7_74: mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 10 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf7+16 +nf23: ; low 4x4x1 (4 bytes) + + xor eax, eax + lea ecx, nfpk_mov4l + lea edx, byte ptr ds:nf23_11+2 + + mov al, [esi+2] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_11-nf23_11)], bl + mov [edx+(nf23_12-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_13-nf23_11)], bl + mov [edx+(nf23_14-nf23_11)], bh + + mov al, [esi+2] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_31-nf23_11)], bl + mov [edx+(nf23_32-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_33-nf23_11)], bl + mov [edx+(nf23_34-nf23_11)], bh + + + mov al, [esi+3] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_51-nf23_11)], bl + mov [edx+(nf23_52-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_53-nf23_11)], bl + mov [edx+(nf23_54-nf23_11)], bh + + mov al, [esi+3] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_71-nf23_11)], bl + mov [edx+(nf23_72-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_73-nf23_11)], bl + mov [edx+(nf23_74-nf23_11)], bh + + mov edx, nf_width + + ; load bx,cx with 00,11 color combinations + mov bx, [esi] + mov cl, bh + mov bh, bl + mov ch, cl + + jmp nf23_0 ; flush prefetch + ALIGN 4 +nf23_0: + +nf23_11:mov ax, bx + shl eax, 16 +nf23_12:mov ax, bx + mov [edi], eax + +nf23_13:mov ax, bx + shl eax, 16 +nf23_14:mov ax, bx + mov [edi+4], eax + add edi, edx + +nf23_31:mov ax, bx + shl eax, 16 +nf23_32:mov ax, bx + mov [edi], eax + +nf23_33:mov ax, bx + shl eax, 16 +nf23_34:mov ax, bx + mov [edi+4], eax + add edi, edx + +nf23_51:mov ax, bx + shl eax, 16 +nf23_52:mov ax, bx + mov [edi], eax + +nf23_53:mov ax, bx + shl eax, 16 +nf23_54:mov ax, bx + mov [edi+4], eax + add edi, edx + +nf23_71:mov ax, bx + shl eax, 16 +nf23_72:mov ax, bx + mov [edi], eax + +nf23_73:mov ax, bx + shl eax, 16 +nf23_74:mov ax, bx + mov [edi+4], eax + + sub edi, nfpk_back_right + add esi, 4 + retn + +;---------------------------------------- + ALIGN 4 +nf8: ; 2x2 4x4x1 (16 bytes) + + mov ax, [esi] + cmp al, ah + ja nf24 + + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf8_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_11-nf8_11)], bl + mov [edx+(nf8_12-nf8_11)], bh + + mov al, [esi+3] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_21-nf8_11)], bl + mov [edx+(nf8_22-nf8_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_31-nf8_11)], bl + mov [edx+(nf8_32-nf8_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_41-nf8_11)], bl + mov [edx+(nf8_42-nf8_11)], bh + + add edx, nf8_51-nf8_11 + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_51-nf8_51)], bl + mov [edx+(nf8_52-nf8_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_61-nf8_51)], bl + mov [edx+(nf8_62-nf8_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_71-nf8_51)], bl + mov [edx+(nf8_72-nf8_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_81-nf8_51)], bl + mov [edx+(nf8_82-nf8_51)], bh + + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf8_0 ; flush prefetch + ALIGN 4 +nf8_0: +nf8_11: mov ax, bx + shl eax, 16 +nf8_12: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_21: mov ax, bx + shl eax, 16 +nf8_22: mov ax, bx + mov [edi], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+4] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_31: mov ax, bx + shl eax, 16 +nf8_32: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_41: mov ax, bx + shl eax, 16 +nf8_42: mov ax, bx + mov [edi], eax + add edi, esi + + lea eax, [esi*4-4] + sub edi, eax + + mov eax, [esp] + mov cx, [eax+8] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_51: mov ax, bx + shl eax, 16 +nf8_52: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_61: mov ax, bx + shl eax, 16 +nf8_62: mov ax, bx + mov [edi], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+12] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_71: mov ax, bx + shl eax, 16 +nf8_72: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_81: mov ax, bx + shl eax, 16 +nf8_82: mov ax, bx + mov [edi], eax + + pop esi + pop ebp + add esi, 16 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+16 +nf24: ; 2x1 4x8x1 (12 bytes) + + mov ax, [esi+6] + cmp al, ah + ja nf40 + + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf24_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_11-nf24_11)], bl + mov [edx+(nf24_12-nf24_11)], bh + + mov al, [esi+3] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_21-nf24_11)], bl + mov [edx+(nf24_22-nf24_11)], bh + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_31-nf24_11)], bl + mov [edx+(nf24_32-nf24_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_41-nf24_11)], bl + mov [edx+(nf24_42-nf24_11)], bh + + add edx, nf24_51-nf24_11 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_51-nf24_51)], bl + mov [edx+(nf24_52-nf24_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_61-nf24_51)], bl + mov [edx+(nf24_62-nf24_51)], bh + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_71-nf24_51)], bl + mov [edx+(nf24_72-nf24_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_81-nf24_51)], bl + mov [edx+(nf24_82-nf24_51)], bh + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf24_0 ; flush prefetch + ALIGN 4 +nf24_0: +nf24_11:mov ax, bx + shl eax, 16 +nf24_12:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_21:mov ax, bx + shl eax, 16 +nf24_22:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_31:mov ax, bx + shl eax, 16 +nf24_32:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_41:mov ax, bx + shl eax, 16 +nf24_42:mov ax, bx + mov [edi], eax + add edi, esi + + lea eax, [esi*4-4] + sub edi, eax + + mov eax, [esp] + mov cx, [eax+6] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf24_51:mov ax, bx + shl eax, 16 +nf24_52:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_61:mov ax, bx + shl eax, 16 +nf24_62:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_71:mov ax, bx + shl eax, 16 +nf24_72:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_81:mov ax, bx + shl eax, 16 +nf24_82:mov ax, bx + mov [edi], eax + + pop esi + pop ebp + add esi, 12 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+32 +nf40: ; 1x2 8x4x1 (12 bytes) + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf40_11+2 + + mov al, [esi+2] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_11-nf40_11)], bl + mov [edx+(nf40_12-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_13-nf40_11)], bl + mov [edx+(nf40_14-nf40_11)], bh + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_31-nf40_11)], bl + mov [edx+(nf40_32-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_33-nf40_11)], bl + mov [edx+(nf40_34-nf40_11)], bh + + add edx, nf40_51-nf40_11 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_51-nf40_51)], bl + mov [edx+(nf40_52-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_53-nf40_51)], bl + mov [edx+(nf40_54-nf40_51)], bh + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_71-nf40_51)], bl + mov [edx+(nf40_72-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_73-nf40_51)], bl + mov [edx+(nf40_74-nf40_51)], bh + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf40_0 ; flush prefetch + ALIGN 4 +nf40_0: +nf40_11:mov ax, bx + shl eax, 16 +nf40_12:mov ax, bx + mov [edi], eax +nf40_13:mov ax, bx + shl eax, 16 +nf40_14:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_31:mov ax, bx + shl eax, 16 +nf40_32:mov ax, bx + mov [edi], eax +nf40_33:mov ax, bx + shl eax, 16 +nf40_34:mov ax, bx + mov [edi+4], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+6] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf40_51:mov ax, bx + shl eax, 16 +nf40_52:mov ax, bx + mov [edi], eax +nf40_53:mov ax, bx + shl eax, 16 +nf40_54:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_71:mov ax, bx + shl eax, 16 +nf40_72:mov ax, bx + mov [edi], eax +nf40_73:mov ax, bx + shl eax, 16 +nf40_74:mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +nf9: ; 8x8x2 (20 bytes) + + mov eax, [esi] + cmp al, ah + ja nf41 + + shr eax, 16 + cmp al, ah + ja nf25 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf9_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_11-nf9_11)], bl + mov [edx+(nf9_12-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_13-nf9_11)], bl + mov [edx+(nf9_14-nf9_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_15-nf9_11)], bl + mov [edx+(nf9_16-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_17-nf9_11)], bl + mov [edx+(nf9_18-nf9_11)], bh + + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_31-nf9_11)], bl + mov [edx+(nf9_32-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_33-nf9_11)], bl + mov [edx+(nf9_34-nf9_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_35-nf9_11)], bl + mov [edx+(nf9_36-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_37-nf9_11)], bl + mov [edx+(nf9_38-nf9_11)], bh + + lea edx, [edx+(nf9_51-nf9_11)] + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_51-nf9_51)], bl + mov [edx+(nf9_52-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_53-nf9_51)], bl + mov [edx+(nf9_54-nf9_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_55-nf9_51)], bl + mov [edx+(nf9_56-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_57-nf9_51)], bl + mov [edx+(nf9_58-nf9_51)], bh + + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_71-nf9_51)], bl + mov [edx+(nf9_72-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_73-nf9_51)], bl + mov [edx+(nf9_74-nf9_51)], bh + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_75-nf9_51)], bl + mov [edx+(nf9_76-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_77-nf9_51)], bl + mov [edx+(nf9_78-nf9_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf9_0 ; flush prefetch + ALIGN 4 +nf9_0: +nf9_11: mov al, bl +nf9_12: mov ah, bl + shl eax, 16 +nf9_13: mov al, bl +nf9_14: mov ah, bl + mov [edi], eax + +nf9_15: mov al, bl +nf9_16: mov ah, bl + shl eax, 16 +nf9_17: mov al, bl +nf9_18: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_31: mov al, bl +nf9_32: mov ah, bl + shl eax, 16 +nf9_33: mov al, bl +nf9_34: mov ah, bl + mov [edi], eax + +nf9_35: mov al, bl +nf9_36: mov ah, bl + shl eax, 16 +nf9_37: mov al, bl +nf9_38: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_51: mov al, bl +nf9_52: mov ah, bl + shl eax, 16 +nf9_53: mov al, bl +nf9_54: mov ah, bl + mov [edi], eax + +nf9_55: mov al, bl +nf9_56: mov ah, bl + shl eax, 16 +nf9_57: mov al, bl +nf9_58: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_71: mov al, bl +nf9_72: mov ah, bl + shl eax, 16 +nf9_73: mov al, bl +nf9_74: mov ah, bl + mov [edi], eax + +nf9_75: mov al, bl +nf9_76: mov ah, bl + shl eax, 16 +nf9_77: mov al, bl +nf9_78: mov ah, bl + mov [edi+4], eax + + add esi, 20 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +;nf9+16 +nf25: ; low 4x4x2 (8 bytes) + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 8 + jmp nf_solid +endif + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf25_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_14-nf25_11)], bl + mov [edx+(nf25_13-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_12-nf25_11)], bl + mov [edx+(nf25_11-nf25_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_24-nf25_11)], bl + mov [edx+(nf25_23-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_22-nf25_11)], bl + mov [edx+(nf25_21-nf25_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_34-nf25_11)], bl + mov [edx+(nf25_33-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_32-nf25_11)], bl + mov [edx+(nf25_31-nf25_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_44-nf25_11)], bl + mov [edx+(nf25_43-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_42-nf25_11)], bl + mov [edx+(nf25_41-nf25_11)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf25_0 ; flush prefetch + ALIGN 4 +nf25_0: +nf25_11:mov ah, bl + mov al, ah + shl eax, 16 +nf25_12:mov al, bl + mov ah, al + mov [edi], eax +nf25_13:mov ah, bl + mov al, ah + shl eax, 16 +nf25_14:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf25_21:mov ah, bl + mov al, ah + shl eax, 16 +nf25_22:mov al, bl + mov ah, al + mov [edi], eax +nf25_23:mov ah, bl + mov al, ah + shl eax, 16 +nf25_24:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf25_31:mov ah, bl + mov al, ah + shl eax, 16 +nf25_32:mov al, bl + mov ah, al + mov [edi], eax +nf25_33:mov ah, bl + mov al, ah + shl eax, 16 +nf25_34:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf25_41:mov ah, bl + mov al, ah + shl eax, 16 +nf25_42:mov al, bl + mov ah, al + mov [edi], eax +nf25_43:mov ah, bl + mov al, ah + shl eax, 16 +nf25_44:mov al, bl + mov ah, al + mov [edi+4], eax + + add esi, 8 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+32 +nf41: ; low 4x8x2 (12 bytes) + shr eax, 16 + cmp al, ah + ja nf57 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf41_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_14-nf41_11)], bl + mov [edx+(nf41_13-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_12-nf41_11)], bl + mov [edx+(nf41_11-nf41_11)], bh + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_34-nf41_11)], bl + mov [edx+(nf41_33-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_32-nf41_11)], bl + mov [edx+(nf41_31-nf41_11)], bh + + lea edx, [edx+(nf41_51-nf41_11)] + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_54-nf41_51)], bl + mov [edx+(nf41_53-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_52-nf41_51)], bl + mov [edx+(nf41_51-nf41_51)], bh + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_74-nf41_51)], bl + mov [edx+(nf41_73-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_72-nf41_51)], bl + mov [edx+(nf41_71-nf41_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf41_0 ; flush prefetch + ALIGN 4 +nf41_0: +nf41_11:mov ah, bl + mov al, ah + shl eax, 16 +nf41_12:mov al, bl + mov ah, al + mov [edi], eax +nf41_13:mov ah, bl + mov al, ah + shl eax, 16 +nf41_14:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_31:mov ah, bl + mov al, ah + shl eax, 16 +nf41_32:mov al, bl + mov ah, al + mov [edi], eax +nf41_33:mov ah, bl + mov al, ah + shl eax, 16 +nf41_34:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_51:mov ah, bl + mov al, ah + shl eax, 16 +nf41_52:mov al, bl + mov ah, al + mov [edi], eax +nf41_53:mov ah, bl + mov al, ah + shl eax, 16 +nf41_54:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf41_71:mov ah, bl + mov al, ah + shl eax, 16 +nf41_72:mov al, bl + mov ah, al + mov [edi], eax +nf41_73:mov ah, bl + mov al, ah + shl eax, 16 +nf41_74:mov al, bl + mov ah, al + mov [edi+4], eax + + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+48 +nf57: ; low 8x4x2 (12 bytes) + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf57_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_11-nf57_11)], bl + mov [edx+(nf57_12-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_13-nf57_11)], bl + mov [edx+(nf57_14-nf57_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_15-nf57_11)], bl + mov [edx+(nf57_16-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_17-nf57_11)], bl + mov [edx+(nf57_18-nf57_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_21-nf57_11)], bl + mov [edx+(nf57_22-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_23-nf57_11)], bl + mov [edx+(nf57_24-nf57_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_25-nf57_11)], bl + mov [edx+(nf57_26-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_27-nf57_11)], bl + mov [edx+(nf57_28-nf57_11)], bh + + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_31-nf57_11)], bl + mov [edx+(nf57_32-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_33-nf57_11)], bl + mov [edx+(nf57_34-nf57_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_35-nf57_11)], bl + mov [edx+(nf57_36-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_37-nf57_11)], bl + mov [edx+(nf57_38-nf57_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_41-nf57_11)], bl + mov [edx+(nf57_42-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_43-nf57_11)], bl + mov [edx+(nf57_44-nf57_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_45-nf57_11)], bl + mov [edx+(nf57_46-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_47-nf57_11)], bl + mov [edx+(nf57_48-nf57_11)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf57_0 ; flush prefetch + ALIGN 4 +nf57_0: +nf57_11:mov al, bl +nf57_12:mov ah, bl + shl eax, 16 +nf57_13:mov al, bl +nf57_14:mov ah, bl + mov [edi], eax + +nf57_15:mov al, bl +nf57_16:mov ah, bl + shl eax, 16 +nf57_17:mov al, bl +nf57_18:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf57_21:mov al, bl +nf57_22:mov ah, bl + shl eax, 16 +nf57_23:mov al, bl +nf57_24:mov ah, bl + mov [edi], eax + +nf57_25:mov al, bl +nf57_26:mov ah, bl + shl eax, 16 +nf57_27:mov al, bl +nf57_28:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf57_31:mov al, bl +nf57_32:mov ah, bl + shl eax, 16 +nf57_33:mov al, bl +nf57_34:mov ah, bl + mov [edi], eax + +nf57_35:mov al, bl +nf57_36:mov ah, bl + shl eax, 16 +nf57_37:mov al, bl +nf57_38:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf57_41:mov al, bl +nf57_42:mov ah, bl + shl eax, 16 +nf57_43:mov al, bl +nf57_44:mov ah, bl + mov [edi], eax + +nf57_45:mov al, bl +nf57_46:mov ah, bl + shl eax, 16 +nf57_47:mov al, bl +nf57_48:mov ah, bl + mov [edi+4], eax + + add esi, 12 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf10: ; 2x2 4x4x2 (32 bytes) + + mov ax, [esi] + cmp al, ah + ja nf26 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf10_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_11-nf10_11)], bl + mov [edx+(nf10_12-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_13-nf10_11)], bl + mov [edx+(nf10_14-nf10_11)], bh + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_21-nf10_11)], bl + mov [edx+(nf10_22-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_23-nf10_11)], bl + mov [edx+(nf10_24-nf10_11)], bh + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_31-nf10_11)], bl + mov [edx+(nf10_32-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_33-nf10_11)], bl + mov [edx+(nf10_34-nf10_11)], bh + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_41-nf10_11)], bl + mov [edx+(nf10_42-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_43-nf10_11)], bl + mov [edx+(nf10_44-nf10_11)], bh + + lea edx, [edx+(nf10_51-nf10_11)] + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_51-nf10_51)], bl + mov [edx+(nf10_52-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_53-nf10_51)], bl + mov [edx+(nf10_54-nf10_51)], bh + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_61-nf10_51)], bl + mov [edx+(nf10_62-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_63-nf10_51)], bl + mov [edx+(nf10_64-nf10_51)], bh + + mov al, [esi+28] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_71-nf10_51)], bl + mov [edx+(nf10_72-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_73-nf10_51)], bl + mov [edx+(nf10_74-nf10_51)], bh + + mov al, [esi+30] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_81-nf10_51)], bl + mov [edx+(nf10_82-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_83-nf10_51)], bl + mov [edx+(nf10_84-nf10_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf10_0 ; flush prefetch + ALIGN 4 +nf10_0: +nf10_11:mov al, bl +nf10_12:mov ah, bl + shl eax, 16 +nf10_13:mov al, bl +nf10_14:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_21:mov al, bl +nf10_22:mov ah, bl + shl eax, 16 +nf10_23:mov al, bl +nf10_24:mov ah, bl + mov [edi], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+8] + mov cx, [esi+10] + +nf10_31:mov al, bl +nf10_32:mov ah, bl + shl eax, 16 +nf10_33:mov al, bl +nf10_34:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_41:mov al, bl +nf10_42:mov ah, bl + shl eax, 16 +nf10_43:mov al, bl +nf10_44:mov ah, bl + mov [edi], eax + add edi, edx + + lea eax, [edx*4-4] + sub edi, eax + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+16] + mov cx, [esi+18] + +nf10_51:mov al, bl +nf10_52:mov ah, bl + shl eax, 16 +nf10_53:mov al, bl +nf10_54:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_61:mov al, bl +nf10_62:mov ah, bl + shl eax, 16 +nf10_63:mov al, bl +nf10_64:mov ah, bl + mov [edi], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+24] + mov cx, [esi+26] + +nf10_71:mov al, bl +nf10_72:mov ah, bl + shl eax, 16 +nf10_73:mov al, bl +nf10_74:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_81:mov al, bl +nf10_82:mov ah, bl + shl eax, 16 +nf10_83:mov al, bl +nf10_84:mov ah, bl + mov [edi], eax + + add esi, 32 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+16 +nf26: ; 2x1 4x8x2 (24 bytes) + + mov ax, [esi+12] + cmp al, ah + ja nf42 + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 24 + jmp nf_solid +endif + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf26_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_11-nf26_11)], bl + mov [edx+(nf26_12-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_13-nf26_11)], bl + mov [edx+(nf26_14-nf26_11)], bh + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_21-nf26_11)], bl + mov [edx+(nf26_22-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_23-nf26_11)], bl + mov [edx+(nf26_24-nf26_11)], bh + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_31-nf26_11)], bl + mov [edx+(nf26_32-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_33-nf26_11)], bl + mov [edx+(nf26_34-nf26_11)], bh + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_41-nf26_11)], bl + mov [edx+(nf26_42-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_43-nf26_11)], bl + mov [edx+(nf26_44-nf26_11)], bh + + lea edx, [edx+(nf26_51-nf26_11)] + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_51-nf26_51)], bl + mov [edx+(nf26_52-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_53-nf26_51)], bl + mov [edx+(nf26_54-nf26_51)], bh + + mov al, [esi+18] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_61-nf26_51)], bl + mov [edx+(nf26_62-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_63-nf26_51)], bl + mov [edx+(nf26_64-nf26_51)], bh + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_71-nf26_51)], bl + mov [edx+(nf26_72-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_73-nf26_51)], bl + mov [edx+(nf26_74-nf26_51)], bh + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_81-nf26_51)], bl + mov [edx+(nf26_82-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_83-nf26_51)], bl + mov [edx+(nf26_84-nf26_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf26_0 ; flush prefetch + ALIGN 4 +nf26_0: +nf26_11:mov al, bl +nf26_12:mov ah, bl + shl eax, 16 +nf26_13:mov al, bl +nf26_14:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_21:mov al, bl +nf26_22:mov ah, bl + shl eax, 16 +nf26_23:mov al, bl +nf26_24:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_31:mov al, bl +nf26_32:mov ah, bl + shl eax, 16 +nf26_33:mov al, bl +nf26_34:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_41:mov al, bl +nf26_42:mov ah, bl + shl eax, 16 +nf26_43:mov al, bl +nf26_44:mov ah, bl + mov [edi], eax + add edi, edx + + lea eax, [edx*4-4] + sub edi, eax + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+12] + mov cx, [esi+14] + +nf26_51:mov al, bl +nf26_52:mov ah, bl + shl eax, 16 +nf26_53:mov al, bl +nf26_54:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_61:mov al, bl +nf26_62:mov ah, bl + shl eax, 16 +nf26_63:mov al, bl +nf26_64:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_71:mov al, bl +nf26_72:mov ah, bl + shl eax, 16 +nf26_73:mov al, bl +nf26_74:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_81:mov al, bl +nf26_82:mov ah, bl + shl eax, 16 +nf26_83:mov al, bl +nf26_84:mov ah, bl + mov [edi], eax + + add esi, 24 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+32 +nf42: ; 1x2 8x4x2 (24 bytes) + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 24 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf42_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_11-nf42_11)], bl + mov [edx+(nf42_12-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_13-nf42_11)], bl + mov [edx+(nf42_14-nf42_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_15-nf42_11)], bl + mov [edx+(nf42_16-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_17-nf42_11)], bl + mov [edx+(nf42_18-nf42_11)], bh + + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_31-nf42_11)], bl + mov [edx+(nf42_32-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_33-nf42_11)], bl + mov [edx+(nf42_34-nf42_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_35-nf42_11)], bl + mov [edx+(nf42_36-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_37-nf42_11)], bl + mov [edx+(nf42_38-nf42_11)], bh + + lea edx, [edx+(nf42_51-nf42_11)] + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_51-nf42_51)], bl + mov [edx+(nf42_52-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_53-nf42_51)], bl + mov [edx+(nf42_54-nf42_51)], bh + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_55-nf42_51)], bl + mov [edx+(nf42_56-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_57-nf42_51)], bl + mov [edx+(nf42_58-nf42_51)], bh + + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_71-nf42_51)], bl + mov [edx+(nf42_72-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_73-nf42_51)], bl + mov [edx+(nf42_74-nf42_51)], bh + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_75-nf42_51)], bl + mov [edx+(nf42_76-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_77-nf42_51)], bl + mov [edx+(nf42_78-nf42_51)], bh + + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf42_0 ; flush prefetch + ALIGN 4 +nf42_0: +nf42_11:mov al, bl +nf42_12:mov ah, bl + shl eax, 16 +nf42_13:mov al, bl +nf42_14:mov ah, bl + mov [edi], eax + +nf42_15:mov al, bl +nf42_16:mov ah, bl + shl eax, 16 +nf42_17:mov al, bl +nf42_18:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_31:mov al, bl +nf42_32:mov ah, bl + shl eax, 16 +nf42_33:mov al, bl +nf42_34:mov ah, bl + mov [edi], eax + +nf42_35:mov al, bl +nf42_36:mov ah, bl + shl eax, 16 +nf42_37:mov al, bl +nf42_38:mov ah, bl + mov [edi+4], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+12] + mov cx, [esi+14] + +nf42_51:mov al, bl +nf42_52:mov ah, bl + shl eax, 16 +nf42_53:mov al, bl +nf42_54:mov ah, bl + mov [edi], eax + +nf42_55:mov al, bl +nf42_56:mov ah, bl + shl eax, 16 +nf42_57:mov al, bl +nf42_58:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_71:mov al, bl +nf42_72:mov ah, bl + shl eax, 16 +nf42_73:mov al, bl +nf42_74:mov ah, bl + mov [edi], eax + +nf42_75:mov al, bl +nf42_76:mov ah, bl + shl eax, 16 +nf42_77:mov al, bl +nf42_78:mov ah, bl + mov [edi+4], eax + + add esi, 24 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf11: ; 8x8x8 (64 bytes) +if 0 ;debug + add esi, 64 + mov eax, 0fefefefeH +; mov ebx, eax + mov ebx, 0 + jmp nf_solid +endif + mov edx, nf_width + + mov eax, [esi] ;0 + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + add edi, edx + mov eax, [esi+16] ;2 + mov [edi], eax + mov eax, [esi+20] + mov [edi+4], eax + add edi, edx + mov eax, [esi+32] ;4 + mov [edi], eax + mov eax, [esi+36] + mov [edi+4], eax + add edi, edx + mov eax, [esi+48] ;6 + mov [edi], eax + mov eax, [esi+52] + mov [edi+4], eax + + add esi, 64 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +;---------------------------------------- + ALIGN 4 +nf12: ; low 4x4x8 (16 bytes) + mov edx, nf_width + + mov eax, [esi] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + add edi, edx + + mov eax, [esi+4] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + add edi, edx + + mov eax, [esi+8] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + add edi, edx + + mov eax, [esi+12] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + + sub edi, nfpk_back_right + add esi, 16 + retn + +;---------------------------------------- + ALIGN 4 +nf13: ; 2x2 4x4x0 (4 bytes) + mov edx, nf_width + + mov cl, [esi] + mov ch, cl + mov eax, ecx + shl eax, 16 + mov ax, cx + + mov cl, [esi+1] + mov ch, cl + mov ebx, ecx + shl ebx, 16 + mov bx, cx + + mov [edi], eax + mov [edi+4], ebx + mov [edi+edx], eax + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + + mov cl, [esi+2] + mov ch, cl + mov eax, ecx + shl eax, 16 + mov ax, cx + + mov cl, [esi+3] + mov ch, cl + mov ebx, ecx + shl ebx, 16 + mov bx, cx + + mov [edi], eax + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], ebx + + sub edi, nfpk_back_right + add esi, 4 + + retn + +;---------------------------------------- + ALIGN 4 +nf14: ; 8x8x0 (1 byte) + mov bl, [esi] ; Copy color into 8 positions + inc esi + mov bh, bl + mov eax, ebx + shl eax, 16 + mov ax, bx + mov ebx, eax +if 0 ;debug + mov eax, 080808080h + mov ebx, eax +endif + jmp nf_solid + + retn + +;---------------------------------------- + ALIGN 4 +nf15: ; mix 8x8x0 (2 bytes) + mov bx, [esi] ; Copy 2 colors into 8 positions + add esi, 2 ; in a checkerboard + mov ax, bx + shl eax, 16 + mov ax, bx + mov ebx, eax + rol ebx, 8 +if 0 ;debug + mov eax, 080808080h + mov ebx, eax +endif +nf_solid: + mov edx, nf_width + + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +nfPkDecompH ENDP + +if DECOMPD + +; Half vertical resolution version (dither between lines) +; +nfPkDecompD PROC USES ESI EDI EBX, \ + ops:PTRBYTE, comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row:DWORD + LOCAL DiffBufPtrs:DWORD + + LOCAL nfpk_back_right: DWORD + LOCAL wcnt:DWORD + + LOG_LABEL "StartPkDecomp" + +.data +nfpk_OpTblD label dword + dword offset nf0 ; Prev Same (0) + dword offset nf1 ; No change (and copied to screen) (0) + dword offset nf2 ; Near shift from older part of current buf (1) + dword offset nf3 ; Near shift from newer part of current buf (1) + dword offset nf4 ; Near shift from previous buffer (1) + dword offset nf5 ; Far shift from previous buffer (2) + dword offset nf6 ; Far shift from current buffer (2) + ; [Or if COMPOPS, run of no changes (0)] + dword offset nf7 ; 8x8x1 (10 bytes) or low 4x4x1 (4 bytes) + dword offset nf8 ; 2x2 4x4x1 (16 bytes) or 2x1 4x8x1 (12 bytes) or 1x2 8x4x1 (12 bytes) + dword offset nf9 ; 8x8x2 (20 bytes) or low 4x4x2 (8 bytes) or + ; low 4x8x2 (12 bytes) or low 8x4x2 (12 bytes) + dword offset nf10 ; 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + dword offset nf11 ; 8x8x8 (64 bytes) + dword offset nf12 ; low 4x4x8 (16 bytes) + dword offset nf13 ; 2x2 4x4x0 (ie 2x2x8) (4 bytes) + dword offset nf14 ; 8x8x0 (1 byte) + dword offset nf15 ; mix 8x8x0 (2 bytes) +.code + +ifdef SYMANTEC + mov ebx, ds ; Allow DS to access code + mov ecx, 0 + mov ax, 3505h + int 21h +endif + + NF_DECOMP_INIT 0 + + mov eax, nf_width + shl eax, 2 + sub eax, nf_new_w + mov new_row, eax + + shr nf_new_h, 1 + + mov eax, nf_width + lea eax, [eax*2+eax-SWIDTH] + mov nfpk_back_right, eax + + mov esi, comp + mov edi, tbuf +nf_StartRow: + mov eax, w + shr eax, 1 + mov wcnt,eax + ALIGN 4 +nf_NextPair: + dec wcnt + js nf_NextRow + mov ebx, ops + mov al, [ebx] + inc ebx + mov ops, ebx + + xor ebx, ebx + mov bl, al + shr bl, 4 + and eax, 0Fh + push offset nf_NextPair + push nfpk_OpTblD[ebx*4] + jmp nfpk_OpTblD[eax*4] + +nf_NextRow: + add edi, new_row + dec h + jnz nf_StartRow + LOG_LABEL "EndPkDecomp" + +ifdef SYMANTEC + mov ebx, ds ; Disable DS from accessing code + mov ecx, offset DGROUP:_data_bottom[-1] + mov ax, 3505h + int 21h +endif + ret + +;---------------------------------------- + ALIGN 4 +nf0: ; No change from previous buffer + mov eax, DiffBufPtrs + jmp nf_shiftr + +;---------------------------------------- + ALIGN 4 +nf1: ; No change (and copied to screen) + add edi, SWIDTH + retn + +;---------------------------------------- + ALIGN 4 +nf2: ; Near shift from older part of current buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP2[eax*2] +nf_xyc_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24 + sar bl, 1 + pushf + add eax, nfpk_ShiftY[ebx*4] + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf3: ; Near shift from newer part of current buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP2[eax*2] + neg al + neg ah + jmp nf_xyc_shift + +;---------------------------------------- + ALIGN 4 +nf4: ; Near shift from previous buffer + xor eax, eax + mov al, [esi] + inc esi + mov ax, nfpk_ShiftP1[eax*2] + jmp nf_xyp_shift + +;---------------------------------------- + ALIGN 4 +nf5: ; Far shift from previous buffer + mov ax, [esi] + add esi, 2 +nf_xyp_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24 + sar bl, 1 + pushf + add eax, nfpk_ShiftY[ebx*4] + add eax, DiffBufPtrs + jmp nf_shift + +;---------------------------------------- + ALIGN 4 + +if COMPOPS + +nf6: ; Run of no changes (must only appear in first nibble opcodes) + ; Next nibble k specifies 2k+4 squares with no changes + add esp, 4 ; Next nibble is not an opcode + add ebx, 2 ; (minimum of 4 squares) + ALIGN 4 +nf6a: add edi, SWIDTH*2 ; Advance over two squares + dec ebx + jz nf6z ; Last pair of squares + dec wcnt ; Same row? + jns nf6a ; Yes + add edi, new_row ; Advance to next row + dec h ; Decrement row count (should never become zero here) + mov eax, w ; Reset wcnt + shr eax ,1 + dec eax + mov wcnt, eax + jmp nf6a + +nf6z: retn + +else + +nf6: ; Far shift from current buffer + mov ax, [esi] + add esi, 2 + jmp nf_xyc_shift + +endif + +;---------------------------------------- + ALIGN 4 +nf_shift: + popf +if 0 ;debug + mov eax, 0 + mov ebx, eax + jmp nf_solid +endif + jc nf_shiftd + +nf_shiftr: + mov ebx, esi ; save esi + lea esi, [edi+eax] + mov edx, nf_width + + REPEAT 3 + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + add esi, edx + add edi, edx + ENDM + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + mov esi, ebx ; restore esi + retn + +nf_shiftd: + push esi + lea esi, [edi+eax] + mov edx, nf_width + mov ebx, 000ff00ffH + + REPEAT 3 + mov eax, [esi] + mov ecx, eax + xor ecx, [esi+edx] + and ecx, ebx + xor eax, ecx + mov [edi], eax + mov eax, [esi+4] + mov ecx, eax + xor ecx, [esi+edx+4] + and ecx, ebx + xor eax, ecx + mov [edi+4], eax + add esi, edx + add edi, edx + ENDM + mov eax, [esi] + mov ecx, eax + xor ecx, [esi+edx] + and ecx, ebx + xor eax, ecx + mov [edi], eax + mov eax, [esi+4] + mov ecx, eax + xor ecx, [esi+edx+4] + and ecx, ebx + xor eax, ecx + mov [edi+4], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + pop esi + retn + +;---------------------------------------- + ALIGN 4 +nf7: ; 8x8x1 (10 bytes) + + mov ax, [esi] + cmp al, ah + ja nf23 + +if 0 ;debug + add esi, 10 + mov eax, 0fefefefeH + mov ebx, eax + jmp nf_solid +endif + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf7_11+2 + + mov al, [esi+2] + mov bl, al + xor bl, [esi+3] + and bl, 0aaH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf7_11-nf7_11)], bl + mov [edx+(nf7_12-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_13-nf7_11)], bl + mov [edx+(nf7_14-nf7_11)], bh + + mov al, [esi+4] + mov bl, al + xor bl, [esi+5] + and bl, 0aaH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf7_31-nf7_11)], bl + mov [edx+(nf7_32-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_33-nf7_11)], bl + mov [edx+(nf7_34-nf7_11)], bh + + lea edx, [edx+(nf7_51-nf7_11)] + + mov al, [esi+6] + mov bl, al + xor bl, [esi+7] + and bl, 0aaH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf7_51-nf7_51)], bl + mov [edx+(nf7_52-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_53-nf7_51)], bl + mov [edx+(nf7_54-nf7_51)], bh + + mov al, [esi+8] + mov bl, al + xor bl, [esi+9] + and bl, 0aaH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf7_71-nf7_51)], bl + mov [edx+(nf7_72-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_73-nf7_51)], bl + mov [edx+(nf7_74-nf7_51)], bh + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi,nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + jmp nf7_0 ; flush prefetch + ALIGN 4 +nf7_0: +nf7_11: mov ax, bx + shl eax, 16 +nf7_12: mov ax, bx + mov [edi], eax +nf7_13: mov ax, bx + shl eax, 16 +nf7_14: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_31: mov ax, bx + shl eax, 16 +nf7_32: mov ax, bx + mov [edi], eax +nf7_33: mov ax, bx + shl eax, 16 +nf7_34: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_51: mov ax, bx + shl eax, 16 +nf7_52: mov ax, bx + mov [edi], eax +nf7_53: mov ax, bx + shl eax, 16 +nf7_54: mov ax, bx + mov [edi+4], eax + add edi, esi + +nf7_71: mov ax, bx + shl eax, 16 +nf7_72: mov ax, bx + mov [edi], eax +nf7_73: mov ax, bx + shl eax, 16 +nf7_74: mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 10 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf7+16 +nf23: ; low 4x4x1 (4 bytes) + + xor eax, eax + lea ecx, nfpk_mov4l + lea edx, byte ptr ds:nf23_11+2 + + mov al, [esi+2] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_11-nf23_11)], bl + mov [edx+(nf23_12-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_13-nf23_11)], bl + mov [edx+(nf23_14-nf23_11)], bh + + mov al, [esi+2] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_31-nf23_11)], bl + mov [edx+(nf23_32-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_33-nf23_11)], bl + mov [edx+(nf23_34-nf23_11)], bh + + + mov al, [esi+3] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_51-nf23_11)], bl + mov [edx+(nf23_52-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_53-nf23_11)], bl + mov [edx+(nf23_54-nf23_11)], bh + + mov al, [esi+3] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_71-nf23_11)], bl + mov [edx+(nf23_72-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_73-nf23_11)], bl + mov [edx+(nf23_74-nf23_11)], bh + + mov edx, nf_width + + ; load bx,cx with 00,11 color combinations + mov bx, [esi] + mov cl, bh + mov bh, bl + mov ch, cl + + jmp nf23_0 ; flush prefetch + ALIGN 4 +nf23_0: + +nf23_11:mov ax, bx + shl eax, 16 +nf23_12:mov ax, bx + mov [edi], eax + +nf23_13:mov ax, bx + shl eax, 16 +nf23_14:mov ax, bx + mov [edi+4], eax + add edi, edx + +nf23_31:mov ax, bx + shl eax, 16 +nf23_32:mov ax, bx + mov [edi], eax + +nf23_33:mov ax, bx + shl eax, 16 +nf23_34:mov ax, bx + mov [edi+4], eax + add edi, edx + +nf23_51:mov ax, bx + shl eax, 16 +nf23_52:mov ax, bx + mov [edi], eax + +nf23_53:mov ax, bx + shl eax, 16 +nf23_54:mov ax, bx + mov [edi+4], eax + add edi, edx + +nf23_71:mov ax, bx + shl eax, 16 +nf23_72:mov ax, bx + mov [edi], eax + +nf23_73:mov ax, bx + shl eax, 16 +nf23_74:mov ax, bx + mov [edi+4], eax + + sub edi, nfpk_back_right + add esi, 4 + retn + +;---------------------------------------- + ALIGN 4 +nf8: ; 2x2 4x4x1 (16 bytes) + + mov ax, [esi] + cmp al, ah + ja nf24 + + ; Note: This could be made faster with a new (16 16-bit entry) table. + + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf8_11+2 + + mov al, [esi+2] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_11-nf8_11)], bl + mov [edx+(nf8_12-nf8_11)], bh + + mov al, [esi+3] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_21-nf8_11)], bl + mov [edx+(nf8_22-nf8_11)], bh + + + mov al, [esi+6] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_31-nf8_11)], bl + mov [edx+(nf8_32-nf8_11)], bh + + mov al, [esi+7] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_41-nf8_11)], bl + mov [edx+(nf8_42-nf8_11)], bh + + add edx, nf8_51-nf8_11 + + mov al, [esi+10] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_51-nf8_51)], bl + mov [edx+(nf8_52-nf8_51)], bh + + mov al, [esi+11] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_61-nf8_51)], bl + mov [edx+(nf8_62-nf8_51)], bh + + + mov al, [esi+14] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_71-nf8_51)], bl + mov [edx+(nf8_72-nf8_51)], bh + + mov al, [esi+15] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf8_81-nf8_51)], bl + mov [edx+(nf8_82-nf8_51)], bh + + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf8_0 ; flush prefetch + ALIGN 4 +nf8_0: +nf8_11: mov ax, bx + shl eax, 16 +nf8_12: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_21: mov ax, bx + shl eax, 16 +nf8_22: mov ax, bx + mov [edi], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+4] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_31: mov ax, bx + shl eax, 16 +nf8_32: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_41: mov ax, bx + shl eax, 16 +nf8_42: mov ax, bx + mov [edi], eax + add edi, esi + + lea eax, [esi*4-4] + sub edi, eax + + mov eax, [esp] + mov cx, [eax+8] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_51: mov ax, bx + shl eax, 16 +nf8_52: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_61: mov ax, bx + shl eax, 16 +nf8_62: mov ax, bx + mov [edi], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+12] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf8_71: mov ax, bx + shl eax, 16 +nf8_72: mov ax, bx + mov [edi], eax + add edi, esi + +nf8_81: mov ax, bx + shl eax, 16 +nf8_82: mov ax, bx + mov [edi], eax + + pop esi + pop ebp + add esi, 16 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+16 +nf24: ; 2x1 4x8x1 (12 bytes) + + mov ax, [esi+6] + cmp al, ah + ja nf40 + + ; Note: This could be made faster with a new (16 16-bit entry) table. + + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf24_11+2 + + mov al, [esi+2] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_11-nf24_11)], bl + mov [edx+(nf24_12-nf24_11)], bh + + mov al, [esi+3] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_21-nf24_11)], bl + mov [edx+(nf24_22-nf24_11)], bh + + mov al, [esi+4] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_31-nf24_11)], bl + mov [edx+(nf24_32-nf24_11)], bh + + mov al, [esi+5] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_41-nf24_11)], bl + mov [edx+(nf24_42-nf24_11)], bh + + add edx, nf24_51-nf24_11 + + mov al, [esi+8] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_51-nf24_51)], bl + mov [edx+(nf24_52-nf24_51)], bh + + mov al, [esi+9] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_61-nf24_51)], bl + mov [edx+(nf24_62-nf24_51)], bh + + mov al, [esi+10] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_71-nf24_51)], bl + mov [edx+(nf24_72-nf24_51)], bh + + mov al, [esi+11] + mov bl, al + shr bl, 4 + xor bl, al + and bl, 0aH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf24_81-nf24_51)], bl + mov [edx+(nf24_82-nf24_51)], bh + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf24_0 ; flush prefetch + ALIGN 4 +nf24_0: +nf24_11:mov ax, bx + shl eax, 16 +nf24_12:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_21:mov ax, bx + shl eax, 16 +nf24_22:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_31:mov ax, bx + shl eax, 16 +nf24_32:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_41:mov ax, bx + shl eax, 16 +nf24_42:mov ax, bx + mov [edi], eax + add edi, esi + + lea eax, [esi*4-4] + sub edi, eax + + mov eax, [esp] + mov cx, [eax+6] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf24_51:mov ax, bx + shl eax, 16 +nf24_52:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_61:mov ax, bx + shl eax, 16 +nf24_62:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_71:mov ax, bx + shl eax, 16 +nf24_72:mov ax, bx + mov [edi], eax + add edi, esi + +nf24_81:mov ax, bx + shl eax, 16 +nf24_82:mov ax, bx + mov [edi], eax + + pop esi + pop ebp + add esi, 12 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+32 +nf40: ; 1x2 8x4x1 (12 bytes) + xor eax, eax + lea ecx, nfpk_mov8 + lea edx, byte ptr ds:nf40_11+2 + + mov al, [esi+2] + mov bl, al + xor bl, [esi+3] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf40_11-nf40_11)], bl + mov [edx+(nf40_12-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_13-nf40_11)], bl + mov [edx+(nf40_14-nf40_11)], bh + + mov al, [esi+4] + mov bl, al + xor bl, [esi+5] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf40_31-nf40_11)], bl + mov [edx+(nf40_32-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_33-nf40_11)], bl + mov [edx+(nf40_34-nf40_11)], bh + + add edx, nf40_51-nf40_11 + + mov al, [esi+8] + mov bl, al + xor bl, [esi+9] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf40_51-nf40_51)], bl + mov [edx+(nf40_52-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_53-nf40_51)], bl + mov [edx+(nf40_54-nf40_51)], bh + + mov al, [esi+10] + mov bl, al + xor bl, [esi+11] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf40_71-nf40_51)], bl + mov [edx+(nf40_72-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_73-nf40_51)], bl + mov [edx+(nf40_74-nf40_51)], bh + + push ebp + push esi + ; load bx,dx,cx,bp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + mov cx, [esi] + mov esi, nf_width + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + + jmp nf40_0 ; flush prefetch + ALIGN 4 +nf40_0: +nf40_11:mov ax, bx + shl eax, 16 +nf40_12:mov ax, bx + mov [edi], eax +nf40_13:mov ax, bx + shl eax, 16 +nf40_14:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_31:mov ax, bx + shl eax, 16 +nf40_32:mov ax, bx + mov [edi], eax +nf40_33:mov ax, bx + shl eax, 16 +nf40_34:mov ax, bx + mov [edi+4], eax + add edi, esi + + mov eax, [esp] + mov cx, [eax+6] + mov bl,cl + mov bh,cl + mov dl,ch + mov dh,cl + mov al,ch + mov ah,ch + mov ebp,eax + +nf40_51:mov ax, bx + shl eax, 16 +nf40_52:mov ax, bx + mov [edi], eax +nf40_53:mov ax, bx + shl eax, 16 +nf40_54:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf40_71:mov ax, bx + shl eax, 16 +nf40_72:mov ax, bx + mov [edi], eax +nf40_73:mov ax, bx + shl eax, 16 +nf40_74:mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +nf9: ; 8x8x2 (20 bytes) + + mov eax, [esi] + cmp al, ah + ja nf41 + + shr eax, 16 + cmp al, ah + ja nf25 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf9_11+1 + + mov al, [esi+4] + mov bl, al + xor bl, [esi+6] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_11-nf9_11)], bl + mov [edx+(nf9_12-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_13-nf9_11)], bl + mov [edx+(nf9_14-nf9_11)], bh + + mov al, [esi+5] + mov bl, al + xor bl, [esi+7] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_15-nf9_11)], bl + mov [edx+(nf9_16-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_17-nf9_11)], bl + mov [edx+(nf9_18-nf9_11)], bh + + + mov al, [esi+8] + mov bl, al + xor bl, [esi+10] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_31-nf9_11)], bl + mov [edx+(nf9_32-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_33-nf9_11)], bl + mov [edx+(nf9_34-nf9_11)], bh + + mov al, [esi+9] + mov bl, al + xor bl, [esi+11] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_35-nf9_11)], bl + mov [edx+(nf9_36-nf9_11)], bh + shr ebx, 16 + mov [edx+(nf9_37-nf9_11)], bl + mov [edx+(nf9_38-nf9_11)], bh + + lea edx, [edx+(nf9_51-nf9_11)] + + mov al, [esi+12] + mov bl, al + xor bl, [esi+14] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_51-nf9_51)], bl + mov [edx+(nf9_52-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_53-nf9_51)], bl + mov [edx+(nf9_54-nf9_51)], bh + + mov al, [esi+13] + mov bl, al + xor bl, [esi+15] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_55-nf9_51)], bl + mov [edx+(nf9_56-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_57-nf9_51)], bl + mov [edx+(nf9_58-nf9_51)], bh + + + mov al, [esi+16] + mov bl, al + xor bl, [esi+18] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_71-nf9_51)], bl + mov [edx+(nf9_72-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_73-nf9_51)], bl + mov [edx+(nf9_74-nf9_51)], bh + + mov al, [esi+17] + mov bl, al + xor bl, [esi+19] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf9_75-nf9_51)], bl + mov [edx+(nf9_76-nf9_51)], bh + shr ebx, 16 + mov [edx+(nf9_77-nf9_51)], bl + mov [edx+(nf9_78-nf9_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf9_0 ; flush prefetch + ALIGN 4 +nf9_0: +nf9_11: mov al, bl +nf9_12: mov ah, bl + shl eax, 16 +nf9_13: mov al, bl +nf9_14: mov ah, bl + mov [edi], eax + +nf9_15: mov al, bl +nf9_16: mov ah, bl + shl eax, 16 +nf9_17: mov al, bl +nf9_18: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_31: mov al, bl +nf9_32: mov ah, bl + shl eax, 16 +nf9_33: mov al, bl +nf9_34: mov ah, bl + mov [edi], eax + +nf9_35: mov al, bl +nf9_36: mov ah, bl + shl eax, 16 +nf9_37: mov al, bl +nf9_38: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_51: mov al, bl +nf9_52: mov ah, bl + shl eax, 16 +nf9_53: mov al, bl +nf9_54: mov ah, bl + mov [edi], eax + +nf9_55: mov al, bl +nf9_56: mov ah, bl + shl eax, 16 +nf9_57: mov al, bl +nf9_58: mov ah, bl + mov [edi+4], eax + add edi, edx + +nf9_71: mov al, bl +nf9_72: mov ah, bl + shl eax, 16 +nf9_73: mov al, bl +nf9_74: mov ah, bl + mov [edi], eax + +nf9_75: mov al, bl +nf9_76: mov ah, bl + shl eax, 16 +nf9_77: mov al, bl +nf9_78: mov ah, bl + mov [edi+4], eax + + add esi, 20 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +;nf9+16 +nf25: ; low 4x4x2 (8 bytes) + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 8 + jmp nf_solid +endif + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf25_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_14-nf25_11)], bl + mov [edx+(nf25_13-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_12-nf25_11)], bl + mov [edx+(nf25_11-nf25_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_24-nf25_11)], bl + mov [edx+(nf25_23-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_22-nf25_11)], bl + mov [edx+(nf25_21-nf25_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_34-nf25_11)], bl + mov [edx+(nf25_33-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_32-nf25_11)], bl + mov [edx+(nf25_31-nf25_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_44-nf25_11)], bl + mov [edx+(nf25_43-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_42-nf25_11)], bl + mov [edx+(nf25_41-nf25_11)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf25_0 ; flush prefetch + ALIGN 4 +nf25_0: +nf25_11:mov ah, bl + mov al, ah + shl eax, 16 +nf25_12:mov al, bl + mov ah, al + mov [edi], eax +nf25_13:mov ah, bl + mov al, ah + shl eax, 16 +nf25_14:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf25_21:mov ah, bl + mov al, ah + shl eax, 16 +nf25_22:mov al, bl + mov ah, al + mov [edi], eax +nf25_23:mov ah, bl + mov al, ah + shl eax, 16 +nf25_24:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf25_31:mov ah, bl + mov al, ah + shl eax, 16 +nf25_32:mov al, bl + mov ah, al + mov [edi], eax +nf25_33:mov ah, bl + mov al, ah + shl eax, 16 +nf25_34:mov al, bl + mov ah, al + mov [edi+4], eax + add edi, edx + +nf25_41:mov ah, bl + mov al, ah + shl eax, 16 +nf25_42:mov al, bl + mov ah, al + mov [edi], eax +nf25_43:mov ah, bl + mov al, ah + shl eax, 16 +nf25_44:mov al, bl + mov ah, al + mov [edi+4], eax + + add esi, 8 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+32 +nf41: ; low 4x8x2 (12 bytes) + shr eax, 16 + cmp al, ah + ja nf57 + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 12 + jmp nf_solid +endif + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf41_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_14-nf41_11)], bl + mov [edx+(nf41_13-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_12-nf41_11)], bl + mov [edx+(nf41_11-nf41_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_24-nf41_11)], bl + mov [edx+(nf41_23-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_22-nf41_11)], bl + mov [edx+(nf41_21-nf41_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_34-nf41_11)], bl + mov [edx+(nf41_33-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_32-nf41_11)], bl + mov [edx+(nf41_31-nf41_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_44-nf41_11)], bl + mov [edx+(nf41_43-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_42-nf41_11)], bl + mov [edx+(nf41_41-nf41_11)], bh + + lea edx, [edx+(nf41_51-nf41_11)] + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_54-nf41_51)], bl + mov [edx+(nf41_53-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_52-nf41_51)], bl + mov [edx+(nf41_51-nf41_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_64-nf41_51)], bl + mov [edx+(nf41_63-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_62-nf41_51)], bl + mov [edx+(nf41_61-nf41_51)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_74-nf41_51)], bl + mov [edx+(nf41_73-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_72-nf41_51)], bl + mov [edx+(nf41_71-nf41_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_84-nf41_51)], bl + mov [edx+(nf41_83-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_82-nf41_51)], bl + mov [edx+(nf41_81-nf41_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf41_0 ; flush prefetch + ALIGN 4 +nf41_0: +nf41_11:mov ah, bl + mov al, ah +nf41_21:mov ah, bl + shl eax, 16 +nf41_22:mov al, bl + mov ah, al +nf41_12:mov al, bl + mov [edi], eax +nf41_13:mov ah, bl + mov al, ah +nf41_23:mov ah, bl + shl eax, 16 +nf41_24:mov al, bl + mov ah, al +nf41_14:mov al, bl + mov [edi+4], eax + add edi, edx + +nf41_31:mov ah, bl + mov al, ah +nf41_41:mov ah, bl + shl eax, 16 +nf41_42:mov al, bl + mov ah, al +nf41_32:mov al, bl + mov [edi], eax +nf41_33:mov ah, bl + mov al, ah +nf41_43:mov ah, bl + shl eax, 16 +nf41_44:mov al, bl + mov ah, al +nf41_34:mov al, bl + mov [edi+4], eax + add edi, edx + +nf41_51:mov ah, bl + mov al, ah +nf41_61:mov ah, bl + shl eax, 16 +nf41_62:mov al, bl + mov ah, al +nf41_52:mov al, bl + mov [edi], eax +nf41_53:mov ah, bl + mov al, ah +nf41_63:mov ah, bl + shl eax, 16 +nf41_64:mov al, bl + mov ah, al +nf41_54:mov al, bl + mov [edi+4], eax + add edi, edx + +nf41_71:mov ah, bl + mov al, ah +nf41_81:mov ah, bl + shl eax, 16 +nf41_82:mov al, bl + mov ah, al +nf41_72:mov al, bl + mov [edi], eax +nf41_73:mov ah, bl + mov al, ah +nf41_83:mov ah, bl + shl eax, 16 +nf41_84:mov al, bl + mov ah, al +nf41_74:mov al, bl + mov [edi+4], eax + + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+48 +nf57: ; low 8x4x2 (12 bytes) + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf57_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_11-nf57_11)], bl + mov [edx+(nf57_12-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_13-nf57_11)], bl + mov [edx+(nf57_14-nf57_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_15-nf57_11)], bl + mov [edx+(nf57_16-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_17-nf57_11)], bl + mov [edx+(nf57_18-nf57_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_21-nf57_11)], bl + mov [edx+(nf57_22-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_23-nf57_11)], bl + mov [edx+(nf57_24-nf57_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_25-nf57_11)], bl + mov [edx+(nf57_26-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_27-nf57_11)], bl + mov [edx+(nf57_28-nf57_11)], bh + + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_31-nf57_11)], bl + mov [edx+(nf57_32-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_33-nf57_11)], bl + mov [edx+(nf57_34-nf57_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_35-nf57_11)], bl + mov [edx+(nf57_36-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_37-nf57_11)], bl + mov [edx+(nf57_38-nf57_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_41-nf57_11)], bl + mov [edx+(nf57_42-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_43-nf57_11)], bl + mov [edx+(nf57_44-nf57_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_45-nf57_11)], bl + mov [edx+(nf57_46-nf57_11)], bh + shr ebx, 16 + mov [edx+(nf57_47-nf57_11)], bl + mov [edx+(nf57_48-nf57_11)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf57_0 ; flush prefetch + ALIGN 4 +nf57_0: +nf57_11:mov al, bl +nf57_12:mov ah, bl + shl eax, 16 +nf57_13:mov al, bl +nf57_14:mov ah, bl + mov [edi], eax + +nf57_15:mov al, bl +nf57_16:mov ah, bl + shl eax, 16 +nf57_17:mov al, bl +nf57_18:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf57_21:mov al, bl +nf57_22:mov ah, bl + shl eax, 16 +nf57_23:mov al, bl +nf57_24:mov ah, bl + mov [edi], eax + +nf57_25:mov al, bl +nf57_26:mov ah, bl + shl eax, 16 +nf57_27:mov al, bl +nf57_28:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf57_31:mov al, bl +nf57_32:mov ah, bl + shl eax, 16 +nf57_33:mov al, bl +nf57_34:mov ah, bl + mov [edi], eax + +nf57_35:mov al, bl +nf57_36:mov ah, bl + shl eax, 16 +nf57_37:mov al, bl +nf57_38:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf57_41:mov al, bl +nf57_42:mov ah, bl + shl eax, 16 +nf57_43:mov al, bl +nf57_44:mov ah, bl + mov [edi], eax + +nf57_45:mov al, bl +nf57_46:mov ah, bl + shl eax, 16 +nf57_47:mov al, bl +nf57_48:mov ah, bl + mov [edi+4], eax + + add esi, 12 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf10: ; 2x2 4x4x2 (32 bytes) + + mov ax, [esi] + cmp al, ah + ja nf26 + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf10_11+1 + + mov al, [esi+4] + mov bl, al + xor bl, [esi+5] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_11-nf10_11)], bl + mov [edx+(nf10_12-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_13-nf10_11)], bl + mov [edx+(nf10_14-nf10_11)], bh + + mov al, [esi+6] + mov bl, al + xor bl, [esi+7] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_21-nf10_11)], bl + mov [edx+(nf10_22-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_23-nf10_11)], bl + mov [edx+(nf10_24-nf10_11)], bh + + mov al, [esi+12] + mov bl, al + xor bl, [esi+13] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_31-nf10_11)], bl + mov [edx+(nf10_32-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_33-nf10_11)], bl + mov [edx+(nf10_34-nf10_11)], bh + + mov al, [esi+14] + mov bl, al + xor bl, [esi+15] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_41-nf10_11)], bl + mov [edx+(nf10_42-nf10_11)], bh + shr ebx, 16 + mov [edx+(nf10_43-nf10_11)], bl + mov [edx+(nf10_44-nf10_11)], bh + + lea edx, [edx+(nf10_51-nf10_11)] + + mov al, [esi+20] + mov bl, al + xor bl, [esi+21] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_51-nf10_51)], bl + mov [edx+(nf10_52-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_53-nf10_51)], bl + mov [edx+(nf10_54-nf10_51)], bh + + mov al, [esi+22] + mov bl, al + xor bl, [esi+23] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_61-nf10_51)], bl + mov [edx+(nf10_62-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_63-nf10_51)], bl + mov [edx+(nf10_64-nf10_51)], bh + + mov al, [esi+28] + mov bl, al + xor bl, [esi+29] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_71-nf10_51)], bl + mov [edx+(nf10_72-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_73-nf10_51)], bl + mov [edx+(nf10_74-nf10_51)], bh + + mov al, [esi+30] + mov bl, al + xor bl, [esi+31] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf10_81-nf10_51)], bl + mov [edx+(nf10_82-nf10_51)], bh + shr ebx, 16 + mov [edx+(nf10_83-nf10_51)], bl + mov [edx+(nf10_84-nf10_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf10_0 ; flush prefetch + ALIGN 4 +nf10_0: +nf10_11:mov al, bl +nf10_12:mov ah, bl + shl eax, 16 +nf10_13:mov al, bl +nf10_14:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_21:mov al, bl +nf10_22:mov ah, bl + shl eax, 16 +nf10_23:mov al, bl +nf10_24:mov ah, bl + mov [edi], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+8] + mov cx, [esi+10] + +nf10_31:mov al, bl +nf10_32:mov ah, bl + shl eax, 16 +nf10_33:mov al, bl +nf10_34:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_41:mov al, bl +nf10_42:mov ah, bl + shl eax, 16 +nf10_43:mov al, bl +nf10_44:mov ah, bl + mov [edi], eax + add edi, edx + + lea eax, [edx*4-4] + sub edi, eax + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+16] + mov cx, [esi+18] + +nf10_51:mov al, bl +nf10_52:mov ah, bl + shl eax, 16 +nf10_53:mov al, bl +nf10_54:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_61:mov al, bl +nf10_62:mov ah, bl + shl eax, 16 +nf10_63:mov al, bl +nf10_64:mov ah, bl + mov [edi], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+24] + mov cx, [esi+26] + +nf10_71:mov al, bl +nf10_72:mov ah, bl + shl eax, 16 +nf10_73:mov al, bl +nf10_74:mov ah, bl + mov [edi], eax + add edi, edx + +nf10_81:mov al, bl +nf10_82:mov ah, bl + shl eax, 16 +nf10_83:mov al, bl +nf10_84:mov ah, bl + mov [edi], eax + + add esi, 32 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+16 +nf26: ; 2x1 4x8x2 (24 bytes) + + mov ax, [esi+12] + cmp al, ah + ja nf42 + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 24 + jmp nf_solid +endif + + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf26_11+1 + + mov al, [esi+4] + mov bl, al + xor bl, [esi+5] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_11-nf26_11)], bl + mov [edx+(nf26_12-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_13-nf26_11)], bl + mov [edx+(nf26_14-nf26_11)], bh + + mov al, [esi+6] + mov bl, al + xor bl, [esi+7] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_21-nf26_11)], bl + mov [edx+(nf26_22-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_23-nf26_11)], bl + mov [edx+(nf26_24-nf26_11)], bh + + mov al, [esi+8] + mov bl, al + xor bl, [esi+9] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_31-nf26_11)], bl + mov [edx+(nf26_32-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_33-nf26_11)], bl + mov [edx+(nf26_34-nf26_11)], bh + + mov al, [esi+10] + mov bl, al + xor bl, [esi+11] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_41-nf26_11)], bl + mov [edx+(nf26_42-nf26_11)], bh + shr ebx, 16 + mov [edx+(nf26_43-nf26_11)], bl + mov [edx+(nf26_44-nf26_11)], bh + + lea edx, [edx+(nf26_51-nf26_11)] + + mov al, [esi+16] + mov bl, al + xor bl, [esi+17] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_51-nf26_51)], bl + mov [edx+(nf26_52-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_53-nf26_51)], bl + mov [edx+(nf26_54-nf26_51)], bh + + mov al, [esi+18] + mov bl, al + xor bl, [esi+19] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_61-nf26_51)], bl + mov [edx+(nf26_62-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_63-nf26_51)], bl + mov [edx+(nf26_64-nf26_51)], bh + + mov al, [esi+20] + mov bl, al + xor bl, [esi+21] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_71-nf26_51)], bl + mov [edx+(nf26_72-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_73-nf26_51)], bl + mov [edx+(nf26_74-nf26_51)], bh + + mov al, [esi+22] + mov bl, al + xor bl, [esi+23] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf26_81-nf26_51)], bl + mov [edx+(nf26_82-nf26_51)], bh + shr ebx, 16 + mov [edx+(nf26_83-nf26_51)], bl + mov [edx+(nf26_84-nf26_51)], bh + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf26_0 ; flush prefetch + ALIGN 4 +nf26_0: +nf26_11:mov al, bl +nf26_12:mov ah, bl + shl eax, 16 +nf26_13:mov al, bl +nf26_14:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_21:mov al, bl +nf26_22:mov ah, bl + shl eax, 16 +nf26_23:mov al, bl +nf26_24:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_31:mov al, bl +nf26_32:mov ah, bl + shl eax, 16 +nf26_33:mov al, bl +nf26_34:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_41:mov al, bl +nf26_42:mov ah, bl + shl eax, 16 +nf26_43:mov al, bl +nf26_44:mov ah, bl + mov [edi], eax + add edi, edx + + lea eax, [edx*4-4] + sub edi, eax + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+12] + mov cx, [esi+14] + +nf26_51:mov al, bl +nf26_52:mov ah, bl + shl eax, 16 +nf26_53:mov al, bl +nf26_54:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_61:mov al, bl +nf26_62:mov ah, bl + shl eax, 16 +nf26_63:mov al, bl +nf26_64:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_71:mov al, bl +nf26_72:mov ah, bl + shl eax, 16 +nf26_73:mov al, bl +nf26_74:mov ah, bl + mov [edi], eax + add edi, edx + +nf26_81:mov al, bl +nf26_82:mov ah, bl + shl eax, 16 +nf26_83:mov al, bl +nf26_84:mov ah, bl + mov [edi], eax + + add esi, 24 + sub edi, 4 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+32 +nf42: ; 1x2 8x4x2 (24 bytes) + +if 0 ;debug + mov eax, 0 + mov ebx, 0 + add esi, 24 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfpk_mov4 + lea edx, byte ptr ds:nf42_11+1 + + mov al, [esi+4] + mov bl, al + xor bl, [esi+6] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_11-nf42_11)], bl + mov [edx+(nf42_12-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_13-nf42_11)], bl + mov [edx+(nf42_14-nf42_11)], bh + + mov al, [esi+5] + mov bl, al + xor bl, [esi+7] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_15-nf42_11)], bl + mov [edx+(nf42_16-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_17-nf42_11)], bl + mov [edx+(nf42_18-nf42_11)], bh + + + mov al, [esi+8] + mov bl, al + xor bl, [esi+10] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_31-nf42_11)], bl + mov [edx+(nf42_32-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_33-nf42_11)], bl + mov [edx+(nf42_34-nf42_11)], bh + + mov al, [esi+9] + mov bl, al + xor bl, [esi+11] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_35-nf42_11)], bl + mov [edx+(nf42_36-nf42_11)], bh + shr ebx, 16 + mov [edx+(nf42_37-nf42_11)], bl + mov [edx+(nf42_38-nf42_11)], bh + + + lea edx, [edx+(nf42_51-nf42_11)] + + mov al, [esi+16] + mov bl, al + xor bl, [esi+18] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_51-nf42_51)], bl + mov [edx+(nf42_52-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_53-nf42_51)], bl + mov [edx+(nf42_54-nf42_51)], bh + + mov al, [esi+17] + mov bl, al + xor bl, [esi+19] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_55-nf42_51)], bl + mov [edx+(nf42_56-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_57-nf42_51)], bl + mov [edx+(nf42_58-nf42_51)], bh + + + mov al, [esi+20] + mov bl, al + xor bl, [esi+22] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_71-nf42_51)], bl + mov [edx+(nf42_72-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_73-nf42_51)], bl + mov [edx+(nf42_74-nf42_51)], bh + + mov al, [esi+21] + mov bl, al + xor bl, [esi+23] + and bl, 0ccH + xor al, bl + mov ebx, [ecx+eax*4] + mov [edx+(nf42_75-nf42_51)], bl + mov [edx+(nf42_76-nf42_51)], bh + shr ebx, 16 + mov [edx+(nf42_77-nf42_51)], bl + mov [edx+(nf42_78-nf42_51)], bh + + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi] + mov cx, [esi+2] + + mov edx, nf_width + jmp nf42_0 ; flush prefetch + ALIGN 4 +nf42_0: +nf42_11:mov al, bl +nf42_12:mov ah, bl + shl eax, 16 +nf42_13:mov al, bl +nf42_14:mov ah, bl + mov [edi], eax + +nf42_15:mov al, bl +nf42_16:mov ah, bl + shl eax, 16 +nf42_17:mov al, bl +nf42_18:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_31:mov al, bl +nf42_32:mov ah, bl + shl eax, 16 +nf42_33:mov al, bl +nf42_34:mov ah, bl + mov [edi], eax + +nf42_35:mov al, bl +nf42_36:mov ah, bl + shl eax, 16 +nf42_37:mov al, bl +nf42_38:mov ah, bl + mov [edi+4], eax + add edi, edx + + ; Load bl,bh,cl,ch with four colors + mov bx, [esi+12] + mov cx, [esi+14] + +nf42_51:mov al, bl +nf42_52:mov ah, bl + shl eax, 16 +nf42_53:mov al, bl +nf42_54:mov ah, bl + mov [edi], eax + +nf42_55:mov al, bl +nf42_56:mov ah, bl + shl eax, 16 +nf42_57:mov al, bl +nf42_58:mov ah, bl + mov [edi+4], eax + add edi, edx + +nf42_71:mov al, bl +nf42_72:mov ah, bl + shl eax, 16 +nf42_73:mov al, bl +nf42_74:mov ah, bl + mov [edi], eax + +nf42_75:mov al, bl +nf42_76:mov ah, bl + shl eax, 16 +nf42_77:mov al, bl +nf42_78:mov ah, bl + mov [edi+4], eax + + add esi, 24 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf11: ; 8x8x8 (64 bytes) +if 0 ;debug + add esi, 64 + mov eax, 0fefefefeH +; mov ebx, eax + mov ebx, 0 + jmp nf_solid +endif + mov edx, nf_width + mov ebx, 0ff00ff00H + + mov eax, [esi] ;0 + mov ecx, eax + xor ecx, [esi+8] + and ecx, ebx + xor eax, ecx + mov [edi], eax + mov eax, [esi+4] + mov ecx, eax + xor ecx, [esi+4+8] + and ecx, ebx + xor eax, ecx + mov [edi+4], eax + add edi, edx + mov eax, [esi+16] ;2 + mov ecx, eax + xor ecx, [esi+16+8] + and ecx, ebx + xor eax, ecx + mov [edi], eax + mov eax, [esi+20] + mov ecx, eax + xor ecx, [esi+20+8] + and ecx, ebx + xor eax, ecx + mov [edi+4], eax + add edi, edx + mov eax, [esi+32] ;4 + mov ecx, eax + xor ecx, [esi+32+8] + and ecx, ebx + xor eax, ecx + mov [edi], eax + mov eax, [esi+36] + mov ecx, eax + xor ecx, [esi+36+8] + and ecx, ebx + xor eax, ecx + mov [edi+4], eax + add edi, edx + mov eax, [esi+48] ;6 + mov ecx, eax + xor ecx, [esi+48+8] + and ecx, ebx + xor eax, ecx + mov [edi], eax + mov eax, [esi+52] + mov ecx, eax + xor ecx, [esi+52+8] + and ecx, ebx + xor eax, ecx + mov [edi+4], eax + + add esi, 64 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +;---------------------------------------- + ALIGN 4 +nf12: ; low 4x4x8 (16 bytes) + mov edx, nf_width + + mov eax, [esi] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + add edi, edx + + mov eax, [esi+4] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + add edi, edx + + mov eax, [esi+8] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + add edi, edx + + mov eax, [esi+12] + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi], ebx + shr eax, 16 + mov bl, ah + mov bh, ah + shl ebx, 16 + mov bl, al + mov bh, al + mov [edi+4], ebx + + sub edi, nfpk_back_right + add esi, 16 + retn + +;---------------------------------------- + ALIGN 4 +nf13: ; 2x2 4x4x0 (4 bytes) + mov edx, nf_width + + mov cl, [esi] + mov ch, cl + mov eax, ecx + shl eax, 16 + mov ax, cx + + mov cl, [esi+1] + mov ch, cl + mov ebx, ecx + shl ebx, 16 + mov bx, cx + + mov [edi], eax + mov [edi+4], ebx + mov [edi+edx], eax + mov [edi+edx+4], ebx + lea edi, [edi+edx*2] + + mov cl, [esi+2] + mov ch, cl + mov eax, ecx + shl eax, 16 + mov ax, cx + + mov cl, [esi+3] + mov ch, cl + mov ebx, ecx + shl ebx, 16 + mov bx, cx + + mov [edi], eax + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], ebx + + sub edi, nfpk_back_right + add esi, 4 + + retn + +;---------------------------------------- + ALIGN 4 +nf14: ; 8x8x0 (1 byte) + mov bl, [esi] ; Copy color into 8 positions + inc esi + mov bh, bl + mov eax, ebx + shl eax, 16 + mov ax, bx + mov ebx, eax +if 0 ;debug + mov eax, 080808080h + mov ebx, eax +endif + jmp nf_solid + + retn + +;---------------------------------------- + ALIGN 4 +nf15: ; mix 8x8x0 (2 bytes) + mov bx, [esi] ; Copy 2 colors into 8 positions + add esi, 2 ; in a checkerboard + mov ax, bx + shl eax, 16 + mov ax, bx + mov ebx, eax + rol ebx, 8 +if 0 ;debug + mov eax, 080808080h + mov ebx, eax +endif +nf_solid: + mov edx, nf_width + + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + add edi, edx + mov [edi], eax + mov [edi+4], eax + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +nfPkDecompD ENDP + +endif + +;--- + + .data + +; Constant tables + +nfhpk_mov4l LABEL DWORD +; low 4x1 in 8x1 (patch +1) +; mov eax, ebx/ecx +MOVH4L_REGS TEXTEQU > +%FOR m4, MOVH4L_REGS +% FOR m3, MOVH4L_REGS +% FOR m2, MOVH4L_REGS +% FOR m1, MOVH4L_REGS + BYTE m1,m2,m3,m4 + ENDM + ENDM + ENDM + ENDM + +nfhpk_mov8 LABEL DWORD +; 8x1 (each two bits select a pair of colors in a reg) +; low 4x2 in 8x2 (each two bits select a duplicated color in reg) +; (patch +1) +; mov ds:[edi+0/4/8/12], ebx/edx/ecx/ebp +; Note: Patched code specifies mov [ebp+0]... instead +; of mov [edi+0]... to insure that 8-bit offsets are +; used by the assembler even for offset of zero. +; +MOVH8_REGS TEXTEQU > +%FOR m4, MOVH8_REGS +% FOR m3, MOVH8_REGS +% FOR m2, MOVH8_REGS +% FOR m1, MOVH8_REGS + BYTE m1+047h,m2+047h,m3+047h,m4+047h + ENDM + ENDM + ENDM + ENDM + +nfhpk_mov4 LABEL DWORD +; 4x2 (patch +2) +; mov ax, bx/dx/cx/bp +; low 4x2 in 8x2 (patch +1) +; mov eax, ebx/edx/ecx/ebp +MOVH4_REGS TEXTEQU > +%FOR m4, MOVH4_REGS +% FOR m3, MOVH4_REGS +% FOR m2, MOVH4_REGS +% FOR m1, MOVH4_REGS + BYTE m1,m2,m3,m4 + ENDM + ENDM + ENDM + ENDM + + .code + +; Normal version (HiColor) +; + +if TRANS16 + +if 0 +Trans16 MACRO dst:req, idx:req, mask + mov dst, [idx] + ifnb + and dst, 07FFFh + endif + ENDM +elseif 0 +Trans16 MACRO dst:req, idx:req, mask + mov dst, [idx] + mov ax, dst + and ax, 0FFE0h + add dst, ax + ENDM +else +EXTERN nf_trans16_lo: WORD +EXTERN nf_trans16_hi: WORD +Trans16 MACRO dst:req, idx:req, mask + xor eax, eax + mov al, [idx] + mov dst, nf_trans16_lo[eax*2] + xor eax, eax + mov al, [idx+1] + or dst, nf_trans16_hi[eax*2] + ENDM +endif + +else + +Trans16 MACRO dst:req, idx:req, mask + mov dst, [idx] + ifnb + and dst, 07FFFh + endif + ENDM + +endif + +nfHPkDecomp PROC USES ESI EDI EBX, \ + ops:PTRBYTE, comp:PTRBYTE, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD + LOCAL tbuf: PTRBYTE + LOCAL new_row:DWORD + LOCAL DiffBufPtrs:DWORD + + LOCAL nfpk_back_right: DWORD + LOCAL wcnt:DWORD + LOCAL bcomp:PTRBYTE + + LOG_LABEL "StartPkDecomp" + +.data +nfhpk_OpTbl label dword + dword offset nf0 ; Prev Same (0) + dword offset nf1 ; No change (and copied to screen) (0) + dword offset nf2 ; Near shift from older part of current buf (1) + dword offset nf3 ; Near shift from newer part of current buf (1) + dword offset nf4 ; Near shift from previous buffer (1) + dword offset nf5 ; Far shift from previous buffer (2) + dword offset nf6 ; Far shift from current buffer (2) + ; [Or if COMPOPS, run of no changes (0)] + dword offset nf7 ; 8x8x1 (10 bytes) or low 4x4x1 (4 bytes) + dword offset nf8 ; 2x2 4x4x1 (16 bytes) or 2x1 4x8x1 (12 bytes) or 1x2 8x4x1 (12 bytes) + dword offset nf9 ; 8x8x2 (20 bytes) or low 4x4x2 (8 bytes) or + ; low 4x8x2 (12 bytes) or low 8x4x2 (12 bytes) + dword offset nf10 ; 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + dword offset nf11 ; 8x8x8 (64 bytes) + dword offset nf12 ; low 4x4x8 (16 bytes) + dword offset nf13 ; 2x2 4x4x0 (ie 2x2x8) (4 bytes) + dword offset nf14 ; 8x8x0 (1 byte) + dword offset nf15 ; mix 8x8x0 (2 bytes) +.code + +ifdef SYMANTEC + mov ebx, ds ; Allow DS to access code + mov ecx, 0 + mov ax, 3505h + int 21h +endif + + NF_DECOMP_INIT 1 + + mov eax, nf_back_right + sub eax, SWIDTH*2 + mov nfpk_back_right, eax + + mov esi, comp + mov edi, tbuf + + xor eax, eax + mov ax, [esi] + add eax, esi + mov bcomp, eax + add esi, 2 + +nf_StartRow: + mov eax, w + shr eax, 1 + mov wcnt,eax + ALIGN 4 +nf_NextPair: + dec wcnt + js nf_NextRow + mov ebx, ops + mov al, [ebx] + inc ebx + mov ops, ebx + + xor ebx, ebx + mov bl, al + shr bl, 4 + and eax, 0Fh + push offset nf_NextPair + push nfhpk_OpTbl[ebx*4] + jmp nfhpk_OpTbl[eax*4] + +nf_NextRow: + add edi, new_row + dec h + jnz nf_StartRow + LOG_LABEL "EndPkDecomp" + +ifdef SYMANTEC + mov ebx, ds ; Disable DS from accessing code + mov ecx, offset DGROUP:_data_bottom[-1] + mov ax, 3505h + int 21h +endif + ret + +;---------------------------------------- + ALIGN 4 +nf0: ; No change from previous buffer + mov eax, DiffBufPtrs + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf1: ; No change (and copied to screen) +if 0 ;debug + mov ebx, 0 + jmp nf_solid +endif + add edi, SWIDTH*2 + retn + +;---------------------------------------- + ALIGN 4 +nf2: ; Near shift from older part of current buffer + xor eax, eax + mov ebx, bcomp + inc bcomp + mov al, [ebx] + mov ax, nfpk_ShiftP2[eax*2] +nf_xyc_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24-1 + add eax, nfpk_ShiftY[ebx*4] + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf3: ; Near shift from newer part of current buffer + xor eax, eax + mov ebx, bcomp + inc bcomp + mov al, [ebx] + mov ax, nfpk_ShiftP2[eax*2] + neg al + neg ah + jmp nf_xyc_shift + +;---------------------------------------- + ALIGN 4 +nf4: ; Near shift from previous buffer + xor eax, eax + mov ebx, bcomp + inc bcomp + mov al, [ebx] + mov ax, nfpk_ShiftP1[eax*2] + jmp nf_xyp_shift + +;---------------------------------------- + ALIGN 4 +nf5: ; Far shift from previous buffer + mov ax, [esi] + add esi, 2 +nf_xyp_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24-1 + add eax, nfpk_ShiftY[ebx*4] + add eax, DiffBufPtrs + jmp nf_shift + +;---------------------------------------- + ALIGN 4 + +nf6: ; Far shift from current buffer + mov ax, [esi] + add esi, 2 + jmp nf_xyc_shift + +;---------------------------------------- + ALIGN 4 +nf_shift: +if 0 ;debug + mov ebx, 0 + jmp nf_solid +endif + mov ebx, esi ; save esi + lea esi, [edi+eax] + mov edx, nf_width + + REPEAT 7 + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + mov eax, [esi+8] + mov [edi+8], eax + mov eax, [esi+12] + mov [edi+12], eax + add esi, edx + add edi, edx + ENDM + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + mov eax, [esi+8] + mov [edi+8], eax + mov eax, [esi+12] + mov [edi+12], eax + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + mov esi, ebx ; restore esi + retn + +;---------------------------------------- + ALIGN 4 +nf7: ; 8x8x1 (12 bytes) + + test word ptr [esi], 08000h + jnz nf23 + +if 0 ;debug + add esi, 12 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov8 + lea edx, byte ptr ds:nf7_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_11-nf7_11)], bl + mov [edx+(nf7_12-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_13-nf7_11)], bl + mov [edx+(nf7_14-nf7_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_21-nf7_11)], bl + mov [edx+(nf7_22-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_23-nf7_11)], bl + mov [edx+(nf7_24-nf7_11)], bh + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_31-nf7_11)], bl + mov [edx+(nf7_32-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_33-nf7_11)], bl + mov [edx+(nf7_34-nf7_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_41-nf7_11)], bl + mov [edx+(nf7_42-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_43-nf7_11)], bl + mov [edx+(nf7_44-nf7_11)], bh + + lea edx, [edx+(nf7_51-nf7_11)] + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_51-nf7_51)], bl + mov [edx+(nf7_52-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_53-nf7_51)], bl + mov [edx+(nf7_54-nf7_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_61-nf7_51)], bl + mov [edx+(nf7_62-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_63-nf7_51)], bl + mov [edx+(nf7_64-nf7_51)], bh + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_71-nf7_51)], bl + mov [edx+(nf7_72-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_73-nf7_51)], bl + mov [edx+(nf7_74-nf7_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_81-nf7_51)], bl + mov [edx+(nf7_82-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_83-nf7_51)], bl + mov [edx+(nf7_84-nf7_51)], bh + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). +if TRANS16 + Trans16 cx, esi+2 + shl ecx, 16 + Trans16 cx, esi +else + mov ecx, [esi] +endif + mov esi,nf_width + mov edx, ecx + + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf7_0 ; flush prefetch + ALIGN 4 +nf7_0: +nf7_11: mov [ebp+0], ebx +nf7_12: mov [ebp+4], ebx +nf7_13: mov [ebp+8], ebx +nf7_14: mov [ebp+12], ebx + add edi, esi + +nf7_21: mov [ebp+0], ebx +nf7_22: mov [ebp+4], ebx +nf7_23: mov [ebp+8], ebx +nf7_24: mov [ebp+12], ebx + add edi, esi + +nf7_31: mov [ebp+0], ebx +nf7_32: mov [ebp+4], ebx +nf7_33: mov [ebp+8], ebx +nf7_34: mov [ebp+12], ebx + add edi, esi + +nf7_41: mov [ebp+0], ebx +nf7_42: mov [ebp+4], ebx +nf7_43: mov [ebp+8], ebx +nf7_44: mov [ebp+12], ebx + add edi, esi + +nf7_51: mov [ebp+0], ebx +nf7_52: mov [ebp+4], ebx +nf7_53: mov [ebp+8], ebx +nf7_54: mov [ebp+12], ebx + add edi, esi + +nf7_61: mov [ebp+0], ebx +nf7_62: mov [ebp+4], ebx +nf7_63: mov [ebp+8], ebx +nf7_64: mov [ebp+12], ebx + add edi, esi + +nf7_71: mov [ebp+0], ebx +nf7_72: mov [ebp+4], ebx +nf7_73: mov [ebp+8], ebx +nf7_74: mov [ebp+12], ebx + add edi, esi + +nf7_81: mov [ebp+0], ebx +nf7_82: mov [ebp+4], ebx +nf7_83: mov [ebp+8], ebx +nf7_84: mov [ebp+12], ebx + + pop esi + pop ebp + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf7+16 +nf23: ; low 4x4x1 (6 bytes) + +if 0 ;debug + add esi, 6 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov4l + lea edx, byte ptr ds:nf23_11+1 + + mov al, [esi+4] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_11-nf23_11)], bl + mov [edx+(nf23_12-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_13-nf23_11)], bl + mov [edx+(nf23_14-nf23_11)], bh + + mov al, [esi+4] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_31-nf23_11)], bl + mov [edx+(nf23_32-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_33-nf23_11)], bl + mov [edx+(nf23_34-nf23_11)], bh + + + mov al, [esi+5] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_51-nf23_11)], bl + mov [edx+(nf23_52-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_53-nf23_11)], bl + mov [edx+(nf23_54-nf23_11)], bh + + mov al, [esi+5] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_71-nf23_11)], bl + mov [edx+(nf23_72-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_73-nf23_11)], bl + mov [edx+(nf23_74-nf23_11)], bh + + mov edx, nf_width + + ; load ebx,ecx with 00,11 color combinations +if TRANS16 + Trans16 cx, esi, 1 + shrd ebx, ecx, 16 + mov bx, cx + Trans16 cx, esi+2 + shrd eax, ecx, 16 + mov ax, cx + mov ecx, eax +else + mov ebx, [esi] + and ebx, 07FFF7FFFh + mov ecx, ebx + ror ebx, 16 + xchg bx,cx +endif + + jmp nf23_0 ; flush prefetch + ALIGN 4 +nf23_0: + +nf23_11:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_12:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_13:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_14:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + lea edi, [edi+edx*2] + +nf23_31:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_32:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_33:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_34:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + lea edi, [edi+edx*2] + +nf23_51:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_52:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_53:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_54:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + lea edi, [edi+edx*2] + +nf23_71:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_72:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_73:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_74:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + add edi, edx + + sub edi, nfpk_back_right + add esi, 6 + retn + +;---------------------------------------- + ALIGN 4 +nf8: ; 2x2 4x4x1 (24 bytes) + + test word ptr [esi], 08000h + jnz nf24 + +if 0 ;debug + add esi, 24 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov8 + lea edx, byte ptr ds:nf8_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_11-nf8_11)], bl + mov [edx+(nf8_12-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_13-nf8_11)], bl + mov [edx+(nf8_14-nf8_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_21-nf8_11)], bl + mov [edx+(nf8_22-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_23-nf8_11)], bl + mov [edx+(nf8_24-nf8_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_31-nf8_11)], bl + mov [edx+(nf8_32-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_33-nf8_11)], bl + mov [edx+(nf8_34-nf8_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_41-nf8_11)], bl + mov [edx+(nf8_42-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_43-nf8_11)], bl + mov [edx+(nf8_44-nf8_11)], bh + + add edx, nf8_51-nf8_11 + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_51-nf8_51)], bl + mov [edx+(nf8_52-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_53-nf8_51)], bl + mov [edx+(nf8_54-nf8_51)], bh + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_61-nf8_51)], bl + mov [edx+(nf8_62-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_63-nf8_51)], bl + mov [edx+(nf8_64-nf8_51)], bh + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_71-nf8_51)], bl + mov [edx+(nf8_72-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_73-nf8_51)], bl + mov [edx+(nf8_74-nf8_51)], bh + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_81-nf8_51)], bl + mov [edx+(nf8_82-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_83-nf8_51)], bl + mov [edx+(nf8_84-nf8_51)], bh + + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). +if TRANS16 + Trans16 cx, esi+18+2 + shl ecx, 16 + Trans16 cx, esi+18 + push ecx + + Trans16 cx, esi+12+2 + shl ecx, 16 + Trans16 cx, esi+12 + push ecx + + Trans16 cx, esi+6+2 + shl ecx, 16 + Trans16 cx, esi+6 + push ecx + + Trans16 cx, esi+2 + shl ecx, 16 + Trans16 cx, esi +else + mov ecx, [esi] +endif + mov esi,nf_width + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf8_0 ; flush prefetch + ALIGN 4 +nf8_0: +nf8_11: mov [ebp+0], ebx +nf8_12: mov [ebp+4], ebx + add edi, esi +nf8_13: mov [ebp+0], ebx +nf8_14: mov [ebp+4], ebx + add edi, esi + +nf8_21: mov [ebp+0], ebx +nf8_22: mov [ebp+4], ebx + add edi, esi +nf8_23: mov [ebp+0], ebx +nf8_24: mov [ebp+4], ebx + add edi, esi + +if TRANS16 + pop ecx +else + mov eax, [esp] + mov ecx, [eax+6] +endif + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + +nf8_31: mov [ebp+0], ebx +nf8_32: mov [ebp+4], ebx + add edi, esi +nf8_33: mov [ebp+0], ebx +nf8_34: mov [ebp+4], ebx + add edi, esi + +nf8_41: mov [ebp+0], ebx +nf8_42: mov [ebp+4], ebx + add edi, esi +nf8_43: mov [ebp+0], ebx +nf8_44: mov [ebp+4], ebx + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + +if TRANS16 + pop ecx +else + mov eax, [esp] + mov ecx, [eax+12] +endif + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf8_51: mov [ebp+0], ebx +nf8_52: mov [ebp+4], ebx + add edi, esi +nf8_53: mov [ebp+0], ebx +nf8_54: mov [ebp+4], ebx + add edi, esi + +nf8_61: mov [ebp+0], ebx +nf8_62: mov [ebp+4], ebx + add edi, esi +nf8_63: mov [ebp+0], ebx +nf8_64: mov [ebp+4], ebx + add edi, esi + +if TRANS16 + pop ecx +else + mov eax, [esp] + mov ecx, [eax+18] +endif + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf8_71: mov [ebp+0], ebx +nf8_72: mov [ebp+4], ebx + add edi, esi +nf8_73: mov [ebp+0], ebx +nf8_74: mov [ebp+4], ebx + add edi, esi + +nf8_81: mov [ebp+0], ebx +nf8_82: mov [ebp+4], ebx + add edi, esi +nf8_83: mov [ebp+0], ebx +nf8_84: mov [ebp+4], ebx + + pop esi + pop ebp + add esi, 24 + sub edi, 8 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+16 +nf24: ; 2x1 4x8x1 (16 bytes) + + test word ptr [esi+8], 08000h + jnz nf40 + +if 0 ;debug + add esi, 16 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov8 + lea edx, byte ptr ds:nf24_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_11-nf24_11)], bl + mov [edx+(nf24_12-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_13-nf24_11)], bl + mov [edx+(nf24_14-nf24_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_21-nf24_11)], bl + mov [edx+(nf24_22-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_23-nf24_11)], bl + mov [edx+(nf24_24-nf24_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_31-nf24_11)], bl + mov [edx+(nf24_32-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_33-nf24_11)], bl + mov [edx+(nf24_34-nf24_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_41-nf24_11)], bl + mov [edx+(nf24_42-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_43-nf24_11)], bl + mov [edx+(nf24_44-nf24_11)], bh + + add edx, nf24_51-nf24_11 + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_51-nf24_51)], bl + mov [edx+(nf24_52-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_53-nf24_51)], bl + mov [edx+(nf24_54-nf24_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_61-nf24_51)], bl + mov [edx+(nf24_62-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_63-nf24_51)], bl + mov [edx+(nf24_64-nf24_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_71-nf24_51)], bl + mov [edx+(nf24_72-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_73-nf24_51)], bl + mov [edx+(nf24_74-nf24_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_81-nf24_51)], bl + mov [edx+(nf24_82-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_83-nf24_51)], bl + mov [edx+(nf24_84-nf24_51)], bh + + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). +if TRANS16 + Trans16 cx, esi+8+2 + shl ecx, 16 + Trans16 cx, esi+8 + push ecx + + Trans16 cx, esi+2 + shl ecx, 16 + Trans16 cx, esi, 1 +else + mov ecx, [esi] + and ecx, 07FFF7FFFh +endif + mov esi,nf_width + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf24_0 ; flush prefetch + ALIGN 4 +nf24_0: + +nf24_11:mov [ebp+0], ebx +nf24_12:mov [ebp+4], ebx + add edi, esi +nf24_13:mov [ebp+0], ebx +nf24_14:mov [ebp+4], ebx + add edi, esi + +nf24_21:mov [ebp+0], ebx +nf24_22:mov [ebp+4], ebx + add edi, esi +nf24_23:mov [ebp+0], ebx +nf24_24:mov [ebp+4], ebx + add edi, esi + +nf24_31:mov [ebp+0], ebx +nf24_32:mov [ebp+4], ebx + add edi, esi +nf24_33:mov [ebp+0], ebx +nf24_34:mov [ebp+4], ebx + add edi, esi + +nf24_41:mov [ebp+0], ebx +nf24_42:mov [ebp+4], ebx + add edi, esi +nf24_43:mov [ebp+0], ebx +nf24_44:mov [ebp+4], ebx + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + +if TRANS16 + pop ecx +else + mov eax, [esp] + mov ecx, [eax+8] +endif + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf24_51:mov [ebp+0], ebx +nf24_52:mov [ebp+4], ebx + add edi, esi +nf24_53:mov [ebp+0], ebx +nf24_54:mov [ebp+4], ebx + add edi, esi + +nf24_61:mov [ebp+0], ebx +nf24_62:mov [ebp+4], ebx + add edi, esi +nf24_63:mov [ebp+0], ebx +nf24_64:mov [ebp+4], ebx + add edi, esi + +nf24_71:mov [ebp+0], ebx +nf24_72:mov [ebp+4], ebx + add edi, esi +nf24_73:mov [ebp+0], ebx +nf24_74:mov [ebp+4], ebx + add edi, esi + +nf24_81:mov [ebp+0], ebx +nf24_82:mov [ebp+4], ebx + add edi, esi +nf24_83:mov [ebp+0], ebx +nf24_84:mov [ebp+4], ebx + + pop esi + pop ebp + add esi, 16 + sub edi, 8 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+32 +nf40: ; 1x2 8x4x1 (16 bytes) + +if 0 ;debug + add esi, 16 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov8 + lea edx, byte ptr ds:nf40_11+1 + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_11-nf40_11)], bl + mov [edx+(nf40_12-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_13-nf40_11)], bl + mov [edx+(nf40_14-nf40_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_21-nf40_11)], bl + mov [edx+(nf40_22-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_23-nf40_11)], bl + mov [edx+(nf40_24-nf40_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_31-nf40_11)], bl + mov [edx+(nf40_32-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_33-nf40_11)], bl + mov [edx+(nf40_34-nf40_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_41-nf40_11)], bl + mov [edx+(nf40_42-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_43-nf40_11)], bl + mov [edx+(nf40_44-nf40_11)], bh + + add edx, nf40_51-nf40_11 + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_51-nf40_51)], bl + mov [edx+(nf40_52-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_53-nf40_51)], bl + mov [edx+(nf40_54-nf40_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_61-nf40_51)], bl + mov [edx+(nf40_62-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_63-nf40_51)], bl + mov [edx+(nf40_64-nf40_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_71-nf40_51)], bl + mov [edx+(nf40_72-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_73-nf40_51)], bl + mov [edx+(nf40_74-nf40_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_81-nf40_51)], bl + mov [edx+(nf40_82-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_83-nf40_51)], bl + mov [edx+(nf40_84-nf40_51)], bh + + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). +if TRANS16 + Trans16 cx, esi+8+2 + shl ecx, 16 + Trans16 cx, esi+8, 1 + push ecx + + Trans16 cx, esi+2 + shl ecx, 16 + Trans16 cx, esi, 1 +else + mov ecx, [esi] + and ecx, 07FFF7FFFh +endif + mov esi,nf_width + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf40_0 ; flush prefetch + ALIGN 4 +nf40_0: + +nf40_11:mov [ebp+0], ebx +nf40_12:mov [ebp+4], ebx +nf40_13:mov [ebp+8], ebx +nf40_14:mov [ebp+12], ebx + add edi, esi + +nf40_21:mov [ebp+0], ebx +nf40_22:mov [ebp+4], ebx +nf40_23:mov [ebp+8], ebx +nf40_24:mov [ebp+12], ebx + add edi, esi + +nf40_31:mov [ebp+0], ebx +nf40_32:mov [ebp+4], ebx +nf40_33:mov [ebp+8], ebx +nf40_34:mov [ebp+12], ebx + add edi, esi + +nf40_41:mov [ebp+0], ebx +nf40_42:mov [ebp+4], ebx +nf40_43:mov [ebp+8], ebx +nf40_44:mov [ebp+12], ebx + add edi, esi + +if TRANS16 + pop ecx +else + mov eax, [esp] + mov ecx, [eax+8] + and ecx, 07FFF7FFFh +endif + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf40_51:mov [ebp+0], ebx +nf40_52:mov [ebp+4], ebx +nf40_53:mov [ebp+8], ebx +nf40_54:mov [ebp+12], ebx + add edi, esi + +nf40_61:mov [ebp+0], ebx +nf40_62:mov [ebp+4], ebx +nf40_63:mov [ebp+8], ebx +nf40_64:mov [ebp+12], ebx + add edi, esi + +nf40_71:mov [ebp+0], ebx +nf40_72:mov [ebp+4], ebx +nf40_73:mov [ebp+8], ebx +nf40_74:mov [ebp+12], ebx + add edi, esi + +nf40_81:mov [ebp+0], ebx +nf40_82:mov [ebp+4], ebx +nf40_83:mov [ebp+8], ebx +nf40_84:mov [ebp+12], ebx + + pop esi + pop ebp + add esi, 16 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +nf9: ; 8x8x2 (24 bytes) + + test word ptr [esi], 08000h + jnz nf41 + + test word ptr [esi+4], 08000h + jnz nf25 + +if 0 ;debug + add esi, 24 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov4 + lea edx, byte ptr ds:nf9_11+2 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_11-nf9_11)], bh + mov [edx+(nf9_12-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_13-nf9_11)], bh + mov [edx+(nf9_14-nf9_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_15-nf9_11)], bh + mov [edx+(nf9_16-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_17-nf9_11)], bh + mov [edx+(nf9_18-nf9_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_21-nf9_11)], bh + mov [edx+(nf9_22-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_23-nf9_11)], bh + mov [edx+(nf9_24-nf9_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_25-nf9_11)], bh + mov [edx+(nf9_26-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_27-nf9_11)], bh + mov [edx+(nf9_28-nf9_11)], bl + + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_31-nf9_11)], bh + mov [edx+(nf9_32-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_33-nf9_11)], bh + mov [edx+(nf9_34-nf9_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_35-nf9_11)], bh + mov [edx+(nf9_36-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_37-nf9_11)], bh + mov [edx+(nf9_38-nf9_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_41-nf9_11)], bh + mov [edx+(nf9_42-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_43-nf9_11)], bh + mov [edx+(nf9_44-nf9_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_45-nf9_11)], bh + mov [edx+(nf9_46-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_47-nf9_11)], bh + mov [edx+(nf9_48-nf9_11)], bl + + + lea edx, [edx+(nf9_51-nf9_11)] + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_51-nf9_51)], bh + mov [edx+(nf9_52-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_53-nf9_51)], bh + mov [edx+(nf9_54-nf9_51)], bl + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_55-nf9_51)], bh + mov [edx+(nf9_56-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_57-nf9_51)], bh + mov [edx+(nf9_58-nf9_51)], bl + + + mov al, [esi+18] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_61-nf9_51)], bh + mov [edx+(nf9_62-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_63-nf9_51)], bh + mov [edx+(nf9_64-nf9_51)], bl + + mov al, [esi+19] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_65-nf9_51)], bh + mov [edx+(nf9_66-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_67-nf9_51)], bh + mov [edx+(nf9_68-nf9_51)], bl + + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_71-nf9_51)], bh + mov [edx+(nf9_72-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_73-nf9_51)], bh + mov [edx+(nf9_74-nf9_51)], bl + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_75-nf9_51)], bh + mov [edx+(nf9_76-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_77-nf9_51)], bh + mov [edx+(nf9_78-nf9_51)], bl + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_81-nf9_51)], bh + mov [edx+(nf9_82-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_83-nf9_51)], bh + mov [edx+(nf9_84-nf9_51)], bl + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_85-nf9_51)], bh + mov [edx+(nf9_86-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_87-nf9_51)], bh + mov [edx+(nf9_88-nf9_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors +if TRANS16 + Trans16 bx, esi + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 +else + mov bx, [esi] + mov dx, [esi+2] + mov cx, [esi+4] + mov bp, [esi+6] +endif + mov esi, nf_width + + jmp nf9_0 ; flush prefetch + ALIGN 4 +nf9_0: + +nf9_11: mov ax, bx + shl eax, 16 +nf9_12: mov ax, bx + mov [edi], eax +nf9_13: mov ax, bx + shl eax, 16 +nf9_14: mov ax, bx + mov [edi+4], eax +nf9_15: mov ax, bx + shl eax, 16 +nf9_16: mov ax, bx + mov [edi+8], eax +nf9_17: mov ax, bx + shl eax, 16 +nf9_18: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_21: mov ax, bx + shl eax, 16 +nf9_22: mov ax, bx + mov [edi], eax +nf9_23: mov ax, bx + shl eax, 16 +nf9_24: mov ax, bx + mov [edi+4], eax +nf9_25: mov ax, bx + shl eax, 16 +nf9_26: mov ax, bx + mov [edi+8], eax +nf9_27: mov ax, bx + shl eax, 16 +nf9_28: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_31: mov ax, bx + shl eax, 16 +nf9_32: mov ax, bx + mov [edi], eax +nf9_33: mov ax, bx + shl eax, 16 +nf9_34: mov ax, bx + mov [edi+4], eax +nf9_35: mov ax, bx + shl eax, 16 +nf9_36: mov ax, bx + mov [edi+8], eax +nf9_37: mov ax, bx + shl eax, 16 +nf9_38: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_41: mov ax, bx + shl eax, 16 +nf9_42: mov ax, bx + mov [edi], eax +nf9_43: mov ax, bx + shl eax, 16 +nf9_44: mov ax, bx + mov [edi+4], eax +nf9_45: mov ax, bx + shl eax, 16 +nf9_46: mov ax, bx + mov [edi+8], eax +nf9_47: mov ax, bx + shl eax, 16 +nf9_48: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_51: mov ax, bx + shl eax, 16 +nf9_52: mov ax, bx + mov [edi], eax +nf9_53: mov ax, bx + shl eax, 16 +nf9_54: mov ax, bx + mov [edi+4], eax +nf9_55: mov ax, bx + shl eax, 16 +nf9_56: mov ax, bx + mov [edi+8], eax +nf9_57: mov ax, bx + shl eax, 16 +nf9_58: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_61: mov ax, bx + shl eax, 16 +nf9_62: mov ax, bx + mov [edi], eax +nf9_63: mov ax, bx + shl eax, 16 +nf9_64: mov ax, bx + mov [edi+4], eax +nf9_65: mov ax, bx + shl eax, 16 +nf9_66: mov ax, bx + mov [edi+8], eax +nf9_67: mov ax, bx + shl eax, 16 +nf9_68: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_71: mov ax, bx + shl eax, 16 +nf9_72: mov ax, bx + mov [edi], eax +nf9_73: mov ax, bx + shl eax, 16 +nf9_74: mov ax, bx + mov [edi+4], eax +nf9_75: mov ax, bx + shl eax, 16 +nf9_76: mov ax, bx + mov [edi+8], eax +nf9_77: mov ax, bx + shl eax, 16 +nf9_78: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_81: mov ax, bx + shl eax, 16 +nf9_82: mov ax, bx + mov [edi], eax +nf9_83: mov ax, bx + shl eax, 16 +nf9_84: mov ax, bx + mov [edi+4], eax +nf9_85: mov ax, bx + shl eax, 16 +nf9_86: mov ax, bx + mov [edi+8], eax +nf9_87: mov ax, bx + shl eax, 16 +nf9_88: mov ax, bx + mov [edi+12], eax + + pop esi + pop ebp + add esi, 24 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +;nf9+16 +nf25: ; low 4x4x2 (12 bytes) + +if 0 ;debug + add esi, 12 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov4 + lea edx, byte ptr ds:nf25_11+1 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_11-nf25_11)], bl + mov [edx+(nf25_12-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_13-nf25_11)], bl + mov [edx+(nf25_14-nf25_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_21-nf25_11)], bl + mov [edx+(nf25_22-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_23-nf25_11)], bl + mov [edx+(nf25_24-nf25_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_31-nf25_11)], bl + mov [edx+(nf25_32-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_33-nf25_11)], bl + mov [edx+(nf25_34-nf25_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_41-nf25_11)], bl + mov [edx+(nf25_42-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_43-nf25_11)], bl + mov [edx+(nf25_44-nf25_11)], bh + + push ebp + push esi + ; Load ebx,edx,ecx,ebp with four colors, duplicated in high order. +if TRANS16 + Trans16 cx, esi + shrd ebx, ecx, 16 + mov bx, cx + Trans16 cx, esi+2 + shrd edx, ecx, 16 + mov dx, cx + Trans16 cx, esi+4, 1 + shrd eax, ecx, 16 + mov ax, cx + push eax + Trans16 cx, esi+6 + shrd ebp, ecx, 16 + mov bp, cx + pop ecx +else + mov ax, [esi] + shrd ebx, eax, 16 + mov bx, ax + mov ax, [esi+2] + shrd edx, eax, 16 + mov dx, ax + mov ax, [esi+4] + and eax, 07fffh + shrd ecx, eax, 16 + mov cx, ax + mov ax, [esi+6] + shrd ebp, eax, 16 + mov bp, ax +endif + mov esi, nf_width + + jmp nf25_0 ; flush prefetch + ALIGN 4 +nf25_0: + +nf25_11:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_12:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_13:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_14:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf25_21:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_22:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_23:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_24:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf25_31:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_32:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_33:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_34:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf25_41:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_42:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_43:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_44:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + + add edi, esi + + pop esi + pop ebp + add esi, 12 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+32 +nf41: ; low 4x8x2 (16 bytes) + test word ptr [esi+4], 08000h + jnz nf57 + +if 0 ;debug + add esi, 16 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov8 + lea edx, byte ptr ds:nf41_11+1 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_11-nf41_11)], bl + mov [edx+(nf41_12-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_13-nf41_11)], bl + mov [edx+(nf41_14-nf41_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_21-nf41_11)], bl + mov [edx+(nf41_22-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_23-nf41_11)], bl + mov [edx+(nf41_24-nf41_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_31-nf41_11)], bl + mov [edx+(nf41_32-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_33-nf41_11)], bl + mov [edx+(nf41_34-nf41_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_41-nf41_11)], bl + mov [edx+(nf41_42-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_43-nf41_11)], bl + mov [edx+(nf41_44-nf41_11)], bh + + lea edx, [edx+(nf41_51-nf41_11)] + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_51-nf41_51)], bl + mov [edx+(nf41_52-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_53-nf41_51)], bl + mov [edx+(nf41_54-nf41_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_61-nf41_51)], bl + mov [edx+(nf41_62-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_63-nf41_51)], bl + mov [edx+(nf41_64-nf41_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_71-nf41_51)], bl + mov [edx+(nf41_72-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_73-nf41_51)], bl + mov [edx+(nf41_74-nf41_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_81-nf41_51)], bl + mov [edx+(nf41_82-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_83-nf41_51)], bl + mov [edx+(nf41_84-nf41_51)], bh + + push ebp + push esi + ; Load ebx,edx,ecx,ebp with four colors, duplicated in high order. +if TRANS16 + Trans16 cx, esi, 1 + shrd ebx, ecx, 16 + mov bx, cx + Trans16 cx, esi+2 + shrd edx, ecx, 16 + mov dx, cx + Trans16 cx, esi+4 + shrd eax, ecx, 16 + mov ax, cx + push eax + Trans16 cx, esi+6 + shrd ebp, ecx, 16 + mov bp, cx + pop ecx +else + mov ax, [esi] + and eax, 07fffh + shrd ebx, eax, 16 + mov bx, ax + mov ax, [esi+2] + shrd edx, eax, 16 + mov dx, ax + mov ax, [esi+4] + shrd ecx, eax, 16 + mov cx, ax + mov ax, [esi+6] + shrd ebp, eax, 16 + mov bp, ax +endif + mov esi, nf_width + + jmp nf41_0 ; flush prefetch + ALIGN 4 +nf41_0: + +nf41_11:mov [ebp+0], ebx +nf41_12:mov [ebp+4], ebx +nf41_13:mov [ebp+8], ebx +nf41_14:mov [ebp+12], ebx + add edi, esi + +nf41_21:mov [ebp+0], ebx +nf41_22:mov [ebp+4], ebx +nf41_23:mov [ebp+8], ebx +nf41_24:mov [ebp+12], ebx + add edi, esi + +nf41_31:mov [ebp+0], ebx +nf41_32:mov [ebp+4], ebx +nf41_33:mov [ebp+8], ebx +nf41_34:mov [ebp+12], ebx + add edi, esi + +nf41_41:mov [ebp+0], ebx +nf41_42:mov [ebp+4], ebx +nf41_43:mov [ebp+8], ebx +nf41_44:mov [ebp+12], ebx + add edi, esi + +nf41_51:mov [ebp+0], ebx +nf41_52:mov [ebp+4], ebx +nf41_53:mov [ebp+8], ebx +nf41_54:mov [ebp+12], ebx + add edi, esi + +nf41_61:mov [ebp+0], ebx +nf41_62:mov [ebp+4], ebx +nf41_63:mov [ebp+8], ebx +nf41_64:mov [ebp+12], ebx + add edi, esi + +nf41_71:mov [ebp+0], ebx +nf41_72:mov [ebp+4], ebx +nf41_73:mov [ebp+8], ebx +nf41_74:mov [ebp+12], ebx + add edi, esi + +nf41_81:mov [ebp+0], ebx +nf41_82:mov [ebp+4], ebx +nf41_83:mov [ebp+8], ebx +nf41_84:mov [ebp+12], ebx + + pop esi + pop ebp + add esi, 16 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+48 +nf57: ; low 8x4x2 (16 bytes) +if 0 ;debug + add esi, 16 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov4 + lea edx, byte ptr ds:nf57_11+2 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_11-nf57_11)], bh + mov [edx+(nf57_12-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_13-nf57_11)], bh + mov [edx+(nf57_14-nf57_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_15-nf57_11)], bh + mov [edx+(nf57_16-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_17-nf57_11)], bh + mov [edx+(nf57_18-nf57_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_21-nf57_11)], bh + mov [edx+(nf57_22-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_23-nf57_11)], bh + mov [edx+(nf57_24-nf57_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_25-nf57_11)], bh + mov [edx+(nf57_26-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_27-nf57_11)], bh + mov [edx+(nf57_28-nf57_11)], bl + + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_31-nf57_11)], bh + mov [edx+(nf57_32-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_33-nf57_11)], bh + mov [edx+(nf57_34-nf57_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_35-nf57_11)], bh + mov [edx+(nf57_36-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_37-nf57_11)], bh + mov [edx+(nf57_38-nf57_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_41-nf57_11)], bh + mov [edx+(nf57_42-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_43-nf57_11)], bh + mov [edx+(nf57_44-nf57_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_45-nf57_11)], bh + mov [edx+(nf57_46-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_47-nf57_11)], bh + mov [edx+(nf57_48-nf57_11)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors +if TRANS16 + Trans16 bx, esi, 1 + Trans16 dx, esi+2 + Trans16 cx, esi+4, 1 + Trans16 bp, esi+6 +else + mov bx, [esi] + and ebx, 07fffh + mov dx, [esi+2] + mov cx, [esi+4] + and ecx, 07fffh + mov bp, [esi+6] +endif + mov esi, nf_width + + jmp nf57_0 ; flush prefetch + ALIGN 4 +nf57_0: + +nf57_11:mov ax, bx + shl eax, 16 +nf57_12:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_13:mov ax, bx + shl eax, 16 +nf57_14:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_15:mov ax, bx + shl eax, 16 +nf57_16:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_17:mov ax, bx + shl eax, 16 +nf57_18:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf57_21:mov ax, bx + shl eax, 16 +nf57_22:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_23:mov ax, bx + shl eax, 16 +nf57_24:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_25:mov ax, bx + shl eax, 16 +nf57_26:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_27:mov ax, bx + shl eax, 16 +nf57_28:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf57_31:mov ax, bx + shl eax, 16 +nf57_32:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_33:mov ax, bx + shl eax, 16 +nf57_34:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_35:mov ax, bx + shl eax, 16 +nf57_36:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_37:mov ax, bx + shl eax, 16 +nf57_38:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf57_41:mov ax, bx + shl eax, 16 +nf57_42:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_43:mov ax, bx + shl eax, 16 +nf57_44:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_45:mov ax, bx + shl eax, 16 +nf57_46:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_47:mov ax, bx + shl eax, 16 +nf57_48:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + add edi, esi + + pop esi + pop ebp + add esi, 16 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf10: ; 2x2 4x4x2 (48 bytes) + + test word ptr [esi], 08000h + jnz nf26 + +if 0 ;debug + add esi, 48 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov4 + lea edx, byte ptr ds:nf10_11+2 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_11-nf10_11)], bh + mov [edx+(nf10_12-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_13-nf10_11)], bh + mov [edx+(nf10_14-nf10_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_15-nf10_11)], bh + mov [edx+(nf10_16-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_17-nf10_11)], bh + mov [edx+(nf10_18-nf10_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_21-nf10_11)], bh + mov [edx+(nf10_22-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_23-nf10_11)], bh + mov [edx+(nf10_24-nf10_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_25-nf10_11)], bh + mov [edx+(nf10_26-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_27-nf10_11)], bh + mov [edx+(nf10_28-nf10_11)], bl + + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_31-nf10_11)], bh + mov [edx+(nf10_32-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_33-nf10_11)], bh + mov [edx+(nf10_34-nf10_11)], bl + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_35-nf10_11)], bh + mov [edx+(nf10_36-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_37-nf10_11)], bh + mov [edx+(nf10_38-nf10_11)], bl + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_41-nf10_11)], bh + mov [edx+(nf10_42-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_43-nf10_11)], bh + mov [edx+(nf10_44-nf10_11)], bl + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_45-nf10_11)], bh + mov [edx+(nf10_46-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_47-nf10_11)], bh + mov [edx+(nf10_48-nf10_11)], bl + + + lea edx, [edx+(nf10_51-nf10_11)] + + mov al, [esi+32] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_51-nf10_51)], bh + mov [edx+(nf10_52-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_53-nf10_51)], bh + mov [edx+(nf10_54-nf10_51)], bl + + mov al, [esi+33] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_55-nf10_51)], bh + mov [edx+(nf10_56-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_57-nf10_51)], bh + mov [edx+(nf10_58-nf10_51)], bl + + + mov al, [esi+34] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_61-nf10_51)], bh + mov [edx+(nf10_62-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_63-nf10_51)], bh + mov [edx+(nf10_64-nf10_51)], bl + + mov al, [esi+35] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_65-nf10_51)], bh + mov [edx+(nf10_66-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_67-nf10_51)], bh + mov [edx+(nf10_68-nf10_51)], bl + + + mov al, [esi+44] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_71-nf10_51)], bh + mov [edx+(nf10_72-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_73-nf10_51)], bh + mov [edx+(nf10_74-nf10_51)], bl + + mov al, [esi+45] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_75-nf10_51)], bh + mov [edx+(nf10_76-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_77-nf10_51)], bh + mov [edx+(nf10_78-nf10_51)], bl + + + mov al, [esi+46] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_81-nf10_51)], bh + mov [edx+(nf10_82-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_83-nf10_51)], bh + mov [edx+(nf10_84-nf10_51)], bl + + mov al, [esi+47] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_85-nf10_51)], bh + mov [edx+(nf10_86-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_87-nf10_51)], bh + mov [edx+(nf10_88-nf10_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors +if TRANS16 + Trans16 bx, esi + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 +else + mov bx, [esi] + mov dx, [esi+2] + mov cx, [esi+4] + mov bp, [esi+6] +endif + mov esi, nf_width + + jmp nf10_0 ; flush prefetch + ALIGN 4 +nf10_0: + +nf10_11:mov ax, bx + shl eax, 16 +nf10_12:mov ax, bx + mov [edi], eax +nf10_13:mov ax, bx + shl eax, 16 +nf10_14:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_15:mov ax, bx + shl eax, 16 +nf10_16:mov ax, bx + mov [edi], eax +nf10_17:mov ax, bx + shl eax, 16 +nf10_18:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_21:mov ax, bx + shl eax, 16 +nf10_22:mov ax, bx + mov [edi], eax +nf10_23:mov ax, bx + shl eax, 16 +nf10_24:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_25:mov ax, bx + shl eax, 16 +nf10_26:mov ax, bx + mov [edi], eax +nf10_27:mov ax, bx + shl eax, 16 +nf10_28:mov ax, bx + mov [edi+4], eax + add edi, esi + + ; Load bx,dx,cx,bp with four colors +if TRANS16 + mov esi, [esp] + Trans16 bx, esi+12 + Trans16 dx, esi+14 + Trans16 cx, esi+16 + Trans16 bp, esi+18 + mov esi, nf_width +else + mov eax, [esp] + mov bx, [eax+12] + mov dx, [eax+14] + mov cx, [eax+16] + mov bp, [eax+18] +endif + +nf10_31:mov ax, bx + shl eax, 16 +nf10_32:mov ax, bx + mov [edi], eax +nf10_33:mov ax, bx + shl eax, 16 +nf10_34:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_35:mov ax, bx + shl eax, 16 +nf10_36:mov ax, bx + mov [edi], eax +nf10_37:mov ax, bx + shl eax, 16 +nf10_38:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_41:mov ax, bx + shl eax, 16 +nf10_42:mov ax, bx + mov [edi], eax +nf10_43:mov ax, bx + shl eax, 16 +nf10_44:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_45:mov ax, bx + shl eax, 16 +nf10_46:mov ax, bx + mov [edi], eax +nf10_47:mov ax, bx + shl eax, 16 +nf10_48:mov ax, bx + mov [edi+4], eax + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + + ; Load bx,dx,cx,bp with four colors +if TRANS16 + mov esi, [esp] + Trans16 bx, esi+24 + Trans16 dx, esi+26 + Trans16 cx, esi+28 + Trans16 bp, esi+30 + mov esi, nf_width +else + mov eax, [esp] + mov bx, [eax+24] + mov dx, [eax+26] + mov cx, [eax+28] + mov bp, [eax+30] +endif + +nf10_51:mov ax, bx + shl eax, 16 +nf10_52:mov ax, bx + mov [edi], eax +nf10_53:mov ax, bx + shl eax, 16 +nf10_54:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_55:mov ax, bx + shl eax, 16 +nf10_56:mov ax, bx + mov [edi], eax +nf10_57:mov ax, bx + shl eax, 16 +nf10_58:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_61:mov ax, bx + shl eax, 16 +nf10_62:mov ax, bx + mov [edi], eax +nf10_63:mov ax, bx + shl eax, 16 +nf10_64:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_65:mov ax, bx + shl eax, 16 +nf10_66:mov ax, bx + mov [edi], eax +nf10_67:mov ax, bx + shl eax, 16 +nf10_68:mov ax, bx + mov [edi+4], eax + add edi, esi + + ; Load bx,dx,cx,bp with four colors +if TRANS16 + mov esi, [esp] + Trans16 bx, esi+36 + Trans16 dx, esi+38 + Trans16 cx, esi+40 + Trans16 bp, esi+42 + mov esi, nf_width +else + mov eax, [esp] + mov bx, [eax+36] + mov dx, [eax+38] + mov cx, [eax+40] + mov bp, [eax+42] +endif + +nf10_71:mov ax, bx + shl eax, 16 +nf10_72:mov ax, bx + mov [edi], eax +nf10_73:mov ax, bx + shl eax, 16 +nf10_74:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_75:mov ax, bx + shl eax, 16 +nf10_76:mov ax, bx + mov [edi], eax +nf10_77:mov ax, bx + shl eax, 16 +nf10_78:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_81:mov ax, bx + shl eax, 16 +nf10_82:mov ax, bx + mov [edi], eax +nf10_83:mov ax, bx + shl eax, 16 +nf10_84:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_85:mov ax, bx + shl eax, 16 +nf10_86:mov ax, bx + mov [edi], eax +nf10_87:mov ax, bx + shl eax, 16 +nf10_88:mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 48 + sub edi, 8 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+16 +nf26: ; 2x1 4x8x2 (32 bytes) + + test word ptr [esi+16], 08000h + jnz nf42 + +if 0 ;debug + add esi, 32 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov4 + lea edx, byte ptr ds:nf26_11+2 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_11-nf26_11)], bh + mov [edx+(nf26_12-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_13-nf26_11)], bh + mov [edx+(nf26_14-nf26_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_15-nf26_11)], bh + mov [edx+(nf26_16-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_17-nf26_11)], bh + mov [edx+(nf26_18-nf26_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_21-nf26_11)], bh + mov [edx+(nf26_22-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_23-nf26_11)], bh + mov [edx+(nf26_24-nf26_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_25-nf26_11)], bh + mov [edx+(nf26_26-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_27-nf26_11)], bh + mov [edx+(nf26_28-nf26_11)], bl + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_31-nf26_11)], bh + mov [edx+(nf26_32-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_33-nf26_11)], bh + mov [edx+(nf26_34-nf26_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_35-nf26_11)], bh + mov [edx+(nf26_36-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_37-nf26_11)], bh + mov [edx+(nf26_38-nf26_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_41-nf26_11)], bh + mov [edx+(nf26_42-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_43-nf26_11)], bh + mov [edx+(nf26_44-nf26_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_45-nf26_11)], bh + mov [edx+(nf26_46-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_47-nf26_11)], bh + mov [edx+(nf26_48-nf26_11)], bl + + + lea edx, [edx+(nf26_51-nf26_11)] + + mov al, [esi+24] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_51-nf26_51)], bh + mov [edx+(nf26_52-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_53-nf26_51)], bh + mov [edx+(nf26_54-nf26_51)], bl + + mov al, [esi+25] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_55-nf26_51)], bh + mov [edx+(nf26_56-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_57-nf26_51)], bh + mov [edx+(nf26_58-nf26_51)], bl + + + mov al, [esi+26] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_61-nf26_51)], bh + mov [edx+(nf26_62-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_63-nf26_51)], bh + mov [edx+(nf26_64-nf26_51)], bl + + mov al, [esi+27] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_65-nf26_51)], bh + mov [edx+(nf26_66-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_67-nf26_51)], bh + mov [edx+(nf26_68-nf26_51)], bl + + + mov al, [esi+28] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_71-nf26_51)], bh + mov [edx+(nf26_72-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_73-nf26_51)], bh + mov [edx+(nf26_74-nf26_51)], bl + + mov al, [esi+29] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_75-nf26_51)], bh + mov [edx+(nf26_76-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_77-nf26_51)], bh + mov [edx+(nf26_78-nf26_51)], bl + + + mov al, [esi+30] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_81-nf26_51)], bh + mov [edx+(nf26_82-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_83-nf26_51)], bh + mov [edx+(nf26_84-nf26_51)], bl + + mov al, [esi+31] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_85-nf26_51)], bh + mov [edx+(nf26_86-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_87-nf26_51)], bh + mov [edx+(nf26_88-nf26_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors +if TRANS16 + Trans16 bx, esi, 1 + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 +else + mov bx, [esi] + and ebx, 07fffh + mov dx, [esi+2] + mov cx, [esi+4] + mov bp, [esi+6] +endif + mov esi, nf_width + + jmp nf26_0 ; flush prefetch + ALIGN 4 +nf26_0: + +nf26_11:mov ax, bx + shl eax, 16 +nf26_12:mov ax, bx + mov [edi], eax +nf26_13:mov ax, bx + shl eax, 16 +nf26_14:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_15:mov ax, bx + shl eax, 16 +nf26_16:mov ax, bx + mov [edi], eax +nf26_17:mov ax, bx + shl eax, 16 +nf26_18:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_21:mov ax, bx + shl eax, 16 +nf26_22:mov ax, bx + mov [edi], eax +nf26_23:mov ax, bx + shl eax, 16 +nf26_24:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_25:mov ax, bx + shl eax, 16 +nf26_26:mov ax, bx + mov [edi], eax +nf26_27:mov ax, bx + shl eax, 16 +nf26_28:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_31:mov ax, bx + shl eax, 16 +nf26_32:mov ax, bx + mov [edi], eax +nf26_33:mov ax, bx + shl eax, 16 +nf26_34:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_35:mov ax, bx + shl eax, 16 +nf26_36:mov ax, bx + mov [edi], eax +nf26_37:mov ax, bx + shl eax, 16 +nf26_38:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_41:mov ax, bx + shl eax, 16 +nf26_42:mov ax, bx + mov [edi], eax +nf26_43:mov ax, bx + shl eax, 16 +nf26_44:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_45:mov ax, bx + shl eax, 16 +nf26_46:mov ax, bx + mov [edi], eax +nf26_47:mov ax, bx + shl eax, 16 +nf26_48:mov ax, bx + mov [edi+4], eax + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + + ; Load bx,dx,cx,bp with four colors +if TRANS16 + mov esi, [esp] + Trans16 bx, esi+16 + Trans16 dx, esi+18 + Trans16 cx, esi+20 + Trans16 bp, esi+22 + mov esi, nf_width +else + mov eax, [esp] + mov bx, [eax+16] + mov dx, [eax+18] + mov cx, [eax+20] + mov bp, [eax+22] +endif + +nf26_51:mov ax, bx + shl eax, 16 +nf26_52:mov ax, bx + mov [edi], eax +nf26_53:mov ax, bx + shl eax, 16 +nf26_54:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_55:mov ax, bx + shl eax, 16 +nf26_56:mov ax, bx + mov [edi], eax +nf26_57:mov ax, bx + shl eax, 16 +nf26_58:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_61:mov ax, bx + shl eax, 16 +nf26_62:mov ax, bx + mov [edi], eax +nf26_63:mov ax, bx + shl eax, 16 +nf26_64:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_65:mov ax, bx + shl eax, 16 +nf26_66:mov ax, bx + mov [edi], eax +nf26_67:mov ax, bx + shl eax, 16 +nf26_68:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_71:mov ax, bx + shl eax, 16 +nf26_72:mov ax, bx + mov [edi], eax +nf26_73:mov ax, bx + shl eax, 16 +nf26_74:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_75:mov ax, bx + shl eax, 16 +nf26_76:mov ax, bx + mov [edi], eax +nf26_77:mov ax, bx + shl eax, 16 +nf26_78:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_81:mov ax, bx + shl eax, 16 +nf26_82:mov ax, bx + mov [edi], eax +nf26_83:mov ax, bx + shl eax, 16 +nf26_84:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_85:mov ax, bx + shl eax, 16 +nf26_86:mov ax, bx + mov [edi], eax +nf26_87:mov ax, bx + shl eax, 16 +nf26_88:mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 32 + sub edi, 8 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+32 +nf42: ; 1x2 8x4x2 (32 bytes) + +if 0 ;debug + add esi, 32 + mov ebx, 0 + jmp nf_solid +endif + xor eax, eax + lea ecx, nfhpk_mov4 + lea edx, byte ptr ds:nf42_11+2 + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_11-nf42_11)], bh + mov [edx+(nf42_12-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_13-nf42_11)], bh + mov [edx+(nf42_14-nf42_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_15-nf42_11)], bh + mov [edx+(nf42_16-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_17-nf42_11)], bh + mov [edx+(nf42_18-nf42_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_21-nf42_11)], bh + mov [edx+(nf42_22-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_23-nf42_11)], bh + mov [edx+(nf42_24-nf42_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_25-nf42_11)], bh + mov [edx+(nf42_26-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_27-nf42_11)], bh + mov [edx+(nf42_28-nf42_11)], bl + + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_31-nf42_11)], bh + mov [edx+(nf42_32-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_33-nf42_11)], bh + mov [edx+(nf42_34-nf42_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_35-nf42_11)], bh + mov [edx+(nf42_36-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_37-nf42_11)], bh + mov [edx+(nf42_38-nf42_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_41-nf42_11)], bh + mov [edx+(nf42_42-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_43-nf42_11)], bh + mov [edx+(nf42_44-nf42_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_45-nf42_11)], bh + mov [edx+(nf42_46-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_47-nf42_11)], bh + mov [edx+(nf42_48-nf42_11)], bl + + + lea edx, [edx+(nf42_51-nf42_11)] + + mov al, [esi+24] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_51-nf42_51)], bh + mov [edx+(nf42_52-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_53-nf42_51)], bh + mov [edx+(nf42_54-nf42_51)], bl + + mov al, [esi+25] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_55-nf42_51)], bh + mov [edx+(nf42_56-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_57-nf42_51)], bh + mov [edx+(nf42_58-nf42_51)], bl + + + mov al, [esi+26] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_61-nf42_51)], bh + mov [edx+(nf42_62-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_63-nf42_51)], bh + mov [edx+(nf42_64-nf42_51)], bl + + mov al, [esi+27] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_65-nf42_51)], bh + mov [edx+(nf42_66-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_67-nf42_51)], bh + mov [edx+(nf42_68-nf42_51)], bl + + + mov al, [esi+28] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_71-nf42_51)], bh + mov [edx+(nf42_72-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_73-nf42_51)], bh + mov [edx+(nf42_74-nf42_51)], bl + + mov al, [esi+29] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_75-nf42_51)], bh + mov [edx+(nf42_76-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_77-nf42_51)], bh + mov [edx+(nf42_78-nf42_51)], bl + + + mov al, [esi+30] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_81-nf42_51)], bh + mov [edx+(nf42_82-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_83-nf42_51)], bh + mov [edx+(nf42_84-nf42_51)], bl + + mov al, [esi+31] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_85-nf42_51)], bh + mov [edx+(nf42_86-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_87-nf42_51)], bh + mov [edx+(nf42_88-nf42_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors +if TRANS16 + Trans16 bx, esi, 1 + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 +else + mov bx, [esi] + and ebx, 07fffh + mov dx, [esi+2] + mov cx, [esi+4] + mov bp, [esi+6] +endif + mov esi, nf_width + + jmp nf42_0 ; flush prefetch + ALIGN 4 +nf42_0: + +nf42_11:mov ax, bx + shl eax, 16 +nf42_12:mov ax, bx + mov [edi], eax +nf42_13:mov ax, bx + shl eax, 16 +nf42_14:mov ax, bx + mov [edi+4], eax +nf42_15:mov ax, bx + shl eax, 16 +nf42_16:mov ax, bx + mov [edi+8], eax +nf42_17:mov ax, bx + shl eax, 16 +nf42_18:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_21:mov ax, bx + shl eax, 16 +nf42_22:mov ax, bx + mov [edi], eax +nf42_23:mov ax, bx + shl eax, 16 +nf42_24:mov ax, bx + mov [edi+4], eax +nf42_25:mov ax, bx + shl eax, 16 +nf42_26:mov ax, bx + mov [edi+8], eax +nf42_27:mov ax, bx + shl eax, 16 +nf42_28:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_31:mov ax, bx + shl eax, 16 +nf42_32:mov ax, bx + mov [edi], eax +nf42_33:mov ax, bx + shl eax, 16 +nf42_34:mov ax, bx + mov [edi+4], eax +nf42_35:mov ax, bx + shl eax, 16 +nf42_36:mov ax, bx + mov [edi+8], eax +nf42_37:mov ax, bx + shl eax, 16 +nf42_38:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_41:mov ax, bx + shl eax, 16 +nf42_42:mov ax, bx + mov [edi], eax +nf42_43:mov ax, bx + shl eax, 16 +nf42_44:mov ax, bx + mov [edi+4], eax +nf42_45:mov ax, bx + shl eax, 16 +nf42_46:mov ax, bx + mov [edi+8], eax +nf42_47:mov ax, bx + shl eax, 16 +nf42_48:mov ax, bx + mov [edi+12], eax + add edi, esi + + ; Load bx,dx,cx,bp with four colors +if TRANS16 + mov esi, [esp] + Trans16 bx, esi+16, 1 + Trans16 dx, esi+18 + Trans16 cx, esi+20 + Trans16 bp, esi+22 + mov esi, nf_width +else + mov eax, [esp] + mov bx, [eax+16] + and ebx, 07fffh + mov dx, [eax+18] + mov cx, [eax+20] + mov bp, [eax+22] +endif + +nf42_51:mov ax, bx + shl eax, 16 +nf42_52:mov ax, bx + mov [edi], eax +nf42_53:mov ax, bx + shl eax, 16 +nf42_54:mov ax, bx + mov [edi+4], eax +nf42_55:mov ax, bx + shl eax, 16 +nf42_56:mov ax, bx + mov [edi+8], eax +nf42_57:mov ax, bx + shl eax, 16 +nf42_58:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_61:mov ax, bx + shl eax, 16 +nf42_62:mov ax, bx + mov [edi], eax +nf42_63:mov ax, bx + shl eax, 16 +nf42_64:mov ax, bx + mov [edi+4], eax +nf42_65:mov ax, bx + shl eax, 16 +nf42_66:mov ax, bx + mov [edi+8], eax +nf42_67:mov ax, bx + shl eax, 16 +nf42_68:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_71:mov ax, bx + shl eax, 16 +nf42_72:mov ax, bx + mov [edi], eax +nf42_73:mov ax, bx + shl eax, 16 +nf42_74:mov ax, bx + mov [edi+4], eax +nf42_75:mov ax, bx + shl eax, 16 +nf42_76:mov ax, bx + mov [edi+8], eax +nf42_77:mov ax, bx + shl eax, 16 +nf42_78:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_81:mov ax, bx + shl eax, 16 +nf42_82:mov ax, bx + mov [edi], eax +nf42_83:mov ax, bx + shl eax, 16 +nf42_84:mov ax, bx + mov [edi+4], eax +nf42_85:mov ax, bx + shl eax, 16 +nf42_86:mov ax, bx + mov [edi+8], eax +nf42_87:mov ax, bx + shl eax, 16 +nf42_88:mov ax, bx + mov [edi+12], eax + + pop esi + pop ebp + add esi, 32 + sub edi, nfpk_back_right + retn + +;---------------------------------------- + ALIGN 4 +nf11: ; 8x8x16 (128 bytes) +if 0 ;debug + add esi, 128 + mov ebx, 0 + jmp nf_solid +endif + mov edx, nf_width + +if TRANS16 + +Trans16Blk MACRO idx + Trans16 bx, idx + mov [edi], bx + Trans16 bx, idx+2 + mov [edi+2], bx + Trans16 bx, idx+4 + mov [edi+4], bx + Trans16 bx, idx+6 + mov [edi+6], bx + Trans16 bx, idx+8 + mov [edi+8], bx + Trans16 bx, idx+10 + mov [edi+10], bx + Trans16 bx, idx+12 + mov [edi+12], bx + Trans16 bx, idx+14 + mov [edi+14], bx + ENDM + + Trans16Blk esi ;0 + add edi, edx + Trans16Blk esi+16 ;1 + add edi, edx + Trans16Blk esi+32 ;2 + add edi, edx + Trans16Blk esi+48 ;3 + add edi, edx + Trans16Blk esi+64 ;4 + add edi, edx + Trans16Blk esi+80 ;5 + add edi, edx + Trans16Blk esi+96 ;6 + add edi, edx + Trans16Blk esi+112 ;7 +else + mov eax, [esi] ;0 + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + mov eax, [esi+8] + mov [edi+8], eax + mov eax, [esi+12] + mov [edi+12], eax + add edi, edx + mov eax, [esi+16] ;1 + mov [edi], eax + mov eax, [esi+20] + mov [edi+4], eax + mov eax, [esi+24] + mov [edi+8], eax + mov eax, [esi+28] + mov [edi+12], eax + add edi, edx + mov eax, [esi+32] ;2 + mov [edi], eax + mov eax, [esi+36] + mov [edi+4], eax + mov eax, [esi+40] + mov [edi+8], eax + mov eax, [esi+44] + mov [edi+12], eax + add edi, edx + mov eax, [esi+48] ;3 + mov [edi], eax + mov eax, [esi+52] + mov [edi+4], eax + mov eax, [esi+56] + mov [edi+8], eax + mov eax, [esi+60] + mov [edi+12], eax + add edi, edx + mov eax, [esi+64] ;4 + mov [edi], eax + mov eax, [esi+68] + mov [edi+4], eax + mov eax, [esi+72] + mov [edi+8], eax + mov eax, [esi+76] + mov [edi+12], eax + add edi, edx + mov eax, [esi+80] ;5 + mov [edi], eax + mov eax, [esi+84] + mov [edi+4], eax + mov eax, [esi+88] + mov [edi+8], eax + mov eax, [esi+92] + mov [edi+12], eax + add edi, edx + mov eax, [esi+96] ;6 + mov [edi], eax + mov eax, [esi+100] + mov [edi+4], eax + mov eax, [esi+104] + mov [edi+8], eax + mov eax, [esi+108] + mov [edi+12], eax + add edi, edx + mov eax, [esi+112] ;7 + mov [edi], eax + mov eax, [esi+116] + mov [edi+4], eax + mov eax, [esi+120] + mov [edi+8], eax + mov eax, [esi+124] + mov [edi+12], eax +endif + + add esi, 128 + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + retn + +;---------------------------------------- + ALIGN 4 +nf12: ; low 4x4x16 (32 bytes) +if 0 ;debug + add esi, 32 + mov ebx, 0 + jmp nf_solid +endif + mov edx, nf_width + + Trans16 bx, esi + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+2 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+4 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+6 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + lea edi, [edi+edx*2] + + Trans16 bx, esi+8 + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+10 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+12 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+14 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + lea edi, [edi+edx*2] + + Trans16 bx, esi+16 + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+18 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+20 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+22 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + lea edi, [edi+edx*2] + + Trans16 bx, esi+24 + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+26 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+28 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+30 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + add edi, edx + + sub edi, nfpk_back_right + add esi, 32 + retn + +;---------------------------------------- + ALIGN 4 +nf13: ; 2x2 4x4x0 (8 bytes) +if 0 ;debug + add esi, 8 + mov ebx, 0 + jmp nf_solid +endif + mov edx, nf_width + +if TRANS16 + Trans16 cx, esi + shrd ebx, ecx, 16 + mov bx, cx + + Trans16 cx, esi+2 + shrd eax, ecx, 16 + mov ax, cx + mov ecx, eax +else + mov ax, [esi] + shrd ebx, eax, 16 + mov bx, ax + + mov ax, [esi+2] + shrd ecx, eax, 16 + mov cx, ax +endif + + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + lea edi, [edi+edx*2] + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + lea edi, [edi+edx*2] + +if TRANS16 + Trans16 cx, esi+4 + shrd ebx, ecx, 16 + mov bx, cx + + Trans16 cx, esi+6 + shrd eax, ecx, 16 + mov ax, cx + mov ecx, eax +else + mov ax, [esi+4] + shrd ebx, eax, 16 + mov bx, ax + + mov ax, [esi+6] + shrd ecx, eax, 16 + mov cx, ax +endif + + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + lea edi, [edi+edx*2] + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + add edi, edx + + sub edi, nfpk_back_right + add esi, 8 + + retn + +;---------------------------------------- + ALIGN 4 +nf14: ; 8x8x0 (2 bytes) + Trans16 cx, esi + add esi, 2 + shrd ebx, ecx, 16 + mov bx, cx + +nf_solid: + mov edx, nf_width + + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + + sub edi, nfpk_back_right ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +nf15: ; unused + retn + +nfHPkDecomp ENDP + +endif ; PKDATA + +;--------------------------------------------------------------------- +; ShowFrame +;------------ + +EXTERN sf_LineWidth: DWORD ;unsigned sf_LineWidth; // Distance between lines in memory + +; Banked screen parameters +EXTERN sf_SetBank: PTRPROC ;unsigned long sf_SetBank; +EXTERN sf_WinGran: DWORD ;unsigned sf_WinGran; +EXTERN sf_WinSize: DWORD ;unsigned long sf_WinSize; +EXTERN sf_WinGranPerSize: DWORD ;unsigned sf_WinGranPerSize; +;{sf_WriteWinPtr and sf_WriteWinLimit replace sf_WriteWinSeg, see mveliba.asm} +EXTERN sf_WriteWinPtr: PTRBYTE ;unsigned char *sf_WriteWinPtr; +EXTERN sf_WriteWinLimit: PTRBYTE ;unsigned char *WriteWinLimit; +EXTERN sf_WriteWin: DWORD ;unsigned sf_WriteWin; + +if SCALING +EXTERN opt_hscale_step: DWORD +EXTERN opt_hscale_adj: DWORD +endif + +;void mve_ShowFrameField( +; unsigned char *buf, unsigned bufw, unsigned bufh, +; unsigned sx, unsigned sy, unsigned w, unsigned h, +; unsigned dstx, unsigned dsty, unsigned field) + +mve_ShowFrameField PROC USES ESI EDI EBX, \ + buf:PTRBYTE, bufw:DWORD, bufh:DWORD, \ + sx:DWORD, sy:DWORD, w:DWORD, h:DWORD, \ + dstx:DWORD, dsty:DWORD, field:DWORD + LOCAL bank:DWORD + LOCAL w4:DWORD + LOCAL new_src_line:DWORD + LOCAL linestep:DWORD + LOCAL new_dst_line:DWORD + + mov ax, ds ; Insure es==ds for symantec flat mode + mov es, ax + + mov eax, w ; w4 = w>>2 + shr eax, 2 + mov w4, eax + +;;; +;;; In stretched width mode, we either keep 4/5 (a) of the source pixels, +;;; or duplicate every fourth pixel to magnify by 5/4 (b). +;;; In these cases, new_src_line is either bufw-w*5/4 (a) or bufw-w*4/5 (b). +;;; Let ScaleStep be 5 (a) or 3 (b) instead of 4. This is the amount to advance +;;; the source after copying 32-bits from source to destination. +;;; The coordinate system used for the source will be a simulated scaled system. +;;; Rather than scale height, I plan to use alternate vertical resolutions. However, +;;; it might be a good idea to also provide for scaled height in case we want a +;;; higher resolution border. +;;; Question: Do we still need to support transferring subrectangles? + +if SCALING + .if opt_hscale_step==4 +endif + mov eax, bufw ; new_src_line = bufw - w + sub eax, w + mov new_src_line, eax +if SCALING + .else + mov eax, opt_hscale_adj + mov new_src_line, eax + .endif +endif + + mov eax, sf_LineWidth ; linestep = sf_LineWidth<<1; + .if field ; if (field) + add eax, eax ; linestep <<= 1; + .endif + mov linestep, eax + + sub eax, w ; new_dst_line = linestep - w; + mov new_dst_line, eax + + mov eax, sy ; buf += sy*bufw + sx + mul bufw + add eax, sx + add buf, eax + + mov eax, sx ; dstx += sx + add dstx, eax + + ; This is a hack. We should pass in src x,y of origin + ; or make dstx/dsty absolute. + ; + mov eax, bufw ; if (field && sx >= (bufw>>1) + shr eax, 1 + .if field && sx >= eax + sub dstx, eax ; dstx -= bufw>>1 + .endif + + mov eax, sy ; dsty += sy + add dsty, eax + + .if sf_SetBank==0 ;------------------ + + + ; dst = WriteWinPtr + (dsty*linestep+dstx) + mov edi, sf_WriteWinPtr + mov eax, dsty + mul linestep + add eax, dstx + add edi, eax + + .if field & 1 + add edi, sf_LineWidth; + .endif + + mov eax, new_src_line + mov edx, new_dst_line + mov esi, buf + mov ebx, h +if SCALING + .if opt_hscale_step==3 + sub edi, 8 +sf_lp2a:mov ecx, w4 + shr ecx, 2 + ALIGN 4 +sf_lp2b:mov eax, [esi] + mov [edi+8], eax + mov eax, [esi+3] + mov [edi+12], eax + add edi, 16 + mov eax, [esi+6] + mov [edi], eax + mov eax, [esi+9] + mov [edi+4], eax + add esi, 12 + dec ecx + jnz sf_lp2b + ; To avoid problem of last pixel coming from next line + ; with arrange for w%16==12, so here is where we copy + ; last 12 pixels. + mov eax, [esi] + mov [edi+8], eax + mov eax, [esi+3] + mov [edi+12], eax + add edi, 12 + mov eax, [esi+6] + mov [edi+4], eax + add esi, 9 + add esi, new_src_line + add edi, edx + dec ebx + jnz sf_lp2a + add edi, 8 + .else +endif +sf_lp: mov ecx, w4 ;width/4 + rep movsd + add esi, eax + add edi, edx + dec ebx + jnz sf_lp +if SCALING + .endif +endif + + .else ; sf_SetBank ;------------------ + + + mov esi, buf + + ; start = dsty * linestep + dstx + + mov eax, linestep + mul dsty + .if field & 1 + add eax, sf_LineWidth + .endif + add eax, dstx + ; bank = start / WinGran + ; dst = (start % WinGran) + sf_WriteWinPtr + mov edx, 0 + div sf_WinGran + mov bank, eax + mov edi, edx + add edi, sf_WriteWinPtr + + ; Select new bank + mov bh, 0 + mov bl, byte ptr sf_WriteWin + mov edx, bank + call sf_SetBank + ; eax/edx destroyed by sf_SetBank + +sf_0: ; rem = sf_WriteWinLimit - dst + mov eax, sf_WriteWinLimit + sub eax, edi + ; h2 = (rem+(LineWidth-w))/LineWidth + add eax, linestep + sub eax, w + mov edx, 0 + div linestep + ; if (h>1 + shr eax, 1 + mov w4, eax ; Number of dst words to transfer + + mov eax, bufw ; new_src_line = bufw - w + sub eax, w + mov new_src_line, eax + + mov eax, sf_LineWidth ; linestep = sf_LineWidth<<1; + .if field ; if (field) + add eax, eax ; linestep <<= 1; + .endif + mov linestep, eax + + sub eax, w ; new_dst_line = linestep - w*2; + sub eax, w + mov new_dst_line, eax + + mov eax, sy ; buf += sy*bufw + sx + mul bufw + add eax, sx + add buf, eax + + mov eax, sx ; dstx += sx + add dstx, eax + + ; This is a hack. We should pass in src x,y of origin + ; or make dstx/dsty absolute. + ; + mov eax, bufw ; if (field && sx >= (bufw>>1) + shr eax, 1 + .if field && sx >= eax + sub dstx, eax ; dstx -= bufw>>1 + .endif + + mov eax, sy ; dsty += sy + add dsty, eax + + .if sf_SetBank==0 ;------------------ + + + ; dst = WriteWinPtr + (dsty*linestep+dstx*2) + mov edi, sf_WriteWinPtr + mov eax, dsty + mul linestep + add eax, dstx + add eax, dstx + add edi, eax + + .if field & 1 + add edi, sf_LineWidth; + .endif + + mov esi, buf + mov ebx, h +sf_lp: mov ecx, w4 ;width/4 + + push ebx + lea ebx, pal15_tbl + xor eax, eax +sf_movsd1: + mov al, [esi] + add esi, 2 + mov dx, [ebx+eax*2] + mov al, [esi-1] + shl edx, 16 + mov dx, [ebx+eax*2] + rol edx, 16 + mov [edi], edx + add edi, 4 + dec ecx + jnz sf_movsd1 + pop ebx + +; rep movsd ;;;;;-----;;;;; + + add esi, new_src_line + add edi, new_dst_line + dec ebx + jnz sf_lp + + .else ; sf_SetBank ;------------------ + + + mov esi, buf + + ; start = dsty * linestep + dstx*2 + + mov eax, linestep + mul dsty + .if field & 1 + add eax, sf_LineWidth + .endif + add eax, dstx + add eax, dstx + ; bank = start / WinGran + ; dst = (start % WinGran) + sf_WriteWinPtr + mov edx, 0 + div sf_WinGran + mov bank, eax + mov edi, edx + add edi, sf_WriteWinPtr + + ; Select new bank + mov bh, 0 + mov bl, byte ptr sf_WriteWin + mov edx, bank + call sf_SetBank + ; eax/edx destroyed by sf_SetBank + +sf_0: ; rem = sf_WriteWinLimit - dst + mov eax, sf_WriteWinLimit + sub eax, edi + ; h2 = (rem+(LineWidth-w*2))/LineWidth + add eax, new_dst_line + mov edx, 0 + div linestep + ; if (h=WinSize, we're done with squares (but need to correctly + ; adjust si and di!) + ; if di+4*cx>WinSize, we need to clip and then we're done + ; (but need to correctly adjust si and di!) + ; Reduce cx to (WinSize-di)/4. + + ; limit=WriteWinLimit-4*ax + +Split: push ebx + push edx + push esi + push edi + + mov ecx, w + mov eax, 0 + jmp aTest1 + +aNext1: mov dx, word ptr [ebx] + add ebx, 2 +aTest1: add dx, dx + jz aNext1 + jb aChgd2 + add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + loop aTest1 + jmp aDone + +aNext2: mov dx, [ebx] + add ebx, 2 +aTest2: add dx, dx + ja aCopy3 + jz aNext2 +aChgd2: add eax, SWIDTH*HI_COLOR_SCALE/4 + loop aTest2 + call aCopy + jmp aDone + + +aCopy3: call aCopy + add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + mov eax, 0 + loop aTest1 + jmp aDone + +aCopy: push ebx + push ecx + push edx + push esi + push edi + + mov ecx, eax + shl ecx, 2 + mov ebx, SrcWidth + sub ebx, ecx + mov edx, DstWidth + sub edx, ecx + sub ecx, sf_WriteWinLimit + neg ecx + mov limit, ecx + + REPEAT 7 + cmp edi, limit + jns aFinal + mov ecx, eax + rep movsd + add esi, ebx + add edi, edx + ENDM + cmp edi, limit + jns aFinal + mov ecx, eax + jmp aLast + +aFinal: mov ecx, sf_WriteWinLimit + sub ecx, edi + js aCpyDn + shr ecx, 2 +aLast: rep movsd + +aCpyDn: pop edi + pop esi + mov ecx, eax + shl ecx, 2 + add esi, ecx + add edi, ecx + pop edx + pop ecx + pop ebx + retn + +aDone: pop edi + pop esi + + ; Advance bank + mov eax, sf_WinGranPerSize + add bank, eax + sub edi, sf_WinSize + + ; Select new bank + cmp sf_SetBank, 0 + jz nobank2 + mov bh, 0 + mov bl, byte ptr sf_WriteWin + mov edx, bank + call sf_SetBank + ; eax/edx destroyed by SetBank +nobank2: + pop edx + pop ebx + + ; For start of next bank... + ; While di+4*cx<=0, advance si & di by src/dst line step instead of + ; doing rep mov + ; If di<0, cx += di/4, si-=di, di=0 + ; Do remaining rep mov's (first with modified args, remainder with + ; with full args). + + ; Init bx/dx to src/dst line steps. + ; limit=-4*ax + + mov ecx, w + mov eax, 0 + jmp bTest1 + +bNext1: mov dx, [ebx] + add ebx, 2 +bTest1: add dx, dx + jz bNext1 + jb bChgd2 + add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + loop bTest1 + jmp LineDone + +bNext2: mov dx, [ebx] + add ebx, 2 +bTest2: add dx, dx + ja bCopy3 + jz bNext2 +bChgd2: add eax, SWIDTH*HI_COLOR_SCALE/4 + loop bTest2 + call bCopy + jmp LineDone + + +bCopy3: call bCopy + add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + mov eax, 0 + loop bTest1 + jmp LineDone + +bCopy: push ebx + push ecx + push edx + + mov ecx, eax + shl ecx, 2 + neg ecx + mov limit, ecx + mov ebx, SrcWidth + mov edx, DstWidth + + sub edi, sf_WriteWinPtr + + FOR bMovN, + mov ecx, offset bMovN + jns bFull + cmp limit, edi + js bPart + add esi, ebx + add edi, edx + ENDM + + mov ecx, offset bMov8 + jns bFull + cmp limit, edi + js bPart + + add edi, sf_WriteWinPtr + + shl eax, 2 + add esi, eax + add edi, eax + + jmp bCpyDn + + +bFull: push ecx + mov ecx, eax + add ebx, limit + add edx, limit + add edi, sf_WriteWinPtr + retn + +bPart: push ecx + mov ecx, eax + sub esi, edi + sar edi, 2 + add ecx, edi + mov edi, sf_WriteWinPtr + add ebx, limit + add edx, limit + retn + + + FOR bMovN, +bMovN: rep movsd + mov ecx, eax + add esi, ebx + add edi, edx + ENDM +bMov8: rep movsd + +bCpyDn: sub esi, SrcWidth7 + sub edi, DstWidth7 + + pop edx + pop ecx + pop ebx + retn + +Finished: + ret + + ENDM ; SHOW_FRAME_CHG_BODY + +;void +;mve_sfShowFrameChg( +; bool prvbuf, +; unsigned x, unsigned y, unsigned w, unsigned h, +; unsigned short *chgs, +; unsigned dstx, unsigned dsty) +; +mve_sfShowFrameChg PROC USES ESI EDI EBX, \ + prvbuf:DWORD, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD, \ + chgs:PTRWORD, \ + dstx:DWORD, dsty:DWORD + LOCAL _width:DWORD + LOCAL SrcWidth:DWORD + LOCAL DstWidth:DWORD + LOCAL SrcWidth7:DWORD + LOCAL DstWidth7:DWORD + LOCAL SrcLineStep:DWORD + LOCAL DstLineStep1:DWORD + LOCAL DstLineStep2:DWORD + LOCAL LineEnd:DWORD + LOCAL bank:DWORD + LOCAL limit:DWORD + + SHOW_FRAME_CHG_BODY 0 ; Not HiColor + +mve_sfShowFrameChg ENDP + + +if HICOLOR + +;void +;mve_sfHiColorShowFrameChg( +; bool prvbuf, +; unsigned x, unsigned y, unsigned w, unsigned h, +; unsigned short *chgs, +; unsigned dstx, unsigned dsty) +; +mve_sfHiColorShowFrameChg PROC USES ESI EDI EBX, \ + prvbuf:DWORD, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD, \ + chgs:PTRWORD, \ + dstx:DWORD, dsty:DWORD + LOCAL _width:DWORD + LOCAL SrcWidth:DWORD + LOCAL DstWidth:DWORD + LOCAL SrcWidth7:DWORD + LOCAL DstWidth7:DWORD + LOCAL SrcLineStep:DWORD + LOCAL DstLineStep1:DWORD + LOCAL DstLineStep2:DWORD + LOCAL LineEnd:DWORD + LOCAL bank:DWORD + LOCAL limit:DWORD + + SHOW_FRAME_CHG_BODY 1 ; HiColor + +mve_sfHiColorShowFrameChg ENDP + + +endif ;HICOLOR + +endif ;PARTIAL + + +;---------------------------------------------------------------------- + +if 0 ; No supported +if PKDATA + +PK_SHOW_FRAME_CHG_BODY MACRO HI_COLOR_FLAG:REQ + + LOCAL HI_COLOR_SCALE +HI_COLOR_SCALE equ HI_COLOR_FLAG+1 + + mov eax, w ; _width = w*SWIDTH*HI_COLOR_SCALE; + shl eax, LOG2_SWIDTH+HI_COLOR_FLAG + mov _width, eax + + xor ebx, ebx ; ebx = nf_fqty (converted to 32-bits) + mov bl, nf_fqty + + mov eax, nf_width ; SrcWidth = nf_width*nf_fqty; + mul ebx ;nf_fqty + mov SrcWidth, eax + imul eax, (SHEIGHT-1) ; SrcWidth7 = SrcWidth * (SHEIGHT-1) + mov SrcWidth7, eax + add eax, SrcWidth ; SrcLineStep = SrcWidth*SHEIGHT-_width + sub eax, _width + mov SrcLineStep, eax + + mov eax, sf_LineWidth ; DstWidth = sf_LineWidth*nf_fqty; + mul ebx ;nf_fqty + mov DstWidth, eax + imul eax, (SHEIGHT-1) ; DstWidth7 = DstWidth * (SHEIGHT-1) + mov DstWidth7, eax + ;Note: DstLineStep1+2 = DstWidth*SHEIGHT - _width = ????Not True!!! + dec eax ; DstLineStep1 = DstWidth*(SHEIGHT-1)-1 + mov DstLineStep1, eax + + mov eax, DstWidth ; DstLineStep2 = DstWidth-_width+1 + sub eax, _width + inc eax + mov DstLineStep2, eax + + mov eax, DstLineStep1 ; LineEnd = DstWidth*(SHEIGHT-1)+_width-1 + add eax, _width + mov LineEnd, eax + + ; esi = buf (pointer into buf) + ; ebx = pointer into ops + ; dx = temp for current op. dl xor dh keeps just upper nibble op. + ; edi = pointer into screen + ; ecx = remaining square lines to copy + + .if prvbuf ; buf = prvbuf ? nf_buf_prv : nf_buf_cur + mov esi, nf_buf_prv + .else + mov esi, nf_buf_cur + .endif + mov eax, y ; + y*SHEIGHT*nf_WIDTH + shl eax, LOG2_SHEIGHT + mul nf_width + add esi, eax + mov eax, x ; + x*SWIDTH*HI_COLOR_SCALE + shl eax, LOG2_SWIDTH+HI_COLOR_FLAG + add esi, eax + + ; dstx must be a multiple of 4 because everything is done on 32-bit words + ; and bank crossing checks don't check for a crossing within a word. + and dstx, NOT 3 ; dstx &= ~3 + + mov ebx, ops + + mov cl, nf_fqty +nxtfld: push ecx + push esi + + mov ecx, h + + push ebx + + mov eax, sf_LineWidth + mul dsty + add eax, dstx + ; bank = start / WinGran + ; dst = (start % WinGran) + sf_WriteWinPtr + mov edx, 0 + div sf_WinGran + mov bank, eax + mov edi, edx + add edi, sf_WriteWinPtr + + ; Select new bank + cmp sf_SetBank, 0 + jz nobank + mov bh, 0 + mov bl, byte ptr sf_WriteWin + mov edx, bank + call sf_SetBank + ; eax/edx destroyed by sf_SetBank +nobank: pop ebx + +NextLine: + push ecx + + mov eax, edi + add eax, LineEnd ; (SHEIGHT-1)*DstWidth+_width-1 + sub eax, sf_WriteWinLimit + jb NoSplit + jmp Split + +LineDone: + pop ecx + add esi, SrcLineStep ; Move back to start column, down SHEIGHT + add edi, DstLineStep1 ; First advance to last byte + add edi, DstLineStep2 ; Then advance to new start + loop NextLine + pop esi + pop ecx + add esi, nf_width + inc dsty + dec cl + jnz nxtfld + jmp Finished + + ; --- Copy full squares --- + + ; Scan over contiguous unchanged squares up to max per line + ; For each unchanged square, add 8 (SWIDTH) to esi and edi. + + ; count # of contiguous changed squares up to max per line + ; Init eax to 0, ebx and edx to line steps for source and dest. + ; For each square, add 2 (SWIDTH/4) to eax and subtract 8 (SWIDTH) + ; from ebx and edx. + +NoSplit: + mov ecx, w + shr ecx, 1 + mov eax, 0 +fNext1: mov dl, [ebx] + inc ebx + mov dh, dl + and dh, 0Fh + jnz fChgd2a +fTest1a:add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + xor dl, dh + jnz fChgd2b +fTest1b:add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + dec ecx + jnz fNext1 + jmp LineDone + +fNext2: mov dl, [ebx] + inc ebx + mov dh, dl + and dh, 0Fh + jz fCopy3a +fChgd2a:add eax, SWIDTH*HI_COLOR_SCALE/4 + xor dl, dh + jz fCopy3b +fChgd2b:add eax, SWIDTH*HI_COLOR_SCALE/4 + dec ecx + jnz fNext2 + call fCopy + jmp LineDone + +fCopy3a:call fCopy + xor eax, eax + jmp fTest1a + +fCopy3b:call fCopy + xor eax, eax + jmp fTest1b + +fCopy: push ebx + push ecx + push edx + + mov ecx, eax + shl ecx, 2 + mov ebx, SrcWidth + sub ebx, ecx + mov edx, DstWidth + sub edx, ecx + + REPEAT 7 + mov ecx, eax + rep movsd + add esi, ebx + add edi, edx + ENDM + mov ecx, eax + rep movsd + + sub esi, SrcWidth7 + sub edi, DstWidth7 + + pop edx + pop ecx + pop ebx + retn + + + ; --- Copy squares across bank boundary --- + ; (occurs infrequently, but should be streamlined as much as possible + ; because it could potentially be much more expensive than normal + ; operation). + ; HMMM... 16*640 = 10240 = approx 1/6 64K, so for 640x480, + ; roughly 1 in 6 square lines will need special processing + ; (actually, 2 in 12 due to interlacing, but that's the same ratio). + + ; Repeat above twice, once for end of cur bank, once for start + ; of next bank, with following modifications: + + ; For end of cur bank... + ; if di>=WinSize, we're done with squares (but need to correctly + ; adjust si and di!) + ; if di+4*cx>WinSize, we need to clip and then we're done + ; (but need to correctly adjust si and di!) + ; Reduce cx to (WinSize-di)/4. + + ; limit=WriteWinLimit-4*ax + +Split: push ebx + push esi + push edi + + mov ecx, w + shr ecx, 1 + mov eax, 0 +aNext1: mov dl, [ebx] + inc ebx + mov dh, dl + and dh, 0Fh + jnz aChgd2a +aTest1a:add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + xor dl, dh + jnz aChgd2b +aTest1b:add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + dec ecx + jnz aNext1 + jmp LineDone + +aNext2: mov dl, [ebx] + inc ebx + mov dh, dl + and dh, 0Fh + jz aCopy3a +aChgd2a:add eax, SWIDTH*HI_COLOR_SCALE/4 + xor dl, dh + jz aCopy3b +aChgd2b:add eax, SWIDTH*HI_COLOR_SCALE/4 + dec ecx + jnz aNext2 + call aCopy + jmp LineDone + +aCopy3a:call aCopy + xor eax, eax + jmp aTest1a + +aCopy3b:call aCopy + xor eax, eax + jmp aTest1b + +aCopy: push ebx + push ecx + push edx + push esi + push edi + + mov ecx, eax + shl ecx, 2 + mov ebx, SrcWidth + sub ebx, ecx + mov edx, DstWidth + sub edx, ecx + sub ecx, sf_WriteWinLimit + neg ecx + mov limit, ecx + + REPEAT 7 + cmp edi, limit + jns aFinal + mov ecx, eax + rep movsd + add esi, ebx + add edi, edx + ENDM + cmp edi, limit + jns aFinal + mov ecx, eax + jmp aLast + +aFinal: mov ecx, sf_WriteWinLimit + sub ecx, edi + js aCpyDn + shr ecx, 2 +aLast: rep movsd + +aCpyDn: pop edi + pop esi + mov ecx, eax + shl ecx, 2 + add esi, ecx + add edi, ecx + pop edx + pop ecx + pop ebx + retn + +aDone: pop edi + pop esi + + ; Advance bank + mov eax, sf_WinGranPerSize + add bank, eax + sub edi, sf_WinSize + + ; Select new bank + cmp sf_SetBank, 0 + jz nobank2 + mov bh, 0 + mov bl, byte ptr sf_WriteWin + mov edx, bank + call sf_SetBank + ; eax/edx destroyed by SetBank +nobank2: + pop ebx + + ; For start of next bank... + ; While di+4*cx<=0, advance si & di by src/dst line step instead of + ; doing rep mov + ; If di<0, cx += di/4, si-=di, di=0 + ; Do remaining rep mov's (first with modified args, remainder with + ; with full args). + + ; Init bx/dx to src/dst line steps. + ; limit=-4*ax + + mov ecx, w + shr ecx, 1 + mov eax, 0 +bNext1: mov dl, [ebx] + inc ebx + mov dh, dl + and dh, 0Fh + jnz bChgd2a +bTest1a:add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + xor dl, dh + jnz bChgd2b +bTest1b:add esi, SWIDTH*HI_COLOR_SCALE + add edi, SWIDTH*HI_COLOR_SCALE + dec ecx + jnz bNext1 + jmp LineDone + +bNext2: mov dl, [ebx] + inc ebx + mov dh, dl + and dh, 0Fh + jz bCopy3a +bChgd2a:add eax, SWIDTH*HI_COLOR_SCALE/4 + xor dl, dh + jz bCopy3b +bChgd2b:add eax, SWIDTH*HI_COLOR_SCALE/4 + dec ecx + jnz bNext2 + call bCopy + jmp LineDone + +bCopy3a:call bCopy + xor eax, eax + jmp bTest1a + +bCopy3b:call bCopy + xor eax, eax + jmp bTest1b + +bCopy: push ebx + push ecx + push edx + + mov ecx, eax + shl ecx, 2 + neg ecx + mov limit, ecx + mov ebx, SrcWidth + mov edx, DstWidth + + sub edi, sf_WriteWinPtr + + FOR bMovN, + mov ecx, offset bMovN + jns bFull + cmp limit, edi + js bPart + add esi, ebx + add edi, edx + ENDM + + mov ecx, offset bMov8 + jns bFull + cmp limit, edi + js bPart + + add edi, sf_WriteWinPtr + + shl eax, 2 + add esi, eax + add edi, eax + + jmp bCpyDn + + +bFull: push ecx + mov ecx, eax + add ebx, limit + add edx, limit + add edi, sf_WriteWinPtr + retn + +bPart: push ecx + mov ecx, eax + sub esi, edi + sar edi, 2 + add ecx, edi + mov edi, sf_WriteWinPtr + add ebx, limit + add edx, limit + retn + + + FOR bMovN, +bMovN: rep movsd + mov ecx, eax + add esi, ebx + add edi, edx + ENDM +bMov8: rep movsd + +bCpyDn: sub esi, SrcWidth7 + sub edi, DstWidth7 + + pop edx + pop ecx + pop ebx + retn + +Finished: + ret + + ENDM ; PK_SHOW_FRAME_CHG_BODY + +;void +;mve_sfPkShowFrameChg( +; bool prvbuf, +; unsigned x, unsigned y, unsigned w, unsigned h, +; unsigned char *ops, +; unsigned dstx, unsigned dsty) +; +mve_sfPkShowFrameChg PROC USES ESI EDI EBX, \ + prvbuf:DWORD, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD, \ + ops:PTRBYTE, \ + dstx:DWORD, dsty:DWORD + LOCAL _width:DWORD + LOCAL SrcWidth:DWORD + LOCAL DstWidth:DWORD + LOCAL SrcWidth7:DWORD + LOCAL DstWidth7:DWORD + LOCAL SrcLineStep:DWORD + LOCAL DstLineStep1:DWORD + LOCAL DstLineStep2:DWORD + LOCAL LineEnd:DWORD + LOCAL bank:DWORD + LOCAL limit:DWORD + + PK_SHOW_FRAME_CHG_BODY 0 ; Not HiColor + +mve_sfPkShowFrameChg ENDP + + +if HICOLOR + +;void +;mve_sfPkHiColorShowFrameChg( +; bool prvbuf, +; unsigned x, unsigned y, unsigned w, unsigned h, +; unsigned char *ops, +; unsigned dstx, unsigned dsty) +; +mve_sfPkHiColorShowFrameChg PROC USES ESI EDI EBX, \ + prvbuf:DWORD, \ + x:DWORD, y:DWORD, w:DWORD, h:DWORD, \ + ops:PTRBYTE, \ + dstx:DWORD, dsty:DWORD + LOCAL _width:DWORD + LOCAL SrcWidth:DWORD + LOCAL DstWidth:DWORD + LOCAL SrcWidth7:DWORD + LOCAL DstWidth7:DWORD + LOCAL SrcLineStep:DWORD + LOCAL DstLineStep1:DWORD + LOCAL DstLineStep2:DWORD + LOCAL LineEnd:DWORD + LOCAL bank:DWORD + LOCAL limit:DWORD + + PK_SHOW_FRAME_CHG_BODY 1 ; HiColor + +mve_sfPkHiColorShowFrameChg ENDP + + +endif ;HICOLOR + +endif ;PKDATA + +endif + +;--------------------------------------------------------------------- +; Palette Management +;--------------------- + +;void __cdecl +;MVE_SetPalette(unsigned char *p, unsigned start, unsigned count) +; +MVE_SetPalette PROC USES ESI EBX, \ + p:PTRBYTE, start:DWORD, count:DWORD + mov eax, start + mov ecx, count + mov esi, p + .if eax>=256 ; if (start>=256) return; + ret + .endif + lea ebx, [eax+ecx] ; if (start+count>256) + .if ebx>256 + mov ecx, 256 ; count = 256-start + sub ecx, eax + .endif + add esi, eax ; p += start*3 + add esi, eax + add esi, eax + lea ecx, [ecx+2*ecx] ; count *= 3 + + mov edx, 03c8h ; DAC Write Index Register + out dx, al ; Init write index to start + inc edx ; DAC Data Register + rep outsb + ret +MVE_SetPalette ENDP + + +; If at least 11 palette entries aren't changed, this is more compact +; than uncompressed 256 entry palette. +; +;static void palLoadCompPalette(unsigned char *buf) +; +palLoadCompPalette PROC USES ESI EDI, \ + buf: PTRBYTE + mov ax, ds ; Insure es==ds for symantec flat mode + mov es, ax + + mov cx, 32 + mov esi, buf + mov edi, offset pal_tbl +next: lodsb + or al, al + jnz chk0 + add edi, 24 + loop next + jmp done + +chk0: test al, 1 + jz not0 + movsw + movsb + test al, 2 + jz not1 +cpy1: movsw + movsb + test al, 4 + jz not2 +cpy2: movsw + movsb + test al, 8 + jz not3 +cpy3: movsw + movsb + test al, 16 + jz not4 +cpy4: movsw + movsb + test al, 32 + jz not5 +cpy5: movsw + movsb + test al, 64 + jz not6 +cpy6: movsw + movsb + or al, al + jns not7 +cpy7: movsw + movsb + loop next + jmp done + +not0: add edi, 3 + test al, 2 + jnz cpy1 +not1: add edi, 3 + test al, 4 + jnz cpy2 +not2: add edi, 3 + test al, 8 + jnz cpy3 +not3: add edi, 3 + test al, 16 + jnz cpy4 +not4: add edi, 3 + test al, 32 + jnz cpy5 +not5: add edi, 3 + test al, 64 + jnz cpy6 +not6: add edi, 3 + or al, al + js cpy7 +not7: add edi, 3 + loop next + +done: ret + +palLoadCompPalette ENDP + +;----------------------------------------------------------------------- +; Graphics +;---------- + +gfxMode proc USES EBP ESI EDI EBX, mode:DWORD + mov eax, mode + int 10h + ret +gfxMode endp + +gfxLoadCrtc proc USES ESI EDI EBX, crtc:PTRBYTE, chain4:BYTE, res:BYTE + + mov edx, 03c4h ; alter sequence registers + mov al, 04h ; disable or enable chain 4 in memory mode + mov ah, chain4 + out dx, ax + + mov dx, 03dah ; General Input State #1 register + +l1: in al, dx ; Loop until vertical retrace is off + test al, 8 + jnz l1 +l2: in al, dx ; Now loop until it's back on + test al, 8 + jz l2 + + cli ; turn off all interrupts + mov edx, 03c4h ; Sequencer Synchronous reset + mov eax, 0100h ; Set sequencer reset + out dx, ax + mov edx, 03c2h ; Misc Output Register + mov al, res ; 25/28-mHz, 350/400/480 lines + out dx, al + mov edx, 03c4h ; Sequencer Synchronous reset + mov eax, 0300h ; Clear sequencer reset + out dx, ax + + mov edx, 03d4h ; 6845 CRTC + mov esi, crtc ; tweaked values for CRTC registers + mov al, 011h ; deprotect CRTC registers 0-7 + mov ah, [esi+011h] + and ah, 07Fh + out dx, ax + + mov ecx, 018h ; Update CRTC registers with tweaked values + mov ebx, 0 +l3: mov al, bl + mov ah, [esi+ebx] + out dx, ax + inc bl + loop l3 + + sti ; restore interrupts + + ret +gfxLoadCrtc endp + +; void __cdecl gfxGetCrtc(unsigned char *crtc); +; +gfxGetCrtc proc USES ESI EBX, crtc:PTRBYTE + mov edx, 03d4h ; 6845 CRTC + mov esi, crtc + mov ecx, 018h + mov ebx, 0 +l3: mov al, bl + out dx, al + inc dx + in al, dx + dec dx + mov [esi+ebx], al + inc bl + loop l3 + ret +gfxGetCrtc endp + +; void __cdecl gfxVres(unsigned char misc, unsigned char *crtc); +; misc is one of the following: +; 350: 0x23 | 0x80 (2) +; 400: 0x23 | 0x40 (1) +; 480: 0x23 | 0xc0 (3) + +; Get crtc register specified by crtc_addr into ah. +; To update register, do out dx,ax +GetCrtc MACRO crtc_addr + mov al, crtc_addr + out dx, al + inc dx + in al, dx + dec dx + mov ah, al + mov al, crtc_addr + ENDM + +gfxVres PROC USES EBX, misc:BYTE, crtc:PTRBYTE + + mov edx, 03dah ; General Input State #1 register + +l1: in al, dx ; Loop until vertical retrace is off + test al, 8 + jnz l1 +l2: in al, dx ; Now loop until it's back on + test al, 8 + jz l2 + + cli ; turn off all interrupts + + mov edx, 03c4h ; Sequencer Synchronous reset + mov eax, 0100h ; Set sequencer reset + out dx, ax + mov edx, 03cch ; Misc Output Register (read port) + in al, dx + and al, 03fh ; Keep all but lines field + mov edx, 03c2h ; Misc Output Register (write port) + and misc, 0c0h ; Only keep lines field + or al, misc ; 350/400/480 lines + out dx, al + mov edx, 03c4h ; Sequencer Synchronous reset + mov eax, 0300h ; Clear sequencer reset + out dx, ax + + mov edx, 03d4h ; CRTC address port + mov ebx, crtc ; Desired CRTC image + + GetCrtc 011h ; Vertical Retrace End register + and ah, 07Fh ; Deprotect CRTC registers 0-7 + out dx, ax + + GetCrtc 03h ; End Horizontal Blanking register + or ah, 080h ; Enable CRTC registers 10-11 + out dx, ax + + mov al, 06h ; Vertical Total register + mov ah, byte ptr 06h[ebx] + out dx, ax + + GetCrtc 07h ; Overflow register + and ah, 010h ; (Preserve LC) + or ah, byte ptr 07h[ebx] + out dx, ax + + GetCrtc 09h ; Maximum Scan Line register + and ah, 040h ; (Preserve LC) + or ah, byte ptr 09h[ebx] + out dx, ax + + mov al, 010h ; Vertical Retrace Start register + mov ah, byte ptr 010h[ebx] + out dx, ax + + GetCrtc 11h ; Vertical Retrace End register + and ah, 070h ; (Preserve BW,DVI,CVI) + or ah, byte ptr 011h[ebx] + or ah, 080h ; Reprotect 0-7 + out dx, ax + + mov al, 012h ; Vertical Display End register + mov ah, byte ptr 012h[ebx] + out dx, ax + + mov al, 015h ; Start Vertical Blank register + mov ah, byte ptr 015h[ebx] + out dx, ax + +; Some SVGA's use 7-bit vbe, others 8-bit vbe! +if 0 + GetCrtc 16h ; End Vertical Blank register + and ah, 080h ; (Preserve reserved field) + or ah, byte ptr 016h[ebx] + out dx, ax +else + mov al, 16h ; End Vertical Blank register + mov ah, byte ptr 016h[ebx] + out dx, ax +endif + + sti ; restore interrupts + + ret +gfxVres ENDP + +; void __cdecl MVE_gfxWaitRetrace(unsigned state); +; +MVE_gfxWaitRetrace proc state:DWORD + mov edx, 03dah ; Input Status #1 register + mov eax, state + or eax, eax + jnz wt1 +wt0: in al, dx ; Wait for retrace off + and al, 8 + jnz wt0 + ret + +wt1: in al, dx ; Wait for retrace on + and al, 8 + jz wt1 + ret + +MVE_gfxWaitRetrace endp + +; void __cdecl MVE_gfxSetSplit(unsigned line) +; +MVE_gfxSetSplit proc line:DWORD + mov edx, 03dah ; Input State #1 register +wt0: in al, dx ; Wait for retrace off + and al, 8 + jnz wt0 +wt1: in al, dx ; Wait for retrace on + and al, 8 + jz wt1 + + mov edx, 03d4h ; CRTC address port + + mov ecx, line + shr ecx, 4 + and cl, 010h + GetCrtc 07h ; Overflow Register + and ah, 0EFh ; LC8 (mask=10h) + or ah, cl + out dx, ax + + mov ecx, line + shr ecx, 3 + and cl, 040h + GetCrtc 09h ; Maximum Scan Line Register + and ah, 0BFh ; LC9 (mask=40h) + or ah, cl + out dx, ax + + mov al, 18h ; Line Compare Register + mov ah, byte ptr line + out dx, ax + + ret + +MVE_gfxSetSplit endp + +;---------------------------------------------------------------------- + +mveliba_end: + + + END diff --git a/libmve/platform.cpp b/libmve/platform.cpp new file mode 100644 index 000000000..46adc350e --- /dev/null +++ b/libmve/platform.cpp @@ -0,0 +1,23 @@ +#if _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include + + unsigned int platform_timeGetTime(void) + { + return timeGetTime(); + } + +#else + #include + #include + + unsigned int platform_timeGetTime(void) + { + struct timeval t; + gettimeofday(&t,NULL); + + return (t.tv_sec*1000 + t.tv_usec/1000); + } + +#endif diff --git a/libmve/platform.h b/libmve/platform.h new file mode 100644 index 000000000..c75eaa23c --- /dev/null +++ b/libmve/platform.h @@ -0,0 +1,6 @@ +#ifndef MVEPLATFORM_H_ +#define MVEPLATFORM_H_ + +unsigned int platform_timeGetTime(void); + +#endif diff --git a/libmve/snd8to16.h b/libmve/snd8to16.h new file mode 100644 index 000000000..12372a3c9 --- /dev/null +++ b/libmve/snd8to16.h @@ -0,0 +1,53 @@ +/* +** #include +** void init_snd_8to16(void) +** { +** int i; +** for (i=0; i<44; ++i) +** snd_8to16[i] = i; +** for (i=44; i<128; ++i) +** snd_8to16[i] = (unsigned)floor(pow(65535,i/127.0)+.5); +** for (i=1; i<128; ++i) +** snd_8to16[256-i] = -snd_8to16[i]; +** snd_8to16[128] = snd_8to16[129]; +** } +*/ +#pragma warning( disable: 4245) +#pragma warning( disable: 4309) + +signed short snd_8to16[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 47, 51, 56, 61, + 66, 72, 79, 86, 94, 102, 112, 122, + 133, 145, 158, 173, 189, 206, 225, 245, + 267, 292, 318, 348, 379, 414, 452, 493, + 538, 587, 640, 699, 763, 832, 908, 991, + 1081, 1180, 1288, 1405, 1534, 1673, 1826, 1993, + 2175, 2373, 2590, 2826, 3084, 3365, 3672, 4008, + 4373, 4772, 5208, 5683, 6202, 6767, 7385, 8059, + 8794, 9597, 10472, 11428, 12471, 13609, 14851, 16206, + 17685, 19298, 21060, 22981, 25078, 27367, 29864, 32589, + 35563, 38808, 42350, 46214, 50431, 55033, 60055, 65535, +-65535,-65535,-60055,-55033,-50431,-46214,-42350,-38808, +-35563,-32589,-29864,-27367,-25078,-22981,-21060,-19298, +-17685,-16206,-14851,-13609,-12471,-11428,-10472, -9597, + -8794, -8059, -7385, -6767, -6202, -5683, -5208, -4772, + -4373, -4008, -3672, -3365, -3084, -2826, -2590, -2373, + -2175, -1993, -1826, -1673, -1534, -1405, -1288, -1180, + -1081, -991, -908, -832, -763, -699, -640, -587, + -538, -493, -452, -414, -379, -348, -318, -292, + -267, -245, -225, -206, -189, -173, -158, -145, + -133, -122, -112, -102, -94, -86, -79, -72, + -66, -61, -56, -51, -47, -43, -42, -41, + -40, -39, -38, -37, -36, -35, -34, -33, + -32, -31, -30, -29, -28, -27, -26, -25, + -24, -23, -22, -21, -20, -19, -18, -17, + -16, -15, -14, -13, -12, -11, -10, -9, + -8, -7, -6, -5, -4, -3, -2, -1 +}; +#pragma warning( default: 4245) +#pragma warning( default: 4309) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 000000000..ee7118b11 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,13 @@ +SET (HEADERS registry.h ) +SET (CPPS + lnxcon.cpp + lnxcon_raw.cpp + lnxdebug.cpp + lnxtask.cpp + lnxapp.cpp + lnxcon_null.cpp + lnxdata.cpp + lnxmono.cpp + registry.cpp) +# DynXLib.cpp +ADD_LIBRARY(linux STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/linux/DynXLib.cpp b/linux/DynXLib.cpp new file mode 100644 index 000000000..92bf73163 --- /dev/null +++ b/linux/DynXLib.cpp @@ -0,0 +1,209 @@ +#include +#include +#include +#include + +// interface functions to what we need +extern "C" +{ + Bool VidModeQueryVersion(Display* dpy,int* majorversion,int* minorversion); + Bool VidModeQueryExtension(Display* dpy,int* event_base,int* error_base); + Bool VidModeGetModeLine(Display* dpy,int screen,int* dotclock,XF86VidModeModeLine* modeline); + Bool VidModeGetAllModeLines(Display* dpy,int screen,int* modecount,XF86VidModeModeInfo*** modelinesPtr); + Bool VidModeAddModeLine(Display* dpy,int screen,XF86VidModeModeInfo* modeline,XF86VidModeModeInfo* aftermodeline); + Bool VidModeDeleteModeLine(Display* dpy,int screen,XF86VidModeModeInfo* modeline); + Bool VidModeModModeLine(Display* dpy,int screen,XF86VidModeModeLine* modeline); + Status VidModeValidateModeLine(Display* dpy,int screen,XF86VidModeModeInfo* modeline); + Bool VidModeSwitchMode(Display* dpy,int screen,int zoom); + Bool VidModeSwitchToMode(Display* dpy,int screen,XF86VidModeModeInfo* modeline); + Bool VidModeLockModeSwitch(Display* dpy,int screen,int lock); + Bool VidModeGetMonitor(Display* dpy,int screen,XF86VidModeMonitor* monitor); + Bool VidModeGetViewPort(Display* dpy,int screen,int* xreturn,int* yreturn); + Bool VidModeSetViewPort(Display* dpy,int screen,int x,int y); + void VidModeFreeData(int num_modes,XF86VidModeModeInfo** modelinePtr); + + Bool DGAQueryVersion(Display* pa,int* pb,int* pc); + Bool DGAQueryExtension(Display* pa,int* pb,int* pc); + Status DGAGetVideoLL(Display* pa,int pb,int *pc,int *pd,int *pe,int *pf); + Status DGAGetVideo(Display* pa,int pb,char **pc,int *pd,int *pe,int *pf); + Status DGADirectVideo(Display* pa,int pb,int pc); + Status DGADirectVideoLL(Display* pa,int pb,int pc); + Status DGAGetViewPortSize(Display* pa,int pb,int *pc,int *pd); + Status DGASetViewPort(Display* pa,int pb,int x,int y); + Status DGAGetVidPage(Display* pa,int pb,int *pc); + Status DGASetVidPage(Display* pa,int pb,int pc); + Status DGAInstallColormap(Display*,int pa,Colormap); + int DGAForkApp(int screen); + Status DGAQueryDirectVideo(Display *pa,int pb,int *pc); + Bool DGAViewPortChanged(Display *pa,int pb,int pc); + Bool DGACopyArea(Display *pa,int pb,Drawable pc,GC pd,int pe,int pf,unsigned int pg,unsigned int ph,int pi,int pj); + Bool DGAFillRectangle(Display *pa,int pb,Drawable pc,GC pd,int pe,int pf,unsigned int pg,unsigned int ph); +} + +Bool VidModeQueryVersion(Display* dpy,int* majorversion,int* minorversion) +{ + return XF86VidModeQueryVersion(dpy,majorversion,minorversion); +} + +Bool VidModeQueryExtension(Display* dpy,int* event_base,int* error_base) +{ + return XF86VidModeQueryExtension(dpy,event_base,error_base); +} + +Bool VidModeGetModeLine(Display* dpy,int screen,int* dotclock,XF86VidModeModeLine* modeline) +{ + return XF86VidModeGetModeLine(dpy,screen,dotclock,modeline); +} + +Bool VidModeGetAllModeLines(Display* dpy,int screen,int* modecount,XF86VidModeModeInfo*** modelinesPtr) +{ + return XF86VidModeGetAllModeLines(dpy,screen,modecount,modelinesPtr); +} + +Bool VidModeAddModeLine(Display* dpy,int screen,XF86VidModeModeInfo* modeline,XF86VidModeModeInfo* aftermodeline) +{ + return XF86VidModeAddModeLine(dpy,screen,modeline,aftermodeline); +} + +Bool VidModeDeleteModeLine(Display* dpy,int screen,XF86VidModeModeInfo* modeline) +{ + return XF86VidModeDeleteModeLine(dpy,screen,modeline); +} + +Bool VidModeModModeLine(Display* dpy,int screen,XF86VidModeModeLine* modeline) +{ + return XF86VidModeModModeLine(dpy,screen,modeline); +} + +Status VidModeValidateModeLine(Display* dpy,int screen,XF86VidModeModeInfo* modeline) +{ + return XF86VidModeValidateModeLine(dpy,screen,modeline); +} + +Bool VidModeSwitchMode(Display* dpy,int screen,int zoom) +{ + return XF86VidModeSwitchMode(dpy,screen,zoom); +} + +Bool VidModeSwitchToMode(Display* dpy,int screen,XF86VidModeModeInfo* modeline) +{ + return XF86VidModeSwitchToMode(dpy,screen,modeline); +} + +Bool VidModeLockModeSwitch(Display* dpy,int screen,int lock) +{ + return XF86VidModeLockModeSwitch(dpy,screen,lock); +} + +Bool VidModeGetMonitor(Display* dpy,int screen,XF86VidModeMonitor* monitor) +{ + return XF86VidModeGetMonitor(dpy,screen,monitor); +} + +Bool VidModeGetViewPort(Display* dpy,int screen,int* xreturn,int* yreturn) +{ + return XF86VidModeGetViewPort(dpy,screen,xreturn,yreturn); +} + +Bool VidModeSetViewPort(Display* dpy,int screen,int x,int y) +{ + return XF86VidModeSetViewPort(dpy,screen,x,y); +} + +void VidModeFreeData(int num_modes,XF86VidModeModeInfo** modelinePtr) +{ + int i; + for(i=0;iprivsize>0) + { + XFree(modelinePtr[i]->c_private); + modelinePtr[i]->c_private = NULL; + modelinePtr[i]->privsize = 0; + } + } + + XFree(modelinePtr); +} + + +Bool DGAQueryVersion(Display* pa,int* pb,int* pc) +{ + return XF86DGAQueryVersion(pa,pb,pc); +} + +Bool DGAQueryExtension(Display* pa,int* pb,int* pc) +{ + return XF86DGAQueryExtension(pa,pb,pc); +} + +Status DGAGetVideoLL(Display* pa,int pb,int *pc,int *pd,int *pe,int *pf) +{ + return XF86DGAGetVideoLL(pa,pb,(unsigned int *)pc,pd,pe,pf); +} + +Status DGAGetVideo(Display* pa,int pb,char **pc,int *pd,int *pe,int *pf) +{ + return XF86DGAGetVideo(pa,pb,pc,pd,pe,pf); +} + +Status DGADirectVideo(Display* pa,int pb,int pc) +{ + return XF86DGADirectVideo(pa,pb,pc); +} + +Status DGADirectVideoLL(Display* pa,int pb,int pc) +{ + return XF86DGADirectVideoLL(pa,pb,pc); +} + +Status DGAGetViewPortSize(Display* pa,int pb,int *pc,int *pd) +{ + return XF86DGAGetViewPortSize(pa,pb,pc,pd); +} + +Status DGASetViewPort(Display* pa,int pb,int x,int y) +{ + return XF86DGASetViewPort(pa,pb,x,y); +} + +Status DGAGetVidPage(Display* pa,int pb,int *pc) +{ + return XF86DGAGetVidPage(pa,pb,pc); +} + +Status DGASetVidPage(Display* pa,int pb,int pc) +{ + return XF86DGASetVidPage(pa,pb,pc); +} + +Status DGAInstallColormap(Display* pa,int pb,Colormap pc) +{ + return XF86DGAInstallColormap(pa,pb,pc); +} + +int DGAForkApp(int screen) +{ + return XF86DGAForkApp(screen); +} + +Status DGAQueryDirectVideo(Display *pa,int pb,int *pc) +{ + return XF86DGAQueryDirectVideo(pa,pb,pc); +} + +Bool DGAViewPortChanged(Display *pa,int pb,int pc) +{ + return XF86DGAViewPortChanged(pa,pb,pc); +} +/* +Bool DGACopyArea(Display *pa,int pb,Drawable pc,GC pd,int pe,int pf,unsigned int pg,unsigned int ph,int pi,int pj) +{ + return XDGACopyArea(pa,pb,pc,pd,pe,pf,pg,ph,pi,pj); +} + +Bool DGAFillRectangle(Display *pa,int pb,Drawable pc,GC pd,int pe,int pf,unsigned int pg,unsigned int ph) +{ + return XDGAFillRectangle(pa,pb,pc,pd,pe,pf,pg,ph); +} +*/ diff --git a/linux/lnxapp.cpp b/linux/lnxapp.cpp new file mode 100644 index 000000000..3906f951e --- /dev/null +++ b/linux/lnxapp.cpp @@ -0,0 +1,300 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxapp.cpp $ +* $Revision: 1.3 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* Linux application routines +* +* $Log: lnxapp.cpp,v $ +* Revision 1.3 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.2 2000/04/28 20:18:27 icculus +* Replaced X stuff with SDL stuff. +* +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 25 10/19/99 8:35p Jeff + * added a couple functions for Window OpenGL + * + * 24 10/17/99 3:55p Jeff + * + * 23 10/15/99 12:14p Jeff + * dyna load pthread library + * + * 22 9/24/99 8:26p Jeff + * better cleanup (including restoring the mouse) in case of a crash or + * error exit + * + * 21 9/09/99 4:35p Jeff + * dynamically load the Outrage Xlib extension library (for DGA). Init + * DGA if needed for Mouse control + * + * 20 9/07/99 4:37p Jeff + * + * 19 9/06/99 9:22p Jeff + * set initial window position to 0,0 + * + * 18 9/05/99 9:44p Jeff + * handle window option flag + * + * 17 7/15/99 11:37a Jeff + * turn on/off autorepeat + * + * 16 7/14/99 9:09p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + + +#include "application.h" +#include "linux/lnxapp.h" +#include "mono.h" +#include +#include "ddio.h" +//#include "local_malloc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct termios Linux_initial_terminal_settings; + +bool oeLnxApplication::os_initialized = false; +bool oeLnxApplication::first_time = true; + +bool con_Create(int flags); +void con_Destroy(void); +void con_Defer(void); + +void GlobalFree(void *mptr) +{ + if(mptr) + free(mptr); +} + +void *GlobalAlloc(int flags,int size) +{ + if(size<=0) + return NULL; + return malloc(size); +} + +void *GlobalLock(HGLOBAL hMem) +{ + return hMem; +} + +void Sleep(int millis) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = millis*1000; + select(0,NULL,NULL,NULL,&tv); +} + +char *strupr(char *string) +{ + while(string && *string) + { + *string = toupper(*string); + string++; + } + return string; +} + +char *itoa(int value,char *string,int radix) +{ + if(radix==10) + { + sprintf(string,"%d",value); + }else if(radix==16) + { + sprintf(string,"%x",value); + }else + { + mprintf((0,"!!!!!!!!!!!!!!!WARNING CALLING itoa WITHOUT 10 or 16 RADIX!!!!!!!!!!!!!!!!!!!!!!\n")); + sprintf(string,"%d",value); + } + return string; +} + + +static unsigned int LinuxAppFlags=0; +//static Display *LinuxAppDisplay=NULL; +static bool LinuxAppSetAtExit=false; +static bool LinuxAppDontCallShutdown=false; +void LnxAppShutdown(void) +{ + if(LinuxAppDontCallShutdown) + return; + LinuxAppDontCallShutdown = true; + if(LinuxAppFlags&OEAPP_CONSOLE) + { + con_Destroy(); + tcsetattr(0,TCSANOW,&Linux_initial_terminal_settings); + }else + { + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + } +} + +// Creates the application object +oeLnxApplication::oeLnxApplication(unsigned flags) +{ + m_Flags = flags; + m_AppActive = true; + + + if(flags&OEAPP_CONSOLE) + { + tcgetattr(0,&Linux_initial_terminal_settings); + con_Create(m_Flags); + } + + SDL_EnableKeyRepeat(0, 0); + + LinuxAppFlags=m_Flags; + + + if(!LinuxAppSetAtExit) + { + LinuxAppSetAtExit = true; + atexit(LnxAppShutdown); + } +} + +// Create object with a premade info +oeLnxApplication::oeLnxApplication(tLnxAppInfo *appinfo) +{ + tcgetattr(0,&Linux_initial_terminal_settings); + m_Flags = appinfo->flags; + m_X = appinfo->wnd_x; + m_Y = appinfo->wnd_y; + m_W = appinfo->wnd_w; + m_H = appinfo->wnd_h; + m_AppActive = true; + + if(m_Flags&OEAPP_CONSOLE) + { + con_Create(m_Flags); + } + + LinuxAppFlags=m_Flags; + + + if(!LinuxAppSetAtExit) + { + LinuxAppSetAtExit = true; + atexit(LnxAppShutdown); + } +} + +// Destructor +oeLnxApplication::~oeLnxApplication() +{ + LnxAppShutdown(); +} + +// initializes the object +void oeLnxApplication::init() +{ + if(m_WasCreated){ + //Create graphics window and prepare for graphics! + + } +} + +// Function to retrieve information from object through a platform defined structure. +void oeLnxApplication::get_info(void *info) +{ + tLnxAppInfo *appinfo = (tLnxAppInfo *)info; + appinfo->flags = m_Flags; + appinfo->wnd_x = m_X; + appinfo->wnd_y = m_Y; + appinfo->wnd_w = m_W; + appinfo->wnd_h = m_H; +} + +// defer returns some flags. essentially this function defers program control to OS. +unsigned oeLnxApplication::defer() +{ + bool res = 0; + + if(m_DeferFunc) + { + res = 1; + (*m_DeferFunc)(true); + } + + con_Defer(); + + return res; +} + +// set a function to run when deferring to OS. +void oeLnxApplication::set_defer_handler(void (*func)(bool)) +{ + m_DeferFunc = func; +} + +// delays app for a certain amount of time +void oeLnxApplication::delay(float secs) +{ + int msecs = (int)(secs * 1000.0f); + Sleep(msecs); +} + +// Function to get the flags +int oeLnxApplication::flags(void) const +{ + return m_Flags; +} + + +// Sizes the displayable region of the app (the window) +void oeLnxApplication::set_sizepos(int x, int y, int w, int h) +{ + m_X = x; + m_Y = y; + m_W = w; + m_H = h; +} + +char *oeLnxApplication::get_window_name(void) +{ + return "Descent 3"; +} + +void oeLnxApplication::clear_window(void) +{ + SDL_FillRect(SDL_GetVideoSurface(),NULL,SDL_MapRGB(SDL_GetVideoSurface()->format,255,0,0)); +} + +// initializes OS components. +void oeLnxApplication::os_init() +{ + /* We only need to do this once */ + if (!os_initialized) + { + os_initialized = true; + } +} + + + + diff --git a/linux/lnxcon.cpp b/linux/lnxcon.cpp new file mode 100644 index 000000000..f615afea9 --- /dev/null +++ b/linux/lnxcon.cpp @@ -0,0 +1,535 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxcon.cpp $ +* $Revision: 1.2 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* +* +* $Log: lnxcon.cpp,v $ +* Revision 1.2 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 7 7/19/99 12:54p Jeff + * created lnxcon_raw.cpp so ncurses is now only used for SVGALib, cuts + * down on redraw + * + * 6 6/24/99 4:43p Jeff + * removed ncurses dependency + * + * 5 4/22/99 2:04a Jeff + * support for null console + * + * 4 4/19/99 4:52p Jeff + * removed useless calls to curses lib for keyboard setup + * + * 3 4/19/99 3:58a Jeff + * got console working again (keyboard based off ddio) + * + * 2 4/14/99 1:59a Jeff + * fixed case mismatched #includes + * + * 1 1/12/99 3:42a Jeff +* +* $NoKeywords: $ +*/ + +#include "DDAccess.h" +#include "application.h" +#include "AppConsole.h" +#include "TaskSystem.h" +//@@#include "mem.h" +#include "mono.h" +//#include "local_malloc.h" +#include "pstring.h" +#include +#include +#include +#include +#include + +#define DECLARE_POINTERS +#include "linux/dyna_curses.h" +#undef DECLARE_POINTERS + +////////////////////////////////////////////////// +// Defines +#define CON_MAX_STRINGLEN 768 + +#define CON_MAINWND 0x01 +#define CON_INPUTWND 0x02 + +enum{ +Console_normal,Console_null,Console_raw +}Console_mode; + +//////////////////////////////////////////////// +// NULL driver functions +void con_null_Printf(const char *fmt, ...); +bool con_null_Input(char *buf, int buflen); +void con_null_Defer(void); +bool con_null_Create(void); +void con_null_Destroy(void); +void con_null_Puts(int window,const char *str); + +//////////////////////////////////////////////// +// raw driver functions +void con_raw_Printf(const char *fmt, ...); +bool con_raw_Input(char *buf, int buflen); +void con_raw_Defer(void); +bool con_raw_Create(void); +void con_raw_Destroy(void); +void con_raw_Puts(int window,const char *str); + +////////////////////////////////////////////////// +// Global Variables +char *Con_read_buf = NULL; //The next buffer of text from user input +char *Con_inp_buf = NULL, Con_inp_pos=0; //Currently updating input buffer of text (and it's position) +char Con_last_command[CON_MAX_STRINGLEN]; //The last command entered by the user +int Con_cols = 0,Con_rows = 0; //The size of the main window (input window is (1 row, Con_cols)) +bool Con_newline = false; +bool Con_init = false; //Console has been initialized +WINDOW *Con_main_wnd_ptr = NULL,*Con_input_wnd_ptr = NULL; //Window handles for the subwindows + +#ifdef mem_malloc +#undef mem_malloc +#endif +#ifdef mem_free +#undef mem_free +#endif +#define mem_malloc(x) malloc(x) +#define mem_free(x) free(x) +#define min(x,y) (((x)<(y))?(x):(y)) +#define max(x,y) (((x)>(y))?(x):(y)) + +////////////////////////////////////////////////// +// Prototypes +void con_Update(int windows); //Refreshes a window +void con_Defer(void); //Performs the actions for the frame +void con_DoKeyboard(void); //Handles any keyboard input +void con_Puts(int window,const char *str); +////////////////////////////////////////////////// +// Functions +void con_Update(int windows) +{ + if(!Con_init) //console hasn't been initialized yet, short circuit + return; + + if(Console_mode==Console_null || Console_mode==Console_raw) + { + return; + } + + if(windows&CON_MAINWND) //update the main window + wnoutrefresh(Con_main_wnd_ptr); + if(windows&CON_INPUTWND){ //update the input data window + //clear the line + wclear(Con_input_wnd_ptr); + + mvwprintw(Con_input_wnd_ptr,0,0,"%s",Con_inp_buf); + touchwin(Con_input_wnd_ptr); + wnoutrefresh(Con_input_wnd_ptr); + leaveok(Con_input_wnd_ptr,false); + } + //update the screen + doupdate(); + + int len = strlen(Con_inp_buf); + mvwin(Con_input_wnd_ptr,Con_rows,len); + move(Con_rows,len); +} + +void con_Printf(const char *fmt, ...) +{ + if(!Con_init) //console hasn't been initialized yet, short circuit + return; + + char buf[CON_MAX_STRINGLEN]; + va_list args; + + // filter out messages + va_start(args, fmt ); + vsprintf(buf,fmt,args); + + int len = strlen(buf); + if(len >= CON_MAX_STRINGLEN ){ + //we overflowed our buffer!!! + //we need to do some sort of error here!!!! + buf[CON_MAX_STRINGLEN-1] = '\0'; + } + +// filter out unprintable characters + char *p,*fp,filter_buf[CON_MAX_STRINGLEN]; + p = buf; + fp = filter_buf; + + while( *p ){ + if( *p == 0x01 ){ + //this is a color, skip the next 3 + p+=4; + }else{ + if( isalnum (*p) || ispunct(*p) || (*p==' ') || (*p=='\n') || (*p=='\r') || (*p=='\b') ){ + *fp = *p; + fp++; + } + p++; + } + } + *fp = '\0'; + + switch(Console_mode) + { + case Console_null: + con_null_Puts(0,filter_buf); + break; + case Console_raw: + con_raw_Puts(0,filter_buf); + break; + case Console_normal: + con_Puts(CON_MAINWND,filter_buf); + break; + default: + break; + } +} + +bool con_Input(char *buf, int buflen) +{ + if(!Con_init){ //the console hasn't been initialized yet + *buf = '\0'; + return false; + } + + if(Console_mode==Console_null) + { + *buf = '\0'; + return false; + } + + if(Console_mode==Console_raw) + { + return con_raw_Input(buf,buflen); + } + + if(!Con_read_buf){ //there is no read buffer...yipes + *buf = '\0'; + return false; + } + + if (Con_read_buf[0]) { + //we have a new buffer of input...send it away + strncpy(buf, Con_read_buf, buflen-1); + buf[buflen-1] = 0; + Con_read_buf[0] = 0; + return true; + } + + return false; +} + +void con_Defer(void) +{ + if(!Con_init) //the console hasn't been initialized yet + { + return; + } + + if(Console_mode==Console_null) + { + con_null_Defer(); + return; + } + + if(Console_mode==Console_raw) + { + con_raw_Defer(); + return; + } + + //handle any keyboard input + con_DoKeyboard(); +} + +bool con_Create(int flags) +{ + //determine what type of console to open up + if(flags&APPFLAG_USESERVICE) + { + //use the NULL driver! + Console_mode = Console_null; + }else + { + if(flags&APPFLAG_USESVGA) + { + //use the regular ncurses driver + Console_mode = Console_normal; + }else + { + //use stdout driver + Console_mode = Console_raw; + } + } + + if(Console_mode == Console_null) + { + Con_init = con_null_Create(); + return Con_init; + } + + if(Console_mode == Console_raw) + { + Con_init = con_raw_Create(); + return Con_init; + } + + if(!LoadCursesLib(true)) + { + fprintf(stderr,"LoadLib: Unable to load ncurses lib\n"); + exit(-1); + }else + { + fprintf(stderr,"LoadLib: Loaded ncurses lib\n"); + } + + //start up the curses library + initscr(); + + Con_cols = COLS; + Con_rows = LINES-1; //one less, since the bottom window takes up one row + + //create the sub windows + Con_main_wnd_ptr = newwin(Con_rows,Con_cols,0,0); + Con_input_wnd_ptr = newwin(1,Con_cols,Con_rows,0); + + if(!Con_main_wnd_ptr||!Con_input_wnd_ptr){ + //error creating subdirectories. + return false; + } + + //clear the subwindows to make them nice + wclear(Con_main_wnd_ptr); + wclear(Con_input_wnd_ptr); + wrefresh(Con_main_wnd_ptr); + wrefresh(Con_input_wnd_ptr); + scrollok(Con_main_wnd_ptr,true); + leaveok(Con_input_wnd_ptr,true); + leaveok(Con_main_wnd_ptr,true); + + //allocate any memory needed for buffers + Con_inp_buf = (char *)mem_malloc(sizeof(char) * (Con_cols+4)); + Con_read_buf = (char *)mem_malloc(sizeof(char) * (Con_cols+4)); + if(!Con_inp_buf || !Con_read_buf){ + //error allocating memory + return false; + } + memset(Con_inp_buf,0,sizeof(char)*(Con_cols+4)); + memset(Con_read_buf,0,sizeof(char)*(Con_cols+4)); + Con_last_command[0] = '\0'; + + // setup the keyboard for use + //!crmode(); + //!keypad(stdscr,TRUE); + //!noecho(); + //!cbreak(); + //nodelay(stdscr,true); + + Con_inp_pos=0; + Con_init = true; + + move(0,0); + con_Update(CON_INPUTWND); + con_Update(CON_MAINWND); + + return true; +} + +void con_Destroy(void) +{ + if(Console_mode==Console_null) + { + con_null_Destroy(); + return; + } + if(Console_mode==Console_raw) + { + con_raw_Destroy(); + return; + } + + // reset the keyboard + //!echo(); + //!nocbreak(); + + //delete the sub windows + if(Con_main_wnd_ptr) + delwin(Con_main_wnd_ptr); Con_main_wnd_ptr = NULL; + if(Con_input_wnd_ptr) + delwin(Con_input_wnd_ptr); Con_input_wnd_ptr = NULL; + + //free any allocated memory + if(Con_inp_buf){ + mem_free(Con_inp_buf); + Con_inp_buf = NULL; + } + + if(Con_read_buf){ + mem_free(Con_read_buf); + Con_read_buf = NULL; + } + Con_cols = Con_rows = 0; + + //shutdown curses + endwin(); + + LoadCursesLib(false); +} + +//put some data up on the screen +void con_Puts(int window,const char *str) +{ + if(Console_mode==Console_null) + { + con_null_Puts(window,str); + return; + } + if(Console_mode==Console_raw) + { + con_raw_Puts(window,str); + return; + } + + int string_len = strlen(str); + if(!string_len) + return; + + if(window&CON_MAINWND){ + //add the string to the main window + if(Con_main_wnd_ptr){ + wprintw(Con_main_wnd_ptr,str); + } + } + + if(window&CON_INPUTWND){ + //add the string to the input window + + //fill in the buffer + if(Con_input_wnd_ptr) + wprintw(Con_input_wnd_ptr,str); + } + + con_Update(window); +} + +//HACK!!! this shouldn't be dependant...oh well +int ddio_KeyInKey(); +int ddio_KeyToAscii(int code); + +void con_DoKeyboard(void) +{ + int keypressed = ddio_KeyInKey(); + + while(keypressed!=0) + { + //we got a key...handle it + switch (keypressed){ + case 0xCB://KEY_LEFT: // Left arrow + Con_inp_pos = max(Con_inp_pos-1,0); + con_Update(CON_INPUTWND); + break; + + case 0xCD://KEY_RIGHT: // Right arrow + { + int len = strlen(Con_inp_buf); + Con_inp_pos = min(Con_inp_pos+1, len); + con_Update(CON_INPUTWND); + }break; + + case 0xC8://KEY_UP: // Up arrow + // Replace the current buffer that is being typed with the last completed command (if there was one) + if(Con_last_command[0]!=0){ + memset(Con_inp_buf, 0, Con_cols+4); + strcpy(Con_inp_buf,Con_last_command); + + Con_inp_pos = strlen(Con_last_command); + + con_Update(CON_INPUTWND); + } + break; + + case 0xD3://KEY_DELETE: // Delete + { + // Move all the characters that followed the + // deleted character (on the same line) one + // space back (to the left) in the matrix. + for (int x = Con_inp_pos; x < Con_cols; x++) + Con_inp_buf[x] = Con_inp_buf[x+1]; + + con_Update(CON_INPUTWND); + }break; + case 0x0E://KEY_BACKSP: + { + // Move the caret back one space, and then + // process this like the DEL key. + if (Con_inp_pos > 0) { + Con_inp_pos--; + + for (int x = Con_inp_pos; x < Con_cols; x++) + Con_inp_buf[x] = Con_inp_buf[x+1]; + + con_Update(CON_INPUTWND); + } + }break; + case 0x1C://KEY_ENTER: + { + // Go to the beginning of the next line. + // The bottom line wraps around to the top. + strcpy(Con_read_buf, Con_inp_buf); + strcat(Con_inp_buf,"\n"); + + //add the string to the buffer + con_Puts(CON_MAINWND,Con_inp_buf); + + //only save the buffer if there is text in the buffer + char *p = Con_read_buf; + while( *p ){ + if(isalnum(*p)){ + strcpy(Con_last_command,Con_read_buf); + break; + } + p++; + } + + memset(Con_inp_buf, 0, Con_cols+4); + Con_inp_pos = 0; + con_Update(CON_INPUTWND); + }break; + case 0x01://KEY_ESC: // Escape + { + memset(Con_inp_buf, 0, Con_cols+4); + Con_inp_pos = 0; + con_Update(CON_INPUTWND); + }break; + default: + if (Con_inp_pos < (Con_cols-2)) { + // Add the character to the text buffer. + unsigned char str[2]; + str[0] = ddio_KeyToAscii(keypressed); + str[1] = 0; + if(str[0]!=255) + { + Con_inp_buf[Con_inp_pos] = str[0]; + Con_inp_buf[Con_inp_pos+1] = '\0'; + con_Update(CON_INPUTWND); + Con_inp_pos++; + } + } + } + + + keypressed = ddio_KeyInKey(); + } +} diff --git a/linux/lnxcon_null.cpp b/linux/lnxcon_null.cpp new file mode 100644 index 000000000..4cec06e15 --- /dev/null +++ b/linux/lnxcon_null.cpp @@ -0,0 +1,78 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxcon_null.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2000/04/18 00:00:39 $ +* $Author: icculus $ +* +* +* +* $Log: lnxcon_null.cpp,v $ +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 1 4/22/99 2:04a Jeff + * + * 4 4/19/99 4:52p Jeff + * removed useless calls to curses lib for keyboard setup + * + * 3 4/19/99 3:58a Jeff + * got console working again (keyboard based off ddio) + * + * 2 4/14/99 1:59a Jeff + * fixed case mismatched #includes + * + * 1 1/12/99 3:42a Jeff +* +* $NoKeywords: $ +*/ + +#include "DDAccess.h" +#include "application.h" +#include "AppConsole.h" +#include "TaskSystem.h" +#include "mono.h" + +#include +#include +#include + +//put some data up on the screen +void con_null_Puts(int window,const char *str); + +void con_null_Printf(const char *fmt, ...) +{ + char buffer[1024]; + va_list args; + va_start(args,fmt); + vsprintf(buffer,fmt,args); + va_end(args); + con_null_Puts(0,buffer); +} + +bool con_null_Input(char *buf, int buflen) +{ + return false; +} + +void con_null_Defer(void) +{ +} + +bool con_null_Create(void) +{ + printf("Descent 3 Dedicated Server\n"); + printf("Running in quiet mode.\n"); + printf("To Administer, you must telnet in to the dedicated server.\n"); + return true; +} + +void con_null_Destroy(void) +{ +} + +//put some data up on the screen +void con_null_Puts(int window,const char *str) +{ + mprintf((0,(char *)str)); +} diff --git a/linux/lnxcon_raw.cpp b/linux/lnxcon_raw.cpp new file mode 100644 index 000000000..d0166ea2e --- /dev/null +++ b/linux/lnxcon_raw.cpp @@ -0,0 +1,196 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxcon_raw.cpp $ +* $Revision: 1.2 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* stdout console driver +* +* $Log: lnxcon_raw.cpp,v $ +* Revision 1.2 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 2 7/19/99 12:54p Jeff + * created lnxcon_raw.cpp so ncurses is now only used for SVGALib, cuts + * down on redraw +* +* $NoKeywords: $ +*/ + +#include "DDAccess.h" +#include "application.h" +#include "AppConsole.h" +#include "TaskSystem.h" +#include "mono.h" +//#include "local_malloc.h" +#include "pstring.h" +#include +#include +#include +#include +#include +#include + +#define min(x,y) (((x)<(y))?(x):(y)) +#define max(x,y) (((x)>(y))?(x):(y)) + + +static char *Con_raw_read_buf = NULL; //The next buffer of text from user input +static char *Con_raw_inp_buf = NULL, Con_raw_inp_pos=0; //Currently updating input buffer of text (and it's position) +static char Con_raw_last_command[CON_MAX_STRINGLEN]; //The last command entered by the user +static int Con_raw_cols = 0,Con_raw_rows = 0; //The size of the main window (input window is (1 row, Con_cols)) +static bool Con_raw_newline = false; + + +//put some data up on the screen +void con_raw_Puts(int window,const char *str); + +void con_raw_Printf(const char *fmt, ...) +{ + char buffer[1024]; + va_list args; + va_start(args,fmt); + vsprintf(buffer,fmt,args); + va_end(args); + con_raw_Puts(0,buffer); +} + +bool con_raw_Input(char *buf, int buflen) +{ + if(!Con_raw_read_buf){ //there is no read buffer...yipes + *buf = '\0'; + return false; + } + + if (Con_raw_read_buf[0]) { + //we have a new buffer of input...send it away + strncpy(buf, Con_raw_read_buf, buflen-1); + buf[buflen-1] = 0; + Con_raw_read_buf[0] = 0; + return true; + } + + return false; +} + +int ddio_KeyInKey(); +int ddio_KeyToAscii(int code); + +void con_raw_Defer(void) +{ + int keypressed = ddio_KeyInKey(); + + while(keypressed!=0) + { + //we got a key...handle it + switch (keypressed){ + case 0xCB://KEY_LEFT: // Left arrow + case 0xCD://KEY_RIGHT: // Right arrow + case 0xC8://KEY_UP: // Up arrow + case 0xD3://KEY_DELETE: // Delete + case 0x01://KEY_ESC: // Escape + break; + case 0x0E://KEY_BACKSP: + { + // Move the caret back one space, and then + // process this like the DEL key. + if (Con_raw_inp_pos > 0) { + Con_raw_inp_pos--; + + for (int x = Con_raw_inp_pos; x < Con_raw_cols; x++) + Con_raw_inp_buf[x] = Con_raw_inp_buf[x+1]; + + con_raw_Puts(-1,"\b \b"); + } + }break; + case 0x1C://KEY_ENTER: + { + // Go to the beginning of the next line. + // The bottom line wraps around to the top. + strcpy(Con_raw_read_buf, Con_raw_inp_buf); + strcat(Con_raw_inp_buf,"\n"); + + //go to the next line + con_raw_Puts(-1,"\n"); + + //only save the buffer if there is text in the buffer + char *p = Con_raw_read_buf; + while( *p ){ + if(isalnum(*p)){ + strcpy(Con_raw_last_command,Con_raw_read_buf); + break; + } + p++; + } + + memset(Con_raw_inp_buf, 0, Con_raw_cols+4); + Con_raw_inp_pos = 0; + }break; + default: + if (Con_raw_inp_pos < (Con_raw_cols-2)) { + // Add the character to the text buffer. + unsigned char str[2]; + str[0] = ddio_KeyToAscii(keypressed); + str[1] = 0; + if(str[0]!=255) + { + Con_raw_inp_buf[Con_raw_inp_pos] = str[0]; + Con_raw_inp_buf[Con_raw_inp_pos+1] = '\0'; + Con_raw_inp_pos++; + con_raw_Puts(-1, (const char *)str); + } + } + } + + + keypressed = ddio_KeyInKey(); + } +} + +bool con_raw_Create(void) +{ + Con_raw_cols = 80; + Con_raw_rows = 24; //one less, since the bottom window takes up one row + + //allocate any memory needed for buffers + Con_raw_inp_buf = (char *)malloc(sizeof(char) * (Con_raw_cols+4)); + Con_raw_read_buf = (char *)malloc(sizeof(char) * (Con_raw_cols+4)); + if(!Con_raw_inp_buf || !Con_raw_read_buf){ + //error allocating memory + return false; + } + memset(Con_raw_inp_buf,0,sizeof(char)*(Con_raw_cols+4)); + memset(Con_raw_read_buf,0,sizeof(char)*(Con_raw_cols+4)); + Con_raw_last_command[0] = '\0'; + + Con_raw_inp_pos=0; + + return true; +} + +void con_raw_Destroy(void) +{ + //free any allocated memory + if(Con_raw_inp_buf){ + free(Con_raw_inp_buf); + Con_raw_inp_buf = NULL; + } + + if(Con_raw_read_buf){ + free(Con_raw_read_buf); + Con_raw_read_buf = NULL; + } + Con_raw_cols = Con_raw_rows = 0; +} + +//put some data up on the screen +void con_raw_Puts(int window,const char *str) +{ + fprintf(stdout,str,strlen(str)); + fflush(stdout); +} + diff --git a/linux/lnxdata.cpp b/linux/lnxdata.cpp new file mode 100644 index 000000000..ac7b129dd --- /dev/null +++ b/linux/lnxdata.cpp @@ -0,0 +1,223 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxdata.cpp $ +* $Revision: 1.3 $ +* $Date: 2004/03/21 17:11:39 $ +* $Author: kevinb $ +* +* Linux database routines +* +* $Log: lnxdata.cpp,v $ +* Revision 1.3 2004/03/21 17:11:39 kevinb +* Fixes so linux will compile again. Tested with gcc-2.96 +* +* Revision 1.2 2000/04/28 20:19:05 icculus +* Writes "registry" to prefpath instead of current directory. +* +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 9 7/14/99 9:09p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include +#include +#include +#include +#include + +#include "appdatabase.h" +#include "linux/lnxdatabase.h" +#include "pserror.h" +#include "mono.h" +#include "pserror.h" +#include "registry.h" +#include "loki_utils.h" + +#define REGISTRY_FILENAME ".Descent3Registry" + +//Construction and destruction. + +oeLnxAppDatabase::oeLnxAppDatabase() +{ + //Open up the database file, for reading, read in all data and keep it in memory + //then close the database + + char *prefPath = (char *)loki_getprefpath(); + char fileName[strlen(prefPath) + strlen(REGISTRY_FILENAME) + 2]; + sprintf(fileName, "%s/%s", prefPath, REGISTRY_FILENAME); + + database = new CRegistry(fileName); + database->Import(); + create_record("Version"); +} + +oeLnxAppDatabase::oeLnxAppDatabase(oeLnxAppDatabase *parent) +{ + char name[256]; + CRegistry *db = parent->GetSystemRegistry(); + db->Export(); + database = new CRegistry(""); + db->GetSystemName(name); + database->SetSystemName(name); + database->Import(); +} + +oeLnxAppDatabase::~oeLnxAppDatabase() +{ + if(database){ + database->Export(); + delete database; + return; + } + + mprintf((0,"Can't Export Database Since It's Not There!\n")); +} + +CRegistry *oeLnxAppDatabase::GetSystemRegistry() +{ + return database; +} + +//Record functions +//these are actual folders of information + +//creates an empty classification or structure where you can store information +bool oeLnxAppDatabase::create_record(const char *pathname) +{ + ASSERT(pathname!=NULL); + if(database){ + database->CreateKey((char *)pathname); + return true; + } + mprintf((0,"Can't CreateKey because database NULL\n")); + return false; +} + + +//set current database focus to a particular record +bool oeLnxAppDatabase::lookup_record(const char *pathname) +{ + ASSERT(pathname); + if(database){ + return database->LookupKey((char *)pathname); + } + mprintf((0,"Can't lookup key because database NULL\n")); + return false; +} + + +//read either a string from the current record +bool oeLnxAppDatabase::read(const char *label, char *entry, int *entrylen) +{ + ASSERT(label); + ASSERT(entry); + ASSERT(entrylen); + if(!database){ + mprintf((0,"Can't read record because database NULL\n")); + return false; + } + + //See if it exists + int size = database->GetDataSize((char *)label); + if(size>0) + *entrylen = size-1;//-1 because of NULL + else + return false; + + //ok it exists, no look it up + database->LookupRecord((char *)label,entry); + return true; +} + +//read a variable-sized integer from the current record +bool oeLnxAppDatabase::read(const char *label, void *entry, int wordsize) +{ + ASSERT(label); + ASSERT(entry); + if(!database){ + mprintf((0,"Can't read record because Database NULL\n")); + return false; + } + + int size = database->GetDataSize((char *)label); + if(size==0) + return false; + + //ok so it does exist + int data; + database->LookupRecord((char *)label,&data); + + switch(wordsize){ + case 1: + *((unsigned char *)entry) = (unsigned char)data; + break; + case 2: + *((unsigned short *)entry) = (unsigned short)data; + break; + case 4: + *((unsigned int *)entry) = (unsigned int)data; + break; + default: + mprintf((0,"Unable to read key %s, unsupported size",label)); + return false; + break; + } + return true; +} + +bool oeLnxAppDatabase::read(const char *label, bool *entry) +{ + bool data; + if(!read(label,&data,sizeof(bool))) + return false; + + *entry = (data!=0)?true:false; + return true; +} + +//write either an integer or string to a record. +bool oeLnxAppDatabase::write(const char *label, const char *entry, int entrylen) +{ + ASSERT(label); + ASSERT(entry); + if(!database){ + mprintf((0,"Can't write record because database NULL\n")); + return false; + } + + return database->CreateRecord((char *)label,REGT_STRING,(void *)entry); +} + + +bool oeLnxAppDatabase::write(const char *label, int entry) +{ + ASSERT(label); + if(!database){ + mprintf((0,"Can't write record because database NULL\n")); + return false; + } + return database->CreateRecord((char *)label,REGT_DWORD,&entry); +} + +// get the current user's name from the os +void oeLnxAppDatabase::get_user_name(char* buffer, ulong* size) +{ + struct passwd *pwuid = getpwuid(geteuid()); + + if ((pwuid != NULL) && (pwuid->pw_name != NULL)) + { + strncpy(buffer,pwuid->pw_name,(*size)-1); + buffer[(*size)-1] = '\0'; + *size = strlen(buffer); + }else + { + strncpy(buffer,"Unknown",(*size)-1); + buffer[(*size)-1] = '\0'; + *size = strlen(buffer); + } +} + diff --git a/linux/lnxdebug.cpp b/linux/lnxdebug.cpp new file mode 100644 index 000000000..c3a2501d5 --- /dev/null +++ b/linux/lnxdebug.cpp @@ -0,0 +1,88 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxdebug.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2000/04/18 00:00:39 $ +* $Author: icculus $ +* +* Linux debugging routines +* +* $Log: lnxdebug.cpp,v $ +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 8 7/22/99 1:21p Jeff + * added 3rd debugging window + * + * 7 7/14/99 9:09p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include "debug.h" +#include "mono.h" +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// + +bool Debug_break=false; +bool Debug_mono=false; + +char *Debug_DumpInfo(); + +// if we are running under a debugger, then pass true +bool Debug_Init(bool debugger, bool mono_debug) +{ +#ifndef RELEASE + Debug_break = debugger; + + if (mono_debug) { + Debug_ConsoleInit(); + Debug_ConsoleOpen( 0, 9, 1, 78, 15, "Debug Spew"); + Debug_ConsoleOpen( 1, 1, 1, 58, 6, "Warnings"); + Debug_ConsoleOpen( 2, 1,61, 18, 6, "Stats"); + + mprintf((0, "Linux system.\n")); + } + + if (Debug_break) + mprintf((0, "Debug Break enabled.\n")); + +#endif //ifndef RELEASE + + return true; +} + +//Does a messagebox with a stack dump +//Messagebox shows topstring, then stack dump, then bottomstring +//Return types are the same as the Windows return values +int Debug_ErrorBox(int type,const char *topstring, const char *title,const char *bottomstring) +{ + int answer = 0; + char *dumptext = Debug_DumpInfo(); + + fprintf(stderr,"\r\n%s(%s)\r\n\n%s\r\n\n%s\r\n",title,topstring,dumptext,bottomstring); + + debug_break(); + + return answer; +} + +// displays an message box +// Returns the same values as the Win32 MessageBox() function +int Debug_MessageBox(int type, const char *title, const char *str) +{ + return Debug_ErrorBox(type,str,"Descent 3 Message",""); +} + +/////////////////////////////////////////////////////////////////////////////// + +char *Debug_DumpInfo(void) +{ + static char e[]= "System Error"; + return e; +} diff --git a/linux/lnxmono.cpp b/linux/lnxmono.cpp new file mode 100644 index 000000000..3240158d5 --- /dev/null +++ b/linux/lnxmono.cpp @@ -0,0 +1,633 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxmono.cpp $ +* $Revision: 1.3 $ +* $Date: 2004/02/23 03:03:48 $ +* $Author: ryan $ +* +* Linux monochrome routines +* +* $Log: lnxmono.cpp,v $ +* Revision 1.3 2004/02/23 03:03:48 ryan +* Patched to compile with gcc3 and a modern linux distro... +* +* Revision 1.2 2000/05/29 05:39:59 icculus +* Changed some exit() calls to _exit(). +* +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 13 8/22/99 5:52a Jeff + * remote monochrome server code added + * + * 12 7/14/99 9:09p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include "debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// -------- Start TCP/IP Mono Logging Section +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TCPLOG_LEN 2000 +#define SOCKET int +#define SOCKADDR_IN sockaddr_in +#define SOCKADDR sockaddr +#define INVALID_SOCKET -1 +static SOCKET tcp_log_sock; +static struct sockaddr_in tcp_log_addr; +static char tcp_log_buffer[MAX_TCPLOG_LEN]; +static int Console_fd = -1; +static bool Mono_initialized = 0; +static bool Mono_use_real = false; +static bool Mono_use_window_remote = false; +static int Debug_logfile = 0; +bool Debug_print_block = false; +#define min(a,b) ((a +#include + +#if DLOPEN_PTHREAD +typedef int (*pthread_create_fp)(pthread_t *__thread,__const pthread_attr_t *__attr,void *(*__start_routine) (void *),void *__arg); +typedef int (*pthread_detach_fp)(pthread_t __th); +typedef pthread_t (*pthread_self_fp)(void); + +static pthread_create_fp dpthread_create = NULL; +static pthread_detach_fp dpthread_detach = NULL; +static pthread_self_fp dpthread_self = NULL; +#else +#define dpthread_create pthread_create +#define dpthread_detach pthread_detach +#define dpthread_self pthread_self +#endif + +static unsigned long long Timer_sys_start_time = 0; +static unsigned long long Timer_accum = 0,Timer_high_mark = 0; + +static float nw_TCPLoggingTimer(void) +{ + unsigned long time_ms; + unsigned long long ret; + + struct timeval t; + gettimeofday(&t,NULL); + + ret = (unsigned long long)t.tv_sec*1000000.0 + t.tv_usec; + + if(ret > Timer_high_mark) + { + Timer_high_mark = ret; + }else + { + // timer roll over + if(Timer_high_mark>0) + Timer_accum += (Timer_high_mark - Timer_sys_start_time); + Timer_high_mark = ret; + Timer_sys_start_time = ret; + ret = 100;//give some time + } + + time_ms = Timer_accum + ret - Timer_sys_start_time; + + return (float)((double)time_ms/((double)1000000.0)); +} + +static bool nw_TCPLoggingTimer_Init(void) +{ + struct timeval t; + gettimeofday(&t,NULL); + + Timer_sys_start_time = (unsigned long long)t.tv_sec*1000000.0 + t.tv_usec; + Timer_accum = 0; + Timer_high_mark = 0; + return true; +} + +static unsigned char outgoing_mono_packet[512]; +static volatile int outgoing_packet_size = 0; +static volatile float outgoing_last_time_sent = 0; +static volatile bool outgoing_packet_flush = false; +static volatile bool outgoing_building_packet = false; + +void *nw_TCPLoggingWorker(void *arg) +{ + dpthread_detach(dpthread_self()); + + while(1) + { + if(tcp_log_sock==INVALID_SOCKET) + break; + + fd_set wfds; + timeval timeout = {0,0}; + + FD_ZERO(&wfds); + FD_SET(tcp_log_sock, &wfds ); + + int sock_writable = select(tcp_log_sock+1, NULL, &wfds, NULL, &timeout); + + if ( sock_writable == -1 ) + { + fprintf(stdout,"Error blocking on Monochrome server port\n"); + _exit(1); + } + + if(!sock_writable) + { + continue; + } + + if(outgoing_packet_size==0) + continue; + if(outgoing_building_packet) + continue; + + int save_size = outgoing_packet_size; + outgoing_packet_size = -1;//this informs the other thread that we are sending + + float curr_time = nw_TCPLoggingTimer(); + if(curr_time>(outgoing_last_time_sent+0.05f)) + { + //time to send the packet + outgoing_last_time_sent = curr_time; + + send(tcp_log_sock,outgoing_mono_packet,save_size,0); + + save_size = 0; + } + + outgoing_packet_size = save_size; + } + + return NULL; +} + +bool nw_InitTCPLogging(char *ip,unsigned short port) +{ +#if DLOPEN_PTHREAD + // load up the pthread library + void *lib = dlopen("libpthread.so",RTLD_GLOBAL|RTLD_NOW); + if(!lib) + { + fprintf(stdout,"Error: Unable to load libpthread.so\n"); + _exit(1); + } + dpthread_create = (pthread_create_fp)dlsym(lib,"pthread_create"); + dpthread_detach = (pthread_detach_fp)dlsym(lib,"pthread_detach"); + dpthread_self = (pthread_self_fp)dlsym(lib,"pthread_self"); +#endif + + unsigned long argp = 1; + int addrlen = sizeof(SOCKADDR_IN); + tcp_log_sock = socket(AF_INET,SOCK_STREAM,0); + if(INVALID_SOCKET == tcp_log_sock) + { + return false; + } + + memset( &tcp_log_addr, 0, sizeof(SOCKADDR_IN) ); + tcp_log_addr.sin_family = AF_INET; + tcp_log_addr.sin_addr.s_addr = INADDR_ANY; + tcp_log_addr.sin_port = 0; + + if( bind(tcp_log_sock,(SOCKADDR*)&tcp_log_addr, sizeof (sockaddr)) ) + { + return false; + } + + tcp_log_addr.sin_addr.s_addr = inet_addr(ip); + tcp_log_addr.sin_port = htons(port); + + fd_set write_fs; + FD_ZERO(&write_fs); + FD_SET(tcp_log_sock,&write_fs); + select(tcp_log_sock+1,NULL,&write_fs,NULL,NULL); + + fprintf(stdout,"Connecting to monochrome server (%s:%d)...\n",ip,port); + + if(connect(tcp_log_sock,(SOCKADDR *)&tcp_log_addr,addrlen)) + { + int wserr; + wserr = errno; + fprintf(stdout,"Mono: Connect Failed..."); + + switch(wserr) + { + case EBADF: + fprintf(stdout,"EBADF\n"); + break; + case EFAULT: + fprintf(stdout,"EFAULT\n"); + break; + case ENOTSOCK: + fprintf(stdout,"ENOTSOCK\n"); + break; + case EISCONN: + fprintf(stdout,"EISCONN\n"); + break; + case ECONNREFUSED: + fprintf(stdout,"EREFUSED\n"); + break; + case ETIMEDOUT: + fprintf(stdout,"ETIMEDOUT\n"); + break; + case ENETUNREACH: + fprintf(stdout,"ENETUNREACH\n"); + break; + default: + fprintf(stdout,"Unknown error %d\n",wserr); + break; + } + + return false; + } + + fprintf(stdout,"Monochrome server connected\n"); + + // init the packet sent time + pthread_t thread; + nw_TCPLoggingTimer_Init(); + outgoing_last_time_sent = nw_TCPLoggingTimer(); + + dpthread_create(&thread,NULL,nw_TCPLoggingWorker,NULL); + + return true; +} + + +void nw_TCPPrintf(int n, char * format, ... ) +{ + return; +} + +void nw_SendMonoPacket(unsigned char *data,int size) +{ + if(tcp_log_sock==INVALID_SOCKET){ + return; + } + + if(size>512) + { + fprintf(stdout,"Mono: Packet > 512 bytes\n"); + _exit(1); + } + + outgoing_building_packet = true; + while(outgoing_packet_size==-1); + + if(outgoing_packet_size+size>512) + { + // crap...we need to flush + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(tcp_log_sock, &wfds ); + + int sock_writable = select(tcp_log_sock+1, NULL, &wfds, NULL, NULL); + + if ( sock_writable == -1 ) + { + fprintf(stdout,"Error blocking on Monochrome server port\n"); + _exit(1); + } + + send(tcp_log_sock,outgoing_mono_packet,outgoing_packet_size,0); + outgoing_packet_size = 0; + } + + memcpy(&outgoing_mono_packet[outgoing_packet_size],data,size); + outgoing_packet_size += size; + + outgoing_building_packet = false; +} + +// ---------------- End TCP/IP Mono Logging Section + +void Debug_LogClose(); + +bool Debug_Logfile(const char *filename) +{ + if (Debug_logfile == -1) { + Debug_logfile = open(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); + if (Debug_logfile == -1) { + Debug_MessageBox(OSMBOX_OK, "Debug", "FYI Logfile couldn't be created."); + return false; + } + atexit(Debug_LogClose); + } + Debug_LogWrite("BEGINNING LOG\n\n"); + + return true; +} + + +void Debug_LogWrite(const char *str) +{ + if (Debug_logfile > -1) + write(Debug_logfile, str, strlen(str)); +} + +void Debug_LogClose() +{ + if (Debug_logfile > -1) { + Debug_LogWrite("\nEND LOG"); + close(Debug_logfile); + Debug_logfile = -1; + } +} + +//void Debug_SetSpewFilter(uint spew_filter) +void Debug_SetSpewFilter(uint) +{ +} + +//uint Debug_GetSpewFilter(uint spew_filter) +uint Debug_GetSpewFilter(uint) +{ + return 0; +} + +void Debug_ConsoleExit() +{ + if(Console_fd>=0) + close(Console_fd); + Console_fd = -1; +} + + +#define MAX_ARGS 30 +#define MAX_CHARS_PER_ARG 100 +extern char GameArgs[MAX_ARGS][MAX_CHARS_PER_ARG]; +int FindArg (char *which); + +bool Debug_ConsoleInit() +{ + int n=0; + + if (Mono_initialized) + return 1; + + //Only use monochrome if D3_MONO environment var is set + atexit(Debug_ConsoleExit); + + Console_fd = open("/dev/omono",O_WRONLY); + if(Console_fd>=0) + { + Mono_use_real = true; + Mono_initialized = 1; + } + + int arg; + if((arg = FindArg("-monotcp"))!=0) + { + char address[256]; + strcpy(address,GameArgs[arg+1]); + char *port_ptr; + port_ptr = strchr(address,':'); + if(port_ptr) + { + *port_ptr = '\0'; + port_ptr++; + unsigned short port; + port = atoi(port_ptr); + if(nw_InitTCPLogging(address,port)) + { + Mono_use_window_remote = true; + Mono_initialized = 1; + } + } + } + return 1; +} + +void Debug_ConsoleRedirectMessages(int virtual_window, int physical_window) +{ + if(!Mono_initialized) + return; + +/* +0x02 Redirect Messages + + Parameters: + Offset Size Value Description + ====== ==== ===== =========== + 0x01 0x01 0-7 Virtual window handle to be associated with physical window + 0x02 0x01 0-3 Window handle to be redirected +*/ + //create the packet and send it off + unsigned char packet[3]; + packet[0x00] = 0x02; //control code + packet[0x01] = virtual_window; //virtual_handle + packet[0x02] = physical_window; //physical_handle + + //send the packet + if(Mono_use_real) + { + write(Console_fd,packet,3); + } + if(Mono_use_window_remote) + { + nw_SendMonoPacket(packet,3); + } +} + +void Debug_ConsoleOpen(int n, int row, int col, int width, int height, char * title ) +{ + if(!Mono_initialized) + return; + +/* +0x00 Open a window + + Parameters: + Offset Size Value Description + ====== ==== ===== =========== + 0x01 0x01 0-3 Window handle to be associated with window + 0x02 0x01 0-23 Upper left corner row value + 0x03 0x01 0-78 Upper left corner col value + 0x04 0x01 1-79 Number of cols (width) + 0x05 0x01 1-24 Number of rows (height) + 0x06 0x01 1-255 Length of the title string + 0x07-> [0x06] ---- Title string (not \0 terminated) + */ + + //create the packet and send it off + unsigned char packet[512]; + int len = (title)?strlen(title):1; + packet[0x00] = 0x00; //control code + packet[0x01] = n; //window_handle + packet[0x02] = row; //row + packet[0x03] = col; //col + packet[0x04] = width;//width + packet[0x05] = height;//height + packet[0x06] = len; + if(title) + memcpy(&packet[0x07],title,len); + else + packet[0x07] = ' '; + + //send the packet + if(Mono_use_real) + { + write(Console_fd,packet,len+7); + } + if(Mono_use_window_remote) + { + nw_SendMonoPacket(packet,len+7); + } +} + + +void Debug_ConsoleClose(int n) +{ + if(!Mono_initialized) + return; + +/* + 0x01 Closes a window + + Parameters: + Offset Size Value Description + ====== ==== ===== =========== + 0x01 0x01 0-3 Window handle to be associated with window + + */ + //create the packet and send it off + unsigned char packet[2]; + packet[0x00] = 0x01; //control code + packet[0x01] = n; //window_handle + + //send the packet + if(Mono_use_real) + { + write(Console_fd,packet,2); + } + if(Mono_use_window_remote) + { + nw_SendMonoPacket(packet,2); + } +} + +#ifdef _DEBUG +#define MAX_MONO_BUFFER 2048 +#else +#define MAX_MONO_BUFFER 32 +#endif +static char Mono_buffer[MAX_MONO_BUFFER]; +void Debug_ConsolePrintf( int n, char * format, ... ) +{ + va_list marker; + va_start(marker,format); + int text_len = vsnprintf(Mono_buffer,MAX_MONO_BUFFER,format,marker); + va_end(marker); + + if( n == 0 ) + { + printf("%s", Mono_buffer); + + int end = strlen(Mono_buffer) - 1; + if((end > 1) && (Mono_buffer[end]!=0x0a) && (Mono_buffer[end]!=0x0d)) + { + printf("\n"); + } + } + nw_TCPPrintf(n,Mono_buffer); + + if(!Mono_initialized) + return; + +/* +0x03 Window Print at cursor position + + Parameters: + Offset Size Value Description + ====== ==== ===== =========== + 0x01 0x01 0-3 Window handle to be associated with window + 0x02 0x02 0-512 (Little Endian) Length of the text string + 0x04 [0x02] ---- Text string (not \0 terminated) +*/ + + //create the packet and send it off + text_len = min(text_len,512); + unsigned char packet[518]; + packet[0x00] = 0x03; //control code + packet[0x01] = n; //window_handle + *((unsigned short *)&packet[0x02]) = text_len; + memcpy(&packet[0x04],Mono_buffer,text_len); + + //send the packet + if(Mono_use_real) + { + write(Console_fd,packet,text_len+4); + } + if(Mono_use_window_remote) + { + nw_SendMonoPacket(packet,text_len+4); + } +} + + +void Debug_ConsolePrintf( int n, int row, int col, char * format, ... ) +{ + if(!Mono_initialized) + return; + + va_list marker; + va_start(marker,format); + int text_len = vsnprintf(Mono_buffer,MAX_MONO_BUFFER,format,marker); + va_end(marker); + +/* +0x04 Window Print at row/col given + + Parameters: + Offset Size Value Description + ====== ==== ===== =========== + 0x01 0x01 0-3 Window handle to be associated with window + 0x02 0x01 0-24 Starting row (relative to window) of text + 0x03 0x01 0-79 Starting col (relative to window) of text + 0x04 0x02 0-512 (Little Endian) Length of the text string + 0x06 [0x04] ---- Text string (not \0 terminated) +*/ + //create the packet and send it off + unsigned char packet[518]; + text_len = min(text_len,512); + packet[0x00] = 0x04; //control code + packet[0x01] = n; //window_handle + packet[0x02] = row; //row + packet[0x03] = col; //col + *((unsigned short *)&packet[0x04]) = text_len; + memcpy(&packet[0x06],Mono_buffer,text_len); + + //send the packet + if(Mono_use_real) + { + write(Console_fd,packet,text_len+6); + } + if(Mono_use_window_remote) + { + //we can't send these to a mono server, they flood us + //nw_SendMonoPacket(packet,text_len+6); + } +} diff --git a/linux/lnxtask.cpp b/linux/lnxtask.cpp new file mode 100644 index 000000000..3a6dc3fff --- /dev/null +++ b/linux/lnxtask.cpp @@ -0,0 +1,48 @@ +/* +* $Logfile: /DescentIII/Main/linux/lnxtask.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2000/04/18 00:00:39 $ +* $Author: icculus $ +* +* Linux multitasking routines +* +* $Log: lnxtask.cpp,v $ +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 3 7/14/99 9:09p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include "DDAccess.h" +#include "TaskSystem.h" +#include "pserror.h" + +osMutex::osMutex() +{ +} + +osMutex::~osMutex() +{ + Destroy(); +} + +bool osMutex::Create() +{ + return false; +} + +void osMutex::Destroy() +{ +} + +bool osMutex::Acquire(int timeout) +{ +} + +void osMutex::Release() +{ +} \ No newline at end of file diff --git a/linux/registry.cpp b/linux/registry.cpp new file mode 100644 index 000000000..9c336f99c --- /dev/null +++ b/linux/registry.cpp @@ -0,0 +1,493 @@ +/* +* $Logfile: /DescentIII/Main/linux/registry.cpp $ +* $Revision: 1.4 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* Linux registry routines +* +* $Log: registry.cpp,v $ +* Revision 1.4 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.3 2000/06/24 01:15:15 icculus +* patched to compile. +* +* Revision 1.2 2000/04/28 20:19:29 icculus +* Minor mprintf update +* +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 8 8/20/99 1:59p Jeff + * removed mprintfs... + * + * 7 7/14/99 9:09p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include +#include +#include +#include +//#include "local_malloc.h" +#include "registry.h" +#include "mono.h" + +//Convert a string that represents a hex value into an int +int hextoi(char *p) +{ + int value = 0; + while( (p) && (*p) && isalnum(*p) ){ + *p = toupper(*p); + if ( (*p>='0') && (*p<='9') ) + value = (value * 16) + ((*p)-'0'); + else if ( (*p>='A') && (*p<='F') ) + value = (value * 16) + ((*p)-'A'); + else + return value; + + p++; + } + return value; +} + +//Removes whitespace from the start of the given string. +//Returns a pointer to the first non-white character +char *SkipWhite(char *p) +{ + while (isspace(*p)) + p++; + return p; +} + +//Parses a quoted string +//Returns true if got string ok, else false +char *ParseString(char *p,char *buf,int bufsize,char sdelim,char edelim) +{ + char *save_p; + + p = SkipWhite(p); + + save_p = p; + + if (*p != sdelim) { + return NULL; + } + else + p++; //skip initial quote + + //Copy chars until endquote or out of space + while (*p && (*p != edelim) && --bufsize) + *buf++ = *p++; + + //Check for buffer overflow + if (bufsize <= 0) { + return NULL; + } + + //Check for missing endquote + if (! *p) { + return NULL; + } + + //Write terminator + *buf = 0; + + //Return new pointer (move over a char for end quote) + return p+1; +} + +//Parses a sequence of non-space characters +char *ParseToken(char *p,char *buf,int bufsize) +{ + char *save_p; + p = SkipWhite(p); + save_p = p; + + while (!isspace(*p) && (*p != ',') && *p && --bufsize) + *buf++ = *p++; + + *buf = 0; + + //Check for buffer overflow + if (bufsize <= 0) { + return NULL; + } + + return p; +} +#define PARSE_KEY(buf) do {ptr = ParseString(ptr,buf,sizeof(buf),'[',']'); } while(0) +#define PARSE_STRING(buf) do {ptr = ParseString(ptr,buf,sizeof(buf),'"','"'); } while (0) +#define PARSE_TOKEN(buf) do {ptr = ParseToken(ptr,buf,sizeof(buf)); } while (0) + +CRegistry::CRegistry(char *str) +{ + currentkey = root = NULL; + strcpy(name,str); +} + +CRegistry::~CRegistry() +{ + Destroy(); +} + +void CRegistry::GetSystemName(char *n) +{ + strcpy(n,name); +} + +void CRegistry::SetSystemName(char *n) +{ + strcpy(name,n); +} + +void CRegistry::Destroy(void) +{ + tKey *curr,*next; + curr = next = root; + while(curr){ + next = curr->next; + DestroyKey(curr); + curr = next; + } + root = NULL; +} + +void CRegistry::DestroyKey(tKey *key) +{ + tRecord *curr, *next; + curr = next = key->records; + while(curr){ + next = curr->next; + DestroyRecord(curr); + curr = next; + } + free(key); +} + +void CRegistry::DestroyRecord(tRecord *record) +{ + if(record->data){ + free(record->data); + record->data = NULL; + } + free(record); +} + +void CRegistry::Export() +{ + tKey *curr,*next; + curr = next = root; + FILE *file; + file = fopen(name,"wt"); + if(!file) + return; + while(curr){ + next = curr->next; + ExportKey(curr,file); + curr = next; + } + fclose(file); +} + +void CRegistry::ExportKey(tKey *key,FILE *file) +{ + tRecord *curr, *next; + curr = next = key->records; + //write out name + char buffer[258]; + sprintf(buffer,"[%s]\n",key->name); + fputs(buffer,file); + while(curr){ + next = curr->next; + ExportRecord(curr,file); + curr = next; + } +} + +void CRegistry::ExportRecord(tRecord *record,FILE *file) +{ + int *dw; + char *st; + char buffer[512]; + switch(record->type){ + case REGT_STRING: + { + st = (char *)record->data; + sprintf(buffer,"\"%s\"=\"%s\"\n",record->name,st); + }break; + case REGT_DWORD: + { + dw = (int *)record->data; + sprintf(buffer,"\"%s\"=dword:%X\n",record->name,*dw); + }break; + }; + fputs(buffer,file); +} + +bool CRegistry::Import() +{ + char buffer[500]; + char newbuff[500]; + FILE *file; + char *ptr; + file = fopen(name,"rt"); + if(!file){ + mprintf((0,"REGISTRY: Unable to import %s\n",name)); + return false; + } + mprintf((0,"REGISTRY: Importing %s\n",name)); + Destroy(); + + bool oktocreate; + //loop till we are done + while(!feof(file)){ + oktocreate = true; + char type = REGT_STRING; + + //read in the string + fgets(buffer,500,file); + ptr = buffer; + if(feof(file))//we are at the end of the file so there isn't data to continue + oktocreate = false; + //see what we read, a key or record, if the first character is a [ than a key " is record + if(oktocreate){ + if(buffer[0]=='['){ + //Create a key! + PARSE_KEY(newbuff); + //mprintf((0,"Found Key: |%s|\n",newbuff)); + CreateKey(newbuff); + }else if(buffer[0]=='\"'){ + //Create a record + //see what type of record by looking at whats after the = + char *p; + p = buffer; + bool done = false; + type = REGT_STRING; + while(!done){ + if( (!p) || (!*p) ) + done = true; + if( (p) && (*p=='=')){ + if(*(p+1)=='d') + type = REGT_DWORD; + else + type = REGT_STRING; + done = true; + } + p++; + } + //now we "SHOULD" know the type, parse the info + char data[300]; + int idata; + switch(type){ + case REGT_STRING: + PARSE_STRING(newbuff); + + // rcg06212000 getting a NULL ptr in here...added a check. + if (ptr == NULL) + continue; + + ptr++; //blow by = + PARSE_STRING(data); + if(!CreateRecord(newbuff,REGT_STRING,data)) + { + //mprintf((0,"Unable to create String record: %s\n",newbuff)); + }else + { + //mprintf((0,"Created String record %s = %s\n",newbuff,data)); + }break; + case REGT_DWORD: + PARSE_STRING(newbuff); + ptr+=7; //blow by =dword: + PARSE_TOKEN(data); + idata = hextoi(data); + if(!CreateRecord(newbuff,REGT_DWORD,&idata)) + { + //mprintf((0,"Unable to create dword record: %s\n",newbuff)); + }else + { + //mprintf((0,"Created dword record %s = %X\n",newbuff,idata)); + }break; + }; + }else + { + //mprintf((0,"Expected [ or \"\n")); + } + } + } + fclose(file); + return true; +} + +void CRegistry::CreateKey(char *name) +{ + tKey *curr; + if(LookupKey(name)){ + //mprintf((0,"Key: %s already exists\n",name)); + return; + } + if(!root){ + root = (tKey *)malloc(sizeof(tKey)); + if(!root) + return; + curr = root; + }else{ + curr = root; + while(curr->next){ + curr = curr->next; + } + curr->next = (tKey *)malloc(sizeof(tKey)); + if(!curr->next) + return; + curr = curr->next; + + } + curr->next = NULL; + strcpy(curr->name,name); + curr->records = NULL; + currentkey = curr; +} + +bool CRegistry::LookupKey(char *name) +{ + tKey *curr; + curr = root; + while(curr){ + if( !strcasecmp(name,curr->name) ){ + //found a match + currentkey = curr; + return true; + } + curr = curr->next; + } + return false; +} + +tRecord *CRegistry::LookupRecord(char *record,void *data) +{ + if(!currentkey) + return NULL; + + tRecord *curr; + curr = currentkey->records; + while(curr){ + if( !strcasecmp(record,curr->name) ){ + //found the record + switch(curr->type) + { + case REGT_STRING: + char *st; + st = (char *)curr->data; + strcpy((char *)data,st); + break; + case REGT_DWORD: + int *dw; + dw = (int *)curr->data; + *((int *)data) = *dw; + break; + }; + return curr; + } + curr = curr->next; + } + return NULL; +} + +int CRegistry::GetDataSize(char *record) +{ + if(!currentkey) + return false; + tRecord *curr; + curr = currentkey->records; + while(curr){ + if( !strcasecmp(record,curr->name) ){ + //found the record + switch(curr->type) + { + case REGT_STRING: + return strlen((char *)curr->data)+1; + break; + case REGT_DWORD: + return sizeof(int); + break; + }; + return 0; + } + curr = curr->next; + } + return 0; +} + +bool CRegistry::CreateRecord(char *name,char type,void *data) +{ + if(!currentkey) + return false; + tRecord *curr; + //first see if the record exists under this key + int datasize = GetDataSize(name); + if(datasize){ + char *olddata = (char *)malloc(datasize); + if(olddata){ + curr = LookupRecord(name,olddata); + if(curr){ + //ok we have an old value, replace it! + //mprintf((0,"Replacing %s\n",name)); + if(curr->data) + free(curr->data); + free(olddata); + curr->type = type; + switch(type){ + case REGT_STRING: + curr->data = malloc(strlen((char *)data)+1); + strcpy((char *)curr->data,(char *)data); + break; + case REGT_DWORD: + curr->data = malloc(sizeof(int)); + *((int *)curr->data) = *((int *)data); + break; + } + return true; + } + } + } + + //it is a new record + if(currentkey->records){ + curr = currentkey->records; + while(curr->next) + curr = curr->next; + curr->next = (tRecord *)malloc(sizeof(tRecord)); + if(!curr->next) + return false; + curr = curr->next; + }else{ + currentkey->records = (tRecord *)malloc(sizeof(tRecord)); + if(!currentkey->records) + return false; + curr = currentkey->records; + } + curr->next = NULL; + strcpy(curr->name,name); + switch(type) + { + case REGT_STRING: + curr->data = malloc(strlen((char *)data)+1); + curr->type = REGT_STRING; + strcpy((char *)curr->data,(char *)data); + break; + case REGT_DWORD: + curr->data = malloc(sizeof(int)); + curr->type = REGT_DWORD; + *((int *)curr->data) = *((int *)data); + break; + }; + return true; +} + + + + diff --git a/linux/registry.h b/linux/registry.h new file mode 100644 index 000000000..8d20f54ac --- /dev/null +++ b/linux/registry.h @@ -0,0 +1,79 @@ +/* +* $Logfile: /DescentIII/Main/linux/registry.h $ +* $Revision: 1.2 $ +* $Date: 2004/02/09 04:14:51 $ +* $Author: kevinb $ +* +* Linux registry header +* +* $Log: registry.h,v $ +* Revision 1.2 2004/02/09 04:14:51 kevinb +* Added newlines to all headers to reduce number of warnings printed +* +* Made some small changes to get everything compiling. +* +* All Ready to merge the 1.5 tree. +* +* Revision 1.1.1.1 2000/04/18 00:00:39 icculus +* initial checkin +* + * + * 4 7/14/99 9:09p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#ifndef __REGISTRY_H_ +#define __REGISTRY_H_ + +#define MAX_RECORD_NAME 256 +#define REGT_STRING 0 +#define REGT_DWORD 1 + +#include + +typedef struct tRecord +{ + char name[MAX_RECORD_NAME]; + char type; + void *data; + tRecord *next; +}tRecord; + +typedef struct tKey +{ + char name[MAX_RECORD_NAME]; + tKey *next; + tRecord *records; +}tKey; + + +class CRegistry +{ +public: + CRegistry(char *name); + ~CRegistry(); + void Export(); + bool Import(); + void CreateKey(char *name); + bool LookupKey(char *name); + bool CreateRecord(char *name,char type,void *data); + tRecord *LookupRecord(char *record,void *data); + int GetDataSize(char *record); + void GetSystemName(char *name); + void SetSystemName(char *name); +private: + void Destroy(void); + void DestroyKey(tKey *key); + void DestroyRecord(tRecord *record); + void ExportKey(tKey *key,FILE *file); + void ExportRecord(tRecord *record,FILE *file); + char name[MAX_RECORD_NAME]; + tKey *root; + tKey *currentkey; +}; + +#endif + + diff --git a/lnxcontroller/CMakeLists.txt b/lnxcontroller/CMakeLists.txt new file mode 100644 index 000000000..370cfaf73 --- /dev/null +++ b/lnxcontroller/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (HEADERS ) +SET (CPPS + lnxcontroller.cpp +) + +ADD_LIBRARY(lnxcontroller STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/lnxcontroller/lnxcontroller.cpp b/lnxcontroller/lnxcontroller.cpp new file mode 100644 index 000000000..c210c4483 --- /dev/null +++ b/lnxcontroller/lnxcontroller.cpp @@ -0,0 +1,1572 @@ +/* +* $Logfile: /DescentIII/Main/lnxcontroller/lnxcontroller.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:58:14 $ +* $Author: kevinb $ +* +* Linux controller routines +* +* $Log: lnxcontroller.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:58:14 kevinb +* initial 1.5 import +* + * + * 7 9/23/99 12:10p Jeff + * changes to reflect new wincontroller + * + * 6 8/18/99 8:52p Jeff + * make up to date with windows + * + * 5 7/14/99 9:10p Jeff + * added comment header +* +* $NoKeywords: $ +*/ + +#include "Controller.h" +#include +#include +#include "ddio.h" +#include "pserror.h" +#include "joystick.h" +#include "InfFile.h" + +//Sorry! This is needed for the semi-hacky mouselook support +#include "descent.h" +#include "player.h" +#include "object.h" +#include "pilot.h" +#include "multi.h" +#include "game.h" +//End of hacky includes + + +#define JOY_DEADZONE 0.20f +#define MOUSE_DEADZONE 0.00f + +static float WinControllerTimer = 0.0f; +static longlong g_last_frame_timer_ms = -1; +static float g_accum_frame_time = 0.0f; + +lnxgameController::lnxgameController(int num_funcs, ct_function *funcs) : gameController(num_funcs,funcs) +{ + enum_controllers(); + + for (int i = 0; i < num_funcs; i++) + assign_function(&funcs[i]); + + m_Suspended = 0; + m_frame_timer_ms = -1; + m_frame_time = 1.0f; + g_last_frame_timer_ms = -1; + g_accum_frame_time = 0.0f; + + lnxgameController::flush(); +} + +lnxgameController::~lnxgameController() {} + +// these functions suspend or resume any controller reading. this is really only useful for +// preemptive controller polling, but they should be used to activate and deactivate controller +// reading. +void lnxgameController::suspend() { m_Suspended = 1; } +void lnxgameController::resume() { m_Suspended = 0; m_frame_timer_ms = -1; m_frame_time = 1.0f;} + +#define CONTROLLER_POLLING_TIME 50 +#define MOUSE_POLLING_TIME (1.0f/20.0f) + +// this functions polls the controllers if needed. some systems may not need to implement +// this function. +void lnxgameController::poll() +{ + longlong cur_frame_timer_ms; + + if (m_Suspended) + return; + + cur_frame_timer_ms = timer_GetMSTime(); + if (m_frame_timer_ms == -1) { + // don't poll this frame. + m_frame_timer_ms = cur_frame_timer_ms; + g_last_frame_timer_ms = cur_frame_timer_ms; + g_accum_frame_time = 0.0f; + return; + } + + m_frame_time = (float)((cur_frame_timer_ms - m_frame_timer_ms)/1000.0); + m_frame_timer_ms = cur_frame_timer_ms; + g_accum_frame_time += m_frame_time; + + if(g_accum_frame_time>=MOUSE_POLLING_TIME){ + g_accum_frame_time = 0.0f; + } + + for (int ctl = 0; ctl < m_NumControls; ctl++) + { + if (m_ControlList[ctl].id >= CTID_EXTCONTROL0) { + extctl_getpos(m_ControlList[ctl].id); + } + else if (m_ControlList[ctl].id == CTID_MOUSE) { + // if ((cur_frame_timer_ms - g_last_frame_timer_ms) > CONTROLLER_POLLING_TIME) { + mouse_geteval(); + // g_last_frame_timer_ms = cur_frame_timer_ms; + // } + } + } +} + +// toggles use of deadzone for controllers. ctl can be 0 to ??? +// dead zone is from 0.0 to 0.5 +void lnxgameController::set_controller_deadzone(int ctl,float deadzone) +{ + if(ctl<0 || ctl>=(m_NumControls-2)){ + return; + } + + if(deadzone<0.0f) deadzone = 0.0f; + if(deadzone>0.9f) deadzone = 0.9f; + + m_ControlList[ctl+2].deadzone = deadzone; +} + +char Ctltext_AxisBindings[][16] = { + "", "X-axis", "Y-axis", "Z-axis", "R-axis", "U-axis", "V-axis" +}; + + +char Ctltext_BtnBindings[][16] = { + "", "btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8", "btn9", "btn10", "btn11", + "btn12", "btn13", "btn14", "btn15", "btn16", "btn17", "btn18", "btn19", "btn20", "btn21", "btn22", + "btn23", "btn24", "btn25", "btn26", "btn27", "btn28", "btn29", "btn30", "btn31", "btn32" +}; + +char Ctltext_PovBindings[][16] = { + "","pov-U", "pov-R", "pov-D", "pov-L" +}; + +#define NUM_AXISBINDSTRINGS (sizeof(Ctltext_AxisBindings)/sizeof(Ctltext_AxisBindings[0])) +#define NUM_BTNBINDSTRINGS (sizeof(Ctltext_BtnBindings)/sizeof(Ctltext_AxisBindings[0])) + + +// retrieves binding text for desired function, binding, etc. +const char *lnxgameController::get_binding_text(ct_type type, ubyte ctrl, ubyte bind) +{ + static char binding_text[16]; + const char *str; + + if (ctrl == NULL_CONTROLLER) { + return NULL; + } + + switch (type) + { + int pov_n; + case ctAxis: + { + ASSERT (bind < NUM_AXISBINDSTRINGS); + str = Ctltext_AxisBindings[bind]; + if ((ctrl-2) > 0) { + sprintf(binding_text,"J%d:%s", (ctrl-2)+1, str); + } + else { + return str; + } + break; + } + + case ctMouseAxis: + { + str = ddio_MouseGetAxisText(((sbyte)bind)-1); + return str; + } + + case ctButton: + { + ASSERT(bind < NUM_BTNBINDSTRINGS); + str = Ctltext_BtnBindings[bind]; + if ((ctrl-2) > 0) { + sprintf(binding_text,"J%d:%s", (ctrl-2)+1, str); + } + else { + return str; + } + break; + } + + case ctMouseButton: + { + str = ddio_MouseGetBtnText(((sbyte)bind)-1); + return str; + } + + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + { + ushort povpos = bind; + + if(type==ctPOV) + pov_n = 0; + else + pov_n = (type - ctPOV2)+1; + + if (povpos == JOYPOV_UP) + str = Ctltext_PovBindings[1]; + else if (povpos == JOYPOV_DOWN) + str = Ctltext_PovBindings[3]; + else if (povpos == JOYPOV_LEFT) + str = Ctltext_PovBindings[4]; + else if (povpos == JOYPOV_RIGHT) + str = Ctltext_PovBindings[2]; + else + str = Ctltext_PovBindings[0]; + if ((ctrl-2) > 0) { + if(pov_n){ + sprintf(binding_text,"J%d:%s%d", (ctrl-2)+1, str,pov_n); + }else{ + sprintf(binding_text,"J%d:%s", (ctrl-2)+1, str); + } + } + else { + if(pov_n){ + sprintf(binding_text,"%s%d",str,pov_n); + }else{ + return str; + } + } + break; + } + + case ctKey: + break; + + default: + if (type == ctNone) { + Int3(); + } + binding_text[0] = 0; + } + + return binding_text; +} + + + +// flushes all controller information +void lnxgameController::flush() +{ + bool old_mse = m_MouseActive, old_joy = m_JoyActive; + + ddio_KeyFlush(); + ddio_MouseQueueFlush(); + +// does real flush + mask_controllers(false, false); + mask_controllers(old_joy, old_mse); +} + +// returns the value of a requested controller type. make sure you flush the controller before polling. +ct_config_data lnxgameController::get_controller_value(ct_type type_req) +{ +// will return the current value of a requested control type. + ct_config_data val = MAKE_CONFIG_DATA(INVALID_CONTROLLER_INFO, NULL_BINDING); + int i, j; + + switch(type_req) + { + int pov_n; + + case ctKey: + val = makeword(0,ddio_KeyInKey()); + break; + + case ctButton: + for (i = 2; i < m_NumControls; i++) + { + for (j = 0; j < m_ControlList[i].buttons; j++) + { + if (m_ExtCtlStates[m_ControlList[i].id].btnpresses[j] && !(m_ExtCtlStates[m_ControlList[i].id].buttons & (1< 1.5f)?0.95f : (m_ControlList[i].sens[CT_V_AXIS-1]>1.0f)?0.80f:(m_ControlList[i].sens[CT_V_AXIS-1]/2); + pos = get_axis_value(i, CT_V_AXIS, ctAnalog); + if(fabs(pos)>limit) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_V_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_U_AXIS) { + limit = (m_ControlList[i].sens[CT_U_AXIS-1] > 1.5f)?0.95f : (m_ControlList[i].sens[CT_U_AXIS-1]>1.0f)?0.80f:(m_ControlList[i].sens[CT_U_AXIS-1]/2); + pos = get_axis_value(i, CT_U_AXIS, ctAnalog); + if (fabs(pos) > limit) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_U_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_R_AXIS) { + limit = (m_ControlList[i].sens[CT_R_AXIS-1] > 1.5f)?0.95f : (m_ControlList[i].sens[CT_R_AXIS-1]>1.0f)?0.80f:(m_ControlList[i].sens[CT_R_AXIS-1]/2); + pos = get_axis_value(i, CT_R_AXIS, ctAnalog); + if (fabs(pos) > limit) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_R_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_Z_AXIS) { + limit = (m_ControlList[i].sens[CT_Z_AXIS-1] > 1.5f)?0.95f : (m_ControlList[i].sens[CT_Z_AXIS-1]>1.0f)?0.80f:(m_ControlList[i].sens[CT_Z_AXIS-1]/2); + pos = get_axis_value(i, CT_Z_AXIS, ctAnalog); + if (fabs(pos) > limit) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_Z_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_Y_AXIS) { + limit = (m_ControlList[i].sens[CT_Y_AXIS-1] > 1.5f)?0.95f : (m_ControlList[i].sens[CT_Y_AXIS-1]>1.0f)?0.80f:(m_ControlList[i].sens[CT_Y_AXIS-1]/2); + pos = get_axis_value(i, CT_Y_AXIS, ctAnalog); + if (fabs(pos) > limit) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_Y_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_X_AXIS) { + limit = (m_ControlList[i].sens[CT_X_AXIS-1] > 1.5f)?0.95f : (m_ControlList[i].sens[CT_X_AXIS-1]>1.0f)?0.80f:(m_ControlList[i].sens[CT_X_AXIS-1]/2); + pos = get_axis_value(i, CT_X_AXIS, ctAnalog); + if (fabs(pos) > limit) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_X_AXIS,NULL_BINDING)); + } + } + break; + + case ctMouseAxis: + { + float pos=0.0f; + unsigned ctl = CONTROLLER_CTL_INFO(1,NULL_CONTROLLER), i = 1; + + ASSERT(m_ControlList[i].id == CTID_MOUSE); + + if (m_ControlList[i].flags & CTF_V_AXIS) { + pos = get_axis_value(i, CT_V_AXIS, ctAnalog); + if (fabs(pos) >= 0.50f) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_V_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_U_AXIS) { + pos = get_axis_value(i, CT_U_AXIS, ctAnalog); + if (fabs(pos) >= 0.50f) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_U_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_R_AXIS) { + pos = get_axis_value(i, CT_R_AXIS, ctAnalog); + if (fabs(pos) >= 0.90f) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_R_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_Z_AXIS) { + pos = get_axis_value(i, CT_Z_AXIS, ctAnalog); + if (fabs(pos) >= 0.50f) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_Z_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_Y_AXIS) { + pos = get_axis_value(i, CT_Y_AXIS, ctAnalog); + // mprintf((0, "y=%.2f ", pos)); + if (fabs(pos) >= 0.90f) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_Y_AXIS,NULL_BINDING)); + } + if (m_ControlList[i].flags & CTF_X_AXIS) { + pos = get_axis_value(i, CT_X_AXIS, ctAnalog); + // mprintf((0, "x=%.2f\n", pos)); + if (fabs(pos) >= 0.90f) + val = MAKE_CONFIG_DATA(ctl, CONTROLLER_CTL_VALUE(CT_X_AXIS,NULL_BINDING)); + } + } + break; + + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + if(type_req==ctPOV){ + pov_n = 0; + }else{ + pov_n = (type_req - ctPOV2) + 1; + } + for (i = 2; i < m_NumControls; i++) + { + float pos; + int ctl = CONTROLLER_CTL_INFO(i,-1); + + if (m_ControlList[i].flags & (CTF_POV<= CT_MAX_ELEMENTS) return; + +// auto assign keyboard controller if type is key. + if (type[0] == ctKey) + elem.ctl[0] = CONTROLLER_CTL1_INFO(0); + else + elem.ctl[0] = CONTROLLER_CTL1_INFO(CONTROLLER_INFO(value)); + + if (type[1] == ctKey) + elem.ctl[1] = CONTROLLER_CTL2_INFO(0); + else + elem.ctl[1] = CONTROLLER_CTL2_INFO(CONTROLLER_INFO(value)); + + elem.ctype[0] = type[0]; + elem.ctype[1] = type[1]; + elem.format = m_ElementList[id].format; + elem.value[0] = CONTROLLER_CTL1_VALUE(CONTROLLER_VALUE(value)); + elem.value[1] = CONTROLLER_CTL2_VALUE(CONTROLLER_VALUE(value)); + elem.flags[0] = flags[0]; + elem.flags[1] = flags[1]; + elem.enabled = m_ElementList[id].enabled; + +// if controller doesn't exist, set it to invalid. + if (elem.ctl[0] > CT_MAX_CONTROLLERS) + elem.ctl[0] = NULL_LNXCONTROLLER; + if (elem.ctl[1] >= CT_MAX_CONTROLLERS) + elem.ctl[1] = NULL_LNXCONTROLLER; + + assign_element(id, &elem); +} + +// returns information about a requested function (type must be of an array == CTLBINDS_PER_FUNC) +void lnxgameController::get_controller_function(int id, ct_type *type, ct_config_data *value, ubyte *flags) +{ + type[0] = m_ElementList[id].ctype[0]; + type[1] = m_ElementList[id].ctype[1]; + *value = makeword(CONTROLLER_CTL_INFO(m_ElementList[id].ctl[0],m_ElementList[id].ctl[1]), + CONTROLLER_CTL_VALUE(m_ElementList[id].value[0], m_ElementList[id].value[1])); + flags[0] = m_ElementList[id].flags[0]; + flags[1] = m_ElementList[id].flags[1]; +} + +// temporarily enables or disables a function +void lnxgameController::enable_function(int id, bool enable) +{ + m_ElementList[id].enabled = enable; +} + +// all systems need to implement this function. this returns information about the controller +bool lnxgameController::get_packet(int id, ct_packet *packet, ct_format alt_format) +{ + float val= (float)0.0; + int i; + + ASSERT(id < CT_MAX_ELEMENTS); + + packet->format = (alt_format != ctNoFormat) ? alt_format : m_ElementList[id].format; + alt_format = packet->format; + + WinControllerTimer = timer_GetTime(); + packet->flags = 0; + + if (!m_ElementList[id].enabled) { + goto skip_packet_read; + } + +// check if the element's controller is valid. + + for (i = 0; i < CTLBINDS_PER_FUNC; i++) + { + ubyte value = m_ElementList[id].value[i]; + sbyte controller = m_ElementList[id].ctl[i]; + + if (controller == -1 || m_ControlList[controller].id == CTID_INVALID) { + continue; + } + switch (m_ElementList[id].ctype[i]) + { + case ctKey: + if (value) { + val = get_key_value(value, alt_format); + if (KEY_STATE(value)) packet->flags |= CTPK_ELEMENTACTIVE; + } + break; + + case ctMouseAxis: + packet->flags |= CTPK_MOUSE; + case ctAxis: + val = get_axis_value(controller, value, alt_format,(m_ElementList[id].flags[i] & CTFNF_INVERT)?true:false); + if (m_ElementList[id].flags[i] & CTFNF_INVERT) { + if (alt_format == ctDigital) { + val = (val==0.0f) ? 1.0f : 0.0f; + } + else if (alt_format == ctAnalog) { + val = -val; + } + } + break; + + case ctMouseButton: + packet->flags |= CTPK_MOUSE; + case ctButton: + val = get_button_value(controller, alt_format, value); + break; + + case ctPOV: + val = get_pov_value(controller, alt_format, 0, value); + break; + case ctPOV2: + case ctPOV3: + case ctPOV4: + val = get_pov_value(controller, alt_format, (m_ElementList[id].ctype[i] - ctPOV2)+1,value); + break; + + default: + Int3(); + val = 0.0f; + } + + if (val) + break; + } + +skip_packet_read: + if (val) packet->flags |= CTPK_ELEMENTACTIVE; + + packet->value = val; + + return true; +} + +// gets sensitivity of axis item +float lnxgameController::get_axis_sensitivity(ct_type axis_type, ubyte axis) +{ + axis--; + ASSERT(axis < CT_NUM_AXES); + + switch (axis_type) + { + case ctMouseAxis: + return m_ControlList[1].sens[axis]; + + case ctAxis: + return m_ControlList[2].sens[axis]; + + default: + Int3(); + } + + return 0.0f; +} + +// sets sensitivity of axis item +void lnxgameController::set_axis_sensitivity(ct_type axis_type, ubyte axis, float val) +{ + int i; + + axis--; + ASSERT(axis < CT_NUM_AXES); + + switch (axis_type) + { + case ctMouseAxis: + m_ControlList[1].sens[axis] = val; + break; + case ctAxis: + for (i = 2; i < CT_MAX_CONTROLLERS;i++) + m_ControlList[i].sens[axis] = val; + break; + default: + Int3(); + } +} + +// assigns an individual function +int lnxgameController::assign_function(ct_function *func) +{ +// for now this is a straight forward translation (that is, no mapping of needs to controller +// list to create elements. + ct_element elem; + int i; + + for (i = 0; i < CTLBINDS_PER_FUNC; i++) + { + elem.ctl[i] = NULL_LNXCONTROLLER; + + switch (func->ctype[i]) + { + int pov_n; + case ctNone: + break; + case ctKey: + elem.ctl[i] = 0; + break; + case ctAxis: + elem.ctl[i] = get_axis_controller(func->value[i]); + break; + + case ctButton: + elem.ctl[i] = get_button_controller(func->value[i]); + break; + + case ctMouseAxis: + elem.ctl[i] = 1; + break; + + case ctMouseButton: + // find a free mouse button. + if ((m_ControlList[1].btnmask & (1 << (func->value[i]-1))) && ((func->value[i]-1) < m_ControlList[1].buttons)) { + elem.ctl[i] = 1; + } + break; + + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: + if(func->ctype[i]==ctPOV) + pov_n = 0; + else + pov_n = (func->ctype[i]-ctPOV2)+1; + + elem.ctl[i] = get_pov_controller(pov_n); + break; + } + + elem.ctype[i] = func->ctype[i]; + elem.value[i] = func->value[i]; + } + + elem.format = func->format; + elem.flags[0] = func->flags[0]; + elem.flags[1] = func->flags[1]; + elem.enabled = true; + + assign_element(func->id, &elem); + + return func->id; +} + + +// get raw values for the controllers +int lnxgameController::get_mouse_raw_values(int *x, int *y) +{ + if (m_Suspended) + return 0; + + *x = m_MseState.mx; + *y = m_MseState.my; + + return m_MseState.btnmask; +} + +unsigned lnxgameController::get_joy_raw_values(int *x, int *y) +{ + unsigned btn=0; + + if (m_Suspended) + return 0; + + + for (int ctl = 0; ctl < m_NumControls; ctl++) + { + int dev = m_ControlList[ctl].id; + + if (dev >= CTID_EXTCONTROL0) { + *x = m_ExtCtlStates[dev].x; + *y = m_ExtCtlStates[dev].y; + btn = m_ExtCtlStates[dev].buttons; + if (*x || *y || btn) + return btn; + } + } + + return 0; +} + + +gameController *CreateController(int num_funcs, ct_function *funcs, char *remote_ip) +{ + return new lnxgameController(num_funcs,funcs); +} + +void DestroyController(gameController *ctl) +{ + if(ctl) + delete ctl; +} + + +// activates or deactivates mouse and or controller +void lnxgameController::mask_controllers(bool joystick, bool mouse) +{ + int i,j; + + m_JoyActive = joystick; + m_MouseActive = mouse; + + if (!m_MouseActive) { + m_MseState.x = 0; + m_MseState.y = 0; + m_MseState.z = 0; + m_MseState.mx = 0; + m_MseState.my = 0; + m_MseState.btnmask = 0; + } + + if (!m_JoyActive) { + for (int ctl = 0; ctl < m_NumControls; ctl++) + { + if (m_ControlList[ctl].id >= CTID_EXTCONTROL0) { + // handle buttons + int dev = m_ControlList[ctl].id; + m_ExtCtlStates[dev].x = (m_ControlList[ctl].normalizer[0]); + m_ExtCtlStates[dev].y = (m_ControlList[ctl].normalizer[1]); + m_ExtCtlStates[dev].z = (m_ControlList[ctl].normalizer[2]); + m_ExtCtlStates[dev].r = (m_ControlList[ctl].normalizer[3]); + m_ExtCtlStates[dev].u = (m_ControlList[ctl].normalizer[4]); + m_ExtCtlStates[dev].v = (m_ControlList[ctl].normalizer[5]); + for(j=0;j=3 ? CTF_Z_AXIS : 0); + m_ControlList[num_devs].btnmask = btnmask; + m_ControlList[num_devs].normalizer[0] = 320.0f; + m_ControlList[num_devs].normalizer[1] = 240.0f; + m_ControlList[num_devs].normalizer[2] = 100.0f; + m_ControlList[num_devs].sens[0] = 1.0f; + m_ControlList[num_devs].sens[1] = 1.0f; + m_ControlList[num_devs].sens[2] = 1.0f; + m_ControlList[num_devs].sensmod[0] = 1.0f; + m_ControlList[num_devs].sensmod[1] = 1.0f; + m_ControlList[num_devs].sensmod[2] = 1.0f; + num_devs++; + + +// we should initialize multiple controls +// before doing this, MAKE SURE REMOTE CONTROL SUPPORTS IT. + for (dev = JOYSTICK_1; dev <= JOYSTICK_8; dev++) + { + tJoyInfo jc; + + // check if device is plugged in. + if (joy_IsValid(dev)) { + // query the joystick's capabilites to see if joystick is truly valid + joy_GetJoyInfo((tJoystick)dev, &jc); + + m_ControlList[num_devs].id = dev; + m_ControlList[num_devs].buttons = jc.num_btns; + m_ControlList[num_devs].btnmask = 0; + m_ControlList[num_devs].flags = CTF_X_AXIS | + CTF_Y_AXIS | + ((jc.axes_mask & JOYFLAG_ZVALID) ? CTF_Z_AXIS : 0) | + ((jc.axes_mask & JOYFLAG_RVALID) ? CTF_R_AXIS : 0) | + ((jc.axes_mask & JOYFLAG_UVALID) ? CTF_U_AXIS : 0) | + ((jc.axes_mask & JOYFLAG_VVALID) ? CTF_V_AXIS : 0) | + ((jc.axes_mask & JOYFLAG_POVVALID) ? CTF_POV : 0) | + ((jc.axes_mask & JOYFLAG_POV2VALID) ? CTF_POV2 : 0) | + ((jc.axes_mask & JOYFLAG_POV3VALID) ? CTF_POV3 : 0) | + ((jc.axes_mask & JOYFLAG_POV4VALID) ? CTF_POV4 : 0); + m_ControlList[num_devs].normalizer[0] = (jc.maxx-jc.minx)/2.0f; + m_ControlList[num_devs].normalizer[1] = (jc.maxy-jc.miny)/2.0f; + m_ControlList[num_devs].normalizer[2] = (jc.maxz-jc.minz)/2.0f; + m_ControlList[num_devs].normalizer[3] = (jc.maxr-jc.minr)/2.0f; + m_ControlList[num_devs].normalizer[4] = (jc.maxu-jc.minu)/2.0f; + m_ControlList[num_devs].normalizer[5] = (jc.maxv-jc.minv)/2.0f; + + for(i=0;iformat; + m_ElementList[id].flags[0] = elem->flags[0]; + m_ElementList[id].flags[1] = elem->flags[1]; + m_ElementList[id].enabled = elem->enabled; + +// look through each controller and validate each element + for (i = 0; i < CTLBINDS_PER_FUNC; i++) + { + m_ElementList[id].ctl[i] = elem->ctl[i]; + m_ElementList[id].value[i] = elem->value[i]; + m_ElementList[id].ctype[i] = elem->ctype[i]; + + if (m_ElementList[id].ctl[i] != NULL_LNXCONTROLLER) { + // this function shouldn't do any error checking!!!! keep same controller values and bindings unless + // bindings are truly bogus. + switch (m_ElementList[id].ctype[i]) + { + case ctMouseButton: + case ctButton: + if (elem->value[i] > CT_MAX_BUTTONS) { + m_ElementList[id].ctl[i] = NULL_LNXCONTROLLER; + m_ElementList[id].value[i] = NULL_BINDING; + } + break; + case ctMouseAxis: + case ctAxis: +// if (!(m_ControlList[elem->ctl[i]].flags & (1<<(elem->value[i]-1)))) +// m_ElementList[id].ctl[i] = NULL_WINCONTROLLER; + break; + case ctPOV: + case ctPOV2: + case ctPOV3: + case ctPOV4: +// if (!(m_ControlList[elem->ctl[i]].flags & CTF_POV)) +// m_ElementList[id].ctl[i] = NULL_WINCONTROLLER; + break; + case ctKey: + break; + default: + m_ElementList[id].value[i] = NULL_BINDING; + m_ElementList[id].ctl[i] = NULL_LNXCONTROLLER; + } + } + else { + m_ElementList[id].value[i] = NULL_BINDING; + } + } +} + + +float lnxgameController::get_button_value(sbyte controller, ct_format format, ubyte button) +{ + float val = (float)0.0; + + if (controller <= NULL_LNXCONTROLLER || controller >= CT_MAX_CONTROLLERS) { + return 0.0f; + } + if (m_ControlList[controller].id == CTID_INVALID) { + return 0.0f; + } + +#ifdef _DEBUG + if (m_ControlList[controller].id == CTID_KEYBOARD) { + Int3(); + return 0.0f; + } +#endif + + if (button == NULL_BINDING) { + return val; + } + +// buttons are idenitifed as 0=none, 1 = button 1, etc. so if we have a valid button, then +// decrement counter. + button--; + +// verify valid button. + if ((unsigned)button >= m_ControlList[controller].buttons) + return val; + + switch (format) + { + // note we take care of mouse controls and external controls here + case ctDownCount: + if (m_ControlList[controller].id == CTID_MOUSE) { + val = (float)ddio_MouseBtnDownCount(button); + } + else { + val = (float)m_ExtCtlStates[m_ControlList[controller].id].btnpresses[button]; + m_ExtCtlStates[m_ControlList[controller].id].btnpresses[button] = 0; + } + break; + + case ctTime: + if (m_ControlList[controller].id == CTID_MOUSE) { + val = ddio_MouseBtnDownTime(button); + } + else { + if (!(m_ExtCtlStates[m_ControlList[controller].id].buttons & (1<= CT_MAX_CONTROLLERS) { + return 0.0f; + } + + ctldev = &m_ControlList[controller]; + if (ctldev->id == CTID_INVALID) { + return 0.0f; + } + +#ifdef _DEBUG + if (m_ControlList[controller].id == CTID_KEYBOARD) { + Int3(); + return 0.0f; + } +#endif + +// verify controller axis + if (!CHECK_FLAG(ctldev->flags, 1<<(axis-1))) { + return val; + } + +// get raw value + switch (axis) + { + // note we take care of mouse controls and external controls here + case CT_X_AXIS: axisval = (float)((ctldev->id == CTID_MOUSE) ? m_MseState.x : m_ExtCtlStates[ctldev->id].x); break; + case CT_Y_AXIS: axisval = (float)((ctldev->id == CTID_MOUSE) ? m_MseState.y : m_ExtCtlStates[ctldev->id].y); break; + case CT_Z_AXIS: axisval = (float)((ctldev->id == CTID_MOUSE) ? m_MseState.z : m_ExtCtlStates[ctldev->id].z); break; + case CT_R_AXIS: axisval = (float)m_ExtCtlStates[ctldev->id].r; break; + case CT_U_AXIS: axisval = (float)m_ExtCtlStates[ctldev->id].u; break; + case CT_V_AXIS: axisval = (float)m_ExtCtlStates[ctldev->id].v; break; + default: Int3(); // NOT A VALID AXIS + } + +// create normalizer + axis--; + if (ctldev->id == CTID_MOUSE) { + if (m_frame_time < 0.005f) m_frame_time = 0.005f; // to trap potential errors. + normalizer = ctldev->normalizer[axis]*m_frame_time; + nullzone = MOUSE_DEADZONE; + if (axis == CT_X_AXIS) { + //mprintf_at((4, 4, 0, "m_dX:%03d normal:%03.2f", (int)axisval, normalizer)); + } + + } + else { + normalizer = ctldev->normalizer[axis]; + nullzone = (m_ControlList[controller].deadzone<0.05f)?0.05f:m_ControlList[controller].deadzone; + } + + val = axisval/normalizer; + val = val - ((ctldev->id == CTID_MOUSE) ? 0.0f : 1.0f); // joystick needs to be normalized to -1.0 to 1.0 + +// calculate adjusted value + if (val > nullzone) { + val = (val-nullzone)/(1.0f-nullzone); + } + else if (val < -nullzone) { + val = (val+nullzone)/(1.0f-nullzone); + } + else { + val = 0.0f; + } + val = ctldev->sensmod[axis]*ctldev->sens[axis] * val; + val = val + 1.0f; + + if (val < 0.0f) val = 0.0f; + if (val > 2.0f) val = 2.0f; + +// determine value based off requested format. + if (format == ctDigital) { + if (val < 0.5) val = (float)0.0; + else val = (float)1.0; + } + else if (format == ctAnalog) { + val = val - (float)1.0; + } + else { + val = (float)0.0; + mprintf((1, "gameController::axis unsupported format for function.\n")); + } + + ct_packet key_slide1, key_bank; + + get_packet(ctfTOGGLE_SLIDEKEY, &key_slide1); + get_packet(ctfTOGGLE_BANKKEY, &key_bank); + + if(key_slide1.value || key_bank.value) + { + //Don't do mouse look if either toggle is happening + return val; + } + if((Current_pilot.mouselook_control)&&(GAME_MODE==GetFunctionMode())) + { + //Don't do mouselook controls if they aren't enabled in multiplayer + if( (Game_mode & GM_MULTI) && (!(Netgame.flags & NF_ALLOW_MLOOK)) ) + return val; + + //Account for guided missile control + if(Players[Player_num].guided_obj) + return val; + axis++; + + if((axis == CT_X_AXIS)&&(ctldev->id == CTID_MOUSE)&&(val!=0.0f)) + { + matrix orient; + + if(!(Players[Player_num].controller_bitflags & PCBF_HEADINGLEFT)) + { + if(val<0) + val = 0.0f; + } + if(!(Players[Player_num].controller_bitflags & PCBF_HEADINGRIGHT)) + { + if(val>0) + val = 0.0f; + } + + if(invert) + val = -val; + + vm_AnglesToMatrix(&orient, 0.0, val*(((float)(65535.0f/20))*.5), 0.0); + + Objects[Players[Player_num].objnum].orient = Objects[Players[Player_num].objnum].orient * orient; + + vm_Orthogonalize(&Objects[Players[Player_num].objnum].orient); + ObjSetOrient(&Objects[Players[Player_num].objnum], &Objects[Players[Player_num].objnum].orient); + return 0; + + } + if((axis == CT_Y_AXIS)&&(ctldev->id == CTID_MOUSE)&&(val!=0.0f)) + { + matrix orient; + + if(!(Players[Player_num].controller_bitflags & PCBF_PITCHUP)) + { + if(val<0) + val = 0.0f; + } + if(!(Players[Player_num].controller_bitflags & PCBF_PITCHDOWN)) + { + if(val>0) + val = 0.0f; + } + + if(invert) + val = -val; + + vm_AnglesToMatrix(&orient, val*(((float)(65535.0f/20))*.5),0.0, 0.0); + + Objects[Players[Player_num].objnum].orient = Objects[Players[Player_num].objnum].orient * orient; + + vm_Orthogonalize(&Objects[Players[Player_num].objnum].orient); + ObjSetOrient(&Objects[Players[Player_num].objnum], &Objects[Players[Player_num].objnum].orient); + return 0; + } + + } + + + return val; +} + + +// do some pov stuff +float lnxgameController::get_pov_value(sbyte controller, ct_format format, ubyte pov_number, ubyte pov) +{ + float val = (float)0.0; + + if (controller <= NULL_LNXCONTROLLER || controller >= CT_MAX_CONTROLLERS) { + return val; + } + if (m_ControlList[controller].id == CTID_INVALID) { + return val; + } +#ifdef _DEBUG + if (m_ControlList[controller].id == CTID_KEYBOARD) { + Int3(); + return 0.0f; + } +#endif + if (!(m_ControlList[controller].flags & (CTF_POV< INFFILE_ERROR) + { + // we want to assert that the name command comes before any other to verify + // this is the file we really want to change. + switch (cmd) + { + case CTLCMD_NAME: + if (strcmp(ctlname, operand) != 0) goto cancel_file_parse; + found_name = true; + break; + + case CTLCMD_DEAD: // deadzone + if (!found_name) goto cancel_file_parse; + else { + m_ControlList[devnum].deadzone = atof(operand); + } + break; + + case CTLCMD_AXIS: // allowable axis. + // format of command is "+Z-R" + // this would add a Z axis to the controller. -R would remove the Rudder. + // you can do this for X,Y,Z,R,U,V. + if (!found_name) goto cancel_file_parse; + else { + int slen = strlen(operand); + for (i = 0; i <= slen; i+=2) + { + int axis_flag; + if ((i+1) <= slen) { + char axis_cmd = tolower(operand[i+1]); + if (axis_cmd == 'x') axis_flag = CTF_X_AXIS; + else if (axis_cmd == 'y') axis_flag = CTF_Y_AXIS; + else if (axis_cmd == 'z') axis_flag = CTF_Z_AXIS; + else if (axis_cmd == 'r') axis_flag = CTF_R_AXIS; + else if (axis_cmd == 'u') axis_flag = CTF_U_AXIS; + else if (axis_cmd == 'v') axis_flag = CTF_V_AXIS; + else axis_flag = 0; + if (operand[i] == '+') { + m_ControlList[devnum].flags |= axis_flag; + } + else if (operand[i] == '-') { + m_ControlList[devnum].flags &= (~axis_flag); + } + else { + goto cancel_file_parse; + } + } + else { + break; // this should break out of the axis search but continue with the file + } + } + } + break; + + case CTLCMD_SX: // allow modification of global sensitivity modifiers + case CTLCMD_SY: + case CTLCMD_SZ: + case CTLCMD_SR: + case CTLCMD_SU: + case CTLCMD_SV: + { + int idx = (cmd-CTLCMD_SX); + m_ControlList[devnum].sensmod[idx] = atof(operand); + break; + } + } + } + } +cancel_file_parse: + file.Close(); + } + } + while (ddio_FindNextFile(filename)); + ddio_FindFileClose(); + } +} + + diff --git a/lnxmvelib/CMakeLists.txt b/lnxmvelib/CMakeLists.txt new file mode 100644 index 000000000..e2738a533 --- /dev/null +++ b/lnxmvelib/CMakeLists.txt @@ -0,0 +1,10 @@ +SET (HEADERS dyna_pthread.h dyna_xext.h dyna_xwin.h lnxdsound.h mvegfx.h mvelibi.h mvelibl.h snd8to16.h ) +SET (CPPS + lnxdraw.cpp + lnxdsound.cpp + mveasm.cpp + mvelibl.cpp) + +# asmstub.c + +ADD_LIBRARY(lnxmvelib STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/lnxmvelib/asmstub.c b/lnxmvelib/asmstub.c new file mode 100644 index 000000000..4eba59153 --- /dev/null +++ b/lnxmvelib/asmstub.c @@ -0,0 +1,225 @@ +#include +#include + +/* These are our global variables for passing values - AH */ +/* For _asm_sndDecompM16 */ +unsigned short *sndDecompM16_dst; +unsigned char *sndDecompM16_src; +unsigned int sndDecompM16_len; +unsigned int sndDecompM16_prev; +unsigned int sndDecompM16_return; +/* For _asm_sndDecompM16 */ +unsigned short *sndDecompS16_dst; +unsigned char *sndDecompS16_src; +unsigned int sndDecompS16_len; +unsigned int sndDecompS16_prev; +unsigned int sndDecompS16_return; +/* For _asm_nfHPkDecomp */ +unsigned char *nfHPkDecomp_ops; +unsigned char *nfHPkDecomp_comp; +unsigned int nfHPkDecomp_x; +unsigned int nfHPkDecomp_y; +unsigned int nfHPkDecomp_w; +unsigned int nfHPkDecomp_h; + +#ifdef __cplusplus /* Avoid C++ name mangling - AH */ +extern "C" { +#endif + void _asm_sndDecompM16(void); + void _asm_sndDecompS16(void); + void _asm_nfPkConfig(void); + void _asm_nfHPkDecomp(void); + void _asm_selfModify(void); +#ifdef __cplusplus +} +#endif + +#define MAX_MEM_UNLOCK_POINTS 20 +int global_unlock_memory_pointers[MAX_MEM_UNLOCK_POINTS]; // _asm_selfModify() sets these + +int allow_self_modification(void) +{ + int i; + unsigned int page_start; + + for (i = 0; i < MAX_MEM_UNLOCK_POINTS; i++) + global_unlock_memory_pointers[i] = 0; + + _asm_selfModify(); + + for (i = 0; i < MAX_MEM_UNLOCK_POINTS; i++) + if (global_unlock_memory_pointers[i] != 0) + { + page_start = global_unlock_memory_pointers[i] - (global_unlock_memory_pointers[i] % getpagesize()); + mprotect((void *)page_start, getpagesize() * 2, PROT_READ | PROT_WRITE | PROT_EXEC); + //fprintf(stderr, "Unlocked memory location %x for location %x.\n",page_start, global_unlock_memory_pointers[i]); + } + return(1); +} +unsigned sndDecompM16(unsigned short *dst, unsigned char *src,unsigned len, unsigned prev) +{ + sndDecompM16_dst = dst; + sndDecompM16_src = src; + sndDecompM16_len = len; + sndDecompM16_prev = prev; + __asm__ (" call _asm_sndDecompM16" + : : : "%esi", "%edi", "%ebx", "cc", "memory"); + return(sndDecompM16_return); +} + +unsigned sndDecompS16(unsigned short *dst, unsigned char *src, unsigned len, unsigned prev) +{ + sndDecompS16_dst = dst; + sndDecompS16_src = src; + sndDecompS16_len = len; + sndDecompS16_prev = prev; + __asm__ ("call _asm_sndDecompS16" + : : : "%esi", "%edi", "%ebx", "cc", "memory"); + return(sndDecompS16_return); +} + +void nfPkConfig(void) +{ + __asm__ ("call _asm_nfPkConfig" + : : : "%esi", "%edi", "%ebx", "cc", "memory"); + return; +} + +void nfHPkDecomp(unsigned char *ops,unsigned char *comp, unsigned int x, unsigned int y, unsigned int w,unsigned int h) +{ + nfHPkDecomp_ops = ops; + nfHPkDecomp_comp = comp; + nfHPkDecomp_x = x; + nfHPkDecomp_y = y; + nfHPkDecomp_w = w; + nfHPkDecomp_h = h; + __asm__ ("call _asm_nfHPkDecomp" + : : : "%esi", "%edi", "%ebx", "cc", "memory"); + return; +} + +/***********************************************************/ +/* Non-Implemented functions (from inside mveasm.cpp - AH) */ +/***********************************************************/ + +void nfHiColorDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfHiColorDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfPkPal(void); +void nfPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void nfPkDecompH(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void nfPkDecompD(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field); +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field); +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty); +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty); +void mve_sfPkShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty); +void mve_sfPkHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty); + +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); +void palLoadCompPalette(unsigned char *buf); +void gfxMode(unsigned mode); +void gfxLoadCrtc(unsigned char *crtc,unsigned char chain4,unsigned char res); +void gfxGetCrtc(unsigned char *crtc); +void gfxVres(unsigned char misc,unsigned char *crtc); +void MVE_gfxWaitRetrace(int state); +void MVE_gfxSetSplit(unsigned line); + +// rcg07272000 +// need this on non-Intel platforms. Intel uses int $3. +#if (defined __i386__) +#define int3 __asm__ __volatile__ ( "int $3" ); +#else +#define int3 raise(SIGTRAP); +#endif + +void nfHiColorDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3 +} +void nfHiColorDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3 +} +void nfDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3 +} +void nfDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3 +} +void nfPkPal(void) +{ + int3 +} +void nfPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3 +} +void nfPkDecompH(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3 +} +void nfPkDecompD(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3 +} +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field) +{ + int3 +} +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field) +{ + int3 +} +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty) +{ + int3 +} +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty) +{ + int3 +} +void mve_sfPkShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty) +{ + int3 +} +void mve_sfPkHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty) +{ + int3 +} +/* Avoid name mangling issues by moving this into mvelibl.cpp - AH +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count) +{ + int3 +} */ +void palLoadCompPalette(unsigned char *buf) +{ + int3 +} +void gfxMode(unsigned mode) +{ + int3 +} +void gfxLoadCrtc(unsigned char *crtc,unsigned char chain4,unsigned char res) +{ + int3 +} +void gfxGetCrtc(unsigned char *crtc) +{ + int3 +} +void gfxVres(unsigned char misc,unsigned char *crtc) +{ + int3 +} +void MVE_gfxWaitRetrace(int state) +{ + int3 +} +void MVE_gfxSetSplit(unsigned line) +{ + int3 +} diff --git a/lnxmvelib/dyna_pthread.h b/lnxmvelib/dyna_pthread.h new file mode 100644 index 000000000..5c3a43b6a --- /dev/null +++ b/lnxmvelib/dyna_pthread.h @@ -0,0 +1,89 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include + +#define pthread_create sopthread_create +#define pthread_exit sopthread_exit +#define pthread_detach sopthread_detach +#define pthread_self sopthread_self + +typedef int (*pthread_create_fp)(pthread_t *__thread,__const pthread_attr_t *__attr,void *(*__start_routine) (void *),void *__arg); +FEXTERN pthread_create_fp sopthread_create; + +typedef void (*pthread_exit_fp)(void *__retval); +FEXTERN pthread_exit_fp sopthread_exit; + +typedef int (*pthread_detach_fp)(pthread_t __th); +FEXTERN pthread_detach_fp sopthread_detach; + +typedef pthread_t (*pthread_self_fp)(void); +FEXTERN pthread_self_fp sopthread_self; + +#ifndef DECLARE_POINTERS +bool LoadPThreadLib(bool load = true); +#else +#include +#include +#include +void LoadPThreadLibSetNULL(void) +{ + sopthread_create = NULL; + sopthread_exit = NULL; + sopthread_detach = NULL; + sopthread_self = NULL; +} + +bool LoadPThreadLib(bool load) +{ +#define PTHREADLIB "libpthread.so" + static void *handle = NULL; + + if(!load) + { + LoadPThreadLibSetNULL(); + if(handle) + { + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",PTHREADLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(PTHREADLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",PTHREADLIB); + return false; + } + + sopthread_create = (pthread_create_fp)dlsym(handle,"pthread_create"); + if(!sopthread_create) goto load_error; + + sopthread_exit = (pthread_exit_fp)dlsym(handle,"pthread_exit"); + if(!sopthread_exit) goto load_error; + + sopthread_detach = (pthread_detach_fp)dlsym(handle,"pthread_detach"); + if(!sopthread_detach) goto load_error; + + sopthread_self = (pthread_self_fp)dlsym(handle,"pthread_self"); + if(!sopthread_self) goto load_error; + + return true; + +load_error: + LoadPThreadLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",PTHREADLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lnxmvelib/dyna_xext.h b/lnxmvelib/dyna_xext.h new file mode 100644 index 000000000..47cf605d9 --- /dev/null +++ b/lnxmvelib/dyna_xext.h @@ -0,0 +1,99 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include +#include +#include + +#define XShmAttach soXShmAttach +#define XShmCreateImage soXShmCreateImage +#define XShmDetach soXShmDetach +#define XShmPutImage soXShmPutImage +#define XShmQueryExtension soXShmQueryExtension + +typedef Status (*XShmAttach_fp)(Display*,XShmSegmentInfo*); +FEXTERN XShmAttach_fp soXShmAttach; + +typedef XImage *(*XShmCreateImage_fp)(Display*,Visual*,unsigned int,int,char*,XShmSegmentInfo*,unsigned int,unsigned int); +FEXTERN XShmCreateImage_fp soXShmCreateImage; + +typedef Status (*XShmDetach_fp)(Display*,XShmSegmentInfo*); +FEXTERN XShmDetach_fp soXShmDetach; + +typedef Status (*XShmPutImage_fp)(Display*,Drawable,GC,XImage*,int,int,int,int,unsigned int,unsigned int,Bool); +FEXTERN XShmPutImage_fp soXShmPutImage; + +typedef Bool (*XShmQueryExtension_fp)(Display*); +FEXTERN XShmQueryExtension_fp soXShmQueryExtension; + +#ifndef DECLARE_POINTERS +bool LoadXWindowsExtLib(bool load = true); +#else +#include +#include +#include +void LoadXWindowsExtLibSetNULL(void) +{ + soXShmAttach = NULL; + soXShmCreateImage = NULL; + soXShmDetach = NULL; + soXShmPutImage = NULL; + soXShmQueryExtension = NULL; +} + +bool LoadXWindowsExtLib(bool load) +{ +#define XWINDOWSEXTLIB "libXext.so" + static void *handle = NULL; + + if(!load) + { + LoadXWindowsExtLibSetNULL(); + if(handle) + { + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",XWINDOWSEXTLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(XWINDOWSEXTLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",XWINDOWSEXTLIB); + return false; + } + + soXShmAttach = (XShmAttach_fp)dlsym(handle,"XShmAttach"); + if(!soXShmAttach) goto load_error; + + soXShmCreateImage = (XShmCreateImage_fp)dlsym(handle,"XShmCreateImage"); + if(!soXShmCreateImage) goto load_error; + + soXShmDetach = (XShmDetach_fp)dlsym(handle,"XShmDetach"); + if(!soXShmDetach) goto load_error; + + soXShmPutImage = (XShmPutImage_fp)dlsym(handle,"XShmPutImage"); + if(!soXShmPutImage) goto load_error; + + soXShmQueryExtension = (XShmQueryExtension_fp)dlsym(handle,"XShmQueryExtension"); + if(!soXShmQueryExtension) goto load_error; + + return true; + +load_error: + LoadXWindowsExtLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",XWINDOWSEXTLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lnxmvelib/dyna_xwin.h b/lnxmvelib/dyna_xwin.h new file mode 100644 index 000000000..513a422ae --- /dev/null +++ b/lnxmvelib/dyna_xwin.h @@ -0,0 +1,431 @@ +#ifdef DECLARE_POINTERS +#define FEXTERN +#else +#define FEXTERN extern +#endif +#include +#include + +#define XAllocClassHint soXAllocClassHint +#define XAllocSizeHints soXAllocSizeHints +#define XAllocWMHints soXAllocWMHints +#define XCloseDisplay soXCloseDisplay +#define XCreateGC soXCreateGC +#define XCreateImage soXCreateImage +#define XCreatePixmap soXCreatePixmap +#define XCreatePixmapCursor soXCreatePixmapCursor +#define XCreateWindow soXCreateWindow +#define XDefineCursor soXDefineCursor +#define XFillRectangle soXFillRectangle +#define XFreeGC soXFreeGC +#define XFreePixmap soXFreePixmap +#define XGrabPointer soXGrabPointer +#define XKeycodeToKeysym soXKeycodeToKeysym +#define XCheckMaskEvent soXCheckMaskEvent +#define XLookupString soXLookupString +#define XMapWindow soXMapWindow +#define XMatchVisualInfo soXMatchVisualInfo +#define XNextEvent soXNextEvent +#define XOpenDisplay soXOpenDisplay +#define XPutImage soXPutImage +#define XSetIconName soXSetIconName +#define XSetWMNormalHints soXSetWMNormalHints +#define XStoreName soXStoreName +#define XSync soXSync +#define XWarpPointer soXWarpPointer +#define XAutoRepeatOn soXAutoRepeatOn +#define XAutoRepeatOff soXAutoRepeatOff +#define XGetWindowProperty soXGetWindowProperty +#define XQueryTree soXQueryTree +#define XInternAtom soXInternAtom +#define XLowerWindow soXLowerWindow +#define XRaiseWindow soXRaiseWindow +#define XChangeProperty soXChangeProperty +#define XChangeWindowAttributes soXChangeWindowAttributes +#define XMoveResizeWindow soXMoveResizeWindow +#define XUnmapWindow soXUnmapWindow +#define XDestroyWindow soXDestroyWindow +#define XFree soXFree +#define XGetWMNormalHints soXGetWMNormalHints +#define XSetWMProtocols soXSetWMProtocols +#define XSetWMHints soXSetWMHints +#define XSetStandardProperties soXSetStandardProperties +#define XFlush soXFlush +#define XMaskEvent soXMaskEvent + +typedef XClassHint *(*XAllocClassHint_fp)(void); +FEXTERN XAllocClassHint_fp soXAllocClassHint; + +typedef XSizeHints *(*XAllocSizeHints_fp)(void); +FEXTERN XAllocSizeHints_fp soXAllocSizeHints; + +typedef XWMHints *(*XAllocWMHints_fp)(void); +FEXTERN XAllocWMHints_fp soXAllocWMHints; + +typedef int (*XCloseDisplay_fp)(Display *); +FEXTERN XCloseDisplay_fp soXCloseDisplay; + +typedef GC (*XCreateGC_fp)(Display*,Drawable,unsigned long,XGCValues*); +FEXTERN XCreateGC_fp soXCreateGC; + +typedef XImage *(*XCreateImage_fp)(Display*,Visual*,unsigned int,int,int,char*,unsigned int,unsigned int,int,int); +FEXTERN XCreateImage_fp soXCreateImage; + +typedef Pixmap (*XCreatePixmap_fp)(Display*,Drawable,unsigned int,unsigned int,unsigned int); +FEXTERN XCreatePixmap_fp soXCreatePixmap; + +typedef Cursor (*XCreatePixmapCursor_fp)(Display*,Pixmap,Pixmap,XColor*,XColor*,unsigned int,unsigned int); +FEXTERN XCreatePixmapCursor_fp soXCreatePixmapCursor; + +typedef Window (*XCreateWindow_fp)(Display *,Window,int,int,unsigned int,unsigned int,unsigned int,int,unsigned int,Visual *,unsigned long,XSetWindowAttributes *); +FEXTERN XCreateWindow_fp soXCreateWindow; + +typedef int (*XDefineCursor_fp)(Display *,Window,Cursor); +FEXTERN XDefineCursor_fp soXDefineCursor; + +typedef int (*XFillRectangle_fp)(Display*,Drawable,GC,int,int,unsigned int,unsigned int); +FEXTERN XFillRectangle_fp soXFillRectangle; + +typedef int (*XFreeGC_fp)(Display*,GC); +FEXTERN XFreeGC_fp soXFreeGC; + +typedef int (*XFreePixmap_fp)(Display*,Pixmap); +FEXTERN XFreePixmap_fp soXFreePixmap; + +typedef int (*XGrabPointer_fp)(Display *,Window,Bool,unsigned int,int,int,Window,Cursor,Time); +FEXTERN XGrabPointer_fp soXGrabPointer; + +typedef KeySym (*XKeycodeToKeysym_fp)(Display *, KeyCode, int); +FEXTERN XKeycodeToKeysym_fp soXKeycodeToKeysym; + +typedef Bool (*XCheckMaskEvent_fp)(Display *, long, XEvent *); +FEXTERN XCheckMaskEvent_fp soXCheckMaskEvent; + +typedef int (*XLookupString_fp)(XKeyEvent *, char *,int, KeySym, XComposeStatus *); +FEXTERN XLookupString_fp soXLookupString; + +typedef int (*XMapWindow_fp)(Display *,Window); +FEXTERN XMapWindow_fp soXMapWindow; + +typedef Status (*XMatchVisualInfo_fp)(Display *,int,int,int,XVisualInfo *); +FEXTERN XMatchVisualInfo_fp soXMatchVisualInfo; + +typedef int (*XNextEvent_fp)(Display *,XEvent *); +FEXTERN XNextEvent_fp soXNextEvent; + +typedef Display *(*XOpenDisplay_fp)(_Xconst char *); +FEXTERN XOpenDisplay_fp soXOpenDisplay; + +typedef int (*XPutImage_fp)(Display*,Drawable,GC,XImage*,int,int,int,int,unsigned int,unsigned int); +FEXTERN XPutImage_fp soXPutImage; + +typedef int (*XSetIconName_fp)(Display *,Window,_Xconst char *); +FEXTERN XSetIconName_fp soXSetIconName; + +typedef void (*XSetWMNormalHints_fp)(Display *,Window,XSizeHints *); +FEXTERN XSetWMNormalHints_fp soXSetWMNormalHints; + +typedef int (*XStoreName_fp)(Display *,Window,_Xconst char *); +FEXTERN XStoreName_fp soXStoreName; + +typedef int (*XSync_fp)(Display *,Bool); +FEXTERN XSync_fp soXSync; + +typedef int (*XWarpPointer_fp)(Display*,Window,Window,int,int,unsigned int,unsigned int,int,int); +FEXTERN XWarpPointer_fp soXWarpPointer; + +typedef int (*XAutoRepeatOn_fp)(Display*); +FEXTERN XAutoRepeatOn_fp soXAutoRepeatOn; + +typedef int (*XAutoRepeatOff_fp)(Display*); +FEXTERN XAutoRepeatOff_fp soXAutoRepeatOff; + +typedef int (*XGetWindowProperty_fp)(Display*,Window,Atom,long,long,Bool,Atom,Atom*,int*,unsigned long*,unsigned long*,unsigned char**); +FEXTERN XGetWindowProperty_fp soXGetWindowProperty; + +typedef Status (*XQueryTree_fp)(Display*,Window,Window*,Window*,Window**,unsigned int*); +FEXTERN XQueryTree_fp soXQueryTree; + +typedef Atom (*XInternAtom_fp)(Display*,_Xconst char*,Bool); +FEXTERN XInternAtom_fp soXInternAtom; + +typedef int (*XLowerWindow_fp)(Display*,Window); +FEXTERN XLowerWindow_fp soXLowerWindow; + +typedef int (*XRaiseWindow_fp)(Display*,Window); +FEXTERN XRaiseWindow_fp soXRaiseWindow; + +typedef int (*XChangeProperty_fp)(Display*,Window,Atom,Atom,int,int,_Xconst unsigned char*,int); +FEXTERN XChangeProperty_fp soXChangeProperty; + +typedef int (*XChangeWindowAttributes_fp)(Display*,Window,unsigned long,XSetWindowAttributes*); +FEXTERN XChangeWindowAttributes_fp soXChangeWindowAttributes; + +typedef int (*XMoveResizeWindow_fp)(Display*,Window,int,int,unsigned int,unsigned int); +FEXTERN XMoveResizeWindow_fp soXMoveResizeWindow; + +typedef int (*XUnmapWindow_fp)(Display*,Window); +FEXTERN XUnmapWindow_fp soXUnmapWindow; + +typedef int (*XDestroyWindow_fp)(Display*,Window); +FEXTERN XDestroyWindow_fp soXDestroyWindow; + +typedef int (*XFree_fp)(void*); +FEXTERN XFree_fp soXFree; + +typedef Status (*XGetWMNormalHints_fp)(Display*,Window,XSizeHints*,long*); +FEXTERN XGetWMNormalHints_fp soXGetWMNormalHints; + +typedef Status (*XSetWMProtocols_fp)(Display*,Window,Atom*,int); +FEXTERN XSetWMProtocols_fp soXSetWMProtocols; + +typedef int (*XSetWMHints_fp)(Display*,Window,XWMHints*); +FEXTERN XSetWMHints_fp soXSetWMHints; + +typedef int (*XSetStandardProperties_fp)(Display*,Window,_Xconst char*,_Xconst char*,Pixmap,char**,int,XSizeHints*); +FEXTERN XSetStandardProperties_fp soXSetStandardProperties; + +typedef int (*XFlush_fp)(Display*); +FEXTERN XFlush_fp soXFlush; + +typedef int (*XMaskEvent_fp)(Display*,long,XEvent*); +FEXTERN XMaskEvent_fp soXMaskEvent; + +#ifndef DECLARE_POINTERS +bool LoadXWindowsLib(bool load = true); +#else +#include +#include +#include + +void LoadXWindowsLibSetNULL(void) +{ + soXAllocClassHint = NULL; + soXAllocSizeHints = NULL; + soXAllocWMHints = NULL; + soXCloseDisplay = NULL; + soXCreateGC = NULL; + soXCreateImage = NULL; + soXCreatePixmap = NULL; + soXCreatePixmapCursor = NULL; + soXCreateWindow = NULL; + soXDefineCursor = NULL; + soXFillRectangle = NULL; + soXFreeGC = NULL; + soXFreePixmap = NULL; + soXGrabPointer = NULL; + soXLookupString = NULL; + soXMapWindow = NULL; + soXMatchVisualInfo = NULL; + soXNextEvent = NULL; + soXOpenDisplay = NULL; + soXPutImage = NULL; + soXSetIconName = NULL; + soXSetWMNormalHints = NULL; + soXStoreName = NULL; + soXSync = NULL; + soXWarpPointer = NULL; + soXCheckMaskEvent = NULL; + soXKeycodeToKeysym = NULL; + soXLookupString = NULL; + soXAutoRepeatOn = NULL; + soXAutoRepeatOff = NULL; + soXGetWindowProperty = NULL; + soXQueryTree = NULL; + soXInternAtom = NULL; + soXLowerWindow = NULL; + soXRaiseWindow = NULL; + soXChangeProperty = NULL; + soXChangeWindowAttributes = NULL; + soXMoveResizeWindow = NULL; + soXUnmapWindow = NULL; + soXDestroyWindow = NULL; + soXFree = NULL; + soXGetWMNormalHints = NULL; + soXSetWMProtocols = NULL; + soXSetWMHints = NULL; + soXSetStandardProperties = NULL; + soXFlush = NULL; + soXMaskEvent = NULL; +} + +bool LoadXWindowsLib(bool load) +{ +#define XWINDOWSLIB "libX11.so" + static void *handle = NULL; + + if(!load) + { + if(handle) + { + LoadXWindowsLibSetNULL(); + dlclose(handle); + handle = NULL; + return true; + } + fprintf(stderr,"Library Unload Failed: %s\n",XWINDOWSLIB); + return false; + } + + if(handle) + return true; + + // Load the library + handle = dlopen(XWINDOWSLIB,RTLD_LAZY|RTLD_GLOBAL); + if(!handle) + { + fprintf(stderr,"Library Load Failed: %s\n",XWINDOWSLIB); + return false; + } + + soXAllocClassHint = (XAllocClassHint_fp)dlsym(handle,"XAllocClassHint"); + if(!soXAllocClassHint) goto load_error; + + soXAllocSizeHints = (XAllocSizeHints_fp)dlsym(handle,"XAllocSizeHints"); + if(!soXAllocSizeHints) goto load_error; + + soXAllocWMHints = (XAllocWMHints_fp)dlsym(handle,"XAllocWMHints"); + if(!soXAllocWMHints) goto load_error; + + soXCloseDisplay = (XCloseDisplay_fp)dlsym(handle,"XCloseDisplay"); + if(!soXCloseDisplay) goto load_error; + + soXCreateGC = (XCreateGC_fp)dlsym(handle,"XCreateGC"); + if(!soXCreateGC) goto load_error; + + soXCreateImage = (XCreateImage_fp)dlsym(handle,"XCreateImage"); + if(!soXCreateImage) goto load_error; + + soXCreatePixmap = (XCreatePixmap_fp)dlsym(handle,"XCreatePixmap"); + if(!soXCreatePixmap) goto load_error; + + soXCreatePixmapCursor = (XCreatePixmapCursor_fp)dlsym(handle,"XCreatePixmapCursor"); + if(!soXCreatePixmapCursor) goto load_error; + + soXCheckMaskEvent = (XCheckMaskEvent_fp)dlsym(handle,"XCheckMaskEvent"); + if(!soXCheckMaskEvent) goto load_error; + + soXKeycodeToKeysym = (XKeycodeToKeysym_fp)dlsym(handle,"XKeycodeToKeysym"); + if(!soXKeycodeToKeysym) goto load_error; + + soXLookupString = (XLookupString_fp)dlsym(handle,"XLookupString"); + if(!soXLookupString) goto load_error; + + soXCreateWindow = (XCreateWindow_fp)dlsym(handle,"XCreateWindow"); + if(!soXCreateWindow) goto load_error; + + soXDefineCursor = (XDefineCursor_fp)dlsym(handle,"XDefineCursor"); + if(!soXDefineCursor) goto load_error; + + soXFillRectangle = (XFillRectangle_fp)dlsym(handle,"XFillRectangle"); + if(!soXFillRectangle) goto load_error; + + soXFreeGC = (XFreeGC_fp)dlsym(handle,"XFreeGC"); + if(!soXFreeGC) goto load_error; + + soXFreePixmap = (XFreePixmap_fp)dlsym(handle,"XFreePixmap"); + if(!soXFreePixmap) goto load_error; + + soXGrabPointer = (XGrabPointer_fp)dlsym(handle,"XGrabPointer"); + if(!soXGrabPointer) goto load_error; + + soXLookupString = (XLookupString_fp)dlsym(handle,"XLookupString"); + if(!soXLookupString) goto load_error; + + soXMapWindow = (XMapWindow_fp)dlsym(handle,"XMapWindow"); + if(!soXMapWindow) goto load_error; + + soXMatchVisualInfo = (XMatchVisualInfo_fp)dlsym(handle,"XMatchVisualInfo"); + if(!soXMatchVisualInfo) goto load_error; + + soXNextEvent = (XNextEvent_fp)dlsym(handle,"XNextEvent"); + if(!soXNextEvent) goto load_error; + + soXOpenDisplay = (XOpenDisplay_fp)dlsym(handle,"XOpenDisplay"); + if(!soXOpenDisplay) goto load_error; + + soXPutImage = (XPutImage_fp)dlsym(handle,"XPutImage"); + if(!soXPutImage) goto load_error; + + soXSetIconName = (XSetIconName_fp)dlsym(handle,"XSetIconName"); + if(!soXSetIconName) goto load_error; + + soXSetWMNormalHints = (XSetWMNormalHints_fp)dlsym(handle,"XSetWMNormalHints"); + if(!soXSetWMNormalHints) goto load_error; + + soXStoreName = (XStoreName_fp)dlsym(handle,"XStoreName"); + if(!soXStoreName) goto load_error; + + soXSync = (XSync_fp)dlsym(handle,"XSync"); + if(!soXSync) goto load_error; + + soXWarpPointer = (XWarpPointer_fp)dlsym(handle,"XWarpPointer"); + if(!soXWarpPointer) goto load_error; + + soXAutoRepeatOn = (XAutoRepeatOn_fp)dlsym(handle, "XAutoRepeatOn"); + if (!soXAutoRepeatOn) goto load_error; + + soXAutoRepeatOff = (XAutoRepeatOff_fp)dlsym(handle, "XAutoRepeatOff"); + if (!soXAutoRepeatOff) goto load_error; + + soXGetWindowProperty = (XGetWindowProperty_fp)dlsym(handle,"XGetWindowProperty"); + if(!soXGetWindowProperty) goto load_error; + + soXQueryTree = (XQueryTree_fp)dlsym(handle,"XQueryTree"); + if(!soXQueryTree) goto load_error; + + soXInternAtom = (XInternAtom_fp)dlsym(handle,"XInternAtom"); + if(!soXInternAtom) goto load_error; + + soXLowerWindow = (XLowerWindow_fp)dlsym(handle,"XLowerWindow"); + if(!soXLowerWindow) goto load_error; + + soXRaiseWindow = (XRaiseWindow_fp)dlsym(handle,"XRaiseWindow"); + if(!soXRaiseWindow) goto load_error; + + soXChangeProperty = (XChangeProperty_fp)dlsym(handle,"XChangeProperty"); + if(!soXChangeProperty) goto load_error; + + soXChangeWindowAttributes = (XChangeWindowAttributes_fp)dlsym(handle,"XChangeWindowAttributes"); + if(!soXChangeWindowAttributes) goto load_error; + + soXMoveResizeWindow = (XMoveResizeWindow_fp)dlsym(handle,"XMoveResizeWindow"); + if(!soXMoveResizeWindow) goto load_error; + + soXUnmapWindow = (XUnmapWindow_fp)dlsym(handle,"XUnmapWindow"); + if(!soXUnmapWindow) goto load_error; + + soXDestroyWindow = (XDestroyWindow_fp)dlsym(handle,"XDestroyWindow"); + if(!soXDestroyWindow) goto load_error; + + soXFree = (XFree_fp)dlsym(handle,"XFree"); + if(!soXFree) goto load_error; + + soXGetWMNormalHints = (XGetWMNormalHints_fp)dlsym(handle,"XGetWMNormalHints"); + if(!soXGetWMNormalHints) goto load_error; + + soXSetWMProtocols = (XSetWMProtocols_fp)dlsym(handle,"XSetWMProtocols"); + if(!soXSetWMProtocols) goto load_error; + + soXSetWMHints = (XSetWMHints_fp)dlsym(handle,"XSetWMHints"); + if(!soXSetWMHints) goto load_error; + + soXSetStandardProperties = (XSetStandardProperties_fp)dlsym(handle,"XSetStandardProperties"); + if(!soXSetStandardProperties) goto load_error; + + soXFlush = (XFlush_fp)dlsym(handle,"XFlush"); + if(!soXFlush) goto load_error; + + soXMaskEvent = (XMaskEvent_fp)dlsym(handle,"XMaskEvent"); + if(!soXMaskEvent) goto load_error; + + return true; + +load_error: + LoadXWindowsLibSetNULL(); + fprintf(stderr,"Library Symbol Resolve Error: %s\n",XWINDOWSLIB); + dlclose(handle); + handle = NULL; + return false; +} +#endif \ No newline at end of file diff --git a/lnxmvelib/lnxdraw.cpp b/lnxmvelib/lnxdraw.cpp new file mode 100644 index 000000000..a4458c5b6 --- /dev/null +++ b/lnxmvelib/lnxdraw.cpp @@ -0,0 +1,776 @@ +#include +#include +#include +#include +#include +#include +#include +#include "linux/lnxdraw.h" +#include "lnxscreenmode.h" + +//static Display *lpDisplay = NULL; +static int nDefaultScreen = -1; +static unsigned int dwOriginalWidth,dwOriginalHeight; +static LnxWindow **WindowList; +static int NumWindows = 0; +//static int GetXSharedMemory(int size); +inline void BltBuffer16ToPixMap24(unsigned char *pixmap,unsigned char *buffer,int width,int height); +inline void BltBuffer32ToPixMap24(unsigned char *pixmap,unsigned char *buffer,int width,int height); +inline void BltBuffer16ToPixMap16(unsigned char *pixmap,unsigned char *buffer,int width,int height); +inline void BltBuffer32ToPixMap16(unsigned char *pixmap,unsigned char *buffer,int width,int height); + +static SDL_Rect dispSize; + +////////////////////// +// LnxDraw_InitVideo +////////////////////// +// Initializes the Linux video system (for X-Windows) +// +// Returns: +// 0 : no error +// -1 : invalid parameter +// -2 : already initialized +int LnxDraw_InitVideo(LnxVideoDesc *ldesc) +{ + return -1; + if(!ldesc) + return -1; +// if(lpDisplay) +// return -2; + +// lpDisplay = ldesc->dDisplay; +// nDefaultScreen = ldesc->nScreen; + +// dwOriginalWidth = DisplayWidth(lpDisplay,nDefaultScreen); +// dwOriginalHeight = DisplayHeight(lpDisplay,nDefaultScreen); + + memset(&dispSize, '\0', sizeof (dispSize)); + return 0; +} + + + +int d3SDLEventFilter(const SDL_Event *event); + + +///////////////////////// +// LnxDraw_CreateWindow +///////////////////////// +// Creates and displays a window +// +// Returns: +// 0 : no error (handle in lphandle) +// -1 : invalid parameter +// -2 : Display not opened +// -3 : Out of memory +int LnxDraw_CreateWindow(LnxWindowDesc *ldesc,LnxWindow **lphandle) +{ + + return -1; + if(!ldesc || !lphandle) + return -1; + *lphandle = NULL; + +// rcg09182000 don't need to quitsubsystem anymore... +// SDL_QuitSubSystem(SDL_INIT_VIDEO); // here goes nothing... + SDL_ClearError(); + int rc = SDL_Init(SDL_INIT_VIDEO); + if (rc != 0) + { + fprintf(stderr, "SDL: SDL_Init() failed! rc == (%d).\n", rc); + fprintf(stderr, "SDL_GetError() reports \"%s\".\n", SDL_GetError()); + return(-2); + } // if + SDL_SetEventFilter(d3SDLEventFilter); + + SDL_Rect **modes = LinuxVideoMode.getModes(); + Uint32 sdlflags = LinuxVideoMode.getSDLFlags(); + +// if(!lpDisplay) +// return -2; + + // determine what we have to work with (currently) +// unsigned int display_width,display_height; +// display_width = DisplayWidth(lpDisplay,nDefaultScreen); +// display_height = DisplayHeight(lpDisplay,nDefaultScreen); + + // allocate a window for use + LnxWindow *wnd; + wnd = (LnxWindow *)malloc(sizeof(*wnd)); + if(!wnd) + { + return -3; + } + + int i = 0; + + dispSize.x = dispSize.y = 0; + dispSize.w = ldesc->dwWidth; + dispSize.h = ldesc->dwHeight; + + // need these two lines for a Voodoo3 bug. + SDL_ShowCursor( 0 ); + SDL_WM_GrabInput(SDL_GRAB_ON); + + wnd->surface = SDL_SetVideoMode(ldesc->dwWidth, ldesc->dwHeight, ldesc->bpp, sdlflags); + + // failed? Try a window. + if ((wnd->surface == NULL) && (sdlflags & SDL_FULLSCREEN)) + { + sdlflags &= ~SDL_FULLSCREEN; + wnd->surface = SDL_SetVideoMode(ldesc->dwWidth, ldesc->dwHeight, ldesc->bpp, sdlflags); + } // if + + SDL_WM_GrabInput(SDL_GRAB_OFF); + if (!(sdlflags & SDL_FULLSCREEN)) + SDL_ShowCursor(1); + + if (wnd->surface == NULL) + { + fprintf(stderr, "ERROR: SDL could not set the video mode!\n"); + return -3; + } // if + + wnd->fullScreen = (sdlflags & SDL_FULLSCREEN) ? true : false; + + SDL_WM_SetCaption("Descent 3", NULL); + +/* + wnd->WindowPreCreated = (ldesc->dwFlags&LNXDRAWF_USEPRECREATEDWIN)?true:false; + + if(!wnd->WindowPreCreated) + { + // allocate what the hints, etc. + wnd->lpSizeHints = XAllocSizeHints(); + if(!wnd->lpSizeHints) + { + // TODO: Free above allocated hints + free(wnd); + return -3; + } + + wnd->lpWmHints = XAllocWMHints(); + if(!wnd->lpWmHints) + { + // TODO: Free above allocated hints + free(wnd); + return -3; + } + + wnd->lpClassHints = XAllocClassHint(); + if(!wnd->lpClassHints) + { + // TODO: Free above allocated hints + free(wnd); + return -3; + } + } + + // try to match a visual + if (!XMatchVisualInfo(lpDisplay,nDefaultScreen,16,TrueColor,&wnd->viVisualInfo)) + { + fprintf(stderr,"Error: Unable to get 16bit TrueColor Visual\n"); + + // TODO: Free above allocated hints + free(wnd); + return -3; + } + + // see if we have shared memory available + wnd->bHaveSharedMemory = (bool)(XShmQueryExtension(lpDisplay)!=0); + wnd->shmInfo.shmaddr = NULL; + + // even if the display says it has shared memory, it still might not be + // available (if we are not running on a local connection) + if(wnd->bHaveSharedMemory) + { + char *ptr; + char *displayname = (char *)getenv("DISPLAY"); + if (displayname) + { + ptr = displayname; + while (*ptr && (*ptr != ':')) ptr++; + if (*ptr) *ptr = '\0'; + if (!(!strcasecmp(displayname, "unix") || !*displayname)) + wnd->bHaveSharedMemory = false; + }else + wnd->bHaveSharedMemory = false; + } + //fprintf(stdout,"Shared Memory: %savailable\n",wnd->bHaveSharedMemory?"":"Not "); + + if(!wnd->WindowPreCreated) + { + wnd->lpXvisual = wnd->viVisualInfo.visual; + wnd->cmColorMap = DefaultColormapOfScreen(DefaultScreenOfDisplay(lpDisplay)); + + unsigned long attrib_mask; + XSetWindowAttributes attrib; + + // setup some attribute and hints for actual window creation + attrib_mask = CWEventMask | CWColormap | CWBorderPixel; + attrib.event_mask = KeyPressMask | KeyReleaseMask | PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | ExposureMask; + attrib.colormap = wnd->cmColorMap; + attrib.border_pixel = 0; + + wnd->lpSizeHints->width = ldesc->dwWidth; + wnd->lpSizeHints->height = ldesc->dwHeight; + wnd->lpSizeHints->min_width = ldesc->dwWidth; + wnd->lpSizeHints->max_width = ldesc->dwWidth; + wnd->lpSizeHints->min_height = ldesc->dwHeight; + wnd->lpSizeHints->max_height = ldesc->dwHeight; + wnd->lpSizeHints->x = ldesc->dwXPos; + wnd->lpSizeHints->y = ldesc->dwYPos; + wnd->lpSizeHints->flags |= USSize | PMinSize | PMaxSize | USPosition; + + wnd->wWindow = XCreateWindow(lpDisplay,RootWindow(lpDisplay,nDefaultScreen), + ldesc->dwXPos,ldesc->dwYPos, + ldesc->dwWidth,ldesc->dwHeight, + 0, + wnd->viVisualInfo.depth, + InputOutput, + wnd->lpXvisual, + attrib_mask, + &attrib); + + XStoreName(lpDisplay,wnd->wWindow,ldesc->lpszName); + XSetIconName(lpDisplay,wnd->wWindow,ldesc->lpszName); + XSetWMNormalHints(lpDisplay,wnd->wWindow,wnd->lpSizeHints); + + // Display the window! + XMapWindow(lpDisplay,wnd->wWindow); + + // Wait until it is actually visible + bool wait_for_draw = false; + XEvent event; + while (!wait_for_draw) + { + XNextEvent(lpDisplay, &event); + if (event.type == Expose && !event.xexpose.count) wait_for_draw = true; + } + }else + { + wnd->wWindow = *ldesc->pre_created_window; + + // resize window and stuff here + wnd->viVisualInfo = ldesc->pre_created_visinfo; + wnd->lpXvisual = wnd->viVisualInfo.visual; + wnd->cmColorMap = DefaultColormapOfScreen(DefaultScreenOfDisplay(lpDisplay)); + } +*/ + + // We're done, add it to our Window list + WindowList = (LnxWindow **)realloc(WindowList,sizeof(LnxWindow*)*(NumWindows+1)); + if(!WindowList) + { + return -3; + } + + WindowList[NumWindows] = wnd; + *lphandle = wnd; + NumWindows++; + + // Setup window for blitting + wnd->bLocked = false; + wnd->dwWidth = ldesc->dwWidth; + wnd->dwHeight = ldesc->dwHeight; + + // Create the Graphics Context +// int valuemask; +// XGCValues xgcvalues; +// xgcvalues.graphics_exposures = False; +// valuemask = GCGraphicsExposures; + +// wnd->m_GC = XCreateGC(lpDisplay,wnd->wWindow,valuemask,&xgcvalues); +// wnd->lock_ptr = (unsigned char *)malloc(wnd->dwWidth*wnd->dwHeight<<1); + +// int id = GetXSharedMemory(wnd->dwWidth*wnd->dwHeight<<1); +// if(id<0) +// { +// wnd->bHaveSharedMemory = false; +// wnd->shmInfo.shmaddr = NULL; +// }else +// { +// // attach +// wnd->shmInfo.shmid = id; +// wnd->shmInfo.shmaddr = (char *)shmat(id, 0, 0); +// } + + //fprintf(stdout,"Draw: %s shared memory\n",(wnd->bHaveSharedMemory)?"Using":"Not Using"); + + // Initial clear + unsigned char *lock_ptr; + int lock_pitch; + if(LnxDraw_LockSurface(wnd,0,0,wnd->dwWidth-1,wnd->dwHeight-1,&lock_ptr,&lock_pitch)) + { + memset(lock_ptr,0,wnd->dwWidth*wnd->dwHeight<<1); + LnxDraw_UnlockSurface(wnd,lock_ptr); + } + + return 0; +} + +////////////////////////// +// LnxDraw_DestroyWindow +////////////////////////// +// Closes and deletes a window +// +// Returns: +// 0 : no error +// -1 : invalid parameter +int LnxDraw_DestroyWindow(LnxWindow *handle) +{ + return -1; + int i; + LnxWindow *wnd = NULL; + + for(i=0;ishmInfo.shmaddr) + { + // Release shared memory. + shmdt(wnd->shmInfo.shmaddr); + shmctl(wnd->shmInfo.shmid, IPC_RMID, 0); + } + + if(!wnd->WindowPreCreated) + { + // Do what we need to do to close the window + XDestroyWindow(lpDisplay,wnd->wWindow); + + XFree(wnd->lpSizeHints); + XFree(wnd->lpWmHints); + XFree(wnd->lpClassHints); + } + + free(wnd->lock_ptr); +*/ + + if (wnd->fullScreen) + { + SDL_WM_ToggleFullScreen(wnd->surface); + SDL_ShowCursor(1); + } // if + + free(wnd); + + return 0; +} + +//////////////////////// +// LnxDraw_LockSurface +//////////////////////// +// Locks the window surface, giving you a pointer to write data to +// +// Returns: +// true : success +// false : error +bool LnxDraw_LockSurface(LnxWindow *wnd,unsigned x1,unsigned y1,unsigned x2,unsigned y2,unsigned char **ptr,int *pitch) +{ + return -1; + if(!wnd || !ptr || !pitch) + return false; + if(wnd->bLocked) + return false; + wnd->bLocked = true; + + *pitch = wnd->dwWidth<<1; + +/* + int w,h; + if(x2bHaveSharedMemory) + { + wnd->lpImage = XShmCreateImage(lpDisplay,wnd->lpXvisual,wnd->viVisualInfo.depth,ZPixmap,0,&wnd->shmInfo,wnd->dwWidth,h); + wnd->lpImage->bitmap_bit_order = LSBFirst; + wnd->lpImage->byte_order = LSBFirst; + wnd->lpImage->bits_per_pixel = 16; + wnd->lpImage->bytes_per_line = wnd->dwWidth<<1; + wnd->lpImage->red_mask = wnd->lpXvisual->red_mask; + wnd->lpImage->green_mask = wnd->lpXvisual->green_mask; + wnd->lpImage->blue_mask = wnd->lpXvisual->blue_mask; + wnd->lpImage->data = wnd->shmInfo.shmaddr; + + if(!wnd->lpImage->data) + { + wnd->bHaveSharedMemory = false; + //fprintf(stderr,"Shared Memory: Invalid Memory\n"); + goto try_no_shared; + } + + // attach the X server to it + wnd->shmInfo.readOnly = False; + if(!XShmAttach(lpDisplay,&wnd->shmInfo)) + { + wnd->bHaveSharedMemory = false; + //fprintf(stderr,"Shared Memory: Unable to attach to server\n"); + goto try_no_shared; + } + }else + { + wnd->lpImage = XCreateImage(lpDisplay,wnd->lpXvisual,wnd->viVisualInfo.depth,ZPixmap,0,(char *)wnd->lock_ptr,wnd->dwWidth,h,16,0); + wnd->lpImage->bitmap_bit_order = LSBFirst; + wnd->lpImage->byte_order = LSBFirst; + wnd->lpImage->bits_per_pixel = 16; + wnd->lpImage->bytes_per_line = wnd->dwWidth<<1; + wnd->lpImage->red_mask = wnd->lpXvisual->red_mask; + wnd->lpImage->green_mask = wnd->lpXvisual->green_mask; + wnd->lpImage->blue_mask = wnd->lpXvisual->blue_mask; + wnd->lpImage->data = (char *)wnd->lock_ptr; + } + + *ptr = (unsigned char *)wnd->lpImage->data; + wnd->lock_x = x1; + wnd->lock_y = y1; + wnd->lock_w = w; + wnd->lock_h = h; +*/ + + if (SDL_MUSTLOCK(wnd->surface)) + { + if (SDL_LockSurface(wnd->surface) < 0) + return(false); + } // if + + int imgHeight = y2 - y1; + int linesDown = (wnd->dwHeight - imgHeight) / 2; + + *ptr = ((unsigned char *) wnd->surface->pixels) + + ((wnd->surface->format->BytesPerPixel * wnd->dwWidth) * linesDown); + return true; +} + +////////////////////////// +// LnxDraw_UnlockSurface +////////////////////////// +// Unlocks the window surface, blitting the buffer +// +void LnxDraw_UnlockSurface(LnxWindow *wnd,unsigned char *ptr) +{ + return ; + if(!wnd->bLocked) + return; + bool still_have_shared = true; + wnd->bLocked = false; + +/* + // blit the region + if(wnd->bHaveSharedMemory) + { + if(!XShmPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0,0,wnd->lock_x,wnd->lock_y,wnd->lock_w,wnd->lock_h,True)) + { + //fprintf(stderr,"XShmPutImage: blit failed\n"); + wnd->bHaveSharedMemory = false; + } + // Detach from X server + XShmDetach(lpDisplay,&wnd->shmInfo); + }else + { + // draw the image + if(XPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0,0,wnd->lock_x,wnd->lock_y,wnd->lock_w,wnd->lock_h)) + { + //fprintf(stderr,"XPutImage: blit failed\n"); + } + } + + // sync up with server + XSync(lpDisplay, False); + + // Kill the memory + wnd->lpImage->data = NULL;//make sure we don't delete our memory here + XDestroyImage(wnd->lpImage); +*/ + + if (SDL_MUSTLOCK(wnd->surface)) + SDL_UnlockSurface(wnd->surface); + + SDL_UpdateRect(wnd->surface, 0, 0, wnd->dwWidth, wnd->dwHeight); + + #ifdef __DUMP_MVE_TO_DISK + static unsigned long framenum = 0; + char filename[100]; + snprintf(filename, sizeof (filename), "./mve/frame%lu.bmp", framenum); + SDL_SaveBMP(wnd->surface, filename); + framenum++; + #endif +} + +//////////////////////////// +/// LnxDraw_Blit +//////////////////////////// +// Blits a buffer to the window +// +// Returns: +// 0 : no error +// -1 : invalid parameter +// -2 : unknown error +int LnxDraw_Blit(LnxWindow *wnd,unsigned char *ptr,unsigned int x,unsigned int y,unsigned int w,unsigned int h) +{ + return 0; +/* + if(!wnd || !ptr) + return -1; + + //blt to pixmap + if(wnd->viVisualInfo.depth==16) + { + //check for best case + if(x==0 && y==0 && w==wnd->dwWidth && h==wnd->dwHeight) + { + BltBuffer16ToPixMap16((unsigned char *)wnd->lpImage->data,ptr,wnd->dwWidth,wnd->dwHeight); + }else + { + int num_rows_to_blit; + int start_row,pitch; + unsigned char *curr_dest,*curr_src; + + pitch = wnd->dwWidth<<1; + curr_dest = (unsigned char *)wnd->lpImage->data + (y*pitch) + (x<<1); + curr_src = ptr; + num_rows_to_blit = h; + + // blit away + while(num_rows_to_blit>0) + { + BltBuffer16ToPixMap16(curr_dest,curr_src,w,1); + + num_rows_to_blit--; + curr_dest += pitch; + curr_src += pitch; + } + } + }else + { + return -2; + } + + //Update window + if(wnd->bHaveSharedMemory) + { + if(!XShmPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0, 0, 0, 0,wnd->dwWidth,wnd->dwHeight,True)) + { + return -2; + } + }else + { + // draw the image + XPutImage(lpDisplay,wnd->wWindow,wnd->m_GC,wnd->lpImage,0, 0,0,0,wnd->dwWidth,wnd->dwHeight); + } + + // sync up with server + XSync(lpDisplay, False); + + return 0; +*/ +} + +//////////////////////// +// LnxDraw_GetRGBMasks +//////////////////////// +// Returns the RGB masks for the display +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxDraw_GetRGBMasks(LnxWindow *wnd,unsigned int *r,unsigned int *g,unsigned int *b) +{ + return -1; + if(!wnd || !r || !g || !b) + return -1; +/* + *r = wnd->lpImage->red_mask; + *g = wnd->lpImage->green_mask; + *b = wnd->lpImage->blue_mask; +*/ + + SDL_PixelFormat *pixelFmt = wnd->surface->format; + + *r = pixelFmt->Rmask; + *g = pixelFmt->Gmask; + *b = pixelFmt->Bmask; + return(0); +} + +static int GetXSharedMemory(int size) +{ +/* + int key = (14<<24)|(70<<16)|(81<<8)|49; + struct shmid_ds shminfo; + int minsize = 640*480; + int id; + int rc; + int num_try=5; + + do + { + //try to get the id + id = shmget((key_t) key, minsize,0x1FF); + + if(id!=-1) + { + rc = shmctl(id, IPC_STAT, &shminfo); + if(!rc) + { + if(shminfo.shm_nattch) + { + key++; + }else + { + if(getuid()==shminfo.shm_perm.cuid) + { + rc = shmctl(id,IPC_RMID,0); + if(!rc) + { + //fprintf(stderr,"Shared Memory: Stale memory killed\n"); + } + else + { + //fprintf(stderr,"Shared Memory: Unable to kill stale memory\n"); + return -2; + } + + id = shmget((key_t)key,size,IPC_CREAT|0x1FF); + if(id==-1) + { + //fprintf(stderr,"Shared Memory: Unable to create shared memory block (size=%d)\n",size); + return -2; + } + + rc=shmctl(id,IPC_STAT,&shminfo); + break; + } + + if(size>=shminfo.shm_segsz) + { + //fprintf(stderr,"Shared Memory: Using User %d's shared memory\n",shminfo.shm_cpid); + break; + }else + { + //fprintf(stderr,"Shared Memory: Stale memory (User %d, Key=0x%x) is too small\n",shminfo.shm_cpid,key); + key++; + } + } + }else + { + //fprintf(stderr,"Shared Memory: Unable to read stats on 0x%x\n",key); + return -2; + } + }else + { + id = shmget((key_t)key,size,IPC_CREAT|0x1FF); + if(id==-1) + { + //fprintf(stderr,"Shared Memory: Unable to get shared memory (error = %d)\n",errno); + return -2; + } + break; + } + }while (--num_try); + + if(num_try == 0) + { + //fprintf(stderr,"Shared Memory: Unable to get shared memory (too many stale shared memory segments)\n"); + } + + return id; +*/ + return(0); +} + +inline void BltBuffer32ToPixMap16(unsigned char *pixmap,unsigned char *buffer,int width,int height) +{ + unsigned char *data; + unsigned short int l; + int r,g,b,a; + unsigned int c; + + data = (unsigned char *)pixmap; + for ( l = height*width; l > 0; l-- ) + { + c = *(unsigned int *)buffer; + a = ((c & 0xff000000) >> 24); + r = ((c & 0x00ff0000) >> 16); + g = ((c & 0x0000ff00) >> 8); + b = (c & 0x000000ff); + + if(a) + *(unsigned int *)data = (((r>>3)<<10) + ((g>>3)<<5) + (b>>3)); + data += 2; + buffer+=4; + } +} + +inline void BltBuffer16ToPixMap16(unsigned char *pixmap,unsigned char *buffer,int width,int height) +{ + unsigned char *data; + data = (unsigned char *)pixmap; + memcpy(data,buffer,(width*height)<<1); +} + +inline void BltBuffer32ToPixMap24(unsigned char *pixmap,unsigned char *buffer,int width,int height) +{ + unsigned char *data; + unsigned short int l; + int r,g,b,a; + unsigned int c; + + data = ( unsigned char *)pixmap; + for(l=height*width;l>0;l--) + { + c = *(unsigned int *)buffer; + a = ((c & 0xff000000) >> 24); + r = ((c & 0x00ff0000) >> 16); + g = ((c & 0x0000ff00) >> 8); + b = (c & 0x000000ff); + + if(a) + *(unsigned long *)data = ((r << 16) + (g << 8) + b); + data += 4; + buffer+=4; + } +} + +inline void BltBuffer16ToPixMap24(unsigned char *pixmap,unsigned char *buffer,int width,int height) +{ + unsigned char *data; + unsigned short int l; + int r,g,b,a; + unsigned short c; + + data = ( unsigned char *)pixmap; + for(l=height*width;l>0;l--) + { + c = *(unsigned short *)buffer; + a = ((c&0x8000)>>15); + r = ((c&0x7C00)>>10); + g = ((c&0x03E0)>>5); + b = (c&0x001F); + + if(a) + *(unsigned long *)data = ((r << 19) + (g << 11) + (b<<3)); + data +=4; + buffer+=2; + } +} + diff --git a/lnxmvelib/lnxdsound.cpp b/lnxmvelib/lnxdsound.cpp new file mode 100644 index 000000000..3540a6313 --- /dev/null +++ b/lnxmvelib/lnxdsound.cpp @@ -0,0 +1,845 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "dyna_pthread.h"//threads +#include "linux/lnxdsound.h" + +#include "SDL.h" +#include "SDL_audio.h" + +#include + +#define FRAGMENT_LENGTH (LnxBuffers[0]->bps>>4) +#define FREQUENCY_SHIFT (14) + +/* + * TODO: + * * Might be wise to use mutex's for the enter/exit critical functions + */ +static int LnxNumBuffers = 0; +static LnxSoundBuffer **LnxBuffers = NULL; +static LnxSoundDevice LinuxSoundDevice; + +static bool StartupSoundSystem(LnxSoundDevice *dev); +static void ShutdownSoundSystem(void); +static void LinuxSoundMixWithVolume(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len); +static unsigned int LinuxSoundMixNormalize(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len); +static unsigned int LinuxSoundMixInMainBuffer(LnxSoundBuffer *dsb, int len); +static void LinuxSoundMixBuffersIntoMain(int len); +static void LinuxSoundThreadHandler(void *unused, Uint8 *stream, int len); + +static inline void enter_critical(void) +{ + SDL_LockAudio(); +} + +static inline void exit_critical(void) +{ + SDL_UnlockAudio(); +} + +/////////////////////////////// +// LnxSound_CreateSoundBuffer +/////////////////////////////// +// Creates a sound buffer to be used with mixing and output. +// +// Returns: +// -1 : Invalid Parameter +// -2 : Out of memory +// 0 : Ok! +int LnxSound_CreateSoundBuffer(LnxSoundDevice *dev,LnxBufferDesc *lbdesc,LnxSoundBuffer **lsndb) +{ + WAVEFORMATEX *wfex; + + if(!lbdesc || !lsndb || !dev) + return -1; + + wfex = lbdesc->lpwfxFormat; + if(!wfex) + return -1; + + // Check to see if we have a primary buffer yet, if not, create it + // now + if(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER) + { + if(LnxNumBuffers!=0) + return -1; + }else + { + if(LnxNumBuffers==0) + { + // we need to create a primary buffer + LnxSoundBuffer *primary; + LnxBufferDesc primdesc; + WAVEFORMATEX wf; + + memset(&primdesc,0,sizeof(LnxBufferDesc)); + memset(&wf,0,sizeof(wf)); + + primdesc.dwBufferBytes = 0; + primdesc.dwFlags = LNXSND_CAPS_PRIMARYBUFFER; + primdesc.lpwfxFormat = &wf; + + int ret = LnxSound_CreateSoundBuffer(dev,&primdesc,&primary); + if(ret!=0) + return ret; + } + } + + *lsndb = (LnxSoundBuffer *)malloc(sizeof(LnxSoundBuffer)); + if(!(*lsndb)) + return -2; + memset(*lsndb,0,sizeof(LnxSoundBuffer)); + + if(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER) + { + (*lsndb)->buffer_len = dev->bps; + (*lsndb)->freq = dev->freq; + (*lsndb)->bps = dev->bps; + (*lsndb)->buffer = NULL; + }else + { + (*lsndb)->buffer_len = lbdesc->dwBufferBytes; + (*lsndb)->freq = lbdesc->lpwfxFormat->nSamplesPerSec; + + (*lsndb)->buffer = (unsigned char *)malloc((*lsndb)->buffer_len); + if(!(*lsndb)->buffer) + { + free(*lsndb); + *lsndb = NULL; + return -2; + } + memset((*lsndb)->buffer,0,(*lsndb)->buffer_len); + } + + (*lsndb)->play_cursor = 0; + (*lsndb)->write_cursor = 0; + (*lsndb)->playing = 0; + (*lsndb)->left_vol = (1 << 15); + (*lsndb)->right_vol = (1 << 15); + + if(!(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER)) + { + (*lsndb)->freq_adjustment = ((*lsndb)->freq<freq; + (*lsndb)->bps = (*lsndb)->freq * lbdesc->lpwfxFormat->nBlockAlign; + } + + memcpy(&((*lsndb)->lbdesc),lbdesc,sizeof(LnxBufferDesc)); + + if(!(lbdesc->dwFlags&LNXSND_CAPS_PRIMARYBUFFER)) + { + memcpy(&((*lsndb)->wfx),lbdesc->lpwfxFormat,sizeof(WAVEFORMATEX)); + }else + { + // set up the wave format based on the device settings (primary) + (*lsndb)->wfx.wFormatTag = WAVE_FORMAT_PCM; + (*lsndb)->wfx.nChannels = dev->channels; + (*lsndb)->wfx.nSamplesPerSec = dev->freq; + (*lsndb)->wfx.nBlockAlign = dev->channels * (dev->bit_depth/8); + (*lsndb)->wfx.nAvgBytesPerSec = dev->freq * (*lsndb)->wfx.nBlockAlign; + (*lsndb)->wfx.wBitsPerSample = dev->bit_depth; + (*lsndb)->wfx.cbSize = 0; + } + + + if(LnxBuffers) + { + enter_critical(); + LnxBuffers = (LnxSoundBuffer **)realloc(LnxBuffers,sizeof(LnxSoundBuffer *)*(LnxNumBuffers+1)); + LnxBuffers[LnxNumBuffers] = *lsndb; + LnxNumBuffers++; + exit_critical(); + }else + { + LnxBuffers = (LnxSoundBuffer **)malloc(sizeof(LnxSoundBuffer *)); + LnxBuffers[0] = *lsndb; + LnxNumBuffers++; + + // Initialize the Sound system and thread + StartupSoundSystem(dev); + } + + return 0; +} + +//////////////////////////// +// LnxSoundBuffer_Release +//////////////////////////// +// Releases the memory associated with a sound buffer. This pointer is +// no longer valid after return. +// +// Returns: +// -1 : Invalid Parameter +// 0 : Ok! +int LnxSoundBuffer_Release(LnxSoundBuffer *buff) +{ + int i; + + if(!buff) + return -1; + + for(i=0;ibuffer) + free(buff->buffer); + free(buff); + }else + return -1; + + if(LnxNumBuffers==1) + { + // we freed the last non-primary buffer + // so remove the primary buffer that is remaining + return LnxSoundBuffer_Release(LnxBuffers[0]); + } + + return 0; +} + +////////////////////////////// +// LnxSoundBuffer_SetVolume +////////////////////////////// +// Sets the volume of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set volume +// -2 : Invalid parameters +int LnxSoundBuffer_SetVolume(LnxSoundBuffer *buff,signed long vol) +{ + if(!buff) + return -1; + + if(!(buff->lbdesc.dwFlags&LNXSND_CAPS_CTRLVOLUME)) + return -1; + + if((vol>LNXSND_VOLUME_MAX)||(vollbdesc.dwFlags&LNXSND_CAPS_PRIMARYBUFFER) + { + // not supported + enter_critical(); + buff->volume = vol; + exit_critical(); + return 0; + } + + enter_critical(); + + buff->volume = vol; + + double vt; + vt = (double)(buff->volume-(buff->pan>0?buff->pan:0)); + buff->left_vol = (unsigned long)(pow(2.0,vt/600.0)*32768.0); + vt = (double)(buff->volume+(buff->pan<0?buff->pan:0)); + buff->right_vol = (unsigned long)(pow(2.0,vt/600.0)*32768.0); + + exit_critical(); + + return 0; +} + +/////////////////////////// +// LnxSoundBuffer_SetPan +/////////////////////////// +// Sets the pan of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set pan +// -2 : Invalid parameters +int LnxSoundBuffer_SetPan(LnxSoundBuffer *buff,signed long pan) +{ + if(!buff) + return -1; + + if((pan > LNXSND_PAN_RIGHT) || (pan < LNXSND_PAN_LEFT)) + return -2; + + if(!(buff->lbdesc.dwFlags&LNXSND_CAPS_CTRLPAN)|| + (buff->lbdesc.dwFlags&LNXSND_CAPS_PRIMARYBUFFER)) + { + return -1; + } + + enter_critical(); + + buff->pan = pan; + + double pt; + pt = (double)(buff->volume-(buff->pan>0?buff->pan:0)); + buff->left_vol = (unsigned long)(pow(2.0,pt/600.0)*32768.0); + pt = (double)(buff->volume+(buff->pan<0?buff->pan:0)); + buff->right_vol = (unsigned long)(pow(2.0,pt/600.0)*32768.0); + + exit_critical(); + + return 0; +} + +///////////////////////// +// LnxSoundBuffer_Stop +///////////////////////// +// Stops a buffer from playing +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Stop(LnxSoundBuffer *buff) +{ + if(!buff) + return -1; + + enter_critical(); + buff->playing = 0; + exit_critical(); + return 0; +} + +///////////////////////// +// LnxSoundBuffer_Play +///////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Play(LnxSoundBuffer *buff,unsigned int flags) +{ + if(!buff) + return -1; + + enter_critical(); + buff->flags = flags; + buff->playing = 1; + exit_critical(); + return 0; +} + +//////////////////////////// +// LnxSoundBuffer_GetCaps +//////////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCaps(LnxSoundBuffer *buff,LinuxSoundCaps *caps) +{ + if(!caps || !buff) + return -1; + + caps->dwFlags = buff->lbdesc.dwFlags|LNXSND_CAPS_LOCSOFTWARE; + caps->dwBufferBytes = buff->lbdesc.dwBufferBytes; + + return 0; +} + +////////////////////////////// +// LnxSoundBuffer_GetStatus +////////////////////////////// +// Returns the status of a buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetStatus(LnxSoundBuffer *buff,unsigned int *status) +{ + if(!status || !buff) + return -1; + + *status = 0; + if(buff->playing) *status |= LNXSND_PLAYING; + if(buff->flags&LNXSND_LOOPING) *status |= LNXSND_LOOPING; + + return 0; +} + +/////////////////////////////////////// +// LnxSoundBuffer_GetCurrentPosition +/////////////////////////////////////// +// Returns the current play and write positions of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCurrentPosition(LnxSoundBuffer *buff,unsigned int *ppos,unsigned int *wpos) +{ + if(!buff) + return -1; + + if(ppos) *ppos = buff->play_cursor; + if(wpos) *wpos = buff->write_cursor; + + return 0; +} + +/////////////////////////////////////// +// LnxSoundBuffer_SetCurrentPosition +/////////////////////////////////////// +// Sets the current play position of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_SetCurrentPosition(LnxSoundBuffer *buff,unsigned int pos) +{ + if(!buff) + return -1; + + enter_critical(); + buff->play_cursor = pos; + exit_critical(); + return 0; +} + +///////////////////////// +// LnxSoundBuffer_Lock +///////////////////////// +// Locks the given buffer, returning pointer(s) to the buffer(s) along with +// available the size of the buffer(s) for writing. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Lock( + LnxSoundBuffer *buff, + unsigned int pos, + unsigned int numbytes, + void **ptr1, + unsigned int *numbytes1, + void **ptr2, + unsigned int *numbytes2, + unsigned int flags) +{ + if(!buff) + return -1; + + if(flags&LNXSND_LOCK_FROMWRITECURSOR) + pos += buff->write_cursor; + if(flags&LNXSND_LOCK_ENTIREBUFFER) + numbytes = buff->buffer_len; + if(numbytes>buff->buffer_len) + numbytes = buff->buffer_len; + + assert(numbytes1!=numbytes2); + assert(ptr1!=ptr2); + + if(pos+numbytes<=buff->buffer_len) + { + *(unsigned char **)ptr1 = buff->buffer+pos; + *numbytes1 = numbytes; + if(ptr2) + *(unsigned char **)ptr2 = NULL; + if(numbytes2) + *numbytes2 = 0; + }else + { + *(unsigned char **)ptr1 = buff->buffer+pos; + *numbytes1 = buff->buffer_len-pos; + if(ptr2) + *(unsigned char **)ptr2 = buff->buffer; + if(numbytes2) + *numbytes2 = numbytes-(buff->buffer_len-pos); + } + return 0; +} + +/////////////////////////// +// LnxSoundBuffer_Unlock +/////////////////////////// +// Unlocks a buffer. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Unlock( + LnxSoundBuffer *buff, + void *ptr1, + unsigned int num1, + void *ptr2, + unsigned int num2) +{ + if(!buff) + return -1; + + return 0; +} + +/////////////////////////////////////////// +// Internal Sound System routines +////////////////////////////////////////////////////////////// + +// Starts up the sound processing thread +static bool StartupSoundSystem(LnxSoundDevice *dev) +{ + SDL_AudioSpec spec; + + if(LnxNumBuffers<1) + return false; + + memcpy(&LinuxSoundDevice,dev,sizeof(LnxSoundDevice)); + spec.freq = dev->freq; + spec.format = dev->bit_depth == 8 ? AUDIO_U8 : AUDIO_S16SYS; + spec.channels = dev->channels; + spec.samples = 1024; + spec.callback = LinuxSoundThreadHandler; + + if ( SDL_OpenAudio(&spec, NULL) < 0 ) { + return false; + } + SDL_PauseAudio(0); + return true; +} + +// Shutsdown the sound processing thread +static void ShutdownSoundSystem(void) +{ + SDL_CloseAudio(); +} + +static inline void GetValues(const LnxSoundBuffer *dsb, unsigned char *buf, unsigned int *fl, unsigned int *fr) +{ + signed short *bufs = (signed short *) buf; + + // 8 bit stereo + if((dsb->wfx.wBitsPerSample==8)&&dsb->wfx.nChannels==2) + { + *fl = (*buf - 128) << 8; + *fr = (*(buf+1) - 128) << 8; + return; + } + + // 16 bit stereo + if((dsb->wfx.wBitsPerSample==16)&&dsb->wfx.nChannels==2) + { + *fl = *bufs; + *fr = *(bufs + 1); + return; + } + + // 8 bit mono + if((dsb->wfx.wBitsPerSample==8)&&dsb->wfx.nChannels==1) + { + *fl = (*buf - 128)<<8; + *fr = *fl; + return; + } + + // 16 bit mono + if((dsb->wfx.wBitsPerSample==16)&&dsb->wfx.nChannels==1) + { + *fl = *bufs; + *fr = *bufs; + return; + } + return; +} + +static inline void SetValues(unsigned char *buf,unsigned int fl,unsigned int fr) +{ + signed short *bufs = (signed short *) buf; + + // 8 bit stereo + if((LnxBuffers[0]->wfx.wBitsPerSample==8)&&(LnxBuffers[0]->wfx.nChannels==2)) + { + *buf = (fl+32768)>>8; + *(buf + 1) = (fr+32768)>>8; + return; + } + + // 16 bit stereo + if((LnxBuffers[0]->wfx.wBitsPerSample==16)&&(LnxBuffers[0]->wfx.nChannels==2)) + { + *bufs = fl; + *(bufs + 1) = fr; + return; + } + + // 8 bit mono + if((LnxBuffers[0]->wfx.wBitsPerSample==8)&&(LnxBuffers[0]->wfx.nChannels==1)) + { + *buf = (((fl+fr)>>1)+32768)>>8; + return; + } + + // 16 bit mono + if((LnxBuffers[0]->wfx.wBitsPerSample==16)&&(LnxBuffers[0]->wfx.nChannels==1)) + { + *bufs = (fl+fr)>>1; + return; + } + return; +} + +static void LinuxSoundMixWithVolume(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len) +{ + unsigned int i,inc=(LnxBuffers[0]->wfx.wBitsPerSample>>3); + unsigned char *bpc=buf; + signed short *bps=(signed short *)buf; + + if ((!(dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLPAN)||(dsb->pan==0)) && + (!(dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLVOLUME)||(dsb->volume==0))) + return; + + for(i=0;iright_vol:dsb->left_vol))>>15); + *bpc=val+128; + bpc++; + }break; + case 2: + { + val=*bps; + val=((val*((i&inc)?dsb->right_vol:dsb->left_vol))>>15); + *bps=val; + bps++; + }break; + } + } +} + +static unsigned int LinuxSoundMixNormalize(LnxSoundBuffer *dsb,unsigned char *buf,unsigned int len) +{ + unsigned int i, size, ipos, ilen, fieldL, fieldR; + unsigned char *ibp, *obp; + unsigned int iAdvance = dsb->wfx.nBlockAlign; + unsigned int oAdvance = LnxBuffers[0]->wfx.nBlockAlign; + + ibp=dsb->buffer+dsb->play_cursor; + obp=buf; + + if ((dsb->freq==LnxBuffers[0]->wfx.nSamplesPerSec) && + (dsb->wfx.wBitsPerSample==LnxBuffers[0]->wfx.wBitsPerSample) && + (dsb->wfx.nChannels==LnxBuffers[0]->wfx.nChannels)) + { + if((ibp+len)<(unsigned char *)(dsb->buffer+dsb->buffer_len)) + memcpy(obp,ibp,len); + else + { + memcpy(obp,ibp,dsb->buffer_len-dsb->play_cursor); + memcpy(obp+(dsb->buffer_len-dsb->play_cursor),dsb->buffer,len-(dsb->buffer_len-dsb->play_cursor)); + } + return len; + } + + if(dsb->freq==LnxBuffers[0]->wfx.nSamplesPerSec) + { + ilen = 0; + for(i=0;i=(unsigned char *)(dsb->buffer+dsb->buffer_len)) + ibp=dsb->buffer; + } + return (ilen); + } + + size=len/oAdvance; + ilen=((size*dsb->freq_adjustment)>>FREQUENCY_SHIFT)*iAdvance; + for(i=0;ifreq_adjustment)>>FREQUENCY_SHIFT)*iAdvance)+dsb->play_cursor; + + if(ipos>=dsb->buffer_len) + ipos%=dsb->buffer_len; + + GetValues(dsb,(dsb->buffer+ipos),&fieldL,&fieldR); + SetValues(obp,fieldL,fieldR); + obp+=oAdvance; + } + return ilen; +} + +int DoMulDiv(int nNumber,int nNumerator,int nDenominator) +{ + if (!nDenominator) + return -1; + long long ret; + ret = (((long long)nNumber*nNumerator)+(nDenominator/2))/nDenominator; + + if((ret>0x7FFFFFFF)||(ret<0xFFFFFFFF)) + return -1; + return ret; +} + +static void *TempSoundBuffer = NULL; +static int TempSoundBufferLen = 0; +static unsigned int LinuxSoundMixInMainBuffer(LnxSoundBuffer *dsb, int len) +{ + unsigned int i,ilen,advance = (LnxBuffers[0]->wfx.wBitsPerSample>>3); + unsigned char *buf,*ibuf,*obuf; + signed int temp,field; + signed short *ibufs,*obufs; + + if(!(dsb->flags&LNXSND_LOOPING)) + { + temp=DoMulDiv(LnxBuffers[0]->wfx.nAvgBytesPerSec,dsb->buffer_len,dsb->bps)-DoMulDiv(LnxBuffers[0]->wfx.nAvgBytesPerSec,dsb->play_cursor,dsb->bps); + len=(len>temp)?temp:len; + } + len&=~3;//align to 4 byte boundary + + if(!len) + { + dsb->playing=0; + dsb->write_cursor=0; + dsb->play_cursor=0; + return 0; + } + + if(len>TempSoundBufferLen) + { + void *nb=realloc(TempSoundBuffer,len); + if(nb) + { + TempSoundBuffer=nb; + TempSoundBufferLen=len; + buf=ibuf=(unsigned char *)nb; + }else + { + return 0; + } + }else + { + buf=ibuf=(unsigned char *)TempSoundBuffer; + } + + ilen = LinuxSoundMixNormalize(dsb,ibuf,len); + if((dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLPAN)||(dsb->lbdesc.dwFlags&LNXSND_CAPS_CTRLVOLUME)) + { + LinuxSoundMixWithVolume(dsb,ibuf,len); + } + + obuf = LnxBuffers[0]->buffer + LnxBuffers[0]->play_cursor; + for(i=0;iwfx.wBitsPerSample==16) + { + field=*ibufs; + field+=*obufs; + field=(field>32767)?(32767):field; + field=(field<-32768)?(-32768):field; + *obufs=field; + }else + { + field=(*ibuf-128); + field+=(*obuf-128); + field=(field>127)?(127):field; + field=(field<-128)?(-128):field; + *obuf=field+128; + } + ibuf+=advance; + obuf+=advance; + if(obuf>=(unsigned char *)(LnxBuffers[0]->buffer+LnxBuffers[0]->buffer_len)) + obuf = LnxBuffers[0]->buffer; + } + + // adjust positions of the cursors in the buffer + dsb->play_cursor+=ilen; + dsb->write_cursor=dsb->play_cursor+ilen; + + if(dsb->play_cursor>=dsb->buffer_len) + { + if(!(dsb->flags&LNXSND_LOOPING)) + { + // we're not looping, this buffer is done, reset it + dsb->playing=0; + dsb->write_cursor=0; + dsb->play_cursor=0; + }else + { + // loop back around + dsb->play_cursor=dsb->play_cursor%dsb->buffer_len; + } + } + + if(dsb->write_cursor>=dsb->buffer_len) + { + dsb->write_cursor=dsb->write_cursor%dsb->buffer_len; + } + + return len; +} + +static void LinuxSoundMixBuffersIntoMain(int len) +{ + LnxSoundBuffer *dsb; + + // only go to 1 since 0 is the main buffer + for(int i=LnxNumBuffers-1;i>0;i--) + { + if(!(dsb=LnxBuffers[i])) + continue; + + if(dsb->buffer_len && dsb->playing) { + LinuxSoundMixInMainBuffer(dsb, len); + } + } +} + + +static void LinuxSoundThreadHandler(void *unused, Uint8 *stream, int len) +{ + LnxBuffers[0]->buffer = stream; + LnxBuffers[0]->buffer_len = len; + LnxBuffers[0]->play_cursor = 0; + LnxBuffers[0]->write_cursor = 0; + + LinuxSoundMixBuffersIntoMain(len); + + LnxBuffers[0]->buffer = NULL; +} diff --git a/lnxmvelib/lnxdsound.h b/lnxmvelib/lnxdsound.h new file mode 100644 index 000000000..d59ed0be2 --- /dev/null +++ b/lnxmvelib/lnxdsound.h @@ -0,0 +1,224 @@ +#ifndef __LNX_DSOUND_H_ +#define __LNX_DSOUND_H_ + +// Max&Min values for settings +#define LNXSND_VOLUME_MAX 0 +#define LNXSND_VOLUME_MIN -10000 +#define LNXSND_PAN_LEFT -10000 +#define LNXSND_PAN_RIGHT 10000 + +// Status/Buffer flags +#define LNXSND_PLAYING 0x0001 +#define LNXSND_LOOPING 0x0002 + +// Buffer lock flags +#define LNXSND_LOCK_FROMWRITECURSOR 0x0001 +#define LNXSND_LOCK_ENTIREBUFFER 0x0002 + +// Capability flags +#define LNXSND_CAPS_PRIMARYBUFFER 0x0001 +#define LNXSND_CAPS_CTRLVOLUME 0x0002 +#define LNXSND_CAPS_CTRLPAN 0x0004 +#define LNXSND_CAPS_CTRLFREQUENCY 0x0008 +#define LNXSND_CAPS_CTRLDEFAULT 0x000E +#define LNXSND_CAPS_LOCSOFTWARE 0x0010 + +typedef struct +{ + int sound_device; // file device handle for sound + unsigned int bps; // (bytes per second) channels*freq*bit_depth/8 + unsigned int freq; // frequency (22050, etc.) + unsigned int bit_depth; // 8 or 16 + unsigned int channels; // 1 or 2 (mono or stereo) +}LnxSoundDevice; + +typedef struct +{ + unsigned short wFormatTag; + unsigned short nChannels; + unsigned int nSamplesPerSec; + unsigned int nAvgBytesPerSec; + unsigned short nBlockAlign; + unsigned short wBitsPerSample; + unsigned short cbSize; +} WAVEFORMATEX; // Taken from Windows for porting +#define WAVE_FORMAT_PCM 0x01 + +typedef struct +{ + unsigned int dwFlags; + unsigned int dwBufferBytes; +}LinuxSoundCaps; + +typedef struct +{ + WAVEFORMATEX *lpwfxFormat; + unsigned int dwBufferBytes; + unsigned int dwFlags; +} LnxBufferDesc; + +typedef struct +{ + int freq_adjustment; + int bps; + unsigned int buffer_len; + unsigned int play_cursor; + unsigned int write_cursor; + unsigned int flags; + unsigned long left_vol,right_vol; + + unsigned char *buffer; + + signed long volume; + signed long pan; + + WAVEFORMATEX wfx; + + LnxBufferDesc lbdesc; + + unsigned short freq; + char playing; + char __pad; +} LnxSoundBuffer; + +/////////////////////////////// +// LnxSound_CreateSoundBuffer +/////////////////////////////// +// Creates a sound buffer to be used with mixing and output. +// +// Returns: +// -1 : Invalid Parameter +// -2 : Out of memory +// 0 : Ok! +int LnxSound_CreateSoundBuffer(LnxSoundDevice *dev,LnxBufferDesc *lbdesc,LnxSoundBuffer **lsndb); + +//////////////////////////// +// LnxSoundBuffer_Release +//////////////////////////// +// Releases the memory associated with a sound buffer. This pointer is +// no longer valid after return. +// +// Returns: +// -1 : Invalid Parameter +// 0 : Ok! +int LnxSoundBuffer_Release(LnxSoundBuffer *buff); + +////////////////////////////// +// LnxSoundBuffer_SetVolume +////////////////////////////// +// Sets the volume of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set volume +// -2 : Invalid parameters +int LnxSoundBuffer_SetVolume(LnxSoundBuffer *buff,signed long vol); + +/////////////////////////// +// LnxSoundBuffer_SetPan +/////////////////////////// +// Sets the pan of a buffer. +// +// Returns: +// 0 : no error +// -1 : Cannot set pan +// -2 : Invalid parameters +int LnxSoundBuffer_SetPan(LnxSoundBuffer *buff,signed long pan); + +///////////////////////// +// LnxSoundBuffer_Stop +///////////////////////// +// Stops a buffer from playing +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Stop(LnxSoundBuffer *buff); + +///////////////////////// +// LnxSoundBuffer_Play +///////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Play(LnxSoundBuffer *buff,unsigned int flags); + +//////////////////////////// +// LnxSoundBuffer_GetCaps +//////////////////////////// +// Starts a buffer playing (or changes the flags for a buffer currently +// playing). +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCaps(LnxSoundBuffer *buff,LinuxSoundCaps *caps); + +////////////////////////////// +// LnxSoundBuffer_GetStatus +////////////////////////////// +// Returns the status of a buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetStatus(LnxSoundBuffer *buff,unsigned int *status); + +/////////////////////////////////////// +// LnxSoundBuffer_GetCurrentPosition +/////////////////////////////////////// +// Returns the current play and write positions of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_GetCurrentPosition(LnxSoundBuffer *buff,unsigned int *ppos,unsigned int *wpos); + +/////////////////////////////////////// +// LnxSoundBuffer_SetCurrentPosition +/////////////////////////////////////// +// Sets the current play position of the buffer +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_SetCurrentPosition(LnxSoundBuffer *buff,unsigned int pos); + +///////////////////////// +// LnxSoundBuffer_Lock +///////////////////////// +// Locks the given buffer, returning pointer(s) to the buffer(s) along with +// available the size of the buffer(s) for writing. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Lock( + LnxSoundBuffer *buff, + unsigned int pos, + unsigned int numbytes, + void **ptr1, + unsigned int *numbytes1, + void **ptr2, + unsigned int *numbytes2, + unsigned int flags); + +/////////////////////////// +// LnxSoundBuffer_Unlock +/////////////////////////// +// Unlocks a buffer. +// +// Returns: +// 0 : no error +// -1 : invalid parameters +int LnxSoundBuffer_Unlock( + LnxSoundBuffer *buff, + void *ptr1, + unsigned int num1, + void *ptr2, + unsigned int num2); + +#endif \ No newline at end of file diff --git a/lnxmvelib/mveasm.cpp b/lnxmvelib/mveasm.cpp new file mode 100644 index 000000000..f5ccedd69 --- /dev/null +++ b/lnxmvelib/mveasm.cpp @@ -0,0 +1,1645 @@ +/* +** mveasm.cpp +** +** Interplay Movie File (MVE) Player Library (32-Bit Linux Version) +** Written by Paul Allen Edelstein. Partial Linux port by Jeff Slutter. +** +** (c) 1997 Interplay Productions. All Rights Reserved. +** This file is confidential and consists of proprietary information +** of Interplay Productions. This file and associated libraries +** may not, in whole or in part, be disclosed to third parties, +** incorporated into any software product which is not being created +** for Interplay Productions, copied or duplicated in any form, +** without the prior written permission of Interplay Productions. +** Further, you may not reverse engineer, decompile or otherwise +** attempt to derive source code of this material. +** +*/ +#include "mvelibl.h" +#include "mvelibi.h" +#include "byteswap.h" + +// rcg07272000 +// need this for SIGTRAP on non-Intel platforms. Intel uses int $3. +#if (!defined __i386__) +#include +#endif + +extern unsigned char* nf_buf_cur; +extern unsigned char* nf_buf_prv; +extern unsigned nf_new_x; +extern unsigned nf_new_y; +extern unsigned nf_new_w; +extern unsigned nf_new_h; +extern unsigned char nf_fqty; // Number of fields +extern unsigned nf_new_row0; // SHEIGHT*width*2-width +extern unsigned nf_width; // wqty * SWIDTH +extern unsigned nf_new_line; // width - SWIDTH +extern unsigned nf_back_right; // (SHEIGHT-1)*width + +extern signed short snd_8to16[256]; +void nfHPkDecomp(unsigned char *ops,unsigned char *comp,int x,int y,int w,int h); +void nfPkConfig(void); +unsigned sndDecompM16(unsigned short *dst, unsigned char *src,unsigned len, unsigned prev); +unsigned sndDecompS16(unsigned short *dst, unsigned char *src,unsigned len, unsigned prev); + +void Trans16Blk(unsigned char *edi,unsigned char *idx); +void call_hnfxycshift(unsigned int eax,unsigned char **medi,unsigned char **mesi,int nfpk_back_right); +void call_hnfxypshift(unsigned int eax,unsigned char **medi,unsigned char **mesi,int nfpk_back_right,int DiffBufPtrs); +void call_hnfshift(unsigned int meax,unsigned char **medi,unsigned char **mesi,int nfpk_back_right); + +//-------------------------------------------------------------------- +// Sound Management +//-------------------- + +// Decompresses a mono stream containing len samples +// (src is len bytes, dst is len*2 bytes) +// prev is the previous decompression state or zero. +// Returns new decompression state. +unsigned sndDecompM16(unsigned short *dst, unsigned char *src,unsigned len, unsigned prev) +{ + unsigned int i,eax,ebx; + if(len==0) + return prev; + eax = prev; + ebx = 0; + + for(i=0;i>16); + ebx = 0; + + for(i=0;iDiffBufPtrs = nf_buf_prv - nf_buf_cur; + + if(HI_COLOR_FLAG) + { + nf_new_x = nf->x<<(LOG2_SWIDTH+1); + nf_new_w = nf->w<<(LOG2_SWIDTH+1); + }else + { + nf_new_x = nf->x<w<y<h<new_row = nf_new_row0 - nf_new_w; + + // Move to correct place in current buffer + nf->tbuf = nf_buf_cur; + if(nf->x||nf->y) + { + nf->tbuf += nf_new_y*nf_width + nf_new_x; + } +} + +//---------------------------------------------------------------------- + +// signed 8-bit y * nf_width +signed int nfpk_ShiftY[256]; + +// Constant tables + +// 8-bit -8:7 x nf_width + -8:7 +signed short nfpk_ShiftP1[256]; +signed short nfpk_ShiftP2[256]; + + +// Constant tables +// mov eax, ebx/ecx +// EBX = 0 +// ECX = 1 +unsigned char nfhpk_mov4l[64]; + +// mov ds:[edi+0/4/8/12], ebx/edx/ecx/ebp +// EBX = 0 +// EDX = 1 +// ECX = 2 +// EBP = 3 +unsigned char nfhpk_mov8[1024]; + +// mov eax, ebx/edx/ecx/ebp +// EBX = 0 +// EDX = 1 +// ECX = 2 +// EBP = 3 +unsigned char nfhpk_mov4[1024]; + +class initme +{ +public: + initme() + { + int x,y; + int m4,m3,m2,m1; + signed char *ptr; + unsigned char *uptr; + + // Do nfhpk_mov4l + uptr = nfhpk_mov4l; + for(m4=0;m4<2;m4++) + for(m3=0;m3<2;m3++) + for(m2=0;m2<2;m2++) + for(m1=0;m1<2;m1++) + { + *uptr = m1; uptr++; + *uptr = m2; uptr++; + *uptr = m3; uptr++; + *uptr = m4; uptr++; + } + + // Do nfhpk_mov8 + uptr = nfhpk_mov8; + for(m4=0;m4<4;m4++) + for(m3=0;m3<4;m3++) + for(m2=0;m2<4;m2++) + for(m1=0;m1<4;m1++) + { + *uptr = m1; uptr++; + *uptr = m2; uptr++; + *uptr = m3; uptr++; + *uptr = m4; uptr++; + } + + // Do nfhpk_mov4 + uptr = nfhpk_mov4; + for(m4=0;m4<4;m4++) + for(m3=0;m3<4;m3++) + for(m2=0;m2<4;m2++) + for(m1=0;m1<4;m1++) + { + *uptr = m1; uptr++; + *uptr = m2; uptr++; + *uptr = m3; uptr++; + *uptr = m4; uptr++; + } + // do nfpk_ShiftP1 + ptr = (signed char *)nfpk_ShiftP1; + + for(y=-8;y!=8;y++) + { + for(x=-8;x!=8;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + *(ptr) = y; + *(ptr+1) = x; + #else + *(ptr) = x; + *(ptr+1) = y; + #endif + ptr+=2; + } + } + + // do nfpk_ShiftP2[] + ptr = (signed char *)nfpk_ShiftP2; + + for(y=0;y!=8;y++) + { + for(x=8;x!=15;x++) + { + #ifdef OUTRAGE_BIG_ENDIAN + *(ptr) = y; + *(ptr+1) = x; + #else + *(ptr) = x; + *(ptr+1) = y; + #endif + ptr+=2; + } + } + + for(y=8;y!=14;y++) + { + for(x=-14;x!=0;x++) + { + *(ptr) = x; + *(ptr+1) = y; + ptr+=2; + } + + for(x=0;x!=15;x++) + { + *(ptr) = x; + *(ptr+1) = y; + ptr+=2; + } + } + for(x=-14;x!=0;x++) + { + *(ptr) = x; + *(ptr+1) = 14; + ptr+=2; + } + for(x=0;x!=12;x++) + { + *(ptr) = x; + *(ptr+1) = 14; + ptr+=2; + } + } +}; + +initme _initme; + +// nfPkConfig initializes tables used by nfPkDecomp +// which are dependent on screen size. +void nfPkConfig(void) +{ + // Build ShiftY table + int i,val,index; + + val = 0; + index = 0; + + for(i=0;i<128;i++) + { + nfpk_ShiftY[index] = val; + index++; + val += nf_width; + } + + val = nf_width; + val = val<<7; + val = -val; + + for(i=0;i<128;i++) + { + nfpk_ShiftY[index] = val; + index++; + val += nf_width; + } +} + +extern unsigned short nf_trans16_lo[256]; +extern unsigned short nf_trans16_hi[256]; + +// NOTE: EAX is destroyed after this call (actually the value of +// nf_trans16_hi[idx+1] +void Trans16(unsigned short *dst,unsigned short *idx,bool mask) +{ + *dst = nf_trans16_lo[*idx]; + *dst |= nf_trans16_hi[*(idx+1)]; +} + +// HiColor version +// +void nfHPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + tNextFrame nf; + nf.w = w; + nf.h = h; + nf.x = x; + nf.y = y; + + int nfpk_back_right,wcnt; + unsigned char *bcomp; + unsigned char *esi,*edi; + unsigned char opcode; + + + NF_DECOMP_INIT(1,&nf); + + nfpk_back_right = nf_back_right - SWIDTH*2; + + esi = comp; + edi = nf.tbuf; + + unsigned short swapped = (*(unsigned short *)esi); + swapped = INTEL_SHORT(swapped); + bcomp = swapped + esi; + esi = esi + 2; + + wcnt = w>>1; + + do{ + wcnt--; + if(wcnt>=0) + { + opcode = *ops;//al == opcode + ops++; + + bool first_opcode = true; + int opcode_to_use = opcode&0xF; + +do_next_opcode: + + switch(opcode_to_use) + { + case 0: + { + // No change from previous buffer + call_hnfshift(nf.DiffBufPtrs,&edi,&esi,nfpk_back_right); + }break; + case 1: + { + // No change (and copied to screen) (0) + edi += SWIDTH*2; + }break; + case 2: + { + unsigned int eax; + eax = *bcomp; + bcomp++; + eax = nfpk_ShiftP2[eax]; + + call_hnfxycshift(eax,&edi,&esi,nfpk_back_right); + + }break; + case 3: + { + // Near shift from newer part of current buffer + #ifdef OUTRAGE_BIG_ENDIAN + typedef struct { unsigned short hax,ax;}reg_word; + typedef struct { signed char hah,hal,ah,al;}reg_byte; + #else + typedef struct { unsigned short ax,hax;}reg_word; + typedef struct { signed char al,ah,hal,hah;}reg_byte; + #endif + union + { + unsigned int eax; + reg_word word; + reg_byte byte; + }myeax; + + myeax.eax = *bcomp; + bcomp++; + + myeax.word.ax = nfpk_ShiftP2[myeax.eax]; + myeax.byte.al = -myeax.byte.al; + myeax.byte.ah = -myeax.byte.ah; + + call_hnfxycshift(myeax.eax,&edi,&esi,nfpk_back_right); + + }break; + case 4: + { + // Near shift from previous buffer + #ifdef OUTRAGE_BIG_ENDIAN + typedef struct { unsigned short hax,ax;}reg_word; + typedef struct { signed char hah,hal,ah,al;}reg_byte; + #else + typedef struct { unsigned short ax,hax;}reg_word; + typedef struct { signed char al,ah,hal,hah;}reg_byte; + #endif + union + { + unsigned int eax; + reg_word word; + reg_byte byte; + }myeax; + + myeax.eax = 0; + myeax.byte.al = *bcomp; + bcomp++; + + myeax.word.ax = nfpk_ShiftP1[myeax.eax]; + call_hnfxypshift(myeax.eax,&edi,&esi,nfpk_back_right,nf.DiffBufPtrs); + + }break; + case 5: + { + unsigned short swapper = *(unsigned short *)(esi); + unsigned int eax = INTEL_SHORT(swapper); + esi += 2; + call_hnfxypshift(eax,&edi,&esi,nfpk_back_right,nf.DiffBufPtrs); + }break; + case 6: + { + // Far shift from current buffer + unsigned int val1,val2; + + unsigned short swapper = *(unsigned short *)(esi); + val1 = INTEL_SHORT(swapper); + esi += 2; + + val2 = ((val1&0xFF00)>>8); + + // sign extend al into eax, and multiply by two + if(val1&0x80) + { + val1 = ((val1&0x7F)<<1); + val1 = (val1&0xFF) | 0xFFFFFF00; + }else + { + val1 = ((val1&0x7F)<<1); + } + + val1 += nfpk_ShiftY[val2]; + + call_hnfshift(val1,&edi,&esi,nfpk_back_right); + + }break; + case 7: + { + bool donf23 = false; + unsigned short val; + int rep_count,max_repcount; + + val = *(unsigned short *)esi; + val = INTEL_SHORT(val); + if(val&0x8000) + { + donf23 = true; + } + + if(!donf23) + { + unsigned int colors[4]; + max_repcount = 8; + + unsigned int temp_color; + temp_color = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + temp_color = (nf_trans16_lo[*(esi+0)] | nf_trans16_hi[*(esi+1)]) | (temp_color<<16); + + colors[2] = temp_color; + temp_color = ((temp_color&0xFFFF0000)>>16) | ((temp_color&0xFFFF)<<16); + colors[0] = (temp_color&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = temp_color; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + if(rep_count!=(max_repcount-1)) + edi += nf_width; + } + + esi += 12; + edi -= nfpk_back_right; + }else + { + unsigned int colors[2]; + max_repcount = 4; + + unsigned int temp; + temp = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[0] = (temp<<16) | temp; + temp = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[1] = (temp<<16) | temp; + + for(rep_count=0;rep_count>4); break; + case 2: idx = (*(esi+5))&0xF; break; + case 3: idx = ((*(esi+5))>>4); break; + } + + color_idx = *(unsigned int *)( nfhpk_mov4l + (idx*4) ); + w1 = colors[color_idx&0x000000FF]; + w2 = colors[(color_idx&0x0000FF00)>>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + nf_width) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + nf_width+4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + nf_width+8) = w3; + *(unsigned int *)(edi + 12) = w4; + *(unsigned int *)(edi + nf_width+12) = w4; + if(rep_count!=(max_repcount-1)) + edi = edi + nf_width*2; + } + edi += nf_width; + edi -= nfpk_back_right; + esi += 6; + } + }break; + case 8: + { + bool donf24 = false; + bool donf40 = false; + unsigned short val; + int rep_count,max_repcount; + + val = *(unsigned short *)esi; + val = INTEL_SHORT(val); + if(val&0x8000) + { + val = *(unsigned short *)(esi+8); + val = INTEL_SHORT(val); + if(val&0x8000) + { + donf40 = true; + }else + { + donf24 = true; + } + } + + if(!donf24 && !donf40) + { + unsigned int colors[4]; + max_repcount = 8; + + unsigned int tempcolor; + tempcolor = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+0)] | nf_trans16_hi[*(esi+1)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + edi += nf_width; + *(unsigned int *)(edi + 0) = w3; + *(unsigned int *)(edi + 4) = w4; + if(rep_count!=(max_repcount-1)) + edi += nf_width; + + if(rep_count==1) + { + tempcolor = nf_trans16_lo[*(esi+8)] | nf_trans16_hi[*(esi+9)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + + if(rep_count==3) + { + edi -= nf_width*8-8; + + tempcolor = nf_trans16_lo[*(esi+14)] | nf_trans16_hi[*(esi+15)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+12)] | nf_trans16_hi[*(esi+13)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + + if(rep_count==5) + { + tempcolor = nf_trans16_lo[*(esi+20)] | nf_trans16_hi[*(esi+21)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+18)] | nf_trans16_hi[*(esi+19)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + } + + esi += 24; + edi -= (8+nfpk_back_right); + + } + + if(donf24) + { + unsigned int colors[4]; + max_repcount = 8; + + unsigned int tempcolor; + tempcolor = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+0)] | nf_trans16_hi[*(esi+1)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + edi += nf_width; + *(unsigned int *)(edi + 0) = w3; + *(unsigned int *)(edi + 4) = w4; + if(rep_count!=(max_repcount-1)) + edi += nf_width; + + if(rep_count==3) + { + edi -= nf_width*8 - 8; + + tempcolor = nf_trans16_lo[*(esi+10)] | nf_trans16_hi[*(esi+11)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+8)] | nf_trans16_hi[*(esi+9)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + } + + esi += 16; + edi -= (8+nfpk_back_right); + + } + + if(donf40) + { + unsigned int colors[4]; + max_repcount = 8; + + unsigned int tempcolor; + tempcolor = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+0)] | nf_trans16_hi[*(esi+1)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + if(rep_count!=(max_repcount-1)) + edi += nf_width; + + if(rep_count==3) + { + tempcolor = nf_trans16_lo[*(esi+10)] | nf_trans16_hi[*(esi+11)]; + tempcolor = (tempcolor<<16) | (nf_trans16_lo[*(esi+8)] | nf_trans16_hi[*(esi+9)]); + colors[2] = tempcolor; + tempcolor = ((tempcolor&0xFFFF0000)>>16) | ((tempcolor&0xFFFF)<<16); + colors[0] = (tempcolor&0xFFFF0000) | (colors[2]&0xFFFF); + colors[1] = tempcolor; + colors[3] = (colors[2]&0xFFFF0000) | (colors[1]&0xFFFF); + } + } + + esi += 16; + edi -= nfpk_back_right; + } + }break; + case 9: + { + bool donf41 = false; + bool donf25 = false; + bool donf57 = false; + unsigned short val; + int rep_count,max_repcount; + + val = *(unsigned short *)esi; + val = INTEL_SHORT(val); + if(val&0x8000) + { + val = *(unsigned short *)(esi+4); + val = INTEL_SHORT(val); + if(val&0x8000) + { + donf57 = true; + }else + { + donf41 = true; + } + + }else + { + val = *(unsigned short *)(esi+4); + val = INTEL_SHORT(val); + if(val&0x8000) + { + donf25 = true; + } + } + + if(donf57) + { + unsigned short colors[4]; + max_repcount = 8; + + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + if(rep_count%2) + { + *(unsigned int *)(edi + 8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + + *(unsigned int *)(edi + 12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + + if(rep_count!=(max_repcount-1)) + edi = edi + nf_width*2; + }else + { + *(unsigned int *)(edi + 0) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + + *(unsigned int *)(edi + 4) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + *(unsigned int *)(edi + nf_width + 4) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + } + } + + edi += nf_width; + esi += 16; + edi -= nfpk_back_right; + } + + if(donf41) + { + unsigned int colors[4]; + max_repcount = 8; + + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[0] = ((colors[0])<<16) | colors[0]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[1] = ((colors[1])<<16) | colors[1]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[2] = ((colors[2])<<16) | colors[2]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + colors[3] = ((colors[3])<<16) | colors[3]; + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + + if(rep_count!=(max_repcount-1)) + edi += nf_width; + } + + esi += 16; + edi -= nfpk_back_right; + } + + if(donf25) + { + unsigned int colors[4]; + max_repcount = 4; + + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[0] = ((colors[0])<<16) | colors[0]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[1] = ((colors[1])<<16) | colors[1]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[2] = ((colors[2])<<16) | colors[2]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + colors[3] = ((colors[3])<<16) | colors[3]; + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + *(unsigned int *)(edi + 0) = w1; + *(unsigned int *)(edi + nf_width) = w1; + *(unsigned int *)(edi + 4) = w2; + *(unsigned int *)(edi + nf_width + 4) = w2; + *(unsigned int *)(edi + 8) = w3; + *(unsigned int *)(edi + nf_width + 8) = w3; + *(unsigned int *)(edi + 12) = w4; + *(unsigned int *)(edi + nf_width + 12) = w4; + + if(rep_count!=(max_repcount-1)) + edi = edi + nf_width*2; + } + + edi += nf_width; + esi += 12; + edi -= nfpk_back_right; + } + + if(!donf25 && !donf41 && !donf57) + { + unsigned short colors[4]; + max_repcount = 16; + + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + + for(rep_count=0;rep_count>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + if(rep_count%2) + { + *(unsigned int *)(edi + 8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi + 12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + + if(rep_count!=(max_repcount-1)) + edi += nf_width; + }else + { + *(unsigned int *)(edi + 0) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi + 4) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + } + } + + esi += 24; + edi -= nfpk_back_right; + } + }break; + case 10: + { + // 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + int val1; + int rep_count; + unsigned short colors[4]; + bool do26 = false; + bool do42 = false; + + unsigned short swapper = *(unsigned short *)esi; + val1 = INTEL_SHORT(swapper); + if(val1&0x8000) + { + swapper = *(unsigned short *)(esi+16); + val1 = INTEL_SHORT(swapper); + if(val1&0x8000) + { + do42 = true; + }else + { + do26 = true; + } + } + + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + colors[1] = nf_trans16_lo[*(esi+2)] | nf_trans16_hi[*(esi+3)]; + colors[2] = nf_trans16_lo[*(esi+4)] | nf_trans16_hi[*(esi+5)]; + colors[3] = nf_trans16_lo[*(esi+6)] | nf_trans16_hi[*(esi+7)]; + + for(rep_count=0;rep_count<16;rep_count++) + { + unsigned int w1,w2,w3,w4; + unsigned int color_idx; + int idx; + + if(!do26 && !do42) + { + switch(rep_count) + { + case 0: idx = 8; break; + case 1: idx = 9; break; + case 2: idx = 10; break; + case 3: idx = 11; break; + case 4: idx = 20; break; + case 5: idx = 21; break; + case 6: idx = 22; break; + case 7: idx = 23; break; + case 8: idx = 32; break; + case 9: idx = 33; break; + case 10: idx = 34; break; + case 11: idx = 35; break; + case 12: idx = 44; break; + case 13: idx = 45; break; + case 14: idx = 46; break; + case 15: idx = 47; break; + } + }else + { + switch(rep_count) + { + case 0: idx = 8; break; + case 1: idx = 9; break; + case 2: idx = 10; break; + case 3: idx = 11; break; + case 4: idx = 12; break; + case 5: idx = 13; break; + case 6: idx = 14; break; + case 7: idx = 15; break; + case 8: idx = 24; break; + case 9: idx = 25; break; + case 10: idx = 26; break; + case 11: idx = 27; break; + case 12: idx = 28; break; + case 13: idx = 29; break; + case 14: idx = 30; break; + case 15: idx = 31; break; + } + + } + color_idx = *(unsigned int *)( nfhpk_mov4+(*(esi+idx)*4) ); + w1 = colors[color_idx&0x000000FF]; + w2 = colors[(color_idx&0x0000FF00)>>8]; + w3 = colors[(color_idx&0x00FF0000)>>16]; + w4 = colors[(color_idx&0xFF000000)>>24]; + + if(!do42) + { + *(unsigned int *)(edi+0) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi+4) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + + if(rep_count!=15) + edi += nf_width; + }else + { + if(rep_count%2) + { + *(unsigned int *)(edi+8) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi+12) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + + if(rep_count!=15) + edi += nf_width; + }else + { + *(unsigned int *)(edi+0) = (w1&0xFFFF)|((w2&0xFFFF)<<16); + *(unsigned int *)(edi+4) = (w3&0xFFFF)|((w4&0xFFFF)<<16); + } + } + + if(rep_count==3 && !do42 && !do26) + { + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+12)] | nf_trans16_hi[*(esi+13)]; + colors[1] = nf_trans16_lo[*(esi+14)] | nf_trans16_hi[*(esi+15)]; + colors[2] = nf_trans16_lo[*(esi+16)] | nf_trans16_hi[*(esi+17)]; + colors[3] = nf_trans16_lo[*(esi+18)] | nf_trans16_hi[*(esi+19)]; + } + + if(rep_count==7) + { + if(!do42 && !do26) + { + edi -= nf_width*8-8; + + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+24)] | nf_trans16_hi[*(esi+25)]; + colors[1] = nf_trans16_lo[*(esi+26)] | nf_trans16_hi[*(esi+27)]; + colors[2] = nf_trans16_lo[*(esi+28)] | nf_trans16_hi[*(esi+29)]; + colors[3] = nf_trans16_lo[*(esi+30)] | nf_trans16_hi[*(esi+31)]; + } + + if(do26) + { + edi -= nf_width*8-8; + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+16)] | nf_trans16_hi[*(esi+17)]; + colors[1] = nf_trans16_lo[*(esi+18)] | nf_trans16_hi[*(esi+19)]; + colors[2] = nf_trans16_lo[*(esi+20)] | nf_trans16_hi[*(esi+21)]; + colors[3] = nf_trans16_lo[*(esi+22)] | nf_trans16_hi[*(esi+23)]; + } + + if(do42) + { + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+16)] | nf_trans16_hi[*(esi+17)]; + colors[1] = nf_trans16_lo[*(esi+18)] | nf_trans16_hi[*(esi+19)]; + colors[2] = nf_trans16_lo[*(esi+20)] | nf_trans16_hi[*(esi+21)]; + colors[3] = nf_trans16_lo[*(esi+22)] | nf_trans16_hi[*(esi+23)]; + } + } + + if(rep_count==11 && !do42 && !do26) + { + // Load bx,dx,cx,bp with four colors + colors[0] = nf_trans16_lo[*(esi+36)] | nf_trans16_hi[*(esi+37)]; + colors[1] = nf_trans16_lo[*(esi+38)] | nf_trans16_hi[*(esi+39)]; + colors[2] = nf_trans16_lo[*(esi+40)] | nf_trans16_hi[*(esi+41)]; + colors[3] = nf_trans16_lo[*(esi+42)] | nf_trans16_hi[*(esi+43)]; + } + } + + if(!do42 && !do26) + { + esi += 48; + }else + { + esi += 32; + } + + if(!do42) + { + edi -= (8+nfpk_back_right); + }else + { + edi-=nfpk_back_right; + } + + }break; + + case 11: + { + //8x8x16 (128 bytes) + Trans16Blk(edi,esi); + edi += nf_width; + + Trans16Blk(edi,esi+16); + edi += nf_width; + + Trans16Blk(edi,esi+32); + edi += nf_width; + + Trans16Blk(edi,esi+48); + edi += nf_width; + + Trans16Blk(edi,esi+64); + edi += nf_width; + + Trans16Blk(edi,esi+80); + edi += nf_width; + + Trans16Blk(edi,esi+96); + edi += nf_width; + + Trans16Blk(edi,esi+112); + + esi += 128; + edi -= nfpk_back_right; + }break; + case 12: + { + // low 4x4x16 (32 bytes) + int i; + unsigned int eax,ebx; + + for(i=0;i<4;i++) + { + eax = *(unsigned char *)(esi+i*8+0); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+1); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+0) = eax; + *(unsigned int *)(edi+nf_width) = eax; + + eax = *(unsigned char *)(esi+i*8+2); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+3); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+4) = eax; + *(unsigned int *)(edi+nf_width+4) = eax; + + eax = *(unsigned char *)(esi+i*8+4); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+5); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+8) = eax; + *(unsigned int *)(edi+nf_width+8) = eax; + + eax = *(unsigned char *)(esi+i*8+6); + ebx = nf_trans16_lo[eax]; + eax = *(unsigned char *)(esi+i*8+7); + ebx |= nf_trans16_hi[eax]; + eax = ((ebx&0xFFFF)<<16) | (ebx&0xFFFF); + *(unsigned int *)(edi+12) = eax; + *(unsigned int *)(edi+nf_width+12) = eax; + + if(i!=3) + edi = edi + nf_width*2; + } + + edi += nf_width; + edi -= nfpk_back_right; + esi += 32; + }break; + case 13: + { + // 2x2 4x4x0 (8 bytes) + unsigned int temp,ebx,ecx; + + temp = nf_trans16_lo[(*esi)] | nf_trans16_hi[(*(esi+1))]; + ebx = ((temp&0xFFFF)<<16) | (temp&0xFFFF); + + temp = nf_trans16_lo[(*(esi+2))] | nf_trans16_hi[(*(esi+3))]; + ecx = ((temp&0xFFFF)<<16) | (temp&0xFFFF); + + *(unsigned int *)(edi + 0) = ebx; + *(unsigned int *)(edi + 4) = ebx; + *(unsigned int *)(edi + 8) = ecx; + *(unsigned int *)(edi + 12) = ecx; + *(unsigned int *)(edi + nf_width) = ebx; + *(unsigned int *)(edi + nf_width+4) = ebx; + *(unsigned int *)(edi + nf_width+8) = ecx; + *(unsigned int *)(edi + nf_width+12) = ecx; + + edi = edi + nf_width*2; + + *(unsigned int *)(edi + 0) = ebx; + *(unsigned int *)(edi + 4) = ebx; + *(unsigned int *)(edi + 8) = ecx; + *(unsigned int *)(edi + 12) = ecx; + *(unsigned int *)(edi + nf_width) = ebx; + *(unsigned int *)(edi + nf_width+4) = ebx; + *(unsigned int *)(edi + nf_width+8) = ecx; + *(unsigned int *)(edi + nf_width+12) = ecx; + + edi = edi + nf_width*2; + + temp = nf_trans16_lo[(*(esi+4))] | nf_trans16_hi[(*(esi+5))]; + ebx = ((temp&0xFFFF)<<16) | (temp&0xFFFF); + + temp = nf_trans16_lo[(*(esi+6))] | nf_trans16_hi[(*(esi+7))]; + ecx = ((temp&0xFFFF)<<16) | (temp&0xFFFF); + + *(unsigned int *)(edi + 0) = ebx; + *(unsigned int *)(edi + 4) = ebx; + *(unsigned int *)(edi + 8) = ecx; + *(unsigned int *)(edi + 12) = ecx; + *(unsigned int *)(edi + nf_width) = ebx; + *(unsigned int *)(edi + nf_width+4) = ebx; + *(unsigned int *)(edi + nf_width+8) = ecx; + *(unsigned int *)(edi + nf_width+12) = ecx; + + edi = edi + nf_width*2; + + *(unsigned int *)(edi + 0) = ebx; + *(unsigned int *)(edi + 4) = ebx; + *(unsigned int *)(edi + 8) = ecx; + *(unsigned int *)(edi + 12) = ecx; + *(unsigned int *)(edi + nf_width) = ebx; + *(unsigned int *)(edi + nf_width+4) = ebx; + *(unsigned int *)(edi + nf_width+8) = ecx; + *(unsigned int *)(edi + nf_width+12) = ecx; + + edi += nf_width; + edi -= nfpk_back_right; + esi += 8; + }break; + case 14: + { + // 8x8x0 (2 bytes) + unsigned int ecx,ebx; + ecx = nf_trans16_lo[*(esi)] | nf_trans16_hi[*(esi+1)]; + esi += 2; + ebx = ((ecx&0xFFFF)<<16) | (ecx&0xFFFF); + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + edi += nf_width; + + *(unsigned int *)(edi + 0 ) = ebx; + *(unsigned int *)(edi + 4 ) = ebx; + *(unsigned int *)(edi + 8 ) = ebx; + *(unsigned int *)(edi + 12 ) = ebx; + + edi -= nfpk_back_right; + }break; + case 15: + { + int i; + i = 0; + }break; + } + + if(first_opcode) + { + first_opcode = false; + opcode_to_use = (opcode>>4); + goto do_next_opcode; + } + + // go back up + continue; + }else + { + edi += nf.new_row; + h--; + wcnt = w>>1; + } + }while(h!=0); +} + +void Trans16Blk(unsigned char *edi,unsigned char *idx) +{ + *((unsigned short *)(edi+0)) = nf_trans16_lo[*(idx+0)] | nf_trans16_hi[*(idx+1)]; + *((unsigned short *)(edi+2)) = nf_trans16_lo[*(idx+2)] | nf_trans16_hi[*(idx+3)]; + *((unsigned short *)(edi+4)) = nf_trans16_lo[*(idx+4)] | nf_trans16_hi[*(idx+5)]; + *((unsigned short *)(edi+6)) = nf_trans16_lo[*(idx+6)] | nf_trans16_hi[*(idx+7)]; + *((unsigned short *)(edi+8)) = nf_trans16_lo[*(idx+8)] | nf_trans16_hi[*(idx+9)]; + *((unsigned short *)(edi+10)) = nf_trans16_lo[*(idx+10)] | nf_trans16_hi[*(idx+11)]; + *((unsigned short *)(edi+12)) = nf_trans16_lo[*(idx+12)] | nf_trans16_hi[*(idx+13)]; + *((unsigned short *)(edi+14)) = nf_trans16_lo[*(idx+14)] | nf_trans16_hi[*(idx+15)]; +} + + +void call_hnfxycshift(unsigned int eax,unsigned char **medi,unsigned char **mesi,int nfpk_back_right) +{ + unsigned int ebx; + ebx = ((eax&0xFF00)>>8); + + if(eax&0x80) + { + // we have to sign extend also + eax = ((eax&0x7F)<<1); + eax = (eax&0xFF) | 0xFFFFFF00; + }else + { + eax = ((eax&0x7F)<<1); + } + eax += nfpk_ShiftY[ebx]; + + call_hnfshift(eax,medi,mesi,nfpk_back_right); +} + +void call_hnfxypshift(unsigned int eax,unsigned char **medi,unsigned char **mesi,int nfpk_back_right,int DiffBufPtrs) +{ + unsigned int ebx; + ebx = ((eax&0xFF00)>>8); + + if(eax&0x80) + { + // we have to sign extend also + eax = ((eax&0x7F)<<1); + eax = (eax&0xFF) | 0xFFFFFF00; + }else + { + eax = ((eax&0x7F)<<1); + } + eax += nfpk_ShiftY[ebx]; + eax += DiffBufPtrs; + + call_hnfshift(eax,medi,mesi,nfpk_back_right); +} + +void call_hnfshift(unsigned int meax,unsigned char **medi,unsigned char **mesi,int nfpk_back_right) +{ + unsigned char *esi,*edi,*saved_esi; + int i; + + edi = *medi; + saved_esi = esi = *mesi; + esi = edi + meax; + + for(i=0;i<8;i++) + { + #define HNFSHIFT_WRITEOUT(x) \ + *(unsigned int *)(edi + x) = *(unsigned int *)(esi + x); + + HNFSHIFT_WRITEOUT(0); + HNFSHIFT_WRITEOUT(4); + HNFSHIFT_WRITEOUT(8); + HNFSHIFT_WRITEOUT(12); + + #undef HNFSHIFT_WRITEOUT + + if(i!=7) + { + esi += nf_width; + edi += nf_width; + } + } + + edi -= nfpk_back_right; + esi = saved_esi; + + *medi = edi; + *mesi = esi; +} + +//////////////////////////////////////////////// +// Non-Implemented Functions +//////////////////////////////////////////////// +void nfHiColorDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfHiColorDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfPkPal(void); +void nfPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void nfPkDecompH(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void nfPkDecompD(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h); +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field); +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field); +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty); +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty); +void mve_sfPkShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty); +void mve_sfPkHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty); + +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); +void palLoadCompPalette(unsigned char *buf); +void gfxMode(unsigned mode); +void gfxLoadCrtc(unsigned char *crtc,unsigned char chain4,unsigned char res); +void gfxGetCrtc(unsigned char *crtc); +void gfxVres(unsigned char misc,unsigned char *crtc); +void MVE_gfxWaitRetrace(int state); +void MVE_gfxSetSplit(unsigned line); + +// rcg07272000 +// need this on non-Intel platforms. Intel uses int $3. +#if (defined __i386__) +#define int3() __asm__ __volatile__ ( "int $3" ) +#else +#define int3() raise(SIGTRAP) +#endif + +void nfHiColorDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3(); +} +void nfHiColorDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3(); +} +void nfDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3(); +} +void nfDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h) +{ + int3(); +} +void nfPkPal(void) +{ + int3(); +} +void nfPkDecomp(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3(); +} +void nfPkDecompH(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3(); +} +void nfPkDecompD(unsigned char *ops,unsigned char *comp,unsigned x,unsigned y,unsigned w,unsigned h) +{ + int3(); +} +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field) +{ + int3(); +} +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h,unsigned dstx, unsigned dsty, unsigned field) +{ + int3(); +} +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty) +{ + int3(); +} +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned short *chgs,unsigned dstx, unsigned dsty) +{ + int3(); +} +void mve_sfPkShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty) +{ + int3(); +} +void mve_sfPkHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h,unsigned char *ops,unsigned dstx, unsigned dsty) +{ + int3(); +} +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count) +{ + int3(); +} +void palLoadCompPalette(unsigned char *buf) +{ + int3(); +} +void gfxMode(unsigned mode) +{ + int3(); +} +void gfxLoadCrtc(unsigned char *crtc,unsigned char chain4,unsigned char res) +{ + int3(); +} +void gfxGetCrtc(unsigned char *crtc) +{ + int3(); +} +void gfxVres(unsigned char misc,unsigned char *crtc) +{ + int3(); +} +void MVE_gfxWaitRetrace(int state) +{ + int3(); +} +void MVE_gfxSetSplit(unsigned line) +{ + int3(); +} diff --git a/lnxmvelib/mvegfx.h b/lnxmvelib/mvegfx.h new file mode 100644 index 000000000..17a626575 --- /dev/null +++ b/lnxmvelib/mvegfx.h @@ -0,0 +1,32 @@ +#define MVE_GFX_VESA_640_400 0x100 +#define MVE_GFX_VESA_640_200 0x8100 +#define MVE_GFX_VESA_640_480 0x101 +#define MVE_GFX_VESA_640_240 0x8101 +#define MVE_GFX_VESA_800_600 0x103 +#define MVE_GFX_VESA_800_300 0x8103 +#define MVE_GFX_VESA_1024_768 0x105 +#define MVE_GFX_VESA_1024_384 0x8105 +#define MVE_GFX_VESA_1280_1024 0x107 +#define MVE_GFX_VESA_1280_512 0x8107 + +#define MVE_GFX_VESA_320_200_HI 0x10d /* 1:5:5:5 Hi Color modes */ +#define MVE_GFX_VESA_640_480_HI 0x110 +#define MVE_GFX_VESA_800_600_HI 0x113 +#define MVE_GFX_VESA_1024_768_HI 0x116 +#define MVE_GFX_VESA_1280_1024_HI 0x119 + +/* Some hacked vesa modes */ +#define MVE_GFX_VESA_640_350 0x0F00 /* 640x350 */ +#define MVE_GFX_VESA_640_175 0x8F00 /* 640x175 */ +#define MVE_GFX_VESA_640_160 0x0F01 /* 640x160 */ + +#define MVE_GFX_VESA_CURRENT 0 /* Use current VESA mode */ + +#define MVE_GFX_AUTO (-1) /* Choose mode based on movie's preference */ +#define MVE_GFX_VGA 0x13 /* 320x200 */ +#define MVE_GFX_VGA_CURRENT (-2) /* 320x200 (mode already set) */ +#define MVE_GFX_VGA_MEDRES (-3) /* 248x264 out of 320x350 */ +#define MVE_GFX_VGA_HIRES (-4) /* 224x288 out of 360x480 */ +#define MVE_GFX_VGA_LORES (-5) /* 288x224 out of 320x240 */ +#define MVE_GFX_VESA_320_480 (-6)/* 320x480 */ + diff --git a/lnxmvelib/mvelibi.h b/lnxmvelib/mvelibi.h new file mode 100644 index 000000000..855518040 --- /dev/null +++ b/lnxmvelib/mvelibi.h @@ -0,0 +1,231 @@ +#if __SC__ +#pragma SC align 1 +#elif __WATCOMC__ +#pragma pack(1); +#elif _MSC_VER +#pragma pack(1) +#else +//??? +#pragma pack(push, 1) +#endif + +#include "byteswap.h" + +//-------------------------------- +// Useful type definitions +//-------------------------------- + +#ifndef __cplusplus +typedef unsigned bool; +#endif +#define FALSE 0 +#define TRUE 1 + +//-------------------------------- +// 386 Assembler Support +//-------------------------------- + +#define OPERAND32 __asm _emit 0x66 +#define SEG_ES __asm _emit 0x26 +#define SEG_FS __asm _emit 0x64 +#define SEG_GS __asm _emit 0x65 +#define MOVZX_BP_IND_BX __asm _emit 0x0F __asm _emit 0xB6 __asm _emit 0x2F +#define MOV_FS_AX __asm _emit 0x8e __asm _emit 0xE0 +#define MOV_GS_AX __asm _emit 0x8e __asm _emit 0xE8 + +//-------------------------------- +// Compressed Video Constants +//-------------------------------- + +// Width and height of sections in pixels. +#define SWIDTH 8 +#define SHEIGHT 8 + +#define LOG2_SWIDTH 3 +#define LOG2_SHEIGHT 3 + +//------------------------------ +// Movie File Header +//------------------------------ + +#define MVE_FILE_TYPE "Interplay MVE File\x1A\0" +#define MVE_FILE_VERSION 0x0100 + +typedef struct _mve_hdr { + char FileType[20]; // MVE_FILE_TYPE + unsigned short HdrSize; // sizeof(mve_hdr) + unsigned short version; // MVE_FILE_VERSION + unsigned short id; // ~MVE_FILE_VERSION+0x1234 +} mve_hdr; + +//------------------------------ +// Movie File Records +//------------------------------ + +typedef struct _io_hdr { + unsigned short len; // Length of record data (pad to even) + unsigned short kind; // See IO_REC_xxx +// unsigned char data[0]; // Record data + } ioHdrRec; + +// This record classifications simplify utilities which must operate on +// records. They are not used by this library when running a movie. +// +#define IO_REC_SND_INIT 0 // Sound setup +#define IO_REC_SND_PRELOAD 1 // Sound preload +#define IO_REC_FRAME_INIT 2 // Frame (video) setup +#define IO_REC_FRAME 3 // Movie frames +#define IO_REC_END 4 // Last Record (End of Movie) +#define IO_REC_EOF 5 // Empty eof record at end of file. + +//------------------------------ +// Movie File Major Opcodes +//------------------------------ +// + +#define MCMD_DATA(arg) ( (unsigned char *) ((arg)+1) ) + +typedef struct _mcmd_hdr { + unsigned short len; // Length of data (pad to even) + unsigned char major; // Major opcode + unsigned char minor; // Minor opcode +// unsigned char data[0]; // Opcode data + } mcmd_hdr; + +#define mcmd_end 0 // End processing of movie + +#define mcmd_next 1 // Advance to next movie record + +#define mcmd_syncInit 2 +typedef struct _syncInit { + unsigned long period; // period of quanta + unsigned short wait_quanta; // # of quanta per frame + } marg_syncInit; + +#define mcmd_sndConfigure 3 +typedef struct _sndConfigure { + unsigned short rate; // 65536-(256E6/(frequency*(stereo+1))) + // comp16 is a minor opcode 1 field + // It indicates that 16-bit data has been compressed to 8-bits. + // When it is set, bits16 will also be set. + // Each record will contain initial 16-bit sample followed + // by remaining compressed 8-bit samples. + // For stereo, there will be two initial 16-bit samples. + // and compressed streams will be interleaved. + //unsigned short stereo:1, bits16:1, comp16:1; + #ifdef OUTRAGE_BIG_ENDIAN + unsigned char bitpadder:5; + #endif + unsigned char stereo:1, bits16:1, comp16:1; + unsigned char dummy1; + unsigned short frequency; + // Minor opcode 1 extends buflen to be a long + unsigned long buflen; + } marg_sndConfigure; + +#define mcmd_sndSync 4 + +#define mcmd_nfConfig 5 +typedef struct _nfConfig { + unsigned short wqty; + unsigned short hqty; + // Minor opcode 1 fields: + unsigned short fqty; + // Minor opcode 2 fields: + unsigned short hicolor; /*0=256-color, 1=HiColor, 2=HiColorSwapped*/ + } marg_nfConfig; + +#define mcmd_nfDecomp 6 +#define mcmd_nfDecompChg 16 +#define mcmd_nfPkDecomp 17 +typedef struct _nfDecomp { + unsigned short prev; // info:Prev frames+1 needed for full picture + unsigned short iframe; // info:Current internal frame # + unsigned short x; + unsigned short y; + unsigned short w; + unsigned short h; + //unsigned short advance:1; + #ifdef OUTRAGE_BIG_ENDIAN + unsigned char bitpadder:7; + #endif + unsigned char advance:1; + unsigned char dummy1; +// unsigned char comp[0]; + } marg_nfDecomp; + +#define mcmd_sfShowFrame 7 +#if 0 // Not supported +#define mcmd_sfPkShowFrameChg 18 +#endif +typedef struct _sfShowFrame { + unsigned short pal_start; + unsigned short pal_count; + // Minor opcode 1 fields: + unsigned short field; // 0:none, 2:send to even, 3:send to odd + } marg_sfShowFrame; + +#define mcmd_sndAdd 8 +#define mcmd_sndSilence 9 +typedef struct _sndAdd { + unsigned short iframe; //info: iframe # of sound + unsigned short TrackMask; + unsigned short qty; //Uncompressed audio size in bytes +// unsigned char data[0]; + } marg_sndAdd; + +#define mcmd_gfxMode 10 +typedef struct _gfxMode { + unsigned short minw; + unsigned short minh; + unsigned short mode; + } marg_gfxMode; + +#define mcmd_palMakeSynthPalette 11 +typedef struct _palMakeSynthPalette { + unsigned char base_r; + unsigned char range_r; + unsigned char range_rb; + unsigned char base_g; + unsigned char range_g; + unsigned char range_gb; + } marg_palMakeSynthPalette; + +#define mcmd_palLoadPalette 12 +typedef struct _palLoadPalette { + unsigned short start; + unsigned short count; +// unsigned char data[0]; + } marg_palLoadPalette; + +#define mcmd_palLoadCompPalette 13 + +#define mcmd_nfChanges 14 +#define mcmd_nfParms 15 +// 16 is used for mcmd_nfDecompChg, see above. +// 17 is used for mcmd_nfPkDecomp, see above. +// 18 is used for mcmd_nfPkShowFrameChg, see above + +#define mcmd_nfPkInfo 19 +#define mcmd_nfHPkInfo 20 +typedef struct _nfPkInfo { + unsigned long error; // scaled by 10000 + unsigned short usage[64]; + } marg_nfPkInfo; + +#define mcmd_idcode 21 +typedef struct _idcode { + unsigned long idcode; // Code identifying version mcomp used to create + } marg_idcode; + +#if __SC__ +#pragma SC align +#elif __WATCOMC__ +#pragma pack(); +#elif _MSC_VER +#pragma pack() +#else +//??? +#pragma pack(pop) +#endif + diff --git a/lnxmvelib/mvelibl.cpp b/lnxmvelib/mvelibl.cpp new file mode 100644 index 000000000..b51a719a0 --- /dev/null +++ b/lnxmvelib/mvelibl.cpp @@ -0,0 +1,2025 @@ +/* +** mvelibl.cpp +** +** Interplay Movie File (MVE) Player Library (32-Bit Linux Version) +** Written by Paul Allen Edelstein. Partial Linux port by Jeff Slutter. +** +** (c) 1997 Interplay Productions. All Rights Reserved. +** This file is confidential and consists of proprietary information +** of Interplay Productions. This file and associated libraries +** may not, in whole or in part, be disclosed to third parties, +** incorporated into any software product which is not being created +** for Interplay Productions, copied or duplicated in any form, +** without the prior written permission of Interplay Productions. +** Further, you may not reverse engineer, decompile or otherwise +** attempt to derive source code of this material. +** +*/ + +/* +** Linux Specific Notes: +** It uses SDL. We love it. Woohoo. None of this low-level X11 shit. --ryan. +*/ +static char notice1[] = +"(c) 1997 Interplay Productions. All Rights Reserved.\n" +"This file is confidential and consists of proprietary information\n" +"of Interplay Productions. This file and associated libraries\n" +"may not, in whole or in part, be disclosed to third parties,\n" +"incorporated into any software product which is not being created\n" +"for Interplay Productions, copied or duplicated in any form,\n" +"without the prior written permission of Interplay Productions.\n" +"Further, you may not reverse engineer, decompile or otherwise\n" +"attempt to derive source code of this material.\n"; + +#include +#include +#include +#include +#include + +#include "mvelibi.h" +#include "mvegfx.h" +#include "mvelibl.h" + +#include "byteswap.h" + +static unsigned opt_fastmode = 0; // 0:normal, 1:even lines only, 2:dither between even/odd lines |4 to spread lines + +unsigned opt_hscale_step = 4; // 3: 4/3, 4:4/4, 5:4/5 (not fully implemented) +unsigned opt_hscale_adj; + +#define logLabel(x) + +#include "snd8to16.h" +// len always specifies length of destination in bytes. +unsigned sndDecompM16(unsigned short *dst, unsigned char *src, unsigned len, unsigned state); +unsigned sndDecompS16(unsigned short *dst, unsigned char *src, unsigned len, unsigned state); + +static LnxWindow *mve_lpWin = NULL; + +unsigned int timeGetTime(void) +{ + struct timeval t; + gettimeofday(&t,NULL); + + return (t.tv_sec*1000 + t.tv_usec/1000); +} + +//---------------------------------------------------------------------- +// Memory Management +//-------------------- + +static void *(*mem_alloc)(unsigned size); +static void (*mem_free)(void *p); + +typedef struct _mem +{ + void *ptr; + unsigned size; + bool dynamic; +} MemRec, *MemPtr; + +static void MemInit(MemPtr m, unsigned size, void *p); +static void *MemAlloc(MemPtr m, unsigned size); +static void MemFree(MemPtr m); + +void MVE_memCallbacks(void *(*fn_alloc)(unsigned size),void (*fn_free)(void *p)) +{ + mem_alloc = fn_alloc; + mem_free = fn_free; +} + +static void MemFree(MemPtr m) +{ + if (m->dynamic && mem_free) + { + (*mem_free)(m->ptr); + m->dynamic = FALSE; // prevent from being freed again! + } + m->size = 0; +} + +static void MemInit(MemPtr m, unsigned size, void *p) +{ + if (!p) + return; + MemFree(m); + m->ptr = p; + m->size = size; + m->dynamic = FALSE; +} + +static void *MemAlloc(MemPtr m, unsigned size) +{ + if (size <= m->size) + return m->ptr; + if (mem_alloc) + { + void *p; + MemFree(m); + size += 100; // Add some pad to reduce chance of another realloc. + p = (*mem_alloc)(size); + if (!p) + return (void *)NULL; + MemInit(m, size, p); + m->dynamic = TRUE; + return m->ptr; + } + return (void *)NULL; +} + +//---------------------------------------------------------------------- +// Synchronization Management +//----------------------------- + +static bool sync_active = FALSE; +static int sync_time; +static int sync_wait_quanta; + +static bool sync_late = FALSE; +static bool sync_FrameDropped = FALSE; + +static void syncReset(unsigned long wait_quanta); +static void syncRelease(void); +static bool syncInit(unsigned long period, unsigned wait_quanta); +static bool syncWait(void); +static void syncSync(void); + +static void syncReset(unsigned long wait_quanta) +{ + sync_time = wait_quanta - timeGetTime()*1000; + sync_active = TRUE; +} + +static void syncRelease(void) +{ + sync_active = FALSE; +} + +static bool syncInit(unsigned long period, unsigned wait_quanta) +{ + int new_wait_quanta = -(long)(period*wait_quanta+(wait_quanta>>1)); + // If timer is still running and has same timing + // characteristics, assume we are trying to continue smoothly + // with another movie and ignore new syncInit() call. + if (sync_active && sync_wait_quanta==new_wait_quanta) + return TRUE; + + syncWait(); + sync_wait_quanta = new_wait_quanta; + syncReset(sync_wait_quanta); + return TRUE; +} + +// Returns true if actually waited, false if called too late to wait. +static bool syncWait(void) +{ + bool waited = FALSE; + if (!sync_active) + return FALSE; + while ((int)(sync_time+timeGetTime()*1000) < 0) + waited = TRUE; + sync_time += sync_wait_quanta; + return waited; +} + +// Returns true if actually waited, false if called too late to wait. +static int syncWaitLevel(int level) +{ + int waited; + if (!sync_active) + return 0; + level += sync_time; + for (;;) + { + waited = level+timeGetTime()*1000; + if (waited >= 0) + break; + } + sync_time += sync_wait_quanta; + return waited; +} + +static void syncSync(void) +{ + if (sync_active) + { + while ((int)(sync_time+timeGetTime()*1000) < 0); + } +} + +int MVE_syncTime(void) +{ + if (sync_active) + return sync_time + timeGetTime()*1000; + else + return 0; +} + +void MVE_logDumpStats(void) +{ +} + +//---------------------------------------------------------------------- +// I/O Management +//----------------- + +static unsigned (*io_read)(int handle, void *buf, unsigned count); + +static MemRec io_mem_buf; + +static int io_handle; +static ioHdrRec io_next_hdr; + +static bool ioReset(int h); +static unsigned char *ioRead(unsigned qty); +static unsigned char *ioNextRecord(void); +static void ioRelease(void); + + +void MVE_ioCallbacks(unsigned (*fn_read)(int handle, void *buf,unsigned count)) +{ + io_read = fn_read; +} + +static bool ioReset(int h) +{ + mve_hdr *hdr; + + io_handle = h; + hdr = (mve_hdr *)ioRead(sizeof(mve_hdr) + sizeof(ioHdrRec)); + if (!hdr) + return FALSE; + + hdr->HdrSize = INTEL_SHORT(hdr->HdrSize); + hdr->version = INTEL_SHORT(hdr->version); + hdr->id = INTEL_SHORT(hdr->id); + + if (strcmp(hdr->FileType, MVE_FILE_TYPE) != 0 || + hdr->id != ~hdr->version + 0x1234 || + // The following two checks may eventually be weakened. + hdr->version != MVE_FILE_VERSION || + hdr->HdrSize != sizeof(mve_hdr)) + return FALSE; + + io_next_hdr = *(ioHdrRec *)(hdr+1); + io_next_hdr.len = INTEL_SHORT(io_next_hdr.len); + io_next_hdr.kind = INTEL_SHORT(io_next_hdr.kind); + return TRUE; +} + +void MVE_memIO(void *p, unsigned size) +{ + MemInit(&io_mem_buf, size, p); +} + +static unsigned char *ioRead(unsigned len) +{ + unsigned char *buf; + + buf = (unsigned char *)MemAlloc(&io_mem_buf, len); + if (!buf) + return (unsigned char *)NULL; + if (!(*io_read)(io_handle, buf, len)) + return (unsigned char *)NULL; + return buf; +} + +static unsigned char *ioNextRecord(void) +{ + unsigned char *buf; + logLabel("StartRead"); + buf = ioRead(io_next_hdr.len + sizeof(ioHdrRec)); + logLabel("EndRead"); + if (!buf) + return (unsigned char *)NULL; + io_next_hdr = *(ioHdrRec *)(buf+io_next_hdr.len); + io_next_hdr.len = INTEL_SHORT(io_next_hdr.len); + io_next_hdr.kind = INTEL_SHORT(io_next_hdr.kind); + return buf; +} + +static void ioRelease(void) +{ + MemFree(&io_mem_buf); +} + + +//---------------------------------------------------------------------- +// Sound Management +//----------------------- + +#define SOUND_SUPPORT 1 + +#if SOUND_SUPPORT + +static LnxSoundDevice *snd_ds = NULL; +static LnxSoundBuffer *snd_buffer = NULL; +static LinuxSoundCaps snd_buffer_caps; +static unsigned int snd_write_cursor; + + +enum {snd_queue_max=60}; + +static struct _snd_queue +{ + unsigned ptr; +} snd_queue[snd_queue_max]; +static unsigned snd_fill, snd_empty; +static int snd_pad; + +static unsigned snd_stereo; +static unsigned snd_comp16; +static unsigned snd_bits16; + +static long snd_volume = 0; +static long snd_pan = 0; + +#endif + +void MVE_sndInit(LnxSoundDevice *lpDS) +{ +#if SOUND_SUPPORT + snd_ds = lpDS; +#endif +} + +void MVE_dsbSetVolume(long lVolume) +{ +#if SOUND_SUPPORT + snd_volume = lVolume; + if (snd_buffer) + LnxSoundBuffer_SetVolume(snd_buffer,snd_volume); +#endif +} + +void MVE_dsbSetPan(long lPan) +{ +#if SOUND_SUPPORT + snd_pan = lPan; + if (snd_buffer) + LnxSoundBuffer_SetPan(snd_buffer,snd_pan); +#endif +} + +static void sndReset(void) +{ +#if SOUND_SUPPORT + if (snd_buffer) + { + LnxSoundBuffer_Stop(snd_buffer); + LnxSoundBuffer_Release(snd_buffer); + snd_buffer = NULL; + } +#endif +} + +static bool sndConfigure(unsigned rate, unsigned buflen, unsigned stereo, + unsigned frequency, unsigned bits16, unsigned comp16) +{ +#if SOUND_SUPPORT + + LnxBufferDesc snd_bufferdesc; + WAVEFORMATEX snd_wfx; + int dsrval; + + if (!snd_ds) + return TRUE; + syncSync(); + sndReset(); + + snd_stereo = stereo; + snd_bits16 = bits16; + snd_comp16 = comp16; + + snd_bufferdesc.dwFlags = LNXSND_CAPS_CTRLDEFAULT; + snd_bufferdesc.dwBufferBytes = (buflen + (buflen>>1)) & ~3; + snd_bufferdesc.lpwfxFormat = &snd_wfx; + snd_wfx.wFormatTag = WAVE_FORMAT_PCM; + snd_wfx.nChannels = stereo?2:1; + snd_wfx.nSamplesPerSec = frequency; + snd_wfx.nBlockAlign = (stereo?2:1)*(bits16?2:1); + snd_wfx.nAvgBytesPerSec = frequency * snd_wfx.nBlockAlign; + snd_wfx.wBitsPerSample = bits16?16:8; + snd_fill = 0; + snd_empty = 0; + + dsrval = LnxSound_CreateSoundBuffer(snd_ds,&snd_bufferdesc,&snd_buffer); + if (dsrval != 0) return FALSE; + + LnxSoundBuffer_SetVolume(snd_buffer, snd_volume); + LnxSoundBuffer_SetPan(snd_buffer, snd_pan); + + snd_write_cursor = 0; + + dsrval = LnxSoundBuffer_GetCaps(snd_buffer, &snd_buffer_caps); + if (dsrval != 0) return FALSE; + +#endif + + return TRUE; +} + +static void sndSync(void) +{ + +#ifdef __DUMP_MVE_TO_DISK + return; +#else + +#if SOUND_SUPPORT + + int dsrval; + unsigned int dsbstatus; + unsigned int play_cursor,write_cursor,target; + + bool need_resync; +#endif + + //Better frame dropping using more flexible synchronization + sync_late = syncWaitLevel(sync_wait_quanta>>2)>(-sync_wait_quanta>>1) && !sync_FrameDropped; + sync_FrameDropped = FALSE; + +#if SOUND_SUPPORT + if (!snd_ds || !snd_buffer) return; + + need_resync = FALSE; + for (;;) + { + dsrval = LnxSoundBuffer_GetStatus(snd_buffer, &dsbstatus); + if (dsrval != 0) return; + dsrval = LnxSoundBuffer_GetCurrentPosition(snd_buffer, &play_cursor, &write_cursor); + if (dsrval != 0) return; + write_cursor = snd_write_cursor; + +#define set_target(t) (target = (snd_queue[snd_empty].ptr+(t)+snd_buffer_caps.dwBufferBytes)% snd_buffer_caps.dwBufferBytes) +#define target_pending() ( (play_cursor<=write_cursor)?(play_cursor <= target && target < write_cursor):(play_cursor <= target || target < write_cursor)) + + // Don't get too far ahead of sound (target-pad not yet played) + set_target(-snd_pad); + if (target_pending() && (dsbstatus & LNXSND_PLAYING)) + need_resync = TRUE; + else + break; + } + if (need_resync) + { + syncReset(sync_wait_quanta+(sync_wait_quanta>>2)); + } + + if (!(dsbstatus & LNXSND_PLAYING)) // If currently not playing + { + // Don't restart too soon (restart when target hasn't been played) + set_target(0); + if (target_pending()) + { + dsrval = LnxSoundBuffer_SetCurrentPosition(snd_buffer, target); + if (dsrval != 0) return; + dsrval = LnxSoundBuffer_Play(snd_buffer, LNXSND_LOOPING); + if (dsrval != 0) return; + + syncReset(sync_wait_quanta); + } + } + //Because DirectSound can consume an unpredictable amount into primary, this won't always be invoked when it should + else + { + // Don't get too far behind sound (has target+pad already been played?) + set_target(snd_pad); + { + int amt = write_cursor - play_cursor; + if (amt < 0) amt += snd_buffer_caps.dwBufferBytes; + amt = snd_buffer_caps.dwBufferBytes - amt - 1; + if (amt > (int)snd_buffer_caps.dwBufferBytes/2) + amt = snd_buffer_caps.dwBufferBytes/2; + play_cursor = (play_cursor - amt + snd_buffer_caps.dwBufferBytes) % snd_buffer_caps.dwBufferBytes; + } + if (!target_pending()) + { + LnxSoundBuffer_Stop(snd_buffer); + //dsrval = LnxSoundBuffer_GetCurrentPosition(snd_buffer, &snd_write_cursor, &write_cursor); + } + } + + if (snd_empty != snd_fill) + { + if (snd_empty==snd_queue_max-1) snd_empty=0; + else ++snd_empty; + } +#endif + +#endif // dump mve to disk +} + + +// For compressed streams, assumes len (which is in bytes) will be in multiples +// of 2 for mono and 4 for stereo. +static unsigned sndAddHelper(unsigned char *dst, unsigned char **pSrc, unsigned len, unsigned state, bool init) +{ +#if SOUND_SUPPORT + unsigned char *src; + src = *pSrc; + if (!src) + memset(dst, (snd_bits16?0:0x80), len); + else + if (snd_comp16) + if (!snd_stereo) + { + if (init) + { + unsigned short swapper = *(unsigned short *)src; + state = INTEL_SHORT(swapper); + *(unsigned short *)dst = state; + src += 2; + dst += 2; + len -= 2; + } + + state = sndDecompM16((unsigned short *)dst, src, len>>1, state); + + src += len>>1; + }else + { + if (init) + { + state = *(unsigned long *)src; + state = INTEL_INT(state); + *(unsigned long *)dst = state; + src += 4; + dst += 4; + len -= 4; + } + state = sndDecompS16((unsigned short *)dst, src, len>>2, state); + src += len>>1; + } + else + { + memcpy(dst, src, len); + src += len; + } + *pSrc = src; + return state; +#else + return 0; +#endif +} + +static void sndAdd(unsigned char *buf, unsigned len) +{ +#if SOUND_SUPPORT + + int dsrval; + unsigned int play_cursor,write_cursor; + unsigned int len1, len2; + + unsigned state = 0; + bool init = TRUE; + unsigned char *ptr1, *ptr2; + + snd_pad = len; + + if (!snd_buffer || snd_fill+1==(snd_empty?snd_empty:snd_queue_max)) + return; + + dsrval = LnxSoundBuffer_GetCurrentPosition(snd_buffer, &play_cursor, &write_cursor); + if (dsrval != 0) return; + write_cursor = snd_write_cursor; + + dsrval = LnxSoundBuffer_Lock(snd_buffer,write_cursor,len,(void **)&ptr1,&len1,(void **)&ptr2,&len2, + 0/*LNXSND_LOCK_FROMWRITECURSOR*/ /*flags*/); + if (dsrval != 0) return; + if (len1) + { + state = sndAddHelper(ptr1, &buf, len1, state, init); + init = FALSE; + snd_write_cursor += len1; + } + if (len2) + { + sndAddHelper(ptr2, &buf, len2, state, init); + snd_write_cursor = len2; + } + + if (snd_write_cursor==snd_buffer_caps.dwBufferBytes) + snd_write_cursor = 0; + + LnxSoundBuffer_Unlock(snd_buffer,ptr1,len1,ptr2,len2); + + snd_queue[snd_fill].ptr = write_cursor; + if (snd_fill==snd_queue_max-1) + snd_fill=0; + else + ++snd_fill; +#endif +} + +static void sndRelease(void) +{ +#if SOUND_SUPPORT + // Nothing to free +#endif +} + +static void sndPause(void) +{ +#if SOUND_SUPPORT + if (snd_buffer) + LnxSoundBuffer_Stop(snd_buffer); +#endif +} + +static void sndResume(void) +{ + //Nothing need be done here to resume sound + //The next call to sndSync will automatically resume the sound. +} + +//-------------------------------------------------------------------- +// NextFrame (Video Decompression) +//---------------------------------- + +// static removed from most nf_ vars to support mveliba.asm + +static bool nf_memory_mode = FALSE; + +// NextFrame working storage +static unsigned char *nf_dds_cur = NULL; +static unsigned char *nf_dds_prv = NULL; +static MemRec nf_mem_buf1; +static MemRec nf_mem_buf2; +unsigned char* nf_buf_cur; +unsigned char* nf_buf_prv; + +// NextFrame parameters +unsigned char nf_wqty; // (width/SWIDTH) +unsigned char nf_hqty; // (height/SHEIGHT) +unsigned char nf_fqty; // Number of fields +unsigned nf_hicolor; // HiColor (0:none,1:normal,2:swapped) +// +unsigned nf_width; // wqty * SWIDTH +unsigned nf_height; // hqty * SHEIGHT; +unsigned nf_new_line; // width - SWIDTH +unsigned nf_new_row0; // SHEIGHT*width*2-width +unsigned nf_back_right; // (SHEIGHT-1)*width + +// Frame parameters +// Portion of current frame which has been updated +// and needs to be sent to screen. +// +unsigned nf_new_x; +unsigned nf_new_y; +unsigned nf_new_w; +unsigned nf_new_h; + +// Hicolor format translation tables +unsigned short nf_trans16_lo[256]; +unsigned short nf_trans16_hi[256]; + +void MVE_memVID(void *p1, void *p2, unsigned size) +{ + MemInit(&nf_mem_buf1, size, p1); + MemInit(&nf_mem_buf2, size, p2); +} + +void nfPkConfig(void); + +// ffs() +// Returns position of most significant bit set (0 to 31). +// Assumes bits is nonzero. +static int ffs(unsigned bits) +{ + int pos; + unsigned t; + t = bits & 0xFFFF0000; + if (t) bits=t, pos=16; + else pos=0; + t = bits & 0xFF00FF00; + if (t) bits=t, pos|=8; + t = bits & 0xF0F0F0F0; + if (t) bits=t, pos|=4; + t = bits & 0xCCCCCCCC; + if (t) bits=t, pos|=2; + if (bits & 0xAAAAAAAA) pos|=1; + return pos; +} + + +static bool nfConfig(int wqty, int hqty, int fqty, int hicolor) +{ + unsigned size; + + if (!nf_memory_mode) + { + if (nf_dds_cur) + { + free(nf_dds_cur); + nf_dds_cur = NULL; + } + if (nf_dds_prv) + { + free(nf_dds_prv); + nf_dds_prv = NULL; + } + } + + nf_wqty = (unsigned char)wqty; + nf_hqty = (unsigned char)hqty; + nf_fqty = (unsigned char)fqty; + nf_width = wqty * SWIDTH; + nf_height = hqty * fqty * SHEIGHT; + if (opt_fastmode) nf_height >>= 1; + { + if (hicolor) + { + int pal_rshift, pal_gshift, pal_bshift; + unsigned pal_rmask, pal_bmask, pal_gmask; + unsigned i, r,g,b; + LnxDraw_GetRGBMasks(mve_lpWin,&pal_rmask,&pal_gmask,&pal_bmask); + + pal_rshift = ffs(pal_rmask)-4; + pal_gshift = ffs(pal_gmask)-4; + pal_bshift = ffs(pal_bmask)-4; + for (i=0, r=0, g=0; g<8; ++g) + for (b=0; b<32; ++b, ++i) + nf_trans16_lo[i] = (((pal_rshift>0?r<>-pal_rshift)&pal_rmask) | + ((pal_gshift>0?g<>-pal_gshift)&pal_gmask) | + ((pal_bshift>0?b<>-pal_bshift)&pal_bmask) ); + + for (i=0, r=0, b=0; r<32; ++r) + for (g=0; g<32; g+=8, ++i) + nf_trans16_hi[i+128] = nf_trans16_hi[i] = (((pal_rshift>0?r<>-pal_rshift)&pal_rmask) | + ((pal_gshift>0?g<>-pal_gshift)&pal_gmask) | + ((pal_bshift>0?b<>-pal_bshift)&pal_bmask) ); + } + int size = nf_width*nf_height<<1; + + nf_dds_cur = (unsigned char *)malloc(size); + nf_dds_prv = (unsigned char *)malloc(size); + } + + nf_new_line = nf_width * fqty - SWIDTH; + nf_hicolor = hicolor; + if (hicolor) + { + nf_width <<= 1; + nf_new_line <<= 1; + } + + nf_new_row0 = fqty * SHEIGHT * nf_width; + nf_back_right = fqty * (SHEIGHT-1) * nf_width; + size = nf_width*nf_height; + nfPkConfig(); + + return TRUE; +} + +static bool nfLock(void) +{ + //nf_buf_cur = (unsigned char *)nf_dds_cur; + int pitch; + int x1,y1,x2,y2,mw; + extern unsigned sf_ScreenHeight; // Height of modifiable screen + extern unsigned sf_ScreenWidth; // Width of modifiable screen + + // center it + mw = (nf_hicolor)?nf_width>>1:nf_width; + x1 = (sf_ScreenWidth>>1) - (mw>>1); + if(x1<0) x1 = 0; + y1 = (sf_ScreenHeight>>1) - (nf_height>>1); + if(y1<0) y1 = 0; + x2 = x1 + mw -1; + y2 = y1 + nf_height - 1; + + LnxDraw_LockSurface(mve_lpWin,x1,y1,x2,y2,&nf_buf_cur,&pitch); + memcpy(nf_buf_cur,nf_dds_cur,nf_width*nf_height); + nf_buf_prv = (unsigned char *)nf_dds_prv; + if (!nf_buf_cur || !nf_buf_prv) return FALSE; + return TRUE; +} + +static void nfUnlock(void) +{ + memcpy(nf_dds_cur,nf_buf_cur,nf_width*nf_height); + LnxDraw_UnlockSurface(mve_lpWin,nf_buf_cur); +} + +static void nfRelease(void) +{ + free(nf_dds_cur); + free(nf_dds_prv); + nf_dds_cur = NULL; + nf_dds_prv = NULL; +} + +static void nfAdvance(void) +{ +/* + int ret; + int x,y; + extern unsigned sf_ScreenHeight; // Height of modifiable screen + extern unsigned sf_ScreenWidth; // Width of modifiable screen + + // center it + int mw; + mw = (nf_hicolor)?nf_width>>1:nf_width; + x = (sf_ScreenWidth>>1) - (mw>>1); + if(x<0) x = 0; + y = (sf_ScreenHeight>>1) - (nf_height>>1); + if(y<0) y = 0; + + ret = LnxDraw_Blit(mve_lpWin,nf_dds_cur,(unsigned int)x,(unsigned int)y,mw,nf_height); +*/ + unsigned char *tmp; + tmp = nf_dds_prv; + nf_dds_prv = nf_dds_cur; + nf_dds_cur = tmp; +} + + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// +void nfHiColorDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// Chgs specifies which squares to update. +// Parms are motion parms for squares to update. +// +void nfHiColorDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp, + unsigned x, unsigned y, unsigned w, unsigned h); + + +// Non-HiColor versions + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// +void nfDecomp(unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + +void nfPkDecomp(unsigned char *ops, unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); +void nfPkDecompH(unsigned char *ops, unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + +void nfHPkDecomp(unsigned char *ops, unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + +// Decompress into subsection of current buffer specified +// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8). +// Chgs specifies which squares to update. +// Parms are motion parms for squares to update. +// +void nfDecompChg(unsigned short *chgs,unsigned short *parms,unsigned char *comp,unsigned x, unsigned y, unsigned w, unsigned h); + +//--------------------------------------------------------------------- +// ShowFrame +//------------ + +// TODO: +// Support for software cursor. +// Issues & Considerations: +// 1. Current code only deals with writing to screen, not reading it. +// 2. To prevent flicker, cursor should be drawn into current buffer +// before it is sent to screen, first saving area overwritten. +// After current before is sent to screen, the area modified +// by the cursor in the current buffer should be restored. +// 3. Screen must also be updated in outside areas if cursor +// appears there. This requires read/modify/write to screen, +// because current buffer may not exist or be valid for area +// of screen. Also, contents of screen must be saved so that +// if next frame doesn't modify that area of screen, it can be +// restored when mouse moves away. +// In other words: +// (a): +// Save area on screen where mouse will go. +// Draw mouse onto screen. +// When mouse moves, restore previous contents of screen and +// loop back to (a). +// When screen is to be redrawn with video, draw mouse clipped +// into buffer, saving previous contents. Draw video, then restore +// buffer. Note that saving previous contents saves into same +// area that was used when area under mouse was originally saved, +// but it may only be a subrectangle. +// Question: Should I implement VESA read from screen code? +// Or work with caller to implement? +// With caller: +// Caller provides pointer to mouse image and save area buffers, +// and screen location. Caller informs us when mouse changes +// (position or contents). We deal with drawing mouse into internal +// buffer and updating save area. Caller deals with drawing +// mouse on screen and saving areas from screen. Color zero will +// be assumed transparent. If old and new locations are within +// area we are about to draw, caller need do nothing other than +// call us (we can return a bool to let him know that). We can +// call him to draw cursor just before showframe (whereas +// call back from pausing would be just after showframe). +// Without special support? +// Everytime frame is shown, redraw mouse on screen, saving previous +// contents. If mouse moves, restore previous contents and redraw. +// Result will be a flickering mouse (mostly on, briefly off). + +static void (*sf_ShowFrame)(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned nf_hicolor) = NULL; + +unsigned sf_ResolutionHeight; // Height of screen +unsigned sf_ResolutionWidth; // Width of screen +unsigned sf_ScreenHeight; // Height of modifiable screen +unsigned sf_ScreenWidth; // Width of modifiable screen +// Private, see mveliba.asm : +unsigned sf_LineWidth; // Distance between lines in memory +unsigned sf_hicolor; // Hicolor mode (0:none,1:normal,2:swapped) + +// Banked screen parameters, Private, see mveliba.asm +void *sf_SetBank; +unsigned sf_WinGran; +unsigned long sf_WinSize; +unsigned sf_WinGranPerSize; +//{sf_WriteWinPtr and sf_WriteWinLimit replace sf_WriteWinSeg, see mveliba.asm} +unsigned char *sf_WriteWinPtr; +unsigned char *sf_WriteWinLimit; +unsigned sf_WriteWin; + +// +static bool sf_auto = TRUE; // True if mode can be set from movie + // (mode not specified by caller). +static int sf_auto_mode=0; // Current sf_auto mode. + +static void sfVGA(unsigned w, unsigned h, unsigned resw, unsigned resh); +static void sfShowFrame(int dx, int dy, unsigned field); + +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned field); +void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned field); + + +void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned field); + +// Restrictions/Assumptions: +// 64K >= WinSize >= WinGran +// WinSize % WinGran == 0 +// WinGran of 64K is represented by 0 +// SetBank is address of function with following protocol: +// bh: 0=Set window, 1=Get Window +// bl: Window number (0 or 1) +// dx: Window position in video memory in units of WinGran. +// on return, registers AX and DX are destroyed. +void MVE_sfSVGA(unsigned w, unsigned h, unsigned LineWidth,unsigned WriteWin, unsigned char *WriteWinPtr, + unsigned long WinSize, unsigned WinGran,void *SetBank, unsigned hicolor) +{ + sf_ScreenWidth = w; + sf_ScreenHeight = h; + sf_ResolutionWidth = w; + sf_ResolutionHeight = h; + sf_LineWidth = LineWidth; + if (opt_fastmode & 4) + sf_LineWidth<<=1; + sf_WriteWin = WriteWin; + sf_WinSize = WinSize; + sf_WriteWinPtr = WriteWinPtr; + sf_WriteWinLimit = sf_WriteWinPtr + sf_WinSize; + sf_WinGran = WinGran; + sf_SetBank = SetBank; + if (WinGran) + // Assumes WinGran divides evenly into WinSize. + sf_WinGranPerSize = (unsigned)WinSize / WinGran; + else + // Assumes WinSize is also 64K. + sf_WinGranPerSize = 1; + sf_auto = FALSE; + sf_hicolor = hicolor; +} + +//QUESTION: Should sfShowFrame also take x,y,w,h as command args? +// The issue is, will it always be true that area to update on +// screen matches area updated in memory, since previous contents +// of memory will be from two frames back, not one! +// Answer: By having compressor compare previous screen to desired screen, +// areas that are identical can be located. Only areas that change +// need to be built in memory. Remaining areas will not be correct, +// but can still be used for source data for next screen. +// Therefore, additional x,y,w,h args are not needed. +// However, should this prove to be wrong, a minor opcode variant +// can always be added which supplies the additional arguments. + +static void sfShowFrame(int dx, int dy, unsigned field) +{ + unsigned scaled_width = nf_width*4/opt_hscale_step; + scaled_width = ((scaled_width-12)&~0xf) + 12; // Round down to a multiple of 16 + 12. + opt_hscale_adj = nf_width - (scaled_width/4*opt_hscale_step); + + logLabel("StartShow"); + if (dx<0) + if (nf_hicolor) + dx = (sf_ScreenWidth - (scaled_width>>1)) >> 1; + else + dx = (sf_ScreenWidth - scaled_width) >> 1; + + if (nf_hicolor) + dx <<= 1; + + if (dy<0) + if (opt_fastmode&4) //HACK + dy = (sf_ScreenHeight - nf_height*2) >> 1; + else + dy = (sf_ScreenHeight - nf_height) >> 1; + + dx &= ~3; // Force to a multiple of 4 boundary for performance! + + if (opt_fastmode&4) //HACK + dy>>=1; + + if (field) + mve_ShowFrameField(nf_buf_cur, nf_width, nf_height,nf_new_x, nf_new_y, nf_new_w, nf_new_h,dx, dy, field); + else + if (opt_hscale_step!=4) + { + (*sf_ShowFrame)(nf_dds_cur, nf_width, nf_height,0, nf_new_y, scaled_width, nf_new_h, + dx, dy, nf_hicolor); + } + else + (*sf_ShowFrame)(nf_dds_cur, nf_width, nf_height,nf_new_x, nf_new_y, nf_new_w, nf_new_h,dx, dy, nf_hicolor); + + logLabel("EndShow"); +} + +void MVE_sfCallbacks(void ( *fn_ShowFrame)(unsigned char *buf, unsigned bufw, unsigned bufh,unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned hicolor)) +{ + sf_ShowFrame = fn_ShowFrame; +} + +// Restriction: w must be a multiple of 4. +// Strong Recommendation: sx and dstx should be multiples of 4. +// dstx & dsty are deltas relative to sx & sy. +// Field is a hack and should be removed. Instead, nfConfig +// should specify interlace mode and cause linestep to be twice +// sf_LineWidth. dstx/dsty should be made absolute to allow interlace +// field to be determined by dst. +// ON THE OTHER HAND -- All this makes user clipping quite complex... +// A caller would probably like to deal in ordinary coordinates, +// but then we still need 'field'. +// Also note that when field is on, the actual height of the image +// on the screen is 2*h alternate lines. +// + +void mve_sfHiColorShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h, + unsigned short *chgs,unsigned dstx, unsigned dsty); + + +void mve_sfShowFrameChg(bool prvbuf,unsigned x, unsigned y, unsigned w, unsigned h, + unsigned short *chgs,unsigned dstx, unsigned dsty); + +static void sfShowFrameChg(int dx, int dy, unsigned short *chgs) +{ + logLabel("StartShowChg"); +} + +//--------------------------------------------------------------------- +// Palette Management +//--------------------- +#ifdef __USE_X86_ASM__ // AH +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count) { __asm__ ("int $3"); } +#else +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); +#endif + +static void (*pal_SetPalette)(unsigned char *p, unsigned start, unsigned count) = MVE_SetPalette; + +unsigned char pal_tbl[3*256]; // Private, see mveliba.asm +#if DBL_DBG +unsigned char pal_tbl_old[3*256]; +#endif +unsigned short pal15_tbl[256]; // Private, see mveliba.asm + +void MVE_palCallbacks(void (*fn_SetPalette)(unsigned char *p, unsigned start, unsigned count)) +{ + pal_SetPalette = fn_SetPalette; +} + +static void palSetPalette(unsigned start, unsigned count) +{ + if (!nf_hicolor) + (*pal_SetPalette)((unsigned char *)pal_tbl,start,count); +} + +static void palClrPalette(unsigned start, unsigned count) +{ + static unsigned char clr_pal_tbl[256*3]; + if (!nf_hicolor) + (*pal_SetPalette)((unsigned char *)clr_pal_tbl,start,count); +} + +// Old Synth: 0,21,6 +// 128,32,4 +// +// New Synth: 129,21,6 +// 17,28,4 +// +static void palMakeSynthPalette(int base_r, int range_r, int range_rb,int base_g, int range_g, int range_gb) +{ + unsigned char (*SynthPal)[3] = (unsigned char (*)[3])pal_tbl; + int i,j; + + for (i=0; iperiod = INTEL_INT(arg->period); + arg->wait_quanta = INTEL_SHORT(arg->wait_quanta); + if (!syncInit(arg->period, arg->wait_quanta)) + { + result = MVE_ERR_SYNC; + goto done; + } + continue; + } + + case mcmd_sndConfigure: + { + marg_sndConfigure *arg = (marg_sndConfigure *)p; + arg->rate = INTEL_SHORT(arg->rate); + arg->frequency = INTEL_SHORT(arg->frequency); + arg->buflen = INTEL_INT(arg->buflen); + unsigned comp16 = hdr.minor>=1?arg->comp16:0; + //linux has a problem with bit fields in the middle of the struct + unsigned buflen = *(unsigned *)(p+6); + if (hdr.minor==0) buflen &= 0xFFFF; + if (!sndConfigure(arg->rate, buflen, arg->stereo,arg->frequency, arg->bits16, comp16)) + { + result = MVE_ERR_SND; + goto done; + } + continue; + } + + case mcmd_sndSync: + { + sndSync(); + continue; + } + + case mcmd_nfConfig: + { + marg_nfConfig *arg =(marg_nfConfig *)p; + arg->wqty = INTEL_SHORT(arg->wqty); + arg->hqty = INTEL_SHORT(arg->hqty); + arg->fqty = INTEL_SHORT(arg->fqty); + arg->hicolor = INTEL_SHORT(arg->hicolor); + unsigned hicolor = hdr.minor>=2?arg->hicolor:0; + if (!nfConfig(arg->wqty, arg->hqty,(hdr.minor>=1?arg->fqty:1), hicolor)) + { + result = MVE_ERR_NF; + goto done; + } + // To handle interlace mode, we need the following: + // A window width/height in addition to nf_width/height. + // Window width/height should be used here and in centering + // code in sfShowFrame. + { + unsigned scaled_width = nf_width*4/opt_hscale_step; + scaled_width &= ~0xf; // Round down to a multiple of 16. + if (nf_hicolor) scaled_width>>=1; /*HACK*/ + if (scaled_width + (rm_dx<0?0:rm_dx) > sf_ScreenWidth || + nf_height + (rm_dy<0?0:rm_dy) > sf_ScreenHeight) + { + result = MVE_ERR_GFX_FIT; + goto done; + } + } + // NOTE: Eventually, change this to allow sf_hicolor when !nf_hicolor and + // have show function convert from 256-color to hicolor format. + // HiColor will also need to disable hardware palette changes and + // maintain 15-bit software palette along with 24-bit palette. + if (nf_hicolor && !sf_hicolor) + { + result = MVE_ERR_GFX_FIT; + goto done; + } + continue; + } + + case mcmd_nfDecomp: + { + marg_nfDecomp *arg = (marg_nfDecomp *)p; + arg->prev = INTEL_SHORT(arg->prev); + arg->iframe = INTEL_SHORT(arg->iframe); + arg->x = INTEL_SHORT(arg->x); + arg->y = INTEL_SHORT(arg->y); + arg->w = INTEL_SHORT(arg->w); + arg->h = INTEL_SHORT(arg->h); + if (arg->advance) + nfAdvance(); + if (!nfLock()) + { + result = MVE_ERR_LOST; + goto done; + } + nfDecomp(MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h); + nfUnlock(); + continue; + } + + case mcmd_sfShowFrame: + { + unsigned field = 0; + marg_sfShowFrame *arg = (marg_sfShowFrame *)p; + arg->pal_start = INTEL_SHORT(arg->pal_start); + arg->pal_count = INTEL_SHORT(arg->pal_count); + arg->field = INTEL_SHORT(arg->field); + + ++rm_FrameCount; + + if (hdr.minor >= 1) + field = arg->field; + + if (arg->pal_count && !DecompChg_chgs && !sf_hicolor) + palClrPalette(arg->pal_start, arg->pal_count); + else + { + palSetPalette(arg->pal_start, arg->pal_count); + } + + if (DecompChg_chgs) + sfShowFrameChg(rm_dx, rm_dy, DecompChg_chgs); + else + if (sync_late && arg->pal_count==0) + { + sync_FrameDropped = TRUE; + ++rm_FrameDropCount; + }else + { + sfShowFrame(rm_dx, rm_dy, field); + } + + if (arg->pal_count && !DecompChg_chgs && !sf_hicolor) + { + palSetPalette(arg->pal_start, arg->pal_count); + } + + goto FrameDone; + } + + case mcmd_sndAdd: + case mcmd_sndSilence: + { + marg_sndAdd *arg = (marg_sndAdd *)p; + arg->iframe = INTEL_SHORT(arg->iframe); + arg->TrackMask = INTEL_SHORT(arg->TrackMask); + arg->qty = INTEL_SHORT(arg->qty); + if (arg->TrackMask & rm_track_bit) + sndAdd((hdr.major==mcmd_sndAdd?MCMD_DATA(arg):(unsigned char *)NULL),arg->qty); + continue; + } + + case mcmd_gfxMode: + { + marg_gfxMode *arg = (marg_gfxMode *)p; + arg->minw = INTEL_SHORT(arg->minw); + arg->minh = INTEL_SHORT(arg->minh); + arg->mode = INTEL_SHORT(arg->mode); + if (sf_auto) + { + short mode = arg->mode; + if (opt_fastmode && (opt_fastmode&4)==0) mode |= 0x8000; + if (sf_auto_mode != mode) + if (!MVE_gfxMode(mode)) + { + result = MVE_ERR_GFX_FAIL; + goto done; + } + sf_auto = TRUE; + sf_auto_mode = mode; + } + continue; + } + + case mcmd_palMakeSynthPalette: + { + marg_palMakeSynthPalette *arg = (marg_palMakeSynthPalette *)p; + palMakeSynthPalette(arg->base_r, arg->range_r, arg->range_rb, + arg->base_g, arg->range_g, arg->range_gb); + continue; + } + + case mcmd_palLoadPalette: + { + marg_palLoadPalette *arg = (marg_palLoadPalette *)p; + arg->start = INTEL_SHORT(arg->start); + arg->count = INTEL_SHORT(arg->count); + palLoadPalette(MCMD_DATA(arg), arg->start, arg->count); + continue; + } + + case mcmd_palLoadCompPalette: + { + palLoadCompPalette(p); + continue; + } + + case mcmd_nfChanges: + { + DecompChg_chgs = (unsigned short *)p; + continue; + } + case mcmd_nfParms: + { + DecompChg_parms = (unsigned short *)p; + continue; + } + case mcmd_nfDecompChg: + { + marg_nfDecomp *arg = (marg_nfDecomp *)p; + arg->prev = INTEL_SHORT(arg->prev); + arg->iframe = INTEL_SHORT(arg->iframe); + arg->x = INTEL_SHORT(arg->x); + arg->y = INTEL_SHORT(arg->y); + arg->w = INTEL_SHORT(arg->w); + arg->h = INTEL_SHORT(arg->h); + if (arg->advance) + nfAdvance(); + if (!nfLock()) + { + result = MVE_ERR_LOST; + goto done; + } + nfDecompChg(DecompChg_chgs, DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + nfUnlock(); + continue; + } + case mcmd_nfPkDecomp: + { + marg_nfDecomp *arg = (marg_nfDecomp *)p; + arg->prev = INTEL_SHORT(arg->prev); + arg->iframe = INTEL_SHORT(arg->iframe); + arg->x = INTEL_SHORT(arg->x); + arg->y = INTEL_SHORT(arg->y); + arg->w = INTEL_SHORT(arg->w); + arg->h = INTEL_SHORT(arg->h); + if (hdr.minor<3) + { + result = MVE_ERR_BADFMT; + goto done; + } + + if (arg->advance) + nfAdvance(); + + if (nf_hicolor) + { + if (opt_fastmode) + { + result = MVE_ERR_BADFMT; + goto done; + } + + if (!nfLock()) + { + result = MVE_ERR_LOST; + goto done; + } + + nfHPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + + nfUnlock(); + }else if ((opt_fastmode&3)==1) + { + // Half mode (half height, even lines only) + if (!nfLock()) + { + result = MVE_ERR_LOST; + goto done; + } + + nfPkDecompH((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + + nfUnlock(); + }else if ((opt_fastmode&3)==2) + { + if (!nfLock()) + { + result = MVE_ERR_LOST; + goto done; + } + // Support for dithered mode disabled... + // so just use half mode instead + nfPkDecompH((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + nfUnlock(); + }else //opt_fastmode==0 + { + // Normal mode + if (!nfLock()) + { + result = MVE_ERR_LOST; + goto done; + } + + nfPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + + nfUnlock(); + } + continue; + } + } + break; + } + } +done: //Error + MVE_rmEndMovie(); + +movie_eof: //End of movie + return result; + +FrameDone: //Done with frame, return to caller for between frame processing + rm_p = p; + rm_len = len; + return 0; +} + +void MVE_rmEndMovie(void) +{ + if (rm_active) + { + syncWait(); + syncRelease(); + sndReset(); + rm_active = FALSE; + } +} + +int MVE_RunMovie(int hFile, int dx, int dy, unsigned track) +{ + int result; + result = MVE_rmPrepMovie(hFile,dx,dy,track); + while (!result) + { + result = MVE_rmStepMovie(); + logLabel("StartUser"); + while (!result) + { + result = (*rm_ctl)(); + if (result!=MVE_CTL_HOLD) break; + result = MVE_rmHoldMovie(); + } + logLabel("EndUser"); + } + MVE_rmEndMovie(); + return (result==MVE_ERR_EOF)?0:result; +} + +int MVE_RunMovieContinue(int hFile, int dx, int dy, unsigned track) +{ + int result; + result = MVE_rmPrepMovie(hFile,dx,dy,track); + while (!result) + { + result = MVE_rmStepMovie(); + logLabel("StartUser"); + while (!result) + { + result = (*rm_ctl)(); + if (result!=MVE_CTL_HOLD) break; + result = MVE_rmHoldMovie(); + } + logLabel("EndUser"); + } + //Continue version doesn't call MVE_rmEndMovie; + return (result==MVE_ERR_EOF)?0:result; +} + +void MVE_ReleaseMem(void) +{ + MVE_rmEndMovie(); + ioRelease(); + sndRelease(); + nfRelease(); +} + +//---------------------------------------------------------------------- + +char* MVE_strerror(int code) +{ + char *errors[] = + { + "Movie aborted with special code", + "Movie aborted", + "Movie completed normally", //0 + "Movie completed normally", //-1 + "File I/O error or Unable to allocate I/O buffers", + "Unable to create timer", + "Unable to allocate sound buffers", + "Unable to allocate video buffers", + "Insufficient screen resolution for movie", + "Unable to setup graphics mode used by movie", + "Invalid movie file", + "Incorrect screen color mode", + "StepMovie() without PrepMovie()", + "Unable to initialize Linux Draw System", + "Unable to lock window surface", + "Unknown movie error code" + }; + + if (code >= MVE_CTL_EXIT+1) code = MVE_CTL_EXIT+1; + if (code <= MVE_ERR_LAST-1) code = MVE_ERR_LAST-1; + return errors[MVE_CTL_EXIT+1 - code]; +} + +//---------------------------------------------------------------------- +// Frame Reader +//-------------- + +typedef struct _MVE_frstream +{ + int (*callback)(unsigned op, unsigned subop, void *buf); + + // I/O Stream state + unsigned (*io_read)(int handle, void *buf, unsigned count); + MemRec io_mem_buf; + int io_handle; + ioHdrRec io_next_hdr; + unsigned char *p; + unsigned len; + + // NextFrame working storage + bool nf_memory_mode; + unsigned char *nf_dds_cur; + unsigned char *nf_dds_prv; + + MemRec nf_mem_buf1; + MemRec nf_mem_buf2; + unsigned char* nf_buf_cur; + unsigned char* nf_buf_prv; + // NextFrame parameters + unsigned char nf_wqty; // (width/SWIDTH) + unsigned char nf_hqty; // (height/SHEIGHT) + unsigned char nf_fqty; // Number of fields + unsigned nf_hicolor; // HiColor (0:none,1:normal,2:swapped) + // NextFrame derived quantities + unsigned nf_width; // wqty * SWIDTH + unsigned nf_height; // hqty * SHEIGHT; + unsigned nf_new_line; // width - SWIDTH + unsigned nf_new_row0; // SHEIGHT*width*2-width + unsigned nf_back_right; // (SHEIGHT-1)*width + // Palette + unsigned char pal_tbl[3*256]; + unsigned pal_start, pal_count; + +} MVE_frStreamRec; + +static void frLoad(MVE_frStream frs) +{ + io_read = frs->io_read; + io_mem_buf = frs->io_mem_buf; + io_handle = frs->io_handle; + io_next_hdr = frs->io_next_hdr; + + nf_memory_mode = frs->nf_memory_mode; + nf_dds_cur = frs->nf_dds_cur; + nf_dds_prv = frs->nf_dds_prv; + nf_mem_buf1 = frs->nf_mem_buf1; + nf_mem_buf2 = frs->nf_mem_buf2; + nf_buf_cur = frs->nf_buf_cur; + nf_buf_prv = frs->nf_buf_prv; + nf_wqty = frs->nf_wqty; + nf_hqty = frs->nf_hqty; + nf_fqty = frs->nf_fqty; + nf_hicolor = frs->nf_hicolor; + nf_width = frs->nf_width; + nf_height = frs->nf_height; + nf_new_line = frs->nf_new_line; + nf_new_row0 = frs->nf_new_row0; + nf_back_right = frs->nf_back_right; +} + +static void frSave(MVE_frStream frs) +{ + frs->io_read = io_read; + frs->io_mem_buf = io_mem_buf; + frs->io_handle = io_handle; + frs->io_next_hdr = io_next_hdr; + + frs->nf_memory_mode = nf_memory_mode; + frs->nf_dds_cur = nf_dds_cur; + frs->nf_dds_prv = nf_dds_prv; + frs->nf_mem_buf1 = nf_mem_buf1; + frs->nf_mem_buf2 = nf_mem_buf2; + frs->nf_buf_cur = nf_buf_cur; + frs->nf_buf_prv = nf_buf_prv; + frs->nf_wqty = nf_wqty; + frs->nf_hqty = nf_hqty; + frs->nf_fqty = nf_fqty; + frs->nf_hicolor = nf_hicolor; + frs->nf_width = nf_width; + frs->nf_height = nf_height; + frs->nf_new_line = nf_new_line; + frs->nf_new_row0 = nf_new_row0; + frs->nf_back_right = nf_back_right; +} + +MVE_frStream MVE_frOpen(unsigned (*fn_read)(int handle, void *buf,unsigned count),int handle, + int (*fr_callback)(unsigned, unsigned,void *buf)) +{ + MVE_frStream frs; + MVE_frStreamRec save; + bool failed = FALSE; + + if (!mve_lpWin || !mem_alloc) return (MVE_frStream)NULL; + + frs = (struct _MVE_frstream *)(*mem_alloc)(sizeof(*frs)); + if (!frs) return frs; + memset(frs, 0, sizeof(*frs)); + + frSave(&save); + frLoad(frs); + + MVE_ioCallbacks(fn_read); + failed = !ioReset(handle); + frs->callback = fr_callback; + + if (!failed) + { + frs->p = ioNextRecord(); + frs->len = 0; + } + + frSave(frs); + frLoad(&save); + + if (failed) + { + MVE_frClose(frs); + return (MVE_frStream)NULL; + } + + return frs; +} + +int MVE_frGet(MVE_frStream frs,unsigned char **pBuf,unsigned *width, unsigned *height) +{ + MVE_frStreamRec save; + unsigned char *p; + unsigned len; + int result = 0; + + frSave(&save); + frLoad(frs); + p = frs->p; + len = frs->len; + + for (;;p=ioNextRecord(),len=0) + { + unsigned short *DecompChg_parms = (unsigned short *)NULL; + + if (!p) + { + result = MVE_ERR_IO; + goto done; + } + for (;;) + { + mcmd_hdr hdr; + p += len; //Advance past data of previous command + hdr = *(mcmd_hdr *)p; + hdr.len = INTEL_SHORT(hdr.len); + p += sizeof(hdr); + len = hdr.len; + switch (hdr.major) + { + default: + if (frs->callback) + { + result = (*frs->callback)(hdr.major,hdr.minor,p); + if (result) + goto done; + } + continue; + + case mcmd_end: + result = MVE_ERR_EOF; + goto done; + + case mcmd_next: + break; + + case mcmd_nfConfig: + { + marg_nfConfig *arg = (marg_nfConfig *)p; + arg->wqty = INTEL_SHORT(arg->wqty); + arg->hqty = INTEL_SHORT(arg->hqty); + arg->fqty = INTEL_SHORT(arg->fqty); + arg->hicolor = INTEL_SHORT(arg->hicolor); + unsigned hicolor = hdr.minor>=2?arg->hicolor:0; + unsigned opt_fastmode_save = opt_fastmode; + opt_fastmode = 0; + if (hicolor ||(hdr.minor>=1 && arg->fqty!=1) ||!nfConfig(arg->wqty, arg->hqty,(hdr.minor>=1)?arg->fqty:1, hicolor)) + { + opt_fastmode = opt_fastmode_save; + result = MVE_ERR_NF; + goto done; + } + opt_fastmode = opt_fastmode_save; + continue; + } + + case mcmd_sfShowFrame: + { + unsigned field = 0; + marg_sfShowFrame *arg =(marg_sfShowFrame *)p; + arg->pal_start = INTEL_SHORT(arg->pal_start); + arg->pal_count = INTEL_SHORT(arg->pal_count); + if (hdr.minor >= 1) field = arg->field; + if (field) + { + result = MVE_ERR_BADFMT; + goto done; + } + *pBuf = nf_dds_cur; + *width = nf_width; + *height = nf_height; + frs->pal_start = arg->pal_start; + frs->pal_count = arg->pal_count; + goto done; + } + + case mcmd_nfParms: + { + DecompChg_parms = (unsigned short *)p; + continue; + } + + case mcmd_nfPkDecomp: + { + marg_nfDecomp *arg = + (marg_nfDecomp *)p; + arg->prev = INTEL_SHORT(arg->prev); + arg->iframe = INTEL_SHORT(arg->iframe); + arg->x = INTEL_SHORT(arg->x); + arg->y = INTEL_SHORT(arg->y); + arg->w = INTEL_SHORT(arg->w); + arg->h = INTEL_SHORT(arg->h); + if (hdr.minor<3) + { + result = MVE_ERR_BADFMT; + goto done; + } + if (arg->advance) + nfAdvance(); + if (nf_hicolor) + { + result = MVE_ERR_BADFMT; + goto done; + } + nfPkConfig(); + if (!nfLock()) + { + result = MVE_ERR_LOST; + goto done; + } + nfPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg),arg->x, arg->y, arg->w, arg->h); + nfUnlock(); + continue; + } + case mcmd_palLoadPalette: + { + marg_palLoadPalette *arg =(marg_palLoadPalette *)p; + arg->start = INTEL_SHORT(arg->start); + arg->count = INTEL_SHORT(arg->count); + memcpy(&frs->pal_tbl[arg->start*3], MCMD_DATA(arg), arg->count*3); + continue; + } + } + break; + } + } + +done: + frSave(frs); + frs->p = p; + frs->len = len; + frLoad(&save); + nfPkConfig(); + if (result) + frs->pal_start = frs->pal_count = 0; + return result; +} + +void MVE_frPal(MVE_frStream frs, unsigned char **p, unsigned *start, unsigned *count) +{ + *p = frs->pal_tbl; + *start = frs->pal_start; + *count = frs->pal_count; +} + +void MVE_frClose(MVE_frStream frs) +{ + MVE_frStreamRec save; + + frSave(&save); + frLoad(frs); + + ioRelease(); + nfRelease(); + + frLoad(&save); + if (mem_free) + (*mem_free)(frs); +} diff --git a/lnxmvelib/mvelibl.h b/lnxmvelib/mvelibl.h new file mode 100644 index 000000000..43896ac25 --- /dev/null +++ b/lnxmvelib/mvelibl.h @@ -0,0 +1,338 @@ +/* +** mvelibl.h +** +** Interplay Movie File (MVE) Player +** Library Definitions (32-Bit Linux Version) +** Written by Paul Allen Edelstein, Interplay Productions. +** Partial Linux port by Jeff Slutter, Outrage Entertainment. +** +** (c) 1997 Interplay Productions. All Rights Reserved. +** This file is confidential and consists of proprietary information +** of Interplay Productions. This file and associated libraries +** may not, in whole or in part, be disclosed to third parties, +** incorporated into any software product which is not being created +** for Interplay Productions, copied or duplicated in any form, +** without the prior written permission of Interplay Productions. +** Further, you may not reverse engineer, decompile or otherwise +** attempt to derive source code of this material. +*/ + +#ifndef _MVELIB_H_INCLUDED + +#include "linux/lnxdsound.h" +#include "linux/lnxdraw.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Call this function to provide hooks into your memory management. +*/ +typedef void *(mve_cb_alloc)(unsigned size); +typedef void (mve_cb_free)(void *p); +void MVE_memCallbacks(mve_cb_alloc *fn_alloc, + mve_cb_free *fn_free); + +/* This function remains from the DOS version of mvelib. +** It allows you to provide a preallocated buffer for file I/O, +** but under Windows there's no real point to doing this. +*/ +void MVE_memIO(void *p, unsigned size); + +/* Call this function to provide hook into your file io. +*/ +typedef unsigned mve_cb_read(int handle, void *buf, unsigned count); +void MVE_ioCallbacks(mve_cb_read *fn_read); + +/* Call this function to provide hook into your digital sound driver. +** Call with NULL if no sound support is available (default). +*/ +void MVE_sndInit(LnxSoundDevice *lpDS); + +/* Volume controls. +** These functions are equivalent to the IDirectSoundBuffer +** SetVolume and SetPan functions. They take effect immediately +** and do NOT reset when a new movie starts. +** Volume ranges from 0 (0 db, no volume change) to -10,000 (-100db, essentially silent). +** Pan ranges from -10,000 (left full volume, right -100db), thru 0 (both full), +** thru 10,000 (left -100db, right full volume). +** The default value for volume and pan is zero. +*/ +void MVE_dsbSetVolume(long lVolume); +void MVE_dsbSetPan(long lPan); + + +/* Only call this function to configure software to work with a Super VGA +** mode if you do not have VESA support. +** +** Restrictions/Assumptions: +** 64K >= WinSize >= WinGran +** WinSize % WinGran == 0 +** WinGran of 64K is represented by 0 +** SetBank is address of function with following protocol: +** bh: 0=Set window, 1=Get Window +** bl: Window number (0 or 1) +** dx: Window position in video memory in units of WinGran. +** on return, registers AX and DX are destroyed. +** +** Hicolor is 0 for 8-bit color, 1 for 15-bit rgb color, 2 +** for byte swapped 15-bit rgb color. +*/ + +/* Note: 16-bit WriteWinSeg replaced with 32-bit WriteWinPtr */ + +/* The functionality of the following function is reduced in the Windows +** version of the player. Call it as follows: +** MVE_sfSVGA(w,h,w,0,NULL,0,0,NULL,hicolor) +** where w and h are the width and height of your window, +** and hicolor is a boolean which indicates if the screen +** is operating in hi color, rather than 8-bit paletted color. +** Under windows, the information provided by this function +** is just used for window centering and for determining +** how and when to do palette callbacks. +*/ +void MVE_sfSVGA(unsigned w, unsigned h, unsigned LineWidth, + unsigned WriteWin, unsigned char *WriteWinPtr, + unsigned long WinSize, unsigned WinGran, + void *SetBank, unsigned hicolor); + +/* This function alters the display from 640x480 or 640x400 to 640x350 resolution. +*/ +void MVE_ForceVres350(void); + +/* This function alters the display from 640x480/400/350 to +** 640x240/200/175. +*/ +void MVE_ForceVresHalf(void); + +/* **NOTE** There still need to be calls to restore original screen resolution +** after using MVE_ForceVres350() or MVE_ForceVresHalf()! +*/ + +/* Only call this function to either +** 1. Replace method of copying frame to screen (perhaps for a nonstandard +** screen format). +** 2. Wrap your own code around the transfer of frame to screen +** or modify which portions of the screen are updated. +** This function replaces calls to the default MVE_ShowFrame function +** with calls to your function, which can itself call MVE_ShowFrame. +*/ +typedef void mve_cb_ShowFrame (unsigned char *buf, unsigned bufw, unsigned bufh, + unsigned sx, unsigned sy, unsigned w, unsigned h, + unsigned dstx, unsigned dsty, unsigned hicolor); +void MVE_sfCallbacks(mve_cb_ShowFrame *fn_ShowFrame); + +typedef void mve_cb_SetPalette + (unsigned char *p, unsigned start, unsigned count); +void MVE_palCallbacks(mve_cb_SetPalette *fn_SetPalette); + +/* I'm pulling this, since it removes a C/C++ name mangling conflict - AH +void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count); +*/ + +/* Configure the software for a graphics mode, optionally setting the +** display to that mode (see the MVE_GFX_xxx constants defined below). +*/ +unsigned MVE_gfxMode(short mode); + +/* Reset the screen to text mode (usually done before exiting a program). +*/ +void MVE_gfxReset(void); + +/* Set line for split screen graphics */ +/* {Use vbe_SetDisplayStart(x,y) to set vid buf for upper screen} */ +void MVE_gfxSetSplit(unsigned line); + +/* Setup double buffering */ +void MVE_gfxSetDoubleBuffer(unsigned y1, unsigned y2, unsigned vis); + +/* Get double buffering state */ +void MVE_gfxGetDoubleBuffer(unsigned *vis_y, unsigned *hid_y); + +/* Enable double buffering for auto screen modes */ +void MVE_sfAutoDoubleBuffer(unsigned on); + +/* Wait for video retrace off (0) or on (1) */ +void MVE_gfxWaitRetrace(unsigned state); + + +/*---------------------------------------------*/ +/* Establish link to a graphics window +*/ +void MVE_rmWindow(LnxWindow *wnd); + + +/*---------------------------------------------*/ + +/* Establish callback for user control of movie playback. +*/ +typedef int mve_cb_ctl(void); +void MVE_rmCallbacks(mve_cb_ctl *fn_ctl); + +/* Specify playback fastmode option (default is MVE_RM_NORMAL). +*/ +#define MVE_RM_NORMAL 0 /* Normal playback */ +#define MVE_RM_HALF 1 /* Half height (even lines only) */ +#define MVE_RM_DITHERED 2 /* Half height (dither between lines) */ +#define MVE_RM_HALF_2 5 /* Full height, even lines only */ +#define MVE_RM_DITHERED_2 6 /* Full height, dither, even lines only */ + +void MVE_rmFastMode(int mode); + +/* Specifying horizontal magnification: +** 3: 4/3 horizontal magnification +** 4: normal +*/ +void MVE_rmHScale(int hscale); + +/* Get frame count and number of dropped frames from last movie played. +*/ +void MVE_rmFrameCounts(unsigned *FrameCount, unsigned *FrameDropCount); + +/* Dump timing statistics (if enabled). +*/ +void MVE_logDumpStats(void); + +/* Run a compressed movie by reading data starting at the current +** position in the file specified by handle hFile. +** The movie window is displaced by dx,dy from the upper left hand corner +** or is centered if dx,dy is -1,-1. +** track specifies which audio track to play (usually 0 for a single +** audio track). +** +** Returns an error/result code. +** +** Memory may be dynamically allocated while movie runs. +*/ +int MVE_RunMovie(int hFile, int dx, int dy, unsigned track); + +/* MVE_RunMovieContinue is the same as MVE_RunMovie except that it does not +** automatically call MVE_rmEndMovie(). This may improve the smoothness +** of immediately playing another movie afterwards. +*/ +int MVE_RunMovieContinue(int hFile, int dx, int dy, unsigned track); + +/* +** Alternative to using MVE_RunMovie() and MVE_rmCallbacks(). +** Call MVE_rmPrepMovie() to prepare movie for playing. +** Call MVE_rmStepMovie() to display next frame of movie until nonzero +** result is returned (MVE_ERR_EOF for no next frame or some other error). +** Call MVE_rmHoldMovie() to hold on current frame (and pause audio). +** Call MVE_rmEndMovie() to abort movie. +** All functions except MVE_rmEndMovie() return an error code. +*/ +int MVE_rmPrepMovie(int hFile, int dx, int dy, unsigned track); +int MVE_rmStepMovie(void); +int MVE_rmHoldMovie(void); +void MVE_rmEndMovie(void); + +/* Frame Reader Streams +** This is a special interface to the movie system which +** allows a movie file to be opened as a stream from which +** its frames may be retrieved. Audio and timing information +** are ignored. For 256-color screen applications, palette +** information is also typically ignored, and movies with a common +** predefined palette are used. However, for hi-color screen +** applications, an interface to obtain palette information has +** been provided. This system is intended for use by video sprites +** played off of the hard drive or out of memory. +*/ +typedef struct _MVE_frstream *MVE_frStream; + +/* MVE_frOpen +** Before calling this function, be sure to call MVE_memCallbacks() +** and MVE_rmDirectDraw(). +** +** fn_read specifies a file reader similar to the one +** used by MVE_ioCallbacks(). +** handle specifies a file handle for an already opened +** movie file. It is used by the file reader and is similar +** to hFile argument used by MVE_RunMovie() and MVE_rmPrepMovie(). +** fr_callback is normally NULL, but can be used to supply +** a handler for user data which has been interleaved into +** the movie stream. +** +** If the movie file is invalid or the call otherwise fails, +** NULL is returned. +*/ +MVE_frStream MVE_frOpen(unsigned (*fn_read)(int handle, void *buf, + unsigned count), + int handle, + int (*fr_callback)(unsigned op, unsigned subop, + void *buf)); + +/* MVE_frGet +** Returns the next frame from the specified frame reader stream +** a nonzero error code {the same codes as returned by MVE_RunMovie() +** and MVE_rmStepMovie()}. +** If successful, MVE_frGet(frs, &buf, &w, &h) returns a pointer +** to a direct draw surface containing the frame in buf, +** and its width and height in w and h. +*/ +int MVE_frGet(MVE_frStream frs,unsigned char **pBuf,unsigned *width, unsigned *height); + +/* MVE_frPal +** After each successful call to MVE_frGet(), this call may be used to +** obtain corresponding palette information. It returns a pointer to the +** entire current palette for the frame, and the subportion of the palette +** which has changed this frame is identified by start and count (they will +** both be zero on frames for which the palette has not changed). +** +** Paltbl points to 256*3 bytes of 6-bit r,g,b triples. +** Start ranges from 0 to 255. Count from 0 to 256. +** +** These conventions are similar to those used by the palette callback arguments +** with the standard player interface, except that this interface requires +** polling each frame instead, and must be passed pointers to the variables where +** the values will be returned. +** +*/ +void MVE_frPal(MVE_frStream frs, + unsigned char **pPaltbl, unsigned *pStart, unsigned *pCount); + +/* MVE_frClose +** Closes the specified Frame Reader Stream frs. +** Frees all storage associated with the stream. +** The specified frs must not be used after this call. +** Note that the open file handle specified in MVE_frOpen() is +** not closed by this call...that is the caller's responsibility. +*/ +void MVE_frClose(MVE_frStream frs); + + +/* Release any memory dynamically allocated by MVE_RunMovie. +*/ +void MVE_ReleaseMem(void); + +/* Return string corresponding to MVE_RunMovie result code. +*/ +char* MVE_strerror(int code); + +/* RunMovie callback control code and result codes. +** Codes > 1 are user defined. +*/ + +#define MVE_CTL_HOLD -1 /* Returned by rmCtl() to hold current frame */ +#define MVE_CTL_EXIT 1 /* Returned by rmCtl() to end movie */ + +#define MVE_ERR_EOF -1 /* Returned by StepMovie() for end of movie */ +#define MVE_ERR_IO -2 /* File I/O error or unable to alloc memory. */ +#define MVE_ERR_SYNC -3 /* Timer error. */ +#define MVE_ERR_SND -4 /* Unable to allocate memory for sound */ +#define MVE_ERR_NF -5 /* Unable to allocate memory for video */ +#define MVE_ERR_GFX_FIT -6 /* Screen size too small for movie */ +#define MVE_ERR_GFX_FAIL -7 /* Failed to set desired graphics mode */ +#define MVE_ERR_BADFMT -8 /* Not a MVE file or unacceptable version */ +#define MVE_ERR_GFX_CLR -9 /* Incorrect screen color mode */ +#define MVE_ERR_PREP -10 /* StepMovie() without PrepMovie() */ +#define MVE_ERR_LD -11 /* Unable to initialize LinuxDraw */ +#define MVE_ERR_LOST -12 /* Direct Draw Surface Lost */ + +#define MVE_ERR_LAST -12 + +#define _MVELIB_H_INCLUDED +#ifdef __cplusplus +}; +#endif +#endif + diff --git a/lnxmvelib/mvelibla.asm b/lnxmvelib/mvelibla.asm new file mode 100644 index 000000000..4d334fd69 --- /dev/null +++ b/lnxmvelib/mvelibla.asm @@ -0,0 +1,3632 @@ +; mvelibwa.c +; +; Interplay Movie (MVE) File Player Library (32-Bit Win95 Version) +; Assembly Language Components +; Written by Paul Allen Edelstein +; +; (c) 1997 Interplay Productions. All Rights Reserved. +; This file is confidential and consists of proprietary information +; of Interplay Productions. This file and associated libraries +; may not, in whole or in part, be disclosed to third parties, +; incorporated into any software product which is not being created +; for Interplay Productions, copied or duplicated in any form, +; without the prior written permission of Interplay Productions. +; Further, you may not reverse engineer, decompile or otherwise +; attempt to derive source code of this material. +; + +;;--- Options --- + +ONLYNEW equ 0 ; For debug, disables motion comp +LOGGING equ 0 ; Log timing statistics +PARTIAL equ 1 ; Support for partial updates +PKDATA equ 1 ; Support for packed data +HICOLOR equ 1 ; Support for HiColor +INTERP equ 0 ; Interpolated squares + ; 0:none (4x4x8), 1:generic dither, + ; 2:direction dither, 3:blend +COMPOPS equ 1 ; Compressed opcode table +SCALING equ 1 ; Scaling support +DECOMPD equ 0 ; Support for dithered half vert res +TRANS16 equ 1 ; Support for translating 16-bit rgb format + +;;--- Constants --- + +; Width and height of sections in pixels. +SWIDTH equ 8 +SHEIGHT equ 8 + +LOG2_SWIDTH equ 3 +LOG2_SHEIGHT equ 3 + +;;--- + +EXTERN pal_tbl ;:BYTE ; unsigned char pal_tbl[3*256]; +EXTERN pal15_tbl ;:WORD ; unsigned short pal15_tbl[256]; +EXTERN nf_trans16_lo +EXTERN nf_trans16_hi +EXTERN snd_8to16 ;: WORD ; short snd_8to16[256]; + +EXTERN nf_buf_cur ;: PTRBYTE ; unsigned char* nf_buf_cur; +EXTERN nf_buf_prv ;: PTRBYTE ; unsigned char* nf_buf_prv; + +;; NextFrame parameters +EXTERN nf_wqty ;: BYTE ;unsigned char nf_wqty; // (width/SWIDTH) +EXTERN nf_hqty ;: BYTE ;unsigned char nf_hqty; // (height/SHEIGHT) +EXTERN nf_fqty ;: BYTE ;unsigned char nf_fqty; // Number of fields +EXTERN nf_hicolor ;: DWORD ;unsigned nf_hicolor; // HiColor (0:none,1:normal,2:swapped) + +;; +EXTERN nf_width ;: DWORD ;unsigned nf_width; // wqty * SWIDTH +EXTERN nf_height ;: DWORD ;unsigned nf_height; // hqty * SHEIGHT; +EXTERN nf_new_line ;: DWORD ;unsigned nf_new_line; // width - SWIDTH +EXTERN nf_new_row0 ;: DWORD ;unsigned nf_new_row0; // SHEIGHT*width*2-width +EXTERN nf_back_right ;: DWORD ;unsigned nf_back_right; // (SHEIGHT-1)*width + +;; Frame parameters +;; Portion of current frame which has been updated +;; and needs to be sent to screen. +;; +EXTERN nf_new_x ;: DWORD ;unsigned nf_new_x; +EXTERN nf_new_y ;: DWORD ;unsigned nf_new_y; +EXTERN nf_new_w ;: DWORD ;unsigned nf_new_w; +EXTERN nf_new_h ;: DWORD ;unsigned nf_new_h; + +; These are all of our global parameter-passing variables - AH +extern sndDecompM16_dst +extern sndDecompM16_src +extern sndDecompM16_len +extern sndDecompM16_prev +extern sndDecompM16_return +extern sndDecompS16_dst +extern sndDecompS16_src +extern sndDecompS16_len +extern sndDecompS16_prev +extern sndDecompS16_return +extern nfHPkDecomp_ops +extern nfHPkDecomp_comp +extern nfHPkDecomp_x +extern nfHPkDecomp_y +extern nfHPkDecomp_w +extern nfHPkDecomp_h + +; This is the global array of pointers to memory locations that +; need to be self-modified - AH +extern global_unlock_memory_pointers + +; These are our functions that the C stubs call - AH +global _asm_sndDecompM16 +global _asm_sndDecompS16 +global _asm_nfPkConfig +global _asm_nfHPkDecomp + +; This is our memory "unlock" function for the self-mofiying asm - AH +global _asm_selfModify + +SECTION .data + + db "(c) 1997 Interplay Productions. All Rights Reserved.\n" + db "This file is confidential and consists of proprietary information\n" + db "of Interplay Productions. This file and associated libraries\n" + db "may not, in whole or in part, be disclosed to third parties,\n" + db "incorporated into any software product which is not being created\n" + db "for Interplay Productions, copied or duplicated in any form,\n" + db "without the prior written permission of Interplay Productions.\n" + db "Further, you may not reverse engineer, decompile or otherwise\n" + db "attempt to derive source code of this material.\n",0 + + tbuf dd 0 ;LOCAL tbuf : PTRBYTE + new_row dd 0 ;LOCAL new_row :DWORD + DiffBufPtrs dd 0 ;LOCAL DiffBufPtrs :DWORD + + nfpk_back_right dd 0 ;LOCAL nfpk_back_right : DWORD + wcnt dd 0 ;LOCAL wcnt :DWORD + bcomp dd 0 ;LOCAL bcomp :PTRBYTE + +nfhpk_OpTbl: ;label dword + dd nf0 ;dword offset nf0 ; Prev Same (0) + dd nf1 ;dword offset nf1 ; No change (and copied to screen) (0) + dd nf2 ;dword offset nf2 ; Near shift from older part of current buf (1) + dd nf3 ;dword offset nf3 ; Near shift from newer part of current buf (1) + dd nf4 ;dword offset nf4 ; Near shift from previous buffer (1) + dd nf5 ;dword offset nf5 ; Far shift from previous buffer (2) + dd nf6 ;dword offset nf6 ; Far shift from current buffer (2) + ; [Or if COMPOPS, run of no changes (0)] + dd nf7 ;dword offset nf7 ; 8x8x1 (10 bytes) or low 4x4x1 (4 bytes) + dd nf8 ;dword offset nf8 ; 2x2 4x4x1 (16 bytes) or 2x1 4x8x1 (12 bytes) or 1x2 8x4x1 (12 bytes) + dd nf9 ;dword offset nf9 ; 8x8x2 (20 bytes) or low 4x4x2 (8 bytes) or + ; low 4x8x2 (12 bytes) or low 8x4x2 (12 bytes) + dd nf10 ;dword offset nf10 ; 2x2 4x4x2 (32 bytes) or 2x1 4x8x2 (24 bytes) or 1x2 4x8x2 (24 bytes) + dd nf11 ;dword offset nf11 ; 8x8x8 (64 bytes) + dd nf12 ;dword offset nf12 ; low 4x4x8 (16 bytes) + dd nf13 ;dword offset nf13 ; 2x2 4x4x0 (ie 2x2x8) (4 bytes) + dd nf14 ;dword offset nf14 ; 8x8x0 (1 byte) + dd nf15 ;dword offset nf15 ; mix 8x8x0 (2 bytes) + +; signed 8-bit y * nf_width +nfpk_ShiftY times 256 dd 0 + +; Constant tables + +; 8-bit -8:7 x nf_width + -8:7 +nfpk_ShiftP1 + +%assign y -8 +%rep 16 ;16 + %assign x -8 + %rep 16 ;16 + db x,y + %assign x x+1 + %endrep +%assign y y+1 +%endrep + +; 8-bit to right and below in roughly 0:14*nf_width + -14:14 (-3 cases) +; negative is +; 8-bit to left and above in roughly -14:0*nf_width + -14:14 (-3 cases) +nfpk_ShiftP2 + +%assign y 0 +%rep 8 ;8 + %assign x 8 + %rep 7 ;7 + db x,y + %assign x x+1 + %endrep +%assign y y+1 +%endrep + +%assign y 8 +%rep 6 ;6 + + %assign x -14 + %rep 14 ;14 + db x,y + %assign x x+1 + %endrep + + %assign x 0 + %rep 15 ;15 + db x,y + %assign x x+1 + %endrep + +%assign y y+1 +%endrep + +%assign x -14 +%rep 14 ;14 + db x,14 +%assign x x+1 +%endrep + +%assign x 0 +%rep 12 ;12 + db x,14 +%assign x x+1 +%endrep + +; Constant tables +nfhpk_mov4l ;LABEL DWORD +; low 4x1 in 8x1 (patch +1) +; mov eax, ebx/ecx +db 0c0h+3, 0c0h+3, 0c0h+3, 0c0h+3 +db 0c0h+1, 0c0h+3, 0c0h+3, 0c0h+3 +db 0c0h+3, 0c0h+1, 0c0h+3, 0c0h+3 +db 0c0h+1, 0c0h+1, 0c0h+3, 0c0h+3 +db 0c0h+3, 0c0h+3, 0c0h+1, 0c0h+3 +db 0c0h+1, 0c0h+3, 0c0h+1, 0c0h+3 +db 0c0h+3, 0c0h+1, 0c0h+1, 0c0h+3 +db 0c0h+1, 0c0h+1, 0c0h+1, 0c0h+3 +db 0c0h+3, 0c0h+3, 0c0h+3, 0c0h+1 +db 0c0h+1, 0c0h+3, 0c0h+3, 0c0h+1 +db 0c0h+3, 0c0h+1, 0c0h+3, 0c0h+1 +db 0c0h+1, 0c0h+1, 0c0h+3, 0c0h+1 +db 0c0h+3, 0c0h+3, 0c0h+1, 0c0h+1 +db 0c0h+1, 0c0h+3, 0c0h+1, 0c0h+1 +db 0c0h+3, 0c0h+1, 0c0h+1, 0c0h+1 +db 0c0h+1, 0c0h+1, 0c0h+1, 0c0h+1 + +nfhpk_mov8 ;LABEL DWORD +; 8x1 (each two bits select a pair of colors in a reg) +; low 4x2 in 8x2 (each two bits select a duplicated color in reg) +; (patch +1) +; mov ds:[edi+0/4/8/12], ebx/edx/ecx/ebp +; Note: Patched code specifies mov [ebp+0]... instead +; of mov [edi+0]... to insure that 8-bit offsets are +; used by the assembler even for offset of zero. + +%assign m4 24 +%rep 4 + %assign m3 24 + %rep 4 + %assign m2 24 + %rep 4 + %assign m1 24 + %rep 4 + db m1+047h,m2+047h,m3+047h,m4+047h + %if (m1 == 24) + %assign m1 16 + %elif (m1 == 16) + %assign m1 8 + %elif (m1 == 8) + %assign m1 40 + %endif + %endrep + %if (m2 == 24) + %assign m2 16 + %elif (m2 == 16) + %assign m2 8 + %elif (m2 == 8) + %assign m2 40 + %endif + %endrep + %if (m3 == 24) + %assign m3 16 + %elif (m3 == 16) + %assign m3 8 + %elif (m3 == 8) + %assign m3 40 + %endif + %endrep + %if (m4 == 24) + %assign m4 16 + %elif (m4 == 16) + %assign m4 8 + %elif (m4 == 8) + %assign m4 40 + %endif +%endrep + +nfhpk_mov4 + +; 4x2 (patch +2) +; mov ax, bx/dx/cx/bp +; low 4x2 in 8x2 (patch +1) +; mov eax, ebx/edx/ecx/ebp +%assign m4 0c3h +%rep 4 + %assign m3 0c3h + %rep 4 + %assign m2 0c3h + %rep 4 + %assign m1 0c3h + %rep 4 + db m1,m2,m3,m4 + %if (m1 == 0c3h) + %assign m1 0c2h + %elif (m1 == 0c2h) + %assign m1 0c1h + %elif (m1 == 0c1h) + %assign m1 0c5h + %endif + %endrep + %if (m2 == 0c3h) + %assign m2 0c2h + %elif (m2 == 0c2h) + %assign m2 0c1h + %elif (m2 == 0c1h) + %assign m2 0c5h + %endif + %endrep + %if (m3 == 0c3h) + %assign m3 0c2h + %elif (m3 == 0c2h) + %assign m3 0c1h + %elif (m3 == 0c1h) + %assign m3 0c5h + %endif + %endrep + %if (m4 == 0c3h) + %assign m4 0c2h + %elif (m4 == 0c2h) + %assign m4 0c1h + %elif (m4 == 0c1h) + %assign m4 0c5h + %endif +%endrep + + +SEGMENT .text + +_asm_selfModify: + pushf + ; Move the pointer to the start of the pointer array into eax + mov eax, global_unlock_memory_pointers + + ; Load unlock addresses from _asm_nfHPkDecomp + mov dword [eax], nf7_0 + mov dword [eax + 4], nf8_0 + mov dword [eax + 8], nf9_0 + mov dword [eax + 12], nf10_0 + mov dword [eax + 16], nf23_0 + mov dword [eax + 20], nf24_0 + mov dword [eax + 24], nf25_0 + mov dword [eax + 28], nf26_0 + mov dword [eax + 32], nf42_0 + + popf + ret + +;-------------------------------------------------------------------- +; Sound Management +;-------------------- + +;unsigned sndDecompM16(unsigned short *dst, unsigned char *src, +; unsigned len, unsigned prev); +; +;Decompresses a mono stream containing len samples +;(src is len bytes, dst is len*2 bytes) +;prev is the previous decompression state or zero. +;Returns new decompression state. +; +_asm_sndDecompM16: ; PROC USES ESI EDI EBX, \ +; dst:PTRWORD, src:PTRBYTE, len:DWORD, prev:DWORD + mov eax, [sndDecompM16_prev] + + mov ecx, [sndDecompM16_len] + jecxz done + + mov esi, [sndDecompM16_src] + mov edi, [sndDecompM16_dst] + + xor ebx, ebx + +lp: mov bl, byte [esi] + add esi, 1 + add ax, word [snd_8to16 + ebx*2] + mov word [edi], ax + add edi, 2 + dec ecx + jnz lp + +done: + ; Store our return value - AH + mov dword [sndDecompM16_return], eax + + ret +;sndDecompM16 ENDP + +;unsigned sndDecompS16(unsigned short *dst, unsigned char *src, +; unsigned len, unsigned prev); +; +;Decompresses a stereo stream containing len samples +;(src is len*2 bytes, dst is len*4 bytes) +;prev is the previous decompression state or zero +; (It encodes the 16-bit states of the two stereo channels +; in its low and high order 16-bit halves.) +;Returns new decompression state. +; +_asm_sndDecompS16: ; PROC USES ESI EDI EBX, \ +; dst:PTRWORD, src:PTRBYTE, len:DWORD, prev:DWORD + movzx eax, word [sndDecompS16_prev] + movzx edx, word [sndDecompS16_prev+2] + + mov ecx, [sndDecompS16_len] + jecxz Sdone + + mov esi, [sndDecompS16_src] + mov edi, [sndDecompS16_dst] + + xor ebx, ebx + +Slp: mov bl, byte [esi] + add esi, 1 + add ax, word [snd_8to16 + ebx*2] + mov word [edi], ax + add edi, 2 + + mov bl, byte [esi] + add esi, 1 + add dx, word [snd_8to16 + ebx*2] + mov word [edi], dx + add edi, 2 + + dec ecx + jnz Slp + +Sdone: shl edx, 16 + or eax, edx + + ; Store our return value - AH + mov dword [sndDecompS16_return], eax + ret + +;sndDecompS16 ENDP + +;-------------------------------------------------------------------- +; NextFrame (Video Decompression) +;---------------------------------- + +%macro NF_DECOMP_INIT 1 ;HI_COLOR_FLAG: REQ + + mov eax, [nf_buf_prv] ;br ; DiffBufPtrs = nf_buf_prv - nf_buf_cur + sub eax, [nf_buf_cur] ;br + mov [DiffBufPtrs], eax + + xor ebx, ebx ; ebx = nf_fqty (convert to 32-bits) + mov bl, [nf_fqty] + + mov eax, [nfHPkDecomp_x] ;br ; nf_new_x = x*SWIDTH*2^HI_COLOR_FLAG; + shl eax, LOG2_SWIDTH+%1 ;HI_COLOR_FLAG + mov [nf_new_x], eax + + mov eax, [nfHPkDecomp_w] ;br ; nf_new_w = w*SWIDTH*2^HI_COLOR_FLAG; + shl eax, LOG2_SWIDTH+%1 ;HI_COLOR_FLAG + mov [nf_new_w], eax + + mov eax, [nfHPkDecomp_y] ;br ; nf_new_y = y*nf_fqty*SHEIGHT; + shl eax, LOG2_SHEIGHT + mul ebx ;nf_fqty + mov [nf_new_y], eax + + mov eax, [nfHPkDecomp_h] ;br ; nf_new_h = h*nf_fqty*SHEIGHT; + shl eax, LOG2_SHEIGHT + mul ebx ;nf_fqty + mov [nf_new_h], eax + + mov eax, [nf_new_row0] ;br ; new_row = nf_new_row0 - nf_new_w; + sub eax, [nf_new_w] ;br + mov [new_row], eax + + ;; Move to correct place in current buffer + mov eax, [nf_buf_cur] ;br ; tbuf = nf_buf_cur + mov [tbuf], eax +; %if (nfHPkDecomp_x || nfHPkDecomp_y) ; if (x||y) + ; cmp, jnz, jz and labels are mine - AH + cmp dword [nfHPkDecomp_x], 0 + jne before + cmp dword [nfHPkDecomp_y], 0 + je after +before: + mov eax, [nf_new_y] ;br ; tbuf += nf_new_y*nf_width + nf_new_x; + mul dword [nf_width] ; Added dword - AH + add eax, [nf_new_x] ;br + add [tbuf], eax +after: +; %endif + +%endmacro ; DECOMP_INIT + +;---------------------------------------------------------------------- + +; nfPkConfig initializes tables used by nfPkDecomp +; which are dependent on screen size. +_asm_nfPkConfig: ; PROC USES ESI EDI EBX + + ; Build ShiftY table + ; + lea edi, [nfpk_ShiftY] + mov ebx, [nf_width] + + mov eax, 0 + mov ecx, 128 +lp1: mov [edi], eax + add edi,4 + add eax,ebx + dec ecx + jne lp1 + + mov eax, ebx + shl eax, 7 + neg eax + mov ecx, 128 +lp2: mov [edi], eax + add edi,4 + add eax,ebx + dec ecx + jne lp2 + + ret +;nfPkConfig ENDP + +%macro Trans16_3 3 ; dst:req, idx:req, mask + xor eax, eax + mov al, [%2] + mov %1, [nf_trans16_lo + eax*2] + xor eax, eax + mov al, [%2+1] + or %1, [nf_trans16_hi + eax*2] +%endmacro + +%macro Trans16 2 ; dst:req, idx:req + xor eax, eax + mov al, [%2] + mov %1, [nf_trans16_lo + eax*2] + xor eax, eax + mov al, [%2+1] + or %1, [nf_trans16_hi + eax*2] +%endmacro + +_asm_nfHPkDecomp: ; PROC USES ESI EDI EBX, \ +; ops:PTRBYTE, comp:PTRBYTE, \ +; x:DWORD, y:DWORD, w:DWORD, h:DWORD + + NF_DECOMP_INIT 1 + + mov eax, [nf_back_right] ;br + sub eax, SWIDTH*2 + mov [nfpk_back_right], eax + + mov esi, [nfHPkDecomp_comp] ;br + mov edi, [tbuf] + + xor eax, eax + mov ax, [esi] + add eax, esi + mov [bcomp], eax + add esi, 2 + +nf_StartRow: + mov eax, [nfHPkDecomp_w] ;br + shr eax, 1 + mov [wcnt],eax + ALIGN 4 +nf_NextPair: + dec dword [wcnt] ; Added dword - AH + js nf_NextRow + mov ebx, [nfHPkDecomp_ops] ; br + mov al, [ebx] + inc ebx + mov [nfHPkDecomp_ops], ebx + + xor ebx, ebx + mov bl, al + shr bl, 4 + and eax, 0Fh + push dword nf_NextPair ; Added dword on these two - AH + push dword [nfhpk_OpTbl + ebx*4] + jmp [nfhpk_OpTbl + eax*4] + +nf_NextRow: + add edi, [new_row] ;br + dec dword [nfHPkDecomp_h] ; Added dword - AH + jnz nf_StartRow + + ret + +;---------------------------------------- + ALIGN 4 +nf0: ; No change from previous buffer + mov eax, [DiffBufPtrs] ; br + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf1: ; No change (and copied to screen) + + add edi, SWIDTH*2 + retn + +;---------------------------------------- + ALIGN 4 +nf2: ; Near shift from older part of current buffer + xor eax, eax + mov ebx, [bcomp] ; br + inc dword [bcomp] ; Added dword - AH + mov al, [ebx] + mov ax, [nfpk_ShiftP2 + eax*2] +nf_xyc_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24-1 + add eax, [nfpk_ShiftY + ebx*4] + jmp nf_shift + +;---------------------------------------- + ALIGN 4 +nf3: ; Near shift from newer part of current buffer + xor eax, eax + mov ebx, [bcomp] ; br + inc dword [bcomp] ; Added dword - AH + mov al, [ebx] + mov ax, [nfpk_ShiftP2 + eax*2] + neg al + neg ah + jmp nf_xyc_shift + +;---------------------------------------- + ALIGN 4 +nf4: ; Near shift from previous buffer + xor eax, eax + mov ebx, [bcomp] ; br + inc dword [bcomp] ; Added dword - AH + mov al, [ebx] + mov ax, [nfpk_ShiftP1 + eax*2] + jmp nf_xyp_shift + +;---------------------------------------- + ALIGN 4 +nf5: ; Far shift from previous buffer + mov ax, [esi] + add esi, 2 +nf_xyp_shift: + xor ebx, ebx + mov bl, ah + shl eax, 24 + sar eax, 24-1 + add eax, [nfpk_ShiftY + ebx*4] + add eax, [DiffBufPtrs] ; br + jmp nf_shift + +;---------------------------------------- + ALIGN 4 + +nf6: ; Far shift from current buffer + mov ax, [esi] + add esi, 2 + jmp nf_xyc_shift + +;---------------------------------------- + ALIGN 4 +nf_shift: + + mov ebx, esi ; save esi + lea esi, [edi+eax] + mov edx, [nf_width] + + %rep 7 + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + mov eax, [esi+8] + mov [edi+8], eax + mov eax, [esi+12] + mov [edi+12], eax + add esi, edx + add edi, edx + %endrep + mov eax, [esi] + mov [edi], eax + mov eax, [esi+4] + mov [edi+4], eax + mov eax, [esi+8] + mov [edi+8], eax + mov eax, [esi+12] + mov [edi+12], eax + + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + mov esi, ebx ; restore esi + + retn + +;---------------------------------------- + ALIGN 4 +nf7: ; 8x8x1 (12 bytes) + test word [esi], 08000h + jnz near nf23 + + xor eax, eax + + lea ecx, [nfhpk_mov8] + lea edx, [nf7_11+1] ; Removed byte ds:- AH + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_11-nf7_11)], bl + mov [edx+(nf7_12-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_13-nf7_11)], bl + mov [edx+(nf7_14-nf7_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_21-nf7_11)], bl + mov [edx+(nf7_22-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_23-nf7_11)], bl + mov [edx+(nf7_24-nf7_11)], bh + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_31-nf7_11)], bl + mov [edx+(nf7_32-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_33-nf7_11)], bl + mov [edx+(nf7_34-nf7_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_41-nf7_11)], bl + mov [edx+(nf7_42-nf7_11)], bh + shr ebx, 16 + mov [edx+(nf7_43-nf7_11)], bl + mov [edx+(nf7_44-nf7_11)], bh + + lea edx, [edx+(nf7_51-nf7_11)] + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_51-nf7_51)], bl + mov [edx+(nf7_52-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_53-nf7_51)], bl + mov [edx+(nf7_54-nf7_51)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_61-nf7_51)], bl + mov [edx+(nf7_62-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_63-nf7_51)], bl + mov [edx+(nf7_64-nf7_51)], bh + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_71-nf7_51)], bl + mov [edx+(nf7_72-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_73-nf7_51)], bl + mov [edx+(nf7_74-nf7_51)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf7_81-nf7_51)], bl + mov [edx+(nf7_82-nf7_51)], bh + shr ebx, 16 + mov [edx+(nf7_83-nf7_51)], bl + mov [edx+(nf7_84-nf7_51)], bh + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + + Trans16 cx, esi+2 + shl ecx, 16 + Trans16 cx, esi + + mov esi,[nf_width] + mov edx, ecx + + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf7_0 ; flush prefetch + ALIGN 4 +nf7_0: +nf7_11: mov [ebp+0], ebx +nf7_12: mov [ebp+4], ebx +nf7_13: mov [ebp+8], ebx +nf7_14: mov [ebp+12], ebx + add edi, esi + +nf7_21: mov [ebp+0], ebx +nf7_22: mov [ebp+4], ebx +nf7_23: mov [ebp+8], ebx +nf7_24: mov [ebp+12], ebx + add edi, esi + +nf7_31: mov [ebp+0], ebx +nf7_32: mov [ebp+4], ebx +nf7_33: mov [ebp+8], ebx +nf7_34: mov [ebp+12], ebx + add edi, esi + +nf7_41: mov [ebp+0], ebx +nf7_42: mov [ebp+4], ebx +nf7_43: mov [ebp+8], ebx +nf7_44: mov [ebp+12], ebx + add edi, esi + +nf7_51: mov [ebp+0], ebx +nf7_52: mov [ebp+4], ebx +nf7_53: mov [ebp+8], ebx +nf7_54: mov [ebp+12], ebx + add edi, esi + +nf7_61: mov [ebp+0], ebx +nf7_62: mov [ebp+4], ebx +nf7_63: mov [ebp+8], ebx +nf7_64: mov [ebp+12], ebx + add edi, esi + +nf7_71: mov [ebp+0], ebx +nf7_72: mov [ebp+4], ebx +nf7_73: mov [ebp+8], ebx +nf7_74: mov [ebp+12], ebx + add edi, esi + +nf7_81: mov [ebp+0], ebx +nf7_82: mov [ebp+4], ebx +nf7_83: mov [ebp+8], ebx +nf7_84: mov [ebp+12], ebx + + pop esi + pop ebp + add esi, 12 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf7+16 +nf23: ; low 4x4x1 (6 bytes) + + xor eax, eax + lea ecx, [nfhpk_mov4l] + lea edx, [nf23_11+1] ; Removed byte ds: - AH + + mov al, [esi+4] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_11-nf23_11)], bl + mov [edx+(nf23_12-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_13-nf23_11)], bl + mov [edx+(nf23_14-nf23_11)], bh + + mov al, [esi+4] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_31-nf23_11)], bl + mov [edx+(nf23_32-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_33-nf23_11)], bl + mov [edx+(nf23_34-nf23_11)], bh + + + mov al, [esi+5] + and al, 0fH + mov ebx, [ecx+eax*4] + mov [edx+(nf23_51-nf23_11)], bl + mov [edx+(nf23_52-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_53-nf23_11)], bl + mov [edx+(nf23_54-nf23_11)], bh + + mov al, [esi+5] + shr al, 4 + mov ebx, [ecx+eax*4] + mov [edx+(nf23_71-nf23_11)], bl + mov [edx+(nf23_72-nf23_11)], bh + shr ebx, 16 + mov [edx+(nf23_73-nf23_11)], bl + mov [edx+(nf23_74-nf23_11)], bh + + mov edx, [nf_width] + + ; load ebx,ecx with 00,11 color combinations + + Trans16_3 cx, esi, 1 + shrd ebx, ecx, 16 + mov bx, cx + Trans16 cx, esi+2 + shrd eax, ecx, 16 + mov ax, cx + mov ecx, eax + + jmp nf23_0 ; flush prefetch + ALIGN 4 +nf23_0: + +nf23_11:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_12:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_13:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_14:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + lea edi, [edi+edx*2] + +nf23_31:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_32:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_33:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_34:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + lea edi, [edi+edx*2] + +nf23_51:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_52:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_53:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_54:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + lea edi, [edi+edx*2] + +nf23_71:mov eax, ebx + mov [edi], eax + mov [edi+edx], eax +nf23_72:mov eax, ebx + mov [edi+4], eax + mov [edi+edx+4], eax +nf23_73:mov eax, ebx + mov [edi+8], eax + mov [edi+edx+8], eax +nf23_74:mov eax, ebx + mov [edi+12], eax + mov [edi+edx+12], eax + add edi, edx + + sub edi, [nfpk_back_right] + add esi, 6 + retn + +;---------------------------------------- + ALIGN 4 +nf8: ; 2x2 4x4x1 (24 bytes) + test word [esi], 08000h + jnz near nf24 + + xor eax, eax + + lea ecx, [nfhpk_mov8] + lea edx, [nf8_11+1] ; Removed byte ds: - AH + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_11-nf8_11)], bl + mov [edx+(nf8_12-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_13-nf8_11)], bl + mov [edx+(nf8_14-nf8_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_21-nf8_11)], bl + mov [edx+(nf8_22-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_23-nf8_11)], bl + mov [edx+(nf8_24-nf8_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_31-nf8_11)], bl + mov [edx+(nf8_32-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_33-nf8_11)], bl + mov [edx+(nf8_34-nf8_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_41-nf8_11)], bl + mov [edx+(nf8_42-nf8_11)], bh + shr ebx, 16 + mov [edx+(nf8_43-nf8_11)], bl + mov [edx+(nf8_44-nf8_11)], bh + + add edx, nf8_51-nf8_11 + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_51-nf8_51)], bl + mov [edx+(nf8_52-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_53-nf8_51)], bl + mov [edx+(nf8_54-nf8_51)], bh + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_61-nf8_51)], bl + mov [edx+(nf8_62-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_63-nf8_51)], bl + mov [edx+(nf8_64-nf8_51)], bh + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_71-nf8_51)], bl + mov [edx+(nf8_72-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_73-nf8_51)], bl + mov [edx+(nf8_74-nf8_51)], bh + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf8_81-nf8_51)], bl + mov [edx+(nf8_82-nf8_51)], bh + shr ebx, 16 + mov [edx+(nf8_83-nf8_51)], bl + mov [edx+(nf8_84-nf8_51)], bh + + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + + Trans16 cx, esi+18+2 + shl ecx, 16 + Trans16 cx, esi+18 + push ecx + + Trans16 cx, esi+12+2 + shl ecx, 16 + Trans16 cx, esi+12 + push ecx + + Trans16 cx, esi+6+2 + shl ecx, 16 + Trans16 cx, esi+6 + push ecx + + Trans16 cx, esi+2 + shl ecx, 16 + Trans16 cx, esi + + mov esi,[nf_width] + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf8_0 ; flush prefetch + ALIGN 4 +nf8_0: +nf8_11: mov [ebp+0], ebx +nf8_12: mov [ebp+4], ebx + add edi, esi +nf8_13: mov [ebp+0], ebx +nf8_14: mov [ebp+4], ebx + add edi, esi + +nf8_21: mov [ebp+0], ebx +nf8_22: mov [ebp+4], ebx + add edi, esi +nf8_23: mov [ebp+0], ebx +nf8_24: mov [ebp+4], ebx + add edi, esi + + pop ecx + + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + +nf8_31: mov [ebp+0], ebx +nf8_32: mov [ebp+4], ebx + add edi, esi +nf8_33: mov [ebp+0], ebx +nf8_34: mov [ebp+4], ebx + add edi, esi + +nf8_41: mov [ebp+0], ebx +nf8_42: mov [ebp+4], ebx + add edi, esi +nf8_43: mov [ebp+0], ebx +nf8_44: mov [ebp+4], ebx + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + + pop ecx + + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf8_51: mov [ebp+0], ebx +nf8_52: mov [ebp+4], ebx + add edi, esi +nf8_53: mov [ebp+0], ebx +nf8_54: mov [ebp+4], ebx + add edi, esi + +nf8_61: mov [ebp+0], ebx +nf8_62: mov [ebp+4], ebx + add edi, esi +nf8_63: mov [ebp+0], ebx +nf8_64: mov [ebp+4], ebx + add edi, esi + + pop ecx + + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf8_71: mov [ebp+0], ebx +nf8_72: mov [ebp+4], ebx + add edi, esi +nf8_73: mov [ebp+0], ebx +nf8_74: mov [ebp+4], ebx + add edi, esi + +nf8_81: mov [ebp+0], ebx +nf8_82: mov [ebp+4], ebx + add edi, esi +nf8_83: mov [ebp+0], ebx +nf8_84: mov [ebp+4], ebx + + pop esi + pop ebp + add esi, 24 + sub edi, 8 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+16 +nf24: ; 2x1 4x8x1 (16 bytes) + + test word [esi+8], 08000h + jnz near nf40 + + xor eax, eax + + lea ecx, [nfhpk_mov8] + lea edx, [nf24_11+1] ; Removed byte ds: - AH + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_11-nf24_11)], bl + mov [edx+(nf24_12-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_13-nf24_11)], bl + mov [edx+(nf24_14-nf24_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_21-nf24_11)], bl + mov [edx+(nf24_22-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_23-nf24_11)], bl + mov [edx+(nf24_24-nf24_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_31-nf24_11)], bl + mov [edx+(nf24_32-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_33-nf24_11)], bl + mov [edx+(nf24_34-nf24_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_41-nf24_11)], bl + mov [edx+(nf24_42-nf24_11)], bh + shr ebx, 16 + mov [edx+(nf24_43-nf24_11)], bl + mov [edx+(nf24_44-nf24_11)], bh + + add edx, nf24_51-nf24_11 + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_51-nf24_51)], bl + mov [edx+(nf24_52-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_53-nf24_51)], bl + mov [edx+(nf24_54-nf24_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_61-nf24_51)], bl + mov [edx+(nf24_62-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_63-nf24_51)], bl + mov [edx+(nf24_64-nf24_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_71-nf24_51)], bl + mov [edx+(nf24_72-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_73-nf24_51)], bl + mov [edx+(nf24_74-nf24_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf24_81-nf24_51)], bl + mov [edx+(nf24_82-nf24_51)], bh + shr ebx, 16 + mov [edx+(nf24_83-nf24_51)], bl + mov [edx+(nf24_84-nf24_51)], bh + + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + + Trans16 cx, esi+8+2 + shl ecx, 16 + Trans16 cx, esi+8 + push ecx + + Trans16 cx, esi+2 + shl ecx, 16 + Trans16_3 cx, esi, 1 + + mov esi,[nf_width] + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf24_0 ; flush prefetch + ALIGN 4 +nf24_0: + +nf24_11:mov [ebp+0], ebx +nf24_12:mov [ebp+4], ebx + add edi, esi +nf24_13:mov [ebp+0], ebx +nf24_14:mov [ebp+4], ebx + add edi, esi + +nf24_21:mov [ebp+0], ebx +nf24_22:mov [ebp+4], ebx + add edi, esi +nf24_23:mov [ebp+0], ebx +nf24_24:mov [ebp+4], ebx + add edi, esi + +nf24_31:mov [ebp+0], ebx +nf24_32:mov [ebp+4], ebx + add edi, esi +nf24_33:mov [ebp+0], ebx +nf24_34:mov [ebp+4], ebx + add edi, esi + +nf24_41:mov [ebp+0], ebx +nf24_42:mov [ebp+4], ebx + add edi, esi +nf24_43:mov [ebp+0], ebx +nf24_44:mov [ebp+4], ebx + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + + pop ecx + + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf24_51:mov [ebp+0], ebx +nf24_52:mov [ebp+4], ebx + add edi, esi +nf24_53:mov [ebp+0], ebx +nf24_54:mov [ebp+4], ebx + add edi, esi + +nf24_61:mov [ebp+0], ebx +nf24_62:mov [ebp+4], ebx + add edi, esi +nf24_63:mov [ebp+0], ebx +nf24_64:mov [ebp+4], ebx + add edi, esi + +nf24_71:mov [ebp+0], ebx +nf24_72:mov [ebp+4], ebx + add edi, esi +nf24_73:mov [ebp+0], ebx +nf24_74:mov [ebp+4], ebx + add edi, esi + +nf24_81:mov [ebp+0], ebx +nf24_82:mov [ebp+4], ebx + add edi, esi +nf24_83:mov [ebp+0], ebx +nf24_84:mov [ebp+4], ebx + + pop esi + pop ebp + add esi, 16 + sub edi, 8 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf8+32 +nf40: ; 1x2 8x4x1 (16 bytes) + + xor eax, eax + + lea ecx, [nfhpk_mov8] + lea edx, [nf40_11+1] ; Removed byte ds: - AH + + mov al, [esi+4] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_11-nf40_11)], bl + mov [edx+(nf40_12-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_13-nf40_11)], bl + mov [edx+(nf40_14-nf40_11)], bh + + mov al, [esi+5] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_21-nf40_11)], bl + mov [edx+(nf40_22-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_23-nf40_11)], bl + mov [edx+(nf40_24-nf40_11)], bh + + + mov al, [esi+6] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_31-nf40_11)], bl + mov [edx+(nf40_32-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_33-nf40_11)], bl + mov [edx+(nf40_34-nf40_11)], bh + + mov al, [esi+7] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_41-nf40_11)], bl + mov [edx+(nf40_42-nf40_11)], bh + shr ebx, 16 + mov [edx+(nf40_43-nf40_11)], bl + mov [edx+(nf40_44-nf40_11)], bh + + add edx, nf40_51-nf40_11 + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_51-nf40_51)], bl + mov [edx+(nf40_52-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_53-nf40_51)], bl + mov [edx+(nf40_54-nf40_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_61-nf40_51)], bl + mov [edx+(nf40_62-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_63-nf40_51)], bl + mov [edx+(nf40_64-nf40_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_71-nf40_51)], bl + mov [edx+(nf40_72-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_73-nf40_51)], bl + mov [edx+(nf40_74-nf40_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf40_81-nf40_51)], bl + mov [edx+(nf40_82-nf40_51)], bh + shr ebx, 16 + mov [edx+(nf40_83-nf40_51)], bl + mov [edx+(nf40_84-nf40_51)], bh + + + push ebp + push esi + ; load ebx,edx,ecx,ebp with 00,01,10,11 color combinations + ; (note that bits are read least significant first). + + Trans16 cx, esi+8+2 + shl ecx, 16 + Trans16_3 cx, esi+8, 1 + push ecx + + Trans16 cx, esi+2 + shl ecx, 16 + Trans16_3 cx, esi, 1 + + mov esi,[nf_width] + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + + jmp nf40_0 ; flush prefetch + ALIGN 4 +nf40_0: + +nf40_11:mov [ebp+0], ebx +nf40_12:mov [ebp+4], ebx +nf40_13:mov [ebp+8], ebx +nf40_14:mov [ebp+12], ebx + add edi, esi + +nf40_21:mov [ebp+0], ebx +nf40_22:mov [ebp+4], ebx +nf40_23:mov [ebp+8], ebx +nf40_24:mov [ebp+12], ebx + add edi, esi + +nf40_31:mov [ebp+0], ebx +nf40_32:mov [ebp+4], ebx +nf40_33:mov [ebp+8], ebx +nf40_34:mov [ebp+12], ebx + add edi, esi + +nf40_41:mov [ebp+0], ebx +nf40_42:mov [ebp+4], ebx +nf40_43:mov [ebp+8], ebx +nf40_44:mov [ebp+12], ebx + add edi, esi + + pop ecx + + mov edx, ecx + ror edx, 16 + mov ebx, edx + mov bx, cx + mov ebp, ecx + mov bp, dx + +nf40_51:mov [ebp+0], ebx +nf40_52:mov [ebp+4], ebx +nf40_53:mov [ebp+8], ebx +nf40_54:mov [ebp+12], ebx + add edi, esi + +nf40_61:mov [ebp+0], ebx +nf40_62:mov [ebp+4], ebx +nf40_63:mov [ebp+8], ebx +nf40_64:mov [ebp+12], ebx + add edi, esi + +nf40_71:mov [ebp+0], ebx +nf40_72:mov [ebp+4], ebx +nf40_73:mov [ebp+8], ebx +nf40_74:mov [ebp+12], ebx + add edi, esi + +nf40_81:mov [ebp+0], ebx +nf40_82:mov [ebp+4], ebx +nf40_83:mov [ebp+8], ebx +nf40_84:mov [ebp+12], ebx + + pop esi + pop ebp + add esi, 16 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +nf9: ; 8x8x2 (24 bytes) + + test word [esi], 08000h + jnz near nf41 + + test word [esi+4], 08000h + jnz near nf25 + + xor eax, eax + + lea ecx, [nfhpk_mov4] + lea edx, [nf9_11+2] ; Removed byte ds: - AH + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_11-nf9_11)], bh + mov [edx+(nf9_12-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_13-nf9_11)], bh + mov [edx+(nf9_14-nf9_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_15-nf9_11)], bh + mov [edx+(nf9_16-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_17-nf9_11)], bh + mov [edx+(nf9_18-nf9_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_21-nf9_11)], bh + mov [edx+(nf9_22-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_23-nf9_11)], bh + mov [edx+(nf9_24-nf9_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_25-nf9_11)], bh + mov [edx+(nf9_26-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_27-nf9_11)], bh + mov [edx+(nf9_28-nf9_11)], bl + + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_31-nf9_11)], bh + mov [edx+(nf9_32-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_33-nf9_11)], bh + mov [edx+(nf9_34-nf9_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_35-nf9_11)], bh + mov [edx+(nf9_36-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_37-nf9_11)], bh + mov [edx+(nf9_38-nf9_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_41-nf9_11)], bh + mov [edx+(nf9_42-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_43-nf9_11)], bh + mov [edx+(nf9_44-nf9_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_45-nf9_11)], bh + mov [edx+(nf9_46-nf9_11)], bl + shr ebx, 16 + mov [edx+(nf9_47-nf9_11)], bh + mov [edx+(nf9_48-nf9_11)], bl + + + lea edx, [edx+(nf9_51-nf9_11)] + + mov al, [esi+16] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_51-nf9_51)], bh + mov [edx+(nf9_52-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_53-nf9_51)], bh + mov [edx+(nf9_54-nf9_51)], bl + + mov al, [esi+17] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_55-nf9_51)], bh + mov [edx+(nf9_56-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_57-nf9_51)], bh + mov [edx+(nf9_58-nf9_51)], bl + + + mov al, [esi+18] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_61-nf9_51)], bh + mov [edx+(nf9_62-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_63-nf9_51)], bh + mov [edx+(nf9_64-nf9_51)], bl + + mov al, [esi+19] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_65-nf9_51)], bh + mov [edx+(nf9_66-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_67-nf9_51)], bh + mov [edx+(nf9_68-nf9_51)], bl + + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_71-nf9_51)], bh + mov [edx+(nf9_72-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_73-nf9_51)], bh + mov [edx+(nf9_74-nf9_51)], bl + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_75-nf9_51)], bh + mov [edx+(nf9_76-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_77-nf9_51)], bh + mov [edx+(nf9_78-nf9_51)], bl + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_81-nf9_51)], bh + mov [edx+(nf9_82-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_83-nf9_51)], bh + mov [edx+(nf9_84-nf9_51)], bl + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf9_85-nf9_51)], bh + mov [edx+(nf9_86-nf9_51)], bl + shr ebx, 16 + mov [edx+(nf9_87-nf9_51)], bh + mov [edx+(nf9_88-nf9_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors + + Trans16 bx, esi + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 + + mov esi, [nf_width] + + jmp nf9_0 ; flush prefetch + ALIGN 4 +nf9_0: + +nf9_11: mov ax, bx + shl eax, 16 +nf9_12: mov ax, bx + mov [edi], eax +nf9_13: mov ax, bx + shl eax, 16 +nf9_14: mov ax, bx + mov [edi+4], eax +nf9_15: mov ax, bx + shl eax, 16 +nf9_16: mov ax, bx + mov [edi+8], eax +nf9_17: mov ax, bx + shl eax, 16 +nf9_18: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_21: mov ax, bx + shl eax, 16 +nf9_22: mov ax, bx + mov [edi], eax +nf9_23: mov ax, bx + shl eax, 16 +nf9_24: mov ax, bx + mov [edi+4], eax +nf9_25: mov ax, bx + shl eax, 16 +nf9_26: mov ax, bx + mov [edi+8], eax +nf9_27: mov ax, bx + shl eax, 16 +nf9_28: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_31: mov ax, bx + shl eax, 16 +nf9_32: mov ax, bx + mov [edi], eax +nf9_33: mov ax, bx + shl eax, 16 +nf9_34: mov ax, bx + mov [edi+4], eax +nf9_35: mov ax, bx + shl eax, 16 +nf9_36: mov ax, bx + mov [edi+8], eax +nf9_37: mov ax, bx + shl eax, 16 +nf9_38: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_41: mov ax, bx + shl eax, 16 +nf9_42: mov ax, bx + mov [edi], eax +nf9_43: mov ax, bx + shl eax, 16 +nf9_44: mov ax, bx + mov [edi+4], eax +nf9_45: mov ax, bx + shl eax, 16 +nf9_46: mov ax, bx + mov [edi+8], eax +nf9_47: mov ax, bx + shl eax, 16 +nf9_48: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_51: mov ax, bx + shl eax, 16 +nf9_52: mov ax, bx + mov [edi], eax +nf9_53: mov ax, bx + shl eax, 16 +nf9_54: mov ax, bx + mov [edi+4], eax +nf9_55: mov ax, bx + shl eax, 16 +nf9_56: mov ax, bx + mov [edi+8], eax +nf9_57: mov ax, bx + shl eax, 16 +nf9_58: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_61: mov ax, bx + shl eax, 16 +nf9_62: mov ax, bx + mov [edi], eax +nf9_63: mov ax, bx + shl eax, 16 +nf9_64: mov ax, bx + mov [edi+4], eax +nf9_65: mov ax, bx + shl eax, 16 +nf9_66: mov ax, bx + mov [edi+8], eax +nf9_67: mov ax, bx + shl eax, 16 +nf9_68: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_71: mov ax, bx + shl eax, 16 +nf9_72: mov ax, bx + mov [edi], eax +nf9_73: mov ax, bx + shl eax, 16 +nf9_74: mov ax, bx + mov [edi+4], eax +nf9_75: mov ax, bx + shl eax, 16 +nf9_76: mov ax, bx + mov [edi+8], eax +nf9_77: mov ax, bx + shl eax, 16 +nf9_78: mov ax, bx + mov [edi+12], eax + add edi, esi + +nf9_81: mov ax, bx + shl eax, 16 +nf9_82: mov ax, bx + mov [edi], eax +nf9_83: mov ax, bx + shl eax, 16 +nf9_84: mov ax, bx + mov [edi+4], eax +nf9_85: mov ax, bx + shl eax, 16 +nf9_86: mov ax, bx + mov [edi+8], eax +nf9_87: mov ax, bx + shl eax, 16 +nf9_88: mov ax, bx + mov [edi+12], eax + + pop esi + pop ebp + add esi, 24 + sub edi, [nfpk_back_right] ; br + retn + +;---------------------------------------- + ALIGN 4 +;nf9+16 +nf25: ; low 4x4x2 (12 bytes) + + xor eax, eax + + lea ecx, [nfhpk_mov4] + lea edx, [nf25_11+1] ; Removed byte ds: - AH + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_11-nf25_11)], bl + mov [edx+(nf25_12-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_13-nf25_11)], bl + mov [edx+(nf25_14-nf25_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_21-nf25_11)], bl + mov [edx+(nf25_22-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_23-nf25_11)], bl + mov [edx+(nf25_24-nf25_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_31-nf25_11)], bl + mov [edx+(nf25_32-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_33-nf25_11)], bl + mov [edx+(nf25_34-nf25_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf25_41-nf25_11)], bl + mov [edx+(nf25_42-nf25_11)], bh + shr ebx, 16 + mov [edx+(nf25_43-nf25_11)], bl + mov [edx+(nf25_44-nf25_11)], bh + + push ebp + push esi + ; Load ebx,edx,ecx,ebp with four colors, duplicated in high order. + + Trans16 cx, esi + shrd ebx, ecx, 16 + mov bx, cx + Trans16 cx, esi+2 + shrd edx, ecx, 16 + mov dx, cx + Trans16_3 cx, esi+4, 1 + shrd eax, ecx, 16 + mov ax, cx + push eax + Trans16 cx, esi+6 + shrd ebp, ecx, 16 + mov bp, cx + pop ecx + + mov esi, [nf_width] + + jmp nf25_0 ; flush prefetch + ALIGN 4 +nf25_0: + +nf25_11:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_12:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_13:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_14:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf25_21:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_22:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_23:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_24:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf25_31:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_32:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_33:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_34:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf25_41:mov eax, ebx + mov [edi], eax + mov [edi+esi], eax +nf25_42:mov eax, ebx + mov [edi+4], eax + mov [edi+esi+4], eax +nf25_43:mov eax, ebx + mov [edi+8], eax + mov [edi+esi+8], eax +nf25_44:mov eax, ebx + mov [edi+12], eax + mov [edi+esi+12], eax + + add edi, esi + + pop esi + pop ebp + add esi, 12 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+32 +nf41: ; low 4x8x2 (16 bytes) + test word [esi+4], 08000h + jnz near nf57 + + xor eax, eax + + lea ecx, [nfhpk_mov8] + lea edx, [nf41_11+1] ; Removed byte ds: - AH + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_11-nf41_11)], bl + mov [edx+(nf41_12-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_13-nf41_11)], bl + mov [edx+(nf41_14-nf41_11)], bh + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_21-nf41_11)], bl + mov [edx+(nf41_22-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_23-nf41_11)], bl + mov [edx+(nf41_24-nf41_11)], bh + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_31-nf41_11)], bl + mov [edx+(nf41_32-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_33-nf41_11)], bl + mov [edx+(nf41_34-nf41_11)], bh + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_41-nf41_11)], bl + mov [edx+(nf41_42-nf41_11)], bh + shr ebx, 16 + mov [edx+(nf41_43-nf41_11)], bl + mov [edx+(nf41_44-nf41_11)], bh + + lea edx, [edx+(nf41_51-nf41_11)] + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_51-nf41_51)], bl + mov [edx+(nf41_52-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_53-nf41_51)], bl + mov [edx+(nf41_54-nf41_51)], bh + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_61-nf41_51)], bl + mov [edx+(nf41_62-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_63-nf41_51)], bl + mov [edx+(nf41_64-nf41_51)], bh + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_71-nf41_51)], bl + mov [edx+(nf41_72-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_73-nf41_51)], bl + mov [edx+(nf41_74-nf41_51)], bh + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf41_81-nf41_51)], bl + mov [edx+(nf41_82-nf41_51)], bh + shr ebx, 16 + mov [edx+(nf41_83-nf41_51)], bl + mov [edx+(nf41_84-nf41_51)], bh + + push ebp + push esi + ; Load ebx,edx,ecx,ebp with four colors, duplicated in high order. + + Trans16_3 cx, esi, 1 + shrd ebx, ecx, 16 + mov bx, cx + Trans16 cx, esi+2 + shrd edx, ecx, 16 + mov dx, cx + Trans16 cx, esi+4 + shrd eax, ecx, 16 + mov ax, cx + push eax + Trans16 cx, esi+6 + shrd ebp, ecx, 16 + mov bp, cx + pop ecx + + mov esi, [nf_width] + + jmp nf41_0 ; flush prefetch + ALIGN 4 +nf41_0: + +nf41_11:mov [ebp+0], ebx +nf41_12:mov [ebp+4], ebx +nf41_13:mov [ebp+8], ebx +nf41_14:mov [ebp+12], ebx + add edi, esi + +nf41_21:mov [ebp+0], ebx +nf41_22:mov [ebp+4], ebx +nf41_23:mov [ebp+8], ebx +nf41_24:mov [ebp+12], ebx + add edi, esi + +nf41_31:mov [ebp+0], ebx +nf41_32:mov [ebp+4], ebx +nf41_33:mov [ebp+8], ebx +nf41_34:mov [ebp+12], ebx + add edi, esi + +nf41_41:mov [ebp+0], ebx +nf41_42:mov [ebp+4], ebx +nf41_43:mov [ebp+8], ebx +nf41_44:mov [ebp+12], ebx + add edi, esi + +nf41_51:mov [ebp+0], ebx +nf41_52:mov [ebp+4], ebx +nf41_53:mov [ebp+8], ebx +nf41_54:mov [ebp+12], ebx + add edi, esi + +nf41_61:mov [ebp+0], ebx +nf41_62:mov [ebp+4], ebx +nf41_63:mov [ebp+8], ebx +nf41_64:mov [ebp+12], ebx + add edi, esi + +nf41_71:mov [ebp+0], ebx +nf41_72:mov [ebp+4], ebx +nf41_73:mov [ebp+8], ebx +nf41_74:mov [ebp+12], ebx + add edi, esi + +nf41_81:mov [ebp+0], ebx +nf41_82:mov [ebp+4], ebx +nf41_83:mov [ebp+8], ebx +nf41_84:mov [ebp+12], ebx + + pop esi + pop ebp + add esi, 16 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf9+48 +nf57: ; low 8x4x2 (16 bytes) + + xor eax, eax + + lea ecx, [nfhpk_mov4] + lea edx, [nf57_11+2] ; Removed byte ds: - AH + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_11-nf57_11)], bh + mov [edx+(nf57_12-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_13-nf57_11)], bh + mov [edx+(nf57_14-nf57_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_15-nf57_11)], bh + mov [edx+(nf57_16-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_17-nf57_11)], bh + mov [edx+(nf57_18-nf57_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_21-nf57_11)], bh + mov [edx+(nf57_22-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_23-nf57_11)], bh + mov [edx+(nf57_24-nf57_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_25-nf57_11)], bh + mov [edx+(nf57_26-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_27-nf57_11)], bh + mov [edx+(nf57_28-nf57_11)], bl + + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_31-nf57_11)], bh + mov [edx+(nf57_32-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_33-nf57_11)], bh + mov [edx+(nf57_34-nf57_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_35-nf57_11)], bh + mov [edx+(nf57_36-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_37-nf57_11)], bh + mov [edx+(nf57_38-nf57_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_41-nf57_11)], bh + mov [edx+(nf57_42-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_43-nf57_11)], bh + mov [edx+(nf57_44-nf57_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf57_45-nf57_11)], bh + mov [edx+(nf57_46-nf57_11)], bl + shr ebx, 16 + mov [edx+(nf57_47-nf57_11)], bh + mov [edx+(nf57_48-nf57_11)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors + + Trans16_3 bx, esi, 1 + Trans16 dx, esi+2 + Trans16_3 cx, esi+4, 1 + Trans16 bp, esi+6 + + mov esi, [nf_width] + + jmp nf57_0 ; flush prefetch + ALIGN 4 +nf57_0: + +nf57_11:mov ax, bx + shl eax, 16 +nf57_12:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_13:mov ax, bx + shl eax, 16 +nf57_14:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_15:mov ax, bx + shl eax, 16 +nf57_16:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_17:mov ax, bx + shl eax, 16 +nf57_18:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf57_21:mov ax, bx + shl eax, 16 +nf57_22:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_23:mov ax, bx + shl eax, 16 +nf57_24:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_25:mov ax, bx + shl eax, 16 +nf57_26:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_27:mov ax, bx + shl eax, 16 +nf57_28:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf57_31:mov ax, bx + shl eax, 16 +nf57_32:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_33:mov ax, bx + shl eax, 16 +nf57_34:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_35:mov ax, bx + shl eax, 16 +nf57_36:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_37:mov ax, bx + shl eax, 16 +nf57_38:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + lea edi, [edi+esi*2] + +nf57_41:mov ax, bx + shl eax, 16 +nf57_42:mov ax, bx + mov [edi], eax + mov [edi+esi], eax +nf57_43:mov ax, bx + shl eax, 16 +nf57_44:mov ax, bx + mov [edi+4], eax + mov [edi+esi+4], eax +nf57_45:mov ax, bx + shl eax, 16 +nf57_46:mov ax, bx + mov [edi+8], eax + mov [edi+esi+8], eax +nf57_47:mov ax, bx + shl eax, 16 +nf57_48:mov ax, bx + mov [edi+12], eax + mov [edi+esi+12], eax + add edi, esi + + pop esi + pop ebp + add esi, 16 + sub edi, [nfpk_back_right] ;br + retn + +;---------------------------------------- + ALIGN 4 +nf10: ; 2x2 4x4x2 (48 bytes) + + test word [esi], 08000h + jnz near nf26 + + xor eax, eax + + lea ecx, [nfhpk_mov4] + lea edx, [nf10_11+2] ; Remove byte ds: - AH + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_11-nf10_11)], bh + mov [edx+(nf10_12-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_13-nf10_11)], bh + mov [edx+(nf10_14-nf10_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_15-nf10_11)], bh + mov [edx+(nf10_16-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_17-nf10_11)], bh + mov [edx+(nf10_18-nf10_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_21-nf10_11)], bh + mov [edx+(nf10_22-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_23-nf10_11)], bh + mov [edx+(nf10_24-nf10_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_25-nf10_11)], bh + mov [edx+(nf10_26-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_27-nf10_11)], bh + mov [edx+(nf10_28-nf10_11)], bl + + + mov al, [esi+20] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_31-nf10_11)], bh + mov [edx+(nf10_32-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_33-nf10_11)], bh + mov [edx+(nf10_34-nf10_11)], bl + + mov al, [esi+21] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_35-nf10_11)], bh + mov [edx+(nf10_36-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_37-nf10_11)], bh + mov [edx+(nf10_38-nf10_11)], bl + + + mov al, [esi+22] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_41-nf10_11)], bh + mov [edx+(nf10_42-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_43-nf10_11)], bh + mov [edx+(nf10_44-nf10_11)], bl + + mov al, [esi+23] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_45-nf10_11)], bh + mov [edx+(nf10_46-nf10_11)], bl + shr ebx, 16 + mov [edx+(nf10_47-nf10_11)], bh + mov [edx+(nf10_48-nf10_11)], bl + + + lea edx, [edx+(nf10_51-nf10_11)] + + mov al, [esi+32] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_51-nf10_51)], bh + mov [edx+(nf10_52-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_53-nf10_51)], bh + mov [edx+(nf10_54-nf10_51)], bl + + mov al, [esi+33] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_55-nf10_51)], bh + mov [edx+(nf10_56-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_57-nf10_51)], bh + mov [edx+(nf10_58-nf10_51)], bl + + + mov al, [esi+34] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_61-nf10_51)], bh + mov [edx+(nf10_62-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_63-nf10_51)], bh + mov [edx+(nf10_64-nf10_51)], bl + + mov al, [esi+35] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_65-nf10_51)], bh + mov [edx+(nf10_66-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_67-nf10_51)], bh + mov [edx+(nf10_68-nf10_51)], bl + + + mov al, [esi+44] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_71-nf10_51)], bh + mov [edx+(nf10_72-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_73-nf10_51)], bh + mov [edx+(nf10_74-nf10_51)], bl + + mov al, [esi+45] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_75-nf10_51)], bh + mov [edx+(nf10_76-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_77-nf10_51)], bh + mov [edx+(nf10_78-nf10_51)], bl + + + mov al, [esi+46] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_81-nf10_51)], bh + mov [edx+(nf10_82-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_83-nf10_51)], bh + mov [edx+(nf10_84-nf10_51)], bl + + mov al, [esi+47] + mov ebx, [ecx+eax*4] + mov [edx+(nf10_85-nf10_51)], bh + mov [edx+(nf10_86-nf10_51)], bl + shr ebx, 16 + mov [edx+(nf10_87-nf10_51)], bh + mov [edx+(nf10_88-nf10_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors + + Trans16 bx, esi + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 + + mov esi, [nf_width] + + jmp nf10_0 ; flush prefetch + ALIGN 4 +nf10_0: + +nf10_11:mov ax, bx + shl eax, 16 +nf10_12:mov ax, bx + mov [edi], eax +nf10_13:mov ax, bx + shl eax, 16 +nf10_14:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_15:mov ax, bx + shl eax, 16 +nf10_16:mov ax, bx + mov [edi], eax +nf10_17:mov ax, bx + shl eax, 16 +nf10_18:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_21:mov ax, bx + shl eax, 16 +nf10_22:mov ax, bx + mov [edi], eax +nf10_23:mov ax, bx + shl eax, 16 +nf10_24:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_25:mov ax, bx + shl eax, 16 +nf10_26:mov ax, bx + mov [edi], eax +nf10_27:mov ax, bx + shl eax, 16 +nf10_28:mov ax, bx + mov [edi+4], eax + add edi, esi + + ; Load bx,dx,cx,bp with four colors + + mov esi, [esp] + Trans16 bx, esi+12 + Trans16 dx, esi+14 + Trans16 cx, esi+16 + Trans16 bp, esi+18 + mov esi, [nf_width] + +nf10_31:mov ax, bx + shl eax, 16 +nf10_32:mov ax, bx + mov [edi], eax +nf10_33:mov ax, bx + shl eax, 16 +nf10_34:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_35:mov ax, bx + shl eax, 16 +nf10_36:mov ax, bx + mov [edi], eax +nf10_37:mov ax, bx + shl eax, 16 +nf10_38:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_41:mov ax, bx + shl eax, 16 +nf10_42:mov ax, bx + mov [edi], eax +nf10_43:mov ax, bx + shl eax, 16 +nf10_44:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_45:mov ax, bx + shl eax, 16 +nf10_46:mov ax, bx + mov [edi], eax +nf10_47:mov ax, bx + shl eax, 16 +nf10_48:mov ax, bx + mov [edi+4], eax + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + + ; Load bx,dx,cx,bp with four colors + + mov esi, [esp] + Trans16 bx, esi+24 + Trans16 dx, esi+26 + Trans16 cx, esi+28 + Trans16 bp, esi+30 + mov esi, [nf_width] + +nf10_51:mov ax, bx + shl eax, 16 +nf10_52:mov ax, bx + mov [edi], eax +nf10_53:mov ax, bx + shl eax, 16 +nf10_54:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_55:mov ax, bx + shl eax, 16 +nf10_56:mov ax, bx + mov [edi], eax +nf10_57:mov ax, bx + shl eax, 16 +nf10_58:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_61:mov ax, bx + shl eax, 16 +nf10_62:mov ax, bx + mov [edi], eax +nf10_63:mov ax, bx + shl eax, 16 +nf10_64:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_65:mov ax, bx + shl eax, 16 +nf10_66:mov ax, bx + mov [edi], eax +nf10_67:mov ax, bx + shl eax, 16 +nf10_68:mov ax, bx + mov [edi+4], eax + add edi, esi + + ; Load bx,dx,cx,bp with four colors + + mov esi, [esp] + Trans16 bx, esi+36 + Trans16 dx, esi+38 + Trans16 cx, esi+40 + Trans16 bp, esi+42 + mov esi, [nf_width] + +nf10_71:mov ax, bx + shl eax, 16 +nf10_72:mov ax, bx + mov [edi], eax +nf10_73:mov ax, bx + shl eax, 16 +nf10_74:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_75:mov ax, bx + shl eax, 16 +nf10_76:mov ax, bx + mov [edi], eax +nf10_77:mov ax, bx + shl eax, 16 +nf10_78:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_81:mov ax, bx + shl eax, 16 +nf10_82:mov ax, bx + mov [edi], eax +nf10_83:mov ax, bx + shl eax, 16 +nf10_84:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf10_85:mov ax, bx + shl eax, 16 +nf10_86:mov ax, bx + mov [edi], eax +nf10_87:mov ax, bx + shl eax, 16 +nf10_88:mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 48 + sub edi, 8 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+16 +nf26: ; 2x1 4x8x2 (32 bytes) + + test word [esi+16], 08000h + jnz near nf42 + + xor eax, eax + + lea ecx, [nfhpk_mov4] + lea edx, [nf26_11+2] ; Removed byte ds: - AH + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_11-nf26_11)], bh + mov [edx+(nf26_12-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_13-nf26_11)], bh + mov [edx+(nf26_14-nf26_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_15-nf26_11)], bh + mov [edx+(nf26_16-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_17-nf26_11)], bh + mov [edx+(nf26_18-nf26_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_21-nf26_11)], bh + mov [edx+(nf26_22-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_23-nf26_11)], bh + mov [edx+(nf26_24-nf26_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_25-nf26_11)], bh + mov [edx+(nf26_26-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_27-nf26_11)], bh + mov [edx+(nf26_28-nf26_11)], bl + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_31-nf26_11)], bh + mov [edx+(nf26_32-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_33-nf26_11)], bh + mov [edx+(nf26_34-nf26_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_35-nf26_11)], bh + mov [edx+(nf26_36-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_37-nf26_11)], bh + mov [edx+(nf26_38-nf26_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_41-nf26_11)], bh + mov [edx+(nf26_42-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_43-nf26_11)], bh + mov [edx+(nf26_44-nf26_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_45-nf26_11)], bh + mov [edx+(nf26_46-nf26_11)], bl + shr ebx, 16 + mov [edx+(nf26_47-nf26_11)], bh + mov [edx+(nf26_48-nf26_11)], bl + + + lea edx, [edx+(nf26_51-nf26_11)] + + mov al, [esi+24] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_51-nf26_51)], bh + mov [edx+(nf26_52-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_53-nf26_51)], bh + mov [edx+(nf26_54-nf26_51)], bl + + mov al, [esi+25] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_55-nf26_51)], bh + mov [edx+(nf26_56-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_57-nf26_51)], bh + mov [edx+(nf26_58-nf26_51)], bl + + + mov al, [esi+26] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_61-nf26_51)], bh + mov [edx+(nf26_62-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_63-nf26_51)], bh + mov [edx+(nf26_64-nf26_51)], bl + + mov al, [esi+27] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_65-nf26_51)], bh + mov [edx+(nf26_66-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_67-nf26_51)], bh + mov [edx+(nf26_68-nf26_51)], bl + + + mov al, [esi+28] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_71-nf26_51)], bh + mov [edx+(nf26_72-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_73-nf26_51)], bh + mov [edx+(nf26_74-nf26_51)], bl + + mov al, [esi+29] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_75-nf26_51)], bh + mov [edx+(nf26_76-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_77-nf26_51)], bh + mov [edx+(nf26_78-nf26_51)], bl + + + mov al, [esi+30] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_81-nf26_51)], bh + mov [edx+(nf26_82-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_83-nf26_51)], bh + mov [edx+(nf26_84-nf26_51)], bl + + mov al, [esi+31] + mov ebx, [ecx+eax*4] + mov [edx+(nf26_85-nf26_51)], bh + mov [edx+(nf26_86-nf26_51)], bl + shr ebx, 16 + mov [edx+(nf26_87-nf26_51)], bh + mov [edx+(nf26_88-nf26_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors + + Trans16_3 bx, esi, 1 + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 + + mov esi, [nf_width] + + jmp nf26_0 ; flush prefetch + ALIGN 4 +nf26_0: + +nf26_11:mov ax, bx + shl eax, 16 +nf26_12:mov ax, bx + mov [edi], eax +nf26_13:mov ax, bx + shl eax, 16 +nf26_14:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_15:mov ax, bx + shl eax, 16 +nf26_16:mov ax, bx + mov [edi], eax +nf26_17:mov ax, bx + shl eax, 16 +nf26_18:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_21:mov ax, bx + shl eax, 16 +nf26_22:mov ax, bx + mov [edi], eax +nf26_23:mov ax, bx + shl eax, 16 +nf26_24:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_25:mov ax, bx + shl eax, 16 +nf26_26:mov ax, bx + mov [edi], eax +nf26_27:mov ax, bx + shl eax, 16 +nf26_28:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_31:mov ax, bx + shl eax, 16 +nf26_32:mov ax, bx + mov [edi], eax +nf26_33:mov ax, bx + shl eax, 16 +nf26_34:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_35:mov ax, bx + shl eax, 16 +nf26_36:mov ax, bx + mov [edi], eax +nf26_37:mov ax, bx + shl eax, 16 +nf26_38:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_41:mov ax, bx + shl eax, 16 +nf26_42:mov ax, bx + mov [edi], eax +nf26_43:mov ax, bx + shl eax, 16 +nf26_44:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_45:mov ax, bx + shl eax, 16 +nf26_46:mov ax, bx + mov [edi], eax +nf26_47:mov ax, bx + shl eax, 16 +nf26_48:mov ax, bx + mov [edi+4], eax + add edi, esi + + lea eax, [esi*8-8] + sub edi, eax + + ; Load bx,dx,cx,bp with four colors + + mov esi, [esp] + Trans16 bx, esi+16 + Trans16 dx, esi+18 + Trans16 cx, esi+20 + Trans16 bp, esi+22 + mov esi, [nf_width] + +nf26_51:mov ax, bx + shl eax, 16 +nf26_52:mov ax, bx + mov [edi], eax +nf26_53:mov ax, bx + shl eax, 16 +nf26_54:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_55:mov ax, bx + shl eax, 16 +nf26_56:mov ax, bx + mov [edi], eax +nf26_57:mov ax, bx + shl eax, 16 +nf26_58:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_61:mov ax, bx + shl eax, 16 +nf26_62:mov ax, bx + mov [edi], eax +nf26_63:mov ax, bx + shl eax, 16 +nf26_64:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_65:mov ax, bx + shl eax, 16 +nf26_66:mov ax, bx + mov [edi], eax +nf26_67:mov ax, bx + shl eax, 16 +nf26_68:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_71:mov ax, bx + shl eax, 16 +nf26_72:mov ax, bx + mov [edi], eax +nf26_73:mov ax, bx + shl eax, 16 +nf26_74:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_75:mov ax, bx + shl eax, 16 +nf26_76:mov ax, bx + mov [edi], eax +nf26_77:mov ax, bx + shl eax, 16 +nf26_78:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_81:mov ax, bx + shl eax, 16 +nf26_82:mov ax, bx + mov [edi], eax +nf26_83:mov ax, bx + shl eax, 16 +nf26_84:mov ax, bx + mov [edi+4], eax + add edi, esi + +nf26_85:mov ax, bx + shl eax, 16 +nf26_86:mov ax, bx + mov [edi], eax +nf26_87:mov ax, bx + shl eax, 16 +nf26_88:mov ax, bx + mov [edi+4], eax + + pop esi + pop ebp + add esi, 32 + sub edi, 8 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +;nf10+32 +nf42: ; 1x2 8x4x2 (32 bytes) + + xor eax, eax + + lea ecx, [nfhpk_mov4] + lea edx, [nf42_11+2] ; removed byte ds: - AH + + mov al, [esi+8] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_11-nf42_11)], bh + mov [edx+(nf42_12-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_13-nf42_11)], bh + mov [edx+(nf42_14-nf42_11)], bl + + mov al, [esi+9] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_15-nf42_11)], bh + mov [edx+(nf42_16-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_17-nf42_11)], bh + mov [edx+(nf42_18-nf42_11)], bl + + + mov al, [esi+10] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_21-nf42_11)], bh + mov [edx+(nf42_22-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_23-nf42_11)], bh + mov [edx+(nf42_24-nf42_11)], bl + + mov al, [esi+11] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_25-nf42_11)], bh + mov [edx+(nf42_26-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_27-nf42_11)], bh + mov [edx+(nf42_28-nf42_11)], bl + + + mov al, [esi+12] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_31-nf42_11)], bh + mov [edx+(nf42_32-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_33-nf42_11)], bh + mov [edx+(nf42_34-nf42_11)], bl + + mov al, [esi+13] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_35-nf42_11)], bh + mov [edx+(nf42_36-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_37-nf42_11)], bh + mov [edx+(nf42_38-nf42_11)], bl + + + mov al, [esi+14] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_41-nf42_11)], bh + mov [edx+(nf42_42-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_43-nf42_11)], bh + mov [edx+(nf42_44-nf42_11)], bl + + mov al, [esi+15] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_45-nf42_11)], bh + mov [edx+(nf42_46-nf42_11)], bl + shr ebx, 16 + mov [edx+(nf42_47-nf42_11)], bh + mov [edx+(nf42_48-nf42_11)], bl + + + lea edx, [edx+(nf42_51-nf42_11)] + + mov al, [esi+24] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_51-nf42_51)], bh + mov [edx+(nf42_52-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_53-nf42_51)], bh + mov [edx+(nf42_54-nf42_51)], bl + + mov al, [esi+25] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_55-nf42_51)], bh + mov [edx+(nf42_56-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_57-nf42_51)], bh + mov [edx+(nf42_58-nf42_51)], bl + + + mov al, [esi+26] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_61-nf42_51)], bh + mov [edx+(nf42_62-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_63-nf42_51)], bh + mov [edx+(nf42_64-nf42_51)], bl + + mov al, [esi+27] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_65-nf42_51)], bh + mov [edx+(nf42_66-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_67-nf42_51)], bh + mov [edx+(nf42_68-nf42_51)], bl + + + mov al, [esi+28] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_71-nf42_51)], bh + mov [edx+(nf42_72-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_73-nf42_51)], bh + mov [edx+(nf42_74-nf42_51)], bl + + mov al, [esi+29] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_75-nf42_51)], bh + mov [edx+(nf42_76-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_77-nf42_51)], bh + mov [edx+(nf42_78-nf42_51)], bl + + + mov al, [esi+30] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_81-nf42_51)], bh + mov [edx+(nf42_82-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_83-nf42_51)], bh + mov [edx+(nf42_84-nf42_51)], bl + + mov al, [esi+31] + mov ebx, [ecx+eax*4] + mov [edx+(nf42_85-nf42_51)], bh + mov [edx+(nf42_86-nf42_51)], bl + shr ebx, 16 + mov [edx+(nf42_87-nf42_51)], bh + mov [edx+(nf42_88-nf42_51)], bl + + push ebp + push esi + ; Load bx,dx,cx,bp with four colors + + Trans16_3 bx, esi, 1 + Trans16 dx, esi+2 + Trans16 cx, esi+4 + Trans16 bp, esi+6 + + mov esi, [nf_width] + + jmp nf42_0 ; flush prefetch + ALIGN 4 +nf42_0: + +nf42_11:mov ax, bx + shl eax, 16 +nf42_12:mov ax, bx + mov [edi], eax +nf42_13:mov ax, bx + shl eax, 16 +nf42_14:mov ax, bx + mov [edi+4], eax +nf42_15:mov ax, bx + shl eax, 16 +nf42_16:mov ax, bx + mov [edi+8], eax +nf42_17:mov ax, bx + shl eax, 16 +nf42_18:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_21:mov ax, bx + shl eax, 16 +nf42_22:mov ax, bx + mov [edi], eax +nf42_23:mov ax, bx + shl eax, 16 +nf42_24:mov ax, bx + mov [edi+4], eax +nf42_25:mov ax, bx + shl eax, 16 +nf42_26:mov ax, bx + mov [edi+8], eax +nf42_27:mov ax, bx + shl eax, 16 +nf42_28:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_31:mov ax, bx + shl eax, 16 +nf42_32:mov ax, bx + mov [edi], eax +nf42_33:mov ax, bx + shl eax, 16 +nf42_34:mov ax, bx + mov [edi+4], eax +nf42_35:mov ax, bx + shl eax, 16 +nf42_36:mov ax, bx + mov [edi+8], eax +nf42_37:mov ax, bx + shl eax, 16 +nf42_38:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_41:mov ax, bx + shl eax, 16 +nf42_42:mov ax, bx + mov [edi], eax +nf42_43:mov ax, bx + shl eax, 16 +nf42_44:mov ax, bx + mov [edi+4], eax +nf42_45:mov ax, bx + shl eax, 16 +nf42_46:mov ax, bx + mov [edi+8], eax +nf42_47:mov ax, bx + shl eax, 16 +nf42_48:mov ax, bx + mov [edi+12], eax + add edi, esi + + ; Load bx,dx,cx,bp with four colors + + mov esi, [esp] + Trans16_3 bx, esi+16, 1 + Trans16 dx, esi+18 + Trans16 cx, esi+20 + Trans16 bp, esi+22 + mov esi, [nf_width] + +nf42_51:mov ax, bx + shl eax, 16 +nf42_52:mov ax, bx + mov [edi], eax +nf42_53:mov ax, bx + shl eax, 16 +nf42_54:mov ax, bx + mov [edi+4], eax +nf42_55:mov ax, bx + shl eax, 16 +nf42_56:mov ax, bx + mov [edi+8], eax +nf42_57:mov ax, bx + shl eax, 16 +nf42_58:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_61:mov ax, bx + shl eax, 16 +nf42_62:mov ax, bx + mov [edi], eax +nf42_63:mov ax, bx + shl eax, 16 +nf42_64:mov ax, bx + mov [edi+4], eax +nf42_65:mov ax, bx + shl eax, 16 +nf42_66:mov ax, bx + mov [edi+8], eax +nf42_67:mov ax, bx + shl eax, 16 +nf42_68:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_71:mov ax, bx + shl eax, 16 +nf42_72:mov ax, bx + mov [edi], eax +nf42_73:mov ax, bx + shl eax, 16 +nf42_74:mov ax, bx + mov [edi+4], eax +nf42_75:mov ax, bx + shl eax, 16 +nf42_76:mov ax, bx + mov [edi+8], eax +nf42_77:mov ax, bx + shl eax, 16 +nf42_78:mov ax, bx + mov [edi+12], eax + add edi, esi + +nf42_81:mov ax, bx + shl eax, 16 +nf42_82:mov ax, bx + mov [edi], eax +nf42_83:mov ax, bx + shl eax, 16 +nf42_84:mov ax, bx + mov [edi+4], eax +nf42_85:mov ax, bx + shl eax, 16 +nf42_86:mov ax, bx + mov [edi+8], eax +nf42_87:mov ax, bx + shl eax, 16 +nf42_88:mov ax, bx + mov [edi+12], eax + + pop esi + pop ebp + add esi, 32 + sub edi, [nfpk_back_right] ;br + retn + +;---------------------------------------- + ALIGN 4 +nf11: ; 8x8x16 (128 bytes) + mov edx, [nf_width] + +%macro Trans16Blk 1 ; MACRO idx + Trans16 bx, %1 ;idx + mov [edi], bx + Trans16 bx, (%1 + 2) ;idx+2 + mov [edi+2], bx + Trans16 bx, (%1 + 4) ;idx+4 + mov [edi+4], bx + Trans16 bx, (%1 + 6) ;idx+6 + mov [edi+6], bx + Trans16 bx, (%1 + 8) ;idx+8 + mov [edi+8], bx + Trans16 bx, (%1 + 10) ;idx+10 + mov [edi+10], bx + Trans16 bx, (%1 + 12) ;idx+12 + mov [edi+12], bx + Trans16 bx, (%1 + 14) ;idx+14 + mov [edi+14], bx +%endmacro + + Trans16Blk esi ;0 + add edi, edx + Trans16Blk esi+16 ;1 + add edi, edx + Trans16Blk esi+32 ;2 + add edi, edx + Trans16Blk esi+48 ;3 + add edi, edx + Trans16Blk esi+64 ;4 + add edi, edx + Trans16Blk esi+80 ;5 + add edi, edx + Trans16Blk esi+96 ;6 + add edi, edx + Trans16Blk esi+112 ;7 + + add esi, 128 + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + retn + +;---------------------------------------- + ALIGN 4 +nf12: ; low 4x4x16 (32 bytes) + + mov edx, [nf_width] + + Trans16 bx, esi + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+2 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+4 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+6 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + lea edi, [edi+edx*2] + + Trans16 bx, esi+8 + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+10 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+12 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+14 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + lea edi, [edi+edx*2] + + Trans16 bx, esi+16 + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+18 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+20 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+22 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + lea edi, [edi+edx*2] + + Trans16 bx, esi+24 + shrd eax, ebx, 16 + mov ax, bx + mov [edi], eax + mov [edi+edx], eax + + Trans16 bx, esi+26 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+4], eax + mov [edi+edx+4], eax + + Trans16 bx, esi+28 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+8], eax + mov [edi+edx+8], eax + + Trans16 bx, esi+30 + shrd eax, ebx, 16 + mov ax, bx + mov [edi+12], eax + mov [edi+edx+12], eax + + add edi, edx + + sub edi, [nfpk_back_right] ;br + add esi, 32 + retn + +;---------------------------------------- + ALIGN 4 +nf13: ; 2x2 4x4x0 (8 bytes) + mov edx, [nf_width] + + Trans16 cx, esi + shrd ebx, ecx, 16 + mov bx, cx + + Trans16 cx, esi+2 + shrd eax, ecx, 16 + mov ax, cx + mov ecx, eax + + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + lea edi, [edi+edx*2] + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + lea edi, [edi+edx*2] + + Trans16 cx, esi+4 + shrd ebx, ecx, 16 + mov bx, cx + + Trans16 cx, esi+6 + shrd eax, ecx, 16 + mov ax, cx + mov ecx, eax + + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + lea edi, [edi+edx*2] + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], ecx + mov [edi+edx], ebx + mov [edi+edx+4], ebx + mov [edi+edx+8], ecx + mov [edi+edx+12], ecx + add edi, edx + + sub edi, [nfpk_back_right] ; br + add esi, 8 + + retn + +;---------------------------------------- + ALIGN 4 +nf14: ; 8x8x0 (2 bytes) + Trans16 cx, esi + add esi, 2 + shrd ebx, ecx, 16 + mov bx, cx + +nf_solid: + mov edx, [nf_width] + + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + add edi, edx + mov [edi], ebx + mov [edi+4], ebx + mov [edi+8], ebx + mov [edi+12], ebx + + sub edi, [nfpk_back_right] ;br ; (SHEIGHT-1)*width+8 + + retn + +;---------------------------------------- + ALIGN 4 +nf15: ; unused + retn + +;nfHPkDecomp ENDP + + diff --git a/lnxmvelib/snd8to16.h b/lnxmvelib/snd8to16.h new file mode 100644 index 000000000..81c7090a0 --- /dev/null +++ b/lnxmvelib/snd8to16.h @@ -0,0 +1,53 @@ +/* +** #include +** void init_snd_8to16(void) +** { +** int i; +** for (i=0; i<44; ++i) +** snd_8to16[i] = i; +** for (i=44; i<128; ++i) +** snd_8to16[i] = (unsigned)floor(pow(65535,i/127.0)+.5); +** for (i=1; i<128; ++i) +** snd_8to16[256-i] = -snd_8to16[i]; +** snd_8to16[128] = snd_8to16[129]; +** } +*/ +#pragma warning( disable: 4245) + +signed short snd_8to16[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 47, 51, 56, 61, + 66, 72, 79, 86, 94, 102, 112, 122, + 133, 145, 158, 173, 189, 206, 225, 245, + 267, 292, 318, 348, 379, 414, 452, 493, + 538, 587, 640, 699, 763, 832, 908, 991, + 1081, 1180, 1288, 1405, 1534, 1673, 1826, 1993, + 2175, 2373, 2590, 2826, 3084, 3365, 3672, 4008, + 4373, 4772, 5208, 5683, 6202, 6767, 7385, 8059, + 8794, 9597, 10472, 11428, 12471, 13609, 14851, 16206, + 17685, 19298, 21060, 22981, 25078, 27367, 29864, 32589, + 35563, 38808, 42350, 46214, 50431, 55033, 60055, 65535, +-65535,-65535,-60055,-55033,-50431,-46214,-42350,-38808, +-35563,-32589,-29864,-27367,-25078,-22981,-21060,-19298, +-17685,-16206,-14851,-13609,-12471,-11428,-10472, -9597, + -8794, -8059, -7385, -6767, -6202, -5683, -5208, -4772, + -4373, -4008, -3672, -3365, -3084, -2826, -2590, -2373, + -2175, -1993, -1826, -1673, -1534, -1405, -1288, -1180, + -1081, -991, -908, -832, -763, -699, -640, -587, + -538, -493, -452, -414, -379, -348, -318, -292, + -267, -245, -225, -206, -189, -173, -158, -145, + -133, -122, -112, -102, -94, -86, -79, -72, + -66, -61, -56, -51, -47, -43, -42, -41, + -40, -39, -38, -37, -36, -35, -34, -33, + -32, -31, -30, -29, -28, -27, -26, -25, + -24, -23, -22, -21, -20, -19, -18, -17, + -16, -15, -14, -13, -12, -11, -10, -9, + -8, -7, -6, -5, -4, -3, -2, -1 +}; +#pragma warning( default: 4245) + + diff --git a/mac/DrawSprocket.cpp b/mac/DrawSprocket.cpp new file mode 100644 index 000000000..607e12d83 --- /dev/null +++ b/mac/DrawSprocket.cpp @@ -0,0 +1,392 @@ +#include +#include +#include +#include +#include +#include +#include "game.h" +#include "gl.h" +#include "aglRenderers.h" +#include "agl.h" +#include "macdisplays.h" +#include "macapp.h" +#include "descent.h" +#include "pstypes.h" +#include "config.h" +#include "application.h" +#include "rend_opengl.h" + +const RGBColor rgbBlack = { 0x0000, 0x0000, 0x0000 }; +const RGBColor rgbWhite = { 0xFFFF, 0xFFFF, 0xFFFF }; +/* globals */ +DSpContextAttributes gDSpContextAttributes[N_SUPPORTED_VIDRES]; +DSpContextReference gDSpContext[N_SUPPORTED_VIDRES] = {NULL, NULL, NULL}; +short current_context = DSP_640x480; +DSpContextReference * gpContextRefUnused = NULL; + +GDHandle hGD; +void WindowReset (WindowPtr pWindow, short width, short height); + +short gnumDevices = 0; +// Set up DSp screens, handles multi-monitor correctly +void SetupScreen (CGrafPtr *ppWin) + +//void SetupScreen (GDHandle *hGD, CGrafPtr *ppWin) +//void SetupScreen (GDHandle *hGD, CGrafPtr *ppWin, short *numDevices) +{ + DSpContextAttributes foundAttributes; + DisplayIDType displayID; + Rect rectWin; + RGBColor rgbSave; + GrafPtr pGrafSave; + OSStatus err; + int i; + + // check number of screens + GDHandle hDevice = DMGetFirstScreenDevice (true); + do + { + gnumDevices++; + hDevice = DMGetNextScreenDevice (hDevice, true); + } + while (hDevice); + + // start DSp and find a good context + err = DSpStartup(); + if( err ) + Error("%s:%d DSpStartup: Error %d ", __FILE__, __LINE__, err); + +#ifdef DAJ_DEBUG + DSpSetDebugMode(true); +#endif + // Note: DSp currently REQUIRES the back buffer attributes even if only one buffer is required + // Also note: Choose either 16 or 32 bpp +#ifdef USE_OPENGL + for(i = 0; iportRect)); + RGBForeColor (&rgbSave); // ensure color is reset for proper blitting + SetPort (pGrafSave); + +} diff --git a/mac/IO.H b/mac/IO.H new file mode 100644 index 000000000..83940e473 --- /dev/null +++ b/mac/IO.H @@ -0,0 +1,4 @@ +// Created by John C Ardussi as a placeholder for a file that +// does not exist under CodeWarrior. +// +// Date: 5-10-99 \ No newline at end of file diff --git a/mac/Iconm b/mac/Iconm new file mode 100644 index 000000000..e69de29bb diff --git a/mac/InSprocket.c b/mac/InSprocket.c new file mode 100644 index 000000000..586e287e5 --- /dev/null +++ b/mac/InSprocket.c @@ -0,0 +1,477 @@ +#include "fix.h" +#include "Macros.h" +#include +#include "InSprocket.h" +#include "InSprocketResource.h" +#include "renderer.h" +#include "game.h" +#include "descent.h" +#include "streamaudio.h" +#include "pilot.h" +#include "macapp.h" + +//#ifndef DAJ_DEBUG //REMOVE +#define USE_MOUSE +//#endif +//#define MOUSE_MAX 32768.0f +#define MOUSE_MAX 48000.0f +//#define MOUSE_MAX 65336.0f +#undef USE_KEYBOARD //NOT on you life +Boolean iSprocket_inited = false; + + ISpNeed ISp_needs[kNeed_COUNT] = + { + { "\pHeading", kIconSuiteID_Yaw, 0, 0, + kISpElementKind_Axis, + kISpElementLabel_Axis_Yaw, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pPitch", kIconSuiteID_Pitch, 0, 0, + kISpElementKind_Axis, + kISpElementLabel_Axis_Pitch, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pBank", kIconSuiteID_Roll, 0, 0, + kISpElementKind_Axis, + kISpElementLabel_Axis_Roll, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pSideways", kIconSuiteID_SlideHorz, 0, 0, + kISpElementKind_Axis, + kISpElementLabel_Axis_XAxis, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pThrust", kIconSuiteID_Thrust, 0, 0, + kISpElementKind_Axis, + kISpElementLabel_Axis_ZAxis, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pVertical", kIconSuiteID_SlideVert, 0, 0, + kISpElementKind_Axis, + kISpElementLabel_Axis_YAxis, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + // Mouse Related Input + { "\pMouse Heading", kIconSuiteID_Yaw, 0, 0, + kISpElementKind_Delta, + kISpElementLabel_Delta_Yaw, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pMouse Pitch", kIconSuiteID_Pitch, 0, 0, + kISpElementKind_Delta, + kISpElementLabel_Delta_Pitch, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pMouse Bank", kIconSuiteID_Roll, 0, 0, + kISpElementKind_Delta, + kISpElementLabel_Delta_Roll, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pMouse Sideways", kIconSuiteID_SlideHorz, 0, 0, + kISpElementKind_Delta, + kISpElementLabel_Delta_X, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pMouse Thrust", kIconSuiteID_Thrust, 0, 0, + kISpElementKind_Delta, + kISpElementLabel_Delta_Z, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + { "\pMouse Vertical", kIconSuiteID_SlideVert, 0, 0, + kISpElementKind_Delta, + kISpElementLabel_Delta_Y, + kISpNeedFlag_NoMultiConfig, 0, 0, 0 + }, + // The buttons + { "\pForward", kIconSuiteID_Foward, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + + { "\pReverse", kIconSuiteID_Back, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pSlide Left", kIconSuiteID_SlideLeft, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pSlide Right", kIconSuiteID_SlideRight, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pSlide Up", kIconSuiteID_SlideUp, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pSlide Down", kIconSuiteID_SlideDown, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pTurn Left", kIconSuiteID_YawL, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pTurn Right", kIconSuiteID_YawR, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pPitch Up", kIconSuiteID_PitchU, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pPitch Down", kIconSuiteID_PitchD, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pBank Left", kIconSuiteID_RollL, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pBank Right", kIconSuiteID_RollR, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + + { "\p-", 0, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + // seperator + { "\pFire Primary", kIconSuiteID_FirePrim, 0, 0, + kISpElementKind_Button, + kISpElementLabel_Btn_Fire, + 0, 0, 0, 0 + }, + { "\pFire Secondary", kIconSuiteID_FireSecond, 0, 0, + kISpElementKind_Button, + kISpElementLabel_Btn_SecondaryFire, + 0, 0, 0, 0 + }, + { "\pNext Primary", kIconSuiteID_NextPrim, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pNext Secondary", kIconSuiteID_NextSecond, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pAfterburner", kIconSuiteID_AfterBurn, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pHeadlight", kIconSuiteID_HeadLight, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, 0, 0, 0, 0 + }, + { "\pFlare", kIconSuiteID_Flare, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pRear View", kIconSuiteID_RearView, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pMap", kIconSuiteID_Map, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pUse Inventory", kIconSuiteID_UseInv, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pNext Inventory", kIconSuiteID_NextInv, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pPrevious Inventory", kIconSuiteID_PrevInv, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pUse Counter Measure", kIconSuiteID_UseCM, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pNext Counter Measure", kIconSuiteID_NextCM, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pPrevious Counter Measure", kIconSuiteID_PrevCM, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, 0, 0, 0, 0 + }, + { "\pBank On", kIconSuiteID_BankOn, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + }, + { "\pSlide On", kIconSuiteID_SlideOn, 0, 0, + kISpElementKind_Button, + kISpElementLabel_None, + 0, 0, 0, 0 + } + }; +void inSprocket_Init(void) +{ + int i; + + if(iSprocket_inited) + return; + + mprintf((1, "inSprocket_init: need_count %d\n", kNeed_COUNT)); + +#ifdef USE_MOUSE + ISpDevices_ActivateClass (kISpDeviceClass_Mouse); +#endif +#ifdef USE_KEYBOARD + ISpDevices_ActivateClass (kISpDeviceClass_Keyboard); +#endif + ISpElement_NewVirtualFromNeeds(kNeed_COUNT, ISp_needs, gInputElement, 0); + + char pilot_name[40]; + Current_pilot.get_name(pilot_name); + if(pilot_name[0]) + ISpInit(kNeed_COUNT, ISp_needs, gInputElement, 'DNT3', (ulong)pilot_name, 0, kSetListID, 0); + else + ISpInit(kNeed_COUNT, ISp_needs, gInputElement, 'DNT3', kNeedsVersion, 0, kSetListID, 0); + + ISpElementList_New(0, NULL, &gInputEventList, 0); + ISpElementList_New(0, NULL, &gInputHoldDownEventList, 0); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_FirePrim_On,1, &gInputElement[kNeed_FirePrim]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_FireSecond_On,1, &gInputElement[kNeed_FireSecond]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_AfterBurn_On, 1, &gInputElement[kNeed_AfterBurn]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_RearView_On, 1, &gInputElement[kNeed_RearView]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_Bank_On, 1, &gInputElement[kNeed_BankOn]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_Slide_On, 1, &gInputElement[kNeed_SlideOn]); + + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_Accelerate_On, 1, &gInputElement[kNeed_Accelerate]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_Reverse_On, 1, &gInputElement[kNeed_Reverse]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_SlideLeft_On, 1, &gInputElement[kNeed_SlideLeft]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_SlideRight_On, 1, &gInputElement[kNeed_SlideRight]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_SlideUp_On, 1, &gInputElement[kNeed_SlideUp]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_SlideDown_On, 1, &gInputElement[kNeed_SlideDown]); + + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_TurnLeft_On, 1, &gInputElement[kNeed_TurnLeft]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_TurnRight_On, 1, &gInputElement[kNeed_TurnRight]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_PitchUp_On, 1, &gInputElement[kNeed_PitchUp]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_PitchDown_On, 1, &gInputElement[kNeed_PitchDown]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_BankLeft_On, 1, &gInputElement[kNeed_BankLeft]); + ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_BankRight_On, 1, &gInputElement[kNeed_BankRight]); + ISpElementList_AddElements(gInputEventList, kInputEvent_NextPrim, 1, &gInputElement[kNeed_NextPrim]); + ISpElementList_AddElements(gInputEventList, kInputEvent_NextSecond, 1, &gInputElement[kNeed_NextSecond]); + ISpElementList_AddElements(gInputEventList, kInputEvent_HeadLight, 1, &gInputElement[kNeed_HeadLight]); + ISpElementList_AddElements(gInputEventList, kInputEvent_Flare, 1, &gInputElement[kNeed_Flare]); + ISpElementList_AddElements(gInputEventList, kInputEvent_Map, 1, &gInputElement[kNeed_Map]); + ISpElementList_AddElements(gInputEventList, kInputEvent_InventoryUse, 1, &gInputElement[kNeed_InventoryUse]); + ISpElementList_AddElements(gInputEventList, kInputEvent_InventoryNext, 1, &gInputElement[kNeed_InventoryNext]); + ISpElementList_AddElements(gInputEventList, kInputEvent_InventoryPrev, 1, &gInputElement[kNeed_InventoryPrev]); + ISpElementList_AddElements(gInputEventList, kInputEvent_CounterUse, 1, &gInputElement[kNeed_CounterUse]); + ISpElementList_AddElements(gInputEventList, kInputEvent_CounterNext, 1, &gInputElement[kNeed_CounterNext]); + ISpElementList_AddElements(gInputEventList, kInputEvent_CounterPrev, 1, &gInputElement[kNeed_CounterPrev]); +/* + ISpElementList_AddElements(gInputEventList, kInputEvent_ATaunt1, 1, &gInputElement[kNeed_ATaunt1]); + ISpElementList_AddElements(gInputEventList, kInputEvent_ATaunt2, 1, &gInputElement[kNeed_ATaunt2]); + ISpElementList_AddElements(gInputEventList, kInputEvent_ATaunt3, 1, &gInputElement[kNeed_ATaunt3]); + ISpElementList_AddElements(gInputEventList, kInputEvent_ATaunt4, 1, &gInputElement[kNeed_ATaunt4]); +*/ + ISpElementList_Flush(gInputEventList); + ISpElementList_Flush(gInputHoldDownEventList); + for(i = 0; i < 12; i++) + ISpElement_Flush(gInputElement[i]); + + ISpSuspend(); + + iSprocket_inited = true; +} +void inSprocket_Configure(void) +{ + #if defined USE_GLIDE + rend_Close(); +#elif defined USE_OPENGL + PauseDSpContext(); +#endif + + AudioStream::PauseAll(); + + inSprocket_Init(); //Just incase the config gets called before noral init + + ISpConfigure(NULL); + + AudioStream::ResumeAll(); +#ifdef USE_MOUSE + HideCursor(); +#endif +#if defined USE_GLIDE + rend_Init (PreferredRenderer, Descent,&Render_preferred_state); +#elif defined USE_OPENGL + ResumeDSpContext(); +#endif +} +void inSprocket_Activate( Boolean inActivate) +{ + Boolean doActivate; + int i; + + doActivate = inActivate; + if (gInputActive != doActivate) + { + gInputActive = doActivate; + + if (gInputActive) + { + #ifdef USE_MOUSE + HideCursor(); + #endif + + ISpResume(); + } + else + { + ISpSuspend(); + + #ifdef USE_MOUSE +// ShowCursor(); + #endif + } + } +} + +void InSprocket_Flush(void) +{ + ISpElementList_Flush(gInputEventList); + ISpElementList_Flush(gInputHoldDownEventList); + for(int i = 0; i < 12; i++) + ISpElement_Flush(gInputElement[i]); +} + +float inSprocket_GetAllAxis(int axis) +{ + ISpAxisData axisValue = kISpAxisMiddle; + OSStatus err; + ISpElementEvent event; + Boolean gotEvent; + float val = 0, fval; + signed int maxval = 0; + + while (!(err = ISpElement_GetNextEvent(gInputElement[axis+6], sizeof(event), &event, &gotEvent)) && gotEvent) { + maxval += (signed int)event.data; + } + if(maxval) + return maxval/MOUSE_MAX; + err = ISpElement_GetNextEvent(gInputElement[axis], sizeof(event), &event, &gotEvent); + if (!err && gotEvent) { + return ISpSymmetricAxisToFloat (event.data); + } + err = ISpElement_GetSimpleState(gInputElement[axis], &axisValue); + if(!err) { + return ISpSymmetricAxisToFloat (axisValue); + } + return val; +} +float inSprocket_GetMouse(int axis) +{ + OSStatus err; + ISpElementEvent event; + Boolean gotEvent = false; + float val = 0; + signed int maxval = 0; + + err = ISpElement_GetNextEvent(gInputElement[axis+6], sizeof(event), &event, &gotEvent); + while (!err && gotEvent) { + maxval += (signed int)event.data; + err = ISpElement_GetNextEvent(gInputElement[axis+6], sizeof(event), &event, &gotEvent); + } + if(maxval) + return val = maxval/MOUSE_MAX; + return val; +} +float inSprocket_GetAxis(int axis) +{ + ISpAxisData axisValue = kISpAxisMiddle; + OSStatus err; + float val = 0; + ISpElementEvent event; + Boolean gotEvent; + + if (!(err = ISpElement_GetNextEvent(gInputElement[axis], sizeof(event), &event, &gotEvent)) && gotEvent) { + val = ISpSymmetricAxisToFloat (event.data); + } else if (!(err = ISpElement_GetSimpleState(gInputElement[axis], &axisValue))) { + val = ISpSymmetricAxisToFloat (axisValue); + } + return val; +} +int inSprocket_GetAxisInt(int axis) +{ + ISpAxisData axisValue = kISpAxisMiddle; + OSStatus err; + int val = 0; + ISpElementEvent event; + Boolean gotEvent; + + if (!(err = ISpElement_GetSimpleState(gInputElement[axis], &axisValue))) { + val = (int)((axisValue >> 24) - 0x7F); + } + return val; +} +TInputEvent inSprocket_GetButtonEvent(void) +{ + OSErr err; +// TInputEvent result = kInputEvent_None; + int result = kInputEvent_None; + ISpElementEvent event; + Boolean gotEvent; + + if (gInputActive) + { + err = ISpElementList_GetNextEvent(gInputHoldDownEventList, sizeof(event), &event, &gotEvent); + if (err == noErr && gotEvent) + { + result = (TInputEvent) event.refCon; + if (event.data == kISpButtonUp) + result += 1; // Note: we rely on off being on+1 (ie kInputEvent_InertialDampers_Off == kInputEvent_InertialDampers_On + 1) + } + else + { + err = ISpElementList_GetNextEvent(gInputEventList, sizeof(event), &event, &gotEvent); + + if (err == noErr && gotEvent && event.data == kISpButtonDown) + result = (TInputEvent) event.refCon; + } + } + + return (TInputEvent)result; +} +void inSprocket_Exit(void) +{ + if (gInputActive) + { + inSprocket_Activate(false); + } + ISpElementList_Dispose(gInputEventList); + ISpStop(); +//?? ISpElement_DisposeVirtual(kNeed_COUNT, gInputElement); +} diff --git a/mac/InSprocket.h b/mac/InSprocket.h new file mode 100644 index 000000000..87aabfe15 --- /dev/null +++ b/mac/InSprocket.h @@ -0,0 +1,167 @@ +/* + * File: InSprocket.h + * + */ + +#ifndef __InSprocket__ +#define __InSprocket__ + +#include +#include + +typedef enum TInputEvent { + kInputEvent_None, + // it's IMPORTANT that end be begin+1 + kInputEvent_FirePrim_On , + kInputEvent_FirePrim_Off, + kInputEvent_FireSecond_On, + kInputEvent_FireSecond_Off, + kInputEvent_AfterBurn_On, + kInputEvent_AfterBurn_Off, + kInputEvent_RearView_On, + kInputEvent_RearView_Off, + + kInputEvent_NextPrim, + kInputEvent_NextSecond, + kInputEvent_Bank_On, + kInputEvent_Bank_Off, + kInputEvent_Slide_On, + kInputEvent_Slide_Off, + + kInputEvent_InventoryUse, + kInputEvent_InventoryNext, + kInputEvent_InventoryPrev, + kInputEvent_CounterUse, + kInputEvent_CounterNext, + kInputEvent_CounterPrev, + + kInputEvent_Accelerate_On, + kInputEvent_Accelerate_Off, + kInputEvent_Reverse_On, + kInputEvent_Reverse_Off, + kInputEvent_SlideLeft_On, + kInputEvent_SlideLeft_Off, + kInputEvent_SlideRight_On, + kInputEvent_SlideRight_Off, + kInputEvent_SlideUp_On, + kInputEvent_SlideUp_Off, + kInputEvent_SlideDown_On, + kInputEvent_SlideDown_Off, + + kInputEvent_BankLeft_On, + kInputEvent_BankLeft_Off, + kInputEvent_BankRight_On, + kInputEvent_BankRight_Off, + kInputEvent_TurnLeft_On, + kInputEvent_TurnLeft_Off, + kInputEvent_TurnRight_On, + kInputEvent_TurnRight_Off, + kInputEvent_PitchUp_On, + kInputEvent_PitchUp_Off, + kInputEvent_PitchDown_On, + kInputEvent_PitchDown_Off, + + kInputEvent_HeadLight, + kInputEvent_Flare, + kInputEvent_Map, + + kInputEvent_ATaunt1, + kInputEvent_ATaunt2, + kInputEvent_ATaunt3, + kInputEvent_ATaunt4, + + kNumEvents + +} TInputEvent; + +// input sprocket +enum +{ + kNeed_Yaw, + kNeed_Pitch, + kNeed_Roll, + kNeed_MoveX, + kNeed_MoveZ, + kNeed_MoveY, + + kNeed_MouseYaw, + kNeed_MousePitch, + kNeed_MouseRoll, + kNeed_MouseMoveX, + kNeed_MouseMoveZ, + kNeed_MouseMoveY, + + kNeed_Accelerate, + kNeed_Reverse, + kNeed_SlideLeft, + kNeed_SlideRight, + + kNeed_SlideUp, + kNeed_SlideDown, + kNeed_TurnLeft, + kNeed_TurnRight, + kNeed_PitchUp, + kNeed_PitchDown, + kNeed_BankLeft, + kNeed_BankRight, + + kNeed_Seperator, + + kNeed_FirePrim, + kNeed_FireSecond, + kNeed_NextPrim, + kNeed_NextSecond, +// kNeed_PrevPrim, +// kNeed_PrevSecond, + kNeed_AfterBurn, + kNeed_HeadLight, + kNeed_Flare, + kNeed_RearView, + kNeed_Map, + + kNeed_InventoryUse, + kNeed_InventoryNext, + kNeed_InventoryPrev, + kNeed_CounterUse, + kNeed_CounterNext, + kNeed_CounterPrev, + + kNeed_BankOn, + kNeed_SlideOn, +/* + kNeed_ATaunt1, + kNeed_ATaunt2, + kNeed_ATaunt3, + kNeed_ATaunt4, +*/ + kNeed_COUNT +}; + +#define kNeedsVersion '0003' + +static Boolean gInputActive = false; +static ISpElementListReference gInputEventList = NULL; +static ISpElementListReference gInputHoldDownEventList = NULL; +static ISpElementReference gInputElement[kNeed_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + + +#define ISpSymmetricAxisToFloat(axis) ((((float) axis) - kISpAxisMiddle) / (kISpAxisMaximum-kISpAxisMiddle)) +#define ISpAsymmetricAxisToFloat(axis) (((float) axis) / (kISpAxisMaximum)) + +void inSprocket_Init( void); + +void inSprocket_Exit( void); + +void inSprocket_Configure( void); +void InSprocket_Flush(void); + +float inSprocket_GetAllAxis (int axis); +float inSprocket_GetAxis (int axis); +float inSprocket_GetMouse(int axis); +int inSprocket_GetAxisInt(int axis); + +TInputEvent inSprocket_GetButtonEvent( void); + +void inSprocket_Activate( Boolean inActivate); + +#endif /* __InSprocket__ */ \ No newline at end of file diff --git a/mac/InSprocketResource.h b/mac/InSprocketResource.h new file mode 100644 index 000000000..61dbf8b87 --- /dev/null +++ b/mac/InSprocketResource.h @@ -0,0 +1,69 @@ +/* + * File: InSprocketResource.h + * + */ + +#ifndef __InSprocketResource__ +#define __InSprocketResource__ + + +// ICN#s -- Icon suites +enum { + kIconSuiteID_YawR = 132, + kIconSuiteID_YawL = 133, + kIconSuiteID_Yaw = 134, + + kIconSuiteID_PitchD = 135, + kIconSuiteID_PitchU = 136, + kIconSuiteID_Pitch = 137, + + kIconSuiteID_RollL = 138, + kIconSuiteID_RollR = 139, + kIconSuiteID_Roll = 140, + + kIconSuiteID_SlideUp = 141, + kIconSuiteID_SlideDown = 142, + kIconSuiteID_SlideVert = 143, + + kIconSuiteID_SlideRight = 144, + kIconSuiteID_SlideLeft = 145, + kIconSuiteID_SlideHorz = 146, + + kIconSuiteID_Back = 147, + kIconSuiteID_Foward = 148, + kIconSuiteID_Thrust = 149, + + kIconSuiteID_BankOn = 150, + kIconSuiteID_SlideOn = 151, + + kIconSuiteID_UseCM = 152, + kIconSuiteID_NextCM = 153, + kIconSuiteID_PrevCM = 154, + + kIconSuiteID_HeadLight = 155, + kIconSuiteID_Map = 156, + + kIconSuiteID_UseInv = 157, + kIconSuiteID_NextInv = 158, + kIconSuiteID_PrevInv = 159, + + kIconSuiteID_FirePrim = 160, + kIconSuiteID_FireSecond = 161, + kIconSuiteID_NextPrim = 162, + kIconSuiteID_NextSecond = 163, + kIconSuiteID_AfterBurn = 164, + kIconSuiteID_Flare = 165, + kIconSuiteID_RearView = 166, + + kIconSuiteID_PrevPrim = 168, + kIconSuiteID_PrevSecond = 169 +}; + +// setl +enum +{ + kSetListID = 128 +}; + + +#endif /* __InSprocketResourcee__ */ diff --git a/mac/MACAPP.CPP b/mac/MACAPP.CPP new file mode 100644 index 000000000..e98ab502e --- /dev/null +++ b/mac/MACAPP.CPP @@ -0,0 +1,405 @@ +/* + * $Logfile: /DescentIII/Main/mac/MACAPP.CPP $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:14 $ + * $Author: kevinb $ + * + * Mac Application Object. Encapsulates some app info for libraries + * + * $Log: MACAPP.CPP,v $ + * Revision 1.1.1.1 2003/08/26 03:58:14 kevinb + * initial 1.5 import + * + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * + * $NoKeywords: $ + */ +#define OEAPP_INTERNAL_MODULE +#include "Application.h" +#include "AppConsole.h" +#include "mono.h" +#include "networking.h" +#include +#include +#include +#include "pserror.h" +#include "ddio.h" +#include "mem.h" +// taken from Macuser.h +#ifndef WHEEL_DELTA +#define WHEEL_DELTA 120 +#endif +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x20a +#endif +/* Main windows Procedure for this OS object +*/ +const int MAX_MacAPPS = 4; +static struct tAppNodes +{ + WindowPtr hWnd; + oeMacApplication *app; +} Mac_AppObjects[MAX_MacAPPS]; +// system mouse info. +short w32_msewhl_delta=0; // -val = up, pos val = down, 0 = no change. +bool w32_mouseman_hack=false; +/* Mac Application Object + This object entails initialization and cleanup of all operating system + elements, as well as data that libraries may need to initialize their + systems. + The Mac Application object creates the application window and housekeeps + the window and instance handle for the application. + We also allow the option of setting these handles from outside the Application object. +*/ +extern void con_Defer(); +bool oeMacApplication::os_initialized = false; +bool oeMacApplication::first_time = true; +CGrafPtr pWin; +//short numDevices = 0; +// Creates the window handle and instance +oeMacApplication::oeMacApplication(const char *name, unsigned flags) //:oeApplication() +{ + Rect rect; + + if (oeMacApplication::first_time) { + int i; + for (i = 0; i < MAX_MacAPPS; i++) + { + Mac_AppObjects[i].hWnd = NULL; + Mac_AppObjects[i].app = NULL; + } + oeMacApplication::first_time = false; + + mem_Init(); + + } + if (flags & OEAPP_CONSOLE) { + m_X = CW_USEDEFAULT; + m_Y = CW_USEDEFAULT; + m_W = 640; + m_H = 480; + } else { + // initialize main window and display it. +#if 0 +#ifdef RELEASE + rect = qd.screenBits.bounds; +#else + SetRect(&rect, 0,0,640,480); +#endif + m_X = 0; + m_Y = 0; + m_W = rect.right - rect.left; + m_H = rect.bottom - rect.top; +#endif + } + m_Flags = flags; + strcpy(m_WndName, name); + os_init(); + m_hWnd = NULL; + m_WasCreated = true; + m_DeferFunc = NULL; +#ifndef DAJ_DEBUG +//#if 0 + SetupScreen(&m_hWnd); +#else +#ifdef USE_OPENGL + SetRect(&rect, 0, 20, 640, 500); + m_hWnd = (CGrafPort *)NewCWindow(NULL, &rect, "\pDescent 3", false, plainDBox, (WindowPtr) -1L, true, 0L); + ShowWindow((GrafPort *)m_hWnd); + rect = (*m_hWnd->portPixMap)->bounds; + m_X = rect.left; + m_Y = rect.bottom; + m_W = rect.right-rect.left; + m_H = rect.bottom - rect.top; +#endif +#endif +} + +// Create object with a premade window handle/instance +oeMacApplication::oeMacApplication(tMacAppInfo *appinfo) // :oeApplication() +{ + Rect rect; +// store handles + m_hWnd = appinfo->hwnd; + m_Flags = appinfo->flags; +// returns the dimensions of the window + rect = m_hWnd->portRect; +// rect = (**((WindowPeek)m_hWnd)->contRgn).rgnBBox; + appinfo->wnd_x = m_X = rect.left; + appinfo->wnd_y = m_Y = rect.top; + appinfo->wnd_w = m_W = rect.right-rect.left; + appinfo->wnd_h = m_H = rect.bottom - rect.top; + m_WasCreated = false; + os_init(); + clear_handlers(); + m_DeferFunc = NULL; +} + +oeMacApplication::~oeMacApplication() +{ + // do this only if we created the window, not just initializing the window + if (m_WasCreated) { + if (m_hWnd) { +#ifndef DAJ_DEBUG +//#if 1 + ShutdownScreen(&m_hWnd); +#else + #ifdef USE_OPENGL + CloseWindow((GrafPort *)m_hWnd); + #endif +#endif + } + } +} +// initializes the object +void oeMacApplication::init() +{ + if (m_Flags & OEAPP_CONSOLE) { + } + else { + } +} +// Function to retrieve information from object through a platform defined structure. +void oeMacApplication::get_info(void *info) +{ + tMacAppInfo *appinfo = (tMacAppInfo *)info; + appinfo->hwnd = m_hWnd; + appinfo->flags = m_Flags; + + Rect rect = (m_hWnd->portRect); +// Rect rect = (*m_hWnd->portPixMap)->bounds; + appinfo->wnd_x = m_X = rect.left; + appinfo->wnd_y = m_Y = rect.top; + appinfo->wnd_w = m_W = rect.right-rect.left; + appinfo->wnd_h = m_H = rect.bottom - rect.top; +// appinfo->wnd_x = m_X; +// appinfo->wnd_y = m_Y; +// appinfo->wnd_w = m_W; +// appinfo->wnd_h = m_H; +} +void oeMacApplication::set_sizepos(int x, int y, int w, int h) +{ + if (!m_hWnd) + return; + m_X = x; + m_Y = y; + m_W = w; + m_H = h; + MoveWindow((GrafPort *)m_hWnd, x, y, TRUE); + SizeWindow((GrafPort *)m_hWnd, w, h, false); +} +// real defer code. +#define DEFER_PROCESS_ACTIVE 1 // process is still active +#define DEFER_PROCESS_INPUT_IDLE 2 // process input from os not pending. +//#include "ddio.h" +int oeMacApplication::defer_block() +{ + // IDLE PROCESSING + if (m_DeferFunc) + (*m_DeferFunc)(this->active()); + return (0); +} +// defer returns some flags. essentially this function defers program control to OS. +unsigned oeMacApplication::defer() +{ + int result; + con_Defer(); +// system mouse info. + w32_msewhl_delta = 0; + do + { + result = defer_block(); + } + while ((result == DEFER_PROCESS_ACTIVE) || (result == DEFER_PROCESS_INPUT_IDLE)); + return 0; +} +// set a function to run when deferring to OS. +void oeMacApplication::set_defer_handler(void (*func)(bool)) +{ + m_DeferFunc = func; +} +// initializes OS components. +void oeMacApplication::os_init() +{ +} +// These functions allow you to add message handlers. +bool oeMacApplication::add_handler(unsigned msg, tOEMacMsgCallback fn) +{ + int i=0; +// search for redundant callbacks. + for (i = 0; i < MAX_MSG_FUNCTIONS; i++) + { + if (m_MsgFn[i].msg == msg && m_MsgFn[i].fn == fn) + return true; + } + for (i = 0; i < MAX_MSG_FUNCTIONS; i++) + { + if (m_MsgFn[i].fn == NULL) { + m_MsgFn[i].msg = msg; + m_MsgFn[i].fn = fn; + return true; + } + } + Debugger(); // We have reached the max number of message functions! + return false; +} +// These functions remove a handler +bool oeMacApplication::remove_handler(unsigned msg, tOEMacMsgCallback fn) +{ + int i; + if (!fn) + Debugger(); + for (i = 0; i < MAX_MSG_FUNCTIONS; i++) + { + if (msg == m_MsgFn[i].msg && m_MsgFn[i].fn == fn) { + m_MsgFn[i].fn = NULL; + return true; + } + } + return false; +} +// Run handler for message (added by add_handler) +bool oeMacApplication::run_handler(WindowPtr wnd, unsigned msg, unsigned wParam, long lParam) +{ + int j; +// run user-defined message handlers +// the guess here is that any callback that returns a 0, will not want to handle the window's WndProc function. + for (j = 0; j < MAX_MSG_FUNCTIONS; j++) + if (msg == m_MsgFn[j].msg && m_MsgFn[j].fn) { + if (!(*m_MsgFn[j].fn)(wnd, msg, wParam, lParam)) + return false; + } + return true; +} +void oeMacApplication::clear_handlers() +{ + int j; + for (j = 0; j < MAX_MSG_FUNCTIONS; j++) + m_MsgFn[j].fn = NULL; +} +void oeMacApplication::delay(float secs) +{ + int result; +//DAJ long msecs = (long)(secs * 1000.0); + long time_start; + w32_msewhl_delta = 0; + time_start = timer_GetTime(); + Sleep(0); + while (timer_GetTime() < (time_start+secs)) +//DAJ while (timer_GetTime() < (time_start+msecs)) + { + this->defer_block(); + } +// block if messages are still pending (for task switching too, this call will not return until messages are clear + do + { + result = this->defer_block(); + } + while (result == DEFER_PROCESS_ACTIVE || result == DEFER_PROCESS_INPUT_IDLE); +} +#ifdef FIXED +LRESULT MacAPI MyWndProc( HWND hWnd,UINT msg,UINT wParam,LPARAM lParam) +{ + int i=-1; + bool force_default = false; + for (i = 0; i < MAX_MacAPPS; i++) + if (Mac_AppObjects[i].hWnd == hWnd) break; + if (i == MAX_MacAPPS) + i = -1; + switch (msg) + { + LPCREATESTRUCT lpCreateStruct; + case WM_CREATE: + // here we store the this pointer to the app object this instance belongs to. + lpCreateStruct = (LPCREATESTRUCT)lParam; + for (i = 0; i < MAX_MacAPPS; i++) + if (Mac_AppObjects[i].hWnd == NULL) break; + if (i == MAX_MacAPPS) + debug_break(); + Mac_AppObjects[i].hWnd = hWnd; + Mac_AppObjects[i].app = (oeMacApplication *)lpCreateStruct->lpCreateParams; + Mac_AppObjects[i].app->clear_handlers(); + force_default = true; + break; + case WM_DESTROY: + // get window handle and clear it. + if (i == MAX_MacAPPS) + debug_break(); + Mac_AppObjects[i].hWnd = NULL; + Mac_AppObjects[i].app = NULL; + i = -1; + break; + case WM_SYSCOMMAND: + // bypass screen saver and system menu. + if((wParam & 0xFFF0)==SC_SCREENSAVE || (wParam & 0xFFF0)==SC_MONITORPOWER) + return 0; + if ((wParam & 0xfff0)== SC_KEYMENU) + return 0; + break; + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + if (lParam & 0x20000000) + return 0; + break; + case WM_POWERBROADCAST: // Won't allow OS to suspend operation for now. + mprintf((0, "WM_POWERBROADCAST=%u,%d\n", wParam, lParam)); + if (wParam == PBT_APMQUERYSUSPEND) { + return BROADCAST_QUERY_DENY; + } + break; + case WM_MOUSEWHEEL: + case 0xcc41: + if (w32_mouseman_hack) { + if (msg!=0xcc41) { + w32_msewhl_delta = HIWORD(wParam); + } + else { + w32_msewhl_delta = (short)(wParam); + } + } + else if (msg == WM_MOUSEWHEEL) { + w32_msewhl_delta = HIWORD(wParam); + } + break; + } + + oeMacApplication *Macapp = Mac_AppObjects[i].app; +// if this window not on list, then run default window proc. + if (i == -1 || Macapp == NULL || force_default) + return DefwindowProc(hWnd, msg, wParam, lParam); + if (!Macapp->run_handler((HWnd)hWnd, (unsigned)msg, (unsigned)wParam, (long)lParam)) + return 0; + +// run user defined window procedure. + return + (LRESULT)Macapp->WndProc((HWnd)hWnd, (unsigned)msg, (unsigned)wParam, (long)lParam); +} +// detect if application can handle what we want of it. +bool oeMacApplication::GetSystemSpecs(const char *fname) +{ + FILE *fp = fopen(fname, "wt"); + tMacOS os; + int maj,min, build; + char desc[256]; + if (!fp) return false; + os = oeMacApplication::version(&maj,&min,&build, desc); + fprintf(0, "OS: %s %d.%d.%d %s\n", "Mac", maj,min, build,desc); +// get system memory info + MEMORYSTATUS mem_stat; + mem_stat.dwLength = sizeof(MEMORYSTATUS); + GlobalMemoryStatus(&mem_stat); + fprintf(fp, "Memory:\n"); + fprintf(fp, "\tLoad:\t\t\t%u\n\tTotalPhys:\t\t%u\n\tAvailPhys:\t\t%u\nPageFile:\t\t%u\n", + (unsigned)mem_stat.dwMemoryLoad, (unsigned)mem_stat.dwTotalPhys, (unsigned)mem_stat.dwAvailPhys, (unsigned)mem_stat.dwTotalPageFile); + fprintf(fp, "\tPageFileFree:\t%u\n\tVirtual:\t\t%u\n\tVirtualFree:\t%u\n", + (unsigned)mem_stat.dwAvailPageFile, (unsigned)mem_stat.dwTotalVirtual, (unsigned)mem_stat.dwAvailVirtual); + fclose(fp); + return true; +} +#endif diff --git a/mac/MACAPP.H b/mac/MACAPP.H new file mode 100644 index 000000000..7827d54b9 --- /dev/null +++ b/mac/MACAPP.H @@ -0,0 +1,139 @@ +/* + * $Logfile: /DescentIII/Main/mac/MACAPP.H $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:14 $ + * $Author: kevinb $ + * + * Application object for Mac + * + * $Log: MACAPP.H,v $ + * Revision 1.1.1.1 2003/08/26 03:58:14 kevinb + * initial 1.5 import + * + * + * 3 4/12/00 7:08p Matt + * From Duane for 1.4 + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * + * $NoKeywords: $ + */ +#ifndef MACAPP_H +#define MACAPP_H +#define MAX_MSG_FUNCTIONS 64 +#ifndef CW_USEDEFAULT +#define CW_USEDEFAULT 0 +#endif +#include "Application.h" +// This structure is used to retrieve and set +typedef struct tMacAppInfo { + unsigned flags; // Application Flags + CWindowPtr hwnd; // Window Handle + int wnd_x, wnd_y, wnd_w, wnd_h; // Window dimensions +} tMacAppInfo; +/* Mac Application Object + This object entails initialization and cleanup of all operating system + elements, as well as data that libraries may need to initialize their + systems. + The Mac Application object creates the application window and housekeeps + the window and instance handle for the application. + We also allow the option of setting these handles from outside the Application object. + +tOEMacMsgCallback: + Callbacks return a 0 if we don't want to call the default action for the message, otherwise return 1 + Callbacks are executed in the Window's message procedure, so the calling program need not manually run + the handler (although, I give you a function to do just that, if you can't depend on the WndProc). + NOTE: the callbacks are executed in the window's root procedure and not in the inherited WndProc function. + This means, that callbacks have priority over the application defined WndProc. + General priority of message handling. + Outrage Window Procedure: + takes care of window creation, destruction and system menus. + if message's window not registered then + calls Mac Window Procedure + else + runs OEMacMsgCallbacks for that message + if OEMacMsgCallback functions return 0 then + leave Outrage Window Procedure + calls oeMacApplication->WndProc hierarchy which + optionally calls Mac Window Procedure. + endif +*/ +typedef int (*tOEMacMsgCallback)(WindowPtr,unsigned,unsigned,long); +class oeMacApplication: public oeApplication +{ +#if defined(OEAPP_INTERNAL_MODULE) +public: +#else +private: +#endif +// WindowPtr m_hWnd; + CWindowPtr m_hWnd; + + bool m_WasCreated; // Tells us if this app created the window handle or not. + int m_NumMsgFn; // Number of message functions. + struct { // assign functions to messages. + unsigned msg; + tOEMacMsgCallback fn; + } + m_MsgFn[MAX_MSG_FUNCTIONS]; + void (*m_DeferFunc)(bool); // function to call when deffering to OS (OnIdle for instance) + char m_WndName[64]; // name of window. + static bool os_initialized; // is the OS check initialized? + static bool first_time; // first time init? +private: + int defer_block(); // real defer code. +public: +// Creates the window handle + oeMacApplication(const char *name, unsigned flags); + oeMacApplication(tMacAppInfo *appinfo); + + oeMacApplication(void) {}; + virtual ~oeMacApplication(); +// initializes the object + virtual void init(); +// Function to retrieve information from object through a platform defined structure. + virtual void get_info(void *appinfo); +// defer returns some flags. essentially this function defers program control to OS. + virtual unsigned defer(); +// set a function to run when deferring to OS. + virtual void set_defer_handler(void (*func)(bool)); +// delays app for a certain amount of time + virtual void delay(float secs); +// Sizes the displayable region of the app (the window) + void set_sizepos(int x, int y, int w, int h); +// These functions allow you to add message handlers. + bool add_handler(unsigned msg, tOEMacMsgCallback fn); +// These functions remove a handler + bool remove_handler(unsigned msg, tOEMacMsgCallback fn); +// Run handler for message (added by add_handler) + bool run_handler(WindowPtr wPtr, unsigned msg, unsigned wParam, long lParam); +// clears handler list + void clear_handlers(); +// These variables are only accessable to modules that have DD_ACCESS. +#if defined(DD_ACCESS_RING) +public: +#else +private: +#endif + unsigned m_Flags; + int m_X, m_Y, m_W, m_H; // window dimensions. +private: + void os_init(); // initializes OS components. +}; + +#endif + +//void SetupScreen (GDHandle *hGD, CGrafPtr *m_hWnd, short *numDevices); +//void SetupScreen (GDHandle *hGD, CGrafPtr *m_hWnd); +void SetupScreen (CGrafPtr *m_hWnd); +void ShutdownScreen(CGrafPtr *m_hWnd); + +void SwitchDSpContex(int newContext); + +void PauseDSpContext(); +void ResumeDSpContext(); \ No newline at end of file diff --git a/mac/MACCON.CPP b/mac/MACCON.CPP new file mode 100644 index 000000000..57c18ad8c --- /dev/null +++ b/mac/MACCON.CPP @@ -0,0 +1,368 @@ +/* + * $Logfile: /DescentIII/Main/mac/MACCON.CPP $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:14 $ + * $Author: kevinb $ + * + * + * + * $Log: MACCON.CPP,v $ + * Revision 1.1.1.1 2003/08/26 03:58:14 kevinb + * initial 1.5 import + * + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * + * $NoKeywords: $ + */ +#include "AppConsole.h" +#include +void con_Printf(const char *fmt, ...) +{ + char buf[256]; + va_list args; +// filter out messages + va_start(args, fmt ); + vsprintf(buf,fmt,args); +#ifdef FIXED + int len = strlen(buf); + if(len >= CON_MAX_STRINGLEN ) + { + //we overflowed our buffer!!! + //we need to do some sort of error here!!!! + buf[CON_MAX_STRINGLEN-1] = '\0'; + } +// filter out unprintable characters + char *p,*fp,filter_buf[CON_MAX_STRINGLEN]; + p = buf; + fp = filter_buf; + while( *p ) + { + if( *p == 0x01 ) + { + //this is a color, skip the next 3 + p+=4; + }else + { + if( isalnum (*p) || ispunct(*p) || (*p==' ') || (*p=='\n') || (*p=='\r') || (*p=='\b')) + { + *fp = *p; + fp++; + } + p++; + } + } + *fp = '\0'; + con_Puts(filter_buf); + if (hConWnd) { + InvalidateRect(hConWnd, NULL, TRUE); + UpdateWindow(hConWnd); + } +#endif +} +bool con_Input(char *buf, int buflen) +{ +#ifdef FIXED + if (Con_read_buf[0]) { + strncpy(buf, Con_read_buf, buflen-1); + buf[buflen-1] = 0; + Con_read_buf[0] = 0; + return true; + } +#endif + return false; +} +void con_Defer() +{ +#ifdef FIXED + if (Con_newline) { + Con_newline = false; + con_Puts("%"); + if (hConWnd) { + InvalidateRect(hConWnd, NULL, TRUE); + UpdateWindow(hConWnd); + } + } +#endif +} +// console window helper functions +void con_Create(WindowPtr hWnd/*, LPCREATESTRUCT lpcs*/) +{ +#ifdef FIXED + oeWin32Application *app = (oeWin32Application *)lpcs->lpCreateParams; + HDC hdc; + HFONT oldfont; + RECT rect; + TEXTMETRIC tm; +// create console region + Con_row = 0; + Con_col = 0; + Con_buffer = new char[CON_SCROLL_ROWS * (CON_SCROLL_COLS+1)]; + Con_read_buf[0] = 0; + Con_inp_pos = 0; + memset(Con_inp_buf, 0, sizeof(Con_inp_buf)); + memset(Con_buffer, 0, CON_SCROLL_ROWS*(CON_SCROLL_COLS+1)); + memset(Con_last_command, 0, sizeof(Con_last_command)); + con_Puts("Outrage PC Console v1.0\n"); + Con_newline = true; +// get font width and height + hdc = GetDC(hWnd); + oldfont = (HFONT)SelectObject(hdc, (HFONT)GetStockObject(ANSI_FIXED_FONT)); + GetTextMetrics(hdc, &tm); + Con_ch_w = tm.tmMaxCharWidth; + Con_ch_h = tm.tmHeight; + rect.left = lpcs->x; + rect.top = lpcs->y; + rect.right = rect.left + Con_ch_w*CON_SCROLL_COLS; + rect.bottom = rect.top + Con_ch_h*CON_SCROLL_ROWS; + + AdjustWindowRect(&rect, lpcs->style, FALSE); + MoveWindow(hWnd, lpcs->x, lpcs->y, rect.right-rect.left, rect.bottom-rect.top, TRUE); + app->m_X = lpcs->x; + app->m_Y = lpcs->y; + app->m_W = rect.right - rect.left; + app->m_H = rect.bottom - rect.top; + SelectObject(hdc, oldfont); + ReleaseDC(hWnd, hdc); + EnableMenuItem(GetSystemMenu(hWnd, FALSE), SC_CLOSE,MF_BYCOMMAND | MF_GRAYED); + hConWnd = hWnd; +#endif +} +void con_Paint(WindowPtr hWnd) +{ +#ifdef FIXED + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + int i; + HFONT hfont; +// use a fixed font. + hfont = (HFONT)SelectObject(hdc, (HFONT)GetStockObject(ANSI_FIXED_FONT)); +// draw buffer at 0 to Con_row to end of buffer. + for (i = 0; i < CON_SCROLL_ROWS; i++) + { + char *ptr = &Con_buffer[i*(CON_SCROLL_COLS+1)]; + TextOut(hdc, 0, i*Con_ch_h, ptr, strlen(ptr)); + } +// end + SelectObject(hdc, hfont); + EndPaint(hWnd, &ps); +#endif +} +void con_Destroy(WindowPtr hWnd) +{ +#ifdef FIXED + if (Con_buffer) + delete[] Con_buffer; + hConWnd = NULL; +#endif +} +void con_Puts(const char *str) +{ +#ifdef FIXED + int i,len = strlen(str); + char *row = &Con_buffer[Con_row * (CON_SCROLL_COLS+1)]; + bool endline = false; + for (i = 0; i < len; i++) + { + switch(str[i]) + { + case '\n': + endline = true; + break; + case '\b': + if(Con_col>0) + { + row[Con_col] = ' '; + Con_col--; + } + break; + default: + row[Con_col] = str[i]; + Con_col++; + break; + } + if (Con_col == CON_SCROLL_COLS) { + endline = true; + } + if (endline) { + Con_row++; + if (Con_row == CON_SCROLL_ROWS) { + con_Scroll(); + Con_row--; + } + row = &Con_buffer[Con_row * (CON_SCROLL_COLS+1)]; + memset(row, 0, CON_SCROLL_COLS+1); + Con_col = 0; + endline = false; + } + } + SetCaretPos(Con_ch_w * Con_col, Con_ch_h * Con_row); +#endif +} +void con_Scroll() +{ +#ifdef FIXED + int i; + for (i = 1; i < CON_SCROLL_ROWS; i++) + memcpy(&Con_buffer[(i-1)*(CON_SCROLL_COLS+1)], &Con_buffer[i*(CON_SCROLL_COLS+1)], CON_SCROLL_COLS+1); + memset(&Con_buffer[(i-1)*(CON_SCROLL_COLS+1)], 0, CON_SCROLL_COLS+1); +#endif +} +int con_KeyDown(WindowPtr hWnd, int vkey) +{ +#ifdef FIXED + //HDC hdc; + int x; + char *ptr = &Con_buffer[Con_row * (CON_SCROLL_COLS+1)]; + switch (vkey) + { + case VK_LEFT: // Left arrow + Con_col = max(Con_col - 1, 0); + Con_inp_pos = max(Con_inp_pos-1,0); + break; + case VK_RIGHT: // Right arrow + Con_col = min(Con_col+1, CON_SCROLL_COLS-1); + Con_inp_pos = min(Con_inp_pos+1, CON_SCROLL_COLS-1); + break; + case VK_UP: // Up arrow + // Replace the current buffer that is being typed with the last completed command (if there was one) + if(Con_last_command[0]!=0){ + memset(Con_inp_buf, 0, sizeof(Con_inp_buf)); + strcpy(Con_inp_buf,Con_last_command); + ptr[0] = '%'; + strcpy(&ptr[1],Con_last_command); //add for % + Con_inp_pos = strlen(Con_last_command); + Con_col = Con_inp_pos + 1; //add for % prompt + // The application will draw outside the + // WM_PAINT message processing, so hide the caret. + HideCaret(hWnd); + // Redraw the line, adjusted for the + // deleted character. + InvalidateRect(hWnd, NULL, TRUE); + UpdateWindow(hWnd); + // Display the caret. + ShowCaret(hWnd); + } + break; + case VK_DELETE: // Delete + // Move all the characters that followed the + // deleted character (on the same line) one + // space back (to the left) in the matrix. + for (x = Con_inp_pos; x < CON_SCROLL_COLS; x++) + Con_inp_buf[x] = Con_inp_buf[x+1]; + + for (x = Con_col; x < CON_SCROLL_COLS; x++) + ptr[x] = ptr[x+1]; + // The application will draw outside the + // WM_PAINT message processing, so hide the caret. + HideCaret(hWnd); + // Redraw the line, adjusted for the + // deleted character. + InvalidateRect(hWnd, NULL, TRUE); + UpdateWindow(hWnd); + // Display the caret. + ShowCaret(hWnd); + break; + } +// Adjust the caret position based on the +// virtual-key processing. + SetCaretPos(Con_ch_w * Con_col, Con_ch_h * Con_row); +#endif + return 0; +} +int con_Char(WindowPtr hWnd, int vkey) +{ +#ifdef FIXED + HDC hdc; + char *ptr = &Con_buffer[Con_row * (CON_SCROLL_COLS+1)]; + switch (vkey) + { + case 0x08: // Backspace + // Move the caret back one space, and then + // process this like the DEL key. + if (Con_inp_pos > 0) + Con_inp_pos--; + + if (Con_col > 0) { + Con_col--; + SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1L); + } + break; + case 0x0D: // Carriage return + { + // Go to the beginning of the next line. + // The bottom line wraps around to the top. + strcpy(Con_read_buf, Con_inp_buf); + Con_col = 0; + Con_row++; + if (Con_row == CON_SCROLL_ROWS) { + con_Scroll(); + Con_row--; + } + ptr = &Con_buffer[Con_row * (CON_SCROLL_COLS+1)]; + memset(ptr, 0, CON_SCROLL_COLS+1); + //only save the buffer if there is text in the buffer + char *p = Con_inp_buf; + while( *p ){ + if(isalnum(*p)){ + strcpy(Con_last_command,Con_inp_buf); + break; + } + p++; + } + memset(Con_inp_buf, 0, sizeof(Con_inp_buf)); + Con_inp_pos = 0; + // con_Printf("Command is %s.\n", Con_read_buf); + Con_newline = true; + }break; + case 0x1B: // Escape + { + memset(Con_inp_buf, 0, sizeof(Con_inp_buf)); + memset(ptr, 0, CON_SCROLL_COLS+1); + ptr[0] = '%'; + Con_inp_pos = 0; + Con_col = 1; //for % sign + // The application will draw outside the + // WM_PAINT message processing, so hide the caret. + HideCaret(hWnd); + // Redraw the line, adjusted for the + // deleted character. + InvalidateRect(hWnd, NULL, TRUE); + UpdateWindow(hWnd); + // Display the caret. + ShowCaret(hWnd); + }break; + case 0x0A: // Linefeed + break; + default: + if (Con_inp_pos < (CON_SCROLL_COLS-2)) { + // Add the character to the text buffer. + int oldcol = Con_col, oldrow = Con_row; + char str[2]; + str[0] = (char)vkey; + str[1] = 0; + con_Puts(str); + Con_inp_buf[Con_inp_pos] = (char)vkey; + // The application will draw outside the + // WM_PAINT message processing, so hide the caret. + HideCaret(hWnd); + // Draw the character on the screen. + hdc = GetDC(hWnd); + SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT)); + TextOut(hdc, Con_ch_w*oldcol, Con_ch_h*oldrow, str, 1); + ReleaseDC(hWnd, hdc); + // Display the caret. + ShowCaret(hWnd); + Con_inp_pos++; + } + break; + } + SetCaretPos(Con_col*Con_ch_w, Con_row*Con_ch_h); +#endif + return 0; +} diff --git a/mac/MACDATA.CPP b/mac/MACDATA.CPP new file mode 100644 index 000000000..f203983f2 --- /dev/null +++ b/mac/MACDATA.CPP @@ -0,0 +1,350 @@ +/* + * + * Database functions + * + * + * $NoKeywords: $ + */ +#include "appdatabase.h" +#include +#include "mem.h" +#include "cfile.h" +#include "ddio.h" +#include "ddio_mac.h" +#include "descent.h" +#include "debug.h" +#include "pserror.h" + +char RECORD_FORMAT_STR[]=":%s"; +char ENTRY_FORMAT_STR[]="-\t%s = %s"; + +#define STR_BUFF_SIZE 1024 + +void oeMacAppDatabase::DBReadIn(void) +{ + char str[64]; + DBRecord *record=NULL; + DBEntry *entry=NULL; + + this->data = NULL; + + while (fgets(str, STR_BUFF_SIZE, this->file)) + { + if (str[0] == RECORD_FORMAT_STR[0]) + { + if (record) + { + record->next = (DBRecord*)mem_malloc(sizeof(DBRecord)); + record = record->next; + } + else + { + record = (DBRecord*)mem_malloc(sizeof(DBRecord)); + this->data = record; + } + sscanf(str, RECORD_FORMAT_STR, record->name); + record->entries = NULL; + record->next = NULL; + entry = NULL; + } + else if (str[0] == ENTRY_FORMAT_STR[0]) + { + if (!record) + { + DebugStr("\pD3 Error: Bad Database Format"); + return; + } + if (entry) + { + entry->next = (DBEntry*)mem_malloc(sizeof(DBEntry)); + entry = entry->next; + } + else + { + entry = (DBEntry*)mem_malloc(sizeof(DBEntry)); + record->entries = entry; + } +//DAJ sscanf(str, ENTRY_FORMAT_STR, entry->label, entry->entry); +//DAJ entry->next = NULL; + + char temp_label[STR_BUFF_SIZE], temp_entry[STR_BUFF_SIZE]; + if(strlen(str) < sizeof(temp_entry)) { + sscanf(str, ENTRY_FORMAT_STR, temp_label, temp_entry); + entry->label = mem_strdup(temp_label); + entry->entry = mem_strdup(temp_entry); + entry->next = NULL; + } else { + mprintf((2, "DBReadIn: entry string to long\n")); + Int3(); + } + + } + + // no error for other line starts + // allow other lines as comments or blanks + } +} +void oeMacAppDatabase::DBWriteOut(void) +{ + char str[STR_BUFF_SIZE]; + DBRecord *record; + DBEntry *entry; + fseek(this->file, 0, SEEK_SET); + + record = this->data; + + while (record) + { + sprintf(str, RECORD_FORMAT_STR, record->name); + fputs(str, this->file); + fputc('\n', this->file); + + entry = record->entries; + while (entry) + { + sprintf(str, ENTRY_FORMAT_STR, entry->label, entry->entry); + fputs(str, this->file); + fputc('\n', this->file); + + entry = entry->next; + } + record = record->next; + } +} + +//Construction and destruction. +oeMacAppDatabase::oeMacAppDatabase() +{ + //Open up the database file, for reading, read in all data and keep it in memory + //then close the database + OSErr err; + short foundVRefNum; + long foundDirID; + FSSpec spec; + + err = FindFolder(kOnSystemDisk,'pref',kDontCreateFolder,&foundVRefNum,&foundDirID); + if(err) mprintf((2, "unable to find preferences folder\n")); + + err = FSMakeFSSpec(foundVRefNum, foundDirID, "\pDescent3 Prefs", &spec); + if(!(err == fnfErr || err == noErr)) + mprintf((2, "unable to make FSspec for Descent3 Prefs\n")); + + this->file = FSp_fopen(&spec, "rt", 'pref'); + this->data = NULL; + this->current = NULL; + if (this->file) + { + DBReadIn(); + fclose(this->file); + this->file = NULL; + } + create_record("Version"); +} +oeMacAppDatabase::oeMacAppDatabase(oeMacAppDatabase *parent) +{ + this->file = NULL; + this->data = parent->data; + this->current = NULL; +} +oeMacAppDatabase::~oeMacAppDatabase() +{ + OSErr err; + short foundVRefNum; + long foundDirID; + FSSpec spec; + + err = FindFolder(kOnSystemDisk,'pref',kDontCreateFolder,&foundVRefNum,&foundDirID); + if(err) mprintf((2, "unable to find preferences folder\n")); + + err = FSMakeFSSpec(foundVRefNum, foundDirID, "\pDescent3 Prefs", &spec); + if(!(err == fnfErr || err == noErr)) + mprintf((2, "unable to make FSspec for Descent3 Prefs\n")); + + this->file = FSp_fopen(&spec, "wt", 'pref'); + + if (this->file) + { + DBWriteOut(); + fclose(this->file); + this->file = NULL; + } +} +//Record functions +//these are actual folders of information +//creates an empty classification or structure where you can store information +bool oeMacAppDatabase::create_record(const char *pathname) +{ + DBRecord *last = NULL; + DBRecord *record = NULL; + int len = strlen(pathname); + + record = this->data; + + while (record) + { + if (len == strlen(record->name) && stricmp(pathname, record->name) == 0) + { + current = record; + return true; + } + last = record; + record = record->next; + } + if (last) + { + last->next = (DBRecord*)mem_malloc(sizeof(DBRecord)); + this->current = record = last->next; + } + else + { + this->data = (DBRecord*)mem_malloc(sizeof(DBRecord)); + this->current = record = this->data; + } + + strcpy(record->name, pathname); + record->entries = NULL; + record->next = NULL; + + return true; +} +//set current database focus to a particular record +bool oeMacAppDatabase::lookup_record(const char *pathname) +{ + DBRecord *record; + int len = strlen(pathname); + + record = this->data; + + while (record) + { + if (len == strlen(record->name) && stricmp(pathname, record->name) == 0) + { + current = record; + return true; + } + record = record->next; + } + return false; +} +//read either a string from the current record +bool oeMacAppDatabase::read(const char *label, char *entry, int *entrylen) +{ + DBEntry *record; + int len = strlen(label); + + record = this->current->entries; + + while (record) + { + if (len == strlen(record->label) && stricmp(label, record->label) == 0) + { + strcpy(entry, record->entry); + *entrylen = strlen(entry); + return true; + } + record = record->next; + } + return false; +} +//read a variable-sized integer from the current record +bool oeMacAppDatabase::read(const char *label, void *entry, int wordsize) +{ + DBEntry *record; + int len = strlen(label); + + record = this->current->entries; + + while (record) + { + if (len == strlen(record->label) && stricmp(label, record->label) == 0) + { + int value; + + sscanf(record->entry, "%d", &value); + switch(wordsize){ + case 1: + *((unsigned char *)entry) = (unsigned char)value; + break; + case 2: + *((unsigned short *)entry) = (unsigned short)value; + break; + case 4: + *((unsigned int *)entry) = (unsigned int)value; + break; + default: + DebugStr("\pD3 Error: Unable to read key, unsupported size"); + return false; + break; + } + return true; + } + record = record->next; + } + return false; +} +bool oeMacAppDatabase::read(const char *label, bool *entry) +{ + bool value; + if (!read(label, &value, sizeof(bool))) + return false; + *entry = (value != 0)?true:false; + return true; +} +//write either an integer or string to a record. +bool oeMacAppDatabase::write(const char *label, const char *entry, int entrylen) +{ + DBEntry *record, *last=NULL; + int len = strlen(label); + + record = this->current->entries; + + while (record) + { + if (len == strlen(record->label) && stricmp(label, record->label) == 0) + { + strcpy(record->entry, entry); + return true; + } + last = record; + record = record->next; + } + if (last) + { + last->next = (DBEntry*)mem_malloc(sizeof(DBEntry)); + record = last->next; + } + else + { + this->current->entries = (DBEntry*)mem_malloc(sizeof(DBEntry)); + record = this->current->entries; + } + + record->label = mem_strdup(label); + record->entry = mem_strdup(entry); + record->next = NULL; + return true; +} +bool oeMacAppDatabase::write(const char *label, int entry) +{ + char str[32]; + + sprintf(str, "%d", entry); + return(write(label, str, strlen(str))); +} +// get the current user's name from the os +void oeMacAppDatabase::get_user_name(char* buffer, ulong* size) +{ +#ifdef FIXED + char temp[STR_BUFF_SIZE]; + if (cuserid(temp)) + strncpy(buffer,temp,(*size)-1); + else + strncpy(buffer,"Unknown",(*size)-1); + buffer[(*size)-1] = '\0'; + *size = strlen(buffer); +#endif // FIXED + strncpy(buffer,"Unknown",(*size)-1); + buffer[(*size)-1] = '\0'; + *size = strlen(buffer); +} + diff --git a/mac/MACDATABASE.H b/mac/MACDATABASE.H new file mode 100644 index 000000000..ac52d8d93 --- /dev/null +++ b/mac/MACDATABASE.H @@ -0,0 +1,98 @@ +/* + * $Logfile: /DescentIII/Main/mac/MACDATABASE.H $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:15 $ + * $Author: kevinb $ + * + * Application Database for Win32 version + * + * $Log: MACDATABASE.H,v $ + * Revision 1.1.1.1 2003/08/26 03:58:15 kevinb + * initial 1.5 import + * + * + * 3 4/12/00 7:09p Matt + * From Duane for 1.4 + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * 6 5/11/99 12:12a Ardussi + * changes to compile on Mac + * + * 5 4/24/99 5:47p Samir + * added functions to set current win32 resource dll or exe. + * + * 4 7/27/97 11:07p Matt + * Added write_string() macro + * + * 3 7/24/97 3:06p Matt + * Added functions to read & write bools & variable-length integers, and + * fixed a few small bugs. + * + * 2 6/11/97 2:41p Samir + * fixed class declaration. + * + * 1 6/10/97 4:53p Samir + * Database control for Win32 systems + * + * $NoKeywords: $ + */ +#ifndef MACDATABASE +#define MACDATABASE +#include +typedef struct DBEntry +{ + char *label, *entry; + struct DBEntry *next; +} DBEntry; +typedef struct DBRecord +{ + char name[64]; + DBEntry *entries; + struct DBRecord *next; +} DBRecord; +/* oeWin32AppDatabase + to get info about the application from a managed database (or a custom info file) + we get our information from the registry! +*/ +class oeMacAppDatabase: public oeAppDatabase +{ + //unsigned hBaseKey; // look up from this key. + //unsigned hCurKey; // current key for lookup +protected: + //char m_Basepath[256]; + FILE *file; + DBRecord *data; + DBRecord *current; + + void DBReadIn(void); + void DBWriteOut(void); +public: + oeMacAppDatabase(); + oeMacAppDatabase(oeMacAppDatabase *parent); + virtual ~oeMacAppDatabase(); +// creates an empty classification or structure where you can store information + virtual bool create_record(const char *pathname); +// set current database focus to a particular record + virtual bool lookup_record(const char *pathname); +// read either an integer or string from the current record + virtual bool read(const char *label, char *entry, int *entrylen); + virtual bool read(const char *label, void *entry, int wordsize); //read a variable-size int + virtual bool read(const char *label, bool *entry); +// write either an integer or string to a record. + virtual bool write(const char *label, const char *entry, int entrylen); + virtual bool write(const char *label, int entry); + +// get the current user's name. + virtual void get_user_name(char* buffer, ulong* size); +}; +// pass name of dll which contains desired language +// NULL library is the default resource DLL +bool mac_SetResourceDLL(const char *libname); +// returns a string from the current resource +bool mac_GetStringResource(int txt_id, char *buffer, int buflen); +#endif \ No newline at end of file diff --git a/mac/MACFORCEFEEDBACK.CPP b/mac/MACFORCEFEEDBACK.CPP new file mode 100644 index 000000000..89cf22509 --- /dev/null +++ b/mac/MACFORCEFEEDBACK.CPP @@ -0,0 +1,339 @@ +/* +* $Logfile: /DescentIII/Main/mac/MACFORCEFEEDBACK.CPP $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:58:15 $ +* $Author: kevinb $ +* +* Linux force feedback stub +* +* $Log: MACFORCEFEEDBACK.CPP,v $ +* Revision 1.1.1.1 2003/08/26 03:58:15 kevinb +* initial 1.5 import +* + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * 3 4/16/99 8:28p Jeff + * created more stub functions + * + * 2 4/14/99 1:57a Jeff + * fixed case mismatched #includes + * + * 1 1/18/99 12:13a Jeff +* +* $NoKeywords: $ +*/ +#include "DDAccess.h" +#include +#include +#include +#include "pserror.h" +#include "pstring.h" +#include "mono.h" +#include "ddio.h" +#include "application.h" +#include "forcefeedback.h" +#include "mem.h" +#include "object.h" + +void ForceInit(void) {}; +void ForceShutdown(void) {}; +bool ForceIsEnabled(void) { return false; }; +void DoForceForRecoil(object *playerobj,object *weap) {}; +void DoForceForWall(object *playerobj, float hitspeed, int hitseg, int hitwall, vector *wall_normal) {}; +void DoForceForWeapon(object *me_obj,object *it_obj,vector *force_vec) {}; +void ForceEffectsPlay(int id,float *scale,int *direction) {}; +void ForceEffectsPlay(int id,float *scale,vector *direction) {}; +void DoForceForShake(float magnitude) {}; + +#if 0 +bool ddForce_found; //a Force Feedback device was found +bool ddForce_enabled; //Force Feedback is ready and can be used +// ------------------------------------------------------------------- +// ddio_ff_AttachForce +// Purpose: +// Attaches variables initialized in the general ddio system to +// the force feedback system. +// ------------------------------------------------------------------- +void ddio_ff_AttachForce(void) +{ +} +// ------------------------------------------------------------------- +// ddio_ff_DetachForce +// Purpose: +// Detaches variables used by the force-feedback system from the +// ddio system +// ------------------------------------------------------------------- +void ddio_ff_DetachForce(void) +{ +} +// ------------------------------------------------------------------- +// ddio_ff_Init +// Purpose: +// Initialize force feedback if available. +// ------------------------------------------------------------------- +int ddio_ff_Init(void) +{ + ddForce_found = ddForce_enabled = false; + return 0; +} +// ------------------------------------------------------------------- +// ddio_ffjoy_Init +// Purpose: +// Creates and acquires all joysticks +// +// Input: +// None +// +// Return: +// # of sticks acquired +// +// Description: +// +// ------------------------------------------------------------------- +int ddio_ffjoy_Init(void) +{ + ddForce_found = ddForce_enabled = false; + return 0; +} +// ------------------------------------------------------------------- +// ddio_ff_Acquire +// Purpose: +// Acquires a direct input device for use. +// +// Input: +// The device to acquire (use kDI_MaxJoy to acquire all available +// joysticks). +// +// Return: +// # of devices acquired. +// +// Description: +// Call this to gain access to a device after the device has been +// created & after regaining the focus after losing it. +// +// ------------------------------------------------------------------- +int ddio_ff_Acquire(tDevice) +{ + return 0; +} +// ------------------------------------------------------------------- +// ddio_ff_Unacquire +// Purpose: +// Unacquires a direct input device +// +// Input: +// The device to unacquire (use kDI_MaxJoy to unacquire all available +// joysticks). +// +// Return: +// # of devices unacquired. +// +// Description: +// Call this to lose access to a device after the device has been +// aquired +// +// ------------------------------------------------------------------- +int ddio_ff_Unacquire(tDevice) +{ + return 0; +} +// ------------------------------------------------------------------- +// ddio_ff_SetCoopLevel +// ------------------------------------------------------------------- +int ddio_ff_SetCoopLevel(tDevice, int) +{ + return 0; +} +// ------------------------------------------------------------------- +// ddio_ffjoy_Query +// Purpose: +// Besides checking what buttons/axis are available, this function +// also checks for force feedback support. +// ------------------------------------------------------------------- +int ddio_ffjoy_Query(int , int* , int* ) +{ + return 0; +} +/* +======================================================================== + Force Feedback Effect Functions +======================================================================== +*/ +// ------------------------------------------------------------------- +// ddio_ff_GetInfo +// Purpose: +// Returns information about the current state of the low-level +// Force Feedback system. +// ------------------------------------------------------------------- +void ddio_ff_GetInfo(bool *ff_found,bool *ff_enabled) +{ + if(ff_found) + *ff_found = false; + if(ff_enabled) + *ff_enabled = false; +} +// ------------------------------------------------------------------- +// ddio_ffb_Pause +// Purpose: +// Pause the FFB output on the given device. Use ddio_ffb_Continue to +// continue where you left off. +// ------------------------------------------------------------------- +void ddio_ffb_Pause(tDevice) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_Continue +// Purpose: +// Unpause the FFB output on the given device. Complimentary to +// ddio_ffb_Pause. +// ------------------------------------------------------------------- +void ddio_ffb_Continue(tDevice) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_Enable +// Purpose: +// Must be called after initialization in order to activate the +// device. +// Use ddio_ffb_Pause & ddio_ffb_Continue if you want disable forces +// temporarily and resume later. +// ------------------------------------------------------------------- +void ddio_ffb_Enable(tDevice) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_Disable +// Purpose: +// Turns off FFB, but effects still play on processor. +// ------------------------------------------------------------------- +void ddio_ffb_Disable(tDevice) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_effectCreate +// Purpose: +// Create a single effect for future playback. +// Effect is given a logical ID +// ------------------------------------------------------------------- +int ddio_ffb_effectCreate(tDevice, tFFB_Effect* ) +{ + return 0; +} +// ------------------------------------------------------------------- +// ddio_ffb_DestroyAll +// Purpose: +// Destroys all created effects +// ------------------------------------------------------------------- +void ddio_ffb_DestroyAll(void) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_effectPlay +// Purpose: +// Play an effect that was previously created. +// ------------------------------------------------------------------- +void ddio_ffb_effectPlay(short) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_effectStop +// Purpose: +// Stop a single effect. +// ------------------------------------------------------------------- +void ddio_ffb_effectStop(short) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_effectStopAll +// Purpose: +// Stops all forces on the given device. +// ------------------------------------------------------------------- +void ddio_ffb_effectStopAll(tDevice) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_effectUnload +// Purpose: +// Unload a single effect... Necessary to make room for other +// effects. +// ------------------------------------------------------------------- +void ddio_ffb_effectUnload(short) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_effectModify +// Purpose: +// Modifies a single effect, only if the given parameters are +// different from what's currently loaded. +// ------------------------------------------------------------------- +void ddio_ffb_effectModify(short, int* , unsigned int* , unsigned int* , unsigned int* , tEffInfo* , tEffEnvelope* ) +{ +} +// ------------------------------------------------------------------- +// ddio_ffb_GetEffectData +// Purpose: +// Retrieves affect data for the given parameters, pass NULL for those you don't want +// ------------------------------------------------------------------- +void ddio_ffb_GetEffectData(short, int* , unsigned int* , unsigned int* , unsigned int* , tEffInfo* , tEffEnvelope* ) +{ +} +// ------------------------------------------------------------------- +// ddio_ffjoy_EnableAutoCenter +// Purpose: +// Disables/Enables the autocentering of the joystick +// ------------------------------------------------------------------- +void ddio_ffjoy_EnableAutoCenter(tDevice,bool) +{ +} +// ------------------------------------------------------------------- +// ddio_ffjoy_SetGain +// Purpose: +// Sets the gain for joystick, pass a value of 0-1 +// ------------------------------------------------------------------- +void ddio_ffjoy_SetGain(tDevice,float ) +{ +} +// ------------------------------------------------------------------- +// ddio_ffjoy_IsAutoCentered +// Purpose: +// Returns true if the joystick is set for autocentering +// ------------------------------------------------------------------- +bool ddio_ffjoy_IsAutoCentered(tDevice) +{ + return true; +} +// ------------------------------------------------------------------- +// ddio_ffjoy_SupportAutoCenter +// Purpose: +// Returns true if the FF joystick supports auto centering +// ------------------------------------------------------------------- +bool ddio_ffjoy_SupportAutoCenter(tDevice) +{ + return false; +} +// Given a filename resource, this loads the file and creates a resource +// for it. It returns a handle to that resource. +// If it returns NULL, then it couldn't load the project. +// Make sure device is aquired before calling. +FORCEPROJECT ddio_ForceLoadProject(char *filename,tDevice dev) +{ + return NULL; +} +// Unloads a FORCEPROJECT file +void ddio_ForceUnloadProject(FORCEPROJECT prj) +{ +} +// Given a handle to a resource, and the name of the effect to load +// it will load that effect. Returns the effect ID, or -1 if it couldn't +// be created +int ddio_CreateForceFromProject(FORCEPROJECT project,char *forcename) +{ + return -1; +} + +#endif \ No newline at end of file diff --git a/mac/MACFUNCS.CPP b/mac/MACFUNCS.CPP new file mode 100644 index 000000000..87855ce2e --- /dev/null +++ b/mac/MACFUNCS.CPP @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include "mem.h" +#include "streamaudio.h" + +#if 0 +// Create an audio decoder +// You supply a function for reading bytes from the compressed data via a +// void *data handle, and the handle itself (typically a FILE *). +// Create_AudioDecoder returns a new AudioDecoder which can be used to +// read uncompressed decoded data from the compressed stream, +// and also returns the number of channels (1 or 2), the sample rate +// (e.g. 22050), and the number of samples contained in the compressed file +// (in case you want to pre-allocate a buffer to load them all into memory). +typedef unsigned ReadFunction(void *data, void *buf, unsigned qty); +AudioDecoder *Create_AudioDecoder(ReadFunction *reader, void *data,unsigned *pChannels, unsigned *pSampleRate,long *pSampleCount) +{ + return (AudioDecoder*)mem_malloc(sizeof(AudioDecoder)); +} +// Read from audio decoder at most the specified qty of bytes +// (each sample takes two bytes). +// Returns zero when the end of file is reached. +unsigned AudioDecoder_Read(AudioDecoder *ad, void *buf, unsigned qty) +{ +} +// Close audio decoder +void AudioDecoder_Close(AudioDecoder *ad) +{ + if(ad) mem_free(ad); +} +// Optional interface for supplying your own malloc and free functions +// Default is to use standard malloc and free. +void AudioDecoder_MallocFree(ad_malloc *fn_malloc, ad_free *fn_free) +{ +} +#endif \ No newline at end of file diff --git a/mac/MACGAMESPY.CPP b/mac/MACGAMESPY.CPP new file mode 100644 index 000000000..886e67151 --- /dev/null +++ b/mac/MACGAMESPY.CPP @@ -0,0 +1,604 @@ +/* + * $Logfile: /DescentIII/Main/mac/MACGAMESPY.CPP $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:15 $ + * $Author: kevinb $ + * + * Gamespy client code + * This library knows about the game and is responsible for telling the + * gamespy client and server about everything it wants to know + * + * + * $Log: MACGAMESPY.CPP,v $ + * Revision 1.1.1.1 2003/08/26 03:58:15 kevinb + * initial 1.5 import + * + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * + * + * $NoKeywords: $ +*/ +#include "pstypes.h" +#include "pserror.h" +#include "player.h" +#include "multi.h" +#include "networking.h" +#include "descent.h" +#include "ddio.h" +#include "args.h" +#include "CFILE.H" +#include "program.h" +#include +#include +#include "gamespyutils.h" +#include "gamespy.h" +#ifdef FIXED +extern short Multi_kills[MAX_NET_PLAYERS]; +extern short Multi_deaths[MAX_NET_PLAYERS]; +//Secret code... encrypted using some really high tech method... +char gspy_d3_secret[10];// = "feWh2G"; +const char origstring[] = {(const char)0x50,(const char)0xf8,(const char)0xa4,(const char)0xba,(const char)0xc7,(const char)0x7c}; +char gspy_cfgfilename[_MAX_PATH]; +#define MAX_GAMESPY_SERVERS 5 +#define MAX_GAMESPY_BUFFER 1400 +#define MAX_HOSTNAMELEN 300 +#define GSPY_HEARBEAT_INTERVAL 300 //Seconds between heartbeats. +#define GAMESPY_PORT 27900 +#define GAMESPY_LISTENPORT 20142 +#define THISGAMENAME "descent3" +#ifdef DEMO +#define THISGAMEVER "Demo2" +#elif defined(OEM) +#define THISGAMEVER "OEM" +#else +#define THISGAMEVER "Retail" +#endif +SOCKET gspy_socket; +SOCKADDR_IN gspy_server[MAX_GAMESPY_SERVERS]; +extern ushort Gameport; +int gspy_region = 0; +char gspy_outgoingbuffer[MAX_GAMESPY_BUFFER] = ""; +float gspy_last_heartbeat; +bool gspy_game_running = false; +int gspy_packetnumber = 0; +int gspy_queryid = 0; +char gspy_validate[MAX_GAMESPY_BUFFER] = ""; +unsigned short gspy_listenport; +#endif // FIXED +//Register a game with this library so we will tell the servers about it... +void gspy_StartGame(char *name) +{ +#ifdef FIXED + gspy_last_heartbeat = timer_GetTime()-GSPY_HEARBEAT_INTERVAL; + gspy_game_running = true; +#endif // FIXED +} +// Let the servers know that the game is over +void gspy_EndGame() +{ +#ifdef FIXED + gspy_game_running = false; +#endif // FIXED +} +//Initialize gamespy with the info we need to talk to the servers +int gspy_Init(void) +{ +#ifdef FIXED +#ifndef OEM + char cfgpath[_MAX_PATH*2]; + int argnum = FindArg("-gspyfile"); + if(argnum) + { + strcpy(gspy_cfgfilename,GameArgs[argnum+1]); + } + else + { + strcpy(gspy_cfgfilename,"gamespy.cfg"); + } + + for(int a=0;ah_addr_list[0],sizeof(unsigned int)); + memcpy(&gspy_server[i].sin_addr,he->h_addr_list[0],sizeof(unsigned int)); + } + } + else + { + //This is just a number + //gspy_server[i].sin_addr.S_un.S_addr = inet_addr(hostn); + INADDR_SET_SUN_SADDR(&gspy_server[i].sin_addr,inet_addr(hostn)); + //break; + } + } + #if defined(WIN32) + if(gspy_server[i].sin_addr.S_un.S_addr != INADDR_NONE) + { + mprintf((0,"Sending gamespy heartbeats to %s:%d\n",inet_ntoa(gspy_server[i].sin_addr),htons(gspy_server[i].sin_port))); + } + #elif defined(__LINUX__) + if(gspy_server[i].sin_addr.s_addr != INADDR_NONE) + { + mprintf((0,"Sending gamespy heartbeats to %s:%d\n",inet_ntoa(gspy_server[i].sin_addr),htons(gspy_server[i].sin_port))); + } + #endif + } + } +#endif +#endif // FIXED + return 1; +} +//Takes a gspy response and puts the appropriate validation code to the end +//Of the string. If crypt is something besides NULL, create and tack the proper +//response to the end +#define VALIDATE_SIZE 6 +bool gpsy_ValidateString(char *str,char *crypt) +{ +#ifdef FIXED + + char keyvalue[80]; + unsigned char encrypted_val[VALIDATE_SIZE]; //don't need to num terminate + unsigned char encoded_val[(VALIDATE_SIZE * 4) / 3 + 1]; + + if(crypt) + { + strcpy((char *)encrypted_val,crypt); + gspy_encrypt(encrypted_val,VALIDATE_SIZE, (unsigned char *)gspy_d3_secret); + gspy_encode(encrypted_val,VALIDATE_SIZE, (unsigned char *)encoded_val); + sprintf(keyvalue,"\\validate\\%s",encoded_val); + strcat(str,keyvalue); + } + sprintf(keyvalue,"\\final\\"); + strcat(str,keyvalue); +#endif // FIXED + return false; +} +//Check the socket for data, and respond properly if needed +//Also send heartbeat when needed +void gspy_DoFrame() +{ +#ifdef FIXED +#ifndef OEM + SOCKADDR_IN fromaddr; + int bytesin; + int fromsize = sizeof(SOCKADDR_IN); + char inbuffer[MAX_GAMESPY_BUFFER]; + + + if(!gspy_game_running) + return; + //If it's time, send the heartbeat + if((timer_GetTime()-gspy_last_heartbeat)>GSPY_HEARBEAT_INTERVAL) + { + for(int a=0;a 0) + { + *(inbuffer+bytesin) = NULL; + mprintf((0,"Got a gamespy request:\n%s\n",inbuffer)); + gspy_ParseReq(inbuffer,&fromaddr); + } + else if(bytesin == SOCKET_ERROR) + { + int lerror = WSAGetLastError(); + if(lerror != WSAEWOULDBLOCK) + { + mprintf((0,"Warning: recvfrom failed for gamespy! (%d)\n",lerror)); + } + } + }while(bytesin > 0); +#endif +#endif // FIXED +} +//Sends the packet out to whoever it is that we are sending to +int gspy_SendPacket(SOCKADDR_IN *addr) +{ +#ifdef FIXED + gspy_packetnumber++; //packet numbers start at 1 + char keyvalue[80]; + if(!*gspy_outgoingbuffer) + { + //It's an empty buffer, so don't send anything!! + return 0; + } + gpsy_ValidateString(gspy_outgoingbuffer,*gspy_validate?gspy_validate:NULL); + sprintf(keyvalue,"\\queryid\\%d.%d",gspy_queryid, gspy_packetnumber); + strcat(gspy_outgoingbuffer,keyvalue); + + mprintf((0,"GSPYOUT:%s\n",gspy_outgoingbuffer)); + sendto(gspy_socket,gspy_outgoingbuffer,strlen(gspy_outgoingbuffer)+1,0,(SOCKADDR *)addr,sizeof(SOCKADDR_IN)); + *gspy_outgoingbuffer = NULL; +#endif // FIXED + return 0; +} +//Adds some values\keys to the send buffer and sends the packet if it overflows +int gspy_AddToBuffer(SOCKADDR_IN *addr,char *addstr) +{ +#ifdef FIXED + if(strlen(gspy_outgoingbuffer)+strlen(addstr)+50>=MAX_GAMESPY_BUFFER+1) + { + //package up this response and send this packet + gspy_SendPacket(addr); + + } + else + { + strcat(gspy_outgoingbuffer,addstr); + } +#endif // FIXED + return 1; +} +//Looks for the secure key in the request and returns it if there is. If there isn't, it returns a NULL +char * gspy_GetSecure(char * req) +{ +#ifdef FIXED + char *tokp; + char str[MAX_GAMESPY_BUFFER]; + strcpy(str,req); + tokp = strtok(str,"\\"); + if(tokp) + { + while(tokp) + { + if(strcmpi(tokp,"secure")==0) + { + tokp = strtok(NULL,"\\"); + return tokp; + } + tokp = strtok(NULL,"\\"); + }; + return NULL; + } + else + { + return NULL; + } +#endif // FIXED + return NULL; +} +int gspy_ContainsKey(char *buffer,char *key) +{ +#ifdef FIXED + char str[MAX_GAMESPY_BUFFER]; + char lowkey[MAX_GAMESPY_BUFFER]; + strcpy(str,buffer); + int len = strlen(str); + int i; + //If it's an empty string return 0 + if(*buffer=='\0') + return 0; + for(i=0;i + long GetCurrentA5(void) + ONEWORDINLINE(0x200D); +#endif + */ +/* + * Apple Universal Headers 3.1 + * + * Uncomment any additional #includes you want to add to your MacHeaders. + */ +// #include +// #include + #include + #include + #include + #include + #include +// #include + #include + #include + #include + #include + #include + #include + #include + #include +// #include +// #include + #include +// #include +// #include + #include +// #include +// #include +// #include + #include +// #include +// #include +// #include + #include +// #include + #include +// #include +// #include + #include + #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include + #include + #include +// #include +// #include + #include +// #include + #include + #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include + #include + #include + #include +// #include + #include +// #include +// #include + #include + #include + #include + #include + #include + #include +// #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include + #include +// #include + #include +// #include +// #include + #include +// #include +// #include +// #include + #include +// #include +// #include + #include +// #include + #include +// #include + #include + #include +// #include +// #include + #include // Start using MacMemory.h + #include +// #include + #include + #include +// #include +// #include +// #include + #include +// #include + #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include + #include + #include + #include +// #include + #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include + #include + #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include + #include + #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include +// #include +// #include + #include + #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include + #include + #include +// #include + #include // Start using TextUtils.h +// #include +// #include +// #include + #include + #include +// #include +// #include + #include + #include + #include + #include +// #include +// #include + #include +// #include + #include // Start using MacTypes.h +// #include +// #include + #include +// #include + #include // Start using MacWindows.h +// #include +// #include +/* + * Required for c-style toolbox glue functions: c2pstr and p2cstr + * (matches the inverse operation at the start of the file...) + */ +#include +#include +#if __MC68K__ && !__CFM68K__ + #pragma d0_pointers reset +#endif diff --git a/mac/MACHEADERSD3.H b/mac/MACHEADERSD3.H new file mode 100644 index 000000000..7f3b40e36 --- /dev/null +++ b/mac/MACHEADERSD3.H @@ -0,0 +1,325 @@ +// =========================================================================== +// MacHeaders.c ©1995-1998 Metrowerks Inc. All rights reserved. +// =========================================================================== +// +// Includes used to generate the 'MacHeaders' precompiled header for +// Metrowerks C/C++. +// +// RA/8/19/98 Updated to Universal Headers 3.1 +/* + * Required for c-style toolbox glue functions: c2pstr and p2cstr + * (the inverse operation (pointers_in_A0) is performed at the end...) + */ +#if __MC68K__ && !__CFM68K__ + #pragma d0_pointers on +#endif +/* + * To allow the use of ToolBox calls which have now become obsolete on + * PowerPC, but which are still needed for System 6 applications, we need to + * #define OBSOLETE. If your application will never use these calls then you + * can comment out this #define. NB: This is for 68K only ... + * + +#if !defined(powerc) && !defined(OBSOLETE) + #define OBSOLETE 1 +#endif + */ +#ifndef OLDROUTINENAMES + #define OLDROUTINENAMES 0 +#endif +#ifndef OLDROUTINELOCATIONS + #define OLDROUTINELOCATIONS 0 +#endif +/* + * Metrowerks-specific definitions + * + * These definitions are commonly used but not in Apple's headers. We define + * them in our precompiled header so we can use the Apple headers without + * modification. + */ +#ifndef PtoCstr + #define PtoCstr p2cstr +#endif +#ifndef CtoPstr + #define CtoPstr c2pstr +#endif +#ifndef PtoCString + #define PtoCString p2cstr +#endif +#ifndef CtoPString + #define CtoPString c2pstr +#endif +#ifndef topLeft + #define topLeft(r) (((Point *) &(r))[0]) +#endif +#ifndef botRight + #define botRight(r) (((Point *) &(r))[1]) +#endif +#ifndef TRUE + #define TRUE true +#endif +#ifndef FALSE + #define FALSE false +#endif +/* RA/8/19/98 No longer necessary -- will be removed for Pro5 +#ifndef powerc + #include + long GetCurrentA5(void) + ONEWORDINLINE(0x200D); +#endif + */ +/* + * Apple Universal Headers 3.1 + * + * Uncomment any additional #includes you want to add to your MacHeaders. + */ +// #include +// #include + #include + #include + #include + #include + #include +// #include + #include + #include + #include + #include + #include + #include + #include + #include +// #include +// #include + #include +// #include +// #include + #include +// #include +// #include +// #include + #include +// #include +// #include +// #include + #include +// #include + #include +// #include +// #include + #include + #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include + #include + #include +// #include +// #include + #include +// #include + #include + #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include + #include + #include + #include +// #include + #include +// #include +// #include + #include + #include + #include + #include + #include + #include +// #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include + #include +// #include + #include +// #include +// #include + #include +// #include +// #include +// #include + #include +// #include +// #include + #include +// #include + #include +// #include + #include + #include +// #include +// #include + #include // Start using MacMemory.h + #include +// #include + #include + #include +// #include +// #include +// #include + #include +// #include + #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include + #include + #include + #include +// #include + #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include + #include + #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include + #include + #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include +// #include +// #include + #include + #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + #include +// #include +// #include +// #include +// #include +// #include +// #include + #include + #include +// #include + #include // Start using TextUtils.h +// #include +// #include +// #include + #include + #include +// #include +// #include + #include + #include + #include + #include +// #include +// #include + #include +// #include + #include // Start using MacTypes.h +// #include +// #include + #include +// #include + #include // Start using MacWindows.h +// #include +// #include +/* + * Required for c-style toolbox glue functions: c2pstr and p2cstr + * (matches the inverse operation at the start of the file...) + */ + +#include "maconly.h" +#include +#include +#if __MC68K__ && !__CFM68K__ + #pragma d0_pointers reset +#endif diff --git a/mac/MACINETFILE.CPP b/mac/MACINETFILE.CPP new file mode 100644 index 000000000..8caa5c8b1 --- /dev/null +++ b/mac/MACINETFILE.CPP @@ -0,0 +1,72 @@ +#define SOCKET int +#include +#include +#include +#include "inetgetfile.h" +#include "CFtp.h" +#include "Chttpget.h" +InetGetFile::InetGetFile(char *URL,char *localfile) +{ +} +InetGetFile::~InetGetFile() +{ +} +BOOL InetGetFile::IsFileReceived() +{ + return false; +} +BOOL InetGetFile::IsFileError() +{ + return true; +} +BOOL InetGetFile::IsConnecting() +{ + return true; +} +BOOL InetGetFile::IsReceiving() +{ + return false; +} +int InetGetFile::GetErrorCode() +{ + return 0; +} +int InetGetFile::GetBytesIn() +{ + return 0; +} +int InetGetFile::GetTotalBytes() +{ + return 0; +} +void InetGetFile::AbortGet() +{ +} +//////////////////////////////////////////////////////////////////// +CFtpGet::CFtpGet(char *URL,char *localfile,char *Username,char *Password){} +CFtpGet::~CFtpGet(){} +int CFtpGet::GetStatus(){return 0;} +unsigned int CFtpGet::GetBytesIn(){return 0;} +unsigned int CFtpGet::GetTotalBytes(){return 0;} +//DAJ void CFtpGet::AbortGet(){} +void CFtpGet::WorkerThread(){} +int CFtpGet::ConnectControlSocket(){return 0;} +int CFtpGet::LoginHost(){return 0;} +unsigned int CFtpGet::SendFTPCommand(char *command){return 0;} +unsigned int CFtpGet::ReadFTPServerReply(){return 0;} +unsigned int CFtpGet::GetFile(){return 0;} +unsigned int CFtpGet::IssuePort(){return 0;} +unsigned int CFtpGet::ReadDataChannel(){return 0;} +void CFtpGet::FlushControlChannel(){} +///////////////////////////////////////////////////////////////////// +ChttpGet::ChttpGet(char *URL,char *localfile){} +ChttpGet::~ChttpGet(){} +int ChttpGet::GetStatus(){return 0;} +unsigned int ChttpGet::GetBytesIn(){return 0;} +unsigned int GetTotalBytes(){return 0;} +void AbortGet(){} +void WorkerThread(){} +int ConnectSocket(){return 0;} +char *GetHTTPLine(){return "ERR";} +unsigned int ReadDataChannel(){return 0;} + diff --git a/mac/MACNETWORKING.CPP b/mac/MACNETWORKING.CPP new file mode 100644 index 000000000..7f0089350 --- /dev/null +++ b/mac/MACNETWORKING.CPP @@ -0,0 +1,2424 @@ +/* + * $Logfile: /DescentIII/Main/mac/MACNETWORKING.CPP $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:15 $ + * $Author: kevinb $ + * + * + * + * $Log: MACNETWORKING.CPP,v $ + * Revision 1.1.1.1 2003/08/26 03:58:15 kevinb + * initial 1.5 import + * + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * + */ +#include +#include +#include "descent.h" +#include "shippage.h" +#include "appdatabase.h" +#include "pstypes.h" +#include "pserror.h" +#include "mono.h" +#include "networking.h" +#include "ddio.h" +#include "mem.h" +#include "game.h" +#include "args.h" +#include "pstring.h" +#include "module.h" //for some nice defines to use below +#ifndef WIN32 +bool Use_DirectPlay = false; +#endif +BOOL TCP_active = FALSE; +BOOL IPX_active = FALSE; +BOOL DP_active = FALSE; //Direct Play active +#ifdef FIXED +#define MAX_CONNECT_TRIES 50 +#define MAX_RECEIVE_BUFSIZE (1<<16) // 32 K, eh? +int Dialup_connection = 0; +int nw_ServerSocket=-1; +int nw_ClientSocket=-1; +network_protocol NetworkProtocol=NP_NONE; +int Sockets_initted = 0; +int Network_initted =0; +unsigned long Net_fixed_ip = INADDR_NONE; +// sockets for IPX and TCP +SOCKET IPX_socket; +SOCKET IPX_reliable_socket; +SOCKET IPX_listen_socket; +SOCKET TCP_socket; +SOCKET TCP_reliable_socket; +SOCKET TCP_listen_socket; +// the sockets that the game will use when selecting network type +static SOCKET *Unreliable_socket; +static SOCKET *Reliable_socket; +static SOCKET *Listen_socket; +BOOL TCP_active = FALSE; +BOOL IPX_active = FALSE; +BOOL DP_active = FALSE; //Direct Play active +//BOOL TCP_MT_active = FALSE; +// This structure contains the local computer info +network_address My_addr; +typedef struct network_checksum_packet +{ + int sequence_number; + ushort flags; + ushort checksum; + ubyte data[MAX_PACKET_SIZE]; +} network_checksum_packet; +// definition for a non-checksum packet +typedef struct network_packet +{ + int sequence_number; + ushort flags; + ubyte data[MAX_PACKET_SIZE]; +} network_naked_packet; +// structure definition for our packet buffers +typedef struct network_packet_buffer +{ + int sequence_number; + int len; + network_address from_addr; + ubyte data[MAX_PACKET_SIZE]; +} network_packet_buffer; +#define MAX_PACKET_BUFFERS 96 +static network_packet_buffer Packet_buffers[MAX_PACKET_BUFFERS]; // buffer to hold packets sent to us +static short Packet_free_list[MAX_PACKET_BUFFERS]; // contains id's of free packet buffers +static Num_packet_buffers; +static int Largest_packet_index = 0; +int Uncompressed_outgoing_data_len = 0; +int Compressed_outgoing_data_len = 0; +int Next_packet_id; +int Last_packet_id; +//An array of callbacks +NetworkReceiveCallback Netcallbacks[16]; +#define R_NET_SEQUENCE_NONE 0 +#define R_NET_SEQUENCE_CONNECTING 1 +#define R_NET_SEQUENCE_CONNECTED 2 +#define R_NET_SEQUENCE_FAILED 3 +#define R_NET_PACKET_QUEUE_TIME .1f +int Net_connect_socket_id = INVALID_SOCKET; +int Net_connect_sequence = R_NET_SEQUENCE_NONE; +// ------------------------------------------------------------------------------------------------------ +// PACKET BUFFERING FUNCTIONS +// +// a sequence number of -1 will indicate that this packet is not valid +network_packet_buffer Psnet_buffers[MAX_PACKET_BUFFERS]; +int Psnet_seq_number = 0; +int Psnet_lowest_id = 0; +int Psnet_highest_id = 0; +//Reliable UDP stuff +//******************************* +#ifdef WIN32 +#pragma pack(push,r_udp) +#endif +#pragma pack(1) +typedef struct +{ + ubyte type; //packet type + ubyte compressed; // + ushort seq; //sequence packet 0-65535 used for ACKing also + ushort data_len; //length of data + float send_time; //Time the packet was sent, if an ACK the time the packet being ACK'd was sent. + ubyte data[NETBUFFERSIZE]; //Packet data +}reliable_header; +#define RELIABLE_PACKET_HEADER_ONLY_SIZE (sizeof(reliable_header)-NETBUFFERSIZE) +#define MAX_PING_HISTORY 10 +typedef struct +{ + ubyte buffer[NETBUFFERSIZE]; +}reliable_net_sendbuffer; +typedef struct +{ + ubyte buffer[NETBUFFERSIZE]; +}reliable_net_rcvbuffer; +SOCKET Reliable_UDP_socket = INVALID_SOCKET; +SOCKET Reliable_IPX_socket = INVALID_SOCKET; +float first_sent_iamhere = 0; +float last_sent_iamhere = 0; +unsigned int serverconn = 0xFFFFFFFF; +#ifdef WIN32 +#pragma pack(pop,r_udp) +#elif defined(__LINUX__) +#pragma pack() +#endif +typedef struct +{ + + float timesent[MAXNETBUFFERS]; + int send_len[MAXNETBUFFERS]; + int recv_len[MAXNETBUFFERS]; + float last_packet_received; //For a given connection, this is the last packet we received + float last_packet_sent; + float pings[MAX_PING_HISTORY]; + unsigned int num_ping_samples; + float mean_ping; + float last_sent; //The last time we sent a packet (used for NAGLE emulation) + int waiting_packet_number; //Which packet has data in it that is waiting for the interval to send + ushort status; //Status of this connection + unsigned short oursequence; //This is the next sequence number the application is expecting + unsigned short theirsequence; //This is the next sequence number the peer is expecting + unsigned short rsequence[MAXNETBUFFERS]; //This is the sequence number of the given packet + ubyte ping_pos; + + network_address net_addr; //A D3 network address structure + network_protocol connection_type; //IPX, IP, modem, etc. + reliable_net_rcvbuffer *rbuffers[MAXNETBUFFERS]; + SOCKADDR addr; //SOCKADDR of our peer + reliable_net_sendbuffer *sbuffers[MAXNETBUFFERS]; //This is an array of pointers for quick sorting + unsigned short ssequence[MAXNETBUFFERS]; //This is the sequence number of the given packet + ubyte send_urgent; +}reliable_socket; +reliable_socket reliable_sockets[MAXRELIABLESOCKETS]; +#endif // FIXED +//******************************* +void CloseNetworking() +{ +#ifdef FIXED + ASSERT (Network_initted==1); + ASSERT (Sockets_initted==1); + mprintf ((0,"Shutting down networking...\n")); + if ( IPX_socket != INVALID_SOCKET ) + { + shutdown( IPX_socket, 1 ); + #ifdef WIN32 + closesocket( IPX_socket ); + #else + close(IPX_socket); + #endif + } + if ( TCP_socket != INVALID_SOCKET ) + { + shutdown( TCP_socket, 1 ); + #ifdef WIN32 + closesocket( TCP_socket ); + #else + close( TCP_socket ); + #endif + } + + Network_initted=0; + Sockets_initted=0; + NetworkProtocol=NP_NONE; + +#endif // FIXED +} +// Inits the sockets layer to activity +void nw_InitNetworking () +{ +#ifdef FIXED + static char exewithpath[_MAX_PATH*2]; + static char exefile[_MAX_PATH*2]; + static char ourargs[_MAX_PATH*2]; + static char exedir[_MAX_PATH*2]; + static char exeext[_MAX_PATH]; + static char *fixdir; + static char szconntype[100]; + int parmlen; + int len = 99; + Database->read("NetworkConnection", szconntype, &len); + if(strcmpi(szconntype,"DIALUP")==0) + { + Dialup_connection=1; + } + else + { + Dialup_connection=0; + } + int iparg; + iparg = FindArg("-useip"); + if(!iparg) + { + iparg = FindArg("+ip"); + } + if(iparg) + { + Net_fixed_ip = inet_addr(GameArgs[iparg+1]); + if(Net_fixed_ip==INADDR_NONE) + { + Net_fixed_ip = INADDR_NONE; + } + } +#ifdef WIN32 + if(!dp_DidLobbyLaunchGame()) + { + // Tell direct play about this game + char *p = GetCommandLine(); + mprintf((0,"Command line: %s\n",p)); + parmlen = strlen(p); + for(int a=0;a= cursize; trysize >>= 1 ) + { + ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (LPSTR)&trysize, sizeof(trysize)); + if ( ret == SOCKET_ERROR ) + { + int wserr; + wserr = WSAGetLastError(); + if ( (wserr == WSAENOPROTOOPT) || (wserr == WSAEINVAL) ) + break; + } + else + break; + } + getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (LPSTR)&cursize, &cursizesize); + mprintf((0,"Receive buffer set to %d\n", cursize)); + /* + // set the current size of the send buffer + bufsize = MAX_RECEIVE_BUFSIZE/4; + cursizesize = sizeof(int); + getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (LPSTR)&cursize, &cursizesize); + for ( trysize = bufsize; trysize >= cursize; trysize >>= 1 ) + { + ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (LPSTR)&trysize, sizeof(trysize)); + if ( ret == SOCKET_ERROR ) + { + int wserr; + wserr = WSAGetLastError(); + if ( (wserr == WSAENOPROTOOPT) || (wserr == WSAEINVAL) ) + break; + } + else + break; + } + getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (LPSTR)&cursize, &cursizesize); + mprintf((0, "Send buffer set to %d\n", cursize)); + */ +#endif // FIXED +} +unsigned short nw_ListenPort = 0; +// Inits the sockets that the application will be using +void nw_InitSockets(ushort port) +{ +#ifdef FIXED + + nw_ListenPort = port; + // UDP/TCP socket structure + SOCKADDR_IN sock_addr; + // IPX socket structure + SOCKADDR_IPX ipx_addr; + + // Initialize IPX stuff first + IPX_active = 0; + IPX_socket = INVALID_SOCKET; + IPX_reliable_socket = INVALID_SOCKET; + IPX_listen_socket = INVALID_SOCKET; + IPX_socket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX); + if ( IPX_socket != INVALID_SOCKET ) + { + memset(&ipx_addr, 0, sizeof(SOCKADDR_IPX)); + #ifdef WIN32 + ipx_addr.sa_socket = htons( port ); + ipx_addr.sa_family = AF_IPX; + #else + ipx_addr.sipx_port = htons( port ); + ipx_addr.sipx_family = AF_IPX; + #endif + if (bind(IPX_socket, (SOCKADDR *)&ipx_addr, sizeof(SOCKADDR_IPX)) == SOCKET_ERROR) + { + mprintf((0,"Couldn't bind IPX socket (%d)! Invalidating IPX\n", WSAGetLastError() )); + goto init_tcp; + } + nw_SetSocketOptions( IPX_socket ); + IPX_active = 1; + } + else + { + mprintf(( 0,"Cannot create IPX socket (%d)!\n", WSAGetLastError() )); + } + init_tcp: + // Now do tcp! + TCP_active = 0; + TCP_socket = INVALID_SOCKET; + //TCP_reliable_socket = INVALID_SOCKET; + TCP_listen_socket = INVALID_SOCKET; + // Initialize the UDP socket + TCP_socket = socket( AF_INET, SOCK_DGRAM,IPPROTO_UDP); + + //Initialize all reliable sockets, IPX and TCP + nw_InitReliableSocket(); + + if ( TCP_socket != INVALID_SOCKET ) + { + // bind the socket + memset(&sock_addr,0,sizeof(SOCKADDR_IN)); + sock_addr.sin_family = AF_INET; + + unsigned int my_ip; + + my_ip = nw_GetThisIP(); + memcpy(&sock_addr.sin_addr.s_addr,&my_ip,sizeof(uint)); + sock_addr.sin_port = htons( port ); + if ( bind(TCP_socket, (SOCKADDR*)&sock_addr, sizeof (sock_addr)) == SOCKET_ERROR) + { + mprintf((0,"Couldn't bind TCP socket (%d)! Invalidating TCP\n", WSAGetLastError() )); + goto tcp_done; + } + nw_SetSocketOptions( TCP_socket ); + TCP_active = 1; + } + else + { + mprintf((0, "Cannot create TCP socket (%d)!\n", WSAGetLastError() )); + } + + tcp_done: + int ret; + unsigned int isocktrue = 1; + setsockopt(IPX_socket, SOL_SOCKET, SO_REUSEADDR, (LPSTR)&isocktrue, sizeof(isocktrue) ); + ret = setsockopt(IPX_socket,SOL_SOCKET,SO_BROADCAST,(LPSTR)&isocktrue,sizeof(unsigned int)); + if ( ret == SOCKET_ERROR ) + { + int wserr; + wserr = WSAGetLastError(); + if ( (wserr == WSAENOPROTOOPT) || (wserr == WSAEINVAL) ) + { + mprintf((0,"Unable to make socket broadcastable!")); + Int3();//Get Kevin + } + } + setsockopt(TCP_socket, SOL_SOCKET, SO_REUSEADDR, (LPSTR)&isocktrue, sizeof(isocktrue) ); + ret = setsockopt(TCP_socket,SOL_SOCKET,SO_BROADCAST,(LPSTR)&isocktrue,sizeof(unsigned int)); + if ( ret == SOCKET_ERROR ) + { + int wserr; + wserr = WSAGetLastError(); + if ( (wserr == WSAENOPROTOOPT) || (wserr == WSAEINVAL) ) + { + mprintf((0,"Unable to make socket broadcastable!")); + Int3();//Get Kevin + } + } + Sockets_initted = 1; + if (TCP_active) + mprintf((0,"TCP Initialized\n")); + if (IPX_active) + mprintf((0,"IPX Initialized\n")); + + nw_psnet_buffer_init(); + nw_RegisterCallback((NetworkReceiveCallback)nw_HandleUnreliableData,NWT_UNRELIABLE); +#endif // FIXED +} +// Copies my address into the passed argument +void nw_GetMyAddress (network_address *addr) +{ +#ifdef FIXED + int len; + SOCKADDR_IN in_addr; + SOCKADDR_IPX ipx_addr; + memset(&My_addr,0, sizeof(network_address)); + if (TCP_active) + { + // assign the TCP_* sockets to the socket values used elsewhere + Unreliable_socket = &TCP_socket; + Reliable_socket = &TCP_reliable_socket; + Listen_socket = &TCP_listen_socket; + // get the socket name for the TCP_socket, and put it into My_addr + len = sizeof(SOCKADDR_IN); + if ( getsockname(*Unreliable_socket, (SOCKADDR *)&in_addr, &len) == SOCKET_ERROR ) + { + mprintf((0, "Unable to get sock name for TCP unreliable socket (%s)\n", WSAGetLastError() )); + return; + } + memcpy(My_addr.address, &in_addr.sin_addr, 4); + // My_addr.port = in_addr.sin_port; + My_addr.port = ntohs(in_addr.sin_port); + } + else if(IPX_active) + { + // assign the IPX_* sockets to the socket values used elsewhere + Unreliable_socket = &IPX_socket; + Reliable_socket = &IPX_reliable_socket; + Listen_socket = &IPX_listen_socket; + // get the socket name for the IPX_socket, and put it into My_addr + len = sizeof(SOCKADDR_IPX); + if ( getsockname(IPX_socket, (SOCKADDR *)&ipx_addr, &len) == SOCKET_ERROR ) + { + mprintf((0, "Unable to get sock name for IPX unreliable socket (%d)\n", WSAGetLastError() )); + return; + } + #ifdef WIN32 + memcpy(My_addr.net_id, ipx_addr.sa_netnum, 4); + memcpy(My_addr.address, ipx_addr.sa_nodenum, 6); + #else + memcpy(My_addr.net_id, &ipx_addr.sipx_network, 4); + memcpy(My_addr.address, ipx_addr.sipx_node, 6); + #endif + My_addr.port = DEFAULT_GAME_PORT; + } + *addr=My_addr; +#endif // FIXED +} +// Returns internet address format from string address format...ie "204.243.217.14" +// turns into 1414829242 +unsigned long nw_GetHostAddressFromNumbers (char *str) +{ +#ifdef FIXED + //ASSERT (NetworkProtocol==NP_TCP); + return inet_addr (str); +#endif // FIXED +} +// Fills in the string with the string address from the internet address +void nw_GetNumbersFromHostAddress(network_address * address,char *str) +{ +#ifdef FIXED + //ASSERT(NetworkProtocol==NP_TCP); + ASSERT( str ); + struct in_addr addr; + if(address->connection_type==NP_TCP) + { + memcpy(&addr,address->address,sizeof(struct in_addr)); + sprintf(str,"IP: %s:%d",inet_ntoa(addr),address->port); + } + else if(address->connection_type==NP_IPX) + { + char sznet[10] = ""; + char sznode[20] = ""; + char sztmp[4]; + int i; + for(i=0;i<4;i++) + { + sprintf(sztmp,"%.2X",address->net_id[i]); + strcat(sznet,sztmp); + } + for(i=0;i<6;i++) + { + sprintf(sztmp,"%.2X",address->address[i]); + strcat(sznode,sztmp); + } + sprintf(str,"IPX: %s:%s:%d",sznet,sznode,address->port); + } + #ifdef WIN32 + else if(Use_DirectPlay && (address->connection_type==NP_DIRECTPLAY)) + { + DPID id; + memcpy(&id,address->address,sizeof(DPID)); + sprintf(str,"DirectPlay: 0x%x",id); + } + #endif +#endif // FIXED +} +#define CLOSE_TIMEOUT_TIME 3 // 3 seconds +// returns the ip address of this computer +unsigned int nw_GetThisIP() +{ +#ifdef FIXED + SOCKADDR_IN local_address; + int address_size = sizeof(SOCKADDR); + + if(Net_fixed_ip != INADDR_NONE) + { + return Net_fixed_ip; + } + // Init local address to zero + local_address.sin_addr.s_addr = INADDR_ANY; + if(Dialup_connection) + { + #ifdef WIN32 + local_address.sin_addr.s_addr = psnet_ras_status(); + #else + local_address.sin_addr.s_addr = INADDR_ANY; + #endif + } + if((!Dialup_connection)||(!local_address.sin_addr.s_addr)) + { + //Removed the fancy way, it worked worse than the easy way (some adsl/cable people had problems. + /* + // Get the local host name + ret = gethostname(local, 255 ); + if (ret != SOCKET_ERROR ) + { + // Resolve host name for local address + hostent = gethostbyname((LPSTR)local); + if ( hostent ) + local_address.sin_addr.s_addr = *((u_long FAR *)(hostent->h_addr)); + } + */ + local_address.sin_addr.s_addr = INADDR_ANY; + } + return local_address.sin_addr.s_addr; + +#endif // FIXED +} +// Calculates a unique ushort checksum for a stream of data +ushort nw_CalculateChecksum( void * vptr, int len ) +{ + ubyte * ptr = (ubyte *)vptr; + unsigned int sum1,sum2; + sum1 = sum2 = 0; + while(len--) { + sum1 += *ptr++; + if (sum1 >= 255 ) sum1 -= 255; + sum2 += sum1; + } + sum2 %= 255; + + return (unsigned short)((sum1<<8)+ sum2); +} +// Sends data on an unreliable socket +int nw_Send( network_address * who_to, void * data, int len, int flags ) +{ +#ifdef FIXED + if(len==0) + { + mprintf((0,"Attempting to send 0 byte network packet in nw_Send()\n")); + Int3(); + } + return nw_SendWithID(NWT_UNRELIABLE,(ubyte *)data,len,who_to); +#endif // FIXED +} +void nw_HandleUnreliableData(ubyte *data,int len,network_address *from_addr) +{ +#ifdef FIXED + nw_psnet_buffer_packet((ubyte *)data,len,from_addr); +#endif // FIXED +} +// routine to "free" a packet buffer +void nw_FreePacket( int id ) +{ +#ifdef FIXED + Packet_buffers[id].sequence_number = -1; + Packet_free_list[ --Num_packet_buffers ] = (short)id; + if ( Largest_packet_index == id) + while ((--Largest_packet_index>0) && (Packet_buffers[Largest_packet_index].sequence_number == -1 )); +#endif // FIXED +} +// nw_Recieve will call the above function to read data out of the socket. It will then determine +// which of the buffers we should use and pass to the routine which called us +int nw_Receive( void * data, network_address *from_addr ) +{ +#ifdef FIXED + // call the routine to read data out of the socket (which stuffs it into the packet buffers) + if(Use_DirectPlay) + { +#ifdef WIN32 + dp_DirectPlayDispatch(); +#endif + } + else + { + // nw_ReceiveFromSocket(); + nw_DoReceiveCallbacks(); + } + + int buffer_size; + // try and get a free buffer and return its size + if(nw_psnet_buffer_get_next((ubyte*)data,&buffer_size,from_addr)) + { + return buffer_size; + } + return 0; +#endif // FIXED +} +int ExtraBufferTempHack=0; +//Temp hack to figure out this buffer overflow thing +//Return codes: +//-1 socket not connected +// 0 No packet ready to receive +// >0 Buffer filled with the number of bytes recieved +int nw_ReceiveReliable(unsigned int socketid, ubyte *buffer, int max_len) +{ +#ifdef FIXED + + int i; + if(Use_DirectPlay) + { + #ifdef WIN32 + dp_DirectPlayDispatch(); + + + // try and get a free buffer and return its size + if(nw_psnet_buffer_get_next_by_dpid((ubyte*)buffer,&max_len,socketid)) + { + return max_len; + } + return 0; + #endif + } + reliable_socket *rsocket = NULL; + //nw_WorkReliable(); + nw_DoReceiveCallbacks(); + if(socketid>=MAXRELIABLESOCKETS) + { + mprintf((0,"Invalid socket id passed to nw_NewReceiveReliable() -- %d\n",socketid)); + return -1; + } + rsocket=&reliable_sockets[socketid]; + if( (RNF_CONNECTED!=rsocket->status) && (RNF_LIMBO!=rsocket->status) ) + { + mprintf((0,"Can't receive packet because it isn't connected in nw_ReceiveReliable(). socket = %d\n",socketid)); + return 0; + } + //If the buffer position is the position we are waiting for, fill in + //the buffer we received in the call to this function and return true + for(i=0;irsequence[i]==rsocket->oursequence)&&(rsocket->rbuffers[i])) + { + memcpy(buffer,rsocket->rbuffers[i]->buffer,rsocket->recv_len[i]); + mem_free(rsocket->rbuffers[i]); + rsocket->rbuffers[i] = NULL; + rsocket->rsequence[i] = 0; + //mprintf((0,"Found packet for upper layer in nw_ReceiveReliable() %d bytes. seq:%d.\n",rsocket->recv_len[i],rsocket->oursequence)); + rsocket->oursequence++; + return rsocket->recv_len[i]; + } + } +#endif // FIXED + return 0; +} +//This function will look for a control packet, indicating a +//desire to establish a reliable link. +//When is makes a link, it will return a SOCKET, and fill in from_addr. +// if there is no waiting line, it returns -1 +int nw_CheckListenSocket(network_address *from_addr) +{ +#ifdef FIXED + SOCKADDR_IN *ip_addr; // UDP/TCP socket structure + SOCKADDR_IPX *ipx_addr; // IPX socket structure +#ifdef WIN32 + DPID id; +#endif + if(Use_DirectPlay) + { + #ifdef WIN32 + // look for a pending connection + for(int i=0;iaddress,&Pending_dp_conn[i],sizeof(DPID)); + from_addr->connection_type = NP_DIRECTPLAY; + id = Pending_dp_conn[i]; + Pending_dp_conn[i] = DPID_UNKNOWN; + mprintf((0,"New DirectPlay connection in nw_CheckListenSocket().\n")); + return id; + } + } + return -1; + #endif + } + + //nw_WorkReliable(); + nw_DoReceiveCallbacks(); + int i; + for(i=1;iport = ntohs( ipx_addr->sa_socket ); + from_addr->connection_type = NP_IPX; + memcpy(from_addr->address, ipx_addr->sa_nodenum, 6 ); + memcpy(from_addr->net_id, ipx_addr->sa_netnum, 4 ); + #else + from_addr->port = ntohs( ipx_addr->sipx_port ); + from_addr->connection_type = NP_IPX; + memcpy(from_addr->address, ipx_addr->sipx_node, 6); + memcpy(from_addr->net_id, &ipx_addr->sipx_network, 4); + #endif + break; + case NP_TCP: + ip_addr = (SOCKADDR_IN *)&reliable_sockets[i].addr; + memset(from_addr, 0x00, sizeof(network_address)); + from_addr->port = ntohs( ip_addr->sin_port ); + from_addr->connection_type = NP_TCP; + #ifdef WIN32 + memcpy(from_addr->address, &ip_addr->sin_addr.S_un.S_addr, 4); + #else + memcpy(from_addr->address, &ip_addr->sin_addr.s_addr, 4); + #endif + break; + + default: + Int3(); + break; + } + char dbg_output[50]; + nw_GetNumbersFromHostAddress(from_addr,dbg_output); + mprintf((0,"Got address from: %s\n",dbg_output)); + return i; + } + } +#endif // FIXED + return INVALID_SOCKET; +} +int nw_SendReliable(unsigned int socketid, ubyte *data, int length,bool urgent ) +{ +#ifdef FIXED + int i; + int bytesout; + int use_buffer = -1; + reliable_socket *rsocket; + reliable_header send_header; + int send_this_packet=1; + + if(length==0) + { + mprintf((0,"Attempting to send 0 byte network packet in nw_SendReliable()\n")); + Int3(); + } + //mprintf((0,"Socket id passed to nw_NewSendReliable() -- %d\n",socketid)); + if(Use_DirectPlay) + { + #ifdef WIN32 + network_address who_to; + who_to.connection_type = NP_DIRECTPLAY; + memcpy(&who_to.address,&socketid,sizeof(DPID)); + return dp_DirectPlaySend(&who_to,data,length,true); + #endif + } + ASSERT(length=MAXRELIABLESOCKETS) + { + mprintf((0,"Invalid socket id passed to nw_NewSendReliable() -- %d\n",socketid)); + return -1; + } + rsocket=&reliable_sockets[socketid]; + if(rsocket->status!=RNF_CONNECTED) + { + //We can't send because this isn't a connected reliable socket. + mprintf((0,"Can't send packet because of status %d in nw_SendReliable(). socket = %d\n",rsocket->status,socketid)); + return -1; + } + if(urgent) + rsocket->send_urgent = 1; + //See if there is a packet waiting to be sent + if(-1 != rsocket->waiting_packet_number) + { + int pnum = rsocket->waiting_packet_number; + ASSERT(rsocket->sbuffers[pnum]); + //See if there's room for this data + if(sizeof(reliable_net_sendbuffer) < (rsocket->send_len[pnum]+length)) + { + //Send the previous packet, then use the normal code to generate a new packet + mprintf((0,"Pending reliable packet buffer full, sending packet now.\n")); + rsocket->waiting_packet_number = -1; + use_buffer = pnum; + network_address send_address; + memset(&send_address,0,sizeof(network_address)); + + memcpy(send_header.data,rsocket->sbuffers[pnum],rsocket->send_len[pnum]); + send_header.data_len = rsocket->send_len[pnum]; + send_header.type = RNT_DATA; + send_header.send_time = timer_GetTime(); + send_address.connection_type = rsocket->connection_type; + if(NP_IPX==rsocket->connection_type) + { + SOCKADDR_IPX *ipxaddr = (SOCKADDR_IPX *)&rsocket->addr; + #ifdef WIN32 + memcpy(send_address.address,ipxaddr->sa_nodenum, 6); + memcpy(send_address.net_id,ipxaddr->sa_netnum, 4); + send_address.port = htons(ipxaddr->sa_socket); + #else + memcpy(send_address.address,ipxaddr->sipx_node, 6); + memcpy(send_address.net_id,&ipxaddr->sipx_network, 4); + send_address.port = htons(ipxaddr->sipx_port); + #endif + send_address.connection_type = NP_IPX; + } + else if(NP_TCP==rsocket->connection_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rsocket->addr; + memcpy(send_address.address,&inaddr->sin_addr, 4); + send_address.port = htons(inaddr->sin_port); + send_address.connection_type = NP_TCP; + } + bytesout = nw_SendWithID(NWT_RELIABLE,(ubyte *)&send_header,RELIABLE_PACKET_HEADER_ONLY_SIZE+rsocket->send_len[use_buffer],&send_address); + + if((bytesout==SOCKET_ERROR)&&(WSAEWOULDBLOCK==WSAGetLastError())) + { + //This will cause it to try to send again next frame. (or sooner) + rsocket->timesent[use_buffer] = timer_GetTime()-(NETRETRYTIME*4); + } + else + { + rsocket->timesent[use_buffer] = timer_GetTime(); + } + } + else + { + //tack this data on the end of the previous packet + //mprintf((0,"Appending to delayed packet...\n")); + ASSERT(rsocket->sbuffers[pnum]); + memcpy(rsocket->sbuffers[pnum]->buffer+rsocket->send_len[pnum],data,length); + int msize = mem_size(rsocket->sbuffers[pnum]); + rsocket->send_len[pnum] += length; + return length; + } + } + + //Add the new packet to the sending list and send it. + for(i=0;isbuffers[i]) + { + //mprintf((0,"Sending in nw_SendReliable() %d bytes seq=%d.\n",length,rsocket->theirsequence)); + + rsocket->send_len[i] = length; + rsocket->sbuffers[i] = (reliable_net_sendbuffer *)mem_malloc(sizeof(reliable_net_sendbuffer)); + + memcpy(rsocket->sbuffers[i]->buffer,data,length); + send_header.seq = rsocket->theirsequence; + rsocket->ssequence[i] = rsocket->theirsequence; + + use_buffer = i; + rsocket->waiting_packet_number = i; + + rsocket->theirsequence++; + return length; + } + } + mprintf((0,"Can't send packet because a buffer overflow nw_SendReliable(). socket = %d\n",socketid)); + rsocket->status = RNF_BROKEN; + + for(i=0;isbuffers[i]) + { + mprintf((0,"Buffer %d: %d,%d,%d,%d,%d,%d\n",i,rsocket->sbuffers[i]->buffer[0],rsocket->sbuffers[i]->buffer[1], + rsocket->sbuffers[i]->buffer[2],rsocket->sbuffers[i]->buffer[3], + rsocket->sbuffers[i]->buffer[4],rsocket->sbuffers[i]->buffer[5])); + } + } + + //Error ("Couldn't send packet because of buffer overflow!"); + //Int3(); +#endif // FIXED + return 0; +} +int nw_InitReliableSocket() +{ +#ifdef FIXED + nw_RegisterCallback((NetworkReceiveCallback)nw_WorkReliable,NWT_RELIABLE); +#endif // FIXED + return 1; +} +void nw_SendReliableAck(SOCKADDR *raddr,unsigned int sig, network_protocol link_type,float time_sent) +{ +#ifdef FIXED + int ret; + reliable_header ack_header; + ack_header.type = RNT_ACK; + //mprintf((0,"Sending ACK for sig %d.\n",sig)); + ack_header.data_len = sizeof(unsigned int); + ack_header.send_time = time_sent; + memcpy(&ack_header.data,&sig,sizeof(unsigned int)); + + network_address send_address; + memset(&send_address,0,sizeof(network_address)); + + send_address.connection_type = reliable_sockets[serverconn].connection_type; + if(NP_IPX==link_type) + { + SOCKADDR_IPX *ipxaddr = (SOCKADDR_IPX *)raddr; + #ifdef WIN32 + memcpy(send_address.address,ipxaddr->sa_nodenum, 6); + memcpy(send_address.net_id,ipxaddr->sa_netnum, 4); + send_address.port = htons(ipxaddr->sa_socket); + #else + memcpy(send_address.address,ipxaddr->sipx_node, 6); + memcpy(send_address.net_id,&ipxaddr->sipx_network, 4); + send_address.port = htons(ipxaddr->sipx_port); + #endif + send_address.connection_type = NP_IPX; + } + else if(NP_TCP==link_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)raddr; + memcpy(send_address.address,&inaddr->sin_addr, 4); + send_address.port = htons(inaddr->sin_port); + send_address.connection_type = NP_TCP; + } + ret = nw_SendWithID(NWT_RELIABLE,(ubyte *)&ack_header,RELIABLE_PACKET_HEADER_ONLY_SIZE+sizeof(unsigned int),&send_address); +#endif // FIXED +} +void nw_DoNetworkIdle(void) +{ +#ifdef FIXED + if(!Use_DirectPlay) + { + nw_DoReceiveCallbacks(); + nw_ReliableResend(); + } +#endif // FIXED +} +#define CONNECTSEQ 0x142//Magic number for starting a connection, just so it isn't 0 +void nw_WorkReliable(ubyte * data,int len,network_address *naddr) +{ +#ifdef FIXED + int i; + int rcode = -1; + int max_len = NETBUFFERSIZE; + static reliable_header rcv_buff; + static SOCKADDR rcv_addr; + int bytesin = 0; + int addrlen = sizeof(SOCKADDR); + unsigned int rcvid;//The id of who we actually received a packet from, as opposed to socketid parm + if(NP_IPX==naddr->connection_type) + { + SOCKADDR_IPX *ipxaddr = (SOCKADDR_IPX *)&rcv_addr; + #ifdef WIN32 + memcpy(&ipxaddr->sa_nodenum,&naddr->address, 6); + memcpy(&ipxaddr->sa_netnum,&naddr->net_id, 4); + ipxaddr->sa_socket = htons(naddr->port); + #else + memcpy(ipxaddr->sipx_node,&naddr->address, 6); + memcpy(&ipxaddr->sipx_network,&naddr->net_id, 4); + ipxaddr->sipx_port = htons(naddr->port); + #endif + } + else if(NP_TCP==naddr->connection_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rcv_addr; + memcpy(&inaddr->sin_addr,&naddr->address, 4); + inaddr->sin_port = htons(naddr->port); + } + + //memcpy(&rcv_addr,&naddr->address,sizeof(SOCKADDR)); + if(Net_connect_sequence == R_NET_SEQUENCE_CONNECTING) + { + nw_HandleConnectResponse(data,len,naddr); + return; + } + //Check for incoming data + + reliable_socket *rsocket = NULL; + //Check to see if we need to send a packet out. + if((reliable_sockets[serverconn].status==RNF_LIMBO) && ((serverconn!=-1)&&(timer_GetTime() - last_sent_iamhere)>NETRETRYTIME) ) + { + reliable_header conn_header; + //Now send I_AM_HERE packet + conn_header.type = RNT_I_AM_HERE; + conn_header.seq = ~CONNECTSEQ; + conn_header.data_len = 0; + last_sent_iamhere = timer_GetTime(); + network_address send_address; + memset(&send_address,0,sizeof(network_address)); + + send_address.connection_type = reliable_sockets[serverconn].connection_type; + if(NP_IPX==send_address.connection_type) + { + SOCKADDR_IPX *ipxaddr = (SOCKADDR_IPX *)&reliable_sockets[serverconn].addr; + #ifdef WIN32 + memcpy(send_address.address,ipxaddr->sa_nodenum, 6); + memcpy(send_address.net_id,ipxaddr->sa_netnum, 4); + send_address.port = htons(ipxaddr->sa_socket); + #else + memcpy(send_address.address,ipxaddr->sipx_node, 6); + memcpy(send_address.net_id,&ipxaddr->sipx_network, 4); + send_address.port = htons(ipxaddr->sipx_port); + #endif + send_address.connection_type = NP_IPX; + } + else if(NP_TCP==send_address.connection_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&reliable_sockets[serverconn].addr; + memcpy(send_address.address,&inaddr->sin_addr, 4); + send_address.port = htons(inaddr->sin_port); + send_address.connection_type = NP_TCP; + } + int ret = nw_SendWithID(NWT_RELIABLE,(ubyte *)&conn_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,&send_address); + + if((ret==SOCKET_ERROR)&&(WSAEWOULDBLOCK==WSAGetLastError())) + { + reliable_sockets[serverconn].last_packet_sent = timer_GetTime()-NETRETRYTIME; + } + else + { + reliable_sockets[serverconn].last_packet_sent = timer_GetTime(); + } + } + network_protocol link_type = naddr->connection_type; + network_address d3_rcv_addr; + memcpy(&d3_rcv_addr,naddr,sizeof(network_address)); + memcpy((ubyte *)&rcv_buff,data,len); + SOCKADDR_IN *rcvaddr,*rsockaddr; + do + { + rsocket = NULL; + if(len) + { + //Someone wants to connect, so find a slot + if(rcv_buff.type == RNT_REQ_CONN) + { + for(i=1;isin_addr),htons(rcvaddr->sin_port))); + break; + } + } + if(i==MAXRELIABLESOCKETS) + { + //No more connections! + mprintf((0,"Out of incoming reliable connection sockets\n")); + //Int3();//See Kevin + continue; + } + nw_SendReliableAck(&rsocket->addr,rcv_buff.seq,link_type,rcv_buff.send_time); + } + + //Find out if this is a packet from someone we were expecting a packet. + rcvaddr = (SOCKADDR_IN *)&rcv_addr; + for(i=1;ilast_packet_received = timer_GetTime(); + + if(rsocket->status!=RNF_CONNECTED) + { + //Get out of limbo + if(rsocket->status==RNF_LIMBO) + { + //this is our connection to the server + if((serverconn!=-1)) + { + if(rcv_buff.type == RNT_ACK) + { + int *acknum = (int *)&rcv_buff.data; + if(*acknum == (~CONNECTSEQ & 0xffff)) + { + rsocket->status = RNF_CONNECTED; + mprintf((0,"Got ACK for IAMHERE!\n")); + } + continue; + } + } + else if(rcv_buff.type == RNT_I_AM_HERE) + { + rsocket->status = RNF_CONNECTING; + nw_SendReliableAck(&rsocket->addr,rcv_buff.seq,link_type,rcv_buff.send_time); + mprintf((0,"Got IAMHERE!\n")); + continue; + } + } + if((rcv_buff.type==RNT_DATA)&&(serverconn!=-1)) + { + rsocket->status = RNF_CONNECTED; + } + else + { + //mprintf((0,"Packet from nonconnected socket -- seq: %d status: %d\n",rcv_buff.seq,rsocket->status)); + rsocket->last_packet_received = timer_GetTime(); + continue; + } + + } + //Update the last recv variable so we don't need a heartbeat + rsocket->last_packet_received = timer_GetTime(); + if(rcv_buff.type == RNT_HEARTBEAT) + { + continue; + } + if(rcv_buff.type == RNT_ACK) + { + //Update ping time + rsocket->num_ping_samples++; + + rsocket->pings[rsocket->ping_pos] = rsocket->last_packet_received - rcv_buff.send_time; + //mprintf((0,"ping time: %f\n",rsocket->pings[rsocket->ping_pos])); + if(rsocket->num_ping_samples>=MAX_PING_HISTORY) + { + float sort_ping[MAX_PING_HISTORY]; + for(int a=0;apings[a]; + qsort(sort_ping,MAX_PING_HISTORY,sizeof(float),nw_PingCompare); + rsocket->mean_ping = ((sort_ping[MAX_PING_HISTORY/2]+sort_ping[(MAX_PING_HISTORY/2)+1]))/2; + //mprintf_at((2,i+1,0,"Ping: %f ",rsocket->mean_ping)); + } + rsocket->ping_pos++; + if(rsocket->ping_pos>=MAX_PING_HISTORY) + { + rsocket->ping_pos=0; + } + for(i=0;isbuffers[i]) + if(rsocket->ssequence[i]==*acksig) + { + //mprintf((0,"Received ACK %d\n",*acksig)); + mem_free(rsocket->sbuffers[i]); + rsocket->sbuffers[i] = NULL; + rsocket->ssequence[i] = 0; + } + } + //remove that packet from the send buffer + rsocket->last_packet_received = timer_GetTime(); + continue; + } + if(rcv_buff.type == RNT_DATA_COMP) + { + //More2Come + //Decompress it. Put it back in the buffer. Process it as RNT_DATA + rcv_buff.type = RNT_DATA; + } + if(rcv_buff.type == RNT_DATA) + { + + //If the data is out of order by >= MAXNETBUFFERS-1 ignore that packet for now + int seqdelta; + seqdelta = rcv_buff.seq - rsocket->oursequence; + if(seqdelta<0) seqdelta = seqdelta*-1; + if(seqdelta>=MAXNETBUFFERS-1) + { + mprintf((0,"Received reliable packet out of order!\n")); + //It's out of order, so we won't ack it, which will mean we will get it again soon. + continue; + } + //else move data into the proper buffer position + int savepacket=1; + + if(rsocket->oursequence < (0xffff - (MAXNETBUFFERS-1))) + { + if (rsocket->oursequence > rcv_buff.seq) + { + savepacket = 0; + } + } + else + { + //Sequence is high, so prepare for wrap around + if( ((unsigned short)(rcv_buff.seq + rsocket->oursequence)) > (MAXNETBUFFERS-1)) + { + savepacket = 0; + } + } + for(i=0;irbuffers[i]) && (rsocket->rsequence[i] == rcv_buff.seq)) + { + //Received duplicate packet! + //mprintf((0,"Received duplicate packet!\n")); + savepacket = 0; + } + } + if(savepacket) + { + for(i=0;irbuffers[i]) + { + //mprintf((0,"Got good data seq: %d\n",rcv_buff.seq)); + if(rcv_buff.data_len>max_len) rsocket->recv_len[i] = rcv_buff.data_len; + else rsocket->recv_len[i] = rcv_buff.data_len; + rsocket->rbuffers[i] = (reliable_net_rcvbuffer *)mem_malloc(sizeof(reliable_net_rcvbuffer)); + memcpy(rsocket->rbuffers[i]->buffer,rcv_buff.data,rsocket->recv_len[i]); + rsocket->rsequence[i] = rcv_buff.seq; + //mprintf((0,"Adding packet to receive buffer in nw_ReceiveReliable().\n")); + break; + } + } + } + nw_SendReliableAck(&rsocket->addr,rcv_buff.seq,link_type,rcv_buff.send_time); + } + + } + }while(0);//while((IPX_has_data>0) || (UDP_has_data>0)); + +#endif // FIXED +} +void nw_HandleConnectResponse(ubyte *data,int len,network_address *server_addr) +{ +#ifdef FIXED + int i; + static reliable_header ack_header; + static reliable_header conn_header; + SOCKADDR rcv_addr; + + memcpy(&ack_header,data,len); + if(NP_IPX==server_addr->connection_type) + { + SOCKADDR_IPX *ipxaddr = (SOCKADDR_IPX *)&rcv_addr; + #ifdef WIN32 + memcpy(&ipxaddr->sa_nodenum,&server_addr->address, 6); + memcpy(&ipxaddr->sa_netnum,&server_addr->net_id, 4); + ipxaddr->sa_socket = htons(server_addr->port); + #else + memcpy(&ipxaddr->sipx_node,&server_addr->address, 6); + memcpy(&ipxaddr->sipx_network,&server_addr->net_id, 4); + ipxaddr->sipx_port = htons(server_addr->port); + #endif + } + else if(NP_TCP==server_addr->connection_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rcv_addr; + memcpy(&inaddr->sin_addr,&server_addr->address, 4); + inaddr->sin_port = htons(server_addr->port); + } + mprintf((0,"Got a connect response!\n")); + if(ack_header.type == RNT_ACK) + { + int *acknum = (int *)&ack_header.data; + if(*acknum == CONNECTSEQ) + { + //if(memcmp(&rcv_addr,&sockaddr,sizeof(SOCKADDR))==0) + { + for(i=1;iconnection_type; + memcpy(&reliable_sockets[i].net_addr,server_addr,sizeof(network_address)); + reliable_sockets[i].last_packet_received = timer_GetTime(); + memcpy(&reliable_sockets[i].addr,&rcv_addr,sizeof(SOCKADDR)); + reliable_sockets[i].status = RNF_LIMBO; + Net_connect_socket_id = i; + reliable_sockets[i].last_sent = timer_GetTime(); + reliable_sockets[i].waiting_packet_number = -1; + mprintf((0,"Succesfully connected to server in nw_ConnectToServer().\n")); + //Now send I_AM_HERE packet + conn_header.type = RNT_I_AM_HERE; + conn_header.seq = ~CONNECTSEQ; + conn_header.data_len = 0; + serverconn = i; + first_sent_iamhere = timer_GetTime(); + last_sent_iamhere = timer_GetTime(); + + + int rcode = nw_SendWithID(NWT_RELIABLE,(ubyte *)&conn_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,server_addr); + //int rcode = sendto(typeless_sock,(char *)&conn_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,0,addr,sizeof(SOCKADDR)); + if(rcode == SOCKET_ERROR) + { + Net_connect_socket_id = INVALID_SOCKET; + reliable_sockets[i].status = RNF_UNUSED; + memset(&reliable_sockets[i],0,sizeof(reliable_socket)); + mprintf((0,"Unable to send packet in nw_ConnectToServer()\n")); + Net_connect_sequence = R_NET_SEQUENCE_FAILED; + return; + } + reliable_sockets[i].last_packet_sent = timer_GetTime(); + /* + float f; + f = timer_GetTime(); + while(((timer_GetTime() - f)<2) && (reliable_sockets[i].status!=RNF_CONNECTING)) + { + //nw_WorkReliable(); + nw_DoReceiveCallbacks(); + } + */ + Net_connect_sequence = R_NET_SEQUENCE_CONNECTED; + return; + } + } + mprintf((0,"Out of reliable socket space in nw_ConnectToServer().\n")); + Net_connect_sequence = R_NET_SEQUENCE_FAILED; + return; + } + //else + //{ + // mprintf((0,"Received a reliable packet from a server other than the current server\n")); + //} + } + else + { + mprintf((0,"Received out of sequence ACK in nw_ConnectToServer().\n")); + } + } + else + { + mprintf((0,"Received something that isn't an ACK in nw_ConnectToServer().\n")); + } +#endif // FIXED +} +void nw_ConnectToServer(uint *socket, network_address *server_addr) +{ +#ifdef FIXED + //Send out a RNT_REQ_CONN packet, and wait for it to be acked. + float time_sent_req = 0; + float first_sent_req = 0; + static reliable_header conn_header; + static reliable_header ack_header; + int bytesin; + struct timeval timeout; + *socket = INVALID_SOCKET; + if(Use_DirectPlay) + { + //We need a session description to do this, so we don't use this function + return; + } + + conn_header.type = RNT_REQ_CONN; + conn_header.seq = CONNECTSEQ; + conn_header.data_len = 0; + + timeout.tv_sec=0; + timeout.tv_usec=0; + if((server_addr->connection_type==NP_IPX) && (!IPX_active)) + { + return; + } + if((server_addr->connection_type==NP_TCP) && (!TCP_active)) + { + return; + } + + Net_connect_sequence = R_NET_SEQUENCE_CONNECTING; + memset(&ack_header,0,sizeof(reliable_header)); + bytesin = 0; + network_address d3_rcv_addr; + memset(&d3_rcv_addr,0,sizeof(network_address)); + int ret = nw_SendWithID(NWT_RELIABLE,(ubyte *)&conn_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,server_addr); + if(SOCKET_ERROR==ret) + { + mprintf((0,"Unable to send IPX packet in nw_ConnectToServer()! -- %d\n",WSAGetLastError())); + return; + } + + first_sent_req = timer_GetTime(); + time_sent_req = timer_GetTime(); + + //Wait until we get a response from the server or we timeout + + do + { + nw_DoReceiveCallbacks(); + //Now we wait for the connection to be made.... + if(Net_connect_sequence == R_NET_SEQUENCE_CONNECTED) + { + *socket = Net_connect_socket_id; + return; + } + if((timer_GetTime()-time_sent_req)>2) + { + mprintf((0,"Resending connect request.\n")); + int ret = nw_SendWithID(NWT_RELIABLE,(ubyte *)&conn_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,server_addr); + if(ret!=SOCKET_ERROR) + { + time_sent_req = timer_GetTime(); + } + else + { + mprintf(( 0,"Error sending connection request! -- %d\n",WSAGetLastError() )); + } + } + }while((timer_GetTime()-first_sent_req)=MAXRELIABLESOCKETS) + { + mprintf((0,"Invalid socket id passed to nw_NewCloseSocket() -- %d\n",*sockp)); + return; + } + if(reliable_sockets[*sockp].status == RNF_UNUSED) + { + mprintf((0,"Trying to close an unused socket (%d) -- ignoring request.\n",*sockp)); + } + mprintf((0,"Closing socket %d\n",*sockp)); + //Go through every buffer and "free it up(tm)" + int i; + for(i=0;isa_nodenum, 6); + memcpy(send_address.net_id,ipxaddr->sa_netnum, 4); + send_address.port = htons(ipxaddr->sa_socket); + #else + memcpy(send_address.address,ipxaddr->sipx_node, 6); + memcpy(send_address.net_id,&ipxaddr->sipx_network, 4); + send_address.port = htons(ipxaddr->sipx_port); + #endif + send_address.connection_type = NP_IPX; + } + else if(NP_TCP==reliable_sockets[*sockp].connection_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&reliable_sockets[*sockp].addr; + memcpy(send_address.address,&inaddr->sin_addr, 4); + send_address.port = htons(inaddr->sin_port); + send_address.connection_type = NP_TCP; + } + nw_SendWithID(NWT_RELIABLE,(ubyte *)&diss_conn_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,&send_address); + + memset(&reliable_sockets[*sockp],0,sizeof(reliable_socket)); + reliable_sockets[*sockp].status = RNF_UNUSED; + +#endif // FIXED +} +int nw_CheckReliableSocket(int socknum) +{ +#ifdef FIXED + //Checks to see if a socket is connected or not. + if(Use_DirectPlay) + { + return true; + } + if(socknum>=MAXRELIABLESOCKETS) + { + mprintf((0,"Invalid socket id passed to nw_CheckReliableSocket() -- %d\n",socknum)); + return 0; + } + switch(reliable_sockets[socknum].status) + { + case RNF_UNUSED: + case RNF_BROKEN: + case RNF_DISCONNECTED: + return 0; + default: + return 1; + } +#endif // FIXED + return 1; +} +int nw_PingCompare( const void *arg1, const void *arg2 ) +{ +#ifdef FIXED + float *ping1 = (float *)arg1; + float *ping2 = (float *)arg2; + + if(*ping1==*ping2) return 0; + else if(*ping1>*ping2) return 1; + else if(*ping1<*ping2) return -1; +#endif // FIXED + return 0; +} +//Warning, experimental compression below, if you want to use it, talk to Kevin. Doesn't do much currently, only reduces 0's +#define COMPRESS_KEY 0xfd +int nw_Compress(void *srcdata,void *destdata,int count) +{ +#ifdef FIXED + int i; + ubyte *curr_src = (ubyte *)srcdata; + ubyte *currp = (ubyte *)destdata; + for(i = 0;i Psnet_highest_id)) + { + return 0; + } + // search until we find the lowest packet index id# + for(idx=0;idx Psnet_highest_id)) + { + return 0; + } + // search until we find the lowest packet index id# + for(idx=0;idxabort = true; + async_dns_lookup *newaslu; + newaslu = (async_dns_lookup *)mem_malloc(sizeof(async_dns_lookup)); + memset(&newaslu->ip,0,sizeof(unsigned int)); + newaslu->host = hostname; + newaslu->done = false; + newaslu->error = false; + newaslu->abort = false; + lastaslu = newaslu; + aslu.done = false; +#ifdef WIN32 + _beginthread(gethostbynameworker,0,newaslu); +#else + HOSTENT *he = gethostbyname(lastaslu->host); + if(he==NULL) + { + lastaslu->error = true; + } + else + { + memcpy(&lastaslu->ip,he->h_addr_list[0],sizeof(unsigned int)); + lastaslu->done = true; + memcpy(&aslu,lastaslu,sizeof(async_dns_lookup)); + } +#endif + return 1; + } + else if(command==NW_AGHBN_CANCEL) + { + if(lastaslu) + lastaslu->abort = true; + lastaslu = NULL; + } + else if(command==NW_AGHBN_READ) + { + if(!lastaslu) + return -1; + if(aslu.done) + { + lastaslu = NULL; + memcpy(ip,&aslu.ip,sizeof(unsigned int)); + return 1; + } + else if(aslu.error) + { + mem_free(lastaslu); + lastaslu = NULL; + return -1; + } + else return 0; + } +#endif // FIXED + return -2; +} +// This is the worker thread which does the lookup. +void __cdecl gethostbynameworker(void *parm) +{ +#ifdef FIXED + async_dns_lookup *lookup = (async_dns_lookup *)parm; + HOSTENT *he = gethostbyname(lookup->host); + if(he==NULL) + { + lookup->error = true; + return; + } + else if(!lookup->abort) + { + memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int)); + lookup->done = true; + memcpy(&aslu,lookup,sizeof(async_dns_lookup)); + } + mem_free(lookup); +#endif // FIXED +} +int nw_ReccomendPPS() +{ +#ifdef FIXED + static char szconnspeed[100]; + int len = 99; + strcpy(szconnspeed,""); + Database->read("ConnectionSpeed", szconnspeed, &len); + if(strcmpi(szconnspeed,"28K")==0) + return 5; + else if(strcmpi(szconnspeed,"33K")==0) + return 6; + else if(strcmpi(szconnspeed,"56K")==0) + return 7; + else if(strcmpi(szconnspeed,"ISDN")==0) + return 8; + else if(strcmpi(szconnspeed,"Cable")==0) + return 9; + else if(strcmpi(szconnspeed,"Fast")==0) + return 12; + else + return 7; +#endif // FIXED +} +//Register the networking library to call your function back +//When data containing your ID is found +//Returns non-zero if succesfull, Zero if this ID is already registered +int nw_RegisterCallback(NetworkReceiveCallback nfp, ubyte id) +{ +#ifdef FIXED + ASSERT(id<16); + if(Netcallbacks[id]) + { + mprintf((0,"Trying to reregister a callback!\n")); + Int3(); // Get Kevin! + } + + Netcallbacks[id] = nfp; +#endif // FIXED + return 0; +} +NetworkReceiveCallback nw_UnRegisterCallback(ubyte id) +{ +#ifdef FIXED + NetworkReceiveCallback nfp; + ASSERT(id<16); + nfp = Netcallbacks[id]; + Netcallbacks[id] = NULL; + return nfp; +#endif // FIXED +} +int nw_SendWithID(ubyte id,ubyte *data,int len,network_address *who_to) +{ +#ifdef FIXED + ubyte packet_data[1500]; + int send_this_packet=1; + SOCKET send_sock; + SOCKADDR_IN sock_addr; // UDP/TCP socket structure + SOCKADDR_IPX ipx_addr; // IPX socket structure + int ret, send_len; + ubyte iaddr[6], *send_data; + short port; + fd_set wfds; + + ASSERT(data); + ASSERT(len); + ASSERT(who_to); + timeval timeout = {0,0}; + + packet_data[0] = id; + memcpy(packet_data+1,data,len); + len++;//Account for the added byte + //mprintf((0,"Sending packet for id %d.\n",id)); + #ifdef WIN32 + if(Use_DirectPlay) + return dp_DirectPlaySend(who_to,(ubyte *)data,len,false); + #endif + //send_sock = *Unreliable_socket; + switch ( who_to->connection_type ) + { + case NP_IPX: + send_sock = IPX_socket; + if(!IPX_active) + return 0; + break; + case NP_TCP: + send_sock = TCP_socket; + if(!TCP_active) + return 0; + break; + default: + mprintf((0,"Unknown protocol type in nw_Send()\n")); + Int3(); + return 0; + } + /* + ubyte compdata[MAX_PACKET_SIZE*3]; + ubyte testdata[MAX_PACKET_SIZE*3]; + int uncompsize; + ////Int3(); + Uncompressed_outgoing_data_len += len; + int compsize = nw_Compress(data,compdata,len); + Compressed_outgoing_data_len += compsize; + uncompsize = nw_Uncompress(compdata,testdata,compsize); + + ASSERT(uncompsize==len); + ASSERT(memcmp(data,testdata,uncompsize)==0); + + int my_comp_ratio = (float) ((float)Uncompressed_outgoing_data_len/(float)Compressed_outgoing_data_len); + + mprintf_at((2,1,0,"Compression: %d%% ",my_comp_ratio)); + */ + if ( !Sockets_initted) + { + mprintf((0,"Network ==> Socket not inited in nw_Send\n")); + return 0; + } + + memset(iaddr, 0x00, 6); + memcpy(iaddr, who_to->address, 6); + port = who_to->port; + + if ( port == 0) + { + mprintf((0,"Network ==> destination port %d invalid in psnet_send\n", port)); + Int3(); + return 0; + } + + + send_len = len; + send_data = (ubyte *)packet_data; + FD_ZERO(&wfds); + FD_SET( send_sock, &wfds ); + int sock_writable = select( send_sock+1, NULL, &wfds, NULL, &timeout); + if ( sock_writable == SOCKET_ERROR ) + { + mprintf((0, "Error on blocking select for write %d\n", WSAGetLastError() )); + return 0; + } + if(!sock_writable) + { + //This packet gets dropped. + return 0; + } + + if (send_this_packet) + { + switch ( who_to->connection_type ) + { + + case NP_IPX: + #ifdef WIN32 + ipx_addr.sa_family = AF_IPX; + ipx_addr.sa_socket = htons(port); + memcpy(ipx_addr.sa_nodenum, iaddr, 6); + memcpy(ipx_addr.sa_netnum, who_to->net_id, 4); + #else + ipx_addr.sipx_family = AF_IPX; + ipx_addr.sipx_port = htons(port); + memcpy(ipx_addr.sipx_node, iaddr, 6); + memcpy(&ipx_addr.sipx_network, who_to->net_id, 4); + #endif + + ret = sendto(IPX_socket, (char *)send_data, send_len, 0, (SOCKADDR*)&ipx_addr, sizeof(ipx_addr)); + break; + case NP_TCP: + sock_addr.sin_family = AF_INET; + memcpy(&sock_addr.sin_addr.s_addr, iaddr, 4); + sock_addr.sin_port = htons(port); + ret = sendto( TCP_socket, (char *)send_data, send_len, 0, (SOCKADDR*)&sock_addr, sizeof(sock_addr) ); + break; + + default: + Int3(); // Unknown protocol + break; + } // end switch + } + int lasterr; + if ( ret != SOCKET_ERROR ) + { + return 1; + } + lasterr = WSAGetLastError(); + if(lasterr == WSAEWOULDBLOCK) + { + return 0; + } + mprintf((0, "Couldn't send data (%d)!\n", lasterr) ); +#endif // FIXED + return 0; +} +int nw_DoReceiveCallbacks(void) +{ +#ifdef FIXED + + SOCKADDR_IN ip_addr; // UDP/TCP socket structure + SOCKADDR_IPX ipx_addr; // IPX socket structure + fd_set rfds; + timeval timeout; + int read_len, from_len; + network_address from_addr; + ubyte packet_data[1500]; + nw_ReliableResend(); + while ( TCP_active ) + { + // check if there is any data on the socket to be read. The amount of data that can be + // atomically read is stored in len. + + FD_ZERO(&rfds); + FD_SET( TCP_socket, &rfds ); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if ( select( TCP_socket+1, &rfds, NULL, NULL, &timeout) == SOCKET_ERROR) + { + mprintf((0, "Error %d doing a socket select on IP read\n", WSAGetLastError())); + break; + } + // if the read file descriptor is not set, then bail! + if ( !FD_ISSET(TCP_socket, &rfds ) ) + break; + // get data off the socket and process + from_len = sizeof(SOCKADDR_IN); + read_len = recvfrom( TCP_socket, (char *)packet_data,1500, 0, (SOCKADDR*)&ip_addr, &from_len ); + + if ( read_len == SOCKET_ERROR ) + { + int x = WSAGetLastError(); + mprintf((0, "Read error on IP socket. Winsock error %d \n", x)); + break; + } + memset(&from_addr, 0x00, sizeof(network_address)); + from_addr.connection_type = NP_TCP; + from_addr.port = ntohs( ip_addr.sin_port ); + + #ifdef WIN32 + memcpy(from_addr.address, &ip_addr.sin_addr.S_un.S_addr, 4); + #else + memcpy(from_addr.address, &ip_addr.sin_addr.s_addr, 4); + #endif + ubyte packet_id = (packet_data[0] & 0x0f); + if(Netcallbacks[packet_id]) + { + //mprintf((0,"Calling network callback for id %d.\n",packet_id)); + Netcallbacks[packet_id](packet_data+1,read_len-1,&from_addr); + } + } + while ( IPX_active ) + { + // check if there is any data on the socket to be read. The amount of data that can be + // atomically read is stored in len. + FD_ZERO(&rfds); + FD_SET( IPX_socket, &rfds ); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if ( select( IPX_socket+1, &rfds, NULL, NULL, &timeout) == SOCKET_ERROR ) + { + mprintf((0, "Error %d doing a socket select on IPX read\n", WSAGetLastError())); + break; + } + // if the read file descriptor is not set, then bail! + if ( !FD_ISSET(IPX_socket, &rfds ) ) + break; + // get data off the socket and process + from_len = sizeof(SOCKADDR_IPX); + read_len = recvfrom( IPX_socket, (char *)packet_data,1500, 0, (SOCKADDR*)&ipx_addr, &from_len ); + + if ( read_len == SOCKET_ERROR ) + { + int x = WSAGetLastError(); + mprintf((0, "Read error on IPX socket. Winsock error %d \n", x)); + break; + } + memset(&from_addr, 0x00, sizeof(network_address)); + from_addr.connection_type = NP_IPX; + #ifdef WIN32 + from_addr.port = ntohs( ipx_addr.sa_socket ); + memcpy(from_addr.address, &ipx_addr.sa_nodenum, 6); + memcpy(from_addr.net_id, &ipx_addr.sa_netnum, 4); + #else + from_addr.port = ntohs( ipx_addr.sipx_port ); + memcpy(from_addr.address, &ipx_addr.sipx_node, 6); + memcpy(from_addr.net_id, &ipx_addr.sipx_network, 4); + #endif + + ubyte packet_id = (packet_data[0] & 0x0f); + if(Netcallbacks[packet_id]) + { + //mprintf((0,"Calling network callback for id %d.\n",packet_id)); + Netcallbacks[packet_id](packet_data+1,read_len-1,&from_addr); + } + } +#endif // FIXED + return 0; +} +//Resend any unack'd packets and send any buffered packets, heartbeats, etc. +void nw_ReliableResend(void) +{ +#ifdef FIXED + int i,j; + int rcode = -1; + int max_len = NETBUFFERSIZE; + static reliable_header rcv_buff; + static SOCKADDR rcv_addr; + int bytesin = 0; + int addrlen = sizeof(SOCKADDR); + reliable_socket *rsocket = NULL; + //Go through each reliable socket that is connected and do any needed work. + for(j=0;jstatus==RNF_LIMBO) + if((timer_GetTime() - rsocket->last_packet_received)>NETTIMEOUT) + { + mprintf((0,"Reliable (but in limbo) socket (%d) timed out in nw_WorkReliable().\n",j)); + memset(rsocket,0,sizeof(reliable_socket)); + rsocket->status = RNF_UNUSED;//Won't work if this is an outgoing connection. + } + } + else + { + if((rsocket->status==RNF_LIMBO)&&((timer_GetTime() - first_sent_iamhere)>NETTIMEOUT)) + { + rsocket->status = RNF_BROKEN; + mprintf((0,"Reliable socket (%d) timed out in nw_WorkReliable().\n",j)); + } + } + + if(rsocket->status==RNF_CONNECTED) + { + float retry_packet_time; + if((rsocket->mean_ping==0) || (rsocket->mean_ping > (NETRETRYTIME*4))) + { + retry_packet_time = NETRETRYTIME; + } + else + { + if(rsocket->mean_pingmean_ping * (float)1.25); + //mprintf((0,"Using retransmission time of %f\n",retry_packet_time)); + } + } + //Iterate through send buffers. + for(i=0;iwaiting_packet_number) + && ( (((timer_GetTime() - rsocket->last_sent) > R_NET_PACKET_QUEUE_TIME)) || ((rsocket->mean_ping>0)&&(rsocket->mean_pingsend_urgent ) + ) + || ( (rsocket->sbuffers[i]) && ((timer_GetTime() - rsocket->timesent[i]) >= retry_packet_time) ) + ) //Send again + { + + if(i==rsocket->waiting_packet_number) + { + rsocket->waiting_packet_number = -1; + rsocket->last_sent = timer_GetTime(); + //mprintf((0,"Sending delayed packet...\n")); + } + reliable_header send_header; + //mprintf((0,"Resending reliable packet in nw_WorkReliable().\n")); + send_header.send_time = timer_GetTime(); + send_header.seq = rsocket->ssequence[i]; + memcpy(send_header.data,rsocket->sbuffers[i]->buffer,rsocket->send_len[i]); + send_header.data_len = rsocket->send_len[i]; + send_header.type = RNT_DATA; + + network_address send_address; + memset(&send_address,0,sizeof(network_address)); + + send_address.connection_type = rsocket->connection_type; + + if(NP_IPX==send_address.connection_type) + { + SOCKADDR_IPX *ipxaddr = (SOCKADDR_IPX *)&rsocket->addr; + #ifdef WIN32 + memcpy(send_address.address,ipxaddr->sa_nodenum, 6); + memcpy(send_address.net_id,ipxaddr->sa_netnum, 4); + send_address.port = htons(ipxaddr->sa_socket); + #else + memcpy(send_address.address,ipxaddr->sipx_node, 6); + memcpy(send_address.net_id,&ipxaddr->sipx_network, 4); + send_address.port = htons(ipxaddr->sipx_port); + #endif + send_address.connection_type = NP_IPX; + } + else if(NP_TCP==send_address.connection_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rsocket->addr; + memcpy(send_address.address,&inaddr->sin_addr, 4); + send_address.port = htons(inaddr->sin_port); + send_address.connection_type = NP_TCP; + } + rcode = nw_SendWithID(NWT_RELIABLE,(ubyte *)&send_header,RELIABLE_PACKET_HEADER_ONLY_SIZE+rsocket->send_len[i],&send_address); + + if((rcode==SOCKET_ERROR)&&(WSAEWOULDBLOCK==WSAGetLastError())) + { + //The packet didn't get sent, flag it to try again next frame + rsocket->timesent[i] = timer_GetTime()-(NETRETRYTIME*4); + } + else + { + rsocket->last_packet_sent = timer_GetTime(); + rsocket->timesent[i] = timer_GetTime(); + } + + } + } + //We've sent all the packets, now we go out of urgent mode. + rsocket->send_urgent = 0; + if((rsocket->status==RNF_CONNECTED) && ((timer_GetTime() - rsocket->last_packet_sent)>NETHEARTBEATTIME)) + { + reliable_header send_header; + //mprintf((0,"Resending reliable packet in nw_WorkReliable().\n")); + send_header.send_time = timer_GetTime(); + send_header.seq = 0; + send_header.data_len = 0; + send_header.type = RNT_HEARTBEAT; + rcode = -1; + network_address send_address; + memset(&send_address,0,sizeof(network_address)); + + send_address.connection_type = rsocket->connection_type; + + if(NP_IPX==send_address.connection_type) + { + SOCKADDR_IPX *ipxaddr = (SOCKADDR_IPX *)&rsocket->addr; + #ifdef WIN32 + memcpy(send_address.address,ipxaddr->sa_nodenum, 6); + memcpy(send_address.net_id,ipxaddr->sa_netnum, 4); + send_address.port = htons(ipxaddr->sa_socket); + #else + memcpy(send_address.address,ipxaddr->sipx_node, 6); + memcpy(send_address.net_id,&ipxaddr->sipx_network, 4); + send_address.port = htons(ipxaddr->sipx_port); + #endif + send_address.connection_type = NP_IPX; + } + else if(NP_TCP==send_address.connection_type) + { + SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rsocket->addr; + memcpy(send_address.address,&inaddr->sin_addr, 4); + send_address.port = htons(inaddr->sin_port); + send_address.connection_type = NP_TCP; + } + rcode = nw_SendWithID(NWT_RELIABLE,(ubyte *)&send_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,&send_address); + + if((rcode!=SOCKET_ERROR)&&(WSAEWOULDBLOCK!=WSAGetLastError())) + { + //It must have been sent + rsocket->last_packet_sent = timer_GetTime(); + } + } + if((rsocket->status==RNF_CONNECTED) && ((timer_GetTime() - rsocket->last_packet_received)>NETTIMEOUT)) + { + //This socket is hosed.....inform someone? + mprintf((0,"Reliable Socket (%d) timed out in nw_WorkReliable().\n",j)); + rsocket->status = RNF_BROKEN; + } + } + } +#endif // FIXED +} \ No newline at end of file diff --git a/mac/MACONLY.CPP b/mac/MACONLY.CPP new file mode 100644 index 000000000..3705e7559 --- /dev/null +++ b/mac/MACONLY.CPP @@ -0,0 +1,132 @@ +#include +#include +#include +#include "debug.h" +#include "mem.h" +#include "ddio.h" + +//void atexit(void *proc) +//{ +// mprintf((2, "atexit\n")); +//} + +int stricmp(const char *s1, const char *s2) +{ + char c1, c2; + while (1) + { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} +int strnicmp(const char *s1, const char *s2, int n) +{ + int i; + char c1, c2; + for (i=0; i c2) return 1; + if (!c1) return 0; + } + return 0; +} +void Sleep(int millis) +{ + longlong start = timer_GetMSTime(); + while(timer_GetMSTime() - start < millis) + ; +} +#define USE_MALLOC + +void HeapFree(HANDLE heap, int dummy, void *mptr) +{ + if(mptr) +#ifdef USE_MALLOC + free(mptr); +#else + DisposePtr((char *)mptr); +#endif +} + +void *HeapAlloc(HANDLE heap, int dummy, int size) +{ + if(size<=0) + return NULL; +#ifdef USE_MALLOC + return malloc(size); +#else + return NewPtr(size); +#endif +} +void *HeapReAlloc(HANDLE heap, int dummy, void * oldblock, int size) +{ + if(size<=0) + return NULL; +#ifdef USE_MALLOC + return (realloc(oldblock,size)); +#else + SetPtrSize((char*)oldblock, size); + return (oldblock); +#endif +} + +void GlobalFree(void *mptr) +{ + if(mptr) + mem_free(mptr); +} +void *GlobalAlloc(int flags,int size) +{ + if(size<=0) + return NULL; + return mem_malloc(size); +} + +void *GlobalLock(HGLOBAL hMem) +{ + return hMem; +} +char *strupr(char *string) +{ + while(string && *string) + { + *string = toupper(*string); + string++; + } + return string; +} +char *itoa(int value,char *string,int radix) +{ + if(radix==10) + { + sprintf(string,"%d",value); + }else if(radix==16) + { + sprintf(string,"%x",value); + }else + { + DebugStr("\p!!!!!!!!!!!!!!!WARNING CALLING itoa WITHOUT 10 or 16 RADIX!!!!!!!!!!!!!!!!!!!!!!"); + sprintf(string,"%d",value); + } + return string; +} + +void Debug_ConsoleRedirectMessages(int virtual_window, int physical_window) {} + +int debug_level = 2; +void Debug_ConsolePrintf(int n,char *format,...) +{ + if(n >= debug_level) { + va_list args; + va_start(args, format ); + vprintf(format,args); + va_end(args); + } + return; +} diff --git a/mac/MACONLY.H b/mac/MACONLY.H new file mode 100644 index 000000000..368cdefc0 --- /dev/null +++ b/mac/MACONLY.H @@ -0,0 +1,194 @@ +#include "MacHeaders.h" + +#include +#include +#include +#include + +// Constants +#define MACINTOSH + +#include "pserror.h" +#include "Macros.h" + +#define RELEASE +//#define mprintf(args) //Debug_ConsolePrintf args + +//#pragma options align packed +#define MEM_USE_RTL 1 +#define and andVar +#define or orVar +#define _fstat fstat +#define _filelength filelength +#define __cdecl +#define _MAX_PATH 1024 +#define _MAX_FNAME 64 /* max. length of path component*/ +#define _MAX_EXT 64 /* max. length of extension component*/ +#define TRANSPARENT_COLOR 0 +// These are other error types defined in Visual C++. +// The values are made up and hopefullydon't conflict JCA +#define EACCES 102 +#define EDEADLOCK 104 +#define EMFILE 105 +#define ENOENT 106 +#define ENFILE 107 +#define ENOSPC 108 +#define ENOSYS 112 + +#define _finite(a) finite(a) +#define strcmpi(a,b) stricmp(a,b) +#define strcmpni(a,b,c) strnicmp(a,b,c) +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define HeapSize(a,b,c) 0 +#define HeapDestroy(a) + +int stricmp(const char *inX, const char *inY); +int strnicmp(const char *inX, const char *inY, int len); +char *strdup(const char *str); + +#if 0 +/////////////////////////// +// From Winsock.h +/////////////////////////// + +/* + * Address families. + */ +#define AF_UNSPEC 0 /* unspecified */ +#define AF_UNIX 1 /* local to host (pipes, portals) */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#define AF_IMPLINK 3 /* arpanet imp addresses */ +#define AF_PUP 4 /* pup protocols: e.g. BSP */ +#define AF_CHAOS 5 /* mit CHAOS protocols */ +#define AF_IPX 6 /* IPX and SPX */ +#define AF_NS 6 /* XEROX NS protocols */ +#define AF_ISO 7 /* ISO protocols */ +#define AF_OSI AF_ISO /* OSI is ISO */ +#define AF_ECMA 8 /* european computer manufacturers */ +#define AF_DATAKIT 9 /* datakit protocols */ +#define AF_CCITT 10 /* CCITT protocols, X.25 etc */ +#define AF_SNA 11 /* IBM SNA */ +#define AF_DECnet 12 /* DECnet */ +#define AF_DLI 13 /* Direct data link interface */ +#define AF_LAT 14 /* LAT */ +#define AF_HYLINK 15 /* NSC Hyperchannel */ +#define AF_APPLETALK 16 /* AppleTalk */ +#define AF_NETBIOS 17 /* NetBios-style addresses */ +#define AF_VOICEVIEW 18 /* VoiceView */ +#define AF_FIREFOX 19 /* FireFox */ +#define AF_UNKNOWN1 20 /* Somebody is using this! */ +#define AF_BAN 21 /* Banyan */ +#define AF_MAX 22 + +/* + * Types + */ +#define SOCK_STREAM 1 /* stream socket */ +#define SOCK_DGRAM 2 /* datagram socket */ +#define SOCK_RAW 3 /* raw-protocol interface */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequenced packet stream */ +/* + * Basic system type definitions, taken from the BSD file sys/types.h. + */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +/* + * The new type to be used in all + * instances which refer to sockets. + */ +typedef u_int SOCKET; +/* + * Socket address, internet style. + */ +struct sockaddr { + unsigned short sa_family; /* address family */ + char sa_data[14]; /* up to 14 bytes of direct address */ +}; +struct in_addr { + union { + struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { u_short s_w1,s_w2; } S_un_w; + u_long S_addr; + } S_un; +#define s_addr S_un.S_addr + /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 + /* host on imp */ +#define s_net S_un.S_un_b.s_b1 + /* network */ +#define s_imp S_un.S_un_w.s_w2 + /* imp */ +#define s_impno S_un.S_un_b.s_b4 + /* imp # */ +#define s_lh S_un.S_un_b.s_b3 + /* logical host */ +}; +struct sockaddr_in { + short sin_family; + u_short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; +#endif + +// Types +typedef unsigned char BOOL; +typedef void **HANDLE; +typedef Fixed fix; +typedef long long INT64; +typedef void *HGLOBAL; +// Function Definitions +#define _finite(a) finite(a) +#define strcmpi(a,b) stricmp(a,b) +#define strcmpni(a,b,c) strnicmp(a,b,c) +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define HeapSize(a,b,c) 0 +#define HeapDestroy(a) +int stricmp(const char *inX, const char *inY); +int strnicmp(const char *inX, const char *inY, int len); +char *strdup(const char *str); +void GlobalFree(void *); +void *GlobalAlloc(int flags,int size); +void *GlobalLock(HGLOBAL hMem); +void Sleep(int millis); +char *itoa(int value,char *string,int radix); +char *strupr(char *string); +inline int finite(double a) +{ + return(a != HUGE_VAL); +} + + +#if 0 +/////////////////////////// +// From Winsock.h +/////////////////////////// +SOCKET accept (SOCKET s, struct sockaddr *addr, + int *addrlen); +int bind (SOCKET s, const struct sockaddr *addr, int namelen); +int closesocket (SOCKET s); +int connect (SOCKET s, const struct sockaddr *name, int namelen); +int ioctlsocket (SOCKET s, long cmd, u_long *argp); +int getpeername (SOCKET s, struct sockaddr *name, int * namelen); +int getsockname (SOCKET s, struct sockaddr *name, int * namelen); +int getsockopt (SOCKET s, int level, int optname, char * optval, int *optlen); +#define htonl(hostlong) (hostlong) +#define htons(hostshort) (hostshort) +unsigned long inet_addr (const char * cp); +char * inet_ntoa (struct in_addr in); +int listen (SOCKET s, int backlog); +u_long ntohl (u_long netlong); +u_short ntohs (u_short netshort); +int recv (SOCKET s, char * buf, int len, int flags); +int recvfrom (SOCKET s, char * buf, int len, int flags, struct sockaddr *from, int * fromlen); +//int select (int nfds, fd_set *readfds, fd_set *writefds, +// fd_set *exceptfds, const struct timeval *timeout); +int send (SOCKET s, const char * buf, int len, int flags); +int sendto (SOCKET s, const char * buf, int len, int flags, const struct sockaddr *to, int tolen); +int setsockopt (SOCKET s, int level, int optname, const char * optval, int optlen); +int shutdown (SOCKET s, int how); +SOCKET socket (int af, int type, int protocol); +#endif \ No newline at end of file diff --git a/mac/MACREGISTRY.CPP b/mac/MACREGISTRY.CPP new file mode 100644 index 000000000..08c9377cd --- /dev/null +++ b/mac/MACREGISTRY.CPP @@ -0,0 +1,427 @@ +#include +#include +#include +#include +#include +#include "registry.h" +#include "mono.h" +//Convert a string that represents a hex value into an int +int hextoi(char *p) +{ + int value = 0; + while( (p) && (*p) && isalnum(*p) ){ + *p = toupper(*p); + if ( (*p>='0') && (*p<='9') ) + value = (value * 16) + ((*p)-'0'); + else if ( (*p>='A') && (*p<='F') ) + value = (value * 16) + ((*p)-'A'); + else + return value; + + p++; + } + return value; +} +//Removes whitespace from the start of the given string. +//Returns a pointer to the first non-white character +char *SkipWhite(char *p) +{ + while (isspace(*p)) + p++; + return p; +} +//Parses a quoted string +//Returns true if got string ok, else false +char *ParseString(char *p,char *buf,int bufsize,char sdelim,char edelim) +{ + char *save_p; + + p = SkipWhite(p); + + save_p = p; + + if (*p != sdelim) { + return NULL; + } + else + p++; //skip initial quote + + //Copy chars until endquote or out of space + while (*p && (*p != edelim) && --bufsize) + *buf++ = *p++; + + //Check for buffer overflow + if (bufsize <= 0) { + return NULL; + } + + //Check for missing endquote + if (! *p) { + return NULL; + } + //Write terminator + *buf = 0; + + //Return new pointer (move over a char for end quote) + return p+1; +} +//Parses a sequence of non-space characters +char *ParseToken(char *p,char *buf,int bufsize) +{ + char *save_p; + p = SkipWhite(p); + save_p = p; + + while (!isspace(*p) && (*p != ',') && *p && --bufsize) + *buf++ = *p++; + + *buf = 0; + + //Check for buffer overflow + if (bufsize <= 0) { + return NULL; + } + + return p; +} +#define PARSE_KEY(buf) do {ptr = ParseString(ptr,buf,sizeof(buf),'[',']'); } while(0) +#define PARSE_STRING(buf) do {ptr = ParseString(ptr,buf,sizeof(buf),'"','"'); } while (0) +#define PARSE_TOKEN(buf) do {ptr = ParseToken(ptr,buf,sizeof(buf)); } while (0) +CRegistry::CRegistry(char *str) +{ + currentkey = root = NULL; + strcpy(name,str); +} +CRegistry::~CRegistry() +{ + Destroy(); +} +void CRegistry::GetSystemName(char *n) +{ + strcpy(n,name); +} +void CRegistry::SetSystemName(char *n) +{ + strcpy(name,n); +} +void CRegistry::Destroy(void) +{ + tKey *curr,*next; + curr = next = root; + while(curr){ + next = curr->next; + DestroyKey(curr); + curr = next; + } + root = NULL; +} +void CRegistry::DestroyKey(tKey *key) +{ + tRecord *curr, *next; + curr = next = key->records; + while(curr){ + next = curr->next; + DestroyRecord(curr); + curr = next; + } + free(key); +} +void CRegistry::DestroyRecord(tRecord *record) +{ + if(record->data){ + free(record->data); + record->data = NULL; + } + free(record); +} +void CRegistry::Export() +{ + tKey *curr,*next; + curr = next = root; + FILE *file; + file = fopen(name,"wt"); + if(!file) + return; + while(curr){ + next = curr->next; + ExportKey(curr,file); + curr = next; + } + fclose(file); +} +void CRegistry::ExportKey(tKey *key,FILE *file) +{ + tRecord *curr, *next; + curr = next = key->records; + //write out name + char buffer[258]; + sprintf(buffer,"[%s]\n",key->name); + fputs(buffer,file); + while(curr){ + next = curr->next; + ExportRecord(curr,file); + curr = next; + } +} +void CRegistry::ExportRecord(tRecord *record,FILE *file) +{ + int *dw; + char *st; + char buffer[512]; + switch(record->type){ + case REGT_STRING: + { + st = (char *)record->data; + sprintf(buffer,"\"%s\"=\"%s\"\n",record->name,st); + }break; + case REGT_DWORD: + { + dw = (int *)record->data; + sprintf(buffer,"\"%s\"=dword:%X\n",record->name,*dw); + }break; + }; + fputs(buffer,file); +} +bool CRegistry::Import() +{ + char buffer[500]; + char newbuff[500]; + FILE *file; + char *ptr; + file = fopen(name,"rt"); + if(!file){ + mprintf((0,"Unable to import %s\n",name)); + return false; + } + mprintf((0,"Importing %s\n",name)); + Destroy(); + + bool oktocreate; + //loop till we are done + while(!feof(file)){ + oktocreate = true; + char type = REGT_STRING; + + //read in the string + fgets(buffer,500,file); + ptr = buffer; + if(feof(file))//we are at the end of the file so there isn't data to continue + oktocreate = false; + //see what we read, a key or record, if the first character is a [ than a key " is record + if(oktocreate){ + if(buffer[0]=='['){ + //Create a key! + PARSE_KEY(newbuff); + mprintf((0,"Found Key: |%s|\n",newbuff)); + CreateKey(newbuff); + }else if(buffer[0]=='\"'){ + //Create a record + //see what type of record by looking at whats after the = + char *p; + p = buffer; + bool done = false; + type = REGT_STRING; + while(!done){ + if( (!p) || (!*p) ) + done = true; + if( (p) && (*p=='=')){ + if(*(p+1)=='d') + type = REGT_DWORD; + else + type = REGT_STRING; + done = true; + } + p++; + } + //now we "SHOULD" know the type, parse the info + char data[300]; + int idata; + switch(type){ + case REGT_STRING: + PARSE_STRING(newbuff); + ptr++; //blow by = + PARSE_STRING(data); + if(!CreateRecord(newbuff,REGT_STRING,data)) + mprintf((0,"Unable to create String record: %s\n",newbuff)); + else + mprintf((0,"Created String record %s = %s\n",newbuff,data)); + break; + case REGT_DWORD: + PARSE_STRING(newbuff); + ptr+=7; //blow by =dword: + PARSE_TOKEN(data); + idata = hextoi(data); + if(!CreateRecord(newbuff,REGT_DWORD,&idata)) + mprintf((0,"Unable to create dword record: %s\n",newbuff)); + else + mprintf((0,"Created dword record %s = %X\n",newbuff,idata)); + break; + }; + }else + mprintf((0,"Expected [ or \"\n")); + } + } + fclose(file); + return true; +} +void CRegistry::CreateKey(char *name) +{ + tKey *curr; + if(LookupKey(name)){ + mprintf((0,"Key: %s already exists\n",name)); + return; + } + if(!root){ + root = (tKey *)malloc(sizeof(tKey)); + if(!root) + return; + curr = root; + }else{ + curr = root; + while(curr->next){ + curr = curr->next; + } + curr->next = (tKey *)malloc(sizeof(tKey)); + if(!curr->next) + return; + curr = curr->next; + + } + curr->next = NULL; + strcpy(curr->name,name); + curr->records = NULL; + currentkey = curr; +} +bool CRegistry::LookupKey(char *name) +{ + tKey *curr; + curr = root; + while(curr){ + if( !strcasecmp(name,curr->name) ){ + //found a match + currentkey = curr; + return true; + } + curr = curr->next; + } + return false; +} +tRecord *CRegistry::LookupRecord(char *record,void *data) +{ + if(!currentkey) + return NULL; + + tRecord *curr; + curr = currentkey->records; + while(curr){ + if( !strcasecmp(record,curr->name) ){ + //found the record + switch(curr->type) + { + case REGT_STRING: + char *st; + st = (char *)curr->data; + strcpy((char *)data,st); + break; + case REGT_DWORD: + int *dw; + dw = (int *)curr->data; + *((int *)data) = *dw; + break; + }; + return curr; + } + curr = curr->next; + } + return NULL; +} +int CRegistry::GetDataSize(char *record) +{ + if(!currentkey) + return false; + tRecord *curr; + curr = currentkey->records; + while(curr){ + if( !strcasecmp(record,curr->name) ){ + //found the record + switch(curr->type) + { + case REGT_STRING: + return strlen((char *)curr->data)+1; + break; + case REGT_DWORD: + return sizeof(int); + break; + }; + return 0; + } + curr = curr->next; + } + return 0; +} +bool CRegistry::CreateRecord(char *name,char type,void *data) +{ + if(!currentkey) + return false; + tRecord *curr; + //first see if the record exists under this key + int datasize = GetDataSize(name); + if(datasize){ + char *olddata = (char *)malloc(datasize); + if(olddata){ + curr = LookupRecord(name,olddata); + if(curr){ + //ok we have an old value, replace it! + mprintf((0,"Replacing %s\n",name)); + if(curr->data) + free(curr->data); + free(olddata); + curr->type = type; + switch(type){ + case REGT_STRING: + curr->data = malloc(strlen((char *)data)+1); + strcpy((char *)curr->data,(char *)data); + break; + case REGT_DWORD: + curr->data = malloc(sizeof(int)); + *((int *)curr->data) = *((int *)data); + break; + } + return true; + } + } + } + + //it is a new record + if(currentkey->records){ + curr = currentkey->records; + while(curr->next) + curr = curr->next; + curr->next = (tRecord *)malloc(sizeof(tRecord)); + if(!curr->next) + return false; + curr = curr->next; + }else{ + currentkey->records = (tRecord *)malloc(sizeof(tRecord)); + if(!currentkey->records) + return false; + curr = currentkey->records; + } + curr->next = NULL; + strcpy(curr->name,name); + switch(type) + { + case REGT_STRING: + curr->data = malloc(strlen((char *)data)+1); + curr->type = REGT_STRING; + strcpy((char *)curr->data,(char *)data); + break; + case REGT_DWORD: + curr->data = malloc(sizeof(int)); + curr->type = REGT_DWORD; + *((int *)curr->data) = *((int *)data); + break; + }; + return true; +} + diff --git a/mac/MACREGISTRY.H b/mac/MACREGISTRY.H new file mode 100644 index 000000000..0942753e2 --- /dev/null +++ b/mac/MACREGISTRY.H @@ -0,0 +1,44 @@ +#ifndef __REGISTRY_H_ +#define __REGISTRY_H_ +#define MAX_RECORD_NAME 256 +#define REGT_STRING 0 +#define REGT_DWORD 1 +#include +typedef struct tRecord +{ + char name[MAX_RECORD_NAME]; + char type; + void *data; + tRecord *next; +}tRecord; +typedef struct tKey +{ + char name[MAX_RECORD_NAME]; + tKey *next; + tRecord *records; +}tKey; +class CRegistry +{ +public: + CRegistry(char *name); + ~CRegistry(); + void Export(); + bool Import(); + void CreateKey(char *name); + bool LookupKey(char *name); + bool CreateRecord(char *name,char type,void *data); + tRecord *LookupRecord(char *record,void *data); + int GetDataSize(char *record); + void GetSystemName(char *name); + void SetSystemName(char *name); +private: + void Destroy(void); + void DestroyKey(tKey *key); + void DestroyRecord(tRecord *record); + void ExportKey(tKey *key,FILE *file); + void ExportRecord(tRecord *record,FILE *file); + char name[MAX_RECORD_NAME]; + tKey *root; + tKey *currentkey; +}; +#endif diff --git a/mac/MACSNDCONVERT.CPP b/mac/MACSNDCONVERT.CPP new file mode 100644 index 000000000..7df79cc14 --- /dev/null +++ b/mac/MACSNDCONVERT.CPP @@ -0,0 +1,62 @@ +#include +OSErr +ConvertFromWavToRawSound(void* inBufferP, UInt32 inBuffSize, void* outBufferP, UInt32* outBuffSize) +{ + OSErr theErr; + SoundConverter theSC; + UInt32 numFrames, numBytes, outBytes; + UInt32 aditionalOutputFrames, additionalOutputSize; + SInt32 currentBytes, bytesLeft; + SoundComponentData input, output; //these hold info for the formats + input.flags = 0; + input.format = MicrosoftADPCMFormat; /*one of the wav formats*/ + input.numChannels = 2; + input.sampleSize = 16; /*8 for eight bit sound*/ + input.sampleRate = rate44khz; /* a constant defined in Sound.h*/ + input.sampleCount = 0; + input.buffer = 0; + input.reserved = 0; + output.flags = 0; + output.format = kSoundNotCompressed; /*'raw ' */ + output.numChannels = 2; + output.sampleSize = 16; /*8 for eight bit sound*/ + output.sampleRate = rate44khz; /* a constant defined in Sound.h*/ + output.sampleCount = 0; + output.buffer = 0; + output.reserved = 0; + /*This opens a SoundConverter component. the params are ... + output format, output format, and the sound converter*/ + theErr = SoundConverterOpen(&input, &output, &theSC); + if(theErr) return theErr; + /* so how to allocate the new buffer? */ + theErr = SoundConverterGetBufferSize(theSC, inBuffSize, &numFrames, &numBytes, outBuffSize); + if(theErr) return theErr; + + outBuffer = mem_malloc(outBytes); + if(!outBuffer) { + mprintf((0, "ConvertFromWavToRawSound: unable to allocate %d bytes", outBytes)); + Int3(); + } + theErr = SoundConverterBeginConversion(theSC); + if(theErr) return theErr; + + if(inBuffSize <= outBytes) { /*we can do it in one big chunk...*/ + theErr = SoundConverterConvertBuffer(theSC, inBufferP, numFrames, outBufferP, 0, 0); + if(theErr) return theErr; + + theErr = SoundConverterEndConversion(theSC, outBufferP, &aditionalOutputFrames, &additionalOutputSize); + if(theErr) return theErr; + } + else { /*we need to do it in chunks :( */ + bytesLeft = *outBuffSize; + while(bytesLeft > 0) { + theErr = SoundConvertBuffer(theSC, inBufferP, numFrames, numBytes, outBufferP, 0, 0); + if(theErr) return theErr; + + bytesLeft -= numBytes; + } + theErr = SoundConverterEndConversion(theSC, outBufferP, &aditionalOutputFrames, &additionalOutputSize); + if(theErr) return theErr; + } + return theErr; +} diff --git a/mac/MACSOUND.CPP b/mac/MACSOUND.CPP new file mode 100644 index 000000000..d19d1674f --- /dev/null +++ b/mac/MACSOUND.CPP @@ -0,0 +1,193 @@ +/* +* $Logfile: /DescentIII/Main/mac/MACSOUND.CPP $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:58:15 $ +* $Author: kevinb $ +* +* Low-level linux sound driver +* +* $Log: MACSOUND.CPP,v $ +* Revision 1.1.1.1 2003/08/26 03:58:15 kevinb +* initial 1.5 import +* + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff +* +* +* $NoKeywords: $ +*/ +// NEED THIS SINCE DDSNDLIB is a DD library. +#include "DDAccess.h" +#include +#include +//#include +#include "CFILE.H" +#include "pserror.h" +#include "mono.h" +#include "soundload.h" +#include "ssl_lib.h" +#include "mem.h" +#include "application.h" +#include "macsound.h" +// Starts the sound library, maybe have it send back some information -- 3d support? +int macsound::InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played) +{ + return 0; +} +// Cleans up after the Sound Library +void macsound::DestroySoundLib(void) +{ +} +// Locks and unlocks sounds (used when changing play_info data) +bool macsound::LockSound(int sound_uid) +{ + return false; +} +bool macsound::UnlockSound(int sound_uid) +{ + return false; +} +bool macsound::SetSoundQuality(char quality) +{ + return true; +} +char macsound::GetSoundQuality(void) +{ + return 0; +} +bool macsound::SetSoundMixer(char mixer_type) +{ + return true; +} + +char macsound::GetSoundMixer(void) +{ + return 0; +} +// Plays a 2d sound +int macsound::PlaySound2d(play_information *play_info, int sound_index, float volume, float pan, bool f_looped) +{ + return -1; +} +int macsound::PlayStream(play_information *play_info) +{ + return -1; +} +void macsound::SetListener(pos_state *cur_pos) +{ +} +int macsound::PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float master_volume, bool f_looped, float reverb) //, unsigned short frequency +{ + return -1; +} +void macsound::AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency) +{ +} +void macsound::AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb) +{ +} +void macsound::StopAllSounds(void) +{ +} + +// Checks if a sound is playing (removes finished sound); +bool macsound::IsSoundInstancePlaying(int sound_uid) +{ + return false; +} +int macsound::IsSoundPlaying(int sound_index) +{ + return 0; +} +// Stops 2d and 3d sounds +void macsound::StopSound(int sound_uid, unsigned char f_immediately) +{ +} +// Pause all sounds/resume all sounds +void macsound::PauseSounds(void) +{ +} +void macsound::ResumeSounds(void) +{ +} +bool macsound::CheckAndForceSoundDataAlloc(int sound_file_index) +{ + return false; +} +// Begin sound frame +void macsound::SoundStartFrame(void) +{ +} +// End sound frame +void macsound::SoundEndFrame(void) +{ +} +// returns the error string. +char *macsound::GetErrorStr() const +{ + static char buffer[] = "Linux SND Driver Not Implementeded"; + return buffer; +} +bool macsound::SetGlobalReverbProperties(float volume,float damping,float decay) +{ + return false; +} +/////////////////////////////////////////////////////////////////////// +//llsGeometry +#include "ddsndgeometry.h" +// specify a sound library to associate geometry with +bool llsGeometry::Init(llsSystem *snd_sys) +{ + return false; +} +// closes low level geometry system. +void llsGeometry::Shutdown() +{ +} +void llsGeometry::StartFrame() +{ +} +void llsGeometry::EndFrame() +{ +} +// polygon lists +// is a group cached?, check before rendering it. +void llsGeometry::IsGroupValid(int group) +{ +} +// marks beginning of a list of polygons to render, (-1 group for non cache) +void llsGeometry::StartPolygonGroup(int group) +{ +} +// ends a list of polygons to render. +void llsGeometry::EndPolygonGroup(int group) +{ +} +// renders a group. +void llsGeometry::RenderGroup(int group) +{ +} +// primatives, nv = number of verts, and verts is an array of pointers to vertices. +// you can pass a sound material value if you want special reflective properties on this polygon. +void llsGeometry::AddPoly(int nv,vector **verts, tSoundMaterial material) +{ +} +// 4 verts here. +void llsGeometry::AddQuad(vector **verts) +{ +} + +// 3 verts here. +void llsGeometry::AddTriangle(vector **verts) +{ +} +void llsGeometry::CreateMaterial(tSoundMaterial material, float transmit_gain, float transmit_highfreq, float reflect_gain, float reflect_highfreq) +{ +} +void llsGeometry::DestroyMaterial(tSoundMaterial material) +{ +} diff --git a/mac/MACSOUND.H b/mac/MACSOUND.H new file mode 100644 index 000000000..92ecefbb2 --- /dev/null +++ b/mac/MACSOUND.H @@ -0,0 +1,54 @@ +#ifndef __MAC_DD_SOUND_H_ +#define __MAC_DD_SOUND_H_ +#include "ssl_lib.h" +class macsound : public llsSystem +{ +// Public functions +public: + // Starts the sound library, maybe have it send back some information -- 3d support? + virtual int InitSoundLib(char mixer_type, oeApplication *sos, unsigned char max_sounds_played); + // Cleans up after the Sound Library + virtual void DestroySoundLib(void); + // Locks and unlocks sounds (used when changing play_info data) + virtual bool LockSound(int sound_uid); + virtual bool UnlockSound(int sound_uid); + virtual bool SetSoundQuality(char quality); + virtual char GetSoundQuality(void); + virtual bool SetSoundMixer(char mixer_type); + virtual char GetSoundMixer(void); + // Plays a 2d sound + virtual int PlaySound2d(play_information *play_info, int sound_index, float volume, float pan, bool f_looped); + virtual int PlayStream(play_information *play_info); + virtual void SetListener(pos_state *cur_pos); + virtual int PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float master_volume, bool f_looped, float reverb=0.5f); //, unsigned short frequency + virtual void AdjustSound(int sound_uid, float f_volume, float f_pan, unsigned short frequency); + virtual void AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb=0.5f); + virtual void StopAllSounds(void); + + // Checks if a sound is playing (removes finished sound); + virtual bool IsSoundInstancePlaying(int sound_uid); + virtual int IsSoundPlaying(int sound_index); +// virtual void AdjustSound(int sound_uid, play_information *play_info) = 0; + // Stops 2d and 3d sounds + virtual void StopSound(int sound_uid, unsigned char f_immediately = SKT_STOP_IMMEDIATELY); + // Pause all sounds/resume all sounds + virtual void PauseSounds(void); + virtual void ResumeSounds(void); + virtual bool CheckAndForceSoundDataAlloc(int sound_file_index); + // Begin sound frame + virtual void SoundStartFrame(void); + + // End sound frame + virtual void SoundEndFrame(void); + // returns the error string. + virtual char *GetErrorStr() const; + // Returns current error code + int GetLastError() { int code = m_lib_error_code; m_lib_error_code = 0; return code; }; + // environmental sound interface + // volume modifier (0-1), damping(0-1), 1 = complete, 0 = none + // decay 0.1 to 100 seconds, how long it takes for a sound to die. + virtual bool SetGlobalReverbProperties(float volume, float damping, float decay); +protected: + void SetError(int code) { m_lib_error_code = code; }; +}; +#endif \ No newline at end of file diff --git a/mac/MACSTREAMAUDIO.CPP b/mac/MACSTREAMAUDIO.CPP new file mode 100644 index 000000000..239e11c46 --- /dev/null +++ b/mac/MACSTREAMAUDIO.CPP @@ -0,0 +1,1440 @@ +/* + * $Logfile: /DescentIII/Main/mac/MACSTREAMAUDIO.CPP $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:15 $ + * $Author: kevinb $ + * + * Interface for streaming audio, compressed or not + * + * $Log: MACSTREAMAUDIO.CPP,v $ + * Revision 1.1.1.1 2003/08/26 03:58:15 kevinb + * initial 1.5 import + * + * + * 2 4/12/00 7:08p Matt + * From Duane for 1.4 + * + * 1 10/21/99 1:55p Kevin + * Mac! + * + * 41 5/17/99 1:56p Ardussi + * changed to compile on Mac + * + * 40 5/10/99 9:22p Samir + * added code to allow for certain streams to be specified to start on + * next sound frame. + * + * 39 4/29/99 3:03p Samir + * took out mprintfs and if we're opening an opened stream, close it and + * continue. + * + * 38 4/17/99 5:58a Jeff + * Changes for linux + * + * 37 4/14/99 1:48a Jeff + * fixed case mismatched #includes + * + * 36 4/10/99 5:09p Samir + * beginning to add prioritization of sounds code. currently non + * functional, but allows me to change all calls to Play3dSound so that I + * can test later. + * + * 35 3/22/99 5:12p Samir + * on shutdown, CLOSE any open streams. + * + * 34 3/17/99 4:22p Samir + * made pause and resume for streams work. + * + * 33 3/09/99 6:40p Jeff + * put try/catch around StreamPlay + * + * 32 3/04/99 6:38p Samir + * Saved stream state per frame. + * + * 31 3/03/99 5:08p Samir + * added slew of debug code: hopefully the library will be revamped after + * OEM. + * + * 30 3/01/99 8:11p Samir + * fixed bug when audio stream was playing while sound system shutdown. + * + * 29 2/28/99 6:35p Samir + * fixed streaming bugs for very small samples. + * + * 28 2/28/99 5:11p Nate + * added playcount. + * + * 27 2/27/99 6:52p Samir + * added function to get current loop count + * + * 26 2/26/99 8:34p Samir + * may have fixed problem with really small streams. + * + * 25 2/26/99 5:31p Samir + * + * 24 2/25/99 9:20p Kevin + * Semi fix for music in DS + * + * 23 2/17/99 8:35p Samir + * use m_streamdone to determine whether play should reset the stream or + * not. + * + * 22 12/13/98 6:50p Samir + * fixed problems with looping and next.switching sync. + * + * 21 12/11/98 5:25p Samir + * took out debug messages. + * + * 20 12/11/98 5:20p Samir + * perhaps prevent unused buffers from playing. + * + * 19 12/11/98 3:27p Samir + * add debug code. + * + * 18 12/10/98 7:12p Samir + * fixed some bugs in file/playback buffer sequencing with new system. + * + * 17 12/10/98 10:11a Samir + * newer streaming audio library. + * + * 11 11/20/98 5:20p Samir + * correcting a lot of errors in AudioStream::Next + * + * 10 11/13/98 4:03p Nate + * took out volume stuff from high level sound lib + * + * 9 11/13/98 2:25p Samir + * added 'next' stream processing. + * + * 9 11/13/98 10:48a Samir + * added 'next' stream capability. + * + * 8 10/23/98 7:04p Samir + * + * 7 10/23/98 6:45p Matt + * Scale the streaming audio volume by the master volume. + * + * 6 8/10/98 5:54p Samir + * added looping streams and soft measure stopping. + * + * 5 7/30/98 4:35p Dan + * more error checking. + * + * 4 7/30/98 10:57a Sean + * if no sound, then don't do anything. + * + * 3 7/24/98 5:18p Samir + * use OSFArchive for stream file operations now. + * + * 2 7/09/98 8:36p Samir + * fully implemented redo of Stream interface. + * + * 1 7/09/98 6:48p Samir + * + * MOVED FROM MAIN PROJECT + * + * 10 6/29/98 4:15p Chris + * Streaming audio works + * + * 9 6/24/98 5:00p Jeff + * set filehandle to null on close, short circuit read data if file handle + * is NULL + * + * 8 6/24/98 4:41p Jeff + * will kill the stream is the sound is killed + * + * + * $NoKeywords: $ + */ +#include +#include "streamaudio.h" +#include "pserror.h" +#include "CFILE.H" +#include "mem.h" +#include "Macros.h" +#include "ddio.h" + +#include +#include + +#ifdef MACINTOSH +//#define __MACBUILD__ +#include "adecode.h" +#endif +//#include "samirlog.h" +#define LOGFILE(_s) + +#if defined(__LINUX__) +typedef struct {bool empty;} AudioDecoder; +typedef void *(*ad_malloc)(unsigned size); +typedef void (*ad_free)(void *p); +#endif + +#if defined(__LINUX__) +typedef unsigned ReadFunction(void *data, void *buf, unsigned qty); +AudioDecoder *Create_AudioDecoder(ReadFunction *reader, void *data,unsigned *pChannels, unsigned *pSampleRate,long *pSampleCount); +unsigned AudioDecoder_Read(AudioDecoder *ad, void *buf, unsigned qty); +void AudioDecoder_Close(AudioDecoder *ad); +void AudioDecoder_MallocFree(ad_malloc *fn_malloc, ad_free *fn_free); +#endif + +void * MyDoubleBackProc (SndChannelPtr channel, SndDoubleBufferPtr dBuffer); + +// this stream is for everyone (used by the StreamPlay interface) +static AudioStream User_audio_stream; + +llsSystem *AudioStream::m_ll_sndsys = NULL; +AudioStream *AudioStream::m_streams[STRM_LIMIT]; +int AudioStream::m_thisid = -1; + +#define SAMPLES_PER_STREAM_SHORT_BUF 22050 +#define SAMPLES_PER_STREAM_BUF 44100 + +SndDoubleBackUPP gSndDoubleBackUPP = NULL; + +// sets the low-level sound object +void AudioStream::InitSystem(llsSystem *sndsys) +{ + int i; + OSErr err; + + if (AudioStream::m_ll_sndsys) + return; + + AudioStream::m_ll_sndsys = sndsys; + +// reserve sound slots for streams, they should never get replaced, and always should be played. + for (i = 0; i < STRM_LIMIT; i++) + { + AudioStream::m_streams[i] = NULL; + } + if(gSndDoubleBackUPP == NULL) + gSndDoubleBackUPP = NewSndDoubleBackProc(MyDoubleBackProc); +} + + +// shutdsown +void AudioStream::Shutdown() +{ + if (AudioStream::m_ll_sndsys) { + int i; + + // stop all streams. + for (i = 0; i < STRM_LIMIT; i++) + { + if (AudioStream::m_streams[i]) { + AudioStream::m_streams[i]->Close(); + AudioStream::m_streams[i] = NULL; + } + } + + AudioStream::m_ll_sndsys = NULL; + } +} + + +// allocates a stream slot for a stream +bool AudioStream::ActivateStream(AudioStream *stream) +{ + int i; + + for (i = 0; i < STRM_LIMIT; i++) + { + if (AudioStream::m_streams[i] == NULL) { + AudioStream::m_streams[i] = stream; + stream->m_streamindex = i; + return true; + } + } + mprintf((1, "STRMAUD: AudioStream queue filled!\n")); + return false; +} + + +void AudioStream::DeactivateStream(AudioStream *stream) +{ + if (stream->m_streamindex > -1) { + mprintf((1, "Deactivateing: STRM[%d]\n", stream->m_streamindex)); + AudioStream::m_streams[stream->m_streamindex] = NULL; + stream->m_streamindex = -1; + } +} + +// called by application to allow stream playback +void AudioStream::Frame() +{ + int t; + float time = timer_GetTime(); + + for(t=0; t < STRM_LIMIT; t++) + { + if (AudioStream::m_streams[t]) { + AudioStream *strm = AudioStream::m_streams[t]; + + if (strm->m_laststate != strm->m_state) { + mprintf((1, "frame:STRM[%d]: NEW m_state=%d\n", strm->m_curid, strm->m_state)); + } + + if (strm->m_state == STRM_PLAYING ) { +// mprintf((1, "frame:STRM[%d]: Playing\n", strm->m_curid)); + strm->m_measure_timer += (time - strm->m_last_frametime); + if (strm->m_measure_timer >= strm->m_measure_time) { + strm->m_curmeasure++; + } + + strm->m_last_frametime = time; + + strm->UpdateData(); + } + +// else if (strm->m_state == STRM_STOPPED) { +// if (CHECK_FLAG(strm->m_flags,STRM_OPNF_ONETIME)) +// strm->Close(); +// } + + else if (strm->m_state == STRM_STOPPING) { + if (CHECK_FLAG((strm->doubleHeader.dbhBufferPtr[0])->dbFlags, dbLastBuffer) && !strm->IsPlaying()) { + mprintf((1, "frame STRM[%d]: chan 0x%X StopING! Last Buffer read!!\n", strm->m_curid, strm->strm_channel)); + strm->Stop(); + if (CHECK_FLAG(strm->m_flags,STRM_OPNF_ONETIME)) { + mprintf((1, "Frame: STRM[%d] stoped in onetime so closing\n", strm->m_curid)); + strm->Close(); + } + strm->m_readahead = false; + } + + } + else if (strm->m_state == STRM_INVALID) { + mprintf((1, "STRM[%d]: INVALID!!\n", strm->m_curid)); + strm->DeactivateStream(strm); + } + else if (strm->m_start_on_frame) { + mprintf((1, "frame: STRM[%d]: start on frame playstream\n", strm->m_curid)); + strm->m_llshandle = strm->PlayStream(&strm->m_playinfo); + + if(strm->m_llshandle > -1) { + mprintf((1, "frame: STRM[%d]:handle %d\n", strm->m_curid, strm->m_llshandle)); + // for one buffer samples, prepare to stop now so that the one and only one buffer gets called. + // looped one buffer samples dont do this though... + if (strm->m_nbufs == 1 && !strm->m_start_on_frame_looped) { + strm->m_state = STRM_STOPPING; + mprintf((1, "frame: ONLY ONE BUFFERS WORTH nbufs == 1 & !looped\n")); + } + else { + strm->m_state = STRM_PLAYING; + mprintf((1, "frame: STRM[%d]: playing\n", strm->m_curid)); + } + } + + strm->m_start_on_frame = false; + strm->m_start_on_frame_looped = false; + } + else { +// mprintf((1, "frame: STRM[%d]: NOT DOING ANYTHING WHOOPS!!!\n", strm->m_curid)); + } + + strm->m_laststate = strm->m_state; + } + } +} + +// called to pause all streams. +void AudioStream::PauseAll() +{ + int t; + for (t=0; t < STRM_LIMIT; t++) + { + if (AudioStream::m_streams[t]) { + AudioStream::m_streams[t]->Pause(); + } + } +} + + +void AudioStream::ResumeAll() +{ + int t; + for (t=0; t < STRM_LIMIT; t++) + { + if (AudioStream::m_streams[t]) { + AudioStream::m_streams[t]->Resume(); + } + } +} + + + + +// AudioStream object Interface +// allows dynamic playing of streams + +AudioStream::AudioStream() +{ + m_decoder = NULL; // audio decomp object + m_llshandle = -1; // snd system handle + m_sbufidx = 0; // stream position markers for synching. + m_fbufidx = 0; + m_flags = 0; + m_state = STRM_INVALID; + m_streamindex = -1; + m_curmeasure = -1; + m_stopnextmeasure = false; + m_stopflag = NULL; + m_bytesleft = 0; + m_readahead_finished_loop = false; + m_playcount = 0; + m_curid = -1; + m_nbufs = 0; + m_start_on_frame = false; + + strm_channel = NULL; + OSErr err; + err = SndNewChannel(&strm_channel, sampledSynth, initStereo+initNoInterp, NULL); + if(err) mprintf((1, "unable to open stream channel\n")); +} + + +AudioStream::~AudioStream() +{ + AudioStream::Close(); + if (strm_channel == 0) + return; + SndDisposeChannel(strm_channel, true); +// mprintf((0, "distroying: STRM[%d]:\n", this->m_curid)); +} + + +// sets flags for playback (STRM_OPNF_XXX) +void AudioStream::SetFlags(int flags) +{ + m_flags = flags; +} + + +void AudioStream::SetLoopCount(int loop_count) +{ +// if loop_count = 0, then m_loopcount= -1, infinite; +// if loop_count = 1, then m_loopcount=0, no looping, etc. + m_loopcount = loop_count-1; + mprintf((1, "SetLoopCount: %d\n", m_loopcount)); +} + + +int AudioStream::GetLoopCount() const +{ +// if loop_count = 0, then m_loopcount= -1, infinite; +// if loop_count = 1, then m_loopcount=0, no looping, etc. + return m_loopcount+1; +} + + +void AudioStream::SetVolume(float vol) +{ + m_volume = vol; + SndCommand sndCommand; + OSErr err; + + if (m_ll_sndsys) { + if (strm_channel == 0) + return; + + short one_side = vol*0x0080; + long both_side = (one_side << 16) | one_side; + + sndCommand.cmd = volumeCmd; + sndCommand.param1 = 0; + sndCommand.param2 = both_side; + err = SndDoImmediate(strm_channel, &sndCommand); + if(err) mprintf((0,"stream channel is corrupt: volume\n")); + } +} + + +float AudioStream::GetVolume() +{ + return m_volume; +} + +// flags specify what type of stream it is. +bool AudioStream::Open(const char *filename,int open_flags) +{ +// don't open a stream that's already open, or bogus filename + if (m_state != STRM_INVALID) { + AudioStream::Close(); + // Int3(); + // return false; + } + + if (m_archive.Opened() || !filename) + return false; + + if (!m_ll_sndsys) + return false; + + m_state = STRM_INVALID; + m_streamindex = -1; + m_flags = open_flags; + m_curmeasure = 0; +// m_volume = 1.0f; + m_bufsize = SAMPLES_PER_STREAM_BUF*4; + m_measure_timer = 0.0f; + m_measure_time = 0.0f; + m_last_frametime = 0.0f; + m_readahead_finished_loop = false; + m_curid = -1; + m_nbufs = 0; + m_start_on_frame = m_start_on_frame_looped = false; + + mprintf((0, "Open new stream %s flags %d sof %d\n", filename, open_flags, m_start_on_frame)); +// open up stream file + if (m_archive.Open(filename)) { + int i; + int nbufs; + + ASSERT(m_archive.StreamType() == OSF_DIGITAL_STRM); + + m_thisid++; + m_curid = m_thisid; + + for (i=0;im_curid, m_start_on_frame)); + m_state = STRM_STOPPED; +// m_state = STRM_PENDING; + + return true; + } + + return false; +} + + +// deallocates all stream buffers and decoder. +void AudioStream::Close() +{ + int i; + + if (m_archive.Opened()) { + mprintf((0, "Close: STRM[%d]: \n", this->m_curid)); + // stop the stream, close the archive, close the decoder. + AudioStream::Stop(); + m_archive.Close(); + m_loopmutex.Destroy(); + m_curid = -1; + + // free streaming buffers and decoder if we need to. + for (i = 0; i < STRM_BUFCOUNT; i++) + { + if (m_buffer[i].data) { + mem_free(m_buffer[i].data); + m_buffer[i].data = NULL; + m_buffer[i].nbytes = 0; + m_buffer[i].flags = 0; + m_buffer[i].id = -1; + } + } + + if (m_decoder) { + AudioDecoder_Close(m_decoder); + m_decoder = NULL; + } + } + + if(doubleHeader.dbhBufferPtr[0]) { + mem_free(doubleHeader.dbhBufferPtr[0]); + doubleHeader.dbhBufferPtr[0] = NULL; + } + if(doubleHeader.dbhBufferPtr[1]) { + mem_free(doubleHeader.dbhBufferPtr[1]); + doubleHeader.dbhBufferPtr[1] = NULL; + } + + m_playcount = 0; + m_nbufs = 0; + m_state = STRM_INVALID; +} + + +// has stream finished with its readahead of current loop? +bool AudioStream::ReadAheadFinishedLoop() +{ + if (m_readahead_finished_loop) { + m_readahead_finished_loop = false; + return true; + } + + return false; +} + + +// are we still reading from disk? +bool AudioStream::ReadAhead() +{ + return m_readahead; +} + + +bool AudioStream::IsReady() +{ + return true; + if (m_state == STRM_STOPPED) { + if (!m_readahead || m_fbufidx == (STRM_BUFCOUNT-1)) { + return true; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////////// + +bool AudioStream::ReopenDigitalStream(ubyte fbufidx, int nbufs) +{ + const tOSFDigiHdr *digihdr = (const tOSFDigiHdr *)m_archive.StreamHeader(); + int bytesize, granularity; + long sample_count; + unsigned channels; + + m_bytesleft = m_archive.StreamLength(); + m_readahead = false; + mprintf((1, "reopen: %d total length %d\n", m_curid, m_bytesleft)); +// free current decoder. + if (m_decoder) { + AudioDecoder_Close(m_decoder); + m_decoder = NULL; + } + +// instatiate decompression facility or use raw source data + if(m_archive.StreamComp() == OSF_DIGIACM_STRM) { + unsigned sample_rate; + + m_decoder = Create_AudioDecoder(ADecodeFileRead,this,&channels,&sample_rate,&sample_count); + if(!m_decoder) { + AudioDecoder_Close(m_decoder); + m_decoder = NULL; + return false; + } +// m_total_size = sample_count; + } + else { + return false; + } + +// convert bufsize to true bufsize (bufsize = samples per measure for now.) + switch(m_archive.StreamFormat()) + { + case SAF_8BIT_M: granularity = 1; break; + case SAF_8BIT_S: granularity = 2; bytesize = digihdr->measure << 1; break; + case SAF_16BIT_M: granularity = 2; bytesize = digihdr->measure << 1; break; + case SAF_16BIT_S: granularity = 4; bytesize = digihdr->measure << 2; break; + } + +// bufsize is bytes per measure. now scale according to memory requirements. +// clear out buffer list + if (channels == 0 || channels > 2) { + // weird, faulty osf + mprintf((0, "STRM[%d]: Illegal OSF (no channels?): %d.\n",m_curid, channels)); + return false; + } + + long bytes_per_buf = (SAMPLES_PER_STREAM_BUF*granularity); + long filelen = (sample_count/channels)*granularity; + int nbuffers = filelen/bytes_per_buf; + if (nbuffers >= 0 && nbuffers <=1) { + if (filelen > 0) { + bytes_per_buf = (SAMPLES_PER_STREAM_SHORT_BUF*granularity); + nbuffers = filelen/bytes_per_buf; + if (nbuffers == 0) nbuffers=1; + } + else { + mprintf((0, "STRM[%d]: Bad stream length %d\n", m_curid, filelen)); + return false; + } + } + + mprintf((1, "Reopen: nbuffers %d of %d each\n", nbuffers, bytes_per_buf)); + + m_playbytestotal = filelen; + m_playbytesleft = filelen; + m_fbufidx = fbufidx; + m_sbufidx = 0; + +//Get the file size, and figure out an ideal buffer size +//so our looping sounds don't have an odd silence in the middle + m_measure_time = (digihdr->measure/22050.0f); + m_bufsize = (filelen/nbuffers); + +// figure out if we still need to be 4-byte aligned. + granularity = granularity >> 1; + if (granularity) { + if (m_bufsize % 4) { + m_bufsize = m_bufsize + (4 - (m_bufsize % 4)); + if (m_bufsize % 4) { + Int3(); + } + } + } + mprintf((1,"reopen: %d Using buffer size of %d\n",m_curid, m_bufsize)); + +// mark stream as not done. + m_readahead = true; + m_readahead_finished_loop = false; + + if (!nbufs) { + // dont set m_readahead, because readahead only told this function whether it was going to do + // the read of buffers now, or let UpdateData do it later. + return true; + } + +// fill buffers. + nbufs--; + + while (!CHECK_FLAG(m_buffer[m_fbufidx].flags, STRM_BUFF_USED) && nbufs >= 0 && m_readahead) + { + // if our stream's current id does not match the streaming buffer's id, then we need to reallocate + // the stream buffer with the new memory size + if (m_buffer[m_fbufidx].id != (int)m_curid) { + if (m_buffer[m_fbufidx].data) { + mem_free(m_buffer[m_fbufidx].data); + } + m_buffer[m_fbufidx].data = (ubyte *)mem_malloc(m_bufsize); + } + m_buffer[m_fbufidx].nbytes = AudioStream::ReadFileData(m_fbufidx, m_bufsize); + m_buffer[m_fbufidx].flags = 0; + m_buffer[m_fbufidx].flags |= STRM_BUFF_USED; + m_buffer[m_fbufidx].id = m_thisid; + + m_playbytesleft -= m_buffer[m_fbufidx].nbytes; + + mprintf((1, "reopen: STRM[%d] m_buffer[%d] 0x%X bytes readin 0x%x left %X\n", this->m_curid, m_fbufidx, m_bufsize, m_playbytesleft, m_buffer[m_fbufidx].flags)); + + if (m_playbytesleft <= (m_bufsize/4)) { + mprintf((0, "STRM[%d]: ", m_curid)); + if (m_buffer[m_fbufidx].nbytes == 0) { + memset(m_buffer[m_fbufidx].data, 0, 4); + m_buffer[m_fbufidx].nbytes = 4; + mprintf((0, "making empty buffer and")); + } + mprintf((0, "TERMINAL buffer.\n")); + m_buffer[m_fbufidx].flags |= STRM_BUFF_TERMINAL; + m_readahead = false; + m_readahead_finished_loop = true; + } + m_nbufs++; + + m_fbufidx = (m_fbufidx+1) % STRM_BUFCOUNT; +//T2 m_fbufidx = (m_fbufidx)?0:1; + } + +// readjust file buffer index down so that it matches the CURRENT file index, not the next one. + if (m_fbufidx == 0) m_fbufidx = STRM_BUFCOUNT-1; + else m_fbufidx--; + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// plays a stream +bool AudioStream::Play(bool start_on_frame) +{ +// call low level stream manager. - samir + int sflag = SIF_STREAMING_16_M,i; + bool looped = false; + + mprintf((1, "Play: STRM[%d]: called w sof %d\n", this->m_curid, start_on_frame)); + + if (m_state == STRM_INVALID) { + return false; + } + +// play stream if there's room to. + if (!ActivateStream(this)) + return false; + + switch(m_archive.StreamFormat()) + { + case SAF_8BIT_M: sflag = SIF_STREAMING_8_M; break; + case SAF_8BIT_S: sflag = SIF_STREAMING_8_S; break; + case SAF_16BIT_M: sflag = SIF_STREAMING_16_M; break; + case SAF_16BIT_S: sflag = SIF_STREAMING_16_S; break; + default: Int3(); return false; + } + +// reset soft stopping. + m_stopnextmeasure = false; + m_curmeasure = 0; + m_stopflag = NULL; + + if (m_state != STRM_STOPPED) { + mprintf((1, "Play: STRM[%d]: Not Opened stop???\n", this->m_curid)); + AudioStream::Stop(); + } + if (m_playcount > 0) { + mprintf((1, "Play: STRM[%d]: m_playcount > 0 resetting!!\n", this->m_curid)); + AudioStream::Reset(); + } + + m_playcount++; + + m_measure_timer = 0.0f; + m_last_frametime = timer_GetTime(); + +// check for terminal and if loopcount != 0 then specify terminal as looping + if (m_loopcount != 0) { + for (i = 0; i < STRM_BUFCOUNT; i++) + { + if (CHECK_FLAG(m_buffer[i].flags, STRM_BUFF_USED)) { + if (CHECK_FLAG(m_buffer[i].flags, STRM_BUFF_TERMINAL)) { + if (m_loopcount == -1) { + m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; + looped = true; + AudioStream::Reset(); + } + else if (m_loopcount > 0) { + m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; + looped = true; + AudioStream::Reset(); + m_loopcount--; + } + m_readahead_finished_loop = true; + } + } + } + } + +// invoke low level sound system. + memset(&m_playinfo, 0, sizeof(m_playinfo)); +#if 0 + m_playinfo.samples_per_22khz_sample = 1.0; + m_playinfo.left_volume = m_playinfo.right_volume = m_volume; +#endif + m_playinfo.sample_skip_interval = 0; + m_playinfo.m_stream_cback = AudioStreamCB; + m_playinfo.m_stream_data = m_buffer[m_sbufidx].data; + m_playinfo.m_stream_format = sflag; + m_playinfo.m_stream_size = m_buffer[m_sbufidx].nbytes; + m_playinfo.m_stream_handle = m_streamindex; + m_playinfo.m_stream_bufsize = m_bufsize; + m_playinfo.user_data = this; + m_playinfo.priority = SND_PRIORITY_CRITICAL; // this stream must play. + + m_start_on_frame = start_on_frame; + m_start_on_frame_looped = looped; + + mprintf((1, "Play: STRM[%d]: PlayStream sof %d\n", this->m_curid, m_start_on_frame)); + if (!m_start_on_frame) { + mprintf((1, "play: size %d bufsize %d\n", m_playinfo.m_stream_size, m_playinfo.m_stream_bufsize)); + m_llshandle = AudioStream::PlayStream(&m_playinfo); + mprintf((1, "Play: STRM[%d]: PlayStream handle %d\n", this->m_curid, m_llshandle)); + + if(m_llshandle > -1) { + // for one buffer samples, prepare to stop now so that the one and only one buffer gets called. + // looped one buffer samples dont do this though... + if (m_nbufs == 1 && !looped) { + m_state = STRM_STOPPING; + mprintf((1, " not looped so stoping IMEDIATLY\n")); + } + else { + m_state = STRM_PLAYING; + mprintf((1, "looped so start playing\n")); + } + return true; + } + } + return true; +} + + +bool AudioStream::IsPlaying() +{ + SCStatus channel_status; + SndChannelStatus(strm_channel, sizeof(SCStatus), &channel_status); + + return (channel_status.scChannelBusy); +} + +// pauses a stream +void AudioStream::Pause() +{ + if (m_state != STRM_PLAYING) { + return; + } + + m_state = STRM_PAUSED; + + SndCommand sndCommand; + OSErr err; + + sndCommand.cmd = pauseCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(strm_channel, &sndCommand); + if(err) mprintf((1,"stream channelis corrupt: pause\n")); + + mprintf((0, "Paused stream\n")); +} + + +// pauses a stream +void AudioStream::Resume() +{ + if (m_state != STRM_PAUSED) { + return; + } + + SndCommand sndCommand; + OSErr err; + sndCommand.cmd = resumeCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(strm_channel, &sndCommand); + if(err) mprintf((1,"stream channelis corrupt: resume\n")); + + int sflag = SIF_STREAMING_16_M; + switch(m_archive.StreamFormat()) + { + case SAF_8BIT_M: sflag = SIF_STREAMING_8_M; break; + case SAF_8BIT_S: sflag = SIF_STREAMING_8_S; break; + case SAF_16BIT_M: sflag = SIF_STREAMING_16_M; break; + case SAF_16BIT_S: sflag = SIF_STREAMING_16_S; break; + } + + memset(&m_playinfo, 0, sizeof(m_playinfo)); +#if 0 + m_playinfo.samples_per_22khz_sample = 1.0; + m_playinfo.left_volume = m_playinfo.right_volume = m_volume; + #endif + m_playinfo.sample_skip_interval = 0; + m_playinfo.m_stream_cback = AudioStreamCB; + m_playinfo.m_stream_data = m_buffer[m_sbufidx].data; + m_playinfo.m_stream_format = sflag; + m_playinfo.m_stream_size = m_buffer[m_sbufidx].nbytes; + m_playinfo.m_stream_handle = m_streamindex; + m_playinfo.m_stream_bufsize = m_bufsize; + m_playinfo.user_data = this; + m_playinfo.priority = SND_PRIORITY_CRITICAL; // this stream must play. + + mprintf((1, "play: size %d bufsize %d\n", m_playinfo.m_stream_size, m_playinfo.m_stream_bufsize)); + + for (int i = 0; i <= 1; ++i) { + doubleHeader.dbhBufferPtr[i]->dbUserInfo [0] = (long) &m_playinfo; + AudioStream::MyDoubleBackStart (strm_channel, doubleHeader.dbhBufferPtr[i]); // initialize the buffers + } + err = SndPlayDoubleBuffer (strm_channel, &doubleHeader); + + m_state = STRM_PLAYING; + mprintf((1, "Resumed stream (%d)\n", m_llshandle)); +} + + +// causes a rewind to start of stream, if on_measure is true, stop occurs when measure ends +void AudioStream::Stop(bool on_measure, int *stop_flag) +{ + SndCommand sndCommand; + OSErr err; + + m_start_on_frame = false; + + if (strm_channel == 0) + return; + if (m_state == STRM_INVALID) { + if (stop_flag) { + *stop_flag = true; + } + return; + } + + if (m_state != STRM_STOPPED) { + if (!on_measure) { + if (m_state == STRM_PLAYING || m_state == STRM_PAUSED || m_state == STRM_STOPPING) { + + sndCommand.cmd = flushCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(strm_channel, &sndCommand); + + sndCommand.cmd = quietCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(strm_channel, &sndCommand); + + m_state = STRM_STOPPED; + mprintf((1, "Stop: STRM[%d]\n", m_curid)); + m_readahead = m_readahead_finished_loop = true; + DeactivateStream(this); + m_llshandle = -1; + m_curmeasure = 0; + } + } + else { + // handle soft stopping. + m_stopflag = stop_flag; + if (m_stopflag) { + *m_stopflag = false; + } + m_stopnextmeasure = on_measure; + } + } + else { + DeactivateStream(this); + m_llshandle = -1; + m_curmeasure = 0; + if (m_stopflag) { + *m_stopflag = true; + } + } +} + + + +// performs a clean stop and play when switching to next stream +void AudioStream::Reset() +{ + m_archive.Rewind(); + m_bytesleft = m_archive.StreamLength(); + m_curmeasure = 0; + m_readahead = true; + m_playbytesleft = m_playbytestotal; + + if (m_decoder) { + unsigned channels; + unsigned sample_rate; + long sample_count; + + AudioDecoder_Close(m_decoder); + m_decoder = Create_AudioDecoder(ADecodeFileRead,this,&channels,&sample_rate,&sample_count); + } +} + + +////////////////////////////////////////////////////////////////////////////// +// invoked by AudioStreamCB. +void *AudioStream::StreamCallback(int *size) +{ + void *data = NULL; + + ubyte nextbuffer = (m_sbufidx+1) % STRM_BUFCOUNT; +// mprintf((0, "callback: m_state %d flag %d %d\n", m_state,m_buffer[0].flags, m_buffer[1].flags)); + + if(m_state == STRM_PAUSED) { +// mprintf((0, "callback: %d paused\n", m_curid)); + *size = 1; + m_buffer[0].flags = STRM_BUFF_USED; + m_buffer[1].flags = STRM_BUFF_USED; + m_buffer[0].nbytes = m_bufsize; + m_buffer[1].nbytes = m_bufsize; + return NULL; + } + +// we're not done yet. +//adjust sound buffer to the next buffer + if (m_state == STRM_STOPPED || m_state == STRM_STOPPING) { + // mark played buffer as unused. + m_buffer[m_sbufidx].flags =0; + m_buffer[m_sbufidx].nbytes = 0; + *size = 0; + return NULL; + } + + if (!CHECK_FLAG(m_buffer[nextbuffer].flags, STRM_BUFF_USED)) { +// mprintf((0, "**> stream %d (nextbuffer %d) (flags %x) unused stoping!! \n", m_sbufidx, nextbuffer, m_buffer[nextbuffer].flags)); + // mprintf((0, "STRM[%d]: Playing onetime buffer?\n",m_curid)); + + m_state = STRM_STOPPED; + *size = 0; + return NULL; + } + +// mark played buffer as unused. + m_buffer[m_sbufidx].flags =0; + m_buffer[m_sbufidx].nbytes = 0; + + m_sbufidx = nextbuffer; + + +// increment measure count if we've entered a new measure. +// if stop measure has been flagged, don't return new valid data. +// make sure and lock mutex, because we're modifying these contents when the caller could +// do the same. + +// if we reached the last buffer in an opened stream, and we're not switching prepare to stop +// if we are switching, release the switch lock and continue +// otherwise just continue. + data = (void *)m_buffer[m_sbufidx].data; + *size = m_buffer[m_sbufidx].nbytes; +// mprintf((0, "cb size %x\n", m_buffer[m_sbufidx].nbytes)); +// mprintf((0, "callback: m_buffer[%d] nbytes 0x%x data 0x%X\n", m_sbufidx, m_buffer[m_sbufidx].nbytes, m_buffer[m_sbufidx].data)); + + if (CHECK_FLAG(m_buffer[m_sbufidx].flags, STRM_BUFF_TERMINAL)) { +// mprintf((0, "STRMAUD: reached end of stream (%d bytes).\n", (*size))); + if (!CHECK_FLAG(m_buffer[m_sbufidx].flags, STRM_BUFF_LOOPEND)) { + m_state = STRM_STOPPING; +// mprintf((0, "callback: flags == STRM_BUFF_TERMINAL stoping NOT loopend\n")); + } + if (m_stopnextmeasure) { + m_state = STRM_STOPPING; +// mprintf((0, "callback: flags == STRM_BUFF_TERMINAL stoping stopnexmesure\n")); + } + } + + if ((*size) == 0) { + // mprintf((0, "STRM[%d]: Used buffer has 0 bytes!\n", m_curid)); + m_state = STRM_STOPPING; + data = NULL; + } + + return data; +} + + + +// reads in decompressed raw data. +int AudioStream::ReadFileData(int buf, int len) +{ + if(!m_archive.Opened()) + return 0; + + if(m_decoder) { + //We have a compressed stream + return AudioDecoder_Read(m_decoder,m_buffer[buf].data, len); + } + else { + //We have a non-compressed stream + if(len > m_bytesleft){ + len = m_bytesleft; + m_bytesleft = 0; + } + else + m_bytesleft-=len; + + return m_archive.Read(m_buffer[buf].data,len); + } +} + + +// reads in decompressed raw data. +int AudioStream::ReadFileDirect(char * buf, int len) +{ + if(!m_archive.Opened()) + return 0; + + if(m_decoder) { + //We have a compressed stream + return AudioDecoder_Read(m_decoder,buf, len); + } + else { + //We have a non-compressed stream + if(len > m_bytesleft){ + len = m_bytesleft; + m_bytesleft = 0; + } + else + m_bytesleft-=len; + + return m_archive.Read((unsigned char *)buf,len); + } +} + + +// TURN OFF OPTIMIZATIONS HERE. Placement of instructions is VERY IMPORTANT here. synched with StreamCallback, +// which also has optimizations off. +// updates file buffers +void AudioStream::UpdateData() +{ + + int nextbuffer = ((m_fbufidx+1) % STRM_BUFCOUNT); + +// mprintf((1, "update: nextbuf %d flags %d\n", nextbuffer, m_buffer[nextbuffer].flags)); +// mprintf((0, "UD proc_sw %d m_state %d flags %d\n", proc_sw, m_state, m_buffer[nextbuffer].flags)); +// check if are on a measure boundary for current stream. if so, then check if we have a next request pending + if (CHECK_FLAG(m_buffer[nextbuffer].flags, STRM_BUFF_USED)) { + return; + } +// quit out if we can. + if(nextbuffer == m_sbufidx) { + return; + } + + +// do read! +// READ DATA INTO BUFFER. UPDATE BYTES LEFT PER MEASURE, ETC. + if (m_readahead) { + // ok update the next buffer with data + m_fbufidx = nextbuffer; + // mprintf((0,"%c",m_fbufidx+'a')); + + // if our stream's current id does not match the streaming buffer's id, then we need to reallocate + // the stream buffer with the new memory size + if (m_buffer[m_fbufidx].id != (int)m_curid) { + if (m_buffer[m_fbufidx].data) { + mem_free(m_buffer[m_fbufidx].data); + } + m_buffer[m_fbufidx].data = (ubyte *)mem_malloc(m_bufsize); + } + + m_buffer[m_fbufidx].nbytes = AudioStream::ReadFileData(m_fbufidx, m_bufsize); + m_buffer[m_fbufidx].flags = 0; + m_buffer[m_fbufidx].flags |= STRM_BUFF_USED; + m_buffer[m_fbufidx].id = (int)m_curid; + + m_playbytesleft -= m_buffer[m_fbufidx].nbytes; + + mprintf((1, "update: %d m_buffer[%d] 0x%X bytes readin 0x%x left flag %d\n", m_curid, m_fbufidx, m_bufsize, m_playbytesleft, m_buffer[m_fbufidx].flags)); + + if (m_playbytesleft <= (m_bufsize/4)) { + mprintf((1, "update: %d last buffer has %d left\n", m_curid, m_playbytesleft)); + if (m_buffer[m_fbufidx].nbytes == 0) { + memset(m_buffer[m_fbufidx].data, 0, 4); + m_buffer[m_fbufidx].nbytes = 4; + mprintf((1, "update: %d PAD last buffer with %d\n", m_curid, m_buffer[m_fbufidx].nbytes)); + } + m_buffer[m_fbufidx].flags |= STRM_BUFF_TERMINAL; + m_readahead = false; + m_readahead_finished_loop = true; + } + + // looping? + if (CHECK_FLAG(m_buffer[m_fbufidx].flags, STRM_BUFF_TERMINAL)) { + mprintf((1, "update: loopcount %d\n", m_loopcount)); + if (m_loopcount == -1) { + m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; + AudioStream::Reset(); + } + else if (m_loopcount > 0) { + m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; + AudioStream::Reset(); + m_loopcount--; + } + m_readahead_finished_loop = true; + } + } +} + + +int AudioStream::PlayStream(play_information *play_info) +{ + ASSERT(play_info != NULL); + SndDoubleBufferPtr doubleBuffer; + + OSStatus err; + + switch (play_info->m_stream_format) { + case SIF_STREAMING_8_M: + doubleHeader.dbhSampleSize = 8; + doubleHeader.dbhNumChannels = 1; + break; + case SIF_STREAMING_8_S: + doubleHeader.dbhSampleSize = 8; + doubleHeader.dbhNumChannels = 2; + break; + case SIF_STREAMING_16_M: + doubleHeader.dbhSampleSize = 16; + doubleHeader.dbhNumChannels = 1; + break; + case SIF_STREAMING_16_S: + doubleHeader.dbhSampleSize = 16; + doubleHeader.dbhNumChannels = 2; + break; + } + doubleHeader.dbhSampleRate = rate22050hz; + doubleHeader.dbhCompressionID = notCompressed; + doubleHeader.dbhPacketSize = 0; + + doubleHeader.dbhDoubleBack = gSndDoubleBackUPP; + + for (int i = 0; i <= 1; ++i) { + doubleBuffer = (SndDoubleBufferPtr) mem_malloc (sizeof(SndDoubleBuffer) + play_info->m_stream_bufsize); + if (doubleBuffer == nil) + return 0; +// Debugger(); + + doubleBuffer->dbNumFrames = 0; + doubleBuffer->dbFlags = 0; + doubleBuffer->dbUserInfo [0] = (long) play_info; + + mprintf((1, "PlayStream bufsize %d\n", play_info->m_stream_bufsize)); + AudioStream::MyDoubleBackStart (strm_channel, doubleBuffer); // initialize the buffers + + doubleHeader.dbhBufferPtr[i] = doubleBuffer; + } + err = SndPlayDoubleBuffer (strm_channel, &doubleHeader); + mprintf((1, "PlayStream:STRM[%d]: channel 0x%X SndPlayDoubleBuffer 0x%X & 0x%X\n", this->m_curid, strm_channel, + doubleHeader.dbhBufferPtr[0], doubleHeader.dbhBufferPtr[1])); + + strm_channel->userInfo = play_info->m_stream_handle; + + return (play_info->m_stream_handle); +} + +void * MyDoubleBackProc (SndChannelPtr channel, SndDoubleBufferPtr dBuffer) +{ + play_information *play_info; + + int bytesFromBuffer; + Ptr dataPtr; + + play_info = (play_information *)dBuffer->dbUserInfo[0]; + + if((dBuffer->dbFlags) & dbLastBuffer) + return NULL; + + dataPtr = (Ptr)play_info->m_stream_cback(play_info->user_data, play_info->m_stream_handle, &bytesFromBuffer); + + dBuffer->dbFlags = (dBuffer->dbFlags) | dbBufferReady; + if(dataPtr) { + + memcpy (&dBuffer->dbSoundData[ 0], dataPtr, bytesFromBuffer); +// BlockMove (dataPtr, &dBuffer->dbSoundData[0], bytesFromBuffer); + dBuffer->dbNumFrames = bytesFromBuffer; + if(play_info->m_stream_format >= SIF_STREAMING_16_M) + dBuffer->dbNumFrames = dBuffer->dbNumFrames>>1; + if(play_info->m_stream_format == SIF_STREAMING_16_S) + dBuffer->dbNumFrames = dBuffer->dbNumFrames>>1; + + } else if (bytesFromBuffer) { + dBuffer->dbFlags = 0; + dBuffer->dbNumFrames = 0; + } else { + dBuffer->dbFlags = (dBuffer->dbFlags) | dbLastBuffer; + dBuffer->dbNumFrames = 0; + } + return dataPtr; +} + +void * AudioStream::MyDoubleBackStart (SndChannelPtr channel, SndDoubleBufferPtr dBuffer) +{ + int bytesToCopy; + + play_information *play_info; + play_info = (play_information *)dBuffer->dbUserInfo[0]; + + mprintf((1, "MyDoubleBackStart: bufsize %d\n", play_info->m_stream_bufsize)); + bytesToCopy = play_info->m_stream_bufsize/2; //split the buffer in half + + memcpy (&dBuffer->dbSoundData[ 0], play_info->m_stream_data, bytesToCopy); +// BlockMove (play_info->m_stream_data, &dBuffer->dbSoundData[ 0], bytesToCopy); + + play_info->m_stream_data = (void *)((int)(play_info->m_stream_data) + bytesToCopy); + + dBuffer->dbNumFrames = bytesToCopy; + + if(play_info->m_stream_format >= SIF_STREAMING_16_M) + dBuffer->dbNumFrames = dBuffer->dbNumFrames/2; + if(play_info->m_stream_format == SIF_STREAMING_16_S) + dBuffer->dbNumFrames = dBuffer->dbNumFrames/2; + + dBuffer->dbFlags = (dBuffer->dbFlags) | dbBufferReady; + + return NULL; +} + + +// Router for stream callbacks. +// Invoked by sound system +void *AudioStreamCB(void *user_data, int handle, int *size) +{ + AudioStream *stream = (AudioStream *)user_data; + ASSERT(stream); +// ASSERT(stream->m_streamindex == handle); + return stream->StreamCallback(size); +// return stream->NewStreamCallback(size); +} + + +static float Stream_volume = 1.0f; +static float Stream_master_volume = 1.0f; + +void StreamVolume(float master_volume) +{ + Stream_master_volume = master_volume; + User_audio_stream.SetVolume(Stream_volume * master_volume); +} + + +/////////////////////////////////////////////////////////////////////////////// +// decoder +unsigned ADecodeFileRead(void *data, void *buf, unsigned qty) +{ + AudioStream *stream = (AudioStream *)data; + int iqty = (int)qty; + + if (iqty>stream->m_bytesleft) { + iqty = stream->m_bytesleft; + stream->m_bytesleft = 0; + } + else + stream->m_bytesleft-=iqty; + + return stream->m_archive.Read((ubyte *)buf, iqty); + +} + +int voice_stream = -1; +// these functions are the 'simplified' stream interface from Jeff (most of this code is from Jeff) +int StreamPlay(const char *filename, float volume, int flags) +{ + int retval = -1; + try + { + flags = 0; + User_audio_stream.Close(); + User_audio_stream.Open(filename, STRM_OPNF_ONETIME); + + Stream_volume = volume; + User_audio_stream.SetVolume(Stream_volume * Stream_master_volume); + User_audio_stream.Play(true); // start this stream on the next frame. + + retval = User_audio_stream.m_streamindex; + voice_stream = retval; + }catch(...) + { + return -1; + } + return retval; +} + + +void StreamStop(int handle) +{ + if(handle == 0 && voice_stream > -1) { + if (AudioStream::m_streams[voice_stream]) + AudioStream::m_streams[voice_stream]->Stop(); + } + else if (handle > -1) { + if (AudioStream::m_streams[handle]) + AudioStream::m_streams[handle]->Stop(); + } + User_audio_stream.Close(); +} + + +int StreamGetSoundHandle(int handle) +{ + if (handle > -1) { + if (AudioStream::m_streams[handle]) + return AudioStream::m_streams[handle]->GetSoundHandle(); + else + return -1; + } + + return -1; +} diff --git a/mac/MACTASK.CPP b/mac/MACTASK.CPP new file mode 100644 index 000000000..1911ec679 --- /dev/null +++ b/mac/MACTASK.CPP @@ -0,0 +1,23 @@ +#include "DDAccess.h" +#include "TaskSystem.h" +#include "pserror.h" +osMutex::osMutex() +{ +} +osMutex::~osMutex() +{ + Destroy(); +} +bool osMutex::Create() +{ + return false; +} +void osMutex::Destroy() +{ +} +bool osMutex::Acquire(int timeout) +{ +} +void osMutex::Release() +{ +} \ No newline at end of file diff --git a/mac/MAC_GLIDE_DEBUG.H b/mac/MAC_GLIDE_DEBUG.H new file mode 100644 index 000000000..4cc7f8d88 --- /dev/null +++ b/mac/MAC_GLIDE_DEBUG.H @@ -0,0 +1,177 @@ +#include "MacHeadersD3.pch" +#include +#include "Macros.h" +#include +// Constants +#define MACINTOSH 1 +#define USE_GLIDE +#undef USE_OPENGL +#define NUM_MIP_LEVELS 5 +#define mprintf(args) Debug_ConsolePrintf args +#define STEALTH +#undef SOUND_ON +#define DAJ_DEBUG +//#pragma options align packed +#define MEM_USE_RTL 1 +#define and andVar +#define or orVar +#define _fstat fstat +#define _filelength filelength +#define __cdecl +#define _MAX_PATH 1024 +#define _MAX_FNAME 64 /* max. length of path component*/ +#define _MAX_EXT 64 /* max. length of extension component*/ +#define TRANSPARENT_COLOR 0 +// These are other error types defined in Visual C++. +// The values are made up and hopefullydon't conflict JCA +#define EACCES 102 +#define EDEADLOCK 104 +#define EMFILE 105 +#define ENOENT 106 +#define ENFILE 107 +#define ENOSPC 108 +#define ENOSYS 112 + +#if 0 +/////////////////////////// +// From Winsock.h +/////////////////////////// +/* + * Address families. + */ +#define AF_UNSPEC 0 /* unspecified */ +#define AF_UNIX 1 /* local to host (pipes, portals) */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#define AF_IMPLINK 3 /* arpanet imp addresses */ +#define AF_PUP 4 /* pup protocols: e.g. BSP */ +#define AF_CHAOS 5 /* mit CHAOS protocols */ +#define AF_IPX 6 /* IPX and SPX */ +#define AF_NS 6 /* XEROX NS protocols */ +#define AF_ISO 7 /* ISO protocols */ +#define AF_OSI AF_ISO /* OSI is ISO */ +#define AF_ECMA 8 /* european computer manufacturers */ +#define AF_DATAKIT 9 /* datakit protocols */ +#define AF_CCITT 10 /* CCITT protocols, X.25 etc */ +#define AF_SNA 11 /* IBM SNA */ +#define AF_DECnet 12 /* DECnet */ +#define AF_DLI 13 /* Direct data link interface */ +#define AF_LAT 14 /* LAT */ +#define AF_HYLINK 15 /* NSC Hyperchannel */ +#define AF_APPLETALK 16 /* AppleTalk */ +#define AF_NETBIOS 17 /* NetBios-style addresses */ +#define AF_VOICEVIEW 18 /* VoiceView */ +#define AF_FIREFOX 19 /* FireFox */ +#define AF_UNKNOWN1 20 /* Somebody is using this! */ +#define AF_BAN 21 /* Banyan */ +#define AF_MAX 22 +/* + * Types + */ +#define SOCK_STREAM 1 /* stream socket */ +#define SOCK_DGRAM 2 /* datagram socket */ +#define SOCK_RAW 3 /* raw-protocol interface */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequenced packet stream */ +/* + * Basic system type definitions, taken from the BSD file sys/types.h. + */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +/* + * The new type to be used in all + * instances which refer to sockets. + */ +typedef u_int SOCKET; +/* + * Socket address, internet style. + */ +struct sockaddr { + unsigned short sa_family; /* address family */ + char sa_data[14]; /* up to 14 bytes of direct address */ +}; +struct in_addr { + union { + struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { u_short s_w1,s_w2; } S_un_w; + u_long S_addr; + } S_un; +#define s_addr S_un.S_addr + /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 + /* host on imp */ +#define s_net S_un.S_un_b.s_b1 + /* network */ +#define s_imp S_un.S_un_w.s_w2 + /* imp */ +#define s_impno S_un.S_un_b.s_b4 + /* imp # */ +#define s_lh S_un.S_un_b.s_b3 + /* logical host */ +}; +struct sockaddr_in { + short sin_family; + u_short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; +#endif + +// Types +typedef unsigned char BOOL; +typedef void **HANDLE; +typedef Fixed fix; +typedef long long INT64; +typedef void *HGLOBAL; +// Function Definitions +#define _finite(a) finite(a) +#define strcmpi(a,b) stricmp(a,b) +#define strcmpni(a,b,c) strnicmp(a,b,c) +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define HeapSize(a,b,c) 0 +#define HeapDestroy(a) +int stricmp(const char *inX, const char *inY); +int strnicmp(const char *inX, const char *inY, int len); +char *strdup(const char *str); +void GlobalFree(void *); +void *GlobalAlloc(int flags,int size); +void *GlobalLock(HGLOBAL hMem); +void Sleep(int millis); +char *itoa(int value,char *string,int radix); +char *strupr(char *string); +inline int finite(double a) +{ + return(a != HUGE_VAL); +} + +#if 0 +/////////////////////////// +// From Winsock.h +/////////////////////////// +SOCKET accept (SOCKET s, struct sockaddr *addr, + int *addrlen); +int bind (SOCKET s, const struct sockaddr *addr, int namelen); +int closesocket (SOCKET s); +int connect (SOCKET s, const struct sockaddr *name, int namelen); +int ioctlsocket (SOCKET s, long cmd, u_long *argp); +int getpeername (SOCKET s, struct sockaddr *name, int * namelen); +int getsockname (SOCKET s, struct sockaddr *name, int * namelen); +int getsockopt (SOCKET s, int level, int optname, char * optval, int *optlen); +#define htonl(hostlong) (hostlong) +#define htons(hostshort) (hostshort) +unsigned long inet_addr (const char * cp); +char * inet_ntoa (struct in_addr in); +int listen (SOCKET s, int backlog); +u_long ntohl (u_long netlong); +u_short ntohs (u_short netshort); +int recv (SOCKET s, char * buf, int len, int flags); +int recvfrom (SOCKET s, char * buf, int len, int flags, struct sockaddr *from, int * fromlen); +//int select (int nfds, fd_set *readfds, fd_set *writefds, +// fd_set *exceptfds, const struct timeval *timeout); +int send (SOCKET s, const char * buf, int len, int flags); +int sendto (SOCKET s, const char * buf, int len, int flags, const struct sockaddr *to, int tolen); +int setsockopt (SOCKET s, int level, int optname, const char * optval, int optlen); +int shutdown (SOCKET s, int how); +SOCKET socket (int af, int type, int protocol); +#endif \ No newline at end of file diff --git a/mac/MAC_GLIDE_RELEASE.H b/mac/MAC_GLIDE_RELEASE.H new file mode 100644 index 000000000..331cd93ac --- /dev/null +++ b/mac/MAC_GLIDE_RELEASE.H @@ -0,0 +1,176 @@ +#include "MacHeadersD3.pch" +#include +#include "Macros.h" +#include +// Constants +#define MACINTOSH 1 +#define USE_GLIDE +#undef USE_OPENGL +#define NUM_MIP_LEVELS 5 +#define mprintf(args) +#define SOUND_ON +#undef STEALTH +#undef DAJ_DEBUG +//#pragma options align packed +#define MEM_USE_RTL 1 +#define and andVar +#define or orVar +#define _fstat fstat +#define _filelength filelength +#define __cdecl +#define _MAX_PATH 1024 +#define _MAX_FNAME 64 /* max. length of path component*/ +#define _MAX_EXT 64 /* max. length of extension component*/ +#define TRANSPARENT_COLOR 0 +// These are other error types defined in Visual C++. +// The values are made up and hopefullydon't conflict JCA +#define EACCES 102 +#define EDEADLOCK 104 +#define EMFILE 105 +#define ENOENT 106 +#define ENFILE 107 +#define ENOSPC 108 +#define ENOSYS 112 + +#if 0 +/////////////////////////// +// From Winsock.h +/////////////////////////// +/* + * Address families. + */ +#define AF_UNSPEC 0 /* unspecified */ +#define AF_UNIX 1 /* local to host (pipes, portals) */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#define AF_IMPLINK 3 /* arpanet imp addresses */ +#define AF_PUP 4 /* pup protocols: e.g. BSP */ +#define AF_CHAOS 5 /* mit CHAOS protocols */ +#define AF_IPX 6 /* IPX and SPX */ +#define AF_NS 6 /* XEROX NS protocols */ +#define AF_ISO 7 /* ISO protocols */ +#define AF_OSI AF_ISO /* OSI is ISO */ +#define AF_ECMA 8 /* european computer manufacturers */ +#define AF_DATAKIT 9 /* datakit protocols */ +#define AF_CCITT 10 /* CCITT protocols, X.25 etc */ +#define AF_SNA 11 /* IBM SNA */ +#define AF_DECnet 12 /* DECnet */ +#define AF_DLI 13 /* Direct data link interface */ +#define AF_LAT 14 /* LAT */ +#define AF_HYLINK 15 /* NSC Hyperchannel */ +#define AF_APPLETALK 16 /* AppleTalk */ +#define AF_NETBIOS 17 /* NetBios-style addresses */ +#define AF_VOICEVIEW 18 /* VoiceView */ +#define AF_FIREFOX 19 /* FireFox */ +#define AF_UNKNOWN1 20 /* Somebody is using this! */ +#define AF_BAN 21 /* Banyan */ +#define AF_MAX 22 +/* + * Types + */ +#define SOCK_STREAM 1 /* stream socket */ +#define SOCK_DGRAM 2 /* datagram socket */ +#define SOCK_RAW 3 /* raw-protocol interface */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequenced packet stream */ +/* + * Basic system type definitions, taken from the BSD file sys/types.h. + */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +/* + * The new type to be used in all + * instances which refer to sockets. + */ +typedef u_int SOCKET; +/* + * Socket address, internet style. + */ +struct sockaddr { + unsigned short sa_family; /* address family */ + char sa_data[14]; /* up to 14 bytes of direct address */ +}; +struct in_addr { + union { + struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { u_short s_w1,s_w2; } S_un_w; + u_long S_addr; + } S_un; +#define s_addr S_un.S_addr + /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 + /* host on imp */ +#define s_net S_un.S_un_b.s_b1 + /* network */ +#define s_imp S_un.S_un_w.s_w2 + /* imp */ +#define s_impno S_un.S_un_b.s_b4 + /* imp # */ +#define s_lh S_un.S_un_b.s_b3 + /* logical host */ +}; +struct sockaddr_in { + short sin_family; + u_short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; +#endif +// Types +typedef unsigned char BOOL; +typedef void **HANDLE; +typedef Fixed fix; +typedef long long INT64; +typedef void *HGLOBAL; +// Function Definitions +#define _finite(a) finite(a) +#define strcmpi(a,b) stricmp(a,b) +#define strcmpni(a,b,c) strnicmp(a,b,c) +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define HeapSize(a,b,c) 0 +#define HeapDestroy(a) +int stricmp(const char *inX, const char *inY); +int strnicmp(const char *inX, const char *inY, int len); +char *strdup(const char *str); +void GlobalFree(void *); +void *GlobalAlloc(int flags,int size); +void *GlobalLock(HGLOBAL hMem); +void Sleep(int millis); +char *itoa(int value,char *string,int radix); +char *strupr(char *string); +inline int finite(double a) +{ + return(a != HUGE_VAL); +} + +#if 0 +/////////////////////////// +// From Winsock.h +/////////////////////////// +SOCKET accept (SOCKET s, struct sockaddr *addr, + int *addrlen); +int bind (SOCKET s, const struct sockaddr *addr, int namelen); +int closesocket (SOCKET s); +int connect (SOCKET s, const struct sockaddr *name, int namelen); +int ioctlsocket (SOCKET s, long cmd, u_long *argp); +int getpeername (SOCKET s, struct sockaddr *name, int * namelen); +int getsockname (SOCKET s, struct sockaddr *name, int * namelen); +int getsockopt (SOCKET s, int level, int optname, char * optval, int *optlen); +#define htonl(hostlong) (hostlong) +#define htons(hostshort) (hostshort) +unsigned long inet_addr (const char * cp); +char * inet_ntoa (struct in_addr in); +int listen (SOCKET s, int backlog); +u_long ntohl (u_long netlong); +u_short ntohs (u_short netshort); +int recv (SOCKET s, char * buf, int len, int flags); +int recvfrom (SOCKET s, char * buf, int len, int flags, struct sockaddr *from, int * fromlen); +//int select (int nfds, fd_set *readfds, fd_set *writefds, +// fd_set *exceptfds, const struct timeval *timeout); +int send (SOCKET s, const char * buf, int len, int flags); +int sendto (SOCKET s, const char * buf, int len, int flags, const struct sockaddr *to, int tolen); +int setsockopt (SOCKET s, int level, int optname, const char * optval, int optlen); +int shutdown (SOCKET s, int how); +SOCKET socket (int af, int type, int protocol); +#endif \ No newline at end of file diff --git a/mac/MAC_LLSOUND.CPP b/mac/MAC_LLSOUND.CPP new file mode 100644 index 000000000..a122e383a --- /dev/null +++ b/mac/MAC_LLSOUND.CPP @@ -0,0 +1,986 @@ +/* + * $Logfile: /DescentIII/Main/mac/MAC_LLSOUND.CPP $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:16 $ + * $Author: kevinb $ + * + * Mac implementation of low level sound library + * + * $Log: MAC_LLSOUND.CPP,v $ + * Revision 1.1.1.1 2003/08/26 03:58:16 kevinb + * initial 1.5 import + * + * + * 4 4/18/00 12:58p Matt + * Another update from Duane for 1.4 + * + * 3 4/12/00 7:08p Matt + * From Duane for 1.4 + * + * 2 10/21/99 1:55p Kevin + * Mac Merge! + * + * 1 7/28/99 2:31p Kevin + * Mac only stuff + * + * 1 5/21/97 6:53 PM Jeremy + * + * $NoKeywords: $ +*/ +#include +#include +#include +#include "pserror.h" +#include "mem.h" +#include "mac_llsound.h" +#include "ssl_lib.h" // Shared sound header (between high and low-levels) + +#define REF_DIST 50 +#define CLIP_ATTENUATION 0.7f //look in ddsoundload.cpp for its twin + +long master_volume_save = -1; +int saveSampleRate; +Component defaultSoundOutputDevice; + +pascal void SoundCallBack(SndChannelPtr channel, SndCommand cmd) +{ +// long sound_uid = (long)channel->userInfo; + channel->userInfo = -1; //set it to negative one to indicate I'm through with you +// channel->userInfo = -channel->userInfo; //set it to negative to indicate I'm through with you +} +void SetLLMasterVolume(float volume) +{ + short one_side; + long both_side; + + if(master_volume_save == -1) + GetDefaultOutputVolume(&master_volume_save); + + one_side = volume*0x0100; + both_side = (one_side << 16) | one_side; + SetDefaultOutputVolume(both_side); +} +void +mac_llsSystem::SetSoundCard(const char *name) +{ + sndsprk_initialized = false; +} +// Starts the sound library, maybe have it send back some information -- 3d support? +//mac_llsSystem::InitSoundLib(oeApplication *sos, char mixer_type, char m_sound_quality, bool f_kill_sound_list) +int +mac_llsSystem::InitSoundLib(char mixer_type, oeApplication *sos, ubyte max_sounds_played) +{ + int i; + OSStatus err = noErr; + extern UInt16 gNumTracks; + if( sndsprk_initialized) { + if(max_sounds_played != m_channel_count) { + SetNumChannels(max_sounds_played); + return 1; + } else { + return 0; + } + } + sndsprk_initialized = true; + ll_sound_ptr = this; + m_in_sound_frame = false; + m_pending_actions = false; + + m_channel_count = 0; + + m_mixer_type = SOUND_MIXER_NONE; + if(master_volume_save == -1) + GetDefaultOutputVolume(&master_volume_save); + + + ComponentDescription theDesc = { kSoundOutputDeviceType, 0, 0, 0, 0 }; + defaultSoundOutputDevice = FindNextComponent(NULL, &theDesc); + + err = GetSoundOutputInfo(defaultSoundOutputDevice, siSampleRate, &saveSampleRate); + if(err) mprintf((1, "InitSoundLib: unable st Get output rate\n")); + err = SetSoundOutputInfo(defaultSoundOutputDevice, siSampleRate, (void*)rate22050hz); + if(err) mprintf((1, "InitSoundLib: unable st set output rate\n")); + + for(i=0; i < 3; i++) { + snd_localization[i].cpuLoad = 1; + snd_localization[i].medium = kSSpMedium_Air; + snd_localization[i].humidity = 0; + snd_localization[i].roomSize = 0; + snd_localization[i].roomReflectivity = 0; + snd_localization[i].reverbAttenuation = 0; + snd_localization[i].sourceMode = kSSpSourceMode_Localized; + snd_localization[i].referenceDistance = REF_DIST; + snd_localization[i].coneAngleCos = 0; + snd_localization[i].coneAttenuation = 0; + snd_localization[i].currentLocation.elevation = 0; + snd_localization[i].currentLocation.azimuth = 0; + snd_localization[i].currentLocation.distance = 0; + snd_localization[i].currentLocation.projectionAngle = 1; + snd_localization[i].currentLocation.sourceVelocity = 0; + snd_localization[i].currentLocation.listenerVelocity = 0; + } + snd_localization[SQT_LOW].sourceMode = kSSpSourceMode_Unfiltered; + + SetNumChannels(max_sounds_played); + +#ifdef SPROCKET17 + SSpListener_New (&m_lr); + SSpListener_SetMetersPerUnit(m_lr, 1); + SSpListener_SetReverb(m_lr, 0.0, 0.0, 0.0); + for (i = 0; i < MAX_SOUNDS_MIXED; i++) { + SSpSource_New (&m_sr[i]); + SSpSource_SetMode(m_sr[i], kSSpSourceMode_Localized); +// SSpSource_SetSize(m_sr[i], 0.0, 0.0, 0.0); +// SSpSource_SetAngularAttenuation(m_sr[i], 0.0, 0.0); + } +#endif + return 1; +} +// Cleans up after the Sound Library +void mac_llsSystem::DestroySoundLib(void) +{ + int i; + + if ( !sndsprk_initialized ) + return; +#ifdef SPROCKET17 + if(m_sound_quality == SQT_HIGH) { + SSpListener_Dispose (m_lr); + for (i = 0; i < MAX_SOUNDS_MIXED; i++) { + snd_channel[i] = NULL; + SSpSource_Dispose (m_sr[i]); + } + } +#endif + for(i=0; iuserInfo = file_index; + snd_channel[channel]->userInfo = (file_index<<5) | channel; +// long sound_uid = (file_index<<5) | channel; +// snd_channel[channel]->userInfo = sound_uid; +// mprintf((2, "P %d ch %d\n", file_index, channel)); + + looped[channel] = f_looped; + + sndCommand.cmd = flushCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[channel], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: flushCmd", channel); + sndCommand.cmd = quietCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[channel], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: quietCmd", channel); + + sndCommand.cmd = bufferCmd; + sndCommand.param1 = 0; + sndCommand.param2 = (long)(&sndHeader); + err = SndDoCommand(snd_channel[channel], &sndCommand, true); + if(err) + Error("Sound Channel %d is corrupt: bufferCmd", channel); + + if(f_looped) { + sndCommand.cmd = freqCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 60L; + } else { + + sndCommand.cmd = callBackCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + } + err = SndDoCommand(snd_channel[channel], &sndCommand, true); + if(err) + Error("Sound Channel %d is corrupt: bufferCmd", channel); + +} +int mac_llsSystem::PlayStream(play_information *play_info) +{ + return 0; +} + +// Plays a 2d sound +int mac_llsSystem::PlaySound2d(play_information *play_info, int sound_index, float volume, float pan, bool f_looped) +{ + int channel; + + if(sound_index < 0) + return -1; + + int file_index = Sounds[sound_index].sample_index; + + // do common processing. + if (SoundFiles[file_index].used == 0) { + mprintf((2, "Tryed to play %d sound, it DNE.\n", file_index)); + return -1; + } + if(volume < 0.01) + return -1; + + channel = FindFreeChannel(volume, play_info->priority); + if(channel == -1) + return -1; + + channel_snd_index[channel] = sound_index; + sound_priority[channel] = play_info->priority; + channel_priority[channel] = volume*sound_priority[channel]; + + mprintf((1, "2d%4d L%d %16s pri %d vol %.2f\n", file_index, f_looped, SoundFiles[file_index].name, play_info->priority, volume)); +#ifdef SPROCKET17 + if (m_sound_quality == SQT_HIGH || m_sound_quality == SQT_NORMAL) +#else + if (m_sound_quality == SQT_HIGH) +#endif + { + SetChannelVolume(channel, 2.0); + snd_localization[m_sound_quality].currentLocation.azimuth = pan*PIOVER2; + snd_localization[m_sound_quality].currentLocation.elevation = 0; + snd_localization[m_sound_quality].referenceDistance = 1; + snd_localization[m_sound_quality].currentLocation.distance = volume; + SndSetInfo(snd_channel[channel], siSSpLocalization, &snd_localization[m_sound_quality]); +// mprintf((2, "2d %3d ch %2d d %8.3f pan %f9.4\n", sound_index, channel, +// snd_localization[m_sound_quality].currentLocation.azimuth, volume)); + } else { + SetChannelVolume(channel, volume); + } + + PlaySound(file_index, channel, f_looped); + + return (snd_channel[channel]->userInfo); +} +// Plays a 3d sound +int mac_llsSystem::PlaySound3d(play_information *play_info, int sound_index, pos_state *cur_pos, float master_volume, + bool f_looped, float reverb) +{ + int channel; + float volume; + + if(sound_index < 0) + return -1; + + // do common processing. + if (SoundFiles[Sounds[sound_index].sample_index].used == 0) { + mprintf((2, "Tryed to play %d sound, it DNE.\n", sound_index)); + return -1; + } + int file_index = Sounds[sound_index].sample_index; + float min = Sounds[sound_index].min_distance; + float max = Sounds[sound_index].max_distance; + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float dist = vm_NormalizeVectorFast(&dir_to_sound); + + if(dist< 0.1) + dir_to_sound = m_emulated_listener.orient.fvec; + + if (dist >= Sounds[sound_index].max_distance) + return -1; + else + if (dist < Sounds[sound_index].min_distance) + volume = 1.0; + else + volume = (1.0 - ((dist - min) / (max - min))); + +// if(volume < 0.01) { +// mprintf((1, "SMALL VOL %d %f\n", file_index, volume)); +// return -1; +// } + + channel = FindFreeChannel(volume, play_info->priority); + if(channel == -1) { + return -1; + } + channel_snd_index[channel] = sound_index; + sound_priority[channel] = play_info->priority; + channel_priority[channel] = volume*sound_priority[channel]; +// mprintf((2, "ply %d chn %d vol %f pri %d\n", file_index, channel, volume, play_info->priority)); +// mprintf((1, "3d%4d L%d %16s dist %.5f (%.5f,%.5f) pri %d, vol %.5f\n", file_index, f_looped, SoundFiles[file_index].name, dist, min, max, +// play_info->priority, volume)); +#ifdef SPROCKET17 + if(m_sound_quality == SQT_HIGH) + { +// mprintf((1, "SRC %.5f %.5f %.5f ",cur_pos->position->x, cur_pos->position->y, cur_pos->position->z)); +// mprintf((1, "LST %.5f %.5f %.5f\n", m_emulated_listener.position.x, m_emulated_listener.position.y, m_emulated_listener.position.z)); + SSpSource_SetPosition3f(m_sr[channel], cur_pos->position->x, cur_pos->position->y, cur_pos->position->z); + SSpSource_SetOrientation3f(m_sr[channel], cur_pos->orient->fvec.x, cur_pos->orient->fvec.y, cur_pos->orient->fvec.z); + SSpSource_SetUpVector3f(m_sr[channel], cur_pos->orient->uvec.x, cur_pos->orient->uvec.y, cur_pos->orient->uvec.z); + SSpSource_SetVelocity3f(m_sr[channel], 0, 0, 0); +// SSpSource_SetVelocity3f(m_sr[channel], cur_pos->velocity->x, cur_pos->velocity->y, cur_pos->velocity->z); + SSpSource_SetReferenceDistance(m_sr[channel], (max)/ DIST_SCALE); + + SSpSource_CalcLocalization(m_sr[channel], m_lr, &snd_localization[m_sound_quality]); + SndSetInfo(snd_channel[channel], siSSpLocalization, &snd_localization[m_sound_quality]); + } + else if (m_sound_quality == SQT_NORMAL) +#else + if (m_sound_quality == SQT_HIGH) +#endif + { + float az = (dir_to_sound * m_emulated_listener.orient.rvec); + float el = (dir_to_sound * m_emulated_listener.orient.uvec); + if(el > PIOVER2) el = PIOVER2; + else if(el < PIOVER2) el = -PIOVER2; + + snd_localization[m_sound_quality].referenceDistance = (max) / DIST_SCALE; + snd_localization[m_sound_quality].currentLocation.distance = dist; + snd_localization[m_sound_quality].currentLocation.azimuth = az; + snd_localization[m_sound_quality].currentLocation.elevation = el; + + SndSetInfo(snd_channel[channel], siSSpLocalization, &snd_localization[m_sound_quality]); + } else { + SetChannelVolume(channel, volume); + } + + PlaySound(file_index, channel, f_looped); + + return (snd_channel[channel]->userInfo); +} +// Sync's a single sound (changes something - frequency, volume, pan 3d stuff) +void mac_llsSystem::AdjustSound(int sound_uid, float volume, float pan, unsigned short frequency) +{ + int channel; + + if(sound_uid < 0) + return; + + for(int channel = 0; channel < m_channel_count; channel++) { + if(snd_channel[channel]->userInfo == sound_uid) { + channel_priority[channel] = volume*sound_priority[channel]; + +#ifdef SPROCKET17 + if (m_sound_quality == SQT_NORMAL || m_sound_quality == SQT_HIGH) +#else + if (m_sound_quality == SQT_HIGH) +#endif + { + SetChannelVolume(channel, 2.0); + snd_localization[m_sound_quality].currentLocation.azimuth = pan*PIOVER2; + snd_localization[m_sound_quality].currentLocation.elevation = 0; + snd_localization[m_sound_quality].referenceDistance = 1.0; + snd_localization[m_sound_quality].currentLocation.distance = volume; + SndSetInfo(snd_channel[channel], siSSpLocalization, &snd_localization[m_sound_quality]); + // mprintf((2, "adj2d %3d ch %2d d %8.3f pan %f9.4\n", sound_uid, channel, + // snd_localization[SQT_NORMAL].currentLocation.azimuth, volume)); + } else { + SetChannelVolume(channel, volume); + } +// break; + } + } +} +void mac_llsSystem::AdjustSound(int sound_uid, pos_state *cur_pos, float adjusted_volume, float reverb) +{ + int channel; + float volume; + + if(sound_uid < 0) + return; + + vector dir_to_sound = *cur_pos->position - m_emulated_listener.position; + float dist = vm_NormalizeVectorFast(&dir_to_sound); + + if(dist < 0.1) + dir_to_sound = m_emulated_listener.orient.fvec; + + for(channel = 0; channel < m_channel_count; channel++) { + if(snd_channel[channel]->userInfo == sound_uid) { + float min = Sounds[channel_snd_index[channel]].min_distance; + float max = Sounds[channel_snd_index[channel]].max_distance; + if (dist >= max) { +// mprintf((1, " %d %.4f >MAX %.4f\n", sound_uid, dist, max)); + StopSound(sound_uid); + continue; + } + else + if (dist < min) + volume = 1.0; + else + volume = (1.0 - ((dist - min) / (max - min))); + +// if(volume <= 0.01) { +// mprintf((2, "SMALL VOL %d %.3f dist %.3f (%.3f %.3f)\n", sound_uid, volume, dist, min, max)); +// StopSound(sound_uid); +// continue; +// } + channel_priority[channel] = volume*sound_priority[channel]; +#ifdef SPROCKET17 + if(m_sound_quality == SQT_HIGH) { + SSpSource_SetPosition3f(m_sr[channel], cur_pos->position->x, cur_pos->position->y, cur_pos->position->z); + SSpSource_SetOrientation3f(m_sr[channel], cur_pos->orient->fvec.x, cur_pos->orient->fvec.y, cur_pos->orient->fvec.z); + SSpSource_SetUpVector3f(m_sr[channel], cur_pos->orient->uvec.x, cur_pos->orient->uvec.y, cur_pos->orient->uvec.z); +// SSpSource_SetVelocity3f(m_sr[channel], 0, 0, 0); + SSpSource_SetVelocity3f(m_sr[channel], cur_pos->velocity->x, cur_pos->velocity->y, cur_pos->velocity->z); + + SSpSource_CalcLocalization(m_sr[channel], m_lr, &snd_localization[m_sound_quality]); + SndSetInfo(snd_channel[channel], siSSpLocalization, &snd_localization[m_sound_quality]); + } else if (m_sound_quality == SQT_NORMAL) +#else + if (m_sound_quality == SQT_HIGH) +#endif + { + float az = (dir_to_sound * m_emulated_listener.orient.rvec); + float el = (dir_to_sound * m_emulated_listener.orient.uvec); + if(el > PIOVER2) el = PIOVER2; + else if(el < -PIOVER2) el = -PIOVER2; + + snd_localization[m_sound_quality].currentLocation.distance = dist; + snd_localization[m_sound_quality].currentLocation.azimuth = az; + snd_localization[m_sound_quality].currentLocation.elevation = el; + + SndSetInfo(snd_channel[channel], siSSpLocalization, &snd_localization[m_sound_quality]); + } else { + + SetChannelVolume(channel, volume); + } +// break; + } + } +} +// Changes the sound list -- cmphack +void mac_llsSystem::NewSoundList(unsigned short num_sounds, unsigned int *sound_offset_array, + char *sound_data, unsigned int sound_data_size) +{ +} +// Locks and unlocks sounds (used when changing play_info data) +bool mac_llsSystem::LockSound(int sound_uid) +{ + return false; +} +bool mac_llsSystem::UnlockSound(int sound_uid) +{ + return false; +} +bool mac_llsSystem::SetSoundQuality(char quality) +{ int i; + OSErr err; + + if(quality == m_sound_quality) + return true; +// pause any sounds that may be playing + PauseSounds(); + if(quality == SQT_NORMAL) + { + m_sound_quality = SQT_NORMAL; + } + else if (quality == SQT_LOW) + { + m_sound_quality = SQT_LOW; + } + else + { + m_sound_quality = SQT_HIGH; + } + + if ( sndsprk_initialized ) { + for(i = 0; i < m_channel_count; i++) { +#ifdef SPROCKET17 + if (m_sound_quality == SQT_HIGH || m_sound_quality == SQT_NORMAL) +#else + if (m_sound_quality == SQT_HIGH) +#endif + SetChannelVolume(i, 2.0); + else + SetChannelVolume(i, 0.5); + + err = SndSetInfo(snd_channel[i], siSSpLocalization, &snd_localization[m_sound_quality]); + if(err) + Error("unable to localize sound channel %d\n", i); + } + } +#if 1 + for(i = 0; i < MAX_SOUNDS; i++) + { + if (Sounds[i].used != 0 && Sounds[i].sample_index > -1) + { + int snd_index = Sounds[i].sample_index; + ASSERT(snd_index < MAX_SOUNDS); + if(SoundFiles[snd_index].sample_8bit && (m_sound_quality == SQT_HIGH || m_sound_quality == SQT_NORMAL)) + { + mem_free(SoundFiles[snd_index].sample_8bit); + SoundFiles[snd_index].sample_8bit = NULL; + + CheckAndForceSoundDataAlloc(i); + } + else if(SoundFiles[snd_index].sample_16bit && m_sound_quality == SQT_LOW) + { + int count; + ASSERT(SoundFiles[snd_index].sample_8bit == NULL); + SoundFiles[snd_index].sample_8bit = (unsigned char *) mem_malloc(SoundFiles[snd_index].sample_length); + // Do the volume clipping with the high quality sound + for(count = 0; count < (int)SoundFiles[snd_index].sample_length; count++) + { + SoundFiles[snd_index].sample_16bit[count] *= CLIP_ATTENUATION; + } + // NOTE: Interesting note on sound conversion: 16 bit sounds are signed (0 biase). 8 bit sounds are unsigned (+128 biase). + for(count = 0; count < (int)SoundFiles[snd_index].sample_length; count++) + { + SoundFiles[snd_index].sample_8bit[count] = (unsigned char)((((int)SoundFiles[snd_index].sample_16bit[count]) + 32767) >> 8); + } + mem_free(SoundFiles[snd_index].sample_16bit); + SoundFiles[snd_index].sample_16bit = NULL; + } + + } + } +#endif + ResumeSounds(); + return true; +} +char mac_llsSystem::GetSoundQuality(void) +{ + return m_sound_quality; +} +bool mac_llsSystem::SetSoundMixer(char mixer_type) +{ + return true; +} +char mac_llsSystem::GetSoundMixer(void) +{ + return SOUND_MIXER_SOFTWARE_16; +} +void mac_llsSystem::StopAllSounds(void) +{ + int i; + SndCommand sndCommand; + OSErr err; + + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo > 0) { + + sndCommand.cmd = flushCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: flushCmd", i); + sndCommand.cmd = quietCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: quietCmd", i); + + snd_channel[i]->userInfo = -1; + channel_priority[i] = 0; + + } + } +} + +// Stops 2d and 3d sounds +void mac_llsSystem::StopSound(int sound_uid, unsigned char f_immediately) +{ + int i; + SndCommand sndCommand; + OSErr err; + + if(sound_uid < 0) + return; + + for(i = 0; i < m_channel_count; i++) { +// mprintf((2, "%4d:%.5f ", snd_channel[i]->userInfo, channel_priority[i])); + if((snd_channel[i]->userInfo) == sound_uid) { +// mprintf((2, "stp %d chn %d\n", snd_channel[i]->userInfo, i)); + +// long sound_index = (long)ABS(snd_channel[i]->userInfo); +// mprintf((1, "S (ch %d %d) %ld\n", i, sound_index & 0x000f, sound_index>>4 )); + + if(f_immediately == SKT_STOP_AFTER_LOOP) { + if(looped[i] == false) + return; + sndCommand.cmd = freqCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoCommand(snd_channel[i], &sndCommand, true); + if(err) + Error("Sound Channel %d is corrupt: freqCmd", i); + } + else + { + sndCommand.cmd = flushCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: flushCmd", i); + + sndCommand.cmd = quietCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: quietCmd", i); + } + snd_channel[i]->userInfo = -1; +// snd_channel[i]->userInfo = -snd_channel[i]->userInfo; + channel_priority[i] = 0; + sound_priority[i] = 0; + channel_snd_index[i] = -1; +// return; + } + } +// mprintf((2, "\n")); +} +// Checks if a sound is playing (removes finished sound); +bool mac_llsSystem::IsSoundInstancePlaying(int sound_uid) { + int i; + if(sound_uid < 0) + return false; + + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo == sound_uid) { + return true; + } + } + return false; +} +int mac_llsSystem::IsSoundPlaying(int sound_uid) { + int i; + if(sound_uid < 0) + return -1; + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo == sound_uid) { + return snd_channel[i]->userInfo; + } + } + return -1; +} +// Set listener's position +void mac_llsSystem::SetListener(pos_state *cur_pos) +{ + m_emulated_listener.orient = *cur_pos->orient; + m_emulated_listener.position = *cur_pos->position; + m_emulated_listener.velocity = *cur_pos->velocity; +// m_emulated_listener.velocity.x = 0; +// m_emulated_listener.velocity.y = 0; +// m_emulated_listener.velocity.z = 0; +#ifdef SPROCKET17 + if(m_sound_quality == SQT_HIGH) { + SSpListener_SetPosition3f(m_lr, m_emulated_listener.position.x, m_emulated_listener.position.y, m_emulated_listener.position.z); + SSpListener_SetOrientation3f(m_lr, m_emulated_listener.orient.fvec.x, m_emulated_listener.orient.fvec.y, m_emulated_listener.orient.fvec.z); + SSpListener_SetUpVector3f(m_lr, m_emulated_listener.orient.uvec.x, m_emulated_listener.orient.uvec.y, m_emulated_listener.orient.uvec.z); + SSpListener_SetVelocity3f(m_lr, m_emulated_listener.velocity.x, m_emulated_listener.velocity.y, m_emulated_listener.velocity.z); + } +#endif +} +//float ll_volume; +// Sets the master volume (2d and 3d sounds) -- chrishack -- use primary buffer +void mac_llsSystem::SetMasterVolume(float volume) +//void mac_llsSystem::SetLLVolume(float volume) +{ + m_volume = volume; +} +// Gets the master volume +float mac_llsSystem::GetMasterVolume(void) +//float mac_llsSystem::GetLLVolume(void) +{ + return m_volume; //ll_volume; +} +// Pause all sounds/resume all sounds +void mac_llsSystem::PauseSounds(void) +{ + int i; + SndCommand sndCommand; + OSErr err; + + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo >= 0) { + sndCommand.cmd = pauseCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: pauseCmd", i); + } + } +} +void mac_llsSystem::ResumeSounds(void) +{ + int i; + SndCommand sndCommand; + OSErr err; + + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo >= 0) { + sndCommand.cmd = resumeCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: resumeCmd", i); + } + } +} +// Begin sound frame +void mac_llsSystem::SoundStartFrame(void) {} +void mac_llsSystem::PauseSound(int sound_uid) +{ + int i; + SndCommand sndCommand; + OSErr err; + + if(sound_uid < 0) + return; + + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo == sound_uid) { + sndCommand.cmd = pauseCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: pauseCmd", i); + + return; + } + } +} +void mac_llsSystem::ResumeSound(int sound_uid) +{ int i; + SndCommand sndCommand; + OSErr err; + + if(sound_uid < 0) + return; + + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo == sound_uid) { + sndCommand.cmd = resumeCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + err = SndDoImmediate(snd_channel[i], &sndCommand); + if(err) + Error("Sound Channel %d is corrupt: resumeCmd", i); + + return; + } + } +} +bool mac_llsSystem::CheckAndForceSoundDataAlloc(int sound_uid) +{ + int result; + + if(sound_uid < 0) + return; + + int sound_file_index = Sounds[sound_uid].sample_index; + ASSERT(sound_file_index >= 0 && sound_file_index < MAX_SOUND_FILES); + if (sound_file_index < 0 || sound_file_index >= MAX_SOUND_FILES) { + return false; + } + // Check if the sample data is already loaded + if(SoundFiles[sound_file_index].sample_16bit != NULL || SoundFiles[sound_file_index].sample_8bit != NULL) + return true; + // If not, get the sound data +// result = SoundLoadWaveFile(SoundFiles[sound_file_index].name, Sounds[sound_uid].import_volume, sound_file_index, SQT_HIGH, true); + result = SoundLoadWaveFile(SoundFiles[sound_file_index].name, Sounds[sound_uid].import_volume, sound_file_index, (m_sound_quality>0)?1:0, true); + // Why would it load once (table load time) and not now? + if(!result) + return false; +// mprintf((1, "%s loaded.\n", SoundFiles[sound_file_index].name)); + return true; +} +// End sound frame +void mac_llsSystem::SoundEndFrame(void) +{ +} +ubyte mac_llsSystem::GetNumChannels(void) +{ + return m_channel_count; +} +int mac_llsSystem::FindFreeChannel(float volume, int priority) +{ + int i; + + int found = -1; + int num_used_chan = 0; + + float weighted_priority = priority*volume; + + // first look for an empty slot + for(i = 0; i < m_channel_count; i++) { + if(snd_channel[i]->userInfo < 0) { + channel_priority[i] = weighted_priority; + return (i); + } + } + + if(priority == 0) // lowest priority does not register past here + return -1; + + // return the lowest priority slot + float min_priority = weighted_priority; + int min_chan = -1; + for(i = 0; i < m_channel_count; i++) { + if(channel_priority[i] < min_priority) { + min_priority = channel_priority[i]; + min_chan = i; + } + } + + if(min_chan != -1) { +// mprintf((1, "\nK %d snd %d pri %.5f > %.5f\n", min_chan, snd_channel[min_chan]->userInfo, weighted_priority, channel_priority[min_chan])); + StopSound(snd_channel[min_chan]->userInfo); + return (min_chan); + } + + mprintf((2, "NO FREE Sound CHANNEL: priority %f\n", weighted_priority)); + return -1; +} +void mac_llsSystem::SetNumChannels(ubyte num_chan) +{ + int i; + OSStatus err; + unsigned int cpuLoadLimit; + SoundComponentLink myLink; + SndCommand sndCommand; + + if ( !sndsprk_initialized ) + return; + myLink.description.componentType = kSoundEffectsType; + myLink.description.componentSubType = kSSpLocalizationSubType; + myLink.description.componentManufacturer = kAnyComponentManufacturer; + myLink.description.componentFlags = 0; + myLink.description.componentFlagsMask = kAnyComponentFlagsMask; + myLink.mixerID = nil; + myLink.linkID = nil; + if(num_chan > m_channel_count) { + SndCallBackUPP myCallBackUPP; + myCallBackUPP = NewSndCallBackProc(SoundCallBack); + + for(i=m_channel_count; iuserInfo = -1; + channel_priority[i] = 0; + } + } else if (num_chan< m_channel_count) { + for(i=m_channel_count-1; i>=num_chan; i--) { + SndDisposeChannel(snd_channel[i], true); + snd_channel[i] = NULL; + } + } + m_channel_count = num_chan; +} +// environmental sound interface +// volume modifier (0-1), damping(0-1), 1 = complete, 0 = none +// decay 0.1 to 100 seconds, how long it takes for a sound to die. +bool mac_llsSystem::SetGlobalReverbProperties(float volume, float damping, float decay) +{ +#ifdef SPROCKET17 + if(m_sound_quality == SQT_HIGH) { + SSpListener_SetReverb(m_lr, 9, damping*-20, volume*-20); + SSpListener_SetReverb(m_lr, 0, 0, 0); + } +#endif + return false; +} +// set special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void mac_llsSystem::SetEnvironmentValues(const t3dEnvironmentValues *env) {} +// get special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void mac_llsSystem::GetEnvironmentValues(t3dEnvironmentValues *env) {} +// enable special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void mac_llsSystem::SetEnvironmentToggles(const t3dEnvironmentToggles *env) +{ +} +// get states of special parameters for the 3d environment. +// of strcuture passed, you must set the appropriate 'flags' value for values you wish to modify +void mac_llsSystem::GetEnvironmentToggles(t3dEnvironmentToggles *env) {} +// Sound System Error Handler. +void +mac_llsSystem::CheckForErrors() +{ +// if a fatal error occurred, quit and display an error +// non fatal errors should be put inside a logfile, or just mprinted out. + switch (m_lib_error_code) + { + } +// must call! + llsSystem::CheckForErrors(); +} + +short *sound_render_audible_rooms(pos_state *listener_pos, float max_radius) { return 0; }; +void SoundRenderReset() {}; +void sound_render_end_frame() {}; +bool sound_render_start_frame() { return 0; }; + +/* +inline int mac_llsSystem::MakeUniqueId(int sound_index, int channel) +{ + return (sound_index + (channel << 10)); +} + +inline int mac_llsSystem::DecodeId(int sound_index, int channel) +{ + return (sound_index - (channel >> 10)); +} +*/ diff --git a/mac/MAC_OPENGL_DEBUG.H b/mac/MAC_OPENGL_DEBUG.H new file mode 100644 index 000000000..740deed8e --- /dev/null +++ b/mac/MAC_OPENGL_DEBUG.H @@ -0,0 +1,78 @@ +#include "MacHeadersD3.pch" + +#include +#include "Macros.h" +#include + +// Constants +#define MACINTOSH 1 + +#undef USE_GLIDE +#define USE_OPENGL +#define NUM_MIP_LEVELS 8 +#define mprintf(args) Debug_ConsolePrintf args +#undef STEALTH +#define DAJ_DEBUG +#define _DEBUG +#undef RELEASE + +#define and andVar +#define or orVar + +#define _fstat fstat +#define _filelength filelength + +#define __cdecl + +#define _MAX_PATH 1024 +#define _MAX_FNAME 64 /* max. length of path component*/ +#define _MAX_EXT 64 /* max. length of extension component*/ + +#define TRANSPARENT_COLOR 0 + +// These are other error types defined in Visual C++. +// The values are made up and hopefullydon't conflict JCA +#define EACCES 102 +#define EDEADLOCK 104 +#define EMFILE 105 +#define ENOENT 106 +#define ENFILE 107 +#define ENOSPC 108 +#define ENOSYS 112 + + +// Types +typedef unsigned char BOOL; +typedef void **HANDLE; +typedef Fixed fix; +typedef long long INT64; +typedef void *HGLOBAL; + +// Function Definitions +#define _finite(a) finite(a) +#define strcmpi(a,b) stricmp(a,b) +#define strcmpni(a,b,c) strnicmp(a,b,c) +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define HeapSize(a,b,c) 0 +#define HeapDestroy(a) + +int stricmp(const char *inX, const char *inY); +int strnicmp(const char *inX, const char *inY, int len); +char *strdup(const char *str); + +void GlobalFree(void *); +void *GlobalAlloc(int flags,int size); +void *GlobalLock(HGLOBAL hMem); +void Sleep(int millis); +char *itoa(int value,char *string,int radix); +char *strupr(char *string); +void HeapFree(HANDLE heap, int dummy, void *mptr); +void *HeapAlloc(HANDLE heap, int dummy, int size); + +#define HEAP_NO_SERIALIZE 0 + +inline int finite(double a) +{ + return(a != HUGE_VAL); +} + diff --git a/mac/MAC_OPENGL_REL.H b/mac/MAC_OPENGL_REL.H new file mode 100644 index 000000000..134619e13 --- /dev/null +++ b/mac/MAC_OPENGL_REL.H @@ -0,0 +1,74 @@ +#include "MacHeadersD3.pch" + +#include +#include "Macros.h" +#include + +// Constants +#define MACINTOSH 1 + +#undef USE_GLIDE +#define USE_OPENGL +#define NUM_MIP_LEVELS 8 +#define mprintf(args) //Debug_ConsolePrintf args +#undef STEALTH +#undef DAJ_DEBUG +#undef _DEBUG +#define RELEASE + +#define and andVar +#define or orVar + +#define _fstat fstat +#define _filelength filelength + +#define __cdecl + +#define _MAX_PATH 1024 +#define _MAX_FNAME 64 /* max. length of path component*/ +#define _MAX_EXT 64 /* max. length of extension component*/ + +#define TRANSPARENT_COLOR 0 + +// These are other error types defined in Visual C++. +// The values are made up and hopefullydon't conflict JCA +#define EACCES 102 +#define EDEADLOCK 104 +#define EMFILE 105 +#define ENOENT 106 +#define ENFILE 107 +#define ENOSPC 108 +#define ENOSYS 112 + + +// Types +typedef unsigned char BOOL; +typedef void **HANDLE; +typedef Fixed fix; +typedef long long INT64; +typedef void *HGLOBAL; + +// Function Definitions +#define _finite(a) finite(a) +#define strcmpi(a,b) stricmp(a,b) +#define strcmpni(a,b,c) strnicmp(a,b,c) +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#define HeapSize(a,b,c) 0 +#define HeapDestroy(a) + +int stricmp(const char *inX, const char *inY); +int strnicmp(const char *inX, const char *inY, int len); +char *strdup(const char *str); + +void GlobalFree(void *); +void *GlobalAlloc(int flags,int size); +void *GlobalLock(HGLOBAL hMem); +void Sleep(int millis); +char *itoa(int value,char *string,int radix); +char *strupr(char *string); + +inline int finite(double a) +{ + return(a != HUGE_VAL); +} + diff --git a/mac/MALLOC.H b/mac/MALLOC.H new file mode 100644 index 000000000..83940e473 --- /dev/null +++ b/mac/MALLOC.H @@ -0,0 +1,4 @@ +// Created by John C Ardussi as a placeholder for a file that +// does not exist under CodeWarrior. +// +// Date: 5-10-99 \ No newline at end of file diff --git a/mac/MacDisplays.h b/mac/MacDisplays.h new file mode 100644 index 000000000..33da70c0d --- /dev/null +++ b/mac/MacDisplays.h @@ -0,0 +1,12 @@ +#include + + +#define DSP_640x480 0 +#define DSP_800x600 1 +#define DSP_1024x768 2 +#define DSP_1152x870 3 +//#define DSP_1280x960 4 + +extern DSpContextAttributes gDSpContextAttributes[]; +extern DSpContextReference gDSpContext[]; +extern short current_context; diff --git a/mac/MacMovie.cpp b/mac/MacMovie.cpp new file mode 100644 index 000000000..2e7a78362 --- /dev/null +++ b/mac/MacMovie.cpp @@ -0,0 +1,167 @@ +// Mac implementation + +#include + +#include + +#include "descent.h" +#include "macapp.h" +#include "hlsoundlib.h" +#include "movie.h" +#include "mac_llsound.h" + +// sets the directory where movies are stored +int mve_Init(const char *dir, const char *sndcard) +{ + long myAttrs; + OSErr err; + + err = Gestalt(gestaltQuickTime, &myAttrs); + if(err) { + return MVELIB_INIT_ERROR; + } + + err = EnterMovies(); + if(err) { + return MVELIB_INIT_ERROR; + } + + return MVELIB_NOERROR; +} + +int mve_PlayMovie(const char *mvename, oeApplication *app) +{ + OSErr theErr,err; + OSStatus theStatus; + FSSpec theFSSpec; + Rect r; + int movieError; + short fileRef; + CGrafPtr port; + Rect movieBox; + extern DSpContextReference gDSpContext[]; + + short movieResFile; + tMacAppInfo macApp; + + err = FSMakeFSSpec(0, 0L, (const unsigned char *) c2pstr(mvename), &theFSSpec); + if(err != noErr) { + //mprintf((2, "could not make FSSpec in mve_PlayMovie()\n")); + //Int3(); + return MVELIB_FILE_ERROR; + } + + theErr = OpenMovieFile (&theFSSpec, &movieResFile, fsRdPerm); + + if (fileRef != -1 && theErr == noErr) { + + Movie aMovie = nil; + short movieResID = 0; + Str255 movieName; + Boolean wasChanged; + + err = NewMovieFromFile (&aMovie, movieResFile, &movieResID, movieName, newMovieActive, &wasChanged); + + DSpContext_GetBackBuffer(gDSpContext[0], kDSpBufferKind_Normal, &port); + SetPort((GrafPtr)port); + BackColor(whiteColor); + r = port->portRect; + PaintRect(&r); + DSpContext_InvalBackBufferRect (gDSpContext[0], &r); + DSpContext_SwapBuffers (gDSpContext[0], NULL, 0); + + float saveVol = Sound_system.GetMasterVolume(); + Sound_system.SetMasterVolume(0.7); + + FlushEvents(everyEvent, 0); + + Descent->get_info(&macApp); + GetMovieBox (aMovie, &movieBox); + OffsetRect (&movieBox, 0, (macApp.wnd_h-movieBox.bottom)/2); + SetMovieBox (aMovie, &movieBox); + SetMovieGWorld (aMovie, macApp.hwnd, nil); +// SetMovieVolume(aMovie, 0x0300); + + StartMovie (aMovie); + + EventRecord theEvent; + bool done = false; + while ( !IsMovieDone(aMovie) && !done) { + if(::GetOSEvent(everyEvent - diskMask, &theEvent)) { +// if (WaitNextEvent (everyEvent, &theEvent, 0, nil)) { + switch ( theEvent.what ) { + case mouseDown: + done = true; + break; + case keyUp: + case keyDown: + case autoKey: + if((theEvent.message & charCodeMask) & 0x1B) //ESC + done = true; + break; + } + } + MoviesTask (aMovie, DoTheRightThing); + } + // Clean up. + DisposeMovie (aMovie); + + BackColor(blackColor); + PaintRect(&r); + DSpContext_InvalBackBufferRect (gDSpContext[0], &r); + DSpContext_SwapBuffers (gDSpContext[0], NULL, 0); + + Sound_system.SetMasterVolume(saveVol); + + return MVELIB_NOERROR; + } + return MVELIB_PLAYBACK_ERROR; +} + +// sets the directory where movies are stored +unsigned mve_SequenceStart(const char *mvename, int *fhandle, oeApplication *app, bool looping) +{ + return 0; +} + + +unsigned mve_SequenceFrame(unsigned handle, int fhandle, bool sequence, int *bm_handle) +{ + return (unsigned)(-1); +} + + +bool mve_SequenceClose(unsigned handle, int fhandle) +{ + return false; +} + + +void mve_SetRenderProperties(short x, short y, short w, short h, renderer_type type, bool hicolor) +{ + +} + + +void mve_SetCallback(void (*fn)(int,int,int)) +{ +} + +// call to print out text. +void mve_Puts(short x, short y, const char *txt) +{ + +} + + +void mve_ClearRect(short x1, short y1, short x2, short y2) +{ + + +} + +// call to print out text. +void mve_Puts(short x, short y, ddgr_color col, const char *txt) +{ +} + diff --git a/mac/SNDSPROCKET.CPP b/mac/SNDSPROCKET.CPP new file mode 100644 index 000000000..26df48bcb --- /dev/null +++ b/mac/SNDSPROCKET.CPP @@ -0,0 +1,906 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "pstypes.h" +#define SOUND_ON +#define MAX_VOLUME 256 +#define MAX_SOUND_CHANNELS 16 +Boolean have_descent_cd = false; +int sndsprk_lomem = 0; +int master_save; +int sndsprk_initialized = 0; +ubyte sndsprk_paused = 0; +ubyte sndsprk_cd_started = 0; // have we started a disc that is not the Descent cd? +int midi_volume = 256; +int sndsprk_volume = MAX_VOLUME; +int sndsprk_midi_song_playing = 0; +int sndsprk_last_midi_song = 0; +int sndsprk_last_midi_song_loop = 0; +int sndsprk_max_channels = MAX_SOUND_CHANNELS; +ushort num_sounds = 0; +ushort num_channels = 0; +ushort near_channel = 0; +ushort far_channel = 0; +extern ubyte Config_master_volume; +static int CD_volume; +static SSpListenerReference gListener = nil; +// sound object stuff -- used for fans and the boss only?? +#define SOF_USED 1 // Set if this sample is used +#define SOF_PLAYING 2 // Set if this sample is playing on a channel +#define SOF_LINK_TO_OBJ 4 // Sound is linked to a moving object. If object dies, then finishes play and quits. +#define SOF_LINK_TO_POS 8 // Sound is linked to segment, pos +#define SOF_PLAY_FOREVER 16 // Play forever (or until level is stopped), otherwise plays once +#define SOUND_PLAYING 1 +#define SOUND_OBJECT_PLAYING 2 +typedef struct song_resource { + short midi_id; + ubyte lead_inst; + ubyte buffer_ahead; + ushort tempo; + ushort pitch_shift; + ubyte sound_voices; + ubyte max_notes; + ushort norm_voices; +} song_resource; +typedef struct sound_object { + short signature; // A unique signature to this sound + ubyte flags; // Used to tell if this slot is used and/or currently playing, and how long. + fix max_volume; // Max volume that this sound is playing at + fix max_distance; // The max distance that this sound can be heard at... + fix distance; // The distance that this sound is from the player + int volume; // Volume that this sound is playing at + int pan; // Pan value that this sound is playing at + short handle; // What handle this sound is playing on. Valid only if SOF_PLAYING is set. + short soundnum; // The sound number that is playing + union { + struct { + short segnum; // Used if SOF_LINK_TO_POS field is used + short sidenum; + vms_vector position; + } pos; + struct { + short objnum; // Used if SOF_LINK_TO_OBJ field is used + short objsignature; + } obj; + } link_type; +} sound_object; +int num_sound_objects = 0; +sound_object SoundObjects[MAX_SOUND_OBJECTS]; +short next_signature=0; +int old_sndnum[3] = {-1, -1, -1}; +#define SOUND_OFFSET 10000 +#define MIN_SOUND_DISTANCE 10 +#define MAX_SOUND_DISTANCE 100 +Ptr sound_ptr[MAX_SOUNDS+1]; +SndChannelPtr snd_channel_near[MAX_SOUND_CHANNELS]; +SndChannelPtr snd_channel_far[MAX_SOUND_CHANNELS]; +SndChannelPtr snd_channel_ambient[3] = { 0, 0, 0 }; +SSpLocalizationData farLocalization, nearLocalization, ambientLocalization; +void calc_polar_sound(vms_vector * sound_pos, float *azimuth, float *elevation); +void sndsprk_reset_sndsprk_sounds() +{ + int i; + SndCommand sndCommand; + if ( !sndsprk_initialized ) return; + +//DAJ EndAllSound(); +// num_sound_objects = 0; +// stop_redbook(); + + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + + sndCommand.cmd = flushCmd; + for(i=0; i= num_channels) { + far_channel = 0; + } + } +#endif +} +void sndsprk_play_loop_3d(int chan, int sndnum, float distance, float elevation, float azimuth) +{ + SndCommand sndCommand; + OSStatus err; + + if (Newdemo_state == ND_STATE_RECORDING) + newdemo_record_sound_3d_once(sndnum, azimuth, distance); +#ifdef SOUND_ON + if(sound_ptr[sndnum]) { + ambientLocalization.currentLocation.elevation = elevation; + ambientLocalization.currentLocation.azimuth = azimuth; + ambientLocalization.currentLocation.distance = distance; + err = SndSetInfo(snd_channel_ambient[chan], siSSpLocalization, &ambientLocalization); +// if(err != noErr) +// Error("Ambient SndSetInfo siSSpLocalization channel %d", chan); + + if(sndnum != old_sndnum[chan]) { + sndCommand.cmd = bufferCmd; + sndCommand.param1 = 0; + sndCommand.param2 = (long)(sound_ptr[sndnum]); + err = SndDoCommand(snd_channel_ambient[chan], &sndCommand, true); +// if(err != noErr) +// Error("Ambient Sound Channel %d is corrupt: bufferCmd", chan); + sndCommand.cmd = freqCmd; + sndCommand.param1 = 0; + sndCommand.param2 = 60; + err = SndDoCommand(snd_channel_ambient[chan], &sndCommand, true); +// if(err != noErr) +// Error("Ambient Sound Channel %d is corrupt: freqCmd", chan); + + old_sndnum[chan] = sndnum; + } + } +#endif +} +void sndsprk_play_sample( int sndnum, fix max_volume ) +{ + SndCommand sndCommand; + OSStatus err; + + if (Newdemo_state == ND_STATE_RECORDING) + newdemo_record_sound(sndnum); +#ifdef SOUND_ON + + if(sound_ptr[sndnum] && snd_channel_near[near_channel]) { + // set in set_max_channels & does not change + SndSetInfo(snd_channel_near[near_channel], siSSpLocalization, &nearLocalization); + + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + + sndCommand.cmd = flushCmd; + err = SndDoImmediate(snd_channel_near[near_channel], &sndCommand); +// if(err != noErr) +// Error("Near Sound Channel %d is corrupt: flushCmd", near_channel); + + sndCommand.cmd = quietCmd; // Send quietCmd to stop any current sound. + err = SndDoImmediate(snd_channel_near[near_channel], &sndCommand); +// if(err != noErr) +// Error("Near Sound Channel %d is corrupt: quietCmd", near_channel); + + sndCommand.cmd = bufferCmd; + sndCommand.param2 = (long)(sound_ptr[sndnum]); + err = SndDoCommand(snd_channel_near[near_channel], &sndCommand, true); +// if(err != noErr) +// Error("Near Sound Channel %d is corrupt: bufferCmd", near_channel); + + near_channel++; + if(near_channel >= num_channels) { +// mprintf((STDOUT, "near snd_channel roll over at %f\n", f2fl(GameTime))); + near_channel = 0; + } + } +#endif +} +void sndsprk_play_sample_once( int sndnum, fix max_volume ) +{ + sndsprk_play_sample( sndnum, max_volume ); +} +void sndsprk_set_sndsprk_volume(int volume) +{ + SndCommand sndCommand; + int i; + short one_side; + long both_side; + + if ( !sndsprk_initialized ) return; + + one_side = volume*0x20; + both_side = (one_side << 16) | one_side; + sndCommand.cmd = volumeCmd; + sndCommand.param1 = 0; + sndCommand.param2 = both_side; + for(i=0; i num_channels) { + + for(i=num_channels; iqLength = 2; + err = SndNewChannel(&snd_channel_near[i], sampledSynth, initStereo /*initMono+initNoInterp*/, NULL); + if(err != noErr) + Error("Error %d Unable to allocate near sound channel %d", err, i); + + err = SndSetInfo(snd_channel_near[i], siPreMixerSoundComponent, &myLink); + if(err != noErr) + Error("SndSetInfo siPreMixerSoundComponent near Error %d", err); + err = SndSetInfo(snd_channel_near[i], siSSpLocalization, &nearLocalization); + if(err != noErr) + Error("SndSetInfo siSSpLocalization near Error %d", err); + } + for(i=num_channels; iqLength = 2; + err = SndNewChannel(&snd_channel_far[i], sampledSynth, initStereo /*initMono+initNoInterp*/, NULL); + if(err != noErr) + Error("Error %d Unable to allocate far sound channel %d", err, i); + + err = SndSetInfo(snd_channel_far[i], siPreMixerSoundComponent, &myLink); + if(err != noErr) + Error("SndSetInfo siPreMixerSoundComponent far Error %d", err); + } + } else if (n< num_channels) { + for(i=num_channels-1; i>=n; i--) { + SndDisposeChannel(snd_channel_near[i], true); + snd_channel_near[i] = NULL; + SndDisposeChannel(snd_channel_far[i], true); + snd_channel_far[i] = NULL; + } + } + num_channels = n; + + if(snd_channel_ambient[0] == 0) { + for(i = 0; i < 3; i++) { + snd_channel_ambient[i] = (SndChannelPtr)NewPtr(sizeof(SndChannel)); + snd_channel_ambient[i]->qLength = 2; + err = SndNewChannel(&snd_channel_ambient[i], sampledSynth, initStereo /*initMono+initNoInterp*/, NULL); + if(err != noErr) + Error("Err %d Unable to allocate ambient Sound Channel %d", err, i); + + err = SndSetInfo(snd_channel_ambient[i], siPreMixerSoundComponent, &myLink); + if(err != noErr) + Error("Error %d in SndSetInfo ambient chan %d", err, i); + } + } +} +int sndsprk_start_sound_object(int i) +{ + fix path_distance; + int num_search_segs = 20; + float distance, elevation, azimuth; + calc_polar_sound(&SoundObjects[i].link_type.pos.position, &azimuth, &elevation); + path_distance = find_connected_distance(&Viewer->pos, Viewer->segnum, &SoundObjects[i].link_type.pos.position, SoundObjects[i].link_type.pos.segnum, num_search_segs, WID_RENDPAST_FLAG ); + distance = f2fl(path_distance); + if( distance > 0 ) { + sndsprk_play_loop_3d(0, SoundObjects[i].soundnum, distance, elevation, azimuth); + return 1; + } + return 0; +} +void +sndsprk_stop_loop(int chan) +{ + SndCommand sndCommand; + if(old_sndnum[chan] != -1) { + old_sndnum[chan] = -1; + sndCommand.cmd = quietCmd; // Send quietCmd to stop any current sound. + sndCommand.param1 = 0; + sndCommand.param2 = 0L; + SndDoImmediate(snd_channel_ambient[chan], &sndCommand); + } +} +void sndsprk_get_sound_loc( vms_matrix * listener, vms_vector * listener_pos, int listener_seg, vms_vector * sound_pos, int sound_seg, fix max_volume, int *volume, int *pan, fix max_distance ) +{ + vms_vector vector_to_sound; + fix angle_from_ear, cosang,sinang; + fix distance; + fix path_distance; + *volume = 0; + *pan = 0; + if (!sndsprk_initialized) return; + + max_distance = (max_distance*5)/4; // Make all sounds travel 1.25 times as far. + // Warning: Made the vm_vec_normalized_dir be vm_vec_normalized_dir_quick and got illegal values to acos in the fang computation. + distance = vm_vec_normalized_dir_quick( &vector_to_sound, sound_pos, listener_pos ); + + if (distance < max_distance ) { + int num_search_segs = f2i(max_distance/20); + if ( num_search_segs < 1 ) num_search_segs = 1; + path_distance = find_connected_distance(listener_pos, listener_seg, sound_pos, sound_seg, num_search_segs, WID_RENDPAST_FLAG ); + //path_distance = distance; + if ( path_distance > -1 ) { + *volume = max_volume - (path_distance/f2i(max_distance)); + //mprintf( (0, "Sound path distance %.2f, volume is %d / %d\n", f2fl(distance), *volume, max_volume )); + if (*volume > 0 ) { + angle_from_ear = vm_vec_delta_ang_norm(&listener->rvec,&vector_to_sound,&listener->uvec); + fix_sincos(angle_from_ear,&sinang,&cosang); + //mprintf( (0, "volume is %.2f\n", f2fl(*volume) )); + if (Config_channels_reversed) cosang *= -1; + *pan = fixmuldiv(cosang, 255, F1_0); + } else { + *volume = 0; + } + } + } +} +int sndsprk_link_sound_to_object( int soundnum, short objnum, int forever, fix max_volume ) +{ // 10 segs away + return sndsprk_link_sound_to_object2( soundnum, objnum, forever, max_volume, 256*F1_0 ); +} +int sndsprk_link_sound_to_pos( int soundnum, short segnum, short sidenum, vms_vector * pos, int forever, + fix max_volume ) +{ + return sndsprk_link_sound_to_pos2( soundnum, segnum, sidenum, pos, forever, max_volume, F1_0 * 256 ); +} +int sndsprk_link_sound_to_object2( int org_soundnum, short objnum, int forever, fix max_volume, fix max_distance ) +{ + fix path_distance; + float distance, elevation, azimuth; + int num_search_segs = 20; + + calc_polar_sound(&Objects[objnum].pos, &azimuth, &elevation); + path_distance = find_connected_distance(&Viewer->pos, Viewer->segnum, &Objects[objnum].pos, Objects[objnum].segnum, num_search_segs, WID_RENDPAST_FLAG ); + distance = f2fl(path_distance); + if(!forever && distance > 0) { + clamp(distance, MIN_SOUND_DISTANCE, MAX_SOUND_DISTANCE); + sndsprk_play_sample_3d( org_soundnum, distance, elevation, azimuth); + } +} +int sndsprk_link_sound_to_pos2( int org_soundnum, short sound_seg, short sidenum, vms_vector * sound_pos, int forever, + fix max_volume, fix max_distance ) +{ + fix path_distance; + float distance, elevation, azimuth; + int num_search_segs = 20; + int i; + + if(num_sound_objects > MAX_SOUND_OBJECTS) +// Error("Max sound objects exceeded"); + return -1; + + path_distance = find_connected_distance(&Viewer->pos, Viewer->segnum, sound_pos, sound_seg, num_search_segs, WID_RENDPAST_FLAG ); + if(!forever) { + if( path_distance >= 0 ) { + distance = f2fl(path_distance); + clamp(distance, MIN_SOUND_DISTANCE, MAX_SOUND_DISTANCE); + calc_polar_sound(sound_pos, &azimuth, &elevation); + sndsprk_play_sample_3d( org_soundnum, distance, elevation, azimuth); + } + return -1; + } + + SoundObjects[num_sound_objects].signature=next_signature++; + SoundObjects[num_sound_objects].flags = SOF_USED | SOF_LINK_TO_POS; + if ( forever ) + SoundObjects[num_sound_objects].flags |= SOF_PLAY_FOREVER; + SoundObjects[num_sound_objects].link_type.pos.segnum = sound_seg; + SoundObjects[num_sound_objects].link_type.pos.sidenum = sidenum; + SoundObjects[num_sound_objects].link_type.pos.position = *sound_pos; + SoundObjects[num_sound_objects].soundnum = (short)org_soundnum; + SoundObjects[num_sound_objects].max_volume = max_volume; + SoundObjects[num_sound_objects].max_distance = max_distance; + SoundObjects[num_sound_objects].distance = path_distance; + SoundObjects[num_sound_objects].volume = 0; + SoundObjects[num_sound_objects].pan = 0; + num_sound_objects++; + +// sndsprk_get_sound_loc( &Viewer->orient, &Viewer->pos, Viewer->segnum, +// &SoundObjects[i].link_type.pos.position, SoundObjects[i].link_type.pos.segnum, SoundObjects[i].max_volume, +// &SoundObjects[i].volume, &SoundObjects[i].pan, SoundObjects[i].max_distance ); + +// sndsprk_start_sound_object(i); +// sndsprk_play_loop_3d( org_soundnum, distance, elevation, azimuth); + return SoundObjects[num_sound_objects].signature; +} +void calc_polar_sound(vms_vector * sound_pos, float *azimuth, float *elevation) +{ + vms_vector vector_to_sound; + vms_vector cross_vec; + fix distfix; + fix fixdot; +// fix updot, rtdot; +// float float_dot; + fix cosang,sinang; + fixang ang_from_ear; + distfix = vm_vec_normalized_dir_quick( &vector_to_sound, sound_pos, &Viewer->pos ); +// vm_vec_sub( &vector_to_sound, sound_pos, &Viewer->pos); +// distfix = vm_vec_normalize_quick(&vector_to_sound); + fixdot = vm_vec_dot(&(Viewer->orient.rvec), &vector_to_sound); + + ang_from_ear = fix_acos(fixdot); + fix_sincos(ang_from_ear, &sinang, &cosang); + *azimuth = f2fl(cosang); + *elevation = f2fl(sinang); +// mprintf((0, "sndvec %f %f\n", *azimuth*57, *elevation*57)); +/* + fixdot = vm_vec_dot(&(Viewer->orient.fvec), &vector_to_sound); + vm_vec_cross(&cross_vec, &(Viewer->orient.uvec), &vector_to_sound); + float_dot = f2fl(fixdot); + updot = vm_vec_dot(&cross_vec,&(Viewer->orient.fvec)); + if (updot < 0) + *azimuth = -acosf(float_dot); + else + *azimuth = acosf(float_dot); + + rtdot = vm_vec_dot(&cross_vec,&(Viewer->orient.rvec)); + if (rtdot < 0) + *elevation = -asinf(float_dot); + else + *elevation = asinf(float_dot); +*/ +#ifdef DEBUG_ON +// mprintf((0, "az %f ", *azimuth*57.295780)); +// mprintf((0, "el %f ", *elevation*57.295780)); +// mprintf((0, "| %f %f %", f2fl(angle_from_ear), f2fl(cosang)*57.295780,f2fl(sinang)*57.295780)); +// mprintf((0, "\n")); +#endif +} +#define MAX_AMBIENT_DISTANCE 0xFF0000 //DAJ is 255 +void sndsprk_sync_sounds() +{ + int i; + int oldvolume, oldpan; + SndCommand snd_cmd; + vms_vector pnt; + int set = 0; + fix path_distance; + fix closet_dist[3] = {MAX_AMBIENT_DISTANCE, MAX_AMBIENT_DISTANCE, MAX_AMBIENT_DISTANCE}; + fix direct_distance; + fix found; + int num_search_segs = 4; + float elevation, azimuth; + int closet_sound[3] = {-1, -1, -1}; + static byte was_playing[3] = {0, 0, 0}; + int chan; + + if (!sndsprk_initialized) + return; + + if(!num_sound_objects) + return; + + //find the closest ambient sounds + for (i=0; ipos, &SoundObjects[i].link_type.pos.position); + if(SoundObjects[i].distance <= MAX_AMBIENT_DISTANCE) { + if(SoundObjects[i].soundnum == 224 || + SoundObjects[i].soundnum == 41) + chan = 0; + else if(SoundObjects[i].soundnum == 235 || + SoundObjects[i].soundnum == 150 ) + chan = 1; + else if( SoundObjects[i].soundnum == 121 || + SoundObjects[i].soundnum == 42 ) + chan = 2; + else { + mprintf((STDOUT, "%d is not one of the standard ambient sounds\n", SoundObjects[i].soundnum)); + continue; + } + found = find_connected_distance(&Viewer->pos, Viewer->segnum, &SoundObjects[i].link_type.pos.position, + SoundObjects[i].link_type.pos.segnum, num_search_segs, WID_RENDPAST_FLAG ); + if(found > 0 && SoundObjects[i].distance < closet_dist[chan]) { + closet_dist[chan] = SoundObjects[i].distance; + closet_sound[chan] = i; + } + } + } + } + // now play them + for(i=0; i<3; i++) { + int segnum, sidenum, wallnum; + if((closet_sound[i] != -1) && (closet_dist[i] > 0) && (closet_dist[i] < MAX_AMBIENT_DISTANCE)) { + // first is it a valid wall number then look to see if we turned off the force field + segnum = SoundObjects[closet_sound[i]].link_type.pos.segnum; + sidenum = SoundObjects[closet_sound[i]].link_type.pos.sidenum; + wallnum = Segments[segnum].sides[sidenum].wall_num; + if((wallnum != -1) && (Walls[wallnum].flags & WALL_ILLUSION_OFF)) { + sndsprk_stop_loop(i); + was_playing[i] = 0; + } else { + calc_polar_sound(&SoundObjects[closet_sound[i]].link_type.pos.position, &azimuth, &elevation); + sndsprk_play_loop_3d(i, SoundObjects[closet_sound[i]].soundnum, f2fl(closet_dist[i]), elevation, azimuth); + was_playing[i] = 1; + } + } + else if (was_playing[i]) { + sndsprk_stop_loop(i); + was_playing[i] = 0; + } + } +} +void sndsprk_kill_sound_linked_to_segment( int segnum, int sidenum, int soundnum ) +{ + sndsprk_stop_loop(0); +} +void sndsprk_kill_sound_linked_to_object( int objnum ) +{ +} +void sndsprk_close() +{ + int i; + if (!sndsprk_initialized) + return; + sndsprk_stop_current_song(); + sndsprk_stop_loop(0); + sndsprk_stop_loop(1); + sndsprk_stop_loop(2); + for(i=0; iloopStart = 128; + else + ((SoundHeaderPtr)(sound_ptr[i]))->loopStart = 0; + ((SoundHeaderPtr)(sound_ptr[i]))->loopEnd = soundDataSize-24; + ((SoundHeaderPtr)(sound_ptr[i]))->baseFrequency = 60; + num_sounds++; +// HoldMemory(sound_ptr[i], soundDataSize); + } else { + Error("Unable to allocate sound_ptr %d", i); + } + } else { + sound_ptr[i] = NULL; + } + } +} +int sndsprk_init() +{ + int i; + OSStatus err; + extern UInt16 gNumTracks; + + GetDefaultOutputVolume(&master_save); + + RedbookHandlerInit(); + if(RedbookIsCDInserted()) { + have_descent_cd = true; + gNumTracks = RedbookGetNumTracks(); + } + CD_volume = redbook_get_volume(); + + sndsprk_initialized = 1; + + for (i = 0; i < MAX_SOUND_CHANNELS; i++) { + snd_channel_near[i] = NULL; + snd_channel_far[i] = NULL; + } + farLocalization.cpuLoad = 0; + farLocalization.medium = kSSpMedium_Air; + farLocalization.humidity = 0; + farLocalization.roomSize = 100; + farLocalization.roomReflectivity = -100; + farLocalization.reverbAttenuation = 0; + farLocalization.sourceMode = kSSpSourceMode_Localized; + farLocalization.referenceDistance = 10; + farLocalization.coneAngleCos = 0; + farLocalization.coneAttenuation = 0; + farLocalization.currentLocation.elevation = 0; + farLocalization.currentLocation.azimuth = 0; + farLocalization.currentLocation.distance = 1; + farLocalization.currentLocation.projectionAngle = 1; + farLocalization.currentLocation.sourceVelocity = 0; + farLocalization.currentLocation.listenerVelocity = 0; + farLocalization.reserved0 = 0; + farLocalization.reserved1 = 0; + farLocalization.reserved2 = 0; + farLocalization.reserved3 = 0; + farLocalization.virtualSourceCount = 0; + nearLocalization.cpuLoad = 0; + nearLocalization.medium = kSSpMedium_Air; + nearLocalization.humidity = 0; + nearLocalization.roomSize = 0; + nearLocalization.roomReflectivity = 0; + nearLocalization.reverbAttenuation = 0; + nearLocalization.sourceMode = kSSpSourceMode_Ambient; + nearLocalization.referenceDistance = 5; + nearLocalization.coneAngleCos = 0; + nearLocalization.coneAttenuation = 0; + nearLocalization.currentLocation.elevation = 0; + nearLocalization.currentLocation.azimuth = 0; + nearLocalization.currentLocation.distance = 5; + nearLocalization.currentLocation.projectionAngle = 1; + nearLocalization.currentLocation.sourceVelocity = 0; + nearLocalization.currentLocation.listenerVelocity = 0; + nearLocalization.reserved0 = 0; + nearLocalization.reserved1 = 0; + nearLocalization.reserved2 = 0; + nearLocalization.reserved3 = 0; + nearLocalization.virtualSourceCount = 0; + ambientLocalization.cpuLoad = 0; + ambientLocalization.medium = kSSpMedium_Air; + ambientLocalization.humidity = 0; + ambientLocalization.roomSize = 0; + ambientLocalization.roomReflectivity = 0; + ambientLocalization.reverbAttenuation = 0; + ambientLocalization.sourceMode = kSSpSourceMode_Localized; + ambientLocalization.referenceDistance = 10; + ambientLocalization.coneAngleCos = 0; + ambientLocalization.coneAttenuation = 0; + ambientLocalization.currentLocation.elevation = 0; + ambientLocalization.currentLocation.azimuth = 0; + ambientLocalization.currentLocation.distance = 1; + ambientLocalization.currentLocation.projectionAngle = 1; + ambientLocalization.currentLocation.sourceVelocity = 0; + ambientLocalization.currentLocation.listenerVelocity = 0; + ambientLocalization.reserved0 = 0; + ambientLocalization.reserved1 = 0; + ambientLocalization.reserved2 = 0; + ambientLocalization.reserved3 = 0; + ambientLocalization.virtualSourceCount = 0; + atexit(sndsprk_close); + return 0; + +} +OSErr +ConvertFromWavToRawSound(void* inBufferP, UInt32 inBuffSize, void* outBufferP, UInt32* outBuffSize) +{ + OSErr theErr; + SoundConverter theSC; + UInt32 numFrames, numBytes, outBytes; + UInt32 aditionalOutputFrames, additionalOutputSize; + SInt32 currentBytes, bytesLeft; + SoundComponentData input, output; //these hold info for the formats + input.flags = 0; + input.format = MicrosoftADPCMFormat; /*one of the wav formats*/ + input.numChannels = 2; + input.sampleSize = 16; /*8 for eight bit sound*/ + input.sampleRate = rate44khz; /* a constant defined in Sound.h*/ + input.sampleCount = 0; + input.buffer = 0; + input.reserved = 0; + output.flags = 0; + output.format = kSoundNotCompressed; /*'raw ' */ + output.numChannels = 2; + output.sampleSize = 16; /*8 for eight bit sound*/ + output.sampleRate = rate44khz; /* a constant defined in Sound.h*/ + output.sampleCount = 0; + output.buffer = 0; + output.reserved = 0; + /*This opens a SoundConverter component. the params are ... + output format, output format, and the sound converter*/ + theErr = SoundConverterOpen(&input, &output, &theSC); + if(theErr) return theErr; + /* so how to allocate the new buffer? */ + theErr = SoundConverterGetBufferSize(theSC, inBuffSize, &numFrames, &numBytes, outBuffSize); + if(theErr) return theErr; + + outBuffer = mem_malloc(outBytes); + if(!outBuffer) { + mprintf((0, "ConvertFromWavToRawSound: unable to allocate %d bytes", outBytes)); + Int3(); + } + theErr = SoundConverterBeginConversion(theSC); + if(theErr) return theErr; + + if(inBuffSize <= outBytes) { /*we can do it in one big chunk...*/ + theErr = SoundConverterConvertBuffer(theSC, inBufferP, numFrames, outBufferP, 0, 0); + if(theErr) return theErr; + + theErr = SoundConverterEndConversion(theSC, outBufferP, &aditionalOutputFrames, &additionalOutputSize); + if(theErr) return theErr; + } + else { /*we need to do it in chunks :( */ + bytesLeft = *outBuffSize; + while(bytesLeft > 0) { + theErr = SoundConvertBuffer(theSC, inBufferP, numFrames, numBytes, outBufferP, 0, 0); + if(theErr) return theErr; + + bytesLeft -= numBytes; + } + theErr = SoundConverterEndConversion(theSC, outBufferP, &aditionalOutputFrames, &additionalOutputSize); + if(theErr) return theErr; + } + return theErr; +} diff --git a/mac/SYSSTAT.H b/mac/SYSSTAT.H new file mode 100644 index 000000000..514c7abb3 --- /dev/null +++ b/mac/SYSSTAT.H @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/mac/maccore.cpp b/mac/maccore.cpp new file mode 100644 index 000000000..b78de3462 --- /dev/null +++ b/mac/maccore.cpp @@ -0,0 +1,159 @@ +/* + * $Logfile: /DescentIII/Main/mac/maccore.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:14 $ + * $Author: kevinb $ + * + * Operating system management library MacOS + * + * $Log: maccore.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:58:14 kevinb + * initial 1.5 import + * + * + * 3 10/21/99 1:55p Kevin + * Mac Merge! + * + * 3 5/15/97 1:49 AM Jeremy + * added some macintosh memory initialization to macOsObject and removed + * keyboard handler (to put it into the d3MacOsObject) + * + * 2 5/9/97 7:16 PM Jeremy + * #defines of some constants and functions from the dos world + * + * 1 2/28/97 12:16 PM Jeremy + * MacOS specific OS object libraries + * + * $NoKeywords: $ + */ + +// ========================= +// ANSI Headers +// ========================= +#include +#include + +// ========================= +// Macintosh System Headers +// ========================= +#include +#include +#include +#include +#include +#include +#include + +// ========================= +// D3 Headers +// ========================= +#include "gameos.h" +#include "mono.h" +#include "pserror.h" + +// ========================= +// File Level Globals +// ========================= + +// ========================= +// Private Function Prototypes +// ========================= + +// ========================= +// Function Definitions +// ========================= + +void +osMacObject::os_init(void) +{ + ; +} + +osMacObject::osMacObject(void) +{ + ::InitGraf(&(qd.thePort)); + ::InitFonts(); + ::InitWindows(); + ::InitMenus(); + ::TEInit(); + ::InitDialogs(nil); + ::InitCursor(); + + ::MaxApplZone(); + ::MoreMasters(); + ::MoreMasters(); + ::MoreMasters(); + ::MoreMasters(); +} + +osMacObject::~osMacObject(void) +{ + ; +} + +void +osMacObject::init(osObject *parent_os, void *info) +{ + ; +} + +bool +osMacObject::create(osObject *parent_os, void *info) +{ + bool success = true; + + + return (success); +} + +gameos_packet* +osMacObject::defer() +{ + gameos_packet* packet = NULL; + + return packet; +} + +void +osMacObject::get_info(void *info, int size_str) +{ + ASSERT(info); + + if (size_str > 0) + { + memset(info, 0, size_str); + } +} + +/******************************************* + Utility functions specific to the Mac OS +*******************************************/ +int +stricmp(const char* s1, const char* s2) +{ + char c1, c2; + while (1) + { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + } +} + +int +strnicmp(const char *s1, const char *s2, int n) +{ + int i; + char c1, c2; + for (i=0; i c2) return 1; + if (!c1) return 0; + } + return 0; +} \ No newline at end of file diff --git a/mac/macdatabase.cpp b/mac/macdatabase.cpp new file mode 100644 index 000000000..3c03051b0 --- /dev/null +++ b/mac/macdatabase.cpp @@ -0,0 +1,562 @@ +/* + * $Logfile: /DescentIII/Main/mac/macdatabase.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:15 $ + * $Author: kevinb $ + * + * Mac Database objects functions + * + * $Log: macdatabase.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:58:15 kevinb + * initial 1.5 import + * + * + * 3 10/21/99 1:55p Kevin + * Mac Merge! + * + * 2 7/28/99 2:51p Kevin + * + * 3 5/15/97 2:34 PM Jeremy + * made database return user name or default user name if no user name in + * database + * + * 2 5/15/97 1:50 AM Jeremy + * correct and more complete implementation of macOSDatabase object which + * uses the mac resource manager to store entries in the application + * preferences file (used like the windows registry file) + * + * 1 4/11/97 4:12 PM Jeremy + * initial checkin + * + * $NoKeywords: $ + */ + +// ANSI Headers +#include +#include +#include + +// Macintosh Headers +#include +#include +#include +#include +#include +#include + +// MacPlayLib Headers +#include "PString.h" +#include "File Utils.h" + +// Descent 3 Headers +#include "mono.h" +#include "pserror.h" +#include "gameos.h" + +//#define ASSERT(x) +//#define mprintf(x) +//#define _MAX_FNAME 32 +//#define _MAX_DIR 32 +//typedef unsigned long ulong; +//#include +//#include +//#include +//#include +//#include + +// ---------------------------------------------------------------------------- +// Internal Data Types +// ---------------------------------------------------------------------------- +class CDatabaseErr +{ + public: + CDatabaseErr(char* inErrStr = NULL, OSErr inMacErr = noErr) + { + mErrStr = inErrStr; + mMacErr = inMacErr; + } + + char* mErrStr; + OSErr mMacErr; +}; + + +// ---------------------------------------------------------------------------- +// Functions +// ---------------------------------------------------------------------------- + +osMacDatabase::osMacDatabase(void) +{ + mprintf((0, "Creating mac database object\n")); + + mInitted = false; + + mAppSignature = '????'; + + memset(mPrefsFileName, 0, sizeof(mPrefsFileName)); + memset(mPrefsFolderName, 0, sizeof(mPrefsFolderName)); + mPrefsFileType = '????'; + memset(&mPrefsFileSpec, 0, sizeof(mPrefsFileSpec)); + mPrefsFileRefNum = -1; +} + +osMacDatabase::~osMacDatabase(void) +{ + mprintf((0, "Destroying mac database object\n")); +} + +bool +osMacDatabase::init(void) +{ + bool success = false; + OSErr err = noErr; + ProcessSerialNumber myPSN; + ProcessInfoRec myInfo; + + // Get information about the current process (this app) + err = GetCurrentProcess(&myPSN); + + memset(&myInfo, 0, sizeof(myInfo)); + myInfo.processInfoLength = sizeof(myInfo); + + err = GetProcessInformation(&myPSN, &myInfo); + if (!err) + { + bool prefsSuccess = false; + + mAppSignature = myInfo.processSignature; + + prefsSuccess = FillOutPrefsInfo(); + + if (prefsSuccess) + { + InitPrefsFile(); + success = true; + } + } + + mInitted = true; + + return mInitted; +} + +// read either an integer or string from the current record +bool +osMacDatabase::read(const char *label, char *entry, int *entrylen) +{ + return ReadDataFromResourceFork(label, entry, entrylen); +} + +bool +osMacDatabase::read(const char *label, int *entry) +{ + int intSize = sizeof(int); + + return ReadDataFromResourceFork(label, entry, &intSize); +} + +// read either an integer or string from the current record +bool +osMacDatabase::ReadDataFromResourceFork(const char* label, void* entry, int* entrylen) +{ + ASSERT(label); + ASSERT(entry); + ASSERT(entrylen); + + short saveResFile = CurResFile(); + OSErr err = noErr; + bool success = false; + + try + { + //¥ Start by terminating the entry in case anything goes wrong + ((char*) entry)[0] = 0; + + //¥ Open the resource file (must be closed later) + mPrefsFileRefNum = FSpOpenResFile(&mPrefsFileSpec, fsRdWrPerm); + if (mPrefsFileRefNum == -1) + { + err = ResError(); + + if (err) + { + throw (CDatabaseErr("An error occurred opening the prefs file resource fork.", err)); + } + } + + //¥ Switch to the prefs file's resource fork + UseResFile(mPrefsFileRefNum); + err = ResError(); + if (err) + { + throw (CDatabaseErr("Could not switch to database resource file.", err)); + } + + //¥ Convert the name string from a c to a pascal style string + Str255 labelPStr = "\p"; + CPstrcpy(labelPStr, (char*) label); + + //¥ See if this resource exists in the prefs file database + Handle dataHandle = nil; + dataHandle = Get1NamedResource(kMacDatabaseResourceType, labelPStr); + err = ResError(); + if (err == resNotFound) + { + mprintf((0, "Could not find label %s in the database.\n", label)); + throw (CDatabaseErr("Entry does not exist.", err)); + } + else if (err) + { + mprintf((0, "LABEL: %s\n", label)); + throw (CDatabaseErr("Error getting label %s from the database.", err)); + } + + ASSERT(dataHandle); + + //¥ Lock the handle down in memory + HLock(dataHandle); + + //¥ Copy the data to the buffer passed in by the database user + // Note! Only copies up to as many bytes as specified by entrylen initially, returns the + // actual size of the data in entrylen on completion + int dataSize = GetHandleSize(dataHandle); + *entrylen = (*entrylen < dataSize) ? *entrylen : dataSize; + BlockMove(*dataHandle, entry, *entrylen); + + //¥ Unlock the memory handle + HUnlock(dataHandle); + + success = true; + } + catch (CDatabaseErr databaseErr) + { + mprintf((0, "An error occurred reading from the mac resource database: %d\n", databaseErr.mMacErr)); + mprintf((0, databaseErr.mErrStr)); + mprintf((0, "\n")); + success = false; + } + + CloseResFile(mPrefsFileRefNum); + mPrefsFileRefNum = -1; + + //¥ Restore the original resource file + UseResFile(saveResFile); + + return success; +} + +// write either an integer or string to a record. +bool +osMacDatabase::write(const char *label, char *entry, int entrylen) +{ + return WriteDataToResourceFork(label, entry, entrylen); +} + +bool +osMacDatabase::write(const char *label, int *entry) +{ + return WriteDataToResourceFork(label, entry, sizeof(int)); +} + +bool +osMacDatabase::WriteDataToResourceFork(const char* label, void* entry, int entrylen) +{ + short saveResFile = CurResFile(); + OSErr err = noErr; + bool success = false; + + try + { + //¥ Open the resource file (must be closed later in destructor) + mPrefsFileRefNum = FSpOpenResFile(&mPrefsFileSpec, fsRdWrPerm); + if (mPrefsFileRefNum == -1) + { + err = ResError(); + + if (err) + { + throw (CDatabaseErr("An error occurred opening the prefs file resource fork.", err)); + } + } + + //¥ Switch to the prefs file's resource fork + UseResFile(mPrefsFileRefNum); + err = ResError(); + if (err) + { + throw (CDatabaseErr("Could not switch to database resource file.", err)); + } + + //¥ Convert the name string from a c to a pascal style string + Str255 labelPStr = "\p"; + CPstrcpy(labelPStr, (char*) label); + + //¥ See if this resource already exists in the prefs file database + Handle oldHandle = nil; + oldHandle = Get1NamedResource(kMacDatabaseResourceType, labelPStr); + err = ResError(); + if (err && (err != resNotFound)) + { + throw (CDatabaseErr("Could not check if database entry already exists.", err)); + } + + if (oldHandle != nil) // If this resource exists, then delete it + { + //¥ Now delete the old resource + RemoveResource(oldHandle); + err = ResError(); + if (err) + { + throw (CDatabaseErr("Could not get delete previously existing database entry", err)); + } + + //¥ Write the changes to disk + UpdateResFile(mPrefsFileRefNum); + err = ResError(); + if (err) + { + throw (CDatabaseErr("Could add write mac database resource to disk.", err)); + } + } + + //¥ Create a handle to store the entry in + Handle entryH = nil; + entryH = NewHandleClear(entrylen); + if (!entryH) + { + throw (CDatabaseErr("Could not allocate memory for new database entry", nilHandleErr)); + } + + //¥ Lock the handle down in memory + HLock(entryH); + + //¥ Copy the new entry data to a handle to be added as a named resource + BlockMove(entry, *entryH, entrylen); + + //¥ Get a unique ID for this resource + short entryResID = 0; + entryResID = Unique1ID(kMacDatabaseResourceType); + + //¥ Add the resource to the database + AddResource(entryH, kMacDatabaseResourceType, entryResID, labelPStr); + err = ResError(); + if (err) + { + throw (CDatabaseErr("Could not add entry to mac database resource file.", err)); + } + + //¥ Write the changes to disk + UpdateResFile(mPrefsFileRefNum); + err = ResError(); + if (err) + { + throw (CDatabaseErr("Could add write mac database resource to disk.", err)); + } + + //¥ Unlock the memory handle + HUnlock(entryH); + + //¥ Dispose of the memory handle + DisposeHandle(entryH); + + success = true; + } + catch (CDatabaseErr databaseErr) + { + mprintf((0, "An error occurred writing to the mac resource database: %d\n", databaseErr.mMacErr)); + mprintf((0, databaseErr.mErrStr)); + mprintf((0, "\n")); + success = false; + } + + CloseResFile(mPrefsFileRefNum); + mPrefsFileRefNum = -1; + + //¥ Restore the original resource file + UseResFile(saveResFile); + + return success; +} + + + +// get the current user's name from the os +void +osMacDatabase::get_user_name(char* buffer, ulong* size) +{ + bool success = false; + + success = read("user name", buffer, (int*) size); + + if (!success) + { + mprintf((0, "Error reading user name from data base!\n")); + mprintf((0, "Using default name\n")); + + char defaultName[100] = "MacUser"; + strncpy(buffer, defaultName, *size); + *size = strlen(defaultName) + 1; + } +} + +// creates an empty classification or structure where you can store information +bool +osMacDatabase::create_record(const char *pathname) +{ + bool result = false; + + + return result; +} + +// set current database focus to a particular record +bool +osMacDatabase::lookup_record(const char *pathname) +{ + bool result = false; + + return result; +} + +bool +osMacDatabase::FillOutPrefsInfo(void) +{ + ProcessSerialNumber thePSN; + ProcessInfoRec thePIR; + FSSpec appSpec; + OSErr theErr = noErr; + bool success = false; + + //¥ Get information about the current process (this app) + theErr = GetCurrentProcess(&thePSN); + thePIR.processName = nil; + thePIR.processInfoLength = sizeof(ProcessInfoRec); + thePIR.processAppSpec = &appSpec; + + theErr = GetProcessInformation(&thePSN, &thePIR); + if (!theErr) + { + //¥ Make sure the constructed name will not overflow the maximum file/folder length + Str31 fileTag = "\p Prefs"; + Str31 folderTag = "\p Preferences"; + int fileTagLength = (int) fileTag[0]; + int folderTagLength = (int) folderTag[0]; + + Str255 tempAppName ="\p"; + int tempAppNameLength = 0; + + + + //¥ Shorten the app name length until it will fit into the file name + Pstrcpy(tempAppName, appSpec.name); + tempAppNameLength = tempAppName[0]; + while (fileTagLength + tempAppNameLength >= _MAX_FNAME) + { + tempAppNameLength--; + } + tempAppName[0] = tempAppNameLength; + + //¥ Construct the Prefs File Name + Pstrcpy(mPrefsFileName, tempAppName); + Pstrcat(mPrefsFileName, fileTag); + + + + + //¥ Shorten the app name length until it will fit into the folder name + Pstrcpy(tempAppName, appSpec.name); + tempAppNameLength = tempAppName[0]; + while (folderTagLength + tempAppNameLength >= _MAX_DIR) + { + tempAppNameLength--; + } + tempAppName[0] = tempAppNameLength; + + //¥ Construct the Prefs Folder Name + Pstrcpy(mPrefsFolderName, tempAppName); + Pstrcat(mPrefsFolderName, folderTag); + + success = true; + } + + return success; +} + +bool +osMacDatabase::InitPrefsFile(void) +{ + long newDirID = 0; + short prefVRefNum = 0; + long prefDirID = 0; + OSErr theErr = noErr; + bool success = false; + + try + { + //¥ Find the System "Preferences" folder + theErr = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &prefVRefNum, &prefDirID); + + //¥ Find (or make) the app prefs folder in the Preferences folder + mPrefsFileSpec.vRefNum = prefVRefNum; + mPrefsFileSpec.parID = prefDirID; + Pstrcpy(mPrefsFileSpec.name, mPrefsFolderName); + + theErr = NormalizeFolderSpec(&mPrefsFileSpec); + if (theErr) + { + if (theErr != fnfErr) + { + // A Real Error Occurred + throw (CDatabaseErr("An Error occurred getting at the preferences folder", theErr)); + } + + //¥ Create the app prefs folder in the Preferences folder + theErr = FSpDirCreate(&mPrefsFileSpec, smCurrentScript, &newDirID); + + if (theErr) + { + throw (CDatabaseErr("Error creating preferences folder", theErr)); + } + + mPrefsFileSpec.parID = newDirID; + } + + //¥ At this point we should have a valid FSSpec for items inside the app prefs folder + Pstrcpy(mPrefsFileSpec.name, mPrefsFileName); + + //¥ If we make it here OK, create Preferences file if necessary + theErr = FSpCreate(&mPrefsFileSpec, mAppSignature, mPrefsFileType, smCurrentScript); + + //¥ If there was no error, then file did not already exist, + // and we must create the resource map in the file + if (!theErr) + { + FSpCreateResFile(&mPrefsFileSpec, mAppSignature, mPrefsFileType, smCurrentScript); + theErr = ResError(); + + if (theErr) + { + throw (CDatabaseErr("An error occurred creating the prefs file resource map.", theErr)); + } + } + else + { + if (theErr != dupFNErr) // If the error is not "duplicate file error" then there is a problem + { + // We have a real error + throw (CDatabaseErr("An error occurred creating the prefs file.", theErr)); + } + } + + success = true; + } + catch (CDatabaseErr databaseErr) + { + mprintf((0, "ERROR in osMacDatabase::InitPrefsFile: %d\n", databaseErr.mMacErr)); + mprintf((0, databaseErr.mErrStr)); + success = false; + } + + return success; +} + diff --git a/mac/macdebug.cpp b/mac/macdebug.cpp new file mode 100644 index 000000000..41b50bf40 --- /dev/null +++ b/mac/macdebug.cpp @@ -0,0 +1,138 @@ +/* + * $Logfile: /DescentIII/Main/mac/macdebug.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:15 $ + * $Author: kevinb $ + * + * MacOS Debugging routines + * + * $Log: macdebug.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:58:15 kevinb + * initial 1.5 import + * + * + * 4 4/12/00 7:08p Matt + * From Duane for 1.4 + * + * 3 10/21/99 1:55p Kevin + * Mac Merge! + * + * 2 7/28/99 2:51p Kevin + * + * 4 5/15/97 1:51 AM Jeremy + * made osMessage/Error box correctly handle newlines and added some error + * checking for strings which are too long to fit into pascal strings + * + * 3 5/9/97 8:04 PM Jeremy + * changed include of to + * + * 2 3/21/97 6:53 PM Jeremy + * First crack at implementing debugging and console io routines. + * Currently the functions display a mac dialog box for errors and + * messages and send all console messages through the macintosh's modem + * port. + * + * 1 2/28/97 12:16 PM Jeremy + * MacOS specific Debug object libraries + * + * $NoKeywords: $ + */ + +#include "debug.h" +#include "mono.h" +#include +#include +#include +#include + +#include + +extern int debug_level; + +void setup_sioux(void) +{ + SIOUXSettings.standalone = true; + SIOUXSettings.setupmenus = true; + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.toppixel = 526; + SIOUXSettings.leftpixel =10; + SIOUXSettings.rows = 20; + SIOUXSettings.columns = 120; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Debug_break=false; +bool Debug_mono=false; + +char *Debug_DumpInfo(); +#include "stringtable.h" +#include "newui_core.h" +#include "newui.h" +#include "descent.h" +#include "renderer.h" + +// if we are running under a debugger, then pass true +bool Debug_Init(bool debugger, bool mono_debug) +{ +#ifdef DAJ_DEBUG +//#ifndef RELEASE + Debug_break = debugger; + + if (mono_debug) { + + mprintf((0, "Linux system.\n")); + } + + if (Debug_break) + mprintf((0, "Debug Break enabled.\n")); + +#endif //ifndef RELEASE + + return true; +} + +//Does a messagebox with a stack dump +//Messagebox shows topstring, then stack dump, then bottomstring +//Return types are the same as the Windows return values +int Debug_ErrorBox(int type,const char *title,const char *topstring,const char *bottomstring) +{ + int answer = 0; +// char *dumptext = Debug_DumpInfo(); + + Str255 debug_str; + sprintf((char *)debug_str,"%s:%s\r\n%s",title,topstring,bottomstring); +#ifdef DAJ_DEBUG + DebugStr(debug_str); +#else +// if (GetFunctionMode() > INIT_MODE) { + rend_Close(); + ::ShowCursor(); + ::ParamText(c2pstr(topstring), c2pstr(bottomstring), NULL, NULL); + ::Alert(911, NULL); +// } else { +// DoMessageBox(TXT_ERROR, (char *)debug_str, MSGBOX_OK, UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); +// } + exit(0); +// fprintf(stderr,"%s:%s\r\n\r\n%s\r\n\r\n%s",title,topstring,dumptext,bottomstring); +#endif +// debug_break(); + + return answer; +} + +// displays an message box +// Returns the same values as the Win32 MessageBox() function +int Debug_MessageBox(int type, const char *title, const char *str) +{ + return Debug_ErrorBox(type,str,"Descent 3 Message",""); +} + +/////////////////////////////////////////////////////////////////////////////// + +char *Debug_DumpInfo(void) +{ + static char e[]= "System Error"; + return e; +} + diff --git a/mac_sndlib/mac_llsound.cpp b/mac_sndlib/mac_llsound.cpp new file mode 100644 index 000000000..413cb7c12 --- /dev/null +++ b/mac_sndlib/mac_llsound.cpp @@ -0,0 +1,168 @@ +/* + * $Logfile: /Descent3/main/mac_sndlib/mac_llsound.cpp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/08/26 03:58:16 $ + * $Author: kevinb $ + * + * Mac implementation of low level sound library + * + * $Log: mac_llsound.cpp,v $ + * Revision 1.1.1.1 2003/08/26 03:58:16 kevinb + * initial 1.5 import + * + * + * 1 5/21/97 6:53 PM Jeremy + * + * $NoKeywords: $ +*/ + +#include "mac_llsound.h" + +// Starts the sound library, maybe have it send back some information -- 3d support? +int +mac_llsSystem::InitSoundLib(osObject *sos, unsigned char max_sounds_played, + unsigned char max_sounds_cached, float volume) +{ + return 0; +} + +// Changes the sound list -- cmphack +void +mac_llsSystem::NewSoundList(unsigned short num_sounds, unsigned int *sound_offset_array, + char *sound_data, unsigned int sound_data_size) +{ + ; +} + +// Cleans up after the Sound Library +void +mac_llsSystem::DestroySoundLib(void) +{ + ; +} + + +// Gets info on a sound index +void +mac_llsSystem::GetSoundMaxMinDist(int sound_index, float *min_dist, float *max_dist) +{ + ; +} + + +void +mac_llsSystem::GetSoundConeInfo(int sound_index, unsigned short *inner_cone_angle, unsigned short *outer_cone_angle, float outer_volume) +{ + ; +} + + +// Plays a 2d sound +int +mac_llsSystem::PlaySound2d(int sound_index, float volume, float pan, unsigned short frequency) +{ + return 0; +} + + +// Plays a 3d sound +int +mac_llsSystem::PlaySound3d(int sound_index, pos_state *cur_pos, unsigned short frequency) +{ + return -1; +} + +void +mac_llsSystem::StopAllSounds(void) +{ + ; +} + +// Checks if a sound is playing (removes finished sound); +int +mac_llsSystem::IsSoundInstancePlaying(int sound_uid) +{ + return -1; +} + + +int +mac_llsSystem::IsSoundPlaying(int sound_index) +{ + return -1; +} + + +// Set listener's position +void +mac_llsSystem::SetListener(pos_state cur_pos) +{ + ; +} + + +// Sync's a single sound (changes something - frequency, volume, pan 3d stuff) +void +mac_llsSystem::AdjustSound(int sound_uid, float volume, float pan, unsigned short frequency) +{ + ; +} + +void +mac_llsSystem::AdjustSound(int sound_uid, pos_state cur_pos) +{ + ; +} + +// Stops 2d and 3d sounds +void +mac_llsSystem::StopSound(int sound_uid, unsigned char f_immediately) +{ + ; +} + +// Sets the master volume (2d and 3d sounds) -- chrishack -- use primary buffer +void +mac_llsSystem::SetMasterVolume(float volume) +{ + ; +} + +// Gets the master volume +float +mac_llsSystem::GetMasterVolume(void) +{ + return 0.0; +} + + +// Pause all sounds/resume all sounds +void +mac_llsSystem::PauseSounds(void) +{ + ; +} + + +void +mac_llsSystem::ResumeSounds(void) +{ + ; +} + + +// Begin sound frame +void +mac_llsSystem::SoundStartFrame(void) +{ + ; +} + + +// End sound frame +void +mac_llsSystem::SoundEndFrame(void) +{ + ; +} + diff --git a/manage/CMakeLists.txt b/manage/CMakeLists.txt new file mode 100644 index 000000000..2e61f4489 --- /dev/null +++ b/manage/CMakeLists.txt @@ -0,0 +1,23 @@ +SET (HEADERS doorpage.h + gamefilepage.h + genericpage.h + megapage.h + powerpage.h + robotpage.h + shippage.h + soundpage.h + texpage.h + weaponpage.h ) +SET (CPPS + doorpage.cpp + gamefilepage.cpp + generic.cpp + manage.cpp + megapage.cpp + pagelock.cpp + shippage.cpp + soundpage.cpp + texpage.cpp + weaponpage.cpp) + +ADD_LIBRARY(manage STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/manage/doorpage.cpp b/manage/doorpage.cpp new file mode 100644 index 000000000..46004a091 --- /dev/null +++ b/manage/doorpage.cpp @@ -0,0 +1,691 @@ +/* + * $Logfile: /DescentIII/Main/manage/doorpage.cpp $ + * $Revision: 25 $ + * $Date: 10/26/99 3:30p $ + * $Author: Jeff $ + * + * For loading/saving of door types + * + * $Log: /DescentIII/Main/manage/doorpage.cpp $ + * + * 25 10/26/99 3:30p Jeff + * handle extra.gam addon tablefile + * + * 24 10/20/99 6:27p Jeff + * sped up addon page popping (by saving page offsets) + * + * 23 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 22 4/14/99 1:33a Jeff + * fixed case mismatched #includes + * + * 21 3/04/99 4:47p Jason + * temp fix (ie BAD HACK) for OEM table file woes + * + * 20 2/04/99 2:05p Matt + * Added blastable doors + * + * 19 1/21/99 11:16p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 18 1/13/99 7:04a Jeff + * put #ifdef around #include + * + * 17 12/29/98 4:30p Jason + * added add-on data functionality + * + * 16 12/21/98 5:27p Jeff + * added osiris module information to door page. Fixed up dialogs for + * objects and doors for script interface (added a browse button) + * + * 15 12/17/98 12:50p Jason + * changed doorpage + * + * 14 11/06/98 12:35p Jason + * more speedups for manage system + * + * 13 11/05/98 7:54p Jason + * changes for new manage system + * + * 12 11/02/98 6:02p Jason + * made yes network updates much faster + * + * 11 10/09/98 2:27p Jason + * reorganized table file system + * + * 10 5/24/98 11:59p Mark + * Fixed a bug introduced with last revs + * + * 9 5/22/98 8:50p Matt + * Added sounds for doors + * + * 8 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 7 2/23/98 1:22p Jason + * made FindSpecific* read from the local drive, not the net drive - when + * starting the editor + * + * 6 2/02/98 7:07p Matt + * Added support for doors that can be seen through even when closed + * + * 5 1/15/98 6:22p Jason + * added safety checks so the network won't copy over a primitive you have + * held locally + * + * 4 10/06/97 6:39p Jason + * got terrain objects working with lightmaps/radiosity + * + * 3 10/06/97 4:05p Samir + * Jason forgot to put a break in a switch-case statement. I fixed + * this... + * + * 2 10/06/97 1:01p Jason + * made doors work with scripts (sort of) + * + * 9 5/08/97 12:41p Jason + * made manage system work with device dependant path names + * + * 8 4/25/97 6:16p Jason + * added switcheroo function + * + * 7 4/08/97 2:25a Chris + * Fixed a problem with uninitialized data. In addition it + * fixed a problem with the .used flag that would happen + * 1 out of 2^(sizeof(used_flag)) times (it would be zero + * when it was supposed to be non-zero) + * + * 6 3/17/97 6:39p Jason + * added staysopen page write + * + * 5 3/13/97 6:13p Jason + * got poly doors working + * + * 4 3/11/97 6:03p Chris + * Fixed the fix to a simple function. Isn't crossplatform development + * wonderful. :) + * + * 3 3/11/97 5:08 PM Jeremy + * in mng_WriteDoorPage there are two for loops. The second tried to use + * the int "i" which was declared in the scope of the first. This breaks + * under Codewarrior. I think that's how it's supposed to work under + * ANSI. + * + * 2 3/05/97 12:16p Jason + * added code to support our new 3d doors + * + * 1 3/05/97 11:04a Jason + * + * + * $NoKeywords: $ + */ +#if defined(WIN32) +#include +#endif + +#include "CFILE.H" +#include "manage.h" +#include "door.h" +#include "doorpage.h" +#include "mono.h" +#include "pserror.h" +#include "polymodel.h" +#include +#include "vclip.h" +#include "ddio.h" +#include "soundload.h" +#include "soundpage.h" +#include "args.h" + +// doorpage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define DOORPAGE_COMMAND_NAME 1 +#define DOORPAGE_COMMAND_END 2 +#define DOORPAGE_COMMAND_IMAGE_NAME 3 +#define DOORPAGE_COMMAND_VERSION 4 +#define DOORPAGE_COMMAND_OPEN_TIME 5 +#define DOORPAGE_COMMAND_CLOSE_TIME 6 +#define DOORPAGE_COMMAND_STAYS_OPEN 7 +#define DOORPAGE_COMMAND_SCRIPTNAME 8 +#define DOORPAGE_COMMAND_FLAGS 9 +#define DOORPAGE_COMMAND_OPEN_SOUND_NAME 10 +#define DOORPAGE_COMMAND_CLOSE_SOUND_NAME 11 + + +#define DOORPAGE_VERSION 3 + +extern char *TablefileNameOverride; + +// Given an open file pointer and a door_page struct, writes that door page out +void mng_WriteDoorPage (CFILE *outfile,mngs_door_page *doorpage) +{ + int i; + + ASSERT (outfile!=NULL); + ASSERT (doorpage!=NULL); + + cf_WriteByte (outfile,PAGETYPE_DOOR); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_NAME); // write out Door name + cf_WriteByte (outfile,PAGENAME_LEN); + for (i=0;idoor_struct.name[i]); + + // Write out its models name + cf_WriteByte (outfile,DOORPAGE_COMMAND_IMAGE_NAME); // get ready to write out name + cf_WriteByte (outfile,PAGENAME_LEN); + for (i=0;iimage_name[i]); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_OPEN_TIME); + cf_WriteByte (outfile,sizeof(float)); + cf_WriteFloat (outfile,doorpage->door_struct.total_open_time); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_CLOSE_TIME); + cf_WriteByte (outfile,sizeof(float)); + cf_WriteFloat (outfile,doorpage->door_struct.total_close_time); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_STAYS_OPEN); + cf_WriteByte (outfile,sizeof(float)); + cf_WriteFloat (outfile,doorpage->door_struct.total_time_open); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_SCRIPTNAME); + cf_WriteByte (outfile,strlen("")+1); + cf_WriteString (outfile,""); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_FLAGS); + cf_WriteByte (outfile,sizeof(sbyte)); + cf_WriteByte (outfile,doorpage->door_struct.flags); + + Int3(); //this shouldn't be called -- hit points not written out + + cf_WriteByte (outfile,DOORPAGE_COMMAND_OPEN_SOUND_NAME); + cf_WriteByte (outfile,strlen(doorpage->open_sound_name)+1); + cf_WriteString (outfile,doorpage->open_sound_name); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_CLOSE_SOUND_NAME); + cf_WriteByte (outfile,strlen(doorpage->close_sound_name)+1); + cf_WriteString (outfile,doorpage->close_sound_name); + + cf_WriteByte (outfile,DOORPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} + +// Given an open file pointer and a door_page struct, writes that door page out +void mng_WriteNewDoorPage (CFILE *outfile,mngs_door_page *doorpage) +{ + ASSERT (outfile!=NULL); + ASSERT (doorpage!=NULL); + + int offset=StartManagePage (outfile,PAGETYPE_DOOR); + + cf_WriteShort (outfile,DOORPAGE_VERSION); + cf_WriteString (outfile,doorpage->door_struct.name); + cf_WriteString (outfile,doorpage->image_name); + + cf_WriteFloat (outfile,doorpage->door_struct.total_open_time); + cf_WriteFloat (outfile,doorpage->door_struct.total_close_time); + cf_WriteFloat (outfile,doorpage->door_struct.total_time_open); + + cf_WriteByte (outfile,doorpage->door_struct.flags); + cf_WriteShort(outfile,doorpage->door_struct.hit_points); + + cf_WriteString (outfile,doorpage->open_sound_name); + cf_WriteString (outfile,doorpage->close_sound_name); + + cf_WriteString (outfile,doorpage->door_struct.module_name); + + EndManagePage (outfile,offset); +} + +// Reads a door page from an open file. Returns 0 on error. +int mng_ReadNewDoorPage (CFILE *infile,mngs_door_page *doorpage) +{ + ASSERT (infile!=NULL); + + int version=cf_ReadShort (infile); + + cf_ReadString(doorpage->door_struct.name, PAGENAME_LEN, infile); + cf_ReadString(doorpage->image_name, PAGENAME_LEN, infile); + + doorpage->door_struct.total_open_time=cf_ReadFloat (infile); + doorpage->door_struct.total_close_time=cf_ReadFloat (infile); + doorpage->door_struct.total_time_open=cf_ReadFloat (infile); + + doorpage->door_struct.flags=cf_ReadByte (infile); + + if (version >= 3) + doorpage->door_struct.hit_points = cf_ReadShort(infile); + else + doorpage->door_struct.hit_points = 0; + + cf_ReadString(doorpage->open_sound_name, PAGENAME_LEN, infile); + cf_ReadString(doorpage->close_sound_name, PAGENAME_LEN, infile); + + if(version>=2) + cf_ReadString(doorpage->door_struct.module_name, MAX_MODULENAME_LEN, infile); + else + doorpage->door_struct.module_name[0] = '\0'; + + // This is a valid new page + doorpage->door_struct.used=1; + + return 1; // successfully read +} + + +// Reads a door page from an open file. Returns 0 on error. +int mng_ReadDoorPage (CFILE *infile,mngs_door_page *doorpage) +{ + int done=0; + char command; + ubyte len; + int i; + + if (!Old_table_method) + return mng_ReadNewDoorPage (infile,doorpage); + + //doorpage->door_struct.script_name[0]=0; + strcpy(doorpage->open_sound_name,"INVALID SOUND NAME"); + strcpy(doorpage->close_sound_name,"INVALID SOUND NAME"); + + ASSERT (infile!=NULL); + + while (!done) + { + // Read in command byte then read in the length of that commands data + + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + + switch (command) + { + case DOORPAGE_COMMAND_END: + done=1; + break; + case DOORPAGE_COMMAND_IMAGE_NAME: // the name of the door model + for (i=0;iimage_name[i]=cf_ReadByte (infile); + break; + case DOORPAGE_COMMAND_NAME: + for (i=0;idoor_struct.name[i]=cf_ReadByte (infile); + break; + case DOORPAGE_COMMAND_OPEN_TIME: + doorpage->door_struct.total_open_time=cf_ReadFloat(infile); + break; + case DOORPAGE_COMMAND_STAYS_OPEN: + doorpage->door_struct.total_time_open=cf_ReadFloat(infile); + break; + case DOORPAGE_COMMAND_CLOSE_TIME: + doorpage->door_struct.total_close_time=cf_ReadFloat(infile); + break; + case DOORPAGE_COMMAND_SCRIPTNAME: + { + char dummy[256]; + cf_ReadString(dummy, len+1, infile); + }break; + case DOORPAGE_COMMAND_FLAGS: + doorpage->door_struct.flags = cf_ReadByte(infile); + break; + case DOORPAGE_COMMAND_OPEN_SOUND_NAME: + cf_ReadString(doorpage->open_sound_name, len+1, infile); + break; + case DOORPAGE_COMMAND_CLOSE_SOUND_NAME: + cf_ReadString(doorpage->close_sound_name, len+1, infile); + break; + default: + // Ignore the ones we don't know + for (i=0;idoor_struct.used=1; + + return 1; // successfully read +} + + +// Reads in the door named "name" into doorpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificDoorPage (char *name,mngs_door_page *doorpage,int offset) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + + char tablename[TABLE_NAME_LEN]; + + if (Loading_locals) + { + infile=cfopen (LocalTableFilename,"rb"); + } + else if (Loading_addon_table!=-1) + { + infile=cfopen (AddOnDataTables[Loading_addon_table].AddOnTableFilename,"rb"); + } + else + { + if (Network_up && Starting_editor) + { + int farg=FindArg("-filter"); + + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + + infile=cfopen (tablename,"rb"); + } + else + { + infile = NULL; + if(TablefileNameOverride) + { + infile=cfopen(TablefileNameOverride,"rb"); + } + + if(!infile) + infile=cfopen (TableFilename,"rb"); + } + } + + if (!infile) + { + mprintf ((0,"Couldn't open table file to find door!\n")); + Int3(); + return 0; + } + + if (offset) + cfseek (infile,offset,SEEK_SET); + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + + // If not a door page, just read it in and ignore it + if (pagetype!=PAGETYPE_DOOR) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + + mng_ReadNewDoorPage (infile,doorpage); + + if (!stricmp(name,doorpage->door_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + + cfclose (infile); + + return found; // successful! +} + +// Given a door page, allocs a door and calls AssignDoorPageToDoor to actually +// load models and values. Rturns door handle on success, -1 if fail +int mng_SetAndLoadDoor (mngs_door_page *doorpage) +{ + int n; + + n=AllocDoor(); + if (n<0) + return -1; + if (!mng_AssignDoorPageToDoor(doorpage,n)) + return -1; + + return n; +} + +// Given a doorpage and a door handle, attempts to make door n correspond to +// to the doorpage. +// Returns 1 on success, 0 otherwise +int mng_AssignDoorPageToDoor(mngs_door_page *doorpage,int n) +{ + door *doorpointer=&Doors[n]; + int img_handle; + + // copy our values + memcpy (doorpointer,&doorpage->door_struct,sizeof(door)); + strcpy (doorpointer->name,doorpage->door_struct.name); + + + // First see if our image differs from the one on the net + // If it is, make a copy + // If its a release version, don't do any of this + + #ifndef RELEASE + if (Network_up) + { + char str[200]; + char netstr[200]; + + ddio_MakePath(str,LocalModelsDir,doorpage->image_name,NULL); + ddio_MakePath(netstr,NetModelsDir,doorpage->image_name,NULL); + + UpdatePrimitive (str,netstr,doorpage->image_name,PAGETYPE_DOOR,doorpointer->name); + } + #endif + + // Try and load our door model from the disk + + img_handle=LoadPolyModel (doorpage->image_name,1); + + //Set sounds + doorpointer->open_sound = mng_GetGuaranteedSoundPage(doorpage->open_sound_name); + doorpointer->close_sound = mng_GetGuaranteedSoundPage(doorpage->close_sound_name); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignDoorPage...\n",doorpage->image_name)); + doorpointer->model_handle=-1; + return 0; + } + else + doorpointer->model_handle=img_handle; + + return 1; +} + +// Copies values from a door into a door_page +void mng_AssignDoorToDoorPage(int n,mngs_door_page *doorpage) +{ + door *doorpointer=&Doors[n]; + + // Assign the values + memcpy (&doorpage->door_struct,doorpointer,sizeof(door)); + + strcpy (doorpage->door_struct.name,doorpointer->name); + + if (doorpointer->model_handle!=-1) + strcpy (doorpage->image_name,Poly_models[doorpointer->model_handle].name); + else + strcpy (doorpage->image_name,"INVALID IMAGE NAME"); + + if (doorpointer->open_sound != -1) + strcpy(doorpage->open_sound_name,Sounds[doorpointer->open_sound].name); + else + strcpy(doorpage->open_sound_name,"INVALID SOUND NAME"); + + if (doorpointer->close_sound != -1) + strcpy(doorpage->close_sound_name,Sounds[doorpointer->close_sound].name); + else + strcpy(doorpage->close_sound_name,"INVALID SOUND NAME"); +} + +// Loads a door found in the net table file. It then allocs a door and +// then calls SetAndLoadDoor to actually load in any images/models associated +// with it +void mng_LoadNetDoorPage(CFILE *infile,bool overlay) +{ + mngs_door_page doorpage; + memset(&doorpage, 0, sizeof(mngs_door_page)); + + if (mng_ReadNewDoorPage (infile,&doorpage)) + { + int n; + n = FindDoorName(doorpage.door_struct.name); + if(n!=-1) + { + if(overlay) + { + mprintf((0,"OVERLAYING DOOR %s\n",doorpage.door_struct.name)); + mng_FreePagetypePrimitives (PAGETYPE_DOOR,doorpage.door_struct.name,0); + mng_AssignDoorPageToDoor(&doorpage,n); + } + return; + } + + int ret=mng_SetAndLoadDoor (&doorpage); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load doorpage named %s!\n",doorpage.door_struct.name)); +} + +// Reads a door page from a local table file. It then allocs a door and +// loads any images/models associated with that door +void mng_LoadLocalDoorPage(CFILE *infile) +{ + mngs_door_page doorpage; + int ok=0; + memset(&doorpage, 0, sizeof(mngs_door_page)); + + if (mng_ReadNewDoorPage (infile,&doorpage)) + { + // Check to see if this is a local copy that is supposed + // to go over a network copy (supersede the net copy) + + int i=FindDoorName (doorpage.door_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + + + strcpy (pl.name,doorpage.door_struct.name); + pl.pagetype=PAGETYPE_DOOR; + + /*if (Network_up && Stand_alone==0) + { + int locked; + locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + + ok=1; + bool need_to_load_page = true; + + if (Loading_addon_table!=-1) + { + AddOnTablefile *addon; + int tidx; + + // see if we really need to load this page + //check to see if we already have loaded this page (because it was + //a dependancy of another) + addon = &AddOnDataTables[Loading_addon_table]; + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_DOOR && + !stricmp(addon->Addon_tracklocks[tidx].name,doorpage.door_struct.name) ) + { + // found it!! + mprintf((0,"DoorPage: %s previously loaded\n",doorpage.door_struct.name)); + need_to_load_page = false; + break; + } + } + } + + if(need_to_load_page) + { + mng_FreePagetypePrimitives (PAGETYPE_DOOR,doorpage.door_struct.name,0); + mng_AssignDoorPageToDoor (&doorpage,i); + + // For addon data + if (Loading_addon_table!=-1) + { + // this is an overlay of some sort..see which we are overlaying + int overlay = 1; + int addidx,tidx; + bool found = false; + + for(addidx = Num_addon_tables-1; addidx>=0; addidx--) + { + if(addidx==Loading_addon_table) + continue; + AddOnTablefile *addon = &AddOnDataTables[addidx]; + + // look for the page in this table file + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_DOOR && + !stricmp(addon->Addon_tracklocks[tidx].name,doorpage.door_struct.name) ) + { + // found it!! + found = true; + overlay = addidx+2; + break; + } + } + + if(found) + break; + } + + mng_PushAddonPage (PAGETYPE_DOOR,doorpage.door_struct.name,overlay); + } + } + } + else + { + // This is a local door that has never been checked in + if ((i=mng_SetAndLoadDoor (&doorpage))<0) + ok=0; + else ok=1; + + // For addon data + if (ok && Loading_addon_table!=-1) + mng_PushAddonPage (PAGETYPE_DOOR,doorpage.door_struct.name,0); + } + + ASSERT (ok==1); + + if (Loading_addon_table==-1) + mng_AllocTrackLock (doorpage.door_struct.name,PAGETYPE_DOOR); + } + else + + mprintf ((0,"Could not load doorpage named %s!\n",doorpage.door_struct.name)); + +} + + + + + \ No newline at end of file diff --git a/manage/doorpage.h b/manage/doorpage.h new file mode 100644 index 000000000..f0ef78a4e --- /dev/null +++ b/manage/doorpage.h @@ -0,0 +1,56 @@ +#ifndef DOORPAGE_H +#define DOORPAGE_H + +#include "manage.h" +#include "door.h" +#include "CFILE.H" +#include "pstypes.h" + +typedef struct +{ + door door_struct; + char image_name[PAGENAME_LEN]; + char open_sound_name[PAGENAME_LEN]; + char close_sound_name[PAGENAME_LEN]; +} mngs_door_page; + +// Door page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a door_page struct, writes that doorpage out +void mng_WriteDoorPage (CFILE *outfile,mngs_door_page *doorpage); + +// Reads a door page from an open file. Returns 0 on error. +int mng_ReadDoorPage (CFILE *infile,mngs_door_page *doorpage); + +// Given an open file pointer and a door_page struct, writes that doorpage out +void mng_WriteNewDoorPage (CFILE *outfile,mngs_door_page *doorpage); + +// Reads a door page from an open file. Returns 0 on error. +int mng_ReadNewDoorPage (CFILE *infile,mngs_door_page *doorpage); + +// Reads in the doorpage named "name" into doorpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificDoorPage (char *name,mngs_door_page *doorpage,int offset=0); + +// Given a door page, allocs a door and calls AssignDoorPageToDoor to actually +// load model and values. Rturns door handle on success, -1 if fail +int mng_SetAndLoadDoor (mngs_door_page *doorpage); + +// Given a doorpage and a door handle, attempts to make door n correspond to +// to the doorpage. +// Returns 1 on success, 0 otherwise +int mng_AssignDoorPageToDoor(mngs_door_page *doorpage,int n); + +// Copies values from a Door into a door_page +void mng_AssignDoorToDoorPage(int n,mngs_door_page *doorpage); + + +// Reads in a door page from the local table file, superseding any door +// already in RAM with that same name +void mng_LoadLocalDoorPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetDoorPage (CFILE *,bool overlay=false); + +#endif \ No newline at end of file diff --git a/manage/gamefilepage.cpp b/manage/gamefilepage.cpp new file mode 100644 index 000000000..9265df590 --- /dev/null +++ b/manage/gamefilepage.cpp @@ -0,0 +1,419 @@ +#if defined(WIN32) +#include +#endif + +#include "CFILE.H" +#include "manage.h" +#include "mono.h" +#include "pserror.h" +#include "ddio.h" +#include "gamefile.h" +#include "gamefilepage.h" +#include "args.h" + +#include + +// gamefilepage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define GAMEFILEPAGE_COMMAND_NAME 1 +#define GAMEFILEPAGE_COMMAND_DIR_NAME 2 +#define GAMEFILEPAGE_COMMAND_END 3 + +#define GAMEFILE_VERSION 2 + +extern char *TablefileNameOverride; + +// Given an open file pointer and a gamefile_page struct, writes that gamefile page out +void mng_WriteGamefilePage (CFILE *outfile,mngs_gamefile_page *gamefilepage) +{ + ASSERT (outfile!=NULL); + ASSERT (gamefilepage!=NULL); + + cf_WriteByte (outfile,PAGETYPE_GAMEFILE); + + cf_WriteByte (outfile,GAMEFILEPAGE_COMMAND_NAME); // write out gamefile name + cf_WriteByte (outfile,strlen(gamefilepage->gamefile_struct.name)+1); + cf_WriteString (outfile,gamefilepage->gamefile_struct.name); + + cf_WriteByte (outfile,GAMEFILEPAGE_COMMAND_DIR_NAME); // write out gamefile name + cf_WriteByte (outfile,strlen(gamefilepage->gamefile_struct.dir_name)+1); + cf_WriteString (outfile,gamefilepage->gamefile_struct.dir_name); + + cf_WriteByte (outfile,GAMEFILEPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} +// Given an open file pointer and a gamefile_page struct, writes that gamefile page out +void mng_WriteNewGamefilePage (CFILE *outfile,mngs_gamefile_page *gamefilepage) +{ + ASSERT (outfile!=NULL); + ASSERT (gamefilepage!=NULL); + + int offset=StartManagePage (outfile,PAGETYPE_GAMEFILE); + + cf_WriteShort (outfile,GAMEFILE_VERSION); + + cf_WriteString (outfile,gamefilepage->gamefile_struct.name); + cf_WriteString (outfile,gamefilepage->gamefile_struct.dir_name); + + EndManagePage (outfile,offset); +} + +// Reads a gamefile page from an open file. Returns 0 on error. +int mng_ReadNewGamefilePage (CFILE *infile,mngs_gamefile_page *gamefilepage) +{ + ASSERT (infile!=NULL); + memset (gamefilepage,0,sizeof(mngs_gamefile_page)); + + int version=cf_ReadShort (infile); + + cf_ReadString (gamefilepage->gamefile_struct.name,PAGENAME_LEN,infile); + cf_ReadString (gamefilepage->gamefile_struct.dir_name,PAGENAME_LEN,infile); + + // This is a valid new page + gamefilepage->gamefile_struct.used=1; + + return 1; // successfully read +} + + +// Reads a gamefile page from an open file. Returns 0 on error. +int mng_ReadGamefilePage (CFILE *infile,mngs_gamefile_page *gamefilepage) +{ + int done=0; + char command; + ushort len; + int i; + + if (!Old_table_method) + return mng_ReadNewGamefilePage (infile,gamefilepage); + + ASSERT (infile!=NULL); + memset (gamefilepage,0,sizeof(mngs_gamefile_page)); + + while (!done) + { + // Read in command byte then read in the length of that commands data + + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + + switch (command) + { + case GAMEFILEPAGE_COMMAND_END: + done=1; + break; + case GAMEFILEPAGE_COMMAND_NAME: // the name of the gamefile model + cf_ReadString (gamefilepage->gamefile_struct.name,len+1,infile); + break; + case GAMEFILEPAGE_COMMAND_DIR_NAME: + cf_ReadString (gamefilepage->gamefile_struct.dir_name,len+1,infile); + break; + default: + // Ignore the ones we don't know + for (i=0;igamefile_struct.used=1; + + return 1; // successfully read +} + +// Reads in the gamefile named "name" into gamefilepage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificGamefilePage (char *name,mngs_gamefile_page *gamefilepage,int offset) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + char tablename[TABLE_NAME_LEN]; + + if (Loading_locals) + { + infile=cfopen (LocalTableFilename,"rb"); + } + else if (Loading_addon_table!=-1) + { + infile=cfopen (AddOnDataTables[Loading_addon_table].AddOnTableFilename,"rb"); + } + else + { + if (Network_up && Starting_editor) + { + int farg=FindArg("-filter"); + + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + + infile=cfopen (tablename,"rb"); + } + else + { + infile = NULL; + if(TablefileNameOverride) + { + infile=cfopen(TablefileNameOverride,"rb"); + } + + if(!infile) + infile=cfopen (TableFilename,"rb"); + } + } + + if (!infile) + Error("Cannot open table file."); + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt (infile); + + // If not a gamefile page, just read it in and ignore it + if (pagetype!=PAGETYPE_GAMEFILE) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + + mng_ReadNewGamefilePage (infile,gamefilepage); + + if (!stricmp(name,gamefilepage->gamefile_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + + cfclose (infile); + + return found; // successful! +} + +// Given a gamefile page, allocs a gamefile and calls AssignGamefilePageToGamefile to actually +// load models and values. Rturns gamefile handle on success, -1 if fail +int mng_SetAndLoadGamefile (mngs_gamefile_page *gamefilepage) +{ + int n; + + n=AllocGamefile(); + if (n<0) + return -1; + if (!mng_AssignGamefilePageToGamefile(gamefilepage,n)) + return -1; + + return n; +} + +// Given a gamefilepage and a gamefile handle, attempts to make gamefile n correspond to +// to the gamefilepage. +// Returns 1 on success, 0 otherwise +int mng_AssignGamefilePageToGamefile(mngs_gamefile_page *gamefilepage,int n) +{ + gamefile *gamefilepointer=&Gamefiles[n]; + + // copy our values + memcpy (gamefilepointer,&gamefilepage->gamefile_struct,sizeof(gamefile)); + strcpy (gamefilepointer->name,gamefilepage->gamefile_struct.name); + strcpy (gamefilepointer->dir_name,gamefilepage->gamefile_struct.dir_name); + + // First see if our image differs from the one on the net + // If it is, make a copy + // If its a release version, don't do any of this + + #ifndef RELEASE + if (Network_up) + { + char str[200]; + char netstr[200]; + + ddio_MakePath (str,LocalD3Dir,"data",gamefilepointer->dir_name,gamefilepointer->name,NULL); + ddio_MakePath (netstr,NetD3Dir,"data",gamefilepointer->dir_name,gamefilepointer->name,NULL); + + UpdatePrimitive (str,netstr,gamefilepointer->name,PAGETYPE_GAMEFILE,gamefilepointer->name); + + if (!Starting_editor) + { + if (cf_Diff(str,netstr)) + { + ASSERT(1); // Get Jason Immediately...versions between local and net don't match! + + // Do it again so we can trace to see what is wrong + UpdatePrimitive (str,netstr,gamefilepointer->name,PAGETYPE_GAMEFILE,gamefilepointer->name); + } + } + } + #endif + + return 1; +} + +// Copies values from a gamefile into a gamefile_page +void mng_AssignGamefileToGamefilePage(int n,mngs_gamefile_page *gamefilepage) +{ + gamefile *gamefilepointer=&Gamefiles[n]; + + // Assign the values + memcpy (&gamefilepage->gamefile_struct,gamefilepointer,sizeof(gamefile)); + + strcpy (gamefilepage->gamefile_struct.name,gamefilepointer->name); + strcpy (gamefilepage->gamefile_struct.dir_name,gamefilepointer->dir_name); +} + +// Loads a gamefile found in the net table file. It then allocs a gamefile and +// then calls SetAndLoadGamefile to actually load in any images/models associated +// with it +void mng_LoadNetGamefilePage(CFILE *infile,bool overlay) +{ + mngs_gamefile_page gamefilepage; + memset(&gamefilepage, 0, sizeof(mngs_gamefile_page)); + + if (mng_ReadNewGamefilePage (infile,&gamefilepage)) + { + int n = FindGamefileName(gamefilepage.gamefile_struct.name); + if(n!=-1) + { + if(overlay) + { + mprintf((0,"OVERLAYING GAMEFILE %s\n",gamefilepage.gamefile_struct.name)); + mng_FreePagetypePrimitives (PAGETYPE_GAMEFILE,gamefilepage.gamefile_struct.name,0); + mng_AssignGamefilePageToGamefile(&gamefilepage,n); + } + return; + } + + int ret=mng_SetAndLoadGamefile (&gamefilepage); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load gamefilepage named %s!\n",gamefilepage.gamefile_struct.name)); +} + +// Reads a gamefile page from a local table file. It then allocs a gamefile and +// loads any images/models associated with that gamefile +void mng_LoadLocalGamefilePage(CFILE *infile) +{ + mngs_gamefile_page gamefilepage; + int ok=0; + memset(&gamefilepage, 0, sizeof(mngs_gamefile_page)); + + if (mng_ReadNewGamefilePage (infile,&gamefilepage)) + { + // Check to see if this is a local copy that is supposed + // to go over a network copy (supersede the net copy) + + int i=FindGamefileName (gamefilepage.gamefile_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + + strcpy (pl.name,gamefilepage.gamefile_struct.name); + pl.pagetype=PAGETYPE_GAMEFILE; + + /*if (Network_up && Stand_alone==0) + { + int locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + + ok=1; + bool need_to_load_page = true; + + if (Loading_addon_table!=-1) + { + AddOnTablefile *addon; + int tidx; + + // see if we really need to load this page + //check to see if we already have loaded this page (because it was + //a dependancy of another) + addon = &AddOnDataTables[Loading_addon_table]; + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_GAMEFILE && + !stricmp(addon->Addon_tracklocks[tidx].name,gamefilepage.gamefile_struct.name) ) + { + // found it!! + mprintf((0,"GamefilePage: %s previously loaded\n",gamefilepage.gamefile_struct.name)); + need_to_load_page = false; + break; + } + } + } + + if(need_to_load_page) + { + mng_FreePagetypePrimitives (PAGETYPE_GAMEFILE,gamefilepage.gamefile_struct.name,0); + mng_AssignGamefilePageToGamefile (&gamefilepage,i); + + // For addon data + if (Loading_addon_table!=-1) + { + // this is an overlay of some sort..see which we are overlaying + int overlay = 1; + int addidx,tidx; + bool found = false; + for(addidx = Num_addon_tables-1; addidx>=0; addidx--) + { + if(addidx==Loading_addon_table) + continue; + AddOnTablefile *addon = &AddOnDataTables[addidx]; + + // look for the page in this table file + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_GAMEFILE && + !stricmp(addon->Addon_tracklocks[tidx].name,gamefilepage.gamefile_struct.name) ) + { + // found it!! + found = true; + overlay = addidx+2; + break; + } + } + + if(found) + break; + } + + mng_PushAddonPage (PAGETYPE_GAMEFILE,gamefilepage.gamefile_struct.name,overlay); + } + } + } + else + { + // This is a local gamefile that has never been checked in + if (mng_SetAndLoadGamefile (&gamefilepage)<0) + ok=0; + else ok=1; + + // For addon data + if (ok && Loading_addon_table!=-1) + mng_PushAddonPage (PAGETYPE_GAMEFILE,gamefilepage.gamefile_struct.name,0); + } + + ASSERT (ok==1); // There was problem loading a page! + + if (Loading_addon_table==-1) + mng_AllocTrackLock (gamefilepage.gamefile_struct.name,PAGETYPE_GAMEFILE); + + } + else + mprintf ((0,"Could not load gamefilepage named %s!\n",gamefilepage.gamefile_struct.name)); +} diff --git a/manage/gamefilepage.h b/manage/gamefilepage.h new file mode 100644 index 000000000..dd280a34a --- /dev/null +++ b/manage/gamefilepage.h @@ -0,0 +1,59 @@ +#ifndef GAMEFILEPAGE_H +#define GAMEFILEPAGE_H + +#include "manage.h" +#include "CFILE.H" +#include "pstypes.h" +#include "gamefile.h" + +typedef struct +{ + gamefile gamefile_struct; +} mngs_gamefile_page; + +// Gamefile page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a gamefile_page struct, writes that gamefile page out +void mng_WriteGamefilePage (CFILE *outfile,mngs_gamefile_page *gamefilepage); + +// Reads a gamefile page from an open file. Returns 0 on error. +int mng_ReadGamefilePage (CFILE *infile,mngs_gamefile_page *gamefilepage); + +// Given an open file pointer and a gamefile_page struct, writes that gamefile page out +void mng_WriteNewGamefilePage (CFILE *outfile,mngs_gamefile_page *gamefilepage); + +// Reads a gamefile page from an open file. Returns 0 on error. +int mng_ReadNewGamefilePage (CFILE *infile,mngs_gamefile_page *gamefilepage); + +// Reads in the gamefilepage named "name" into gamefilepage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificGamefilePage (char *name,mngs_gamefile_page *gamefilepage,int offset=0); + +// Given a gamefile page, allocs a gamefile and calls AssignGamefilePageToGamefile to actually +// load bitmaps and values. Rturns gamefile handle on success, -1 if fail +int mng_SetAndLoadGamefile (mngs_gamefile_page *gamefilepage); + +// Given a gamefilepage and a gamefile handle, attempts to make gamefile n correspond to +// to the gamefilepage. +// Returns 1 on success, 0 otherwise +int mng_AssignGamefilePageToGamefile(mngs_gamefile_page *gamefilepage,int n); + +// Copies values from a gamefile into a gamefile_page +void mng_AssignGamefileToGamefilePage(int n,mngs_gamefile_page *gamefilepage); + + +// Reads in a gamefile page from the local table file, superseding any gamefile +// already in RAM with that same name +void mng_LoadLocalGamefilePage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetGamefilePage (CFILE *,bool overlay=false); + +// First searches through the gamefile index to see if the gamefile is already +// loaded. If not, searches in the table file and loads it. +// Returns index of gamefile if found, -1 if not +int mng_GetGuaranteedGamefilePage (char *name,CFILE *infile=NULL); + + +#endif \ No newline at end of file diff --git a/manage/generic.cpp b/manage/generic.cpp new file mode 100644 index 000000000..a59763794 --- /dev/null +++ b/manage/generic.cpp @@ -0,0 +1,2569 @@ +/* + * $Logfile: /DescentIII/Main/manage/generic.cpp $ + * $Revision: 100 $ + * $Date: 9/06/01 10:32a $ + * $Author: Matt $ + * + * + * $Log: /DescentIII/Main/manage/generic.cpp $ + * + * 100 9/06/01 10:32a Matt + * Added code to fix problem poping add-on pages when the original pages + * were in the extra.gam file. + * + * 99 4/19/00 5:27p Matt + * From Duane for 1.4 + * Mac file extension change + * + * 98 10/26/99 3:30p Jeff + * handle extra.gam addon tablefile + * + * 97 10/22/99 10:41p Jeff + * correctly merged and handle this mac code merge stuff + * + * 96 10/22/99 6:04p Jeff + * fixed bugs and compiler errors resulting from mac code merge + * + * 95 10/22/99 2:56p Kevin + * Mac merge w/memory savings + * + * 94 9/18/99 8:49p Jeff + * fixed bug with addon pages that have dependencies on other pages in the + * addon tablefile + * + * 93 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 92 4/25/99 10:19p Matt + * Fixed multiplayer and demo problems will killing an object from script, + * and cleaned up the death code a bit in the process. + * + * 91 4/22/99 7:08p Matt + * Reduced the number of object sounds from 3 to 2, since we were only + * using two. + * + * 90 4/19/99 3:57p Matt + * Took out guidebot velocity hack. + * + * 89 4/18/99 4:49p Matt + * Took out code that hacked in ammo amounts for weapon powerups, and + * added code to set the counts on the page and read and write to the + * table file. + * + * 88 4/15/99 5:21p Jason + * sped up table file loading + * + * 87 4/14/99 1:33a Jeff + * fixed case mismatched #includes + * + * 86 4/10/99 5:56p Matt + * Cleaned up object initialization code, including create object & load + * object. + * + * 85 4/06/99 6:02p Matt + * Added score system + * + * 84 3/29/99 11:20a Matt + * Added more flexibility to death delays, and added fade away deaths. + * + * 83 3/27/99 2:15p Jason + * took out bashing of hangturret + * + * 82 3/09/99 1:44p Kevin + * Bashed the aiming point value for the hangturret. + * + * 81 3/04/99 4:47p Jason + * temp fix (ie BAD HACK) for OEM table file woes + * + * 80 2/28/99 6:20p Matt + * Added the ability to set death types for generic objects. + * + * 79 2/23/99 12:39p Jason + * added more options for generic objects + * + * 78 2/23/99 11:02a Matt + * Changed generic object spew info to use -1 for none + * + * 77 2/23/99 10:19a Matt + * Temporary fix + * + * 76 2/16/99 12:38a Matt + * Took out gunboy sound hack + * + * 75 2/02/99 8:44a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 74 1/21/99 11:16p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 73 1/13/99 7:04a Jeff + * put #ifdef around #include + * + * 72 12/30/98 5:17p Jeff + * changes made to handle script name override, to override the name of + * the script to use in a module. + * + * 71 12/29/98 4:30p Jason + * added add-on data functionality + * + * 70 12/17/98 7:26p Jeff + * new script system data + * + * 69 12/01/98 4:31p Chris + * + * 68 12/01/98 4:31p Chris + * Checked in a massive amount of AI work. Improved flocking code. :) + * (Still hacked lightly). In addition, I am moving toward using the + * composite dir. :) + * + * 67 11/19/98 8:36p Chris + * Tweaked + * + * 66 11/19/98 8:26p Chris + * Starting to add generic team avoidance code + * + * 65 11/13/98 12:30p Jason + * changes for weapons + * + * 64 11/06/98 12:35p Jason + * more speedups for manage system + * + * 63 11/05/98 7:54p Jason + * changes for new manage system + * + * 62 11/02/98 6:02p Jason + * made yes network updates much faster + * + * 61 10/23/98 6:43p Matt + * Kill the gunboy's sounds (for the demo). + * + * 60 10/12/98 11:38p Jeff + * wrapped all the Object_info[].description whenever freed...trying to + * find an obscure bug. Added icon_name to manage page of Generic + * + * 59 10/09/98 2:27p Jason + * reorganized table file system + * + * 58 10/08/98 10:03p Jason + * more filtered table file stuff + * + * 57 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 56 8/26/98 8:07p Jason + * added directional lights to objects + * + * 55 8/25/98 3:42p Jason + * fixed generic object problems + * + * 54 8/24/98 2:37p Jason + * made table file more efficient with regards to invalid names + * + * 53 8/19/98 6:29p Chris + * + * 52 8/16/98 6:17p Chris + * Added generic object spewing code + * + * 51 8/15/98 6:12p Chris + * Added 13 new AI fields + * + * 50 8/14/98 4:56p Jeff + * added quad fire mask for weapon battery + * + * 49 6/12/98 1:06p Jason + * added smart loading from local table file + * + * 48 6/04/98 6:44p Jason + * implemented smart FindGenericPage function + * + * 47 6/01/98 10:37a Matt + * Added system to spew powerups when the player dies. Still needs Jason + * to deal with mng_FindSpecificGenericPage(). + * + * 46 5/18/98 8:06p Chris + * Removed an unused AI value + * + * 45 5/07/98 1:41p Chris + * Added hit_death_dot + * + * 44 5/03/98 5:38p Chris + * Added sounds to anim page + * + * 43 4/30/98 12:22p Jason + * did some lo-res model optimizations + * + * 42 4/03/98 10:07a Chris + * Added support for objects getting their size computed when the + * polymodel is paged in the first time as an object + * + * 41 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 40 3/31/98 3:49p Jason + * added memory lib + * + * 39 3/23/98 10:03a Chris + * Added independant wb animations + * + * 38 3/13/98 5:55p Chris + * Added the new collision spheres + * + * 37 3/11/98 4:59p Chris + * Changed the ComputeDefualtSize function call + * + * 36 3/10/98 6:54p Chris + * All start and end frame for ComputeDefaultSize + * + * 35 3/02/98 6:56p Chris + * Changed melee_dist to melee_damage + * + * 34 3/02/98 6:43p Chris + * + * 33 3/02/98 4:16p Chris + * Added 14 new fields to the AI Settings Dialog/page. + * + * 32 2/23/98 1:22p Jason + * made FindSpecific* read from the local drive, not the net drive - when + * starting the editor + * + * 31 2/06/98 2:15a Chris + * Activated the max_velocity, max_delta_velocity, and max_turn_rate + * fields for AI objects. + * + * 30 2/04/98 11:47a Jason + * added dynamic description field to generic pages + * + * 29 1/29/98 3:29p Chris + * Major update to the AI system. + * + * 28 1/23/98 6:25p Jason + * Got spray weapons working + * + * 27 1/15/98 6:22p Jason + * added safety checks so the network won't copy over a primitive you have + * held locally + * + * 26 1/05/98 3:55p Chris + * Added explosion and ambient sounds + * + * 25 12/22/97 6:19p Chris + * Moved weapon battery firing sound off the projectile (weapon) and into + * the weapon battery. + * + * 24 12/01/97 9:54a Chris + * Added support for concussive forces, generalized robot collisions to + * generic collisions + * + * 23 11/25/97 1:16p Jason + * added lod system for certain objects + * + * 22 11/17/97 10:47p Jason + * fixed loading/saving of flicker_distance + * + * 21 11/05/97 12:22p Chris + * Fixed size of wb_info in the page file + * + * 20 11/06/97 11:09a Jason + * added extra assert to catch errors + * + * 19 10/28/97 12:23p Chris + * We now save out more AI info + * + * 18 10/01/97 11:01a Chris + * Added new support for additional physics items + * + * 17 9/24/97 6:18p Samir + * Use script names instead of script id values to identify scripts. + * + * 16 9/17/97 10:59a Chris + * Added a new way to compute radi + * + * 15 9/10/97 5:26p Jason + * fixed stupid off by one bug + * + * 14 9/10/97 5:17p Jason + * more lighting improvements + * + * 13 9/10/97 11:44a Chris + * Added support for weapon batteries + * + * 12 9/08/97 11:51a Chris + * Extended the weapon system + * + * 11 9/05/97 1:29p Jason + * revamped generic object lighting + * + * 10 9/04/97 5:49p Jason + * made generic pages read/write pulse time for lighting + * + * 9 9/04/97 3:21p Jason + * added pulse timing for lit objects + * + * 8 9/03/97 6:18p Jason + * added code to support powerups/robots/etc that cast light + * + * 7 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 6 8/13/97 4:54p Matt + * Added destroyable flag & hitpoints + * + * 5 8/11/97 6:47p Matt + * Fixed read/write size mismatches in generic page + * + * 4 8/11/97 1:53p Matt + * Ripped out robot & powerup pages, and added generic page + * + * 3 8/08/97 5:25p Matt + * Type was writing as byte but reading as int + * + * 2 8/08/97 3:44p Jason + * added code to support new generic page + * + * 1 8/08/97 1:29p Jason + * file for object_info pages + * + * $NoKeywords: $ + */ +#if defined(WIN32) +#include +#endif + +#include "CFILE.H" +#include "manage.h" +#include "genericpage.h" +#include "soundpage.h" +#include "weaponpage.h" +#include "mono.h" +#include "pserror.h" +#include "polymodel.h" +#include "ddio.h" +#include +#include "robotfire.h" +#include "weapon.h" +#include "sounds.h" +#include "mem.h" +#include "args.h" + +#define GENERICFILE_VERSION 27 + +// genericpage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define GENERICPAGE_COMMAND_NAME 1 +#define GENERICPAGE_COMMAND_END 2 +#define GENERICPAGE_COMMAND_IMAGE_NAME 3 +#define GENERICPAGE_COMMAND_VERSION 4 +#define GENERICPAGE_COMMAND_PHYS_MASS 5 +#define GENERICPAGE_COMMAND_PHYS_DRAG 6 +#define GENERICPAGE_COMMAND_SIZE 7 +#define GENERICPAGE_COMMAND_ANIM_STATES 8 +#define GENERICPAGE_COMMAND_FLAGS 9 + +#define GENERICPAGE_COMMAND_PHYS_FLAGS 10 + +//AI info +#define GENERICPAGE_COMMAND_AI_INFO 11 +#define GENERICPAGE_COMMAND_TYPE 12 +#define GENERICPAGE_COMMAND_SCORE 13 +#define GENERICPAGE_COMMAND_SCRIPT 14 +#define GENERICPAGE_COMMAND_SOUND_NAME 15 + +#define GENERICPAGE_COMMAND_HITPOINTS 16 + +#define GENERICPAGE_COMMAND_LIGHT_CAST 17 + +#define GENERICPAGE_COMMAND_WB_INFO 18 +#define GENERICPAGE_COMMAND_WB_WEAPON 19 +#define GENERICPAGE_COMMAND_SCRIPTNAME 20 + +#define GENERICPAGE_COMMAND_ROT_DRAG 21 +#define GENERICPAGE_COMMAND_FULL_THRUST 22 +#define GENERICPAGE_COMMAND_FULL_ROTTHRUST 23 +#define GENERICPAGE_COMMAND_TURNROLL_RATE 24 +#define GENERICPAGE_COMMAND_TURNROLL_RATIO 25 +#define GENERICPAGE_COMMAND_WIGGLE_AMP 26 +#define GENERICPAGE_COMMAND_WIGGLE_FREQ 27 +#define GENERICPAGE_COMMAND_INT_VELOCITY 28 +#define GENERICPAGE_COMMAND_INT_ROTVEL 29 +#define GENERICPAGE_COMMAND_NUM_BOUNCES 30 +#define GENERICPAGE_COMMAND_COEFF_REST 31 + +#define GENERICPAGE_COMMAND_FLICKER_DISTANCE 32 + +#define GENERICPAGE_COMMAND_LOD_MODELS 33 + +// Impact stuff +#define GENERICPAGE_COMMAND_IMPACT 34 + +#define GENERICPAGE_COMMAND_WB_FIRE_SOUND 35 + +#define GENERICPAGE_COMMAND_WB_FLAGS 36 +#define GENERICPAGE_COMMAND_DESCRIPTION 37 + +#define GENERICPAGE_COMMAND_AI_SOUND_NAME 38 +#define GENERICPAGE_COMMAND_LOD_DISTANCE 39 +#define GENERICPAGE_COMMAND_ANIM_SOUND_NAME 40 + +#define GENERICPAGE_COMMAND_HIT_DIE_DOT 41 +#define GENERICPAGE_COMMAND_WB_QUADMASK 42 + +#define GENERICPAGE_COMMAND_AI_INFO2 43 + +#define GENERICPAGE_COMMAND_DSPEW_INFO 44 +#define GENERICPAGE_COMMAND_DIRECTION_DOT 45 + +#define GENERICPAGE_COMMAND_ICON_NAME 46 + + +void mng_WriteLightingChunk ( light_info *lighting_info,CFILE *outfile); +void mng_ReadLightingChunk (light_info *lighting_info,CFILE *infile); + +extern bool Running_editor;//in init.cpp +extern char *TablefileNameOverride; + +// Sets a page structure to default values +void mng_InitGenericPage (mngs_generic_page *genericpage) +{ + int i; + + memset (genericpage,0,sizeof(mngs_generic_page)); + strcpy (genericpage->image_name,""); + + strcpy (genericpage->med_image_name,""); + strcpy (genericpage->lo_image_name,""); + + for (i=0;isound_name[i],""); + + for (i=0;iai_sound_name[i],""); + + for (i = 0; i < MAX_DSPEW_TYPES; i++) + { + strcpy(genericpage->dspew_name[i],"\0"); + } + + genericpage->objinfo_struct.description = NULL; + genericpage->objinfo_struct.icon_name[0] = '\0'; + + for (i=0;ianim_sound_name[i][j],""); + + genericpage->objinfo_struct.med_lod_distance=DEFAULT_MED_LOD_DISTANCE; + genericpage->objinfo_struct.lo_lod_distance=DEFAULT_LO_LOD_DISTANCE; + + genericpage->objinfo_struct.phys_info.hit_die_dot = 1.0f; + genericpage->objinfo_struct.respawn_scalar = 1.0f; + + genericpage->ai_info.curiousity = .5f; + genericpage->ai_info.night_vision = .7f; + genericpage->ai_info.fog_vision = .7f; + genericpage->ai_info.lead_accuracy = 1.0f; + genericpage->ai_info.lead_varience = 0.0f; + genericpage->ai_info.fire_spread = 0.0f; + genericpage->ai_info.fight_team = 0.15f; + genericpage->ai_info.fight_same = 0.8f; + genericpage->ai_info.agression = 0.5f; + genericpage->ai_info.hearing = 1.0f; + genericpage->ai_info.frustration = 0.5f; + genericpage->ai_info.roaming = 0.5f; + genericpage->ai_info.life_preservation = 0.0f; + genericpage->objinfo_struct.module_name[0] = '\0'; + + for (i=0;iobjinfo_struct.death_types[i].flags = 0; + genericpage->objinfo_struct.death_types[i].delay_min = 0.0; + genericpage->objinfo_struct.death_types[i].delay_max = 0.0; + genericpage->objinfo_struct.death_probabilities[i] = 0; + } +} + +// Given an open file pointer and a generic_page struct, writes that generic page out +void mng_WriteGenericPage (CFILE *outfile,mngs_generic_page *genericpage) +{ + int i,size; + + ASSERT (outfile!=NULL); + ASSERT (genericpage!=NULL); + ASSERT ((strlen(genericpage->image_name))>2); + + cf_WriteByte (outfile,PAGETYPE_GENERIC); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_VERSION); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,GENERICFILE_VERSION); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_TYPE); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,genericpage->objinfo_struct.type); + + // Write the name + cf_WriteByte (outfile,GENERICPAGE_COMMAND_NAME); + cf_WriteByte (outfile,strlen(genericpage->objinfo_struct.name)+1); + cf_WriteString (outfile,genericpage->objinfo_struct.name); + + // Write the model name + cf_WriteByte (outfile,GENERICPAGE_COMMAND_IMAGE_NAME); + cf_WriteByte (outfile,strlen(genericpage->image_name)+1); + cf_WriteString (outfile,genericpage->image_name); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_IMPACT); + cf_WriteByte (outfile, 3*sizeof(float)); + cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_size); + cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_time); + cf_WriteFloat(outfile, genericpage->objinfo_struct.damage); + + // Write the LOD model names + cf_WriteByte (outfile,GENERICPAGE_COMMAND_LOD_MODELS); + size=strlen(genericpage->med_image_name)+1; + size+=strlen(genericpage->lo_image_name)+1; + + cf_WriteByte (outfile,size); + cf_WriteString (outfile,genericpage->med_image_name); + cf_WriteString (outfile,genericpage->lo_image_name); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_SCORE); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,genericpage->objinfo_struct.score); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_SCRIPTNAME); + cf_WriteByte (outfile,strlen("")+1);//genericpage->objinfo_struct.script_name + cf_WriteString (outfile,""); + + if (genericpage->objinfo_struct.description!=NULL) + { + cf_WriteByte (outfile,GENERICPAGE_COMMAND_DESCRIPTION); + cf_WriteByte (outfile,strlen(genericpage->objinfo_struct.description)+1); + cf_WriteString (outfile,genericpage->objinfo_struct.description); + } + + if (genericpage->objinfo_struct.icon_name[0]!='\0') + { + cf_WriteByte (outfile,GENERICPAGE_COMMAND_ICON_NAME); + cf_WriteByte (outfile,strlen(genericpage->objinfo_struct.icon_name)+1); + cf_WriteString (outfile,genericpage->objinfo_struct.icon_name); + } + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_LOD_DISTANCE); + cf_WriteByte (outfile,8); + cf_WriteFloat (outfile,genericpage->objinfo_struct.med_lod_distance); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lo_lod_distance); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_PHYS_MASS); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.mass); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_PHYS_DRAG); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.drag); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_SIZE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.size); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_LIGHT_CAST); + cf_WriteByte (outfile,(10*4)+2); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.light_distance); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.red_light1); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.green_light1); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.blue_light1); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.time_interval); + + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.red_light2); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.green_light2); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.blue_light2); + cf_WriteInt (outfile,genericpage->objinfo_struct.lighting_info.flags); + cf_WriteInt (outfile,genericpage->objinfo_struct.lighting_info.timebits); + cf_WriteByte (outfile,genericpage->objinfo_struct.lighting_info.angle); + cf_WriteByte (outfile,genericpage->objinfo_struct.lighting_info.lighting_render_type); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_FLICKER_DISTANCE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.flicker_distance); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_DIRECTION_DOT); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lighting_info.directional_dot); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_HITPOINTS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,genericpage->objinfo_struct.hit_points); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_FLAGS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,genericpage->objinfo_struct.flags); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_PHYS_FLAGS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,genericpage->objinfo_struct.phys_info.flags); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_ROT_DRAG); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.rotdrag); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_FULL_THRUST); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.full_thrust); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_FULL_ROTTHRUST); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.full_rotthrust); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_TURNROLL_RATE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.max_turnroll_rate); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_TURNROLL_RATIO); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.turnroll_ratio); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_WIGGLE_AMP); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.wiggle_amplitude); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_WIGGLE_FREQ); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.wiggles_per_sec); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_INT_VELOCITY); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.velocity.z); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_INT_ROTVEL); + cf_WriteByte (outfile,12); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.rotvel.x); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.rotvel.y); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.rotvel.z); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_NUM_BOUNCES); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,genericpage->objinfo_struct.phys_info.num_bounces); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_COEFF_REST); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.coeff_restitution); + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_HIT_DIE_DOT); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,genericpage->objinfo_struct.phys_info.hit_die_dot); + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_AI_INFO); + cf_WriteByte (outfile, 76); + + cf_WriteInt (outfile, genericpage->ai_info.flags); + cf_WriteByte (outfile, genericpage->ai_info.ai_class); + cf_WriteByte (outfile, genericpage->ai_info.ai_type); + cf_WriteByte (outfile, genericpage->ai_info.movement_type); + cf_WriteByte (outfile, genericpage->ai_info.movement_subtype); + cf_WriteFloat(outfile, genericpage->ai_info.fov); + + cf_WriteFloat(outfile, genericpage->ai_info.max_velocity); + cf_WriteFloat(outfile, genericpage->ai_info.max_delta_velocity); + cf_WriteFloat(outfile, genericpage->ai_info.max_turn_rate); + + cf_WriteFloat(outfile, genericpage->ai_info.avoid_friends_distance); + + // Makes sure there are no bugs as things are added and removed -- ask chris + genericpage->ai_info.notify_flags &= ~AI_NOTIFIES_ALWAYS_ON; + cf_WriteInt (outfile, genericpage->ai_info.notify_flags); + genericpage->ai_info.notify_flags |= AI_NOTIFIES_ALWAYS_ON; + + cf_WriteFloat(outfile, genericpage->ai_info.max_delta_turn_rate); + cf_WriteFloat(outfile, genericpage->ai_info.circle_distance); + cf_WriteFloat(outfile, genericpage->ai_info.attack_vel_percent); + cf_WriteFloat(outfile, genericpage->ai_info.dodge_percent); + cf_WriteFloat(outfile, genericpage->ai_info.dodge_vel_percent); + cf_WriteFloat(outfile, genericpage->ai_info.flee_vel_percent); + cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[0]); + cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[1]); + cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[0]); + cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[1]); + + for (i=0;isound_name[i])+2); // 1 for sound index, 1 for null term + cf_WriteByte (outfile,i); + cf_WriteString (outfile,genericpage->sound_name[i]); + } + + for (i=0;iai_sound_name[i])+2); // 1 for sound index, 1 for null term + cf_WriteByte (outfile,i); + cf_WriteString (outfile,genericpage->ai_sound_name[i]); + } + + for (i = 0; idspew_name[i]) + 9); // 1 for null charactor + cf_WriteByte(outfile, i); + cf_WriteByte(outfile, genericpage->objinfo_struct.f_dspew); + cf_WriteFloat(outfile, genericpage->objinfo_struct.dspew_percent[i]); + cf_WriteShort(outfile, genericpage->objinfo_struct.dspew_number[i]); + cf_WriteString(outfile, genericpage->dspew_name[i]); + } + + cf_WriteByte(outfile,GENERICPAGE_COMMAND_AI_INFO2); + cf_WriteByte(outfile,13*sizeof(float)); + cf_WriteFloat(outfile, genericpage->ai_info.curiousity); + cf_WriteFloat(outfile, genericpage->ai_info.night_vision); + cf_WriteFloat(outfile, genericpage->ai_info.fog_vision); + cf_WriteFloat(outfile, genericpage->ai_info.lead_accuracy); + cf_WriteFloat(outfile, genericpage->ai_info.lead_varience); + cf_WriteFloat(outfile, genericpage->ai_info.fire_spread); + cf_WriteFloat(outfile, genericpage->ai_info.fight_team); + cf_WriteFloat(outfile, genericpage->ai_info.fight_same); + cf_WriteFloat(outfile, genericpage->ai_info.agression); + cf_WriteFloat(outfile, genericpage->ai_info.hearing); + cf_WriteFloat(outfile, genericpage->ai_info.frustration); + cf_WriteFloat(outfile, genericpage->ai_info.roaming); + cf_WriteFloat(outfile, genericpage->ai_info.life_preservation); + + for (i=0;ianim[i].elem[j].from); + cf_WriteShort (outfile, genericpage->anim[i].elem[j].to); + cf_WriteFloat (outfile, genericpage->anim[i].elem[j].spc); + } + } + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_WB_FLAGS); + cf_WriteByte (outfile, sizeof(int) * MAX_WBS_PER_OBJ); + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + cf_WriteByte(outfile, genericpage->static_wb[i].flags); + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + int j; + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_WB_INFO); + size = 202; + + cf_WriteByte (outfile,size); + + cf_WriteByte (outfile,i); + for(j = 0; j < MAX_WB_GUNPOINTS; j++) cf_WriteShort(outfile, genericpage->static_wb[i].gp_weapon_index[j]); + cf_WriteShort(outfile, genericpage->static_wb[i].aiming_gp_index); + + cf_WriteByte(outfile, genericpage->static_wb[i].num_masks); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) cf_WriteByte(outfile, genericpage->static_wb[i].gp_fire_masks[j]); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) cf_WriteFloat(outfile, genericpage->static_wb[i].gp_fire_wait[j]); + + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + cf_WriteFloat(outfile, genericpage->static_wb[i].anim_time[j]); + cf_WriteFloat(outfile, genericpage->static_wb[i].anim_start_frame[j]); + cf_WriteFloat(outfile, genericpage->static_wb[i].anim_fire_frame[j]); + cf_WriteFloat(outfile, genericpage->static_wb[i].anim_end_frame[j]); + } + + cf_WriteByte(outfile, genericpage->static_wb[i].aiming_flags); + cf_WriteFloat(outfile, genericpage->static_wb[i].aiming_3d_dot); + cf_WriteFloat(outfile, genericpage->static_wb[i].aiming_3d_dist); + cf_WriteFloat(outfile, genericpage->static_wb[i].aiming_XZ_dot); + } + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + int j; + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + cf_WriteByte (outfile, GENERICPAGE_COMMAND_WB_WEAPON); + size = strlen(genericpage->weapon_name[i][j])+1+2; // 1 for the null charactor and 2 for the 2 indices + + cf_WriteByte (outfile,size); + + cf_WriteByte (outfile,i); + cf_WriteByte (outfile,j); + cf_WriteString (outfile,genericpage->weapon_name[i][j]); + } + } + + cf_WriteByte (outfile, GENERICPAGE_COMMAND_WB_QUADMASK); + cf_WriteByte (outfile, MAX_WBS_PER_OBJ); + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + cf_WriteByte (outfile, genericpage->static_wb[i].gp_quad_fire_mask); + } + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + int j; + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + cf_WriteByte (outfile, GENERICPAGE_COMMAND_WB_FIRE_SOUND); + size = strlen(genericpage->fire_sound_name[i][j])+1+2; // 1 for the null charactor and 2 for the 2 indices + + cf_WriteByte (outfile,size); + + cf_WriteByte (outfile,i); + cf_WriteByte (outfile,j); + cf_WriteString (outfile,genericpage->fire_sound_name[i][j]); + } + } + + for(i = 0; i < NUM_MOVEMENT_CLASSES; i++) + { + int j; + for(j = 0; j < NUM_ANIMS_PER_CLASS; j++) + { + cf_WriteByte (outfile, GENERICPAGE_COMMAND_ANIM_SOUND_NAME); + size = strlen(genericpage->anim_sound_name[i][j])+1+2; // 1 for the null charactor and 2 for the 2 indices + + cf_WriteByte (outfile,size); + + cf_WriteByte (outfile,i); + cf_WriteByte (outfile,j); + cf_WriteString (outfile,genericpage->anim_sound_name[i][j]); + } + } + + + cf_WriteByte (outfile,GENERICPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} + +// Given an open file pointer and a generic_page struct, writes that generic page out +void mng_WriteNewGenericPage (CFILE *outfile,mngs_generic_page *genericpage) +{ + int i,j; + + ASSERT (outfile!=NULL); + ASSERT (genericpage!=NULL); + ASSERT ((strlen(genericpage->image_name))>2); + + int offset=StartManagePage (outfile,PAGETYPE_GENERIC); + + cf_WriteShort (outfile,GENERICFILE_VERSION); + + cf_WriteByte (outfile,genericpage->objinfo_struct.type); + + + // Write out object name + cf_WriteString (outfile,genericpage->objinfo_struct.name); + + // Write out model names + cf_WriteString (outfile,genericpage->image_name); + cf_WriteString (outfile,genericpage->med_image_name); + cf_WriteString (outfile,genericpage->lo_image_name); + + + // Write out impact data + cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_size); + cf_WriteFloat(outfile, genericpage->objinfo_struct.impact_time); + cf_WriteFloat(outfile, genericpage->objinfo_struct.damage); + + // Write score + cf_WriteShort(outfile,genericpage->objinfo_struct.score); + + // Write ammo + if (genericpage->objinfo_struct.type == OBJ_POWERUP) + cf_WriteShort(outfile,genericpage->objinfo_struct.ammo_count); + + //Write script name + cf_WriteString (outfile,"");//genericpage->objinfo_struct.script_name + + //Write module name + cf_WriteString (outfile,genericpage->objinfo_struct.module_name); + + //Write scriptname override + cf_WriteString (outfile,genericpage->objinfo_struct.script_name_override); + + if (genericpage->objinfo_struct.description!=NULL) + { + // Write description if there is one + cf_WriteByte (outfile,1); + cf_WriteString (outfile,genericpage->objinfo_struct.description); + } + else + cf_WriteByte (outfile,0); + + // Write icon name + cf_WriteString (outfile,genericpage->objinfo_struct.icon_name); + + // Write LOD distances + cf_WriteFloat (outfile,genericpage->objinfo_struct.med_lod_distance); + cf_WriteFloat (outfile,genericpage->objinfo_struct.lo_lod_distance); + + // Write physics stuff + mng_WritePhysicsChunk (&genericpage->objinfo_struct.phys_info,outfile); + + + // Write size + cf_WriteFloat (outfile,genericpage->objinfo_struct.size); + + // Write light info + mng_WriteLightingChunk (&genericpage->objinfo_struct.lighting_info,outfile); + + + // Write hit points + cf_WriteInt (outfile,genericpage->objinfo_struct.hit_points); + + // Write flags + cf_WriteInt (outfile,genericpage->objinfo_struct.flags); + + // Write AI info + cf_WriteInt (outfile, genericpage->ai_info.flags); + cf_WriteByte (outfile, genericpage->ai_info.ai_class); + cf_WriteByte (outfile, genericpage->ai_info.ai_type); + cf_WriteByte (outfile, genericpage->ai_info.movement_type); + cf_WriteByte (outfile, genericpage->ai_info.movement_subtype); + cf_WriteFloat(outfile, genericpage->ai_info.fov); + + cf_WriteFloat(outfile, genericpage->ai_info.max_velocity); + cf_WriteFloat(outfile, genericpage->ai_info.max_delta_velocity); + cf_WriteFloat(outfile, genericpage->ai_info.max_turn_rate); + + // Makes sure there are no bugs as things are added and removed -- ask chris + genericpage->ai_info.notify_flags &= ~AI_NOTIFIES_ALWAYS_ON; + cf_WriteInt (outfile, genericpage->ai_info.notify_flags); + genericpage->ai_info.notify_flags |= AI_NOTIFIES_ALWAYS_ON; + + cf_WriteFloat(outfile, genericpage->ai_info.max_delta_turn_rate); + cf_WriteFloat(outfile, genericpage->ai_info.circle_distance); + cf_WriteFloat(outfile, genericpage->ai_info.attack_vel_percent); + cf_WriteFloat(outfile, genericpage->ai_info.dodge_percent); + cf_WriteFloat(outfile, genericpage->ai_info.dodge_vel_percent); + cf_WriteFloat(outfile, genericpage->ai_info.flee_vel_percent); + cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[0]); + cf_WriteFloat(outfile, genericpage->ai_info.melee_damage[1]); + cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[0]); + cf_WriteFloat(outfile, genericpage->ai_info.melee_latency[1]); + + cf_WriteFloat(outfile, genericpage->ai_info.curiousity); + cf_WriteFloat(outfile, genericpage->ai_info.night_vision); + cf_WriteFloat(outfile, genericpage->ai_info.fog_vision); + cf_WriteFloat(outfile, genericpage->ai_info.lead_accuracy); + cf_WriteFloat(outfile, genericpage->ai_info.lead_varience); + cf_WriteFloat(outfile, genericpage->ai_info.fire_spread); + cf_WriteFloat(outfile, genericpage->ai_info.fight_team); + cf_WriteFloat(outfile, genericpage->ai_info.fight_same); + cf_WriteFloat(outfile, genericpage->ai_info.agression); + cf_WriteFloat(outfile, genericpage->ai_info.hearing); + cf_WriteFloat(outfile, genericpage->ai_info.frustration); + cf_WriteFloat(outfile, genericpage->ai_info.roaming); + cf_WriteFloat(outfile, genericpage->ai_info.life_preservation); + cf_WriteFloat(outfile, genericpage->ai_info.avoid_friends_distance); + + cf_WriteFloat(outfile, genericpage->ai_info.biased_flight_importance); + cf_WriteFloat(outfile, genericpage->ai_info.biased_flight_min); + cf_WriteFloat(outfile, genericpage->ai_info.biased_flight_max); + + // Write out objects spewed + for (i = 0; iobjinfo_struct.f_dspew); + cf_WriteFloat(outfile, genericpage->objinfo_struct.dspew_percent[i]); + cf_WriteShort(outfile, genericpage->objinfo_struct.dspew_number[i]); + cf_WriteString(outfile, genericpage->dspew_name[i]); + } + + // Write out animation info + for (i=0;ianim[i].elem[j].from); + cf_WriteShort (outfile, genericpage->anim[i].elem[j].to); + cf_WriteFloat (outfile, genericpage->anim[i].elem[j].spc); + } + } + + // Write out weapon batteries + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + mng_WriteWeaponBatteryChunk (&genericpage->static_wb[i],outfile); + + // Write out weapon names + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + cf_WriteString (outfile,genericpage->weapon_name[i][j]); + } + + // Write out sounds + for (i=0;isound_name[i]); + + for (i=0;iai_sound_name[i]); + + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + cf_WriteString (outfile,genericpage->fire_sound_name[i][j]); + } + + for(i = 0; i < NUM_MOVEMENT_CLASSES; i++) + { + for(j = 0; j < NUM_ANIMS_PER_CLASS; j++) + cf_WriteString (outfile,genericpage->anim_sound_name[i][j]); + } + + // Write out respawn scalar + cf_WriteFloat (outfile,genericpage->objinfo_struct.respawn_scalar); + + //Write out death information + cf_WriteShort(outfile,MAX_DEATH_TYPES); + for (i=0;iobjinfo_struct.death_types[i].flags); + cf_WriteFloat(outfile,genericpage->objinfo_struct.death_types[i].delay_min); + cf_WriteFloat(outfile,genericpage->objinfo_struct.death_types[i].delay_max); + cf_WriteByte(outfile,genericpage->objinfo_struct.death_probabilities[i]); + } + + EndManagePage (outfile,offset); +} + +//Old delay types +#define OLD_DF_DELAY_NONE 0x0000000 +#define OLD_DF_DELAY_MIN_MAX 0x0000001 +#define OLD_DF_DELAY_FROM_ANIM 0x0000002 +#define OLD_DF_DELAY_MASK 0x0000003 +#define OLD_DF_DELAY_SHIFT 0 + +void GenericPageSetPowerupDefaultAmmo(object_info *ip) +{ + //Default is zero + ip->ammo_count = 0; + + //Set for specific types + if (!stricmp(ip->name,"Vauss")) + ip->ammo_count = 5000; + if (!stricmp(ip->name,"Napalm")) + ip->ammo_count = 500; + if (!stricmp(ip->name,"MassDriver")) + ip->ammo_count = 20; + + if (!stricmp(ip->name,"Frag")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"ImpactMortar")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"NapalmRocket")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"Cyclone")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"BlackShark")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"Concussion")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"Homing")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"Smart")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"Mega")) + ip->ammo_count = 1; + if (!stricmp(ip->name,"Guided")) + ip->ammo_count = 1; + + if (!stricmp(ip->name,"4PackHoming")) + ip->ammo_count = 4; + if (!stricmp(ip->name,"4PackConc")) + ip->ammo_count = 4; + if (!stricmp(ip->name,"4PackFrag")) + ip->ammo_count = 4; + if (!stricmp(ip->name,"4PackGuided")) + ip->ammo_count = 4; + + if (!stricmp(ip->name,"Vauss clip")) + ip->ammo_count = 1250; + if (!stricmp(ip->name,"MassDriverAmmo")) + ip->ammo_count = 5; + if (!stricmp(ip->name,"NapalmTank")) + ip->ammo_count = 100; +} + +// Reads a generic page from an open file. Returns 0 on error. +int mng_ReadNewGenericPage (CFILE *infile,mngs_generic_page *genericpage) +{ + int i,j; + + ASSERT (infile!=NULL); + mng_InitGenericPage (genericpage); + + int version=cf_ReadShort (infile); + + genericpage->objinfo_struct.type=cf_ReadByte (infile); + + + // Read object name + cf_ReadString (genericpage->objinfo_struct.name,PAGENAME_LEN,infile); + + // Read model names + cf_ReadString (genericpage->image_name,PAGENAME_LEN,infile); + cf_ReadString (genericpage->med_image_name,PAGENAME_LEN,infile); + cf_ReadString (genericpage->lo_image_name,PAGENAME_LEN,infile); + + + // Read out impact data + genericpage->objinfo_struct.impact_size=cf_ReadFloat (infile); + genericpage->objinfo_struct.impact_time=cf_ReadFloat (infile); + genericpage->objinfo_struct.damage=cf_ReadFloat (infile); + + // Read score + if (version >= 24) + genericpage->objinfo_struct.score = cf_ReadShort(infile); + else + genericpage->objinfo_struct.score = cf_ReadByte(infile); + + // Read ammo + if (genericpage->objinfo_struct.type == OBJ_POWERUP) { + if (version >= 25) + genericpage->objinfo_struct.ammo_count = cf_ReadShort(infile); + else + GenericPageSetPowerupDefaultAmmo(&genericpage->objinfo_struct); + } + else + genericpage->objinfo_struct.ammo_count = 0; + + //Read script name + char dummy[256]; + cf_ReadString (dummy,PAGENAME_LEN,infile);//genericpage->objinfo_struct.script_name + + if(version>=18) + { + cf_ReadString (genericpage->objinfo_struct.module_name,MAX_MODULENAME_LEN,infile); +#ifdef MACINTOSH //DAJ + char *dot = strchr(genericpage->objinfo_struct.module_name, '.'); + if(dot) + strcpy(dot, ".msl"); +#endif + }else + { + genericpage->objinfo_struct.module_name[0] = '\0'; + } + + if(version>=19) + { + cf_ReadString (genericpage->objinfo_struct.script_name_override,PAGENAME_LEN,infile); + }else + { + genericpage->objinfo_struct.script_name_override[0] = '\0'; + } + + int desc=cf_ReadByte (infile); + if (desc) + { + // Read description if there is one + char tempbuf[1024]; + + cf_ReadString (tempbuf,1024,infile); + int slen=strlen (tempbuf)+1; + + genericpage->objinfo_struct.description=(char *)mem_malloc (slen); + ASSERT (genericpage->objinfo_struct.description); + strcpy (genericpage->objinfo_struct.description,tempbuf); + } + else + genericpage->objinfo_struct.description=NULL; + + // Read icon name + cf_ReadString (genericpage->objinfo_struct.icon_name,PAGENAME_LEN,infile); + + // Read LOD distances + genericpage->objinfo_struct.med_lod_distance=cf_ReadFloat (infile); + genericpage->objinfo_struct.lo_lod_distance=cf_ReadFloat (infile); + + // Read physics stuff + mng_ReadPhysicsChunk (&genericpage->objinfo_struct.phys_info,infile); + + // Write size + genericpage->objinfo_struct.size=cf_ReadFloat(infile); + + // Write light info + mng_ReadLightingChunk (&genericpage->objinfo_struct.lighting_info,infile); + + + // Read hit points + genericpage->objinfo_struct.hit_points=cf_ReadInt(infile); + + // Read flags + genericpage->objinfo_struct.flags=cf_ReadInt (infile); + + // Write AI info + genericpage->ai_info.flags=cf_ReadInt(infile); + genericpage->ai_info.ai_class=cf_ReadByte (infile); + genericpage->ai_info.ai_type=cf_ReadByte (infile); + genericpage->ai_info.movement_type=cf_ReadByte (infile); + genericpage->ai_info.movement_subtype=cf_ReadByte (infile); + genericpage->ai_info.fov=cf_ReadFloat (infile); + + genericpage->ai_info.max_velocity=cf_ReadFloat (infile); + genericpage->ai_info.max_delta_velocity=cf_ReadFloat (infile); + genericpage->ai_info.max_turn_rate=cf_ReadFloat (infile); + + // Makes sure there are no bugs as things are added and removed -- ask chris + genericpage->ai_info.notify_flags &= ~AI_NOTIFIES_ALWAYS_ON; + genericpage->ai_info.notify_flags=cf_ReadInt(infile); + genericpage->ai_info.notify_flags |= AI_NOTIFIES_ALWAYS_ON; + + genericpage->ai_info.max_delta_turn_rate=cf_ReadFloat (infile); + genericpage->ai_info.circle_distance=cf_ReadFloat (infile); + genericpage->ai_info.attack_vel_percent=cf_ReadFloat (infile); + genericpage->ai_info.dodge_percent=cf_ReadFloat (infile); + genericpage->ai_info.dodge_vel_percent=cf_ReadFloat (infile); + genericpage->ai_info.flee_vel_percent=cf_ReadFloat (infile); + genericpage->ai_info.melee_damage[0]=cf_ReadFloat (infile); + genericpage->ai_info.melee_damage[1]=cf_ReadFloat (infile); + genericpage->ai_info.melee_latency[0]=cf_ReadFloat (infile); + genericpage->ai_info.melee_latency[1]=cf_ReadFloat (infile); + + genericpage->ai_info.curiousity=cf_ReadFloat (infile); + genericpage->ai_info.night_vision=cf_ReadFloat (infile); + genericpage->ai_info.fog_vision=cf_ReadFloat (infile); + genericpage->ai_info.lead_accuracy=cf_ReadFloat (infile); + genericpage->ai_info.lead_varience=cf_ReadFloat (infile); + genericpage->ai_info.fire_spread=cf_ReadFloat (infile); + genericpage->ai_info.fight_team=cf_ReadFloat (infile); + genericpage->ai_info.fight_same=cf_ReadFloat (infile); + genericpage->ai_info.agression=cf_ReadFloat (infile); + genericpage->ai_info.hearing=cf_ReadFloat (infile); + genericpage->ai_info.frustration=cf_ReadFloat (infile); + genericpage->ai_info.roaming=cf_ReadFloat (infile); + genericpage->ai_info.life_preservation=cf_ReadFloat (infile); + + if(version >= 16) + { + genericpage->ai_info.avoid_friends_distance =cf_ReadFloat (infile); + } + else if((genericpage->objinfo_struct.flags | OIF_USES_PHYSICS) && + genericpage->ai_info.max_velocity > 0.0f) + { + genericpage->ai_info.flags |= AIF_AUTO_AVOID_FRIENDS; + genericpage->ai_info.avoid_friends_distance = genericpage->ai_info.circle_distance / 10.f; + if(genericpage->ai_info.avoid_friends_distance < 4.0f) + genericpage->ai_info.avoid_friends_distance = 4.0f; + } + else + { + genericpage->ai_info.avoid_friends_distance = 4.0f; + } + + if(version >= 17) + { + genericpage->ai_info.biased_flight_importance = cf_ReadFloat(infile); + genericpage->ai_info.biased_flight_min = cf_ReadFloat(infile); + genericpage->ai_info.biased_flight_max = cf_ReadFloat(infile); + } + else + { + genericpage->ai_info.biased_flight_importance = .5f; + genericpage->ai_info.biased_flight_min = 10.0f; + genericpage->ai_info.biased_flight_max = 50.0f; + } + + + // Write out objects spewed + for (i = 0; iobjinfo_struct.f_dspew=cf_ReadByte (infile); + genericpage->objinfo_struct.dspew_percent[i]=cf_ReadFloat (infile); + genericpage->objinfo_struct.dspew_number[i]=cf_ReadShort (infile); + + // Read spew name + cf_ReadString (genericpage->dspew_name[i],PAGENAME_LEN,infile); + } + + // Read out animation info + for (i=0;ianim[i].elem[j].from=cf_ReadByte (infile); + genericpage->anim[i].elem[j].to=cf_ReadByte(infile); + } + else + { + genericpage->anim[i].elem[j].from=cf_ReadShort(infile); + genericpage->anim[i].elem[j].to=cf_ReadShort(infile); + } + genericpage->anim[i].elem[j].spc=cf_ReadFloat (infile); + } + } + + // read weapon batteries + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + if (version>=15) + mng_ReadWeaponBatteryChunk (&genericpage->static_wb[i],infile,2); + else + mng_ReadWeaponBatteryChunk (&genericpage->static_wb[i],infile,1); + } + + // read weapon names + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + cf_ReadString (genericpage->weapon_name[i][j],PAGENAME_LEN,infile); + } + + + // read sounds + ASSERT(MAX_OBJ_SOUNDS == 2); + for (i=0;isound_name[i],PAGENAME_LEN,infile); + if (version < 26) { //used to be three sounds + char temp_sound_name[PAGENAME_LEN]; + cf_ReadString (temp_sound_name,PAGENAME_LEN,infile); + } + + + for (i=0;iai_sound_name[i],PAGENAME_LEN,infile); + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + cf_ReadString (genericpage->fire_sound_name[i][j],PAGENAME_LEN,infile); + } + + for(i = 0; i < NUM_MOVEMENT_CLASSES; i++) + { + for(j = 0; j < NUM_ANIMS_PER_CLASS; j++) + cf_ReadString (genericpage->anim_sound_name[i][j],PAGENAME_LEN,infile); + } + + // Read respawn scalar + if (version>=21) + genericpage->objinfo_struct.respawn_scalar=cf_ReadFloat (infile); + else + genericpage->objinfo_struct.respawn_scalar=1.0; + + if (version >= 22) { + int n_death_types = cf_ReadShort(infile); + for (i=0;iobjinfo_struct.death_types[i].flags = flags; + genericpage->objinfo_struct.death_types[i].delay_min = cf_ReadFloat(infile); + genericpage->objinfo_struct.death_types[i].delay_max = cf_ReadFloat(infile); + genericpage->objinfo_struct.death_probabilities[i] = cf_ReadByte(infile); + + //Fix up for changed flags + if (version < 27) { + if ((flags & OLD_DF_DELAY_MASK) != OLD_DF_DELAY_MIN_MAX) { + genericpage->objinfo_struct.death_types[i].delay_min = 0.0; + genericpage->objinfo_struct.death_types[i].delay_max = 0.0; + } + flags &= ~DF_UNUSED; + } + } + } + + //Set score from hitpoints if old version + if (version < 24) { + if ((genericpage->objinfo_struct.type == OBJ_ROBOT) || (genericpage->objinfo_struct.type == OBJ_BUILDING && (genericpage->objinfo_struct.flags & OIF_CONTROL_AI))) + if (genericpage->objinfo_struct.flags & OIF_DESTROYABLE) + genericpage->objinfo_struct.score = 3 * genericpage->objinfo_struct.hit_points; + } + + ASSERT(genericpage->objinfo_struct.type != OBJ_NONE); + + return 1; // successfully read +} + + + +// Reads a generic page from an open file. Returns 0 on error. +int mng_ReadGenericPage (CFILE *infile,mngs_generic_page *genericpage) +{ + int done=0; + char command; + ushort len; + int i,temp,version,t; + + if (!Old_table_method) + return mng_ReadNewGenericPage (infile,genericpage); + + ASSERT (infile!=NULL); + mng_InitGenericPage (genericpage); + + while (!done) + { + // Read in command byte then read in the length of that commands data + + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + + switch (command) + { + case GENERICPAGE_COMMAND_END: + done=1; + break; + case GENERICPAGE_COMMAND_VERSION: + version = cf_ReadByte(infile); + break; + case GENERICPAGE_COMMAND_TYPE: + genericpage->objinfo_struct.type = cf_ReadByte(infile); + break; + case GENERICPAGE_COMMAND_SCORE: + genericpage->objinfo_struct.score = cf_ReadByte(infile); + break; + case GENERICPAGE_COMMAND_SCRIPT: // Obsolete as of 09-24-97 + cf_ReadByte(infile); + break; + case GENERICPAGE_COMMAND_SCRIPTNAME: + { + char dummy[256]; + cf_ReadString(dummy, len+1, infile);//genericpage->objinfo_struct.script_name + }break; + case GENERICPAGE_COMMAND_DESCRIPTION: + genericpage->objinfo_struct.description=(char *)mem_malloc (len+1); + ASSERT (genericpage->objinfo_struct.description); // out of memory! + cf_ReadString(genericpage->objinfo_struct.description, len+1, infile); + break; + case GENERICPAGE_COMMAND_ICON_NAME: + cf_ReadString(genericpage->objinfo_struct.icon_name, len+1, infile); + break; + case GENERICPAGE_COMMAND_IMAGE_NAME: // the name of the generic model + cf_ReadString (genericpage->image_name,len+1,infile); + break; + case GENERICPAGE_COMMAND_LOD_MODELS: // the name of the lower res models + cf_ReadString (genericpage->med_image_name,len+1,infile); + cf_ReadString (genericpage->lo_image_name,len+1,infile); + break; + case GENERICPAGE_COMMAND_NAME: + cf_ReadString (genericpage->objinfo_struct.name,len+1,infile); + break; + case GENERICPAGE_COMMAND_SIZE: + genericpage->objinfo_struct.size=cf_ReadFloat(infile); + break; + case GENERICPAGE_COMMAND_LOD_DISTANCE: + genericpage->objinfo_struct.med_lod_distance=cf_ReadFloat(infile); + genericpage->objinfo_struct.lo_lod_distance=cf_ReadFloat(infile); + break; + + case GENERICPAGE_COMMAND_DSPEW_INFO: + { + int i = cf_ReadByte(infile); + + genericpage->objinfo_struct.f_dspew = cf_ReadByte(infile); + genericpage->objinfo_struct.dspew_percent[i] = cf_ReadFloat(infile); + genericpage->objinfo_struct.dspew_number[i] = cf_ReadShort(infile); + cf_ReadString (genericpage->dspew_name[i],len-7,infile); + } + break; + + case GENERICPAGE_COMMAND_WB_FLAGS: + { + for (int i=0;istatic_wb[i].flags=cf_ReadByte (infile); + } + break; + case GENERICPAGE_COMMAND_IMPACT: + genericpage->objinfo_struct.impact_size = cf_ReadFloat(infile); + genericpage->objinfo_struct.impact_time = cf_ReadFloat(infile); + genericpage->objinfo_struct.damage = cf_ReadFloat(infile); + break; + case GENERICPAGE_COMMAND_FLICKER_DISTANCE: + genericpage->objinfo_struct.lighting_info.flicker_distance=cf_ReadFloat(infile); + break; + case GENERICPAGE_COMMAND_DIRECTION_DOT: + genericpage->objinfo_struct.lighting_info.directional_dot=cf_ReadFloat(infile); + break; + case GENERICPAGE_COMMAND_AI_INFO2: + genericpage->ai_info.curiousity=cf_ReadFloat(infile); + genericpage->ai_info.night_vision=cf_ReadFloat(infile); + genericpage->ai_info.fog_vision=cf_ReadFloat(infile); + genericpage->ai_info.lead_accuracy=cf_ReadFloat(infile); + genericpage->ai_info.lead_varience=cf_ReadFloat(infile); + genericpage->ai_info.fire_spread=cf_ReadFloat(infile); + genericpage->ai_info.fight_team=cf_ReadFloat(infile); + genericpage->ai_info.fight_same=cf_ReadFloat(infile); + genericpage->ai_info.agression=cf_ReadFloat(infile); + genericpage->ai_info.hearing=cf_ReadFloat(infile); + genericpage->ai_info.frustration=cf_ReadFloat(infile); + genericpage->ai_info.roaming=cf_ReadFloat(infile); + genericpage->ai_info.life_preservation=cf_ReadFloat(infile); + break; + case GENERICPAGE_COMMAND_LIGHT_CAST: + genericpage->objinfo_struct.lighting_info.light_distance=cf_ReadFloat(infile); + + genericpage->objinfo_struct.lighting_info.red_light1=cf_ReadFloat(infile); + genericpage->objinfo_struct.lighting_info.green_light1=cf_ReadFloat(infile); + genericpage->objinfo_struct.lighting_info.blue_light1=cf_ReadFloat(infile); + + if (version>=2) + genericpage->objinfo_struct.lighting_info.time_interval=cf_ReadFloat(infile); + + if (version>=3) + { + genericpage->objinfo_struct.lighting_info.red_light2=cf_ReadFloat(infile); + genericpage->objinfo_struct.lighting_info.green_light2=cf_ReadFloat(infile); + genericpage->objinfo_struct.lighting_info.blue_light2=cf_ReadFloat(infile); + + genericpage->objinfo_struct.lighting_info.flags=cf_ReadInt(infile); + genericpage->objinfo_struct.lighting_info.timebits=cf_ReadInt(infile); + genericpage->objinfo_struct.lighting_info.angle=cf_ReadByte(infile); + } + if (version>=5) + { + genericpage->objinfo_struct.lighting_info.lighting_render_type=cf_ReadByte(infile); + } + else + genericpage->objinfo_struct.lighting_info.lighting_render_type=LRT_STATIC; + + + break; + case GENERICPAGE_COMMAND_HITPOINTS: + genericpage->objinfo_struct.hit_points = cf_ReadInt(infile); + break; + case GENERICPAGE_COMMAND_PHYS_MASS: + genericpage->objinfo_struct.phys_info.mass=cf_ReadFloat(infile); + break; + case GENERICPAGE_COMMAND_PHYS_DRAG: + genericpage->objinfo_struct.phys_info.drag=cf_ReadFloat(infile); + break; + case GENERICPAGE_COMMAND_ANIM_STATES: + temp=cf_ReadByte (infile); + { + int j; + + for(j = 0; j < NUM_ANIMS_PER_CLASS;j++) + { + if(version < 20) + { + genericpage->anim[temp].elem[j].from = cf_ReadByte(infile); + genericpage->anim[temp].elem[j].to = cf_ReadByte(infile); + } + else + { + genericpage->anim[temp].elem[j].from = cf_ReadShort(infile); + genericpage->anim[temp].elem[j].to = cf_ReadShort(infile); + } + + if(version <= 3) + genericpage->anim[temp].elem[j].spc = 1.0f; + else + genericpage->anim[temp].elem[j].spc = cf_ReadFloat(infile); + } + } + break; + case GENERICPAGE_COMMAND_FLAGS: + { + int t = cf_ReadInt(infile); + genericpage->objinfo_struct.flags = t; + break; + } + case GENERICPAGE_COMMAND_WB_INFO: + { + int i,j; + i = cf_ReadByte(infile); + + if(version <= 7) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) genericpage->static_wb[i].gp_weapon_index[j] = cf_ReadInt(infile); + genericpage->static_wb[i].aiming_gp_index = cf_ReadInt(infile); + } + else + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) genericpage->static_wb[i].gp_weapon_index[j] = cf_ReadShort(infile); + genericpage->static_wb[i].aiming_gp_index = cf_ReadShort(infile); + } + + genericpage->static_wb[i].num_masks = cf_ReadByte(infile); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) genericpage->static_wb[i].gp_fire_masks[j] = cf_ReadByte(infile); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) genericpage->static_wb[i].gp_fire_wait[j] = cf_ReadFloat(infile); + + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if(version < 12) + { + genericpage->static_wb[i].anim_time[j] = 0.0f; + genericpage->static_wb[i].anim_start_frame[j] = 0.0f; + genericpage->static_wb[i].anim_fire_frame[j] = 0.0f; + genericpage->static_wb[i].anim_end_frame[j] = 0.0f; + } + else + { + genericpage->static_wb[i].anim_time[j] = cf_ReadFloat(infile); + genericpage->static_wb[i].anim_start_frame[j] = cf_ReadFloat(infile); + genericpage->static_wb[i].anim_fire_frame[j] = cf_ReadFloat(infile); + genericpage->static_wb[i].anim_end_frame[j] = cf_ReadFloat(infile); + } + } + + genericpage->static_wb[i].aiming_flags = cf_ReadByte(infile); + genericpage->static_wb[i].aiming_3d_dot = cf_ReadFloat(infile); + genericpage->static_wb[i].aiming_3d_dist = cf_ReadFloat(infile); + genericpage->static_wb[i].aiming_XZ_dot = cf_ReadFloat(infile); + break; + } + case GENERICPAGE_COMMAND_WB_QUADMASK: + { + for(int i = 0; i < MAX_WBS_PER_OBJ; i++) + { + genericpage->static_wb[i].gp_quad_fire_mask = cf_ReadByte(infile); + } + break; + } + case GENERICPAGE_COMMAND_WB_WEAPON: + { + int i,j; + + i = cf_ReadByte (infile); + j = cf_ReadByte (infile); + cf_ReadString (genericpage->weapon_name[i][j],len-1,infile); + break; + } + case GENERICPAGE_COMMAND_WB_FIRE_SOUND: + { + int i,j; + + i = cf_ReadByte (infile); + j = cf_ReadByte (infile); + cf_ReadString (genericpage->fire_sound_name[i][j],len-1,infile); + break; + } + case GENERICPAGE_COMMAND_ANIM_SOUND_NAME: + { + int i,j; + + i = cf_ReadByte (infile); + j = cf_ReadByte (infile); + cf_ReadString (genericpage->anim_sound_name[i][j],len-1,infile); + break; + } + case GENERICPAGE_COMMAND_SOUND_NAME: + { + int i; + + i = cf_ReadByte (infile); + + cf_ReadString (genericpage->sound_name[i],len,infile); + + break; + } + case GENERICPAGE_COMMAND_AI_SOUND_NAME: + { + int i; + + i = cf_ReadByte (infile); + + cf_ReadString (genericpage->ai_sound_name[i],len,infile); + + break; + } + case GENERICPAGE_COMMAND_AI_INFO: + if(version <= 6) + { + genericpage->ai_info.flags=cf_ReadInt(infile); + } + else + { + // Makes sure there are no bugs as things are added and removed -- ask chris + genericpage->ai_info.notify_flags = AI_NOTIFIES_ALWAYS_ON; + + genericpage->ai_info.flags=cf_ReadInt(infile); + genericpage->ai_info.ai_class=cf_ReadByte(infile); + genericpage->ai_info.ai_type=cf_ReadByte(infile); + if(version < 13) + cf_ReadInt(infile); + genericpage->ai_info.movement_type=cf_ReadByte(infile); + genericpage->ai_info.movement_subtype=cf_ReadByte(infile); + genericpage->ai_info.fov=cf_ReadFloat(infile); + + if(version >= 10) + { + genericpage->ai_info.max_velocity=cf_ReadFloat(infile); + genericpage->ai_info.max_delta_velocity=cf_ReadFloat(infile); + genericpage->ai_info.max_turn_rate=cf_ReadFloat(infile); + + // Makes sure there are no bugs as things are added and removed -- ask chris + genericpage->ai_info.notify_flags|=cf_ReadInt(infile); + } + + if(version >= 11) + { + genericpage->ai_info.max_delta_turn_rate=cf_ReadFloat(infile); + genericpage->ai_info.circle_distance=cf_ReadFloat(infile); + genericpage->ai_info.attack_vel_percent=cf_ReadFloat(infile); + genericpage->ai_info.dodge_percent=cf_ReadFloat(infile); + genericpage->ai_info.dodge_vel_percent=cf_ReadFloat(infile); + genericpage->ai_info.flee_vel_percent=cf_ReadFloat(infile); + genericpage->ai_info.melee_damage[0]=cf_ReadFloat(infile); + genericpage->ai_info.melee_damage[1]=cf_ReadFloat(infile); + genericpage->ai_info.melee_latency[0]=cf_ReadFloat(infile); + genericpage->ai_info.melee_latency[1]=cf_ReadFloat(infile); + } + else + { + genericpage->ai_info.max_delta_turn_rate = 16000.0; + genericpage->ai_info.circle_distance = 10.0f; + genericpage->ai_info.attack_vel_percent = 1.0f; + genericpage->ai_info.dodge_percent = .4f; + genericpage->ai_info.dodge_vel_percent = .5f; + genericpage->ai_info.flee_vel_percent = 1.0f; + genericpage->ai_info.melee_damage[0] = 9.0f; + genericpage->ai_info.melee_damage[1] = 13.0f; + genericpage->ai_info.melee_latency[0] = 3.1f; + genericpage->ai_info.melee_latency[1] = 4.5f; + } + + if(version >= 16) + { + genericpage->ai_info.avoid_friends_distance = cf_ReadFloat(infile); + } + } + break; + case GENERICPAGE_COMMAND_PHYS_FLAGS: + genericpage->objinfo_struct.phys_info.flags=cf_ReadInt(infile); + break; + + case GENERICPAGE_COMMAND_ROT_DRAG: + genericpage->objinfo_struct.phys_info.rotdrag=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_FULL_THRUST: + genericpage->objinfo_struct.phys_info.full_thrust=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_FULL_ROTTHRUST: + genericpage->objinfo_struct.phys_info.full_rotthrust=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_TURNROLL_RATE: + genericpage->objinfo_struct.phys_info.max_turnroll_rate=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_TURNROLL_RATIO: + genericpage->objinfo_struct.phys_info.turnroll_ratio=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_WIGGLE_AMP: + genericpage->objinfo_struct.phys_info.wiggle_amplitude=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_WIGGLE_FREQ: + genericpage->objinfo_struct.phys_info.wiggles_per_sec=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_INT_VELOCITY: + genericpage->objinfo_struct.phys_info.velocity.z=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_INT_ROTVEL: + genericpage->objinfo_struct.phys_info.rotvel.x=cf_ReadFloat (infile); + genericpage->objinfo_struct.phys_info.rotvel.y=cf_ReadFloat (infile); + genericpage->objinfo_struct.phys_info.rotvel.z=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_NUM_BOUNCES: + genericpage->objinfo_struct.phys_info.num_bounces=cf_ReadInt (infile); + break; + + case GENERICPAGE_COMMAND_COEFF_REST: + genericpage->objinfo_struct.phys_info.coeff_restitution=cf_ReadFloat (infile); + break; + + case GENERICPAGE_COMMAND_HIT_DIE_DOT: + genericpage->objinfo_struct.phys_info.hit_die_dot=cf_ReadFloat (infile); + break; + + default: + // Ignore the ones we don't know + for (i=0;iobjinfo_struct.type != OBJ_NONE); + + if(version < 16) + { + genericpage->ai_info.flags |= AIF_AUTO_AVOID_FRIENDS; + genericpage->ai_info.avoid_friends_distance = genericpage->ai_info.circle_distance / 10.f; + if(genericpage->ai_info.avoid_friends_distance < 4.0f) + genericpage->ai_info.avoid_friends_distance = 4.0f; + } + + if (!strnicmp ("INVALID",genericpage->med_image_name,7)) + strcpy (genericpage->med_image_name,""); + if (!strnicmp ("INVALID",genericpage->lo_image_name,7)) + strcpy (genericpage->lo_image_name,""); + + for (i=0;isound_name[i],7)) + strcpy (genericpage->sound_name[i],""); + } + + for (i=0;idspew_name[i],7)) + strcpy (genericpage->dspew_name[i],""); + } + + for (i=0;iai_sound_name[i],7)) + strcpy (genericpage->ai_sound_name[i],""); + } + + for (i=0;ifire_sound_name[i][t],7)) + strcpy (genericpage->fire_sound_name[i][t],""); + } + } + + for (i=0;ianim_sound_name[i][t],7)) + strcpy (genericpage->anim_sound_name[i][t],""); + } + } + return 1; // successfully read +} + +// Reads in the generic named "name" into genericpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificGenericPage (char *name,mngs_generic_page *genericpage,int offset) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + int first_try=1; + char tablename[TABLE_NAME_LEN]; + + if (Loading_locals) + { + infile=cfopen (LocalTableFilename,"rb"); + } + else if (Loading_addon_table!=-1) + { + infile=cfopen (AddOnDataTables[Loading_addon_table].AddOnTableFilename,"rb"); + } + else + { + if (Network_up && Starting_editor) + { + int farg=FindArg("-filter"); + + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + + infile=cfopen (tablename,"rb"); + } + else + { + infile = NULL; + if(TablefileNameOverride) + { + infile=cfopen(TablefileNameOverride,"rb"); + } + + if(!infile) + infile=cfopen (TableFilename,"rb"); + } + } + + if (!infile) + { + mprintf ((0,"Couldn't open table file to find generic!\n")); + Int3(); + return 0; + } + + +try_again:; + + if (offset) + cfseek (infile,offset,SEEK_SET); + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + + // If not a generic page, just read it in and ignore it + if (pagetype!=PAGETYPE_GENERIC) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + + mng_ReadNewGenericPage (infile,genericpage); + + if (!stricmp(name,genericpage->objinfo_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + else + { + if (genericpage->objinfo_struct.description!=NULL) + { + mem_free (genericpage->objinfo_struct.description); + genericpage->objinfo_struct.description = NULL; + } + genericpage->objinfo_struct.icon_name[0] = '\0'; + } + + } + + cfclose (infile); + + if (!found && first_try) { + done = first_try = 0; + infile=cfopen ("extra.gam","rb"); + if (infile) + goto try_again; + } + + return found; // successful! +} + +// Given a generic page, allocs a generic and calls AssignGenericPageTogeneric to actually +// load models and values. Rturns generic handle on success, -1 if fail +int mng_SetAndLoadGeneric (mngs_generic_page *genericpage,CFILE *infile) +{ + int i, j, n; + bool f_anim = false; + bool f_weapons = false; + bool f_ai = false; + + f_ai = (genericpage->objinfo_struct.flags & OIF_CONTROL_AI)?true:false; + + if(f_ai) + { + f_weapons = f_anim = true; + }else + { + if(genericpage->objinfo_struct.type == OBJ_ROBOT || genericpage->objinfo_struct.type == OBJ_BUILDING) { + f_anim = true; + } + else if(genericpage->objinfo_struct.type == OBJ_POWERUP) { + if(f_ai) { + f_anim = true; + } else { + for (i=0;ianim[i].elem[j].to != 0 || genericpage->anim[i].elem[j].from != 0) { + f_anim = true; + break; + } + } + } + } + } + else if(genericpage->objinfo_struct.type == OBJ_CLUTTER) { + if(f_ai) { + f_anim = true; + } else { + for (i=0;ianim[i].elem[j].to != 0 || genericpage->anim[i].elem[j].from != 0) { + f_anim = true; + break; + } + } + } + } + } + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) { + for(j = 0; j < genericpage->static_wb[i].num_masks; j++) { + if( genericpage->static_wb[i].gp_fire_masks[j] != 0) { + f_weapons = true; + break; + } + } + } + } + + if(Running_editor) + { + f_ai = f_weapons = f_anim = true; + } + + n=AllocObjectID(genericpage->objinfo_struct.type,f_anim,f_weapons,f_ai); + if (n<0) + return -1; + if (!mng_AssignGenericPageToObjInfo(genericpage,n,infile)) + return -1; + + return n; +} + +// Given a genericpage and a generic handle, attempts to make generic n correspond to +// to the genericpage. +// Returns 1 on success, 0 otherwise +int mng_AssignGenericPageToObjInfo(mngs_generic_page *genericpage,int n,CFILE *infile) +{ + object_info *objinfopointer=&Object_info[n]; + int img_handle; + int i, j; + + // copy our values +// memcpy (objinfopointer,&genericpage->objinfo_struct,sizeof(*objinfopointer)); + memcpy (objinfopointer,&genericpage->objinfo_struct,sizeof(*objinfopointer)-sizeof(anim_elem *)-sizeof(otype_wb_info *)-sizeof(t_ai_info *)); + strcpy (objinfopointer->name,genericpage->objinfo_struct.name); + if(objinfopointer->anim) + memcpy(objinfopointer->anim,&genericpage->anim,sizeof(genericpage->anim)); + + if(objinfopointer->static_wb) + memcpy(objinfopointer->static_wb,&genericpage->static_wb,sizeof(genericpage->static_wb)); + + if(objinfopointer->ai_info) + memcpy(objinfopointer->ai_info,&genericpage->ai_info,sizeof(genericpage->ai_info)); + + objinfopointer->multi_allowed = 1; + + //since the description pointer was just copied over, no need to malloc mem, copy and then free old, just move ptr + genericpage->objinfo_struct.description = NULL; + + strcpy(objinfopointer->icon_name,genericpage->objinfo_struct.icon_name); + // First see if our image differs from the one on the net + // If it is, make a copy + // If its a release version, don't do any of this + + #ifndef RELEASE + if (Network_up) + { + char str[200]; + char netstr[200]; + + ddio_MakePath (str,LocalModelsDir,genericpage->image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,genericpage->image_name,NULL); + + UpdatePrimitive (str,netstr,genericpage->image_name,PAGETYPE_GENERIC,objinfopointer->name); + + if (stricmp (genericpage->med_image_name,"INVALID NAME") && genericpage->med_image_name[0]!=0) + { + ddio_MakePath (str,LocalModelsDir,genericpage->med_image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,genericpage->med_image_name,NULL); + + UpdatePrimitive (str,netstr,genericpage->med_image_name,PAGETYPE_GENERIC,objinfopointer->name); + } + + if (stricmp (genericpage->lo_image_name,"INVALID NAME") && genericpage->lo_image_name[0]!=0) + { + ddio_MakePath (str,LocalModelsDir,genericpage->lo_image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,genericpage->lo_image_name,NULL); + + UpdatePrimitive (str,netstr,genericpage->lo_image_name,PAGETYPE_GENERIC,objinfopointer->name); + } + } + #endif + + // Try and load our generic model from the disk + + img_handle=LoadPolyModel (genericpage->image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignGenericPage...\n",genericpage->image_name)); + objinfopointer->render_handle=-1; + return 0; + } + else + objinfopointer->render_handle=img_handle; + + + if (stricmp (genericpage->med_image_name,"INVALID NAME") && genericpage->med_image_name[0]!=0) + { + img_handle=LoadPolyModel (genericpage->med_image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignGenericPage...\n",genericpage->med_image_name)); + objinfopointer->med_render_handle=-1; + return 0; + } + else + objinfopointer->med_render_handle=img_handle; + } + else + objinfopointer->med_render_handle=-1; + + if (stricmp (genericpage->lo_image_name,"INVALID NAME") && genericpage->lo_image_name[0]!=0) + { + img_handle=LoadPolyModel (genericpage->lo_image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignGenericPage...\n",genericpage->lo_image_name)); + objinfopointer->lo_render_handle=-1; + return 0; + } + else + objinfopointer->lo_render_handle=img_handle; + } + else + objinfopointer->lo_render_handle=-1; + + + // Try and load the various sounds + for (i=0;isound_name[i],"INVALID NAME") && genericpage->sound_name[i][0]!=0) + { + int sound_handle=mng_GetGuaranteedSoundPage (genericpage->sound_name[i]); + + if (sound_handle<0) + { + mprintf ((0,"Couldn't load sound file '%s' in AssignPowPage %s...\n",genericpage->sound_name[i],genericpage->objinfo_struct.name)); + objinfopointer->sounds[i]=SOUND_NONE_INDEX; + } + else + objinfopointer->sounds[i]=sound_handle; + } + else + objinfopointer->sounds[i]=SOUND_NONE_INDEX; + } + + for(i=0;idspew_name[i][0] != '\0') + { + int obj_handle = mng_GetGuaranteedGenericPage(genericpage->dspew_name[i], infile); + + if(obj_handle < 0) + { + objinfopointer->dspew[i] = 0; + objinfopointer->dspew_number[i] = 0; + objinfopointer->dspew_percent[i] = 0.0f; + } + else + { + objinfopointer->dspew[i] = obj_handle; + } + } + else + { + objinfopointer->dspew[i] = -1; + objinfopointer->dspew_number[i] = 0; + objinfopointer->dspew_percent[i] = 0.0f; + } + } + + // Try and load the various sounds + if(objinfopointer->ai_info) { + for (i=0;iai_sound_name[i],"INVALID NAME") && genericpage->ai_sound_name[i][0]!=0) + { + int sound_handle=mng_GetGuaranteedSoundPage (genericpage->ai_sound_name[i]); + + if (sound_handle<0) + { + mprintf ((0,"Couldn't load ai sound file '%s' in AssignPowPage %s...\n",genericpage->ai_sound_name[i],genericpage->objinfo_struct.name)); + objinfopointer->ai_info->sound[i]=SOUND_NONE_INDEX; + } + else + objinfopointer->ai_info->sound[i]=sound_handle; + } + else + objinfopointer->ai_info->sound[i]=SOUND_NONE_INDEX; + } + } + + + // Try and load the various weapons + if(objinfopointer->static_wb) { + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if(genericpage->weapon_name[i][j][0] != '\0') + { + int weapon_handle = mng_GetGuaranteedWeaponPage (genericpage->weapon_name[i][j]); + + if (weapon_handle < 0) + { + mprintf ((0,"Couldn't load weapon file '%s' in AssignPowPage %s...\n",genericpage->weapon_name[i][j],genericpage->objinfo_struct.name)); + objinfopointer->static_wb[i].gp_weapon_index[j] = LASER_INDEX; + } + else + objinfopointer->static_wb[i].gp_weapon_index[j] = weapon_handle; + } + else + objinfopointer->static_wb[i].gp_weapon_index[j] = LASER_INDEX; + + } + } + + // Try and load the various wb sounds + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if(genericpage->fire_sound_name[i][j][0] != '\0') + { + int fire_sound_handle = mng_GetGuaranteedSoundPage (genericpage->fire_sound_name[i][j]); + + if (fire_sound_handle < 0) + { + mprintf ((0,"Couldn't load fire sound file '%s' in AssignPowPage %s...\n",genericpage->fire_sound_name[i][j],genericpage->objinfo_struct.name)); + objinfopointer->static_wb[i].fm_fire_sound_index[j] = SOUND_NONE_INDEX; + } + else + objinfopointer->static_wb[i].fm_fire_sound_index[j] = fire_sound_handle; + } + else + objinfopointer->static_wb[i].fm_fire_sound_index[j] = SOUND_NONE_INDEX; + } + } + } + + // Try and load the various wb sounds + if(objinfopointer->anim) { + for(i = 0; i < NUM_MOVEMENT_CLASSES; i++) + { + for(j = 0; j < NUM_ANIMS_PER_CLASS; j++) + { + if(stricmp(genericpage->anim_sound_name[i][j], "INVALID NAME") && genericpage->anim_sound_name[i][j][0]!=0) + { + int anim_sound_handle = mng_GetGuaranteedSoundPage (genericpage->anim_sound_name[i][j]); + + if (anim_sound_handle < 0) + { + mprintf ((0,"Couldn't load anim sound file '%s' in AssignPowPage %s...\n",genericpage->anim_sound_name[i][j],genericpage->objinfo_struct.name)); + objinfopointer->anim[i].elem[j].anim_sound_index = SOUND_NONE_INDEX; + } + else + objinfopointer->anim[i].elem[j].anim_sound_index = anim_sound_handle; + } + else + objinfopointer->anim[i].elem[j].anim_sound_index = SOUND_NONE_INDEX; + } + } + } + +// objinfopointer->size=(ComputeDefaultSize(objinfopointer->render_handle, &objinfopointer->size_offset, 0, 0)); + + return 1; +} + +// Copies values from a generic into a generic_page +void mng_AssignObjInfoToGenericPage(int n,mngs_generic_page *genericpage) +{ + object_info *objinfopointer=&Object_info[n]; + int i,j; + + // Assign the values + memcpy (&genericpage->objinfo_struct,objinfopointer,sizeof(*objinfopointer)); + strcpy (genericpage->objinfo_struct.name,objinfopointer->name); + + if(objinfopointer->anim) + memcpy(&genericpage->anim,objinfopointer->anim,sizeof(genericpage->anim)); + + if(objinfopointer->static_wb) + memcpy(&genericpage->static_wb,objinfopointer->static_wb,sizeof(genericpage->static_wb)); + + if(objinfopointer->ai_info) + memcpy(&genericpage->ai_info,objinfopointer->ai_info,sizeof(genericpage->ai_info)); + + if (objinfopointer->description!=NULL) + { + int len=strlen (objinfopointer->description); + genericpage->objinfo_struct.description=(char *)mem_malloc(len+1); + ASSERT (genericpage->objinfo_struct.description); + strcpy (genericpage->objinfo_struct.description,objinfopointer->description); + }else + genericpage->objinfo_struct.description = NULL; + + strcpy(genericpage->objinfo_struct.icon_name,objinfopointer->icon_name); + + if (objinfopointer->render_handle!=-1) + strcpy (genericpage->image_name,Poly_models[objinfopointer->render_handle].name); + else + strcpy (genericpage->image_name,""); + + if (objinfopointer->med_render_handle!=-1) + strcpy (genericpage->med_image_name,Poly_models[objinfopointer->med_render_handle].name); + else + strcpy (genericpage->med_image_name,""); + + if (objinfopointer->lo_render_handle!=-1) + strcpy (genericpage->lo_image_name,Poly_models[objinfopointer->lo_render_handle].name); + else + strcpy (genericpage->lo_image_name,""); + + for (i=0;isounds[i]!=SOUND_NONE_INDEX) + strcpy (genericpage->sound_name[i],Sounds[objinfopointer->sounds[i]].name); + else + strcpy (genericpage->sound_name[i],""); + } + + for (i=0; idspew[i] != -1) && Object_info[objinfopointer->dspew[i]].type != OBJ_NONE) + strcpy(genericpage->dspew_name[i], Object_info[objinfopointer->dspew[i]].name); + else + strcpy(genericpage->dspew_name[i], "\0"); + } + + for (i=0;iai_info && objinfopointer->ai_info->sound[i] != SOUND_NONE_INDEX) + strcpy (genericpage->ai_sound_name[i],Sounds[objinfopointer->ai_info->sound[i]].name); + else + strcpy (genericpage->ai_sound_name[i],""); + } + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if (objinfopointer->static_wb && objinfopointer->static_wb[i].fm_fire_sound_index[j] >= 0) + strcpy (genericpage->fire_sound_name[i][j], Sounds[objinfopointer->static_wb[i].fm_fire_sound_index[j]].name); + else + strcpy (genericpage->fire_sound_name[i][j],""); + } + } + + for(i = 0; i < NUM_MOVEMENT_CLASSES; i++) + { + for(j = 0; j < NUM_ANIMS_PER_CLASS; j++) + { + if (objinfopointer->anim && objinfopointer->anim[i].elem[j].anim_sound_index >= 0) + strcpy (genericpage->anim_sound_name[i][j], Sounds[objinfopointer->anim[i].elem[j].anim_sound_index].name); + else + strcpy (genericpage->anim_sound_name[i][j],""); + } + } + + for(i = 0; i < MAX_WBS_PER_OBJ; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if (objinfopointer->static_wb && objinfopointer->static_wb[i].gp_weapon_index[j] >= 0) + strcpy (genericpage->weapon_name[i][j], Weapons[objinfopointer->static_wb[i].gp_weapon_index[j]].name); + else + strcpy (genericpage->weapon_name[i][j],"Laser"); + } + } + +} + +// Loads a generic found in the net table file. It then allocs a generic and +// then calls SetAndLoadgeneric to actually load in any images/models associated +// with it +void mng_LoadNetGenericPage(CFILE *infile,bool overlay) +{ + mngs_generic_page genericpage; + memset(&genericpage, 0, sizeof(mngs_generic_page)); + + if (mng_ReadNewGenericPage (infile,&genericpage)) + { + int n = FindObjectIDName (genericpage.objinfo_struct.name); + if (n!=-1) + { + if(overlay) + { + mprintf((0,"OVERLAYING GENERIC %s\n",genericpage.objinfo_struct.name)); + mng_FreePagetypePrimitives (PAGETYPE_GENERIC,genericpage.objinfo_struct.name,0); + mng_AssignGenericPageToObjInfo(&genericpage,n); + } + return; // A weapon has already loaded this generic + } + + int ret=mng_SetAndLoadGeneric (&genericpage,infile); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load genericpage named %s!\n",genericpage.objinfo_struct.name)); +} + +// Reads a generic page from a local table file. It then allocs a generic and +// loads any images/models associated with that generic +void mng_LoadLocalGenericPage(CFILE *infile) +{ + mngs_generic_page genericpage; + int ok=0; + memset(&genericpage, 0, sizeof(mngs_generic_page)); + + if (mng_ReadNewGenericPage (infile,&genericpage)) + { + // Check to see if this is a local copy that is supposed + // to go over a network copy (supersede the net copy) + + int i=FindObjectIDName (genericpage.objinfo_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + + strcpy (pl.name,genericpage.objinfo_struct.name); + pl.pagetype=PAGETYPE_GENERIC; + + /*if (Network_up && Stand_alone==0) + { + int locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + + ok=1; + bool need_to_load_page = true; + + if (Loading_addon_table!=-1) + { + AddOnTablefile *addon; + int tidx; + + // see if we really need to load this page + //check to see if we already have loaded this page (because it was + //a dependancy of another) + addon = &AddOnDataTables[Loading_addon_table]; + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_GENERIC && + !stricmp(addon->Addon_tracklocks[tidx].name,genericpage.objinfo_struct.name) ) + { + // found it!! + mprintf((0,"GenericPage: %s previously loaded\n",genericpage.objinfo_struct.name)); + need_to_load_page = false; + break; + } + } + } + + if(need_to_load_page) + { + mng_FreePagetypePrimitives (PAGETYPE_GENERIC,genericpage.objinfo_struct.name,0); + mng_AssignGenericPageToObjInfo (&genericpage,i); + + // For addon data + if (Loading_addon_table!=-1) + { + // this is an overlay of some sort..see which we are overlaying + int overlay = 1; + int addidx,tidx; + bool found = false; + for(addidx = Num_addon_tables-1; addidx>=0; addidx--) + { + if(addidx==Loading_addon_table) + continue; + AddOnTablefile *addon = &AddOnDataTables[addidx]; + + // look for the page in this table file + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_GENERIC && + !stricmp(addon->Addon_tracklocks[tidx].name,genericpage.objinfo_struct.name) ) + { + // found it!! + found = true; + overlay = addidx+2; + break; + } + } + + if(found) + break; + } + + mng_PushAddonPage (PAGETYPE_GENERIC,genericpage.objinfo_struct.name,overlay); + } + } + } + else + { + // This is a local generic that has never been checked in + if ((i=mng_SetAndLoadGeneric (&genericpage))<0) + ok=0; + else ok=1; + + // For addon data + if (ok && Loading_addon_table!=-1) + mng_PushAddonPage (PAGETYPE_GENERIC,genericpage.objinfo_struct.name,0); + } + + ASSERT (ok==1); + + if (Loading_addon_table==-1) + mng_AllocTrackLock (genericpage.objinfo_struct.name,PAGETYPE_GENERIC); + } + else + + mprintf ((0,"Could not load genericpage named %s!\n",genericpage.objinfo_struct.name)); + +} + +// First searches through the object index to see if the object is already +// loaded. If not, searches in the table file and loads it. +// Returns index of object found, -1 if not +int mng_GetGuaranteedGenericPage (char *name,CFILE *infile) +{ + int i; + mngs_generic_page page; + + // See if its in memory + i = FindObjectIDName(name); + if (i!=-1) + return i; + + // Not in memory. Load it from the table file. Start searching from the + // current spot in the open table file + int ret = mng_FindSpecificGenericPage(name,&page,infile?infile->position:0); + + if (!ret) + return -1; + + // We've found it in the table file, now load it. + ret = mng_SetAndLoadGeneric(&page); + ASSERT (ret>=0); + + if(Loading_addon_table!=-1) + { + // we're loading addon table pages, this will not overlay anything + mng_PushAddonPage (PAGETYPE_GENERIC,page.objinfo_struct.name,0); + } + + return ret; +} + + + + + diff --git a/manage/genericpage.h b/manage/genericpage.h new file mode 100644 index 000000000..dd16378d4 --- /dev/null +++ b/manage/genericpage.h @@ -0,0 +1,72 @@ +#ifndef GENERICPAGE_H +#define GENERICPAGE_H + +#include "manage.h" +#include "CFILE.H" +#include "pstypes.h" +#include "objinfo.h" +#include "robotfirestruct.h" + +typedef struct +{ + object_info objinfo_struct; + anim_elem anim[NUM_MOVEMENT_CLASSES]; + otype_wb_info static_wb[MAX_WBS_PER_OBJ]; + t_ai_info ai_info; + char image_name[PAGENAME_LEN]; + char med_image_name[PAGENAME_LEN]; + char lo_image_name[PAGENAME_LEN]; + char sound_name[MAX_OBJ_SOUNDS][PAGENAME_LEN]; + char ai_sound_name[MAX_AI_SOUNDS][PAGENAME_LEN]; + char weapon_name[MAX_WBS_PER_OBJ][MAX_WB_GUNPOINTS][PAGENAME_LEN]; + char fire_sound_name[MAX_WBS_PER_OBJ][MAX_WB_FIRING_MASKS][PAGENAME_LEN]; + char anim_sound_name[NUM_MOVEMENT_CLASSES][NUM_ANIMS_PER_CLASS][PAGENAME_LEN]; + char dspew_name[MAX_DSPEW_TYPES][PAGENAME_LEN]; +} mngs_generic_page; + +// Generic page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a generic_page struct, writes that genericpage out +void mng_WriteGenericPage (CFILE *outfile,mngs_generic_page *genericpage); + +// Reads a generic page from an open file. Returns 0 on error. +int mng_ReadGenericPage (CFILE *infile,mngs_generic_page *genericpage); + +// Given an open file pointer and a generic_page struct, writes that genericpage out +void mng_WriteNewGenericPage (CFILE *outfile,mngs_generic_page *genericpage); + +// Reads a generic page from an open file. Returns 0 on error. +int mng_ReadNewGenericPage (CFILE *infile,mngs_generic_page *genericpage); + + +// Reads in the genericpage named "name" into genericpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificGenericPage (char *name,mngs_generic_page *genericpage,int offset=0); + +// Given a generic page, allocs a generic and calls AssignGenericPageToGeneric to actually +// load model and values. Rturns generic handle on success, -1 if fail +int mng_SetAndLoadGeneric (mngs_generic_page *genericpage,CFILE *infile=NULL); + +// Given a genericpage and a generic handle, attempts to make generic n correspond to +// to the genericpage. +// Returns 1 on success, 0 otherwise +int mng_AssignGenericPageToObjInfo(mngs_generic_page *genericpage,int n,CFILE *infile=NULL); + +// Copies values from a Generic into a generic_page +void mng_AssignObjInfoToGenericPage(int n,mngs_generic_page *genericpage); + + +// Reads in a generic page from the local table file, superseding any generic +// already in RAM with that same name +void mng_LoadLocalGenericPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetGenericPage (CFILE *,bool overlay=false); + +// First searches through the object index to see if the object is already +// loaded. If not, searches in the table file and loads it. +// Returns index of object found, -1 if not +int mng_GetGuaranteedGenericPage (char *name,CFILE *infile); + +#endif \ No newline at end of file diff --git a/manage/manage.cpp b/manage/manage.cpp new file mode 100644 index 000000000..18a12b64c --- /dev/null +++ b/manage/manage.cpp @@ -0,0 +1,3425 @@ +/* + * $Logfile: /DescentIII/Main/manage/manage.cpp $ + * $Revision: 103 $ + * $Date: 10/10/01 11:32a $ + * $Author: Matt $ + * + * Jason should put something here + * + * $Log: /DescentIII/Main/manage/manage.cpp $ + * + * 103 10/10/01 11:32a Matt + * Added system to check for errors when reading in add-on data. + * + * 102 10/08/01 1:50p Matt + * Added a case for gamefile pagetype to avoid int3 + * + * 101 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 100 3/20/00 12:27p Matt + * Merge of Duane's post-1.3 changes. + * Mac pilot directory stuff. + * + * 99 10/26/99 3:30p Jeff + * handle extra.gam addon tablefile + * + * 98 10/20/99 6:27p Jeff + * sped up addon page popping (by saving page offsets) + * + * 97 10/19/99 9:14p Chris + * Fixed a memory free bug + * + * 96 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 95 7/28/99 2:29p Kevin + * Added macintosh DLL extentions (msl) + * + * 94 5/14/99 12:45p Matt + * Removed yet more static data + * + * 93 5/14/99 12:33p Matt + * Fixed another case of too much local data for the Mac. + * + * 92 5/13/99 8:36p Matt + * Made some local variables global to get around the 32K local variable + * limit on the Mac. + * + * 91 5/12/99 3:01p Matt + * Declared one texpage structure statically for all the functions that + * need it, because the Mac limits local data to 32K. + * + * 90 4/30/99 8:53p Matt + * Added a "voice" directory for gamefiles. + * + * 89 4/22/99 3:26p Jason + * added transferring of pagelocks + * + * 88 4/20/99 12:06a Jeff + * added so files to data/scripts search path + * + * 87 4/15/99 5:21p Jason + * sped up table file loading + * + * 86 4/14/99 10:46a Kevin + * Removed OutrageMessageBox from release builds + * + * 85 4/14/99 1:33a Jeff + * fixed case mismatched #includes + * + * 84 4/12/99 3:05p Jason + * changes for 256 textures + * + * 83 3/05/99 10:42a Jason + * more deletion of pagelocls + * + * 82 3/04/99 1:46p Jason + * fixed some manage problems + * + * 81 2/27/99 5:15p Jason + * fixed search path bug + * + * 80 2/17/99 12:11p Jason + * added music directory to searchable list + * + * 79 2/16/99 11:35a Samir + * added art directory. + * + * 78 2/10/99 3:47p Jason + * before doing a backup, makes sure that the tablefile version is the + * same on the net and on the local machine + * + * 77 1/29/99 6:29p Jason + * first pass at adding bumpmaps + * + * 76 1/21/99 11:16p Jeff + * pulled out some structs and defines from header files and moved them + * into seperate header files so that multiplayer dlls don't require major + * game headers, just those new headers. Side effect is a shorter build + * time. Also cleaned up some header file #includes that weren't needed. + * This affected polymodel.h, object.h, player.h, vecmat.h, room.h, + * manage.h and multi.h + * + * 75 1/13/99 2:49p Jeff + * added .msg to the search path for data\scripts + * + * 74 1/13/99 7:08a Jeff + * put some #ifdef's around some window's specific code (really only used + * in the editor, but EDITOR is never defined when building manage) so it + * builds in linux + * + * 73 12/30/98 6:52p Matt + * Fixed compile warnings + * + * 72 12/29/98 4:30p Jason + * added add-on data functionality + * + * 71 12/13/98 7:51p Jeff + * only check the script directory for cpp,dll and def files + * + * 70 12/11/98 5:50p Jeff + * implemented and added changes regarding Level&Scripting manage system + * and compiler interface + * + * 69 11/28/98 2:19p Jason + * fixed stupid filecopy bug + * + * 68 11/18/98 11:02a Jason + * temp fix for table problems + * + * 67 11/16/98 3:49p Jason + * changes for manage system + * + * 66 11/16/98 2:43p Jason + * better file checking for old files + * + * 65 11/13/98 12:30p Jason + * fixed reordered pages bug + * + * 64 11/13/98 12:30p Jason + * changes for weapons + * + * 63 11/06/98 6:00p Josh + * fixed dumb bug + * + * 62 11/06/98 5:28p Josh + * FROM JASON:upped tracklock limit + * + * 61 11/06/98 12:35p Jason + * more speedups for manage system + * + * 60 11/05/98 7:55p Jason + * changes for new manage system + * + * 59 11/04/98 11:02a Jason + * added levels and briefing directories to new "old files" update method + * + * 58 11/02/98 6:35p Jason + * changes for filter + * + * 57 11/02/98 6:02p Jason + * made yes network updates much faster + * + * 56 10/15/98 8:48a Matt + * Changed some errors to use Error() instead of OutrageMessageBox() + * + * 55 10/14/98 5:15p Jason + * added version checking to the table file + * + * 54 10/12/98 11:38p Jeff + * wrapped all the Object_info[].description whenever freed...trying to + * find an obscure bug. Added icon_name to manage page of Generic + * + * 53 10/12/98 10:31a Jason + * don't seach data directories if release + * + * 52 10/09/98 4:39p Jason + * fixed local table file message + * + * 51 10/09/98 2:27p Jason + * reorganized table file system + * + * 50 10/09/98 2:40a Jason + * fixed table file issues with demo + * + * 49 10/08/98 10:03p Jason + * more filtered table file stuff + * + * 48 10/08/98 7:05p Jason + * added file filter support + * + * 47 9/28/98 6:53p Kevin + * localized some multiplayer menus + * + * 46 9/25/98 4:37p Jason + * fixed dedicated server printing out progress messages + * + * 45 9/25/98 2:53p Jason + * added progress bar + * + * 44 9/25/98 12:24p Samir + * fixed bugs for release version. + * + * 43 9/24/98 6:22p Jason + * fixed RELEASE version asking to update network files + * + * 42 9/18/98 3:58p Jason + * change weapon reordering to do countermeasure weapons after generics + * + * 41 9/15/98 4:31p Jason + * added more functionality for the dedicated server + * + * 40 9/14/98 6:28p Jason + * first pass at getting dedicated server working + * + * 39 8/25/98 3:42p Jason + * fixed generic object problems + * + * 38 8/25/98 3:25p Jason + * turned off fast load trick + * + * 37 8/17/98 4:00p Jason + * Added mprintf + * + * 36 8/15/98 5:17p Matt + * Added new Base_directory variable. Got rid of D3_LOCAL check and + * 'local directory' registry variable. + * + * 35 8/13/98 6:34p Jason + * made table file loading much faster + * + * 34 8/10/98 1:49p Samir + * added music directory. + * + * 33 8/03/98 6:44p Jason + * set custom graphics in the search path + * + * 32 7/27/98 6:25p Jeff + * added creation of custom directories + * + * 31 6/23/98 2:43p Matt + * Changed calls to OutrageMessageBox() & Debug_MessageBox() to deal with + * int return value (instead of bool). + * + * 30 6/12/98 1:06p Jason + * added smart loading from local table file + * + * 29 5/04/98 5:00p Keneta + * FROM JASON:Fixed copyfile bug + * + * 28 5/04/98 4:42p Jason + * even better error checking + * + * 26 5/04/98 4:24p Jason + * upped MAX_TRIES + * + * 25 5/04/98 4:18p Jason + * added assert to prevent table file problems + * + * 24 3/31/98 3:49p Jason + * added memory lib + * + * 23 3/19/98 3:51p Samir + * added misc data directory. + * + * 22 2/23/98 2:00p Jason + * Pop up a message box when table file couldn't be opened + * + * 21 2/06/98 12:15p Jason + * upped max times program will try to delete the table file before + * bailing + * + * 20 2/04/98 11:47a Jason + * added dynamic description field to generic pages + * + * 19 1/26/98 11:32a Jason + * upped the number of times the system will try to delete a table file + * + * 18 1/22/98 2:49p Samir + * Added D3 Local Dir to the search path. + * + * 17 1/15/98 6:22p Jason + * added safety checks so the network won't copy over a primitive you have + * held locally + * + * 16 1/15/98 4:54p Mark + * FROM JASON:Do switcheroo a few times before giving up + * + * 15 12/22/97 3:50p Chris + * + * 14 11/17/97 4:16p Jason + * added briefings directory + * + * 13 9/09/97 4:07p Matt + * Added mprintf() + * + * 12 9/04/97 2:53p Samir + * Added gamefile and generic page strings to PageNames array. + * + * 11 8/12/97 12:47p Matt + * Only copy pagefile from net if different from local copy. + * When loading pages, print different char for each type + * Show how long it took to load the pagefile + * + * 10 8/11/97 1:54p Matt + * Ripped out robot & powerup pages, and added generic page + * + * 9 8/08/97 5:17p Jason + * made it so that when you update from the network it doesn't halt other + * users + * + * 8 8/08/97 3:44p Jason + * added code to support new generic page + * + * 7 8/08/97 1:57p Matt + * Took out error message now handled by mng_MakeLocker() + * + * 6 7/29/97 12:07p Jason + * added gamefile page for auto retrieval of non-standard page types + * + * 50 6/27/97 3:11p Jason + * added room directories + * + * + * 49 6/11/97 1:07p Samir + * The removal of gameos and replaced with oeApplication, oeDatabase + * + * 48 6/10/97 5:08p Jason + * added reorderpages menu item + * + * 47 6/05/97 2:52p Jason + * added megacell functions + * + * 46 5/30/97 11:41a Jason + * made a better error message if someone already has the table file + * locked upon startup + * + * 45 5/22/97 3:08p Jason + * added the ReorderPage function + * + * 44 5/16/97 3:53p Jason + * added filepage dialog + * + * 43 5/15/97 5:56 PM Jeremy + * made initlocaltable files check if the file exists by using cfexist + * rather than trying to open the file and checking the error code + * + * 42 5/14/97 6:38p Jason + * fixed a plethora of potential problems by locking the table file when + * someone is starting up. + * + * 41 5/14/97 6:44 PM Jeremy + * fixed a bug where local dir backup directory was not being set + * correctly in init local table files + * + * 40 5/13/97 3:41p Jason + * made all manage code work with the new device independant database + * + * 39 5/08/97 12:41p Jason + * made manage system work with device dependant path names + * + * 38 4/29/97 5:07p Samir + * Added levels directory to search path + * + * 37 4/25/97 6:16p Jason + * added switcheroo function + * + * 36 4/01/97 2:13p Jason + * changes for sound page functionality + * + * 35 3/31/97 4:35p Jason + * added player ship and weapon pages + * + * 34 3/25/97 3:10p Jason + * added robots and robot page functionality + * + * 33 3/17/97 4:27p Jason + * added sounds directory to path list + * + * 32 3/14/97 7:18p Matt + * Added missing include + * + * 31 3/14/97 6:13 PM Jeremy + * changed calls to windows "MessageBox" to OutrageMessageBox, changed + * call of GetUserName to os_database->get_user_name, #included descent.h + * in order to refer to the OS_database object, unincluded and + * + * + * 30 3/13/97 7:39p Matt + * Changed code to use getenv() (ANSI-standard) instead of + * GetEnvironmentVariable() + * + * 29 3/13/97 12:34p Matt + * Changed code to not leave directory changed after checking for presence + * of a directory. + * + * 28 3/13/97 11:37a Samir + * Changed os file functions to ddio file functions + * + * 27 3/10/97 2:23p Jason + * added auto creation of models directory + * + * 26 3/07/97 1:02p Jason + * Now uses Samir's OS specific directory functions + * + * 25 3/05/97 3:10p Jason + * added more door functionality + * + * 24 3/05/97 12:16p Jason + * added code to support our new 3d doors + * + * 23 3/03/97 6:21p Matt + * Changed cfile functions to use D3 naming convention + * + * $NoKeywords: $ + */ +#include +#include +#include +#include +#if defined(WIN32) +#include +#elif defined(__LINUX__) +#include "linux/linux_fix.h" +#endif +#include "descent.h" +#include "manage.h" +#include "pserror.h" +#include "gametexture.h" +#include "texpage.h" +#include "doorpage.h" +#include "soundpage.h" +#include "megapage.h" +#include "shippage.h" +#include "weaponpage.h" +#include "gamefilepage.h" +#include "mono.h" +#include "object.h" +#include "ddio.h" +#include "CFILE.H" +#include "appdatabase.h" +#include "genericpage.h" +#include "mem.h" +#include "dedicated_server.h" +#include "AppConsole.h" +#include "init.h" +#include "stringtable.h" +#include "args.h" +#include "vclip.h" +#include "polymodel.h" +int Old_table_method=0; +void mng_WriteNewUnknownPage (CFILE *outfile); +// This is for levels +char LocalLevelsDir[TABLE_NAME_LEN]; +#ifdef MACINTOSH +char LocalPilotsDir[TABLE_NAME_LEN]; +#endif +// This is for pages +char TableLockFilename[TABLE_NAME_LEN],TableFilename[TABLE_NAME_LEN]; +char TempTableLockFilename[TABLE_NAME_LEN],TempTableFilename[TABLE_NAME_LEN]; +char LocalTableFilename[TABLE_NAME_LEN],LocalTempTableFilename[TABLE_NAME_LEN]; +char BackupTableFilename[TABLE_NAME_LEN],BackupLockFilename[TABLE_NAME_LEN]; +char ManageGraphicsDir[TABLE_NAME_LEN],LocalManageGraphicsDir[TABLE_NAME_LEN]; +char LocalModelsDir[TABLE_NAME_LEN],NetModelsDir[TABLE_NAME_LEN]; +char LocalSoundsDir[TABLE_NAME_LEN],NetSoundsDir[TABLE_NAME_LEN]; +char LocalRoomsDir[TABLE_NAME_LEN],NetRoomsDir[TABLE_NAME_LEN]; +char LocalBriefingDir[TABLE_NAME_LEN],NetBriefingDir[TABLE_NAME_LEN]; +char LocalScriptDir[TABLE_NAME_LEN],NetScriptDir[TABLE_NAME_LEN]; +char LocalMiscDir[TABLE_NAME_LEN],NetMiscDir[TABLE_NAME_LEN]; +char LocalArtDir[TABLE_NAME_LEN],NetArtDir[TABLE_NAME_LEN]; +char LocalMusicDir[TABLE_NAME_LEN],NetMusicDir[TABLE_NAME_LEN]; +char LocalVoiceDir[TABLE_NAME_LEN],NetVoiceDir[TABLE_NAME_LEN]; +char NetTableDir[TABLE_NAME_LEN],LocalTableDir[TABLE_NAME_LEN]; +char LocalD3Dir[TABLE_NAME_LEN],NetD3Dir[TABLE_NAME_LEN]; +char LocalCustomGraphicsDir[TABLE_NAME_LEN]; +char LocalCustomSoundsDir[TABLE_NAME_LEN]; +char LockerFile [TABLE_NAME_LEN]; +char VersionFile [TABLE_NAME_LEN]; +char TableUser[TABLE_NAME_LEN]; +char ErrorString[INFO_STRING_LEN],InfoString[INFO_STRING_LEN]; +mngs_track_lock GlobalTrackLocks[MAX_TRACKLOCKS]; +bool Use_old_update_method=false; +char *TablefileNameOverride = NULL; +// Only valid when first starting the editor +#define MAX_LOCKLIST_ELEMENTS 1000 +mngs_Pagelock *LockList; +int Num_locklist,Starting_editor=0,Loading_locals=0,Fast_load_trick=0; +#define PRIMTYPE_OOF 0 +#define PRIMTYPE_OGF 1 +#define PRIMTYPE_WAV 2 +#define PRIMTYPE_OAF 3 +#define PRIMTYPE_FILE 4 +#if defined(WIN32) +FILETIME TableTimeThreshold; +// Builds a list of old files so we know which ones to update +// Searches through all our netdirectories for old files +void BuildOldFileList (FILETIME threshold); +#endif +typedef struct +{ + ubyte type; + char name[PAGENAME_LEN]; +} old_file; +#define MAX_OLDFILE_ELEMENTS 10000 +int Num_old_files=0; +old_file *OldFiles; +char *PageNames[]={"Unknown","Texture","Weapon","Robot","Powerup","Door","Player ship","Sound","Megacell", "Files", "Generic objects"}; +#ifndef RELEASE + int Network_up=1; + int Stand_alone=0; +#else + int Network_up=0; + int Stand_alone=1; +#endif +void mng_BackupTableFile(); +// returns 1 if network is up, 0 if down +int mng_IsNetworkUp() +{ + char dir[100]; + int ret; + if (Stand_alone) + return 0; + + char net_dir[255]={0}; + int dirlen=255; + Database->read("net directory", net_dir, &dirlen); + if (net_dir[0]==0) + return 0; + ddio_MakePath (dir,net_dir,"data",NULL); + + ret=ddio_CreateDir (dir); + if (!ret) + { + char old_dir[100]; + ddio_GetWorkingDir(old_dir,100); + if (!ddio_SetWorkingDir(dir)) + return 0; // network down + else { + ddio_SetWorkingDir(old_dir); //restore directory + return 1; // directory is already there + } + } + + return 1; +} +void ReorderPages (int); +//#define JASONS_REORDERING +void Read256TextureNames (); +// Sets up our table files, get their filenames, etc. +// Returns 1 on success, zero on error +int mng_InitTableFiles() +{ + ulong size=TABLE_NAME_LEN; + int answer; + Database->get_user_name(TableUser, &size); + if (FindArg("-filter")) + Use_old_update_method=true; + //Read256TextureNames (); + + if (FindArg("-oldmethod")) + Use_old_update_method=true; + if (mng_IsNetworkUp()) + { +#ifndef RELEASE + answer=OutrageMessageBox (MBOX_YESNO, "Do you wish to update your data files from the network?\n(If NO is selected then you will have to restart to use networking functions)"); +#else + answer = IDNO; +#endif + if (answer == IDNO) + Network_up=0; + else + { + Network_up=1; + #ifndef RELEASE + #if defined(WIN32) + if (cfexist("c:\\edload")) + Use_old_update_method=true; + else + { + CFILE *fp=cfopen("c:\\edload","wt"); + cfclose (fp); + } + #endif + #endif + } + } + else + { + mprintf ((0,"Network is down...\n")); + Network_up=0; + } + + if (Network_up==0) + { + mng_InitLocalTables(); + mng_InitLocalDirectories(); + mng_CheckToCreateLocalTables(); + mng_InitTrackLocks(); + } + else + { + // Do locals + mng_InitLocalTables (); + mng_InitLocalDirectories(); + mng_CheckToCreateLocalTables(); + + // Do network + mng_InitNetTables(); + mng_InitNetDirectories (); + mng_CheckToCreateNetTables(); + mng_BackupTableFile(); + mng_InitPagelocks (); + mng_InitTrackLocks(); + #ifdef JASONS_REORDERING + ReorderPages(0); + return 0; + #endif + } + return 1; +} +// Loads our tables +int mng_LoadTableFiles (int show_progress) +{ + if (Network_up) + { + LockList=(mngs_Pagelock *)mem_malloc (MAX_LOCKLIST_ELEMENTS*sizeof(mngs_Pagelock)); + Num_locklist=mng_GetListOfLocks (LockList,MAX_LOCKLIST_ELEMENTS,TableUser); + OldFiles=(old_file *)mem_malloc (MAX_OLDFILE_ELEMENTS*sizeof(old_file)); + Num_old_files=0; + ASSERT (OldFiles); + #if defined (WIN32) + if (TableTimeThreshold.dwHighDateTime!=-1) + BuildOldFileList (TableTimeThreshold); + #endif + + Starting_editor=1; + } + int ret1,ret2; + if (Fast_load_trick && !FindArg ("-filter")) + Network_up=0; + + ret1=mng_LoadNetPages(show_progress); + if (Fast_load_trick) + { + Network_up=1; + Fast_load_trick=0; + } + ret2=mng_LoadLocalPages(); + if (Network_up) + { + Starting_editor=0; + Num_locklist=0; + Num_old_files=0; + mem_free (OldFiles); + mem_free (LockList); + #ifndef RELEASE + #if defined(WIN32) + remove ("c:\\edload"); + #endif + #endif + } + RemapEverything(); + + + if (!ret1 || !ret2) + return 0; + return 1; +} +// This is for initting tables on STAND_ALONE, if the network is down, or if +// the user doesn't want network support +int mng_InitLocalTables () +{ + //Set the local table directory from the base directory + strcpy(LocalD3Dir,Base_directory); + mprintf ((1,"Local dir:%s\n",LocalD3Dir)); + + // Make the CFILE system first look at our local directories. If the goods aren't + // found there, try out on the network + ddio_MakePath (LocalTableDir,LocalD3Dir,"data","tables",NULL); + ddio_MakePath (LocalManageGraphicsDir,LocalD3Dir,"data","graphics",NULL); + ddio_MakePath (LocalModelsDir,LocalD3Dir,"data","models",NULL); + ddio_MakePath (LocalSoundsDir,LocalD3Dir,"data","sounds",NULL); + ddio_MakePath (LocalCustomSoundsDir,LocalD3Dir,"custom","sounds",NULL); + ddio_MakePath (LocalCustomGraphicsDir,LocalD3Dir,"custom","graphics",NULL); + ddio_MakePath (LocalRoomsDir,LocalD3Dir,"data","rooms",NULL); + ddio_MakePath (LocalBriefingDir,LocalD3Dir,"data","briefings",NULL); + ddio_MakePath (LocalScriptDir,LocalD3Dir,"data","scripts",NULL); + ddio_MakePath (LocalMiscDir,LocalD3Dir,"data","misc",NULL); + ddio_MakePath (LocalArtDir, LocalD3Dir,"data", "art", NULL); + ddio_MakePath (LocalMusicDir, LocalD3Dir, "data", "music", NULL); + ddio_MakePath (LocalVoiceDir, LocalD3Dir, "data", "voice", NULL); + ddio_MakePath (LocalLevelsDir,LocalD3Dir,"data","levels",NULL); + cf_SetSearchPath (LocalD3Dir, NULL); +#ifdef MACINTOSH + ddio_MakePath (LocalPilotsDir,LocalD3Dir,"pilots",NULL); + cf_SetSearchPath (LocalPilotsDir, "plt", NULL); +#endif + #ifndef RELEASE + cf_SetSearchPath (LocalLevelsDir, NULL); + cf_SetSearchPath (LocalTableDir,NULL); // Local table directory + + cf_SetSearchPath (LocalManageGraphicsDir,NULL); + cf_SetSearchPath (LocalModelsDir,NULL); + cf_SetSearchPath (LocalSoundsDir,NULL); + cf_SetSearchPath (LocalRoomsDir,NULL); + cf_SetSearchPath (LocalBriefingDir,NULL); + cf_SetSearchPath (LocalScriptDir,"cpp","dll","def","msg","so","msl","dylib",NULL); + cf_SetSearchPath (LocalMiscDir, NULL); + cf_SetSearchPath (LocalArtDir, NULL); + cf_SetSearchPath (LocalMusicDir, NULL); + cf_SetSearchPath (LocalVoiceDir, NULL); + #endif + + if (Network_up) + { + ddio_MakePath (LocalTableFilename,LocalTableDir,LOCAL_TABLE,NULL); + ddio_MakePath (LocalTempTableFilename,LocalTableDir,TEMP_LOCAL_TABLE,NULL); + } + else + { + strcpy (LocalTableFilename,LOCAL_TABLE); + strcpy (LocalTempTableFilename,TEMP_LOCAL_TABLE); + } + + return 1; +} +int mng_InitNetTables () +{ + char dir[255]; + int dirlen=255; + Database->read("net directory", dir, &dirlen); + if (dir[0]==0) + Error ("D3_DIR environment variable not set."); + + strcpy (NetD3Dir,dir); + mprintf ((1,"Net dir:%s\n",NetD3Dir)); + ddio_MakePath (NetModelsDir,NetD3Dir,"data","models",NULL); + ddio_MakePath (NetSoundsDir,NetD3Dir,"data","sounds",NULL); + ddio_MakePath (NetRoomsDir,NetD3Dir,"data","rooms",NULL); + ddio_MakePath (NetBriefingDir,NetD3Dir,"data","briefings",NULL); + ddio_MakePath (NetScriptDir,NetD3Dir,"data","scripts",NULL); + ddio_MakePath (NetMiscDir,NetD3Dir,"data","misc",NULL); + ddio_MakePath (ManageGraphicsDir,NetD3Dir,"data","graphics",NULL); + ddio_MakePath (NetTableDir,NetD3Dir,"data","tables",NULL); + ddio_MakePath (NetArtDir,NetD3Dir,"data", "art", NULL); + ddio_MakePath (NetMusicDir, NetD3Dir, "data", "music", NULL); + ddio_MakePath (NetVoiceDir, NetD3Dir, "data", "voice", NULL); + ddio_MakePath (TableLockFilename,NetTableDir,"table.lok",NULL); + ddio_MakePath (BackupLockFilename,NetTableDir,"tablelok.bak",NULL); + ddio_MakePath (BackupTableFilename,NetTableDir,"table.bak",NULL); + ddio_MakePath (TableFilename,NetTableDir,NET_TABLE,NULL); + ddio_MakePath (TempTableLockFilename,NetTableDir,"lock.tmp",NULL); + ddio_MakePath (TempTableFilename,NetTableDir,TEMP_NET_TABLE,NULL); + ddio_MakePath (LockerFile,NetTableDir,"locker",NULL); + ddio_MakePath (VersionFile,NetTableDir,"TableVersion",NULL); + + cf_SetSearchPath (ManageGraphicsDir,NULL); + cf_SetSearchPath (NetModelsDir,NULL); + cf_SetSearchPath (NetSoundsDir,NULL); + cf_SetSearchPath (NetRoomsDir,NULL); + cf_SetSearchPath (NetMiscDir,NULL); + cf_SetSearchPath (NetMusicDir, NULL); + cf_SetSearchPath (NetVoiceDir, NULL); + return 1; +} +void mng_CheckToCreateNetTables() +{ + CFILE *infile,*outfile; + + ASSERT (Stand_alone!=1); + + infile=(CFILE *)cfopen (TableFilename,"rb"); + if (infile==NULL) + { + if (errno==ENOENT) + { + outfile=(CFILE *)cfopen (TableFilename,"wb"); + if (!outfile) + { + mprintf ((0,"Error creating table file! The network must be down...\n")); + Network_up=0; + } + else + { + mng_WriteNewUnknownPage (outfile); + cfclose (outfile); + } + } + else + { + mprintf ((0,"Error creating table file! The network must be down...\n")); + Network_up=0; + } + } + + if (infile) + cfclose (infile); +} +// Checks to see if there is a table file...if not, create one with a dummy page +void mng_CheckToCreateLocalTables() +{ + CFILE *outfile; + + if (!Network_up) + { + strcpy (TableFilename,NET_TABLE); + mprintf((0, "table filename = %s\n", TableFilename)); + return; + } + + if (!cfexist(LocalTableFilename)) + { + outfile=(CFILE *)cfopen (LocalTableFilename,"wb"); + if (!outfile) + { + Error("Error creating local table file!"); + return; + } + else + { + mng_WriteNewUnknownPage (outfile); + cfclose (outfile); + } + } +} +// Creates directories if needed +void mng_InitLocalDirectories () +{ + char dir[255]; + ddio_MakePath (dir,LocalD3Dir,"custom",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"custom","graphics",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"custom","sounds",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"custom","cache",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"custom","settings",NULL); + ddio_CreateDir(dir); +#ifdef MACINTOSH + ddio_MakePath (dir,LocalD3Dir,"pilots",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"demo",NULL); + ddio_CreateDir(dir); +#endif + cf_SetSearchPath (LocalCustomGraphicsDir,NULL); + cf_SetSearchPath (LocalCustomSoundsDir,NULL); + + if (Network_up) + { + ddio_MakePath (dir,LocalD3Dir,"data",NULL); + ddio_CreateDir(dir); + + ddio_MakePath (dir,LocalD3Dir,"data","tables",NULL); + ddio_CreateDir(dir); + + ddio_MakePath (dir,LocalD3Dir,"data","graphics",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","sounds",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","rooms",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","levels",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","models",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","briefings",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","scripts",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","misc",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","art",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","music",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,LocalD3Dir,"data","voice",NULL); + ddio_CreateDir(dir); + } +} +void mng_InitNetDirectories () +{ + char dir[255]; + + if (Stand_alone) + return; + ddio_MakePath (dir,NetD3Dir,"data",NULL); + ddio_CreateDir(dir); + + ddio_MakePath (dir,NetD3Dir,"data","tables",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","graphics",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","sounds",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","rooms",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","levels",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","models",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","briefings",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","scripts",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","misc",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","art",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","music",NULL); + ddio_CreateDir(dir); + ddio_MakePath (dir,NetD3Dir,"data","voice",NULL); + ddio_CreateDir(dir); +} +extern int TableVersionCurrent(); +#if !defined(WIN32) +void mng_BackupTableFile(){} +#else +void mng_BackupTableFile () +{ + char str[200]; + Fast_load_trick=0; + if (!TableVersionCurrent()) + { + Error ("You must do a source update and recompile. The data on the network is newer that your sourcecode."); + return; + } + + ddio_MakePath (str,LocalTableDir,NET_TABLE,NULL); + if (!cfexist(str)) + { + TableTimeThreshold.dwHighDateTime=0; + TableTimeThreshold.dwLowDateTime=0; + } + else + { + WIN32_FIND_DATA filedata; + HANDLE filehandle=FindFirstFile (str,&filedata); + if (filehandle==INVALID_HANDLE_VALUE) + { + Error ("Couldn't open net table file for some reason!"); + return; + } + TableTimeThreshold=filedata.ftLastWriteTime; + FindClose (filehandle); + } + + if (!cfexist(str) || cf_Diff(str,TableFilename)) { + mprintf ((0,"Making local copy of table file.\n")); + + if (!cf_CopyFile (str,TableFilename,1)) + Error ("There was an error making a backup copy of the table file.\n"); + ddio_MakePath (str,LocalTableDir,"tablelok.loc",NULL); + if (!cf_CopyFile (str,TableLockFilename,1)) + Error ("There was an error making a backup copy of the locker table file.\n"); + } + else + { + mprintf((0,"Local table file same as network copy.\n")); + TableTimeThreshold.dwHighDateTime=-1; + Fast_load_trick=1; + } +} +#endif +void mng_WriteUnknownPage (CFILE *outfile) +{ + // Function for writing out "undefined" page...useful for placeholding + cf_WriteByte (outfile,PAGETYPE_UNKNOWN); +} +void mng_WriteNewUnknownPage (CFILE *outfile) +{ + // Function for writing out "undefined" page...useful for placeholding + int offset=StartManagePage(outfile,PAGETYPE_UNKNOWN); + EndManagePage (outfile,offset); +} +// Clear out tracklocks +void mng_InitTrackLocks () +{ + for (int i=0;i5000-100) + break; + } + // Display that string + OutrageMessageBox (MBOX_OK, str); +#endif +} +//Declare these here because it's too big to put on the stack on the Mac +static mngs_texture_page texpage; +static mngs_door_page doorpage; +static mngs_generic_page genericpage; +static mngs_sound_page soundpage; +static mngs_megacell_page megacellpage; +static mngs_ship_page shippage; +static mngs_weapon_page weaponpage; +static mngs_gamefile_page gamefilepage; +// IF YOU ADD ANY NEW PAGETYPE YOU MUST CHANGE THE FUNCTIONS LISTED UNDER THIS LINE +// TO DEAL WITH YOUR PAGE TYPE. IF YOU FORGET, YOU CAN CORRUPT THE PAGEFILE!!!!! +//------------------------------------------------------------------------------ +// Given a pagetype, reads it in but discards it. Useful for parsing. +void mng_ReadDummyPage (CFILE *infile,ubyte pagetype) +{ + switch (pagetype) + { + case PAGETYPE_TEXTURE: + mng_ReadNewTexturePage (infile,&texpage); + break; + case PAGETYPE_POWERUP: + case PAGETYPE_ROBOT: + Error("Your local table file is invalid. You must update from the network."); + break; + case PAGETYPE_DOOR: + mng_ReadNewDoorPage (infile,&doorpage); + break; + case PAGETYPE_GENERIC: + mng_ReadNewGenericPage (infile,&genericpage); + if (genericpage.objinfo_struct.description!=NULL){ + mem_free (genericpage.objinfo_struct.description); + genericpage.objinfo_struct.description = NULL; + } + break; + case PAGETYPE_GAMEFILE: + mng_ReadNewGamefilePage (infile,&gamefilepage); + break; + case PAGETYPE_SOUND: + mng_ReadNewSoundPage (infile,&soundpage); + break; + case PAGETYPE_SHIP: + mng_ReadNewShipPage (infile,&shippage); + break; + case PAGETYPE_WEAPON: + mng_ReadNewWeaponPage (infile,&weaponpage); + break; + case PAGETYPE_MEGACELL: + mng_ReadNewMegacellPage (infile,&megacellpage); + break; + case PAGETYPE_UNKNOWN: + break; + default: + Int3(); // unrecognized pagetype + break; + } +} +// Reads a page in that we don't care about, and writes it right back out +// This is useful for replacing a specific page in a file but ignoring others +void mng_ReadWriteDummyPage (CFILE *infile,CFILE *outfile,ubyte pagetype) +{ + switch (pagetype) + { + case PAGETYPE_TEXTURE: + // Read it in, write it out. + mng_ReadNewTexturePage (infile,&texpage); + mng_WriteNewTexturePage (outfile,&texpage); + break; + case PAGETYPE_DOOR: + // Read it in, write it out. + mng_ReadNewDoorPage (infile,&doorpage); + mng_WriteNewDoorPage (outfile,&doorpage); + break; + case PAGETYPE_GENERIC: + // Read it in, write it out. + mng_ReadNewGenericPage (infile,&genericpage); + mng_WriteNewGenericPage (outfile,&genericpage); + if (genericpage.objinfo_struct.description!=NULL) + { + mem_free (genericpage.objinfo_struct.description); + genericpage.objinfo_struct.description = NULL; + } + break; + case PAGETYPE_GAMEFILE: + // Read it in, write it out. + mng_ReadNewGamefilePage (infile,&gamefilepage); + mng_WriteNewGamefilePage (outfile,&gamefilepage); + break; + case PAGETYPE_SOUND: + // Read it in, write it out. + mng_ReadNewSoundPage (infile,&soundpage); + mng_WriteNewSoundPage (outfile,&soundpage); + break; + case PAGETYPE_MEGACELL: + // Read it in, write it out. + mng_ReadNewMegacellPage (infile,&megacellpage); + mng_WriteNewMegacellPage (outfile,&megacellpage); + break; + case PAGETYPE_SHIP: + // Read it in, write it out. + mng_ReadNewShipPage (infile,&shippage); + mng_WriteNewShipPage (outfile,&shippage); + break; + case PAGETYPE_WEAPON: + // Read it in, write it out. + mng_ReadNewWeaponPage (infile,&weaponpage); + mng_WriteNewWeaponPage (outfile,&weaponpage); + break; + case PAGETYPE_UNKNOWN: + mng_WriteNewUnknownPage (outfile); + break; + default: + Int3(); // unrecognized pagetype + break; + } +} +// Renames a page on the network +// This function is called when you rename your object, regardless if you check +// it in +int mng_RenamePage (char *oldname,char *newname,int pagetype) +{ + int l,i; + mngs_Pagelock pl; + char oname[PAGENAME_LEN]; + + mprintf ((0,"Renaming %s to %s...\n",oldname,newname)); + strcpy (oname,oldname); + strcpy (pl.name,oname); + pl.pagetype=pagetype; + // Make sure we own it + l=mng_CheckIfPageOwned (&pl,TableUser); + ASSERT (l==1); + strcpy (pl.name,newname); + strcpy (pl.holder,TableUser); + + // First, change the name of the network pagelock + l=mng_ReplacePagelock (oname,&pl); + ASSERT (l==1); + switch (pagetype) + { + // Find the page type with this name and rename it + case PAGETYPE_TEXTURE: + i=FindTextureName(oname); + ASSERT (i!=-1); + strcpy (GameTextures[i].name,newname); + + l=mng_ReplacePage (oname,GameTextures[i].name,i,PAGETYPE_TEXTURE,0); + ASSERT (l==1); + if (mng_FindTrackLock (oname,PAGETYPE_TEXTURE)!=-1) + mng_ReplacePage (oname,GameTextures[i].name,i,PAGETYPE_TEXTURE,1); + + break; + case PAGETYPE_DOOR: + i=FindDoorName(oname); + ASSERT (i!=-1); + strcpy (Doors[i].name,newname); + l=mng_ReplacePage (oname,Doors[i].name,i,PAGETYPE_DOOR,0); + if (mng_FindTrackLock (oname,PAGETYPE_DOOR)!=-1) + mng_ReplacePage (oname,Doors[i].name,i,PAGETYPE_DOOR,1); + ASSERT (l==1); + break; + case PAGETYPE_GENERIC: + i=FindObjectIDName(oname); + ASSERT (i!=-1); + strcpy (Object_info[i].name,newname); + + l=mng_ReplacePage (oname,Object_info[i].name,i,PAGETYPE_GENERIC,0); + + if (mng_FindTrackLock (oname,PAGETYPE_GENERIC)!=-1) + mng_ReplacePage (oname,Object_info[i].name,i,PAGETYPE_GENERIC,1); + + ASSERT (l==1); + break; + case PAGETYPE_GAMEFILE: + i=FindGamefileName(oname); + ASSERT (i!=-1); + strcpy (Gamefiles[i].name,newname); + + l=mng_ReplacePage (oname,Gamefiles[i].name,i,PAGETYPE_GAMEFILE,0); + if (mng_FindTrackLock (oname,PAGETYPE_GAMEFILE)!=-1) + mng_ReplacePage (oname,Gamefiles[i].name,i,PAGETYPE_GAMEFILE,1); + ASSERT (l==1); + break; + case PAGETYPE_SOUND: + i=FindSoundName(oname); + ASSERT (i!=-1); + strcpy (Sounds[i].name,newname); + l=mng_ReplacePage (oname,Sounds[i].name,i,PAGETYPE_SOUND,0); + if (mng_FindTrackLock (oname,PAGETYPE_SOUND)!=-1) + mng_ReplacePage (oname,Sounds[i].name,i,PAGETYPE_SOUND,1); + ASSERT (l==1); + break; + case PAGETYPE_MEGACELL: + i=FindMegacellName(oname); + ASSERT (i!=-1); + strcpy (Megacells[i].name,newname); + l=mng_ReplacePage (oname,Megacells[i].name,i,PAGETYPE_MEGACELL,0); + + if (mng_FindTrackLock (oname,PAGETYPE_MEGACELL)!=-1) + mng_ReplacePage (oname,Megacells[i].name,i,PAGETYPE_MEGACELL,1); + ASSERT (l==1); + break; + case PAGETYPE_SHIP: + i=FindShipName(oname); + ASSERT (i!=-1); + strcpy (Ships[i].name,newname); + + l=mng_ReplacePage (oname,Ships[i].name,i,PAGETYPE_SHIP,0); + if (mng_FindTrackLock (oname,PAGETYPE_SHIP)!=-1) + mng_ReplacePage (oname,Ships[i].name,i,PAGETYPE_SHIP,1); + + ASSERT (l==1); + break; + case PAGETYPE_WEAPON: + i=FindWeaponName(oname); + ASSERT (i!=-1); + strcpy (Weapons[i].name,newname); + + l=mng_ReplacePage (oname,Weapons[i].name,i,PAGETYPE_WEAPON,0); + if (mng_FindTrackLock (oname,PAGETYPE_WEAPON)!=-1) + mng_ReplacePage (oname,Weapons[i].name,i,PAGETYPE_WEAPON,1); + ASSERT (l==1); + break; + default: + Int3(); // Unknown type, get Jason + break; + } + return 1; +} +#define PROGRESS_PERCENTAGE_THRESHOLD 20 +// This is the function that opens the table files and reads in the individual pages +// If you want your data to be in the game, it must hook into this function +int mng_LoadNetPages (int show_progress) +{ + CFILE *infile; + ubyte pagetype; + char tablename[TABLE_NAME_LEN]; + float start_time; + int n_pages = 0; + int total_bytes; + int current_byte; + float progress; + int int_progress=0; + int len; + + mprintf ((0,"Loading pages...")); + if (Dedicated_server) + show_progress=0; // turn off progress meter for dedicated server + // If the network is up we still want to read from the local table because it + // will allow others to start the game at the same time + if (Network_up) + { + int farg=FindArg("-filter"); + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + { + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + } + infile=cfopen (tablename,"rb"); + } + else + infile=cfopen (TableFilename,"rb"); + if (!infile) + { + mprintf ((0,"Couldn't open table file (%s) to read pages!\n",TableFilename)); + Error("Cannot open table file <%s>",TableFilename); + return 0; + } + if (show_progress) + { + cfseek (infile,0,SEEK_END); + total_bytes=cftell (infile); + cfseek (infile,0,SEEK_SET); + } + start_time = timer_GetTime(); + while (!cfeof(infile)) + { + // Read in a pagetype. If its a page we recognize, load it + // mprintf ((0,".")); + if (show_progress) + { + + current_byte=cftell (infile); + progress=(float)current_byte/(float)total_bytes; + progress*=PROGRESS_PERCENTAGE_THRESHOLD; + int temp_int_progress=progress; + if (temp_int_progress>int_progress) + { + int_progress=temp_int_progress; + InitMessage (TXT_INITDATA,progress/PROGRESS_PERCENTAGE_THRESHOLD); + } + } + pagetype=cf_ReadByte (infile); + if (!Old_table_method) + len=cf_ReadInt (infile); + switch (pagetype) + { + case PAGETYPE_TEXTURE: + mprintf ((0,"T")); + + PrintDedicatedMessage ("T"); + mng_LoadNetTexturePage (infile); + break; + case PAGETYPE_POWERUP: + case PAGETYPE_ROBOT: + Error("Your local table file is invalid. You must update from the network."); + break; + case PAGETYPE_DOOR: + mprintf ((0,"D")); + PrintDedicatedMessage ("D"); + mng_LoadNetDoorPage (infile); + break; + case PAGETYPE_GENERIC: + mprintf ((0,"G")); + PrintDedicatedMessage ("G"); + mng_LoadNetGenericPage (infile); + break; + case PAGETYPE_GAMEFILE: + mprintf ((0,"F")); + PrintDedicatedMessage ("F"); + mng_LoadNetGamefilePage (infile); + break; + case PAGETYPE_SOUND: + mprintf ((0,"S")); + PrintDedicatedMessage ("S"); + mng_LoadNetSoundPage (infile); + break; + case PAGETYPE_SHIP: + mprintf ((0,"P")); + PrintDedicatedMessage ("P"); + mng_LoadNetShipPage (infile); + break; + case PAGETYPE_WEAPON: + mprintf ((0,"W")); + PrintDedicatedMessage ("W"); + mng_LoadNetWeaponPage (infile); + break; + case PAGETYPE_MEGACELL: + mprintf ((0,"M")); + PrintDedicatedMessage ("M"); + mng_LoadNetMegacellPage (infile); + break; + case PAGETYPE_UNKNOWN: + mprintf ((0,"?")); + break; + default: + Int3(); // Unrecognized pagetype, possible corrupt data following + return 0; + break; + } + n_pages++; + } + mprintf((0,"\n%d pages read in %.1f seconds.\n",n_pages,timer_GetTime()-start_time)); + mprintf ((0,"\n")); + PrintDedicatedMessage ((0,"\nPage reading completed.\n")); + + cfclose (infile); + + // attempt to load extra.gam if found + char name_override[256]; + strcpy(name_override,"extra.gam"); + infile=cfopen (name_override,"rb"); + if(!infile) + return 1; + + mprintf((0,"==================================================\n")); + mprintf((0," Loading extra.gam \n")); + mprintf((0,"==================================================\n")); + PrintDedicatedMessage ("\nLoading extra.gam.....\n"); + n_pages = 0; + + TablefileNameOverride = name_override; + + while (!cfeof(infile)) + { + pagetype=cf_ReadByte (infile); + len=cf_ReadInt (infile); + switch (pagetype) + { + case PAGETYPE_TEXTURE: + mprintf ((0,"T")); + PrintDedicatedMessage ("T"); + mng_LoadNetTexturePage (infile,true); + break; + case PAGETYPE_DOOR: + mprintf ((0,"D")); + PrintDedicatedMessage ("D"); + mng_LoadNetDoorPage (infile,true); + break; + case PAGETYPE_GENERIC: + mprintf ((0,"G")); + PrintDedicatedMessage ("G"); + mng_LoadNetGenericPage (infile,true); + break; + case PAGETYPE_GAMEFILE: + mprintf ((0,"F")); + PrintDedicatedMessage ("F"); + mng_LoadNetGamefilePage (infile,true); + break; + case PAGETYPE_SOUND: + mprintf ((0,"S")); + PrintDedicatedMessage ("S"); + mng_LoadNetSoundPage (infile,true); + break; + case PAGETYPE_SHIP: + mprintf ((0,"P")); + PrintDedicatedMessage ("P"); + mng_LoadNetShipPage (infile,true); + break; + case PAGETYPE_WEAPON: + mprintf ((0,"W")); + PrintDedicatedMessage ("W"); + mng_LoadNetWeaponPage (infile,true); + break; + case PAGETYPE_UNKNOWN: + mprintf ((0,"?")); + break; + default: + Int3(); // Unrecognized pagetype, possible corrupt data following + cfclose(infile); + TablefileNameOverride = NULL; + return 0; + break; + } + n_pages++; + } + mprintf((0,"\n%d extra pages read.\n",n_pages)); + TablefileNameOverride = NULL; + cfclose(infile); + return 1; +} +// Loads and allocs all pages found locally +int mng_LoadLocalPages () +{ + CFILE *infile; + ubyte pagetype; + int len; + + mprintf ((0,"Overlaying local pages...")); + infile=cfopen (LocalTableFilename,"rb"); + if (!infile) + { + mprintf ((0,"Couldn't open local table file (%s) to read pages!\n",LocalTableFilename)); + return 1; + } + Loading_locals=1; + while (!cfeof(infile)) + { + // Read in a pagetype. If its a page we recognize, load it + + pagetype=cf_ReadByte (infile); + if (!Old_table_method) + len=cf_ReadInt (infile); + mprintf ((0,".")); + switch (pagetype) + { + case PAGETYPE_TEXTURE: + mng_LoadLocalTexturePage (infile); + break; + case PAGETYPE_POWERUP: + case PAGETYPE_ROBOT: + Error("Your local table file is invalid. You must update from the network."); + break; + case PAGETYPE_DOOR: + mng_LoadLocalDoorPage (infile); + break; + case PAGETYPE_GENERIC: + mng_LoadLocalGenericPage (infile); + break; + case PAGETYPE_GAMEFILE: + mng_LoadLocalGamefilePage (infile); + break; + case PAGETYPE_SOUND: + mng_LoadLocalSoundPage (infile); + break; + case PAGETYPE_SHIP: + mng_LoadLocalShipPage (infile); + break; + case PAGETYPE_WEAPON: + mng_LoadLocalWeaponPage (infile); + break; + case PAGETYPE_MEGACELL: + mng_LoadLocalMegacellPage (infile); + break; + case PAGETYPE_UNKNOWN: + break; + default: + Int3(); // Unrecognized pagetype, possible corrupt data following + return 0; + break; + } + } + mprintf ((0,"\n")); + cfclose (infile); + Loading_locals=0; + return 1; +} +#define MAX_TRIES 10000 +// Removes a file, then renames another file to be the removed file. Get it? +// Returns 1 on success, else 0 on fail +int SwitcherooFiles (char *name,char *tempname) +{ + /*// If we're changing the net table file, make a backup first! + if ((!stricmp (name,TableFilename))) + { + cf_CopyFile (BackupTableFilename,TableFilename); + cf_CopyFile (BackupLockFilename,TableLockFilename); + }*/ + int num_tries=0; + while (!ddio_DeleteFile (name) && num_tries=MAX_TRIES) + { + strcpy (ErrorString,"MANAGE:There was a problem deleting the table file."); + ASSERT (0); // GET JASON IMMEDIATELY + Int3(); + return (0); + } + num_tries=0; + while ((rename (tempname,name)) && num_tries<=MAX_TRIES) + { + Sleep (100); + num_tries++; + } + if (num_tries>=MAX_TRIES) + { + strcpy (ErrorString,"MANAGE:There was a problem renaming the temp file."); + ASSERT (0); // Get JASON IMMEDIATELY + Int3(); + return (0); + } + + return 1; +} + +void mng_TransferPages () +{ + CFILE *infile,*outfile; + int pagetype; + int num_tracklocks=0; + mprintf ((0,"Transferring pages, please wait...\n")); + if (!mng_MakeLocker()) + return; + infile=cfopen (TableFilename,"rb"); + + if (!infile) + { + mprintf ((0,"Couldn't open table file to transfer!\n")); + Int3(); + return; + } + mngs_track_lock *local_tracklocks = (mngs_track_lock *) mem_malloc(sizeof(*local_tracklocks) * 5000); + // Do textures + int done=0; + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt (infile); + switch (pagetype) + { + case PAGETYPE_TEXTURE: + mng_ReadNewTexturePage (infile,&texpage); + strcpy (local_tracklocks[num_tracklocks].name,texpage.tex_struct.name); + local_tracklocks[num_tracklocks].pagetype=PAGETYPE_TEXTURE; + num_tracklocks++; + + break; + case PAGETYPE_SOUND: + mng_ReadNewSoundPage (infile,&soundpage); + strcpy (local_tracklocks[num_tracklocks].name,soundpage.sound_struct.name); + local_tracklocks[num_tracklocks].pagetype=PAGETYPE_SOUND; + num_tracklocks++; + break; + case PAGETYPE_WEAPON: + mng_ReadNewWeaponPage (infile,&weaponpage); + strcpy (local_tracklocks[num_tracklocks].name,weaponpage.weapon_struct.name); + local_tracklocks[num_tracklocks].pagetype=PAGETYPE_WEAPON; + num_tracklocks++; + break; + case PAGETYPE_GENERIC: + mng_ReadNewGenericPage (infile,&genericpage); + strcpy (local_tracklocks[num_tracklocks].name,genericpage.objinfo_struct.name); + local_tracklocks[num_tracklocks].pagetype=PAGETYPE_GENERIC; + num_tracklocks++; + break; + case PAGETYPE_DOOR: + mng_ReadNewDoorPage (infile,&doorpage); + strcpy (local_tracklocks[num_tracklocks].name,doorpage.door_struct.name); + local_tracklocks[num_tracklocks].pagetype=PAGETYPE_DOOR; + num_tracklocks++; + break; + case PAGETYPE_SHIP: + mng_ReadNewShipPage (infile,&shippage); + strcpy (local_tracklocks[num_tracklocks].name,shippage.ship_struct.name); + local_tracklocks[num_tracklocks].pagetype=PAGETYPE_SHIP; + num_tracklocks++; + break; + case PAGETYPE_GAMEFILE: + mng_ReadNewGamefilePage (infile,&gamefilepage); + strcpy (local_tracklocks[num_tracklocks].name,gamefilepage.gamefile_struct.name); + local_tracklocks[num_tracklocks].pagetype=PAGETYPE_GAMEFILE; + num_tracklocks++; + break; + case PAGETYPE_MEGACELL: + Int3();// huh? + break; + default: + break; + } + } + cfclose (infile); + // Now go through and filter out all unused lock files + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (!infile) + { + strcpy (ErrorString,"Couldn't open Table lock file!"); + goto done; + } + outfile=(CFILE *)cfopen (TempTableLockFilename,"wb"); + if (!outfile) + { + cfclose (infile); + strcpy (ErrorString,"Couldn't open temporary table lock file!"); + goto done; + } + done=0; + mngs_Pagelock temp_pl; + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + if (mng_ReadPagelock (infile,&temp_pl)) + { + int found=-1; + for (int i=0;ipagetype) + { + if (!stricmp(LockList[i].name,pl->name)) + return true; + } + } + } + else + { + if ((mng_CheckIfPageOwned(pl,TableUser)) > 0) //DAJ -1FIX + return true; + } + return false; +} +// Given a filename, returns the type of primitive it is +int GetPrimType (char *name) +{ + char ext[10]; + char tname[_MAX_PATH]; + int primtype; + ddio_SplitPath(name,tname,tname,ext); + if (!stricmp ("oof",ext)) + primtype=PRIMTYPE_OOF; + else if (!stricmp ("ogf",ext)) + primtype=PRIMTYPE_OGF; + else if (!stricmp ("oaf",ext)) + primtype=PRIMTYPE_OAF; + else if (!stricmp ("wav",ext)) + primtype=PRIMTYPE_WAV; + else + primtype=PRIMTYPE_FILE; + return primtype; + +} +#if defined(WIN32) +// Builds a list of old files in a path +void BuildOldFilesForDirectory (char *path,FILETIME threshold) +{ + HANDLE filehandle; + WIN32_FIND_DATA filedata; + char newpath[MAX_PATH]; + ddio_MakePath(newpath,path,"*.*",NULL); + filehandle=FindFirstFile (newpath,&filedata); + bool go_ahead=true; + if (filehandle==INVALID_HANDLE_VALUE) + go_ahead=false; + while (go_ahead) + { + bool add_it=false; + + // if this file is newer than the last time we updated, add it to the list + + if (filedata.ftLastWriteTime.dwHighDateTime>threshold.dwHighDateTime) + add_it=true; + if (filedata.ftLastWriteTime.dwHighDateTime==threshold.dwHighDateTime) + { + if (filedata.ftLastWriteTime.dwLowDateTime>threshold.dwLowDateTime) + add_it=true; + } + if (filedata.ftCreationTime.dwHighDateTime>threshold.dwHighDateTime) + add_it=true; + if (filedata.ftCreationTime.dwHighDateTime==threshold.dwHighDateTime) + { + if (filedata.ftCreationTime.dwLowDateTime>threshold.dwLowDateTime) + add_it=true; + } + // Add it to the list! + if (add_it) + { + int primtype=GetPrimType (filedata.cFileName); + OldFiles[Num_old_files].type=primtype; + strcpy (OldFiles[Num_old_files].name,filedata.cFileName); + Num_old_files++; + } + go_ahead = (FindNextFile(filehandle,&filedata) != 0); + } + if (filehandle!=INVALID_HANDLE_VALUE) + FindClose (filehandle); +} +// Builds a list of old files so we know which ones to update +// Searches through all our netdirectories for old files +void BuildOldFileList (FILETIME threshold) +{ + char str[MAX_PATH]; + mprintf ((0,"Building old files list!\n")); + BuildOldFilesForDirectory (NetModelsDir,threshold); + BuildOldFilesForDirectory (NetSoundsDir,threshold); + BuildOldFilesForDirectory (NetMiscDir,threshold); + BuildOldFilesForDirectory (ManageGraphicsDir,threshold); + BuildOldFilesForDirectory (NetArtDir, threshold); + BuildOldFilesForDirectory (NetMusicDir,threshold); + BuildOldFilesForDirectory (NetVoiceDir,threshold); + ddio_MakePath (str,NetD3Dir,"data","levels",NULL); + BuildOldFilesForDirectory (str,threshold); + ddio_MakePath (str,NetD3Dir,"data","briefings",NULL); + BuildOldFilesForDirectory (str,threshold); + ddio_MakePath (str,NetD3Dir,"data","scripts",NULL); + BuildOldFilesForDirectory (str,threshold); + mprintf ((0,"Found %d old files.\n",Num_old_files)); +} +#endif +// Returns true if the passed in primitive is old (ie needs to be updated from the network) +bool IsPrimitiveOld (char *name) +{ + int primtype=GetPrimType (name); + for (int i=0;i0) + cf_WriteBytes (copybuffer,len-4,outfile); + + continue; + } + switch (pagetype) + { + case PAGETYPE_TEXTURE: + { + mng_ReadNewTexturePage (infile,&texpage); + if (!stricmp(srcname,texpage.tex_struct.name)) + { + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewTexturePage (outfile,&texpage); + } + break; + } + case PAGETYPE_SOUND: + { + mng_ReadNewSoundPage (infile,&soundpage); + if (!stricmp(srcname,soundpage.sound_struct.name)) + { + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewSoundPage (outfile,&soundpage); + } + break; + } + case PAGETYPE_WEAPON: + { + mng_ReadNewWeaponPage (infile,&weaponpage); + if (!stricmp(srcname,weaponpage.weapon_struct.name)) + { + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewWeaponPage (outfile,&weaponpage); + } + break; + } + case PAGETYPE_GENERIC: + { + mng_ReadNewGenericPage (infile,&genericpage); + if (!stricmp(srcname,genericpage.objinfo_struct.name)) + { + // This is the page we want to replace, so write the new one out. + if (genericpage.objinfo_struct.description!=NULL) + { + mem_free (genericpage.objinfo_struct.description); + genericpage.objinfo_struct.description = NULL; + } + genericpage.objinfo_struct.icon_name[0] = '\0'; + + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewGenericPage (outfile,&genericpage); + } + break; + } + case PAGETYPE_DOOR: + { + mng_ReadNewDoorPage (infile,&doorpage); + if (!stricmp(srcname,doorpage.door_struct.name)) + { + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewDoorPage (outfile,&doorpage); + } + break; + } + case PAGETYPE_MEGACELL: + { + mng_ReadNewMegacellPage (infile,&megacellpage); + if (!stricmp(srcname,megacellpage.megacell_struct.name)) + { + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewMegacellPage (outfile,&megacellpage); + } + break; + } + case PAGETYPE_SHIP: + { + mng_ReadNewShipPage (infile,&shippage); + if (!stricmp(srcname,shippage.ship_struct.name)) + { + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewShipPage (outfile,&shippage); + } + break; + } + case PAGETYPE_GAMEFILE: + { + mng_ReadNewGamefilePage (infile,&gamefilepage); + if (!stricmp(srcname,gamefilepage.gamefile_struct.name)) + { + mng_AssignAndWritePage (handle,pagetype,outfile); + replaced=1; + } + else + { + mng_WriteNewGamefilePage (outfile,&gamefilepage); + } + break; + } + default: + break; + } + } + + if (!replaced) + { + // This is a new page, so append it to the end of file. + mng_AssignAndWritePage (handle,dest_pagetype,outfile); + } + if (replaced) + mprintf ((0,"Page replaced.\n")); + else + mprintf ((0,"New page added.\n")); + cfclose (infile); + cfclose (outfile); + mem_free (copybuffer); + if (local) + { + if (!SwitcherooFiles (LocalTableFilename,LocalTempTableFilename)) + return 0; + } + else + { + if (!SwitcherooFiles (TableFilename,TempTableFilename)) + return 0; + } + return 1; // successful! +} +// Given a texture name, finds it in the table file and deletes it +// If local is 1, deletes from the local table file +int mng_DeletePage (char *name,int dest_pagetype,int local) +{ + CFILE *infile,*outfile; + ubyte pagetype,replaced=0; + int done=0; + int deleted=0; + + mprintf ((0,"Deleting %s (%s).\n",name,local?"locally":"on network")); + + if (local) + infile=cfopen (LocalTableFilename,"rb"); + else + infile=cfopen (TableFilename,"rb"); + if (!infile) + { + mprintf ((0,"Couldn't open table file to delete page!\n")); + Int3(); + return 0; + } + if (local) + outfile=cfopen (LocalTempTableFilename,"wb"); + else + outfile=cfopen (TempTableFilename,"wb"); + if (!outfile) + { + mprintf ((0,"Couldn't open temp table file to delete page!\n")); + cfclose (infile); + Int3(); + return 0; + } + // Allocate memory for copying + ubyte *copybuffer=(ubyte *)mem_malloc (COPYBUFFER_SIZE); + if (!copybuffer) + { + mprintf ((0,"Couldn't allocate memory to delete page!\n")); + cfclose (infile); + cfclose (outfile); + Int3(); + return 0; + } + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt (infile); + if (pagetype!=dest_pagetype) + { + ASSERT (len0) + cf_WriteBytes (copybuffer,len-4,outfile); + + continue; + } + switch (pagetype) + { + + case PAGETYPE_TEXTURE: + { + mng_ReadNewTexturePage (infile,&texpage); + if (stricmp(name,texpage.tex_struct.name)) + mng_WriteNewTexturePage (outfile,&texpage); + else + deleted=1; // Don't write out the one we want to delete + break; + } + case PAGETYPE_DOOR: + { + mng_ReadNewDoorPage (infile,&doorpage); + if (stricmp(name,doorpage.door_struct.name)) + mng_WriteNewDoorPage (outfile,&doorpage); + else + deleted=1; // Don't write out the one we want to delete + break; + } + case PAGETYPE_GENERIC: + { + mng_ReadNewGenericPage (infile,&genericpage); + if (stricmp(name,genericpage.objinfo_struct.name)) + mng_WriteNewGenericPage (outfile,&genericpage); + else + deleted=1; // Don't write out the one we want to delete + if (genericpage.objinfo_struct.description!=NULL) + { + mem_free (genericpage.objinfo_struct.description); + genericpage.objinfo_struct.description = NULL; + } + genericpage.objinfo_struct.icon_name[0] = '\0'; + break; + } + case PAGETYPE_SOUND: + { + mng_ReadNewSoundPage (infile,&soundpage); + if (stricmp(name,soundpage.sound_struct.name)) + mng_WriteNewSoundPage (outfile,&soundpage); + else + deleted=1; // Don't write out the one we want to delete + break; + } + case PAGETYPE_SHIP: + { + mng_ReadNewShipPage (infile,&shippage); + if (stricmp(name,shippage.ship_struct.name)) + mng_WriteNewShipPage (outfile,&shippage); + else + deleted=1; // Don't write out the one we want to delete + break; + } + case PAGETYPE_WEAPON: + { + mng_ReadNewWeaponPage (infile,&weaponpage); + if (stricmp(name,weaponpage.weapon_struct.name)) + mng_WriteNewWeaponPage (outfile,&weaponpage); + else + deleted=1; // Don't write out the one we want to delete + break; + } + case PAGETYPE_MEGACELL: + { + mng_ReadNewMegacellPage (infile,&megacellpage); + if (stricmp(name,megacellpage.megacell_struct.name)) + mng_WriteNewMegacellPage (outfile,&megacellpage); + else + deleted=1; // Don't write out the one we want to delete + break; + } + case PAGETYPE_GAMEFILE: + { + mng_ReadNewGamefilePage (infile,&gamefilepage); + if (stricmp(name,gamefilepage.gamefile_struct.name)) + mng_WriteNewGamefilePage (outfile,&gamefilepage); + else + deleted=1; // Don't write out the one we want to delete + break; + } + default: + break; + } + } + + if (!local) + { + // It's gotta be there if on the network + ASSERT (deleted==1); + } + else + { + if (!deleted) + { + mprintf ((0,"Not found locally?!\n")); + } + } + cfclose (infile); + cfclose (outfile); + mem_free (copybuffer); + // Now, remove our table file and rename the temp file to be the table file + if (local) + { + if (!SwitcherooFiles (LocalTableFilename,LocalTempTableFilename)) + return 0; + } + else + { + if (!SwitcherooFiles (TableFilename,TempTableFilename)) + return 0; + } + return 1; // successful! + +} +// Reads in a physics chunk from an open file +void mng_ReadPhysicsChunk (physics_info *phys_info,CFILE *infile) +{ + phys_info->mass=cf_ReadFloat (infile); + phys_info->drag=cf_ReadFloat (infile); + phys_info->full_thrust=cf_ReadFloat (infile); + phys_info->flags=cf_ReadInt (infile); + phys_info->rotdrag=cf_ReadFloat (infile); + phys_info->full_rotthrust=cf_ReadFloat (infile); + phys_info->num_bounces=cf_ReadInt (infile); + phys_info->velocity.z=cf_ReadFloat (infile); + phys_info->rotvel.x=cf_ReadFloat (infile); + phys_info->rotvel.y=cf_ReadFloat (infile); + phys_info->rotvel.z=cf_ReadFloat (infile); + phys_info->wiggle_amplitude=cf_ReadFloat (infile); + phys_info->wiggles_per_sec=cf_ReadFloat (infile); + phys_info->coeff_restitution=cf_ReadFloat (infile); + phys_info->hit_die_dot=cf_ReadFloat (infile); + phys_info->max_turnroll_rate=cf_ReadFloat (infile); + phys_info->turnroll_ratio=cf_ReadFloat (infile); + +} +// Writes out a physics chunk to an open file +void mng_WritePhysicsChunk (physics_info *phys_info,CFILE *outfile) +{ + cf_WriteFloat (outfile,phys_info->mass); + cf_WriteFloat (outfile,phys_info->drag); + cf_WriteFloat (outfile,phys_info->full_thrust); + cf_WriteInt (outfile,phys_info->flags); + cf_WriteFloat (outfile,phys_info->rotdrag); + cf_WriteFloat (outfile,phys_info->full_rotthrust); + cf_WriteInt (outfile,phys_info->num_bounces); + cf_WriteFloat (outfile,phys_info->velocity.z); + cf_WriteFloat (outfile,phys_info->rotvel.x); + cf_WriteFloat (outfile,phys_info->rotvel.y); + cf_WriteFloat (outfile,phys_info->rotvel.z); + cf_WriteFloat (outfile,phys_info->wiggle_amplitude); + cf_WriteFloat (outfile,phys_info->wiggles_per_sec); + cf_WriteFloat (outfile,phys_info->coeff_restitution); + cf_WriteFloat (outfile,phys_info->hit_die_dot); + cf_WriteFloat (outfile,phys_info->max_turnroll_rate); + cf_WriteFloat (outfile,phys_info->turnroll_ratio); +} +// Writes out weapon battery info +void mng_WriteWeaponBatteryChunk (otype_wb_info *static_wb,CFILE *outfile) +{ + int j; + cf_WriteFloat(outfile, static_wb->energy_usage); + cf_WriteFloat(outfile, static_wb->ammo_usage); + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + cf_WriteShort(outfile, static_wb->gp_weapon_index[j]); + } + + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + cf_WriteByte(outfile, static_wb->gp_fire_masks[j]); + cf_WriteFloat(outfile, static_wb->gp_fire_wait[j]); + cf_WriteFloat(outfile, static_wb->anim_time[j]); + cf_WriteFloat(outfile, static_wb->anim_start_frame[j]); + cf_WriteFloat(outfile, static_wb->anim_fire_frame[j]); + cf_WriteFloat(outfile, static_wb->anim_end_frame[j]); + } + cf_WriteByte(outfile, static_wb->num_masks); + cf_WriteShort(outfile, static_wb->aiming_gp_index); + cf_WriteByte(outfile, static_wb->aiming_flags); + cf_WriteFloat(outfile, static_wb->aiming_3d_dot); + cf_WriteFloat(outfile, static_wb->aiming_3d_dist); + cf_WriteFloat(outfile, static_wb->aiming_XZ_dot); + cf_WriteShort(outfile, static_wb->flags); + cf_WriteByte(outfile, static_wb->gp_quad_fire_mask); +} +// Reads in weapon battery info +void mng_ReadWeaponBatteryChunk (otype_wb_info *static_wb,CFILE *infile,int version) +{ + int j; + + static_wb->energy_usage=cf_ReadFloat(infile); + static_wb->ammo_usage=cf_ReadFloat(infile); + + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + static_wb->gp_weapon_index[j]=cf_ReadShort(infile); + } + + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + static_wb->gp_fire_masks[j]=cf_ReadByte(infile); + static_wb->gp_fire_wait[j]=cf_ReadFloat(infile); + static_wb->anim_time[j]=cf_ReadFloat (infile); + static_wb->anim_start_frame[j]=cf_ReadFloat (infile); + static_wb->anim_fire_frame[j]=cf_ReadFloat (infile); + static_wb->anim_end_frame[j]=cf_ReadFloat (infile); + } + static_wb->num_masks=cf_ReadByte (infile); + static_wb->aiming_gp_index=cf_ReadShort(infile); + static_wb->aiming_flags=cf_ReadByte(infile); + static_wb->aiming_3d_dot=cf_ReadFloat(infile); + static_wb->aiming_3d_dist=cf_ReadFloat(infile); + static_wb->aiming_XZ_dot=cf_ReadFloat(infile); + if (version>=2) + static_wb->flags=cf_ReadShort(infile); + else + static_wb->flags=cf_ReadByte(infile); + static_wb->gp_quad_fire_mask=cf_ReadByte(infile); +} +// Writes a lighting chunk in from an open file +void mng_WriteLightingChunk ( light_info *lighting_info,CFILE *outfile) +{ + cf_WriteFloat (outfile,lighting_info->light_distance); + cf_WriteFloat (outfile,lighting_info->red_light1); + cf_WriteFloat (outfile,lighting_info->green_light1); + cf_WriteFloat (outfile,lighting_info->blue_light1); + cf_WriteFloat (outfile,lighting_info->time_interval); + cf_WriteFloat (outfile,lighting_info->flicker_distance); + cf_WriteFloat (outfile,lighting_info->directional_dot); + cf_WriteFloat (outfile,lighting_info->red_light2); + cf_WriteFloat (outfile,lighting_info->green_light2); + cf_WriteFloat (outfile,lighting_info->blue_light2); + cf_WriteInt (outfile,lighting_info->flags); + cf_WriteInt (outfile,lighting_info->timebits); + cf_WriteByte (outfile,lighting_info->angle); + cf_WriteByte (outfile,lighting_info->lighting_render_type); + +} +// Reads a lighting chunk in from an open file +void mng_ReadLightingChunk (light_info *lighting_info,CFILE *infile) +{ + lighting_info->light_distance=cf_ReadFloat (infile); + lighting_info->red_light1=cf_ReadFloat (infile); + lighting_info->green_light1=cf_ReadFloat (infile); + lighting_info->blue_light1=cf_ReadFloat (infile); + lighting_info->time_interval=cf_ReadFloat (infile); + lighting_info->flicker_distance=cf_ReadFloat (infile); + lighting_info->directional_dot=cf_ReadFloat (infile); + lighting_info->red_light2=cf_ReadFloat (infile); + lighting_info->green_light2=cf_ReadFloat (infile); + lighting_info->blue_light2=cf_ReadFloat (infile); + lighting_info->flags=cf_ReadInt (infile); + lighting_info->timebits=cf_ReadInt (infile); + lighting_info->angle=cf_ReadByte (infile); + lighting_info->lighting_render_type=cf_ReadByte (infile); +} + +// Record keeping +AddOnTablefile AddOnDataTables[MAX_ADDON_TABLES]; + +// the number of addon tables currently in memory +int Num_addon_tables=0; + +// if not -1, then it is the addon table we are working with +int Loading_addon_table=-1; + +//---------------------- +// Add-on data routines +//---------------------- +// Frees all the primitives associated with an page +void mng_FreePagetypePrimitives (int pagetype,char *name,int freetype) +{ + int n; + switch (pagetype) + { + case PAGETYPE_TEXTURE: + n=FindTextureName (name); + ASSERT (n>=0); + if (GameTextures[n].flags & TF_ANIMATED) + FreeVClip(GameTextures[n].bm_handle); + else + bm_FreeBitmap (GameTextures[n].bm_handle); + if (GameTextures[n].flags & TF_DESTROYABLE && GameTextures[n].destroy_handle>=0 && GameTextures[n].destroy_handle!=n) + { + int oldn=n; + n=GameTextures[n].destroy_handle; + if (GameTextures[n].flags & TF_ANIMATED) + FreeVClip(GameTextures[n].bm_handle); + else + bm_FreeBitmap (GameTextures[n].bm_handle); + n=oldn; + } + if (freetype) + FreeTexture(n); + + break; + case PAGETYPE_SOUND: + n=FindSoundName (name); + ASSERT (n>=0); + FreeSoundFile (Sounds[n].sample_index); + if (freetype) + FreeSound (n); + break; + case PAGETYPE_WEAPON: + n=FindWeaponName (name); + ASSERT (n>=0); + // Free weapon images + if (Weapons[n].flags & WF_HUD_ANIMATED) + FreeVClip(Weapons[n].hud_image_handle); + else + bm_FreeBitmap (Weapons[n].hud_image_handle); + if (Weapons[n].fire_image_handle!=-1) + { + if (Weapons[n].flags & WF_IMAGE_BITMAP) + bm_FreeBitmap (Weapons[n].fire_image_handle); + else if (Weapons[n].flags & WF_IMAGE_VCLIP) + FreeVClip (Weapons[n].fire_image_handle); + else + FreePolyModel(Weapons[n].fire_image_handle); + } + if (freetype) + FreeWeapon (n); + break; + case PAGETYPE_SHIP: + n=FindShipName (name); + ASSERT (n>=0); + FreePolyModel (Ships[n].model_handle); + if (Ships[n].dying_model_handle!=-1) + FreePolyModel (Ships[n].dying_model_handle); + if (Ships[n].med_render_handle!=-1) + FreePolyModel (Ships[n].med_render_handle); + if (Ships[n].lo_render_handle!=-1) + FreePolyModel (Ships[n].lo_render_handle); + if (freetype) + FreeShip (n); + break; + case PAGETYPE_GENERIC: + n=FindObjectIDName (name); + ASSERT (n>=0); + + FreePolyModel (Object_info[n].render_handle); + if (Object_info[n].med_render_handle!=-1) + FreePolyModel (Object_info[n].med_render_handle); + if (Object_info[n].lo_render_handle!=-1) + FreePolyModel (Object_info[n].lo_render_handle); + if (freetype) + FreeObjectID(n); + break; + case PAGETYPE_DOOR: + n=FindDoorName (name); + ASSERT (n>=0); + FreePolyModel (Doors[n].model_handle); + if (freetype) + FreeDoor (n); + break; + default: + break; + } +} +// Takes our addon pages and frees/restores our data to the appropriate pages +void mng_PopAddonPages() +{ + int i,n,ok; + + ASSERT(Num_addon_tables>0); + if(Num_addon_tables<=0) + return; //no addon pages to pop off + + Num_addon_tables--; + Loading_locals=0; + AddOnTablefile *addondata = &AddOnDataTables[Num_addon_tables]; + + for (i=0;iNum_addon_tracklocks;i++) + { + mprintf ((0,"Freeing addon page %s [%s].\n",addondata->Addon_tracklocks[i].name,addondata->AddOnTableFilename)); + + // set the Loading_addon_table to the appropriate value... + // it depends on if we are overlaying from a previous tablefile + // or this isn't an overlay at all + // overlay = 0 (not an overlay of anything) + // overlay = 1 (overlay of main tablefile) + // overlay > 1 (overlay of addon table [overlay-2]) + if(addondata->Addon_tracklocks[i].overlay>1) + { + // this is an overlay of another table file + Loading_addon_table=addondata->Addon_tracklocks[i].overlay-2; + }else + { + // this is an overlay of the main table file + // or not an overlay at all + Loading_addon_table=-1; + } + + if (addondata->Addon_tracklocks[i].overlay>0) + { + // Free this data, then read the old stuff back in + mng_FreePagetypePrimitives (addondata->Addon_tracklocks[i].pagetype,addondata->Addon_tracklocks[i].name,0); + char *name=addondata->Addon_tracklocks[i].name; + switch (addondata->Addon_tracklocks[i].pagetype) + { + case PAGETYPE_TEXTURE: + { + n=FindTextureName (name); + ASSERT (n>=0); + ok=mng_FindSpecificTexPage (name,&texpage,addondata->Addon_tracklocks[i].stack_filepos); + if (ok) + { + ok=mng_AssignTexPageToTexture (&texpage,n); + if (!ok) + Error ("Error 1 restoring page %s from addon data!",name); + } + else + Error ("Error 2 restoring page %s from addon data!",name); + break; + } + case PAGETYPE_SOUND: + { + n=FindSoundName (name); + ASSERT (n>=0); + ok=mng_FindSpecificSoundPage (name,&soundpage,addondata->Addon_tracklocks[i].stack_filepos); + if (ok) + { + ok=mng_AssignSoundPageToSound (&soundpage,n); + if (!ok) + Error ("Error 1 restoring page %s from addon data!",name); + } + else + Error ("Error 2 restoring page %s from addon data!",name); + break; + } + case PAGETYPE_DOOR: + { + n=FindDoorName (name); + ASSERT (n>=0); + ok=mng_FindSpecificDoorPage (name,&doorpage,addondata->Addon_tracklocks[i].stack_filepos); + if (ok) + { + ok=mng_AssignDoorPageToDoor (&doorpage,n); + if (!ok) + Error ("Error 1 restoring page %s from addon data!",name); + } + else + Error ("Error 2 restoring page %s from addon data!",name); + break; + } + case PAGETYPE_GENERIC: + { + n=FindObjectIDName (name); + ASSERT (n>=0); + ok=mng_FindSpecificGenericPage (name,&genericpage,addondata->Addon_tracklocks[i].stack_filepos); + if (ok) + { + ok=mng_AssignGenericPageToObjInfo (&genericpage,n); + if (!ok) + Error ("Error 1 restoring page %s from addon data!",name); + } + else + Error ("Error 2 restoring page %s from addon data!",name); + break; + } + case PAGETYPE_SHIP: + { + n=FindShipName (name); + ASSERT (n>=0); + ok=mng_FindSpecificShipPage (name,&shippage,addondata->Addon_tracklocks[i].stack_filepos); + if (ok) + { + ok=mng_AssignShipPageToShip (&shippage,n); + if (!ok) + Error ("Error 1 restoring page %s from addon data!",name); + } + else + Error ("Error 2 restoring page %s from addon data!",name); + break; + } + case PAGETYPE_WEAPON: + { + n=FindWeaponName (name); + ASSERT (n>=0); + ok=mng_FindSpecificWeaponPage (name,&weaponpage,addondata->Addon_tracklocks[i].stack_filepos); + if (ok) + { + ok=mng_AssignWeaponPageToWeapon (&weaponpage,n); + if (!ok) + Error ("Error 1 restoring page %s from addon data!",name); + } + else + Error ("Error 2 restoring page %s from addon data!",name); + break; + } + case PAGETYPE_GAMEFILE: + //I don't think there's anything we need to do here + break; + default: + Int3(); // bad type in list? Get Jason + break; + } + + } + else + { + // Not overlay, just free this data + mng_FreePagetypePrimitives (addondata->Addon_tracklocks[i].pagetype,addondata->Addon_tracklocks[i].name,1); + } + } + + Loading_addon_table=-1; +} +// Simply sets no addon data to be loaded +void mng_ClearAddonTables () +{ + int count = Num_addon_tables; + while(count>0) + { + ASSERT(count==Num_addon_tables); + + if(AddOnDataTables[count-1].Addon_tracklocks) + { + mng_PopAddonPages(); + mem_free (AddOnDataTables[count-1].Addon_tracklocks); + AddOnDataTables[count-1].Addon_tracklocks = NULL; + AddOnDataTables[count-1].Num_addon_tracklocks = 0; + } + + count--; + } +} +// Push the given table file as an addon table file +// returns true on success +bool mng_SetAddonTable(char *name) +{ + ASSERT(Num_addon_tables=MAX_ADDON_TABLES) + return false; + + //make sure the table file exists! + if(!cfexist(name)) + return false; + + strcpy(AddOnDataTables[Num_addon_tables].AddOnTableFilename,name); + AddOnDataTables[Num_addon_tables].Addon_tracklocks=(mngs_track_lock *)mem_malloc (MAX_ADDON_TRACKLOCKS*sizeof(mngs_track_lock)); + AddOnDataTables[Num_addon_tables].Num_addon_tracklocks = 0; + ASSERT (AddOnDataTables[Num_addon_tables].Addon_tracklocks); + memset(AddOnDataTables[Num_addon_tables].Addon_tracklocks,0,MAX_ADDON_TRACKLOCKS*sizeof(mngs_track_lock)); + + Num_addon_tables++; + return true; +} + +// Pushes an addon pack onto the stack so we can keep track of it +void mng_PushAddonPage (int pagetype,char *name,int overlay) +{ + ASSERT(Loading_addon_table>=0 && Loading_addon_tableNum_addon_tracklocksNum_addon_tracklocks;i++) + { + if (addon->Addon_tracklocks[i].used && addon->Addon_tracklocks[i].pagetype==pagetype) + { + if (!stricmp (addon->Addon_tracklocks[i].name,name)) + { + Int3(); + Error ("Redundant addon page '%s' loaded...",name); + return; + } + } + } + mprintf ((0,"Adding addon page %s [%s] to list.\n",name,addon->AddOnTableFilename)); + addon->Addon_tracklocks[addon->Num_addon_tracklocks].used=1; + addon->Addon_tracklocks[addon->Num_addon_tracklocks].pagetype=pagetype; + addon->Addon_tracklocks[addon->Num_addon_tracklocks].overlay=overlay; + addon->Addon_tracklocks[addon->Num_addon_tracklocks].stack_filepos = 0; + strcpy (addon->Addon_tracklocks[addon->Num_addon_tracklocks].name,name); + addon->Num_addon_tracklocks++; +} + +// Compiles the addon pages. By looking at all the addon pages (after they have been +// loaded) and does some compiling and saving of information to speed up addon page +// freeing +void mng_CompileAddonPages(void) +{ + if(Num_addon_tables<=0) + return; //no addon pages to pop off + + int curr_tablefile,i,tf,pagetype,len,next_pos,page_pos; + CFILE *file; + char pagename[256]; + bool found_page; + + for(curr_tablefile=1;curr_tablefile<=Num_addon_tables;curr_tablefile++) + { + // find all the pages that are loaded from this tablefile + // overlay = 0 (not from any tablefile) + // overlay = 1 (from main tablefile) + // overlay > 1 (from addontable[overlay-2] + if(curr_tablefile==1) + { + file = cfopen(TableFilename,"rb"); + mprintf((0,"Compiling addon pages of %s\n",TableFilename)); + } + else + { + file = cfopen(AddOnDataTables[curr_tablefile-2].AddOnTableFilename,"rb"); + mprintf((0,"Compiling addon pages of %s\n",AddOnDataTables[curr_tablefile-2].AddOnTableFilename)); + } + ASSERT(file!=NULL); + + // start reading the pages from this tablefile + // as we come across each page, check to see if it was + // ever overlayed. + while(!cfeof(file)) + { + // Read in a pagetype. If its a page we recognize, load it + page_pos = cftell(file); + pagetype=cf_ReadByte (file); + len=cf_ReadInt (file); + next_pos = (page_pos + 1) + len; + + // get the name of the page + switch (pagetype) + { + case PAGETYPE_TEXTURE: + cf_ReadShort(file);//version + cf_ReadString(pagename,PAGENAME_LEN,file); + break; + case PAGETYPE_DOOR: + cf_ReadShort(file);//version + cf_ReadString(pagename,PAGENAME_LEN,file); + break; + case PAGETYPE_GENERIC: + cf_ReadShort(file);//version + cf_ReadByte(file);//type + cf_ReadString(pagename,PAGENAME_LEN,file); + break; + case PAGETYPE_GAMEFILE: + cf_ReadShort(file);//version + cf_ReadString(pagename,PAGENAME_LEN,file); + break; + case PAGETYPE_SOUND: + cf_ReadShort(file);//version + cf_ReadString(pagename,PAGENAME_LEN,file); + break; + case PAGETYPE_SHIP: + cf_ReadShort(file);//version + cf_ReadString(pagename,PAGENAME_LEN,file); + break; + case PAGETYPE_WEAPON: + cf_ReadShort(file);//version + cf_ReadString(pagename,PAGENAME_LEN,file); + break; + case PAGETYPE_UNKNOWN: + continue; + break; + default: + Int3(); // Unrecognized pagetype, possible corrupt data following + break; + } + + // now look for all the places where this page is overlayed + found_page = false; + for(tf=0;tfAddOnTableFilename)); + Addon_filename = addon->AddOnTableFilename; + infile=cfopen (addon->AddOnTableFilename,"rb"); + if (!infile) + { + mprintf ((0,"Couldn't addon table file (%s) to read pages!\n",addon->AddOnTableFilename)); + return; + } + Loading_addon_table=c; + while (!cfeof(infile)) + { + // Read in a pagetype. If its a page we recognize, load it + pagetype=cf_ReadByte (infile); + len=cf_ReadInt (infile); + + mprintf ((0,".")); + switch (pagetype) + { + case PAGETYPE_TEXTURE: + mng_LoadLocalTexturePage (infile); + break; + case PAGETYPE_POWERUP: + case PAGETYPE_ROBOT: + Error("Your local table file is invalid. You must update from the network."); + break; + case PAGETYPE_DOOR: + mng_LoadLocalDoorPage (infile); + break; + case PAGETYPE_GENERIC: + mng_LoadLocalGenericPage (infile); + break; + case PAGETYPE_GAMEFILE: + mng_LoadLocalGamefilePage (infile); + break; + case PAGETYPE_SOUND: + mng_LoadLocalSoundPage (infile); + break; + case PAGETYPE_SHIP: + mng_LoadLocalShipPage (infile); + break; + case PAGETYPE_WEAPON: + mng_LoadLocalWeaponPage (infile); + break; + case PAGETYPE_MEGACELL: + mng_LoadLocalMegacellPage (infile); + break; + case PAGETYPE_UNKNOWN: + break; + default: + Int3(); // Unrecognized pagetype, possible corrupt data following + break; + } + } + mprintf ((0,"------------------------------------\n")); + cfclose (infile); + } + + Loading_locals=0; + Loading_addon_table=-1; + + mng_CompileAddonPages(); + + //Close error file + if (Data_error_file != NULL) { + fprintf(Data_error_file,"\nTotal errors: %d",Data_error_count); + fclose(Data_error_file); + } + + //Clear flag + Loading_addon = false; +} +/* +#define MAX_256s 200 +int Num_256s=0; +char Texture256Names[MAX_256s][80]; +void Read256TextureNames () +{ + int n=FindArg ("-File256"); + if (!n) + return; + CFILE *infile; + infile=(CFILE *)cfopen (GameArgs[n+1],"rt"); + if (!infile) + { + mprintf ((0,"Couldn't open 256 file!\n")); + return; + } + + char curline[200]; + int done=0; + while (!done) + { + if (cfeof(infile)) + { + done=1; + continue; + } + + // Read a line and parse it + cf_ReadString (curline,200,infile); + if (curline[0]==';' || curline[1]==';' || curline[0]==' ' || curline[1]==' ') + continue; + if (!(isalnum(curline[0]))) + continue; + strcpy (Texture256Names[Num_256s],curline); + Num_256s++; + } + cfclose (infile); +}*/ + +#include "pstring.h" + +void DataError(char *fmt,...) +{ + //Got a data error! +// Int3(); + + //Ignore this if not loading add-on data + if (! Loading_addon) + return; + + //Increment error count + Data_error_count++; + + //Write to file if switch specified + if (FindArg("-datacheck")) { + static char last_filename[_MAX_PATH]; + va_list arglist; + char buf[1024]; + + va_start(arglist,fmt); + Pvsprintf(buf,sizeof(buf),fmt,arglist); + va_end(arglist); + + //Open file if not already open + if (Data_error_file == NULL) { + Data_error_file = fopen("datacheck.out","wt"); + + if (Data_error_file == NULL) + return; + + last_filename[0] = 0; + } + + //If this is a new addon file, print the name + if (strcmp(last_filename,Addon_filename)) { + if (last_filename[0]) + fprintf(Data_error_file,"\n\n"); + fprintf(Data_error_file,"Errors in addon file <%s>:\n\n",Addon_filename); + strcpy(last_filename,Addon_filename); + } + + //Print the message + fprintf(Data_error_file," "); + fprintf(Data_error_file,"%s",buf); + } +} diff --git a/manage/megapage.cpp b/manage/megapage.cpp new file mode 100644 index 000000000..cc75fd097 --- /dev/null +++ b/manage/megapage.cpp @@ -0,0 +1,358 @@ +/* + * $Logfile: /DescentIII/Main/manage/megapage.cpp $ + * $Revision: 9 $ + * $Date: 8/11/99 5:32p $ + * $Author: Jeff $ + * + * + * $Log: /DescentIII/Main/manage/megapage.cpp $ + * + * 9 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 8 7/28/99 2:28p Kevin + * Macintosh Changes! + * + * 7 4/14/99 1:33a Jeff + * fixed case mismatched #includes + * + * 6 3/04/99 4:47p Jason + * temp fix (ie BAD HACK) for OEM table file woes + * + * 5 1/13/99 7:05a Jeff + * put #ifdef around #include + * + * 4 12/29/98 4:30p Jason + * added add-on data functionality + * + * 3 11/06/98 12:35p Jason + * more speedups for manage system + * + * 2 11/05/98 7:55p Jason + * changes for new manage system + * + * 3 6/05/97 6:58p Jason + * added more megacell functionality + * + * 2 6/05/97 2:52p Jason + * added megacell functions + * + * 1 6/05/97 11:13a Jason + * page for megacells + * + * $NoKeywords: $ + */ + +#if defined(WIN32) +#include +#endif +#include "CFILE.H" +#include "manage.h" +#include "megacell.h" +#include "megapage.h" +#include "texpage.h" +#include "mono.h" +#include "pserror.h" +#include "polymodel.h" +#include "ddio.h" +#if defined(LINUX) +#include +#endif +// megacellpage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define MEGACELLPAGE_COMMAND_NAME 1 +#define MEGACELLPAGE_COMMAND_END 2 +#define MEGACELLPAGE_COMMAND_CELL_NAME 3 +#define MEGACELLPAGE_COMMAND_VERSION 4 +#define MEGACELLPAGE_COMMAND_WIDTH 5 +#define MEGACELLPAGE_COMMAND_HEIGHT 6 +#define MEGACELL_VERSION 2 +// Given an open file pointer and a megacell_page struct, writes that megacell page out +void mng_WriteMegacellPage (CFILE *outfile,mngs_megacell_page *megacellpage) +{ + int i; + ASSERT (outfile!=NULL); + ASSERT (megacellpage!=NULL); + + cf_WriteByte (outfile,PAGETYPE_MEGACELL); + + cf_WriteByte (outfile,MEGACELLPAGE_COMMAND_NAME); // write out megacell name + cf_WriteByte (outfile,strlen(megacellpage->megacell_struct.name)+1); + cf_WriteString (outfile,megacellpage->megacell_struct.name); + + // Write out its cell names + for (i=0;icellname[i])+2); + cf_WriteByte (outfile,i); + cf_WriteString (outfile,megacellpage->cellname[i]); + } + + cf_WriteByte (outfile,MEGACELLPAGE_COMMAND_WIDTH); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,megacellpage->megacell_struct.width); + cf_WriteByte (outfile,MEGACELLPAGE_COMMAND_HEIGHT); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,megacellpage->megacell_struct.height); + cf_WriteByte (outfile,MEGACELLPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} +// Given an open file pointer and a megacell_page struct, writes that megacell page out +void mng_WriteNewMegacellPage (CFILE *outfile,mngs_megacell_page *megacellpage) +{ + int i; + ASSERT (outfile!=NULL); + ASSERT (megacellpage!=NULL); + int offset=StartManagePage(outfile,PAGETYPE_MEGACELL); + cf_WriteShort (outfile,MEGACELL_VERSION); + + cf_WriteString (outfile,megacellpage->megacell_struct.name); + + // Write out its cell names + for (i=0;icellname[i]); + + cf_WriteByte (outfile,megacellpage->megacell_struct.width); + cf_WriteByte (outfile,megacellpage->megacell_struct.height); + EndManagePage (outfile,offset); +} +// Reads a megacell page from an open file. Returns 0 on error. +int mng_ReadMegacellPage (CFILE *infile,mngs_megacell_page *megacellpage) +{ + int done=0; + char command; + ushort len; + int i,temp; + if (!Old_table_method) + return mng_ReadNewMegacellPage (infile,megacellpage); + ASSERT (infile!=NULL); + memset (megacellpage,0,sizeof(mngs_megacell_page)); + while (!done) + { + // Read in command byte then read in the length of that commands data + + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + switch (command) + { + case MEGACELLPAGE_COMMAND_END: + done=1; + break; + case MEGACELLPAGE_COMMAND_CELL_NAME: // the name of the megacell model + temp=cf_ReadByte (infile); + cf_ReadString (megacellpage->cellname[temp],PAGENAME_LEN,infile); + break; + case MEGACELLPAGE_COMMAND_NAME: + cf_ReadString (megacellpage->megacell_struct.name,PAGENAME_LEN,infile); + break; + case MEGACELLPAGE_COMMAND_WIDTH: + megacellpage->megacell_struct.width=cf_ReadByte(infile); + break; + case MEGACELLPAGE_COMMAND_HEIGHT: + megacellpage->megacell_struct.height=cf_ReadByte(infile); + break; + default: + // Ignore the ones we don't know + for (i=0;imegacell_struct.used=1; + return 1; // successfully read +} +// Reads a megacell page from an open file. Returns 0 on error. +int mng_ReadNewMegacellPage (CFILE *infile,mngs_megacell_page *megacellpage) +{ + int i; + ASSERT (infile!=NULL); + memset (megacellpage,0,sizeof(mngs_megacell_page)); + int version=cf_ReadShort (infile); + + cf_ReadString (megacellpage->megacell_struct.name,PAGENAME_LEN,infile); + + // Write out its cell names + for (i=0;icellname[i],PAGENAME_LEN,infile); + + megacellpage->megacell_struct.width=cf_ReadByte (infile); + megacellpage->megacell_struct.height=cf_ReadByte (infile); + // This is a valid new page + megacellpage->megacell_struct.used=1; + return 1; // successfully read +} +// Reads in the megacell named "name" into megacellpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificMegacellPage (char *name,mngs_megacell_page *megacellpage,int local) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + + if (local) + infile=cfopen (LocalTableFilename,"rb"); + else + infile=cfopen (TableFilename,"rb"); + if (!infile) + { + mprintf ((0,"Couldn't open table file to find megacell!\n")); + Int3(); + return 0; + } + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + // If not a megacell page, just read it in and ignore it + if (pagetype!=PAGETYPE_MEGACELL) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + mng_ReadNewMegacellPage (infile,megacellpage); + + if (!stricmp(name,megacellpage->megacell_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + cfclose (infile); + + return found; // successful! +} +// Given a megacell page, allocs a megacell and calls AssignMegacellPageToMegacell to actually +// load models and values. Rturns megacell handle on success, -1 if fail +int mng_SetAndLoadMegacell (mngs_megacell_page *megacellpage) +{ + int n; + + n=AllocMegacell(); + if (n<0) + return -1; + if (!mng_AssignMegacellPageToMegacell(megacellpage,n)) + return -1; + + return n; +} +// Given a megacellpage and a megacell handle, attempts to make megacell n correspond to +// to the megacellpage. +// Returns 1 on success, 0 otherwise +int mng_AssignMegacellPageToMegacell(mngs_megacell_page *megacellpage,int n) +{ + megacell *megacellpointer=&Megacells[n]; + int img_handle,i; + // copy our values + memcpy (megacellpointer,&megacellpage->megacell_struct,sizeof(megacell)); + strcpy (megacellpointer->name,megacellpage->megacell_struct.name); + // Try and load our megacell images from the disk + for (i=0;icellname[i],"INVALID IMAGE NAME")) + { + megacellpointer->texture_handles[i]=0; + } + else + { + img_handle=mng_GetGuaranteedTexturePage (megacellpage->cellname[i]); + if (img_handle<0) + megacellpointer->texture_handles[i]=0; + else + megacellpointer->texture_handles[i]=img_handle; + } + } + + return 1; +} +// Copies values from a megacell into a megacell_page +void mng_AssignMegacellToMegacellPage(int n,mngs_megacell_page *megacellpage) +{ + megacell *megacellpointer=&Megacells[n]; + // Assign the values + memcpy (&megacellpage->megacell_struct,megacellpointer,sizeof(megacell)); + + strcpy (megacellpage->megacell_struct.name,megacellpointer->name); + for (int i=0;itexture_handles[i]!=0) + strcpy (megacellpage->cellname[i],GameTextures[megacellpointer->texture_handles[i]].name); + else + strcpy (megacellpage->cellname[i],"INVALID IMAGE NAME"); + } +} +// Loads a megacell found in the net table file. It then allocs a megacell and +// then calls SetAndLoadMegacell to actually load in any images/models associated +// with it +void mng_LoadNetMegacellPage(CFILE *infile) +{ + mngs_megacell_page megacellpage; + memset(&megacellpage, 0, sizeof(mngs_megacell_page)); + + if (mng_ReadNewMegacellPage (infile,&megacellpage)) + { + int ret=mng_SetAndLoadMegacell (&megacellpage); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load megacellpage named %s!\n",megacellpage.megacell_struct.name)); +} +// Reads a megacell page from a local table file. It then allocs a megacell and +// loads any images/models associated with that megacell +void mng_LoadLocalMegacellPage(CFILE *infile) +{ + mngs_megacell_page megacellpage; + int ok=0; + memset(&megacellpage, 0, sizeof(mngs_megacell_page)); + if (mng_ReadNewMegacellPage (infile,&megacellpage)) + { + // Check to see if this is a local copy that is supposed + // to go over a network copy (supersede the net copy) + int i=FindMegacellName (megacellpage.megacell_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + + strcpy (pl.name,megacellpage.megacell_struct.name); + pl.pagetype=PAGETYPE_MEGACELL; + + /*if (Network_up && Stand_alone==0) + { + int locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + + mng_AssignMegacellPageToMegacell (&megacellpage,i); + ok=1; + } + else + { + // This is a local megacell that has never been checked in + if ((i=mng_SetAndLoadMegacell (&megacellpage))<0) + ok=0; + else ok=1; + } + + ASSERT (ok==1); + if (Loading_addon_table==-1) + mng_AllocTrackLock (megacellpage.megacell_struct.name,PAGETYPE_MEGACELL); + } + else + mprintf ((0,"Could not load megacellpage named %s!\n",megacellpage.megacell_struct.name)); + +} + + diff --git a/manage/megapage.h b/manage/megapage.h new file mode 100644 index 000000000..e1b9c8eef --- /dev/null +++ b/manage/megapage.h @@ -0,0 +1,55 @@ +#ifndef MEGACELLPAGE_H +#define MEGACELLPAGE_H + +#include "manage.h" +#include "megacell.h" +#include "CFILE.H" +#include "pstypes.h" + +typedef struct +{ + megacell megacell_struct; + char cellname[MAX_MEGACELL_WIDTH*MAX_MEGACELL_HEIGHT][PAGENAME_LEN]; +} mngs_megacell_page; + +// Megacell page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a megacell_page struct, writes that megacellpage out +void mng_WriteMegacellPage (CFILE *outfile,mngs_megacell_page *megacellpage); + +// Reads a megacell page from an open file. Returns 0 on error. +int mng_ReadMegacellPage (CFILE *infile,mngs_megacell_page *megacellpage); + +// Given an open file pointer and a megacell_page struct, writes that megacellpage out +void mng_WriteNewMegacellPage (CFILE *outfile,mngs_megacell_page *megacellpage); + +// Reads a megacell page from an open file. Returns 0 on error. +int mng_ReadNewMegacellPage (CFILE *infile,mngs_megacell_page *megacellpage); + + +// Reads in the megacellpage named "name" into megacellpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificMegacellPage (char *name,mngs_megacell_page *megacellpage,int local); + +// Given a megacell page, allocs a megacell and calls AssignMegacellPageToMegacell to actually +// load model and values. Rturns megacell handle on success, -1 if fail +int mng_SetAndLoadMegacell (mngs_megacell_page *megacellpage); + +// Given a megacellpage and a megacell handle, attempts to make megacell n correspond to +// to the megacellpage. +// Returns 1 on success, 0 otherwise +int mng_AssignMegacellPageToMegacell(mngs_megacell_page *megacellpage,int n); + +// Copies values from a Megacell into a megacell_page +void mng_AssignMegacellToMegacellPage(int n,mngs_megacell_page *megacellpage); + + +// Reads in a megacell page from the local table file, superseding any megacell +// already in RAM with that same name +void mng_LoadLocalMegacellPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetMegacellPage (CFILE *); + +#endif \ No newline at end of file diff --git a/manage/pagelock.cpp b/manage/pagelock.cpp new file mode 100644 index 000000000..a2e82d2f8 --- /dev/null +++ b/manage/pagelock.cpp @@ -0,0 +1,983 @@ +/* + * $Logfile: /DescentIII/Main/manage/pagelock.cpp $ + * $Revision: 42 $ + * $Date: 5/17/99 2:09p $ + * $Author: Jeff $ + * + * Jason should put something here + * + * $Log: /DescentIII/Main/manage/pagelock.cpp $ + * + * 42 5/17/99 2:09p Jeff + * fixed tablefile logging + * + * 41 5/14/99 12:15p Jason + * added page logging + * + * 40 4/30/99 8:53p Matt + * Added a "voice" directory for gamefiles. + * + * 39 4/22/99 7:08p Matt + * Reduced the number of object sounds from 3 to 2, since we were only + * using two. + * + * 38 4/18/99 4:49p Matt + * Took out code that hacked in ammo amounts for weapon powerups, and + * added code to set the counts on the page and read and write to the + * table file. + * + * 37 4/14/99 10:46a Kevin + * Removed OutrageMessageBox from release builds + * + * 36 4/14/99 1:33a Jeff + * fixed case mismatched #includes + * + * 35 4/12/99 12:49p Jeff + * added recoil_force to weapon's page + * + * 34 4/09/99 12:51p Jason + * corrected bug sound resolving code + * + * 33 4/06/99 6:02p Matt + * Added score system + * + * 32 3/30/99 6:11p Jason + * changed pagelock version + * + * 31 3/29/99 11:20a Matt + * Added more flexibility to death delays, and added fade away deaths. + * + * 30 3/04/99 1:47p Jason + * fixed some manage problems + * + * + * 29 3/01/99 12:54a Matt + * Incremented table file version + * + * 28 2/23/99 12:39p Jason + * added more options for generic objects + * + * 27 2/22/99 2:03p Jason + * added different damages for players and generics + * + * 26 2/03/99 1:58a Matt + * Added a system to show all pages locked. + * + * 25 2/02/99 8:44a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 24 12/30/98 5:17p Jeff + * changes made to handle script name override, to override the name of + * the script to use in a module. + * + * 23 12/21/98 5:27p Jeff + * added osiris module information to door page. Fixed up dialogs for + * objects and doors for script interface (added a browse button) + * + * 22 12/17/98 7:26p Jeff + * new script system data + * + * 21 12/17/98 12:50p Jason + * changed doorpage + * + * 20 12/01/98 4:31p Chris + * Checked in a massive amount of AI work. Improved flocking code. :) + * (Still hacked lightly). In addition, I am moving toward using the + * composite dir. :) + * + * 19 11/19/98 8:25p Chris + * + * 18 11/13/98 12:30p Jason + * changes for weapons + * + * 17 11/06/98 11:24a Jason + * fixed length bug + * + * 16 11/05/98 7:55p Jason + * changes for new manage system + * + * 15 10/19/98 11:57a Chris + * Update the sound system to use the import volume + * + * 14 10/14/98 5:15p Jason + * added version checking to the table file + * + * 13 6/23/98 2:43p Matt + * Changed calls to OutrageMessageBox() & Debug_MessageBox() to deal with + * int return value (instead of bool). + * + * 12 6/05/98 12:54p Jason + * fixed stupid pagelock bug + * + * 11 6/03/98 3:46p Jason + * made megacell system more robust + * + * 10 5/04/98 5:13p Jason + * better error handling + * + * 9 3/19/98 1:15p Jason + * more error checking + * + * 8 3/19/98 12:02p Jason + * added better error checking to pagelocks + * + * 7 2/19/98 1:52p Jason + * added emergency override to unlock function + * + * 6 10/06/97 3:30p Samir + * FROM JASON: added error message + * + * 5 9/25/97 2:19p Jason + * added test case to EraseLocker + * + * 4 9/25/97 11:22a Jason + * added error checking to EraseLocker + * + * 3 8/12/97 12:07p Matt + * Treat old robot locks as generic locks + * + * 2 8/08/97 1:48p Matt + * Change mng_MakeLocker() bring up dialog if no network, and give the + * player a chance to retry if someone else has the database locked. + * + * 16 3/17/97 6:52p Jason + * added mprintf to locker file error + * + * 15 3/13/97 7:38p Matt + * Added include of string.h + * + * 14 3/13/97 6:42 PM Jeremy + * #included and unincluded + * + * 13 3/03/97 6:21p Matt + * Changed cfile functions to use D3 naming convention + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include "CFILE.H" +#include "manage.h" +#include "pstypes.h" +#include "pserror.h" +#include "mono.h" +#include "string.h" +#include "mem.h" +#include "ddio.h" + +#ifndef RELEASE +#include +#endif + +#define CURRENT_TABLE_VERSION 22 +extern char *PageNames[]; + +void mng_InitPagelocks () +{ + // If there is not a pagelock file, create one with a dummy header. + + CFILE *infile,*outfile; + mngs_Pagelock testlock; + + if (!Network_up) + return; + + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (infile==NULL) + { + outfile=(CFILE *)cfopen (TableLockFilename,"wb"); + if (!outfile) + { + Error("Error creating table lock file!"); + return; + } + testlock.pagetype=PAGETYPE_UNKNOWN; + strcpy (testlock.name,"DUMMY PAGE"); + strcpy (testlock.holder,"NOT HELD AT ALL"); + mng_WritePagelock (outfile,&testlock); + + cfclose (outfile); + } + else cfclose (infile); +} + +// Checks to see if the locker file is present. +// Returns pointer to locker's name, or NULL if the file is unlocked +char *mng_CheckIfLockerPresent () +{ + CFILE *infile; + int i=0; + static char lockname[200]; + + ASSERT(Network_up); + + infile=(CFILE *)cfopen (LockerFile,"rb"); + if (!infile) + return NULL; + + cf_ReadString(lockname,sizeof(lockname),infile); + + mprintf ((0,"%s has already got exlusive access to the table file.\n",lockname)); + + cfclose (infile); + + return lockname; +} + +// Returns 1 if we have the most up to date code for the manage system, else 0 +int TableVersionCurrent () +{ + CFILE *infile; + + if (!cfexist (VersionFile)) + return 1; // Doesn't exist, so go ahead + + infile=(CFILE *)cfopen (VersionFile,"rb"); + if (!infile) + { + mprintf ((0,"Couldn't open the version file for querying!\n")); + return 0; + } + + int version=cf_ReadInt (infile); + + cfclose (infile); + + if (version>CURRENT_TABLE_VERSION) + return 0; + + return 1; +} + + +// Call this before any chokepoint functions are executed. +// Locks the whole table system for our exclusive use +int mng_MakeLocker() +{ + CFILE *outfile; + int len; + char *locker; + + len=strlen(TableUser); +#ifndef RELEASE + if (!Network_up) { + OutrageMessageBox("Error: You are not connected to the network."); + return 0; + } + + //See if someone else has the file locked + do { + locker = mng_CheckIfLockerPresent(); + if (locker) { + strupr(locker); + if (OutrageMessageBox (MBOX_YESNO, "The network database is already locked by %s.\nIt will probably become available if you wait a few moments.\n\nWould you like to try again?",locker) != IDYES) + return 0; + } + } while (locker); + + // Make sure the version is current + if (!TableVersionCurrent()) + { + OutrageMessageBox ("You must do a source update and recompile. Your table file code is outdated!"); + return 0; + } + + outfile=(CFILE *)cfopen (LockerFile,"wb"); + if (!outfile) + { + OutrageMessageBox("Error opening the locker file!"); + mprintf ((0,"couldn't open the locker file!\n")); + return 0; + } +#endif + for (int i=0;i=10) + { + OutrageMessageBox ("There was a problem deleting the locker file...get Jason immediately!"); + return; + } + else + count++; + } + else + { + done=1; + } + } + + // Now update the version info + count=0; + while (1) + { + CFILE *fp=(CFILE *)cfopen(VersionFile,"wb"); + if (!fp) + { + if (count>=10) + { + OutrageMessageBox ("There was a problem opening the version file...get Jason immediately!"); + return; + } + else + count++; + } + else + { + cf_WriteInt (fp,CURRENT_TABLE_VERSION); + cfclose (fp); + return; + } + } +#endif +} + + +int mng_CheckIfPageLocked (mngs_Pagelock *pl) +{ + // Given a page name, checks to see if it is locked. + // Returns 1 if locked, 0 if not. + + CFILE *infile; + mngs_Pagelock testlock; + int r,done=0; + + if (!Network_up) + return 1; + + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (infile==NULL) + { + sprintf (ErrorString,"There was a problem opening the table lock file for reading."); + return -1; + } + + while (!done) + { + r=mng_ReadPagelock (infile,&testlock); + if (r==0) + { + done=1; + r=2; + } + else + { + if (!stricmp (pl->name,testlock.name)) + { + int test=0; + if (pl->pagetype==testlock.pagetype) + { + if (!stricmp (testlock.holder,"UNLOCKED")) + r=0; + else + { + sprintf (InfoString,"User %s already has this page locked.",testlock.holder); + if (!stricmp (testlock.holder,TableUser)) + r=0; + else + r=1; + } + strcpy (pl->holder,testlock.holder); + done=1; + } + } + } + } + + cfclose (infile); + return r; +} + +int mng_CheckIfPageOwned (mngs_Pagelock *pl,char *owner) +{ + // Given a page name, checks to see if it is locked by owner. + // Returns 1 if locked by owner, 0 if not. + + CFILE *infile; + mngs_Pagelock testlock; + int r,done=0; + + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (infile==NULL) + { + sprintf (ErrorString,"There was a problem opening the table lock file for reading."); + Int3(); + return -1; + } + + while (!done) + { + r=mng_ReadPagelock (infile,&testlock); + if (r==0) + { + // We've reached the end of file, must be a new page and nobody owns it + done=1; + r=2; + } + else + { + if (!stricmp (pl->name,testlock.name) && pl->pagetype==testlock.pagetype) + { + if (!stricmp (testlock.holder,owner)) + r=1; + else + { + sprintf (InfoString,"User %s does not have this page locked.",owner); + r=0; + } + done=1; + } + } + } + + cfclose (infile); + return r; +} + + + +int mng_ReadPagelock (CFILE *fp,mngs_Pagelock *pl) +{ + // Reads a pagelock from the fp file. Returns 1 if successfully read, 0 if eof was encountered. + + char c; + int i; + + if (cfeof (fp)) + return 0; + + // Read type of page + pl->pagetype=cf_ReadByte(fp); + + //Convert old robot locks to generic locks + if (pl->pagetype == PAGETYPE_ROBOT) + pl->pagetype = PAGETYPE_GENERIC; + + // Read the name of this page + for (i=0;iname[i]=c; + } + + // Read the user who has this page locked. + for (i=0;iholder[i]=c; + } + + pl->name[PAGELOCK_NAME_LEN]=0; + pl->holder[PAGELOCK_NAME_LEN]=0; + + + if (pl->pagetype == PAGETYPE_POWERUP) + { + mprintf((0,"Powerup lock: %d, %s, %s\n",pl->pagetype,pl->name,pl->holder)); + } + + return 1; +} + +int mng_ReplacePagelock (char *name,mngs_Pagelock *pl) +{ + // Given a pagelock, replaces the one already inside the lock file, or if not present, adds it to + // the lock file. Returns 0 on error, or 1 if successful. + + CFILE *infile,*outfile; + int replaced=0; + int done=0; + + mngs_Pagelock temp_pl; + + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (!infile) + { + strcpy (ErrorString,"Couldn't open Table lock file!"); + Int3(); // Get Jason + return 0; + } + outfile=(CFILE *)cfopen (TempTableLockFilename,"wb"); + if (!outfile) + { + cfclose (infile); + strcpy (ErrorString,"Couldn't open temporary table lock file!"); + Int3(); // Get Jason + return 0; + } + + while (!done) + { + if (mng_ReadPagelock (infile,&temp_pl)) + { + if (!stricmp(temp_pl.name,name) && pl->pagetype==temp_pl.pagetype) + { + // This is the page we want to replace, so write the new one out. + mng_WritePagelock (outfile,pl); + replaced=1; + } + else + { + mng_WritePagelock (outfile,&temp_pl); + } + } + else + { + + if (!replaced) + { + // This is a new page, so append it to the end of file. + mng_WritePagelock (outfile,pl); + strcpy (InfoString,"Page was successfully added."); + } + else strcpy (InfoString,"Page was successfully replaced."); + done=1; + } + } + cfclose (infile); + cfclose (outfile); + + + if (!SwitcherooFiles (TableLockFilename,TempTableLockFilename)) + { + Int3(); + return 0; + } + + // Log this change + #ifndef RELEASE + char pathstr[255]; + ddio_MakePath (pathstr,NetD3Dir,"TableLog",NULL); + FILE *logfile = fopen(pathstr,"at"); + if (logfile) + { + char str[255]; + char date[255]; + time_t t; + t = time(NULL); + strftime(date,254,"[%a %m/%d/%y %H:%M:%S]",localtime(&t)); + sprintf (str,"%s Page %s (%s) last touched by %s\n",date,name,PageNames[pl->pagetype],TableUser); + fwrite(str,1,strlen(str),logfile); + fflush(logfile); + fclose (logfile); + } + #endif + + return 1; // successful! +} + +// Given a name and a pagetype, deletes the one already inside the lock file +int mng_DeletePagelock (char *name,int pagetype) +{ + CFILE *infile,*outfile; + int done=0,deleted=0; + mngs_Pagelock temp_pl; + + mprintf ((0,"Deleting pagelock %s.\n",name)); + + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (!infile) + { + strcpy (ErrorString,"Couldn't open Table lock file!"); + return 0; + } + outfile=(CFILE *)cfopen (TempTableLockFilename,"wb"); + if (!outfile) + { + cfclose (infile); + strcpy (ErrorString,"Couldn't open temporary table lock file!"); + return 0; + } + + while (!done) + { + if (mng_ReadPagelock (infile,&temp_pl)) + { + if (!stricmp(temp_pl.name,name) && pagetype==temp_pl.pagetype) + deleted=1; + else mng_WritePagelock (outfile,&temp_pl); + + } + else done=1; + } + cfclose (infile); + cfclose (outfile); + + if (!deleted) + { + Int3(); // Get Jason! + } + + if (remove (TableLockFilename)) + { + sprintf (ErrorString,"There was a problem deleting the temp file - errno %d",errno); + return (0); + } + if (rename (TempTableLockFilename,TableLockFilename)) + { + sprintf (ErrorString,"There was a problem renaming the temp file - errno %d",errno); + + return (0); + } + return 1; // successful! +} + +// Given a list of names and a pagetype, deletes the ones already inside the lock file +int mng_DeletePagelockSeries (char *names[],int num,int pagetype) +{ + CFILE *infile,*outfile; + int done=0,deleted=0; + mngs_Pagelock temp_pl; + + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (!infile) + { + strcpy (ErrorString,"Couldn't open Table lock file!"); + return 0; + } + outfile=(CFILE *)cfopen (TempTableLockFilename,"wb"); + if (!outfile) + { + cfclose (infile); + strcpy (ErrorString,"Couldn't open temporary table lock file!"); + return 0; + } + + while (!done) + { + if (mng_ReadPagelock (infile,&temp_pl)) + { + if (pagetype==temp_pl.pagetype) + { + int found=-1; + + for (int i=0;ipagetype); + + // Write name + for (i=0;iname[i]); + + // Who owns this page? + for (i=0;iholder[i]); + +} + +int mng_GetListOfLocks (mngs_Pagelock *pl,int max,char *who) +{ + // Given a list of pagelocks, a max number allowed, and a specified user "who", fills in the + // given pagelocks with all the pages that are locked by "who". + + CFILE *infile; + mngs_Pagelock temp_pl; + int done=0,num=0; + + infile=(CFILE *)cfopen (TableLockFilename,"rb"); + if (!infile) + { + strcpy (ErrorString,"Couldn't open Table lock file!"); + return -1; + } + + while (!done) + { + // Read in a page, compare it with the owner we're searching for. If found, make a copy of it. + + if (mng_ReadPagelock (infile,&temp_pl)) + { + if (((who == NULL) && stricmp(temp_pl.holder,"UNLOCKED")) || ((who != NULL) && (!stricmp (temp_pl.holder,who)))) + { + pl[num]=temp_pl; + num++; + if (num>=max) + done=1; + } + + } + else done=1; + } + + cfclose (infile); + return num; +} + +// Takes a pagelock and sets its holder name to UNLOCKED +void mng_OverrideToUnlocked (mngs_Pagelock *temp_pl) +{ + int r; +#ifndef RELEASE + r=mng_CheckIfPageOwned (temp_pl,TableUser); + + if (r==1) + { + OutrageMessageBox ("You own this page, so there is no use in you setting it to unlocked!"); + return; + } + + int answer=OutrageMessageBox (MBOX_YESNO, "Are you sure you wish to override this page to unlocked? (choose 'NO' if you don't know what you're doing!)"); + + if (answer==IDNO) + return; + + answer=OutrageMessageBox (MBOX_YESNO, "Do you wish to actually erase this pagelock? (Again, answer NO if you are not sure!)"); + + if (!mng_MakeLocker()) + return; + + strcpy (temp_pl->holder,"UNLOCKED"); + + if (answer==IDNO) + { + if (!mng_ReplacePagelock (temp_pl->name,temp_pl)) + OutrageMessageBox (ErrorString,"Error!"); + else + { + OutrageMessageBox ("Page set to unlocked."); + } + } + else + { + // Given a name and a pagetype, deletes the one already inside the lock file + if (!mng_DeletePagelock (temp_pl->name,temp_pl->pagetype)) + { + OutrageMessageBox (ErrorString,"Error!"); + } + else + OutrageMessageBox ("Pagelock deleted!"); + + } +#endif + mng_EraseLocker(); +} + + diff --git a/manage/powerpage.h b/manage/powerpage.h new file mode 100644 index 000000000..4a0d27ee3 --- /dev/null +++ b/manage/powerpage.h @@ -0,0 +1,65 @@ +#ifndef POWERPAGE_H +#define POWERPAGE_H + +#include "manage.h" +#include "cfile.h" +#include "pstypes.h" +#include "powerup.h" + +typedef struct +{ + powerup powerup_struct; + char image_name[PAGENAME_LEN]; + char sound_name[MAX_POWERUP_SOUNDS][PAGENAME_LEN]; +} mngs_power_page; + +// Powerup page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a power_page struct, writes that power page out +void mng_WritePowerPage (CFILE *outfile,mngs_power_page *powpage); + +// Reads a powerup page from an open file. Returns 0 on error. +int mng_ReadPowerPage (CFILE *infile,mngs_power_page *powpage); + + +// Given a power handle, searches the table file and replaces the powerup with the same name +// If local=1, then does it to the users local copy +// Returns 0 on error, else 1 if all is good +int mng_ReplacePowPage (char *name,int handle,int local); + +// Given a powerup name, finds it in the table file and deletes it +// If local is 1, deletes from the local table file +int mng_DeletePowPage (char *name,int local); + +// Reads in the powpage named "name" into powpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificPowPage (char *name,mngs_power_page *powpage,int offset=0); + +// Given a power page, allocs a powerup and calls AssignPowPageToPowerup to actually +// load bitmaps and values. Rturns powerup handle on success, -1 if fail +int mng_SetAndLoadPowerup (mngs_power_page *powpage); + +// Given a powpage and a powerup handle, attempts to make powerup n correspond to +// to the powpage. +// Returns 1 on success, 0 otherwise +int mng_AssignPowPageToPowerup(mngs_power_page *powpage,int n); + +// Copies values from a powerup into a power_page +void mng_AssignPowerupToPowPage(int n,mngs_power_page *powpage); + + +// Reads in a power page from the local table file, superseding any powerup +// already in RAM with that same name +void mng_LoadLocalPowerupPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetPowerupPage (CFILE *); + +// First searches through the powerup index to see if the powerup is already +// loaded. If not, searches in the table file and loads it. +// Returns index of powerup if found, -1 if not +int mng_GetGuaranteedPowerupPage (char *name,CFILE *infile=NULL); + + +#endif \ No newline at end of file diff --git a/manage/robotpage.h b/manage/robotpage.h new file mode 100644 index 000000000..8e8c06727 --- /dev/null +++ b/manage/robotpage.h @@ -0,0 +1,59 @@ +#ifndef ROBOTPAGE_H +#define ROBOTPAGE_H + +#include "manage.h" +#include "robot.h" +#include "cfile.h" +#include "pstypes.h" +#include "objinfo.h" + +typedef struct +{ + object_info robot_struct; + char image_name[PAGENAME_LEN]; +} mngs_robot_page; + +// Robot page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a robot_page struct, writes that robotpage out +void mng_WriteRobotPage (CFILE *outfile,mngs_robot_page *robotpage); + +// Reads a robot page from an open file. Returns 0 on error. +int mng_ReadRobotPage (CFILE *infile,mngs_robot_page *robotpage); + + +// Given a robot handle, searches the table file and replaces the robot with the same name +// If local=1, then does it to the users local copy +// Returns 0 on error, else 1 if all is good +int mng_ReplaceRobotPage (char *name,int handle,int local); + +// Given a robot name, finds it in the table file and deletes it +// If local is 1, deletes from the local table file +int mng_DeleteRobotPage (char *name,int local); + +// Reads in the robotpage named "name" into robotpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificRobotPage (char *name,mngs_robot_page *robotpage); + +// Given a robot page, allocs a robot and calls AssignRobotPageToRobot to actually +// load model and values. Rturns robot handle on success, -1 if fail +int mng_SetAndLoadRobot (mngs_robot_page *robotpage); + +// Given a robotpage and a robot handle, attempts to make robot n correspond to +// to the robotpage. +// Returns 1 on success, 0 otherwise +int mng_AssignRobotPageToRobot(mngs_robot_page *robotpage,int n); + +// Copies values from a Robot into a robot_page +void mng_AssignRobotToRobotPage(int n,mngs_robot_page *robotpage); + + +// Reads in a robot page from the local table file, superseding any robot +// already in RAM with that same name +void mng_LoadLocalRobotPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetRobotPage (CFILE *); + +#endif \ No newline at end of file diff --git a/manage/shippage.cpp b/manage/shippage.cpp new file mode 100644 index 000000000..cf9cad80d --- /dev/null +++ b/manage/shippage.cpp @@ -0,0 +1,1409 @@ +/* + * $Logfile: /DescentIII/Main/manage/shippage.cpp $ + * $Revision: 46 $ + * $Date: 10/22/01 12:41p $ + * $Author: Matt $ + * + * $Log: /DescentIII/Main/manage/shippage.cpp $ + * + * 46 10/22/01 12:41p Matt + * After loading a ship model to check for errors, unload it so it can get + * loaded again later after the textures have been read in. + * + * 45 10/08/01 4:20p Matt + * Added system to check for errors when reading in add-on data. + * + * 44 9/06/01 10:32a Matt + * Added code to fix problem poping add-on pages when the original pages + * were in the extra.gam file. + * + * 43 4/19/00 5:07p Matt + * From Duane for 1.4 + * Added checks, asserts, and fixes for bad return values + * + * 42 10/26/99 3:30p Jeff + * handle extra.gam addon tablefile + * + * 41 10/20/99 6:27p Jeff + * sped up addon page popping (by saving page offsets) + * + * 40 10/20/99 1:30p Jeff + * fixed fusion recharge hack + * + * 39 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 38 7/27/99 1:14p Jason + * fixed fusion firing time + * + * 37 5/14/99 11:56a Jeff + * fixed copy/paste bug (Jason) + * + * 36 4/14/99 1:33a Jeff + * fixed case mismatched #includes + * + * 35 3/04/99 4:47p Jason + * temp fix (ie BAD HACK) for OEM table file woes + * + * 34 1/13/99 7:05a Jeff + * put #ifdef around #include + * + * 33 12/29/98 4:30p Jason + * added add-on data functionality + * + * 32 11/13/98 12:30p Jason + * changes for weapons + * + * 31 11/06/98 12:35p Jason + * more speedups for manage system + * + * 30 11/05/98 7:55p Jason + * changes for new manage system + * + * 29 11/03/98 6:16p Chris + * Starting to make on/off and spray weapons accessable to robots + * + * 28 11/02/98 6:02p Jason + * made yes network updates much faster + * + * 27 10/13/98 12:56p Matt + * Finished (hopefully) with the ammo system. Added support for napalm + * fuel. + * + * 26 10/09/98 2:27p Jason + * reorganized table file system + * + * 25 10/02/98 1:47p Jason + * added lod player ships + * + * 24 10/01/98 11:51a Matt + * When loading or saving the player ship page, check for spew powerups + * that aren't powerups & fix them. + * + * 23 9/21/98 8:19p Chris + * Improved volatile and forcefield hits + * + * 22 9/10/98 11:53a Jason + * added hud config filename to ship page + * + * 21 8/24/98 2:37p Jason + * made table file more efficient with regards to invalid names + * + * 20 8/14/98 5:25p Jeff + * fixed stupid bug setting wrong varible (nothing dangerous) + * + * 19 8/14/98 4:56p Jeff + * added quad fire mask for weapon battery + * + * 18 8/07/98 2:06p Jason + * now saves flags field + * + * 17 7/31/98 5:23p Jason + * added ship armor scalars + * + * 16 6/01/98 10:37a Matt + * Added system to spew powerups when the player dies. Still needs Jason + * to deal with mng_FindSpecificGenericPage(). + * + * 15 5/04/98 6:08p Jason + * sped up predictive pagefile loading + * + * 14 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 13 3/19/98 1:07p Jason + * added cockpit name + * + * 12 2/23/98 1:22p Jason + * made FindSpecific* read from the local drive, not the net drive - when + * starting the editor + * + * 11 2/17/98 8:12p Matt + * Added looping & release sounds for player weapons + * + * 10 1/23/98 6:25p Jason + * Got spray weapons working + * + * 9 1/15/98 6:22p Jason + * added safety checks so the network won't copy over a primitive you have + * held locally + * + * 8 12/22/97 6:19p Chris + * Moved weapon battery firing sound off the projectile (weapon) and into + * the weapon battery. + * + * 7 11/12/97 5:35p Jason + * made energy/ammo usage work with weapon batteries + * + * 6 11/12/97 1:13p Jason + * added weapons that can ramp up + * + * 5 11/05/97 12:22p Chris + * Fixed size of wb_info in the page file + * + * 4 11/05/97 10:45a Chris + * Now init's the ship struct to zero (and fills in some values) before + * reading. + * + * 3 11/04/97 6:18p Chris + * Added support so that the player ship uses the robot's firing dialog. + * + * 2 10/21/97 11:57a Jason + * added ability to set dying model for player ship + * + * 8 5/08/97 12:41p Jason + * made manage system work with device dependant path names + * + * 7 4/25/97 6:16p Jason + * added switcheroo function + * + * 6 4/09/97 9:57p Chris + * Added more ability to customize ships/objects. + * + * 5 4/08/97 1:16a Chris + * Fixed the bug with initial values for some items being random. It also + * fixed a intermitent bug that was caused because the used flag was + * random... we wanted it to be 1 (non-zero), but it was random so there + * was a chance it could be zero(1 in 2^(sizeof(used_flag))) + * + * 4 4/07/97 3:13p Chris + * + * 3 4/04/97 2:44p Matt + * Changed object pages to store physics info in a physics_info structure. + * + * 2 3/31/97 4:34p Jason + * added player ship page + * + * 1 3/31/97 12:10p Jason + * Page file for player ship management + * + * $NoKeywords: $ + */ +#if defined(WIN32) +#include +#endif + +#include "CFILE.H" +#include "manage.h" +#include "ship.h" +#include "shippage.h" +#include "mono.h" +#include "pserror.h" +#include "polymodel.h" +#include "ddio.h" +#include "robotfire.h" +#include "weaponpage.h" +#include +#include "soundload.h" +#include "sounds.h" +#include "soundpage.h" +#include "genericpage.h" +#include "args.h" + +// shippage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define SHIPPAGE_COMMAND_NAME 1 +#define SHIPPAGE_COMMAND_END 2 +#define SHIPPAGE_COMMAND_IMAGE_NAME 3 +#define SHIPPAGE_COMMAND_VERSION 4 +#define SHIPPAGE_COMMAND_MASS 5 +#define SHIPPAGE_COMMAND_DRAG 6 +#define SHIPPAGE_COMMAND_SIZE 7 +#define SHIPPAGE_COMMAND_WIGGLE_AMPLITUDE 8 +#define SHIPPAGE_COMMAND_THRUST 9 +#define SHIPPAGE_COMMAND_ROTTHRUST 10 +#define SHIPPAGE_COMMAND_TURNROLLRATE 11 +#define SHIPPAGE_COMMAND_ROTDRAG 12 +#define SHIPPAGE_COMMAND_PHYSICS_FLAGS 13 +#define SHIPPAGE_COMMAND_WIGGLE_PER_SEC 14 +#define SHIPPAGE_COMMAND_TURNROLL_RATIO 15 +#define SHIPPAGE_COMMAND_DYING_IMAGE_NAME 16 +#define SHIPPAGE_COMMAND_WB_INFO 17 +#define SHIPPAGE_COMMAND_WB_WEAPON 18 +#define SHIPPAGE_COMMAND_FIRE_FLAGS 19 +#define SHIPPAGE_COMMAND_RESOURCE_USAGE 20 +#define SHIPPAGE_COMMAND_WB_FIRE_SOUND 21 +#define SHIPPAGE_COMMAND_WB_FLAGS 22 +#define SHIPPAGE_COMMAND_FIRING_SOUND 23 +#define SHIPPAGE_COMMAND_RELEASE_SOUND 24 +#define SHIPPAGE_COMMAND_COCKPIT_NAME 25 +#define SHIPPAGE_COMMAND_SPEW_POWERUP 26 +#define SHIPPAGE_COMMAND_ARMOR_SCALAR 27 +#define SHIPPAGE_COMMAND_FLAGS 28 +#define SHIPPAGE_COMMAND_WB_QUADMASK 29 +#define SHIPPAGE_COMMAND_HUD_NAME 30 +#define SHIPPAGE_COMMAND_LOD_MODELS 31 +#define SHIPPAGE_COMMAND_LOD_DISTANCE 32 +#define SHIPPAGE_COMMAND_MAX_AMMO 33 + +#define SHIPPAGE_VERSION 6 + +extern char *TablefileNameOverride; + +// Given an open file pointer and a ship_page struct, writes that ship page out +void mng_WriteShipPage (CFILE *outfile,mngs_ship_page *shippage) +{ + int i; + int j; + int size; + + ASSERT (outfile!=NULL); + ASSERT (shippage!=NULL); + + cf_WriteByte (outfile,PAGETYPE_SHIP); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_VERSION); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,SHIPPAGE_VERSION); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_NAME); // write out ship name + cf_WriteByte (outfile,PAGENAME_LEN); + for (i=0;iship_struct.name[i]); + + // Write out cockpit name + cf_WriteByte (outfile,SHIPPAGE_COMMAND_COCKPIT_NAME); // write out ship name + cf_WriteByte (outfile,strlen(shippage->ship_struct.cockpit_name)+1); + cf_WriteString (outfile,shippage->ship_struct.cockpit_name); + + // Write out hud config name + cf_WriteByte (outfile,SHIPPAGE_COMMAND_HUD_NAME); // write out ship name + cf_WriteByte (outfile,strlen(shippage->ship_struct.hud_config_name)+1); + cf_WriteString (outfile,shippage->ship_struct.hud_config_name); + + // Write out its models name + cf_WriteByte (outfile,SHIPPAGE_COMMAND_IMAGE_NAME); // get ready to write out name + cf_WriteByte (outfile,PAGENAME_LEN); + for (i=0;iimage_name[i]); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_DYING_IMAGE_NAME); // get ready to write out name + cf_WriteByte (outfile,strlen(shippage->dying_image_name)+1); + cf_WriteString (outfile,shippage->dying_image_name); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_MASS); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.mass); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_DRAG); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.drag); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_SIZE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.size); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_WIGGLE_AMPLITUDE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.wiggle_amplitude); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_THRUST); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.full_thrust); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_ROTTHRUST); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.full_rotthrust); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_TURNROLLRATE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.max_turnroll_rate); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_ROTDRAG); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.rotdrag); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_WIGGLE_PER_SEC); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.wiggles_per_sec); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_TURNROLL_RATIO); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.phys_info.turnroll_ratio); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_PHYSICS_FLAGS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,shippage->ship_struct.phys_info.flags); + + // Write the LOD model names + cf_WriteByte (outfile,SHIPPAGE_COMMAND_LOD_MODELS); + size=strlen(shippage->med_image_name)+1; + size+=strlen(shippage->lo_image_name)+1; + + cf_WriteByte (outfile,size); + cf_WriteString (outfile,shippage->med_image_name); + cf_WriteString (outfile,shippage->lo_image_name); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_LOD_DISTANCE); + cf_WriteByte (outfile,8); + cf_WriteFloat (outfile,shippage->ship_struct.med_lod_distance); + cf_WriteFloat (outfile,shippage->ship_struct.lo_lod_distance); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_ARMOR_SCALAR); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,shippage->ship_struct.armor_scalar); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_FLAGS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,shippage->ship_struct.flags); + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + int j; + int size; + + cf_WriteByte (outfile, SHIPPAGE_COMMAND_WB_INFO); + size = sizeof(otype_wb_info); + size++; // add a byte for the wb index + + cf_WriteByte (outfile,size); + + cf_WriteByte (outfile,i); + for(j = 0; j < MAX_WB_GUNPOINTS; j++) cf_WriteShort(outfile, shippage->ship_struct.static_wb[i].gp_weapon_index[j]); + cf_WriteShort(outfile, shippage->ship_struct.static_wb[i].aiming_gp_index); + + cf_WriteByte(outfile, shippage->ship_struct.static_wb[i].num_masks); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) cf_WriteByte(outfile, shippage->ship_struct.static_wb[i].gp_fire_masks[j]); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) cf_WriteFloat(outfile, shippage->ship_struct.static_wb[i].gp_fire_wait[j]); + + cf_WriteByte(outfile, shippage->ship_struct.static_wb[i].aiming_flags); + cf_WriteFloat(outfile, shippage->ship_struct.static_wb[i].aiming_3d_dot); + cf_WriteFloat(outfile, shippage->ship_struct.static_wb[i].aiming_3d_dist); + cf_WriteFloat(outfile, shippage->ship_struct.static_wb[i].aiming_XZ_dot); + } + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_WB_FLAGS); + cf_WriteByte (outfile,MAX_PLAYER_WEAPONS); + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + cf_WriteByte(outfile, shippage->ship_struct.static_wb[i].flags); + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_WB_QUADMASK); + cf_WriteByte (outfile,MAX_PLAYER_WEAPONS); + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + cf_WriteByte(outfile, shippage->ship_struct.static_wb[i].gp_quad_fire_mask); + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + cf_WriteByte (outfile, SHIPPAGE_COMMAND_WB_WEAPON); + size = strlen(shippage->weapon_name[i][j])+1+2; // 1 for the null charactor and 2 for the 2 indices + + cf_WriteByte (outfile,size); + + cf_WriteByte (outfile,i); + cf_WriteByte (outfile,j); + cf_WriteString (outfile,shippage->weapon_name[i][j]); + } + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + cf_WriteByte (outfile, SHIPPAGE_COMMAND_WB_FIRE_SOUND); + size = strlen(shippage->fire_sound_name[i][j])+1+2; // 1 for the null charactor and 2 for the 2 indices + + cf_WriteByte (outfile,size); + + cf_WriteByte (outfile,i); + cf_WriteByte (outfile,j); + cf_WriteString (outfile,shippage->fire_sound_name[i][j]); + } + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + cf_WriteByte (outfile, SHIPPAGE_COMMAND_FIRE_FLAGS); + cf_WriteByte (outfile,2); + + cf_WriteByte (outfile,i); + cf_WriteByte (outfile,shippage->ship_struct.fire_flags[i]); + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + cf_WriteByte (outfile, SHIPPAGE_COMMAND_RESOURCE_USAGE); + cf_WriteByte (outfile,9); + + cf_WriteByte (outfile,i); + cf_WriteFloat(outfile, shippage->ship_struct.static_wb[i].energy_usage); + cf_WriteFloat(outfile, shippage->ship_struct.static_wb[i].ammo_usage); + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + if (shippage->firing_sound_name[i][0] != 0) { + cf_WriteByte (outfile, SHIPPAGE_COMMAND_FIRING_SOUND); + size = strlen(shippage->firing_sound_name[i])+1+1; // 1 for the null charactor and 1 for the index + cf_WriteByte (outfile,size); + cf_WriteByte (outfile,i); + cf_WriteString (outfile,shippage->firing_sound_name[i]); + } + } + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) { + if (shippage->release_sound_name[i][0] != 0) { + cf_WriteByte (outfile, SHIPPAGE_COMMAND_RELEASE_SOUND); + size = strlen(shippage->release_sound_name[i])+1+1; // 1 for the null charactor and 1 for the index + cf_WriteByte (outfile,size); + cf_WriteByte (outfile,i); + cf_WriteString (outfile,shippage->release_sound_name[i]); + } + } + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) { + if (shippage->spew_powerup_name[i][0] != 0) { + cf_WriteByte (outfile, SHIPPAGE_COMMAND_SPEW_POWERUP); + size = strlen(shippage->spew_powerup_name[i])+1+1; // 1 for the null charactor and 1 for the index + cf_WriteByte (outfile,size); + cf_WriteByte (outfile,i); + cf_WriteString (outfile,shippage->spew_powerup_name[i]); + } + } + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) { + cf_WriteByte(outfile, SHIPPAGE_COMMAND_MAX_AMMO); + cf_WriteByte (outfile,5); + + cf_WriteByte (outfile,i); + cf_WriteInt(outfile,shippage->ship_struct.max_ammo[i]); + } + + cf_WriteByte (outfile,SHIPPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} + +// Given an open file pointer and a ship_page struct, writes that ship page out +void mng_WriteNewShipPage (CFILE *outfile,mngs_ship_page *shippage) +{ + int i; + + ASSERT (outfile!=NULL); + ASSERT (shippage!=NULL); + + int offset=StartManagePage(outfile,PAGETYPE_SHIP); + + cf_WriteShort (outfile,SHIPPAGE_VERSION); + + + cf_WriteString (outfile,shippage->ship_struct.name); + + cf_WriteString (outfile,shippage->ship_struct.cockpit_name); + + cf_WriteString (outfile,shippage->ship_struct.hud_config_name); + + // Write out its models name + cf_WriteString (outfile,shippage->image_name); + cf_WriteString (outfile,shippage->dying_image_name); + cf_WriteString (outfile,shippage->med_image_name); + cf_WriteString (outfile,shippage->lo_image_name); + + // Write out lod distance + cf_WriteFloat (outfile,shippage->ship_struct.med_lod_distance); + cf_WriteFloat (outfile,shippage->ship_struct.lo_lod_distance); + + mng_WritePhysicsChunk (&shippage->ship_struct.phys_info,outfile); + + cf_WriteFloat (outfile,shippage->ship_struct.size); + cf_WriteFloat (outfile,shippage->ship_struct.armor_scalar); + cf_WriteInt (outfile,shippage->ship_struct.flags); + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + int j; + + cf_WriteByte (outfile,shippage->ship_struct.fire_flags[i]); + cf_WriteString (outfile,shippage->firing_sound_name[i]); + cf_WriteString (outfile,shippage->release_sound_name[i]); + cf_WriteString (outfile,shippage->spew_powerup_name[i]); + cf_WriteInt(outfile,shippage->ship_struct.max_ammo[i]); + + mng_WriteWeaponBatteryChunk (&shippage->ship_struct.static_wb[i],outfile); + + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + cf_WriteString (outfile,shippage->fire_sound_name[i][j]); + + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + cf_WriteString (outfile,shippage->weapon_name[i][j]); + } + + EndManagePage (outfile,offset); +} + +// Reads a ship page from an open file. Returns 0 on error. +int mng_ReadNewShipPage (CFILE *infile,mngs_ship_page *shippage) +{ + int i,j; + + ASSERT (infile!=NULL); + + // Defaults + memset(shippage, 0, sizeof(mngs_ship_page)); + + int version=cf_ReadShort(infile); + + // Read In misc names + cf_ReadString (shippage->ship_struct.name,PAGENAME_LEN,infile); + cf_ReadString (shippage->ship_struct.cockpit_name,PAGENAME_LEN,infile); + cf_ReadString (shippage->ship_struct.hud_config_name,PAGENAME_LEN,infile); + + // Read in model names + cf_ReadString (shippage->image_name,PAGENAME_LEN,infile); + cf_ReadString (shippage->dying_image_name,PAGENAME_LEN,infile); + cf_ReadString (shippage->med_image_name,PAGENAME_LEN,infile); + cf_ReadString (shippage->lo_image_name,PAGENAME_LEN,infile); + + // read lod distance + shippage->ship_struct.med_lod_distance=cf_ReadFloat (infile); + shippage->ship_struct.lo_lod_distance=cf_ReadFloat (infile); + + // Read physics + mng_ReadPhysicsChunk (&shippage->ship_struct.phys_info,infile); + + shippage->ship_struct.size=cf_ReadFloat (infile); + shippage->ship_struct.armor_scalar=cf_ReadFloat (infile); + shippage->ship_struct.flags=cf_ReadInt (infile); + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + shippage->ship_struct.fire_flags[i]=cf_ReadByte (infile); + cf_ReadString (shippage->firing_sound_name[i],PAGENAME_LEN,infile); + cf_ReadString (shippage->release_sound_name[i],PAGENAME_LEN,infile); + cf_ReadString (shippage->spew_powerup_name[i],PAGENAME_LEN,infile); + shippage->ship_struct.max_ammo[i]=cf_ReadInt (infile); + + + if (version>=6) + mng_ReadWeaponBatteryChunk (&shippage->ship_struct.static_wb[i],infile,2); + else + mng_ReadWeaponBatteryChunk (&shippage->ship_struct.static_wb[i],infile,1); + + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + cf_ReadString (shippage->fire_sound_name[i][j],PAGENAME_LEN,infile); + + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + cf_ReadString (shippage->weapon_name[i][j],PAGENAME_LEN,infile); + } + + // Mark the newly filled structure as used + shippage->ship_struct.used=1; + + + // Bash Fusion recharge times for the ships + if(!stricmp(shippage->ship_struct.name,"Pyro-GL")) + { + // Pyro-GL + for (j=0;jship_struct.static_wb[FUSION_INDEX].gp_fire_wait[j] = 0.66f; + }else if(!stricmp(shippage->ship_struct.name,"Phoenix")) + { + // Phoenix + for (j=0;jship_struct.static_wb[FUSION_INDEX].gp_fire_wait[j] = 0.792f; + }else if(!stricmp(shippage->ship_struct.name,"Magnum-AHT")) + { + // Magnum + for (j=0;jship_struct.static_wb[FUSION_INDEX].gp_fire_wait[j] = 1.122f; + }else + { + // Other + } + + return 1; // successfully read +} + +// Reads a ship page from an open file. Returns 0 on error. +int mng_ReadShipPage (CFILE *infile,mngs_ship_page *shippage) +{ + int done=0; + char command; + ubyte len; + int i; + int version = 0; + + if (!Old_table_method) + return mng_ReadNewShipPage (infile,shippage); + + strcpy (shippage->dying_image_name,""); + ASSERT (infile!=NULL); + + // Defaults + memset(shippage, 0, sizeof(mngs_ship_page)); + shippage->ship_struct.armor_scalar=1.0; + + strcpy (shippage->med_image_name,""); + strcpy (shippage->lo_image_name,""); + + shippage->ship_struct.med_lod_distance=DEFAULT_MED_LOD_DISTANCE; + shippage->ship_struct.lo_lod_distance=DEFAULT_LO_LOD_DISTANCE; + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + shippage->ship_struct.static_wb[i].aiming_3d_dist = 1.0; + shippage->ship_struct.static_wb[i].num_masks = 1; + shippage->ship_struct.fire_flags[i]=0; + shippage->firing_sound_name[i][0] = 0; + shippage->release_sound_name[i][0] = 0; + shippage->spew_powerup_name[i][0] = 0; + shippage->ship_struct.max_ammo[i] = 0; + } + + while (!done) + { + // Read in command byte then read in the length of that commands data + + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + + switch (command) + { + case SHIPPAGE_COMMAND_END: + done=1; + break; + case SHIPPAGE_COMMAND_VERSION: + version = cf_ReadByte(infile); + break; + case SHIPPAGE_COMMAND_IMAGE_NAME: // the name of the ship model + for (i=0;iimage_name[i]=cf_ReadByte (infile); + break; + case SHIPPAGE_COMMAND_DYING_IMAGE_NAME: + cf_ReadString(shippage->dying_image_name,PAGENAME_LEN,infile); + break; + case SHIPPAGE_COMMAND_LOD_MODELS: // the name of the lower res models + cf_ReadString (shippage->med_image_name,len+1,infile); + cf_ReadString (shippage->lo_image_name,len+1,infile); + break; + case SHIPPAGE_COMMAND_LOD_DISTANCE: + shippage->ship_struct.med_lod_distance=cf_ReadFloat (infile); + shippage->ship_struct.lo_lod_distance=cf_ReadFloat (infile); + break; + case SHIPPAGE_COMMAND_COCKPIT_NAME: + cf_ReadString(shippage->ship_struct.cockpit_name,PAGENAME_LEN,infile); + break; + case SHIPPAGE_COMMAND_HUD_NAME: + cf_ReadString(shippage->ship_struct.hud_config_name,PAGENAME_LEN,infile); + break; + case SHIPPAGE_COMMAND_NAME: + for (i=0;iship_struct.name[i]=cf_ReadByte (infile); + break; + case SHIPPAGE_COMMAND_SIZE: + shippage->ship_struct.size=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_ARMOR_SCALAR: + shippage->ship_struct.armor_scalar=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_MASS: + shippage->ship_struct.phys_info.mass=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_DRAG: + shippage->ship_struct.phys_info.drag=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_WIGGLE_AMPLITUDE: + shippage->ship_struct.phys_info.wiggle_amplitude=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_THRUST: + shippage->ship_struct.phys_info.full_thrust=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_ROTTHRUST: + shippage->ship_struct.phys_info.full_rotthrust=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_TURNROLLRATE: + shippage->ship_struct.phys_info.max_turnroll_rate=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_ROTDRAG: + shippage->ship_struct.phys_info.rotdrag=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_WIGGLE_PER_SEC: + shippage->ship_struct.phys_info.wiggles_per_sec=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_TURNROLL_RATIO: + shippage->ship_struct.phys_info.turnroll_ratio=cf_ReadFloat(infile); + break; + case SHIPPAGE_COMMAND_PHYSICS_FLAGS: + shippage->ship_struct.phys_info.flags=cf_ReadInt(infile); + break; + case SHIPPAGE_COMMAND_FLAGS: + shippage->ship_struct.flags=cf_ReadInt(infile); + break; + case SHIPPAGE_COMMAND_WB_FLAGS: + { + for (int i=0;iship_struct.static_wb[i].flags=cf_ReadByte (infile); + } + break; + case SHIPPAGE_COMMAND_WB_QUADMASK: + { + for(int i=0;iship_struct.static_wb[i].gp_quad_fire_mask=cf_ReadByte (infile); + } + break; + case SHIPPAGE_COMMAND_WB_INFO: + { + int i,j; + i = cf_ReadByte(infile); + + if(version < 1) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) shippage->ship_struct.static_wb[i].gp_weapon_index[j] = cf_ReadInt(infile); + shippage->ship_struct.static_wb[i].aiming_gp_index = cf_ReadInt(infile); + } + else + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) shippage->ship_struct.static_wb[i].gp_weapon_index[j] = cf_ReadShort(infile); + shippage->ship_struct.static_wb[i].aiming_gp_index = cf_ReadShort(infile); + } + shippage->ship_struct.static_wb[i].num_masks = cf_ReadByte(infile); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) shippage->ship_struct.static_wb[i].gp_fire_masks[j] = cf_ReadByte(infile); + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) shippage->ship_struct.static_wb[i].gp_fire_wait[j] = cf_ReadFloat(infile); + + shippage->ship_struct.static_wb[i].aiming_flags = cf_ReadByte(infile); + shippage->ship_struct.static_wb[i].aiming_3d_dot = cf_ReadFloat(infile); + shippage->ship_struct.static_wb[i].aiming_3d_dist = cf_ReadFloat(infile); + shippage->ship_struct.static_wb[i].aiming_XZ_dot = cf_ReadFloat(infile); + break; + } + + case SHIPPAGE_COMMAND_WB_WEAPON: + { + int i,j; + + i = cf_ReadByte (infile); + j = cf_ReadByte (infile); + cf_ReadString (shippage->weapon_name[i][j],len-1,infile); + break; + } + case SHIPPAGE_COMMAND_WB_FIRE_SOUND: + { + int i,j; + + i = cf_ReadByte (infile); + j = cf_ReadByte (infile); + cf_ReadString (shippage->fire_sound_name[i][j],len-1,infile); + break; + } + case SHIPPAGE_COMMAND_FIRING_SOUND: + { + int i = cf_ReadByte (infile); + cf_ReadString (shippage->firing_sound_name[i],len,infile); + break; + } + case SHIPPAGE_COMMAND_RELEASE_SOUND: + { + int i = cf_ReadByte (infile); + cf_ReadString (shippage->release_sound_name[i],len,infile); + break; + } + case SHIPPAGE_COMMAND_SPEW_POWERUP: + { + int i = cf_ReadByte (infile); + cf_ReadString(shippage->spew_powerup_name[i],len,infile); + break; + } + case SHIPPAGE_COMMAND_FIRE_FLAGS: + { + ubyte slot; + + slot=cf_ReadByte (infile); + shippage->ship_struct.fire_flags[slot]=cf_ReadByte (infile); + break; + } + case SHIPPAGE_COMMAND_MAX_AMMO: + { + ubyte slot; + + slot=cf_ReadByte (infile); + shippage->ship_struct.max_ammo[slot]=cf_ReadInt(infile); + break; + } + case SHIPPAGE_COMMAND_RESOURCE_USAGE: + { + ubyte slot; + + slot=cf_ReadByte (infile); + shippage->ship_struct.static_wb[slot].energy_usage=cf_ReadFloat (infile); + shippage->ship_struct.static_wb[slot].ammo_usage=cf_ReadFloat (infile); + break; + } + default: + // Ignore the ones we don't know + for (i=0;iship_struct.fire_flags[xxx] & 2)) + { + shippage->ship_struct.static_wb[xxx].flags |= WBF_ON_OFF; + } + } + + shippage->ship_struct.phys_info.num_bounces = -1; + shippage->ship_struct.phys_info.coeff_restitution = 1.0f; + + // Mark the newly filled structure as used + shippage->ship_struct.used=1; + + return 1; // successfully read +} + +// Reads in the ship named "name" into shippage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificShipPage (char *name,mngs_ship_page *shippage,int offset) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + int first_try=1; + char tablename[TABLE_NAME_LEN]; + + if (Loading_locals) + { + infile=cfopen (LocalTableFilename,"rb"); + } + else if (Loading_addon_table!=-1) + { + infile=cfopen (AddOnDataTables[Loading_addon_table].AddOnTableFilename,"rb"); + } + else + { + if (Network_up && Starting_editor) + { + int farg=FindArg("-filter"); + + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + + infile=cfopen (tablename,"rb"); + } + else + { + infile = NULL; + if(TablefileNameOverride) + { + infile=cfopen(TablefileNameOverride,"rb"); + } + + if(!infile) + infile=cfopen (TableFilename,"rb"); + } + } + + if (!infile) + { + mprintf ((0,"Couldn't open table file to find ship!\n")); + Int3(); + return 0; + } + +try_again:; + + if (offset) + cfseek (infile,offset,SEEK_SET); + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + + // If not a ship page, just read it in and ignore it + if (pagetype!=PAGETYPE_SHIP) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + + mng_ReadNewShipPage (infile,shippage); + + if (!stricmp(name,shippage->ship_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + + cfclose (infile); + + if (!found && first_try) { + done = first_try = 0; + infile=cfopen ("extra.gam","rb"); + if (infile) + goto try_again; + } + + return found; // successful! +} + +// Given a ship page, allocs a ship and calls AssignShipPageToShip to actually +// load models and values. Rturns ship handle on success, -1 if fail +int mng_SetAndLoadShip (mngs_ship_page *shippage,CFILE *infile) +{ + int n; + + n=AllocShip(); + if (n<0) + return -1; + if (!mng_AssignShipPageToShip(shippage,n,infile)) + return -1; + + return n; +} + +// Given a shippage and a ship handle, attempts to make ship n correspond to +// to the shippage. +// Returns 1 on success, 0 otherwise +int mng_AssignShipPageToShip(mngs_ship_page *shippage,int n,CFILE *infile) +{ + ship *shippointer=&Ships[n]; + int img_handle; + int i,j; + + // copy our values + memcpy (shippointer,&shippage->ship_struct,sizeof(ship)); + strcpy (shippointer->name,shippage->ship_struct.name); + + + // First see if our image differs from the one on the net + // If it is, make a copy + // If its a release version, don't do any of this + + #ifndef RELEASE + if (Network_up) + { + char str[200]; + char netstr[200]; + + ddio_MakePath (str,LocalModelsDir,shippage->image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,shippage->image_name,NULL); + + UpdatePrimitive (str,netstr,shippage->image_name,PAGETYPE_SHIP,shippointer->name); + + if (stricmp ("INVALID IMAGE NAME",shippage->dying_image_name) && shippage->dying_image_name[0]!=0) + { + ddio_MakePath (str,LocalModelsDir,shippage->dying_image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,shippage->dying_image_name,NULL); + + UpdatePrimitive (str,netstr,shippage->dying_image_name,PAGETYPE_SHIP,shippointer->name); + } + + if (shippage->med_image_name[0]!=0) + { + ddio_MakePath (str,LocalModelsDir,shippage->med_image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,shippage->med_image_name,NULL); + + UpdatePrimitive (str,netstr,shippage->med_image_name,PAGETYPE_SHIP,shippointer->name); + } + + if (shippage->lo_image_name[0]!=0) + { + ddio_MakePath (str,LocalModelsDir,shippage->lo_image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,shippage->lo_image_name,NULL); + + UpdatePrimitive (str,netstr,shippage->lo_image_name,PAGETYPE_SHIP,shippointer->name); + } + } + #endif + + // Try and load our ship model from the disk + + img_handle=LoadPolyModel (shippage->image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignShipPage...\n",shippage->image_name)); + shippointer->model_handle=-1; + return 0; + } + else + shippointer->model_handle=img_handle; + + if (stricmp ("INVALID IMAGE NAME",shippage->dying_image_name) && shippage->dying_image_name[0]!=0) + { + img_handle=LoadPolyModel (shippage->dying_image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignShipPage...\n",shippage->dying_image_name)); + shippointer->dying_model_handle=-1; + return 0; + } + else + shippointer->dying_model_handle=img_handle; + } + else + shippointer->dying_model_handle=-1; + + // Load lo-res stuff + if (shippage->med_image_name[0]!=0) + { + img_handle=LoadPolyModel (shippage->med_image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignShipPage...\n",shippage->med_image_name)); + shippointer->med_render_handle=-1; + return 0; + } + else + shippointer->med_render_handle=img_handle; + } + else + shippointer->med_render_handle=-1; + + if (shippage->lo_image_name[0]!=0) + { + img_handle=LoadPolyModel (shippage->lo_image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignGenericPage...\n",shippage->lo_image_name)); + shippointer->lo_render_handle=-1; + return 0; + } + else + shippointer->lo_render_handle=img_handle; + } + else + shippointer->lo_render_handle=-1; + + // Try and load the various weapons + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if(shippage->weapon_name[i][j][0] != '\0') + { + int weapon_handle = mng_GetGuaranteedWeaponPage (shippage->weapon_name[i][j],infile); + + if (weapon_handle < 0) + { + mprintf ((0,"Couldn't load weapon file '%s' in AssignPowPage %s...\n",shippage->weapon_name[i][j],shippage->ship_struct.name)); + shippointer->static_wb[i].gp_weapon_index[j] = LASER_INDEX; + } + else + shippointer->static_wb[i].gp_weapon_index[j] = weapon_handle; + } + else + shippointer->static_wb[i].gp_weapon_index[j] = LASER_INDEX; + } + } + + // Try and load the various weapons + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if(shippage->fire_sound_name[i][j][0] != '\0') + { + int fire_sound_handle = mng_GetGuaranteedSoundPage (shippage->fire_sound_name[i][j],infile); + + if (fire_sound_handle < 0) + { + mprintf ((0,"Couldn't load fire_sound file '%s' in AssignPowPage %s...\n",shippage->fire_sound_name[i][j],shippage->ship_struct.name)); + shippointer->static_wb[i].fm_fire_sound_index[j] = SOUND_NONE_INDEX; + } + else + shippointer->static_wb[i].fm_fire_sound_index[j] = fire_sound_handle; + } + else + shippointer->static_wb[i].fm_fire_sound_index[j] = SOUND_NONE_INDEX; + } + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + if(shippage->firing_sound_name[i][0] != 0) + { + int sound_handle = mng_GetGuaranteedSoundPage (shippage->firing_sound_name[i],infile); + + if (sound_handle < 0) + { + mprintf ((0,"Couldn't load firing_sound file '%s' in AssignPowPage %s...\n",shippage->firing_sound_name[i],shippage->ship_struct.name)); + shippointer->firing_sound[i] = SOUND_NONE_INDEX; + } + else + shippointer->firing_sound[i] = sound_handle; + } + else + shippointer->firing_sound[i] = -1; + + if(shippage->release_sound_name[i][0] != 0) + { + int sound_handle = mng_GetGuaranteedSoundPage (shippage->release_sound_name[i],infile); + + if (sound_handle < 0) + { + mprintf ((0,"Couldn't load firing_sound file '%s' in AssignPowPage %s...\n",shippage->release_sound_name[i],shippage->ship_struct.name)); + shippointer->firing_release_sound[i] = SOUND_NONE_INDEX; + } + else + shippointer->firing_release_sound[i] = sound_handle; + } + else + shippointer->firing_release_sound[i] = -1; + + if(shippage->spew_powerup_name[i][0] != 0) + { + shippointer->spew_powerup[i] = mng_GetGuaranteedGenericPage(shippage->spew_powerup_name[i],infile); + if (shippointer->spew_powerup[i] > -1 && Object_info[shippointer->spew_powerup[i]].type != OBJ_POWERUP) { //DAJ -1FIX + mprintf((1,"Spew powerup is not a powerup! Setting to none.\n")); + shippointer->spew_powerup[i] = -1; + } + } + else + shippointer->spew_powerup[i] = -1; + + } + + return 1; +} + +// Copies values from a ship into a ship_page +void mng_AssignShipToShipPage(int n,mngs_ship_page *shippage) +{ + ship *shippointer=&Ships[n]; + int i, j; + + // Assign the values + memcpy (&shippage->ship_struct,shippointer,sizeof(ship)); + + strcpy (shippage->ship_struct.name,shippointer->name); + + if (shippointer->model_handle!=-1) + strcpy (shippage->image_name,Poly_models[shippointer->model_handle].name); + else + strcpy (shippage->image_name,""); + + if (shippointer->dying_model_handle!=-1) + strcpy (shippage->dying_image_name,Poly_models[shippointer->dying_model_handle].name); + else + strcpy (shippage->dying_image_name,""); + + if (shippointer->med_render_handle!=-1) + strcpy (shippage->med_image_name,Poly_models[shippointer->med_render_handle].name); + else + strcpy (shippage->med_image_name,""); + + if (shippointer->lo_render_handle!=-1) + strcpy (shippage->lo_image_name,Poly_models[shippointer->lo_render_handle].name); + else + strcpy (shippage->lo_image_name,""); + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_GUNPOINTS; j++) + { + if (shippointer->static_wb[i].gp_weapon_index[j] >= 0) + strcpy (shippage->weapon_name[i][j], Weapons[shippointer->static_wb[i].gp_weapon_index[j]].name); + else + strcpy (shippage->weapon_name[i][j],"Laser"); + } + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) + { + for(j = 0; j < MAX_WB_FIRING_MASKS; j++) + { + if (shippointer->static_wb[i].fm_fire_sound_index[j] >= 0) + strcpy (shippage->fire_sound_name[i][j], Sounds[shippointer->static_wb[i].fm_fire_sound_index[j]].name); + else + strcpy (shippage->fire_sound_name[i][j],""); + } + } + + for(i = 0; i < MAX_PLAYER_WEAPONS; i++) { + + if (shippointer->firing_sound[i] != -1) + strcpy(shippage->firing_sound_name[i], Sounds[shippointer->firing_sound[i]].name); + else + shippage->firing_sound_name[i][0] = 0; + + if (shippointer->firing_release_sound[i] != -1) + strcpy(shippage->release_sound_name[i], Sounds[shippointer->firing_release_sound[i]].name); + else + shippage->release_sound_name[i][0] = 0; + + if (shippointer->spew_powerup[i] != -1) { + if (Object_info[shippointer->spew_powerup[i]].type != OBJ_POWERUP) { + mprintf((1,"Spew powerup is not a powerup! Setting to none.\n")); + shippage->spew_powerup_name[i][0] = 0; + } + else + strcpy(shippage->spew_powerup_name[i], Object_info[shippointer->spew_powerup[i]].name); + } + else + shippage->spew_powerup_name[i][0] = 0; + } + +} + +// Loads a ship found in the net table file. It then allocs a ship and +// then calls SetAndLoadShip to actually load in any images/models associated +// with it +void mng_LoadNetShipPage(CFILE *infile,bool overlay) +{ + mngs_ship_page shippage; + // Have to zero out all fields that are not in the page (some physics fields), else we + // end up with random values for initial velocity and the such... :) + memset(&shippage, 0, sizeof(mngs_ship_page)); + + if (mng_ReadNewShipPage (infile,&shippage)) + { + int n = FindShipName(shippage.ship_struct.name); + if(n!=-1) + { + if(overlay) + { + mprintf((0,"OVERLAYING SHIP %s\n",shippage.ship_struct.name)); + mng_FreePagetypePrimitives (PAGETYPE_SHIP,shippage.ship_struct.name,0); + mng_AssignShipPageToShip(&shippage,n); + } + return; + } + int ret=mng_SetAndLoadShip (&shippage,infile); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load shippage named %s!\n",shippage.ship_struct.name)); +} + +// Reads a ship page from a local table file. It then allocs a ship and +// loads any images/models associated with that ship +void mng_LoadLocalShipPage(CFILE *infile) +{ + mngs_ship_page shippage; + int ok=0; + // Have to zero out all fields that are not in the page (some physics fields), else we + // end up with random values for initial velocity and the such... :) + memset(&shippage, 0, sizeof(mngs_ship_page)); + + if (mng_ReadNewShipPage (infile,&shippage)) + { + // Check to see if this is a local copy that is supposed + // to go over a network copy (supersede the net copy) + + int i=FindShipName (shippage.ship_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + + strcpy (pl.name,shippage.ship_struct.name); + pl.pagetype=PAGETYPE_SHIP; + + /*if (Network_up && Stand_alone==0) + { + int locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + ok=1; + bool need_to_load_page = true; + + if (Loading_addon_table!=-1) + { + AddOnTablefile *addon; + int tidx; + + // see if we really need to load this page + //check to see if we already have loaded this page (because it was + //a dependancy of another) + addon = &AddOnDataTables[Loading_addon_table]; + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_SHIP && + !stricmp(addon->Addon_tracklocks[tidx].name,shippage.ship_struct.name) ) + { + // found it!! + mprintf((0,"ShipPage: %s previously loaded\n",shippage.ship_struct.name)); + need_to_load_page = false; + break; + } + } + } + + if(need_to_load_page) + { + mng_FreePagetypePrimitives (PAGETYPE_SHIP,shippage.ship_struct.name,0); + mng_AssignShipPageToShip (&shippage,i); + + // For addon data + if (ok && Loading_addon_table!=-1) + { + // this is an overlay of some sort..see which we are overlaying + int overlay = 1; + int addidx,tidx; + bool found = false; + for(addidx = Num_addon_tables-1; addidx>=0; addidx--) + { + if(addidx==Loading_addon_table) + continue; + AddOnTablefile *addon = &AddOnDataTables[addidx]; + + // look for the page in this table file + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_SHIP && + !stricmp(addon->Addon_tracklocks[tidx].name,shippage.ship_struct.name) ) + { + // found it!! + found = true; + overlay = addidx+2; + break; + } + } + + if(found) + break; + } + + mng_PushAddonPage (PAGETYPE_SHIP,shippage.ship_struct.name,overlay); + } + } + } + else + { + // This is a local ship that has never been checked in + if ((i=mng_SetAndLoadShip (&shippage,infile))<0) + ok=0; + else ok=1; + + // For addon data + if (ok && Loading_addon_table!=-1) + mng_PushAddonPage (PAGETYPE_SHIP,shippage.ship_struct.name,0); + } + + ASSERT (ok==1); + + if (Loading_addon_table==-1) + mng_AllocTrackLock (shippage.ship_struct.name,PAGETYPE_SHIP); + + if (i != -1) { + poly_model *pm = GetPolymodelPointer(Ships[i].model_handle); //page in the model + if (pm->n_attach < 3) { //check for enough attach points + DataError("Model <%s> for ship <%s> has %d attach points (must have at least 3)\n",pm->name,Ships[i].name,pm->n_attach); + } + FreePolymodelData(Ships[i].model_handle); //Unload the data so it will get loaded later after the textures are in memory + } + } + else + + mprintf ((0,"Could not load shippage named %s!\n",shippage.ship_struct.name)); + +} + diff --git a/manage/shippage.h b/manage/shippage.h new file mode 100644 index 000000000..46f8b3b8d --- /dev/null +++ b/manage/shippage.h @@ -0,0 +1,63 @@ +#ifndef SHIPPAGE_H +#define SHIPPAGE_H + +#include "manage.h" +#include "ship.h" +#include "CFILE.H" +#include "pstypes.h" + +typedef struct +{ + ship ship_struct; + char image_name[PAGENAME_LEN]; + char dying_image_name[PAGENAME_LEN]; + char weapon_name[MAX_PLAYER_WEAPONS][MAX_WB_GUNPOINTS][PAGENAME_LEN]; + char fire_sound_name[MAX_PLAYER_WEAPONS][MAX_WB_FIRING_MASKS][PAGENAME_LEN]; + char med_image_name[PAGENAME_LEN]; + char lo_image_name[PAGENAME_LEN]; + char firing_sound_name[MAX_PLAYER_WEAPONS][PAGENAME_LEN]; + char release_sound_name[MAX_PLAYER_WEAPONS][PAGENAME_LEN]; + char spew_powerup_name[MAX_PLAYER_WEAPONS][PAGENAME_LEN]; +} mngs_ship_page; + +// Ship page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a ship_page struct, writes that shippage out +void mng_WriteShipPage (CFILE *outfile,mngs_ship_page *shippage); + +// Reads a ship page from an open file. Returns 0 on error. +int mng_ReadShipPage (CFILE *infile,mngs_ship_page *shippage); + +// Given an open file pointer and a ship_page struct, writes that shippage out +void mng_WriteNewShipPage (CFILE *outfile,mngs_ship_page *shippage); + +// Reads a ship page from an open file. Returns 0 on error. +int mng_ReadNewShipPage (CFILE *infile,mngs_ship_page *shippage); + + +// Reads in the shippage named "name" into shippage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificShipPage (char *name,mngs_ship_page *shippage,int offset=0); + +// Given a ship page, allocs a ship and calls AssignShipPageToShip to actually +// load model and values. Rturns ship handle on success, -1 if fail +int mng_SetAndLoadShip (mngs_ship_page *shippage,CFILE *infile); + +// Given a shippage and a ship handle, attempts to make ship n correspond to +// to the shippage. +// Returns 1 on success, 0 otherwise +int mng_AssignShipPageToShip(mngs_ship_page *shippage,int n,CFILE *infile=NULL); + +// Copies values from a Ship into a ship_page +void mng_AssignShipToShipPage(int n,mngs_ship_page *shippage); + + +// Reads in a ship page from the local table file, superseding any ship +// already in RAM with that same name +void mng_LoadLocalShipPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetShipPage (CFILE *,bool overlay=false); + +#endif \ No newline at end of file diff --git a/manage/soundpage.cpp b/manage/soundpage.cpp new file mode 100644 index 000000000..86f0130c8 --- /dev/null +++ b/manage/soundpage.cpp @@ -0,0 +1,623 @@ +/* + * $Logfile: /DescentIII/Main/manage/soundpage.cpp $ + * $Revision: 23 $ + * $Date: 10/08/01 4:20p $ + * $Author: Matt $ + * + * + * $Log: /DescentIII/Main/manage/soundpage.cpp $ + * + * 23 10/08/01 4:20p Matt + * Added system to check for errors when reading in add-on data. + * + * 21 10/26/99 3:31p Jeff + * handle extra.gam addon tablefile + * + * 20 9/18/99 8:49p Jeff + * fixed bug with addon pages that have dependencies on other pages in the + * addon tablefile + * + * 19 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 18 7/28/99 2:24p Kevin + * Macintosh Fixes! + * + * 17 4/19/99 7:37p 3dsmax + * more demo explosion fixes + * + * 16 4/19/99 5:42p Jason + * fixes for explosions in the demo + * + * 15 4/14/99 1:34a Jeff + * fixed case mismatched #includes + * + * 14 3/04/99 4:47p Jason + * temp fix (ie BAD HACK) for OEM table file woes + * + * 13 1/13/99 7:05a Jeff + * put #ifdef around #include + * + * 12 12/29/98 4:30p Jason + * added add-on data functionality + * + * 11 11/06/98 12:35p Jason + * more speedups for manage system + * + * 10 11/05/98 7:55p Jason + * changes for new manage system + * + * 9 11/02/98 6:02p Jason + * made yes network updates much faster + * + * 8 10/19/98 11:57a Chris + * Update the sound system to use the import volume + * + * 7 10/09/98 2:27p Jason + * reorganized table file system + * + * 6 10/08/98 10:03p Jason + * more filtered table file stuff + * + * 5 8/24/98 2:37p Jason + * made table file more efficient with regards to invalid names + * + * 4 6/12/98 1:06p Jason + * added smart loading from local table file + * + * 3 2/23/98 1:22p Jason + * made FindSpecific* read from the local drive, not the net drive - when + * starting the editor + * + * 2 1/15/98 6:22p Jason + * added safety checks so the network won't copy over a primitive you have + * held locally + * + * 7 5/08/97 12:41p Jason + * made manage system work with device dependant path names + * + * 6 4/29/97 7:26a Chris + * Improved the sound code. It is healthy. + * + * 5 4/25/97 6:16p Jason + * added switcheroo function + * + * 4 4/22/97 3:24p Jason + * added more code for recursive page loading + * + * 3 4/08/97 2:25a Chris + * Fixed a problem with uninitialized data. In addition it + * fixed a problem with the .used flag that would happen + * 1 out of 2^(sizeof(used_flag)) times (it would be zero + * when it was supposed to be non-zero) + * + * 2 4/01/97 2:13p Jason + * changes for sound page functionality + * + * 1 4/01/97 1:48p Jason + * + * + * $NoKeywords: $ + */ +#if defined(WIN32) +#include +#endif +#include "CFILE.H" +#include "manage.h" +#include "soundpage.h" +#include "mono.h" +#include "pserror.h" +#include "soundload.h" +#include "ddio.h" +#include "args.h" +#include +// soundpage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define SOUNDPAGE_COMMAND_NAME 1 +#define SOUNDPAGE_COMMAND_END 2 +#define SOUNDPAGE_COMMAND_RAW_NAME 3 +#define SOUNDPAGE_COMMAND_VERSION 4 +#define SOUNDPAGE_COMMAND_FLAGS 5 +#define SOUNDPAGE_COMMAND_INNER_CONE_ANGLE 6 +#define SOUNDPAGE_COMMAND_OUTER_CONE_ANGLE 7 +#define SOUNDPAGE_COMMAND_OUTER_CONE_VOLUME 8 +#define SOUNDPAGE_COMMAND_MAX_DIST 9 +#define SOUNDPAGE_COMMAND_MIN_DIST 10 +#define SOUNDPAGE_COMMAND_LOOP_START 11 +#define SOUNDPAGE_COMMAND_LOOP_END 12 +#define SOUNDPAGE_COMMAND_IMPORT_VOLUME 13 +#define SOUNDPAGE_VERSION 1 + +extern char *TablefileNameOverride; + +// Given an open file pointer and a sound_page struct, writes that sound page out +void mng_WriteSoundPage (CFILE *outfile,mngs_sound_page *soundpage) +{ + int i; + ASSERT (outfile!=NULL); + ASSERT (soundpage!=NULL); + + cf_WriteByte (outfile,PAGETYPE_SOUND); + + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_NAME); // write out sound name + cf_WriteByte (outfile,PAGENAME_LEN); + for (i=0;isound_struct.name[i]); + // Write out its rawfile name + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_RAW_NAME); // get ready to write out name + cf_WriteByte (outfile,PAGENAME_LEN); + for (i=0;iraw_name[i]); + + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_FLAGS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,soundpage->sound_struct.flags); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_LOOP_START); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,soundpage->sound_struct.loop_start); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_LOOP_END); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,soundpage->sound_struct.loop_end); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_OUTER_CONE_VOLUME); + cf_WriteByte (outfile,4); + cf_WriteFloat(outfile,soundpage->sound_struct.outer_cone_volume); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_INNER_CONE_ANGLE); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,soundpage->sound_struct.inner_cone_angle); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_OUTER_CONE_ANGLE); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,soundpage->sound_struct.outer_cone_angle); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_MAX_DIST); + cf_WriteByte (outfile,4); + cf_WriteFloat(outfile,soundpage->sound_struct.max_distance); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_MIN_DIST); + cf_WriteByte (outfile,4); + cf_WriteFloat(outfile,soundpage->sound_struct.min_distance); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_IMPORT_VOLUME); + cf_WriteByte (outfile,4); + cf_WriteFloat(outfile,soundpage->sound_struct.import_volume); + cf_WriteByte (outfile,SOUNDPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} +// Given an open file pointer and a sound_page struct, writes that sound page out +void mng_WriteNewSoundPage (CFILE *outfile,mngs_sound_page *soundpage) +{ + ASSERT (outfile!=NULL); + ASSERT (soundpage!=NULL); + + int offset=StartManagePage(outfile,PAGETYPE_SOUND); + cf_WriteShort (outfile,SOUNDPAGE_VERSION); + // Write out name,rawfile name + cf_WriteString (outfile,soundpage->sound_struct.name); + cf_WriteString (outfile,soundpage->raw_name); + cf_WriteInt (outfile,soundpage->sound_struct.flags); + cf_WriteInt (outfile,soundpage->sound_struct.loop_start); + cf_WriteInt (outfile,soundpage->sound_struct.loop_end); + cf_WriteFloat(outfile,soundpage->sound_struct.outer_cone_volume); + cf_WriteInt (outfile,soundpage->sound_struct.inner_cone_angle); + cf_WriteInt (outfile,soundpage->sound_struct.outer_cone_angle); + cf_WriteFloat(outfile,soundpage->sound_struct.max_distance); + cf_WriteFloat(outfile,soundpage->sound_struct.min_distance); + cf_WriteFloat(outfile,soundpage->sound_struct.import_volume); + EndManagePage (outfile,offset); +} +// Reads a sound page from an open file. Returns 0 on error. +int mng_ReadNewSoundPage (CFILE *infile,mngs_sound_page *soundpage) +{ + ASSERT (infile!=NULL); + int version=cf_ReadShort (infile); + // read in name,rawfile name + cf_ReadString (soundpage->sound_struct.name,PAGENAME_LEN,infile); + cf_ReadString (soundpage->raw_name,PAGENAME_LEN,infile); + soundpage->sound_struct.flags=cf_ReadInt (infile); + + soundpage->sound_struct.loop_start=cf_ReadInt (infile); + soundpage->sound_struct.loop_end=cf_ReadInt (infile); + soundpage->sound_struct.outer_cone_volume=cf_ReadFloat (infile); + soundpage->sound_struct.inner_cone_angle=cf_ReadInt (infile); + soundpage->sound_struct.outer_cone_angle=cf_ReadInt (infile); + soundpage->sound_struct.max_distance=cf_ReadFloat (infile); + soundpage->sound_struct.min_distance=cf_ReadFloat (infile); + soundpage->sound_struct.import_volume=cf_ReadFloat (infile); + #ifdef DEMO + if (!stricmp ("DefaultBuildingExplode",soundpage->sound_struct.name)) + { + soundpage->sound_struct.import_volume=1.0; + soundpage->sound_struct.flags&=~(SPF_OBJ_UPDATE|SPF_FIXED_FREQ); + } + if (!stricmp ("DefaultRobotExplode1",soundpage->sound_struct.name)) + { + soundpage->sound_struct.import_volume=1.0; + soundpage->sound_struct.flags&=~(SPF_OBJ_UPDATE|SPF_FIXED_FREQ); + } + if (!stricmp ("DefaultRobotExplode2",soundpage->sound_struct.name)) + { + soundpage->sound_struct.import_volume=1.0; + soundpage->sound_struct.flags&=~(SPF_OBJ_UPDATE|SPF_FIXED_FREQ); + } + #endif + // This is a valid new page + soundpage->sound_struct.used=1; + return 1; // successfully read +} +// Reads a sound page from an open file. Returns 0 on error. +int mng_ReadSoundPage (CFILE *infile,mngs_sound_page *soundpage) +{ + int done=0; + char command; + ubyte len; + int i; + if (!Old_table_method) + return mng_ReadNewSoundPage (infile,soundpage); + ASSERT (infile!=NULL); + soundpage->sound_struct.import_volume = 1.0f; + while (!done) + { + // Read in command byte then read in the length of that commands data + + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + switch (command) + { + case SOUNDPAGE_COMMAND_END: + done=1; + break; + case SOUNDPAGE_COMMAND_RAW_NAME: // the name of the sound sample + for (i=0;iraw_name[i]=cf_ReadByte (infile); + break; + case SOUNDPAGE_COMMAND_NAME: + for (i=0;isound_struct.name[i]=cf_ReadByte (infile); + break; + case SOUNDPAGE_COMMAND_FLAGS: + soundpage->sound_struct.flags = cf_ReadInt(infile); + break; + case SOUNDPAGE_COMMAND_LOOP_START: + soundpage->sound_struct.loop_start = cf_ReadInt(infile); + break; + case SOUNDPAGE_COMMAND_LOOP_END: + soundpage->sound_struct.loop_end = cf_ReadInt(infile); + break; + case SOUNDPAGE_COMMAND_OUTER_CONE_VOLUME: + soundpage->sound_struct.outer_cone_volume = cf_ReadFloat(infile); + break; + case SOUNDPAGE_COMMAND_INNER_CONE_ANGLE: + soundpage->sound_struct.inner_cone_angle = cf_ReadInt(infile); + break; + case SOUNDPAGE_COMMAND_OUTER_CONE_ANGLE: + soundpage->sound_struct.outer_cone_angle = cf_ReadInt(infile); + break; + case SOUNDPAGE_COMMAND_MAX_DIST: + soundpage->sound_struct.max_distance = cf_ReadFloat(infile); + break; + case SOUNDPAGE_COMMAND_MIN_DIST: + soundpage->sound_struct.min_distance = cf_ReadFloat(infile); + break; + case SOUNDPAGE_COMMAND_IMPORT_VOLUME: + soundpage->sound_struct.import_volume = cf_ReadFloat(infile); + break; + default: + // Ignore the ones we don't know + for (i=0;isound_struct.used=1; + return 1; // successfully read +} +// Reads in the sound named "name" into soundpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificSoundPage (char *name,mngs_sound_page *soundpage,int offset) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + char tablename[TABLE_NAME_LEN]; + if (Loading_locals) + { + infile=cfopen (LocalTableFilename,"rb"); + } + else if (Loading_addon_table!=-1) + { + infile=cfopen (AddOnDataTables[Loading_addon_table].AddOnTableFilename,"rb"); + } + else + { + if (Network_up && Starting_editor) + { + int farg=FindArg("-filter"); + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + infile=cfopen (tablename,"rb"); + } + else + { + infile = NULL; + if(TablefileNameOverride) + { + infile=cfopen(TablefileNameOverride,"rb"); + } + + if(!infile) + infile=cfopen (TableFilename,"rb"); + } + } + if (!infile) + { + mprintf ((0,"Couldn't open table file to find sound!\n")); + Int3(); + return 0; + } + if (offset) + cfseek (infile,offset,SEEK_SET); + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + // If not a sound page, just read it in and ignore it + if (pagetype!=PAGETYPE_SOUND) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + mng_ReadNewSoundPage (infile,soundpage); + + if (!stricmp(name,soundpage->sound_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + cfclose (infile); + + return found; // successful! +} +// Given a sound page, allocs a sound and calls AssignSoundPageToSound to actually +// load raws and values. Rturns sound handle on success, -1 if fail +int mng_SetAndLoadSound (mngs_sound_page *soundpage) +{ + int n; + + n=AllocSound(); + if (n<0) + return -1; + if (!mng_AssignSoundPageToSound(soundpage,n)) + return -1; + + return n; +} +// Given a soundpage and a sound handle, attempts to make sound n correspond to +// to the soundpage. +// Returns 1 on success, 0 otherwise +int mng_AssignSoundPageToSound(mngs_sound_page *soundpage,int n) +{ + sound_info *soundpointer=&Sounds[n]; + int raw_handle; + // copy our values + memcpy (soundpointer,&soundpage->sound_struct,sizeof(sound_info)); + strcpy (soundpointer->name,soundpage->sound_struct.name); + // First see if our raw differs from the one on the net + // If it is, make a copy + // If its a release version, don't do any of this + #ifndef RELEASE + if (Network_up) + { + char str[200]; + char netstr[200]; + + ddio_MakePath (str,LocalSoundsDir,soundpage->raw_name,NULL); + ddio_MakePath (netstr,NetSoundsDir,soundpage->raw_name,NULL); + UpdatePrimitive (str,netstr,soundpage->raw_name,PAGETYPE_SOUND,soundpointer->name); + } + #endif + // Try and load our sound raw from the disk + raw_handle=LoadSoundFile (soundpage->raw_name, Sounds[n].import_volume, false); + if (raw_handle<0) + { + mprintf ((0,"Couldn't load file '%s' in AssignSoundPage...\n",soundpage->raw_name)); + soundpointer->sample_index=-1; + return 0; + } + else + soundpointer->sample_index=raw_handle; + return 1; +} +// Copies values from a sound into a sound_page +void mng_AssignSoundToSoundPage(int n,mngs_sound_page *soundpage) +{ + sound_info *soundpointer=&Sounds[n]; + // Assign the values + memcpy (&soundpage->sound_struct,soundpointer,sizeof(sound_info)); + + strcpy (soundpage->sound_struct.name,soundpointer->name); + if (soundpointer->sample_index!=-1) + strcpy (soundpage->raw_name,SoundFiles[soundpointer->sample_index].name); + else + strcpy (soundpage->raw_name,""); +} +// Loads a sound found in the net table file. It then allocs a sound and +// then calls SetAndLoadSound to actually load in any raws associated +// with it +void mng_LoadNetSoundPage(CFILE *infile,bool overlay) +{ + mngs_sound_page soundpage; + memset(&soundpage, 0, sizeof(mngs_sound_page)); + + if (mng_ReadNewSoundPage (infile,&soundpage)) + { + int n = FindSoundName(soundpage.sound_struct.name); + if (n!=-1) + { + if(overlay) + { + mprintf((0,"OVERLAYING SOUND %s\n",soundpage.sound_struct.name)); + mng_FreePagetypePrimitives (PAGETYPE_SOUND,soundpage.sound_struct.name,0); + mng_AssignSoundPageToSound(&soundpage,n); + } + return; // already in memory + } + int ret=mng_SetAndLoadSound (&soundpage); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load soundpage named %s!\n",soundpage.sound_struct.name)); +} +// Reads a sound page from a local table file. It then allocs a sound and +// loads any raws associated with that sound +void mng_LoadLocalSoundPage(CFILE *infile) +{ + mngs_sound_page soundpage; + int ok=0; + memset(&soundpage, 0, sizeof(mngs_sound_page)); + if (mng_ReadNewSoundPage (infile,&soundpage)) + { + // Check to see if this is a local copy that is supposed + // to go over a network copy (supersede the net copy) + int i=FindSoundName (soundpage.sound_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + + strcpy (pl.name,soundpage.sound_struct.name); + pl.pagetype=PAGETYPE_SOUND; + + /*if (Network_up && Stand_alone==0) + { + int locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + + ok=1; + bool need_to_load_page = true; + + if (Loading_addon_table!=-1) + { + AddOnTablefile *addon; + int tidx; + + // see if we really need to load this page + //check to see if we already have loaded this page (because it was + //a dependancy of another) + addon = &AddOnDataTables[Loading_addon_table]; + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_SOUND && + !stricmp(addon->Addon_tracklocks[tidx].name,soundpage.sound_struct.name) ) + { + // found it!! + mprintf((0,"SoundPage: %s previously loaded\n",soundpage.sound_struct.name)); + need_to_load_page = false; + break; + } + } + } + + if(need_to_load_page) + { + mng_FreePagetypePrimitives (PAGETYPE_SOUND,soundpage.sound_struct.name,0); + mng_AssignSoundPageToSound (&soundpage,i); + + // For addon data + if (Loading_addon_table!=-1) + { + int overlay = 1; + int addidx,tidx; + bool found = false; + AddOnTablefile *addon; + + // this is an overlay of some sort..see which we are overlaying + for(addidx = Num_addon_tables-1; addidx>=0; addidx--) + { + if(addidx==Loading_addon_table) + continue; + addon = &AddOnDataTables[addidx]; + + // look for the page in this table file + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_SOUND && + !stricmp(addon->Addon_tracklocks[tidx].name,soundpage.sound_struct.name) ) + { + // found it!! + found = true; + overlay = addidx+2; + break; + } + } + + if(found) + break; + } + + mng_PushAddonPage (PAGETYPE_SOUND,soundpage.sound_struct.name,overlay); + } + } + } + else + { + // This is a local sound that has never been checked in + if ((i=mng_SetAndLoadSound (&soundpage))<0) + ok=0; + else ok=1; + // For addon data + if (ok && Loading_addon_table!=-1) + mng_PushAddonPage (PAGETYPE_SOUND,soundpage.sound_struct.name,0); + } + + ASSERT (ok==1); + if (Loading_addon_table==-1) + mng_AllocTrackLock (soundpage.sound_struct.name,PAGETYPE_SOUND); + } + else + mprintf ((0,"Could not load soundpage named %s!\n",soundpage.sound_struct.name)); + +} +// First searches through the sound index to see if the sound is already +// loaded. If not, searches in the table file and loads it. +// Returns index of sound found, -1 if not +int mng_GetGuaranteedSoundPage (char *name,CFILE *infile) +{ + int i; + mngs_sound_page soundpage; + // See if its in memory + i=FindSoundName (name); + if (i!=-1) + return i; + // Not in memory. Load it from the table file. Start searching from the + // current spot in the open table file + int ret=mng_FindSpecificSoundPage (name,&soundpage,infile?infile->position:0); + if (!ret) + return -1; + // We've found it in the table file, now load it. + ret=mng_SetAndLoadSound (&soundpage); + //ASSERT (ret>=0); + if (ret < 0) + DataError("Cannot load sound <%s>, wav file <%s>\n",soundpage.sound_struct.name,soundpage.raw_name); + + if(Loading_addon_table!=-1) + { + // we're loading addon table pages, this will not overlay anything + mng_PushAddonPage (PAGETYPE_SOUND,soundpage.sound_struct.name,0); + } + return ret; +} + + \ No newline at end of file diff --git a/manage/soundpage.h b/manage/soundpage.h new file mode 100644 index 000000000..4f1e03808 --- /dev/null +++ b/manage/soundpage.h @@ -0,0 +1,59 @@ +#ifndef SOUNDPAGE_H +#define SOUNDPAGE_H + +#include "manage.h" +#include "soundload.h" +#include "CFILE.H" +#include "pstypes.h" + +typedef struct +{ + sound_info sound_struct; + char raw_name[PAGENAME_LEN]; +} mngs_sound_page; + +// Sound page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a sound_page struct, writes that soundpage out +void mng_WriteSoundPage (CFILE *outfile,mngs_sound_page *soundpage); + +// Reads a sound page from an open file. Returns 0 on error. +int mng_ReadSoundPage (CFILE *infile,mngs_sound_page *soundpage); +// Given an open file pointer and a sound_page struct, writes that soundpage out +void mng_WriteNewSoundPage (CFILE *outfile,mngs_sound_page *soundpage); + +// Reads a sound page from an open file. Returns 0 on error. +int mng_ReadNewSoundPage (CFILE *infile,mngs_sound_page *soundpage); + +// Reads in the soundpage named "name" into soundpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificSoundPage (char *name,mngs_sound_page *soundpage,int offset=0); + +// Given a sound page, allocs a sound and calls AssignSoundPageToSound to actually +// load model and values. Rturns sound handle on success, -1 if fail +int mng_SetAndLoadSound (mngs_sound_page *soundpage); + +// Given a soundpage and a sound handle, attempts to make sound n correspond to +// to the soundpage. +// Returns 1 on success, 0 otherwise +int mng_AssignSoundPageToSound(mngs_sound_page *soundpage,int n); + +// Copies values from a Sound into a sound_page +void mng_AssignSoundToSoundPage(int n,mngs_sound_page *soundpage); + + +// Reads in a sound page from the local table file, superseding any sound +// already in RAM with that same name +void mng_LoadLocalSoundPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetSoundPage (CFILE *,bool overlay=false); + +// First searches through the sound index to see if the sound is already +// loaded. If not, searches in the table file and loads it. +// Returns index of sound if found, -1 if not +int mng_GetGuaranteedSoundPage (char *name,CFILE *infile=NULL); + + +#endif \ No newline at end of file diff --git a/manage/texpage.cpp b/manage/texpage.cpp new file mode 100644 index 000000000..4edab9ce4 --- /dev/null +++ b/manage/texpage.cpp @@ -0,0 +1,1381 @@ +/* + * $Logfile: /DescentIII/Main/manage/texpage.cpp $ + * $Revision: 61 $ + * $Date: 10/08/01 4:20p $ + * $Author: Matt $ + * + * For loading/saving of texture types + * + * $Log: /DescentIII/Main/manage/texpage.cpp $ + * + * 61 10/08/01 4:20p Matt + * Added system to check for errors when reading in add-on data. + * + * 59 9/06/01 10:32a Matt + * Added code to fix problem poping add-on pages when the original pages + * were in the extra.gam file. + * + * 58 3/20/00 12:28p Matt + * Merge of Duane's post-1.3 changes. + * Added malloc for temp struct instead of delcaring on the stack. + * + * 57 10/26/99 3:31p Jeff + * handle extra.gam addon tablefile + * + * 56 9/18/99 8:49p Jeff + * fixed bug with addon pages that have dependencies on other pages in the + * addon tablefile + * + * 55 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 54 5/21/99 6:39a Matt + * Fixed massive evil caused by recent changes for Mac, which resulted in + * two functions using the same static data at the same time. + * + * 53 5/14/99 12:07p Jeff + * error handling (Jason) + * + * 52 5/13/99 8:36p Matt + * Made some local variables global to get around the 32K local variable + * limit on the Mac. + * + * 51 4/14/99 1:34a Jeff + * fixed case mismatched #includes + * + * 50 4/12/99 3:05p Jason + * changes for 256 textures + * + * 49 4/09/99 7:04p Jason + * changed some texture defines + * + * 48 4/09/99 12:51p Jason + * corrected buggy sound resolving code + * + * 47 3/30/99 6:01p Jason + * added new procedural effect + * + * 46 2/20/99 2:17p Matt + * Added texture sounds + * + * 45 2/16/99 11:17a Jason + * took out mprintf + * + * 44 2/01/99 11:47a Jason + * fixed bumpmap errors + * + * 43 1/22/99 3:59p Jason + * added 256x256 textures to help with terrain skies + * + * 42 1/13/99 7:05a Jeff + * put #ifdef around #include + * + * 41 12/29/98 4:30p Jason + * added add-on data functionality + * + * 40 12/17/98 12:02p Jason + * fixed dumb procedural thing + * + * 39 11/06/98 12:35p Jason + * more speedups for manage system + * + * 38 11/05/98 7:55p Jason + * changes for new manage system + * + * 37 11/02/98 6:02p Jason + * made yes network updates much faster + * + * 36 10/09/98 2:27p Jason + * reorganized table file system + * + * 35 10/08/98 10:03p Jason + * more filtered table file stuff + * + * 34 10/07/98 5:03p Jason + * more options for textures + * + * 33 10/01/98 2:47p Jason + * added timed procedurals + * + * 32 8/24/98 2:37p Jason + * made table file more efficient with regards to invalid names + * + * 31 8/15/98 3:46p Matt + * Got rid of compile warning + * + * 30 8/13/98 1:03p Jason + * added some more procedural workers + * + * 29 8/12/98 4:24p Luke + * temporary fix for dumb local table file conundrum + * + * 28 7/14/98 6:50p Jason + * made procedurals save out to the table file + * + * 27 6/12/98 1:06p Jason + * added smart loading from local table file + * + * 26 6/11/98 12:54p Mark + * temp fix + * + * 25 6/10/98 12:21p Matt + * Revamped system that controls what textures are displayed on the + * texture tab. Should work correctly now, and will now save the settings + * to the registry. Also, textures now default to no type set, not all + * types set. + * + * 24 6/03/98 3:46p Jason + * made megacell system more robust + * + * 23 5/20/98 1:08p Jason + * more stuff for adding bumpmaps + * + * 22 5/04/98 6:08p Jason + * sped up predictive pagefile loading + * + * 21 4/06/98 4:42p Jason + * added texture alpha change + * + * 20 3/17/98 4:33p Jason + * Added size changing to bitmaps/textures + * + * 19 2/23/98 1:22p Jason + * made FindSpecific* read from the local drive, not the net drive - when + * starting the editor + * + * 18 2/16/98 11:42a Jason + * added better error checking for checking out files + * + * 17 2/12/98 1:32p Jason + * got mipmapping working + * + * 16 1/14/98 7:55p Jason + * make textures pageable depending on whether or not you're using the + * network + * + * 15 12/19/97 2:46p Jason + * implemented bitmap paging routines + * + * 14 12/10/97 5:29p Jason + * warn if reusing a net texture + * + * 13 11/21/97 2:02p Jason + * made light textures on model render that model face fully bright + * + * 12 10/20/97 5:05p Mark + * FROM JASON: Changed error bitmap from -1 to 0 + * + * 11 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 10 9/25/97 4:30p Jason + * got destroyable textures working + * + * 9 8/19/97 1:21p Jason + * set reflectivity to .5 by default + * + * 8 8/19/97 1:57a Jason + * made rgb lighting take floats + * + * 7 8/12/97 1:10a Jason + * added code to support radiosity lighting + * + * 6 8/08/97 12:59p Jason + * got texture sliding and pingponging working + * + * 5 8/06/97 2:57p Jason + * fixed alpha problem with reorder net pages + * + * 4 8/04/97 5:05p Jason + * fixed dumb off-by-one error + * + * 3 8/04/97 3:28p Jason + * added alpha blending per texture + * + * 31 6/24/97 12:42p Jason + * checked in for safety + * + * 30 6/06/97 3:57p Jason + * implemented changes for small textures and automatic tmap2 recognition + * + * 29 6/05/97 2:52p Jason + * added megacell functions + * + * 28 5/30/97 5:46p Jason + * added colored light functionality on a vertex level + * + * 27 5/23/97 7:07p Matt + * Added code, data, & dialogs to keep track of what a texture is used for + * (mine, terrain, object) so we can display in the texture dialog only + * the ones we're interested in. + * + * 26 5/12/97 1:19p Jason + * sped up texture loading a little bit + * + * 25 5/12/97 11:41a Jason + * made game work (default) to 16bit no mip maps mode + * Saves us alot of memory + * + * 24 5/08/97 12:41p Jason + * made manage system work with device dependant path names + * + * 23 4/25/97 6:16p Jason + * added switcheroo function + * + * 22 4/08/97 2:25a Chris + * Fixed a problem with uninitialized data. In addition it + * fixed a problem with the .used flag that would happen + * 1 out of 2^(sizeof(used_flag)) times (it would be zero + * when it was supposed to be non-zero) + * + * 21 3/12/97 6:15 PM Jeremy + * in mng_WriteTexturePage moved the declaration of int i outside of the + * first for loop to the top of the function so that it will compile on + * mac and PC at the same time. :) + * + * 20 3/03/97 6:21p Matt + * Changed cfile functions to use D3 naming convention + * + * $NoKeywords: $ + */ + +#if defined(WIN32) +#include +#endif + +#include "CFILE.H" +#include "manage.h" +#include "gametexture.h" +#include "bitmap.h" +#include "mono.h" +#include "pserror.h" +#include "texpage.h" +#include +#include "vclip.h" +#include "ddio.h" +#include "args.h" +#include "sounds.h" +#include "soundpage.h" +#include "soundload.h" + +// Texpage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define TEXPAGE_COMMAND_NAME 1 +#define TEXPAGE_COMMAND_FLAGS 2 +#define TEXPAGE_COMMAND_END 3 +#define TEXPAGE_COMMAND_BITMAP_NAME 4 +#define TEXPAGE_COMMAND_DESTROY_NAME 5 +#define TEXPAGE_COMMAND_VERSION 6 +#define TEXPAGE_COMMAND_REFLECT 7 +#define TEXPAGE_COMMAND_DAMAGE 8 +#define TEXPAGE_COMMAND_LIGHTING 9 +#define TEXPAGE_COMMAND_SLIDE 10 +#define TEXPAGE_COMMAND_NULL_NAME 11 // null terminated name string +#define TEXPAGE_COMMAND_BITMAP_NULL_NAME 12 +#define TEXPAGE_COMMAND_DESTROY_NULL_NAME 13 +#define TEXPAGE_COMMAND_COLORED_LIGHTING 14 +#define TEXPAGE_COMMAND_ALPHA 15 +#define TEXPAGE_COMMAND_SLIDE_FLOAT 16 +#define TEXPAGE_COMMAND_LIGHTING_FLOAT 17 +#define TEXPAGE_COMMAND_SPEED 18 +#define TEXPAGE_COMMAND_FIRST_PROC_PAL 19 +#define TEXPAGE_COMMAND_SECOND_PROC_PAL 20 +#define TEXPAGE_COMMAND_PROCEDURAL 21 +#define TEXPAGE_COMMAND_EXTRA_PROCEDURAL 22 +#define TEXPAGE_COMMAND_EXTRA_PROCEDURAL2 23 +#define TEXPAGE_COMMAND_CORONA 24 + +//Declare this here because it's too big to put on the stack on the Mac +static mngs_texture_page texpage1; + +extern char *TablefileNameOverride; + +#define TEXPAGE_VERSION 7 +// Given an open file pointer and a texture handle, writes that texture page out +void mng_WriteTexturePage (CFILE *outfile,mngs_texture_page *texpage) +{ + int i = 0; + + ASSERT (outfile!=NULL); + ASSERT (texpage!=NULL); + + cf_WriteByte (outfile,PAGETYPE_TEXTURE); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_VERSION); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,TEXPAGE_VERSION); + + // Write out texture name + cf_WriteByte (outfile,TEXPAGE_COMMAND_NULL_NAME); + cf_WriteByte (outfile,strlen (texpage->tex_struct.name)+1); + cf_WriteString (outfile,texpage->tex_struct.name); + + // Write out its bitmaps name + cf_WriteByte (outfile,TEXPAGE_COMMAND_BITMAP_NULL_NAME); // get ready to write out name + cf_WriteByte (outfile,strlen(texpage->bitmap_name)+1); + cf_WriteString (outfile,texpage->bitmap_name); + + // Write out its bitmaps name + cf_WriteByte (outfile,TEXPAGE_COMMAND_DESTROY_NULL_NAME); // get ready to write out name + cf_WriteByte (outfile,strlen (texpage->destroy_name)+1); + cf_WriteString (outfile,texpage->destroy_name); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_LIGHTING_FLOAT); + cf_WriteByte (outfile,4*3); + cf_WriteFloat (outfile,texpage->tex_struct.r); + cf_WriteFloat (outfile,texpage->tex_struct.g); + cf_WriteFloat (outfile,texpage->tex_struct.b); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_ALPHA); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,texpage->tex_struct.alpha); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_SPEED); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,texpage->tex_struct.speed); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_SLIDE_FLOAT); + cf_WriteByte (outfile,8); + cf_WriteFloat (outfile,texpage->tex_struct.slide_u); + cf_WriteFloat (outfile,texpage->tex_struct.slide_v); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_REFLECT); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,texpage->tex_struct.reflectivity); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_CORONA); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,texpage->tex_struct.corona_type); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_DAMAGE); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,texpage->tex_struct.damage); + + cf_WriteByte (outfile,TEXPAGE_COMMAND_FLAGS); // write out flags field + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,texpage->tex_struct.flags); + + // Write out the procedural + if (texpage->tex_struct.flags & TF_PROCEDURAL) + { + // Write out the palette in two halves + cf_WriteByte (outfile,TEXPAGE_COMMAND_FIRST_PROC_PAL); + cf_WriteByte (outfile,(ubyte)255); + for (i=0;i<255;i++) + { + ushort val=texpage->proc_palette[i]; + val>>=8; + + cf_WriteByte (outfile,val); + } + + cf_WriteByte (outfile,TEXPAGE_COMMAND_SECOND_PROC_PAL); + cf_WriteByte (outfile,(ubyte)255); + for (i=0;i<255;i++) + { + ushort val=texpage->proc_palette[i]; + val&=0xFF; + + cf_WriteByte (outfile,val); + } + + cf_WriteByte (outfile,TEXPAGE_COMMAND_EXTRA_PROCEDURAL); + cf_WriteByte (outfile,3); + cf_WriteByte (outfile,texpage->proc_heat); + cf_WriteByte (outfile,texpage->proc_light); + cf_WriteByte (outfile,texpage->proc_thickness); + + + cf_WriteByte (outfile,TEXPAGE_COMMAND_EXTRA_PROCEDURAL2); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,texpage->proc_evaluation_time); + + // Write out static procedural + for (i=0;inum_proc_elements;i++) + { + cf_WriteByte (outfile,TEXPAGE_COMMAND_PROCEDURAL); + + cf_WriteByte (outfile,8); + + cf_WriteByte (outfile,texpage->proc_type[i]); + cf_WriteByte (outfile,texpage->proc_frequency[i]); + cf_WriteByte (outfile,texpage->proc_speed[i]); + cf_WriteByte (outfile,texpage->proc_size[i]); + + cf_WriteByte (outfile,texpage->proc_x1[i]); + cf_WriteByte (outfile,texpage->proc_y1[i]); + + cf_WriteByte (outfile,texpage->proc_x2[i]); + cf_WriteByte (outfile,texpage->proc_y2[i]); + + } + } + + cf_WriteByte (outfile,TEXPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} + +// Given an open file pointer and a texture handle, writes that texture page out +void mng_WriteNewTexturePage (CFILE *outfile,mngs_texture_page *texpage) +{ + int i = 0; + + ASSERT (outfile!=NULL); + ASSERT (texpage!=NULL); + + int offset=StartManagePage(outfile,PAGETYPE_TEXTURE); + + + cf_WriteShort (outfile,TEXPAGE_VERSION); + cf_WriteString (outfile,texpage->tex_struct.name); + + // Write out its bitmaps name + cf_WriteString (outfile,texpage->bitmap_name); + + // Write out destroy bitmap name + cf_WriteString (outfile,texpage->destroy_name); + + // Write out rgb lighting and alpha + cf_WriteFloat (outfile,texpage->tex_struct.r); + cf_WriteFloat (outfile,texpage->tex_struct.g); + cf_WriteFloat (outfile,texpage->tex_struct.b); + cf_WriteFloat (outfile,texpage->tex_struct.alpha); + + // Write out animation speed + cf_WriteFloat (outfile,texpage->tex_struct.speed); + + // Write out sliding values + cf_WriteFloat (outfile,texpage->tex_struct.slide_u); + cf_WriteFloat (outfile,texpage->tex_struct.slide_v); + + + // Write reflectivity + cf_WriteFloat (outfile,texpage->tex_struct.reflectivity); + + // Write corona type + cf_WriteByte (outfile,texpage->tex_struct.corona_type); + + // Write damage + cf_WriteInt (outfile,texpage->tex_struct.damage); + + // Write flags + cf_WriteInt (outfile,texpage->tex_struct.flags); + + // Write out the procedural + if (texpage->tex_struct.flags & TF_PROCEDURAL) + { + // Write procedural palette + for (i=0;i<255;i++) + { + ushort val=texpage->proc_palette[i]; + cf_WriteShort (outfile,val); + } + + // Write procedural globals + cf_WriteByte (outfile,texpage->proc_heat); + cf_WriteByte (outfile,texpage->proc_light); + cf_WriteByte (outfile,texpage->proc_thickness); + cf_WriteFloat (outfile,texpage->proc_evaluation_time); + + cf_WriteFloat (outfile,texpage->osc_time); + cf_WriteByte (outfile,texpage->osc_value); + + // Write out static procedurals + cf_WriteShort (outfile,texpage->num_proc_elements); + for (i=0;inum_proc_elements;i++) + { + cf_WriteByte (outfile,texpage->proc_type[i]); + cf_WriteByte (outfile,texpage->proc_frequency[i]); + cf_WriteByte (outfile,texpage->proc_speed[i]); + cf_WriteByte (outfile,texpage->proc_size[i]); + cf_WriteByte (outfile,texpage->proc_x1[i]); + cf_WriteByte (outfile,texpage->proc_y1[i]); + cf_WriteByte (outfile,texpage->proc_x2[i]); + cf_WriteByte (outfile,texpage->proc_y2[i]); + } + } + + cf_WriteString (outfile,texpage->sound_name); + cf_WriteFloat(outfile,texpage->tex_struct.sound_volume); + + EndManagePage (outfile,offset); +} + +void mng_InitTexturePage (mngs_texture_page *texpage) +{ + memset (texpage,0,sizeof(mngs_texture_page)); + texpage->proc_thickness=4; + texpage->proc_heat=200; + texpage->proc_light=1; + texpage->num_proc_elements=0; + texpage->proc_evaluation_time=0; + texpage->osc_time=0; + texpage->osc_value=8; + + texpage->tex_struct.alpha=1.0; + texpage->tex_struct.speed=1.0; + texpage->tex_struct.reflectivity=.5; + texpage->tex_struct.corona_type=0; + texpage->tex_struct.slide_v=0; + texpage->tex_struct.slide_u=0; + texpage->tex_struct.bumpmap=-1; + texpage->tex_struct.sound = -1; + texpage->tex_struct.sound_volume = 1.0; + + strcpy (texpage->bitmap_name,""); + strcpy (texpage->destroy_name,""); + strcpy (texpage->sound_name,""); +} + + +// Reads a texture page from an open file. Returns 0 on error. +int mng_ReadTexturePage (CFILE *infile,mngs_texture_page *texpage) +{ + int done=0; + char command; + ubyte len; + int i; + int proc_index=0; + + if (!Old_table_method) + return mng_ReadNewTexturePage (infile,texpage); + + ASSERT (infile!=NULL); + mng_InitTexturePage (texpage); + + while (!done) + { + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + + switch (command) + { + case TEXPAGE_COMMAND_END: + done=1; + break; + case TEXPAGE_COMMAND_FLAGS: + texpage->tex_struct.flags=cf_ReadInt(infile); + break; + case TEXPAGE_COMMAND_LIGHTING: + cf_ReadInt(infile); // Ignored + break; + case TEXPAGE_COMMAND_LIGHTING_FLOAT: + texpage->tex_struct.r=cf_ReadFloat(infile); + texpage->tex_struct.g=cf_ReadFloat(infile); + texpage->tex_struct.b=cf_ReadFloat(infile); + break; + case TEXPAGE_COMMAND_DAMAGE: + texpage->tex_struct.damage=cf_ReadInt(infile); + break; + case TEXPAGE_COMMAND_REFLECT: + texpage->tex_struct.reflectivity=cf_ReadFloat(infile); + break; + case TEXPAGE_COMMAND_CORONA: + texpage->tex_struct.corona_type=cf_ReadByte(infile); + break; + case TEXPAGE_COMMAND_ALPHA: + texpage->tex_struct.alpha=cf_ReadFloat (infile); + break; + case TEXPAGE_COMMAND_SPEED: + texpage->tex_struct.speed=cf_ReadFloat (infile); + break; + case TEXPAGE_COMMAND_SLIDE_FLOAT: + texpage->tex_struct.slide_u=cf_ReadFloat (infile); + texpage->tex_struct.slide_v=cf_ReadFloat (infile); + break; + case TEXPAGE_COMMAND_BITMAP_NAME: // the name of the texture's bitmap + for (i=0;ibitmap_name[i]=cf_ReadByte (infile); + break; + case TEXPAGE_COMMAND_DESTROY_NAME: // the name of the texture's bitmap + for (i=0;idestroy_name[i]=cf_ReadByte (infile); + break; + case TEXPAGE_COMMAND_NAME: + for (i=0;itex_struct.name[i]=cf_ReadByte (infile); + break; + case TEXPAGE_COMMAND_NULL_NAME: + cf_ReadString(texpage->tex_struct.name,PAGENAME_LEN,infile); + break; + case TEXPAGE_COMMAND_BITMAP_NULL_NAME: // the name of the texture's bitmap + cf_ReadString(texpage->bitmap_name,PAGENAME_LEN,infile); + break; + case TEXPAGE_COMMAND_DESTROY_NULL_NAME: // the name of the texture's bitmap + cf_ReadString(texpage->destroy_name,PAGENAME_LEN,infile); + break; + case TEXPAGE_COMMAND_FIRST_PROC_PAL: + for (i=0;i<255;i++) + { + ushort val=cf_ReadByte (infile); + val<<=8; + texpage->proc_palette[i]&=0xFF; + texpage->proc_palette[i]|=val; + } + break; + case TEXPAGE_COMMAND_SECOND_PROC_PAL: + for (i=0;i<255;i++) + { + ubyte val=cf_ReadByte (infile); + texpage->proc_palette[i]&=0xFF00; + texpage->proc_palette[i]|=val; + } + break; + case TEXPAGE_COMMAND_EXTRA_PROCEDURAL: + texpage->proc_heat=cf_ReadByte (infile); + texpage->proc_light=cf_ReadByte (infile); + texpage->proc_thickness=cf_ReadByte (infile); + break; + case TEXPAGE_COMMAND_EXTRA_PROCEDURAL2: + texpage->proc_evaluation_time=cf_ReadFloat (infile); + break; + case TEXPAGE_COMMAND_PROCEDURAL: + texpage->proc_type[proc_index]=cf_ReadByte (infile); + texpage->proc_frequency[proc_index]=cf_ReadByte (infile); + texpage->proc_speed[proc_index]=cf_ReadByte (infile); + texpage->proc_size[proc_index]=cf_ReadByte (infile); + texpage->proc_x1[proc_index]=cf_ReadByte (infile); + texpage->proc_y1[proc_index]=cf_ReadByte (infile); + + texpage->proc_x2[proc_index]=cf_ReadByte (infile); + texpage->proc_y2[proc_index]=cf_ReadByte (infile); + + proc_index++; + break; + default: + for (i=0;itex_struct.alpha<.9999) + texpage->tex_struct.flags|=TF_ALPHA; + else + texpage->tex_struct.flags&=~TF_ALPHA; + + texpage->num_proc_elements=proc_index; + + if (texpage->num_proc_elements==0) + texpage->tex_struct.flags &=~TF_PROCEDURAL; + + if (!strnicmp(texpage->destroy_name,"INVALID",7)) + strcpy (texpage->destroy_name,""); + + texpage->tex_struct.used=1; + + + return 1; // successfully read +} + +// Reads a texture page from an open file. Returns 0 on error. +int mng_ReadNewTexturePage (CFILE *infile,mngs_texture_page *texpage) +{ + int i; + + ASSERT (infile!=NULL); + mng_InitTexturePage (texpage); + + int version=cf_ReadShort (infile); + + cf_ReadString(texpage->tex_struct.name,PAGENAME_LEN,infile); + cf_ReadString(texpage->bitmap_name,PAGENAME_LEN,infile); + cf_ReadString(texpage->destroy_name,PAGENAME_LEN,infile); + + texpage->tex_struct.r=cf_ReadFloat (infile); + texpage->tex_struct.g=cf_ReadFloat (infile); + texpage->tex_struct.b=cf_ReadFloat (infile); + texpage->tex_struct.alpha=cf_ReadFloat (infile); + + texpage->tex_struct.speed=cf_ReadFloat (infile); + + texpage->tex_struct.slide_u=cf_ReadFloat (infile); + texpage->tex_struct.slide_v=cf_ReadFloat (infile); + texpage->tex_struct.reflectivity=cf_ReadFloat (infile); + + texpage->tex_struct.corona_type=cf_ReadByte (infile); + texpage->tex_struct.damage=cf_ReadInt (infile); + texpage->tex_struct.flags=cf_ReadInt (infile); + + if (texpage->tex_struct.flags & TF_PROCEDURAL) + { + for (i=0;i<255;i++) + { + ushort val=cf_ReadShort (infile); + texpage->proc_palette[i]=val; + } + + texpage->proc_heat=cf_ReadByte (infile); + texpage->proc_light=cf_ReadByte (infile); + texpage->proc_thickness=cf_ReadByte (infile); + texpage->proc_evaluation_time=cf_ReadFloat (infile); + + if (version>=6) + { + texpage->osc_time=cf_ReadFloat(infile); + texpage->osc_value=cf_ReadByte (infile); + } + + texpage->num_proc_elements=cf_ReadShort (infile); + + if (texpage->num_proc_elements>MAX_PROC_ELEMENTS) + { + mprintf ((0,"Warning! Too many procedural elements!\n")); + Int3(); + } + + + for (i=0;inum_proc_elements;i++) + { + texpage->proc_type[i]=cf_ReadByte (infile); + texpage->proc_frequency[i]=cf_ReadByte (infile); + texpage->proc_speed[i]=cf_ReadByte (infile); + texpage->proc_size[i]=cf_ReadByte (infile); + texpage->proc_x1[i]=cf_ReadByte (infile); + texpage->proc_y1[i]=cf_ReadByte (infile); + + texpage->proc_x2[i]=cf_ReadByte (infile); + texpage->proc_y2[i]=cf_ReadByte (infile); + } + } + + if (texpage->tex_struct.flags & TF_PROCEDURAL) + { + if (texpage->num_proc_elements==0) + texpage->tex_struct.flags &=~TF_PROCEDURAL; + } + + if (!strnicmp(texpage->destroy_name,"INVALID",7)) + strcpy (texpage->destroy_name,""); + + if (version >= 5) { + + if (version<7) + { + // Kill buggy version of sound resolving code + texpage->tex_struct.sound = cf_ReadInt(infile); + texpage->tex_struct.sound=-1; + strcpy (texpage->sound_name,""); + } + else + cf_ReadString(texpage->sound_name,PAGENAME_LEN,infile); + + texpage->tex_struct.sound_volume = cf_ReadFloat(infile); + } + else { + texpage->tex_struct.sound = -1; + texpage->tex_struct.sound_volume = 1.0; + } + + texpage->tex_struct.used=1; + + return 1; // successfully read +} + +// Given some texture names, finds them in the table file and deletes them +// If local is 1, deletes from the local table file +int mng_DeleteTexPageSeries (char *names[],int num_textures,int local) +{ + CFILE *infile,*outfile; + ubyte pagetype,replaced=0; + int done=0; + int deleted=0; + memset(&texpage1, 0, sizeof(mngs_texture_page)); + + if (local) + infile=cfopen (LocalTableFilename,"rb"); + else + infile=cfopen (TableFilename,"rb"); + + if (!infile) + { + mprintf ((0,"Couldn't open table file to delete texture!\n")); + Int3(); + return 0; + } + + if (local) + outfile=cfopen (LocalTempTableFilename,"wb"); + else + outfile=cfopen (TempTableFilename,"wb"); + + if (!outfile) + { + mprintf ((0,"Couldn't open temp table file to delete texture!\n")); + cfclose (infile); + Int3(); + return 0; + } + + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + + // If not a texture page, just read it in and write it right back out + if (pagetype!=PAGETYPE_TEXTURE) + { + mng_ReadWriteDummyPage (infile,outfile,pagetype); + continue; + } + + mng_ReadNewTexturePage (infile,&texpage1); + + int found=-1; + for (int i=0;itex_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + + cfclose (infile); + + if (!found && first_try) { + done = first_try = 0; + infile=cfopen ("extra.gam","rb"); + if (infile) + goto try_again; + } + + return found; // successful! +} + +// Given a texture page, allocs a texture and calls AssignTexPageToTexture to actually +// load bitmaps and values. Rturns texture handle on success, -1 if fail +int mng_SetAndLoadTexture (mngs_texture_page *texpage,CFILE *infile) +{ + int n; + + n=AllocTexture(); + if (n<0) + return -1; + if (!mng_AssignTexPageToTexture(texpage,n,infile)) + return -1; + + return n; +} + +//extern char Texture256Names[][80]; +//extern int Num_256s; +// Given a texpage and a texture handle, attempts to make texture n correspond to +// to the texpage. +// Returns 1 on success, 0 otherwise +int mng_AssignTexPageToTexture(mngs_texture_page *texpage,int n,CFILE *infile) +{ + texture *tex=&GameTextures[n]; + int bm_handle; + + // copy our values + memcpy (tex,&texpage->tex_struct,sizeof(texture)); + + // Check to see if this image differs from the one on the net + // If so, make a local copy + // If this is a release, don't do any of this stuff + #ifndef RELEASE + if (Network_up) + { + char str[200]; + char netstr[200]; + + ddio_MakePath (str,LocalManageGraphicsDir,texpage->bitmap_name,NULL); + ddio_MakePath (netstr,ManageGraphicsDir,texpage->bitmap_name,NULL); + + UpdatePrimitive (str,netstr,texpage->bitmap_name,PAGETYPE_TEXTURE,tex->name); + } + #endif + + // Try and load our textures bitmaps from the disk + ubyte pageable; + ubyte mipped=1; + + if (Network_up) + pageable=0; + else + pageable=1; + + pageable=1; + + /*int done=0; + for (int i=0;iname)) + { + tex->flags&=~(TF_TEXTURE_64|TF_TEXTURE_32); + tex->flags|=TF_TEXTURE_256; + mprintf ((0,"Found 256...%s\n",tex->name)); + int len=strlen (texpage->bitmap_name); + texpage->bitmap_name[len-3]='t'; + texpage->bitmap_name[len-2]='g'; + texpage->bitmap_name[len-1]='a'; + + done=1; + } + }*/ + + if (tex->flags & TF_TEXTURE_64) + { + bm_handle=LoadTextureImage (texpage->bitmap_name,NULL,SMALL_TEXTURE,mipped,pageable); + } + else if (tex->flags & TF_TEXTURE_32) + { + bm_handle=LoadTextureImage (texpage->bitmap_name,NULL,TINY_TEXTURE,mipped,pageable); + } + else if (tex->flags & TF_TEXTURE_256) + { + mipped=0; + bm_handle=LoadTextureImage (texpage->bitmap_name,NULL,HUGE_TEXTURE,mipped,pageable); + } + else + bm_handle=LoadTextureImage (texpage->bitmap_name,NULL,NORMAL_TEXTURE,mipped,pageable); + + + if (bm_handle<0) + { + mprintf ((0,"Couldn't load bitmap '%s' in AssignTexPage...\n",texpage->bitmap_name)); + tex->bm_handle=0; + return 0; + } + else + tex->bm_handle=bm_handle; + + + // Load the destroyed texture + if (tex->flags & TF_DESTROYABLE) + { + // Are destroyed and non-destroyed the same? + if (!(stricmp (texpage->destroy_name,tex->name))) + { + tex->flags &=~TF_DESTROYABLE; + tex->destroy_handle=-1; + } + else + { + + int destroy_handle=mng_GetGuaranteedTexturePage (texpage->destroy_name,infile); + if (destroy_handle<0) + tex->destroy_handle=0; + else + tex->destroy_handle=destroy_handle; + } + + } + + // Get sound if needed + if (stricmp ("",texpage->sound_name)) + tex->sound=mng_GetGuaranteedSoundPage (texpage->sound_name,infile); + else + tex->sound=-1; + + + // Set lighting flag + if (tex->r>0 || tex->g>0 || tex->b>0) + tex->flags |=TF_LIGHT; + else + tex->flags &=~TF_LIGHT; + + // Set procedural if there is one + if (tex->flags & TF_PROCEDURAL) + { + if (tex->procedural!=NULL) + FreeProceduralForTexture (n); + + if (texpage->num_proc_elements==0) + { + tex->flags &=~TF_PROCEDURAL; + + } + else + { + AllocateProceduralForTexture (n); + AllocateStaticProceduralsForTexture (n,texpage->num_proc_elements); + GameTextures[n].procedural->num_static_elements=texpage->num_proc_elements; + GameTextures[n].procedural->heat=texpage->proc_heat; + GameTextures[n].procedural->light=texpage->proc_light; + GameTextures[n].procedural->thickness=texpage->proc_thickness; + GameTextures[n].procedural->evaluation_time=texpage->proc_evaluation_time; + GameTextures[n].procedural->osc_time=texpage->osc_time; + GameTextures[n].procedural->osc_value=texpage->osc_value; + + // Last 2 colors are the same + texpage->proc_palette[255]=texpage->proc_palette[254]; + + memcpy (GameTextures[n].procedural->palette,texpage->proc_palette,256*2); + + for (int i=0;inum_proc_elements;i++) + { + static_proc_element *proc=&GameTextures[n].procedural->static_proc_elements[i]; + proc->type=texpage->proc_type[i]; + proc->frequency=texpage->proc_frequency[i]; + proc->speed=texpage->proc_speed[i]; + proc->size=texpage->proc_size[i]; + proc->x1=texpage->proc_x1[i]; + proc->y1=texpage->proc_y1[i]; + proc->x2=texpage->proc_x2[i]; + proc->y2=texpage->proc_y2[i]; + + } + } + } + return 1; +} + +// Copies values from a texture into a texture_page +void mng_AssignTextureToTexPage(int n,mngs_texture_page *texpage) +{ + texture *tex=&GameTextures[n]; + + memcpy (&texpage->tex_struct,tex,sizeof(texture)); + + if (tex->bm_handle!=-1) + { + if (tex->flags & TF_ANIMATED) + strcpy (texpage->bitmap_name,GameVClips[tex->bm_handle].name); + else + strcpy (texpage->bitmap_name,GameBitmaps[tex->bm_handle].name); + } + else + strcpy (texpage->bitmap_name,""); + + if (tex->destroy_handle!=-1) + strcpy (texpage->destroy_name,GameTextures[tex->destroy_handle].name); + else + strcpy (texpage->destroy_name,""); + + if (texpage->tex_struct.sound!=-1) + strcpy (texpage->sound_name,Sounds[texpage->tex_struct.sound].name); + else + strcpy (texpage->sound_name,""); + + + // assign procedural if there is one + if (tex->flags & TF_PROCEDURAL) + { + memcpy (texpage->proc_palette,tex->procedural->palette,256*2); + texpage->num_proc_elements=tex->procedural->num_static_elements; + texpage->proc_heat=tex->procedural->heat; + texpage->proc_light=tex->procedural->light; + texpage->proc_thickness=tex->procedural->thickness; + texpage->proc_evaluation_time=tex->procedural->evaluation_time; + texpage->osc_time=tex->procedural->osc_time; + texpage->osc_value=tex->procedural->osc_value; + + for (int i=0;iprocedural->num_static_elements;i++) + { + texpage->proc_type[i]=tex->procedural->static_proc_elements[i].type; + texpage->proc_frequency[i]=tex->procedural->static_proc_elements[i].frequency; + texpage->proc_speed[i]=tex->procedural->static_proc_elements[i].speed; + texpage->proc_size[i]=tex->procedural->static_proc_elements[i].size; + + texpage->proc_x1[i]=tex->procedural->static_proc_elements[i].x1; + texpage->proc_y1[i]=tex->procedural->static_proc_elements[i].y1; + texpage->proc_x2[i]=tex->procedural->static_proc_elements[i].x2; + texpage->proc_y2[i]=tex->procedural->static_proc_elements[i].y2; + } + } +} + +// Loads in a texture page +void mng_LoadNetTexturePage(CFILE *infile,bool overlay) +{ + memset(&texpage1, 0, sizeof(mngs_texture_page)); + + if (mng_ReadNewTexturePage (infile,&texpage1)) + { + int n; + n = FindTextureName(texpage1.tex_struct.name); + if(n!=-1) + { + if(overlay) + { + mprintf((0,"OVERLAYING TEXTURE %s\n",texpage1.tex_struct.name)); + mng_FreePagetypePrimitives (PAGETYPE_TEXTURE,texpage1.tex_struct.name,0); + mng_AssignTexPageToTexture(&texpage1,n); + } + return; + } + + int ret=mng_SetAndLoadTexture (&texpage1,infile); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load texpage named %s!\n",texpage1.tex_struct.name)); +} + +// Loads in a texture page from a file, superseding any texture with that name +// already in memory +void mng_LoadLocalTexturePage(CFILE *infile) +{ + int ok=0; + memset(&texpage1, 0, sizeof(mngs_texture_page)); + + if (mng_ReadNewTexturePage (infile,&texpage1)) + { + int i=FindTextureName (texpage1.tex_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + //int locked; + + strcpy (pl.name,texpage1.tex_struct.name); + pl.pagetype=PAGETYPE_TEXTURE; + + /*if (Network_up) + { + locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + + ok=1; + bool need_to_load_page = true; + + if (Loading_addon_table!=-1) + { + AddOnTablefile *addon; + int tidx; + + // see if we really need to load this page + //check to see if we already have loaded this page (because it was + //a dependancy of another) + addon = &AddOnDataTables[Loading_addon_table]; + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_TEXTURE && + !stricmp(addon->Addon_tracklocks[tidx].name,texpage1.tex_struct.name) ) + { + // found it!! + mprintf((0,"TexturePage: %s previously loaded\n",texpage1.tex_struct.name)); + need_to_load_page = false; + break; + } + } + } + + if(need_to_load_page) + { + mng_FreePagetypePrimitives (PAGETYPE_TEXTURE,texpage1.tex_struct.name,0); + mng_AssignTexPageToTexture (&texpage1,i); + + // For addon data + if (Loading_addon_table!=-1) + { + // this is an overlay of some sort..see which we are overlaying + int overlay = 1; + int addidx,tidx; + bool found = false; + for(addidx = Num_addon_tables-1; addidx>=0; addidx--) + { + if(addidx==Loading_addon_table) + continue; + AddOnTablefile *addon = &AddOnDataTables[addidx]; + + // look for the page in this table file + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_TEXTURE && + !stricmp(addon->Addon_tracklocks[tidx].name,texpage1.tex_struct.name) ) + { + // found it!! + found = true; + overlay = addidx+2; + break; + } + } + + if(found) + break; + } + + mng_PushAddonPage (PAGETYPE_TEXTURE,texpage1.tex_struct.name,overlay); + } + } + } + else + { + // This is a local texture that has never been checked in + if ((i=mng_SetAndLoadTexture (&texpage1,infile))<0) + ok=0; + else ok=1; + + // For addon data + if (ok && Loading_addon_table!=-1) + mng_PushAddonPage (PAGETYPE_TEXTURE,texpage1.tex_struct.name,0); + } + + //ASSERT (ok==1); // There was problem loading a page! + if (ok != 1) + DataError("Cannot load texture <%s>, bitmap name <%s>\n",texpage1.tex_struct.name,texpage1.bitmap_name); + + if (Loading_addon_table==-1) + mng_AllocTrackLock (texpage1.tex_struct.name,PAGETYPE_TEXTURE); + + } + else + mprintf ((0,"Could not load texpage named %s!\n",texpage1.tex_struct.name)); + +} + +#include "mem.h" + +// First searches through the texture index to see if the texture is already +// loaded. If not, searches in the table file and loads it. +// Returns index of texture found, -1 if not +int mng_GetGuaranteedTexturePage (char *name,CFILE *infile) +{ + int i; + + // See if its in memory + i=FindTextureName (name); + if (i!=-1) + return i; + + mngs_texture_page *texpage = (mngs_texture_page *) mem_malloc(sizeof(*texpage)); + + // Not in memory. Load it from the table file. Start searching from the + // current spot in the open table file + int ret=mng_FindSpecificTexPage (name,texpage,infile?infile->position:0); + if (!ret) + return -1; + + // We've found it in the table file, now load it. + ret=mng_SetAndLoadTexture (texpage,infile); + //ASSERT (ret>=0); + if (ret < 0) + DataError("Cannot load texture <%s>, bitmap name <%s>\n",texpage->tex_struct.name,texpage->bitmap_name); + + if(Loading_addon_table!=-1) + { + // we're loading addon table pages, this will not overlay anything + mng_PushAddonPage (PAGETYPE_TEXTURE,texpage->tex_struct.name,0); + } + mem_free(texpage); + + return ret; +} + + diff --git a/manage/texpage.h b/manage/texpage.h new file mode 100644 index 000000000..9d49c8762 --- /dev/null +++ b/manage/texpage.h @@ -0,0 +1,89 @@ +#ifndef TEXPAGE_H +#define TEXPAGE_H + + +#include "manage.h" +#include "CFILE.H" +#include "pstypes.h" +#include "procedurals.h" + +typedef struct +{ + char bitmap_name[PAGENAME_LEN]; // filename for the bitmap associated with this texture + char destroy_name[PAGENAME_LEN]; // filename for the destroyed bitmap + char sound_name[PAGENAME_LEN]; // filename for the destroyed bitmap + texture tex_struct; + + int num_proc_elements; + + ubyte proc_heat,proc_light,proc_thickness,osc_value; + + float proc_evaluation_time,osc_time; + + ubyte proc_type[MAX_PROC_ELEMENTS]; + + ubyte proc_frequency[MAX_PROC_ELEMENTS]; + ubyte proc_speed[MAX_PROC_ELEMENTS]; + + ubyte proc_size[MAX_PROC_ELEMENTS]; + + ubyte proc_x1[MAX_PROC_ELEMENTS]; + ubyte proc_y1[MAX_PROC_ELEMENTS]; + ubyte proc_x2[MAX_PROC_ELEMENTS]; + ubyte proc_y2[MAX_PROC_ELEMENTS]; + + ushort proc_palette[256]; +} mngs_texture_page; + + +// Texture page functions +//--------------------------------------------------------------- + +// Reads a texture page from an open file. Returns 0 on error. + +int mng_ReadTexturePage (CFILE *infile,mngs_texture_page *texpage); + + +// Given an open file pointer and a texture handle, writes that texture page out +void mng_WriteTexturePage (CFILE *outfile,mngs_texture_page *texpage); + +// Reads a texture page from an open file. Returns 0 on error. + +int mng_ReadNewTexturePage (CFILE *infile,mngs_texture_page *texpage); + + +// Given an open file pointer and a texture handle, writes that texture page out +void mng_WriteNewTexturePage (CFILE *outfile,mngs_texture_page *texpage); + +// Given a texture page, allocs a texture and loads that textures associated bitmap +// returns texture handle on success, -1 if fail +int mng_SetAndLoadTexture (mngs_texture_page *texpage); + +// Reads in a texture page from the local table file, superseding any texture +// already in RAM with that same name +void mng_LoadLocalTexturePage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetTexturePage (CFILE *,bool overlay=false); + +// Reads in the texpage named "name" into texpage struct +// Returns 0 on error, else 1 if all is good +int mng_FindSpecificTexPage (char *name,mngs_texture_page *texpage,int offset=0); + +// Given a texpage and a texture handle, attempts to make texture n correspond to +// to the texpage. +int mng_AssignTexPageToTexture(mngs_texture_page *texpage,int n,CFILE *infile=NULL); + +// Copies values from a texture_page into a texture +void mng_AssignTextureToTexPage(int n,mngs_texture_page *texpage); + +// First searches through the texture index to see if the texture is already +// loaded. If not, searches in the table file and loads it. +// Returns index of texture found, -1 if not +int mng_GetGuaranteedTexturePage (char *name,CFILE *infile=NULL); + +// Given some texture names, finds them in the table file and deletes them +// If local is 1, deletes from the local table file +int mng_DeleteTexPageSeries (char *names[],int num_textures,int local); + +#endif diff --git a/manage/weaponpage.cpp b/manage/weaponpage.cpp new file mode 100644 index 000000000..c58dc0bff --- /dev/null +++ b/manage/weaponpage.cpp @@ -0,0 +1,1911 @@ +/* + * $Logfile: /DescentIII/Main/manage/weaponpage.cpp $ + * $Revision: 71 $ + * $Date: 10/08/01 4:20p $ + * $Author: Matt $ + * + * For loading/saving of weapon types + * + * $Log: /DescentIII/Main/manage/weaponpage.cpp $ + * + * 71 10/08/01 4:20p Matt + * Added system to check for errors when reading in add-on data. + * + * 69 9/06/01 10:32a Matt + * Added code to fix problem poping add-on pages when the original pages + * were in the extra.gam file. + * + * 68 10/26/99 4:26p Jeff + * handle extra.gam + * + * 67 10/26/99 3:31p Jeff + * handle extra.gam addon tablefile + * + * 66 9/18/99 8:49p Jeff + * fixed bug with addon pages that have dependencies on other pages in the + * addon tablefile + * + * 65 8/11/99 5:32p Jeff + * changes to fix addon tablefile support so it works correctly + * + * 64 7/22/99 4:38p Jason + * made EMD time out faster to help balance it out + * + * 63 4/14/99 1:34a Jeff + * fixed case mismatched #includes + * + * 62 4/12/99 12:49p Jeff + * added recoil_force to weapon's page + * + * 61 3/08/99 7:48p Jeff + * oem hack for napalm barrel + * + * 60 3/04/99 4:47p Jason + * temp fix (ie BAD HACK) for OEM table file woes + * + * 59 3/04/99 1:47p Jason + * fixed some manage problems + * + * 58 2/22/99 2:03p Jason + * added different damages for players and generics + * + * 57 1/13/99 7:05a Jeff + * put #ifdef around #include + * + * 56 12/29/98 4:30p Jason + * added add-on data functionality + * + * 55 11/06/98 12:35p Jason + * more speedups for manage system + * + * 54 11/05/98 7:55p Jason + * changes for new manage system + * + * 53 11/02/98 6:02p Jason + * made yes network updates much faster + * + * 52 10/15/98 6:46p Chris + * Added custom size for weapons + * + * 51 10/09/98 2:55p Jason + * fixed bug with my last rev + * + * 50 10/09/98 2:27p Jason + * reorganized table file system + * + * 49 10/08/98 10:03p Jason + * more filtered table file stuff + * + * 48 9/18/98 3:58p Jason + * change weapon reordering to do countermeasure weapons after generics + * + * 47 9/01/98 4:41p Matt + * Removed obsolete fields in the weapon structure + * + * 46 8/24/98 2:37p Jason + * made table file more efficient with regards to invalid names + * + * 45 8/06/98 1:00p Chris + * Added new homing flags + * + * 44 7/30/98 11:09a Jason + * added weapons that freeze and deform terrain + * + * 43 7/28/98 12:29p Jason + * chagned text string + * + * 42 7/01/98 12:11p Jason + * added countermeasures + * + * 41 6/26/98 5:26p Sean + * fixed some remaining weapons issues + * + * 40 6/22/98 6:26p Jason + * added gravity field effect for weapons + * + * 39 6/19/98 12:04p Jason + * + * 38 6/12/98 1:06p Jason + * added smart loading from local table file + * + * 37 5/25/98 8:36p Matt + * Added code to set different sizes for different weapon scorch marks. + * Also, don't leave scorch marks on lights. + * + * 36 5/25/98 6:39p Jason + * got icons working for weapons + * + * 35 5/25/98 4:16p Jason + * added more verbose mprintfs + * + * 34 5/22/98 12:34p Matt + * Added scorch mark/bullet hole system. + * + * 33 5/19/98 5:09a Chris + * Initial values are better + * + * 32 5/19/98 5:02a Chris + * More adjustments + * + * 31 5/19/98 4:42a Chris + * Added shockwave's -- enjoy. :) + * + * 30 5/13/98 6:35p Chris + * Fixed problems with hit die dot + * + * 29 5/07/98 1:41p Chris + * Added hit_death_dot + * + * 28 5/04/98 6:08p Jason + * sped up predictive pagefile loading + * + * 27 4/07/98 3:31p Jason + * got particle effects working with weapons + * + * 26 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 25 2/23/98 1:22p Jason + * made FindSpecific* read from the local drive, not the net drive - when + * starting the editor + * + * 24 2/16/98 11:42a Jason + * added better error checking for checking out files + * + * 23 2/11/98 3:52p Jason + * + * 22 2/11/98 2:04p Jason + * got spawning weapons working + * + * 21 2/05/98 6:29p Jason + * added user settable explode time/size + * + * 20 2/04/98 9:28p Jason + * added some new weapons effects + * + * 19 1/23/98 6:25p Jason + * Got spray weapons working + * + * 18 1/15/98 6:22p Jason + * added safety checks so the network won't copy over a primitive you have + * held locally + * + * 17 12/22/97 6:19p Chris + * Moved weapon battery firing sound off the projectile (weapon) and into + * the weapon battery. + * + * 16 12/11/97 11:55a Jason + * better error checking for dependant weapon pages + * + * 15 12/04/97 6:59p Jason + * fixed stupid off-by-one bug in cf_ReadString + * + * 14 12/04/97 12:15p Jason + * gave designers the ability to set their own weapon-to-wall hit vclips + * + * 13 12/03/97 11:54a Jason + * added designer-settable smoketrails + * + * 12 12/01/97 9:54a Chris + * Added support for concussive forces, generalized robot collisions to + * generic collisions + * + * 11 11/14/97 9:55p Jason + * took out size fix because we changed out minds + * + * 10 11/14/97 9:31p Jason + * automatically adjust size of weapon polymodels + * + * 9 10/30/97 5:00p Jason + * Made weapons use generic lighting code + * + * 8 10/20/97 4:46p Jason + * changes for explosions + * + * 7 10/15/97 5:20p Jason + * did a HUGE overhaul of the bitmap system + * + * 6 9/10/97 11:44a Chris + * Added major functionality for wb info + * FIXED a big remapping bug + * + * 5 9/03/97 3:54p Jason + * got objects to cast light + * + * 4 8/08/97 11:44a Jason + * got rid of annoying sound loading of a blank string + * + * 3 8/07/97 5:27p Chris + * Expanded the weapon system + * + * 2 8/06/97 4:34p Chris + * Expanded the weapons page + * + * 14 5/15/97 6:24p Mark + * fixed small bugs with sound loading invalid names + * + * 13 5/08/97 12:41p Jason + * made manage system work with device dependant path names + * + * 12 4/28/97 6:46p Jason + * made ships have multiple gun points + * + * 11 4/25/97 6:16p Jason + * added switcheroo function + * + * 10 4/25/97 2:18p Jason + * added fire_delay functionality + * + * 9 4/24/97 5:42p Jason + * got fireball vclips working + * + * 8 4/23/97 3:04p Jason + * added more stuff to weapon pages + * + * 7 4/16/97 11:50a Jason + * finally got weapons to fire + * + * 6 4/14/97 5:09p Jason + * fixed dumb bug with weapon flags + * + * 5 4/14/97 4:35p Jason + * added thrust field to page file + * + * 4 4/14/97 1:50p Jason + * first pass at getting weapons to fire + * + * 3 4/08/97 2:25a Chris + * Fixed a problem with uninitialized data. In addition it + * fixed a problem with the .used flag that would happen + * 1 out of 2^(sizeof(used_flag)) times (it would be zero + * when it was supposed to be non-zero) + * + * 2 3/31/97 4:35p Jason + * added weapon page functionality + * + * 1 3/31/97 3:40p Jason + * weaponpage implementation file + * + * $NoKeywords: $ + */ +#if defined(WIN32) +#include +#endif + +#include "CFILE.H" +#include "manage.h" +#include "weapon.h" +#include "weaponpage.h" +#include "mono.h" +#include "pserror.h" +#include "vclip.h" +#include "polymodel.h" +#include "soundpage.h" +#include "soundload.h" +#include "ddio.h" +#include "gametexture.h" +#include "texpage.h" +#include +#include "sounds.h" +#include "genericpage.h" +#include "args.h" + +// weaponpage commands that are read/written +// A command is followed by a byte count describing how many bytes +// are in the data for the command +#define WEAPONPAGE_COMMAND_NAME 1 +#define WEAPONPAGE_COMMAND_END 2 +#define WEAPONPAGE_COMMAND_HUD_IMAGE_NAME 3 +#define WEAPONPAGE_COMMAND_VERSION 4 +#define WEAPONPAGE_COMMAND_MASS 5 +#define WEAPONPAGE_COMMAND_DRAG 6 +#define WEAPONPAGE_COMMAND_FLAGS 7 +#define WEAPONPAGE_COMMAND_FIRE_IMAGE_NAME 8 +//#define WEAPONPAGE_COMMAND_THRUST 9 // not used anymore +#define WEAPONPAGE_COMMAND_FULL_THRUST 10 +#define WEAPONPAGE_COMMAND_SOUND_NAME 11 +#define WEAPONPAGE_COMMAND_AMMO_USAGE 12 +#define WEAPONPAGE_COMMAND_ENERGY_USAGE 13 +//#define WEAPONPAGE_COMMAND_EXPLODE_NAME 14 // exploding vclip name (not used anymore) +#define WEAPONPAGE_COMMAND_FIRE_DELAY 15 +#define WEAPONPAGE_COMMAND_NUM_SHOTS 16 +#define WEAPONPAGE_COMMAND_PHYS_FLAGS 17 +#define WEAPONPAGE_COMMAND_SIZE 18 +#define WEAPONPAGE_COMMAND_IMPACT_SIZE 19 +#define WEAPONPAGE_COMMAND_FLASH_SIZE 20 +#define WEAPONPAGE_COMMAND_ROT_DRAG 21 +#define WEAPONPAGE_COMMAND_FULL_ROT_THRUST 22 +#define WEAPONPAGE_COMMAND_MAX_BOUNCES 23 +#define WEAPONPAGE_COMMAND_INIT_VELOCITY 24 +#define WEAPONPAGE_COMMAND_INIT_ROT_VEL_X 25 +#define WEAPONPAGE_COMMAND_INIT_ROT_VEL_Y 26 +#define WEAPONPAGE_COMMAND_INIT_ROT_VEL_Z 27 +#define WEAPONPAGE_COMMAND_LIFE_TIME 28 +#define WEAPONPAGE_COMMAND_THRUST_TIME 29 +#define WEAPONPAGE_COMMAND_WIGGLE_AMP 30 +#define WEAPONPAGE_COMMAND_WIGGLE_FREQ 31 +#define WEAPONPAGE_COMMAND_COEFF_RESTITUTION 32 +#define WEAPONPAGE_COMMAND_LIGHT_CAST 33 +#define WEAPONPAGE_COMMAND_DAMAGE 34 +#define WEAPONPAGE_COMMAND_BETTER_LIGHT_CAST 35 +#define WEAPONPAGE_COMMAND_IMPACT_TIME 36 +#define WEAPONPAGE_COMMAND_SMOKE_NAME 37 +#define WEAPONPAGE_COMMAND_EXPLOSION_NAME 38 // Wall hit vclip +#define WEAPONPAGE_COMMAND_ALPHA 39 +#define WEAPONPAGE_COMMAND_EXPLODE_TIME 40 +#define WEAPONPAGE_COMMAND_EXPLODE_SIZE 41 +#define WEAPONPAGE_COMMAND_SPAWN_COUNT 42 +#define WEAPONPAGE_COMMAND_SPAWN_NAME 43 +#define WEAPONPAGE_COMMAND_FIRE_IMAGE_NULL 44 +#define WEAPONPAGE_COMMAND_HUD_IMAGE_NULL 45 +#define WEAPONPAGE_COMMAND_NAME_NULL 46 +#define WEAPONPAGE_COMMAND_PARTICLE_NAME 47 +#define WEAPONPAGE_COMMAND_PARTICLE_DATA 48 +#define WEAPONPAGE_COMMAND_HIT_DIE_DOT 49 +#define WEAPONPAGE_COMMAND_IMPACT_FORCE 50 +#define WEAPONPAGE_COMMAND_IMPACT_DAMAGE 51 +#define WEAPONPAGE_COMMAND_SCORCH_NAME 52 +#define WEAPONPAGE_COMMAND_ICON_NAME 53 +#define WEAPONPAGE_COMMAND_SCORCH_SIZE 54 +#define WEAPONPAGE_COMMAND_ALT_SPAWN_NAME 55 +#define WEAPONPAGE_COMMAND_ALT_CHANCE 56 +#define WEAPONPAGE_COMMAND_GRAVITY_FIELD 57 +#define WEAPONPAGE_COMMAND_ROBOT_SPAWN_NAME 58 +#define WEAPONPAGE_COMMAND_TERRAIN_DAMAGE 59 +#define WEAPONPAGE_COMMAND_HOMING_FOV 60 +#define WEAPONPAGE_COMMAND_CUSTOM_SIZE 61 + +#define WEAPONPAGE_VERSION 8 + +void mng_WriteLightingChunk ( light_info *lighting_info,CFILE *outfile); +void mng_ReadLightingChunk (light_info *lighting_info,CFILE *infile); + +extern char *TablefileNameOverride; + +// Sets a page structure to default values +void mng_InitWeaponPage (mngs_weapon_page *weaponpage) +{ + int i; + + memset (weaponpage,0,sizeof(mngs_weapon_page)); + strcpy (weaponpage->hud_image_name,""); + strcpy (weaponpage->fire_image_name,""); + strcpy (weaponpage->explode_image_name,""); + strcpy (weaponpage->spawn_name,""); + strcpy (weaponpage->alternate_spawn_name,""); + strcpy (weaponpage->robot_spawn_name,""); + strcpy (weaponpage->smoke_image_name,""); + strcpy (weaponpage->scorch_image_name,""); + strcpy (weaponpage->icon_name,""); + strcpy (weaponpage->particle_name,""); + + weaponpage->weapon_struct.alpha=1.0; + weaponpage->weapon_struct.alternate_chance=0; + weaponpage->weapon_struct.explode_time=1.0; + weaponpage->weapon_struct.explode_size=1.0; + weaponpage->weapon_struct.particle_count=0; + weaponpage->weapon_struct.scorch_size=1.0; + weaponpage->weapon_struct.terrain_damage_size=0; + weaponpage->weapon_struct.terrain_damage_depth=0; + weaponpage->weapon_struct.homing_fov = 0.4f; + weaponpage->weapon_struct.custom_size = 0.0f; + weaponpage->weapon_struct.recoil_force = 0.0f; + + weaponpage->weapon_struct.phys_info.hit_die_dot = 1.0f; + + for (i=0;isound_name[i],""); +} + +// Given an open file pointer and a weapon_page struct, writes that weapon page out +void mng_WriteWeaponPage (CFILE *outfile,mngs_weapon_page *weaponpage) +{ + int i; + + ASSERT (outfile!=NULL); + ASSERT (weaponpage!=NULL); + + cf_WriteByte (outfile,PAGETYPE_WEAPON); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_VERSION); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,WEAPONPAGE_VERSION); + + if ((strlen(weaponpage->weapon_struct.name))<2) + Int3(); // Get Jason, right now + + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_NAME_NULL); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->weapon_struct.name))+1); + cf_WriteString (outfile,weaponpage->weapon_struct.name); + + // Write out its image name + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_EXPLOSION_NAME); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->explode_image_name))+1); + cf_WriteString (outfile,weaponpage->explode_image_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_PARTICLE_NAME); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->particle_name))+1); + cf_WriteString (outfile,weaponpage->particle_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_PARTICLE_DATA); + cf_WriteByte (outfile,9); + cf_WriteByte (outfile,weaponpage->weapon_struct.particle_count); + cf_WriteFloat (outfile,weaponpage->weapon_struct.particle_life); + cf_WriteFloat (outfile,weaponpage->weapon_struct.particle_size); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_SPAWN_NAME); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->spawn_name))+1); + cf_WriteString (outfile,weaponpage->spawn_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_SPAWN_COUNT); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,weaponpage->weapon_struct.spawn_count); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_ROBOT_SPAWN_NAME); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->robot_spawn_name))+1); + cf_WriteString (outfile,weaponpage->robot_spawn_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_ALT_SPAWN_NAME); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->alternate_spawn_name))+1); + cf_WriteString (outfile,weaponpage->alternate_spawn_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_ALT_CHANCE); + cf_WriteByte (outfile,1); + cf_WriteByte (outfile,weaponpage->weapon_struct.alternate_chance); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_HUD_IMAGE_NULL); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->hud_image_name))+1); + cf_WriteString (outfile,weaponpage->hud_image_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_FIRE_IMAGE_NULL); // get ready to write out name + cf_WriteByte (outfile,(strlen(weaponpage->fire_image_name))+1); + cf_WriteString (outfile,weaponpage->fire_image_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_HOMING_FOV); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.homing_fov); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_CUSTOM_SIZE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.custom_size); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_GRAVITY_FIELD); + cf_WriteByte (outfile,8); + cf_WriteFloat (outfile,weaponpage->weapon_struct.gravity_time); + cf_WriteFloat (outfile,weaponpage->weapon_struct.gravity_size); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_MASS); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.mass); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_DRAG); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.drag); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_FULL_THRUST); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.full_thrust); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_FLAGS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,weaponpage->weapon_struct.flags); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_PHYS_FLAGS); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,weaponpage->weapon_struct.phys_info.flags); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_SIZE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.size); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_TERRAIN_DAMAGE); + cf_WriteByte (outfile,5); + cf_WriteFloat (outfile,weaponpage->weapon_struct.terrain_damage_size); + cf_WriteByte (outfile,weaponpage->weapon_struct.terrain_damage_depth); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_ALPHA); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.alpha); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_EXPLODE_TIME); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.explode_time); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_EXPLODE_SIZE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.explode_size); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_DAMAGE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.player_damage); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_IMPACT_SIZE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_size); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_IMPACT_TIME); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_time); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_IMPACT_DAMAGE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_player_damage); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_IMPACT_FORCE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_force); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_ROT_DRAG); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.rotdrag); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_FULL_ROT_THRUST); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.full_rotthrust); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_MAX_BOUNCES); + cf_WriteByte (outfile,4); + cf_WriteInt (outfile,weaponpage->weapon_struct.phys_info.num_bounces); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_INIT_VELOCITY); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.velocity.z); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_INIT_ROT_VEL_X); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.rotvel.x); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_INIT_ROT_VEL_Y); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.rotvel.y); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_INIT_ROT_VEL_Z); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.rotvel.z); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_LIFE_TIME); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.life_time); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_THRUST_TIME); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.thrust_time); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_WIGGLE_AMP); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.wiggle_amplitude); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_WIGGLE_FREQ); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.wiggles_per_sec); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_COEFF_RESTITUTION); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.coeff_restitution); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_HIT_DIE_DOT); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.phys_info.hit_die_dot); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_BETTER_LIGHT_CAST); + cf_WriteByte (outfile,(10*4)+2); + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.light_distance); + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.red_light1); + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.green_light1); + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.blue_light1); + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.time_interval); + + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.red_light2); + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.green_light2); + cf_WriteFloat (outfile,weaponpage->weapon_struct.lighting_info.blue_light2); + cf_WriteInt (outfile,weaponpage->weapon_struct.lighting_info.flags); + cf_WriteInt (outfile,weaponpage->weapon_struct.lighting_info.timebits); + cf_WriteByte (outfile,weaponpage->weapon_struct.lighting_info.angle); + cf_WriteByte (outfile,weaponpage->weapon_struct.lighting_info.lighting_render_type); + + // Write out its sounds name + + for (i=0;isound_name[i])+2); // 1 for sound index, 1 for null term + cf_WriteByte (outfile,i); + cf_WriteString (outfile,weaponpage->sound_name[i]); + } + + // Write smoke name + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_SMOKE_NAME); + cf_WriteByte (outfile,strlen (weaponpage->smoke_image_name)+1); // 1 for null term + cf_WriteString (outfile,weaponpage->smoke_image_name); + + // Write scorch name + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_SCORCH_NAME); + cf_WriteByte (outfile,strlen (weaponpage->scorch_image_name)+1); // 1 for null term + cf_WriteString (outfile,weaponpage->scorch_image_name); + + // Write icon name + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_ICON_NAME); + cf_WriteByte (outfile,strlen (weaponpage->icon_name)+1); // 1 for null term + cf_WriteString (outfile,weaponpage->icon_name); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_SCORCH_SIZE); + cf_WriteByte (outfile,4); + cf_WriteFloat (outfile,weaponpage->weapon_struct.scorch_size); + + cf_WriteByte (outfile,WEAPONPAGE_COMMAND_END); // we're all done + cf_WriteByte (outfile,0); +} + +// Given an open file pointer and a weapon_page struct, writes that weapon page out +void mng_WriteNewWeaponPage (CFILE *outfile,mngs_weapon_page *weaponpage) +{ + int i; + + ASSERT (outfile!=NULL); + ASSERT (weaponpage!=NULL); + + int offset=StartManagePage(outfile,PAGETYPE_WEAPON); + + cf_WriteShort (outfile,WEAPONPAGE_VERSION); + + if ((strlen(weaponpage->weapon_struct.name))<2) + Int3(); // Get Jason, right now + + cf_WriteString (outfile,weaponpage->weapon_struct.name); + + // Write hud image name + cf_WriteString (outfile,weaponpage->hud_image_name); + + // Write fire image + cf_WriteString (outfile,weaponpage->fire_image_name); + + // Write out particle data + cf_WriteString (outfile,weaponpage->particle_name); + + cf_WriteByte (outfile,weaponpage->weapon_struct.particle_count); + cf_WriteFloat (outfile,weaponpage->weapon_struct.particle_life); + cf_WriteFloat (outfile,weaponpage->weapon_struct.particle_size); + + // Write flags + cf_WriteInt (outfile,weaponpage->weapon_struct.flags); + + // Write spawn data + cf_WriteString (outfile,weaponpage->spawn_name); + cf_WriteByte (outfile,weaponpage->weapon_struct.spawn_count); + + cf_WriteString (outfile,weaponpage->robot_spawn_name); + + cf_WriteString (outfile,weaponpage->alternate_spawn_name); + + cf_WriteByte (outfile,weaponpage->weapon_struct.alternate_chance); + + + // Write gravity stuff + cf_WriteFloat (outfile,weaponpage->weapon_struct.gravity_time); + cf_WriteFloat (outfile,weaponpage->weapon_struct.gravity_size); + + // Write misc stuff + cf_WriteFloat (outfile,weaponpage->weapon_struct.homing_fov); + cf_WriteFloat (outfile,weaponpage->weapon_struct.custom_size); + cf_WriteFloat (outfile,weaponpage->weapon_struct.size); + cf_WriteFloat (outfile,weaponpage->weapon_struct.thrust_time); + + // Write physics stuff + mng_WritePhysicsChunk (&weaponpage->weapon_struct.phys_info,outfile); + + // Write terrain damage + cf_WriteFloat (outfile,weaponpage->weapon_struct.terrain_damage_size); + cf_WriteByte (outfile,weaponpage->weapon_struct.terrain_damage_depth); + + // Write alpha + cf_WriteFloat (outfile,weaponpage->weapon_struct.alpha); + + // Write explosion data + cf_WriteString (outfile,weaponpage->explode_image_name); + cf_WriteFloat (outfile,weaponpage->weapon_struct.explode_time); + cf_WriteFloat (outfile,weaponpage->weapon_struct.explode_size); + + + // Write damage data + cf_WriteFloat (outfile,weaponpage->weapon_struct.player_damage); + cf_WriteFloat (outfile,weaponpage->weapon_struct.generic_damage); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_size); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_time); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_player_damage); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_generic_damage); + cf_WriteFloat (outfile,weaponpage->weapon_struct.impact_force); + + // Write lifetime + cf_WriteFloat (outfile,weaponpage->weapon_struct.life_time); + + // Write lighting + mng_WriteLightingChunk (&weaponpage->weapon_struct.lighting_info,outfile); + + // Write recoil force + cf_WriteFloat (outfile,weaponpage->weapon_struct.recoil_force); + + // Write out its sounds name + for (i=0;isound_name[i]); + + // Write smoke name + cf_WriteString (outfile,weaponpage->smoke_image_name); + + // Write scorch data + cf_WriteString (outfile,weaponpage->scorch_image_name); + cf_WriteFloat (outfile,weaponpage->weapon_struct.scorch_size); + + // Write icon name + cf_WriteString (outfile,weaponpage->icon_name); + + EndManagePage (outfile,offset); +} + +#ifdef OEM +void DoOEMNapalmBarrelHack(mngs_weapon_page *weaponpage) +{ + if(!strcmp(weaponpage->weapon_struct.name,"NapalmBarrelPart2")) + { + mprintf((0,"BASHING NAPALMBARRELPART2!!!!!!\n")); + weaponpage->weapon_struct.flags |= (WF_SATURATE|WF_NO_ROTATE|WF_CUSTOM_SIZE|WF_NAPALM); + weaponpage->weapon_struct.custom_size = 6.0f; + weaponpage->weapon_struct.phys_info.mass = 0.6f; + weaponpage->weapon_struct.phys_info.drag = 0.01f; + weaponpage->weapon_struct.phys_info.rotdrag = 0.001f; + + weaponpage->weapon_struct.phys_info.velocity.x = 0; + weaponpage->weapon_struct.phys_info.velocity.y = 0; + weaponpage->weapon_struct.phys_info.velocity.z = 50.0f; + + }else + { + mprintf((0,"BASHING NAPALMBARREL!!!!!!\n")); + weaponpage->weapon_struct.flags |= (WF_INVISIBLE|WF_CUSTOM_SIZE|WF_NAPALM); + weaponpage->weapon_struct.custom_size = 1.0f; + weaponpage->weapon_struct.phys_info.flags |= PF_USES_PARENT_VELOCITY; + + weaponpage->weapon_struct.phys_info.velocity.x = 0; + weaponpage->weapon_struct.phys_info.velocity.y = 0; + weaponpage->weapon_struct.phys_info.velocity.z = 110.0f; + } +} +#endif + +// Given an open file pointer and a weapon_page struct, reads a weapon page +int mng_ReadNewWeaponPage (CFILE *infile,mngs_weapon_page *weaponpage) +{ + int i; + + mng_InitWeaponPage (weaponpage); + + int version=cf_ReadShort (infile); + + cf_ReadString (weaponpage->weapon_struct.name,PAGENAME_LEN,infile); + + // Read hud image name + cf_ReadString (weaponpage->hud_image_name,PAGENAME_LEN,infile); + + // Read fire image + cf_ReadString (weaponpage->fire_image_name,PAGENAME_LEN,infile); + + // Read particle data + cf_ReadString (weaponpage->particle_name,PAGENAME_LEN,infile); + + weaponpage->weapon_struct.particle_count=cf_ReadByte (infile); + weaponpage->weapon_struct.particle_life=cf_ReadFloat (infile); + weaponpage->weapon_struct.particle_size=cf_ReadFloat (infile); + + // Read flags + weaponpage->weapon_struct.flags=cf_ReadInt (infile); + + // Read spawn data + cf_ReadString (weaponpage->spawn_name,PAGENAME_LEN,infile); + weaponpage->weapon_struct.spawn_count=cf_ReadByte (infile); + + cf_ReadString (weaponpage->robot_spawn_name,PAGENAME_LEN,infile); + cf_ReadString (weaponpage->alternate_spawn_name,PAGENAME_LEN,infile); + + weaponpage->weapon_struct.alternate_chance=cf_ReadByte (infile); + + // Read gravity stuff + weaponpage->weapon_struct.gravity_time=cf_ReadFloat (infile); + weaponpage->weapon_struct.gravity_size=cf_ReadFloat (infile); + + // Read size and homing data + weaponpage->weapon_struct.homing_fov=cf_ReadFloat (infile); + weaponpage->weapon_struct.custom_size=cf_ReadFloat (infile); + weaponpage->weapon_struct.size=cf_ReadFloat (infile); + weaponpage->weapon_struct.thrust_time=cf_ReadFloat (infile); + + // Read physics info + mng_ReadPhysicsChunk (&weaponpage->weapon_struct.phys_info,infile); + + // Read terrain damage + weaponpage->weapon_struct.terrain_damage_size=cf_ReadFloat (infile); + weaponpage->weapon_struct.terrain_damage_depth=cf_ReadByte (infile); + + // Read alpha + weaponpage->weapon_struct.alpha=cf_ReadFloat (infile); + + // Read explosion data + cf_ReadString (weaponpage->explode_image_name,PAGENAME_LEN,infile); + weaponpage->weapon_struct.explode_time=cf_ReadFloat (infile); + weaponpage->weapon_struct.explode_size=cf_ReadFloat (infile); + + // Read damage data + weaponpage->weapon_struct.player_damage=cf_ReadFloat (infile); + + if (version>=7) + weaponpage->weapon_struct.generic_damage=cf_ReadFloat (infile); + else + weaponpage->weapon_struct.generic_damage=weaponpage->weapon_struct.player_damage; + + + weaponpage->weapon_struct.impact_size=cf_ReadFloat (infile); + weaponpage->weapon_struct.impact_time=cf_ReadFloat (infile); + weaponpage->weapon_struct.impact_player_damage=cf_ReadFloat (infile); + + if (version>=7) + weaponpage->weapon_struct.impact_generic_damage=cf_ReadFloat (infile); + else + weaponpage->weapon_struct.impact_generic_damage=weaponpage->weapon_struct.impact_player_damage; + + weaponpage->weapon_struct.impact_force=cf_ReadFloat (infile); + + // Read lifetime + weaponpage->weapon_struct.life_time=cf_ReadFloat (infile); + + // read lighting + mng_ReadLightingChunk (&weaponpage->weapon_struct.lighting_info,infile); + + // read recoil force + if (version>=8) + weaponpage->weapon_struct.recoil_force = cf_ReadFloat(infile); + else + weaponpage->weapon_struct.recoil_force = 0.0f; + + // Read its sound names + for (i=0;isound_name[i],PAGENAME_LEN,infile); + + // Read smoke name + cf_ReadString (weaponpage->smoke_image_name,PAGENAME_LEN,infile); + + // Read scorch data + cf_ReadString (weaponpage->scorch_image_name,PAGENAME_LEN,infile); + weaponpage->weapon_struct.scorch_size=cf_ReadFloat (infile); + + // Read icon name + cf_ReadString (weaponpage->icon_name,PAGENAME_LEN,infile); + + weaponpage->weapon_struct.used=1; + + +#ifdef OEM + if(!strcmp(weaponpage->weapon_struct.name,"NapalmBarrelPart2") || + !strcmp(weaponpage->weapon_struct.name,"NapalmBarrel") ) + { + //Do OEM hack + DoOEMNapalmBarrelHack(weaponpage); + } +#endif + + if (!stricmp (weaponpage->weapon_struct.name,"EMDBlob")) + { + weaponpage->weapon_struct.life_time=1.7f; + } + + return 1; // successfully read + +} + + +// Reads a weapon page from an open file. Returns 0 on error. +int mng_ReadWeaponPage (CFILE *infile,mngs_weapon_page *weaponpage) +{ + int done=0; + char command; + ubyte len; + int i,t; + int version = 0; + + if (!Old_table_method) + return mng_ReadNewWeaponPage (infile,weaponpage); + + ASSERT (infile!=NULL); + + mng_InitWeaponPage (weaponpage); + + while (!done) + { + // Read in command byte then read in the length of that commands data + + command=cf_ReadByte (infile); + len=cf_ReadByte(infile); + + switch (command) + { + case WEAPONPAGE_COMMAND_END: + done=1; + break; + case WEAPONPAGE_COMMAND_VERSION: + version = cf_ReadByte(infile); + break; + case WEAPONPAGE_COMMAND_HUD_IMAGE_NAME: // the name of the weapon model + for (i=0;ihud_image_name[i]=cf_ReadByte (infile); + break; + case WEAPONPAGE_COMMAND_FIRE_IMAGE_NAME: // the name of the weapon model + for (i=0;ifire_image_name[i]=cf_ReadByte (infile); + break; + case WEAPONPAGE_COMMAND_NAME_NULL: + cf_ReadString (weaponpage->weapon_struct.name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_FIRE_IMAGE_NULL: + cf_ReadString (weaponpage->fire_image_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_HUD_IMAGE_NULL: + cf_ReadString (weaponpage->hud_image_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_SPAWN_NAME: + cf_ReadString (weaponpage->spawn_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_SPAWN_COUNT: + weaponpage->weapon_struct.spawn_count=cf_ReadByte(infile); + break; + case WEAPONPAGE_COMMAND_ALT_SPAWN_NAME: + cf_ReadString (weaponpage->alternate_spawn_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_ROBOT_SPAWN_NAME: + cf_ReadString (weaponpage->robot_spawn_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_ALT_CHANCE: + weaponpage->weapon_struct.alternate_chance=cf_ReadByte(infile); + break; + case WEAPONPAGE_COMMAND_NAME: + for (i=0;iweapon_struct.name[i]=cf_ReadByte (infile); + break; + case WEAPONPAGE_COMMAND_EXPLOSION_NAME: + cf_ReadString (weaponpage->explode_image_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_PARTICLE_NAME: + cf_ReadString (weaponpage->particle_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_FLAGS: + weaponpage->weapon_struct.flags=cf_ReadInt(infile); + break; + case WEAPONPAGE_COMMAND_MASS: + weaponpage->weapon_struct.phys_info.mass=cf_ReadFloat(infile); + break; + case WEAPONPAGE_COMMAND_HOMING_FOV: + weaponpage->weapon_struct.homing_fov=cf_ReadFloat(infile); + break; + case WEAPONPAGE_COMMAND_CUSTOM_SIZE: + weaponpage->weapon_struct.custom_size=cf_ReadFloat(infile); + break; + case WEAPONPAGE_COMMAND_GRAVITY_FIELD: + weaponpage->weapon_struct.gravity_time=cf_ReadFloat(infile); + weaponpage->weapon_struct.gravity_size=cf_ReadFloat(infile); + break; + case WEAPONPAGE_COMMAND_FULL_THRUST: + weaponpage->weapon_struct.phys_info.full_thrust=cf_ReadFloat(infile); + break; + case WEAPONPAGE_COMMAND_DRAG: + weaponpage->weapon_struct.phys_info.drag=cf_ReadFloat(infile); + break; + case WEAPONPAGE_COMMAND_TERRAIN_DAMAGE: + weaponpage->weapon_struct.terrain_damage_size=cf_ReadFloat(infile); + weaponpage->weapon_struct.terrain_damage_depth=cf_ReadByte(infile); + break; + case WEAPONPAGE_COMMAND_SOUND_NAME: + t=cf_ReadByte (infile); + cf_ReadString (weaponpage->sound_name[t],len,infile); + break; + case WEAPONPAGE_COMMAND_PHYS_FLAGS: + weaponpage->weapon_struct.phys_info.flags = cf_ReadInt (infile); + break; + case WEAPONPAGE_COMMAND_SIZE: + weaponpage->weapon_struct.size = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_ALPHA: + weaponpage->weapon_struct.alpha = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_EXPLODE_TIME: + weaponpage->weapon_struct.explode_time = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_EXPLODE_SIZE: + weaponpage->weapon_struct.explode_size = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_PARTICLE_DATA: + weaponpage->weapon_struct.particle_count = cf_ReadByte (infile); + weaponpage->weapon_struct.particle_life = cf_ReadFloat (infile); + weaponpage->weapon_struct.particle_size = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_DAMAGE: + weaponpage->weapon_struct.player_damage = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_IMPACT_SIZE: + weaponpage->weapon_struct.impact_size = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_IMPACT_TIME: + weaponpage->weapon_struct.impact_time = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_IMPACT_DAMAGE: + weaponpage->weapon_struct.impact_player_damage = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_IMPACT_FORCE: + weaponpage->weapon_struct.impact_force = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_ROT_DRAG: + weaponpage->weapon_struct.phys_info.rotdrag = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_FULL_ROT_THRUST: + weaponpage->weapon_struct.phys_info.full_rotthrust = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_MAX_BOUNCES: + weaponpage->weapon_struct.phys_info.num_bounces = cf_ReadInt (infile); + break; + case WEAPONPAGE_COMMAND_INIT_VELOCITY: + weaponpage->weapon_struct.phys_info.velocity.z = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_INIT_ROT_VEL_X: + weaponpage->weapon_struct.phys_info.rotvel.x = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_INIT_ROT_VEL_Y: + weaponpage->weapon_struct.phys_info.rotvel.y = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_INIT_ROT_VEL_Z: + weaponpage->weapon_struct.phys_info.rotvel.z = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_LIFE_TIME: + weaponpage->weapon_struct.life_time = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_THRUST_TIME: + weaponpage->weapon_struct.thrust_time = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_WIGGLE_AMP: + weaponpage->weapon_struct.phys_info.wiggle_amplitude = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_WIGGLE_FREQ: + weaponpage->weapon_struct.phys_info.wiggles_per_sec = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_COEFF_RESTITUTION: + weaponpage->weapon_struct.phys_info.coeff_restitution = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_HIT_DIE_DOT: + weaponpage->weapon_struct.phys_info.hit_die_dot = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_LIGHT_CAST: + weaponpage->weapon_struct.lighting_info.light_distance = cf_ReadFloat (infile); + weaponpage->weapon_struct.lighting_info.red_light1 = cf_ReadFloat (infile); + weaponpage->weapon_struct.lighting_info.green_light1 = cf_ReadFloat (infile); + weaponpage->weapon_struct.lighting_info.blue_light1 = cf_ReadFloat (infile); + weaponpage->weapon_struct.lighting_info.timebits=0xFFFFFFFF; + break; + case WEAPONPAGE_COMMAND_BETTER_LIGHT_CAST: + weaponpage->weapon_struct.lighting_info.light_distance=cf_ReadFloat(infile); + + weaponpage->weapon_struct.lighting_info.red_light1=cf_ReadFloat(infile); + weaponpage->weapon_struct.lighting_info.green_light1=cf_ReadFloat(infile); + weaponpage->weapon_struct.lighting_info.blue_light1=cf_ReadFloat(infile); + + weaponpage->weapon_struct.lighting_info.time_interval=cf_ReadFloat(infile); + + weaponpage->weapon_struct.lighting_info.red_light2=cf_ReadFloat(infile); + weaponpage->weapon_struct.lighting_info.green_light2=cf_ReadFloat(infile); + weaponpage->weapon_struct.lighting_info.blue_light2=cf_ReadFloat(infile); + + weaponpage->weapon_struct.lighting_info.flags=cf_ReadInt(infile); + weaponpage->weapon_struct.lighting_info.timebits=cf_ReadInt(infile); + weaponpage->weapon_struct.lighting_info.angle=cf_ReadByte(infile); + weaponpage->weapon_struct.lighting_info.lighting_render_type=cf_ReadByte(infile); + break; + case WEAPONPAGE_COMMAND_SMOKE_NAME: + cf_ReadString (weaponpage->smoke_image_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_SCORCH_NAME: + cf_ReadString (weaponpage->scorch_image_name,len+1,infile); + break; + case WEAPONPAGE_COMMAND_SCORCH_SIZE: + weaponpage->weapon_struct.scorch_size = cf_ReadFloat (infile); + break; + case WEAPONPAGE_COMMAND_ICON_NAME: + cf_ReadString (weaponpage->icon_name,len+1,infile); + break; + default: + // Ignore the ones we don't know + for (i=0;iweapon_struct.impact_player_damage = weaponpage->weapon_struct.player_damage; + weaponpage->weapon_struct.impact_force = weaponpage->weapon_struct.player_damage * 100.0f; + if(weaponpage->weapon_struct.impact_size > 0.0) + { + weaponpage->weapon_struct.impact_time = weaponpage->weapon_struct.explode_time; + weaponpage->weapon_struct.impact_size = weaponpage->weapon_struct.explode_size * 1.5; + } + } + + // Clear out old strings + if (!strnicmp ("INVALID",weaponpage->explode_image_name,7)) + strcpy (weaponpage->explode_image_name,""); + if (!strnicmp ("INVALID",weaponpage->smoke_image_name,7)) + strcpy (weaponpage->smoke_image_name,""); + if (!strnicmp ("INVALID",weaponpage->scorch_image_name,7)) + strcpy (weaponpage->scorch_image_name,""); + if (!strnicmp ("INVALID",weaponpage->icon_name,7)) + strcpy (weaponpage->icon_name,""); + if (!strnicmp ("INVALID",weaponpage->spawn_name,7)) + strcpy (weaponpage->spawn_name,""); + if (!strnicmp ("INVALID",weaponpage->alternate_spawn_name,7)) + strcpy (weaponpage->alternate_spawn_name,""); + if (!strnicmp ("INVALID",weaponpage->robot_spawn_name,7)) + strcpy (weaponpage->robot_spawn_name,""); + if (!strnicmp ("INVALID",weaponpage->particle_name,7)) + strcpy (weaponpage->particle_name,""); + + for (i=0;isound_name[i],7)) + strcpy (weaponpage->sound_name[i],""); + } + + // This is a valid new page + weaponpage->weapon_struct.used=1; + + return 1; // successfully read +} + + +// Reads in the weapon named "name" into weaponpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificWeaponPage (char *name,mngs_weapon_page *weaponpage) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + int first_try=1; + char tablename[TABLE_NAME_LEN]; + + if (Loading_locals) + { + infile=cfopen (LocalTableFilename,"rb"); + } + else if (Loading_addon_table!=-1) + { + infile=cfopen (AddOnDataTables[Loading_addon_table].AddOnTableFilename,"rb"); + } + else + { + if (Network_up && Starting_editor) + { + int farg=FindArg("-filter"); + + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + + infile=cfopen (tablename,"rb"); + } + else + { + infile = NULL; + if(TablefileNameOverride) + { + infile=cfopen(TablefileNameOverride,"rb"); + } + + if(!infile) + infile=cfopen (TableFilename,"rb"); + } + } + + if (!infile) + { + mprintf ((0,"Couldn't open table file to find weapon!\n")); + Int3(); + return 0; + } + +try_again:; + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + + // If not a weapon page, just read it in and ignore it + if (pagetype!=PAGETYPE_WEAPON) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + + mng_ReadNewWeaponPage (infile,weaponpage); + + if (!stricmp(name,weaponpage->weapon_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + + cfclose (infile); + + if (!found && first_try) { + done = first_try = 0; + infile=cfopen ("extra.gam","rb"); + if (infile) + goto try_again; + } + + return found; // successful! +} + +// Reads in the weapon named "name" into weaponpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificWeaponPage (char *name,mngs_weapon_page *weaponpage,int offset) +{ + CFILE *infile; + ubyte pagetype; + int done=0,found=0; + + if (Loading_locals) + { + infile=cfopen (LocalTableFilename,"rb"); + } + else if (Loading_addon_table!=-1) + { + infile=cfopen (AddOnDataTables[Loading_addon_table].AddOnTableFilename,"rb"); + } + else + { + if (Network_up && Starting_editor) + { + char tablename[TABLE_NAME_LEN]; + + int farg=FindArg("-filter"); + if (farg) + strcpy (tablename,GameArgs[farg+1]); + else + ddio_MakePath (tablename,LocalTableDir,NET_TABLE,NULL); + + infile=cfopen (tablename,"rb"); + } + else + { + infile = NULL; + if(TablefileNameOverride) + { + infile=cfopen(TablefileNameOverride,"rb"); + } + + if(!infile) + infile=cfopen (TableFilename,"rb"); + } + } + + if (!infile) + { + mprintf ((0,"Couldn't open table file to find weapon!\n")); + Int3(); + return 0; + } + + if (offset) + cfseek (infile,offset,SEEK_SET); + + // Read in the entire page file until we find the page we want + while (!done) + { + if (cfeof (infile)) + { + done=1; + continue; + } + pagetype=cf_ReadByte (infile); + int len=cf_ReadInt(infile); + + + + // If not a weapon page, just read it in and ignore it + if (pagetype!=PAGETYPE_WEAPON) + { + cfseek (infile,len-4,SEEK_CUR); + continue; + } + + mng_ReadNewWeaponPage (infile,weaponpage); + + if (!stricmp(name,weaponpage->weapon_struct.name)) + { + // This is the page we want + found=1; + done=1; + } + + } + + cfclose (infile); + + return found; // successful! +} + +char *Weapon_error=NULL,*Weapon_error_filename=NULL; + +// First searches through the weapon index to see if the weapon is already +// loaded. If not, searches in the table file and loads it. +// Returns index of weapon found, -1 if not +int mng_GetGuaranteedWeaponPage (char *name,CFILE *infile) +{ + int i; + mngs_weapon_page weaponpage; + + + // See if its in memory + i=FindWeaponName (name); + if (i!=-1) + return i; + + // Not in memory. Load it from the table file. Start searching from the + // current spot in the open table file + + + int ret=mng_FindSpecificWeaponPage (name,&weaponpage,infile?infile->position:0); + + if (!ret) + return -1; + + // We've found it in the table file, now load it. + ret=mng_SetAndLoadWeapon (&weaponpage,infile); + //ASSERT (ret>=0); + if (ret < 0) + DataError("Error loading weapon <%s>: %s, file <%s>\n",weaponpage.weapon_struct.name,Weapon_error,Weapon_error_filename); + + if(Loading_addon_table!=-1) + { + // we're loading addon table pages, this will not overlay anything + mng_PushAddonPage (PAGETYPE_WEAPON,weaponpage.weapon_struct.name,0); + } + + return ret; +} + +// Given a weapon page, allocs a weapon and calls AssignWeaponPageToWeapon to actually +// load models and values. Rturns weapon handle on success, -1 if fail +int mng_SetAndLoadWeapon (mngs_weapon_page *weaponpage,CFILE *infile) +{ + int n; + + Weapon_error = Weapon_error_filename = ""; + + n=AllocWeapon(); + if (n<0) { + Weapon_error = "No free weapon slots"; + return -1; + } + if (!mng_AssignWeaponPageToWeapon(weaponpage,n,infile)) + return -1; + + return n; +} + +// Given a weaponpage and a weapon handle, attempts to make weapon n correspond to +// to the weaponpage. +// Returns 1 on success, 0 otherwise +int mng_AssignWeaponPageToWeapon(mngs_weapon_page *weaponpage,int n,CFILE *infile) +{ + weapon *weaponpointer=&Weapons[n]; + int img_handle,i,sound_handle; + + // copy our values + memcpy (weaponpointer,&weaponpage->weapon_struct,sizeof(weapon)); + strcpy (weaponpointer->name,weaponpage->weapon_struct.name); + + + // First see if our image differs from the one on the net + // If it is, make a copy + // If its a release version, don't do any of this + + #ifndef RELEASE + if (Network_up) + { + char str[200]; + char netstr[200]; + + ddio_MakePath (str,LocalManageGraphicsDir,weaponpage->hud_image_name,NULL); + ddio_MakePath (netstr,ManageGraphicsDir,weaponpage->hud_image_name,NULL); + + UpdatePrimitive (str,netstr,weaponpage->hud_image_name,PAGETYPE_WEAPON,weaponpointer->name); + + // Now copy the discharge image, depending on whether or not its a model + if ((weaponpage->weapon_struct.flags & WF_IMAGE_BITMAP) || (weaponpage->weapon_struct.flags & WF_IMAGE_VCLIP)) + { + ddio_MakePath (str,LocalManageGraphicsDir,weaponpage->fire_image_name,NULL); + ddio_MakePath (netstr,ManageGraphicsDir,weaponpage->fire_image_name,NULL); + } + else + { + ddio_MakePath (str,LocalModelsDir,weaponpage->fire_image_name,NULL); + ddio_MakePath (netstr,NetModelsDir,weaponpage->fire_image_name,NULL); + } + + UpdatePrimitive (str,netstr,weaponpage->fire_image_name,PAGETYPE_WEAPON,weaponpointer->name); + } + #endif + + // Try and load our weapon model from the disk + + if (weaponpointer->flags & WF_HUD_ANIMATED) + img_handle=AllocLoadVClip (weaponpage->hud_image_name,NOT_TEXTURE,0); + else + img_handle=bm_AllocLoadFileBitmap (weaponpage->hud_image_name,0); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load bitmap '%s' in AssignWeaponPage...\n",weaponpage->hud_image_name)); + weaponpointer->hud_image_handle=-1; + Weapon_error = "Can't load HUD image"; + Weapon_error_filename = weaponpage->hud_image_name; + return 0; + } + else + weaponpointer->hud_image_handle=img_handle; + + if (weaponpointer->flags & WF_IMAGE_BITMAP) + img_handle=bm_AllocLoadFileBitmap (weaponpage->fire_image_name,0); + else if (weaponpointer->flags & WF_IMAGE_VCLIP) + img_handle=AllocLoadVClip (weaponpage->fire_image_name,NOT_TEXTURE,0); + else + img_handle=LoadPolyModel (weaponpage->fire_image_name,1); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load bitmap/model '%s' in AssignWeaponPage...\n",weaponpage->fire_image_name)); + weaponpointer->fire_image_handle=-1; + Weapon_error = "Can't load fire image"; + Weapon_error_filename = weaponpage->fire_image_name; + return 0; + } + else + { + weaponpointer->fire_image_handle=img_handle; + + /*// Fix size + if (!(weaponpointer->flags & WF_IMAGE_BITMAP)) + { + if (Poly_models[img_handle].new_style && Poly_models[img_handle].flags & PMF_TIMED) + { + weaponpointer->size=ComputeDefaultSize(img_handle); + } + }*/ + } + + if (stricmp (weaponpage->explode_image_name,"INVALID NAME") && weaponpage->explode_image_name[0]!=0) + { + img_handle=mng_GetGuaranteedTexturePage (weaponpage->explode_image_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load bitmap/model '%s' in AssignWeaponPage...\n",weaponpage->explode_image_name)); + weaponpointer->explode_image_handle=-1; + } + else + weaponpointer->explode_image_handle=img_handle; + } + else + weaponpointer->explode_image_handle=-1; + + // Try to load particle texture + if (stricmp (weaponpage->particle_name,"INVALID NAME") && weaponpage->particle_name[0]!=0) + { + img_handle=mng_GetGuaranteedTexturePage (weaponpage->particle_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load bitmap/model '%s' in AssignWeaponPage...\n",weaponpage->particle_name)); + weaponpointer->particle_handle=-1; + } + else + weaponpointer->particle_handle=img_handle; + } + else + weaponpointer->particle_handle=-1; + + // Try to load spawn weapons + if (stricmp (weaponpage->spawn_name,"INVALID NAME") && weaponpage->spawn_name[0]!=0) + { + img_handle=mng_GetGuaranteedWeaponPage (weaponpage->spawn_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load spawn weapon '%s' in AssignWeaponPage...\n",weaponpage->spawn_name)); + weaponpointer->spawn_handle=-1; + } + else + weaponpointer->spawn_handle=img_handle; + } + else + weaponpointer->spawn_handle=-1; + + if (stricmp (weaponpage->alternate_spawn_name,"INVALID NAME") && weaponpage->alternate_spawn_name[0]!=0) + { + img_handle=mng_GetGuaranteedWeaponPage (weaponpage->alternate_spawn_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load alternate spawn weapon '%s' in AssignWeaponPage...\n",weaponpage->alternate_spawn_name)); + weaponpointer->alternate_spawn_handle=-1; + } + else + weaponpointer->alternate_spawn_handle=img_handle; + } + else + weaponpointer->alternate_spawn_handle=-1; + + // Try to load robot spawn + if (stricmp (weaponpage->robot_spawn_name,"INVALID NAME") && weaponpage->robot_spawn_name[0]!=0) + { + img_handle=mng_GetGuaranteedGenericPage (weaponpage->robot_spawn_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load robot spawn weapon '%s' in AssignWeaponPage...\n",weaponpage->robot_spawn_name)); + weaponpointer->robot_spawn_handle=-1; + } + else + weaponpointer->robot_spawn_handle=img_handle; + } + else + weaponpointer->robot_spawn_handle=-1; + + // Try to load smoke + if (stricmp (weaponpage->smoke_image_name,"INVALID NAME") && weaponpage->smoke_image_name[0]!=0) + { + img_handle=mng_GetGuaranteedTexturePage (weaponpage->smoke_image_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load smoke trail file '%s' in AssignWeaponPage...\n",weaponpage->smoke_image_name)); + weaponpointer->flags&=~WF_SMOKE; + weaponpointer->smoke_handle=-1; + + } + else + weaponpointer->smoke_handle=img_handle; + } + else + { + weaponpointer->flags&=~WF_SMOKE; + weaponpointer->smoke_handle=-1; + } + + // Try to load scorch + if (stricmp (weaponpage->scorch_image_name,"INVALID NAME") && weaponpage->scorch_image_name[0]!=0) + { + img_handle=mng_GetGuaranteedTexturePage (weaponpage->scorch_image_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load scorch file '%s' in AssignWeaponPage...\n",weaponpage->scorch_image_name)); + weaponpointer->scorch_handle=-1; + + } + else + weaponpointer->scorch_handle=img_handle; + } + else + { + weaponpointer->scorch_handle=-1; + } + + // Try to load icone + if (stricmp (weaponpage->icon_name,"INVALID NAME") && weaponpage->icon_name[0]!=0) + { + img_handle=mng_GetGuaranteedTexturePage (weaponpage->icon_name,infile); + + if (img_handle<0) + { + mprintf ((0,"Couldn't load icon file '%s' in AssignWeaponPage...\n",weaponpage->icon_name)); + weaponpointer->icon_handle=-1; + + } + else + weaponpointer->icon_handle=img_handle; + } + else + { + weaponpointer->icon_handle=-1; + } + + + + // Try and load the various sounds + for (i=0;isound_name[i],"INVALID NAME") && (strlen (weaponpage->sound_name[i])>3)) + { + sound_handle=mng_GetGuaranteedSoundPage (weaponpage->sound_name[i]); + + if (sound_handle<0) + { + mprintf ((0,"Couldn't load sound file '%s' in AssignWeaponPage. Weapon=%s\n",weaponpage->sound_name[i],weaponpage->weapon_struct.name)); + weaponpointer->sounds[i]=SOUND_NONE_INDEX; + } + else + weaponpointer->sounds[i]=sound_handle; + } + else + weaponpointer->sounds[i]=SOUND_NONE_INDEX; + } + + + return 1; +} + +// Copies values from a weapon into a weapon_page +void mng_AssignWeaponToWeaponPage(int n,mngs_weapon_page *weaponpage) +{ + weapon *weaponpointer=&Weapons[n]; + int i; + + // Assign the values + memcpy (&weaponpage->weapon_struct,weaponpointer,sizeof(weapon)); + + strcpy (weaponpage->weapon_struct.name,weaponpointer->name); + + if (weaponpointer->hud_image_handle!=-1) + { + if (weaponpointer->flags & WF_HUD_ANIMATED) + strcpy (weaponpage->hud_image_name,GameVClips[weaponpointer->hud_image_handle].name); + else + strcpy (weaponpage->hud_image_name,GameBitmaps[weaponpointer->hud_image_handle].name); + } + else + strcpy (weaponpage->hud_image_name,""); + + if (weaponpointer->fire_image_handle!=-1) + { + if (weaponpointer->flags & WF_IMAGE_BITMAP) + strcpy (weaponpage->fire_image_name,GameBitmaps[weaponpointer->fire_image_handle].name); + else if (weaponpointer->flags & WF_IMAGE_VCLIP) + strcpy (weaponpage->fire_image_name,GameVClips[weaponpointer->fire_image_handle].name); + else + strcpy (weaponpage->fire_image_name,Poly_models[weaponpointer->fire_image_handle].name); + } + else + strcpy (weaponpage->fire_image_name,""); + + // do explosion name + + if (weaponpointer->explode_image_handle!=-1) + strcpy (weaponpage->explode_image_name,GameTextures[weaponpointer->explode_image_handle].name); + else + strcpy (weaponpage->explode_image_name,""); + + // Do particle name + if (weaponpointer->particle_handle!=-1) + strcpy (weaponpage->particle_name,GameTextures[weaponpointer->particle_handle].name); + else + strcpy (weaponpage->particle_name,""); + + // Do spawn name + if (weaponpointer->spawn_handle!=-1) + strcpy (weaponpage->spawn_name,Weapons[weaponpointer->spawn_handle].name); + else + strcpy (weaponpage->spawn_name,""); + + // Do spawn name + if (weaponpointer->alternate_spawn_handle!=-1) + strcpy (weaponpage->alternate_spawn_name,Weapons[weaponpointer->alternate_spawn_handle].name); + else + strcpy (weaponpage->alternate_spawn_name,""); + + // Do robot spawn name + if (weaponpointer->robot_spawn_handle!=-1) + strcpy (weaponpage->robot_spawn_name,Object_info[weaponpointer->robot_spawn_handle].name); + else + strcpy (weaponpage->robot_spawn_name,""); + + + if ((weaponpointer->flags & WF_SMOKE) && weaponpointer->smoke_handle>=0 && GameTextures[weaponpointer->smoke_handle].used) + { + strcpy (weaponpage->smoke_image_name,GameTextures[weaponpointer->smoke_handle].name); + } + else + strcpy (weaponpage->smoke_image_name,""); + + if (weaponpointer->scorch_handle >= 0 && GameTextures[weaponpointer->scorch_handle].used) + { + strcpy (weaponpage->scorch_image_name,GameTextures[weaponpointer->scorch_handle].name); + } + else + strcpy (weaponpage->scorch_image_name,""); + + if (weaponpointer->icon_handle >= 0 && GameTextures[weaponpointer->icon_handle].used) + { + strcpy (weaponpage->icon_name,GameTextures[weaponpointer->icon_handle].name); + } + else + strcpy (weaponpage->icon_name,""); + + + for (i=0;isounds[i]!=SOUND_NONE_INDEX) + strcpy (weaponpage->sound_name[i],Sounds[weaponpointer->sounds[i]].name); + else + strcpy (weaponpage->sound_name[i],""); + } + +} + +// Loads a weapon found in the net table file. It then allocs a weapon and +// then calls SetAndLoadWeapon to actually load in any images/models associated +// with it +void mng_LoadNetWeaponPage(CFILE *infile,bool overlay) +{ + mngs_weapon_page weaponpage; + memset(&weaponpage, 0, sizeof(mngs_weapon_page)); + + if (mng_ReadNewWeaponPage (infile,&weaponpage)) + { + int n = FindWeaponName (weaponpage.weapon_struct.name); + if (n!=-1) + { + if(overlay) + { + mprintf((0,"OVERLAYING WEAPON %s\n",weaponpage.weapon_struct.name)); + mng_FreePagetypePrimitives (PAGETYPE_WEAPON,weaponpage.weapon_struct.name,0); + mng_AssignWeaponPageToWeapon(&weaponpage,n); + } + //mprintf ((0,"Found weapon dependency! You probably should reorder the netpages.\n")); + return; + } + + int ret=mng_SetAndLoadWeapon (&weaponpage,infile); + ASSERT (ret>=0); + } + else + mprintf ((0,"Could not load weaponpage named %s!\n",weaponpage.weapon_struct.name)); +} + +// Reads a weapon page from a local table file. It then allocs a weapon and +// loads any images/models associated with that weapon +void mng_LoadLocalWeaponPage(CFILE *infile) +{ + mngs_weapon_page weaponpage; + int ok=0; + memset(&weaponpage, 0, sizeof(mngs_weapon_page)); + + if (mng_ReadNewWeaponPage (infile,&weaponpage)) + { + // Check to see if this is a local copy that is supposed + // to go over a network copy (supersede the net copy) + + int i=FindWeaponName (weaponpage.weapon_struct.name); + if (i!=-1) + { + // Make sure we really have this page checked out + mngs_Pagelock pl; + + strcpy (pl.name,weaponpage.weapon_struct.name); + pl.pagetype=PAGETYPE_WEAPON; + + /*if (Network_up && Stand_alone==0) + { + int locked=mng_CheckIfPageOwned(&pl,TableUser); + if (locked!=1) + Int3(); // Your local vs net copies of the lock file do not match + }*/ + ok=1; + bool need_to_load_page = true; + + if (Loading_addon_table!=-1) + { + AddOnTablefile *addon; + int tidx; + + // see if we really need to load this page + //check to see if we already have loaded this page (because it was + //a dependancy of another) + addon = &AddOnDataTables[Loading_addon_table]; + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_WEAPON && + !stricmp(addon->Addon_tracklocks[tidx].name,weaponpage.weapon_struct.name) ) + { + // found it!! + mprintf((0,"WeaponPage: %s previously loaded\n",weaponpage.weapon_struct.name)); + need_to_load_page = false; + break; + } + } + } + + if(need_to_load_page) + { + mng_FreePagetypePrimitives (PAGETYPE_WEAPON,weaponpage.weapon_struct.name,0); + mng_AssignWeaponPageToWeapon (&weaponpage,i); + + // For addon data + if (ok && Loading_addon_table!=-1) + { + // this is an overlay of some sort..see which we are overlaying + int overlay = 1; + int addidx,tidx; + bool found = false; + for(addidx = Num_addon_tables-1; addidx>=0; addidx--) + { + if(addidx==Loading_addon_table) + continue; + AddOnTablefile *addon = &AddOnDataTables[addidx]; + + // look for the page in this table file + for(tidx=0;tidxNum_addon_tracklocks;tidx++) + { + if(addon->Addon_tracklocks[tidx].pagetype==PAGETYPE_WEAPON && + !stricmp(addon->Addon_tracklocks[tidx].name,weaponpage.weapon_struct.name) ) + { + // found it!! + found = true; + overlay = addidx+2; + break; + } + } + + if(found) + break; + } + + mng_PushAddonPage (PAGETYPE_WEAPON,weaponpage.weapon_struct.name,overlay); + } + } + } + else + { + // This is a local weapon that has never been checked in + if ((i=mng_SetAndLoadWeapon (&weaponpage,infile))<0) + ok=0; + else ok=1; + + // For addon data + if (ok && Loading_addon_table!=-1) + mng_PushAddonPage (PAGETYPE_WEAPON,weaponpage.weapon_struct.name,0); + } + + //ASSERT (ok==1); + if (ok != 1) + DataError("Error loading weapon <%s>: %s, file <%s>\n",weaponpage.weapon_struct.name,Weapon_error,Weapon_error_filename); + + if (Loading_addon_table==-1) + mng_AllocTrackLock (weaponpage.weapon_struct.name,PAGETYPE_WEAPON); + } + else + + mprintf ((0,"Could not load weaponpage named %s!\n",weaponpage.weapon_struct.name)); + +} + + + + + \ No newline at end of file diff --git a/manage/weaponpage.h b/manage/weaponpage.h new file mode 100644 index 000000000..3ff0c066a --- /dev/null +++ b/manage/weaponpage.h @@ -0,0 +1,69 @@ + +#ifndef WEAPONPAGE_H +#define WEAPONPAGE_H + +#include "manage.h" +#include "weapon.h" +#include "CFILE.H" +#include "pstypes.h" + +typedef struct +{ + weapon weapon_struct; + char hud_image_name[PAGENAME_LEN]; + char fire_image_name[PAGENAME_LEN]; + char explode_image_name[PAGENAME_LEN]; + char smoke_image_name[PAGENAME_LEN]; + char scorch_image_name[PAGENAME_LEN]; + char icon_name[PAGENAME_LEN]; + char spawn_name[PAGENAME_LEN]; + char alternate_spawn_name[PAGENAME_LEN]; + char particle_name[PAGENAME_LEN]; + char robot_spawn_name[PAGENAME_LEN]; + char sound_name[MAX_WEAPON_SOUNDS][PAGENAME_LEN]; +} mngs_weapon_page; + +// Weapon page functions +//--------------------------------------------------------------- + +// Given an open file pointer and a weapon_page struct, writes that weaponpage out +void mng_WriteWeaponPage (CFILE *outfile,mngs_weapon_page *weaponpage); + +// Reads a weapon page from an open file. Returns 0 on error. +int mng_ReadWeaponPage (CFILE *infile,mngs_weapon_page *weaponpage); + +// Given an open file pointer and a weapon_page struct, writes that weaponpage out +void mng_WriteNewWeaponPage (CFILE *outfile,mngs_weapon_page *weaponpage); + +// Reads a weapon page from an open file. Returns 0 on error. +int mng_ReadNewWeaponPage (CFILE *infile,mngs_weapon_page *weaponpage); + + +// Reads in the weaponpage named "name" into weaponpage struct +// Returns 0 on error or couldn't find, else 1 if all is good +int mng_FindSpecificWeaponPage (char *name,mngs_weapon_page *weaponpage,int offset=0); +int mng_FindSpecificWeaponPage (char *name,mngs_weapon_page *weaponpage); + +// Given a weapon page, allocs a weapon and calls AssignWeaponPageToWeapon to actually +// load model and values. Rturns weapon handle on success, -1 if fail +int mng_SetAndLoadWeapon (mngs_weapon_page *weaponpage,CFILE *infile); + +// Given a weaponpage and a weapon handle, attempts to make weapon n correspond to +// to the weaponpage. +// Returns 1 on success, 0 otherwise +int mng_AssignWeaponPageToWeapon(mngs_weapon_page *weaponpage,int n,CFILE *infile=NULL); + +// Copies values from a Weapon into a weapon_page +void mng_AssignWeaponToWeaponPage(int n,mngs_weapon_page *weaponpage); + + +// Reads in a weapon page from the local table file, superseding any weapon +// already in RAM with that same name +void mng_LoadLocalWeaponPage(CFILE *); + +// Reads in a page off the net +void mng_LoadNetWeaponPage (CFILE *,bool overlay=false); + +int mng_GetGuaranteedWeaponPage (char *name,CFILE *infile=NULL); + +#endif \ No newline at end of file diff --git a/md5/CMakeLists.txt b/md5/CMakeLists.txt new file mode 100644 index 000000000..108eef3cb --- /dev/null +++ b/md5/CMakeLists.txt @@ -0,0 +1,8 @@ +SET (HEADERS md5.h) +SET (CPPS + md5.cpp) + +SET (PLATFORMCPPS ) + + +ADD_LIBRARY(md5 STATIC ${HEADERS} ${CPPS} ${PLATFORMCPPS}) diff --git a/md5/md5.cpp b/md5/md5.cpp new file mode 100644 index 000000000..fed273038 --- /dev/null +++ b/md5/md5.cpp @@ -0,0 +1,441 @@ +/* + This is the C++ implementation of the MD5 Message-Digest + Algorithm desrcipted in RFC 1321. + I translated the C code from this RFC to C++. + There is now warranty. + + Feb. 12. 2005 + Benjamin Grüdelbach +*/ + +/* + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. +*/ + +//md5 class include +#include "md5.h" +#include +#include + +#include "stdio.h" +static FILE *md5log = NULL; + +#define MD5_DEBUG_LOG 0 + +#ifdef OUTRAGE_BIG_ENDIAN +void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32_t t; + do { + t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32_t *) buf = t; + buf += 4; + } while (--longs); +} +#else +# define byteReverse(buf, len) /* Nothing */ +#endif + +MD5::~MD5 () +{ +#if MD5_DEBUG_LOG + if(md5log) + { + fclose(md5log); + md5log = NULL; + } +#endif +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void MD5::MD5Init () +{ + MD5_CTX *context = &ctx; + context->buf[0] = 0x67452301; + context->buf[1] = 0xefcdab89; + context->buf[2] = 0x98badcfe; + context->buf[3] = 0x10325476; + + context->bits[0] = 0; + context->bits[1] = 0; +#if MD5_DEBUG_LOG + if(!md5log) + { + md5log = fopen("md5.log","wt+"); + } + if(md5log) + { + fprintf(md5log,"Starting new sum...\n"); + } +#endif +} + +void MD5::MD5Update (float valin) +{ + float val = INTEL_FLOAT(valin); + unsigned char *p = (unsigned char *)&val; +#if MD5_DEBUG_LOG + if(md5log) + { + fprintf(md5log,"[float]"); + } +#endif + MD5Update(p,sizeof(float)); +} + +void MD5::MD5Update (int valin) +{ + int val = INTEL_INT(valin); + unsigned char *p = (unsigned char *)&val; +#if MD5_DEBUG_LOG + if(md5log) + { + fprintf(md5log,"[int]"); + } +#endif + MD5Update(p,sizeof(int)); +} + +void MD5::MD5Update (short valin) +{ + short val = INTEL_SHORT(valin); + unsigned char *p = (unsigned char *)&val; +#if MD5_DEBUG_LOG + if(md5log) + { + fprintf(md5log,"[short]"); + } +#endif + MD5Update(p,sizeof(short)); +} + +void MD5::MD5Update (unsigned int valin) +{ + unsigned int val = INTEL_INT(valin); + unsigned char *p = (unsigned char *)&val; +#if MD5_DEBUG_LOG + if(md5log) + { + fprintf(md5log,"[u_int]"); + } +#endif + MD5Update(p,sizeof(unsigned int)); +} + +void MD5::MD5Update (unsigned char val) +{ + unsigned char *p = (unsigned char *)&val; +#if MD5_DEBUG_LOG + if(md5log) + { + fprintf(md5log,"[u_char]"); + } +#endif + MD5Update(p,sizeof(unsigned char)); +} + + + +/* + MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. +*/ +void MD5::MD5Update (unsigned char *buf, unsigned int len) +{ + MD5_CTX *context = &ctx; +#if MD5_DEBUG_LOG + if(md5log) + { + fprintf(md5log,"(%d) ",len); + for(unsigned int a=0;abits[0]; + if ((context->bits[0] = t + ((uint32_t) len << 3)) < t) + context->bits[1]++; /* Carry from low to high */ + context->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) context->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(context->in, 16); + MD5Transform(context->buf, (uint32_t *) context->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(context->in, buf, 64); + byteReverse(context->in, 16); + MD5Transform(context->buf, (uint32_t *) context->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(context->in, buf, len); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5::MD5Final (unsigned char digest[16]) +{ + MD5_CTX *context = &ctx; + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (context->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = context->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(context->in, 16); + MD5Transform(context->buf, (uint32_t *) context->in); + + /* Now fill the next block with 56 bytes */ + memset(context->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(context->in, 14); + + /* Append length in bits and transform */ + ((uint32_t *) context->in)[14] = context->bits[0]; + ((uint32_t *) context->in)[15] = context->bits[1]; + + MD5Transform(context->buf, (uint32_t *) context->in); + byteReverse((unsigned char *) context->buf, 4); + memcpy(digest, context->buf, 16); + + memset(context, 0, sizeof(* context)); /* In case it's sensitive */ + /* The original version of this code omitted the asterisk. In + effect, only the first part of context was wiped with zeros, not + the whole thing. Bug found by Derek Jones. Original line: */ + // memset(context, 0, sizeof(context)); /* In case it's sensitive */ +} + + + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5::MD5Transform(uint32_t buf[4], uint32_t const in[16]) +{ + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* Encodes input (unsigned long int) into output (unsigned char). Assumes len is + a multiple of 4. + */ +void MD5::Encode (unsigned char *output, unsigned int *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + unsigned int inp = INTEL_INT(input[i]); + output[j] = (unsigned char)(inp & 0xff); + output[j+1] = (unsigned char)((inp >> 8) & 0xff); + output[j+2] = (unsigned char)((inp >> 16) & 0xff); + output[j+3] = (unsigned char)((inp >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (unsigned long int). Assumes len is + a multiple of 4. + */ +void MD5::Decode (unsigned int *output, unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + unsigned int inp0 = INTEL_INT(input[j]); + unsigned int inp1 = INTEL_INT(input[j+1]); + unsigned int inp2 = INTEL_INT(input[j+2]); + unsigned int inp3 = INTEL_INT(input[j+3]); + output[i] = ((unsigned int)inp0) | (((unsigned int)inp1) << 8) | + (((unsigned int)inp2) << 16) | (((unsigned int)inp3) << 24); + } +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +void MD5::MD5_memcpy (POINTER output, POINTER input, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +void MD5::MD5_memset (POINTER output,int value,unsigned int len) +{ + unsigned int i; + for (i = 0; i < len; i++) + { + ((char *)output)[i] = (char)value; + } +} + + +MD5 * MD5::Clone() +{ + MD5 *clone = new MD5(); + clone->ctx = this->ctx; + return clone; +} + +void MD5::Destroy(MD5 *obj) +{ + if(obj) + delete obj; +} \ No newline at end of file diff --git a/md5/md5.h b/md5/md5.h new file mode 100644 index 000000000..f562760a2 --- /dev/null +++ b/md5/md5.h @@ -0,0 +1,76 @@ +/* + This is the C++ implementation of the MD5 Message-Digest + Algorithm desrcipted in RFC 1321. + I translated the C code from this RFC to C++. + There is now warranty. + + Feb. 12. 2005 + Benjamin Grüdelbach +*/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#pragma once + +typedef unsigned char *POINTER; +typedef unsigned int uint32_t; +/* MD5 context. */ +typedef struct _context_md5_t { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +} context_md5_t; + +typedef context_md5_t MD5_CTX; + +class MD5 +{ + + private: + + MD5_CTX ctx; + void MD5Transform(uint32_t buf[4], uint32_t const in[16]); + void Encode (unsigned char*, unsigned int*, unsigned int); + void Decode (unsigned int*, unsigned char*, unsigned int); + void MD5_memcpy (POINTER, POINTER, unsigned int); + void MD5_memset (POINTER, int, unsigned int); + + public: + + void MD5Init (); + + void MD5Update (unsigned char*, unsigned int); + void MD5Update (float val); + void MD5Update (int val); + void MD5Update (unsigned int val); + void MD5Update (unsigned char val); + void MD5Update (short val); + + void MD5Final (unsigned char [16]); + + ~MD5(); + MD5(){}; + + MD5* Clone(); + static void Destroy(MD5 *obj); +}; diff --git a/mem/CMakeLists.txt b/mem/CMakeLists.txt new file mode 100644 index 000000000..ba41df085 --- /dev/null +++ b/mem/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (HEADERS ) +SET (CPPS + mem.cpp) + +ADD_LIBRARY(mem STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/mem/mem.cpp b/mem/mem.cpp new file mode 100644 index 000000000..5fa065b4e --- /dev/null +++ b/mem/mem.cpp @@ -0,0 +1,1228 @@ +/* + * $Logfile: /DescentIII/Main/mem/mem.cpp $ + * $Revision: 56 $ + * $Date: 1/03/02 5:27p $ + * $Author: Matt $ + * + * Memory library + * + * $Log: /DescentIII/Main/mem/mem.cpp $ + * + * 56 1/03/02 5:27p Matt + * Add a longlong cast to prevent math overflow. + * + * 55 4/19/00 5:33p Matt + * From Duane for 1.4 + * Close renderer on malloc fail error exit + * Mac changes + * + * 54 10/21/99 2:25p Kevin + * Mac merge + * + * 53 8/10/99 5:12p Jeff + * added a debug function to dump the current state of the mem library to + * file + * + * 52 7/28/99 2:22p Kevin + * Macintosh Stuff + * + * 51 5/13/99 5:06p Ardussi + * changes for compiling on the Mac + * + * 50 5/02/99 12:24a Jason + * mem was incorrectly choosing lowmem mode on 64 meg machines + * + * 49 4/30/99 5:07p Kevin + * misc dedicated server, networking and low memory enhancements + * + * 48 4/16/99 10:42p Jeff + * moved global variables out of window scope code to all builds for linux + * + * 47 4/15/99 11:09p Jeff + * fixed mem_strdup for linux + * + * 46 4/15/99 9:22p Jeff + * Changes for Linux version + * + * 45 4/12/99 2:23p Kevin + * fixed missing symbol + * + * 44 4/12/99 1:13p Kevin + * Moved call to GetLastError and played with some debugging + * + * 43 3/17/99 11:19a 3dsmax + * Took out debugging + * + * 42 3/16/99 4:49p Kevin + * Fixed realloc + * + * 41 3/15/99 4:32p Jeff + * fixed code so mem library compiles correctly + * + * 40 3/15/99 11:24a Kevin + * Improved memory leak detection for mem_strdup and turned on debugging + * + * 39 3/14/99 2:31p Kevin + * Added mem_heapcheck() function for debugging + * + * 38 3/02/99 5:50p Kevin + * Ouch. Duplicate structures existed and were conflicting. + * + * 37 2/28/99 6:30p Kevin + * Added a call to Error() when unable to alloc memory + * + * 36 1/09/99 4:40p Jeff + * added some ifdefs and fixes to get files to compile under Linux + * + * 35 1/05/99 3:46p Matt + * For Kevin, added call to GetLastError() + * + * 34 11/05/98 2:00p Sean + * + * 33 11/05/98 11:23a Kevin + * fixed bug that caused it not to work under NT + * + * 32 10/30/98 11:23a Samir + * + * 31 10/30/98 11:10a Kevin + * doh! fixed ifdef + * + * 30 10/30/98 11:01a Kevin + * took out mem debug stuff for nt users (temp) + * + * 29 10/28/98 5:01p Kevin + * Improved debugging aids in mem lib + * + * 28 10/21/98 7:18p Matt + * Added quick-exit system to not free individual mem chunks on exit, + * since the whole heap gets freed at the end. + * + * 27 10/18/98 9:11p Matt + * Took the program name out of some error messages. + * + * 26 10/18/98 6:21p Matt + * Changed a Debug_ConsolePrintf() to mprintf() since the former doesn't + * exist in a Release build and the latter compiles out. + * + * 25 10/17/98 12:46p Kevin + * Beta 4 fixes + * + * 24 10/15/98 12:15p Kevin + * put in heap checking code + * + * 23 10/15/98 11:59a Kevin + * removed useage of timer_GetTime and FindArg + * + * 22 10/14/98 11:25p Jeff + * commented out call to HeapDestroy() to keep NT users from crashing on + * exit because it destroys the heap before Sound thread closes + * + * 21 10/14/98 7:20p Kevin + * More dsp changes... + * + * 20 10/14/98 4:39p Matt + * Changed OutrageMessageBox() calls to use either Error() or + * EditorMessageBox() + * + * 19 10/13/98 3:42p Kevin + * bug fixes + * + * 18 10/12/98 8:39p Kevin + * removed mprintf's and fixed some smallish bugs + * + * 17 10/12/98 11:32a Kevin + * doh! heap size was 160 megs + * + * 16 10/12/98 11:25a Kevin + * doh! + * + * 15 10/12/98 10:55a Kevin + * Don't use memory lib for editor currently + * + * 14 10/12/98 10:22a Samir + * made mem_strdup take a const char pointer. + * + * 13 10/09/98 7:40p Kevin + * + * 12 10/09/98 6:55p Kevin + * fized bug with zero byte mallocs and mem_size + * + * 11 10/09/98 3:32p Kevin + * New memory library + * + * 10 10/08/98 4:24p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 9 10/08/98 2:40p Kevin + * + * 8 10/08/98 12:00p Kevin + * Demo system work + * + * 7 9/22/98 3:55p Samir + * ifdef out stuff for non-debug version. + * + * 6 8/31/98 12:42p Samir + * added some debug code. + * + * 5 7/31/98 5:44p Samir + * improved memory debugging. + * + * $NoKeywords: $ + */ +#ifndef LINUX +#include +#endif +#if defined(MACOSX) + +#include +#else +#include +#endif +#include +#include +#include +#ifdef WIN32 +#include +//Non-Linux Includes +#include +#include +#else +#endif + +#include "init.h" +#include "mem.h" +#include "pserror.h" +#include "pstypes.h" +//#include "args.h" +//#include "ddio.h" +// +//#define MEM_DEBUG + +#ifdef MEM_USE_RTL +#pragma message("mem.cpp: Compiling For Run-Time Library usage") +#endif + +#ifdef MEM_DEBUG +#pragma message("mem.cpp: Compiling with Debug Settings") +#endif + +#ifdef MEM_LOGFILE +#pragma message("mem.cpp: Compiling with logfile support") +#endif + +int Total_mem_used=0; +int Mem_high_water_mark = 0; + +#define MEM_NO_MEMORY_PTR 0xdeadbeef +#define MEM_MAX_MALLOCS 199999 +#define MEM_GAURDIAN_SIG 0x2bad +#define MEM_MALLOC_TO_SORT 10000 + +typedef struct mem_alloc_info +{ + int len; + void * ptr; + unsigned short line; + char file[17]; +}mem_alloc_info; + +static void *Mem_failsafe_block = NULL;; +bool Mem_low_memory_mode = false; +bool Mem_superlow_memory_mode = false; +//If this is set, the mem library ignores mem_free() calls. All the memory is then freed at once oon exit. +bool Mem_quick_exit = 0; +#if defined (__LINUX__) +//Linux memory management +int LnxTotalMemUsed; +void mem_shutdown(void) +{ +} +void mem_Init(void) +{ + LnxTotalMemUsed = 0; +} +int mem_GetTotalMemoryUsed(void) +{ + return LnxTotalMemUsed; +} +void *mem_malloc_sub(int size,const char *file,int line) +{ + void *new_mem = malloc(size); + if(!new_mem){ + mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",size,line,file)); + Int3(); + return NULL; + } + LnxTotalMemUsed += size; + return new_mem; +} +void mem_free_sub(void *memblock) +{ + if(memblock){ +#if defined(MACOSX) + LnxTotalMemUsed -= malloc_size(memblock); +#else + LnxTotalMemUsed -= malloc_usable_size(memblock); +#endif + free(memblock); + } +} +void mem_error_msg(const char *file,int line,int size) +{ + mprintf((0,"Memory error (size=%d) line %d in %s\n",size,line,file)); + Int3(); +} +char *mem_strdup_sub(const char *string,char *file,int line) +{ + char *ret = strdup(string); + if(!ret) + { + mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",strlen(string)+1,line,file)); + Int3(); + return NULL; + } + return ret; +} +void *mem_realloc_sub(void *mem,int size) +{ + return realloc(mem,size); +} +int mem_size_sub(void *memblock) +{ +#if defined(MACOSX) + return malloc_size(memblock); +#else + return malloc_usable_size(memblock); +#endif +} +bool mem_dumpmallocstofile(char *filename) +{ + return false; +} +#pragma mark - +#elif defined (MACINTOSH) +#include "ddio.h" +#include "ddio_mac.h" +#include "descent.h" +#include "gamesave.h" +#include "renderer.h" + +//Macintosh memory management +int MacTotalMemUsed; +int MacDynamicStartMem = 0; + +#define FAILSAFE_SIZE 2048 + +#ifdef DAJ_DEBUG +//#define MEM_LOGFILE +#endif +#define USE_MALLOC + +//int Mem_next_slot = 0; +//mem_alloc_info mem_info[MEM_MAX_MALLOCS]; +FILE * mem_out = NULL; + +void mem_shutdown(void) +{ + if (Mem_failsafe_block) { + mem_free(Mem_failsafe_block); + Mem_failsafe_block = NULL; + } + MacTotalMemUsed = 0; +// Mem_next_slot = 0; +} +void mem_Init(void) +{ + if(Mem_failsafe_block) + return; + + Mem_failsafe_block = malloc(FAILSAFE_SIZE); + if (!Mem_failsafe_block) { + Error("No available heap memory."); + } + MacDynamicStartMem = (int)Mem_failsafe_block; + MacTotalMemUsed = 0; +#ifdef MEM_LOGFILE + { + mem_out = fopen("memory.out","wt"); + ASSERT(mem_out); + } +#endif + atexit(mem_shutdown); + return; +} +int mem_GetTotalMemoryUsed(void) +{ + return MacTotalMemUsed - MacDynamicStartMem; +} + +void *mem_malloc_sub(int size,const char *file,int line) +{ + void *new_mem; + + if(size <= 0) + { + mprintf((0, "Warning: Zero byte malloc in %s line %d!\n", file, line)); +// Int3(); + return (void *)MEM_NO_MEMORY_PTR; + } + +#ifdef USE_MALLOC + new_mem = malloc(size); +#else + new_mem = NewPtr(size); +#endif + if(!new_mem){ + if(Mem_failsafe_block == NULL) //Been here already!! + return NULL; + + mem_free (Mem_failsafe_block); + Mem_failsafe_block = NULL; + +#ifdef _DEBUG + mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",size,line,file)); + Debugger(); +#else + rend_Close(); + ::ShowCursor(); + + if (GetFunctionMode() == GAME_MODE) + { + char pathname[PSPATHNAME_LEN]; + ddio_MakePath(pathname, Base_directory, "savegame", "crash", NULL); + SaveGameState(pathname, "crash save"); + + ::Alert(129, NULL); +// ::Alert(128, NULL); + } + else + { + ::Alert(129, NULL); + } +#endif + +// ShutdownD3(); + + exit(0); +// Int3(); +// ExitToShell(); +// return NULL; + } + +#ifdef MEM_LOGFILE + + int mem_addr = (int)new_mem; + if(mem_addr > MacTotalMemUsed) { + MacTotalMemUsed = mem_addr; + fprintf(mem_out, "0x%0X %d %d %s:%d\n", mem_addr, + MacTotalMemUsed - MacDynamicStartMem, size, file, line); + } +#endif + return new_mem; +} +void mem_free_sub(void *memblock) +{ + if((void *)MEM_NO_MEMORY_PTR == memblock) + { + return; + } + + if(memblock){ +#ifdef USE_MALLOC + free(memblock); +#else + DisposePtr((char *)memblock); +#endif + } +#if 0 + for(int i=0; iptr == (b)->ptr) +typedef struct Node_ { + struct Node_ *next; /* next node */ + T data; /* data stored in node */ +} Node; +Node *findNode (T data); +void deleteNode(T data); +Node *insertNode(T data); +hashTableIndex hash(T data); +Node **hashTable; +int hashTableSize = MEM_MAX_MALLOCS; +#ifdef MEM_DEBUG +mem_alloc_info mem_info[MEM_MAX_MALLOCS]; +int Mem_next_slot = 0; +int Mem_highest_used_slot = 0; +FILE * mem_out = NULL; +int Mem_mallocs_since_last_sort = 0; +/* +#undef new +void *operator new(unsigned int size, char *file, int line) +{ + return mem_malloc_sub(size, file, line); +} +void *operator new [](unsigned int size, char *file, int line) +{ + return mem_malloc_sub(size, file, line); +} +void *operator new(unsigned int size) +{ + return mem_malloc_sub(size,"unknown",0); +} +void operator delete(void *ptr) +{ + mem_free_sub(ptr); +} +*/ +#endif +int handle_program_memory_depletion( size_t size ); +HANDLE Heap; +void mem_Init() +{ +#if defined(WIN32) || defined(__LINUX__) +// allocate failsafe block for memory used by any functions after we run out of memory. +// (printf for instance needs heap memory, as well as our error library.) + MEMORYSTATUS ms; +#ifdef MEM_DEBUG + hashTable = (Node **)malloc(hashTableSize * sizeof(Node *)); + for(int a=0;aMem_highest_used_slot) + Mem_highest_used_slot = Mem_next_slot; + } + else + { + for(int i=0;iMem_highest_used_slot) + Mem_highest_used_slot = i; + break; + } + } + + } + } + if(bail_now||(mi==NULL)) +#else + +#endif + { + + mi = &no_track_mi; + if(bail_now==false) + { + track_node = false; +#ifdef MEM_DEBUG + mprintf((0,"Out of memory tracking slots!!!!\n")); +#endif + } + bail_now = true; + + } +#ifndef MEM_DEBUG + mi->ptr = HeapAlloc(Heap,HEAP_NO_SERIALIZE,size); +#else + mi->ptr = HeapAlloc(Heap,HEAP_NO_SERIALIZE,size+2); + mi->len = size; + unsigned short mem_sig = MEM_GAURDIAN_SIG; + memcpy(((char *)mi->ptr)+size,(void *)&mem_sig,2); + int flen = strlen(file); + int lofs = 0; + //Strip down the filename to be < 16 bytes + if(flen > 16) + lofs = flen-16; + + strcpy(mi->file,file+lofs); + mi->line = line; +#ifdef MEM_LOGFILE + fprintf(mem_out,"%s,%d,%d,%d\n",mi->file,mi->line,size,HeapSize(Heap,0,mi->ptr)); +#endif +#endif + int errors; + if(mi->ptr==NULL) + { +#ifndef MACINTOSH + errors = GetLastError(); +#endif + mprintf((0,"Unable to alloc memory in mem_malloc_sub()!\n")); + Int3(); + ASSERT(mi->ptr); + Error("Out of memory, unable to continue."); + mem_error_msg(file, line, size); + return 0; + } + + retp = mi->ptr; + + Total_mem_used+=size; + if(Mem_high_water_markdata; + freemem->ptr=(void *)MEM_NO_MEMORY_PTR; + unsigned short mem_sig = MEM_GAURDIAN_SIG; + if(memcmp((char *)memblock+freemem->len,&mem_sig,2)!=0) + { + // Corrupted memory found when we went to free it. + mprintf((0,"Memory block found to be damaged when it was freed!\n")); + Int3(); + } + Total_mem_used-=freemem->len; + HeapFree(Heap,HEAP_NO_SERIALIZE,memblock); + deleteNode(mynode->data); + return; + } + else + { + mprintf((0,"Warning, hash lookup of memory block failed!\n")); + HeapFree(Heap,HEAP_NO_SERIALIZE,memblock); + return; + } +#endif + HeapFree(Heap,HEAP_NO_SERIALIZE,memblock); +#endif +} +int handle_program_memory_depletion( size_t size ) +{ + // Release character buffer memory. + mem_free (Mem_failsafe_block); + Mem_failsafe_block = NULL; + Error("Unable to allocate %d bytes of memory.", size); +// Tell new to stop allocation attempts. + return 0; +} +// prints out a memory error message +void mem_error_msg(const char *file, int line, int size) +{ + Error ("Attempt to allocate %d bytes of memory in %s line %d failed.",size, file, line); +} +//int strdup_malloc_line = 0; +char * mem_strdup_sub(const char *src,char *file,int line) +{ +#if defined(WIN32) + if(!Heap) + { + return 0; + } +#endif + int len = strlen(src)+1; + //strdup_malloc_line = __LINE__+1; + //Leave this line here! + char * dest = (char *)mem_malloc_sub(len,file,line); + strcpy(dest,src); + return dest; +} +void * mem_realloc_sub(void * memblock,int size) +{ +#ifdef MEM_USE_RTL + void *retp = realloc(memblock,size); + return retp; +#else +#if defined(WIN32) + if(!Heap) + { + return 0; + } +#endif + if((void *)MEM_NO_MEMORY_PTR == memblock) + { + return mem_malloc(size); + } +#ifdef MEM_DEBUG + for(int i=0;idata->file,elem2->data->file); + if(res!=0) + return (res<0)?-1:1; + + if(elem1->data->linedata->line) + return -1; + else if(elem1->data->line>elem2->data->line) + return 1; + + if(elem1->data->lendata->len) + return -1; + else if(elem1->data->len>elem2->data->len) + return 1; + + return 0; +} + +#endif + +bool mem_dumpmallocstofile(char *filename) +{ +#ifdef MEM_DEBUG + FILE *file; + file = fopen(filename,"wt"); + if(!file) + return false; + + int h_table_idx,num_allocs=0; + mem_alloc_info *alloc_info; + char buffer[384]; + + for(h_table_idx=0;h_table_idxfile,alloc_info->file)) && + (last_alloc->line==alloc_info->line) ) + { + //its the same! + continue; + } + } + + last_unique = i; + + if(ifile,alloc_info->file)) && + (next_alloc->line==alloc_info->line) ) + { + // we have multiple of the same line! + // see how many we have of this line + int repeat_count = 1; + int total_size = alloc_info->len; + i++; + while(ifile,alloc_info->file)) && + (next_alloc->line==alloc_info->line) ) + { + repeat_count++; + total_size += next_alloc->len; + }else + { + break; + } + i++; + } + + sprintf(buffer,"\n--Repeated %d Times--\n",repeat_count); + fputs(buffer,file); + + sprintf(buffer,"*[%d bytes total]\tFile %s Line %d\n",total_size,alloc_info->file,alloc_info->line); + fputs(buffer,file); + + continue; + } + } + + sprintf(buffer,"[%d bytes]\tFile %s Line %d\n",alloc_info->len,alloc_info->file,alloc_info->line); + fputs(buffer,file); + } + + free(sorted_allocs); + free(allocs); + + fclose(file); + return true; +#else + return false; +#endif + +} + + +void mem_heapcheck(void) +{ +#ifdef MEM_DEBUG + for(int i=0;iptr+freemem->len,&mem_sig,2)!=0) + { + mprintf((0,"Memory block found to be damaged in mem_heapcheck()!\n")); + mprintf((0,"Originally allocated from file %s, line %d\n",freemem->file,freemem->line)); + Int3(); + } + } +#endif +} +hashTableIndex hash(T data) { + /*********************************** + * hash function applied to data * + ***********************************/ + unsigned int hval = (unsigned int)data->ptr; + return (hval % MEM_MAX_MALLOCS); +} +Node *insertNode(T data) +{ + Node *p, *p0; + hashTableIndex bucket; + /************************************************ + * allocate node for data and insert in table * + ************************************************/ + /* insert node at beginning of list */ + bucket = hash(data); + if ((p = (Node *)malloc(sizeof(Node))) == 0) { + fprintf (stderr, "out of memory (insertNode)\n"); + exit(1); + } + p0 = hashTable[bucket]; + hashTable[bucket] = p; + p->next = p0; + p->data = data; + return p; +} +void deleteNode(T data) +{ + Node *p0, *p; + hashTableIndex bucket; + /******************************************** + * delete node containing data from table * + ********************************************/ + /* find node */ + p0 = 0; + bucket = hash(data); + p = hashTable[bucket]; + while (p && !compEQ(p->data, data)) { + p0 = p; + p = p->next; + } + if (!p) return; + /* p designates node to delete, remove it from list */ + if (p0) + /* not first node, p0 points to previous node */ + p0->next = p->next; + else + /* first node on chain */ + hashTable[bucket] = p->next; + free (p); +} +Node *findNode (T data) +{ + Node *p; + /******************************* + * find node containing data * + *******************************/ + p = hashTable[hash(data)]; + while (p && !compEQ(p->data, data)) + p = p->next; + return p; +} +#endif + diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt new file mode 100644 index 000000000..8aaa7aff2 --- /dev/null +++ b/misc/CMakeLists.txt @@ -0,0 +1,10 @@ +SET (HEADERS ) +SET (CPPS + endian.cpp + error.cpp + logfile.cpp + psglob.cpp + psrand.cpp + pstring.cpp) + +ADD_LIBRARY(misc STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/misc/endian.cpp b/misc/endian.cpp new file mode 100644 index 000000000..c05d8a70e --- /dev/null +++ b/misc/endian.cpp @@ -0,0 +1,97 @@ +/* +* $Logfile: /DescentIII/Main/misc/endian.cpp $ +* $Revision: 5 $ +* $Date: 10/21/99 2:29p $ +* $Author: Kevin $ +* +* Endian detection and usage routines +* +* $Log: /DescentIII/Main/misc/endian.cpp $ + * + * 5 10/21/99 2:29p Kevin + * Mac Merge + * + * 4 5/05/99 5:27a Jeff + * renamed endian.h to psendian.h + * + * 3 5/01/99 8:47p Jeff + * removed 2 unused functions + * + * 2 5/01/99 2:52p Jeff + * added automatic endian detection of the system +* +* $NoKeywords: $ +*/ + +#include "psendian.h" +#include "mono.h" + +#define SWAPSHORT(x) ((0xffff & ((x) << 8) | (((unsigned short)(x)) >> 8) )) +#define SWAPINT(x) ( ((x) << 24) | (((unsigned long)(x)) >> 24) | (((x) & 0x0000ff00) << 8) | (((x) & 0x00ff0000) >> 8) ) +inline float SWAPFLOAT(float x){int i = SWAPINT(*((int *) &(x)));return *((float *) &(i));} + +#define ENDIAN_BIG_ENDIAN 0 +#define ENDIAN_LITTLE_ENDIAN 1 +signed char Endian_type = -1; + +// Endian_IsLittleEndian +// +// Returns true if the machine is Little Endian (i.e. 80x86) +// Returns false if the machine is Big Endian (i.e. Macintosh) +bool Endian_IsLittleEndian(void) +{ + char c_vals[2] = { 0x00, 0x01 }; + short *s_val; + + s_val = (short *)c_vals; + + if((*s_val)==0x01) + { + //we have a Big Endian machine + mprintf((0,"Machine is BIG ENDIAN format\n")); + Endian_type = ENDIAN_BIG_ENDIAN; + return false; + } + + //else s_val == 256 + //we have a Little Endian machine + mprintf((0,"Machine is LITTLE ENDIAN format\n")); + Endian_type = ENDIAN_LITTLE_ENDIAN; + return true; +} + +// Swaps (if needed) a short value (2 bytes) (assumes incoming value is in little endian format) +short Endian_SwapShort(short value) +{ + if(Endian_type==-1) + Endian_IsLittleEndian(); //detect endianness + + if(Endian_type==ENDIAN_BIG_ENDIAN) + value = SWAPSHORT(value); + + return value; +} + +// Swaps (if needed) an int value (4 bytes) (assumes incoming value is in little endian format) +int Endian_SwapInt(int value) +{ + if(Endian_type==-1) + Endian_IsLittleEndian(); //detect endianness + + if(Endian_type==ENDIAN_BIG_ENDIAN) + value = SWAPINT(value); + + return value; +} + +// Swaps (if needed) a float value (4 bytes) (assumes incoming value is in little endian format) +float Endian_SwapFloat(float value) +{ + if(Endian_type==-1) + Endian_IsLittleEndian(); //detect endianness + + if(Endian_type==ENDIAN_BIG_ENDIAN) + value = SWAPFLOAT(value); + + return value; +} diff --git a/misc/error.cpp b/misc/error.cpp new file mode 100644 index 000000000..1ed723797 --- /dev/null +++ b/misc/error.cpp @@ -0,0 +1,293 @@ +/* + * $Logfile: /DescentIII/Main/misc/error.cpp $ + * $Revision: 26 $ + * $Date: 10/09/01 5:40p $ + * $Author: Matt $ + * + * Error-handling functions + * + * $Log: /DescentIII/Main/misc/error.cpp $ + * + * 26 10/09/01 5:40p Matt + * Made -nocrashbox work in release build. + * + * 25 9/15/01 1:46p Kevin + * Added -nocrashbox to suppress crash dialog box + * + * 24 4/19/00 5:23p Matt + * From Duane for 1.4 + * Changed to Mac error exit message + * + * 23 5/05/99 5:22p Samir + * exit(0) instead of exit(1) and other stuff to see why error isn't + * exiting in release? + * + * 22 4/14/99 1:35a Jeff + * fixed case mismatched #includes + * + * 21 4/01/99 4:50p Matt + * Took out Warning() function, chaning those calls to mprintf() + * + * 20 3/02/99 5:50p Kevin + * Ouch. Duplicate structures existed and were conflicting. + * + * 19 2/15/99 1:22p Kevin + * Changes for GameGauge + * + * 18 1/10/99 6:47a Jeff + * Changes made to get linux version to compile + * + * 17 11/01/98 1:58a Jeff + * converted the vsprintf calls to use the Pvsprintf, which is a safe + * vsprintf, no buffer overflows allowed + * + * 16 10/18/98 8:52p Matt + * Revamped debug/error system. + * + */ + +#include +#include +#include +#include + +#include "mono.h" +#include "pserror.h" +#include "pstring.h" +#include "debug.h" +#include "application.h" + +#define MAX_MSG_LEN 2000 + + +void Default_dbgbrk_callback(); + +// Debug break chain handlers +void (*DebugBreak_callback_stop)() = NULL; +void (*DebugBreak_callback_resume)() = NULL; + +// library initialized flag +static bool Error_initialized = false; + +// message that is printed out when error occurs +static char Exit_message[MAX_MSG_LEN]; +static char App_title[32]; +static char Exit_title_str[64]; + +// error message output function +void error_Spew(); + + +////////////////////////////////////////////////////////////////////////////// + +// initializes error handler. +bool error_Init(bool debugger, bool mono_debug, const char *app_title) +{ + Debug_Init(debugger, mono_debug); + + Error_initialized = true; + Exit_message[0] = 0; + + strncpy(App_title, app_title, sizeof(App_title)-1); + App_title[sizeof(App_title)-1] = 0; + + strncpy(Exit_title_str, app_title, sizeof(Exit_title_str)-1); + Exit_title_str[sizeof(Exit_title_str)-1] = 0; + + atexit(error_Spew); + + return true; +} + +extern int no_debug_dialog; + +// exits the application and prints out a standard error message +void Error(char *fmt,...) +{ + va_list arglist; + int exit_msg_len; + +#ifdef MACINTOSH + strcpy(Exit_message,""); +#else + strcpy(Exit_message,"Error: "); +#endif + + va_start(arglist,fmt); + exit_msg_len = strlen(Exit_message); + Pvsprintf(Exit_message+exit_msg_len,MAX_MSG_LEN-exit_msg_len,fmt,arglist); + va_end(arglist); + +#ifdef MACINTOSH + Debug_ErrorBox(OSMBOX_OK, "Fatal Error", Exit_message, ""); +#else + sprintf(Exit_title_str,"%s Error",App_title); +#endif + mprintf ((0,"%s\n",Exit_message)); + +#ifdef _DEBUG + int answer; + + if (DebugBreak_callback_stop) + (*DebugBreak_callback_stop)(); + + if (Debug_break) + answer = Debug_ErrorBox(OSMBOX_ABORTRETRYIGNORE,Exit_title_str,Exit_message,"Press RETRY to debug."); + else if(!no_debug_dialog) + answer = Debug_ErrorBox(OSMBOX_OKCANCEL,Exit_title_str,Exit_message,"Press OK to exit, CANCEL to ignore this error and continue."); + + if(no_debug_dialog) + answer = IDOK; + + switch (answer) { + case IDRETRY: + debug_break(); //Step Out of this function to see where Error() was called + //fall into ignore/cancel case + case IDIGNORE: + case IDCANCEL: + if (DebugBreak_callback_resume) + (*DebugBreak_callback_resume)(); + return; + case IDOK: + case IDABORT: + break; //do nothing, and exit below + } + + //Clear the message, since we don't need to see it again when we exit + Exit_message[0] = 0; + +#else + if (!Error_initialized) + error_Spew(); +#endif + + //Clear the DEBUG_BREAK() callbacks + SetDebugBreakHandlers(NULL,NULL); + + //Leave the program + exit(0); +} + + + + +//Brings up an error message for an int3 +void Int3MessageBox(char *file,int line) +{ +#ifndef RELEASE + char title[128],message[500]; + int answer; + + sprintf(title,"%s Debug Break",App_title); + sprintf(message,"Int3 in %s at line %d.",file,line); + + if (DebugBreak_callback_stop) + (*DebugBreak_callback_stop)(); + + answer = Debug_ErrorBox(OSMBOX_YESNO,title,message,"It's probably safe to continue. Continue?"); + + if (answer == IDNO) { + SetDebugBreakHandlers(NULL,NULL); + exit(1); + } + + if (DebugBreak_callback_resume) + (*DebugBreak_callback_resume)(); +#endif +} + +// prints out an assertion error +void AssertionFailed(char *expstr, char *file, int line) +{ +#ifndef RELEASE + char title[128],message[500]; + int answer; + + sprintf(title,"%s Assertion Failed",App_title); + sprintf(message, "Assertion failed: (%s)\n\nFile %s at line %d.", expstr, file, line); + + if (DebugBreak_callback_stop) + (*DebugBreak_callback_stop)(); + + answer = Debug_ErrorBox(OSMBOX_YESNO,title,message,"Continue?"); + + if (answer == IDNO) { + SetDebugBreakHandlers(NULL,NULL); + exit(1); + } + + if (DebugBreak_callback_resume) + (*DebugBreak_callback_resume)(); +#endif //#ifndef RELEASE +} + +// error message output function +void error_Spew() +{ + if (Exit_message[0] && !no_debug_dialog) + Debug_MessageBox(OSMBOX_OK,Exit_title_str,Exit_message); +} + + +////////////////////////////////////////////////////////////////////////////// +// MESSAGE BOX SYSTEM + +char Messagebox_title[80]="Message"; + +// Sets the title for future OutrageMessageBox() dialogs +void SetMessageBoxTitle(char *title) +{ + strncpy(Messagebox_title,title,sizeof(Messagebox_title)); +} + +#define BUF_LEN 1024 + +// Pops up a dialog box to display a message +void OutrageMessageBox(char *str, ...) +{ + char buf[BUF_LEN]; + va_list arglist; + int nchars; + + va_start(arglist, str); + nchars = Pvsprintf(buf, BUF_LEN,str, arglist); + va_end(arglist); + + if (nchars >= BUF_LEN) + Debug_MessageBox(OSMBOX_OK, Messagebox_title, "The dialog that follows this one overflowed its text buffer. The program may crash."); + + Debug_MessageBox(OSMBOX_OK, Messagebox_title, buf); + +} + + +int OutrageMessageBox(int type, char *str, ...) +{ + char buf[BUF_LEN]; + va_list arglist; + int os_flags; + int nchars; + + va_start(arglist, str); + nchars = Pvsprintf(buf, BUF_LEN,str, arglist); + va_end(arglist); + + if (type == MBOX_OK) + os_flags = OSMBOX_OK; + else if (type == MBOX_YESNO) + os_flags = OSMBOX_YESNO; + else if (type == MBOX_YESNOCANCEL) + os_flags = OSMBOX_YESNOCANCEL; + else if (type == MBOX_ABORTRETRYIGNORE) + os_flags = OSMBOX_ABORTRETRYIGNORE; + else + Int3(); + + if (nchars >= BUF_LEN) + Debug_MessageBox(OSMBOX_OK, Messagebox_title, "The dialog that follows this one overflowed its text buffer. The program may crash."); + + return Debug_MessageBox(os_flags, Messagebox_title, buf); +} + + + diff --git a/misc/logfile.cpp b/misc/logfile.cpp new file mode 100644 index 000000000..1708f3951 --- /dev/null +++ b/misc/logfile.cpp @@ -0,0 +1,111 @@ +#include "logfile.h" +#include "pstring.h" +#include +#include + +#ifdef _DEBUG +static bool log_enable = true; +#else +static bool log_enable = false; +#endif + + +void log_Enable(bool enable) +{ + log_enable= true; +} + + +void log_Disable() +{ + log_enable = false; +} + + +logfile::logfile() +{ + fp = NULL; +} + + +logfile::~logfile() +{ + end(); +} + + +// restarts the logfile (opens a new one.) +void logfile::start(const char *fname, const char *longname) +{ + if (log_enable) { + try + { + fp = (FILE*)fopen(fname, "wt"); + logfile::printf("%s\n",longname); + } + catch(...) + { + fp = NULL; + } + } +} + + +void logfile::end() +{ + if (fp) { + try + { + fclose((FILE*)fp); + fp = NULL; + } + catch(...) + { + fp = NULL; + } + } +} + + +void logfile::printf(const char *fmt, ...) +{ + if (fp && fmt) { + char msg[256]; + va_list arglist; + va_start(arglist,fmt); + Pvsprintf(msg,sizeof(msg),fmt,arglist); + va_end(arglist); + + logfile::puts(msg); + } +} + + +void logfile::puts(const char *msg) +{ + if (fp && msg) { + try + { + fputs(msg,(FILE*)fp); + } + catch(...) + { + end(); + } + } +} + + +void logfile::update() +{ + if (fp) { + try + { + fflush((FILE*)fp); + } + catch(...) + { + end(); + } + } +} diff --git a/misc/psglob.cpp b/misc/psglob.cpp new file mode 100644 index 000000000..86f545f23 --- /dev/null +++ b/misc/psglob.cpp @@ -0,0 +1,196 @@ +/* +* $Logfile: /DescentIII/Main/misc/psglob.cpp $ +* $Revision: 3 $ +* $Date: 4/15/99 11:10p $ +* $Author: Jeff $ +* +* string globbing functions +* +* $Log: /DescentIII/Main/misc/psglob.cpp $ + * + * 3 4/15/99 11:10p Jeff + * fixed gcc warning + * + * 2 1/07/99 10:51p Jeff + * added psglob and support to do find in files for hog files +* +* $NoKeywords: $ +*/ + +#include "psglob.h" +#include +#include + +#ifdef __LINUX__ +#include +#endif + +// Returns 1 if string contains globbing characters in it +int PSGlobHasPattern(char *string) +{ + register char *p = string; + register char c; + int open = 0; + + while ((c = *p++)!='\0') + { + switch (c){ + case '?': + case '*': return 1; + case '[': // Open brackets must have a matching close bracket in order to be a glob + open++; + continue; + case ']': + if (open) + return 1; + continue; + case '\\': + if (*p++ == '\0') + return 0; + } + } + + return 0; +} + +// PSGlobMatch +// Matches the pattern passed in to the string in text. If the pattern matches +// it returns 1, if not, it returns 0. In order to have a match, the following +// conditions must be met: +// 1) The entire string (text) is used for matching. +// 2) '*' matches any sequence of characters. +// 3) '?' matches any character +// 4) [SET] matches any character in the specified set. +// 5) [!SET] matches any character _not_ in the specified set. +// A set is composed of characters or ranges. A range looks like +// character hyphen character (as in 0-9 or A-Z). [0-9a-zA-Z_] is the set +// of all characters allowed in C identifiers. +// 6) Any other character in the pattern must be matched exactly.(see 9) +// 7) Because of the syntactic significance of []*?!- and \ to match +// these characters exactly, preced it with a '\'. +// 8) If dot_special is not zero, '*' and '?' do not match '.' at the beginning of text +// 9) If case_sensitive is 0, than case does not matter for the non-pattern characters +int PSGlobMatch(char *pattern, char *text, int case_sensitive, int dot_special) +{ + register char *p = pattern, *t = text; + register char c; + + while ((c = *p++) != '\0'){ + switch (c){ + case '?': + if (*t == '\0' || (dot_special && t == text && *t == '.')) + return 0; + else + ++t; + break; + case '\\': + if (*p++ != *t++) + return 0; + break; + case '*': + if (dot_special && t == text && *t == '.') + return 0; + return PSGlobMatchAfterStar(p, case_sensitive, t); + case '[': + { + register char c1 = *t++; + int invert; + + if (!c1) + return (0); + + invert = ((*p == '!') || (*p == '^')); + if (invert) + p++; + c = *p++; + + while (1){ + register char cstart = c, cend = c; + if (c == '\\'){ + cstart = *p++; + cend = cstart; + } + + if (c == '\0') + return 0; + c = *p++; + + if (c == '-' && *p != ']'){ + cend = *p++; + if (cend == '\\') + cend = *p++; + if (cend == '\0') + return 0; + c = *p++; + } + + if (c1 >= cstart && c1 <= cend) + goto match; + if (c == ']') + break; + } + + if (!invert) + return 0; + break; +match: + /* Skip the rest of the [...] construct that already matched. */ + while (c != ']'){ + if (c == '\0') + return 0; + c = *p++; + if (c == '\0') + return 0; + else if (c == '\\') + ++p; + } + + if (invert) + return 0; + break; + } + default: + if(case_sensitive){ + if (c != *t++) + return 0; + }else{ + if (tolower(c) != tolower(*t++)) + return 0; + } + } + } + + return *t == '\0'; +} + +//Like PSGlobMatch, but match pattern against any final segment of text +int PSGlobMatchAfterStar(char *pattern, int case_sensitive, char *text) +{ + register char *p = pattern, *t = text; + register char c, c1; + + while ((c = *p++) == '?' || c == '*'){ + if (c == '?' && *t++ == '\0') + return 0; + } + + if (c == '\0') + return 1; + if (c == '\\') + c1 = *p; + else + c1 = c; + + while (1){ + if(case_sensitive){ + if ((c == '[' || *t == c1) && PSGlobMatch(p - 1, t, case_sensitive, 0)) + return 1; + }else{ + if ((c == '[' || tolower(*t) == tolower(c1)) && PSGlobMatch(p - 1, t, case_sensitive, 0)) + return 1; + } + + if (*t++ == '\0') + return 0; + } +} diff --git a/misc/psrand.cpp b/misc/psrand.cpp new file mode 100644 index 000000000..d1794fdca --- /dev/null +++ b/misc/psrand.cpp @@ -0,0 +1,33 @@ +/* +* $Logfile: /DescentIII/Main/misc/psrand.cpp $ +* $Revision: 2 $ +* $Date: 4/21/99 11:05a $ +* $Author: Kevin $ +* +* Outrage random number generator code +* +* $Log: /DescentIII/Main/misc/psrand.cpp $ + * + * 2 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 1 4/21/99 10:16a Kevin +* +* $NoKeywords: $ +*/ + +#include "psrand.h" + +static long ps_holdrand = 1L; + +//These are adapted from the C runtime lib. Pretty simple. + +void ps_srand(unsigned int seed) +{ + ps_holdrand = (long)seed; +} + +int ps_rand(void) +{ + return(((ps_holdrand = ps_holdrand * 214013L + 2531011L) >> 16) & 0x7fff); +} diff --git a/misc/pstring.cpp b/misc/pstring.cpp new file mode 100644 index 000000000..caf755159 --- /dev/null +++ b/misc/pstring.cpp @@ -0,0 +1,164 @@ +/* +* $Logfile: /DescentIII/Main/misc/pstring.cpp $ +* $Revision: 4 $ +* $Date: 4/15/99 1:51a $ +* $Author: Jeff $ +* +* Safe string manipulation and creation functions +* +* $Log: /DescentIII/Main/misc/pstring.cpp $ + * + * 4 4/15/99 1:51a Jeff + * changes for linux compile + * + * 3 12/16/98 1:57p Samir + * Replaced CleanupString2 with CleanupStr + * + * 2 11/01/98 1:56a Jeff + * added pstring.cpp/.h +* +* $NoKeywords: $ +*/ + +#include +#include +#include +#include +#include "pstring.h" + +#ifdef __LINUX__ +#include "lnxfix.h" +#endif + + +// Pvsprintf +// Similar to vsprintf/_vsnprintf, however handles the case of a buffer overflow. Arguments are the +// same as _vsnprintf. In the case of count, pass in the size of the buffer, in the case where there is +// a buffer overflow, a \0 will be tacked on as the last byte in the buffer, ensuring a valid string. +int Pvsprintf(char *buffer, int count, const char *format, va_list argptr) +{ + int ret = _vsnprintf(buffer,count-1,format,argptr); + if(ret==-1) + buffer[count-1] = '\0'; + return ret; +} + +// Psprintf +// Similar to sprintf/_snprintf, however handles the case of a buffer overflow. Arguments are the +// same as _snprintf. In the case of count, pass in the size of the buffer, in the case where there is +// a buffer overflow, a \0 will be tacked on as the last byte in the buffer, ensuring a valid string. +int Psprintf(char *buffer, int count, const char *format, ... ) +{ + va_list ap; + va_start(ap,format); + int ret = Pvsprintf(buffer,count,format,ap); + va_end(ap); + return ret; +} + + +// CleanupStr +// this function strips all leading and trailing spaces, keeping internal spaces. this goes +// for tabs too. +int CleanupStr(char *dest, const char *src, int destlen) +{ + int i,j, err, begin=0, end=0, len; + + err = 0; + + len = strlen(src); + for (i = 0; i < len; i++) + { + char ch; + ch = src[i]; + + // mark beginning. + if ((ch>' ' && ch>'\t') && err < 1) { err = 1; begin = i; end = i; } + else if (ch == ' ' && err == 1) { err = 2; end = i; } + else if (ch > ' ' && err >= 1) { end = i; } + } + + j = 0; + for (i = begin; i < (end+1); i++) + { + char ch; + ch = src[i]; + if (j == destlen-1) break; + if (ch != '\"') dest[j++] = ch; + } + + dest[j] = 0; + + return 0; +} + + +// tStringTok +// you may start a string tokenization object by calling the start function +// then call next, to get the following tokens. +// note that this class uses it's own copy of the string to ensure that strtok doesn't +// get corrupted. + +tStringTok::~tStringTok() +{ + if (m_strbuf) + delete[] m_strbuf; +} + + +char *tStringTok::start(const char *str, const char *tokens) +{ +// if we pass a null string, then reset this object + if (str == NULL) { + if (m_strbuf) + delete[] m_strbuf; + m_strbuf = NULL; + m_curptr = NULL; + return NULL; + } + + char *new_str = new char[strlen(str)+1]; + +// copy string into new string buffer. AFTER THIS, delete the current string buffer, since the pointer +// passed in could point to the current m_strbuf ptr. + strcpy(new_str, str); + + if (m_strbuf) + delete[] m_strbuf; + m_strbuf = new_str; + m_curptr = m_strbuf; + + return this->next(tokens); +} + + +char *tStringTok::next(const char *tokens) +{ +// create string by terminating m_strbuf when a token is it. + char *cur_str, *end_str; + int slen2, j; + + if (!m_curptr) + return m_curptr; + + slen2 = strlen(tokens); + cur_str = NULL; + + for (j = 0; j < slen2; j++) + { + end_str = strchr(m_curptr, tokens[j]); + if (end_str) { + *end_str = 0; + cur_str = m_curptr; + m_curptr = end_str+1; + return cur_str; + } + } + // at this point, we found no tokens, so m_curptr will point to the string we want to return. + // then we set m_curptr to NULL, telling any subsequent calls to next to return NULL. + + cur_str = m_curptr; + m_curptr = NULL; + + return cur_str; +} diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt new file mode 100644 index 000000000..2650bdaf1 --- /dev/null +++ b/model/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (HEADERS ) +SET (CPPS + newstyle.cpp + polymodel.cpp) + +ADD_LIBRARY(model STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/model/newstyle.cpp b/model/newstyle.cpp new file mode 100644 index 000000000..117ff9137 --- /dev/null +++ b/model/newstyle.cpp @@ -0,0 +1,1447 @@ +/* + * $Logfile: /DescentIII/Main/model/newstyle.cpp $ + * $Revision: 122 $ + * $Date: 3/20/00 12:28p $ + * $Author: Matt $ + * + * Polygon model code + * + * $Log: /DescentIII/Main/model/newstyle.cpp $ + * + * 122 3/20/00 12:28p Matt + * Merge of Duane's post-1.3 changes. + * Check for NULL pointer. + * + * 121 7/08/99 5:47p Jason + * changes for new bumpmapping system in 1.1 update patch + * + * 120 6/08/99 1:00p Jason + * changes for bumpmapping + * + * 119 5/18/99 10:31a Jason + * polymodel stuff wasn't getting paged in before drawing + * + * 118 5/14/99 2:03p Jason + * added polymodel errors to catch bugs + * + * 117 5/14/99 12:02a Jason + * more speedups for g3_DrawPoly + * + * 116 5/13/99 8:33p Matt + * Consolidated Mac & Windows code. + * + * 115 5/13/99 5:07p Ardussi + * changes for compiling on the Mac + * + * 114 5/04/99 4:34p Jason + * changes for bumpmapping + * + * 113 4/22/99 8:29p Kevin + * made psrand.h come after stdlib.h + * + * 112 4/21/99 11:05a Kevin + * new ps_rand and ps_srand to replace rand & srand + * + * 111 4/16/99 5:32p Jason + * fixed smooth specularity with objects + * + * 110 4/14/99 1:36a Jeff + * fixed case mismatched #includes + * + * 109 4/09/99 12:06p Jason + * made model setup code faster + * + * 108 3/31/99 12:26p Jason + * fixed some issues related to non wbuffer cards + * + * 107 3/24/99 10:22a Matt + * Fixed null-pointer bug + * + */ + +#include "pserror.h" +#include "pstypes.h" + +#include "3d.h" +#include "vecmat.h" +#include "grdefs.h" +#include "polymodel.h" +#include "gametexture.h" +#include "BYTESWAP.H" +#include "renderer.h" +#include "lighting.h" +#include "game.h" +#include "render.h" +#include "fireball.h" +#include "lightmap_info.h" +#include "lightmap.h" +#include "lighting.h" +#include "findintersection.h" + + +#include +#include +#include + +#include "psrand.h" + +static float face_depth[MAX_POLYGON_VECS]; +static ubyte triangulated_faces[MAX_FACES_PER_ROOM]; + +static ubyte FacingPass=0; +static int Multicolor_texture=-1; + +static vector Fog_plane; +static float Fog_distance,Fog_eye_distance; +static vector Fog_view_pos,Specular_view_pos,Bump_view_pos; +static matrix Unscaled_bumpmap_matrix; + +static int ModelFaceSortFunc(const short *a, const short *b) +{ + float az,bz; + + az = face_depth[*a]; + bz = face_depth[*b]; + + if (az < bz) + return -1; + else if (az > bz) + return 1; + else + return 0; +} + +#ifdef _DEBUG +void model_draw_outline(int nverts,g3Point **pointlist) +{ + int i; + + for (i=0;ifaces[facenum]; + int modelnum=sm-pm->submodel; + texture *texp=NULL; + int t; + int custom=0; + g3Codes face_cc; + int triface=0; + + face_cc.cc_and=0xff; + face_cc.cc_or=0; + + triangulated_faces[facenum]=0; + + + if (sm->flags & SOF_CUSTOM) + custom=1; + + // Setup texturing + if (fp->texnum!=-1) + texp=&GameTextures[pm->textures[fp->texnum]]; + + if (texp && custom && Polymodel_use_effect && (Polymodel_effect.type & PEF_CUSTOM_TEXTURE)) + texp=&GameTextures[Polymodel_effect.custom_texture]; + + // Set radiosity lightmaps if needed + if (Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP) + { + rend_SetOverlayMap (LightmapInfo[Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].lmi_handle].lm_handle); + + } + + // Do bump mapping + if ((Polymodel_effect.type & PEF_BUMPMAPPED) &&texp && texp->bumpmap!=-1 && Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD ) + { + rend_SetOverlayType (OT_NONE); + rend_SetBumpmapReadyState(1,texp->bumpmap); + if ((GameTextures[fp->texnum].flags & TF_SMOOTH_SPECULAR)) + smooth=1; + } + + float uchange=0,vchange=0; + + // Figure out if there is any texture sliding + if (texp && texp->slide_u!=0) + { + int int_time=Gametime/texp->slide_u; + float norm_time=Gametime-(int_time*texp->slide_u); + norm_time/=texp->slide_u; + + uchange=norm_time; + } + + if (texp && texp->slide_v!=0) + { + int int_time=Gametime/texp->slide_v; + float norm_time=Gametime-(int_time*texp->slide_v); + norm_time/=texp->slide_v; + vchange=norm_time; + } + + ASSERT (fp->nverts<100); + + // Setup the points for this face + for (t=0;tnverts;t++) + { + g3Point *p = &Robot_points[fp->vertnums[t]]; + pointlist[t] = p; + + if (texp) + { + p->p3_uvl.u = fp->u[t]+uchange; + p->p3_uvl.v = fp->v[t]+vchange; + p->p3_uvl.a = sm->alpha[fp->vertnums[t]]; + p->p3_flags |=PF_UV+PF_RGBA+PF_L; + + // Assign bump mapping coords + if ((Polymodel_effect.type & PEF_BUMPMAPPED) && texp->bumpmap!=-1 && Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD ) + { + p->p3_flags|=PF_UV2; + + vector vert=sm->verts[fp->vertnums[t]]; + + vector vertnorm; + + if (smooth) + vertnorm=sm->vertnorms[fp->vertnums[t]]; + else + vertnorm=fp->normal; + + vector subvec=Bump_view_pos-vert; + vm_NormalizeVectorFast (&subvec); + + vector incident_norm=vert-Polymodel_bump_pos; + vm_NormalizeVectorFast (&incident_norm); + + float d=incident_norm * vertnorm; + vector upvec=d * vertnorm; + incident_norm-=(2*upvec); + + float dotp=(subvec * incident_norm); + + if (dotp<0) + dotp=0; + if (dotp>1) + dotp=1; + + float val=dotp*.5; + + p->p3_uvl.u2=val; + p->p3_uvl.v2=val; + } + } + + if (Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP) + { + p->p3_flags |=PF_UV2; + p->p3_uvl.u2=Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].u2[t]; + p->p3_uvl.v2=Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].v2[t]; + } + + face_cc.cc_or|=p->p3_codes; + face_cc.cc_and&=p->p3_codes; + } + + if (face_cc.cc_or && Polymodel_use_effect && (Polymodel_effect.type & (PEF_FOGGED_MODEL|PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES))) + { + triface=1; + triangulated_faces[facenum]=1; + } + + // If there is a texture, set it up + if (texp) + { + bm_handle=GetTextureBitmap(texp-GameTextures,0); + + rend_SetTextureType (TT_LINEAR); + if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD) + rend_SetLighting (LS_GOURAUD); + + // If this is a light texture, make the texture full bright + if (texp->flags & TF_LIGHT) + { + rend_SetLighting (LS_FLAT_GOURAUD); + rend_SetFlatColor (GR_RGB(255,255,255)); + } + + // Setup custom color if there is one + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_CUSTOM_COLOR) && (texp-GameTextures)==Multicolor_texture) + { + int r,g,b; + + rend_SetLighting (LS_FLAT_GOURAUD); + + r=GR_COLOR_RED (Polymodel_effect.custom_color); + g=GR_COLOR_GREEN (Polymodel_effect.custom_color); + b=GR_COLOR_BLUE (Polymodel_effect.custom_color); + + if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD) + { + if (Polymodel_use_effect && Polymodel_effect.type & PEF_COLOR) + { + r=Polymodel_effect.r*(float)r*Polylighting_static_red; + g=Polymodel_effect.g*(float)g*Polylighting_static_green; + b=Polymodel_effect.b*(float)b*Polylighting_static_blue; + } + else + { + r=(float)r*Polylighting_static_red; + g=(float)g*Polylighting_static_green; + b=(float)b*Polylighting_static_blue; + } + } + + rend_SetFlatColor(GR_RGB(r,g,b)); + } + + + + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_ALPHA)) + rend_SetAlphaValue (texp->alpha * Polymodel_effect.alpha * 255 ); + else + rend_SetAlphaValue (texp->alpha*255); + + if (texp->flags & TF_SATURATE) + rend_SetAlphaType (AT_SATURATE_CONSTANT_VERTEX); + else + { + if ((texp->flags & TF_ALPHA) || (Polymodel_use_effect && (Polymodel_effect.type & PEF_ALPHA))) + rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX); + else + rend_SetAlphaType (ATF_TEXTURE+ATF_VERTEX); + } + } + else + { + rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX); + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_ALPHA)) + rend_SetAlphaValue (Polymodel_effect.alpha * 255 ); + else + rend_SetAlphaValue (255); + + rend_SetLighting(LS_NONE); + rend_SetTextureType (TT_FLAT); + + int r,g,b; + + r=GR_COLOR_RED (fp->color); + g=GR_COLOR_GREEN (fp->color); + b=GR_COLOR_BLUE (fp->color); + + if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD) + { + if (Polymodel_use_effect && Polymodel_effect.type & PEF_COLOR) + { + r=Polymodel_effect.r*(float)r*Polylighting_static_red; + g=Polymodel_effect.g*(float)g*Polylighting_static_green; + b=Polymodel_effect.b*(float)b*Polylighting_static_blue; + } + else + { + r=(float)r*Polylighting_static_red; + g=(float)g*Polylighting_static_green; + b=(float)b*Polylighting_static_blue; + } + } + + rend_SetFlatColor(GR_RGB(r,g,b)); + + bm_handle=0; + } + + if (triface) + g3_SetTriangulationTest(1); + + g3_DrawPoly(fp->nverts,pointlist,bm_handle,MAP_TYPE_BITMAP,&face_cc); + + if (triface) + g3_SetTriangulationTest(0); + + if (texp && (Polymodel_effect.type & PEF_BUMPMAPPED) && texp->bumpmap!=-1 && Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD ) + { + rend_SetBumpmapReadyState(0,0); + } + + #ifdef _DEBUG + if (Polymodel_outline_mode) + DrawSubmodelFaceOutline (fp->nverts,pointlist); + +/* if (Lightmap_debug_model==(pm-Poly_models) && Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP && Lightmap_debug_subnum==modelnum && Lightmap_debug_facenum==facenum) + { + + int lmi_handle=Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].lmi_handle; + lightmap_object_face *lfp=&Polylighting_lightmap_object->lightmap_faces[modelnum][facenum]; + lightmap_info *lmi_ptr=&LightmapInfo[lmi_handle]; + int w=lmi_w (lmi_handle); + int h=lmi_h (lmi_handle); + vector rvec=lfp->rvec*lmi_ptr->xspacing; + vector uvec=lfp->uvec*lmi_ptr->yspacing; + ushort *src_data=(ushort *)lm_data(lmi_ptr->lm_handle); + + for (int i=0;iupper_left-(y*uvec)+(x*rvec); + g3_RotatePoint(&epoints[0],&evec[0]); + pointlist[0] = &epoints[0]; + + evec[1]=lmi_ptr->upper_left-(y*uvec)+((x+1)*rvec); + g3_RotatePoint(&epoints[1],&evec[1]); + pointlist[1] = &epoints[1]; + + evec[2]=lmi_ptr->upper_left-((y+1)*uvec)+((x+1)*rvec); + g3_RotatePoint(&epoints[2],&evec[2]); + pointlist[2] = &epoints[2]; + + evec[3]=lmi_ptr->upper_left-((y+1)*uvec)+(x*rvec); + g3_RotatePoint(&epoints[3],&evec[3]); + pointlist[3] = &epoints[3]; + + if (!(src_data[y*w+x] & OPAQUE_FLAG)) + { + for (t=0;t<4;t++) + g3_DrawLine(GR_RGB(255,0,255),pointlist[t],pointlist[(t+1)%4]); + } + else + { + for (t=0;t<4;t++) + g3_DrawLine(GR_RGB(255,255,255),pointlist[t],pointlist[(t+1)%4]); + } + } + + // Draw red cross where upper left is + ubyte c0; + g3Point p0; + p0.p3_flags=0; + c0 = g3_RotatePoint(&p0,&LightmapInfo[lmi_handle].upper_left); + + if (! c0) + { + + //Draw a little cross at the current vert + g3_ProjectPoint(&p0); //make sure projected + rend_SetFlatColor(GR_RGB(255,0,0)); + rend_DrawLine(p0.p3_sx-CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy-CROSS_WIDTH); + rend_DrawLine(p0.p3_sx,p0.p3_sy-CROSS_WIDTH,p0.p3_sx+CROSS_WIDTH,p0.p3_sy); + rend_DrawLine(p0.p3_sx+CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy+CROSS_WIDTH); + rend_DrawLine(p0.p3_sx,p0.p3_sy+CROSS_WIDTH,p0.p3_sx-CROSS_WIDTH,p0.p3_sy); + } + + }*/ + + + #endif +} + +inline void RenderSubmodelLightmapFace (poly_model *pm,bsp_info *sm,int facenum) +{ + g3Point *pointlist[100]; + + polyface *fp=&sm->faces[facenum]; + int modelnum=sm-pm->submodel; + int t; + + int lm_handle=LightmapInfo[Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].lmi_handle].lm_handle; + float xscalar=(float)GameLightmaps[lm_handle].width/(float)GameLightmaps[lm_handle].square_res; + float yscalar=(float)GameLightmaps[lm_handle].height/(float)GameLightmaps[lm_handle].square_res; + + + ASSERT (fp->nverts<100); + + // Setup the points for this face + for (t=0;tnverts;t++) + { + g3Point *p = &Robot_points[fp->vertnums[t]]; + pointlist[t] = p; + + p->p3_uvl.u = Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].u2[t]*xscalar; + p->p3_uvl.v = Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].v2[t]*yscalar; + p->p3_uvl.l = 1.0; + + p->p3_flags |=PF_UV2+PF_RGBA+PF_L; + } + + if (triangulated_faces[facenum]) + g3_SetTriangulationTest (1); + + g3_DrawPoly(fp->nverts,pointlist,lm_handle,MAP_TYPE_LIGHTMAP); + + if (triangulated_faces[facenum]) + g3_SetTriangulationTest (0); +} + +inline void RenderSubmodelFaceFogged (poly_model *pm,bsp_info *sm,int facenum) +{ + g3Point *pointlist[100]; + polyface *fp=&sm->faces[facenum]; + int modelnum=sm-pm->submodel; + int t; + + for (t=0;tnverts;t++) + { + g3Point *p = &Robot_points[fp->vertnums[t]]; + pointlist[t] = p; + + float mag; + + if (Polymodel_effect.fog_plane_check==1) + { + mag = vm_DotProduct(&Fog_plane,&sm->verts[fp->vertnums[t]])+Fog_distance; + } + else + { + vector *vec=&sm->verts[fp->vertnums[t]]; + + // Now we must generate the split point. This is simply + // an equation in the form Origin + t*Direction + + float dist = (*vec*Polymodel_fog_plane)+ Fog_distance; + + vector subvec=*vec-Fog_view_pos; + + float t = Fog_eye_distance / (Fog_eye_distance - dist); + vector portal_point=Fog_view_pos+(t*subvec); + + float eye_distance=-(vm_DotProduct (&Fog_plane,&portal_point)); + mag = vm_DotProduct(&Fog_plane,vec)+eye_distance; + } + + float scalar=mag/Polymodel_effect.fog_depth; + + if (scalar>1) + scalar=1; + if (scalar<0) + scalar=0; + p->p3_a=scalar; + + p->p3_flags |= PF_RGBA; + } + + if (triangulated_faces[facenum]) + g3_SetTriangulationTest (1); + + g3_DrawPoly(fp->nverts,pointlist,0); + + if (triangulated_faces[facenum]) + g3_SetTriangulationTest (0); + +} + +inline void RenderSubmodelFaceSpecular (poly_model *pm,bsp_info *sm,int facenum) +{ + g3Point *pointlist[100]; + polyface *fp=&sm->faces[facenum]; + int modelnum=sm-pm->submodel; + int t; + bool smooth=0; + + if ((Polymodel_effect.type & PEF_SPECULAR_FACES) && (GameTextures[fp->texnum].flags & TF_SMOOTH_SPECULAR)) + smooth=1; + + for (t=0;tnverts;t++) + { + g3Point *p = &Robot_points[fp->vertnums[t]]; + vector vert=sm->verts[fp->vertnums[t]]; + + vector vertnorm; + + if (smooth) + vertnorm=sm->vertnorms[fp->vertnums[t]]; + else + vertnorm=fp->normal; + + pointlist[t] = p; + + p->p3_flags |= PF_RGBA; + p->p3_a=0.0; + + vector subvec=Specular_view_pos-vert; + vm_NormalizeVectorFast (&subvec); + + vector incident_norm=vert-Polymodel_specular_pos; + vm_NormalizeVectorFast (&incident_norm); + + float d=incident_norm * vertnorm; + vector upvec=d * vertnorm; + incident_norm-=(2*upvec); + + float dotp=subvec * incident_norm; + + if (dotp<0) + continue; + if (dotp>1) + dotp=1; + + if (dotp>0) + { + int index=((float)(MAX_SPECULAR_INCREMENTS-1)*dotp); + float val=Specular_tables[2][index]; + + p->p3_a=val*Polymodel_effect.spec_scalar; + } + } + + if (triangulated_faces[facenum]) + g3_SetTriangulationTest (1); + + g3_DrawPoly(fp->nverts,pointlist,0); + + if (triangulated_faces[facenum]) + g3_SetTriangulationTest (0); +} + + +#define MAX_PARTS 100 + +// Draws a glowing cone of light that represents thrusters +void DrawThrusterEffect (vector *pos,float r,float g,float b,vector *norm,float size,float length) +{ + vector cur_pos=*pos; + float cur_length=0; + vector glow_pos[MAX_PARTS]; + float glow_size[MAX_PARTS]; + int total_parts=0; + + if (length<.1) + return; + + int num_divs=length*3; + if (num_divs>MAX_PARTS) + num_divs=MAX_PARTS; + + float size_change=size/num_divs; + float pos_change=length/num_divs; + + if (!UseHardware) + return; // No software stuff here! + + rend_SetZBufferWriteMask (0); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (.3*255); + + rend_SetLighting (LS_GOURAUD); + rend_SetColorModel (CM_RGB); + + ddgr_color color=GR_RGB(r*255,g*255,b*255); + int bm_handle=Fireballs[GRADIENT_BALL_INDEX].bm_handle; + int t; + + // We must draw the small ones first, but we're starting the iteration from the + // large one. Consequently, we must store our variables so we can draw in reverse order + for (t=0;t.05;t++) + { + glow_pos[t]=cur_pos; + glow_size[t]=size; + + size-=size_change; + cur_pos+=((*norm)*pos_change); + + total_parts++; + } + + for (t=total_parts-1;t>=0;t--) + { + rend_SetZBias (-glow_size[t]); + g3_DrawBitmap (&glow_pos[t],glow_size[t],(glow_size[t]*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color); + } + rend_SetZBias (0.0); + rend_SetZBufferWriteMask (1); +} + +// Draws a glowing cone of light +void DrawGlowEffect (vector *pos,float r,float g,float b,vector *norm,float size) +{ + if (!UseHardware) + return; // No software stuff here! + + if (Polymodel_use_effect && Polymodel_effect.type & PEF_NO_GLOWS) + return; + + rend_SetZBufferWriteMask (0); + rend_SetAlphaType (AT_SATURATE_TEXTURE); + rend_SetAlphaValue (.8*255); + rend_SetLighting (LS_GOURAUD); + rend_SetColorModel (CM_RGB); + + ddgr_color color=GR_RGB(r*255,g*255,b*255); + int bm_handle=Fireballs[GRADIENT_BALL_INDEX].bm_handle; + + rend_SetZBias (-size); + g3_DrawBitmap (pos,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color); + + rend_SetZBias (0.0); + rend_SetZBufferWriteMask (1); +} + +void RenderSubmodelFacesSorted (poly_model *pm,bsp_info *sm) +{ + int i,t; + + int rcount; + int model_render_order[MAX_POLYGON_VECS]; + int modelnum=sm-pm->submodel; + + ASSERT (sm->nvertsnum_faces;i++) + { + polyface *fp = &sm->faces[i]; + + //check for visible face + if (g3_CheckNormalFacing(&sm->verts[fp->vertnums[0]],&fp->normal)) + { + face_depth[i] = 0; + for (t=0;tnverts;t++) + face_depth[i] += Robot_points[fp->vertnums[t]].p3_z; + + face_depth[i] /= fp->nverts; + + //initialize order list + model_render_order[rcount] = i; + + rcount++; + + ASSERT (rcount=0;i--) + { + int facenum=model_render_order[i]; + + RenderSubmodelFace (pm,sm,facenum); + } +} +void RenderSubmodelFacesUnsorted (poly_model *pm,bsp_info *sm) +{ + int i; + int modelnum=sm-pm->submodel; + short alpha_faces[MAX_FACES_PER_ROOM],num_alpha_faces=0; + int rcount=0; + vector view_pos; + + g3_GetViewPosition (&view_pos); + g3_GetUnscaledMatrix (&Unscaled_bumpmap_matrix); + + Specular_view_pos=view_pos; + Bump_view_pos=view_pos; + + if (modelnum<0 || modelnum>=pm->n_models) + { + Error ("Got bad model number %d from polymodel %s!",modelnum,pm->name); + return; + } + + if (sm->flags & SOF_CUSTOM) + rend_SetZBias (-.5); + + for (i=0;inum_faces;i++) + { + vector tempv; + polyface *fp=&sm->faces[i]; + texture *texp; + + // Check to see if this face even faces us! + tempv = view_pos - sm->verts[fp->vertnums[0]]; + if ((tempv * fp->normal)<0) + continue; + + if (fp->texnum!=-1) + { + texp=&GameTextures[pm->textures[fp->texnum]]; + + if (texp->flags & TF_ALPHA || texp->flags & TF_SATURATE) + { + alpha_faces[num_alpha_faces++] = i; + continue; + } + } + + if (StateLimited) + { + State_elements[rcount].facenum=i; + State_elements[rcount].sort_key=pm->textures[fp->texnum]; + rcount++; + } + else + RenderSubmodelFace (pm,sm,i); + } + + if (StateLimited) + { + SortStates (State_elements,rcount); + for (i=rcount-1;i>=0;i--) + { + int facenum=State_elements[i].facenum; + RenderSubmodelFace (pm,sm,facenum); + } + + if (!NoLightmaps) + { + if (!UseMultitexture && Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP) + { + rend_SetAlphaType(AT_LIGHTMAP_BLEND); + rend_SetLighting (LS_GOURAUD); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + rend_SetTextureType(TT_PERSPECTIVE); + rend_SetWrapType (WT_CLAMP); + rend_SetMipState (0); + + for (i=rcount-1;i>=0;i--) + { + int facenum=State_elements[i].facenum; + RenderSubmodelLightmapFace (pm,sm,facenum); + } + + rend_SetWrapType (WT_WRAP); + rend_SetMipState (1); + } + } + } + + // Now render all alpha faces + //rend_SetZBufferWriteMask (0); + for (i=0;iflags & SOF_CUSTOM) + rend_SetZBias (0); + + // Draw specular faces if needed + if (Polymodel_use_effect && Polymodel_effect.type & (PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES)) + { + rend_SetOverlayType (OT_NONE); + rend_SetTextureType (TT_FLAT); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetAlphaType (AT_SATURATE_VERTEX); + rend_SetAlphaValue (255); + rend_SetZBufferWriteMask (0); + + rend_SetFlatColor (GR_RGB((int)(Polymodel_effect.spec_r*255.0),(int)(Polymodel_effect.spec_g*255.0),(int)(Polymodel_effect.spec_b*255.0))); + + for (i=0;inum_faces;i++) + { + polyface *fp=&sm->faces[i]; + + if (!g3_CheckNormalFacing(&sm->verts[fp->vertnums[0]],&fp->normal)) + continue; + + /* vector subvec=sm->verts[fp->vertnums[0]]-Polymodel_specular_pos; + if ((fp->normal * subvec)> 0) + continue;*/ + + if ((Polymodel_effect.type & PEF_SPECULAR_MODEL) || (fp->texnum!=-1 && GameTextures[pm->textures[fp->texnum]].flags & TF_SPECULAR)) + RenderSubmodelFaceSpecular (pm,sm,i); + } + + rend_SetZBufferWriteMask (1); + } + + // Draw fog if need be + if (Polymodel_use_effect && Polymodel_effect.type & PEF_FOGGED_MODEL) + { + matrix mat; + + g3_GetUnscaledMatrix (&mat); + g3_GetViewPosition (&Fog_view_pos); + Fog_plane=mat.fvec; + + if (Polymodel_effect.fog_plane_check==1) + Fog_distance=-(vm_DotProduct (&Fog_plane,&Fog_view_pos)); + else + { + Fog_distance=-(vm_DotProduct (&Polymodel_fog_plane,&Polymodel_fog_portal_vert)); + Fog_eye_distance = (Fog_view_pos*Polymodel_fog_plane)+ Fog_distance; + } + + rend_SetOverlayType (OT_NONE); + rend_SetTextureType (TT_FLAT); + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetAlphaType (AT_VERTEX); + rend_SetAlphaValue (255); + rend_SetZBufferWriteMask (0); + rend_SetCoplanarPolygonOffset(1); + rend_SetFlatColor (GR_RGB((int)(Polymodel_effect.fog_r*255.0),(int)(Polymodel_effect.fog_g*255.0),(int)(Polymodel_effect.fog_b*255.0))); + + for (i=0;inum_faces;i++) + { + polyface *fp=&sm->faces[i]; + + if (!g3_CheckNormalFacing(&sm->verts[fp->vertnums[0]],&fp->normal)) + continue; + + RenderSubmodelFaceFogged (pm,sm,i); + } + + rend_SetCoplanarPolygonOffset(0); + rend_SetZBufferWriteMask (1); + } + +} + + +void BuildModelAngleMatrix( matrix *mat, angle ang,vector *axis); +void StartLightInstance (vector *,matrix *); +void DoneLightInstance(); + +// Rotates all of the points of a submodel, plus supplies color info +void RotateModelPoints (poly_model *pm,bsp_info *sm) +{ + + // Figure out lighting + if (Polymodel_light_type==POLYMODEL_LIGHTING_STATIC) + { + if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER)) + { + for (int i=0;inverts;i++) + { + vector vec=sm->verts[i]; + + float val=((ps_rand()%1000)-500.0)/500.0; + vec*=1.0+(Polymodel_effect.deform_range*val); + + g3_RotatePoint(&Robot_points[i],&vec); + } + } + else + { + for (int i=0;inverts;i++) + g3_RotatePoint(&Robot_points[i],&sm->verts[i]); + } + } + else if (Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP) + { + if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER)) + { + for (int i=0;inverts;i++) + { + vector vec=sm->verts[i]; + float val=((ps_rand()%1000)-500.0)/500.0; + vec*=1.0+(Polymodel_effect.deform_range*val); + + g3_RotatePoint(&Robot_points[i],&vec); + + Robot_points[i].p3_r=1.0; + Robot_points[i].p3_g=1.0; + Robot_points[i].p3_b=1.0; + } + } + else + { + for (int i=0;inverts;i++) + { + g3_RotatePoint(&Robot_points[i],&sm->verts[i]); + Robot_points[i].p3_r=1.0; + Robot_points[i].p3_g=1.0; + Robot_points[i].p3_b=1.0; + } + } + } + else if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD) + { + if (Polymodel_use_effect && Polymodel_effect.type & PEF_COLOR) + { + if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER)) + { + for (int i=0;inverts;i++) + { + vector vec=sm->verts[i]; + float val=((ps_rand()%1000)-500.0)/500.0; + vec*=1.0+(Polymodel_effect.deform_range*val); + + g3_RotatePoint(&Robot_points[i],&vec); + + vector normvec=sm->vertnorms[i]; + val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2; + + Robot_points[i].p3_r=Polymodel_effect.r*val*Polylighting_static_red; + Robot_points[i].p3_g=Polymodel_effect.g*val*Polylighting_static_green; + Robot_points[i].p3_b=Polymodel_effect.b*val*Polylighting_static_blue; + } + } + else + { + if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER)) + { + for (int i=0;inverts;i++) + { + vector vec=sm->verts[i]; + float val=((ps_rand()%1000)-500.0)/500.0; + vec*=1.0+(Polymodel_effect.deform_range*val); + + g3_RotatePoint(&Robot_points[i],&vec); + + vector normvec=sm->vertnorms[i]; + val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2; + + Robot_points[i].p3_r=Polymodel_effect.r*val*Polylighting_static_red; + Robot_points[i].p3_g=Polymodel_effect.g*val*Polylighting_static_green; + Robot_points[i].p3_b=Polymodel_effect.b*val*Polylighting_static_blue; + } + } + else + { + for (int i=0;inverts;i++) + { + g3_RotatePoint(&Robot_points[i],&sm->verts[i]); + vector normvec=sm->vertnorms[i]; + float val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2; + + Robot_points[i].p3_r=Polymodel_effect.r*val*Polylighting_static_red; + Robot_points[i].p3_g=Polymodel_effect.g*val*Polylighting_static_green; + Robot_points[i].p3_b=Polymodel_effect.b*val*Polylighting_static_blue; + } + } + } + } + else + { + if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER)) + { + for (int i=0;inverts;i++) + { + vector vec=sm->verts[i]; + float val=((ps_rand()%1000)-500.0)/500.0; + vec*=1.0+(Polymodel_effect.deform_range*val); + + g3_RotatePoint(&Robot_points[i],&vec); + vector normvec=sm->vertnorms[i]; + val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2; + + Robot_points[i].p3_r=val*Polylighting_static_red; + Robot_points[i].p3_g=val*Polylighting_static_green; + Robot_points[i].p3_b=val*Polylighting_static_blue; + } + } + else + { + for (int i=0;inverts;i++) + { + g3_RotatePoint(&Robot_points[i],&sm->verts[i]); + vector normvec=sm->vertnorms[i]; + float val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2; + + Robot_points[i].p3_r=val*Polylighting_static_red; + Robot_points[i].p3_g=val*Polylighting_static_green; + Robot_points[i].p3_b=val*Polylighting_static_blue; + } + } + + } + } + + #ifndef RELEASE + if (!UseHardware) + { + for (int i=0;inverts;i++) + Robot_points[i].p3_l=Robot_points[i].p3_g; + rend_SetColorModel (CM_MONO); + } + #endif + + +} + +void RenderSubmodel (poly_model *pm,bsp_info *sm, uint f_render_sub) +{ + int i; + matrix lightmatrix; + + // Don't render door housings + if (IsNonRenderableSubmodel (pm,sm-pm->submodel)) + return; + + if (Polymodel_light_type!=POLYMODEL_LIGHTING_LIGHTMAP) + { + // Turn off bumpmapping if not needed + rend_SetBumpmapReadyState(0,0); + } + else + { + if (!StateLimited || UseMultitexture) + rend_SetOverlayType (OT_BLEND); + } + + + if (Multicolor_texture==-1 && Polymodel_use_effect && (Polymodel_effect.type & PEF_CUSTOM_COLOR)) + Multicolor_texture=FindTextureName ("MultiColor"); + + + rend_SetColorModel (CM_RGB); + StartPolyModelPosInstance(&sm->mod_pos); + vector temp_vec=sm->mod_pos+sm->offset; + g3_StartInstanceAngles(&temp_vec,&sm->angs ); + + vm_AnglesToMatrix (&lightmatrix,sm->angs.p,sm->angs.h,sm->angs.b); + StartLightInstance(&temp_vec,&lightmatrix); + + // Check my bit to see if I get drawn + if(f_render_sub & (0x00000001 << (sm - pm->submodel))) + { + if (sm->flags & SOF_CUSTOM) + { + if (!(Polymodel_effect.type & PEF_CUSTOM_TEXTURE)) + goto pop_lighting; + } + + // Check to draw glow faces + if (sm->flags & (SOF_GLOW | SOF_THRUSTER)) + { + if (!FacingPass) + goto pop_lighting; + + vector zero_pos={0,0,0}; + rend_SetOverlayType (OT_NONE); + + if (Polymodel_use_effect && Polymodel_effect.type & PEF_GLOW_SCALAR) + { + if (Polymodel_effect.type & PEF_CUSTOM_GLOW) + DrawThrusterEffect (&zero_pos,Polymodel_effect.glow_r,Polymodel_effect.glow_g,Polymodel_effect.glow_b,&sm->glow_info->normal,sm->glow_info->glow_size*Polymodel_effect.glow_size_scalar,3*Polymodel_effect.glow_length_scalar); + else + DrawThrusterEffect (&zero_pos,sm->glow_info->glow_r,sm->glow_info->glow_g,sm->glow_info->glow_b,&sm->glow_info->normal,sm->glow_info->glow_size*Polymodel_effect.glow_size_scalar,3*Polymodel_effect.glow_length_scalar); + } + else + { + if (Polymodel_use_effect && Polymodel_effect.type & PEF_CUSTOM_GLOW) + DrawGlowEffect (&zero_pos,Polymodel_effect.glow_r,Polymodel_effect.glow_g,Polymodel_effect.glow_b,&sm->glow_info->normal,sm->glow_info->glow_size); + else + DrawGlowEffect (&zero_pos,sm->glow_info->glow_r,sm->glow_info->glow_g,sm->glow_info->glow_b,&sm->glow_info->normal,sm->glow_info->glow_size); + } + + goto pop_lighting; + } + else if (sm->flags & SOF_FACING) + { + if (!FacingPass) + goto pop_lighting; + + vector pos; + rend_SetLighting (LS_NONE); + rend_SetColorModel (CM_MONO); + rend_SetOverlayType (OT_NONE); + + int bm_handle=GetTextureBitmap(pm->textures[sm->faces[0].texnum],0); + rend_SetAlphaValue (GameTextures[pm->textures[sm->faces[0].texnum]].alpha*255); + + vm_MakeZero (&pos); + + if (GameTextures[pm->textures[sm->faces[0].texnum]].flags & TF_SATURATE) + rend_SetAlphaType (AT_SATURATE_TEXTURE); + else + rend_SetAlphaType (ATF_CONSTANT+ATF_TEXTURE); + + rend_SetZBufferWriteMask (0); + g3_DrawBitmap (&pos,sm->rad,(sm->rad*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle); + rend_SetZBufferWriteMask (1); + + goto pop_lighting; + } + else + { + if (FacingPass) + goto pop_lighting; + } + + RotateModelPoints (pm,sm); + + if (!UseHardware) + RenderSubmodelFacesSorted (pm, sm); + else + RenderSubmodelFacesUnsorted (pm, sm); + } + + pop_lighting: + + for (i=0;inum_children;i++) + { + RenderSubmodel(pm,&pm->submodel[sm->children[i]], f_render_sub); + } + + + g3_DoneInstance(); + DonePolyModelPosInstance(); + DoneLightInstance(); +} + +int RenderPolygonModel(poly_model * pm, uint f_render_sub) +{ + ASSERT (pm->new_style==1); + int i=0; + + rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX); + rend_SetWrapType (WT_WRAP); + + FacingPass=0; + for (i=0;in_models;i++) + { + bsp_info *sm=&pm->submodel[i]; + if (sm->parent==-1) + RenderSubmodel (pm,sm, f_render_sub); + } + + // Now render any facing submodels + if (pm->flags & PMF_FACING) + { + // Don't render if we have it set for no glows + FacingPass=1; + rend_SetOverlayType (OT_NONE); + for (i=0;in_models;i++) + { + bsp_info *sm=&pm->submodel[i]; + if (sm->parent==-1) + RenderSubmodel (pm,sm, f_render_sub); + } + } + + FacingPass=0; + + return 1; +} + +float ComputeDefaultSizeFunc(int handle, float *size_ptr, vector *offset_ptr, bool f_use_all_frames) +{ + poly_model *pm; + matrix m; + float normalized_time[MAX_SUBOBJECTS]; + int i, j, n; + float cur_dist; + float size = 0.0; + int start_frame = 0; + int end_frame = 0; + + vector geometric_center = Zero_vector; + + // Chris: Come see me when you are ready to deal with the paging problem - JL + pm = GetPolymodelPointer(handle); + + ASSERT(start_frame <= end_frame); + ASSERT(end_frame <= pm->frame_max); + + if(f_use_all_frames) + { + end_frame = pm->frame_max; + } + + if(offset_ptr) + { + vector min_xyz; + vector max_xyz; + + for(n = start_frame; n <= end_frame; n++) + { + // Because size changes with animation, we need the worst case point -- so, check every keyframe + // NOTE: This code does not currently account for all $turret and $rotate positions + + SetNormalizedTimeAnim(n, normalized_time, pm); + + SetModelAnglesAndPos (pm,normalized_time); + + for (i = 0;i < pm->n_models; i++) + { + bsp_info *sm=&pm->submodel[i]; + + // For every vertex + for(j = 0; j < sm->nverts; j++) + { + vector pnt; + int mn; + + // Get the point and its current sub-object + pnt = sm->verts[j]; + mn = i; + + // Instance up the tree + while (mn != -1) + { + vector tpnt; + + vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b); + vm_TransposeMatrix(&m); + + tpnt = pnt * m; + + pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos; + + mn = pm->submodel[mn].parent; + } + + // Maybe use for other code -- Accounts for world coordinates + // m = obj->orient; + // vm_TransposeMatrix(&m); + // + // pnt = pnt * m; + // + // *gun_point += obj->pos; + + // Find the min_xyz and max_xyz + if(n == start_frame && i == 0 && j == 0) + { + min_xyz = max_xyz = pnt; + } + else + { + if(pnt.x < min_xyz.x) min_xyz.x = pnt.x; + else if(pnt.x > max_xyz.x) max_xyz.x = pnt.x; + + if(pnt.y < min_xyz.y) min_xyz.y = pnt.y; + else if(pnt.y > max_xyz.y) max_xyz.y = pnt.y; + + if(pnt.z < min_xyz.z) min_xyz.z = pnt.z; + else if(pnt.z > max_xyz.z) max_xyz.z = pnt.z; + } + } + } + } + + geometric_center = (max_xyz + min_xyz)/2.0; + *offset_ptr = geometric_center; + } + + for(n = start_frame; n <= end_frame; n++) + { + // Because size changes with animation, we need the worst case point -- so, check every keyframe + // NOTE: This code does not currently account for all $turret and $rotate positions + + SetNormalizedTimeAnim(n, normalized_time, pm); + + SetModelAnglesAndPos (pm,normalized_time); + + for (i = 0;i < pm->n_models; i++) + { + bsp_info *sm=&pm->submodel[i]; + + // For every vertex + for(j = 0; j < sm->nverts; j++) + { + vector pnt; + int mn; + + // Get the point and its current sub-object + pnt = sm->verts[j]; + mn = i; + + // Instance up the tree + while (mn != -1) + { + vector tpnt; + + vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b); + vm_TransposeMatrix(&m); + + tpnt = pnt * m; + + pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos; + + mn = pm->submodel[mn].parent; + } + +// Maybe use for other code -- Accounts for world coordinates +// m = obj->orient; +// vm_TransposeMatrix(&m); +// +// pnt = pnt * m; +// +// *gun_point += obj->pos; + + cur_dist = vm_VectorDistance(&geometric_center, &pnt); + if(cur_dist > size) size = cur_dist; + } + } + } + + // This is a arbitary value. It allows for some turret and rotations to be caught + size = size + 0.01f; + + if(size_ptr) //DAJ + *size_ptr = size; + + return size; +} + +float ComputeDefaultSize(int type, int handle, float *size_ptr) +{ + float size = ComputeDefaultSizeFunc(handle, size_ptr, NULL, true); + + if(type != OBJ_WEAPON && type != OBJ_DEBRIS && type != OBJ_POWERUP) + { + ComputeDefaultSizeFunc(handle, &Poly_models[handle].wall_size, &Poly_models[handle].wall_size_offset, false); + ComputeDefaultSizeFunc(handle, &Poly_models[handle].anim_size, &Poly_models[handle].anim_size_offset, true); + + if (type == OBJ_PLAYER) + { + Poly_models[handle].anim_size *= PLAYER_SIZE_SCALAR; + Poly_models[handle].anim_size_offset = Zero_vector; + } + } + else + { + if(type == OBJ_POWERUP) + { + size *= 2.0f; + *size_ptr *= 2.0f; + } + + Poly_models[handle].wall_size = size; + Poly_models[handle].wall_size_offset = Zero_vector; + + Poly_models[handle].anim_size = size; + Poly_models[handle].anim_size_offset = Zero_vector; + } + + Poly_models[handle].flags |= PMF_SIZE_COMPUTED; + + return size; +} diff --git a/model/polymodel.cpp b/model/polymodel.cpp new file mode 100644 index 000000000..9a6e9e549 --- /dev/null +++ b/model/polymodel.cpp @@ -0,0 +1,3625 @@ +/* + * $Logfile: /DescentIII/Main/model/polymodel.cpp $ + * $Revision: 152 $ + * $Date: 10/08/01 4:20p $ + * $Author: Matt $ + * + * Jason should put something here + * + * $Log: /DescentIII/Main/model/polymodel.cpp $ + * + * 152 10/08/01 4:20p Matt + * Added system to check for errors when reading in add-on data. + * + * 151 10/01/01 1:58p Matt + * In release build, Increment the used count when loading a model that's + * already in memory. This was already done in non-release builds. + * + * 150 5/10/00 2:43p Jeff + * fixed bug when paging in data if the tablefile specifies a full path to + * a primative instead of just a filename (bug in 3rd party tablefile + * editors) + * + * 149 4/19/00 5:34p Matt + * From Duane for 1.4 + * Check for glow_info memory already allocated before mallocing + * + * 148 10/21/99 2:40p Kevin + * Mac merge + * + * 147 10/16/99 6:40p Jeff + * swaped way of determining textures for objects on load + * + * 146 10/14/99 3:02p Jeff + * correctly look up texture names + * + * 145 7/08/99 5:48p Jason + * changes for new bumpmapping system in 1.1 update patch + * + * 144 6/08/99 1:00p Jason + * changes for bumpmapping + * + * 143 5/18/99 10:31a Jason + * polymodel stuff wasn't getting paged in before drawing + * + * 142 5/14/99 2:04p Jason + * added polymodel errors to catch bugs + * + * 141 4/28/99 1:55a Samir + * error instead of assert when we can't open a model for paging in a + * polymodel. + * + * 140 4/19/99 3:50p Jeff + * fixed mem_free/copy-paste bug, wasn't setting the correct pointer to + * NULL after free + * + * 139 4/19/99 4:00a Jeff + * fixed min/max for Linux + * + * 138 4/09/99 12:51p Jason + * fixed bug with new lookup code + * + * 137 4/09/99 12:06p Jason + * made model setup code faster + * + * 136 4/08/99 11:45a Jason + * greatly sped up the time it takes to get model anges/positions by + * precalculation + * + * 135 3/05/99 11:24a Jason + * fixed pesky no-lightmap drawing problem that has plagued me for the + * last 10 months + * + * 134 2/16/99 12:42p Kevin + * Improvements to the paging data progress indicator + * + * 133 2/10/99 4:58p Jeff + * put assert if pageinpolymodel file open fails + * + * 132 2/09/99 7:01p Kevin + * First work for new and improved progress screen while loading a level. + * Note that this is a hack at this point, while I get the details worked + * out, then I'll make it cleaner. + * + * 131 1/15/99 3:04p Jason + * fixed polymodel data freeing problem + * + * 130 1/13/99 12:43p Jason + * added some more detail settings + * + * 129 10/16/98 1:54p Kevin + * Changes for Demo Beta 4 + * + * 128 10/13/98 5:25p Jason + * fixed zero faced submodel problem + * + * 127 10/12/98 10:59a Jason + * fixed zero length malloc + * + * 126 10/12/98 9:35a Jason + * New polymodel memory scheme + * + * 125 10/08/98 4:24p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 124 10/08/98 2:27p Jason + * sped up table file loading + * + * 123 10/07/98 5:29p Chris + * Added SOF_THRUSTER support + * + * 122 10/07/98 3:40p Chris + * Added $thruster + * + * 121 9/11/98 4:04p Jason + * added better multitexture support + * + * 120 8/27/98 11:16a Jason + * fixed math bug in GetPolymodelPointInWorld + * + * 119 8/25/98 12:22p Jason + * more changes for cockpits + * + * 118 8/24/98 6:43p Keneta + * Long proofed the turrets and weapon batteries + * + * 117 8/24/98 2:44p Jason + * checked in so Samir can test + * + * 116 8/21/98 5:14p Jason + * made better memory use of primitives + * + * 115 8/18/98 11:38a Jason + * fixed polymodel fog lighting + * + * 114 8/18/98 11:26a Matt + * Changed outline code to only outline polymodel objects when they're + * being rendered in the mine, and not when they're being drawn in the + * editor, main menu, cockpit, etc. + * + * 113 8/17/98 12:10p Chris + * Fixed MAJOR bug in getting gunpoint positions + * + * 112 8/16/98 9:52p Chris + * Added some error checking for the attach system + * + * 111 8/14/98 4:00p Jason + * added specular objects outside + * + * 110 8/13/98 6:56p Jason + * made objects foggable correctly + * + * 109 8/12/98 12:04p Chris + * Attach system version .5 (still needs more work to be multiplayer + * friendly) + * + * 108 8/07/98 2:49p Keneta + * fixed number of subobjects assertion + * + * 107 8/04/98 2:32p Chris + * Improved attach code added more fixes to the AABB partial computation + * patch + * + * 106 7/31/98 11:52a Chris + * Weapons can be persistent. Added ability for objects to be manually + * set for no object collisions. + * + * 105 7/30/98 1:13p Chris + * Improved attach point code + * + * 104 7/29/98 5:05p Chris + * Made more of the wb info dynamically allocated. Added Attach points to + * the polymodel structure + * + * 103 7/27/98 10:39a Jason + * added customizable skins + * + * 102 7/02/98 2:49p Chris + * + * 101 7/02/98 2:47p Chris + * Dynamic weapon info is now dynamically allocated + * Dynamic weapon info is now dynamically allocated + * + * 100 6/15/98 4:00p Jason + * replaced monochromatic polymodel lighting with rgb lighting + * + * 99 5/27/98 5:17p Jason + * fixed some bugs for the E3 Demo + * + * 98 5/15/98 3:24p Keneta + * + * 97 4/30/98 11:32a Chris + * ClearWB to WBClear + * + * 96 4/24/98 1:35p Jason + * Don't sort non-alphaed polymodels + * + * 95 4/22/98 12:10p Chris + * Fixed path length problems + * + * 94 4/17/98 12:45p Jason + * various changes for multiplayer + * + * 93 4/15/98 3:28p Jason + * changed glow stuff to work with new system + * + * 92 4/07/98 3:35p Jason + * possible fix for polymodel paging bug + * + * 91 4/06/98 3:03p Jason + * added polygon rendering overlays + * + * 90 4/03/98 12:23p Jason + * dealt with overlay types being loaded from disk more than once + * + * 89 4/03/98 11:55a Jason + * fixed polymodel paging problem + * + * 88 4/03/98 10:07a Chris + * Added support for objects getting their size computed when the + * polymodel is paged in the first time as an object + * + * 87 4/02/98 3:54p Jason + * first pass in getting polymodel paging to work + * + * 86 4/01/98 6:23p Jason + * added a slew of stuff for multiplayer + * + * 85 3/31/98 3:49p Jason + * added memory lib + * + * 84 3/31/98 11:12a Brent + * added assert for max textures + * + * 83 3/30/98 12:27a Jason + * fixed memory leaks as reported by BoundsChecker + * + * 82 3/25/98 5:51p Chris + * Added full model/body animations for weapon firing + * + * 81 3/24/98 2:12p Brent + * fixed another glowcount bug + * + * 80 3/24/98 12:47p Brent + * fixed glow bug + * + * 79 3/24/98 12:38p Brent + * added assert + * + * 78 3/23/98 10:03a Chris + * Added independant wb animations + * + * 77 3/19/98 4:30p Samir + * added ability to mark subobjects as layered. + * + * 76 3/18/98 6:24p Samir + * Fixed some bugs with normalizing MONITORs and added VIEWER flag. + * + * 75 3/16/98 11:34a Chris + * Polymodels have 3 levels of AABB (object, subobject, and face level) + * + * 74 3/10/98 2:03p Chris + * Added support for 0.0 angle fov + * + * 73 2/16/98 4:05p Jason + * render polymodels sorted or unsorted depending on whether or not they + * have alpha faces + * + * 72 2/16/98 2:49p Chris + * Made the MAX_SUBOBJECTS determine the number of normalized_time values + * to be processed. No longer a 'literal' problem. + * + * 71 2/16/98 2:25p Jason + * fixed bad indexing into subobjects problem + * + * 70 2/16/98 2:12p Jason + * fixed bug with lightmaps and door shells + * + * 69 2/13/98 6:59p Chris + * FIxed the normalized_time array from being smaller than the number of + * subobjects. Also update newstyle_fi.cpp + * + * 68 2/13/98 12:44p Jason + * upped max number of subobjects + * + * 67 2/10/98 3:50p Jason + * added pulsing walls + * + * 66 2/06/98 8:08p Jason + * fixed size problem with facing subobjects + * + * 65 2/06/98 6:44p Jason + * made polymodels replace themselves if they find the same name + * + * 64 2/06/98 4:33p Jason + * fixed facing model sizes + * + * 63 2/06/98 1:45p Jason + * No really, I fixed the min max problem. + * + * 62 2/06/98 12:53p Jason + * fixed object min max stuff + * + * 61 2/04/98 9:28p Jason + * added the ability to have models that always face you + * + * 60 1/26/98 4:32p Jason + * took out some goofy mprintfs + * + * 59 1/23/98 5:39p Matt + * Removed some door frontface error checking now more thoroughly handled + * in WorldObjectsDoorDialog.cpp + * + * 58 1/23/98 11:16a Luke + * Remove a really old Assert that limitted which types could animate + * + * 57 1/13/98 3:46p Jason + * fixed memory corruption bug introduced by my last rev + * + * 56 1/13/98 3:09p Jason + * added glow effect for engines + * + * 55 1/02/98 1:03p Jason + * fixed memory deallocation leak in FreePolymodel + * + * 54 12/31/97 3:34p Jason + * added alpha per vertex for polymodels + * + * 53 12/08/97 6:18p Jason + * more tweaks for destroyable buildings + * + * 52 11/17/97 6:28p Mark + * FROM Jason:fixed memory leak + * + * 51 11/17/97 12:02p Jason + * added extra newstyle variable set + * + * 50 11/10/97 3:33p Jason + * ignore whitespace + * + * 49 11/04/97 4:58p Samir + * Added 4 more monitors. + * + * 48 10/30/97 10:53a Jason + * set color model to be MONO when drawing gouraud shaded polymodels + * + * 47 10/28/97 6:37p Samir + * Submodels now can be monitors. + * + * 46 10/28/97 11:10a Jason + * tweaked subojbect rotation + * + * 45 10/24/97 11:57a Jason + * fixed memory leak + * + * 44 10/23/97 2:07p Jason + * fixed some problems with debris not spinning about their center point + * + * 43 10/20/97 4:46p Jason + * changes for explosions + * + * 42 10/06/97 6:39p Jason + * fixed frame min/max problem + * + * 41 10/05/97 5:30a Chris + * Added more support for TIMED animations + * + * 40 10/01/97 7:00p Jason + * did more work on object lightmaps + * + * 39 9/30/97 6:40p Jason + * got lightmap stuff sort of working with objects + * + * 38 9/29/97 3:48p Jason + * added more code to support new timed models + * + * 37 9/26/97 12:09p Jason + * made positional/rotational animations have differing + * track_min/track_max + * + * 36 9/25/97 4:54p Jason + * added timer info to polymodels + * + * 35 9/18/97 1:27p Matt + * Cleaned up object struct + * + * 34 9/17/97 10:59a Chris + * Added a new way to compute radi + * + * 33 9/15/97 11:11a Jason + * fixed mins/maxs to be based on the center of the model + * + * 32 9/12/97 5:38p Jason + * got doors working + * + * 31 9/11/97 5:38p Jason + * initial door coding for room engine + * + * 30 9/10/97 6:01p Jason + * fixed double used variable bug + * + * 29 9/10/97 5:17p Jason + * more lighting improvements + * + * 28 9/09/97 6:55p Jason + * better error checking for model usage counts + * + * 27 9/09/97 6:15p Jason + * made dynamic lighting on objects more memory efficient + * + * 26 9/09/97 11:05a Jason + * fixed wacky bizarre problem with instancing and positional + * interpolation + * + * 25 9/05/97 1:29p Jason + * revamped generic object lighting + * + * 24 9/04/97 5:38p Jason + * fixed dumb bug that was fixed a long time ago...model positional + * interpolation + * + * 23 9/04/97 3:49p Chris + * Added additional turret information from pofgen, added ground plane + * stuff + * + * 22 9/03/97 4:41p Chris + * Moved some code so that pofview will compile + * + * 21 9/03/97 2:12p Chris + * Added new weapon battery system and made the animation system usable. + * + * 20 8/25/97 6:19p Chris + * Added support for knowing which subobject a gun point is linked to + * + * 19 8/24/97 1:40p Jason + * don't compute normals on model load...let pofgen do it + * + * 18 8/22/97 12:13p Jason + * made rotators work on keyframe 1 instead of keyframe 0 since max + * insists on some weird "start axis" for keyframe 0 + * + * 17 8/20/97 4:24p Matt + * Removed unused code + * + * 16 8/20/97 3:16p Chris + * Added some hooks for turrets + * + * 15 8/08/97 4:38p Jason + * added sliding textures for models + * + * 14 8/08/97 2:30p Jason + * added error checking for rotators without keyframes + * + * 13 8/08/97 10:58a Jason + * fixed out of bounds error + * + * 12 8/07/97 3:19p Jason + * only remap models when explicitely told to + * + * 11 8/06/97 3:07p Jason + * fixed problem where polymodels were being freed twice + * + * 10 8/06/97 12:40p Jason + * fixed some potentially serious memory problems + * + * 9 8/05/97 5:18p Jason + * fixed circular dependencies with child/parents + * + * 8 8/03/97 2:25p Jason + * made polymodels use less memory + * + * 7 7/28/97 1:14p Chris + * Added support for sub-object visability. Plus, debris. + * + * 6 7/23/97 11:48a Jason + * added support for newstyle pof format + * + * 5 7/22/97 1:34a Jason + * added code to support newstyle polymodels + * + * 4 7/15/97 4:27p Mark + * + * 36 6/24/97 6:13p Matt + * Got rid of DrawPolygonObject(), since it required the inclusion of + * object.h, which is a main directory header file (& thus shouldn't be + * accessible by a library). + * + * 34 5/20/97 7:21p Jason + * fixed stupid off-by-one bug + * + * 33 5/20/97 5:52p Jason + * tweaked a couple of things with magnitude division + * + * 32 5/19/97 5:10p Jason + * changes for our new abstracted renderer code + * + * 31 5/19/97 11:46a Jason + * normalize rotation vectors + * + * 30 5/16/97 3:13p Jason + * fixed more problems with model rotation + * + * 29 5/16/97 1:24p Jason + * changed the way models work their angle magic + * Now its much more intuitive + * + * 28 5/16/97 12:04p Jason + * better memory use for rotational keyframes + * + * 27 5/15/97 4:23p Jason + * swap interpreted model data for mac + * + * 26 5/14/97 11:57p Jason + * made polymodels use far less memory + * + * 25 5/14/97 7:45p Jason + * fixed polymodel bug where it would not get deallocated correctly + * + * + * 24 5/13/97 5:59p Jason + * fixed yet another local transform bug + * + * 23 5/13/97 12:43p Jason + * fixed a delta rotation problem, plus add interpolated rotational axis + * + * 22 5/08/97 5:11p Jason + * fixed bug with animation rotation keyframes + * + * 21 5/08/97 1:16p Jason + * made ChangeEndName work with device independant calls + * + * 20 4/30/97 5:43p Jason + * remap polymodels when pagefile is done loading + * + * 19 4/28/97 6:46p Jason + * made ships have multiple gun points + * + * 18 4/21/97 5:29p Jason + * got animating textures to work on polygonal objects + * + * 17 4/21/97 4:09p Jason + * added GetNormalizedKeyframe function + * + * 16 4/04/97 11:48a Jason + * render polygon objects as linear maps + * + * 15 4/02/97 5:21p Jason + * added the ability for pages to free the models that they point to + * + * 14 3/26/97 3:29p Jason + * made robots work/render + * + * 13 3/18/97 5:22p Jason + * took out dumb mprintfs + * + * 12 3/14/97 5:54p Jason + * made positional interpolation work with 3d doors + * + * 11 3/14/97 10:55a Jason + * made polymodels use delta angles instead of absolute angles + * + * 10 3/13/97 6:13p Jason + * got poly doors working + * + * 9 3/07/97 3:59p Samir + * Took out include to memory.h for portability. + * + * 8 3/05/97 3:10p Jason + * added FreeAllModels call + * + * 7 3/05/97 12:17p Jason + * took out autoloading of the ship.pof upon startup + * + * 6 3/04/97 11:52a Jason + * made polymodels remap correctly + * + * 5 3/03/97 6:21p Matt + * Changed cfile functions to use D3 naming convention + * + * $NoKeywords: $ + */ + +#include "objinfo.h" +#include "polymodel.h" +#include "pserror.h" +#include "3d.h" +#include "mono.h" +#include "bitmap.h" +#include "renderer.h" +#include "manage.h" +#include "gametexture.h" +#include "lighting.h" +#include "ddio.h" +#include "game.h" +#include +#include +#include "robotfire.h" +#include "mem.h" + +#ifdef __LINUX__ +#define max(a,b) ((a>b)?a:b) +#endif + +int Num_poly_models=0; +poly_model Poly_models[MAX_POLY_MODELS]; + +g3Point Robot_points[MAX_POLYGON_VECS]; + +vector Interp_pos_instance_vec={0,0,0}; +vector Instance_vec_stack[MAX_SUBOBJECTS]; +int Instance_vec_cnt=0; + +#ifdef _DEBUG +//Flag to draw an outline around the faces +bool Polymodel_outline_mode = 0; +#endif + +#define ID_OHDR 'RDHO' // POF file header +#define ID_SOBJ 'JBOS' // Subobject header +#define ID_IDTA 'ATDI' // Interpreter data +#define ID_TXTR 'RTXT' // Texture filename list +#define ID_INFO 'FNIP' // POF file information, like command line, etc +#define ID_GRID 'DIRG' // Grid information +#define ID_GPNT 'TNPG' // gun points +#define ID_ROT_ANIM 'INAR' // angular animation data +#define ID_POS_ANIM 'INAP' // positional animation data +#define ID_ANIM 'MINA' // angular information +#define ID_WBS 'TABW' // Weapon Battery Info +#define ID_GROUND 'DNRG' // Ground Plane info +#define ID_ATTACH 'HCTA' // Attach points +#define ID_ATTACH_NORMALS 'HTAN' // Attach uvecs + +int Polymodel_use_effect=0; +polymodel_effect Polymodel_effect; +polymodel_light_type Polymodel_light_type=POLYMODEL_LIGHTING_STATIC; +float Polylighting_static_red; +float Polylighting_static_green; +float Polylighting_static_blue; +ubyte *Polylighting_gouraud; +lightmap_object *Polylighting_lightmap_object; + +vector *Polymodel_light_direction,Polymodel_fog_plane,Polymodel_specular_pos,Polymodel_fog_portal_vert,Polymodel_bump_pos; + +int findtextbmpname = 0; +int findtextname = 0; + +void WBClearInfo(poly_model *pm) +{ + pm->num_wbs = 0; +} + +inline void RecursiveAssignWB(poly_model *pm, int sm_index, int wb_index) +{ + int flags; + int i; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + flags = wb_index << WB_INDEX_SHIFT; + + pm->submodel[sm_index].flags |= flags | SOF_WB; + + for(i = 0; i < pm->submodel[sm_index].num_children; i++) + { + RecursiveAssignWB(pm, pm->submodel[sm_index].children[i], wb_index); + } +} + +void FindWBSubobjects(poly_model *pm) +{ + int i; + bool found; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + for(i = 0; i < pm->n_models; i++) + { + if(pm->submodel[i].flags & SOF_TURRET) + { + int j; + found = false; + + for(j = 0; j < pm->num_wbs; j++) + { + int k; + + for(k = 0; k < pm->poly_wb[j].num_turrets; k++) + { + if(pm->poly_wb[j].turret_index[k] == i) + { + found = true; + RecursiveAssignWB(pm, i, j); + } + + if(found) break; + } + + if(found) break; + } + } + } +} + +// Sets aside a polymodel for use +// Errors and returns -1 if none free +int AllocPolyModel () +{ + for (int i=0;i0); + + Poly_models[i].used--; + + if (Poly_models[i].used) + return; + + FreePolymodelData (i); + + Poly_models[i].used=0; + Poly_models[i].flags|=PMF_NOT_RESIDENT; +} + +void ReadModelVector (vector *vec,CFILE *infile) +{ + vec->x=cf_ReadFloat(infile); + vec->y=cf_ReadFloat(infile); + vec->z=cf_ReadFloat(infile); +} + +void ReadModelStringLen (char *ptr,int len,CFILE *infile) +{ + int i; + + int mlen=cf_ReadInt(infile); + for (i=0;in_textures) + { + Int3(); // Get Jason, new model doesn't match old model!!! + cfclose (infile); + return 0; + } + + for (i=0; itextures[i]=ret; + if (GameTextures[ret].alpha<.99) + pm->flags|=PMF_ALPHA; + } + + done=1; + break; + } + + default: + cfseek(infile,len,SEEK_CUR); + break; + + } + } + + cfclose (infile); + return 1; +} + +void BuildModelAngleMatrix( matrix *mat, angle ang,vector *axis) +{ + float x,y,z; + float s,c,t; + + x = axis->x; + y = axis->y; + z = axis->z; + + s = (float)FixSin(ang); + c = (float)FixCos(ang); + t = 1.0f - c; + + mat->rvec.x = t*x*x + c; + mat->rvec.y = t*x*y + s*z; + mat->rvec.z = t*x*z - s*y; + + mat->uvec.x = t*x*y - s*z; + mat->uvec.y = t*y*y + c; + mat->uvec.z = t*y*z + s*x; + + mat->fvec.x = t*x*z + s*y; + mat->fvec.y = t*y*z - s*x; + mat->fvec.z = t*z*z + c; +} + +void SetPolymodelProperties (bsp_info *subobj,char *props) +{ + // first, extract the command + + int len,i; + char command[200],data[200]; + + len=strlen(props); + + if (len<3) + return; // nothing to set! + + for (i=0;iflags|=SOF_JITTER; + + return; + } + + if (!strnicmp (command,"$shell",6)) + { + // this subobject is a door shell + + subobj->flags|=SOF_SHELL; + + return; + } + if (!strnicmp (command,"$facing",7)) + { + // this subobject always faces you + subobj->flags|=SOF_FACING; + return; + } + + if (!strnicmp (command,"$frontface",10)) + { + // this subobject is a door front + + subobj->flags|=SOF_FRONTFACE; + + return; + } + + if (!stricmp (command,"$glow=")) + { + float r,g,b; + float size; + int num_found; + + ASSERT (!(subobj->flags & (SOF_GLOW | SOF_THRUSTER))); + + num_found = sscanf(data, " %f, %f, %f, %f", &r,&g,&b,&size); + + ASSERT(num_found == 4); + + subobj->flags|=SOF_GLOW; + + if(subobj->glow_info == NULL) //DAJ may already exist + subobj->glow_info=(glowinfo *)mem_malloc (sizeof(glowinfo)); + + subobj->glow_info->glow_r=r; + subobj->glow_info->glow_g=g; + subobj->glow_info->glow_b=b; + subobj->glow_info->glow_size=size; + + return; + } + + if (!stricmp (command,"$thruster=")) + { + float r,g,b; + float size; + int num_found; + + ASSERT (!(subobj->flags & (SOF_GLOW | SOF_THRUSTER))); + + num_found = sscanf(data, " %f, %f, %f, %f", &r,&g,&b,&size); + + ASSERT(num_found == 4); + + subobj->flags |= SOF_THRUSTER; + + if(subobj->glow_info == NULL) //DAJ may already exist + subobj->glow_info=(glowinfo *)mem_malloc (sizeof(glowinfo)); + + subobj->glow_info->glow_r=r; + subobj->glow_info->glow_g=g; + subobj->glow_info->glow_b=b; + subobj->glow_info->glow_size=size; + + return; + } + + if (!stricmp (command,"$fov=")) + { + float fov_angle; + float turret_spr; + float reaction_time; + int num_found; + + num_found = sscanf(data, " %f, %f, %f", &fov_angle, &turret_spr, &reaction_time); + + ASSERT(num_found == 3); + + if (fov_angle < 0.0f || fov_angle > 360.0f) + { + // Bad data + ASSERT(0); + fov_angle = 1.0; + } + + // .4 is really fast and really arbitrary + if (turret_spr < 0.0f || turret_spr > 60.0f) + { + // Bad data + ASSERT(0); + turret_spr = 1.0f; + } + + // 10 seconds is really slow and really arbitrary + if (reaction_time < 0.0f || reaction_time > 10.0f) + { + // Bad data + ASSERT(0); + reaction_time = 10.0; + } + + subobj->flags|=SOF_TURRET; + subobj->fov = fov_angle/720.0f; // 720 = 360 * 2 and we want to make fov the amount we can move in either direction + // it has a minimum value of (0.0) to [0.5] + subobj->rps = 1.0/turret_spr; // convert spr to rps (rotations per second) + subobj->think_interval = reaction_time; + + return; + } + + if (!stricmp (command,"$monitor01")) + { + // this subobject is a monitor + subobj->flags|=SOF_MONITOR1; + return; + } + + if (!stricmp (command,"$monitor02")) + { + // this subobject is a 2nd monitor + subobj->flags|=SOF_MONITOR2; + return; + } + + if (!stricmp (command,"$monitor03")) + { + // this subobject is a 3rd monitor + subobj->flags|=SOF_MONITOR3; + return; + } + + if (!stricmp (command,"$monitor04")) + { + // this subobject is a 4th monitor + subobj->flags|=SOF_MONITOR4; + return; + } + + if (!stricmp (command,"$monitor05")) + { + // this subobject is a 4th monitor + subobj->flags|=SOF_MONITOR5; + return; + } + + if (!stricmp (command,"$monitor06")) + { + // this subobject is a 4th monitor + subobj->flags|=SOF_MONITOR6; + return; + } + + if (!stricmp (command,"$monitor07")) + { + // this subobject is a 4th monitor + subobj->flags|=SOF_MONITOR7; + return; + } + + if (!stricmp (command,"$monitor08")) + { + // this subobject is a 4th monitor + subobj->flags|=SOF_MONITOR8; + return; + } + + if (!stricmp (command,"$viewer")) + { + // this subobject is a viewer + subobj->flags|=SOF_VIEWER; + return; + } + + if (!stricmp (command,"$layer")) + { + // this subobject is a layer to be drawn after original object. + subobj->flags|=SOF_LAYER; + return; + } + if (!stricmp (command,"$custom")) + { + // this subobject has custom textures/colors + subobj->flags|=SOF_CUSTOM; + + return; + } + +} + + +void MinMaxSubmodel (poly_model *pm,bsp_info *sm,vector offset) +{ + + + offset+=sm->offset; + // Get max + if ((sm->max.x+offset.x)>pm->maxs.x) + pm->maxs.x=sm->max.x+offset.x; + if ((sm->max.y+offset.y)>pm->maxs.y) + pm->maxs.y=sm->max.y+offset.y; + if ((sm->max.z+offset.z)>pm->maxs.z) + pm->maxs.z=sm->max.z+offset.z; + + // Get min + if ((sm->min.x+offset.x)mins.x) + pm->mins.x=sm->min.x+offset.x; + if ((sm->min.y+offset.y)mins.y) + pm->mins.y=sm->min.y+offset.y; + if ((sm->min.z+offset.z)mins.z) + pm->mins.z=sm->min.z+offset.z; + + for (int i=0;inum_children;i++) + MinMaxSubmodel(pm,&pm->submodel[sm->children[i]],offset); +} + +void FindMinMaxForModel (poly_model *pm) +{ + + vector zero_vec; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + vm_MakeZero (&zero_vec); + pm->mins.x=pm->mins.y=pm->mins.z=90000; + pm->maxs.x=pm->maxs.y=pm->maxs.z=-90000; + + for (int i=0;in_models;i++) + { + bsp_info *sm=&pm->submodel[i]; + if (sm->parent==-1) + MinMaxSubmodel (pm,sm,zero_vec); + } + + +} + +int ReadNewModelFile (int polynum,CFILE *infile) +{ + int version,done=0,i,t,version_major; + int id, len; + poly_model *pm=&Poly_models[polynum]; + int timed=0; + + ASSERT (pm->new_style); + + id = cf_ReadInt(infile); + + if (id!='OPSP') + Error("Bad ID in model file!"); + + // Version is major*100+minor + // So, major = version / 100; + // minor = version % 100; + + version = cf_ReadInt(infile); + if ( version < 18 ) + { + mprintf((0, "Old POF Version of %d fixed up to %d\n", version, version*100 )); + version*=100; + } + + if (version < PM_COMPATIBLE_VERSION || version > PM_OBJFILE_VERSION) + { + mprintf ((0,"Bad version (%d) in model file!\n",version)); + Int3(); + return 0; + } + + pm->version = version; + version_major=version/100; + + if (version_major>=21) + pm->flags|=PMF_LIGHTMAP_RES; + if (version_major>=22) + { + timed=1; + pm->flags |=PMF_TIMED; + pm->frame_min=0; + pm->frame_max=0; + } + + pm->n_attach = 0; + + memset( &pm->view_pos, 0, sizeof(pm->view_pos) ); + + while (!done) + { + if (cfeof(infile)) + { + done=1; + continue; + } + + id = cf_ReadInt(infile); + len = cf_ReadInt(infile); + + switch (id) + { + case ID_OHDR: + { + //Object header + + //mprintf ((0,"Object header...\n")); + + pm->n_models = cf_ReadInt(infile); + pm->rad = cf_ReadFloat(infile); + + pm->submodel = (bsp_info *)mem_malloc( sizeof(bsp_info)*pm->n_models ); + ASSERT(pm->submodel != NULL ); + memset( pm->submodel, 0, sizeof(bsp_info)*pm->n_models ); + + ReadModelVector(&pm->mins,infile); + ReadModelVector(&pm->maxs,infile); + + //pm->n_detail_levels = cf_ReadInt(infile); + int det=cf_ReadInt(infile); // skip detail + + for (i=0; idetail[i] = cf_ReadInt(infile); + //pm->detail_depth[i] = 0.0f; + cf_ReadInt(infile); + } + + // Adjust min/maxs to be based on zero + /*vector temp_vec=pm->maxs-pm->mins; + + temp_vec/=2; + + pm->mins=-temp_vec; + pm->maxs=temp_vec;*/ + + + break; + } + + case ID_SOBJ: + { + //Subobject header + int n; + char props[MAX_PROP_LEN]; + float d; + + //mprintf((0,"Got chunk SOBJ, len=%d\n",len)); + + n = cf_ReadInt(infile); + + ASSERT(n < pm->n_models ); + + pm->submodel[n].min.x=pm->submodel[n].min.y=pm->submodel[n].min.z=90000; + pm->submodel[n].max.x=pm->submodel[n].max.y=pm->submodel[n].max.z=-90000; + + pm->submodel[n].parent = cf_ReadInt(infile); + + ReadModelVector(&pm->submodel[n].norm,infile); + + + d = cf_ReadFloat(infile); + ReadModelVector(&pm->submodel[n].pnt,infile); + ReadModelVector(&pm->submodel[n].offset,infile); + + pm->submodel[n].rad = cf_ReadFloat(infile); //radius + + pm->submodel[n].tree_offset = cf_ReadInt(infile); //offset + pm->submodel[n].data_offset = cf_ReadInt(infile); //offset + + if ( pm->version > 1805 ) + ReadModelVector(&pm->submodel[n].geometric_center,infile); + else + { + // for old models, hack in 0,0,0, which says the geometric center is + // equal to pivot point, which is not always true. + vm_MakeZero( &pm->submodel[n].geometric_center ); + } + + pm->submodel[n].name[0] = '\0'; + + ReadModelStringLen (pm->submodel[n].name,PAGENAME_LEN,infile); + ReadModelStringLen(props, MAX_PROP_LEN,infile); // and the user properites + + SetPolymodelProperties (&pm->submodel[n],props); + + pm->submodel[n].movement_type = cf_ReadInt(infile); + pm->submodel[n].movement_axis = cf_ReadInt(infile); + + if ( pm->submodel[n].name[0] == '\0' ) + strcpy(pm->submodel[n].name, "unknown object name"); + + memset (&pm->submodel[n].angs,0,sizeof(angvec)); + + // Skip freespace chunks + int n_chunks = cf_ReadInt( infile ); + if (n_chunks > 0 ) + { + for (i=0; isubmodel[n].verts=(vector *)mem_malloc (nverts*sizeof(vector)); + pm->submodel[n].vertnorms=(vector *)mem_malloc (nverts*sizeof(vector)); + pm->submodel[n].alpha=(float *)mem_malloc(nverts*sizeof(float)); + ASSERT (pm->submodel[n].verts); + ASSERT (pm->submodel[n].vertnorms); + ASSERT (pm->submodel[n].alpha); + } + else + { + //Let me take a moment right here to say how annoying it is that I can't + //set all these pointers to NULL with one assignment on the same line due + //to the stupid strong typying they've added to C. + pm->submodel[n].verts = NULL; + pm->submodel[n].vertnorms = NULL; + pm->submodel[n].alpha = NULL; + } + + pm->submodel[n].nverts=nverts; + + for (i=0;isubmodel[n].verts[i],infile); + + // Get max + if (pm->submodel[n].verts[i].x>pm->submodel[n].max.x) + pm->submodel[n].max.x=pm->submodel[n].verts[i].x; + if (pm->submodel[n].verts[i].y>pm->submodel[n].max.y) + pm->submodel[n].max.y=pm->submodel[n].verts[i].y; + if (pm->submodel[n].verts[i].z>pm->submodel[n].max.z) + pm->submodel[n].max.z=pm->submodel[n].verts[i].z; + + // Get min + if (pm->submodel[n].verts[i].xsubmodel[n].min.x) + pm->submodel[n].min.x=pm->submodel[n].verts[i].x; + if (pm->submodel[n].verts[i].ysubmodel[n].min.y) + pm->submodel[n].min.y=pm->submodel[n].verts[i].y; + if (pm->submodel[n].verts[i].zsubmodel[n].min.z) + pm->submodel[n].min.z=pm->submodel[n].verts[i].z; + + } + for (i=0;isubmodel[n].vertnorms[i],infile); + + // Read alpha per vertex + if (version_major>=23) + { + for (i=0;isubmodel[n].alpha[i]=cf_ReadFloat(infile); + if (pm->submodel[n].alpha[i]<.99) + pm->flags |=PMF_ALPHA; + } + } + else + { + for (i=0;isubmodel[n].alpha[i]=1.0; + } + + int nfaces=cf_ReadInt (infile); + pm->submodel[n].num_faces=nfaces; + + if (nfaces) + { + pm->submodel[n].faces=(polyface *)mem_malloc (nfaces*sizeof(polyface)); + pm->submodel[n].face_min =(vector *)mem_malloc (nfaces*sizeof(vector)); + pm->submodel[n].face_max =(vector *)mem_malloc (nfaces*sizeof(vector)); + ASSERT (pm->submodel[n].faces); + } + else + { + pm->submodel[n].faces=NULL; + pm->submodel[n].face_max=NULL; + pm->submodel[n].face_min=NULL; + } + + // Find out how much space we'll need + bsp_info *sm=&pm->submodel[n]; + + vector tvec; + int current_count=0; + + int save_position=cftell (infile); + int *start_index; + + if (nfaces) + { + start_index=(int *)mem_malloc (sizeof(int)*nfaces); + ASSERT (start_index); + } + else + start_index=NULL; + + ubyte tempbuf[2000]; + + + for (i=0;i0); + start_index[i]=current_count; + current_count+=num_verts; + + // Skip the rest + if (cf_ReadInt(infile)) + cf_ReadInt(infile); + else + cf_ReadBytes (tempbuf,3,infile); + + cf_ReadBytes (tempbuf,12*num_verts,infile); + + if (version_major>=21) + cf_ReadBytes (tempbuf,8,infile); + + + } + + // Allocate our space + if (current_count) + { + sm->vertnum_memory=(short *)mem_malloc (current_count*sizeof(short)); + ASSERT (sm->vertnum_memory); + + sm->u_memory=(float *)mem_malloc (current_count*sizeof(float)); + ASSERT (sm->u_memory); + + sm->v_memory=(float *)mem_malloc (current_count*sizeof(float)); + ASSERT (sm->v_memory); + } + else + { + sm->vertnum_memory=NULL; + sm->u_memory=NULL; + sm->v_memory=NULL; + } + + // Now go through and set up our fake pointers + for (i=0;ifaces[i].vertnums=&sm->vertnum_memory[start_index[i]]; + sm->faces[i].u=&sm->u_memory[start_index[i]]; + sm->faces[i].v=&sm->v_memory[start_index[i]]; + } + + // Reset our file pointer and free the temp memory + cfseek (infile,save_position,SEEK_SET); + + if (start_index) + mem_free (start_index); + + for (i=0;isubmodel[n].faces[i].normal,infile); + + int num_verts=cf_ReadInt(infile); + pm->submodel[n].faces[i].nverts=num_verts; + + int textured=cf_ReadInt (infile); + if (textured) + { + pm->submodel[n].faces[i].texnum=cf_ReadInt(infile); + + } + else + { + pm->submodel[n].faces[i].texnum=-1; + ubyte r,g,b; + r=cf_ReadByte (infile); + g=cf_ReadByte (infile); + b=cf_ReadByte (infile); + pm->submodel[n].faces[i].color=GR_RGB(r,g,b); + } + + for (t=0;tsubmodel[n].faces[i].nverts;t++) + { + int val; + vector *min_ptr = &pm->submodel[n].face_min[i]; + vector *max_ptr = &pm->submodel[n].face_max[i]; + vector *v_ptr; + + val=cf_ReadInt (infile); + pm->submodel[n].faces[i].vertnums[t]=val; + pm->submodel[n].faces[i].u[t]=cf_ReadFloat (infile); + pm->submodel[n].faces[i].v[t]=cf_ReadFloat (infile); + + v_ptr = &pm->submodel[n].verts[pm->submodel[n].faces[i].vertnums[t]]; + + if(t == 0) + { + *min_ptr = *max_ptr = *v_ptr; + } + else + { + if(v_ptr->x < min_ptr->x) + min_ptr->x = v_ptr->x; + else if(v_ptr->x > max_ptr->x) + max_ptr->x = v_ptr->x; + + if(v_ptr->y < min_ptr->y) + min_ptr->y = v_ptr->y; + else if(v_ptr->y > max_ptr->y) + max_ptr->y = v_ptr->y; + + if(v_ptr->z < min_ptr->z) + min_ptr->z = v_ptr->z; + else if(v_ptr->z > max_ptr->z) + max_ptr->z = v_ptr->z; + } + } + + // Do lightmap res computation + if (version_major>=21) + { + float xdiff,ydiff; + + xdiff=cf_ReadFloat (infile); + ydiff=cf_ReadFloat (infile); + } + } + + break; + + } + + + case ID_GPNT: + pm->n_guns = cf_ReadInt(infile); + pm->gun_slots = (w_bank *)mem_malloc(sizeof(w_bank) * pm->n_guns); + ASSERT( pm->gun_slots != NULL ); + + for (i = 0; i < pm->n_guns; i++ ) + { + w_bank *bank = &pm->gun_slots[i]; + + // In Version 19.08 and beyond, gunpoints are associated to their parent object. + if(version >= 19*100+8) + { + bank->parent = cf_ReadInt( infile ); + } + else + { + bank->parent = 0; + } + + ReadModelVector( &bank->pnt, infile ); + ReadModelVector( &bank->norm, infile ); + } + break; + + case ID_ATTACH: + pm->n_attach = cf_ReadInt(infile); + if(pm->n_attach) + { + pm->attach_slots = (a_bank *)mem_malloc(sizeof(a_bank) * pm->n_attach); + ASSERT( pm->attach_slots != NULL ); + + for (i = 0; i < pm->n_attach; i++ ) + { + a_bank *bank = &pm->attach_slots[i]; + + bank->parent = cf_ReadInt( infile ); + ReadModelVector( &bank->pnt, infile ); + ReadModelVector( &bank->norm, infile ); + bank->f_uvec = false; + } + } + else + { + pm->attach_slots = NULL; + } + break; + + case ID_ATTACH_NORMALS: + { + bool f_uvec = false; + int num_normals; + + if(pm->n_attach == (num_normals = cf_ReadInt(infile))) + f_uvec = true; + else + { + mprintf((0, "WARNING: Ingoring ATTACH normals - total number doesn't match number of attach points\n")); + //Int3(); + DataError("Model <%s> specifies %d attach points but only contains %d attach normals\n",pm->name,pm->n_attach,num_normals); + } + + if(num_normals) + { + for (i = 0; i < num_normals; i++ ) + { + vector temp; + a_bank *bank = &pm->attach_slots[i]; + + cf_ReadInt( infile ); + ReadModelVector( &temp, infile ); + ReadModelVector( &bank->uvec, infile ); + bank->f_uvec = f_uvec; + } + } + else + { + pm->attach_slots = NULL; + } + break; + } + + case ID_WBS: + { + int i; + int j; + + // Get the number of weapon batteries on this object + pm->num_wbs = cf_ReadInt(infile); + + if(pm->num_wbs) + { + pm->poly_wb = (poly_wb_info *) mem_malloc(sizeof(poly_wb_info) * pm->num_wbs); + + // Get each individual wb info struct + for(i = 0; i < pm->num_wbs; i++) + { + pm->poly_wb[i].num_gps = cf_ReadInt(infile); + for(j = 0; j < pm->poly_wb[i].num_gps; j++) + { + if(j <= 7) + pm->poly_wb[i].gp_index[j] = cf_ReadInt(infile); + else + cf_ReadInt(infile); + } + + if(pm->poly_wb[i].num_gps > 8) + pm->poly_wb[i].num_gps = 8; + + pm->poly_wb[i].num_turrets = cf_ReadInt(infile); + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + if(j <= 7) + pm->poly_wb[i].turret_index[j] = cf_ReadInt(infile); + else + cf_ReadInt(infile); + } + if(pm->poly_wb[i].num_turrets > 8) + pm->poly_wb[i].num_turrets = 8; + + } + } + else + { + pm->poly_wb = NULL; + } + + break; + } + + case ID_GROUND: + pm->n_ground = cf_ReadInt(infile); + pm->ground_slots = (w_bank *)mem_malloc(sizeof(w_bank) * pm->n_ground); + ASSERT( pm->ground_slots != NULL ); + + for (i = 0; i < pm->n_ground; i++ ) + { + w_bank *bank = &pm->ground_slots[i]; + + bank->parent = cf_ReadInt( infile ); + + ReadModelVector( &bank->pnt, infile ); + ReadModelVector( &bank->norm, infile ); + } + break; + + case ID_TXTR: + { + //Texture filename list + int i,n; + char name_buf[128]; + + //mprintf((0,"Got chunk TXTR, len=%d\n",len)); + + n = cf_ReadInt(infile); + pm->n_textures = n; + ASSERT (ntextures[i]=ret; + if (GameTextures[ret].alpha<.99) + pm->flags|=PMF_ALPHA; + } + + break; + } + + case ID_ROT_ANIM: + case ID_ANIM: + { + int nframes; + //mprintf ((0,"ROT ANIM chunk!!!\n")); + + if (!timed) + { + nframes=cf_ReadInt (infile); + pm->num_key_angles=nframes; + } + + for (int i=0;in_models;i++) + { + + if (timed) + { + pm->submodel[i].num_key_angles=cf_ReadInt(infile); + pm->submodel[i].rot_track_min=cf_ReadInt(infile); + pm->submodel[i].rot_track_max=cf_ReadInt(infile); + + if (pm->submodel[i].rot_track_minframe_min) + pm->frame_min=pm->submodel[i].rot_track_min; + if (pm->submodel[i].rot_track_max>pm->frame_max) + pm->frame_max=pm->submodel[i].rot_track_max; + } + else + { + pm->submodel[i].num_key_angles=nframes; + } + + pm->submodel[i].keyframe_axis=(vector *)mem_malloc ((pm->submodel[i].num_key_angles+1)*sizeof(vector)); + pm->submodel[i].keyframe_angles=(int *)mem_malloc ((pm->submodel[i].num_key_angles+1)*sizeof(int)); + pm->submodel[i].keyframe_matrix=(matrix *)mem_malloc ((pm->submodel[i].num_key_angles+1)*sizeof(matrix)); + if (timed) + { + pm->submodel[i].rot_start_time=(int *)mem_malloc ((pm->submodel[i].num_key_angles+1)*sizeof(int)); + ASSERT (pm->submodel[i].rot_start_time!=NULL); + + int num_ticks=(pm->submodel[i].rot_track_max-pm->submodel[i].rot_track_min); + + if (num_ticks>0) + { + pm->submodel[i].tick_ang_remap=(ushort *)mem_malloc (num_ticks*2); + ASSERT (pm->submodel[i].tick_ang_remap); + } + else + { + pm->submodel[i].tick_ang_remap=NULL; + } + } + + ASSERT (pm->submodel[i].keyframe_axis!=NULL); + ASSERT (pm->submodel[i].keyframe_angles!=NULL); + ASSERT (pm->submodel[i].keyframe_matrix!=NULL); + + for (int t=0;tsubmodel[i].num_key_angles;t++) + { + vector *axis; + float mag; + + if (timed) + pm->submodel[i].rot_start_time[t]=cf_ReadInt(infile); + + ReadModelVector (&pm->submodel[i].keyframe_axis[t+0],infile); + axis=&pm->submodel[i].keyframe_axis[t+0]; + + mag=vm_GetMagnitude (axis); + if (mag>0) + pm->submodel[i].keyframe_axis[t]/=mag; + + pm->submodel[i].keyframe_angles[t+0]=cf_ReadInt(infile); + //if this axis is backwards, rotate the angle the other direction + if (t==0) + { + /*if (pm->submodel[i].keyframe_angles[0]>=32768) + { + mprintf ((0,"\nFlipping suboject %s!\n",pm->submodel[i].name)); + pm->submodel[i].keyframe_angles[0]*=-1; + }*/ + + } + /*else + dir=vm_DotProduct (axis,&pm->submodel[i].keyframe_axis[1]); + + if (dir<0) + { + pm->submodel[i].keyframe_angles[t+0]*=-1; + pm->submodel[i].keyframe_axis[t+0]*=-1.0; + }*/ + + } + } + + break; + } + + case ID_POS_ANIM: + { + int nframes; + + + //mprintf ((0,"POS ANIM chunk!!!\n")); + if (!timed) + { + nframes=cf_ReadInt (infile); + pm->num_key_pos=nframes; + } + + for (int i=0;in_models;i++) + { + if (timed) + { + pm->submodel[i].num_key_pos=cf_ReadInt(infile); + pm->submodel[i].pos_track_min=cf_ReadInt(infile); + pm->submodel[i].pos_track_max=cf_ReadInt(infile); + + + if (pm->submodel[i].pos_track_minframe_min) + pm->frame_min=pm->submodel[i].pos_track_min; + if (pm->submodel[i].pos_track_max>pm->frame_max) + pm->frame_max=pm->submodel[i].pos_track_max; + + int num_ticks=(pm->submodel[i].pos_track_max-pm->submodel[i].pos_track_min); + + if (num_ticks>0) + { + pm->submodel[i].tick_pos_remap=(ushort *)mem_malloc (num_ticks*2); + ASSERT (pm->submodel[i].tick_pos_remap); + } + else + { + pm->submodel[i].tick_pos_remap=NULL; + } + + } + else + pm->submodel[i].num_key_pos=nframes; + + pm->submodel[i].keyframe_pos=(vector *)mem_malloc ((pm->submodel[i].num_key_pos+1)*sizeof(vector)); + ASSERT (pm->submodel[i].keyframe_pos!=NULL); + + if (timed) + { + pm->submodel[i].pos_start_time=(int *)mem_malloc ((pm->submodel[i].num_key_pos+1)*sizeof(int)); + ASSERT (pm->submodel[i].pos_start_time!=NULL); + } + + for (int t=0;tsubmodel[i].num_key_pos;t++) + { + if (timed) + pm->submodel[i].pos_start_time[t]=cf_ReadInt(infile); + + ReadModelVector (&pm->submodel[i].keyframe_pos[t+0],infile); + + } + } + + break; + } + default: + //mprintf((0,"Unknown chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len)); + cfseek(infile,len,SEEK_CUR); + break; + + } + + } + + // Set the greater of keyframe of positions or keyframe angles to to be max keyframes + pm->max_keys=max(pm->num_key_pos,pm->num_key_angles); + + // Build animation keyframe matrices + + matrix base_matrix,dest_matrix,temp_matrix; + int cur_angle; + int parent; + + for (i=0;in_models;i++) + { + vm_MakeIdentity (&base_matrix); + + bsp_info *sm=&pm->submodel[i]; + + int newt; + for (newt=0;newtnum_key_angles;newt++) + { + cur_angle=sm->keyframe_angles[newt]; + BuildModelAngleMatrix (&temp_matrix,cur_angle,&sm->keyframe_axis[newt]); + vm_MakeIdentity (&dest_matrix); + dest_matrix=temp_matrix * base_matrix; + base_matrix=dest_matrix; + sm->keyframe_matrix[newt]=base_matrix; + } + + // Build children + parent=pm->submodel[i].parent; + + if (parent==i) + { + pm->submodel[i].parent=-1; + + } + else if (parent!=-1) + { + pm->submodel[parent].children[pm->submodel[parent].num_children]=i; + pm->submodel[parent].num_children++; + } + + if (pm->submodel[i].num_key_angles==0 && (pm->submodel[i].flags & SOF_ROTATE)) + { + mprintf ((0,"You have a rotator that has no keyframe on model %s.\n",pm->name)); + pm->submodel[i].flags&=~SOF_ROTATE; + } + else if (pm->submodel[i].num_key_angles==0 && (pm->submodel[i].flags & SOF_TURRET)) + { + mprintf ((0,"You have a turret that has no keyframe on model %s.\n",pm->name)); + pm->submodel[i].flags&=~SOF_TURRET; + } + + // Figure out the size of this facing subobject + if (pm->submodel[i].flags & SOF_FACING) + { + ASSERT (pm->submodel[i].num_faces==1); // This facing has more than one face + vector vecs[30]; + vector avg; + + int newt; + for (newt=0;newtsubmodel[i].faces[0].nverts;newt++) + { + vecs[newt]=pm->submodel[i].verts[pm->submodel[i].faces[0].vertnums[newt]]; + } + + pm->submodel[i].rad=(sqrt(vm_GetCentroid (&avg,vecs,pm->submodel[i].faces[0].nverts))/2); + + + pm->flags |=PMF_FACING; + + } + + if (pm->submodel[i].flags & (SOF_GLOW | SOF_THRUSTER)) + { + ASSERT (pm->submodel[i].num_faces==1); // This glow has more than one face + vector vecs[30]; + + for (t=0;tsubmodel[i].faces[0].nverts;t++) + vecs[t]=pm->submodel[i].verts[pm->submodel[i].faces[0].vertnums[t]]; + + + vm_GetNormal (&pm->submodel[i].glow_info->normal,&vecs[0],&vecs[1],&vecs[2]); + + pm->flags |=PMF_FACING; // Set this so we know when to draw + + } + + // Now build the tick/keyframe remap list + int num_ticks=(pm->submodel[i].pos_track_max-pm->submodel[i].pos_track_min); + for (t=0;timed && tpos_track_min+t; + + if (sm->num_key_pos>1) + { + for (int k=sm->num_key_pos-1;k>=0 && !done;k--) + { + if (current_tick>=sm->pos_start_time[k]) + { + sm->tick_pos_remap[t]=k; + done=1; + } + } + + if (done==0) + { + mprintf ((0,"Couldn't get a good keyframe!\n")); + Int3(); + } + } + } + + // Now build the tick/keyframe remap list + num_ticks=(pm->submodel[i].rot_track_max-pm->submodel[i].rot_track_min); + for (t=0;timed && tnum_key_angles>1) + { + int done=0; + int current_tick=sm->rot_track_min+t; + for (int k=sm->num_key_angles-1;k>=0 && !done;k--) + { + if (current_tick>=sm->rot_start_time[k]) + { + sm->tick_ang_remap[t]=k; + done=1; + } + } + + if (done==0) + { + mprintf ((0,"Couldn't get a good keyframe!\n")); + Int3(); + } + } + } + } + + pm->flags &=~PMF_NOT_RESIDENT; // mark it as in memory + + // Find Min/Max of whole model + FindMinMaxForModel (pm); + + // adjust positional interpolation frames + + #ifdef MACINTOSH + //DAJ added check for null data + if(pm->model_data) + SwapPolymodelData(pm->model_data); + #endif + + FindWBSubobjects (pm); + + // Set as newstyle + pm->new_style=1; + + + if (pm->n_models>MAX_SUBOBJECTS) + { + mprintf ((0,"This model has more than the max number of subobjects! (%d)\n",MAX_SUBOBJECTS)); + Int3(); + FreePolyModel (pm-Poly_models); + return 0; + } + + + + return 1; +} + +extern int paged_in_count; +extern int paged_in_num; + +// given a filename, reads in a POF and returns an index into the Poly_models array +// returns -1 if something is wrong +int LoadPolyModel (char *filename,int pageable) +{ + char name[256]; + char fname[256],pname[256],extname[256]; + int i,polynum; + CFILE *infile=NULL; + int overlay=0; + + ASSERT(Num_poly_models >= 0); + ASSERT(Num_poly_models < MAX_POLY_MODELS); + + ChangePolyModelName (filename,name); + + // If this polymodel is already in memory, just use that index + i=FindPolyModelName (name); + if (i!=-1) + { + #ifdef RELEASE + Poly_models[i].used++; + return i; + #endif + + int old_used=Poly_models[i].used; + int not_res=0; + + if (Poly_models[i].flags & PMF_NOT_RESIDENT) + not_res=1; + + + mprintf ((1,"Model '%s' usage count is now %d.\n",Poly_models[i].name,Poly_models[i].used+1)); + + Poly_models[i].used=1; + FreePolyModel (i); + + memset (&Poly_models[i],0,sizeof(poly_model)); + WBClearInfo(&Poly_models[i]); + Poly_models[i].used=old_used+1; + if (not_res) + Poly_models[i].flags=PMF_NOT_RESIDENT; + + overlay=1; + polynum=i; + } + + // Not in memory, so we must load it + + ddio_SplitPath (filename,pname,fname,extname); + + if (!pageable) + { + infile=cfopen (filename,"rb"); + if (!infile) + return -1; + } + + + if (!overlay) + polynum=AllocPolyModel (); + else + { + if (!(Poly_models[polynum].flags & PMF_NOT_RESIDENT)) + { + if (pageable) + { + infile=cfopen (filename,"rb"); + if (!infile) + return -1; + } + pageable=0; + } + } + + ASSERT (polynum>=0); + + //Used for progress bar when loading the level + if(infile) + { + paged_in_count += cfilelength(infile); + paged_in_num++; + } + + // if this is an oof instead of a pof, flag it as such + if (!stricmp (".OOF",extname)) + { + Poly_models[polynum].new_style=1; + } + else + Poly_models[polynum].new_style=0; + + //mprintf ((0,"Loading model %s\n",name)); + strcpy (Poly_models[polynum].name,name); + + int ret=0; + if (!pageable) + ret=ReadNewModelFile (polynum,infile); + else + ret=1; + + if (infile) + cfclose (infile); + + Poly_models[polynum].id=polynum; + + if (ret) + return polynum; // loaded successfully + + return -1; // damn, didn't load +} + +// Pages in a polymodel if its not already in memory +void PageInPolymodel (int polynum, int type, float *size_ptr) +{ + if (!(Poly_models[polynum].flags & PMF_NOT_RESIDENT)) + { + if(!(Poly_models[polynum].flags & PMF_SIZE_COMPUTED)) + if(type != -1) + { + ComputeDefaultSize(type, polynum, size_ptr); + } + return; + } + + mprintf ((0,"Paging in polymodel %s.\n",Poly_models[polynum].name)); + + CFILE *infile; + infile=(CFILE *)cfopen (Poly_models[polynum].name,"rb"); + + if (!infile) + { + // due to a bug in some 3rd party tablefile editors, full paths might + // have been used when they shouldn't have been + char *end_ptr,*start_ptr; + start_ptr = Poly_models[polynum].name; + end_ptr = start_ptr + strlen(start_ptr) - 1; + while( (end_ptr>=start_ptr) && (*end_ptr!='\\') ) end_ptr--; + if(end_ptr < start_ptr) + { + Error("Failed to page in %s.", Poly_models[polynum].name); + return; + } + + ASSERT(*end_ptr=='\\'); + end_ptr++; + + infile = (CFILE *)cfopen(end_ptr,"rb"); + if(!infile) + { + Error("Failed to page in %s.", Poly_models[polynum].name); + return; + } + } + +// ASSERT(infile); + + int ret=ReadNewModelFile (polynum,infile); + + cfclose (infile); + ASSERT (ret>0); + + // See if textures need to be remapped + int remap=0; + for (int t=0;trtype.pobj_info.model_num]; + object_info *obj_info = &Object_info[obj->id]; + + if(obj->type == OBJ_PLAYER || obj->type == OBJ_WEAPON) return; + + // Setup all the subobjects for the keyframe + float frame=obj->rtype.pobj_info.anim_frame; + + for(i = 0; i < pm->n_models; i++) + { + bsp_info *sm=&pm->submodel[i]; + int x = (sm->flags & SOF_WB_MASKS) >> WB_INDEX_SHIFT; + + if(!(sm->flags & SOF_TURRET) && !((sm->flags & SOF_WB) && ((obj_info->static_wb[x].flags & WBF_ANIM_MASKS) == WBF_ANIM_LOCAL))) + { + if (frame<=sm->rot_track_min) + normalized_time[i]=0.0; + else if (frame>=sm->rot_track_max) + normalized_time[i]=1.0; + else + { + float total_time=sm->rot_track_max-sm->rot_track_min; + normalized_time[i]=(frame-sm->rot_track_min)/total_time; + } + } + else if((sm->flags & SOF_WB) && !(sm->flags & SOF_TURRET)) + { + static float w_frame; + ASSERT(x >= 0 && x < MAX_WBS_PER_OBJ); + + if(obj->dynamic_wb) + w_frame = obj->dynamic_wb[x].wb_anim_frame; + else + w_frame = 0; + + if (w_frame <= sm->rot_track_min) + normalized_time[i] = 0.0; + else if (w_frame >= sm->rot_track_max) + normalized_time[i] = 1.0; + else + { + float total_time = sm->rot_track_max - sm->rot_track_min; + normalized_time[i] = (w_frame - sm->rot_track_min) / total_time; + } + + } + } + + // Now, override angles of weapon bank turrets + if(obj->dynamic_wb) + { + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + int sobj_index; + + sobj_index = pm->poly_wb[i].turret_index[j]; + normalized_time[sobj_index] = obj->dynamic_wb[i].norm_turret_angle[j]; + } + } + } + else + { + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + int sobj_index; + + sobj_index = pm->poly_wb[i].turret_index[j]; + normalized_time[sobj_index] = 0; + } + } + } +} + + +void SetNormalizedTimeObj(object *obj, float *normalized_time) +{ + int i,j; + + if (Poly_models[obj->rtype.pobj_info.model_num].flags & PMF_TIMED) + { + SetNormalizedTimeObjTimed(obj,normalized_time); + return; + } + + float norm_anim_frame = GetNormalizedKeyframe(obj->rtype.pobj_info.model_num, obj->rtype.pobj_info.anim_frame); + + // Setup all the subobjects for the keyframe + for(i = 0; i < Poly_models[obj->rtype.pobj_info.model_num].n_models; i++) + normalized_time[i] = norm_anim_frame; + + // Currently, we are not handling player weapons in this manner + // chrishack -- weapons with turrets -- COOL. + if(obj->type == OBJ_PLAYER || obj->type == OBJ_WEAPON) return; + + ASSERT(obj->type == OBJ_POWERUP || obj->type == OBJ_ROBOT || obj->type == OBJ_BUILDING || obj->type == OBJ_DEBRIS || obj->type==OBJ_DOOR || obj->type==OBJ_CLUTTER); + + // Now, override angles of weapon bank turrets + for(i = 0; i < Poly_models[obj->rtype.pobj_info.model_num].num_wbs; i++) + { + for(j = 0; j < Poly_models[obj->rtype.pobj_info.model_num].poly_wb[i].num_turrets; j++) + { + int sobj_index; + + sobj_index = Poly_models[obj->rtype.pobj_info.model_num].poly_wb[i].turret_index[j]; + normalized_time[sobj_index] = obj->dynamic_wb[i].norm_turret_angle[j]; + } + } +} + +void SetNormalizedTimeAnimTimed(float frame, float *normalized_time, poly_model *pm) +{ + int i,j; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + for(i = 0; i < pm->n_models; i++) + { + bsp_info *sm=&pm->submodel[i]; + + if (frame<=sm->rot_track_min) + normalized_time[i]=0.0; + else if (frame>=sm->rot_track_max) + normalized_time[i]=1.0; + else + { + float total_time=sm->rot_track_max-sm->rot_track_min; + normalized_time[i]=(frame-sm->rot_track_min)/total_time; + } + } + + // Now, override angles of weapon bank turrets + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + int sobj_index; + + sobj_index = pm->poly_wb[i].turret_index[j]; + normalized_time[sobj_index] = 0.0; + } + } +} + +// This is for non-rotated turrets or rotators +void SetNormalizedTimeAnim(float anim_frame, float *normalized_time, poly_model *pm) +{ + int i,j; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + float norm_anim_frame = anim_frame/pm->max_keys; + + if (pm->flags & PMF_TIMED) + { + SetNormalizedTimeAnimTimed(anim_frame, normalized_time, pm); + return; + } + + // Setup all the subobjects for the keyframe + for(i = 0; i < pm->n_models; i++) + normalized_time[i] = norm_anim_frame; + + // Now, override angles of weapon bank turrets + for(i = 0; i < pm->num_wbs; i++) + { + for(j = 0; j < pm->poly_wb[i].num_turrets; j++) + { + int sobj_index; + + sobj_index = pm->poly_wb[i].turret_index[j]; + normalized_time[sobj_index] = 0.0; + } + } +} + +//Given a model pointer and an array of floats that go from 0..1, calculate the angles of each +// corresponding subobject +void SetModelAngles (poly_model *po,float *normalized_angles) +{ + int i; + + ASSERT (!(po->flags & PMF_NOT_RESIDENT)); + + if (po->num_key_angles>0 && normalized_angles) + { + // get time per keyframe state + float state_time=1.0/(po->num_key_angles-1); + float cur_state_time; + int cur_state; + float normal_state_time; + + for (i=0;in_models;i++) + { + // Don't rotate turrets or auto-rotators or weapon battery submodels + if (!(po->submodel[i].flags & (SOF_ROTATE | SOF_TURRET ))) + { + + // Find out which keyframe we're at + + cur_state=normalized_angles[i]/state_time; + matrix dest_matrix; + + // Find out how far into that keyframe we are + cur_state_time=normalized_angles[i]-((float)cur_state*state_time); + normal_state_time=cur_state_time/state_time; + + // Now do a parametric adjustment on the angles + + int cur_angle=0; + + // If we're already at the high point of the interpolation then just + // stuff some values + if (cur_state==po->num_key_angles-1) + { + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&po->submodel[i].keyframe_matrix[po->num_key_angles-1]); + continue; + + } + + dest_matrix=po->submodel[i].keyframe_matrix[cur_state]+ + ((po->submodel[i].keyframe_matrix[cur_state+1]-po->submodel[i].keyframe_matrix[cur_state])*normal_state_time); + + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&dest_matrix); + //po->submodel[i].mod_matrix=dest_matrix; + + } + else if (po->submodel[i].flags & SOF_ROTATE) + { + float flrot=Gametime*po->submodel[i].rps; + int introt=flrot; + + float fdiff=flrot-introt; + + ASSERT (fdiff>=0 && fdiff<=1); + + // fdiff now equals 0 to 1 + matrix temp_matrix; + vector temp_vec; + if (po->new_style) + temp_vec=po->submodel[i].keyframe_axis[1]; + else + temp_vec=po->submodel[i].norm; + + BuildModelAngleMatrix (&temp_matrix,fdiff*65535,&temp_vec); + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&temp_matrix); + } + else if (po->submodel[i].flags & SOF_TURRET) + { + matrix temp_matrix; + if (po->new_style) + BuildModelAngleMatrix (&temp_matrix,normalized_angles[i]*65535,&po->submodel[i].keyframe_axis[1]); + else + BuildModelAngleMatrix (&temp_matrix,normalized_angles[i]*65535,&po->submodel[i].norm); + + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&temp_matrix); + } + } + } + else + { + for (i=0;in_models;i++) + { + if (po->submodel[i].flags & SOF_ROTATE) + { + float flrot=Gametime*po->submodel[i].rps; + int introt=flrot; + + float fdiff=flrot-introt; + + ASSERT (fdiff>=0 && fdiff<=1); + + // fdiff now equals 0 to 1 + matrix temp_matrix; + vector temp_vec; + if (po->new_style) + temp_vec=po->submodel[i].keyframe_axis[1]; + else + temp_vec=po->submodel[i].norm; + + BuildModelAngleMatrix (&temp_matrix,fdiff*65535,&temp_vec); + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&temp_matrix); + } + else if (po->submodel[i].flags & SOF_TURRET) + { + // We need to find where in this rotation is relative to gametime + float flrot = Gametime; + int introt = flrot; + + float fdiff=flrot-introt; + + // fdiff now equals 0 to 1 + matrix temp_matrix; + if (po->new_style) + BuildModelAngleMatrix (&temp_matrix,fdiff*65535,&po->submodel[i].keyframe_axis[0]); + else + BuildModelAngleMatrix (&temp_matrix,fdiff*65535,&po->submodel[i].norm); + + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&temp_matrix); + } + else + { + po->submodel[i].angs.h=0; + po->submodel[i].angs.p=0; + po->submodel[i].angs.h=0; + } + } + } + +} + +//Given a model pointer and an array of floats that go from 0..1, calculate the interpolated +//position of each corresponding subobject +void SetModelInterpPos (poly_model *po,float *normalized_pos) +{ + int i; + + ASSERT (!(po->flags & PMF_NOT_RESIDENT)); + + if (normalized_pos && po->num_key_pos>0) + { + for (i=0;in_models;i++) + { + bsp_info *sm=&po->submodel[i]; + + // get time per keyframe state + float state_time=1.0/(po->num_key_pos-1); + float cur_state_time; + int cur_state; + float normal_state_time; + + // Find out which keyframe we're at + cur_state=normalized_pos[i]/state_time; + + // Find out how far into that keyframe we are + cur_state_time=normalized_pos[i]-((float)cur_state*state_time); + normal_state_time=cur_state_time/state_time; + + // Now do a parametric adjustment on the positions + + vector total_delta_pos={0,0,0}; + vector subpos; + vector final_pos; + + // If we're already at the high point of the interpolation then just + // stuff some values + if (cur_state==sm->num_key_pos-1) + { + po->submodel[i].mod_pos=po->submodel[i].keyframe_pos[po->num_key_pos-1]; + continue; + } + + if (cur_state==0) + po->submodel[i].mod_pos=normal_state_time*po->submodel[i].keyframe_pos[1]; + else + { + vm_SubVectors (&subpos,&po->submodel[i].keyframe_pos[cur_state+1],&po->submodel[i].keyframe_pos[cur_state]); + final_pos=po->submodel[i].keyframe_pos[cur_state]+(subpos*normal_state_time); + po->submodel[i].mod_pos=final_pos; + } + + } + } + else + { + for (i=0;in_models;i++) + { + po->submodel[i].mod_pos.x=0; + po->submodel[i].mod_pos.y=0; + po->submodel[i].mod_pos.z=0; + } + } +} + +// Sets the effect used by a polymodel +void SetPolymodelEffect (polymodel_effect *poly_effect) +{ + Polymodel_effect=*poly_effect; +} + +//Given a model pointer and an array of floats that go from 0..1, calculate the interpolated +//position/angle of each corresponding subobject +void SetModelAnglesAndPosTimed (poly_model *po,float *normalized_time,uint subobj_flags) +{ + int i; + + ASSERT (!(po->flags & PMF_NOT_RESIDENT)); + + if (normalized_time) + { + // get time per keyframe state + + for (i=0;in_models;i++) + { + bsp_info *sm=&po->submodel[i]; + int int_tick,current_frame,total_ticks; + float normal_state_time,current_tick; + + if (!(subobj_flags & (1<num_key_pos<=1) + { + vm_MakeZero(&sm->mod_pos); + goto do_angles; + } + + if (normalized_time[i]==1.0) + { + sm->mod_pos=sm->keyframe_pos[sm->num_key_pos-1]; + goto do_angles; + } + + // Find out which keyframe we're at + total_ticks=sm->pos_track_max-sm->pos_track_min; + current_tick=(normalized_time[i]*total_ticks)+sm->pos_track_min; + int_tick=current_tick; + current_frame=sm->tick_pos_remap[int_tick-sm->pos_track_min]; + + ASSERT (current_frame>=0 && current_frame<=sm->num_key_pos-1); + + if (current_frame==sm->num_key_pos-1) + { + sm->mod_pos=sm->keyframe_pos[sm->num_key_pos-1]; + goto do_angles; + } + else + { + int ticks_between_frames=sm->pos_start_time[current_frame+1]-sm->pos_start_time[current_frame]; + float this_tick=current_tick-sm->pos_start_time[current_frame]; + normal_state_time=(float)this_tick/(float)ticks_between_frames; + + vector subpos; + vm_SubVectors (&subpos,&sm->keyframe_pos[current_frame+1],&sm->keyframe_pos[current_frame]); + sm->mod_pos=sm->keyframe_pos[current_frame]+(subpos*normal_state_time); + } + + + // Now do angle stuff here + do_angles: + // Don't rotate turrets or auto-rotators + if (!(sm->flags & (SOF_TURRET | SOF_ROTATE))) + { + if (sm->num_key_angles<=1) + { + sm->angs.p=0; + sm->angs.h=0; + sm->angs.b=0; + continue; + } + + if (normalized_time[i]==1.0) + { + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&po->submodel[i].keyframe_matrix[sm->num_key_angles-1]); + continue; + } + + // Find out which keyframe we're at + total_ticks=sm->rot_track_max-sm->rot_track_min; + current_tick=(normalized_time[i]*total_ticks)+sm->rot_track_min; + int_tick=current_tick; + current_frame=sm->tick_ang_remap[int_tick-sm->rot_track_min]; + + ASSERT (current_frame>=0 && current_frame<=sm->num_key_angles-1); + + if (current_frame==sm->num_key_angles-1) + { + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&po->submodel[i].keyframe_matrix[sm->num_key_angles-1]); + continue; + } + else + { + int ticks_between_frames=sm->rot_start_time[current_frame+1]-sm->rot_start_time[current_frame]; + float this_tick=current_tick-sm->rot_start_time[current_frame]; + normal_state_time=(float)this_tick/(float)ticks_between_frames; + + // Now do a parametric adjustment on the angles + + matrix dest_matrix=sm->keyframe_matrix[current_frame]+ + ((sm->keyframe_matrix[current_frame+1]-sm->keyframe_matrix[current_frame])*normal_state_time); + + vm_ExtractAnglesFromMatrix (&sm->angs,&dest_matrix); + } + + + } + else + { + // Adjust special subobjects + if (po->submodel[i].flags & SOF_ROTATE) + { + float flrot=Gametime*po->submodel[i].rps; + int introt=flrot; + + float fdiff=flrot-introt; + + ASSERT (fdiff>=0 && fdiff<=1); + + // fdiff now equals 0 to 1 + matrix temp_matrix; + vector temp_vec; + if (po->new_style) + temp_vec=po->submodel[i].keyframe_axis[1]; + else + temp_vec=po->submodel[i].norm; + + BuildModelAngleMatrix (&temp_matrix,fdiff*65535,&temp_vec); + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&temp_matrix); + } + else if (po->submodel[i].flags & SOF_TURRET) + { + matrix temp_matrix; + if (po->new_style) + BuildModelAngleMatrix (&temp_matrix,normalized_time[i]*65535,&po->submodel[i].keyframe_axis[1]); + else + BuildModelAngleMatrix (&temp_matrix,normalized_time[i]*65535,&po->submodel[i].norm); + + vm_ExtractAnglesFromMatrix (&po->submodel[i].angs,&temp_matrix); + } + } + + } + + } + else + { + for (i=0;in_models;i++) + { + po->submodel[i].mod_pos.x=0; + po->submodel[i].mod_pos.y=0; + po->submodel[i].mod_pos.z=0; + + po->submodel[i].angs.p=0; + po->submodel[i].angs.h=0; + po->submodel[i].angs.b=0; + } + } +} + +// Sets the position and rotation of a polymodel. Used for rendering and collision detection +void SetModelAnglesAndPos (poly_model *po,float *normalized_time,uint subobj_flags) +{ + ASSERT (!(po->flags & PMF_NOT_RESIDENT)); + + + if (po->flags & PMF_TIMED) + { + SetModelAnglesAndPosTimed(po,normalized_time,subobj_flags); + return; + } + else + { + SetModelAngles(po,normalized_time); + SetModelInterpPos (po,normalized_time); + + } +} + + + +vector Instance_fog_plane_stack[MAX_SUBOBJECTS]; +vector Instance_fog_portal_vert_stack[MAX_SUBOBJECTS]; +vector Instance_light_stack[MAX_SUBOBJECTS]; +vector Instance_specular_pos[MAX_SUBOBJECTS]; +vector Instance_bump_pos[MAX_SUBOBJECTS]; + +int Instance_light_cnt=0; + +void StartLightInstance (vector *pos,matrix *orient) +{ + int gouraud=0,specular=0,fogged=0,bumped=0; + + if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD) + gouraud=1; + if (Polymodel_use_effect && Polymodel_effect.type & PEF_FOGGED_MODEL) + fogged=1; + if (Polymodel_use_effect && Polymodel_effect.type & (PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES)) + specular=1; + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_BUMPMAPPED)) + bumped=1; + + if (gouraud) + Instance_light_stack[Instance_light_cnt]=*Polymodel_light_direction; + if (fogged) + { + Instance_fog_plane_stack[Instance_light_cnt]=Polymodel_fog_plane; + Instance_fog_portal_vert_stack[Instance_light_cnt]=Polymodel_fog_portal_vert; + } + + if (specular) + Instance_specular_pos[Instance_light_cnt]=Polymodel_specular_pos; + + if (bumped) + Instance_bump_pos[Instance_light_cnt]=Polymodel_bump_pos; + + Instance_light_cnt++; + + vector temp_vec; + + if (gouraud) + { + vm_MatrixMulVector (&temp_vec,Polymodel_light_direction,orient); + *Polymodel_light_direction=temp_vec; + } + if (fogged) + { + vector tempv = Polymodel_fog_portal_vert - *pos; + vm_MatrixMulVector (&temp_vec,&tempv,orient); + Polymodel_fog_portal_vert = temp_vec; + + vm_MatrixMulVector (&temp_vec,&Polymodel_fog_plane,orient); + Polymodel_fog_plane=temp_vec; + } + if (specular) + { + vector tempv = Polymodel_specular_pos - *pos; + vm_MatrixMulVector (&temp_vec,&tempv,orient); + Polymodel_specular_pos = temp_vec; + } + + if (bumped) + { + vector tempv = Polymodel_bump_pos - *pos; + vm_MatrixMulVector (&temp_vec,&tempv,orient); + Polymodel_bump_pos = temp_vec; + } + +} +void DoneLightInstance () +{ + ASSERT (Instance_light_cnt!=0); + Instance_light_cnt--; + + if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD) + *Polymodel_light_direction=Instance_light_stack[Instance_light_cnt]; + + if (Polymodel_use_effect && Polymodel_effect.type & PEF_FOGGED_MODEL) + { + Polymodel_fog_plane=Instance_fog_plane_stack[Instance_light_cnt]; + Polymodel_fog_portal_vert=Instance_fog_portal_vert_stack[Instance_light_cnt]; + } + + if (Polymodel_use_effect && Polymodel_effect.type & (PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES)) + Polymodel_specular_pos=Instance_specular_pos[Instance_light_cnt]; + + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_BUMPMAPPED)) + Polymodel_bump_pos=Instance_bump_pos[Instance_light_cnt]; +} + +// Draws a polygon model to the viewport +// Normalized_time is an array of floats from 0 to 1 that represent how far into +// an animation state we are + +// This is the static light version +void DrawPolygonModel(vector *pos,matrix *orient,int model_num,float *normalized_time,int flags,float r,float g,float b, uint f_render_sub,ubyte use_effect,ubyte overlay) +{ + poly_model *po; + + ASSERT (Poly_models[model_num].used); + ASSERT (!(Poly_models[model_num].flags & PMF_NOT_RESIDENT)); + + GetPolymodelPointer (model_num); + + Polymodel_use_effect=use_effect; + Polymodel_light_type=POLYMODEL_LIGHTING_STATIC; + Polylighting_static_red=r; + Polylighting_static_green=g; + Polylighting_static_blue=b; + + rend_SetOverlayType (OT_NONE); + rend_SetLighting (LS_NONE); + + po=&Poly_models[model_num]; + + g3_StartInstanceMatrix(pos,orient); + + if (Polymodel_use_effect && Polymodel_effect.type & PEF_FOGGED_MODEL) + { + Polymodel_fog_plane=Polymodel_effect.fog_plane; + Polymodel_fog_portal_vert=Polymodel_effect.fog_portal_vert; + } + + if (Polymodel_use_effect && Polymodel_effect.type & (PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES)) + Polymodel_specular_pos=Polymodel_effect.spec_light_pos; + + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_BUMPMAPPED)) + Polymodel_bump_pos=Polymodel_effect.bump_light_pos; + + StartLightInstance(pos,orient); + + SetModelAnglesAndPos (po,normalized_time,f_render_sub); + + if (f_render_sub==0xFFFFFFFF || overlay) //draw entire object + { + if (po->new_style) + { + RenderPolygonModel (po, f_render_sub); + } + } + else + { + ASSERT (po->new_style==1); + + rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX); + + int i; + for (i=0;in_models;i++) + { + if ((f_render_sub & (1<submodel[i].min+po->submodel[i].max)/2; + save_offset=po->submodel[i].offset; + vm_MakeZero (&po->submodel[i].offset); + vm_MakeZero (&po->submodel[i].mod_pos); + memset (&po->submodel[i].angs,0,sizeof(angvec)); + ofs*=-1; + + po->submodel[i].offset=ofs; + RenderSubmodel (po,&po->submodel[i], f_render_sub); + + po->submodel[i].offset=save_offset; + } + else + RenderSubmodel (po,&po->submodel[i], f_render_sub); + + } + } + } + + + DoneLightInstance(); + g3_DoneInstance(); +} + +// This draws a gouraud shaded version +void DrawPolygonModel(vector *pos,matrix *orient,int model_num,float *normalized_time,int flags,vector *lightdir,float r,float g,float b, uint f_render_sub,ubyte use_effect,ubyte overlay) +{ + poly_model *po; + vector light_vec=*lightdir; + + Polymodel_use_effect=use_effect; + + ASSERT (Poly_models[model_num].used); + ASSERT (!(Poly_models[model_num].flags & PMF_NOT_RESIDENT)); + + GetPolymodelPointer (model_num); + + rend_SetOverlayType (OT_NONE); + rend_SetLighting (LS_GOURAUD); + rend_SetColorModel (CM_RGB); + + Polymodel_light_type=POLYMODEL_LIGHTING_GOURAUD; + Polylighting_static_red=r; + Polylighting_static_green=g; + Polylighting_static_blue=b; + + Polymodel_light_direction=&light_vec; + + if (Polymodel_use_effect && Polymodel_effect.type & PEF_FOGGED_MODEL) + { + Polymodel_fog_plane=Polymodel_effect.fog_plane; + Polymodel_fog_portal_vert=Polymodel_effect.fog_portal_vert; + } + + if (Polymodel_use_effect && Polymodel_effect.type & (PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES)) + Polymodel_specular_pos=Polymodel_effect.spec_light_pos; + + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_BUMPMAPPED)) + Polymodel_bump_pos=Polymodel_effect.bump_light_pos; + + g3_StartInstanceMatrix(pos,orient); + StartLightInstance(pos,orient); + + po=&Poly_models[model_num]; + SetModelAnglesAndPos (po,normalized_time,f_render_sub); + + if (f_render_sub==0xFFFFFFFF || overlay) //draw entire object + { + if (po->new_style) + RenderPolygonModel (po, f_render_sub); + } + else + { + ASSERT (po->new_style==1); + + rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX); + + int i; + for (i=0;in_models;i++) + { + if ((f_render_sub & (1<submodel[i].min+po->submodel[i].max)/2; + save_offset=po->submodel[i].offset; + vm_MakeZero (&po->submodel[i].offset); + vm_MakeZero (&po->submodel[i].mod_pos); + memset (&po->submodel[i].angs,0,sizeof(angvec)); + ofs*=-1; + + po->submodel[i].offset=ofs; + RenderSubmodel (po,&po->submodel[i], f_render_sub); + + po->submodel[i].offset=save_offset; + } + else + RenderSubmodel (po,&po->submodel[i], f_render_sub); + } + } + } + + g3_DoneInstance(); + DoneLightInstance(); +} + +// This draws a lightmap shaded version +void DrawPolygonModel(vector *pos,matrix *orient,int model_num,float *normalized_time,int flags,lightmap_object *lm_object, uint f_render_sub,ubyte use_effect,ubyte overlay) +{ + poly_model *po; + + ASSERT (Poly_models[model_num].used); + ASSERT (!(Poly_models[model_num].flags & PMF_NOT_RESIDENT)); + + GetPolymodelPointer (model_num); + + Polymodel_use_effect=use_effect; + + rend_SetLighting (LS_NONE); + Polymodel_light_type=POLYMODEL_LIGHTING_LIGHTMAP; + Polylighting_lightmap_object=lm_object; + + if (Polymodel_use_effect && Polymodel_effect.type & PEF_FOGGED_MODEL) + { + Polymodel_fog_plane=Polymodel_effect.fog_plane; + Polymodel_fog_portal_vert=Polymodel_effect.fog_portal_vert; + } + + if (Polymodel_use_effect && Polymodel_effect.type & (PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES)) + Polymodel_specular_pos=Polymodel_effect.spec_light_pos; + + if (Polymodel_use_effect && (Polymodel_effect.type & PEF_BUMPMAPPED)) + Polymodel_bump_pos=Polymodel_effect.bump_light_pos; + + g3_StartInstanceMatrix(pos,orient); + StartLightInstance (pos,orient); + + po=&Poly_models[model_num]; + SetModelAnglesAndPos (po,normalized_time,f_render_sub); + + if (f_render_sub==0xFFFFFFFF || overlay) //draw entire object + { + if (po->new_style) + RenderPolygonModel (po, f_render_sub); + } + else + { + ASSERT (po->new_style==1); + + rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX); + + int i; + for (i=0;in_models;i++) + { + if ((f_render_sub & (1<submodel[i].min+po->submodel[i].max)/2; + save_offset=po->submodel[i].offset; + vm_MakeZero (&po->submodel[i].offset); + vm_MakeZero (&po->submodel[i].mod_pos); + memset (&po->submodel[i].angs,0,sizeof(angvec)); + ofs*=-1; + + po->submodel[i].offset=ofs; + RenderSubmodel (po,&po->submodel[i], f_render_sub); + + po->submodel[i].offset=save_offset; + } + else + RenderSubmodel (po,&po->submodel[i], f_render_sub); + + } + } + } + + + g3_DoneInstance(); + DoneLightInstance(); + rend_SetOverlayType (OT_NONE); +} + +void FreeAllModels () +{ + for (int i=0;i0) + { + Poly_models[i].used=1; + + FreePolyModel (i); + } + } +} + + +// Inits our models array and loads our ship pof +int InitModels () +{ + for (int i=0;iflags & PMF_NOT_RESIDENT)); + + ASSERT(pm->used); + ASSERT (num>=0 && num<=pm->max_keys); + + return ((num/(float)pm->max_keys)); +} + +// Goes through all poly models and gets all missing textures +void RemapPolyModels () +{ + int i; + + for (i=0;iused>0); + int count=0; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + for (i=0;in_models;i++) + { + if (IsNonRenderableSubmodel (pm,i)) + continue; + + count+=pm->submodel[i].num_faces; + } + + + return count; +} + +// Given an object, a submodel, and a vertex number, calculates the world position +// of that point +void GetPolyModelPointInWorld (vector *dest,poly_model *pm, vector *wpos, matrix *orient,int subnum, vector *pos,vector *norm) +{ + bsp_info *sm=&pm->submodel[subnum]; + float normalized_time[MAX_SUBOBJECTS]; + int i; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + if (!pm->new_style) + return; + + for (i=0;isubmodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b); + vm_TransposeMatrix(&m); + + tpnt = pnt * m; + + if (norm!=NULL) + cur_norm = cur_norm *m; + + pnt = tpnt + pm->submodel[mn].offset+pm->submodel[mn].mod_pos; + + mn = pm->submodel[mn].parent; + } + + //now instance for the entire object + m = *orient; + vm_TransposeMatrix(&m); + + if (norm!=NULL) + *norm=(cur_norm * m); + *dest = pnt * m; + *dest += (*wpos); +} + + +void GetPolyModelPointInWorld (vector *dest,poly_model *pm, vector *wpos, matrix *orient,int subnum, float *normalized_time, vector *pos, vector *norm) +{ + bsp_info *sm=&pm->submodel[subnum]; + + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + if (!pm->new_style) + return; + + SetModelAnglesAndPos (pm,normalized_time); + + vector pnt = *pos; + int mn = subnum; + vector cur_norm; + + if (norm!=NULL) + cur_norm=*norm; + + matrix m; + + // Instance up the tree for this gun + while (mn != -1) + { + vector tpnt; + + vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b); + vm_TransposeMatrix(&m); + + tpnt = pnt * m; + + if (norm!=NULL) + cur_norm = cur_norm *m; + + pnt = tpnt + pm->submodel[mn].offset+pm->submodel[mn].mod_pos; + + mn = pm->submodel[mn].parent; + } + + //now instance for the entire object + m = *orient; + vm_TransposeMatrix(&m); + + if (norm!=NULL) + *norm=(cur_norm * m); + *dest = pnt * m; + *dest += (*wpos); +} + + +// Returns 1 if this submodel shouldn't be rendered +int IsNonRenderableSubmodel (poly_model *pm,int submodelnum) +{ + ASSERT (pm->used); + ASSERT (!(pm->flags & PMF_NOT_RESIDENT)); + + if ((pm->submodel[submodelnum].flags & SOF_FRONTFACE) || (pm->submodel[submodelnum].flags & SOF_SHELL)) + return 1; + + if (pm->submodel[submodelnum].num_faces==0) + return 1; + + return 0; + +} diff --git a/module/CMakeLists.txt b/module/CMakeLists.txt new file mode 100644 index 000000000..528767374 --- /dev/null +++ b/module/CMakeLists.txt @@ -0,0 +1,5 @@ +SET (HEADERS ) +SET (CPPS + module.cpp) + +ADD_LIBRARY(module STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/module/module.cpp b/module/module.cpp new file mode 100644 index 000000000..6ac048aa7 --- /dev/null +++ b/module/module.cpp @@ -0,0 +1,892 @@ +/* +* $Logfile: /DescentIII/Main/module/module.cpp $ +* $Revision: 20 $ +* $Date: 10/21/99 2:43p $ +* $Author: Kevin $ +* +* Source for Dynamic Loadable Module functions +* +* $Log: /DescentIII/Main/module/module.cpp $ + * + * 20 10/21/99 2:43p Kevin + * Mac Merge + * + * 19 8/24/99 4:33p Jeff + * fixed bug with finding alternate file names in Linux + * + * 18 8/22/99 7:12p Jeff + * when open a module on a Linux system, if the original open fails, then + * check for alternate files with different case. + * + * 17 8/16/99 11:48a Nate + * (JEFF) Use calls to dd_ instead of ddio_ (this library cannot be + * dependant on any other libs) + * + * 16 7/28/99 5:22p Kevin + * Mac Merge fixes + * + * 15 7/28/99 2:17p Kevin + * Macintosh Stuff! + * + * 14 5/13/99 5:07p Ardussi + * changes for compiling on the Mac + * + * 13 4/19/99 4:01a Jeff + * fixed splitpath + * + * 12 4/18/99 3:14p Jeff + * fixed splitpath for linux + * + * 11 4/16/99 1:06a Jeff + * + * 10 1/19/99 1:08p Jason + * added dynamically loadable dlls + * + * 9 1/14/99 10:36a Jeff + * removed ddio_ dependency (requires a #ifdef around functions taken out + * of ddio_) + * + * 8 1/13/99 6:50a Jeff + * made linux friendly (#include case-sensitivity) + * + * 7 1/11/99 1:01p Jeff + * code will convert an .so->.dll for win32 and .dll->.so for Linux + * + * 6 1/11/99 12:53p Jeff + * added a function that given a module name it will make sure it has an + * extension. Made Osiris friendly with modules with no extension + * + * 5 1/11/99 12:29p Jeff + * tack on an automatic extension to the module name if one isn't given + * (system dependent) + * + * 4 1/10/99 6:47a Jeff + * Changes made to get linux version to compile + * + * 3 7/06/98 10:45a Jeff + * Made Linux friendly + * + * 2 6/05/98 2:15p Jeff + * Initial creation + * + * 1 6/05/98 2:14p Jeff +* +* $NoKeywords: $ +*/ +#include "module.h" +#include "pstypes.h" +#include "pserror.h" +#include "ddio.h" + +#ifdef __LINUX__ +bool mod_FindRealFileNameCaseInsenstive(const char *directory,const char *filename,char *new_filename); +#endif + +#ifdef MACINTOSH +#include +#include +#endif +#include "module.h" +#include "pstypes.h" +#include "pserror.h" +#include +#include +#include +#if defined(__LINUX__) +#include "linux/linux_fix.h" +#endif +#if defined(WIN32) //INSTEAD OF MAKING MODULE HAVE DEPENDENCIES, PUT THE 2 DDIO FUNCTIONS I NEED HERE +// Split a pathname into its component parts +void dd_SplitPath(const char* srcPath, char* path, char* filename, char* ext) +{ + char drivename[_MAX_DRIVE], dirname[_MAX_DIR]; + _splitpath(srcPath, drivename, dirname, filename, ext); + if (path) + sprintf(path, "%s%s", drivename, dirname); +} +// Constructs a path in the local file system's syntax +// newPath: stores the constructed path +// absolutePathHeader: absolute path on which the sub directories will be appended +// (specified in local file system syntax) +// takes a variable number of subdirectories which will be concatenated on to the path +// the last argument in the list of sub dirs *MUST* be NULL to terminate the list +void dd_MakePath(char* newPath, const char* absolutePathHeader, const char* subDir, ...) +{ + const char delimiter = '\\'; + va_list args; + char* currentDir = NULL; + int pathLength = 0; + + assert(newPath); + assert(absolutePathHeader); + assert(subDir); + + if (newPath != absolutePathHeader) + { + strcpy(newPath, absolutePathHeader); + } + // Add the first sub directory + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, subDir); + + // Add the additional subdirectories + va_start(args, subDir); + while ((currentDir = va_arg(args, char*)) != NULL) + { + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, currentDir); + } + va_end(args); +} +#elif defined(MACINTOSH) +//DAJ why duplicate the functions that ddio provides??? +// Split a pathname into its component parts +void dd_SplitPath(const char* srcPath, char* path, char* filename, char* ext) +{ +#ifdef FIXED + char drivename[_MAX_DRIVE], dirname[_MAX_DIR]; + _splitpath(srcPath, drivename, dirname, filename, ext); + if (path) + sprintf(path, "%s%s", drivename, dirname); +#endif +} +// Constructs a path in the local file system's syntax +// newPath: stores the constructed path +// absolutePathHeader: absolute path on which the sub directories will be appended +// (specified in local file system syntax) +// takes a variable number of subdirectories which will be concatenated on to the path +// the last argument in the list of sub dirs *MUST* be NULL to terminate the list +void dd_MakePath(char* newPath, const char* absolutePathHeader, const char* subDir, ...) +{ +#ifdef FIXED + const char delimiter = '\\'; + va_list args; + char* currentDir = NULL; + int pathLength = 0; + + assert(newPath); + assert(absolutePathHeader); + assert(subDir); + + if (newPath != absolutePathHeader) + { + strcpy(newPath, absolutePathHeader); + } + // Add the first sub directory + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, subDir); + + // Add the additional subdirectories + va_start(args, subDir); + while ((currentDir = va_arg(args, char*)) != NULL) + { + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter) + { + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, currentDir); + } + va_end(args); +#endif +} +#elif defined(__LINUX__) +// Split a pathname into its component parts +void dd_SplitPath(const char* srcPath, char* path, char* filename, char* ext) +{ + int pathStart = -1; + int pathEnd = -1; + int fileStart = -1; + int fileEnd = -1; + int extStart = -1; + int extEnd = -1; + int totalLen = strlen(srcPath); + // Check for an extension + /////////////////////////////////////// + int t = totalLen - 1; + while( (srcPath[t]!='.') && (srcPath[t]!='/') && (t>=0) ) t--; + //see if we are at an extension + if((t>=0)&&(srcPath[t]=='.')){ + //we have an extension + extStart = t; + extEnd = totalLen - 1; + if(ext) + { + strncpy(ext,&(srcPath[extStart]),extEnd - extStart + 1); + ext[extEnd - extStart + 1] = '\0'; + } + }else{ + //no extension + if(ext) + ext[0] = '\0'; + } + + // Check for file name + //////////////////////////////////// + int temp = (extStart!=-1)?(extStart):(totalLen-1); + while( (srcPath[temp]!='/') && (temp>=0) ) temp--; + if(temp<0) + temp = 0; + if(srcPath[temp]=='/'){ + //we have a file + fileStart = temp + 1; + if(extStart!=-1) + fileEnd = extStart - 1; + else + fileEnd = totalLen - 1; + if(filename) + { + strncpy(filename,&(srcPath[fileStart]),fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = '\0'; + } + pathStart = 0; + pathEnd = fileStart - 2; + //Copy the rest into the path name + if(path) + { + strncpy(path, &(srcPath[pathStart]),pathEnd - pathStart + 1); + path[pathEnd - pathStart + 1] = 0; + } + }else{ + //only file, no path + fileStart = 0; + if(extStart != -1) + fileEnd = extStart - 1; + else + fileEnd = totalLen - 1; + if(filename) + { + strncpy(filename, &(srcPath[fileStart]), fileEnd - fileStart + 1); + filename[fileEnd - fileStart + 1] = 0; + } + + // Only file no path + if(path) + { + path[0] = 0; + } + } +} +// Constructs a path in the local file system's syntax +// newPath: stores the constructed path +// absolutePathHeader: absolute path on which the sub directories will be appended +// (specified in local file system syntax) +// takes a variable number of subdirectories which will be concatenated on to the path +// the last argument in the list of sub dirs *MUST* be NULL to terminate the list +void dd_MakePath(char* newPath, const char* absolutePathHeader, const char* subDir, ...) +{ + const char delimiter = '/'; + va_list args; + char* currentDir = NULL; + int pathLength = 0; + + ASSERT(newPath); + ASSERT(absolutePathHeader); + ASSERT(subDir); + + if (newPath != absolutePathHeader){ + strcpy(newPath, absolutePathHeader); + } + // Add the first sub directory + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter){ + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, subDir); + + // Add the additional subdirectories + va_start(args, subDir); + while ((currentDir = va_arg(args, char*)) != NULL){ + pathLength = strlen(newPath); + if (newPath[pathLength - 1] != delimiter){ + newPath[pathLength] = delimiter; // add the delimiter + newPath[pathLength+1] = 0; // terminate the string + } + strcat(newPath, currentDir); + } + va_end(args); +} +#endif +int ModLastError = MODERR_NOERROR; +// Returns the real name of the module. If a given file has an extension, it will +// just return that filename. If the given file has no given extension, the +// system specific extension is concatted and returned. +void mod_GetRealModuleName(const char *modfilename,char *realmodfilename) +{ + char pathname[_MAX_PATH],filename[_MAX_FNAME],extension[_MAX_EXT]; +#ifdef MACINTOSH + ddio_SplitPath(modfilename,pathname,filename,extension); +#else + dd_SplitPath(modfilename,pathname,filename,extension); +#endif + if(*extension=='\0') +#if defined (WIN32) + strcat(filename,".dll"); +#elif defined(MACINTOSH) + strcat(filename,".msl"); +#elif defined (__LINUX__) + #if defined(MACOSX) + strcat(filename,".dylib"); + #else + strcat(filename,".so"); + #endif +#endif + else{ +#if defined (WIN32) + if(!stricmp(extension,".so") || !stricmp(extension,"msl") || !stricmp(extension,"dylib")) + strcat(filename,".dll"); + else + strcat(filename,extension); +#elif defined (MACINTOSH) + if(!stricmp(extension,".dll") || !stricmp(extension,"so") || !stricmp(extension,"dylib")) + strcat(filename,".msl"); + else + strcat(filename,extension); +#elif defined (__LINUX__) && !defined(MACOSX) + if(!stricmp(extension,".dll") || !stricmp(extension,"msl") || !stricmp(extension,"dylib")) + strcat(filename,".so"); + else + strcat(filename,extension); +#elif defined (__LINUX__) && defined(MACOSX) + if(!stricmp(extension,".dll") || !stricmp(extension,"msl") || !stricmp(extension,"so")) + strcat(filename,".dylib"); + else + strcat(filename,extension); + +#endif + } + if(*pathname!='\0') +#ifdef MACINTOSH + ddio_MakePath(realmodfilename,pathname,filename,NULL); +#else + dd_MakePath(realmodfilename,pathname,filename,NULL); +#endif + else + strcpy(realmodfilename,filename); +} +//Loads a dynamic module into memory for use. +//Returns true on success, false otherwise +//modfilename is the name of the module (without an extension such as DLL, or so) +bool mod_LoadModule(module *handle,char *imodfilename,int flags) +{ + if(!imodfilename){ + ModLastError = MODERR_OTHER; + return false; + } + if(!handle) { + ModLastError = MODERR_INVALIDHANDLE; + return false; + } + handle->handle = NULL; + char modfilename[_MAX_PATH]; + mod_GetRealModuleName(imodfilename,modfilename); +#if defined (WIN32) + handle->handle = LoadLibrary(modfilename); + if(!handle->handle){ + //There was an error loading the module + DWORD err = GetLastError(); + switch(err){ + case ERROR_DLL_INIT_FAILED: + ModLastError = MODERR_MODINITFAIL; + break; + case ERROR_DLL_NOT_FOUND : + ModLastError = MODERR_MODNOTFOUND; + break; + case ERROR_INVALID_DLL: + ModLastError = MODERR_INVALIDMOD; + break; + default: + ModLastError = MODERR_OTHER; + break; + } + return false; + } +#elif defined (__LINUX__) + int f = 0; + if(flags&MODF_LAZY) + f |= RTLD_LAZY; + if(flags&MODF_NOW) + f |= RTLD_NOW; + if(flags&MODF_GLOBAL) + f |= RTLD_GLOBAL; + handle->handle = dlopen(modfilename,f); + if(!handle->handle) + { + // ok we couldn't find the given name...try other ways + char dir[_MAX_PATH],fname[_MAX_PATH],nname[_MAX_PATH],ext[64]; + dd_SplitPath(modfilename,dir,fname,ext); + strcat(fname,ext); + + if(!mod_FindRealFileNameCaseInsenstive(dir,fname,nname)) + { + mprintf((0,"Module Load Err: %s\n",dlerror())); + ModLastError = MODERR_MODNOTFOUND; + return false; + }else + { + // ok we have a different filename + dd_MakePath(modfilename,dir,nname,NULL); + mprintf((0,"MOD: Attempting to open %s instead of %s\n",modfilename,fname)); + handle->handle = dlopen(modfilename,f); + if(!handle->handle) + { + mprintf((0,"Module Load Err: %s\n",dlerror())); + ModLastError = MODERR_MODNOTFOUND; + return false; + } + } + } +#elif defined (MACINTOSH) + OSErr err = noErr; + FSSpec SLSpec; + Str255 errName; + Str63 toolName; + Ptr myMainAddr; + CFragConnectionID connID; + Str255 symName; + strcpy((char*) symName, modfilename); + c2pstr((char*) symName); + err = FSMakeFSSpec(0, 0L, symName, &SLSpec); + if(err) { + mprintf((2, "could not make FSSpec in loadModule\n")); +// Int3(); + } + err = GetDiskFragment(&SLSpec, 0, 0, NULL, 5, &connID, (Ptr*)NULL, NULL); +// err = GetDiskFragment(&SLSpec, 0, kCFragGoesToEOF, toolName, kPrivateCFragCopy, &connID, (Ptr*)&myMainAddr, errName); + if(err) { + mprintf((2, "could not get disk fragmet %s\n", modfilename)); +// Int3();; + } + + handle->handle = connID; + mprintf((0, "opening fragment ID 0x%X\n", handle->handle)); +#endif + //Success + return true; +} +//Frees a previously loaded module from memory, it can no longer be used +//Returns true on success, false otherwise +bool mod_FreeModule(module *handle) +{ + bool ret=true; + + if(!handle){ + ModLastError = MODERR_INVALIDHANDLE; + return false; + } + if(!handle->handle){ + ModLastError = MODERR_OTHER; + return false; + } +#if defined (WIN32) + ret = (FreeLibrary(handle->handle)!=0); +#elif defined (__LINUX__) + dlclose(handle->handle);//dlclose() returns an int, but no docs say what values!!! +#elif defined (MACINTOSH) +/* //BROKE! but not needed, OS closes connection on quit + OSErr err = noErr; + if(handle->handle) + mprintf((0, "closeing fragment 0x%X\n", handle->handle)); + err = CloseConnection((CFragConnectionID*)handle->handle); + if(err != noErr) { + mprintf((2, "could not close fragment 0x%X\n", handle->handle)); +// return false; + } +*/ +#endif + handle->handle = NULL; + return ret; +} +//Returns a pointer to a function within a loaded module. If it returns NULL there was an error. Check mod_GetLastError +//to see if there was an error +//symstr is the name of the function you want to get the symbol for (Do NOT give any pre/suffix to this name) +//parmbytes is the size (in bytes) of the parameter list the function should have +MODPROCADDRESS mod_GetSymbol(module *handle,char *symstr,unsigned char parmbytes) +{ + char buffer[256]; + MODPROCADDRESS sym; + if(!handle){ + ModLastError = MODERR_INVALIDHANDLE; + return NULL; + } + if(!symstr){ + ModLastError = MODERR_OTHER; + return NULL; + } + if(!handle->handle){ + ModLastError = MODERR_NOMOD; + return NULL; + } +#if defined (WIN32) + //We need to first form the correct symbol name (for Windows) + if (parmbytes==255) + sprintf(buffer,"%s",symstr); + else + sprintf(buffer,"_%s@%d",symstr,parmbytes); + DWORD err; + sym = GetProcAddress(handle->handle,buffer); + if (!sym) + { + err=GetLastError (); + + // Try again using no byte ordinals + if (parmbytes!=255) + { + sprintf(buffer,"%s",symstr); + sym = GetProcAddress(handle->handle,buffer); + if (!sym) + err = GetLastError(); + } + + } + if(!sym){ + //there was an error + + switch(err){ + case ERROR_DLL_INIT_FAILED: + ModLastError = MODERR_MODINITFAIL; + break; + case ERROR_DLL_NOT_FOUND : + ModLastError = MODERR_MODNOTFOUND; + break; + case ERROR_INVALID_DLL: + ModLastError = MODERR_INVALIDMOD; + break; + default: + ModLastError = MODERR_OTHER; + break; + } + return NULL; + } +#elif defined (__LINUX__) + sym = dlsym(handle->handle,symstr); + if(!sym){ + ModLastError = MODERR_OTHER; + return NULL; + } +#elif defined (MACINTOSH) + OSErr err = noErr; + CFragSymbolClass symClass; + CFragConnectionID connID; + Str255 symName; + mprintf((0, "finding %s in fragment 0x%X\n", symstr, handle->handle)); + connID = (CFragConnectionID) handle->handle; + + //DAJ for testing only + long numSym; + Ptr symAddr; + err = CountSymbols(connID, &numSym); + for(int i = 0; i < numSym; i++) { + err = GetIndSymbol (connID, i, symName, &symAddr, &symClass); + } + + strcpy((char*) symName, symstr); + c2pstr((char*) symName); + + err = FindSymbol (connID, symName, (Ptr *)&sym, &symClass); + if(err) { + mprintf((2, "could not find %s in fragment %d\n", symstr, handle->handle)); + return NULL; + } + if(!sym){ + ModLastError = MODERR_OTHER; + return NULL; + } +#endif + return sym; +} +//Returns an error code to what the last error was. When this function is called the last error is cleared, so by calling +//this function it not only returns the last error, but it removes it, so if you were to call this function again, it would +//return no error +int mod_GetLastError(void) +{ + //Clear out the errors +#if defined (WIN32) + SetLastError(0); +#elif defined (__LINUX__) + dlerror(); +#endif + int ret = ModLastError; + ModLastError = MODERR_NOERROR; + return ret; +} + +#ifdef __LINUX__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void dd_GetWorkingDir(char *path, int len); +bool dd_SetWorkingDir(const char *path); +bool dd_FindFileStart(const char *wildcard, char *namebuf); +bool dd_FindNextFile(char *namebuf); +void dd_FindFileClose(); + +// retrieve the current working folder where file operation will occur. +void dd_GetWorkingDir(char *path, int len) +{ + getcwd(path,len); +} + +bool dd_SetWorkingDir(const char *path) +{ + return (chdir(path)) ? false : true; +} + +//These functions allow one to find a file +static int globerrfn(const char *path,int err) +{ + mprintf((0,"Error accessing %s: %s .... \n",path,strerror(err))); + return 0; +} + +class CModFindFiles +{ +public: + CModFindFiles() + { + globindex = -1; + } + + bool Start(const char *wildcard, char *namebuf); + bool Next(char *namebuf); + void Close(void); + +private: + int globindex; + glob_t ffres; +}; + +bool CModFindFiles::Start(const char *wildcard, char *namebuf) +{ + ASSERT(wildcard); + ASSERT(namebuf); + + if(globindex!=-1) + Close(); + + int rc,flags; + flags = GLOB_MARK; + rc = glob(wildcard,flags,globerrfn,&ffres); + if(rc==GLOB_NOSPACE){ + mprintf((0,"Out of space during glob\n")); + globindex = -1; + return false; + } + if(!ffres.gl_pathc){ + globindex = -1; + return false; + } + + globindex = 0; + char ext[256]; + dd_SplitPath(ffres.gl_pathv[0],NULL,namebuf,ext); + strcat(namebuf,ext); + return true; +} + +bool CModFindFiles::Next(char *namebuf) +{ + ASSERT(namebuf); + if(globindex==-1) + return false; + globindex++; + if(globindex>=ffres.gl_pathc) + return false; + + char ext[256]; + dd_SplitPath(ffres.gl_pathv[globindex],NULL,namebuf,ext); + strcat(namebuf,ext); + return true; +} + +void CModFindFiles::Close(void) +{ + if(globindex==-1) + return; + globindex = -1; + globfree(&ffres); +} + +bool mod_FindRealFileNameCaseInsenstive(const char *directory,const char *fname,char *new_filename) +{ + bool use_dir = false; + char dir_to_use[_MAX_PATH]; + char file_to_use[_MAX_PATH]; + + char *real_dir,*real_file; + + if(directory) + { + // there is a directory for this path + use_dir = true; + real_dir = (char *)directory; + real_file = (char *)fname; + }else + { + // there may be a directory in the path (*sigh*) + char t_ext[256]; + char t_dir[_MAX_PATH]; + char t_filename[_MAX_PATH]; + + dd_SplitPath(fname,t_dir,t_filename,t_ext); + if(strlen(t_dir)>0) + { + use_dir = true; + strcpy(dir_to_use,t_dir); + real_dir = (char *)dir_to_use; + strcpy(file_to_use,t_filename); + strcat(file_to_use,t_ext); + real_file = (char *)file_to_use; + + mprintf((1,"MOD: Found directory \"%s\" in filename, new filename is \"%s\"\n",real_dir,real_file)); + }else + { + use_dir = false; + real_dir = NULL; + real_file = (char *)fname; + } + } + + // build up a list of filenames in the current directory that begin with the lowercase and + // upper case first letter of the filename + + // do the case of the first letter to start + int case_val; + char wildcard_pattern[_MAX_PATH]; + int iterations = 1; + bool found_match = false; + + if( (real_file[0]>='a' && real_file[0] <= 'z') || + (real_file[0]>='A' && real_file[0] <= 'Z') ) + { + // alpha first letter...we need to do 2 iterations + iterations = 2; + } + + for(case_val=0;case_val= 'a' && first_letter <= 'z') + { + // we need to uppercase the letter + first_letter = toupper(first_letter); + }else + { + // we need to lowercase the letter + first_letter = tolower(first_letter); + } + + // create a wildcard patter full of ? replacing letters (except the first one) + char *wptr = wildcard_pattern; + char *fptr = &real_file[1]; + *wptr = first_letter; + wptr++; + while(*fptr) + { + if(isalpha(*fptr)) + { + *wptr = '?'; + }else + { + *wptr = *fptr; + } + + fptr++; + wptr++; + } + *wptr = '\0'; + }else + { + // use the case of the first letter + // create a wildcard patter full of ? replacing letters (except the first one) + char *wptr = wildcard_pattern; + char *fptr = &real_file[1]; + *wptr = real_file[0]; + wptr++; + while(*fptr) + { + if(isalpha(*fptr)) + { + *wptr = '?'; + }else + { + *wptr = *fptr; + } + + fptr++; + wptr++; + } + *wptr = '\0'; + } + + // now tack on a directory if we are to use a directory + char *wpattern; + char fullpath[_MAX_PATH]; + if(use_dir) + { + dd_MakePath(fullpath,real_dir,wildcard_pattern,NULL); + wpattern = fullpath; + }else + { + wpattern = wildcard_pattern; + } + + // ok, we have our wildcard pattern, get all the files that match it + // and search them looking for a match (case insensitive) + char namebuffer[_MAX_PATH]; + bool gotfile; + CModFindFiles ff; + for(gotfile = ff.Start(wpattern,namebuffer); gotfile ; gotfile = ff.Next(namebuffer) ) + { + if(!stricmp(namebuffer,real_file)) + { + // we found a match! + found_match = true; + break; + } + } + ff.Close(); + + if(found_match) + { + strcpy(new_filename,namebuffer); + mprintf((1,"MOD: Using \"%s\" instead of \"%s\"\n",new_filename,real_file)); + break; + } + } + + return found_match; +} +#endif diff --git a/movie/CMakeLists.txt b/movie/CMakeLists.txt new file mode 100644 index 000000000..489d8c43a --- /dev/null +++ b/movie/CMakeLists.txt @@ -0,0 +1,6 @@ +SET (HEADERS ) +SET (CPPS + d3movie.cpp) + +ADD_LIBRARY(movie STATIC ${HEADERS} ${CPPS}) +target_link_libraries(movie libmve) \ No newline at end of file diff --git a/movie/d3movie.cpp b/movie/d3movie.cpp new file mode 100644 index 000000000..2324d048e --- /dev/null +++ b/movie/d3movie.cpp @@ -0,0 +1,1098 @@ +#include + +#ifdef __LINUX__ +#include +#include +#include +#include + +#define O_BINARY 0 +#endif + +#include "DDAccess.h" + +#ifdef WIN32 +#include +#include +#include "dsound.h" +#endif + + +#include +#include + + +#include "movie.h" +#include "mvelibw.h" +#include "pserror.h" +#include "renderer.h" +#include "application.h" +#include "ddio.h" +#include "ddvid.h" +#include "grtext.h" +#include "mem.h" +#include "bitmap.h" +#include "gamefont.h" +#include "game.h" + + + +namespace { + MovieFrameCallback_fp Movie_callback = NULL; + char MovieDir[512]; + char SoundCardName[512]; + ushort CurrentPalette[256]; + int Movie_bm_handle = -1; + uint Movie_current_framenum = 0; + bool Movie_looping = false; + + + + +#ifndef NO_MOVIES + +#ifdef WIN32 +class MovieSoundBuffer : public ISysSoundBuffer + { + private: + LPDIRECTSOUNDBUFFER m_pBuffer; + + public: + MovieSoundBuffer(LPDIRECTSOUNDBUFFER buffer) + : m_pBuffer( buffer ) + { + } + + //////////////////////////// + // Release + //////////////////////////// + // Releases the memory associated with a sound buffer. This pointer is + // no longer valid after return. + // + // Returns: + // -1 : Invalid Parameter + // 0 : Ok! + int Release() + { + m_pBuffer->Release(); + delete this; + return 0; + } + + ////////////////////////////// + // SetVolume + ////////////////////////////// + // Sets the volume of a buffer. + // + // Returns: + // 0 : no error + // -1 : Cannot set volume + // -2 : Invalid parameters + int SetVolume(signed long vol) + { + return m_pBuffer->SetVolume( vol ); + } + + /////////////////////////// + // SetPan + /////////////////////////// + // Sets the pan of a buffer. + // + // Returns: + // 0 : no error + // -1 : Cannot set pan + // -2 : Invalid parameters + int SetPan(signed long pan) + { + return m_pBuffer->SetPan( pan ); + } + + ///////////////////////// + // Stop + ///////////////////////// + // Stops a buffer from playing + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Stop() + { + return m_pBuffer->Stop(); + } + + ///////////////////////// + // Play + ///////////////////////// + // Starts a buffer playing (or changes the flags for a buffer currently + // playing). + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Play(unsigned int flags) + { + DWORD dsFlags = ( flags & LNXSND_LOOPING ) ? DSBPLAY_LOOPING : 0; + return m_pBuffer->Play( 0, 0, dsFlags ); + } + + //////////////////////////// + // GetCaps + //////////////////////////// + // Get the capabilities of a sound buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int GetCaps(SysSoundCaps *caps) + { + DSBCAPS dsCaps; + dsCaps.dwSize = sizeof(dsCaps); + int res = m_pBuffer->GetCaps( &dsCaps ); + if( res != 0 ) + return res; + + caps->dwBufferBytes = dsCaps.dwBufferBytes; + caps->dwFlags = dsCaps.dwFlags; + return 0; + } + + ////////////////////////////// + // GetStatus + ////////////////////////////// + // Returns the status of a buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int GetStatus(unsigned int *status) + { + return m_pBuffer->GetStatus( reinterpret_cast( status ) ); + } + + /////////////////////////////////////// + // GetCurrentPosition + /////////////////////////////////////// + // Returns the current play and write positions of the buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int GetCurrentPosition(unsigned int *ppos,unsigned int *wpos) + { + return m_pBuffer->GetCurrentPosition( reinterpret_cast( ppos ), reinterpret_cast( wpos ) ); + } + + /////////////////////////////////////// + // SetCurrentPosition + /////////////////////////////////////// + // Sets the current play position of the buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int SetCurrentPosition(unsigned int pos) + { + return m_pBuffer->SetCurrentPosition( pos ); + } + + ///////////////////////// + // Lock + ///////////////////////// + // Locks the given buffer, returning pointer(s) to the buffer(s) along with + // available the size of the buffer(s) for writing. + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Lock( + unsigned int pos, + unsigned int numbytes, + void **ptr1, + unsigned int *numbytes1, + void **ptr2, + unsigned int *numbytes2, + unsigned int flags) + { + return m_pBuffer->Lock( pos, numbytes, ptr1, reinterpret_cast( numbytes1 ), ptr2, reinterpret_cast( numbytes2 ), flags ); + } + + /////////////////////////// + // Unlock + /////////////////////////// + // Unlocks a buffer. + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Unlock( + void *ptr1, + unsigned int num1, + void *ptr2, + unsigned int num2) + { + return m_pBuffer->Unlock( ptr1, num1, ptr2, num2 ); + } + }; + + class MovieSoundDevice : public ISoundDevice + { + private: + LPDIRECTSOUND m_ds; + + public: + MovieSoundDevice() + : m_ds( NULL ) + { + } + + void SetDirectSound( LPDIRECTSOUND ds ) + { + m_ds = ds; + } + + LPDIRECTSOUND GetDirectSound() + { + return m_ds; + } + + /////////////////////////////// + // CreateSoundBuffer + /////////////////////////////// + // Creates a sound buffer to be used with mixing and output. + // + // Returns: + // -1 : Invalid Parameter + // -2 : Out of memory + // 0 : Ok! + int CreateSoundBuffer( SysSoundBufferDesc *lbdesc, ISysSoundBuffer **lsndb ) + { + if( m_ds == NULL ) + return -1; + + DSBUFFERDESC dsBufferDesc; + dsBufferDesc.dwSize = sizeof(dsBufferDesc); + dsBufferDesc.dwFlags = lbdesc->dwFlags; + dsBufferDesc.dwBufferBytes = lbdesc->dwBufferBytes; + dsBufferDesc.dwReserved = 0; + dsBufferDesc.lpwfxFormat = reinterpret_cast( lbdesc->lpwfxFormat ); + + LPDIRECTSOUNDBUFFER dsSndBuffer = NULL; + int res = m_ds->CreateSoundBuffer( &dsBufferDesc, &dsSndBuffer, NULL ); + if( res != DS_OK ) + { + *lsndb = NULL; + return res; + } + + *lsndb = new MovieSoundBuffer( dsSndBuffer ); + return res; + } + }; + + + + +#else + + class MovieSoundBuffer : public ISysSoundBuffer + { + private: + LnxSoundBuffer *m_pBuffer; + + public: + + LnxSoundBuffer *GetLnxBuffer() + { + return m_pBuffer; + } + MovieSoundBuffer(LnxSoundBuffer *buffer) + : m_pBuffer( buffer ) + { + } + + //////////////////////////// + // Release + //////////////////////////// + // Releases the memory associated with a sound buffer. This pointer is + // no longer valid after return. + // + // Returns: + // -1 : Invalid Parameter + // 0 : Ok! + int Release() + { + return LnxSoundBuffer_Release(m_pBuffer); + //m_pBuffer->Release(); + delete this; + return 0; + } + + ////////////////////////////// + // SetVolume + ////////////////////////////// + // Sets the volume of a buffer. + // + // Returns: + // 0 : no error + // -1 : Cannot set volume + // -2 : Invalid parameters + int SetVolume(signed long vol) + { + return LnxSoundBuffer_SetVolume(m_pBuffer, vol ); + } + + /////////////////////////// + // SetPan + /////////////////////////// + // Sets the pan of a buffer. + // + // Returns: + // 0 : no error + // -1 : Cannot set pan + // -2 : Invalid parameters + int SetPan(signed long pan) + { + return LnxSoundBuffer_SetPan(m_pBuffer, pan ); + } + + ///////////////////////// + // Stop + ///////////////////////// + // Stops a buffer from playing + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Stop() + { + return LnxSoundBuffer_Stop(m_pBuffer); + } + + ///////////////////////// + // Play + ///////////////////////// + // Starts a buffer playing (or changes the flags for a buffer currently + // playing). + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Play(unsigned int flags) + { + unsigned int dsFlags = ( flags & LNXSND_LOOPING ) ? LNXSND_LOOPING : 0; + return LnxSoundBuffer_Play( m_pBuffer, dsFlags ); + } + + //////////////////////////// + // GetCaps + //////////////////////////// + // Get the capabilities of a sound buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int GetCaps(SysSoundCaps *caps) + { + return LnxSoundBuffer_GetCaps(m_pBuffer,(LinuxSoundCaps *)caps); + } + + ////////////////////////////// + // GetStatus + ////////////////////////////// + // Returns the status of a buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int GetStatus(unsigned int *status) + { + return LnxSoundBuffer_GetStatus(m_pBuffer, status ); + } + + /////////////////////////////////////// + // GetCurrentPosition + /////////////////////////////////////// + // Returns the current play and write positions of the buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int GetCurrentPosition(unsigned int *ppos,unsigned int *wpos) + { + + return LnxSoundBuffer_GetCurrentPosition(m_pBuffer, ppos , wpos ); + } + + /////////////////////////////////////// + // SetCurrentPosition + /////////////////////////////////////// + // Sets the current play position of the buffer + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int SetCurrentPosition(unsigned int pos) + { + return LnxSoundBuffer_SetCurrentPosition(m_pBuffer, pos ); + } + + ///////////////////////// + // Lock + ///////////////////////// + // Locks the given buffer, returning pointer(s) to the buffer(s) along with + // available the size of the buffer(s) for writing. + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Lock( + unsigned int pos, + unsigned int numbytes, + void **ptr1, + unsigned int *numbytes1, + void **ptr2, + unsigned int *numbytes2, + unsigned int flags) + { + return LnxSoundBuffer_Lock(m_pBuffer, pos, numbytes, ptr1, numbytes1 , ptr2, numbytes2 , flags ); + } + + /////////////////////////// + // Unlock + /////////////////////////// + // Unlocks a buffer. + // + // Returns: + // 0 : no error + // -1 : invalid parameters + int Unlock( + void *ptr1, + unsigned int num1, + void *ptr2, + unsigned int num2) + { + return LnxSoundBuffer_Unlock(m_pBuffer, ptr1, num1, ptr2, num2 ); + } + }; + class MovieSoundDevice : public ISoundDevice + { + private: + LnxSoundDevice m_ds; + public: + MovieSoundDevice() + { + + } + + void SetDirectSound( LnxSoundDevice ds ) + { + m_ds = ds; + } + + LnxSoundDevice GetDirectSound() + { + return m_ds; + } + + /////////////////////////////// + // CreateSoundBuffer + /////////////////////////////// + // Creates a sound buffer to be used with mixing and output. + // + // Returns: + // -1 : Invalid Parameter + // -2 : Out of memory + // 0 : Ok! + int CreateSoundBuffer( SysSoundBufferDesc *lbdesc, ISysSoundBuffer **lsndb ) + { + LnxSoundBuffer *sb = NULL; + int ret = LnxSound_CreateSoundBuffer(&m_ds,(LnxBufferDesc *)lbdesc,(LnxSoundBuffer**)&sb); + if(ret==0) + { + ISysSoundBuffer *p = (ISysSoundBuffer *)new MovieSoundBuffer(sb); + *lsndb = p; + } + else + { + lsndb = NULL; + } + return ret; + } + }; + + +#endif + +#endif +} + +void* CallbackAlloc( unsigned int size ); +void CallbackFree( void *p ); +unsigned int CallbackFileRead( int hFile, void *pBuffer, unsigned int bufferCount ); +void InitializePalette(); +void CallbackSetPalette( unsigned char *pBuffer, unsigned int start, unsigned int count ); +void CallbackShowFrame( unsigned char* buf, unsigned int bufw, unsigned int bufh, + unsigned int sx, unsigned int sy, unsigned int w, unsigned int h, + unsigned int dstx, unsigned int dsty, unsigned int hicolor ); + +#ifndef NO_MOVIES +bool mve_InitSound(oeApplication *app, MovieSoundDevice& device); +void mve_CloseSound(MovieSoundDevice& device); +#endif + + + +// sets the directory where movies are stored +int mve_Init( const char *dir, const char *sndcard ) +{ +#ifndef NO_MOVIES + strcpy( MovieDir, dir ); + strcpy( SoundCardName, sndcard ); + return MVELIB_NOERROR; +#else + return MVELIB_INIT_ERROR; +#endif +} + +// callback called per frame of playback of movies. +void mve_SetCallback( MovieFrameCallback_fp callBack ) +{ +#ifndef NO_MOVIES + Movie_callback = callBack; +#endif +} + +// used to tell movie library how to render movies. +void mve_SetRenderProperties( short x, short y, short w, short h, renderer_type type, bool hicolor ) +{ +} + +// plays a movie using the current screen. +int mve_PlayMovie( const char *pMovieName, oeApplication *pApp ) +{ +#ifndef NO_MOVIES + // open movie file. + int hFile = open( pMovieName, O_RDONLY|O_BINARY ); + if( hFile == -1 ) + { + mprintf(( 0, "MOVIE: Unable to open %s\n", pMovieName )); + return MVELIB_FILE_ERROR; + } + + // determine the movie type + const char* pExtension = strrchr( pMovieName, '.' ); + bool highColor = ( pExtension != NULL && stricmp( pExtension, ".mv8" ) != 0 ); + + // setup + MVE_rmFastMode( MVE_RM_NORMAL ); + MVE_sfCallbacks( CallbackShowFrame ); + MVE_memCallbacks( CallbackAlloc, CallbackFree ); + MVE_ioCallbacks( CallbackFileRead ); + MVE_sfSVGA( 640, 480, 480, 0, NULL, 0, 0, NULL, highColor ? 1 : 0 ); + MVE_palCallbacks( CallbackSetPalette ); + InitializePalette(); + Movie_bm_handle = -1; + + MovieSoundDevice soundDevice; + if( !mve_InitSound( pApp, soundDevice ) ) + { + mprintf((0, "Failed to initialize sound\n")); + close( hFile ); + return MVELIB_INIT_ERROR; + } + + int result = MVE_rmPrepMovie( hFile, -1, -1, 0 ); + if( result != 0 ) + { + mprintf(( 0, "PrepMovie result = %d\n", result )); + close( hFile ); + mve_CloseSound( soundDevice ); + return MVELIB_INIT_ERROR; + } + + bool aborted = false; + Movie_current_framenum = 0; + while( (result = MVE_rmStepMovie()) == 0 ) + { + // let the OS do its thing + pApp->defer(); + + // check for bail + int key = ddio_KeyInKey(); + if (key == KEY_ESC) + { + aborted = true; + break; + } + } + + // free our bitmap + if( Movie_bm_handle != -1 ) + { + bm_FreeBitmap( Movie_bm_handle ); + Movie_bm_handle = -1; + } + + // close our file handle + close( hFile ); + + // determine the return code + int err = MVELIB_NOERROR; + if( aborted ) + { + err = MVELIB_PLAYBACK_ABORTED; + } + else if( result != MVE_ERR_EOF ) + { + err = MVELIB_PLAYBACK_ERROR; + } + + // cleanup and shutdown + MVE_rmEndMovie(); + MVE_ReleaseMem(); + + // reset sound + mve_CloseSound( soundDevice ); + + // return out + return err; +#else + return MVELIB_INIT_ERROR; +#endif +} + +void* CallbackAlloc( unsigned int size ) +{ + return mem_malloc( size ); +} + +void CallbackFree( void *p ) +{ + mem_free( p ); +} + +unsigned int CallbackFileRead( int hFile, void *pBuffer, unsigned int bufferCount ) +{ + unsigned int numRead = read( hFile, pBuffer, bufferCount ); + return ( numRead == bufferCount ) ? 1 : 0; +} + +void InitializePalette() +{ + for( int i = 0; i < 256; ++i ) + { + CurrentPalette[i] = OPAQUE_FLAG | GR_RGB16(0,0,0); + } +} + +void CallbackSetPalette( unsigned char *pBuffer, unsigned int start, unsigned int count ) +{ +#ifndef NO_MOVIES + pBuffer += start * 3; + + for( unsigned int i = 0; i < count; ++i ) + { + unsigned int r = pBuffer[ 0 ] << 2; + unsigned int g = pBuffer[ 1 ] << 2; + unsigned int b = pBuffer[ 2 ] << 2; + pBuffer += 3; + + CurrentPalette[ start + i ] = OPAQUE_FLAG | GR_RGB16( r, g, b ); + } +#endif +} + +int NextPow2( int n ) +{ + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n; +} + +#ifndef NO_MOVIES +void BlitToMovieBitmap(unsigned char* buf, unsigned int bufw, unsigned int bufh, unsigned int hicolor, bool usePow2Texture, int& texW, int& texH) +{ + // get some sizes + int drawWidth = hicolor ? (bufw >> 1) : bufw; + int drawHeight = bufh; + + if( usePow2Texture ) + { + int wPow2 = NextPow2( drawWidth ); + int hPow2 = NextPow2( drawHeight ); + int texSize = ( wPow2 > hPow2 ) ? wPow2 : hPow2; + texW = texSize; + texH = texSize; + } + else + { + texW = drawWidth; + texH = drawHeight; + } + + if( Movie_bm_handle == -1 ) + { + // Allocate our bitmap + Movie_bm_handle = bm_AllocBitmap( texW, texH, 0 ); + } + + unsigned short* pPixelData = (ushort *)bm_data( Movie_bm_handle, 0 ); + GameBitmaps[Movie_bm_handle].flags |= BF_CHANGED; + if( hicolor ) + { + unsigned short* wBuf = (unsigned short*)buf; + for( int y = 0; y < drawHeight; ++y ) + { + for( int x = 0; x < drawWidth; ++x ) + { + unsigned short col16 = *wBuf++; + unsigned int b = (( col16 >> 11 ) & 0x1F) << 3; + unsigned int g = (( col16 >> 5 ) & 0x3F) << 2; + unsigned int r = (( col16 >> 0 ) & 0x1F) << 3; + pPixelData[ x ] = OPAQUE_FLAG | GR_RGB16( r, g, b ); + } + + pPixelData += texW; + } + } + else + { + for( int y = 0; y < drawHeight; ++y ) + { + for( int x = 0; x < drawWidth; ++x ) + { + unsigned char palIndex = *buf++; + pPixelData[ x ] = CurrentPalette[ palIndex ]; + } + + pPixelData += texW; + } + } +} + +void CallbackShowFrame( unsigned char* buf, unsigned int bufw, unsigned int bufh, + unsigned int sx, unsigned int sy, unsigned int w, unsigned int h, + unsigned int dstx, unsigned int dsty, unsigned int hicolor ) +{ + // prepare our bitmap + int texW, texH; + BlitToMovieBitmap( buf, bufw, bufh, hicolor, true, texW, texH ); + + // calculate UVs from texture + int drawWidth = hicolor ? (bufw >> 1) : bufw; + int drawHeight = bufh; + float u = float(drawWidth-1) / float(texW-1); + float v = float(drawHeight-1) / float(texH-1); + + StartFrame( 0, 0, 640, 480, false ); + + rend_ClearScreen( GR_BLACK ); + rend_SetAlphaType( AT_CONSTANT ); + rend_SetAlphaValue( 255 ); + rend_SetLighting( LS_NONE ); + rend_SetColorModel( CM_MONO ); + rend_SetOverlayType( OT_NONE ); + rend_SetWrapType( WT_CLAMP ); + rend_SetFiltering( 0 ); + rend_SetZBufferState( 0 ); + rend_DrawScaledBitmap( dstx, dsty, dstx+drawWidth, dsty+drawHeight, Movie_bm_handle, 0.0f, 0.0f, u, v ); + rend_SetFiltering( 1 ); + rend_SetZBufferState( 1 ); + + // call our callback + if( Movie_callback != NULL ) + { + Movie_callback( dstx, dsty, Movie_current_framenum ); + } + ++Movie_current_framenum; + + EndFrame(); + + rend_Flip(); +} +#endif + +unsigned int mve_SequenceStart( const char *mvename, int *fhandle, oeApplication *app, bool looping ) +{ +#ifndef NO_MOVIES + + int hfile = open( mvename, O_RDONLY|O_BINARY ); + + if (hfile == -1) + { + mprintf((1, "MOVIE: Unable to open %s\n", mvename)); + *fhandle = -1; + return 0; + } + + // setup + MVE_rmFastMode( MVE_RM_NORMAL ); + MVE_memCallbacks( CallbackAlloc, CallbackFree ); + MVE_ioCallbacks( CallbackFileRead ); + InitializePalette(); + Movie_bm_handle = -1; + Movie_looping = looping; + + // let the render know we will be copying bitmaps to framebuffer (or something) + rend_SetFrameBufferCopyState(true); + + *fhandle = hfile; + return (unsigned int)MVE_frOpen( CallbackFileRead, hfile, NULL ); +#else + return 0; +#endif +} + +unsigned int mve_SequenceFrame( unsigned int handle, int fhandle, bool sequence, int *bm_handle ) +{ +#ifndef NO_MOVIES + if( bm_handle ) + { + *bm_handle = -1; + } + + if( handle == -1 ) + { + return (unsigned int)(-1); + } + + static unsigned sw = 0, sh = 0, hicolor = 0; + int err = 0; + +reread_frame: + + // get the next frame of data + unsigned char* pBuffer = NULL; + err = MVE_frGet( (MVE_frStream)handle, &pBuffer, &sw, &sh, &hicolor ); + + // refresh our palette + { + unsigned int palstart = 0; + unsigned int palcount = 0; + unsigned char *pal = NULL; + MVE_frPal( (MVE_frStream)handle, &pal, &palstart, &palcount ); + CallbackSetPalette( pal, palstart, palcount ); + } + + if( err == 0 ) + { + // blit to bitmap + int texW, texH; + BlitToMovieBitmap( pBuffer, sw, sh, hicolor, false, texW, texH ); + + if( bm_handle ) + { + *bm_handle = Movie_bm_handle; + } + + return handle; + } + + if( Movie_looping && err == MVE_ERR_EOF ) + { + MVE_frClose( (MVE_frStream)handle ); +#ifdef WIN32 + _lseek( fhandle, 0, SEEK_SET ); +#else + lseek( fhandle, 0, SEEK_SET ); +#endif + handle = (unsigned int)MVE_frOpen( CallbackFileRead, fhandle, NULL ); + sequence = true; + goto reread_frame; + } + + return (unsigned int)( -1 ); +#else + return (unsigned int)(-1); +#endif +} + +bool mve_SequenceClose( unsigned int hMovie, int hFile ) +{ +#ifndef NO_MOVIES + if( hMovie == -1 ) + return false; + + MVE_frClose( (MVE_frStream)hMovie ); + MVE_ReleaseMem(); + close( hFile ); + + // free our bitmap + if( Movie_bm_handle != -1 ) + { + bm_FreeBitmap( Movie_bm_handle ); + Movie_bm_handle = -1; + } + + // We're no longer needing this + rend_SetFrameBufferCopyState( false ); + + return true; +#else + return false; +#endif +} + +void mve_Puts( short x, short y, ddgr_color col, const char *txt ) +{ + grtext_SetFont( BRIEFING_FONT ); + grtext_SetColor( col ); + grtext_SetAlpha( 255 ); + grtext_SetFlags( GRTEXTFLAG_SHADOW ); + grtext_CenteredPrintf( 0, y, txt ); + grtext_Flush(); +} + +void mve_ClearRect( short x1, short y1, short x2, short y2 ) +{ + //Note: I can not figure out how to clear, and then write over it with text. It always covers my text! + //rend_FillRect( GR_BLACK, x1, y1, x2, y2 ); +} + +#ifndef NO_MOVIES +#ifdef WIN32 +// Internal function to enumerate sound devices +BOOL CALLBACK DSEnumCallback( LPGUID lp_guid, LPCSTR lpstr_description, LPCSTR lpstr_module, LPVOID lp_Context ) +{ + GUID FAR *lp_ret_guid = (GUID FAR *)lp_Context; + + if( SoundCardName[0] ) + { + if( strcmp( lpstr_description, SoundCardName ) == 0 ) + { + if( lp_guid ) + { + memmove( lp_ret_guid, lp_guid, sizeof(GUID) ); + } + + return FALSE; + } + } + + return TRUE; +} + + +bool mve_InitSound(oeApplication *app, MovieSoundDevice& device) +{ + // Perform Direct Sound Initialization + device.SetDirectSound( NULL ); + + GUID *pguid = NULL; + GUID card_guid, zero_card_guid; + ZeroMemory(&card_guid, sizeof(GUID)); + ZeroMemory(&zero_card_guid, sizeof(GUID)); + HRESULT hr = DirectSoundEnumerate(DSEnumCallback, &card_guid); + if (hr == DS_OK) + { + if (memcmp(&card_guid, &zero_card_guid, sizeof(GUID)) != 0) + { + pguid = &card_guid; + } + } + + LPDIRECTSOUND lpDS; + if (DirectSoundCreate(pguid, &lpDS, NULL) != DS_OK) + { + return false; + } + + HWND hWnd = (HWND)((oeWin32Application *)app)->m_hWnd; + hr = lpDS->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE); + if (hr != DS_OK) + { + lpDS->Release(); + return false; + } + + bool use_22k_sound = false; + + // Start Mixer + LPDIRECTSOUNDBUFFER lpPrimaryBuffer; + DSBUFFERDESC dsbd; + memset(&dsbd, 0, sizeof(DSBUFFERDESC)); + dsbd.dwSize = sizeof(DSBUFFERDESC); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + hr = lpDS->CreateSoundBuffer(&dsbd, &lpPrimaryBuffer, NULL); + if (hr == DS_OK) + { + // set format to 44khz if requested. + WAVEFORMATEX fmt; + fmt.cbSize = sizeof(fmt); + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = 2; + fmt.wBitsPerSample = 16; + fmt.nSamplesPerSec = use_22k_sound ? 22050 : 44100; + fmt.nBlockAlign = fmt.nChannels * (fmt.wBitsPerSample/8); + fmt.nAvgBytesPerSec = ((DWORD)fmt.nSamplesPerSec)*((DWORD)fmt.nBlockAlign); + hr = lpPrimaryBuffer->SetFormat(&fmt); + if( hr != DS_OK ) + { + lpPrimaryBuffer->Release(); + lpDS->Release(); + lpDS = NULL; + return false; + } + + hr = lpPrimaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); + if( hr != DS_OK ) + { + lpPrimaryBuffer->Release(); + lpDS->Release(); + return false; + } + + lpPrimaryBuffer->Release(); + } + else + { + lpDS->Release(); + return false; + } + + device.SetDirectSound( lpDS ); + MVE_sndInit( &device ); + + return true; +} + +void mve_CloseSound(MovieSoundDevice& device) +{ + LPDIRECTSOUND ds = device.GetDirectSound(); + if( ds ) + { + ds->Release(); + device.SetDirectSound( NULL ); + } +} +#else +bool mve_InitSound(oeApplication *app, MovieSoundDevice& device) +{ + + LnxSoundDevice snddev; + bool use_22k_sound = false; + snddev.freq = use_22k_sound ? 22050 : 44100; + snddev.bit_depth = 16; + snddev.channels = 2; + snddev.bps = snddev.freq * snddev.channels * snddev.bit_depth/8; + + device.SetDirectSound(snddev); + + MVE_sndInit( &device ); + + return true; +} + +void mve_CloseSound(MovieSoundDevice& device) +{ + // TODO: close the driver out +} +#endif + +#endif + diff --git a/music/CMakeLists.txt b/music/CMakeLists.txt new file mode 100644 index 000000000..ac0efdcc2 --- /dev/null +++ b/music/CMakeLists.txt @@ -0,0 +1,8 @@ +SET (HEADERS ) +SET (CPPS + omflex.cpp + sequencer.cpp + streamer.cpp + tracklist.cpp) + +ADD_LIBRARY(music STATIC ${HEADERS} ${CPPS}) \ No newline at end of file diff --git a/music/musiclib.h b/music/musiclib.h new file mode 100644 index 000000000..3a26f96fa --- /dev/null +++ b/music/musiclib.h @@ -0,0 +1,30 @@ + +#define OMFCMD_NUM 22 + +#define OMFCMD_STREAM 0 // associates a symbol with a stream filename +#define OMFCMD_SECTION 1 // begins a theme +#define OMFCMD_PLAY 2 // plays a sample specified through llpt and lplc +#define OMFCMD_ENDSECTION 3 // ends a theme +#define OMFCMD_LABEL 4 // defines a label for branching +#define OMFCMD_COMPARE 5 // compares a register rX to what's loaded via lcmp +#define OMFCMD_LCMP 6 // loads NUMBER into lcmp +#define OMFCMD_LLPT 7 // specifies next play will load the sample specified +#define OMFCMD_LPLC 8 // specifies next play will loop sample n times +#define OMFCMD_INCI 9 // increments 'I' counter +#define OMFCMD_SETI 10 // sets 'I' counter +#define OMFCMD_IFI 11 // if 'I' counter == n then inst1;inst2;...;inst n +#define OMFCMD_REGION 12 // specifies a start of a named region +#define OMFCMD_ENDREGION 13 // specifies the end of a named region + +#define OMFCMD_BRANCHING 15 +#define OMFCMD_BLT 15 // branch if rX < lcmp +#define OMFCMD_BGT 16 // branch if rX > lcmp +#define OMFCMD_BEQ 17 // branch if rX == lcmp +#define OMFCMD_BNIF 18 // branch if stream playing is NOT finished. +#define OMFCMD_GOTO 19 // unconditional goto a label +#define OMFCMD_WAIT 20 // tells the system to wait + + +// undocumented codes. +#define OMFCMD_MPLAY 200 // internal code. +#define OMFCMD_ENDIFI 201 // end of ifi conditional. \ No newline at end of file diff --git a/music/omflex.cpp b/music/omflex.cpp new file mode 100644 index 000000000..886042f7e --- /dev/null +++ b/music/omflex.cpp @@ -0,0 +1,410 @@ +/* + * $Logfile: /DescentIII/Main/music/omflex.cpp $ + * $Revision: 14 $ + * $Date: 4/01/99 4:50p $ + * $Author: Matt $ + * + * Read OMF file lexical analyzer. + * + * $Log: /DescentIII/Main/music/omflex.cpp $ + * + * 14 4/01/99 4:50p Matt + * Took out Warning() function, chaning those calls to mprintf() + * + * 13 2/27/99 6:52p Samir + * fixed label-region scope problem. + * + * 12 1/28/99 2:22p Samir + * simplified music system for D3. + * + * 11 12/15/98 10:22a Samir + * upped max labels. + * + * 10 12/11/98 3:28p Samir + * added start of regions + * + * 9 12/08/98 11:41a Samir + * added new region commands. + * + * 8 12/07/98 2:57p Samir + * added transitions for combat. + * + * 7 12/03/98 12:48p Samir + * speed up sequencer so that gap between samples isn't too big. + * + * 6 11/20/98 5:20p Samir + * added a bunch of high level scripting commands. + * + * 5 11/16/98 4:15p Samir + * added death. + * + * 4 11/13/98 2:27p Samir + * new music system. + * + * 3 8/10/98 5:56p Samir + * new command for end of combat music added. + * + * 2 7/28/98 5:43p Samir + * reorg of music system. + * + * 1 7/28/98 11:48a Samir + * moved from sequencer.cpp + * + * $NoKeywords: $ + */ + +#include "music.h" +#include "musiclib.h" +#include "InfFile.h" +#include "mem.h" + +#include +#include + + +// OMF INF FILE READ +#define OMFFILEERR_ADDSECTION INFFILE_CUSTOM // error adding section to list. +#define OMFFILEERR_INSOVERFLOW (INFFILE_CUSTOM+1) // too many instructions! +#define OMFFILEERR_LBLOVERFLOW (INFFILE_CUSTOM+2) // too many labels! +#define OMFFILEERR_GOTO (INFFILE_CUSTOM+3) // no goto label exists +#define OMFFILEERR_SYNTAX (INFFILE_CUSTOM+4) // syntax error. + +const char *OMFCommands[OMFCMD_NUM] = { + "stream", + "section", + "play", + "endsection", + "label", + "compare", + "lcmp", + "llpt", + "lplc", + "inci", + "seti", + "ifi", + "region", + "endregion", + "q2", + "blt", + "bgt", + "beq", + "bnif", + "goto", + "wait" +}; + + + +int OMFLex(const char *command) +{ + for (int i = 0; i < OMFCMD_NUM; i++) + { + if (strcmp(OMFCommands[i], command) == 0) + return i; + } + + + return INFFILE_ERROR; +} + + +#define ADD_NEW_INS_NUM(_cmd, _num) do { \ + if (temp_ins_idx == MAX_MUSIC_INSTRUCTIONS) {\ + cmd = OMFFILEERR_INSOVERFLOW; \ + goto force_error;\ + }\ + temp_ins_buf[temp_ins_idx].cmd = (_cmd); \ + temp_ins_buf[temp_ins_idx++].opr.num = (_num); \ +} while(0) + +#define ADD_NEW_INS_STR(_cmd, _str) do { \ + if (temp_ins_idx == MAX_MUSIC_INSTRUCTIONS) {\ + cmd = OMFFILEERR_INSOVERFLOW; \ + goto force_error;\ + }\ + temp_ins_buf[temp_ins_idx].cmd = (_cmd); \ + temp_ins_buf[temp_ins_idx++].opr.str = alloc_and_copy_str((_str)); \ +} while (0) + + + +// takes a filename containing oms data. +bool OutrageMusicSeq::LoadTheme(const char *file) +{ + const int MAX_FILE_LABELS = 256; + + InfFile inf; + char operand[INFFILE_LINELEN]; // operand + int theme_type; + music_ins temp_ins_buf[MAX_MUSIC_INSTRUCTIONS]; + int temp_ins_idx; + short cur_region; + struct { + char *name; + short index; + short region; + } labels[MAX_FILE_LABELS]; + int n_labels=0, n_relabels=0; + bool in_region =false; + +// reset list management for new theme. + m_tracklist.reset(); + m_ins_curptr = m_ins_buffer; + m_str_curptr = m_str_buffer; + +// open file + if (!file) + return false; + + if (!inf.Open(file, "[theme file]", OMFLex)) { + mprintf((0,"Unable to find requested theme %s or bad file.\n", file)); + return false; + } + + cur_region = 0; + +// perform FIRST PASS for LABELS + while (inf.ReadLine()) + { + int cmd; + bool ifi_block; + ifi_block = false; + while ((cmd = inf.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) + { + switch (cmd) + { + case OMFCMD_LABEL: + if (n_labels == MAX_FILE_LABELS) { + cmd = OMFFILEERR_LBLOVERFLOW; + goto force_pre_error; + } + labels[n_labels].index = temp_ins_idx; + labels[n_labels].region = cur_region; + labels[n_labels++].name = mem_strdup(operand); + break; + case OMFCMD_SECTION: + case OMFCMD_REGION: + temp_ins_idx = 0; + break; + case OMFCMD_ENDREGION: + cur_region++; + break; + case OMFCMD_STREAM: + break; + case OMFCMD_BNIF: + case OMFCMD_BLT: + case OMFCMD_BGT: + case OMFCMD_BEQ: + case OMFCMD_GOTO: + case OMFCMD_ENDSECTION: + case OMFCMD_WAIT: + case OMFCMD_COMPARE: + case OMFCMD_LLPT: + case OMFCMD_SETI: + case OMFCMD_LCMP: + case OMFCMD_LPLC: + case OMFCMD_INCI: + temp_ins_idx++; + break; + case OMFCMD_IFI: + temp_ins_idx++; + ifi_block = true; + break; + case OMFCMD_PLAY: + temp_ins_idx++; + temp_ins_idx++; + break; + } + } + + if (ifi_block) { + temp_ins_idx++; + } +force_pre_error: + if (cmd == INFFILE_ERROR) { + mprintf((0,"Error in music file %s line %d.\n", file, inf.line())); + Int3(); + } + else if (cmd == OMFFILEERR_LBLOVERFLOW) { + mprintf((0,"Error in music file %s line %d (too many labels).\n", file, inf.line())); + Int3(); + } + } + + inf.Close(); + +// reopen for SECOND PASS (actual code read) + if (!inf.Open(file, "[theme file]", OMFLex)) { + mprintf((0,"Unable to find requested theme %s or bad file.\n", file)); + return false; + } + + theme_type = -1; + cur_region = 0; + + while (inf.ReadLine()) + { + int cmd, i; + bool ifi_block; + ifi_block = false; + + while ((cmd = inf.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) + { + switch (cmd) + { + case OMFCMD_REGION: + if (in_region) { + cmd = OMFFILEERR_SYNTAX; + goto force_error; + } + temp_ins_idx = 0; + in_region =true; + break; + + case OMFCMD_ENDREGION: + in_region = false; + cur_region++; + break; + + case OMFCMD_STREAM: + { + char *sym = strchr(operand, '$'); // symbol AFTER '$' + if (!sym) { + cmd = INFFILE_ERROR; + goto force_error; + } + sym[0] = 0; + m_tracklist.add(operand, &sym[1]); // sym[0] was '$' now null terminator + } + break; + + case OMFCMD_LABEL: + // already taken care of. + ASSERT(labels[n_relabels].index == temp_ins_idx); + n_relabels++; + break; + + case OMFCMD_BNIF: + case OMFCMD_BLT: + case OMFCMD_BGT: + case OMFCMD_BEQ: + case OMFCMD_GOTO: + for (i = 0; i < n_labels; i++) + { + if (labels[i].region == cur_region && strcmp(operand, labels[i].name)==0) { + ADD_NEW_INS_NUM(cmd, labels[i].index); + break; + } + } + if (i == n_labels) { + cmd = OMFFILEERR_GOTO; + goto force_error; + } + break; + + case OMFCMD_SECTION: + if (!in_region) { + cmd = OMFFILEERR_SYNTAX; + goto force_error; + } + if (strcmp(operand, "intro") == 0) { + theme_type = OMS_THEME_TYPE_INTRO; + } + else if (strcmp(operand, "idle") == 0) { + theme_type = OMS_THEME_TYPE_IDLE; + } + else if (strcmp(operand, "combat") == 0) { + theme_type = OMS_THEME_TYPE_COMBAT; + } + else if (strcmp(operand, "death") == 0) { + theme_type = OMS_THEME_TYPE_DEATH; + } + else if (strcmp(operand, "idle_combat") == 0) { + theme_type = OMS_THEME_TYPE_IDLE_TO_COMBAT; + } + else if (strcmp(operand, "combat_idle") == 0) { + theme_type = OMS_THEME_TYPE_COMBAT_TO_IDLE; + } + + temp_ins_idx = 0; // reset instruction buffer + break; + + case OMFCMD_ENDSECTION: + ADD_NEW_INS_NUM(OMFCMD_ENDSECTION, 0); + if (!AddToList((short)cur_region, (short)theme_type, temp_ins_idx,temp_ins_buf)) { + cmd = OMFFILEERR_ADDSECTION; + goto force_error; + } + theme_type = -1; + break; + + case OMFCMD_PLAY: + ADD_NEW_INS_NUM(OMFCMD_PLAY, 0); + ADD_NEW_INS_NUM(OMFCMD_MPLAY, 0); + break; + + case OMFCMD_WAIT: + ADD_NEW_INS_NUM(OMFCMD_WAIT, strcmp(operand, "finished") ? 0 : 1); + break; + + case OMFCMD_COMPARE: + { + tMusicVal val; + sscanf(operand, "r%d", &val); + ADD_NEW_INS_NUM(OMFCMD_COMPARE, val); + break; + } + + case OMFCMD_LLPT: + ADD_NEW_INS_STR(OMFCMD_LLPT, operand); + break; + + case OMFCMD_SETI: + case OMFCMD_LCMP: + case OMFCMD_LPLC: + ADD_NEW_INS_NUM(cmd, (tMusicVal)atoi(operand)); + break; + + case OMFCMD_INCI: + ADD_NEW_INS_NUM(cmd, 0); + break; + + case OMFCMD_IFI: + ADD_NEW_INS_NUM(cmd, (tMusicVal)atoi(operand)); + ifi_block = true; + break; + } + } + + if (ifi_block) { + ADD_NEW_INS_NUM(OMFCMD_ENDIFI, 0); + } + +force_error: + if (cmd == INFFILE_ERROR) { + mprintf((0,"Error in music file %s line %d.\n", file, inf.line())); + Int3(); + } + else if (cmd == OMFFILEERR_ADDSECTION) { + mprintf((0,"Error in music file %s line %d (failed to add section).\n", file, inf.line())); + Int3(); + } + else if (cmd == OMFFILEERR_INSOVERFLOW) { + mprintf((0,"Error in music file %s line %d (too many instructions).\n", file, inf.line())); + Int3(); + } + } + +// free any memory + while (n_labels) + { + mem_free(labels[n_labels-1].name); + n_labels--; + } + +// close file + inf.Close(); + + return true; +} + diff --git a/music/sequencer.cpp b/music/sequencer.cpp new file mode 100644 index 000000000..483728e29 --- /dev/null +++ b/music/sequencer.cpp @@ -0,0 +1,823 @@ +/* + * $Logfile: /DescentIII/Main/music/sequencer.cpp $ + * $Revision: 35 $ + * $Date: 4/09/99 12:04p $ + * $Author: Samir $ + * + * Event driven music system + * + * $Log: /DescentIII/Main/music/sequencer.cpp $ + * + * 35 4/09/99 12:04p Samir + * take out error messages (mprintfs) for stream load failures. + * + * 34 4/01/99 4:50p Matt + * Took out Warning() function, chaning those calls to mprintf() + * + * 33 3/18/99 10:13a Samir + * msuic update. + * + * 32 3/03/99 5:08p Samir + * added slew of debug code: hopefully the library will be revamped after + * OEM. + * + * 31 3/03/99 3:43a Samir + * moved mprintfs for debugging. + * + * 30 3/02/99 1:18p Samir + * fixes to prevent bad filenames from crashing system. + * + * 29 3/01/99 5:01a Samir + * fixed bug when no pending or active song, it would still try to start a + * pending song. + * + * 28 2/28/99 6:35p Samir + * fixed streaming bugs for very small samples. + * + * 27 2/27/99 8:23p Samir + * fixes to music system to act nicely to sudden and frequent region + * changes. + * + * 26 2/27/99 6:51p Samir + * added code for music tester to display current stream and loop/region. + * + * 25 2/27/99 5:13p Nate + * Samir: fixes + * + * 24 2/27/99 4:37p Samir + * return name of loop currently playing. + * + * 23 2/26/99 5:26p Samir + * fixes to streaming audio to reflect fix in direct sound mixer. + * + * 22 2/22/99 3:23p Luke + * upped size of music buffers + * + * 21 2/16/99 12:09p Samir + * immediate switches open new stream object. + * + * 20 1/28/99 2:22p Samir + * simplified music system for D3. + * + * 19 12/13/98 6:51p Samir + * enhancements. + * + * 18 12/11/98 6:05p Samir + * fixed looping bug (didn't set loop count!) + * + * 17 12/11/98 4:02p Samir + * clean theme changes. + * + * 16 12/10/98 10:12a Samir + * uses newer streaming audio library + * + * 15 12/07/98 11:45a Samir + * hacks to get music system mostly working in DS_8. Will change this + * system so it's a lot cleaner after music demo. + * + * 14 12/03/98 12:48p Samir + * speed up sequencer so that gap between samples isn't too big. + * + * 13 11/20/98 5:20p Samir + * added a bunch of high level scripting commands. + * + * 12 11/16/98 4:15p Samir + * fadeouts. + * + * 11 11/13/98 2:27p Samir + * new music system. + * + * 10 8/10/98 5:53p Samir + * improved switching between streams and basic background/combat music. + * + * 9 7/30/98 7:33p Jeff + * fixed bug passing NULL to reset. + * + * 8 7/28/98 5:43p Samir + * reorg of music system. + * + * 7 7/24/98 5:20p Samir + * first real music checkin. + * + * 6 7/08/98 6:26p Samir + * took out obsolete code. + * + * 5 2/15/98 7:12p Chris + * More improvements to the sound lib + * + * 4 1/05/98 3:54p Chris + * Added ambient and explosion sounds + * + * 3 12/31/97 2:58a Chris + * Another major revision to the SoundLib. This cleaned up the code after + * removing the direct sound secondary buffers. + * + * 2 12/10/97 4:47p Chris + * Revision 1.0 of new sound library (no hardware -- uses primary buffer + * streaming) + * + * 3 5/29/97 3:58p Samir + * Works with some very small pauses. + * + * 2 5/23/97 4:34p Samir + * + * $NoKeywords: $ + */ + + + +#include "music.h" +#include "musiclib.h" +#include "ssl_lib.h" +#include "soundload.h" +#include "textaux.h" +#include "pserror.h" +#include "ddio.h" +#include "Macros.h" +#include "InfFile.h" +#include "streamaudio.h" +#include "mem.h" +#include +#include + +//#include "samirlog.h" +#define LOGFILE(_s) + +OutrageMusicSeq::OutrageMusicSeq() +{ + m_sequencer_run = false; + m_sequencer_init = false; + m_str_buffer = NULL; + m_ins_buffer = NULL; + m_curregion = -1; + m_pending_song = m_active_song = m_playing_song = NULL; +} + + +OutrageMusicSeq::~OutrageMusicSeq() +{ + OutrageMusicSeq::Shutdown(); +} + + +// takes a OMS theme file. +bool OutrageMusicSeq::Init(const char *theme_file) +{ +// reset streams + m_dominant_strm = 0; + m_timer = 0.0f; + + if (theme_file[0] == 0) { + return false; + } + + Stop(); + +// initialize memory buffers + m_ins_buffer = (music_ins *)mem_malloc(sizeof(music_ins)*MAX_MUSIC_INSTRUCTIONS); + m_str_buffer = (char *)mem_malloc(MAX_MUSIC_STRLEN); + m_ins_curptr = m_ins_buffer; + m_str_curptr = m_str_buffer; + m_str_curptr[0] = 0; + +// initialize music lists + m_music_list.free(); + m_tracklist.init(64); + m_pending_song = m_active_song = m_playing_song = NULL; + m_output_q.flush(); + m_curregion = -1; + + OutrageMusicSeq::SetVolume(1.0f); + m_sequencer_init = true; + + return LoadTheme(theme_file); +} + + +void OutrageMusicSeq::Shutdown() +{ + if (!m_sequencer_init) + return; + + Stop(); + +// free music lists. + FreeList(); + m_tracklist.free(); + +// free allocated buffers + mem_free(m_ins_buffer); + mem_free(m_str_buffer); + +// free streams + for (int i = 0; i < OMS_NUM_STRM; i++) + m_strm[i].Reset(this); + + m_playing_song = NULL; + m_sequencer_init = false; +} + + +// starts the sequencer (usually at the beginning of a level) +void OutrageMusicSeq::Start() +{ + if( IsSequencerRunning() || !m_sequencer_init ) + return; + + int i; + + m_sequencer_run = true; + m_dominant_strm = 0; + + m_timer = 0.0f; + + for (i = 0; i < N_MUSIC_REGS; i++) + { + m_registers[i] = 0; + } + + mprintf((0, "Music system on.\n")); +} + + +// stops the sequencer, flushes events +void OutrageMusicSeq::Stop() +{ + if( !IsSequencerRunning() ) + return; + + int i; + + mprintf((0, "Music system off.\n")); + m_sequencer_run = false; + + for (i = 0; i < OMS_NUM_STRM; i++) + { + m_strm[i].Reset(this); + } +} + + + +// set volume of sequencer +void OutrageMusicSeq::SetVolume(float vol) +{ + int i; + + ASSERT(vol >= 0.0f && vol <=1.0f); + m_mastervol = vol; + + for (i = 0; i < OMS_NUM_STRM; i++) + m_strm[i].SetVolume(m_mastervol); +} + +#define START_PENDING_SONG() do { \ + ASSERT(m_pending_song); \ + if (m_active_song) { \ + m_pending_song->strm = m_active_song->strm; \ + } \ + m_active_song = m_pending_song; \ + if (m_active_song) { \ + m_active_song->stream_idle = false; \ + m_active_song->request_stop = false; \ + } \ + m_pending_song = NULL; \ +} while (0) + + + +// runs a frame of input. +void OutrageMusicSeq::Frame(float frame_time) +{ + bool start_pending_song = false; + int i; + +// run check. + if (!m_sequencer_run) + return; + +// close all streams that aren't the dominant one and have stopped playing + for (i = 0; i < OMS_NUM_STRM; i++) + { + if ((&m_strm[i]) != (&m_strm[m_dominant_strm])) { + if (m_strm[i].m_stream.State() == STRM_STOPPED) { + m_strm[i].m_stream.Close(); + } + } + } + +// determine when to start song if a song is pending. + if (!m_active_song && m_pending_song) { + start_pending_song = true; + } + + if (m_pending_song) { + if (m_pending_song->immediate_switch == true) { + start_pending_song = true; + } + else if (m_active_song) { + if (m_active_song->stream_idle) { + // this will transfer the current song's steam to the pending one, so the + // ExecScript function can manage these streams. + start_pending_song = true; + } + else if (!m_active_song->request_stop) { + m_active_song->request_stop = true; + } + } + } + + if (start_pending_song) { + mprintf((0, "MUSIC:Starting pending song.\n")); + LOGFILE((_logfp, "MUSIC:Starting pending song.\n")); + START_PENDING_SONG(); + } + +// execute song code + ExecScript(m_active_song); + +// process streams, starting at dominant stream. +// this is vital to ensure that the dominant stream's commands are executed before any +// other streams. +// i = m_dominant_strm; +// do +// { +// m_strm[i].Process(frame_time); +// i++; +// if (i == OMS_NUM_STRM) i = 0; +// } +// while (i != m_dominant_strm); + + m_timer += frame_time; +} + + +// music kernal. +void OutrageMusicSeq::ExecScript(music_stream *strm) +{ + if (!strm) + return; + +// get command +next_ins: + music_ins *cur_ins = &strm->ins[strm->ip]; + ubyte cmd = cur_ins->cmd; + tMusicVal val = cur_ins->opr.num; + char *str = cur_ins->opr.str; + const char *name; + +// goto next instruction. + strm->ip++; + +// mprintf((0, "%d ", cmd)); + AudioStream *stream = &strm->strm->m_stream; + +// execute opcodes. +// if (strm->last_ip != strm->ip) { +// LOGFILE((_logfp, "ins=%d\n", cmd)); +// } + strm->last_ip = strm->ip; + + switch (cmd) + { + case OMFCMD_PLAY: + name = m_tracklist.get(strm->ln_reg); + if (name) { + // close this song's stream. + bool err = false; + + strm->old_strm = strm->strm; + + strm->error = false; + + if (strm->immediate_switch) { + // we start playing the current stream immediately. + stream->Close(); + m_dominant_strm = DOMINANT_STRM_ADJUST(); + // mprintf((0, "MUSIC: Starting stream with %s on channel %d.\n", name, m_dominant_strm)); + LOGFILE((_logfp, "MUSIC: Starting stream with %s on channel %d.\n", name, m_dominant_strm)); + strm->strm = &m_strm[m_dominant_strm]; + stream = &strm->strm->m_stream; + err = stream->Open(name); + } + else { + m_dominant_strm = DOMINANT_STRM_ADJUST(); + // mprintf((0, "MUSIC: Preparing stream with %s on channel %d.\n", name, m_dominant_strm)); + LOGFILE((_logfp, "MUSIC: Preparing stream with %s on channel %d.\n", name, m_dominant_strm)); + strm->strm = &m_strm[m_dominant_strm]; + stream = &strm->strm->m_stream; + err = stream->Open(name); + // stream->Open(name, STRM_OPNF_GRADUAL); + } + + // skip instructions until error is cleared. + if (!err) { + // mprintf((0, "MUSIC: Error opening stream %s on channel %d.\n", name, m_dominant_strm)); + LOGFILE((_logfp, "MUSIC: Error opening stream %s on channel %d.\n", name, m_dominant_strm)); + strm->error = true; + } + + stream->SetLoopCount(strm->p_reg); + stream->SetVolume(m_mastervol); + strm->stream_idle = false; + } + else { + mprintf((0,"OMS: Stream was not found in track list.\n")); + } + + strm->pending_loop_name = name; + break; + + case OMFCMD_MPLAY: + if (strm->immediate_switch || strm->error) { + strm->immediate_switch = false; + } + else if (!stream->IsReady()) { + strm->ip--; + break; + } + else if (strm->old_strm) { + if (strm->old_strm->m_stream.State()!=STRM_STOPPED && strm->old_strm->m_stream.State()!=STRM_INVALID) { + strm->ip--; + break; + } + } + strm->loop_name = strm->pending_loop_name; + m_playing_song = strm; + + if (!strm->error) { + strm->error = !(stream->Play()); + } + + if (!strm->error) { + // mprintf((0, "MUSIC: playing %s.\n", strm->loop_name)); + LOGFILE((_logfp, "MUSIC: playing %s.\n", strm->loop_name)); + LOGFILE((_logfp, "MUSIC: state of played stream is %d.\n", strm->loop_name, stream->State())); + } + else { + // mprintf((0, "MUSIC: Error playing %s.\n", strm->loop_name)); + LOGFILE((_logfp, "MUSIC: Error playing %s.\n", strm->loop_name)); + } + break; + + case OMFCMD_WAIT: + Int3(); // flaky, shouldn't be used anymore. + strm->ip--; + if (val == 1) { + if (strm->strm->m_valid_result && strm->strm->m_result.cmd == OMS_STRM_STOP) { + // wait till stream has finished to continue. + strm->ip++; + } + } + break; + + case OMFCMD_LCMP: + strm->c_reg = val; + break; + + case OMFCMD_LLPT: + strm->ln_reg = str; + break; + + case OMFCMD_LPLC: + strm->p_reg = val; + break; + + case OMFCMD_SETI: + strm->i_reg = val; + break; + + case OMFCMD_INCI: + strm->i_reg++; + break; + + case OMFCMD_IFI: + strm->ifi_block = (strm->i_reg == val) ? true : false; + if (!strm->ifi_block) { + // skip ifi block. + while (strm->ins[strm->ip].cmd != OMFCMD_ENDIFI) + strm->ip++; + strm->ip++; + } + break; + + case OMFCMD_ENDIFI: + strm->ifi_block = false; + break; + + case OMFCMD_COMPARE: + { + tMusicVal reg1 = strm->c_reg; + tMusicVal reg2 = m_registers[val]; + strm->b_reg = (reg2 - reg1); + break; + } + + case OMFCMD_GOTO: + // simply set IP. + strm->ip = val; + break; + + case OMFCMD_BLT: + if (strm->b_reg < 0) { + strm->ip = val; + } + break; + + case OMFCMD_BGT: + if (strm->b_reg > 0) { + strm->ip = val; + } + break; + + case OMFCMD_BEQ: + if (strm->b_reg == 0) { + strm->ip = val; + } + break; + + case OMFCMD_BNIF: + if (stream->ReadAheadFinishedLoop() || strm->error) { + oms_q_evt evt; + evt.cmd = OMS_EVT_LOOPENDING; + evt.parm.i = strm->type; + m_output_q.send(evt); + if (!strm->error) { + strm->ip--; // repeat this instruction again + } + } + else if (!strm->request_stop) { + if (stream->ReadAhead()) { + strm->ip = val; + } + else if (stream->State() != STRM_STOPPING && stream->State() != STRM_STOPPED) { + strm->ip = val; + } + } + else { + stream->Stop(true); // soft stop on end of stream. + if (strm->request_stop) { + strm->stream_idle = true; + strm->request_stop = false; + mprintf((0, "MUSIC: Processed stop request.\n")); + LOGFILE((_logfp, "MUSIC: Processed stop request.\n")); + } + } + break; + + case OMFCMD_ENDSECTION: + { + strm->ip--; + if (stream->State() == STRM_STOPPED) { + oms_q_evt evt; + strm->ip++; + evt.cmd = OMS_EVT_SONGENDED; + evt.parm.i = strm->type; + m_output_q.send(evt); + strm->stream_idle = true; + m_active_song = NULL; + m_playing_song = NULL; + } + break; + } + } + +// okay, ifi_block is only really useful to determine if we're going to execute consecutive instructions +// note that even if we turn off ifi_block, this doesn't stop instructions from running inside the ifi_block +// but it does stop consecutive instructions. +// the actual decision to run the instructions in an ifi_block depends on the initial check. + if (cmd != OMFCMD_ENDSECTION && cmd != OMFCMD_PLAY && cmd != OMFCMD_MPLAY && cmd < OMFCMD_BRANCHING) { + goto next_ins; + } + else { + strm->ifi_block = false; + } +} + + +// start a song, stopping the old either cleanly (on measure) or abruptly. +void OutrageMusicSeq::StartSong(int song, bool clean_switch) +{ + if (m_curregion >= 0) { + m_pending_song = GetSong(m_curregion, (short)song); + } + else { + m_pending_song = NULL; + } + + if (m_pending_song) { + m_pending_song->request_stop = false; + m_pending_song->immediate_switch = !clean_switch; + m_pending_song->strm = &m_strm[m_dominant_strm]; + } +} + + +// stops song, cold. +void OutrageMusicSeq::StopSong() +{ + if (m_active_song) { + m_active_song = NULL; + m_playing_song= NULL; + } + m_strm[m_dominant_strm].m_stream.Close(); +} + + +// pauses +void OutrageMusicSeq::Pause() +{ + int i; + + for(i = 0; i < OMS_NUM_STRM; i++) + { + m_strm[i].m_stream.Pause(); + } +} + + +void OutrageMusicSeq::Resume() +{ + int i; + + for(i = 0; i < OMS_NUM_STRM; i++) + { + m_strm[i].m_stream.Resume(); + } +} + + +// get and set user defined (theme file) parameters +void OutrageMusicSeq::SetRegister(int parm, tMusicVal val) +{ + ASSERT(parm >= 0 && parm < N_MUSIC_REGS); + m_registers[parm] = val; +} + + +tMusicVal OutrageMusicSeq::GetRegister(int parm) +{ + ASSERT(parm >= 0 && parm < N_MUSIC_REGS); + return m_registers[parm]; +} + + + +// memory management +OutrageMusicSeq::music_ins *OutrageMusicSeq::alloc_and_copy_ins(int len, const music_ins *ins) +{ +// copy instructions to static buffer and return a pointer to it + if ((m_ins_curptr+len) > (m_ins_buffer+MAX_MUSIC_INSTRUCTIONS)) { + Int3(); + return NULL; + } + else { + memcpy(m_ins_curptr, ins, len*sizeof(music_ins)); + m_ins_curptr += len; + return (m_ins_curptr - len); + } +} + + +char *OutrageMusicSeq::alloc_and_copy_str(const char *str) +{ +// copy string to static buffer and return a pointer to it + int slen = strlen(str)+1; + if ((m_str_curptr+slen) > (m_str_buffer+MAX_MUSIC_STRLEN)) { + Int3(); + return NULL; + } + else { + strcpy(m_str_curptr, str); + m_str_curptr += slen; + return (m_str_curptr - slen); + } +} + + +// Music list management +OutrageMusicSeq::music_stream *OutrageMusicSeq::AddToList(short region, short theme_type, int len, const music_ins *ins) +{ + tListNode *node = new tListNode; + +// create list item + node->t.region = region; + node->t.type = theme_type; + node->t.ins = alloc_and_copy_ins(len, ins); + if (!node->t.ins) { + delete node; + return NULL; + } + +// link it in. + m_music_list.link(node); + + return &node->t; +} + + +void OutrageMusicSeq::FreeList() +{ + tListNode *node; + + while (node = m_music_list.start()) + { + node = m_music_list.unlink(); + delete[] node; + } + + m_music_list.free(); +} + + +OutrageMusicSeq::music_stream *OutrageMusicSeq::GetSong(short region, short theme_type) +{ + tListNode *node = m_music_list.start(); + music_stream *strm = NULL; + int i; + + for (i=0; i < m_music_list.length(); node = m_music_list.next(),i++) + { + if (region == node->t.region) { + if (theme_type == node->t.type) { + strm = &node->t; + } + + if (strm) { + strm->request_stop = false; + strm->immediate_switch = false; + strm->ip = 0; + strm->b_reg = 0; + strm->c_reg = 0; + strm->ln_reg = 0; + strm->p_reg = 0; + strm->i_reg = 0; + strm->ifi_block = false; + strm->stream_idle = true; + strm->strm = NULL; + strm->old_strm = NULL; + strm->loop_name = NULL; + strm->pending_loop_name = NULL; + strm->last_ip = -1; + strm->error = false; + break; + } + } + } + + return strm; +} + + +//@@void OutrageMusicSeq::seqSilence() +//@@{ +//@@// silence all streams. +//@@ int init_strm = m_dominant_strm; +//@@ do +//@@ { +//@@ // request switch to dominant stream when measure is complete. +//@@ m_strm[m_dominant_strm].SEND_STRM_FADEOUT(4.0f); +//@@ m_strm[m_dominant_strm].SEND_STRM_STOP(); +//@@ m_strm[m_dominant_strm].SEND_STRM_FREE(); +//@@ m_dominant_strm = DOMINANT_STRM_ADJUST(); +//@@ } +//@@ while (init_strm != m_dominant_strm); +//@@} + + + +void OutrageMusicSeq::SetCurrentRegion(short region) +{ + m_curregion = region; +} + + +short OutrageMusicSeq::GetCurrentRegion() const +{ + return m_curregion; +} + + +// get current loop playing +const char *OutrageMusicSeq::GetCurrentLoopName(int *count) +{ + if (m_playing_song) { + *count = m_playing_song->strm->m_stream.GetLoopCount(); + return m_playing_song->loop_name; + } + + *count = 0; + return NULL; +} + + + +// gets current region PLAYING, not PENDING like above. +short OutrageMusicSeq::GetPlayingRegion() const +{ + if (m_playing_song) { + return m_playing_song->region; + } + + return -1; +} diff --git a/music/streamer.cpp b/music/streamer.cpp new file mode 100644 index 000000000..6a0d2ad4f --- /dev/null +++ b/music/streamer.cpp @@ -0,0 +1,354 @@ +/* + * $Logfile: /DescentIII/Main/music/streamer.cpp $ + * $Revision: 10 $ + * $Date: 4/01/99 4:50p $ + * $Author: Matt $ + * + * Stream interface with sequencer. + * + * $Log: /DescentIII/Main/music/streamer.cpp $ + * + * 10 4/01/99 4:50p Matt + * Took out Warning() function, chaning those calls to mprintf() + * + * 9 2/26/99 5:26p Samir + * fixes to streaming audio to reflect fix in direct sound mixer. + * + * 8 12/10/98 10:12a Samir + * uses newer streaming audio library + * + * 7 12/07/98 11:45a Samir + * hacks to get music system mostly working in DS_8. Will change this + * system so it's a lot cleaner after music demo. + * + * 6 12/03/98 12:48p Samir + * speed up sequencer so that gap between samples isn't too big. + * + * 5 11/20/98 5:21p Samir + * added SEND_STRM_NEXT + * + * 4 11/13/98 2:27p Samir + * new music system. + * + * 3 8/10/98 5:53p Samir + * improved switching between streams and basic background/combat music. + * + * 2 7/28/98 5:43p Samir + * reorg of music system. + * + * 1 7/28/98 12:47p Samir + * moved and revamped from sequencer.cpp + * + * $NoKeywords: $ + */ + +#include "music.h" +#include "streamaudio.h" + + +oms_stream::oms_stream() +{ +} + + +oms_stream::~oms_stream() +{ +} + + +// processes a stream's events. +void oms_stream::Process(float frmtime) +{ + oms_q_evt evt,evt2; // event for use + + if (m_timer > 0.0f) + m_timer -= frmtime; + +// invalidate current process result. + m_valid_result = false; + + if (!processCommand()) + return; + +// process queue events. + if (!m_q.recv(&evt)) + return; + + switch (evt.cmd) + { + case OMS_STRM_LOAD: + processQLoad((const char *)evt.parm.p); + break; + + case OMS_STRM_FADEOUT: + // reset timer for fadeout to 0 volume + STREAM_COMMANDI(OMS_STRM_FADEOUT); + STREAM_TIMER(evt.parm.f); + break; + + case OMS_STRM_FADEIN: + // fade in to volume m_maxvol + STREAM_COMMANDI(OMS_STRM_FADEIN); + STREAM_TIMER(evt.parm.f); + break; + + case OMS_STRM_STOP: + STREAM_COMMANDI(OMS_STRM_STOP); + m_stream.Stop(); + break; + + case OMS_STRM_FREE: + STREAM_COMMANDI(OMS_STRM_FREE); + m_stream.Close(); + break; + + case OMS_STRM_PLAY: + // process queue events. + if (!m_q.recv(&evt2)) { + Int3(); + } + STREAM_COMMANDI(OMS_STRM_PLAY, evt2.parm.i); + m_stream.SetLoopCount(evt2.parm.i); + m_stream.SetVolume(evt.parm.f); + m_stream.Play(); + break; + + case OMS_STRM_SWITCH: + STREAM_COMMANDP(OMS_STRM_SWITCH, evt.parm.p); + m_stream.Stop(true, &m_data.i); + mprintf((0, "%d-%d ",m_data.i, m_stream.State())); + break; + + case OMS_STRM_NEXT: +// m_stream.Next((const char *)evt.parm.p); + if (!m_q.recv(&evt2)) { + Int3(); + } + m_data.f = evt2.parm.f; + if (!m_q.recv(&evt2)) { + Int3(); + } + m_data2.i = evt2.parm.i; + if (!m_q.recv(&evt2)) { + Int3(); + } + STREAM_COMMANDP(OMS_STRM_NEXT, evt2.parm.p); + *(bool *)(evt2.parm.p) = true; + break; + } +} + + +// sends an event to the stream. +void oms_stream::Send(oms_q_evt *evt) +{ + m_q.send(*evt); +} + + +// reset stream. +void oms_stream::Reset(OutrageMusicSeq *seq) +{ + m_timer = 0.0f; + m_timer_init = 0.0f; + m_maxvol = 1.0f; + m_status.cmd = OMS_STRM_FREE; + m_seq = seq; + m_q.flush(); + m_stream.Close(); +} + + + +// volume. +void oms_stream::SetVolume(float vol) +{ + m_maxvol = vol; + m_stream.SetVolume(vol); +} + + +// processes current command on stream, return false to end processing. +bool oms_stream::processCommand() +{ + switch (m_status.cmd) + { + case OMS_STRM_FADEIN: + // timed fadein to dest volume + if (m_timer <= 0.0f) { + m_stream.SetVolume(m_maxvol); + STREAM_COMMANDI(OMS_STRM_PLAY); + } + else { + m_stream.SetVolume(m_maxvol * (m_timer_init-m_timer)/m_timer_init); + } + return false; + + case OMS_STRM_FADEOUT: + // timed fadeout to dest volume + if (m_timer <= 0.0f) { + m_stream.SetVolume(0.0f); + STREAM_COMMANDI(OMS_STRM_PLAY); + } + else { + m_stream.SetVolume(m_maxvol * m_timer/m_timer_init); + } + return false; + + case OMS_STRM_SWITCH: + // if current measure is greater than initial measure at switch, then we will stop + // this stream and send a play command to the current dominant stream. + // mprintf((0, "%d-%d ",m_data.i, m_stream.State())); + if (m_data.i || m_stream.State() == STRM_STOPPED || m_stream.State() == STRM_INVALID) { + bool *still_playing = (bool *)m_status.parm.p; + *still_playing = false; + if (m_status.cmd != OMS_STRM_STOP) { + STREAM_COMMANDI(OMS_STRM_STOP); + m_valid_result = true; + m_result.cmd = OMS_STRM_STOP; + } + } + return false; + + case OMS_STRM_PLAY: + // check if stream is still playing. if not, then set current stream status and signal + // a normal song end. + if (m_stream.State() == STRM_STOPPED) { + if (m_status.cmd != OMS_STRM_STOP) { + STREAM_COMMANDI(OMS_STRM_STOP); + m_valid_result = true; + m_result.cmd = OMS_STRM_STOP; + return false; + } + } + break; + + case OMS_STRM_NEXT: +// if (m_stream.HasNextStarted()) { +// *(bool *)(m_status.parm.p) = false; +// m_stream.SetLoopCount(m_data2.i); +// m_stream.SetVolume(m_data.f); +// STREAM_COMMANDI(OMS_STRM_PLAY, m_data2.i); +// return false; +// } +// else { +// *(bool *)(m_status.parm.p) = true; +// } + break; + } + + return true; +} + + +// processes load song q event. +void oms_stream::processQLoad(const char *fname) +{ + m_stream.Close(); + + if (m_stream.Open(fname)) { + STREAM_COMMANDP(OMS_STRM_LOAD, (void *)fname); + m_data.p = (void *)fname; + } + else { + mprintf((0,"OMS: Couldn't load song %s.\n", fname)); + + STREAM_COMMANDI(OMS_STRM_FREE); + m_valid_result = true; + m_result.cmd = OMS_EVT_SONGENDED; + m_result.parm.p = (void *)fname; + } +} + + +// THESE FUNCTIONS SEND EVENTS TO THE STREAM EASILY + +void oms_stream::SEND_STRM_LOAD(const char *fname) +{ + oms_q_evt evt; + + evt.cmd = OMS_STRM_LOAD; + evt.parm.p = (void *)fname; + m_q.send(evt); +} + + +void oms_stream::SEND_STRM_FADEOUT(float time) +{ + oms_q_evt evt; + + evt.cmd = OMS_STRM_FADEOUT; + evt.parm.f = time; + m_q.send(evt); +} + + +void oms_stream::SEND_STRM_FADEIN(float time) +{ + oms_q_evt evt; + + evt.cmd = OMS_STRM_FADEIN; + evt.parm.f = time; + m_q.send(evt); +} + + +void oms_stream::SEND_STRM_STOP() +{ + oms_q_evt evt; + + evt.cmd = OMS_STRM_STOP; + m_q.send(evt); +} + + +void oms_stream::SEND_STRM_PLAY(float vol, ubyte count) +{ + oms_q_evt evt; + + evt.cmd = OMS_STRM_PLAY; + evt.parm.f = vol; + m_q.send(evt); + evt.cmd = OMS_STRM_PARM; + evt.parm.i = (int)count; + m_q.send(evt); +} + + +void oms_stream::SEND_STRM_FREE() +{ + oms_q_evt evt; + + evt.cmd = OMS_STRM_FREE; + m_q.send(evt); +} + + +void oms_stream::SEND_STRM_SWITCH(bool *switch_flag) +{ + oms_q_evt evt; + evt.cmd = OMS_STRM_SWITCH; + evt.parm.p = (void *)switch_flag; + m_q.send(evt); +} + + +void oms_stream::SEND_STRM_NEXT(const char *fname, float vol, ubyte count, bool *switch_flag) +{ + oms_q_evt evt; + + evt.cmd = OMS_STRM_NEXT; + evt.parm.p = (void *)fname; + m_q.send(evt); + evt.cmd = OMS_STRM_PARM; + evt.parm.f = vol; + m_q.send(evt); + evt.cmd = OMS_STRM_PARM; + evt.parm.i = (int)count; + m_q.send(evt); + evt.cmd = OMS_STRM_PARM; + evt.parm.p = (void *)switch_flag; + m_q.send(evt); +} + + diff --git a/music/tracklist.cpp b/music/tracklist.cpp new file mode 100644 index 000000000..150b1f2d9 --- /dev/null +++ b/music/tracklist.cpp @@ -0,0 +1,125 @@ +/* + * $Source: $ + * $Revision: 4 $ + * $Author: Jeff $ + * $Date: 3/15/99 4:32p $ + * + * + * + * $Log: /DescentIII/Main/music/tracklist.cpp $ + * + * 4 3/15/99 4:32p Jeff + * fixed code so mem library compiles correctly + * + * 3 12/11/98 4:03p Samir + * error checking. + * + * 2 11/13/98 2:27p Samir + * created. + * + */ + +#include "music.h" +#include "mem.h" +#include "pserror.h" + +#include + +////////////////////////////////////////////////////////////////////////////// + +oms_tracklist::oms_tracklist() +{ + m_fnamelist = NULL; + m_symlist = NULL; + m_numtracks = 0; + m_maxtracks = 0; + m_init = false; +} + + +oms_tracklist::~oms_tracklist() +{ + oms_tracklist::free(); +} + + +// initializes track list system +void oms_tracklist::init(short maxtracks) +{ + if (!m_init) { + m_maxtracks = maxtracks; + m_numtracks = 0; + + m_fnamelist = new char*[m_maxtracks]; + m_symlist = new char*[m_maxtracks]; + m_init = true; + } +} + + +// frees track list system +void oms_tracklist::free() +{ + if (m_init) { + oms_tracklist::reset(); + delete[] m_symlist; + delete[] m_fnamelist; + + m_init = false; + } +} + + +// resets track list to 0 +void oms_tracklist::reset() +{ + int i; + + if (!m_init) + return; + + for (i = 0; i < m_numtracks; i++) + { + ::mem_free(m_fnamelist[i]); + ::mem_free(m_symlist[i]); + } + + m_numtracks = 0; +} + + +// adds a track to list. +bool oms_tracklist::add(const char *fname, const char *sym) +{ + if (!m_init) + return false; + + if (m_numtracks == m_maxtracks) { + Int3(); + return false; + } + +// add symbol and track name + m_fnamelist[m_numtracks] = mem_strdup(fname); + m_symlist[m_numtracks] = mem_strdup(sym); + m_numtracks++; + + return true; +} + + +// returns a track filename. +const char *oms_tracklist::get(const char *sym) +{ + int i; + + if (sym) { + for (i = 0; i < m_numtracks; i++) + { + if (strcmp(m_symlist[i],sym)==0) + return m_fnamelist[i]; + } + } + + return NULL; +} diff --git a/netcon/CMakeLists.txt b/netcon/CMakeLists.txt new file mode 100644 index 000000000..ee98060d3 --- /dev/null +++ b/netcon/CMakeLists.txt @@ -0,0 +1,3 @@ +include_directories("includes") +ADD_SUBDIRECTORY(inetfile) +ADD_SUBDIRECTORY(lanclient) \ No newline at end of file diff --git a/netcon/includes/CFtp.h b/netcon/includes/CFtp.h new file mode 100644 index 000000000..fabaf9d02 --- /dev/null +++ b/netcon/includes/CFtp.h @@ -0,0 +1,115 @@ +/* +* $Logfile: /DescentIII/Main/lib/CFtp.h $ +* $Revision: 1.3 $ +* $Date: 2004/02/09 04:14:51 $ +* $Author: kevinb $ +* +* FTP Client class (get only) +* +* $Log: CFtp.h,v $ +* Revision 1.3 2004/02/09 04:14:51 kevinb +* Added newlines to all headers to reduce number of warnings printed +* +* Made some small changes to get everything compiling. +* +* All Ready to merge the 1.5 tree. +* +* Revision 1.2 2000/06/03 14:30:33 icculus +* 1.4 code merge and pthread->SDL thread conversion. +* +* Revision 1.1.1.1 2000/04/18 00:00:38 icculus +* initial checkin +* + * + * 4 8/21/99 6:48a Jeff + * Linux port + * + * 3 7/31/98 11:40a Kevin + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/27/98 9:54a Kevin + * + * 1 5/25/98 5:31p Kevin + * Initial version +* +* $NoKeywords: $ +*/ +#ifndef _CFTP_HEADER_ +#define _CFTP_HEADER_ + +#define FTP_STATE_INTERNAL_ERROR 0 +#define FTP_STATE_SOCKET_ERROR 1 +#define FTP_STATE_URL_PARSING_ERROR 2 +#define FTP_STATE_CONNECTING 3 +#define FTP_STATE_HOST_NOT_FOUND 4 +#define FTP_STATE_CANT_CONNECT 5 +#define FTP_STATE_LOGGING_IN 6 +#define FTP_STATE_LOGIN_ERROR 7 +#define FTP_STATE_LOGGED_IN 8 +#define FTP_STATE_DIRECTORY_INVALID 9 +#define FTP_STATE_FILE_NOT_FOUND 10 +#define FTP_STATE_RECEIVING 11 +#define FTP_STATE_FILE_RECEIVED 12 +#define FTP_STATE_UNKNOWN_ERROR 13 +#define FTP_STATE_RECV_FAILED 14 +#define FTP_STATE_CANT_WRITE_FILE 15 +#define FTP_STATE_STARTUP 16 + +#ifdef __LINUX__ +extern int FTPObjThread( void * obj ); +#else +extern void FTPObjThread( void * obj ); +#endif + +class CFtpGet +{ + +public: + CFtpGet(char *URL,char *localfile,char *Username = NULL,char *Password = NULL); + ~CFtpGet(); + int GetStatus(); + unsigned int GetBytesIn(); + unsigned int GetTotalBytes(); + void AbortGet(); + + void WorkerThread(); + +protected: + + int ConnectControlSocket(); + int LoginHost(); + unsigned int SendFTPCommand(char *command); + unsigned int ReadFTPServerReply(); + unsigned int GetFile(); + unsigned int IssuePort(); + unsigned int ReadDataChannel(); + void FlushControlChannel(); + + unsigned int m_iBytesIn; + unsigned int m_iBytesTotal; + unsigned int m_State; + + bool m_Aborting; + bool m_Aborted; + + char m_szUserName[100]; + char m_szPassword[100]; + char m_szHost[200]; + char m_szDir[200]; + char m_szFilename[100]; + + char recv_buffer[1000]; + + SOCKET m_ListenSock; + SOCKET m_DataSock; + SOCKET m_ControlSock; + + FILE *LOCALFILE; +}; + + + +#endif + diff --git a/netcon/includes/Chttpget.h b/netcon/includes/Chttpget.h new file mode 100644 index 000000000..d81d779ae --- /dev/null +++ b/netcon/includes/Chttpget.h @@ -0,0 +1,119 @@ +/* +* $Logfile: /DescentIII/Main/lib/Chttpget.h $ +* $Revision: 1.2 $ +* $Date: 2004/02/09 04:14:51 $ +* $Author: kevinb $ +* +* HTTP Client class (get only) +* +* $Log: Chttpget.h,v $ +* Revision 1.2 2004/02/09 04:14:51 kevinb +* Added newlines to all headers to reduce number of warnings printed +* +* Made some small changes to get everything compiling. +* +* All Ready to merge the 1.5 tree. +* +* Revision 1.1.1.1 2000/04/18 00:00:38 icculus +* initial checkin +* + * + * 7 8/21/99 9:14p Kevin + * Added support for redirection + * + * 6 8/21/99 6:48a Jeff + * Linux port + * + * 5 8/20/99 3:01p Kevin + * Added support for Proxies (I hope!) + * + * 4 7/31/98 12:19p Nate + * Fixed http abort problem. + * + * 3 7/31/98 11:57a Kevin + * Added new functions for getting state + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/27/98 9:54a Kevin + * + * 1 5/25/98 5:31p Kevin + * Initial version +* +* $NoKeywords: $ +*/ +#ifndef _CHTTPGET_HEADER_ +#define _CHTTPGET_HEADER_ + +#define HTTP_STATE_INTERNAL_ERROR 0 +#define HTTP_STATE_SOCKET_ERROR 1 +#define HTTP_STATE_URL_PARSING_ERROR 2 +#define HTTP_STATE_CONNECTING 3 +#define HTTP_STATE_HOST_NOT_FOUND 4 +#define HTTP_STATE_CANT_CONNECT 5 +#define HTTP_STATE_CONNECTED 6 +#define HTTP_STATE_FILE_NOT_FOUND 10 +#define HTTP_STATE_RECEIVING 11 +#define HTTP_STATE_FILE_RECEIVED 12 +#define HTTP_STATE_UNKNOWN_ERROR 13 +#define HTTP_STATE_RECV_FAILED 14 +#define HTTP_STATE_CANT_WRITE_FILE 15 +#define HTTP_STATE_STARTUP 16 + +#define MAX_URL_LEN 300 + +class ChttpGet +{ +public: + ChttpGet(char *URL,char *localfile); + ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport); + void PrepSocket(char *URL); + ~ChttpGet(); + void GetFile(char *URL,char *localfile); + int GetStatus(); + unsigned int GetBytesIn(); + unsigned int GetTotalBytes(); + void AbortGet(); + void WorkerThread(); + bool m_Aborted; + +protected: + int ConnectSocket(); + char *GetHTTPLine(); + unsigned int ReadDataChannel(); + unsigned int m_iBytesIn; + unsigned int m_iBytesTotal; + unsigned int m_State; + bool m_ProxyEnabled; + char *m_ProxyIP; + char m_URL[MAX_URL_LEN]; + unsigned short m_ProxyPort; + + char m_szUserName[100]; + char m_szPassword[100]; + char m_szHost[200]; + char m_szDir[200]; + char m_szFilename[100]; + + bool m_Aborting; + + + SOCKET m_DataSock; + + FILE *LOCALFILE; + char recv_buffer[1000]; + +}; + + + + + + + + + + +#endif + diff --git a/netcon/includes/DLLUiItems.h b/netcon/includes/DLLUiItems.h new file mode 100644 index 000000000..7b084f38c --- /dev/null +++ b/netcon/includes/DLLUiItems.h @@ -0,0 +1,71 @@ +/* +* $Logfile: /DescentIII/main/DLLUiItems.h $ +* $Revision: 1.3 $ +* $Date: 2004/02/09 04:14:49 $ +* $Author: kevinb $ +* +* UI helper classes for the DLLs, make sure whatever file you include this in has the following line +* int DLLUIClass_CurrID = 0xE0; +* +* $Log: DLLUiItems.h,v $ +* Revision 1.3 2004/02/09 04:14:49 kevinb +* Added newlines to all headers to reduce number of warnings printed +* +* Made some small changes to get everything compiling. +* +* All Ready to merge the 1.5 tree. +* +* Revision 1.2 2000/05/29 05:03:22 icculus +* Moved class implementation to DLLUiItems.cpp, since statically +* linking the network clients would result in duplicate definitions. +* +* Revision 1.1.1.1 2000/04/18 00:00:30 icculus +* initial checkin +* + * + * 3 10/08/98 4:23p Kevin + * Changed code to comply with memory library usage. Always use mem_malloc + * , mem_free and mem_strdup + * + * 2 8/07/98 12:39p Jeff + * initial creation +* +* $NoKeywords: $ +*/ + +extern int DLLUIClass_CurrID; + + +class CheckBoxItem +{ +public: + CheckBoxItem(void *window,char *title,bool initial_state,int x,int y,int flags); + ~CheckBoxItem(); + void SetState(bool checked); + bool GetState(void); + bool Update(int id); +private: + + char *m_Title; + bool m_State; + int m_x,m_y,m_flags; + void *m_textitem_i_off; + void *m_textitem_o_off; + void *m_textitem_i_on; + void *m_textitem_o_on; + void *m_hotspot; + void *m_hWnd; + int m_ID; +}; + + +#ifndef DLLUICLASS_H_ +#define DLLUICLASS_H_ + +#define JEFF_RED GR_RGB(255,40,40) +#define JEFF_BLUE GR_RGB(40,40,255) +#define JEFF_GREEN GR_RGB(40,255,40) + +#endif + + diff --git a/netcon/includes/con_dll.h b/netcon/includes/con_dll.h new file mode 100644 index 000000000..2572c775e --- /dev/null +++ b/netcon/includes/con_dll.h @@ -0,0 +1,1397 @@ + +/* +* $Logfile: /DescentIII/Main/lib/con_dll.h $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:58:12 $ +* $Author: kevinb $ +* +* Common header for connection DLLs. +* +* $Log: con_dll.h,v $ +* Revision 1.1.1.1 2003/08/26 03:58:12 kevinb +* initial 1.5 import +* + * + * 78 5/09/00 5:11p Jeff + * fixed struct packing bug + * + * 77 3/26/00 10:30p Kevin + * MOD Downloader for 1.4 patch. + * + * 75 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 74 8/02/99 11:47a Kevin + * don't display the d3 single mission twice! + * + * 73 7/12/99 4:15p Kevin + * Changed the way we determine if we should report stats or not in PXO + * + * 72 7/06/99 5:52p Kevin + * PXO & multiplayer fixes for the patch + * + * 71 6/23/99 3:35p Samir + * added pstypes.h + * + * 70 6/03/99 8:48a Kevin + * fixes for new OEM version.... + * + * 69 5/24/99 4:21p Jason + * fixed difficultly level problem + * + * 68 4/28/99 6:39p Kevin + * Build 182 fixes + * + * 67 4/26/99 2:54p Kevin + * fixed team count & keyword system to work with dedicated server + * + * 66 4/25/99 5:02p Kevin + * Bunches of multiplayer UI improvements + * + * 65 4/25/99 12:11a Kevin + * game info dialog stuff + * + * 64 4/23/99 5:32p Kevin + * Fixed a few mission bugs. + * + * 63 4/23/99 3:33p Kevin + * mission file/multiplayer mod keyword system + * + * 62 4/19/99 7:56p Kevin + * ifdef'd out some win32 specific stuff + * + * 61 4/19/99 6:10p Kevin + * Demo now only has one multiplayer level + * + * 60 4/18/99 3:12p Jeff + * got working with linux + * + * 59 4/16/99 3:17p Kevin + * More mouselook support + * + * 58 4/14/99 3:06p Kevin + * Force main mission to list even on small installations + * + * 57 4/08/99 4:54p Kevin + * Display level warp dialog for multiplayer + * + * 56 4/08/99 3:42p Kevin + * Added some stuff for the scoring API. Not functional yet. + * + * 55 4/03/99 9:26p Jeff + * changed dialogs that weren't using UID_OK and UID_CANCEL to use and + * handle them properly + * + * 54 3/25/99 3:29p Jason + * added option to randomize powerup respawn points + * + * 53 3/25/99 3:26p Kevin + * Made PXO games be based on your chat channel + * + * 52 3/24/99 1:41p Jeff + * some dedicated server fixups...ability to set number of teams + * + * 51 3/17/99 4:08p Kevin + * Changed the way games appear and timeout in the game list. + * + * 50 3/04/99 6:11p Kevin + * Fixed mission name for OEM + * + * 49 3/02/99 5:50p Kevin + * Ouch. Duplicate structures existed and were conflicting. + * + * 48 3/02/99 1:18a Kevin + * + * 47 3/02/99 1:11a Kevin + * + * 46 3/01/99 9:37p Jeff + * fixed missing semicolon + * + * 45 3/01/99 9:03p Kevin + * OEM Beta 4 + * + * 44 2/28/99 11:04p Jeff + * handle Capture The Flag->CTF in Demo/OEM + * + * 43 2/19/99 5:21p Kevin + * Fixed some connection DLLs and a Direct Sound bug with threads. + * + * 42 2/15/99 7:47p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 41 2/04/99 11:40a Kevin + * Made listboxes sort + * + * 40 2/03/99 6:14p Kevin + * OEM changes + * + * 39 2/03/99 4:20p Kevin + * Got multiplayer working with .mn3 files, and setup autodownloading + * + * 38 1/11/99 12:29p Jeff + * changes made not to call the module library directly + * + * 37 1/07/99 11:51a Kevin + * Added support for joining servers on alternate ports and hosting behind + * a proxy/firewall + * + * 36 12/30/98 3:49p Kevin + * Moved multiplayer options out of DLL and into main app + * + * 35 12/30/98 12:16p Kevin + * Auto Mission Download system + * + * 34 12/23/98 6:38p Kevin + * All UDP data (except gamespy) now uses one (registered) port number + * + * 33 12/18/98 11:00a Jason + * + * 32 12/15/98 10:53a Jason + * yet more changes for 1.1 + * + * 31 12/14/98 10:53a Jason + * added bright player ships option + * + * 30 12/01/98 12:47p Jason + * got rid of NF_DROPMISORDERED and added NF_USE_SMOOTHING + * + * 29 11/18/98 3:26p Kevin + * Put multiplayer options into con_dll.h + * + * 28 10/19/98 11:07p Kevin + * fixed bug + * + * 27 10/19/98 7:51p Kevin + * performance testing + * + * 26 10/19/98 1:08p Kevin + * Removed single player level from demo build + * + * 25 10/15/98 1:36p Jeff + * updated dllinfo struct + * + * 24 10/13/98 12:03p Kevin + * Changed use of preprocessors for debug, etc. + * + * 23 10/12/98 8:39p Kevin + * removed mprintf's and fixed some smallish bugs + * + * 22 10/08/98 12:05p Kevin + * Moved some start menu stuff around for Luke + * + * 21 10/07/98 11:45a Kevin + * changed demo mission name + * + * 20 10/06/98 11:00a Kevin + * Updated mission names for demo + * + * 19 10/01/98 11:37a Kevin + * UI fixes and stuff + * + * 18 9/29/98 2:23p Kevin + * More UI tweaks + * + * 17 9/28/98 9:53a Kevin + * Fixing misc UI problems, and fixed some bugs that VC 6 found + * + * 16 9/24/98 12:50p Kevin + * Added UI for rotational velocity and drop out of order packets in net + * games + * + * 15 9/23/98 6:33p Kevin + * Fixed load settings + * + * 14 9/23/98 2:55p Kevin + * Saved multi config and changed UI to conform + * + * 13 9/22/98 3:55p Kevin + * Removed obsolete function + * + * 12 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 11 9/21/98 11:19a Kevin + * check protocol before entering multiplayer screens + * + * 10 9/16/98 8:06p Jason + * got mastertracker working with the dedicated server + * + * 9 9/04/98 1:51p Kevin + * implemented asyncronous gethostbyname + * + * 8 9/02/98 6:54p Kevin + * Fixed general directplay support up, and got modem-modem working + * + * 7 8/31/98 10:14a Kevin + * Misc. multi-UI fixes + * + * 6 8/27/98 5:03p Kevin + * Prettied up multiplayer screens and fixed some bugs. + * + * 5 8/25/98 6:33p Kevin + * pxo + * + * 4 8/24/98 5:04p Kevin + * Made msn files have the option to not be playable in multiplayer + * + * 3 8/24/98 10:42a Kevin + * Updated DLL for directplay and PXO background + * + * 2 8/19/98 11:50a Kevin + * Got DirectPlay IPX working, and localized connection DLLs +* +* $NoKeywords: $ +*/ +/////////////////////////////////////////////// + +#include "ship.h" +#include "pstypes.h" + +#ifdef __LINUX__ +#include +#include "linux/linux_fix.h" +#endif + +//Uncomment out this line of code to build the demo version of the multiplayer connection dlls +//#define DEMO 1 + +//localization info +char **StringTable; +int StringTableSize = 0; +char *_ErrorString = "Missing String"; +char *GetString(int d){if( (d<0) || (d>=StringTableSize) ) return _ErrorString; else return StringTable[d];} +#define TXT(d) GetString(d) +/////////////////////////////////////////////// +#define CHAR_LEFT_ARROW 24 +#define CHAR_UP_ARROW 25 +#define CHAR_RIGHT_ARROW 26 +#define CHAR_DOWN_ARROW 27 +#define CHAR_CHECKBOX_OFF 28 +#define CHAR_CHECKBOX_ON 29 +#define CHAR_RADIO_OFF 30 +#define CHAR_RADIO_ON 31 + +#define DLL_UI_CANCEL 99 + +#define DLL_BRIEFING_FONT 1 +#define DLL_BIG_BRIEFING_FONT 2 + + + +typedef struct +{ + int me_handle; + int it_handle; + ubyte *special_data; + char *input_string; + int input_key; + union{ + int iRet; + float fRet; + }; + float fParam; + int iParam; +} dllinfo; + +typedef struct +{ + int *objs; + int *rooms; + int *terrain; + int *players; + int *netgame; + int *netplayers; + int *ships; + int *fp[200]; // function pointers + int *vp[200]; // variable pointers +} multi_api; + +typedef void( *GetMultiAPI_fp) (multi_api *api); +GetMultiAPI_fp DLLGetMultiAPI; + +typedef void( *SetUITextItemText_fp ) (void *uit,char *newtext,unsigned int color); +SetUITextItemText_fp DLLSetUITextItemText; + +typedef void *( *NewUIWindowCreate_fp ) (int x, int y, int w, int h, int flags); +NewUIWindowCreate_fp DLLNewUIWindowCreate; + +typedef void( *NewUIWindowDestroy_fp ) (void *deswin); +NewUIWindowDestroy_fp DLLNewUIWindowDestroy; + +typedef void( *NewUIWindowOpen_fp ) (void *deswin); +NewUIWindowOpen_fp DLLNewUIWindowOpen; + +typedef void( *NewUIWindowClose_fp ) (void *deswin); +NewUIWindowClose_fp DLLNewUIWindowClose; + +typedef void *( *TextCreate_fp ) (void *parentwin,void * textitem, int x, int y, int flags); +TextCreate_fp DLLTextCreate; + +typedef void *( *EditCreate_fp ) (void *parentwin, int id, int x, int y, int w, int h, int flags); +EditCreate_fp DLLEditCreate; + +typedef void *( *ButtonCreate_fp ) (void *parentwin, int id,void * titleitem, int x, int y, int w, int h, int flags); +ButtonCreate_fp DLLButtonCreate; + +typedef void *( *ListCreate_fp ) (void *parentwin, int id, int x, int y, int w, int h, int flags); +ListCreate_fp DLLListCreate; + +typedef void( *ListRemoveAll_fp ) (void *item); +ListRemoveAll_fp DLLListRemoveAll; + +typedef void( *ListAddItem_fp ) (void *item,void * uitext); +ListAddItem_fp DLLListAddItem; + +typedef void( *ListRemoveItem_fp ) (void * item,void * txtitem); +ListRemoveItem_fp DLLListRemoveItem; + +typedef void( *ListSelectItem_fp ) (void * item,void * txtitem); +ListSelectItem_fp DLLListSelectItem; + +typedef char *( *ListGetItem_fp ) (void * item,int index); +ListGetItem_fp DLLListGetItem; + +typedef int( *ListGetSelectedIndex_fp ) (void * item); +ListGetSelectedIndex_fp DLLListGetSelectedIndex; + +typedef void( *EditSetText_fp ) (void * item,char *buff); +EditSetText_fp DLLEditSetText; + +typedef void( *EditGetText_fp ) (void * item,char *buff,int len); +EditGetText_fp DLLEditGetText; + +typedef void( *DatabaseWrite_fp) (const char *label, const char *entry, int entrylen); +DatabaseWrite_fp DLLDatabaseWrite; + +typedef void( *DatabaseRead_fp) (const char *label, char *entry, int *entrylen); +DatabaseRead_fp DLLDatabaseRead; + +typedef void( *DescentDefer_fp) (void); +DescentDefer_fp DLLDescentDefer; + + +typedef void( *DoMessageBox_fp) (const char *title, const char *msg, int type, ddgr_color title_color, ddgr_color text_color); +DoMessageBox_fp DLLDoMessageBoxFP; +inline void DLLDoMessageBox (const char *title, const char *msg, int type, ddgr_color title_color = GR_WHITE, ddgr_color text_color = GR_WHITE) +{ + DLLDoMessageBoxFP(title,msg,type,title_color,text_color); +} + + + +typedef int( *DoUI_fp) (void); +DoUI_fp DLLDoUI; + +typedef void( *Debug_ConsolePrintf_fp ) (int n, char *format, ... ); +Debug_ConsolePrintf_fp DLLDebug_ConsolePrintf; + +typedef void( *DedicatedServerPrintf_fp ) (char *format, ... ); +DedicatedServerPrintf_fp DLLPrintDedicatedMessage; +/* +typedef int( *ValidateUser_fp) (validate_id_request *valid_id, char *trackerid); +ValidateUser_fp DLLValidateUser; + +typedef void( *PollPTrackNet_fp) (); +PollPTrackNet_fp DLLPollPTrackNet; +*/ +typedef void *( *NewUIGameWindowCreate_fp) (int x, int y, int w, int h, int flags); +NewUIGameWindowCreate_fp DLLNewUIGameWindowCreate; + +typedef void( *NewUIGameWindowDestroy_fp) (void * item); +NewUIGameWindowDestroy_fp DLLNewUIGameWindowDestroy; + +typedef void( *NewUIGameWindowOpen_fp) (void * item); +NewUIGameWindowOpen_fp DLLNewUIGameWindowOpen; + +typedef void( *NewUIGameWindowClose_fp) (void * item); +NewUIGameWindowClose_fp DLLNewUIGameWindowClose; + +typedef void( *SetScreenMode_fp) (int); +SetScreenMode_fp DLLSetScreenMode; + +typedef float( *timer_GetTime_fp) (void); +timer_GetTime_fp DLLtimer_GetTime; + +typedef int( *TryToJoinServer_fp) (network_address *addr); +TryToJoinServer_fp DLLTryToJoinServer; + +typedef void( *MultiStartClient_fp) (char *scriptname); +MultiStartClient_fp DLLMultiStartClient; + +typedef void( *rend_GetRenderState_fp) (rendering_state *rstate); +rend_GetRenderState_fp DLLrend_GetRenderState; + +typedef bool( *LoadMission_fp) (char *msn); +LoadMission_fp DLLLoadMission; + +typedef void( *ddio_MakePath_fp) (char* newPath, const char* absolutePathHeader, const char* subDir, ...); +ddio_MakePath_fp DLLddio_MakePath; + +typedef bool( *ddio_FindFileStart_fp) (const char *wildcard, char *namebuf); +ddio_FindFileStart_fp DLLddio_FindFileStart; + +typedef void( *ddio_FindFileClose_fp) (); +ddio_FindFileClose_fp DLLddio_FindFileClose; + +typedef bool( *ddio_FindNextFile_fp) (char *namebuf); +ddio_FindNextFile_fp DLLddio_FindNextFile; + +//typedef void( *MultiStartServer_fp) (int playing,char *scriptname,int dedicated_num_teams=-1); +typedef void( *MultiStartServer_fp) (int playing,char *scriptname,int dedicated_num_teams); +MultiStartServer_fp DLLMultiStartServerFP; +inline void DLLMultiStartServer (int playing,char *scriptname,int dedicated_num_teams=-1) +{ + DLLMultiStartServerFP(playing,scriptname,dedicated_num_teams); +} + + +typedef void( *ShowProgressScreen_fp) (char *,char *); +ShowProgressScreen_fp DLLShowProgressScreen; + +typedef int( *SearchForLocalGamesTCP_fp)(unsigned int,ushort); +SearchForLocalGamesTCP_fp DLLSearchForLocalGamesTCP; + +typedef int( *nw_GetHostAddressFromNumbers_fp) (char *str); +nw_GetHostAddressFromNumbers_fp DLLnw_GetHostAddressFromNumbers; + +typedef int( *nw_GetProtocolType_fp) (void); +nw_GetProtocolType_fp DLLnw_GetProtocolType; + +typedef void *(*HotSpotCreate_fp) (void *parentwin, int id, int key, void * txtitemoff, void * txtitemon, int x, int y, int w, int h, int flags); +HotSpotCreate_fp DLLHotSpotCreate; + +typedef int (*PollUI_fp) (void); +PollUI_fp DLLPollUI; + +typedef char * (*GetMissionName_fp) (char *mission); +GetMissionName_fp DLLGetMissionName; + +typedef void (*RemoveUITextItem_fp) (void *item); +RemoveUITextItem_fp DLLRemoveUITextItem; + +typedef void * (*CreateNewUITextItem_fp) (char *newtext,unsigned int color,int font); +CreateNewUITextItem_fp DLLCreateNewUITextItemFP; +inline void * DLLCreateNewUITextItem(char *newtext,unsigned int color,int font=-1) +{ + return DLLCreateNewUITextItemFP(newtext,color,font); +} + +typedef void *(*mem_malloc_fp) (int size); +mem_malloc_fp DLLmem_malloc; + +typedef void (*mem_free_fp) (void *memblock); +mem_free_fp DLLmem_free; + +typedef void (*CreateSplashScreen_fp) (char *msg,int usecancel); +CreateSplashScreen_fp DLLCreateSplashScreen; + +typedef void (*CloseSplashScreen_fp) (void); +CloseSplashScreen_fp DLLCloseSplashScreen; + +typedef void *(*UIConsoleGadgetCreate_fp) (void * parentid, int id, int x, int y, int font, int cols, int rows, int flags); +UIConsoleGadgetCreate_fp DLLUIConsoleGadgetCreate; + +typedef void (*UIConsoleGadgetputs_fp) (void * item, const char *str); +UIConsoleGadgetputs_fp DLLUIConsoleGadgetputs; + +typedef void (*NewUIWindowSetFocusOnEditGadget_fp) (void * item,void * parent); +NewUIWindowSetFocusOnEditGadget_fp DLLNewUIWindowSetFocusOnEditGadget; + +typedef void *( *OldEditCreate_fp ) (void * parentitem, int id, int x, int y, int w, int h, int flags); +OldEditCreate_fp DLLOldEditCreate; + +typedef void *( *OldListCreate_fp ) (void * parentitem, int id, int x, int y, int w, int h, int flags); +OldListCreate_fp DLLOldListCreate; + +typedef void( *OldListRemoveAll_fp ) (void * item); +OldListRemoveAll_fp DLLOldListRemoveAll; + +typedef void( *OldListAddItem_fp ) (void * item,void * uitext); +OldListAddItem_fp DLLOldListAddItem; + +typedef void( *OldListRemoveItem_fp ) (void * item,void * txtitem); +OldListRemoveItem_fp DLLOldListRemoveItem; + +typedef void( *OldListSelectItem_fp ) (void * item,void * txtitem); +OldListSelectItem_fp DLLOldListSelectItem; + +typedef char *( *OldListGetItem_fp ) (void * item,int index); +OldListGetItem_fp DLLOldListGetItem; + +typedef int( *OldListGetSelectedIndex_fp ) (void * item); +OldListGetSelectedIndex_fp DLLOldListGetSelectedIndex; + +typedef void( *OldEditSetText_fp ) (void * item,char *newtext); +OldEditSetText_fp DLLOldEditSetText; + +typedef void( *OldEditGetText_fp ) (void * item,char *buff,int len); +OldEditGetText_fp DLLOldEditGetText; + +typedef void (*ToggleUICallback_fp) (int state); +ToggleUICallback_fp DLLToggleUICallback; + +typedef int (*SearchForGamesPXO_fp) (unsigned int ask,ushort port); +SearchForGamesPXO_fp DLLSearchForGamesPXO; + +typedef void (*SetOldEditBufferLen_fp) (void * item,int len); +SetOldEditBufferLen_fp DLLSetOldEditBufferLen; + +typedef void (*NewUIWindowLoadBackgroundImage_fp) (void * item,const char *image_name); +NewUIWindowLoadBackgroundImage_fp DLLNewUIWindowLoadBackgroundImage; + +typedef void (*DeleteUIItem_fp) (void *delitem); +DeleteUIItem_fp DLLDeleteUIItem; + +typedef int (*SearchForLocalGamesIPX_fp) (network_address *check_addr); +SearchForLocalGamesIPX_fp DLLSearchForLocalGamesIPX; + +typedef void (*HotSpotSetStates_fp)(void *hotspot,void *texton,void *textoff); +HotSpotSetStates_fp DLLHotSpotSetStates; + +typedef bool (*PlayerSetShipPermission_fp)(int pnum,char *ship_name,bool allowed); +PlayerSetShipPermission_fp DLLPlayerSetShipPermission; + +typedef bool (*PlayerIsShipAllowed_fp)(int pnum,int ship_index); +PlayerIsShipAllowed_fp DLLPlayerIsShipAllowed; + +#ifdef WIN32 +typedef int (*dp_ListDirectPlayGames_fp)(); +dp_ListDirectPlayGames_fp DLLdp_ListDirectPlayGames; + +typedef int (*dp_InitDirectPlay_fp)(char *conn_name, void *parms,int num_elements); +dp_InitDirectPlay_fp DLLdp_InitDirectPlay; + + +typedef int (*dp_GetModemChoices_fp)(char *buffer,unsigned long *size); +dp_GetModemChoices_fp DLLdp_GetModemChoices; +#endif + +//Given a filename, pointer to a char * array and a pointer to an int, +//it will load the string table and fill in the information +//returns true on success +typedef bool (*CreateStringTable_fp)(char *filename,char ***table,int *size); +CreateStringTable_fp DLLCreateStringTable; + +//Given a string table and it's count of strings, it will free up it's memory +typedef void (*DestroyStringTable_fp)(char **table,int size); +DestroyStringTable_fp DLLDestroyStringTable; + +typedef void (*DatabaseReadInt_fp)(const char *label, int *val); +DatabaseReadInt_fp DLLDatabaseReadInt; + +typedef void (*DatabaseWriteInt_fp)(const char *label, int val); +DatabaseWriteInt_fp DLLDatabaseWriteInt; + +typedef void (*DoScreenshot_fp) (); +DoScreenshot_fp DLLDoScreenshot; + +typedef bool (*IsMissionMultiPlayable_fp) (char *mission); +IsMissionMultiPlayable_fp DLLIsMissionMultiPlayable; + +// returns width of text in current font. +typedef int (*grtext_GetTextLineWidth_fp) (const char *str); +grtext_GetTextLineWidth_fp DLLgrtext_GetTextLineWidth; + +typedef void (*GadgetDestroy_fp) (void *item); +GadgetDestroy_fp DLLGadgetDestroy; + +#ifdef WIN32 +typedef int (*dp_StartGame_fp) (char *gamename); +dp_StartGame_fp DLLdp_StartGame; + +typedef void (*dp_EndGame_fp)(); +dp_EndGame_fp DLLdp_EndGame; +#endif + +typedef int (*nw_Asyncgethostbyname_fp) (unsigned int *ip,int command, char *hostname); +nw_Asyncgethostbyname_fp DLLnw_Asyncgethostbyname; + +typedef int (*nw_ReccomendPPS_fp) (); +nw_ReccomendPPS_fp DLLnw_ReccomendPPS; + +typedef void (*DoMultiAllowed_fp) (void); +DoMultiAllowed_fp DLLDoMultiAllowed; + +typedef void (*MultiDoConfigSave_fp) (void); +MultiDoConfigSave_fp DLLMultiDoConfigSave; + +typedef void (*MultiDoConfigLoad_fp) (void); +MultiDoConfigLoad_fp DLLMultiDoConfigLoad; + +typedef int (*MultiLoadSettings_fp) (char *filename); +MultiLoadSettings_fp DLLMultiLoadSettings; + +typedef void * ( * NetworkReceiveCallback ) (ubyte *data,int len, network_address *from); + +typedef int (*nw_RegisterCallback_fp) (void * nfp, ubyte id); +nw_RegisterCallback_fp DLLnw_RegisterCallback; + +typedef NetworkReceiveCallback (*nw_UnRegisterCallback_fp) (ubyte id); +nw_UnRegisterCallback_fp DLLnw_UnRegisterCallback; + +typedef int (*nw_SendWithID_fp) (ubyte id,ubyte *data,int len,network_address *who_to); +nw_SendWithID_fp DLLnw_SendWithID; + +typedef int (*nw_DoReceiveCallbacks_fp)(void); +nw_DoReceiveCallbacks_fp DLLnw_DoReceiveCallbacks; + +typedef int (*msn_CheckGetMission_fp)(network_address *net_addr,char *filename); +msn_CheckGetMission_fp DLLmsn_CheckGetMission; + +typedef void (*MultiGameOptionsMenu_fp) (int alloptions); +MultiGameOptionsMenu_fp DLLMultiGameOptionsMenu; + +//Loads a dynamic module into memory for use. +//Returns true on success, false otherwise +//typedef bool (*mod_LoadModule_fp)(module *handle,char *modfilename,int flags=MODF_NOW); +typedef bool (*mod_LoadModule_fp)(module *handle,char *modfilename,int flags); +mod_LoadModule_fp DLLmod_LoadModule; + +//Frees a previously loaded module from memory, it can no longer be used +//Returns true on success, false otherwise +typedef bool (*mod_FreeModule_fp)(module *handle); +mod_FreeModule_fp DLLmod_FreeModule; + +//Returns a pointer to a function within a loaded module. If it returns NULL there was an error. Check mod_GetLastError +//to see if there was an error +//symstr is the name of the function you want to get the symbol for (Do NOT give any pre/suffix to this name) +//parmbytes is the size (in bytes) of the parameter list the function should have +typedef MODPROCADDRESS (*mod_GetSymbol_fp)(module *handle,char *symstr,unsigned char parmbytes); +mod_GetSymbol_fp DLLmod_GetSymbol; + +//Returns an error code to what the last error was. When this function is called the last error is cleared, so by calling +//this function it not only returns the last error, but it removes it, so if you were to call this function again, it would +//return no error +typedef int (*mod_GetLastError_fp)(void); +mod_GetLastError_fp DLLmod_GetLastError; + +// "Current Pilot" name access function +typedef void (*CurrentPilotName_fp)(char *buffer); +CurrentPilotName_fp CurrentPilotName; + +typedef void (*UpdateAndPackGameList_fp) (void); +UpdateAndPackGameList_fp DLLUpdateAndPackGameList; + +typedef int (*MultiLevelSelection_fp) (void); +MultiLevelSelection_fp DLLMultiLevelSelection; + +typedef bool (*DoPlayerMouselookCheck_fp) (unsigned int flags); +DoPlayerMouselookCheck_fp DLLDoPlayerMouselookCheck; + +typedef int (*CheckMissionForScript_fp) (char *mission,char *script,int dedicated_server_num_teams); +CheckMissionForScript_fp DLLCheckMissionForScript; + + +typedef void (*ShowNetgameInfo_fp) (network_game *game); +ShowNetgameInfo_fp DLLShowNetgameInfo; + +typedef int (*CheckGetD3M_fp)(char *d3m); +CheckGetD3M_fp DLLCheckGetD3M; + +int DLLUIClass_CurrID = 0xD0; + +#define MAX_NET_GAMES 100 +#define JEFF_RED GR_RGB(255,40,40) +#define JEFF_BLUE GR_RGB(40,40,255) +#define JEFF_GREEN GR_RGB(40,255,40) +#define NETPOLLINTERVAL 10.0 + +extern int MTAVersionCheck(unsigned int oldver, char *URL); + + +#define LOGIN_LEN 33 +#define REAL_NAME_LEN 66 +#define PASSWORD_LEN 17 +#define EMAIL_LEN 100 +#define TRACKER_ID_LEN 10 +#define PILOT_NAME_LEN 20 + +#ifdef WIN32 +#pragma pack(push,pxo) +#endif +#pragma pack(1) +typedef struct vmt_descent3_struct { + char tracker_id[TRACKER_ID_LEN]; + char pilot_name[PILOT_NAME_LEN]; + int rank; + + int kills; + int deaths; + int suicides; + int online_time; + int games_played; + unsigned int security; + unsigned char virgin_pilot; //This pilot was just created if TRUE + unsigned int lateral_thrust; + unsigned int rotational_thrust; + unsigned int sliding_pct; //Percentage of the time you were sliding + unsigned long checksum; //This value needs to be equal to whatever the checksum is once the packet is decoded + unsigned long pad; //just to provide room for out 4 byte encryption boundry only needed on the client side for now +} vmt_descent3_struct; +#define DESCENT3_BLOCK_SIZE (sizeof(vmt_descent3_struct)-4) +#ifdef WIN32 +#pragma pack(pop,pxo) +#else +#pragma pack() +#endif + + +///////////////////////////// +// Defines +#ifndef RELEASE +#define DLLmprintf(args) DLLDebug_ConsolePrintf args +#else +#define DLLmprintf(args) +#endif + +unsigned int MTClientVer = 100; + +char MTUpdateURL[300] = ""; + +multi_api API; + +player *DLLMPlayers; +ship *DLLShips; +vmt_descent3_struct *DLLMTPilotinfo; +int DLLPlayer_num; +int *DLLGame_is_master_tracker_game; +int DLLGame_mode; +char *DLLTracker_id; +int *DLLNum_directplay_games; +netgame_info *DLLNetgame; +char *DLLLocalD3Dir; +int *DLLMultiGameStarting; +netplayer *DLLMNetPlayers; +int MTWritingPilot,MTReadingPilot; +int UnvalidatedDLL = 0; +int *DLLNum_network_games_known; +bool *DLLDedicated_server; +network_game *DLLNetwork_games; +int MT_Initialized = 0; +ubyte *DLLNewUIWindow_alpha; +float LastTrackerDataUpdate; +void * pconsole = NULL; +bool * DLLDP_active; +bool * DLLUse_DirectPlay; +bool * DLLMulti_Gamelist_changed; +bool * DLLSupports_score_api; +//bool * DLLMulti_no_stats_saved; +unsigned short DLLnw_ListenPort; + + +char szloginid[LOGIN_LEN] = ""; +char sztrackerid[TRACKER_ID_LEN] = ""; +char szpassword[PASSWORD_LEN] = ""; + +char *DLLHelpText1; +char *DLLHelpText2; +char *DLLHelpText3; +char *DLLHelpText4; + +char *DLLPXO_hosted_lobby_name = NULL; + +#define DLLMAX_SHIPS 30 + +#define PARENT0 0 +#define PARENT1 1 +#define PARENT2 2 +#define PARENT3 3 +#define PARENT4 4 + +char *DLLAuto_login_name; +char *DLLAuto_login_pass; +char *DLLAuto_login_addr; +char *DLLAuto_login_port; + +bool Use_netgame_flags; + +module MTAVDLLHandle={NULL}; + +#if defined(WIN32) +typedef void( DLLFUNCCALL DLLAVInit_fp) (int *ptr); +typedef void( DLLFUNCCALL DLLAVCall_fp) (int eventnum); +typedef void( DLLFUNCCALL DLLAVClose_fp) (); +typedef void( DLLFUNCCALL DLLAVGetVersion_fp) (int *version); +typedef void( DLLFUNCCALL DLLRunCheck_fp) (char *d3_path); +#elif defined(__LINUX__) +typedef void DLLFUNCCALL (DLLAVInit_fp) (int *ptr); +typedef void DLLFUNCCALL (DLLAVCall_fp) (int eventnum); +typedef void DLLFUNCCALL (DLLAVClose_fp) (); +typedef void DLLFUNCCALL (DLLAVGetVersion_fp) (int *version); +typedef void DLLFUNCCALL (DLLRunCheck_fp) (char *d3_path); +#endif + +#ifndef MACINTOSH +DLLAVInit_fp *DLLAVInit = NULL; +DLLAVCall_fp *DLLAVCall = NULL; +DLLAVClose_fp *DLLAVClose = NULL; +DLLAVGetVersion_fp *DLLAVGetVersion = NULL; +DLLRunCheck_fp *DLLRunCheck = NULL; +#endif + +typedef struct _msn_list +{ + char msn_name[_MAX_PATH]; + char msn_file[_MAX_PATH]; + void *ti; + struct _msn_list *next; +}msn_list; + +_msn_list *FirstMsn,*CurrMsn,*TmpMsn; + +#ifdef WIN32 +#include "directplay.h" + +modem_list *DLLModems_found; +int *DLLNum_modems_found; + +LPDPSESSIONDESC2 DLLDirectplay_sessions; +#endif + +BOOL DLLTCP_active; +BOOL DLLIPX_active; + + +void AddMsnItem(_msn_list *new_msn) +{ + CurrMsn = FirstMsn; + if(CurrMsn) + { + while(CurrMsn->next) + { + CurrMsn = CurrMsn->next; + } + CurrMsn->next = new_msn; + CurrMsn = CurrMsn->next; + } + else + { + FirstMsn = new_msn; + CurrMsn = FirstMsn; + } + CurrMsn->next = NULL; +} +void RemoveAllMsnItems(void) +{ + CurrMsn = FirstMsn; + if(CurrMsn) + { + while(CurrMsn->next) + { + TmpMsn = CurrMsn; + DLLRemoveUITextItem(CurrMsn->ti); + CurrMsn = CurrMsn->next; + DLLmem_free(TmpMsn); + } + DLLRemoveUITextItem(CurrMsn->ti); + DLLmem_free(CurrMsn); + } + FirstMsn = NULL; +} + +_msn_list * FindMsnItem(char *name) +{ + CurrMsn = FirstMsn; + if(CurrMsn) + { + while(CurrMsn->next) + { + if(strcmp(CurrMsn->msn_name,name)==0) + return CurrMsn; + CurrMsn = CurrMsn->next; + } + if(strcmp(CurrMsn->msn_name,name)==0) + return CurrMsn; + } + return NULL; +} + +void MultiplayerOptionsMenu (); + +// Draws a menu that inputs multiplayer game options +// Returns true if we are starting a game +#define MAX_DLLS 40 +char dll_text[MAX_DLLS][_MAX_PATH]; + +int StartMultiplayerGameMenu () +{ + void * game_name_text = DLLCreateNewUITextItem(TXT(10), UICOL_TEXT_NORMAL,-1);//TXT_LC_GAMENAME + void * mission_name_text = DLLCreateNewUITextItem(TXT(11), UICOL_TEXT_NORMAL,-1);//TXT_LC_MSNNAME + void * script_name_text = DLLCreateNewUITextItem(TXT(12), UICOL_TEXT_NORMAL,-1);//TXT_LC_SCRIPTNAME + + void * start_game_on_text = DLLCreateNewUITextItem(TXT(13), UICOL_HOTSPOT_HI,-1);//TXT_LC_STARTGAME + void * multiplayer_opts_on_text = DLLCreateNewUITextItem(TXT(14), UICOL_HOTSPOT_HI,-1);//TXT_LC_MPLYROPTIONS + void * exit_on_text = DLLCreateNewUITextItem(TXT(5), UICOL_HOTSPOT_HI,-1);//TXT_LC_PREVMENU + + void * start_game_off_text = DLLCreateNewUITextItem(TXT(13), UICOL_HOTSPOT_LO,-1);//TXT_LC_STARTGAME + void * multiplayer_opts_off_text = DLLCreateNewUITextItem(TXT(14), UICOL_HOTSPOT_LO,-1);//TXT_LC_MPLYROPTIONS + void * exit_off_text = DLLCreateNewUITextItem(TXT(5), UICOL_HOTSPOT_LO,-1);//TXT_LC_PREVMENU + + + void * save_settings_txt_on = DLLCreateNewUITextItem(TXT_DLL_SAVESETTINGS, UICOL_HOTSPOT_HI,-1); + void * save_settings_txt_off = DLLCreateNewUITextItem(TXT_DLL_SAVESETTINGS, UICOL_HOTSPOT_LO,-1); + + void * load_settings_txt_on = DLLCreateNewUITextItem(TXT_DLL_LOADSETTINGS, UICOL_HOTSPOT_HI,-1); + void * load_settings_txt_off = DLLCreateNewUITextItem(TXT_DLL_LOADSETTINGS, UICOL_HOTSPOT_LO,-1); + + void * start_text = DLLCreateNewUITextItem(TXT(13), UICOL_WINDOW_TITLE,DLL_BIG_BRIEFING_FONT); + + void * blank_text = DLLCreateNewUITextItem("",GR_BLACK,-1); + void * dll_txt_items[MAX_DLLS]; + int a; + for(a=0;amsn_name,DLLGetMissionName(buffer)); + strcpy(mi->msn_file,buffer); + mi->ti = DLLCreateNewUITextItem(mi->msn_name,UICOL_LISTBOX_LO,-1); + AddMsnItem(mi); + DLLListAddItem(list_1,mi->ti); + } + while(DLLddio_FindNextFile(buffer)) + { + if(DLLIsMissionMultiPlayable(buffer)) + { + DLLmprintf((0,"Found a mission: %s\n",buffer)); + mi = (_msn_list *)DLLmem_malloc(sizeof(msn_list)); + strcpy(mi->msn_name,DLLGetMissionName(buffer)); + strcpy(mi->msn_file,buffer); + mi->ti = DLLCreateNewUITextItem(mi->msn_name,UICOL_LISTBOX_LO,-1); + AddMsnItem(mi); + DLLListAddItem(list_1,mi->ti); + + } + } + } + DLLddio_FindFileClose(); + + //char mn3_path[_MAX_PATH*2]; + DLLddio_MakePath(search,DLLLocalD3Dir,"missions","*.mn3",NULL); + //DLLmprintf((0,search)); + if(DLLddio_FindFileStart(search,buffer)) + { + //DLLddio_MakePath(mn3_path,DLLLocalD3Dir,"missions",buffer,NULL); + + if(DLLIsMissionMultiPlayable(buffer) && (strcmpi("d3_2.mn3",buffer)!=0)) + { + DLLmprintf((0,"Found a mission: %s\n",buffer)); + mi = (_msn_list *)DLLmem_malloc(sizeof(msn_list)); + strcpy(mi->msn_name,DLLGetMissionName(buffer)); + strcpy(mi->msn_file,buffer); + mi->ti = DLLCreateNewUITextItem(mi->msn_name,UICOL_LISTBOX_LO,-1); + AddMsnItem(mi); + DLLListAddItem(list_1,mi->ti); + } + while(DLLddio_FindNextFile(buffer)) + { + if(strcmpi("d3_2.mn3",buffer)==0) + continue; + //DLLddio_MakePath(mn3_path,DLLLocalD3Dir,"missions",buffer,NULL); + if(DLLIsMissionMultiPlayable(buffer)) + { + DLLmprintf((0,"Found a mission: %s\n",buffer)); + mi = (_msn_list *)DLLmem_malloc(sizeof(msn_list)); + strcpy(mi->msn_name,DLLGetMissionName(buffer)); + strcpy(mi->msn_file,buffer); + mi->ti = DLLCreateNewUITextItem(mi->msn_name,UICOL_LISTBOX_LO,-1); + AddMsnItem(mi); + DLLListAddItem(list_1,mi->ti); + + } + } + } + DLLddio_FindFileClose(); +#ifdef RELEASE + //TODO: Make sure the main mission is always listed -- even on a minimal install + if(!FindMsnItem("Descent 3: Retribution")) + { + mi = (_msn_list *)DLLmem_malloc(sizeof(msn_list)); + + strcpy(mi->msn_name,"Descent 3: Retribution"); + strcpy(mi->msn_file,"d3.mn3"); + AddMsnItem(mi); + mi->ti = DLLCreateNewUITextItem(mi->msn_name,UICOL_LISTBOX_LO); + DLLListAddItem(list_1,mi->ti); + } +#endif + char *p; +#else + void * msn_single_ti = DLLCreateNewUITextItem("Polaris",UICOL_LISTBOX_LO); + void * msn_multi_ti = DLLCreateNewUITextItem("The Core",UICOL_LISTBOX_LO); + void * msn_multi_2 = DLLCreateNewUITextItem("Taurus",UICOL_LISTBOX_LO); + DLLListAddItem(list_1,msn_single_ti); + DLLListAddItem(list_1,msn_multi_ti); + DLLListAddItem(list_1,msn_multi_2); +#endif +// Bash some values to default + char pilot_name[PILOT_STRING_SIZE]; + CurrentPilotName(pilot_name); + + strcpy (DLLMPlayers[DLLPlayer_num].callsign,pilot_name); + sprintf (str,"%s's Game",DLLMPlayers[DLLPlayer_num].callsign); + DLLEditSetText(mission_name_edit,str); + + DLLNetgame->max_players = 8; + DLLNetgame->packets_per_second=DLLnw_ReccomendPPS(); + DLLNetgame->respawn_time=60; + DLLNetgame->difficulty=2; + DLLNetgame->flags=NF_RANDOMIZE_RESPAWN; + DLLNewUIWindowLoadBackgroundImage(main_wnd,"multimain.ogf"); + DLLNewUIWindowOpen(main_wnd); + char dftset[_MAX_PATH*2]; + DLLddio_MakePath(dftset,DLLLocalD3Dir,"custom","settings","default.mps",NULL); + if(DLLMultiLoadSettings(dftset)) + { + DLLEditSetText(mission_name_edit,DLLNetgame->name); +#if (!(defined(OEM) || defined(DEMO))) + p = DLLGetMissionName(DLLNetgame->mission); + mi = FindMsnItem(p); + + if(mi) DLLListSelectItem(list_1,mi->ti); +#endif + for(index=0;indexscriptname)==0) + { + DLLListSelectItem(script_list,dll_txt_items[index]); + break; + } + } + } + while (!exit_menu) + { + int res; + + res = DLLDoUI(); + + // handle all UI results. + + if (res==option_button) + { + DLLNewUIWindowClose(main_wnd); + MultiplayerOptionsMenu(); + DLLNewUIWindowOpen(main_wnd); + } + else if (res==start_button) + { + // Get Game name + DLLEditGetText(mission_name_edit,DLLNetgame->name,NETGAME_NAME_LEN); + // Get mission name +#if (!(defined(OEM) || defined(DEMO))) + mi = FindMsnItem(DLLListGetItem(list_1,DLLListGetSelectedIndex(list_1))); + if(!mi) break; + strncpy(DLLNetgame->mission,mi->msn_file,MSN_NAMELEN); + DLLNetgame->mission[MSN_NAMELEN] = '\0'; + strncpy(DLLNetgame->mission_name,mi->msn_name,MISSION_NAME_LEN); + DLLNetgame->mission_name[MISSION_NAME_LEN] = '\0'; +#else + //strcpy(DLLNetgame->mission,DLLListGetSelectedIndex(list_1)?"thecore.d3l":"polaris.d3l"); + + switch(DLLListGetSelectedIndex(list_1)) + { + case 0: + strcpy(DLLNetgame->mission,"polaris.d3l"); + strncpy(DLLNetgame->mission_name,"Polaris",MISSION_NAME_LEN); + break; + case 1: + strcpy(DLLNetgame->mission,"thecore.d3l"); + strncpy(DLLNetgame->mission_name,"The Core",MISSION_NAME_LEN); + break; + case 2: + + strcpy(DLLNetgame->mission,"taurus.d3l"); + strncpy(DLLNetgame->mission_name,"Taurus",MISSION_NAME_LEN); + + break; + default:; + } + + + + + //strcpy(DLLNetgame->name,DLLListGetSelectedIndex(list_1)?"The Core":"Polaris"); +#endif + // Get script + strcpy(buffer,DLLListGetItem(script_list,DLLListGetSelectedIndex(script_list))); + if(strcmp(buffer,"None")) + { + //remove file extension + for(int d=strlen(buffer)-1;d>=0;d--) + { + if(buffer[d]=='.') + { + buffer[d]='\0'; + break; + } + } + +#if (defined(OEM) || defined(DEMO)) + if(!stricmp(buffer,"Capture The Flag")) + strcpy(buffer,"CTF"); +#endif + + strcpy(DLLNetgame->scriptname,buffer); + + } + + // Actually start the game +//DAJ commen out next line if problems + int teams = DLLCheckMissionForScript(DLLNetgame->mission,DLLNetgame->scriptname,0); +// int teams = 1; + if(teams>=0) +#if (!(defined(OEM) || defined(DEMO))) + { + if (DLLLoadMission (DLLNetgame->mission)) +#else + { + if (DLLLoadMission (DLLNetgame->mission)) +#endif + { + //Do warp dialog here if needed + if(-1!=DLLMultiLevelSelection()) + { + DLLmprintf ((0,"Mission loaded successfully!\n")); + DLLMultiStartServer(1,DLLNetgame->scriptname,teams); + exit_menu=1; + ret=1; + } + } + } + } + else if (res==cancel_button) + { + exit_menu=1; + } + else if(res==save_button) + { + // Get Game name + DLLEditGetText(mission_name_edit,DLLNetgame->name,NETGAME_NAME_LEN); +#if (!(defined(OEM) || defined(DEMO))) + // Get mission name + mi = FindMsnItem(DLLListGetItem(list_1,DLLListGetSelectedIndex(list_1))); + if(!mi) break; + strcpy(DLLNetgame->mission,mi->msn_file); +#endif + // Get script + strcpy(buffer,DLLListGetItem(script_list,DLLListGetSelectedIndex(script_list))); + if(strcmp(buffer,"None")) + { + //remove file extension + for(int d=strlen(buffer)-1;d>=0;d--) + { + if(buffer[d]=='.') + { + buffer[d]='\0'; + break; + } + } + + strcpy(DLLNetgame->scriptname,buffer); + + } + DLLMultiDoConfigSave(); + } + else if(res==load_button) + { + // Get Game name + DLLEditGetText(mission_name_edit,DLLNetgame->name,NETGAME_NAME_LEN); +#if (!(defined(OEM) || defined(DEMO))) + // Get mission name + mi = FindMsnItem(DLLListGetItem(list_1,DLLListGetSelectedIndex(list_1))); + if(!mi) break; + strcpy(DLLNetgame->mission,mi->msn_file); +#endif + // Get script + strcpy(buffer,DLLListGetItem(script_list,DLLListGetSelectedIndex(script_list))); + if(strcmp(buffer,"None")) + { + //remove file extension + for(int d=strlen(buffer)-1;d>=0;d--) + { + if(buffer[d]=='.') + { + buffer[d]='\0'; + break; + } + } + + strcpy(DLLNetgame->scriptname,buffer); + + } + DLLMultiDoConfigLoad(); + if(!Use_netgame_flags) + { + DLLNetgame->flags=0; + } + DLLEditSetText(mission_name_edit,DLLNetgame->name); +#if (!(defined(OEM) || defined(DEMO))) + p = DLLGetMissionName(DLLNetgame->mission); + mi = FindMsnItem(p); + + if(mi) DLLListSelectItem(list_1,mi->ti); +#endif + for(index=0;indexscriptname)==0) + { + DLLListSelectItem(script_list,dll_txt_items[index]); + break; + } + } + + + } + } + + DLLNewUIWindowClose(main_wnd); + DLLNewUIWindowDestroy(main_wnd); + for(a=0;a +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/linux_fix.h" + +//Linux includes/defines +#if !MACOSX +#include +#include +//#include +#endif + +#define BOOL bool + +#ifndef SOCKET +#define SOCKET unsigned int +#endif + +#define SOCKADDR_IN sockaddr_in +#define SOCKADDR_IPX sockaddr_ipx +#define SOCKADDR sockaddr +#define INVALID_SOCKET -1 +#define NSPROTO_IPX AF_IPX +#define HOSTENT struct hostent +#define SERVENT struct servent +#define TIMEVAL struct timeval +//Winsock = sockets error translation +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINVAL EINVAL +#define WSAENOPROTOOPT ENOPROTOOPT +#define WSAEALREADY EALREADY +#define WSAEISCONN EISCONN +#define SOCKET_ERROR -1 + +#ifndef NETWORKING_H +inline int WSAGetLastError(){return errno;} +#endif + +#ifdef MACINTOSH +#include "macsock.h" +#endif + +#include +//#include + +/* +typedef int (*pthread_create_fp)(pthread_t *__thread,__const pthread_attr_t *__attr,void *(*__start_routine) (void *),void *__arg); +typedef void (*pthread_exit_fp)(void *__retval); +typedef int (*pthread_detach_fp)(pthread_t __th); +typedef pthread_t (*pthread_self_fp)(void); + +extern pthread_create_fp df_pthread_create; +extern pthread_exit_fp df_pthread_exit; +extern pthread_detach_fp df_pthread_detach; +extern pthread_self_fp df_pthread_self; +*/ + +bool inet_LoadThreadLib(void); + +#endif + +#include "CFtp.h" +#include "Chttpget.h" + + +#define INET_ERROR_NO_ERROR 0 +#define INET_ERROR_BADPARMS 1 +#define INET_ERROR_CANT_WRITE_FILE 2 +#define INET_ERROR_CANT_PARSE_URL 3 +#define INET_ERROR_BAD_FILE_OR_DIR 4 +#define INET_ERROR_HOST_NOT_FOUND 5 +#define INET_ERROR_UNKNOWN_ERROR 6 +#define INET_ERROR_NO_MEMORY 7 + +#define INET_STATE_CONNECTING 1 +#define INET_STATE_ERROR 2 +#define INET_STATE_RECEIVING 3 +#define INET_STATE_GOT_FILE 4 + + +class InetGetFile +{ +public: + InetGetFile(char *URL,char *localfile); + InetGetFile(char *URL,char *localfile,char *proxyip,short proxyport); + ~InetGetFile(); + BOOL IsFileReceived(); + BOOL IsFileError(); + BOOL IsConnecting(); + BOOL IsReceiving(); + int GetErrorCode(); + int GetBytesIn(); + int GetTotalBytes(); + void AbortGet(); + +protected: + CFtpGet *ftp; + ChttpGet *http; + BOOL m_bUseHTTP; + int m_ErrorCode; + int m_State; + int m_HardError; + +}; + +#endif + +/* + +#include +#include +#include +#include + +#include "inetgetfile.h" + +int main(int argc,char **argv) +{ + unsigned int LastPrintbytes = time(NULL); + InetGetFile *inetfile; + WSADATA ws_data; + WORD ver=MAKEWORD(1,1); + + int error=WSAStartup(ver,&ws_data); + inetfile = new InetGetFile("http://www.volition-inc.com/images/download/freespace/fsdemo1x-12u.exe","e:\\fsdemo1x-12u.exe"); + do + { + if(inetfile->IsFileReceived()) + { + printf("File received\n"); + break; + } + if(inetfile->IsFileError()) + { + printf("File not received. Error code: %d\n",inetfile->GetErrorCode()); + break; + } + if(time(NULL)-LastPrintbytes>=1) + { + int ipct = 0; + if(inetfile->GetTotalBytes()) + { + ipct = 100*(float)((float)inetfile->GetBytesIn()/(float)inetfile->GetTotalBytes()); + } + printf("Received %d Bytes out of %d (%d%%).\n",inetfile->GetBytesIn(),inetfile->GetTotalBytes(),ipct); + LastPrintbytes = time(NULL); + } + + + }while(!kbhit()); + return 0; + +} + + + + */ + + + diff --git a/netcon/includes/mdllinit.h b/netcon/includes/mdllinit.h new file mode 100644 index 000000000..e950f7fb4 --- /dev/null +++ b/netcon/includes/mdllinit.h @@ -0,0 +1,300 @@ +/* +* $Logfile: /DescentIII/Main/lib/mdllinit.h $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:58:12 $ +* $Author: kevinb $ +* +* Multiplayer DLL function pointer init +* +* $Log: mdllinit.h,v $ +* Revision 1.1.1.1 2003/08/26 03:58:12 kevinb +* initial 1.5 import +* + * + * 40 10/03/01 1:06a Kevin + * pxo bandwidth reduction and nat fix + * + * 39 3/26/00 10:30p Kevin + * MOD Downloader for 1.4 patch. + * + * 38 7/12/99 4:15p Kevin + * Changed the way we determine if we should report stats or not in PXO + * + * 37 7/06/99 5:52p Kevin + * PXO & multiplayer fixes for the patch + * + * 36 4/25/99 12:11a Kevin + * game info dialog stuff + * + * 35 4/23/99 3:33p Kevin + * mission file/multiplayer mod keyword system + * + * 34 4/19/99 3:57a Jeff + * got compiling for Linux + * + * 33 4/18/99 3:13p Jeff + * got working with linux + * + * 32 4/16/99 3:17p Kevin + * More mouselook support + * + * 31 4/08/99 4:54p Kevin + * Display level warp dialog for multiplayer + * + * 30 4/08/99 3:42p Kevin + * Added some stuff for the scoring API. Not functional yet. + * + * 29 3/25/99 3:26p Kevin + * Made PXO games be based on your chat channel + * + * 28 3/17/99 4:08p Kevin + * Changed the way games appear and timeout in the game list. + * + * 27 2/15/99 7:48p Jeff + * new pilot file class and read/write system checked in...should be more + * robust than old + * + * 26 1/11/99 12:29p Jeff + * changes made not to call the module library directly + * + * 25 1/07/99 11:51a Kevin + * Added support for joining servers on alternate ports and hosting behind + * a proxy/firewall + * + * 24 12/30/98 3:49p Kevin + * Moved multiplayer options out of DLL and into main app + * + * 23 12/30/98 12:16p Kevin + * Auto Mission Download system + * + * 22 12/23/98 6:38p Kevin + * All UDP data (except gamespy) now uses one (registered) port number + * + * 21 10/01/98 11:37a Kevin + * UI fixes and stuff + * + * 20 9/23/98 2:55p Kevin + * Saved multi config and changed UI to conform + * + * 19 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 18 9/21/98 11:19a Kevin + * check protocol before entering multiplayer screens + * + * 17 9/16/98 8:06p Jason + * got mastertracker working with the dedicated server + * + * 16 9/04/98 1:51p Kevin + * implemented asyncronous gethostbyname + * + * 15 9/02/98 6:54p Kevin + * Fixed general directplay support up, and got modem-modem working + * + * 14 8/27/98 5:03p Kevin + * Prettied up multiplayer screens and fixed some bugs. + * + * 13 8/25/98 6:33p Kevin + * pxo + * + * 12 8/24/98 5:04p Kevin + * Made msn files have the option to not be playable in multiplayer + * + * 11 8/24/98 10:42a Kevin + * Updated DLL for directplay and PXO background + * + * 10 8/19/98 11:50a Kevin + * Got DirectPlay IPX working, and localized connection DLLs + * + * 9 8/07/98 12:39p Jeff + * added "allowed ships" to multiplayer options + * + * 8 7/21/98 1:49p Kevin + * IPX support and peer-peer option for multi + * + * 7 7/20/98 2:34p Kevin + * Re-wrote the DLL wrapper, to allow for better expandability + * + * 6 7/10/98 10:47a Kevin + * Added command line connecting to games + * + * 5 6/24/98 6:40p Kevin + * Added help to main dll menu + * + * 4 6/24/98 3:24p Kevin + * Updated PXO screens with chat, etc. + * + * 3 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/18/98 2:26p Kevin +* +* $NoKeywords: $ +*/ + + DLLGetMultiAPI=(GetMultiAPI_fp)api_func; + DLLGetMultiAPI (&API); + DLLSetUITextItemText = (SetUITextItemText_fp)API.fp[0]; + DLLNewUIWindowCreate = (NewUIWindowCreate_fp)API.fp[1]; + DLLNewUIWindowDestroy = (NewUIWindowDestroy_fp)API.fp[2]; + DLLNewUIWindowOpen = (NewUIWindowOpen_fp)API.fp[3]; + DLLNewUIWindowClose = (NewUIWindowClose_fp)API.fp[4]; + DLLTextCreate = (TextCreate_fp)API.fp[5]; + DLLEditCreate = (EditCreate_fp)API.fp[6]; + DLLButtonCreate = (ButtonCreate_fp)API.fp[7]; + DLLListCreate = (ListCreate_fp)API.fp[8]; + DLLListRemoveAll = (ListRemoveAll_fp)API.fp[9]; + DLLListAddItem = (ListAddItem_fp)API.fp[10]; + DLLListRemoveItem = (ListRemoveItem_fp)API.fp[11]; + DLLListSelectItem = (ListSelectItem_fp)API.fp[12]; + DLLListGetItem = (ListGetItem_fp)API.fp[13]; + DLLListGetSelectedIndex = (ListGetSelectedIndex_fp)API.fp[14]; + DLLEditSetText = (EditSetText_fp)API.fp[15]; + DLLEditGetText = (EditGetText_fp)API.fp[16]; + DLLDatabaseWrite = (DatabaseWrite_fp)API.fp[17]; + DLLDatabaseRead = (DatabaseRead_fp)API.fp[18]; + DLLDoUI = (DoUI_fp)API.fp[19]; + DLLDoMessageBoxFP = (DoMessageBox_fp)API.fp[20]; + DLLDescentDefer = (DescentDefer_fp)API.fp[21]; + DLLDebug_ConsolePrintf=(Debug_ConsolePrintf_fp)API.fp[22]; + DLLDestroyStringTable = (DestroyStringTable_fp)API.fp[23]; + DLLCreateStringTable = (CreateStringTable_fp)API.fp[24]; + DLLNewUIGameWindowCreate = (NewUIGameWindowCreate_fp)API.fp[25]; + DLLNewUIGameWindowDestroy = (NewUIGameWindowDestroy_fp)API.fp[26]; + DLLNewUIGameWindowOpen = (NewUIGameWindowOpen_fp)API.fp[27]; + DLLNewUIGameWindowClose = (NewUIGameWindowClose_fp)API.fp[28]; + DLLSetScreenMode = (SetScreenMode_fp)API.fp[29]; + DLLtimer_GetTime = (timer_GetTime_fp)API.fp[30]; + DLLTryToJoinServer = (TryToJoinServer_fp)API.fp[31]; + DLLMultiStartClient = (MultiStartClient_fp)API.fp[32]; + DLLrend_GetRenderState = (rend_GetRenderState_fp)API.fp[33]; + DLLLoadMission = (LoadMission_fp)API.fp[34]; + DLLddio_MakePath = (ddio_MakePath_fp)API.fp[35]; + DLLddio_FindFileStart = (ddio_FindFileStart_fp)API.fp[36]; + DLLddio_FindFileClose = (ddio_FindFileClose_fp)API.fp[37]; + DLLddio_FindNextFile = (ddio_FindNextFile_fp)API.fp[38]; + DLLMultiStartServerFP = (MultiStartServer_fp)API.fp[39]; + DLLShowProgressScreen = (ShowProgressScreen_fp)API.fp[40]; + DLLSearchForLocalGamesTCP = (SearchForLocalGamesTCP_fp)API.fp[41]; + DLLnw_GetHostAddressFromNumbers = (nw_GetHostAddressFromNumbers_fp)API.fp[42]; + DLLnw_GetProtocolType = (nw_GetProtocolType_fp)API.fp[43]; + DLLHotSpotCreate = (HotSpotCreate_fp )API.fp[44]; + DLLPollUI = (PollUI_fp)API.fp[45]; + DLLGetMissionName = (GetMissionName_fp)API.fp[46]; + DLLRemoveUITextItem = (RemoveUITextItem_fp)API.fp[47]; + DLLCreateNewUITextItemFP = (CreateNewUITextItem_fp)API.fp[48]; + DLLmem_malloc = (mem_malloc_fp)API.fp[49]; + DLLmem_free =(mem_free_fp)API.fp[50]; + DLLCreateSplashScreen = (CreateSplashScreen_fp)API.fp[51]; + DLLCloseSplashScreen = (CloseSplashScreen_fp)API.fp[52]; + DLLUIConsoleGadgetCreate = (UIConsoleGadgetCreate_fp)API.fp[53]; + DLLUIConsoleGadgetputs = (UIConsoleGadgetputs_fp)API.fp[54]; + DLLNewUIWindowSetFocusOnEditGadget = (NewUIWindowSetFocusOnEditGadget_fp)API.fp[55]; + DLLOldEditCreate = (OldEditCreate_fp)API.fp[56]; + DLLOldListCreate = (OldListCreate_fp)API.fp[57]; + DLLOldListRemoveAll = (OldListRemoveAll_fp)API.fp[58]; + DLLOldListAddItem = (OldListAddItem_fp)API.fp[59]; + DLLOldListRemoveItem = (OldListRemoveItem_fp)API.fp[60]; + DLLOldListSelectItem = (OldListSelectItem_fp)API.fp[61]; + DLLOldListGetItem = (OldListGetItem_fp)API.fp[62]; + DLLOldListGetSelectedIndex = (OldListGetSelectedIndex_fp)API.fp[63]; + DLLOldEditSetText = (OldEditSetText_fp)API.fp[64]; + DLLOldEditGetText = (OldEditGetText_fp)API.fp[65]; + DLLToggleUICallback = (ToggleUICallback_fp)API.fp[66]; + DLLSearchForGamesPXO = (SearchForGamesPXO_fp)API.fp[67]; + DLLSetOldEditBufferLen = (SetOldEditBufferLen_fp)API.fp[68]; + DLLNewUIWindowLoadBackgroundImage = (NewUIWindowLoadBackgroundImage_fp)API.fp[69]; + DLLDeleteUIItem = (DeleteUIItem_fp)API.fp[70]; + DLLSearchForLocalGamesIPX = (SearchForLocalGamesIPX_fp)API.fp[71]; + DLLHotSpotSetStates = (HotSpotSetStates_fp)API.fp[72]; + DLLPlayerSetShipPermission = (PlayerSetShipPermission_fp)API.fp[73]; + DLLPlayerIsShipAllowed = (PlayerIsShipAllowed_fp)API.fp[74]; +#ifdef WIN32 + DLLdp_InitDirectPlay = (dp_InitDirectPlay_fp)API.fp[75]; + DLLdp_ListDirectPlayGames = (dp_ListDirectPlayGames_fp)API.fp[76]; + DLLdp_GetModemChoices = (dp_GetModemChoices_fp)API.fp[77]; +#endif + DLLDatabaseReadInt = (DatabaseReadInt_fp)API.fp[78]; + DLLDatabaseWriteInt = (DatabaseWriteInt_fp)API.fp[79]; + DLLDoScreenshot = (DoScreenshot_fp)API.fp[80]; + DLLIsMissionMultiPlayable = (IsMissionMultiPlayable_fp)API.fp[81]; + DLLgrtext_GetTextLineWidth = (grtext_GetTextLineWidth_fp)API.fp[82]; + DLLGadgetDestroy = (GadgetDestroy_fp)API.fp[83]; +#ifdef WIN32 + DLLdp_StartGame = (dp_StartGame_fp)API.fp[84]; + DLLdp_EndGame = (dp_EndGame_fp)API.fp[85]; +#endif + DLLnw_Asyncgethostbyname = (nw_Asyncgethostbyname_fp)API.fp[86]; + DLLPrintDedicatedMessage = (DedicatedServerPrintf_fp)API.fp[87]; + DLLnw_ReccomendPPS = (nw_ReccomendPPS_fp)API.fp[88]; + DLLDoMultiAllowed = (DoMultiAllowed_fp)API.fp[89]; + DLLMultiDoConfigSave = (MultiDoConfigSave_fp)API.fp[90]; + DLLMultiDoConfigLoad = (MultiDoConfigLoad_fp)API.fp[91]; + DLLMultiLoadSettings = (MultiLoadSettings_fp)API.fp[92]; + DLLnw_DoReceiveCallbacks = (nw_DoReceiveCallbacks_fp)API.fp[93]; + DLLnw_SendWithID = (nw_SendWithID_fp)API.fp[94]; + DLLnw_UnRegisterCallback = (nw_UnRegisterCallback_fp)API.fp[95]; + DLLnw_RegisterCallback = (nw_RegisterCallback_fp)API.fp[96]; + DLLmsn_CheckGetMission = (msn_CheckGetMission_fp)API.fp[97]; + DLLMultiGameOptionsMenu = (MultiGameOptionsMenu_fp)API.fp[98]; + DLLmod_FreeModule = (mod_FreeModule_fp)API.fp[99]; + DLLmod_GetLastError = (mod_GetLastError_fp)API.fp[100]; + DLLmod_GetSymbol = (mod_GetSymbol_fp)API.fp[101]; + DLLmod_LoadModule = (mod_LoadModule_fp)API.fp[102]; + CurrentPilotName = (CurrentPilotName_fp)API.fp[103]; + DLLUpdateAndPackGameList = (UpdateAndPackGameList_fp)API.fp[104]; + DLLMultiLevelSelection = (MultiLevelSelection_fp)API.fp[105]; + DLLDoPlayerMouselookCheck = (DoPlayerMouselookCheck_fp)API.fp[106]; + DLLCheckMissionForScript = (CheckMissionForScript_fp)API.fp[107]; + DLLShowNetgameInfo = (ShowNetgameInfo_fp)API.fp[108]; + //API.fp[109]; // Not used + DLLCheckGetD3M = (CheckGetD3M_fp)API.fp[110]; + + DLLMPlayers = (player *)API.players; + DLLNetgame = (netgame_info *)API.netgame; + DLLMNetPlayers=(netplayer *)API.netplayers; + DLLShips = (ship *)API.ships; + + DLLPlayer_num = *API.vp[0]; + DLLTracker_id = (char *)API.vp[1]; + DLLGame_is_master_tracker_game = API.vp[2]; + DLLGame_mode = *API.vp[3]; + //DLLCurrent_pilot = (pilot *)API.vp[4]; + DLLLocalD3Dir = (char *)(pilot *)API.vp[5]; + DLLMultiGameStarting = (int *)API.vp[6]; + DLLMTPilotinfo = (vmt_descent3_struct *)API.vp[7]; + DLLNum_network_games_known = API.vp[8]; + DLLNetwork_games = (network_game *)API.vp[9]; + DLLNewUIWindow_alpha = (ubyte *)API.vp[10]; + DLLHelpText1 = (char *)API.vp[11]; + DLLHelpText2 = (char *)API.vp[12]; + DLLHelpText3 = (char *)API.vp[13]; + DLLHelpText4 = (char *)API.vp[14]; + DLLAuto_login_name = (char *)API.vp[15]; + DLLAuto_login_pass = (char *)API.vp[16]; + DLLAuto_login_addr = (char *)API.vp[17]; + DLLAuto_login_port = (char *)API.vp[18]; + #ifdef WIN32 + DLLNum_directplay_games = (int *)API.vp[19]; + DLLDirectplay_sessions = (LPDPSESSIONDESC2)API.vp[20]; + DLLDP_active = (bool *)API.vp[21]; + DLLUse_DirectPlay = (bool *)API.vp[22]; + DLLModems_found = (modem_list *)API.vp[23]; + DLLNum_modems_found = (int *)API.vp[24]; + #else + DLLUse_DirectPlay = (bool *)API.vp[22]; + #endif + DLLDedicated_server = (bool *)API.vp[25]; + DLLTCP_active = (BOOL)*API.vp[26]; + DLLIPX_active = (BOOL)*API.vp[27]; + DLLnw_ListenPort = (unsigned short)((int)API.vp[28]&0xffff); + DLLMulti_Gamelist_changed = (bool *)API.vp[29]; + DLLPXO_hosted_lobby_name = (char *)API.vp[30]; + DLLSupports_score_api = (bool *)API.vp[31]; + //DLLPXOPort = (bool *)API.vp[32]; // only defined for mtclient because I'm lazy... + + *DLLSupports_score_api = false; diff --git a/netcon/inetfile/CFtp.cpp b/netcon/inetfile/CFtp.cpp new file mode 100644 index 000000000..7c4b4fdef --- /dev/null +++ b/netcon/inetfile/CFtp.cpp @@ -0,0 +1,685 @@ +/* +* $Logfile: /DescentIII/Main/inetfile/CFtp.cpp $ +* $Revision: 1.3 $ +* $Date: 2001/01/13 21:48:46 $ +* $Author: icculus $ +* +* FTP Client class (get only) +* +* $Log: CFtp.cpp,v $ +* Revision 1.3 2001/01/13 21:48:46 icculus +* patched to (re)compile on win32. +* +* Revision 1.2 2000/06/03 14:30:21 icculus +* 1.4 code merge and pthread->SDL thread conversion. +* +* Revision 1.1.1.1 2000/04/18 00:00:38 icculus +* initial checkin +* + * + * 8 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 7 9/08/99 6:37p Jeff + * fixed http/ftp downloading for Linux, should all work fine now. + * + * 6 8/22/99 12:32a Jeff + * fixed select calls for Linux. Ported Kevin's new http stuff to Linux + * + * 5 8/21/99 6:48a Jeff + * Linux port + * + * 4 4/14/99 1:20a Jeff + * fixed case mismatched #includes + * + * 3 7/31/98 11:40a Kevin + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/27/98 9:52a Kevin + * + * 1 5/25/98 5:31p Kevin + * Initial version +* +* $NoKeywords: $ +*/ + +#ifdef WIN32 +#include +#include +typedef int socklen_t; +#endif + +#include +#include +#include + +#ifdef __LINUX__ +//sorry, I'm lazy, I guess we could copy the defines +//that we need to transalte winsock->linux into this header...but no need to now +#include "SDL_thread.h" +#include "inetgetfile.h" +#endif + +#ifdef MACINTOSH +#include "macsock.h" +#endif + +#include "CFtp.h" + +#ifdef __LINUX__ +int FTPObjThread( void * obj ) +#else +void FTPObjThread( void * obj ) +#endif +{ + ((CFtpGet *)obj)->WorkerThread(); + #ifdef __LINUX__ + return 0; + #endif +} + +void CFtpGet::AbortGet() +{ + m_Aborting = true; + while(!m_Aborted) ; //Wait for the thread to end + fclose(LOCALFILE); +} + +CFtpGet::CFtpGet(char *URL,char *localfile,char *Username,char *Password) +{ + SOCKADDR_IN listensockaddr; + m_State = FTP_STATE_STARTUP; + + m_ListenSock = INVALID_SOCKET; + m_DataSock = INVALID_SOCKET; + m_ControlSock = INVALID_SOCKET; + m_iBytesIn = 0; + m_iBytesTotal = 0; + m_Aborting = false; + m_Aborted = false; + + LOCALFILE = fopen(localfile,"wb"); + if(NULL == LOCALFILE) + { + m_State = FTP_STATE_CANT_WRITE_FILE; + return; + } + + if(Username) + { + strcpy(m_szUserName,Username); + } + else + { + strcpy(m_szUserName,"anonymous"); + } + if(Password) + { + strcpy(m_szPassword,Password); + } + else + { + strcpy(m_szPassword,"pxouser@pxo.net"); + } + m_ListenSock = socket(AF_INET, SOCK_STREAM, 0); + if(INVALID_SOCKET == m_ListenSock) + { + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + else + { + listensockaddr.sin_family = AF_INET; + listensockaddr.sin_port = 0; + listensockaddr.sin_addr.s_addr = INADDR_ANY; + + // Bind the listen socket + if (bind(m_ListenSock, (SOCKADDR *)&listensockaddr, sizeof(SOCKADDR))) + { + //Couldn't bind the socket + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + + // Listen for the server connection + if (listen(m_ListenSock, 1)) + { + //Couldn't listen on the socket + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + } + m_ControlSock = socket(AF_INET, SOCK_STREAM, 0); + if(INVALID_SOCKET == m_ControlSock) + { + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + //Parse the URL + //Get rid of any extra ftp:// stuff + char *pURL = URL; + if(strnicmp(URL,"ftp:",4)==0) + { + pURL +=4; + while(*pURL == '/') + { + pURL++; + } + } + //There shouldn't be any : in this string + if(strchr(pURL,':')) + { + m_State = FTP_STATE_URL_PARSING_ERROR; + return; + } + //read the filename by searching backwards for a / + //then keep reading until you find the first / + //when you found it, you have the host and dir + char *filestart = NULL; + char *dirstart; + for(int i = strlen(pURL);i>=0;i--) + { + if(pURL[i]== '/') + { + if(!filestart) + { + filestart = pURL+i+1; + dirstart = pURL+i+1; + strcpy(m_szFilename,filestart); + } + else + { + dirstart = pURL+i+1; + } + } + } + if((dirstart==NULL) || (filestart==NULL)) + { + m_State = FTP_STATE_URL_PARSING_ERROR; + return; + } + else + { + strncpy(m_szDir,dirstart,(filestart-dirstart)); + m_szDir[(filestart-dirstart)] = '\0'; + strncpy(m_szHost,pURL,(dirstart-pURL)); + m_szHost[(dirstart-pURL)-1] = '\0'; + } + //At this point we should have a nice host,dir and filename + +#ifdef WIN32 + if(NULL==_beginthread(FTPObjThread,0,this)) + { + m_State = FTP_STATE_INTERNAL_ERROR; + return; + } +#elif defined(__LINUX__) +// pthread_t thread; + + SDL_Thread *thread; + + if(!inet_LoadThreadLib()) + { + m_State = FTP_STATE_INTERNAL_ERROR; + return; + } + +// if(df_pthread_create(&thread,NULL,FTPObjThread,this)!=0) + thread = SDL_CreateThread(FTPObjThread, this); + if (thread == NULL) + { + m_State = FTP_STATE_INTERNAL_ERROR; + return; + } +#endif + m_State = FTP_STATE_CONNECTING; +} + + + +CFtpGet::~CFtpGet() +{ + if(m_ListenSock != INVALID_SOCKET) + { + shutdown(m_ListenSock,2); + #ifndef __LINUX__ + closesocket(m_ListenSock); + #else + close(m_ListenSock); + #endif + } + if(m_DataSock != INVALID_SOCKET) + { + shutdown(m_DataSock,2); + #ifndef __LINUX__ + closesocket(m_DataSock); + #else + close(m_DataSock); + #endif + } + if(m_ControlSock != INVALID_SOCKET) + { + shutdown(m_ControlSock,2); + #ifndef __LINUX__ + closesocket(m_ControlSock); + #else + close(m_ControlSock); + #endif + } + + +} + +//Returns a value to specify the status (ie. connecting/connected/transferring/done) +int CFtpGet::GetStatus() +{ + return m_State; +} + +unsigned int CFtpGet::GetBytesIn() +{ + return m_iBytesIn; +} + +unsigned int CFtpGet::GetTotalBytes() +{ + + return m_iBytesTotal; +} + +//This function does all the work -- connects on a blocking socket +//then sends the appropriate user and password commands +//and then the cwd command, the port command then get and finally the quit +void CFtpGet::WorkerThread() +{ + ConnectControlSocket(); + if(m_State != FTP_STATE_LOGGING_IN) + { + return; + } + LoginHost(); + if(m_State != FTP_STATE_LOGGED_IN) + { + return; + } + GetFile(); + + //We are all done now, and state has the current state. + m_Aborted = true; + + +} + +unsigned int CFtpGet::GetFile() +{ + + //Start off by changing into the proper dir. + char szCommandString[200]; + int rcode; + + sprintf(szCommandString,"TYPE I\r\n"); + rcode = SendFTPCommand(szCommandString); + if(rcode >=400) + { + m_State = FTP_STATE_UNKNOWN_ERROR; + return 0; + } + if(m_Aborting) + return 0; + if(m_szDir[0]) + { + sprintf(szCommandString,"CWD %s\r\n",m_szDir); + rcode = SendFTPCommand(szCommandString); + if(rcode >=400) + { + m_State = FTP_STATE_DIRECTORY_INVALID; + return 0; + } + } + if(m_Aborting) + return 0; + if(!IssuePort()) + { + m_State = FTP_STATE_UNKNOWN_ERROR; + return 0; + } + if(m_Aborting) + return 0; + sprintf(szCommandString,"RETR %s\r\n",m_szFilename); + rcode = SendFTPCommand(szCommandString); + if(rcode >=400) + { + m_State = FTP_STATE_FILE_NOT_FOUND; + return 0; + } + if(m_Aborting) + return 0; + //Now we will try to determine the file size... + char *p,*s; + p = strchr(recv_buffer,'('); + if(p) + { + p++; + s = strchr(p,' '); + *s = '\0'; + m_iBytesTotal = atoi(p); + } + if(m_Aborting) + return 0; + + m_DataSock = accept(m_ListenSock, NULL,NULL);//(SOCKADDR *)&sockaddr,&iAddrLength); + // Close the listen socket +#ifndef __LINUX__ + closesocket(m_ListenSock); +#else + close(m_ListenSock); +#endif + if (m_DataSock == INVALID_SOCKET) + { + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + if(m_Aborting) + return 0; + + ReadDataChannel(); + + m_State = FTP_STATE_FILE_RECEIVED; + return 1; +} + +unsigned int CFtpGet::IssuePort() +{ + + char szCommandString[200]; + SOCKADDR_IN listenaddr; // Socket address structure + socklen_t iLength; // Length of the address structure + unsigned int nLocalPort; // Local port for listening + unsigned int nReplyCode; // FTP server reply code + + + // Get the address for the hListenSocket + iLength = sizeof(listenaddr); + if (getsockname(m_ListenSock, (SOCKADDR *)&listenaddr,&iLength) == SOCKET_ERROR) + { + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + + // Extract the local port from the hListenSocket + nLocalPort = listenaddr.sin_port; + + // Now, reuse the socket address structure to + // get the IP address from the control socket. + if (getsockname(m_ControlSock, (SOCKADDR *)&listenaddr,&iLength) == SOCKET_ERROR) + { + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + + #ifdef WIN32 + // Format the PORT command with the correct numbers. + sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n", + listenaddr.sin_addr.S_un.S_un_b.s_b1, + listenaddr.sin_addr.S_un.S_un_b.s_b2, + listenaddr.sin_addr.S_un.S_un_b.s_b3, + listenaddr.sin_addr.S_un.S_un_b.s_b4, + nLocalPort & 0xFF, + nLocalPort >> 8); + #else + union{ + struct{ unsigned char s_b1,s_b2,s_b3,s_b4;}S_un_b; + struct{ unsigned short s_w1,s_w2;} S_un_w; + unsigned int S_addr; + }S_un; + + S_un.S_addr = (unsigned int)listenaddr.sin_addr.s_addr; + // Format the PORT command with the correct numbers. + sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n", + S_un.S_un_b.s_b1, + S_un.S_un_b.s_b2, + S_un.S_un_b.s_b3, + S_un.S_un_b.s_b4, + nLocalPort & 0xFF, + nLocalPort >> 8); + #endif + + // Tell the server which port to use for data. + nReplyCode = SendFTPCommand(szCommandString); + if (nReplyCode!= 200 ) + { + #ifdef __LINUX__ + // I don't know if this is just Linux or do to a bug I fixed while porting to linux + // for some reason I kept getting reply 250 here and have to read again to get the + // "200 PORT Command OK" or whatever + if(nReplyCode!=250 || (ReadFTPServerReply()!=200))//ummmmmmmm + #endif + { + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + } + + return 1; +} + +int CFtpGet::ConnectControlSocket() +{ + HOSTENT *he; + SERVENT *se; + SOCKADDR_IN hostaddr; + he = gethostbyname(m_szHost); + if(he == NULL) + { + m_State = FTP_STATE_HOST_NOT_FOUND; + return 0; + } + //m_ControlSock + if(m_Aborting) + return 0; + se = getservbyname("ftp", NULL); + + if(se == NULL) + { + hostaddr.sin_port = htons(21); + } + else + { + hostaddr.sin_port = se->s_port; + } + hostaddr.sin_family = AF_INET; + memcpy(&hostaddr.sin_addr,he->h_addr_list[0],4); + if(m_Aborting) + return 0; + //Now we will connect to the host + if(connect(m_ControlSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR))) + { + int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_CANT_CONNECT; + return 0; + } + m_State = FTP_STATE_LOGGING_IN; + return 1; +} + + +int CFtpGet::LoginHost() +{ + char szLoginString[200]; + int rcode; + + sprintf(szLoginString,"USER %s\r\n",m_szUserName); + rcode = SendFTPCommand(szLoginString); + if(rcode >=400) + { + m_State = FTP_STATE_LOGIN_ERROR; + return 0; + } + sprintf(szLoginString,"PASS %s\r\n",m_szPassword); + rcode = SendFTPCommand(szLoginString); + if(rcode >=400) + { + m_State = FTP_STATE_LOGIN_ERROR; + return 0; + } + + m_State = FTP_STATE_LOGGED_IN; + return 1; +} + + +unsigned int CFtpGet::SendFTPCommand(char *command) +{ + + FlushControlChannel(); + // Send the FTP command + if (SOCKET_ERROR ==(send(m_ControlSock,command,strlen(command), 0))) + { + int iWinsockErr = WSAGetLastError(); + // Return 999 to indicate an error has occurred + return(999); + } + + // Read the server's reply and return the reply code as an integer + return(ReadFTPServerReply()); +} + + + +unsigned int CFtpGet::ReadFTPServerReply() +{ + unsigned int rcode; + unsigned int iBytesRead; + char chunk[2]; + char szcode[5]; + unsigned int igotcrlf = 0; + memset(recv_buffer,0,1000); + do + { + chunk[0]='\0'; + iBytesRead = recv(m_ControlSock,chunk,1,0); + + if (iBytesRead == SOCKET_ERROR) + { + int iWinsockErr = WSAGetLastError(); + #ifdef __LINUX__ + if(0==iWinsockErr) + { + continue; + } + #endif + // Return 999 to indicate an error has occurred + return(999); + } + + if((chunk[0]==0x0a) || (chunk[0]==0x0d)) + { + if(recv_buffer[0]!='\0') + { + igotcrlf = 1; + } + } + else + { chunk[1] = '\0'; + strcat(recv_buffer,chunk); + } + + + }while(igotcrlf==0); + + if(recv_buffer[3] == '-') + { + //Hack -- must be a MOTD + return ReadFTPServerReply(); + } + if(recv_buffer[3] != ' ') + { + //We should have 3 numbers then a space + return 999; + } + memcpy(szcode,recv_buffer,3); + szcode[3] = '\0'; + rcode = atoi(szcode); + // Extract the reply code from the server reply and return as an integer + return(rcode); +} + + +unsigned int CFtpGet::ReadDataChannel() +{ + char sDataBuffer[4096]; // Data-storage buffer for the data channel + int nBytesRecv; // Bytes received from the data channel + m_State = FTP_STATE_RECEIVING; + if(m_Aborting) + return 0; + do + { + if(m_Aborting) + return 0; + nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0); + + #ifdef __LINUX__ + if(nBytesRecv==-1) + { + int iWinsockErr = WSAGetLastError(); + if(iWinsockErr==0) + { + nBytesRecv = 1; + continue; + } + } + #endif + + m_iBytesIn += nBytesRecv; + if (nBytesRecv > 0 ) + { + fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE); + //Write sDataBuffer, nBytesRecv + } + }while (nBytesRecv > 0); + fclose(LOCALFILE); + // Close the file and check for error returns. + if (nBytesRecv == SOCKET_ERROR) + { + //Ok, we got a socket error -- xfer aborted? + m_State = FTP_STATE_RECV_FAILED; + return 0; + } + else + { + //done! + m_State = FTP_STATE_FILE_RECEIVED; + return 1; + } +} + + +void CFtpGet::FlushControlChannel() +{ + fd_set read_fds; + TIMEVAL timeout; + int bytesin = 0; + char flushbuff[3]; + + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(m_ControlSock,&read_fds); + + while(select(m_ControlSock+1,&read_fds,NULL,NULL,&timeout)) + { + recv(m_ControlSock,flushbuff,1,0); + } + +} \ No newline at end of file diff --git a/netcon/inetfile/CMakeLists.txt b/netcon/inetfile/CMakeLists.txt new file mode 100644 index 000000000..2cb794f88 --- /dev/null +++ b/netcon/inetfile/CMakeLists.txt @@ -0,0 +1,9 @@ +SET (HEADERS ) + +SET (CPPS + CFtp.cpp + Chttpget.cpp + inetgetfile.cpp +) + +ADD_LIBRARY(inetfile STATIC ${HEADERS} ${CPPS}) diff --git a/netcon/inetfile/Chttpget.cpp b/netcon/inetfile/Chttpget.cpp new file mode 100644 index 000000000..c95a8bb02 --- /dev/null +++ b/netcon/inetfile/Chttpget.cpp @@ -0,0 +1,892 @@ +/* +* $Logfile: /DescentIII/Main/inetfile/Chttpget.cpp $ +* $Revision: 1.4 $ +* $Date: 2001/01/13 21:48:46 $ +* $Author: icculus $ +* +* HTTP Client class (get only) +* +* $Log: Chttpget.cpp,v $ +* Revision 1.4 2001/01/13 21:48:46 icculus +* patched to (re)compile on win32. +* +* Revision 1.3 2000/06/29 06:41:23 icculus +* mad commits. +* +* Revision 1.2 2000/06/03 14:30:21 icculus +* 1.4 code merge and pthread->SDL thread conversion. +* +* Revision 1.1.1.1 2000/04/18 00:00:38 icculus +* initial checkin +* + * + * 26 10/22/99 3:40p Kevin + * mac merge fixes + * + * 25 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 24 9/08/99 6:37p Jeff + * fixed http/ftp downloading for Linux, should all work fine now. + * + * 23 8/22/99 12:32a Jeff + * fixed select calls for Linux. Ported Kevin's new http stuff to Linux + * + * 22 8/21/99 9:14p Kevin + * Added support for redirection + * + * 21 8/21/99 6:33p Kevin + * Fixed Proxy Stuff + * + * 20 8/21/99 6:48a Jeff + * Linux port + * + * 19 8/20/99 3:01p Kevin + * Added support for Proxies (I hope!) + * + * 18 8/15/99 6:38p Jeff + * fixed compile error + * + * 17 8/15/99 6:26p Kevin + * + * 16 4/14/99 1:20a Jeff + * fixed case mismatched #includes + * + * 15 3/03/99 12:28a Nate + * sped up something or other when the connection is done + * + * 14 2/03/99 4:20p Kevin + * Got multiplayer working with .mn3 files, and setup autodownloading + * + * 13 1/27/99 5:49p Kevin + * + * 12 1/27/99 5:38p Kevin + * + * 11 12/30/98 12:15p Kevin + * Auto Mission Download system + * + * 10 10/12/98 4:59p Kevin + * Added delay to thread when cancelled... + * + * 9 10/12/98 4:49p Nate + * More fixes + * + * 8 10/12/98 1:54p Nate + * Fixed bug + * + * 7 10/12/98 11:30a Kevin + * More memory stuff + * + * 6 10/08/98 12:59p Nate + * fixed cancel + * + * 5 10/08/98 9:57a Kevin + * made transfer cancellable + * + * 4 7/31/98 12:19p Nate + * Fixed http abort problem. + * + * 3 7/31/98 11:57a Kevin + * Added new functions for getting state + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/27/98 9:52a Kevin + * + * 1 5/25/98 5:31p Kevin + * Initial version +* +* $NoKeywords: $ +*/ + +#ifdef WIN32 +#include +#include +#endif + +#ifdef MACINTOSH +#include "macsock.h" +#endif + +#include +#include +#include + +#include "inetgetfile.h" +#include "Chttpget.h" + +#ifndef WIN32 +#include "mem.h" +#else +#define mem_malloc(a) malloc(a) +#define mem_free(a) free(a) +#endif + +#ifdef __LINUX__ + +#include "SDL_thread.h" + +inline void Sleep(int millis) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = millis*1000; + select(0,NULL,NULL,NULL,&tv); +} +#endif + +#define NW_AGHBN_CANCEL 1 +#define NW_AGHBN_LOOKUP 2 +#define NW_AGHBN_READ 3 + +#ifndef __LINUX__ +void __cdecl http_gethostbynameworker(void *parm); +#else +int http_gethostbynameworker(void *parm); +#endif + +int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname); + +#ifndef __LINUX__ +void HTTPObjThread( void * obj ) +#else +int HTTPObjThread( void * obj ) +#endif +{ + ((ChttpGet *)obj)->WorkerThread(); + ((ChttpGet *)obj)->m_Aborted = true; + //OutputDebugString("http transfer exiting....\n"); + + #ifdef __LINUX__ + return 0; + #endif +} + +void ChttpGet::AbortGet() +{ +#ifdef WIN32 + OutputDebugString("Aborting....\n"); +#endif + m_Aborting = true; + while(!m_Aborted) Sleep(50); //Wait for the thread to end +#ifdef WIN32 + OutputDebugString("Aborted....\n"); +#endif +} + +ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport) +{ + m_ProxyEnabled = true; + m_ProxyIP = proxyip; + m_ProxyPort = proxyport; + GetFile(URL,localfile); +} + +ChttpGet::ChttpGet(char *URL,char *localfile) +{ + m_ProxyEnabled = false; + GetFile(URL,localfile); +} + +void ChttpGet::PrepSocket(char *URL) +{ + + m_DataSock = socket(AF_INET, SOCK_STREAM, 0); + if(INVALID_SOCKET == m_DataSock) + { + m_State = HTTP_STATE_SOCKET_ERROR; + return; + } + unsigned long arg; + + arg = true; +#if defined(WIN32) + ioctlsocket( m_DataSock, FIONBIO, &arg ); +#elif defined(__LINUX__) + ioctl( m_DataSock, FIONBIO, &arg ); +#endif + + char *pURL = URL; + if(strnicmp(URL,"http:",5)==0) + { + pURL +=5; + while(*pURL == '/') + { + pURL++; + } + } + //There shouldn't be any : in this string + if(strchr(pURL,':')) + { + m_State = HTTP_STATE_URL_PARSING_ERROR; + return; + } + //read the filename by searching backwards for a / + //then keep reading until you find the first / + //when you found it, you have the host and dir + char *filestart = NULL; + char *dirstart; + for(int i = strlen(pURL);i>=0;i--) + { + if(pURL[i]== '/') + { + if(!filestart) + { + filestart = pURL+i+1; + dirstart = pURL+i+1; + strcpy(m_szFilename,filestart); + } + else + { + dirstart = pURL+i+1; + } + } + } + if((dirstart==NULL) || (filestart==NULL)) + { + m_State = HTTP_STATE_URL_PARSING_ERROR; + return; + } + else + { + strcpy(m_szDir,dirstart);//,(filestart-dirstart)); + //m_szDir[(filestart-dirstart)] = NULL; + strncpy(m_szHost,pURL,(dirstart-pURL)); + m_szHost[(dirstart-pURL)-1] = '\0'; + } + +} + + +void ChttpGet::GetFile(char *URL,char *localfile) +{ + m_DataSock = INVALID_SOCKET; + m_iBytesIn = 0; + m_iBytesTotal = 0; + m_State = HTTP_STATE_STARTUP;; + m_Aborting = false; + m_Aborted = false; + + strncpy(m_URL,URL,MAX_URL_LEN-1); + m_URL[MAX_URL_LEN-1] = 0; + + LOCALFILE = fopen(localfile,"wb"); + if(NULL == LOCALFILE) + { + m_State = HTTP_STATE_CANT_WRITE_FILE; + return; + } + + PrepSocket(URL); + +#ifdef WIN32 + if(NULL==_beginthread(HTTPObjThread,0,this)) + { + m_State = HTTP_STATE_INTERNAL_ERROR; + return; + } +#elif defined(__LINUX__) +// pthread_t thread; + SDL_Thread *thread; + + if(!inet_LoadThreadLib()) + { + m_State = HTTP_STATE_INTERNAL_ERROR; + return; + } + +// if(df_pthread_create(&thread,NULL,HTTPObjThread,this)!=0) + thread = SDL_CreateThread(HTTPObjThread, this); + if (thread == NULL) + { + m_State = HTTP_STATE_INTERNAL_ERROR; + return; + } +#endif +} + + +ChttpGet::~ChttpGet() +{ + if(m_DataSock != INVALID_SOCKET) + { + shutdown(m_DataSock,2); +#ifndef __LINUX__ + closesocket(m_DataSock); +#else + close(m_DataSock); +#endif + } +} + +int ChttpGet::GetStatus() +{ + return m_State; +} + +unsigned int ChttpGet::GetBytesIn() +{ + return m_iBytesIn; +} + +unsigned int ChttpGet::GetTotalBytes() +{ + return m_iBytesTotal; +} + + +void ChttpGet::WorkerThread() +{ + char szCommand[1000]; + char *p; + int irsp = 0; + ConnectSocket(); + if(m_Aborting) + { + fclose(LOCALFILE); + return; + } + if(m_State != HTTP_STATE_CONNECTED) + { + fclose(LOCALFILE); + return; + } + sprintf(szCommand,"GET %s%s HTTP/1.1\nAccept: */*\nAccept-Encoding: deflate\nHost: %s\n\n\n",m_ProxyEnabled?"":"/",m_ProxyEnabled?m_URL:m_szDir,m_szHost); + send(m_DataSock,szCommand,strlen(szCommand),0); + p = GetHTTPLine(); + if(p && strnicmp("HTTP/",p,5)==0) + { + char *pcode; + pcode = strchr(p,' ')+1; + if(!pcode) + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + + } + pcode[3] = '\0'; + irsp = atoi(pcode); + + if(irsp == 0) + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + } + if(irsp == 301) + { + + //This is a redirection! woohoo, what fun we are going to have. + //Next thing we need to do is find where it's redirected to. + //We do that by looking for a "Location: xxxx" line. + + int idataready=0; + do + { + p = GetHTTPLine(); + if(p==NULL) + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + } + if(*p=='\0') + { + idataready = 1; + break; + } + if(strnicmp(p,"Location:",strlen("Location:"))==0) + { + char *s = strchr(p,' ')+1; + + + //Then, once we've found that, we close the sockets & reissue the whole freakin request. + shutdown(m_DataSock,2); + + #ifdef WIN32 + closesocket(m_DataSock); + #else + close(m_DataSock); + #endif + + m_DataSock = INVALID_SOCKET; + + //New location to look at is in 's' + PrepSocket(s); + WorkerThread(); + return; + } + }while(!idataready); + + + } + if(irsp==200) + { + int idataready=0; + do + { + p = GetHTTPLine(); + if(p==NULL) + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + } + if(*p=='\0') + { + idataready = 1; + break; + } + if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0) + { + char *s = strchr(p,' ')+1; + p = s; + if(s) + { + while(*s) + { + if(!isdigit(*s)) + { + *s='\0'; + } + s++; + }; + m_iBytesTotal = atoi(p); + } + + } + }while(!idataready); + ReadDataChannel(); + return; + } + m_State = HTTP_STATE_FILE_NOT_FOUND; + fclose(LOCALFILE); + return; + } + else + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + } +} + +int ChttpGet::ConnectSocket() +{ + //HOSTENT *he; + unsigned int ip; + SERVENT *se; + SOCKADDR_IN hostaddr; + + int rcode = 0; + + if(m_Aborting) + return 0; + + ip = inet_addr((const char *)m_szHost); + + if(ip==INADDR_NONE) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_szHost); + rcode = 0; + do + { + if(m_Aborting) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_szHost); + return 0; + } + rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_szHost); + }while(rcode==0); + } + + if(rcode == -1) + { + m_State = HTTP_STATE_HOST_NOT_FOUND; + return 0; + } + //m_ControlSock + if(m_Aborting) + return 0; + se = getservbyname("http", NULL); + if(m_Aborting) + return 0; + if(se == NULL) + { + hostaddr.sin_port = htons(80); + } + else + { + hostaddr.sin_port = se->s_port; + } + hostaddr.sin_family = AF_INET; + //ip = htonl(ip); + memcpy(&hostaddr.sin_addr,&ip,4); + + if(m_ProxyEnabled) + { + //This is on a proxy, so we need to make sure to connect to the proxy machine + ip = inet_addr((const char *)m_ProxyIP); + + if(ip==INADDR_NONE) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_ProxyIP); + rcode = 0; + do + { + if(m_Aborting) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_ProxyIP); + return 0; + } + rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_ProxyIP); + }while(rcode==0); + + + if(rcode == -1) + { + m_State = HTTP_STATE_HOST_NOT_FOUND; + return 0; + } + + } + //Use either the proxy port or 80 if none specified + hostaddr.sin_port = htons(m_ProxyPort?m_ProxyPort:80); + //Copy the proxy address... + memcpy(&hostaddr.sin_addr,&ip,4); + + } + //Now we will connect to the host + fd_set wfds; + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)); + int cerr = WSAGetLastError(); + if(serr) + { + #ifdef __LINUX__ + while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK)||(cerr==EINPROGRESS)) + #else + while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK)) + #endif + { + FD_ZERO(&wfds); + FD_SET( m_DataSock, &wfds ); + if(select(m_DataSock+1,NULL,&wfds,NULL,&timeout)) + { + serr = 0; + break; + } + if(m_Aborting) + return 0; + serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)); + if(serr == 0) + break; + cerr = WSAGetLastError(); + if(cerr==WSAEISCONN) + { + serr = 0; + break; + } + }; + } + if(serr) + { + m_State = HTTP_STATE_CANT_CONNECT; + return 0; + } + m_State = HTTP_STATE_CONNECTED; + return 1; +} + +char *ChttpGet::GetHTTPLine() +{ + unsigned int iBytesRead; + char chunk[2]; + unsigned int igotcrlf = 0; + memset(recv_buffer,0,1000); + do + { + chunk[0]='\0'; + bool gotdata = false; + do + { + iBytesRead = recv(m_DataSock,chunk,1,0); + + if(SOCKET_ERROR == iBytesRead) + { + int error = WSAGetLastError(); + #ifdef __LINUX__ + if(WSAEWOULDBLOCK==error || 0==error) + #else + if(WSAEWOULDBLOCK==error) + #endif + { + gotdata = false; + continue; + } + else + { + return NULL; + } + } + else + { + gotdata = true; + } + }while(!gotdata); + + if(chunk[0]==0x0d) + { + //This should always read a 0x0a + do + { + iBytesRead = recv(m_DataSock,chunk,1,0); + + if(SOCKET_ERROR == iBytesRead) + { + int error = WSAGetLastError(); + #ifdef __LINUX__ + if(WSAEWOULDBLOCK==error || 0==error) + #else + if(WSAEWOULDBLOCK==error) + #endif + { + gotdata = false; + continue; + } + else + { + return NULL; + } + } + else + { + gotdata = true; + } + }while(!gotdata); + igotcrlf = 1; + } + else + { chunk[1] = '\0'; + strcat(recv_buffer,chunk); + } + + + }while(igotcrlf==0); + return recv_buffer; +} + +unsigned int ChttpGet::ReadDataChannel() +{ + char sDataBuffer[4096]; // Data-storage buffer for the data channel + int nBytesRecv; // Bytes received from the data channel + + fd_set wfds; + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 500; + + m_State = HTTP_STATE_RECEIVING; + do + { + FD_ZERO(&wfds); + FD_SET( m_DataSock, &wfds ); + + if((m_iBytesTotal)&&(m_iBytesIn==m_iBytesTotal)) + { + break; + } + + select(m_DataSock+1,&wfds,NULL,NULL,&timeout); + + if(m_Aborting) + { + fclose(LOCALFILE); + return 0; + } + + nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0); + + if(m_Aborting) + { + fclose(LOCALFILE); + return 0; + } + + if(SOCKET_ERROR == nBytesRecv) + { + int error = WSAGetLastError(); + #ifdef __LINUX__ + if(WSAEWOULDBLOCK==error || 0==error) + #else + if(WSAEWOULDBLOCK==error) + #endif + { + nBytesRecv = 1; + continue; + } + } + m_iBytesIn += nBytesRecv; + if (nBytesRecv > 0 ) + { + fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE); + //Write sDataBuffer, nBytesRecv + } + + + }while (nBytesRecv > 0); + + fclose(LOCALFILE); + + // Close the file and check for error returns. + if (nBytesRecv == SOCKET_ERROR) + { + //Ok, we got a socket error -- xfer aborted? + m_State = HTTP_STATE_RECV_FAILED; + return 0; + } + else + { + //OutputDebugString("HTTP File complete!\n"); + //done! + m_State = HTTP_STATE_FILE_RECEIVED; + return 1; + } +} + + +typedef struct _async_dns_lookup +{ + unsigned int ip; //resolved host. Write only to worker thread. + char * host;//host name to resolve. read only to worker thread + bool done; //write only to the worker thread. Signals that the operation is complete + bool error; //write only to worker thread. Thread sets this if the name doesn't resolve + bool abort; //read only to worker thread. If this is set, don't fill in the struct. + + #ifdef __LINUX__ + SDL_Thread *threadId; + #endif +}async_dns_lookup; + +async_dns_lookup httpaslu; +async_dns_lookup *http_lastaslu = NULL; + +#ifndef __LINUX__ +void __cdecl http_gethostbynameworker(void *parm); +#else +int http_gethostbynameworker(void *parm); +#endif + +int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname) +{ + + if(command==NW_AGHBN_LOOKUP) + { + if(http_lastaslu) + http_lastaslu->abort = true; + + async_dns_lookup *newaslu; + newaslu = (async_dns_lookup *)mem_malloc(sizeof(async_dns_lookup)); + memset(&newaslu->ip,0,sizeof(unsigned int)); + newaslu->host = hostname; + newaslu->done = false; + newaslu->error = false; + newaslu->abort = false; + http_lastaslu = newaslu; + httpaslu.done = false; + +#ifdef WIN32 + _beginthread(http_gethostbynameworker,0,newaslu); +#elif defined(__LINUX__) +// pthread_t thread; + if(!inet_LoadThreadLib()) + { + return 0; + } + +// df_pthread_create(&thread,NULL,http_gethostbynameworker,newaslu); + newaslu->threadId = SDL_CreateThread(http_gethostbynameworker,newaslu); +#endif + return 1; + } + else if(command==NW_AGHBN_CANCEL) + { + if(http_lastaslu) + http_lastaslu->abort = true; + + #ifdef __LINUX__ + SDL_WaitThread(http_lastaslu->threadId, NULL); + #endif + + http_lastaslu = NULL; + } + else if(command==NW_AGHBN_READ) + { + if(!http_lastaslu) + return -1; + if(httpaslu.done) + { + //free(http_lastaslu); + #ifdef __LINUX__ + SDL_WaitThread(http_lastaslu->threadId, NULL); + #endif + + http_lastaslu = NULL; + memcpy(ip,&httpaslu.ip,sizeof(unsigned int)); + return 1; + } + else if(httpaslu.error) + { + #ifdef __LINUX__ + SDL_WaitThread(http_lastaslu->threadId, NULL); + #endif + + mem_free(http_lastaslu); + http_lastaslu = NULL; + return -1; + } + else return 0; + } + return -2; + +} + +// This is the worker thread which does the lookup. +#ifndef __LINUX__ +void __cdecl http_gethostbynameworker(void *parm) +#else +int http_gethostbynameworker(void *parm) +#endif +{ +#ifdef __LINUX__ + //df_pthread_detach(df_pthread_self()); +#endif + async_dns_lookup *lookup = (async_dns_lookup *)parm; + HOSTENT *he = gethostbyname(lookup->host); + if(he==NULL) + { + lookup->error = true; + #ifdef __LINUX__ + return NULL; + #else + return; + #endif + } + else if(!lookup->abort) + { + memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int)); + lookup->done = true; + memcpy(&httpaslu,lookup,sizeof(async_dns_lookup)); + } + mem_free(lookup); + +#ifdef __LINUX__ + return NULL; +#endif +} diff --git a/netcon/inetfile/inetgetfile.cpp b/netcon/inetfile/inetgetfile.cpp new file mode 100644 index 000000000..9886ab703 --- /dev/null +++ b/netcon/inetfile/inetgetfile.cpp @@ -0,0 +1,386 @@ +/* +* $Logfile: /DescentIII/Main/inetfile/inetgetfile.cpp $ +* $Revision: 1.2 $ +* $Date: 2000/06/03 14:33:51 $ +* $Author: icculus $ +* +* InternetGetFile Class +* +* $Log: inetgetfile.cpp,v $ +* Revision 1.2 2000/06/03 14:33:51 icculus +* Merge with Outrage 1.4 tree... +* +* Revision 1.1.1.1 2000/04/18 00:00:38 icculus +* initial checkin +* + * + * 10 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 9 9/08/99 6:37p Jeff + * fixed http/ftp downloading for Linux, should all work fine now. + * + * 8 8/23/99 5:12p Kevin + * Proxy support for http + * + * 7 8/21/99 6:48a Jeff + * Linux port + * + * 6 4/14/99 1:20a Jeff + * fixed case mismatched #includes + * + * 5 7/31/98 11:57a Kevin + * Added new functions for getting state + * + * 4 7/31/98 11:40a Kevin + * + * 3 7/31/98 11:17a Nate + * Fixed memory leak and added memory alloc checking. + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/27/98 9:52a Kevin + * + * 1 5/25/98 5:31p Kevin + * Initial version +* +* $NoKeywords: $ +*/ + +#if defined(WIN32) +#include +#endif + +#include +#include +#include + +#ifdef MACINTOSH +#include "macsock.h" +#endif + +// include inetgetfile.h before CFtp.h and Chttpget.h because it has some winsock defines +// to make Linux happy +#include "inetgetfile.h" +#include "CFtp.h" +#include "Chttpget.h" + +extern char *Proxy_server; +extern short Proxy_port; + + +#ifdef __LINUX__ +/* ryan sez: use SDL. +pthread_create_fp df_pthread_create = NULL; +pthread_exit_fp df_pthread_exit = NULL; +pthread_detach_fp df_pthread_detach = NULL; +pthread_self_fp df_pthread_self = NULL; +*/ +bool inet_LoadThreadLib(void) +{ + return true; +/* + if(df_pthread_create) + return true; + + void *lib; + + lib = dlopen("libpthread.so",RTLD_GLOBAL|RTLD_NOW); + if(!lib) + { + return false; + } + + df_pthread_create = (pthread_create_fp)dlsym(lib,"pthread_create"); + df_pthread_detach = (pthread_detach_fp)dlsym(lib,"pthread_detach"); + df_pthread_self = (pthread_self_fp)dlsym(lib,"pthread_self"); + + if(!df_pthread_create || !df_pthread_detach || !df_pthread_self) + { + df_pthread_create = NULL; //this variable I use to see if the library is loaded + return false; + } + + // don't close the library...we need it open for future use + // and we can't close it on atexit() because linux will segfault for some stupid reason + return true; +*/ +} +#endif + +void InetGetFile::AbortGet() +{ + if(m_bUseHTTP) + { + http->AbortGet(); + } + else + { + ftp->AbortGet(); + } +} + +InetGetFile::InetGetFile(char *URL,char *localfile,char *proxyip,short proxyport) +{ + m_HardError = 0; + http=NULL; + ftp=NULL; + if((URL==NULL)||(localfile==NULL)) + { + m_HardError = INET_ERROR_BADPARMS; + } + if(strstr(URL,"http:")) + { + m_bUseHTTP = true; + http = new ChttpGet(URL,localfile,proxyip,proxyport); + if(http==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else if(strstr(URL,"HTTP:")) + { + m_bUseHTTP = true; + http = new ChttpGet(URL,localfile,proxyip,proxyport); + if(http==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else if(strstr(URL,"FTP:")) + { + m_bUseHTTP = false; + ftp = new CFtpGet(URL,localfile); + if(ftp==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else if(strstr(URL,"ftp:")) + { + m_bUseHTTP = false; + ftp = new CFtpGet(URL,localfile); + if(ftp==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else + { + m_HardError = INET_ERROR_CANT_PARSE_URL; + } + Sleep(1000); +} + +InetGetFile::InetGetFile(char *URL,char *localfile) +{ + m_HardError = 0; + http=NULL; + ftp=NULL; + if((URL==NULL)||(localfile==NULL)) + { + m_HardError = INET_ERROR_BADPARMS; + } + if(strstr(URL,"http:")) + { + m_bUseHTTP = true; + http = new ChttpGet(URL,localfile); + if(http==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else if(strstr(URL,"HTTP:")) + { + m_bUseHTTP = true; + http = new ChttpGet(URL,localfile); + if(http==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else if(strstr(URL,"FTP:")) + { + m_bUseHTTP = false; + ftp = new CFtpGet(URL,localfile); + if(ftp==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else if(strstr(URL,"ftp:")) + { + m_bUseHTTP = false; + ftp = new CFtpGet(URL,localfile); + if(ftp==NULL) + m_HardError = INET_ERROR_NO_MEMORY; + } + else + { + m_HardError = INET_ERROR_CANT_PARSE_URL; + } + Sleep(1000); +} + +InetGetFile::~InetGetFile() +{ + if(http!=NULL) delete http; + if(ftp!=NULL) delete ftp; +} + +BOOL InetGetFile::IsConnecting() +{ + int state; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else if(ftp) + { + state = ftp->GetStatus(); + } + else + { + return false; + } + if(state == FTP_STATE_CONNECTING) + { + return true; + } + else + { + return false; + } + +} + +BOOL InetGetFile::IsReceiving() +{ + int state; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else if(ftp) + { + state = ftp->GetStatus(); + } + if(state == FTP_STATE_RECEIVING) + { + return true; + } + else + { + return false; + } +} + +BOOL InetGetFile::IsFileReceived() +{ + int state; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else if(ftp) + { + state = ftp->GetStatus(); + } + if(state == FTP_STATE_FILE_RECEIVED) + { + return true; + } + else + { + return false; + } +} + +BOOL InetGetFile::IsFileError() +{ + int state; + if(m_HardError) return true; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else if(ftp) + { + state = ftp->GetStatus(); + } + switch(state) + { + + case FTP_STATE_URL_PARSING_ERROR: + case FTP_STATE_HOST_NOT_FOUND: + case FTP_STATE_DIRECTORY_INVALID: + case FTP_STATE_FILE_NOT_FOUND: + case FTP_STATE_CANT_CONNECT: + case FTP_STATE_LOGIN_ERROR: + case FTP_STATE_INTERNAL_ERROR: + case FTP_STATE_SOCKET_ERROR: + case FTP_STATE_UNKNOWN_ERROR: + case FTP_STATE_RECV_FAILED: + case FTP_STATE_CANT_WRITE_FILE: + return true; + case FTP_STATE_CONNECTING: + return false; + default: + return false; + } +} + +int InetGetFile::GetErrorCode() +{ + int state; + if(m_HardError) return m_HardError; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else if(ftp) + { + state = ftp->GetStatus(); + } + switch(state) + { + + case FTP_STATE_URL_PARSING_ERROR: + return INET_ERROR_CANT_PARSE_URL; + + case FTP_STATE_HOST_NOT_FOUND: + return INET_ERROR_HOST_NOT_FOUND; + + + case FTP_STATE_DIRECTORY_INVALID: + case FTP_STATE_FILE_NOT_FOUND: + return INET_ERROR_BAD_FILE_OR_DIR; + + case FTP_STATE_CANT_CONNECT: + case FTP_STATE_LOGIN_ERROR: + case FTP_STATE_INTERNAL_ERROR: + case FTP_STATE_SOCKET_ERROR: + case FTP_STATE_UNKNOWN_ERROR: + case FTP_STATE_RECV_FAILED: + + return INET_ERROR_UNKNOWN_ERROR; + + case FTP_STATE_CANT_WRITE_FILE: + return INET_ERROR_CANT_WRITE_FILE; + default: + return INET_ERROR_NO_ERROR; + } +} + +int InetGetFile::GetTotalBytes() +{ + if(m_bUseHTTP) + { + return http->GetTotalBytes(); + } + else if(ftp) + { + return ftp->GetTotalBytes(); + } + else return 0; +} + +int InetGetFile::GetBytesIn() +{ + if(m_bUseHTTP) + { + return http->GetBytesIn(); + } + else if(ftp) + { + return ftp->GetBytesIn(); + } + else return 0; +} \ No newline at end of file diff --git a/netcon/inetfile/lnxinetfile.cpp b/netcon/inetfile/lnxinetfile.cpp new file mode 100644 index 000000000..42d3692ea --- /dev/null +++ b/netcon/inetfile/lnxinetfile.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include "inetgetfile.h" +#include "CFtp.h" +#include "Chttpget.h" + + +InetGetFile::InetGetFile(char *URL,char *localfile) +{ +} + +InetGetFile::~InetGetFile() +{ +} + +BOOL InetGetFile::IsFileReceived() +{ + return false; +} +BOOL InetGetFile::IsFileError() +{ + return true; +} + +BOOL InetGetFile::IsConnecting() +{ + return true; +} + +BOOL InetGetFile::IsReceiving() +{ + return false; +} + +int InetGetFile::GetErrorCode() +{ + return 0; +} + +int InetGetFile::GetBytesIn() +{ + return 0; +} + +int InetGetFile::GetTotalBytes() +{ + return 0; +} + +void InetGetFile::AbortGet() +{ +} + +//////////////////////////////////////////////////////////////////// + +CFtpGet::CFtpGet(char *URL,char *localfile,char *Username,char *Password){} +CFtpGet::~CFtpGet(){} +int CFtpGet::GetStatus(){return 0;} +unsigned int CFtpGet::GetBytesIn(){return 0;} +unsigned int CFtpGet::GetTotalBytes(){return 0;} +void CFtpGet::AbortGet(){} +void CFtpGet::WorkerThread(){} +int CFtpGet::ConnectControlSocket(){return 0;} +int CFtpGet::LoginHost(){return 0;} +unsigned int CFtpGet::SendFTPCommand(char *command){return 0;} +unsigned int CFtpGet::ReadFTPServerReply(){return 0;} +unsigned int CFtpGet::GetFile(){return 0;} +unsigned int CFtpGet::IssuePort(){return 0;} +unsigned int CFtpGet::ReadDataChannel(){return 0;} +void CFtpGet::FlushControlChannel(){} +///////////////////////////////////////////////////////////////////// +ChttpGet::ChttpGet(char *URL,char *localfile){} +ChttpGet::~ChttpGet(){} +int ChttpGet::GetStatus(){return 0;} +unsigned int ChttpGet::GetBytesIn(){return 0;} +unsigned int GetTotalBytes(){return 0;} +void AbortGet(){} +void WorkerThread(){} +int ConnectSocket(){return 0;} +char *GetHTTPLine(){return "ERR";} +unsigned int ReadDataChannel(){return 0;} + diff --git a/netcon/lanclient/CMakeLists.txt b/netcon/lanclient/CMakeLists.txt new file mode 100644 index 000000000..54025c880 --- /dev/null +++ b/netcon/lanclient/CMakeLists.txt @@ -0,0 +1,10 @@ +SET (HEADERS lanclient.h) +SET (CPPS lanclient.cpp) + +SET (NETGAME_MODULE "TCP~IP") + +ADD_LIBRARY(${NETGAME_MODULE} SHARED ${CPPS} ${HEADERS}) +set_target_properties(${NETGAME_MODULE} PROPERTIES PREFIX "") +set_target_properties(${NETGAME_MODULE} PROPERTIES SUFFIX ".d3c") +install(TARGETS ${NETGAME_MODULE} DESTINATION "${D3_GAMEDIR}online/") +target_link_libraries(${NETGAME_MODULE} inetfile) \ No newline at end of file diff --git a/netcon/lanclient/lanclient.cpp b/netcon/lanclient/lanclient.cpp new file mode 100644 index 000000000..8a84b28d4 --- /dev/null +++ b/netcon/lanclient/lanclient.cpp @@ -0,0 +1,759 @@ +/* +* $Logfile: /DescentIII/Main/lanclient/lanclient.cpp $ +* $Revision: 1.3 $ +* $Date: 2004/02/25 00:04:06 $ +* $Author: ryan $ +* +* LAN Client DLL +* +* $Log: lanclient.cpp,v $ +* Revision 1.3 2004/02/25 00:04:06 ryan +* Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). +* +* Revision 1.2 2000/05/29 05:30:46 icculus +* support for static linking into main executable. +* +* Revision 1.1.1.1 2000/04/18 00:00:38 icculus +* initial checkin +* + * + * 75 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 74 8/24/99 5:47p Kevin + * Macintosh crap + * + * 73 5/23/99 2:38a Kevin + * fixed text overlapping thingy + * + * 72 5/20/99 9:18p Kevin + * removed invisible hotspot + * + * 71 5/04/99 5:39p Kevin + * connection dll & pxo game tracking improvements (added server type to + * list) + * + * 70 5/01/99 3:16p Kevin + * + * 69 4/29/99 11:49a Kevin + * enabled the 'i' key in the game list + * + * 68 4/28/99 6:39p Kevin + * Build 182 fixes + * + * 67 4/25/99 5:20p Kevin + * fixed a bug where the last item would never dissapear + * + * 66 4/25/99 5:02p Kevin + * Bunches of multiplayer UI improvements + * + * 65 4/22/99 3:43p Kevin + * Training missions show controls on screen + * + * 64 4/18/99 3:12p Jeff + * got working for linux + * + * 63 4/17/99 3:44p Kevin + * Demo2 changes & fixes + * + * 62 4/14/99 1:22a Jeff + * fixed case mismatched #includes + * + * 61 4/03/99 9:26p Jeff + * changed dialogs that weren't using UID_OK and UID_CANCEL to use and + * handle them properly + * + * 60 3/17/99 4:08p Kevin + * Changed the way games appear and timeout in the game list. + * + * 59 2/19/99 5:21p Kevin + * Fixed some connection DLLs and a Direct Sound bug with threads. + * + * 58 2/08/99 11:44p Kevin + * Fixed a bug with entering addresses + * + * 57 2/03/99 4:20p Kevin + * Got multiplayer working with .mn3 files, and setup autodownloading + * + * 56 1/23/99 3:19p Kevin + * Made gamelist boxes not sort + * + * 55 1/07/99 11:51a Kevin + * Added support for joining servers on alternate ports and hosting behind + * a proxy/firewall + * + * 54 12/30/98 12:16p Kevin + * Auto Mission Download system + * + * 53 12/14/98 10:53a Jason + * added bright player ships option + * + * 52 12/04/98 4:37p Kevin + * Fixed selection being reset in game lists... + * + * 51 12/01/98 12:47p Jason + * got rid of NF_DROPMISORDERED and added NF_USE_SMOOTHING + * + * 50 11/18/98 3:26p Kevin + * Put multiplayer options into con_dll.h + * + * 49 10/19/98 2:48p Kevin + * Added accurate weapon thingy for Chris + * + * 48 10/17/98 2:32p Kevin + * FIxed problem with banned users getting stuck on the ban message + * screen. + * + * 47 10/15/98 12:08p Kevin + * Changed game list to line up differently + * + * 46 10/13/98 3:41p Kevin + * bug fixes + * + * 45 10/12/98 8:39p Kevin + * removed mprintf's and fixed some smallish bugs + * + * 44 10/08/98 3:37p Jeff + * removed time_left from Netgame + * + * 43 10/01/98 11:37a Kevin + * UI fixes and stuff + * + * 42 9/29/98 2:23p Kevin + * More UI tweaks + * + * 41 9/28/98 4:21p Kevin + * Redesigned game list menus + * + * 40 9/28/98 11:25a Kevin + * + * 39 9/28/98 11:24a Kevin + * More UI improvements + * + * 38 9/28/98 11:02a Kevin + * added Networking defer, and fixed some UI issues + * + * 37 9/28/98 9:53a Kevin + * Fixing misc UI problems, and fixed some bugs that VC 6 found + * + * 36 9/25/98 11:07a Kevin + * fixed columns to line up and cleaned up some PXO bugs + * + * 35 9/24/98 12:50p Kevin + * Added UI for rotational velocity and drop out of order packets in net + * games + * + * 34 9/23/98 6:33p Kevin + * Fixed load settings + * + * 33 9/23/98 2:55p Kevin + * Saved multi config and changed UI to conform + * + * 32 9/22/98 3:55p Kevin + * Removed obsolete function + * + * 31 9/22/98 2:29p Kevin + * moved ships allowed code out of dll and into main app. Also added + * powerup exclusions + * + * 30 9/21/98 11:19a Kevin + * check protocol before entering multiplayer screens + * + * 29 9/15/98 12:42p Jason + * got dedicated server actually working + * + * 28 9/14/98 2:36p Kevin + * fixed some UI issues + * + * 27 9/09/98 12:41p Kevin + * Fixed up some UI issues + * + * 26 9/04/98 3:46p Kevin + * Added ping_time to Netplayers struct. It's updated in peer-peer and + * client server + * + * 25 8/27/98 5:03p Kevin + * Prettied up multiplayer screens and fixed some bugs. + * + * 24 8/24/98 5:04p Kevin + * Made msn files have the option to not be playable in multiplayer + * + * 23 8/24/98 12:37p Kevin + * added background bmp things + * + * 22 8/19/98 11:49a Kevin + * Got DirectPlay IPX working, and localized connection DLLs + * + * 21 8/17/98 11:00a Kevin + * Moved DLLs into subdirectories + * + * 20 8/07/98 12:39p Jeff + * added "allowed ships" to multiplayer options + * + * 19 8/04/98 10:26a Kevin + * Custom sound and bmp exchange system + * + * 18 7/21/98 1:49p Kevin + * IPX support and peer-peer option for multi + * + * 17 7/20/98 6:20p Kevin + * peer-peer stuff + * + * 16 7/20/98 2:34p Kevin + * Re-wrote the DLL wrapper, to allow for better expandability + * + * 13 7/02/98 4:52p Kevin + * direct ip list fixes + * + * 12 7/02/98 12:19p Kevin + * misc network fixes + * + * 11 6/30/98 3:20p Kevin + * fixed ping time + * + * 10 6/25/98 10:03a Kevin + * Minor chat fixes in PXO + * + * 9 6/24/98 6:40p Kevin + * Added help to main dll menu + * + * 8 6/24/98 5:12p Kevin + * Fixed bug with main menu crashing + * + * 7 6/24/98 3:24p Kevin + * Updated PXO screens with chat, etc. + * + * 6 6/18/98 4:49p Kevin + * Updated multiplayer menus + * + * 5 6/11/98 1:56p Jeff + * looks for d3m files instead of dll + * + * 4 6/05/98 2:58p Jeff + * changes made for new module library + * + * 3 6/01/98 3:52p Kevin + * changed listbox item to be white + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/28/98 12:14p Kevin + * + * 1 5/18/98 12:47p Kevin +* +* $NoKeywords: $ +*/ + +#ifndef __LINUX__ +#include "local_malloc.h" +#endif +#include "ui.h" +#include "newui.h" +#include "grdefs.h" +#include "player.h" +#include "game.h" +#include "pilot.h" +#include "module.h" +#include "ddio_common.h" + +#include "inetgetfile.h" + +#ifdef __STATIC_NETWORK_CLIENTS +#define DLLMultiCall DLLMultiCall_LAN +#define DLLMultiInit DLLMultiInit_LAN +#define DLLMultiClose DLLMultiClose_LAN +#define MainMultiplayerMenu MainMultiplayerMenu_LAN +#define AutoLoginAndJoinGame AutoLoginAndJoinGame_LAN +#endif + +#define TXT_DLL_SAVESETTINGS TXT(27) +#define TXT_DLL_LOADSETTINGS TXT(28) + +/////////////////////////////////////////////// +//localization header +#include "lanstrings.h" + + +#define TXT_GEN_MPLYROPTIONS TXT_LC_MPLYROPTIONS +#define TXT_GEN_TIMELIMIT TXT_LC_TIMELIMIT +#define TXT_GEN_KILLGOAL TXT_LC_KILLGOAL +#define TXT_GEN_PPS TXT_LC_PPS +#define TXT_GEN_RESPAWNRATE TXT_LC_RESPAWNRATE +#define TXT_GEN_MAXPLAYERS TXT_LC_MAXPLAYERS +#define TXT_GEN_PREVMENU TXT_LC_PREVMENU +#define TXT_GEN_CANCEL TXT_LC_CANCEL +#define TXT_GEN_CFGALLOWEDSHIP TXT_LC_CFGALLOWEDSHIP +#define TXT_GEN_USEROTVEL TXT_LC_USEROTVEL +#define TXT_GEN_USEROTVEL TXT_LC_USEROTVEL +#define TXT_GEN_USESMOOTHING TXT_LC_USESMOOTHING +#define TXT_GEN_CLIENTSERVER TXT_LC_CLIENTSERVER +#define TXT_GEN_PEERPEER TXT_LC_PEERPEER +#define TXT_GEN_ACC_WEAP_COLL TXT_LC_ACC_WEAP_COLL +#define TXT_GEN_BRIGHT_PLAYERS TXT_LC_BRIGHT_PLAYERS + +#define MULTI_USE_ALL_OPTIONS 1 + +#include "lanclient.h" +#include "DLLUiItems.h" + +using namespace lanclient; + +//int DLLUIClass_CurrID = 0xD0; + +#define MAX_NET_GAMES 100 +#define JEFF_RED GR_RGB(255,40,40) +#define JEFF_BLUE GR_RGB(40,40,255) +#define JEFF_GREEN GR_RGB(40,255,40) +#define NETPOLLINTERVAL 10.0 + +extern int MTAVersionCheck(unsigned int oldver, char *URL); +///////////////////////////// +// Defines + + +#ifdef MACINTOSH +#pragma export on +#endif + +// These next two function prototypes MUST appear in the extern "C" block if called +// from a CPP file. +extern "C" +{ + DLLEXPORT void DLLFUNCCALL DLLMultiInit (int *api_func); + DLLEXPORT void DLLFUNCCALL DLLMultiCall (int eventnum); + DLLEXPORT void DLLFUNCCALL DLLMultiClose (); +} + +static bool All_ok = true; +// Initializes the game function pointers +void DLLFUNCCALL DLLMultiInit (int *api_func) +{ + Use_netgame_flags = 1; + #include "mdllinit.h" + DLLCreateStringTable("lanclient.str",&StringTable,&StringTableSize); + DLLmprintf((0,"%d strings loaded from string table\n",StringTableSize)); + if(!StringTableSize){ + All_ok = false; + return; + } + *DLLUse_DirectPlay = false; +} + +// Called when the DLL is shutdown +void DLLFUNCCALL DLLMultiClose () +{ + DLLDestroyStringTable(StringTable,StringTableSize); +} + + +// The main entry point where the game calls the dll +void DLLFUNCCALL DLLMultiCall (int eventnum) +{ + + switch(eventnum) + { + case MT_EVT_GET_HELP: + strcpy(DLLHelpText1,TXT_LC_HELP1); + strcpy(DLLHelpText2,TXT_LC_HELP2); + strcpy(DLLHelpText3,TXT_LC_HELP3); + strcpy(DLLHelpText4,TXT_LC_HELP4); + break; + case MT_AUTO_START: + if(!All_ok) + *DLLMultiGameStarting = 0; + else + *DLLMultiGameStarting = 1; + break; + case MT_RETURN_TO_GAME_LIST: + case MT_EVT_LOGIN: + if(!DLLTCP_active) + { + DLLDoMessageBox(TXT_LC_ERROR,TXT_LC_NO_TCPIP,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + *DLLMultiGameStarting = 0; + break; + } + if(!All_ok) + *DLLMultiGameStarting = 0; + else + *DLLMultiGameStarting = MainMultiplayerMenu (); + break; + case MT_EVT_FRAME: + + break; + case MT_EVT_FIRST_FRAME: + + break; + case MT_EVT_GAME_OVER: + break; + case MT_AUTO_LOGIN: + if(!DLLTCP_active) + { + DLLDoMessageBox(TXT_LC_ERROR,TXT_LC_NO_TCPIP,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + *DLLMultiGameStarting = 0; + break; + } + if(All_ok) + AutoLoginAndJoinGame(); + break; + } +} + +#ifdef MACINTOSH +#pragma export off +#endif +namespace lanclient { +#define GET_INFO_ID 50 +// The first multiplayer menu that the user will see...all multiplayer stuff is +// reached from this menu +// Returns true if we're starting a multiplayer game +int MainMultiplayerMenu () +{ + + char selgame[200] = ""; + void * join_LC_text = DLLCreateNewUITextItem(TXT_LC_RETURNMAIN,GR_BLACK);//return_menu + void * list_head_txt = DLLCreateNewUITextItem(TXT_LC_GAMELISTHDR,UICOL_TEXT_NORMAL); + void * exit_on_text = DLLCreateNewUITextItem(TXT_LC_EXIT,UICOL_HOTSPOT_HI); + void * exit_off_text = DLLCreateNewUITextItem(TXT_LC_EXIT,UICOL_HOTSPOT_LO); + void * join_on_text = DLLCreateNewUITextItem(TXT_LC_JOINSEL,UICOL_HOTSPOT_HI); + void * join_off_text = DLLCreateNewUITextItem(TXT_LC_JOINSEL,UICOL_HOTSPOT_LO); + void * start_on_text = DLLCreateNewUITextItem(TXT_LC_STARTNEW,UICOL_HOTSPOT_HI); + void * start_off_text = DLLCreateNewUITextItem(TXT_LC_STARTNEW,UICOL_HOTSPOT_LO); + void * srch_on_text = DLLCreateNewUITextItem(TXT_LC_SRCHADDR,UICOL_HOTSPOT_HI); + void * srch_off_text = DLLCreateNewUITextItem(TXT_LC_SRCHADDR,UICOL_HOTSPOT_LO); + void * scan_on_text = DLLCreateNewUITextItem(TXT_LC_SCANLOCAL,UICOL_HOTSPOT_HI); + void * scan_off_text = DLLCreateNewUITextItem(TXT_LC_SCANLOCAL,UICOL_HOTSPOT_LO); + void * game_hdr_text = DLLCreateNewUITextItem(TXT_LC_GAMEHEADER,UICOL_WINDOW_TITLE,DLL_BIG_BRIEFING_FONT); + + int exit_menu=0; + void * net_game_txt_items[MAX_NET_GAMES]; + int a; + for(a=0;aNETPOLLINTERVAL)) + { + DLLSearchForLocalGamesTCP(0xffffffffl,htons(DEFAULT_GAME_PORT)); + //*DLLNum_network_games_known = 0; + //DLLListRemoveAll(main_list); + lastgamesfound = 0; + lastpoll = DLLtimer_GetTime(); + selno = DLLListGetSelectedIndex(main_list); + if(selno>=0) + strcpy(selgame,DLLNetwork_games[selno].name); + else + selgame[0]=NULL; + } + res = DLLPollUI(); + + if(res==-1) + { + continue; + + } + // handle all UI results. + switch(res) + { + + case UID_CANCEL: + DLLNewUIWindowClose(main_wnd); + exit_menu = 1; + break; + case UID_OK: + //Double click on listbox, or join selected hit. + if(*DLLNum_network_games_known) + { + //Get the appropriate game address + int gameno; + gameno = DLLListGetSelectedIndex(main_list); + DLLmprintf((0,"Selected item is %d\n",gameno)); + network_address s_address; + memcpy (&s_address,&DLLNetwork_games[gameno].addr,sizeof(network_address)); + s_address.connection_type = NP_TCP; + //s_address.port=&DLLNetwork_games[gameno].addr.port;//DEFAULT_GAME_PORT; + *DLLGame_is_master_tracker_game = 0; + DLLMultiStartClient (NULL); + if(DLLDoPlayerMouselookCheck(DLLNetwork_games[gameno].flags)) + { + if(DLLmsn_CheckGetMission(&s_address,DLLNetwork_games[gameno].mission)) + { + if ((DLLTryToJoinServer (&s_address))) + { + DLLmprintf ((0,"Menu: Game joined!\n")); + DLLNewUIWindowClose(main_wnd); + exit_menu=1; + ret=1; + } + else + { + DLLNewUIWindowClose(main_wnd); + DLLNewUIWindowOpen(main_wnd); + } + } + } + } + else + { + DLLDoMessageBox(TXT_LC_ERROR,TXT_LC_NO_GAMES,MSGBOX_OK,UICOL_WINDOW_TITLE,UICOL_TEXT_NORMAL); + } + break; + case 7: + //Start a new game + // Start a netgame + DLLNewUIWindowClose(main_wnd); + *DLLGame_is_master_tracker_game = 0; + if (StartMultiplayerGameMenu()) + { + exit_menu=1; + ret=1; + } + else + { + DLLNewUIWindowOpen(main_wnd); + } + break; + case 8: + //Scan for local games + *DLLMulti_Gamelist_changed = true; + *DLLNum_network_games_known = 0; + looklocal = 1; + lastgamesfound = 0; + selno = DLLListGetSelectedIndex(main_list); + if(selno>=0) + strcpy(selgame,DLLNetwork_games[selno].name); + else + selgame[0]=NULL; + + DLLSearchForLocalGamesTCP(0xffffffffl,htons(DEFAULT_GAME_PORT)); + DLLListRemoveAll(main_list); + for(a=0;a +* +* $Log: Anarchy.h,v $ +* Revision 1.1.1.1 2003/08/26 03:56:37 kevinb +* initial 1.5 import +* + * + * 33 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 32 5/12/99 11:28a Jeff + * added sourcesafe comment block +* +* $NoKeywords: $ +*/ + + + +#ifndef __DMFC_APP_H_ +#define __DMFC_APP_H_ + +#include "osiris_share.h" +#include "d3events.h" + +//Setup and processing functions +void AnarchyGameInit(int teams); +void AnarchyGameClose(void); + +void OnKeypress(int key); +void OnHUDInterval(void); +void OnInterval(void); +void OnClientPlayerKilled(object *killer_obj,int victim_pnum); +void OnClientPlayerEntersGame(int player_num); +void OnClientLevelStart(void); +void OnClientLevelEnd(void); +void OnServerGameCreated(void); +void OnPLRInterval(void); +void OnPLRInit(void); +void OnSaveStatsToFile(void); +void OnLevelEndSaveStatsToFile(void); +void OnDisconnectSaveStatsToFile(void); +void OnPrintScores(int level); +extern IDMFC *DMFCBase; + + + +/************************************************************************************************* + *The following functions and declaration are needed to connect the DLL to the game. These must * + *stay here and must call the functions that are in them. You can not delete from here, but you * + *can add to it no problem * + ************************************************************************************************* +*/ +// These next two function prototypes MUST appear in the extern "C" block if called +// from a CPP file. +#ifdef __cplusplus +extern "C" +{ +#endif + DLLEXPORT void DLLFUNCCALL DLLGameInit (int *api_func,ubyte *all_ok,int num_teams_to_use); + DLLEXPORT void DLLFUNCCALL DLLGameCall (int eventnum,dllinfo *data); + DLLEXPORT void DLLFUNCCALL DLLGameClose (); + DLLEXPORT void DLLFUNCCALL DLLGetGameInfo (tDLLOptions *options); + DLLEXPORT int DLLFUNCCALL GetGOScriptID(char *name,ubyte isdoor); + DLLEXPORT void DLLFUNCCALLPTR CreateInstance(int id); + DLLEXPORT void DLLFUNCCALL DestroyInstance(int id,void *ptr); + DLLEXPORT short DLLFUNCCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data); + DLLEXPORT int DLLFUNCCALL SaveRestoreState( void *file_ptr, ubyte saving_state ); +#ifdef __cplusplus +} +#endif + +#ifdef MACINTOSH +#pragma export on +#endif + +// The main entry point where the game calls the dll +void DLLFUNCCALL DLLGameCall (int eventnum,dllinfo *data) +{ + if((eventnumGetLocalRole()!=LR_SERVER)){ + return; + } + + DMFCBase->TranslateEvent(eventnum,data); +} + + +// GetGOScriptID +// Purpose: +// Given the name of the object (from it's pagename), this function will search through it's +// list of General Object Scripts for a script with a matching name (to see if there is a script +// for that type/id of object within this DLL). If a matching scriptname is found, a UNIQUE ID +// is to be returned back to Descent 3. This ID will be used from here on out for all future +// interaction with the DLL. Since doors are not part of the generic object's, it's possible +// for a door to have the same name as a generic object (OBJ_POWERUP, OBJ_BUILDING, OBJ_CLUTTER +// or OBJ_ROBOT), therefore, a 1 is passed in for isdoor if the given object name refers to a +// door, else it is a 0. The return value is the unique identifier, else -1 if the script +// does not exist in the DLL. +int DLLFUNCCALL GetGOScriptID(char *name,ubyte isdoor) +{ + return -1; +} + +// CreateInstance +// Purpose: +// Given an ID from a call to GetGOScriptID(), this function will create a new instance for that +// particular script (by allocating and initializing memory, etc.). A pointer to this instance +// is to be returned back to Descent 3. This pointer will be passed around, along with the ID +// for CallInstanceEvent() and DestroyInstance(). Return NULL if there was an error. +void DLLFUNCCALLPTR CreateInstance(int id) +{ + return NULL; +} + +// DestroyInstance +// Purpose: +// Given an ID, and a pointer to a particular instance of a script, this function will delete and +// destruct all information associated with that script, so it will no longer exist. +void DLLFUNCCALL DestroyInstance(int id,void *ptr) +{ +} + +// CallInstanceEvent +// Purpose: +// Given an ID, a pointer to a script instance, an event and a pointer to the struct of +// information about the event, this function will translate who this event belongs to and +// passes the event to that instance of the script to be handled. Return a combination of +// CONTINUE_CHAIN and CONTINUE_DEFAULT, to give instructions on what to do based on the +// event. CONTINUE_CHAIN means to continue through the chain of scripts (custom script, level +// script, mission script, and finally default script). If CONTINUE_CHAIN is not specified, +// than the chain is broken and those scripts of lower priority will never get the event. Return +// CONTINUE_DEFAULT in order to tell D3 if you want process the normal action that is built into +// the game for that event. This only pertains to certain events. If the chain continues +// after this script, than the CONTINUE_DEFAULT setting will be overridden by lower priority +// scripts return value. +short DLLFUNCCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data) +{ + return CONTINUE_CHAIN|CONTINUE_DEFAULT; +} + +// SaveRestoreState +// Purpose: +// This function is called when Descent 3 is saving or restoring the game state. In this function +// you should save/restore any global data that you want preserved through load/save (which includes +// demos). You must be very careful with this function, corrupting the file (reading or writing too +// much or too little) may be hazardous to the game (possibly making it impossible to restore the +// state). It would be best to use version information to keep older versions of saved states still +// able to be used. IT IS VERY IMPORTANT WHEN SAVING THE STATE TO RETURN THE NUMBER OF _BYTES_ WROTE +// TO THE FILE. When restoring the data, the return value is ignored. saving_state is 1 when you should +// write data to the file_ptr, 0 when you should read in the data. +int DLLFUNCCALL SaveRestoreState( void *file_ptr, ubyte saving_state ) +{ + return 0; +} + +#ifdef MACINTOSH +#pragma export off +#endif + +#endif diff --git a/netgames/anarchy/CMakeLists.txt b/netgames/anarchy/CMakeLists.txt new file mode 100644 index 000000000..69b969097 --- /dev/null +++ b/netgames/anarchy/CMakeLists.txt @@ -0,0 +1,11 @@ +SET (HEADERS anarchystr.h Anarchy.h) +SET (CPPS anarchy.cpp) + +SET (NETGAME_MODULE anarchy) + +ADD_LIBRARY(${NETGAME_MODULE} SHARED ${CPPS} ${HEADERS}) +set_target_properties(${NETGAME_MODULE} PROPERTIES PREFIX "") +set_target_properties(${NETGAME_MODULE} PROPERTIES SUFFIX ".d3m") + +target_link_libraries(${NETGAME_MODULE} dmfc) +install(TARGETS ${NETGAME_MODULE} DESTINATION "${D3_GAMEDIR}netgames/") \ No newline at end of file diff --git a/netgames/anarchy/anarchy.cpp b/netgames/anarchy/anarchy.cpp new file mode 100644 index 000000000..642f88edf --- /dev/null +++ b/netgames/anarchy/anarchy.cpp @@ -0,0 +1,1020 @@ +/* +* $Logfile: /DescentIII/Main/anarchy/anarchy.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:56:37 $ +* $Author: kevinb $ +* +* +* +* $Log: anarchy.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:56:37 kevinb +* initial 1.5 import +* + * + * 84 11/08/99 11:42p Jeff + * SourceSafe Test...ignore + * + * 83 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 82 7/12/99 1:22p Jeff + * updated for new netflags + * + * 81 7/11/99 6:43p Jeff + * cleaner plr if the list is too long + * + * 80 5/23/99 3:04a Jason + * fixed bug with player rankings not being updated correctly + * + * 79 5/12/99 11:28a Jeff + * added sourcesafe comment block +* +* $NoKeywords: $ +*/ + + + +#include +#include +#include +#include "idmfc.h" +#include "Anarchy.h" +#include "anarchystr.h" +IDMFC *DMFCBase = NULL; +IDmfcStats *dstat = NULL; + +int SortedPlayers[MAX_PLAYER_RECORDS]; //sorted player nums +bool DisplayScoreScreen; +int Highlight_bmp = -1; + +#define AHD_NONE 0 +#define AHD_SCORE 1 +#define AHD_EFFICIENCY 2 +#define ACM_PLAYERCOLOR 0 +#define ACM_NORMAL 1 +ubyte Anarchy_hud_display = AHD_SCORE; +ubyte HUD_color_model = ACM_PLAYERCOLOR; +bool display_my_welcome = false; + +void DisplayHUDScores(struct tHUDItem *hitem); +void DisplayScores(void); +void DisplayWelcomeMessage(int player_num); +void SaveStatsToFile(char *filename); +void SwitchHUDColor(int i); + +/////////////////////////////////////////////// +//localization info +char **StringTable; +int StringTableSize = 0; +char *_ErrorString = "Missing String"; +char *GetString(int d){if( (d<0) || (d>=StringTableSize) ) return _ErrorString; else return StringTable[d];} +/////////////////////////////////////////////// + +void SwitchAnarchyScores(int i) +{ + if(i<0) + i = 0; + if(i>2) + i = 2; + + Anarchy_hud_display = i; + + switch(i){ + case AHD_NONE: + DLLAddHUDMessage(TXT_HUDD_NONE); + break; + case AHD_SCORE: + DLLAddHUDMessage(TXT_HUDD_SCORES); + break; + case AHD_EFFICIENCY: + DLLAddHUDMessage(TXT_HUDD_EFFIC); + break; + }; +} + +#ifdef MACINTOSH +#pragma export on +#endif + +// This function gets called by the game when it wants to learn some info about the game +void DLLFUNCCALL DLLGetGameInfo (tDLLOptions *options) +{ + options->flags = DOF_MAXTEAMS; + options->max_teams = 1; + strcpy(options->game_name,"Anarchy"); + strcpy(options->requirements,""); +} + +// Initializes the game function pointers +void DLLFUNCCALL DLLGameInit (int *api_func,ubyte *all_ok,int num_teams_to_use) +{ + *all_ok = 1; + DMFCBase = CreateDMFC(); + if(!DMFCBase) + { + *all_ok = 0; + return; + } + + dstat = CreateDmfcStats(); + if(!dstat) + { + *all_ok = 0; + return; + } + + DMFCBase->LoadFunctions(api_func); + + // Setup the event handlers that we handle + DMFCBase->Set_OnKeypress(OnKeypress); + DMFCBase->Set_OnHUDInterval(OnHUDInterval); + DMFCBase->Set_OnInterval(OnInterval); + DMFCBase->Set_OnClientPlayerKilled(OnClientPlayerKilled); + DMFCBase->Set_OnClientPlayerEntersGame(OnClientPlayerEntersGame); + DMFCBase->Set_OnClientLevelStart(OnClientLevelStart); + DMFCBase->Set_OnClientLevelEnd(OnClientLevelEnd); + DMFCBase->Set_OnServerGameCreated(OnServerGameCreated); + DMFCBase->Set_OnPLRInterval(OnPLRInterval); + DMFCBase->Set_OnPLRInit(OnPLRInit); + DMFCBase->Set_OnSaveStatsToFile(OnSaveStatsToFile); + DMFCBase->Set_OnLevelEndSaveStatsToFile(OnLevelEndSaveStatsToFile); + DMFCBase->Set_OnDisconnectSaveStatsToFile(OnDisconnectSaveStatsToFile); + DMFCBase->Set_OnPrintScores(OnPrintScores); + + + DLLCreateStringTable("Anarchy.str",&StringTable,&StringTableSize); + mprintf((0,"%d strings loaded from string table\n",StringTableSize)); + if(!StringTableSize){ + *all_ok = 0; + return; + } + + AnarchyGameInit(1); + + //add the death and suicide messages + DMFCBase->AddDeathMessage(TXT_DEATH1,true); + DMFCBase->AddDeathMessage(TXT_DEATH2,true); + DMFCBase->AddDeathMessage(TXT_DEATH3,false); + DMFCBase->AddDeathMessage(TXT_DEATH4,false); + DMFCBase->AddDeathMessage(TXT_DEATH5,true); + DMFCBase->AddDeathMessage(TXT_DEATH6,true); + DMFCBase->AddDeathMessage(TXT_DEATH7,false); + DMFCBase->AddDeathMessage(TXT_DEATH8,true); + DMFCBase->AddDeathMessage(TXT_DEATH9,true); + DMFCBase->AddDeathMessage(TXT_DEATH10,true); + + DMFCBase->AddSuicideMessage(TXT_SUICIDE1); + DMFCBase->AddSuicideMessage(TXT_SUICIDE2); + DMFCBase->AddSuicideMessage(TXT_SUICIDE3); + DMFCBase->AddSuicideMessage(TXT_SUICIDE4); + DMFCBase->AddSuicideMessage(TXT_SUICIDE5); + DMFCBase->AddSuicideMessage(TXT_SUICIDE6); + + DMFCBase->SetNumberOfTeams(1); + + netgame_info *Netgame = DMFCBase->GetNetgameInfo(); + Netgame->flags |= (NF_DAMAGE_FRIENDLY|NF_TRACK_RANK); + + Highlight_bmp = DLLbm_AllocBitmap(32,32,0); + if(Highlight_bmp>BAD_BITMAP_HANDLE){ + ushort *data = DLLbm_data(Highlight_bmp,0); + if(!data){ + //bail on out of here + *all_ok = 0; + return; + } + for(int x=0;x<32*32;x++){ + data[x] = GR_RGB16(50,50,50)|OPAQUE_FLAG; + } + } + + DMFCBase->AddHUDItemCallback(HI_TEXT,DisplayHUDScores); + + DisplayScoreScreen = false; + +} +// Called when the DLL is shutdown +void DLLFUNCCALL DLLGameClose () +{ + if(Highlight_bmp>BAD_BITMAP_HANDLE) + DLLbm_FreeBitmap(Highlight_bmp); + + DLLDestroyStringTable(StringTable,StringTableSize); + + if(dstat) + { + dstat->DestroyPointer(); + dstat = NULL; + } + + if(DMFCBase) + { + AnarchyGameClose(); + DMFCBase->GameClose(); + DMFCBase->DestroyPointer(); + DMFCBase = NULL; + } +} + +void DetermineScore(int precord_num,int column_num,char *buffer,int buffer_size) +{ + player_record *pr = DMFCBase->GetPlayerRecord(precord_num); + if(!pr || pr->state==STATE_EMPTY){ + buffer[0] = '\0'; + return; + } + + sprintf(buffer,"%d", pr->dstats.kills[DSTAT_LEVEL]-pr->dstats.suicides[DSTAT_LEVEL]); +} + +void AnarchyGameClose(void) +{ +} + +// DMFCApp::GameInit +// +// Sets up all the DLL functions and pointers and preps the class for use. This ABSOLUTLY must be +// called, so if you override DMFCApp::GameInit, make sure that you put a call to this somewhere in +// the override. +void AnarchyGameInit(int teams) +{ + //add the anarchy menu/submenus + IMenuItem *lev1,*lev2; + + lev1 = CreateMenuItemWArgs("Anarchy",MIT_NORMAL,0,NULL); + + lev2 = CreateMenuItemWArgs(TXT_MNU_HUDSTYLE,MIT_STATE,0,SwitchAnarchyScores); + lev2->SetStateItemList(3,TXT_NONE,TXT_SCOREHD,TXT_EFFICIENCY); + lev2->SetState(1); + lev1->AddSubMenu(lev2); + + lev2 = CreateMenuItemWArgs(TXT_MNU_HUDCOLOR,MIT_STATE,0,SwitchHUDColor); + lev2->SetStateItemList(2,TXT_PLAYERCOLORS,TXT_NORMAL); + lev2->SetState(HUD_color_model); + lev1->AddSubMenu(lev2); + + lev2 = DMFCBase->GetOnScreenMenu(); + lev2->AddSubMenu(lev1); + + DMFCBase->GameInit(teams); + + // Initialize the Stats Manager + // ---------------------------- + + tDmfcStatsInit tsi; + tDmfcStatsColumnInfo pl_col[6]; + char gname[20]; + strcpy(gname,"Anarchy"); + + tsi.flags = DSIF_SHOW_PIC|DSIF_SHOW_OBSERVERICON; + tsi.cColumnCountDetailed = 0; + tsi.cColumnCountPlayerList = 6; + tsi.clbDetailedColumn = NULL; + tsi.clbDetailedColumnBMP = NULL; + tsi.clbPlayerColumn = DetermineScore; + tsi.clbPlayerColumnBMP = NULL; + tsi.DetailedColumns = NULL; + tsi.GameName = gname; + tsi.MaxNumberDisplayed = NULL; + tsi.PlayerListColumns = pl_col; + tsi.SortedPlayerRecords = SortedPlayers; + + pl_col[0].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[0].title,TXT_PILOT); + pl_col[0].type = DSCOL_PILOT_NAME; + pl_col[0].width = 120; + + pl_col[1].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[1].title,TXT_SCORE); + pl_col[1].type = DSCOL_CUSTOM; + pl_col[1].width = 50; + + pl_col[2].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[2].title,TXT_KILLS_SHORT); + pl_col[2].type = DSCOL_KILLS_LEVEL; + pl_col[2].width = 50; + + pl_col[3].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[3].title,TXT_DEATHS_SHORT); + pl_col[3].type = DSCOL_DEATHS_LEVEL; + pl_col[3].width = 60; + + pl_col[4].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[4].title,TXT_SUICIDES_SHORT); + pl_col[4].type = DSCOL_SUICIDES_LEVEL; + pl_col[4].width = 65; + + pl_col[5].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[5].title,TXT_PING); + pl_col[5].type = DSCOL_PING; + pl_col[5].width = 40; + + dstat->Initialize(&tsi); +} + +void OnHUDInterval(void) +{ + dstat->DoFrame(); + DMFCBase->DisplayOutrageLogo(); + DMFCBase->OnHUDInterval(); +} + +void OnInterval(void) +{ + DMFCBase->GetSortedPlayerSlots(SortedPlayers,MAX_PLAYER_RECORDS); + DMFCBase->OnInterval(); +} + +void OnKeypress(int key) +{ + dllinfo *Data = DMFCBase->GetDLLInfoCallData(); + + switch(key){ + case K_F7: + DisplayScoreScreen = !DisplayScoreScreen; + DMFCBase->EnableOnScreenMenu(false); + dstat->Enable(DisplayScoreScreen); + break; + case K_PAGEDOWN: + if(DisplayScoreScreen){ + dstat->ScrollDown(); + Data->iRet = 1; + } + break; + case K_PAGEUP: + if(DisplayScoreScreen){ + dstat->ScrollUp(); + Data->iRet = 1; + } + break; + case K_F6: + DisplayScoreScreen = false; + dstat->Enable(false); + break; + case K_ESC: + if(DisplayScoreScreen){ + dstat->Enable(false); + DisplayScoreScreen = false; + Data->iRet = 1; + } + break; + } + + DMFCBase->OnKeypress(key); +} + +// The server has just started, so clear out all the stats and game info +void OnServerGameCreated(void) +{ + DMFCBase->OnServerGameCreated(); +} + +// The server has started a new level, so clear out any scores needed to be reset +void OnClientLevelStart(void) +{ + for(int i=0;iOnClientLevelStart(); +} + +void OnClientLevelEnd(void) +{ + DMFCBase->OnClientLevelEnd(); +} + +// A new player has entered the game, zero their stats out +void OnClientPlayerEntersGame(int player_num) +{ + DMFCBase->OnClientPlayerEntersGame(player_num); + + if(player_num!=DMFCBase->GetPlayerNum()) + DisplayWelcomeMessage(player_num); + else + display_my_welcome = true; +} + +// We need to adjust the scores +void OnClientPlayerKilled(object *killer_obj,int victim_pnum) +{ + DMFCBase->OnClientPlayerKilled(killer_obj,victim_pnum); + player_record *kpr; + + int kpnum; + + if(killer_obj){ + if((killer_obj->type==OBJ_PLAYER)||(killer_obj->type==OBJ_GHOST)) + kpnum = killer_obj->id; + else if(killer_obj->type==OBJ_ROBOT || killer_obj->type == OBJ_BUILDING){ + //countermeasure kill + kpnum = DMFCBase->GetCounterMeasureOwner(killer_obj); + }else{ + kpnum = -1; + } + }else{ + kpnum = -1; + } + + kpr = DMFCBase->GetPlayerRecordByPnum(kpnum); + if(kpr){ + int goal; + if(DMFCBase->GetScoreLimit(&goal)){ + int score = kpr->dstats.kills[DSTAT_LEVEL] - kpr->dstats.suicides[DSTAT_LEVEL]; + if(score>=goal){ + DMFCBase->EndLevel(); + } + } + } +} + +bool compare_slots(int a,int b) +{ + int ascore,bscore; + player_record *apr,*bpr; + apr = DMFCBase->GetPlayerRecord(a); + bpr = DMFCBase->GetPlayerRecord(b); + if( !apr ) + return true; + if( !bpr ) + return false; + if( apr->state==STATE_EMPTY ) + return true; + if( bpr->state==STATE_EMPTY ) + return false; + if( (apr->state==STATE_INGAME) && (bpr->state==STATE_INGAME) ){ + //both players were in the game + ascore = apr->dstats.kills[DSTAT_LEVEL] - apr->dstats.suicides[DSTAT_LEVEL]; + bscore = bpr->dstats.kills[DSTAT_LEVEL] - bpr->dstats.suicides[DSTAT_LEVEL]; + return (ascorestate==STATE_INGAME) && (bpr->state==STATE_DISCONNECTED) ){ + //apr gets priority since he was in the game on exit + return false; + } + if( (apr->state==STATE_DISCONNECTED) && (bpr->state==STATE_INGAME) ){ + //bpr gets priority since he was in the game on exit + return true; + } + //if we got here then both players were disconnected + ascore = apr->dstats.kills[DSTAT_LEVEL] - apr->dstats.suicides[DSTAT_LEVEL]; + bscore = bpr->dstats.kills[DSTAT_LEVEL] - bpr->dstats.suicides[DSTAT_LEVEL]; + return (ascore=0 && compare_slots(tempsort[j],t); j--){ + tempsort[j+1] = tempsort[j]; + } + // insert + tempsort[j+1] = t; + } + + //copy the array over + memcpy(SortedPlayers,tempsort,DLLMAX_PLAYERS*sizeof(int)); + + DMFCBase->OnPLRInit(); +} + +void OnPLRInterval(void) +{ +#define PLAYERS_COL 130 +#define SCORE_COL 280 +#define DEATHS_COL 330 +#define SUICIDES_COL 390 +#define TOTAL_COL 460 + + DMFCBase->OnPLRInterval(); + + int y = 40; + + int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]) + 1; + DLLgrtext_SetFont((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]); + + //print out header + DLLgrtext_SetColor(GR_RGB(255,255,150)); + DLLgrtext_Printf(PLAYERS_COL,y,TXT_PILOT); + DLLgrtext_Printf(SCORE_COL,y,TXT_KILLS_SHORT); + DLLgrtext_Printf(DEATHS_COL,y,TXT_DEATHS_SHORT); + DLLgrtext_Printf(SUICIDES_COL,y,TXT_SUICIDES_SHORT); + DLLgrtext_Printf(TOTAL_COL,y,TXT_SCORE); + y+=height; + + //print out player stats + int rank = 1; + int slot,pnum; + player_record *pr; + + for(int i=0;iGetPlayerRecord(slot); + if((pr) && (pr->state!=STATE_EMPTY) ){ + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue;//skip a dedicated server + + pnum=DMFCBase->WasPlayerInGameAtLevelEnd(slot); + if(pnum!=-1) + { + DLLgrtext_SetColor((DMFCBase->GetPlayerColors())[pnum]); + }else + { + DLLgrtext_SetColor(GR_RGB(128,128,128)); + } + + char temp[100]; + sprintf(temp,"%d)%s",rank,pr->callsign); + DMFCBase->ClipString(SCORE_COL - PLAYERS_COL - 10,temp,true); + DLLgrtext_Printf(PLAYERS_COL,y,"%s",temp); + + DLLgrtext_Printf(SCORE_COL,y,"%d",pr->dstats.kills[DSTAT_LEVEL]); + DLLgrtext_Printf(DEATHS_COL,y,"%d",pr->dstats.deaths[DSTAT_LEVEL]); + DLLgrtext_Printf(SUICIDES_COL,y,"%d",pr->dstats.suicides[DSTAT_LEVEL]); + DLLgrtext_Printf(TOTAL_COL,y,"%d",pr->dstats.kills[DSTAT_LEVEL]-pr->dstats.suicides[DSTAT_LEVEL]); + y+=height; + rank++; + + if(y>=440) + goto quick_exit; + + } + } + +quick_exit:; +} + + +void SaveStatsToFile(char *filename) +{ + CFILE *file; + DLLOpenCFILE(&file,filename,"wt"); + if(!file){ + mprintf((0,"Unable to open output file\n")); + return; + } + + //write out game stats + #define BUFSIZE 150 + char buffer[BUFSIZE]; + char tempbuffer[25]; + int sortedslots[MAX_PLAYER_RECORDS]; + player_record *pr,*dpr; + tPInfoStat stat; + int count,length,p; + + //sort the stats + DMFCBase->GetSortedPlayerSlots(sortedslots,MAX_PLAYER_RECORDS); + count = 1; + + sprintf(buffer,TXT_SAVE_HEADER,(DMFCBase->GetNetgameInfo())->name,(DMFCBase->GetCurrentMission())->cur_level); + DLLcf_WriteString(file,buffer); + + sprintf(buffer,TXT_SAVE_HEADERB); + DLLcf_WriteString(file,buffer); + sprintf(buffer,"-----------------------------------------------------------------------------------------------"); + DLLcf_WriteString(file,buffer); + + + for(int s=0;sGetPlayerRecord(p); + if( pr && pr->state!=STATE_EMPTY) { + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + memset(buffer,' ',BUFSIZE); + + sprintf(tempbuffer,"%d)",count); + memcpy(&buffer[0],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%s%s",(pr->state==STATE_INGAME)?"":"*",pr->callsign); + memcpy(&buffer[7],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.kills[DSTAT_LEVEL]-pr->dstats.suicides[DSTAT_LEVEL],pr->dstats.kills[DSTAT_OVERALL]-pr->dstats.suicides[DSTAT_OVERALL]); + memcpy(&buffer[36],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.kills[DSTAT_LEVEL],pr->dstats.kills[DSTAT_OVERALL]); + memcpy(&buffer[48],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.deaths[DSTAT_LEVEL],pr->dstats.deaths[DSTAT_OVERALL]); + memcpy(&buffer[60],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.suicides[DSTAT_LEVEL],pr->dstats.suicides[DSTAT_OVERALL]); + memcpy(&buffer[71],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%s",DMFCBase->GetTimeString(DMFCBase->GetTimeInGame(p))); + memcpy(&buffer[82],tempbuffer,strlen(tempbuffer)); + + int pos; + pos = 82 + strlen(tempbuffer) + 1; + if(posGetPlayerRecord(p); + if( pr && pr->state!=STATE_EMPTY) { + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + //Write out header + sprintf(buffer,"%d) %s%s",count,(pr->state==STATE_INGAME)?"":"*",pr->callsign); + DLLcf_WriteString(file,buffer); + length = strlen(buffer); + memset(buffer,'=',length); + buffer[length] = '\0'; + DLLcf_WriteString(file,buffer); + + //time in game + sprintf(buffer,TXT_SAVE_TIMEINGAME,DMFCBase->GetTimeString(DMFCBase->GetTimeInGame(p))); + DLLcf_WriteString(file,buffer); + + if(DMFCBase->FindPInfoStatFirst(p,&stat)){ + sprintf(buffer,TXT_SAVE_KILLERLIST); + DLLcf_WriteString(file,buffer); + + if(stat.slot!=p){ + memset(buffer,' ',BUFSIZE); + dpr = DMFCBase->GetPlayerRecord(stat.slot); + int pos; + + ASSERT(dpr!=NULL); + if(dpr){ + sprintf(tempbuffer,"%s",dpr->callsign); + memcpy(buffer,tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.kills); + memcpy(&buffer[30],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.deaths); + memcpy(&buffer[40],tempbuffer,strlen(tempbuffer)); + + pos = 40 + strlen(tempbuffer) + 1; + if(posFindPInfoStatNext(&stat)){ + if(stat.slot!=p){ + int pos; + memset(buffer,' ',BUFSIZE); + dpr = DMFCBase->GetPlayerRecord(stat.slot); + + if(dpr) + { + sprintf(tempbuffer,"%s",dpr->callsign); + memcpy(buffer,tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.kills); + memcpy(&buffer[30],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.deaths); + memcpy(&buffer[40],tempbuffer,strlen(tempbuffer)); + + pos = 40 + strlen(tempbuffer) + 1; + if(posFindPInfoStatClose(); + DLLcf_WriteString(file,""); //skip a line + count++; + } + } + + //done writing stats + DLLcfclose(file); + DLLAddHUDMessage(TXT_MSG_SAVESTATS); +} + +#define ROOTFILENAME "Anarchy" +void OnSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,false); + SaveStatsToFile(filename); +} + +void OnLevelEndSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,true); + SaveStatsToFile(filename); +} + +void OnDisconnectSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,false); + SaveStatsToFile(filename); +} + +void OnPrintScores(int level) +{ + char buffer[256]; + char name[50]; + memset(buffer,' ',256); + + int t; + int pos[6]; + int len[6]; + pos[0] = 0; t = len[0] = 20; //give ample room for pilot name + pos[1] = pos[0] + t + 1; t = len[1] = strlen(TXT_POINTS); + pos[2] = pos[1] + t + 1; t = len[2] = strlen(TXT_KILLS_SHORT); + pos[3] = pos[2] + t + 1; t = len[3] = strlen(TXT_DEATHS_SHORT); + pos[4] = pos[3] + t + 1; t = len[4] = strlen(TXT_SUICIDES_SHORT); + pos[5] = pos[4] + t + 1; t = len[5] = strlen(TXT_PING); + + memcpy(&buffer[pos[0]],TXT_PILOT,strlen(TXT_PILOT)); + memcpy(&buffer[pos[1]],TXT_POINTS,len[1]); + memcpy(&buffer[pos[2]],TXT_KILLS_SHORT,len[2]); + memcpy(&buffer[pos[3]],TXT_DEATHS_SHORT,len[3]); + memcpy(&buffer[pos[4]],TXT_SUICIDES_SHORT,len[4]); + memcpy(&buffer[pos[5]],TXT_PING,len[5]); + buffer[pos[5]+len[5]+1] = '\n'; + buffer[pos[5]+len[5]+2] = '\0'; + DPrintf(buffer); + + int slot; + player_record *pr; + int pcount; + + if(level<0 || level>=MAX_PLAYER_RECORDS) + pcount = MAX_PLAYER_RECORDS; + else + pcount = level; + + int sortedplayers[MAX_PLAYER_RECORDS]; + DMFCBase->GetSortedPlayerSlots(sortedplayers,MAX_PLAYER_RECORDS); + + for(int i=0;iGetPlayerRecord(slot); + if((pr)&&(pr->state!=STATE_EMPTY)){ + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + sprintf(name,"%s%s:",(pr->state==STATE_DISCONNECTED)?"*":"",pr->callsign); + name[19] = '\0'; + + memset(buffer,' ',256); + t = strlen(name); memcpy(&buffer[pos[0]],name,(tdstats.kills[DSTAT_LEVEL]-pr->dstats.suicides[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[1]],name,(tdstats.kills[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[2]],name,(tdstats.deaths[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[3]],name,(tdstats.suicides[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[4]],name,(tstate==STATE_INGAME) + sprintf(name,"%.0f",(DMFCBase->GetNetPlayers())[pr->pnum].ping_time*1000.0f); + else + strcpy(name,"---"); + t = strlen(name); memcpy(&buffer[pos[5]],name,(tGetPlayerNum()); + display_my_welcome = false; + } + + if(DisplayScoreScreen) + return; + + int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[HUD_FONT_INDEX]) + 3; + ubyte alpha = DMFCBase->ConvertHUDAlpha((ubyte)((DisplayScoreScreen)?128:255)); + int y = (DMFCBase->GetGameWindowH()/2) - ((height*5)/2); + int x = 520; + ddgr_color color; + + int rank = 1; + player_record *pr; + + //Display your Kills & Deaths on the top corners of the screen + pr = DMFCBase->GetPlayerRecordByPnum(DMFCBase->GetPlayerNum()); + if(pr){ + int y = 25,x; + int lwidth; + char buffer[20]; + + int w_kill,w_death,max_w; + w_kill = DLLgrtext_GetTextLineWidth(TXT_KILLS); + w_death = DLLgrtext_GetTextLineWidth(TXT_DEATHS); + max_w = max(w_kill,w_death); + + x = DMFCBase->GetGameWindowW() - DMFCBase->GetGameWindowW()*0.0078125f; + DLLgrtext_SetColor(GR_GREEN); + DLLgrtext_SetAlpha(alpha); + DLLgrtext_Printf(x-(max_w/2)-(w_kill/2),y,TXT_KILLS); + y+=height; + + sprintf(buffer,"%d",pr->dstats.kills[DSTAT_LEVEL]); + lwidth = DLLgrtext_GetTextLineWidth(buffer); + DLLgrtext_SetColor(GR_GREEN); + DLLgrtext_SetAlpha(alpha); + DLLgrtext_Printf(x-(max_w/2)-(lwidth/2),y,buffer); + y+=height+3; + + DLLgrtext_SetColor(GR_GREEN); + DLLgrtext_SetAlpha(alpha); + DLLgrtext_Printf(x - (max_w/2) - (w_death/2),y,TXT_DEATHS); + y+=height; + + sprintf(buffer,"%d",pr->dstats.deaths[DSTAT_LEVEL]); + lwidth = DLLgrtext_GetTextLineWidth(buffer); + DLLgrtext_SetColor(GR_GREEN); + DLLgrtext_SetAlpha(alpha); + DLLgrtext_Printf(x - (max_w/2) - (lwidth/2),y,buffer); + } + + int ESortedPlayers[DLLMAX_PLAYERS]; + + switch(Anarchy_hud_display){ + case AHD_NONE: + return; + break; + case AHD_EFFICIENCY: + DMFCBase->GetSortedPlayerSlots(ESortedPlayers,DLLMAX_PLAYERS); + break; + } + + char name[30]; + + //determine coordinates to use here + //we'll use a virtual width of 85 pixels on a 640x480 screen + //so first determine the new width + int name_width = 85.0f * DMFCBase->GetHudAspectX(); + int score_width = DLLgrtext_GetTextLineWidth("888"); + int name_x = DMFCBase->GetGameWindowW() - name_width - score_width - 10; + int score_x = DMFCBase->GetGameWindowW() - score_width - 5; + + for(int i=0;iGetPlayerRecord(slot); + + if((pr)&&(pr->state!=STATE_EMPTY)){ + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + if( (pr->state==STATE_DISCONNECTED) || (pr->state==STATE_INGAME && !DMFCBase->IsPlayerObserver(pr->pnum)) ){ + + if(pr->pnum==DMFCBase->GetPlayerNum()){ + + switch(HUD_color_model){ + case ACM_PLAYERCOLOR: + color = (DMFCBase->GetPlayerColors())[pr->pnum]; + break; + case ACM_NORMAL: + color = GR_RGB(40,255,40); + break; + }; + + if(Highlight_bmp>BAD_BITMAP_HANDLE){ + //draw the highlite bar in the background + DLLrend_SetAlphaValue(alpha*0.50f); + DLLrend_SetZBufferState (0); + DLLrend_SetTextureType (TT_LINEAR); + DLLrend_SetLighting (LS_NONE); + DLLrend_SetAlphaType (AT_CONSTANT_TEXTURE); + DLLrend_DrawScaledBitmap(name_x-2,y-2,score_x+score_width+2,y+height-1,Highlight_bmp,0,0,1,1,1.0,-1,NULL); + DLLrend_SetZBufferState (1); + } + + strcpy(name,pr->callsign); + DMFCBase->ClipString(name_width,name,true); + + DLLgrtext_SetAlpha(alpha); + DLLgrtext_SetColor(color); + DLLgrtext_Printf(name_x,y,"%s",name); + + if(Anarchy_hud_display==AHD_EFFICIENCY){ + float t = pr->dstats.kills[DSTAT_LEVEL]+pr->dstats.suicides[DSTAT_LEVEL]+pr->dstats.deaths[DSTAT_LEVEL]; + float value = (float)(pr->dstats.kills[DSTAT_LEVEL])/((t)?t:0.0000001f); + DLLgrtext_Printf(score_x,y,"%.1f",value); + }else{ + DLLgrtext_Printf(score_x,y,"%d",pr->dstats.kills[DSTAT_LEVEL]-pr->dstats.suicides[DSTAT_LEVEL]); + } + + y+=height; + }else + if(rank<6){ + + if(pr->state==STATE_DISCONNECTED){ + color = GR_GREY; + }else{ + switch(HUD_color_model){ + case ACM_PLAYERCOLOR: + color = (DMFCBase->GetPlayerColors())[pr->pnum]; + break; + case ACM_NORMAL: + color = GR_RGB(40,255,40); + break; + }; + } + strcpy(name,pr->callsign); + DMFCBase->ClipString(name_width,name,true); + + DLLgrtext_SetAlpha(alpha); + DLLgrtext_SetColor(color); + DLLgrtext_Printf(name_x,y,"%s",name); + + if(Anarchy_hud_display==AHD_EFFICIENCY){ + float t = pr->dstats.kills[DSTAT_LEVEL]+pr->dstats.suicides[DSTAT_LEVEL]+pr->dstats.deaths[DSTAT_LEVEL]; + float value = (float)(pr->dstats.kills[DSTAT_LEVEL])/((t)?t:0.0000001f); + DLLgrtext_Printf(score_x,y,"%.1f",value); + }else{ + DLLgrtext_Printf(score_x,y,"%d",pr->dstats.kills[DSTAT_LEVEL]-pr->dstats.suicides[DSTAT_LEVEL]); + } + + y+=height; + } + rank++; + } + } + } +} + +void DisplayWelcomeMessage(int player_num) +{ + char name_buffer[64]; + strcpy(name_buffer,(DMFCBase->GetPlayers())[player_num].callsign); + + if(player_num==DMFCBase->GetPlayerNum()) + { + DLLAddHUDMessage(TXT_WELCOME,name_buffer); + } + else + { + DLLAddHUDMessage(TXT_JOINED,name_buffer); + } +} + +void SwitchHUDColor(int i) +{ + if(i<0 || i>1) + return; + HUD_color_model = i; + + switch(HUD_color_model){ + case ACM_PLAYERCOLOR: + DLLAddHUDMessage(TXT_MSG_COLORPLR); + break; + case ACM_NORMAL: + DLLAddHUDMessage(TXT_MSG_COLORNORM); + break; + }; +} + +#ifdef MACINTOSH +#pragma export off +#endif \ No newline at end of file diff --git a/netgames/anarchy/anarchystr.h b/netgames/anarchy/anarchystr.h new file mode 100644 index 000000000..44cd93d98 --- /dev/null +++ b/netgames/anarchy/anarchystr.h @@ -0,0 +1,55 @@ +#ifndef __ANARCHY_STRING_TABLE_H_ +#define __ANARCHY_STRING_TABLE_H_ + +#define TXT(d) GetString(d) + + +#define TXT_DEATH1 TXT(0) //"%s got blasted by %s" +#define TXT_DEATH2 TXT(1) //"%s knows %s is his god" +#define TXT_DEATH3 TXT(2) //"%s sucks %s's milk" +#define TXT_DEATH4 TXT(3) //"%s realizes %s's power" +#define TXT_DEATH5 TXT(4) //"%s got killed by %s" +#define TXT_DEATH6 TXT(5) //"%s begs for %s's mercy" +#define TXT_DEATH7 TXT(6) //"%s realizes %s is a better player" +#define TXT_DEATH8 TXT(7) //"%s was no match for %s" +#define TXT_DEATH9 TXT(8) //"%s wishes he was as good as %s" +#define TXT_DEATH10 TXT(9) //"%s got messed up by %s" +#define TXT_SUICIDE1 TXT(10) //"%s blasts himself" +#define TXT_SUICIDE2 TXT(11) //"%s Bursts his own bubble" +#define TXT_SUICIDE3 TXT(12) //"%s doesn't know his own strength" +#define TXT_SUICIDE4 TXT(13) //"No prize for %s" +#define TXT_SUICIDE5 TXT(14) //"%s doesn't wish to live anymore" +#define TXT_SUICIDE6 TXT(15) //"%s SUCKS!" +#define TXT_PILOT TXT(16) //"Pilot" +#define TXT_KILLS TXT(17) //"Kills" +#define TXT_DEATHS TXT(18) //"Deaths" +#define TXT_SUICIDES TXT(19) //"Suicides" +#define TXT_SCORE TXT(20) //"Score" +#define TXT_STATS TXT(21) //"Stats" +#define TXT_WELCOME TXT(22) //"Welcome to the Anarchy %s!" +#define TXT_JOINED TXT(23) //"%s has joined the Anarchy" +#define TXT_POINTS TXT(24) //"Points" +#define TXT_HUDD_NONE TXT(25) //"Anarchy: HUD Display set to None" +#define TXT_HUDD_SCORES TXT(26) //"Anarchy: HUD Display set to Scores" +#define TXT_HUDD_EFFIC TXT(27) //"Anarchy: HUD Display set to Efficiency" +#define TXT_MNU_HUDSTYLE TXT(28) //"HUD Display" +#define TXT_NONE TXT(29) //"None" +#define TXT_SCOREHD TXT(30) //"Score" +#define TXT_EFFICIENCY TXT(31) //"Efficiency" +#define TXT_MNU_HUDCOLOR TXT(32) //"HUD Score Colors" +#define TXT_PLAYERCOLORS TXT(33) //"Player Colors" +#define TXT_NORMAL TXT(34) //"Normal" +#define TXT_PING TXT(35) //"Ping" +#define TXT_SAVE_HEADER TXT(36) //"Anarchy\r\nGame: %s\r\nLevel: %d\r\n" +#define TXT_SAVE_HEADERB TXT(37) //"[Rank] [Name] [Score] [Kills] [Deaths] [Suicides] [Time In Game]" +#define TXT_SAVE_HEADERC TXT(38) //"\r\nIndividual Stats\r\n" +#define TXT_SAVE_TIMEINGAME TXT(39) //"Total Time In Game: %s" +#define TXT_SAVE_KILLERLIST TXT(40) //"Callsign: Kills: Deaths:" +#define TXT_MSG_SAVESTATS TXT(41) //"Stats saved to file" +#define TXT_MSG_COLORPLR TXT(42) //"Anarchy HUD Color Model: Player Colors" +#define TXT_MSG_COLORNORM TXT(43) //"Anarchy HUD Color Model: Normal" +#define TXT_KILLS_SHORT TXT(44) //"K" +#define TXT_DEATHS_SHORT TXT(45) //"D" +#define TXT_SUICIDES_SHORT TXT(46) //"S" + +#endif diff --git a/netgames/coop.d3m b/netgames/coop.d3m new file mode 100644 index 000000000..3f8c49ddd Binary files /dev/null and b/netgames/coop.d3m differ diff --git a/netgames/coop/CMakeLists.txt b/netgames/coop/CMakeLists.txt new file mode 100644 index 000000000..fcdfffc9f --- /dev/null +++ b/netgames/coop/CMakeLists.txt @@ -0,0 +1,12 @@ +SET (HEADERS coop.h) +SET (CPPS coop.cpp) + +SET (NETGAME_MODULE coop) + +ADD_LIBRARY(${NETGAME_MODULE} SHARED ${CPPS} ${HEADERS}) +set_target_properties(${NETGAME_MODULE} PROPERTIES PREFIX "") +set_target_properties(${NETGAME_MODULE} PROPERTIES SUFFIX ".d3m") + +target_link_libraries(${NETGAME_MODULE} dmfc) + +install(TARGETS ${NETGAME_MODULE} DESTINATION "${D3_GAMEDIR}netgames/") diff --git a/netgames/coop/coop.cpp b/netgames/coop/coop.cpp new file mode 100644 index 000000000..1e296dfb1 --- /dev/null +++ b/netgames/coop/coop.cpp @@ -0,0 +1,862 @@ +/* +* $Logfile: /DescentIII/Main/coop/coop.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:56:42 $ +* $Author: kevinb $ +* +* Co-Op Play +* +* $Log: coop.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:56:42 kevinb +* initial 1.5 import +* + * + * 23 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 22 7/12/99 1:17p Jeff + * added netgame flag NF_COOP + * + * 21 5/23/99 3:04a Jason + * fixed bug with player rankings not being updated correctly + * + * 20 5/19/99 3:53p Jeff + * fixed possible (???) bug with displaying hud names + * + * 19 5/12/99 11:04p Jeff + * dmfc and multiplayer games now have endian friendly packets (*whew*) + * + * 18 5/09/99 6:20a Jeff + * improved Entropy (added sounds, max virii per room). Fixed rendering + * bugs for other multiplayer dlls. + * + * 17 5/07/99 12:52p Jeff + * audio taunt icon is ppic if available. coop has hard max team set of 4 + * + * 16 5/02/99 8:39a Jeff + * made quick & dirty PLR for coop + * + * 15 4/23/99 6:15p Jeff + * fixed double calls to GameClose + * + * 14 4/23/99 12:43p Jeff + * forgot to call CloseGame + * + * 13 4/21/99 5:34p Jeff + * added requirements + * + * 12 4/19/99 6:07p Jeff + * compile for Linux + * + * 11 3/27/99 4:53p Jeff + * player rankings in multiplayer games implemented. Fixed join message + * so it doesn't get cut off + * + * 10 3/19/99 12:54p Jeff + * base support for requesting the number of teams for a multiplayer game + * + * 9 3/18/99 8:40p Jeff + * hi res font fixes + * + * 8 3/17/99 12:23p Jeff + * converted DMFC to be COM interface + * + * 7 3/01/99 4:17p Jeff + * added net flag whether buddy bot allowed + * + * 6 2/11/99 3:09p Jeff + * localized + * + * 5 2/11/99 12:49a Jeff + * changed names of exported variables + * + * 3 2/07/99 2:06a Jeff + * updated coop...fixed bug when getting countermeasure owner, if owner is + * observer + * + * 2 2/03/99 7:22p Jeff + * + * 1 2/03/99 7:19p Jeff +* +* $NoKeywords: $ +*/ + + +#include +#include +#include +#include "idmfc.h" +#include "coop.h" +#include "coopstr.h" +IDMFC *DMFCBase = NULL; +IDmfcStats *dstat = NULL; +player *dPlayers; + +typedef struct{ + int Score[2]; +}tPlayerStat; +int pack_pstat(tPlayerStat *user_info,ubyte *data) +{ + int count = 0; + MultiAddInt(user_info->Score[0],data,&count); + MultiAddInt(user_info->Score[1],data,&count); + return count; +} + +int unpack_pstat(tPlayerStat *user_info,ubyte *data) +{ + int count = 0; + user_info->Score[0] = MultiGetInt(data,&count); + user_info->Score[1] = MultiGetInt(data,&count); + return count; +} + + +/////////////////////////////////////////////// +//localization info +char **StringTable; +int StringTableSize = 0; +char *_ErrorString = "Missing String"; +char *GetStringFromTable(int d){if( (d<0) || (d>=StringTableSize) ) return _ErrorString; else return StringTable[d];} +/////////////////////////////////////////////// +int SortedPlayers[MAX_PLAYER_RECORDS]; +bool DisplayScoreScreen = false; +bool FirstFrame = false; +bool display_my_welcome = false; + +void DisplayWelcomeMessage(int player_num); +void DisplayHUDScores(struct tHUDItem *hitem); +int Highlight_bmp = -1; + +void OnPLRInterval(void); +void OnPLRInit(void); + +#ifdef MACINTOSH +#pragma export on +#endif + +void DetermineScore(int precord_num,int column_num,char *buffer,int buffer_size) +{ + player_record *pr = DMFCBase->GetPlayerRecord(precord_num); + if(!pr || pr->state==STATE_EMPTY){ + buffer[0] = '\0'; + return; + } + tPlayerStat *stat = (tPlayerStat *)pr->user_info; + + sprintf(buffer,"%d",stat->Score[DSTAT_LEVEL]); +} + +// This function gets called by the game when it wants to learn some info about the game +void DLLFUNCCALL DLLGetGameInfo (tDLLOptions *options) +{ + options->flags = DOF_MAXTEAMS; + options->max_teams = 1; + strcpy(options->game_name,TXT_COOP); + strcpy(options->requirements,"COOP"); +} + +// Initializes the game function pointers +void DLLFUNCCALL DLLGameInit (int *api_func,ubyte *all_ok,int num_teams_to_use) +{ + *all_ok = 1; + DMFCBase = CreateDMFC(); + if(!DMFCBase) + { + *all_ok = 0; + return; + } + + dstat = CreateDmfcStats(); + if(!dstat) + { + *all_ok = 0; + return; + } + + DMFCBase->LoadFunctions(api_func); + + //Setup event handlers + DMFCBase->Set_OnHUDInterval(OnHUDInterval); + DMFCBase->Set_OnInterval(OnInterval); + DMFCBase->Set_OnKeypress(OnKeypress); + DMFCBase->Set_OnClientPlayerEntersGame(OnClientPlayerEntersGame); + DMFCBase->Set_OnServerObjectKilled(OnServerObjectKilled); + DMFCBase->Set_OnClientObjectKilled(OnClientObjectKilled); + DMFCBase->Set_OnPlayerConnect(OnPlayerConnect); + DMFCBase->Set_OnServerGameCreated(OnServerGameCreated); + DMFCBase->Set_OnClientLevelStart(OnClientLevelStart); + DMFCBase->Set_OnGameStateRequest(OnGameStateRequest); + DMFCBase->Set_OnSaveStatsToFile(OnSaveStatsToFile); + DMFCBase->Set_OnLevelEndSaveStatsToFile(OnLevelEndSaveStatsToFile); + DMFCBase->Set_OnDisconnectSaveStatsToFile(OnDisconnectSaveStatsToFile); + DMFCBase->Set_OnPrintScores(OnPrintScores); + DMFCBase->Set_OnPLRInterval(OnPLRInterval); + DMFCBase->Set_OnPLRInit(OnPLRInit); + + dPlayers = DMFCBase->GetPlayers(); + + DLLCreateStringTable("Coop.str",&StringTable,&StringTableSize); + mprintf((0,"%d strings loaded from string table\n",StringTableSize)); + if(!StringTableSize){ + *all_ok = 0; + return; + } + + DMFCBase->GameInit(1); + + Highlight_bmp = DLLbm_AllocBitmap(32,32,0); + if(Highlight_bmp>BAD_BITMAP_HANDLE){ + ushort *data = DLLbm_data(Highlight_bmp,0); + if(!data){ + //bail on out of here + *all_ok = 0; + return; + } + for(int x=0;x<32*32;x++){ + data[x] = GR_RGB16(50,50,50)|OPAQUE_FLAG; + } + } + + DMFCBase->AddHUDItemCallback(HI_TEXT,DisplayHUDScores); + DMFCBase->SetupPlayerRecord(sizeof(tPlayerStat),(int (*)(void *,ubyte *))pack_pstat,(int (*)(void *,ubyte *))unpack_pstat); + DMFCBase->SetNumberOfTeams(1); + + netgame_info *netgameinfo = DMFCBase->GetNetgameInfo(); + netgameinfo->flags |= (NF_USE_ROBOTS|NF_RESPAWN_WAYPOINT|NF_ALLOWGUIDEBOT|NF_COOP); + netgameinfo->max_players = min(4,netgameinfo->max_players); + DMFCBase->SetMaxPlayerHardLimit(4); + + // Initialize the Stats Manager + // ---------------------------- + tDmfcStatsInit tsi; + tDmfcStatsColumnInfo pl_col[5]; + char gname[20]; + strcpy(gname,TXT_STATGAMENAME); + + tsi.flags = DSIF_SHOW_PIC|DSIF_SHOW_OBSERVERICON|DSIF_NOLASTKILLER|DSIF_NOLASTVICTIM|DSIF_NODETAILEDINFO|DSIF_SHOWPLAYERVIEW; + tsi.cColumnCountDetailed = 0; + tsi.cColumnCountPlayerList = 5; + tsi.clbDetailedColumn = NULL; + tsi.clbDetailedColumnBMP = NULL; + tsi.clbPlayerColumn = DetermineScore; + tsi.clbPlayerColumnBMP = NULL; + tsi.DetailedColumns = NULL; + tsi.GameName = gname; + tsi.MaxNumberDisplayed = NULL; + tsi.PlayerListColumns = pl_col; + tsi.SortedPlayerRecords = SortedPlayers; + + pl_col[0].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[0].title,TXT_PILOT); + pl_col[0].type = DSCOL_PILOT_NAME; + pl_col[0].width = 84; + + pl_col[1].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[1].title,TXT_KILLS); + pl_col[1].type = DSCOL_CUSTOM; + pl_col[1].width = 51; + + pl_col[2].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[2].title,TXT_DEATHS); + pl_col[2].type = DSCOL_DEATHS_LEVEL; + pl_col[2].width = 64; + + pl_col[3].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[3].title,TXT_SUICIDES); + pl_col[3].type = DSCOL_SUICIDES_LEVEL; + pl_col[3].width = 69; + + pl_col[4].color_type = DSCOLOR_SHIPCOLOR; + strcpy(pl_col[4].title,TXT_PING); + pl_col[4].type = DSCOL_PING; + pl_col[4].width = 40; + + dstat->Initialize(&tsi); +} +// Called when the DLL is shutdown +void DLLFUNCCALL DLLGameClose () +{ + if(Highlight_bmp>BAD_BITMAP_HANDLE) + DLLbm_FreeBitmap(Highlight_bmp); + + DLLDestroyStringTable(StringTable,StringTableSize); + + if(dstat) + { + dstat->DestroyPointer(); + dstat = NULL; + } + + if(DMFCBase) + { + DMFCBase->GameClose(); + DMFCBase->DestroyPointer(); + DMFCBase = NULL; + } +} + + +void OnHUDInterval(void) +{ + dstat->DoFrame(); + DMFCBase->DisplayOutrageLogo(); + DMFCBase->OnHUDInterval(); +} + +bool compare_slots(int a,int b) +{ + int ascore,bscore; + player_record *apr,*bpr; + tPlayerStat *astat,*bstat; + + apr = DMFCBase->GetPlayerRecord(a); + bpr = DMFCBase->GetPlayerRecord(b); + if( !apr ) + return true; + if( !bpr ) + return false; + if( apr->state==STATE_EMPTY ) + return true; + if( bpr->state==STATE_EMPTY ) + return false; + astat = (tPlayerStat *)apr->user_info; + bstat = (tPlayerStat *)bpr->user_info; + + if( (apr->state==STATE_INGAME) && (bpr->state==STATE_INGAME) ){ + //both players were in the game + ascore = (astat)?astat->Score[DSTAT_LEVEL]:0; + bscore = (bstat)?bstat->Score[DSTAT_LEVEL]:0; + return (ascorestate==STATE_INGAME) && (bpr->state==STATE_DISCONNECTED) ){ + //apr gets priority since he was in the game on exit + return false; + } + if( (apr->state==STATE_DISCONNECTED) && (bpr->state==STATE_INGAME) ){ + //bpr gets priority since he was in the game on exit + return true; + } + //if we got here then both players were disconnected + ascore = (astat)?astat->Score[DSTAT_LEVEL]:0; + bscore = (bstat)?bstat->Score[DSTAT_LEVEL]:0; + return (ascore=0 && compare_slots(SortedPlayers[j],t); j--){ + SortedPlayers[j+1] = SortedPlayers[j]; + } + // insert + SortedPlayers[j+1] = t; + } + + FirstFrame = true; + DMFCBase->OnInterval(); +} + +void OnKeypress(int key) +{ + dllinfo *Data = DMFCBase->GetDLLInfoCallData(); + + switch(key){ + case K_F7: + DisplayScoreScreen = !DisplayScoreScreen; + DMFCBase->EnableOnScreenMenu(false); + dstat->Enable(DisplayScoreScreen); + break; + case K_PAGEDOWN: + if(DisplayScoreScreen){ + dstat->ScrollDown(); + Data->iRet = 1; + } + break; + case K_PAGEUP: + if(DisplayScoreScreen){ + dstat->ScrollUp(); + Data->iRet = 1; + } + break; + case K_F6: + DisplayScoreScreen = false; + dstat->Enable(false); + break; + case K_ESC: + if(DisplayScoreScreen){ + dstat->Enable(false); + DisplayScoreScreen = false; + Data->iRet = 1; + } + break; + } + + DMFCBase->OnKeypress(key); +} + +void OnClientPlayerEntersGame(int player_num) +{ + if(player_num==DMFCBase->GetPlayerNum()){ + FirstFrame = false; + DisplayScoreScreen = false; + } + + DMFCBase->OnClientPlayerEntersGame(player_num); + + if(player_num!=DMFCBase->GetPlayerNum()) + DisplayWelcomeMessage(player_num); + else + display_my_welcome = true; +} + +// A new player has entered the game, zero there stats out +void OnPlayerConnect(int player_num) +{ + DMFCBase->OnPlayerConnect(player_num); + + tPlayerStat *stat = (tPlayerStat *)DMFCBase->GetPlayerRecordData(player_num); + if(stat){ + stat->Score[DSTAT_LEVEL] = 0; + stat->Score[DSTAT_OVERALL] = 0; + } +} + + +// The server has just started, so clear out all the stats and game info +void OnServerGameCreated(void) +{ + DMFCBase->OnServerGameCreated(); + player_record *pr; + tPlayerStat *stat; + for(int i=0;iGetPlayerRecord(i); + if(pr) + stat = (tPlayerStat *) pr->user_info; + else + stat = NULL; + if(stat){ + stat->Score[DSTAT_LEVEL] = 0; + stat->Score[DSTAT_OVERALL] = 0; + } + } +} + +// The server has started a new level, so clear out level scores +void OnClientLevelStart(void) +{ + DMFCBase->OnClientLevelStart(); + player_record *pr; + tPlayerStat *stat; + for(int i=0;iGetPlayerRecord(i); + if(pr) + stat = (tPlayerStat *)pr->user_info; + else + stat = NULL; + + if(stat){ + stat->Score[DSTAT_LEVEL] = 0; + } + } + DMFCBase->RequestGameState(); +} + +// We need to send all the inventory info out to the new player +void OnGameStateRequest(int player_num) +{ + DMFCBase->OnGameStateRequest(player_num); +} + +void DisplayWelcomeMessage(int player_num) +{ + char name_buffer[64]; + strcpy(name_buffer,(DMFCBase->GetPlayers())[player_num].callsign); + + if(player_num==DMFCBase->GetPlayerNum()) + { + DLLAddHUDMessage(TXT_WELCOME,name_buffer); + } + else + { + DLLAddHUDMessage(TXT_JOINED,name_buffer); + } +} + +void OnServerObjectKilled(object *obj,object *killer) +{ + if(!killer) + return; //can't handle this + + if(obj->type==OBJ_ROBOT || (obj->type == OBJ_BUILDING && obj->ai_info)){ + + //check to see if the killer is a player + bool ok_to_call = false; + + if(killer->type==OBJ_PLAYER){ + ok_to_call = true; + }else if(killer->type==OBJ_ROBOT){ + if(DMFCBase->GetCounterMeasureOwner(killer)!=-1){ + ok_to_call = true; + } + } + + if(ok_to_call){ + DMFCBase->CallClientEvent(EVT_CLIENT_GAMEOBJKILLED,DMFCBase->GetMeObjNum(),DMFCBase->GetItObjNum(),-1); + DMFCBase->CallOnClientObjectKilled(obj,killer); + } + } + + DMFCBase->OnServerObjectKilled(obj,killer); +} + +void OnClientObjectKilled(object *obj,object *killer) +{ + object *parent; + DLLGetUltimateParentForObject(&parent,killer); + + if((parent->type!=OBJ_PLAYER)&&(parent->type!=OBJ_OBSERVER)){ + mprintf((0,"Robot killed wasn't by a OBJ_PLAYER or OBJ_OBSERVER (%d)\n",parent->type)); + return; + } + + int killer_pnum = parent->id; + + player_record *pr = DMFCBase->GetPlayerRecordByPnum(killer_pnum); + if(!pr){ + mprintf((0,"Invalid player record!\n")); + Int3(); + return; + } + + tPlayerStat *stat = (tPlayerStat *)pr->user_info; + + stat->Score[DSTAT_LEVEL]++; + stat->Score[DSTAT_OVERALL]++; + + mprintf((0,"%s's score is now %d\n",dPlayers[killer_pnum].callsign,stat->Score[DSTAT_LEVEL])); + + DMFCBase->OnClientObjectKilled(obj,killer); +} + +void DisplayHUDScores(struct tHUDItem *hitem) +{ + if(!FirstFrame) + return; + + if(display_my_welcome) + { + DisplayWelcomeMessage(DMFCBase->GetPlayerNum()); + display_my_welcome = false; + } + + if(DisplayScoreScreen) + return; + + if(DMFCBase->IAmDedicatedServer()) + return; + + int old_font = DLLgrtext_GetFont(); + int start_y; + int i; + + DLLgrtext_SetFont((DMFCBase->GetGameFontTranslateArray())[HUD_FONT_INDEX]); + int height = DLLRenderHUDGetTextHeight("X") + 1; + int players_in_game = 0; + + for(i=0;iCheckPlayerNum(i) && !DMFCBase->IsPlayerDedicatedServer(i)){ + players_in_game++; + } + } + + if(players_in_game<=0) + return; + + players_in_game = min(players_in_game,8); + + start_y = (DMFCBase->GetGameWindowH()/2) - ((players_in_game*5)/2); + //determine coordinates to use here + //we'll use a virtual width of 85 pixels on a 640x480 screen + //so first determine the new width + int name_width = 85.0f * DMFCBase->GetHudAspectX(); + int name_x = DMFCBase->GetGameWindowW() - name_width - 10; + + int pnum,index = 0; + ubyte alpha = DMFCBase->ConvertHUDAlpha((ubyte)((DisplayScoreScreen)?128:255)); + + DLLgrtext_SetAlpha(alpha); + char name[64]; + + // Display only the players in the game + while( (players_in_game > 0) && (indexCheckPlayerNum(pnum) && !DMFCBase->IsPlayerDedicatedServer(pnum) ){ + //valid player + if(pnum==DMFCBase->GetPlayerNum()) + { + if(Highlight_bmp>BAD_BITMAP_HANDLE){ + //draw the highlite bar in the background + DLLrend_SetAlphaValue(alpha*0.50f); + DLLrend_SetZBufferState (0); + DLLrend_SetTextureType (TT_LINEAR); + DLLrend_SetLighting (LS_NONE); + DLLrend_SetAlphaType (AT_CONSTANT_TEXTURE); + + DLLrend_DrawScaledBitmap(name_x-2,start_y-2,DMFCBase->GetGameWindowW()-8,start_y+height+1,Highlight_bmp,0,0,1,1,1.0,-1,NULL); + DLLrend_SetZBufferState (1); + } + } + + strcpy(name,dPlayers[pnum].callsign); + DMFCBase->ClipString(name_width,name,true); + + DLLgrtext_SetColor((DMFCBase->GetPlayerColors())[pnum]); + DLLgrtext_Printf(name_x,start_y,name); + + start_y += height; + players_in_game--; + } + index++; + } + + DLLgrtext_SetFont(old_font); +} + +void SaveStatsToFile(char *filename) +{ + /* + CFILE *file; + DLLOpenCFILE(&file,filename,"wt"); + if(!file){ + mprintf((0,"Unable to open output file\n")); + return; + } + + //write out game stats + #define BUFSIZE 150 + char buffer[BUFSIZE]; + char tempbuffer[25]; + int sortedslots[MAX_PLAYER_RECORDS]; + player_record *pr,*dpr; + tPInfoStat stat; + int count,length,p; + + count = 1; + + sprintf(buffer,TXT_SAVE_HEADER,Netgame->name,Current_mission->cur_level); + DLLcf_WriteString(file,buffer); + + sprintf(buffer,TXT_SAVE_HEADERB); + DLLcf_WriteString(file,buffer); + sprintf(buffer,"-----------------------------------------------------------------------------------"); + DLLcf_WriteString(file,buffer); + + + tPlayerStat *st; + for(int s=0;sstate!=STATE_EMPTY) { + + if(IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + st = (tPlayerStat *)pr->user_info; + + memset(buffer,' ',BUFSIZE); + + sprintf(tempbuffer,"%d)",count); + memcpy(&buffer[0],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%s%s",(pr->state==STATE_INGAME)?"":"*",pr->callsign); + memcpy(&buffer[7],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",st->Score[DSTAT_LEVEL],st->Score[DSTAT_OVERALL]); + memcpy(&buffer[36],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.deaths[DSTAT_LEVEL],pr->dstats.deaths[DSTAT_OVERALL]); + memcpy(&buffer[48],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.suicides[DSTAT_LEVEL],pr->dstats.suicides[DSTAT_OVERALL]); + memcpy(&buffer[60],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%s",GetTimeString(GetTimeInGame(p))); + memcpy(&buffer[71],tempbuffer,strlen(tempbuffer)); + + int pos; + pos = 71 + strlen(tempbuffer) + 1; + if(posGenerateStatFilename(filename,ROOTFILENAME,false); + SaveStatsToFile(filename); +} + +void OnLevelEndSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,true); + SaveStatsToFile(filename); +} + +void OnDisconnectSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,false); + SaveStatsToFile(filename); +} + +void OnPrintScores(int level) +{ + /* + char buffer[256]; + char name[50]; + memset(buffer,' ',256); + + int t; + int pos[5]; + int len[5]; + pos[0] = 0; t = len[0] = 20; //give ample room for pilot name + pos[1] = pos[0] + t + 1; t = len[1] = strlen(TXT_KILLS_SHORT); + pos[2] = pos[1] + t + 1; t = len[2] = strlen(TXT_DEATHS_SHORT); + pos[3] = pos[2] + t + 1; t = len[3] = strlen(TXT_SUICIDES_SHORT); + pos[4] = pos[3] + t + 1; t = len[4] = strlen(TXT_PING); + + memcpy(&buffer[pos[0]],TXT_PILOT,strlen(TXT_PILOT)); + memcpy(&buffer[pos[1]],TXT_KILLS_SHORT,len[1]); + memcpy(&buffer[pos[2]],TXT_DEATHS_SHORT,len[2]); + memcpy(&buffer[pos[3]],TXT_SUICIDES_SHORT,len[3]); + memcpy(&buffer[pos[4]],TXT_PING,len[4]); + buffer[pos[4]+len[4]+1] = '\n'; + buffer[pos[4]+len[4]+2] = '\0'; + DPrintf(buffer); + + int slot; + player_record *pr; + int pcount; + + if(level<0 || level>=MAX_PLAYER_RECORDS) + pcount = MAX_PLAYER_RECORDS; + else + pcount = level; + + tPlayerStat *st; + + for(int i=0;istate!=STATE_EMPTY)){ + + if(IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + st = (tPlayerStat *)pr->user_info; + + sprintf(name,"%s%s:",(pr->state==STATE_DISCONNECTED)?"*":"",pr->callsign); + name[19] = '\0'; + + memset(buffer,' ',256); + t = strlen(name); memcpy(&buffer[pos[0]],name,(tScore[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[1]],name,(tdstats.deaths[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[2]],name,(tdstats.suicides[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[3]],name,(tstate==STATE_INGAME) + sprintf(name,"%.0f",NetPlayers[pr->pnum].ping_time*1000.0f); + else + strcpy(name,"---"); + t = strlen(name); memcpy(&buffer[pos[4]],name,(tGetGametime(); +} + +void OnPLRInterval(void) +{ + level_info *li = DMFCBase->GetLevelInfo(); + + int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[BIG_FONT_INDEX]); + DLLgrtext_SetFont((DMFCBase->GetGameFontTranslateArray())[BIG_FONT_INDEX]); + + int y = 240; + + float ft,newtime; + newtime = DMFCBase->GetGametime(); + ft = newtime - PLRLasttime; + PLRLasttime = newtime; + PLRElapsedtime += ft; + + float alpha; + + if(PLRElapsedtime>5.0f) + { + alpha = 1.0f; + } + else + { + alpha = (5.0f - PLRElapsedtime)/5.0f; + } + + DLLrend_ClearScreen(GR_BLACK); + DLLgrtext_SetColor(GR_RGB(40,40,255)); + DLLgrtext_CenteredPrintf(0,y-height-1,TXT_LEVELCOMPLETE); + + if(li) + { + DLLgrtext_SetAlpha((ubyte)(alpha*255.0f)); + DLLgrtext_SetColor(GR_RGB(255,255,255)); + DLLgrtext_CenteredPrintf(0,y+1,li->name); + } + + DLLgrtext_Flush(); +} + +#ifdef MACINTOSH +#pragma export off +#endif diff --git a/netgames/coop/coop.h b/netgames/coop/coop.h new file mode 100644 index 000000000..59400ea2b --- /dev/null +++ b/netgames/coop/coop.h @@ -0,0 +1,171 @@ +/* +* $Logfile: /DescentIII/Main/coop/coop.h $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:56:42 $ +* $Author: kevinb $ +* +* Co-Op play +* +* $Log: coop.h,v $ +* Revision 1.1.1.1 2003/08/26 03:56:42 kevinb +* initial 1.5 import +* + * + * 8 10/21/99 9:28p Jeff + * B.A. Macintosh code merge + * + * 7 3/22/99 5:51p Matt + * Removed some includes from one file and added some to other files, to + * reduce the amount of rebuilding when headers change. + * + * 6 3/19/99 12:54p Jeff + * base support for requesting the number of teams for a multiplayer game + * + * 5 3/17/99 12:23p Jeff + * converted DMFC to be COM interface + * + * 4 2/08/99 12:46a Jeff + * updated + * + * 3 2/07/99 2:06a Jeff + * updated coop...fixed bug when getting countermeasure owner, if owner is + * observer + * + * 2 2/03/99 8:48p Josh + * Luke: Checked in for Josh (copied from Jeff) so he could work + * + * 1 2/03/99 8:47p Josh +* +* $NoKeywords: $ +*/ + + +#ifndef __DMFC_APP_H_ +#define __DMFC_APP_H_ + +#include "osiris_share.h" +#include "d3events.h" + +void OnHUDInterval(void); +void OnInterval(void); +void OnKeypress(int key); +void OnClientPlayerEntersGame(int player_num); +void OnServerObjectKilled(object *obj,object *killer); +void OnClientObjectKilled(object *obj,object *killer); +void OnPlayerConnect(int player_num); +void OnServerGameCreated(void); +void OnClientLevelStart(void); +void OnGameStateRequest(int player_num); +void SaveStatsToFile(char *filename); +void OnSaveStatsToFile(void); +void OnLevelEndSaveStatsToFile(void); +void OnDisconnectSaveStatsToFile(void); +void OnPrintScores(int level); +extern IDMFC *DMFCBase; + +// These next two function prototypes MUST appear in the extern "C" block if called +// from a CPP file. +#ifdef __cplusplus +extern "C" +{ +#endif + DLLEXPORT void DLLFUNCCALL DLLGameInit (int *api_func,ubyte *all_ok,int num_teams_to_use); + DLLEXPORT void DLLFUNCCALL DLLGameCall (int eventnum,dllinfo *data); + DLLEXPORT void DLLFUNCCALL DLLGameClose (); + DLLEXPORT void DLLFUNCCALL DLLGetGameInfo (tDLLOptions *options); + DLLEXPORT int DLLFUNCCALL GetGOScriptID(char *name,ubyte isdoor); + DLLEXPORT void DLLFUNCCALLPTR CreateInstance(int id); + DLLEXPORT void DLLFUNCCALL DestroyInstance(int id,void *ptr); + DLLEXPORT short DLLFUNCCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data); + DLLEXPORT int DLLFUNCCALL SaveRestoreState( void *file_ptr, ubyte saving_state ); +#ifdef __cplusplus +} +#endif + +#ifdef MACINTOSH +#pragma export on +#endif + +// The main entry point where the game calls the dll +void DLLFUNCCALL DLLGameCall (int eventnum,dllinfo *data) +{ + if((eventnumGetLocalRole()!=LR_SERVER)){ + return; + } + + DMFCBase->TranslateEvent(eventnum,data); +} + +// GetGOScriptID +// Purpose: +// Given the name of the object (from it's pagename), this function will search through it's +// list of General Object Scripts for a script with a matching name (to see if there is a script +// for that type/id of object within this DLL). If a matching scriptname is found, a UNIQUE ID +// is to be returned back to Descent 3. This ID will be used from here on out for all future +// interaction with the DLL. Since doors are not part of the generic object's, it's possible +// for a door to have the same name as a generic object (OBJ_POWERUP, OBJ_BUILDING, OBJ_CLUTTER +// or OBJ_ROBOT), therefore, a 1 is passed in for isdoor if the given object name refers to a +// door, else it is a 0. The return value is the unique identifier, else -1 if the script +// does not exist in the DLL. +int DLLFUNCCALL GetGOScriptID(char *name,ubyte isdoor) +{ + return -1; +} + +// CreateInstance +// Purpose: +// Given an ID from a call to GetGOScriptID(), this function will create a new instance for that +// particular script (by allocating and initializing memory, etc.). A pointer to this instance +// is to be returned back to Descent 3. This pointer will be passed around, along with the ID +// for CallInstanceEvent() and DestroyInstance(). Return NULL if there was an error. +void DLLFUNCCALLPTR CreateInstance(int id) +{ + return NULL; +} + +// DestroyInstance +// Purpose: +// Given an ID, and a pointer to a particular instance of a script, this function will delete and +// destruct all information associated with that script, so it will no longer exist. +void DLLFUNCCALL DestroyInstance(int id,void *ptr) +{ +} + +// CallInstanceEvent +// Purpose: +// Given an ID, a pointer to a script instance, an event and a pointer to the struct of +// information about the event, this function will translate who this event belongs to and +// passes the event to that instance of the script to be handled. Return a combination of +// CONTINUE_CHAIN and CONTINUE_DEFAULT, to give instructions on what to do based on the +// event. CONTINUE_CHAIN means to continue through the chain of scripts (custom script, level +// script, mission script, and finally default script). If CONTINUE_CHAIN is not specified, +// than the chain is broken and those scripts of lower priority will never get the event. Return +// CONTINUE_DEFAULT in order to tell D3 if you want process the normal action that is built into +// the game for that event. This only pertains to certain events. If the chain continues +// after this script, than the CONTINUE_DEFAULT setting will be overridden by lower priority +// scripts return value. +short DLLFUNCCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data) +{ + return CONTINUE_CHAIN|CONTINUE_DEFAULT; +} + +// SaveRestoreState +// Purpose: +// This function is called when Descent 3 is saving or restoring the game state. In this function +// you should save/restore any global data that you want preserved through load/save (which includes +// demos). You must be very careful with this function, corrupting the file (reading or writing too +// much or too little) may be hazardous to the game (possibly making it impossible to restore the +// state). It would be best to use version information to keep older versions of saved states still +// able to be used. IT IS VERY IMPORTANT WHEN SAVING THE STATE TO RETURN THE NUMBER OF _BYTES_ WROTE +// TO THE FILE. When restoring the data, the return value is ignored. saving_state is 1 when you should +// write data to the file_ptr, 0 when you should read in the data. +int DLLFUNCCALL SaveRestoreState( void *file_ptr, ubyte saving_state ) +{ + return 0; +} + +#ifdef MACINTOSH +#pragma export off +#endif + +#endif diff --git a/netgames/coop/coopstr.h b/netgames/coop/coopstr.h new file mode 100644 index 000000000..d8945c9bd --- /dev/null +++ b/netgames/coop/coopstr.h @@ -0,0 +1,23 @@ +//Defines for indexes into the string table for in-code strings + +#ifndef __STRING_TABLE____ +#define __STRING_TABLE____ + +#define TXT(index) GetStringFromTable(index) + +//Returns a pointer to the string at the index location from the string table +//if it is a bad index given, then the pointer to the error string "ERROR MISSING STRING" is given + +char *GetStringFromTable(int index); + +#define TXT_COOP TXT(0) //"Coop" +#define TXT_STATGAMENAME TXT(1) //"Co-op Descent 3" +#define TXT_PILOT TXT(2) //"Pilot" +#define TXT_KILLS TXT(3) //"Kills" +#define TXT_DEATHS TXT(4) //"Deaths" +#define TXT_SUICIDES TXT(5) //"Suicides" +#define TXT_PING TXT(6) //"Ping" +#define TXT_WELCOME TXT(7) //"Welcome to the Co-Op Game %s" +#define TXT_JOINED TXT(8) //"%s has joined the game" +#define TXT_LEVELCOMPLETE TXT(9) //"Level Completed!" +#endif diff --git a/netgames/ctf.d3m b/netgames/ctf.d3m new file mode 100644 index 000000000..748a1c021 Binary files /dev/null and b/netgames/ctf.d3m differ diff --git a/netgames/ctf/CMakeLists.txt b/netgames/ctf/CMakeLists.txt new file mode 100644 index 000000000..c48dc7f89 --- /dev/null +++ b/netgames/ctf/CMakeLists.txt @@ -0,0 +1,11 @@ +SET (HEADERS ctf.h CTFstr.h) +SET (CPPS ctf.cpp) + +SET (NETGAME_MODULE ctf) + +ADD_LIBRARY(${NETGAME_MODULE} SHARED ${CPPS} ${HEADERS}) +set_target_properties(${NETGAME_MODULE} PROPERTIES PREFIX "") +set_target_properties(${NETGAME_MODULE} PROPERTIES SUFFIX ".d3m") + +target_link_libraries(${NETGAME_MODULE} dmfc) +install(TARGETS ${NETGAME_MODULE} DESTINATION "${D3_GAMEDIR}netgames/") \ No newline at end of file diff --git a/netgames/ctf/CTFstr.h b/netgames/ctf/CTFstr.h new file mode 100644 index 000000000..59a9783fa --- /dev/null +++ b/netgames/ctf/CTFstr.h @@ -0,0 +1,53 @@ +#ifndef __CTF_STR_H_ +#define __CTF_STR_H_ + +#define TXT(d) GetString(d) + +#define TXT_DEATH1 TXT(0) //"%s got blasted by %s" +#define TXT_DEATH2 TXT(1) //"%s knows %s is his god" +#define TXT_DEATH3 TXT(2) //"%s sucks %s's milk" +#define TXT_DEATH4 TXT(3) //"%s realizes %s's power" +#define TXT_DEATH5 TXT(4) //"%s got killed by %s" +#define TXT_DEATH6 TXT(5) //"%s begs for %s's mercy" +#define TXT_DEATH7 TXT(6) //"%s realizes %s is a better player" +#define TXT_DEATH8 TXT(7) //"%s was no match for %s" +#define TXT_DEATH9 TXT(8) //"%s wishes he was as good as %s" +#define TXT_DEATH10 TXT(9) //"%s got messed up by %s" +#define TXT_SUICIDE1 TXT(10) //"%s blasts himself" +#define TXT_SUICIDE2 TXT(11) //"%s Bursts his own bubble" +#define TXT_SUICIDE3 TXT(12) //"%s doesn't know his own strength" +#define TXT_SUICIDE4 TXT(13) //"No prize for %s" +#define TXT_SUICIDE5 TXT(14) //"%s doesn't wish to live anymore" +#define TXT_SUICIDE6 TXT(15) //"%s SUCKS!" +#define TXT_TEAMSCORE TXT(16) //"%s Team Scores %d Points [%d]" +#define TXT_PICKUPFLAGSPEW TXT(17) //"%s (%s) finds the %s Flag among some debris!" +#define TXT_PICKUPFLAG TXT(18) //"%s (%s) picks up the %s flag" +#define TXT_STATS TXT(19) //"Stats" +#define TXT_TEAMSCOREFORM TXT(20) //"%s Team %d[%d]" +#define TXT_TEAMFORM TXT(21) //"%s Team" +#define TXT_WELCOME TXT(22) //"Welcome to Capture The Flag %s!" +#define TXT_JOINED TXT(23) //"%s has joined CTF" +#define TXT_PILOT TXT(24) //"Pilot" +#define TXT_KILLS TXT(25) //"Kills" +#define TXT_DEATHS TXT(26) //"Deaths" +#define TXT_SUICIDES TXT(27) //"Suicides" +#define TXT_POINTS TXT(28) //"Points" +#define TXT_CTF TXT(29) //"Capture The Flag" +#define TXT_PING TXT(30) //"Ping" +#define TXT_HATTRICK TXT(31) //"%s has achieved a Hat Trick!!!" +#define TXT_HATTRICKFIRST TXT(32) //"%s is the first to get a Hat Trick!!!" +#define TXT_CAPTURE TXT(33) //"%s (%s) captures the %s Flag!" +#define TXT_CAPTURETWO TXT(34) //"%s (%s) captures the %s and %s Flags!" +#define TXT_CAPTURETHREE TXT(35) //"%s (%s) captures the %s, %s and %s Flags!" +#define TXT_RETURN TXT(36) //"%s (%s) returns the %s Flag!" +#define TXT_STAT_HEADING TXT(37) //"Capture The Flag\r\nGame: %s\r\nLevel: %d\r\n" +#define TXT_STAT_HEADINGA TXT(38) //"Rank Team Name Score Kills Deaths Suicides Time In Game" +#define TXT_STAT_HEADINGB TXT(39) //"\r\nIndividual Stats\r\n" +#define TXT_STAT_TIG TXT(40) //"Total Time In Game: %s" +#define TXT_STAT_HEADINGC TXT(41) //"Callsign: Kills: Deaths:" +#define TXT_STAT_SAVED TXT(42) //"Stats saved to file" +#define TXT_KILLS_SHORT TXT(43) //"K" +#define TXT_DEATHS_SHORT TXT(44) //"D" +#define TXT_SUICIDES_SHORT TXT(45) //"S" + +#endif \ No newline at end of file diff --git a/netgames/ctf/ctf.cpp b/netgames/ctf/ctf.cpp new file mode 100644 index 000000000..06e1b0f58 --- /dev/null +++ b/netgames/ctf/ctf.cpp @@ -0,0 +1,2837 @@ +/* +* $Logfile: /DescentIII/Main/ctf/ctf.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:56:47 $ +* $Author: kevinb $ +* +* +* +* $Log: ctf.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:56:47 kevinb +* initial 1.5 import +* + * + * 140 10/03/01 1:05p Matt + * Made team_name buffer large enough to hold the team number *plus* the + * number of players on the team. + * + * 139 9/24/01 2:28p Matt + * Allowed room for longer team name on results screen. + * + * 138 9/20/01 12:58p Matt + * Added a team member list to the stats file. + * + * 137 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 136 9/03/99 5:30p Jeff + * use the flag's roomnumber instead of the player's roomnum when checking + * for a score (in case of a really thin room that the flag is in) + * + * 135 8/17/99 5:53p Jeff + * track ranks on PXO + * + * 134 7/15/99 1:17a Jeff + * fixed up $scores + * + * 133 7/13/99 12:10p Jeff + * added some specific text taunt token decoding + * + * 132 7/12/99 2:27p Jeff + * fixed PLR to only display the team label for the disconnected list if + * there are people in the list + * + * 131 7/11/99 6:54p Jeff + * fixed PLR so it doesn't go off the screen on long lists and active + * players in the game are shown first + * + * 130 6/10/99 5:21p Jeff + * fixed crash in french version, due to local array being to small + * + * 129 5/26/99 4:38a Jeff + * fixed ctf bad scoring bugs + * + * 128 5/23/99 3:04a Jason + * fixed bug with player rankings not being updated correctly + * + * 127 5/20/99 7:54p 3dsmax + * changed number of attach points + * + * 126 5/12/99 11:04p Jeff + * dmfc and multiplayer games now have endian friendly packets (*whew*) + * + * 125 5/12/99 11:28a Jeff + * added sourcesafe comment block +* +* $NoKeywords: $ +*/ + + + +#include "gamedll_header.h" +#include + +#include "idmfc.h" +#include "ctf.h" +#include "CTFstr.h" + +IDMFC *DMFCBase; +IDmfcStats *dstat; + +object *dObjects; +player *dPlayers; +room *dRooms; +netplayer *dNetPlayers; + +#define SPID_GAMESTATE 0x01 +#define SPID_ADDDELFLAG 0x02 +#define SPID_COLLIDE 0x03 + +//Inventory flags +#define FLAGMASK_REDTEAM 0x01 +#define FLAGMASK_BLUETEAM 0x02 +#define FLAGMASK_GREENTEAM 0x04 +#define FLAGMASK_YELLOWTEAM 0x08 + +//athome masks +#define REDAH 0x001 +#define BLUEAH 0x002 +#define GREENAH 0x004 +#define YELLOWAH 0x008 + +#define FLAG_TIMEOUT_VALUE 120 +//Sound name defines +#define SOUND_PICKUPFLAG_OWNTEAM "CTFPickupFlag1" +#define SOUND_PICKUPFLAG_OTHERTEAM "CTFPickupFlag1" +#define SOUND_SCORE_OWNTEAM "CTFScore1" +#define SOUND_SCORE_OTHERTEAM "CTFScore1" +#define SOUND_LOSEFLAG_OWNTEAM "CTFLostFlag1" +#define SOUND_LOSEFLAG_OTHERTEAM "CTFLostFlag1" +#define SOUND_FLAGRETURNED_OWNTEAM "CTFReturnedFlag1" +#define SOUND_FLAGRETURNED_OTHERTEAM "CTFReturnedFlag1" +#define SOUND_HATTRICK_FIRST "CTFHatTrick" +#define SOUND_HATTRICK_REGULAR "CTFHatTrick" +/* +$$TABLE_SOUND "CTFPickupFlag1" +$$TABLE_SOUND "CTFPickupFlag1" +$$TABLE_SOUND "CTFScore1" +$$TABLE_SOUND "CTFScore1" +$$TABLE_SOUND "CTFLostFlag1" +$$TABLE_SOUND "CTFLostFlag1" +$$TABLE_SOUND "CTFReturnedFlag1" +$$TABLE_SOUND "CTFReturnedFlag1" +$$TABLE_SOUND "CTFHatTrick" +$$TABLE_SOUND "CTFHatTrick" +*/ +int snd_return_ownteam = -1; +int snd_return_otherteam = -1; +int snd_pickedup_otherteam = -1; +int snd_pickedup_ownteam = -1; +int snd_score_ownteam = -1; +int snd_score_otherteam = -1; +int snd_lose_ownteam = -1; +int snd_lose_otherteam = -1; +int snd_hattrick_first = -1; +int snd_hattrick_reg = -1; + +typedef struct +{ + int Score[2]; +}tPlayerStat; //Overall scores (throughout the game) +int pack_pstat(tPlayerStat *user_info,ubyte *data) +{ + int count = 0; + MultiAddInt(user_info->Score[0],data,&count); + MultiAddInt(user_info->Score[1],data,&count); + return count; +} + +int unpack_pstat(tPlayerStat *user_info,ubyte *data) +{ + int count = 0; + user_info->Score[0] = MultiGetInt(data,&count); + user_info->Score[1] = MultiGetInt(data,&count); + return count; +} + +int SortedPlayers[MAX_PLAYER_RECORDS]; //sorted player nums +int SortedPLRPlayers[DLLMAX_TEAMS][MAX_PLAYER_RECORDS]; + +int TeamScores[DLLMAX_TEAMS]; //teams scores +int OverallTeamScores[DLLMAX_TEAMS]; //overall scores per team +int SortedTeams[DLLMAX_TEAMS]; //sorted team scores +int Highlight_bmp = -1; +bool DisplayScoreScreen; +bool Someone_has_hattrick = false; //this is false if no one has had a hattrick this level +bool First_game_frame = false; + +int FlagIDs[DLLMAX_TEAMS]; //Flag Object ID's +int AFlagIDs[DLLMAX_TEAMS]; //Attached Flag Object ID's +int GoalRooms[DLLMAX_TEAMS]; //Goal Rooms for Teams +int FlagBmp[DLLMAX_TEAMS]; //Flag Bitmap handles +int FlagAHBmp[DLLMAX_TEAMS]; //Flag At Home Bitmap handles +int DimFlagAHBmp[DLLMAX_TEAMS]; //Dimmed versions of the Flag At Home Bitmaps +bool FlagAtHome[DLLMAX_TEAMS]; //Flag At Home bools +int HasFlag[DLLMAX_TEAMS]; //List of Playernums of who has what flag, -1 if no one does +bool DisplayFlagBlink=true,DisplayScoreBlink=true; +int WhoJustFlagged=-1,WhoJustFlaggedTimer=-1; +int WhoJustScored=-1,WhoJustScoredTimer=-1; +int CTFNumOfTeams = 2; +int ChildFlags[DLLMAX_TEAMS]; //Object handles of attached flags as a player has em + +float Flag_timeout_timers[DLLMAX_TEAMS]; +bool display_my_welcome = false; + +void OnTimerScoreKill(void); //timer callback: when a score flash timer ends +void OnTimerScore(void); //timer callback: on a score flash interval +void OnTimer(void); //timer callback: when a flag taken flash timer ends +void OnTimerKill(void); //timer callback: on a flag taken flash interval +void DisplayWelcomeMessage(int player_num); //displays a welcome message to the player when he joins +void SortTeamScores(int *sortedindex,int *scores); //sorts an array of team scores, filling in the sorted index numbers +void DisplayHUDScores(struct tHUDItem *hitem); //callback when the HUD info is to be drawn +void ReceiveGameState(ubyte *data); //callback when a gamestate packet is received from the server +void SendGameState(int playernum); //called when the server is to send gamestate packet to a client +void SetColoredBalls(int playernum,bool reset=false);//sets the colored balls around a player (determined by what is in their inventory) +void ChangeNumberOfTeams(int newsize); //called when the number of teams in the game is changed or to be changed +void DoFlagReturnedHome(int team); //called to handle any events when a flag is returned home for a team +void DoLoseFlag(int team); //called to handle any events when a team loses their flag +void TellClientsToAddorDelFlag(int pnum,int team,int objnum,bool add); +void ServerIsTellingMeToAddorDelAFlag(ubyte *data); +void OnGetTokenString(char *src,char *dest,int dest_size); +// returns the number of flags a player has, 0 if none, or an invalid pnum +int GetFlagCountForPlayer(int pnum); +// returns the mask of which flags this player currently has +ubyte GetFlagMaskForPlayer(int pnum); +// adds a flag to a player, as a precaution, it will go through all the players and makes sure that no one +// has the flag that is being added. If they are adding the flag, than remove that flag from whoever we thought had it +// it will return false if it had to remove a flag from a player +bool GivePlayerFlag(int pnum,ubyte team); +//this function takes a flag away from the player, useful for when he scores, spews, disconnects, or observer modes +void LoseFlagForPlayer(int pnum,ubyte team,bool remove_from_inven=true); + +/////////////////////////////////////////////// +//localization info/functions +char **StringTable; +int StringTableSize = 0; +char *_ErrorString = "Missing String"; +char *GetString(int d){if( (d<0) || (d>=StringTableSize) ) return _ErrorString; else return StringTable[d];} +void SaveStatsToFile(char *filename); + +#ifdef MACINTOSH +#pragma export on +#endif + +// This function gets called by the game when it wants to learn some info about the game +void DLLFUNCCALL DLLGetGameInfo (tDLLOptions *options) +{ + options->flags = DOF_MAXTEAMS|DOF_MINTEAMS; + options->max_teams = 4; + options->min_teams = 2; + strcpy(options->game_name,TXT_CTF); + strcpy(options->requirements,"MINGOALS2,GOALPERTEAM"); +} + +void DetermineScore(int precord_num,int column_num,char *buffer,int buffer_size) +{ + player_record *pr = DMFCBase->GetPlayerRecord(precord_num); + if(!pr || pr->state==STATE_EMPTY){ + buffer[0] = '\0'; + return; + } + + tPlayerStat *stat = (tPlayerStat *)pr->user_info; + sprintf(buffer,"%d",(stat)?stat->Score[DSTAT_LEVEL]:0); +} + +void TeamScoreCallback(int team,char *buffer,int buffer_size) +{ + ASSERT(team>=0 && teamGetPlayerRecord(precord_num); + int flagcount,flagmask; + + if(pr && pr->state==STATE_INGAME ){ + + flagcount = GetFlagCountForPlayer(pr->pnum); + + if(!flagcount) + return; + + flagmask = GetFlagMaskForPlayer(pr->pnum); + + //Display the flags that this player has + x = x + w - (11*flagcount); + for(int q=0;q>1; + } + } +} + + +/////////////////////////////////////////////// +// Initializes the game function pointers +void DLLFUNCCALL DLLGameInit(int *api_func,ubyte *all_ok,int num_teams_to_use) +{ + *all_ok = 1; + DMFCBase = CreateDMFC(); + if(!DMFCBase) + { + *all_ok = 0; + return; + } + + dstat = CreateDmfcStats(); + if(!dstat) + { + *all_ok = 0; + return; + } + + DMFCBase->LoadFunctions(api_func); + + // Setup event handlers + DMFCBase->Set_OnInterval(OnInterval); + DMFCBase->Set_OnHUDInterval(OnHUDInterval); + DMFCBase->Set_OnKeypress(OnKeypress); + DMFCBase->Set_OnServerGameCreated(OnServerGameCreated); + DMFCBase->Set_OnClientLevelStart(OnClientLevelStart); + DMFCBase->Set_OnClientLevelEnd(OnClientLevelEnd); + DMFCBase->Set_OnGameStateRequest(OnGameStateRequest); + DMFCBase->Set_OnPlayerConnect(OnPlayerConnect); + DMFCBase->Set_OnClientPlayerKilled(OnClientPlayerKilled); + DMFCBase->Set_OnServerCollide(OnServerCollide); + //DMFCBase->Set_OnClientCollide(OnClientCollide); + DMFCBase->Set_OnPLRInterval(OnPLRInterval); + DMFCBase->Set_OnPLRInit(OnPLRInit); + DMFCBase->Set_OnCanChangeTeam(OnCanChangeTeam); + DMFCBase->Set_OnSaveStatsToFile(OnSaveStatsToFile); + DMFCBase->Set_OnLevelEndSaveStatsToFile(OnLevelEndSaveStatsToFile); + DMFCBase->Set_OnDisconnectSaveStatsToFile(OnDisconnectSaveStatsToFile); + DMFCBase->Set_OnPrintScores(OnPrintScores); + DMFCBase->Set_OnPlayerEntersObserver(OnPlayerEntersObserver); + DMFCBase->Set_OnClientPlayerDisconnect(OnClientPlayerDisconnect); + DMFCBase->Set_OnPlayerChangeTeam(OnPlayerChangeTeam); + DMFCBase->Set_OnGetTokenString(OnGetTokenString); + + // Setup arrays for easier to read code + dObjects = DMFCBase->GetObjects(); + dPlayers = DMFCBase->GetPlayers(); + dRooms = DMFCBase->GetRooms(); + dNetPlayers = DMFCBase->GetNetPlayers(); + + netgame_info *Netgame = DMFCBase->GetNetgameInfo(); + Netgame->flags |= (NF_TRACK_RANK); + + CTFNumOfTeams = num_teams_to_use; + + DMFCBase->GameInit(CTFNumOfTeams); + DLLCreateStringTable("CTF.str",&StringTable,&StringTableSize); + DLLmprintf((0,"%d strings loaded from string table\n",StringTableSize)); + if(!StringTableSize){ + *all_ok = 0; + return; + } + + ChangeNumberOfTeams(CTFNumOfTeams); + + //add the death and suicide messages + DMFCBase->AddDeathMessage(TXT_DEATH1,true); + DMFCBase->AddDeathMessage(TXT_DEATH2,true); + DMFCBase->AddDeathMessage(TXT_DEATH3,false); + DMFCBase->AddDeathMessage(TXT_DEATH4,false); + DMFCBase->AddDeathMessage(TXT_DEATH5,true); + DMFCBase->AddDeathMessage(TXT_DEATH6,true); + DMFCBase->AddDeathMessage(TXT_DEATH7,false); + DMFCBase->AddDeathMessage(TXT_DEATH8,true); + DMFCBase->AddDeathMessage(TXT_DEATH9,true); + DMFCBase->AddDeathMessage(TXT_DEATH10,true); + + DMFCBase->AddSuicideMessage(TXT_SUICIDE1); + DMFCBase->AddSuicideMessage(TXT_SUICIDE2); + DMFCBase->AddSuicideMessage(TXT_SUICIDE3); + DMFCBase->AddSuicideMessage(TXT_SUICIDE4); + DMFCBase->AddSuicideMessage(TXT_SUICIDE5); + DMFCBase->AddSuicideMessage(TXT_SUICIDE6); + + //setup the Playerstats struct so DMFC can handle it automatically when a new player enters the game + DMFCBase->SetupPlayerRecord(sizeof(tPlayerStat),(int (*)(void *,ubyte *))pack_pstat,(int (*)(void *,ubyte *))unpack_pstat); + + DMFCBase->AddHUDItemCallback(HI_TEXT,DisplayHUDScores); + + DMFCBase->RegisterPacketReceiver(SPID_GAMESTATE,ReceiveGameState); + DMFCBase->RegisterPacketReceiver(SPID_ADDDELFLAG,ServerIsTellingMeToAddorDelAFlag); + DMFCBase->RegisterPacketReceiver(SPID_COLLIDE,OnClientCollide); + + //Load all the bitmaps + FlagBmp[RED_TEAM] = DLLbm_AllocLoadFileBitmap("RedFlagIcon.ogf",0,BITMAP_FORMAT_1555); + FlagBmp[BLUE_TEAM] = DLLbm_AllocLoadFileBitmap("BlueFlagIcon.ogf",0,BITMAP_FORMAT_1555); + FlagBmp[GREEN_TEAM] = DLLbm_AllocLoadFileBitmap("GreenFlagIcon.ogf",0,BITMAP_FORMAT_1555); + FlagBmp[YELLOW_TEAM] = DLLbm_AllocLoadFileBitmap("YellowFlagIcon.ogf",0,BITMAP_FORMAT_1555); + //athome bitmaps + FlagAHBmp[RED_TEAM] = DLLbm_AllocLoadFileBitmap("RedFlagIcon.ogf",0,BITMAP_FORMAT_1555); + FlagAHBmp[BLUE_TEAM] = DLLbm_AllocLoadFileBitmap("BlueFlagIcon.ogf",0,BITMAP_FORMAT_1555); + FlagAHBmp[GREEN_TEAM] = DLLbm_AllocLoadFileBitmap("GreenFlagIcon.ogf",0,BITMAP_FORMAT_1555); + FlagAHBmp[YELLOW_TEAM] = DLLbm_AllocLoadFileBitmap("YellowFlagIcon.ogf",0,BITMAP_FORMAT_1555); + DimFlagAHBmp[RED_TEAM] = DLLbm_AllocLoadFileBitmap("RedFlagIconDim.ogf",0,BITMAP_FORMAT_1555); + DimFlagAHBmp[BLUE_TEAM] = DLLbm_AllocLoadFileBitmap("BlueFlagIconDim.ogf",0,BITMAP_FORMAT_1555); + DimFlagAHBmp[GREEN_TEAM] = DLLbm_AllocLoadFileBitmap("GreenFlagIconDim.ogf",0,BITMAP_FORMAT_1555); + DimFlagAHBmp[YELLOW_TEAM] = DLLbm_AllocLoadFileBitmap("YellowFlagIconDim.ogf",0,BITMAP_FORMAT_1555); + //fill in the IDs + + + FlagIDs[RED_TEAM] = DLLFindObjectIDName("FlagRed"); + FlagIDs[BLUE_TEAM] = DLLFindObjectIDName("Flagblue"); + FlagIDs[GREEN_TEAM] = DLLFindObjectIDName("FlagGreen"); + FlagIDs[YELLOW_TEAM] = DLLFindObjectIDName("FlagYellow"); + AFlagIDs[RED_TEAM] = DLLFindObjectIDName("ShipRedFlag"); + AFlagIDs[BLUE_TEAM] = DLLFindObjectIDName("ShipBlueFlag"); + AFlagIDs[GREEN_TEAM] = DLLFindObjectIDName("ShipGreenFlag"); + AFlagIDs[YELLOW_TEAM] = DLLFindObjectIDName("ShipYellowFlag"); + + //give initial values for the at home so they're set + FlagAtHome[RED_TEAM] = FlagAtHome[BLUE_TEAM] = + FlagAtHome[GREEN_TEAM] = FlagAtHome[YELLOW_TEAM] = false; + + //make sure all was init ok + for(int i=0;iBAD_BITMAP_HANDLE){ + ushort *data = DLLbm_data(Highlight_bmp,0); + if(!data){ + //bail on out of here + *all_ok = 0; + return; + } + for(int x=0;x<32*32;x++){ + data[x] = GR_RGB16(50,50,50)|OPAQUE_FLAG; + } + } + + //Load in and touch all the sounds so they're ready to rock + snd_score_ownteam = DLLFindSoundName(IGNORE_TABLE(SOUND_SCORE_OWNTEAM)); + if(snd_score_ownteam!=-1) + DLLTouchSound(snd_score_ownteam); + snd_score_otherteam = DLLFindSoundName(IGNORE_TABLE(SOUND_SCORE_OTHERTEAM)); + if(snd_score_otherteam!=-1) + DLLTouchSound(snd_score_otherteam); + snd_pickedup_otherteam =DLLFindSoundName(IGNORE_TABLE(SOUND_PICKUPFLAG_OWNTEAM)); + if(snd_pickedup_otherteam!=-1) + DLLTouchSound(snd_pickedup_otherteam); + snd_pickedup_ownteam =DLLFindSoundName(IGNORE_TABLE(SOUND_PICKUPFLAG_OTHERTEAM)); + if(snd_pickedup_ownteam!=-1) + DLLTouchSound(snd_pickedup_ownteam); + snd_return_ownteam = DLLFindSoundName(IGNORE_TABLE(SOUND_FLAGRETURNED_OWNTEAM)); + if(snd_return_ownteam!=-1) + DLLTouchSound(snd_return_ownteam); + snd_return_otherteam = DLLFindSoundName(IGNORE_TABLE(SOUND_FLAGRETURNED_OTHERTEAM)); + if(snd_return_otherteam!=-1) + DLLTouchSound(snd_return_otherteam); + snd_lose_ownteam = DLLFindSoundName(IGNORE_TABLE(SOUND_LOSEFLAG_OWNTEAM)); + if(snd_lose_ownteam!=-1) + DLLTouchSound(snd_lose_ownteam); + snd_lose_otherteam = DLLFindSoundName(IGNORE_TABLE(SOUND_LOSEFLAG_OTHERTEAM)); + if(snd_lose_otherteam!=-1) + DLLTouchSound(snd_lose_otherteam); + snd_hattrick_first = DLLFindSoundName(IGNORE_TABLE(SOUND_HATTRICK_FIRST)); + if(snd_hattrick_first!=-1) + DLLTouchSound(snd_hattrick_first); + snd_hattrick_reg = DLLFindSoundName(IGNORE_TABLE(SOUND_HATTRICK_REGULAR)); + if(snd_hattrick_reg!=-1) + DLLTouchSound(snd_hattrick_reg); + + DisplayScoreScreen = false; + + + // Initialize the Stats Manager + // ---------------------------- + + tDmfcStatsInit tsi; + tDmfcStatsColumnInfo pl_col[7]; + char gname[32]; + strcpy(gname,TXT_CTF); + + tsi.flags = DSIF_SHOW_PIC|DSIF_SHOW_OBSERVERICON|DSIF_SEPERATE_BY_TEAM; + tsi.cColumnCountDetailed = 0; + tsi.cColumnCountPlayerList = 7; + tsi.clbDetailedColumnBMP = NULL; + tsi.clbDetailedColumn = NULL; + tsi.clbPlayerColumn = DetermineScore; + tsi.clbPlayerColumnBMP = ShowStatBitmap; + tsi.DetailedColumns = NULL; + tsi.GameName = gname; + tsi.MaxNumberDisplayed = NULL; + tsi.PlayerListColumns = pl_col; + tsi.SortedPlayerRecords = SortedPlayers; + tsi.clTeamLine = TeamScoreCallback; + + pl_col[0].color_type = DSCOLOR_TEAM; + pl_col[0].title[0] = '\0'; + pl_col[0].type = DSCOL_BMP; + pl_col[0].width = 33; + + pl_col[1].color_type = DSCOLOR_TEAM; + strcpy(pl_col[1].title,TXT_PILOT); + pl_col[1].type = DSCOL_PILOT_NAME; + pl_col[1].width = 120; + + pl_col[2].color_type = DSCOLOR_TEAM; + strcpy(pl_col[2].title,TXT_POINTS); + pl_col[2].type = DSCOL_CUSTOM; + pl_col[2].width = 52; + + pl_col[3].color_type = DSCOLOR_TEAM; + strcpy(pl_col[3].title,TXT_KILLS_SHORT); + pl_col[3].type = DSCOL_KILLS_LEVEL; + pl_col[3].width = 47; + + pl_col[4].color_type = DSCOLOR_TEAM; + strcpy(pl_col[4].title,TXT_DEATHS_SHORT); + pl_col[4].type = DSCOL_DEATHS_LEVEL; + pl_col[4].width = 47; + + pl_col[5].color_type = DSCOLOR_TEAM; + strcpy(pl_col[5].title,TXT_SUICIDES_SHORT); + pl_col[5].type = DSCOL_SUICIDES_LEVEL; + pl_col[5].width = 47; + + pl_col[6].color_type = DSCOLOR_TEAM; + strcpy(pl_col[6].title,TXT_PING); + pl_col[6].type = DSCOL_PING; + pl_col[6].width = 40; + + dstat->Initialize(&tsi); +} +// Called when the DLL is shutdown +void DLLFUNCCALL DLLGameClose () +{ + //@@SaveConfig(); + //@@DMFCBase->CFGClose(); + + //Free all the bitmaps + DLLbm_FreeBitmap(FlagBmp[RED_TEAM]); + DLLbm_FreeBitmap(FlagBmp[BLUE_TEAM]); + DLLbm_FreeBitmap(FlagBmp[GREEN_TEAM]); + DLLbm_FreeBitmap(FlagBmp[YELLOW_TEAM]); + DLLbm_FreeBitmap(DimFlagAHBmp[RED_TEAM]); + DLLbm_FreeBitmap(DimFlagAHBmp[BLUE_TEAM]); + DLLbm_FreeBitmap(DimFlagAHBmp[GREEN_TEAM]); + DLLbm_FreeBitmap(DimFlagAHBmp[YELLOW_TEAM]); + DLLbm_FreeBitmap(FlagAHBmp[RED_TEAM]); + DLLbm_FreeBitmap(FlagAHBmp[BLUE_TEAM]); + DLLbm_FreeBitmap(FlagAHBmp[GREEN_TEAM]); + DLLbm_FreeBitmap(FlagAHBmp[YELLOW_TEAM]); + + if(Highlight_bmp>BAD_BITMAP_HANDLE) + DLLbm_FreeBitmap(Highlight_bmp); + + DLLDestroyStringTable(StringTable,StringTableSize); + + if(dstat) + { + dstat->DestroyPointer(); + dstat = NULL; + } + + if(DMFCBase) + { + DMFCBase->GameClose(); + DMFCBase->DestroyPointer(); + DMFCBase = NULL; + } +} + +///////////////////////////////////////////////////////////// +//DMFC Overrides + +//OnHUDInterval +void OnHUDInterval(void) +{ + dstat->DoFrame(); + + DMFCBase->DisplayOutrageLogo(); + + DMFCBase->OnHUDInterval(); +} + +//OnInterval +void OnInterval(void) +{ + DMFCBase->GetSortedPlayerSlots(SortedPlayers,MAX_PLAYER_RECORDS); + + SortTeamScores(SortedTeams,TeamScores); + + //Determine if a flag has timed out yet + for(int i=0;iGetGametime()>=Flag_timeout_timers[i]){ + //the timeout has expired, move home! + vector home_pos; + int home,objnum; + + Flag_timeout_timers[i] = 0; + FlagAtHome[i] = true; + HasFlag[i] = -1; + home = GoalRooms[i]; + objnum = -1; + + //find the flag objnum + for(int o=0;oGetLocalRole()==LR_SERVER){ + int flagid = FlagIDs[i]; + if( (flagid!=-1) && (home>=0) && (home<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(home)) && (dRooms[home].used) ){ + //Safe to create the flag + DLLComputeRoomCenter(&home_pos,&dRooms[home]); + objnum = DLLObjCreate(OBJ_POWERUP,flagid,home,&home_pos,NULL,OBJECT_HANDLE_NONE); + DLLMultiSendObject(&dObjects[objnum],0,true); + } + } + }else{ + if( (home>=0) && (home<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(home)) && (dRooms[home].used) ){ + DLLComputeRoomCenter(&home_pos,&dRooms[home]); + DLLObjSetPos(&dObjects[objnum],&home_pos,home,NULL,false); + } + } + } + } + } + + DMFCBase->OnInterval(); + + First_game_frame = true; +} + +//OnKeypress +// What it needs to do: +// 1) Test for F7 keypress +void OnKeypress(int key) +{ + dllinfo *Data; + Data = DMFCBase->GetDLLInfoCallData(); + + switch(key){ + case K_F7: + DisplayScoreScreen = !DisplayScoreScreen; + DMFCBase->EnableOnScreenMenu(false); + dstat->Enable(DisplayScoreScreen); + break; + case K_PAGEDOWN: + if(DisplayScoreScreen){ + dstat->ScrollDown(); + Data->iRet = 1; + } + break; + case K_PAGEUP: + if(DisplayScoreScreen){ + dstat->ScrollUp(); + Data->iRet = 1; + } + break; + case K_F6: + DisplayScoreScreen = false; + dstat->Enable(false); + break; + case K_ESC: + if(DisplayScoreScreen){ + dstat->Enable(false); + DisplayScoreScreen = false; + Data->iRet = 1; + } + break; + } + + DMFCBase->OnKeypress(key); +} + +//OnServerGameCreated +// What it needs to do: +// 1) Zero out all the stats for every player +// 2) Zero out team scores +// 3) Display welcome message to server +void OnServerGameCreated(void) +{ + DMFCBase->OnServerGameCreated(); + tPlayerStat *stat; + player_record *pr; + int i; + + for(i=0;iGetPlayerRecord(i); + if(pr){ + stat = (tPlayerStat *)pr->user_info; + if(stat){ + stat->Score[DSTAT_LEVEL] = 0; + stat->Score[DSTAT_OVERALL] = 0; + } + } + } + for(i=0;iOnClientLevelStart(); + + First_game_frame = false; + + for(i=0;iGetPlayerRecord(i); + if(pr){ + stat = (tPlayerStat *)pr->user_info; + if(stat){ + stat->Score[DSTAT_LEVEL] = 0; + } + } + } + for(i=0;iSetNumberOfTeams(CTFNumOfTeams); + DLLMultiPaintGoalRooms(NULL); + + //Bash the inventory since it doesn't get reset for the server + DLLInvReset(DMFCBase->GetPlayerNum(),true); + + //Display our welcome message + display_my_welcome = true; + + //If we are the server create the flags + if(DMFCBase->GetLocalRole()!=LR_SERVER){ + //Get the game state + DMFCBase->RequestGameState(); + return; + } + + vector vpos; + int objnum; + int flagid,goalroom; + + for(i=0;i=0) && (goalroom<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(goalroom)) && (dRooms[goalroom].used) ){ + //Safe to create the flag + DLLmprintf((0,"Creating %s Flag\n",DMFCBase->GetTeamString(i))); + DLLComputeRoomCenter(&vpos,&dRooms[goalroom]); + objnum = DLLObjCreate(OBJ_POWERUP,flagid,goalroom,&vpos,NULL,OBJECT_HANDLE_NONE); + DLLMultiSendObject(&dObjects[objnum],0,true); + } + } +} + +//OnServerCollide +// What it needs to do: +// 1) See if the player has collided with a flag, if so pass to clients +void OnServerCollide(object *me_obj,object *it_obj) +{ + //see if we need to send this event to the clients + //only if me==player it==flag powerup + if(!me_obj || !it_obj) + return; + if( (me_obj->type==OBJ_PLAYER||me_obj->type==OBJ_GHOST) && (it_obj->type==OBJ_POWERUP) ) + if( (it_obj->id==FlagIDs[RED_TEAM]) || + (it_obj->id==FlagIDs[BLUE_TEAM]) || + (it_obj->id==FlagIDs[GREEN_TEAM]) || + (it_obj->id==FlagIDs[YELLOW_TEAM]) ){ + + //Start a packet for the collide + ubyte data[MAX_GAME_DATA_SIZE]; + int count = 0; + DMFCBase->StartPacket(data,SPID_COLLIDE,&count); + int start = count; + + //we need me, it and me roomnum + MultiAddUshort((me_obj-dObjects),data,&count); + MultiAddUshort((it_obj-dObjects),data,&count); + MultiAddInt(it_obj->roomnum,data,&count); + + DMFCBase->SendPacket(data,count,SP_ALL); + OnClientCollide(&data[start]); + } +} + +//OnClientCollide +// What it needs to do: +// 1) See if Player collided with own flag +// 1.1) Check to see if in home goal +// 1.1.1) Reset colored balls +// 1.1.2) Remove all the flags from the inventory, send them back to their home goal +// 1.1.3) Add scores +// 1.1.4) Print out score +// 1.1.5) Do Kill Goal check +// 1,2) Move home flag to center of base +// 2) else collide with another team's flag +// 2.1) Add flag to inventory +// 2.2) Set rotating balls +// 2.3) Print out message +void OnClientCollide(ubyte *data) +{ + object *me_obj,*it_obj; + int me_roomnum; + int count = 0; + int me_serv_objnum,it_serv_objnum,me_client_objnum,it_client_objnum; + + me_serv_objnum = MultiGetUshort(data,&count); + it_serv_objnum = MultiGetUshort(data,&count); + me_roomnum = MultiGetInt(data,&count); + + me_client_objnum = DMFCBase->ConvertServerToLocalObjnum(me_serv_objnum); + it_client_objnum = DMFCBase->ConvertServerToLocalObjnum(it_serv_objnum); + + if(me_client_objnum==-1 || it_client_objnum==-1) + { + Int3(); + return; + } + + me_obj = &dObjects[me_client_objnum]; + it_obj = &dObjects[it_client_objnum]; + + // now process the collide + + int fteam=-1; //flags team + int pnum = me_obj->id; //player number + int pteam=DMFCBase->GetPlayerTeam(pnum); //player's team + int i; + bool play_return_home_msg = false; + + for(i=0;iid==FlagIDs[i]) + fteam = i; + } + + if(fteam==-1){ + return; //something wrong, as none of the IDs are one of the flag's IDs + } + + // Reset the timer for this flag since it's guaranteed someone owns it now + // or it has been sent home. + Flag_timeout_timers[fteam] = 0; + + //Did player collide with his own team's flag? + if(fteam==pteam) + { + short flag_count = 0; + vector fpos; + int groom; + int flagcount; + int flagmask; + + bool captured_flags[DLLMAX_TEAMS]; + for(i=0;i=0) && (groom<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(groom)) && (dRooms[groom].used)){ + //the room is valid, so move it back to home + + LoseFlagForPlayer(pnum,i); + + if(DMFCBase->GetLocalRole()==LR_SERVER){ + //if we are the server create the object and send it on it's way to the player's + DLLComputeRoomCenter(&fpos,&dRooms[groom]); + int objnum = DLLObjCreate(OBJ_POWERUP,FlagIDs[i],groom,&fpos,NULL,OBJECT_HANDLE_NONE); + DLLMultiSendObject(&dObjects[objnum],1,true); + }//end server create + }//end room ok + + //set it's at home flag + FlagAtHome[i] = true; + }//end flag check + //rotate the flagmask over one to check for the next flag + flagmask = flagmask >> 1; + }//end for loop + + //remove any rotating balls + SetColoredBalls(pnum,true); + + //Now handle any scoring + int score = 0; + switch(flagcount) + { + case 0: + break; + case 1: + score = 1; + break; + case 2: + score = 3; + break; + case 3: + score = 9; + break; + } + + //print out the message and add to the scores + + if(score) + { + tPlayerStat *stat = (tPlayerStat *)DMFCBase->GetPlayerRecordData(pnum); + if(stat){ + stat->Score[DSTAT_LEVEL] +=score; + stat->Score[DSTAT_OVERALL] +=score; + + if(stat->Score[DSTAT_LEVEL]==3){ + if(Someone_has_hattrick){ + DLLAddHUDMessage(TXT_HATTRICK,dPlayers[pnum].callsign); + if(snd_hattrick_reg!=-1) + DLLPlay2dSound(snd_hattrick_reg,MAX_GAME_VOLUME/2); + }else{ + DLLAddHUDMessage(TXT_HATTRICKFIRST,dPlayers[pnum].callsign); + if(snd_hattrick_first!=-1) + DLLPlay2dSound(snd_hattrick_first,MAX_GAME_VOLUME/2); + Someone_has_hattrick = true; + } + } + } + + TeamScores[pteam]+=score; + OverallTeamScores[pteam]+=score; + int newscore=TeamScores[pteam]; + + switch(flagcount){ + case 1: + { + char t[20]; + t[0] = '\0'; + for(int c=0;cGetTeamString(c)); + break; + } + } + DLLAddHUDMessage(TXT_CAPTURE,dPlayers[pnum].callsign,DMFCBase->GetTeamString(pteam),t); + }break; + case 2: + { + char t[2][20]; + t[0][0] = '\0'; + t[1][0] = '\0'; + int index = 0; + for(int c=0;cGetTeamString(c)); + index++; + if(index==2) + break; + } + } + DLLAddHUDMessage(TXT_CAPTURETWO,dPlayers[pnum].callsign, + DMFCBase->GetTeamString(pteam),t[0],t[1]); + }break; + case 3: + { + char t[3][20]; + t[0][0] = '\0'; + t[1][0] = '\0'; + t[2][0] = '\0'; + + int index = 0; + for(int c=0;cGetTeamString(c)); + index++; + if(index==3) + break; + } + } + DLLAddHUDMessage(TXT_CAPTURETHREE,dPlayers[pnum].callsign, + DMFCBase->GetTeamString(pteam),t[0],t[1],t[2]); + } + } + + if(dPlayers[DMFCBase->GetPlayerNum()].team==pteam){ + if(snd_score_ownteam!=-1) + DLLPlay2dSound(snd_score_ownteam,MAX_GAME_VOLUME/2); + }else{ + if(snd_score_otherteam!=-1) + DLLPlay2dSound(snd_score_otherteam,MAX_GAME_VOLUME/2); + } + + //Set a Timer to display + if(WhoJustScoredTimer!=-1) + DMFCBase->KillTimer(WhoJustScoredTimer); + WhoJustScoredTimer = DMFCBase->SetTimerInterval(OnTimerScore,0.5f,5.0f,OnTimerScoreKill); + WhoJustScored = pteam; + } + //do the killgoal check + int killgoal; + if(DMFCBase->GetScoreLimit(&killgoal)){ + if(TeamScores[pteam]>=killgoal){ + //That's all she wrote for this level + DLLmprintf((0,"OnClientCollide:Kill Goal Reached!\n")); + DMFCBase->EndLevel(); + } + } + }else{ + DoFlagReturnedHome(pteam); + if(DMFCBase->CheckPlayerNum(pnum)){ + char parm1[20],parm2[20]; + strcpy(parm1,DMFCBase->GetTeamString(pteam)); + strcpy(parm2,DMFCBase->GetTeamString(fteam)); + DLLAddHUDMessage(TXT_RETURN,dPlayers[pnum].callsign,parm1,parm2); + } + } + + //since the player collided with his own team's flag we need to move the flag back home + FlagAtHome[pteam] = true; + HasFlag[pteam] = -1; + vector home_pos; + int home = GoalRooms[pteam]; + + if( (home>=0) && (home<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(home)) && (dRooms[home].used) ) + { + DLLComputeRoomCenter(&home_pos,&dRooms[home]); + DLLObjSetPos(it_obj,&home_pos,home,NULL,false); + } + + }//end Player Collides with own team's flag + else{ + //The Player collide with another team's flag + FlagAtHome[fteam] = false; + + //add the flag to the player + GivePlayerFlag(pnum,fteam); + + if(dPlayers[DMFCBase->GetPlayerNum()].team==dPlayers[pnum].team){ + if(snd_pickedup_ownteam!=-1) + DLLPlay2dSound(snd_pickedup_ownteam,MAX_GAME_VOLUME/2); + }else{ + if(snd_pickedup_otherteam!=-1) + DLLPlay2dSound(snd_pickedup_otherteam,MAX_GAME_VOLUME/2); + } + + //Set a Timer to display + if(WhoJustFlaggedTimer!=-1) + DMFCBase->KillTimer(WhoJustFlaggedTimer); + WhoJustFlaggedTimer = DMFCBase->SetTimerInterval(OnTimer,0.5f,5.0f,OnTimerKill); + WhoJustFlagged = fteam; + + char parm1[20],parm2[20]; + strcpy(parm1,DMFCBase->GetTeamString(pteam)); + strcpy(parm2,DMFCBase->GetTeamString(fteam)); + if(dObjects[dPlayers[pnum].objnum].roomnum==GoalRooms[fteam]){ + DLLAddHUDMessage(TXT_PICKUPFLAG,dPlayers[pnum].callsign,parm1,parm2); + }else{ + DLLAddHUDMessage(TXT_PICKUPFLAGSPEW,dPlayers[pnum].callsign,parm1,parm2); + } + + //remove the flag from the world + if(DMFCBase->GetLocalRole()==LR_SERVER){ + //tell the clients to remove this flag + DLLSetObjectDeadFlag(it_obj,true,false); + } + } +} + +//OnGameStateRequest +// 1) Send game state to new player +void OnGameStateRequest(int player_num) +{ + SendGameState(player_num); + DMFCBase->OnGameStateRequest(player_num); +} + +void OnPlayerConnect(int player_num) +{ + DMFCBase->OnPlayerConnect(player_num); + + tPlayerStat *stat; + stat = (tPlayerStat *)DMFCBase->GetPlayerRecordData(player_num); + if(stat){ + stat->Score[DSTAT_LEVEL] = 0; + stat->Score[DSTAT_OVERALL] = 0; + } + + if(player_num!=DMFCBase->GetPlayerNum()) + { + //announce the arrival of the player + DisplayWelcomeMessage(player_num); + } +} + +void OnClientLevelEnd(void) +{ + DMFCBase->OnClientLevelEnd(); +} + +void OnPlayerChangeTeam(int player_num,int newteam,bool announce,bool spew_onrespawn) +{ + DMFCBase->OnPlayerChangeTeam(player_num,newteam,announce,spew_onrespawn); + player_record *pr = DMFCBase->GetPlayerRecordByPnum(player_num); +} + +//OnClientPlayerKilled +// 1) If suicide add to suicides +// 2) Else add to killer's kill, add to victim's deaths +// 3) Reset colored balls +// 4) If killed in a flag's home goal than put it in the center of the room +void OnClientPlayerKilled(object *killer_obj,int victim_pnum) +{ + int kpnum = -1; + + if(killer_obj){ + if((killer_obj->type==OBJ_PLAYER)||(killer_obj->type==OBJ_GHOST)||(killer_obj->type==OBJ_OBSERVER)) + kpnum = killer_obj->id; + else if(killer_obj->type==OBJ_ROBOT || killer_obj->type == OBJ_BUILDING){ + //countermeasure kill + kpnum = DMFCBase->GetCounterMeasureOwner(killer_obj); + }else{ + kpnum = -1; + } + } + + HandlePlayerSpew(victim_pnum); + DMFCBase->OnClientPlayerKilled(killer_obj,victim_pnum); +} + +void OnClientPlayerDisconnect(int player_num) +{ + HandlePlayerSpew(player_num); + DMFCBase->OnClientPlayerDisconnect(player_num); +} + +void OnPlayerEntersObserver(int pnum,object *piggy) +{ + HandlePlayerSpew(pnum); + DMFCBase->OnPlayerEntersObserver(pnum,piggy); +} + +bool OnCanChangeTeam(int pnum,int newteam) +{ + if(!DMFCBase->OnCanChangeTeam(pnum,newteam)) + return false; + + if(GetFlagCountForPlayer(pnum)){ + DMFCBase->AnnounceTeamChangeDeny(pnum); + return false; + } + + return true; +} + +bool compare_slots(int a,int b) +{ + int ascore,bscore; + player_record *apr,*bpr; + tPlayerStat *astat,*bstat; + + apr = DMFCBase->GetPlayerRecord(a); + bpr = DMFCBase->GetPlayerRecord(b); + if( !apr ) + return true; + if( !bpr ) + return false; + if( apr->state==STATE_EMPTY ) + return true; + if( bpr->state==STATE_EMPTY ) + return false; + astat = (tPlayerStat *)apr->user_info; + bstat = (tPlayerStat *)bpr->user_info; + + if( (apr->state==STATE_INGAME) && (bpr->state==STATE_INGAME) ){ + //both players were in the game + ascore = (astat)?astat->Score[DSTAT_LEVEL]:0; + bscore = (bstat)?bstat->Score[DSTAT_LEVEL]:0; + return (ascorestate==STATE_INGAME) && (bpr->state==STATE_DISCONNECTED) ){ + //apr gets priority since he was in the game on exit + return false; + } + if( (apr->state==STATE_DISCONNECTED) && (bpr->state==STATE_INGAME) ){ + //bpr gets priority since he was in the game on exit + return true; + } + //if we got here then both players were disconnected + ascore = (astat)?astat->Score[DSTAT_LEVEL]:0; + bscore = (bstat)?bstat->Score[DSTAT_LEVEL]:0; + return (ascore=0 && compare_slots(tempsort[j],t); j--){ + tempsort[j+1] = tempsort[j]; + } + // insert + tempsort[j+1] = t; + } + + //copy the array over + memcpy(SortedPlayers,tempsort,MAX_PLAYER_RECORDS*sizeof(int)); + + //Now fill in the final structure of sorted names + int TeamCount[DLLMAX_TEAMS]; + int team; + for(i=0;iGetPlayerRecord(slot); + if(pr && pr->state!=STATE_EMPTY){ + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + team = (pr->state==STATE_INGAME)?dPlayers[pr->pnum].team:pr->team; + + if(team>=CTFNumOfTeams) + team = 0; + + SortedPLRPlayers[team][TeamCount[team]] = slot; + + TeamCount[team]++; + } + } + for(i=0;iOnPLRInit(); +} + +void OnPLRInterval(void) +{ + DMFCBase->OnPLRInterval(); + + int TeamCol = 35; + int NameCol = 180; + int PointsCol = 320; + int KillsCol = 370; + int DeathsCol = 410; + int SuicidesCol = 450; + int y = 40; + int slot; + bool had_members; + player_record *pr; + tPlayerStat *stat; + + DLLgrtext_SetFont((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]); + + int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]) + 1; + + //print out header + DLLgrtext_SetColor(GR_RGB(255,255,150)); + DLLgrtext_Printf(PointsCol,y,TXT_POINTS); + DLLgrtext_Printf(NameCol,y,TXT_PILOT); + DLLgrtext_Printf(KillsCol,y,TXT_KILLS_SHORT); + DLLgrtext_Printf(DeathsCol,y,TXT_DEATHS_SHORT); + DLLgrtext_Printf(SuicidesCol,y,TXT_SUICIDES_SHORT); + y+=height; + + bool doing_connected = true; + +do_disconnected_folk: + + for(int team=0;teamWasPlayerInGameAtLevelEnd(SortedPLRPlayers[team][temp_idx]); + if(pnum==-1) + { + show_team_label = true; + break; + } + temp_idx++; + } + }else + { + show_team_label = true; + } + + if(show_team_label){ + //is there anyone on this team? + DLLgrtext_SetColor(DMFCBase->GetTeamColor(team)); + DLLgrtext_Printf(TeamCol,y,TXT_TEAMSCOREFORM,DMFCBase->GetTeamString(team),TeamScores[team],OverallTeamScores[team]); + } + had_members = false; + + for(int index=0;indexGetPlayerRecord(slot); + if(!pr || pr->state==STATE_EMPTY) + continue; + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue; + + if( (doing_connected && DMFCBase->WasPlayerInGameAtLevelEnd(slot)==-1) || + (!doing_connected && DMFCBase->WasPlayerInGameAtLevelEnd(slot)!=-1) ) + continue;//we're not handling them right now + + stat = (tPlayerStat *)pr->user_info; + + int pnum=DMFCBase->WasPlayerInGameAtLevelEnd(slot); + if(pnum!=-1) + { + DLLgrtext_SetColor(DMFCBase->GetTeamColor(team)); + }else + { + DLLgrtext_SetColor(GR_RGB(128,128,128)); + } + + //valid player + char temp[120]; + sprintf(temp,"%s",pr->callsign); + DMFCBase->ClipString(PointsCol - NameCol - 10,temp,true); + + DLLgrtext_Printf(NameCol,y,"%s",temp); + DLLgrtext_Printf(PointsCol,y,"%d",(stat)?stat->Score[DSTAT_LEVEL]:0); + DLLgrtext_Printf(KillsCol,y,"%d",pr->dstats.kills[DSTAT_LEVEL]); + DLLgrtext_Printf(DeathsCol,y,"%d",pr->dstats.deaths[DSTAT_LEVEL]); + DLLgrtext_Printf(SuicidesCol,y,"%d",pr->dstats.suicides[DSTAT_LEVEL]); + y+=height; + + if(y>=440) + goto quick_exit; + + had_members = true; + + }//end for + + if(!had_members) + { + //skip a line + y+=height; + + if(y>=440) + goto quick_exit; + } + //on to the next team + }//end for + + if(doing_connected) + { + doing_connected = false; + goto do_disconnected_folk; + } + +quick_exit:; + +} + +void SaveStatsToFile(char *filename) +{ + CFILE *file; + DLLOpenCFILE(&file,filename,"wt"); + if(!file){ + DLLmprintf((0,"Unable to open output file\n")); + return; + } + + //write out game stats + #define BUFSIZE 150 + char buffer[BUFSIZE]; + char tempbuffer[25]; + int sortedslots[MAX_PLAYER_RECORDS]; + player_record *pr,*dpr; + tPInfoStat stat; + tPlayerStat *ps; + int count,length,p; + int team; + + //sort the stats + DMFCBase->GetSortedPlayerSlots(sortedslots,MAX_PLAYER_RECORDS); + + int i,t,j; + + for(i=0;i=0 && compare_slots(sortedslots[j],t); j--){ + sortedslots[j+1] = sortedslots[j]; + } + // insert + sortedslots[j+1] = t; + } + + count = 1; + + sprintf(buffer,TXT_STAT_HEADING,(DMFCBase->GetNetgameInfo())->name,(DMFCBase->GetCurrentMission())->cur_level); + DLLcf_WriteString(file,buffer); + + for(i=0;iGetNumTeams();i++){ + int team = SortedTeams[i]; + + sprintf(buffer,"%s: %d[%d]",DMFCBase->GetTeamString(team),TeamScores[team],OverallTeamScores[team]); + DLLcf_WriteString(file,buffer); + } + strcpy(buffer,"\r\n"); + DLLcf_WriteString(file,buffer); + + //Write team members + DLLcf_WriteString(file,""); //blank line + for(t=0;tGetNumTeams();t++){ + int team_i = SortedTeams[t]; + + sprintf(buffer,"%s:",DMFCBase->GetTeamString(team_i)); + DLLcf_WriteString(file,buffer); + + for(p=0;pGetPlayerRecord(sortedslots[p]); + if( pr && pr->state!=STATE_EMPTY) { + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue;//skip dedicated server + + if (pr->team == team_i) { //Check if current team + sprintf(buffer," %s",pr->callsign); + DLLcf_WriteString(file,buffer); + } + } + } + } + DLLcf_WriteString(file,""); //blank line + + sprintf(buffer,TXT_STAT_HEADINGA); + DLLcf_WriteString(file,buffer); + sprintf(buffer,"--------------------------------------------------------------------------------------------------"); + DLLcf_WriteString(file,buffer); + + for(int pslot=0;pslotGetPlayerRecord(real_slot); + if( pr && pr->state!=STATE_EMPTY){ + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue;//skip dedicated server + + //we have a valid play + ps = (tPlayerStat *)pr->user_info; + memset(buffer,' ',BUFSIZE); + + team = pr->team; + + sprintf(tempbuffer,"%d)",count); + memcpy(&buffer[0],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%.6s",DMFCBase->GetTeamString(team)); + memcpy(&buffer[5],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%s%s",(pr->state==STATE_INGAME)?"":"*",pr->callsign); + memcpy(&buffer[12],tempbuffer,strlen(tempbuffer)); + + if(ps){ + sprintf(tempbuffer,"%d[%d]",ps->Score[DSTAT_LEVEL],ps->Score[DSTAT_OVERALL]); + memcpy(&buffer[41],tempbuffer,strlen(tempbuffer)); + } + + sprintf(tempbuffer,"%d[%d]",pr->dstats.kills[DSTAT_LEVEL],pr->dstats.kills[DSTAT_OVERALL]); + memcpy(&buffer[53],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.deaths[DSTAT_LEVEL],pr->dstats.deaths[DSTAT_OVERALL]); + memcpy(&buffer[65],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d[%d]",pr->dstats.suicides[DSTAT_LEVEL],pr->dstats.suicides[DSTAT_OVERALL]); + memcpy(&buffer[76],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%s",DMFCBase->GetTimeString(DMFCBase->GetTimeInGame(real_slot))); + memcpy(&buffer[86],tempbuffer,strlen(tempbuffer)); + + int pos; + pos = 86 + strlen(tempbuffer) + 1; + if(posGetPlayerRecord(p); + if( pr && pr->state!=STATE_EMPTY) { + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue;//skip dedicated server + + //Write out header + sprintf(buffer,"%d) %s%s",count,(pr->state==STATE_INGAME)?"":"*",pr->callsign); + DLLcf_WriteString(file,buffer); + length = strlen(buffer); + memset(buffer,'=',length); + buffer[length] = '\0'; + DLLcf_WriteString(file,buffer); + + //time in game + sprintf(buffer,TXT_STAT_TIG,DMFCBase->GetTimeString(DMFCBase->GetTimeInGame(p))); + DLLcf_WriteString(file,buffer); + + if(DMFCBase->FindPInfoStatFirst(p,&stat)){ + sprintf(buffer,TXT_STAT_HEADINGC); + DLLcf_WriteString(file,buffer); + + if(stat.slot!=p){ + memset(buffer,' ',BUFSIZE); + dpr = DMFCBase->GetPlayerRecord(stat.slot); + int pos; + + if(dpr) + { + sprintf(tempbuffer,"%s",dpr->callsign); + memcpy(buffer,tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.kills); + memcpy(&buffer[30],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.deaths); + memcpy(&buffer[40],tempbuffer,strlen(tempbuffer)); + + pos = 40 + strlen(tempbuffer) + 1; + if(posFindPInfoStatNext(&stat)){ + if(stat.slot!=p){ + int pos; + memset(buffer,' ',BUFSIZE); + dpr = DMFCBase->GetPlayerRecord(stat.slot); + if(dpr) + { + sprintf(tempbuffer,"%s",dpr->callsign); + memcpy(buffer,tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.kills); + memcpy(&buffer[30],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.deaths); + memcpy(&buffer[40],tempbuffer,strlen(tempbuffer)); + + pos = 40 + strlen(tempbuffer) + 1; + if(posFindPInfoStatClose(); + DLLcf_WriteString(file,""); //skip a line + count++; + } + } + + //done writing stats + DLLcfclose(file); + DLLAddHUDMessage(TXT_STAT_SAVED); +} + +#define ROOTFILENAME "CTF" +void OnSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,false); + SaveStatsToFile(filename); +} + +void OnLevelEndSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,true); + SaveStatsToFile(filename); +} + +void OnDisconnectSaveStatsToFile(void) +{ + char filename[256]; + DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,false); + SaveStatsToFile(filename); +} + +void OnPrintScores(int level) +{ + char buffer[256]; + char name[70]; + int t,i; + int pos[6]; + int len[6]; + + + for(i=0;iGetTeamString(i),TeamScores[i]); + DPrintf(buffer); + } + + memset(buffer,' ',256); + pos[0] = 0; t = len[0] = 30; //give ample room for pilot name + pos[1] = pos[0] + t + 1; t = len[1] = strlen(TXT_POINTS); + pos[2] = pos[1] + t + 1; t = len[2] = strlen(TXT_KILLS_SHORT); + pos[3] = pos[2] + t + 1; t = len[3] = strlen(TXT_DEATHS_SHORT); + pos[4] = pos[3] + t + 1; t = len[4] = strlen(TXT_SUICIDES_SHORT); + pos[5] = pos[4] + t + 1; t = len[5] = strlen(TXT_PING); + + memcpy(&buffer[pos[0]],TXT_PILOT,strlen(TXT_PILOT)); + memcpy(&buffer[pos[1]],TXT_POINTS,len[1]); + memcpy(&buffer[pos[2]],TXT_KILLS_SHORT,len[2]); + memcpy(&buffer[pos[3]],TXT_DEATHS_SHORT,len[3]); + memcpy(&buffer[pos[4]],TXT_SUICIDES_SHORT,len[4]); + memcpy(&buffer[pos[5]],TXT_PING,len[5]); + buffer[pos[5]+len[5]+1] = '\n'; + buffer[pos[5]+len[5]+2] = '\0'; + DPrintf(buffer); + + int slot; + player_record *pr; + int pcount; + + if(level<0 || level>=MAX_PLAYER_RECORDS) + pcount = MAX_PLAYER_RECORDS; + else + pcount = level; + + int sortedplayers[MAX_PLAYER_RECORDS]; + DMFCBase->GetSortedPlayerSlots(sortedplayers,MAX_PLAYER_RECORDS); + + for(i=0;iGetPlayerRecord(slot); + if((pr)&&(pr->state!=STATE_EMPTY)){ + + if(DMFCBase->IsPlayerDedicatedServer(pr)) + continue; //skip dedicated server + + sprintf(name,"%s%s: %.8s",(pr->state==STATE_DISCONNECTED)?"*":"",pr->callsign,DMFCBase->GetTeamString(pr->team)); + name[29] = '\0'; + + tPlayerStat *stat; + stat = (tPlayerStat *)pr->user_info; + + memset(buffer,' ',256); + t = strlen(name); memcpy(&buffer[pos[0]],name,(tScore[DSTAT_LEVEL]:0); + t = strlen(name); memcpy(&buffer[pos[1]],name,(tdstats.kills[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[2]],name,(tdstats.deaths[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[3]],name,(tdstats.suicides[DSTAT_LEVEL]); + t = strlen(name); memcpy(&buffer[pos[4]],name,(tstate==STATE_INGAME) + sprintf(name,"%.0f",dNetPlayers[pr->pnum].ping_time*1000.0f); + else + strcpy(name,"---"); + t = strlen(name); memcpy(&buffer[pos[5]],name,(tGetLocalRole()==LR_SERVER){ + //if we are the server put the flag back in the center of the goal and tell all the players to do the same + DLLComputeRoomCenter(&fpos,&dRooms[GoalRooms[color]]); + int objnum = DLLObjCreate(OBJ_POWERUP,FlagIDs[color],GoalRooms[color],&fpos,NULL,OBJECT_HANDLE_NONE); + DLLMultiSendObject(&dObjects[objnum],1,true); + } + }else{ + //ok the flag is spewing out into the great beyond + //Start the timer! + Flag_timeout_timers[color] = DMFCBase->GetGametime() + FLAG_TIMEOUT_VALUE; + } + + //player has the flag + LoseFlagForPlayer(pnum,color,false); + } + //rotate flagmask so it is pointing at the next bit + flagmask = (flagmask>>1); + } + + // Reset colored balls + SetColoredBalls(pnum,true); + + + if(play_lose) + DoLoseFlag(DMFCBase->GetPlayerTeam(pnum)); +} + +///////////////////////////////////////////////////////////// +//CTF Functions +void DisplayHUDScores(struct tHUDItem *hitem) +{ + if(display_my_welcome) + { + //announce our arrival to ourselves (as server) + DisplayWelcomeMessage(DMFCBase->GetPlayerNum()); + display_my_welcome = false; + } + + if(!First_game_frame || DisplayScoreScreen) //interval hasn't been called yet or we are display the stats + return; + + ubyte alpha = DMFCBase->ConvertHUDAlpha((ubyte)((DisplayScoreScreen)?128:255)); + + int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[HUD_FONT_INDEX]) + 3; + + int y = 170; + int x = 540; + int team = DMFCBase->GetPlayerTeam(DMFCBase->GetPlayerNum()); + int myteam = team; + int i; + + //Flag bitmaps + //if (HasFlag[team]!=-1) then draw a dimmed flag with the team flag of who has it on top + //if (HasFlag[team]==-1) && (FlagAtHome[team]==true) then draw normal flag + //if (HasFlag[team]==-1) && (FlagAtHome[team]==false) then draw dimmed flag + int cx,cy; + for(i=0;iGetPlayerTeam(HasFlag[i])],alpha,0); + } + else{ + if(FlagAtHome[i]){ + //draw normal flag + DLLRenderHUDQuad(cx,cy,DLLbm_w(FlagAHBmp[i],0),DLLbm_h(FlagAHBmp[i],0),0,0,1,1,FlagAHBmp[i],alpha,0); + } + else{ + //draw dimmed flag + int f_height = DLLbm_h(DimFlagAHBmp[i],0); + DLLRenderHUDQuad(cx,cy,DLLbm_w(DimFlagAHBmp[i],0),f_height,0,0,1,1,DimFlagAHBmp[i],alpha,0); + + //Draw timeout time + int time_left; + time_left = (int)( Flag_timeout_timers[i] - DMFCBase->GetGametime()); + if(time_left>0 && Flag_timeout_timers[i]!=0){ + int minutes; + minutes = time_left/60; + time_left = time_left%60; + DLLRenderHUDText(GR_GREEN,alpha,0,cx,cy+2+f_height,"%01d:%02d",minutes,time_left); + } + + } + } + } + } + + x = 520; + + if(Highlight_bmp<=BAD_BITMAP_HANDLE){ + //write the name of your team at the top of the screen since for some reason we don't a highlight bmp + DLLRenderHUDText(DMFCBase->GetTeamColor(team),alpha,0,x,y,TXT_TEAMFORM,DMFCBase->GetTeamString(team)); y+=height; + } + + //determine coordinates to use here + //we'll use a virtual width of 85 pixels on a 640x480 screen + //so first determine the new width + int name_width = 85.0f * DMFCBase->GetHudAspectX(); + int score_width = DLLgrtext_GetTextLineWidth("888"); + int name_x = DMFCBase->GetGameWindowW() - name_width - score_width - 10; + int score_x = DMFCBase->GetGameWindowW() - score_width - 5; + y = (DMFCBase->GetGameWindowH()/2) - ((height*DMFCBase->GetNumTeams())/2); + + //draw the team scores + for(team=0;team=CTFNumOfTeams) + continue; + + if((WhoJustScored!=i) || (DisplayScoreBlink) ){ + + //determine the number of players on the team + int num_players; + num_players = 0; + for(int w=0;wCheckPlayerNum(w) && dPlayers[w].team==i && !DMFCBase->IsPlayerDedicatedServer(w)) + num_players++; + } + + if(i==myteam && Highlight_bmp>BAD_BITMAP_HANDLE){ + //draw the highlite bar in the background + DLLrend_SetAlphaValue(alpha*0.50f); + DLLrend_SetZBufferState (0); + DLLrend_SetTextureType (TT_LINEAR); + DLLrend_SetLighting (LS_NONE); + DLLrend_SetAlphaType (AT_CONSTANT_TEXTURE); + + DLLrend_DrawScaledBitmap(name_x-2,y-2,score_x+score_width+2,y+height-1,Highlight_bmp,0,0,1,1,1.0,BITMAP_FORMAT_1555,NULL); + DLLrend_SetZBufferState (1); + } + + char team_name[MAX_TEAMNAME_LEN+5]; + sprintf(team_name,"[%d]%s",num_players,DMFCBase->GetTeamString(i)); + //DMFCBase->ClipString(615 - x - 10,team_name,true); + //DLLRenderHUDText(DMFCBase->GetTeamColor(i),alpha,0,x,y,team_name); + //DLLRenderHUDText(DMFCBase->GetTeamColor(i),alpha,0,615,y,"%d",TeamScores[i]); + DMFCBase->ClipString(name_width,team_name,true); + + DLLgrtext_SetAlpha(alpha); + DLLgrtext_SetColor(DMFCBase->GetTeamColor(i)); + DLLgrtext_Printf(name_x,y,team_name); + DLLgrtext_Printf(score_x,y,"%d",TeamScores[i]); + + } + y+=height; + } + + //draw the bitmap for any flags you have currently + int flagcount; + int flagmask; + + flagcount = GetFlagCountForPlayer(DMFCBase->GetPlayerNum()); + flagmask = GetFlagMaskForPlayer(DMFCBase->GetPlayerNum()); + + if(flagcount==0) + return; + + y+=5; + + for(i=0;i>1; + } + +} + +#define compGT(a,b) (a < b) +void SortTeamScores(int *sortedindex,int *scores) +{ + int t; + int i, j; + + //copy scores into scoreinfo array + for(i=0;i=0 && compGT(scores[sortedindex[j]],scores[t]); j--){ + sortedindex[j+1] = sortedindex[j]; + } + // insert + sortedindex[j+1] = t; + } +} + +void DisplayWelcomeMessage(int player_num) +{ + char name_buffer[64]; + strcpy(name_buffer,(DMFCBase->GetPlayers())[player_num].callsign); + + if(player_num==DMFCBase->GetPlayerNum()) + { + DLLAddHUDMessage(TXT_WELCOME,name_buffer); + } + else + { + DLLAddHUDMessage(TXT_JOINED,name_buffer); + } +} + + +//adds a colored ball to a player (without removing what he has already) +void SetColoredBalls(int playernum,bool reset) +{ + float redb[4]; + float greenb[4]; + float blueb[4]; + + if(reset){ + DLLPlayerSetRotatingBall(playernum,0,0,NULL,NULL,NULL); + return; + } + + int flagcount; + int flagmask; + + bool red,blue,green,yellow; + red=blue=green=yellow = false; + + flagcount = GetFlagCountForPlayer(playernum); + flagmask = GetFlagMaskForPlayer(playernum); + + if(flagcount==0){ + DLLPlayerSetRotatingBall(playernum,0,0,NULL,NULL,NULL); + return; + } + + if(flagmask&FLAGMASK_REDTEAM) + red = true; + if(flagmask&FLAGMASK_BLUETEAM) + blue = true; + if(flagmask&FLAGMASK_GREENTEAM) + green = true; + if(flagmask&FLAGMASK_YELLOWTEAM) + yellow = true; + + for(int i=0;iSetNumberOfTeams(CTFNumOfTeams); +} + + +void DoFlagReturnedHome(int team) +{ + if(DMFCBase->GetPlayerTeam(DMFCBase->GetPlayerNum())==team){ + if(snd_return_ownteam!=-1) + DLLPlay2dSound(snd_return_ownteam,MAX_GAME_VOLUME/2); + }else{ + if(snd_return_otherteam!=-1) + DLLPlay2dSound(snd_return_otherteam,MAX_GAME_VOLUME/2); + } +} + +void DoLoseFlag(int team) +{ + if(DMFCBase->GetPlayerTeam(DMFCBase->GetPlayerNum())==team){ + if(snd_lose_ownteam!=-1) + DLLPlay2dSound(snd_lose_ownteam,MAX_GAME_VOLUME/2); + }else{ + if(snd_lose_otherteam!=-1) + DLLPlay2dSound(snd_lose_otherteam,MAX_GAME_VOLUME/2); + } + +} + +// VerifyFlagPosition +// Call this at certain intervals to verify a flags position. It will look at the +// flags room and makes sure that if it's in it's home room, the correct values +// are set, and vice versa. +void VerifyFlagPosition(void) +{ +} + +// AddFlagToPlayer +// gives a player a flag +bool AddFlagToPlayer(int pnum,int team,int flagobjnum) +{ + object *pobj = &dObjects[dPlayers[pnum].objnum]; + bool ret = true; + + if(DMFCBase->GetLocalRole()==LR_SERVER){ + //We're the server, so we need to create the flag and tell the clients, and then attach it + flagobjnum = DLLObjCreate(OBJ_POWERUP,AFlagIDs[team],pobj->roomnum,&pobj->pos,NULL,OBJECT_HANDLE_NONE); + + if(flagobjnum!=-1){ + //tell the clients that the flag has been created and they should attach it + TellClientsToAddorDelFlag(pnum,team,flagobjnum,true); + } + + // Attach the flag + + // 1st figure out how many flags the guy already has + // ------------------------------------------------- + int max_num_attach = 3; //based off chrishack for multiplayer + int num_attached = 0; + + if(!pobj->attach_children) + ret = false; + else + { + for(int ap=0;apattach_children[ap]!=OBJECT_HANDLE_NONE) + { + //attempt to make sure it's a valid object + object *tobj; + if(DLLObjGet(pobj->attach_children[ap],&tobj)) + { + num_attached++; + } + } + } + + // 2nd num_attached is the current number of flags this object has + // --------------------------------------------------------------- + switch(num_attached) + { + case 0: + { + //the easy case, nothing is attached, so just attach it to ap0 + ret = DLLAttachObject(pobj,0,&dObjects[flagobjnum],0,true); + }break; + + case 1: + { + bool retval; + object *tobj; + + //we should have a flag at ap0, move it to ap1 + ASSERT(pobj->attach_children[0]!=OBJECT_HANDLE_NONE); + int saved_ap0 = pobj->attach_children[0]; + retval = DLLUnattachChild(pobj,0); + ASSERT(retval); + + retval = DLLObjGet(saved_ap0,&tobj); + ASSERT(retval); + + //attach to ap1 + retval = DLLAttachObject(pobj,1,tobj,0,true); + ASSERT(retval); + + //attach new flag to ap2 + retval = DLLAttachObject(pobj,2,&dObjects[flagobjnum],0,true); + ASSERT(retval); + + ret = retval; + + }break; + + case 2: + { + //we should have a flag at ap1 and ap2 + //so just add this to ap3 + + //attach new flag to ap3 + ret = DLLAttachObject(pobj,0,&dObjects[flagobjnum],0,true); + + }break; + + case 3: + default: + { + Int3(); //Get Jeff + ret = false; + }break; + } + } + + if(!ret){ + //couldn't attach the flag + mprintf((0,"CTF: COULDN'T ATTACH FLAG TO PLAYER, DELETING\n")); + //tell the clients to remove this flag + DLLSetObjectDeadFlag(&dObjects[flagobjnum],true,false); + } + } + + if(flagobjnum==-1){ + //there was an error creating the flag...not good + mprintf((0,"CTF: Couldn't create/unhash flag for attachment\n")); + DMFCBase->DisconnectMe(); + return false; + } + + ChildFlags[team] = dObjects[flagobjnum].handle; + + return ret; +} + +// RemoveFlagFromPlayer +// removes a flag from a player +bool RemoveFlagFromPlayer(int pnum,int team) +{ + int flagobjnum = -1; + + if ( DMFCBase->GetLocalRole()==LR_SERVER) { + + if(ChildFlags[team]==OBJECT_HANDLE_NONE) + return false; + + object *fptr; + if(!DLLObjGet(ChildFlags[team],&fptr)){ + //the flag is already dead?!? + return false; + } + + DLLSetObjectDeadFlag(fptr,true,false); + + //tell the clients to remove the flag + TellClientsToAddorDelFlag(pnum,team,-1,false); + } + + ChildFlags[team] = OBJECT_HANDLE_NONE; + + return true; +} + +void TellClientsToAddorDelFlag(int pnum,int team,int objnum,bool add) +{ + if(add){ + //if we are adding a flag, than we need to send the object to them to make sure that + //they have it for when we go to process it + DLLMultiSendObject(&dObjects[objnum],false,true); + } + + ubyte data[MAX_GAME_DATA_SIZE]; + int size = 0; + DMFCBase->StartPacket(data,SPID_ADDDELFLAG,&size); + + MultiAddByte(pnum,data,&size); + MultiAddByte(team,data,&size); + MultiAddByte(add,data,&size); + + if(add){ + //add the objnum of the flag and pack it into the packet + MultiAddInt(objnum,data,&size); + } + + DMFCBase->SendPacket(data,size,SP_ALL); +} + +void ServerIsTellingMeToAddorDelAFlag(ubyte *data) +{ + int size = 0; + int pnum,team; + bool add; + + pnum = MultiGetByte(data,&size); + team = MultiGetByte(data,&size); + add = (MultiGetByte(data,&size))?true:false; + + //determine if we should add or delete a flag + if(add){ + int objnum; + objnum = MultiGetInt(data,&size); + objnum = DMFCBase->ConvertServerToLocalObjnum(objnum); + if(objnum==-1){ + //uh oh...corruption??? + FatalError("CTF: Server->Local Object Corruption\n"); + return; + } + AddFlagToPlayer(pnum,team,objnum); + }else{ + RemoveFlagFromPlayer(pnum,team); + } +} + + +///////////////////////////////// +//Timer event handlers +void OnTimer(void) +{ + DisplayFlagBlink = !DisplayFlagBlink; +} +void OnTimerKill(void) +{ + DisplayFlagBlink = true; + WhoJustFlagged = WhoJustFlaggedTimer = -1; +} + +void OnTimerScore(void) +{ + DisplayScoreBlink = !DisplayScoreBlink; +} +void OnTimerScoreKill(void) +{ + DisplayScoreBlink = true; + WhoJustScored = WhoJustScoredTimer = -1; +} + +/////////////////////////////////////////////////////////////////////////////////// +int PackByte(ubyte byte,ubyte *buffer,int pos) +{ + buffer[pos] = byte; + pos++; + return pos; +} + +int UnPackByte(ubyte *byte,ubyte *buffer,int pos) +{ + *byte = buffer[pos]; + pos++; + return pos; +} + +int PackBytes(ubyte *bytes,int count,ubyte *buffer,int pos) +{ + memcpy(&buffer[pos],bytes,count); + pos += count; + return pos; +} + +int UnPackBytes(ubyte *bytes,int count,ubyte *buffer,int pos) +{ + memcpy(bytes,&buffer[pos],count); + pos += count; + return pos; +} + +int PackWord(ushort word,ubyte *buffer,int pos) +{ + return PackBytes((ubyte *)&word,sizeof(ushort),buffer,pos); +} + +int UnPackWord(ushort *word,ubyte *buffer,int pos) +{ + return UnPackBytes((ubyte *)word,sizeof(ushort),buffer,pos); +} + +int PackInt(int data,ubyte *buffer,int pos) +{ + return PackBytes((ubyte *)&data,sizeof(int),buffer,pos); +} + +int UnPackInt(int *data,ubyte *buffer,int pos) +{ + return UnPackBytes((ubyte *)data,sizeof(int),buffer,pos); +} + +int PackArray(ubyte *data,int size,int count,ubyte *buffer,int pos) +{ + return PackBytes(data,size*count,buffer,pos); +} + +int UnPackArray(ubyte *data,int size,int count,ubyte *buffer,int pos) +{ + return UnPackBytes(data,size*count,buffer,pos); +} + +void SendGameState(int pnum) +{ + ubyte data[MAX_GAME_DATA_SIZE]; + int count = 0; + int i; + + DMFCBase->StartPacket(data,SPID_GAMESTATE,&count); + + //pack number of teams in this game + MultiAddInt(CTFNumOfTeams,data,&count); + + //pack flag whether someone has scored a hattrick + MultiAddByte(Someone_has_hattrick,data,&count); + + for(i=0;iSendPacket(data,count,pnum); +} + +void ReceiveGameState(ubyte *data) +{ + int count = 0; + int num_teams; + int i; + + for( i=0;iChildFlags and Attach the flags + for(i = 0; i ConvertServerToLocalObjnum(server_objnums[i]); + + if(our_objnum==-1){ + //fatal error + mprintf((0,"CTF: Local Objnums don't match server objnums\n")); + ChildFlags[i] = OBJECT_HANDLE_NONE; + DMFCBase->DisconnectMe(); + }else{ + //yeah! a valid objnum, attach it!!! + if(HasFlag[i]!=-1) + AddFlagToPlayer(HasFlag[i],i,our_objnum); + else{ + //hmm, HasFlag doesn't match ChildFlags + mprintf((0,"CTF: HasFlag doesn't match ChildFlags!!!!\n")); + ChildFlags[i] = OBJECT_HANDLE_NONE; + } + } + }else{ + //ok invalid... + ChildFlags[i] = OBJECT_HANDLE_NONE; + } + } +} + +/* +******************************************************************************************** +*/ + + +// returns the number of flags a player has, 0 if none, or an invalid pnum +int GetFlagCountForPlayer(int pnum) +{ + //1st check the pnum, make sure it is OK, is it isn't, return 0 + if(pnum<0 || pnum>=DLLMAX_PLAYERS){ + //invalid player number, return 0 flags + mprintf((0,"CTF: Invalid PNUM passed to GetFlagCountForPlayer()\n")); + return 0; + } + + int flag_count = 0; + //2nd, go through all the team flags, and check the player's inventory, see if they have the ID, + //if so, add it to their count + for(int i = 0; i < DLLMAX_TEAMS; i++){ + if(DLLInvCheckItem(pnum,OBJ_POWERUP,FlagIDs[i])){ + //they have this flag + flag_count++; + } + } + + return flag_count; +} + +// returns the mask of which flags this player currently has +ubyte GetFlagMaskForPlayer(int pnum) +{ + //1st check the pnum, make sure it is OK, if it isn't, return 0, meaning no flags + if(pnum<0 || pnum>=DLLMAX_PLAYERS){ + //invalid player number, return 0 flags + mprintf((0,"CTF: Invalid PNUM passed to GetFlagMaskForPlayer()\n")); + return 0; + } + + int flag_mask = 0; + ubyte mask = 0x01; + + //2nd go through all the teams flags, and check the player's inventory, see if they have the ID, + //if so, OR the current mask to the running flag_mask + for(int i = 0; i < DLLMAX_TEAMS; i++){ + if(DLLInvCheckItem(pnum,OBJ_POWERUP,FlagIDs[i])){ + //the have this flag + flag_mask |= mask; + } + //adjust the mask because we are moving to the next flag + mask = mask << 1; + } + + return flag_mask; +} + +// adds a flag to a player, as a precaution, it will go through all the players and makes sure that no one +// has the flag that is being added. If they are adding the flag, than remove that flag from whoever we thought had it +// it will return false if it had to remove a flag from a player +bool GivePlayerFlag(int pnum,ubyte team) +{ + //1st check the player num, make sure it is valid + if(!DMFCBase->CheckPlayerNum(pnum)){ + //not a valid player + mprintf((0,"CTF: Invalid pnum passed to GivePlayerFlag()\n")); + return false; + } + + //2nd check to make sure the team given is valid, and not our own team + if(team >= CTFNumOfTeams){ + //not a valid team + mprintf((0,"CTF: Invalid team passed to GivePlayerFlag() (team>=CTFNumOfTeams)\n")); + return false; + } + if(team == DMFCBase->GetPlayerTeam(pnum)){ + //we can't add a flag of the same team to a player + mprintf((0,"CTF: In GivePlayerFlag(), trying to add a player's home team flag\n")); + return false; + } + + //3rd, make sure no one else currently has this flag + //we'll check our HasFlags[] first + if(HasFlag[team]!=-1){ + //hmm, we have someone listed as already having this flag...odd + mprintf((0,"CTF: In GivePlayerFlag(), trying to add a flag, but we see someone else should already have it\n")); + int player = HasFlag[team]; + if(DMFCBase->CheckPlayerNum(player)){ + //this player is in the game... + //make sure this player doesn't have the flag in his inventory + while(DLLInvCheckItem(player,OBJ_POWERUP,FlagIDs[team])){ + //we have it listed that he does + mprintf((0,"CTF: In GivePlayerFlag(), we detected the flag in someone elses inventory\n")); + //remove all the flags that this player has of this team...very weird + DLLInvRemove(player,OBJ_POWERUP,FlagIDs[team]); + SetColoredBalls(player,false); + //check to see if the player had a flag attached to him + if(ChildFlags[team] != OBJECT_HANDLE_NONE && DMFCBase->GetLocalRole()==LR_SERVER){ + //he does have a flag attached to him...kill it + RemoveFlagFromPlayer(player,team); + } + } + } + //reset this value of the array + HasFlag[team] = -1; + if(DMFCBase->GetLocalRole()!=LR_SERVER){ + mprintf((0,"CTF: Game must be out of sync, requesting game state\n")); + DMFCBase->RequestGameState(); + } + } + + //loop through all the players and make sure they don't have this flag + for(int player = 0; player < DLLMAX_PLAYERS; player++ ){ + //check to see if it's an active player + if(!DMFCBase->CheckPlayerNum(player)) + continue; + + //remove all the flags the player has + while(DLLInvCheckItem(player,OBJ_POWERUP,FlagIDs[team])){ + mprintf((0,"CTF: In GivePlayerFlag(), detected a flag in a stranger's inventory\n")); + DLLInvRemove(player,OBJ_POWERUP,FlagIDs[team]); + SetColoredBalls(player,false); + //check to see if the player had a flag attached to him + if(ChildFlags[team]!=OBJECT_HANDLE_NONE && DMFCBase->GetLocalRole()==LR_SERVER){ + //he does have a flag attached to him...kill it + RemoveFlagFromPlayer(player,team); + } + } + } + + //ok, when we get here everything should be ducky, just add the flag and do the necessary things + DLLInvAddTypeID(pnum,OBJ_POWERUP,FlagIDs[team],-1,-1,0,NULL); + HasFlag[team] = pnum; + SetColoredBalls(pnum); + + if(DMFCBase->GetLocalRole()==LR_SERVER){ + //so we got here and added a flag to the player, now we need to attach the flag to the player + if(!AddFlagToPlayer(pnum,team)){ + //there was an error adding the flag,,,,ack! + mprintf((0,"CTF: In GivePlayerFlag(), couldn't attach the flag to the player\n")); + } + } + + return true; +} + +//this function takes a flag away from the player, useful for when he scores, spews, disconnects, or observer modes +void LoseFlagForPlayer(int pnum,ubyte team,bool remove_from_inven) +{ + //1st check the player number + if(pnum<0 || pnum>=DLLMAX_PLAYERS){ + mprintf((0,"CTF:Invalid pnum passed to LoseFlagForPlayer()\n")); + return; + } + + //2nd check the team number + if(team>=CTFNumOfTeams){ + mprintf((0,"CTF:Invalid team passed to LoseFlagForPlayer()\n")); + return; + } + + if(team==DMFCBase->GetPlayerTeam(pnum)){ + mprintf((0,"CTF:Invalid team passed to LoseFlagForPlayer()...same team as player\n")); + return; + } + + //ok, we have it registered that the flag belongs to us + while(remove_from_inven && DLLInvCheckItem(pnum,OBJ_POWERUP,FlagIDs[team])){ + DLLInvRemove(pnum,OBJ_POWERUP,FlagIDs[team]); + } + + SetColoredBalls(pnum); + HasFlag[team] = -1; + + //check to see if the player had a flag attached to him + if(ChildFlags[team] != OBJECT_HANDLE_NONE){ + //he does have a flag attached to him...kill it + if(DMFCBase->GetLocalRole()==LR_SERVER){ + //remove this flag and tell the clients to do so, cause we're the server + RemoveFlagFromPlayer(pnum,team); + } + } +} + +void OnGetTokenString(char *src,char *dest,int dest_size) +{ + if(!stricmp(src,"flag")) + { + int mypnum = DMFCBase->GetPlayerNum(); + int flagcount = GetFlagCountForPlayer(mypnum); + int flagmask = GetFlagMaskForPlayer(mypnum); + int team; + bool hasflag[DLLMAX_TEAMS]; + + for(team=0;team>1; + } + for(;teamGetTeamString(team)); + break; + } + } + + strncpy(dest,t,dest_size-1); + dest[dest_size-1] = '\0'; + return; + }break; + case 2: + { + char t[2][64]; + int found = 0; + for(team=0;teamGetTeamString(team)); + found++; + if(found==flagcount) + break; + } + } + + char buffer[256]; + sprintf(buffer,"%s and %s",t[0],t[1]); + strncpy(dest,buffer,dest_size-1); + dest[dest_size-1] = '\0'; + return; + }break; + case 3: + { + char t[3][64]; + int found = 0; + for(team=0;teamGetTeamString(team)); + found++; + if(found==flagcount) + break; + } + } + + char buffer[256]; + sprintf(buffer,"%s, %s and %s",t[0],t[1],t[2]); + strncpy(dest,buffer,dest_size-1); + dest[dest_size-1] = '\0'; + return; + + }break; + default: + strncpy(dest,"No",dest_size-1); + dest[dest_size-1] = '\0'; + return; + break; + } + + } + + DMFCBase->OnGetTokenString(src,dest,dest_size); +} + +#ifdef MACINTOSH +#pragma export off +#endif diff --git a/netgames/ctf/ctf.h b/netgames/ctf/ctf.h new file mode 100644 index 000000000..aad6bea8c --- /dev/null +++ b/netgames/ctf/ctf.h @@ -0,0 +1,148 @@ +#ifndef __DMFC_APP_H_ +#define __DMFC_APP_H_ + +#include "osiris_share.h" +#include "d3events.h" + +// If a player dies, becomes an observer or disconnects, it's possible that they +// have a flag or more. This function will handle setting certain variables, timer +// and other various settings related to a flag spew +void HandlePlayerSpew(int pnum); +// AddFlagToPlayer +// gives a player a flag +bool AddFlagToPlayer(int pnum,int team,int flagobjnum = -1); +// RemoveFlagFromPlayer +// removes a flag from a player +bool RemoveFlagFromPlayer(int pnum,int team); + + +void OnInterval(void); +void OnHUDInterval(void); +void OnKeypress(int key); +void OnServerGameCreated(void); +void OnClientLevelStart(void); +void OnClientLevelEnd(void); +void OnGameStateRequest(int player_num); +void OnPlayerConnect(int player_num); +void OnClientPlayerKilled(object *killer_obj,int victim_pnum); +void OnServerCollide(object *me_obj,object *it_obj); +void OnClientCollide(ubyte *data); +void OnPLRInterval(void); +void OnPLRInit(void); +bool OnCanChangeTeam(int pnum,int newteam); +void OnSaveStatsToFile(void); +void OnLevelEndSaveStatsToFile(void); +void OnDisconnectSaveStatsToFile(void); +void OnPrintScores(int level); +void OnPlayerEntersObserver(int pnum,object *piggy); +void OnClientPlayerDisconnect(int player_num); +void OnPlayerChangeTeam(int player_num,int newteam,bool announce,bool spew_onrespawn); + +extern IDMFC *DMFCBase; + +// These next two function prototypes MUST appear in the extern "C" block if called +// from a CPP file. +#ifdef __cplusplus +extern "C" +{ +#endif + DLLEXPORT void DLLFUNCCALL DLLGameInit (int *api_func,ubyte *all_ok,int num_teams_to_use); + DLLEXPORT void DLLFUNCCALL DLLGameCall (int eventnum,dllinfo *data); + DLLEXPORT void DLLFUNCCALL DLLGameClose (); + DLLEXPORT void DLLFUNCCALL DLLGetGameInfo (tDLLOptions *options); + DLLEXPORT int DLLFUNCCALL GetGOScriptID(char *name,ubyte isdoor); + DLLEXPORT void DLLFUNCCALLPTR CreateInstance(int id); + DLLEXPORT void DLLFUNCCALL DestroyInstance(int id,void *ptr); + DLLEXPORT short DLLFUNCCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data); + DLLEXPORT int DLLFUNCCALL SaveRestoreState( void *file_ptr, ubyte saving_state ); +#ifdef __cplusplus +} +#endif + +#ifdef MACINTOSH +#pragma export on +#endif + +// The main entry point where the game calls the dll +void DLLFUNCCALL DLLGameCall (int eventnum,dllinfo *data) +{ + if((eventnumGetLocalRole()!=LR_SERVER)){ + return; + } + + DMFCBase->TranslateEvent(eventnum,data); +} + +// GetGOScriptID +// Purpose: +// Given the name of the object (from it's pagename), this function will search through it's +// list of General Object Scripts for a script with a matching name (to see if there is a script +// for that type/id of object within this DLL). If a matching scriptname is found, a UNIQUE ID +// is to be returned back to Descent 3. This ID will be used from here on out for all future +// interaction with the DLL. Since doors are not part of the generic object's, it's possible +// for a door to have the same name as a generic object (OBJ_POWERUP, OBJ_BUILDING, OBJ_CLUTTER +// or OBJ_ROBOT), therefore, a 1 is passed in for isdoor if the given object name refers to a +// door, else it is a 0. The return value is the unique identifier, else -1 if the script +// does not exist in the DLL. +int DLLFUNCCALL GetGOScriptID(char *name,ubyte isdoor) +{ + return -1; +} + +// CreateInstance +// Purpose: +// Given an ID from a call to GetGOScriptID(), this function will create a new instance for that +// particular script (by allocating and initializing memory, etc.). A pointer to this instance +// is to be returned back to Descent 3. This pointer will be passed around, along with the ID +// for CallInstanceEvent() and DestroyInstance(). Return NULL if there was an error. +void DLLFUNCCALLPTR CreateInstance(int id) +{ + return NULL; +} + +// DestroyInstance +// Purpose: +// Given an ID, and a pointer to a particular instance of a script, this function will delete and +// destruct all information associated with that script, so it will no longer exist. +void DLLFUNCCALL DestroyInstance(int id,void *ptr) +{ +} + +// CallInstanceEvent +// Purpose: +// Given an ID, a pointer to a script instance, an event and a pointer to the struct of +// information about the event, this function will translate who this event belongs to and +// passes the event to that instance of the script to be handled. Return a combination of +// CONTINUE_CHAIN and CONTINUE_DEFAULT, to give instructions on what to do based on the +// event. CONTINUE_CHAIN means to continue through the chain of scripts (custom script, level +// script, mission script, and finally default script). If CONTINUE_CHAIN is not specified, +// than the chain is broken and those scripts of lower priority will never get the event. Return +// CONTINUE_DEFAULT in order to tell D3 if you want process the normal action that is built into +// the game for that event. This only pertains to certain events. If the chain continues +// after this script, than the CONTINUE_DEFAULT setting will be overridden by lower priority +// scripts return value. +short DLLFUNCCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data) +{ + return CONTINUE_CHAIN|CONTINUE_DEFAULT; +} + +// SaveRestoreState +// Purpose: +// This function is called when Descent 3 is saving or restoring the game state. In this function +// you should save/restore any global data that you want preserved through load/save (which includes +// demos). You must be very careful with this function, corrupting the file (reading or writing too +// much or too little) may be hazardous to the game (possibly making it impossible to restore the +// state). It would be best to use version information to keep older versions of saved states still +// able to be used. IT IS VERY IMPORTANT WHEN SAVING THE STATE TO RETURN THE NUMBER OF _BYTES_ WROTE +// TO THE FILE. When restoring the data, the return value is ignored. saving_state is 1 when you should +// write data to the file_ptr, 0 when you should read in the data. +int DLLFUNCCALL SaveRestoreState( void *file_ptr, ubyte saving_state ) +{ + return 0; +} + +#ifdef MACINTOSH +#pragma export off +#endif + +#endif diff --git a/netgames/dmfc/CMakeLists.txt b/netgames/dmfc/CMakeLists.txt new file mode 100644 index 000000000..d1182b28e --- /dev/null +++ b/netgames/dmfc/CMakeLists.txt @@ -0,0 +1,30 @@ +SET (HEADERS dmfcdllinit.h + dmfcinputcommands.h + dmfcinternal.h + encryption.h +) + +SET (CPPS + dmfcbase.cpp + dmfccfg.cpp + dmfcclient.cpp + dmfcfunctions.cpp + dmfchudmessages.cpp + dmfcinputcommand.cpp + dmfcmenu.cpp + dmfcpackets.cpp + dmfcpinfo.cpp + dmfcprecord.cpp + dmfcremote.cpp + dmfcserver.cpp + dmfcstats.cpp + dmfctimer.cpp + dmfcui.cpp + dmfcvirtual.cpp + idmfc.cpp + encryption.cpp + dmfcinterface.cpp) + +ADD_DEFINITIONS(-DOUTRAGE_VERSION -DDMFC_DLL) + +ADD_LIBRARY(dmfc STATIC ${HEADERS} ${CPPS}) diff --git a/netgames/dmfc/dmfc.def b/netgames/dmfc/dmfc.def new file mode 100644 index 000000000..4fdd1122d --- /dev/null +++ b/netgames/dmfc/dmfc.def @@ -0,0 +1,1175 @@ +LIBRARY +EXPORTS + _CreateMenuItemWArgs@20 + CreateMenuItemWArgs@20=_CreateMenuItemWArgs@20 + _CreateDMFC@0 + CreateDMFC@0=_CreateDMFC@0 + _CreateDmfcStats@0 + CreateDmfcStats@0=_CreateDmfcStats@0 + _CreateMenuItem@0 + CreateMenuItem@0=_CreateMenuItem@0 + DLLAddBlinkingHUDMessage + DLLAddColoredHUDMessage + DLLAddHUDItem + DLLAddHUDMessage + DLLApplyDamageToPlayer + DLLAttachObject + DLLAttachObjectRadius + DLLAttachRandomNapalmEffectsToObject + DLLButtonCreate + DLLCheckBoxCreate + DLLCheckBoxIsChecked + DLLCheckBoxSetCheck + DLLComputeRoomCenter + DLLConvertAxisAmountToEuler + DLLConvertEulerToAxisAmount + DLLCreateAndFireWeapon + DLLCreateNewUIBmpItem + DLLCreateNewUITextItem + DLLCreateRandomLineSparks + DLLCreateRandomParticles + DLLCreateRandomSparks + DLLCreateStringTable + DLLDebugBreak_callback_resume + DLLDebugBreak_callback_stop + DLLDebug_ConsolePrintf + DLLDeleteUIItem + DLLDescentDefer + DLLDestroyStringTable + DLLDoMessageBox + DLLDoUI + DLLEditCreate + DLLEditGetText + DLLEditSetText + DLLEndFrame + DLLFindArg + DLLFindObjectIDName + DLLFindShipName + DLLFindSoundName + DLLFindTextureName + DLLFindWeaponName + DLLFireWeaponFromObject + DLLGameFrame + DLLGameRenderWorld + DLLGetFrameParameters + DLLGetGameAPI + DLLGetGoalRoomForTeam + DLLGetUICallback + DLLGetUIItemHeight + DLLGetUIItemPosition + DLLGetUIItemWidth + DLLGetUltimateParentForObject + DLLHideCursor + DLLHotSpotCreate + DLLHotSpotSetStates + DLLIncTeamScore + DLLInitObjectScripts + DLLInitPlayerNewShip + DLLInt3MessageBox + DLLInvAddTypeID + DLLInvCheckItem + DLLInvGetTypeIDCount + DLLInvRemove + DLLInvReset + DLLListAddItem + DLLListCreate + DLLListGetItem + DLLListGetSelectedIndex + DLLListRemoveAll + DLLListRemoveItem + DLLListSelectItem + DLLListSetSelectedIndex + DLLMultiClientSendSpecialPacket + DLLMultiDisconnectPlayer + DLLMultiEndLevel + DLLMultiGetMatchChecksum + DLLMultiMatchGeneric + DLLMultiMatchWeapon + DLLMultiPaintGoalRooms + DLLMultiSendClientExecuteDLL + DLLMultiSendObject + DLLMultiSendRequestToObserve + DLLMultiSendSpecialPacket + DLLMultiSetLogoState + DLLNewUIGameWindowClose + DLLNewUIGameWindowCreate + DLLNewUIGameWindowDestroy + DLLNewUIGameWindowOpen + DLLNewUIWindowClose + DLLNewUIWindowCreate + DLLNewUIWindowDestroy + DLLNewUIWindowLoadBackgroundImage + DLLNewUIWindowOpen + DLLNewUIWindowSetFocusOnEditGadget + DLLObjCreate + DLLObjGet + DLLObjSetPos + DLLObjSetPosNoMark + DLLOldEditCreate + DLLOldEditGetText + DLLOldEditSetText + DLLOldListAddItem + DLLOldListCreate + DLLOldListGetItem + DLLOldListGetSelectedIndex + DLLOldListRemoveAll + DLLOldListRemoveItem + DLLOldListSelectItem + DLLOpenCFILE + DLLPPic_GetBitmapHandle + DLLPPic_GetPilot + DLLPlay2dSound + DLLPlay3dSound + DLLPlayerChangeShip + DLLPlayerGetRandomStartPosition + DLLPlayerSetHUDNameFOV + DLLPlayerSetLighting + DLLPlayerSetRotatingBall + DLLPlayerSpewInventory + DLLPlayerStopSounds + DLLPollUI + DLLRemoveUIBmpItem + DLLRemoveUITextItem + DLLRenderHUDGetTextHeight + DLLRenderHUDGetTextLineWidth + DLLRenderHUDQuad + DLLRenderHUDText + DLLRenderHUDTextFlags + DLLResetFacings + DLLResumeControls + DLLSelectNextCameraView + DLLSetMaxTeams + DLLSetObjectDeadFlag + DLLSetObjectDeadFlagRaw + DLLSetOldEditBufferLen + DLLSetUICallback + DLLSetUITextItemText + DLLShowCursor + DLLSliderCreate + DLLSliderGetPos + DLLSliderGetRange + DLLSliderSetPos + DLLSliderSetRange + DLLSliderSetSelectChangeCallback + DLLSliderSetSelectChangeCallbackWData + DLLSpewClearEvent + DLLSpewCreate + DLLStartFrame + DLLSuspendControls + DLLTextCreate + DLLTextSetTitle + DLLToggleUICallback + DLLTouchSound + DLLUIConsoleGadgetCreate + DLLUIConsoleGadgetputs + DLLUnattachChild + DLLUnattachChildren + DLLUnattachFromParent + DLLVisEffectAllocate + DLLVisEffectCreate + DLLVisEffectCreateControlled + DLLVisEffectDelete + DLLVisEffectFree + DLLVisEffectInitType + DLLVisEffectLink + DLLVisEffectRelink + DLLVisEffectUnlink + DLLassert + DLLbm_AllocBitmap + DLLbm_AllocLoadFileBitmap + DLLbm_CreateChunkedBitmap + DLLbm_DestroyChunkedBitmap + DLLbm_FreeBitmap + DLLbm_data + DLLbm_h + DLLbm_w + DLLcf_CloseLibrary + DLLcf_CopyFile + DLLcf_Diff + DLLcf_OpenLibrary + DLLcf_ReadByte + DLLcf_ReadBytes + DLLcf_ReadDouble + DLLcf_ReadFloat + DLLcf_ReadInt + DLLcf_ReadShort + DLLcf_ReadString + DLLcf_WriteByte + DLLcf_WriteBytes + DLLcf_WriteDouble + DLLcf_WriteFloat + DLLcf_WriteInt + DLLcf_WriteShort + DLLcf_WriteString + DLLcfclose + DLLcfeof + DLLcfexist + DLLddio_MakePath + DLLddio_SplitPath + DLLfvi_FindIntersection + DLLfvi_QuickDistCellList + DLLfvi_QuickDistFaceList + DLLfvi_QuickDistObjectList + DLLfvi_QuickRoomCheck + DLLg3_AddDeltaVec + DLLg3_CalcPointDepth + DLLg3_CheckAndDrawPoly + DLLg3_CheckNormalFacing + DLLg3_ClipPolygon + DLLg3_CodePoint + DLLg3_DoneInstance + DLLg3_DrawBitmap + DLLg3_DrawBox + DLLg3_DrawLine + DLLg3_DrawPlanarRotatedBitmap + DLLg3_DrawPoly + DLLg3_DrawRotatedBitmap + DLLg3_DrawSpecialLine + DLLg3_DrawSphere + DLLg3_EndFrame + DLLg3_FreeTempPoints + DLLg3_GetMatrixScale + DLLg3_GetUnscaledMatrix + DLLg3_GetViewMatrix + DLLg3_GetViewPosition + DLLg3_Point2Vec + DLLg3_ProjectPoint + DLLg3_RotateDeltaVec + DLLg3_RotateDeltaX + DLLg3_RotateDeltaY + DLLg3_RotateDeltaZ + DLLg3_RotatePoint + DLLg3_SetCustomClipPlane + DLLg3_SetFarClipZ + DLLg3_SetTriangulationTest + DLLg3_StartFrame + DLLg3_StartInstanceAngles + DLLg3_StartInstanceMatrix + DLLgrfont_GetHeight + DLLgrtext_CenteredPrintf + DLLgrtext_Flush + DLLgrtext_GetAlpha + DLLgrtext_GetFont + DLLgrtext_GetTextLineWidth + DLLgrtext_Printf + DLLgrtext_SetAlpha + DLLgrtext_SetColor + DLLgrtext_SetFancyColor + DLLgrtext_SetFont + DLLnw_GetHostAddressFromNumbers + DLLnw_GetNumbersFromHostAddress + DLLnw_GetThisIP + DLLphys_apply_force + DLLphys_apply_rot + DLLrend_ClearScreen + DLLrend_DrawChunkedBitmap + DLLrend_DrawCircle + DLLrend_DrawLFBBitmap + DLLrend_DrawLine + DLLrend_DrawPolygon + DLLrend_DrawScaledBitmap + DLLrend_DrawScaledChunkedBitmap + DLLrend_DrawSimpleBitmap + DLLrend_DrawSpecialLine + DLLrend_FillCircle + DLLrend_FillRect + DLLrend_GetLFBLock + DLLrend_GetPixel + DLLrend_ReleaseLFBLock + DLLrend_SetAlphaType + DLLrend_SetAlphaValue + DLLrend_SetColorModel + DLLrend_SetFiltering + DLLrend_SetFlatColor + DLLrend_SetFogState + DLLrend_SetLighting + DLLrend_SetMipState + DLLrend_SetOverlayMap + DLLrend_SetOverlayType + DLLrend_SetPixel + DLLrend_SetTextureType + DLLrend_SetWrapType + DLLrend_SetZBias + DLLrend_SetZBufferState + DLLrend_SetZBufferWriteMask + DLLtaunt_AreEnabled + DLLtaunt_Enable + DLLvm_AddVectors + DLLvm_AnglesToMatrix + DLLvm_AverageVector + DLLvm_CalcDetValue + DLLvm_ClearMatrix + DLLvm_ComputeBoundingSphere + DLLvm_CrossProduct + DLLvm_DeltaAngVec + DLLvm_DeltaAngVecNorm + DLLvm_DistToPlane + DLLvm_DivVector + DLLvm_DotProduct + DLLvm_ExtractAnglesFromMatrix + DLLvm_GetCentroid + DLLvm_GetCentroidFast + DLLvm_GetMagnitude + DLLvm_GetMagnitudeFast + DLLvm_GetNormal + DLLvm_GetNormalizedDir + DLLvm_GetNormalizedDirFast + DLLvm_GetPerp + DLLvm_GetSlope + DLLvm_MakeAngleZero + DLLvm_MakeIdentity + DLLvm_MakeInverseMatrix + DLLvm_MakeRandomVector + DLLvm_MakeVectorZero + DLLvm_MatrixMul + DLLvm_MatrixMulTMatrix + DLLvm_MatrixMulVector + DLLvm_NormalizeVector + DLLvm_NormalizeVectorFast + DLLvm_Orthogonalize + DLLvm_ScaleAddVector + DLLvm_ScaleVector + DLLvm_SinCos + DLLvm_SinCosToMatrix + DLLvm_SubVectors + DLLvm_TransposeMatrix + DLLvm_VectorAngleToMatrix + DLLvm_VectorDistance + DLLvm_VectorDistanceQuick + DLLvm_VectorMulTMatrix + DLLvm_VectorToMatrix + DPrintf + DatabaseRead1 + DatabaseRead2 + DatabaseRead3 + DatabaseWrite1 + DatabaseWrite2 + FatalError + GetPlayerRankIndex + Inven_Add + Inven_AddCounterMeasure + Inven_AddObject + Inven_AtBeginning + Inven_AtEnd + Inven_CheckItem + Inven_FindPos + Inven_GetAuxPosTypeID + Inven_GetInventoryItemList + Inven_GetPos + Inven_GetPosCount + Inven_GetPosDescription + Inven_GetPosIconName + Inven_GetPosInfo + Inven_GetPosName + Inven_GetPosTypeID + Inven_GetTypeIDCount + Inven_GotoPos + Inven_GotoPosTypeID + Inven_IsSelectable + Inven_IsUsable + Inven_NextPos + Inven_PrevPos + Inven_Remove + Inven_Reset + Inven_ResetPos + Inven_Size + Inven_Use + Inven_UseObjHandle + Inven_UsePos + Inven_ValidatePos + TableFileAdd + TableFilesClear + _IDMFC_AddDeathMessage@12 + IDMFC_AddDeathMessage@12=_IDMFC_AddDeathMessage@12 + _IDMFC_AddHUDItemCallback@12 + IDMFC_AddHUDItemCallback@12=_IDMFC_AddHUDItemCallback@12 + _IDMFC_AddInputCommand@16 + IDMFC_AddInputCommand@16=_IDMFC_AddInputCommand@16 + _IDMFC_AddSuicideMessage@8 + IDMFC_AddSuicideMessage@8=_IDMFC_AddSuicideMessage@8 + _IDMFC_AddWeaponHash@16 + IDMFC_AddWeaponHash@16=_IDMFC_AddWeaponHash@16 + _IDMFC_AllowTeamChange@4 + IDMFC_AllowTeamChange@4=_IDMFC_AllowTeamChange@4 + _IDMFC_AnnounceTeamChangeDeny@8 + IDMFC_AnnounceTeamChangeDeny@8=_IDMFC_AnnounceTeamChangeDeny@8 + _IDMFC_AreLogosEnabled@4 + IDMFC_AreLogosEnabled@4=_IDMFC_AreLogosEnabled@4 + _IDMFC_AreTauntsEnabled@4 + IDMFC_AreTauntsEnabled@4=_IDMFC_AreTauntsEnabled@4 + _IDMFC_AutoDeathMessage@8 + IDMFC_AutoDeathMessage@8=_IDMFC_AutoDeathMessage@8 + _IDMFC_AutoTeamSelect@8 + IDMFC_AutoTeamSelect@8=_IDMFC_AutoTeamSelect@8 + _IDMFC_AutoTimeLimit@8 + IDMFC_AutoTimeLimit@8=_IDMFC_AutoTimeLimit@8 + _IDMFC_BanPlayerFromGame@8 + IDMFC_BanPlayerFromGame@8=_IDMFC_BanPlayerFromGame@8 + _IDMFC_CFGClose@4 + IDMFC_CFGClose@4=_IDMFC_CFGClose@4 + _IDMFC_CFGCreateKey@8 + IDMFC_CFGCreateKey@8=_IDMFC_CFGCreateKey@8 + _IDMFC_CFGCreateRecord@16 + IDMFC_CFGCreateRecord@16=_IDMFC_CFGCreateRecord@16 + _IDMFC_CFGFlush@4 + IDMFC_CFGFlush@4=_IDMFC_CFGFlush@4 + _IDMFC_CFGLookupKey@8 + IDMFC_CFGLookupKey@8=_IDMFC_CFGLookupKey@8 + _IDMFC_CFGLookupRecord@12 + IDMFC_CFGLookupRecord@12=_IDMFC_CFGLookupRecord@12 + _IDMFC_CFGOpen@8 + IDMFC_CFGOpen@8=_IDMFC_CFGOpen@8 + _IDMFC_CallClientEvent@24 + IDMFC_CallClientEvent@24=_IDMFC_CallClientEvent@24 + _IDMFC_CallOnAllowObserverChange@8 + IDMFC_CallOnAllowObserverChange@8=_IDMFC_CallOnAllowObserverChange@8 + _IDMFC_CallOnCanChangeTeam@12 + IDMFC_CallOnCanChangeTeam@12=_IDMFC_CallOnCanChangeTeam@12 + _IDMFC_CallOnClientCollideA@12 + IDMFC_CallOnClientCollideA@12=_IDMFC_CallOnClientCollideA@12 + _IDMFC_CallOnClientCollideB@20 + IDMFC_CallOnClientCollideB@20=_IDMFC_CallOnClientCollideB@20 + _IDMFC_CallOnClientGameCreated@4 + IDMFC_CallOnClientGameCreated@4=_IDMFC_CallOnClientGameCreated@4 + _IDMFC_CallOnClientLevelChange@4 + IDMFC_CallOnClientLevelChange@4=_IDMFC_CallOnClientLevelChange@4 + _IDMFC_CallOnClientLevelEnd@4 + IDMFC_CallOnClientLevelEnd@4=_IDMFC_CallOnClientLevelEnd@4 + _IDMFC_CallOnClientLevelStart@4 + IDMFC_CallOnClientLevelStart@4=_IDMFC_CallOnClientLevelStart@4 + _IDMFC_CallOnClientObjectChangeSegment@16 + IDMFC_CallOnClientObjectChangeSegment@16=_IDMFC_CallOnClientObjectChangeSegment@16 + _IDMFC_CallOnClientObjectDestroyed@8 + IDMFC_CallOnClientObjectDestroyed@8=_IDMFC_CallOnClientObjectDestroyed@8 + _IDMFC_CallOnClientObjectKilled@12 + IDMFC_CallOnClientObjectKilled@12=_IDMFC_CallOnClientObjectKilled@12 + _IDMFC_CallOnClientObjectShieldsChanged@12 + IDMFC_CallOnClientObjectShieldsChanged@12=_IDMFC_CallOnClientObjectShieldsChanged@12 + _IDMFC_CallOnClientPlayerChangeSegment@16 + IDMFC_CallOnClientPlayerChangeSegment@16=_IDMFC_CallOnClientPlayerChangeSegment@16 + _IDMFC_CallOnClientPlayerDisconnect@8 + IDMFC_CallOnClientPlayerDisconnect@8=_IDMFC_CallOnClientPlayerDisconnect@8 + _IDMFC_CallOnClientPlayerEntersGame@8 + IDMFC_CallOnClientPlayerEntersGame@8=_IDMFC_CallOnClientPlayerEntersGame@8 + _IDMFC_CallOnClientPlayerExploded@8 + IDMFC_CallOnClientPlayerExploded@8=_IDMFC_CallOnClientPlayerExploded@8 + _IDMFC_CallOnClientPlayerKilled@12 + IDMFC_CallOnClientPlayerKilled@12=_IDMFC_CallOnClientPlayerKilled@12 + _IDMFC_CallOnClientShowUI@12 + IDMFC_CallOnClientShowUI@12=_IDMFC_CallOnClientShowUI@12 + _IDMFC_CallOnClientWallCollide@32 + IDMFC_CallOnClientWallCollide@32=_IDMFC_CallOnClientWallCollide@32 + _IDMFC_CallOnControlMessage@12 + IDMFC_CallOnControlMessage@12=_IDMFC_CallOnControlMessage@12 + _IDMFC_CallOnDisconnectSaveStatsToFile@4 + IDMFC_CallOnDisconnectSaveStatsToFile@4=_IDMFC_CallOnDisconnectSaveStatsToFile@4 + _IDMFC_CallOnDoControls@8 + IDMFC_CallOnDoControls@8=_IDMFC_CallOnDoControls@8 + _IDMFC_CallOnGameStateRequest@8 + IDMFC_CallOnGameStateRequest@8=_IDMFC_CallOnGameStateRequest@8 + _IDMFC_CallOnGetHudCallSignColor@8 + IDMFC_CallOnGetHudCallSignColor@8=_IDMFC_CallOnGetHudCallSignColor@8 + _IDMFC_CallOnHUDInterval@4 + IDMFC_CallOnHUDInterval@4=_IDMFC_CallOnHUDInterval@4 + _IDMFC_CallOnInputString@8 + IDMFC_CallOnInputString@8=_IDMFC_CallOnInputString@8 + _IDMFC_CallOnInterval@4 + IDMFC_CallOnInterval@4=_IDMFC_CallOnInterval@4 + _IDMFC_CallOnKeypress@8 + IDMFC_CallOnKeypress@8=_IDMFC_CallOnKeypress@8 + _IDMFC_CallOnLevelEndSaveStatsToFile@4 + IDMFC_CallOnLevelEndSaveStatsToFile@4=_IDMFC_CallOnLevelEndSaveStatsToFile@4 + _IDMFC_CallOnMeDisconnectFromServer@4 + IDMFC_CallOnMeDisconnectFromServer@4=_IDMFC_CallOnMeDisconnectFromServer@4 + _IDMFC_CallOnPLRInit@4 + IDMFC_CallOnPLRInit@4=_IDMFC_CallOnPLRInit@4 + _IDMFC_CallOnPLRInterval@4 + IDMFC_CallOnPLRInterval@4=_IDMFC_CallOnPLRInterval@4 + _IDMFC_CallOnPlayAudioTaunt@8 + IDMFC_CallOnPlayAudioTaunt@8=_IDMFC_CallOnPlayAudioTaunt@8 + _IDMFC_CallOnPlayerChangeTeam@20 + IDMFC_CallOnPlayerChangeTeam@20=_IDMFC_CallOnPlayerChangeTeam@20 + _IDMFC_CallOnPlayerConnect@8 + IDMFC_CallOnPlayerConnect@8=_IDMFC_CallOnPlayerConnect@8 + _IDMFC_CallOnPlayerEntersObserver@12 + IDMFC_CallOnPlayerEntersObserver@12=_IDMFC_CallOnPlayerEntersObserver@12 + _IDMFC_CallOnPlayerExitsObserver@8 + IDMFC_CallOnPlayerExitsObserver@8=_IDMFC_CallOnPlayerExitsObserver@8 + _IDMFC_CallOnPlayerReconnect@8 + IDMFC_CallOnPlayerReconnect@8=_IDMFC_CallOnPlayerReconnect@8 + _IDMFC_CallOnPrintScores@8 + IDMFC_CallOnPrintScores@8=_IDMFC_CallOnPrintScores@8 + _IDMFC_CallOnSaveStatsToFile@4 + IDMFC_CallOnSaveStatsToFile@4=_IDMFC_CallOnSaveStatsToFile@4 + _IDMFC_CallOnServerCollideA@12 + IDMFC_CallOnServerCollideA@12=_IDMFC_CallOnServerCollideA@12 + _IDMFC_CallOnServerCollideB@20 + IDMFC_CallOnServerCollideB@20=_IDMFC_CallOnServerCollideB@20 + _IDMFC_CallOnServerGameCreated@4 + IDMFC_CallOnServerGameCreated@4=_IDMFC_CallOnServerGameCreated@4 + _IDMFC_CallOnServerIsAddressBanned@12 + IDMFC_CallOnServerIsAddressBanned@12=_IDMFC_CallOnServerIsAddressBanned@12 + _IDMFC_CallOnServerLevelChange@4 + IDMFC_CallOnServerLevelChange@4=_IDMFC_CallOnServerLevelChange@4 + _IDMFC_CallOnServerLevelEnd@4 + IDMFC_CallOnServerLevelEnd@4=_IDMFC_CallOnServerLevelEnd@4 + _IDMFC_CallOnServerLevelStart@4 + IDMFC_CallOnServerLevelStart@4=_IDMFC_CallOnServerLevelStart@4 + _IDMFC_CallOnServerObjectChangeSegment@16 + IDMFC_CallOnServerObjectChangeSegment@16=_IDMFC_CallOnServerObjectChangeSegment@16 + _IDMFC_CallOnServerObjectDestroyed@8 + IDMFC_CallOnServerObjectDestroyed@8=_IDMFC_CallOnServerObjectDestroyed@8 + _IDMFC_CallOnServerObjectKilled@12 + IDMFC_CallOnServerObjectKilled@12=_IDMFC_CallOnServerObjectKilled@12 + _IDMFC_CallOnServerObjectShieldsChanged@12 + IDMFC_CallOnServerObjectShieldsChanged@12=_IDMFC_CallOnServerObjectShieldsChanged@12 + _IDMFC_CallOnServerPlayerChangeSegment@16 + IDMFC_CallOnServerPlayerChangeSegment@16=_IDMFC_CallOnServerPlayerChangeSegment@16 + _IDMFC_CallOnServerPlayerDisconnect@8 + IDMFC_CallOnServerPlayerDisconnect@8=_IDMFC_CallOnServerPlayerDisconnect@8 + _IDMFC_CallOnServerPlayerEntersGame@8 + IDMFC_CallOnServerPlayerEntersGame@8=_IDMFC_CallOnServerPlayerEntersGame@8 + _IDMFC_CallOnServerPlayerExploded@8 + IDMFC_CallOnServerPlayerExploded@8=_IDMFC_CallOnServerPlayerExploded@8 + _IDMFC_CallOnServerPlayerKilled@12 + IDMFC_CallOnServerPlayerKilled@12=_IDMFC_CallOnServerPlayerKilled@12 + _IDMFC_CallOnServerWallCollide@32 + IDMFC_CallOnServerWallCollide@32=_IDMFC_CallOnServerWallCollide@32 + _IDMFC_CallOnSpecialPacket@4 + IDMFC_CallOnSpecialPacket@4=_IDMFC_CallOnSpecialPacket@4 + _IDMFC_CallOnTeamChangeName@16 + IDMFC_CallOnTeamChangeName@16=_IDMFC_CallOnTeamChangeName@16 + _IDMFC_CallOnWeaponFired@12 + IDMFC_CallOnWeaponFired@12=_IDMFC_CallOnWeaponFired@12 + _IDMFC_CheckPInfo@4 + IDMFC_CheckPInfo@4=_IDMFC_CheckPInfo@4 + _IDMFC_CheckPlayerNum@8 + IDMFC_CheckPlayerNum@8=_IDMFC_CheckPlayerNum@8 + _IDMFC_ClipString@16 + IDMFC_ClipString@16=_IDMFC_ClipString@16 + _IDMFC_CompareNetworkAddress@16 + IDMFC_CompareNetworkAddress@16=_IDMFC_CompareNetworkAddress@16 + _IDMFC_ConvertHUDAlphaByte@8 + IDMFC_ConvertHUDAlphaByte@8=_IDMFC_ConvertHUDAlphaByte@8 + _IDMFC_ConvertHUDAlphaFloat@8 + IDMFC_ConvertHUDAlphaFloat@8=_IDMFC_ConvertHUDAlphaFloat@8 + _IDMFC_ConvertHUDCoord@20 + IDMFC_ConvertHUDCoord@20=_IDMFC_ConvertHUDCoord@20 + _IDMFC_ConvertLocalToServerObjnum@8 + IDMFC_ConvertLocalToServerObjnum@8=_IDMFC_ConvertLocalToServerObjnum@8 + _IDMFC_ConvertServerToLocalObjnum@8 + IDMFC_ConvertServerToLocalObjnum@8=_IDMFC_ConvertServerToLocalObjnum@8 + _IDMFC_DatabaseReadA@16 + IDMFC_DatabaseReadA@16=_IDMFC_DatabaseReadA@16 + _IDMFC_DatabaseReadB@16 + IDMFC_DatabaseReadB@16=_IDMFC_DatabaseReadB@16 + _IDMFC_DatabaseReadC@12 + IDMFC_DatabaseReadC@12=_IDMFC_DatabaseReadC@12 + _IDMFC_DatabaseRegister@8 + IDMFC_DatabaseRegister@8=_IDMFC_DatabaseRegister@8 + _IDMFC_DatabaseWriteA@16 + IDMFC_DatabaseWriteA@16=_IDMFC_DatabaseWriteA@16 + _IDMFC_DatabaseWriteB@12 + IDMFC_DatabaseWriteB@12=_IDMFC_DatabaseWriteB@12 + _IDMFC_DecryptData@12 + IDMFC_DecryptData@12=_IDMFC_DecryptData@12 + _IDMFC_Delete@4 + IDMFC_Delete@4=_IDMFC_Delete@4 + _IDMFC_DestroyPointer@4 + IDMFC_DestroyPointer@4=_IDMFC_DestroyPointer@4 + _IDMFC_DisconnectMe@4 + IDMFC_DisconnectMe@4=_IDMFC_DisconnectMe@4 + _IDMFC_DisplayInputCommandHelp@8 + IDMFC_DisplayInputCommandHelp@8=_IDMFC_DisplayInputCommandHelp@8 + _IDMFC_DisplayNetGameInfo@12 + IDMFC_DisplayNetGameInfo@12=_IDMFC_DisplayNetGameInfo@12 + _IDMFC_DisplayOutrageLogo@4 + IDMFC_DisplayOutrageLogo@4=_IDMFC_DisplayOutrageLogo@4 + _IDMFC_DisplayPlayerInfo@12 + IDMFC_DisplayPlayerInfo@12=_IDMFC_DisplayPlayerInfo@12 + _IDMFC_DisplayingPlayerInfo@4 + IDMFC_DisplayingPlayerInfo@4=_IDMFC_DisplayingPlayerInfo@4 + _IDMFC_DoDamageToPlayer@20 + IDMFC_DoDamageToPlayer@20=_IDMFC_DoDamageToPlayer@20 + _IDMFC_DoRandomDeathMessage@16 + IDMFC_DoRandomDeathMessage@16=_IDMFC_DoRandomDeathMessage@16 + _IDMFC_DrawMenu@4 + IDMFC_DrawMenu@4=_IDMFC_DrawMenu@4 + _IDMFC_DuplicatePointer@4 + IDMFC_DuplicatePointer@4=_IDMFC_DuplicatePointer@4 + _IDMFC_Dynamic_Cast@8 + IDMFC_Dynamic_Cast@8=_IDMFC_Dynamic_Cast@8 + _IDMFC_EnableAudioTaunts@8 + IDMFC_EnableAudioTaunts@8=_IDMFC_EnableAudioTaunts@8 + _IDMFC_EnableAutoSaveDisconnect@8 + IDMFC_EnableAutoSaveDisconnect@8=_IDMFC_EnableAutoSaveDisconnect@8 + _IDMFC_EnableAutoSaveLevelEnd@8 + IDMFC_EnableAutoSaveLevelEnd@8=_IDMFC_EnableAutoSaveLevelEnd@8 + _IDMFC_EnableOnScreenMenu@8 + IDMFC_EnableOnScreenMenu@8=_IDMFC_EnableOnScreenMenu@8 + _IDMFC_EnableOnScreenMenuBackground@8 + IDMFC_EnableOnScreenMenuBackground@8=_IDMFC_EnableOnScreenMenuBackground@8 + _IDMFC_EnableShipLogos@8 + IDMFC_EnableShipLogos@8=_IDMFC_EnableShipLogos@8 + _IDMFC_EnableStatisticalMessages@8 + IDMFC_EnableStatisticalMessages@8=_IDMFC_EnableStatisticalMessages@8 + _IDMFC_EncryptData@12 + IDMFC_EncryptData@12=_IDMFC_EncryptData@12 + _IDMFC_EndLevel@4 + IDMFC_EndLevel@4=_IDMFC_EndLevel@4 + _IDMFC_FindPInfoStatClose@4 + IDMFC_FindPInfoStatClose@4=_IDMFC_FindPInfoStatClose@4 + _IDMFC_FindPInfoStatFirst@12 + IDMFC_FindPInfoStatFirst@12=_IDMFC_FindPInfoStatFirst@12 + _IDMFC_FindPInfoStatNext@8 + IDMFC_FindPInfoStatNext@8=_IDMFC_FindPInfoStatNext@8 + _IDMFC_FreeHostsLists@4 + IDMFC_FreeHostsLists@4=_IDMFC_FreeHostsLists@4 + _IDMFC_GameClose@4 + IDMFC_GameClose@4=_IDMFC_GameClose@4 + _IDMFC_GameInit@8 + IDMFC_GameInit@8=_IDMFC_GameInit@8 + _IDMFC_GenerateStatFilename@16 + IDMFC_GenerateStatFilename@16=_IDMFC_GenerateStatFilename@16 + _IDMFC_GetBannedPlayerCallsign@8 + IDMFC_GetBannedPlayerCallsign@8=_IDMFC_GetBannedPlayerCallsign@8 + _IDMFC_GetCameraViewType@8 + IDMFC_GetCameraViewType@8=_IDMFC_GetCameraViewType@8 + _IDMFC_GetChangeTeamPacket@8 + IDMFC_GetChangeTeamPacket@8=_IDMFC_GetChangeTeamPacket@8 + _IDMFC_GetCounterMeasureOwner@8 + IDMFC_GetCounterMeasureOwner@8=_IDMFC_GetCounterMeasureOwner@8 + _IDMFC_GetCurrentMission@4 + IDMFC_GetCurrentMission@4=_IDMFC_GetCurrentMission@4 + _IDMFC_GetDLLInfoCallData@4 + IDMFC_GetDLLInfoCallData@4=_IDMFC_GetDLLInfoCallData@4 + _IDMFC_GetDMFCGameInfo@8 + IDMFC_GetDMFCGameInfo@8=_IDMFC_GetDMFCGameInfo@8 + _IDMFC_GetFrametime@4 + IDMFC_GetFrametime@4=_IDMFC_GetFrametime@4 + _IDMFC_GetGameFontTranslateArray@4 + IDMFC_GetGameFontTranslateArray@4=_IDMFC_GetGameFontTranslateArray@4 + _IDMFC_GetGamePolyModels@4 + IDMFC_GetGamePolyModels@4=_IDMFC_GetGamePolyModels@4 + _IDMFC_GetGameStateRequest@8 + IDMFC_GetGameStateRequest@8=_IDMFC_GetGameStateRequest@8 + _IDMFC_GetGameTextures@4 + IDMFC_GetGameTextures@4=_IDMFC_GetGameTextures@4 + _IDMFC_GetGameVClips@4 + IDMFC_GetGameVClips@4=_IDMFC_GetGameVClips@4 + _IDMFC_GetGameWindowH@4 + IDMFC_GetGameWindowH@4=_IDMFC_GetGameWindowH@4 + _IDMFC_GetGameWindowW@4 + IDMFC_GetGameWindowW@4=_IDMFC_GetGameWindowW@4 + _IDMFC_GetGameWindowX@4 + IDMFC_GetGameWindowX@4=_IDMFC_GetGameWindowX@4 + _IDMFC_GetGameWindowY@4 + IDMFC_GetGameWindowY@4=_IDMFC_GetGameWindowY@4 + _IDMFC_GetGametime@4 + IDMFC_GetGametime@4=_IDMFC_GetGametime@4 + _IDMFC_GetHighestRoomIndex@4 + IDMFC_GetHighestRoomIndex@4=_IDMFC_GetHighestRoomIndex@4 + _IDMFC_GetHudAspectX@4 + IDMFC_GetHudAspectX@4=_IDMFC_GetHudAspectX@4 + _IDMFC_GetHudAspectY@4 + IDMFC_GetHudAspectY@4=_IDMFC_GetHudAspectY@4 + _IDMFC_GetItObjNum@4 + IDMFC_GetItObjNum@4=_IDMFC_GetItObjNum@4 + _IDMFC_GetLastGameControls@4 + IDMFC_GetLastGameControls@4=_IDMFC_GetLastGameControls@4 + _IDMFC_GetLevelInfo@4 + IDMFC_GetLevelInfo@4=_IDMFC_GetLevelInfo@4 + _IDMFC_GetLocalD3Dir@4 + IDMFC_GetLocalD3Dir@4=_IDMFC_GetLocalD3Dir@4 + _IDMFC_GetLocalRole@4 + IDMFC_GetLocalRole@4=_IDMFC_GetLocalRole@4 + _IDMFC_GetMeObjNum@4 + IDMFC_GetMeObjNum@4=_IDMFC_GetMeObjNum@4 + _IDMFC_GetMyTeam@4 + IDMFC_GetMyTeam@4=_IDMFC_GetMyTeam@4 + _IDMFC_GetNetPlayers@4 + IDMFC_GetNetPlayers@4=_IDMFC_GetNetPlayers@4 + _IDMFC_GetNetgameInfo@4 + IDMFC_GetNetgameInfo@4=_IDMFC_GetNetgameInfo@4 + _IDMFC_GetNumBannedPlayers@4 + IDMFC_GetNumBannedPlayers@4=_IDMFC_GetNumBannedPlayers@4 + _IDMFC_GetNumTeams@4 + IDMFC_GetNumTeams@4=_IDMFC_GetNumTeams@4 + _IDMFC_GetObjectInfo@8 + IDMFC_GetObjectInfo@8=_IDMFC_GetObjectInfo@8 + _IDMFC_GetObjects@4 + IDMFC_GetObjects@4=_IDMFC_GetObjects@4 + _IDMFC_GetObserverModeBitmap@4 + IDMFC_GetObserverModeBitmap@4=_IDMFC_GetObserverModeBitmap@4 + _IDMFC_GetOnScreenMenu@4 + IDMFC_GetOnScreenMenu@4=_IDMFC_GetOnScreenMenu@4 + _IDMFC_GetOsirisModuleData@4 + IDMFC_GetOsirisModuleData@4=_IDMFC_GetOsirisModuleData@4 + _IDMFC_GetPilotPicBitmapHandles@4 + IDMFC_GetPilotPicBitmapHandles@4=_IDMFC_GetPilotPicBitmapHandles@4 + _IDMFC_GetPlayerColors@4 + IDMFC_GetPlayerColors@4=_IDMFC_GetPlayerColors@4 + _IDMFC_GetPlayerLogoBmp@12 + IDMFC_GetPlayerLogoBmp@12=_IDMFC_GetPlayerLogoBmp@12 + _IDMFC_GetPlayerNum@4 + IDMFC_GetPlayerNum@4=_IDMFC_GetPlayerNum@4 + _IDMFC_GetPlayerRecord@8 + IDMFC_GetPlayerRecord@8=_IDMFC_GetPlayerRecord@8 + _IDMFC_GetPlayerRecordByPnum@8 + IDMFC_GetPlayerRecordByPnum@8=_IDMFC_GetPlayerRecordByPnum@8 + _IDMFC_GetPlayerRecordData@8 + IDMFC_GetPlayerRecordData@8=_IDMFC_GetPlayerRecordData@8 + _IDMFC_GetPlayerTeam@8 + IDMFC_GetPlayerTeam@8=_IDMFC_GetPlayerTeam@8 + _IDMFC_GetPlayers@4 + IDMFC_GetPlayers@4=_IDMFC_GetPlayers@4 + _IDMFC_GetRenderZoom@4 + IDMFC_GetRenderZoom@4=_IDMFC_GetRenderZoom@4 + _IDMFC_GetRooms@4 + IDMFC_GetRooms@4=_IDMFC_GetRooms@4 + _IDMFC_GetScoreLimit@8 + IDMFC_GetScoreLimit@8=_IDMFC_GetScoreLimit@8 + _IDMFC_GetShieldDeltaArray@4 + IDMFC_GetShieldDeltaArray@4=_IDMFC_GetShieldDeltaArray@4 + _IDMFC_GetShips@4 + IDMFC_GetShips@4=_IDMFC_GetShips@4 + _IDMFC_GetSortedPlayerSlots@12 + IDMFC_GetSortedPlayerSlots@12=_IDMFC_GetSortedPlayerSlots@12 + _IDMFC_GetSortedPlayerSlotsByEfficiency@12 + IDMFC_GetSortedPlayerSlotsByEfficiency@12=_IDMFC_GetSortedPlayerSlotsByEfficiency@12 + _IDMFC_GetTeamAssignmentPacket@8 + IDMFC_GetTeamAssignmentPacket@8=_IDMFC_GetTeamAssignmentPacket@8 + _IDMFC_GetTeamColor@8 + IDMFC_GetTeamColor@8=_IDMFC_GetTeamColor@8 + _IDMFC_GetTeamForNewPlayer@12 + IDMFC_GetTeamForNewPlayer@12=_IDMFC_GetTeamForNewPlayer@12 + _IDMFC_GetTeamFromString@8 + IDMFC_GetTeamFromString@8=_IDMFC_GetTeamFromString@8 + _IDMFC_GetTeamString@8 + IDMFC_GetTeamString@8=_IDMFC_GetTeamString@8 + _IDMFC_GetTerrainSegs@4 + IDMFC_GetTerrainSegs@4=_IDMFC_GetTerrainSegs@4 + _IDMFC_GetTimeInGame@8 + IDMFC_GetTimeInGame@8=_IDMFC_GetTimeInGame@8 + _IDMFC_GetTimeLeft@8 + IDMFC_GetTimeLeft@8=_IDMFC_GetTimeLeft@8 + _IDMFC_GetTimeString@8 + IDMFC_GetTimeString@8=_IDMFC_GetTimeString@8 + _IDMFC_GetViewerObjectPtr@8 + IDMFC_GetViewerObjectPtr@8=_IDMFC_GetViewerObjectPtr@8 + _IDMFC_GetVisEffectArray@8 + IDMFC_GetVisEffectArray@8=_IDMFC_GetVisEffectArray@8 + _IDMFC_GetWeaponDeathMessage@12 + IDMFC_GetWeaponDeathMessage@12=_IDMFC_GetWeaponDeathMessage@12 + _IDMFC_GetWeapons@4 + IDMFC_GetWeapons@4=_IDMFC_GetWeapons@4 + _IDMFC_IAmDedicatedServer@4 + IDMFC_IAmDedicatedServer@4=_IDMFC_IAmDedicatedServer@4 + _IDMFC_IsAddressBanned@12 + IDMFC_IsAddressBanned@12=_IDMFC_IsAddressBanned@12 + _IDMFC_IsDisplayingNetGameInfo@4 + IDMFC_IsDisplayingNetGameInfo@4=_IDMFC_IsDisplayingNetGameInfo@4 + _IDMFC_IsMasterTrackerGame@4 + IDMFC_IsMasterTrackerGame@4=_IDMFC_IsMasterTrackerGame@4 + _IDMFC_IsMenuUp@4 + IDMFC_IsMenuUp@4=_IDMFC_IsMenuUp@4 + _IDMFC_IsPlayerAlive@8 + IDMFC_IsPlayerAlive@8=_IDMFC_IsPlayerAlive@8 + _IDMFC_IsPlayerBanned@8 + IDMFC_IsPlayerBanned@8=_IDMFC_IsPlayerBanned@8 + _IDMFC_IsPlayerDedicatedServer@8 + IDMFC_IsPlayerDedicatedServer@8=_IDMFC_IsPlayerDedicatedServer@8 + _IDMFC_IsPlayerDedicatedServerPnum@8 + IDMFC_IsPlayerDedicatedServerPnum@8=_IDMFC_IsPlayerDedicatedServerPnum@8 + _IDMFC_IsPlayerObserver@8 + IDMFC_IsPlayerObserver@8=_IDMFC_IsPlayerObserver@8 + _IDMFC_KillAllTimers@4 + IDMFC_KillAllTimers@4=_IDMFC_KillAllTimers@4 + _IDMFC_KillTimer@8 + IDMFC_KillTimer@8=_IDMFC_KillTimer@8 + _IDMFC_LoadFunctions@8 + IDMFC_LoadFunctions@8=_IDMFC_LoadFunctions@8 + _IDMFC_OnAllowObserverChange@8 + IDMFC_OnAllowObserverChange@8=_IDMFC_OnAllowObserverChange@8 + _IDMFC_OnCanChangeTeam@12 + IDMFC_OnCanChangeTeam@12=_IDMFC_OnCanChangeTeam@12 + _IDMFC_OnClientGameCreated@4 + IDMFC_OnClientGameCreated@4=_IDMFC_OnClientGameCreated@4 + _IDMFC_OnClientLevelChange@4 + IDMFC_OnClientLevelChange@4=_IDMFC_OnClientLevelChange@4 + _IDMFC_OnClientLevelEnd@4 + IDMFC_OnClientLevelEnd@4=_IDMFC_OnClientLevelEnd@4 + _IDMFC_OnClientLevelStart@4 + IDMFC_OnClientLevelStart@4=_IDMFC_OnClientLevelStart@4 + _IDMFC_OnClientObjectChangeSegment@16 + IDMFC_OnClientObjectChangeSegment@16=_IDMFC_OnClientObjectChangeSegment@16 + _IDMFC_OnClientObjectDestroyed@8 + IDMFC_OnClientObjectDestroyed@8=_IDMFC_OnClientObjectDestroyed@8 + _IDMFC_OnClientObjectKilled@12 + IDMFC_OnClientObjectKilled@12=_IDMFC_OnClientObjectKilled@12 + _IDMFC_OnClientObjectShieldsChanged@12 + IDMFC_OnClientObjectShieldsChanged@12=_IDMFC_OnClientObjectShieldsChanged@12 + _IDMFC_OnClientPlayerChangeSegment@16 + IDMFC_OnClientPlayerChangeSegment@16=_IDMFC_OnClientPlayerChangeSegment@16 + _IDMFC_OnClientPlayerDisconnect@8 + IDMFC_OnClientPlayerDisconnect@8=_IDMFC_OnClientPlayerDisconnect@8 + _IDMFC_OnClientPlayerEntersGame@8 + IDMFC_OnClientPlayerEntersGame@8=_IDMFC_OnClientPlayerEntersGame@8 + _IDMFC_OnClientPlayerExploded@8 + IDMFC_OnClientPlayerExploded@8=_IDMFC_OnClientPlayerExploded@8 + _IDMFC_OnClientPlayerKilled@12 + IDMFC_OnClientPlayerKilled@12=_IDMFC_OnClientPlayerKilled@12 + _IDMFC_OnClientShowUI@12 + IDMFC_OnClientShowUI@12=_IDMFC_OnClientShowUI@12 + _IDMFC_OnClientWallCollide@32 + IDMFC_OnClientWallCollide@32=_IDMFC_OnClientWallCollide@32 + _IDMFC_OnControlMessage@12 + IDMFC_OnControlMessage@12=_IDMFC_OnControlMessage@12 + _IDMFC_OnDisconnectSaveStatsToFile@4 + IDMFC_OnDisconnectSaveStatsToFile@4=_IDMFC_OnDisconnectSaveStatsToFile@4 + _IDMFC_OnDoControls@8 + IDMFC_OnDoControls@8=_IDMFC_OnDoControls@8 + _IDMFC_OnGameStateRequest@8 + IDMFC_OnGameStateRequest@8=_IDMFC_OnGameStateRequest@8 + _IDMFC_OnGetHudCallSignColor@8 + IDMFC_OnGetHudCallSignColor@8=_IDMFC_OnGetHudCallSignColor@8 + _IDMFC_OnHUDInterval@4 + IDMFC_OnHUDInterval@4=_IDMFC_OnHUDInterval@4 + _IDMFC_OnInputString@8 + IDMFC_OnInputString@8=_IDMFC_OnInputString@8 + _IDMFC_OnInterval@4 + IDMFC_OnInterval@4=_IDMFC_OnInterval@4 + _IDMFC_OnKeypress@8 + IDMFC_OnKeypress@8=_IDMFC_OnKeypress@8 + _IDMFC_OnLevelEndSaveStatsToFile@4 + IDMFC_OnLevelEndSaveStatsToFile@4=_IDMFC_OnLevelEndSaveStatsToFile@4 + _IDMFC_OnMeDisconnectFromServer@4 + IDMFC_OnMeDisconnectFromServer@4=_IDMFC_OnMeDisconnectFromServer@4 + _IDMFC_OnPLRInit@4 + IDMFC_OnPLRInit@4=_IDMFC_OnPLRInit@4 + _IDMFC_OnPLRInterval@4 + IDMFC_OnPLRInterval@4=_IDMFC_OnPLRInterval@4 + _IDMFC_OnPlayAudioTaunt@8 + IDMFC_OnPlayAudioTaunt@8=_IDMFC_OnPlayAudioTaunt@8 + _IDMFC_OnPlayerChangeTeam@20 + IDMFC_OnPlayerChangeTeam@20=_IDMFC_OnPlayerChangeTeam@20 + _IDMFC_OnPlayerConnect@8 + IDMFC_OnPlayerConnect@8=_IDMFC_OnPlayerConnect@8 + _IDMFC_OnPlayerEntersObserver@12 + IDMFC_OnPlayerEntersObserver@12=_IDMFC_OnPlayerEntersObserver@12 + _IDMFC_OnPlayerExitsObserver@8 + IDMFC_OnPlayerExitsObserver@8=_IDMFC_OnPlayerExitsObserver@8 + _IDMFC_OnPlayerReconnect@8 + IDMFC_OnPlayerReconnect@8=_IDMFC_OnPlayerReconnect@8 + _IDMFC_OnPrintScores@8 + IDMFC_OnPrintScores@8=_IDMFC_OnPrintScores@8 + _IDMFC_OnSaveStatsToFile@4 + IDMFC_OnSaveStatsToFile@4=_IDMFC_OnSaveStatsToFile@4 + _IDMFC_OnServerGameCreated@4 + IDMFC_OnServerGameCreated@4=_IDMFC_OnServerGameCreated@4 + _IDMFC_OnServerIsAddressBanned@12 + IDMFC_OnServerIsAddressBanned@12=_IDMFC_OnServerIsAddressBanned@12 + _IDMFC_OnServerLevelChange@4 + IDMFC_OnServerLevelChange@4=_IDMFC_OnServerLevelChange@4 + _IDMFC_OnServerLevelEnd@4 + IDMFC_OnServerLevelEnd@4=_IDMFC_OnServerLevelEnd@4 + _IDMFC_OnServerLevelStart@4 + IDMFC_OnServerLevelStart@4=_IDMFC_OnServerLevelStart@4 + _IDMFC_OnServerObjectChangeSegment@16 + IDMFC_OnServerObjectChangeSegment@16=_IDMFC_OnServerObjectChangeSegment@16 + _IDMFC_OnServerObjectDestroyed@8 + IDMFC_OnServerObjectDestroyed@8=_IDMFC_OnServerObjectDestroyed@8 + _IDMFC_OnServerObjectKilled@12 + IDMFC_OnServerObjectKilled@12=_IDMFC_OnServerObjectKilled@12 + _IDMFC_OnServerObjectShieldsChanged@12 + IDMFC_OnServerObjectShieldsChanged@12=_IDMFC_OnServerObjectShieldsChanged@12 + _IDMFC_OnServerPlayerChangeSegment@16 + IDMFC_OnServerPlayerChangeSegment@16=_IDMFC_OnServerPlayerChangeSegment@16 + _IDMFC_OnServerPlayerDisconnect@8 + IDMFC_OnServerPlayerDisconnect@8=_IDMFC_OnServerPlayerDisconnect@8 + _IDMFC_OnServerPlayerEntersGame@8 + IDMFC_OnServerPlayerEntersGame@8=_IDMFC_OnServerPlayerEntersGame@8 + _IDMFC_OnServerPlayerExploded@8 + IDMFC_OnServerPlayerExploded@8=_IDMFC_OnServerPlayerExploded@8 + _IDMFC_OnServerPlayerKilled@12 + IDMFC_OnServerPlayerKilled@12=_IDMFC_OnServerPlayerKilled@12 + _IDMFC_OnServerWallCollide@32 + IDMFC_OnServerWallCollide@32=_IDMFC_OnServerWallCollide@32 + _IDMFC_OnSpecialPacket@4 + IDMFC_OnSpecialPacket@4=_IDMFC_OnSpecialPacket@4 + _IDMFC_OnTeamChangeName@16 + IDMFC_OnTeamChangeName@16=_IDMFC_OnTeamChangeName@16 + _IDMFC_OnWeaponFired@12 + IDMFC_OnWeaponFired@12=_IDMFC_OnWeaponFired@12 + _IDMFC_PacketCheckPlayerNum@8 + IDMFC_PacketCheckPlayerNum@8=_IDMFC_PacketCheckPlayerNum@8 + _IDMFC_ReadInHostsAllowDeny@4 + IDMFC_ReadInHostsAllowDeny@4=_IDMFC_ReadInHostsAllowDeny@4 + _IDMFC_ReceiveControlMessage@8 + IDMFC_ReceiveControlMessage@8=_IDMFC_ReceiveControlMessage@8 + _IDMFC_ReceiveRequestForPlayerRecords@8 + IDMFC_ReceiveRequestForPlayerRecords@8=_IDMFC_ReceiveRequestForPlayerRecords@8 + _IDMFC_RegisterPacketReceiver@12 + IDMFC_RegisterPacketReceiver@12=_IDMFC_RegisterPacketReceiver@12 + _IDMFC_RehashAllowDeny@4 + IDMFC_RehashAllowDeny@4=_IDMFC_RehashAllowDeny@4 + _IDMFC_RemoveAllBans@4 + IDMFC_RemoveAllBans@4=_IDMFC_RemoveAllBans@4 + _IDMFC_RemoveBan@8 + IDMFC_RemoveBan@8=_IDMFC_RemoveBan@8 + _IDMFC_RequestGameState@4 + IDMFC_RequestGameState@4=_IDMFC_RequestGameState@4 + _IDMFC_RequestTeamChange@16 + IDMFC_RequestTeamChange@16=_IDMFC_RequestTeamChange@16 + _IDMFC_ResetPInfo@4 + IDMFC_ResetPInfo@4=_IDMFC_ResetPInfo@4 + _IDMFC_RespawnPlayer@16 + IDMFC_RespawnPlayer@16=_IDMFC_RespawnPlayer@16 + _IDMFC_SelectNextCameraView@8 + IDMFC_SelectNextCameraView@8=_IDMFC_SelectNextCameraView@8 + _IDMFC_SendChangeTeamRequest@12 + IDMFC_SendChangeTeamRequest@12=_IDMFC_SendChangeTeamRequest@12 + _IDMFC_SendControlMessageToPlayer@12 + IDMFC_SendControlMessageToPlayer@12=_IDMFC_SendControlMessageToPlayer@12 + _IDMFC_SendDMFCGameInfo@8 + IDMFC_SendDMFCGameInfo@8=_IDMFC_SendDMFCGameInfo@8 + _IDMFC_SendNetGameInfoSync@8 + IDMFC_SendNetGameInfoSync@8=_IDMFC_SendNetGameInfoSync@8 + _IDMFC_SendPacket@16 + IDMFC_SendPacket@16=_IDMFC_SendPacket@16 + _IDMFC_SendRequestForPlayerRecords@4 + IDMFC_SendRequestForPlayerRecords@4=_IDMFC_SendRequestForPlayerRecords@4 + _IDMFC_SendTeamAssignment@16 + IDMFC_SendTeamAssignment@16=_IDMFC_SendTeamAssignment@16 + _IDMFC_SetDeathMessageFilter@8 + IDMFC_SetDeathMessageFilter@8=_IDMFC_SetDeathMessageFilter@8 + _IDMFC_SetMaxPlayerHardLimit@8 + IDMFC_SetMaxPlayerHardLimit@8=_IDMFC_SetMaxPlayerHardLimit@8 + _IDMFC_SetNumberOfTeams@8 + IDMFC_SetNumberOfTeams@8=_IDMFC_SetNumberOfTeams@8 + _IDMFC_SetTeamName@16 + IDMFC_SetTeamName@16=_IDMFC_SetTeamName@16 + _IDMFC_SetTimerInterval@20 + IDMFC_SetTimerInterval@20=_IDMFC_SetTimerInterval@20 + _IDMFC_SetViewerObjectPtr@8 + IDMFC_SetViewerObjectPtr@8=_IDMFC_SetViewerObjectPtr@8 + _IDMFC_SetWeaponDeathMessage@16 + IDMFC_SetWeaponDeathMessage@16=_IDMFC_SetWeaponDeathMessage@16 + _IDMFC_Set_OnAllowObserverChange@8 + IDMFC_Set_OnAllowObserverChange@8=_IDMFC_Set_OnAllowObserverChange@8 + _IDMFC_Set_OnCanChangeTeam@8 + IDMFC_Set_OnCanChangeTeam@8=_IDMFC_Set_OnCanChangeTeam@8 + _IDMFC_Set_OnClientCollideA@8 + IDMFC_Set_OnClientCollideA@8=_IDMFC_Set_OnClientCollideA@8 + _IDMFC_Set_OnClientCollideB@8 + IDMFC_Set_OnClientCollideB@8=_IDMFC_Set_OnClientCollideB@8 + _IDMFC_Set_OnClientGameCreated@8 + IDMFC_Set_OnClientGameCreated@8=_IDMFC_Set_OnClientGameCreated@8 + _IDMFC_Set_OnClientLevelChange@8 + IDMFC_Set_OnClientLevelChange@8=_IDMFC_Set_OnClientLevelChange@8 + _IDMFC_Set_OnClientLevelEnd@8 + IDMFC_Set_OnClientLevelEnd@8=_IDMFC_Set_OnClientLevelEnd@8 + _IDMFC_Set_OnClientLevelStart@8 + IDMFC_Set_OnClientLevelStart@8=_IDMFC_Set_OnClientLevelStart@8 + _IDMFC_Set_OnClientObjectChangeSegment@8 + IDMFC_Set_OnClientObjectChangeSegment@8=_IDMFC_Set_OnClientObjectChangeSegment@8 + _IDMFC_Set_OnClientObjectDestroyed@8 + IDMFC_Set_OnClientObjectDestroyed@8=_IDMFC_Set_OnClientObjectDestroyed@8 + _IDMFC_Set_OnClientObjectKilled@8 + IDMFC_Set_OnClientObjectKilled@8=_IDMFC_Set_OnClientObjectKilled@8 + _IDMFC_Set_OnClientObjectShieldsChanged@8 + IDMFC_Set_OnClientObjectShieldsChanged@8=_IDMFC_Set_OnClientObjectShieldsChanged@8 + _IDMFC_Set_OnClientPlayerChangeSegment@8 + IDMFC_Set_OnClientPlayerChangeSegment@8=_IDMFC_Set_OnClientPlayerChangeSegment@8 + _IDMFC_Set_OnClientPlayerDisconnect@8 + IDMFC_Set_OnClientPlayerDisconnect@8=_IDMFC_Set_OnClientPlayerDisconnect@8 + _IDMFC_Set_OnClientPlayerEntersGame@8 + IDMFC_Set_OnClientPlayerEntersGame@8=_IDMFC_Set_OnClientPlayerEntersGame@8 + _IDMFC_Set_OnClientPlayerExploded@8 + IDMFC_Set_OnClientPlayerExploded@8=_IDMFC_Set_OnClientPlayerExploded@8 + _IDMFC_Set_OnClientPlayerKilled@8 + IDMFC_Set_OnClientPlayerKilled@8=_IDMFC_Set_OnClientPlayerKilled@8 + _IDMFC_Set_OnClientShowUI@8 + IDMFC_Set_OnClientShowUI@8=_IDMFC_Set_OnClientShowUI@8 + _IDMFC_Set_OnClientWallCollide@8 + IDMFC_Set_OnClientWallCollide@8=_IDMFC_Set_OnClientWallCollide@8 + _IDMFC_Set_OnControlMessage@8 + IDMFC_Set_OnControlMessage@8=_IDMFC_Set_OnControlMessage@8 + _IDMFC_Set_OnDisconnectSaveStatsToFile@8 + IDMFC_Set_OnDisconnectSaveStatsToFile@8=_IDMFC_Set_OnDisconnectSaveStatsToFile@8 + _IDMFC_Set_OnDoControls@8 + IDMFC_Set_OnDoControls@8=_IDMFC_Set_OnDoControls@8 + _IDMFC_Set_OnGameStateRequest@8 + IDMFC_Set_OnGameStateRequest@8=_IDMFC_Set_OnGameStateRequest@8 + _IDMFC_Set_OnGetHudCallSignColor@8 + IDMFC_Set_OnGetHudCallSignColor@8=_IDMFC_Set_OnGetHudCallSignColor@8 + _IDMFC_Set_OnHUDInterval@8 + IDMFC_Set_OnHUDInterval@8=_IDMFC_Set_OnHUDInterval@8 + _IDMFC_Set_OnInputString@8 + IDMFC_Set_OnInputString@8=_IDMFC_Set_OnInputString@8 + _IDMFC_Set_OnInterval@8 + IDMFC_Set_OnInterval@8=_IDMFC_Set_OnInterval@8 + _IDMFC_Set_OnKeypress@8 + IDMFC_Set_OnKeypress@8=_IDMFC_Set_OnKeypress@8 + _IDMFC_Set_OnLevelEndSaveStatsToFile@8 + IDMFC_Set_OnLevelEndSaveStatsToFile@8=_IDMFC_Set_OnLevelEndSaveStatsToFile@8 + _IDMFC_Set_OnMeDisconnectFromServer@8 + IDMFC_Set_OnMeDisconnectFromServer@8=_IDMFC_Set_OnMeDisconnectFromServer@8 + _IDMFC_Set_OnPLRInit@8 + IDMFC_Set_OnPLRInit@8=_IDMFC_Set_OnPLRInit@8 + _IDMFC_Set_OnPLRInterval@8 + IDMFC_Set_OnPLRInterval@8=_IDMFC_Set_OnPLRInterval@8 + _IDMFC_Set_OnPlayAudioTaunt@8 + IDMFC_Set_OnPlayAudioTaunt@8=_IDMFC_Set_OnPlayAudioTaunt@8 + _IDMFC_Set_OnPlayerChangeTeam@8 + IDMFC_Set_OnPlayerChangeTeam@8=_IDMFC_Set_OnPlayerChangeTeam@8 + _IDMFC_Set_OnPlayerConnect@8 + IDMFC_Set_OnPlayerConnect@8=_IDMFC_Set_OnPlayerConnect@8 + _IDMFC_Set_OnPlayerEntersObserver@8 + IDMFC_Set_OnPlayerEntersObserver@8=_IDMFC_Set_OnPlayerEntersObserver@8 + _IDMFC_Set_OnPlayerExitsObserver@8 + IDMFC_Set_OnPlayerExitsObserver@8=_IDMFC_Set_OnPlayerExitsObserver@8 + _IDMFC_Set_OnPlayerReconnect@8 + IDMFC_Set_OnPlayerReconnect@8=_IDMFC_Set_OnPlayerReconnect@8 + _IDMFC_Set_OnPrintScores@8 + IDMFC_Set_OnPrintScores@8=_IDMFC_Set_OnPrintScores@8 + _IDMFC_Set_OnSaveStatsToFile@8 + IDMFC_Set_OnSaveStatsToFile@8=_IDMFC_Set_OnSaveStatsToFile@8 + _IDMFC_Set_OnServerCollideA@8 + IDMFC_Set_OnServerCollideA@8=_IDMFC_Set_OnServerCollideA@8 + _IDMFC_Set_OnServerCollideB@8 + IDMFC_Set_OnServerCollideB@8=_IDMFC_Set_OnServerCollideB@8 + _IDMFC_Set_OnServerGameCreated@8 + IDMFC_Set_OnServerGameCreated@8=_IDMFC_Set_OnServerGameCreated@8 + _IDMFC_Set_OnServerIsAddressBanned@8 + IDMFC_Set_OnServerIsAddressBanned@8=_IDMFC_Set_OnServerIsAddressBanned@8 + _IDMFC_Set_OnServerLevelChange@8 + IDMFC_Set_OnServerLevelChange@8=_IDMFC_Set_OnServerLevelChange@8 + _IDMFC_Set_OnServerLevelEnd@8 + IDMFC_Set_OnServerLevelEnd@8=_IDMFC_Set_OnServerLevelEnd@8 + _IDMFC_Set_OnServerLevelStart@8 + IDMFC_Set_OnServerLevelStart@8=_IDMFC_Set_OnServerLevelStart@8 + _IDMFC_Set_OnServerObjectChangeSegment@8 + IDMFC_Set_OnServerObjectChangeSegment@8=_IDMFC_Set_OnServerObjectChangeSegment@8 + _IDMFC_Set_OnServerObjectDestroyed@8 + IDMFC_Set_OnServerObjectDestroyed@8=_IDMFC_Set_OnServerObjectDestroyed@8 + _IDMFC_Set_OnServerObjectKilled@8 + IDMFC_Set_OnServerObjectKilled@8=_IDMFC_Set_OnServerObjectKilled@8 + _IDMFC_Set_OnServerObjectShieldsChanged@8 + IDMFC_Set_OnServerObjectShieldsChanged@8=_IDMFC_Set_OnServerObjectShieldsChanged@8 + _IDMFC_Set_OnServerPlayerChangeSegment@8 + IDMFC_Set_OnServerPlayerChangeSegment@8=_IDMFC_Set_OnServerPlayerChangeSegment@8 + _IDMFC_Set_OnServerPlayerDisconnect@8 + IDMFC_Set_OnServerPlayerDisconnect@8=_IDMFC_Set_OnServerPlayerDisconnect@8 + _IDMFC_Set_OnServerPlayerEntersGame@8 + IDMFC_Set_OnServerPlayerEntersGame@8=_IDMFC_Set_OnServerPlayerEntersGame@8 + _IDMFC_Set_OnServerPlayerExploded@8 + IDMFC_Set_OnServerPlayerExploded@8=_IDMFC_Set_OnServerPlayerExploded@8 + _IDMFC_Set_OnServerPlayerKilled@8 + IDMFC_Set_OnServerPlayerKilled@8=_IDMFC_Set_OnServerPlayerKilled@8 + _IDMFC_Set_OnServerWallCollide@8 + IDMFC_Set_OnServerWallCollide@8=_IDMFC_Set_OnServerWallCollide@8 + _IDMFC_Set_OnSpecialPacket@8 + IDMFC_Set_OnSpecialPacket@8=_IDMFC_Set_OnSpecialPacket@8 + _IDMFC_Set_OnTeamChangeName@8 + IDMFC_Set_OnTeamChangeName@8=_IDMFC_Set_OnTeamChangeName@8 + _IDMFC_Set_OnWeaponFired@8 + IDMFC_Set_OnWeaponFired@8=_IDMFC_Set_OnWeaponFired@8 + _IDMFC_SetupPlayerRecord@16 + IDMFC_SetupPlayerRecord@16=_IDMFC_SetupPlayerRecord@16 + _IDMFC_ShouldIDisplayHUDName@8 + IDMFC_ShouldIDisplayHUDName@8=_IDMFC_ShouldIDisplayHUDName@8 + _IDMFC_StartPacket@16 + IDMFC_StartPacket@16=_IDMFC_StartPacket@16 + _IDMFC_StartUIWindow@12 + IDMFC_StartUIWindow@12=_IDMFC_StartUIWindow@12 + _IDMFC_SwitchAllowTeamChange@8 + IDMFC_SwitchAllowTeamChange@8=_IDMFC_SwitchAllowTeamChange@8 + _IDMFC_SwitchNetGameInfoDisplay@8 + IDMFC_SwitchNetGameInfoDisplay@8=_IDMFC_SwitchNetGameInfoDisplay@8 + _IDMFC_SwitchPlayerInfoDisplay@8 + IDMFC_SwitchPlayerInfoDisplay@8=_IDMFC_SwitchPlayerInfoDisplay@8 + _IDMFC_SwitchServerHudCallsignLevel@8 + IDMFC_SwitchServerHudCallsignLevel@8=_IDMFC_SwitchServerHudCallsignLevel@8 + _IDMFC_SwitchShowHudCallsignLevel@12 + IDMFC_SwitchShowHudCallsignLevel@12=_IDMFC_SwitchShowHudCallsignLevel@12 + _IDMFC_TranslateEvent@12 + IDMFC_TranslateEvent@12=_IDMFC_TranslateEvent@12 + _IDMFC_UpdatePInfo@16 + IDMFC_UpdatePInfo@16=_IDMFC_UpdatePInfo@16 + _IDMFC_VersionCheck@8 + IDMFC_VersionCheck@8=_IDMFC_VersionCheck@8 + _IDMFC_WarpToLevel@8 + IDMFC_WarpToLevel@8=_IDMFC_WarpToLevel@8 + _IDMFC_WasPlayerInGameAtLevelEnd@8 + IDMFC_WasPlayerInGameAtLevelEnd@8=_IDMFC_WasPlayerInGameAtLevelEnd@8 + _IDMFC_WriteDMFCStatsToFile@8 + IDMFC_WriteDMFCStatsToFile@8=_IDMFC_WriteDMFCStatsToFile@8 + _IDmfcStats_CanScrollDown@4 + IDmfcStats_CanScrollDown@4=_IDmfcStats_CanScrollDown@4 + _IDmfcStats_CanScrollUp@4 + IDmfcStats_CanScrollUp@4=_IDmfcStats_CanScrollUp@4 + _IDmfcStats_Delete@4 + IDmfcStats_Delete@4=_IDmfcStats_Delete@4 + _IDmfcStats_DestroyPointer@4 + IDmfcStats_DestroyPointer@4=_IDmfcStats_DestroyPointer@4 + _IDmfcStats_DoFrame@4 + IDmfcStats_DoFrame@4=_IDmfcStats_DoFrame@4 + _IDmfcStats_DuplicatePointer@4 + IDmfcStats_DuplicatePointer@4=_IDmfcStats_DuplicatePointer@4 + _IDmfcStats_Dynamic_Cast@8 + IDmfcStats_Dynamic_Cast@8=_IDmfcStats_Dynamic_Cast@8 + _IDmfcStats_Enable@8 + IDmfcStats_Enable@8=_IDmfcStats_Enable@8 + _IDmfcStats_Initialize@8 + IDmfcStats_Initialize@8=_IDmfcStats_Initialize@8 + _IDmfcStats_IsEnabled@4 + IDmfcStats_IsEnabled@4=_IDmfcStats_IsEnabled@4 + _IDmfcStats_ScrollDown@4 + IDmfcStats_ScrollDown@4=_IDmfcStats_ScrollDown@4 + _IDmfcStats_ScrollUp@4 + IDmfcStats_ScrollUp@4=_IDmfcStats_ScrollUp@4 + _IMenuItem_AddSubMenu@8 + IMenuItem_AddSubMenu@8=_IMenuItem_AddSubMenu@8 + _IMenuItem_Back@4 + IMenuItem_Back@4=_IMenuItem_Back@4 + _IMenuItem_CallFunc@8 + IMenuItem_CallFunc@8=_IMenuItem_CallFunc@8 + _IMenuItem_Delete@4 + IMenuItem_Delete@4=_IMenuItem_Delete@4 + _IMenuItem_DestroyPointer@4 + IMenuItem_DestroyPointer@4=_IMenuItem_DestroyPointer@4 + _IMenuItem_DetachSubMenu@8 + IMenuItem_DetachSubMenu@8=_IMenuItem_DetachSubMenu@8 + _IMenuItem_Down@4 + IMenuItem_Down@4=_IMenuItem_Down@4 + _IMenuItem_Draw@24 + IMenuItem_Draw@24=_IMenuItem_Draw@24 + _IMenuItem_DuplicatePointer@4 + IMenuItem_DuplicatePointer@4=_IMenuItem_DuplicatePointer@4 + _IMenuItem_Dynamic_Cast@8 + IMenuItem_Dynamic_Cast@8=_IMenuItem_Dynamic_Cast@8 + _IMenuItem_Execute@4 + IMenuItem_Execute@4=_IMenuItem_Execute@4 + _IMenuItem_Forward@4 + IMenuItem_Forward@4=_IMenuItem_Forward@4 + _IMenuItem_GetCustomSubMenuCount@4 + IMenuItem_GetCustomSubMenuCount@4=_IMenuItem_GetCustomSubMenuCount@4 + _IMenuItem_GetFocus@4 + IMenuItem_GetFocus@4=_IMenuItem_GetFocus@4 + _IMenuItem_GetTitle@4 + IMenuItem_GetTitle@4=_IMenuItem_GetTitle@4 + _IMenuItem_GetType@4 + IMenuItem_GetType@4=_IMenuItem_GetType@4 + _IMenuItem_HasSubMenus@4 + IMenuItem_HasSubMenus@4=_IMenuItem_HasSubMenus@4 + _IMenuItem_LoseInputFocus@4 + IMenuItem_LoseInputFocus@4=_IMenuItem_LoseInputFocus@4 + _IMenuItem_SetInputFocus@4 + IMenuItem_SetInputFocus@4=_IMenuItem_SetInputFocus@4 + _IMenuItem_SetState@8 + IMenuItem_SetState@8=_IMenuItem_SetState@8 + _IMenuItem_SetStateItemList@12 + IMenuItem_SetStateItemList@12=_IMenuItem_SetStateItemList@12 + _IMenuItem_Up@4 + IMenuItem_Up@4=_IMenuItem_Up@4 diff --git a/netgames/dmfc/dmfcbase.cpp b/netgames/dmfc/dmfcbase.cpp new file mode 100644 index 000000000..ce9e520e6 --- /dev/null +++ b/netgames/dmfc/dmfcbase.cpp @@ -0,0 +1,5412 @@ +/* +* $Logfile: /DescentIII/Main/dmfc/dmfcbase.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:57:20 $ +* $Author: kevinb $ +* +* Base functions for DMFC implementation +* +* $Log: dmfcbase.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb +* initial 1.5 import +* + * + * 170 9/28/01 2:09p Matt + * Don't show ping/loss info on HUD if peer-to-peer game. + * + * 169 9/07/01 8:57a Matt + * Fixed hud name bug from previous change. + * + * 168 9/05/01 6:04p Matt + * Added code to save the user's preferred HUD name level setting even if + * the server bashes it down. + * + * 167 10/26/99 10:31a Jeff + * fixed COM interface bug + * + * 166 10/21/99 9:27p Jeff + * B.A. Macintosh code merge + * + * 165 8/21/99 12:33a Jeff + * Changed the name of the GetRealGametime function to + * GetRealGametimePacket since it was confusing some compilers with the + * other GetRealGametime function. + * + * 164 8/15/99 4:36p Jeff + * finished exporting all inventory class functions. export object_info + * array. added check for -nooutragelogo to display Outrage logo display. + * + * 163 8/11/99 6:36p Jeff + * don't set input command list root to NULL when creating input commands + * (it would create a memleak and ignore any ICs added by a custom mod) + * + * 162 8/11/99 1:21p Jeff + * exported needed functions for camera windows + * + * 161 7/30/99 11:59a Jeff + * fixed bug with autobalance being turned off + * + * 160 7/14/99 11:47a Jeff + * localized text for patch fixes + * + * 159 7/13/99 10:05a Jeff + * text taunt token decoding + * + * 158 7/11/99 3:31p Jeff + * exported game arguments, made command line option to specify + * autoexec.dmfc + * + * 157 7/09/99 7:02p Jeff + * put in countdown timer for when a level is about to end + * + * 156 7/09/99 6:17p Jeff + * added $remoteadminlogout and $wait commands + * + * 155 7/09/99 2:53p Jeff + * handle gametime better (pause it when needed) if the server is 'waiting + * for players' + * + * 154 7/08/99 6:25p Jeff + * remote admin in and working + * + * 153 7/08/99 2:39a Jeff + * rough implementation of remote administration checked in. Still needs + * some polishing, but should work basically. + * + * 152 7/07/99 5:00p Jeff + * removed vararg functions from interface functions, just made different + * versions of them + * + * 151 6/11/99 5:36p Jeff + * removed ai_info #ifdefs (better way of doing it) + * + * 150 6/10/99 12:34p Jeff + * GetCounterMeasureOwner doesn't use ->ai_info for non-Outrage versions + * + * 149 6/10/99 11:10a Jeff + * don't display the Outrage logo for non-Outrage games + * + * 148 5/22/99 1:12a Jeff + * correctly handle Viewer_object + * + * 147 5/20/99 5:32p Jeff + * called PlayerStopSounds if respawning a player + * + * 146 5/20/99 3:51p Jeff + * for apply damage to player, server says should be 1 (so it works in + * peer-peer) + * + * 145 5/13/99 4:55p Ardussi + * changes for compiling on the Mac + * + * 144 5/12/99 11:04p Jeff + * dmfc and multiplayer games now have endian friendly packets (*whew*) + * + * 143 5/10/99 2:43a Jeff + * handle new scheme of player's joining in a team game, where the team is + * set before player enters game in the main code, but the team is + * determined via event call to dmfc + * + * 142 5/09/99 6:20a Jeff + * improved Entropy (added sounds, max virii per room). Fixed rendering + * bugs for other multiplayer dlls. + * + * 141 5/08/99 11:06a Jeff + * + * 140 5/07/99 4:34p Jason + * fixed audio taunt icon + * + * 139 5/07/99 12:52p Jeff + * audio taunt icon is ppic if available. coop has hard max team set of 4 + * + * 138 5/04/99 8:46p Jeff + * display icon when someone plays an audio taunt + * + * 137 5/03/99 8:39a Jeff + * fixed apply damage to player + * + * 136 4/30/99 10:52p Jeff + * added $warp command + * + * 135 4/30/99 7:36p Jeff + * exported vis_effects to dmfc + * + * 134 4/27/99 1:56p Jeff + * audio taunts stuff in pilot menu, added stringtables + * + * 133 4/26/99 3:41p Jeff + * put in debug multiplayer command dump to file + * + * 132 4/25/99 7:18p Jeff + * added code to handle suicides in pinfo + * + * 131 4/23/99 6:15p Jeff + * fixed double calls to GameClose + * + * 130 4/23/99 4:49p Jason + * played with loss colors a bit + * + * 129 4/23/99 4:24p Jason + * fixed ping display bug + * + * 128 4/23/99 12:46p Jeff + * lag loss indicator on by default + * + * 127 4/22/99 11:28a Jeff + * changed percent sign as a result of Samir's change + * + * 126 4/21/99 10:35p Jeff + * fixed lag/loss displays + * + * 125 4/20/99 8:57p Jeff + * compile for Linux + * + * 124 4/14/99 3:03p Jeff + * + * 123 4/12/99 11:39a Jeff + * removed movement smoothing, added permissable cs to netgame info + * + * 122 4/04/99 4:55p Jeff + * added functionality to call osiris functions from multiplayer d3ms + * + * 121 4/03/99 4:06p Jeff + * added loss/ping gauge + * + * 120 4/02/99 9:02p Jeff + * fixed crashes if there was an error initializing module + * + * 119 3/30/99 9:01p Jeff + * exported polymodels + * + * 118 3/30/99 7:42p Jeff + * fixed a misspelling on a function name + * + * 117 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 116 3/22/99 1:55p Jeff + * make sure initialization happens (possible crashing) + * + * 115 3/17/99 12:23p Jeff + * converted DMFC to be COM interface + * + * 114 3/09/99 1:13p Jeff + * fixed control packet for wait being overflowed out and accidently sent + * by the client + * + * 113 3/05/99 1:29p Jeff + * fixed 99% of the high res issues + * + * 112 3/01/99 8:48p Jeff + * fixed banning bug (when not a master tracker game) + * + * 111 2/25/99 8:54p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 110 2/11/99 12:50a Jeff + * changed names of exported variables + * + * 109 2/10/99 1:47p Matt + * Changed object handle symbolic constants + * + * 108 2/09/99 3:32p Jeff + * table file parser takes quotes strings for force keywords + * + * 107 2/07/99 2:06a Jeff + * updated coop...fixed bug when getting countermeasure owner, if owner is + * observer + * + * 106 2/07/99 1:19a Jeff + * added new multiplayer game events EVT_GAMEOBJKILLED and + * EVT_GAMEOBJDESTROYED + * + * 105 2/05/99 8:24p Jeff + * added table file parser macros + * + * 104 2/03/99 4:09p Jeff + * moved function pointers to seperate file. created autoexec.dmfc + * + * 103 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 102 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 101 1/21/99 11:16p Jeff + * exported vecmat functions + * + * 100 1/20/99 8:06p Jeff + * added members into DLLinfo struct for game change segment events, pass + * them over on execute dll packets + * + * 99 1/19/99 5:34p Jeff + * updated monsterball + * + * 98 1/18/99 7:27p Jeff + * localized strings in dmfcbase.cpp + * + * 97 1/17/99 11:52p Jeff + * added some new events, and changed a couple event handlers + * + * 96 1/15/99 8:29p Jeff + * updates to powerball + * + * 95 1/15/99 7:52p Chris + * Updated ObjSetPos() to include a f_update_attach_children flag + * + * 94 1/15/99 3:53a Jeff + * exported a couple more functions. Added event handlers for Weapon + * collide event + * + * 93 1/12/99 2:55p Jeff + * added/finished the waiting for player's to join dialog + * + * 92 1/12/99 11:29a Jason + * fixed a broken externed function + * + * 91 1/07/99 5:01p Jeff + * added Int3's and updated all net games to use stats manager...correctly + * too + * + * 90 1/06/99 7:02p Jeff + * added a multiplayer event for game controls + * + * 89 1/06/99 12:53a Jeff + * put in support for $piggyback and $observer + * + * 88 1/04/99 8:11p Jason + * fixed packet loss tracking problem + * + * 87 1/04/99 2:19p Jeff + * exported table file management functions + * + * 86 1/04/99 12:21p Jeff + * added support for hosts.allow/deny and updates stats manager a little + * + * 85 12/13/98 5:32p Jeff + * fixed ugly crash due to freeing memory allocated in another heap + * + * 84 12/09/98 12:38p Jeff + * removed possible security bug displaying a player's ip address (now + * server only can see it) + * + * 83 12/08/98 4:47p Jeff + * umm, various changes, fixed pilot pics so that they work for everyone + * now + * + * 82 12/08/98 3:29p Jeff + * updated the team control dialog so the server can determine if they + * want to make the clients wait + * + * 81 12/08/98 12:17p Jeff + * various changes that include an improved Team Control dialog (doesn't + * switch teams until exit..) and spew/respawn players that change teams + * + * 80 12/04/98 7:04p Jeff + * almost finished up dmfc stat manager + * + * 79 12/01/98 6:56p Jeff + * put in quick and dirty implementation of pilot pics for testing + * + * 78 11/19/98 5:56p Jeff + * added slider exported and improved Hoard + * + * 77 11/17/98 6:29p Jeff + * mod can specify whether or not to display the team setup dialog on team + * game start. Added a menu item to display team setup dialog in mid-game + * + * 76 11/17/98 12:36p Jeff + * fixed dedicated server detection and display a dprintf for setting new + * team + * + * 75 11/16/98 5:35p Jeff + * removed log functions, added support for changing team names, fixed ctf + * to work better with different team names + * + * 74 11/13/98 6:36p Jeff + * created dmfc_dll (a DLL version of DMFC) and converted current mods to + * use it + * + * 73 11/12/98 12:16p Jeff + * more changes to handle (ignore) dedicated server + * + * 72 11/11/98 7:19p Jeff + * changes made so that a dedicated server's team is always -1 (team game + * or not) + * + * 71 11/02/98 4:38p Jeff + * added ability to sort and display by efficiency + * + * 70 11/01/98 1:59a Jeff + * made a $help inputcommand for help in a dedicated server environment + * + * 69 10/30/98 12:47p Jeff + * cut down a couple bytes on memory usage + * + * 68 10/29/98 7:01p Jeff + * creation of team placement dialog. Moved TranslateEvent into DMFC + * + * 67 10/24/98 2:35p Matt + * Changed "callsign" to "name" or "player name" in the multiplayer menus + * and commands. + * + * 66 10/24/98 2:18p Jeff + * + * 65 10/23/98 6:51p Jeff + * fixed hud num of teams sort in ctf, and memory overwrite in registry + * + * 64 10/23/98 11:22a Jeff + * changes to handle mixcase, and display the client netgame info + * correctly + * + * 63 10/21/98 5:02p Jeff + * removed player from HUD if in observer nide + * + * 62 10/20/98 4:35p Jeff + * added a flag for menu to add a to MIT_PLIST... + * + * 61 10/20/98 12:16p Jeff + * added death message filter, hud callsign filter + * + * 60 10/19/98 7:19p Matt + * Added system to support different types of damage to the player and + * have these different types make different sounds. + * + * 59 10/18/98 7:59p Jeff + * functions added to dmfc for client->server objnum matching. Banning + * now uses tracker id when available. + * + * 58 10/17/98 7:30p Jeff + * network_address compares don't compare port on somethings + * + * 57 10/15/98 6:18p Jeff + * created the is player banned event, removed prejoin event + * + * 56 10/15/98 1:34p Jeff + * added scrollable onscreen menu. Remove ban in dmfc. prejoin event + * + * 55 10/13/98 6:01p Jeff + * added attaching + * + * 54 10/13/98 2:15a Jeff + * created new event for when a player leaves a multiplayer game. Fixed + * some 'english' bugs in the network games. + * + * 53 10/08/98 3:37p Jeff + * general improvements (Netgame info things, save to registry). Changes + * so it would send packets on NETSEQ_OBJECTS + * + * 51 10/05/98 2:49p Jeff + * + * 50 10/02/98 6:10p Jeff + * + * 49 10/01/98 11:30a Jeff + * made the observer mode events into just a client event + * + * 48 9/30/98 4:21p Jeff + * team changing is handled correctly + * + * 47 9/30/98 3:50p Jeff + * general improvements (many) + * + * 46 9/29/98 3:04p Jeff + * added time in game and start_time support + * + * 45 9/28/98 5:05p Jeff + * made the statisitical death messages an option in the menu + * + * 44 9/25/98 7:25p Jeff + * + * 43 9/25/98 4:50p Jeff + * + * 42 9/24/98 6:54p Jeff + * added DisconnectMe() and made use of it when you get kicked/banned + * + * 41 9/24/98 6:29p Jeff + * + * 40 9/24/98 5:52p Jeff + * starting adding statistical death messages, checked in for testing + * + * 39 9/23/98 5:27p Jeff + * fixed death message bug (using objnum instead of pnum) + * + * 38 9/23/98 4:17p Jeff + * basic changes/improvements, started changing death messages + * + * 37 9/21/98 7:11p Jeff + * made InputCommand interface API and moved existing input commands to + * the interface. Changed mprintf/ASSERT so they are valid in DMFC +* +* $NoKeywords: $ +*/ + +#include "gamedll_header.h" +#include "DMFC.h" +#include "dmfcinternal.h" +#include "dmfcinputcommands.h" + + +#include +#include + +char **DMFCStringTable; +int DMFCStringTableSize = 0; +char *_DMFCErrorString = "DMFC Missing String"; +ubyte seeds1[31] = {49,73,0,44,87,253,35,74,62,250,4,247,251,72,244,30,59,61,60,52,50,237,23,48,56,55,65,232,231,230,0}; +ubyte seeds2[6] = {70,95,103,102,112,0}; + + + +char *DMFCGetString(int d) +{ + if( (d<0) || (d>=DMFCStringTableSize) ) + return _DMFCErrorString; + else + return DMFCStringTable[d]; +} + +DMFCBase::DMFCBase(void) +{ + m_cPtrs = 0; + + m_iServerHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; + m_iMyCurrentHUDCallsignLevel = m_iMyPreferredHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; + m_iProtectedFlags = 0; + m_bHasInitialized = false; + LossGuageEnabled = true; + DMFCInit = false; + RealGametime = 0; + DedicatedLevelWait = 0; + m_bDisplayTimelimitCountdown = true; + m_iTimeLimitCountdown = 10; + + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_PLRFIRSTTIME|DMFC_PRF_CANTEAMCHANGE); + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVELEVELEND); + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTIMELIMIT|DMFC_PRF_AUTODEATHMSG|DMFC_PRF_AUTOTEAMSELECT|DMFC_PRF_DISPSTATHUDMSGS); + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_INOPTIONS|DMFC_PRF_DISPPLAYERINFO|DMFC_PRF_DISPNETGAMEINFO); + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVEDISC|DMFC_PRF_DISPMENUBACKGR|DMFC_PRF_PAUSETIME); + + m_iNumBanPlayers = 0; + m_bMakeClientsWait = false; + m_iPlayerDisplayed = 0; + + DatabaseRegisteredName[0] = '\0'; + + + SPRoot = NULL; //no special packet handlers yet + + int i; + + for(i=0;iBAD_BITMAP_HANDLE){ + DLLbm_FreeBitmap(PilotPicBmpHandles[i]); + PilotPicBmpHandles[i] = BAD_BITMAP_HANDLE; + } + } + + if(TauntIndicatorBMP>BAD_BITMAP_HANDLE){ + DLLbm_FreeBitmap(TauntIndicatorBMP); + TauntIndicatorBMP = BAD_BITMAP_HANDLE; + } + + for(i=0;inext; + free(current); + current=next; + } + SPRoot = NULL; +} + + +//##################Setup and processing functions####################### + +// DMFCBase::InitializeForLevel +// +// +void DMFCBase::InitializeForLevel(void) +{ + if(m_bHasInitialized) + return; + + //Reset the timers + InitTimers(); + + m_bHasInitialized = true; +} + +// DMFCBase::LoadSettings +// +// Loads the settings of DMFC from the registry and initializes those variables +void DMFCBase::LoadSettings(void) +{ + //set default values + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTIMELIMIT|DMFC_PRF_AUTODEATHMSG|DMFC_PRF_AUTOTEAMSELECT|DMFC_PRF_DISPSTATHUDMSGS); + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVELEVELEND|DMFC_PRF_CANTEAMCHANGE); + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVEDISC|DMFC_PRF_DISPMENUBACKGR); + m_iServerHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; + m_iMyCurrentHUDCallsignLevel = m_iMyPreferredHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; + m_iDeathMessageFilter = DM_FILTER_FULL; + LossGuageEnabled = true; + + bool bTemp; + + if(DatabaseRead3("DMFCAutoTimeLimit",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTIMELIMIT); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTIMELIMIT); + } + + if(DatabaseRead3("DMFCAutoDeathMessage",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTODEATHMSG); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTODEATHMSG); + } + + if(DatabaseRead3("DMFCAutoTeamSelect",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTEAMSELECT); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTEAMSELECT); + } + + if(DatabaseRead3("DMFCStatHUDMessages",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPSTATHUDMSGS); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPSTATHUDMSGS); + } + + if(DatabaseRead3("DMFCAutoSaveStatLevelEnd",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVELEVELEND); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVELEVELEND); + } + + if(DatabaseRead3("DMFCAutoSaveStatDisconnect",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVEDISC); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVEDISC); + } + + if(DatabaseRead3("DMFCMenuBackground",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPMENUBACKGR); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPMENUBACKGR); + } + + if(DatabaseRead3("DMFCCanChangeTeams",&bTemp)){ + if(bTemp) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_CANTEAMCHANGE); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_CANTEAMCHANGE); + } + + DatabaseRead3("DMFCLagLossIndicator",&LossGuageEnabled); + + + DatabaseRead2("DMFCShowHudCallsigns" ,&m_iMyPreferredHUDCallsignLevel,sizeof(m_iMyPreferredHUDCallsignLevel)); + DatabaseRead2("DMFCServerShowHudCallsigns" ,&m_iServerHUDCallsignLevel,sizeof(m_iServerHUDCallsignLevel)); + DatabaseRead2("DMFCDeathMessageFilter" ,&m_iDeathMessageFilter,sizeof(m_iDeathMessageFilter)); + + m_iMyCurrentHUDCallsignLevel = m_iMyPreferredHUDCallsignLevel; +} + +// DMFCBase::SaveSettings +// +// Saves out the settings of DMFC to the registry +void DMFCBase::SaveSettings(void) +{ + bool bTemp; + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_AUTOTIMELIMIT)!=0); + DatabaseWrite2("DMFCAutoTimeLimit" ,bTemp); + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_AUTODEATHMSG)!=0); + DatabaseWrite2("DMFCAutoDeathMessage" ,bTemp); + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_AUTOTEAMSELECT)!=0); + DatabaseWrite2("DMFCAutoTeamSelect" ,bTemp); + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_DISPSTATHUDMSGS)!=0); + DatabaseWrite2("DMFCStatHUDMessages" ,bTemp); + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_AUTOSAVELEVELEND)!=0); + DatabaseWrite2("DMFCAutoSaveStatLevelEnd" ,bTemp); + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_AUTOSAVEDISC)!=0); + DatabaseWrite2("DMFCAutoSaveStatDisconnect" ,bTemp); + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR)!=0); + DatabaseWrite2("DMFCMenuBackground" ,bTemp); + + bTemp = (bool)((m_iProtectedFlags&DMFC_PRF_CANTEAMCHANGE)!=0); + DatabaseWrite2("DMFCCanChangeTeams" ,bTemp); + + DatabaseWrite2("DMFCLagLossIndicator",LossGuageEnabled); + + DatabaseWrite2("DMFCShowHudCallsigns" ,m_iMyPreferredHUDCallsignLevel); + DatabaseWrite2("DMFCServerShowHudCallsigns" ,m_iServerHUDCallsignLevel); + DatabaseWrite2("DMFCDeathMessageFilter" ,m_iDeathMessageFilter); +} + +// DMFCBase::LoadFunctions +// +// Initialized all the functions for a multiplayer game...must be first thing called +#ifndef MACINTOSH +void DMFCBase::LoadFunctions(int *api_func) +{ + #include "dmfcdllinit.h" +} +#endif + +// DMFCBase::GameInit +// +// Sets up all the DLL functions and pointers and preps the class for use. This ABSOLUTLY must be +// called, so if you override DMFCApp::GameInit, make sure that you put a call to this somewhere in +// the override. +void DMFCBase::GameInit(int teams) +{ + ASSERT(!DMFCInit); + DMFCInit = true; + + LoadSettings(); + + DLLCreateStringTable("dmfc.str",&DMFCStringTable,&DMFCStringTableSize); + mprintf((0,"DMFC Note: %d strings loaded from string table\n",DMFCStringTableSize)); + + //initialize player records + PRec_Init(); + + //initialize remote administration + Remote_Initialize(); + + //see if we should display Outrage logo at all + if(DLLFindArg("-nooutragelogo")) + m_bDisplayOutrageLogo = false; + else + m_bDisplayOutrageLogo = true; + + SetNumberOfTeams(teams); + + SetTeamName(RED_TEAM,DTXT_RED,false); + SetTeamName(BLUE_TEAM,DTXT_BLUE,false); + SetTeamName(GREEN_TEAM,DTXT_GREEN,false); + SetTeamName(YELLOW_TEAM,DTXT_YELLOW,false); + + //Load bitmaps + hBitmapObserver = DLLbm_AllocLoadFileBitmap("observer icon.ogf",0,BITMAP_FORMAT_1555); + if(hBitmapObserver<=BAD_BITMAP_HANDLE) + hBitmapObserver = BAD_BITMAP_HANDLE; + + + RegisterPacketReceiver(SPID_TEAMASSIGNMENT,&DMFCBase::GetTeamAssignmentPacket); + RegisterPacketReceiver(SPID_CHANGETEAM,&DMFCBase::GetChangeTeamPacket); + RegisterPacketReceiver(SPID_REQGAMESTATE,&DMFCBase::GetGameStateRequest); + RegisterPacketReceiver(SPID_DMFCGAMEINFO,&DMFCBase::GetDMFCGameInfo); + RegisterPacketReceiver(SPID_VERSIONID,&DMFCBase::GetDMFCVersionCheck); + RegisterPacketReceiver(SPID_PRECORD,PRec_ReceivePRecFromServer); + RegisterPacketReceiver(SPID_PRECORDREQ,&DMFCBase::ReceiveRequestForPlayerRecords); + RegisterPacketReceiver(SPID_CONTROLMSG,&DMFCBase::ReceiveControlMessage); + RegisterPacketReceiver(SPID_NETGAMESYNC,&DMFCBase::ReceiveNetGameInfoSync); + RegisterPacketReceiver(SPID_NEWTEAMNAME,&DMFCBase::ReceiveNewTeamName); + RegisterPacketReceiver(SPID_REMOTEKEY,&DMFCBase::GetRemoteKey); + RegisterPacketReceiver(SPID_REALGAMETIME,&DMFCBase::GetRealGametimePacket); + AddWeaponHash("Laser","Laser Level 1 - Red","Laser Level 2 - Blue","Laser Level 3 - Purple", + "Laser Level 4 - Green",NULL); + AddWeaponHash("Vauss","Vauss Spark",NULL); + AddWeaponHash("Raygun",NULL); + AddWeaponHash("Plasma","plasma sparks",NULL); + AddWeaponHash("Fusion",NULL); + AddWeaponHash("Super Laser","Laser Level 5 -Yellow","Laser Level 6 -White",NULL); + AddWeaponHash("Mass Driver",NULL); + AddWeaponHash("Napalm",NULL); + AddWeaponHash("EMDyellowleft","EMDyellowright","EMDgreenleft","EMDgreenright",NULL); + AddWeaponHash("Omega",NULL); + AddWeaponHash("Concussion",NULL); + AddWeaponHash("Homing",NULL); + AddWeaponHash("Smart","SmartPlasmaHomers",NULL); + AddWeaponHash("Mega","MegaExplosion",NULL); + AddWeaponHash("Frag","frag particles",NULL); + AddWeaponHash("Guided",NULL); + AddWeaponHash("NapalmRocket","NapalmBlob","NapalmParticles",NULL); + AddWeaponHash("Cyclone Pack","Swarm",NULL); + AddWeaponHash("Yellow Flare",NULL); + + SetWeaponDeathMessage("Laser" ,DTXT_DEATHLASER ,false); + SetWeaponDeathMessage("Vauss" ,DTXT_DEATHVAUSS ,false); + SetWeaponDeathMessage("RayGun" ,DTXT_DEATHMICROWAVE,true); + SetWeaponDeathMessage("Plasma" ,DTXT_DEATHPLASMA ,false); + SetWeaponDeathMessage("Fusion" ,DTXT_DEATHFUSION ,false); + SetWeaponDeathMessage("Super Laser" ,DTXT_DEATHSUPERLASER,false); + SetWeaponDeathMessage("Mass Driver" ,DTXT_DEATHMASS ,false); + SetWeaponDeathMessage("Napalm" ,DTXT_DEATHNAPALM ,false); + SetWeaponDeathMessage("EMDyellowleft" ,DTXT_DEATHEMD ,false); + SetWeaponDeathMessage("Omega" ,DTXT_DEATHOMEGA ,false); + SetWeaponDeathMessage("Concussion" ,DTXT_DEATHCONC ,true); + SetWeaponDeathMessage("Homing" ,DTXT_DEATHHOMING ,false); + SetWeaponDeathMessage("Smart" ,DTXT_DEATHSMART ,true); + SetWeaponDeathMessage("Mega" ,DTXT_DEATHMEGA ,false); + SetWeaponDeathMessage("Frag" ,DTXT_DEATHFRAG ,true); + SetWeaponDeathMessage("Guided" ,DTXT_DEATHGUIDED ,false); + SetWeaponDeathMessage("NapalmRocket" ,DTXT_DEATHNAPALMROCKET,true); + SetWeaponDeathMessage("Cyclone Pack" ,DTXT_DEATHCYCLONE ,false); + SetWeaponDeathMessage("Yellow Flare" ,DTXT_DEATHFLARE ,false); + +/* +$$TABLE_WEAPON "Laser" +$$TABLE_WEAPON "Laser Level 1 - Red" +$$TABLE_WEAPON "Laser Level 2 - Blue" +$$TABLE_WEAPON "Laser Level 3 - Purple" +$$TABLE_WEAPON "Laser Level 4 - Green" +$$TABLE_WEAPON "Vauss" +$$TABLE_WEAPON "Vauss Spark" +$$TABLE_WEAPON "Raygun" +$$TABLE_WEAPON "Plasma" +$$TABLE_WEAPON "plasma sparks" +$$TABLE_WEAPON "Fusion" +$$TABLE_WEAPON "Super Laser" +$$TABLE_WEAPON "Laser Level 5 -Yellow" +$$TABLE_WEAPON "Laser Level 6 -White" +$$TABLE_WEAPON "Mass Driver" +$$TABLE_WEAPON "Napalm" +$$TABLE_WEAPON "EMDyellowleft" +$$TABLE_WEAPON "EMDyellowright" +$$TABLE_WEAPON "EMDgreenleft" +$$TABLE_WEAPON "EMDgreenright" +$$TABLE_WEAPON "Omega" +$$TABLE_WEAPON "Concussion" +$$TABLE_WEAPON "Homing" +$$TABLE_WEAPON "Smart" +$$TABLE_WEAPON "SmartPlasmaHomers" +$$TABLE_WEAPON "Mega" +$$TABLE_WEAPON "MegaExplosion" +$$TABLE_WEAPON "Frag" +$$TABLE_WEAPON "frag particles" +$$TABLE_WEAPON "Guided" +$$TABLE_WEAPON "NapalmRocket" +$$TABLE_WEAPON "NapalmBlob" +$$TABLE_WEAPON "NapalmParticles" +$$TABLE_WEAPON "Cyclone Pack" +$$TABLE_WEAPON "Swarm" +$$TABLE_WEAPON "Yellow Flare" +*/ + + //Set up default messages so we have something + AddDeathMessage(DTXT_KILLED1,true); + AddSuicideMessage(DTXT_SUICIDE1); + + //Setup InputCommand handlers + InputCommandInit(); + + ParseStartupScript(); + + TauntIndicatorBMP = DLLbm_AllocLoadFileBitmap("Taunt.ogf",0,BITMAP_FORMAT_1555); + + MenuBackgroundBMP = DLLbm_AllocBitmap(32,32,0); + if(MenuBackgroundBMP>BAD_BITMAP_HANDLE){ + ushort *data = DLLbm_data(MenuBackgroundBMP,0); + int j; + for(j=0;j<32*32;j++) + data[j] = OPAQUE_FLAG|GR_RGB(0,0,0); + } + + MenuItem *lev1,*lev2,*lev3; + + if(GetLocalRole()==LR_SERVER){ + //Start Server Commands Menu + lev1 = new MenuItem(DTXT_MNUSRVRCOMMAND,MIT_NORMAL,0,NULL); //Server + lev2 = new MenuItem(DTXT_MNUKICK,MIT_PLIST,0,KickPlayer); // |- Kick + lev1->AddSubMenu(lev2); + lev2 = new MenuItem(DTXT_MNUBAN,MIT_PLIST,0,BanPlayer); // |- Ban + lev1->AddSubMenu(lev2); + + tCustomMenu cm; + cm.GetItem = GetBannedPlayerString; + cm.GetListCount = GetBanPlayerList; + lev2 = new MenuItem(DTXT_OSM_REMOVEBAN,MIT_CUSTOM,0,RemoveBanByIndex,&cm); + lev1->AddSubMenu(lev2); + + lev2 = new MenuItem(DTXT_OSM_REHASHLIST,MIT_NORMAL,0,RehashAllowDenyLists); + lev1->AddSubMenu(lev2); + + lev2 = new MenuItem(DTXT_MNUENDLEVEL,MIT_NORMAL,0,EndMultiLevel); // |- End Level + lev1->AddSubMenu(lev2); + ServerHUDCallsignsItem = lev2 = new MenuItem(DTXT_OSM_MAXHUDNAME,MIT_STATE,0,SwitchServerHudPlayerName,3,m_iServerHUDCallsignLevel,DTXT_PLAIN_NONE,DTXT_OSM_TEAMONLY,DTXT_OSM_FULL); + lev1->AddSubMenu(lev2); + if(teams>1){ + //Team Game Stuff here + lev2 = new MenuItem(DTXT_MNUTEAMCONTROL,MIT_NORMAL,0,NULL); // |- Team Control + lev3 = new MenuItem(DTXT_MNUBALANCE,MIT_NORMAL,0,BalanceTeams); // |- Balance Teams + lev2->AddSubMenu(lev3); // |- AutoTeam Balance Off/On + AutoBalanceItem = lev3 = new MenuItem(DTXT_MNUAUTOBALANCE,MIT_STATE,0,SwitchAutoTeamSelect,2,(m_iProtectedFlags&DMFC_PRF_AUTOTEAMSELECT)?1:0,DTXT_MNUOFF,DTXT_MNUON); + lev2->AddSubMenu(lev3); + AllowTeamChangeItem = lev3 = new MenuItem(DTXT_MNUALLOWTEAMCNG,MIT_STATE,0,SwitchTeamChange,2,AllowTeamChange()?1:0,DTXT_MNUOFF,DTXT_MNUON); + lev2->AddSubMenu(lev3); + lev3 = new MenuItem(DTXT_OSM_TEAMCONFIG,MIT_NORMAL,0,OnScreenDisplayTeamConfig); + lev2->AddSubMenu(lev3); + lev1->AddSubMenu(lev2); + } + + Menu.AddSubMenu(lev1); + + //End Server Commands Menu + } + + if(GetLocalRole()==LR_CLIENT){ + //Start Client Commands Menu + bool peertopeer = (bool)((Netgame->flags&NF_PEER_PEER)!=0); + if(!peertopeer) + { + lev1 = HUDIndicatorItem = new MenuItem(DTXT_LOSSPINGIND,MIT_STATE,0,SwitchLossPingIndicator,2,(LossGuageEnabled)?1:0,DTXT_MNUOFF,DTXT_MNUON); + Menu.AddSubMenu(lev1); + } + //End Client Commands Menu + } + + //Do General stuff here + lev1 = new MenuItem(DTXT_MNUOBSERVER,MIT_NORMAL,0,NULL); + ObserverItem = lev2 = new MenuItem(DTXT_MNUOBSERVER,MIT_STATE,0,SwitchObserverMode,2,0,DTXT_MNUOFF,DTXT_MNUON); + lev1->AddSubMenu(lev2); + + lev2 = new MenuItem(DTXT_OSM_PIGGYBACK,MIT_PLIST,0,SwitchPiggyBack); + lev1->AddSubMenu(lev2); + Menu.AddSubMenu(lev1); + + /* + lev1 = new MenuItem("OnScreen Menu Options",MIT_NORMAL,0,NULL); + lev2 = MenuBackgroundItem = new MenuItem("Background",MIT_STATE,0,SwitchMenuBackground,2,0,DTXT_MNUOFF,DTXT_MNUON); + lev1->AddSubMenu(lev2); + Menu.AddSubMenu(lev1); + */ + lev1 = MenuBackgroundItem = new MenuItem(DTXT_OSM_BACKGROUND,MIT_STATE,0,SwitchMenuBackground,2,0,DTXT_MNUOFF,DTXT_MNUON); + Menu.AddSubMenu(lev1); + + HUDCallsignsItem = lev1 = new MenuItem(DTXT_OSM_HUDNAMELEVEL,MIT_STATE,0,SwitchHudPlayerNum,3,m_iMyPreferredHUDCallsignLevel,DTXT_PLAIN_NONE,DTXT_OSM_TEAMONLY,DTXT_OSM_FULL); + Menu.AddSubMenu(lev1); + + ShipLogosItem = lev1 = new MenuItem(DTXT_OSM_SHIPLOGOS,MIT_STATE,0,SwitchShipLogoEnable,2,(AreLogosEnabled())?1:0,DTXT_MNUOFF,DTXT_MNUON); + Menu.AddSubMenu(lev1); + + AudioTauntsItem = lev1 = new MenuItem(DTXT_OSM_AUDIOTAUNTS,MIT_STATE,0,SwitchAudioTauntsEnable,2,(AreTauntsEnabled())?1:0,DTXT_MNUOFF,DTXT_MNUON); + Menu.AddSubMenu(lev1); + + //File Statitics menu + lev1 = new MenuItem(DTXT_OSM_STATSTOFILE,MIT_NORMAL,0,NULL); + + lev2 = new MenuItem(DTXT_SAVESTATS,MIT_NORMAL,0,SaveStatsToFile); + lev1->AddSubMenu(lev2); + + SaveStatsLevelEndItem = lev2 = new MenuItem(DTXT_OSM_STATLEVELEND,MIT_STATE,0,SwitchSaveStatsLevelEnd,2,1,DTXT_MNUOFF,DTXT_MNUON); + lev1->AddSubMenu(lev2); + + SaveStatsDisconnectItem = lev2 = new MenuItem(DTXT_OSM_STATDISCONNECT,MIT_STATE,0,SwitchSaveStatsDisconnect,2,1,DTXT_MNUOFF,DTXT_MNUON); + lev1->AddSubMenu(lev2); + + Menu.AddSubMenu(lev1); + + lev1 = new MenuItem(DTXT_MNUGETPLYRINFO,MIT_PLIST,MIF_INCLUDENONE,SwitchPlayerInfo); + Menu.AddSubMenu(lev1); + + NetGameInfoItem = lev1 = new MenuItem(DTXT_OSM_NETGAMEINFO,MIT_STATE,0,SwitchNetGameInfo,2,(m_iProtectedFlags&DMFC_PRF_DISPNETGAMEINFO)?1:0,DTXT_MNUOFF,DTXT_MNUON); + Menu.AddSubMenu(lev1); + + lev1 = new MenuItem(DTXT_OSM_HUDFILTER,MIT_NORMAL,0,NULL); + lev2 = DeathMessageFilterItem = new MenuItem(DTXT_OSM_KILLMESSAGES,MIT_STATE,0,SwitchDeathMessageFilter,3,m_iDeathMessageFilter,DTXT_PLAIN_NONE,DTXT_OSM_SIMPLE,DTXT_OSM_FULL); + lev1->AddSubMenu(lev2); + + lev2 = StatisticMessagesItem = new MenuItem(DTXT_OSM_STATMESSAGES,MIT_STATE,0,SwitchStatHUDMessages,2,1,DTXT_MNUOFF,DTXT_MNUON); + lev1->AddSubMenu(lev2); + Menu.AddSubMenu(lev1); + + if(teams>1){ + //Team Game Stuff here + char names[DLLMAX_TEAMS][MAX_TEAMNAME_LEN]; + strcpy(names[RED_TEAM],GetTeamString(RED_TEAM)); + strcpy(names[BLUE_TEAM],GetTeamString(BLUE_TEAM)); + switch(teams){ + case 2: + TeamMenuItem = lev1 = new MenuItem(DTXT_MNUCHANGETEAMS,MIT_STATE,0,ChangeTeams,2,GetPlayerTeam(GetPlayerNum()),names[RED_TEAM],names[BLUE_TEAM]); + break; + case 3: + strcpy(names[GREEN_TEAM],GetTeamString(GREEN_TEAM)); + TeamMenuItem = lev1 = new MenuItem(DTXT_MNUCHANGETEAMS,MIT_STATE,0,ChangeTeams,3,GetPlayerTeam(GetPlayerNum()),names[RED_TEAM],names[BLUE_TEAM],names[GREEN_TEAM]); + break; + case 4: + strcpy(names[GREEN_TEAM],GetTeamString(GREEN_TEAM)); + strcpy(names[YELLOW_TEAM],GetTeamString(YELLOW_TEAM)); + TeamMenuItem = lev1 = new MenuItem(DTXT_MNUCHANGETEAMS,MIT_STATE,0,ChangeTeams,4,GetPlayerTeam(GetPlayerNum()),names[RED_TEAM],names[BLUE_TEAM],names[GREEN_TEAM],names[YELLOW_TEAM]); + break; + } + Menu.AddSubMenu(lev1); + } + + lev1 = new MenuItem(DTXT_OSM_EXIT,MIT_NORMAL,0,MenuExitMenu); + Menu.AddSubMenu(lev1); + + //save the hud level var + int curr_call_level = m_iMyPreferredHUDCallsignLevel; + m_iMyCurrentHUDCallsignLevel = -1;//set this to -1 so it definitly gets changes/set + SwitchShowHudCallsignLevel(curr_call_level,false); + + Menu.SetInputFocus(); + + EnableShipLogos(true); + + ReadInHostsAllowDeny(); + + InitializeForLevel(); +} + + +// DMFCBase::GameClose +// +// Closes up any needed DLL and DMFC tasks. This ABSOLUTLY must be called, so if you override +// DMFCApp::GameClose, make sure that you put a call to this somewhere in the override +void DMFCBase::GameClose(void) +{ + ASSERT(DMFCInit); + DMFCInit = false; + + SaveSettings(); + + //Free bitmaps + if(hBitmapObserver>BAD_BITMAP_HANDLE){ + DLLbm_FreeBitmap(hBitmapObserver); + hBitmapObserver = BAD_BITMAP_HANDLE; + } + + FreeHostsLists(); + + //Close up InputCommands + InputCommandFree(); + + //close up and free player records + PRec_Close(); + + if(MenuBackgroundBMP>BAD_BITMAP_HANDLE) + DLLbm_FreeBitmap(MenuBackgroundBMP); + + DLLDestroyStringTable(DMFCStringTable,DMFCStringTableSize); + +} + + +void DMFCBase::DrawMenu(void) +{ + DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); + + //Menu.Draw(10,100,DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX])+1,this,(m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR)?MenuBackgroundBMP:-1); + //Menu.Draw(10,100,DLLRenderHUDGetTextHeight("X")+1,this,(m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR)?MenuBackgroundBMP:-1); + int font_height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]); + + Menu.Draw(10,(font_height*8)+10,font_height+1,(m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR)?MenuBackgroundBMP:-1); +} + +char DMFCPlayerInfo[6][100]; +void DMFCBase::SetPlayerInfo(int pnum) +{ + if(!CheckPlayerNum(pnum)){ + return; + } + + m_iPlayerDisplayed = pnum; + + int x,y; + char buffer[100]; + y = 0; + x = 15; + int index = 0; + bool display_addr = false; + + //Print out callsign + sprintf(buffer,"%s (%s)",Players[pnum].callsign,(0==pnum)?DTXT_SERVER:DTXT_CLIENT); + strcpy(DMFCPlayerInfo[index],buffer); index++; + + if(m_iNumTeams>1 && Players[pnum].team!=-1){ + sprintf(buffer,DTXT_TEAMFORMAT,GetTeamString(GetPlayerTeam(pnum))); + strcpy(DMFCPlayerInfo[index],buffer); index++; + } + + //Print out playernum + sprintf(buffer,DTXT_PLRNUMFORMAT,pnum); + strcpy(DMFCPlayerInfo[index],buffer); index++; + +#ifdef _DEBUG + display_addr = true; +#endif + if(GetLocalRole()==LR_SERVER) + display_addr = true; + + //Print out net address + if(display_addr){ + network_protocol proto = NP_NONE; + + if(pnum!=GetPlayerNum()) + proto = NetPlayers[pnum].addr.connection_type; + else + proto = NP_TCP; + + if(proto!=NP_NONE){ + char string[32]; + if(pnum!=0){ + DLLnw_GetNumbersFromHostAddress(&NetPlayers[pnum].addr,string); + }else{ + int addr = DLLnw_GetThisIP(); + network_address local_addr; + memset(&local_addr,0,sizeof(network_address)); + memcpy(local_addr.address,&addr,sizeof(int)); + local_addr.connection_type = NP_TCP; + + DLLnw_GetNumbersFromHostAddress(&local_addr,string); + } + strcpy(DMFCPlayerInfo[index],string); index++; + } + } + + //Print out Ship info + sprintf(buffer,DTXT_SHIPFORM,Ships[Players[pnum].ship_index].name); + strcpy(DMFCPlayerInfo[index],buffer); index++; + + for(;index<5;index++){ + DMFCPlayerInfo[index][0] = '\0'; + } +} + +void DMFCBase::DisplayPlayerInfo(int background_bmp,bool dedicated_server) +{ + DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); + + int height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX])+1; + //int height = DLLRenderHUDGetTextHeight("X")+1; + ddgr_color color = GR_RGB(180,255,180); + int x,y; + y = 0; + x = 15; + int index = 0; + + if(!CheckPlayerNum(m_iPlayerDisplayed)){ + m_iPlayerDisplayed = -1; + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPPLAYERINFO); + return; + } + + if( (m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR) && background_bmp>BAD_BITMAP_HANDLE){ + //draw the background bitmap + DLLrend_SetAlphaValue(255*0.85f); + DLLrend_SetZBufferState (0); + DLLrend_SetTextureType (TT_LINEAR); + DLLrend_SetLighting (LS_NONE); + DLLrend_SetAlphaType (AT_CONSTANT_TEXTURE); + DLLrend_DrawScaledBitmap(0,0,*Game_window_w,6*height,background_bmp,0,0,1,1,1.0,-1,NULL); + DLLrend_SetZBufferState(1); + } + + //Print out callsign + //DLLRenderHUDText(color,255,0,x,y,DMFCPlayerInfo[index]); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DMFCPlayerInfo[index]); + y+=height; + + if(dedicated_server) + DPrintf("%s\n",DMFCPlayerInfo[index]); + index++; + color = GR_RGB(40,255,40); + + for(;index<5;index++){ + //DLLRenderHUDText(color,255,0,x,y,DMFCPlayerInfo[index]); + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DMFCPlayerInfo[index]); + + if(DMFCPlayerInfo[index][0]!='\0') + y+=height; + if(dedicated_server) + DPrintf("%s\n",DMFCPlayerInfo[index]); + } + + //print out the volatile information + player_record *pr = PRec_GetPRecordByPnum(m_iPlayerDisplayed); + char temp[150]; + + if(!pr || pr->state!=STATE_INGAME) + return; + + int precindex = translate_precptr_to_index(pr); + float ti = GetTimeInGame(precindex); + + sprintf(temp,DTXT_PI_TIMEINGAME,GetTimeString(ti)); + //DLLRenderHUDText(color,255,0,x,y,temp); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,temp); + y+=height; + + if(dedicated_server) + DPrintf("%s\n",temp); +} + +// DMFCBase::SwitchPlayerInfoDisplay +// +// Switches on/off displaying a Playerinfo +void DMFCBase::SwitchPlayerInfoDisplay(int pnum) +{ + m_iProtectedFlags ^= DMFC_PRF_DISPPLAYERINFO; + if(m_iProtectedFlags&DMFC_PRF_DISPPLAYERINFO){ + + if(IsDisplayingNetGameInfo()) //if we are displaying netgame info, turn it off + SwitchNetGameInfoDisplay(0); + + if(!CheckPlayerNum(pnum)){ + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPPLAYERINFO); + return; + } + SetPlayerInfo(pnum); + } +} + +// DMFCBase::SwitchNetGameInfoDisplay +// +// Switches on/off displaying netgame info +void DMFCBase::SwitchNetGameInfoDisplay(int on) +{ + if(on) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPNETGAMEINFO); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPNETGAMEINFO); + + if(on){ + if(DisplayingPlayerInfo()!=-1){ + //turn on displaying of player info before displaying netgame info + SwitchPlayerInfoDisplay(-1); + } + } +} + +// DMFCBase::IsDisplayingNetGameInfo +// +// returns true if we are currently displaying netgame info +bool DMFCBase::IsDisplayingNetGameInfo(void) +{ + return (m_iProtectedFlags&DMFC_PRF_DISPNETGAMEINFO)?true:false; +} + +// DMFCBase::DisplayingPlayerInfo +// +// Returns the pnum of the player info being displayed, -1 if none +int DMFCBase::DisplayingPlayerInfo(void) +{ + if(m_iProtectedFlags&DMFC_PRF_DISPPLAYERINFO) + return m_iPlayerDisplayed; + else + return -1; +} + +//DMFCBase::SetTeamName +// +// Sets the team name for a given team +// team: integer value of the team to change +// name: new name for the team +// announce: if this is true, and we are the server, it will tell all the clients about the change +bool DMFCBase::SetTeamName(int team,char *name,bool announce) +{ + if(team<0 || team>DLLMAX_TEAMS) + return false; + if(!name) + return false; + + char old_teamname[MAX_TEAMNAME_LEN]; + strcpy(old_teamname,DMFC_team_names[team]); + + strncpy(DMFC_team_names[team],name,MAX_TEAMNAME_LEN-1); + DMFC_team_names[team][MAX_TEAMNAME_LEN-1] = '\0'; + + CallOnTeamChangeName(team,old_teamname,DMFC_team_names[team]); + + if(announce && GetLocalRole()==LR_SERVER){ + //tell the clients about the new team name + SendNewTeamName(team); + DPrintf(DTXT_SETTEAMNAME,old_teamname,name); + } + + return true; +} + + +// DMFCBase::GetTeamFromString +// +// Returns the int value of a team based on a string, -1 if not a team +int DMFCBase::GetTeamFromString(char *str) +{ + if(!str) + return -1; + for(int i=0;ilocal_role; +} + +// DMFCBase::CheckPlayerNum +// +// Returns true if the player number passed in is a valid player number (the player is connected) +bool DMFCBase::CheckPlayerNum(int player_num) +{ + if( (player_num<0) || (player_num>=DLLMAX_PLAYERS) ) + return false; + if( (NetPlayers[player_num].flags&NPF_CONNECTED) && (NetPlayers[player_num].sequence>=NETSEQ_PLAYING) ) + return true; + else + return false; +} + +// DMFCBase::PacketCheckPlayerNum +// +// Returns true if it's ok to send a packet to this player +bool DMFCBase::PacketCheckPlayerNum(int player_num) +{ + if( (player_num<0) || (player_num>=DLLMAX_PLAYERS) ) + return false; + if( (NetPlayers[player_num].flags&NPF_CONNECTED) && (NetPlayers[player_num].sequence>=NETSEQ_OBJECTS) ) + return true; + else + return false; +} + +// DMFCBase::CallClientEvent +// +// Server Only. This will send an event to a client for it to execute. +// event = An EVT_CLIENT_* +// me_objnum,it_objnum = Object numbers of the objects to be me and it for the event +// destination = Player number of the client to send to, 0or -1 if to send to all +void DMFCBase::CallClientEvent(int event,int me_objnum,int it_objnum,int destination,bool parms) +{ + if(GetLocalRole()!=LR_SERVER) + return; + if( (destination<-1) || (destination>DLLMAX_PLAYERS) ) + return; + DLLMultiSendClientExecuteDLL(event,me_objnum,it_objnum,destination,parms?Data:NULL); +} + + +// DMFCBase::GetTimeLeft +// +// Server Only. This will fill in the float pointer passed to it with how much time is +// left in the multiplayer game. The value placed in the float is only valid if GetTimeLeft +// returns true. If it returns false it was because either it is not a game with a time limit +// or it is being called on a client. +bool DMFCBase::GetTimeLeft(float *time) +{ + if(GetLocalRole()!=LR_SERVER) + return false; + if(Netgame->flags&NF_TIMER){ + *time = (Netgame->timelimit*60.0f) - RealGametime; + return true; + } + return false; +} + +// DMFCBase::EndLevel +// +// Server Only. This will end the current multiplayer level, and will go on to the next level. +void DMFCBase::EndLevel(void) +{ + if(GetLocalRole()!=LR_SERVER) + return; + DLLMultiEndLevel(); +} + +// DMFCBase::GetScoreLimit +// +// Server Only. This will fill in the int pointer passed to it with what the scoring limit +// is, set in in the multiplayer options. The value point into the int is only valid if +// GetScoreLimit returns true. If it returns false it is because you are either not the +// server or this option wasn't set in the multiplayer options +bool DMFCBase::GetScoreLimit(int *limit) +{ + if(GetLocalRole()!=LR_SERVER) + return false; + if(Netgame->flags&NF_KILLGOAL){ + *limit = Netgame->killgoal; + return true; + } + return false; +} + +// DMFCBase::AutoTimeLimit +// +// Server Only. This turns off or on the automatic level ending by DMFC of a timed multiplayer game. +// If you turn it off, it is your responsibility to end a time multiplayer game when it's time +// is up. If it is turned on, DMFC will automatically handle ending the game. By default it is on. +void DMFCBase::AutoTimeLimit(bool turnon) +{ + if(GetLocalRole()!=LR_SERVER) + return; + if((Netgame->flags&NF_TIMER)==0) + return; + if(turnon) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTIMELIMIT); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTIMELIMIT); +} + + +// DMFCBase::AutoDeathMessage +// +// This turns on or off DMFC's automatic handling of death messages. If it is turned on (Default) +// then when a player dies it will display a random death message from the list added by you using +// AddDeathMessage. If it is turned off, then it is your responsibility to handle the messages, you +// can use DoRandomDeathMessage to display one when appropriate. +void DMFCBase::AutoDeathMessage(bool turnon) +{ + if(turnon) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTODEATHMSG); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTODEATHMSG); +} + + +// DMFCBase::AddDeathMessage +// +// This will add a death message to DMFC. +// format = string in a "printf" type format (using %s for player callsigns) of the message +// victim_first = Set this to true if the victim is listed first in the format +void DMFCBase::AddDeathMessage(char *string,bool victim_first) +{ + if( (m_iDeathMsgCount>=0) && (m_iDeathMsgCount=0) && (m_iSuicideMsgCountit_handle==OBJECT_HANDLE_NONE) + return -1; + + return Data->it_handle&HANDLE_OBJNUM_MASK; +} + +// DMFCBase::GetMeObjNum +// +// Returns the me object number of the current event for use. +int DMFCBase::GetMeObjNum(void) +{ + if(Data->me_handle==OBJECT_HANDLE_NONE) + return -1; + return Data->me_handle&HANDLE_OBJNUM_MASK; +} + + +//DMFCBase::OnGameStateRequest +// +// Server only. Override this to listen for Game state requests from the clients +// When this function is called a client (who's player number is passed in) is requesting +// game state information. Do what you need to do to send game state information to that player +void DMFCBase::OnGameStateRequest(int pnum) +{ + if(pnum==-1) + return; + mprintf((0,"%s is requesting Game State information\n",Players[pnum].callsign)); +} + + +// DMFCBase::GetTeamForNewPlayer +// +// A Helper function (Server only), which will give you the optimum team assignment (whoever has the +// lowest amount of players on their team, for a new player. +// player_num = player number of the new player +// num_teams = The number of teams in your game +int DMFCBase::GetTeamForNewPlayer(int player_num,int num_teams) +{ + if( (player_num<0) || (player_num>=DLLMAX_PLAYERS) ) + return 0; + + //see if it's a dedicated server game, and if so, are we the server + if(IAmDedicatedServer() && (player_num==GetPlayerNum()) ){ + return -1; //place on the special "no team" + } + + int i; + int *Teams; + Teams = (int *)malloc(sizeof(int)*num_teams); + if(!Teams) + return 0; + + for(i=0;iTeams[i]) + { + low_count = Teams[i]; + low_team = i; + } + } + free(Teams); + return low_team; +} + + + +// DMFCBase::SetNumberOfTeams +// +// Sets the number of teams to be used in the game. By default there is 1 team (everyone against everyone). +// You can set up to a maximum of 4 teams. Call this function as soon as possible. +// teams = number of teams +void DMFCBase::SetNumberOfTeams(int teams) +{ + if( (teams<1) || (teams>4) ) + return; + + DLLSetMaxTeams(teams); + DLLMultiPaintGoalRooms(NULL); + + m_iNumTeams = teams; + + char names[DLLMAX_TEAMS][MAX_TEAMNAME_LEN]; + strcpy(names[RED_TEAM],GetTeamString(RED_TEAM)); + strcpy(names[BLUE_TEAM],GetTeamString(BLUE_TEAM)); + if(TeamMenuItem){ + switch(m_iNumTeams){ + case 2: + TeamMenuItem->SetStateItemList(m_iNumTeams,names[RED_TEAM],names[BLUE_TEAM]); + break; + case 3: + strcpy(names[GREEN_TEAM],GetTeamString(GREEN_TEAM)); + TeamMenuItem->SetStateItemList(m_iNumTeams,names[RED_TEAM],names[BLUE_TEAM],names[GREEN_TEAM]); + break; + case 4: + strcpy(names[GREEN_TEAM],GetTeamString(GREEN_TEAM)); + strcpy(names[YELLOW_TEAM],GetTeamString(YELLOW_TEAM)); + TeamMenuItem->SetStateItemList(m_iNumTeams,names[RED_TEAM],names[BLUE_TEAM],names[GREEN_TEAM],names[YELLOW_TEAM]); + break; + } + } +} + + +// DMFCBase::AutoTeamSelect +// +// Turns on or off DMFC's auto team assignment. If it is on, then when a new player joins, they will +// be placed on the team with fewest players. If it is off, then you must handle that. Defualt on. +void DMFCBase::AutoTeamSelect(bool turnon) +{ + if(turnon) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTEAMSELECT); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOTEAMSELECT); +} + +// DMFCBase::RequestChangeTeams +// +// If you are the server it will make the player change teams. If you +// are a client then it sends a request to the server to change teams +void DMFCBase::RequestTeamChange(int team,int pnum,bool spew_on_respawn) +{ + if(!CheckPlayerNum(pnum)) + return; + if(Players[pnum].team==-1) //Dedicated server can't change teams + return; + if( (team<0) || (team>=m_iNumTeams) ) + return; + if(Players[pnum].team==team) + return; + + if(GetLocalRole()==LR_SERVER){ + if(!AllowTeamChange()){ + return; + } + if(!CallOnCanChangeTeam(pnum,team)){ + return; + } + + Players[pnum].team = team; + CallOnPlayerChangeTeam(pnum,team,true,spew_on_respawn); + SendTeamAssignment(pnum,team,spew_on_respawn); + }else{ + if(pnum!=GetPlayerNum()) + return; + SendChangeTeamRequest(team,spew_on_respawn); + } +} + + +// DMFCBase::AddHudItemCallback +// +// Adds an item to the hud. Everytime the hud needs to be updated, it will call the +// handler passed in. The handler must be declared as: +// void func(struct tHUDItem *item); +// type = HI_BITMAP for bitmap, HI_TEXT for a text item +// func = function callback +void DMFCBase::AddHUDItemCallback(int type,void (*func)(struct tHUDItem *)) +{ + //add all the needed Hud Items + tHUDItem item; + switch(type) + { + case HI_TEXT: + item.type = HUD_ITEM_CUSTOMTEXT; + break; + case HI_BITMAP: + item.type = HUD_ITEM_CUSTOMIMAGE; + break; + default: + item.type = HUD_ITEM_CUSTOMTEXT; + break; + } + item.stat = STAT_CUSTOM; + item.flags = HUD_FLAG_PERSISTANT; + item.data.text = NULL; + item.render_fn = func; + item.y = 0; + item.x = 0; + DLLAddHUDItem(&item); +} + +// DMFCBase::GetMyTeam +// +// Returns the int value of the team you are on...only useful in a team game. +int DMFCBase::GetMyTeam(void) +{ + return Players[GetPlayerNum()].team; +} + +// DMFCBase::GetTeamString +// +// Returns a pointer to a string name of a team +// team = integer value of the team +const char *DMFCBase::GetTeamString(int team) +{ + static char name[20]; + + if(team>=0 && team=width){ + if(DLLgrtext_GetTextLineWidth(string)>=width){ + //We have to clip + size--; + if(arrow){ + string[size] = CHAR_RIGHT_ARROW; + string[size+1] = '\0'; + } + return; + } + //replace the char and move to the next + string[size] = save; + size++; + save = string[size]; + } + //The string didn't need to be clipped +} + + +// DMFCBase::ConvertHUDAlpha +// +// Returns a converted alpha based on what you give, it will be a more transparent if the onscreen menu is up +float DMFCBase::ConvertHUDAlpha(float normal) +{ + if(!IsMenuUp()) + return normal; + return normal * 0.3f; +} +ubyte DMFCBase::ConvertHUDAlpha(ubyte normal) +{ + if(!IsMenuUp()) + return normal; + + float conv = ((float)normal); + conv = conv * 0.3f; + return (ubyte)conv; +} + + +bool DMFCBase::DMFC_compare_slots(int a,int b) +{ + int ascore,bscore; + player_record *apr,*bpr; + apr = GetPlayerRecord(a); + bpr = GetPlayerRecord(b); + if( !apr ) + return true; + if( !bpr ) + return false; + if( apr->state==STATE_EMPTY ) + return true; + if( bpr->state==STATE_EMPTY ) + return false; + if( (apr->state==STATE_INGAME) && (bpr->state==STATE_INGAME) ){ + //both players were in the game + ascore = apr->dstats.kills[DSTAT_LEVEL] - apr->dstats.suicides[DSTAT_LEVEL]; + bscore = bpr->dstats.kills[DSTAT_LEVEL] - bpr->dstats.suicides[DSTAT_LEVEL]; + return (ascorestate==STATE_INGAME) && (bpr->state==STATE_DISCONNECTED) ){ + //apr gets priority since he was in the game on exit + return false; + } + if( (apr->state==STATE_DISCONNECTED) && (bpr->state==STATE_INGAME) ){ + //bpr gets priority since he was in the game on exit + return true; + } + //if we got here then both players were disconnected + ascore = apr->dstats.kills[DSTAT_LEVEL] - apr->dstats.suicides[DSTAT_LEVEL]; + bscore = bpr->dstats.kills[DSTAT_LEVEL] - bpr->dstats.suicides[DSTAT_LEVEL]; + return (ascorestate==STATE_EMPTY ) + return true; + if( bpr->state==STATE_EMPTY ) + return false; + if( (apr->state==STATE_INGAME) && (bpr->state==STATE_INGAME) ){ + //both players were in the game + atemp = (float)(apr->dstats.kills[DSTAT_LEVEL]+apr->dstats.suicides[DSTAT_LEVEL]+apr->dstats.deaths[DSTAT_LEVEL]); + ascore = (float)(apr->dstats.kills[DSTAT_LEVEL])/((atemp)?atemp:0.0000001f); + + btemp = (float)(bpr->dstats.kills[DSTAT_LEVEL]+bpr->dstats.suicides[DSTAT_LEVEL]+bpr->dstats.deaths[DSTAT_LEVEL]); + bscore = (float)(bpr->dstats.kills[DSTAT_LEVEL])/((btemp)?btemp:0.0000001f); + return (ascorestate==STATE_INGAME) && (bpr->state==STATE_DISCONNECTED) ){ + //apr gets priority since he was in the game on exit + return false; + } + if( (apr->state==STATE_DISCONNECTED) && (bpr->state==STATE_INGAME) ){ + //bpr gets priority since he was in the game on exit + return true; + } + //if we got here then both players were disconnected + atemp = (float)(apr->dstats.kills[DSTAT_LEVEL]+apr->dstats.suicides[DSTAT_LEVEL]+apr->dstats.deaths[DSTAT_LEVEL]); + ascore = (float)(apr->dstats.kills[DSTAT_LEVEL])/((atemp)?atemp:0.0000001f); + + btemp = (float)(bpr->dstats.kills[DSTAT_LEVEL]+bpr->dstats.suicides[DSTAT_LEVEL]+bpr->dstats.deaths[DSTAT_LEVEL]); + bscore = (float)(bpr->dstats.kills[DSTAT_LEVEL])/((btemp)?btemp:0.0000001f); + return (ascore=0 && DMFC_compare_slots(tempsort[j],t); j--){ + tempsort[j+1] = tempsort[j]; + } + + // insert + tempsort[j+1] = t; + } + + if(maxsize=0 && DMFC_compare_slots_efficiency(tempsort[j],t); j--){ + tempsort[j+1] = tempsort[j]; + } + + // insert + tempsort[j+1] = t; + } + + if(maxsizem_iServerHUDCallsignLevel) + level = m_iServerHUDCallsignLevel; + + if(m_iMyCurrentHUDCallsignLevel==level) + return; + + m_iMyCurrentHUDCallsignLevel = level; + + switch(level){ + case HUD_CALLSIGN_LEVEL_FULL: + DLLPlayerSetHUDNameFOV(10); + if(announce) + DLLAddHUDMessage(DTXT_HUDLEVEL_FULL); + break; + case HUD_CALLSIGN_LEVEL_TEAM: + DLLPlayerSetHUDNameFOV(10); + if(announce) + DLLAddHUDMessage(DTXT_HUDLEVEL_TEAM); + break; + case HUD_CALLSIGN_LEVEL_NONE: + DLLPlayerSetHUDNameFOV(-1); + if(announce) + DLLAddHUDMessage(DTXT_HUDLEVEL_NONE); + break; + default: + mprintf((0,"DMFC: Invalid HUD Name Level\n")); + return; + break; + }; +} + +//DMFCBase::SwitchServerHudCallsignLevel +// +// +// Sets the max level of HUD callsign displayage...determined by the server +void DMFCBase::SwitchServerHudCallsignLevel(ubyte level) +{ + switch(level){ + case HUD_CALLSIGN_LEVEL_FULL: + m_iServerHUDCallsignLevel = level; + DLLAddHUDMessage(DTXT_SHUDLEVEL_FULL); + break; + case HUD_CALLSIGN_LEVEL_TEAM: + m_iServerHUDCallsignLevel = level; + DLLAddHUDMessage(DTXT_SHUDLEVEL_TEAM); + break; + case HUD_CALLSIGN_LEVEL_NONE: + m_iServerHUDCallsignLevel = level; + DLLAddHUDMessage(DTXT_SHUDLEVEL_NONE); + break; + default: + mprintf((0,"DMFC: Invalid Server HUD Name Level\n")); + return; + break; + } + + //make sure we're not set higher than the server allows + SwitchShowHudCallsignLevel(m_iMyPreferredHUDCallsignLevel); + + if(GetLocalRole()==LR_SERVER) + SendDMFCGameInfo(SP_ALL); +} + +//DMFCBase::ShouldIDisplayHUDName +// +// Given a player num, it will determine if the callsign should be drawn on the HUD +bool DMFCBase::ShouldIDisplayHUDName(int pnum) +{ + switch(m_iMyCurrentHUDCallsignLevel){ + case HUD_CALLSIGN_LEVEL_FULL: + return true; + break; + case HUD_CALLSIGN_LEVEL_TEAM: + { + //see if this is a team game + if(GetNumTeams()>=2){ + //ok it's a team game, see if this player is on my team, if so, than disply + if(Players[pnum].team==GetMyTeam()){ + return true; + }else{ + return false; + } + } + return false; + }break; + case HUD_CALLSIGN_LEVEL_NONE: + return false; + break; + } + return false; +} + + +//DMFCBase::GetCounterMeasureOwner +// +// +// Given a counter measure it will determine the pnum of it's owner...if it can't find it, it returns -1 +int DMFCBase::GetCounterMeasureOwner(object *robot) +{ + if(!robot) + return -1; + + if(robot->type!=OBJ_ROBOT && (!(robot->type == OBJ_BUILDING && robot->ai_info))) + return -1; + + object *killer; + DLLGetUltimateParentForObject(&killer,robot); + + if(killer==robot) + return -1; + + if( (killer->type!=OBJ_PLAYER) && (killer->type!=OBJ_OBSERVER) ) + return -1; + + return killer->id; +} + + +//DMFCBase::EncryptData +// +// Encrypts (weak) a buffer of data +void DMFCBase::EncryptData(ubyte *data,int size) +{ + if(!data) + return; + if(size<=0) + return; + + int offset = size; + + for(int i=0;i=0) && (pnumpinfo; +} + +// DMFCBase::UpdatePInfo +// +// +// Updates a victim's pinfo stat (pass in player nums) +void DMFCBase::UpdatePInfo(int victim,int killer,int amount) +{ + ASSERT( (victim>=0) || (victim=0) || (killer=DLLMAX_PLAYERS) || (killer<0) || (killer>=DLLMAX_PLAYERS) ){ + + return; + } + + PInfo *id; + int victim_slot = PRec_GetPlayerSlot(victim); + tPKillerInfo ki; + + memset(&ki,0,sizeof(tPKillerInfo)); + id = FindPInfo(killer); + + if( (id==NULL) || (victim_slot==-1) ){ + mprintf((0,"Unable to find PInfos\n")); + return; + } + + //update killer + ki.kills = amount; + id->Update(victim_slot,&ki); + + + PInfo *kpi,*vpi; + kpi = id; + vpi = FindPInfo(victim); + int killer_slot = PRec_GetPlayerSlot(killer); + + if( (vpi==NULL) || (killer_slot==-1) ){ + mprintf((0,"Unable to find PInfos\n")); + return; + } + + if(victim!=killer){ + //regular kill + kpi->HandleKill(victim_slot); + vpi->HandleDeath(killer_slot); + }else{ + //suicide + kpi->HandleSuicide(); + } +} + + +// DMFCBase::ResetPInfo +// +// +// Resets all the PInfo stats (and frees memory) +void DMFCBase::ResetPInfo(void) +{ + for(int i=0;ipinfo){ + ((PInfo *)pr->pinfo)->ResetAll(); + } + } +} + +// DMFCBase::WriteDMFCStatsToFile +// +// +// This function will write out DMFC's stats to the file given (it must be opened for writing) +void DMFCBase::WriteDMFCStatsToFile(CFILE *file) +{ +#define BUFSIZE 150 + ASSERT(file!=NULL); + char buffer[BUFSIZE]; + player_record *pr,*dpr; + tPInfoStat stat; + int count,length; + + count =1; + + for(int p=0;pstate!=STATE_EMPTY) { + //Write out header + sprintf(buffer,"%d) %s%s",count,(pr->state==STATE_INGAME)?"":"*",pr->callsign); + DLLcf_WriteString(file,buffer); + length = strlen(buffer); + memset(buffer,'=',length); + buffer[length] = '\0'; + DLLcf_WriteString(file,buffer); + + if(FindPInfoStatFirst(p,&stat)){ + char tempbuffer[25]; + sprintf(buffer,DTXT_DMFC_STAT_HEADER); + DLLcf_WriteString(file,buffer); + memset(buffer,' ',BUFSIZE); + dpr = GetPlayerRecord(stat.slot); + + sprintf(tempbuffer,"%s",dpr->callsign); + memcpy(buffer,tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.kills); + memcpy(&buffer[30],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.deaths); + memcpy(&buffer[40],tempbuffer,strlen(tempbuffer)); + + int pos; + pos = 40 + strlen(tempbuffer) + 1; + if(poscallsign); + memcpy(buffer,tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.kills); + memcpy(&buffer[30],tempbuffer,strlen(tempbuffer)); + + sprintf(tempbuffer,"%d",stat.deaths); + memcpy(&buffer[40],tempbuffer,strlen(tempbuffer)); + + int pos; + pos = 40 + strlen(tempbuffer) + 1; + if(pos=MAX_PLAYER_RECORDS) + return false; + + if( !stat ) + return false; + + FindPInfoStatClose(); + + player_record *pr,*dpr; + tPKillerInfo *killer,*victim; + int deaths; + + _SlotsUsed[slot] = true; + _CurrentPInfoSlot = slot; + memset(&_CurrentPInfoStat,0,sizeof(tPInfoStat)); + + //return the given slot + pr = GetPlayerRecord(slot); + if( !pr || pr->state==STATE_EMPTY || !pr->pinfo) + return false; + + killer = ((PInfo *)pr->pinfo)->GetFirstKiller(); + victim = NULL; + + if(!killer){ + _DoneWithKills = true; + //go through the slots looking for deaths + for(int p=0;pstate!=STATE_EMPTY && dpr->pinfo){ + victim = ((PInfo *)dpr->pinfo)->GetKillerInfo(slot); + if(victim) + deaths = victim->kills; + } + if(deaths>0){ + _CurrentPInfoStat.slot = p; + _CurrentPInfoStat.kills = 0; + _CurrentPInfoStat.deaths = deaths; + memcpy(stat,&_CurrentPInfoStat,sizeof(tPInfoStat)); + return true; + } + } + } + return false; + } + + _CurrentPInfoStat.slot = killer->slot; + _CurrentPInfoStat.kills = killer->kills; + + deaths = 0; + dpr = GetPlayerRecord(killer->slot); + if( dpr && dpr->state!=STATE_EMPTY && dpr->pinfo){ + victim = ((PInfo *)dpr->pinfo)->GetKillerInfo(slot); + } + + if(victim) + deaths = victim->kills; + + _CurrentPInfoStat.deaths = deaths; + _SlotsUsed[killer->slot] = true; + + memcpy(stat,&_CurrentPInfoStat,sizeof(tPInfoStat)); + return true; +} + +// DMFCBase::FindPInfoStatNext +// +// Call this repeatedly until you get a value of false, finish by calling FindPInfoStatClose +bool DMFCBase::FindPInfoStatNext(tPInfoStat *stat) +{ + if(_CurrentPInfoSlot==-1) + return false; + if( !stat ) + return false; + + int slot; + player_record *pr,*dpr; + slot = _CurrentPInfoSlot; + tPKillerInfo *killer,*victim; + int deaths = 0; + memset(&_CurrentPInfoStat,0,sizeof(tPInfoStat)); + + //return the given slot + pr = GetPlayerRecord(slot); + if( !pr || pr->state==STATE_EMPTY || !pr->pinfo) + return false; + + killer = ((PInfo *)pr->pinfo)->GetNextKiller(); + victim = NULL; + + if(!killer){ + _DoneWithKills = true; + //go through the slots looking for deaths + for(int p=0;pstate!=STATE_EMPTY && dpr->pinfo){ + victim = ((PInfo *)dpr->pinfo)->GetKillerInfo(slot); + if(victim) + deaths = victim->kills; + } + if(deaths>0){ + _CurrentPInfoStat.slot = p; + _CurrentPInfoStat.kills = 0; + _CurrentPInfoStat.deaths = deaths; + memcpy(stat,&_CurrentPInfoStat,sizeof(tPInfoStat)); + return true; + } + } + } + return false; + } + + _CurrentPInfoStat.slot = killer->slot; + _CurrentPInfoStat.kills = killer->kills; + + deaths = 0; + dpr = GetPlayerRecord(killer->slot); + if( dpr && dpr->state!=STATE_EMPTY && dpr->pinfo){ + victim = ((PInfo *)dpr->pinfo)->GetKillerInfo(slot); + } + + if(victim) + deaths = victim->kills; + + _CurrentPInfoStat.deaths = deaths; + _SlotsUsed[killer->slot] = true; + + memcpy(stat,&_CurrentPInfoStat,sizeof(tPInfoStat)); + return true; +} + +// DMFCBase::FindPInfoStatClose +// +// Closes up a FindPInfo series of calls +void DMFCBase::FindPInfoStatClose(void) +{ + _CurrentPInfoSlot = -1; + _DoneWithKills = false; +} + + +// DMFCBase::SetWeaponDeathMessage +// +// Sets a death message for a weapon kill +bool DMFCBase::SetWeaponDeathMessage(char *weapon_name,char *message,bool victim_first) +{ + ASSERT(weapon_name!=NULL); + ASSERT(message!=NULL); + + int weapon_index = DLLFindWeaponName(IGNORE_TABLE(weapon_name)); + if(weapon_index==-1){ + mprintf((0,"Unable to set WeaponMessage for %s...can't find it\n",weapon_name)); + return false; + } + + int real_weapon = WeaponHash[weapon_index]; + + if(real_weapon==-1){ + mprintf((0,"You forgot to call AddWeaponHash before adding this Message\n")); + return false; + } + + if( (WeaponMessages[real_weapon].inuse) && (WeaponMessages[real_weapon].message) ){ + free(WeaponMessages[real_weapon].message); + WeaponMessages[real_weapon].message = NULL; + } + + WeaponMessages[real_weapon].message = (char *)malloc(strlen(message)+1); + if(WeaponMessages[real_weapon].message){ + strcpy(WeaponMessages[real_weapon].message,message); + WeaponMessages[real_weapon].inuse = true; + WeaponMessages[real_weapon].victim_first = victim_first; + } + + return WeaponMessages[real_weapon].inuse; +} + +// DMFCBase::GetWeaponDeathMessage +// +// Returns the format string for a weapon death message, NULL if it doesn't exist +char *DMFCBase::GetWeaponDeathMessage(int index,bool *victim_first) +{ + if( (index<0) || (index>=MAX_WEAPONS) ) + return NULL; + + int real_weapon; + + real_weapon = WeaponHash[index]; + + if(real_weapon==-1) + return NULL; + + if( !WeaponMessages[real_weapon].inuse ) + return NULL; + + *victim_first = WeaponMessages[real_weapon].victim_first; + return WeaponMessages[real_weapon].message; +} + +// DMFCBase::AddWeaponHash +// +// Since one weapon may actually consist of many weapons, in order to save space you can create +// one weapon where all those other weapon id's will be mapped to it...use WeaponHash[id] to +// get the actual weapon. End list of children with a NULL +void DMFCBase::AddWeaponHash(char *parent, ... ) +{ + ASSERT(parent!=NULL); + + int parent_id = DLLFindWeaponName(IGNORE_TABLE(parent)); + if(parent_id==-1){ + mprintf((0,"Unable to find parent weapon ID in AddWeaponHash (%s)\n",parent)); + return; + } + + va_list marker; + va_start (marker,parent); + + WeaponHash[parent_id] = parent_id; + + bool done = false; + + while(!done){ + int id; + char *name; + name = va_arg (marker,char *); + if(name) + id = DLLFindWeaponName(IGNORE_TABLE(name)); + else + id = -1; + + if ( id == -1 ){ + done = true; + }else{ + if ( (id<0) || (id>=MAX_WEAPONS) ) + done = true; + else{ + WeaponHash[id] = parent_id; + } + } + } + + va_end (marker); +} + +// DMFCBase::AddWeaponHashArray +// +// Since one weapon may actually consist of many weapons, in order to save space you can create +// one weapon where all those other weapon id's will be mapped to it...use WeaponHash[id] to +// get the actual weapon. +void DMFCBase::AddWeaponHashArray(char *parent,int count,char **array) +{ + ASSERT(parent!=NULL); + + int parent_id = DLLFindWeaponName(IGNORE_TABLE(parent)); + if(parent_id==-1){ + mprintf((0,"Unable to find parent weapon ID in AddWeaponHash (%s)\n",parent)); + return; + } + + WeaponHash[parent_id] = parent_id; + + int index = 0; + while(index=0 && id=0 && pnumnext){ + c = c->next; + } + c->next = (tBanItem *)malloc(sizeof(tBanItem)); + c = c->next; + if(!c) + return; + } + c->next = NULL; + memcpy(&c->addr,&NetPlayers[pnum].addr,sizeof(network_address)); + strncpy(c->callsign,Players[pnum].callsign,MAX_CALLSIGN_SIZE-1); + c->callsign[MAX_CALLSIGN_SIZE-1] = '\0'; + c->tracker_id[0] = '\0'; + + if(IsMasterTrackerGame() ) { + strcpy(c->tracker_id,Players[pnum].tracker_id); + } + + m_iNumBanPlayers++; + }else{ + mprintf((0,"Unable to ban player...pnum not valid\n")); + DPrintf(DTXT_DEDS_BAN_ERROR); + } +} + + +// DMFCBase::RemoveAllBans +// +// Removes all the temp bans +void DMFCBase::RemoveAllBans(void) +{ + tBanItem *c,*n; + c = n = m_BanList; + while(c){ + n = c->next; + free(c); + c = n; + } + m_BanList = NULL; + m_iNumBanPlayers = 0; +} + +// DMFCBase::GetNumBannedPlayers +// +// Returns the number of players banned from the server +int DMFCBase::GetNumBannedPlayers(void) +{ + return m_iNumBanPlayers; +} + +// DMCBase::GetBannedPlayerCallsign +// +// Returns the callsign of the banned player at position index, NULL on error +char *DMFCBase::GetBannedPlayerCallsign(int index) +{ + if(index<0 || index>=m_iNumBanPlayers) + return NULL; + + tBanItem *c; + c = m_BanList; + while(c){ + if(index==0){ + return c->callsign; + } + index--; + c = c->next; + } + return NULL; +} + +// DMFCBase::RemoveBan +// +// Removes a temp ban on given the ban #...returns true on success +bool DMFCBase::RemoveBan(int index) +{ + if(index<0||index>=m_iNumBanPlayers) + return false; + + tBanItem *c,*p; + c = m_BanList; + p = NULL; + while(c){ + if(!index){ + //we found em, remove em + if(p){ + p->next = c->next; + free(c); + m_iNumBanPlayers--; + }else{ + m_BanList = c->next; + free(c); + m_iNumBanPlayers--; + } + return true; + } + p = c; + c = c->next; + index--; + } + return false; +} + +// DMFCBase::IsPlayerBanned +// +// returns true is the given pnum is banned from the game +bool DMFCBase::IsPlayerBanned(int pnum) +{ + mprintf((0,"Checking a ban on a player...")); + + if(pnum<0 || pnum>=DLLMAX_PLAYERS){ + mprintf((0,"Playernum not valid\n")); + return false; + } + + if(IsAddressBanned(&NetPlayers[pnum].addr,Players[pnum].tracker_id)){ + return true; + } + + return false; +} + +// DMFCBase::IsAddressBanned +// +// returns true if the given address is banned from the game +bool DMFCBase::IsAddressBanned(network_address *addr,char *tracker_id) +{ + tBanItem *c; + c = m_BanList; + + mprintf((0,"Checking a ban on an address...")); + + while(c){ + if( addr && CompareNetworkAddress(&c->addr,addr,false)){ + mprintf((0,"Player addr IS BANNED\n")); + return true; + } + if(*m_bTrackerGame) + { + if( tracker_id && !strcmp(tracker_id,c->tracker_id) ){ + mprintf((0,"Player tid IS BANNED\n")); + return true; + } + } + + c = c->next; + } + + if(!addr){ + mprintf((0,"Player not banned\n")); + return false; + } + + //ok, the player doesn't have a temp ban on him, now, if it's tcp/ip, check + //the hosts.allow/.deny + + if(addr->connection_type!=NP_TCP){ + mprintf((0,"Player not banned\n")); + return false; + } + + unsigned long address; + tHostsNode *curr; + + memcpy(&address, &addr->address, 4); + + //check the allow list, if the address is specified on this list, + //than it isn't banned + curr = m_AllowList; + while(curr){ + if( (address&curr->mask)==(curr->ip&curr->mask) ){ + //its a match + mprintf((0,"Player not banned\n")); + return false; + } + curr = curr->next; + } + + //check the deny list, if the address is specified on this list, + //than it is banned + curr = m_DenyList; + while(curr){ + if( (address&curr->mask)==(curr->ip&curr->mask) ){ + //its a match + mprintf((0,"Player IS banned\n")); + return true; + } + curr = curr->next; + } + + mprintf((0,"Player not banned\n")); + return false; +} + + +// DMFCBase::DoDamageToPlayer +// +// Server only...applies damage to player +void DMFCBase::DoDamageToPlayer(int pnum,int type, float amount,bool playsound) +{ + if(GetLocalRole()!=LR_SERVER) + return; + if(!CheckPlayerNum(pnum)) + return; + if(pnum==GetPlayerNum()){ + //apply it to myself + if(amount<=0){ + //just add it...no damage sound + Objects[Players[pnum].objnum].shields -= amount; + }else{ + DLLApplyDamageToPlayer (&Objects[Players[pnum].objnum],NULL,type,amount,1,255,playsound); + } + }else{ + //add it to additional damage + DLLApplyDamageToPlayer (&Objects[Players[pnum].objnum],NULL,type,amount,1,255,playsound); + } +} + +// DMFCBase::StartUIWindow +// +// Prepares the game so that you can display UI stuff +void DMFCBase::StartUIWindow(int id,void *user_data) +{ + if(m_iUIWindowID!=-1){ + mprintf((0,"DMFCBase::StartUIWindow(): A Window ID (0x%X) is already defined\n",m_iUIWindowID)); + return; + } + + *Game_interface_mode=GAME_DLL_INTERFACE; + m_iUIWindowID = id; + m_UIUserData = user_data; +} + +// DMFCBase::EnableStatisticalMessages +// +// Turns on or off the statistical HUD messages that appear due to a player death +void DMFCBase::EnableStatisticalMessages(bool on) +{ + if(on) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPSTATHUDMSGS); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPSTATHUDMSGS); +} + +// DMFCBase::GetTimeInGame +// +// Returns the totaltime the player has been in the game at the moment you call +// this function. Pass in the player record slot. +float DMFCBase::GetTimeInGame(int slot) +{ + if(slot<0 || slot>=MAX_PLAYER_RECORDS) + return 0; + + player_record *pr = PRec_GetPRecord(slot); + + if(!pr || pr->state==STATE_EMPTY) + return 0; + + float ti = pr->total_time_in_game; + + if(pr->state==STATE_INGAME) + ti += RealGametime - pr->start_time; + + return ti; +} + +// DMFCBase::GetTimeString +// +// Returns a string that contains a formated time (i.e. 32 seconds, 1:15 minutes, 3:21:23 hours) +char *DMFCBase::GetTimeString(float sec) +{ + static char buffer[20]; + int minutes,seconds,hours; + minutes = seconds = hours = 0; + + seconds = (int) sec; + + //we have seconds, so convert to minutes:seconds + minutes = seconds/60; + seconds = seconds%60; + + //now we have minutes and seconds, so convert minutes to hours:minutes + hours = minutes/60; + minutes = minutes%60; + + //now form our string hh:mm:ss + if(hours){ + if(hours==1 && minutes==0 && seconds==0) + sprintf(buffer,DTXT_TIME_HOUR); + else + sprintf(buffer,DTXT_TIME_HOURS,hours,minutes,seconds); + }else if(minutes){ + if(minutes==1 && seconds==0) + sprintf(buffer,DTXT_TIME_MINUTE); + else + sprintf(buffer,DTXT_TIME_MINUTES,minutes,seconds); + }else{ + if(seconds==1) + sprintf(buffer,DTXT_TIME_SECOND); + else + sprintf(buffer,DTXT_TIME_SECONDS,seconds); + } + return buffer; +} + +// DMFCBase::PauseRealGameTime +// +// Pauses/unpauses the realgame time +void DMFCBase::PauseRealGameTime(bool enable) +{ + if(GetLocalRole()!=LR_SERVER) + return; + + if(enable) + { + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_PAUSETIME); + SendControlMessageToPlayer(SP_ALL,CM_PAUSETIME); + }else + { + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_PAUSETIME); + SendControlMessageToPlayer(SP_ALL,CM_UNPAUSETIME); + } +} + + +/* + ******************************************************************************** + * * + * Input Command Functions * + * * + ******************************************************************************** + * +*/ + +// DMFCBase::InputCommandHandle +// +// Goes through the list of input commands and calls the associated handle (if it exists). Returns +// true if it called a handler, else false. +bool DMFCBase::InputCommandHandle(char *command_string) +{ + ASSERT(command_string!=NULL); + + //make sure it's a valid command (starts with a $) + if(command_string[0]!='$') + return false; + + /* + if(!stricmp(command_string,"$help dump")) + { + mprintf((0,"Dumping help commands to file\n")); + tInputCommandNode *node = m_InputCommandRootNode; + + CFILE *file; + DLLOpenCFILE(&file,"C:\\DMFCHelp.txt","wt"); + + if(!file) + return false; + + char buffer[1024]; + + while(node){ + + sprintf(buffer,"$%s",node->data); + DLLcf_WriteString(file,buffer); + DLLcf_WriteString(file,"-------------------------------------------"); + sprintf(buffer,"%s\n",node->desc); + DLLcf_WriteString(file,buffer); + node = node->next; + } + + DLLcfclose(file); + } + */ + + //now extract the command + char command[64],index = 0; + char *p = &command_string[1]; + + //lowercase the command_string + while(*p && *p!=' '){ + if( (*p>='A') && (*p<='Z') ){ + *p = tolower(*p); + } + p++; + } + + p = &command_string[1]; + + //break off the first string + while( (*p) && (*p!=' ') ){ + ASSERT(index<64); + command[index] = *p; + p++; index++; + } + command[index] = '\0'; //tack on an ending NULL + + //now go through our list and see if we can find the command added by the user + tInputCommandNode *node = m_InputCommandRootNode; + while(node){ + if(!stricmp(node->data,command)){ + //we found a match! + if(node->handler){ + (*node->handler)(command_string); + return true; + } + return false; //for some reason the handler is NULL (it should never happen) + } + node = node->next; + } + + //if we got here than no nodes match + return false; +} + +// DMFCBase::InputCommandFree +// +// Frees up all memory allocated for the input commands ($ messages) +void DMFCBase::InputCommandFree(void) +{ + tInputCommandNode *next,*node = m_InputCommandRootNode; + + //Destroy the linked list of nodes + while(node){ + next = node->next; + if(node->data) + free(node->data); + if(node->desc) + free(node->desc); + free(node); + node = next; + } + + //set the root to NULL to finish up + m_InputCommandRootNode = NULL; +} + + +// DMFCBase::InputCommandInit +// +// Initializes the variables and data for the input commands. Default DMFC commands are to +// be placed in here. +void DMFCBase::InputCommandInit(void) +{ + if(AddInputCommand(DTXT_IC_ALLOWTEAMCHANGE,DTXT_IC_ALLOWTEAMCHANGED,DMFCInputCommand_AllowTeamChange,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_AUTOBALANCE,DTXT_IC_AUTOBALANCED,DMFCInputCommand_AutoBalance,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_AUTOSAVEDISC,DTXT_IC_AUTOSAVEDISCD,DMFCInputCommand_AutoSaveDisconnect,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_AUTOSAVELEVEL,DTXT_IC_AUTOSAVELEVELD,DMFCInputCommand_AutoSaveLevel,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_BALANCE,DTXT_IC_BALANCED,DMFCInputCommand_Balance,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_BAN,DTXT_IC_BAND,DMFCInputCommand_Ban,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_BANLIST,DTXT_IC_BANLISTD,DMFCInputCommand_BanList)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_CHANGETEAM,DTXT_IC_CHANGETEAMD,DMFCInputCommand_ChangeTeam,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_ENDLEVEL,DTXT_IC_ENDLEVELD,DMFCInputCommand_EndLevel,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_HELP,DTXT_IC_HELPD,DMFCInputCommand_Help)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_HUDNAME,DTXT_IC_HUDNAMED,DMFCInputCommand_HudCallsigns)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_KICK,DTXT_IC_KICKD,DMFCInputCommand_Kick,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_KILLMSGFILTER,DTXT_IC_KILLMSGFILTERD,DMFCInputCommand_KillMsgFilter)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_NETGAMEINFO,DTXT_IC_NETGAMEINFO,DMFCInputCommand_NetGameInfo)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_OBSERVER,DTXT_IC_OBSERVERD,DMFCInputCommand_Observer)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_PIGGYBACK,DTXT_IC_PIGGYBACKD,DMFCInputCommand_Piggyback)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_PLAYERINFO,DTXT_IC_PLAYERINFOD,DMFCInputCommand_PlayerInfo)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_PLAYERS,DTXT_IC_PLAYERSD,DMFCInputCommand_Players)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_REHASH,DTXT_IC_REHASHD,DMFCInputCommand_Rehash,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_REMOTE,DTXT_IC_REMOTED,DMFCInputCommand_Remote)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_REMOTEADMIN,DTXT_IC_REMOTEADMIND,DMFCInputCommand_RemoteAdmin,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_REMOTEADMINLOGOUT,DTXT_IC_REMOTEADMINLOGOUTD,DMFCInputCommand_RemoteAdminLogout)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_REMOTEADMINPASS,DTXT_IC_REMOTEADMINPASSD,DMFCInputCommand_RemoteAdminPass,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_REMOVEBAN,DTXT_IC_REMOVEBAND,DMFCInputCommand_RemoveBan,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SAVESTATS,DTXT_IC_SAVESTATSD,DMFCInputCommand_SaveStats,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SCORES,DTXT_IC_SCORESD,DMFCInputCommand_Scores)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SERVERHUDNAMES,DTXT_IC_SERVERHUDNAMESD,DMFCInputCommand_ServerHudCallsigns,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SETGOALLIMIT,DTXT_IC_SETGOALLIMITD,DMFCInputCommand_SetGoalLimit,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SETMAXPLAYERS,DTXT_IC_SETMAXPLAYERSD,DMFCInputCommand_SetMaxPlayers,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SETPPS,DTXT_IC_SETPPSD,DMFCInputCommand_SetPPS,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SETRESPAWNTIME,DTXT_IC_SETRESPAWNTIMED,DMFCInputCommand_SetRespawnTime,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SETTEAMNAME,DTXT_IC_SETTEAMNAMED,DMFCInputCommand_SetTeamName,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_SETTIMELIMIT,DTXT_IC_SETTIMELIMITD,DMFCInputCommand_SetTimeLimit,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_STATMSGS,DTXT_IC_STATMSGSD,DMFCInputCommand_StatMsgs)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_TEAM,DTXT_IC_TEAMD,DMFCInputCommand_Team)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_WAIT,DTXT_IC_WAITD,DMFCInputCommand_Wait,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); + + if(AddInputCommand(DTXT_IC_WARP,DTXT_IC_WARPD,DMFCInputCommand_Warp,true)<1) + mprintf((0,"DMFC Warning: Error Adding Input Command\n")); +} + +// Returns true if the input command can be called remotely +// +// Returns 1 if the input command can be called remotely +// Returns -1 if the input command is not found +// Returns 0 if the input command can be called remotely +int DMFCBase::CanInputCommandBeUsedRemotely(char *command) +{ + tInputCommandNode *node = m_InputCommandRootNode; + + while(node){ + if(!stricmp(node->data,command)){ + //we found a match! + return (node->allow_remote)?1:0; + } + node = node->next; + } + + return -1; +} + +// Displays dedicated server help +void DMFCBase::DisplayInputCommandHelp(char *s) +{ + if(!IAmDedicatedServer()) + return; + + tInputCommandNode *node = m_InputCommandRootNode; + + if(s[0]=='\0'){ + char buffer[80]; + DPrintf(DTXT_IC_HEADEERLIST); + DPrintf(DTXT_IC_HEADERINSTRUCT); + //just display the list of commands + while(node){ + memset(buffer,' ',80); + memcpy(&buffer[0],node->data,strlen(node->data)); + node = node->next; + if(node){ + memcpy(&buffer[30],node->data,strlen(node->data)); + int pos = 30 + strlen(node->data); + if(pos<=78){ + buffer[pos] = '\n'; + buffer[pos+1] = '\0'; + } + } + buffer[78] = '\n'; + buffer[79] = '\0'; + + DPrintf(buffer); + + if(node) + node = node->next; + } + + return; + } + + //display help description + while(node){ + if(!stricmp(node->data,s)){ + //we found a match! + DPrintf("%s:\n",node->data); + if(node->desc) + DPrintf("%s\n",node->desc); + else + DPrintf(DTXT_IC_NOADDITIONALINFO); + return; + } + node = node->next; + } + + DPrintf(DTXT_IC_COMMANDNOTFOUND); + +} + +//copies a string from src to dest, making dest all lower case +void strlowcpy(char *dest,const char *src) +{ + ASSERT((dest!=NULL) && (src!=NULL)); + char *s,*d; + s = (char *)src; + d = dest; + + while(*s){ + *d = tolower(*s); + s++; d++; + } + //do the final NULL + *d = '\0'; +} + +// DMFCBase::AddInputCommand +// +// When the client (or dedicated server) types a message that begins with a $, DMFC will look through +// all the values you passed to AddInputCommand() and see if any match. If so it passes the entire +// command string to the given function handler. Returns 1 on success, -1 if out of memory, 0 if it already +// exists. These commands are not case sensitive. +// Ex. AddInputCommand("team"); //this handles all the '$team' passed in +signed char DMFCBase::AddInputCommand(char *command,char *description,void (*handler)(char *),bool allow_remotely) +{ + ASSERT(command!=NULL); + ASSERT(handler!=NULL); + + //traverse to the end of the list checking for duplicates along the way + tInputCommandNode *node = m_InputCommandRootNode; + + if(!node){ + //there is none currently in the list, add it now + node = (tInputCommandNode *)malloc(sizeof(tInputCommandNode)); + if(!node){ + return -1; + } + //fill in data + node->next = NULL; + node->handler = handler; + node->allow_remote = allow_remotely; + node->data = (char *)malloc(strlen(command)+1); + if(!node->data){//out of memory + free(node); + return -1; + } + strlowcpy(node->data,command); + + node->desc = (char *)malloc(strlen(description)+1); + if(node->desc) + strcpy(node->desc,description); + + //all went ok + m_InputCommandRootNode = node; + return 1; + } + + while(node->next){ + //check this node to make sure it's not a duplicate + if(!stricmp(node->data,command)){ + //we found a duplicate + return 0; + } + node = node->next; + } + + //we need to check the current node since while(node->next) breaks out before checking the last node + if(!stricmp(node->data,command)){ + //we found a duplicate + return 0; + } + + //it looks like everything went ok, now just create a new node and tack it on to the end + //of the list. All that can go wrong now is an out of memory error. + tInputCommandNode *temp; + temp = (tInputCommandNode *)malloc(sizeof(tInputCommandNode)); + if(!temp){//out of memory + return -1; + } + temp->next = NULL; + temp->handler = handler; + temp->allow_remote = allow_remotely; + temp->data = (char *)malloc(strlen(command)+1); + if(!temp->data){//out of memory + free(temp); + return -1; + } + strlowcpy(temp->data,command); + + temp->desc = (char *)malloc(strlen(description)+1); + if(temp->desc) + strcpy(temp->desc,description); + + //now tack it on the end of the list and thats all + node->next = temp; + return 1; +} + +// DMFCBase::DisconnectMe +// +// Disconnects yourself from the game. It should be called when you are kicked or banned to make the +// whole process a bit nicer/cleaner. Even if it doesn't get called when kicked, you will still +// eventually disconnect. +void DMFCBase::DisconnectMe(void) +{ + Netgame->flags |= NF_EXIT_NOW; +} + +// Does a check on on the pinfo info making sure it is valid +void DMFCBase::CheckPInfo() +{ + for(int slot=0;slotstate!=STATE_EMPTY){ + //now go through the victim list and check + tPKillerInfo *node; + + ASSERT(pr->pinfo!=NULL); + node = ((PInfo *)pr->pinfo)->GetFirstKiller(); + + while(node){ + ASSERT(node->slot>=0 && node->slot<64); + node = node->next; + } + } + } +} + +// DMFCBase::EnableOnScreenMenu +// +// Turns on/off the onscreen menu +void DMFCBase::EnableOnScreenMenu(bool enable) +{ + if(enable) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_INOPTIONS); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_INOPTIONS); +} + +// DMFCBase::EnableAutoSaveLevelEnd +// +// Disables/Enables the autosaving of game stats to file on level end +void DMFCBase::EnableAutoSaveLevelEnd(bool enable) +{ + if(enable) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVELEVELEND); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVELEVELEND); +} + +// DMFCBase::EnableAutoSaveDisconnect +// +// Disables/Enables the autosaving of game stats to file on disconnect from the game +void DMFCBase::EnableAutoSaveDisconnect(bool enable) +{ + if(enable) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVEDISC); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_AUTOSAVEDISC); +} + +// DMFCBase::GenerateStatFilename +// +// Given the following information it will return a full path to what +// the recommended filename to save stats to should be. +// root = Multiplayer DLL Name (filename will start with this) +// end_of_level = pass true if this is the end of a level stats +void DMFCBase::GenerateStatFilename(char *filename,char *root,bool end_of_level) +{ + int level = Current_mission->cur_level; + char *name = Netgame->name; + struct tm *newtime; + time_t long_time; + + time( &long_time ); + newtime = localtime( &long_time ); + + char fname[256]; + char timestr[100]; + char day[8],month[8]; + + switch(newtime->tm_wday){ + case 0: + strcpy(day,DTXT_SUNDAY); + break; + case 1: + strcpy(day,DTXT_MONDAY); + break; + case 2: + strcpy(day,DTXT_TUESDAY); + break; + case 3: + strcpy(day,DTXT_WEDNESDAY); + break; + case 4: + strcpy(day,DTXT_THURSDAY); + break; + case 5: + strcpy(day,DTXT_FRIDAY); + break; + case 6: + strcpy(day,DTXT_SATURDAY); + break; + } + + switch(newtime->tm_mon){ + case 0: + strcpy(month,DTXT_JANUARY); + break; + case 1: + strcpy(month,DTXT_FEBRUARY); + break; + case 2: + strcpy(month,DTXT_MARCH); + break; + case 3: + strcpy(month,DTXT_APRIL); + break; + case 4: + strcpy(month,DTXT_MAY); + break; + case 5: + strcpy(month,DTXT_JUNE); + break; + case 6: + strcpy(month,DTXT_JULY); + break; + case 7: + strcpy(month,DTXT_AUGUST); + break; + case 8: + strcpy(month,DTXT_SEPTEMBER); + break; + case 9: + strcpy(month,DTXT_OCTOBER); + break; + case 10: + strcpy(month,DTXT_NOVEMBER); + break; + case 11: + strcpy(month,DTXT_DECEMBER); + break; + } + + sprintf(timestr,"%s._%s._%d_%d_%02d%02d",day,month,newtime->tm_mday,newtime->tm_year+1900,newtime->tm_hour,newtime->tm_min); + sprintf(fname,"%s_%s_%d_%s%s.stats",root,name,level,(end_of_level)?DTXT_ENDOFLEVELCONCAT:"",timestr); + + //remove all spaces (convert to _) + char *p = fname; + while( (*p) ){ + if( *p == ' ' ) + *p = '_'; + p++; + } + + //build the path info here + DLLddio_MakePath(filename,LocalD3Dir,"netgames",fname,NULL); +} + +// DMFCBase::IsPlayerObserver +// +// Returns true if the given pnum is currently an observer in the game, else returns false. +// If an invalid pnum is given, then it returns false +bool DMFCBase::IsPlayerObserver(int pnum) +{ + if(!CheckPlayerNum(pnum)) + return false; + + if(Objects[Players[pnum].objnum].type==OBJ_OBSERVER) + return true; + + return false; +} + +// DMFCBase::EnableOnScreenMenuBackground +// +// Enables/disables the onscreen menu background +void DMFCBase::EnableOnScreenMenuBackground(bool enable) +{ + if(enable) ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPMENUBACKGR); + else DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_DISPMENUBACKGR); +} + +// DMFCBase::DisplayNetGameInfo +// +// Displays information about the Netgame that is currently being played +// on to the screen. +void DMFCBase::DisplayNetGameInfo(int background_bmp,bool dedicated_server) +{ + //char server_config_name[PAGENAME_LEN]; + //char connection_name[PAGENAME_LEN]; + DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); + + int height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX])+1; + //int height = DLLRenderHUDGetTextHeight("X")+1; + ddgr_color color = GR_RGB(180,255,180); + int x,y; + y = 0; + x = 15; + int w,maxx = 0; + char fullbuffer[1024]; + + int topy = 0; + bool peertopeer = (bool)((Netgame->flags&NF_PEER_PEER)!=0); + bool permissable = (bool)((Netgame->flags&NF_PERMISSABLE)!=0); + + if( (m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR) && background_bmp>BAD_BITMAP_HANDLE){ + //draw the background bitmap + DLLrend_SetAlphaValue(255*0.85f); + DLLrend_SetZBufferState (0); + DLLrend_SetTextureType (TT_LINEAR); + DLLrend_SetLighting (LS_NONE); + DLLrend_SetAlphaType (AT_CONSTANT_TEXTURE); + DLLrend_DrawScaledBitmap(0,0,*Game_window_w,8*height,background_bmp,0,0,1,1,1.0,-1,NULL); + DLLrend_SetZBufferState(1); + } + + //Print out Title + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_HEADING); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_HEADING); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_HEADING); + color = GR_RGB(0,255,0); + topy = y; //save this position + + //Print out Game name + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_GAMENAME,Netgame->name); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_GAMENAME,Netgame->name); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_GAMENAME,Netgame->name); + + //Print out Mission name + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_MISSIONNAME,Netgame->mission); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_MISSIONNAME,Netgame->mission); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_MISSIONNAME,Netgame->mission); + + //Print out Script name + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_SCRIPTNAME,Netgame->scriptname); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_SCRIPTNAME,Netgame->scriptname); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_SCRIPTNAME,Netgame->scriptname); + + //Print out PPS + if(GetLocalRole()==LR_SERVER){ + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PPS,Netgame->packets_per_second); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_PPS,Netgame->packets_per_second); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_PPS,Netgame->packets_per_second); + }else{ + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PPSSERVER,Netgame->packets_per_second,NetPlayers[GetPlayerNum()].pps); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_PPSSERVER,Netgame->packets_per_second,NetPlayers[GetPlayerNum()].pps); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_PPSSERVER,Netgame->packets_per_second,NetPlayers[GetPlayerNum()].pps); + } + + //Print out Max Players + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_MAXPLAYERS,Netgame->max_players); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_MAXPLAYERS,Netgame->max_players); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_MAXPLAYERS,Netgame->max_players); + + //Accurate Collision Detection + bool use_acc_weap = (bool) ( (Netgame->flags&NF_USE_ACC_WEAP)!=0); + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_ACCURATECOLL,(use_acc_weap)?DTXT_MNUON:DTXT_MNUOFF); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_ACCURATECOLL,(use_acc_weap)?DTXT_MNUON:DTXT_MNUOFF); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + maxx = (w>maxx)?w:maxx; + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_ACCURATECOLL,(use_acc_weap)?DTXT_MNUON:DTXT_MNUOFF); + + //move to second column + y = topy; + x += (maxx+10); + + //Rotatitional Velocity + bool rot_vel_sent = (bool) ( (Netgame->flags&NF_SENDROTVEL)!=0); + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_SENDROTATIONAL,(rot_vel_sent)?DTXT_MNUON:DTXT_MNUOFF); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + sprintf(fullbuffer,DTXT_NGI_SENDROTATIONAL,(rot_vel_sent)?DTXT_MNUON:DTXT_MNUOFF); + DLLgrtext_Printf(x,y,fullbuffer); + w = DLLgrtext_GetTextLineWidth(fullbuffer); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_SENDROTATIONAL,(rot_vel_sent)?DTXT_MNUON:DTXT_MNUOFF); + + //Print out time limit + if(Netgame->flags&NF_TIMER){ + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_TIMELIMIT,Netgame->timelimit,(Netgame->timelimit==1)?DTXT_MINUTE:DTXT_MINUTES); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_TIMELIMIT,Netgame->timelimit,(Netgame->timelimit==1)?DTXT_MINUTE:DTXT_MINUTES); + + float t_left = 0; + t_left = (Netgame->timelimit*60.0f) - RealGametime; + + //Print out time left + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_TIMELEFT,GetTimeString(t_left)); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_TIMELEFT,GetTimeString(t_left)); + }else{ + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_NOTIMELIMIT); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_NOTIMELIMIT); + } + + //Print out Goal + if(Netgame->killgoal>0){ + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_GOALLIMIT,Netgame->killgoal); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_GOALLIMIT,Netgame->killgoal); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_GOALLIMIT,Netgame->killgoal); + }else{ + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_NOGOAL); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_NOGOAL); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_NOGOAL); + } + + //Print out Respawn time + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_RESPAWNTIME,GetTimeString(Netgame->respawn_time)); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_RESPAWNTIME,GetTimeString(Netgame->respawn_time)); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_RESPAWNTIME,GetTimeString(Netgame->respawn_time)); + + //Print out Client/Server Packet Loss + if( (GetPlayerNum()==0) || peertopeer ){ + //packet loss info not available + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PACKETLOSSNA); y+= height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_PACKETLOSSNA); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_PACKETLOSSNA); + }else{ + float packetloss = NetPlayers[GetPlayerNum()].percent_loss; + + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PACKETLOSS,packetloss); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_PACKETLOSS,packetloss); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_PACKETLOSS,packetloss); + } + + //Print out Architecture + //DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_NETWORKMODEL,(peertopeer)?DTXT_PEERTOPEER:DTXT_CLIENTSERVER); y+=height; + DLLgrtext_SetColor(color); + DLLgrtext_SetAlpha(255); + DLLgrtext_Printf(x,y,DTXT_NGI_NETWORKMODEL,(peertopeer)?DTXT_PEERTOPEER:((permissable)?DTXT_PERMISSABLE:DTXT_CLIENTSERVER)); + y+=height; + + if(dedicated_server) + DPrintf(DTXT_NGI_NETWORKMODEL,(peertopeer)?DTXT_PEERTOPEER:((permissable)?DTXT_PERMISSABLE:DTXT_CLIENTSERVER)); +} + +/* + **************************************************************** + * Registry Database Functions * + **************************************************************** + */ + +// DatabaseRegister +// Registers your multiplayer game with the database. This must be +// called before any other database function, or they will fail until this +// is called. +void DMFCBase::DatabaseRegister(char *name) +{ + if(name){ + strncpy(DatabaseRegisteredName,name,MAX_DBNAME_SIZE-1); + DatabaseRegisteredName[MAX_DBNAME_SIZE-1] = '\0'; + } +} + +// DatabaseRead +// Reads a string from the database +bool DMFCBase::DatabaseRead(const char *label,char *entry,int *entrylen) +{ + if(DatabaseRegisteredName[0]=='\0') + return false; + if(!label || !entry || !entrylen) + return false; + + int reg_len = strlen(DatabaseRegisteredName); + int copy = MAX_DBLABEL_SIZE - 1 - reg_len; + char buffer[MAX_DBLABEL_SIZE]; + + strcpy(buffer,DatabaseRegisteredName); + strncpy(&buffer[reg_len],label,copy); + buffer[MAX_DBLABEL_SIZE-1] = '\0'; + + return DatabaseRead1(buffer,entry,entrylen); +} + +// DatabaseRead +// Reads wordsize bytes from the database +bool DMFCBase::DatabaseRead(const char *label, void *entry, int wordsize) +{ + if(DatabaseRegisteredName[0]=='\0') + return false; + if(!label || !entry) + return false; + + int reg_len = strlen(DatabaseRegisteredName); + int copy = MAX_DBLABEL_SIZE - 1 - reg_len; + char buffer[MAX_DBLABEL_SIZE]; + + strcpy(buffer,DatabaseRegisteredName); + strncpy(&buffer[reg_len],label,copy); + buffer[MAX_DBLABEL_SIZE-1] = '\0'; + + return DatabaseRead2(buffer,entry,wordsize); +} + +// DatabaseRead +// Reads a bool from the database +bool DMFCBase::DatabaseRead(const char *label, bool *entry) +{ + if(DatabaseRegisteredName[0]=='\0') + return false; + if(!label || !entry) + return false; + + int reg_len = strlen(DatabaseRegisteredName); + int copy = MAX_DBLABEL_SIZE - 1 - reg_len; + char buffer[MAX_DBLABEL_SIZE]; + + strcpy(buffer,DatabaseRegisteredName); + strncpy(&buffer[reg_len],label,copy); + buffer[MAX_DBLABEL_SIZE-1] = '\0'; + + return DatabaseRead3(buffer,entry); +} + +// DatabaseWrite +// Writes/Updates a string to the database +bool DMFCBase::DatabaseWrite(const char *label, const char *entry, int entrylen) +{ + if(DatabaseRegisteredName[0]=='\0') + return false; + if(!label || !entry) + return false; + + int reg_len = strlen(DatabaseRegisteredName); + int copy = MAX_DBLABEL_SIZE - 1 - reg_len; + char buffer[MAX_DBLABEL_SIZE]; + + strcpy(buffer,DatabaseRegisteredName); + strncpy(&buffer[reg_len],label,copy); + buffer[MAX_DBLABEL_SIZE-1] = '\0'; + + return DatabaseWrite(buffer,entry,entrylen); +} + +// DatabaseWrite +// Writes/Updates a value to the database +bool DMFCBase::DatabaseWrite(const char *label, int entry) +{ + if(DatabaseRegisteredName[0]=='\0') + return false; + if(!label) + return false; + + int reg_len = strlen(DatabaseRegisteredName); + int copy = MAX_DBLABEL_SIZE - 1 - reg_len; + char buffer[MAX_DBLABEL_SIZE]; + + strcpy(buffer,DatabaseRegisteredName); + strncpy(&buffer[reg_len],label,copy); + buffer[MAX_DBLABEL_SIZE-1] = '\0'; + + return DatabaseWrite(buffer,entry); +} + +// CompareNetworkAddress +// +// Compare's two network addresses, returns true if they are the same, false if not. +// use_port: if this is true, than it will consider the port part of the network address +bool DMFCBase::CompareNetworkAddress(network_address *one,network_address *two,bool use_port) +{ + if(use_port) { + //compare the whole darned thing + if( !memcmp(one,two,sizeof(network_address)) ) + return true; + else + return false; + }else{ + //compare all but the port + if( !memcmp(one->address,two->address,6) && + !memcmp(one->net_id,two->net_id,4) && + !memcmp(&one->connection_type,&two->connection_type,sizeof(network_protocol) )){ + //looks the same to me + return true; + }else{ + //something didn't match + return false; + } + } + + return false; +} + +// IsMasterTrackerGame +// +// returns true if this game is being played on the master tracker +bool DMFCBase::IsMasterTrackerGame(void) +{ + return (bool)((*m_bTrackerGame)!=0); +} + +// ConvertLocalToServerObjnum +// +// Given an objnum, it will convert the number from your local objnum to the server's objnum +// It will return -1 on error +int DMFCBase::ConvertLocalToServerObjnum(int objnum) +{ + if(GetLocalRole()==LR_SERVER) + return (objnum<0||objnum>=MAX_OBJECTS)?-1:objnum; + + if( objnum < 0 || objnum >= MAX_OBJECTS ) + return -1; + + int s = Local_object_list[objnum]; + + return (s==65535)?-1:s; +} + +// ConvertServerToLocalObjnum +// +// Given an objnum from the server, this function will convert the objnum to your local objnum +// It will return -1 on error +int DMFCBase::ConvertServerToLocalObjnum(int objnum) +{ + if(GetLocalRole()==LR_SERVER) + return (objnum<0||objnum>=MAX_OBJECTS)?-1:objnum; + + if( objnum < 0 || objnum >= MAX_OBJECTS ) + return -1; + + int s = Server_object_list[objnum]; + + return (s==65535)?-1:s; +} + +// DMFCBase::AnnounceTeamChangeDeny +// +// Tells a player that team change request was denied +void DMFCBase::AnnounceTeamChangeDeny(int pnum) +{ + if(pnum==*Player_num){ + DLLAddHUDMessage(DTXT_CHANGETEAMDENIED); + }else{ + if(GetLocalRole()==LR_SERVER){ + //tell the client the change has been denied + SendControlMessageToPlayer(pnum,CM_TEAMCHANGEDENIED); + } + } +} + +//DMFCBase::SetDeathMessageFilter +// +// Sets the death message filter +void DMFCBase::SetDeathMessageFilter(int level) +{ + if(level<0) + level = 0; + if(level>2) + level = 2; + + if(level==m_iDeathMessageFilter) + return; + + m_iDeathMessageFilter = level; + switch(level){ + case DM_FILTER_FULL: + DLLAddHUDMessage(DTXT_KILLMSGFULL); + break; + case DM_FILTER_SIMPLE: + DLLAddHUDMessage(DTXT_KILLMSGSIMPLE); + break; + case DM_FILTER_NONE: + DLLAddHUDMessage(DTXT_KILLMSGNONE); + break; + }; +} + +// DMFCBase::MakeClientsWait +// +// If passed true than all joining clients are forced to wait until they are given the signal to +// join, either through SignalClientStart() or by passing false to MakeClientsWait +void DMFCBase::MakeClientsWait(bool wait) +{ + m_bMakeClientsWait = wait; + + if(!wait){ + //tell all the clients it's time to start + for(int i=0;i0.5){ + time_ac = 0; + show = !show; + } + + if(show){ + DLLgrtext_SetFont(Game_fonts[MENU_FONT_INDEX]); + + int height = DLLgrfont_GetHeight(Game_fonts[MENU_FONT_INDEX]); + + DLLRenderHUDTextFlags(HUDTEXT_CENTERED,GR_WHITE,255,0,0,240 - (height/2),DTXT_WAITFORSERVER); + DLLgrtext_Flush(); + } + + //Make sure we stay waiting... + Players[GetPlayerNum()].movement_scalar = 0; + Players[GetPlayerNum()].weapon_recharge_scalar = 99999.9f; +} + +//DMFCBase::IAmDedicatedServer +// +// Returns true if we are a dedicated server +bool DMFCBase::IAmDedicatedServer(void) +{ + return *Dedicated_server; +} + +//DMFCBase::IsPlayerDedicatedServer +// +// Returns true if the passed in pnum/player_record is a dedicated server +bool DMFCBase::IsPlayerDedicatedServer(int pnum) +{ + //since we are looking for a server, the pnum must be 0 + if(pnum!=0) + return false; + + if(Players[pnum].team==-1) + return true; + + return false; +} +bool DMFCBase::IsPlayerDedicatedServer(player_record *pr) +{ + if(pr->state==STATE_EMPTY) + return false; + + if(pr->state==STATE_DISCONNECTED) + return (pr->team==-1)?true:false; + + return IsPlayerDedicatedServer(pr->pnum); +} + + +//DMFCBase::GetPlayerTeam +// +// Returns the team of the player...call this instead of accessing .team directly +int DMFCBase::GetPlayerTeam(int pnum) +{ + return (Players[pnum].team==-1)?0:Players[pnum].team; +} + +//DMFCBase::ConvertHUDCoord +// +// Given an x,y based on a virtual 640x480 screen, this will convert it to the x,y that should be +// used based on the current screen size +void DMFCBase::ConvertHUDCoord(int x,int y,int *rx,int *ry) +{ + *rx = ((float)(x)*(*Hud_aspect_x)); + *ry = ((float)(y)*(*Hud_aspect_y)); +} + +//DMFCBase::GetPlayerLogoBmp +// +// Given a player_num, it will return a bitmap handle to that player's ship logo, or +// -1 if they don't have a logo for their ship. +// if is_vclip comes back as true, than it is not a bitmap handle, but a handle +// to a vclip (animated bitmap). It is an index into the DLLGameVClips[]. +int DMFCBase::GetPlayerLogoBmp(int player_num,bool *is_vclip) +{ + if(!CheckPlayerNum(player_num)) + return -1; //invalid pnum + + int texture_handle = Players[player_num].custom_texture_handle; + + if(GameTextures[texture_handle].bm_handle<=BAD_BITMAP_HANDLE) + return -1; + + *is_vclip = (GameTextures[texture_handle].flags&TF_ANIMATED)?true:false; + + return GameTextures[texture_handle].bm_handle; +} + +//DMFCBase::EnableShipLogos +// +// Disables or enables logo displaying for the client +void DMFCBase::EnableShipLogos(bool enable) +{ + if(enable){ + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_SHIPLOGOSENABLED); + }else{ + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_SHIPLOGOSENABLED); + } + + DLLMultiSetLogoState(enable); +} + +//DMFCBase::AreLogosEnabled +// +// returns true if ship logos are enabled +bool DMFCBase::AreLogosEnabled(void) +{ + return (bool)((m_iProtectedFlags&DMFC_PRF_SHIPLOGOSENABLED)!=0); +} + +//DMFCBase::EnableAudioTaunts +// +// Disables or enables audio taunts +void DMFCBase::EnableAudioTaunts(bool enable) +{ + DLLtaunt_Enable(enable); +} + +//DMFCBase::AreTauntsEnabled(void) +// +// returns true if audio taunts are enabled +bool DMFCBase::AreTauntsEnabled(void) +{ + return DLLtaunt_AreEnabled(); +} + +//DMFCBase::RespawnPlayer +// +// Takes the given player and respawns his at a random start point (in a team game, team respawn +// points are taken into consideration, so make sure the player is on the correct team before you +// respawn him. spew_everything, if false, will override spew_energy_and_shields +void DMFCBase::RespawnPlayer(int pnum,bool spew_energy_and_shields,bool spew_everything) +{ + if(!CheckPlayerNum(pnum)) + return; + + object *obj = &Objects[Players[pnum].objnum]; + + if(spew_everything) + DLLPlayerSpewInventory(obj,spew_energy_and_shields,spew_everything); + + DLLPlayerStopSounds(pnum); + + DLLInitPlayerNewShip(pnum,(spew_everything)?INVRESET_ALL:INVRESET_DEATHSPEW); + + if(pnum!=GetPlayerNum()) + return; + + int slot = DLLPlayerGetRandomStartPosition(pnum); + + DLLObjSetPos(obj,&Players[slot].start_pos,Players[slot].start_roomnum,&Players[slot].start_orient,NULL); +} + +//If you are going to create submenus you MUST use this function. along with: +//void SetState(int state); +//bool SetStateItemList(int count, ... ); for MIT_STATE items +MenuItem *DMFCBase::CreateMenuItem(char *title,char type,ubyte flags,void (*fp)(int), ... ) +{ + MenuItem *p; + + if(type!=MIT_CUSTOM && type!=MIT_STATE){ + p = new MenuItem(title,type,flags,fp); + return p; + }else{ + if(type==MIT_CUSTOM){ + tCustomMenu cm; + + va_list marker; + va_start(marker,fp); + memcpy(&cm,va_arg(marker,tCustomMenu *),sizeof(tCustomMenu)); + va_end(marker); + + p = new MenuItem(title,type,flags,fp,&cm); + return p; + }else{ + p = new MenuItem(title,type,flags,fp,0,0); //set initially to 0 state items + return p; + } + } + + return NULL; +} + +void ParseHostsFile(char *filename,tHostsNode **root) +{ + CFILE *file; + DLLOpenCFILE(&file,filename,"rt"); + + tHostsNode *curr = *root; + + if(!file) + return; + + char buffer[256]; + char save_buffer[256]; + + char s_ip[16],s_mask[16]; + unsigned long ip_address; + unsigned long mask; + char *ptr; + + while(!DLLcfeof(file)){ + DLLcf_ReadString(buffer,256,file); + + //handle the buffer of data + ptr = buffer; + + //parse white space + while(*ptr && (*ptr==' '||*ptr=='\t') ) ptr++; + strcpy(save_buffer,ptr); + + if(*ptr){ + //attempt to parse the dotted ip address (only 0-9,'.' and '*' allowed) + char *start = ptr; + + //check to make sure its in a valid form + + int dot_count = 0; + while(*ptr){ + if(*ptr=='.') + dot_count++; + if((*ptr<'0' || *ptr>'9') && *ptr!='*' && *ptr!='.' ) + goto error_parse; //invalid character + ptr++; + } + if(dot_count!=3) + goto error_parse; //not enough dots + ptr = start; + + //check the string length, no matter what it shouldn't be more than 15 + if(strlen(ptr)>15) + goto error_parse; + + //break up the string into seperate strings + while(*ptr){ + if(*ptr=='.') *ptr = '\0'; + ptr++; + } + ptr = start; + + //check to make sure the string length of each string isn't more than 3 + int length,count; + + for(count=0;count<4;count++){ + length = strlen(ptr); + if(length>3 || length==0) + goto error_parse; + + if(length!=1){ + //make sure it's a pure number + char *save = ptr; + while( *ptr ){ + if( !isdigit(*ptr)) + goto error_parse; + ptr++; + } + ptr = save; + int value =atoi(ptr); + if(value<0 || value>255) + goto error_parse; + } + + ptr += length+1; + } + ptr = start; + + //Its a valid IP address mask! yeah...*whew* that was fun + + int value; + char temp_str[3]; + s_ip[0] = s_mask[0] = '\0'; + + for(count=0;count<4;count++){ + length = strlen(ptr); + + //Mask + if( *ptr=='*'){ + value = 0; + }else{ + value = 255; + } + sprintf(temp_str,"%d",value); + strcat(s_mask,temp_str); + if(count!=3) + strcat(s_mask,"."); + + //IP Address + if( *ptr=='*'){ + value = 0; + }else{ + value = atoi(ptr); + } + sprintf(temp_str,"%d",value); + strcat(s_ip,temp_str); + if(count!=3) + strcat(s_ip,"."); + + ptr += length +1; + } + + //we now have a valid mask (s_mask) and a valid ip (s_ip) + mprintf((0,"IP: %s Mask: %s\n",s_ip,s_mask)); + ip_address = DLLnw_GetHostAddressFromNumbers(s_ip); + mask = DLLnw_GetHostAddressFromNumbers(s_mask); + + //add the node + if(!curr){ + curr = *root = (tHostsNode *)malloc(sizeof(tHostsNode)); + }else{ + curr->next = (tHostsNode *)malloc(sizeof(tHostsNode)); + curr = curr->next; + } + if(curr){ + curr->next = NULL; + curr->ip = ip_address; + curr->mask = mask; + } + + goto noerror_parse; + } + +error_parse: + mprintf((0,"Error parsing IP Address Mask: %s\n",save_buffer)); + +noerror_parse:; + + } + + DLLcfclose(file); + +} + +// DMFCBase::ReadInHostsAllowDeny +// +// Reads in the hosts.allow and hosts.deny files (if available) +void DMFCBase::ReadInHostsAllowDeny(void) +{ + char allow_fn[_MAX_PATH],deny_fn[_MAX_PATH]; + bool allow_exist,deny_exist; + + //build the path info here + DLLddio_MakePath(allow_fn,LocalD3Dir,"netgames","hosts.allow",NULL); + DLLddio_MakePath(deny_fn,LocalD3Dir,"netgames","hosts.deny",NULL); + + allow_exist = (bool)(DLLcfexist(allow_fn)!=0); + deny_exist = (bool)(DLLcfexist(deny_fn)!=0); + + m_DenyList = NULL; + m_AllowList = NULL; + + //parse away + if(deny_exist){ + mprintf((0,"Parsing hosts.deny\n")); + ParseHostsFile(deny_fn,&m_DenyList); + } + if(allow_exist){ + mprintf((0,"Parsing hosts.allow\n")); + ParseHostsFile(allow_fn,&m_AllowList); + } +} + +// DMFCBase::FreeHostsLists +// +// Frees all the memory allocated for the host allow/deny lists +void DMFCBase::FreeHostsLists(void) +{ + tHostsNode *curr = NULL,*next; + + curr = m_DenyList; + while(curr){ + next = curr->next; + free(curr); + curr = next; + } + + curr = m_AllowList; + while(curr){ + next = curr->next; + free(curr); + curr = next; + } +} + +// DMFCBase::RehashAllowDeny +// +// Flushes and reloads the hosts.allow/.deny lists +void DMFCBase::RehashAllowDeny(void) +{ + FreeHostsLists(); + ReadInHostsAllowDeny(); +} + +// DMFCBase::IsPlayerAlive +// +// Returns true is the given pnum is a player, flying around the level (not dying, dead or observing) +bool DMFCBase::IsPlayerAlive(int pnum) +{ + if(!CheckPlayerNum(pnum)) + return false; + + if(Objects[Players[pnum].objnum].type!=OBJ_PLAYER) + return false; + + if(Players[pnum].flags&(PLAYER_FLAGS_DYING|PLAYER_FLAGS_DEAD)) + return false; + return true; +} + + +// DMFCBase::ParseStartupScript +// +// +// Loads up the startup script and sets variables accordingly +void DMFCBase::ParseStartupScript(void) +{ + CFILE *file; + char path[_MAX_PATH]; + char buffer[256]; + int size; + bool ok_to_read; + + int autoexec_arg = -1; + + if( (autoexec_arg=DLLFindArg("-autoexec")) != 0 ) + { + // a specific autoexec.dmfc file was specified, use that + strcpy(path,GetGameArg(autoexec_arg+1)); + mprintf((0,"Override AUTOEXEC.DMFC to %s\n",path)); + }else + { + // use the default autoexec.dmfc + DLLddio_MakePath(path,LocalD3Dir,"netgames","autoexec.dmfc",NULL); + } + + DLLOpenCFILE(&file,path,"rt"); + if(!file) + return; + + while(!DLLcfeof(file)){ + ok_to_read = true; + + size = DLLcf_ReadString(&buffer[1],254,file); + buffer[255] = '\0'; + + if(size>=254){ + //invalid string, too long..trash the rest of the line + ok_to_read = false; + while(size>=254){ + size = DLLcf_ReadString(buffer,254,file); + } + } + + if(!ok_to_read){ + mprintf((0,"AUTOEXEC.DMFC: Line too long\n")); + }else{ + if(buffer[1]=='$') + { + InputCommandHandle(&buffer[1]); + }else + { + buffer[0] = '$'; //insert starting $ sign for input command + InputCommandHandle(buffer); + } + } + } + + DLLcfclose(file); +} + +dllinfo *DMFCBase::GetDLLInfoCallData(void) +{ + return Data; +} + +int DMFCBase::GetHighestRoomIndex(void) +{ + return *Highest_room_index; +} + +int DMFCBase::GetGameWindowW(void) +{ + return *Game_window_w; +} + +int DMFCBase::GetGameWindowH(void) +{ + return *Game_window_h; +} + +int DMFCBase::GetGameWindowX(void) +{ + return *Game_window_x; +} + +int DMFCBase::GetGameWindowY(void) +{ + return *Game_window_y; +} + +int *DMFCBase::GetGameFontTranslateArray(void) +{ + return Game_fonts; +} + +int DMFCBase::GetObserverModeBitmap(void) +{ + return hBitmapObserver; +} + +float DMFCBase::GetFrametime(void) +{ + return *Frametime; +} + +float DMFCBase::GetGametime(void) +{ + return *Gametime; +} + +float DMFCBase::GetRealGametime(bool *ispaused) +{ + if(ispaused) + { + *ispaused = (m_iProtectedFlags&DMFC_PRF_PAUSETIME)?true:false; + } + + return RealGametime; +} + +float *DMFCBase::GetShieldDeltaArray(void) +{ + return ShieldDelta; +} + +float DMFCBase::GetHudAspectX(void) +{ + return *Hud_aspect_x; +} + +float DMFCBase::GetHudAspectY(void) +{ + return *Hud_aspect_y; +} + +const char *DMFCBase::GetLocalD3Dir(void) +{ + return LocalD3Dir; +} + +const tMission *DMFCBase::GetCurrentMission(void) +{ + return Current_mission; +} + +room *DMFCBase::GetRooms(void) +{ + return Rooms; +} + +object *DMFCBase::GetObjects(void) +{ + return Objects; +} + +terrain_segment *DMFCBase::GetTerrainSegs(void) +{ + return Terrain_seg; +} + +netgame_info *DMFCBase::GetNetgameInfo(void) +{ + return Netgame; +} + +player *DMFCBase::GetPlayers(void) +{ + return Players; +} + +netplayer *DMFCBase::GetNetPlayers(void) +{ + return NetPlayers; +} + +ship *DMFCBase::GetShips(void) +{ + return Ships; +} + +weapon *DMFCBase::GetWeapons(void) +{ + return Weapons; +} + +texture *DMFCBase::GetGameTextures(void) +{ + return GameTextures; +} + +vclip *DMFCBase::GetGameVClips(void) +{ + return GameVClips; +} + +poly_model *DMFCBase::GetGamePolyModels(void) +{ + return Poly_models; +} + +ddgr_color *DMFCBase::GetPlayerColors(void) +{ + return Player_colors; +} + +game_controls DMFCBase::GetLastGameControls(void) +{ + return Last_game_controls; +} + +int *DMFCBase::GetPilotPicBitmapHandles(void) +{ + return PilotPicBmpHandles; +} + +void DMFCBase::GetViewerObjectPtr(object **v_obj) +{ + *v_obj = *Viewer_object; +} + +void DMFCBase::SetViewerObjectPtr(object *v_obj) +{ + *Viewer_object = v_obj; +} + +float DMFCBase::GetRenderZoom(void) +{ + return *Render_zoom; +} + +IMenuItem *DMFCBase::GetOnScreenMenu(void) +{ + return &Menu; +} + +tOSIRISModuleInit *DMFCBase::GetOsirisModuleData(void) +{ + return API.osiris_functions; +} + +vis_effect *DMFCBase::GetVisEffectArray(int **Highviseptr) +{ + if(Highviseptr) + *Highviseptr = Highest_vis_effect_index; + + return VisEffects; +} + +char *DMFCBase::GetGameArg(int arg) +{ + static char EMPTY_ARG[1] = ""; + + ASSERT(arg>=0 && arg=MAX_ARGS) + return (char *)EMPTY_ARG; + + char *arguments = (char *)GameArgs; + + int position = arg*MAX_CHARS_PER_ARG; + + return &arguments[position]; +} + +// DMFCBase::EnableLossGuage +// +// Turns on/off the Loss guage on the screen +void DMFCBase::EnableLossGuage(bool enable) +{ + bool peertopeer = (bool)((Netgame->flags&NF_PEER_PEER)!=0); + + if( (GetLocalRole()==LR_SERVER) || peertopeer ){ + return; + } + + LossGuageEnabled = enable; +} + +ddgr_color GetTriLinColor(float val,float low,float med,float hi,ddgr_color lowc,ddgr_color medc,ddgr_color hic) +{ + ddgr_color low_col,hi_col; + float perc; + + if(val<=low) + { + //we're pure lowc + perc = 1.0f; + low_col = hi_col = lowc; + }else if(val<=med) + { + //we're between lowc and medc + low_col = lowc; + hi_col = medc; + + perc = (val-low)/(med-low); + }else if(val<=hi) + { + //we're between medc and hic + low_col = medc; + hi_col = hic; + perc = (val-med)/(hi-med); + }else + { + //we're pure hic + perc = 1.0f; + low_col = hi_col = hic; + } + + if(perc<0.0f) perc = 0.0f; + if(perc>1.0f) perc = 1.0f; + + ddgr_color color_to_use; + int r,g,b; + float amount_low,amount_hi; + + amount_low = 1.0 - perc; + amount_hi = perc; + + r = GR_COLOR_RED(low_col)*amount_low + GR_COLOR_RED(hi_col)*amount_hi; + g = GR_COLOR_GREEN(low_col)*amount_low + GR_COLOR_GREEN(hi_col)*amount_hi; + b = GR_COLOR_BLUE(low_col)*amount_low + GR_COLOR_BLUE(hi_col)*amount_hi; + + color_to_use = GR_RGB(r,g,b); + + return color_to_use; +} + +// DMFCBase::LossGuageFrame +// +// Processes the Loss guage for the frame +void DMFCBase::LossGuageFrame(void) +{ + if(!LossGuageEnabled) + return; + + if(GetLocalRole()==LR_SERVER) + return; + + //Don't show if peer-to-peer + if (Netgame->flags & NF_PEER_PEER) + return; + + +#define LOW_COLOR GR_RGB(20,255,20) +#define MED_COLOR GR_RGB(255,255,40) +#define HI_COLOR GR_RGB(255,20,20) +#define LOW_LOSS 1.0f +#define MED_LOSS 5.0f +#define HI_LOSS 10.0f + + float packetloss = NetPlayers[GetPlayerNum()].percent_loss; + + ddgr_color loss_color = GetTriLinColor(packetloss,LOW_LOSS,MED_LOSS,HI_LOSS,LOW_COLOR,MED_COLOR,HI_COLOR); + ddgr_color ping_color = GetTriLinColor(NetPlayers[GetPlayerNum()].ping_time*1000.0f,0,200.0f,400.0f,LOW_COLOR,MED_COLOR,HI_COLOR); + + DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); + + int max_x = 0; + int y = 2; + + DLLRenderHUDText(loss_color,255,2,0,y,DTXT_NETWORK_LOSS); + max_x = DLLRenderHUDGetTextLineWidth(DTXT_NETWORK_LOSS); + + DLLRenderHUDText(ping_color,255,2,0,y+10,DTXT_NETWORK_PING); + max_x = max(max_x,DLLRenderHUDGetTextLineWidth(DTXT_NETWORK_PING)); + + max_x += 10; + DLLRenderHUDText(loss_color,255,0,2+max_x,y,"%.2f%%",packetloss); + DLLRenderHUDText(ping_color,255,0,2+max_x,y+10,"%dms",(int)(NetPlayers[GetPlayerNum()].ping_time*1000.0f)); +} + +level_info *DMFCBase::GetLevelInfo(void) +{ + return Level_info; +} + +// 1 - (num_levels) +void DMFCBase::WarpToLevel(int lev) +{ + if(GetLocalRole()!=LR_SERVER) + return; + + if(lev<1 || lev>Current_mission->num_levels) + return; + + *Multi_next_level = lev; + EndLevel(); +} + +void DMFCBase::SetPlayerTauntIndicator(int pnum) +{ + if(!CheckPlayerNum(pnum)) + return; + + DisplayTauntIndicator = true; + TauntIndicatorStartTime = *Gametime; + TauntIndicatorPlayerNum = pnum; +} + +void DMFCBase::DisplayTauntIndicatorFrame(void) +{ + if(!DisplayTauntIndicator) + return; + + if(!CheckPlayerNum(TauntIndicatorPlayerNum)) + { + DisplayTauntIndicator = false; + return; + } + + float alpha,perc; + int igt = floor((*Gametime)); + float igth = (*Gametime) - igt; + if(igth>0.5f) + { + igth -= 0.5f; + } + + if(igth>0.25f) + { + perc = 1.0f - ((igth-0.25)/0.25f); + }else + { + perc = (igth/0.25f); + } + + float base_alpha = 0.40f; + float range = 1.0f - base_alpha; + + alpha = base_alpha + (perc*range); + if(alpha1.0f) alpha = 1.0f; + + int bmp = TauntIndicatorBMP; + if(PilotPicBmpHandles[TauntIndicatorPlayerNum]>BAD_BITMAP_HANDLE) + { + bmp = PilotPicBmpHandles[TauntIndicatorPlayerNum]; + } + + if(bmp<=BAD_BITMAP_HANDLE) + { + DisplayTauntIndicator = false; + return; + } + + int bmh = 32; + int bmw = 32; + int x,y; + + x = 10; + y = 200; + + DLLrend_SetAlphaType (AT_CONSTANT_TEXTURE); + DLLrend_SetAlphaValue (alpha*255); + DLLrend_SetLighting (LS_NONE); + DLLrend_SetColorModel (CM_MONO); + DLLrend_SetOverlayType (OT_NONE); + DLLrend_SetFiltering (0); + DLLrend_SetWrapType(WT_CLAMP); + DLLrend_SetZBufferState(0); + DLLrend_SetTextureType(TT_LINEAR); + + DLLrend_DrawScaledBitmap (x,y,x+bmw,y+bmh,bmp,0,0,1,1,1,-1,NULL); + DLLrend_SetFiltering(1); + DLLrend_SetZBufferState(1); + + int font_height; + + DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); + font_height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]); + + x = 15 + bmw; + y = (y + (bmh/2)) - (font_height/2); + + DLLgrtext_SetColor(GR_GREEN); + DLLgrtext_SetAlpha(alpha*255.0f); + DLLgrtext_Printf(x,y,Players[TauntIndicatorPlayerNum].callsign); + + if( (*Gametime - TauntIndicatorStartTime) > 3.0f ) + DisplayTauntIndicator = false; +} + +// DMFCBase::SetMaxPlayerHardLimit +// +// Sets a hard limit to the max number of players allowed in the game +// changing the number of players can never go above this...defaults +// to DLLMAX_PLAYERS +void DMFCBase::SetMaxPlayerHardLimit(int max) +{ + if(GetLocalRole()!=LR_SERVER) + return; + if(max>DLLMAX_PLAYERS || max<1) + return; + + Hard_max_players = max; + if(Netgame->max_players>Hard_max_players) + { + Netgame->max_players = max; + DLLAddHUDMessage(DTXT_MAXPLAYERSSETMSG,max); + SendNetGameInfoSync(); + } +} + +// DMFCBase::MarkPlayersInGame +// +// Goes through all the player records and marks those that are in the game +// This is needed because at level end, all clients disconnect, so we lose +// that information. +void DMFCBase::MarkPlayersInGame(void) +{ + player_record *pr; + for(int i=0;istate==STATE_INGAME) + { + players_in_game[i] = pr->pnum; + }else + { + players_in_game[i] = -1; + } + } +} + +// DMFCBase::ResetPlayersInGame +// +// Resets the Players-in-game list +void DMFCBase::ResetPlayersInGame(void) +{ + for(int i=0;i=MAX_PLAYER_RECORDS) + return -1; + + return players_in_game[prec]; +} + +//DMFCBase::GetConnectingPlayerTeam +// +// Returns the team of a player just connecting, this is to be set by the +// game and is DMFC assumes that all players have the correct team on join +int DMFCBase::GetConnectingPlayerTeam(int slot) +{ + if(GetLocalRole()!=LR_SERVER) + return -2; + + int prec_num; + if(IsMasterTrackerGame()) + prec_num = PRec_FindPlayer(Players[slot].callsign,NULL,Players[slot].tracker_id); + else + prec_num = PRec_FindPlayer(Players[slot].callsign,&NetPlayers[slot].addr,NULL); + + int team = -2; + + if(prec_num!=-1){ + //we have a reconnecting player + + //we need to reconnect the player to the player records before we get the team + if(!PRec_ReconnectPlayerToSlot(slot,prec_num,Players,NetPlayers)) + { + mprintf((0,"Unable to reassign reconnecting player (%s) to Player Record slot #%d\n",Players[slot].callsign,prec_num)); + Int3(); + }else + { + team = PRec_GetPlayerTeam(slot); + + //disconnect the player again since he isn't in the game just yet + PRec_DisconnectPlayer(slot); + } + }else{ + //we have a new player + + if(slot==0 && IAmDedicatedServer()){ + team = -1; + }else + { + //get team assignment + if(m_iProtectedFlags&DMFC_PRF_AUTOTEAMSELECT){ + team = GetTeamForNewPlayer(slot,m_iNumTeams); + }else + { + team = RED_TEAM; + } + } + } + + mprintf((0,"CONNECTING PLAYER (%s): Team assigned to %d\n",Players[slot].callsign,team)); + + return team; +} + +// DMFCBase::SetDedicatedWait +// +// Sets the level wait time for the dedicated server. A dedicated server will make all +// clients wait until this time (in seconds) is up each level. +void DMFCBase::SetDedicatedWait(float time_secs) +{ + if(time_secs<0) + time_secs = 0; + + DedicatedLevelWait = time_secs; +} + +//DMFCBase::EnableTimelimitCountdown +// +// Enables/Disables the timelimit countdown (if there is a level time limit) +// Optionally you can specify what time to start the count down. (default 10) +void DMFCBase::EnableTimelimitCountdown(bool enable,int seconds) +{ + m_bDisplayTimelimitCountdown = enable; + + if(enable && seconds>0) + { + m_iTimeLimitCountdown = seconds; + } +} + +//DMFCBase::DisplayTimelimitCountdown +// +// Displays (if needed) the time limit countdown +void DMFCBase::DisplayTimelimitCountdown(void) +{ + if(!m_bDisplayTimelimitCountdown) + return; + + if(!(Netgame->flags&NF_TIMER)) + return; + + float time_left = (Netgame->timelimit*60.0f) - RealGametime; + + if(time_left>m_iTimeLimitCountdown || time_left<0) + return; + + //display a countdown! + DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); + DLLgrtext_SetColor(GR_WHITE); + DLLgrtext_SetAlpha(255); + + char buffer[256]; + sprintf(buffer,"Time Left: T - %d",(int)time_left); + + int font_height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]); + DLLgrtext_CenteredPrintf(0,font_height*10,buffer); +} + +//DMFCBase::TranslateTextMacro +// +// Given a macro with tokens, this function will replace the tokens +// and create a new string for display. +// All tokens begin with $$ (i.e. $$TEAMBASE) +void DMFCBase::TranslateTextMacro(const char *src,char *destination,int dest_size) +{ + ASSERT(destination!=NULL); + ASSERT(src!=NULL); + ASSERT(dest_size>=0); + + if(dest_size<=0) + return; + + char token[128]; + char token_string[512]; + + char *curr_dest = destination; + int src_idx = 0; + bool done = false; + + //now we need to go through the src and copy to destination + //while looking for tokens, and making sure we don't go over our size + while(dest_size>1 && !done) + { + switch(src[src_idx]) + { + case '$': + { + //possible token!, check the next character + if(src[src_idx+1]=='$') + { + //TOKEN!!! + src_idx+=2; //move to the beginning of the token + if(src[src_idx]==' ' || src[src_idx]=='\0') + { + //ok, maybe it isn't a token + *curr_dest = src[src_idx-2]; + curr_dest++; + dest_size--; + *curr_dest = src[src_idx-1]; + curr_dest++; + dest_size--; + *curr_dest = src[src_idx]; + if(*curr_dest=='\0') + { + done = true; + }else + { + src_idx++; + } + curr_dest++; + dest_size--; + }else + { + //process it, it's gotta be a token + + //extract the token out + char *ptr = token; + while(src[src_idx]!=' ' && src[src_idx]!='\0') + { + *ptr = src[src_idx]; + ptr++; + src_idx++; + } + *ptr = '\0'; + + //now replace the token... + *token_string = '\0'; + mprintf((0,"Looking for token for %s\n",token)); + CallOnGetTokenString(token,token_string,512); + + //make sure we don't go too far + int len = strlen(token_string); + if(len>(dest_size-1)) + len = dest_size-1; + + ptr = token_string; + + while(len>0) + { + *curr_dest = *ptr; + curr_dest++; + dest_size--; + ptr++; + len--; + } + } + }else + { + //just copy the character over + *curr_dest = src[src_idx]; + src_idx++; + curr_dest++; + dest_size--; + } + }break; + case '\0': + done = true; + break; + default: + { + //just copy the character over + *curr_dest = src[src_idx]; + src_idx++; + curr_dest++; + dest_size--; + }break; + } + } + + *curr_dest = '\0'; //NULL terminate +} + +//DMFCBase::SelectNextCameraView +// +// This function, given which window (corresponds to left, middle, right), switches the +// current view of the small camera windows on the screen +void DMFCBase::SelectNextCameraView(int window) +{ + if(window<0 || window>=NUM_CAMERA_VIEWS) + return; + DLLSelectNextCameraView(window); +} + +int DMFCBase::GetCameraViewType(int window) +{ + if(window<0 || window>=NUM_CAMERA_VIEWS) + return -1; + return Camera_view_mode[window]; +} + +// Given a generic object (OBJ_POWERUP,OBJ_ROBOT,OBJ_BUILDING or OBJ_CLUTTER) id +// in the range of 0 to MAX_OBJECT_IDS, this returns a pointer to it's information (see objinfo.h) +// It returns NULL if an invalid id is given (or it's not used) +object_info *DMFCBase::GetObjectInfo(int objinfo_id) +{ + if(objinfo_id<0 || objinfo_id>=MAX_OBJECT_IDS) + return NULL; + + if(Object_info[objinfo_id].type==OBJ_NONE) + return NULL; + + return &Object_info[objinfo_id]; +} diff --git a/netgames/dmfc/dmfccfg.cpp b/netgames/dmfc/dmfccfg.cpp new file mode 100644 index 000000000..f98caba08 --- /dev/null +++ b/netgames/dmfc/dmfccfg.cpp @@ -0,0 +1,647 @@ +/* +* $Logfile: /DescentIII/Main/dmfc/dmfccfg.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:57:20 $ +* $Author: kevinb $ +* +* Functions for a D3M config file +* +* $Log: dmfccfg.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb +* initial 1.5 import +* + * + * 7 9/12/99 12:06a Jeff + * fixed atoxi bug (stupid bug) + * + * 6 5/17/99 2:34p Ardussi + * changed to compile on Mac + * + * 5 5/13/99 4:55p Ardussi + * changes for compiling on the Mac + * + * 4 3/17/99 12:23p Jeff + * converted DMFC to be COM interface + * + * 3 9/21/98 7:11p Jeff + * made InputCommand interface API and moved existing input commands to + * the interface. Changed mprintf/ASSERT so they are valid in DMFC +* +* $NoKeywords: $ +*/ + + + +#include "gamedll_header.h" +#include "DMFC.h" +#include "dmfcinternal.h" + +#ifdef MACINTOSH +#include +#endif + +extern char **DMFCStringTable; +extern int DMFCStringTableSize; +extern char *_DMFCErrorString; +extern DMFCBase *basethis; +char *DMFCGetString(int d); + +CRegistry *reg = NULL; + +//DMFCBase::CFGOpen +// +// Opens the registry/cfg information for the DMFC game, see error codes for return values +int DMFCBase::CFGOpen(char *filename) +{ + if(!filename) + return CFG_NOCFGFILE; + + if(reg) + return CFG_ALREADYOPEN; + + reg = new CRegistry(filename); + if(!reg) + return CFG_OUTOFMEMORY; + + reg->Import(); + + return CFG_NOERROR; +} + +//DMFCBase::CFGClose +// +// Closes the registry information for the game. +void DMFCBase::CFGClose(void) +{ + if(reg){ + reg->Export(); + delete reg; + reg = NULL; + } +} + +//DMFCBase::CFGFlush +// +// Flushes out the registry information to fall +int DMFCBase::CFGFlush(void) +{ + if(reg){ + reg->Export(); + return CFG_NOERROR; + }else + return CFG_NOTOPEN; +} + +//DMFCBase::CFGCreateKey +// +// Creates a key in the registry +int DMFCBase::CFGCreateKey(char *name) +{ + if(reg){ + reg->CreateKey(name); + return CFG_NOERROR; + }else + return CFG_NOTOPEN; +} + +//DMFCBase::CFGLookupKey +// +// Sets the active key in the registry +int DMFCBase::CFGLookupKey(char *name) +{ + if(reg){ + if(reg->LookupKey(name)) + return CFG_NOERROR; + else + return CFG_KEYNOTFOUND; + }else + return CFG_NOTOPEN; +} + +//DMFCBase::CFGLookupRecord +// +// Looks up a record in the active key +int DMFCBase::CFGLookupRecord(char *record,void *data) +{ + if(reg){ + if(reg->LookupRecord(record,data)) + return CFG_NOERROR; + else + return CFG_RECORDNOTFOUND; + }else + return CFG_NOTOPEN; +} + + +//DMFCBase::CFGCreateRecord +// +// Create/overwrites a record in the active key. Type is either REGT_DWORD or REGT_STRING +int DMFCBase::CFGCreateRecord(char *name,char type,void *data) +{ + if(reg){ + if(reg->CreateRecord(name,type,data)) + return CFG_NOERROR; + else + return CFG_CANTCREATE; + }else + return CFG_NOTOPEN; +} + + +//Convert a string that represents a hex value into an int +#ifdef MACINTOSH +static +#endif +int axtoi(char *p) +{ + int value = 0; + while( (p) &&(*p)) + { + *p = toupper(*p); + if ( (*p>='0') && (*p<='9') ) + value = (value * 16) + ((*p)-'0'); + else if ( (*p>='A') && (*p<='F') ) + value = (value * 16) + (((*p)-'A')+10); + else + return 0; + p++; + } + return value; +} + +//Removes whitespace from the start of the given string. +//Returns a pointer to the first non-white character +char *CRegistry::SkipWhite(char *p) +{ + while (isspace(*p)) + p++; + return p; +} + +//Parses a quoted string +//Returns true if got string ok, else false +char *CRegistry::ParseString(char *p,char *buf,int bufsize,char sdelim,char edelim) +{ + char *save_p; + + p = SkipWhite(p); + + save_p = p; + + if (*p != sdelim) + { + return NULL; + }else + { + p++; //skip initial quote + } + + //Copy chars until endquote or out of space + while (*p && (*p != edelim) && --bufsize) + *buf++ = *p++; + + //Check for buffer overflow + if (bufsize <= 0) + { + return NULL; + } + + //Check for missing endquote + if (! *p) + { + return NULL; + } + + //Write terminator + *buf = 0; + + //Return new pointer (move over a char for end quote) + return p+1; +} + +//Parses a sequence of non-space characters +char *CRegistry::ParseToken(char *p,char *buf,int bufsize) +{ + char *save_p; + p = SkipWhite(p); + save_p = p; + + while (!isspace(*p) && (*p != ',') && *p && --bufsize) + *buf++ = *p++; + + *buf = 0; + + //Check for buffer overflow + if (bufsize <= 0) + { + return NULL; + } + + return p; +} + +#define PARSE_KEY(buf) do {ptr = ParseString(ptr,buf,sizeof(buf),'[',']'); } while(0) +#define PARSE_STRING(buf) do {ptr = ParseString(ptr,buf,sizeof(buf),'"','"'); } while (0) +#define PARSE_TOKEN(buf) do {ptr = ParseToken(ptr,buf,sizeof(buf)); } while (0) + +CRegistry::CRegistry(char *str) +{ + currentkey = root = NULL; + strcpy(name,str); +} + +CRegistry::~CRegistry() +{ + Destroy(); +} + +void CRegistry::Destroy(void) +{ + tKey *curr,*next; + curr = next = root; + + while(curr) + { + next = curr->next; + DestroyKey(curr); + curr = next; + } + root = NULL; +} + +void CRegistry::DestroyKey(tKey *key) +{ + tRecord *curr, *next; + curr = next = key->records; + + while(curr) + { + next = curr->next; + DestroyRecord(curr); + curr = next; + } + + free(key); +} + +void CRegistry::DestroyRecord(tRecord *record) +{ + if(record->data) + { + free(record->data); + record->data = NULL; + } + free(record); +} + +void CRegistry::Export() +{ + tKey *curr,*next; + curr = next = root; + CFILE *file; + DLLOpenCFILE(&file,name,"wt"); + + if(!file) + return; + + while(curr) + { + next = curr->next; + ExportKey(curr,file); + curr = next; + } + DLLcfclose(file); +} + +void CRegistry::ExportKey(tKey *key,CFILE *file) +{ + tRecord *curr, *next; + curr = next = key->records; + + //write out name + char buffer[258]; + sprintf(buffer,"[%s]",key->name); + DLLcf_WriteString(file,buffer); + while(curr) + { + next = curr->next; + ExportRecord(curr,file); + curr = next; + } +} + +void CRegistry::ExportRecord(tRecord *record,CFILE *file) +{ + int *dw; + char *st; + char buffer[512]; + switch(record->type){ + case REGT_STRING: + { + st = (char *)record->data; + sprintf(buffer,"\"%s\"=\"%s\"",record->name,st); + }break; + case REGT_DWORD: + { + dw = (int *)record->data; + sprintf(buffer,"\"%s\"=dword:%X",record->name,*dw); + }break; + }; + DLLcf_WriteString(file,buffer); +} + +bool CRegistry::Import() +{ + char buffer[500]; + char newbuff[500]; + CFILE *file; + char *ptr; + + DLLOpenCFILE(&file,name,"rt"); + + if(!file) + { + mprintf((0,"Unable to import %s\n",name)); + return false; + } + mprintf((0,"Importing %s\n",name)); + Destroy(); + + bool oktocreate; + //loop till we are done + while(!DLLcfeof(file)) + { + oktocreate = true; + char type = REGT_STRING; + + //read in the string + DLLcf_ReadString(buffer,500,file); + if(strlen(buffer)<=1) + continue; + + ptr = buffer; + if(DLLcfeof(file))//we are at the end of the file so there isn't data to continue + oktocreate = false; + + //see what we read, a key or record, if the first character is a [ than a key " is record + if(oktocreate) + { + if(buffer[0]=='[') + { + //Create a key! + PARSE_KEY(newbuff); + mprintf((0,"Found Key: |%s|\n",newbuff)); + CreateKey(newbuff); + }else if(buffer[0]=='\"') + { + //Create a record + //see what type of record by looking at whats after the = + char *p; + p = buffer; + bool done = false; + type = REGT_STRING; + while(!done) + { + if( (!p) || (!*p) ) + done = true; + if( (p) && (*p=='=')) + { + if(*(p+1)=='d') + type = REGT_DWORD; + else + type = REGT_STRING; + done = true; + } + p++; + } + + //now we "SHOULD" know the type, parse the info + char data[300]; + int idata; + + switch(type) + { + case REGT_STRING: + PARSE_STRING(newbuff); + ptr++; //blow by = + PARSE_STRING(data); + if(!CreateRecord(newbuff,REGT_STRING,data)) + { + mprintf((0,"Unable to create String record: %s\n",newbuff)); + } + else + { + mprintf((0,"Created String record %s = %s\n",newbuff,data)); + } + break; + case REGT_DWORD: + PARSE_STRING(newbuff); + ptr+=7; //blow by =dword: + PARSE_TOKEN(data); + idata = axtoi(data); + + if(!CreateRecord(newbuff,REGT_DWORD,&idata)) + { + mprintf((0,"Unable to create dword record: %s\n",newbuff)); + }else + { + mprintf((0,"Created dword record %s = %X\n",newbuff,idata)); + } + break; + } + }else + { + mprintf((0,"Expected [ or \"\n")); + } + } + } + DLLcfclose(file); + return true; +} + +void CRegistry::CreateKey(char *name) +{ + tKey *curr; + if(LookupKey(name)) + { + mprintf((0,"Key: %s already exists\n",name)); + return; + } + + if(!root) + { + root = (tKey *)malloc(sizeof(tKey)); + if(!root) + return; + curr = root; + }else + { + curr = root; + while(curr->next) + { + curr = curr->next; + } + + curr->next = (tKey *)malloc(sizeof(tKey)); + if(!curr->next) + return; + curr = curr->next; + } + + curr->next = NULL; + strcpy(curr->name,name); + curr->records = NULL; + currentkey = curr; +} + +bool CRegistry::LookupKey(char *name) +{ + tKey *curr; + curr = root; + while(curr) + { + if( !stricmp(name,curr->name) ) + { + //found a match + currentkey = curr; + return true; + } + curr = curr->next; + } + return false; +} + +tRecord *CRegistry::LookupRecord(char *record,void *data) +{ + if(!currentkey) + return NULL; + + tRecord *curr; + curr = currentkey->records; + while(curr) + { + if( !stricmp(record,curr->name) ) + { + //found the record + switch(curr->type) + { + case REGT_STRING: + char *st; + st = (char *)curr->data; + strcpy((char *)data,st); + break; + case REGT_DWORD: + int *dw; + dw = (int *)curr->data; + *((int *)data) = *dw; + break; + }; + return curr; + } + curr = curr->next; + } + return NULL; +} + +int CRegistry::GetDataSize(char *record) +{ + if(!currentkey) + return false; + tRecord *curr; + curr = currentkey->records; + while(curr) + { + if( !stricmp(record,curr->name) ) + { + //found the record + switch(curr->type) + { + case REGT_STRING: + return strlen((char *)curr->data)+1; + break; + case REGT_DWORD: + return sizeof(int); + break; + }; + return 0; + } + curr = curr->next; + } + return 0; +} + +bool CRegistry::CreateRecord(char *name,char type,void *data) +{ + if(!currentkey) + return false; + tRecord *curr; + + //first see if the record exists under this key + int datasize = GetDataSize(name); + if(datasize) + { + char *olddata = (char *)malloc(datasize); + if(olddata) + { + curr = LookupRecord(name,olddata); + if(curr) + { + //ok we have an old value, replace it! + mprintf((0,"Replacing %s\n",name)); + if(curr->data) + free(curr->data); + free(olddata); + curr->type = type; + + switch(type) + { + case REGT_STRING: + curr->data = malloc(strlen((char *)data)+1); + strcpy((char *)curr->data,(char *)data); + break; + case REGT_DWORD: + curr->data = malloc(sizeof(int)); + *((int *)curr->data) = *((int *)data); + break; + } + return true; + } + } + } + + //it is a new record + if(currentkey->records) + { + curr = currentkey->records; + while(curr->next) + curr = curr->next; + + curr->next = (tRecord *)malloc(sizeof(tRecord)); + if(!curr->next) + return false; + curr = curr->next; + }else + { + currentkey->records = (tRecord *)malloc(sizeof(tRecord)); + if(!currentkey->records) + return false; + curr = currentkey->records; + } + + curr->next = NULL; + strcpy(curr->name,name); + switch(type) + { + case REGT_STRING: + curr->data = malloc(strlen((char *)data)+1); + curr->type = REGT_STRING; + strcpy((char *)curr->data,(char *)data); + break; + case REGT_DWORD: + curr->data = malloc(sizeof(int)); + curr->type = REGT_DWORD; + *((int *)curr->data) = *((int *)data); + break; + }; + return true; +} diff --git a/netgames/dmfc/dmfcclient.cpp b/netgames/dmfc/dmfcclient.cpp new file mode 100644 index 000000000..e3a828a6e --- /dev/null +++ b/netgames/dmfc/dmfcclient.cpp @@ -0,0 +1,1211 @@ +/* +* $Logfile: /DescentIII/Main/dmfc/dmfcclient.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:57:20 $ +* $Author: kevinb $ +* +* Client Event handlers +* +* $Log: dmfcclient.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb +* initial 1.5 import +* + * + * 92 9/05/01 6:04p Matt + * Added code to save the user's preferred HUD name level setting even if + * the server bashes it down. + * + * 91 7/15/99 3:33p Jeff + * play a sound when a player disconnects/leaves the game + * + * 90 7/13/99 12:11p Jeff + * added some specific text taunt token decoding + * + * 89 7/13/99 10:05a Jeff + * text taunt token decoding + * + * 88 7/09/99 7:02p Jeff + * put in countdown timer for when a level is about to end + * + * 87 7/09/99 6:17p Jeff + * added $remoteadminlogout and $wait commands + * + * 86 7/09/99 2:53p Jeff + * handle gametime better (pause it when needed) if the server is 'waiting + * for players' + * + * 85 7/08/99 9:56p Jeff + * added event handler for weapon fired event + * + * 84 7/08/99 2:39a Jeff + * rough implementation of remote administration checked in. Still needs + * some polishing, but should work basically. + * + * 83 7/07/99 6:10p Jeff + * don't count the time that a player is in observer mode for death/kill + * messages + * + * 82 6/22/99 5:41p Jeff + * removed int3 (causing probs in Linux) + * + * 81 6/11/99 5:36p Jeff + * removed ai_info #ifdefs (better way of doing it) + * + * 80 6/10/99 12:41p Jeff + * no ai_info in non-Outrage builds + * + * 79 5/24/99 11:05p Jeff + * fixed bug for dedicated server team getting reset on endlevel + * + * 78 5/11/99 9:42p Jeff + * fixed bug changing teams while dead + * + * 77 5/10/99 2:43a Jeff + * handle new scheme of player's joining in a team game, where the team is + * set before player enters game in the main code, but the team is + * determined via event call to dmfc + * + * 76 5/08/99 4:45a Jeff + * fixed printscreen position + * + * 75 5/08/99 4:30a Jeff + * fixed sequencing bug where clients never got a level end event for the + * multiplayer games + * + * 74 5/07/99 5:16p Jeff + * fixed player disconnect at level end + * + * 73 5/07/99 2:21p Jeff + * fixed (?) not disconnecting clients on level end + * + * 72 5/04/99 8:46p Jeff + * display icon when someone plays an audio taunt + * + * 71 4/03/99 4:06p Jeff + * added loss/ping gauge + * + * 70 3/30/99 7:42p Jeff + * fixed a misspelling on a function name + * + * 69 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 68 3/22/99 1:55p Jeff + * make sure initialization happens (possible crashing) + * + * 67 3/17/99 12:23p Jeff + * converted DMFC to be COM interface + * + * 66 2/11/99 12:50a Jeff + * changed names of exported variables + * + * 65 2/09/99 3:32p Jeff + * table file parser takes quotes strings for force keywords + * + * 64 2/07/99 1:19a Jeff + * added new multiplayer game events EVT_GAMEOBJKILLED and + * EVT_GAMEOBJDESTROYED + * + * 63 2/05/99 8:24p Jeff + * added table file parser macros + * + * 62 2/04/99 7:17p Jeff + * put in sounds for hud messages + * + * 61 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 60 2/01/99 10:52p Jeff + * added sounds for kills (teammate and regular) + * + * 59 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 58 1/19/99 3:55a Jeff + * all strings localized out + * + * 56 1/15/99 3:54a Jeff + * exported a couple more functions. Added event handlers for Weapon + * collide event + * + * 55 1/12/99 2:55p Jeff + * added/finished the waiting for player's to join dialog + * + * 54 1/10/99 7:41p Jeff + * added initial version of waitforplayers dialog + * + * 53 1/07/99 5:01p Jeff + * added Int3's and updated all net games to use stats manager...correctly + * too + * + * 52 1/06/99 7:02p Jeff + * added a multiplayer event for game controls + * + * 51 12/23/98 6:38p Kevin + * All UDP data (except gamespy) now uses one (registered) port number + * + * 50 12/08/98 4:47p Jeff + * umm, various changes, fixed pilot pics so that they work for everyone + * now + * + * 49 12/08/98 3:29p Jeff + * updated the team control dialog so the server can determine if they + * want to make the clients wait + * + * 48 12/08/98 12:17p Jeff + * various changes that include an improved Team Control dialog (doesn't + * switch teams until exit..) and spew/respawn players that change teams + * + * 47 12/03/98 7:05p Jeff + * updated new stats + * + * 46 12/01/98 6:56p Jeff + * put in quick and dirty implementation of pilot pics for testing + * + * 45 11/19/98 5:56p Jeff + * added slider exported and improved Hoard + * + * 44 11/17/98 6:29p Jeff + * mod can specify whether or not to display the team setup dialog on team + * game start. Added a menu item to display team setup dialog in mid-game + * + * 43 11/16/98 5:35p Jeff + * removed log functions, added support for changing team names, fixed ctf + * to work better with different team names + * + * 42 11/11/98 7:19p Jeff + * changes made so that a dedicated server's team is always -1 (team game + * or not) + * + * 41 11/11/98 12:47p Jeff + * fixed bug where it would give team change messages in a non-team game + * + * 40 11/01/98 1:59a Jeff + * made a $help inputcommand for help in a dedicated server environment + * + * 39 10/30/98 12:47p Jeff + * cut down a couple bytes on memory usage + * + * 38 10/29/98 7:01p Jeff + * creation of team placement dialog. Moved TranslateEvent into DMFC + * + * 37 10/20/98 12:16p Jeff + * added death message filter, hud callsign filter + * + * 36 10/18/98 7:59p Jeff + * functions added to dmfc for client->server objnum matching. Banning + * now uses tracker id when available. + * + * 35 10/15/98 6:18p Jeff + * created the is player banned event, removed prejoin event + * + * 34 10/15/98 1:34p Jeff + * added scrollable onscreen menu. Remove ban in dmfc. prejoin event + * + * 33 10/13/98 2:15a Jeff + * created new event for when a player leaves a multiplayer game. Fixed + * some 'english' bugs in the network games. + * + * 32 10/11/98 2:57a Jeff + * added new multiplayer event EVT_GAME_INTERVAL, which is to be called on + * interval, and EVT_HUD_INTERVAL is only to be called when the hud is to + * be rendered + * + * 31 10/08/98 3:37p Jeff + * general improvements (Netgame info things, save to registry). Changes + * so it would send packets on NETSEQ_OBJECTS + * + * 30 10/05/98 2:49p Jeff + * + * 29 10/01/98 7:02p Jeff + * implemented colored HUD callsigns + * + * 28 10/01/98 11:30a Jeff + * made the observer mode events into just a client event + * + * 27 9/30/98 4:21p Jeff + * team changing is handled correctly + * + * 26 9/30/98 4:07p Jeff + * + * 25 9/30/98 3:50p Jeff + * general improvements (many) + * + * 24 9/28/98 5:05p Jeff + * made the statisitical death messages an option in the menu + * + * 23 9/25/98 7:57p Jeff + * removed CheckPInfo call + * + * 22 9/25/98 7:25p Jeff + * + * 21 9/25/98 4:50p Jeff + * + * 20 9/24/98 6:54p Jeff + * added DisconnectMe() and made use of it when you get kicked/banned + * + * 19 9/24/98 5:52p Jeff + * starting adding statistical death messages, checked in for testing + * + * 18 9/23/98 4:56p Jeff + * set teams always for 1 team game + * + * 17 9/23/98 4:17p Jeff + * basic changes/improvements, started changing death messages + * + * 16 9/21/98 7:11p Jeff + * made InputCommand interface API and moved existing input commands to + * the interface. Changed mprintf/ASSERT so they are valid in DMFC +* +* $NoKeywords: $ +*/ + + + +#include "gamedll_header.h" +#include "DMFC.h" +#include "dmfcinternal.h" + +extern char **DMFCStringTable; +extern int DMFCStringTableSize; +extern char *_DMFCErrorString; +extern DMFCBase *basethis; +char *DMFCGetString(int d); + +#define SND_MYTEAM_TEAMMATE_KILLED "PlayerDeath" +#define SND_NONTEAM_KILL "PlayerDeath" + +/* +$$TABLE_SOUND "PlayerDeath" +*/ + +//######################### Client allowed event handlers############################ + +// DMFCBase::OnClientPlayerKilled (Only called if server tells client to execute) +// +// Event handler for when a player gets killed, either by another player or some other way. +// killer_obj = object pointer to the object that killed the player +// victim_pnum = player number of the player that got killed +void DMFCBase::OnClientPlayerKilled(object *killer_obj,int victim_pnum) +{ + uint hash = 0xFFFFFFFF; + int kpnum; + player_record *kpr,*vpr; + + if(!killer_obj){ + kpnum = -1; + }else{ + if((killer_obj->type==OBJ_PLAYER)||(killer_obj->type==OBJ_GHOST)||(killer_obj->type==OBJ_OBSERVER)){ + kpnum = killer_obj->id; + + //now we need to extract out the weapon of the kill + if(Data->iParam!=-1) + hash = (uint)Data->iParam; + }else if(killer_obj->type==OBJ_ROBOT || (killer_obj->type == OBJ_BUILDING && killer_obj->ai_info)){ + //countermeasure kill + kpnum = GetCounterMeasureOwner(killer_obj); + }else + kpnum = -1; + } + + if(victim_pnum!=-1){ + if(kpnum==victim_pnum){ + //suicide + vpr = PRec_GetPRecordByPnum(victim_pnum); + if(vpr){ + vpr->dstats.suicides[DSTAT_LEVEL]++; + vpr->dstats.suicides[DSTAT_OVERALL]++; + } + UpdatePInfo(kpnum,kpnum,1); + }else{ + //regular death + vpr = PRec_GetPRecordByPnum(victim_pnum); + + if(vpr){ + vpr->dstats.deaths[DSTAT_LEVEL]++; + vpr->dstats.deaths[DSTAT_OVERALL]++; + } + + if(kpnum!=-1){ + kpr = PRec_GetPRecordByPnum(kpnum); + if(kpr){ + kpr->dstats.kills[DSTAT_LEVEL]++; + kpr->dstats.kills[DSTAT_OVERALL]++; + } + UpdatePInfo(victim_pnum,kpnum,1); + } + } + } + + static int teammate_kill_id = -2; + static int nonteam_kill_id = -2; + + if(killer_obj && killer_obj->type==OBJ_PLAYER && victim_pnum!=-1 ){ + + if( Players[killer_obj->id].team == Players[victim_pnum].team && + (GetNumTeams()>=2) && (killer_obj->id!=victim_pnum) ) { + //a teammate killed another teammate! + if(teammate_kill_id==-2){ + teammate_kill_id = DLLFindSoundName(IGNORE_TABLE(SND_MYTEAM_TEAMMATE_KILLED)); + } + if(teammate_kill_id!=-1){ + DLLPlay2dSound(teammate_kill_id,MAX_GAME_VOLUME/2); + } + }else{ + //normal kill sound + if(nonteam_kill_id==-2){ + nonteam_kill_id = DLLFindSoundName(IGNORE_TABLE(SND_NONTEAM_KILL)); + } + if(nonteam_kill_id!=-1){ + DLLPlay2dSound(nonteam_kill_id,MAX_GAME_VOLUME/2); + } + } + + } + + //if DMFC is supposed to handle Deathmessages, than do one + if(m_iProtectedFlags&DMFC_PRF_AUTODEATHMSG) + DoRandomDeathMessage(GetItObjNum(),GetMeObjNum(),hash); +} + +// DMFCBase::OnClientPlayerExploded (Only called if server tells client to execute) +// +// Event handler for when a player explodes. Gets called after a EVT_GAMEPLAYERKILLED event. +// player_num = player number of the player exploding +void DMFCBase::OnClientPlayerExploded(int player_num) +{ +} + +// DMFCBase::OnClientCollide (Only called if server tells client to execute) +// +// Event handler for when two objects collide. At least one of the two objects is a player or a robot. +// Be aware that this event will be called twice per collision, the second time it is called the me and +// it objects will be flipped. +// ALSO NOTE: In order for the OnClientCollide() that passes the point and normal to have valid values +// the server must pass true to the parameter to send arguments in CallClientEvent(). +// me_obj = object pointer to the me object +// it_obj = object pointer to the it object +void DMFCBase::OnClientCollide(object *me_obj,object *it_obj) +{ +} +void DMFCBase::OnClientCollide(object *me_obj,object *it_obj,vector *point,vector *normal) +{ +} + +// DMFCBase::OnClientPlayerChangeSegment (Only called if server tells client to execute) +// +// Event handler for when a player changes rooms or a "large" terrain cell +// (8x8 Terrain block). +// player_num = player number of the player who just changed the room/segment +// newseg = New room/cell location +// oldseg = Old room/cell location +void DMFCBase::OnClientPlayerChangeSegment(int player_num,int newseg,int oldseg) +{ +} + +// DMFCBase::OnClientObjectChangeSegment (Only called if server tells client to execute) +// +// Event handler for when a player changes rooms or a "large" terrain cell +// (8x8 Terrain block). +// obj = Object pointer of the object who just changed the room/segment +// newseg = New room/cell location +// oldseg = Old room/cell location +void DMFCBase::OnClientObjectChangeSegment(object *obj,int newseg,int oldseg) +{ +} + +// DMFCBase::OnClientPlayerEntersGame (Only called if server tells client to execute) +// +// Event handler for when a player enters the game. This will only get called once per +// player, it usually gets called right after they connect to the server to start playing. +// player_num = player number of the player entering the game +void DMFCBase::OnClientPlayerEntersGame(int player_num) +{ + if( (GetLocalRole()==LR_SERVER) || (player_num!=GetPlayerNum()) ){ + int slot; + + if(IsMasterTrackerGame()) + slot = PRec_FindPlayer(Players[player_num].callsign,NULL,Players[player_num].tracker_id); + else + slot = PRec_FindPlayer(Players[player_num].callsign,&NetPlayers[player_num].addr,NULL); + + if(slot==-1){ + //we have a new player + slot = PRec_GetFreeSlot(); + if(!PRec_AssignPlayerToSlot(player_num,slot,Players,NetPlayers)) + mprintf((0,"Unable to assign New Player (%s) to Player Record slot #%d\n",Players[player_num].callsign,slot)); + else + mprintf((0,"(%s) has been assigned to Player Record slot #%d\n",Players[player_num].callsign,slot)); + CallOnPlayerConnect(player_num); + }else{ + //we have a reconnecting player + if(!PRec_ReconnectPlayerToSlot(player_num,slot,Players,NetPlayers)) + mprintf((0,"Unable to reassign reconnecting player (%s) to Player Record slot #%d\n",Players[player_num].callsign,slot)); + else + mprintf((0,"Reconnected player (%s) to Player Record slot #%d\n",Players[player_num].callsign,slot)); + CallOnPlayerReconnect(player_num); + } + } + + // If the correct options are set, go into the D1/D2 style wait screen + if(GetLocalRole()==LR_SERVER && (player_num==GetPlayerNum())){ + + if( GetNumTeams()>1 && (!(m_iProtectedFlags&DMFC_PRF_NOTEAMSELECTONSTART)) ){ + //Display the team placement dialog + static char enable; + enable = 1; + StartUIWindow(UIID_TEAMPLACEMENT,&enable); + } + + if( GetNumTeams()<2 && (!(m_iProtectedFlags&DMFC_PRF_NOWAITFORPLAYERS)) ){ + StartUIWindow(UIID_WAITFORPLAYERS,NULL); + } + } + + //Load up the pilot picture bitmap + //-------------------------------- + if(player_num==GetPlayerNum()){ + //since we are entering the game, go through all the players and setup their bitmaps + for(int w=0;wBAD_BITMAP_HANDLE){ + DLLbm_FreeBitmap(PilotPicBmpHandles[w]); + PilotPicBmpHandles[w] = BAD_BITMAP_HANDLE; + } + + mprintf((0,"PPIC ID: %d is %d\n",w,NetPlayers[w].pilot_pic_id)); + if(NetPlayers[w].pilot_pic_id!=65535){ + PilotPicBmpHandles[w] = DLLPPic_GetBitmapHandle(NetPlayers[w].pilot_pic_id); + if(PilotPicBmpHandles[w]BAD_BITMAP_HANDLE){ + DLLbm_FreeBitmap(PilotPicBmpHandles[player_num]); + PilotPicBmpHandles[player_num] = BAD_BITMAP_HANDLE; + } + + mprintf((0,"PPIC ID: %d is %d\n",player_num,NetPlayers[player_num].pilot_pic_id)); + if(NetPlayers[player_num].pilot_pic_id!=65535){ + PilotPicBmpHandles[player_num] = DLLPPic_GetBitmapHandle(NetPlayers[player_num].pilot_pic_id); + if(PilotPicBmpHandles[player_num]-1 && GetPlayerNum()!=player_num){ + DLLPlay2dSound(sound_id,MAX_GAME_VOLUME/2); + } +} + +// DMFCBase::OnClientPlayerDisconnect +// +// Event handler for when a player disconnects from the server. +// player_num = player number of the player that just disconnected +void DMFCBase::OnClientPlayerDisconnect(int player_num) +{ + //Play the player hud message sound + static int sound_id = -2; + if(sound_id==-2){ + sound_id = DLLFindSoundName("Hudmessage"); + } + + if(sound_id>-1 && GetPlayerNum()!=player_num){ + DLLPlay2dSound(sound_id,MAX_GAME_VOLUME/2); + } + + //save the team + PRec_SetPlayerTeam(player_num,Players[player_num].team); + + if(!PRec_DisconnectPlayer(player_num)) + mprintf((0,"Unable to disconnect player (%s) from Player Records\n",Players[player_num].callsign)); + else + mprintf((0,"Disconnected player (%s) from Player Records\n",Players[player_num].callsign)); +} + +// DMFCBase::OnMeDisconnectFromServer +// +// Event handler that gets called if we disconnect from the server for some reason (not purposesly quit) +void DMFCBase::OnMeDisconnectFromServer(void) +{ + //We're disconnecting so save the stats to file + if(m_iProtectedFlags&DMFC_PRF_AUTOSAVEDISC) + CallOnDisconnectSaveStatsToFile(); +} + + +// DMFCBase::OnClientGameCreated (Only called if server tells client to execute) +// +// Event handler when the server's game first gets started. This will only be called once +// while the server is running, and that is when the server first starts up. +void DMFCBase::OnClientGameCreated(void) +{ +} + +// DMFCBase::OnClientLevelChange (Only called if server tells client to execute) +// +// Event handler when the server changes levels. This will get called after a level ends and a server +// is starting a new level. +void DMFCBase::OnClientLevelChange(void) +{ +} + +// DMFCBase::OnClientLevelStart (Only called if server tells client to execute) +// +// Event handler for when a multiplayer level is starting up. This will get called right before the level +// starts. +void DMFCBase::OnClientLevelStart(void) +{ + //Clear this array so we don't get weird anomalies + ResetPlayersInGame(); + + //Reset the timers + InitTimers(); + + // Reset level stats of Player Records + PRec_LevelReset(); + + //reset the pinfo list + ResetPInfo(); + + // Request Player Records + SendRequestForPlayerRecords(); + + DisplayTauntIndicator = false; +} + +// DMFCBase::OnClientLevelEnd +// +// Event handler for when a multiplayer level is ending. +void DMFCBase::OnClientLevelEnd(void) +{ + MarkPlayersInGame(); + + if(m_iProtectedFlags&DMFC_PRF_AUTOSAVELEVELEND) + CallOnLevelEndSaveStatsToFile(); + + KillAllTimers(); + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_PLRFIRSTTIME); + + //Call PLRInit so data can be initialized + CallOnPLRInit(); + + for(int i=0;i=NETSEQ_PLAYING || NetPlayers[i].sequence==NETSEQ_WAITING_FOR_LEVEL) ) + { + + //save the team + PRec_SetPlayerTeam(i,Players[i].team); + + if(!PRec_DisconnectPlayer(i)) + mprintf((0,"Unable to disconnect player (%s) from Player Records\n",Players[i].callsign)); + else + mprintf((0,"Disconnected player (%s) from Player Records\n",Players[i].callsign)); + } + } +} + +// DMFCBase::OnPlayerEntersObserver +// +// Event handler when a player becomes an observer mode +// If they are piggybacking another player than piggy is the object pointer, else it's NULL +void DMFCBase::OnPlayerEntersObserver(int pnum,object *piggy) +{ + mprintf((0,"Player %d entering observermode %s\n",pnum,(piggy)?"Piggyback":"Roam")); + player_record *pr = PRec_GetPRecordByPnum(pnum); + if(pr && pr->state==STATE_INGAME) + { + PInfo *pi = (PInfo *)pr->pinfo; + ASSERT(pi!=NULL); + pi->EnterObserverMode(); + }else + { + Int3(); + } +} + +// DMFCBase::OnPlayerExitsObserver +// +// Event handler when a player is leaving observer mode +void DMFCBase::OnPlayerExitsObserver(int pnum) +{ + mprintf((0,"Player %d leaving observer mode\n",pnum)); + player_record *pr = PRec_GetPRecordByPnum(pnum); + if(pr && pr->state==STATE_INGAME) + { + PInfo *pi = (PInfo *)pr->pinfo; + ASSERT(pi!=NULL); + pi->ExitObserverMode(); + }else + { + Int3(); + } +} + + +// DMFCBase::OnCanChangeTeam +// +// Called to determine if a player can change teams, you can override this so the server won't +// let a player change teams if it is not desired (for that player) +bool DMFCBase::OnCanChangeTeam(int pnum,int newteam) +{ + if(Players[pnum].team==-1) + return false; //no team switching for dedicated server + + //AnnounceTeamChangeDeny(pnum); + return true; +} + +//DMFCBase::OnSaveStatsToFile +// +// +// The user is requesting the game stats to be saved to file, you must handle this completly on +// the game's side, nothing is done in DMFC +void DMFCBase::OnSaveStatsToFile(void) +{ + mprintf((0,"User requested stats to be saved!\n")); +} + +//DMFCBase::OnLevelEndSaveStatsToFile +// +// +// The game should save the "End of Level" stats to file. You must handle this completly on +// the game's side, nothing is done in DMFC +void DMFCBase::OnLevelEndSaveStatsToFile(void) +{ +} + +//DMFCBase::OnDisconnectSaveStatsToFile +// +// +// The game should save the stats because the player (only ourself as the client in the game), +// has disconnected. +void DMFCBase::OnDisconnectSaveStatsToFile(void) +{ +} + +// DMFCBase::OnSpecialPacket +// +// Event handler for when a special packet arrives and needs to be processed. +// Both the server and client can get this event, although it is more common for the +// client to recieve these. +void DMFCBase::OnSpecialPacket(void) +{ + if(!SPRoot) + return; + + //see if we have a handler for the ID, if so, call the handler, else do nothing + ubyte *data = Data->special_data; + int id = data[0]; + + tSPHandler *current; + current = SPRoot; + while(current){ + if(current->ID==id){ + //we got a winner + switch(current->type) + { + case SPH_DMFCFUNC: + if(current->DMFCfunc) + (this->*current->DMFCfunc)(data+1); + break; + case SPH_FUNC: + if(current->func) + (*current->func)(data+1); + break; + } + return; + } + current = current->next; + } +} + +// DMFCBase::OnHUDInterval +// +// Event handler that gets called once a frame when it's time to render the HUD images +void DMFCBase::OnHUDInterval(void) +{ + if(m_bMakeClientsWait) + WaitingForServerLoop(); + if(m_iProtectedFlags&DMFC_PRF_DISPPLAYERINFO) + DisplayPlayerInfo(MenuBackgroundBMP); + if(m_iProtectedFlags&DMFC_PRF_DISPNETGAMEINFO) + DisplayNetGameInfo(MenuBackgroundBMP); + LossGuageFrame(); + DisplayTauntIndicatorFrame(); + + if(m_iProtectedFlags&DMFC_PRF_INOPTIONS){ + //Update all the state submenus so they are reporting the correct value + if(TeamMenuItem) + TeamMenuItem->SetState(GetPlayerTeam(GetPlayerNum())); + if(AutoBalanceItem) + AutoBalanceItem->SetState((m_iProtectedFlags&DMFC_PRF_AUTOTEAMSELECT)?1:0); + if(AllowTeamChangeItem) + AllowTeamChangeItem->SetState(AllowTeamChange()?1:0); + if(ObserverItem) + ObserverItem->SetState((Objects[Players[GetPlayerNum()].objnum].type==OBJ_OBSERVER)?1:0); + if(StatisticMessagesItem) + StatisticMessagesItem->SetState((m_iProtectedFlags&DMFC_PRF_DISPSTATHUDMSGS)?1:0); + if(SaveStatsLevelEndItem) + SaveStatsLevelEndItem->SetState((m_iProtectedFlags&DMFC_PRF_AUTOSAVELEVELEND)?1:0); + if(SaveStatsDisconnectItem) + SaveStatsDisconnectItem->SetState((m_iProtectedFlags&DMFC_PRF_AUTOSAVEDISC)?1:0); + if(MenuBackgroundItem) + MenuBackgroundItem->SetState((m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR)?1:0); + if(NetGameInfoItem) + NetGameInfoItem->SetState(IsDisplayingNetGameInfo()?1:0); + if(HUDCallsignsItem) + HUDCallsignsItem->SetState(m_iMyCurrentHUDCallsignLevel); + if(ServerHUDCallsignsItem) + ServerHUDCallsignsItem->SetState(m_iServerHUDCallsignLevel); + if(DeathMessageFilterItem) + DeathMessageFilterItem->SetState(m_iDeathMessageFilter); + if(ShipLogosItem) + ShipLogosItem->SetState(AreLogosEnabled()?1:0); + if(HUDIndicatorItem) + HUDIndicatorItem->SetState(LossGuageEnabled?1:0); + + //We have an options screen up, so draw it + DrawMenu(); + } + + //DisplayPilotPicsFrame(); + + // Display countdown if needed + DisplayTimelimitCountdown(); + + DLLgrtext_Flush(); +} + +// DMFCBase::OnInterval +// +// Event handler that gets called once a frame +void DMFCBase::OnInterval(void) +{ + if(!(m_iProtectedFlags&DMFC_PRF_PAUSETIME)) + { + RealGametime += *Frametime; //increment the timer + } + + //If we are a dedicated server, we are waiting, and there is a wait time + //check to see if it's time to disable + if(GetLocalRole()==LR_SERVER && IAmDedicatedServer() && m_bMakeClientsWait) + { + if(DedicatedLevelWait>0 && DedicatedLevelWait<=(*Gametime)) + { + //time to stop waiting! + mprintf((0,"Telling clients that they can play!\n")); + DPrintf("Allowing Clients To Play\n"); + PauseRealGameTime(false); + MakeClientsWait(false); + } + } + + //CheckPInfo(); + + //Process remote admin frame + Remote_ProcessFrame(); + + //Process all currently running timers + ProcessTimers(); + + //Handle time limit games + float tleft; + if(GetLocalRole()==LR_SERVER) + { + if((m_iProtectedFlags&DMFC_PRF_AUTOTIMELIMIT)&&(GetTimeLeft(&tleft)) ) + { + if(tleft<=0) + { + EndLevel(); + return; + } + } + } +} + +// DMFCBase::OnPLRInterval +// +// Event handler that gets called once a frame when the Post Level Results screen is being display +void DMFCBase::OnPLRInterval(void) +{ + DLLgrtext_SetFont(Game_fonts[SMALL_UI_FONT_INDEX]); + DLLgrtext_SetColor(GR_RGB(255,40,40)); + DLLgrtext_CenteredPrintf(0,13,Netgame->scriptname); + DLLgrtext_CenteredPrintf(0,461,DTXT_PRESSPRNTSCRN); +} + +// DMFCBase::OnPLRInit +// +// Event handler that gets called the first frame of the PLR screen for each level +void DMFCBase::OnPLRInit(void) +{ + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_PLRFIRSTTIME); +} + +// DMFCBase::OnKeypress +// +// Event handler for when a user presses a key while in the game +// key = Key code of the key being pressed +void DMFCBase::OnKeypress(int key) +{ + if(m_iProtectedFlags&DMFC_PRF_INOPTIONS){ + Data->iRet = 1; + //Handle keyboard input for an options menu + if(key==K_F6||key==K_ESC){ //escape out of options + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_INOPTIONS); + } + if(key==K_ENTER){ + Menu.Execute(); + } + switch(key){ + case K_DOWN: + { + Menu.Down(); + } + break; + case K_UP: + { + Menu.Up(); + } + break; + case K_RIGHT: + { + Menu.Forward(); + } + break; + case K_LEFT: + { + Menu.Back(); + } + break; + } + } + else + if(key==K_F6){ + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_INOPTIONS); + } +} + + +// DMFCBase::OnInputString +// +// Event handler for when the user types a message at the console (F8) that begins with a $ +// input_string = string that was typed +void DMFCBase::OnInputString(char *input_string) +{ + if(!InputCommandHandle(input_string)){ + //The input command wasn't handled! + mprintf((0,"DMFC Warning: Input Command '%s' wasn't handled\n",input_string)); + } +} + + +// DMFCBase::OnPlayerChangeTeam +// +// Called when a player changes team +void DMFCBase::OnPlayerChangeTeam(int player_num,int newteam,bool announce,bool spew_everything) +{ + player_record *pr = GetPlayerRecordByPnum(player_num); + if(pr){ + if(GetNumTeams()>1 && announce && newteam!=-1) + DLLAddHUDMessage(DTXT_CHANGETEAM,pr->callsign,GetTeamString(newteam)); + pr->team = newteam; + + //only respawn them if they are not dead + if(!((Players[player_num].flags & PLAYER_FLAGS_DYING)||(Players[player_num].flags & PLAYER_FLAGS_DEAD))) + { + RespawnPlayer(player_num,false,spew_everything); + } + } +} + +// DMFCBase::OnClientObjectShieldsChanged +// +// Event handler for when an objects shield's change +void DMFCBase::OnClientObjectShieldsChanged(object *obj,float amount) +{ +} + +// DMFCBase::OnWeaponFired +// +// Event handler for when an object fires a weapon +void DMFCBase::OnWeaponFired(object *weapon_obj,object *shooter) +{ +} + +//DMFCBase::OnPlayerReconnect +// +// +// This player is reconnecting to the game +void DMFCBase::OnPlayerReconnect(int player_num) +{ + if(GetLocalRole()==LR_SERVER){ + //restore their team + int team = PRec_GetPlayerTeam(player_num); + if(team!=Players[player_num].team) + { + //hey! Jason hasn't set the correct team for this player + ASSERT(player_num==0);//this is only going to happen for the server + } + + SendTeamAssignment(player_num,team,false); + Players[player_num].team = team; + CallOnPlayerChangeTeam(player_num,team,false,false); + mprintf((0,"Reassigning (%s) to %s team\n",Players[player_num].callsign,GetTeamString(team))); + } +} + +//DMFCBase::OnPlayerConnect +// +// +// This player is connecting to the game for the first time +void DMFCBase::OnPlayerConnect(int player_num) +{ + if(GetLocalRole()==LR_SERVER){ + + //a dedicated server MUST be given a -1 team + //------------------------------------------ + if(player_num==0 && IAmDedicatedServer()){ + Players[0].team = -1; + } + + //get team assignment + if(m_iProtectedFlags&DMFC_PRF_AUTOTEAMSELECT){ + //JEFF: Commented out since team's are now determined right when a client connects + //@@int team = GetTeamForNewPlayer(player_num,m_iNumTeams); + int team = Players[player_num].team; + SendTeamAssignment(player_num,team,false); + //@@Players[player_num].team = team; + CallOnPlayerChangeTeam(player_num,team,false,false); + mprintf((0,"Assigning (%s) to %s team\n",Players[player_num].callsign,GetTeamString(team))); + } + } +} + +//DMFCBase::OnControlMessage +// +// +// There is a control message sent from someone +void DMFCBase::OnControlMessage(ubyte msg,int from_pnum) +{ + switch(msg){ + case CM_KICKED: + { + DLLAddHUDMessage(DTXT_KICKEDMSG); + DisconnectMe(); + } + break; + case CM_BANNED: + { + DLLAddHUDMessage(DTXT_BANNEDMSG); + DisconnectMe(); + } + break; + case CM_TEAMCHANGEDENIED: + { + //if we get this, the server is telling us that the request for a + //team change has been denied + AnnounceTeamChangeDeny(*Player_num); + }break; + case CM_SIGNALSTART: + { + if(from_pnum==0){ + //we can move!! + WaitForServer(false); + } + }break; + case CM_SIGNALWAIT: + { + if(from_pnum==0){ + //we got to wait + WaitForServer(true); + } + }break; + case CM_PAUSETIME: + { + ENABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_PAUSETIME); + }break; + case CM_UNPAUSETIME: + { + DISABLE_FLAGS(m_iProtectedFlags,DMFC_PRF_PAUSETIME); + }break; + }; +} + + +//DMFCBase::OnAllowObserverChange +// +// +// returns true if the requested change for observer mode should go through +bool DMFCBase::OnAllowObserverChange(bool turnonobserver) +{ + return true; +} + +//DMFCBase::OnClientShowUI +// +// +// The game is saying it's ok to do any UI +void DMFCBase::OnClientShowUI(int id,void *user_data) +{ + switch(id){ + case UIID_TEAMPLACEMENT: + { + bool clients_wait; + bool call_from_game; + ubyte d = *(ubyte *)user_data; + + clients_wait = (d&0x01)?true:false; + call_from_game = (d&0x02)?false:true; + + DoDMFCUITeamPlacement(clients_wait,call_from_game); + }break; + case UIID_WAITFORPLAYERS: + { + DoDMFCUIWaitForPlayers(true); + }break; + }; +} + +//DMFCBase::OnPrintScores +// +// +// The user is requesting that the scores be printed out (For Dedicated Server use). Use DPrintf +// level: -1 Requesting all the available scores +// n Print only the top n scores +// Override this to how you see fit, but it should conform to the above. +void DMFCBase::OnPrintScores(int level) +{ +} + +//DMFCBase::OnGetHudCallSignColor +// +// This is an event sent in by the game requesting what color it should draw the HUD callsign +// of the passed in playernum. Using GR_RGB return the color from the function. This function +// gets called every frame that the player is on the clients HUD. +ddgr_color DMFCBase::OnGetHudCallSignColor(int playernum) +{ + if(!CheckPlayerNum(playernum)) + return GR_RGB(255,255,255); + + if(GetNumTeams()<=1) + return GR_RGB(0,255,0); + + return GetTeamColor(GetPlayerTeam(playernum)); +} + +//DMFCBase::OnTeamChangeName +// +// This event occurs on the client when a team's name has just changed +void DMFCBase::OnTeamChangeName(int team,char *oldname,char *newname) +{ + char names[DLLMAX_TEAMS][MAX_TEAMNAME_LEN]; + strcpy(names[RED_TEAM],GetTeamString(RED_TEAM)); + strcpy(names[BLUE_TEAM],GetTeamString(BLUE_TEAM)); + if(TeamMenuItem){ + switch(m_iNumTeams){ + case 2: + TeamMenuItem->SetStateItemList(m_iNumTeams,names[RED_TEAM],names[BLUE_TEAM]); + break; + case 3: + strcpy(names[GREEN_TEAM],GetTeamString(GREEN_TEAM)); + TeamMenuItem->SetStateItemList(m_iNumTeams,names[RED_TEAM],names[BLUE_TEAM],names[GREEN_TEAM]); + break; + case 4: + strcpy(names[GREEN_TEAM],GetTeamString(GREEN_TEAM)); + strcpy(names[YELLOW_TEAM],GetTeamString(YELLOW_TEAM)); + TeamMenuItem->SetStateItemList(m_iNumTeams,names[RED_TEAM],names[BLUE_TEAM],names[GREEN_TEAM],names[YELLOW_TEAM]); + break; + } + } +} + +//DMFCBase::OnDoControls +// +// This event occurs when there is a new back of game controls data +void DMFCBase::OnDoControls(game_controls *controls) +{ + memcpy(&Last_game_controls,controls,sizeof(Last_game_controls)); +} + +// DMFCBase::OnClientWallCollide +// +// Called by the game when their is a collision between an object and a wall +void DMFCBase::OnClientWallCollide(object *obj,float hitspeed,int hitseg,int hitwall,vector * hitpt,vector *wall_normal,float hit_dot) +{ +} + +// DMFCBase::OnClientObjectKilled +// +// Called when an object is being killed +// Not automatically sent to clients (OnClientObjectKilled) +// killer might not be valid (NULL) +void DMFCBase::OnClientObjectKilled(object *obj,object *killer) +{ +} + +// DMFCBase::OnClientObjectDestroyed +// +// Called when an object is about to be deleted +// Not automatically sent to clients +void DMFCBase::OnClientObjectDestroyed(object *obj) +{ +} + +//DMFCBase::OnPlayAudioTaunt +// +// This event occurs when a player plays an audio taunt +void DMFCBase::OnPlayAudioTaunt(int pnum) +{ + mprintf((0,"%s plays an audio taunt\n",Players[pnum].callsign)); + SetPlayerTauntIndicator(pnum); +} + +// DMFCBase::OnGetTokenString +// +// This event occurs when a text macro is being used that has tokens +// in it, if you have a specific token, override this event to handle it. +void DMFCBase::OnGetTokenString(char *src,char *dest,int dest_size) +{ + *dest = '\0'; + + if(!stricmp(src,"e")) + { + //energy + char buffer[64]; + int mypnum = GetPlayerNum(); + sprintf(buffer,"%.0f",Players[mypnum].energy); + strncpy(dest,buffer,dest_size-1); + dest[dest_size-1] = '\0'; + return; + } + + if(!stricmp(src,"s")) + { + //shields + char buffer[64]; + int mypnum = GetPlayerNum(); + sprintf(buffer,"%.0f",Objects[Players[mypnum].objnum].shields); + strncpy(dest,buffer,dest_size-1); + dest[dest_size-1] = '\0'; + return; + } +} diff --git a/netgames/dmfc/dmfcdllinit.h b/netgames/dmfc/dmfcdllinit.h new file mode 100644 index 000000000..85d6eadb9 --- /dev/null +++ b/netgames/dmfc/dmfcdllinit.h @@ -0,0 +1,594 @@ +/* +* $Logfile: /DescentIII/Main/Dmfc/dmfcdllinit.h $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:57:20 $ +* $Author: kevinb $ +* +* Function and variable initialization +* +* $Log: dmfcdllinit.h,v $ +* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb +* initial 1.5 import +* + * + * 64 9/28/99 10:55a Jeff + * changed size of pointer for mastertracker game from ubyte* to int* + * + * 63 8/15/99 4:36p Jeff + * finished exporting all inventory class functions. export object_info + * array. added check for -nooutragelogo to display Outrage logo display. + * + * 62 8/11/99 1:22p Jeff + * exported needed functions for camera windows + * + * 61 7/20/99 11:26a Jeff + * fire weapon functions + * + * 60 7/11/99 3:31p Jeff + * exported game arguments, made command line option to specify + * autoexec.dmfc + * + * 59 5/22/99 1:12a Jeff + * correctly handle Viewer_object + * + * 58 5/20/99 5:31p Jeff + * exported PlayerStopSounds + * + * 57 5/19/99 11:49p Jeff + * final preparations for dmfc version 1.0 + * + * 56 5/19/99 5:26p Jeff + * fixed some exported functions. Removed no longer used functions + * + * 55 5/03/99 8:39a Jeff + * tons of Entropy fixes + * + * 54 4/30/99 10:53p Jeff + * added $warp command + * + * 53 4/30/99 7:36p Jeff + * exported vis_effects to dmfc + * + * 52 3/30/99 9:01p Jeff + * exported polymodels + * + * 51 3/27/99 4:53p Jeff + * player rankings in multiplayer games implemented. Fixed join message + * so it doesn't get cut off + * + * 50 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 49 3/17/99 12:23p Jeff + * converted DMFC to be COM interface + * + * 48 3/12/99 7:40p Jeff + * removed enhanced objcreate, added enhanced objsetpos + * + * 47 3/11/99 6:30p Jeff + * numerous fixes to demo system in multiplayer games (when + * recording/playback a demo in a multiplayer game) + * + * 46 2/25/99 8:54p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 45 2/11/99 12:50a Jeff + * changed names of exported variables + * + * 43 2/08/99 5:22p Jeff + * exported rend_SetZBuffer...fixed up small view of stats + * + * 42 2/08/99 12:05a Jeff + * exported some new functions and variables + * + * 41 2/06/99 6:59p Jeff + * created RenderHUDGetTextLineWidth and RenderHUDGetTextHeight + * + * 40 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 39 1/21/99 11:16p Jeff + * exported vecmat functions + * + * 38 1/19/99 5:34p Jeff + * updated monsterball + * + * 37 1/15/99 8:29p Jeff + * updates to powerball + * + * 36 1/15/99 3:54a Jeff + * exported a couple more functions. Added event handlers for Weapon + * collide event + * + * 35 1/12/99 2:55p Jeff + * added/finished the waiting for player's to join dialog + * + * 34 1/07/99 5:01p Jeff + * added Int3's and updated all net games to use stats manager...correctly + * too + * + * 33 1/04/99 2:19p Jeff + * exported table file management functions + * + * 32 1/04/99 12:21p Jeff + * added support for hosts.allow/deny and updates stats manager a little + * + * 31 12/08/98 3:28p Jeff + * created and exported UI Checkbox wrappers + * + * 30 12/08/98 12:17p Jeff + * various changes that include an improved Team Control dialog (doesn't + * switch teams until exit..) and spew/respawn players that change teams + * + * 29 12/04/98 7:04p Jeff + * almost finished up dmfc stat manager + * + * 28 12/01/98 6:56p Jeff + * put in quick and dirty implementation of pilot pics for testing + * + * 27 11/19/98 5:56p Jeff + * added slider exported and improved Hoard + * + * 26 11/09/98 11:54a Jeff + * Added Player_colors + * + * 25 10/29/98 7:01p Jeff + * creation of team placement dialog. Moved TranslateEvent into DMFC + * + * 24 10/18/98 7:59p Jeff + * functions added to dmfc for client->server objnum matching. Banning + * now uses tracker id when available. + * + * 23 10/13/98 6:01p Jeff + * added attaching + * + * 22 10/08/98 3:37p Jeff + * general improvements (Netgame info things, save to registry). Changes + * so it would send packets on NETSEQ_OBJECTS + * + * 21 10/02/98 6:11p Jeff + * + * 20 9/30/98 3:50p Jeff + * general improvements (many) + * + * 19 9/21/98 7:11p Jeff + * made InputCommand interface API and moved existing input commands to + * the interface. Changed mprintf/ASSERT so they are valid in DMFC +* +* $NoKeywords: $ +*/ + + + + +//This file contains the DLLInit function that is to be used across all multiplayer +//DLLs. So all changes can be done in one place. This file should only be included where +//the DLLGameInit function should be. + + DLLGetGameAPI=(GetGameAPI_fp)api_func; + DLLGetGameAPI (&API); + + Objects=(object *)API.objs; + Rooms=(room *)API.rooms; + Terrain_seg=(terrain_segment *)API.terrain; + Players=(player *)API.players; + Netgame=(netgame_info *)API.netgame; + NetPlayers=(netplayer *)API.netplayers; + Ships = (ship *)API.ships; + Weapons = (weapon *)API.weapons; + Current_mission = (tMission *)API.Current_mission; + GameTextures = (texture *)API.GameTextures; + GameVClips = (vclip *)API.GameVClips; + + int i = 0; + + // Do functions, variables + // Note, these must match the ordering on the D3 side + DLLAddHUDMessage=(AddHUDMessage_fp)API.fp[i++]; + DLLMultiSendClientExecuteDLL=(MultiSendClientExecuteDLL_fp)API.fp[i++]; + DLLFindObjectIDName=(FindObjectIDName_fp)API.fp[i++]; + DLLGetGoalRoomForTeam=(GetGoalRoomForTeam_fp)API.fp[i++]; + DLLSetMaxTeams=(SetMaxTeams_fp)API.fp[i++]; + DLLComputeRoomCenter=(ComputeRoomCenter_fp)API.fp[i++]; + DLLObjCreate=(ObjCreate_fp)API.fp[i++]; + DLLMultiSendObject=(MultiSendObject_fp)API.fp[i++]; + DLLMultiPaintGoalRooms=(MultiPaintGoalRooms_fp)API.fp[i++]; + DLLMultiSendSpecialPacket=(MultiSendSpecialPacket_fp)API.fp[i++]; + DLLIncTeamScore=(IncTeamScore_fp)API.fp[i++]; + DLLDebug_ConsolePrintf=(Debug_ConsolePrintf_fp)API.fp[i++]; + DLLObjSetPosNoMark=(ObjSetPosNoMark_fp)API.fp[i++]; + DLLInvCheckItem=(InvCheckItem_fp)API.fp[i++]; + DLLInvAddTypeID=(InvAddTypeID_fp)API.fp[i++]; + DLLInvRemove=(InvRemove_fp)API.fp[i++]; + DLLPlayerSetLighting=(PlayerSetLighting_fp)API.fp[i++]; + DLLFindShipName=(FindShipName_fp)API.fp[i++]; + DLLPlayerSetRotatingBall=(PlayerSetRotatingBall_fp)API.fp[i++]; + DLLPlayerChangeShip=(PlayerChangeShip_fp)API.fp[i++]; + DLLInvGetTypeIDCount = (InvGetTypeIDCount_fp)API.fp[i++]; + DLLPlay3dSound = (D3W_Play3dSound_fp)API.fp[i++]; + DLLFindSoundName = (FindSoundName_fp)API.fp[i++]; + DLLSpewCreate = (SpewCreate_fp)API.fp[i++]; + DLLSpewClearEvent = (SpewClearEvent_fp)API.fp[i++]; + DLLbm_AllocLoadFileBitmap=(bm_AllocLoadFileBitmap_fp)API.fp[i++]; + DLLbm_FreeBitmap=(bm_FreeBitmap_fp)API.fp[i++]; + DLLrend_DrawScaledBitmap=(rend_DrawScaledBitmap_fp)API.fp[i++]; + DLLgrtext_Printf=(grtext_Printf_fp)API.fp[i++]; + DLLgrtext_Flush=(grtext_Flush_fp)API.fp[i++]; + DLLgrtext_SetColor=(grtext_SetColor_fp)API.fp[i++]; + DLLgrtext_SetFancyColor=(grtext_SetFancyColor_fp)API.fp[i++]; + DLLgrtext_SetAlpha=(grtext_SetAlpha_fp)API.fp[i++]; + DLLgrtext_GetAlpha=(grtext_GetAlpha_fp)API.fp[i++]; + DLLgrtext_SetFont=(grtext_SetFont_fp)API.fp[i++]; + DLLgrtext_GetFont=(grtext_GetFont_fp)API.fp[i++]; + DLLgrtext_GetTextLineWidth=(grtext_GetTextLineWidth_fp)API.fp[i++]; + DLLgrfont_GetHeight=(grfont_GetHeight_fp)API.fp[i++]; + DLLgrtext_CenteredPrintf=(grtext_CenteredPrintf_fp)API.fp[i++]; + DLLAddColoredHUDMessage=(AddColoredHUDMessage_fp)API.fp[i++]; + DLLbm_w=(bm_w_fp)API.fp[i++]; + DLLbm_h=(bm_h_fp)API.fp[i++]; + DLLrend_DrawSimpleBitmap=(rend_DrawSimpleBitmap_fp)API.fp[i++]; + DLLMultiClientSendSpecialPacket=(MultiClientSendSpecialPacket_fp)API.fp[i++]; + DLLAddBlinkingHUDMessage = (AddBlinkingHUDMessage_fp)API.fp[i++]; + DLLInvReset = (InvReset_fp)API.fp[i++]; + DLLAddHUDItem = (AddHUDItem_fp)API.fp[i++]; + DLLRenderHUDQuad = (RenderHUDQuad_fp)API.fp[i++]; + DLLRenderHUDText = (RenderHUDText_fp)API.fp[i++]; + DLLMultiEndLevel = (MultiEndLevel_fp)API.fp[i++]; + DLLbm_data = (bm_data_fp)API.fp[i++]; + DLLbm_AllocBitmap = (bm_AllocBitmap_fp)API.fp[i++]; + DLLrend_FillRect = (rend_FillRect_fp)API.fp[i++]; + DLLbm_CreateChunkedBitmap = (bm_CreateChunkedBitmap_fp)API.fp[i++]; + DLLbm_DestroyChunkedBitmap = (bm_DestroyChunkedBitmap_fp)API.fp[i++]; + DLLrend_DrawChunkedBitmap = (rend_DrawChunkedBitmap_fp)API.fp[i++]; + DLLrend_DrawScaledChunkedBitmap = (rend_DrawScaledChunkedBitmap_fp)API.fp[i++]; + DLLOpenCFILE = (OpenCFILE_fp)API.fp[i++]; + DLLcfclose = (cfclose_fp)API.fp[i++]; + DLLcfeof = (cfeof_fp)API.fp[i++]; + DLLcfexist = (cfexist_fp)API.fp[i++]; + DLLcf_ReadBytes = (cf_ReadBytes_fp)API.fp[i++]; + DLLcf_ReadInt = (cf_ReadInt_fp)API.fp[i++]; + DLLcf_ReadShort = (cf_ReadShort_fp)API.fp[i++]; + DLLcf_ReadByte = (cf_ReadByte_fp)API.fp[i++]; + DLLcf_ReadFloat = (cf_ReadFloat_fp)API.fp[i++]; + DLLcf_ReadDouble = (cf_ReadDouble_fp)API.fp[i++]; + DLLcf_ReadString = (cf_ReadString_fp)API.fp[i++]; + DLLcf_WriteBytes = (cf_WriteBytes_fp)API.fp[i++]; + DLLcf_WriteString = (cf_WriteString_fp)API.fp[i++]; + DLLcf_WriteInt = (cf_WriteInt_fp)API.fp[i++]; + DLLcf_WriteShort = (cf_WriteShort_fp)API.fp[i++]; + DLLcf_WriteByte = (cf_WriteByte_fp)API.fp[i++]; + DLLcf_WriteFloat = (cf_WriteFloat_fp)API.fp[i++]; + DLLcf_WriteDouble = (cf_WriteDouble_fp)API.fp[i++]; + DLLcf_CopyFile = (cf_CopyFile_fp)API.fp[i++]; + DLLcf_Diff = (cf_Diff_fp)API.fp[i++]; + DLLMultiDisconnectPlayer = (MultiDisconnectPlayer_fp)API.fp[i++]; + DLLnw_GetNumbersFromHostAddress = (nw_GetNumbersFromHostAddress_fp)API.fp[i++]; + DLLnw_GetThisIP = (nw_GetThisIP_fp)API.fp[i++]; + DLLCreateStringTable = (CreateStringTable_fp)API.fp[i++]; + DLLDestroyStringTable = (DestroyStringTable_fp)API.fp[i++]; + DLLRenderHUDTextFlags = (RenderHUDTextFlags_fp)API.fp[i++]; + DLLPlayerSpewInventory = (PlayerSpewInventory_fp)API.fp[i++]; + DLLPlayerSetHUDNameFOV = (PlayerSetHUDNameFOV_fp)API.fp[i++]; + DLLGetUltimateParentForObject = (GetUltimateParentForObject_fp)API.fp[i++]; + DLLSetObjectDeadFlagRaw = (SetObjectDeadFlagRaw_fp)API.fp[i++]; + FatalError = (DLLFatalError_fp)API.fp[i++]; + DLLassert = (assertdll_fp)API.fp[i++]; + DLLMultiMatchWeapon = (MultiMatchWeapon_fp)API.fp[i++]; + DLLMultiGetMatchChecksum = (MultiGetMatchChecksum_fp)API.fp[i++]; + DLLFindWeaponName = (FindWeaponName_fp)API.fp[i++]; + DLLcf_OpenLibrary = (cf_OpenLibrary_fp)API.fp[i++]; + DLLcf_CloseLibrary = (cf_CloseLibrary_fp)API.fp[i++]; + DLLMultiSendRequestToObserve = (MultiSendRequestToObserve_fp)API.fp[i++]; + DLLFindTextureName = (FindTextureName_fp)API.fp[i++]; + DLLApplyDamageToPlayer = (ApplyDamageToPlayer_fp)API.fp[i++]; + DLLMultiMatchGeneric = (MultiMatchGeneric_fp)API.fp[i++]; + DLLSetUITextItemText = (SetUITextItemText_fp)API.fp[i++]; + DLLNewUIWindowCreate = (NewUIWindowCreate_fp)API.fp[i++]; + DLLNewUIWindowDestroy= (NewUIWindowDestroy_fp)API.fp[i++]; + DLLNewUIWindowOpen = (NewUIWindowOpen_fp)API.fp[i++]; + DLLNewUIWindowClose = (NewUIWindowClose_fp)API.fp[i++]; + DLLTextCreate = (TextCreate_fp)API.fp[i++]; + DLLEditCreate = (EditCreate_fp)API.fp[i++]; + DLLButtonCreate = (ButtonCreate_fp)API.fp[i++]; + DLLListCreate = (ListCreate_fp)API.fp[i++]; + DLLListRemoveAll = (ListRemoveAll_fp)API.fp[i++]; + DLLListAddItem = (ListAddItem_fp)API.fp[i++]; + DLLListRemoveItem = (ListRemoveItem_fp)API.fp[i++]; + DLLListSelectItem = (ListSelectItem_fp)API.fp[i++]; + DLLListGetItem = (ListGetItem_fp)API.fp[i++]; + DLLListGetSelectedIndex = (ListGetSelectedIndex_fp)API.fp[i++]; + DLLEditSetText = (EditSetText_fp)API.fp[i++]; + DLLEditGetText = (EditGetText_fp)API.fp[i++]; + DLLDoUI = (DoUI_fp)API.fp[i++]; + DLLDoMessageBox = (DoMessageBox_fp)API.fp[i++]; + DLLDescentDefer = (DescentDefer_fp)API.fp[i++]; + DLLNewUIGameWindowCreate = (NewUIGameWindowCreate_fp)API.fp[i++]; + DLLNewUIGameWindowDestroy = (NewUIGameWindowDestroy_fp)API.fp[i++]; + DLLNewUIGameWindowOpen = (NewUIGameWindowOpen_fp)API.fp[i++]; + DLLNewUIGameWindowClose = (NewUIGameWindowClose_fp)API.fp[i++]; + DLLHotSpotCreate = (HotSpotCreate_fp)API.fp[i++]; + DLLPollUI = (PollUI_fp)API.fp[i++]; + DLLRemoveUITextItem = (RemoveUITextItem_fp)API.fp[i++]; + DLLCreateNewUITextItem = (CreateNewUITextItem_fp)API.fp[i++]; + DLLUIConsoleGadgetCreate = (UIConsoleGadgetCreate_fp)API.fp[i++]; + DLLUIConsoleGadgetputs = (UIConsoleGadgetputs_fp)API.fp[i++]; + DLLNewUIWindowSetFocusOnEditGadget = (NewUIWindowSetFocusOnEditGadget_fp)API.fp[i++]; + DLLOldEditCreate = (OldEditCreate_fp)API.fp[i++]; + DLLOldListCreate = (OldListCreate_fp)API.fp[i++]; + DLLOldListRemoveAll = (OldListRemoveAll_fp)API.fp[i++]; + DLLOldListAddItem = (OldListAddItem_fp)API.fp[i++]; + DLLOldListRemoveItem = (OldListRemoveItem_fp)API.fp[i++]; + DLLOldListSelectItem = (OldListSelectItem_fp)API.fp[i++]; + DLLOldListGetItem = (OldListGetItem_fp)API.fp[i++]; + DLLOldListGetSelectedIndex = (OldListGetSelectedIndex_fp)API.fp[i++]; + DLLOldEditSetText = (OldEditSetText_fp)API.fp[i++]; + DLLOldEditGetText = (OldEditGetText_fp)API.fp[i++]; + DLLToggleUICallback = (ToggleUICallback_fp)API.fp[i++]; + DLLSetOldEditBufferLen = (SetOldEditBufferLen_fp)API.fp[i++]; + DLLNewUIWindowLoadBackgroundImage = (NewUIWindowLoadBackgroundImage_fp)API.fp[i++]; + DLLDeleteUIItem = (DeleteUIItem_fp)API.fp[i++]; + DLLHotSpotSetStates = (HotSpotSetStates_fp)API.fp[i++]; + DLLSetUICallback = (SetUICallback_fp)API.fp[i++]; + DLLGetUICallback = (RetrieveUICallback_fp)API.fp[i++]; + DLLSuspendControls = (SuspendControls_fp)API.fp[i++]; + DLLResumeControls = (ResumeControls_fp)API.fp[i++]; + DLLShowCursor = (ui_ShowCursor_fp)API.fp[i++]; + DLLHideCursor = (ui_HideCursor_fp)API.fp[i++]; + DLLGameFrame = (GameFrame_fp)API.fp[i++]; + DPrintf = (DPrintf_fp)API.fp[i++]; + DLLddio_MakePath = (ddio_MakePath_fp)API.fp[i++]; + DLLddio_SplitPath = (ddio_SplitPath_fp)API.fp[i++]; + DLLPlay2dSound = (Play2dSound_fp)API.fp[i++]; + DLLTouchSound = (TouchSound_fp)API.fp[i++]; + DatabaseRead1 = (dDatabaseRead_fp1)API.fp[i++]; + DatabaseRead2 = (dDatabaseRead_fp2)API.fp[i++]; + DatabaseRead3 = (dDatabaseRead_fp3)API.fp[i++]; + DatabaseWrite1 = (dDatabaseWrite_fp1)API.fp[i++]; + DatabaseWrite2 = (dDatabaseWrite_fp2)API.fp[i++]; + DLLAttachObject = (AttachObject_fp)API.fp[i++]; + DLLObjGet = (ObjGet_fp)API.fp[i++]; + DLLListSetSelectedIndex = (ListSetSelectedIndex_fp)API.fp[i++]; + DLLRemoveUIBmpItem = (RemoveUIBmpItem_fp)API.fp[i++]; + DLLCreateNewUIBmpItem = (CreateNewUIBmpItem_fp)API.fp[i++]; + DLLGetUIItemWidth = (GetUIItemWidth_fp)API.fp[i++]; + DLLGetUIItemHeight = (GetUIItemHeight_fp)API.fp[i++]; + DLLSliderCreate = (SliderCreate_fp)API.fp[i++]; + DLLSliderSetRange = (SliderSetRange_fp)API.fp[i++]; + DLLSliderGetRange = (SliderGetRange_fp)API.fp[i++]; + DLLSliderSetPos = (SliderSetPos_fp)API.fp[i++]; + DLLSliderGetPos = (SliderGetPos_fp)API.fp[i++]; + DLLSliderSetSelectChangeCallback = (SliderSetSelectChangeCallback_fp)API.fp[i++]; + DLLSliderSetSelectChangeCallbackWData = (SliderSetSelectChangeCallbackWData_fp)API.fp[i++]; + DLLTextSetTitle = (TextSetTitle_fp)API.fp[i++]; + DLLPPic_GetPilot = (PPic_GetPilot_fp)API.fp[i++]; + DLLPPic_GetBitmapHandle = (PPic_GetBitmapHandle_fp)API.fp[i++]; + DLLrend_DrawLine = (rend_DrawLine_fp)API.fp[i++]; + DLLrend_SetFlatColor = (rend_SetFlatColor_fp)API.fp[i++]; + DLLMultiSetLogoState = (MultiSetLogoState_fp)API.fp[i++]; + DLLPlayerGetRandomStartPosition = (PlayerGetRandomStartPosition_fp)API.fp[i++]; + DLLInitPlayerNewShip = (InitPlayerNewShip_fp)API.fp[i++]; + DLLCheckBoxCreate = (CheckBoxCreate_fp)API.fp[i++]; + DLLCheckBoxSetCheck = (CheckBoxSetCheck_fp)API.fp[i++]; + DLLCheckBoxIsChecked = (CheckBoxIsChecked_fp)API.fp[i++]; + DLLnw_GetHostAddressFromNumbers = (nw_GetHostAddressFromNumbers_fp)API.fp[i++]; + TableFilesClear = (TableFilesClear_fp)API.fp[i++]; + TableFileAdd = (TableFileAdd_fp)API.fp[i++]; + DLLDebugBreak_callback_stop = (DebugBreak_callback_stop_fp)API.fp[i++]; + DLLDebugBreak_callback_resume = (DebugBreak_callback_resume_fp)API.fp[i++]; + DLLInt3MessageBox = (Int3MessageBox_fp)API.fp[i++]; + DLLGetUIItemPosition = (GetUIItemPosition_fp)API.fp[i++]; + DLLAttachObjectRadius = (AttachObjectRadius_fp)API.fp[i++]; + DLLUnattachChildren = (UnattachChildren_fp)API.fp[i++]; + DLLUnattachChild = (UnattachChild_fp)API.fp[i++]; + DLLUnattachFromParent = (UnattachFromParent_fp)API.fp[i++]; + DLLvm_GetMagnitude = (vm_GetMagnitude_fp)API.fp[i++]; + DLLvm_MatrixMulVector = (vm_MatrixMulVector_fp)API.fp[i++]; + DLLphys_apply_force = (phys_apply_force_fp)API.fp[i++]; + DLLphys_apply_rot = (phys_apply_rot_fp)API.fp[i++]; + DLLvm_TransposeMatrix = (vm_TransposeMatrix_fp)API.fp[i++]; + DLLvm_CrossProduct = (vm_CrossProduct_fp)API.fp[i++]; + DLLvm_NormalizeVector = (vm_NormalizeVector_fp)API.fp[i++]; + DLLConvertEulerToAxisAmount = (ConvertEulerToAxisAmount_fp)API.fp[i++]; + DLLConvertAxisAmountToEuler = (ConvertAxisAmountToEuler_fp)API.fp[i++]; + DLLvm_GetMagnitudeFast = (vm_GetMagnitudeFast_fp)API.fp[i++]; + DLLvm_MakeIdentity = (vm_MakeIdentity_fp)API.fp[i++]; + DLLvm_MakeVectorZero = (vm_MakeVectorZero_fp)API.fp[i++]; + DLLvm_MakeAngleZero = (vm_MakeAngleZero_fp)API.fp[i++]; + DLLvm_VectorMulTMatrix = (vm_VectorMulTMatrix_fp)API.fp[i++]; + DLLvm_MatrixMul = (vm_MatrixMul_fp)API.fp[i++]; + DLLvm_MatrixMulTMatrix = (vm_MatrixMulTMatrix_fp)API.fp[i++]; + DLLvm_DotProduct = (vm_DotProduct_fp)API.fp[i++]; + DLLvm_SubVectors = (vm_SubVectors_fp)API.fp[i++]; + DLLvm_AddVectors = (vm_AddVectors_fp)API.fp[i++]; + DLLvm_AverageVector = (vm_AverageVector_fp)API.fp[i++]; + DLLvm_ScaleVector = (vm_ScaleVector_fp)API.fp[i++]; + DLLvm_ScaleAddVector = (vm_ScaleAddVector_fp)API.fp[i++]; + DLLvm_DivVector = (vm_DivVector_fp)API.fp[i++]; + DLLvm_NormalizeVectorFast = (vm_NormalizeVectorFast_fp)API.fp[i++]; + DLLvm_ClearMatrix = (vm_ClearMatrix_fp)API.fp[i++]; + DLLvm_AnglesToMatrix = (vm_AnglesToMatrix_fp)API.fp[i++]; + DLLvm_Orthogonalize = (vm_Orthogonalize_fp)API.fp[i++]; + DLLvm_VectorToMatrix = (vm_VectorToMatrix_fp)API.fp[i++]; + DLLvm_VectorAngleToMatrix = (vm_VectorAngleToMatrix_fp)API.fp[i++]; + DLLvm_SinCos = (vm_SinCos_fp)API.fp[i++]; + DLLvm_GetSlope = (vm_GetSlope_fp)API.fp[i++]; + DLLvm_GetPerp = (vm_GetPerp_fp)API.fp[i++]; + DLLvm_GetNormal = (vm_GetNormal_fp)API.fp[i++]; + DLLvm_VectorDistance = (vm_VectorDistance_fp)API.fp[i++]; + DLLvm_VectorDistanceQuick = (vm_VectorDistanceQuick_fp)API.fp[i++]; + DLLvm_GetNormalizedDir = (vm_GetNormalizedDir_fp)API.fp[i++]; + DLLvm_GetNormalizedDirFast = (vm_GetNormalizedDirFast_fp)API.fp[i++]; + DLLvm_ExtractAnglesFromMatrix = (vm_ExtractAnglesFromMatrix_fp)API.fp[i++]; + DLLvm_DeltaAngVec = (vm_DeltaAngVec_fp)API.fp[i++]; + DLLvm_DeltaAngVecNorm = (vm_DeltaAngVecNorm_fp)API.fp[i++]; + DLLvm_DistToPlane = (vm_DistToPlane_fp)API.fp[i++]; + DLLvm_CalcDetValue = (vm_CalcDetValue_fp)API.fp[i++]; + DLLvm_MakeInverseMatrix = (vm_MakeInverseMatrix_fp)API.fp[i++]; + DLLvm_SinCosToMatrix = (vm_SinCosToMatrix_fp)API.fp[i++]; + DLLvm_GetCentroid = (vm_GetCentroid_fp)API.fp[i++]; + DLLvm_MakeRandomVector = (vm_MakeRandomVector_fp)API.fp[i++]; + DLLvm_ComputeBoundingSphere = (vm_ComputeBoundingSphere_fp)API.fp[i++]; + DLLvm_GetCentroidFast = (vm_GetCentroidFast_fp)API.fp[i++]; + DLLRenderHUDGetTextLineWidth = (RenderHUDGetTextLineWidth_fp)API.fp[i++]; + DLLRenderHUDGetTextHeight = (RenderHUDGetTextHeight_fp)API.fp[i++]; + DLLStartFrame = (StartFrame_fp)API.fp[i++]; + DLLEndFrame = (EndFrame_fp)API.fp[i++]; + DLLResetFacings = (ResetFacings_fp)API.fp[i++]; + DLLGameRenderWorld = (GameRenderWorld_fp)API.fp[i++]; + DLLGetFrameParameters = (GetFrameParameters_fp)API.fp[i++]; + DLLrend_SetZBufferState = (rend_SetZBufferState_fp)API.fp[i++]; + DLLrend_SetLighting = (rend_SetLighting_fp)API.fp[i++]; + DLLrend_SetColorModel = (rend_SetColorModel_fp)API.fp[i++]; + DLLrend_SetTextureType = (rend_SetTextureType_fp)API.fp[i++]; + DLLrend_DrawPolygon = (rend_DrawPolygon_fp)API.fp[i++]; + DLLrend_SetMipState = (rend_SetMipState_fp)API.fp[i++]; + DLLrend_SetFogState = (rend_SetFogState_fp)API.fp[i++]; + DLLrend_SetFiltering = (rend_SetFiltering_fp)API.fp[i++]; + DLLrend_SetOverlayMap = (rend_SetOverlayMap_fp)API.fp[i++]; + DLLrend_SetOverlayType = (rend_SetOverlayType_fp)API.fp[i++]; + DLLrend_ClearScreen = (rend_ClearScreen_fp)API.fp[i++]; + DLLrend_SetPixel = (rend_SetPixel_fp)API.fp[i++]; + DLLrend_GetPixel = (rend_GetPixel_fp)API.fp[i++]; + DLLrend_FillCircle = (rend_FillCircle_fp)API.fp[i++]; + DLLrend_DrawCircle = (rend_DrawCircle_fp)API.fp[i++]; + DLLrend_SetAlphaType = (rend_SetAlphaType_fp)API.fp[i++]; + DLLrend_SetAlphaValue = (rend_SetAlphaValue_fp)API.fp[i++]; + DLLrend_SetWrapType = (rend_SetWrapType_fp)API.fp[i++]; + DLLrend_SetZBias = (rend_SetZBias_fp)API.fp[i++]; + DLLrend_SetZBufferWriteMask = (rend_SetZBufferWriteMask_fp)API.fp[i++]; + DLLrend_GetLFBLock = (rend_GetLFBLock_fp)API.fp[i++]; + DLLrend_ReleaseLFBLock = (rend_ReleaseLFBLock_fp)API.fp[i++]; + DLLrend_DrawLFBBitmap = (rend_DrawLFBBitmap_fp)API.fp[i++]; + DLLrend_DrawSpecialLine = (rend_DrawSpecialLine_fp)API.fp[i++]; + DLLfvi_FindIntersection = (fvi_FindIntersection_fp)API.fp[i++]; + DLLfvi_QuickDistFaceList = (fvi_QuickDistFaceList_fp)API.fp[i++]; + DLLfvi_QuickDistCellList = (fvi_QuickDistCellList_fp)API.fp[i++]; + DLLfvi_QuickDistObjectList = (fvi_QuickDistObjectList_fp)API.fp[i++]; + DLLfvi_QuickRoomCheck = (fvi_QuickRoomCheck_fp)API.fp[i++]; + DLLObjSetPos = (ObjSetPos_fp)API.fp[i++]; + DLLSetObjectDeadFlag = (SetObjectDeadFlag_fp)API.fp[i++]; + DLLtaunt_AreEnabled = (taunt_AreEnabled_fp)API.fp[i++]; + DLLtaunt_Enable = (taunt_Enable_fp)API.fp[i++]; + GetPlayerRankIndex = (GetPlayerRankIndex_fp)API.fp[i++]; + DLLVisEffectAllocate = (VisEffectAllocate_fp)API.fp[i++]; + DLLVisEffectFree = (VisEffectFree_fp)API.fp[i++]; + DLLVisEffectInitType = (VisEffectInitType_fp)API.fp[i++]; + DLLVisEffectCreate = (VisEffectCreate_fp)API.fp[i++]; + DLLVisEffectLink = (VisEffectLink_fp)API.fp[i++]; + DLLVisEffectUnlink = (VisEffectUnlink_fp)API.fp[i++]; + DLLVisEffectRelink = (VisEffectRelink_fp)API.fp[i++]; + DLLVisEffectDelete = (VisEffectDelete_fp)API.fp[i++]; + DLLCreateRandomSparks = (CreateRandomSparks_fp)API.fp[i++]; + DLLCreateRandomLineSparks = (CreateRandomLineSparks_fp)API.fp[i++]; + DLLVisEffectCreateControlled = (VisEffectCreateControlled_fp)API.fp[i++]; + DLLCreateRandomParticles = (CreateRandomParticles_fp)API.fp[i++]; + DLLAttachRandomNapalmEffectsToObject = (AttachRandomNapalmEffectsToObject_fp)API.fp[i++]; + DLLInitObjectScripts = (InitObjectScripts_fp)API.fp[i++]; + DLLg3_StartFrame = (g3_StartFrame_fp)API.fp[i++]; + DLLg3_EndFrame = (g3_EndFrame_fp)API.fp[i++]; + DLLg3_GetViewPosition = (g3_GetViewPosition_fp)API.fp[i++]; + DLLg3_GetViewMatrix = (g3_GetViewMatrix_fp)API.fp[i++]; + DLLg3_GetUnscaledMatrix = (g3_GetUnscaledMatrix_fp)API.fp[i++]; + DLLg3_StartInstanceMatrix = (g3_StartInstanceMatrix_fp)API.fp[i++]; + DLLg3_StartInstanceAngles = (g3_StartInstanceAngles_fp)API.fp[i++]; + DLLg3_DoneInstance = (g3_DoneInstance_fp)API.fp[i++]; + DLLg3_CheckNormalFacing = (g3_CheckNormalFacing_fp)API.fp[i++]; + DLLg3_RotatePoint = (g3_RotatePoint_fp)API.fp[i++]; + DLLg3_ProjectPoint = (g3_ProjectPoint_fp)API.fp[i++]; + DLLg3_CalcPointDepth = (g3_CalcPointDepth_fp)API.fp[i++]; + DLLg3_Point2Vec = (g3_Point2Vec_fp)API.fp[i++]; + DLLg3_CodePoint = (g3_CodePoint_fp)API.fp[i++]; + DLLg3_RotateDeltaX = (g3_RotateDeltaX_fp)API.fp[i++]; + DLLg3_RotateDeltaY = (g3_RotateDeltaY_fp)API.fp[i++]; + DLLg3_RotateDeltaZ = (g3_RotateDeltaZ_fp)API.fp[i++]; + DLLg3_RotateDeltaVec = (g3_RotateDeltaVec_fp)API.fp[i++]; + DLLg3_AddDeltaVec = (g3_AddDeltaVec_fp)API.fp[i++]; + DLLg3_DrawPoly = (g3_DrawPoly_fp)API.fp[i++]; + DLLg3_DrawSphere = (g3_DrawSphere_fp)API.fp[i++]; + DLLg3_CheckAndDrawPoly = (g3_CheckAndDrawPoly_fp)API.fp[i++]; + DLLg3_DrawLine = (g3_DrawLine_fp)API.fp[i++]; + DLLg3_DrawBitmap = (g3_DrawBitmap_fp)API.fp[i++]; + DLLg3_DrawRotatedBitmap = (g3_DrawRotatedBitmap_fp)API.fp[i++]; + DLLg3_DrawBox = (g3_DrawBox_fp)API.fp[i++]; + DLLg3_SetCustomClipPlane = (g3_SetCustomClipPlane_fp)API.fp[i++]; + DLLg3_SetFarClipZ = (g3_SetFarClipZ_fp)API.fp[i++]; + DLLg3_ClipPolygon = (g3_ClipPolygon_fp)API.fp[i++]; + DLLg3_FreeTempPoints = (g3_FreeTempPoints_fp)API.fp[i++]; + DLLg3_GetMatrixScale = (g3_GetMatrixScale_fp)API.fp[i++]; + DLLg3_SetTriangulationTest = (g3_SetTriangulationTest_fp)API.fp[i++]; + DLLg3_DrawSpecialLine = (g3_DrawSpecialLine_fp)API.fp[i++]; + DLLg3_DrawPlanarRotatedBitmap = (g3_DrawPlanarRotatedBitmap_fp)API.fp[i++]; + DLLPlayerStopSounds = (PlayerStopSounds_fp)API.fp[i++]; + DLLFindArg = (FindArg_fp)API.fp[i++]; + DLLFireWeaponFromObject = (FireWeaponFromObject_fp)API.fp[i++]; + DLLCreateAndFireWeapon = (CreateAndFireWeapon_fp)API.fp[i++]; + DLLSelectNextCameraView = (SelectNextCameraView_fp)API.fp[i++]; + Inven_Add = (dInven_Add_fp)API.fp[i++]; + Inven_AddObject = (dInven_AddObject_fp)API.fp[i++]; + Inven_AddCounterMeasure = (dInven_AddCounterMeasure_fp)API.fp[i++]; + Inven_Remove = (dInven_Remove_fp)API.fp[i++]; + Inven_Use = (dInven_Use_fp)API.fp[i++]; + Inven_UseObjHandle = (dInven_UseObjHandle_fp)API.fp[i++]; + Inven_Size = (dInven_Size_fp)API.fp[i++]; + Inven_CheckItem = (dInven_CheckItem_fp)API.fp[i++]; + Inven_Reset = (dInven_Reset_fp)API.fp[i++]; + Inven_ResetPos = (dInven_ResetPos_fp)API.fp[i++]; + Inven_NextPos = (dInven_NextPos_fp)API.fp[i++]; + Inven_PrevPos = (dInven_PrevPos_fp)API.fp[i++]; + Inven_GetPosTypeID = (dInven_GetPosTypeID_fp)API.fp[i++]; + Inven_GetAuxPosTypeID = (dInven_GetAuxPosTypeID_fp)API.fp[i++]; + Inven_GetPosDescription = (dInven_GetPosDescription_fp)API.fp[i++]; + Inven_GetPosIconName = (dInven_GetPosIconName_fp)API.fp[i++]; + Inven_GetPosName = (dInven_GetPosName_fp)API.fp[i++]; + Inven_GetPosInfo = (dInven_GetPosInfo_fp)API.fp[i++]; + Inven_GetPosCount = (dInven_GetPosCount_fp)API.fp[i++]; + Inven_AtBeginning = (dInven_AtBeginning_fp)API.fp[i++]; + Inven_AtEnd = (dInven_AtEnd_fp)API.fp[i++]; + Inven_GotoPos = (dInven_GotoPos_fp)API.fp[i++]; + Inven_GotoPosTypeID = (dInven_GotoPosTypeID_fp)API.fp[i++]; + Inven_UsePos = (dInven_UsePos_fp)API.fp[i++]; + Inven_GetPos = (dInven_GetPos_fp)API.fp[i++]; + Inven_ValidatePos = (dInven_ValidatePos_fp)API.fp[i++]; + Inven_IsSelectable = (dInven_IsSelectable_fp)API.fp[i++]; + Inven_IsUsable = (dInven_IsUsable_fp)API.fp[i++]; + Inven_GetTypeIDCount = (dInven_GetTypeIDCount_fp)API.fp[i++]; + Inven_FindPos = (dInven_FindPos_fp)API.fp[i++]; + Inven_GetInventoryItemList = (dInven_GetInventoryItemList_fp)API.fp[i++]; + + // Do variables + Player_num=(int *)API.vp[0]; + Highest_room_index=(int *)API.vp[1]; + Game_window_w=(int *)API.vp[2]; + Game_window_h=(int *)API.vp[3]; + Game_fonts=(int *)API.vp[4]; + Frametime=(float *)API.vp[5]; + Gametime=(float *)API.vp[6]; + ShieldDelta=(float *)API.vp[7]; + Game_interface_mode = (int *)API.vp[8]; + LocalD3Dir = (char *)API.vp[9]; + m_bTrackerGame = (int *)API.vp[10]; + Local_object_list = (ushort *)API.vp[11]; + Server_object_list = (ushort *)API.vp[12]; + Dedicated_server = (bool *)API.vp[13]; + Player_colors = (ddgr_color *)API.vp[14]; + Hud_aspect_x = (float *)API.vp[15]; + Hud_aspect_y = (float *)API.vp[16]; + Viewer_object = (object **)API.vp[17]; + Render_zoom = (float *)API.vp[18]; + Game_window_x = (int *)API.vp[19]; + Game_window_y = (int *)API.vp[20]; + Poly_models = (poly_model *)API.vp[21]; + VisEffects = (vis_effect *)API.vp[22]; + Highest_vis_effect_index = (int *)API.vp[23]; + Multi_next_level = (int *)API.vp[24]; + Level_info = (level_info *)API.vp[25]; + GameArgs = (void *)API.vp[26]; + Camera_view_mode = (int *)API.vp[27]; + Object_info = (object_info *)API.vp[28]; diff --git a/netgames/dmfc/dmfcfunctions.cpp b/netgames/dmfc/dmfcfunctions.cpp new file mode 100644 index 000000000..ea501f110 --- /dev/null +++ b/netgames/dmfc/dmfcfunctions.cpp @@ -0,0 +1,485 @@ +/* +* $Logfile: /DescentIII/Main/Dmfc/dmfcfunctions.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:57:20 $ +* $Author: kevinb $ +* +* Function pointers +* +* $Log: dmfcfunctions.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb +* initial 1.5 import +* + * + * 26 8/16/99 5:38p Jeff + * final updates for patch + * + * 25 8/15/99 4:36p Jeff + * finished exporting all inventory class functions. export object_info + * array. added check for -nooutragelogo to display Outrage logo display. + * + * 24 8/11/99 5:31p Jeff + * addon tablefile function returns a bool now + * + * 23 8/11/99 1:22p Jeff + * exported needed functions for camera windows + * + * 22 7/20/99 11:27a Jeff + * created functions to fire weapons + * + * 21 7/11/99 3:31p Jeff + * exported game arguments, made command line option to specify + * autoexec.dmfc + * + * 20 7/07/99 12:16p Jeff + * all mangles symbol names fixed. Finished making interface functions. + * + * 19 5/20/99 5:31p Jeff + * exported PlayerStopSounds + * + * 18 5/19/99 11:49p Jeff + * final preparations for dmfc version 1.0 + * + * 17 5/19/99 5:27p Jeff + * fixed some function prototypes + * + * 16 5/19/99 2:44a Jeff + * CreateTextItem has a third parameter + * + * 15 5/03/99 8:39a Jeff + * tons of Entropy fixes + * + * 14 4/30/99 7:36p Jeff + * exported vis_effects to dmfc + * + * 13 3/27/99 4:53p Jeff + * player rankings in multiplayer games implemented. Fixed join message + * so it doesn't get cut off + * + * 12 3/22/99 6:20p Jeff + * added 2 more audio taunts. a mulitplayer event when someone plays an + * audio taunt. option to disable audio taunts. + * + * 11 3/17/99 12:23p Jeff + * converted DMFC to be COM interface + * + * 10 3/12/99 7:40p Jeff + * removed enhanced objcreate, added enhanced objsetpos + * + * 9 3/11/99 6:30p Jeff + * numerous fixes to demo system in multiplayer games (when + * recording/playback a demo in a multiplayer game) + * + * 8 3/05/99 1:29p Jeff + * + * 7 2/25/99 8:54p Jeff + * Inventory supports level change persistant items. Inventory supports + * time-out objects. Inventory Reset changed (takes a level of reset + * now). Quad lasers stay across level change (single player). Guidebot + * bug fixed (now back in ship on level start). Quads time out when + * spewed. Invulnerability and cloak powerups no longer use game + * event/callbacks, so they can be saved in game saves (moved to + * MakePlayerInvulnerable and MakeObjectInvisible) + * + * 6 2/10/99 11:36p Jeff + * exported renderer and fvi functions + * + * 5 2/08/99 5:22p Jeff + * exported rend_SetZBuffer...fixed up small view of stats + * + * 4 2/08/99 12:06a Jeff + * exported some new functions and variables + * + * 3 2/06/99 6:59p Jeff + * created RenderHUDGetTextLineWidth and RenderHUDGetTextHeight + * + * 2 2/03/99 4:09p Jeff + * moved function pointers to seperate file. created autoexec.dmfc +* +* $NoKeywords: $ +*/ + +#include "DMFC.h" +#include "dmfcinternal.h" +#include "dmfcinputcommands.h" +#include "gamedll_header.h" + +// Determine if we are building the DLL, or not. If we are building the DMFC +// DLL, than we need to define some things +#if defined(DMFC_DLL) +#define DMFCFUNCTION DLLFUNCEXPORT //for DMFC DLL library +#else +#define DMFCFUNCTION DLLFUNCIMPORT //for modules built for dmfc dll +#endif + +DMFCFUNCTION void (*DLLGetGameAPI)(game_api *); +DMFCFUNCTION bool (*DLLAddHUDMessage)(char *format, ... ); +DMFCFUNCTION void (*DLLDebug_ConsolePrintf)(int n, char *format, ... ); +DMFCFUNCTION void (*DLLMultiSendClientExecuteDLL)(int eventnum,int me_objnum,int it_objnum,int to,dllinfo *info); +DMFCFUNCTION void (*DLLMultiSendObject)(object *obj,ubyte announce,ubyte demo_record); +DMFCFUNCTION void (*DLLMultiPaintGoalRooms)(int *texcolors); +DMFCFUNCTION void (*DLLMultiSendSpecialPacket)(int slot, ubyte *outdata, int size); +DMFCFUNCTION void (*DLLComputeRoomCenter)(vector *vp,room *rp); +DMFCFUNCTION int (*DLLGetGoalRoomForTeam)(int teamnum); +DMFCFUNCTION int (*DLLObjCreate)(ubyte type,ushort id,int roomnum,vector *pos,const matrix *orient,int parent_handle); +DMFCFUNCTION int (*DLLFindObjectIDName)(char *name); +DMFCFUNCTION void (*DLLObjSetPosNoMark)(object *objp,vector *newpos,int roomnum,matrix *orient,bool f_update_attached_children); +DMFCFUNCTION void (*DLLObjSetPos)(object *objp,vector *newpos,int roomnum,matrix *orient,bool f_update_attached_children); +DMFCFUNCTION void (*DLLSetMaxTeams)(int num); +DMFCFUNCTION int (*DLLIncTeamScore)(int team,int amount); +DMFCFUNCTION bool (*DLLInvCheckItem)(int pnum,int type,int id); +DMFCFUNCTION bool (*DLLInvAddTypeID) (int pnum, int type,int id,int aux_type,int aux_id,int flags,char *description); +DMFCFUNCTION bool (*DLLInvRemove)(int pnum, int type,int id); +DMFCFUNCTION void (*DLLPlayerSetLighting)(int slot,float dist,float r,float g,float b); +DMFCFUNCTION int (*DLLFindShipName)(char *name); +DMFCFUNCTION void (*DLLPlayerSetRotatingBall)(int slot,int num,float speed,float *r,float *g,float *b); +DMFCFUNCTION void (*DLLPlayerChangeShip)(int slot,int ship_index); +DMFCFUNCTION int (*DLLInvGetTypeIDCount)(int playernum,int type,int id); +DMFCFUNCTION int (*DLLPlay3dSound)(int sound_index, object *cur_obj, float volume,int flags); +DMFCFUNCTION int (*DLLFindSoundName)(char *str); +DMFCFUNCTION int (*DLLSpewCreate)(spewinfo *spew); +DMFCFUNCTION void (*DLLSpewClearEvent)(int handle,bool force); +DMFCFUNCTION int (*DLLbm_AllocLoadFileBitmap) (const char *filename,int mipped,int format); +DMFCFUNCTION void (*DLLbm_FreeBitmap)(int handle); +DMFCFUNCTION void (*DLLrend_DrawScaledBitmap)(int x1,int y1,int x2,int y2,int bm,float u0,float v0,float u1,float v1,float zval,int color,float *alphas); +DMFCFUNCTION void (*DLLgrtext_Printf)(int x, int y, const char *fmt, ...); +DMFCFUNCTION void (*DLLgrtext_Flush)(void); +DMFCFUNCTION void (*DLLgrtext_SetColor)(ddgr_color col); +DMFCFUNCTION void (*DLLgrtext_SetFancyColor)(ddgr_color col1, ddgr_color col2, ddgr_color col3, ddgr_color col4); +DMFCFUNCTION void (*DLLgrtext_SetAlpha)(ubyte alpha); +DMFCFUNCTION ubyte (*DLLgrtext_GetAlpha)(void); +DMFCFUNCTION void (*DLLgrtext_SetFont)(int font_handle); +DMFCFUNCTION int (*DLLgrtext_GetFont)(void); +DMFCFUNCTION int (*DLLgrtext_GetTextLineWidth)(const char *str); +DMFCFUNCTION int (*DLLgrfont_GetHeight)(int font); +DMFCFUNCTION void (*DLLgrtext_CenteredPrintf)(int xoff, int y, const char *fmt, ...); +DMFCFUNCTION bool (*DLLAddColoredHUDMessage)(ddgr_color color,char *format,...); +DMFCFUNCTION int (*DLLbm_h)(int handle,int miplevel); +DMFCFUNCTION int (*DLLbm_w)(int handle,int miplevel); +DMFCFUNCTION void (*DLLrend_DrawSimpleBitmap)(int bm_handle,int x,int y); +DMFCFUNCTION void (*DLLMultiClientSendSpecialPacket)(ubyte *outdate,int size); +DMFCFUNCTION bool (*DLLAddBlinkingHUDMessage)(char *format, ... ); +DMFCFUNCTION void (*DLLInvReset)(int playernum,bool reset_all); +DMFCFUNCTION void (*DLLAddHUDItem)(tHUDItem *item); +DMFCFUNCTION void (*DLLRenderHUDQuad)(int x, int y, int w, int h, float u0, float v0, float u1, float v1, int bm, ubyte alpha,int sat_count); +DMFCFUNCTION void (*DLLRenderHUDText)(ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *fmt, ...); +DMFCFUNCTION void (*DLLMultiEndLevel)(void); +DMFCFUNCTION ushort *(*DLLbm_data)(int handle,int miplevel); +DMFCFUNCTION int (*DLLbm_AllocBitmap)(int w,int h,int add_mem); +DMFCFUNCTION void (*DLLrend_FillRect)(ddgr_color color,int x1,int y1,int x2,int y2); +DMFCFUNCTION bool (*DLLbm_CreateChunkedBitmap)(int bm_handle, chunked_bitmap *chunk); +DMFCFUNCTION void (*DLLbm_DestroyChunkedBitmap)(chunked_bitmap *chunk); +DMFCFUNCTION void (*DLLrend_DrawChunkedBitmap)(chunked_bitmap *chunk, int x, int y, ubyte alpha); +DMFCFUNCTION void (*DLLrend_DrawScaledChunkedBitmap)(chunked_bitmap *chunk, int x, int y, int neww, int newh, ubyte alpha); +DMFCFUNCTION void (*DLLOpenCFILE)(CFILE **handle,const char *filename,const char *mode); +DMFCFUNCTION void (*DLLcfclose)(CFILE * cfp); +DMFCFUNCTION int (*DLLcfeof)(CFILE *cfp); +DMFCFUNCTION int (*DLLcfexist)(const char * filename); +DMFCFUNCTION int (*DLLcf_ReadBytes)(ubyte *buf, int count, CFILE *cfp); +DMFCFUNCTION int (*DLLcf_ReadInt)(CFILE *cfp); +DMFCFUNCTION short (*DLLcf_ReadShort)(CFILE *cfp); +DMFCFUNCTION sbyte (*DLLcf_ReadByte)(CFILE *cfp); +DMFCFUNCTION float (*DLLcf_ReadFloat)(CFILE *cfp); +DMFCFUNCTION double (*DLLcf_ReadDouble)(CFILE *cfp); +DMFCFUNCTION int (*DLLcf_ReadString)(char * buf, size_t n, CFILE * cfp ); +DMFCFUNCTION int (*DLLcf_WriteBytes)(const ubyte *buf, int count, CFILE *cfp); +DMFCFUNCTION int (*DLLcf_WriteString)(CFILE *cfp, const char *buf); +DMFCFUNCTION void (*DLLcf_WriteInt)(CFILE *cfp,int i); +DMFCFUNCTION void (*DLLcf_WriteShort)(CFILE *cfp,short s); +DMFCFUNCTION void (*DLLcf_WriteByte)(CFILE *cfp,sbyte b); +DMFCFUNCTION void (*DLLcf_WriteFloat)(CFILE *cfp,float f); +DMFCFUNCTION void (*DLLcf_WriteDouble)(CFILE *cfp,double d); +DMFCFUNCTION bool (*DLLcf_CopyFile)(char *dest,const char *src); +DMFCFUNCTION bool (*DLLcf_Diff)(const char *a,const char *b); +DMFCFUNCTION void (*DLLMultiDisconnectPlayer)(int slot); +DMFCFUNCTION void (*DLLnw_GetNumbersFromHostAddress)(network_address * address,char *str); +DMFCFUNCTION int (*DLLnw_GetThisIP)(void); +DMFCFUNCTION bool (*DLLCreateStringTable)(char *filename,char ***table,int *size); +DMFCFUNCTION void (*DLLDestroyStringTable)(char **table,int size); +DMFCFUNCTION void (*DLLRenderHUDTextFlags)(int flags, ddgr_color col, ubyte alpha, int sat_count, int x, int y, char *fmt, ...); +DMFCFUNCTION void (*DLLPlayerSetHUDNameFOV)(int fov); +DMFCFUNCTION void (*DLLGetUltimateParentForObject)(object **parent,object *child); +DMFCFUNCTION void (*DLLSetObjectDeadFlagRaw)(object *obj,bool tell_clients_to_remove,bool tell_clients_to_play_sound); +DMFCFUNCTION void (*DLLSetObjectDeadFlag)(object *obj,bool tell_clients_to_remove,bool tell_clients_to_play_sound); +DMFCFUNCTION void (*FatalError)(char *reason); +DMFCFUNCTION int (*DLLMultiMatchWeapon)(uint unique_id); +DMFCFUNCTION uint (*DLLMultiGetMatchChecksum)(int type,int id); +DMFCFUNCTION int (*DLLFindWeaponName) (char *name); +DMFCFUNCTION int (*DLLcf_OpenLibrary)(const char *libname); +DMFCFUNCTION void (*DLLcf_CloseLibrary)(int handle); +DMFCFUNCTION void (*DLLMultiSendRequestToObserve) (int mode,int on,int objnum); +DMFCFUNCTION int (*DLLFindTextureName)(char *name); +DMFCFUNCTION bool (*DLLApplyDamageToPlayer)(object *playerobj, object *killer, int damage_type, float damage_amount,int server_says,int weapon_id,bool playsound); +DMFCFUNCTION int (*DLLMultiMatchGeneric)(uint unique_id); +DMFCFUNCTION void (*DLLSetUITextItemText)(void *uit,char *newtext,unsigned int color); +DMFCFUNCTION void *(*DLLNewUIWindowCreate)(int x, int y, int w, int h, int flags); +DMFCFUNCTION void (*DLLNewUIWindowDestroy)(void *deswin); +DMFCFUNCTION void (*DLLNewUIWindowOpen)(void *deswin); +DMFCFUNCTION void (*DLLNewUIWindowClose)(void *deswin); +DMFCFUNCTION void *(*DLLTextCreate)(void *parentwin,void * textitem, int x, int y, int flags); +DMFCFUNCTION void *(*DLLEditCreate)(void *parentwin, int id, int x, int y, int w, int h, int flags); +DMFCFUNCTION void *(*DLLButtonCreate)(void *parentwin, int id,void *titleitem, int x, int y, int w, int h, int flags); +DMFCFUNCTION void *(*DLLListCreate)(void *parentwin, int id, int x, int y, int w, int h, int flags); +DMFCFUNCTION void (*DLLListRemoveAll)(void *item); +DMFCFUNCTION void (*DLLListAddItem)(void *item,void *uitext); +DMFCFUNCTION void (*DLLListRemoveItem)(void *item,void *txtitem); +DMFCFUNCTION void (*DLLListSelectItem)(void *item,void *txtitem); +DMFCFUNCTION char *(*DLLListGetItem)(void *item,int index); +DMFCFUNCTION int (*DLLListGetSelectedIndex)(void *item); +DMFCFUNCTION void (*DLLListSetSelectedIndex)(void *item,int index); +DMFCFUNCTION void (*DLLEditSetText)(void *item,char *buff); +DMFCFUNCTION void (*DLLEditGetText)(void *item,char *buff,int len); +DMFCFUNCTION int (*DLLDoUI)(void); +DMFCFUNCTION int (*DLLDoMessageBox)(const char *title, const char *msg, int type, ddgr_color title_color,ddgr_color text_color); +DMFCFUNCTION void (*DLLDescentDefer)(void); +DMFCFUNCTION void *(*DLLNewUIGameWindowCreate)(int x, int y, int w, int h, int flags); +DMFCFUNCTION void (*DLLNewUIGameWindowDestroy)(void *item); +DMFCFUNCTION void (*DLLNewUIGameWindowOpen)(void *item); +DMFCFUNCTION void (*DLLNewUIGameWindowClose)(void *item); +DMFCFUNCTION void *(*DLLHotSpotCreate)(void *parentwin, int id, int key,void *txtitemoff,void *txtitemon, int x, int y, int w, int h, int flags); +DMFCFUNCTION int (*DLLPollUI)(void); +DMFCFUNCTION void (*DLLRemoveUITextItem)(void *item); +DMFCFUNCTION void *(*DLLCreateNewUITextItem)(char *newtext,unsigned int color,int font); +DMFCFUNCTION void (*DLLRemoveUIBmpItem)(void *item); +DMFCFUNCTION void *(*DLLCreateNewUIBmpItem)(int handle,ubyte alpha); +DMFCFUNCTION void *(*DLLUIConsoleGadgetCreate)(void *parentid, int id, int x, int y, int font, int cols, int rows, int flags); +DMFCFUNCTION void (*DLLUIConsoleGadgetputs)(void *item, const char *str); +DMFCFUNCTION void (*DLLNewUIWindowSetFocusOnEditGadget)(void * item,void *parent); +DMFCFUNCTION void *(*DLLOldListCreate)(void *parentitem, int id, int x, int y, int w, int h, int flags); +DMFCFUNCTION void (*DLLOldListRemoveAll)(void *item); +DMFCFUNCTION void (*DLLOldListAddItem)(void *item,void *uitext); +DMFCFUNCTION void (*DLLOldListRemoveItem)(void *item,void *txtitem); +DMFCFUNCTION void (*DLLOldListSelectItem)(void *item,void *txtitem); +DMFCFUNCTION void *(*DLLOldEditCreate)(void *parentitem, int id, int x, int y, int w, int h, int flags); +DMFCFUNCTION void (*DLLOldEditGetText)(void *item,char *buff,int len); +DMFCFUNCTION void (*DLLOldEditSetText)(void *item,char *newtext); +DMFCFUNCTION char *(*DLLOldListGetItem)(void *item,int index); +DMFCFUNCTION int (*DLLOldListGetSelectedIndex)(void *item); +DMFCFUNCTION void (*DLLToggleUICallback)(int state); +DMFCFUNCTION void (*DLLSetOldEditBufferLen)(void *item,int len); +DMFCFUNCTION void (*DLLNewUIWindowLoadBackgroundImage)(void *item,const char *image_name); +DMFCFUNCTION void (*DLLDeleteUIItem)(void *delitem); +DMFCFUNCTION void (*DLLHotSpotSetStates)(void *hs,void *texton,void *textoff); +DMFCFUNCTION void (*DLLSetUICallback)(void (*fn)()); +DMFCFUNCTION void (*DLLGetUICallback)(void **fn); +DMFCFUNCTION void (*DLLSuspendControls)(void); +DMFCFUNCTION void (*DLLResumeControls)(void); +DMFCFUNCTION void (*DLLShowCursor)(void); +DMFCFUNCTION void (*DLLHideCursor)(void); +DMFCFUNCTION void (*DLLGameFrame)(void); +DMFCFUNCTION void (*DPrintf)(const char *format, ... ); +DMFCFUNCTION void (*DLLassert)(int,char *,char *,int); +DMFCFUNCTION void (*DLLddio_SplitPath)(const char *srcPath,char *path,char *filename, char *ext); +DMFCFUNCTION void (*DLLddio_MakePath)(char* newPath, const char* absolutePathHeader, const char* subDir, ...); +DMFCFUNCTION int (*DLLPlay2dSound)(int sound_index, float volume); +DMFCFUNCTION void (*DLLTouchSound)(int sound_index); +DMFCFUNCTION bool (*DatabaseRead1)(const char *label, char *entry, int *entrylen); +DMFCFUNCTION bool (*DatabaseRead2)(const char *label, void *entry, int wordsize); +DMFCFUNCTION bool (*DatabaseRead3)(const char *label, bool *entry); +DMFCFUNCTION bool (*DatabaseWrite1)(const char *label, const char *entry, int entrylen); +DMFCFUNCTION bool (*DatabaseWrite2)(const char *label, int entry); +DMFCFUNCTION bool (*DLLAttachObject)(object *parent, char parent_ap, object *child, char child_ap, bool f_use_aligned); +DMFCFUNCTION bool (*DLLObjGet)(int handle,object **obj); +DMFCFUNCTION int (*DLLGetUIItemWidth)(void *item); +DMFCFUNCTION int (*DLLGetUIItemHeight)(void *item); +DMFCFUNCTION void *(*DLLSliderCreate)(void *parent, int id, int x, int y, int flags); +DMFCFUNCTION void (*DLLSliderSetRange)(void *slider,int range); +DMFCFUNCTION int (*DLLSliderGetRange)(void *slider); +DMFCFUNCTION void (*DLLSliderSetPos)(void *slider,int pos); +DMFCFUNCTION int (*DLLSliderGetPos)(void *slider); +DMFCFUNCTION void (*DLLSliderSetSelectChangeCallback)(void *slider,void (*fn)(int)); +DMFCFUNCTION void (*DLLSliderSetSelectChangeCallbackWData)(void *slider,void (*fn)(int,void *),void *ptr); +DMFCFUNCTION void (*DLLTextSetTitle)(void *text,void *textitem); +DMFCFUNCTION bool (*DLLPPic_GetPilot)(ushort pilot_id,char *pilot_name,int buffersize); +DMFCFUNCTION int (*DLLPPic_GetBitmapHandle)(ushort pilot_id); +DMFCFUNCTION void (*DLLrend_DrawLine)(int x1,int y1,int x2,int y2); +DMFCFUNCTION void (*DLLrend_SetFlatColor) (ddgr_color color); +DMFCFUNCTION void (*DLLMultiSetLogoState)(bool state); +DMFCFUNCTION void (*DLLPlayerSpewInventory)(object *obj,bool spew_energy_and_shield,bool spew_nonspewable); +DMFCFUNCTION int (*DLLPlayerGetRandomStartPosition)(int slot); +DMFCFUNCTION void (*DLLInitPlayerNewShip) (int slot,int inven_reset); +DMFCFUNCTION void *(*DLLCheckBoxCreate)(void *parent, int id, void *title, int x, int y, int w, int h, int flags); +DMFCFUNCTION void (*DLLCheckBoxSetCheck)(void *cb,bool state); +DMFCFUNCTION bool (*DLLCheckBoxIsChecked)(void *cb); +DMFCFUNCTION unsigned long (*DLLnw_GetHostAddressFromNumbers) (char *str); +DMFCFUNCTION void (*TableFilesClear)(void); +DMFCFUNCTION bool (*TableFileAdd)(char *filename); +DMFCFUNCTION void (*DLLDebugBreak_callback_stop)(void); +DMFCFUNCTION void (*DLLDebugBreak_callback_resume)(void); +DMFCFUNCTION void (*DLLInt3MessageBox)(char *file,char *line); +DMFCFUNCTION void (*DLLGetUIItemPosition)(void *item,int *x,int *y,int *w,int *h); +DMFCFUNCTION bool (*DLLAttachObjectRadius)(object *parent, char parent_ap, object *child, float percent_rad); +DMFCFUNCTION bool (*DLLUnattachChildren)(object *parent); +DMFCFUNCTION bool (*DLLUnattachChild)(object *parent, char parent_ap); +DMFCFUNCTION bool (*DLLUnattachFromParent)(object *child); +DMFCFUNCTION float (*DLLvm_GetMagnitude)(vector *); +DMFCFUNCTION void (*DLLvm_MatrixMulVector)(vector *,vector *,matrix *); +DMFCFUNCTION void (*DLLphys_apply_force)(object *obj,vector *force_vec,short weapon_index); +DMFCFUNCTION void (*DLLphys_apply_rot)(object *obj,vector *force_vec); +DMFCFUNCTION void (*DLLvm_TransposeMatrix)(matrix *); +DMFCFUNCTION void (*DLLvm_CrossProduct)(vector *,vector *,vector *); +DMFCFUNCTION float (*DLLvm_NormalizeVector)(vector *); +DMFCFUNCTION void (*DLLConvertEulerToAxisAmount)(vector *e, vector *n, float *w); +DMFCFUNCTION void (*DLLConvertAxisAmountToEuler)(vector *n, float *w, vector *e); +DMFCFUNCTION float (*DLLvm_GetMagnitudeFast) (vector *); +DMFCFUNCTION void (*DLLvm_MakeIdentity)(matrix *); +DMFCFUNCTION void (*DLLvm_MakeVectorZero)(vector *v); +DMFCFUNCTION void (*DLLvm_MakeAngleZero)(angvec *a); +DMFCFUNCTION void (*DLLvm_VectorMulTMatrix)(vector *result,vector *v,matrix *m); +DMFCFUNCTION void (*DLLvm_MatrixMul)(matrix *,matrix *,matrix *); +DMFCFUNCTION void (*DLLvm_MatrixMulTMatrix)(matrix *dest,matrix *src0,matrix *src1); +DMFCFUNCTION float (*DLLvm_DotProduct)(vector *,vector *); +DMFCFUNCTION void (*DLLvm_SubVectors)(vector *,const vector *,const vector *); +DMFCFUNCTION void (*DLLvm_AddVectors)(vector *,vector *,vector *); +DMFCFUNCTION void (*DLLvm_AverageVector)(vector *,int); +DMFCFUNCTION void (*DLLvm_ScaleVector)(vector *,vector *,float); +DMFCFUNCTION void (*DLLvm_ScaleAddVector)(vector *d,vector *p,vector *v,float s); +DMFCFUNCTION void (*DLLvm_DivVector)(vector *,vector *,float); +DMFCFUNCTION float (*DLLvm_NormalizeVectorFast)(vector *); +DMFCFUNCTION void (*DLLvm_ClearMatrix)(matrix *); +DMFCFUNCTION void (*DLLvm_AnglesToMatrix)(matrix *,angle p,angle h,angle b); +DMFCFUNCTION void (*DLLvm_Orthogonalize)(matrix *m); +DMFCFUNCTION void (*DLLvm_VectorToMatrix)(matrix *m,vector *fvec,vector *uvec,vector *rvec); +DMFCFUNCTION void (*DLLvm_VectorAngleToMatrix)(matrix *m,vector *v,angle a); +DMFCFUNCTION void (*DLLvm_SinCos)(angle,float *,float *); +DMFCFUNCTION float (*DLLvm_GetSlope)(float,float,float,float); +DMFCFUNCTION void (*DLLvm_GetPerp)(vector *n,vector *a,vector *b,vector *c); +DMFCFUNCTION float (*DLLvm_GetNormal)(vector *n,vector *v0,vector *v1,vector *v2); +DMFCFUNCTION float (*DLLvm_VectorDistance)(const vector *a, const vector *b); +DMFCFUNCTION float (*DLLvm_VectorDistanceQuick)(vector *a,vector *b); +DMFCFUNCTION float (*DLLvm_GetNormalizedDir)(vector *dest,vector *end,vector *start); +DMFCFUNCTION float (*DLLvm_GetNormalizedDirFast)(vector *dest,vector *end,vector *start); +DMFCFUNCTION angvec *(*DLLvm_ExtractAnglesFromMatrix)(angvec *a,matrix *m); +DMFCFUNCTION angle (*DLLvm_DeltaAngVec)(vector *v0,vector *v1,vector *fvec); +DMFCFUNCTION angle (*DLLvm_DeltaAngVecNorm)(vector *v0,vector *v1,vector *fvec); +DMFCFUNCTION float (*DLLvm_DistToPlane)(vector *checkp,vector *norm,vector *planep); +DMFCFUNCTION float (*DLLvm_CalcDetValue)(matrix *det); +DMFCFUNCTION void (*DLLvm_MakeInverseMatrix)(matrix *dest); +DMFCFUNCTION void (*DLLvm_SinCosToMatrix)(matrix *m,float sinp,float cosp,float sinb,float cosb,float sinh,float cosh); +DMFCFUNCTION float (*DLLvm_GetCentroid)(vector *centroid,vector *src,int nv); +DMFCFUNCTION void (*DLLvm_MakeRandomVector)(vector *vec); +DMFCFUNCTION float (*DLLvm_ComputeBoundingSphere)(vector *center,vector *vecs,int num_verts); +DMFCFUNCTION float (*DLLvm_GetCentroidFast)(vector *centroid,vector *src,int nv); +DMFCFUNCTION int (*DLLRenderHUDGetTextLineWidth)(char *string); +DMFCFUNCTION int (*DLLRenderHUDGetTextHeight)(char *string); +DMFCFUNCTION void (*DLLStartFrame)(int x, int y, int x2, int y2, bool clear); +DMFCFUNCTION void (*DLLEndFrame)(void); +DMFCFUNCTION void (*DLLResetFacings)(void); +DMFCFUNCTION void (*DLLGameRenderWorld)(object *viewer,vector *viewer_eye,int viewer_roomnum,matrix *viewer_orient,float zoom,bool rear_view); +DMFCFUNCTION bool (*DLLGetFrameParameters)(int *x1,int *y1,int *x2,int *y2); +DMFCFUNCTION void (*DLLrend_SetZBufferState)(sbyte state); +DMFCFUNCTION void (*DLLrend_SetLighting)(light_state); +DMFCFUNCTION void (*DLLrend_SetColorModel) (color_model); +DMFCFUNCTION void (*DLLrend_SetTextureType) (texture_type); +DMFCFUNCTION void (*DLLrend_DrawPolygon)(int handle,g3Point **p,int nv,int map_type); +DMFCFUNCTION void (*DLLrend_SetMipState) (sbyte); +DMFCFUNCTION void (*DLLrend_SetFogState) (sbyte on); +DMFCFUNCTION void (*DLLrend_SetFiltering) (sbyte state); +DMFCFUNCTION void (*DLLrend_SetOverlayMap) (int handle); +DMFCFUNCTION void (*DLLrend_SetOverlayType) (ubyte type); +DMFCFUNCTION void (*DLLrend_ClearScreen) (ddgr_color color); +DMFCFUNCTION void (*DLLrend_SetPixel) (ddgr_color color,int x,int y); +DMFCFUNCTION ddgr_color (*DLLrend_GetPixel) (int x,int y); +DMFCFUNCTION void (*DLLrend_FillCircle)(ddgr_color col, int x, int y, int rad); +DMFCFUNCTION void (*DLLrend_DrawCircle)(int x, int y, int rad); +DMFCFUNCTION void (*DLLrend_SetAlphaType) (sbyte); +DMFCFUNCTION void (*DLLrend_SetAlphaValue) (ubyte val); +DMFCFUNCTION void (*DLLrend_SetWrapType) (wrap_type val); +DMFCFUNCTION void (*DLLrend_SetZBias) (float z_bias); +DMFCFUNCTION void (*DLLrend_SetZBufferWriteMask) (int state); +DMFCFUNCTION void (*DLLrend_GetLFBLock) (renderer_lfb *lfb); +DMFCFUNCTION void (*DLLrend_ReleaseLFBLock) (renderer_lfb *lfb); +DMFCFUNCTION void (*DLLrend_DrawLFBBitmap) (int sx,int sy,int w,int h,int dx,int dy,ushort *data,int rowsize); +DMFCFUNCTION void (*DLLrend_DrawSpecialLine) (g3Point *p0,g3Point *p1); +DMFCFUNCTION int (*DLLfvi_FindIntersection)(fvi_query *fq,fvi_info *hit_data, bool no_subdivision); +DMFCFUNCTION int (*DLLfvi_QuickDistFaceList)(int init_room_index, vector *pos, float rad, fvi_face_room_list *quick_fr_list, int max_elements); +DMFCFUNCTION int (*DLLfvi_QuickDistCellList)(int init_cell_index, vector *pos, float rad, int *quick_cell_list, int max_elements); +DMFCFUNCTION int (*DLLfvi_QuickDistObjectList)(vector *pos, int init_roomnum, float rad, short *object_index_list, int max_elements, bool f_lightmap_only, bool f_only_players_and_ais, bool f_include_non_collide_objects, bool f_stop_at_closed_doors); +DMFCFUNCTION bool (*DLLfvi_QuickRoomCheck)(vector *pos, room *cur_room, bool try_again); +DMFCFUNCTION bool (*DLLtaunt_AreEnabled)(void); +DMFCFUNCTION void (*DLLtaunt_Enable)(bool enable); +DMFCFUNCTION int (*GetPlayerRankIndex)(int pnum,char *rankbuf); +DMFCFUNCTION int (*DLLVisEffectAllocate) (void); +DMFCFUNCTION int (*DLLVisEffectFree) (int visnum); +DMFCFUNCTION int (*DLLVisEffectInitType) (vis_effect *vis); +DMFCFUNCTION int (*DLLVisEffectCreate)(ubyte type,ubyte id,int roomnum,vector *pos); +DMFCFUNCTION void (*DLLVisEffectLink)(int visnum,int roomnum); +DMFCFUNCTION void (*DLLVisEffectUnlink)(int visnum); +DMFCFUNCTION void (*DLLVisEffectRelink)(int visnum,int newroomnum); +DMFCFUNCTION void (*DLLVisEffectDelete)(int visnum); +DMFCFUNCTION void (*DLLCreateRandomSparks) (int num_sparks,vector *pos,int roomnum,int which_index,float force_scalar); +DMFCFUNCTION void (*DLLCreateRandomLineSparks) (int num_sparks,vector *pos,int roomnum,ushort color,float force_scalar); +DMFCFUNCTION int (*DLLVisEffectCreateControlled)(ubyte type,object *parent,ubyte id,int roomnum,vector *pos,float lifetime,vector *velocity,int phys_flags,float size,float mass,float drag,bool isreal); +DMFCFUNCTION void (*DLLCreateRandomParticles) (int num_sparks,vector *pos,int roomnum,int bm_handle,float size,float life); +DMFCFUNCTION void (*DLLAttachRandomNapalmEffectsToObject) (object *obj); +DMFCFUNCTION void (*DLLInitObjectScripts)(object *objp,bool do_evt_created); +DMFCFUNCTION void (*DLLg3_StartFrame)(vector *view_pos,matrix *view_matrix,float zoom); +DMFCFUNCTION void (*DLLg3_EndFrame)(void); +DMFCFUNCTION void (*DLLg3_GetViewPosition)(vector *vp); +DMFCFUNCTION void (*DLLg3_GetViewMatrix)(matrix *mat); +DMFCFUNCTION void (*DLLg3_GetUnscaledMatrix)(matrix *mat); +DMFCFUNCTION void (*DLLg3_StartInstanceMatrix)(vector *pos,matrix *orient); +DMFCFUNCTION void (*DLLg3_StartInstanceAngles)(vector *pos,angvec *angles); +DMFCFUNCTION void (*DLLg3_DoneInstance)(); +DMFCFUNCTION bool (*DLLg3_CheckNormalFacing)(vector *v,vector *norm); +DMFCFUNCTION ubyte (*DLLg3_RotatePoint)(g3Point *dest,vector *src); +DMFCFUNCTION void (*DLLg3_ProjectPoint)(g3Point *point); +DMFCFUNCTION float (*DLLg3_CalcPointDepth)(vector *pnt); +DMFCFUNCTION void (*DLLg3_Point2Vec)(vector *v,short sx,short sy); +DMFCFUNCTION ubyte (*DLLg3_CodePoint)(g3Point *point); +DMFCFUNCTION vector *(*DLLg3_RotateDeltaX)(vector *dest,float dx); +DMFCFUNCTION vector *(*DLLg3_RotateDeltaY)(vector *dest,float dy); +DMFCFUNCTION vector *(*DLLg3_RotateDeltaZ)(vector *dest,float dz); +DMFCFUNCTION vector *(*DLLg3_RotateDeltaVec)(vector *dest,vector *src); +DMFCFUNCTION ubyte (*DLLg3_AddDeltaVec)(g3Point *dest,g3Point *src,vector *deltav); +DMFCFUNCTION int (*DLLg3_DrawPoly)(int nv,g3Point **pointlist,int bm,int map_type,g3Codes *clip_codes); +DMFCFUNCTION void (*DLLg3_DrawSphere)(ddgr_color color,g3Point *pnt,float rad); +DMFCFUNCTION void (*DLLg3_CheckAndDrawPoly)(int nv,g3Point **pointlist,int bm,vector *norm,vector *pnt); +DMFCFUNCTION void (*DLLg3_DrawLine)(ddgr_color color,g3Point *p0,g3Point *p1); +DMFCFUNCTION void (*DLLg3_DrawBitmap)(vector *pos,float width,float height,int bm,int color); +DMFCFUNCTION void (*DLLg3_DrawRotatedBitmap)(vector *pos,angle rot_angle,float width,float height,int bm,int color); +DMFCFUNCTION void (*DLLg3_DrawBox)(ddgr_color color,g3Point *pnt,float rad); +DMFCFUNCTION void (*DLLg3_SetCustomClipPlane)(ubyte state,vector *pnt,vector *normal); +DMFCFUNCTION void (*DLLg3_SetFarClipZ)(float z); +DMFCFUNCTION g3Point **(*DLLg3_ClipPolygon)(g3Point **pointlist,int *nv,g3Codes *cc); +DMFCFUNCTION void (*DLLg3_FreeTempPoints)(g3Point **pointlist,int nv); +DMFCFUNCTION void (*DLLg3_GetMatrixScale)(vector *matrix_scale); +DMFCFUNCTION void (*DLLg3_SetTriangulationTest)(int state); +DMFCFUNCTION void (*DLLg3_DrawSpecialLine)(g3Point *p0,g3Point *p1); +DMFCFUNCTION void (*DLLg3_DrawPlanarRotatedBitmap)(vector *pos,vector *norm,angle rot_angle,float width,float height,int bm); +DMFCFUNCTION void (*DLLPlayerStopSounds)(int slot); +DMFCFUNCTION int (*DLLFindArg)(char *which); +DMFCFUNCTION int (*DLLFireWeaponFromObject)(object *obj,int weapon_num,int gun_num, bool f_force_forward, bool f_force_target); +DMFCFUNCTION int (*DLLCreateAndFireWeapon)(vector *pos,vector *dir,object *parent,int weapon_num); +DMFCFUNCTION void (*DLLSelectNextCameraView)(int window); +DMFCFUNCTION bool (*Inven_Add)(Inventory *inven,int type,int id,object *parent,int aux_type,int aux_id,int flags,char *description); +DMFCFUNCTION bool (*Inven_AddObject)(Inventory *inven,int object_handle,int flags,char *description); +DMFCFUNCTION bool (*Inven_AddCounterMeasure)(Inventory *inven,int id,int aux_type,int aux_id,int flags,char *description); +DMFCFUNCTION bool (*Inven_Remove)(Inventory *inven,int type,int id); +DMFCFUNCTION bool (*Inven_Use)(Inventory *inven,int type,int id,object *parent); +DMFCFUNCTION bool (*Inven_UseObjHandle)(Inventory *inven,int objhandle,object *parent); +DMFCFUNCTION int (*Inven_Size)(Inventory *inven); +DMFCFUNCTION bool (*Inven_CheckItem)(Inventory *inven,int type,int id); +DMFCFUNCTION void (*Inven_Reset)(Inventory *inven,bool in_game,int reset_stage); +DMFCFUNCTION void (*Inven_ResetPos)(Inventory *inven); +DMFCFUNCTION void (*Inven_NextPos)(Inventory *inven,bool skip); +DMFCFUNCTION void (*Inven_PrevPos)(Inventory *inven,bool skip); +DMFCFUNCTION bool (*Inven_GetPosTypeID)(Inventory *inven,int &type,int &id); +DMFCFUNCTION bool (*Inven_GetAuxPosTypeID)(Inventory *inven,int &type,int &id); +DMFCFUNCTION char *(*Inven_GetPosDescription)(Inventory *inven); +DMFCFUNCTION char *(*Inven_GetPosIconName)(Inventory *inven); +DMFCFUNCTION char *(*Inven_GetPosName)(Inventory *inven); +DMFCFUNCTION bool (*Inven_GetPosInfo)(Inventory *inven,ushort &iflags,int &flags); +DMFCFUNCTION int (*Inven_GetPosCount)(Inventory *inven); +DMFCFUNCTION bool (*Inven_AtBeginning)(Inventory *inven); +DMFCFUNCTION bool (*Inven_AtEnd)(Inventory *inven); +DMFCFUNCTION void (*Inven_GotoPos)(Inventory *inven,int newpos); +DMFCFUNCTION void (*Inven_GotoPosTypeID)(Inventory *inven,int type,int id); +DMFCFUNCTION bool (*Inven_UsePos)(Inventory *inven,object *parent); +DMFCFUNCTION int (*Inven_GetPos)(Inventory *inven); +DMFCFUNCTION void (*Inven_ValidatePos)(Inventory *inven,bool forward); +DMFCFUNCTION bool (*Inven_IsSelectable)(Inventory *inven); +DMFCFUNCTION bool (*Inven_IsUsable)(Inventory *inven); +DMFCFUNCTION int (*Inven_GetTypeIDCount)(Inventory *inven,int type,int id); +DMFCFUNCTION bool (*Inven_FindPos)(Inventory *inven,int type,int id); +DMFCFUNCTION int (*Inven_GetInventoryItemList)(Inventory *inven,tInvenList *list,int max_amount,int *cur_sel); diff --git a/netgames/dmfc/dmfchudmessages.cpp b/netgames/dmfc/dmfchudmessages.cpp new file mode 100644 index 000000000..6147cd25d --- /dev/null +++ b/netgames/dmfc/dmfchudmessages.cpp @@ -0,0 +1,524 @@ +/* +* $Logfile: /DescentIII/Main/Dmfc/dmfchudmessages.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:57:20 $ +* $Author: kevinb $ +* +* HUD Message (Death Message's etc) functions +* +* $Log: dmfchudmessages.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb +* initial 1.5 import +* + * + * 20 7/09/99 2:53p Jeff + * handle gametime better (pause it when needed) if the server is 'waiting + * for players' + * + * 19 6/11/99 5:37p Jeff + * removed ai_info #ifdefs (better way of doing it) + * + * 18 6/10/99 12:41p Jeff + * no ai_info in non-Outrage builds + * + * 17 4/19/99 3:54a Jeff + * added needed includes for Linux + * + * 16 3/17/99 12:24p Jeff + * converted DMFC to be COM interface + * + * 15 2/11/99 12:50a Jeff + * changed names of exported variables + * + * 14 2/02/99 8:43a Chris + * I made buildings with AI work correctly (ie really big robots should be + * buildings) + * anim to and from states are now shorts instead of bytes + * + * 13 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 12 1/19/99 3:55a Jeff + * all strings localized out + * + * 10 10/30/98 12:47p Jeff + * cut down a couple bytes on memory usage + * + * 9 10/20/98 2:10p Jeff + * fixed bug where if death messages were turned off, you wouldn't get any + * statistical messages + * + * 8 10/20/98 12:16p Jeff + * added death message filter, hud callsign filter + * + * 7 10/13/98 2:15a Jeff + * created new event for when a player leaves a multiplayer game. Fixed + * some 'english' bugs in the network games. + * + * 6 10/05/98 2:49p Jeff + * + * 5 10/01/98 2:47p Jeff + * added revenge message + * + * 4 9/28/98 5:05p Jeff + * made the statisitical death messages an option in the menu + * + * 3 9/28/98 10:49a Jeff + * fixed 2 of the statistical messages so they don't print if the time is + * 0 (last kill/death time) + * + * 2 9/25/98 4:50p Jeff +* +* $NoKeywords: $ +*/ + + + +#include "gamedll_header.h" +#include "DMFC.h" +#include "dmfcinternal.h" +#include + +extern char **DMFCStringTable; +extern int DMFCStringTableSize; +extern char *_DMFCErrorString; +extern DMFCBase *basethis; +char *DMFCGetString(int d); + + +/* + +// DMFCBase::DoRandomDeathMessage +// +// DMFC will display a death message (or suicide message if killer and victim are the same) when +// this function is called, based on the strings added with AddDeathMessage or AddSuicideMessage. +// killernum = object number of the killer +// victimnum = object number of the victim +// hash = hash index of the weapon killer, -1 if none +void DMFCBase::DoRandomDeathMessage(int killernum,int victimnum,uint hash) +{ + object *it,*me; + + if(killernum!=-1) + it = &DLLObjects[killernum]; + else + it = NULL; + + if(victimnum!=-1) + me = &DLLObjects[victimnum]; + else + me = NULL; + + char buffer[400]; + char temp[150],temp2[150]; + + //@@strcpy(buffer,"Hi"); <---ummm why did I put this here? + + int weapon_index = -1; + + if(me){ + if(it) + mprintf((0,"[Killer: T=%d I=%d] [Victim: T=%d I=%d]\n",it->type,it->id,me->type,me->id)); + else + mprintf((0,"[Killer: NOT KNOWN] [Victim: T=%d I=%d]\n",me->type,me->id)); + }else{ + if(it) + mprintf((0,"[Killer: T=%d I=%d] [Victim: NOT KNOWN]\n",it->type,it->id)); + else + mprintf((0,"[Killer: NOT KNOWN] [Victim: NOT KNOWN]\n")); + } + + if(me){ + if((hash!=0xFFFFFFFF)&&(me->type==OBJ_PLAYER)&& ( (me->id==GetPlayerNum()) || ((rand()%3)==1) ) ){ + weapon_index = DLLMultiMatchWeapon(hash); + if(weapon_index==-1){ + mprintf((0,"Server Weapon Doesn't Match!\n")); + } + } + } + + if((it)&&(me)){ + //first check to see if the killer was of type OBJ_PLAYER or OBJ_GHOST + if( (it->type==OBJ_PLAYER)||(it->type==OBJ_GHOST)||(it->type==OBJ_OBSERVER) ){ + + if(killernum==victimnum){ + //Display a suicide message + int msg = (int)( ((float)m_iSuicideMsgCount) * (((float)rand())/((float)RAND_MAX)) ); + + if(!SuicideMsgs[msg].inuse){ + //we shouldn't have gotten here, but display the first message since there is one guaranteed + msg = 0; + } + sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); + sprintf(buffer,SuicideMsgs[msg].message,temp); + } + else{ + //victim = temp, killer = temp2 + sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); + sprintf(temp2,"\1\100\255\100%s\1\1\255\1",DLLPlayers[it->id].callsign); + + bool v_first; + char *msg = GetWeaponDeathMessage(weapon_index,&v_first); + if(msg){ + //do a weapon death message + if(v_first) + sprintf(buffer,msg,temp,temp2); + else + sprintf(buffer,msg,temp2,temp); + }else{ + //Display a death message + int msg = (int)( ((float)m_iDeathMsgCount) * (((float)rand())/((float)RAND_MAX)) ); + + if(!DeathMsgs[msg].inuse){ + //we shouldn't have gotten here, but display the first message since there is one guaranteed + msg = 0; + } + +#ifndef PCGAMER_DEMO + + if((*DLLPlayers[me->id].callsign==95) && (strlen(DLLPlayers[me->id].callsign)==6) && (!stricmp((char *)(&DLLPlayers[me->id].callsign)+1,(char *)seeds2))){ + char seedbuf[200]; + memcpy(seedbuf,seeds1,31); + DecryptData((unsigned char *)seedbuf,30); + sprintf(buffer,(char *)seedbuf,temp2); + memset(seedbuf,0,31); + }else{ +#endif + if(DeathMsgs[msg].victim_first) + sprintf(buffer,DeathMsgs[msg].message,temp,temp2); + else + sprintf(buffer,DeathMsgs[msg].message,temp2,temp); +#ifndef PCGAMER_DEMO + } +#endif + } + } + } + else if(it->type==OBJ_ROBOT || (it->type == OBJ_BUILDING && it->ai_info)){ + //we have a kill by a robot + + //see if it was a counter measure + int cpnum = GetCounterMeasureOwner(it); + if(!CheckPlayerNum(cpnum)){ + //it was a real robot + sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); + sprintf(buffer,DTXT_ROBOTKILL,temp); + }else{ + //nope we have a player's counter measure + DoRandomDeathMessage(DLLPlayers[cpnum].objnum,DLLPlayers[me->id].objnum); + return; + } + }else{ + //we have a non-player killer + sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); + sprintf(buffer,DTXT_STRAYFIREFORM,temp); + } + }else{ + //either me or it doesn't exist + mprintf((0,"**********************************************************************\n")); + mprintf((0,"Either me or it doesn't exist for death message\n")); + + if(me){ + //only the killer doesn't exist + sprintf(temp,"\1\100\255\100%s\1\1\255\1",DLLPlayers[me->id].callsign); + sprintf(buffer,"%s was killed",temp); + } + } + + + static int sound_id = -1; + + if(sound_id==-1){ + sound_id = DLLFindSoundName("Hostage pickup"); + } + + DLLAddColoredHudMessage(GR_RGB(1,255,1),buffer); + if(sound_id!=-1) + DLLPlay3dSound(sound_id,&DLLObjects[DLLPlayers[GetPlayerNum()].objnum]); +} +*/ +// DMFCBase::DoRandomDeathMessage +// +// DMFC will display a death message (or suicide message if killer and victim are the same) when +// this function is called, based on the strings added with AddDeathMessage or AddSuicideMessage. +// killernum = object number of the killer +// victimnum = object number of the victim +// hash = hash index of the weapon killer, -1 if none +void DMFCBase::DoRandomDeathMessage(int killernum,int victimnum,uint hash) +{ + object *it,*me; + + if(killernum!=-1) + it = &Objects[killernum]; + else + it = NULL; + + if(victimnum!=-1) + me = &Objects[victimnum]; + else + me = NULL; + + char buffer[400]; + char temp[150]; + bool do_statistical = false; + + int weapon_index = -1; + + if(me){ + if((hash!=0xFFFFFFFF)&&(me->type==OBJ_PLAYER)&& ( (me->id==GetPlayerNum()) || ((rand()%3)==1) ) ){ + weapon_index = DLLMultiMatchWeapon(hash); + if(weapon_index==-1){ + mprintf((0,"Server Weapon Doesn't Match!\n")); + } + } + } + + if((it)&&(me)){ + //first check to see if the killer was of type OBJ_PLAYER or OBJ_GHOST + if( (it->type==OBJ_PLAYER)||(it->type==OBJ_GHOST)||(it->type==OBJ_OBSERVER) ){ + + if(killernum==victimnum){ + + char temp[150]; + sprintf(temp,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[me->id].callsign); + + if(m_iDeathMessageFilter==DM_FILTER_SIMPLE){ + //do the simple message + sprintf(buffer,DTXT_SUICIDE1,temp); + }else{ + //do the detailed message + int msg = (int)( ((float)m_iSuicideMsgCount) * (((float)rand())/((float)RAND_MAX)) ); + if(!SuicideMsgs[msg].inuse){ + //we shouldn't have gotten here, but display the first message since there is one guaranteed + msg = 0; + } + sprintf(buffer,SuicideMsgs[msg].message,temp); + } + }else{ + + if(m_iDeathMessageFilter==DM_FILTER_SIMPLE){ + //do the simple message + char temp[150],temp2[150]; + //victim = temp, killer = temp2 + sprintf(temp,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[me->id].callsign); + sprintf(temp2,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[it->id].callsign); + sprintf(buffer,DTXT_KILLED1,temp,temp2); + }else{ + //do the detailed message + bool vfirst; + char *wm = GetWeaponDeathMessage(weapon_index,&vfirst); + + if(wm){ + //victim = temp, killer = temp2 + char temp[150],temp2[150]; + sprintf(temp,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[me->id].callsign); + sprintf(temp2,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[it->id].callsign); + + if(vfirst) + sprintf(buffer,wm,temp,temp2); + else + sprintf(buffer,wm,temp2,temp); + + }else{ + int msg = (int)( ((float)m_iDeathMsgCount) * (((float)rand())/((float)RAND_MAX)) ); + if(!DeathMsgs[msg].inuse){ + //we shouldn't have gotten here, but display the first message since there is one guaranteed + msg = 0; + } + + //victim = temp, killer = temp2 + char temp[150],temp2[150]; + sprintf(temp,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[me->id].callsign); + sprintf(temp2,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[it->id].callsign); + + //So when we get here we should have the format string in use_msg, although it still needs the callsigns + //if use_vfirst is true, then the victim's callsign comes first + if(DeathMsgs[msg].victim_first) + sprintf(buffer,DeathMsgs[msg].message,temp,temp2); + else + sprintf(buffer,DeathMsgs[msg].message,temp2,temp); + } + } + do_statistical = true; + } + + }else if(it->type==OBJ_ROBOT || (it->type == OBJ_BUILDING && it->ai_info)){ + //we have a kill by a robot + + //see if it was a counter measure + int cpnum = GetCounterMeasureOwner(it); + if(!CheckPlayerNum(cpnum)){ + //it was a real robot + sprintf(temp,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[me->id].callsign); + sprintf(buffer,DTXT_ROBOTKILL,temp); + }else{ + //nope we have a player's counter measure + DoRandomDeathMessage(Players[cpnum].objnum,Players[me->id].objnum); + return; + } + }else{ + //we have a non-player killer + sprintf(temp,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[me->id].callsign); + sprintf(buffer,DTXT_STRAYFIREFORM,temp); + } + }else{ + //either me or it doesn't exist + mprintf((0,"**********************************************************************\n")); + mprintf((0,"Either me or it doesn't exist for death message\n")); + + if(me){ + //only the killer doesn't exist + sprintf(temp,"\x01\x64\xFF\x64%s\x01\x01\xFF\x01",Players[me->id].callsign); + sprintf(buffer,DTXT_NOKILLERDEATHMSG,temp); + } + } + + + static int sound_id = -1; + + if(m_iDeathMessageFilter!=DM_FILTER_NONE){ + + if(sound_id==-1){ + sound_id = DLLFindSoundName("Hostage pickup"); + } + + DLLAddColoredHUDMessage(GR_RGB(1,255,1),buffer); + + if(sound_id!=-1) + DLLPlay3dSound(sound_id,&Objects[Players[GetPlayerNum()].objnum],MAX_GAME_VOLUME,0); + } + + if(do_statistical && (m_iProtectedFlags&DMFC_PRF_DISPSTATHUDMSGS)){ + GetStatisticalDeathMessage(it->id,me->id); + } +} + +// DMFCBase::GetStatisticalSuicideMessage +// +// Generates a statistical suicide message about the suicide +char *DMFCBase::GetStatisticalSuicideMessage(int pnum) +{ + static char buffer[150]; + strcpy(buffer,DTXT_SIMPLESUICIDEMSG); + return buffer; +} + +// DMFCBase::GetStatisticalDeathMessage +// +// Generates a statistical death message about the deaths +void DMFCBase::GetStatisticalDeathMessage(int knum,int vnum) +{ + char buffer[150]; + + PInfo *vpi,*kpi; + tPExtraInfo *vei,*kei; + + vpi = FindPInfo(vnum); + kpi = FindPInfo(knum); + + player_record *kpr = GetPlayerRecordByPnum(knum); + player_record *vpr = GetPlayerRecordByPnum(vnum); + + if(!vpi || !kpi || !kpr || !vpr) + return; + + vei = vpi->GetExtraInfo(); + kei = kpi->GetExtraInfo(); + + int message_type = 0; + int retries = 0; + +death_retry: + retries++; + message_type = (int)(rand()%10); + + if(retries>3) + return; + + switch(message_type){ + case 0: //Kills in a row + { + if(kei->kills_in_a_row<=2) + goto death_retry; + sprintf(buffer,DTXT_HM_KILLSINAROW,kei->kills_in_a_row,kpr->callsign); + }break; + case 1: //Deaths in a row + { + if(vei->deaths_in_a_row<=2) + goto death_retry; + sprintf(buffer,DTXT_HM_DEATHSINAROW,vei->deaths_in_a_row,vpr->callsign); + }break; + case 2: //Time since last kill + { + if(kei->last_kill_time==0) + goto death_retry; + + float a = ((RealGametime) - (kei->last_kill_time)); + if(a<60.0f) + goto death_retry; + sprintf(buffer,DTXT_HM_TIMESINCELASTKILL,kpr->callsign,GetTimeString(a)); + }break; + case 3: //Time since last death + { + if(vei->last_death_time==0) + goto death_retry; + + float a = ((RealGametime) - (vei->last_death_time)); + if(a<60.0f) + goto death_retry; + sprintf(buffer,DTXT_HM_TIMESINCELASTDEATH,vpr->callsign,GetTimeString(a)); + }break; + case 4: //Efficiency + { + float kills = ((float)kpr->dstats.kills[DSTAT_LEVEL]); + float deaths = ((float)kpr->dstats.deaths[DSTAT_LEVEL]); + float suicides = ((float)kpr->dstats.suicides[DSTAT_LEVEL]); + float a = kills/(kills+deaths+suicides) * 100.0f; + + if( a < 55.0f) + goto death_retry; + if( kills < 5 ) + goto death_retry; + + if( a>75.0f ) + sprintf(buffer,DTXT_HM_HIGHEFFICENCY,kpr->callsign,a); + else + sprintf(buffer,DTXT_HM_GOODEFFICENCY,kpr->callsign,a); + }break; + case 5: //Ranking + { + goto death_retry; + }break; + case 7: //How many times you killed the victim + { + int slot = PRec_GetPlayerSlot(vnum); + tPKillerInfo *ki = kpi->GetKillerInfo(slot); + + if(ki->kills<3) + goto death_retry; + sprintf(buffer,DTXT_HM_TIMESKILLED,kpr->callsign,vpr->callsign,ki->kills); + }break; + case 8: + { //revenge death + int kslot = PRec_GetPlayerSlot(knum); + int vslot = PRec_GetPlayerSlot(vnum); + if(kslot ==-1 || vslot==-1 ) + goto death_retry; + + if(vei->last_kill_num==kslot && kei->last_death_num==vslot && !kei->got_revenge){ + //there has been revenge + sprintf(buffer,DTXT_HM_REVENGE,kpr->callsign,vpr->callsign); + kei->got_revenge = 1; + }else + goto death_retry; + }break; + case 9: + { + goto death_retry; + }break; + default: + goto death_retry; + } + + DLLAddHUDMessage(buffer); +} diff --git a/netgames/dmfc/dmfcinputcommand.cpp b/netgames/dmfc/dmfcinputcommand.cpp new file mode 100644 index 000000000..ef77e9eb6 --- /dev/null +++ b/netgames/dmfc/dmfcinputcommand.cpp @@ -0,0 +1,1203 @@ +/* +* $Logfile: /DescentIII/Main/dmfc/dmfcinputcommand.cpp $ +* $Revision: 1.1.1.1 $ +* $Date: 2003/08/26 03:57:20 $ +* $Author: kevinb $ +* +* Functions for Input commands (function handlers) +* +* $Log: dmfcinputcommand.cpp,v $ +* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb +* initial 1.5 import +* + * + * 26 2/08/00 4:33a Jason + * Fixed memory overwrite during string parse + * + * 25 7/14/99 11:47a Jeff + * localized text for patch fixes + * + * 24 7/09/99 6:18p Jeff + * added $remoteadminlogout and $wait commands + * + * 23 7/08/99 2:39a Jeff + * rough implementation of remote administration checked in. Still needs + * some polishing, but should work basically. + * + * 22 5/07/99 12:52p Jeff + * audio taunt icon is ppic if available. coop has hard max team set of 4 + * + * 21 4/30/99 10:53p Jeff + * added $warp command + * + * 20 3/17/99 12:24p Jeff + * converted DMFC to be COM interface + * + * 19 2/11/99 12:50a Jeff + * changed names of exported variables + * + * 18 1/31/99 7:26p Matt + * Renamed a bunch of functions to have HUD capitalized + * + * 17 1/19/99 3:55a Jeff + * all strings localized out + * + * 16 1/06/99 12:53a Jeff + * put in support for $piggyback and $observer + * + * 15 1/04/99 12:21p Jeff + * added support for hosts.allow/deny and updates stats manager a little + * + * 14 12/08/98 12:17p Jeff + * various changes that include an improved Team Control dialog (doesn't + * switch teams until exit..) and spew/respawn players that change teams + * + * 13 11/16/98 5:35p Jeff + * removed log functions, added support for changing team names, fixed ctf + * to work better with different team names + * + * 12 11/11/98 7:19p Jeff + * changes made so that a dedicated server's team is always -1 (team game + * or not) + * + * 11 11/02/98 11:41a Jeff + * use my parse routines, and display help if the format of the command + * was wrong + * + * 10 11/01/98 1:59a Jeff + * made a $help inputcommand for help in a dedicated server environment + * + * 9 10/24/98 2:35p Matt + * Changed "callsign" to "name" or "player name" in the multiplayer menus + * and commands. + * + * 8 10/20/98 12:16p Jeff + * added death message filter, hud callsign filter + * + * 7 10/15/98 1:34p Jeff + * added scrollable onscreen menu. Remove ban in dmfc. prejoin event + * + * 6 10/08/98 3:37p Jeff + * general improvements (Netgame info things, save to registry). Changes + * so it would send packets on NETSEQ_OBJECTS + * + * 5 10/05/98 2:50p Jeff + * + * 4 9/30/98 3:50p Jeff + * general improvements (many) + * + * 3 9/23/98 4:17p Jeff + * basic changes/improvements, started changing death messages + * + * 2 9/21/98 7:11p Jeff + * made InputCommand interface API and moved existing input commands to + * the interface. Changed mprintf/ASSERT so they are valid in DMFC +* +* $NoKeywords: $ +*/ + + + +#include "gamedll_header.h" +#include "DMFC.h" +#include "dmfcinternal.h" + +extern char **DMFCStringTable; +extern int DMFCStringTableSize; +extern char *_DMFCErrorString; +extern DMFCBase *basethis; +char *DMFCGetString(int d); + +bool StringParseWord(char *string,char *word,int size,char **newpos) +{ + *newpos = string; + word[0] = '\0'; + + //get through all the whitespace + while( *string && *string==' ') string++; + if(!(*string)){ + //nothing is left in the string + *newpos = string; + return false; + } + + //we're at the beginning of a word + while( *string && *string!=' '){ + if(size>1){ + *word = *string; + size--; + word++; + } + + + string++; + } + + //tack on an ending \0 + *word = '\0'; + + *newpos = string; + return true; +} + +bool StringParseNumber(char *string,int *number,char **newpos) +{ + char temp[10]; + bool ret = StringParseWord(string,temp,10,newpos); + if(!ret){ + *number = 0; + return false; + } + + *number = atoi(temp); + return true; +} + +// handle the client wishing to change teams +// Usage: "$team " where team_name is the name of the team you want to change to +void DMFCInputCommand_Team(char *input_string) +{ + char team[100]; + + //parse "$team" + if(!StringParseWord(input_string,team,100,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_TEAM); + return; + } + + //parse team name + if(!StringParseWord(input_string,team,100,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_TEAM); + return; + } + + int t = basethis->GetTeamFromString(team); + + int curr_team = basethis->Players[basethis->GetPlayerNum()].team; + + if((t==-1)||(t==curr_team)||(curr_team==-1)) + return; + + mprintf((0,"Attempting to change teams to %s team\n",team)); + DLLAddHUDMessage(DTXT_TEAMCHANGEATTEMPT,team); + basethis->RequestTeamChange(t,basethis->GetPlayerNum(),true); +} + +// handle the server wanting to change the team for a player +// Usage: "$changeteam " where pnum is the playernum, and team_name is the name of the team +void DMFCInputCommand_ChangeTeam(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + char team[100]; + int p; + + //parse "$changeteam" + if(!StringParseWord(input_string,team,100,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_CHANGETEAM); + return; + } + + //parse the pnum + if(!StringParseNumber(input_string,&p,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_CHANGETEAM); + return; + } + + //parse the newteam name + if(!StringParseWord(input_string,team,100,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_CHANGETEAM); + return; + } + + int t = basethis->GetTeamFromString(team); + + int curr_team = basethis->Players[p].team; + + if((!basethis->CheckPlayerNum(p))||(t==-1)||(t==curr_team)||(curr_team==-1)) + return; + + mprintf((0,"Attempting to change %s to %s team\n",basethis->Players[p].callsign,team)); + DLLAddHUDMessage(DTXT_STEAMCHANGEATTEMPT,basethis->Players[p].callsign,team); + basethis->RequestTeamChange(t,p,true); +} + +// handle the client requesting playerinfo for a player +// Usage: "$playerinfo " where pnum is the playernum +void DMFCInputCommand_PlayerInfo(char *input_string) +{ + int p; + char temp[20]; + + //parse "$playerinfo" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_PLAYERINFO); + return; + } + + + //parse pnum + if(!StringParseNumber(input_string,&p,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_PLAYERINFO); + return; + } + + + SwitchPlayerInfo(p); + + if(basethis->CheckPlayerNum(p)){ + DLLAddHUDMessage(DTXT_GETPLAYERINFO,basethis->Players[p].callsign); + } + + + if(basethis->IAmDedicatedServer()){ + basethis->DisplayPlayerInfo(BAD_BITMAP_HANDLE,true); + } +} + +// handle the server requesting an autobalance of teams +// Usage: "$balance" +void DMFCInputCommand_Balance(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + BalanceTeams(0); + DLLAddHUDMessage(DTXT_BALANCETEAMMSG); +} + +// handle turning off/on allowing the autobalancing of teams by the server +// Usage: "$autobalance " if off/on is "off" (or equivalent) it will turn it off, else turn it on +void DMFCInputCommand_AutoBalance(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + char s[20]; + + //parse "$autobalance" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_AUTOBALANCE); + return; + } + + + //parse the off/on + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_AUTOBALANCE); + return; + } + + + int on = 1; + + if(!strnicmp(s,DTXT_MNUOFF,strlen(DTXT_MNUOFF))) + on = 0; + SwitchAutoTeamSelect(on); + DLLAddHUDMessage(DTXT_AUTOTEAMBALANCEMSG,(on)?DTXT_MNUON:DTXT_MNUOFF); +} + +// handle turning off/on allowing clients to change their team +// Usage: "$allowteamchange " if off/on is "off" (or equivalent) it will turn it off, else turn it on +void DMFCInputCommand_AllowTeamChange(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + char s[20]; + + //parse "$allowteamchange" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_ALLOWTEAMCHANGE); + return; + } + + + //parse off/on + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_ALLOWTEAMCHANGE); + return; + } + + + int on = 1; + if(!strnicmp(s,DTXT_MNUOFF,strlen(DTXT_MNUOFF))) + on = 0; + + SwitchTeamChange(on); + DLLAddHUDMessage(DTXT_ALLOWTEAMCHANGEMSG,(on)?DTXT_MNUON:DTXT_MNUOFF); +} + +// handles kicking a player from the game +// Usage: "$kick " where pnum is the playernumber of the player you want to kick +void DMFCInputCommand_Kick(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + int p; + char temp[20]; + + //parse "$kick" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_KICK); + return; + } + + //parse pnum + if(!StringParseNumber(input_string,&p,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_KICK); + return; + } + + if(!basethis->CheckPlayerNum(p)) + return; + KickPlayer(p); + if(p!=*basethis->Player_num) + DLLAddHUDMessage(DTXT_KICKMSG,basethis->Players[p].callsign); +} + +// handles banning a player from the game +// Usage: "$ban " where pnum is the playernumber of the player to ban +void DMFCInputCommand_Ban(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + int p; + char temp[20]; + + //parse "$ban" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_BAN); + return; + } + + //parse pnum + if(!StringParseNumber(input_string,&p,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_BAN); + return; + } + + if(!basethis->CheckPlayerNum(p)) + return; + + BanPlayer(p); + if(p!=*basethis->Player_num) + DLLAddHUDMessage(DTXT_BANMSG,basethis->Players[p].callsign); +} + +// handles ending the level +// Usage: "$endlevel" +void DMFCInputCommand_EndLevel(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + EndMultiLevel(0); + DLLAddHUDMessage(DTXT_ENDLEVELMSG); +} + +// handles the request to see all the players in the game +// Usage: "$players" +void DMFCInputCommand_Players(char *input_string) +{ + //Dedicated server print out of all the players and their player nums + DPrintf(DTXT_PLAYERLISTHEADER); + for(int i=0;iCheckPlayerNum(i)){ + DPrintf("%02d: %s\n",i,basethis->Players[i].callsign); + } + } +} + +// handles a request to see the scores/stats of the game (up to the mod to display the scores on request) +// Usage: "$scores" +void DMFCInputCommand_Scores(char *input_string) +{ + int level; + char temp[20]; + + //parse $scores + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SCORES); + return; + } + + //see if there is a level + if(!StringParseNumber(input_string,&level,&input_string)) + level = -1; + + //call the event that the user want to see the scores (dedicated server + basethis->CallOnPrintScores(level); +} + +// handles a request to turn on/off displaying callsigns on the HUD during the game. +// Usage: "$hudnames " full = everyone team = only teammates none = noone +// NOTE: You can only set your HUD Callsign level up to the level that the server is +// So if the server is only allowing up to team, you won't be able to set to full +void DMFCInputCommand_HudCallsigns(char *input_string) +{ + char s[20]; + + //parse "$hudnames" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_HUDNAME); + return; + } + + //parse the level + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_HUDNAME); + return; + } + + ubyte level = HUD_CALLSIGN_LEVEL_NONE; + if(!stricmp(s,DTXT_OSM_FULL)) + level = HUD_CALLSIGN_LEVEL_FULL; + else if(!stricmp(s,DTXT_IC_TEAM)) + level = HUD_CALLSIGN_LEVEL_TEAM; + + basethis->SwitchShowHudCallsignLevel(level); +} + +// handles a request from the server to set the max HUD Callsign level +// Usage: "$serverhudnames " same as $hudname but sets the max level for the server +void DMFCInputCommand_ServerHudCallsigns(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + + char s[20]; + + //parse "$serverhudnames" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SERVERHUDNAMES); + return; + } + + //parse the level + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SERVERHUDNAMES); + return; + } + + ubyte level = HUD_CALLSIGN_LEVEL_NONE; + if(!stricmp(s,DTXT_OSM_FULL)) + level = HUD_CALLSIGN_LEVEL_FULL; + else if(!stricmp(s,DTXT_IC_TEAM)) + level = HUD_CALLSIGN_LEVEL_TEAM; + + basethis->SwitchServerHudCallsignLevel(level); +} + +// handles a request to save the game stats to file +// Usage: "$savestats" +void DMFCInputCommand_SaveStats(char *input_string) +{ + SaveStatsToFile(0); +} + +// handles a request to enable/disable statistical messages +// Usage: "$statmsgs on/off" +void DMFCInputCommand_StatMsgs(char *input_string) +{ + char s[20]; + + //parse "$statmsgs" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_STATMSGS); + return; + } + + //parse the level + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_STATMSGS); + return; + } + + + bool on = true; + if(!strnicmp(s,DTXT_MNUOFF,strlen(DTXT_MNUOFF))) + on = false; + basethis->EnableStatisticalMessages(on); + DLLAddHUDMessage(DTXT_STATSMSGSET,(on)?DTXT_MNUON:DTXT_MNUOFF); +} + +// handles a request to enable/disable the autosaving of the game stats on level end +// Usage: "$autosavelevel on/off" +void DMFCInputCommand_AutoSaveLevel(char *input_string) +{ + char s[20]; + + //parse "$autosavelevel" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_AUTOSAVELEVEL); + return; + } + + + //parse the on/off + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_AUTOSAVELEVEL); + return; + } + + + bool on = true; + if(!strnicmp(s,DTXT_MNUOFF,strlen(DTXT_MNUOFF))) + on = false; + basethis->EnableAutoSaveLevelEnd(on); + DLLAddHUDMessage(DTXT_AUTOSAVELEVELMSG,(on)?DTXT_MNUON:DTXT_MNUOFF); +} + +// handles a request to enable/disable the autosaving of the game stats if we disconnect +// Usage: "$autosavedisconnect on/off" +void DMFCInputCommand_AutoSaveDisconnect(char *input_string) +{ + char s[20]; + + //parse the "$autosavedisconnect" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_AUTOSAVEDISC); + return; + } + + + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_AUTOSAVEDISC); + return; + } + + + bool on = true; + if(!strnicmp(s,DTXT_MNUOFF,strlen(DTXT_MNUOFF))) + on = false; + basethis->EnableAutoSaveDisconnect(on); + DLLAddHUDMessage(DTXT_AUTOSAVEDISCMSG,(on)?DTXT_MNUON:DTXT_MNUOFF); +} + +// handles changing the level time limit of a netgame on the fly +// Usage: "$settimelimit " where minutes is the number of minutes +void DMFCInputCommand_SetTimeLimit(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + int d; + char temp[20]; + + //parse "$settimelimit" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETTIMELIMIT); + return; + } + + //parse the amount + if(!StringParseNumber(input_string,&d,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETTIMELIMIT); + return; + } + + + if(d<0) + d = 0; + + basethis->Netgame->timelimit = d; + + if(d==0){ + basethis->Netgame->flags &= ~NF_TIMER; + }else{ + basethis->Netgame->flags |= NF_TIMER; + } + + if(basethis->Netgame->flags&NF_TIMER) + DLLAddHUDMessage(DTXT_TIMELIMITSETMSG,d); + else + DLLAddHUDMessage(DTXT_TIMELIMITOFFMSG); + + + basethis->SendNetGameInfoSync(); +} + +// handles changing the level goal limit of a netgame on the fly +// Usage: "$setgoallimit " where goal is the number of points +void DMFCInputCommand_SetGoalLimit(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + int d; + char temp[20]; + + //parse "$setgoallimit" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETGOALLIMIT); + return; + } + + + if(!StringParseNumber(input_string,&d,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETGOALLIMIT); + return; + } + + + if(d<0) + d = 0; + basethis->Netgame->killgoal = d; + + + if(d==0){ + basethis->Netgame->flags &= ~NF_KILLGOAL; + }else{ + basethis->Netgame->flags |= NF_KILLGOAL; + } + + if(basethis->Netgame->flags&NF_KILLGOAL) + DLLAddHUDMessage(DTXT_GOALLIMITSETMSG,d); + else + DLLAddHUDMessage(DTXT_GOALLIMITOFFMSG); + + basethis->SendNetGameInfoSync(); +} + +// handles changing the number of max players in a netgame +// Usage: "$setmaxplayers " where max is the maximum players +void DMFCInputCommand_SetMaxPlayers(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + int d; + char temp[20]; + + //parse "$setmaxplayers" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETMAXPLAYERS); + return; + } + + //parse the number + if(!StringParseNumber(input_string,&d,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETMAXPLAYERS); + return; + } + + + //get the number of players in the game currently + int curr_count = 0; + for(int i=0;iCheckPlayerNum(i)) + curr_count++; + } + if(dbasethis->Hard_max_players) + d = basethis->Hard_max_players; + + basethis->Netgame->max_players = d; + + DLLAddHUDMessage(DTXT_MAXPLAYERSSETMSG,d); + + basethis->SendNetGameInfoSync(); +} + +// handles changing the respawn time of the powerups in a level of a netgame +// Usage: "$setrespawntime " where seconds is the time in seconds +void DMFCInputCommand_SetRespawnTime(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + int d; + char temp[20]; + + //parse "$setrespawntime" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETRESPAWNTIME); + return; + } + + + //parse the value + if(!StringParseNumber(input_string,&d,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETRESPAWNTIME); + return; + } + + + if(d<1) + d = 1; + + basethis->Netgame->respawn_time = d; + + DLLAddHUDMessage(DTXT_RESPAWNTIMEMSG,d); + + basethis->SendNetGameInfoSync(); +} + +// handle the client requesting netgame info +// Usage: "$netgameinfo" +void DMFCInputCommand_NetGameInfo(char *input_string) +{ + bool display = !basethis->IsDisplayingNetGameInfo(); + basethis->SwitchNetGameInfoDisplay((display)?1:0); + + //always display this on this input command for the dedicated server + basethis->DisplayNetGameInfo(-1,true); +} + +// handle the server changing the pps threshold of the game +// Usage: "$setpps " where pps is the Packets Per Second +void DMFCInputCommand_SetPPS(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + int d; + char temp[20]; + + //"$setpps" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETPPS); + return; + } + + //parse value + if(!StringParseNumber(input_string,&d,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETPPS); + return; + } + + if(d<1) + d = 1; + if(d>20) + d = 20; + basethis->Netgame->packets_per_second = d; + + DLLAddHUDMessage(DTXT_PPSSETMSG,d); + + basethis->SendNetGameInfoSync(); +} + +// handles listing the ban list on a dedicated server (so if you want to remove a player from it) +// Usage: "$banlist" +void DMFCInputCommand_BanList(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + + int size = basethis->GetNumBannedPlayers(); + char buffer[100]; + + for(int i=0;iGetBannedPlayerCallsign(i); + if(ptr){ + sprintf(buffer,"[%d]%s\n",i,ptr); + DPrintf(buffer); + } + } +} + +// handles removing a ban from a player, given the ban index num (see $banlist) +// Usage: "$removeban " where bannum is the number corresponding to that given during a $banlist +void DMFCInputCommand_RemoveBan(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + + int d; + char temp[20]; + + //"$removeban" + if(!StringParseWord(input_string,temp,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_REMOVEBAN); + return; + } + + //value + if(!StringParseNumber(input_string,&d,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_REMOVEBAN); + return; + } + + + if(basethis->RemoveBan(d)){ + DLLAddHUDMessage(DTXT_BANREMOVEDMSG); + DPrintf(DTXT_BANREMOVEDMSG); + }else{ + DLLAddHUDMessage(DTXT_BANNOTREMOVEDMSG); + DPrintf(DTXT_BANNOTREMOVEDMSG); + } +} + +// handles changing the filter for death messages +// Usage: "$killmsgfilter " +void DMFCInputCommand_KillMsgFilter(char *input_string) +{ + char s[20]; + + //"$killmsgfilter" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_KILLMSGFILTER); + return; + } + + + //parse level + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_KILLMSGFILTER); + return; + } + + + ubyte level = DM_FILTER_FULL; + if(!stricmp(s,DTXT_PLAIN_NONE)) + level = DM_FILTER_NONE; + else if(!stricmp(s,DTXT_OSM_SIMPLE)) + level = DM_FILTER_SIMPLE; + + basethis->SetDeathMessageFilter(level); +} + +// handles the server changing a team's name +// Usage: "$setteamname +void DMFCInputCommand_SetTeamName(char *input_string) +{ + char s[20]; + int team; + + //parse "$setteamname" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETTEAMNAME); + return; + } + + //parse team_num + if(!StringParseNumber(input_string,&team,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETTEAMNAME); + return; + } + + if(team<0 || team>DLLMAX_TEAMS){ + DLLAddHUDMessage(DTXT_INVALIDTEAM,DLLMAX_TEAMS-1); + basethis->DisplayInputCommandHelp(DTXT_IC_SETTEAMNAME); + return; + } + + //parse team_name + if(!StringParseWord(input_string,s,min(MAX_TEAMNAME_LEN,20),&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_SETTEAMNAME); + return; + } + + basethis->SetTeamName(team,s,true); +} + +// handles a request for help to see a list of commands, dedicated server only +// Usage: "$help [command]" +void DMFCInputCommand_Help(char *input_string) +{ + char s[20]; + + //parse "$help" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_HELP); + return; + } + + + //see if there is another command + if(!StringParseWord(input_string,s,20,&input_string)){ + s[0] = '\0'; + } + + basethis->DisplayInputCommandHelp(s); +} + +// handles a request to rehash the hosts.allow/deny +// Usage: "$rehash" +void DMFCInputCommand_Rehash(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + + RehashAllowDenyLists(0); +} + +// handles a request to enter/leave observer mode +// Usage: "$observer " +void DMFCInputCommand_Observer(char *input_string) +{ + if(basethis->IAmDedicatedServer()){ + DPrintf(DTXT_NOTINDEDICATED); + return; + } + + char s[20]; + bool turn_on = true; + object *pobj; + int pnum = basethis->GetPlayerNum(); + pobj = &basethis->Objects[basethis->Players[pnum].objnum]; + + //parse "$observer" + StringParseWord(input_string,s,20,&input_string); + + //see if there is another command + if(StringParseWord(input_string,s,20,&input_string)){ + if(stricmp(s,DTXT_MNUON)) + turn_on = false; + }else{ + if (pobj->type==OBJ_OBSERVER){ + turn_on = false; + } + else{ + turn_on = true; + } + } + + if(!basethis->CallOnAllowObserverChange(turn_on)){ + DLLAddHUDMessage(DTXT_OBSERVERDENIED); + return; + } + + mprintf((0,"SwitchObserverMode %s [%d]\n",(turn_on)?"On":"Off",pnum)); + + DLLMultiSendRequestToObserve (OBSERVER_MODE_ROAM,(turn_on)?1:0,0); +} + +// handles a request to piggyback a player +// Usage: "$piggyback " +void DMFCInputCommand_Piggyback(char *input_string) +{ + if(basethis->IAmDedicatedServer()){ + DPrintf(DTXT_NOTINDEDICATED); + return; + } + + char s[20]; + bool turn_on = true; + object *pobj; + int pnum = basethis->GetPlayerNum(); + pobj = &basethis->Objects[basethis->Players[pnum].objnum]; + int topnum = -1; + + //parse "$piggyback" + StringParseWord(input_string,s,20,&input_string); + + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_PIGGYBACK); + return; + } + + char *ptr = s; + while(*ptr){ + if(!isdigit(*ptr)){ + basethis->DisplayInputCommandHelp(DTXT_IC_PIGGYBACK); + return; + } + ptr++; + } + + topnum = atoi(s); + if(!basethis->CheckPlayerNum(topnum)){ + DLLAddHUDMessage(DTXT_INVALIDPNUM); + return; + } + + if (pobj->type==OBJ_OBSERVER){ + if(basethis->CallOnAllowObserverChange(false)) + DLLMultiSendRequestToObserve (OBSERVER_MODE_ROAM,0,0); + } + if(pnum==basethis->GetPlayerNum()){ + mprintf((0,"Returning to self\n")); + return; + } + + if(basethis->CallOnAllowObserverChange(true)){ + mprintf((0,"Switching to piggyback for player %d\n",topnum)); + DLLMultiSendRequestToObserve (OBSERVER_MODE_PIGGYBACK,1,basethis->Players[topnum].objnum); + } +} + +// handles a request to warp to another level +// Usage: "$warp " +void DMFCInputCommand_Warp(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + + char s[20]; + int level; + + //parse "$warp" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_WARP); + return; + } + + //parse levelnum + if(!StringParseNumber(input_string,&level,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_WARP); + return; + } + + basethis->WarpToLevel(level); +} + +// handles enable/disable remote administration +// Usage: "$remoteadmin " +extern bool Use_remote_admin; +void DMFCInputCommand_RemoteAdmin(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + char s[20]; + + //parse "$remoteadmin" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_REMOTEADMIN); + return; + } + + + //parse the off/on + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_REMOTEADMIN); + return; + } + + int on = 1; + + if(!strnicmp(s,DTXT_MNUOFF,strlen(DTXT_MNUOFF))) + on = 0; + + Remote_Enable((bool)(on!=0)); + + if(on && !Use_remote_admin) + { + //no password set + DLLAddHUDMessage(DTXT_UNABLETOREMOTE); + }else + { + DLLAddHUDMessage(DTXT_REMOTEADMINENABLE,(Use_remote_admin)?DTXT_MNUON:DTXT_MNUOFF); + } +} + +// handles setting/changing the remote administration password +// Usage: "$remoteadminpass " +void DMFCInputCommand_RemoteAdminPass(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + char s[64]; + + //parse "$remoteadminpass" + if(!StringParseWord(input_string,s,20,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_REMOTEADMINPASS); + return; + } + + //parse the password + if(!StringParseWord(input_string,s,64,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_REMOTEADMINPASS); + return; + } + + Remote_SetPassword(s); + DLLAddHUDMessage(DTXT_REMOTEADMINPASS); +} + +// handles seeing who is logged in, and allows the server to log them out +// If no parameter is given it lists all the players logged in. +// To log out a player give the login-id as a parameter +// Usage: "$remoteadminlogout [login-id]" +void DMFCInputCommand_RemoteAdminLogout(char *input_string) +{ + if(basethis->GetLocalRole()!=LR_SERVER) + return; + char s[64]; + + //parse "$remoteadminlogout" + if(!StringParseWord(input_string,s,64,&input_string)){ + basethis->DisplayInputCommandHelp(DTXT_IC_REMOTEADMINLOGOUT); + return; + } + + //see if there is another command + if(!StringParseWord(input_string,s,64,&input_string)){ + //list who is logged in + Remote_ListLogins(); + return; + } + + //logout + int prec = atoi(s); + Remote_Logout(prec); +} + +// handles a remote admin command +// Usage: "$remote